Compare commits
No commits in common. "main" and "feature/system" have entirely different histories.
main
...
feature/sy
@ -1,4 +1,3 @@
|
|||||||
VITE_API_BASEURL = "https://ibms-cvilux-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_FORGE_BASEURL = "https://cgems.cvilux-group.com:8088/dist"
|
VITE_FORGE_BASEURL = "https://cgems.cvilux-group.com:8088/dist"
|
||||||
@ -1,4 +1,3 @@
|
|||||||
VITE_API_BASEURL = "https://ibms-cvilux-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_FORGE_BASEURL = "https://cgems.cvilux-group.com:8088/dist"
|
||||||
# VITE_FORGE_BASEURL = "https://cgems.cvilux-group.com:8088/dist"
|
|
||||||
@ -1,3 +1,3 @@
|
|||||||
VITE_API_BASEURL = "https://ibms-cvilux-demo-api.production.mjmtech.com.tw"
|
VITE_API_BASEURL = "http://220.132.206.5:8008"
|
||||||
VITE_FILE_API_BASEURL = "https://cgems.cvilux-group.com:8088"
|
VITE_FILE_API_BASEURL = "http://220.132.206.5:8085/file"
|
||||||
VITE_MQTT_BASEURL = "wss://mqttwss.mjm-staging.developers-homelab.net"
|
VITE_FORGE_BASEURL = "http://localhost:5173"
|
||||||
29
.github/prompts/exportCSV.prompt.md
vendored
29
.github/prompts/exportCSV.prompt.md
vendored
@ -1,29 +0,0 @@
|
|||||||
---
|
|
||||||
mode: agent
|
|
||||||
---
|
|
||||||
# API 路徑整理與引用檢查規則
|
|
||||||
|
|
||||||
## 目標
|
|
||||||
- 針對 apis 目錄下所有子資料夾(如 account、alert、asset、building、dashboard、energy、forge、graph、history、login、operation、productSetting、system)的 api.js 與 index.js 檔案,完整追蹤 API 路徑的實際引用情形。
|
|
||||||
- 追蹤流程:
|
|
||||||
1. 先在 api.js 找出所有 API 路徑常數(如 `export const GET_XXX_API = '/path'`)。
|
|
||||||
2. 在 index.js 檔案確認這些常數有被 import 並包裝成 API function(如 `getXXX`)。
|
|
||||||
3. 再全專案搜尋這些 API function 是否有被其他檔案 import 並呼叫。
|
|
||||||
- 產生一份 csv 報表,格式如下:
|
|
||||||
|
|
||||||
| API 路徑 | 定義常數 | API function | 是否有被引用 |
|
|
||||||
|----------|----------|--------------|-------------|
|
|
||||||
| /user | GET_USER_API | getUser | Y |
|
|
||||||
| /admin | GET_ADMIN_API | getAdmin | N |
|
|
||||||
|
|
||||||
## 詳細規則
|
|
||||||
- 處理 apis 目錄下所有子資料夾的 api.js 與 index.js 檔案。
|
|
||||||
- API 路徑的定義需涵蓋 get/post/put/delete 等(如 `export const API = '/path'`)。
|
|
||||||
- 只統計有被 index.js import 並包裝成 function 的 API 路徑。
|
|
||||||
- 檢查 function 是否有被其他檔案 import 並呼叫(排除 apis 目錄本身)。
|
|
||||||
- 匹配到的檔案需記錄完整路徑,可多個檔案以分號分隔。
|
|
||||||
- 統計結果輸出為 csv 檔案。
|
|
||||||
|
|
||||||
## 輸出
|
|
||||||
- 檔名:api_usage_report.csv
|
|
||||||
- 欄位:API 路徑, 定義常數, API function, 是否有被引用,
|
|
||||||
895
package-lock.json
generated
895
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,8 +6,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview"
|
||||||
"build:staging": "vite build --mode staging"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons-vue": "^7.0.1",
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
@ -22,16 +21,14 @@
|
|||||||
"date-fns": "^3.3.1",
|
"date-fns": "^3.3.1",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
|
"flag-icons": "^7.2.3",
|
||||||
"jquery-ui": "^1.14.1",
|
"jquery-ui": "^1.14.1",
|
||||||
"json-schema-generator": "^2.0.6",
|
|
||||||
"mqtt": "^5.10.3",
|
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"requirejs": "^2.3.6",
|
"requirejs": "^2.3.6",
|
||||||
"tailwind-merge": "^2.2.1",
|
"tailwind-merge": "^2.2.1",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
"vue-i18n": "^10.0.4",
|
"vue-i18n": "^10.0.4",
|
||||||
"vue-router": "^4.2.5",
|
"vue-router": "^4.2.5",
|
||||||
"vuedraggable": "^4.1.0",
|
|
||||||
"yup": "^1.4.0",
|
"yup": "^1.4.0",
|
||||||
"yup-phone-lite": "^2.0.1"
|
"yup-phone-lite": "^2.0.1"
|
||||||
},
|
},
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 137 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 9.4 KiB |
@ -1,6 +1,5 @@
|
|||||||
export const POST_ACK_API = `/obix/alarm`;
|
export const POST_ACK_API = `/obix/alarm`;
|
||||||
export const GET_ALERT_FORMID_API = `/Alert/AlertList`;
|
export const GET_ALERT_FORMID_API = `/Alert/AlertList`;
|
||||||
export const GET_ALERT_LOG_API = `api/Alarm/GetAlarmLog`;
|
|
||||||
export const POST_OPERATION_RECORD_API = `/operation/SavOpeRecord`;
|
export const POST_OPERATION_RECORD_API = `/operation/SavOpeRecord`;
|
||||||
|
|
||||||
export const GET_ALERT_SUB_LIST_API = `api/Device/GetMainSub`;
|
export const GET_ALERT_SUB_LIST_API = `api/Device/GetMainSub`;
|
||||||
@ -10,17 +9,8 @@ export const GET_ALERT_MEMBER = `api/Alarm/GetAlarmMember`;
|
|||||||
export const POST_ALERT_MEMBER = `api/Alarm/SaveAlarmMember`;
|
export const POST_ALERT_MEMBER = `api/Alarm/SaveAlarmMember`;
|
||||||
export const DELETE_ALERT_MEMBER = `api/Alarm/DeleteAlarmMember`;
|
export const DELETE_ALERT_MEMBER = `api/Alarm/DeleteAlarmMember`;
|
||||||
export const GET_NOTICE_LIST_API = `api/Alarm/GetNotice`;
|
export const GET_NOTICE_LIST_API = `api/Alarm/GetNotice`;
|
||||||
export const GET_SHOW_ALERT_API = `api/Alarm/GetShowAlarm`; // 取得告警顯示清單
|
|
||||||
|
|
||||||
export const GET_OUTLIERS_LIST_API = `api/Alarm/GetAlarmSetting`;
|
export const GET_OUTLIERS_LIST_API = `api/Alarm/GetAlarmSetting`;
|
||||||
export const GET_OUTLIERS_DEVLIST_API = `api/Alarm/GetDevList`; // 取得設備
|
export const GET_OUTLIERS_DEVLIST_API = `api/Alarm/GetDevList`; // 取得設備
|
||||||
export const GET_OUTLIERS_POINTS_API = `api/Alarm/GetAlarmPoints`; // 取得點位
|
export const GET_OUTLIERS_POINTS_API = `api/Alarm/GetAlarmPoints`; // 取得點位
|
||||||
export const POST_OUTLIERS_SETTING_API = `api/Alarm/SaveAlarmSetting`; // 新增與修改
|
export const POST_OUTLIERS_SETTING_API = `api/Alarm/SaveAlarmSetting`; // 新增與修改
|
||||||
export const DELETE_OUTLIERS_SETTING_API = `api/Alarm/DeleteAlarmSetting`; // 刪除
|
|
||||||
export const GET_FACTOR_API = `api/Alarm/GetFactor`; // 刪除
|
|
||||||
|
|
||||||
export const GET_ALERT_SCHEDULE_LIST_API = `api/Alarm/GetAlarmSchedule`;
|
|
||||||
export const POST_ALERT_SCHEDULE = `api/Alarm/SaveAlarmSchedule`;
|
|
||||||
export const DELETE_ALERT_SCHEDULE = `api/Alarm/DeleteAlarmSchedule`;
|
|
||||||
|
|
||||||
export const POST_ALERT_MQTT_REFRESH = `api/Alarm/MQTTRefresh`;
|
|
||||||
@ -1,28 +1,48 @@
|
|||||||
import {
|
import {
|
||||||
POST_ACK_API,
|
POST_ACK_API,
|
||||||
GET_ALERT_FORMID_API,
|
GET_ALERT_FORMID_API,
|
||||||
GET_ALERT_LOG_API,
|
|
||||||
POST_OPERATION_RECORD_API,
|
POST_OPERATION_RECORD_API,
|
||||||
GET_ALERT_SUB_LIST_API,
|
GET_ALERT_SUB_LIST_API,
|
||||||
GET_OUTLIERS_LIST_API,
|
GET_OUTLIERS_LIST_API,
|
||||||
GET_OUTLIERS_DEVLIST_API,
|
GET_OUTLIERS_DEVLIST_API,
|
||||||
GET_OUTLIERS_POINTS_API,
|
GET_OUTLIERS_POINTS_API,
|
||||||
POST_OUTLIERS_SETTING_API,
|
POST_OUTLIERS_SETTING_API,
|
||||||
DELETE_OUTLIERS_SETTING_API,
|
|
||||||
GET_FACTOR_API,
|
|
||||||
GET_ALERT_MEMBER_LIST_API,
|
GET_ALERT_MEMBER_LIST_API,
|
||||||
GET_ALERT_MEMBER,
|
GET_ALERT_MEMBER,
|
||||||
POST_ALERT_MEMBER,
|
POST_ALERT_MEMBER,
|
||||||
DELETE_ALERT_MEMBER,
|
DELETE_ALERT_MEMBER,
|
||||||
GET_NOTICE_LIST_API,
|
GET_NOTICE_LIST_API,
|
||||||
GET_SHOW_ALERT_API,
|
|
||||||
GET_ALERT_SCHEDULE_LIST_API,
|
|
||||||
POST_ALERT_SCHEDULE,
|
|
||||||
DELETE_ALERT_SCHEDULE,
|
|
||||||
POST_ALERT_MQTT_REFRESH
|
|
||||||
} 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 axios from "axios";
|
||||||
|
|
||||||
|
export const postChgAck = async (uuid) => {
|
||||||
|
try {
|
||||||
|
const data =
|
||||||
|
'<obj is="obix:AckAlarmIn"><str name="ackUser" val="obix" /></obj>';
|
||||||
|
const res = await axios.post(`${POST_ACK_API}/${uuid}/ack`, data, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "text/plain",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 解析XML錯誤信息
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const xmlDoc = parser.parseFromString(res.data, "text/xml");
|
||||||
|
const errElement = xmlDoc.querySelector("err");
|
||||||
|
|
||||||
|
if (errElement) {
|
||||||
|
console.error("Error in acknowledging alarm");
|
||||||
|
return { isSuccess: false, msg: `Error in acknowledging alarm` };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { isSuccess: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error in acknowledging alarm:", error);
|
||||||
|
return { isSuccess: false, msg: "Error in acknowledging alarm" };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const getAlertFormId = async (uuid) => {
|
export const getAlertFormId = async (uuid) => {
|
||||||
const res = await instance.post(GET_ALERT_FORMID_API, uuid);
|
const res = await instance.post(GET_ALERT_FORMID_API, uuid);
|
||||||
@ -32,24 +52,6 @@ export const getAlertFormId = async (uuid) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAlertLog = async ({
|
|
||||||
Start_date,
|
|
||||||
End_date,
|
|
||||||
isRecovery,
|
|
||||||
device_name_tag,
|
|
||||||
}) => {
|
|
||||||
const res = await instance.post(GET_ALERT_LOG_API, {
|
|
||||||
Start_date,
|
|
||||||
End_date,
|
|
||||||
isRecovery,
|
|
||||||
device_name_tag,
|
|
||||||
});
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postOperationRecord = async (formData) => {
|
export const postOperationRecord = async (formData) => {
|
||||||
const res = await instance.post(POST_OPERATION_RECORD_API, formData);
|
const res = await instance.post(POST_OPERATION_RECORD_API, formData);
|
||||||
|
|
||||||
@ -59,10 +61,8 @@ export const postOperationRecord = async (formData) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAlertSubList = async (building_guid) => {
|
export const getAlertSubList = async () => {
|
||||||
const res = await instance.post(GET_ALERT_SUB_LIST_API, {
|
const res = await instance.post(GET_ALERT_SUB_LIST_API, {});
|
||||||
building_guid,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
return apihandler(res.code, res.data, {
|
||||||
msg: res.msg,
|
msg: res.msg,
|
||||||
@ -148,15 +148,6 @@ export const getOutliersPoints = async (id) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getFactors = async () => {
|
|
||||||
const res = await instance.post(GET_FACTOR_API);
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postOutliersSetting = async (data) => {
|
export const postOutliersSetting = async (data) => {
|
||||||
const res = await instance.post(POST_OUTLIERS_SETTING_API, data);
|
const res = await instance.post(POST_OUTLIERS_SETTING_API, data);
|
||||||
|
|
||||||
@ -165,63 +156,3 @@ export const postOutliersSetting = async (data) => {
|
|||||||
code: res.code,
|
code: res.code,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const delOutliersSetting = async (Id) => {
|
|
||||||
const res = await instance.post(DELETE_OUTLIERS_SETTING_API, {
|
|
||||||
Id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getShowAlarm = async () => {
|
|
||||||
const res = await instance.post(GET_SHOW_ALERT_API);
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getAlarmScheduleList = async () => {
|
|
||||||
const res = await instance.post(GET_ALERT_SCHEDULE_LIST_API, {});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postAlertSchedule = async (data) => {
|
|
||||||
const res = await instance.post(POST_ALERT_SCHEDULE, data);
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteAlarmSchedule = async (id) => {
|
|
||||||
try {
|
|
||||||
const res = await instance.post(DELETE_ALERT_SCHEDULE, { id });
|
|
||||||
return {
|
|
||||||
isSuccess: res.code === "0000",
|
|
||||||
msg: res.msg || "刪除成功",
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("API request failed", error);
|
|
||||||
return { isSuccess: false, msg: "API request failed" };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postMQTTRefresh = async () => {
|
|
||||||
const res = await instance.post(POST_ALERT_MQTT_REFRESH);
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
export const GET_ASSET_MAIN_LIST_API = `/AssetManage/GetAssetMainList`;
|
|
||||||
export const DELETE_ASSET_MAIN_LIST_API = `/AssetManage/DeleteAssetMain`;
|
|
||||||
export const POST_ASSET_MAIN_LIST_API = `/AssetManage/SaveAssetMain`;
|
|
||||||
|
|
||||||
export const GET_ASSET_SUB_LIST_API = `/AssetManage/GetAssetSubList`;
|
export const GET_ASSET_SUB_LIST_API = `/AssetManage/GetAssetSubList`;
|
||||||
|
export const GET_ASSET_MAIN_LIST_API = `/AssetManage/GetAssetMainList`;
|
||||||
export const POST_ASSET_SUB_LIST_API = `/AssetManage/SaveAssetSub`;
|
export const POST_ASSET_SUB_LIST_API = `/AssetManage/SaveAssetSub`;
|
||||||
export const DELETE_ASSET_SUB_LIST_API = `/AssetManage/DeleteAssetSub`;
|
export const DELETE_ASSET_SUB_LIST_API = `/AssetManage/DeleteAssetSub`;
|
||||||
|
|
||||||
@ -18,22 +15,3 @@ export const DELETE_ASSET_FLOOR_API = `/AssetManage/DeleteFloor`;
|
|||||||
|
|
||||||
export const GET_ASSET_IOT_LIST_API = `/AssetManage/GetIOTList`;
|
export const GET_ASSET_IOT_LIST_API = `/AssetManage/GetIOTList`;
|
||||||
export const GET_ASSET_SUB_POINT_API = `/AssetManage/GetSubPoint`;
|
export const GET_ASSET_SUB_POINT_API = `/AssetManage/GetSubPoint`;
|
||||||
|
|
||||||
export const GET_ASSET_IOT_SCHEMA_API = `/AssetManage/GetResponseSchema`;
|
|
||||||
export const POST_ASSET_IOT_SCHEMA_API = `/AssetManage/SaveResponseSchema`;
|
|
||||||
|
|
||||||
export const GET_ASSET_DEVICE_ITEM_API = `/AssetManage/GetDeviceItem`;
|
|
||||||
export const POST_ASSET_DEVICE_ITEM_API = `/AssetManage/SaveDeviceItem`;
|
|
||||||
export const DELETE_ASSET_DEVICE_ITEM_API = `/AssetManage/DeleteDeviceItem`;
|
|
||||||
|
|
||||||
export const GET_ASSET_DEPARTMENT_API = `/AssetManage/GetDepartment`;
|
|
||||||
export const POST_ASSET_DEPARTMENT_API = `/AssetManage/SaveDepartment`;
|
|
||||||
export const DELETE_ASSET_DEPARTMENT_API = `/AssetManage/DeleteDepartment`;
|
|
||||||
|
|
||||||
export const GET_ASSET_ELECTYPE_API = `/AssetManage/GetElecType`;
|
|
||||||
export const POST_ASSET_ELECTYPE_API = `/AssetManage/SaveElecType`;
|
|
||||||
export const DELETE_ASSET_ELECTYPE_API = `/AssetManage/DeleteElecType`;
|
|
||||||
|
|
||||||
export const POST_ASSET_ELEC_SETTING_API = `/AssetManage/SaveAssetSetting`;
|
|
||||||
|
|
||||||
export const POST_ASSET_MQTT_PUBLISH_API = `/api/mqtt/publish`;
|
|
||||||
@ -1,8 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
GET_ASSET_MAIN_LIST_API,
|
|
||||||
DELETE_ASSET_MAIN_LIST_API,
|
|
||||||
POST_ASSET_MAIN_LIST_API,
|
|
||||||
GET_ASSET_SUB_LIST_API,
|
GET_ASSET_SUB_LIST_API,
|
||||||
|
GET_ASSET_MAIN_LIST_API,
|
||||||
DELETE_ASSET_SUB_LIST_API,
|
DELETE_ASSET_SUB_LIST_API,
|
||||||
POST_ASSET_SUB_LIST_API,
|
POST_ASSET_SUB_LIST_API,
|
||||||
GET_ASSET_LIST_API,
|
GET_ASSET_LIST_API,
|
||||||
@ -14,26 +12,13 @@ import {
|
|||||||
DELETE_ASSET_ITEM_API,
|
DELETE_ASSET_ITEM_API,
|
||||||
POST_ASSET_SINGLE_API,
|
POST_ASSET_SINGLE_API,
|
||||||
GET_ASSET_SUB_POINT_API,
|
GET_ASSET_SUB_POINT_API,
|
||||||
GET_ASSET_IOT_SCHEMA_API,
|
|
||||||
POST_ASSET_IOT_SCHEMA_API,
|
|
||||||
GET_ASSET_DEVICE_ITEM_API,
|
|
||||||
POST_ASSET_DEVICE_ITEM_API,
|
|
||||||
DELETE_ASSET_DEVICE_ITEM_API,
|
|
||||||
GET_ASSET_DEPARTMENT_API,
|
|
||||||
POST_ASSET_DEPARTMENT_API,
|
|
||||||
DELETE_ASSET_DEPARTMENT_API,
|
|
||||||
GET_ASSET_ELECTYPE_API,
|
|
||||||
POST_ASSET_ELECTYPE_API,
|
|
||||||
DELETE_ASSET_ELECTYPE_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 getAssetSubList = async () => {
|
||||||
const res = await instance.post(GET_ASSET_MAIN_LIST_API, { building_guid });
|
const res = await instance.post(GET_ASSET_SUB_LIST_API);
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
return apihandler(res.code, res.data, {
|
||||||
msg: res.msg,
|
msg: res.msg,
|
||||||
@ -41,8 +26,8 @@ export const getAssetMainList = async (building_guid) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteAssetMainItem = async (id) => {
|
export const getAssetMainList = async () => {
|
||||||
const res = await instance.post(DELETE_ASSET_MAIN_LIST_API, { id });
|
const res = await instance.post(GET_ASSET_MAIN_LIST_API);
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
return apihandler(res.code, res.data, {
|
||||||
msg: res.msg,
|
msg: res.msg,
|
||||||
@ -50,17 +35,17 @@ export const deleteAssetMainItem = async (id) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const postAssetMainList = async ({
|
export const postAssetSubList = async ({
|
||||||
id,
|
|
||||||
system_key,
|
system_key,
|
||||||
system_value,
|
system_value,
|
||||||
building_guid,
|
system_parent_id,
|
||||||
|
id,
|
||||||
}) => {
|
}) => {
|
||||||
const res = await instance.post(POST_ASSET_MAIN_LIST_API, {
|
const res = await instance.post(POST_ASSET_SUB_LIST_API, {
|
||||||
id,
|
|
||||||
system_key,
|
system_key,
|
||||||
system_value,
|
system_value,
|
||||||
building_guid,
|
system_parent_id,
|
||||||
|
id,
|
||||||
});
|
});
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
return apihandler(res.code, res.data, {
|
||||||
@ -69,24 +54,6 @@ export const postAssetMainList = async ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAssetSubList = async (id) => {
|
|
||||||
const res = await instance.post(GET_ASSET_SUB_LIST_API, { id });
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postAssetSubList = async (formData) => {
|
|
||||||
const res = await instance.post(POST_ASSET_SUB_LIST_API, formData);
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteAssetSubItem = async (id) => {
|
export const deleteAssetSubItem = async (id) => {
|
||||||
const res = await instance.post(DELETE_ASSET_SUB_LIST_API, { id });
|
const res = await instance.post(DELETE_ASSET_SUB_LIST_API, { id });
|
||||||
|
|
||||||
@ -133,17 +100,18 @@ export const postAssetSingle = async (data) => {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
value.forEach((element, index) => {
|
value.forEach((element, index) => {
|
||||||
formData.append(
|
formData.append(`sub_device[${index}].device_number`, element.device_number);
|
||||||
`sub_device[${index}].device_number`,
|
|
||||||
element.device_number
|
|
||||||
);
|
|
||||||
formData.append(`sub_device[${index}].points`, element.points);
|
formData.append(`sub_device[${index}].points`, element.points);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (key === "device_number" && value === "") {
|
||||||
|
formData.append(key, "0");
|
||||||
} else {
|
} else {
|
||||||
formData.append(key, value);
|
formData.append(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const res = await instance.post(POST_ASSET_SINGLE_API, formData);
|
const res = await instance.post(POST_ASSET_SINGLE_API, formData);
|
||||||
return apihandler(res.code, res.data, { msg: res.msg, code: res.code });
|
return apihandler(res.code, res.data, { msg: res.msg, code: res.code });
|
||||||
@ -158,8 +126,8 @@ export const deleteAssetItem = async (main_id) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAssetFloorList = async (building_guid) => {
|
export const getAssetFloorList = async () => {
|
||||||
const res = await instance.post(GET_ASSET_FLOOR_LIST_API, { building_guid });
|
const res = await instance.post(GET_ASSET_FLOOR_LIST_API);
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
return apihandler(res.code, res.data, {
|
||||||
msg: res.msg,
|
msg: res.msg,
|
||||||
@ -207,155 +175,3 @@ export const getAssetSubPoint = async (sub_system_tag) => {
|
|||||||
code: res.code,
|
code: res.code,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getIOTSchema = async (variable_id) => {
|
|
||||||
const res = await instance.post(GET_ASSET_IOT_SCHEMA_API, { variable_id });
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postIOTSchema = async ({ name, variable_id, points }) => {
|
|
||||||
const res = await instance.post(POST_ASSET_IOT_SCHEMA_API, {
|
|
||||||
name,
|
|
||||||
variable_id,
|
|
||||||
points,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getDeviceItem = async (variable_id) => {
|
|
||||||
const res = await instance.post(GET_ASSET_DEVICE_ITEM_API, { variable_id });
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postDeviceItem = async ({
|
|
||||||
id,
|
|
||||||
variable_id,
|
|
||||||
full_name,
|
|
||||||
points,
|
|
||||||
decimals,
|
|
||||||
is_bool,
|
|
||||||
is_link,
|
|
||||||
show_event_switch_btn,
|
|
||||||
event_switch_on_message,
|
|
||||||
event_switch_off_message,
|
|
||||||
}) => {
|
|
||||||
const res = await instance.post(POST_ASSET_DEVICE_ITEM_API, {
|
|
||||||
id,
|
|
||||||
variable_id,
|
|
||||||
full_name,
|
|
||||||
points,
|
|
||||||
decimals,
|
|
||||||
is_bool,
|
|
||||||
is_link,
|
|
||||||
show_event_switch_btn,
|
|
||||||
event_switch_on_message,
|
|
||||||
event_switch_off_message,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteDeviceItem = async (id) => {
|
|
||||||
const res = await instance.post(DELETE_ASSET_DEVICE_ITEM_API, { id });
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getDepartmentList = async () => {
|
|
||||||
const res = await instance.post(GET_ASSET_DEPARTMENT_API, {});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postDepartmentList = async ({ name, id }) => {
|
|
||||||
const res = await instance.post(POST_ASSET_DEPARTMENT_API, {
|
|
||||||
name,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteDepartmentItem = async (id) => {
|
|
||||||
const res = await instance.post(DELETE_ASSET_DEPARTMENT_API, { id });
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getElecTypeList = async () => {
|
|
||||||
const res = await instance.post(GET_ASSET_ELECTYPE_API, {});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postElecTypeList = async ({ name, id }) => {
|
|
||||||
const res = await instance.post(POST_ASSET_ELECTYPE_API, {
|
|
||||||
name,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteElecTypeItem = async (id) => {
|
|
||||||
const res = await instance.post(DELETE_ASSET_ELECTYPE_API, { id });
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postAssetElecSetting = async (formData) => {
|
|
||||||
const res = await instance.post(POST_ASSET_ELEC_SETTING_API, formData);
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
export const GET_BUILDING_API = `/AssetManage/GetBuildingList`;
|
export const GET_BUILDING_API = `/api/Device/GetBuild`;
|
||||||
export const POST_BUILDING_API = `/AssetManage/SaveBuilding`;
|
|
||||||
export const DELETE_BUILDING_API = `/AssetManage/DeleteBuilding`;
|
|
||||||
export const GET_AUTHPAGE_API = `/api/GetUsrFroList`;
|
export const GET_AUTHPAGE_API = `/api/GetUsrFroList`;
|
||||||
export const GET_SUBAUTHPAGE_API = `/api/Device/GetMainSub`;
|
export const GET_SUBAUTHPAGE_API = `/api/Device/GetMainSub`;
|
||||||
export const GET_ALL_DEVICE_API = `/api/Device/GetAllDevice`;
|
export const GET_ALL_DEVICE_API = `/api/Device/GetAllDevice`;
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
GET_BUILDING_API,
|
GET_BUILDING_API,
|
||||||
POST_BUILDING_API,
|
|
||||||
DELETE_BUILDING_API,
|
|
||||||
GET_AUTHPAGE_API,
|
GET_AUTHPAGE_API,
|
||||||
GET_SUBAUTHPAGE_API,
|
GET_SUBAUTHPAGE_API,
|
||||||
GET_ALL_DEVICE_API,
|
GET_ALL_DEVICE_API,
|
||||||
@ -18,27 +16,6 @@ export const getBuildings = async () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const postBuildings = async ({ full_name, building_guid }) => {
|
|
||||||
const res = await instance.post(POST_BUILDING_API, {
|
|
||||||
full_name,
|
|
||||||
building_guid,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteBuildings = async (building_guid) => {
|
|
||||||
const res = await instance.post(DELETE_BUILDING_API, { building_guid });
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getAuth = async (lang) => {
|
export const getAuth = async (lang) => {
|
||||||
const res = await instance.post(GET_AUTHPAGE_API, {
|
const res = await instance.post(GET_AUTHPAGE_API, {
|
||||||
lang,
|
lang,
|
||||||
@ -49,8 +26,10 @@ export const getAuth = async (lang) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAllSysSidebar = async (building_guid) => {
|
export const getAllSysSidebar = async () => {
|
||||||
const res = await instance.post(GET_SUBAUTHPAGE_API, {building_guid});
|
const res = await instance.post(GET_SUBAUTHPAGE_API, {
|
||||||
|
building_tag: "",
|
||||||
|
});
|
||||||
return apihandler(res.code, res.data, {
|
return apihandler(res.code, res.data, {
|
||||||
msg: res.msg,
|
msg: res.msg,
|
||||||
code: res.code,
|
code: res.code,
|
||||||
|
|||||||
@ -7,11 +7,3 @@ export const GET_DASHBOARD_ENERGY_API = `/SituationRoom/GetEnergeData`;
|
|||||||
export const POST_DASHBOARD_PRODUCT_TARGET_SETTING_API = `/SituationRoom/SetTargetSetting`;
|
export const POST_DASHBOARD_PRODUCT_TARGET_SETTING_API = `/SituationRoom/SetTargetSetting`;
|
||||||
export const GET_DASHBOARD_PRODUCT_TARGET_SETTING_API = `/SituationRoom/GetTargetSetting`
|
export const GET_DASHBOARD_PRODUCT_TARGET_SETTING_API = `/SituationRoom/GetTargetSetting`
|
||||||
export const GET_DASHBOARD_PRODUCT_HISTORY_API = `/SituationRoom/GetProductionHistory`
|
export const GET_DASHBOARD_PRODUCT_HISTORY_API = `/SituationRoom/GetProductionHistory`
|
||||||
|
|
||||||
export const GET_DASHBOARD_ENERGY_INFO_API = `api/dashboard/GetEnergyInfo`
|
|
||||||
export const GET_DASHBOARD_ENERGY_COST_API = `api/dashboard/GetEnergyCost`
|
|
||||||
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`
|
|
||||||
|
|
||||||
|
|||||||
@ -8,11 +8,6 @@ import {
|
|||||||
POST_DASHBOARD_PRODUCT_TARGET_SETTING_API,
|
POST_DASHBOARD_PRODUCT_TARGET_SETTING_API,
|
||||||
GET_DASHBOARD_PRODUCT_TARGET_SETTING_API,
|
GET_DASHBOARD_PRODUCT_TARGET_SETTING_API,
|
||||||
GET_DASHBOARD_PRODUCT_HISTORY_API,
|
GET_DASHBOARD_PRODUCT_HISTORY_API,
|
||||||
GET_DASHBOARD_ENERGY_INFO_API,
|
|
||||||
GET_DASHBOARD_ENERGY_COST_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";
|
||||||
@ -140,60 +135,3 @@ export const getDashboardProductRecord = async ({ start_time, end_time }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getEnergyInfo = async (building_guid) => {
|
|
||||||
const res = await instance.post(GET_DASHBOARD_ENERGY_INFO_API, {
|
|
||||||
building_guid,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getEnergyCost = async ({
|
|
||||||
department_id,
|
|
||||||
floor_guid,
|
|
||||||
building_guid,
|
|
||||||
}) => {
|
|
||||||
const res = await instance.post(GET_DASHBOARD_ENERGY_COST_API, {
|
|
||||||
department_id,
|
|
||||||
floor_guid,
|
|
||||||
building_guid,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getAlarmOperationInfo = async (building_guid) => {
|
|
||||||
const res = await instance.post(GET_DASHBOARD_ALARMOPERATION_INFO_API, {
|
|
||||||
building_guid,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@ -1,23 +1,3 @@
|
|||||||
export const GET_REALTIME_DIST_API = `/api/Energe/GetRealTimeDistribution`;
|
export const GET_REALTIME_DIST_API = `/api/Energe/GetRealTimeDistribution`;
|
||||||
export const GET_ELECUSE_DAY_API = `/api/Energe/GetElecUseDay`;
|
export const GET_ELECUSE_DAY_API = `/api/Energe/GetElecUseDay`;
|
||||||
export const GET_TAI_POWER_API = `/api/Energe/GetTaipower`;
|
export const GET_TAI_POWER_API = `/api/Energe/GetTaipower`;
|
||||||
|
|
||||||
export const GET_SIDEBAR_API = `/api/GetSideBar`;
|
|
||||||
export const GET_SEARCH_API = `/api/Energe/GetFilter`;
|
|
||||||
|
|
||||||
export const GET_REPORT_API = `/api/Energe/GetReport`;
|
|
||||||
export const GET_Excel_API = `/api/Energe/GetReportExcel`;
|
|
||||||
|
|
||||||
// 即時需量
|
|
||||||
export const GET_DEMAND_API = `/api/Energe/SearchDemandValue`;
|
|
||||||
export const POST_ADD_DEMAND_API = `/api/Energe/AddDemandValue`;
|
|
||||||
export const POST_EDIT_DEMAND_API = `/api/Energe/UpdateDemandValue`;
|
|
||||||
export const GET_REALTIME_DEMAND_API = `/api/Energe/GetRealTimeDemand`;
|
|
||||||
|
|
||||||
// 碳排係數
|
|
||||||
export const GET_CARBON_API = `/api/Energe/SearchCarbonValue`;
|
|
||||||
export const POST_EDIT_CARBON_API = `/api/Energe/UpdateCarbonValue`;
|
|
||||||
|
|
||||||
// 時間電價
|
|
||||||
export const GET_TIME_ELEC_API = `/api/Energe/SearchTimeElec`;
|
|
||||||
export const POST_TIME_ELEC_API = `/api/Energe/UpdateTimeElecValue`;
|
|
||||||
@ -2,32 +2,12 @@ import {
|
|||||||
GET_REALTIME_DIST_API,
|
GET_REALTIME_DIST_API,
|
||||||
GET_ELECUSE_DAY_API,
|
GET_ELECUSE_DAY_API,
|
||||||
GET_TAI_POWER_API,
|
GET_TAI_POWER_API,
|
||||||
GET_SIDEBAR_API,
|
|
||||||
GET_SEARCH_API,
|
|
||||||
GET_REPORT_API,
|
|
||||||
GET_Excel_API,
|
|
||||||
GET_DEMAND_API,
|
|
||||||
POST_EDIT_DEMAND_API,
|
|
||||||
GET_REALTIME_DEMAND_API,
|
|
||||||
GET_CARBON_API,
|
|
||||||
POST_EDIT_CARBON_API,
|
|
||||||
GET_TIME_ELEC_API,
|
|
||||||
POST_TIME_ELEC_API,
|
|
||||||
} from "./api";
|
} from "./api";
|
||||||
import instance, { fileInstance } from "@/util/request";
|
import instance from "@/util/request";
|
||||||
import apihandler from "@/util/apihandler";
|
import apihandler from "@/util/apihandler";
|
||||||
import downloadExcel from "@/util/downloadExcel";
|
|
||||||
|
|
||||||
export const getRealTimeDist = async ({
|
export const getRealTimeDist = async () => {
|
||||||
building_guid,
|
const res = await instance.post(GET_REALTIME_DIST_API);
|
||||||
department_id_list,
|
|
||||||
floor_guid_list,
|
|
||||||
}) => {
|
|
||||||
const res = await instance.post(GET_REALTIME_DIST_API, {
|
|
||||||
building_guid,
|
|
||||||
department_id_list,
|
|
||||||
floor_guid_list,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
return apihandler(res.code, res.data, {
|
||||||
msg: res.msg,
|
msg: res.msg,
|
||||||
@ -35,16 +15,8 @@ export const getRealTimeDist = async ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getElecUseDay = async ({
|
export const getElecUseDay = async () => {
|
||||||
building_guid,
|
const res = await instance.post(GET_ELECUSE_DAY_API);
|
||||||
department_id_list,
|
|
||||||
floor_guid_list,
|
|
||||||
}) => {
|
|
||||||
const res = await instance.post(GET_ELECUSE_DAY_API,{
|
|
||||||
building_guid,
|
|
||||||
department_id_list,
|
|
||||||
floor_guid_list,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
return apihandler(res.code, res.data, {
|
||||||
msg: res.msg,
|
msg: res.msg,
|
||||||
@ -52,178 +24,8 @@ export const getElecUseDay = async ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getTaipower = async ({
|
export const getTaipower = async () => {
|
||||||
coefficient,
|
const res = await instance.post(GET_TAI_POWER_API);
|
||||||
building_guid,
|
|
||||||
department_id_list,
|
|
||||||
floor_guid_list,
|
|
||||||
}) => {
|
|
||||||
const res = await instance.post(GET_TAI_POWER_API, {
|
|
||||||
coefficient,
|
|
||||||
building_guid,
|
|
||||||
department_id_list,
|
|
||||||
floor_guid_list,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getSideBar = async (system_type) => {
|
|
||||||
const res = await instance.post(GET_SIDEBAR_API, { system_type });
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getEnergySearch = async (type) => {
|
|
||||||
const res = await instance.post(GET_SEARCH_API, { type });
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getReport = async ({
|
|
||||||
department,
|
|
||||||
elecType,
|
|
||||||
floor,
|
|
||||||
start_time,
|
|
||||||
end_time,
|
|
||||||
type,
|
|
||||||
}) => {
|
|
||||||
const res = await instance.post(GET_REPORT_API, {
|
|
||||||
department,
|
|
||||||
elecType,
|
|
||||||
floor,
|
|
||||||
start_time,
|
|
||||||
end_time,
|
|
||||||
type,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getExcel = async ({
|
|
||||||
department,
|
|
||||||
elecType,
|
|
||||||
floor,
|
|
||||||
start_time,
|
|
||||||
end_time,
|
|
||||||
type,
|
|
||||||
}) => {
|
|
||||||
const res = await fileInstance.post(
|
|
||||||
GET_Excel_API,
|
|
||||||
{
|
|
||||||
department,
|
|
||||||
elecType,
|
|
||||||
floor,
|
|
||||||
start_time,
|
|
||||||
end_time,
|
|
||||||
type,
|
|
||||||
},
|
|
||||||
{ responseType: "blob" }
|
|
||||||
);
|
|
||||||
|
|
||||||
return apihandler(
|
|
||||||
res.code,
|
|
||||||
res,
|
|
||||||
{
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
},
|
|
||||||
downloadExcel
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getDemand = async (building_guid) => {
|
|
||||||
const res = await instance.post(GET_DEMAND_API, { building_guid });
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postEditDemand = async ({
|
|
||||||
id,
|
|
||||||
contract,
|
|
||||||
alert,
|
|
||||||
reset,
|
|
||||||
building_guid,
|
|
||||||
}) => {
|
|
||||||
const res = await instance.put(POST_EDIT_DEMAND_API, {
|
|
||||||
id,
|
|
||||||
contract,
|
|
||||||
alert,
|
|
||||||
reset,
|
|
||||||
building_guid,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getCarbonValue = async (building_guid) => {
|
|
||||||
const res = await instance.post(GET_CARBON_API, { building_guid });
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postEditCarbonValue = async ({
|
|
||||||
id,
|
|
||||||
coefficient,
|
|
||||||
building_guid,
|
|
||||||
}) => {
|
|
||||||
const res = await instance.put(POST_EDIT_CARBON_API, {
|
|
||||||
id,
|
|
||||||
coefficient,
|
|
||||||
building_guid,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getRealTimeDemand = async (building_guid) => {
|
|
||||||
const res = await instance.post(GET_REALTIME_DEMAND_API, { building_guid });
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getTimeElec = async (building_guid) => {
|
|
||||||
const res = await instance.post(GET_TIME_ELEC_API, { building_guid });
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const postTimeElec = async ({ sheet, cost, building_guid }) => {
|
|
||||||
const res = await instance.put(POST_TIME_ELEC_API, {
|
|
||||||
sheet,
|
|
||||||
cost,
|
|
||||||
building_guid,
|
|
||||||
});
|
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
return apihandler(res.code, res.data, {
|
||||||
msg: res.msg,
|
msg: res.msg,
|
||||||
|
|||||||
@ -4,10 +4,6 @@ export const GET_HISTORY_SIDEBAR_API = `/api/History/GetDeviceInfo`;
|
|||||||
export const GET_HISTORY_POINT_API = `/api/History/GetAllDevPoi`;
|
export const GET_HISTORY_POINT_API = `/api/History/GetAllDevPoi`;
|
||||||
export const GET_HISTORY_DATA_API = `/api/History/GetHistoryData`;
|
export const GET_HISTORY_DATA_API = `/api/History/GetHistoryData`;
|
||||||
export const GET_HISTORY_EXPORT_API = `/api/ExportHistoryExcel`;
|
export const GET_HISTORY_EXPORT_API = `/api/ExportHistoryExcel`;
|
||||||
export const GET_HISTORY_EXPORT_REPORT_API = `/api/History/GetHistoryExcelReport`;
|
|
||||||
export const GET_HISTORY_EXPORT_CURVE_API = `/api/History/GetHistoricalCurveExcelReport`;
|
|
||||||
export const GET_HISTORY_EXPORT_QUICK_API = `/api/History/GetQuickMeteringExcelReport`;
|
|
||||||
export const GET_HISTORY_EXPORT_CLASS_API = `/api/History/GetElectricityClassificationExcelReport`;
|
|
||||||
|
|
||||||
export const GET_HISTORY_FAVORITE_API = `/api/History/GetHistoryFavorite`;
|
export const GET_HISTORY_FAVORITE_API = `/api/History/GetHistoryFavorite`;
|
||||||
export const POST_HISTORY_FAVORITE_API = `/api/History/SaveHistoryFavorite`;
|
export const POST_HISTORY_FAVORITE_API = `/api/History/SaveHistoryFavorite`;
|
||||||
|
|||||||
@ -7,26 +7,14 @@ import {
|
|||||||
DELETE_HISTORY_FAVORITE_API,
|
DELETE_HISTORY_FAVORITE_API,
|
||||||
UPDATE_HISTORY_FAVORITE_API,
|
UPDATE_HISTORY_FAVORITE_API,
|
||||||
GET_HISTORY_EXPORT_API,
|
GET_HISTORY_EXPORT_API,
|
||||||
GET_HISTORY_EXPORT_REPORT_API,
|
|
||||||
GET_HISTORY_EXPORT_CURVE_API,
|
|
||||||
GET_HISTORY_EXPORT_QUICK_API,
|
|
||||||
GET_HISTORY_EXPORT_CLASS_API,
|
|
||||||
} from "./api";
|
} from "./api";
|
||||||
import instance, { fileInstance } from "@/util/request";
|
import instance, { fileInstance } from "@/util/request";
|
||||||
import apihandler from "@/util/apiHandler";
|
import apihandler from "@/util/apiHandler";
|
||||||
import downloadExcel from "@/util/downloadExcel";
|
import downloadExcel from "@/util/downloadExcel";
|
||||||
|
|
||||||
export const getHistorySideBar = async ({
|
export const getHistorySideBar = async (sub_system_tag) => {
|
||||||
sub_system_tag,
|
|
||||||
department_id,
|
|
||||||
elec_type_id,
|
|
||||||
building_guid,
|
|
||||||
}) => {
|
|
||||||
const res = await instance.post(GET_HISTORY_SIDEBAR_API, {
|
const res = await instance.post(GET_HISTORY_SIDEBAR_API, {
|
||||||
sub_system_tag,
|
sub_system_tag,
|
||||||
department_id,
|
|
||||||
elec_type_id,
|
|
||||||
building_guid,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
return apihandler(res.code, res.data, {
|
||||||
@ -54,7 +42,6 @@ export const getHistoryData = async ({
|
|||||||
End_time,
|
End_time,
|
||||||
Device_list,
|
Device_list,
|
||||||
Points,
|
Points,
|
||||||
table_type,
|
|
||||||
}) => {
|
}) => {
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
@ -75,7 +62,6 @@ export const getHistoryData = async ({
|
|||||||
Device_list: Array.isArray(Device_list) ? Device_list : [Device_list],
|
Device_list: Array.isArray(Device_list) ? Device_list : [Device_list],
|
||||||
Points: Array.isArray(Points) ? Points : [Points],
|
Points: Array.isArray(Points) ? Points : [Points],
|
||||||
Type: parseInt(Type),
|
Type: parseInt(Type),
|
||||||
table_type: parseInt(table_type),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
return apihandler(res.code, res.data, {
|
||||||
@ -85,52 +71,7 @@ export const getHistoryData = async ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getHistoryExportData = async ({
|
export const getHistoryExportData = async ({
|
||||||
Start_date,
|
|
||||||
End_date,
|
|
||||||
Start_time,
|
|
||||||
End_time,
|
|
||||||
Device_list,
|
|
||||||
Points,
|
|
||||||
Type,
|
Type,
|
||||||
table_type,
|
|
||||||
}) => {
|
|
||||||
const api =
|
|
||||||
parseInt(table_type) === 1
|
|
||||||
? GET_HISTORY_EXPORT_CURVE_API
|
|
||||||
: parseInt(table_type) === 2
|
|
||||||
? GET_HISTORY_EXPORT_QUICK_API
|
|
||||||
: parseInt(table_type) === 3
|
|
||||||
? GET_HISTORY_EXPORT_CLASS_API
|
|
||||||
: GET_HISTORY_EXPORT_API;
|
|
||||||
|
|
||||||
const res = await fileInstance.post(
|
|
||||||
api,
|
|
||||||
{
|
|
||||||
Start_date: Start_date,
|
|
||||||
End_date: End_date,
|
|
||||||
Start_time: Start_time,
|
|
||||||
End_time: End_time,
|
|
||||||
Points: Array.isArray(Points) ? Points : [Points],
|
|
||||||
Device_list: Array.isArray(Device_list) ? Device_list : [Device_list],
|
|
||||||
Type: parseInt(Type),
|
|
||||||
Building_tag_list: [...new Set(Device_list.map((d) => d.split("_")[1]))],
|
|
||||||
table_type: parseInt(table_type),
|
|
||||||
},
|
|
||||||
{ responseType: "blob" }
|
|
||||||
);
|
|
||||||
|
|
||||||
return apihandler(
|
|
||||||
res.code,
|
|
||||||
res,
|
|
||||||
{
|
|
||||||
msg: res.msg,
|
|
||||||
code: res.code,
|
|
||||||
},
|
|
||||||
downloadExcel
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getHistoryExportReport = async ({
|
|
||||||
Start_date,
|
Start_date,
|
||||||
End_date,
|
End_date,
|
||||||
Start_time,
|
Start_time,
|
||||||
@ -138,8 +79,19 @@ export const getHistoryExportReport = async ({
|
|||||||
Device_list,
|
Device_list,
|
||||||
Points,
|
Points,
|
||||||
}) => {
|
}) => {
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
Type,
|
||||||
|
Start_date,
|
||||||
|
End_date,
|
||||||
|
Start_time,
|
||||||
|
End_time,
|
||||||
|
Device_list,
|
||||||
|
Points,
|
||||||
|
}
|
||||||
|
*/
|
||||||
const res = await fileInstance.post(
|
const res = await fileInstance.post(
|
||||||
GET_HISTORY_EXPORT_REPORT_API,
|
GET_HISTORY_EXPORT_API,
|
||||||
{
|
{
|
||||||
// ...exportContent,
|
// ...exportContent,
|
||||||
Start_date: Start_date,
|
Start_date: Start_date,
|
||||||
@ -148,6 +100,7 @@ export const getHistoryExportReport = async ({
|
|||||||
End_time: End_time,
|
End_time: End_time,
|
||||||
Points: Array.isArray(Points) ? Points : [Points],
|
Points: Array.isArray(Points) ? Points : [Points],
|
||||||
Device_list: Array.isArray(Device_list) ? Device_list : [Device_list],
|
Device_list: Array.isArray(Device_list) ? Device_list : [Device_list],
|
||||||
|
Type: parseInt(Type),
|
||||||
Building_tag_list: [...new Set(Device_list.map((d) => d.split("_")[1]))],
|
Building_tag_list: [...new Set(Device_list.map((d) => d.split("_")[1]))],
|
||||||
},
|
},
|
||||||
{ responseType: "blob" }
|
{ responseType: "blob" }
|
||||||
|
|||||||
@ -13,10 +13,6 @@ export async function Login({ account, password }) {
|
|||||||
document.cookie = `JWT-Authorization=${res.data.token}; Max-Age=${
|
document.cookie = `JWT-Authorization=${res.data.token}; Max-Age=${
|
||||||
24 * 60 * 60 * 1000
|
24 * 60 * 60 * 1000
|
||||||
}`;
|
}`;
|
||||||
// 設定 user_name Cookie
|
|
||||||
document.cookie = `user_name=${res.data.user_name}; Max-Age=${
|
|
||||||
24 * 60 * 60 * 1000
|
|
||||||
}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
return apihandler(res.code, res.data, {
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import {
|
|||||||
POST_OPERATION_RECORD_API,
|
POST_OPERATION_RECORD_API,
|
||||||
GET_OPERATION_EXPORT_API,
|
GET_OPERATION_EXPORT_API,
|
||||||
GET_OPERATION_FORMID_API,
|
GET_OPERATION_FORMID_API,
|
||||||
DELETE_OPERATION_RECORD_API,
|
|
||||||
POST_OPERATION_COMPANY_API,
|
POST_OPERATION_COMPANY_API,
|
||||||
UPDATE_OPERATION_COMPANY_API,
|
UPDATE_OPERATION_COMPANY_API,
|
||||||
DELETE_OPERATION_COMPANY_API,
|
DELETE_OPERATION_COMPANY_API,
|
||||||
@ -24,10 +23,10 @@ export const getOperationRecord = async ({
|
|||||||
}) => {
|
}) => {
|
||||||
const res = await instance.post(GET_OPERATION_RECORD_API, {
|
const res = await instance.post(GET_OPERATION_RECORD_API, {
|
||||||
work_type: parseInt(work_type),
|
work_type: parseInt(work_type),
|
||||||
// start_created_at: dayjs(start_created_at).format("YYYY-MM-DDTHH:mm:ss"),
|
start_created_at: dayjs(start_created_at).format("YYYY-MM-DDTHH:mm:ss"),
|
||||||
// end_created_at: dayjs(end_created_at)
|
end_created_at: dayjs(end_created_at)
|
||||||
// .date(dayjs(end_created_at).get("date") + 1)
|
.date(dayjs(end_created_at).get("date") + 1)
|
||||||
// .format("YYYY-MM-DDTHH:mm:ss"),
|
.format("YYYY-MM-DDTHH:mm:ss"),
|
||||||
serial_number: serial_number || null,
|
serial_number: serial_number || null,
|
||||||
main_system_tag: null,
|
main_system_tag: null,
|
||||||
sub_system_tag:
|
sub_system_tag:
|
||||||
|
|||||||
@ -20,13 +20,13 @@ export const getSystemFloors = async (building_tag, sub_system_tag) => {
|
|||||||
|
|
||||||
export const getSystemDevices = async ({
|
export const getSystemDevices = async ({
|
||||||
sub_system_tag,
|
sub_system_tag,
|
||||||
building_guid,
|
building_tag,
|
||||||
department_id_list,
|
floor_tag,
|
||||||
}) => {
|
}) => {
|
||||||
const res = await instance.post(GET_SYSTEM_DEVICE_LIST_API, {
|
const res = await instance.post(GET_SYSTEM_DEVICE_LIST_API, {
|
||||||
sub_system_tag,
|
sub_system_tag,
|
||||||
building_guid,
|
building_tag,
|
||||||
department_id_list,
|
floor_tag,
|
||||||
});
|
});
|
||||||
|
|
||||||
return apihandler(res.code, res.data, {
|
return apihandler(res.code, res.data, {
|
||||||
|
|||||||
@ -58,7 +58,6 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
scrollbar-color: auto !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|||||||
@ -1,41 +1,25 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
import { getAlertLog } from "@/apis/alert";
|
import useAlarmStore from "@/stores/useAlarmStore";
|
||||||
import dayjs from "dayjs";
|
import { ackSingleAlarm } from "@/apis/building";
|
||||||
|
|
||||||
const dataSource = ref([]);
|
const store = useAlarmStore();
|
||||||
let intervalId = null; // 用來儲存 setInterval 的 ID
|
|
||||||
|
|
||||||
const getAlarmData = async () => {
|
|
||||||
const res = await getAlertLog({
|
|
||||||
isRecovery: 1,
|
|
||||||
Start_date: dayjs().format("YYYY-MM-DD"),
|
|
||||||
End_date: dayjs().format("YYYY-MM-DD"),
|
|
||||||
});
|
|
||||||
dataSource.value = (res.data || []).map((d) => ({ ...d, key: d.id }));
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getAlarmData();
|
store.getAlarmDataFromBaja();
|
||||||
|
|
||||||
intervalId = setInterval(() => {
|
|
||||||
getAlarmData();
|
|
||||||
}, 30 * 1000);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
const ackedAlarm = async (uuid) => {
|
||||||
if (intervalId) {
|
const res = await ackSingleAlarm(uuid);
|
||||||
clearInterval(intervalId);
|
console.log("ackedAlarm", res);
|
||||||
intervalId = null;
|
};
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<ul class="pr-4 min-h-full text-base-content">
|
<ul class="pr-4 min-h-full text-base-content">
|
||||||
<!-- Sidebar content here -->
|
<!-- Sidebar content here -->
|
||||||
<li class="my-3" v-for="alarm in dataSource" :key="alarm.id">
|
<li class="my-3" v-for="alarm in store.alarmData" :key="alarm.uuid">
|
||||||
<div
|
<div
|
||||||
class="w-full shadow-xl border border-success bg-body bg-opacity-80"
|
class="w-full shadow-xl border border-success bg-body bg-opacity-80"
|
||||||
>
|
>
|
||||||
@ -50,22 +34,30 @@ onUnmounted(() => {
|
|||||||
>
|
>
|
||||||
<small>
|
<small>
|
||||||
<span class="mr-4"
|
<span class="mr-4"
|
||||||
>{{ alarm.created_at }}</span
|
>{{ alarm.timestamp_date }} {{ alarm.timestamp_time }}</span
|
||||||
>
|
>
|
||||||
<!-- <font-awesome-icon
|
<font-awesome-icon
|
||||||
:icon="['fas', 'times']"
|
:icon="['fas', 'times']"
|
||||||
size="lg"
|
size="lg"
|
||||||
class="text-white"
|
class="text-white"
|
||||||
/> -->
|
/>
|
||||||
</small>
|
</small>
|
||||||
</p>
|
</p>
|
||||||
<div class="divider my-2"></div>
|
<div class="divider my-2"></div>
|
||||||
<div>
|
<div>
|
||||||
<p>{{ $t("alarm.number") }}:{{ alarm.id }}</p>
|
<p>{{ $t("alarm.number") }}:{{ alarm.uuid }}</p>
|
||||||
<p>{{ $t("alert.alarmClass") }}:{{ alarm.factor }}</p>
|
<!-- <p>異常等級:255</p> -->
|
||||||
<p>{{ $t("alarm.device_name") }}:{{ alarm.device_number }}</p>
|
<p>{{ $t("alarm.category") }}:{{ alarm.alarmClass }}</p>
|
||||||
<p>{{ $t("alert.device_point_name") }}:{{ alarm.points }}</p>
|
<p>{{ $t("alarm.device_name") }}:{{ alarm.full_name }}</p>
|
||||||
<p>{{ $t("alert.error_msg") }}:{{ alarm.reason }}</p>
|
<p>{{ $t("alarm.message") }}:{{ alarm.msg }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="card-actions mt-1 justify-end">
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-success"
|
||||||
|
@click="() => ackedAlarm(alarm.uuid)"
|
||||||
|
>
|
||||||
|
{{ $t("alarm.confirm") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -36,7 +36,7 @@ const toggleErrIcon = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="showErr"
|
v-if="showErr"
|
||||||
class="drawer-side translate-y-20 max-h-[90vh] overflow-x-hidden overflow-y-scroll w-[300px] left-auto right-0"
|
class="drawer-side translate-y-20 max-h-[90vh] overflow-x-hidden overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<AlarmCards />
|
<AlarmCards />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import { onMounted, ref, markRaw, nextTick } from "vue";
|
import { onMounted, ref, markRaw } from "vue";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -24,15 +24,9 @@ async function updateSvg(svg, option) {
|
|||||||
} else {
|
} else {
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
axios.get(svg.path).then(async ({ data }) => {
|
axios.get(svg.path).then(({ data }) => {
|
||||||
echarts.registerMap(svg.full_name, { svg: data });
|
echarts.registerMap(svg.full_name, { svg: data });
|
||||||
await nextTick();
|
|
||||||
// 延遲執行以避免在主進程中調用 setOption
|
|
||||||
setTimeout(() => {
|
|
||||||
if (chart.value && !chart.value.isDisposed()) {
|
|
||||||
chart.value.setOption(option);
|
chart.value.setOption(option);
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
if (props.getCoordinate) {
|
if (props.getCoordinate) {
|
||||||
chart.value.getZr().on("click", function (params) {
|
chart.value.getZr().on("click", function (params) {
|
||||||
var pixelPoint = [params.offsetX, params.offsetY];
|
var pixelPoint = [params.offsetX, params.offsetY];
|
||||||
@ -50,15 +44,11 @@ async function updateSvg(svg, option) {
|
|||||||
value: dataPoint, // 當前座標值
|
value: dataPoint, // 當前座標值
|
||||||
itemStyle: { color: "#0000FF" }, // 設為藍色
|
itemStyle: { color: "#0000FF" }, // 設為藍色
|
||||||
});
|
});
|
||||||
setTimeout(() => {
|
|
||||||
if (chart.value && !chart.value.isDisposed()) {
|
|
||||||
chart.value.setOption({
|
chart.value.setOption({
|
||||||
series: {
|
series: {
|
||||||
data: updatedData,
|
data: updatedData,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -118,6 +118,6 @@ const curWidth = computed(() => {
|
|||||||
</style>
|
</style>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.line {
|
.line {
|
||||||
@apply mr-3 relative z-30 after:absolute after:top-1/2 after:-right-3 after:w-3 after:h-[1px] after:bg-info after:z-10 last:after:h-0;
|
@apply mr-3 relative z-20 after:absolute after:top-1/2 after:-right-3 after:w-3 after:h-[1px] after:bg-info after:z-10 last:after:h-0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,436 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
import { computed, defineProps, provide, ref, watch } from "vue";
|
|
||||||
import Pagination from "@/components/customUI/Pagination.vue";
|
|
||||||
import Checkbox from "@/components/customUI/Checkbox.vue";
|
|
||||||
import draggable from "vuedraggable";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
const { t } = useI18n();
|
|
||||||
/*
|
|
||||||
column={
|
|
||||||
title,key,class, width, filter:Boolean, sort:Boolean
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
const props = defineProps({
|
|
||||||
columns: Array,
|
|
||||||
dataSource: Array,
|
|
||||||
rowKey: String,
|
|
||||||
withStyle: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
withDraggable: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
pagination: { type: Boolean, default: true } || {
|
|
||||||
pageSize: Number,
|
|
||||||
totalPages: Number,
|
|
||||||
totalItems: Number,
|
|
||||||
},
|
|
||||||
loading: Boolean,
|
|
||||||
});
|
|
||||||
|
|
||||||
const currentDataSource = ref([]);
|
|
||||||
const dataSourceStorage = ref([]);
|
|
||||||
const isDraggable = ref(props.withDraggable);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.dataSource,
|
|
||||||
(newValue) => {
|
|
||||||
dataSourceStorage.value = newValue;
|
|
||||||
filterItems.value = Object.fromEntries(
|
|
||||||
props.columns.map((c, i) => [
|
|
||||||
c.key,
|
|
||||||
[...new Set(newValue.map((d) => d[c.key]))].map((name) => ({
|
|
||||||
name,
|
|
||||||
selected: false,
|
|
||||||
})),
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateDataSource = (data) => {
|
|
||||||
console.log("update", data);
|
|
||||||
currentDataSource.value = data;
|
|
||||||
};
|
|
||||||
provide("current_table_data", {
|
|
||||||
updateDataSource,
|
|
||||||
});
|
|
||||||
|
|
||||||
const sortRule = ref({});
|
|
||||||
const filterColumn = ref({});
|
|
||||||
const filterItems = ref({});
|
|
||||||
const selectedFilterItem = ref({});
|
|
||||||
|
|
||||||
const toggleFilterModal = (key) => {
|
|
||||||
let newFilter = Object.assign(filterColumn.value);
|
|
||||||
|
|
||||||
for (let oKey in newFilter) {
|
|
||||||
newFilter[oKey] = key === oKey && !newFilter[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
filterColumn.value = newFilter;
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.columns,
|
|
||||||
(newValue) => {
|
|
||||||
sortRule.value = Object.fromEntries(newValue.map((c) => [c.key, 0]));
|
|
||||||
filterColumn.value = Object.fromEntries(
|
|
||||||
newValue.map((c, i) => [c.key, false])
|
|
||||||
);
|
|
||||||
selectedFilterItem.value = Object.fromEntries(
|
|
||||||
newValue.map((c, i) => [c.key, []])
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
|
||||||
0:取消
|
|
||||||
1:ascending
|
|
||||||
2:descending
|
|
||||||
*/
|
|
||||||
const toggleSortRule = (key) => {
|
|
||||||
let newSort = Object.assign(sortRule.value);
|
|
||||||
|
|
||||||
for (let oKey in newSort) {
|
|
||||||
newSort[oKey] = key === oKey ? newSort[key] : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
sortRule.value = newSort;
|
|
||||||
};
|
|
||||||
|
|
||||||
const sort = (column) => {
|
|
||||||
toggleSortRule(column);
|
|
||||||
const cantSort = ["object", "boolean"];
|
|
||||||
console.log(props.dataSource?.[0][column]);
|
|
||||||
if (cantSort.includes(typeof props.dataSource?.[0][column])) return;
|
|
||||||
// 小->大
|
|
||||||
const newArray = Object.assign(props.dataSource, []).sort((a, b) => {
|
|
||||||
// if (column === "timestamp") {
|
|
||||||
// return dayjs(a[column]).valueOf() - dayjs(b[column]).valueOf();
|
|
||||||
// }
|
|
||||||
if (typeof a[column] === "number") return a[column] - b[column];
|
|
||||||
else if (typeof a[column] === "string") {
|
|
||||||
console.log(a[column], b[column], a[column].localeCompare(b[column]));
|
|
||||||
return a[column].localeCompare(b[column]);
|
|
||||||
}
|
|
||||||
// return parseInt(a[column]) - parseInt(b[column]);
|
|
||||||
});
|
|
||||||
if (sortRule.value[column] === 0) {
|
|
||||||
sortRule.value[column] = 1;
|
|
||||||
dataSourceStorage.value = newArray;
|
|
||||||
} else if (sortRule.value[column] === 1) {
|
|
||||||
sortRule.value[column] = 2;
|
|
||||||
dataSourceStorage.value = newArray.reverse();
|
|
||||||
} else if (sortRule.value[column] === 2) {
|
|
||||||
sortRule.value[column] = 0;
|
|
||||||
dataSourceStorage.value = props.dataSource;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const form = ref(null);
|
|
||||||
|
|
||||||
const onFilter = (key, reset = false) => {
|
|
||||||
const formData = new FormData(form.value);
|
|
||||||
reset && formData.delete(key);
|
|
||||||
for (let [name, value] of formData) {
|
|
||||||
console.log(name, value);
|
|
||||||
}
|
|
||||||
selectedFilterItem.value[key] = formData.getAll(key);
|
|
||||||
toggleFilterModal(key);
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
|
||||||
selectedFilterItem,
|
|
||||||
(newVal) => {
|
|
||||||
let newData = Object.assign(props.dataSource);
|
|
||||||
for (let key in newVal) {
|
|
||||||
if (newVal[key].length > 0) {
|
|
||||||
newData = newData.filter((d) => newVal[key].includes(d[key]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dataSourceStorage.value = newData;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div :class="withStyle ? 'content-box' : 'py-5'">
|
|
||||||
<div class="content-decoration">
|
|
||||||
<button
|
|
||||||
v-if="withDraggable"
|
|
||||||
class="btn btn-sm mb-2"
|
|
||||||
@click.stop.prevent="isDraggable = !isDraggable"
|
|
||||||
>
|
|
||||||
<font-awesome-icon :icon="['fas', 'stream']" />
|
|
||||||
{{ isDraggable ? "開始排序" : "完成排序" }}
|
|
||||||
</button>
|
|
||||||
<slot name="beforeTable"></slot>
|
|
||||||
<form ref="form" class="overflow-x-auto">
|
|
||||||
<table
|
|
||||||
:class="
|
|
||||||
twMerge(
|
|
||||||
withStyle ? 'table' : 'table border',
|
|
||||||
currentDataSource.length === 0 ? 'h-28' : ''
|
|
||||||
)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<!-- head -->
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
v-for="column in columns"
|
|
||||||
:key="column.key"
|
|
||||||
:class="`${column.class ? column.class : ''}`"
|
|
||||||
:style="{
|
|
||||||
width: `${
|
|
||||||
column.width
|
|
||||||
? typeof column.width === 'string'
|
|
||||||
? column.width
|
|
||||||
: column.width + 'px'
|
|
||||||
: 'auto'
|
|
||||||
}`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<span class="flex justify-center">
|
|
||||||
{{ column.title }}
|
|
||||||
<div
|
|
||||||
v-if="column.sort"
|
|
||||||
class="flex flex-col justify-center w-3 mx-2 relative"
|
|
||||||
@click="() => sort(column.key)"
|
|
||||||
>
|
|
||||||
<font-awesome-icon
|
|
||||||
:icon="['fas', 'sort-up']"
|
|
||||||
:class="
|
|
||||||
twMerge(
|
|
||||||
'absolute top-0',
|
|
||||||
sortRule[column.key] === 1 ? 'text-success' : ''
|
|
||||||
)
|
|
||||||
"
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
<font-awesome-icon
|
|
||||||
:icon="['fas', 'sort-down']"
|
|
||||||
:class="
|
|
||||||
twMerge(
|
|
||||||
'absolute bottom-1',
|
|
||||||
sortRule[column.key] === 2 ? 'text-success' : ''
|
|
||||||
)
|
|
||||||
"
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="ml-2 relative" v-if="column.filter">
|
|
||||||
<font-awesome-icon
|
|
||||||
:icon="['fas', 'filter']"
|
|
||||||
:class="
|
|
||||||
twMerge(
|
|
||||||
filterColumn[column.key] ||
|
|
||||||
selectedFilterItem[column.key].length > 0
|
|
||||||
? 'text-success'
|
|
||||||
: ''
|
|
||||||
)
|
|
||||||
"
|
|
||||||
@click="() => toggleFilterModal(column.key)"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="absolute top-full -left-1/2 z-50"
|
|
||||||
v-if="filterColumn[column.key]"
|
|
||||||
>
|
|
||||||
<div class="card min-w-max bg-body shadow-xl px-10 py-5">
|
|
||||||
<Checkbox
|
|
||||||
v-for="item in filterItems[column.key]"
|
|
||||||
:title="item.name"
|
|
||||||
:value="item.name"
|
|
||||||
:key="item.name"
|
|
||||||
:name="column.key"
|
|
||||||
:checked="
|
|
||||||
selectedFilterItem[column.key].includes(item.name)
|
|
||||||
"
|
|
||||||
className="justify-start"
|
|
||||||
/>
|
|
||||||
<div class="card-actions mt-4 justify-end">
|
|
||||||
<input
|
|
||||||
type="reset"
|
|
||||||
class="btn btn-sm text-white btn-error"
|
|
||||||
:value="t('button.reset')"
|
|
||||||
@click="() => onFilter(column.key, true)"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
class="btn btn-sm btn-success"
|
|
||||||
@click.stop.prevent="() => onFilter(column.key)"
|
|
||||||
>
|
|
||||||
{{ $t("button.submit") }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody v-if="isDraggable">
|
|
||||||
<tr v-if="loading">
|
|
||||||
<td :colspan="columns.length">
|
|
||||||
<Loading />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-else-if="currentDataSource.length == 0">
|
|
||||||
<td :colspan="columns.length">{{ $t("table.no_data") }}</td>
|
|
||||||
</tr>
|
|
||||||
<template v-else :sort="sortRule">
|
|
||||||
<tr
|
|
||||||
v-for="(data, index) in currentDataSource"
|
|
||||||
:key="data.key || data[rowKey]"
|
|
||||||
>
|
|
||||||
<template
|
|
||||||
v-for="column in columns"
|
|
||||||
:key="`${data.key || data[rowKey]}_${column.key}`"
|
|
||||||
>
|
|
||||||
<td
|
|
||||||
:class="column.class"
|
|
||||||
:style="{
|
|
||||||
width: `${
|
|
||||||
column.width
|
|
||||||
? typeof column.width === 'string'
|
|
||||||
? column.width
|
|
||||||
: column.width + 'px'
|
|
||||||
: 'auto'
|
|
||||||
}`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<slot
|
|
||||||
name="bodyCell"
|
|
||||||
v-bind="{ record: data, column, index }"
|
|
||||||
>
|
|
||||||
{{ data[column.key] }}</slot
|
|
||||||
>
|
|
||||||
</td>
|
|
||||||
</template>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
</tbody>
|
|
||||||
<draggable
|
|
||||||
tag="tbody"
|
|
||||||
:list="dataSourceStorage"
|
|
||||||
item-key="rowKey"
|
|
||||||
v-else
|
|
||||||
>
|
|
||||||
<template #item="{ element, index }">
|
|
||||||
<tr :key="element[rowKey] || element.key" >
|
|
||||||
<template
|
|
||||||
v-for="column in columns"
|
|
||||||
:key="`${element[rowKey] || element.key}_${column.key}`"
|
|
||||||
>
|
|
||||||
<td
|
|
||||||
:class="column.class"
|
|
||||||
:style="{
|
|
||||||
width: `
|
|
||||||
${
|
|
||||||
column.width
|
|
||||||
? typeof column.width === 'string'
|
|
||||||
? column.width
|
|
||||||
: column.width + 'px'
|
|
||||||
: 'auto'
|
|
||||||
}`,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<slot
|
|
||||||
name="bodyCell"
|
|
||||||
v-bind="{ record: element, column, index }"
|
|
||||||
>
|
|
||||||
{{ element[column.key] }}</slot
|
|
||||||
>
|
|
||||||
</td>
|
|
||||||
</template>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
</draggable>
|
|
||||||
</table>
|
|
||||||
</form>
|
|
||||||
<slot name="afterTable"></slot>
|
|
||||||
<Pagination
|
|
||||||
:class="twMerge(!isDraggable ? 'hidden' : 'flex')"
|
|
||||||
:pagination="pagination"
|
|
||||||
:dataSource="dataSourceStorage"
|
|
||||||
:sort="sortRule"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="content-decoration2"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
/**資料框**/
|
|
||||||
.content-box {
|
|
||||||
@apply border border-info p-1 relative mb-4 bg-transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-box .table {
|
|
||||||
@apply rounded-none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th {
|
|
||||||
@apply bg-cyan-600 bg-opacity-30 border-r border-b border-white text-lg font-semibold text-white text-center px-2 py-3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table td {
|
|
||||||
@apply border-r border-b border-white text-lg font-semibold text-white text-center px-2 py-3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table tr td:last-child,
|
|
||||||
.table tr:first-child th:last-child {
|
|
||||||
border-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* .table tr:last-child td {
|
|
||||||
border-bottom: v-bind("withStyle ? '0px': '1px'");
|
|
||||||
} */
|
|
||||||
|
|
||||||
/**資料框裝飾**/
|
|
||||||
.content-box::before {
|
|
||||||
@apply absolute top-1 left-1 h-5 w-5 bg-no-repeat z-10 bg-[url('../../assets/img/table/content-box-background01.svg')] bg-center;
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-box::after {
|
|
||||||
@apply absolute bottom-1 right-1 h-5 w-5 bg-no-repeat z-10 bg-[url('../../assets/img/table/content-box-background05.svg')] bg-center;
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
.content-box .content-decoration {
|
|
||||||
@apply bg-normal px-8 py-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* .content-box .content-decoration::before {
|
|
||||||
@apply absolute -top-3 -right-[10px] h-8 w-8 bg-no-repeat z-10 bg-[url('../../assets/img/table/content-box-background02.svg')] bg-center;
|
|
||||||
content: "";
|
|
||||||
} */
|
|
||||||
|
|
||||||
.content-box .content-decoration2::before {
|
|
||||||
@apply absolute -bottom-1 -left-8 h-14 w-14 bg-no-repeat z-10 bg-[url('../../assets/img/table/content-box-background03.svg')] bg-center;
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* .content-box .content-decoration2::after {
|
|
||||||
content: "";
|
|
||||||
background: url(../../assets/img/table/content-box-background04.svg) center
|
|
||||||
center;
|
|
||||||
position: absolute;
|
|
||||||
right: -27px;
|
|
||||||
bottom: -7px;
|
|
||||||
height: 65px;
|
|
||||||
width: 50px;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
z-index: 2;
|
|
||||||
} */
|
|
||||||
</style>
|
|
||||||
@ -64,11 +64,10 @@ watch(
|
|||||||
twMerge(
|
twMerge(
|
||||||
'flex-col text-xl',
|
'flex-col text-xl',
|
||||||
cls,
|
cls,
|
||||||
openChildren.includes(d.key) || open ? 'flex' : 'hidden'
|
openChildren.includes(dataParentKey) || open ? 'flex' : 'hidden'
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
v-for="d in data"
|
v-for="d in data"
|
||||||
:key="d.key"
|
|
||||||
:data-parent="d.key"
|
:data-parent="d.key"
|
||||||
:open="open"
|
:open="open"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
value: Object,
|
value: String,
|
||||||
isTopLabelExist: {
|
isTopLabelExist: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
@ -73,7 +73,7 @@ const curWidth = computed(() => {
|
|||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:required="required"
|
:required="required"
|
||||||
class="text-lg text-white bg-transparent w-full input input-bordered rounded-md px-3 border-info focus-within:border-info read-only:bg-base-300 read-only:text-zinc-300 read-only:border-0 read-only:focus-within:outline-0 read-only:focus:outline-0"
|
class="text-lg text-white bg-transparent w-full input input-bordered rounded-md px-3 border-info focus-within:border-info read-only:bg-base-300 read-only:text-zinc-500 read-only:border-0 read-only:focus-within:outline-0 read-only:focus:outline-0"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
v-else
|
v-else
|
||||||
@ -84,7 +84,7 @@ const curWidth = computed(() => {
|
|||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:required="required"
|
:required="required"
|
||||||
class="text-lg text-white bg-transparent w-full input input-bordered rounded-md px-3 border-info focus-within:border-info read-only:bg-base-300 read-only:text-zinc-300 read-only:border-0 read-only:focus-within:outline-0 read-only:focus:outline-0"
|
class="text-lg text-white bg-transparent w-full input input-bordered rounded-md px-3 border-info focus-within:border-info read-only:bg-base-300 read-only:text-zinc-500 read-only:border-0 read-only:focus-within:outline-0 read-only:focus:outline-0"
|
||||||
/>
|
/>
|
||||||
<div :class="twMerge(isBottomLabelExist ? 'label' : '')">
|
<div :class="twMerge(isBottomLabelExist ? 'label' : '')">
|
||||||
<span class="label-text-alt"><slot name="bottomLeft"></slot></span>
|
<span class="label-text-alt"><slot name="bottomLeft"></slot></span>
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
value: Object,
|
value: String,
|
||||||
isTopLabelExist: {
|
isTopLabelExist: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
|
|||||||
@ -51,7 +51,7 @@ onMounted(() => {
|
|||||||
: 'focus-visible:outline-none backdrop:bg-transparent',
|
: 'focus-visible:outline-none backdrop:bg-transparent',
|
||||||
)" :style="modalStyle" v-draggable="draggable">
|
)" :style="modalStyle" v-draggable="draggable">
|
||||||
<div :class="twMerge(
|
<div :class="twMerge(
|
||||||
'modal-box static rounded-md border border-info py-5 px-6 overflow-y-auto bg-normal',
|
'modal-box static rounded-md border border-info py-5 px-6 overflow-y-scroll bg-normal',
|
||||||
modalClass
|
modalClass
|
||||||
)
|
)
|
||||||
" :style="{ minWidth: isNaN(width) ? width : `${width}px` }">
|
" :style="{ minWidth: isNaN(width) ? width : `${width}px` }">
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { twMerge } from "tailwind-merge";
|
|||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
name: String,
|
name: String,
|
||||||
value: Object,
|
value: String,
|
||||||
items: Array,
|
items: Array,
|
||||||
isLabelExist: {
|
isLabelExist: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|||||||
@ -1,128 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { defineProps, ref, computed, watch, nextTick, watchEffect } from "vue";
|
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
/* --------------------------------------------------------------
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
key: unique,
|
|
||||||
content: "",
|
|
||||||
selected: true / false,
|
|
||||||
disabled: true / false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
---------------------------------------------------------------- */
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
options: Array,
|
|
||||||
name: String,
|
|
||||||
Attribute: String,
|
|
||||||
onChange: Function,
|
|
||||||
selectClass: String,
|
|
||||||
value: Object,
|
|
||||||
isTopLabelExist: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
isBottomLabelExist: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
required: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
readonly: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const searchQuery = ref("");
|
|
||||||
const isOpen = ref(false);
|
|
||||||
const selectedOption = ref(null);
|
|
||||||
|
|
||||||
const filteredOptions = computed(() => {
|
|
||||||
return props.options.filter((option) => {
|
|
||||||
const content = option[props.Attribute] || option.key || "";
|
|
||||||
return content.toLowerCase().includes(searchQuery.value.toLowerCase());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const selectOption = (value) => {
|
|
||||||
if (props.onChange) {
|
|
||||||
props.onChange(value);
|
|
||||||
} else {
|
|
||||||
props.value[props.name] = value.key;
|
|
||||||
selectedOption.value = props.options.find(
|
|
||||||
(option) => option.key === value.key
|
|
||||||
);
|
|
||||||
}
|
|
||||||
isOpen.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
isOpen.value = false;
|
|
||||||
selectedOption.value = props.options.find(
|
|
||||||
(option) => option.key == props.value[props.name]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<label class="form-control w-80 max-w-sm relative">
|
|
||||||
<div :class="twMerge(isTopLabelExist ? 'label' : '')">
|
|
||||||
<span class="label-text text-lg"><slot name="topLeft"></slot></span>
|
|
||||||
<span class="label-text-alt"> <slot name="topRight"></slot></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
:class="
|
|
||||||
twMerge(
|
|
||||||
'select rounded-md text-lg cursor-pointer pt-2',
|
|
||||||
disabled ? 'text-gray-300' : 'bg-transparent border-info'
|
|
||||||
)
|
|
||||||
"
|
|
||||||
@click="disabled ? () => {} : (isOpen = !isOpen)"
|
|
||||||
>
|
|
||||||
{{ selectedOption ? selectedOption[Attribute] : "" }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="isOpen"
|
|
||||||
class="absolute z-10 bg-gray-800 border border-info rounded-md w-full p-2"
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
v-model="searchQuery"
|
|
||||||
type="text"
|
|
||||||
placeholder="Search..."
|
|
||||||
class="input input-bordered border-info rounded-md w-full mb-2 px-2"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ul class="max-h-60 overflow-auto">
|
|
||||||
<li
|
|
||||||
v-for="option in filteredOptions"
|
|
||||||
:key="option.key || option"
|
|
||||||
@click="selectOption(option)"
|
|
||||||
class="px-4 py-2 hover:bg-gray-600 cursor-pointer"
|
|
||||||
>
|
|
||||||
{{ option[Attribute] || option }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div :class="twMerge(isBottomLabelExist ? 'label' : '')">
|
|
||||||
<span class="label-text-alt"> <slot name="bottomLeft"></slot></span>
|
|
||||||
<span class="label-text-alt"> <slot name="bottomRight"></slot></span>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
||||||
@ -18,7 +18,7 @@ const props = defineProps({
|
|||||||
Attribute: String,
|
Attribute: String,
|
||||||
onChange: Function,
|
onChange: Function,
|
||||||
selectClass: String,
|
selectClass: String,
|
||||||
value: Object,
|
value: String || Number,
|
||||||
isTopLabelExist: {
|
isTopLabelExist: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
@ -70,7 +70,9 @@ const props = defineProps({
|
|||||||
:class="twMerge(disabled ? `text-white` : 'text-dark')"
|
:class="twMerge(disabled ? `text-white` : 'text-dark')"
|
||||||
:value="option.value || option.key || option"
|
:value="option.value || option.key || option"
|
||||||
>
|
>
|
||||||
|
<span>
|
||||||
{{ option[Attribute] || option }}
|
{{ option[Attribute] || option }}
|
||||||
|
</span>
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<div :class="twMerge(isBottomLabelExist ? 'label' : '')">
|
<div :class="twMerge(isBottomLabelExist ? 'label' : '')">
|
||||||
|
|||||||
@ -30,10 +30,6 @@ const props = defineProps({
|
|||||||
const currentDataSource = ref([]);
|
const currentDataSource = ref([]);
|
||||||
const dataSourceStorage = ref([]);
|
const dataSourceStorage = ref([]);
|
||||||
|
|
||||||
const tableDataSource = computed(() =>
|
|
||||||
props.pagination ? currentDataSource.value : dataSourceStorage.value
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.dataSource,
|
() => props.dataSource,
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
@ -51,7 +47,7 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const updateDataSource = (data) => {
|
const updateDataSource = (data) => {
|
||||||
// console.log("update", data);
|
console.log("update", data);
|
||||||
currentDataSource.value = data;
|
currentDataSource.value = data;
|
||||||
};
|
};
|
||||||
provide("current_table_data", {
|
provide("current_table_data", {
|
||||||
@ -62,7 +58,6 @@ const sortRule = ref({});
|
|||||||
const filterColumn = ref({});
|
const filterColumn = ref({});
|
||||||
const filterItems = ref({});
|
const filterItems = ref({});
|
||||||
const selectedFilterItem = ref({});
|
const selectedFilterItem = ref({});
|
||||||
const searchQuery = ref("");
|
|
||||||
|
|
||||||
const toggleFilterModal = (key) => {
|
const toggleFilterModal = (key) => {
|
||||||
let newFilter = Object.assign(filterColumn.value);
|
let newFilter = Object.assign(filterColumn.value);
|
||||||
@ -137,10 +132,7 @@ const form = ref(null);
|
|||||||
|
|
||||||
const onFilter = (key, reset = false) => {
|
const onFilter = (key, reset = false) => {
|
||||||
const formData = new FormData(form.value);
|
const formData = new FormData(form.value);
|
||||||
if (reset) {
|
reset && formData.delete(key);
|
||||||
formData.delete(key);
|
|
||||||
searchQuery.value = "";
|
|
||||||
}
|
|
||||||
for (let [name, value] of formData) {
|
for (let [name, value] of formData) {
|
||||||
console.log(name, value);
|
console.log(name, value);
|
||||||
}
|
}
|
||||||
@ -236,28 +228,13 @@ watch(
|
|||||||
"
|
"
|
||||||
@click="() => toggleFilterModal(column.key)"
|
@click="() => toggleFilterModal(column.key)"
|
||||||
/>
|
/>
|
||||||
<div class="fixed z-50" v-if="filterColumn[column.key]">
|
<div
|
||||||
<div class="card min-w-max bg-body shadow-xl px-10 py-5">
|
class="absolute top-full -left-1/2 z-50"
|
||||||
<label
|
v-if="filterColumn[column.key]"
|
||||||
class="input input-bordered bg-transparent rounded-lg flex items-center px-2 mb-4 border-success focus-within:border-success"
|
|
||||||
>
|
>
|
||||||
<font-awesome-icon
|
<div class="card min-w-max bg-body shadow-xl px-10 py-5">
|
||||||
:icon="['fas', 'search']"
|
|
||||||
class="w-6 h-6 mr-2 text-success"
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
:placeholder="t('operation.enter_text')"
|
|
||||||
class="text-white bg-transparent w-full"
|
|
||||||
v-model="searchQuery"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<div class="max-h-72 overflow-x-auto px-2">
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
v-for="item in filterItems[column.key].filter(
|
v-for="item in filterItems[column.key]"
|
||||||
(item) =>
|
|
||||||
item.name && item.name.includes(searchQuery)
|
|
||||||
)"
|
|
||||||
:title="item.name"
|
:title="item.name"
|
||||||
:value="item.name"
|
:value="item.name"
|
||||||
:key="item.name"
|
:key="item.name"
|
||||||
@ -267,7 +244,6 @@ watch(
|
|||||||
"
|
"
|
||||||
className="justify-start"
|
className="justify-start"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div class="card-actions mt-4 justify-end">
|
<div class="card-actions mt-4 justify-end">
|
||||||
<input
|
<input
|
||||||
type="reset"
|
type="reset"
|
||||||
@ -295,12 +271,12 @@ watch(
|
|||||||
<Loading />
|
<Loading />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-else-if="tableDataSource.length == 0">
|
<tr v-else-if="currentDataSource.length == 0">
|
||||||
<td :colspan="columns.length">{{ $t("table.no_data") }}</td>
|
<td :colspan="columns.length">{{ $t("table.no_data") }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<template v-else :sort="sortRule">
|
<template v-else :sort="sortRule">
|
||||||
<tr
|
<tr
|
||||||
v-for="(data, index) in tableDataSource"
|
v-for="(data, index) in currentDataSource"
|
||||||
:key="data.key || data[rowKey]"
|
:key="data.key || data[rowKey]"
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
@ -334,7 +310,6 @@ watch(
|
|||||||
</form>
|
</form>
|
||||||
<slot name="afterTable"></slot>
|
<slot name="afterTable"></slot>
|
||||||
<Pagination
|
<Pagination
|
||||||
v-if="pagination"
|
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
:dataSource="dataSourceStorage"
|
:dataSource="dataSourceStorage"
|
||||||
:sort="sortRule"
|
:sort="sortRule"
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { defineProps } from "vue";
|
|||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
name: String,
|
name: String,
|
||||||
value: Object,
|
value: String,
|
||||||
placeholder: String,
|
placeholder: String,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -15,7 +15,7 @@ const props = defineProps({
|
|||||||
<span class="label-text-alt"> <slot name="topRight"></slot></span>
|
<span class="label-text-alt"> <slot name="topRight"></slot></span>
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
<textarea
|
||||||
class="textarea text-lg rounded-md border-info focus-within:border-info h-40"
|
class="textarea text-lg rounded-md border-info focus-within:border-info h-24"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
:name="name"
|
:name="name"
|
||||||
v-model="value[name]"
|
v-model="value[name]"
|
||||||
|
|||||||
@ -37,7 +37,7 @@ watch(
|
|||||||
role="alert"
|
role="alert"
|
||||||
:class="
|
:class="
|
||||||
twMerge(
|
twMerge(
|
||||||
`alert text-xl rounded-md fixed left-1/2 -translate-x-1/2 top-24 z-[1000] max-w-fit`,
|
`alert text-xl rounded-md absolute left-1/2 -translate-x-1/2 top-5 z-[1000] max-w-fit`,
|
||||||
status === 'info'
|
status === 'info'
|
||||||
? 'alert-info'
|
? 'alert-info'
|
||||||
: status === 'error'
|
: status === 'error'
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const props = defineProps({
|
|||||||
getFileList: Function,
|
getFileList: Function,
|
||||||
multiple: Boolean,
|
multiple: Boolean,
|
||||||
baseUrl: String,
|
baseUrl: String,
|
||||||
formats: { type: String, default: "txt、doc、xls、pdf、png、jpg、ppt、zip、rar" },
|
formats: { type: String, default: "txt、doc、xls、pdf、png、jpg" }
|
||||||
});
|
});
|
||||||
|
|
||||||
const acceptFileType = [
|
const acceptFileType = [
|
||||||
@ -26,13 +26,6 @@ const acceptFileType = [
|
|||||||
"application/msword",
|
"application/msword",
|
||||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
"application/vnd.ms-powerpoint",
|
"application/vnd.ms-powerpoint",
|
||||||
".ppt",
|
|
||||||
".pptx",
|
|
||||||
".zip",
|
|
||||||
".rar",
|
|
||||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation", // PowerPoint Files 2007+
|
|
||||||
"application/zip", // ZIP Files
|
|
||||||
"application/x-rar-compressed", // RAR Files
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@ -142,7 +135,7 @@ const revokeURL = (src) => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<label class="form-control w-full">
|
<label class="form-control w-full">
|
||||||
<div class="label" @click.stop.prevent="() => {}">
|
<div class="label">
|
||||||
<span class="label-text text-lg"><slot name="topLeft"></slot></span>
|
<span class="label-text text-lg"><slot name="topLeft"></slot></span>
|
||||||
<span class="label-text-alt"> <slot name="topRight"></slot></span>
|
<span class="label-text-alt"> <slot name="topRight"></slot></span>
|
||||||
</div>
|
</div>
|
||||||
@ -244,14 +237,6 @@ const revokeURL = (src) => {
|
|||||||
:icon="['fas', 'file-powerpoint']"
|
:icon="['fas', 'file-powerpoint']"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template
|
|
||||||
v-else-if="file.type?.match(/zip|rar/g) || file.ext?.match(/zip|rar/g)"
|
|
||||||
>
|
|
||||||
<font-awesome-icon
|
|
||||||
class="mx-auto mb-2 text-4xl text-white"
|
|
||||||
:icon="['fas', 'file-archive']"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template
|
<template
|
||||||
v-else-if="
|
v-else-if="
|
||||||
!file.type?.match('image/*') &&
|
!file.type?.match('image/*') &&
|
||||||
@ -281,9 +266,7 @@ const revokeURL = (src) => {
|
|||||||
</div>
|
</div>
|
||||||
<p class="text-2xl my-2">{{ $t("upload.title") }}</p>
|
<p class="text-2xl my-2">{{ $t("upload.title") }}</p>
|
||||||
<p class="mb-0 col-grey">{{ $t("upload.description") }}</p>
|
<p class="mb-0 col-grey">{{ $t("upload.description") }}</p>
|
||||||
<p class="mb-0 col-grey">
|
<p class="mb-0 col-grey">{{ $t("upload.formats") }} : {{props.formats}}</p>
|
||||||
{{ $t("upload.formats") }} : {{ props.formats }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -34,20 +34,6 @@ const initViewer = (container) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 設置攝影機位置
|
|
||||||
const setCameraPosition = (position, target) => {
|
|
||||||
const nav = viewer.navigation;
|
|
||||||
// 使用 THREE.Vector3 定義位置與焦點
|
|
||||||
const newPosition = new THREE.Vector3(position.x, position.y, position.z);
|
|
||||||
const newTarget = new THREE.Vector3(target.x, target.y, target.z);
|
|
||||||
|
|
||||||
// 設定攝影機的新位置與焦點
|
|
||||||
nav.setView(newPosition, newTarget);
|
|
||||||
console.log("攝影機定位完成:");
|
|
||||||
console.log("新位置:", newPosition);
|
|
||||||
console.log("新焦點:", newTarget);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 使用本地 .svf 文件加載模型
|
// 使用本地 .svf 文件加載模型
|
||||||
const loadModel = (filePath) => {
|
const loadModel = (filePath) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@ -57,21 +43,6 @@ const loadModel = (filePath) => {
|
|||||||
(model) => {
|
(model) => {
|
||||||
viewer.impl.invalidate(true);
|
viewer.impl.invalidate(true);
|
||||||
viewer.fitToView();
|
viewer.fitToView();
|
||||||
// 設置攝影機位置
|
|
||||||
setTimeout(() => {
|
|
||||||
setCameraPosition(
|
|
||||||
{
|
|
||||||
x: -212.33421531428007,
|
|
||||||
y: 299.3895400707608,
|
|
||||||
z: 157.48711907404862,
|
|
||||||
}, // 攝影機的新位置
|
|
||||||
{
|
|
||||||
x: 151.61159898775077,
|
|
||||||
y: -196.55136189609067,
|
|
||||||
z: -136.47282256290345,
|
|
||||||
} // 攝影機的焦點
|
|
||||||
);
|
|
||||||
}, 500);
|
|
||||||
resolve(model);
|
resolve(model);
|
||||||
console.log("模型加載完成");
|
console.log("模型加載完成");
|
||||||
},
|
},
|
||||||
@ -114,4 +85,5 @@ onUnmounted(() => {
|
|||||||
display: none;
|
display: none;
|
||||||
bottom: 200px;
|
bottom: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -30,7 +30,7 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
const store = useHeatmapBarStore();
|
const store = useHeatmapBarStore();
|
||||||
|
|
||||||
const { updateDataVisualization, createSprites, showSubSystemObjects, forgeClickListener, clear, setCameraPosition } = useForgeSprite()
|
const { updateDataVisualization, createSprites, showSubSystemObjects, forgeClickListener, clear } = useForgeSprite()
|
||||||
|
|
||||||
const forgeDom = ref(null);
|
const forgeDom = ref(null);
|
||||||
|
|
||||||
@ -80,20 +80,6 @@ const loadModel = (viewer, filePath) => {
|
|||||||
(model) => {
|
(model) => {
|
||||||
viewer.impl.invalidate(true);
|
viewer.impl.invalidate(true);
|
||||||
viewer.fitToView();
|
viewer.fitToView();
|
||||||
setTimeout(() => {
|
|
||||||
setCameraPosition(
|
|
||||||
{
|
|
||||||
x: 241.40975707867645,
|
|
||||||
y: -260.4481491801548,
|
|
||||||
z: 129.5719879121458,
|
|
||||||
}, // 攝影機的新位置
|
|
||||||
{
|
|
||||||
x: -183.36302786348594,
|
|
||||||
y: 194.05657710941966,
|
|
||||||
z: -149.3902249981004,
|
|
||||||
} // 攝影機的焦點
|
|
||||||
);
|
|
||||||
}, 500);
|
|
||||||
updateDataVisualization(viewer)
|
updateDataVisualization(viewer)
|
||||||
resolve(model);
|
resolve(model);
|
||||||
console.log("模型加載完成");
|
console.log("模型加載完成");
|
||||||
|
|||||||
@ -93,8 +93,8 @@ const createSprites = async (dataVizExtn) => {
|
|||||||
const DataVizCore = Autodesk.DataVisualization.Core;
|
const DataVizCore = Autodesk.DataVisualization.Core;
|
||||||
const viewableType = DataVizCore.ViewableType.SPRITE;
|
const viewableType = DataVizCore.ViewableType.SPRITE;
|
||||||
let spriteColor = new THREE.Color(0xffffff);
|
let spriteColor = new THREE.Color(0xffffff);
|
||||||
const BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
const BASEURL = import.meta.env.VITE_FORGE_BASEURL;
|
||||||
const spriteIconUrl = `${BASEURL}/dist/hotspot.svg`;
|
const spriteIconUrl = `${BASEURL}/hotspot.svg`;
|
||||||
const style = new DataVizCore.ViewableStyle(
|
const style = new DataVizCore.ViewableStyle(
|
||||||
viewableType,
|
viewableType,
|
||||||
spriteColor,
|
spriteColor,
|
||||||
|
|||||||
@ -1,22 +1,17 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
|
||||||
import NavbarItem from "./NavbarItem.vue";
|
import NavbarItem from "./NavbarItem.vue";
|
||||||
import NavbarBuilding from "./NavbarBuilding.vue";
|
import NavbarBuilding from "./NavbarBuilding.vue";
|
||||||
import Logo from "@/assets/img/logo.svg";
|
import Logo from "@/assets/img/logo.svg";
|
||||||
import useUserInfoStore from "@/stores/useUserInfoStore";
|
import useUserInfoStore from "@/stores/useUserInfoStore";
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
|
||||||
|
|
||||||
import AlarmDrawer from "@/components/alarm/AlarmDrawer.vue";
|
import AlarmDrawer from "@/components/alarm/AlarmDrawer.vue";
|
||||||
import NavbarLang from "./NavbarLang.vue";
|
import NavbarLang from "./NavbarLang.vue";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
const user = ref("");
|
const user = ref("");
|
||||||
const menuShow = ref(true);
|
const menuShow = ref(true);
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const store = useUserInfoStore();
|
const store = useUserInfoStore();
|
||||||
const storeBuilding = useBuildingStore();
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const name = store.user.user_name;
|
const name = store.user.user_name;
|
||||||
if (name) {
|
if (name) {
|
||||||
@ -29,26 +24,12 @@ const toggleMenu = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const src = import.meta.env.MODE === "production" ? "./logo.svg" : Logo;
|
const src = import.meta.env.MODE === "production" ? "./logo.svg" : Logo;
|
||||||
|
|
||||||
const logout = () => {
|
|
||||||
document.cookie = "JWT-Authorization=; Max-Age=0";
|
|
||||||
document.cookie = "user_name=; Max-Age=0";
|
|
||||||
store.user.token = "";
|
|
||||||
store.user.user_name = "";
|
|
||||||
storeBuilding.deleteBuilding();
|
|
||||||
router.push({ path: "/login" });
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<header class="navbar bg-dark text-light-info w-full relative z-50">
|
<header class="navbar bg-dark text-light-info w-full relative z-50">
|
||||||
<div class="navbar-start min-w-[480px] lg:min-w-[440px]">
|
<div class="navbar-start min-w-[480px] lg:min-w-[440px]">
|
||||||
<div
|
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden" @click="toggleMenu">
|
||||||
tabindex="0"
|
|
||||||
role="button"
|
|
||||||
class="btn btn-ghost lg:hidden"
|
|
||||||
@click="toggleMenu"
|
|
||||||
>
|
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="h-5 w-5"
|
class="h-5 w-5"
|
||||||
@ -112,12 +93,12 @@ const logout = () => {
|
|||||||
class="dropdown-content translate-y-2 z-[100] menu py-3 shadow rounded w-32 bg-[#4c625e] border text-center"
|
class="dropdown-content translate-y-2 z-[100] menu py-3 shadow rounded w-32 bg-[#4c625e] border text-center"
|
||||||
>
|
>
|
||||||
<li class="text-white">
|
<li class="text-white">
|
||||||
<a
|
<router-link
|
||||||
href="#"
|
to="logout"
|
||||||
@click.prevent="logout"
|
type="link"
|
||||||
class="flex flex-col justify-center items-center"
|
class="flex flex-col justify-center items-center"
|
||||||
>{{ $t("sign_out") }}
|
>{{ $t("sign_out") }}
|
||||||
</a>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,20 +1,27 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted } from "vue";
|
import { getBuildings } from "@/apis/building";
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
import useBuildingStore from "@/stores/useBuildingStore";
|
||||||
|
|
||||||
const store = useBuildingStore();
|
const store = useBuildingStore();
|
||||||
|
|
||||||
|
const getBui = async () => {
|
||||||
|
console.log(store.buildings);
|
||||||
|
const res = await getBuildings();
|
||||||
|
store.buildings = res.data;
|
||||||
|
store.selectedBuilding = res?.data[0];
|
||||||
|
};
|
||||||
|
|
||||||
const selectBuilding = (bui) => {
|
const selectBuilding = (bui) => {
|
||||||
store.selectedBuilding = bui; // 改變 selectedBuilding,watch 會自動更新資料
|
store.selectedBuilding = bui;
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
store.initialize(); // 初始化資料
|
getBui();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<template v-if="store.buildings.length > 1">
|
|
||||||
<div class="dropdown dropdown-bottom ">
|
<div class="dropdown dropdown-bottom ">
|
||||||
<div
|
<div
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@ -26,10 +33,10 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
<ul
|
<ul
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="dropdown-content w-48 left-8 translate-y-2 z-[1] menu py-3 shadow rounded bg-[#4c625e] border text-center"
|
class="dropdown-content left-8 translate-y-2 z-[1] menu py-3 shadow rounded bg-[#4c625e] border text-center"
|
||||||
>
|
>
|
||||||
<li
|
<li
|
||||||
class="text-white my-1 text-base cursor-pointer"
|
class="text-white my-1 text-base"
|
||||||
v-for="bui in store.buildings"
|
v-for="bui in store.buildings"
|
||||||
:key="bui.building_tag"
|
:key="bui.building_tag"
|
||||||
@click="selectBuilding(bui)"
|
@click="selectBuilding(bui)"
|
||||||
@ -39,10 +46,5 @@ onMounted(() => {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
|
||||||
<div class="text-white ml-8 text-lg font-semiLight">
|
|
||||||
{{ store.selectedBuilding?.full_name }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
import { onMounted, ref, watch, computed } from "vue";
|
import { onMounted, ref, watch, computed } from "vue";
|
||||||
import { AUTHPAGES } from "@/constant";
|
import { AUTHPAGES } from "@/constant";
|
||||||
import { getAuth, getAllSysSidebar } from "@/apis/building";
|
import { getAuth, getAllSysSidebar } from "@/apis/building";
|
||||||
import { getSideBar } from "@/apis/energy";
|
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
import useBuildingStore from "@/stores/useBuildingStore";
|
||||||
import useUserInfoStore from "@/stores/useUserInfoStore";
|
import useUserInfoStore from "@/stores/useUserInfoStore";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
@ -14,8 +13,6 @@ const store = useUserInfoStore();
|
|||||||
const buildingStore = useBuildingStore();
|
const buildingStore = useBuildingStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const openKeys = ref([]); // 追蹤當前打開的子菜單
|
const openKeys = ref([]); // 追蹤當前打開的子菜單
|
||||||
const menu_array = ref([]);
|
|
||||||
const currentAuthCode = ref("");
|
|
||||||
|
|
||||||
const iniFroList = async () => {
|
const iniFroList = async () => {
|
||||||
const res = await getAuth(locale.value);
|
const res = await getAuth(locale.value);
|
||||||
@ -37,22 +34,12 @@ const authPages = computed(() =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
const open = ref(false);
|
const open = ref(false);
|
||||||
const getSubMonitorPage = async (building_guid) => {
|
const getSubMonitorPage = async (building) => {
|
||||||
const res = await getAllSysSidebar(building_guid);
|
const res = await getAllSysSidebar();
|
||||||
buildingStore.mainSubSys = res.data.history_Main_Systems;
|
buildingStore.mainSubSys = res.data.history_Main_Systems;
|
||||||
menu_array.value = res.data.history_Main_Systems;
|
|
||||||
};
|
};
|
||||||
const getSubPage = async (system_type) => {
|
const showDrawer = () => {
|
||||||
const res = await getSideBar(system_type);
|
getSubMonitorPage();
|
||||||
menu_array.value = res.data;
|
|
||||||
};
|
|
||||||
const showDrawer = async (authCode) => {
|
|
||||||
if (authCode === "PF1") {
|
|
||||||
await getSubMonitorPage(buildingStore.selectedBuilding.building_guid);
|
|
||||||
} else if (authCode === "PF2" || authCode === "PF11") {
|
|
||||||
await getSubPage(authCode === "PF2" ? "Energy" : "Setting");
|
|
||||||
}
|
|
||||||
currentAuthCode.value = authCode;
|
|
||||||
open.value = true;
|
open.value = true;
|
||||||
};
|
};
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
@ -71,7 +58,7 @@ watch(
|
|||||||
() => buildingStore.selectedBuilding,
|
() => buildingStore.selectedBuilding,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
if (newVal !== null) {
|
if (newVal !== null) {
|
||||||
getSubMonitorPage(newVal.building_guid);
|
getSubMonitorPage(newVal.building_tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -102,26 +89,14 @@ onMounted(() => {
|
|||||||
<li
|
<li
|
||||||
v-for="page in authPages"
|
v-for="page in authPages"
|
||||||
class="flex flex-col items-center justify-center"
|
class="flex flex-col items-center justify-center"
|
||||||
:key="page.authCode"
|
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
v-if="
|
v-if="page.authCode === 'PF1'"
|
||||||
page.authCode === 'PF1' ||
|
@click="showDrawer"
|
||||||
page.authCode === 'PF2' ||
|
|
||||||
page.authCode === 'PF11'
|
|
||||||
"
|
|
||||||
@click="showDrawer(page.authCode)"
|
|
||||||
:class="
|
:class="
|
||||||
twMerge(
|
twMerge(
|
||||||
'flex lg:flex-col justify-center items-center btn-group text-white cursor-pointer',
|
'flex lg:flex-col justify-center items-center btn-group text-white cursor-pointer',
|
||||||
page.authCode === 'PF1' && route.fullPath.includes('/system')
|
route.fullPath.includes('/system')
|
||||||
? 'router-link-active router-link-exact-active'
|
|
||||||
: '',
|
|
||||||
page.authCode === 'PF2' &&
|
|
||||||
route.fullPath.includes('/energyManagement')
|
|
||||||
? 'router-link-active router-link-exact-active'
|
|
||||||
: '',
|
|
||||||
page.authCode === 'PF11' && route.fullPath.includes('/setting')
|
|
||||||
? 'router-link-active router-link-exact-active'
|
? 'router-link-active router-link-exact-active'
|
||||||
: ''
|
: ''
|
||||||
)
|
)
|
||||||
@ -167,34 +142,22 @@ onMounted(() => {
|
|||||||
@openChange="handleOpenChange"
|
@openChange="handleOpenChange"
|
||||||
>
|
>
|
||||||
<a-sub-menu
|
<a-sub-menu
|
||||||
v-for="main in menu_array"
|
v-for="main in buildingStore.mainSubSys"
|
||||||
:key="main.main_system_tag"
|
:key="main.main_system_tag"
|
||||||
:title="main.full_name"
|
:title="main.full_name"
|
||||||
v-if="menu_array.length > 0 && open"
|
|
||||||
>
|
>
|
||||||
<a-menu-item
|
<a-menu-item
|
||||||
v-for="sub in currentAuthCode === 'PF1'
|
v-for="sub in main.history_Sub_systems"
|
||||||
? main.history_Sub_systems
|
:key="sub.sub_system_tag"
|
||||||
: currentAuthCode === 'PF2'
|
|
||||||
? main.sub
|
|
||||||
: main.sub"
|
|
||||||
:key="sub.sub_system_tag + `_` + sub.type"
|
|
||||||
@click="() => onClose()"
|
@click="() => onClose()"
|
||||||
>
|
>
|
||||||
<router-link
|
<router-link
|
||||||
:to="{
|
:to="{
|
||||||
name:
|
name: 'sub_system',
|
||||||
currentAuthCode === 'PF2'
|
|
||||||
? 'energyManagement'
|
|
||||||
: currentAuthCode === 'PF11'
|
|
||||||
? 'setting'
|
|
||||||
: 'sub_system',
|
|
||||||
params: {
|
params: {
|
||||||
main_system_id: main.main_system_tag,
|
main_system_id: main.main_system_tag,
|
||||||
sub_system_id: sub.sub_system_tag,
|
sub_system_id: sub.sub_system_tag,
|
||||||
...(currentAuthCode === 'PF2' || currentAuthCode === 'PF11'
|
floor_id: 'main',
|
||||||
? { type: sub.type }
|
|
||||||
: { floor_id: 'main' }),
|
|
||||||
},
|
},
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -8,10 +8,7 @@
|
|||||||
"table": {
|
"table": {
|
||||||
"no_data": "表中数据为空",
|
"no_data": "表中数据为空",
|
||||||
"in_otal": "笔资料",
|
"in_otal": "笔资料",
|
||||||
"skip_to": "跳至",
|
"skip_to": "跳至"
|
||||||
"serial_number": "序列号",
|
|
||||||
"name": "名称",
|
|
||||||
"time": "时间"
|
|
||||||
},
|
},
|
||||||
"upload": {
|
"upload": {
|
||||||
"title": "选择一个文件或拖放到这里",
|
"title": "选择一个文件或拖放到这里",
|
||||||
@ -23,35 +20,11 @@
|
|||||||
"elec_consumption_comparison": "用电量比较",
|
"elec_consumption_comparison": "用电量比较",
|
||||||
"elec_consumption_comparison_trend": "用电量比较趋势",
|
"elec_consumption_comparison_trend": "用电量比较趋势",
|
||||||
"electricity_consumption": "用电量",
|
"electricity_consumption": "用电量",
|
||||||
"today_electricity_consumption": "今日用电量 ( kWH )",
|
"today_electricity_consumption": "今日用电量",
|
||||||
"yesterday_electricity_consumption": "昨天用电量 ( kWH )",
|
"yesterday_electricity_consumption": "昨天用电量",
|
||||||
"instant_power": "即时功率 ( kW )",
|
|
||||||
"instant_contract_capacity_ratio": "契約容量佔比 ( % )",
|
|
||||||
"this_last_week": "本周/上周",
|
"this_last_week": "本周/上周",
|
||||||
"thisweek_electricity_consumption": "本周用电量",
|
"thisweek_electricity_consumption": "本周用电量",
|
||||||
"lastweek_electricity_consumption": "上周用电量",
|
"lastweek_electricity_consumption": "上周用电量"
|
||||||
"one_hour": "1小时",
|
|
||||||
"four_hour": "4小时",
|
|
||||||
"eight_hour": "8小时",
|
|
||||||
"energy_ranking": "能耗排行",
|
|
||||||
"last_30_days_energy_trend": "近30天能耗趋势",
|
|
||||||
"today_energy_consumption": "本日能耗",
|
|
||||||
"this_month_energy_consumption": "本月能耗",
|
|
||||||
"relative_energy_consumption": "环比能耗",
|
|
||||||
"daily_relative_change": "日环比",
|
|
||||||
"weekly_relative_change": "周环比",
|
|
||||||
"monthly_relative_change": "月环比",
|
|
||||||
"yearly_relative_change": "年环比",
|
|
||||||
"today": "今日",
|
|
||||||
"yesterday": "昨日",
|
|
||||||
"this_week": "本周",
|
|
||||||
"last_week": "上周",
|
|
||||||
"this_month": "本月",
|
|
||||||
"last_month": "上月",
|
|
||||||
"this_year": "今年",
|
|
||||||
"last_year": "去年",
|
|
||||||
"work_order": "工单",
|
|
||||||
"system_status": "系统状态"
|
|
||||||
},
|
},
|
||||||
"history": {
|
"history": {
|
||||||
"title": "历史资料",
|
"title": "历史资料",
|
||||||
@ -106,61 +79,12 @@
|
|||||||
"total_elec_cost": "总电费",
|
"total_elec_cost": "总电费",
|
||||||
"carbon_equivalent": "碳排当量",
|
"carbon_equivalent": "碳排当量",
|
||||||
"edit_carbon_emission": "编辑碳排放系数",
|
"edit_carbon_emission": "编辑碳排放系数",
|
||||||
"carbon_emission_coefficient": "碳排放系数",
|
"carbon_emission_coefficient":"碳排放系数"
|
||||||
"electricity_classification": "用电分类",
|
|
||||||
"electricity_price": "电费每度单价",
|
|
||||||
"floor": "楼层",
|
|
||||||
"maximum": "最大值",
|
|
||||||
"maximum_time": "最大值时间",
|
|
||||||
"minimum": "最小值",
|
|
||||||
"minimum_time": "最小值时间",
|
|
||||||
"average_value": "平均值",
|
|
||||||
"start_value": "起始值(kWh)",
|
|
||||||
"end_value": "截止值(kWh)",
|
|
||||||
"difference": "差值(kWh)",
|
|
||||||
"power_consumption": "用电量(kWh)",
|
|
||||||
"ranking": "排名",
|
|
||||||
"subtotal": "小计",
|
|
||||||
"unit_price": "单价",
|
|
||||||
"total_amount": "金额总计",
|
|
||||||
"elec_price_list": "电价表",
|
|
||||||
"residential": "住宅型",
|
|
||||||
"standard": "标准型",
|
|
||||||
"simple_elec_price_two_stage": "简易型时间电价二段式",
|
|
||||||
"simple_elec_price_three_stage": "简易型时间电价三段式",
|
|
||||||
"classification": "分类",
|
|
||||||
"summer_months": "夏月",
|
|
||||||
"non_summer_months": "非夏月",
|
|
||||||
"time_outside_summer_months": "夏月以外的时间",
|
|
||||||
"basic_elec_charge": "基本电费",
|
|
||||||
"charged_per_household": "按户计收",
|
|
||||||
"per_household_month": "每户每月",
|
|
||||||
"mon_to_friday": "周一~周五",
|
|
||||||
"peak_hours": "尖峰时间",
|
|
||||||
"semi_peak_hours": "半尖峰时间",
|
|
||||||
"off_peak_hours": "离峰时间",
|
|
||||||
"price_per_kwh": "每度",
|
|
||||||
"all_day": "全日",
|
|
||||||
"sat_sun_off_peak_days": "周六、周日及离峰日",
|
|
||||||
"usage_over_2000kwh": "每月总度数超过2000度之部分",
|
|
||||||
"add": "加",
|
|
||||||
"standard_time_of_use_tariff_2_stage": "标准型时间电价二段式",
|
|
||||||
"standard_time_of_use_tariff_3_stage": "标准型时间电价三段式",
|
|
||||||
"single_phase": "单相",
|
|
||||||
"three_phase": "三相",
|
|
||||||
"frequent_contract": "经常契约",
|
|
||||||
"per_kw_per_month": "每瓩每月",
|
|
||||||
"non_summer_contract": "非夏日契约",
|
|
||||||
"saturday_semi_peak_contract": "周六半尖峰契约",
|
|
||||||
"off_peak_contract": "离峰契约",
|
|
||||||
"variable_electricity_charge": "流动电费",
|
|
||||||
"saturday": "周六",
|
|
||||||
"sunday_and_off_peak_days": "周日及离峰日"
|
|
||||||
},
|
},
|
||||||
"alarm": {
|
"alarm": {
|
||||||
"title": "显示警告",
|
"title": "显示警告",
|
||||||
"notify": "异常通知",
|
"notify": "异常通知",
|
||||||
"number": "异常ID",
|
"number": "异常编号",
|
||||||
"category": "异常类别",
|
"category": "异常类别",
|
||||||
"device_name": "设备名称",
|
"device_name": "设备名称",
|
||||||
"message": "异常讯息",
|
"message": "异常讯息",
|
||||||
@ -178,10 +102,9 @@
|
|||||||
"end_date": "结束日期",
|
"end_date": "结束日期",
|
||||||
"building_and_floor": "栋别-楼层",
|
"building_and_floor": "栋别-楼层",
|
||||||
"uuid": "异常ID",
|
"uuid": "异常ID",
|
||||||
"alarmClass": "告警条件",
|
"alarmClass": "异常类别",
|
||||||
"device_name": "设备名称",
|
"device_name": "设备名称",
|
||||||
"device_number": "设备编号",
|
"device_number": "设备编号",
|
||||||
"device_point_name": "点位名称",
|
|
||||||
"date": "发生日期",
|
"date": "发生日期",
|
||||||
"time": "发生时间",
|
"time": "发生时间",
|
||||||
"error_msg": "异常原因",
|
"error_msg": "异常原因",
|
||||||
@ -208,12 +131,10 @@
|
|||||||
"qualifications": "限定条件",
|
"qualifications": "限定条件",
|
||||||
"upper_limit": "上限",
|
"upper_limit": "上限",
|
||||||
"lower_limit": "下限",
|
"lower_limit": "下限",
|
||||||
"delay": "持续秒数",
|
|
||||||
"highDelay": "上限持续秒数",
|
"highDelay": "上限持续秒数",
|
||||||
"lowDelay": "下限持续秒数",
|
"lowDelay": "下限持续秒数",
|
||||||
"warning_method": "警示方式",
|
"warning_method": "警示方式",
|
||||||
"warning_time": "警示时间",
|
"warning_time": "警示时间",
|
||||||
"warning_value": "警示值",
|
|
||||||
"operation": "功能",
|
"operation": "功能",
|
||||||
"alarm_settings": "异常设定",
|
"alarm_settings": "异常设定",
|
||||||
"time_setting": "时间设定",
|
"time_setting": "时间设定",
|
||||||
@ -225,23 +146,7 @@
|
|||||||
"notify_email": "email",
|
"notify_email": "email",
|
||||||
"notify_items": "通知项目",
|
"notify_items": "通知项目",
|
||||||
"notify_list": "通知名单",
|
"notify_list": "通知名单",
|
||||||
"choose": "选择",
|
"choose": "选择"
|
||||||
"day_time": "星期/时间",
|
|
||||||
"click_time_period": "请用滑鼠点击时间段",
|
|
||||||
"clear": "清空",
|
|
||||||
"sunday": "星期日",
|
|
||||||
"monday": "星期一",
|
|
||||||
"tuesday": "星期二",
|
|
||||||
"wednesday": "星期三",
|
|
||||||
"thursday": "星期四",
|
|
||||||
"friday": "星期五",
|
|
||||||
"saturday": "星期六",
|
|
||||||
"schedule_name": "时段名称",
|
|
||||||
"schedule_content": "时段内容",
|
|
||||||
"reorganization": "MQTT 告警重整",
|
|
||||||
"online": "在线",
|
|
||||||
"offline": "离线",
|
|
||||||
"alarm": "告警"
|
|
||||||
},
|
},
|
||||||
"operation": {
|
"operation": {
|
||||||
"title": "运维管理",
|
"title": "运维管理",
|
||||||
@ -255,14 +160,13 @@
|
|||||||
"start_time": "预计开始时间",
|
"start_time": "预计开始时间",
|
||||||
"upload": "档案上传",
|
"upload": "档案上传",
|
||||||
"finish_time": "完成时间",
|
"finish_time": "完成时间",
|
||||||
"updated_time": "更新时间",
|
|
||||||
"operation": "功能",
|
"operation": "功能",
|
||||||
"vendor": "厂商",
|
"vendor": "厂商",
|
||||||
"contact_person": "联络人",
|
"contact_person": "联络人",
|
||||||
"phone": "电话",
|
"phone": "电话",
|
||||||
"email": "email",
|
"email": "email",
|
||||||
"created_at": "建立日期",
|
"created_at": "建立日期",
|
||||||
"maintenance": "保养",
|
"maintainance": "保养",
|
||||||
"repair": "维修",
|
"repair": "维修",
|
||||||
"company_info": "厂商资料",
|
"company_info": "厂商资料",
|
||||||
"repair_item": "维修项目",
|
"repair_item": "维修项目",
|
||||||
@ -270,8 +174,6 @@
|
|||||||
"responsible_vendor": "负责厂商",
|
"responsible_vendor": "负责厂商",
|
||||||
"not_completed": "未完成",
|
"not_completed": "未完成",
|
||||||
"completed": "已完成",
|
"completed": "已完成",
|
||||||
"complete": "已完成",
|
|
||||||
"incomplete": "未完成",
|
|
||||||
"worker_id": "工作人员编号",
|
"worker_id": "工作人员编号",
|
||||||
"notice": "注意事项",
|
"notice": "注意事项",
|
||||||
"result_description": "结果描述",
|
"result_description": "结果描述",
|
||||||
@ -304,13 +206,10 @@
|
|||||||
},
|
},
|
||||||
"assetManagement": {
|
"assetManagement": {
|
||||||
"title": "资产管理",
|
"title": "资产管理",
|
||||||
"add_system_category": "新增系统类别",
|
"add_category": "新增类别",
|
||||||
"edit_system_category": "修改系统类别",
|
|
||||||
"add_device_category": "新增设备类别",
|
|
||||||
"edit_device_category": "修改设备类别",
|
|
||||||
"system_name": "名称",
|
"system_name": "名称",
|
||||||
"system_value": "代号",
|
"system_value": "代号",
|
||||||
"system_parent": "所属系統",
|
"system_parent": "所属大类",
|
||||||
"device_number": "设备编号",
|
"device_number": "设备编号",
|
||||||
"device_name": "设备名称",
|
"device_name": "设备名称",
|
||||||
"asset_number": "资产编号",
|
"asset_number": "资产编号",
|
||||||
@ -338,10 +237,7 @@
|
|||||||
"associated_device": "关联设备",
|
"associated_device": "关联设备",
|
||||||
"choose": "选择",
|
"choose": "选择",
|
||||||
"index": "编号",
|
"index": "编号",
|
||||||
"floor_plan": "平面图",
|
"floor_plan": "平面图"
|
||||||
"department": "部门",
|
|
||||||
"department_name": "部门名称",
|
|
||||||
"building": "栋别"
|
|
||||||
},
|
},
|
||||||
"accountManagement": {
|
"accountManagement": {
|
||||||
"account_title": "帐号管理",
|
"account_title": "帐号管理",
|
||||||
@ -385,44 +281,15 @@
|
|||||||
"email_format": "请输入正确 Email 地址",
|
"email_format": "请输入正确 Email 地址",
|
||||||
"password_format": "密码长度至少8码,必须包含英文及数字",
|
"password_format": "密码长度至少8码,必须包含英文及数字",
|
||||||
"start_time_placeholder": "请输入预计开始日期",
|
"start_time_placeholder": "请输入预计开始日期",
|
||||||
"finish_time_placeholder": "请输入完成日期",
|
|
||||||
"rename": "重新命名",
|
"rename": "重新命名",
|
||||||
"download": "下载",
|
"download": "下载",
|
||||||
"confirm": "确认",
|
"confirm": "确认",
|
||||||
"restore": "复原",
|
"restore": "复原"
|
||||||
"stop_edit": "停止修改",
|
|
||||||
"start_edit": "开始修改",
|
|
||||||
"convert": "轉換"
|
|
||||||
},
|
},
|
||||||
"msg":{
|
"msg":{
|
||||||
"sure_to_delete": "是否确认删除该项目?",
|
"sure_to_delete": "是否确认删除该项目?",
|
||||||
"sure_to_delete_permanent": "是否确认永久删除该项目?",
|
"sure_to_delete_permanent": "是否确认永久删除该项目?",
|
||||||
"delete_success": "删除成功",
|
"delete_success": "删除成功",
|
||||||
"delete_failed": "删除失败",
|
"delete_failed": "删除失败"
|
||||||
"mqtt_refresh": "重新设定成功",
|
|
||||||
"schema_name_required": "架构名称栏位必填",
|
|
||||||
"incorrect_format":"格式不正确",
|
|
||||||
"send_successfully":"送出成功",
|
|
||||||
"edit_successfully":"修改成功"
|
|
||||||
},
|
|
||||||
"setting": {
|
|
||||||
"electricity_meter": "电表",
|
|
||||||
"MQTT_parse": "MQTT 解析",
|
|
||||||
"schema": "架构",
|
|
||||||
"point": "点位",
|
|
||||||
"description": "描述",
|
|
||||||
"IoT_point_name": "IoT 点位名称",
|
|
||||||
"IoT_point_code": "IoT 点位代号",
|
|
||||||
"number_of_decimal_places": "小数位数",
|
|
||||||
"boolean_value": "布林值",
|
|
||||||
"hide_point": "点位显示",
|
|
||||||
"hide_switch": "switch 功能",
|
|
||||||
"switch_on_message": "switch 开启时传送的讯息",
|
|
||||||
"switch_off_message": "switch 关闭时传送的讯息",
|
|
||||||
"schema_name": "架构名称",
|
|
||||||
"IoT_point_structure": "IoT点位结构",
|
|
||||||
"system_point_name": "系统点位名称",
|
|
||||||
"json_format_text": "请贴上 JSON 格式数据",
|
|
||||||
"json_click_text": "请在左侧输入JSON并点选转换按钮"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,10 +8,7 @@
|
|||||||
"table": {
|
"table": {
|
||||||
"no_data": "表中數據為空",
|
"no_data": "表中數據為空",
|
||||||
"in_otal": "筆資料",
|
"in_otal": "筆資料",
|
||||||
"skip_to": "跳至",
|
"skip_to": "跳至"
|
||||||
"serial_number": "序號",
|
|
||||||
"name": "姓名",
|
|
||||||
"time": "時間"
|
|
||||||
},
|
},
|
||||||
"upload": {
|
"upload": {
|
||||||
"title": "選擇一個文件或拖放到這裡",
|
"title": "選擇一個文件或拖放到這裡",
|
||||||
@ -23,35 +20,11 @@
|
|||||||
"elec_consumption_comparison": "用電量比較",
|
"elec_consumption_comparison": "用電量比較",
|
||||||
"elec_consumption_comparison_trend": "用電量比較趨勢",
|
"elec_consumption_comparison_trend": "用電量比較趨勢",
|
||||||
"electricity_consumption": "用電量",
|
"electricity_consumption": "用電量",
|
||||||
"today_electricity_consumption": "今日用電量 ( kWH )",
|
"today_electricity_consumption": "今日用電量",
|
||||||
"yesterday_electricity_consumption": "昨天用電量 ( kWH )",
|
"yesterday_electricity_consumption": "昨天用電量",
|
||||||
"instant_power": "即時功率 ( kW )",
|
|
||||||
"instant_contract_capacity_ratio": "契約容量佔比 ( % )",
|
|
||||||
"this_last_week": "本週/上週",
|
"this_last_week": "本週/上週",
|
||||||
"thisweek_electricity_consumption": "本周用電量",
|
"thisweek_electricity_consumption": "本周用電量",
|
||||||
"lastweek_electricity_consumption": "上週用電量",
|
"lastweek_electricity_consumption": "上週用電量"
|
||||||
"one_hour": "1小時",
|
|
||||||
"four_hour": "4小時",
|
|
||||||
"eight_hour": "8小時",
|
|
||||||
"energy_ranking": "能耗排行",
|
|
||||||
"last_30_days_energy_trend": "近30天能耗趨勢",
|
|
||||||
"today_energy_consumption": "本日能耗",
|
|
||||||
"this_month_energy_consumption": "本月能耗",
|
|
||||||
"relative_energy_consumption": "環比能耗",
|
|
||||||
"daily_relative_change": "日環比",
|
|
||||||
"weekly_relative_change": "周環比",
|
|
||||||
"monthly_relative_change": "月環比",
|
|
||||||
"yearly_relative_change": "年環比",
|
|
||||||
"today": "今日",
|
|
||||||
"yesterday": "昨日",
|
|
||||||
"this_week": "本周",
|
|
||||||
"last_week": "上周",
|
|
||||||
"this_month": "本月",
|
|
||||||
"last_month": "上月",
|
|
||||||
"this_year": "今年",
|
|
||||||
"last_year": "去年",
|
|
||||||
"work_order": "工單",
|
|
||||||
"system_status": "系統狀態"
|
|
||||||
},
|
},
|
||||||
"history": {
|
"history": {
|
||||||
"title": "歷史資料",
|
"title": "歷史資料",
|
||||||
@ -106,61 +79,12 @@
|
|||||||
"total_elec_cost": "總電費",
|
"total_elec_cost": "總電費",
|
||||||
"carbon_equivalent": "碳排當量",
|
"carbon_equivalent": "碳排當量",
|
||||||
"edit_carbon_emission": "編輯碳排放係數",
|
"edit_carbon_emission": "編輯碳排放係數",
|
||||||
"carbon_emission_coefficient": "碳排放係數",
|
"carbon_emission_coefficient":"碳排放係數"
|
||||||
"electricity_classification": "用電分類",
|
|
||||||
"electricity_price": "電費每度單價",
|
|
||||||
"floor": "樓層",
|
|
||||||
"maximum": "最大值",
|
|
||||||
"maximum_time": "最大值時間",
|
|
||||||
"minimum": "最小值",
|
|
||||||
"minimum_time": "最小值時間",
|
|
||||||
"average_value": "平均值",
|
|
||||||
"start_value": "起始值(kWh)",
|
|
||||||
"end_value": "截止值(kWh)",
|
|
||||||
"difference": "差值(kWh)",
|
|
||||||
"power_consumption": "用電量(kWh)",
|
|
||||||
"ranking": "排名",
|
|
||||||
"subtotal": "小計",
|
|
||||||
"unit_price": "單價",
|
|
||||||
"total_amount": "金額總計",
|
|
||||||
"elec_price_list": "電價表",
|
|
||||||
"residential": "住宅型",
|
|
||||||
"standard": "標準型",
|
|
||||||
"simple_elec_price_two_stage": "簡易型時間電價二段式",
|
|
||||||
"simple_elec_price_three_stage": "簡易型時間電價三段式",
|
|
||||||
"classification": "分類",
|
|
||||||
"summer_months": "夏月",
|
|
||||||
"non_summer_months": "非夏月",
|
|
||||||
"time_outside_summer_months": "夏月以外的時間",
|
|
||||||
"basic_elec_charge": "基本電費",
|
|
||||||
"charged_per_household": "按戶計收",
|
|
||||||
"per_household_month": "每戶每月",
|
|
||||||
"mon_to_friday": "週一~週五",
|
|
||||||
"peak_hours": "尖峰時間",
|
|
||||||
"semi_peak_hours": "半尖峰時間",
|
|
||||||
"off_peak_hours": "離峰時間",
|
|
||||||
"price_per_kwh": "每度",
|
|
||||||
"all_day": "全日",
|
|
||||||
"sat_sun_off_peak_days": "週六、週日及離峰日",
|
|
||||||
"usage_over_2000kwh": "每月總度數超過2000度之部分",
|
|
||||||
"add": "加",
|
|
||||||
"standard_time_of_use_tariff_2_stage": "標準型時間電價二段式",
|
|
||||||
"standard_time_of_use_tariff_3_stage": "標準型時間電價三段式",
|
|
||||||
"single_phase": "單相",
|
|
||||||
"three_phase": "三相",
|
|
||||||
"frequent_contract": "經常契約",
|
|
||||||
"per_kw_per_month": "每瓩每月",
|
|
||||||
"non_summer_contract": "非夏日契約",
|
|
||||||
"saturday_semi_peak_contract": "週六半尖峰契約",
|
|
||||||
"off_peak_contract": "離峰契約",
|
|
||||||
"variable_electricity_charge": "流動電費",
|
|
||||||
"saturday": "週六",
|
|
||||||
"sunday_and_off_peak_days": "週日及離峰日"
|
|
||||||
},
|
},
|
||||||
"alarm": {
|
"alarm": {
|
||||||
"title": "顯示警告",
|
"title": "顯示警告",
|
||||||
"notify": "異常通知",
|
"notify": "異常通知",
|
||||||
"number": "異常ID",
|
"number": "異常編號",
|
||||||
"category": "異常類別",
|
"category": "異常類別",
|
||||||
"device_name": "設備名稱",
|
"device_name": "設備名稱",
|
||||||
"message": "異常訊息",
|
"message": "異常訊息",
|
||||||
@ -178,10 +102,9 @@
|
|||||||
"end_date": "結束日期",
|
"end_date": "結束日期",
|
||||||
"building_and_floor": "棟別-樓層",
|
"building_and_floor": "棟別-樓層",
|
||||||
"uuid": "異常ID",
|
"uuid": "異常ID",
|
||||||
"alarmClass": "告警條件",
|
"alarmClass": "異常類別",
|
||||||
"device_name": "設備名稱",
|
"device_name": "設備名稱",
|
||||||
"device_number": "設備編號",
|
"device_number": "設備編號",
|
||||||
"device_point_name": "點位名稱",
|
|
||||||
"date": "發生日期",
|
"date": "發生日期",
|
||||||
"time": "發生時間",
|
"time": "發生時間",
|
||||||
"error_msg": "異常原因",
|
"error_msg": "異常原因",
|
||||||
@ -208,12 +131,10 @@
|
|||||||
"qualifications": "限定條件",
|
"qualifications": "限定條件",
|
||||||
"upper_limit": "上限",
|
"upper_limit": "上限",
|
||||||
"lower_limit": "下限",
|
"lower_limit": "下限",
|
||||||
"delay": "持續秒數",
|
|
||||||
"highDelay": "上限持續秒數",
|
"highDelay": "上限持續秒數",
|
||||||
"lowDelay": "下限持續秒數",
|
"lowDelay": "下限持續秒數",
|
||||||
"warning_method": "警示方式",
|
"warning_method": "警示方式",
|
||||||
"warning_time": "警示時間",
|
"warning_time": "警示時間",
|
||||||
"warning_value": "警示值",
|
|
||||||
"operation": "功能",
|
"operation": "功能",
|
||||||
"alarm_settings": "異常設定",
|
"alarm_settings": "異常設定",
|
||||||
"time_setting": "時間設定",
|
"time_setting": "時間設定",
|
||||||
@ -225,23 +146,7 @@
|
|||||||
"notify_email": "email",
|
"notify_email": "email",
|
||||||
"notify_items": "通知項目",
|
"notify_items": "通知項目",
|
||||||
"notify_list": "通知名單",
|
"notify_list": "通知名單",
|
||||||
"choose": "選擇",
|
"choose": "選擇"
|
||||||
"day_time": "星期/時間",
|
|
||||||
"click_time_period": "請用滑鼠點擊時間段",
|
|
||||||
"clear": "清空",
|
|
||||||
"sunday": "星期日",
|
|
||||||
"monday": "星期一",
|
|
||||||
"tuesday": "星期二",
|
|
||||||
"wednesday": "星期三",
|
|
||||||
"thursday": "星期四",
|
|
||||||
"friday": "星期五",
|
|
||||||
"saturday": "星期六",
|
|
||||||
"schedule_name": "時段名稱",
|
|
||||||
"schedule_content": "時段內容",
|
|
||||||
"reorganization": "MQTT 告警重整",
|
|
||||||
"online": "在線",
|
|
||||||
"offline": "離線",
|
|
||||||
"alarm": "告警"
|
|
||||||
},
|
},
|
||||||
"operation": {
|
"operation": {
|
||||||
"title": "運維管理",
|
"title": "運維管理",
|
||||||
@ -255,14 +160,13 @@
|
|||||||
"start_time": "預計開始時間",
|
"start_time": "預計開始時間",
|
||||||
"upload": "檔案上傳",
|
"upload": "檔案上傳",
|
||||||
"finish_time": "完成時間",
|
"finish_time": "完成時間",
|
||||||
"updated_time": "更新時間",
|
|
||||||
"operation": "功能",
|
"operation": "功能",
|
||||||
"vendor": "廠商",
|
"vendor": "廠商",
|
||||||
"contact_person": "聯絡人",
|
"contact_person": "聯絡人",
|
||||||
"phone": "電話",
|
"phone": "電話",
|
||||||
"email": "email",
|
"email": "email",
|
||||||
"created_at": "建立日期",
|
"created_at": "建立日期",
|
||||||
"maintenance": "保養",
|
"maintainance": "保養",
|
||||||
"repair": "維修",
|
"repair": "維修",
|
||||||
"company_info": "廠商資料",
|
"company_info": "廠商資料",
|
||||||
"repair_item": "維修項目",
|
"repair_item": "維修項目",
|
||||||
@ -270,8 +174,6 @@
|
|||||||
"responsible_vendor": "負責廠商",
|
"responsible_vendor": "負責廠商",
|
||||||
"not_completed": "未完成",
|
"not_completed": "未完成",
|
||||||
"completed": "已完成",
|
"completed": "已完成",
|
||||||
"complete": "已完成",
|
|
||||||
"incomplete": "未完成",
|
|
||||||
"worker_id": "工作人員編號",
|
"worker_id": "工作人員編號",
|
||||||
"notice": "注意事項",
|
"notice": "注意事項",
|
||||||
"result_description": "結果描述",
|
"result_description": "結果描述",
|
||||||
@ -304,13 +206,10 @@
|
|||||||
},
|
},
|
||||||
"assetManagement": {
|
"assetManagement": {
|
||||||
"title": "資產管理",
|
"title": "資產管理",
|
||||||
"add_system_category": "新增系統類別",
|
"add_category": "新增類別",
|
||||||
"edit_system_category": "修改系統類別",
|
|
||||||
"add_device_category": "新增設備類別",
|
|
||||||
"edit_device_category": "修改設備類別",
|
|
||||||
"system_name": "名稱",
|
"system_name": "名稱",
|
||||||
"system_value": "代號",
|
"system_value": "代號",
|
||||||
"system_parent": "所屬系統",
|
"system_parent": "所屬大類",
|
||||||
"device_number": "設備編號",
|
"device_number": "設備編號",
|
||||||
"device_name": "設備名稱",
|
"device_name": "設備名稱",
|
||||||
"asset_number": "資產編號",
|
"asset_number": "資產編號",
|
||||||
@ -338,10 +237,7 @@
|
|||||||
"associated_device": "關聯設備",
|
"associated_device": "關聯設備",
|
||||||
"choose": "選擇",
|
"choose": "選擇",
|
||||||
"index": "編號",
|
"index": "編號",
|
||||||
"floor_plan": "平面圖",
|
"floor_plan": "平面圖"
|
||||||
"department": "部門",
|
|
||||||
"department_name": "部門名稱",
|
|
||||||
"building": "棟別"
|
|
||||||
},
|
},
|
||||||
"accountManagement": {
|
"accountManagement": {
|
||||||
"account_title": "帳號管理",
|
"account_title": "帳號管理",
|
||||||
@ -385,44 +281,15 @@
|
|||||||
"email_format": "請輸入正確的 Email 地址",
|
"email_format": "請輸入正確的 Email 地址",
|
||||||
"password_format": "密碼長度至少8碼,必須包含英文及數字",
|
"password_format": "密碼長度至少8碼,必須包含英文及數字",
|
||||||
"start_time_placeholder": "請輸入預計開始日期",
|
"start_time_placeholder": "請輸入預計開始日期",
|
||||||
"finish_time_placeholder": "請輸入完成日期",
|
|
||||||
"rename": "重新命名",
|
"rename": "重新命名",
|
||||||
"download": "下載",
|
"download": "下載",
|
||||||
"confirm": "確認",
|
"confirm": "確認",
|
||||||
"restore": "復原",
|
"restore": "復原"
|
||||||
"stop_edit": "停止修改",
|
|
||||||
"start_edit": "開始修改",
|
|
||||||
"convert": "轉換"
|
|
||||||
},
|
},
|
||||||
"msg":{
|
"msg":{
|
||||||
"sure_to_delete": "是否確認刪除該項目?",
|
"sure_to_delete": "是否確認刪除該項目?",
|
||||||
"sure_to_delete_permanent": "是否確認永久刪除該項目?",
|
"sure_to_delete_permanent": "是否確認永久刪除該項目?",
|
||||||
"delete_success": "刪除成功",
|
"delete_success": "刪除成功",
|
||||||
"delete_failed": "刪除失敗",
|
"delete_failed": "刪除失敗"
|
||||||
"mqtt_refresh": "重新設定成功",
|
|
||||||
"schema_name_required": "架構名稱欄位必填",
|
|
||||||
"incorrect_format":"格式不正確",
|
|
||||||
"send_successfully":"送出成功",
|
|
||||||
"edit_successfully":"修改成功"
|
|
||||||
},
|
|
||||||
"setting": {
|
|
||||||
"electricity_meter":"電表",
|
|
||||||
"MQTT_parse": "MQTT 解析",
|
|
||||||
"schema": "架構",
|
|
||||||
"point": "點位",
|
|
||||||
"description": "描述",
|
|
||||||
"IoT_point_name": "IoT 點位名稱",
|
|
||||||
"IoT_point_code": "IoT 點位代號",
|
|
||||||
"number_of_decimal_places": "小數位數",
|
|
||||||
"boolean_value": "布林值",
|
|
||||||
"hide_point": "點位顯示",
|
|
||||||
"hide_switch": "switch 功能",
|
|
||||||
"switch_on_message": "switch 開啟時傳送的訊息",
|
|
||||||
"switch_off_message": "switch 關閉時傳送的訊息",
|
|
||||||
"schema_name": "架構名稱",
|
|
||||||
"IoT_point_structure": "IoT點位結構",
|
|
||||||
"system_point_name": "系統點位名稱",
|
|
||||||
"json_format_text": "請貼上 JSON 格式數據",
|
|
||||||
"json_click_text": "請在左側輸入JSON並點選轉換按鈕"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,51 +8,13 @@
|
|||||||
"table": {
|
"table": {
|
||||||
"no_data": "No data",
|
"no_data": "No data",
|
||||||
"in_otal": "items in total",
|
"in_otal": "items in total",
|
||||||
"skip_to": "Skip to",
|
"skip_to": "Skip to"
|
||||||
"serial_number": "Serial Number",
|
|
||||||
"name": "Name",
|
|
||||||
"time": "Time"
|
|
||||||
},
|
},
|
||||||
"upload": {
|
"upload": {
|
||||||
"title": "Select a file or drag and drop here",
|
"title": "Select a file or drag and drop here",
|
||||||
"description": "File size cannot exceed 10MB",
|
"description": "File size cannot exceed 10MB",
|
||||||
"formats": "File formats"
|
"formats": "File formats"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
|
||||||
"yesterday_today": "Yesterday / Today's",
|
|
||||||
"elec_consumption_comparison": "Electricity Consumption Comparison",
|
|
||||||
"elec_consumption_comparison_trend": "Electricity Consumption Comparison Trend",
|
|
||||||
"electricity_consumption": "electricity consumption",
|
|
||||||
"today_electricity_consumption": "Today's electricity consumption in kWH",
|
|
||||||
"yesterday_electricity_consumption": "Yesterday's electricity consumption in kWH",
|
|
||||||
"instant_power": "Instant power kW",
|
|
||||||
"instant_contract_capacity_ratio": "Instant contract capacity ratio %",
|
|
||||||
"this_last_week": "This Week's / Last Week's",
|
|
||||||
"thisweek_electricity_consumption": "This week’s electricity consumption",
|
|
||||||
"lastweek_electricity_consumption": "Last week’s electricity consumption",
|
|
||||||
"one_hour": "1 hour",
|
|
||||||
"four_hour": "4 hour",
|
|
||||||
"eight_hour": "8 hour",
|
|
||||||
"energy_ranking": "Energy consumption ranking",
|
|
||||||
"last_30_days_energy_trend": "Energy consumption trend for the past 30 days",
|
|
||||||
"today_energy_consumption": "Today",
|
|
||||||
"this_month_energy_consumption": "This month",
|
|
||||||
"relative_energy_consumption": "Energy consumption trend",
|
|
||||||
"daily_relative_change": "Daily",
|
|
||||||
"weekly_relative_change": "Weekly",
|
|
||||||
"monthly_relative_change": "Monthly",
|
|
||||||
"yearly_relative_change": "Yearly",
|
|
||||||
"today": "Today",
|
|
||||||
"yesterday": "Yesterday",
|
|
||||||
"this_week": "This week",
|
|
||||||
"last_week": "Last week",
|
|
||||||
"this_month": "This month",
|
|
||||||
"last_month": "Last month",
|
|
||||||
"this_year": "This year",
|
|
||||||
"last_year": "Last year",
|
|
||||||
"work_order": "Work Order",
|
|
||||||
"system_status": "System Status"
|
|
||||||
},
|
|
||||||
"history": {
|
"history": {
|
||||||
"title": "Historical Data",
|
"title": "Historical Data",
|
||||||
"building_name": "Building",
|
"building_name": "Building",
|
||||||
@ -71,6 +33,17 @@
|
|||||||
"end_date": "End date",
|
"end_date": "End date",
|
||||||
"end_time": "End time"
|
"end_time": "End time"
|
||||||
},
|
},
|
||||||
|
"dashboard": {
|
||||||
|
"yesterday_today": "Yesterday / Today's",
|
||||||
|
"elec_consumption_comparison": "Electricity Consumption Comparison",
|
||||||
|
"elec_consumption_comparison_trend": "Electricity Consumption Comparison Trend",
|
||||||
|
"electricity_consumption": "electricity consumption",
|
||||||
|
"today_electricity_consumption": "Today’s electricity consumption",
|
||||||
|
"yesterday_electricity_consumption": "Yesterday’s electricity consumption",
|
||||||
|
"this_last_week": "This Week's / Last Week's",
|
||||||
|
"thisweek_electricity_consumption": "This week’s electricity consumption",
|
||||||
|
"lastweek_electricity_consumption": "Last week’s electricity consumption"
|
||||||
|
},
|
||||||
"system": {
|
"system": {
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
@ -106,56 +79,7 @@
|
|||||||
"total_elec_cost": "Total Elec. Cost",
|
"total_elec_cost": "Total Elec. Cost",
|
||||||
"carbon_equivalent": "Carbon Equivalent",
|
"carbon_equivalent": "Carbon Equivalent",
|
||||||
"edit_carbon_emission": "Edit carbon emission coefficient",
|
"edit_carbon_emission": "Edit carbon emission coefficient",
|
||||||
"carbon_emission_coefficient": "Carbon emission coefficient",
|
"carbon_emission_coefficient":"Carbon emission coefficient"
|
||||||
"electricity_classification": "Electricity Classification",
|
|
||||||
"electricity_price": "Electricity charge per unit price",
|
|
||||||
"floor": "Floor",
|
|
||||||
"maximum": "Maximum",
|
|
||||||
"maximum_time": "Maximum time",
|
|
||||||
"minimum": "Minimum value",
|
|
||||||
"minimum_time": "Minimum time",
|
|
||||||
"average_value": "Average value",
|
|
||||||
"start_value": "Start value (kWh)",
|
|
||||||
"end_value": "End value (kWh)",
|
|
||||||
"difference": "Difference (kWh)",
|
|
||||||
"power_consumption": "Power consumption (kWh)",
|
|
||||||
"ranking": "Ranking",
|
|
||||||
"subtotal": "Subtotal",
|
|
||||||
"unit_price": "Unit price",
|
|
||||||
"total_amount": "Total amount",
|
|
||||||
"elec_price_list": "Electricity Price List",
|
|
||||||
"residential": "Residential",
|
|
||||||
"standard": "Standard",
|
|
||||||
"simple_elec_price_two_stage": "Simple Time-of-Use Electricity Price (Two-Tier)",
|
|
||||||
"simple_elec_price_three_stage": "Simple Time-of-Use Electricity Price (Three-Tier)",
|
|
||||||
"classification": "Classification",
|
|
||||||
"summer_months": "Summer Months",
|
|
||||||
"non_summer_months": "Non-Summer Months",
|
|
||||||
"time_outside_summer_months": "Time Outside Summer Months",
|
|
||||||
"basic_elec_charge": "Basic Electricity Charge",
|
|
||||||
"charged_per_household": "Charged Per Household",
|
|
||||||
"per_household_month": "Per Household Per Month",
|
|
||||||
"mon_to_friday": "Monday to Friday",
|
|
||||||
"peak_hours": "Peak Hours",
|
|
||||||
"semi_peak_hours": "Semi-Peak Hours",
|
|
||||||
"off_peak_hours": "Off-Peak Hours",
|
|
||||||
"price_per_kwh": "Price Per kWh",
|
|
||||||
"all_day": "All Day",
|
|
||||||
"sat_sun_off_peak_days": "Saturday, Sunday, and Off-Peak Days",
|
|
||||||
"usage_over_2000kwh": "Usage Over 2000 kWh Per Month",
|
|
||||||
"add": "Add",
|
|
||||||
"standard_time_of_use_tariff_2_stage": "Standard Time-of-Use Tariff (Two-Tier)",
|
|
||||||
"standard_time_of_use_tariff_3_stage": "Standard Time-of-Use Tariff (Three-Tier)",
|
|
||||||
"single_phase": "Single Phase",
|
|
||||||
"three_phase": "Three Phase",
|
|
||||||
"frequent_contract": "Demand Charge",
|
|
||||||
"per_kw_per_month": "Per kW Per Month",
|
|
||||||
"non_summer_contract": "Non-Summer Demand Charge",
|
|
||||||
"saturday_semi_peak_contract": "Saturday Semi-Peak Demand Charge",
|
|
||||||
"off_peak_contract": "Off-Peak Demand Charge",
|
|
||||||
"variable_electricity_charge": "Variable Electricity Charge",
|
|
||||||
"saturday": "Saturday",
|
|
||||||
"sunday_and_off_peak_days": "Sunday and Off-Peak Days"
|
|
||||||
},
|
},
|
||||||
"alarm": {
|
"alarm": {
|
||||||
"title": "Warning",
|
"title": "Warning",
|
||||||
@ -178,10 +102,9 @@
|
|||||||
"end_date": "End Date",
|
"end_date": "End Date",
|
||||||
"building_and_floor": "Building - Floor",
|
"building_and_floor": "Building - Floor",
|
||||||
"uuid": "Exception ID",
|
"uuid": "Exception ID",
|
||||||
"alarmClass": "Alarm Conditions",
|
"alarmClass": "Exception Category",
|
||||||
"device_name": "Device Name",
|
"device_name": "Device Name",
|
||||||
"device_number": "Device Number",
|
"device_number": "Device Number",
|
||||||
"device_point_name": "Point Name",
|
|
||||||
"date": "Occurrence Date",
|
"date": "Occurrence Date",
|
||||||
"time": "Occurrence Time",
|
"time": "Occurrence Time",
|
||||||
"error_msg": "Abnormal Cause",
|
"error_msg": "Abnormal Cause",
|
||||||
@ -208,12 +131,10 @@
|
|||||||
"qualifications": "Qualifications",
|
"qualifications": "Qualifications",
|
||||||
"upper_limit": "Upper Limit",
|
"upper_limit": "Upper Limit",
|
||||||
"lower_limit": "Lower Limit",
|
"lower_limit": "Lower Limit",
|
||||||
"delay": "Duration (s)",
|
|
||||||
"highDelay": "Max Duration (s)",
|
"highDelay": "Max Duration (s)",
|
||||||
"lowDelay": "Min Duration (s)",
|
"lowDelay": "Min Duration (s)",
|
||||||
"warning_method": "Warning Method",
|
"warning_method": "Warning Method",
|
||||||
"warning_time": "Warning Time",
|
"warning_time": "Warning Time",
|
||||||
"warning_value": "Warning Value",
|
|
||||||
"operation": "Function",
|
"operation": "Function",
|
||||||
"alarm_settings": "Abnormal Alarm Settings",
|
"alarm_settings": "Abnormal Alarm Settings",
|
||||||
"time_setting": "Time Setting",
|
"time_setting": "Time Setting",
|
||||||
@ -225,23 +146,7 @@
|
|||||||
"notify_email": "Email",
|
"notify_email": "Email",
|
||||||
"notify_items": "Notification Items",
|
"notify_items": "Notification Items",
|
||||||
"notify_list": "Notification List",
|
"notify_list": "Notification List",
|
||||||
"choose": "Choose",
|
"choose": "Choose"
|
||||||
"day_time": "Week/Time",
|
|
||||||
"click_time_period": "Please click the time period with your mouse",
|
|
||||||
"clear": "Clear",
|
|
||||||
"sunday": "Sunday",
|
|
||||||
"monday": "Monday",
|
|
||||||
"tuesday": "Tuesday",
|
|
||||||
"wednesday": "Wednesday",
|
|
||||||
"thursday": "Thursday",
|
|
||||||
"friday": "Friday",
|
|
||||||
"saturday": "Saturday",
|
|
||||||
"schedule_name": "Time period name",
|
|
||||||
"schedule_content": "Time period content",
|
|
||||||
"reorganization": "MQTT Alarm Reorganization",
|
|
||||||
"online": "Online",
|
|
||||||
"offline": "Offline",
|
|
||||||
"alarm": "Alarm"
|
|
||||||
},
|
},
|
||||||
"operation": {
|
"operation": {
|
||||||
"title": "Operation And Maintenance Management",
|
"title": "Operation And Maintenance Management",
|
||||||
@ -255,7 +160,6 @@
|
|||||||
"start_time": "Estimated Start Time",
|
"start_time": "Estimated Start Time",
|
||||||
"upload": "File Upload",
|
"upload": "File Upload",
|
||||||
"finish_time": "Completion Time",
|
"finish_time": "Completion Time",
|
||||||
"updated_time": "Update Time",
|
|
||||||
"operation": "Function",
|
"operation": "Function",
|
||||||
"vendor": "Company",
|
"vendor": "Company",
|
||||||
"contact_person": "Contact Person",
|
"contact_person": "Contact Person",
|
||||||
@ -270,8 +174,6 @@
|
|||||||
"responsible_vendor": "Responsible Vendor",
|
"responsible_vendor": "Responsible Vendor",
|
||||||
"not_completed": "Not Completed",
|
"not_completed": "Not Completed",
|
||||||
"completed": "Completed",
|
"completed": "Completed",
|
||||||
"complete": "Comp",
|
|
||||||
"incomplete": "Inc",
|
|
||||||
"worker_id": "Worker ID",
|
"worker_id": "Worker ID",
|
||||||
"notice": "Notice",
|
"notice": "Notice",
|
||||||
"result_description": "Result Description",
|
"result_description": "Result Description",
|
||||||
@ -304,13 +206,10 @@
|
|||||||
},
|
},
|
||||||
"assetManagement": {
|
"assetManagement": {
|
||||||
"title": "Asset Management",
|
"title": "Asset Management",
|
||||||
"add_system_category": "Add system category",
|
"add_category": "Add Category",
|
||||||
"edit_system_category": "Edit system category",
|
|
||||||
"add_device_category": "Add device category",
|
|
||||||
"edit_device_category": "Edit device category",
|
|
||||||
"system_name": "Name",
|
"system_name": "Name",
|
||||||
"system_value": "Code",
|
"system_value": "Code",
|
||||||
"system_parent": "System category",
|
"system_parent": "Category",
|
||||||
"device_number": "Device Number",
|
"device_number": "Device Number",
|
||||||
"device_name": "Device Name",
|
"device_name": "Device Name",
|
||||||
"asset_number": "Asset Number",
|
"asset_number": "Asset Number",
|
||||||
@ -338,10 +237,7 @@
|
|||||||
"associated_device": "Associated Devices",
|
"associated_device": "Associated Devices",
|
||||||
"choose": "Choose",
|
"choose": "Choose",
|
||||||
"index": "Serial Number",
|
"index": "Serial Number",
|
||||||
"floor_plan": "Floor Plan",
|
"floor_plan": "Floor Plan"
|
||||||
"department": "Department",
|
|
||||||
"department_name": "Department Name",
|
|
||||||
"building": "Building"
|
|
||||||
},
|
},
|
||||||
"accountManagement": {
|
"accountManagement": {
|
||||||
"account_title": "Account Management",
|
"account_title": "Account Management",
|
||||||
@ -385,44 +281,15 @@
|
|||||||
"email_format": "Please enter correct email address",
|
"email_format": "Please enter correct email address",
|
||||||
"password_format": "The password must be at least 8 characters long and must contain English and numbers.",
|
"password_format": "The password must be at least 8 characters long and must contain English and numbers.",
|
||||||
"start_time_placeholder": "Please enter expected start date",
|
"start_time_placeholder": "Please enter expected start date",
|
||||||
"finish_time_placeholder": "Please enter completion date",
|
|
||||||
"rename": "Rename",
|
"rename": "Rename",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
"restore": "Restore",
|
"restore": "Restore"
|
||||||
"stop_edit": "Stop editing",
|
|
||||||
"start_edit": "Start editing",
|
|
||||||
"convert": "Convert"
|
|
||||||
},
|
},
|
||||||
"msg": {
|
"msg": {
|
||||||
"sure_to_delete": "Are you sure to delete this item?",
|
"sure_to_delete": "Are you sure to delete this item?",
|
||||||
"sure_to_delete_permanent": "Are you sure you want to permanently delete this item?",
|
"sure_to_delete_permanent": "Are you sure you want to permanently delete this item?",
|
||||||
"delete_success": "Delete successfully",
|
"delete_success": "Delete successfully",
|
||||||
"delete_failed": "Delete failed",
|
"delete_failed": "Delete failed"
|
||||||
"mqtt_refresh": "MQTT reset successful",
|
|
||||||
"schema_name_required": "The schema name field is required",
|
|
||||||
"incorrect_format":"Incorrect format",
|
|
||||||
"send_successfully":"Sent successfully",
|
|
||||||
"edit_successfully":"Edited successfully"
|
|
||||||
},
|
|
||||||
"setting": {
|
|
||||||
"electricity_meter": "Electricity Meter",
|
|
||||||
"MQTT_parse": "MQTT Parse",
|
|
||||||
"schema": "Schema",
|
|
||||||
"point": "Point",
|
|
||||||
"description": "Description",
|
|
||||||
"IoT_point_name": "IoT Point Name",
|
|
||||||
"IoT_point_code": "IoT Point Code",
|
|
||||||
"number_of_decimal_places": "Number of Decimal Places",
|
|
||||||
"boolean_value": "Boolean Value",
|
|
||||||
"hide_point": "Point Display",
|
|
||||||
"hide_switch": "Switch Function",
|
|
||||||
"switch_on_message": "Switch On Message",
|
|
||||||
"switch_off_message": "Switch Off Message",
|
|
||||||
"schema_name": "Schema name",
|
|
||||||
"IoT_point_structure": "IoT Point Structure",
|
|
||||||
"system_point_name": "System Point Name",
|
|
||||||
"json_format_text": "Please paste JSON format data",
|
|
||||||
"json_click_text": "Please enter JSON on the left and click the conversion button"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,10 +61,4 @@ export const AUTHPAGES = [
|
|||||||
pageName: "ProductSetting",
|
pageName: "ProductSetting",
|
||||||
navigate: "/productSetting",
|
navigate: "/productSetting",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
authCode: "PF11",
|
|
||||||
icon: "cog",
|
|
||||||
pageName: "Setting",
|
|
||||||
navigate: "/Setting",
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|||||||
@ -38,7 +38,6 @@ import {
|
|||||||
faFileExcel,
|
faFileExcel,
|
||||||
faFileWord,
|
faFileWord,
|
||||||
faFilePowerpoint,
|
faFilePowerpoint,
|
||||||
faFileArchive,
|
|
||||||
faFileAlt,
|
faFileAlt,
|
||||||
faDatabase,
|
faDatabase,
|
||||||
faBuilding,
|
faBuilding,
|
||||||
@ -58,14 +57,8 @@ import {
|
|||||||
faEye,
|
faEye,
|
||||||
faEyeSlash,
|
faEyeSlash,
|
||||||
faGlobe,
|
faGlobe,
|
||||||
faDownload,
|
faDownload
|
||||||
faStream,
|
|
||||||
faSave,
|
|
||||||
faCrown,
|
|
||||||
faClock,
|
|
||||||
faCheckCircle
|
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { faCircle,faPaperPlane } from "@fortawesome/free-regular-svg-icons";
|
|
||||||
|
|
||||||
/* add icons to the library */
|
/* add icons to the library */
|
||||||
library.add(
|
library.add(
|
||||||
@ -104,7 +97,6 @@ library.add(
|
|||||||
faFileExcel,
|
faFileExcel,
|
||||||
faFileWord,
|
faFileWord,
|
||||||
faFilePowerpoint,
|
faFilePowerpoint,
|
||||||
faFileArchive,
|
|
||||||
faFileAlt,
|
faFileAlt,
|
||||||
faDatabase,
|
faDatabase,
|
||||||
faBuilding,
|
faBuilding,
|
||||||
@ -124,14 +116,7 @@ library.add(
|
|||||||
faEye,
|
faEye,
|
||||||
faEyeSlash,
|
faEyeSlash,
|
||||||
faGlobe,
|
faGlobe,
|
||||||
faDownload,
|
faDownload
|
||||||
faStream,
|
|
||||||
faSave,
|
|
||||||
faCrown,
|
|
||||||
faClock,
|
|
||||||
faCheckCircle,
|
|
||||||
faCircle,
|
|
||||||
faPaperPlane
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export default library;
|
export default library;
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import useSelectedFloor from "@/hooks/useSelectedFloor";
|
import useSelectedFloor from "@/hooks/useSelectedFloor";
|
||||||
import { watch, ref, inject } from "vue";
|
import { watch, ref, inject } from "vue";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import useSystemShowData from "@/hooks/useSystemShowData";
|
|
||||||
|
|
||||||
function useForgeFloor() {
|
function useForgeFloor() {
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@ -60,11 +59,9 @@ function useForgeFloor() {
|
|||||||
forgeViewer.value.hide(parseInt(allDbIdsStr[i]));
|
forgeViewer.value.hide(parseInt(allDbIdsStr[i]));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const { flatSubData } = useSystemShowData();
|
|
||||||
const showDbIdFn = () => {
|
const showDbIdFn = () => {
|
||||||
hideDbIdFn();
|
hideDbIdFn();
|
||||||
flatSubData.value.forEach((value, index) => {
|
subscribeData.value.forEach((value, index) => {
|
||||||
forgeViewer.value.show(value.forge_dbid);
|
forgeViewer.value.show(value.forge_dbid);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -34,13 +34,12 @@ export default function useForgeHeatmap() {
|
|||||||
const { flatSubData } = useSystemShowData();
|
const { flatSubData } = useSystemShowData();
|
||||||
|
|
||||||
const data = computed(() =>
|
const data = computed(() =>
|
||||||
flatSubData.value?.map((d) => {
|
flatSubData.value?.map((d) => ({
|
||||||
const pointsMap = d.points ? Object.fromEntries(d.points.map(({ point, value }) => [point, 0])) : {};
|
|
||||||
return {
|
|
||||||
...d,
|
...d,
|
||||||
...pointsMap,
|
...Object.fromEntries(
|
||||||
};
|
d.points.map(({ point, value }) => [point, 0]) || []
|
||||||
})
|
),
|
||||||
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { watch, inject, markRaw, ref, computed, provide } from "vue";
|
import { watch, inject, markRaw, ref, computed, provide } from "vue";
|
||||||
import useAlarmStore from "@/stores/useAlarmStore";
|
import useAlarmStore from "@/stores/useAlarmStore";
|
||||||
import hexToRgb from "@/util/hexToRgb";
|
import hexToRgb from "@/util/hexToRgb";
|
||||||
import useSystemShowData from "@/hooks/useSystemShowData";
|
import useSystemShowData from "@/hooks/useSystemShowData"
|
||||||
import useForgeHeatmap from "./useForgeHeatmap";
|
import useForgeHeatmap from "./useForgeHeatmap";
|
||||||
import useForgeFloor from "./useForgeFloor";
|
import useForgeFloor from "./useForgeFloor";
|
||||||
|
|
||||||
export default function useForgeSprite() {
|
export default function useForgeSprite() {
|
||||||
const { subscribeData, realtimeData } = inject("system_deviceList");
|
const { subscribeData } = inject("system_deviceList");
|
||||||
const { getCurrentInfoModalData, clearSelectedDeviceInfo, selected_dbid } =
|
const { getCurrentInfoModalData, clearSelectedDeviceInfo, selected_dbid } =
|
||||||
inject("system_selectedDevice");
|
inject("system_selectedDevice");
|
||||||
const forgeViewer = ref(null);
|
const forgeViewer = ref(null);
|
||||||
@ -16,18 +16,6 @@ export default function useForgeSprite() {
|
|||||||
const { createHeatMap, updateViewExtension } = useForgeHeatmap();
|
const { createHeatMap, updateViewExtension } = useForgeHeatmap();
|
||||||
const { updateViewerFloor } = useForgeFloor();
|
const { updateViewerFloor } = useForgeFloor();
|
||||||
|
|
||||||
const setCameraPosition = (position, target) => {
|
|
||||||
// 使用 THREE.Vector3 定義位置與焦點
|
|
||||||
const newPosition = new THREE.Vector3(position.x, position.y, position.z);
|
|
||||||
const newTarget = new THREE.Vector3(target.x, target.y, target.z);
|
|
||||||
|
|
||||||
// 設定攝影機的新位置與焦點
|
|
||||||
forgeViewer.value.navigation.setView(newPosition, newTarget);
|
|
||||||
|
|
||||||
// 確保 Home 視角
|
|
||||||
forgeViewer.value.autocam.setCurrentViewAsHome(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const updateDataVisualization = async (viewer) => {
|
const updateDataVisualization = async (viewer) => {
|
||||||
if (!forgeViewer.value) {
|
if (!forgeViewer.value) {
|
||||||
forgeViewer.value = markRaw(viewer);
|
forgeViewer.value = markRaw(viewer);
|
||||||
@ -38,7 +26,7 @@ export default function useForgeSprite() {
|
|||||||
);
|
);
|
||||||
dataVizExtn.value = markRaw(dataVisualization);
|
dataVizExtn.value = markRaw(dataVisualization);
|
||||||
updateViewExtension(markRaw(viewer), markRaw(dataVisualization));
|
updateViewExtension(markRaw(viewer), markRaw(dataVisualization));
|
||||||
updateViewerFloor(markRaw(viewer), markRaw(dataVisualization));
|
updateViewerFloor(markRaw(viewer), markRaw(dataVisualization))
|
||||||
};
|
};
|
||||||
|
|
||||||
function onSpriteClicked(event) {
|
function onSpriteClicked(event) {
|
||||||
@ -63,24 +51,7 @@ export default function useForgeSprite() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { flatSubData } = useSystemShowData();
|
const { flatSubData } = useSystemShowData()
|
||||||
|
|
||||||
// 根據設備取得即時狀態顏色
|
|
||||||
const getDeviceRealtimeColor = (d) => {
|
|
||||||
if (d.full_name === "SmartSocket-AA001") return "#ff0000";
|
|
||||||
if (
|
|
||||||
d.full_name === "SmartSocket-AA003" ||
|
|
||||||
d.full_name === "SmartSocket-AA004"
|
|
||||||
)
|
|
||||||
return "#888888";
|
|
||||||
const realtimeDevice = realtimeData?.value?.find(
|
|
||||||
(item) => item.device_number === d.device_number
|
|
||||||
);
|
|
||||||
const state = realtimeDevice?.state || "";
|
|
||||||
if (state === "offnormal" || state === "")
|
|
||||||
return d.device_close_color || "#999999";
|
|
||||||
return d.device_normal_color || "#009100";
|
|
||||||
};
|
|
||||||
|
|
||||||
// 創建 sprites
|
// 創建 sprites
|
||||||
const createSprites = async () => {
|
const createSprites = async () => {
|
||||||
@ -89,27 +60,30 @@ export default function useForgeSprite() {
|
|||||||
const DataVizCore = Autodesk.DataVisualization.Core;
|
const DataVizCore = Autodesk.DataVisualization.Core;
|
||||||
const viewableType = DataVizCore.ViewableType.SPRITE;
|
const viewableType = DataVizCore.ViewableType.SPRITE;
|
||||||
let spriteColor = new THREE.Color(0xffffff);
|
let spriteColor = new THREE.Color(0xffffff);
|
||||||
const BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
const BASEURL = import.meta.env.VITE_FORGE_BASEURL;
|
||||||
const spriteIconUrl = `${BASEURL}/dist/hotspot.svg`;
|
const spriteIconUrl = `${BASEURL}/hotspot.svg`;
|
||||||
|
const style = new DataVizCore.ViewableStyle(
|
||||||
|
viewableType,
|
||||||
|
spriteColor,
|
||||||
|
spriteIconUrl
|
||||||
|
);
|
||||||
const viewableData = new DataVizCore.ViewableData();
|
const viewableData = new DataVizCore.ViewableData();
|
||||||
viewableData.spriteSize = 24; // Sprites as points of size 24 x 24 pixels
|
viewableData.spriteSize = 24; // Sprites as points of size 24 x 24 pixels
|
||||||
flatSubData.value?.forEach((d, index) => {
|
flatSubData.value?.forEach((d, index) => {
|
||||||
if (d.device_coordinate_3d) {
|
if (d.device_coordinate_3d) {
|
||||||
const position = d.device_coordinate_3d;
|
const position = d.device_coordinate_3d;
|
||||||
// 每個都 new 一個 style
|
style.color = new THREE.Color(hexToRgb(d.device_normal_color));
|
||||||
const pointStyle = new DataVizCore.ViewableStyle(
|
|
||||||
viewableType,
|
|
||||||
new THREE.Color(hexToRgb(getDeviceRealtimeColor(d))),
|
|
||||||
spriteIconUrl
|
|
||||||
);
|
|
||||||
const viewable = new DataVizCore.SpriteViewable(
|
const viewable = new DataVizCore.SpriteViewable(
|
||||||
position,
|
position,
|
||||||
pointStyle,
|
style,
|
||||||
d.spriteDbId
|
d.spriteDbId
|
||||||
);
|
);
|
||||||
viewableData.addViewable(viewable);
|
viewableData.addViewable(viewable);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// await viewableData.finish();
|
||||||
|
// dataVizExtn.value.addViewables(viewableData);
|
||||||
|
// console.log(dataVizExtn.value);
|
||||||
viewableData.finish().then(
|
viewableData.finish().then(
|
||||||
() => {
|
() => {
|
||||||
dataVizExtn.value.addViewables(viewableData);
|
dataVizExtn.value.addViewables(viewableData);
|
||||||
@ -121,16 +95,6 @@ export default function useForgeSprite() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// 監聽 realtimeData 變化,重建 sprites
|
|
||||||
watch(
|
|
||||||
() => realtimeData?.value,
|
|
||||||
() => {
|
|
||||||
if (forgeViewer.value?.isLoadDone()) {
|
|
||||||
createSprites();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => flatSubData,
|
() => flatSubData,
|
||||||
@ -185,6 +149,7 @@ export default function useForgeSprite() {
|
|||||||
const fov = nav.getVerticalFov();
|
const fov = nav.getVerticalFov();
|
||||||
nav.setRequestTransition(true, camera.position, target, fov);
|
nav.setRequestTransition(true, camera.position, target, fov);
|
||||||
|
|
||||||
|
|
||||||
if (lastClickedDbId !== null && lastClickedDbId !== spriteDbId) {
|
if (lastClickedDbId !== null && lastClickedDbId !== spriteDbId) {
|
||||||
dataVizExtn.value.invalidateViewables([lastClickedDbId], (viewable) => {
|
dataVizExtn.value.invalidateViewables([lastClickedDbId], (viewable) => {
|
||||||
return {
|
return {
|
||||||
@ -200,11 +165,13 @@ export default function useForgeSprite() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
lastClickedDbId = spriteDbId;
|
lastClickedDbId = spriteDbId;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error in cardfitToView:", error);
|
console.error("Error in cardfitToView:", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const hideAllObjects = () => {
|
const hideAllObjects = () => {
|
||||||
const tree = forgeViewer.value.model.getInstanceTree();
|
const tree = forgeViewer.value.model.getInstanceTree();
|
||||||
const allDbIdsStr = Object.keys(tree.nodeAccess.dbIdToIndex);
|
const allDbIdsStr = Object.keys(tree.nodeAccess.dbIdToIndex);
|
||||||
@ -215,7 +182,7 @@ export default function useForgeSprite() {
|
|||||||
|
|
||||||
const showSubSystemObjects = () => {
|
const showSubSystemObjects = () => {
|
||||||
hideAllObjects();
|
hideAllObjects();
|
||||||
flatSubData.value.forEach((value, index) => {
|
subscribeData.value.forEach((value, index) => {
|
||||||
forgeViewer.value.show(value.forge_dbid);
|
forgeViewer.value.show(value.forge_dbid);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -234,9 +201,10 @@ export default function useForgeSprite() {
|
|||||||
forgeViewer.value.tearDown();
|
forgeViewer.value.tearDown();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createSprites,
|
createSprites,
|
||||||
setCameraPosition,
|
|
||||||
updateDataVisualization,
|
updateDataVisualization,
|
||||||
showSubSystemObjects,
|
showSubSystemObjects,
|
||||||
forgeClickListener,
|
forgeClickListener,
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import "virtual:svg-icons-register";
|
|||||||
// 引入项目中的全部全局组件
|
// 引入项目中的全部全局组件
|
||||||
import SvgIcon from "@/components/svgIcon.vue";
|
import SvgIcon from "@/components/svgIcon.vue";
|
||||||
import library from "./fontawsomeIconRegister";
|
import library from "./fontawsomeIconRegister";
|
||||||
|
import "flag-icons/css/flag-icons.min.css";
|
||||||
|
|
||||||
/* import font awesome icon component */
|
/* import font awesome icon component */
|
||||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import AssetManagement from "@/views/AssetManagement/AssetManagement.vue";
|
|||||||
import AlertManagement from "@/views/alert/AlertManagement.vue";
|
import AlertManagement from "@/views/alert/AlertManagement.vue";
|
||||||
import ProductSetting from "@/views/productSetting/ProductSetting.vue";
|
import ProductSetting from "@/views/productSetting/ProductSetting.vue";
|
||||||
import EnergyManagement from "@/views/energyManagement/EnergyManagement.vue";
|
import EnergyManagement from "@/views/energyManagement/EnergyManagement.vue";
|
||||||
import SettingManagement from "@/views/setting/SettingManagement.vue";
|
|
||||||
import Login from "@/views/login/Login.vue";
|
import Login from "@/views/login/Login.vue";
|
||||||
import useUserInfoStore from "@/stores/useUserInfoStore";
|
import useUserInfoStore from "@/stores/useUserInfoStore";
|
||||||
import useGetCookie from "@/hooks/useGetCookie";
|
import useGetCookie from "@/hooks/useGetCookie";
|
||||||
@ -81,15 +80,10 @@ const router = createRouter({
|
|||||||
component: ProductSetting,
|
component: ProductSetting,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/energyManagement/:main_system_id/:sub_system_id/:type",
|
path: "/energyManagement",
|
||||||
name: "energyManagement",
|
name: "energyManagement",
|
||||||
component: EnergyManagement,
|
component: EnergyManagement,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/setting/:main_system_id/:sub_system_id/:type",
|
|
||||||
name: "setting",
|
|
||||||
component: SettingManagement,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "/mytestfile/mjm",
|
path: "/mytestfile/mjm",
|
||||||
name: "mytestfile",
|
name: "mytestfile",
|
||||||
@ -105,19 +99,22 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
const authRequired = !publicPages.includes(to.path);
|
const authRequired = !publicPages.includes(to.path);
|
||||||
const auth = useUserInfoStore();
|
const auth = useUserInfoStore();
|
||||||
const token = useGetCookie("JWT-Authorization");
|
const token = useGetCookie("JWT-Authorization");
|
||||||
const user_name = useGetCookie("user_name");
|
|
||||||
|
if (to.path === "/logout") {
|
||||||
|
document.cookie = "JWT-Authorization=";
|
||||||
|
auth.user.token = "";
|
||||||
|
window.location.reload();
|
||||||
|
next({ path: "/login" });
|
||||||
|
}
|
||||||
|
|
||||||
if ((authRequired && !token) || to.path === "/") {
|
if ((authRequired && !token) || to.path === "/") {
|
||||||
auth.user.token = "";
|
auth.user.token = "";
|
||||||
next({ path: "/login" });
|
next({ path: "/login" });
|
||||||
} else if (!authRequired) {
|
} else if (!authRequired) {
|
||||||
document.cookie = "JWT-Authorization=; Max-Age=0";
|
document.cookie = "JWT-Authorization=";
|
||||||
document.cookie = "user_name=; Max-Age=0";
|
|
||||||
auth.user.token = "";
|
auth.user.token = "";
|
||||||
auth.user.user_name = "";
|
|
||||||
} else {
|
} else {
|
||||||
auth.user.token = token;
|
auth.user.token = token;
|
||||||
auth.user.user_name = user_name;
|
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,22 +1,17 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { ref, computed, watch } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import { getBuildings } from "@/apis/building";
|
|
||||||
import { getDashboard2D3D } from "@/apis/dashboard";
|
|
||||||
import { getAssetFloorList, getDepartmentList } from "@/apis/asset";
|
|
||||||
|
|
||||||
const useBuildingStore = defineStore("buildingInfo", () => {
|
const useBuildingStore = defineStore("buildingInfo", () => {
|
||||||
// 狀態定義
|
// 所有棟別
|
||||||
const buildings = ref([]);
|
const buildings = ref([]);
|
||||||
const selectedBuilding = ref(null);
|
|
||||||
const floorList = ref([]);
|
|
||||||
const deptList = ref([]);
|
|
||||||
const mainSubSys = ref([]);
|
|
||||||
// 控制顯示2D/3D切換與內容
|
|
||||||
const showForgeArea = ref(true);
|
|
||||||
const previewImageExt = ref("");
|
|
||||||
|
|
||||||
// 計算屬性
|
const selectedBuilding = ref(null);
|
||||||
|
|
||||||
|
// 所有大小類系統
|
||||||
|
const mainSubSys = ref([]);
|
||||||
|
|
||||||
|
// 所有大類
|
||||||
const mainSys = computed(() =>
|
const mainSys = computed(() =>
|
||||||
mainSubSys.value.map(({ main_system_tag, full_name }) => ({
|
mainSubSys.value.map(({ main_system_tag, full_name }) => ({
|
||||||
main_system_tag,
|
main_system_tag,
|
||||||
@ -26,6 +21,7 @@ const useBuildingStore = defineStore("buildingInfo", () => {
|
|||||||
|
|
||||||
const subSys = computed(() => {
|
const subSys = computed(() => {
|
||||||
let subPages = [];
|
let subPages = [];
|
||||||
|
|
||||||
mainSubSys.value.forEach(({ main_system_tag, history_Sub_systems }) => {
|
mainSubSys.value.forEach(({ main_system_tag, history_Sub_systems }) => {
|
||||||
subPages = [
|
subPages = [
|
||||||
...subPages,
|
...subPages,
|
||||||
@ -36,6 +32,7 @@ const useBuildingStore = defineStore("buildingInfo", () => {
|
|||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
return subPages;
|
return subPages;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -47,89 +44,14 @@ const useBuildingStore = defineStore("buildingInfo", () => {
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 獲取所有建築物
|
|
||||||
const fetchBuildings = async () => {
|
|
||||||
// const res = await getBuildings();
|
|
||||||
buildings.value = JSON.parse(localStorage.getItem("CviBuildingList")) || [];
|
|
||||||
const storedBuilding = JSON.parse(localStorage.getItem("CviBuilding"));
|
|
||||||
if (buildings.value.length > 0) {
|
|
||||||
selectedBuilding.value = storedBuilding || buildings.value[0]; // 預設選第一個建築
|
|
||||||
} else {
|
|
||||||
selectedBuilding.value = null; // 如果沒有建築物,清空選擇
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 獲取樓層資料
|
|
||||||
const fetchFloorList = async (building_guid) => {
|
|
||||||
const res = await getAssetFloorList(building_guid);
|
|
||||||
floorList.value =
|
|
||||||
res.data[0]?.floors.map((d) => ({
|
|
||||||
...d,
|
|
||||||
title: d.full_name,
|
|
||||||
key: d.floor_guid,
|
|
||||||
})) || [];
|
|
||||||
};
|
|
||||||
|
|
||||||
// 獲取部門資料
|
|
||||||
const fetchDepartmentList = async () => {
|
|
||||||
const res = await getDepartmentList();
|
|
||||||
deptList.value =
|
|
||||||
res.data.map((d) => ({
|
|
||||||
...d,
|
|
||||||
title: d.name,
|
|
||||||
key: d.id,
|
|
||||||
})) || [];
|
|
||||||
};
|
|
||||||
|
|
||||||
// 獲取2D、3D顯示與否
|
|
||||||
const fetchDashboard2D3D = async (BuildingId) => {
|
|
||||||
const res = await getDashboard2D3D(BuildingId);
|
|
||||||
showForgeArea.value = res.data.is3DEnabled;
|
|
||||||
previewImageExt.value = res.data.previewImageExt || "";
|
|
||||||
};
|
|
||||||
|
|
||||||
// 清除localStorage建築物
|
|
||||||
const deleteBuilding = () => {
|
|
||||||
localStorage.removeItem("CviBuildingList");
|
|
||||||
localStorage.removeItem("CviBuilding");
|
|
||||||
buildings.value = [];
|
|
||||||
selectedBuilding.value = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 當 selectedBuilding 改變時,更新 floorList 和 deptList
|
|
||||||
watch(selectedBuilding, async (newBuilding) => {
|
|
||||||
if (newBuilding) {
|
|
||||||
localStorage.setItem("CviBuilding", JSON.stringify(newBuilding));
|
|
||||||
await Promise.all([
|
|
||||||
fetchFloorList(newBuilding.building_guid),
|
|
||||||
fetchDepartmentList(),
|
|
||||||
fetchDashboard2D3D(newBuilding.building_guid),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 初始化資料
|
|
||||||
const initialize = async () => {
|
|
||||||
await fetchBuildings();
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
buildings,
|
buildings,
|
||||||
selectedBuilding,
|
selectedBuilding,
|
||||||
floorList,
|
|
||||||
deptList,
|
|
||||||
mainSubSys,
|
mainSubSys,
|
||||||
mainSys,
|
mainSys,
|
||||||
subSys,
|
subSys,
|
||||||
selectedSystem,
|
selectedSystem,
|
||||||
showForgeArea,
|
|
||||||
previewImageExt,
|
|
||||||
deleteBuilding,
|
|
||||||
fetchBuildings,
|
|
||||||
fetchFloorList,
|
|
||||||
fetchDepartmentList,
|
|
||||||
fetchDashboard2D3D,
|
|
||||||
initialize,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export default useBuildingStore;
|
export default useBuildingStore;
|
||||||
|
|||||||
@ -5,7 +5,6 @@ const useUserInfoStore = defineStore("userInfo", () => {
|
|||||||
const user = ref({
|
const user = ref({
|
||||||
token: "",
|
token: "",
|
||||||
expires: 0,
|
expires: 0,
|
||||||
user_name:"",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const auth_page = ref([]);
|
const auth_page = ref([]);
|
||||||
|
|||||||
@ -2,80 +2,70 @@ import useGetCookie from "@/hooks/useGetCookie";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
const BASEURL = import.meta.env.VITE_API_BASEURL;
|
const BASEURL = import.meta.env.VITE_API_BASEURL;
|
||||||
|
|
||||||
// --- 請求攔截器的共用邏輯 ---
|
|
||||||
const requestInterceptor = (config) => {
|
|
||||||
// 確保 headers 物件存在
|
|
||||||
if (!config.headers) {
|
|
||||||
config.headers = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. 取得並附加最新的 Token
|
|
||||||
const token = useGetCookie("JWT-Authorization");
|
|
||||||
if (token) {
|
|
||||||
// 正確做法:修改屬性,而不是覆蓋整個物件
|
|
||||||
config.headers.Authorization = `Bearer ${token}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 取得並附加選定的建築物 GUID
|
|
||||||
const storedBuilding = localStorage.getItem("CviBuilding");
|
|
||||||
if (storedBuilding) {
|
|
||||||
try {
|
|
||||||
const buildingObject = JSON.parse(storedBuilding);
|
|
||||||
if (buildingObject && buildingObject.building_guid) {
|
|
||||||
// 與後端約定好要用哪個標頭,這裡使用 'X-Building-GUID' 作為範例
|
|
||||||
config.headers["X-Building-GUID"] = buildingObject.building_guid;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error("解析 localStorage 中的 CviBuilding 失敗:", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
|
|
||||||
const requestErrorInterceptor = (error) => {
|
|
||||||
return Promise.reject(error);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// --- 一般 API 實例 ---
|
|
||||||
const instance = axios.create({
|
const instance = axios.create({
|
||||||
baseURL: BASEURL,
|
baseURL: BASEURL,
|
||||||
timeout: 10000, // 建議設定超時
|
timeout: -1,
|
||||||
// 移除靜態 headers
|
headers: { Authorization: `Bearer ${useGetCookie("JWT-Authorization")}` },
|
||||||
});
|
});
|
||||||
|
|
||||||
// 使用共用的攔截器
|
// Add a request interceptor
|
||||||
instance.interceptors.request.use(requestInterceptor, requestErrorInterceptor);
|
instance.interceptors.request.use(
|
||||||
|
function (config) {
|
||||||
|
// Do something before request is sent
|
||||||
|
const token = useGetCookie("JWT-Authorization");
|
||||||
|
config.headers = {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
};
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
// Do something with request error
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Add a response interceptor
|
// Add a response interceptor
|
||||||
instance.interceptors.response.use(
|
instance.interceptors.response.use(
|
||||||
function (response) {
|
function (response) {
|
||||||
// Any status code that lie within the range of 2xx cause this function to trigger
|
// Any status code that lie within the range of 2xx cause this function to trigger
|
||||||
// Do something with response data
|
// Do something with response data
|
||||||
const { data } = response;
|
const { status, data, headers } = response;
|
||||||
|
|
||||||
return { ...data };
|
return {
|
||||||
|
...data,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
function (error) {
|
function (error) {
|
||||||
// Any status codes that falls outside the range of 2xx cause this function to trigger
|
// Any status codes that falls outside the range of 2xx cause this function to trigger
|
||||||
// Do something with response error
|
// Do something with response error
|
||||||
if (error.response && error.response.status === 401) {
|
if (response && response.status === 401) {
|
||||||
window.location.href = "/";
|
window.location.href = "/logout";
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// --- 檔案處理 API 實例 ---
|
|
||||||
export const fileInstance = axios.create({
|
export const fileInstance = axios.create({
|
||||||
baseURL: BASEURL,
|
baseURL: BASEURL,
|
||||||
timeout: -1,
|
timeout: -1,
|
||||||
// 移除靜態 headers
|
headers: { Authorization: `Bearer ${useGetCookie("JWT-Authorization")}` },
|
||||||
});
|
});
|
||||||
|
|
||||||
// 使用共用的攔截器
|
// Add a request interceptor
|
||||||
fileInstance.interceptors.request.use(requestInterceptor, requestErrorInterceptor);
|
fileInstance.interceptors.request.use(
|
||||||
|
function (config) {
|
||||||
|
// Do something before request is sent
|
||||||
|
const token = useGetCookie("JWT-Authorization");
|
||||||
|
config.headers = {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
};
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
// Do something with request error
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Add a response interceptor
|
// Add a response interceptor
|
||||||
fileInstance.interceptors.response.use(
|
fileInstance.interceptors.response.use(
|
||||||
|
|||||||
@ -1,64 +1,12 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, provide, onMounted, watch, computed } from "vue";
|
|
||||||
import AssetMainList from "./components/AssetMainList.vue";
|
|
||||||
import AssetSubList from "./components/AssetSubList.vue";
|
import AssetSubList from "./components/AssetSubList.vue";
|
||||||
import AssetTable from "./components/AssetTable.vue";
|
import AssetTable from "./components/AssetTable.vue";
|
||||||
import { getOperationCompanyList } from "@/apis/operation";
|
|
||||||
import { getIOTSchema, getElecTypeList } from "@/apis/asset";
|
|
||||||
import useSearchParam from "@/hooks/useSearchParam";
|
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
|
||||||
const storeBuild = useBuildingStore();
|
|
||||||
const { searchParams, changeParams } = useSearchParam();
|
|
||||||
const companyOptions = ref([]);
|
|
||||||
const iotSchemaOptions = ref([]);
|
|
||||||
const elecTypeOptions = ref([]);
|
|
||||||
const getCompany = async () => {
|
|
||||||
const res = await getOperationCompanyList();
|
|
||||||
companyOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
|
|
||||||
};
|
|
||||||
const getIOTSchemaOptions = async (id) => {
|
|
||||||
const res = await getIOTSchema(Number(id));
|
|
||||||
iotSchemaOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
|
|
||||||
};
|
|
||||||
const getElecType = async () => {
|
|
||||||
const res = await getElecTypeList();
|
|
||||||
elecTypeOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const departmentList = computed(() => storeBuild.deptList);
|
|
||||||
const floors = computed(() => storeBuild.floorList);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getCompany();
|
|
||||||
getElecType();
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => searchParams.value.subSys_id,
|
|
||||||
(newValue) => {
|
|
||||||
if (newValue) {
|
|
||||||
getIOTSchemaOptions(newValue);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
provide("asset_modal_options", {
|
|
||||||
companyOptions,
|
|
||||||
iotSchemaOptions,
|
|
||||||
elecTypeOptions,
|
|
||||||
departmentList,
|
|
||||||
floors,
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h1 class="text-2xl font-extrabold mb-2">
|
<h1 class="text-2xl font-extrabold mb-2">
|
||||||
{{ $t("assetManagement.title") }}
|
{{ $t("assetManagement.title") }}
|
||||||
</h1>
|
</h1>
|
||||||
<AssetMainList />
|
|
||||||
<AssetSubList />
|
<AssetSubList />
|
||||||
<AssetTable />
|
<AssetTable />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,134 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { getAssetMainList, deleteAssetMainItem } from "@/apis/asset";
|
|
||||||
import AssetMainListAddModal from "./AssetMainListAddModal.vue";
|
|
||||||
import { ref, onMounted, watch, inject } from "vue";
|
|
||||||
import useSearchParam from "@/hooks/useSearchParam";
|
|
||||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
|
||||||
const store = useBuildingStore();
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { searchParams, changeParams } = useSearchParam();
|
|
||||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
|
||||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
|
||||||
const isEditMode = ref(false);
|
|
||||||
const formState = ref({
|
|
||||||
id: 0,
|
|
||||||
system_key: "",
|
|
||||||
system_value: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
const getMainSystems = async () => {
|
|
||||||
const res = await getAssetMainList(store.selectedBuilding.building_guid);
|
|
||||||
const cate = res.data.map((d, index) => ({
|
|
||||||
...d,
|
|
||||||
title: d.system_key,
|
|
||||||
key: d.id,
|
|
||||||
active: searchParams.value?.mainSys_id
|
|
||||||
? parseInt(searchParams.value?.mainSys_id) === d.id
|
|
||||||
: index === 0,
|
|
||||||
}));
|
|
||||||
setItems(cate);
|
|
||||||
};
|
|
||||||
|
|
||||||
const openModal = () => {
|
|
||||||
asset_add_main_item.showModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCancel = () => {
|
|
||||||
formState.value = {
|
|
||||||
id: 0,
|
|
||||||
system_key: "",
|
|
||||||
system_value: "",
|
|
||||||
};
|
|
||||||
asset_add_main_item.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
const edit = (item) => {
|
|
||||||
formState.value = { ...item };
|
|
||||||
openModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteItem = async (id) => {
|
|
||||||
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
|
|
||||||
await cancelToastOpen();
|
|
||||||
const res = await deleteAssetMainItem(id);
|
|
||||||
if (res.isSuccess) {
|
|
||||||
openToast("success", t("msg.delete_success"));
|
|
||||||
getMainSystems();
|
|
||||||
} else {
|
|
||||||
openToast("error", res.msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => store.selectedBuilding,
|
|
||||||
(newBuilding) => {
|
|
||||||
if (newBuilding) {
|
|
||||||
getMainSystems();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(selectedBtn, (newValue) => {
|
|
||||||
changeParams({
|
|
||||||
mainSys_id: newValue.key,
|
|
||||||
subSys_id: null,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="flex items-center gap-4 mb-4">
|
|
||||||
<h2 class="text-lg font-bold ps-2 whitespace-nowrap">
|
|
||||||
{{ $t("history.system_category") }} :
|
|
||||||
</h2>
|
|
||||||
<AssetMainListAddModal
|
|
||||||
:openModal="openModal"
|
|
||||||
:onCancel="onCancel"
|
|
||||||
:getData="getMainSystems"
|
|
||||||
:formState="formState"
|
|
||||||
/>
|
|
||||||
<!-- <button
|
|
||||||
@click.stop.prevent="isEditMode = !isEditMode"
|
|
||||||
class="btn btn-sm btn-outline-success"
|
|
||||||
>
|
|
||||||
{{ isEditMode ? t("button.stop_edit") : t("button.start_edit") }}
|
|
||||||
</button> -->
|
|
||||||
</div>
|
|
||||||
<ButtonConnectedGroup
|
|
||||||
:items="items"
|
|
||||||
:onclick="
|
|
||||||
(e, item) => {
|
|
||||||
if (!isEditMode) {
|
|
||||||
changeActiveBtn(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"
|
|
||||||
:className="`flex w-full mt-2`"
|
|
||||||
size="sm"
|
|
||||||
color="info"
|
|
||||||
>
|
|
||||||
<template #buttonContent="{ item }">
|
|
||||||
<span class="text-base">{{ item.title }}</span>
|
|
||||||
<template v-if="isEditMode">
|
|
||||||
<span
|
|
||||||
class="ml-2 text-base text-warning"
|
|
||||||
@click.stop.prevent="() => edit(item)"
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon :icon="['fas', 'pencil-alt']"></FontAwesomeIcon>
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="text-base mx-1 text-error"
|
|
||||||
@click.stop.prevent="() => deleteItem(item.id)"
|
|
||||||
>
|
|
||||||
<FontAwesomeIcon :icon="['fas', 'trash-alt']" />
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</ButtonConnectedGroup>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
||||||
@ -1,106 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { ref, defineProps, inject } from "vue";
|
|
||||||
import * as yup from "yup";
|
|
||||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
|
||||||
import { postAssetMainList } from "@/apis/asset";
|
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
|
||||||
import useSearchParam from "@/hooks/useSearchParam";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
|
|
||||||
const storeBuild = useBuildingStore();
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { openToast } = inject("app_toast");
|
|
||||||
const { searchParams, changeParams } = useSearchParam();
|
|
||||||
const props = defineProps({
|
|
||||||
openModal: Function,
|
|
||||||
onCancel: Function,
|
|
||||||
getData: Function,
|
|
||||||
formState: Object,
|
|
||||||
isEdit: Boolean,
|
|
||||||
});
|
|
||||||
|
|
||||||
let subSysSchema = yup.object({
|
|
||||||
system_key: yup.string().required(t("button.required")), // 名稱
|
|
||||||
system_value: yup.string().required(t("button.required")), // 代稱
|
|
||||||
});
|
|
||||||
|
|
||||||
const { formErrorMsg, handleSubmit, handleErrorReset } =
|
|
||||||
useFormErrorMessage(subSysSchema);
|
|
||||||
|
|
||||||
const onOk = async () => {
|
|
||||||
const value = await handleSubmit(subSysSchema, props.formState);
|
|
||||||
|
|
||||||
const res = await postAssetMainList({
|
|
||||||
...props.formState,
|
|
||||||
id: props.formState ? props.formState.id : 0,
|
|
||||||
building_guid:storeBuild.selectedBuilding?.building_guid || null,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.isSuccess) {
|
|
||||||
props.getData();
|
|
||||||
onReset();
|
|
||||||
} else {
|
|
||||||
openToast("error", res.msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onReset = () => {
|
|
||||||
handleErrorReset(); // 重置錯誤訊息
|
|
||||||
props.onCancel(); // 調用父層的 onCancel
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<!-- <button class="btn btn-sm btn-success" @click.stop.prevent="openModal">
|
|
||||||
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
|
||||||
</button> -->
|
|
||||||
<Modal
|
|
||||||
id="asset_add_main_item"
|
|
||||||
:title="
|
|
||||||
props.formState?.id
|
|
||||||
? t('assetManagement.edit_system_category')
|
|
||||||
: t('assetManagement.add_system_category')
|
|
||||||
"
|
|
||||||
:onCancel="onReset"
|
|
||||||
:width="300"
|
|
||||||
>
|
|
||||||
<template #modalContent>
|
|
||||||
<form ref="form" class="mt-5 flex flex-col items-center">
|
|
||||||
<Input name="system_key" :value="formState">
|
|
||||||
<template #topLeft>{{ $t("assetManagement.system_name") }}</template>
|
|
||||||
<template #bottomLeft
|
|
||||||
><span class="text-error text-base">
|
|
||||||
{{ formErrorMsg.system_key }}
|
|
||||||
</span></template
|
|
||||||
>
|
|
||||||
</Input>
|
|
||||||
<Input name="system_value" :value="formState" :readonly="Boolean(props.formState?.id)">
|
|
||||||
<template #topLeft>{{ $t("assetManagement.system_value") }}</template>
|
|
||||||
<template #bottomLeft
|
|
||||||
><span class="text-error text-base">
|
|
||||||
{{ formErrorMsg.system_value }}
|
|
||||||
</span></template
|
|
||||||
>
|
|
||||||
</Input>
|
|
||||||
</form>
|
|
||||||
</template>
|
|
||||||
<template #modalAction>
|
|
||||||
<button
|
|
||||||
type="reset"
|
|
||||||
class="btn btn-outline-success mr-2"
|
|
||||||
@click.prevent="onReset"
|
|
||||||
>
|
|
||||||
{{ $t("button.cancel") }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="btn btn-outline-success"
|
|
||||||
@click.prevent="onOk"
|
|
||||||
>
|
|
||||||
{{ $t("button.submit") }}
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
||||||
@ -1,17 +1,15 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { getAssetSubList, deleteAssetSubItem } from "@/apis/asset";
|
import { getAssetSubList, deleteAssetSubItem } from "@/apis/asset";
|
||||||
import { ref, onMounted, watch, inject } from "vue";
|
import { ref, onMounted, watch } from "vue";
|
||||||
import useSearchParam from "@/hooks/useSearchParam";
|
import useSearchParam from "@/hooks/useSearchParam";
|
||||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||||
import AssetSubListAddModal from "./AssetSubListAddModal.vue";
|
import AssetSubListAddModal from "./AssetSubListAddModal.vue";
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { searchParams, changeParams } = useSearchParam();
|
const { searchParams, changeParams } = useSearchParam();
|
||||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
||||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
|
||||||
const isEditMode = ref(false);
|
const getSubSystems = async () => {
|
||||||
const getSubSystems = async (id) => {
|
const res = await getAssetSubList();
|
||||||
const res = await getAssetSubList(id);
|
|
||||||
const sub = res.data.map((d, index) => ({
|
const sub = res.data.map((d, index) => ({
|
||||||
...d,
|
...d,
|
||||||
title: d.system_key,
|
title: d.system_key,
|
||||||
@ -23,115 +21,60 @@ const getSubSystems = async (id) => {
|
|||||||
setItems(sub);
|
setItems(sub);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {});
|
onMounted(() => {
|
||||||
|
getSubSystems();
|
||||||
|
});
|
||||||
|
|
||||||
watch(selectedBtn, (newValue) => {
|
watch(selectedBtn, (newValue) => {
|
||||||
if (newValue && newValue.key) {
|
changeParams({ subSys_id: newValue.key });
|
||||||
changeParams({
|
|
||||||
...searchParams.value,
|
|
||||||
subSys_id: newValue.key,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
const editRecord = ref(null);
|
||||||
() => searchParams,
|
// 編輯 modal
|
||||||
(newValue) => {
|
const openModal = () => {
|
||||||
if (newValue.value.mainSys_id && !newValue.value.subSys_id) {
|
|
||||||
getSubSystems(parseInt(newValue.value.mainSys_id));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const formState = ref({
|
|
||||||
id: 0,
|
|
||||||
system_key: "",
|
|
||||||
system_value: "",
|
|
||||||
system_parent_id: 0,
|
|
||||||
file: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const openModal = (item) => {
|
|
||||||
if (item.id) {
|
|
||||||
formState.value = { ...item };
|
|
||||||
|
|
||||||
if (item.device_image) {
|
|
||||||
const subFile = item
|
|
||||||
? {
|
|
||||||
name: item.device_image,
|
|
||||||
src: item.device_image,
|
|
||||||
ext: item.device_image?.split(".")[1],
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
formState.value.file = [subFile];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
formState.value = {
|
|
||||||
id: 0,
|
|
||||||
system_key: "",
|
|
||||||
system_value: "",
|
|
||||||
system_parent_id: 0,
|
|
||||||
file: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
asset_add_sub_item.showModal();
|
asset_add_sub_item.showModal();
|
||||||
};
|
};
|
||||||
|
const onCancel = () => {
|
||||||
|
editRecord.value = null;
|
||||||
|
asset_add_sub_item.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
const edit = (item) => {
|
||||||
|
editRecord.value = item;
|
||||||
|
openModal();
|
||||||
|
};
|
||||||
|
|
||||||
const deleteItem = async (id) => {
|
const deleteItem = async (id) => {
|
||||||
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
|
deleteAssetSubItem(id);
|
||||||
await cancelToastOpen();
|
getSubSystems();
|
||||||
const res = await deleteAssetSubItem(id);
|
|
||||||
if (res.isSuccess) {
|
|
||||||
openToast("success", t("msg.delete_success"));
|
|
||||||
getSubSystems(parseInt(searchParams.value.mainSys_id));
|
|
||||||
} else {
|
|
||||||
openToast("error", res.msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<div class="flex items-center gap-4 mb-4">
|
<!-- <AssetSubListAddModal
|
||||||
<h2 class="text-lg font-bold ps-2 whitespace-nowrap">
|
|
||||||
{{ $t("history.device_category") }} :
|
|
||||||
</h2>
|
|
||||||
<AssetSubListAddModal
|
|
||||||
:openModal="openModal"
|
:openModal="openModal"
|
||||||
:getData="getSubSystems"
|
:onCancel="onCancel"
|
||||||
:formState="formState"
|
:getData="getSubList"
|
||||||
/>
|
:editRecord="editRecord"
|
||||||
<button
|
/> -->
|
||||||
@click.stop.prevent="isEditMode = !isEditMode"
|
|
||||||
class="btn btn-sm btn-outline-success mr-3"
|
|
||||||
>
|
|
||||||
{{ isEditMode ? t("button.stop_edit") : t("button.start_edit") }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<ButtonConnectedGroup
|
<ButtonConnectedGroup
|
||||||
:items="items"
|
:items="items"
|
||||||
:onclick="
|
:onclick="
|
||||||
(e, item) => {
|
(e, item) => {
|
||||||
if (!isEditMode) {
|
|
||||||
changeActiveBtn(item);
|
changeActiveBtn(item);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"
|
"
|
||||||
:className="`flex w-full mt-2`"
|
:className="`flex w-full mt-4`"
|
||||||
size="sm"
|
size="sm"
|
||||||
color="info"
|
color="info"
|
||||||
>
|
>
|
||||||
<template #buttonContent="{ item }">
|
<template #buttonContent="{ item }">
|
||||||
<span class="text-base">{{ item.title }}</span>
|
<span class="text-base">{{ item.title }}</span>
|
||||||
<template v-if="isEditMode">
|
<!-- <template v-if="!item.is_IOT">
|
||||||
<span
|
<span
|
||||||
class="ml-2 text-base text-warning"
|
class="ml-2 text-base text-warning"
|
||||||
@click.stop.prevent="() => openModal(item)"
|
@click.stop.prevent="() => edit(item)"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon :icon="['fas', 'pencil-alt']"></FontAwesomeIcon>
|
<FontAwesomeIcon :icon="['fas', 'pencil-alt']"></FontAwesomeIcon>
|
||||||
</span>
|
</span>
|
||||||
@ -141,7 +84,7 @@ const deleteItem = async (id) => {
|
|||||||
>
|
>
|
||||||
<FontAwesomeIcon :icon="['fas', 'trash-alt']" />
|
<FontAwesomeIcon :icon="['fas', 'trash-alt']" />
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template> -->
|
||||||
</template>
|
</template>
|
||||||
</ButtonConnectedGroup>
|
</ButtonConnectedGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,28 +2,19 @@
|
|||||||
import { ref, defineProps, onMounted, watch } from "vue";
|
import { ref, defineProps, onMounted, watch } from "vue";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||||
import useSearchParam from "@/hooks/useSearchParam";
|
|
||||||
import { getAssetMainList, postAssetSubList } from "@/apis/asset";
|
import { getAssetMainList, postAssetSubList } from "@/apis/asset";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
|
||||||
const store = useBuildingStore();
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { searchParams, changeParams } = useSearchParam();
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
openModal: Function,
|
openModal: Function,
|
||||||
|
onCancel: Function,
|
||||||
getData: Function,
|
getData: Function,
|
||||||
formState: Object,
|
editRecord: Object,
|
||||||
});
|
});
|
||||||
const form = ref(null);
|
|
||||||
const mainSystem = ref([]);
|
const mainSystem = ref([]);
|
||||||
|
|
||||||
const updateFileList = (files) => {
|
|
||||||
console.log("file", files);
|
|
||||||
props.formState.file = files;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMainSystems = async () => {
|
const getMainSystems = async () => {
|
||||||
const res = await getAssetMainList(store.selectedBuilding.building_guid);
|
const res = await getAssetMainList();
|
||||||
mainSystem.value = res.data.map((d) => ({ ...d, key: d.id }));
|
mainSystem.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,72 +22,66 @@ let subSysSchema = yup.object({
|
|||||||
system_key: yup.string().required(t("button.required")), // 名稱
|
system_key: yup.string().required(t("button.required")), // 名稱
|
||||||
system_value: yup.string().required(t("button.required")), // 代稱
|
system_value: yup.string().required(t("button.required")), // 代稱
|
||||||
system_parent_id: yup.number().required(t("button.required")), // 大類id
|
system_parent_id: yup.number().required(t("button.required")), // 大類id
|
||||||
file: yup.array(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { formErrorMsg, handleSubmit, handleErrorReset } =
|
const { formErrorMsg, handleSubmit, handleErrorReset } =
|
||||||
useFormErrorMessage(subSysSchema);
|
useFormErrorMessage(subSysSchema);
|
||||||
|
|
||||||
const onCancel = () => {
|
const formState = ref({
|
||||||
props.formState = {
|
system_key: "",
|
||||||
id: 0,
|
system_value: "",
|
||||||
|
system_parent_id: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
formState.value = {
|
||||||
system_key: "",
|
system_key: "",
|
||||||
system_value: "",
|
system_value: "",
|
||||||
system_parent_id: 0,
|
system_parent_id: 0,
|
||||||
file: [],
|
|
||||||
};
|
};
|
||||||
asset_add_sub_item.close();
|
|
||||||
updateFileList([]);
|
|
||||||
handleErrorReset();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
onMounted(() => {
|
||||||
() => store.selectedBuilding,
|
|
||||||
(newBuilding) => {
|
|
||||||
if (newBuilding) {
|
|
||||||
getMainSystems();
|
getMainSystems();
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.editRecord,
|
||||||
|
(newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
formState.value = newValue;
|
||||||
|
} else {
|
||||||
|
resetForm();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const onOk = async () => {
|
const onOk = async () => {
|
||||||
// 編輯
|
// 編輯
|
||||||
const value = await handleSubmit(subSysSchema, props.formState);
|
const value = await handleSubmit(subSysSchema, formState.value);
|
||||||
console.log("props.formState", props.formState);
|
|
||||||
|
|
||||||
const formData = new FormData(form.value);
|
const res = await postAssetSubList({
|
||||||
formData.delete("file");
|
...formState.value,
|
||||||
formData.append("id", props.formState.id);
|
id: props.editRecord ? props.editRecord.id : 0,
|
||||||
|
});
|
||||||
|
|
||||||
if (props.formState.file[0]) {
|
|
||||||
formData.append("file", props.formState.file[0]);
|
|
||||||
}
|
|
||||||
if (props.formState.Device_image) {
|
|
||||||
formData.append("Device_image", props.formState.Device_image);
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await postAssetSubList(formData);
|
|
||||||
if (res.isSuccess) {
|
if (res.isSuccess) {
|
||||||
props.getData(parseInt(searchParams.value.mainSys_id));
|
props.getData();
|
||||||
onCancel();
|
props.onCancel();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button class="btn btn-sm btn-success" @click.stop.prevent="openModal">
|
<button class="btn btn-sm btn-success mr-3" @click.stop.prevent="openModal">
|
||||||
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
||||||
</button>
|
</button>
|
||||||
<Modal
|
<Modal
|
||||||
id="asset_add_sub_item"
|
id="asset_add_sub_item"
|
||||||
:title="
|
:title="t('assetManagement.add_category')"
|
||||||
props.formState?.id
|
:open="open"
|
||||||
? t('assetManagement.edit_device_category')
|
|
||||||
: t('assetManagement.add_device_category')
|
|
||||||
"
|
|
||||||
:onCancel="onCancel"
|
:onCancel="onCancel"
|
||||||
:width="300"
|
width="300"
|
||||||
>
|
>
|
||||||
<template #modalContent>
|
<template #modalContent>
|
||||||
<form ref="form" class="mt-5 flex flex-col items-center">
|
<form ref="form" class="mt-5 flex flex-col items-center">
|
||||||
@ -108,11 +93,7 @@ const onOk = async () => {
|
|||||||
</span></template
|
</span></template
|
||||||
>
|
>
|
||||||
</Input>
|
</Input>
|
||||||
<Input
|
<Input name="system_value" :value="formState">
|
||||||
name="system_value"
|
|
||||||
:value="formState"
|
|
||||||
:readonly="Boolean(props.formState?.id)"
|
|
||||||
>
|
|
||||||
<template #topLeft>{{ $t("assetManagement.system_value") }}</template>
|
<template #topLeft>{{ $t("assetManagement.system_value") }}</template>
|
||||||
<template #bottomLeft
|
<template #bottomLeft
|
||||||
><span class="text-error text-base">
|
><span class="text-error text-base">
|
||||||
@ -123,11 +104,10 @@ const onOk = async () => {
|
|||||||
<Select
|
<Select
|
||||||
:options="mainSystem"
|
:options="mainSystem"
|
||||||
Attribute="system_key"
|
Attribute="system_key"
|
||||||
class=""
|
class="mr-5"
|
||||||
name="system_parent_id"
|
name="system_parent_id"
|
||||||
:value="formState"
|
:value="formState"
|
||||||
selectClass="border-info focus-within:border-info"
|
selectClass="border-info focus-within:border-info"
|
||||||
:disabled="Boolean(props.formState?.id)"
|
|
||||||
>
|
>
|
||||||
<template #topLeft>{{
|
<template #topLeft>{{
|
||||||
$t("assetManagement.system_parent")
|
$t("assetManagement.system_parent")
|
||||||
@ -138,14 +118,6 @@ const onOk = async () => {
|
|||||||
</span></template
|
</span></template
|
||||||
>
|
>
|
||||||
</Select>
|
</Select>
|
||||||
<Upload
|
|
||||||
name="file"
|
|
||||||
:fileList="formState.file || []"
|
|
||||||
:getFileList="updateFileList"
|
|
||||||
:multiple="false"
|
|
||||||
>
|
|
||||||
<template #topLeft>{{ $t("operation.upload_file") }}</template>
|
|
||||||
</Upload>
|
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
<template #modalAction>
|
<template #modalAction>
|
||||||
|
|||||||
@ -4,18 +4,28 @@ import { onMounted, ref, watch, inject, provide, computed } from "vue";
|
|||||||
import useSearchParam from "@/hooks/useSearchParam";
|
import useSearchParam from "@/hooks/useSearchParam";
|
||||||
import AssetTableAddModal from "./AssetTableAddModal.vue";
|
import AssetTableAddModal from "./AssetTableAddModal.vue";
|
||||||
import { getOperationCompanyList } from "@/apis/operation";
|
import { getOperationCompanyList } from "@/apis/operation";
|
||||||
import { postMQTTRefresh } from "@/apis/alert";
|
import { getAssetFloorList } from "@/apis/asset";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
const { openToast, cancelToastOpen } = inject("app_toast");
|
||||||
const { companyOptions, departmentList, floors } = inject(
|
|
||||||
"asset_modal_options"
|
|
||||||
);
|
|
||||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||||
const { searchParams, changeParams } = useSearchParam();
|
const { searchParams, changeParams } = useSearchParam();
|
||||||
|
|
||||||
|
const companyOptions = ref([]);
|
||||||
|
const getCompany = async () => {
|
||||||
|
const res = await getOperationCompanyList();
|
||||||
|
companyOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const floors = ref([]);
|
||||||
const totalCoordinates = ref({});
|
const totalCoordinates = ref({});
|
||||||
|
const getFloors = async () => {
|
||||||
|
const res = await getAssetFloorList();
|
||||||
|
floors.value = res.data[0]?.floors.map((d) => ({ ...d, key: d.floor_guid }));
|
||||||
|
};
|
||||||
|
|
||||||
const tableData = ref([]);
|
const tableData = ref([]);
|
||||||
const getAssetData = async () => {
|
const getAssetData = async () => {
|
||||||
totalCoordinates.value = {}; // 在獲取新數據之前清空 totalCoordinates
|
totalCoordinates.value = {}; // 在獲取新數據之前清空 totalCoordinates
|
||||||
@ -29,14 +39,13 @@ const getAssetData = async () => {
|
|||||||
}
|
}
|
||||||
totalCoordinates.value[floorGuid].push(d.device_coordinate);
|
totalCoordinates.value[floorGuid].push(d.device_coordinate);
|
||||||
});
|
});
|
||||||
|
|
||||||
tableData.value = res.data.map((d) => ({
|
tableData.value = res.data.map((d) => ({
|
||||||
...d,
|
...d,
|
||||||
key: d.id,
|
key: d.id,
|
||||||
floor: floors.value.find(({ floor_guid }) => d.floor_guid === floor_guid)
|
floor: floors.value.find(({ floor_guid }) => d.floor_guid === floor_guid)
|
||||||
?.full_name,
|
?.full_name,
|
||||||
company: companyOptions.value.find(({ id }) => d.operation_id === id),
|
company: companyOptions.value.find(({ id }) => d.operation_id === id),
|
||||||
department: departmentList.value.find(({ id }) => d.department_id === id)
|
|
||||||
?.name,
|
|
||||||
buying_date: d?.buying_date
|
buying_date: d?.buying_date
|
||||||
? dayjs(d?.buying_date).format("YYYY-MM-DD")
|
? dayjs(d?.buying_date).format("YYYY-MM-DD")
|
||||||
: "",
|
: "",
|
||||||
@ -47,25 +56,18 @@ const getAssetData = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const refreshMQTT = async () => {
|
|
||||||
const res = await postMQTTRefresh();
|
|
||||||
if (res.isSuccess) {
|
|
||||||
openToast("success", t("msg.mqtt_refresh"));
|
|
||||||
} else {
|
|
||||||
openToast("error", res.msg, "#outliers_add_table_item");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
await getCompany();
|
||||||
|
await getFloors();
|
||||||
getAssetData();
|
getAssetData();
|
||||||
});
|
});
|
||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
// {
|
{
|
||||||
// title: t("assetManagement.device_number"),
|
title: t("assetManagement.device_number"),
|
||||||
// key: "device_number",
|
key: "device_number",
|
||||||
// class: "break-all",
|
class: "break-all",
|
||||||
// },
|
},
|
||||||
{
|
{
|
||||||
title: t("assetManagement.device_name"),
|
title: t("assetManagement.device_name"),
|
||||||
key: "full_name",
|
key: "full_name",
|
||||||
@ -82,11 +84,6 @@ const columns = computed(() => [
|
|||||||
filter: true,
|
filter: true,
|
||||||
sort: true,
|
sort: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: t("assetManagement.department"),
|
|
||||||
key: "department",
|
|
||||||
filter: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: t("assetManagement.device_coordinate"),
|
title: t("assetManagement.device_coordinate"),
|
||||||
key: "device_coordinate",
|
key: "device_coordinate",
|
||||||
@ -125,8 +122,6 @@ watch(
|
|||||||
(newValue) => {
|
(newValue) => {
|
||||||
if (newValue.value?.subSys_id) {
|
if (newValue.value?.subSys_id) {
|
||||||
getAssetData();
|
getAssetData();
|
||||||
} else {
|
|
||||||
tableData.value = [];
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -187,8 +182,7 @@ provide("asset_table_data", {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex justify-between items-center mt-10">
|
<div class="flex justify-start items-center mt-10">
|
||||||
<div class="flex">
|
|
||||||
<h3 class="text-xl mr-5">{{ $t("assetManagement.device_list") }}</h3>
|
<h3 class="text-xl mr-5">{{ $t("assetManagement.device_list") }}</h3>
|
||||||
<AssetTableAddModal
|
<AssetTableAddModal
|
||||||
:openModal="openModal"
|
:openModal="openModal"
|
||||||
@ -197,12 +191,6 @@ provide("asset_table_data", {
|
|||||||
:getData="getAssetData"
|
:getData="getAssetData"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-add" @click.prevent="refreshMQTT">
|
|
||||||
<font-awesome-icon :icon="['fas', 'cog']" />{{
|
|
||||||
$t("alert.reorganization")
|
|
||||||
}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<Table :columns="columns" :dataSource="tableData" class="mt-3">
|
<Table :columns="columns" :dataSource="tableData" class="mt-3">
|
||||||
<template #bodyCell="{ record, column, index }">
|
<template #bodyCell="{ record, column, index }">
|
||||||
<template v-if="column.key === 'companyAndContact'">
|
<template v-if="column.key === 'companyAndContact'">
|
||||||
@ -215,7 +203,7 @@ provide("asset_table_data", {
|
|||||||
<template v-else-if="column.key === 'oriFile'">
|
<template v-else-if="column.key === 'oriFile'">
|
||||||
<span v-if="record.oriFile.length === 0"></span>
|
<span v-if="record.oriFile.length === 0"></span>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<span class="flex flex-wrap gap-1">
|
<span class="flex">
|
||||||
<a
|
<a
|
||||||
v-for="file in record.oriFile"
|
v-for="file in record.oriFile"
|
||||||
:href="`${FILE_BASEURL}/${file?.file_url}`"
|
:href="`${FILE_BASEURL}/${file?.file_url}`"
|
||||||
|
|||||||
@ -102,18 +102,19 @@ const closeModal = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button class="btn btn-sm btn-add mr-3" @click.stop.prevent="openModal" :disabled="!searchParams.subSys_id">
|
<button class="btn btn-sm btn-add mr-3" @click.stop.prevent="openModal">
|
||||||
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
||||||
</button>
|
</button>
|
||||||
<Modal
|
<Modal
|
||||||
id="asset_add_table_item"
|
id="asset_add_table_item"
|
||||||
:title="editRecord?.main_id ? $t('assetManagement.edit_device') : $t('assetManagement.add_device')"
|
:title="editRecord?.main_id ? $t('assetManagement.edit_device') : $t('assetManagement.add_device')"
|
||||||
|
:open="open"
|
||||||
:onCancel="closeModal"
|
:onCancel="closeModal"
|
||||||
:width="1600"
|
width="1600"
|
||||||
>
|
>
|
||||||
<template #modalContent>
|
<template #modalContent>
|
||||||
<form ref="form" class="grid grid-cols-5 gap-5">
|
<form ref="form" class="grid grid-cols-5 gap-5">
|
||||||
<div class="grid grid-cols-2 col-span-2 items-end">
|
<div class="grid grid-cols-2 col-span-2">
|
||||||
<AssetTableModalLeft :current_component_key="current_component_key" />
|
<AssetTableModalLeft :current_component_key="current_component_key" />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-span-3">
|
<div class="col-span-3">
|
||||||
|
|||||||
@ -2,19 +2,21 @@
|
|||||||
import { ref, inject, onBeforeMount, onMounted, watch } from "vue";
|
import { ref, inject, onBeforeMount, onMounted, watch } from "vue";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import "yup-phone-lite";
|
import "yup-phone-lite";
|
||||||
|
import AssetTableModalLeftInfoIoT from "./AssetTableModalLeftInfoIoT.vue";
|
||||||
|
import AssetTableModalLeftInfoGraph from "./AssetTableModalLeftInfoGraph.vue";
|
||||||
|
import { getOperationCompanyList } from "@/apis/operation";
|
||||||
import useSearchParam from "@/hooks/useSearchParam";
|
import useSearchParam from "@/hooks/useSearchParam";
|
||||||
import AssetTableModalLeftInfoMQTT from "./AssetTableModalLeftInfoMQTT.vue";
|
import OperationTableModal from "@/views/operation/components/OperationTableModal.vue";
|
||||||
import useUserInfoStore from "@/stores/useUserInfoStore";
|
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
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 { searchParams, changeParams } = useSearchParam();
|
const { searchParams, changeParams } = useSearchParam();
|
||||||
const { updateLeftFields, formErrorMsg, formState } = inject(
|
const { updateLeftFields, formErrorMsg, formState } = inject(
|
||||||
"asset_table_modal_form"
|
"asset_table_modal_form"
|
||||||
);
|
);
|
||||||
const { companyOptions, iotSchemaOptions, elecTypeOptions, departmentList } = inject("asset_modal_options");
|
|
||||||
const store = useUserInfoStore();
|
|
||||||
let schema = {
|
let schema = {
|
||||||
full_name: yup.string().nullable(true),
|
full_name: yup.string().nullable(true),
|
||||||
operate_text: yup.string().nullable(true),
|
operate_text: yup.string().nullable(true),
|
||||||
@ -26,15 +28,6 @@ let schema = {
|
|||||||
.number()
|
.number()
|
||||||
.transform((value) => (Number.isNaN(value) ? null : value))
|
.transform((value) => (Number.isNaN(value) ? null : value))
|
||||||
.nullable(true),
|
.nullable(true),
|
||||||
response_schema_id: yup
|
|
||||||
.number()
|
|
||||||
.transform((value) => (Number.isNaN(value) ? null : value))
|
|
||||||
.nullable(true),
|
|
||||||
department_id: yup
|
|
||||||
.number()
|
|
||||||
.transform((value) => (Number.isNaN(value) ? null : value))
|
|
||||||
.nullable(true),
|
|
||||||
topic: yup.string().nullable(true),
|
|
||||||
asset_number: yup.string().nullable(true),
|
asset_number: yup.string().nullable(true),
|
||||||
sub_device: yup.array().nullable(true),
|
sub_device: yup.array().nullable(true),
|
||||||
oriFile: yup.array().nullable(true),
|
oriFile: yup.array().nullable(true),
|
||||||
@ -48,11 +41,7 @@ onBeforeMount(() => {
|
|||||||
brand: "",
|
brand: "",
|
||||||
device_model: "",
|
device_model: "",
|
||||||
operation_id: 0,
|
operation_id: 0,
|
||||||
response_schema_id: 0,
|
|
||||||
department_id: 0,
|
|
||||||
elec_type_id: null,
|
|
||||||
asset_number: "",
|
asset_number: "",
|
||||||
topic: "",
|
|
||||||
sub_device: [],
|
sub_device: [],
|
||||||
oriFile: [],
|
oriFile: [],
|
||||||
buying_date: "",
|
buying_date: "",
|
||||||
@ -91,20 +80,28 @@ watch(formState, (newValue) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
const updateFileList = (files) => {
|
||||||
() => iotSchemaOptions.value,
|
formState.value = { ...formState.value, oriFile: files };
|
||||||
(newVal) => {
|
};
|
||||||
if (newVal && newVal.length > 0) {
|
|
||||||
formState.value.response_schema_id = newVal[0].id;
|
const companyOptions = ref([]);
|
||||||
}
|
const getCompany = async () => {
|
||||||
},
|
const res = await getOperationCompanyList();
|
||||||
{ immediate: true }
|
companyOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||||
);
|
};
|
||||||
|
onMounted(() => {
|
||||||
|
getCompany();
|
||||||
|
});
|
||||||
|
|
||||||
|
const openCompanyAddModal = () => {
|
||||||
|
changeParams({ ...searchParams.value, work_type: 3 }); // 開啟company Add modal
|
||||||
|
operation_action_item.showModal();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- information -->
|
<!-- information -->
|
||||||
<Input :value="formState" width="290" name="full_name">
|
<Input :value="formState" width="270" name="full_name">
|
||||||
<template #topLeft>{{ $t("assetManagement.device_name") }}</template>
|
<template #topLeft>{{ $t("assetManagement.device_name") }}</template>
|
||||||
<template #bottomLeft
|
<template #bottomLeft
|
||||||
><span class="text-error text-base">
|
><span class="text-error text-base">
|
||||||
@ -112,23 +109,15 @@ watch(
|
|||||||
</span></template
|
</span></template
|
||||||
></Input
|
></Input
|
||||||
>
|
>
|
||||||
<div class="flex items-center w-72">
|
<Input :value="formState" width="270" name="operate_text">
|
||||||
<Select
|
<template #topLeft>Mac</template>
|
||||||
:value="formState"
|
<template #bottomLeft
|
||||||
selectClass="border-info focus-within:border-info"
|
><span class="text-error text-base">
|
||||||
name="department_id"
|
{{ formErrorMsg.operate_text }}
|
||||||
Attribute="name"
|
</span></template
|
||||||
:options="departmentList"
|
></Input
|
||||||
>
|
|
||||||
<template #topLeft>{{ $t("assetManagement.department") }}</template>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
<Input
|
|
||||||
:value="formState"
|
|
||||||
width="290"
|
|
||||||
name="device_number"
|
|
||||||
v-if="store.user.user_name == 'webUser'"
|
|
||||||
>
|
>
|
||||||
|
<Input :value="formState" width="270" name="device_number">
|
||||||
<template #topLeft
|
<template #topLeft
|
||||||
>Tag_Name ({{ $t("assetManagement.fill_text") }})</template
|
>Tag_Name ({{ $t("assetManagement.fill_text") }})</template
|
||||||
>
|
>
|
||||||
@ -138,31 +127,28 @@ watch(
|
|||||||
</span></template
|
</span></template
|
||||||
></Input
|
></Input
|
||||||
>
|
>
|
||||||
<div class="flex items-center w-72">
|
<!-- <Input :value="formState" width="270" name="floor_guid">
|
||||||
<Select
|
<template #topLeft>設備位置(樓層 / 區域)</template>
|
||||||
|
<template #bottomLeft
|
||||||
|
><span class="text-error text-base">
|
||||||
|
{{ formErrorMsg.floor_guid }}
|
||||||
|
</span></template
|
||||||
|
></Input
|
||||||
|
> -->
|
||||||
|
<Input
|
||||||
:value="formState"
|
:value="formState"
|
||||||
selectClass="border-info focus-within:border-info"
|
width="270"
|
||||||
name="response_schema_id"
|
name="device_coordinate"
|
||||||
Attribute="name"
|
:disabled="true"
|
||||||
:options="iotSchemaOptions"
|
|
||||||
:required="true"
|
|
||||||
>
|
>
|
||||||
<template #topLeft>IoT</template>
|
<template #topLeft>{{ $t("assetManagement.device_coordinate") }}</template>
|
||||||
</Select>
|
<template #bottomLeft
|
||||||
</div>
|
><span class="text-error text-base">
|
||||||
<div class="flex items-center w-72" v-if="searchParams.mainSys_id==26">
|
{{ formErrorMsg.device_coordinate }}
|
||||||
<Select
|
</span></template
|
||||||
:value="formState"
|
></Input
|
||||||
selectClass="border-info focus-within:border-info"
|
|
||||||
name="elec_type_id"
|
|
||||||
Attribute="name"
|
|
||||||
:options="elecTypeOptions"
|
|
||||||
:required="true"
|
|
||||||
>
|
>
|
||||||
<template #topLeft>{{$t("energy.electricity_classification")}}</template>
|
<Input :value="formState" width="270" name="asset_number">
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
<Input :value="formState" width="290" name="asset_number">
|
|
||||||
<template #topLeft>{{ $t("assetManagement.asset_number") }}</template>
|
<template #topLeft>{{ $t("assetManagement.asset_number") }}</template>
|
||||||
<template #bottomLeft
|
<template #bottomLeft
|
||||||
><span class="text-error text-base">
|
><span class="text-error text-base">
|
||||||
@ -170,7 +156,7 @@ watch(
|
|||||||
</span></template
|
</span></template
|
||||||
></Input
|
></Input
|
||||||
>
|
>
|
||||||
<DateGroup :items="buying_date" width="290" :withLine="false">
|
<DateGroup :items="buying_date" width="270" :withLine="false">
|
||||||
<template #topLeft>{{ $t("assetManagement.buying_date") }}</template>
|
<template #topLeft>{{ $t("assetManagement.buying_date") }}</template>
|
||||||
<template #bottomLeft
|
<template #bottomLeft
|
||||||
><span class="text-error text-base">
|
><span class="text-error text-base">
|
||||||
@ -178,7 +164,7 @@ watch(
|
|||||||
</span></template
|
</span></template
|
||||||
>
|
>
|
||||||
</DateGroup>
|
</DateGroup>
|
||||||
<Input :value="formState" width="290" name="brand">
|
<Input :value="formState" width="270" name="brand">
|
||||||
<template #topLeft>{{ $t("assetManagement.brand") }}</template>
|
<template #topLeft>{{ $t("assetManagement.brand") }}</template>
|
||||||
<template #bottomLeft
|
<template #bottomLeft
|
||||||
><span class="text-error text-base">
|
><span class="text-error text-base">
|
||||||
@ -186,7 +172,7 @@ watch(
|
|||||||
</span></template
|
</span></template
|
||||||
></Input
|
></Input
|
||||||
>
|
>
|
||||||
<Input :value="formState" width="290" name="device_model">
|
<Input :value="formState" width="270" name="device_model">
|
||||||
<template #topLeft>{{ $t("assetManagement.modal") }}</template>
|
<template #topLeft>{{ $t("assetManagement.modal") }}</template>
|
||||||
<template #bottomLeft
|
<template #bottomLeft
|
||||||
><span class="text-error text-base">
|
><span class="text-error text-base">
|
||||||
@ -194,7 +180,7 @@ watch(
|
|||||||
</span></template
|
</span></template
|
||||||
></Input
|
></Input
|
||||||
>
|
>
|
||||||
<div class="flex items-center w-72">
|
<div class="flex items-center col-span-2">
|
||||||
<Select
|
<Select
|
||||||
:value="formState"
|
:value="formState"
|
||||||
selectClass="border-info focus-within:border-info"
|
selectClass="border-info focus-within:border-info"
|
||||||
@ -205,8 +191,28 @@ watch(
|
|||||||
>
|
>
|
||||||
<template #topLeft>{{ $t("assetManagement.company") }}</template>
|
<template #topLeft>{{ $t("assetManagement.company") }}</template>
|
||||||
</Select>
|
</Select>
|
||||||
|
<OperationTableModal type="asset" />
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-add ml-2 mt-7"
|
||||||
|
@click="openCompanyAddModal"
|
||||||
|
>
|
||||||
|
<font-awesome-icon :icon="['fas', 'plus']" />
|
||||||
|
{{ $t("button.add") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<AssetTableModalLeftInfoMQTT />
|
<AssetTableModalLeftInfoGraph />
|
||||||
|
<!-- <Upload
|
||||||
|
name="oriFile"
|
||||||
|
:fileList="formState.oriFile"
|
||||||
|
:getFileList="updateFileList"
|
||||||
|
:multiple="true"
|
||||||
|
class="col-span-2"
|
||||||
|
:baseUrl="FILE_BASEURL"
|
||||||
|
>
|
||||||
|
<template #topLeft>{{ $t("assetManagement.oriFile") }}</template>
|
||||||
|
</Upload> -->
|
||||||
|
<AssetTableModalLeftInfoIoT />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@ -1,161 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { onMounted, ref, inject, onBeforeMount, watch, computed } from "vue";
|
|
||||||
import {
|
|
||||||
getDepartmentList,
|
|
||||||
postDepartmentList,
|
|
||||||
deleteDepartmentItem,
|
|
||||||
} from "@/apis/asset";
|
|
||||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
|
||||||
import * as yup from "yup";
|
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
|
||||||
const { formState } = inject("asset_table_modal_form");
|
|
||||||
|
|
||||||
const selectedOption = ref("add");
|
|
||||||
|
|
||||||
const DeptFormState = ref({ id: 0, name: "" });
|
|
||||||
const departmentList = ref([]);
|
|
||||||
|
|
||||||
const getDepartment = async () => {
|
|
||||||
const res = await getDepartmentList();
|
|
||||||
departmentList.value = res.data.map((d) => ({ ...d, key: d.id }));
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getDepartment();
|
|
||||||
});
|
|
||||||
|
|
||||||
// modal
|
|
||||||
const openModal = () => {
|
|
||||||
if (selectedOption.value === "add") {
|
|
||||||
DeptFormState.value = {
|
|
||||||
id: 0,
|
|
||||||
name: "",
|
|
||||||
};
|
|
||||||
} else if (selectedOption.value === "edit") {
|
|
||||||
const dept = departmentList.value.find(
|
|
||||||
(d) => d.id === formState.value.department_id
|
|
||||||
);
|
|
||||||
if (dept) {
|
|
||||||
DeptFormState.value = {
|
|
||||||
id: dept.id,
|
|
||||||
name: dept.name,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
asset_add_dept.showModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
const form = ref(null);
|
|
||||||
|
|
||||||
const deptScheme = yup.object({
|
|
||||||
name: yup.string().required(t("button.required")),
|
|
||||||
});
|
|
||||||
|
|
||||||
const { formErrorMsg, handleSubmit, handleErrorReset, updateScheme } =
|
|
||||||
useFormErrorMessage(deptScheme);
|
|
||||||
const onOk = async () => {
|
|
||||||
const value = await handleSubmit(deptScheme, DeptFormState.value);
|
|
||||||
|
|
||||||
const res = await postDepartmentList(value);
|
|
||||||
if (res.isSuccess) {
|
|
||||||
getDepartment();
|
|
||||||
onCancel();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDelete = async () => {
|
|
||||||
openToast(
|
|
||||||
"warning",
|
|
||||||
t("msg.sure_to_delete"),
|
|
||||||
"#asset_add_table_item",
|
|
||||||
async () => {
|
|
||||||
await cancelToastOpen();
|
|
||||||
const res = await deleteDepartmentItem(formState.value.department_id);
|
|
||||||
if (res.isSuccess) {
|
|
||||||
getDepartment();
|
|
||||||
openToast("success", t("msg.delete_success"), "#asset_add_table_item");
|
|
||||||
} else {
|
|
||||||
openToast("error", res.msg, "#asset_add_table_item");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCancel = () => {
|
|
||||||
DeptFormState.value = {
|
|
||||||
id: 0,
|
|
||||||
name: "",
|
|
||||||
};
|
|
||||||
asset_add_dept.close();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div className="join w-72 mb-4">
|
|
||||||
<Select
|
|
||||||
:value="formState"
|
|
||||||
selectClass="border-info focus-within:border-info rounded-r-none"
|
|
||||||
name="department_id"
|
|
||||||
Attribute="name"
|
|
||||||
:options="departmentList"
|
|
||||||
:isBottomLabelExist="false"
|
|
||||||
>
|
|
||||||
<template #topLeft>{{ $t("assetManagement.department") }}</template>
|
|
||||||
</Select>
|
|
||||||
<select
|
|
||||||
v-model="selectedOption"
|
|
||||||
className="select border-info focus-within:border-info join-item mt-11"
|
|
||||||
>
|
|
||||||
<option value="add" selected>{{ $t("button.add") }}</option>
|
|
||||||
<option value="edit">{{ $t("button.edit") }}</option>
|
|
||||||
<option value="delete">{{ $t("button.delete") }}</option>
|
|
||||||
</select>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-success join-item mt-11"
|
|
||||||
@click="selectedOption === 'delete' ? onDelete() : openModal()"
|
|
||||||
:aria-label="$t('button.submit')"
|
|
||||||
>
|
|
||||||
{{ $t("button.submit") }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<Modal
|
|
||||||
id="asset_add_dept"
|
|
||||||
:title="t('assetManagement.department')"
|
|
||||||
:onCancel="onCancel"
|
|
||||||
:width="400"
|
|
||||||
>
|
|
||||||
<template #modalContent>
|
|
||||||
<form ref="form">
|
|
||||||
<Input :value="DeptFormState" width="270" name="name">
|
|
||||||
<template #topLeft>{{
|
|
||||||
$t("assetManagement.department_name")
|
|
||||||
}}</template>
|
|
||||||
<template #bottomLeft
|
|
||||||
><span class="text-error text-base">
|
|
||||||
{{ formErrorMsg.name }}
|
|
||||||
</span></template
|
|
||||||
></Input
|
|
||||||
>
|
|
||||||
</form>
|
|
||||||
</template>
|
|
||||||
<template #modalAction>
|
|
||||||
<button
|
|
||||||
type="reset"
|
|
||||||
class="btn btn-outline-success mr-2"
|
|
||||||
@click.prevent="onCancel"
|
|
||||||
>
|
|
||||||
{{ $t("button.cancel") }}
|
|
||||||
</button>
|
|
||||||
<button type="submit" class="btn btn-outline-success" @click="onOk">
|
|
||||||
{{ $t("button.submit") }}
|
|
||||||
</button>
|
|
||||||
</template></Modal
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
||||||
@ -138,7 +138,7 @@ onMounted(async () => {
|
|||||||
id="asset_add_graph_item"
|
id="asset_add_graph_item"
|
||||||
:title="t('graphManagement.title')"
|
:title="t('graphManagement.title')"
|
||||||
:onCancel="onCancel"
|
:onCancel="onCancel"
|
||||||
:width="500"
|
width="500"
|
||||||
>
|
>
|
||||||
<template #modalContent>
|
<template #modalContent>
|
||||||
<ul class="menu bg-base-200 rounded-box text-lg w-full mt-3">
|
<ul class="menu bg-base-200 rounded-box text-lg w-full mt-3">
|
||||||
|
|||||||
@ -44,7 +44,6 @@ const modalColumns = computed(() => [
|
|||||||
{
|
{
|
||||||
title: "tag",
|
title: "tag",
|
||||||
key: "device_name",
|
key: "device_name",
|
||||||
filter: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("assetManagement.point"),
|
title: t("assetManagement.point"),
|
||||||
@ -65,7 +64,7 @@ const getPoint = async (sub_system_tag) => {
|
|||||||
setPoints(
|
setPoints(
|
||||||
res.data.map((d, index) => ({
|
res.data.map((d, index) => ({
|
||||||
...d,
|
...d,
|
||||||
title: d.full_name,
|
title: d.points,
|
||||||
key: d.points,
|
key: d.points,
|
||||||
active: false,
|
active: false,
|
||||||
}))
|
}))
|
||||||
@ -225,7 +224,7 @@ const deleteItem = (value) => {
|
|||||||
id="asset_add_IoT_item"
|
id="asset_add_IoT_item"
|
||||||
:title="t('assetManagement.associated_device')"
|
:title="t('assetManagement.associated_device')"
|
||||||
:onCancel="onCancel"
|
:onCancel="onCancel"
|
||||||
:width="900"
|
width="900"
|
||||||
>
|
>
|
||||||
<template #modalContent>
|
<template #modalContent>
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
|
|||||||
@ -1,185 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { onMounted, ref, inject, watch, computed } from "vue";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { postMQTTpublish } from "@/apis/asset";
|
|
||||||
import mqtt from "mqtt";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
|
||||||
const { formState } = inject("asset_table_modal_form");
|
|
||||||
const BASEURL = import.meta.env.VITE_MQTT_BASEURL;
|
|
||||||
// MQTT相關
|
|
||||||
const mqttClient = ref(null); // MQTT客戶端
|
|
||||||
const receivedMessages = ref([]); // 儲存接收到的訊息
|
|
||||||
const countdown = ref(60); // 倒計時初始為 60 秒
|
|
||||||
let timer = null; // 記錄計時器
|
|
||||||
|
|
||||||
const openModal = () => {
|
|
||||||
if (!mqttClient.value) {
|
|
||||||
connectMqtt();
|
|
||||||
}
|
|
||||||
mqtt_test.showModal();
|
|
||||||
startCountdown(); // 開始倒計時
|
|
||||||
};
|
|
||||||
|
|
||||||
const connectMqtt = () => {
|
|
||||||
const topic = formState.value.topic || ""; // 取得主題
|
|
||||||
const mqttHost = `${BASEURL}`;
|
|
||||||
const protocol = "wss"; // 根據伺服器配置,需要設置為 "ws" 或 "wss"
|
|
||||||
mqttClient.value = mqtt.connect(mqttHost, {
|
|
||||||
protocol,
|
|
||||||
reconnectPeriod: 1000, // 每秒嘗試重新連線
|
|
||||||
username: "admin", // MQTT 帳號
|
|
||||||
password: "mjmadmin@99", // MQTT 密碼
|
|
||||||
port: 443,
|
|
||||||
});
|
|
||||||
|
|
||||||
mqttClient.value.on("connect", () => {
|
|
||||||
console.log("MQTT 已連接");
|
|
||||||
if (topic) {
|
|
||||||
mqttClient.value.subscribe(topic, (err) => {
|
|
||||||
if (!err) {
|
|
||||||
console.log(`已訂閱主題: ${topic}`);
|
|
||||||
} else {
|
|
||||||
console.error("訂閱失敗: ", err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mqttClient.value.on("message", (topic, message) => {
|
|
||||||
// 儲存接收到的訊息
|
|
||||||
const now = dayjs(); // 使用 dayjs() 取得當前時間
|
|
||||||
const timestamp = now.format("YYYY-MM-DD HH:mm:ss");
|
|
||||||
|
|
||||||
receivedMessages.value.push({
|
|
||||||
topic,
|
|
||||||
message: message.toString(),
|
|
||||||
timestamp: timestamp,
|
|
||||||
});
|
|
||||||
clearInterval(timer); // 收到訊息後清除倒計時
|
|
||||||
});
|
|
||||||
|
|
||||||
mqttClient.value.on("error", (err) => {
|
|
||||||
console.error("MQTT 連線錯誤: ", err);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const startCountdown = () => {
|
|
||||||
if (timer) return; // 防止重複啟動計時器
|
|
||||||
timer = setInterval(() => {
|
|
||||||
if (countdown.value > 0) {
|
|
||||||
countdown.value--;
|
|
||||||
} else {
|
|
||||||
onCancel(); // 1分鐘後如果沒有收到訊息則觸發 onCancel
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCancel = () => {
|
|
||||||
receivedMessages.value = [];
|
|
||||||
mqtt_test.close();
|
|
||||||
|
|
||||||
// 斷開 MQTT 連線
|
|
||||||
if (mqttClient.value) {
|
|
||||||
mqttClient.value.end();
|
|
||||||
mqttClient.value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置倒計時
|
|
||||||
if (timer) {
|
|
||||||
clearInterval(timer);
|
|
||||||
timer = null; // 清除計時器引用
|
|
||||||
}
|
|
||||||
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>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="flex col-span-2 pb-5">
|
|
||||||
<Input :value="formState" name="topic">
|
|
||||||
<template #topLeft>MQTT subscribe topic</template>
|
|
||||||
</Input>
|
|
||||||
<button type="button" class="btn btn-add mt-11 ms-1" @click="openModal">
|
|
||||||
<font-awesome-icon :icon="['fas', 'cog']" />
|
|
||||||
Test
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-col col-span-2 border-t-gray-400 border-t py-5">
|
|
||||||
<Input :value="formState" name="topic">
|
|
||||||
<template #topLeft>MQTT publish topic</template>
|
|
||||||
</Input>
|
|
||||||
<Textarea :value="formState" name="publish_message">
|
|
||||||
<template #topLeft>MQTT messages</template>
|
|
||||||
</Textarea>
|
|
||||||
<button type="button" class="btn btn-add mt-6 w-24" @click="onSubmit">
|
|
||||||
<font-awesome-icon :icon="['far', 'paper-plane']" />
|
|
||||||
Send
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Modal id="mqtt_test" title="MQTT Topic" :onCancel="onCancel" :width="400">
|
|
||||||
<template #modalContent>
|
|
||||||
<!-- 顯示接收到的訊息 -->
|
|
||||||
<div v-if="receivedMessages.length > 0" class="overflow-y-auto h-96">
|
|
||||||
<ul>
|
|
||||||
<li
|
|
||||||
v-for="(message, index) in receivedMessages"
|
|
||||||
:key="index"
|
|
||||||
class="bg-base-200 rounded-md text-wrap shadow shadow-slate-400 p-4 my-2 me-2"
|
|
||||||
>
|
|
||||||
<strong class="text-base block text-info mb-2"
|
|
||||||
>{{ message.topic }} :</strong
|
|
||||||
>
|
|
||||||
<p class="text-sm break-words">{{ message.message }}</p>
|
|
||||||
<p class="text-xs text-slate-200 pt-2">
|
|
||||||
<FontAwesomeIcon :icon="['fas', 'clock']" class="me-1" />
|
|
||||||
{{ message.timestamp }}
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<!-- 顯示 loading 和倒計時 -->
|
|
||||||
<p v-else class="text-center mt-20">
|
|
||||||
<Loading />
|
|
||||||
<br />
|
|
||||||
<span class="text-base">{{ countdown }} seconds</span>
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
<template #modalAction>
|
|
||||||
<button
|
|
||||||
type="reset"
|
|
||||||
class="btn btn-outline-success mr-2"
|
|
||||||
@click.prevent="onCancel"
|
|
||||||
>
|
|
||||||
{{ t("button.cancel") }}
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
||||||
@ -1,17 +1,21 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, inject, onBeforeMount, watch, computed } from "vue";
|
import { onMounted, ref, inject, onBeforeMount, watch, computed } from "vue";
|
||||||
import EffectScatter from "@/components/chart/EffectScatter.vue";
|
import EffectScatter from "@/components/chart/EffectScatter.vue";
|
||||||
|
import {
|
||||||
|
getAssetFloorList,
|
||||||
|
postAssetFloor,
|
||||||
|
deleteAssetFloor,
|
||||||
|
} from "@/apis/asset";
|
||||||
|
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
import useBuildingStore from "@/stores/useBuildingStore";
|
||||||
import AssetTableModalLeftInfoIoT from "./AssetTableModalLeftInfoIoT.vue";
|
|
||||||
import AssetTableModalLeftInfoGraph from "./AssetTableModalLeftInfoGraph.vue";
|
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const { openToast, cancelToastOpen } = inject("app_toast");
|
||||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||||
const { totalCoordinates } = inject("asset_table_data");
|
const { totalCoordinates } = inject("asset_table_data");
|
||||||
const { floors } = inject("asset_modal_options");
|
|
||||||
const { updateRightFields, formErrorMsg, formState } = inject(
|
const { updateRightFields, formErrorMsg, formState } = inject(
|
||||||
"asset_table_modal_form"
|
"asset_table_modal_form"
|
||||||
);
|
);
|
||||||
@ -30,7 +34,7 @@ onBeforeMount(() => {
|
|||||||
|
|
||||||
const asset_floor_chart = ref(null);
|
const asset_floor_chart = ref(null);
|
||||||
const currentFloor = ref(null);
|
const currentFloor = ref(null);
|
||||||
const parsedCoordinates = ref(null);
|
const selectedOption = ref("add");
|
||||||
|
|
||||||
const defaultOption = (map, data = []) => {
|
const defaultOption = (map, data = []) => {
|
||||||
// 生成坐標數據,根據坐標值的不同設置不同顏色
|
// 生成坐標數據,根據坐標值的不同設置不同顏色
|
||||||
@ -40,10 +44,7 @@ const defaultOption = (map, data = []) => {
|
|||||||
name: coordString,
|
name: coordString,
|
||||||
value: coordinate,
|
value: coordinate,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color:
|
color: coordString === formState.value.device_coordinate ? "#0000FF" : "#b02a02",
|
||||||
coordString === formState.value.device_coordinate
|
|
||||||
? "#0000FF"
|
|
||||||
: "#b02a02",
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -72,25 +73,26 @@ const defaultOption = (map, data = []) => {
|
|||||||
|
|
||||||
watch(currentFloor, (newValue) => {
|
watch(currentFloor, (newValue) => {
|
||||||
if (newValue?.floor_map_name) {
|
if (newValue?.floor_map_name) {
|
||||||
const coordinates =
|
const coordinates = totalCoordinates.value[newValue.floor_guid] || [];
|
||||||
totalCoordinates.value?.[newValue.floor_guid]?.filter(
|
const parsedCoordinates = coordinates.map((coord) => {
|
||||||
(coord) => coord !== ""
|
return JSON.parse(coord);
|
||||||
) || [];
|
});
|
||||||
|
|
||||||
parsedCoordinates.value =
|
|
||||||
coordinates.length > 0
|
|
||||||
? coordinates.map((coord) => JSON.parse(coord))
|
|
||||||
: [];
|
|
||||||
|
|
||||||
asset_floor_chart.value.updateSvg(
|
asset_floor_chart.value.updateSvg(
|
||||||
{
|
{
|
||||||
full_name: newValue.floor_map_name,
|
full_name: newValue?.floor_map_name,
|
||||||
path: `${FILE_BASEURL}/${newValue.floor_map_url}.svg`,
|
path: `${FILE_BASEURL}/${newValue?.floor_map_url}.svg`,
|
||||||
},
|
},
|
||||||
defaultOption(newValue.floor_map_name, parsedCoordinates.value)
|
defaultOption(newValue?.floor_map_name, parsedCoordinates)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const floors = ref([]);
|
||||||
|
|
||||||
|
const getFloors = async () => {
|
||||||
|
const res = await getAssetFloorList();
|
||||||
|
floors.value = res.data[0]?.floors.map((d) => ({ ...d, key: d.floor_guid }));
|
||||||
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
formState,
|
formState,
|
||||||
@ -113,15 +115,105 @@ const getCoordinate = (position) => {
|
|||||||
formState.value.device_coordinate = JSON.stringify(position);
|
formState.value.device_coordinate = JSON.stringify(position);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getFloors();
|
||||||
|
});
|
||||||
|
|
||||||
|
// modal
|
||||||
|
const openModal = () => {
|
||||||
|
if (selectedOption.value === "add") {
|
||||||
|
FloorFormState.value = {
|
||||||
|
full_name: "",
|
||||||
|
floorFile: [],
|
||||||
|
};
|
||||||
|
} else if (selectedOption.value === "edit") {
|
||||||
|
const floor = floors.value.find(
|
||||||
|
(f) => f.floor_guid === formState.value.floor_guid
|
||||||
|
);
|
||||||
|
if (floor) {
|
||||||
|
console.log("floor", floor);
|
||||||
|
FloorFormState.value = {
|
||||||
|
full_name: floor.full_name,
|
||||||
|
floorFile: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asset_add_floor.showModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const form = ref(null);
|
||||||
|
const FloorFormState = ref({
|
||||||
|
full_name: "",
|
||||||
|
floorFile: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const floorScheme = yup.object({
|
||||||
|
full_name: yup.string().required(t("button.required")),
|
||||||
|
floorFile: yup.array(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const updateFileList = (files) => {
|
||||||
|
console.log("file", files);
|
||||||
|
FloorFormState.value.floorFile = files;
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
formErrorMsg: floorFormErrorMsg,
|
||||||
|
handleSubmit,
|
||||||
|
handleErrorReset,
|
||||||
|
updateScheme,
|
||||||
|
} = useFormErrorMessage(floorScheme);
|
||||||
|
const onOk = async () => {
|
||||||
|
const value = handleSubmit(floorScheme, FloorFormState.value);
|
||||||
|
const formData = new FormData(form.value);
|
||||||
|
formData.append("floor_guid", selectedOption.value === "add" ? null :currentFloor.value.floor_guid);
|
||||||
|
formData.append("building_tag", store.selectedBuilding.building_tag);
|
||||||
|
formData.append("initMapName", FloorFormState.value.floorFile[0]?.name);
|
||||||
|
formData.append("mapFile", FloorFormState.value.floorFile[0]);
|
||||||
|
formData.delete("floorFile");
|
||||||
|
for (let [key, value] of formData) {
|
||||||
|
console.log(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await postAssetFloor(formData);
|
||||||
|
if (res.isSuccess) {
|
||||||
|
getFloors();
|
||||||
|
onCancel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const onDelete = async () => {
|
||||||
|
openToast("warning", t("msg.sure_to_delete"), "#asset_add_table_item", async () => {
|
||||||
|
await cancelToastOpen();
|
||||||
|
const res = await deleteAssetFloor({
|
||||||
|
floor_guid: formState.value.floor_guid,
|
||||||
|
});
|
||||||
|
if (res.isSuccess) {
|
||||||
|
getFloors();
|
||||||
|
openToast("success", t("msg.delete_success"), "#asset_add_table_item");
|
||||||
|
} else {
|
||||||
|
openToast("error", res.msg, "#asset_add_table_item");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
FloorFormState.value = {
|
||||||
|
full_name: "",
|
||||||
|
floorFile: [],
|
||||||
|
};
|
||||||
|
asset_add_floor.close();
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- 平面圖 -->
|
<!-- 平面圖 -->
|
||||||
|
|
||||||
<div class="flex gap-4 mb-5">
|
<div class="flex items-center justify-between mb-5">
|
||||||
|
<div className="join">
|
||||||
<Select
|
<Select
|
||||||
:value="formState"
|
:value="formState"
|
||||||
selectClass="border-info focus-within:border-info"
|
selectClass="border-info focus-within:border-info rounded-r-none"
|
||||||
name="floor_guid"
|
name="floor_guid"
|
||||||
Attribute="full_name"
|
Attribute="full_name"
|
||||||
:options="floors"
|
:options="floors"
|
||||||
@ -129,23 +221,25 @@ const getCoordinate = (position) => {
|
|||||||
>
|
>
|
||||||
<template #topLeft>{{ $t("assetManagement.floor") }}</template>
|
<template #topLeft>{{ $t("assetManagement.floor") }}</template>
|
||||||
</Select>
|
</Select>
|
||||||
<Input
|
<select
|
||||||
:value="formState"
|
v-model="selectedOption"
|
||||||
width="270"
|
className="select border-info focus-within:border-info join-item mt-11"
|
||||||
name="device_coordinate"
|
|
||||||
:disabled="true"
|
|
||||||
>
|
>
|
||||||
<template #topLeft>{{
|
<option value="add" selected>{{ $t("button.add") }}</option>
|
||||||
$t("assetManagement.device_coordinate")
|
<option value="edit">{{ $t("button.edit") }}</option>
|
||||||
}}</template>
|
<option value="delete">{{ $t("button.delete") }}</option>
|
||||||
<template #bottomLeft
|
</select>
|
||||||
><span class="text-error text-base">
|
<button
|
||||||
{{ formErrorMsg.device_coordinate }}
|
type="button"
|
||||||
</span></template
|
class="btn btn-success join-item mt-11"
|
||||||
></Input
|
@click="selectedOption === 'delete' ? onDelete() : openModal()"
|
||||||
|
:aria-label="$t('button.submit')"
|
||||||
>
|
>
|
||||||
|
{{ $t("button.submit") }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative min-h-[70vh]">
|
</div>
|
||||||
|
<div class="relative">
|
||||||
<EffectScatter
|
<EffectScatter
|
||||||
id="asset_floor_chart"
|
id="asset_floor_chart"
|
||||||
ref="asset_floor_chart"
|
ref="asset_floor_chart"
|
||||||
@ -159,13 +253,52 @@ const getCoordinate = (position) => {
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="!currentFloor?.floor_map_url"
|
v-if="!currentFloor?.floor_map_url"
|
||||||
class="absolute top-0 left-0 flex justify-center items-center min-h-[70vh] w-full border border-stone-900 shadow-lg bg-sub-success bg-opacity-25 rounded-md"
|
class="absolute top-0 left-0 flex justify-center items-center min-h-[500px] w-full border border-stone-900 shadow-lg bg-sub-success bg-opacity-25 rounded-md"
|
||||||
>
|
>
|
||||||
<p class="text-2xl">{{ $t("assetManagement.add_floor_text") }}</p>
|
<p class="text-2xl">{{ $t("assetManagement.add_floor_text") }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AssetTableModalLeftInfoGraph />
|
<Modal
|
||||||
<AssetTableModalLeftInfoIoT />
|
id="asset_add_floor"
|
||||||
|
:title="t('assetManagement.floor_plan')"
|
||||||
|
:onCancel="onCancel"
|
||||||
|
width="400"
|
||||||
|
>
|
||||||
|
<template #modalContent>
|
||||||
|
<form ref="form">
|
||||||
|
<Input :value="FloorFormState" width="270" name="full_name">
|
||||||
|
<template #topLeft>{{ $t("assetManagement.system_name") }}</template>
|
||||||
|
<template #bottomLeft
|
||||||
|
><span class="text-error text-base">
|
||||||
|
{{ floorFormErrorMsg.full_name }}
|
||||||
|
</span></template
|
||||||
|
></Input
|
||||||
|
>
|
||||||
|
<Upload
|
||||||
|
name="floorFile"
|
||||||
|
:fileList="FloorFormState.floorFile"
|
||||||
|
:getFileList="updateFileList"
|
||||||
|
:multiple="false"
|
||||||
|
class="col-span-2"
|
||||||
|
formats="svg"
|
||||||
|
>
|
||||||
|
<template #topLeft>{{ $t("assetManagement.oriFile") }}</template>
|
||||||
|
</Upload>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
<template #modalAction>
|
||||||
|
<button
|
||||||
|
type="reset"
|
||||||
|
class="btn btn-outline-success mr-2"
|
||||||
|
@click.prevent="onCancel"
|
||||||
|
>
|
||||||
|
{{ $t("button.cancel") }}
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn btn-outline-success" @click="onOk">
|
||||||
|
{{ $t("button.submit") }}
|
||||||
|
</button>
|
||||||
|
</template></Modal
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import ButtonGroup from "@/components/customUI/ButtonGroup.vue";
|
import ButtonGroup from "@/components/customUI/ButtonGroup.vue";
|
||||||
import Account from "./components/Account.vue";
|
import Account from "./components/Account.vue";
|
||||||
import Role from "./components/Role.vue";
|
import Role from "./components/Role.vue";
|
||||||
import { computed, watch, onBeforeMount, markRaw } from "vue";
|
import { computed, watch, onBeforeMount } from "vue";
|
||||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
const { t, locale } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
@ -18,13 +18,13 @@ const initializeItems = () => {
|
|||||||
title: t("accountManagement.account_title"),
|
title: t("accountManagement.account_title"),
|
||||||
key: "account",
|
key: "account",
|
||||||
active: true,
|
active: true,
|
||||||
component: markRaw(Account),
|
component: Account,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("accountManagement.role_title"),
|
title: t("accountManagement.role_title"),
|
||||||
key: "role",
|
key: "role",
|
||||||
active: false,
|
active: false,
|
||||||
component: markRaw(Role),
|
component: Role,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -59,12 +59,7 @@ const searchData = ref({
|
|||||||
const getDataSource = async () => {
|
const getDataSource = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const res = await getAccountUserList(searchData.value);
|
const res = await getAccountUserList(searchData.value);
|
||||||
// 過濾掉 webUser
|
dataSource.value = res.data;
|
||||||
dataSource.value = res.data.filter(
|
|
||||||
(i) =>
|
|
||||||
i.userinfo_guid !== "6ac24708-3a40-4199-88c5-22df310cd1a8" &&
|
|
||||||
i.userinfo_guid !== "B43E3CA7-96DD-4FC7-B6E6-974ACC3B0878"
|
|
||||||
);
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -127,7 +127,7 @@ const onOk = async () => {
|
|||||||
id="account_user_modal"
|
id="account_user_modal"
|
||||||
:title="formState?.Id ? t('button.edit') : t('button.add')"
|
:title="formState?.Id ? t('button.edit') : t('button.add')"
|
||||||
:onCancel="onCancel"
|
:onCancel="onCancel"
|
||||||
:width="710"
|
width="710"
|
||||||
>
|
>
|
||||||
<template #modalContent>
|
<template #modalContent>
|
||||||
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const { t } = useI18n();
|
|||||||
const { openToast } = inject("app_toast");
|
const { openToast } = inject("app_toast");
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
account: Object,
|
account: String,
|
||||||
});
|
});
|
||||||
|
|
||||||
const formState = ref({
|
const formState = ref({
|
||||||
@ -48,7 +48,7 @@ const onOk = async () => {
|
|||||||
id="account_user_password_modal"
|
id="account_user_password_modal"
|
||||||
:title="t('accountManagement.change_password')"
|
:title="t('accountManagement.change_password')"
|
||||||
:onCancel="onCancel"
|
:onCancel="onCancel"
|
||||||
:width="710"
|
width="710"
|
||||||
>
|
>
|
||||||
<template #modalContent>
|
<template #modalContent>
|
||||||
<p class="mt-10 text-3xl">{{ account.Name }}</p>
|
<p class="mt-10 text-3xl">{{ account.Name }}</p>
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import useActiveBtn from "@/hooks/useActiveBtn";
|
|||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
selectedRole: Object,
|
selectedRole: String,
|
||||||
cancelModal: Function,
|
cancelModal: Function,
|
||||||
disabled: Boolean,
|
disabled: Boolean,
|
||||||
update: Function,
|
update: Function,
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import ButtonGroup from "@/components/customUI/ButtonGroup.vue";
|
import ButtonGroup from "@/components/customUI/ButtonGroup.vue";
|
||||||
import AlertQuery from "./components/AlertQuery/AlertQuery.vue";
|
import AlertQuery from "./components/AlertQuery/AlertQuery.vue";
|
||||||
import AlertSetting from "./components/AlertSetting/AlertSetting.vue";
|
import AlertSetting from "./components/AlertSetting/AlertSetting.vue";
|
||||||
import { computed, watch, onBeforeMount, markRaw } from "vue";
|
import { computed, watch, onBeforeMount } from "vue";
|
||||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
const { t, locale } = useI18n();
|
const { t, locale } = useI18n();
|
||||||
@ -18,13 +18,13 @@ const initializeItems = () => {
|
|||||||
title: t("alert.query_title"),
|
title: t("alert.query_title"),
|
||||||
key: "Query",
|
key: "Query",
|
||||||
active: true,
|
active: true,
|
||||||
component: markRaw(AlertQuery),
|
component: AlertQuery,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("alert.setting_title"),
|
title: t("alert.setting_title"),
|
||||||
key: "Setting",
|
key: "Setting",
|
||||||
active: false,
|
active: false,
|
||||||
component: markRaw(AlertSetting),
|
component: AlertSetting,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,7 +4,8 @@ import AlertTable from "./AlertTable.vue";
|
|||||||
import AlertTableModal from "./AlertTableModal.vue";
|
import AlertTableModal from "./AlertTableModal.vue";
|
||||||
import { ref, provide, onMounted } from "vue";
|
import { ref, provide, onMounted } from "vue";
|
||||||
import useSearchParam from "@/hooks/useSearchParam";
|
import useSearchParam from "@/hooks/useSearchParam";
|
||||||
import { getAlertLog } from "@/apis/alert";
|
import useAlarmData from "@/hooks/baja/useAlarmData";
|
||||||
|
import { getAlertFormId } from "@/apis/alert";
|
||||||
import {
|
import {
|
||||||
getOperationDeviceList,
|
getOperationDeviceList,
|
||||||
getOperationCompanyList,
|
getOperationCompanyList,
|
||||||
@ -12,14 +13,18 @@ import {
|
|||||||
} from "@/apis/operation";
|
} from "@/apis/operation";
|
||||||
import { getAccountUserList } from "@/apis/account";
|
import { getAccountUserList } from "@/apis/account";
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
import useBuildingStore from "@/stores/useBuildingStore";
|
||||||
|
import useAlarmStore from "@/stores/useAlarmStore";
|
||||||
|
const storeAlarm = useAlarmStore();
|
||||||
|
|
||||||
const { searchParams } = useSearchParam();
|
const { searchParams } = useSearchParam();
|
||||||
|
const { getAlarmByBaja, alarmData } = useAlarmData();
|
||||||
const store = useBuildingStore();
|
const store = useBuildingStore();
|
||||||
|
|
||||||
const tableLoading = ref(false);
|
const tableLoading = ref(false);
|
||||||
const dataSource = ref([]);
|
const dataSource = ref([]);
|
||||||
const model_data = ref({
|
const model_data = ref({
|
||||||
model_userList: [],
|
model_userList: [],
|
||||||
|
model_devList: [],
|
||||||
model_companyList: [],
|
model_companyList: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -44,6 +49,23 @@ const updateEditRecord = (data) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getFormId = async (uuid) => {
|
||||||
|
const res = await getAlertFormId(uuid);
|
||||||
|
return res.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getModalDevList = async () => {
|
||||||
|
const sub_system_tags = searchParams.value.system_tag.map(
|
||||||
|
(tag) => tag.split("_")[1]
|
||||||
|
);
|
||||||
|
const res = await getOperationDeviceList({
|
||||||
|
list_sub_system_tag: sub_system_tags,
|
||||||
|
device_building_tag: store.buildings[0].building_tag,
|
||||||
|
device_area_tag: "NTPC",
|
||||||
|
});
|
||||||
|
return res.data.map((d) => ({ ...d, key: d.device_number }));
|
||||||
|
};
|
||||||
|
|
||||||
const getModalUserList = async () => {
|
const getModalUserList = async () => {
|
||||||
const res = await getAccountUserList({});
|
const res = await getAccountUserList({});
|
||||||
return res.data.map((d) => ({ ...d, key: d.userinfo_guid }));
|
return res.data.map((d) => ({ ...d, key: d.userinfo_guid }));
|
||||||
@ -55,23 +77,63 @@ const getModalCompanyList = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getAllOptions = async () => {
|
const getAllOptions = async () => {
|
||||||
Promise.all([getModalUserList(), getModalCompanyList()]).then(
|
Promise.all([
|
||||||
([users, companies]) => {
|
getModalDevList(),
|
||||||
|
getModalUserList(),
|
||||||
|
getModalCompanyList(),
|
||||||
|
]).then(([devices, users, companies]) => {
|
||||||
model_data.value.model_userList = users;
|
model_data.value.model_userList = users;
|
||||||
|
model_data.value.model_devList = devices;
|
||||||
model_data.value.model_companyList = companies;
|
model_data.value.model_companyList = companies;
|
||||||
}
|
});
|
||||||
);
|
};
|
||||||
|
|
||||||
|
const updateDataSource = (data) => {
|
||||||
|
dataSource.value = data.map((d) => ({ ...d, key: d.uuid }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const search = async () => {
|
const search = async () => {
|
||||||
tableLoading.value = true;
|
tableLoading.value = true;
|
||||||
if (Object.keys(searchParams.value).length !== 0) {
|
if (Object.keys(searchParams.value).length !== 0) {
|
||||||
const res = await getAlertLog({
|
storeAlarm.getAlarmDataFromBaja();
|
||||||
...searchParams.value,
|
updateDataSource(storeAlarm.alarmData);
|
||||||
isRecovery: Number(searchParams.value.isRecovery),
|
|
||||||
});
|
|
||||||
dataSource.value = (res.data || []).map((d) => ({ ...d, key: d.id }));
|
|
||||||
tableLoading.value = false;
|
tableLoading.value = false;
|
||||||
|
/*
|
||||||
|
await getAlarmByBaja(
|
||||||
|
searchParams.value.start_created_at,
|
||||||
|
searchParams.value.end_created_at,
|
||||||
|
searchParams.value.isRecover,
|
||||||
|
searchParams.value.isAck,
|
||||||
|
searchParams.value.system_tag,
|
||||||
|
async (result) => {
|
||||||
|
alarmData.value = result.data;
|
||||||
|
// 確保所有 alarm 都包含 formId
|
||||||
|
alarmData.value = alarmData.value.map((alarm) => ({
|
||||||
|
...alarm,
|
||||||
|
formId: null, // 初始設置 formId 為 null
|
||||||
|
}));
|
||||||
|
|
||||||
|
const uuids = alarmData.value.map((alarm) => ({ uuid: alarm.uuid }));
|
||||||
|
const formIds = await getFormId(uuids);
|
||||||
|
|
||||||
|
if (Array.isArray(formIds)) {
|
||||||
|
formIds.forEach((form) => {
|
||||||
|
if (form && form.uuid) {
|
||||||
|
const index = alarmData.value.findIndex(
|
||||||
|
(alarm) => alarm.uuid === form.uuid
|
||||||
|
);
|
||||||
|
if (index !== -1) {
|
||||||
|
alarmData.value[index].formId = form.formId || null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDataSource(alarmData.value);
|
||||||
|
tableLoading.value = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -79,17 +141,11 @@ const openModal = async (record) => {
|
|||||||
try {
|
try {
|
||||||
if (record.formId) {
|
if (record.formId) {
|
||||||
const res = await getOperationEditRecord(record.formId);
|
const res = await getOperationEditRecord(record.formId);
|
||||||
updateEditRecord({
|
updateEditRecord({ ...res.data, uuid: record.uuid });
|
||||||
...res.data,
|
|
||||||
uuid: res.data.error_code,
|
|
||||||
device_number: record.device_number
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
updateEditRecord({
|
updateEditRecord(record);
|
||||||
...record,
|
|
||||||
uuid: record.id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
await getAllOptions();
|
||||||
alert_action_item.showModal();
|
alert_action_item.showModal();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error opening modal:", error);
|
console.error("Error opening modal:", error);
|
||||||
@ -97,7 +153,6 @@ const openModal = async (record) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getAllOptions();
|
|
||||||
if (Object.keys(searchParams.value).length !== 0) {
|
if (Object.keys(searchParams.value).length !== 0) {
|
||||||
search();
|
search();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { inject } from "vue";
|
import { inject } from "vue";
|
||||||
import AlertSearchNormalBtns from "./AlertSearchNormalBtns.vue";
|
import AlertSearchNormalBtns from "./AlertSearchNormalBtns.vue";
|
||||||
|
import AlertSearchAckBtns from "./AlertSearchAckBtns.vue";
|
||||||
import AlertSearchTimeRange from "./AlertSearchTimeRange.vue";
|
import AlertSearchTimeRange from "./AlertSearchTimeRange.vue";
|
||||||
import AlertSearchTypesButton from "./AlertSearchTypesButton.vue";
|
import AlertSearchTypesButton from "./AlertSearchTypesButton.vue";
|
||||||
|
|
||||||
@ -12,7 +13,7 @@ const { search } = inject("alert_table");
|
|||||||
<div class="w-full flex flex-wrap flex-col custom-border px-4 pt-0 pb-4 mb-4">
|
<div class="w-full flex flex-wrap flex-col custom-border px-4 pt-0 pb-4 mb-4">
|
||||||
<div class="w-full flex flex-wrap items-center justify-start">
|
<div class="w-full flex flex-wrap items-center justify-start">
|
||||||
<AlertSearchNormalBtns />
|
<AlertSearchNormalBtns />
|
||||||
<!-- <AlertSearchAckBtns /> -->
|
<AlertSearchAckBtns />
|
||||||
<AlertSearchTimeRange />
|
<AlertSearchTimeRange />
|
||||||
<button class="btn btn-search ml-8" @click.stop.prevent="search">
|
<button class="btn btn-search ml-8" @click.stop.prevent="search">
|
||||||
<font-awesome-icon :icon="['fas', 'search']" class=" text-lg" />
|
<font-awesome-icon :icon="['fas', 'search']" class=" text-lg" />
|
||||||
|
|||||||
@ -11,12 +11,12 @@ const initializeItems = () => {
|
|||||||
setItems([
|
setItems([
|
||||||
{
|
{
|
||||||
title: t("alert.offnormal"),
|
title: t("alert.offnormal"),
|
||||||
key: 1,
|
key: "offnormal",
|
||||||
active: true,
|
active: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("alert.normal"),
|
title: t("alert.normal"),
|
||||||
key: 2,
|
key: "normal",
|
||||||
active: false,
|
active: false,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@ -34,7 +34,7 @@ watch(locale, () => {
|
|||||||
watch(
|
watch(
|
||||||
selectedBtn,
|
selectedBtn,
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
changeParams({ ...searchParams.value, isRecovery: newValue.key });
|
changeParams({ ...searchParams.value, isRecover: newValue.key });
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -42,8 +42,8 @@ watch(
|
|||||||
watch(
|
watch(
|
||||||
searchParams,
|
searchParams,
|
||||||
(newSearchParams) => {
|
(newSearchParams) => {
|
||||||
if (!newSearchParams.isRecovery) {
|
if (!newSearchParams.isRecover) {
|
||||||
changeParams({ ...newSearchParams, isRecovery: 1 });
|
changeParams({ ...newSearchParams, isRecover: "offnormal" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true } // 確保在初始化立即觸發
|
{ immediate: true } // 確保在初始化立即觸發
|
||||||
|
|||||||
@ -9,17 +9,17 @@ const { searchParams, changeParams } = useSearchParam();
|
|||||||
const dateRange = ref([
|
const dateRange = ref([
|
||||||
{
|
{
|
||||||
key: "start_at",
|
key: "start_at",
|
||||||
value: searchParams.value.Start_date
|
value: searchParams.value.start_created_at
|
||||||
? dayjs(searchParams.value.Start_date)
|
? dayjs(searchParams.value.start_created_at).valueOf()
|
||||||
: dayjs().subtract(30, "day"),
|
: dayjs().subtract(30, "day").valueOf(),
|
||||||
dateFormat: "yyyy-MM-dd",
|
dateFormat: "yyyy-MM-dd",
|
||||||
placeholder: t("alert.start_date"),
|
placeholder: t("alert.start_date"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "end_at",
|
key: "end_at",
|
||||||
value: searchParams.value.End_date
|
value: searchParams.value.end_created_at
|
||||||
? dayjs(searchParams.value.End_date)
|
? dayjs(searchParams.value.end_created_at).valueOf()
|
||||||
: dayjs(),
|
: dayjs().valueOf(),
|
||||||
dateFormat: "yyyy-MM-dd",
|
dateFormat: "yyyy-MM-dd",
|
||||||
placeholder: t("alert.end_date"),
|
placeholder: t("alert.end_date"),
|
||||||
},
|
},
|
||||||
@ -29,13 +29,13 @@ const changeTimeRange = () => {
|
|||||||
const newRange = [
|
const newRange = [
|
||||||
{
|
{
|
||||||
key: "start_at",
|
key: "start_at",
|
||||||
value: dayjs().subtract(30, "day"),
|
value: dayjs().subtract(30, "day").startOf("day").valueOf(), // 向前推30天並設置為當天的00:00:00.000
|
||||||
dateFormat: "yyyy-MM-dd",
|
dateFormat: "yyyy-MM-dd",
|
||||||
placeholder: t("alert.start_date"),
|
placeholder: t("alert.start_date"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "end_at",
|
key: "end_at",
|
||||||
value: dayjs().endOf("day"),
|
value: dayjs().endOf("day").valueOf(), // 設置為今日的23:59:59.999
|
||||||
dateFormat: "yyyy-MM-dd",
|
dateFormat: "yyyy-MM-dd",
|
||||||
placeholder: t("alert.end_date"),
|
placeholder: t("alert.end_date"),
|
||||||
},
|
},
|
||||||
@ -45,16 +45,16 @@ const changeTimeRange = () => {
|
|||||||
|
|
||||||
changeParams({
|
changeParams({
|
||||||
...searchParams.value,
|
...searchParams.value,
|
||||||
Start_date: newRange[0].value,
|
start_created_at: newRange[0].value,
|
||||||
End_date: newRange[1].value,
|
end_created_at: newRange[1].value,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 初始化日期範圍
|
// 初始化日期範圍
|
||||||
if (
|
if (
|
||||||
!searchParams.value.Start_date ||
|
!searchParams.value.start_created_at ||
|
||||||
!searchParams.value.End_date
|
!searchParams.value.end_created_at
|
||||||
) {
|
) {
|
||||||
changeTimeRange();
|
changeTimeRange();
|
||||||
}
|
}
|
||||||
@ -66,8 +66,10 @@ watch(
|
|||||||
() => {
|
() => {
|
||||||
changeParams({
|
changeParams({
|
||||||
...searchParams.value,
|
...searchParams.value,
|
||||||
Start_date: dayjs(dateRange.value[0].value).format("YYYY-MM-DD"),
|
start_created_at: dayjs(dateRange.value[0].value)
|
||||||
End_date: dayjs(dateRange.value[1].value).format("YYYY-MM-DD"),
|
.startOf("day")
|
||||||
|
.valueOf(),
|
||||||
|
end_created_at: dayjs(dateRange.value[1].value).endOf("day").valueOf(),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{ deep: true } // 確保在初始化立即觸發
|
{ deep: true } // 確保在初始化立即觸發
|
||||||
|
|||||||
@ -12,12 +12,12 @@ const changeCheckedItem = () => {
|
|||||||
if (checkedItem.value.length === store.subSys.length) {
|
if (checkedItem.value.length === store.subSys.length) {
|
||||||
changeParams({
|
changeParams({
|
||||||
...searchParams.value,
|
...searchParams.value,
|
||||||
device_name_tag: [],
|
system_tag: [],
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
changeParams({
|
changeParams({
|
||||||
...searchParams.value,
|
...searchParams.value,
|
||||||
device_name_tag: store.subSys.map(({ sub_system_tag }) => sub_system_tag),
|
system_tag: store.subSys.map(({ main_system_tag, sub_system_tag }) => main_system_tag+`_`+sub_system_tag),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -26,16 +26,16 @@ const onChange = (value, checked) => {
|
|||||||
if (checked) {
|
if (checked) {
|
||||||
changeParams({
|
changeParams({
|
||||||
...searchParams.value,
|
...searchParams.value,
|
||||||
device_name_tag: searchParams.value.device_name_tag
|
system_tag: searchParams.value.system_tag
|
||||||
? typeof searchParams.value.device_name_tag === "string"
|
? typeof searchParams.value.system_tag === "string"
|
||||||
? [searchParams.value.device_name_tag, value]
|
? [searchParams.value.system_tag, value]
|
||||||
: [...searchParams.value.device_name_tag, value]
|
: [...searchParams.value.system_tag, value]
|
||||||
: [value],
|
: [value],
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
changeParams({
|
changeParams({
|
||||||
...searchParams.value,
|
...searchParams.value,
|
||||||
device_name_tag: searchParams.value.device_name_tag.filter(
|
sub_system_tag: searchParams.value.system_tag.filter(
|
||||||
(sub) => sub !== value
|
(sub) => sub !== value
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
@ -43,18 +43,18 @@ const onChange = (value, checked) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const checkedItem = computed(() =>
|
const checkedItem = computed(() =>
|
||||||
searchParams.value.device_name_tag
|
searchParams.value.system_tag
|
||||||
? typeof searchParams.value.device_name_tag === "string"
|
? typeof searchParams.value.system_tag === "string"
|
||||||
? [searchParams.value.device_name_tag]
|
? [searchParams.value.system_tag]
|
||||||
: searchParams.value.device_name_tag
|
: searchParams.value.system_tag
|
||||||
: []
|
: []
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(searchParams, (newValue) => {
|
watch(searchParams, (newValue) => {
|
||||||
if (!newValue.device_name_tag) {
|
if (!newValue.system_tag) {
|
||||||
changeParams({
|
changeParams({
|
||||||
...newValue,
|
...newValue,
|
||||||
device_name_tag: [store.subSys[0]?.sub_system_tag],
|
system_tag: [store.subSys[0]?.main_system_tag+`_`+store.subSys[0]?.sub_system_tag],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -75,8 +75,8 @@ watch(searchParams, (newValue) => {
|
|||||||
v-for="sub in store.subSys"
|
v-for="sub in store.subSys"
|
||||||
:key="sub.key"
|
:key="sub.key"
|
||||||
:title="sub.full_name"
|
:title="sub.full_name"
|
||||||
:value="sub.sub_system_tag"
|
:value="sub.main_system_tag+`_`+sub.sub_system_tag"
|
||||||
:checked="checkedItem.includes(sub.sub_system_tag)"
|
:checked="checkedItem.includes(sub.main_system_tag+`_`+sub.sub_system_tag)"
|
||||||
class="mx-3 my-3 xl:my-0 text-lg"
|
class="mx-3 my-3 xl:my-0 text-lg"
|
||||||
:onChange="onChange"
|
:onChange="onChange"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { inject, computed } from "vue";
|
import { inject, computed } from "vue";
|
||||||
|
import { postChgAck } from "@/apis/alert";
|
||||||
import { Button } from "ant-design-vue";
|
import { Button } from "ant-design-vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
@ -7,39 +8,71 @@ const { dataSource, openModal, search, tableLoading } = inject("alert_table");
|
|||||||
|
|
||||||
const columns = computed(() => [
|
const columns = computed(() => [
|
||||||
{
|
{
|
||||||
key: "id",
|
key: "building_tag",
|
||||||
|
title: t("alert.building_and_floor"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "uuid",
|
||||||
title: t("alert.uuid"),
|
title: t("alert.uuid"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "factor",
|
key: "alarmClass",
|
||||||
title: t("alert.alarmClass"),
|
title: t("alert.alarmClass"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "device_number",
|
key: "full_name",
|
||||||
title: t("alert.device_name"),
|
title: t("alert.device_name"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "points",
|
key: "device_number",
|
||||||
title: t("alert.device_point_name"),
|
title: t("alert.device_number"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "created_at",
|
key: "timestamp_date",
|
||||||
|
title: t("alert.date"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "timestamp_time",
|
||||||
title: t("alert.time"),
|
title: t("alert.time"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "reason",
|
key: "msg",
|
||||||
title: t("alert.error_msg"),
|
title: t("alert.error_msg"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "ackState",
|
||||||
|
title: t("alert.ack_state"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "repairOrder",
|
key: "repairOrder",
|
||||||
title: t("alert.repair_order_number"),
|
title: t("alert.repair_order_number"),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const chgAck = async (devUuid) => {
|
||||||
|
const res = await postChgAck(devUuid);
|
||||||
|
if (res.isSuccess) {
|
||||||
|
search?.();
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Table :loading="tableLoading" :columns="columns" :dataSource="dataSource">
|
<Table :loading="tableLoading" :columns="columns" :dataSource="dataSource">
|
||||||
<template #bodyCell="{ record, column, index }">
|
<template #bodyCell="{ record, column, index }">
|
||||||
|
<template v-if="column.key === 'ackState'">
|
||||||
|
<template v-if="record.ackState === 'Unacked'">
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-success text-white whitespace-nowrap mr-2"
|
||||||
|
@click.stop.prevent="() => chgAck(record.uuid)"
|
||||||
|
>
|
||||||
|
{{ $t("alert.unacked") }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ record.ackedTime }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
<template v-if="column.key === 'repairOrder'">
|
<template v-if="column.key === 'repairOrder'">
|
||||||
<button
|
<button
|
||||||
class="btn btn-sm btn-success text-white whitespace-nowrap mr-2"
|
class="btn btn-sm btn-success text-white whitespace-nowrap mr-2"
|
||||||
|
|||||||
@ -27,13 +27,12 @@ const dateItem = ref([
|
|||||||
|
|
||||||
const formState = ref({
|
const formState = ref({
|
||||||
formId: null,
|
formId: null,
|
||||||
uuid: null,
|
uuid: "",
|
||||||
work_type: 2,
|
work_type: 2,
|
||||||
fix_do: "",
|
fix_do: "",
|
||||||
fix_do_code: "",
|
fix_do_code: "",
|
||||||
fix_firm: "",
|
fix_firm: "",
|
||||||
status: 0,
|
status: 0,
|
||||||
device_number: "",
|
|
||||||
work_person_id: "",
|
work_person_id: "",
|
||||||
start_time: dayjs().format("YYYY-MM-DD"),
|
start_time: dayjs().format("YYYY-MM-DD"),
|
||||||
notice: "",
|
notice: "",
|
||||||
@ -50,6 +49,7 @@ const { model_data, updateEditRecord, search } = inject("alert_modal") || {
|
|||||||
let alertSchema = yup.object({
|
let alertSchema = yup.object({
|
||||||
start_time: yup.date().required(t("button.required")),
|
start_time: yup.date().required(t("button.required")),
|
||||||
fix_do: yup.string().required(t("button.required")),
|
fix_do: yup.string().required(t("button.required")),
|
||||||
|
fix_do_code: yup.string().required(t("button.required")),
|
||||||
fix_firm: yup.string().required(t("button.required")),
|
fix_firm: yup.string().required(t("button.required")),
|
||||||
status: yup.number().required(t("button.required")),
|
status: yup.number().required(t("button.required")),
|
||||||
work_person_id: yup.string().required(t("button.required")),
|
work_person_id: yup.string().required(t("button.required")),
|
||||||
@ -88,12 +88,12 @@ const onOk = async () => {
|
|||||||
props.editRecord.uuid && formData.append("error_code", props.editRecord.uuid);
|
props.editRecord.uuid && formData.append("error_code", props.editRecord.uuid);
|
||||||
|
|
||||||
formData.append("work_type", 2);
|
formData.append("work_type", 2);
|
||||||
formData.append(
|
|
||||||
"fix_do_code",
|
if (props.editRecord.device_number) {
|
||||||
props.editRecord.main_id
|
formData.append("fix_do_code", props.editRecord.device_number);
|
||||||
? props.editRecord.main_id
|
} else if (props.editRecord.fix_do_code) {
|
||||||
: props.editRecord.fix_do_code
|
formData.append("fix_do_code", props.editRecord.fix_do_code);
|
||||||
);
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const value = await handleSubmit(alertSchema, formState.value);
|
const value = await handleSubmit(alertSchema, formState.value);
|
||||||
@ -110,13 +110,12 @@ const onOk = async () => {
|
|||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
formState.value = {
|
formState.value = {
|
||||||
formId: null,
|
formId: null,
|
||||||
uuid: null,
|
uuid: "",
|
||||||
work_type: 2,
|
work_type: 2,
|
||||||
fix_do: "",
|
fix_do: "",
|
||||||
fix_do_code: "",
|
fix_do_code: "",
|
||||||
fix_firm: "",
|
fix_firm: "",
|
||||||
status: 0,
|
status: 0,
|
||||||
device_number: "",
|
|
||||||
work_person_id: "",
|
work_person_id: "",
|
||||||
start_time: dayjs().format("YYYY-MM-DD"),
|
start_time: dayjs().format("YYYY-MM-DD"),
|
||||||
notice: "",
|
notice: "",
|
||||||
@ -148,6 +147,10 @@ watch(
|
|||||||
if (key === "full_name") {
|
if (key === "full_name") {
|
||||||
formState.value.fix_do = value;
|
formState.value.fix_do = value;
|
||||||
}
|
}
|
||||||
|
// 維修項目代碼
|
||||||
|
if (key === "device_number" || key === "fix_do_code") {
|
||||||
|
formState.value.fix_do_code = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -160,12 +163,12 @@ watch(
|
|||||||
id="alert_action_item"
|
id="alert_action_item"
|
||||||
:title="t('alert.repair_order')"
|
:title="t('alert.repair_order')"
|
||||||
:onCancel="onCancel"
|
:onCancel="onCancel"
|
||||||
:width="710"
|
width="710"
|
||||||
>
|
>
|
||||||
<template #modalContent>
|
<template #modalContent>
|
||||||
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
||||||
<Input
|
<Input
|
||||||
v-if="formState && formState.formId"
|
v-if="formState.value && formState.value.formId"
|
||||||
class="my-2"
|
class="my-2"
|
||||||
:value="formState"
|
:value="formState"
|
||||||
name="formId"
|
name="formId"
|
||||||
@ -199,7 +202,7 @@ watch(
|
|||||||
{
|
{
|
||||||
key: 1,
|
key: 1,
|
||||||
value: 1,
|
value: 1,
|
||||||
title: $t('operation.maintenance'),
|
title: $t('alert.maintenance'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 2,
|
key: 2,
|
||||||
@ -220,20 +223,23 @@ watch(
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</Input>
|
</Input>
|
||||||
<Input
|
<Select
|
||||||
class="my-2"
|
|
||||||
:value="formState"
|
:value="formState"
|
||||||
name="device_number"
|
class="my-2"
|
||||||
|
selectClass="border-info focus-within:border-info"
|
||||||
|
name="fix_do_code"
|
||||||
|
Attribute="device_name"
|
||||||
|
:options="model_data.model_devList"
|
||||||
:required="true"
|
:required="true"
|
||||||
:disabled="true"
|
:disabled="true"
|
||||||
>
|
>
|
||||||
<template #topLeft>{{ $t("alert.repair_item_code") }}</template>
|
<template #topLeft>{{ $t("repair_item_code") }}</template>
|
||||||
<template #bottomLeft
|
<template #bottomLeft
|
||||||
><span class="text-error text-base">
|
><span class="text-error text-base">
|
||||||
{{ formErrorMsg.fix_do_code }}
|
{{ formErrorMsg.fix_do_code }}
|
||||||
</span></template
|
</span></template
|
||||||
>
|
>
|
||||||
</Input>
|
</Select>
|
||||||
<Select
|
<Select
|
||||||
:value="formState"
|
:value="formState"
|
||||||
class="my-2"
|
class="my-2"
|
||||||
@ -243,7 +249,7 @@ watch(
|
|||||||
:options="model_data.model_companyList"
|
:options="model_data.model_companyList"
|
||||||
:required="true"
|
:required="true"
|
||||||
>
|
>
|
||||||
<template #topLeft>{{ $t("alert.responsible_vendor") }}</template>
|
<template #topLeft>{{ $t("responsible_vendor") }}</template>
|
||||||
<template #bottomLeft
|
<template #bottomLeft
|
||||||
><span class="text-error text-base">
|
><span class="text-error text-base">
|
||||||
{{ formErrorMsg.fix_firm }}
|
{{ formErrorMsg.fix_firm }}
|
||||||
|
|||||||
@ -94,8 +94,9 @@ const closeModal = () => {
|
|||||||
<Modal
|
<Modal
|
||||||
id="notify_add_table_item"
|
id="notify_add_table_item"
|
||||||
:title="t('alert.notify_list')"
|
:title="t('alert.notify_list')"
|
||||||
|
:open="open"
|
||||||
:onCancel="closeModal"
|
:onCancel="closeModal"
|
||||||
:width="300"
|
width="300"
|
||||||
>
|
>
|
||||||
<template #modalContent>
|
<template #modalContent>
|
||||||
<form ref="form" class="mt-5 flex flex-col items-center">
|
<form ref="form" class="mt-5 flex flex-col items-center">
|
||||||
@ -127,7 +128,7 @@ const closeModal = () => {
|
|||||||
<p class="text-light text-base">{{ $t("alert.notify_items") }}</p>
|
<p class="text-light text-base">{{ $t("alert.notify_items") }}</p>
|
||||||
<AlertNoticesTable
|
<AlertNoticesTable
|
||||||
:SaveCheckAuth="SaveCheckAuth"
|
:SaveCheckAuth="SaveCheckAuth"
|
||||||
:NoticeData="[noticeList[1]]"
|
:NoticeData="[noticeList[2]]"
|
||||||
:onChange="onChange"
|
:onChange="onChange"
|
||||||
/>
|
/>
|
||||||
<span class="text-error text-base">
|
<span class="text-error text-base">
|
||||||
|
|||||||
@ -4,24 +4,20 @@ import {
|
|||||||
getOutliersList,
|
getOutliersList,
|
||||||
getOutliersDevList,
|
getOutliersDevList,
|
||||||
getOutliersPoints,
|
getOutliersPoints,
|
||||||
getFactors,
|
|
||||||
delOutliersSetting,
|
|
||||||
postMQTTRefresh,
|
|
||||||
} from "@/apis/alert";
|
} from "@/apis/alert";
|
||||||
import useSearchParam from "@/hooks/useSearchParam";
|
import useSearchParam from "@/hooks/useSearchParam";
|
||||||
import AlertOutliersTableAddModal from "./AlertOutliersTableAddModal.vue";
|
import AlertOutliersTableAddModal from "./AlertOutliersTableAddModal.vue";
|
||||||
|
import AlertOutliersTimeModal from "./AlertOutliersTimeModal.vue";
|
||||||
|
import dayjs from "dayjs";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { noticeList, timesList } = inject("notify_table");
|
const { noticeList } = inject("notify_table");
|
||||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
|
||||||
const { searchParams, changeParams } = useSearchParam();
|
const { searchParams, changeParams } = useSearchParam();
|
||||||
|
|
||||||
const tableData = ref([]);
|
const tableData = ref([]);
|
||||||
const dev_data = ref({
|
const dev_data = ref({
|
||||||
devList: [],
|
devList: [],
|
||||||
alarmPoints: [],
|
alarmPoints: [],
|
||||||
alarmFactors: [],
|
|
||||||
});
|
});
|
||||||
const editRecord = ref(null);
|
const editRecord = ref(null);
|
||||||
|
|
||||||
@ -37,7 +33,7 @@ const columns = computed(() => [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("alert.item"),
|
title: t("alert.item"),
|
||||||
key: "points_name",
|
key: "points",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("alert.enable"),
|
title: t("alert.enable"),
|
||||||
@ -47,35 +43,35 @@ const columns = computed(() => [
|
|||||||
title: t("alert.qualifications"),
|
title: t("alert.qualifications"),
|
||||||
key: "factor_name",
|
key: "factor_name",
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// title: `${t("alert.upper_limit")} (>=)`,
|
title: `${t("alert.upper_limit")} (>=)`,
|
||||||
// key: "highLimit",
|
key: "highLimit",
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// title: `${t("alert.lower_limit")} (<=)`,
|
title: `${t("alert.lower_limit")} (<=)`,
|
||||||
// key: "lowLimit",
|
key: "lowLimit",
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// title: t("alert.highDelay"),
|
title: t("alert.highDelay"),
|
||||||
// key: "highDelay",
|
key: "highDelay",
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// title: t("alert.lowDelay"),
|
title: t("alert.lowDelay"),
|
||||||
// key: "lowDelay",
|
key: "lowDelay",
|
||||||
// },
|
},
|
||||||
{
|
{
|
||||||
title: t("alert.warning_method"),
|
title: t("alert.warning_method"),
|
||||||
key: "warning_method",
|
key: "warning_method",
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// title: t("alert.warning_time"),
|
title: t("alert.warning_time"),
|
||||||
// key: "warning_time",
|
key: "warning_time",
|
||||||
// width: 150,
|
width: 150,
|
||||||
// },
|
},
|
||||||
{
|
{
|
||||||
title: t("alert.operation"),
|
title: t("alert.operation"),
|
||||||
key: "operation",
|
key: "operation",
|
||||||
width: 300,
|
width: 150,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -99,14 +95,6 @@ const getAlarmPoints = async () => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const getFactor = async () => {
|
|
||||||
const res = await getFactors();
|
|
||||||
return res.data.map((d) => ({
|
|
||||||
...d,
|
|
||||||
key: d.id,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const getOutliersData = async () => {
|
const getOutliersData = async () => {
|
||||||
const res = await getOutliersList({
|
const res = await getOutliersList({
|
||||||
device_name_tag: searchParams.value?.subSys_id,
|
device_name_tag: searchParams.value?.subSys_id,
|
||||||
@ -119,12 +107,10 @@ const getOutliersData = async () => {
|
|||||||
const matchedPoints = dev_data.value.alarmPoints.find(
|
const matchedPoints = dev_data.value.alarmPoints.find(
|
||||||
(p) => p.points === item.points
|
(p) => p.points === item.points
|
||||||
);
|
);
|
||||||
const matchedFactor = dev_data.value.alarmFactors.find(
|
const matchedFactor =
|
||||||
(p) => p.id === item.factor
|
matchedPoints?.factor && item.factor
|
||||||
);
|
? matchedPoints?.factor?.find((f) => f.id === item.factor)
|
||||||
const matchedTime = timesList.value.find(
|
: null;
|
||||||
(t) => t.id === item.schedule_id
|
|
||||||
);
|
|
||||||
const warningMethodKeys = item.notices
|
const warningMethodKeys = item.notices
|
||||||
?.map((noticeValue) => {
|
?.map((noticeValue) => {
|
||||||
const matchedNotice = noticeList.value.find(
|
const matchedNotice = noticeList.value.find(
|
||||||
@ -138,24 +124,19 @@ const getOutliersData = async () => {
|
|||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
device_name: matchedDevice ? matchedDevice.device_name : "",
|
device_name: matchedDevice ? matchedDevice.device_name : "",
|
||||||
points_name: matchedPoints ? matchedPoints.full_name : "",
|
points: matchedPoints ? matchedPoints.full_name : "",
|
||||||
|
is_bool: matchedPoints ? matchedPoints.is_bool : 1,
|
||||||
factor_name: matchedFactor ? matchedFactor.full_name : "",
|
factor_name: matchedFactor ? matchedFactor.full_name : "",
|
||||||
warning_method: warningMethodKeys,
|
warning_method: warningMethodKeys,
|
||||||
// warning_time: matchedTime ? matchedTime.schedule_name : "",
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAllOptions = async () => {
|
const getAllOptions = async () => {
|
||||||
const [devices, points, factors] = await Promise.all([
|
const [devices, points] = await Promise.all([getDevList(), getAlarmPoints()]);
|
||||||
getDevList(),
|
|
||||||
getAlarmPoints(),
|
|
||||||
getFactor(),
|
|
||||||
]);
|
|
||||||
dev_data.value.devList = devices;
|
dev_data.value.devList = devices;
|
||||||
dev_data.value.alarmPoints = points;
|
dev_data.value.alarmPoints = points;
|
||||||
dev_data.value.alarmFactors = factors;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@ -178,37 +159,22 @@ const openModal = (record) => {
|
|||||||
outliers_add_table_item.showModal();
|
outliers_add_table_item.showModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
const refreshMQTT = async () => {
|
|
||||||
const res = await postMQTTRefresh();
|
|
||||||
if (res.isSuccess) {
|
|
||||||
openToast("success", t("msg.mqtt_refresh"));
|
|
||||||
} else {
|
|
||||||
openToast("error", res.msg, "#outliers_add_table_item");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const remove = async (Id) => {
|
|
||||||
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
|
|
||||||
await cancelToastOpen();
|
|
||||||
const res = await delOutliersSetting(Id);
|
|
||||||
if (res.isSuccess) {
|
|
||||||
getOutliersData();
|
|
||||||
openToast("success", t("msg.delete_success"));
|
|
||||||
} else {
|
|
||||||
openToast("error", res.msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
editRecord.value = null;
|
editRecord.value = null;
|
||||||
outliers_add_table_item.close();
|
outliers_add_table_item.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openTimeModal = () => {
|
||||||
|
outliers_time_item.showModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTimeCancel = () => {
|
||||||
|
outliers_time_item.close();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex items-center justify-between mt-10">
|
<div class="flex justify-start items-center mt-10">
|
||||||
<div class="flex">
|
|
||||||
<h3 class="text-xl mr-5">{{ $t("alert.alarm_settings") }}</h3>
|
<h3 class="text-xl mr-5">{{ $t("alert.alarm_settings") }}</h3>
|
||||||
<AlertOutliersTableAddModal
|
<AlertOutliersTableAddModal
|
||||||
:openModal="openModal"
|
:openModal="openModal"
|
||||||
@ -217,10 +183,10 @@ const onCancel = () => {
|
|||||||
:getData="getOutliersData"
|
:getData="getOutliersData"
|
||||||
:OptionsData="dev_data"
|
:OptionsData="dev_data"
|
||||||
/>
|
/>
|
||||||
</div>
|
<AlertOutliersTimeModal
|
||||||
<button class="btn btn-sm btn-add" @click.prevent="refreshMQTT">
|
:openModal="openTimeModal"
|
||||||
<font-awesome-icon :icon="['fas', 'cog']" />{{ $t("alert.reorganization") }}
|
:onCancel="onTimeCancel"
|
||||||
</button>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Table :columns="columns" :dataSource="tableData" class="mt-3">
|
<Table :columns="columns" :dataSource="tableData" class="mt-3">
|
||||||
<template #bodyCell="{ record, column, index }">
|
<template #bodyCell="{ record, column, index }">
|
||||||
@ -231,12 +197,6 @@ const onCancel = () => {
|
|||||||
>
|
>
|
||||||
{{ $t("button.edit") }}
|
{{ $t("button.edit") }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
class="btn btn-sm btn-error text-white"
|
|
||||||
@click.stop.prevent="() => remove(record.id)"
|
|
||||||
>
|
|
||||||
{{ $t("button.delete") }}
|
|
||||||
</button>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="column.key === 'enable'">
|
<template v-else-if="column.key === 'enable'">
|
||||||
{{ record.enable === 1 ? t("alert.yes") : t("alert.no") }}
|
{{ record.enable === 1 ? t("alert.yes") : t("alert.no") }}
|
||||||
@ -244,9 +204,15 @@ const onCancel = () => {
|
|||||||
<template v-else-if="column.key === 'warning_method'">
|
<template v-else-if="column.key === 'warning_method'">
|
||||||
<span class="whitespace-pre">{{ record.warning_method }}</span>
|
<span class="whitespace-pre">{{ record.warning_method }}</span>
|
||||||
</template>
|
</template>
|
||||||
<!-- <template v-else-if="column.key === 'warning_time'">
|
<template v-else-if="column.key === 'warning_time'">
|
||||||
<span class="whitespace-pre">{{ record.warning_time }}</span>
|
<button
|
||||||
</template> -->
|
class="btn btn-sm btn-success text-white pb-3"
|
||||||
|
:disabled="!record.enable"
|
||||||
|
@click.stop.prevent="() => openTimeModal(record)"
|
||||||
|
>
|
||||||
|
{{ $t("alert.time_setting") }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ record[column.key] }}
|
{{ record[column.key] }}
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import * as yup from "yup";
|
|||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { openToast } = inject("app_toast");
|
const { openToast } = inject("app_toast");
|
||||||
const { timesList, noticeList } = inject("notify_table");
|
const { noticeList } = inject("notify_table");
|
||||||
const { searchParams, changeParams } = useSearchParam();
|
const { searchParams, changeParams } = useSearchParam();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -26,9 +26,8 @@ const formState = ref({
|
|||||||
device_name_tag: searchParams.value?.subSys_id,
|
device_name_tag: searchParams.value?.subSys_id,
|
||||||
points: "",
|
points: "",
|
||||||
enable: 0,
|
enable: 0,
|
||||||
factor: 1,
|
is_bool: 1,
|
||||||
alarm_value: "",
|
factor: null,
|
||||||
delay: 0,
|
|
||||||
highLimit: null,
|
highLimit: null,
|
||||||
lowLimit: null,
|
lowLimit: null,
|
||||||
highDelay: null,
|
highDelay: null,
|
||||||
@ -52,7 +51,8 @@ const { formErrorMsg, handleSubmit, handleErrorReset } = useFormErrorMessage(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const SaveCheckAuth = ref([]);
|
const SaveCheckAuth = ref([]);
|
||||||
const factorNum = ref(1);
|
const isBool = ref(1);
|
||||||
|
const factorData = ref([]);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.editRecord,
|
() => props.editRecord,
|
||||||
@ -61,19 +61,32 @@ watch(
|
|||||||
formState.value = {
|
formState.value = {
|
||||||
...newValue,
|
...newValue,
|
||||||
};
|
};
|
||||||
console.log('formState.value',formState.value);
|
|
||||||
|
|
||||||
SaveCheckAuth.value = newValue.notices ? [...newValue.notices] : [];
|
SaveCheckAuth.value = newValue.notices ? [...newValue.notices] : [];
|
||||||
|
|
||||||
if (newValue.factor) {
|
if (newValue.points) {
|
||||||
onFactorsChange(newValue.factor);
|
onPointsChange(newValue.points);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const onFactorsChange = (selectedFactor) => {
|
const onPointsChange = (selectedPoint) => {
|
||||||
factorNum.value = selectedFactor;
|
const pointData = props.OptionsData.alarmPoints.find(
|
||||||
|
(p) => p.points === selectedPoint
|
||||||
|
);
|
||||||
|
if (pointData) {
|
||||||
|
isBool.value = pointData.is_bool;
|
||||||
|
formState.value.is_bool = pointData.is_bool;
|
||||||
|
if (pointData.factor && Array.isArray(pointData.factor)) {
|
||||||
|
factorData.value = pointData.factor.map((d) => ({
|
||||||
|
...d,
|
||||||
|
key: d.id,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
factorData.value = [];
|
||||||
|
formState.value.factor = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onNoticesChange = (value, checked) => {
|
const onNoticesChange = (value, checked) => {
|
||||||
@ -108,7 +121,8 @@ const closeModal = () => {
|
|||||||
formState.value = {};
|
formState.value = {};
|
||||||
handleErrorReset();
|
handleErrorReset();
|
||||||
props.onCancel();
|
props.onCancel();
|
||||||
factorNum.value = 1;
|
factorData.value = [];
|
||||||
|
isBool.value = 1;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -119,8 +133,9 @@ const closeModal = () => {
|
|||||||
<Modal
|
<Modal
|
||||||
id="outliers_add_table_item"
|
id="outliers_add_table_item"
|
||||||
:title="t('alert.alarm_settings')"
|
:title="t('alert.alarm_settings')"
|
||||||
|
:open="open"
|
||||||
:onCancel="closeModal"
|
:onCancel="closeModal"
|
||||||
:width="710"
|
width="710"
|
||||||
>
|
>
|
||||||
<template #modalContent>
|
<template #modalContent>
|
||||||
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
||||||
@ -146,6 +161,7 @@ const closeModal = () => {
|
|||||||
name="points"
|
name="points"
|
||||||
Attribute="full_name"
|
Attribute="full_name"
|
||||||
:options="OptionsData.alarmPoints"
|
:options="OptionsData.alarmPoints"
|
||||||
|
:onChange="onPointsChange"
|
||||||
>
|
>
|
||||||
<template #topLeft>{{ $t("alert.item") }}</template>
|
<template #topLeft>{{ $t("alert.item") }}</template>
|
||||||
<template #bottomLeft
|
<template #bottomLeft
|
||||||
@ -154,17 +170,6 @@ const closeModal = () => {
|
|||||||
</span></template
|
</span></template
|
||||||
>
|
>
|
||||||
</Select>
|
</Select>
|
||||||
<Select
|
|
||||||
:value="formState"
|
|
||||||
class="my-2"
|
|
||||||
selectClass="border-info focus-within:border-info"
|
|
||||||
name="factor"
|
|
||||||
Attribute="full_name"
|
|
||||||
:options="OptionsData.alarmFactors"
|
|
||||||
:onChange="onFactorsChange"
|
|
||||||
>
|
|
||||||
<template #topLeft>{{ $t("alert.qualifications") }}</template>
|
|
||||||
</Select>
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
class="my-2"
|
class="my-2"
|
||||||
name="enable"
|
name="enable"
|
||||||
@ -185,60 +190,49 @@ const closeModal = () => {
|
|||||||
>
|
>
|
||||||
<template #topLeft>{{ $t("alert.status") }}</template>
|
<template #topLeft>{{ $t("alert.status") }}</template>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
<template v-if="factorNum == 1">
|
<Select
|
||||||
<InputNumber :value="formState" class="my-2" name="delay">
|
|
||||||
<template #topLeft>{{ $t("alert.delay") }}</template>
|
|
||||||
</InputNumber>
|
|
||||||
</template>
|
|
||||||
<template v-if="factorNum == 2">
|
|
||||||
<div class="flex gap-4 w-full">
|
|
||||||
<InputNumber :value="formState" class="my-2" name="highLimit">
|
|
||||||
<template #topLeft>{{ $t("alert.upper_limit") }}(>=)</template>
|
|
||||||
</InputNumber>
|
|
||||||
<InputNumber :value="formState" class="my-2" name="lowLimit">
|
|
||||||
<template #topLeft>{{ $t("alert.lower_limit") }}(<=)</template>
|
|
||||||
</InputNumber>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-4 w-full">
|
|
||||||
<InputNumber :value="formState" class="my-2" name="highDelay">
|
|
||||||
<template #topLeft>{{ $t("alert.highDelay") }}</template>
|
|
||||||
</InputNumber>
|
|
||||||
<InputNumber :value="formState" class="my-2" name="lowDelay">
|
|
||||||
<template #topLeft>{{ $t("alert.lowDelay") }}</template>
|
|
||||||
</InputNumber>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-if="factorNum == 3">
|
|
||||||
<Input :value="formState" class="my-2" name="alarm_value">
|
|
||||||
<template #topLeft>{{ $t("alert.warning_value") }}</template>
|
|
||||||
</Input>
|
|
||||||
</template>
|
|
||||||
<!-- <Select
|
|
||||||
:value="formState"
|
:value="formState"
|
||||||
class="my-2"
|
class="my-2"
|
||||||
selectClass="border-info focus-within:border-info"
|
selectClass="border-info focus-within:border-info"
|
||||||
name="schedule_id"
|
name="factor"
|
||||||
Attribute="schedule_name"
|
Attribute="full_name"
|
||||||
:options="timesList"
|
:options="factorData"
|
||||||
|
v-if="factorData.length !== 0"
|
||||||
>
|
>
|
||||||
<template #topLeft>{{ $t("alert.warning_time") }}</template>
|
<template #topLeft>{{ $t("alert.qualifications") }}</template>
|
||||||
<template #topRight
|
</Select>
|
||||||
><button
|
<InputNumber
|
||||||
v-if="formState.schedule_id"
|
:value="formState"
|
||||||
class="text-base btn-text-without-border"
|
class="my-2"
|
||||||
@click="
|
name="highLimit"
|
||||||
() => {
|
v-if="!isBool"
|
||||||
formState.schedule_id = null;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<font-awesome-icon
|
<template #topLeft>{{ $t("alert.upper_limit") }}(>=)</template>
|
||||||
:icon="['fas', 'times']"
|
</InputNumber>
|
||||||
class="text-[#a5abb1] me-1"
|
<InputNumber
|
||||||
/>{{ $t("alert.clear") }}
|
:value="formState"
|
||||||
</button>
|
class="my-2"
|
||||||
</template>
|
name="lowLimit"
|
||||||
</Select> -->
|
v-if="!isBool"
|
||||||
|
>
|
||||||
|
<template #topLeft>{{ $t("alert.lower_limit") }}(<=)</template>
|
||||||
|
</InputNumber>
|
||||||
|
<InputNumber
|
||||||
|
:value="formState"
|
||||||
|
class="my-2"
|
||||||
|
name="highDelay"
|
||||||
|
v-if="!isBool"
|
||||||
|
>
|
||||||
|
<template #topLeft>{{ $t("alert.highDelay") }}</template>
|
||||||
|
</InputNumber>
|
||||||
|
<InputNumber
|
||||||
|
:value="formState"
|
||||||
|
class="my-2"
|
||||||
|
name="lowDelay"
|
||||||
|
v-if="!isBool"
|
||||||
|
>
|
||||||
|
<template #topLeft>{{ $t("alert.lowDelay") }}</template>
|
||||||
|
</InputNumber>
|
||||||
<div class="w-full mt-5">
|
<div class="w-full mt-5">
|
||||||
<p class="text-light text-lg ml-1">
|
<p class="text-light text-lg ml-1">
|
||||||
{{ $t("alert.warning_method") }}
|
{{ $t("alert.warning_method") }}
|
||||||
|
|||||||
@ -0,0 +1,82 @@
|
|||||||
|
<script setup>
|
||||||
|
import { inject, defineProps, watch, ref, provide } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
openModal: Function,
|
||||||
|
onCancel: Function,
|
||||||
|
});
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
props.onCancel();
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "排程名稱",
|
||||||
|
key: "schedule_name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "時段",
|
||||||
|
key: "schedule_time",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "狀態",
|
||||||
|
key: "enable",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "功能",
|
||||||
|
key: "operation",
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Modal
|
||||||
|
id="outliers_time_item"
|
||||||
|
:open="open"
|
||||||
|
:onCancel="closeModal"
|
||||||
|
width="800"
|
||||||
|
>
|
||||||
|
<template #modalTitle>
|
||||||
|
<p>警示時間設定</p>
|
||||||
|
<button class="fixed right-10 top-5" @click.prevent="closeModal">
|
||||||
|
<font-awesome-icon
|
||||||
|
:icon="['fas', 'times']"
|
||||||
|
size="1x"
|
||||||
|
class="text-[#a5abb1]"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<template #modalContent>
|
||||||
|
<Table
|
||||||
|
:columns="columns"
|
||||||
|
:dataSource="tableData"
|
||||||
|
class="mt-3"
|
||||||
|
:withStyle="false"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ record, column, index }">
|
||||||
|
<template v-if="column.key === 'operation'">
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-success text-white mr-2"
|
||||||
|
@click.stop.prevent="() => openModal(record)"
|
||||||
|
>
|
||||||
|
修改
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-error text-white"
|
||||||
|
@click.stop.prevent="() => remove(record.userinfo_guid)"
|
||||||
|
>
|
||||||
|
刪除
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ record[column.key] }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</Table>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@ -1,26 +1,13 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import AlertSubList from "./AlertSubList.vue";
|
import AlertSubList from "./AlertSubList.vue";
|
||||||
import AlertOutliersTable from "./AlertOutliersTable.vue";
|
import AlertOutliersTable from "./AlertOutliersTable.vue";
|
||||||
import AlertTimeTable from "./AlertTimeTable.vue";
|
|
||||||
import AlertNotifyTable from "./AlertNotifyTable.vue";
|
import AlertNotifyTable from "./AlertNotifyTable.vue";
|
||||||
import { ref, provide, onMounted, watch } from "vue";
|
import { ref, provide, onMounted, watch } from "vue";
|
||||||
import { getAlarmScheduleList, getNoticeList } from "@/apis/alert";
|
import { getNoticeList } from "@/apis/alert";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
const { locale } = useI18n();
|
const { locale } = useI18n();
|
||||||
const timesList = ref([]);
|
|
||||||
const noticeList = ref([]);
|
|
||||||
|
|
||||||
// const timesListData = async () => {
|
const noticeList = ref([]);
|
||||||
// const res = await getAlarmScheduleList();
|
|
||||||
// timesList.value = res.data.map((items) => ({
|
|
||||||
// ...items,
|
|
||||||
// key:items.id,
|
|
||||||
// schedule_array: JSON.parse(items.schedule_json).map((time, index) => ({
|
|
||||||
// day: index + 1,
|
|
||||||
// time,
|
|
||||||
// })),
|
|
||||||
// }));
|
|
||||||
// };
|
|
||||||
|
|
||||||
const NoticeListData = async () => {
|
const NoticeListData = async () => {
|
||||||
const res = await getNoticeList(locale.value);
|
const res = await getNoticeList(locale.value);
|
||||||
@ -32,22 +19,16 @@ watch(locale, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// timesListData();
|
|
||||||
NoticeListData();
|
NoticeListData();
|
||||||
});
|
});
|
||||||
|
|
||||||
provide("notify_table", {
|
provide("notify_table", { noticeList });
|
||||||
timesList,
|
|
||||||
noticeList,
|
|
||||||
// timesListData
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<AlertSubList />
|
<AlertSubList />
|
||||||
<AlertOutliersTable />
|
<AlertOutliersTable />
|
||||||
<!-- <AlertTimeTable /> -->
|
|
||||||
<AlertNotifyTable />
|
<AlertNotifyTable />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -3,37 +3,27 @@ import { ref, onMounted, watch } from "vue";
|
|||||||
import useSearchParam from "@/hooks/useSearchParam";
|
import useSearchParam from "@/hooks/useSearchParam";
|
||||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||||
import { getAlertSubList } from "@/apis/alert";
|
import { getAlertSubList } from "@/apis/alert";
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
|
||||||
const store = useBuildingStore();
|
|
||||||
const { searchParams, changeParams } = useSearchParam();
|
const { searchParams, changeParams } = useSearchParam();
|
||||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
||||||
|
|
||||||
const getSubSystems = async () => {
|
const getSubSystems = async () => {
|
||||||
const res = await getAlertSubList(store.selectedBuilding.building_guid);
|
const res = await getAlertSubList();
|
||||||
const history_Sub_systems = res.data.history_Main_Systems.flatMap(
|
const subSystems = res.data.history_Main_Systems.flatMap((mainSystem) => {
|
||||||
(mainSystem) => {
|
return mainSystem.history_Sub_systems.map((subSystem, index) => ({
|
||||||
return mainSystem.history_Sub_systems;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const subSystems = history_Sub_systems.map((subSystem, index) => ({
|
|
||||||
title: subSystem.full_name,
|
title: subSystem.full_name,
|
||||||
key: subSystem.sub_system_tag,
|
key: subSystem.sub_system_tag,
|
||||||
active: searchParams.value?.subSys_id
|
active: searchParams.value?.subSys_id
|
||||||
? searchParams.value.subSys_id === subSystem.sub_system_tag
|
? searchParams.value.subSys_id === subSystem.sub_system_tag
|
||||||
: index == 0,
|
: subSystem.sub_system_tag == "DP",
|
||||||
}));
|
}));
|
||||||
|
});
|
||||||
setItems(subSystems);
|
setItems(subSystems);
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
onMounted(() => {
|
||||||
() => store.selectedBuilding,
|
|
||||||
(newBuilding) => {
|
|
||||||
if (newBuilding) {
|
|
||||||
getSubSystems();
|
getSubSystems();
|
||||||
}
|
});
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(selectedBtn,
|
watch(selectedBtn,
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
|
|||||||
@ -1,110 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { ref, inject, onMounted } from "vue";
|
|
||||||
import { deleteAlarmSchedule } from "@/apis/alert";
|
|
||||||
import AlertTimeTableAddModal from "./AlertTimeTableAddModal.vue";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
const { t } = useI18n();
|
|
||||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
|
||||||
const tableData = ref([]);
|
|
||||||
const editRecord = ref(null);
|
|
||||||
const { timesList,timesListData } = inject("notify_table");
|
|
||||||
const weekDate = ref({
|
|
||||||
1: t("alert.sunday"),
|
|
||||||
2: t("alert.monday"),
|
|
||||||
3: t("alert.tuesday"),
|
|
||||||
4: t("alert.wednesday"),
|
|
||||||
5: t("alert.thursday"),
|
|
||||||
6: t("alert.friday"),
|
|
||||||
7: t("alert.saturday"),
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
timesListData();
|
|
||||||
});
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: t("alert.schedule_name"),
|
|
||||||
key: "schedule_name",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("alert.schedule_content"),
|
|
||||||
key: "schedule_content",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("alert.operation"),
|
|
||||||
key: "operation",
|
|
||||||
width: 200,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const openModal = (record) => {
|
|
||||||
if (record) {
|
|
||||||
editRecord.value = record;
|
|
||||||
} else {
|
|
||||||
editRecord.value = null;
|
|
||||||
}
|
|
||||||
outliers_time_item.showModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCancel = () => {
|
|
||||||
editRecord.value = null;
|
|
||||||
outliers_time_item.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
const remove = async (id) => {
|
|
||||||
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
|
|
||||||
await cancelToastOpen();
|
|
||||||
const res = await deleteAlarmSchedule(id);
|
|
||||||
if (res.isSuccess) {
|
|
||||||
timesListData();
|
|
||||||
openToast("success", t("msg.delete_success"));
|
|
||||||
} else {
|
|
||||||
openToast("error", res.msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="flex justify-start items-center mt-10">
|
|
||||||
<h3 class="text-xl mr-5">{{$t("alert.warning_time")}}</h3>
|
|
||||||
<AlertTimeTableAddModal :openModal="openModal" :onCancel="onCancel" :editRecord="editRecord" :weekDate="weekDate" />
|
|
||||||
</div>
|
|
||||||
<Table :columns="columns" :dataSource="timesList" class="w-3/4 mt-3">
|
|
||||||
<template #bodyCell="{ record, column, index }">
|
|
||||||
<template v-if="column.key === 'schedule_content'">
|
|
||||||
<ul class="text-left">
|
|
||||||
<li v-for="(item, index) in record.schedule_array" :key="index">
|
|
||||||
<strong v-if="item.time.length">
|
|
||||||
<span>{{ weekDate[item.day] }}: </span>
|
|
||||||
<span v-for="(time, timeIndex) in item.time" :key="timeIndex">
|
|
||||||
{{ time }}
|
|
||||||
<span v-if="timeIndex < item.time.length - 1">, </span>
|
|
||||||
</span>
|
|
||||||
</strong>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
<template v-else-if="column.key === 'operation'">
|
|
||||||
<button
|
|
||||||
class="btn btn-sm btn-success text-white mr-2"
|
|
||||||
@click.stop.prevent="() => openModal(record)"
|
|
||||||
>
|
|
||||||
{{$("button.edit")}}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-sm btn-error text-white"
|
|
||||||
@click.stop.prevent="() => remove(record.id)"
|
|
||||||
>
|
|
||||||
{{$("button.delete")}}
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<pre>{{ record[column.key] }}</pre>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</Table>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
||||||
@ -1,429 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { defineProps, onMounted, watch, ref, reactive, inject } from "vue";
|
|
||||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
|
||||||
import { postAlertSchedule } from "@/apis/alert";
|
|
||||||
import * as yup from "yup";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const { openToast } = inject("app_toast");
|
|
||||||
const { timesListData } = inject("notify_table");
|
|
||||||
const props = defineProps({
|
|
||||||
openModal: Function,
|
|
||||||
onCancel: Function,
|
|
||||||
editRecord: Object,
|
|
||||||
weekDate: Object,
|
|
||||||
});
|
|
||||||
|
|
||||||
let scheme = yup.object({
|
|
||||||
schedule_name: yup.string().required(t("button.required")),
|
|
||||||
});
|
|
||||||
|
|
||||||
const form = ref(null);
|
|
||||||
const schedule_array = ref([]);
|
|
||||||
const formState = ref({
|
|
||||||
id: 0,
|
|
||||||
schedule_name: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
const { formErrorMsg, handleSubmit, handleErrorReset } = useFormErrorMessage(
|
|
||||||
scheme.value
|
|
||||||
);
|
|
||||||
|
|
||||||
const closeModal = () => {
|
|
||||||
props.onCancel();
|
|
||||||
formState.value = {
|
|
||||||
id: 0,
|
|
||||||
schedule_name: "",
|
|
||||||
};
|
|
||||||
clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
const tableHeader = ref([
|
|
||||||
"00",
|
|
||||||
"01",
|
|
||||||
"02",
|
|
||||||
"03",
|
|
||||||
"04",
|
|
||||||
"05",
|
|
||||||
"06",
|
|
||||||
"07",
|
|
||||||
"08",
|
|
||||||
"09",
|
|
||||||
"10",
|
|
||||||
"11",
|
|
||||||
"12",
|
|
||||||
"13",
|
|
||||||
"14",
|
|
||||||
"15",
|
|
||||||
"16",
|
|
||||||
"17",
|
|
||||||
"18",
|
|
||||||
"19",
|
|
||||||
"20",
|
|
||||||
"21",
|
|
||||||
"22",
|
|
||||||
"23",
|
|
||||||
]);
|
|
||||||
|
|
||||||
const rowUnit = ref([]); //每一單位格子
|
|
||||||
const timeContent = ref([]); //選中的時間段原始數據
|
|
||||||
const timeSection = ref([]); //時間段,可以返回給後端
|
|
||||||
const timeStr = ref([]); //時間段,前端顯示的數據
|
|
||||||
let beginDay = ref(0);
|
|
||||||
let beginTime = ref(0);
|
|
||||||
let downEvent = ref(false);
|
|
||||||
|
|
||||||
// 其他變數保持不變
|
|
||||||
const hoverRange = reactive({
|
|
||||||
startDay: 0,
|
|
||||||
endDay: 0,
|
|
||||||
startSlot: 0,
|
|
||||||
endSlot: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleMouseMove = (i, day) => {
|
|
||||||
if (!downEvent.value) return;
|
|
||||||
hoverRange.startDay = Math.min(beginDay.value, day);
|
|
||||||
hoverRange.endDay = Math.max(beginDay.value, day);
|
|
||||||
hoverRange.startSlot = Math.min(beginTime.value, i);
|
|
||||||
hoverRange.endSlot = Math.max(beginTime.value, i);
|
|
||||||
};
|
|
||||||
|
|
||||||
const initSchedule = () => {
|
|
||||||
for (let i = 0; i < 7; i++) {
|
|
||||||
const dayIndex = i;
|
|
||||||
const defaultTimes = schedule_array.value[dayIndex] || [];
|
|
||||||
let arr = [];
|
|
||||||
for (let j = 0; j < 96; j++) {
|
|
||||||
const timeSlot = j / 4;
|
|
||||||
const isDefault = defaultTimes.some((timeRange) => {
|
|
||||||
const [start, end] = timeRange.split("~").map((t) => {
|
|
||||||
const [hours, minutes] = t.split(":").map(Number);
|
|
||||||
return hours + minutes / 60;
|
|
||||||
});
|
|
||||||
return timeSlot >= start && timeSlot < end;
|
|
||||||
});
|
|
||||||
|
|
||||||
arr.push({ class: isDefault ? "ui-selected" : null, timeData: j });
|
|
||||||
}
|
|
||||||
rowUnit.value.push(arr);
|
|
||||||
timeContent.value.push({
|
|
||||||
arr: arr
|
|
||||||
.filter((item) => item.class === "ui-selected")
|
|
||||||
.map((item) => item.timeData),
|
|
||||||
});
|
|
||||||
timeSection.value.push(defaultTimes);
|
|
||||||
timeStr.value.push(defaultTimes.join(", "));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseDown = (i, day) => {
|
|
||||||
downEvent.value = true; //滑鼠不在範圍內的不算
|
|
||||||
beginDay.value = day;
|
|
||||||
beginTime.value = i;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseUp = (i, day) => {
|
|
||||||
let begin = beginTime.value;
|
|
||||||
let start = begin <= i ? begin : i; //x軸起點
|
|
||||||
let length = Math.abs(begin - i);
|
|
||||||
let end = start + length; //x軸終點
|
|
||||||
let dayStart = beginDay.value <= day ? beginDay.value : day; //y軸起點
|
|
||||||
let dayLength = Math.abs(beginDay.value - day);
|
|
||||||
let dayEnd = dayStart + dayLength; //y軸終點
|
|
||||||
|
|
||||||
const isAdd = () => {
|
|
||||||
//當選取範圍內所有的都是選取狀態時,則執行取消選取
|
|
||||||
for (let x = dayStart; x < dayEnd + 1; x++) {
|
|
||||||
for (let y = start; y < end + 1; y++) {
|
|
||||||
if (rowUnit.value[x][y].class === null) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (downEvent.value) {
|
|
||||||
//當點擊事件是在table内才觸發選取操作
|
|
||||||
if (isAdd()) {
|
|
||||||
for (let x = dayStart; x < dayEnd + 1; x++) {
|
|
||||||
for (let y = start; y < end + 1; y++) {
|
|
||||||
if (rowUnit.value[x][y].class === null) {
|
|
||||||
rowUnit.value[x][y].class = "ui-selected";
|
|
||||||
timeContent.value[x].arr.push(rowUnit.value[x][y].timeData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (let x = dayStart; x < dayEnd + 1; x++) {
|
|
||||||
for (let y = start; y < end + 1; y++) {
|
|
||||||
if (rowUnit.value[x][y].class === "ui-selected") {
|
|
||||||
rowUnit.value[x][y].class = null;
|
|
||||||
let c = rowUnit.value[x][y].timeData;
|
|
||||||
let kong = "";
|
|
||||||
for (let i = 0; i < timeContent.value[x].arr.length; i++) {
|
|
||||||
if (c === timeContent.value[x].arr[i]) {
|
|
||||||
kong = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
timeContent.value[x].arr.splice(kong, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//過濾時間段,將鄰近的時間段合併
|
|
||||||
filterTime(dayStart, dayEnd);
|
|
||||||
}
|
|
||||||
downEvent.value = false;
|
|
||||||
|
|
||||||
hoverRange.startDay = hoverRange.endDay = 0;
|
|
||||||
hoverRange.startSlot = hoverRange.endSlot = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const filterTime = (start, end) => {
|
|
||||||
const toStr = (num) => {
|
|
||||||
if (Number.isInteger(num)) {
|
|
||||||
return `${num < 10 ? "0" + num : num}:00`;
|
|
||||||
} else {
|
|
||||||
const intPart = Math.floor(num);
|
|
||||||
const fracPart = num - intPart;
|
|
||||||
const str = intPart < 10 ? "0" + intPart : intPart.toString();
|
|
||||||
return `${str}:${
|
|
||||||
fracPart === 0.25 ? "15" : fracPart === 0.5 ? "30" : "45"
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const timeToStr = (arr) => {
|
|
||||||
return arr.map(([start, end]) => `${toStr(start)}~${toStr(end)}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let i = start; i <= end; i++) {
|
|
||||||
const sortedArr = [...new Set(timeContent.value[i].arr)].sort(
|
|
||||||
(a, b) => a - b
|
|
||||||
);
|
|
||||||
const sections = [];
|
|
||||||
for (let j = 0; j < sortedArr.length; j++) {
|
|
||||||
if (j === 0 || sortedArr[j] !== sortedArr[j - 1] + 1) {
|
|
||||||
sections.push([sortedArr[j], sortedArr[j]]);
|
|
||||||
} else {
|
|
||||||
sections[sections.length - 1][1] = sortedArr[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
timeSection.value[i] = timeToStr(
|
|
||||||
sections.map(([start, end]) => [start / 4, (end + 1) / 4])
|
|
||||||
);
|
|
||||||
timeStr.value[i] = timeSection.value[i].join(", ");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const clear = () => {
|
|
||||||
schedule_array.value = [];
|
|
||||||
rowUnit.value = [];
|
|
||||||
timeContent.value = [];
|
|
||||||
timeSection.value = [];
|
|
||||||
timeStr.value = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
const onOk = async () => {
|
|
||||||
const values = await handleSubmit(scheme, formState.value);
|
|
||||||
|
|
||||||
const res = await postAlertSchedule({
|
|
||||||
...values,
|
|
||||||
schedule_json: timeSection.value ? JSON.stringify(timeSection.value) : "",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.isSuccess) {
|
|
||||||
timesListData();
|
|
||||||
closeModal();
|
|
||||||
} else {
|
|
||||||
openToast("error", res.msg, "#outliers_time_item");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.editRecord,
|
|
||||||
(newValue) => {
|
|
||||||
if (newValue) {
|
|
||||||
formState.value = {
|
|
||||||
...newValue,
|
|
||||||
};
|
|
||||||
schedule_array.value = newValue?.schedule_json
|
|
||||||
? JSON.parse(newValue?.schedule_json)
|
|
||||||
: [];
|
|
||||||
initSchedule();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<button class="btn btn-sm btn-add mr-3" @click.stop.prevent="openModal">
|
|
||||||
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
|
||||||
</button>
|
|
||||||
<Modal
|
|
||||||
id="outliers_time_item"
|
|
||||||
:open="open"
|
|
||||||
:onCancel="closeModal"
|
|
||||||
width="1400"
|
|
||||||
:title="t('alert.warning_time')"
|
|
||||||
>
|
|
||||||
<template #modalContent>
|
|
||||||
<form ref="form">
|
|
||||||
<Input :value="formState" class="mt-2" name="schedule_name">
|
|
||||||
<template #topLeft>{{ $t("alert.schedule_name") }}</template>
|
|
||||||
<template #bottomLeft>
|
|
||||||
<span class="text-error text-base">
|
|
||||||
{{ formErrorMsg.schedule_name }}
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</Input>
|
|
||||||
<p class="text-light text-lg my-2">{{ $t("alert.schedule_content") }}</p>
|
|
||||||
<div class="weektime mt-5">
|
|
||||||
<div class="calendar">
|
|
||||||
<table class="calendar-table w-full">
|
|
||||||
<thead class="calendar-head">
|
|
||||||
<tr>
|
|
||||||
<th rowspan="6" class="w-20 py-4">
|
|
||||||
{{ $t("alert.day_time") }}
|
|
||||||
</th>
|
|
||||||
<th colspan="48">00:00 - 12:00</th>
|
|
||||||
<th colspan="48">12:00 - 24:00</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
colspan="4"
|
|
||||||
v-for="(item, index) in tableHeader"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
{{ item }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="tableBody">
|
|
||||||
<tr v-for="(day, index) in weekDate" :key="index">
|
|
||||||
<td>{{ day }}</td>
|
|
||||||
<td
|
|
||||||
v-for="(item, i) in rowUnit[index - 1]"
|
|
||||||
:key="i"
|
|
||||||
@mousedown.prevent="handleMouseDown(i, index - 1)"
|
|
||||||
@mouseup.prevent="handleMouseUp(i, index - 1)"
|
|
||||||
@mousemove.prevent="handleMouseMove(i, index - 1)"
|
|
||||||
class="calendar-atom-time"
|
|
||||||
:class="{
|
|
||||||
'ui-selected': item.class === 'ui-selected',
|
|
||||||
'hover-highlight':
|
|
||||||
downEvent &&
|
|
||||||
index - 1 >= hoverRange.startDay &&
|
|
||||||
index - 1 <= hoverRange.endDay &&
|
|
||||||
i >= hoverRange.startSlot &&
|
|
||||||
i <= hoverRange.endSlot,
|
|
||||||
}"
|
|
||||||
></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="97" class="py-2">
|
|
||||||
<span class="text-base me-2">{{
|
|
||||||
$t("alert.click_time_period")
|
|
||||||
}}</span>
|
|
||||||
<a
|
|
||||||
@click="
|
|
||||||
() => {
|
|
||||||
clear();
|
|
||||||
initSchedule();
|
|
||||||
}
|
|
||||||
"
|
|
||||||
class="cursor-pointer text-active text-base"
|
|
||||||
>
|
|
||||||
{{ $t("alert.clear") }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="97" class="timeContent">
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in timeStr"
|
|
||||||
:key="index"
|
|
||||||
v-show="item.length"
|
|
||||||
>
|
|
||||||
<span>{{ weekDate[index + 1] }}: </span>
|
|
||||||
<strong>
|
|
||||||
<span>{{ item }}</span>
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</template>
|
|
||||||
<template #modalAction>
|
|
||||||
<button
|
|
||||||
type="reset"
|
|
||||||
class="btn btn-outline-success mr-2"
|
|
||||||
@click.prevent="closeModal"
|
|
||||||
>
|
|
||||||
{{ $t("button.cancel") }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="btn btn-outline-success"
|
|
||||||
@click.prevent="onOk"
|
|
||||||
>
|
|
||||||
{{ $t("button.submit") }}
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
</Modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
.weektime .calendar {
|
|
||||||
-webkit-user-select: none;
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weektime .calendar .calendar-table tr .calendar-atom-time:hover {
|
|
||||||
background: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weektime .calendar .calendar-table tr .ui-selected {
|
|
||||||
background: #00ffb3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weektime .calendar .calendar-table tr .ui-selected:hover {
|
|
||||||
background: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weektime .calendar .calendar-table tr,
|
|
||||||
.weektime .calendar .calendar-table td,
|
|
||||||
.weektime .calendar .calendar-table th {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
font-size: 13px;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 1.8em;
|
|
||||||
transition: background 200ms ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weektime .calendar .calendar-table tbody tr {
|
|
||||||
height: 35px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weektime .calendar .calendar-table tbody tr td:first-child {
|
|
||||||
background: #123;
|
|
||||||
}
|
|
||||||
|
|
||||||
.weektime .calendar .calendar-table thead th,
|
|
||||||
.weektime .calendar .calendar-table thead td {
|
|
||||||
background: #123;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hover-highlight {
|
|
||||||
background: rgba(0, 255, 145, 0.5); /* 淡藍色透明背景 */
|
|
||||||
border: 1px dashed #00ffb3; /* 藍色虛線邊框 */
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,188 +1,75 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, onUnmounted, ref, provide, watch } from "vue";
|
|
||||||
import { getEnergyCost } from "@/apis/dashboard";
|
|
||||||
import ButtonConnectedGroup from "@/components/customUI/ButtonConnectedGroup.vue";
|
|
||||||
import Forge from "@/components/forge/Forge.vue";
|
import Forge from "@/components/forge/Forge.vue";
|
||||||
import DashboardStat from "./components/DashboardStat.vue";
|
import DashboardStat from "./components/DashboardStat.vue";
|
||||||
|
import DashboardElecChart from "./components/DashboardElecChart.vue";
|
||||||
import DashboardSysCard from "./components/DashboardSysCard.vue";
|
import DashboardSysCard from "./components/DashboardSysCard.vue";
|
||||||
import DashboardSysProgress from "./components/DashboardSysProgress.vue";
|
import DashboardSysProgress from "./components/DashboardSysProgress.vue";
|
||||||
import DashboardElecRank from "./components/DashboardElecRank.vue";
|
import { getDashboardInit } from "@/apis/dashboard";
|
||||||
import DashboardElecTrends from "./components/DashboardElecTrends.vue";
|
import { onMounted, ref, provide, watch } from "vue";
|
||||||
import DashboardElecCompare from "./components/DashboardElecCompare.vue";
|
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
|
||||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
|
|
||||||
const store = useBuildingStore();
|
const initialData = ref(null);
|
||||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
// const forgeData = ref([]);
|
||||||
let intervalId = null;
|
const init = async () => {
|
||||||
const energyCostData = ref({});
|
const res = await getDashboardInit();
|
||||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
initialData.value = res.data;
|
||||||
const imgBaseUrl = ref("");
|
|
||||||
const formState = ref({
|
|
||||||
building_guid: null,
|
|
||||||
floor_guid: "all",
|
|
||||||
department_id: "all",
|
|
||||||
});
|
|
||||||
|
|
||||||
const getEnergyCostData = async (params) => {
|
|
||||||
const res = await getEnergyCost(params);
|
|
||||||
energyCostData.value = res.data;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
onMounted(() => {
|
||||||
() => store.selectedBuilding,
|
init();
|
||||||
(newBuilding) => {
|
});
|
||||||
if (newBuilding) {
|
|
||||||
formState.value.building_guid = newBuilding.building_guid;
|
|
||||||
imgBaseUrl.value = store.previewImageExt
|
|
||||||
? `${FILE_BASEURL}/upload/setting/previewImage/${newBuilding.building_guid}${store.previewImageExt}`
|
|
||||||
: import.meta.env.MODE === "production"
|
|
||||||
? "dist/build_img.jpg"
|
|
||||||
: "/build_img.jpg";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true, deep: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
const intervalOption = ref({});
|
||||||
() => formState.value,
|
const currentIntervalType = ref("");
|
||||||
(newVal) => {
|
|
||||||
if (newVal) {
|
|
||||||
const params = { ...newVal };
|
|
||||||
|
|
||||||
if (params.floor_guid === "all") {
|
const openModal = (type) => {
|
||||||
delete params.floor_guid;
|
currentIntervalType.value = type;
|
||||||
}
|
dashboard_more.showModal();
|
||||||
if (params.department_id === "all") {
|
};
|
||||||
delete params.department_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.building_guid) {
|
const decideIntervalOption = (option) => {
|
||||||
getEnergyCostData(params);
|
intervalOption.value[currentIntervalType.value] = option;
|
||||||
}
|
};
|
||||||
|
|
||||||
if (intervalId) {
|
provide("dashboard_items", {
|
||||||
clearInterval(intervalId);
|
initialData,
|
||||||
}
|
// forgeData,
|
||||||
intervalId = setInterval(() => {
|
openModal,
|
||||||
getEnergyCostData(params);
|
decideIntervalOption,
|
||||||
}, 3600000);
|
intervalOption,
|
||||||
}
|
currentIntervalType,
|
||||||
},
|
|
||||||
{ immediate: true, deep: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => store.showForgeArea,
|
|
||||||
(newVal) => {
|
|
||||||
if (newVal == true) {
|
|
||||||
setItems([
|
|
||||||
{
|
|
||||||
title: "2D",
|
|
||||||
key: "2D",
|
|
||||||
active: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "3D",
|
|
||||||
key: "3D",
|
|
||||||
active: true,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<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">
|
||||||
<template v-if="store.showForgeArea">
|
<div class="area-img-box">
|
||||||
<ButtonConnectedGroup
|
<Forge />
|
||||||
:items="items"
|
|
||||||
className="btn-xs absolute right-3 top-6 z-20 bg-slate-800 p-0 rounded-lg "
|
|
||||||
:onclick="(e, item) => changeActiveBtn(item)"
|
|
||||||
/>
|
|
||||||
<div class="area-img-box relative">
|
|
||||||
<!-- setting頁面要新增讓他能上傳圖片 -->
|
|
||||||
<img
|
|
||||||
alt="build"
|
|
||||||
:src="imgBaseUrl"
|
|
||||||
:class="
|
|
||||||
twMerge(
|
|
||||||
'absolute w-full h-full transition-opacity duration-300',
|
|
||||||
selectedBtn?.key == '2D'
|
|
||||||
? 'opacity-100 z-10'
|
|
||||||
: 'opacity-0 z-0'
|
|
||||||
)
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Forge
|
|
||||||
:class="
|
|
||||||
twMerge(
|
|
||||||
'absolute transition-opacity duration-300',
|
|
||||||
selectedBtn?.key == '3D'
|
|
||||||
? 'opacity-100 z-10'
|
|
||||||
: 'opacity-0 z-0'
|
|
||||||
)
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<img
|
|
||||||
alt="build"
|
|
||||||
:src="imgBaseUrl || '/build_img.jpg'"
|
|
||||||
class="area-img-box w-full h-[460px] block relative rounded-sm mt-3"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full xl:w-2/3">
|
<div class="w-full xl:w-2/3">
|
||||||
<!-- 用電數據 -->
|
<!-- 用電數據 -->
|
||||||
<DashboardStat />
|
<DashboardStat />
|
||||||
|
<!-- 用電圖表 -->
|
||||||
<div class="flex flex-wrap pt-4">
|
<DashboardElecChart />
|
||||||
<!-- 當月能耗排行 -->
|
|
||||||
<div class="lg:w-1/3 w-full">
|
|
||||||
<DashboardElecRank :energyCostData="energyCostData" />
|
|
||||||
</div>
|
|
||||||
<!-- 近30天能耗趨勢 -->
|
|
||||||
<div class="lg:w-2/3 w-full">
|
|
||||||
<DashboardElecTrends
|
|
||||||
:formState="formState"
|
|
||||||
:energyCostData="energyCostData"
|
|
||||||
:getEnergyCostData="getEnergyCostData"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 設備小卡 -->
|
<!-- 設備小卡 -->
|
||||||
<div class="w-full lg:w-1/3">
|
<div class="w-full lg:w-2/3">
|
||||||
<DashboardSysCard />
|
<DashboardSysCard />
|
||||||
</div>
|
</div>
|
||||||
<!--狀態、進度-->
|
<!--狀態、進度-->
|
||||||
<div class="w-full lg:w-1/3">
|
<div class="w-full lg:w-1/3">
|
||||||
<DashboardSysProgress />
|
<DashboardSysProgress />
|
||||||
</div>
|
</div>
|
||||||
<!-- 環比能耗 -->
|
|
||||||
<div class="w-full lg:w-1/3">
|
|
||||||
<DashboardElecCompare :energyCostData="energyCostData" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
.area-img-box {
|
.area-img-box {
|
||||||
@apply border border-light-info bg-dark-info w-full h-[460px] block relative rounded-sm mt-3;
|
@apply border border-light-info bg-dark-info w-full h-[400px] block relative rounded-sm mb-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.area-img-box::before {
|
.area-img-box::before {
|
||||||
|
|||||||
@ -1,305 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { ref, onMounted, watch, computed } from "vue";
|
|
||||||
import * as echarts from "echarts";
|
|
||||||
import BarChart from "@/components/chart/BarChart.vue";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
|
|
||||||
const { locale, t } = useI18n();
|
|
||||||
const props = defineProps({
|
|
||||||
energyCostData: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const chartData = ref([]); // 初始化為空陣列
|
|
||||||
|
|
||||||
const labels = computed(() => [
|
|
||||||
t("dashboard.today"),
|
|
||||||
t("dashboard.yesterday"),
|
|
||||||
t("dashboard.this_week"),
|
|
||||||
t("dashboard.last_week"),
|
|
||||||
t("dashboard.this_month"),
|
|
||||||
t("dashboard.last_month"),
|
|
||||||
t("dashboard.this_year"),
|
|
||||||
t("dashboard.last_year"),
|
|
||||||
]);
|
|
||||||
const barWidth = 30; // Set barWidth
|
|
||||||
|
|
||||||
const barChartOptions = ref({
|
|
||||||
xAxis: {
|
|
||||||
type: "category",
|
|
||||||
data: chartData.value.map((item) => item.category),
|
|
||||||
axisLine: { lineStyle: { color: "#fff" } },
|
|
||||||
},
|
|
||||||
yAxis: { type: "value", show: false },
|
|
||||||
series: [], // 初始化為空陣列
|
|
||||||
tooltip: {
|
|
||||||
trigger: "axis",
|
|
||||||
axisPointer: { type: "shadow" },
|
|
||||||
formatter: function (params) {
|
|
||||||
let tooltipText = `<div>${params[0].axisValueLabel}</div>`;
|
|
||||||
const filteredParams = params.filter((item) => item.seriesType === "bar");
|
|
||||||
filteredParams.forEach((item) => {
|
|
||||||
tooltipText += `<div>${item.marker} ${
|
|
||||||
item.value ? item.value : "-"
|
|
||||||
}</div>`;
|
|
||||||
});
|
|
||||||
|
|
||||||
return tooltipText;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
function updateChartData(newEnergyCostData) {
|
|
||||||
if (newEnergyCostData && newEnergyCostData.compare) {
|
|
||||||
// 從 props.energyCostData.compare 中提取資料
|
|
||||||
const compareData = newEnergyCostData.compare;
|
|
||||||
|
|
||||||
// 轉換資料格式
|
|
||||||
chartData.value = [
|
|
||||||
{
|
|
||||||
category: t("dashboard.daily_relative_change"),
|
|
||||||
this: compareData.day.current,
|
|
||||||
last: compareData.day.last,
|
|
||||||
change: compareData.day.percentage,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: t("dashboard.weekly_relative_change"),
|
|
||||||
this: compareData.week.current,
|
|
||||||
last: compareData.week.last,
|
|
||||||
change: compareData.week.percentage,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: t("dashboard.monthly_relative_change"),
|
|
||||||
this: compareData.month.current,
|
|
||||||
last: compareData.month.last,
|
|
||||||
change: compareData.month.percentage,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
category: t("dashboard.yearly_relative_change"),
|
|
||||||
this: compareData.year.current,
|
|
||||||
last: compareData.year.last,
|
|
||||||
change: compareData.year.percentage,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// 更新 barChartOptions
|
|
||||||
barChartOptions.value = {
|
|
||||||
xAxis: {
|
|
||||||
type: "category",
|
|
||||||
data: chartData.value.map((item) => item.category),
|
|
||||||
axisLine: { lineStyle: { color: "#fff" } },
|
|
||||||
},
|
|
||||||
yAxis: { type: "value", show: false },
|
|
||||||
grid: {
|
|
||||||
left: "-10%",
|
|
||||||
right: "1%",
|
|
||||||
bottom: "3%",
|
|
||||||
top: "4%",
|
|
||||||
containLabel: true,
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: "當前週期",
|
|
||||||
data: chartData.value.map((item) => item.this),
|
|
||||||
type: "bar",
|
|
||||||
barWidth: barWidth,
|
|
||||||
barGap: "-10%",
|
|
||||||
itemStyle: {
|
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
||||||
{ offset: 0, color: "#186B80" },
|
|
||||||
{ offset: 1, color: "#50C3E3" },
|
|
||||||
]),
|
|
||||||
shadowBlur: 5,
|
|
||||||
shadowColor: "rgba(0, 0, 0, 0.3)",
|
|
||||||
shadowOffsetY: 2,
|
|
||||||
shadowOffsetX: 5,
|
|
||||||
},
|
|
||||||
z: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "對比週期",
|
|
||||||
data: chartData.value.map((item) => item.last),
|
|
||||||
type: "bar",
|
|
||||||
barWidth: barWidth,
|
|
||||||
itemStyle: {
|
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
||||||
{ offset: 0, color: "#988F2C" },
|
|
||||||
{ offset: 1, color: "#FFF26D" },
|
|
||||||
]),
|
|
||||||
shadowBlur: 5,
|
|
||||||
shadowColor: "rgba(0, 0, 0, 0.3)",
|
|
||||||
shadowOffsetY: 2,
|
|
||||||
shadowOffsetX: 5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// this top
|
|
||||||
z: 6,
|
|
||||||
type: "pictorialBar",
|
|
||||||
symbolPosition: "end",
|
|
||||||
data: chartData.value.map((item) => item.this),
|
|
||||||
symbol: "diamond",
|
|
||||||
symbolOffset: ["-45%", "-50%"],
|
|
||||||
symbolSize: [barWidth, barWidth * 0.5],
|
|
||||||
itemStyle: {
|
|
||||||
borderWidth: 0,
|
|
||||||
color: "#50C3E3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// this bot
|
|
||||||
z: 6,
|
|
||||||
type: "pictorialBar",
|
|
||||||
symbolPosition: "start",
|
|
||||||
data: chartData.value.map((item) => item.this),
|
|
||||||
symbol: "diamond",
|
|
||||||
symbolOffset: ["-45%", "50%"],
|
|
||||||
symbolSize: [barWidth, barWidth * 0.5],
|
|
||||||
itemStyle: {
|
|
||||||
borderWidth: 0,
|
|
||||||
color: "#50C3E3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// last top
|
|
||||||
z: 3,
|
|
||||||
type: "pictorialBar",
|
|
||||||
symbolPosition: "end",
|
|
||||||
data: chartData.value.map((item) => item.last),
|
|
||||||
symbol: "diamond",
|
|
||||||
symbolOffset: ["45%", "-50%"],
|
|
||||||
symbolSize: [barWidth, barWidth * 0.5],
|
|
||||||
itemStyle: {
|
|
||||||
borderWidth: 0,
|
|
||||||
color: "#FFF26D",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// last bot
|
|
||||||
z: 3,
|
|
||||||
type: "pictorialBar",
|
|
||||||
symbolPosition: "start",
|
|
||||||
data: chartData.value.map((item) => item.last),
|
|
||||||
symbol: "diamond",
|
|
||||||
symbolOffset: ["45%", "50%"],
|
|
||||||
symbolSize: [barWidth, barWidth * 0.5],
|
|
||||||
itemStyle: {
|
|
||||||
borderWidth: 0,
|
|
||||||
color: "#FFF26D",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
tooltip: {
|
|
||||||
trigger: "axis",
|
|
||||||
axisPointer: { type: "shadow" },
|
|
||||||
formatter: function (params) {
|
|
||||||
let tooltipText = `<div>${params[0].axisValueLabel}</div>`;
|
|
||||||
const filteredParams = params.filter(
|
|
||||||
(item) => item.seriesType === "bar"
|
|
||||||
);
|
|
||||||
filteredParams.forEach((item) => {
|
|
||||||
tooltipText += `<div>${item.marker} ${
|
|
||||||
item.value ? item.value : "-"
|
|
||||||
}</div>`;
|
|
||||||
});
|
|
||||||
|
|
||||||
return tooltipText;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 使用 watch 監聽 energyCostData 的變化
|
|
||||||
watch(
|
|
||||||
() => props.energyCostData,
|
|
||||||
(newEnergyCostData) => {
|
|
||||||
updateChartData(newEnergyCostData);
|
|
||||||
},
|
|
||||||
{ immediate: true } // 立即執行一次,確保初始資料載入
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(locale, () => {
|
|
||||||
updateChartData(props.energyCostData);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="flex flex-wrap">
|
|
||||||
<div class="w-full chart-data relative px-8 py-3">
|
|
||||||
<div class="flex flex-wrap items-center justify-between">
|
|
||||||
<h2 class="font-light">
|
|
||||||
{{ $t("dashboard.relative_energy_consumption") }}
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<div class="h-[180px]">
|
|
||||||
<BarChart
|
|
||||||
id="dashboard_chart_compare"
|
|
||||||
class="h-full"
|
|
||||||
:option="barChartOptions"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 表格數據展示 -->
|
|
||||||
<div class="flex justify-between">
|
|
||||||
<div
|
|
||||||
v-for="(data, index) in chartData"
|
|
||||||
:key="index"
|
|
||||||
class="w-1/4 text-center mx-1"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="text-sm bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
|
|
||||||
>
|
|
||||||
{{ labels[index * 2] }}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="text-sm bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
|
|
||||||
>
|
|
||||||
{{ data.this ?? "-" }}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="text-sm bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
|
|
||||||
>
|
|
||||||
{{ labels[index * 2 + 1] }}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="text-sm bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
|
|
||||||
>
|
|
||||||
{{ data.last ?? "-" }}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="text-sm bg-cyan-900 p-1 border border-cyan-100 border-opacity-20"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
:class="{
|
|
||||||
'text-red-500': data.change > 0,
|
|
||||||
'text-green-500': data.change < 0,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
data.change
|
|
||||||
? (data.change > 0 ? "+" : "") + data.change + "%"
|
|
||||||
: "-"
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.chart-data:before {
|
|
||||||
@apply absolute right-0 left-2 top-0 h-10 w-10 bg-no-repeat z-10;
|
|
||||||
content: "";
|
|
||||||
background: url(@ASSET/img/chart-data-background01.svg) center center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-data::after {
|
|
||||||
@apply absolute right-0 bottom-0 h-10 w-10 bg-no-repeat z-10;
|
|
||||||
content: "";
|
|
||||||
background: url(@ASSET/img/chart-data-background02.svg) center center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,109 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { ref, watch } from "vue";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
energyCostData: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const energyTypeList = ref([
|
|
||||||
{
|
|
||||||
title: t("dashboard.today_energy_consumption"),
|
|
||||||
key: "today",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("dashboard.this_month_energy_consumption"),
|
|
||||||
key: "month",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const currentEnergyType = ref({
|
|
||||||
name: "month",
|
|
||||||
});
|
|
||||||
|
|
||||||
// 取得當前能耗資料
|
|
||||||
const getCurrentEnergyData = () => {
|
|
||||||
if (!props.energyCostData || !props.energyCostData.rank) {
|
|
||||||
return []; // 或者返回一些默认值
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentEnergyType.value.name === "month"
|
|
||||||
? props.energyCostData?.rank.month || []
|
|
||||||
: props.energyCostData?.rank.day || [];
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="state-box-col relative ps-2 h-full max-h-[350px]">
|
|
||||||
<div class="state-box h-full">
|
|
||||||
<!-- 標題和切換按鈕 -->
|
|
||||||
<div class="flex justify-between items-center mb-2">
|
|
||||||
<h2 class="font-light relative">
|
|
||||||
{{ $t("dashboard.energy_ranking") }}
|
|
||||||
</h2>
|
|
||||||
<Select
|
|
||||||
:value="currentEnergyType"
|
|
||||||
class="!w-24"
|
|
||||||
selectClass="border-info focus-within:border-info btn-xs text-xs"
|
|
||||||
name="name"
|
|
||||||
Attribute="title"
|
|
||||||
:options="energyTypeList"
|
|
||||||
:isTopLabelExist="false"
|
|
||||||
:isBottomLabelExist="false"
|
|
||||||
>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 能耗排名列表 -->
|
|
||||||
<div class="max-h-[300px] overflow-y-auto">
|
|
||||||
<table class="table table-sm text-center">
|
|
||||||
<tbody>
|
|
||||||
<tr
|
|
||||||
v-for="(item, index) in getCurrentEnergyData()"
|
|
||||||
:key="index"
|
|
||||||
:class="[
|
|
||||||
{ 'text-red-300': index + 1 === 1 },
|
|
||||||
{ 'text-orange-300': index + 1 === 2 },
|
|
||||||
{ 'text-yellow-300': index + 1 === 3 },
|
|
||||||
{ 'text-teal-300': index + 1 > 3 },
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<td class="flex items-center">
|
|
||||||
<font-awesome-icon :icon="['fas', 'crown']" class="me-1" />{{
|
|
||||||
index + 1
|
|
||||||
}}
|
|
||||||
</td>
|
|
||||||
<td>{{ item.name }}</td>
|
|
||||||
<td>{{ item.value }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.state-box {
|
|
||||||
@apply border-2 border-light-info rounded-sm p-2 text-white relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.state-box-col:before {
|
|
||||||
@apply absolute left-0 right-0 -top-0.5 m-auto h-2 w-36 bg-no-repeat bg-center z-10;
|
|
||||||
content: "";
|
|
||||||
background-image: url(@ASSET/img/state-box-top.png);
|
|
||||||
}
|
|
||||||
|
|
||||||
.state-box-col:after {
|
|
||||||
@apply absolute left-0 right-0 -bottom-0.5 m-auto h-2 w-36 bg-no-repeat bg-center z-10;
|
|
||||||
content: "";
|
|
||||||
background-image: url(@ASSET/img/state-box-bottom.png);
|
|
||||||
}
|
|
||||||
|
|
||||||
tr td {
|
|
||||||
@apply text-[13px] text-start;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,250 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { ref, onMounted, watch } from "vue";
|
|
||||||
import * as echarts from "echarts";
|
|
||||||
import BarChart from "@/components/chart/BarChart.vue";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
|
||||||
|
|
||||||
const storeBuild = useBuildingStore();
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
formState: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
energyCostData: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
getEnergyCostData: {
|
|
||||||
type: Function,
|
|
||||||
required: true,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const chartData = ref([]);
|
|
||||||
const floorList = ref([]);
|
|
||||||
const deptList = ref([]);
|
|
||||||
const weekComparisonOption = ref({});
|
|
||||||
|
|
||||||
// 生成柱狀圖的 option
|
|
||||||
const generateCylinderChartOption = (data) => {
|
|
||||||
const barWidth = 15;
|
|
||||||
return {
|
|
||||||
xAxis: {
|
|
||||||
type: "category",
|
|
||||||
data: data.map((item) => item.date),
|
|
||||||
axisLine: {
|
|
||||||
lineStyle: {
|
|
||||||
color: "#fff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: "value",
|
|
||||||
name: "kWh",
|
|
||||||
axisLine: {
|
|
||||||
lineStyle: {
|
|
||||||
color: "#fff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
data: data.map((item) => item.energy),
|
|
||||||
type: "bar",
|
|
||||||
barWidth: barWidth,
|
|
||||||
itemStyle: {
|
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 1, [
|
|
||||||
{ offset: 0, color: "#1F7B47" },
|
|
||||||
{ offset: 1, color: "#247E95" },
|
|
||||||
]),
|
|
||||||
shadowBlur: 5,
|
|
||||||
shadowColor: "rgba(0, 0, 0, 0.5)",
|
|
||||||
shadowOffsetY: 5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
z: 15,
|
|
||||||
type: "pictorialBar",
|
|
||||||
symbolPosition: "end",
|
|
||||||
data: data.map((item) => item.energy),
|
|
||||||
symbol: "diamond",
|
|
||||||
symbolOffset: [0, -5],
|
|
||||||
symbolSize: [barWidth, barWidth * 0.5],
|
|
||||||
itemStyle: {
|
|
||||||
color: "#62E39A",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
z: 10,
|
|
||||||
type: "pictorialBar",
|
|
||||||
data: data.map((item) => item.energy),
|
|
||||||
symbol: "diamond",
|
|
||||||
symbolSize: [barWidth, barWidth * 0.5],
|
|
||||||
symbolOffset: [0, 6],
|
|
||||||
itemStyle: {
|
|
||||||
color: "#247E95",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
grid: {
|
|
||||||
left: "0%",
|
|
||||||
right: "1%",
|
|
||||||
bottom: "3%",
|
|
||||||
top: "10%",
|
|
||||||
containLabel: true,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
trigger: "axis",
|
|
||||||
formatter: function (params) {
|
|
||||||
const item = params[0];
|
|
||||||
return `<p>${item.name}</p> <p>${item.marker}Energy consumption : ${item.value}</p>`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const processEnergyData = () => {
|
|
||||||
if (!props.energyCostData || !props.energyCostData.trend) {
|
|
||||||
chartData.value = [];
|
|
||||||
weekComparisonOption.value = generateCylinderChartOption(chartData.value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dailyData = [...props.energyCostData.trend].sort(
|
|
||||||
(a, b) => new Date(a.time) - new Date(b.time)
|
|
||||||
);
|
|
||||||
|
|
||||||
chartData.value = dailyData.map((item) => ({
|
|
||||||
date: dayjs(item.time).format("MM/DD"),
|
|
||||||
energy: item.value,
|
|
||||||
}));
|
|
||||||
|
|
||||||
weekComparisonOption.value = generateCylinderChartOption(chartData.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.energyCostData,
|
|
||||||
(newEnergyCostData) => {
|
|
||||||
processEnergyData();
|
|
||||||
},
|
|
||||||
{ deep: true, immediate: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => storeBuild.floorList,
|
|
||||||
(newValue) => {
|
|
||||||
if (newValue) {
|
|
||||||
console.log('newValue',newValue);
|
|
||||||
|
|
||||||
floorList.value = [
|
|
||||||
{
|
|
||||||
title: "All",
|
|
||||||
key: "all",
|
|
||||||
},
|
|
||||||
...storeBuild.floorList,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => storeBuild.floorList,
|
|
||||||
(newValue) => {
|
|
||||||
if (newValue) {
|
|
||||||
floorList.value = [
|
|
||||||
{
|
|
||||||
title: "All",
|
|
||||||
key: "all",
|
|
||||||
},
|
|
||||||
...storeBuild.floorList,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => storeBuild.deptList,
|
|
||||||
(newValue) => {
|
|
||||||
if (newValue) {
|
|
||||||
deptList.value = [
|
|
||||||
{
|
|
||||||
title: "All",
|
|
||||||
key: "all",
|
|
||||||
},
|
|
||||||
...storeBuild.deptList,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
immediate: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="w-full chart-data relative px-8 py-1">
|
|
||||||
<div class="flex flex-wrap items-center justify-between">
|
|
||||||
<h2 class="font-light">{{ $t("dashboard.last_30_days_energy_trend") }}</h2>
|
|
||||||
<div class="flex items-center w-52 gap-4">
|
|
||||||
<Select
|
|
||||||
:value="props.formState"
|
|
||||||
class="my-2"
|
|
||||||
selectClass="border-info focus-within:border-info btn-xs text-xs"
|
|
||||||
name="floor_guid"
|
|
||||||
Attribute="title"
|
|
||||||
:options="floorList"
|
|
||||||
:isTopLabelExist="false"
|
|
||||||
:isBottomLabelExist="false"
|
|
||||||
>
|
|
||||||
</Select>
|
|
||||||
<Select
|
|
||||||
:value="props.formState"
|
|
||||||
class="my-2"
|
|
||||||
selectClass="border-info focus-within:border-info btn-xs text-xs"
|
|
||||||
name="department_id"
|
|
||||||
Attribute="title"
|
|
||||||
:options="deptList"
|
|
||||||
:isTopLabelExist="false"
|
|
||||||
:isBottomLabelExist="false"
|
|
||||||
>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="h-[300px]">
|
|
||||||
<BarChart
|
|
||||||
id="dashboard_chart_week_comparison"
|
|
||||||
class="h-full"
|
|
||||||
:option="weekComparisonOption"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.chart-data:before {
|
|
||||||
@apply absolute right-0 left-2 top-0 h-10 w-10 bg-no-repeat z-10;
|
|
||||||
content: "";
|
|
||||||
background: url(@ASSET/img/chart-data-background01.svg) center center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-data::after {
|
|
||||||
@apply absolute right-0 bottom-0 h-10 w-10 bg-no-repeat z-10;
|
|
||||||
content: "";
|
|
||||||
background: url(@ASSET/img/chart-data-background02.svg) center center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,100 +1,31 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, onMounted, onUnmounted } from "vue";
|
import dayjs from "dayjs";
|
||||||
import { getEnergyInfo } from "@/apis/dashboard";
|
import { computed } from "vue";
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import { useI18n } from "vue-i18n";
|
const mockData = [
|
||||||
|
|
||||||
const { locale, t } = useI18n();
|
|
||||||
const store = useBuildingStore();
|
|
||||||
const energyData = ref([]);
|
|
||||||
let intervalId = null;
|
|
||||||
|
|
||||||
const getEnergyInfos = async () => {
|
|
||||||
try {
|
|
||||||
const res = await getEnergyInfo(store.selectedBuilding.building_guid);
|
|
||||||
const apiData = res.data;
|
|
||||||
|
|
||||||
energyData.value = [
|
|
||||||
{
|
{
|
||||||
value: apiData.todayKWH ? apiData.todayKWH : "N/A",
|
value: "305.50",
|
||||||
label: t("dashboard.today_electricity_consumption"),
|
label: "Today's electricity consumption in kWH",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: apiData.yesterdayKWH ? apiData.yesterdayKWH : "N/A",
|
value: "886.75",
|
||||||
label: t("dashboard.yesterday_electricity_consumption"),
|
label: "Yesterday's electricity consumption in kWH",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: apiData.instantKW ? apiData.instantKW : "N/A",
|
value: "7.84",
|
||||||
label: t("dashboard.instant_power"),
|
label: "Instant power kW",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: apiData.instantContractRatio
|
value: "1.96",
|
||||||
? apiData.instantContractRatio
|
label: "Instant contract capacity ratio %",
|
||||||
: "N/A",
|
|
||||||
label: t("dashboard.instant_contract_capacity_ratio"),
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching energy info:", error);
|
|
||||||
energyData.value = [
|
|
||||||
{ value: "N/A", label: "Today's electricity consumption in kWH" },
|
|
||||||
{ value: "N/A", label: "Yesterday's electricity consumption in kWH" },
|
|
||||||
{ value: "N/A", label: "Instant power kW" },
|
|
||||||
{ value: "N/A", label: "Instant contract capacity ratio %" },
|
|
||||||
];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => store.selectedBuilding,
|
|
||||||
(newBuilding) => {
|
|
||||||
if (newBuilding) {
|
|
||||||
getEnergyInfos();
|
|
||||||
|
|
||||||
if (intervalId) {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
}
|
|
||||||
intervalId = setInterval(() => {
|
|
||||||
getEnergyInfos();
|
|
||||||
}, 30000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(locale, () => {
|
|
||||||
if (energyData.value.length) {
|
|
||||||
energyData.value = [
|
|
||||||
{
|
|
||||||
...energyData.value[0],
|
|
||||||
label: t("dashboard.today_electricity_consumption"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...energyData.value[1],
|
|
||||||
label: t("dashboard.yesterday_electricity_consumption"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...energyData.value[2],
|
|
||||||
label: t("dashboard.instant_power"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...energyData.value[3],
|
|
||||||
label: t("dashboard.instant_contract_capacity_ratio"),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-wrap">
|
<div class="flex flex-wrap">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in energyData"
|
v-for="(item, index) in mockData"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="xl:w-1/4 md:w-1/2 w-full item-data-box relative px-3"
|
class="xl:w-1/4 md:w-1/2 w-full item-data-box relative px-3"
|
||||||
>
|
>
|
||||||
@ -110,7 +41,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.item-data {
|
.item-data {
|
||||||
@apply relative mb-4 min-h-[100px] h-full flex flex-col justify-center;
|
@apply relative mb-4 min-h-[135px] h-full flex flex-col justify-center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-data-box:after {
|
.item-data-box:after {
|
||||||
|
|||||||
@ -2,10 +2,123 @@
|
|||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
|
||||||
const store = useBuildingStore();
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
// 假資料
|
||||||
|
const mockData = ref([
|
||||||
|
{
|
||||||
|
title: "Air Detection System",
|
||||||
|
icon: "temperature-high",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: "Dust",
|
||||||
|
sub_system_tag: "EM",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Lighting System",
|
||||||
|
icon: "lightbulb",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: "LS",
|
||||||
|
sub_system_tag: "ECLS",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Air Condition System",
|
||||||
|
icon: "fan",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: "ME",
|
||||||
|
sub_system_tag: "TH",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Electricity System",
|
||||||
|
icon: "bolt",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: "EE",
|
||||||
|
sub_system_tag: "ECP3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Elevator System",
|
||||||
|
icon: "building",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: null,
|
||||||
|
sub_system_tag: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "High Voltage Switchboard",
|
||||||
|
icon: "charging-station",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: null,
|
||||||
|
sub_system_tag: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Low Voltage Switchboard",
|
||||||
|
icon: "charging-station",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: null,
|
||||||
|
sub_system_tag: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Water Supply System",
|
||||||
|
icon: "tint",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: null,
|
||||||
|
sub_system_tag: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Sewage And Wastewater Equipment",
|
||||||
|
icon: "water",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: null,
|
||||||
|
sub_system_tag: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Emergency Generator",
|
||||||
|
icon: "car-battery",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: null,
|
||||||
|
sub_system_tag: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Fire Equipment",
|
||||||
|
icon: "fire-extinguisher",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: null,
|
||||||
|
sub_system_tag: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "CCTV System",
|
||||||
|
icon: "video",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: null,
|
||||||
|
sub_system_tag: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Access Control System",
|
||||||
|
icon: "door-open",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: null,
|
||||||
|
sub_system_tag: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Shutdown System",
|
||||||
|
icon: "car",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: null,
|
||||||
|
sub_system_tag: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Emergency Rescue System",
|
||||||
|
icon: "exclamation-triangle",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: null,
|
||||||
|
sub_system_tag: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Air Supply Aand Exhaust System",
|
||||||
|
icon: "wind",
|
||||||
|
isError: false,
|
||||||
|
main_system_tag: null,
|
||||||
|
sub_system_tag: null,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
const navigateToSubSystem = (mainSystemId, subSystemId) => {
|
const navigateToSubSystem = (mainSystemId, subSystemId) => {
|
||||||
router.push({
|
router.push({
|
||||||
name: "sub_system",
|
name: "sub_system",
|
||||||
@ -19,13 +132,13 @@ const navigateToSubSystem = (mainSystemId, subSystemId) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-wrap -mx-1 h-[21rem] overflow-y-auto items-start content-start">
|
<div class="flex flex-wrap -mx-1">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in store.subSys"
|
v-for="(item, index) in mockData"
|
||||||
:key="index"
|
:key="index"
|
||||||
:class="
|
:class="
|
||||||
twMerge(
|
twMerge(
|
||||||
'w-full sm:w-1/2 relative my-2',
|
'w-full sm:w-1/2 lg:w-1/4 relative my-2 ',
|
||||||
item.sub_system_tag
|
item.sub_system_tag
|
||||||
? 'saturate-200 cursor-pointer text-base text-info'
|
? 'saturate-200 cursor-pointer text-base text-info'
|
||||||
: 'grayscale opacity-70 cursor-not-allowed text-sm'
|
: 'grayscale opacity-70 cursor-not-allowed text-sm'
|
||||||
@ -41,20 +154,14 @@ const navigateToSubSystem = (mainSystemId, subSystemId) => {
|
|||||||
|
|
||||||
<div class="equipment-item">
|
<div class="equipment-item">
|
||||||
<div class="w-16">
|
<div class="w-16">
|
||||||
<img
|
|
||||||
v-if="item.device_image_url"
|
|
||||||
class="w-7 m-auto"
|
|
||||||
:src="`${FILE_BASEURL}/${item.device_image_url}`"
|
|
||||||
/>
|
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
v-else
|
|
||||||
class="text-2xl mt-1 m-auto"
|
class="text-2xl mt-1 m-auto"
|
||||||
:icon="['fas', 'tv']"
|
:icon="['fas', item.icon]"
|
||||||
></FontAwesomeIcon>
|
></FontAwesomeIcon>
|
||||||
</div>
|
</div>
|
||||||
<div class="icon-text">
|
<div class="icon-text">
|
||||||
<div class="">
|
<div class="">
|
||||||
{{ item.full_name }}
|
{{ item.title }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,138 +1,29 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch, onUnmounted } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
|
||||||
import { getAlarmOperationInfo } from "@/apis/dashboard";
|
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
|
||||||
import DashboardSysProgressModal from "./DashboardSysProgressModal.vue";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
const router = useRouter();
|
|
||||||
const store = useBuildingStore();
|
|
||||||
const equipmentData = ref({
|
const equipmentData = ref({
|
||||||
title: t("dashboard.system_status"),
|
title: "System Status",
|
||||||
items: [],
|
items: [
|
||||||
|
{ label: "Auxiliary", online: 6, offline: 0, alarm: 0 },
|
||||||
|
{ label: "Air Detection", online: 31, offline: 0, alarm: 2 },
|
||||||
|
{ label: "Electricity", online: 12, offline: 0, alarm: 1 },
|
||||||
|
{ label: "Lighting", online: 20, offline: 3, alarm: 0 },
|
||||||
|
{ label: "Air Condition", online: 23, offline: 0, alarm: 0 },
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const orderData = ref({
|
const orderData = ref({
|
||||||
title: t("dashboard.work_order"),
|
title: "Work Order",
|
||||||
items: [],
|
items: [
|
||||||
});
|
{ label: "Unassigned", value: 2 },
|
||||||
const modalData = ref({});
|
{ label: "Assigned", value: 4 },
|
||||||
let intervalId = null;
|
{ label: "Completed", value: 1 },
|
||||||
|
],
|
||||||
const getAlarmsInfos = async () => {
|
|
||||||
try {
|
|
||||||
const res = await getAlarmOperationInfo(
|
|
||||||
store.selectedBuilding.building_guid
|
|
||||||
);
|
|
||||||
const apiData = res.data;
|
|
||||||
|
|
||||||
// 轉換 equipmentData 的資料格式
|
|
||||||
if (apiData && apiData.alarm) {
|
|
||||||
equipmentData.value.items = apiData.alarm.map((item) => ({
|
|
||||||
label: item.name,
|
|
||||||
online: item.online || 0,
|
|
||||||
offline: item.offline || 0,
|
|
||||||
alarm: item.alarm || 0,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 轉換 orderData 的資料格式
|
|
||||||
if (apiData && apiData.operation) {
|
|
||||||
orderData.value.items = [
|
|
||||||
{
|
|
||||||
label: t("operation.repair"),
|
|
||||||
complete: apiData.operation.repair.complete || 0,
|
|
||||||
incomplete: apiData.operation.repair.incomplete || 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t("operation.maintenance"),
|
|
||||||
complete: apiData.operation.upkeep.complete || 0,
|
|
||||||
incomplete: apiData.operation.upkeep.incomplete || 0,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching alarm info:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const navigateToMaintenance = (item) => {
|
|
||||||
router.push({
|
|
||||||
name: "operation",
|
|
||||||
query: {
|
|
||||||
work_type: item == "Repair" ? "2" : "1",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const openModal = (item) => {
|
|
||||||
modalData.value = item;
|
|
||||||
system_status_modal.showModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCancel = () => {
|
|
||||||
modalData.value = {};
|
|
||||||
system_status_modal.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => store.selectedBuilding,
|
|
||||||
(newBuilding) => {
|
|
||||||
if (newBuilding) {
|
|
||||||
getAlarmsInfos();
|
|
||||||
|
|
||||||
if (intervalId) {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
}
|
|
||||||
intervalId = setInterval(() => {
|
|
||||||
getAlarmsInfos();
|
|
||||||
}, 30000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<DashboardSysProgressModal :onCancel="onCancel" :modalData="modalData" />
|
|
||||||
<div class="flex flex-wrap">
|
<div class="flex flex-wrap">
|
||||||
<div class="w-full sm:w-2/5 state-box-col relative ps-2">
|
|
||||||
<div class="state-box">
|
|
||||||
<div class="title">
|
|
||||||
<img class="state-title01" src="@ASSET/img/state-title01.svg" />
|
|
||||||
<span>{{ orderData.title }}</span>
|
|
||||||
<img class="state-title02" src="@ASSET/img/state-title02.svg" />
|
|
||||||
</div>
|
|
||||||
<table class="table table-sm text-center">
|
|
||||||
<thead>
|
|
||||||
<tr class="border-cyan-400 text-cyan-100">
|
|
||||||
<th></th>
|
|
||||||
<th>{{ $t("operation.complete") }}</th>
|
|
||||||
<th>{{ $t("operation.incomplete") }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr
|
|
||||||
v-for="(item, index) in orderData.items"
|
|
||||||
:key="index"
|
|
||||||
class="border-cyan-400 cursor-pointer hover:text-info"
|
|
||||||
@click.stop.prevent="navigateToMaintenance(item.label)"
|
|
||||||
>
|
|
||||||
<th class="px-0 text-start">
|
|
||||||
<span>{{ item.label }}</span>
|
|
||||||
</th>
|
|
||||||
<td>{{ item.complete }}</td>
|
|
||||||
<td>{{ item.incomplete }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full sm:w-3/5 state-box-col relative ps-2">
|
<div class="w-full sm:w-3/5 state-box-col relative ps-2">
|
||||||
<div class="state-box">
|
<div class="state-box">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
@ -144,28 +35,50 @@ onUnmounted(() => {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr class="border-cyan-400 text-cyan-100">
|
<tr class="border-cyan-400 text-cyan-100">
|
||||||
<th></th>
|
<th></th>
|
||||||
<th>{{ $t("alert.online") }}</th>
|
<th>Online</th>
|
||||||
<th>{{ $t("alert.offline") }}</th>
|
<th>Offline</th>
|
||||||
<th>{{ $t("alert.alarm") }}</th>
|
<th>Alarm</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr
|
<tr
|
||||||
v-for="(item, index) in equipmentData.items"
|
v-for="(item, index) in equipmentData.items"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="border-cyan-400 cursor-pointer hover:text-info"
|
class="border-cyan-400"
|
||||||
@click.stop.prevent="openModal(item)"
|
|
||||||
>
|
>
|
||||||
<th class="px-0 text-start">{{ item.label }}</th>
|
<th class="px-0 text-start">{{ item.label }}</th>
|
||||||
<td>
|
<td>{{ item.online }}</td>
|
||||||
{{ item.online.length }}
|
<td>{{ item.offline }}</td>
|
||||||
</td>
|
<td>{{ item.alarm }}</td>
|
||||||
<td>
|
</tr>
|
||||||
{{ item.offline.length }}
|
</tbody>
|
||||||
</td>
|
</table>
|
||||||
<td>
|
</div>
|
||||||
{{ item.alarm.length }}
|
</div>
|
||||||
</td>
|
<div class="w-full sm:w-2/5 state-box-col relative ps-2">
|
||||||
|
<div class="state-box">
|
||||||
|
<div class="title">
|
||||||
|
<img class="state-title01" src="@ASSET/img/state-title01.svg" />
|
||||||
|
<span>{{ orderData.title }}</span>
|
||||||
|
<img class="state-title02" src="@ASSET/img/state-title02.svg" />
|
||||||
|
</div>
|
||||||
|
<table class="table table-sm text-center">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-cyan-400 text-cyan-100">
|
||||||
|
<th></th>
|
||||||
|
<th>value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
|
v-for="(item, index) in orderData.items"
|
||||||
|
:key="index"
|
||||||
|
class="border-cyan-400"
|
||||||
|
>
|
||||||
|
<th class="px-0 text-start">
|
||||||
|
<span>{{ item.label }}</span>
|
||||||
|
</th>
|
||||||
|
<td>{{ item.value }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@ -188,7 +101,7 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.state-box {
|
.state-box {
|
||||||
@apply h-[22rem] border-2 border-light-info rounded-sm py-2 px-6 text-white relative;
|
@apply h-80 border-2 border-light-info rounded-sm py-2 px-6 text-white relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.state-box:after {
|
.state-box:after {
|
||||||
@ -204,7 +117,7 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.state-box .title {
|
.state-box .title {
|
||||||
@apply relative flex items-center mb-1;
|
@apply relative flex items-center mb-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.state-box .title .state-title01 {
|
.state-box .title .state-title01 {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user