2D、3D設定 | 2D圖片路徑 | 設備管理 : MQTTpublish設定與傳送 | MQTT設定 : switch 功能開關與傳送訊息寫入

This commit is contained in:
koko 2025-07-25 12:04:09 +08:00
parent ac5c88a047
commit e6939183fe
16 changed files with 255 additions and 62 deletions

View File

@ -1,4 +1,4 @@
VITE_API_BASEURL = "https://ibms-cvilux-demo-api.production.mjmtech.com.tw" VITE_API_BASEURL = "https://ibms-cvilux-api.production.mjmtech.com.tw"
VITE_FILE_API_BASEURL = "https://cgems.cvilux-group.com:8088" VITE_FILE_API_BASEURL = "https://cgems.cvilux-group.com:8088"
VITE_MQTT_BASEURL = "wss://mqttwss.mjm-staging.developers-homelab.net" VITE_MQTT_BASEURL = "wss://mqttwss.mjm-staging.developers-homelab.net"
VITE_FORGE_BASEURL = "https://cgems.cvilux-group.com:8088/dist" VITE_FORGE_BASEURL = "https://cgems.cvilux-group.com:8088/dist"

View File

@ -34,4 +34,6 @@ export const GET_ASSET_ELECTYPE_API = `/AssetManage/GetElecType`;
export const POST_ASSET_ELECTYPE_API = `/AssetManage/SaveElecType`; export const POST_ASSET_ELECTYPE_API = `/AssetManage/SaveElecType`;
export const DELETE_ASSET_ELECTYPE_API = `/AssetManage/DeleteElecType`; export const DELETE_ASSET_ELECTYPE_API = `/AssetManage/DeleteElecType`;
export const POST_ASSET_ELEC_SETTING_API = `/AssetManage/SaveAssetSetting`; export const POST_ASSET_ELEC_SETTING_API = `/AssetManage/SaveAssetSetting`;
export const POST_ASSET_MQTT_PUBLISH_API = `/api/mqtt/publish`;

View File

@ -26,13 +26,14 @@ import {
POST_ASSET_ELECTYPE_API, POST_ASSET_ELECTYPE_API,
DELETE_ASSET_ELECTYPE_API, DELETE_ASSET_ELECTYPE_API,
POST_ASSET_ELEC_SETTING_API, POST_ASSET_ELEC_SETTING_API,
POST_ASSET_MQTT_PUBLISH_API,
} from "./api"; } from "./api";
import instance from "@/util/request"; import instance from "@/util/request";
import apihandler from "@/util/apihandler"; import apihandler from "@/util/apihandler";
import { object } from "yup"; import { object } from "yup";
export const getAssetMainList = async (building_guid) => { export const getAssetMainList = async (building_guid) => {
const res = await instance.post(GET_ASSET_MAIN_LIST_API,{building_guid}); const res = await instance.post(GET_ASSET_MAIN_LIST_API, { building_guid });
return apihandler(res.code, res.data, { return apihandler(res.code, res.data, {
msg: res.msg, msg: res.msg,
@ -49,12 +50,17 @@ export const deleteAssetMainItem = async (id) => {
}); });
}; };
export const postAssetMainList = async ({ id, system_key, system_value, building_guid }) => { export const postAssetMainList = async ({
id,
system_key,
system_value,
building_guid,
}) => {
const res = await instance.post(POST_ASSET_MAIN_LIST_API, { const res = await instance.post(POST_ASSET_MAIN_LIST_API, {
id, id,
system_key, system_key,
system_value, system_value,
building_guid building_guid,
}); });
return apihandler(res.code, res.data, { return apihandler(res.code, res.data, {
@ -241,6 +247,9 @@ export const postDeviceItem = async ({
decimals, decimals,
is_bool, is_bool,
is_link, is_link,
show_event_switch_btn,
event_switch_on_message,
event_switch_off_message,
}) => { }) => {
const res = await instance.post(POST_ASSET_DEVICE_ITEM_API, { const res = await instance.post(POST_ASSET_DEVICE_ITEM_API, {
id, id,
@ -250,6 +259,9 @@ export const postDeviceItem = async ({
decimals, decimals,
is_bool, is_bool,
is_link, is_link,
show_event_switch_btn,
event_switch_on_message,
event_switch_off_message,
}); });
return apihandler(res.code, res.data, { return apihandler(res.code, res.data, {
@ -335,3 +347,15 @@ export const postAssetElecSetting = async (formData) => {
code: res.code, code: res.code,
}); });
}; };
export const postMQTTpublish = async ({ Topic, Payload }) => {
const res = await instance.post(POST_ASSET_MQTT_PUBLISH_API, {
Topic,
Payload,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};

View File

@ -10,4 +10,8 @@ export const GET_DASHBOARD_PRODUCT_HISTORY_API = `/SituationRoom/GetProductionHi
export const GET_DASHBOARD_ENERGY_INFO_API = `api/dashboard/GetEnergyInfo` export const GET_DASHBOARD_ENERGY_INFO_API = `api/dashboard/GetEnergyInfo`
export const GET_DASHBOARD_ENERGY_COST_API = `api/dashboard/GetEnergyCost` export const GET_DASHBOARD_ENERGY_COST_API = `api/dashboard/GetEnergyCost`
export const GET_DASHBOARD_ALARMOPERATION_INFO_API = `api/dashboard/GetAlarmOperationInfo` export const GET_DASHBOARD_ALARMOPERATION_INFO_API = `api/dashboard/GetAlarmOperationInfo`
export const GET_DASHBOARD_2D3DINFO_API = `api/setting/visual/query`
export const POST_DASHBOARD_2D3DINFO_API = `api/setting/visual/update`

View File

@ -11,6 +11,8 @@ import {
GET_DASHBOARD_ENERGY_INFO_API, GET_DASHBOARD_ENERGY_INFO_API,
GET_DASHBOARD_ENERGY_COST_API, GET_DASHBOARD_ENERGY_COST_API,
GET_DASHBOARD_ALARMOPERATION_INFO_API, GET_DASHBOARD_ALARMOPERATION_INFO_API,
GET_DASHBOARD_2D3DINFO_API,
POST_DASHBOARD_2D3DINFO_API
} from "./api"; } from "./api";
import instance from "@/util/request"; import instance from "@/util/request";
import apihandler from "@/util/apihandler"; import apihandler from "@/util/apihandler";
@ -176,3 +178,22 @@ export const getAlarmOperationInfo = async (building_guid) => {
code: res.code, code: res.code,
}); });
}; };
export const getDashboard2D3D = async (BuildingId) => {
const res = await instance.post(GET_DASHBOARD_2D3DINFO_API, {
BuildingId});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const posttDashboard2D3D = async (formData) => {
const res = await instance.post(POST_DASHBOARD_2D3DINFO_API, formData);
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};

View File

@ -400,9 +400,13 @@
"delete_success": "删除成功", "delete_success": "删除成功",
"delete_failed": "删除失败", "delete_failed": "删除失败",
"mqtt_refresh": "重新设定成功", "mqtt_refresh": "重新设定成功",
"schema_name_required": "架构名称栏位必填" "schema_name_required": "架构名称栏位必填",
"incorrect_format":"格式不正确",
"send_successfully":"送出成功",
"edit_successfully":"修改成功"
}, },
"setting": { "setting": {
"electricity_meter": "电表",
"MQTT_parse": "MQTT 解析", "MQTT_parse": "MQTT 解析",
"schema": "架构", "schema": "架构",
"point": "点位", "point": "点位",
@ -412,6 +416,9 @@
"number_of_decimal_places": "小数位数", "number_of_decimal_places": "小数位数",
"boolean_value": "布林值", "boolean_value": "布林值",
"hide_point": "点位显示", "hide_point": "点位显示",
"hide_switch": "switch 功能",
"switch_on_message": "switch 开启时传送的讯息",
"switch_off_message": "switch 关闭时传送的讯息",
"schema_name": "架构名称", "schema_name": "架构名称",
"IoT_point_structure": "IoT点位结构", "IoT_point_structure": "IoT点位结构",
"system_point_name": "系统点位名称", "system_point_name": "系统点位名称",

View File

@ -400,9 +400,13 @@
"delete_success": "刪除成功", "delete_success": "刪除成功",
"delete_failed": "刪除失敗", "delete_failed": "刪除失敗",
"mqtt_refresh": "重新設定成功", "mqtt_refresh": "重新設定成功",
"schema_name_required": "架構名稱欄位必填" "schema_name_required": "架構名稱欄位必填",
"incorrect_format":"格式不正確",
"send_successfully":"送出成功",
"edit_successfully":"修改成功"
}, },
"setting": { "setting": {
"electricity_meter":"電表",
"MQTT_parse": "MQTT 解析", "MQTT_parse": "MQTT 解析",
"schema": "架構", "schema": "架構",
"point": "點位", "point": "點位",
@ -412,6 +416,9 @@
"number_of_decimal_places": "小數位數", "number_of_decimal_places": "小數位數",
"boolean_value": "布林值", "boolean_value": "布林值",
"hide_point": "點位顯示", "hide_point": "點位顯示",
"hide_switch": "switch 功能",
"switch_on_message": "switch 開啟時傳送的訊息",
"switch_off_message": "switch 關閉時傳送的訊息",
"schema_name": "架構名稱", "schema_name": "架構名稱",
"IoT_point_structure": "IoT點位結構", "IoT_point_structure": "IoT點位結構",
"system_point_name": "系統點位名稱", "system_point_name": "系統點位名稱",

View File

@ -400,9 +400,13 @@
"delete_success": "Delete successfully", "delete_success": "Delete successfully",
"delete_failed": "Delete failed", "delete_failed": "Delete failed",
"mqtt_refresh": "MQTT reset successful", "mqtt_refresh": "MQTT reset successful",
"schema_name_required": "The schema name field is required" "schema_name_required": "The schema name field is required",
"incorrect_format":"Incorrect format",
"send_successfully":"Sent successfully",
"edit_successfully":"Edited successfully"
}, },
"setting": { "setting": {
"electricity_meter": "Electricity Meter",
"MQTT_parse": "MQTT Parse", "MQTT_parse": "MQTT Parse",
"schema": "Schema", "schema": "Schema",
"point": "Point", "point": "Point",
@ -412,6 +416,9 @@
"number_of_decimal_places": "Number of Decimal Places", "number_of_decimal_places": "Number of Decimal Places",
"boolean_value": "Boolean Value", "boolean_value": "Boolean Value",
"hide_point": "Point Display", "hide_point": "Point Display",
"hide_switch": "Switch Function",
"switch_on_message": "Switch On Message",
"switch_off_message": "Switch Off Message",
"schema_name": "Schema name", "schema_name": "Schema name",
"IoT_point_structure": "IoT Point Structure", "IoT_point_structure": "IoT Point Structure",
"system_point_name": "System Point Name", "system_point_name": "System Point Name",

View File

@ -2,6 +2,7 @@ import { defineStore } from "pinia";
import { ref, computed, watch } from "vue"; import { ref, computed, watch } from "vue";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { getBuildings } from "@/apis/building"; import { getBuildings } from "@/apis/building";
import { getDashboard2D3D } from "@/apis/dashboard";
import { getAssetFloorList, getDepartmentList } from "@/apis/asset"; import { getAssetFloorList, getDepartmentList } from "@/apis/asset";
const useBuildingStore = defineStore("buildingInfo", () => { const useBuildingStore = defineStore("buildingInfo", () => {
@ -11,6 +12,9 @@ const useBuildingStore = defineStore("buildingInfo", () => {
const floorList = ref([]); const floorList = ref([]);
const deptList = ref([]); const deptList = ref([]);
const mainSubSys = ref([]); const mainSubSys = ref([]);
// 控制顯示2D/3D切換與內容
const showForgeArea = ref(true);
const previewImageExt = ref("");
// 計算屬性 // 計算屬性
const mainSys = computed(() => const mainSys = computed(() =>
@ -77,22 +81,29 @@ const useBuildingStore = defineStore("buildingInfo", () => {
})) || []; })) || [];
}; };
// 獲取2D、3D顯示與否
const fetchDashboard2D3D = async (BuildingId) => {
const res = await getDashboard2D3D(BuildingId);
showForgeArea.value = res.data.is3DEnabled;
previewImageExt.value = res.data.previewImageExt || "";
};
// 清除localStorage建築物 // 清除localStorage建築物
const deleteBuilding = () => { const deleteBuilding = () => {
localStorage.removeItem("CviBuildingList"); localStorage.removeItem("CviBuildingList");
localStorage.removeItem("CviBuilding"); localStorage.removeItem("CviBuilding");
buildings.value = []; buildings.value = [];
selectedBuilding.value = null; selectedBuilding.value = null;
} };
// 當 selectedBuilding 改變時,更新 floorList 和 deptList // 當 selectedBuilding 改變時,更新 floorList 和 deptList
watch(selectedBuilding, async (newBuilding) => { watch(selectedBuilding, async (newBuilding) => {
if (newBuilding) { if (newBuilding) {
localStorage.setItem("CviBuilding", JSON.stringify(newBuilding)) localStorage.setItem("CviBuilding", JSON.stringify(newBuilding));
await Promise.all([ await Promise.all([
fetchFloorList(newBuilding.building_guid), fetchFloorList(newBuilding.building_guid),
fetchDepartmentList(), fetchDepartmentList(),
fetchDashboard2D3D(newBuilding.building_guid),
]); ]);
} }
}); });
@ -111,10 +122,13 @@ const useBuildingStore = defineStore("buildingInfo", () => {
mainSys, mainSys,
subSys, subSys,
selectedSystem, selectedSystem,
showForgeArea,
previewImageExt,
deleteBuilding, deleteBuilding,
fetchBuildings, fetchBuildings,
fetchFloorList, fetchFloorList,
fetchDepartmentList, fetchDepartmentList,
fetchDashboard2D3D,
initialize, initialize,
}; };
}); });

View File

@ -1,6 +1,7 @@
<script setup> <script setup>
import { onMounted, ref, inject, watch, computed } from "vue"; import { onMounted, ref, inject, watch, computed } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { postMQTTpublish } from "@/apis/asset";
import mqtt from "mqtt"; import mqtt from "mqtt";
import dayjs from "dayjs"; import dayjs from "dayjs";
@ -93,6 +94,28 @@ const onCancel = () => {
} }
countdown.value = 60; countdown.value = 60;
}; };
const onSubmit = async () => {
const Topic = formState.value.topic;
let Payload = "";
try {
Payload = JSON.stringify(JSON.parse(formState.value.publish_message));
} catch (e) {
openToast("error", t("msg.incorrect_format"), "#asset_add_table_item");
return;
}
try {
const res = await postMQTTpublish({ Topic, Payload });
if (res.isSuccess) {
openToast("success", t("msg.send_successfully"), "#asset_add_table_item");
} else {
openToast("error", res.msg, "#asset_add_table_item");
}
} catch (error) {
openToast("error", t("setting.mqtt_send_error"), "#asset_add_table_item");
}
};
</script> </script>
<template> <template>
@ -110,10 +133,10 @@ const onCancel = () => {
<Input :value="formState" name="topic"> <Input :value="formState" name="topic">
<template #topLeft>MQTT publish topic</template> <template #topLeft>MQTT publish topic</template>
</Input> </Input>
<Textarea :value="formState" name="notice" class=""> <Textarea :value="formState" name="publish_message">
<template #topLeft>MQTT 傳送的訊息</template> <template #topLeft>MQTT messages</template>
</Textarea> </Textarea>
<button type="button" class="btn btn-add mt-6 w-24" @click="openModal"> <button type="button" class="btn btn-add mt-6 w-24" @click="onSubmit">
<font-awesome-icon :icon="['far', 'paper-plane']" /> <font-awesome-icon :icon="['far', 'paper-plane']" />
Send Send
</button> </button>

View File

@ -17,13 +17,13 @@ const store = useBuildingStore();
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn(); const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
let intervalId = null; let intervalId = null;
const energyCostData = ref({}); const energyCostData = ref({});
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const imgBaseUrl = ref('');
const formState = ref({ const formState = ref({
building_guid: null, building_guid: null,
floor_guid: "all", floor_guid: "all",
department_id: "all", department_id: "all",
}); });
// 2D/3D
const showForgeArea = ref(true);
const getEnergyCostData = async (params) => { const getEnergyCostData = async (params) => {
const res = await getEnergyCost(params); const res = await getEnergyCost(params);
@ -35,7 +35,8 @@ watch(
(newBuilding) => { (newBuilding) => {
if (newBuilding) { if (newBuilding) {
formState.value.building_guid = newBuilding.building_guid; formState.value.building_guid = newBuilding.building_guid;
} imgBaseUrl.value = `${FILE_BASEURL}/upload/setting/previewImage/${newBuilding.building_guid}${store.previewImageExt}`;
}
}, },
{ immediate: true, deep: true } { immediate: true, deep: true }
); );
@ -68,22 +69,26 @@ watch(
{ immediate: true, deep: true } { immediate: true, deep: true }
); );
onMounted(() => { watch(
if (showForgeArea.value) { () => store.showForgeArea,
setItems([ (newVal) => {
{ if (newVal == true) {
title: "2D", setItems([
key: "2D", {
active: false, title: "2D",
}, key: "2D",
{ active: false,
title: "3D", },
key: "3D", {
active: true, title: "3D",
}, key: "3D",
]); active: true,
} },
}); ]);
}
},
{ immediate: true }
);
onUnmounted(() => { onUnmounted(() => {
clearInterval(intervalId); clearInterval(intervalId);
@ -94,7 +99,7 @@ onUnmounted(() => {
<div class="flex flex-wrap items-center"> <div class="flex flex-wrap items-center">
<!-- 建築圖 --> <!-- 建築圖 -->
<div class="w-full xl:w-1/3 relative"> <div class="w-full xl:w-1/3 relative">
<template v-if="showForgeArea"> <template v-if="store.showForgeArea">
<ButtonConnectedGroup <ButtonConnectedGroup
:items="items" :items="items"
className="btn-xs absolute right-3 top-6 z-20 bg-slate-800 p-0 rounded-lg " className="btn-xs absolute right-3 top-6 z-20 bg-slate-800 p-0 rounded-lg "
@ -104,7 +109,7 @@ onUnmounted(() => {
<!-- setting頁面要新增讓他能上傳圖片 --> <!-- setting頁面要新增讓他能上傳圖片 -->
<img <img
alt="build" alt="build"
src="/build_img.jpg" :src="imgBaseUrl || '/build_img.jpg'"
:class=" :class="
twMerge( twMerge(
'absolute w-full h-full transition-opacity duration-300', 'absolute w-full h-full transition-opacity duration-300',
@ -114,6 +119,7 @@ onUnmounted(() => {
) )
" "
/> />
<Forge <Forge
:class=" :class="
twMerge( twMerge(
@ -129,7 +135,7 @@ onUnmounted(() => {
<template v-else> <template v-else>
<img <img
alt="build" alt="build"
src="/build_img.jpg" :src="imgBaseUrl || '/build_img.jpg'"
class="area-img-box w-full h-[460px] block relative rounded-sm mt-3" class="area-img-box w-full h-[460px] block relative rounded-sm mt-3"
/> />
</template> </template>

View File

@ -113,7 +113,7 @@ watch(
<template> <template>
<div class="flex justify-start items-center mt-10 mb-5"> <div class="flex justify-start items-center mt-10 mb-5">
<h3 class="text-xl mr-5">電表</h3> <h3 class="text-xl mr-5">{{$t("setting.electricity_meter")}}</h3>
<button <button
v-if="!isEditing" v-if="!isEditing"
class="btn btn-sm btn-add mr-3" class="btn btn-sm btn-add mr-3"

View File

@ -95,6 +95,10 @@ const PointsColumns = computed(() => [
title: t("setting.hide_point"), title: t("setting.hide_point"),
key: "is_link", key: "is_link",
}, },
{
title: t("setting.hide_switch"),
key: "show_event_switch_btn",
},
{ {
title: t("assetManagement.operation"), title: t("assetManagement.operation"),
key: "operation", key: "operation",
@ -256,6 +260,9 @@ watch(
<template v-else-if="column.key === 'is_link'"> <template v-else-if="column.key === 'is_link'">
{{ record.is_link === 1 ? t("alert.yes") : t("alert.no") }} {{ record.is_link === 1 ? t("alert.yes") : t("alert.no") }}
</template> </template>
<template v-else-if="column.key === 'show_event_switch_btn'">
{{ record.show_event_switch_btn === true ? t("alert.yes") : t("alert.no") }}
</template>
<template v-else-if="column.key === 'operation'"> <template v-else-if="column.key === 'operation'">
<button <button
class="btn btn-sm btn-success text-white mr-2" class="btn btn-sm btn-success text-white mr-2"

View File

@ -21,6 +21,9 @@ const formState = ref({
decimals: 0, decimals: 0,
is_bool: 0, is_bool: 0,
is_link: 0, is_link: 0,
show_event_switch_btn: false,
event_switch_on_message: "",
event_switch_off_message: "",
}); });
let schema = ref( let schema = ref(
yup.object({ yup.object({
@ -42,10 +45,16 @@ const onOk = async () => {
decimals: Number(values.decimals), decimals: Number(values.decimals),
is_bool: Number(values.is_bool), is_bool: Number(values.is_bool),
is_link: Number(values.is_link), is_link: Number(values.is_link),
event_switch_on_message: values.show_event_switch_btn
? values.event_switch_on_message
: "",
event_switch_off_message: values.show_event_switch_btn
? values.event_switch_off_message
: "",
}); });
if (res.isSuccess) { if (res.isSuccess) {
props.getData(props.variable_id);
onCancel(); onCancel();
props.getData(props.variable_id);
} else { } else {
openToast("error", res.msg, "#point_list_item"); openToast("error", res.msg, "#point_list_item");
} }
@ -154,31 +163,40 @@ watch(
<RadioGroup <RadioGroup
class="my-2" class="my-2"
name="is_link" name="show_event_switch_btn"
:value="formState" :value="formState"
:items="[ :items="[
{ {
key: 1, key: 1,
value: 1, value: true,
title: '開啟', title: $t('alert.yes'),
}, },
{ {
key: 0, key: 0,
value: 0, value: false,
title: '關閉', title: $t('alert.no'),
}, },
]" ]"
:required="true" :required="true"
> >
<template #topLeft>switch 顯示</template> <template #topLeft>{{ $t("setting.hide_switch") }}</template>
</RadioGroup> </RadioGroup>
<Textarea :value="formState" name="notice" class="w-full my-2"> <Textarea
<template #topLeft>switch 開啟時傳送的訊息</template> v-if="formState.show_event_switch_btn"
:value="formState"
name="event_switch_on_message"
class="w-full my-2"
>
<template #topLeft>{{ $t("setting.switch_on_message") }}</template>
</Textarea> </Textarea>
<Textarea :value="formState" name="notice" class="w-full my-2"> <Textarea
<template #topLeft>switch 關閉時傳送的訊息</template> v-if="formState.show_event_switch_btn"
:value="formState"
name="event_switch_off_message"
class="w-full my-2"
>
<template #topLeft>{{ $t("setting.switch_off_message") }}</template>
</Textarea> </Textarea>
</form> </form>
</template> </template>
<template #modalAction> <template #modalAction>

View File

@ -1,16 +1,63 @@
<script setup> <script setup>
import { onMounted, ref, inject, computed } from "vue"; import { onMounted, ref, inject, computed } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { posttDashboard2D3D } from "@/apis/dashboard";
import useBuildingStore from "@/stores/useBuildingStore";
const { openToast, cancelToastOpen } = inject("app_toast");
const buildingStore = useBuildingStore();
const { t } = useI18n(); const { t } = useI18n();
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL; const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const form = ref(null);
const formState = ref({ const formState = ref({
lorf: [], showForgeArea: buildingStore.showForgeArea,
lorf: buildingStore.previewImageExt
? [
{
src: `${FILE_BASEURL}/upload/setting/previewImage/${buildingStore.selectedBuilding.building_guid}${buildingStore.previewImageExt}`,
name: `${buildingStore.selectedBuilding.building_guid}`,
ext: `${buildingStore.previewImageExt}`,
},
]
: [],
}); });
const updateFileList = (files) => { const updateFileList = (files) => {
formState.value.lorf = files; formState.value.lorf = files;
}; };
const onShowForgeAreaChange = (e) => {
formState.value.showForgeArea = e.target.checked;
};
const onOk = async () => {
const formData = new FormData();
formData.delete("file");
formData.append("buildingId", buildingStore.selectedBuilding.building_guid);
formData.append("is3DEnabled", formState.value.showForgeArea);
if (formState.value.lorf && formState.value.lorf.length > 0) {
formData.append("file", formState.value.lorf[0]);
}
const res = await posttDashboard2D3D(formData);
if (res.isSuccess) {
await buildingStore.fetchDashboard2D3D(
buildingStore.selectedBuilding.building_guid
);
openToast("success", t("msg.edit_successfully"));
//
formState.value.showForgeArea = buildingStore.showForgeArea;
formState.value.lorf = buildingStore.previewImageExt
? [ {
src: `${FILE_BASEURL}/upload/setting/previewImage/${buildingStore.selectedBuilding.building_guid}/${buildingStore.previewImageExt}`,
name: `${buildingStore.selectedBuilding.building_guid}`,
ext: `${buildingStore.previewImageExt}`,
},]
: [];
}else{
openToast("error", res.msg);
}
};
</script> </script>
<template> <template>
@ -18,24 +65,28 @@ const updateFileList = (files) => {
<h3 class="text-xl mr-5">2D / 3D 顯示設定 :</h3> <h3 class="text-xl mr-5">2D / 3D 顯示設定 :</h3>
</div> </div>
<div class="flex gap-5"> <div class="flex gap-5">
<span class="text-lg">3D 模型顯示 : </span <span class="text-lg">3D 模型顯示 : </span>
><input <input
type="checkbox" type="checkbox"
className="toggle toggle-lg toggle-success" class="toggle toggle-lg toggle-success"
defaultChecked name="showForgeArea"
:checked="formState.showForgeArea"
@change="onShowForgeAreaChange"
/> />
</div> </div>
<Upload <Upload
class="my-2 max-w-[600px] w-full" class="mt-2 mb-5 max-w-[600px] w-full"
name="oriFile" name="oriFile"
:fileList="formState?.lorf" :fileList="formState.lorf"
:getFileList="updateFileList" :getFileList="updateFileList"
:multiple="true" :multiple="false"
:baseUrl="`${FILE_BASEURL}/upload/operation`"
formats="png、jpg" formats="png、jpg"
> >
<template #topLeft>首頁2D圖上傳 :</template> <template #topLeft>首頁2D圖上傳 :</template>
</Upload> </Upload>
<button type="submit" class="btn btn-outline-success" @click.prevent="onOk">
{{ $t("button.submit") }}
</button>
</template> </template>
<style lang="css" scoped></style> <style lang="css" scoped></style>

View File

@ -16,6 +16,7 @@ import SystemFloor from "./SystemFloor.vue";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import dayjs from "dayjs"; import dayjs from "dayjs";
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const buildingStore = useBuildingStore(); const buildingStore = useBuildingStore();
const statusList = computed(() => { const statusList = computed(() => {
@ -41,7 +42,7 @@ const floors = ref([]);
const deptData = ref([]); const deptData = ref([]);
const companyOptions = ref([]); const companyOptions = ref([]);
const selected_dbid = ref([]); const selected_dbid = ref([]);
const showForgeArea = ref(true); // 2D/3D const imgBaseUrl = ref('');
const getFloors = async () => { const getFloors = async () => {
const res = await getAssetFloorList(); const res = await getAssetFloorList();
@ -129,6 +130,7 @@ watch(
(newBuilding) => { (newBuilding) => {
if (Boolean(newBuilding)) { if (Boolean(newBuilding)) {
getData(); getData();
imgBaseUrl.value = `${FILE_BASEURL}/upload/setting/previewImage/${newBuilding.building_guid}${buildingStore.previewImageExt}`;
} }
}, },
{ {
@ -327,7 +329,7 @@ onBeforeUnmount(() => {
</div> </div>
</div> </div>
<div class="col-span-1 h-full flex flex-col justify-between"> <div class="col-span-1 h-full flex flex-col justify-between">
<template v-if="showForgeArea"> <template v-if="buildingStore.showForgeArea">
<SystemMode /> <SystemMode />
<div class="h-full relative"> <div class="h-full relative">
<SystemFloor <SystemFloor
@ -354,7 +356,7 @@ onBeforeUnmount(() => {
<div class="h-full relative"> <div class="h-full relative">
<img <img
alt="build" alt="build"
src="/build_img.jpg" :src="imgBaseUrl || '/build_img.jpg'"
:class=" :class="
twMerge( twMerge(
'absolute w-full h-full transition-opacity duration-300', 'absolute w-full h-full transition-opacity duration-300',