MQTT位置路徑修改 | CviBuilding存進localStorage | 樓層與部門存到狀態裡 | 首頁、電價表語言包 |能源管理新增棟別、樓層、部門參數 | 歷史資料新增日期區間 | 樓層頁面deBug | 圖資deBug | MQTT頁面 | 系統監控: 新增棟別樓層部門 以及 spriteDbId的deBug | 電表設定
This commit is contained in:
parent
f956402648
commit
8b85e2d67c
@ -1,4 +1,4 @@
|
||||
VITE_API_BASEURL = "https://ibms-cvilux-api.production.mjmtech.com.tw"
|
||||
VITE_FILE_API_BASEURL = "https://cgems.cvilux-group.com:8088"
|
||||
VITE_MQTT_BASEURL = "ws://192.168.0.217:8083/mqtt"
|
||||
VITE_MQTT_BASEURL = "wss://mqttwss.mjm-staging.developers-homelab.net"
|
||||
VITE_FORGE_BASEURL = "https://cgems.cvilux-group.com:8088/dist"
|
@ -1,4 +1,4 @@
|
||||
VITE_API_BASEURL = "https://ibms-cvilux-api.production.mjmtech.com.tw"
|
||||
VITE_FILE_API_BASEURL = "https://cgems.cvilux-group.com:8088"
|
||||
VITE_MQTT_BASEURL = "wss://192.168.0.217:8084/mqtt"
|
||||
VITE_MQTT_BASEURL = "wss://mqttwss.mjm-staging.developers-homelab.net"
|
||||
VITE_FORGE_BASEURL = "https://cgems.cvilux-group.com:8088/dist"
|
@ -20,6 +20,11 @@ export const GET_ASSET_IOT_LIST_API = `/AssetManage/GetIOTList`;
|
||||
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`;
|
||||
@ -28,3 +33,5 @@ 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`;
|
@ -15,12 +15,17 @@ import {
|
||||
POST_ASSET_SINGLE_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
|
||||
DELETE_ASSET_ELECTYPE_API,
|
||||
POST_ASSET_ELEC_SETTING_API,
|
||||
} from "./api";
|
||||
import instance from "@/util/request";
|
||||
import apihandler from "@/util/apihandler";
|
||||
@ -156,8 +161,8 @@ export const deleteAssetItem = async (main_id) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const getAssetFloorList = async () => {
|
||||
const res = await instance.post(GET_ASSET_FLOOR_LIST_API);
|
||||
export const getAssetFloorList = async (building_guid) => {
|
||||
const res = await instance.post(GET_ASSET_FLOOR_LIST_API, { building_guid });
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
msg: res.msg,
|
||||
@ -215,6 +220,62 @@ export const getIOTSchema = async (variable_id) => {
|
||||
});
|
||||
};
|
||||
|
||||
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,
|
||||
}) => {
|
||||
const res = await instance.post(POST_ASSET_DEVICE_ITEM_API, {
|
||||
id,
|
||||
variable_id,
|
||||
full_name,
|
||||
points,
|
||||
decimals,
|
||||
is_bool,
|
||||
is_link,
|
||||
});
|
||||
|
||||
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, {});
|
||||
|
||||
@ -274,3 +335,12 @@ export const deleteElecTypeItem = async (id) => {
|
||||
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,
|
||||
});
|
||||
};
|
||||
|
@ -11,14 +11,22 @@ import {
|
||||
GET_CARBON_API,
|
||||
POST_EDIT_CARBON_API,
|
||||
GET_TIME_ELEC_API,
|
||||
POST_TIME_ELEC_API
|
||||
POST_TIME_ELEC_API,
|
||||
} from "./api";
|
||||
import instance, { fileInstance } from "@/util/request";
|
||||
import apihandler from "@/util/apihandler";
|
||||
import downloadExcel from "@/util/downloadExcel";
|
||||
|
||||
export const getRealTimeDist = async () => {
|
||||
const res = await instance.post(GET_REALTIME_DIST_API);
|
||||
export const getRealTimeDist = async ({
|
||||
building_guid,
|
||||
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, {
|
||||
msg: res.msg,
|
||||
@ -26,8 +34,16 @@ export const getRealTimeDist = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const getElecUseDay = async () => {
|
||||
const res = await instance.post(GET_ELECUSE_DAY_API);
|
||||
export const getElecUseDay = async ({
|
||||
building_guid,
|
||||
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, {
|
||||
msg: res.msg,
|
||||
@ -35,8 +51,18 @@ export const getElecUseDay = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const getTaipower = async () => {
|
||||
const res = await instance.post(GET_TAI_POWER_API);
|
||||
export const getTaipower = async ({
|
||||
coefficient,
|
||||
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,
|
||||
@ -126,13 +152,19 @@ export const getDemand = async (building_guid) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const postEditDemand = async ({ id, contract, alert, reset, building_guid }) => {
|
||||
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
|
||||
building_guid,
|
||||
});
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
@ -150,11 +182,15 @@ export const getCarbonValue = async (building_guid) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const postEditCarbonValue = async ({ id, coefficient, building_guid }) => {
|
||||
export const postEditCarbonValue = async ({
|
||||
id,
|
||||
coefficient,
|
||||
building_guid,
|
||||
}) => {
|
||||
const res = await instance.put(POST_EDIT_CARBON_API, {
|
||||
id,
|
||||
coefficient,
|
||||
building_guid
|
||||
building_guid,
|
||||
});
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
@ -176,7 +212,7 @@ export const postTimeElec = async ({ sheet, cost, building_guid }) => {
|
||||
const res = await instance.put(POST_TIME_ELEC_API, {
|
||||
sheet,
|
||||
cost,
|
||||
building_guid
|
||||
building_guid,
|
||||
});
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
|
@ -16,11 +16,13 @@ export const getHistorySideBar = async ({
|
||||
sub_system_tag,
|
||||
department_id,
|
||||
elec_type_id,
|
||||
building_guid,
|
||||
}) => {
|
||||
const res = await instance.post(GET_HISTORY_SIDEBAR_API, {
|
||||
sub_system_tag,
|
||||
department_id,
|
||||
elec_type_id,
|
||||
building_guid,
|
||||
});
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
|
@ -6,6 +6,7 @@ const store = useBuildingStore();
|
||||
|
||||
const selectBuilding = (bui) => {
|
||||
store.selectedBuilding = bui; // 改變 selectedBuilding,watch 會自動更新資料
|
||||
localStorage.setItem("CviBuilding", JSON.stringify(bui));
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -27,7 +27,24 @@
|
||||
"lastweek_electricity_consumption": "上周用电量",
|
||||
"one_hour": "1小时",
|
||||
"four_hour": "4小时",
|
||||
"eight_hour": "8小时"
|
||||
"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": "去年"
|
||||
},
|
||||
"history": {
|
||||
"title": "历史资料",
|
||||
@ -358,12 +375,29 @@
|
||||
"confirm": "确认",
|
||||
"restore": "复原",
|
||||
"stop_edit": "停止修改",
|
||||
"start_edit": "开始修改"
|
||||
"start_edit": "开始修改",
|
||||
"convert": "轉換"
|
||||
},
|
||||
"msg": {
|
||||
"sure_to_delete": "是否确认删除该项目?",
|
||||
"sure_to_delete_permanent": "是否确认永久删除该项目?",
|
||||
"delete_success": "删除成功",
|
||||
"delete_failed": "删除失败"
|
||||
},
|
||||
"setting": {
|
||||
"MQTT_parse": "MQTT 解析",
|
||||
"schema": "架构",
|
||||
"point": "点位",
|
||||
"description": "描述",
|
||||
"IoT_point_name": "IoT 点位名称",
|
||||
"IoT_point_code": "IoT 点位代号",
|
||||
"number_of_decimal_places": "小数位数",
|
||||
"boolean_value": "布林值",
|
||||
"hide_point": "点位显示",
|
||||
"schema_name": "架构名称",
|
||||
"IoT_point_structure": "IoT点位结构",
|
||||
"system_point_name": "系统点位名称",
|
||||
"json_format_text": "请贴上 JSON 格式数据",
|
||||
"json_click_text": "请在左侧输入JSON并点选转换按钮"
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,24 @@
|
||||
"lastweek_electricity_consumption": "上週用電量",
|
||||
"one_hour": "1小時",
|
||||
"four_hour": "4小時",
|
||||
"eight_hour": "8小時"
|
||||
"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": "去年"
|
||||
},
|
||||
"history": {
|
||||
"title": "歷史資料",
|
||||
@ -358,12 +375,29 @@
|
||||
"confirm": "確認",
|
||||
"restore": "復原",
|
||||
"stop_edit": "停止修改",
|
||||
"start_edit": "開始修改"
|
||||
"start_edit": "開始修改",
|
||||
"convert":"轉換"
|
||||
},
|
||||
"msg": {
|
||||
"sure_to_delete": "是否確認刪除該項目?",
|
||||
"sure_to_delete_permanent": "是否確認永久刪除該項目?",
|
||||
"delete_success": "刪除成功",
|
||||
"delete_failed": "刪除失敗"
|
||||
},
|
||||
"setting": {
|
||||
"MQTT_parse": "MQTT 解析",
|
||||
"schema":"架構",
|
||||
"point":"點位",
|
||||
"description":"描述",
|
||||
"IoT_point_name":"IoT 點位名稱",
|
||||
"IoT_point_code":"IoT 點位代號",
|
||||
"number_of_decimal_places":"小數位數",
|
||||
"boolean_value":"布林值",
|
||||
"hide_point":"點位顯示",
|
||||
"schema_name":"架構名稱",
|
||||
"IoT_point_structure" :"IoT點位結構",
|
||||
"system_point_name":"系統點位名稱",
|
||||
"json_format_text": "請貼上 JSON 格式數據",
|
||||
"json_click_text": "請在左側輸入JSON並點選轉換按鈕"
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,37 @@
|
||||
"description": "File size cannot exceed 10MB",
|
||||
"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",
|
||||
"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",
|
||||
"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"
|
||||
},
|
||||
"history": {
|
||||
"title": "Historical Data",
|
||||
"building_name": "Building",
|
||||
@ -33,20 +64,6 @@
|
||||
"end_date": "End date",
|
||||
"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",
|
||||
"one_hour": "1 hour",
|
||||
"four_hour": "4 hour",
|
||||
"eight_hour": "8 hour"
|
||||
},
|
||||
"system": {
|
||||
"status": "Status",
|
||||
"details": "Details",
|
||||
@ -358,12 +375,29 @@
|
||||
"confirm": "Confirm",
|
||||
"restore": "Restore",
|
||||
"stop_edit": "Stop editing",
|
||||
"start_edit": "Start editing"
|
||||
"start_edit": "Start editing",
|
||||
"convert": "Convert"
|
||||
},
|
||||
"msg": {
|
||||
"sure_to_delete": "Are you sure to delete this item?",
|
||||
"sure_to_delete_permanent": "Are you sure you want to permanently delete this item?",
|
||||
"delete_success": "Delete successfully",
|
||||
"delete_failed": "Delete failed"
|
||||
},
|
||||
"setting": {
|
||||
"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",
|
||||
"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,8 +61,11 @@ import {
|
||||
faDownload,
|
||||
faStream,
|
||||
faSave,
|
||||
faCrown
|
||||
faCrown,
|
||||
faClock,
|
||||
faCheckCircle
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faCircle } from "@fortawesome/free-regular-svg-icons";
|
||||
|
||||
/* add icons to the library */
|
||||
library.add(
|
||||
@ -124,7 +127,10 @@ library.add(
|
||||
faDownload,
|
||||
faStream,
|
||||
faSave,
|
||||
faCrown
|
||||
faCrown,
|
||||
faClock,
|
||||
faCheckCircle,
|
||||
faCircle
|
||||
);
|
||||
|
||||
export default library;
|
||||
|
@ -48,13 +48,14 @@ const useBuildingStore = defineStore("buildingInfo", () => {
|
||||
const res = await getBuildings();
|
||||
buildings.value = res.data;
|
||||
if (res.data.length > 0 && !selectedBuilding.value) {
|
||||
selectedBuilding.value = res.data[0]; // 預設選第一個建築
|
||||
const storedBuilding = JSON.parse(localStorage.getItem("CviBuilding"));
|
||||
selectedBuilding.value = storedBuilding || res.data[0]; // 預設選第一個建築
|
||||
}
|
||||
};
|
||||
|
||||
// 獲取樓層資料
|
||||
const fetchFloorList = async () => {
|
||||
const res = await getAssetFloorList();
|
||||
const fetchFloorList = async (building_guid) => {
|
||||
const res = await getAssetFloorList(building_guid);
|
||||
floorList.value = res.data[0]?.floors.map((d) => ({
|
||||
...d,
|
||||
title: d.full_name,
|
||||
@ -75,7 +76,7 @@ const useBuildingStore = defineStore("buildingInfo", () => {
|
||||
// 當 selectedBuilding 改變時,更新 floorList 和 deptList
|
||||
watch(selectedBuilding, async (newBuilding) => {
|
||||
if (newBuilding) {
|
||||
await Promise.all([fetchFloorList(), fetchDepartmentList()]);
|
||||
await Promise.all([fetchFloorList(newBuilding.building_guid), fetchDepartmentList()]);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, provide, onMounted, watch } from "vue";
|
||||
import { ref, provide, onMounted, watch, computed } from "vue";
|
||||
import AssetMainList from "./components/AssetMainList.vue";
|
||||
import AssetSubList from "./components/AssetSubList.vue";
|
||||
import AssetTable from "./components/AssetTable.vue";
|
||||
@ -12,8 +12,6 @@ const { searchParams, changeParams } = useSearchParam();
|
||||
const companyOptions = ref([]);
|
||||
const iotSchemaOptions = ref([]);
|
||||
const elecTypeOptions = ref([]);
|
||||
const departmentList = ref([]);
|
||||
const floors = ref([]);
|
||||
const getCompany = async () => {
|
||||
const res = await getOperationCompanyList();
|
||||
companyOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||
@ -27,11 +25,12 @@ const getElecType = async () => {
|
||||
elecTypeOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||
};
|
||||
|
||||
const departmentList = computed(() => storeBuild.deptList);
|
||||
const floors = computed(() => storeBuild.floorList);
|
||||
|
||||
onMounted(() => {
|
||||
getCompany();
|
||||
getElecType();
|
||||
floors.value = storeBuild.floorList;
|
||||
departmentList.value = storeBuild.deptList;
|
||||
});
|
||||
|
||||
watch(
|
||||
|
@ -9,23 +9,11 @@ import dayjs from "dayjs";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
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 { 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 getFloors = async () => {
|
||||
const res = await getAssetFloorList();
|
||||
floors.value = res.data[0]?.floors.map((d) => ({ ...d, key: d.floor_guid }));
|
||||
};
|
||||
|
||||
const tableData = ref([]);
|
||||
const getAssetData = async () => {
|
||||
totalCoordinates.value = {}; // 在獲取新數據之前清空 totalCoordinates
|
||||
@ -45,6 +33,7 @@ const getAssetData = async () => {
|
||||
floor: floors.value.find(({ floor_guid }) => d.floor_guid === floor_guid)
|
||||
?.full_name,
|
||||
company: companyOptions.value.find(({ id }) => d.operation_id === id),
|
||||
department: departmentList.value.find(({ id }) => d.department_id === id)?.name,
|
||||
buying_date: d?.buying_date
|
||||
? dayjs(d?.buying_date).format("YYYY-MM-DD")
|
||||
: "",
|
||||
@ -56,17 +45,15 @@ const getAssetData = async () => {
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
await getCompany();
|
||||
await getFloors();
|
||||
getAssetData();
|
||||
});
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: t("assetManagement.device_number"),
|
||||
key: "device_number",
|
||||
class: "break-all",
|
||||
},
|
||||
// {
|
||||
// title: t("assetManagement.device_number"),
|
||||
// key: "device_number",
|
||||
// class: "break-all",
|
||||
// },
|
||||
{
|
||||
title: t("assetManagement.device_name"),
|
||||
key: "full_name",
|
||||
@ -83,6 +70,11 @@ const columns = computed(() => [
|
||||
filter: true,
|
||||
sort: true,
|
||||
},
|
||||
{
|
||||
title: t("assetManagement.department"),
|
||||
key: "department",
|
||||
filter: true,
|
||||
},
|
||||
{
|
||||
title: t("assetManagement.device_coordinate"),
|
||||
key: "device_coordinate",
|
||||
|
@ -2,12 +2,11 @@
|
||||
import { onMounted, ref, inject, watch, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
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 { formState } = inject("asset_table_modal_form");
|
||||
const BASEURL = import.meta.env.VITE_MQTT_BASEURL;
|
||||
// MQTT相關
|
||||
const mqttClient = ref(null); // MQTT客戶端
|
||||
@ -26,10 +25,13 @@ const openModal = () => {
|
||||
const connectMqtt = () => {
|
||||
const topic = formState.value.topic || ""; // 取得主題
|
||||
const mqttHost = `${BASEURL}`;
|
||||
const protocol = import.meta.env.MODE === "production" ? "wss" : "ws"; // 根據伺服器配置,需要設置為 "ws" 或 "wss"
|
||||
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", () => {
|
||||
@ -47,7 +49,14 @@ const connectMqtt = () => {
|
||||
|
||||
mqttClient.value.on("message", (topic, message) => {
|
||||
// 儲存接收到的訊息
|
||||
receivedMessages.value.push({ topic, message: message.toString() });
|
||||
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); // 收到訊息後清除倒計時
|
||||
});
|
||||
|
||||
@ -107,8 +116,14 @@ const onCancel = () => {
|
||||
: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>
|
||||
<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>
|
||||
|
@ -2,35 +2,37 @@
|
||||
import { ref, onMounted } from "vue";
|
||||
import * as echarts from "echarts";
|
||||
import BarChart from "@/components/chart/BarChart.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
const chartData = ref([
|
||||
{
|
||||
category: "日環比",
|
||||
category: t("dashboard.daily_relative_change"),
|
||||
this: 230.68,
|
||||
last: 377.33,
|
||||
change: -39,
|
||||
},
|
||||
{
|
||||
category: "周環比",
|
||||
category: t("dashboard.weekly_relative_change"),
|
||||
this: 608.01,
|
||||
last: 2711.09,
|
||||
change: -78,
|
||||
},
|
||||
{
|
||||
category: "月環比",
|
||||
category: t("dashboard.monthly_relative_change"),
|
||||
this: 6473.8,
|
||||
last: 12701.69,
|
||||
change: -49,
|
||||
},
|
||||
{
|
||||
category: "年環比",
|
||||
category: t("dashboard.yearly_relative_change"),
|
||||
this: 46687.17,
|
||||
last: null,
|
||||
change: null,
|
||||
},
|
||||
]);
|
||||
|
||||
const labels = ["今日", "昨日", "本周", "上周", "本月", "上月", "今年", "去年"];
|
||||
const labels = [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({
|
||||
@ -161,7 +163,7 @@ const barChartOptions = ref({
|
||||
<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">環比能耗</h2>
|
||||
<h2 class="font-light">{{ $t("dashboard.relative_energy_consumption") }}</h2>
|
||||
</div>
|
||||
<div class="h-[180px]">
|
||||
<BarChart
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
// 假資料 - 設備能耗列表
|
||||
const mockEnergyData = {
|
||||
monthly: [
|
||||
@ -47,9 +48,9 @@ const getCurrentEnergyData = () => {
|
||||
<div class="state-box">
|
||||
<!-- 標題和切換按鈕 -->
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="font-light w-1/2 relative">當月能耗排行</h2>
|
||||
<h2 class="font-light relative">{{$t("dashboard.energy_ranking")}}</h2>
|
||||
<button @click="toggleEnergyType" class="btn btn-info btn-xs">
|
||||
{{ currentEnergyType === "monthly" ? "本日能耗" : "當月能耗" }}
|
||||
{{ currentEnergyType === "monthly" ? t("dashboard.today_energy_consumption") : t("dashboard.this_month_energy_consumption") }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import * as echarts from "echarts";
|
||||
import BarChart from "@/components/chart/BarChart.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
@ -113,24 +113,25 @@ const generateCylinderChartOption = (data) => {
|
||||
|
||||
const weekComparisonOption = generateCylinderChartOption(energyData.value);
|
||||
|
||||
onMounted(() => {
|
||||
if (
|
||||
storeBuild.deptList.length > 0 &&
|
||||
storeBuild.floorList.length > 0
|
||||
) {
|
||||
watch(
|
||||
() => [storeBuild.deptList.length, storeBuild.floorList.length],
|
||||
([deptListLength, floorListLength]) => {
|
||||
if (deptListLength > 0 && floorListLength > 0) {
|
||||
formState.value = {
|
||||
...formState.value,
|
||||
floorId: storeBuild.floorList[0].key,
|
||||
deptId: storeBuild.deptList[0].key,
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
{ 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">近30天能耗趨勢</h2>
|
||||
<h2 class="font-light">{{$t("dashboard.last_30_days_energy_trend")}}</h2>
|
||||
<div class="flex items-center w-52 gap-4">
|
||||
<Select
|
||||
:value="formState"
|
||||
|
@ -5,121 +5,6 @@ import { twMerge } from "tailwind-merge";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
const store = useBuildingStore();
|
||||
const router = useRouter();
|
||||
// 假資料
|
||||
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) => {
|
||||
router.push({
|
||||
|
@ -7,7 +7,7 @@ import { getCarbonValue } from "@/apis/energy";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
const store = useBuildingStore();
|
||||
const { t, locale } = useI18n();
|
||||
const { taipower_data } = inject("energy_data");
|
||||
const { taipower_data, carbonValue } = inject("energy_data");
|
||||
const carbonData = ref(null);
|
||||
const defaultChartOption = ref({
|
||||
tooltip: {
|
||||
@ -86,6 +86,7 @@ const getData = async () => {
|
||||
if (store.selectedBuilding.building_guid) {
|
||||
const res = await getCarbonValue(store.selectedBuilding.building_guid);
|
||||
carbonData.value = res.data[0];
|
||||
carbonValue.value = res.data[0].coefficient;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick, computed } from "vue";
|
||||
import { ref, onMounted, nextTick, watch, inject } from "vue";
|
||||
import * as echarts from "echarts";
|
||||
import { getRealTimeDist } from "@/apis/energy";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { search_data } = inject("energy_data");
|
||||
const { t } = useI18n();
|
||||
|
||||
const chartDiv = ref(null);
|
||||
@ -39,7 +39,7 @@ const chartOption = {
|
||||
borderWidth: 0,
|
||||
},
|
||||
lineStyle: {
|
||||
color: 'gradient',
|
||||
color: "gradient",
|
||||
opacity: 0.7,
|
||||
curveness: 0.5,
|
||||
},
|
||||
@ -47,10 +47,11 @@ const chartOption = {
|
||||
],
|
||||
};
|
||||
|
||||
const loadData = async () => {
|
||||
const res = await getRealTimeDist();
|
||||
if (res.isSuccess) {
|
||||
const loadData = async (value) => {
|
||||
const res = await getRealTimeDist(value);
|
||||
if (res.isSuccess && res.data && res.data.length !== 0) {
|
||||
const rawData = res.data;
|
||||
if (rawData) {
|
||||
const totalValue = rawData.reduce((acc, item) => acc + item.value, 0);
|
||||
|
||||
const sortedData = [...rawData].sort((a, b) => b.value - a.value);
|
||||
@ -72,7 +73,14 @@ const loadData = async () => {
|
||||
percentage: item.percentage,
|
||||
}));
|
||||
|
||||
const colors = ["#45f4ef", "#17CEE3", "#E4EA00", "#62E39A", "#E9971F", "#E52EFF"];
|
||||
const colors = [
|
||||
"#45f4ef",
|
||||
"#17CEE3",
|
||||
"#E4EA00",
|
||||
"#62E39A",
|
||||
"#E9971F",
|
||||
"#E52EFF",
|
||||
];
|
||||
// 更新 chartOption
|
||||
chartOption.series[0].data = data.map((item, index) => ({
|
||||
...item,
|
||||
@ -86,11 +94,27 @@ const loadData = async () => {
|
||||
const myChart = echarts.init(chartDiv.value);
|
||||
myChart.setOption(chartOption);
|
||||
}
|
||||
} else {
|
||||
// 初始化圖表
|
||||
echarts.init(chartDiv.value).clear();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
watch(
|
||||
search_data,
|
||||
(newValue, oldValue) => {
|
||||
if (
|
||||
newValue.building_guid &&
|
||||
JSON.stringify(newValue) !== JSON.stringify(oldValue)
|
||||
) {
|
||||
loadData(newValue);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, provide } from "vue";
|
||||
import { ref, computed, provide, watch } from "vue";
|
||||
import ImmediateDemandChart from "./ImmediateDemandChart.vue";
|
||||
import ElecConsumption from "./ElecConsumption.vue";
|
||||
import UsageInformation from "./UsageInformation.vue";
|
||||
@ -28,25 +28,80 @@ const {
|
||||
} = useActiveBtn("multiple");
|
||||
|
||||
const taipower_data = ref([]);
|
||||
const getData = async () => {
|
||||
const res = await getTaipower();
|
||||
const carbonValue = ref(null);
|
||||
const search_data = computed(() => {
|
||||
return {
|
||||
coefficient: carbonValue.value,
|
||||
building_guid: storeBuild.selectedBuilding?.building_guid || null,
|
||||
department_id_list: selectedDeptItems.value.map((item) => item.key),
|
||||
floor_guid_list: selectedFloorItems.value.map((item) => item.key),
|
||||
};
|
||||
});
|
||||
|
||||
const getData = async (value) => {
|
||||
const res = await getTaipower(value);
|
||||
if (res.isSuccess) {
|
||||
taipower_data.value = res.data;
|
||||
taipower_data.value = res.data
|
||||
? res.data.sort((a, b) => a.month.localeCompare(b.month))
|
||||
: [];
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
setDeptItems(storeBuild.deptList);
|
||||
setFloorItems(storeBuild.floorList);
|
||||
});
|
||||
watch(
|
||||
search_data,
|
||||
(newValue, oldValue) => {
|
||||
if (
|
||||
newValue.building_guid &&
|
||||
newValue.coefficient &&
|
||||
JSON.stringify(newValue) !== JSON.stringify(oldValue)
|
||||
) {
|
||||
getData(newValue);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
provide("energy_data", { taipower_data });
|
||||
watch(
|
||||
() => storeBuild.floorList,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
const floorList = newValue.map((d) => ({
|
||||
...d,
|
||||
active: true,
|
||||
}));
|
||||
setFloorItems(floorList);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => storeBuild.deptList,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
const deptList = newValue.map((d) => ({
|
||||
...d,
|
||||
active: true,
|
||||
}));
|
||||
setDeptItems(deptList);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
provide("energy_data", { taipower_data, search_data, carbonValue });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-wrap items-center mb-4">
|
||||
<div class="w-full border border-info px-4 py-2 rounded my-3">
|
||||
<div class="w-full border border-info px-4 py-2 rounded mt-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-md font-extrabold">{{ $t("energy.floor") }} :</span>
|
||||
<ButtonGroup
|
||||
|
@ -1,9 +1,9 @@
|
||||
<script setup>
|
||||
import BarChart from "@/components/chart/BarChart.vue";
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import { ref, watch, computed, inject } from "vue";
|
||||
import { getElecUseDay } from "@/apis/energy";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { search_data } = inject("energy_data");
|
||||
const { t } = useI18n();
|
||||
const dataSource = ref([]);
|
||||
const dateRange = ref({
|
||||
@ -107,10 +107,12 @@ const chartOption = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
const loadData = async () => {
|
||||
const res = await getElecUseDay();
|
||||
const loadData = async (value) => {
|
||||
const res = await getElecUseDay(value);
|
||||
if (res.isSuccess) {
|
||||
dataSource.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||
dataSource.value = res.data
|
||||
.sort((a, b) => a.time.localeCompare(b.time))
|
||||
.map((d) => ({ ...d, key: d.id }));
|
||||
|
||||
const dates = res.data.map((d) => d.time.split(" ")[0]);
|
||||
dateRange.value = {
|
||||
@ -120,9 +122,21 @@ const loadData = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
watch(
|
||||
search_data,
|
||||
(newValue, oldValue) => {
|
||||
if (
|
||||
newValue.building_guid &&
|
||||
JSON.stringify(newValue) !== JSON.stringify(oldValue)
|
||||
) {
|
||||
loadData(newValue);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -19,7 +19,7 @@ const calculateData = () => {
|
||||
item.month.startsWith(currentYear)
|
||||
);
|
||||
|
||||
const totalElecBills = filteredData.reduce((sum, item) => sum + item.kWh, 0);
|
||||
const totalElecBills = filteredData.reduce((sum, item) => sum + item.costTotal, 0);
|
||||
const latestMonthData = filteredData[filteredData.length - 1];
|
||||
const latestMonth = latestMonthData ? latestMonthData.month : "";
|
||||
const monthDays = latestMonth ? daysInMonth(latestMonth) : 0;
|
||||
|
@ -3,7 +3,7 @@ import { computed, defineProps, inject, ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { getHistoryData, getHistoryExportData } from "@/apis/history";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { searchParams } = useSearchParam();
|
||||
const route = useRoute();
|
||||
@ -26,7 +26,6 @@ const cancelToastOpen = () => {
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
const submit = async (e, type = "") => {
|
||||
e?.preventDefault();
|
||||
e?.stopPropagation();
|
||||
@ -53,8 +52,13 @@ const submit = async (e, type = "") => {
|
||||
} else {
|
||||
const res = await getHistoryData({
|
||||
...searchParams.value,
|
||||
Type: 1,
|
||||
table_type:route.params.type
|
||||
Type:
|
||||
route.params.type != 1
|
||||
? 2
|
||||
: searchParams.value.Type
|
||||
? searchParams.value.Type
|
||||
: 1,
|
||||
table_type: route.params.type,
|
||||
});
|
||||
updateTableData(res.data);
|
||||
}
|
||||
@ -121,7 +125,12 @@ watch(
|
||||
status="info"
|
||||
:cancel="cancelToastOpen"
|
||||
/>
|
||||
<ButtonGroup class="ml-5" :items="submitBtns" :withLine="false" :withBtnClass="true"/>
|
||||
<ButtonGroup
|
||||
class="ml-5"
|
||||
:items="submitBtns"
|
||||
:withLine="false"
|
||||
:withBtnClass="true"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
@ -64,7 +64,7 @@ const formatChartData = (data) => {
|
||||
const seriesKey = `${item.device_name || ""}_${item.item_name || ""}`;
|
||||
acc[seriesKey] = {
|
||||
timestamps: item.data.map((d) =>
|
||||
dayjs(d.timestamp).format("YYYY-MM-DD HH:mm")
|
||||
dayjs(d.time).format("YYYY-MM-DD HH:mm")
|
||||
),
|
||||
values: item.data.map((d) =>
|
||||
d.value == "無資料" ? null : parseFloat(d.value)
|
||||
@ -73,7 +73,6 @@ const formatChartData = (data) => {
|
||||
minValue: parseFloat(item.minValue),
|
||||
averageValue: parseFloat(item.averageValue),
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
@ -86,8 +85,7 @@ watch(
|
||||
const formattedData = formatChartData(newData);
|
||||
|
||||
const series = Object.keys(formattedData).map((seriesKey, index) => {
|
||||
const { maxValue, minValue, averageValue } =
|
||||
formattedData[seriesKey];
|
||||
const { maxValue, minValue, averageValue } = formattedData[seriesKey];
|
||||
return {
|
||||
name: seriesKey,
|
||||
type: "line",
|
||||
|
@ -65,7 +65,7 @@ const getElecType = async () => {
|
||||
...d,
|
||||
title: d.name,
|
||||
key: d.id,
|
||||
active: false,
|
||||
active: true,
|
||||
}));
|
||||
setElecTypeItems(elecType);
|
||||
};
|
||||
@ -152,8 +152,23 @@ watch(
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => storeBuild.deptList,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
const deptList = newValue.map((d) => ({
|
||||
...d,
|
||||
active: true,
|
||||
}));
|
||||
setDeptItems(deptList);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
setDeptItems(storeBuild.deptList);
|
||||
getElecType();
|
||||
});
|
||||
</script>
|
||||
|
@ -4,14 +4,40 @@ import useSearchParam from "@/hooks/useSearchParam";
|
||||
import dayjs from "dayjs";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useRoute } from "vue-router";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
const route = useRoute();
|
||||
const {
|
||||
items: searchTypeItems,
|
||||
changeActiveBtn: changeTypeActiveBtn,
|
||||
setItems: setTypeItems,
|
||||
selectedBtn: selectedTypeItems,
|
||||
} = useActiveBtn();
|
||||
|
||||
const itemsForStartTime = ref([]);
|
||||
|
||||
const itemsForEndTime = ref();
|
||||
|
||||
const initializeItems = () => {
|
||||
setTypeItems([
|
||||
{
|
||||
title: t("history.date_range"),
|
||||
key: 1,
|
||||
active: searchParams.value.Type
|
||||
? parseInt(searchParams.value.Type) === 1
|
||||
: true,
|
||||
},
|
||||
{
|
||||
title: t("history.time_range"),
|
||||
key: 2,
|
||||
active: searchParams.value.Type
|
||||
? parseInt(searchParams.value.Type) === 2
|
||||
: false,
|
||||
},
|
||||
]);
|
||||
|
||||
itemsForStartTime.value = [
|
||||
{
|
||||
key: "Start_date",
|
||||
@ -101,6 +127,13 @@ watch(
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(selectedTypeItems, (newValue) => {
|
||||
changeParams({
|
||||
...searchParams.value,
|
||||
Type: newValue.key,
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -108,6 +141,16 @@ watch(
|
||||
<h2 class="text-lg font-bold ps-2 whitespace-nowrap">
|
||||
{{ $t("history.date_range") }} :
|
||||
</h2>
|
||||
<ButtonGroup
|
||||
v-if="route.params.type == 1"
|
||||
:items="searchTypeItems"
|
||||
:withLine="true"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeTypeActiveBtn(item);
|
||||
}
|
||||
"
|
||||
/>
|
||||
<DateGroup class="mr-3" :items="itemsForStartTime" :withLine="true" />
|
||||
<DateGroup :items="itemsForEndTime" :withLine="true" />
|
||||
</div>
|
||||
|
@ -6,13 +6,14 @@ import useSearchParam from "@/hooks/useSearchParam";
|
||||
import { getHistorySideBar } from "@/apis/history";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const storeBuild = useBuildingStore();
|
||||
const buildingGuid = computed(() => storeBuild.selectedBuilding?.building_guid);
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
const { deptData, elecType, subSystem } = inject("energy_table_data");
|
||||
const selectedBuilding = ref([]);
|
||||
const deviceData = ref([]);
|
||||
const searchTerm = ref(""); //搜尋文字
|
||||
const activeSearchTerm = ref("");
|
||||
|
||||
const getDeviceData = async ({
|
||||
sub_system_tag,
|
||||
department_id,
|
||||
@ -22,6 +23,7 @@ const getDeviceData = async ({
|
||||
sub_system_tag,
|
||||
department_id,
|
||||
elec_type_id,
|
||||
building_guid: buildingGuid.value,
|
||||
});
|
||||
deviceData.value = (res.data || []).map((building) => ({
|
||||
building_tag: building.building_tag,
|
||||
|
@ -47,7 +47,8 @@ const getElecType = async () => {
|
||||
const elecType = res.data.map((d, index) => ({
|
||||
...d,
|
||||
title: d.name,
|
||||
key: d.id
|
||||
key: d.id,
|
||||
active: true,
|
||||
}));
|
||||
setElecItems(elecType);
|
||||
};
|
||||
@ -99,9 +100,39 @@ watch(
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => storeBuild.deptList,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
const deptList = newValue.map((d) => ({
|
||||
...d,
|
||||
active: true,
|
||||
}));
|
||||
setDeptItems(deptList);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => storeBuild.floorList,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
const floorList = newValue.map((d) => ({
|
||||
...d,
|
||||
active: true,
|
||||
}));
|
||||
setFloorItems(floorList);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
setDeptItems(storeBuild.deptList);
|
||||
setFloorItems(storeBuild.floorList);
|
||||
getElecType();
|
||||
});
|
||||
</script>
|
||||
|
@ -65,7 +65,7 @@ const updateFileList = (files) => {
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
onSubSysClick(options.value[0].key);
|
||||
// onSubSysClick(options.value[0].key);
|
||||
updateFileList([]);
|
||||
};
|
||||
|
||||
|
@ -122,29 +122,15 @@ const getPoint = async (deviceList) => {
|
||||
};
|
||||
|
||||
watch(
|
||||
selectedPoints,
|
||||
(newVal, oldVal) => {
|
||||
[selectedPoints, selectedDeptItems],
|
||||
([newPoints, newDeptItems], [oldPoints, oldDeptItems]) => {
|
||||
changeParams({
|
||||
...searchParams.value,
|
||||
Points: newVal.map((d) => d.points),
|
||||
Points: newPoints.map((d) => d.points),
|
||||
Dept: newDeptItems.map((d) => d.id),
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
selectedDeptItems,
|
||||
(newVal, oldVal) => {
|
||||
changeParams({
|
||||
...searchParams.value,
|
||||
Dept: newVal.map((d) => d.id),
|
||||
});
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const form = ref(null);
|
||||
@ -154,14 +140,32 @@ watch(searchParams, (newVal, oldValue) => {
|
||||
(newVal?.Device_list?.length && typeof oldValue.Device_list === "string") ||
|
||||
(newVal?.Device_list?.length && !oldValue?.Device_list?.length)
|
||||
) {
|
||||
if (newVal?.Device_list[0]!==null){
|
||||
getPoint(
|
||||
typeof newVal.Device_list == "string"
|
||||
? [newVal.Device_list]
|
||||
: newVal.Device_list
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => store.deptList,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
const deptList = newValue.map((d) => ({
|
||||
...d,
|
||||
active: true,
|
||||
}));
|
||||
setDeptItems(deptList);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
setMainSysItems(
|
||||
store.mainSubSys.map(({ full_name, main_system_tag }, index) => ({
|
||||
@ -170,13 +174,6 @@ onMounted(() => {
|
||||
active: index === 0,
|
||||
}))
|
||||
);
|
||||
setDeptItems(
|
||||
store.deptList.map((d, index) => ({
|
||||
...d,
|
||||
title: d.name,
|
||||
key: d.id,
|
||||
}))
|
||||
);
|
||||
});
|
||||
|
||||
onBeforeMount(() => {
|
||||
|
@ -5,17 +5,23 @@ import useBuildingStore from "@/stores/useBuildingStore";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import { getHistorySideBar } from "@/apis/history";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
|
||||
const storeBuild = useBuildingStore();
|
||||
const selectedBuilding = ref([]);
|
||||
const deviceData = ref([]);
|
||||
const searchTerm = ref(""); //搜尋文字
|
||||
const activeSearchTerm = ref("");
|
||||
|
||||
const getDeviceData = async (sub_system_tag, department_id) => {
|
||||
const deptArray = department_id? department_id.map(Number):null;
|
||||
const res = await getHistorySideBar({sub_system_tag: sub_system_tag,department_id:deptArray});
|
||||
const deptArray = department_id ? department_id.map(Number) : [];
|
||||
const res = await getHistorySideBar({
|
||||
sub_system_tag: sub_system_tag,
|
||||
department_id: deptArray,
|
||||
elec_type_id: [],
|
||||
building_guid: storeBuild.selectedBuilding?.building_guid || null,
|
||||
});
|
||||
deviceData.value = res.data.map((building) => ({
|
||||
building_tag: building.building_tag,
|
||||
building_name: building.building_name,
|
||||
@ -30,9 +36,7 @@ const getDeviceData = async (sub_system_tag,department_id) => {
|
||||
}));
|
||||
|
||||
selectedBuilding.value = res.data.map((d) => d.building_tag);
|
||||
changeSelected(
|
||||
[res.data[0]?.floor_list[0]?.device_list[0]?.device_number]
|
||||
);
|
||||
changeSelected([res.data[0]?.floor_list[0]?.device_list[0]?.device_number]);
|
||||
};
|
||||
|
||||
const sysIsExisted = (building_tag) => {
|
||||
@ -69,10 +73,7 @@ watch(
|
||||
watch(
|
||||
searchParams,
|
||||
(newVal, oldVal) => {
|
||||
if (
|
||||
newVal.Dept &&
|
||||
newVal.Dept?.length != oldVal?.Dept?.length
|
||||
) {
|
||||
if (newVal.Dept && newVal.Dept?.length != oldVal?.Dept?.length) {
|
||||
getDeviceData(searchParams.value.sub_system_tag, newVal.Dept);
|
||||
}
|
||||
},
|
||||
@ -182,15 +183,19 @@ const changeSelected = (Device_list, renew = false) => {
|
||||
|
||||
const filteredDeviceData = computed(() => {
|
||||
if (!activeSearchTerm.value) return deviceData.value;
|
||||
return deviceData.value.map((building) => ({
|
||||
return deviceData.value
|
||||
.map((building) => ({
|
||||
...building,
|
||||
floors: building.floors.map((floor) => ({
|
||||
floors: building.floors
|
||||
.map((floor) => ({
|
||||
...floor,
|
||||
devices: floor.devices.filter((device) =>
|
||||
device.device_name.includes(activeSearchTerm.value)
|
||||
),
|
||||
})).filter(floor => floor.devices.length > 0)
|
||||
})).filter(building => building.floors.length > 0);
|
||||
}))
|
||||
.filter((floor) => floor.devices.length > 0),
|
||||
}))
|
||||
.filter((building) => building.floors.length > 0);
|
||||
});
|
||||
|
||||
const handleSearch = (e) => {
|
||||
@ -222,7 +227,10 @@ const handleInput = (e) => {
|
||||
/>
|
||||
</label>
|
||||
<ul class="menu text-lg">
|
||||
<template v-for="building in filteredDeviceData" :key="building.building_tag">
|
||||
<template
|
||||
v-for="building in filteredDeviceData"
|
||||
:key="building.building_tag"
|
||||
>
|
||||
<li>
|
||||
<details :open="selectedBuilding.includes(building.building_tag)">
|
||||
<summary>
|
||||
|
@ -9,6 +9,7 @@ import Floors from "./components/Floors.vue";
|
||||
import Building from "./components/Building.vue";
|
||||
import ElecPriceManagement from "./components/ElecPriceManagement.vue";
|
||||
import MQTTList from "./components/MQTTList.vue";
|
||||
import Demand from "./components/Demand.vue";
|
||||
// import PointList from "./components/PointList.vue";
|
||||
|
||||
const route = useRoute();
|
||||
@ -31,6 +32,8 @@ const updateComponent = () => {
|
||||
currentComponent.value = ElecPriceManagement;
|
||||
} else if (sub_system_id === "MQTT_Result") {
|
||||
currentComponent.value = MQTTList;
|
||||
} else if (sub_system_id === "Demand") {
|
||||
currentComponent.value = Demand;
|
||||
}
|
||||
};
|
||||
|
||||
|
169
src/views/setting/components/Demand.vue
Normal file
169
src/views/setting/components/Demand.vue
Normal file
@ -0,0 +1,169 @@
|
||||
<script setup>
|
||||
import { onMounted, ref, inject, computed, watch } from "vue";
|
||||
import { getAssetList, postAssetElecSetting } from "@/apis/asset";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
import { getElecTypeList } from "@/apis/asset";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
||||
const storeBuild = useBuildingStore();
|
||||
const departmentList = computed(() => storeBuild.deptList);
|
||||
const floors = computed(() => storeBuild.floorList);
|
||||
const elecType = ref([]);
|
||||
const isEditing = ref(false);
|
||||
const tableData = ref([]);
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: t("assetManagement.operation"),
|
||||
key: "operation",
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: t("assetManagement.device_number"),
|
||||
key: "device_number",
|
||||
class: "break-all",
|
||||
},
|
||||
{
|
||||
title: t("assetManagement.device_name"),
|
||||
key: "full_name",
|
||||
filter: true,
|
||||
class: "break-all",
|
||||
},
|
||||
{
|
||||
title: t("assetManagement.floor"),
|
||||
key: "floor",
|
||||
filter: true,
|
||||
sort: true,
|
||||
},
|
||||
{
|
||||
title: t("assetManagement.department"),
|
||||
key: "department",
|
||||
filter: true,
|
||||
},
|
||||
{
|
||||
title: t("energy.electricity_classification"),
|
||||
key: "elecType",
|
||||
filter: true,
|
||||
},
|
||||
]);
|
||||
|
||||
const getElecType = async () => {
|
||||
const res = await getElecTypeList();
|
||||
elecType.value = res.data.map((d, index) => ({
|
||||
...d,
|
||||
key: d.id,
|
||||
}));
|
||||
};
|
||||
|
||||
const getAssetData = async () => {
|
||||
// Electricity Meter P3
|
||||
const res = await getAssetList(465);
|
||||
if (res.isSuccess) {
|
||||
tableData.value = res.data.map((d) => ({
|
||||
...d,
|
||||
key: d.id,
|
||||
floor: floors.value.find(({ floor_guid }) => d.floor_guid === floor_guid)
|
||||
?.full_name,
|
||||
department: departmentList.value.find(({ id }) => d.department_id === id)
|
||||
?.name,
|
||||
elecType: elecType.value.find(({ id }) => d.elec_type_id === id)?.name,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const onOk = async () => {
|
||||
const formData = tableData.value.map((item) => ({
|
||||
main_id: item.main_id,
|
||||
is_demand: item.is_demand,
|
||||
}));
|
||||
const res = await postAssetElecSetting(formData);
|
||||
if (res.isSuccess) {
|
||||
onCancel();
|
||||
} else {
|
||||
openToast("error", res.msg);
|
||||
}
|
||||
};
|
||||
|
||||
const onCancel = async () => {
|
||||
isEditing.value = false;
|
||||
await getAssetData();
|
||||
};
|
||||
|
||||
const Check = (id) => {
|
||||
const items = tableData.value.find((d) => d.main_id === id);
|
||||
items.is_demand = items.is_demand ? 0 : 1;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getElecType();
|
||||
});
|
||||
|
||||
watch(
|
||||
[floors, departmentList],
|
||||
() => {
|
||||
if (floors.value.length > 0 && departmentList.value.length > 0) {
|
||||
getAssetData();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-start items-center mt-10 mb-5">
|
||||
<h3 class="text-xl mr-5">電表</h3>
|
||||
<button
|
||||
v-if="!isEditing"
|
||||
class="btn btn-sm btn-add mr-3"
|
||||
@click.stop.prevent="isEditing = true"
|
||||
>
|
||||
<font-awesome-icon :icon="['fas', 'pencil-alt']" />{{
|
||||
$t("button.start_edit")
|
||||
}}
|
||||
</button>
|
||||
<template v-else>
|
||||
<button class="btn btn-sm btn-add mr-3" @click.prevent="onOk()">
|
||||
<font-awesome-icon :icon="['fas', 'save']" />{{ $t("button.confirm") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-outline-info mr-3"
|
||||
@click.stop.prevent="onCancel()"
|
||||
>
|
||||
<font-awesome-icon :icon="['fas', 'times']" />{{ $t("button.cancel") }}
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
<Table :columns="columns" :dataSource="tableData" class="mt-3">
|
||||
<template #bodyCell="{ record, column, index }">
|
||||
<template v-if="column.key === 'operation'">
|
||||
<template v-if="!isEditing">
|
||||
<FontAwesomeIcon
|
||||
v-if="record.is_demand"
|
||||
:icon="['fas', 'check-circle']"
|
||||
size="lg"
|
||||
class="text-gray-300"
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
v-else
|
||||
:icon="['far', 'circle']"
|
||||
size="lg"
|
||||
class="text-gray-300"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<Checkbox
|
||||
:checked="record.is_demand"
|
||||
@click="() => Check(record.main_id)"
|
||||
></Checkbox>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ record[column.key] }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped></style>
|
@ -20,7 +20,7 @@ const onOk = async (sheet, data) => {
|
||||
getData();
|
||||
onCancel(sheet);
|
||||
} else {
|
||||
openToast("error", res.msg, "#immediate_demand_add_item");
|
||||
openToast("error", res.msg);
|
||||
}
|
||||
};
|
||||
|
||||
@ -53,7 +53,7 @@ watch(
|
||||
|
||||
<template>
|
||||
<div class="flex justify-start items-center my-5">
|
||||
<h3 class="text-xl mr-5">簡易型時間電價二段式</h3>
|
||||
<h3 class="text-xl mr-5">{{ $t("energy.simple_elec_price_two_stage") }}</h3>
|
||||
<button
|
||||
v-if="!sim2isEditing"
|
||||
class="btn btn-sm btn-add mr-3"
|
||||
@ -81,16 +81,16 @@ watch(
|
||||
<table class="">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="6" class="bg-teal-800 bg-opacity-20">分類</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">夏月<br />(6/1~9/30)</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">非夏月<br />(夏月以外的時間)</th>
|
||||
<th colspan="6" class="bg-teal-800 bg-opacity-20">{{ $t("energy.classification") }}</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">{{ $t("energy.summer_months") }}<br />(6/1~9/30)</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">{{ $t("energy.non_summer_months") }}<br />({{ $t("energy.time_outside_summer_months") }})</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="bg-teal-800 bg-opacity-40">基本電費</td>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">按戶計收</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">每戶每月</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">{{$t("energy.fixed_elec_cost")}}</td>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">{{$t("energy.charged_per_household")}}</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">{{$t("energy.per_household_month")}}</td>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
type="number"
|
||||
@ -100,12 +100,12 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="6">流動電費</td>
|
||||
<td rowspan="4">週一~週五</td>
|
||||
<td rowspan="2" class="bg-rose-600 bg-opacity-70">尖峰時間</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">夏月</td>
|
||||
<td rowspan="6">{{$t("energy.var_elec_cost")}}</td>
|
||||
<td rowspan="4">{{$t("energy.mon_to_friday")}}</td>
|
||||
<td rowspan="2" class="bg-rose-600 bg-opacity-70">{{$t("energy.peak_hours")}}</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">{{$t("energy.summer_months")}}</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">09:00 ~ 24:00</td>
|
||||
<td rowspan="5">每度</td>
|
||||
<td rowspan="5">{{$t("energy.price_per_kwh")}}</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">
|
||||
<input
|
||||
type="number"
|
||||
@ -116,7 +116,7 @@ watch(
|
||||
<td class="bg-rose-600 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-rose-600 bg-opacity-20">非夏月</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">{{$t("energy.non_summer_months")}}</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">
|
||||
06:00 ~ 11:00<br />14:00 ~ 24:00
|
||||
</td>
|
||||
@ -130,8 +130,8 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2" class="bg-green-600 bg-opacity-60">離峰時間</td>
|
||||
<td class="bg-green-600 bg-opacity-20">夏月</td>
|
||||
<td rowspan="2" class="bg-green-600 bg-opacity-60">{{$t("energy.off_peak_hours")}}</td>
|
||||
<td class="bg-green-600 bg-opacity-20">{{$t("energy.summer_months")}}</td>
|
||||
<td class="bg-green-600 bg-opacity-20">00:00 ~ 09:00</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
<input
|
||||
@ -143,7 +143,7 @@ watch(
|
||||
<td class="bg-green-600 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-green-600 bg-opacity-20">非夏月</td>
|
||||
<td class="bg-green-600 bg-opacity-20">{{$t("energy.non_summer_months")}}</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
00:00 ~ 06:00<br />11:00 ~ 14:00
|
||||
</td>
|
||||
@ -157,9 +157,9 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>週六、週日<br />及離峰日</td>
|
||||
<td class="bg-green-600 bg-opacity-60">離峰時間</td>
|
||||
<td colspan="2" class="bg-green-600 bg-opacity-20">全日</td>
|
||||
<td>{{$t("energy.sat_sun_off_peak_days")}}</td>
|
||||
<td class="bg-green-600 bg-opacity-60">{{$t("energy.off_peak_hours")}}</td>
|
||||
<td colspan="2" class="bg-green-600 bg-opacity-20">{{$t("energy.all_day")}}</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
<input
|
||||
type="number"
|
||||
@ -176,11 +176,11 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4">每月總度數超過2000度之部分</td>
|
||||
<td>每度</td>
|
||||
<td colspan="4">{{$t("energy.usage_over_2000kwh")}}</td>
|
||||
<td>{{$t("energy.price_per_kwh")}}</td>
|
||||
<td colspan="2" class="!text-start">
|
||||
<div class="flex items-center">
|
||||
加
|
||||
{{$t("energy.add")}}
|
||||
<input
|
||||
type="number"
|
||||
v-model.number="sim2NewValue[7]"
|
||||
@ -193,7 +193,7 @@ watch(
|
||||
</table>
|
||||
|
||||
<div class="flex justify-start items-center mt-16">
|
||||
<h3 class="text-xl mr-5">簡易型時間電價三段式</h3>
|
||||
<h3 class="text-xl mr-5"> {{$t("energy.simple_elec_price_three_stage")}}</h3>
|
||||
<button
|
||||
v-if="!sim3isEditing"
|
||||
class="btn btn-sm btn-add mr-3"
|
||||
@ -221,16 +221,16 @@ watch(
|
||||
<table class="my-5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="6" class="bg-teal-800 bg-opacity-20">分類</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">夏月<br />(6/1~9/30)</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">非夏月<br />(夏月以外的時間)</th>
|
||||
<th colspan="6" class="bg-teal-800 bg-opacity-20">{{$t("energy.classification")}}</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">{{$t("energy.summer_months")}}<br />(6/1~9/30)</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">{{$t("energy.non_summer_months")}}<br />({{$t("energy.time_outside_summer_months")}})</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="bg-teal-800 bg-opacity-40">基本電費</td>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">按戶計收</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">每戶每月</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">{{$t("energy.fixed_elec_cost")}}</td>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">{{$t("energy.charged_per_household")}}</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">{{$t("energy.per_household_month")}}</td>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
type="number"
|
||||
@ -240,12 +240,12 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="7">流動電費</td>
|
||||
<td rowspan="5">週一~週五</td>
|
||||
<td class="bg-rose-600 bg-opacity-70">尖峰時間</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">夏月</td>
|
||||
<td rowspan="7">{{$t("energy.var_elec_cost")}}</td>
|
||||
<td rowspan="5">{{$t("energy.mon_to_friday")}}</td>
|
||||
<td class="bg-rose-600 bg-opacity-70">{{$t("energy.peak_hours")}}</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">{{$t("energy.summer_months")}}</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">16:00 ~ 22:00</td>
|
||||
<td rowspan="6">每度</td>
|
||||
<td rowspan="6">{{$t("energy.price_per_kwh")}}</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">
|
||||
<input
|
||||
type="number"
|
||||
@ -256,8 +256,8 @@ watch(
|
||||
<td class="bg-rose-600 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2" class="bg-yellow-400 bg-opacity-80">半尖峰時間</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">夏月</td>
|
||||
<td rowspan="2" class="bg-yellow-400 bg-opacity-80">{{$t("energy.semi_peak_hours")}}</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">{{$t("energy.summer_months")}}</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">
|
||||
09:00 ~ 16:00<br />22:00~24:00
|
||||
</td>
|
||||
@ -271,7 +271,7 @@ watch(
|
||||
<td class="bg-yellow-500 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-yellow-500 bg-opacity-20">非夏月</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">{{$t("energy.non_summer_months")}}</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">
|
||||
06:00 ~ 11:00<br />14:00 ~ 24:00
|
||||
</td>
|
||||
@ -285,8 +285,8 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2" class="bg-green-600 bg-opacity-60">離峰時間</td>
|
||||
<td class="bg-green-600 bg-opacity-20">夏月</td>
|
||||
<td rowspan="2" class="bg-green-600 bg-opacity-60">{{$t("energy.off_peak_hours")}}</td>
|
||||
<td class="bg-green-600 bg-opacity-20">{{$t("energy.summer_months")}}</td>
|
||||
<td class="bg-green-600 bg-opacity-20">00:00 ~ 09:00</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
<input
|
||||
@ -298,7 +298,7 @@ watch(
|
||||
<td class="bg-green-600 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-green-600 bg-opacity-20">非夏月</td>
|
||||
<td class="bg-green-600 bg-opacity-20">{{$t("energy.non_summer_months")}}</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
00:00 ~ 06:00<br />11:00 ~ 14:00
|
||||
</td>
|
||||
@ -312,9 +312,9 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>週六、週日<br />及離峰日</td>
|
||||
<td colspan="2" class="bg-green-600 bg-opacity-60">離峰時間</td>
|
||||
<td class="bg-green-600 bg-opacity-20">全日</td>
|
||||
<td>{{$t("energy.sat_sun_off_peak_days")}}</td>
|
||||
<td colspan="2" class="bg-green-600 bg-opacity-60">{{$t("energy.off_peak_hours")}}</td>
|
||||
<td class="bg-green-600 bg-opacity-20">{{$t("energy.all_day")}}</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
<input
|
||||
type="number"
|
||||
@ -331,11 +331,11 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4">每月總度數超過2000度之部分</td>
|
||||
<td>每度</td>
|
||||
<td colspan="4">{{$t("energy.usage_over_2000kwh")}}</td>
|
||||
<td>{{$t("energy.price_per_kwh")}}</td>
|
||||
<td colspan="2" class="!text-start">
|
||||
<div class="flex items-center">
|
||||
加
|
||||
{{$t("energy.add")}}
|
||||
<input
|
||||
type="number"
|
||||
v-model.number="sim3NewValue[8]"
|
||||
|
@ -53,7 +53,9 @@ watch(
|
||||
|
||||
<template>
|
||||
<div class="flex justify-start items-center my-5">
|
||||
<h3 class="text-xl mr-5">標準型時間電價二段式</h3>
|
||||
<h3 class="text-xl mr-5">
|
||||
{{ $t("energy.standard_time_of_use_tariff_2_stage") }}
|
||||
</h3>
|
||||
<button
|
||||
v-if="!stand2isEditing"
|
||||
class="btn btn-sm btn-add mr-3"
|
||||
@ -81,19 +83,33 @@ watch(
|
||||
<table class="">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="6" class="bg-teal-800 bg-opacity-20">分類</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">夏月<br />(6/1~9/30)</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">非夏月<br />(夏月以外的時間)</th>
|
||||
<th colspan="6" class="bg-teal-800 bg-opacity-20">
|
||||
{{ $t("energy.classification") }}
|
||||
</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">
|
||||
{{ $t("energy.summer_months") }}<br />(6/1~9/30)
|
||||
</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">
|
||||
{{ $t("energy.non_summer_months") }}<br />({{
|
||||
$t("energy.time_outside_summer_months")
|
||||
}})
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="6" class="bg-teal-800 bg-opacity-40">基本電費</td>
|
||||
<td colspan="2" rowspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
按戶計收
|
||||
<td rowspan="6" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.fixed_elec_cost") }}
|
||||
</td>
|
||||
<td colspan="2" rowspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.charged_per_household") }}
|
||||
</td>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.single_phase") }}
|
||||
</td>
|
||||
<td rowspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.per_household_month") }}
|
||||
</td>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">單相</td>
|
||||
<td rowspan="2" class="bg-teal-800 bg-opacity-40">每戶每月</td>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
type="number"
|
||||
@ -103,7 +119,9 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">三相</td>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.three_phase") }}
|
||||
</td>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
type="number"
|
||||
@ -113,8 +131,12 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">經常契約</td>
|
||||
<td rowspan="4" class="bg-teal-800 bg-opacity-40">每瓩每月</td>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.frequent_contract") }}
|
||||
</td>
|
||||
<td rowspan="4" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.per_kw_per_month") }}
|
||||
</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
type="number"
|
||||
@ -131,7 +153,9 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">非夏日契約</td>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.non_summer_contract") }}
|
||||
</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">-</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
@ -142,7 +166,9 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">週六半尖峰契約</td>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.saturday_semi_peak_contract") }}
|
||||
</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
type="number"
|
||||
@ -159,7 +185,9 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">離峰契約</td>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.off_peak_contract") }}
|
||||
</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
type="number"
|
||||
@ -176,12 +204,16 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="9">流動電費</td>
|
||||
<td rowspan="4">週一~週五</td>
|
||||
<td rowspan="2" class="bg-rose-600 bg-opacity-70">尖峰時間</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">夏月</td>
|
||||
<td rowspan="9">{{ $t("energy.var_elec_cost") }}</td>
|
||||
<td rowspan="4">{{ $t("energy.mon_to_friday") }}</td>
|
||||
<td rowspan="2" class="bg-rose-600 bg-opacity-70">
|
||||
{{ $t("energy.peak_hours") }}
|
||||
</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">
|
||||
{{ $t("energy.summer_months") }}
|
||||
</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">09:00 ~ 24:00</td>
|
||||
<td rowspan="9">每度</td>
|
||||
<td rowspan="9">{{ $t("energy.price_per_kwh") }}</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">
|
||||
<input
|
||||
type="number"
|
||||
@ -192,8 +224,12 @@ watch(
|
||||
<td class="bg-rose-600 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-rose-600 bg-opacity-20">非夏月</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">06:00 ~ 11:00<br />14:00~24:00</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">
|
||||
{{ $t("energy.non_summer_months") }}
|
||||
</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">
|
||||
06:00 ~ 11:00<br />14:00~24:00
|
||||
</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">-</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">
|
||||
<input
|
||||
@ -204,8 +240,12 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2" class="bg-green-600 bg-opacity-60">離峰時間</td>
|
||||
<td class="bg-green-600 bg-opacity-20">夏月</td>
|
||||
<td rowspan="2" class="bg-green-600 bg-opacity-60">
|
||||
{{ $t("energy.off_peak_hours") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
{{ $t("energy.summer_months") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">00:00 ~ 09:00</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
<input
|
||||
@ -217,7 +257,9 @@ watch(
|
||||
<td class="bg-green-600 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-green-600 bg-opacity-20">非夏月</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
{{ $t("energy.non_summer_months") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
00:00 ~ 06:00<br />
|
||||
11:00 ~ 14:00
|
||||
@ -232,9 +274,13 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="4">週六</td>
|
||||
<td rowspan="2" class="bg-yellow-400 bg-opacity-80">半尖峰時間</td>
|
||||
<td class="bg-yellow-400 bg-opacity-20">夏日</td>
|
||||
<td rowspan="4">{{ $t("energy.saturday") }}</td>
|
||||
<td rowspan="2" class="bg-yellow-400 bg-opacity-80">
|
||||
{{ $t("energy.semi_peak_hours") }}
|
||||
</td>
|
||||
<td class="bg-yellow-400 bg-opacity-20">
|
||||
{{ $t("energy.summer_months") }}
|
||||
</td>
|
||||
<td class="bg-yellow-400 bg-opacity-20">09:00 ~ 24:00</td>
|
||||
<td class="bg-yellow-400 bg-opacity-20">
|
||||
<input
|
||||
@ -246,7 +292,9 @@ watch(
|
||||
<td class="bg-yellow-400 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-yellow-400 bg-opacity-20">非夏日</td>
|
||||
<td class="bg-yellow-400 bg-opacity-20">
|
||||
{{ $t("energy.non_summer_months") }}
|
||||
</td>
|
||||
<td class="bg-yellow-400 bg-opacity-20">
|
||||
06:00 ~ 11:00<br />
|
||||
14:00 ~ 24:00
|
||||
@ -261,8 +309,12 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2" class="bg-green-600 bg-opacity-60">離峰時間</td>
|
||||
<td class="bg-green-600 bg-opacity-20">夏月</td>
|
||||
<td rowspan="2" class="bg-green-600 bg-opacity-60">
|
||||
{{ $t("energy.off_peak_hours") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
{{ $t("energy.summer_months") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">00:00 ~ 09:00</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
<input
|
||||
@ -274,8 +326,12 @@ watch(
|
||||
<td class="bg-green-600 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-green-600 bg-opacity-20">非夏月</td>
|
||||
<td class="bg-green-600 bg-opacity-20">00:00 ~ 06:00<br />11:00 ~ 14:00</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
{{ $t("energy.non_summer_months") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
00:00 ~ 06:00<br />11:00 ~ 14:00
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">-</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
<input
|
||||
@ -286,9 +342,13 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>週六、週日<br />及離峰日</td>
|
||||
<td class="bg-green-600 bg-opacity-60">離峰時間</td>
|
||||
<td colspan="2" class="bg-green-600 bg-opacity-20">全日</td>
|
||||
<td>{{ $t("energy.sat_sun_off_peak_days") }}</td>
|
||||
<td class="bg-green-600 bg-opacity-60">
|
||||
{{ $t("energy.off_peak_hours") }}
|
||||
</td>
|
||||
<td colspan="2" class="bg-green-600 bg-opacity-20">
|
||||
{{ $t("energy.all_day") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
<input
|
||||
type="number"
|
||||
@ -308,7 +368,9 @@ watch(
|
||||
</table>
|
||||
|
||||
<div class="flex justify-start items-center mt-14">
|
||||
<h3 class="text-xl mr-5">標準型時間電價三段式</h3>
|
||||
<h3 class="text-xl mr-5">
|
||||
{{ $t("energy.standard_time_of_use_tariff_3_stage") }}
|
||||
</h3>
|
||||
<button
|
||||
v-if="!stand3isEditing"
|
||||
class="btn btn-sm btn-add mr-3"
|
||||
@ -336,17 +398,33 @@ watch(
|
||||
<table class="my-5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="6" class="bg-teal-800 bg-opacity-20">分類</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">夏月<br />(6/1~9/30)</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">非夏月<br />(夏月以外的時間)</th>
|
||||
<th colspan="6" class="bg-teal-800 bg-opacity-20">
|
||||
{{ $t("energy.classification") }}
|
||||
</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">
|
||||
{{ $t("energy.summer_months") }}<br />(6/1~9/30)
|
||||
</th>
|
||||
<th class="bg-teal-800 bg-opacity-20">
|
||||
{{ $t("energy.non_summer_months") }}<br />({{
|
||||
$t("energy.time_outside_summer_months")
|
||||
}})
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="6" class="bg-teal-800 bg-opacity-40">基本電費</td>
|
||||
<td colspan="2" rowspan="2" class="bg-teal-800 bg-opacity-40">按戶計收</td>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">單相</td>
|
||||
<td rowspan="2" class="bg-teal-800 bg-opacity-40">每戶每月</td>
|
||||
<td rowspan="6" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.fixed_elec_cost") }}
|
||||
</td>
|
||||
<td colspan="2" rowspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.charged_per_household") }}
|
||||
</td>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.single_phase") }}
|
||||
</td>
|
||||
<td rowspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.per_household_month") }}
|
||||
</td>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
type="number"
|
||||
@ -356,7 +434,9 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">三相</td>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.three_phase") }}
|
||||
</td>
|
||||
<td colspan="2" class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
type="number"
|
||||
@ -366,8 +446,12 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">經常契約</td>
|
||||
<td rowspan="4" class="bg-teal-800 bg-opacity-40">每瓩每月</td>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.frequent_contract") }}
|
||||
</td>
|
||||
<td rowspan="4" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.per_kw_per_month") }}
|
||||
</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
type="number"
|
||||
@ -384,7 +468,9 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">非夏日契約</td>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.non_summer_contract") }}
|
||||
</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
type="number"
|
||||
@ -401,7 +487,9 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">週六半尖峰契約</td>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.saturday_semi_peak_contract") }}
|
||||
</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
type="number"
|
||||
@ -418,7 +506,9 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">離峰契約</td>
|
||||
<td colspan="4" class="bg-teal-800 bg-opacity-40">
|
||||
{{ $t("energy.off_peak_contract") }}
|
||||
</td>
|
||||
<td class="bg-teal-800 bg-opacity-40">
|
||||
<input
|
||||
type="number"
|
||||
@ -435,12 +525,14 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="10">流動電費</td>
|
||||
<td rowspan="5">週一~週五</td>
|
||||
<td class="bg-rose-600 bg-opacity-70">尖峰時間</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">夏月</td>
|
||||
<td rowspan="10">{{ $t("energy.var_elec_cost") }}</td>
|
||||
<td rowspan="5">{{ $t("energy.mon_to_friday") }}</td>
|
||||
<td class="bg-rose-600 bg-opacity-70">{{ $t("energy.peak_hours") }}</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">
|
||||
{{ $t("energy.summer_months") }}
|
||||
</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">16:00 ~ 22:00</td>
|
||||
<td rowspan="10">每度</td>
|
||||
<td rowspan="10">{{ $t("energy.price_per_kwh") }}</td>
|
||||
<td class="bg-rose-600 bg-opacity-20">
|
||||
<input
|
||||
type="number"
|
||||
@ -451,9 +543,15 @@ watch(
|
||||
<td class="bg-rose-600 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2" class="bg-yellow-400 bg-opacity-80">半尖峰時間</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">夏月</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">09:00 ~ 16:00<br />22:00 ~ 24:00</td>
|
||||
<td rowspan="2" class="bg-yellow-400 bg-opacity-80">
|
||||
{{ $t("energy.semi_peak_hours") }}
|
||||
</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">
|
||||
{{ $t("energy.summer_months") }}
|
||||
</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">
|
||||
09:00 ~ 16:00<br />22:00 ~ 24:00
|
||||
</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">
|
||||
<input
|
||||
type="number"
|
||||
@ -464,8 +562,12 @@ watch(
|
||||
<td class="bg-yellow-500 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-yellow-500 bg-opacity-20">非夏月</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">06:00 ~ 11:00<br />14:00 ~ 24:00</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">
|
||||
{{ $t("energy.non_summer_months") }}
|
||||
</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">
|
||||
06:00 ~ 11:00<br />14:00 ~ 24:00
|
||||
</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">-</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">
|
||||
<input
|
||||
@ -476,8 +578,12 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2" class="bg-green-600 bg-opacity-60">離峰時間</td>
|
||||
<td class="bg-green-600 bg-opacity-20">夏月</td>
|
||||
<td rowspan="2" class="bg-green-600 bg-opacity-60">
|
||||
{{ $t("energy.off_peak_hours") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
{{ $t("energy.summer_months") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">00:00 ~ 09:00</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
<input
|
||||
@ -489,7 +595,9 @@ watch(
|
||||
<td class="bg-green-600 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-green-600 bg-opacity-20">非夏月</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
{{ $t("energy.non_summer_months") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
00:00 ~ 06:00<br />
|
||||
11:00 ~ 14:00
|
||||
@ -504,9 +612,13 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="4">週六</td>
|
||||
<td rowspan="2" class="bg-yellow-400 bg-opacity-80">半尖峰時間</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">夏日</td>
|
||||
<td rowspan="4">{{ $t("energy.saturday") }}</td>
|
||||
<td rowspan="2" class="bg-yellow-400 bg-opacity-80">
|
||||
{{ $t("energy.semi_peak_hours") }}
|
||||
</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">
|
||||
{{ $t("energy.summer_months") }}
|
||||
</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">09:00 ~ 24:00</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">
|
||||
<input
|
||||
@ -518,7 +630,9 @@ watch(
|
||||
<td class="bg-yellow-500 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-yellow-500 bg-opacity-20">非夏日</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">
|
||||
{{ $t("energy.non_summer_months") }}
|
||||
</td>
|
||||
<td class="bg-yellow-500 bg-opacity-20">
|
||||
06:00 ~ 11:00<br />
|
||||
14:00 ~ 24:00
|
||||
@ -533,8 +647,12 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2" class="bg-green-600 bg-opacity-60">離峰時間</td>
|
||||
<td class="bg-green-600 bg-opacity-20">夏月</td>
|
||||
<td rowspan="2" class="bg-green-600 bg-opacity-60">
|
||||
{{ $t("energy.off_peak_hours") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
{{ $t("energy.summer_months") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">00:00 ~ 09:00</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
<input
|
||||
@ -546,8 +664,12 @@ watch(
|
||||
<td class="bg-green-600 bg-opacity-20">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="bg-green-600 bg-opacity-20">非夏月</td>
|
||||
<td class="bg-green-600 bg-opacity-20">00:00 ~ 06:00<br />11:00 ~ 14:00</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
{{ $t("energy.non_summer_months") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
00:00 ~ 06:00<br />11:00 ~ 14:00
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">-</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
<input
|
||||
@ -558,9 +680,13 @@ watch(
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>週日及離峰日</td>
|
||||
<td class="bg-green-600 bg-opacity-60">離峰時間</td>
|
||||
<td colspan="2" class="bg-green-600 bg-opacity-20">全日</td>
|
||||
<td>{{ $t("energy.sunday_and_off_peak_days") }}</td>
|
||||
<td class="bg-green-600 bg-opacity-60">
|
||||
{{ $t("energy.off_peak_hours") }}
|
||||
</td>
|
||||
<td colspan="2" class="bg-green-600 bg-opacity-20">
|
||||
{{ $t("energy.all_day") }}
|
||||
</td>
|
||||
<td class="bg-green-600 bg-opacity-20">
|
||||
<input
|
||||
type="number"
|
||||
|
@ -1,9 +1,12 @@
|
||||
<script setup>
|
||||
import Table from "@/components/customUI/Table.vue";
|
||||
import FloorsModal from "./FloorsModal.vue";
|
||||
import { getAssetFloorList, deleteAssetFloor } from "@/apis/asset";
|
||||
import { deleteAssetFloor } from "@/apis/asset";
|
||||
import { onMounted, ref, inject, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
|
||||
const storeBuild = useBuildingStore();
|
||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||
const { t } = useI18n();
|
||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
||||
@ -28,18 +31,10 @@ const columns = computed(() => [
|
||||
},
|
||||
]);
|
||||
|
||||
const dataSource = ref([]);
|
||||
const loading = ref(false);
|
||||
|
||||
const getDataSource = async () => {
|
||||
loading.value = true;
|
||||
const res = await getAssetFloorList();
|
||||
dataSource.value = res.data[0]?.floors.map((d) => ({ ...d, key: d.floor_guid }));
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDataSource();
|
||||
storeBuild.fetchFloorList(storeBuild.selectedBuilding?.building_guid);
|
||||
});
|
||||
|
||||
const formState = ref({
|
||||
@ -49,7 +44,20 @@ const formState = ref({
|
||||
|
||||
const openModal = (record) => {
|
||||
if (record.floor_guid) {
|
||||
formState.value = { ...record };
|
||||
console.log('record',record);
|
||||
formState.value.floor_guid = record.floor_guid;
|
||||
formState.value.full_name = record.full_name;
|
||||
if(record.floor_map_url){
|
||||
const floorFile = record
|
||||
? {
|
||||
// size: record.oriSize,
|
||||
name: record.floor_map_name,
|
||||
src: `${record.floor_map_url}.svg`,
|
||||
ext: 'svg',
|
||||
}
|
||||
: {};
|
||||
formState.value.floorFile = [floorFile];
|
||||
}
|
||||
} else {
|
||||
formState.value = {
|
||||
full_name: "",
|
||||
@ -59,14 +67,14 @@ const openModal = (record) => {
|
||||
floor_modal.showModal();
|
||||
};
|
||||
|
||||
const remove = async () => {
|
||||
const remove = async (id) => {
|
||||
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
|
||||
await cancelToastOpen();
|
||||
const res = await deleteAssetFloor({
|
||||
floor_guid: formState.value.floor_guid,
|
||||
floor_guid: id,
|
||||
});
|
||||
if (res.isSuccess) {
|
||||
getDataSource();
|
||||
storeBuild.fetchFloorList(storeBuild.selectedBuilding?.building_guid);
|
||||
openToast("success", t("msg.delete_success"));
|
||||
} else {
|
||||
openToast("error", res.msg);
|
||||
@ -78,13 +86,13 @@ const remove = async () => {
|
||||
<template>
|
||||
<div class="flex justify-start items-center mt-10 mb-5">
|
||||
<h3 class="text-xl mr-5">{{ $t("energy.floor") }}</h3>
|
||||
<FloorsModal
|
||||
:formState="formState"
|
||||
:getData="getDataSource"
|
||||
:openModal="openModal"
|
||||
/>
|
||||
<FloorsModal :formState="formState" :openModal="openModal" />
|
||||
</div>
|
||||
<Table :columns="columns" :dataSource="dataSource" :loading="loading">
|
||||
<Table
|
||||
:columns="columns"
|
||||
:dataSource="storeBuild.floorList"
|
||||
:loading="loading"
|
||||
>
|
||||
<template #bodyCell="{ record, column, index }">
|
||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||
<template v-else-if="column.key === 'operation'">
|
||||
@ -96,13 +104,17 @@ const remove = async () => {
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-error text-white"
|
||||
@click.stop.prevent="() => remove()"
|
||||
@click.stop.prevent="() => remove(record.floor_guid)"
|
||||
>
|
||||
{{ $t("button.delete") }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'floor_plan'">
|
||||
<img :src="`${FILE_BASEURL}/${record.floor_map_url}.svg`" class="w-[200px] bg-white mx-auto" />
|
||||
<img
|
||||
v-if="record.floor_map_url"
|
||||
:src="`${FILE_BASEURL}/${record.floor_map_url}.svg`"
|
||||
class="w-[200px] bg-white mx-auto"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ record[column.key] }}
|
||||
|
@ -5,16 +5,17 @@ import "yup-phone-lite";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import { postAssetFloor } from "@/apis/asset";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
|
||||
const storeBuild = useBuildingStore();
|
||||
const { t } = useI18n();
|
||||
const { openToast } = inject("app_toast");
|
||||
|
||||
const props = defineProps({
|
||||
formState: Object,
|
||||
getData: Function,
|
||||
openModal: Function,
|
||||
});
|
||||
const form = ref(null);
|
||||
|
||||
const floorScheme = yup.object({
|
||||
full_name: yup.string().required(t("button.required")),
|
||||
floorFile: yup.array(),
|
||||
@ -22,7 +23,7 @@ const floorScheme = yup.object({
|
||||
|
||||
const updateFileList = (files) => {
|
||||
console.log("file", files);
|
||||
FloorFormState.value.floorFile = files;
|
||||
props.formState.floorFile = files;
|
||||
};
|
||||
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset, updateScheme } =
|
||||
@ -30,23 +31,26 @@ const { formErrorMsg, handleSubmit, handleErrorReset, updateScheme } =
|
||||
|
||||
const onCancel = () => {
|
||||
handleErrorReset();
|
||||
updateFileList([]);
|
||||
floor_modal.close();
|
||||
};
|
||||
|
||||
const onOk = async () => {
|
||||
const value = await handleSubmit(floorScheme, props.formState);
|
||||
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]);
|
||||
if (props.formState.floor_guid) {
|
||||
formData.append("floor_guid", props.formState.floor_guid);
|
||||
}
|
||||
if (props.formState.floorFile[0]) {
|
||||
formData.append("initMapName", props.formState.floorFile[0]?.name);
|
||||
formData.append("mapFile", props.formState.floorFile[0]);
|
||||
}
|
||||
formData.append("building_guid", storeBuild.selectedBuilding.building_guid);
|
||||
|
||||
formData.delete("floorFile");
|
||||
const res = await postAssetFloor(formData);
|
||||
if (res.isSuccess) {
|
||||
props.getData();
|
||||
storeBuild.fetchFloorList(storeBuild.selectedBuilding?.building_guid);
|
||||
onCancel();
|
||||
} else {
|
||||
openToast("error", res.msg, "#floor_modal");
|
||||
|
@ -79,7 +79,7 @@ const toggleNode = (node, checked) => {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<MITTCheckboxTree
|
||||
<MQTTCheckboxTree
|
||||
v-if="node.children?.length && node.expanded"
|
||||
:data="node.children"
|
||||
:checked-nodes="checkedNodes"
|
||||
|
@ -1,16 +1,24 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, computed } from "vue";
|
||||
import { getAssetMainList, getAssetSubList, getIOTSchema } from "@/apis/asset";
|
||||
import { ref, onMounted, watch, computed, inject } from "vue";
|
||||
import {
|
||||
getAssetMainList,
|
||||
getAssetSubList,
|
||||
getIOTSchema,
|
||||
getDeviceItem,
|
||||
deleteDeviceItem,
|
||||
} from "@/apis/asset";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import MQTTListAddModal from "./MQTTListAddModal.vue";
|
||||
import PointListAddModal from "./PointListAddModal.vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const formState = ref({});
|
||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
||||
const MQTTDataSource = ref([]);
|
||||
const MQTTloading = ref(false);
|
||||
const PointsDataSource = ref([]);
|
||||
const Pointsloading = ref(false);
|
||||
const variable_id = ref(null);
|
||||
const editRecord = ref(null);
|
||||
// 系統類別
|
||||
const {
|
||||
items: mainSysItems,
|
||||
@ -50,36 +58,44 @@ const getSubSystems = async (id) => {
|
||||
|
||||
const MQTTColumns = computed(() => [
|
||||
{
|
||||
title: "schema",
|
||||
title: t("setting.schema"),
|
||||
key: "schema_name",
|
||||
},
|
||||
{
|
||||
title: "point",
|
||||
title: t("setting.point"),
|
||||
key: "pointOrg",
|
||||
filter: true,
|
||||
},
|
||||
{
|
||||
title: "Description",
|
||||
title: t("setting.description"),
|
||||
key: "description",
|
||||
},
|
||||
]);
|
||||
|
||||
const PointsColumns = computed(() => [
|
||||
{
|
||||
title: "IoT 點位名稱",
|
||||
key: "IoT_point_name",
|
||||
title: t("setting.IoT_point_name"),
|
||||
key: "full_name",
|
||||
},
|
||||
{
|
||||
title: "小數點個數",
|
||||
key: "decimal",
|
||||
title: t("setting.IoT_point_code"),
|
||||
key: "points",
|
||||
},
|
||||
{
|
||||
title: "bool 值",
|
||||
title: t("setting.number_of_decimal_places"),
|
||||
key: "decimals",
|
||||
},
|
||||
{
|
||||
title: t("setting.boolean_value"),
|
||||
key: "is_bool",
|
||||
},
|
||||
{
|
||||
title: "點位隱藏",
|
||||
key: "is_open",
|
||||
title: t("setting.hide_point"),
|
||||
key: "is_link",
|
||||
},
|
||||
{
|
||||
title: t("assetManagement.operation"),
|
||||
key: "operation",
|
||||
},
|
||||
]);
|
||||
|
||||
@ -97,6 +113,46 @@ const getDataSource = async (id) => {
|
||||
MQTTloading.value = false;
|
||||
};
|
||||
|
||||
const getPointsData = async (id) => {
|
||||
Pointsloading.value = true;
|
||||
const res = await getDeviceItem(id);
|
||||
PointsDataSource.value = res.data.map((d) => ({
|
||||
...d,
|
||||
key: d.id,
|
||||
}));
|
||||
Pointsloading.value = false;
|
||||
};
|
||||
|
||||
const remove = async (id) => {
|
||||
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
|
||||
await cancelToastOpen();
|
||||
const res = await deleteDeviceItem(id);
|
||||
if (res.isSuccess) {
|
||||
getPointsData(variable_id.value);
|
||||
openToast("success", t("msg.delete_success"));
|
||||
} else {
|
||||
openToast("error", res.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const openModal = (record) => {
|
||||
if (record?.id) {
|
||||
editRecord.value = record;
|
||||
} else {
|
||||
editRecord.value = {
|
||||
id: 0,
|
||||
variable_id: variable_id.value,
|
||||
full_name: "",
|
||||
points: "",
|
||||
decimals: 0,
|
||||
is_bool: 0,
|
||||
is_link: 0,
|
||||
};
|
||||
}
|
||||
point_list_item.showModal();
|
||||
};
|
||||
|
||||
watch(
|
||||
selectedMainSysItems,
|
||||
(newValue) => {
|
||||
@ -114,6 +170,8 @@ watch(
|
||||
(newValue) => {
|
||||
if (newValue && newValue.key) {
|
||||
getDataSource(parseInt(newValue.key));
|
||||
getPointsData(parseInt(newValue.key));
|
||||
variable_id.value = parseInt(newValue.key);
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -157,23 +215,55 @@ onMounted(() => {
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-start items-center mt-5 mb-2">
|
||||
<h3 class="text-xl mr-5">MQTT_Parse : </h3>
|
||||
<MQTTListAddModal />
|
||||
<h3 class="text-xl mr-5">{{ $t("setting.MQTT_parse") }} :</h3>
|
||||
<MQTTListAddModal
|
||||
:getData="getDataSource"
|
||||
:pointsData="PointsDataSource"
|
||||
:variable_id="variable_id"
|
||||
/>
|
||||
</div>
|
||||
<Table
|
||||
:columns="MQTTColumns"
|
||||
:dataSource="MQTTDataSource"
|
||||
:loading="MQTTloading"
|
||||
></Table>
|
||||
>
|
||||
</Table>
|
||||
<div class="flex justify-start items-center mt-5 mb-2">
|
||||
<h3 class="text-xl mr-5">Points : </h3>
|
||||
<PointListAddModal />
|
||||
<h3 class="text-xl mr-5">{{ $t("setting.point") }} :</h3>
|
||||
<PointListAddModal
|
||||
:getData="getPointsData"
|
||||
:variable_id="variable_id"
|
||||
:editRecord="editRecord"
|
||||
:openModal="openModal"
|
||||
/>
|
||||
</div>
|
||||
<Table
|
||||
:columns="PointsColumns"
|
||||
:dataSource="PointsDataSource"
|
||||
:loading="Pointsloading"
|
||||
></Table>
|
||||
><template #bodyCell="{ record, column, index }">
|
||||
<template v-if="column.key === 'is_bool'">
|
||||
{{ record.is_bool === 1 ? t("alert.yes") : t("alert.no") }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'is_link'">
|
||||
{{ record.is_link === 1 ? t("alert.yes") : t("alert.no") }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'operation'">
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white mr-2"
|
||||
@click.stop.prevent="() => openModal(record)"
|
||||
>
|
||||
{{ $t("button.edit") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-error text-white"
|
||||
@click.stop.prevent="() => remove(record.id)"
|
||||
>
|
||||
{{ $t("button.delete") }}
|
||||
</button>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -1,10 +1,15 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, defineProps } from "vue";
|
||||
import { getAssetMainList, getAssetSubList, getIOTSchema } from "@/apis/asset";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import { ref, onMounted, watch, defineProps, inject } from "vue";
|
||||
import { postIOTSchema } from "@/apis/asset";
|
||||
import MQTTCheckboxTree from "./MQTTCheckboxTree.vue";
|
||||
import generateSchema from "json-schema-generator";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { openToast } = inject("app_toast");
|
||||
const props = defineProps({
|
||||
getData: Function,
|
||||
pointsData: Object,
|
||||
variable_id: Number,
|
||||
});
|
||||
const { t } = useI18n();
|
||||
// 新增:將 jsonInput 與結構資料初始化
|
||||
const jsonInput = ref("");
|
||||
@ -14,10 +19,6 @@ const checkedNodes = ref([]);
|
||||
const form = ref(null);
|
||||
const schemaName = ref("");
|
||||
const formStates = ref([]);
|
||||
const optionData = ref([
|
||||
{ key: 0, full_name: "否" },
|
||||
{ key: 1, full_name: "是" },
|
||||
]);
|
||||
|
||||
// JSON Schema 轉換
|
||||
const customGenerateSchema = (json) => {
|
||||
@ -92,22 +93,42 @@ watch(
|
||||
(newCheckedNodes) => {
|
||||
// 為每個 node 創建一個 formState
|
||||
formStates.value = newCheckedNodes.map((node) => ({
|
||||
IoT_point_schema: node,
|
||||
sys_point_name: null,
|
||||
is_bool: 1,
|
||||
decimal: 0,
|
||||
is_open: 1,
|
||||
PointOrg: node,
|
||||
PointSys: "",
|
||||
item_id: null,
|
||||
}));
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const onOk = async () => {
|
||||
const points = formStates.value.map((state) => ({
|
||||
PointOrg: state.PointOrg,
|
||||
PointSys: props.pointsData.find((point) => point.id === state.item_id)
|
||||
.points,
|
||||
item_id: state.item_id,
|
||||
}));
|
||||
const apiData = {
|
||||
name: schemaName.value,
|
||||
variable_id: props.variable_id,
|
||||
points: points,
|
||||
};
|
||||
const res = await postIOTSchema(apiData);
|
||||
if (res.isSuccess) {
|
||||
props.getData(props.variable_id);
|
||||
onCancel();
|
||||
} else {
|
||||
openToast("error", res.msg, "#MQTT_Parse_item");
|
||||
}
|
||||
};
|
||||
|
||||
const openModal = () => {
|
||||
MQTT_Parse_item.showModal();
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
MQTT_Parse_item.close();
|
||||
clearAll();
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -117,7 +138,7 @@ const onCancel = () => {
|
||||
</button>
|
||||
<Modal
|
||||
id="MQTT_Parse_item"
|
||||
title="MQTT_Parse"
|
||||
:title="t('setting.MQTT_parse')"
|
||||
:onCancel="onCancel"
|
||||
:width="1600"
|
||||
>
|
||||
@ -126,21 +147,18 @@ const onCancel = () => {
|
||||
<div class="w-full">
|
||||
<textarea
|
||||
v-model="jsonInput"
|
||||
placeholder="請貼上 JSON 格式數據"
|
||||
:placeholder="t('setting.json_format_text')"
|
||||
class="w-full h-[500px] p-4 border rounded-lg font-mono text-white"
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="my-auto">
|
||||
<button @click="parseJson" class="btn-success btn w-24 mb-4">
|
||||
轉換
|
||||
<font-awesome-icon
|
||||
:icon="['fas', 'chevron-right']"
|
||||
size="lg"
|
||||
class="block"
|
||||
/>
|
||||
<button @click="parseJson" class="btn-success btn w-28 mb-4">
|
||||
{{ $t("button.convert")
|
||||
}}<font-awesome-icon :icon="['fas', 'chevron-right']" />
|
||||
</button>
|
||||
<button @click="clearAll" class="bg-error btn w-24">
|
||||
刪除<font-awesome-icon :icon="['fas', 'trash-alt']" />
|
||||
<button @click="clearAll" class="bg-error btn w-28">
|
||||
{{ $t("button.delete")
|
||||
}}<font-awesome-icon :icon="['fas', 'trash-alt']" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="w-full p-4 rounded-lg border overflow-y-scroll h-[500px]">
|
||||
@ -149,81 +167,71 @@ const onCancel = () => {
|
||||
:data="treeData"
|
||||
v-model:checked-nodes="checkedNodes"
|
||||
/>
|
||||
<div v-else class="text-white">請在左側輸入 JSON 並點擊轉換按鈕</div>
|
||||
<div v-else class="text-white">
|
||||
{{ $t("setting.json_click_text") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form ref="form" class="" v-if="checkedNodes.length">
|
||||
<div class="flex items-center mt-5">
|
||||
<h2 class="text-lg font-bold whitespace-nowrap me-2">結構名稱 :</h2>
|
||||
<Input v-model="schemaName" />
|
||||
<h2 class="text-lg font-bold whitespace-nowrap me-2">
|
||||
{{ $t("setting.schema_name") }}:
|
||||
</h2>
|
||||
<input
|
||||
type="text"
|
||||
v-model.text="schemaName"
|
||||
class="input border-info focus-within:border-info"
|
||||
/>
|
||||
</div>
|
||||
<table class="table">
|
||||
<table class="table w-1/2 mt-2">
|
||||
<thead>
|
||||
<tr>
|
||||
<!-- <th>IoT 點位名稱</th> -->
|
||||
<th>IoT 點位結構</th>
|
||||
<th>系統點位名稱</th>
|
||||
<!-- <th>bool 值</th>
|
||||
<th>小數點個數</th>
|
||||
<th>點位隱藏</th> -->
|
||||
<th>{{$t("setting.IoT_point_structure")}}</th>
|
||||
<th>{{$t("setting.system_point_name")}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(node, index) in checkedNodes" :key="node">
|
||||
<!-- <td>
|
||||
<Input :value="formStates[index]" name="IoT_point_name" />
|
||||
</td> -->
|
||||
<td class="w-1/2">
|
||||
<td>
|
||||
<Input
|
||||
:value="formStates[index]"
|
||||
name="IoT_point_schema"
|
||||
name="PointOrg"
|
||||
:readonly="true"
|
||||
class="mx-auto"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<select>
|
||||
<option value="option1">選項 1</option>
|
||||
<option value="option2">選項 2</option>
|
||||
<option value="option3">選項 3</option>
|
||||
</select>
|
||||
</td>
|
||||
<!-- <td>
|
||||
<div class="flex w-36">
|
||||
<Select
|
||||
:value="formStates[index]"
|
||||
selectClass="w-20 mx-auto border-info focus-within:border-info"
|
||||
name="is_bool"
|
||||
class="my-2 mx-auto"
|
||||
selectClass="border-info focus-within:border-info "
|
||||
name="item_id"
|
||||
Attribute="full_name"
|
||||
:options="optionData"
|
||||
:options="props.pointsData"
|
||||
>
|
||||
</Select>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<InputNumber
|
||||
:value="formStates[index]"
|
||||
class="mx-auto"
|
||||
name="decimal"
|
||||
>
|
||||
</InputNumber>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex w-36">
|
||||
<Select
|
||||
:value="formStates[index]"
|
||||
selectClass="w-20 mx-auto border-info focus-within:border-info"
|
||||
name="is_open"
|
||||
Attribute="full_name"
|
||||
:options="optionData"
|
||||
>
|
||||
</Select>
|
||||
</div>
|
||||
</td> -->
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</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.stop.prevent="onOk"
|
||||
>
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
|
@ -1,56 +1,114 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, defineProps } from "vue";
|
||||
import { ref, onMounted, watch, defineProps, inject, readonly } from "vue";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import { postDeviceItem } from "@/apis/asset";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import * as yup from "yup";
|
||||
const { openToast } = inject("app_toast");
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
getData: Function,
|
||||
openModal: Function,
|
||||
editRecord: Object,
|
||||
variable_id: Number,
|
||||
});
|
||||
const form = ref(null);
|
||||
const formState = ref({
|
||||
id: 0,
|
||||
IoT_point_name: "",
|
||||
variable_id: props.variable_id,
|
||||
full_name: "",
|
||||
points: "",
|
||||
decimals: 0,
|
||||
is_bool: 0,
|
||||
decimal: 2,
|
||||
is_open: 0,
|
||||
is_link: 0,
|
||||
});
|
||||
let schema = ref(
|
||||
yup.object({
|
||||
IoT_point_name: yup.string().required(t("button.required")),
|
||||
is_bool: yup.string().required(t("button.required")),
|
||||
decimal: yup.string().required(t("button.required")),
|
||||
is_open: yup.string().required(t("button.required")),
|
||||
full_name: yup.string().required(t("button.required")),
|
||||
decimals: yup.number().required(t("button.required")),
|
||||
is_bool: yup.number().required(t("button.required")),
|
||||
is_link: yup.number().required(t("button.required")),
|
||||
})
|
||||
);
|
||||
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset, updateScheme } =
|
||||
useFormErrorMessage(schema.value);
|
||||
|
||||
const openModal = () => {
|
||||
point_list_item.showModal();
|
||||
const onOk = async () => {
|
||||
const values = await handleSubmit(schema.value, formState.value);
|
||||
console.log("value", values);
|
||||
const res = await postDeviceItem({
|
||||
...values,
|
||||
decimals: Number(values.decimals),
|
||||
is_bool: Number(values.is_bool),
|
||||
is_link: Number(values.is_link),
|
||||
});
|
||||
if (res.isSuccess) {
|
||||
props.getData(props.variable_id);
|
||||
onCancel();
|
||||
} else {
|
||||
openToast("error", res.msg, "#point_list_item");
|
||||
}
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
point_list_item.close();
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.editRecord,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
formState.value = {
|
||||
...newValue,
|
||||
};
|
||||
} else {
|
||||
formState.value = {
|
||||
id: 0,
|
||||
variable_id: props.variable_id,
|
||||
full_name: "",
|
||||
points: "",
|
||||
decimals: 0,
|
||||
is_bool: 0,
|
||||
is_link: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="btn btn-sm btn-add mr-3" @click.stop.prevent="openModal">
|
||||
<button class="btn btn-sm btn-add mr-3" @click.stop.prevent="props.openModal">
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
||||
</button>
|
||||
<Modal id="point_list_item" title="Points" :onCancel="onCancel" :width="710">
|
||||
<template #modalContent>
|
||||
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
||||
<Input :value="formState" class="my-2" name="IoT_point_name">
|
||||
<template #topLeft>IoT 點位名稱</template>
|
||||
<Input :value="formState" class="my-2" name="full_name">
|
||||
<template #topLeft>{{ $t("setting.IoT_point_name") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.IoT_point_name }}
|
||||
{{ formErrorMsg.full_name }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<InputNumber :value="formState" class="my-2" name="decimal">
|
||||
<template #topLeft>小數點個數</template>
|
||||
<Input
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
name="points"
|
||||
:disabled="props.editRecord?.id"
|
||||
>
|
||||
<template #topLeft>{{ $t("setting.IoT_point_code") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.points }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<InputNumber :value="formState" class="my-2" name="decimals">
|
||||
<template #topLeft>{{
|
||||
$t("setting.number_of_decimal_places")
|
||||
}}</template>
|
||||
</InputNumber>
|
||||
<RadioGroup
|
||||
class="my-2"
|
||||
@ -70,12 +128,12 @@ const onCancel = () => {
|
||||
]"
|
||||
:required="true"
|
||||
>
|
||||
<template #topLeft>bool 值</template>
|
||||
<template #topLeft>{{ $t("setting.boolean_value") }}</template>
|
||||
</RadioGroup>
|
||||
|
||||
<RadioGroup
|
||||
class="my-2"
|
||||
name="is_open"
|
||||
name="is_link"
|
||||
:value="formState"
|
||||
:items="[
|
||||
{
|
||||
@ -91,10 +149,26 @@ const onCancel = () => {
|
||||
]"
|
||||
:required="true"
|
||||
>
|
||||
<template #topLeft>點位隱藏</template>
|
||||
<template #topLeft>{{ $t("setting.hide_point") }}</template>
|
||||
</RadioGroup>
|
||||
</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.stop.prevent="onOk"
|
||||
>
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
|
@ -93,7 +93,7 @@ watch(
|
||||
},
|
||||
params.data[2]
|
||||
);
|
||||
selected_dbid.value[0] = params.data[2].forge_dbid;
|
||||
selected_dbid.value[1] = params.data[2].spriteDbId;
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -146,7 +146,7 @@ watch(
|
||||
allData.value
|
||||
) {
|
||||
selectedData.value = selectedData.value.map((item) => {
|
||||
if (item[2].forge_dbid === newSelectedDbid[0]) {
|
||||
if (item[2].spriteDbId === newSelectedDbid[1]) {
|
||||
return [...item.slice(0, 2), { ...item[2], is2DActive: true }];
|
||||
}
|
||||
return [...item.slice(0, 2), { ...item[2], is2DActive: false }];
|
||||
|
@ -1,51 +1,118 @@
|
||||
<script setup>
|
||||
import { computed, inject, watch } from "vue"
|
||||
import useSystemShowData from "@/hooks/useSystemShowData"
|
||||
import { computed, inject, watch } from "vue";
|
||||
import useSystemShowData from "@/hooks/useSystemShowData";
|
||||
|
||||
const { getCurrentInfoModalData, selected_dbid } = inject("system_selectedDevice")
|
||||
const { getCurrentInfoModalData, selected_dbid } = inject(
|
||||
"system_selectedDevice"
|
||||
);
|
||||
const { subscribeData } = inject("system_deviceList");
|
||||
|
||||
const { showData } = useSystemShowData()
|
||||
const { showData } = useSystemShowData();
|
||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||
|
||||
const fitToView = (forge_dbid, spriteDbId) => {
|
||||
selected_dbid.value = [forge_dbid, spriteDbId];
|
||||
};
|
||||
|
||||
const openDialog = () => {
|
||||
document.getElementById("iframe_modal").showModal();
|
||||
};
|
||||
const cancelDialog = () => {
|
||||
document.getElementById("iframe_modal").close();
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <InfoModal :data="currentInfoModalData" /> -->
|
||||
<dialog
|
||||
id="iframe_modal"
|
||||
className="modal w-[590px] h-[490px] my-auto rounded-lg mx-auto"
|
||||
>
|
||||
<iframe
|
||||
src="https://app.mockplus.cn/p/zNB4y8k5L"
|
||||
frameborder="0"
|
||||
class="w-full h-full"
|
||||
></iframe>
|
||||
<Button
|
||||
type="link"
|
||||
class="btn-link btn-text-without-border z-20 absolute right-[2.3rem] top-[2.2rem]"
|
||||
@click="cancelDialog"
|
||||
>
|
||||
<font-awesome-icon
|
||||
:icon="['fas', 'times']"
|
||||
class="text-[#a5abb1] text-2xl"
|
||||
/>
|
||||
</Button>
|
||||
</dialog>
|
||||
|
||||
<template v-if="showData.length > 0">
|
||||
<div class="equipment-show" v-for="d in showData" :key="d.full_name">
|
||||
<template v-if="d.device_list.length > 0">
|
||||
<p class="title">{{ d.full_name }}</p>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-5">
|
||||
<div class="col-auto relative" v-for="device in d.device_list" :key="device.device_guid">
|
||||
<div class="item h-full cursor-pointer" @click="() => fitToView(device.forge_dbid, device.spriteDbId)">
|
||||
<div
|
||||
class="col-auto relative"
|
||||
v-for="device in d.device_list"
|
||||
:key="device.device_guid"
|
||||
>
|
||||
<div
|
||||
class="item h-full cursor-pointer"
|
||||
@click="() => fitToView(device.forge_dbid, device.spriteDbId)"
|
||||
>
|
||||
<div class="left h-full flex flex-wrap justify-center">
|
||||
<div class="sec02 w-full">
|
||||
<img v-if="device.device_image_url" :src="`${FILE_BASEURL}/upload/device_icon/${device.device_image}`"
|
||||
:alt="device.device_image">
|
||||
<img
|
||||
v-if="device.device_image_url"
|
||||
:src="`${FILE_BASEURL}/upload/device_icon/${device.device_image}`"
|
||||
:alt="device.device_image"
|
||||
/>
|
||||
<span v-else></span>
|
||||
<span>{{ device.full_name }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between w-full self-end">
|
||||
<div class="sec03">
|
||||
<span class="w-5 h-5 rounded-full" :style="{ backgroundColor: device.device_normal_color }"></span>
|
||||
<span
|
||||
class="w-5 h-5 rounded-full"
|
||||
:style="{
|
||||
backgroundColor:
|
||||
device.full_name === 'SmartSocket-AA001'
|
||||
? 'red'
|
||||
: device.full_name === 'SmartSocket-AA003' ||
|
||||
device.full_name === 'SmartSocket-AA004'
|
||||
? 'gray'
|
||||
: device.device_normal_color,
|
||||
}"
|
||||
></span>
|
||||
<span class="mx-2">{{ $t("system.status") }}:</span>
|
||||
<span>{{ device.device_status }}</span>
|
||||
<span>{{
|
||||
device.full_name === "SmartSocket-AA001"
|
||||
? "Error"
|
||||
: device.full_name === "SmartSocket-AA003" ||
|
||||
device.full_name === "SmartSocket-AA004"
|
||||
? "Offline"
|
||||
: device.device_status || 'Online'
|
||||
}}</span>
|
||||
</div>
|
||||
<button class="btn-text border-0 "
|
||||
@click.prevent="(e) => getCurrentInfoModalData(e, { left: e.clientX, top: e.clientY }, device)">{{
|
||||
$t("system.details") }}</button>
|
||||
<button
|
||||
class="btn-text border-0"
|
||||
@click.prevent="
|
||||
(e) => {
|
||||
if (device.full_name === 'SmartSocket-AA001') {
|
||||
openDialog();
|
||||
} else {
|
||||
getCurrentInfoModalData(
|
||||
e,
|
||||
{ left: e.clientX, top: e.clientY },
|
||||
device
|
||||
);
|
||||
}
|
||||
}
|
||||
"
|
||||
>
|
||||
{{ $t("system.details") }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -53,7 +120,7 @@ const fitToView = (forge_dbid, spriteDbId) => {
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style lang='css' scoped>
|
||||
<style lang="css" scoped>
|
||||
/*設備顯示*/
|
||||
.title {
|
||||
@apply text-lg text-white relative inline-block mb-5 after:absolute after:-bottom-2 after:left-1/4 after:w-4/5 after:h-[1px] after:bg-info;
|
||||
@ -88,7 +155,7 @@ const fitToView = (forge_dbid, spriteDbId) => {
|
||||
}
|
||||
|
||||
.equipment-show .item .sec01 span:nth-child(2) {
|
||||
font-size: .6rem;
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
|
||||
.equipment-show .item .sec02 {
|
||||
|
@ -12,18 +12,24 @@ const {
|
||||
changeActiveBtn: changeDeptActiveBtn,
|
||||
setItems: setDeptItems,
|
||||
selectedBtn: selectedDeptItems,
|
||||
} = useActiveBtn();
|
||||
} = useActiveBtn("multiple");
|
||||
|
||||
onMounted(() => {
|
||||
const deptList = [
|
||||
{
|
||||
title: "All",
|
||||
key: "main",
|
||||
},
|
||||
...storeBuild.deptList,
|
||||
];
|
||||
watch(
|
||||
() => storeBuild.deptList,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
const deptList = newValue.map((d) => ({
|
||||
...d,
|
||||
active: true,
|
||||
}));
|
||||
setDeptItems(deptList);
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -34,6 +40,7 @@ onMounted(() => {
|
||||
<ButtonGroup
|
||||
:items="sysDeptItems"
|
||||
className="btn-xs rounded-md"
|
||||
:withLine="true"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeDeptActiveBtn(item);
|
||||
|
@ -1,74 +1,88 @@
|
||||
<script setup>
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { getAssetFloorList } from "@/apis/asset";
|
||||
import { onMounted, ref, watch, inject } from 'vue';
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { onMounted, ref, watch, inject } from "vue";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn"
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const store = useBuildingStore();
|
||||
|
||||
const { updateCurrentFloor } = inject("system_deviceList")
|
||||
const { updateCurrentFloor } = inject("system_deviceList");
|
||||
|
||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
||||
const getFloors = async () => {
|
||||
const res = await getAssetFloorList()
|
||||
let data = res.data.find(d => d.building_tag === store.selectedBuilding?.building_tag)
|
||||
data = [
|
||||
|
||||
const onClick = (item) => {
|
||||
changeActiveBtn(item);
|
||||
|
||||
router.push({
|
||||
name: "sub_system",
|
||||
params: {
|
||||
...route.params,
|
||||
floor_id: item.key,
|
||||
},
|
||||
query: {
|
||||
...route.query,
|
||||
gas: route.query.gas,
|
||||
mode: item.key === "main" ? "3D" : route.query.mode,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => route.params.sub_system_id,
|
||||
(newValue, oldValue) => {
|
||||
if (newValue !== oldValue) {
|
||||
setItems(
|
||||
items.value.map((item, index) => ({ ...item, active: index === 0 }))
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => store.floorList,
|
||||
(newValue) => {
|
||||
if (newValue) {
|
||||
console.log('newValue',newValue);
|
||||
|
||||
const floorList = [
|
||||
{
|
||||
title: "All",
|
||||
key: "main",
|
||||
active: route.params.floor_id === "main",
|
||||
},
|
||||
...data.floors.map((d, idx) => ({
|
||||
...store.floorList.map((d, idx) => ({
|
||||
title: d.full_name,
|
||||
key: d.floor_guid,
|
||||
active: route.params.floor_id === d.floor_guid,
|
||||
map_url: d.floor_map_url ? d.floor_map_url + ".svg" : null
|
||||
}))
|
||||
]
|
||||
setItems(data);
|
||||
|
||||
updateCurrentFloor(data)
|
||||
map_url: d.floor_map_url ? d.floor_map_url + ".svg" : null,
|
||||
})),
|
||||
];
|
||||
setItems(floorList);
|
||||
updateCurrentFloor(floorList);
|
||||
}
|
||||
|
||||
const onClick = (item) => {
|
||||
changeActiveBtn(item)
|
||||
|
||||
router.push({
|
||||
name: 'sub_system', params: {
|
||||
...route.params, floor_id: item.key
|
||||
}, query: { ...route.query, gas: route.query.gas, mode: item.key === "main" ? "3D" : route.query.mode }
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
watch(() => route.params.sub_system_id, (newValue, oldValue) => {
|
||||
if (newValue !== oldValue) {
|
||||
setItems(items.value.map((item, index) => ({ ...item, active: index === 0 })));
|
||||
}
|
||||
|
||||
}, {
|
||||
deep: true
|
||||
})
|
||||
|
||||
watch(() => store.selectedBuilding, (newValue) => {
|
||||
newValue && getFloors()
|
||||
}, {
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
})
|
||||
|
||||
|
||||
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-md font-extrabold">{{ $t("energy.floor") }} :</span>
|
||||
<ButtonGroup :items="items" :withLine="false" className="btn-xs rounded-md" :onclick="(e, item) => onClick(item)" />
|
||||
<ButtonGroup
|
||||
:items="items"
|
||||
:withLine="true"
|
||||
className="btn-xs rounded-md"
|
||||
:onclick="(e, item) => onClick(item)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang='scss' scoped></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
Loading…
Reference in New Issue
Block a user