Compare commits

...

55 Commits

Author SHA1 Message Date
0ef591ca1b 電價表說明 2025-08-04 15:17:14 +08:00
edd7469ef2 setOption debug 2025-07-28 16:49:44 +08:00
acb1f89b0a 能源管理: 週報表欄位名稱調整 、 日報表日期與欄位排序 2025-07-28 13:11:34 +08:00
a7ed0340b7 2d圖預設與顯示 | 系統監控 : 根據realtime.value即時顯示狀態 2025-07-28 11:53:18 +08:00
e6939183fe 2D、3D設定 | 2D圖片路徑 | 設備管理 : MQTTpublish設定與傳送 | MQTT設定 : switch 功能開關與傳送訊息寫入 2025-07-25 12:04:09 +08:00
ac5c88a047 棟別判斷 | 能源管理匯出功能 2025-07-21 09:51:15 +08:00
f0e9d7fda6 棟別設置 2025-07-10 10:34:52 +08:00
14e3f9ea4a 告警遮罩改 | 設備管理UI | 2D / 3D 顯示設定頁面初切版 | MQTT switch 顯示 初切版 2025-07-03 11:37:35 +08:00
68d834aec9 修改語言包、傳入參數格式、欄位必填等bug 2025-06-19 15:33:47 +08:00
3e96223bd9 首頁系統小卡對齊問題修正 2025-06-17 10:26:42 +08:00
5d02cffcf4 2D/3D暫時設定 | VITE_FORGE_BASEURL改成VITE_FILE_API_BASEURL | 首頁語言更新 2025-06-04 17:14:21 +08:00
14187ad9bc 首頁UI調整 2025-05-21 18:00:17 +08:00
0772269c33 首頁 2025-05-06 18:11:17 +08:00
b55ba4003d 告警頁面 2025-04-11 17:35:18 +08:00
502b4f3778 告警設定多限定條件判斷 2025-04-02 18:10:45 +08:00
c8e00421b2 navbar、告警設定、資產管理、運維管理、mqtt 加上building_guid | 資產管理小類能上傳圖片 | 首頁顯示小類icon | 能源管理 : 即時需量串接api 、 圖表初始化設定、新增日期與時間區間 | 系統監控加上部門 2025-04-01 10:22:13 +08:00
8b85e2d67c MQTT位置路徑修改 | CviBuilding存進localStorage | 樓層與部門存到狀態裡 | 首頁、電價表語言包 |能源管理新增棟別、樓層、部門參數 | 歷史資料新增日期區間 | 樓層頁面deBug | 圖資deBug | MQTT頁面 | 系統監控: 新增棟別樓層部門 以及 spriteDbId的deBug | 電表設定 2025-03-13 16:17:09 +08:00
f956402648 首頁重新切版 | 樓層與部門放置store | 設定MQTT重新切版 2025-03-03 10:46:53 +08:00
c8d8fbf254 設定 : 新增棟別、時間電價、MQTT解析 | 能源管理: 即時需量、碳排當量新增 2025-02-21 15:57:56 +08:00
80fcfda16c 設備管理:部門與樓層管理移至設定、小類新稱icon | 能源管理:能匯出excel、報表日期設置 | 運維管理: 廠商管理移至設定 2025-02-17 09:59:36 +08:00
a91c4397a4 設備管理:組合點位-修改組合點位的參數顯示名稱、大類為Electricity System時,新增用電類別(option)、隱藏系統類別的新增與修改功能 | 帳號管理: 角色管理的權限bug | 歷史資料: 查詢時,新增Loading的圖示 | 即時告警: 告警設定要"刪除"功能 2025-02-06 11:41:20 +08:00
5b1ff9749d 系統監控: 2D圓點bug修正 | 能源管理: 日報表功能 2025-01-24 16:01:17 +08:00
8e2b5e1e2c 登入帳號紀錄cookie | table的過濾篩選可以輸入關鍵字 | 能源管理: 新增能源分析、能號報表 | 帳號管理:過濾webUser、admin | 告警設定: 新增警示時間 | 設備管理: iot欄位預設、tag_name只有在webUser時顯示、系統類別和設備類別的更新時如果沒有subSys_id時表格清空新增設備按鈕也不能按、樓層2d圖更新bug修正 | 系統監控: 系統小卡的desktop要根據ori_device_name分類 2025-01-24 10:43:50 +08:00
9e5ff1544c 資產管理: 部門、IOT、MQTT欄位 | 系統監控: 系統小卡詳細內容 TABLE不分頁、顯示即時資料、NO Data改成0 | 運維管理: 維運、保養修正搜尋功能、廠商資料隱藏搜尋 2025-01-03 18:06:46 +08:00
d19c7fd240 告警: 維修項目代碼正確顯示、告警欄位調整 | 設備管理: 小類更新時機優化、檔案上傳圖片換行 | 圖資管理:刪除與重新命名功能bug修正 | 維運管理:檔案上傳圖片換行 | 系統監控: 新增的設備正確顯示、系統小卡的資料Loading效果與顯示更新時間 2024-12-17 11:34:59 +08:00
b0d0194fe6 error.response 401的時候登出 | 歷史資料points換成item_name | 告警: 小類預設勾選第一個、告警時間設定新增 2024-12-12 10:28:02 +08:00
ccbacf9262 設備管理:修改時system_value、system_parent_id不能修改 | 運維管理 : 維修項目代碼(設備編號)要能搜尋 2024-12-11 18:10:14 +08:00
da8eb0d539 設備管理:新增修改刪除大小類 | upload組件避免誤觸label 2024-12-11 11:55:38 +08:00
d05f9ab130 forge定位修改 | 運維大類小類搜尋、廠商非必填 2024-12-06 17:18:18 +08:00
koko1108
3d06cab696 大小類篩選 2024-12-05 18:17:13 +08:00
5e13d284d3 Merge branch 'main' of https://gitea.mjm-staging.developers-homelab.net/ko.ko/CviLux_fe 2024-12-02 09:32:17 +08:00
4063c6a92d 系統監控 : 模型改用main_id來當spriteDbId 、 2D定位點預設顏色改成#009100 2024-12-02 09:32:07 +08:00
4769e77168 修正樓層3D設備顯示 2024-11-30 23:06:09 -05:00
4ab4aeb7f0 Merge branch 'feature/system' 2024-11-22 14:03:21 +08:00
eb60d1ed7d 更換favicon | 上傳新增檔案 ppt、zip、rar 2024-11-22 14:02:30 +08:00
3a766f0db6 修正2D圓點(卡片點選) 2024-11-21 14:02:16 -05:00
96fe7e279b Merge branch 'feature/system' 2024-11-21 09:27:53 +08:00
258f28e056 系統監控: 點擊card時zoom in 且 Sprite 放大 2024-11-21 09:27:30 +08:00
2693ad5713 修改Chart顏色色系 2024-11-20 14:12:57 +08:00
2dcfd957e5 更改平面圖原點大小 2024-11-18 20:19:27 -05:00
deb193d242 新增樓層3D設備顯示 2024-11-18 20:15:39 -05:00
01954e3f6e 能源管理色系改變 | 圖資家暫存區與路徑欄位 | 系統小卡fitToView退後一點 | 上傳組件新增格式說明 2024-11-15 18:12:28 +08:00
8c3e6b18f8 首頁: 用電比較修改 | 系統監控元件卸載時清除計時器 2024-11-11 09:52:27 +08:00
3460130e7e 系統監控getData帶入buildingStore.selectedBuilding 2024-11-08 14:36:27 +08:00
eee56c4273 歷史資料: 按鈕說明不換行、暫時移除table loadind | 能源管理: sankey的分支顏色明顯 2024-11-08 11:53:01 +08:00
ac867d368c Merge remote-tracking branch 'origin/feature/system' 2024-11-08 10:57:40 +08:00
f95f4cee85 系統監控: 熱圖修改多篩選、2D圖路徑有才讀、系統小卡圖示圖片顯示 2024-11-08 10:51:33 +08:00
5cb8e7da6e 刪除按鈕點選後新增"確認刪除" | 能源管理語言切換時即時更新 | 樓層新增與修改bug修正 | 系統監控filter暫時移除device_coordinate_3d條件 2024-11-06 17:27:03 +08:00
b6890e8311 rwd menu修改 2024-11-06 09:59:21 +08:00
14128e1fa8 調整info移動function 2024-11-05 10:08:02 -05:00
bba1b2892e modal的定位維持在中間且在拖曳時不偏移 | fitToView恢復 | 告警加上Device Category說明 2024-11-05 17:16:49 +08:00
a7054c07b5 用電即時分佈圖表 | 首頁進度小卡 | 系統小卡樣式修改 | 歷史資料新增大類 | 設備管理修改文字 2024-11-05 15:49:19 +08:00
1a02d199ed 樓層2D圖,改為ALL時,切換3D圖 2024-11-04 15:59:43 -05:00
c5345db462 能源管理api串接 2024-11-04 18:11:13 +08:00
3c876607ef Merge branch 'feature/assetMgmt' 2024-11-04 16:08:33 +08:00
211 changed files with 14808 additions and 36688 deletions

View File

@ -1,3 +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://mqttwss.mjm-staging.developers-homelab.net"
VITE_FORGE_BASEURL = "https://cgems.cvilux-group.com:8088/dist"

View File

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

View File

@ -1,3 +1,3 @@
VITE_API_BASEURL = "http://220.132.206.5:8008"
VITE_FILE_API_BASEURL = "http://220.132.206.5:8085/file"
VITE_FORGE_BASEURL = "http://localhost:5173"
VITE_API_BASEURL = "https://ibms-cvilux-demo-api.production.mjmtech.com.tw"
VITE_FILE_API_BASEURL = "https://cgems.cvilux-group.com:8088"
VITE_MQTT_BASEURL = "wss://mqttwss.mjm-staging.developers-homelab.net"

29
.github/prompts/exportCSV.prompt.md vendored Normal file
View File

@ -0,0 +1,29 @@
---
mode: agent
---
# API 路徑整理與引用檢查規則
## 目標
- 針對 apis 目錄下所有子資料夾(如 account、alert、asset、building、dashboard、energy、forge、graph、history、login、operation、productSetting、system的 api.js 與 index.js 檔案,完整追蹤 API 路徑的實際引用情形。
- 追蹤流程:
1. 先在 api.js 找出所有 API 路徑常數(如 `export const GET_XXX_API = '/path'`)。
2. 在 index.js 檔案確認這些常數有被 import 並包裝成 API function`getXXX`)。
3. 再全專案搜尋這些 API function 是否有被其他檔案 import 並呼叫。
- 產生一份 csv 報表,格式如下:
| API 路徑 | 定義常數 | API function | 是否有被引用 |
|----------|----------|--------------|-------------|
| /user | GET_USER_API | getUser | Y |
| /admin | GET_ADMIN_API | getAdmin | N |
## 詳細規則
- 處理 apis 目錄下所有子資料夾的 api.js 與 index.js 檔案。
- API 路徑的定義需涵蓋 get/post/put/delete 等(如 `export const API = '/path'`)。
- 只統計有被 index.js import 並包裝成 function 的 API 路徑。
- 檢查 function 是否有被其他檔案 import 並呼叫(排除 apis 目錄本身)。
- 匹配到的檔案需記錄完整路徑,可多個檔案以分號分隔。
- 統計結果輸出為 csv 檔案。
## 輸出
- 檔名api_usage_report.csv
- 欄位API 路徑, 定義常數, API function, 是否有被引用,

View File

@ -3,7 +3,7 @@
<html lang="en" data-theme="dracula">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.png" />
<link rel="icon" href="/favicon.ico" />
<link
rel="stylesheet"
href="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/style.css"
@ -12,11 +12,11 @@
<title>Cvilux EMS</title>
<script src="https://code.jquery.com/jquery-3.7.1.js"></script>
<script src="https://code.jquery.com/ui/1.13.3/jquery-ui.js"></script>
<script type="text/javascript" src="/requirejs/config.js"></script>
<script
<!-- <script type="text/javascript" src="/requirejs/config.js"></script> -->
<!-- <script
type="text/javascript"
src="/module/js/com/tridium/js/ext/require/require.min.js?"
></script>
></script> -->
</head>
<body>
<div id="app"></div>

909
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,8 @@
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
"preview": "vite preview",
"build:staging": "vite build --mode staging"
},
"dependencies": {
"@ant-design/icons-vue": "^7.0.1",
@ -21,13 +22,16 @@
"date-fns": "^3.3.1",
"dayjs": "^1.11.10",
"echarts": "^5.4.3",
"flag-icons": "^7.2.3",
"jquery-ui": "^1.14.1",
"json-schema-generator": "^2.0.6",
"mqtt": "^5.10.3",
"pinia": "^2.1.7",
"requirejs": "^2.3.6",
"tailwind-merge": "^2.2.1",
"vue": "^3.3.4",
"vue-i18n": "^10.0.4",
"vue-router": "^4.2.5",
"vuedraggable": "^4.1.0",
"yup": "^1.4.0",
"yup-phone-lite": "^2.0.1"
},

BIN
public/build_img.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -18,15 +18,16 @@ const cancelToastOpen = () => {
};
};
const openToast = (status, content, to = "body") => {
const openToast = (status, content, to = "body", confirm = null) => {
isToastOpen.value = {
open: true,
content,
status,
to,
confirm,
};
};
provide("app_toast", { openToast });
provide("app_toast", { openToast, cancelToastOpen });
</script>
<template>
@ -35,6 +36,7 @@ provide("app_toast", { openToast });
:open="isToastOpen.open"
:status="isToastOpen.status"
:cancel="cancelToastOpen"
:confirm="isToastOpen.confirm"
:to="isToastOpen.to"
/>
<div v-if="store.user.token" class="min-h-screen">

View File

@ -1,5 +1,6 @@
export const POST_ACK_API = `/obix/alarm`;
export const GET_ALERT_FORMID_API = `/Alert/AlertList`;
export const GET_ALERT_LOG_API = `api/Alarm/GetAlarmLog`;
export const POST_OPERATION_RECORD_API = `/operation/SavOpeRecord`;
export const GET_ALERT_SUB_LIST_API = `api/Device/GetMainSub`;
@ -9,8 +10,17 @@ export const GET_ALERT_MEMBER = `api/Alarm/GetAlarmMember`;
export const POST_ALERT_MEMBER = `api/Alarm/SaveAlarmMember`;
export const DELETE_ALERT_MEMBER = `api/Alarm/DeleteAlarmMember`;
export const GET_NOTICE_LIST_API = `api/Alarm/GetNotice`;
export const GET_SHOW_ALERT_API = `api/Alarm/GetShowAlarm`; // 取得告警顯示清單
export const GET_OUTLIERS_LIST_API = `api/Alarm/GetAlarmSetting`;
export const GET_OUTLIERS_DEVLIST_API = `api/Alarm/GetDevList`; // 取得設備
export const GET_OUTLIERS_POINTS_API = `api/Alarm/GetAlarmPoints`; // 取得點位
export const POST_OUTLIERS_SETTING_API = `api/Alarm/SaveAlarmSetting`; // 新增與修改
export const DELETE_OUTLIERS_SETTING_API = `api/Alarm/DeleteAlarmSetting`; // 刪除
export const GET_FACTOR_API = `api/Alarm/GetFactor`; // 刪除
export const GET_ALERT_SCHEDULE_LIST_API = `api/Alarm/GetAlarmSchedule`;
export const POST_ALERT_SCHEDULE = `api/Alarm/SaveAlarmSchedule`;
export const DELETE_ALERT_SCHEDULE = `api/Alarm/DeleteAlarmSchedule`;
export const POST_ALERT_MQTT_REFRESH = `api/Alarm/MQTTRefresh`;

View File

@ -1,48 +1,28 @@
import {
POST_ACK_API,
GET_ALERT_FORMID_API,
GET_ALERT_LOG_API,
POST_OPERATION_RECORD_API,
GET_ALERT_SUB_LIST_API,
GET_OUTLIERS_LIST_API,
GET_OUTLIERS_DEVLIST_API,
GET_OUTLIERS_POINTS_API,
POST_OUTLIERS_SETTING_API,
DELETE_OUTLIERS_SETTING_API,
GET_FACTOR_API,
GET_ALERT_MEMBER_LIST_API,
GET_ALERT_MEMBER,
POST_ALERT_MEMBER,
DELETE_ALERT_MEMBER,
GET_NOTICE_LIST_API,
GET_SHOW_ALERT_API,
GET_ALERT_SCHEDULE_LIST_API,
POST_ALERT_SCHEDULE,
DELETE_ALERT_SCHEDULE,
POST_ALERT_MQTT_REFRESH
} from "./api";
import instance from "@/util/request";
import apihandler from "@/util/apihandler";
import axios from "axios";
export const postChgAck = async (uuid) => {
try {
const data =
'<obj is="obix:AckAlarmIn"><str name="ackUser" val="obix" /></obj>';
const res = await axios.post(`${POST_ACK_API}/${uuid}/ack`, data, {
headers: {
"Content-Type": "text/plain",
},
});
// 解析XML錯誤信息
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(res.data, "text/xml");
const errElement = xmlDoc.querySelector("err");
if (errElement) {
console.error("Error in acknowledging alarm");
return { isSuccess: false, msg: `Error in acknowledging alarm` };
}
return { isSuccess: true };
} catch (error) {
console.error("Error in acknowledging alarm:", error);
return { isSuccess: false, msg: "Error in acknowledging alarm" };
}
};
export const getAlertFormId = async (uuid) => {
const res = await instance.post(GET_ALERT_FORMID_API, uuid);
@ -52,6 +32,24 @@ export const getAlertFormId = async (uuid) => {
});
};
export const getAlertLog = async ({
Start_date,
End_date,
isRecovery,
device_name_tag,
}) => {
const res = await instance.post(GET_ALERT_LOG_API, {
Start_date,
End_date,
isRecovery,
device_name_tag,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const postOperationRecord = async (formData) => {
const res = await instance.post(POST_OPERATION_RECORD_API, formData);
@ -61,8 +59,10 @@ export const postOperationRecord = async (formData) => {
});
};
export const getAlertSubList = async () => {
const res = await instance.post(GET_ALERT_SUB_LIST_API, {});
export const getAlertSubList = async (building_guid) => {
const res = await instance.post(GET_ALERT_SUB_LIST_API, {
building_guid,
});
return apihandler(res.code, res.data, {
msg: res.msg,
@ -148,6 +148,15 @@ export const getOutliersPoints = async (id) => {
});
};
export const getFactors = async () => {
const res = await instance.post(GET_FACTOR_API);
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const postOutliersSetting = async (data) => {
const res = await instance.post(POST_OUTLIERS_SETTING_API, data);
@ -156,3 +165,63 @@ export const postOutliersSetting = async (data) => {
code: res.code,
});
};
export const delOutliersSetting = async (Id) => {
const res = await instance.post(DELETE_OUTLIERS_SETTING_API, {
Id,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getShowAlarm = async () => {
const res = await instance.post(GET_SHOW_ALERT_API);
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getAlarmScheduleList = async () => {
const res = await instance.post(GET_ALERT_SCHEDULE_LIST_API, {});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const postAlertSchedule = async (data) => {
const res = await instance.post(POST_ALERT_SCHEDULE, data);
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const deleteAlarmSchedule = async (id) => {
try {
const res = await instance.post(DELETE_ALERT_SCHEDULE, { id });
return {
isSuccess: res.code === "0000",
msg: res.msg || "刪除成功",
};
} catch (error) {
console.error("API request failed", error);
return { isSuccess: false, msg: "API request failed" };
}
};
export const postMQTTRefresh = async () => {
const res = await instance.post(POST_ALERT_MQTT_REFRESH);
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};

View File

@ -1,5 +1,8 @@
export const GET_ASSET_SUB_LIST_API = `/AssetManage/GetAssetSubList`;
export const GET_ASSET_MAIN_LIST_API = `/AssetManage/GetAssetMainList`;
export const DELETE_ASSET_MAIN_LIST_API = `/AssetManage/DeleteAssetMain`;
export const POST_ASSET_MAIN_LIST_API = `/AssetManage/SaveAssetMain`;
export const GET_ASSET_SUB_LIST_API = `/AssetManage/GetAssetSubList`;
export const POST_ASSET_SUB_LIST_API = `/AssetManage/SaveAssetSub`;
export const DELETE_ASSET_SUB_LIST_API = `/AssetManage/DeleteAssetSub`;
@ -15,3 +18,22 @@ export const DELETE_ASSET_FLOOR_API = `/AssetManage/DeleteFloor`;
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`;
export const DELETE_ASSET_DEPARTMENT_API = `/AssetManage/DeleteDepartment`;
export const GET_ASSET_ELECTYPE_API = `/AssetManage/GetElecType`;
export const POST_ASSET_ELECTYPE_API = `/AssetManage/SaveElecType`;
export const DELETE_ASSET_ELECTYPE_API = `/AssetManage/DeleteElecType`;
export const POST_ASSET_ELEC_SETTING_API = `/AssetManage/SaveAssetSetting`;
export const POST_ASSET_MQTT_PUBLISH_API = `/api/mqtt/publish`;

View File

@ -1,6 +1,8 @@
import {
GET_ASSET_SUB_LIST_API,
GET_ASSET_MAIN_LIST_API,
DELETE_ASSET_MAIN_LIST_API,
POST_ASSET_MAIN_LIST_API,
GET_ASSET_SUB_LIST_API,
DELETE_ASSET_SUB_LIST_API,
POST_ASSET_SUB_LIST_API,
GET_ASSET_LIST_API,
@ -12,13 +14,26 @@ import {
DELETE_ASSET_ITEM_API,
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,
POST_ASSET_ELEC_SETTING_API,
POST_ASSET_MQTT_PUBLISH_API,
} from "./api";
import instance from "@/util/request";
import apihandler from "@/util/apihandler";
import { object } from "yup";
export const getAssetSubList = async () => {
const res = await instance.post(GET_ASSET_SUB_LIST_API);
export const getAssetMainList = async (building_guid) => {
const res = await instance.post(GET_ASSET_MAIN_LIST_API, { building_guid });
return apihandler(res.code, res.data, {
msg: res.msg,
@ -26,8 +41,8 @@ export const getAssetSubList = async () => {
});
};
export const getAssetMainList = async () => {
const res = await instance.post(GET_ASSET_MAIN_LIST_API);
export const deleteAssetMainItem = async (id) => {
const res = await instance.post(DELETE_ASSET_MAIN_LIST_API, { id });
return apihandler(res.code, res.data, {
msg: res.msg,
@ -35,17 +50,17 @@ export const getAssetMainList = async () => {
});
};
export const postAssetSubList = async ({
export const postAssetMainList = async ({
id,
system_key,
system_value,
system_parent_id,
id,
building_guid,
}) => {
const res = await instance.post(POST_ASSET_SUB_LIST_API, {
const res = await instance.post(POST_ASSET_MAIN_LIST_API, {
id,
system_key,
system_value,
system_parent_id,
id,
building_guid,
});
return apihandler(res.code, res.data, {
@ -54,6 +69,24 @@ export const postAssetSubList = async ({
});
};
export const getAssetSubList = async (id) => {
const res = await instance.post(GET_ASSET_SUB_LIST_API, { id });
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const postAssetSubList = async (formData) => {
const res = await instance.post(POST_ASSET_SUB_LIST_API, formData);
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const deleteAssetSubItem = async (id) => {
const res = await instance.post(DELETE_ASSET_SUB_LIST_API, { id });
@ -100,16 +133,15 @@ export const postAssetSingle = async (data) => {
});
} else {
value.forEach((element, index) => {
formData.append(`sub_device[${index}].device_number`, element.device_number);
formData.append(
`sub_device[${index}].device_number`,
element.device_number
);
formData.append(`sub_device[${index}].points`, element.points);
});
}
} else {
if (key === "device_number" && value === "") {
formData.append(key, "0");
} else {
formData.append(key, value);
}
formData.append(key, value);
}
}
@ -126,8 +158,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,
@ -175,3 +207,155 @@ export const getAssetSubPoint = async (sub_system_tag) => {
code: res.code,
});
};
export const getIOTSchema = async (variable_id) => {
const res = await instance.post(GET_ASSET_IOT_SCHEMA_API, { variable_id });
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const postIOTSchema = async ({ name, variable_id, points }) => {
const res = await instance.post(POST_ASSET_IOT_SCHEMA_API, {
name,
variable_id,
points,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getDeviceItem = async (variable_id) => {
const res = await instance.post(GET_ASSET_DEVICE_ITEM_API, { variable_id });
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const postDeviceItem = async ({
id,
variable_id,
full_name,
points,
decimals,
is_bool,
is_link,
show_event_switch_btn,
event_switch_on_message,
event_switch_off_message,
}) => {
const res = await instance.post(POST_ASSET_DEVICE_ITEM_API, {
id,
variable_id,
full_name,
points,
decimals,
is_bool,
is_link,
show_event_switch_btn,
event_switch_on_message,
event_switch_off_message,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const deleteDeviceItem = async (id) => {
const res = await instance.post(DELETE_ASSET_DEVICE_ITEM_API, { id });
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getDepartmentList = async () => {
const res = await instance.post(GET_ASSET_DEPARTMENT_API, {});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const postDepartmentList = async ({ name, id }) => {
const res = await instance.post(POST_ASSET_DEPARTMENT_API, {
name,
id,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const deleteDepartmentItem = async (id) => {
const res = await instance.post(DELETE_ASSET_DEPARTMENT_API, { id });
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getElecTypeList = async () => {
const res = await instance.post(GET_ASSET_ELECTYPE_API, {});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const postElecTypeList = async ({ name, id }) => {
const res = await instance.post(POST_ASSET_ELECTYPE_API, {
name,
id,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const deleteElecTypeItem = async (id) => {
const res = await instance.post(DELETE_ASSET_ELECTYPE_API, { id });
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const postAssetElecSetting = async (formData) => {
const res = await instance.post(POST_ASSET_ELEC_SETTING_API, formData);
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const postMQTTpublish = async ({ Topic, Payload }) => {
const res = await instance.post(POST_ASSET_MQTT_PUBLISH_API, {
Topic,
Payload,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};

View File

@ -1,4 +1,6 @@
export const GET_BUILDING_API = `/api/Device/GetBuild`;
export const GET_BUILDING_API = `/AssetManage/GetBuildingList`;
export const POST_BUILDING_API = `/AssetManage/SaveBuilding`;
export const DELETE_BUILDING_API = `/AssetManage/DeleteBuilding`;
export const GET_AUTHPAGE_API = `/api/GetUsrFroList`;
export const GET_SUBAUTHPAGE_API = `/api/Device/GetMainSub`;
export const GET_ALL_DEVICE_API = `/api/Device/GetAllDevice`;

View File

@ -1,5 +1,7 @@
import {
GET_BUILDING_API,
POST_BUILDING_API,
DELETE_BUILDING_API,
GET_AUTHPAGE_API,
GET_SUBAUTHPAGE_API,
GET_ALL_DEVICE_API,
@ -16,6 +18,27 @@ export const getBuildings = async () => {
});
};
export const postBuildings = async ({ full_name, building_guid }) => {
const res = await instance.post(POST_BUILDING_API, {
full_name,
building_guid,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const deleteBuildings = async (building_guid) => {
const res = await instance.post(DELETE_BUILDING_API, { building_guid });
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getAuth = async (lang) => {
const res = await instance.post(GET_AUTHPAGE_API, {
lang,
@ -26,10 +49,8 @@ export const getAuth = async (lang) => {
});
};
export const getAllSysSidebar = async () => {
const res = await instance.post(GET_SUBAUTHPAGE_API, {
building_tag: "",
});
export const getAllSysSidebar = async (building_guid) => {
const res = await instance.post(GET_SUBAUTHPAGE_API, {building_guid});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,

View File

@ -6,4 +6,12 @@ export const GET_DASHBOARD_ROOM_TEMP_API = `/SituationRoom/GetFormulaRoomStatusD
export const GET_DASHBOARD_ENERGY_API = `/SituationRoom/GetEnergeData`;
export const POST_DASHBOARD_PRODUCT_TARGET_SETTING_API = `/SituationRoom/SetTargetSetting`;
export const GET_DASHBOARD_PRODUCT_TARGET_SETTING_API = `/SituationRoom/GetTargetSetting`
export const GET_DASHBOARD_PRODUCT_HISTORY_API = `/SituationRoom/GetProductionHistory`
export const GET_DASHBOARD_PRODUCT_HISTORY_API = `/SituationRoom/GetProductionHistory`
export const GET_DASHBOARD_ENERGY_INFO_API = `api/dashboard/GetEnergyInfo`
export const GET_DASHBOARD_ENERGY_COST_API = `api/dashboard/GetEnergyCost`
export const GET_DASHBOARD_ALARMOPERATION_INFO_API = `api/dashboard/GetAlarmOperationInfo`
export const GET_DASHBOARD_2D3DINFO_API = `api/setting/visual/query`
export const POST_DASHBOARD_2D3DINFO_API = `api/setting/visual/update`

View File

@ -8,6 +8,11 @@ import {
POST_DASHBOARD_PRODUCT_TARGET_SETTING_API,
GET_DASHBOARD_PRODUCT_TARGET_SETTING_API,
GET_DASHBOARD_PRODUCT_HISTORY_API,
GET_DASHBOARD_ENERGY_INFO_API,
GET_DASHBOARD_ENERGY_COST_API,
GET_DASHBOARD_ALARMOPERATION_INFO_API,
GET_DASHBOARD_2D3DINFO_API,
POST_DASHBOARD_2D3DINFO_API
} from "./api";
import instance from "@/util/request";
import apihandler from "@/util/apihandler";
@ -135,3 +140,60 @@ export const getDashboardProductRecord = async ({ start_time, end_time }) => {
});
};
export const getEnergyInfo = async (building_guid) => {
const res = await instance.post(GET_DASHBOARD_ENERGY_INFO_API, {
building_guid,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getEnergyCost = async ({
department_id,
floor_guid,
building_guid,
}) => {
const res = await instance.post(GET_DASHBOARD_ENERGY_COST_API, {
department_id,
floor_guid,
building_guid,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getAlarmOperationInfo = async (building_guid) => {
const res = await instance.post(GET_DASHBOARD_ALARMOPERATION_INFO_API, {
building_guid,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getDashboard2D3D = async (BuildingId) => {
const res = await instance.post(GET_DASHBOARD_2D3DINFO_API, {
BuildingId});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const posttDashboard2D3D = async (formData) => {
const res = await instance.post(POST_DASHBOARD_2D3DINFO_API, formData);
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};

23
src/apis/energy/api.js Normal file
View File

@ -0,0 +1,23 @@
export const GET_REALTIME_DIST_API = `/api/Energe/GetRealTimeDistribution`;
export const GET_ELECUSE_DAY_API = `/api/Energe/GetElecUseDay`;
export const GET_TAI_POWER_API = `/api/Energe/GetTaipower`;
export const GET_SIDEBAR_API = `/api/GetSideBar`;
export const GET_SEARCH_API = `/api/Energe/GetFilter`;
export const GET_REPORT_API = `/api/Energe/GetReport`;
export const GET_Excel_API = `/api/Energe/GetReportExcel`;
// 即時需量
export const GET_DEMAND_API = `/api/Energe/SearchDemandValue`;
export const POST_ADD_DEMAND_API = `/api/Energe/AddDemandValue`;
export const POST_EDIT_DEMAND_API = `/api/Energe/UpdateDemandValue`;
export const GET_REALTIME_DEMAND_API = `/api/Energe/GetRealTimeDemand`;
// 碳排係數
export const GET_CARBON_API = `/api/Energe/SearchCarbonValue`;
export const POST_EDIT_CARBON_API = `/api/Energe/UpdateCarbonValue`;
// 時間電價
export const GET_TIME_ELEC_API = `/api/Energe/SearchTimeElec`;
export const POST_TIME_ELEC_API = `/api/Energe/UpdateTimeElecValue`;

232
src/apis/energy/index.js Normal file
View File

@ -0,0 +1,232 @@
import {
GET_REALTIME_DIST_API,
GET_ELECUSE_DAY_API,
GET_TAI_POWER_API,
GET_SIDEBAR_API,
GET_SEARCH_API,
GET_REPORT_API,
GET_Excel_API,
GET_DEMAND_API,
POST_EDIT_DEMAND_API,
GET_REALTIME_DEMAND_API,
GET_CARBON_API,
POST_EDIT_CARBON_API,
GET_TIME_ELEC_API,
POST_TIME_ELEC_API,
} from "./api";
import instance, { fileInstance } from "@/util/request";
import apihandler from "@/util/apihandler";
import downloadExcel from "@/util/downloadExcel";
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,
code: res.code,
});
};
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,
code: res.code,
});
};
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,
code: res.code,
});
};
export const getSideBar = async (system_type) => {
const res = await instance.post(GET_SIDEBAR_API, { system_type });
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getEnergySearch = async (type) => {
const res = await instance.post(GET_SEARCH_API, { type });
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getReport = async ({
department,
elecType,
floor,
start_time,
end_time,
type,
}) => {
const res = await instance.post(GET_REPORT_API, {
department,
elecType,
floor,
start_time,
end_time,
type,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getExcel = async ({
department,
elecType,
floor,
start_time,
end_time,
type,
}) => {
const res = await fileInstance.post(
GET_Excel_API,
{
department,
elecType,
floor,
start_time,
end_time,
type,
},
{ responseType: "blob" }
);
return apihandler(
res.code,
res,
{
msg: res.msg,
code: res.code,
},
downloadExcel
);
};
export const getDemand = async (building_guid) => {
const res = await instance.post(GET_DEMAND_API, { building_guid });
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const postEditDemand = async ({
id,
contract,
alert,
reset,
building_guid,
}) => {
const res = await instance.put(POST_EDIT_DEMAND_API, {
id,
contract,
alert,
reset,
building_guid,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getCarbonValue = async (building_guid) => {
const res = await instance.post(GET_CARBON_API, { building_guid });
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const postEditCarbonValue = async ({
id,
coefficient,
building_guid,
}) => {
const res = await instance.put(POST_EDIT_CARBON_API, {
id,
coefficient,
building_guid,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getRealTimeDemand = async (building_guid) => {
const res = await instance.post(GET_REALTIME_DEMAND_API, { building_guid });
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const getTimeElec = async (building_guid) => {
const res = await instance.post(GET_TIME_ELEC_API, { building_guid });
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};
export const postTimeElec = async ({ sheet, cost, building_guid }) => {
const res = await instance.put(POST_TIME_ELEC_API, {
sheet,
cost,
building_guid,
});
return apihandler(res.code, res.data, {
msg: res.msg,
code: res.code,
});
};

View File

@ -85,8 +85,8 @@ export const addGraphTableData = async (formData) => {
});
};
export const delGraphData = async (id) => {
const res = await instance.post(DELETE_GRAPH_TABLE_API, { id });
export const delGraphData = async (id, hard_delete = false, recover_delete = false) => {
const res = await instance.post(DELETE_GRAPH_TABLE_API, { id, hard_delete, recover_delete });
return apihandler(res.code, res.data, {
msg: res.msg,

View File

@ -4,6 +4,10 @@ export const GET_HISTORY_SIDEBAR_API = `/api/History/GetDeviceInfo`;
export const GET_HISTORY_POINT_API = `/api/History/GetAllDevPoi`;
export const GET_HISTORY_DATA_API = `/api/History/GetHistoryData`;
export const GET_HISTORY_EXPORT_API = `/api/ExportHistoryExcel`;
export const GET_HISTORY_EXPORT_REPORT_API = `/api/History/GetHistoryExcelReport`;
export const GET_HISTORY_EXPORT_CURVE_API = `/api/History/GetHistoricalCurveExcelReport`;
export const GET_HISTORY_EXPORT_QUICK_API = `/api/History/GetQuickMeteringExcelReport`;
export const GET_HISTORY_EXPORT_CLASS_API = `/api/History/GetElectricityClassificationExcelReport`;
export const GET_HISTORY_FAVORITE_API = `/api/History/GetHistoryFavorite`;
export const POST_HISTORY_FAVORITE_API = `/api/History/SaveHistoryFavorite`;

View File

@ -7,14 +7,26 @@ import {
DELETE_HISTORY_FAVORITE_API,
UPDATE_HISTORY_FAVORITE_API,
GET_HISTORY_EXPORT_API,
GET_HISTORY_EXPORT_REPORT_API,
GET_HISTORY_EXPORT_CURVE_API,
GET_HISTORY_EXPORT_QUICK_API,
GET_HISTORY_EXPORT_CLASS_API,
} from "./api";
import instance, { fileInstance } from "@/util/request";
import apihandler from "@/util/apiHandler";
import downloadExcel from "@/util/downloadExcel";
export const getHistorySideBar = async (sub_system_tag) => {
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, {
@ -42,6 +54,7 @@ export const getHistoryData = async ({
End_time,
Device_list,
Points,
table_type,
}) => {
/*
{
@ -62,6 +75,7 @@ export const getHistoryData = async ({
Device_list: Array.isArray(Device_list) ? Device_list : [Device_list],
Points: Array.isArray(Points) ? Points : [Points],
Type: parseInt(Type),
table_type: parseInt(table_type),
});
return apihandler(res.code, res.data, {
@ -71,7 +85,52 @@ export const getHistoryData = async ({
};
export const getHistoryExportData = async ({
Start_date,
End_date,
Start_time,
End_time,
Device_list,
Points,
Type,
table_type,
}) => {
const api =
parseInt(table_type) === 1
? GET_HISTORY_EXPORT_CURVE_API
: parseInt(table_type) === 2
? GET_HISTORY_EXPORT_QUICK_API
: parseInt(table_type) === 3
? GET_HISTORY_EXPORT_CLASS_API
: GET_HISTORY_EXPORT_API;
const res = await fileInstance.post(
api,
{
Start_date: Start_date,
End_date: End_date,
Start_time: Start_time,
End_time: End_time,
Points: Array.isArray(Points) ? Points : [Points],
Device_list: Array.isArray(Device_list) ? Device_list : [Device_list],
Type: parseInt(Type),
Building_tag_list: [...new Set(Device_list.map((d) => d.split("_")[1]))],
table_type: parseInt(table_type),
},
{ responseType: "blob" }
);
return apihandler(
res.code,
res,
{
msg: res.msg,
code: res.code,
},
downloadExcel
);
};
export const getHistoryExportReport = async ({
Start_date,
End_date,
Start_time,
@ -79,19 +138,8 @@ export const getHistoryExportData = async ({
Device_list,
Points,
}) => {
/*
{
Type,
Start_date,
End_date,
Start_time,
End_time,
Device_list,
Points,
}
*/
const res = await fileInstance.post(
GET_HISTORY_EXPORT_API,
GET_HISTORY_EXPORT_REPORT_API,
{
// ...exportContent,
Start_date: Start_date,
@ -100,7 +148,6 @@ export const getHistoryExportData = async ({
End_time: End_time,
Points: Array.isArray(Points) ? Points : [Points],
Device_list: Array.isArray(Device_list) ? Device_list : [Device_list],
Type: parseInt(Type),
Building_tag_list: [...new Set(Device_list.map((d) => d.split("_")[1]))],
},
{ responseType: "blob" }

View File

@ -13,6 +13,10 @@ export async function Login({ account, password }) {
document.cookie = `JWT-Authorization=${res.data.token}; Max-Age=${
24 * 60 * 60 * 1000
}`;
// 設定 user_name Cookie
document.cookie = `user_name=${res.data.user_name}; Max-Age=${
24 * 60 * 60 * 1000
}`;
}
return apihandler(res.code, res.data, {

View File

@ -6,6 +6,7 @@ import {
POST_OPERATION_RECORD_API,
GET_OPERATION_EXPORT_API,
GET_OPERATION_FORMID_API,
DELETE_OPERATION_RECORD_API,
POST_OPERATION_COMPANY_API,
UPDATE_OPERATION_COMPANY_API,
DELETE_OPERATION_COMPANY_API,
@ -23,10 +24,10 @@ export const getOperationRecord = async ({
}) => {
const res = await instance.post(GET_OPERATION_RECORD_API, {
work_type: parseInt(work_type),
start_created_at: dayjs(start_created_at).format("YYYY-MM-DDTHH:mm:ss"),
end_created_at: dayjs(end_created_at)
.date(dayjs(end_created_at).get("date") + 1)
.format("YYYY-MM-DDTHH:mm:ss"),
// start_created_at: dayjs(start_created_at).format("YYYY-MM-DDTHH:mm:ss"),
// end_created_at: dayjs(end_created_at)
// .date(dayjs(end_created_at).get("date") + 1)
// .format("YYYY-MM-DDTHH:mm:ss"),
serial_number: serial_number || null,
main_system_tag: null,
sub_system_tag:

View File

@ -20,13 +20,13 @@ export const getSystemFloors = async (building_tag, sub_system_tag) => {
export const getSystemDevices = async ({
sub_system_tag,
building_tag,
floor_tag,
building_guid,
department_id_list,
}) => {
const res = await instance.post(GET_SYSTEM_DEVICE_LIST_API, {
sub_system_tag,
building_tag,
floor_tag,
building_guid,
department_id_list,
});
return apihandler(res.code, res.data, {

View File

@ -58,6 +58,7 @@
box-sizing: border-box;
margin: 0;
font-weight: normal;
scrollbar-color: auto !important;
}
body {

View File

@ -3,9 +3,9 @@
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 124.4 12" style="enable-background:new 0 0 124.4 12;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0CA9D4;}
.st0{fill:#17CEE3;}
.st1{opacity:0.3;fill:#969696;}
.st2{opacity:0.8;fill:#0CA9D4;}
.st2{opacity:0.8;fill:#17CEE3;}
</style>
<g>
<polygon class="st0" points="92.7,2.9 31.5,2.9 28.8,0 95.6,0 "/>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1 +1,12 @@
<svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 124.4 12.01"><defs><style>.cls-1,.cls-3{fill:#ffe422;}.cls-2{fill:#969696;opacity:0.3;}.cls-3{opacity:0.8;}</style></defs><polygon class="cls-1" points="92.68 2.87 31.5 2.87 28.8 0 95.59 0 92.68 2.87"/><path class="cls-2" d="M124.4.14V11.86c0,.08-.3.15-.66.15H97.84a2.27,2.27,0,0,1-.5,0l-1.76-.45a2.34,2.34,0,0,0-.5,0H29.76a2.2,2.2,0,0,0-.5,0L27.5,12a2.34,2.34,0,0,1-.5,0H.66C.3,12,0,11.94,0,11.86V.14C0,.06.3,0,.66,0L26.87.11a1.79,1.79,0,0,1,.5.05L29,2.25a2.27,2.27,0,0,0,.5,0H94.86a2.34,2.34,0,0,0,.5,0L97.25.16a1.83,1.83,0,0,1,.5,0l26-.11C124.1,0,124.4.06,124.4.14Z"/><path class="cls-3" d="M122.92,12V2.12a.89.89,0,0,0-.89-.89H96.41a.9.9,0,0,0-.67.31l-1.83,1.6a.36.36,0,0,1-.29.13H30.73a.36.36,0,0,1-.29-.13l-1.6-1.6a.9.9,0,0,0-.67-.31H2.37a.89.89,0,0,0-.89.89V12m.25,0V2.12a.64.64,0,0,1,.64-.64h25.8a.65.65,0,0,1,.48.23l1.6,1.59a.64.64,0,0,0,.48.22H93.62a.62.62,0,0,0,.48-.22l1.83-1.59a.65.65,0,0,1,.48-.23H122a.64.64,0,0,1,.64.64V12"/></svg>
<svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 124.4 12.01">
<defs>
<style>
.cls-1,.cls-3 { fill: #E4EA00; }
.cls-2 { fill: #969696; opacity: 0.3; }
.cls-3 { opacity: 0.8; }
</style>
</defs>
<polygon class="cls-1" points="92.68 2.87 31.5 2.87 28.8 0 95.59 0 92.68 2.87"/>
<path class="cls-2" d="M124.4.14V11.86c0,.08-.3.15-.66.15H97.84a2.27,2.27,0,0,1-.5,0l-1.76-.45a2.34,2.34,0,0,0-.5,0H29.76a2.2,2.2,0,0,0-.5,0L27.5,12a2.34,2.34,0,0,1-.5,0H.66C.3,12,0,11.94,0,11.86V.14C0,.06.3,0,.66,0L26.87.11a1.79,1.79,0,0,1,.5.05L29,2.25a2.27,2.27,0,0,0,.5,0H94.86a2.34,2.34,0,0,0,.5,0L97.25.16a1.83,1.83,0,0,1,.5,0l26-.11C124.1,0,124.4.06,124.4.14Z"/>
<path class="cls-3" d="M122.92,12V2.12a.89.89,0,0,0-.89-.89H96.41a.9.9,0,0,0-.67.31l-1.83,1.6a.36.36,0,0,1-.29.13H30.73a.36.36,0,0,1-.29-.13l-1.6-1.6a.9.9,0,0,0-.67-.31H2.37a.89.89,0,0,0-.89.89V12m.25,0V2.12a.64.64,0,0,1,.64-.64h25.8a.65.65,0,0,1,.48.23l1.6,1.59a.64.64,0,0,0,.48.22H93.62a.62.62,0,0,0,.48-.22l1.83-1.59a.65.65,0,0,1,.48-.23H122a.64.64,0,0,1,.64.64V12"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 124.4 12" style="enable-background:new 0 0 124.4 12;" xml:space="preserve">
<style type="text/css">
.st0{fill:#62E39A;}
.st1{opacity:0.3;fill:#969696;}
.st2{opacity:0.8;fill:#62E39A;}
</style>
<g>
<polygon class="st0" points="92.7,2.9 31.5,2.9 28.8,0 95.6,0 "/>
<path class="st1" d="M124.4,0.1v11.7c0,0.1-0.3,0.1-0.7,0.1H97.8c-0.2,0-0.4,0-0.5-0.1l-1.8-0.4c-0.1,0-0.3-0.1-0.5-0.1H29.8
c-0.2,0-0.4,0-0.5,0.1L27.5,12C27.4,12,27.2,12,27,12H0.7C0.3,12,0,11.9,0,11.9V0.1C0,0.1,0.3,0,0.7,0l26.2,0.1
c0.2,0,0.4,0,0.5,0.1L29,2.2c0.1,0,0.3,0.1,0.5,0.1h65.3c0.2,0,0.4,0,0.5-0.1l1.9-2.1c0.1,0,0.3-0.1,0.5-0.1l26-0.1
C124.1,0,124.4,0.1,124.4,0.1z"/>
<path class="st2" d="M122.9,12V2.1c0-0.5-0.4-0.9-0.9-0.9H96.4c-0.3,0-0.5,0.1-0.7,0.3l-1.8,1.6c-0.1,0.1-0.2,0.1-0.3,0.1H30.7
c-0.1,0-0.2,0-0.3-0.1l-1.6-1.6c-0.2-0.2-0.4-0.3-0.7-0.3H2.4c-0.5,0-0.9,0.4-0.9,0.9V12 M1.7,12V2.1c0-0.4,0.3-0.6,0.6-0.6h25.8
c0.2,0,0.4,0.1,0.5,0.2l1.6,1.6c0.1,0.1,0.3,0.2,0.5,0.2h62.9c0.2,0,0.4-0.1,0.5-0.2l1.8-1.6c0.1-0.1,0.3-0.2,0.5-0.2H122
c0.4,0,0.6,0.3,0.6,0.6V12"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="圖層_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 124.4 12" style="enable-background:new 0 0 124.4 12;" xml:space="preserve">
<style type="text/css">
.st0{fill:#E9971F;}
.st1{opacity:0.3;fill:#969696;}
.st2{opacity:0.8;fill:#E9971F;}
</style>
<g>
<polygon class="st0" points="92.7,2.9 31.5,2.9 28.8,0 95.6,0 "/>
<path class="st1" d="M124.4,0.1v11.7c0,0.1-0.3,0.1-0.7,0.1H97.8c-0.2,0-0.4,0-0.5-0.1l-1.8-0.4c-0.1,0-0.3-0.1-0.5-0.1H29.8
c-0.2,0-0.4,0-0.5,0.1L27.5,12C27.4,12,27.2,12,27,12H0.7C0.3,12,0,11.9,0,11.9V0.1C0,0.1,0.3,0,0.7,0l26.2,0.1
c0.2,0,0.4,0,0.5,0.1L29,2.2c0.1,0,0.3,0.1,0.5,0.1h65.3c0.2,0,0.4,0,0.5-0.1l1.9-2.1c0.1,0,0.3-0.1,0.5-0.1l26-0.1
C124.1,0,124.4,0.1,124.4,0.1z"/>
<path class="st2" d="M122.9,12V2.1c0-0.5-0.4-0.9-0.9-0.9H96.4c-0.3,0-0.5,0.1-0.7,0.3l-1.8,1.6c-0.1,0.1-0.2,0.1-0.3,0.1H30.7
c-0.1,0-0.2,0-0.3-0.1l-1.6-1.6c-0.2-0.2-0.4-0.3-0.7-0.3H2.4c-0.5,0-0.9,0.4-0.9,0.9V12 M1.7,12V2.1c0-0.4,0.3-0.6,0.6-0.6h25.8
c0.2,0,0.4,0.1,0.5,0.2l1.6,1.6c0.1,0.1,0.3,0.2,0.5,0.2h62.9c0.2,0,0.4-0.1,0.5-0.2l1.8-1.6c0.1-0.1,0.3-0.2,0.5-0.2H122
c0.4,0,0.6,0.3,0.6,0.6V12"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,25 +1,41 @@
<script setup>
import { onMounted } from "vue";
import useAlarmStore from "@/stores/useAlarmStore";
import { ackSingleAlarm } from "@/apis/building";
import { ref, onMounted, onUnmounted } from "vue";
import { getAlertLog } from "@/apis/alert";
import dayjs from "dayjs";
const store = useAlarmStore();
const dataSource = ref([]);
let intervalId = null; // setInterval ID
const getAlarmData = async () => {
const res = await getAlertLog({
isRecovery: 1,
Start_date: dayjs().format("YYYY-MM-DD"),
End_date: dayjs().format("YYYY-MM-DD"),
});
dataSource.value = (res.data || []).map((d) => ({ ...d, key: d.id }));
};
onMounted(() => {
store.getAlarmDataFromBaja();
getAlarmData();
intervalId = setInterval(() => {
getAlarmData();
}, 30 * 1000);
});
const ackedAlarm = async (uuid) => {
const res = await ackSingleAlarm(uuid);
console.log("ackedAlarm", res);
};
onUnmounted(() => {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
});
</script>
<template>
<div>
<ul class="pr-4 min-h-full text-base-content">
<!-- Sidebar content here -->
<li class="my-3" v-for="alarm in store.alarmData" :key="alarm.uuid">
<li class="my-3" v-for="alarm in dataSource" :key="alarm.id">
<div
class="w-full shadow-xl border border-success bg-body bg-opacity-80"
>
@ -34,30 +50,22 @@ const ackedAlarm = async (uuid) => {
>
<small>
<span class="mr-4"
>{{ alarm.timestamp_date }} {{ alarm.timestamp_time }}</span
>{{ alarm.created_at }}</span
>
<font-awesome-icon
<!-- <font-awesome-icon
:icon="['fas', 'times']"
size="lg"
class="text-white"
/>
/> -->
</small>
</p>
<div class="divider my-2"></div>
<div>
<p>{{ $t("alarm.number") }}{{ alarm.uuid }}</p>
<!-- <p>異常等級255</p> -->
<p>{{ $t("alarm.category") }}{{ alarm.alarmClass }}</p>
<p>{{ $t("alarm.device_name") }}{{ alarm.full_name }}</p>
<p>{{ $t("alarm.message") }}{{ alarm.msg }}</p>
</div>
<div class="card-actions mt-1 justify-end">
<button
class="btn btn-sm btn-success"
@click="() => ackedAlarm(alarm.uuid)"
>
{{ $t("alarm.confirm") }}
</button>
<p>{{ $t("alarm.number") }}{{ alarm.id }}</p>
<p>{{ $t("alert.alarmClass") }}{{ alarm.factor }}</p>
<p>{{ $t("alarm.device_name") }}{{ alarm.device_number }}</p>
<p>{{ $t("alert.device_point_name") }}{{ alarm.points }}</p>
<p>{{ $t("alert.error_msg") }}{{ alarm.reason }}</p>
</div>
</div>
</div>
@ -68,12 +76,12 @@ const ackedAlarm = async (uuid) => {
<style lang="scss" scoped>
.card::before {
@apply absolute h-5 w-5 top-1 left-1 bg-no-repeat z-10 bg-[url('../../assets/img/table/content-box-background01.svg')] bg-center;
@apply absolute h-5 w-5 top-1 left-1 bg-no-repeat z-10 bg-[url('../../assets/img/table/content-box-background01.svg')] bg-center;
content: "";
}
.card::after {
@apply absolute bottom-1 right-1 h-5 w-5 bg-no-repeat z-10 bg-[url('../../assets/img/table/content-box-background05.svg')] bg-center;
@apply absolute bottom-1 right-1 h-5 w-5 bg-no-repeat z-10 bg-[url('../../assets/img/table/content-box-background05.svg')] bg-center;
content: "";
}
</style>
</style>

View File

@ -16,7 +16,7 @@ const toggleErrIcon = () => {
<!-- Page content here -->
<label
for="alarm"
class="drawer-button flex flex-col justify-center items-center btn-group"
class="drawer-button flex flex-col justify-center items-center bg-transparent w-[95px]"
@click="toggleErrIcon"
>
<font-awesome-icon
@ -31,12 +31,12 @@ const toggleErrIcon = () => {
size="2x"
class="text-white w-10 m-auto"
/>
<span class="text-white"> {{ $t("alarm.title") }}</span>
<span class="text-white block mt-0"> {{ $t("alarm.title") }}</span>
</label>
</div>
<div
v-if="showErr"
class="drawer-side translate-y-20 max-h-[90vh] overflow-x-hidden overflow-y-scroll"
class="drawer-side translate-y-20 max-h-[90vh] overflow-x-hidden overflow-y-scroll w-[300px] left-auto right-0"
>
<AlarmCards />
</div>

View File

@ -1,6 +1,6 @@
<script setup>
import * as echarts from "echarts";
import { onMounted, ref, markRaw } from "vue";
import { onMounted, ref, markRaw, nextTick } from "vue";
import axios from "axios";
const props = defineProps({
@ -24,9 +24,15 @@ async function updateSvg(svg, option) {
} else {
clear();
}
axios.get(svg.path).then(({ data }) => {
axios.get(svg.path).then(async ({ data }) => {
echarts.registerMap(svg.full_name, { svg: data });
chart.value.setOption(option);
await nextTick();
// 調 setOption
setTimeout(() => {
if (chart.value && !chart.value.isDisposed()) {
chart.value.setOption(option);
}
}, 0);
if (props.getCoordinate) {
chart.value.getZr().on("click", function (params) {
var pixelPoint = [params.offsetX, params.offsetY];
@ -44,11 +50,15 @@ async function updateSvg(svg, option) {
value: dataPoint, //
itemStyle: { color: "#0000FF" }, //
});
chart.value.setOption({
series: {
data: updatedData,
},
});
setTimeout(() => {
if (chart.value && !chart.value.isDisposed()) {
chart.value.setOption({
series: {
data: updatedData,
},
});
}
}, 0);
});
}
});

View File

@ -118,6 +118,6 @@ const curWidth = computed(() => {
</style>
<style scoped>
.line {
@apply mr-3 relative z-20 after:absolute after:top-1/2 after:-right-3 after:w-3 after:h-[1px] after:bg-info after:z-10 last:after:h-0;
@apply mr-3 relative z-30 after:absolute after:top-1/2 after:-right-3 after:w-3 after:h-[1px] after:bg-info after:z-10 last:after:h-0;
}
</style>

View File

@ -0,0 +1,436 @@
<script setup>
import { twMerge } from "tailwind-merge";
import { computed, defineProps, provide, ref, watch } from "vue";
import Pagination from "@/components/customUI/Pagination.vue";
import Checkbox from "@/components/customUI/Checkbox.vue";
import draggable from "vuedraggable";
import dayjs from "dayjs";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
/*
column={
title,key,class, width, filter:Boolean, sort:Boolean
}
*/
const props = defineProps({
columns: Array,
dataSource: Array,
rowKey: String,
withStyle: {
type: Boolean,
default: true,
},
withDraggable: {
type: Boolean,
default: false,
},
pagination: { type: Boolean, default: true } || {
pageSize: Number,
totalPages: Number,
totalItems: Number,
},
loading: Boolean,
});
const currentDataSource = ref([]);
const dataSourceStorage = ref([]);
const isDraggable = ref(props.withDraggable);
watch(
() => props.dataSource,
(newValue) => {
dataSourceStorage.value = newValue;
filterItems.value = Object.fromEntries(
props.columns.map((c, i) => [
c.key,
[...new Set(newValue.map((d) => d[c.key]))].map((name) => ({
name,
selected: false,
})),
])
);
}
);
const updateDataSource = (data) => {
console.log("update", data);
currentDataSource.value = data;
};
provide("current_table_data", {
updateDataSource,
});
const sortRule = ref({});
const filterColumn = ref({});
const filterItems = ref({});
const selectedFilterItem = ref({});
const toggleFilterModal = (key) => {
let newFilter = Object.assign(filterColumn.value);
for (let oKey in newFilter) {
newFilter[oKey] = key === oKey && !newFilter[key];
}
filterColumn.value = newFilter;
};
watch(
() => props.columns,
(newValue) => {
sortRule.value = Object.fromEntries(newValue.map((c) => [c.key, 0]));
filterColumn.value = Object.fromEntries(
newValue.map((c, i) => [c.key, false])
);
selectedFilterItem.value = Object.fromEntries(
newValue.map((c, i) => [c.key, []])
);
},
{
immediate: true,
}
);
/*
0:取消
1:ascending
2:descending
*/
const toggleSortRule = (key) => {
let newSort = Object.assign(sortRule.value);
for (let oKey in newSort) {
newSort[oKey] = key === oKey ? newSort[key] : 0;
}
sortRule.value = newSort;
};
const sort = (column) => {
toggleSortRule(column);
const cantSort = ["object", "boolean"];
console.log(props.dataSource?.[0][column]);
if (cantSort.includes(typeof props.dataSource?.[0][column])) return;
// ->
const newArray = Object.assign(props.dataSource, []).sort((a, b) => {
// if (column === "timestamp") {
// return dayjs(a[column]).valueOf() - dayjs(b[column]).valueOf();
// }
if (typeof a[column] === "number") return a[column] - b[column];
else if (typeof a[column] === "string") {
console.log(a[column], b[column], a[column].localeCompare(b[column]));
return a[column].localeCompare(b[column]);
}
// return parseInt(a[column]) - parseInt(b[column]);
});
if (sortRule.value[column] === 0) {
sortRule.value[column] = 1;
dataSourceStorage.value = newArray;
} else if (sortRule.value[column] === 1) {
sortRule.value[column] = 2;
dataSourceStorage.value = newArray.reverse();
} else if (sortRule.value[column] === 2) {
sortRule.value[column] = 0;
dataSourceStorage.value = props.dataSource;
}
};
const form = ref(null);
const onFilter = (key, reset = false) => {
const formData = new FormData(form.value);
reset && formData.delete(key);
for (let [name, value] of formData) {
console.log(name, value);
}
selectedFilterItem.value[key] = formData.getAll(key);
toggleFilterModal(key);
};
watch(
selectedFilterItem,
(newVal) => {
let newData = Object.assign(props.dataSource);
for (let key in newVal) {
if (newVal[key].length > 0) {
newData = newData.filter((d) => newVal[key].includes(d[key]));
}
}
dataSourceStorage.value = newData;
},
{
deep: true,
}
);
</script>
<template>
<div :class="withStyle ? 'content-box' : 'py-5'">
<div class="content-decoration">
<button
v-if="withDraggable"
class="btn btn-sm mb-2"
@click.stop.prevent="isDraggable = !isDraggable"
>
<font-awesome-icon :icon="['fas', 'stream']" />
{{ isDraggable ? "開始排序" : "完成排序" }}
</button>
<slot name="beforeTable"></slot>
<form ref="form" class="overflow-x-auto">
<table
:class="
twMerge(
withStyle ? 'table' : 'table border',
currentDataSource.length === 0 ? 'h-28' : ''
)
"
>
<!-- head -->
<thead>
<tr>
<th
v-for="column in columns"
:key="column.key"
:class="`${column.class ? column.class : ''}`"
:style="{
width: `${
column.width
? typeof column.width === 'string'
? column.width
: column.width + 'px'
: 'auto'
}`,
}"
>
<span class="flex justify-center">
{{ column.title }}
<div
v-if="column.sort"
class="flex flex-col justify-center w-3 mx-2 relative"
@click="() => sort(column.key)"
>
<font-awesome-icon
:icon="['fas', 'sort-up']"
:class="
twMerge(
'absolute top-0',
sortRule[column.key] === 1 ? 'text-success' : ''
)
"
size="lg"
/>
<font-awesome-icon
:icon="['fas', 'sort-down']"
:class="
twMerge(
'absolute bottom-1',
sortRule[column.key] === 2 ? 'text-success' : ''
)
"
size="lg"
/>
</div>
<div class="ml-2 relative" v-if="column.filter">
<font-awesome-icon
:icon="['fas', 'filter']"
:class="
twMerge(
filterColumn[column.key] ||
selectedFilterItem[column.key].length > 0
? 'text-success'
: ''
)
"
@click="() => toggleFilterModal(column.key)"
/>
<div
class="absolute top-full -left-1/2 z-50"
v-if="filterColumn[column.key]"
>
<div class="card min-w-max bg-body shadow-xl px-10 py-5">
<Checkbox
v-for="item in filterItems[column.key]"
:title="item.name"
:value="item.name"
:key="item.name"
:name="column.key"
:checked="
selectedFilterItem[column.key].includes(item.name)
"
className="justify-start"
/>
<div class="card-actions mt-4 justify-end">
<input
type="reset"
class="btn btn-sm text-white btn-error"
:value="t('button.reset')"
@click="() => onFilter(column.key, true)"
/>
<button
class="btn btn-sm btn-success"
@click.stop.prevent="() => onFilter(column.key)"
>
{{ $t("button.submit") }}
</button>
</div>
</div>
</div>
</div>
</span>
</th>
</tr>
</thead>
<tbody v-if="isDraggable">
<tr v-if="loading">
<td :colspan="columns.length">
<Loading />
</td>
</tr>
<tr v-else-if="currentDataSource.length == 0">
<td :colspan="columns.length">{{ $t("table.no_data") }}</td>
</tr>
<template v-else :sort="sortRule">
<tr
v-for="(data, index) in currentDataSource"
:key="data.key || data[rowKey]"
>
<template
v-for="column in columns"
:key="`${data.key || data[rowKey]}_${column.key}`"
>
<td
:class="column.class"
:style="{
width: `${
column.width
? typeof column.width === 'string'
? column.width
: column.width + 'px'
: 'auto'
}`,
}"
>
<slot
name="bodyCell"
v-bind="{ record: data, column, index }"
>
{{ data[column.key] }}</slot
>
</td>
</template>
</tr>
</template>
</tbody>
<draggable
tag="tbody"
:list="dataSourceStorage"
item-key="rowKey"
v-else
>
<template #item="{ element, index }">
<tr :key="element[rowKey] || element.key" >
<template
v-for="column in columns"
:key="`${element[rowKey] || element.key}_${column.key}`"
>
<td
:class="column.class"
:style="{
width: `
${
column.width
? typeof column.width === 'string'
? column.width
: column.width + 'px'
: 'auto'
}`,
}"
>
<slot
name="bodyCell"
v-bind="{ record: element, column, index }"
>
{{ element[column.key] }}</slot
>
</td>
</template>
</tr>
</template>
</draggable>
</table>
</form>
<slot name="afterTable"></slot>
<Pagination
:class="twMerge(!isDraggable ? 'hidden' : 'flex')"
:pagination="pagination"
:dataSource="dataSourceStorage"
:sort="sortRule"
/>
</div>
<div class="content-decoration2"></div>
</div>
</template>
<style lang="css" scoped>
/**資料框**/
.content-box {
@apply border border-info p-1 relative mb-4 bg-transparent;
}
.content-box .table {
@apply rounded-none;
}
.table th {
@apply bg-cyan-600 bg-opacity-30 border-r border-b border-white text-lg font-semibold text-white text-center px-2 py-3;
}
.table td {
@apply border-r border-b border-white text-lg font-semibold text-white text-center px-2 py-3;
}
.table tr td:last-child,
.table tr:first-child th:last-child {
border-right: 0;
}
/* .table tr:last-child td {
border-bottom: v-bind("withStyle ? '0px': '1px'");
} */
/**資料框裝飾**/
.content-box::before {
@apply absolute top-1 left-1 h-5 w-5 bg-no-repeat z-10 bg-[url('../../assets/img/table/content-box-background01.svg')] bg-center;
content: "";
}
.content-box::after {
@apply absolute bottom-1 right-1 h-5 w-5 bg-no-repeat z-10 bg-[url('../../assets/img/table/content-box-background05.svg')] bg-center;
content: "";
}
.content-box .content-decoration {
@apply bg-normal px-8 py-4;
}
/* .content-box .content-decoration::before {
@apply absolute -top-3 -right-[10px] h-8 w-8 bg-no-repeat z-10 bg-[url('../../assets/img/table/content-box-background02.svg')] bg-center;
content: "";
} */
.content-box .content-decoration2::before {
@apply absolute -bottom-1 -left-8 h-14 w-14 bg-no-repeat z-10 bg-[url('../../assets/img/table/content-box-background03.svg')] bg-center;
content: "";
}
/* .content-box .content-decoration2::after {
content: "";
background: url(../../assets/img/table/content-box-background04.svg) center
center;
position: absolute;
right: -27px;
bottom: -7px;
height: 65px;
width: 50px;
background-repeat: no-repeat;
z-index: 2;
} */
</style>

View File

@ -64,10 +64,11 @@ watch(
twMerge(
'flex-col text-xl',
cls,
openChildren.includes(dataParentKey) || open ? 'flex' : 'hidden'
openChildren.includes(d.key) || open ? 'flex' : 'hidden'
)
"
v-for="d in data"
:key="d.key"
:data-parent="d.key"
:open="open"
>

View File

@ -9,7 +9,7 @@ const props = defineProps({
type: String,
default: "",
},
value: String,
value: Object,
isTopLabelExist: {
type: Boolean,
default: true,
@ -73,7 +73,7 @@ const curWidth = computed(() => {
:disabled="disabled"
:readonly="readonly"
:required="required"
class="text-lg text-white bg-transparent w-full input input-bordered rounded-md px-3 border-info focus-within:border-info read-only:bg-base-300 read-only:text-zinc-500 read-only:border-0 read-only:focus-within:outline-0 read-only:focus:outline-0"
class="text-lg text-white bg-transparent w-full input input-bordered rounded-md px-3 border-info focus-within:border-info read-only:bg-base-300 read-only:text-zinc-300 read-only:border-0 read-only:focus-within:outline-0 read-only:focus:outline-0"
/>
<input
v-else
@ -84,7 +84,7 @@ const curWidth = computed(() => {
:disabled="disabled"
:readonly="readonly"
:required="required"
class="text-lg text-white bg-transparent w-full input input-bordered rounded-md px-3 border-info focus-within:border-info read-only:bg-base-300 read-only:text-zinc-500 read-only:border-0 read-only:focus-within:outline-0 read-only:focus:outline-0"
class="text-lg text-white bg-transparent w-full input input-bordered rounded-md px-3 border-info focus-within:border-info read-only:bg-base-300 read-only:text-zinc-300 read-only:border-0 read-only:focus-within:outline-0 read-only:focus:outline-0"
/>
<div :class="twMerge(isBottomLabelExist ? 'label' : '')">
<span class="label-text-alt"><slot name="bottomLeft"></slot></span>

View File

@ -9,7 +9,7 @@ const props = defineProps({
type: String,
default: "",
},
value: String,
value: Object,
isTopLabelExist: {
type: Boolean,
default: true,

View File

@ -48,10 +48,10 @@ onMounted(() => {
'modal',
backdrop
? ''
: 'h-fit w-fit max-h-fit max-w-fit focus-visible:outline-none backdrop:bg-transparent',
: 'focus-visible:outline-none backdrop:bg-transparent',
)" :style="modalStyle" v-draggable="draggable">
<div :class="twMerge(
'modal-box static rounded-md border border-info py-5 px-6 overflow-y-scroll bg-normal',
'modal-box static rounded-md border border-info py-5 px-6 overflow-y-auto bg-normal',
modalClass
)
" :style="{ minWidth: isNaN(width) ? width : `${width}px` }">
@ -60,7 +60,7 @@ onMounted(() => {
{{ title }}
</slot>
</div>
<div class="min-h-[400px]">
<div class="min-h-[200px]">
<slot name="modalContent"></slot>
</div>

View File

@ -4,7 +4,7 @@ import { twMerge } from "tailwind-merge";
const props = defineProps({
name: String,
value: String,
value: Object,
items: Array,
isLabelExist: {
type: Boolean,

View File

@ -0,0 +1,128 @@
<script setup>
import { defineProps, ref, computed, watch, nextTick, watchEffect } from "vue";
import { twMerge } from "tailwind-merge";
/* --------------------------------------------------------------
options: [
{
key: unique,
content: "",
selected: true / false,
disabled: true / false
}
]
---------------------------------------------------------------- */
const props = defineProps({
options: Array,
name: String,
Attribute: String,
onChange: Function,
selectClass: String,
value: Object,
isTopLabelExist: {
type: Boolean,
default: true,
},
isBottomLabelExist: {
type: Boolean,
default: true,
},
required: {
type: Boolean,
default: false,
},
readonly: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
search: {
type: Boolean,
default: false,
},
});
const searchQuery = ref("");
const isOpen = ref(false);
const selectedOption = ref(null);
const filteredOptions = computed(() => {
return props.options.filter((option) => {
const content = option[props.Attribute] || option.key || "";
return content.toLowerCase().includes(searchQuery.value.toLowerCase());
});
});
const selectOption = (value) => {
if (props.onChange) {
props.onChange(value);
} else {
props.value[props.name] = value.key;
selectedOption.value = props.options.find(
(option) => option.key === value.key
);
}
isOpen.value = false;
};
watchEffect(() => {
isOpen.value = false;
selectedOption.value = props.options.find(
(option) => option.key == props.value[props.name]
);
});
</script>
<template>
<label class="form-control w-80 max-w-sm relative">
<div :class="twMerge(isTopLabelExist ? 'label' : '')">
<span class="label-text text-lg"><slot name="topLeft"></slot></span>
<span class="label-text-alt"> <slot name="topRight"></slot></span>
</div>
<div
:class="
twMerge(
'select rounded-md text-lg cursor-pointer pt-2',
disabled ? 'text-gray-300' : 'bg-transparent border-info'
)
"
@click="disabled ? () => {} : (isOpen = !isOpen)"
>
{{ selectedOption ? selectedOption[Attribute] : "" }}
</div>
<div
v-if="isOpen"
class="absolute z-10 bg-gray-800 border border-info rounded-md w-full p-2"
>
<input
v-model="searchQuery"
type="text"
placeholder="Search..."
class="input input-bordered border-info rounded-md w-full mb-2 px-2"
/>
<ul class="max-h-60 overflow-auto">
<li
v-for="option in filteredOptions"
:key="option.key || option"
@click="selectOption(option)"
class="px-4 py-2 hover:bg-gray-600 cursor-pointer"
>
{{ option[Attribute] || option }}
</li>
</ul>
</div>
<div :class="twMerge(isBottomLabelExist ? 'label' : '')">
<span class="label-text-alt"> <slot name="bottomLeft"></slot></span>
<span class="label-text-alt"> <slot name="bottomRight"></slot></span>
</div>
</label>
</template>
<style lang="scss" scoped></style>

View File

@ -18,7 +18,7 @@ const props = defineProps({
Attribute: String,
onChange: Function,
selectClass: String,
value: String || Number,
value: Object,
isTopLabelExist: {
type: Boolean,
default: true,
@ -70,9 +70,7 @@ const props = defineProps({
:class="twMerge(disabled ? `text-white` : 'text-dark')"
:value="option.value || option.key || option"
>
<span>
{{ option[Attribute] || option }}
</span>
</option>
</select>
<div :class="twMerge(isBottomLabelExist ? 'label' : '')">

View File

@ -30,6 +30,10 @@ const props = defineProps({
const currentDataSource = ref([]);
const dataSourceStorage = ref([]);
const tableDataSource = computed(() =>
props.pagination ? currentDataSource.value : dataSourceStorage.value
);
watch(
() => props.dataSource,
(newValue) => {
@ -47,7 +51,7 @@ watch(
);
const updateDataSource = (data) => {
console.log("update", data);
// console.log("update", data);
currentDataSource.value = data;
};
provide("current_table_data", {
@ -58,6 +62,7 @@ const sortRule = ref({});
const filterColumn = ref({});
const filterItems = ref({});
const selectedFilterItem = ref({});
const searchQuery = ref("");
const toggleFilterModal = (key) => {
let newFilter = Object.assign(filterColumn.value);
@ -132,7 +137,10 @@ const form = ref(null);
const onFilter = (key, reset = false) => {
const formData = new FormData(form.value);
reset && formData.delete(key);
if (reset) {
formData.delete(key);
searchQuery.value = "";
}
for (let [name, value] of formData) {
console.log(name, value);
}
@ -228,22 +236,38 @@ watch(
"
@click="() => toggleFilterModal(column.key)"
/>
<div
class="absolute top-full -left-1/2 z-50"
v-if="filterColumn[column.key]"
>
<div class="fixed z-50" v-if="filterColumn[column.key]">
<div class="card min-w-max bg-body shadow-xl px-10 py-5">
<Checkbox
v-for="item in filterItems[column.key]"
:title="item.name"
:value="item.name"
:key="item.name"
:name="column.key"
:checked="
selectedFilterItem[column.key].includes(item.name)
"
className="justify-start"
/>
<label
class="input input-bordered bg-transparent rounded-lg flex items-center px-2 mb-4 border-success focus-within:border-success"
>
<font-awesome-icon
:icon="['fas', 'search']"
class="w-6 h-6 mr-2 text-success"
/>
<input
type="text"
:placeholder="t('operation.enter_text')"
class="text-white bg-transparent w-full"
v-model="searchQuery"
/>
</label>
<div class="max-h-72 overflow-x-auto px-2">
<Checkbox
v-for="item in filterItems[column.key].filter(
(item) =>
item.name && item.name.includes(searchQuery)
)"
:title="item.name"
:value="item.name"
:key="item.name"
:name="column.key"
:checked="
selectedFilterItem[column.key].includes(item.name)
"
className="justify-start"
/>
</div>
<div class="card-actions mt-4 justify-end">
<input
type="reset"
@ -271,12 +295,12 @@ watch(
<Loading />
</td>
</tr>
<tr v-else-if="currentDataSource.length == 0">
<tr v-else-if="tableDataSource.length == 0">
<td :colspan="columns.length">{{ $t("table.no_data") }}</td>
</tr>
<template v-else :sort="sortRule">
<tr
v-for="(data, index) in currentDataSource"
v-for="(data, index) in tableDataSource"
:key="data.key || data[rowKey]"
>
<template
@ -310,6 +334,7 @@ watch(
</form>
<slot name="afterTable"></slot>
<Pagination
v-if="pagination"
:pagination="pagination"
:dataSource="dataSourceStorage"
:sort="sortRule"
@ -322,7 +347,7 @@ watch(
<style lang="css" scoped>
/**資料框**/
.content-box {
@apply border border-info p-1 relative mb-4 bg-transparent ;
@apply border border-info p-1 relative mb-4 bg-transparent;
}
.content-box .table {

View File

@ -3,7 +3,7 @@ import { defineProps } from "vue";
const props = defineProps({
name: String,
value: String,
value: Object,
placeholder: String,
});
</script>
@ -15,7 +15,7 @@ const props = defineProps({
<span class="label-text-alt"> <slot name="topRight"></slot></span>
</div>
<textarea
class="textarea text-lg rounded-md border-info focus-within:border-info h-24"
class="textarea text-lg rounded-md border-info focus-within:border-info h-40"
:placeholder="placeholder"
:name="name"
v-model="value[name]"

View File

@ -1,12 +1,14 @@
<script setup>
import { defineProps, ref, watch } from "vue";
import { twMerge } from "tailwind-merge";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const props = defineProps({
content: String,
status: String,
open: Boolean,
cancel: Function,
confirm: Function,
to: {
default: "body",
type: String,
@ -19,7 +21,7 @@ watch(
() => props.open,
(newVal) => {
isOpen.value = newVal;
if (newVal) {
if (newVal && props.status !== "warning") {
setTimeout(() => {
props.cancel && props.cancel();
}, 3000);
@ -35,11 +37,13 @@ watch(
role="alert"
:class="
twMerge(
`alert text-xl rounded-md absolute left-1/2 -translate-x-1/2 top-5 z-[1000] max-w-fit`,
`alert text-xl rounded-md fixed left-1/2 -translate-x-1/2 top-24 z-[1000] max-w-fit`,
status === 'info'
? 'alert-info'
: status === 'error'
? 'alert-error text-white'
: status === 'warning'
? 'alert-warning bg-yellow-400'
: 'alert-success'
)
"
@ -58,6 +62,10 @@ watch(
></path>
</svg>
<span>{{ content }}</span>
<div v-if="status === 'warning'">
<button @click="props.cancel && props.cancel();" className="btn btn-sm btn-outline me-2">{{$t("button.cancel")}}</button>
<button @click="props.confirm && props.confirm()" className="btn btn-sm">{{$t("button.confirm")}}</button>
</div>
</div>
</template>
</Teleport>

View File

@ -9,6 +9,7 @@ const props = defineProps({
getFileList: Function,
multiple: Boolean,
baseUrl: String,
formats: { type: String, default: "txt、doc、xls、pdf、png、jpg、ppt、zip、rar" },
});
const acceptFileType = [
@ -25,6 +26,13 @@ const acceptFileType = [
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-powerpoint",
".ppt",
".pptx",
".zip",
".rar",
"application/vnd.openxmlformats-officedocument.presentationml.presentation", // PowerPoint Files 2007+
"application/zip", // ZIP Files
"application/x-rar-compressed", // RAR Files
];
const loading = ref(false);
@ -134,7 +142,7 @@ const revokeURL = (src) => {
<template>
<label class="form-control w-full">
<div class="label">
<div class="label" @click.stop.prevent="() => {}">
<span class="label-text text-lg"><slot name="topLeft"></slot></span>
<span class="label-text-alt"> <slot name="topRight"></slot></span>
</div>
@ -236,6 +244,14 @@ const revokeURL = (src) => {
:icon="['fas', 'file-powerpoint']"
/>
</template>
<template
v-else-if="file.type?.match(/zip|rar/g) || file.ext?.match(/zip|rar/g)"
>
<font-awesome-icon
class="mx-auto mb-2 text-4xl text-white"
:icon="['fas', 'file-archive']"
/>
</template>
<template
v-else-if="
!file.type?.match('image/*') &&
@ -265,6 +281,9 @@ const revokeURL = (src) => {
</div>
<p class="text-2xl my-2">{{ $t("upload.title") }}</p>
<p class="mb-0 col-grey">{{ $t("upload.description") }}</p>
<p class="mb-0 col-grey">
{{ $t("upload.formats") }} : {{ props.formats }}
</p>
</div>
</div>

View File

@ -1,7 +1,7 @@
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const forgeDom = ref(null);
let viewer = null;
@ -34,6 +34,20 @@ const initViewer = (container) => {
});
};
//
const setCameraPosition = (position, target) => {
const nav = viewer.navigation;
// 使 THREE.Vector3
const newPosition = new THREE.Vector3(position.x, position.y, position.z);
const newTarget = new THREE.Vector3(target.x, target.y, target.z);
//
nav.setView(newPosition, newTarget);
console.log("攝影機定位完成:");
console.log("新位置:", newPosition);
console.log("新焦點:", newTarget);
};
// 使 .svf
const loadModel = (filePath) => {
return new Promise((resolve, reject) => {
@ -43,6 +57,21 @@ const loadModel = (filePath) => {
(model) => {
viewer.impl.invalidate(true);
viewer.fitToView();
//
setTimeout(() => {
setCameraPosition(
{
x: -212.33421531428007,
y: 299.3895400707608,
z: 157.48711907404862,
}, //
{
x: 151.61159898775077,
y: -196.55136189609067,
z: -136.47282256290345,
} //
);
}, 500);
resolve(model);
console.log("模型加載完成");
},
@ -54,7 +83,7 @@ const loadModel = (filePath) => {
onMounted(async () => {
console.log("Forge 加載");
await initViewer(forgeDom.value);
const filePath = `${FILE_BASEURL}/upload/forge/0.svf`;
const filePath = `${FILE_BASEURL}/upload/forge/0.svf`;
loadModel(filePath);
});
@ -85,5 +114,4 @@ onUnmounted(() => {
display: none;
bottom: 200px;
}
</style>

View File

@ -30,7 +30,7 @@ const props = defineProps({
});
const store = useHeatmapBarStore();
const { updateDataVisualization, createSprites, showSubSystemObjects, forgeClickListener, clear } = useForgeSprite()
const { updateDataVisualization, createSprites, showSubSystemObjects, forgeClickListener, clear, setCameraPosition } = useForgeSprite()
const forgeDom = ref(null);
@ -80,6 +80,20 @@ const loadModel = (viewer, filePath) => {
(model) => {
viewer.impl.invalidate(true);
viewer.fitToView();
setTimeout(() => {
setCameraPosition(
{
x: 241.40975707867645,
y: -260.4481491801548,
z: 129.5719879121458,
}, //
{
x: -183.36302786348594,
y: 194.05657710941966,
z: -149.3902249981004,
} //
);
}, 500);
updateDataVisualization(viewer)
resolve(model);
console.log("模型加載完成");

View File

@ -93,8 +93,8 @@ const createSprites = async (dataVizExtn) => {
const DataVizCore = Autodesk.DataVisualization.Core;
const viewableType = DataVizCore.ViewableType.SPRITE;
let spriteColor = new THREE.Color(0xffffff);
const BASEURL = import.meta.env.VITE_FORGE_BASEURL;
const spriteIconUrl = `${BASEURL}/hotspot.svg`;
const BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const spriteIconUrl = `${BASEURL}/dist/hotspot.svg`;
const style = new DataVizCore.ViewableStyle(
viewableType,
spriteColor,

View File

@ -1,17 +1,22 @@
<script setup>
import { onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import NavbarItem from "./NavbarItem.vue";
import NavbarBuilding from "./NavbarBuilding.vue";
import Logo from "@/assets/img/logo.svg";
import useUserInfoStore from "@/stores/useUserInfoStore";
import useBuildingStore from "@/stores/useBuildingStore";
import AlarmDrawer from "@/components/alarm/AlarmDrawer.vue";
import NavbarLang from "./NavbarLang.vue";
import { twMerge } from "tailwind-merge";
const user = ref("");
const menuShow = ref(true);
const router = useRouter();
const store = useUserInfoStore();
const storeBuilding = useBuildingStore();
onMounted(() => {
const name = store.user.user_name;
if (name) {
@ -24,12 +29,26 @@ const toggleMenu = () => {
};
const src = import.meta.env.MODE === "production" ? "./logo.svg" : Logo;
const logout = () => {
document.cookie = "JWT-Authorization=; Max-Age=0";
document.cookie = "user_name=; Max-Age=0";
store.user.token = "";
store.user.user_name = "";
storeBuilding.deleteBuilding();
router.push({ path: "/login" });
};
</script>
<template>
<header class="navbar bg-dark text-light-info w-full relative z-50">
<div class="navbar-start">
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden" @click="toggleMenu">
<div class="navbar-start min-w-[480px] lg:min-w-[440px]">
<div
tabindex="0"
role="button"
class="btn btn-ghost lg:hidden"
@click="toggleMenu"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
@ -67,7 +86,7 @@ const src = import.meta.env.MODE === "production" ? "./logo.svg" : Logo;
<NavbarItem />
</div>
<div class="navbar-end mr-4">
<ul class="menu-box">
<ul class="left-menu">
<li>
<NavbarLang />
</li>
@ -93,12 +112,12 @@ const src = import.meta.env.MODE === "production" ? "./logo.svg" : Logo;
class="dropdown-content translate-y-2 z-[100] menu py-3 shadow rounded w-32 bg-[#4c625e] border text-center"
>
<li class="text-white">
<router-link
to="logout"
type="link"
<a
href="#"
@click.prevent="logout"
class="flex flex-col justify-center items-center"
>{{ $t("sign_out") }}
</router-link>
</a>
</li>
</ul>
</div>
@ -112,39 +131,17 @@ const src = import.meta.env.MODE === "production" ? "./logo.svg" : Logo;
.sub-drawer {
@apply bg-dark bg-opacity-90 shadow-xl !important;
}
/**menu**/
.menu-box {
@apply flex flex-wrap justify-center ;
position: relative;
z-index: 0;
.left-menu {
@apply flex flex-wrap justify-center relative z-0;
}
.menu-box .btn-group {
background: transparent;
width: 95px;
.left-menu > li {
@apply relative;
}
.menu-box > li {
position: relative;
}
.menu-box > li:not(:last-child)::after {
.left-menu > li:not(:last-child)::after {
@apply absolute top-5 bottom-0 left-20 right-0 block w-6 h-0.5 bg-[#93c0dc] z-0;
content: "";
position: absolute;
top: 20px;
bottom: 0;
left: 84px;
right: 0px;
display: block;
width: 25px;
height: 2px;
background-color: #93c0dc;
z-index: 0;
}
.menu-box .btn-group span {
color: #fff;
display: block;
margin-top: 0px;
}
</style>

View File

@ -1,50 +1,48 @@
<script setup>
import { getBuildings } from "@/apis/building";
import { onMounted, ref } from "vue";
import { onMounted } from "vue";
import useBuildingStore from "@/stores/useBuildingStore";
const store = useBuildingStore();
const getBui = async () => {
console.log(store.buildings);
const res = await getBuildings();
store.buildings = res.data;
store.selectedBuilding = res?.data[0];
};
const selectBuilding = (bui) => {
store.selectedBuilding = bui;
store.selectedBuilding = bui; // selectedBuildingwatch
};
onMounted(() => {
getBui();
store.initialize(); //
});
</script>
<template>
<div class="dropdown dropdown-bottom">
<div
tabindex="0"
role="button"
class="text-white ml-8 text-lg font-semiLight"
>
{{ store.selectedBuilding?.full_name }}
<font-awesome-icon :icon="['fas', 'angle-down']" class="ml-1" />
</div>
<ul
tabindex="0"
class="dropdown-content left-8 translate-y-2 z-[1] menu py-3 shadow rounded bg-[#4c625e] border text-center"
>
<li
class="text-white my-1 text-base"
v-for="bui in store.buildings"
:key="bui.building_tag"
@click="selectBuilding(bui)"
<template v-if="store.buildings.length > 1">
<div class="dropdown dropdown-bottom">
<div
tabindex="0"
role="button"
class="text-white ml-8 text-lg font-semiLight"
>
{{ bui.full_name }}
</li>
</ul>
</div>
{{ store.selectedBuilding?.full_name }}
<font-awesome-icon :icon="['fas', 'angle-down']" class="ml-1" />
</div>
<ul
tabindex="0"
class="dropdown-content w-48 left-8 translate-y-2 z-[1] menu py-3 shadow rounded bg-[#4c625e] border text-center"
>
<li
class="text-white my-1 text-base cursor-pointer"
v-for="bui in store.buildings"
:key="bui.building_tag"
@click="selectBuilding(bui)"
>
{{ bui.full_name }}
</li>
</ul>
</div>
</template>
<template v-else>
<div class="text-white ml-8 text-lg font-semiLight">
{{ store.selectedBuilding?.full_name }}
</div>
</template>
</template>
<style lang="scss" scoped></style>

View File

@ -2,6 +2,7 @@
import { onMounted, ref, watch, computed } from "vue";
import { AUTHPAGES } from "@/constant";
import { getAuth, getAllSysSidebar } from "@/apis/building";
import { getSideBar } from "@/apis/energy";
import useBuildingStore from "@/stores/useBuildingStore";
import useUserInfoStore from "@/stores/useUserInfoStore";
import { useI18n } from "vue-i18n";
@ -13,6 +14,8 @@ const store = useUserInfoStore();
const buildingStore = useBuildingStore();
const route = useRoute();
const openKeys = ref([]); //
const menu_array = ref([]);
const currentAuthCode = ref("");
const iniFroList = async () => {
const res = await getAuth(locale.value);
@ -34,12 +37,22 @@ const authPages = computed(() =>
);
const open = ref(false);
const getSubMonitorPage = async (building) => {
const res = await getAllSysSidebar();
const getSubMonitorPage = async (building_guid) => {
const res = await getAllSysSidebar(building_guid);
buildingStore.mainSubSys = res.data.history_Main_Systems;
menu_array.value = res.data.history_Main_Systems;
};
const showDrawer = () => {
getSubMonitorPage();
const getSubPage = async (system_type) => {
const res = await getSideBar(system_type);
menu_array.value = res.data;
};
const showDrawer = async (authCode) => {
if (authCode === "PF1") {
await getSubMonitorPage(buildingStore.selectedBuilding.building_guid);
} else if (authCode === "PF2" || authCode === "PF11") {
await getSubPage(authCode === "PF2" ? "Energy" : "Setting");
}
currentAuthCode.value = authCode;
open.value = true;
};
const onClose = () => {
@ -58,7 +71,7 @@ watch(
() => buildingStore.selectedBuilding,
(newVal) => {
if (newVal !== null) {
getSubMonitorPage(newVal.building_tag);
getSubMonitorPage(newVal.building_guid);
}
}
);
@ -76,27 +89,39 @@ onMounted(() => {
<li class="flex flex-col items-center justify-center">
<router-link
:to="{ name: 'dashboard' }"
class="flex flex-col justify-center items-center btn-group text-white"
class="flex lg:flex-col justify-center items-center btn-group text-white"
>
<font-awesome-icon
:icon="['fas', 'home']"
size="2x"
class="w-10 m-auto"
/>
{{ $t("home") }}
<span>{{ $t("home") }}</span>
</router-link>
</li>
<li
v-for="page in authPages"
class="flex flex-col items-center justify-center"
:key="page.authCode"
>
<a
v-if="page.authCode === 'PF1'"
@click="showDrawer"
v-if="
page.authCode === 'PF1' ||
page.authCode === 'PF2' ||
page.authCode === 'PF11'
"
@click="showDrawer(page.authCode)"
:class="
twMerge(
'flex flex-col justify-center items-center btn-group text-white cursor-pointer',
route.fullPath.includes('/system')
'flex lg:flex-col justify-center items-center btn-group text-white cursor-pointer',
page.authCode === 'PF1' && route.fullPath.includes('/system')
? 'router-link-active router-link-exact-active'
: '',
page.authCode === 'PF2' &&
route.fullPath.includes('/energyManagement')
? 'router-link-active router-link-exact-active'
: '',
page.authCode === 'PF11' && route.fullPath.includes('/setting')
? 'router-link-active router-link-exact-active'
: ''
)
@ -107,20 +132,20 @@ onMounted(() => {
size="2x"
class="w-10 m-auto"
/>
{{ page.subName }}
<span>{{ page.subName }}</span>
</a>
<router-link
v-else
:to="page.navigate"
type="link"
class="flex flex-col justify-center items-center btn-group text-white"
class="flex lg:flex-col justify-center items-center btn-group text-white"
>
<font-awesome-icon
:icon="['fas', page.icon]"
size="2x"
class="w-10 m-auto"
/>
{{ page.subName }}
<span>{{ page.subName }}</span>
</router-link>
</li>
</ul>
@ -142,22 +167,34 @@ onMounted(() => {
@openChange="handleOpenChange"
>
<a-sub-menu
v-for="main in buildingStore.mainSubSys"
v-for="main in menu_array"
:key="main.main_system_tag"
:title="main.full_name"
v-if="menu_array.length > 0 && open"
>
<a-menu-item
v-for="sub in main.history_Sub_systems"
:key="sub.sub_system_tag"
v-for="sub in currentAuthCode === 'PF1'
? main.history_Sub_systems
: currentAuthCode === 'PF2'
? main.sub
: main.sub"
:key="sub.sub_system_tag + `_` + sub.type"
@click="() => onClose()"
>
<router-link
:to="{
name: 'sub_system',
name:
currentAuthCode === 'PF2'
? 'energyManagement'
: currentAuthCode === 'PF11'
? 'setting'
: 'sub_system',
params: {
main_system_id: main.main_system_tag,
sub_system_id: sub.sub_system_tag,
floor_id: 'main',
...(currentAuthCode === 'PF2' || currentAuthCode === 'PF11'
? { type: sub.type }
: { floor_id: 'main' }),
},
}"
>
@ -169,8 +206,30 @@ onMounted(() => {
</a-drawer>
</template>
<style lang="scss" scoped>
/**menu**/
.menu-box {
@apply flex flex-wrap lg:flex-row flex-col items-start lg:justify-center relative z-0 h-full w-60 lg:w-auto;
}
.menu-box .btn-group {
@apply bg-transparent lg:w-[95px] w-full mb-5 lg:mb-0;
}
.menu-box .btn-group span {
@apply text-2xl ms-2 lg:ms-0 lg:text-sm;
}
.menu-box > li {
@apply relative;
}
.menu-box > li:not(:last-child)::after {
@apply absolute top-5 bottom-0 left-20 right-0 lg:block hidden w-6 h-0.5 bg-[#93c0dc] z-0;
content: "";
}
.router-link-active.router-link-exact-active {
color: #93c0dc;
@apply text-[#93c0dc];
}
:deep(.ant-menu-submenu-selected) {

View File

@ -15,14 +15,14 @@ const toggleLanguage = (lang) => {
<button
tabindex="0"
type="button"
class="flex flex-col justify-center items-center btn-group"
class="flex flex-col justify-center items-center bg-transparent w-[95px]"
>
<font-awesome-icon
:icon="['fas', 'globe']"
size="2x"
class="text-white w-10 m-auto"
/>
<span class="text-white">{{ $t("language") }}</span>
<span class="text-white block mt-0">{{ $t("language") }}</span>
</button>
<ul
tabindex="0"

View File

@ -8,25 +8,56 @@
"table": {
"no_data": "表中数据为空",
"in_otal": "笔资料",
"skip_to": "跳至"
"skip_to": "跳至",
"serial_number": "序列号",
"name": "名称",
"time": "时间"
},
"upload": {
"title": "选择一个文件或拖放到这里",
"description": "档案不超过 10MB"
"description": "档案不超过 10MB",
"formats": "档案格式"
},
"dashboard": {
"yesterday_today": "昨天/今天",
"elec_consumption_comparison": "用电量比较",
"today_electricity_consumption": "今日用电量",
"yesterday_electricity_consumption": "昨天用电量",
"elec_consumption_comparison_trend": "用电量比较趋势",
"electricity_consumption": "用电量",
"today_electricity_consumption": "今日用电量 ( kWH )",
"yesterday_electricity_consumption": "昨天用电量 ( kWH )",
"instant_power": "即时功率 ( kW )",
"instant_contract_capacity_ratio": "契約容量佔比 ( % )",
"this_last_week": "本周/上周",
"thisweek_electricity_consumption": "本周用电量",
"lastweek_electricity_consumption": "上周用电量"
"lastweek_electricity_consumption": "上周用电量",
"one_hour": "1小时",
"four_hour": "4小时",
"eight_hour": "8小时",
"energy_ranking": "能耗排行",
"last_30_days_energy_trend": "近30天能耗趋势",
"today_energy_consumption": "本日能耗",
"this_month_energy_consumption": "本月能耗",
"relative_energy_consumption": "环比能耗",
"daily_relative_change": "日环比",
"weekly_relative_change": "周环比",
"monthly_relative_change": "月环比",
"yearly_relative_change": "年环比",
"today": "今日",
"yesterday": "昨日",
"this_week": "本周",
"last_week": "上周",
"this_month": "本月",
"last_month": "上月",
"this_year": "今年",
"last_year": "去年",
"work_order": "工单",
"system_status": "系统状态"
},
"history": {
"title": "历史资料",
"building_name": "厂区",
"device_name": "设备名称",
"system_category": "系统类别",
"device_category": "设备类别",
"category": "类别",
"value": "数值",
@ -52,6 +83,11 @@
"green_elec": "绿电",
"immediate_demand": "即时需量",
"average_demand": "平均需量",
"real_time_Trend": "即时趋势",
"contract_capacity": "契约容量",
"alert_capacity": "警戒容量",
"reset_value": "复归值",
"edit_automatic_demand": "编辑自动需量",
"elec_bills": "今年电费累计(元)",
"interval_elec_charges": "区间电费(元)",
"year_carbon_emission": "今年碳排当量累计(公斤)",
@ -61,12 +97,70 @@
"monthly_elec_consumption": "每月用电分析",
"monthly_carbon_emission_and_reduction": "每月碳排当量 (kgCO2e)",
"monthly_bill_power": "每月计费度数 (kWh)",
"interval_bill_degree": "区间计费度数"
"interval_bill_degree": "区间计费度数",
"peak": "尖峰",
"semi_peak": "半尖峰",
"off_peak": "离峰",
"var_elec_cost": "流动电费",
"fixed_elec_cost": "基本电费",
"total_elec_cost": "总电费",
"carbon_equivalent": "碳排当量",
"edit_carbon_emission": "编辑碳排放系数",
"carbon_emission_coefficient": "碳排放系数",
"electricity_classification": "用电分类",
"electricity_price": "电费每度单价",
"floor": "楼层",
"maximum": "最大值",
"maximum_time": "最大值时间",
"minimum": "最小值",
"minimum_time": "最小值时间",
"average_value": "平均值",
"start_value": "起始值(kWh)",
"end_value": "截止值(kWh)",
"difference": "差值(kWh)",
"power_consumption": "用电量(kWh)",
"ranking": "排名",
"subtotal": "小计",
"unit_price": "单价",
"total_amount": "金额总计",
"elec_price_list": "电价表",
"residential": "住宅型",
"standard": "标准型",
"simple_elec_price_two_stage": "简易型时间电价二段式",
"simple_elec_price_three_stage": "简易型时间电价三段式",
"classification": "分类",
"summer_months": "夏月",
"non_summer_months": "非夏月",
"time_outside_summer_months": "夏月以外的时间",
"basic_elec_charge": "基本电费",
"charged_per_household": "按户计收",
"per_household_month": "每户每月",
"mon_to_friday": "周一~周五",
"peak_hours": "尖峰时间",
"semi_peak_hours": "半尖峰时间",
"off_peak_hours": "离峰时间",
"price_per_kwh": "每度",
"all_day": "全日",
"sat_sun_off_peak_days": "周六、周日及离峰日",
"usage_over_2000kwh": "每月总度数超过2000度之部分",
"add": "加",
"standard_time_of_use_tariff_2_stage": "标准型时间电价二段式",
"standard_time_of_use_tariff_3_stage": "标准型时间电价三段式",
"single_phase": "单相",
"three_phase": "三相",
"frequent_contract": "经常契约",
"per_kw_per_month": "每瓩每月",
"non_summer_contract": "非夏日契约",
"saturday_semi_peak_contract": "周六半尖峰契约",
"off_peak_contract": "离峰契约",
"variable_electricity_charge": "流动电费",
"saturday": "周六",
"sunday_and_off_peak_days": "周日及离峰日"
},
"alarm": {
"title": "显示警告",
"notify": "异常通知",
"number": "异常编号",
"number": "异常ID",
"category": "异常类别",
"device_name": "设备名称",
"message": "异常讯息",
@ -84,9 +178,10 @@
"end_date": "结束日期",
"building_and_floor": "栋别-楼层",
"uuid": "异常ID",
"alarmClass": "异常类别",
"alarmClass": "告警条件",
"device_name": "设备名称",
"device_number": "设备编号",
"device_point_name": "点位名称",
"date": "发生日期",
"time": "发生时间",
"error_msg": "异常原因",
@ -113,10 +208,12 @@
"qualifications": "限定条件",
"upper_limit": "上限",
"lower_limit": "下限",
"delay": "持续秒数",
"highDelay": "上限持续秒数",
"lowDelay": "下限持续秒数",
"warning_method": "警示方式",
"warning_time": "警示时间",
"warning_value": "警示值",
"operation": "功能",
"alarm_settings": "异常设定",
"time_setting": "时间设定",
@ -128,7 +225,23 @@
"notify_email": "email",
"notify_items": "通知项目",
"notify_list": "通知名单",
"choose": "选择"
"choose": "选择",
"day_time": "星期/时间",
"click_time_period": "请用滑鼠点击时间段",
"clear": "清空",
"sunday": "星期日",
"monday": "星期一",
"tuesday": "星期二",
"wednesday": "星期三",
"thursday": "星期四",
"friday": "星期五",
"saturday": "星期六",
"schedule_name": "时段名称",
"schedule_content": "时段内容",
"reorganization": "MQTT 告警重整",
"online": "在线",
"offline": "离线",
"alarm": "告警"
},
"operation": {
"title": "运维管理",
@ -142,13 +255,14 @@
"start_time": "预计开始时间",
"upload": "档案上传",
"finish_time": "完成时间",
"updated_time": "更新时间",
"operation": "功能",
"vendor": "厂商",
"contact_person": "联络人",
"phone": "电话",
"email": "email",
"created_at": "建立日期",
"maintainance": "保养",
"maintenance": "保养",
"repair": "维修",
"company_info": "厂商资料",
"repair_item": "维修项目",
@ -156,6 +270,8 @@
"responsible_vendor": "负责厂商",
"not_completed": "未完成",
"completed": "已完成",
"complete": "已完成",
"incomplete": "未完成",
"worker_id": "工作人员编号",
"notice": "注意事项",
"result_description": "结果描述",
@ -181,14 +297,20 @@
"index": "编号",
"oriOrgName": "档案",
"operation": "功能",
"upload": "图资上传"
"folder_path": "资料夹路径",
"upload": "图资上传",
"staging_area": "删除暂存区",
"no_path": "无资料夹路径"
},
"assetManagement": {
"title": "资产管理",
"add_category": "新增类别",
"add_system_category": "新增系统类别",
"edit_system_category": "修改系统类别",
"add_device_category": "新增设备类别",
"edit_device_category": "修改设备类别",
"system_name": "名称",
"system_value": "代号",
"system_parent": "所属大类",
"system_parent": "所属系統",
"device_number": "设备编号",
"device_name": "设备名称",
"asset_number": "资产编号",
@ -216,7 +338,10 @@
"associated_device": "关联设备",
"choose": "选择",
"index": "编号",
"floor_plan": "平面图"
"floor_plan": "平面图",
"department": "部门",
"department_name": "部门名称",
"building": "栋别"
},
"accountManagement": {
"account_title": "帐号管理",
@ -236,7 +361,7 @@
"phone": "手机",
"created_at": "建立时间",
"operation": "功能",
"name_placeholder": "请输入使用者名称",
"name_placeholder": "请输入使用者、帐号名称",
"role_placeholder": "请输入角色名称",
"change_password": "变更密码",
"choose": "选择"
@ -260,7 +385,44 @@
"email_format": "请输入正确 Email 地址",
"password_format": "密码长度至少8码必须包含英文及数字",
"start_time_placeholder": "请输入预计开始日期",
"finish_time_placeholder": "请输入完成日期",
"rename": "重新命名",
"download": "下载"
"download": "下载",
"confirm": "确认",
"restore": "复原",
"stop_edit": "停止修改",
"start_edit": "开始修改",
"convert": "轉換"
},
"msg": {
"sure_to_delete": "是否确认删除该项目?",
"sure_to_delete_permanent": "是否确认永久删除该项目?",
"delete_success": "删除成功",
"delete_failed": "删除失败",
"mqtt_refresh": "重新设定成功",
"schema_name_required": "架构名称栏位必填",
"incorrect_format":"格式不正确",
"send_successfully":"送出成功",
"edit_successfully":"修改成功"
},
"setting": {
"electricity_meter": "电表",
"MQTT_parse": "MQTT 解析",
"schema": "架构",
"point": "点位",
"description": "描述",
"IoT_point_name": "IoT 点位名称",
"IoT_point_code": "IoT 点位代号",
"number_of_decimal_places": "小数位数",
"boolean_value": "布林值",
"hide_point": "点位显示",
"hide_switch": "switch 功能",
"switch_on_message": "switch 开启时传送的讯息",
"switch_off_message": "switch 关闭时传送的讯息",
"schema_name": "架构名称",
"IoT_point_structure": "IoT点位结构",
"system_point_name": "系统点位名称",
"json_format_text": "请贴上 JSON 格式数据",
"json_click_text": "请在左侧输入JSON并点选转换按钮"
}
}

View File

@ -8,25 +8,56 @@
"table": {
"no_data": "表中數據為空",
"in_otal": "筆資料",
"skip_to": "跳至"
"skip_to": "跳至",
"serial_number": "序號",
"name": "姓名",
"time": "時間"
},
"upload": {
"title": "選擇一個文件或拖放到這裡",
"description": "檔案不超過 10MB"
"description": "檔案不超過 10MB",
"formats": "檔案格式"
},
"dashboard": {
"yesterday_today": "昨天/今天",
"elec_consumption_comparison": "用電量比較",
"today_electricity_consumption": "今日用電量",
"yesterday_electricity_consumption": "昨天用電量",
"elec_consumption_comparison_trend": "用電量比較趨勢",
"electricity_consumption": "用電量",
"today_electricity_consumption": "今日用電量 ( kWH )",
"yesterday_electricity_consumption": "昨天用電量 ( kWH )",
"instant_power": "即時功率 ( kW )",
"instant_contract_capacity_ratio": "契約容量佔比 ( % )",
"this_last_week": "本週/上週",
"thisweek_electricity_consumption": "本周用電量",
"lastweek_electricity_consumption": "上週用電量"
"lastweek_electricity_consumption": "上週用電量",
"one_hour": "1小時",
"four_hour": "4小時",
"eight_hour": "8小時",
"energy_ranking": "能耗排行",
"last_30_days_energy_trend": "近30天能耗趨勢",
"today_energy_consumption": "本日能耗",
"this_month_energy_consumption": "本月能耗",
"relative_energy_consumption": "環比能耗",
"daily_relative_change": "日環比",
"weekly_relative_change": "周環比",
"monthly_relative_change": "月環比",
"yearly_relative_change": "年環比",
"today": "今日",
"yesterday": "昨日",
"this_week": "本周",
"last_week": "上周",
"this_month": "本月",
"last_month": "上月",
"this_year": "今年",
"last_year": "去年",
"work_order": "工單",
"system_status": "系統狀態"
},
"history": {
"title": "歷史資料",
"building_name": "廠區",
"device_name": "設備名稱",
"system_category": "系統類別",
"device_category": "設備類別",
"category": "類別",
"value": "數值",
@ -52,6 +83,11 @@
"green_elec": "綠電",
"immediate_demand": "即時需量",
"average_demand": "平均需量",
"real_time_Trend": "即時趨勢",
"contract_capacity": "契約容量",
"alert_capacity": "警戒容量",
"reset_value": "復歸值",
"edit_automatic_demand": "編輯自動需量",
"elec_bills": "今年電費累計(元)",
"interval_elec_charges": "區間電費(元)",
"year_carbon_emission": "今年碳排當量累計(公斤)",
@ -61,12 +97,70 @@
"monthly_elec_consumption": "每月用電分析",
"monthly_carbon_emission_and_reduction": "每月碳排當量 (kgCO2e)",
"monthly_bill_power": "每月計費度數 (kWh)",
"interval_bill_degree": "區間計費度數"
"interval_bill_degree": "區間計費度數",
"peak": "尖峰",
"semi_peak": "半尖峰",
"off_peak": "離峰",
"var_elec_cost": "流動電費",
"fixed_elec_cost": "基本電費",
"total_elec_cost": "總電費",
"carbon_equivalent": "碳排當量",
"edit_carbon_emission": "編輯碳排放係數",
"carbon_emission_coefficient": "碳排放係數",
"electricity_classification": "用電分類",
"electricity_price": "電費每度單價",
"floor": "樓層",
"maximum": "最大值",
"maximum_time": "最大值時間",
"minimum": "最小值",
"minimum_time": "最小值時間",
"average_value": "平均值",
"start_value": "起始值(kWh)",
"end_value": "截止值(kWh)",
"difference": "差值(kWh)",
"power_consumption": "用電量(kWh)",
"ranking": "排名",
"subtotal": "小計",
"unit_price": "單價",
"total_amount": "金額總計",
"elec_price_list": "電價表",
"residential": "住宅型",
"standard": "標準型",
"simple_elec_price_two_stage": "簡易型時間電價二段式",
"simple_elec_price_three_stage": "簡易型時間電價三段式",
"classification": "分類",
"summer_months": "夏月",
"non_summer_months": "非夏月",
"time_outside_summer_months": "夏月以外的時間",
"basic_elec_charge": "基本電費",
"charged_per_household": "按戶計收",
"per_household_month": "每戶每月",
"mon_to_friday": "週一~週五",
"peak_hours": "尖峰時間",
"semi_peak_hours": "半尖峰時間",
"off_peak_hours": "離峰時間",
"price_per_kwh": "每度",
"all_day": "全日",
"sat_sun_off_peak_days": "週六、週日及離峰日",
"usage_over_2000kwh": "每月總度數超過2000度之部分",
"add": "加",
"standard_time_of_use_tariff_2_stage": "標準型時間電價二段式",
"standard_time_of_use_tariff_3_stage": "標準型時間電價三段式",
"single_phase": "單相",
"three_phase": "三相",
"frequent_contract": "經常契約",
"per_kw_per_month": "每瓩每月",
"non_summer_contract": "非夏日契約",
"saturday_semi_peak_contract": "週六半尖峰契約",
"off_peak_contract": "離峰契約",
"variable_electricity_charge": "流動電費",
"saturday": "週六",
"sunday_and_off_peak_days": "週日及離峰日"
},
"alarm": {
"title": "顯示警告",
"notify": "異常通知",
"number": "異常編號",
"number": "異常ID",
"category": "異常類別",
"device_name": "設備名稱",
"message": "異常訊息",
@ -84,9 +178,10 @@
"end_date": "結束日期",
"building_and_floor": "棟別-樓層",
"uuid": "異常ID",
"alarmClass": "異常類別",
"alarmClass": "告警條件",
"device_name": "設備名稱",
"device_number": "設備編號",
"device_point_name": "點位名稱",
"date": "發生日期",
"time": "發生時間",
"error_msg": "異常原因",
@ -113,10 +208,12 @@
"qualifications": "限定條件",
"upper_limit": "上限",
"lower_limit": "下限",
"delay": "持續秒數",
"highDelay": "上限持續秒數",
"lowDelay": "下限持續秒數",
"warning_method": "警示方式",
"warning_time": "警示時間",
"warning_value": "警示值",
"operation": "功能",
"alarm_settings": "異常設定",
"time_setting": "時間設定",
@ -128,7 +225,23 @@
"notify_email": "email",
"notify_items": "通知項目",
"notify_list": "通知名單",
"choose": "選擇"
"choose": "選擇",
"day_time": "星期/時間",
"click_time_period": "請用滑鼠點擊時間段",
"clear": "清空",
"sunday": "星期日",
"monday": "星期一",
"tuesday": "星期二",
"wednesday": "星期三",
"thursday": "星期四",
"friday": "星期五",
"saturday": "星期六",
"schedule_name": "時段名稱",
"schedule_content": "時段內容",
"reorganization": "MQTT 告警重整",
"online": "在線",
"offline": "離線",
"alarm": "告警"
},
"operation": {
"title": "運維管理",
@ -142,13 +255,14 @@
"start_time": "預計開始時間",
"upload": "檔案上傳",
"finish_time": "完成時間",
"updated_time": "更新時間",
"operation": "功能",
"vendor": "廠商",
"contact_person": "聯絡人",
"phone": "電話",
"email": "email",
"created_at": "建立日期",
"maintainance": "保養",
"maintenance": "保養",
"repair": "維修",
"company_info": "廠商資料",
"repair_item": "維修項目",
@ -156,6 +270,8 @@
"responsible_vendor": "負責廠商",
"not_completed": "未完成",
"completed": "已完成",
"complete": "已完成",
"incomplete": "未完成",
"worker_id": "工作人員編號",
"notice": "注意事項",
"result_description": "結果描述",
@ -181,14 +297,20 @@
"index": "編號",
"oriOrgName": "檔案",
"operation": "功能",
"upload": "圖資上傳"
"folder_path": "資料夾路徑",
"upload": "圖資上傳",
"staging_area": "刪除暫存區",
"no_path": "無資料夾路徑"
},
"assetManagement": {
"title": "資產管理",
"add_category": "新增類別",
"add_system_category": "新增系統類別",
"edit_system_category": "修改系統類別",
"add_device_category": "新增設備類別",
"edit_device_category": "修改設備類別",
"system_name": "名稱",
"system_value": "代號",
"system_parent": "所屬大類",
"system_parent": "所屬系統",
"device_number": "設備編號",
"device_name": "設備名稱",
"asset_number": "資產編號",
@ -216,7 +338,10 @@
"associated_device": "關聯設備",
"choose": "選擇",
"index": "編號",
"floor_plan": "平面圖"
"floor_plan": "平面圖",
"department": "部門",
"department_name": "部門名稱",
"building": "棟別"
},
"accountManagement": {
"account_title": "帳號管理",
@ -236,7 +361,7 @@
"phone": "手機",
"created_at": "建立時間",
"operation": "功能",
"name_placeholder": "請輸入使用者名稱",
"name_placeholder": "請輸入使用者、帳號名稱",
"role_placeholder": "請輸入角色名稱",
"change_password": "變更密碼",
"choose": "選擇"
@ -260,7 +385,44 @@
"email_format": "請輸入正確的 Email 地址",
"password_format": "密碼長度至少8碼必須包含英文及數字",
"start_time_placeholder": "請輸入預計開始日期",
"finish_time_placeholder": "請輸入完成日期",
"rename": "重新命名",
"download": "下載"
"download": "下載",
"confirm": "確認",
"restore": "復原",
"stop_edit": "停止修改",
"start_edit": "開始修改",
"convert": "轉換"
},
"msg": {
"sure_to_delete": "是否確認刪除該項目?",
"sure_to_delete_permanent": "是否確認永久刪除該項目?",
"delete_success": "刪除成功",
"delete_failed": "刪除失敗",
"mqtt_refresh": "重新設定成功",
"schema_name_required": "架構名稱欄位必填",
"incorrect_format":"格式不正確",
"send_successfully":"送出成功",
"edit_successfully":"修改成功"
},
"setting": {
"electricity_meter":"電表",
"MQTT_parse": "MQTT 解析",
"schema": "架構",
"point": "點位",
"description": "描述",
"IoT_point_name": "IoT 點位名稱",
"IoT_point_code": "IoT 點位代號",
"number_of_decimal_places": "小數位數",
"boolean_value": "布林值",
"hide_point": "點位顯示",
"hide_switch": "switch 功能",
"switch_on_message": "switch 開啟時傳送的訊息",
"switch_off_message": "switch 關閉時傳送的訊息",
"schema_name": "架構名稱",
"IoT_point_structure": "IoT點位結構",
"system_point_name": "系統點位名稱",
"json_format_text": "請貼上 JSON 格式數據",
"json_click_text": "請在左側輸入JSON並點選轉換按鈕"
}
}

View File

@ -8,16 +8,56 @@
"table": {
"no_data": "No data",
"in_otal": "items in total",
"skip_to": "Skip to"
"skip_to": "Skip to",
"serial_number": "Serial Number",
"name": "Name",
"time": "Time"
},
"upload": {
"title": "Select a file or drag and drop here",
"description": "File size cannot exceed 10MB"
"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 in kWH",
"yesterday_electricity_consumption": "Yesterday's electricity consumption in kWH",
"instant_power": "Instant power kW",
"instant_contract_capacity_ratio": "Instant contract capacity ratio %",
"this_last_week": "This Week's / Last Week's",
"thisweek_electricity_consumption": "This weeks electricity consumption",
"lastweek_electricity_consumption": "Last weeks electricity consumption",
"one_hour": "1 hour",
"four_hour": "4 hour",
"eight_hour": "8 hour",
"energy_ranking": "Energy consumption ranking",
"last_30_days_energy_trend": "Energy consumption trend for the past 30 days",
"today_energy_consumption": "Today",
"this_month_energy_consumption": "This month",
"relative_energy_consumption": "Energy consumption trend",
"daily_relative_change": "Daily",
"weekly_relative_change": "Weekly",
"monthly_relative_change": "Monthly",
"yearly_relative_change": "Yearly",
"today": "Today",
"yesterday": "Yesterday",
"this_week": "This week",
"last_week": "Last week",
"this_month": "This month",
"last_month": "Last month",
"this_year": "This year",
"last_year": "Last year",
"work_order": "Work Order",
"system_status": "System Status"
},
"history": {
"title": "Historical Data",
"building_name": "Building",
"device_name": "Device Name",
"system_category": "System Category",
"device_category": "Device Category",
"category": "Category",
"value": "Value",
@ -31,15 +71,6 @@
"end_date": "End date",
"end_time": "End time"
},
"dashboard": {
"yesterday_today": "Yesterday / Today's",
"elec_consumption_comparison": "Electricity Consumption Comparison",
"today_electricity_consumption": "Todays electricity consumption",
"yesterday_electricity_consumption": "Yesterdays electricity consumption",
"this_last_week": "This Week's / Last Week's",
"thisweek_electricity_consumption": "This weeks electricity consumption",
"lastweek_electricity_consumption": "Last weeks electricity consumption"
},
"system": {
"status": "Status",
"details": "Details",
@ -50,8 +81,13 @@
"elec_consumption": "Real-time distribution of electricity consumption",
"total_elec": "Total electricity consumption",
"green_elec": "Green electricity",
"immediate_demand": "immediate demand",
"average_demand": "average demand",
"immediate_demand": "Immediate demand",
"average_demand": "Average demand",
"real_time_Trend": "Real-time Trend",
"contract_capacity": "Contract Capacity",
"alert_capacity": "Alert Capacity",
"reset_value": "Reset Value",
"edit_automatic_demand": "Edit automatic demand",
"elec_bills": "Total electricity bills this year (yuan)",
"interval_elec_charges": "Interval electricity charges (yuan)",
"year_carbon_emission": "Cumulative carbon emission equivalent this year (kg)",
@ -61,7 +97,65 @@
"monthly_elec_consumption": "Monthly electricity consumption analysis",
"monthly_carbon_emission_and_reduction": "Monthly carbon emission equivalent (kgCO2e)",
"monthly_bill_power": "Monthly billing power (kWh)",
"interval_bill_degree": "Interval billing degree"
"interval_bill_degree": "Interval billing degree",
"peak": "Peak",
"semi_peak": "Semi-Peak",
"off_peak": "Off-Peak",
"var_elec_cost": "Var. Elec. Cost",
"fixed_elec_cost": "Fixed Elec. Cost",
"total_elec_cost": "Total Elec. Cost",
"carbon_equivalent": "Carbon Equivalent",
"edit_carbon_emission": "Edit carbon emission coefficient",
"carbon_emission_coefficient": "Carbon emission coefficient",
"electricity_classification": "Electricity Classification",
"electricity_price": "Electricity charge per unit price",
"floor": "Floor",
"maximum": "Maximum",
"maximum_time": "Maximum time",
"minimum": "Minimum value",
"minimum_time": "Minimum time",
"average_value": "Average value",
"start_value": "Start value (kWh)",
"end_value": "End value (kWh)",
"difference": "Difference (kWh)",
"power_consumption": "Power consumption (kWh)",
"ranking": "Ranking",
"subtotal": "Subtotal",
"unit_price": "Unit price",
"total_amount": "Total amount",
"elec_price_list": "Electricity Price List",
"residential": "Residential",
"standard": "Standard",
"simple_elec_price_two_stage": "Simple Time-of-Use Electricity Price (Two-Tier)",
"simple_elec_price_three_stage": "Simple Time-of-Use Electricity Price (Three-Tier)",
"classification": "Classification",
"summer_months": "Summer Months",
"non_summer_months": "Non-Summer Months",
"time_outside_summer_months": "Time Outside Summer Months",
"basic_elec_charge": "Basic Electricity Charge",
"charged_per_household": "Charged Per Household",
"per_household_month": "Per Household Per Month",
"mon_to_friday": "Monday to Friday",
"peak_hours": "Peak Hours",
"semi_peak_hours": "Semi-Peak Hours",
"off_peak_hours": "Off-Peak Hours",
"price_per_kwh": "Price Per kWh",
"all_day": "All Day",
"sat_sun_off_peak_days": "Saturday, Sunday, and Off-Peak Days",
"usage_over_2000kwh": "Usage Over 2000 kWh Per Month",
"add": "Add",
"standard_time_of_use_tariff_2_stage": "Standard Time-of-Use Tariff (Two-Tier)",
"standard_time_of_use_tariff_3_stage": "Standard Time-of-Use Tariff (Three-Tier)",
"single_phase": "Single Phase",
"three_phase": "Three Phase",
"frequent_contract": "Demand Charge",
"per_kw_per_month": "Per kW Per Month",
"non_summer_contract": "Non-Summer Demand Charge",
"saturday_semi_peak_contract": "Saturday Semi-Peak Demand Charge",
"off_peak_contract": "Off-Peak Demand Charge",
"variable_electricity_charge": "Variable Electricity Charge",
"saturday": "Saturday",
"sunday_and_off_peak_days": "Sunday and Off-Peak Days"
},
"alarm": {
"title": "Warning",
@ -84,9 +178,10 @@
"end_date": "End Date",
"building_and_floor": "Building - Floor",
"uuid": "Exception ID",
"alarmClass": "Exception Category",
"alarmClass": "Alarm Conditions",
"device_name": "Device Name",
"device_number": "Device Number",
"device_point_name": "Point Name",
"date": "Occurrence Date",
"time": "Occurrence Time",
"error_msg": "Abnormal Cause",
@ -113,10 +208,12 @@
"qualifications": "Qualifications",
"upper_limit": "Upper Limit",
"lower_limit": "Lower Limit",
"delay": "Duration (s)",
"highDelay": "Max Duration (s)",
"lowDelay": "Min Duration (s)",
"warning_method": "Warning Method",
"warning_time": "Warning Time",
"warning_value": "Warning Value",
"operation": "Function",
"alarm_settings": "Abnormal Alarm Settings",
"time_setting": "Time Setting",
@ -128,7 +225,23 @@
"notify_email": "Email",
"notify_items": "Notification Items",
"notify_list": "Notification List",
"choose": "Choose"
"choose": "Choose",
"day_time": "Week/Time",
"click_time_period": "Please click the time period with your mouse",
"clear": "Clear",
"sunday": "Sunday",
"monday": "Monday",
"tuesday": "Tuesday",
"wednesday": "Wednesday",
"thursday": "Thursday",
"friday": "Friday",
"saturday": "Saturday",
"schedule_name": "Time period name",
"schedule_content": "Time period content",
"reorganization": "MQTT Alarm Reorganization",
"online": "Online",
"offline": "Offline",
"alarm": "Alarm"
},
"operation": {
"title": "Operation And Maintenance Management",
@ -142,13 +255,14 @@
"start_time": "Estimated Start Time",
"upload": "File Upload",
"finish_time": "Completion Time",
"updated_time": "Update Time",
"operation": "Function",
"vendor": "Company",
"contact_person": "Contact Person",
"phone": "Phone",
"email": "Email",
"created_at": "Creation Date",
"maintenance": "Maintenance",
"maintenance": "Upkeep",
"repair": "Repair",
"company_info": "Company Info",
"repair_item": "Repair Item",
@ -156,6 +270,8 @@
"responsible_vendor": "Responsible Vendor",
"not_completed": "Not Completed",
"completed": "Completed",
"complete": "Comp",
"incomplete": "Inc",
"worker_id": "Worker ID",
"notice": "Notice",
"result_description": "Result Description",
@ -181,14 +297,20 @@
"index": "Serial Number",
"oriOrgName": "File",
"operation": "Function",
"upload": "Upload"
"folder_path": "Folder Path",
"upload": "Upload",
"staging_area": "Staging Area",
"no_path": "No path"
},
"assetManagement": {
"title": "Asset Management",
"add_category": "Add Category",
"add_system_category": "Add system category",
"edit_system_category": "Edit system category",
"add_device_category": "Add device category",
"edit_device_category": "Edit device category",
"system_name": "Name",
"system_value": "Code",
"system_parent": "Category",
"system_parent": "System category",
"device_number": "Device Number",
"device_name": "Device Name",
"asset_number": "Asset Number",
@ -216,7 +338,10 @@
"associated_device": "Associated Devices",
"choose": "Choose",
"index": "Serial Number",
"floor_plan": "Floor Plan"
"floor_plan": "Floor Plan",
"department": "Department",
"department_name": "Department Name",
"building": "Building"
},
"accountManagement": {
"account_title": "Account Management",
@ -236,8 +361,8 @@
"phone": "Phone",
"created_at": "Created Time",
"operation": "Function",
"name_placeholder": "Please enter user name",
"role_placeholder": "Please enter the role name",
"name_placeholder": "Please enter the user's name / account",
"role_placeholder": "Please enter the role's name",
"change_password": "Change Password",
"choose": "Choose"
},
@ -254,13 +379,50 @@
"submit": "Submit",
"edit": "Edit",
"delete": "Delete",
"deselect_all": "Deselect all",
"select_all": "Select all",
"deselect_all": "Deselect All",
"select_all": "Select All",
"phone_format": "Please enter the correct phone number format",
"email_format": "Please enter correct email address",
"password_format": "The password must be at least 8 characters long and must contain English and numbers.",
"start_time_placeholder": "Please enter expected start date",
"finish_time_placeholder": "Please enter completion date",
"rename": "Rename",
"download": "Download"
"download": "Download",
"confirm": "Confirm",
"restore": "Restore",
"stop_edit": "Stop 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",
"mqtt_refresh": "MQTT reset successful",
"schema_name_required": "The schema name field is required",
"incorrect_format":"Incorrect format",
"send_successfully":"Sent successfully",
"edit_successfully":"Edited successfully"
},
"setting": {
"electricity_meter": "Electricity Meter",
"MQTT_parse": "MQTT Parse",
"schema": "Schema",
"point": "Point",
"description": "Description",
"IoT_point_name": "IoT Point Name",
"IoT_point_code": "IoT Point Code",
"number_of_decimal_places": "Number of Decimal Places",
"boolean_value": "Boolean Value",
"hide_point": "Point Display",
"hide_switch": "Switch Function",
"switch_on_message": "Switch On Message",
"switch_off_message": "Switch Off Message",
"schema_name": "Schema name",
"IoT_point_structure": "IoT Point Structure",
"system_point_name": "System Point Name",
"json_format_text": "Please paste JSON format data",
"json_click_text": "Please enter JSON on the left and click the conversion button"
}
}

View File

@ -61,4 +61,10 @@ export const AUTHPAGES = [
pageName: "ProductSetting",
navigate: "/productSetting",
},
{
authCode: "PF11",
icon: "cog",
pageName: "Setting",
navigate: "/Setting",
},
];

View File

@ -38,6 +38,7 @@ import {
faFileExcel,
faFileWord,
faFilePowerpoint,
faFileArchive,
faFileAlt,
faDatabase,
faBuilding,
@ -57,8 +58,14 @@ import {
faEye,
faEyeSlash,
faGlobe,
faDownload
faDownload,
faStream,
faSave,
faCrown,
faClock,
faCheckCircle
} from "@fortawesome/free-solid-svg-icons";
import { faCircle,faPaperPlane } from "@fortawesome/free-regular-svg-icons";
/* add icons to the library */
library.add(
@ -97,6 +104,7 @@ library.add(
faFileExcel,
faFileWord,
faFilePowerpoint,
faFileArchive,
faFileAlt,
faDatabase,
faBuilding,
@ -116,7 +124,14 @@ library.add(
faEye,
faEyeSlash,
faGlobe,
faDownload
faDownload,
faStream,
faSave,
faCrown,
faClock,
faCheckCircle,
faCircle,
faPaperPlane
);
export default library;

View File

@ -1,6 +1,7 @@
import useSelectedFloor from "@/hooks/useSelectedFloor";
import { watch, ref, inject } from "vue";
import { useRoute } from "vue-router";
import useSystemShowData from "@/hooks/useSystemShowData";
function useForgeFloor() {
const route = useRoute();
@ -59,9 +60,11 @@ function useForgeFloor() {
forgeViewer.value.hide(parseInt(allDbIdsStr[i]));
}
};
const { flatSubData } = useSystemShowData();
const showDbIdFn = () => {
hideDbIdFn();
subscribeData.value.forEach((value, index) => {
flatSubData.value.forEach((value, index) => {
forgeViewer.value.show(value.forge_dbid);
});
@ -83,7 +86,7 @@ function useForgeFloor() {
forgeViewer.value?.fitToView([forgeViewer.value.model.getRootId()]);
showDbIdFn();
} else {
hideDbIdFn();
showDbIdFn();
// forgeViewer.value.clearSelection();
// forgeViewer.value.model.setAllVisibility(0);
forgeViewer.value.impl.toggleGhosting(false);

View File

@ -34,12 +34,13 @@ export default function useForgeHeatmap() {
const { flatSubData } = useSystemShowData();
const data = computed(() =>
flatSubData.value?.map((d) => ({
...d,
...Object.fromEntries(
d.points.map(({ point, value }) => [point, 0]) || []
),
}))
flatSubData.value?.map((d) => {
const pointsMap = d.points ? Object.fromEntries(d.points.map(({ point, value }) => [point, 0])) : {};
return {
...d,
...pointsMap,
};
})
);
watch(
@ -55,7 +56,7 @@ export default function useForgeHeatmap() {
);
const createHeatMap = async () => {
if (!dataVizExtn.value) return;
if (route.query?.gas === "all" || !route.query?.gas || !dataVizExtn.value) return;
const heatMapName = `iot_heatmap_${route.query?.gas}`;
console.log("createHeatMap", heatMapName);
const {
@ -67,8 +68,7 @@ export default function useForgeHeatmap() {
const shadingGroup = new SurfaceShadingGroup(`${heatMapName}`);
const rooms = new Map();
const roomSet = new Set(data.value.map(({ room_dbid }) => room_dbid));
const roomSet = new Set(data.value.filter(({ device_coordinate_3d }) => device_coordinate_3d).map(({ room_dbid }) => room_dbid));
// 每個room是一個node
[...roomSet].forEach((roomDbId) => {
if (!roomDbId) {

View File

@ -1,20 +1,32 @@
import { watch, inject, markRaw, ref, computed, provide } from "vue";
import useAlarmStore from "@/stores/useAlarmStore";
import hexToRgb from "@/util/hexToRgb";
import useSystemShowData from "@/hooks/useSystemShowData"
import useSystemShowData from "@/hooks/useSystemShowData";
import useForgeHeatmap from "./useForgeHeatmap";
import useForgeFloor from "./useForgeFloor";
export default function useForgeSprite() {
const { subscribeData } = inject("system_deviceList");
const { getCurrentInfoModalData, clearSelectedDeviceInfo } = inject(
"system_selectedDevice"
);
const { subscribeData, realtimeData } = inject("system_deviceList");
const { getCurrentInfoModalData, clearSelectedDeviceInfo, selected_dbid } =
inject("system_selectedDevice");
const forgeViewer = ref(null);
const dataVizExtn = ref(null);
let lastClickedDbId = null;
const { createHeatMap, updateViewExtension } = useForgeHeatmap();
const { updateViewerFloor } = useForgeFloor();
const { updateViewerFloor } = useForgeFloor();
const setCameraPosition = (position, target) => {
// 使用 THREE.Vector3 定義位置與焦點
const newPosition = new THREE.Vector3(position.x, position.y, position.z);
const newTarget = new THREE.Vector3(target.x, target.y, target.z);
// 設定攝影機的新位置與焦點
forgeViewer.value.navigation.setView(newPosition, newTarget);
// 確保 Home 視角
forgeViewer.value.autocam.setCurrentViewAsHome(true);
};
const updateDataVisualization = async (viewer) => {
if (!forgeViewer.value) {
@ -26,7 +38,7 @@ export default function useForgeSprite() {
);
dataVizExtn.value = markRaw(dataVisualization);
updateViewExtension(markRaw(viewer), markRaw(dataVisualization));
updateViewerFloor(markRaw(viewer), markRaw(dataVisualization))
updateViewerFloor(markRaw(viewer), markRaw(dataVisualization));
};
function onSpriteClicked(event) {
@ -51,7 +63,24 @@ export default function useForgeSprite() {
}
}
const { flatSubData } = useSystemShowData()
const { flatSubData } = useSystemShowData();
// 根據設備取得即時狀態顏色
const getDeviceRealtimeColor = (d) => {
if (d.full_name === "SmartSocket-AA001") return "#ff0000";
if (
d.full_name === "SmartSocket-AA003" ||
d.full_name === "SmartSocket-AA004"
)
return "#888888";
const realtimeDevice = realtimeData?.value?.find(
(item) => item.device_number === d.device_number
);
const state = realtimeDevice?.state || "";
if (state === "offnormal" || state === "")
return d.device_close_color || "#999999";
return d.device_normal_color || "#009100";
};
// 創建 sprites
const createSprites = async () => {
@ -60,30 +89,27 @@ export default function useForgeSprite() {
const DataVizCore = Autodesk.DataVisualization.Core;
const viewableType = DataVizCore.ViewableType.SPRITE;
let spriteColor = new THREE.Color(0xffffff);
const BASEURL = import.meta.env.VITE_FORGE_BASEURL;
const spriteIconUrl = `${BASEURL}/hotspot.svg`;
const style = new DataVizCore.ViewableStyle(
viewableType,
spriteColor,
spriteIconUrl
);
const BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const spriteIconUrl = `${BASEURL}/dist/hotspot.svg`;
const viewableData = new DataVizCore.ViewableData();
viewableData.spriteSize = 24; // Sprites as points of size 24 x 24 pixels
flatSubData.value?.forEach((d, index) => {
if (d.device_coordinate_3d) {
const position = d.device_coordinate_3d;
style.color = new THREE.Color(hexToRgb(d.device_normal_color));
// 每個都 new 一個 style
const pointStyle = new DataVizCore.ViewableStyle(
viewableType,
new THREE.Color(hexToRgb(getDeviceRealtimeColor(d))),
spriteIconUrl
);
const viewable = new DataVizCore.SpriteViewable(
position,
style,
pointStyle,
d.spriteDbId
);
viewableData.addViewable(viewable);
}
});
// await viewableData.finish();
// dataVizExtn.value.addViewables(viewableData);
// console.log(dataVizExtn.value);
viewableData.finish().then(
() => {
dataVizExtn.value.addViewables(viewableData);
@ -95,6 +121,16 @@ export default function useForgeSprite() {
);
}
};
// 監聽 realtimeData 變化,重建 sprites
watch(
() => realtimeData?.value,
() => {
if (forgeViewer.value?.isLoadDone()) {
createSprites();
}
},
{ deep: true }
);
watch(
() => flatSubData,
@ -109,6 +145,19 @@ export default function useForgeSprite() {
}
);
watch(
() => selected_dbid,
() => {
if (forgeViewer.value?.isLoadDone()) {
cardfitToView(selected_dbid.value);
}
},
{
immediate: true,
deep: true,
}
);
const forgeClickListener = () => {
console.log("監聽forge");
@ -122,24 +171,38 @@ export default function useForgeSprite() {
);
};
const fitToView = () => {
const { x, y, z } = JSON.parse(searchParams.value.camera_position);
const newPosition = new THREE.Vector3(x, y, z); //!<<< 相机的新位置
const cardfitToView = async ([forge_dbid, spriteDbId]) => {
try {
// 相機調整
const nav = forgeViewer.value.navigation;
const camera = nav.getCamera();
forgeViewer.value.fitToView([forge_dbid], null, true);
const direction = new THREE.Vector3();
camera.getWorldDirection(direction);
const distanceBack = 30;
camera.position.add(direction.multiplyScalar(-distanceBack));
const target = nav.getTarget();
const fov = nav.getVerticalFov();
nav.setRequestTransition(true, camera.position, target, fov);
const {
x: x1,
y: y1,
z: z1,
} = JSON.parse(searchParams.value.target_position); //!<<< 计算新焦点位置
const newTarget = new THREE.Vector3(x1, y1, z1); //!<<< 焦點的新位置
if (lastClickedDbId !== null && lastClickedDbId !== spriteDbId) {
dataVizExtn.value.invalidateViewables([lastClickedDbId], (viewable) => {
return {
scale: 1.0, // 恢復為 scale 1
};
});
}
NOP_VIEWER.navigation.getCamera().setView({
position: newPosition.clone(),
target: newTarget.clone(),
});
setTimeout(() => {
updateDbidPosition(NOP_VIEWER, subscribeData.value);
}, 700);
dataVizExtn.value.invalidateViewables([spriteDbId], (viewable) => {
return {
scale: 2.0, // 設置為 scale 2
};
});
lastClickedDbId = spriteDbId;
} catch (error) {
console.error("Error in cardfitToView:", error);
}
};
const hideAllObjects = () => {
@ -152,7 +215,7 @@ export default function useForgeSprite() {
const showSubSystemObjects = () => {
hideAllObjects();
subscribeData.value.forEach((value, index) => {
flatSubData.value.forEach((value, index) => {
forgeViewer.value.show(value.forge_dbid);
});
@ -171,13 +234,13 @@ export default function useForgeSprite() {
forgeViewer.value.tearDown();
};
return {
createSprites,
setCameraPosition,
updateDataVisualization,
showSubSystemObjects,
forgeClickListener,
clear,
cardfitToView,
};
}

View File

@ -1,381 +0,0 @@
Authors ordered by first contribution
A list of current team members is available at https://jqueryui.com/about
Paul Bakaus <paul.bakaus@gmail.com>
Richard Worth <rdworth@gmail.com>
Yehuda Katz <wycats@gmail.com>
Sean Catchpole <sean@sunsean.com>
John Resig <jeresig@gmail.com>
Tane Piper <piper.tane@gmail.com>
Dmitri Gaskin <dmitrig01@gmail.com>
Klaus Hartl <klaus.hartl@gmail.com>
Stefan Petre <stefan.petre@gmail.com>
Gilles van den Hoven <gilles@webunity.nl>
Micheil Bryan Smith <micheil@brandedcode.com>
Jörn Zaefferer <joern.zaefferer@gmail.com>
Marc Grabanski <m@marcgrabanski.com>
Keith Wood <kbwood@iinet.com.au>
Brandon Aaron <brandon.aaron@gmail.com>
Scott González <scott.gonzalez@gmail.com>
Eduardo Lundgren <eduardolundgren@gmail.com>
Aaron Eisenberger <aaronchi@gmail.com>
Joan Piedra <theneojp@gmail.com>
Bruno Basto <b.basto@gmail.com>
Remy Sharp <remy@leftlogic.com>
Bohdan Ganicky <bohdan.ganicky@gmail.com>
David Bolter <david.bolter@gmail.com>
Chi Cheng <cloudream@gmail.com>
Ca-Phun Ung <pazu2k@gmail.com>
Ariel Flesler <aflesler@gmail.com>
Maggie Wachs <maggie@filamentgroup.com>
Scott Jehl <scottjehl@gmail.com>
Todd Parker <todd@filamentgroup.com>
Andrew Powell <andrew@shellscape.org>
Brant Burnett <btburnett3@gmail.com>
Douglas Neiner <doug@dougneiner.com>
Paul Irish <paul.irish@gmail.com>
Ralph Whitbeck <ralph.whitbeck@gmail.com>
Thibault Duplessis <thibault.duplessis@gmail.com>
Dominique Vincent <dominique.vincent@toitl.com>
Jack Hsu <jack.hsu@gmail.com>
Adam Sontag <ajpiano@ajpiano.com>
Carl Fürstenberg <carl@excito.com>
Kevin Dalman <development@allpro.net>
Alberto Fernández Capel <afcapel@gmail.com>
Jacek Jędrzejewski (https://jacek.jedrzejewski.name)
Ting Kuei <ting@kuei.com>
Samuel Cormier-Iijima <sam@chide.it>
Jon Palmer <jonspalmer@gmail.com>
Ben Hollis <bhollis@amazon.com>
Justin MacCarthy <Justin@Rubystars.biz>
Eyal Kobrigo <kobrigo@hotmail.com>
Tiago Freire <tiago.freire@gmail.com>
Diego Tres <diegotres@gmail.com>
Holger Rüprich <holger@rueprich.de>
Ziling Zhao <zilingzhao@gmail.com>
Mike Alsup <malsup@gmail.com>
Robson Braga Araujo <robsonbraga@gmail.com>
Pierre-Henri Ausseil <ph.ausseil@gmail.com>
Christopher McCulloh <cmcculloh@gmail.com>
Andrew Newcomb <ext.github@preceptsoftware.co.uk>
Lim Chee Aun <cheeaun@gmail.com>
Jorge Barreiro <yortx.barry@gmail.com>
Daniel Steigerwald <daniel@steigerwald.cz>
John Firebaugh <john_firebaugh@bigfix.com>
John Enters <github@darkdark.net>
Andrey Kapitcyn <ru.m157y@gmail.com>
Dmitry Petrov <dpetroff@gmail.com>
Eric Hynds <eric@hynds.net>
Chairat Sunthornwiphat <pipo@sixhead.com>
Josh Varner <josh.varner@gmail.com>
Stéphane Raimbault <stephane.raimbault@gmail.com>
Jay Merrifield <fracmak@gmail.com>
J. Ryan Stinnett <jryans@gmail.com>
Peter Heiberg <peter@heiberg.se>
Alex Dovenmuehle <adovenmuehle@gmail.com>
Jamie Gegerson <git@jamiegegerson.com>
Raymond Schwartz <skeetergraphics@gmail.com>
Phillip Barnes <philbar@gmail.com>
Kyle Wilkinson <kai@wikyd.org>
Khaled AlHourani <me@khaledalhourani.com>
Marian Rudzynski <mr@impaled.org>
Jean-Francois Remy <jeff@melix.org>
Doug Blood <dougblood@gmail.com>
Filippo Cavallarin <filippo.cavallarin@codseq.it>
Heiko Henning <heiko@thehennings.ch>
Aliaksandr Rahalevich <saksmlz@gmail.com>
Mario Visic <mario@mariovisic.com>
Xavi Ramirez <xavi.rmz@gmail.com>
Max Schnur <max.schnur@gmail.com>
Saji Nediyanchath <saji89@gmail.com>
Corey Frang <gnarf37@gmail.com>
Aaron Peterson <aaronp123@yahoo.com>
Ivan Peters <ivan@ivanpeters.com>
Mohamed Cherif Bouchelaghem <cherifbouchelaghem@yahoo.fr>
Marcos Sousa <falecomigo@marcossousa.com>
Michael DellaNoce <mdellanoce@mailtrust.com>
George Marshall <echosx@gmail.com>
Tobias Brunner <tobias@strongswan.org>
Martin Solli <msolli@gmail.com>
David Petersen <public@petersendidit.com>
Dan Heberden <danheberden@gmail.com>
William Kevin Manire <williamkmanire@gmail.com>
Gilmore Davidson <gilmoreorless@gmail.com>
Michael Wu <michaelmwu@gmail.com>
Adam Parod <mystic414@gmail.com>
Guillaume Gautreau <guillaume+github@ghusse.com>
Marcel Toele <EleotleCram@gmail.com>
Dan Streetman <ddstreet@ieee.org>
Matt Hoskins <matt@nipltd.com>
Giovanni Giacobbi <giovanni@giacobbi.net>
Kyle Florence <kyle.florence@gmail.com>
Pavol Hluchý <lopo@losys.sk>
Hans Hillen <hans.hillen@gmail.com>
Mark Johnson <virgofx@live.com>
Trey Hunner <treyhunner@gmail.com>
Shane Whittet <whittet@gmail.com>
Edward A Faulkner <ef@alum.mit.edu>
Adam Baratz <adam@adambaratz.com>
Kato Kazuyoshi <kato.kazuyoshi@gmail.com>
Eike Send <eike.send@gmail.com>
Kris Borchers <kris.borchers@gmail.com>
Eddie Monge <eddie@eddiemonge.com>
Israel Tsadok <itsadok@gmail.com>
Carson McDonald <carson@ioncannon.net>
Jason Davies <jason@jasondavies.com>
Garrison Locke <gplocke@gmail.com>
David Murdoch <david@davidmurdoch.com>
Benjamin Scott Boyle <benjamins.boyle@gmail.com>
Jesse Baird <jebaird@gmail.com>
Jonathan Vingiano <jvingiano@gmail.com>
Dylan Just <dev@ephox.com>
Hiroshi Tomita <tomykaira@gmail.com>
Glenn Goodrich <glenn.goodrich@gmail.com>
Tarafder Ashek-E-Elahi <mail.ashek@gmail.com>
Ryan Neufeld <ryan@neufeldmail.com>
Marc Neuwirth <marc.neuwirth@gmail.com>
Philip Graham <philip.robert.graham@gmail.com>
Benjamin Sterling <benjamin.sterling@kenzomedia.com>
Wesley Walser <waw325@gmail.com>
Kouhei Sutou <kou@clear-code.com>
Karl Kirch <karlkrch@gmail.com>
Chris Kelly <ckdake@ckdake.com>
Jason Oster <jay@kodewerx.org>
Felix Nagel <info@felixnagel.com>
Alexander Polomoshnov <alex.polomoshnov@gmail.com>
David Leal <dgleal@gmail.com>
Igor Milla <igor.fsp.milla@gmail.com>
Dave Methvin <dave.methvin@gmail.com>
Florian Gutmann <f.gutmann@chronimo.com>
Marwan Al Jubeh <marwan.aljubeh@gmail.com>
Milan Broum <midlis@googlemail.com>
Sebastian Sauer <info@dynpages.de>
Gaëtan Muller <m.gaetan89@gmail.com>
Michel Weimerskirch <michel@weimerskirch.net>
William Griffiths <william@ycymro.com>
Stojce Slavkovski <stojce@gmail.com>
David Soms <david.soms@gmail.com>
David De Sloovere <david.desloovere@outlook.com>
Michael P. Jung <michael.jung@terreon.de>
Shannon Pekary <spekary@gmail.com>
Dan Wellman <danwellman@hotmail.com>
Matthew Edward Hutton <meh@corefiling.co.uk>
James Khoury <james@jameskhoury.com>
Rob Loach <robloach@gmail.com>
Alberto Monteiro <betimbrasil@gmail.com>
Alex Rhea <alex.rhea@gmail.com>
Krzysztof Rosiński <rozwell69@gmail.com>
Ryan Olton <oltonr@gmail.com>
Genie <386@mail.com>
Rick Waldron <waldron.rick@gmail.com>
Ian Simpson <spoonlikesham@gmail.com>
Lev Kitsis <spam4lev@gmail.com>
TJ VanToll <tj.vantoll@gmail.com>
Justin Domnitz <jdomnitz@gmail.com>
Douglas Cerna <douglascerna@yahoo.com>
Bert ter Heide <bertjh@hotmail.com>
Jasvir Nagra <jasvir@gmail.com>
Yuriy Khabarov <13real008@gmail.com>
Harri Kilpiö <harri.kilpio@gmail.com>
Lado Lomidze <lado.lomidze@gmail.com>
Amir E. Aharoni <amir.aharoni@mail.huji.ac.il>
Simon Sattes <simon.sattes@gmail.com>
Jo Liss <joliss42@gmail.com>
Guntupalli Karunakar <karunakarg@yahoo.com>
Shahyar Ghobadpour <shahyar@gmail.com>
Lukasz Lipinski <uzza17@gmail.com>
Timo Tijhof <krinklemail@gmail.com>
Jason Moon <jmoon@socialcast.com>
Martin Frost <martinf55@hotmail.com>
Eneko Illarramendi <eneko@illarra.com>
EungJun Yi <semtlenori@gmail.com>
Courtland Allen <courtlandallen@gmail.com>
Viktar Varvanovich <non4eg@gmail.com>
Danny Trunk <dtrunk90@gmail.com>
Pavel Stetina <pavel.stetina@nangu.tv>
Michael Stay <metaweta@gmail.com>
Steven Roussey <sroussey@gmail.com>
Michael Hollis <hollis21@gmail.com>
Lee Rowlands <lee.rowlands@previousnext.com.au>
Timmy Willison <timmywillisn@gmail.com>
Karl Swedberg <kswedberg@gmail.com>
Baoju Yuan <the_guy_1987@hotmail.com>
Maciej Mroziński <maciej.k.mrozinski@gmail.com>
Luis Dalmolin <luis.nh@gmail.com>
Mark Aaron Shirley <maspwr@gmail.com>
Martin Hoch <martin@fidion.de>
Jiayi Yang <tr870829@gmail.com>
Philipp Benjamin Köppchen <xgxtpbk@gws.ms>
Sindre Sorhus <sindresorhus@gmail.com>
Bernhard Sirlinger <bernhard.sirlinger@tele2.de>
Jared A. Scheel <jared@jaredscheel.com>
Rafael Xavier de Souza <rxaviers@gmail.com>
John Chen <zhang.z.chen@intel.com>
Robert Beuligmann <robertbeuligmann@gmail.com>
Dale Kocian <dale.kocian@gmail.com>
Mike Sherov <mike.sherov@gmail.com>
Andrew Couch <andy@couchand.com>
Marc-Andre Lafortune <github@marc-andre.ca>
Nate Eagle <nate.eagle@teamaol.com>
David Souther <davidsouther@gmail.com>
Mathias Stenbom <mathias@stenbom.com>
Sergey Kartashov <ebishkek@yandex.ru>
Avinash R <nashpapa@gmail.com>
Ethan Romba <ethanromba@gmail.com>
Cory Gackenheimer <cory.gack@gmail.com>
Juan Pablo Kaniefsky <jpkaniefsky@gmail.com>
Roman Salnikov <bardt.dz@gmail.com>
Anika Henke <anika@selfthinker.org>
Samuel Bovée <samycookie2000@yahoo.fr>
Fabrício Matté <ult_combo@hotmail.com>
Viktor Kojouharov <vkojouharov@gmail.com>
Pawel Maruszczyk (http://hrabstwo.net)
Pavel Selitskas <p.selitskas@gmail.com>
Bjørn Johansen <post@bjornjohansen.no>
Matthieu Penant <thieum22@hotmail.com>
Dominic Barnes <dominic@dbarnes.info>
David Sullivan <david.sullivan@gmail.com>
Thomas Jaggi <thomas@responsive.ch>
Vahid Sohrabloo <vahid4134@gmail.com>
Travis Carden <travis.carden@gmail.com>
Bruno M. Custódio <bruno@brunomcustodio.com>
Nathanael Silverman <nathanael.silverman@gmail.com>
Christian Wenz <christian@wenz.org>
Steve Urmston <steve@urm.st>
Zaven Muradyan <megalivoithos@gmail.com>
Woody Gilk <shadowhand@deviantart.com>
Zbigniew Motyka <zbigniew.motyka@gmail.com>
Suhail Alkowaileet <xsoh.k7@gmail.com>
Toshi MARUYAMA <marutosijp2@yahoo.co.jp>
David Hansen <hansede@gmail.com>
Brian Grinstead <briangrinstead@gmail.com>
Christian Klammer <christian314159@gmail.com>
Steven Luscher <jquerycla@steveluscher.com>
Gan Eng Chin <engchin.gan@gmail.com>
Gabriel Schulhof <gabriel.schulhof@intel.com>
Alexander Schmitz <arschmitz@gmail.com>
Vilhjálmur Skúlason <vis@dmm.is>
Siebrand Mazeland <siebrand@kitano.nl>
Mohsen Ekhtiari <mohsenekhtiari@yahoo.com>
Pere Orga <gotrunks@gmail.com>
Jasper de Groot <mail@ugomobi.com>
Stephane Deschamps <stephane.deschamps@gmail.com>
Jyoti Deka <dekajp@gmail.com>
Andrei Picus <office.nightcrawler@gmail.com>
Ondrej Novy <novy@ondrej.org>
Jacob McCutcheon <jacob.mccutcheon@gmail.com>
Monika Piotrowicz <monika.piotrowicz@gmail.com>
Imants Horsts <imants.horsts@inbox.lv>
Eric Dahl <eric.c.dahl@gmail.com>
Dave Stein <dave@behance.com>
Dylan Barrell <dylan@barrell.com>
Daniel DeGroff <djdegroff@gmail.com>
Michael Wiencek <mwtuea@gmail.com>
Thomas Meyer <meyertee@gmail.com>
Ruslan Yakhyaev <ruslan@ruslan.io>
Brian J. Dowling <bjd-dev@simplicity.net>
Ben Higgins <ben@extrahop.com>
Yermo Lamers <yml@yml.com>
Patrick Stapleton <github@gdi2290.com>
Trisha Crowley <trisha.crowley@gmail.com>
Usman Akeju <akeju00+github@gmail.com>
Rodrigo Menezes <rod333@gmail.com>
Jacques Perrault <jacques_perrault@us.ibm.com>
Frederik Elvhage <frederik.elvhage@googlemail.com>
Will Holley <willholley@gmail.com>
Uri Gilad <antishok@gmail.com>
Richard Gibson <richard.gibson@gmail.com>
Simen Bekkhus <sbekkhus91@gmail.com>
Chen Eshchar <eshcharc@gmail.com>
Bruno Pérel <brunoperel@gmail.com>
Mohammed Alshehri <m@dralshehri.com>
Lisa Seacat DeLuca <ldeluca@us.ibm.com>
Anne-Gaelle Colom <coloma@westminster.ac.uk>
Adam Foster <slimfoster@gmail.com>
Luke Page <luke.a.page@gmail.com>
Daniel Owens <daniel@matchstickmixup.com>
Michael Orchard <morchard@scottlogic.co.uk>
Marcus Warren <marcus@envoke.com>
Nils Heuermann <nils@world-of-scripts.de>
Marco Ziech <marco@ziech.net>
Patricia Juarez <patrixd@gmail.com>
Ben Mosher <me@benmosher.com>
Ablay Keldibek <atomio.ak@gmail.com>
Thomas Applencourt <thomas.applencourt@irsamc.ups-tlse.fr>
Jiabao Wu <jiabao.foss@gmail.com>
Eric Lee Carraway <github@ericcarraway.com>
Victor Homyakov <vkhomyackov@gmail.com>
Myeongjin Lee <aranet100@gmail.com>
Liran Sharir <lsharir@gmail.com>
Weston Ruter <weston@xwp.co>
Mani Mishra <manimishra902@gmail.com>
Hannah Methvin <hannahmethvin@gmail.com>
Leonardo Balter <leonardo.balter@gmail.com>
Benjamin Albert <benjamin_a5@yahoo.com>
Michał Gołębiowski-Owczarek <m.goleb@gmail.com>
Alyosha Pushak <alyosha.pushak@gmail.com>
Fahad Ahmad <fahadahmad41@hotmail.com>
Matt Brundage <github@mattbrundage.com>
Francesc Baeta <francesc.baeta@gmail.com>
Piotr Baran <piotros@wp.pl>
Mukul Hase <mukulhase@gmail.com>
Konstantin Dinev <kdinev@mail.bw.edu>
Rand Scullard <rand@randscullard.com>
Dan Strohl <dan@wjcg.net>
Maksim Ryzhikov <rv.maksim@gmail.com>
Amine HADDAD <haddad@allegorie.tv>
Amanpreet Singh <apsdehal@gmail.com>
Alexey Balchunas <bleshik@gmail.com>
Peter Kehl <peter.kehl@gmail.com>
Peter Dave Hello <hsu@peterdavehello.org>
Johannes Schäfer <johnschaefer@gmx.de>
Ville Skyttä <ville.skytta@iki.fi>
Ryan Oriecuia <ryan.oriecuia@visioncritical.com>
Sergei Ratnikov <sergeir82@gmail.com>
milk54 <milk851@gmail.com>
Evelyn Masso <evoutofambit@gmail.com>
Robin <mail@robin-fowler.com>
Simon Asika <asika32764@gmail.com>
Kevin Cupp <kevin.cupp@gmail.com>
Jeremy Mickelson <Jeremy.Mickelson@gmail.com>
Kyle Rosenberg <kyle.rosenberg@gmail.com>
Petri Partio <petri.partio@gmail.com>
pallxk <github@pallxk.com>
Luke Brookhart <luke@onjax.com>
claudi <hirt-claudia@gmx.de>
Eirik Sletteberg <eiriksletteberg@gmail.com>
Albert Johansson <albert@intervaro.se>
A. Wells <borgboyone@users.noreply.github.com>
Robert Brignull <robertbrignull@gmail.com>
Horus68 <pauloizidoro@gmail.com>
Maksymenkov Eugene <foatei@gmail.com>
OskarNS <soerensen.oskar@gmail.com>
Gez Quinn <holla@gezquinn.design>
jigar gala <jigar.gala140291@gmail.com>
Florian Wegscheider <flo.wegscheider@gmail.com>
Fatér Zsolt <fater.zsolt@gmail.com>
Szabolcs Szabolcsi-Toth <nec@shell8.net>
Jérémy Munsch <github@jeremydev.ovh>
Hrvoje Novosel <hrvoje.novosel@gmail.com>
Paul Capron <PaulCapron@users.noreply.github.com>
Micah Miller <mikhey@runbox.com>
sakshi87 <53863764+sakshi87@users.noreply.github.com>
Mikolaj Wolicki <wolicki.mikolaj@gmail.com>
Patrick McKay <patrick.mckay@vumc.org>
c-lambert <58025159+c-lambert@users.noreply.github.com>
Josep Sanz <josepsanzcamp@gmail.com>
Ben Mullins <benm@umich.edu>
Christian Oliff <christianoliff@pm.me>
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Adam Lidén Hällgren <adamlh92@gmail.com>
James Hinderks <hinderks@gmail.com>
Denny Septian Panggabean <97607754+ddevsr@users.noreply.github.com>
Matías Cánepa <matias.canepa@gmail.com>
Ashish Kurmi <100655670+boahc077@users.noreply.github.com>
DeerBear <andrea.raimondi@gmail.com>
Дилян Палаузов <dpa-github@aegee.org>
Kenneth DeBacker <kcdebacker@gmail.com>
Timo Tijhof <krinkle@fastmail.com>
Timmy Willison <timmywil@users.noreply.github.com>
divdeploy <166095818+divdeploy@users.noreply.github.com>
mark van tilburg <markvantilburg@gmail.com>

View File

@ -1,43 +0,0 @@
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
This software consists of voluntary contributions made by many
individuals. For exact contribution history, see the revision history
available at https://github.com/jquery/jquery-ui
The following license applies to all parts of this software except as
documented below:
====
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
====
Copyright and related rights for sample code are waived via CC0. Sample
code is defined as all source code contained within the demos directory.
CC0: http://creativecommons.org/publicdomain/zero/1.0/
====
All files located in the node_modules and external directories are
externally maintained libraries used by this software which have their
own licenses; we recommend you read them, as their terms may differ from
the terms above.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -1,503 +0,0 @@
<!doctype html>
<html lang="us">
<head>
<meta charset="utf-8">
<title>jQuery UI Example Page</title>
<link href="jquery-ui.css" rel="stylesheet">
<style>
body{
font-family: "Trebuchet MS", sans-serif;
margin: 50px;
}
.demoHeaders {
margin-top: 2em;
}
#dialog-link {
padding: .4em 1em .4em 20px;
text-decoration: none;
position: relative;
}
#dialog-link span.ui-icon {
margin: 0 5px 0 0;
position: absolute;
left: .2em;
top: 50%;
margin-top: -8px;
}
#icons {
margin: 0;
padding: 0;
}
#icons li {
margin: 2px;
position: relative;
padding: 4px 0;
cursor: pointer;
float: left;
list-style: none;
}
#icons span.ui-icon {
float: left;
margin: 0 4px;
}
.fakewindowcontain .ui-widget-overlay {
position: absolute;
}
select {
width: 200px;
}
</style>
</head>
<body>
<h1>Welcome to jQuery UI!</h1>
<div class="ui-widget">
<p>This page demonstrates the widgets and theme you selected in Download Builder. Please make sure you are using them with a compatible jQuery version.</p>
</div>
<h1>YOUR COMPONENTS:</h1>
<!-- Accordion -->
<h2 class="demoHeaders">Accordion</h2>
<div id="accordion">
<h3>First</h3>
<div>Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet.</div>
<h3>Second</h3>
<div>Phasellus mattis tincidunt nibh.</div>
<h3>Third</h3>
<div>Nam dui erat, auctor a, dignissim quis.</div>
</div>
<!-- Autocomplete -->
<h2 class="demoHeaders">Autocomplete</h2>
<div>
<input id="autocomplete" title="type &quot;a&quot;">
</div>
<!-- Button -->
<h2 class="demoHeaders">Button</h2>
<button id="button">A button element</button>
<button id="button-icon">An icon-only button</button>
<!-- Checkboxradio -->
<h2 class="demoHeaders">Checkboxradio</h2>
<form style="margin-top: 1em;">
<div id="radioset">
<input type="radio" id="radio1" name="radio"><label for="radio1">Choice 1</label>
<input type="radio" id="radio2" name="radio" checked="checked"><label for="radio2">Choice 2</label>
<input type="radio" id="radio3" name="radio"><label for="radio3">Choice 3</label>
</div>
</form>
<!-- Controlgroup -->
<h2 class="demoHeaders">Controlgroup</h2>
<fieldset>
<legend>Rental Car</legend>
<div id="controlgroup">
<select id="car-type">
<option>Compact car</option>
<option>Midsize car</option>
<option>Full size car</option>
<option>SUV</option>
<option>Luxury</option>
<option>Truck</option>
<option>Van</option>
</select>
<label for="transmission-standard">Standard</label>
<input type="radio" name="transmission" id="transmission-standard">
<label for="transmission-automatic">Automatic</label>
<input type="radio" name="transmission" id="transmission-automatic">
<label for="insurance">Insurance</label>
<input type="checkbox" name="insurance" id="insurance">
<label for="horizontal-spinner" class="ui-controlgroup-label"># of cars</label>
<input id="horizontal-spinner" class="ui-spinner-input">
<button>Book Now!</button>
</div>
</fieldset>
<!-- Tabs -->
<h2 class="demoHeaders">Tabs</h2>
<div id="tabs">
<ul>
<li><a href="#tabs-1">First</a></li>
<li><a href="#tabs-2">Second</a></li>
<li><a href="#tabs-3">Third</a></li>
</ul>
<div id="tabs-1">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div>
<div id="tabs-2">Phasellus mattis tincidunt nibh. Cras orci urna, blandit id, pretium vel, aliquet ornare, felis. Maecenas scelerisque sem non nisl. Fusce sed lorem in enim dictum bibendum.</div>
<div id="tabs-3">Nam dui erat, auctor a, dignissim quis, sollicitudin eu, felis. Pellentesque nisi urna, interdum eget, sagittis et, consequat vestibulum, lacus. Mauris porttitor ullamcorper augue.</div>
</div>
<h2 class="demoHeaders">Dialog</h2>
<p>
<button id="dialog-link" class="ui-button ui-corner-all ui-widget">
<span class="ui-icon ui-icon-newwin"></span>Open Dialog
</button>
</p>
<h2 class="demoHeaders">Overlay and Shadow Classes</h2>
<div style="position: relative; width: 96%; height: 200px; padding:1% 2%; overflow:hidden;" class="fakewindowcontain">
<p>Lorem ipsum dolor sit amet, Nulla nec tortor. Donec id elit quis purus consectetur consequat. </p><p>Nam congue semper tellus. Sed erat dolor, dapibus sit amet, venenatis ornare, ultrices ut, nisi. Aliquam ante. Suspendisse scelerisque dui nec velit. Duis augue augue, gravida euismod, vulputate ac, facilisis id, sem. Morbi in orci. </p><p>Nulla purus lacus, pulvinar vel, malesuada ac, mattis nec, quam. Nam molestie scelerisque quam. Nullam feugiat cursus lacus.orem ipsum dolor sit amet, consectetur adipiscing elit. Donec libero risus, commodo vitae, pharetra mollis, posuere eu, pede. Nulla nec tortor. Donec id elit quis purus consectetur consequat. </p><p>Nam congue semper tellus. Sed erat dolor, dapibus sit amet, venenatis ornare, ultrices ut, nisi. Aliquam ante. Suspendisse scelerisque dui nec velit. Duis augue augue, gravida euismod, vulputate ac, facilisis id, sem. Morbi in orci. Nulla purus lacus, pulvinar vel, malesuada ac, mattis nec, quam. Nam molestie scelerisque quam. </p><p>Nullam feugiat cursus lacus.orem ipsum dolor sit amet, consectetur adipiscing elit. Donec libero risus, commodo vitae, pharetra mollis, posuere eu, pede. Nulla nec tortor. Donec id elit quis purus consectetur consequat. Nam congue semper tellus. Sed erat dolor, dapibus sit amet, venenatis ornare, ultrices ut, nisi. Aliquam ante. </p><p>Suspendisse scelerisque dui nec velit. Duis augue augue, gravida euismod, vulputate ac, facilisis id, sem. Morbi in orci. Nulla purus lacus, pulvinar vel, malesuada ac, mattis nec, quam. Nam molestie scelerisque quam. Nullam feugiat cursus lacus.orem ipsum dolor sit amet, consectetur adipiscing elit. Donec libero risus, commodo vitae, pharetra mollis, posuere eu, pede. Nulla nec tortor. Donec id elit quis purus consectetur consequat. Nam congue semper tellus. Sed erat dolor, dapibus sit amet, venenatis ornare, ultrices ut, nisi. </p>
<!-- ui-dialog -->
<div class="ui-widget-overlay ui-front"></div>
<div style="position: absolute; width: 320px; left: 50px; top: 30px; padding: 1.2em" class="ui-widget ui-front ui-widget-content ui-corner-all ui-widget-shadow">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
</div>
</div>
<!-- ui-dialog -->
<div id="dialog" title="Dialog Title">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
<h2 class="demoHeaders">Framework Icons (content color preview)</h2>
<ul id="icons" class="ui-widget ui-helper-clearfix">
<li class="ui-state-default ui-corner-all" title=".ui-icon-caret-1-n"><span class="ui-icon ui-icon-caret-1-n"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-caret-1-ne"><span class="ui-icon ui-icon-caret-1-ne"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-caret-1-e"><span class="ui-icon ui-icon-caret-1-e"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-caret-1-se"><span class="ui-icon ui-icon-caret-1-se"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-caret-1-s"><span class="ui-icon ui-icon-caret-1-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-caret-1-sw"><span class="ui-icon ui-icon-caret-1-sw"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-caret-1-w"><span class="ui-icon ui-icon-caret-1-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-caret-1-nw"><span class="ui-icon ui-icon-caret-1-nw"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-caret-2-n-s"><span class="ui-icon ui-icon-caret-2-n-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-caret-2-e-w"><span class="ui-icon ui-icon-caret-2-e-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-n"><span class="ui-icon ui-icon-triangle-1-n"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-ne"><span class="ui-icon ui-icon-triangle-1-ne"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-e"><span class="ui-icon ui-icon-triangle-1-e"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-se"><span class="ui-icon ui-icon-triangle-1-se"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-s"><span class="ui-icon ui-icon-triangle-1-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-sw"><span class="ui-icon ui-icon-triangle-1-sw"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-w"><span class="ui-icon ui-icon-triangle-1-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-1-nw"><span class="ui-icon ui-icon-triangle-1-nw"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-2-n-s"><span class="ui-icon ui-icon-triangle-2-n-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-triangle-2-e-w"><span class="ui-icon ui-icon-triangle-2-e-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-n"><span class="ui-icon ui-icon-arrow-1-n"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-ne"><span class="ui-icon ui-icon-arrow-1-ne"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-e"><span class="ui-icon ui-icon-arrow-1-e"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-se"><span class="ui-icon ui-icon-arrow-1-se"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-s"><span class="ui-icon ui-icon-arrow-1-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-sw"><span class="ui-icon ui-icon-arrow-1-sw"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-w"><span class="ui-icon ui-icon-arrow-1-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-1-nw"><span class="ui-icon ui-icon-arrow-1-nw"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-2-n-s"><span class="ui-icon ui-icon-arrow-2-n-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-2-ne-sw"><span class="ui-icon ui-icon-arrow-2-ne-sw"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-2-e-w"><span class="ui-icon ui-icon-arrow-2-e-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-2-se-nw"><span class="ui-icon ui-icon-arrow-2-se-nw"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowstop-1-n"><span class="ui-icon ui-icon-arrowstop-1-n"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowstop-1-e"><span class="ui-icon ui-icon-arrowstop-1-e"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowstop-1-s"><span class="ui-icon ui-icon-arrowstop-1-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowstop-1-w"><span class="ui-icon ui-icon-arrowstop-1-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-n"><span class="ui-icon ui-icon-arrowthick-1-n"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-ne"><span class="ui-icon ui-icon-arrowthick-1-ne"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-e"><span class="ui-icon ui-icon-arrowthick-1-e"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-se"><span class="ui-icon ui-icon-arrowthick-1-se"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-s"><span class="ui-icon ui-icon-arrowthick-1-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-sw"><span class="ui-icon ui-icon-arrowthick-1-sw"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-w"><span class="ui-icon ui-icon-arrowthick-1-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-1-nw"><span class="ui-icon ui-icon-arrowthick-1-nw"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-2-n-s"><span class="ui-icon ui-icon-arrowthick-2-n-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-2-ne-sw"><span class="ui-icon ui-icon-arrowthick-2-ne-sw"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-2-e-w"><span class="ui-icon ui-icon-arrowthick-2-e-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthick-2-se-nw"><span class="ui-icon ui-icon-arrowthick-2-se-nw"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthickstop-1-n"><span class="ui-icon ui-icon-arrowthickstop-1-n"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthickstop-1-e"><span class="ui-icon ui-icon-arrowthickstop-1-e"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthickstop-1-s"><span class="ui-icon ui-icon-arrowthickstop-1-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowthickstop-1-w"><span class="ui-icon ui-icon-arrowthickstop-1-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturnthick-1-w"><span class="ui-icon ui-icon-arrowreturnthick-1-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturnthick-1-n"><span class="ui-icon ui-icon-arrowreturnthick-1-n"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturnthick-1-e"><span class="ui-icon ui-icon-arrowreturnthick-1-e"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturnthick-1-s"><span class="ui-icon ui-icon-arrowreturnthick-1-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturn-1-w"><span class="ui-icon ui-icon-arrowreturn-1-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturn-1-n"><span class="ui-icon ui-icon-arrowreturn-1-n"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturn-1-e"><span class="ui-icon ui-icon-arrowreturn-1-e"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowreturn-1-s"><span class="ui-icon ui-icon-arrowreturn-1-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowrefresh-1-w"><span class="ui-icon ui-icon-arrowrefresh-1-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowrefresh-1-n"><span class="ui-icon ui-icon-arrowrefresh-1-n"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowrefresh-1-e"><span class="ui-icon ui-icon-arrowrefresh-1-e"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrowrefresh-1-s"><span class="ui-icon ui-icon-arrowrefresh-1-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-4"><span class="ui-icon ui-icon-arrow-4"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-arrow-4-diag"><span class="ui-icon ui-icon-arrow-4-diag"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-extlink"><span class="ui-icon ui-icon-extlink"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-newwin"><span class="ui-icon ui-icon-newwin"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-refresh"><span class="ui-icon ui-icon-refresh"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-shuffle"><span class="ui-icon ui-icon-shuffle"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-transfer-e-w"><span class="ui-icon ui-icon-transfer-e-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-transferthick-e-w"><span class="ui-icon ui-icon-transferthick-e-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-folder-collapsed"><span class="ui-icon ui-icon-folder-collapsed"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-folder-open"><span class="ui-icon ui-icon-folder-open"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-document"><span class="ui-icon ui-icon-document"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-document-b"><span class="ui-icon ui-icon-document-b"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-note"><span class="ui-icon ui-icon-note"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-mail-closed"><span class="ui-icon ui-icon-mail-closed"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-mail-open"><span class="ui-icon ui-icon-mail-open"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-suitcase"><span class="ui-icon ui-icon-suitcase"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-comment"><span class="ui-icon ui-icon-comment"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-person"><span class="ui-icon ui-icon-person"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-print"><span class="ui-icon ui-icon-print"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-trash"><span class="ui-icon ui-icon-trash"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-locked"><span class="ui-icon ui-icon-locked"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-unlocked"><span class="ui-icon ui-icon-unlocked"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-bookmark"><span class="ui-icon ui-icon-bookmark"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-tag"><span class="ui-icon ui-icon-tag"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-home"><span class="ui-icon ui-icon-home"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-flag"><span class="ui-icon ui-icon-flag"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-calculator"><span class="ui-icon ui-icon-calculator"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-cart"><span class="ui-icon ui-icon-cart"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-pencil"><span class="ui-icon ui-icon-pencil"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-clock"><span class="ui-icon ui-icon-clock"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-disk"><span class="ui-icon ui-icon-disk"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-calendar"><span class="ui-icon ui-icon-calendar"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-zoomin"><span class="ui-icon ui-icon-zoomin"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-zoomout"><span class="ui-icon ui-icon-zoomout"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-search"><span class="ui-icon ui-icon-search"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-wrench"><span class="ui-icon ui-icon-wrench"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-gear"><span class="ui-icon ui-icon-gear"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-heart"><span class="ui-icon ui-icon-heart"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-star"><span class="ui-icon ui-icon-star"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-link"><span class="ui-icon ui-icon-link"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-cancel"><span class="ui-icon ui-icon-cancel"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-plus"><span class="ui-icon ui-icon-plus"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-plusthick"><span class="ui-icon ui-icon-plusthick"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-minus"><span class="ui-icon ui-icon-minus"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-minusthick"><span class="ui-icon ui-icon-minusthick"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-close"><span class="ui-icon ui-icon-close"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-closethick"><span class="ui-icon ui-icon-closethick"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-key"><span class="ui-icon ui-icon-key"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-lightbulb"><span class="ui-icon ui-icon-lightbulb"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-scissors"><span class="ui-icon ui-icon-scissors"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-clipboard"><span class="ui-icon ui-icon-clipboard"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-copy"><span class="ui-icon ui-icon-copy"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-contact"><span class="ui-icon ui-icon-contact"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-image"><span class="ui-icon ui-icon-image"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-video"><span class="ui-icon ui-icon-video"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-script"><span class="ui-icon ui-icon-script"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-alert"><span class="ui-icon ui-icon-alert"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-info"><span class="ui-icon ui-icon-info"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-notice"><span class="ui-icon ui-icon-notice"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-help"><span class="ui-icon ui-icon-help"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-check"><span class="ui-icon ui-icon-check"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-bullet"><span class="ui-icon ui-icon-bullet"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-radio-off"><span class="ui-icon ui-icon-radio-off"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-radio-on"><span class="ui-icon ui-icon-radio-on"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-pin-w"><span class="ui-icon ui-icon-pin-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-pin-s"><span class="ui-icon ui-icon-pin-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-play"><span class="ui-icon ui-icon-play"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-pause"><span class="ui-icon ui-icon-pause"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-seek-next"><span class="ui-icon ui-icon-seek-next"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-seek-prev"><span class="ui-icon ui-icon-seek-prev"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-seek-end"><span class="ui-icon ui-icon-seek-end"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-seek-first"><span class="ui-icon ui-icon-seek-first"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-stop"><span class="ui-icon ui-icon-stop"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-eject"><span class="ui-icon ui-icon-eject"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-volume-off"><span class="ui-icon ui-icon-volume-off"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-volume-on"><span class="ui-icon ui-icon-volume-on"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-power"><span class="ui-icon ui-icon-power"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-signal-diag"><span class="ui-icon ui-icon-signal-diag"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-signal"><span class="ui-icon ui-icon-signal"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-battery-0"><span class="ui-icon ui-icon-battery-0"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-battery-1"><span class="ui-icon ui-icon-battery-1"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-battery-2"><span class="ui-icon ui-icon-battery-2"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-battery-3"><span class="ui-icon ui-icon-battery-3"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-plus"><span class="ui-icon ui-icon-circle-plus"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-minus"><span class="ui-icon ui-icon-circle-minus"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-close"><span class="ui-icon ui-icon-circle-close"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-triangle-e"><span class="ui-icon ui-icon-circle-triangle-e"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-triangle-s"><span class="ui-icon ui-icon-circle-triangle-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-triangle-w"><span class="ui-icon ui-icon-circle-triangle-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-triangle-n"><span class="ui-icon ui-icon-circle-triangle-n"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-arrow-e"><span class="ui-icon ui-icon-circle-arrow-e"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-arrow-s"><span class="ui-icon ui-icon-circle-arrow-s"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-arrow-w"><span class="ui-icon ui-icon-circle-arrow-w"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-arrow-n"><span class="ui-icon ui-icon-circle-arrow-n"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-zoomin"><span class="ui-icon ui-icon-circle-zoomin"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-zoomout"><span class="ui-icon ui-icon-circle-zoomout"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circle-check"><span class="ui-icon ui-icon-circle-check"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circlesmall-plus"><span class="ui-icon ui-icon-circlesmall-plus"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circlesmall-minus"><span class="ui-icon ui-icon-circlesmall-minus"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-circlesmall-close"><span class="ui-icon ui-icon-circlesmall-close"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-squaresmall-plus"><span class="ui-icon ui-icon-squaresmall-plus"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-squaresmall-minus"><span class="ui-icon ui-icon-squaresmall-minus"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-squaresmall-close"><span class="ui-icon ui-icon-squaresmall-close"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-grip-dotted-vertical"><span class="ui-icon ui-icon-grip-dotted-vertical"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-grip-dotted-horizontal"><span class="ui-icon ui-icon-grip-dotted-horizontal"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-grip-solid-vertical"><span class="ui-icon ui-icon-grip-solid-vertical"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-grip-solid-horizontal"><span class="ui-icon ui-icon-grip-solid-horizontal"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-gripsmall-diagonal-se"><span class="ui-icon ui-icon-gripsmall-diagonal-se"></span></li>
<li class="ui-state-default ui-corner-all" title=".ui-icon-grip-diagonal-se"><span class="ui-icon ui-icon-grip-diagonal-se"></span></li>
</ul>
<!-- Slider -->
<h2 class="demoHeaders">Slider</h2>
<div id="slider"></div>
<!-- Datepicker -->
<h2 class="demoHeaders">Datepicker</h2>
<div id="datepicker"></div>
<!-- Progressbar -->
<h2 class="demoHeaders">Progressbar</h2>
<div id="progressbar"></div>
<!-- Progressbar -->
<h2 class="demoHeaders">Selectmenu</h2>
<select id="selectmenu">
<option>Slower</option>
<option>Slow</option>
<option selected="selected">Medium</option>
<option>Fast</option>
<option>Faster</option>
</select>
<!-- Spinner -->
<h2 class="demoHeaders">Spinner</h2>
<input id="spinner">
<!-- Menu -->
<h2 class="demoHeaders">Menu</h2>
<ul style="width:100px;" id="menu">
<li><div>Item 1</div></li>
<li><div>Item 2</div></li>
<li><div>Item 3</div>
<ul>
<li><div>Item 3-1</div></li>
<li><div>Item 3-2</div></li>
<li><div>Item 3-3</div></li>
<li><div>Item 3-4</div></li>
<li><div>Item 3-5</div></li>
</ul>
</li>
<li><div>Item 4</div></li>
<li><div>Item 5</div></li>
</ul>
<!-- Tooltip -->
<h2 class="demoHeaders">Tooltip</h2>
<p id="tooltip">
<a href="#" title="That&apos;s what this widget is">Tooltips</a> can be attached to any element. When you hover
the element with your mouse, the title attribute is displayed in a little box next to the element, just like a native tooltip.
</p>
<!-- Highlight / Error -->
<h2 class="demoHeaders">Highlight / Error</h2>
<div class="ui-widget">
<div class="ui-state-highlight ui-corner-all" style="margin-top: 20px; padding: 0 .7em;">
<p><span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>
<strong>Hey!</strong> Sample ui-state-highlight style.</p>
</div>
</div>
<br>
<div class="ui-widget">
<div class="ui-state-error ui-corner-all" style="padding: 0 .7em;">
<p><span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span>
<strong>Alert:</strong> Sample ui-state-error style.</p>
</div>
</div>
<script src="external/jquery/jquery.js"></script>
<script src="jquery-ui.js"></script>
<script>
$( "#accordion" ).accordion();
var availableTags = [
"ActionScript",
"AppleScript",
"Asp",
"BASIC",
"C",
"C++",
"Clojure",
"COBOL",
"ColdFusion",
"Erlang",
"Fortran",
"Groovy",
"Haskell",
"Java",
"JavaScript",
"Lisp",
"Perl",
"PHP",
"Python",
"Ruby",
"Scala",
"Scheme"
];
$( "#autocomplete" ).autocomplete({
source: availableTags
});
$( "#button" ).button();
$( "#button-icon" ).button({
icon: "ui-icon-gear",
showLabel: false
});
$( "#radioset" ).buttonset();
$( "#controlgroup" ).controlgroup();
$( "#tabs" ).tabs();
$( "#dialog" ).dialog({
autoOpen: false,
width: 400,
buttons: [
{
text: "Ok",
click: function() {
$( this ).dialog( "close" );
}
},
{
text: "Cancel",
click: function() {
$( this ).dialog( "close" );
}
}
]
});
// Link to open the dialog
$( "#dialog-link" ).click(function( event ) {
$( "#dialog" ).dialog( "open" );
event.preventDefault();
});
$( "#datepicker" ).datepicker({
inline: true
});
$( "#slider" ).slider({
range: true,
values: [ 17, 67 ]
});
$( "#progressbar" ).progressbar({
value: 20
});
$( "#spinner" ).spinner();
$( "#menu" ).menu();
$( "#tooltip" ).tooltip();
$( "#selectmenu" ).selectmenu();
// Hover states on the static widgets
$( "#dialog-link, #icons li" ).hover(
function() {
$( this ).addClass( "ui-state-hover" );
},
function() {
$( this ).removeClass( "ui-state-hover" );
}
);
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,886 +0,0 @@
/*!
* jQuery UI CSS Framework 1.13.3
* https://jqueryui.com
*
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* https://jquery.org/license
*
* https://api.jqueryui.com/category/theming/
*/
.ui-draggable-handle {
-ms-touch-action: none;
touch-action: none;
}
/* Layout helpers
----------------------------------*/
.ui-helper-hidden {
display: none;
}
.ui-helper-hidden-accessible {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
.ui-helper-reset {
margin: 0;
padding: 0;
border: 0;
outline: 0;
line-height: 1.3;
text-decoration: none;
font-size: 100%;
list-style: none;
}
.ui-helper-clearfix:before,
.ui-helper-clearfix:after {
content: "";
display: table;
border-collapse: collapse;
}
.ui-helper-clearfix:after {
clear: both;
}
.ui-helper-zfix {
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
opacity: 0;
-ms-filter: "alpha(opacity=0)"; /* support: IE8 */
}
.ui-front {
z-index: 100;
}
/* Interaction Cues
----------------------------------*/
.ui-state-disabled {
cursor: default !important;
pointer-events: none;
}
/* Icons
----------------------------------*/
.ui-icon {
display: inline-block;
vertical-align: middle;
margin-top: -.25em;
position: relative;
text-indent: -99999px;
overflow: hidden;
background-repeat: no-repeat;
}
.ui-widget-icon-block {
left: 50%;
margin-left: -8px;
display: block;
}
/* Misc visuals
----------------------------------*/
/* Overlays */
.ui-widget-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.ui-resizable {
position: relative;
}
.ui-resizable-handle {
position: absolute;
font-size: 0.1px;
display: block;
-ms-touch-action: none;
touch-action: none;
}
.ui-resizable-disabled .ui-resizable-handle,
.ui-resizable-autohide .ui-resizable-handle {
display: none;
}
.ui-resizable-n {
cursor: n-resize;
height: 7px;
width: 100%;
top: -5px;
left: 0;
}
.ui-resizable-s {
cursor: s-resize;
height: 7px;
width: 100%;
bottom: -5px;
left: 0;
}
.ui-resizable-e {
cursor: e-resize;
width: 7px;
right: -5px;
top: 0;
height: 100%;
}
.ui-resizable-w {
cursor: w-resize;
width: 7px;
left: -5px;
top: 0;
height: 100%;
}
.ui-resizable-se {
cursor: se-resize;
width: 12px;
height: 12px;
right: 1px;
bottom: 1px;
}
.ui-resizable-sw {
cursor: sw-resize;
width: 9px;
height: 9px;
left: -5px;
bottom: -5px;
}
.ui-resizable-nw {
cursor: nw-resize;
width: 9px;
height: 9px;
left: -5px;
top: -5px;
}
.ui-resizable-ne {
cursor: ne-resize;
width: 9px;
height: 9px;
right: -5px;
top: -5px;
}
.ui-selectable {
-ms-touch-action: none;
touch-action: none;
}
.ui-selectable-helper {
position: absolute;
z-index: 100;
border: 1px dotted black;
}
.ui-sortable-handle {
-ms-touch-action: none;
touch-action: none;
}
.ui-accordion .ui-accordion-header {
display: block;
cursor: pointer;
position: relative;
margin: 2px 0 0 0;
padding: .5em .5em .5em .7em;
font-size: 100%;
}
.ui-accordion .ui-accordion-content {
padding: 1em 2.2em;
border-top: 0;
overflow: auto;
}
.ui-autocomplete {
position: absolute;
top: 0;
left: 0;
cursor: default;
}
.ui-menu {
list-style: none;
padding: 0;
margin: 0;
display: block;
outline: 0;
}
.ui-menu .ui-menu {
position: absolute;
}
.ui-menu .ui-menu-item {
margin: 0;
cursor: pointer;
/* support: IE10, see #8844 */
list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
}
.ui-menu .ui-menu-item-wrapper {
position: relative;
padding: 3px 1em 3px .4em;
}
.ui-menu .ui-menu-divider {
margin: 5px 0;
height: 0;
font-size: 0;
line-height: 0;
border-width: 1px 0 0 0;
}
.ui-menu .ui-state-focus,
.ui-menu .ui-state-active {
margin: -1px;
}
/* icon support */
.ui-menu-icons {
position: relative;
}
.ui-menu-icons .ui-menu-item-wrapper {
padding-left: 2em;
}
/* left-aligned */
.ui-menu .ui-icon {
position: absolute;
top: 0;
bottom: 0;
left: .2em;
margin: auto 0;
}
/* right-aligned */
.ui-menu .ui-menu-icon {
left: auto;
right: 0;
}
.ui-button {
padding: .4em 1em;
display: inline-block;
position: relative;
line-height: normal;
margin-right: .1em;
cursor: pointer;
vertical-align: middle;
text-align: center;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
/* Support: IE <= 11 */
overflow: visible;
}
.ui-button,
.ui-button:link,
.ui-button:visited,
.ui-button:hover,
.ui-button:active {
text-decoration: none;
}
/* to make room for the icon, a width needs to be set here */
.ui-button-icon-only {
width: 2em;
box-sizing: border-box;
text-indent: -9999px;
white-space: nowrap;
}
/* no icon support for input elements */
input.ui-button.ui-button-icon-only {
text-indent: 0;
}
/* button icon element(s) */
.ui-button-icon-only .ui-icon {
position: absolute;
top: 50%;
left: 50%;
margin-top: -8px;
margin-left: -8px;
}
.ui-button.ui-icon-notext .ui-icon {
padding: 0;
width: 2.1em;
height: 2.1em;
text-indent: -9999px;
white-space: nowrap;
}
input.ui-button.ui-icon-notext .ui-icon {
width: auto;
height: auto;
text-indent: 0;
white-space: normal;
padding: .4em 1em;
}
/* workarounds */
/* Support: Firefox 5 - 40 */
input.ui-button::-moz-focus-inner,
button.ui-button::-moz-focus-inner {
border: 0;
padding: 0;
}
.ui-controlgroup {
vertical-align: middle;
display: inline-block;
}
.ui-controlgroup > .ui-controlgroup-item {
float: left;
margin-left: 0;
margin-right: 0;
}
.ui-controlgroup > .ui-controlgroup-item:focus,
.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus {
z-index: 9999;
}
.ui-controlgroup-vertical > .ui-controlgroup-item {
display: block;
float: none;
width: 100%;
margin-top: 0;
margin-bottom: 0;
text-align: left;
}
.ui-controlgroup-vertical .ui-controlgroup-item {
box-sizing: border-box;
}
.ui-controlgroup .ui-controlgroup-label {
padding: .4em 1em;
}
.ui-controlgroup .ui-controlgroup-label span {
font-size: 80%;
}
.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item {
border-left: none;
}
.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item {
border-top: none;
}
.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content {
border-right: none;
}
.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content {
border-bottom: none;
}
/* Spinner specific style fixes */
.ui-controlgroup-vertical .ui-spinner-input {
/* Support: IE8 only, Android < 4.4 only */
width: 75%;
width: calc( 100% - 2.4em );
}
.ui-controlgroup-vertical .ui-spinner .ui-spinner-up {
border-top-style: solid;
}
.ui-checkboxradio-label .ui-icon-background {
box-shadow: inset 1px 1px 1px #ccc;
border-radius: .12em;
border: none;
}
.ui-checkboxradio-radio-label .ui-icon-background {
width: 16px;
height: 16px;
border-radius: 1em;
overflow: visible;
border: none;
}
.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,
.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon {
background-image: none;
width: 8px;
height: 8px;
border-width: 4px;
border-style: solid;
}
.ui-checkboxradio-disabled {
pointer-events: none;
}
.ui-datepicker {
width: 17em;
padding: .2em .2em 0;
display: none;
}
.ui-datepicker .ui-datepicker-header {
position: relative;
padding: .2em 0;
}
.ui-datepicker .ui-datepicker-prev,
.ui-datepicker .ui-datepicker-next {
position: absolute;
top: 2px;
width: 1.8em;
height: 1.8em;
}
.ui-datepicker .ui-datepicker-prev-hover,
.ui-datepicker .ui-datepicker-next-hover {
top: 1px;
}
.ui-datepicker .ui-datepicker-prev {
left: 2px;
}
.ui-datepicker .ui-datepicker-next {
right: 2px;
}
.ui-datepicker .ui-datepicker-prev-hover {
left: 1px;
}
.ui-datepicker .ui-datepicker-next-hover {
right: 1px;
}
.ui-datepicker .ui-datepicker-prev span,
.ui-datepicker .ui-datepicker-next span {
display: block;
position: absolute;
left: 50%;
margin-left: -8px;
top: 50%;
margin-top: -8px;
}
.ui-datepicker .ui-datepicker-title {
margin: 0 2.3em;
line-height: 1.8em;
text-align: center;
}
.ui-datepicker .ui-datepicker-title select {
font-size: 1em;
margin: 1px 0;
}
.ui-datepicker select.ui-datepicker-month,
.ui-datepicker select.ui-datepicker-year {
width: 45%;
}
.ui-datepicker table {
width: 100%;
font-size: .9em;
border-collapse: collapse;
margin: 0 0 .4em;
}
.ui-datepicker th {
padding: .7em .3em;
text-align: center;
font-weight: bold;
border: 0;
}
.ui-datepicker td {
border: 0;
padding: 1px;
}
.ui-datepicker td span,
.ui-datepicker td a {
display: block;
padding: .2em;
text-align: right;
text-decoration: none;
}
.ui-datepicker .ui-datepicker-buttonpane {
background-image: none;
margin: .7em 0 0 0;
padding: 0 .2em;
border-left: 0;
border-right: 0;
border-bottom: 0;
}
.ui-datepicker .ui-datepicker-buttonpane button {
float: right;
margin: .5em .2em .4em;
cursor: pointer;
padding: .2em .6em .3em .6em;
width: auto;
overflow: visible;
}
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
float: left;
}
/* with multiple calendars */
.ui-datepicker.ui-datepicker-multi {
width: auto;
}
.ui-datepicker-multi .ui-datepicker-group {
float: left;
}
.ui-datepicker-multi .ui-datepicker-group table {
width: 95%;
margin: 0 auto .4em;
}
.ui-datepicker-multi-2 .ui-datepicker-group {
width: 50%;
}
.ui-datepicker-multi-3 .ui-datepicker-group {
width: 33.3%;
}
.ui-datepicker-multi-4 .ui-datepicker-group {
width: 25%;
}
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
border-left-width: 0;
}
.ui-datepicker-multi .ui-datepicker-buttonpane {
clear: left;
}
.ui-datepicker-row-break {
clear: both;
width: 100%;
font-size: 0;
}
/* RTL support */
.ui-datepicker-rtl {
direction: rtl;
}
.ui-datepicker-rtl .ui-datepicker-prev {
right: 2px;
left: auto;
}
.ui-datepicker-rtl .ui-datepicker-next {
left: 2px;
right: auto;
}
.ui-datepicker-rtl .ui-datepicker-prev:hover {
right: 1px;
left: auto;
}
.ui-datepicker-rtl .ui-datepicker-next:hover {
left: 1px;
right: auto;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane {
clear: right;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane button {
float: left;
}
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
.ui-datepicker-rtl .ui-datepicker-group {
float: right;
}
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
border-right-width: 0;
border-left-width: 1px;
}
/* Icons */
.ui-datepicker .ui-icon {
display: block;
text-indent: -99999px;
overflow: hidden;
background-repeat: no-repeat;
left: .5em;
top: .3em;
}
.ui-dialog {
position: absolute;
top: 0;
left: 0;
padding: .2em;
outline: 0;
}
.ui-dialog .ui-dialog-titlebar {
padding: .4em 1em;
position: relative;
}
.ui-dialog .ui-dialog-title {
float: left;
margin: .1em 0;
white-space: nowrap;
width: 90%;
overflow: hidden;
text-overflow: ellipsis;
}
.ui-dialog .ui-dialog-titlebar-close {
position: absolute;
right: .3em;
top: 50%;
width: 20px;
margin: -10px 0 0 0;
padding: 1px;
height: 20px;
}
.ui-dialog .ui-dialog-content {
position: relative;
border: 0;
padding: .5em 1em;
background: none;
overflow: auto;
}
.ui-dialog .ui-dialog-buttonpane {
text-align: left;
border-width: 1px 0 0 0;
background-image: none;
margin-top: .5em;
padding: .3em 1em .5em .4em;
}
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
float: right;
}
.ui-dialog .ui-dialog-buttonpane button {
margin: .5em .4em .5em 0;
cursor: pointer;
}
.ui-dialog .ui-resizable-n {
height: 2px;
top: 0;
}
.ui-dialog .ui-resizable-e {
width: 2px;
right: 0;
}
.ui-dialog .ui-resizable-s {
height: 2px;
bottom: 0;
}
.ui-dialog .ui-resizable-w {
width: 2px;
left: 0;
}
.ui-dialog .ui-resizable-se,
.ui-dialog .ui-resizable-sw,
.ui-dialog .ui-resizable-ne,
.ui-dialog .ui-resizable-nw {
width: 7px;
height: 7px;
}
.ui-dialog .ui-resizable-se {
right: 0;
bottom: 0;
}
.ui-dialog .ui-resizable-sw {
left: 0;
bottom: 0;
}
.ui-dialog .ui-resizable-ne {
right: 0;
top: 0;
}
.ui-dialog .ui-resizable-nw {
left: 0;
top: 0;
}
.ui-draggable .ui-dialog-titlebar {
cursor: move;
}
.ui-progressbar {
height: 2em;
text-align: left;
overflow: hidden;
}
.ui-progressbar .ui-progressbar-value {
margin: -1px;
height: 100%;
}
.ui-progressbar .ui-progressbar-overlay {
background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");
height: 100%;
-ms-filter: "alpha(opacity=25)"; /* support: IE8 */
opacity: 0.25;
}
.ui-progressbar-indeterminate .ui-progressbar-value {
background-image: none;
}
.ui-selectmenu-menu {
padding: 0;
margin: 0;
position: absolute;
top: 0;
left: 0;
display: none;
}
.ui-selectmenu-menu .ui-menu {
overflow: auto;
overflow-x: hidden;
padding-bottom: 1px;
}
.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup {
font-size: 1em;
font-weight: bold;
line-height: 1.5;
padding: 2px 0.4em;
margin: 0.5em 0 0 0;
height: auto;
border: 0;
}
.ui-selectmenu-open {
display: block;
}
.ui-selectmenu-text {
display: block;
margin-right: 20px;
overflow: hidden;
text-overflow: ellipsis;
}
.ui-selectmenu-button.ui-button {
text-align: left;
white-space: nowrap;
width: 14em;
}
.ui-selectmenu-icon.ui-icon {
float: right;
margin-top: 0;
}
.ui-slider {
position: relative;
text-align: left;
}
.ui-slider .ui-slider-handle {
position: absolute;
z-index: 2;
width: 1.2em;
height: 1.2em;
cursor: pointer;
-ms-touch-action: none;
touch-action: none;
}
.ui-slider .ui-slider-range {
position: absolute;
z-index: 1;
font-size: .7em;
display: block;
border: 0;
background-position: 0 0;
}
/* support: IE8 - See #6727 */
.ui-slider.ui-state-disabled .ui-slider-handle,
.ui-slider.ui-state-disabled .ui-slider-range {
filter: inherit;
}
.ui-slider-horizontal {
height: .8em;
}
.ui-slider-horizontal .ui-slider-handle {
top: -.3em;
margin-left: -.6em;
}
.ui-slider-horizontal .ui-slider-range {
top: 0;
height: 100%;
}
.ui-slider-horizontal .ui-slider-range-min {
left: 0;
}
.ui-slider-horizontal .ui-slider-range-max {
right: 0;
}
.ui-slider-vertical {
width: .8em;
height: 100px;
}
.ui-slider-vertical .ui-slider-handle {
left: -.3em;
margin-left: 0;
margin-bottom: -.6em;
}
.ui-slider-vertical .ui-slider-range {
left: 0;
width: 100%;
}
.ui-slider-vertical .ui-slider-range-min {
bottom: 0;
}
.ui-slider-vertical .ui-slider-range-max {
top: 0;
}
.ui-spinner {
position: relative;
display: inline-block;
overflow: hidden;
padding: 0;
vertical-align: middle;
}
.ui-spinner-input {
border: none;
background: none;
color: inherit;
padding: .222em 0;
margin: .2em 0;
vertical-align: middle;
margin-left: .4em;
margin-right: 2em;
}
.ui-spinner-button {
width: 1.6em;
height: 50%;
font-size: .5em;
padding: 0;
margin: 0;
text-align: center;
position: absolute;
cursor: default;
display: block;
overflow: hidden;
right: 0;
}
/* more specificity required here to override default borders */
.ui-spinner a.ui-spinner-button {
border-top-style: none;
border-bottom-style: none;
border-right-style: none;
}
.ui-spinner-up {
top: 0;
}
.ui-spinner-down {
bottom: 0;
}
.ui-tabs {
position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
padding: .2em;
}
.ui-tabs .ui-tabs-nav {
margin: 0;
padding: .2em .2em 0;
}
.ui-tabs .ui-tabs-nav li {
list-style: none;
float: left;
position: relative;
top: 0;
margin: 1px .2em 0 0;
border-bottom-width: 0;
padding: 0;
white-space: nowrap;
}
.ui-tabs .ui-tabs-nav .ui-tabs-anchor {
float: left;
padding: .5em 1em;
text-decoration: none;
}
.ui-tabs .ui-tabs-nav li.ui-tabs-active {
margin-bottom: -1px;
padding-bottom: 1px;
}
.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,
.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,
.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {
cursor: text;
}
.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor {
cursor: pointer;
}
.ui-tabs .ui-tabs-panel {
display: block;
border-width: 0;
padding: 1em 1.4em;
background: none;
}
.ui-tooltip {
padding: 8px;
position: absolute;
z-index: 9999;
max-width: 300px;
}
body .ui-tooltip {
border-width: 2px;
}

File diff suppressed because one or more lines are too long

View File

@ -1,446 +0,0 @@
/*!
* jQuery UI CSS Framework 1.13.3
* https://jqueryui.com
*
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license.
* https://jquery.org/license
*
* https://api.jqueryui.com/category/theming/
*
* To view and modify this theme, visit https://jqueryui.com/themeroller/?ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&fwDefault=normal&cornerRadius=3px&bgColorHeader=e9e9e9&bgTextureHeader=flat&borderColorHeader=dddddd&fcHeader=333333&iconColorHeader=444444&bgColorContent=ffffff&bgTextureContent=flat&borderColorContent=dddddd&fcContent=333333&iconColorContent=444444&bgColorDefault=f6f6f6&bgTextureDefault=flat&borderColorDefault=c5c5c5&fcDefault=454545&iconColorDefault=777777&bgColorHover=ededed&bgTextureHover=flat&borderColorHover=cccccc&fcHover=2b2b2b&iconColorHover=555555&bgColorActive=007fff&bgTextureActive=flat&borderColorActive=003eff&fcActive=ffffff&iconColorActive=ffffff&bgColorHighlight=fffa90&bgTextureHighlight=flat&borderColorHighlight=dad55e&fcHighlight=777620&iconColorHighlight=777620&bgColorError=fddfdf&bgTextureError=flat&borderColorError=f1a899&fcError=5f3f3f&iconColorError=cc0000&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=666666&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=5px&offsetTopShadow=0px&offsetLeftShadow=0px&cornerRadiusShadow=8px
*/
/* Component containers
----------------------------------*/
.ui-widget {
font-family: Arial,Helvetica,sans-serif;
font-size: 1em;
}
.ui-widget .ui-widget {
font-size: 1em;
}
.ui-widget input,
.ui-widget select,
.ui-widget textarea,
.ui-widget button {
font-family: Arial,Helvetica,sans-serif;
font-size: 1em;
}
.ui-widget.ui-widget-content {
border: 1px solid #c5c5c5;
}
.ui-widget-content {
border: 1px solid #dddddd;
background: #ffffff;
color: #333333;
}
.ui-widget-content a {
color: #333333;
}
.ui-widget-header {
border: 1px solid #dddddd;
background: #e9e9e9;
color: #333333;
font-weight: bold;
}
.ui-widget-header a {
color: #333333;
}
/* Interaction states
----------------------------------*/
.ui-state-default,
.ui-widget-content .ui-state-default,
.ui-widget-header .ui-state-default,
.ui-button,
/* We use html here because we need a greater specificity to make sure disabled
works properly when clicked or hovered */
html .ui-button.ui-state-disabled:hover,
html .ui-button.ui-state-disabled:active {
border: 1px solid #c5c5c5;
background: #f6f6f6;
font-weight: normal;
color: #454545;
}
.ui-state-default a,
.ui-state-default a:link,
.ui-state-default a:visited,
a.ui-button,
a:link.ui-button,
a:visited.ui-button,
.ui-button {
color: #454545;
text-decoration: none;
}
.ui-state-hover,
.ui-widget-content .ui-state-hover,
.ui-widget-header .ui-state-hover,
.ui-state-focus,
.ui-widget-content .ui-state-focus,
.ui-widget-header .ui-state-focus,
.ui-button:hover,
.ui-button:focus {
border: 1px solid #cccccc;
background: #ededed;
font-weight: normal;
color: #2b2b2b;
}
.ui-state-hover a,
.ui-state-hover a:hover,
.ui-state-hover a:link,
.ui-state-hover a:visited,
.ui-state-focus a,
.ui-state-focus a:hover,
.ui-state-focus a:link,
.ui-state-focus a:visited,
a.ui-button:hover,
a.ui-button:focus {
color: #2b2b2b;
text-decoration: none;
}
.ui-visual-focus {
box-shadow: 0 0 3px 1px rgb(94, 158, 214);
}
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active,
a.ui-button:active,
.ui-button:active,
.ui-button.ui-state-active:hover {
border: 1px solid #003eff;
background: #007fff;
font-weight: normal;
color: #ffffff;
}
.ui-icon-background,
.ui-state-active .ui-icon-background {
border: #003eff;
background-color: #ffffff;
}
.ui-state-active a,
.ui-state-active a:link,
.ui-state-active a:visited {
color: #ffffff;
text-decoration: none;
}
/* Interaction Cues
----------------------------------*/
.ui-state-highlight,
.ui-widget-content .ui-state-highlight,
.ui-widget-header .ui-state-highlight {
border: 1px solid #dad55e;
background: #fffa90;
color: #777620;
}
.ui-state-checked {
border: 1px solid #dad55e;
background: #fffa90;
}
.ui-state-highlight a,
.ui-widget-content .ui-state-highlight a,
.ui-widget-header .ui-state-highlight a {
color: #777620;
}
.ui-state-error,
.ui-widget-content .ui-state-error,
.ui-widget-header .ui-state-error {
border: 1px solid #f1a899;
background: #fddfdf;
color: #5f3f3f;
}
.ui-state-error a,
.ui-widget-content .ui-state-error a,
.ui-widget-header .ui-state-error a {
color: #5f3f3f;
}
.ui-state-error-text,
.ui-widget-content .ui-state-error-text,
.ui-widget-header .ui-state-error-text {
color: #5f3f3f;
}
.ui-priority-primary,
.ui-widget-content .ui-priority-primary,
.ui-widget-header .ui-priority-primary {
font-weight: bold;
}
.ui-priority-secondary,
.ui-widget-content .ui-priority-secondary,
.ui-widget-header .ui-priority-secondary {
opacity: .7;
-ms-filter: "alpha(opacity=70)"; /* support: IE8 */
font-weight: normal;
}
.ui-state-disabled,
.ui-widget-content .ui-state-disabled,
.ui-widget-header .ui-state-disabled {
opacity: .35;
-ms-filter: "alpha(opacity=35)"; /* support: IE8 */
background-image: none;
}
.ui-state-disabled .ui-icon {
-ms-filter: "alpha(opacity=35)"; /* support: IE8 - See #6059 */
}
/* Icons
----------------------------------*/
/* states and images */
.ui-icon {
width: 16px;
height: 16px;
}
.ui-icon,
.ui-widget-content .ui-icon {
background-image: url("images/ui-icons_444444_256x240.png");
}
.ui-widget-header .ui-icon {
background-image: url("images/ui-icons_444444_256x240.png");
}
.ui-state-hover .ui-icon,
.ui-state-focus .ui-icon,
.ui-button:hover .ui-icon,
.ui-button:focus .ui-icon {
background-image: url("images/ui-icons_555555_256x240.png");
}
.ui-state-active .ui-icon,
.ui-button:active .ui-icon {
background-image: url("images/ui-icons_ffffff_256x240.png");
}
.ui-state-highlight .ui-icon,
.ui-button .ui-state-highlight.ui-icon {
background-image: url("images/ui-icons_777620_256x240.png");
}
.ui-state-error .ui-icon,
.ui-state-error-text .ui-icon {
background-image: url("images/ui-icons_cc0000_256x240.png");
}
.ui-button .ui-icon {
background-image: url("images/ui-icons_777777_256x240.png");
}
/* positioning */
/* Three classes needed to override `.ui-button:hover .ui-icon` */
.ui-icon-blank.ui-icon-blank.ui-icon-blank {
background-image: none;
}
.ui-icon-caret-1-n { background-position: 0 0; }
.ui-icon-caret-1-ne { background-position: -16px 0; }
.ui-icon-caret-1-e { background-position: -32px 0; }
.ui-icon-caret-1-se { background-position: -48px 0; }
.ui-icon-caret-1-s { background-position: -65px 0; }
.ui-icon-caret-1-sw { background-position: -80px 0; }
.ui-icon-caret-1-w { background-position: -96px 0; }
.ui-icon-caret-1-nw { background-position: -112px 0; }
.ui-icon-caret-2-n-s { background-position: -128px 0; }
.ui-icon-caret-2-e-w { background-position: -144px 0; }
.ui-icon-triangle-1-n { background-position: 0 -16px; }
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
.ui-icon-triangle-1-e { background-position: -32px -16px; }
.ui-icon-triangle-1-se { background-position: -48px -16px; }
.ui-icon-triangle-1-s { background-position: -65px -16px; }
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
.ui-icon-triangle-1-w { background-position: -96px -16px; }
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
.ui-icon-arrow-1-n { background-position: 0 -32px; }
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
.ui-icon-arrow-1-e { background-position: -32px -32px; }
.ui-icon-arrow-1-se { background-position: -48px -32px; }
.ui-icon-arrow-1-s { background-position: -65px -32px; }
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
.ui-icon-arrow-1-w { background-position: -96px -32px; }
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
.ui-icon-arrowthick-1-n { background-position: 1px -48px; }
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
.ui-icon-arrow-4 { background-position: 0 -80px; }
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
.ui-icon-extlink { background-position: -32px -80px; }
.ui-icon-newwin { background-position: -48px -80px; }
.ui-icon-refresh { background-position: -64px -80px; }
.ui-icon-shuffle { background-position: -80px -80px; }
.ui-icon-transfer-e-w { background-position: -96px -80px; }
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
.ui-icon-folder-collapsed { background-position: 0 -96px; }
.ui-icon-folder-open { background-position: -16px -96px; }
.ui-icon-document { background-position: -32px -96px; }
.ui-icon-document-b { background-position: -48px -96px; }
.ui-icon-note { background-position: -64px -96px; }
.ui-icon-mail-closed { background-position: -80px -96px; }
.ui-icon-mail-open { background-position: -96px -96px; }
.ui-icon-suitcase { background-position: -112px -96px; }
.ui-icon-comment { background-position: -128px -96px; }
.ui-icon-person { background-position: -144px -96px; }
.ui-icon-print { background-position: -160px -96px; }
.ui-icon-trash { background-position: -176px -96px; }
.ui-icon-locked { background-position: -192px -96px; }
.ui-icon-unlocked { background-position: -208px -96px; }
.ui-icon-bookmark { background-position: -224px -96px; }
.ui-icon-tag { background-position: -240px -96px; }
.ui-icon-home { background-position: 0 -112px; }
.ui-icon-flag { background-position: -16px -112px; }
.ui-icon-calendar { background-position: -32px -112px; }
.ui-icon-cart { background-position: -48px -112px; }
.ui-icon-pencil { background-position: -64px -112px; }
.ui-icon-clock { background-position: -80px -112px; }
.ui-icon-disk { background-position: -96px -112px; }
.ui-icon-calculator { background-position: -112px -112px; }
.ui-icon-zoomin { background-position: -128px -112px; }
.ui-icon-zoomout { background-position: -144px -112px; }
.ui-icon-search { background-position: -160px -112px; }
.ui-icon-wrench { background-position: -176px -112px; }
.ui-icon-gear { background-position: -192px -112px; }
.ui-icon-heart { background-position: -208px -112px; }
.ui-icon-star { background-position: -224px -112px; }
.ui-icon-link { background-position: -240px -112px; }
.ui-icon-cancel { background-position: 0 -128px; }
.ui-icon-plus { background-position: -16px -128px; }
.ui-icon-plusthick { background-position: -32px -128px; }
.ui-icon-minus { background-position: -48px -128px; }
.ui-icon-minusthick { background-position: -64px -128px; }
.ui-icon-close { background-position: -80px -128px; }
.ui-icon-closethick { background-position: -96px -128px; }
.ui-icon-key { background-position: -112px -128px; }
.ui-icon-lightbulb { background-position: -128px -128px; }
.ui-icon-scissors { background-position: -144px -128px; }
.ui-icon-clipboard { background-position: -160px -128px; }
.ui-icon-copy { background-position: -176px -128px; }
.ui-icon-contact { background-position: -192px -128px; }
.ui-icon-image { background-position: -208px -128px; }
.ui-icon-video { background-position: -224px -128px; }
.ui-icon-script { background-position: -240px -128px; }
.ui-icon-alert { background-position: 0 -144px; }
.ui-icon-info { background-position: -16px -144px; }
.ui-icon-notice { background-position: -32px -144px; }
.ui-icon-help { background-position: -48px -144px; }
.ui-icon-check { background-position: -64px -144px; }
.ui-icon-bullet { background-position: -80px -144px; }
.ui-icon-radio-on { background-position: -96px -144px; }
.ui-icon-radio-off { background-position: -112px -144px; }
.ui-icon-pin-w { background-position: -128px -144px; }
.ui-icon-pin-s { background-position: -144px -144px; }
.ui-icon-play { background-position: 0 -160px; }
.ui-icon-pause { background-position: -16px -160px; }
.ui-icon-seek-next { background-position: -32px -160px; }
.ui-icon-seek-prev { background-position: -48px -160px; }
.ui-icon-seek-end { background-position: -64px -160px; }
.ui-icon-seek-start { background-position: -80px -160px; }
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
.ui-icon-seek-first { background-position: -80px -160px; }
.ui-icon-stop { background-position: -96px -160px; }
.ui-icon-eject { background-position: -112px -160px; }
.ui-icon-volume-off { background-position: -128px -160px; }
.ui-icon-volume-on { background-position: -144px -160px; }
.ui-icon-power { background-position: 0 -176px; }
.ui-icon-signal-diag { background-position: -16px -176px; }
.ui-icon-signal { background-position: -32px -176px; }
.ui-icon-battery-0 { background-position: -48px -176px; }
.ui-icon-battery-1 { background-position: -64px -176px; }
.ui-icon-battery-2 { background-position: -80px -176px; }
.ui-icon-battery-3 { background-position: -96px -176px; }
.ui-icon-circle-plus { background-position: 0 -192px; }
.ui-icon-circle-minus { background-position: -16px -192px; }
.ui-icon-circle-close { background-position: -32px -192px; }
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
.ui-icon-circle-zoomin { background-position: -176px -192px; }
.ui-icon-circle-zoomout { background-position: -192px -192px; }
.ui-icon-circle-check { background-position: -208px -192px; }
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
.ui-icon-circlesmall-close { background-position: -32px -208px; }
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
.ui-icon-squaresmall-close { background-position: -80px -208px; }
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
/* Misc visuals
----------------------------------*/
/* Corner radius */
.ui-corner-all,
.ui-corner-top,
.ui-corner-left,
.ui-corner-tl {
border-top-left-radius: 3px;
}
.ui-corner-all,
.ui-corner-top,
.ui-corner-right,
.ui-corner-tr {
border-top-right-radius: 3px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-left,
.ui-corner-bl {
border-bottom-left-radius: 3px;
}
.ui-corner-all,
.ui-corner-bottom,
.ui-corner-right,
.ui-corner-br {
border-bottom-right-radius: 3px;
}
/* Overlays */
.ui-widget-overlay {
background: #aaaaaa;
opacity: .3;
-ms-filter: "alpha(opacity=30)"; /* support: IE8 */
}
.ui-widget-shadow {
-webkit-box-shadow: 0px 0px 5px #666666;
box-shadow: 0px 0px 5px #666666;
}

File diff suppressed because one or more lines are too long

View File

@ -1,84 +0,0 @@
{
"name": "jquery-ui",
"title": "jQuery UI",
"description": "A curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library.",
"version": "1.13.3",
"homepage": "https://jqueryui.com",
"author": {
"name": "OpenJS Foundation and other contributors",
"url": "https://github.com/jquery/jquery-ui/blob/1.13.3/AUTHORS.txt"
},
"main": "ui/widget.js",
"maintainers": [
{
"name": "Jörn Zaefferer",
"email": "joern.zaefferer@gmail.com",
"url": "https://bassistance.de"
},
{
"name": "Mike Sherov",
"email": "mike.sherov@gmail.com",
"url": "https://mike.sherov.com"
},
{
"name": "TJ VanToll",
"email": "tj.vantoll@gmail.com",
"url": "https://www.tjvantoll.com"
},
{
"name": "Felix Nagel",
"email": "info@felixnagel.com",
"url": "https://www.felixnagel.com"
},
{
"name": "Alex Schmitz",
"email": "arschmitz@gmail.com",
"url": "https://github.com/arschmitz"
}
],
"repository": {
"type": "git",
"url": "git://github.com/jquery/jquery-ui.git"
},
"bugs": {
"url": "https://github.com/jquery/jquery-ui/issues"
},
"license": "MIT",
"scripts": {
"build": "grunt build",
"lint": "grunt lint",
"test:server": "node tests/runner/server.js",
"test:unit": "node tests/runner/command.js",
"test": "grunt && npm run test:unit -- -h"
},
"dependencies": {
"jquery": ">=1.8.0 <4.0.0"
},
"devDependencies": {
"body-parser": "1.20.2",
"browserstack-local": "1.5.5",
"commitplease": "3.2.0",
"diff": "5.2.0",
"eslint-config-jquery": "3.0.2",
"exit-hook": "4.0.0",
"express": "4.19.2",
"express-body-parser-error-handler": "1.0.7",
"grunt": "1.6.1",
"grunt-bowercopy": "1.2.5",
"grunt-cli": "1.4.3",
"grunt-compare-size": "0.4.2",
"grunt-contrib-concat": "2.1.0",
"grunt-contrib-csslint": "2.0.0",
"grunt-contrib-requirejs": "1.0.0",
"grunt-contrib-uglify": "5.2.2",
"grunt-eslint": "24.0.1",
"grunt-git-authors": "3.2.0",
"grunt-html": "16.0.0",
"load-grunt-tasks": "5.1.0",
"rimraf": "4.4.1",
"selenium-webdriver": "4.18.1",
"testswarm": "1.1.2",
"yargs": "17.7.2"
},
"keywords": []
}

View File

@ -17,7 +17,6 @@ import "virtual:svg-icons-register";
// 引入项目中的全部全局组件
import SvgIcon from "@/components/svgIcon.vue";
import library from "./fontawsomeIconRegister";
import "flag-icons/css/flag-icons.min.css";
/* import font awesome icon component */
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";

View File

@ -8,6 +8,7 @@ import AssetManagement from "@/views/AssetManagement/AssetManagement.vue";
import AlertManagement from "@/views/alert/AlertManagement.vue";
import ProductSetting from "@/views/productSetting/ProductSetting.vue";
import EnergyManagement from "@/views/energyManagement/EnergyManagement.vue";
import SettingManagement from "@/views/setting/SettingManagement.vue";
import Login from "@/views/login/Login.vue";
import useUserInfoStore from "@/stores/useUserInfoStore";
import useGetCookie from "@/hooks/useGetCookie";
@ -80,10 +81,15 @@ const router = createRouter({
component: ProductSetting,
},
{
path: "/energyManagement",
path: "/energyManagement/:main_system_id/:sub_system_id/:type",
name: "energyManagement",
component: EnergyManagement,
},
{
path: "/setting/:main_system_id/:sub_system_id/:type",
name: "setting",
component: SettingManagement,
},
{
path: "/mytestfile/mjm",
name: "mytestfile",
@ -99,22 +105,19 @@ router.beforeEach(async (to, from, next) => {
const authRequired = !publicPages.includes(to.path);
const auth = useUserInfoStore();
const token = useGetCookie("JWT-Authorization");
if (to.path === "/logout") {
document.cookie = "JWT-Authorization=";
auth.user.token = "";
window.location.reload();
next({ path: "/login" });
}
const user_name = useGetCookie("user_name");
if ((authRequired && !token) || to.path === "/") {
auth.user.token = "";
next({ path: "/login" });
} else if (!authRequired) {
document.cookie = "JWT-Authorization=";
document.cookie = "JWT-Authorization=; Max-Age=0";
document.cookie = "user_name=; Max-Age=0";
auth.user.token = "";
auth.user.user_name = "";
} else {
auth.user.token = token;
auth.user.user_name = user_name;
}
next();
});

View File

@ -1,17 +1,22 @@
import { defineStore } from "pinia";
import { ref, computed } from "vue";
import { ref, computed, watch } from "vue";
import { useRoute } from "vue-router";
import { getBuildings } from "@/apis/building";
import { getDashboard2D3D } from "@/apis/dashboard";
import { getAssetFloorList, getDepartmentList } from "@/apis/asset";
const useBuildingStore = defineStore("buildingInfo", () => {
// 所有棟別
// 狀態定義
const buildings = ref([]);
const selectedBuilding = ref(null);
// 所有大小類系統
const floorList = ref([]);
const deptList = ref([]);
const mainSubSys = ref([]);
// 控制顯示2D/3D切換與內容
const showForgeArea = ref(true);
const previewImageExt = ref("");
// 所有大類
// 計算屬性
const mainSys = computed(() =>
mainSubSys.value.map(({ main_system_tag, full_name }) => ({
main_system_tag,
@ -21,7 +26,6 @@ const useBuildingStore = defineStore("buildingInfo", () => {
const subSys = computed(() => {
let subPages = [];
mainSubSys.value.forEach(({ main_system_tag, history_Sub_systems }) => {
subPages = [
...subPages,
@ -32,7 +36,6 @@ const useBuildingStore = defineStore("buildingInfo", () => {
})),
];
});
return subPages;
});
@ -44,14 +47,89 @@ const useBuildingStore = defineStore("buildingInfo", () => {
return null;
});
// 獲取所有建築物
const fetchBuildings = async () => {
// const res = await getBuildings();
buildings.value = JSON.parse(localStorage.getItem("CviBuildingList")) || [];
const storedBuilding = JSON.parse(localStorage.getItem("CviBuilding"));
if (buildings.value.length > 0) {
selectedBuilding.value = storedBuilding || buildings.value[0]; // 預設選第一個建築
} else {
selectedBuilding.value = null; // 如果沒有建築物,清空選擇
}
};
// 獲取樓層資料
const fetchFloorList = async (building_guid) => {
const res = await getAssetFloorList(building_guid);
floorList.value =
res.data[0]?.floors.map((d) => ({
...d,
title: d.full_name,
key: d.floor_guid,
})) || [];
};
// 獲取部門資料
const fetchDepartmentList = async () => {
const res = await getDepartmentList();
deptList.value =
res.data.map((d) => ({
...d,
title: d.name,
key: d.id,
})) || [];
};
// 獲取2D、3D顯示與否
const fetchDashboard2D3D = async (BuildingId) => {
const res = await getDashboard2D3D(BuildingId);
showForgeArea.value = res.data.is3DEnabled;
previewImageExt.value = res.data.previewImageExt || "";
};
// 清除localStorage建築物
const deleteBuilding = () => {
localStorage.removeItem("CviBuildingList");
localStorage.removeItem("CviBuilding");
buildings.value = [];
selectedBuilding.value = null;
};
// 當 selectedBuilding 改變時,更新 floorList 和 deptList
watch(selectedBuilding, async (newBuilding) => {
if (newBuilding) {
localStorage.setItem("CviBuilding", JSON.stringify(newBuilding));
await Promise.all([
fetchFloorList(newBuilding.building_guid),
fetchDepartmentList(),
fetchDashboard2D3D(newBuilding.building_guid),
]);
}
});
// 初始化資料
const initialize = async () => {
await fetchBuildings();
};
return {
buildings,
selectedBuilding,
floorList,
deptList,
mainSubSys,
mainSys,
subSys,
selectedSystem,
showForgeArea,
previewImageExt,
deleteBuilding,
fetchBuildings,
fetchFloorList,
fetchDepartmentList,
fetchDashboard2D3D,
initialize,
};
});
export default useBuildingStore;

View File

@ -5,6 +5,7 @@ const useUserInfoStore = defineStore("userInfo", () => {
const user = ref({
token: "",
expires: 0,
user_name:"",
});
const auth_page = ref([]);

View File

@ -2,70 +2,80 @@ import useGetCookie from "@/hooks/useGetCookie";
import axios from "axios";
const BASEURL = import.meta.env.VITE_API_BASEURL;
// --- 請求攔截器的共用邏輯 ---
const requestInterceptor = (config) => {
// 確保 headers 物件存在
if (!config.headers) {
config.headers = {};
}
// 1. 取得並附加最新的 Token
const token = useGetCookie("JWT-Authorization");
if (token) {
// 正確做法:修改屬性,而不是覆蓋整個物件
config.headers.Authorization = `Bearer ${token}`;
}
// 2. 取得並附加選定的建築物 GUID
const storedBuilding = localStorage.getItem("CviBuilding");
if (storedBuilding) {
try {
const buildingObject = JSON.parse(storedBuilding);
if (buildingObject && buildingObject.building_guid) {
// 與後端約定好要用哪個標頭,這裡使用 'X-Building-GUID' 作為範例
config.headers["X-Building-GUID"] = buildingObject.building_guid;
}
} catch (e) {
console.error("解析 localStorage 中的 CviBuilding 失敗:", e);
}
}
return config;
};
const requestErrorInterceptor = (error) => {
return Promise.reject(error);
};
// --- 一般 API 實例 ---
const instance = axios.create({
baseURL: BASEURL,
timeout: -1,
headers: { Authorization: `Bearer ${useGetCookie("JWT-Authorization")}` },
timeout: 10000, // 建議設定超時
// 移除靜態 headers
});
// Add a request interceptor
instance.interceptors.request.use(
function (config) {
// Do something before request is sent
const token = useGetCookie("JWT-Authorization");
config.headers = {
Authorization: `Bearer ${token}`,
};
return config;
},
function (error) {
// Do something with request error
return Promise.reject(error);
}
);
// 使用共用的攔截器
instance.interceptors.request.use(requestInterceptor, requestErrorInterceptor);
// Add a response interceptor
instance.interceptors.response.use(
function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
const { status, data, headers } = response;
const { data } = response;
return {
...data,
};
return { ...data };
},
function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
if (response && response.status === 401) {
window.location.href = "/logout";
if (error.response && error.response.status === 401) {
window.location.href = "/";
}
return Promise.reject(error);
}
);
// --- 檔案處理 API 實例 ---
export const fileInstance = axios.create({
baseURL: BASEURL,
timeout: -1,
headers: { Authorization: `Bearer ${useGetCookie("JWT-Authorization")}` },
// 移除靜態 headers
});
// Add a request interceptor
fileInstance.interceptors.request.use(
function (config) {
// Do something before request is sent
const token = useGetCookie("JWT-Authorization");
config.headers = {
Authorization: `Bearer ${token}`,
};
return config;
},
function (error) {
// Do something with request error
return Promise.reject(error);
}
);
// 使用共用的攔截器
fileInstance.interceptors.request.use(requestInterceptor, requestErrorInterceptor);
// Add a response interceptor
fileInstance.interceptors.response.use(

View File

@ -1,12 +1,64 @@
<script setup>
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";
import { getOperationCompanyList } from "@/apis/operation";
import { getIOTSchema, getElecTypeList } from "@/apis/asset";
import useSearchParam from "@/hooks/useSearchParam";
import useBuildingStore from "@/stores/useBuildingStore";
const storeBuild = useBuildingStore();
const { searchParams, changeParams } = useSearchParam();
const companyOptions = ref([]);
const iotSchemaOptions = ref([]);
const elecTypeOptions = ref([]);
const getCompany = async () => {
const res = await getOperationCompanyList();
companyOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
};
const getIOTSchemaOptions = async (id) => {
const res = await getIOTSchema(Number(id));
iotSchemaOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
};
const getElecType = async () => {
const res = await getElecTypeList();
elecTypeOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
};
const departmentList = computed(() => storeBuild.deptList);
const floors = computed(() => storeBuild.floorList);
onMounted(() => {
getCompany();
getElecType();
});
watch(
() => searchParams.value.subSys_id,
(newValue) => {
if (newValue) {
getIOTSchemaOptions(newValue);
}
},
{
immediate: true,
}
);
provide("asset_modal_options", {
companyOptions,
iotSchemaOptions,
elecTypeOptions,
departmentList,
floors,
});
</script>
<template>
<h1 class="text-2xl font-extrabold mb-2">
{{ $t("assetManagement.title") }}
</h1>
<AssetMainList />
<AssetSubList />
<AssetTable />
</template>

View File

@ -0,0 +1,134 @@
<script setup>
import { getAssetMainList, deleteAssetMainItem } from "@/apis/asset";
import AssetMainListAddModal from "./AssetMainListAddModal.vue";
import { ref, onMounted, watch, inject } from "vue";
import useSearchParam from "@/hooks/useSearchParam";
import useActiveBtn from "@/hooks/useActiveBtn";
import { useI18n } from "vue-i18n";
import useBuildingStore from "@/stores/useBuildingStore";
const store = useBuildingStore();
const { t } = useI18n();
const { searchParams, changeParams } = useSearchParam();
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
const { openToast, cancelToastOpen } = inject("app_toast");
const isEditMode = ref(false);
const formState = ref({
id: 0,
system_key: "",
system_value: "",
});
const getMainSystems = async () => {
const res = await getAssetMainList(store.selectedBuilding.building_guid);
const cate = res.data.map((d, index) => ({
...d,
title: d.system_key,
key: d.id,
active: searchParams.value?.mainSys_id
? parseInt(searchParams.value?.mainSys_id) === d.id
: index === 0,
}));
setItems(cate);
};
const openModal = () => {
asset_add_main_item.showModal();
};
const onCancel = () => {
formState.value = {
id: 0,
system_key: "",
system_value: "",
};
asset_add_main_item.close();
};
const edit = (item) => {
formState.value = { ...item };
openModal();
};
const deleteItem = async (id) => {
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
await cancelToastOpen();
const res = await deleteAssetMainItem(id);
if (res.isSuccess) {
openToast("success", t("msg.delete_success"));
getMainSystems();
} else {
openToast("error", res.msg);
}
});
};
watch(
() => store.selectedBuilding,
(newBuilding) => {
if (newBuilding) {
getMainSystems();
}
},
{ immediate: true }
);
watch(selectedBtn, (newValue) => {
changeParams({
mainSys_id: newValue.key,
subSys_id: null,
});
});
</script>
<template>
<div class="flex items-center gap-4 mb-4">
<h2 class="text-lg font-bold ps-2 whitespace-nowrap">
{{ $t("history.system_category") }} :
</h2>
<AssetMainListAddModal
:openModal="openModal"
:onCancel="onCancel"
:getData="getMainSystems"
:formState="formState"
/>
<!-- <button
@click.stop.prevent="isEditMode = !isEditMode"
class="btn btn-sm btn-outline-success"
>
{{ isEditMode ? t("button.stop_edit") : t("button.start_edit") }}
</button> -->
</div>
<ButtonConnectedGroup
:items="items"
:onclick="
(e, item) => {
if (!isEditMode) {
changeActiveBtn(item);
}
}
"
:className="`flex w-full mt-2`"
size="sm"
color="info"
>
<template #buttonContent="{ item }">
<span class="text-base">{{ item.title }}</span>
<template v-if="isEditMode">
<span
class="ml-2 text-base text-warning"
@click.stop.prevent="() => edit(item)"
>
<FontAwesomeIcon :icon="['fas', 'pencil-alt']"></FontAwesomeIcon>
</span>
<span
class="text-base mx-1 text-error"
@click.stop.prevent="() => deleteItem(item.id)"
>
<FontAwesomeIcon :icon="['fas', 'trash-alt']" />
</span>
</template>
</template>
</ButtonConnectedGroup>
</template>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,106 @@
<script setup>
import { ref, defineProps, inject } from "vue";
import * as yup from "yup";
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
import { postAssetMainList } from "@/apis/asset";
import useBuildingStore from "@/stores/useBuildingStore";
import useSearchParam from "@/hooks/useSearchParam";
import { useI18n } from "vue-i18n";
const storeBuild = useBuildingStore();
const { t } = useI18n();
const { openToast } = inject("app_toast");
const { searchParams, changeParams } = useSearchParam();
const props = defineProps({
openModal: Function,
onCancel: Function,
getData: Function,
formState: Object,
isEdit: Boolean,
});
let subSysSchema = yup.object({
system_key: yup.string().required(t("button.required")), //
system_value: yup.string().required(t("button.required")), //
});
const { formErrorMsg, handleSubmit, handleErrorReset } =
useFormErrorMessage(subSysSchema);
const onOk = async () => {
const value = await handleSubmit(subSysSchema, props.formState);
const res = await postAssetMainList({
...props.formState,
id: props.formState ? props.formState.id : 0,
building_guid:storeBuild.selectedBuilding?.building_guid || null,
});
if (res.isSuccess) {
props.getData();
onReset();
} else {
openToast("error", res.msg);
}
};
const onReset = () => {
handleErrorReset(); //
props.onCancel(); // 調 onCancel
};
</script>
<template>
<!-- <button class="btn btn-sm btn-success" @click.stop.prevent="openModal">
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
</button> -->
<Modal
id="asset_add_main_item"
:title="
props.formState?.id
? t('assetManagement.edit_system_category')
: t('assetManagement.add_system_category')
"
:onCancel="onReset"
:width="300"
>
<template #modalContent>
<form ref="form" class="mt-5 flex flex-col items-center">
<Input name="system_key" :value="formState">
<template #topLeft>{{ $t("assetManagement.system_name") }}</template>
<template #bottomLeft
><span class="text-error text-base">
{{ formErrorMsg.system_key }}
</span></template
>
</Input>
<Input name="system_value" :value="formState" :readonly="Boolean(props.formState?.id)">
<template #topLeft>{{ $t("assetManagement.system_value") }}</template>
<template #bottomLeft
><span class="text-error text-base">
{{ formErrorMsg.system_value }}
</span></template
>
</Input>
</form>
</template>
<template #modalAction>
<button
type="reset"
class="btn btn-outline-success mr-2"
@click.prevent="onReset"
>
{{ $t("button.cancel") }}
</button>
<button
type="submit"
class="btn btn-outline-success"
@click.prevent="onOk"
>
{{ $t("button.submit") }}
</button>
</template>
</Modal>
</template>
<style lang="scss" scoped></style>

View File

@ -1,15 +1,17 @@
<script setup>
import { getAssetSubList, deleteAssetSubItem } from "@/apis/asset";
import { ref, onMounted, watch } from "vue";
import { ref, onMounted, watch, inject } from "vue";
import useSearchParam from "@/hooks/useSearchParam";
import useActiveBtn from "@/hooks/useActiveBtn";
import AssetSubListAddModal from "./AssetSubListAddModal.vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const { searchParams, changeParams } = useSearchParam();
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
const getSubSystems = async () => {
const res = await getAssetSubList();
const { openToast, cancelToastOpen } = inject("app_toast");
const isEditMode = ref(false);
const getSubSystems = async (id) => {
const res = await getAssetSubList(id);
const sub = res.data.map((d, index) => ({
...d,
title: d.system_key,
@ -21,60 +23,115 @@ const getSubSystems = async () => {
setItems(sub);
};
onMounted(() => {
getSubSystems();
});
onMounted(() => {});
watch(selectedBtn, (newValue) => {
changeParams({ subSys_id: newValue.key });
if (newValue && newValue.key) {
changeParams({
...searchParams.value,
subSys_id: newValue.key,
});
}
});
const editRecord = ref(null);
// modal
const openModal = () => {
asset_add_sub_item.showModal();
};
const onCancel = () => {
editRecord.value = null;
asset_add_sub_item.close();
};
watch(
() => searchParams,
(newValue) => {
if (newValue.value.mainSys_id && !newValue.value.subSys_id) {
getSubSystems(parseInt(newValue.value.mainSys_id));
}
},
{
deep: true,
}
);
const formState = ref({
id: 0,
system_key: "",
system_value: "",
system_parent_id: 0,
file: [],
});
const edit = (item) => {
editRecord.value = item;
openModal();
const openModal = (item) => {
if (item.id) {
formState.value = { ...item };
if (item.device_image) {
const subFile = item
? {
name: item.device_image,
src: item.device_image,
ext: item.device_image?.split(".")[1],
}
: {};
formState.value.file = [subFile];
}
} else {
formState.value = {
id: 0,
system_key: "",
system_value: "",
system_parent_id: 0,
file: [],
};
}
asset_add_sub_item.showModal();
};
const deleteItem = async (id) => {
deleteAssetSubItem(id);
getSubSystems();
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
await cancelToastOpen();
const res = await deleteAssetSubItem(id);
if (res.isSuccess) {
openToast("success", t("msg.delete_success"));
getSubSystems(parseInt(searchParams.value.mainSys_id));
} else {
openToast("error", res.msg);
}
});
};
</script>
<template>
<div class="mt-4">
<!-- <AssetSubListAddModal
:openModal="openModal"
:onCancel="onCancel"
:getData="getSubList"
:editRecord="editRecord"
/> -->
<div class="flex items-center gap-4 mb-4">
<h2 class="text-lg font-bold ps-2 whitespace-nowrap">
{{ $t("history.device_category") }} :
</h2>
<AssetSubListAddModal
:openModal="openModal"
:getData="getSubSystems"
:formState="formState"
/>
<button
@click.stop.prevent="isEditMode = !isEditMode"
class="btn btn-sm btn-outline-success mr-3"
>
{{ isEditMode ? t("button.stop_edit") : t("button.start_edit") }}
</button>
</div>
<ButtonConnectedGroup
:items="items"
:onclick="
(e, item) => {
changeActiveBtn(item);
if (!isEditMode) {
changeActiveBtn(item);
}
}
"
:className="`flex w-full mt-4`"
:className="`flex w-full mt-2`"
size="sm"
color="info"
>
<template #buttonContent="{ item }">
<span class="text-base">{{ item.title }}</span>
<!-- <template v-if="!item.is_IOT">
<template v-if="isEditMode">
<span
class="ml-2 text-base text-warning"
@click.stop.prevent="() => edit(item)"
@click.stop.prevent="() => openModal(item)"
>
<FontAwesomeIcon :icon="['fas', 'pencil-alt']"></FontAwesomeIcon>
</span>
@ -84,7 +141,7 @@ const deleteItem = async (id) => {
>
<FontAwesomeIcon :icon="['fas', 'trash-alt']" />
</span>
</template> -->
</template>
</template>
</ButtonConnectedGroup>
</div>

View File

@ -2,19 +2,28 @@
import { ref, defineProps, onMounted, watch } from "vue";
import * as yup from "yup";
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
import useSearchParam from "@/hooks/useSearchParam";
import { getAssetMainList, postAssetSubList } from "@/apis/asset";
import { useI18n } from "vue-i18n";
import useBuildingStore from "@/stores/useBuildingStore";
const store = useBuildingStore();
const { t } = useI18n();
const { searchParams, changeParams } = useSearchParam();
const props = defineProps({
openModal: Function,
onCancel: Function,
getData: Function,
editRecord: Object,
formState: Object,
});
const form = ref(null);
const mainSystem = ref([]);
const updateFileList = (files) => {
console.log("file", files);
props.formState.file = files;
};
const getMainSystems = async () => {
const res = await getAssetMainList();
const res = await getAssetMainList(store.selectedBuilding.building_guid);
mainSystem.value = res.data.map((d) => ({ ...d, key: d.id }));
};
@ -22,66 +31,72 @@ let subSysSchema = yup.object({
system_key: yup.string().required(t("button.required")), //
system_value: yup.string().required(t("button.required")), //
system_parent_id: yup.number().required(t("button.required")), // id
file: yup.array(),
});
const { formErrorMsg, handleSubmit, handleErrorReset } =
useFormErrorMessage(subSysSchema);
const formState = ref({
system_key: "",
system_value: "",
system_parent_id: 0,
});
const resetForm = () => {
formState.value = {
const onCancel = () => {
props.formState = {
id: 0,
system_key: "",
system_value: "",
system_parent_id: 0,
file: [],
};
asset_add_sub_item.close();
updateFileList([]);
handleErrorReset();
};
onMounted(() => {
getMainSystems();
});
watch(
() => props.editRecord,
(newValue) => {
if (newValue) {
formState.value = newValue;
} else {
resetForm();
() => store.selectedBuilding,
(newBuilding) => {
if (newBuilding) {
getMainSystems();
}
}
},
{ immediate: true }
);
const onOk = async () => {
//
const value = await handleSubmit(subSysSchema, formState.value);
const value = await handleSubmit(subSysSchema, props.formState);
console.log("props.formState", props.formState);
const res = await postAssetSubList({
...formState.value,
id: props.editRecord ? props.editRecord.id : 0,
});
const formData = new FormData(form.value);
formData.delete("file");
formData.append("id", props.formState.id);
if (props.formState.file[0]) {
formData.append("file", props.formState.file[0]);
}
if (props.formState.Device_image) {
formData.append("Device_image", props.formState.Device_image);
}
const res = await postAssetSubList(formData);
if (res.isSuccess) {
props.getData();
props.onCancel();
props.getData(parseInt(searchParams.value.mainSys_id));
onCancel();
}
};
</script>
<template>
<button class="btn btn-sm btn-success mr-3" @click.stop.prevent="openModal">
<button class="btn btn-sm btn-success" @click.stop.prevent="openModal">
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
</button>
<Modal
id="asset_add_sub_item"
:title="t('assetManagement.add_category')"
:open="open"
:title="
props.formState?.id
? t('assetManagement.edit_device_category')
: t('assetManagement.add_device_category')
"
:onCancel="onCancel"
width="300"
:width="300"
>
<template #modalContent>
<form ref="form" class="mt-5 flex flex-col items-center">
@ -93,7 +108,11 @@ const onOk = async () => {
</span></template
>
</Input>
<Input name="system_value" :value="formState">
<Input
name="system_value"
:value="formState"
:readonly="Boolean(props.formState?.id)"
>
<template #topLeft>{{ $t("assetManagement.system_value") }}</template>
<template #bottomLeft
><span class="text-error text-base">
@ -104,10 +123,11 @@ const onOk = async () => {
<Select
:options="mainSystem"
Attribute="system_key"
class="mr-5"
class=""
name="system_parent_id"
:value="formState"
selectClass="border-info focus-within:border-info"
:disabled="Boolean(props.formState?.id)"
>
<template #topLeft>{{
$t("assetManagement.system_parent")
@ -118,6 +138,14 @@ const onOk = async () => {
</span></template
>
</Select>
<Upload
name="file"
:fileList="formState.file || []"
:getFileList="updateFileList"
:multiple="false"
>
<template #topLeft>{{ $t("operation.upload_file") }}</template>
</Upload>
</form>
</template>
<template #modalAction>

View File

@ -4,48 +4,39 @@ import { onMounted, ref, watch, inject, provide, computed } from "vue";
import useSearchParam from "@/hooks/useSearchParam";
import AssetTableAddModal from "./AssetTableAddModal.vue";
import { getOperationCompanyList } from "@/apis/operation";
import { getAssetFloorList } from "@/apis/asset";
import { postMQTTRefresh } from "@/apis/alert";
import dayjs from "dayjs";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const { openToast } = inject("app_toast");
const { openToast, cancelToastOpen } = inject("app_toast");
const { companyOptions, departmentList, floors } = inject(
"asset_modal_options"
);
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const { 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
const res = await getAssetList(searchParams.value?.subSys_id);
if (res.isSuccess) {
// device_coordinate
res.data.forEach(d => {
res.data.forEach((d) => {
const floorGuid = d.floor_guid;
if (!totalCoordinates.value[floorGuid]) {
totalCoordinates.value[floorGuid] = [];
}
totalCoordinates.value[floorGuid].push(d.device_coordinate);
});
tableData.value = res.data.map((d) => ({
...d,
key: d.id,
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,18 +47,25 @@ const getAssetData = async () => {
}
};
const refreshMQTT = async () => {
const res = await postMQTTRefresh();
if (res.isSuccess) {
openToast("success", t("msg.mqtt_refresh"));
} else {
openToast("error", res.msg, "#outliers_add_table_item");
}
};
onMounted(async () => {
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",
@ -84,6 +82,11 @@ const columns = computed(() => [
filter: true,
sort: true,
},
{
title: t("assetManagement.department"),
key: "department",
filter: true,
},
{
title: t("assetManagement.device_coordinate"),
key: "device_coordinate",
@ -122,6 +125,8 @@ watch(
(newValue) => {
if (newValue.value?.subSys_id) {
getAssetData();
} else {
tableData.value = [];
}
},
{
@ -164,10 +169,16 @@ const edit = async (id) => {
};
const remove = async (id) => {
const res = await deleteAssetItem(id);
if (res.isSuccess) {
getAssetData();
}
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
await cancelToastOpen();
const res = await deleteAssetItem(id);
if (res.isSuccess) {
getAssetData();
openToast("success", t("msg.delete_success"));
} else {
openToast("error", res.msg);
}
});
};
provide("asset_table_data", {
@ -176,14 +187,21 @@ provide("asset_table_data", {
</script>
<template>
<div class="flex justify-start items-center mt-10">
<h3 class="text-xl mr-5">{{ $t("assetManagement.device_list") }}</h3>
<AssetTableAddModal
:openModal="openModal"
:onCancel="onCancel"
:editRecord="editRecord"
:getData="getAssetData"
/>
<div class="flex justify-between items-center mt-10">
<div class="flex">
<h3 class="text-xl mr-5">{{ $t("assetManagement.device_list") }}</h3>
<AssetTableAddModal
:openModal="openModal"
:onCancel="onCancel"
:editRecord="editRecord"
:getData="getAssetData"
/>
</div>
<button class="btn btn-sm btn-add" @click.prevent="refreshMQTT">
<font-awesome-icon :icon="['fas', 'cog']" />{{
$t("alert.reorganization")
}}
</button>
</div>
<Table :columns="columns" :dataSource="tableData" class="mt-3">
<template #bodyCell="{ record, column, index }">
@ -197,7 +215,7 @@ provide("asset_table_data", {
<template v-else-if="column.key === 'oriFile'">
<span v-if="record.oriFile.length === 0"></span>
<template v-else>
<span class="flex">
<span class="flex flex-wrap gap-1">
<a
v-for="file in record.oriFile"
:href="`${FILE_BASEURL}/${file?.file_url}`"

View File

@ -102,19 +102,18 @@ const closeModal = () => {
</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="openModal" :disabled="!searchParams.subSys_id">
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
</button>
<Modal
id="asset_add_table_item"
:title="editRecord?.main_id ? $t('assetManagement.edit_device') : $t('assetManagement.add_device')"
:open="open"
:onCancel="closeModal"
width="1600"
:width="1600"
>
<template #modalContent>
<form ref="form" class="grid grid-cols-5 gap-5">
<div class="grid grid-cols-2 col-span-2">
<div class="grid grid-cols-2 col-span-2 items-end">
<AssetTableModalLeft :current_component_key="current_component_key" />
</div>
<div class="col-span-3">

View File

@ -2,21 +2,19 @@
import { ref, inject, onBeforeMount, onMounted, watch } from "vue";
import * as yup from "yup";
import "yup-phone-lite";
import AssetTableModalLeftInfoIoT from "./AssetTableModalLeftInfoIoT.vue";
import AssetTableModalLeftInfoGraph from "./AssetTableModalLeftInfoGraph.vue";
import { getOperationCompanyList } from "@/apis/operation";
import useSearchParam from "@/hooks/useSearchParam";
import OperationTableModal from "@/views/operation/components/OperationTableModal.vue";
import AssetTableModalLeftInfoMQTT from "./AssetTableModalLeftInfoMQTT.vue";
import useUserInfoStore from "@/stores/useUserInfoStore";
import dayjs from "dayjs";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const { searchParams, changeParams } = useSearchParam();
const { updateLeftFields, formErrorMsg, formState } = inject(
"asset_table_modal_form"
);
const { companyOptions, iotSchemaOptions, elecTypeOptions, departmentList } = inject("asset_modal_options");
const store = useUserInfoStore();
let schema = {
full_name: yup.string().nullable(true),
operate_text: yup.string().nullable(true),
@ -28,6 +26,15 @@ let schema = {
.number()
.transform((value) => (Number.isNaN(value) ? null : value))
.nullable(true),
response_schema_id: yup
.number()
.transform((value) => (Number.isNaN(value) ? null : value))
.nullable(true),
department_id: yup
.number()
.transform((value) => (Number.isNaN(value) ? null : value))
.nullable(true),
topic: yup.string().nullable(true),
asset_number: yup.string().nullable(true),
sub_device: yup.array().nullable(true),
oriFile: yup.array().nullable(true),
@ -41,7 +48,11 @@ onBeforeMount(() => {
brand: "",
device_model: "",
operation_id: 0,
response_schema_id: 0,
department_id: 0,
elec_type_id: null,
asset_number: "",
topic: "",
sub_device: [],
oriFile: [],
buying_date: "",
@ -80,28 +91,20 @@ watch(formState, (newValue) => {
}
});
const updateFileList = (files) => {
formState.value = { ...formState.value, oriFile: files };
};
const companyOptions = ref([]);
const getCompany = async () => {
const res = await getOperationCompanyList();
companyOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
};
onMounted(() => {
getCompany();
});
const openCompanyAddModal = () => {
changeParams({ ...searchParams.value, work_type: 3 }); // company Add modal
operation_action_item.showModal();
};
watch(
() => iotSchemaOptions.value,
(newVal) => {
if (newVal && newVal.length > 0) {
formState.value.response_schema_id = newVal[0].id;
}
},
{ immediate: true }
);
</script>
<template>
<!-- information -->
<Input :value="formState" width="270" name="full_name">
<Input :value="formState" width="290" name="full_name">
<template #topLeft>{{ $t("assetManagement.device_name") }}</template>
<template #bottomLeft
><span class="text-error text-base">
@ -109,15 +112,23 @@ const openCompanyAddModal = () => {
</span></template
></Input
>
<Input :value="formState" width="270" name="operate_text">
<template #topLeft>Mac</template>
<template #bottomLeft
><span class="text-error text-base">
{{ formErrorMsg.operate_text }}
</span></template
></Input
<div class="flex items-center w-72">
<Select
:value="formState"
selectClass="border-info focus-within:border-info"
name="department_id"
Attribute="name"
:options="departmentList"
>
<template #topLeft>{{ $t("assetManagement.department") }}</template>
</Select>
</div>
<Input
:value="formState"
width="290"
name="device_number"
v-if="store.user.user_name == 'webUser'"
>
<Input :value="formState" width="270" name="device_number">
<template #topLeft
>Tag_Name ({{ $t("assetManagement.fill_text") }})</template
>
@ -127,28 +138,31 @@ const openCompanyAddModal = () => {
</span></template
></Input
>
<!-- <Input :value="formState" width="270" name="floor_guid">
<template #topLeft>設備位置樓層 / 區域</template>
<template #bottomLeft
><span class="text-error text-base">
{{ formErrorMsg.floor_guid }}
</span></template
></Input
> -->
<Input
:value="formState"
width="270"
name="device_coordinate"
:disabled="true"
>
<template #topLeft>{{ $t("assetManagement.device_coordinate") }}</template>
<template #bottomLeft
><span class="text-error text-base">
{{ formErrorMsg.device_coordinate }}
</span></template
></Input
>
<Input :value="formState" width="270" name="asset_number">
<div class="flex items-center w-72">
<Select
:value="formState"
selectClass="border-info focus-within:border-info"
name="response_schema_id"
Attribute="name"
:options="iotSchemaOptions"
:required="true"
>
<template #topLeft>IoT</template>
</Select>
</div>
<div class="flex items-center w-72" v-if="searchParams.mainSys_id==26">
<Select
:value="formState"
selectClass="border-info focus-within:border-info"
name="elec_type_id"
Attribute="name"
:options="elecTypeOptions"
:required="true"
>
<template #topLeft>{{$t("energy.electricity_classification")}}</template>
</Select>
</div>
<Input :value="formState" width="290" name="asset_number">
<template #topLeft>{{ $t("assetManagement.asset_number") }}</template>
<template #bottomLeft
><span class="text-error text-base">
@ -156,7 +170,7 @@ const openCompanyAddModal = () => {
</span></template
></Input
>
<DateGroup :items="buying_date" width="270" :withLine="false">
<DateGroup :items="buying_date" width="290" :withLine="false">
<template #topLeft>{{ $t("assetManagement.buying_date") }}</template>
<template #bottomLeft
><span class="text-error text-base">
@ -164,7 +178,7 @@ const openCompanyAddModal = () => {
</span></template
>
</DateGroup>
<Input :value="formState" width="270" name="brand">
<Input :value="formState" width="290" name="brand">
<template #topLeft>{{ $t("assetManagement.brand") }}</template>
<template #bottomLeft
><span class="text-error text-base">
@ -172,7 +186,7 @@ const openCompanyAddModal = () => {
</span></template
></Input
>
<Input :value="formState" width="270" name="device_model">
<Input :value="formState" width="290" name="device_model">
<template #topLeft>{{ $t("assetManagement.modal") }}</template>
<template #bottomLeft
><span class="text-error text-base">
@ -180,7 +194,7 @@ const openCompanyAddModal = () => {
</span></template
></Input
>
<div class="flex items-center col-span-2">
<div class="flex items-center w-72">
<Select
:value="formState"
selectClass="border-info focus-within:border-info"
@ -191,28 +205,8 @@ const openCompanyAddModal = () => {
>
<template #topLeft>{{ $t("assetManagement.company") }}</template>
</Select>
<OperationTableModal type="asset" />
<button
type="button"
class="btn btn-add ml-2 mt-7"
@click="openCompanyAddModal"
>
<font-awesome-icon :icon="['fas', 'plus']" />
{{ $t("button.add") }}
</button>
</div>
<AssetTableModalLeftInfoGraph />
<!-- <Upload
name="oriFile"
:fileList="formState.oriFile"
:getFileList="updateFileList"
:multiple="true"
class="col-span-2"
:baseUrl="FILE_BASEURL"
>
<template #topLeft>{{ $t("assetManagement.oriFile") }}</template>
</Upload> -->
<AssetTableModalLeftInfoIoT />
<AssetTableModalLeftInfoMQTT />
</template>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,161 @@
<script setup>
import { onMounted, ref, inject, onBeforeMount, watch, computed } from "vue";
import {
getDepartmentList,
postDepartmentList,
deleteDepartmentItem,
} from "@/apis/asset";
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
import * as yup from "yup";
import { twMerge } from "tailwind-merge";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const { openToast, cancelToastOpen } = inject("app_toast");
const { formState } = inject("asset_table_modal_form");
const selectedOption = ref("add");
const DeptFormState = ref({ id: 0, name: "" });
const departmentList = ref([]);
const getDepartment = async () => {
const res = await getDepartmentList();
departmentList.value = res.data.map((d) => ({ ...d, key: d.id }));
};
onMounted(() => {
getDepartment();
});
// modal
const openModal = () => {
if (selectedOption.value === "add") {
DeptFormState.value = {
id: 0,
name: "",
};
} else if (selectedOption.value === "edit") {
const dept = departmentList.value.find(
(d) => d.id === formState.value.department_id
);
if (dept) {
DeptFormState.value = {
id: dept.id,
name: dept.name,
};
}
}
asset_add_dept.showModal();
};
const form = ref(null);
const deptScheme = yup.object({
name: yup.string().required(t("button.required")),
});
const { formErrorMsg, handleSubmit, handleErrorReset, updateScheme } =
useFormErrorMessage(deptScheme);
const onOk = async () => {
const value = await handleSubmit(deptScheme, DeptFormState.value);
const res = await postDepartmentList(value);
if (res.isSuccess) {
getDepartment();
onCancel();
}
};
const onDelete = async () => {
openToast(
"warning",
t("msg.sure_to_delete"),
"#asset_add_table_item",
async () => {
await cancelToastOpen();
const res = await deleteDepartmentItem(formState.value.department_id);
if (res.isSuccess) {
getDepartment();
openToast("success", t("msg.delete_success"), "#asset_add_table_item");
} else {
openToast("error", res.msg, "#asset_add_table_item");
}
}
);
};
const onCancel = () => {
DeptFormState.value = {
id: 0,
name: "",
};
asset_add_dept.close();
};
</script>
<template>
<div className="join w-72 mb-4">
<Select
:value="formState"
selectClass="border-info focus-within:border-info rounded-r-none"
name="department_id"
Attribute="name"
:options="departmentList"
:isBottomLabelExist="false"
>
<template #topLeft>{{ $t("assetManagement.department") }}</template>
</Select>
<select
v-model="selectedOption"
className="select border-info focus-within:border-info join-item mt-11"
>
<option value="add" selected>{{ $t("button.add") }}</option>
<option value="edit">{{ $t("button.edit") }}</option>
<option value="delete">{{ $t("button.delete") }}</option>
</select>
<button
type="button"
class="btn btn-success join-item mt-11"
@click="selectedOption === 'delete' ? onDelete() : openModal()"
:aria-label="$t('button.submit')"
>
{{ $t("button.submit") }}
</button>
</div>
<Modal
id="asset_add_dept"
:title="t('assetManagement.department')"
:onCancel="onCancel"
:width="400"
>
<template #modalContent>
<form ref="form">
<Input :value="DeptFormState" width="270" name="name">
<template #topLeft>{{
$t("assetManagement.department_name")
}}</template>
<template #bottomLeft
><span class="text-error text-base">
{{ formErrorMsg.name }}
</span></template
></Input
>
</form>
</template>
<template #modalAction>
<button
type="reset"
class="btn btn-outline-success mr-2"
@click.prevent="onCancel"
>
{{ $t("button.cancel") }}
</button>
<button type="submit" class="btn btn-outline-success" @click="onOk">
{{ $t("button.submit") }}
</button>
</template></Modal
>
</template>
<style lang="scss" scoped></style>

View File

@ -138,7 +138,7 @@ onMounted(async () => {
id="asset_add_graph_item"
:title="t('graphManagement.title')"
:onCancel="onCancel"
width="500"
:width="500"
>
<template #modalContent>
<ul class="menu bg-base-200 rounded-box text-lg w-full mt-3">

View File

@ -44,6 +44,7 @@ const modalColumns = computed(() => [
{
title: "tag",
key: "device_name",
filter: true
},
{
title: t("assetManagement.point"),
@ -64,7 +65,7 @@ const getPoint = async (sub_system_tag) => {
setPoints(
res.data.map((d, index) => ({
...d,
title: d.points,
title: d.full_name,
key: d.points,
active: false,
}))
@ -224,7 +225,7 @@ const deleteItem = (value) => {
id="asset_add_IoT_item"
:title="t('assetManagement.associated_device')"
:onCancel="onCancel"
width="900"
:width="900"
>
<template #modalContent>
<ButtonGroup

View File

@ -0,0 +1,185 @@
<script setup>
import { onMounted, ref, inject, watch, computed } from "vue";
import { useI18n } from "vue-i18n";
import { postMQTTpublish } from "@/apis/asset";
import mqtt from "mqtt";
import dayjs from "dayjs";
const { t } = useI18n();
const { openToast, cancelToastOpen } = inject("app_toast");
const { formState } = inject("asset_table_modal_form");
const BASEURL = import.meta.env.VITE_MQTT_BASEURL;
// MQTT
const mqttClient = ref(null); // MQTT
const receivedMessages = ref([]); //
const countdown = ref(60); // 60
let timer = null; //
const openModal = () => {
if (!mqttClient.value) {
connectMqtt();
}
mqtt_test.showModal();
startCountdown(); //
};
const connectMqtt = () => {
const topic = formState.value.topic || ""; //
const mqttHost = `${BASEURL}`;
const protocol = "wss"; // "ws" "wss"
mqttClient.value = mqtt.connect(mqttHost, {
protocol,
reconnectPeriod: 1000, //
username: "admin", // MQTT
password: "mjmadmin@99", // MQTT
port: 443,
});
mqttClient.value.on("connect", () => {
console.log("MQTT 已連接");
if (topic) {
mqttClient.value.subscribe(topic, (err) => {
if (!err) {
console.log(`已訂閱主題: ${topic}`);
} else {
console.error("訂閱失敗: ", err);
}
});
}
});
mqttClient.value.on("message", (topic, message) => {
//
const now = dayjs(); // 使 dayjs()
const timestamp = now.format("YYYY-MM-DD HH:mm:ss");
receivedMessages.value.push({
topic,
message: message.toString(),
timestamp: timestamp,
});
clearInterval(timer); //
});
mqttClient.value.on("error", (err) => {
console.error("MQTT 連線錯誤: ", err);
});
};
const startCountdown = () => {
if (timer) return; //
timer = setInterval(() => {
if (countdown.value > 0) {
countdown.value--;
} else {
onCancel(); // 1 onCancel
}
}, 1000);
};
const onCancel = () => {
receivedMessages.value = [];
mqtt_test.close();
// MQTT
if (mqttClient.value) {
mqttClient.value.end();
mqttClient.value = null;
}
//
if (timer) {
clearInterval(timer);
timer = null; //
}
countdown.value = 60;
};
const onSubmit = async () => {
const Topic = formState.value.topic;
let Payload = "";
try {
Payload = JSON.stringify(JSON.parse(formState.value.publish_message));
} catch (e) {
openToast("error", t("msg.incorrect_format"), "#asset_add_table_item");
return;
}
try {
const res = await postMQTTpublish({ Topic, Payload });
if (res.isSuccess) {
openToast("success", t("msg.send_successfully"), "#asset_add_table_item");
} else {
openToast("error", res.msg, "#asset_add_table_item");
}
} catch (error) {
openToast("error", t("setting.mqtt_send_error"), "#asset_add_table_item");
}
};
</script>
<template>
<div class="flex col-span-2 pb-5">
<Input :value="formState" name="topic">
<template #topLeft>MQTT subscribe topic</template>
</Input>
<button type="button" class="btn btn-add mt-11 ms-1" @click="openModal">
<font-awesome-icon :icon="['fas', 'cog']" />
Test
</button>
</div>
<div class="flex flex-col col-span-2 border-t-gray-400 border-t py-5">
<Input :value="formState" name="topic">
<template #topLeft>MQTT publish topic</template>
</Input>
<Textarea :value="formState" name="publish_message">
<template #topLeft>MQTT messages</template>
</Textarea>
<button type="button" class="btn btn-add mt-6 w-24" @click="onSubmit">
<font-awesome-icon :icon="['far', 'paper-plane']" />
Send
</button>
</div>
<Modal id="mqtt_test" title="MQTT Topic" :onCancel="onCancel" :width="400">
<template #modalContent>
<!-- 顯示接收到的訊息 -->
<div v-if="receivedMessages.length > 0" class="overflow-y-auto h-96">
<ul>
<li
v-for="(message, index) in receivedMessages"
:key="index"
class="bg-base-200 rounded-md text-wrap shadow shadow-slate-400 p-4 my-2 me-2"
>
<strong class="text-base block text-info mb-2"
>{{ message.topic }} :</strong
>
<p class="text-sm break-words">{{ message.message }}</p>
<p class="text-xs text-slate-200 pt-2">
<FontAwesomeIcon :icon="['fas', 'clock']" class="me-1" />
{{ message.timestamp }}
</p>
</li>
</ul>
</div>
<!-- 顯示 loading 和倒計時 -->
<p v-else class="text-center mt-20">
<Loading />
<br />
<span class="text-base">{{ countdown }} seconds</span>
</p>
</template>
<template #modalAction>
<button
type="reset"
class="btn btn-outline-success mr-2"
@click.prevent="onCancel"
>
{{ t("button.cancel") }}
</button>
</template>
</Modal>
</template>
<style lang="scss" scoped></style>

View File

@ -1,13 +1,9 @@
<script setup>
import { onMounted, ref, inject, onBeforeMount, watch, computed } from "vue";
import EffectScatter from "@/components/chart/EffectScatter.vue";
import {
getAssetFloorList,
postAssetFloor,
deleteAssetFloor,
} from "@/apis/asset";
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
import useBuildingStore from "@/stores/useBuildingStore";
import AssetTableModalLeftInfoIoT from "./AssetTableModalLeftInfoIoT.vue";
import AssetTableModalLeftInfoGraph from "./AssetTableModalLeftInfoGraph.vue";
import * as yup from "yup";
import { twMerge } from "tailwind-merge";
import { useI18n } from "vue-i18n";
@ -15,6 +11,7 @@ import { useI18n } from "vue-i18n";
const { t } = useI18n();
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const { totalCoordinates } = inject("asset_table_data");
const { floors } = inject("asset_modal_options");
const { updateRightFields, formErrorMsg, formState } = inject(
"asset_table_modal_form"
);
@ -33,17 +30,20 @@ onBeforeMount(() => {
const asset_floor_chart = ref(null);
const currentFloor = ref(null);
const selectedOption = ref("add");
const parsedCoordinates = ref(null);
const defaultOption = (map, data = []) => {
//
const formattedData = data.map((coordinate) => {
const coordString = JSON.stringify(coordinate);
const coordString = JSON.stringify(coordinate);
return {
name: coordString,
name: coordString,
value: coordinate,
itemStyle: {
color: coordString === formState.value.device_coordinate ? "#0000FF" : "#b02a02",
color:
coordString === formState.value.device_coordinate
? "#0000FF"
: "#b02a02",
},
};
});
@ -72,26 +72,25 @@ const defaultOption = (map, data = []) => {
watch(currentFloor, (newValue) => {
if (newValue?.floor_map_name) {
const coordinates = totalCoordinates.value[newValue.floor_guid] || [];
const parsedCoordinates = coordinates.map((coord) => {
return JSON.parse(coord);
});
const coordinates =
totalCoordinates.value?.[newValue.floor_guid]?.filter(
(coord) => coord !== ""
) || [];
parsedCoordinates.value =
coordinates.length > 0
? coordinates.map((coord) => JSON.parse(coord))
: [];
asset_floor_chart.value.updateSvg(
{
full_name: newValue?.floor_map_name,
path: `${FILE_BASEURL}/${newValue?.floor_map_url}.svg`,
full_name: newValue.floor_map_name,
path: `${FILE_BASEURL}/${newValue.floor_map_url}.svg`,
},
defaultOption(newValue?.floor_map_name, parsedCoordinates)
defaultOption(newValue.floor_map_name, parsedCoordinates.value)
);
}
});
const floors = ref([]);
const getFloors = async () => {
const res = await getAssetFloorList();
floors.value = res.data[0]?.floors.map((d) => ({ ...d, key: d.floor_guid }));
};
watch(
formState,
@ -114,126 +113,39 @@ const getCoordinate = (position) => {
formState.value.device_coordinate = JSON.stringify(position);
};
onMounted(() => {
getFloors();
});
// modal
const openModal = () => {
if (selectedOption.value === "add") {
FloorFormState.value = {
full_name: "",
floorFile: [],
};
} else if (selectedOption.value === "edit") {
const floor = floors.value.find(
(f) => f.floor_guid === formState.value.floor_guid
);
if (floor) {
console.log("floor", floor);
FloorFormState.value = {
full_name: floor.full_name,
floorFile: [],
};
}
}
asset_add_floor.showModal();
};
const form = ref(null);
const FloorFormState = ref({
full_name: "",
floorFile: [],
});
const floorScheme = yup.object({
full_name: yup.string().required(t("button.required")),
floorFile: yup.array(),
});
const updateFileList = (files) => {
console.log("file", files);
FloorFormState.value.floorFile = files;
};
const {
formErrorMsg: floorFormErrorMsg,
handleSubmit,
handleErrorReset,
updateScheme,
} = useFormErrorMessage(floorScheme);
const onOk = async () => {
const value = handleSubmit(floorScheme, FloorFormState.value);
const formData = new FormData(form.value);
formData.append("floor_guid", currentFloor.value.floor_guid);
formData.append("building_tag", store.selectedBuilding.building_tag);
formData.append("initMapName", FloorFormState.value.floorFile[0].name);
formData.append("mapFile", FloorFormState.value.floorFile[0]);
formData.delete("floorFile");
for (let [key, value] of formData) {
console.log(key, value);
}
const res = await postAssetFloor(formData);
if (res.isSuccess) {
getFloors();
onCancel();
}
};
const onDelete = async () => {
console.log("guild", formState.value.floor_guid);
const res = await deleteAssetFloor({
floor_guid: formState.value.floor_guid,
});
if (res.isSuccess) {
getFloors();
}
};
const onCancel = () => {
FloorFormState.value = {
full_name: "",
floorFile: [],
};
asset_add_floor.close();
};
</script>
<template>
<!-- 平面圖 -->
<div class="flex items-center justify-between mb-5">
<div className="join">
<Select
:value="formState"
selectClass="border-info focus-within:border-info rounded-r-none"
name="floor_guid"
Attribute="full_name"
:options="floors"
:isBottomLabelExist="false"
>
<template #topLeft>{{ $t("assetManagement.floor") }}</template>
</Select>
<select
v-model="selectedOption"
className="select border-info focus-within:border-info join-item mt-11"
>
<option value="add" selected>{{ $t("button.add") }}</option>
<option value="edit">{{ $t("button.edit") }}</option>
<option value="delete">{{ $t("button.delete") }}</option>
</select>
<button
type="button"
class="btn btn-success join-item mt-11"
@click="selectedOption === 'delete' ? onDelete() : openModal()"
:aria-label="$t('button.submit')"
>
{{ $t("button.submit") }}
</button>
</div>
<div class="flex gap-4 mb-5">
<Select
:value="formState"
selectClass="border-info focus-within:border-info"
name="floor_guid"
Attribute="full_name"
:options="floors"
:isBottomLabelExist="false"
>
<template #topLeft>{{ $t("assetManagement.floor") }}</template>
</Select>
<Input
:value="formState"
width="270"
name="device_coordinate"
:disabled="true"
>
<template #topLeft>{{
$t("assetManagement.device_coordinate")
}}</template>
<template #bottomLeft
><span class="text-error text-base">
{{ formErrorMsg.device_coordinate }}
</span></template
></Input
>
</div>
<div class="relative">
<div class="relative min-h-[70vh]">
<EffectScatter
id="asset_floor_chart"
ref="asset_floor_chart"
@ -247,51 +159,13 @@ const onCancel = () => {
/>
<div
v-if="!currentFloor?.floor_map_url"
class="absolute top-0 left-0 flex justify-center items-center min-h-[500px] w-full border border-stone-900 shadow-lg bg-sub-success bg-opacity-25 rounded-md"
class="absolute top-0 left-0 flex justify-center items-center min-h-[70vh] w-full border border-stone-900 shadow-lg bg-sub-success bg-opacity-25 rounded-md"
>
<p class="text-2xl">{{ $t("assetManagement.add_floor_text") }}</p>
</div>
</div>
<Modal
id="asset_add_floor"
:title="t('assetManagement.floor_plan')"
:onCancel="onCancel"
width="400"
>
<template #modalContent>
<form ref="form">
<Input :value="FloorFormState" width="270" name="full_name">
<template #topLeft>{{ $t("assetManagement.system_name") }}</template>
<template #bottomLeft
><span class="text-error text-base">
{{ floorFormErrorMsg.full_name }}
</span></template
></Input
>
<Upload
name="floorFile"
:fileList="FloorFormState.floorFile"
:getFileList="updateFileList"
:multiple="false"
class="col-span-2"
>
<template #topLeft>{{ $t("assetManagement.oriFile") }}</template>
</Upload>
</form>
</template>
<template #modalAction>
<button
type="reset"
class="btn btn-outline-success mr-2"
@click.prevent="onCancel"
>
{{ $t("button.cancel") }}
</button>
<button type="submit" class="btn btn-outline-success" @click="onOk">
{{ $t("button.submit") }}
</button>
</template></Modal
>
<AssetTableModalLeftInfoGraph />
<AssetTableModalLeftInfoIoT />
</template>
<style lang="scss" scoped></style>

Some files were not shown because too many files have changed in this diff Show More