語言包與首頁介面
@ -9,7 +9,7 @@
|
||||
href="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/style.css"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Marketing Dashboard - Application Intel - SmartAdmin v4.5.1</title>
|
||||
<title>瀚荃監控系統</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>
|
||||
|
72
package-lock.json
generated
@ -20,10 +20,12 @@
|
||||
"date-fns": "^3.3.1",
|
||||
"dayjs": "^1.11.10",
|
||||
"echarts": "^5.4.3",
|
||||
"flag-icons": "^7.2.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",
|
||||
"yup": "^1.4.0",
|
||||
"yup-phone-lite": "^2.0.1"
|
||||
@ -545,6 +547,50 @@
|
||||
"vue": ">= 3.0.0 < 4"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/core-base": {
|
||||
"version": "10.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.4.tgz",
|
||||
"integrity": "sha512-GG428DkrrWCMhxRMRQZjuS7zmSUzarYcaHJqG9VB8dXAxw4iQDoKVQ7ChJRB6ZtsCsX3Jse1PEUlHrJiyQrOTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/message-compiler": "10.0.4",
|
||||
"@intlify/shared": "10.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/message-compiler": {
|
||||
"version": "10.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.4.tgz",
|
||||
"integrity": "sha512-AFbhEo10DP095/45EauinQJ5hJ3rJUmuuqltGguvc3WsvezZN+g8qNHLGWKu60FHQVizMrQY7VJ+zVlBXlQQkQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/shared": "10.0.4",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/shared": {
|
||||
"version": "10.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.4.tgz",
|
||||
"integrity": "sha512-ukFn0I01HsSgr3VYhYcvkTCLS7rGa0gw4A4AMpcy/A9xx/zRJy7PS2BElMXLwUazVFMAr5zuiTk3MQeoeGXaJg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||
@ -2334,6 +2380,12 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/flag-icons": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/flag-icons/-/flag-icons-7.2.3.tgz",
|
||||
"integrity": "sha512-X2gUdteNuqdNqob2KKTJTS+ZCvyWeLCtDz9Ty8uJP17Y4o82Y+U/Vd4JNrdwTAjagYsRznOn9DZ+E/Q52qbmqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.3",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
|
||||
@ -5378,6 +5430,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-i18n": {
|
||||
"version": "10.0.4",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.4.tgz",
|
||||
"integrity": "sha512-1xkzVxqBLk2ZFOmeI+B5r1J7aD/WtNJ4j9k2mcFcQo5BnOmHBmD7z4/oZohh96AAaRZ4Q7mNQvxc9h+aT+Md3w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "10.0.4",
|
||||
"@intlify/shared": "10.0.4",
|
||||
"@vue/devtools-api": "^6.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/kazupon"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "4.2.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz",
|
||||
|
@ -21,10 +21,12 @@
|
||||
"date-fns": "^3.3.1",
|
||||
"dayjs": "^1.11.10",
|
||||
"echarts": "^5.4.3",
|
||||
"flag-icons": "^7.2.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",
|
||||
"yup": "^1.4.0",
|
||||
"yup-phone-lite": "^2.0.1"
|
||||
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 122 KiB |
@ -39,11 +39,11 @@ provide("app_toast", { openToast });
|
||||
/>
|
||||
<div v-if="store.user.token" class="min-h-screen">
|
||||
<Navbar />
|
||||
<div class="mt-6 px-8 w-full relative app-container">
|
||||
<div class="px-8 w-full relative app-container">
|
||||
<RouterView />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="h-screen"><RouterView /></div>
|
||||
<div v-else class="min-h-screen"><RouterView /></div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
BIN
src/assets/img/area-img-box-line-bottom.png
Normal file
After Width: | Height: | Size: 524 B |
BIN
src/assets/img/area-img-box-line-top.png
Normal file
After Width: | Height: | Size: 530 B |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 94 KiB |
1
src/assets/img/chart-data-background01.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.cls-1{fill:#dff3fa;}.cls-2{fill:#74b6ed;}</style></defs><path class="cls-1" d="M2.79,24.12a2.59,2.59,0,0,0,2.58-2.58V7.1A5,5,0,0,0,7.11,5.4H21.23a2.59,2.59,0,0,0,2.58-2.58.12.12,0,0,0-.12-.12.12.12,0,0,0-.12.12,2.34,2.34,0,0,1-2.34,2.34H7l0,.06A4.86,4.86,0,0,1,5.19,6.93L5.13,7V21.54a2.34,2.34,0,0,1-2.34,2.34.12.12,0,0,0-.12.12A.12.12,0,0,0,2.79,24.12Z"/><path class="cls-2" d="M2,18.05a.6.6,0,0,1-.6-.59V4.14L1,3.86A2.09,2.09,0,0,1,2.09,0,2.05,2.05,0,0,1,3.88,1l.28.46H17a.6.6,0,0,1,.6.6.59.59,0,0,1-.6.59H4.16l-.28.47a2.08,2.08,0,0,1-.77.76l-.49.27V17.46A.59.59,0,0,1,2,18.05Z"/></svg>
|
After Width: | Height: | Size: 697 B |
1
src/assets/img/chart-data-background02.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><style>.cls-1{fill:#dff3fa;}.cls-2{fill:#74b6ed;}</style></defs><path class="cls-1" d="M-.15,21.18A2.58,2.58,0,0,1,2.43,18.6H16.87a4.88,4.88,0,0,1,1.7-1.74V2.74A2.59,2.59,0,0,1,21.15.15a.12.12,0,0,1,.12.12.12.12,0,0,1-.12.12,2.35,2.35,0,0,0-2.34,2.35V17l-.06,0A4.63,4.63,0,0,0,17,18.78l0,.06H2.43A2.35,2.35,0,0,0,.08,21.18.12.12,0,0,1,0,21.3.11.11,0,0,1-.15,21.18Z"/><path class="cls-2" d="M5.92,21.94a.59.59,0,0,0,.59.6H19.83l.28.44a2.07,2.07,0,0,0,1.77,1,2.09,2.09,0,0,0,1.06-3.89l-.47-.27V7a.59.59,0,0,0-.59-.6.6.6,0,0,0-.6.6V19.81l-.46.27a2.11,2.11,0,0,0-.76.78l-.28.48H6.51A.59.59,0,0,0,5.92,21.94Z"/></svg>
|
After Width: | Height: | Size: 713 B |
1
src/assets/img/chart-data-background03.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><defs><style>.cls-1{fill:#0ca9d4;}</style></defs><polygon id="_13" data-name="13" class="cls-1" points="3.96 0 4.48 3.43 7.91 3.96 4.48 4.48 3.96 7.91 3.43 4.48 0 3.96 3.43 3.43 3.96 0"/></svg>
|
After Width: | Height: | Size: 286 B |
21
src/assets/img/chart-title01.svg
Normal 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:#0CA9D4;}
|
||||
.st1{opacity:0.3;fill:#969696;}
|
||||
.st2{opacity:0.8;fill:#0CA9D4;}
|
||||
</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 |
1
src/assets/img/chart-title02.svg
Normal file
@ -0,0 +1 @@
|
||||
<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>
|
After Width: | Height: | Size: 1.0 KiB |
21
src/assets/img/equipment-item-background.svg
Normal 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 2.5 21" style="enable-background:new 0 0 2.5 21;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{opacity:0.8;}
|
||||
.st1{fill:#FDE9F2;}
|
||||
</style>
|
||||
<g id="_x37_0_00000047024518298343511510000016821505459452511412_" class="st0">
|
||||
<rect x="1.2" y="1" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -1.5991 1.79)" class="st1" width="0.3" height="3.6"/>
|
||||
<rect x="1.2" y="-0.4" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -0.5638 1.3612)" class="st1" width="0.3" height="3.6"/>
|
||||
<rect x="1.2" y="5.2" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -4.5762 3.0232)" class="st1" width="0.3" height="3.6"/>
|
||||
<rect x="1.2" y="3.8" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -3.541 2.5943)" class="st1" width="0.3" height="3.6"/>
|
||||
<rect x="1.2" y="9.5" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -7.5533 4.2563)" class="st1" width="0.3" height="3.6"/>
|
||||
<rect x="1.2" y="8" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -6.5181 3.8275)" class="st1" width="0.3" height="3.6"/>
|
||||
<rect x="1.2" y="13.7" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -10.5305 5.4895)" class="st1" width="0.3" height="3.6"/>
|
||||
<rect x="1.2" y="12.2" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -9.4952 5.0607)" class="st1" width="0.3" height="3.6"/>
|
||||
<rect x="1.2" y="17.9" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -13.5076 6.7227)" class="st1" width="0.3" height="3.6"/>
|
||||
<rect x="1.2" y="16.4" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -12.4724 6.2938)" class="st1" width="0.3" height="3.6"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
1
src/assets/img/equipment-item-background04.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 151 38"><defs><style>.cls-1,.cls-5{fill:#b3e8f4;}.cls-1{opacity:0;}.cls-2{fill:#83beed;}.cls-3{fill:#dff3fa;}.cls-4{fill:#fcfefd;}.cls-6{fill:#fde9f2;}</style></defs><path class="cls-1" d="M140.66,2H4.17A4.17,4.17,0,0,0,0,6.15v21.5l10.6,10.6H147.09a4.17,4.17,0,0,0,4.17-4.17V12.59Z"/><path class="cls-2" d="M147.09,36.29H11.41L2,26.84V6.15A2.21,2.21,0,0,1,4.17,3.94H139.85l9.45,9.46V34.08A2.21,2.21,0,0,1,147.09,36.29ZM11.52,36H147.09a2,2,0,0,0,2-2V13.5l-9.31-9.31H4.17a2,2,0,0,0-2,2V26.73Z"/><polygon class="cls-3" points="24.78 5.81 8.01 5.81 11.16 2.33 27.93 2.33 24.78 5.81"/><polygon class="cls-4" points="137.13 36.16 127.56 36.16 129.36 34.17 138.93 34.17 137.13 36.16"/><polygon class="cls-4" points="114.9 36.16 105.33 36.16 107.13 34.17 116.7 34.17 114.9 36.16"/><polygon class="cls-4" points="92.67 36.16 83.11 36.16 84.91 34.17 94.47 34.17 92.67 36.16"/><polygon class="cls-5" points="123.2 6.35 92.68 6.35 98.42 0 128.94 0 123.2 6.35"/><rect class="cls-4" x="77.43" y="33.96" width="71.74" height="0.25"/><polygon id="_13" data-name="13" class="cls-6" points="138.62 8.13 139.09 11.2 142.16 11.67 139.09 12.14 138.62 15.21 138.15 12.14 135.08 11.67 138.15 11.2 138.62 8.13"/></svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
src/assets/img/equipment-item-background05.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 151 38"><defs><style>.cls-1{fill:#fff;opacity:0.05;}.cls-2{fill:#af2334;opacity:0.75;}.cls-3{fill:#30c4c4;}.cls-4{fill:#dff3fa;}.cls-5{fill:#fcfefd;}</style></defs><path class="cls-1" d="M140.18,2H3.68A4.17,4.17,0,0,0-.48,6.15v21.5l10.6,10.6H146.61a4.17,4.17,0,0,0,4.17-4.17V12.59Z"/><path class="cls-2" d="M140.18,2H3.68A4.17,4.17,0,0,0-.48,6.15v21.5l10.6,10.6H146.61a4.17,4.17,0,0,0,4.17-4.17V12.59Z"/><path class="cls-3" d="M146.61,36.29H10.93L1.47,26.84V6.15A2.22,2.22,0,0,1,3.68,3.94H139.36l9.46,9.46V34.08A2.21,2.21,0,0,1,146.61,36.29ZM11,36H146.61a2,2,0,0,0,2-2V13.5l-9.31-9.31H3.68a2,2,0,0,0-2,2V26.73Z"/><polygon class="cls-4" points="24.3 5.81 7.53 5.81 10.68 2.33 27.45 2.33 24.3 5.81"/><polygon class="cls-5" points="136.65 36.16 127.08 36.16 128.88 34.17 138.45 34.17 136.65 36.16"/><polygon class="cls-5" points="114.42 36.16 104.85 36.16 106.65 34.17 116.22 34.17 114.42 36.16"/><polygon class="cls-5" points="92.19 36.16 82.62 36.16 84.42 34.17 93.99 34.17 92.19 36.16"/><polygon class="cls-4" points="122.72 6.35 92.2 6.35 97.94 0 128.45 0 122.72 6.35"/><rect class="cls-5" x="76.95" y="33.96" width="71.74" height="0.25"/></svg>
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/img/icon/C.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src/assets/img/icon/E1.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/img/icon/E2.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
src/assets/img/icon/E3.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/img/icon/E4-1.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
src/assets/img/icon/EL.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
src/assets/img/icon/F1.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
src/assets/img/icon/L1.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
src/assets/img/icon/L2.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/img/icon/M1.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
src/assets/img/icon/M10.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
src/assets/img/icon/M12.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/img/icon/M5-2.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
src/assets/img/icon/M8.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
src/assets/img/icon/P.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
src/assets/img/icon/P1.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
src/assets/img/icon/PSC.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/img/icon/R.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
src/assets/img/icon/W1.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/img/icon/W2.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
src/assets/img/icon/W3.png
Normal file
After Width: | Height: | Size: 12 KiB |
1
src/assets/img/item-data-left-2.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 14 55"><defs><style>.cls-1{isolation:isolate;}.cls-2{mix-blend-mode:screen;opacity:0.75;}.cls-3{fill:#58bfe8;}</style></defs><g class="cls-1"><g id="圖層_1" data-name="圖層 1"><image class="cls-2" width="14" height="55" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAA3CAYAAAA8PXu0AAAACXBIWXMAAAsSAAALEgHS3X78AAAA8ElEQVRIS+2WTQrCMBBGX4roQhDcieAZvP9VPICIKxGyUKtx4Zc6TXVQF67yQZjm52Xarl5IKfFLRsU8FNUm2WrBRvOJqoUTcAHOqimDQYfnwBKYai1ptMAR2KtebMeJoLXA7nbgJChqtGXHqUYEdqo3nh0jcMW8aoYDj047YKPDN/rf2MLwr+bviYIOAvNeV0swbyQBeQzSvFr8JBV0UkEnFXRSQScVdFJBJxV08n/wla5kUWroX+x6jlWzmdZKs+rZIzzFaAwstFa63MAerYpFHh1XfGCPaHOr53e+2tljME7e8IUhh0Lmf3LywaaXO0TIazwsXqhCAAAAAElFTkSuQmCC"/><polygon class="cls-3" points="10.59 51.05 3.9 51.05 3.86 3.77 10.55 3.77 10.55 4.77 4.86 4.77 4.9 50.05 10.59 50.05 10.59 51.05"/></g></g></svg>
|
After Width: | Height: | Size: 926 B |
1
src/assets/img/item-data-left.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 14 55"><defs><style>.cls-1{isolation:isolate;}.cls-2{mix-blend-mode:screen;opacity:0.75;}.cls-3{fill:#23c8cc;}</style></defs><g class="cls-1"><g id="圖層_1" data-name="圖層 1"><image class="cls-2" width="14" height="55" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAA3CAYAAAA8PXu0AAAACXBIWXMAAAsSAAALEgHS3X78AAAA8ElEQVRIS+2WTQrCMBBGX4roQhDcieAZvP9VPICIKxGyUKtx4Zc6TXVQF67yQZjm52Xarl5IKfFLRsU8FNUm2WrBRvOJqoUTcAHOqimDQYfnwBKYai1ptMAR2KtebMeJoLXA7nbgJChqtGXHqUYEdqo3nh0jcMW8aoYDj047YKPDN/rf2MLwr+bviYIOAvNeV0swbyQBeQzSvFr8JBV0UkEnFXRSQScVdFJBJxV08n/wla5kUWroX+x6jlWzmdZKs+rZIzzFaAwstFa63MAerYpFHh1XfGCPaHOr53e+2tljME7e8IUhh0Lmf3LywaaXO0TIazwsXqhCAAAAAElFTkSuQmCC"/><polygon class="cls-3" points="10.59 51.05 3.9 51.05 3.86 3.77 10.55 3.77 10.55 4.77 4.86 4.77 4.9 50.05 10.59 50.05 10.59 51.05"/></g></g></svg>
|
After Width: | Height: | Size: 926 B |
1
src/assets/img/item-data-right-2.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 14 55"><defs><style>.cls-1{isolation:isolate;}.cls-2{mix-blend-mode:screen;opacity:0.75;}.cls-3{fill:#58bfe8;}</style></defs><g class="cls-1"><g id="圖層_1" data-name="圖層 1"><image class="cls-2" width="14" height="55" transform="matrix(-1, 0, 0, 1, 14, 0)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAA3CAYAAAA8PXu0AAAACXBIWXMAAAsSAAALEgHS3X78AAAA8ElEQVRIS+2WTQrCMBBGX4roQhDcieAZvP9VPICIKxGyUKtx4Zc6TXVQF67yQZjm52Xarl5IKfFLRsU8FNUm2WrBRvOJqoUTcAHOqimDQYfnwBKYai1ptMAR2KtebMeJoLXA7nbgJChqtGXHqUYEdqo3nh0jcMW8aoYDj047YKPDN/rf2MLwr+bviYIOAvNeV0swbyQBeQzSvFr8JBV0UkEnFXRSQScVdFJBJxV08n/wla5kUWroX+x6jlWzmdZKs+rZIzzFaAwstFa63MAerYpFHh1XfGCPaHOr53e+2tljME7e8IUhh0Lmf3LywaaXO0TIazwsXqhCAAAAAElFTkSuQmCC"/><polygon class="cls-3" points="3.41 51.05 10.1 51.05 10.14 3.77 3.44 3.77 3.44 4.77 9.14 4.77 9.1 50.05 3.41 50.05 3.41 51.05"/></g></g></svg>
|
After Width: | Height: | Size: 962 B |
1
src/assets/img/item-data-right.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 14 55"><defs><style>.cls-1{isolation:isolate;}.cls-2{mix-blend-mode:screen;opacity:0.75;}.cls-3{fill:#23c8cc;}</style></defs><g class="cls-1"><g id="圖層_1" data-name="圖層 1"><image class="cls-2" width="14" height="55" transform="matrix(-1, 0, 0, 1, 14, 0)" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAA3CAYAAAA8PXu0AAAACXBIWXMAAAsSAAALEgHS3X78AAAA8ElEQVRIS+2WTQrCMBBGX4roQhDcieAZvP9VPICIKxGyUKtx4Zc6TXVQF67yQZjm52Xarl5IKfFLRsU8FNUm2WrBRvOJqoUTcAHOqimDQYfnwBKYai1ptMAR2KtebMeJoLXA7nbgJChqtGXHqUYEdqo3nh0jcMW8aoYDj047YKPDN/rf2MLwr+bviYIOAvNeV0swbyQBeQzSvFr8JBV0UkEnFXRSQScVdFJBJxV08n/wla5kUWroX+x6jlWzmdZKs+rZIzzFaAwstFa63MAerYpFHh1XfGCPaHOr53e+2tljME7e8IUhh0Lmf3LywaaXO0TIazwsXqhCAAAAAElFTkSuQmCC"/><polygon class="cls-3" points="3.41 51.05 10.1 51.05 10.14 3.77 3.44 3.77 3.44 4.77 9.14 4.77 9.1 50.05 3.41 50.05 3.41 51.05"/></g></g></svg>
|
After Width: | Height: | Size: 962 B |
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 122 KiB |
BIN
src/assets/img/state-box-bottom.png
Normal file
After Width: | Height: | Size: 722 B |
BIN
src/assets/img/state-box-top.png
Normal file
After Width: | Height: | Size: 713 B |
1
src/assets/img/state-title01.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><defs><style>.cls-1{fill:#ffe422;}</style></defs><polygon id="_13" data-name="13" class="cls-1" points="4 0.19 4.5 3.5 7.81 4 4.5 4.5 4 7.81 3.5 4.5 0.19 4 3.5 3.5 4 0.19"/></svg>
|
After Width: | Height: | Size: 272 B |
1
src/assets/img/state-title02.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 9"><defs><style>.cls-1{fill:#30c4c4;}</style></defs><path id="_12" data-name="12" class="cls-1" d="M22,4.44C21.92,4.4,8.63,4,7.59,3.11A11.13,11.13,0,0,1,4.82.19h-1A9.65,9.65,0,0,1,0,4V5a9.65,9.65,0,0,1,3.8,3.8h1a11.12,11.12,0,0,1,2.7-3.1C8.17,5.17,21.94,4.59,22,4.56Z"/></svg>
|
After Width: | Height: | Size: 367 B |
1
src/assets/img/state-ul-background01.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 4.91 37.93"><defs><style>.cls-1{opacity:0.8;}.cls-2{fill:#ffe422;}</style></defs><g id="_70" data-name="70" class="cls-1"><rect class="cls-2" x="2.21" y="29.6" width="0.49" height="6.45" transform="translate(-22.5 11.35) rotate(-45)"/><rect class="cls-2" x="2.21" y="32.24" width="0.49" height="6.45" transform="translate(-24.36 12.13) rotate(-45)"/><rect class="cls-2" x="2.21" y="22.01" width="0.49" height="6.45" transform="translate(-17.13 9.13) rotate(-45)"/><rect class="cls-2" x="2.21" y="24.65" width="0.49" height="6.45" transform="translate(-18.99 9.9) rotate(-45)"/><rect class="cls-2" x="2.21" y="14.42" width="0.49" height="6.45" transform="translate(-11.76 6.9) rotate(-45)"/><rect class="cls-2" x="2.21" y="17.06" width="0.49" height="6.45" transform="translate(-13.62 7.68) rotate(-45)"/><rect class="cls-2" x="2.21" y="6.82" width="0.49" height="6.45" transform="translate(-6.39 4.68) rotate(-45)"/><rect class="cls-2" x="2.21" y="9.46" width="0.49" height="6.45" transform="translate(-8.25 5.45) rotate(-45)"/><rect class="cls-2" x="2.21" y="-0.77" width="0.49" height="6.45" transform="translate(-1.02 2.46) rotate(-45)"/><rect class="cls-2" x="2.21" y="1.87" width="0.49" height="6.45" transform="translate(-2.88 3.23) rotate(-45)"/></g></svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
src/assets/img/state-ul-background02.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 7.13 78.94"><defs><style>.cls-1{fill:#fde9f2;}</style></defs><g id="_67" data-name="67"><rect class="cls-1" y="75.13" width="3.57" height="3.57"/><rect class="cls-1" y="68" width="3.57" height="3.57"/><rect class="cls-1" y="60.87" width="3.57" height="3.57"/><rect class="cls-1" y="53.73" width="3.57" height="3.57"/><rect class="cls-1" y="46.6" width="3.57" height="3.57"/><rect class="cls-1" y="39.47" width="3.57" height="3.57"/><rect class="cls-1" y="32.34" width="3.57" height="3.57"/><rect class="cls-1" y="25.2" width="3.57" height="3.57"/><rect class="cls-1" y="18.07" width="3.57" height="3.57"/><rect class="cls-1" y="10.94" width="3.57" height="3.57"/><rect class="cls-1" y="3.81" width="3.57" height="3.57"/><rect class="cls-1" x="3.57" y="71.56" width="3.57" height="3.57"/><rect class="cls-1" x="3.57" y="64.43" width="3.57" height="3.57"/><rect class="cls-1" x="3.57" y="57.3" width="3.57" height="3.57"/><rect class="cls-1" x="3.57" y="50.17" width="3.57" height="3.57"/><rect class="cls-1" x="3.57" y="43.03" width="3.57" height="3.57"/><rect class="cls-1" x="3.57" y="35.9" width="3.57" height="3.57"/><rect class="cls-1" x="3.57" y="28.77" width="3.57" height="3.57"/><rect class="cls-1" x="3.57" y="21.64" width="3.57" height="3.57"/><rect class="cls-1" x="3.57" y="14.51" width="3.57" height="3.57"/><rect class="cls-1" x="3.57" y="7.37" width="3.57" height="3.57"/><rect class="cls-1" x="3.57" y="0.24" width="3.57" height="3.57"/></g></svg>
|
After Width: | Height: | Size: 1.5 KiB |
1
src/assets/img/state-ul-text.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 9"><defs><style>.cls-1{fill:#fff;}</style></defs><path class="cls-1" d="M22.56,4.36h-8a3,3,0,0,0-2.87-2.88V.4h-.29V1.48A3,3,0,0,0,8.49,4.36h-8v.28H8.49a3,3,0,0,0,2.87,2.88V8.6h.29V7.52a3,3,0,0,0,2.87-2.88h8ZM13.44,2.57a2.73,2.73,0,0,1,.8,1.79H12.38a.89.89,0,0,0-.73-.74V1.77A2.7,2.7,0,0,1,13.44,2.57Zm-3.87,0a2.7,2.7,0,0,1,1.79-.8V3.62a.88.88,0,0,0-.73.74H8.77A2.73,2.73,0,0,1,9.57,2.57Zm0,3.87a2.76,2.76,0,0,1-.8-1.8h1.86a.88.88,0,0,0,.73.74V7.23A2.74,2.74,0,0,1,9.57,6.44Zm3.87,0a2.74,2.74,0,0,1-1.79.79V5.38a.89.89,0,0,0,.73-.74h1.86A2.76,2.76,0,0,1,13.44,6.44Z"/></svg>
|
After Width: | Height: | Size: 664 B |
49
src/assets/img/state-ul.svg
Normal file
@ -0,0 +1,49 @@
|
||||
<?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 110.8 43.5" style="enable-background:new 0 0 110.8 43.5;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{opacity:0.5;fill:url(#SVGID_1_);}
|
||||
.st1{fill:#FFFFFF;}
|
||||
.st2{opacity:0.7;}
|
||||
.st3{fill:#79AECC;}
|
||||
.st4{fill:#DFF3FA;}
|
||||
</style>
|
||||
<g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="6.0326" y1="21.8344" x2="106.0948" y2="21.8344">
|
||||
<stop offset="0" style="stop-color:#0CA9D4;stop-opacity:0.7"/>
|
||||
<stop offset="2.671185e-02" style="stop-color:#27ABD7;stop-opacity:0.6626"/>
|
||||
<stop offset="6.063062e-02" style="stop-color:#42AEDA;stop-opacity:0.6151"/>
|
||||
<stop offset="9.965897e-02" style="stop-color:#58B0DC;stop-opacity:0.5605"/>
|
||||
<stop offset="0.1452" style="stop-color:#69B1DE;stop-opacity:0.4967"/>
|
||||
<stop offset="0.2015" style="stop-color:#74B2DF;stop-opacity:0.4178"/>
|
||||
<stop offset="0.2806" style="stop-color:#7BB3E0;stop-opacity:0.3072"/>
|
||||
<stop offset="0.5" style="stop-color:#7DB3E0;stop-opacity:0"/>
|
||||
<stop offset="0.7194" style="stop-color:#7BB3E0;stop-opacity:0.3072"/>
|
||||
<stop offset="0.7985" style="stop-color:#74B2DF;stop-opacity:0.4178"/>
|
||||
<stop offset="0.8548" style="stop-color:#69B1DE;stop-opacity:0.4967"/>
|
||||
<stop offset="0.9003" style="stop-color:#58B0DC;stop-opacity:0.5605"/>
|
||||
<stop offset="0.9394" style="stop-color:#42AEDA;stop-opacity:0.6151"/>
|
||||
<stop offset="0.9733" style="stop-color:#27ABD7;stop-opacity:0.6626"/>
|
||||
<stop offset="1" style="stop-color:#0CA9D4;stop-opacity:0.7"/>
|
||||
</linearGradient>
|
||||
<path class="st0" d="M104.6,39.6H7.5c-0.8,0-1.5-0.7-1.5-1.5V5.5c0-0.8,0.7-1.5,1.5-1.5h97.1c0.8,0,1.5,0.7,1.5,1.5v32.6
|
||||
C106.1,38.9,105.4,39.6,104.6,39.6z"/>
|
||||
<g>
|
||||
<path class="st1" d="M102.5,38H9.6c-0.8,0-1.5-0.7-1.5-1.5V7.1c0-0.8,0.7-1.5,1.5-1.5h92.9c0.8,0,1.5,0.7,1.5,1.5v29.5
|
||||
C104,37.4,103.3,38,102.5,38z M9.6,5.9c-0.7,0-1.2,0.5-1.2,1.2v29.5c0,0.7,0.5,1.2,1.2,1.2h92.9c0.7,0,1.2-0.5,1.2-1.2V7.1
|
||||
c0-0.7-0.5-1.2-1.2-1.2H9.6z"/>
|
||||
</g>
|
||||
<g class="st2">
|
||||
<g>
|
||||
<path class="st3" d="M107,43.4H5.1c-2.4,0-4.4-2-4.4-4.4V4.6c0-2.4,2-4.4,4.4-4.4H107c2.4,0,4.4,2,4.4,4.4V39
|
||||
C111.4,41.5,109.4,43.4,107,43.4z M5.1,2.3c-1.3,0-2.4,1.1-2.4,2.4V39c0,1.3,1.1,2.4,2.4,2.4H107c1.3,0,2.4-1.1,2.4-2.4V4.6
|
||||
c0-1.3-1.1-2.4-2.4-2.4H5.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
<polygon class="st4" points="66.5,3.1 44,3.1 45.7,0.2 68.1,0.2 "/>
|
||||
<polygon class="st4" points="66.5,43.4 44,43.4 45.7,40.6 68.1,40.6 "/>
|
||||
<polygon class="st4" points="4.5,11.4 4.5,33.9 0.7,32.2 0.7,9.8 "/>
|
||||
<polygon class="st4" points="111.4,11.4 111.4,33.9 107.6,32.2 107.6,9.8 "/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
1
src/assets/img/text-position-line.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg id="圖層_1" data-name="圖層 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 111 7"><defs><style>.cls-1{fill:#5eabea;}.cls-2{fill:#d0032c;}</style></defs><path id="_66" data-name="66" class="cls-1" d="M55.74,7.19A3.7,3.7,0,0,1,59.37,4l51.39-.45L59.37,3.15A3.7,3.7,0,0,1,55.74,0Z"/><path id="_66-2" data-name="66" class="cls-2" d="M55.74,0h0a3.7,3.7,0,0,1-3.63,3.15L0,3.59,52.11,4a3.7,3.7,0,0,1,3.63,3.15Z"/></svg>
|
After Width: | Height: | Size: 424 B |
Before Width: | Height: | Size: 276 B After Width: | Height: | Size: 150 KiB |
@ -30,7 +30,7 @@ const ackedAlarm = async (uuid) => {
|
||||
:icon="['fas', 'exclamation-triangle']"
|
||||
class="text-warning mr-2"
|
||||
/>
|
||||
<span>異常通知</span></span
|
||||
<span>{{ $t("alarm.notify") }}</span></span
|
||||
>
|
||||
<small>
|
||||
<span class="mr-4"
|
||||
@ -45,18 +45,18 @@ const ackedAlarm = async (uuid) => {
|
||||
</p>
|
||||
<div class="divider my-2"></div>
|
||||
<div>
|
||||
<p>異常編號:{{ alarm.uuid }}</p>
|
||||
<p>{{ $t("alarm.number") }}:{{ alarm.uuid }}</p>
|
||||
<!-- <p>異常等級:255</p> -->
|
||||
<p>異常類別:{{ alarm.alarmClass }}</p>
|
||||
<p>設備名稱:{{ alarm.full_name }}</p>
|
||||
<p>異常訊息:{{ alarm.msg }}</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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import AlarmCards from "./AlarmCards.vue";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
const showErr = ref(false);
|
||||
const toggleErrIcon = () => {
|
||||
@ -31,10 +32,17 @@ const toggleErrIcon = () => {
|
||||
size="2x"
|
||||
class="text-white menu-icon"
|
||||
/>
|
||||
<span class="text-white"> 顯示警告</span>
|
||||
<span class="text-white"> {{ $t("alarm.title") }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="drawer-side translate-y-16 max-h-[95vh] overflow-y-scroll overflow-x-hidden">
|
||||
<div
|
||||
:class="
|
||||
twMerge(
|
||||
'drawer-side translate-y-16 max-h-[95vh] overflow-x-hidden',
|
||||
showErr ? 'overflow-y-scroll' : ''
|
||||
)
|
||||
"
|
||||
>
|
||||
<AlarmCards />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,31 +1,57 @@
|
||||
<script setup>
|
||||
import * as echarts from "echarts";
|
||||
import { onMounted, ref, markRaw } from "vue";
|
||||
import { onMounted, ref, markRaw, watch, onBeforeUnmount } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
option: Object,
|
||||
class: String,
|
||||
id: String,
|
||||
option: Object, // 圖表配置選項
|
||||
class: String, // 類名
|
||||
id: String, // 圖表ID
|
||||
});
|
||||
|
||||
let chart = ref(null);
|
||||
let chart = ref(null); // 用於儲存圖表實例
|
||||
let dom = ref(null); // 用於儲存DOM元素引用
|
||||
|
||||
// 初始化圖表的函數
|
||||
function init() {
|
||||
if (dom.value) {
|
||||
let echart = echarts;
|
||||
chart.value = markRaw(echart.init(document.getElementById(props.id)));
|
||||
chart.value.setOption(props.option);
|
||||
chart.value = markRaw(echart.init(dom.value)); // 初始化圖表
|
||||
chart.value.setOption(props.option); // 設定初始選項
|
||||
}
|
||||
}
|
||||
|
||||
// 當組件掛載完成後初始化圖表
|
||||
onMounted(() => {
|
||||
if (!chart.value && dom.value) {
|
||||
init();
|
||||
}
|
||||
});
|
||||
|
||||
// 監聽 props.option 的變化,重新設置圖表選項
|
||||
watch(
|
||||
() => props.option,
|
||||
(newOption) => {
|
||||
if (chart.value) {
|
||||
chart.value.setOption(newOption); // 更新圖表選項
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 當組件卸載時銷毀圖表,防止內存洩漏
|
||||
onBeforeUnmount(() => {
|
||||
if (chart.value) {
|
||||
chart.value.dispose(); // 銷毀圖表實例
|
||||
}
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
chart,
|
||||
chart, // 對外暴露 chart 變量
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :id="id" :class="class" class="border p-3"></div>
|
||||
<div :id="id" :class="class" ref="dom" style="height: 100%; width: 100%;"></div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
@ -17,7 +17,6 @@ import ForgeInfoModal from "./ForgeInfoModal.vue";
|
||||
import useAlarmStore from "@/stores/useAlarmStore";
|
||||
|
||||
const props = defineProps({
|
||||
fullScreen: Boolean,
|
||||
initialData: Object,
|
||||
cubeStyle: {
|
||||
type: Object,
|
||||
@ -116,25 +115,7 @@ const initForge = () => {
|
||||
);
|
||||
}
|
||||
);
|
||||
// viewer.addEventListener(
|
||||
// Autodesk.Viewing.SELECTION_CHANGED_EVENT,
|
||||
// (e) => {
|
||||
// console.log("selection changed!", e, e.dbIdArray[0]);
|
||||
// if (e.dbIdArray.length > 0) {
|
||||
// const pos = getModalPosition(viewer, e.dbIdArray[0]);
|
||||
// let currentValue = Object.values(subscribeData.value).find(
|
||||
// ({ forge_dbid }) => forge_dbid === e.dbIdArray[0]
|
||||
// );
|
||||
// viewer.getProperties(e.dbIdArray[0], function (e) {
|
||||
// console.log("Entire object response ", e);
|
||||
// console.log("Properties ", e.properties);
|
||||
|
||||
// });
|
||||
// console.log(Object.values(subscribeData.value), currentValue);
|
||||
// // getCurrentInfoModalData(pos, currentValue);
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -148,11 +129,6 @@ onMounted(() => {
|
||||
// 傳遞目前點擊資訊
|
||||
const currentInfoModalData = ref(null);
|
||||
const isMobile = (pointerType) => {
|
||||
// let flag =
|
||||
// /phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone/gi.test(
|
||||
// navigator.userAgent
|
||||
// );
|
||||
// console.log("isMobile", flag);
|
||||
return pointerType !== "mouse"; // is desktop
|
||||
};
|
||||
const getCurrentInfoModalData = (e, position, value) => {
|
||||
@ -178,24 +154,10 @@ onUnmounted(() => {
|
||||
|
||||
<template>
|
||||
<ForgeInfoModal :data="currentInfoModalData" />
|
||||
<div
|
||||
:class="
|
||||
twMerge(
|
||||
fullScreen
|
||||
? 'absolute top-0 left-0 w-screen h-full z-0'
|
||||
: 'w-full relative'
|
||||
)
|
||||
"
|
||||
>
|
||||
<div
|
||||
id="forge-preview"
|
||||
ref="forgeDom"
|
||||
:class="
|
||||
twMerge(
|
||||
'relative w-full h-full',
|
||||
fullScreen ? 'min-h-screen ' : 'min-h-[600px]'
|
||||
)
|
||||
"
|
||||
class="relative w-full h-full min-h-full"
|
||||
>
|
||||
<div v-show="heat_bar_isShow" class="absolute z-10 heatbar">
|
||||
<div class="w-40 flex justify-between text-[10px] mb-1">
|
||||
@ -253,7 +215,7 @@ onUnmounted(() => {
|
||||
<span v-else>{{ value.show_value }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style lang="css">
|
||||
|
@ -5,6 +5,7 @@ import NavbarBuilding from "./NavbarBuilding.vue";
|
||||
import Logo from "@/assets/img/logo.svg";
|
||||
import useGetCookie from "@/hooks/useGetCookie";
|
||||
import AlarmDrawer from "@/components/alarm/AlarmDrawer.vue";
|
||||
import NavbarLang from "./NavbarLang.vue";
|
||||
|
||||
const user = ref("");
|
||||
|
||||
@ -19,7 +20,7 @@ const src = import.meta.env.MODE === "production" ? "./logo.svg" : Logo;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="navbar bg-dark text-success py-2 mb-3 w-full relative z-50">
|
||||
<header class="navbar bg-dark text-success w-full relative z-50">
|
||||
<div class="navbar-start">
|
||||
<div class="dropdown">
|
||||
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden">
|
||||
@ -44,8 +45,9 @@ const src = import.meta.env.MODE === "production" ? "./logo.svg" : Logo;
|
||||
<NavbarItem />
|
||||
</ul>
|
||||
</div>
|
||||
<router-link to="/dashboard" class="rounded-lg pl-14 text-xl">
|
||||
<img :src="src" alt="logo" class="w-52" />
|
||||
<router-link to="/dashboard" class="rounded-lg pl-4 text-2xl font-bold text-white flex items-center">
|
||||
<img :src="src" alt="logo" class="w-8 me-1" />
|
||||
CviLux Group
|
||||
</router-link>
|
||||
<NavbarBuilding />
|
||||
</div>
|
||||
@ -55,6 +57,9 @@ const src = import.meta.env.MODE === "production" ? "./logo.svg" : Logo;
|
||||
</div>
|
||||
<div class="navbar-end mr-4">
|
||||
<ul class="menu-box">
|
||||
<li>
|
||||
<NavbarLang />
|
||||
</li>
|
||||
<li>
|
||||
<AlarmDrawer />
|
||||
</li>
|
||||
@ -80,7 +85,7 @@ const src = import.meta.env.MODE === "production" ? "./logo.svg" : Logo;
|
||||
<a
|
||||
href="/logout"
|
||||
class="flex flex-col justify-center items-center"
|
||||
>登出</a
|
||||
>{{ $t("sign_out") }}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
@ -102,17 +107,16 @@ const src = import.meta.env.MODE === "production" ? "./logo.svg" : Logo;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
|
||||
.menu-box .btn-group {
|
||||
background: transparent;
|
||||
width: 95px;
|
||||
}
|
||||
|
||||
.menu-box li {
|
||||
.menu-box > li {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.menu-box li:not(:last-child)::after {
|
||||
.menu-box > li:not(:last-child)::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
@ -122,7 +126,7 @@ const src = import.meta.env.MODE === "production" ? "./logo.svg" : Logo;
|
||||
display: block;
|
||||
width: 25px;
|
||||
height: 2px;
|
||||
background-color: #7cedc1;
|
||||
background-color: #93c0dc;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
|
@ -25,10 +25,10 @@ onMounted(() => {
|
||||
<div
|
||||
tabindex="0"
|
||||
role="button"
|
||||
class="text-white ml-8 text-2xl font-semiLight"
|
||||
class="text-white ml-8 text-xl font-semiLight "
|
||||
>
|
||||
{{ store.selectedBuilding?.full_name }}
|
||||
<font-awesome-icon :icon="['fas', 'angle-down']" />
|
||||
<font-awesome-icon :icon="['fas', 'angle-down']" class="ml-1"/>
|
||||
</div>
|
||||
<ul
|
||||
tabindex="0"
|
||||
|
@ -138,6 +138,6 @@ onMounted(() => {
|
||||
</template>
|
||||
<style lang="css" scoped>
|
||||
.router-link-active.router-link-exact-active {
|
||||
color: #7cedc1;
|
||||
color: #93c0dc;
|
||||
}
|
||||
</style>
|
||||
|
44
src/components/navbar/NavbarLang.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<script setup>
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
const { locale } = useI18n(); // 使用 I18n
|
||||
const language = ref(locale.value)
|
||||
// 切換語言
|
||||
const toggleLanguage = (lang) => {
|
||||
locale.value = lang;
|
||||
localStorage.setItem("CviLanguage", lang);
|
||||
language.value = lang;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dropdown dropdown-bottom dropdown-end">
|
||||
<button
|
||||
tabindex="0"
|
||||
type="button"
|
||||
class="flex flex-col justify-center items-center btn-group"
|
||||
>
|
||||
<span
|
||||
:class="`fi fi-${language} fis text-3xl rounded-full border-1 border-white`"
|
||||
></span>
|
||||
<span class="text-white">{{ $t("language") }}</span>
|
||||
</button>
|
||||
<ul
|
||||
tabindex="0"
|
||||
class="dropdown-content translate-y-2 z-[100] menu py-3 shadow rounded w-36 bg-[#4c625e] border text-center"
|
||||
>
|
||||
<li class="text-white" @click="toggleLanguage('tw')">
|
||||
<span><span class="fi fi-tw"></span>繁體中文</span>
|
||||
</li>
|
||||
<li class="text-white" @click="toggleLanguage('cn')">
|
||||
<span><span class="fi fi-cn"></span>简体中文</span>
|
||||
</li>
|
||||
<li class="text-white" @click="toggleLanguage('us')">
|
||||
<span><span class="fi fi-us"></span>English</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
222
src/config/cn.json
Normal file
@ -0,0 +1,222 @@
|
||||
{
|
||||
"language": "简体中文",
|
||||
"sign_out": "登出",
|
||||
"log_in": "登入",
|
||||
"account": "帐号",
|
||||
"password": "密码",
|
||||
"history": {
|
||||
"title": "历史资料",
|
||||
"building_name": "厂区",
|
||||
"device_name": "设备名称",
|
||||
"device_category": "设备类别",
|
||||
"category": "类别",
|
||||
"value": "数值",
|
||||
"date": "记录时间",
|
||||
"point": "点位",
|
||||
"combinations": "常用组合",
|
||||
"date_range": "日期区间",
|
||||
"time_range": "时间区间",
|
||||
"start_date": "起始日期",
|
||||
"start_time": "起始时间",
|
||||
"end_date": "结束日期",
|
||||
"end_time": "结束时间"
|
||||
},
|
||||
"alarm": {
|
||||
"title": "显示警告",
|
||||
"notify": "异常通知",
|
||||
"number": "异常编号",
|
||||
"category": "异常类别",
|
||||
"device_name": "设备名称",
|
||||
"message": "异常讯息",
|
||||
"confirm": "确认"
|
||||
},
|
||||
"alert": {
|
||||
"query_title": "告警纪录查询",
|
||||
"setting_title": "告警设定",
|
||||
"offnormal": "未复归",
|
||||
"normal": "已复归",
|
||||
"unacked": "未确认",
|
||||
"acked": "已确认",
|
||||
"30days": "近30天",
|
||||
"start_date": "起始日期",
|
||||
"end_date": "结束日期",
|
||||
"building_and_floor": "栋别-楼层",
|
||||
"uuid": "异常ID",
|
||||
"alarmClass": "异常类别",
|
||||
"device_name": "设备名称",
|
||||
"device_number": "设备编号",
|
||||
"date": "发生日期",
|
||||
"time": "发生时间",
|
||||
"error_msg": "异常原因",
|
||||
"ack_state": "Ack 确认",
|
||||
"repair_order_number": "派工 / 维运单号",
|
||||
"repair_order": "维修单",
|
||||
"form_number": "表单编号",
|
||||
"start_time": "预计开始时间",
|
||||
"item": "项目",
|
||||
"maintainance": "保养",
|
||||
"repair": "维修",
|
||||
"repair_item": "维修项目",
|
||||
"repair_item_code": "维修项目代码(设备编号)",
|
||||
"responsible_vendor": "负责厂商",
|
||||
"status": "状态",
|
||||
"not_completed": "未完成",
|
||||
"completed": "已完成",
|
||||
"worker_id": "工作人员编号",
|
||||
"notice": "注意事项",
|
||||
"result_description": "结果描述",
|
||||
"upload_file": "上传文件",
|
||||
"enable": "启用",
|
||||
"not_enabled": "不启用",
|
||||
"qualifications": "限定条件",
|
||||
"upper_limit": "上限",
|
||||
"lower_limit": "下限",
|
||||
"highDelay": "上限持续秒数",
|
||||
"lowDelay": "下限持续秒数",
|
||||
"warning_method": "警示方式",
|
||||
"warning_time": "警示时间",
|
||||
"operation": "功能",
|
||||
"alarm_settings": "异常设定",
|
||||
"time_setting": "时间设定",
|
||||
"yes": "是",
|
||||
"no": "否",
|
||||
"no_notify": "无通知",
|
||||
"notify_name": "姓名",
|
||||
"notify_phone": "手机号码",
|
||||
"notify_email": "email",
|
||||
"notify_items": "通知项目",
|
||||
"notify_list": "通知名单",
|
||||
"choose": "选择"
|
||||
},
|
||||
"operation": {
|
||||
"title": "运维管理",
|
||||
"project": "项目",
|
||||
"location": "位置",
|
||||
"uuid": "异常ID",
|
||||
"form_number": "表单编号",
|
||||
"device_name": "设备名称",
|
||||
"status": "狀態",
|
||||
"staff": "处理人员",
|
||||
"start_time": "预计开始时间",
|
||||
"upload": "档案上传",
|
||||
"finish_time": "完成时间",
|
||||
"operation": "功能",
|
||||
"vendor": "厂商",
|
||||
"contact_person": "联络人",
|
||||
"phone": "电话",
|
||||
"email": "email",
|
||||
"created_at": "建立日期",
|
||||
"maintainance": "保养",
|
||||
"repair": "维修",
|
||||
"company_info": "厂商资料",
|
||||
"repair_item": "维修项目",
|
||||
"repair_item_code": "维修项目代码(设备编号)",
|
||||
"responsible_vendor": "负责厂商",
|
||||
"not_completed": "未完成",
|
||||
"completed": "已完成",
|
||||
"worker_id": "工作人员编号",
|
||||
"notice": "注意事项",
|
||||
"result_description": "结果描述",
|
||||
"upload_file": "上传文件",
|
||||
"name": "姓名",
|
||||
"city": "城市",
|
||||
"address": "地址",
|
||||
"tax_id_number": "统一编号",
|
||||
"remark": "备注",
|
||||
"date": "日期",
|
||||
"serial": "单号",
|
||||
"today": "今天",
|
||||
"yesterday": "昨天",
|
||||
"start_created_at": "起始日期",
|
||||
"end_created_at": "结束日期",
|
||||
"enter_text": "请输入文字",
|
||||
"enter_serial": "请输入单号"
|
||||
},
|
||||
"graphManagement": {
|
||||
"title": "图资管理",
|
||||
"category": "图资类别",
|
||||
"new_category": "新类别",
|
||||
"index": "编号",
|
||||
"oriOrgName": "档案",
|
||||
"operation": "功能"
|
||||
},
|
||||
"assetManagement": {
|
||||
"title": "资产管理",
|
||||
"add_category": "新增类别",
|
||||
"system_name": "名称",
|
||||
"system_value": "代号",
|
||||
"system_parent": "所属大类",
|
||||
"device_number": "设备编号",
|
||||
"device_name": "设备名称",
|
||||
"asset_number": "资产编号",
|
||||
"floor": "设备位置",
|
||||
"add_floor": "新增楼层",
|
||||
"add_floor_text": "须先上传楼层地图",
|
||||
"device_coordinate": "图面标识",
|
||||
"brand_and_modal": "品牌 / 型号",
|
||||
"brand": "品牌",
|
||||
"modal": "型号",
|
||||
"company_and_contact": "厂商 / 联络人",
|
||||
"company": "负责厂商",
|
||||
"buying_date": "建置时间",
|
||||
"oriFile": "档案上传",
|
||||
"created_at": "建立时间",
|
||||
"operation": "功能",
|
||||
"device_list": "设备列表",
|
||||
"edit_device": "编辑设备",
|
||||
"add_device": "新增设备",
|
||||
"operate_text": "显示名称",
|
||||
"fill_text": "请由系统人员填写",
|
||||
"equipment_point": "设备点位",
|
||||
"add_sensor": "新增感测器",
|
||||
"associated_device": "关联设备",
|
||||
"choose": "选择",
|
||||
"index": "编号",
|
||||
"floor_plan": "平面图"
|
||||
},
|
||||
"accountManagement": {
|
||||
"account_title": "帐号管理",
|
||||
"role_title": "角色管理",
|
||||
"index": "编号",
|
||||
"name": "姓名",
|
||||
"account": "帐号",
|
||||
"password": "密码",
|
||||
"role": "角色",
|
||||
"role_name": "角色名称",
|
||||
"role_permissions": "角色权限",
|
||||
"role_permissions_setting": "角色权限设定",
|
||||
"permission_name": "权限名称",
|
||||
"basic_permissions": "基础权限",
|
||||
"production_permissions": "生产设定权限",
|
||||
"email": "email",
|
||||
"phone": "手机",
|
||||
"created_at": "建立时间",
|
||||
"operation": "功能",
|
||||
"name_placeholder": "请输入使用者名称",
|
||||
"role_placeholder": "请输入角色名称",
|
||||
"change_password": "变更密码",
|
||||
"choose": "选择"
|
||||
},
|
||||
"button": {
|
||||
"add": "新增",
|
||||
"cancel": "取消",
|
||||
"query": "查询",
|
||||
"search": "搜索",
|
||||
"view": "查看",
|
||||
"reset": "重置",
|
||||
"export": "导出",
|
||||
"enter_text": "输入文字后按下 Enter",
|
||||
"required": "必填",
|
||||
"submit": "确定",
|
||||
"edit": "修改",
|
||||
"delete": "删除",
|
||||
"deselect_all": "取消全选",
|
||||
"select_all": "全选",
|
||||
"phone_format": "请输入正确电话号码格式",
|
||||
"email_format": "请输入正确 Email 地址",
|
||||
"password_format": "密码长度至少8码,必须包含英文及数字",
|
||||
"start_time_placeholder": "请输入预计开始日期",
|
||||
"rename": "重新命名",
|
||||
"download": "下载"
|
||||
}
|
||||
}
|
222
src/config/tw.json
Normal file
@ -0,0 +1,222 @@
|
||||
{
|
||||
"language": "繁體中文",
|
||||
"sign_out": "登出",
|
||||
"log_in": "登入",
|
||||
"account": "帳號",
|
||||
"password": "密碼",
|
||||
"history": {
|
||||
"title": "歷史資料",
|
||||
"building_name": "廠區",
|
||||
"device_name": "設備名稱",
|
||||
"device_category": "設備類別",
|
||||
"category": "類別",
|
||||
"value": "數值",
|
||||
"date": "記錄時間",
|
||||
"point": "點位",
|
||||
"combinations": "常用組合",
|
||||
"date_range": "日期區間",
|
||||
"time_range": "時間區間",
|
||||
"start_date": "起始日期",
|
||||
"start_time": "起始時間",
|
||||
"end_date": "結束日期",
|
||||
"end_time": "結束時間"
|
||||
},
|
||||
"alarm": {
|
||||
"title": "顯示警告",
|
||||
"notify": "異常通知",
|
||||
"number": "異常編號",
|
||||
"category": "異常類別",
|
||||
"device_name": "設備名稱",
|
||||
"message": "異常訊息",
|
||||
"confirm": "確認"
|
||||
},
|
||||
"alert": {
|
||||
"query_title": "告警紀錄查詢",
|
||||
"setting_title": "告警設定",
|
||||
"offnormal": "未復歸",
|
||||
"normal": "已復歸",
|
||||
"unacked": "未確認",
|
||||
"acked": "已確認",
|
||||
"30days": "近30天",
|
||||
"start_date": "起始日期",
|
||||
"end_date": "結束日期",
|
||||
"building_and_floor": "棟別-樓層",
|
||||
"uuid": "異常ID",
|
||||
"alarmClass": "異常類別",
|
||||
"device_name": "設備名稱",
|
||||
"device_number": "設備編號",
|
||||
"date": "發生日期",
|
||||
"time": "發生時間",
|
||||
"error_msg": "異常原因",
|
||||
"ack_state": "Ack 確認",
|
||||
"repair_order_number": "派工 / 維運單號",
|
||||
"repair_order": "維修單",
|
||||
"form_number": "表單編號",
|
||||
"start_time": "預計開始時間",
|
||||
"item": "項目",
|
||||
"maintainance": "保養",
|
||||
"repair": "維修",
|
||||
"repair_item": "維修項目",
|
||||
"repair_item_code": "維修項目代碼(設備編號)",
|
||||
"responsible_vendor": "負責廠商",
|
||||
"status": "狀態",
|
||||
"not_completed": "未完成",
|
||||
"completed": "已完成",
|
||||
"worker_id": "工作人員編號",
|
||||
"notice": "注意事項",
|
||||
"result_description": "結果描述",
|
||||
"upload_file": "上傳檔案",
|
||||
"enable": "啟用",
|
||||
"not_enabled": "不啟用",
|
||||
"qualifications": "限定條件",
|
||||
"upper_limit": "上限",
|
||||
"lower_limit": "下限",
|
||||
"highDelay": "上限持續秒數",
|
||||
"lowDelay": "下限持續秒數",
|
||||
"warning_method": "警示方式",
|
||||
"warning_time": "警示時間",
|
||||
"operation": "功能",
|
||||
"alarm_settings": "異常設定",
|
||||
"time_setting": "時間設定",
|
||||
"yes": "是",
|
||||
"no": "否",
|
||||
"no_notify": "無通知",
|
||||
"notify_name": "姓名",
|
||||
"notify_phone": "手機號碼",
|
||||
"notify_email": "email",
|
||||
"notify_items": "通知項目",
|
||||
"notify_list": "通知名單",
|
||||
"choose": "選擇"
|
||||
},
|
||||
"operation": {
|
||||
"title": "運維管理",
|
||||
"project": "項目",
|
||||
"location": "位置",
|
||||
"uuid": "異常ID",
|
||||
"form_number": "表單編號",
|
||||
"device_name": "設備名稱",
|
||||
"status": "狀態",
|
||||
"staff": "處理人員",
|
||||
"start_time": "預計開始時間",
|
||||
"upload": "檔案上傳",
|
||||
"finish_time": "完成時間",
|
||||
"operation": "功能",
|
||||
"vendor": "廠商",
|
||||
"contact_person": "聯絡人",
|
||||
"phone": "電話",
|
||||
"email": "email",
|
||||
"created_at": "建立日期",
|
||||
"maintainance": "保養",
|
||||
"repair": "維修",
|
||||
"company_info": "廠商資料",
|
||||
"repair_item": "維修項目",
|
||||
"repair_item_code": "維修項目代碼(設備編號)",
|
||||
"responsible_vendor": "負責廠商",
|
||||
"not_completed": "未完成",
|
||||
"completed": "已完成",
|
||||
"worker_id": "工作人員編號",
|
||||
"notice": "注意事項",
|
||||
"result_description": "結果描述",
|
||||
"upload_file": "上傳檔案",
|
||||
"name": "姓名",
|
||||
"city": "城市",
|
||||
"address": "地址",
|
||||
"tax_id_number": "統一編號",
|
||||
"remark": "備注",
|
||||
"date": "日期",
|
||||
"serial": "單號",
|
||||
"today": "今天",
|
||||
"yesterday": "昨天",
|
||||
"start_created_at": "起始日期",
|
||||
"end_created_at": "結束日期",
|
||||
"enter_text": "請輸入文字",
|
||||
"enter_serial": "請輸入單號"
|
||||
},
|
||||
"graphManagement": {
|
||||
"title": "圖資管理",
|
||||
"category": "圖資類別",
|
||||
"new_category": "新類別",
|
||||
"index": "編號",
|
||||
"oriOrgName": "檔案",
|
||||
"operation": "功能"
|
||||
},
|
||||
"assetManagement": {
|
||||
"title": "資產管理",
|
||||
"add_category": "新增類別",
|
||||
"system_name": "名稱",
|
||||
"system_value": "代號",
|
||||
"system_parent": "所屬大類",
|
||||
"device_number": "設備編號",
|
||||
"device_name": "設備名稱",
|
||||
"asset_number": "資產編號",
|
||||
"floor": "設備位置",
|
||||
"add_floor": "新增樓層",
|
||||
"add_floor_text": "須先上傳樓層地圖",
|
||||
"device_coordinate": "圖面標識",
|
||||
"brand_and_modal": "品牌 / 型號",
|
||||
"brand": "品牌",
|
||||
"modal": "型號",
|
||||
"company_and_contact": "廠商 / 聯絡人",
|
||||
"company": "負責廠商",
|
||||
"buying_date": "購買日期",
|
||||
"oriFile": "檔案上傳",
|
||||
"created_at": "建立時間",
|
||||
"operation": "功能",
|
||||
"device_list": "設備列表",
|
||||
"edit_device": "編輯設備",
|
||||
"add_device": "新增設備",
|
||||
"operate_text": "顯示名稱",
|
||||
"fill_text": "請由系統人員填寫",
|
||||
"equipment_point": "設備點位",
|
||||
"add_sensor": "新增感測器",
|
||||
"associated_device": "關聯設備",
|
||||
"choose": "選擇",
|
||||
"index": "編號",
|
||||
"floor_plan": "平面圖"
|
||||
},
|
||||
"accountManagement": {
|
||||
"account_title": "帳號管理",
|
||||
"role_title": "角色管理",
|
||||
"index": "編號",
|
||||
"name": "姓名",
|
||||
"account": "帳號",
|
||||
"password": "密碼",
|
||||
"role": "角色",
|
||||
"role_name": "角色名稱",
|
||||
"role_permissions": "角色權限",
|
||||
"role_permissions_setting": "角色權限設定",
|
||||
"permission_name": "權限名稱",
|
||||
"basic_permissions": "基礎權限",
|
||||
"production_permissions": "生產設定權限",
|
||||
"email": "email",
|
||||
"phone": "手機",
|
||||
"created_at": "建立時間",
|
||||
"operation": "功能",
|
||||
"name_placeholder": "請輸入使用者名稱",
|
||||
"role_placeholder": "請輸入角色名稱",
|
||||
"change_password": "變更密碼",
|
||||
"choose": "選擇"
|
||||
},
|
||||
"button": {
|
||||
"add": "新增",
|
||||
"cancel": "取消",
|
||||
"query": "查詢",
|
||||
"search": "搜尋",
|
||||
"view": "查看",
|
||||
"reset": "重置",
|
||||
"export": "匯出",
|
||||
"enter_text": "輸入文字後按下 Enter",
|
||||
"required": "必填",
|
||||
"submit": "確定",
|
||||
"edit": "修改",
|
||||
"delete": "刪除",
|
||||
"deselect_all": "取消全選",
|
||||
"select_all": "全選",
|
||||
"phone_format": "請輸入正確電話號碼格式",
|
||||
"email_format": "請輸入正確的 Email 地址",
|
||||
"password_format": "密碼長度至少8碼,必須包含英文及數字",
|
||||
"start_time_placeholder": "請輸入預計開始日期",
|
||||
"rename": "重新命名",
|
||||
"download": "下載"
|
||||
}
|
||||
}
|
222
src/config/us.json
Normal file
@ -0,0 +1,222 @@
|
||||
{
|
||||
"language": "English",
|
||||
"sign_out": "Sign out",
|
||||
"log_in": "Log in",
|
||||
"account": "Account",
|
||||
"password": "Password",
|
||||
"history": {
|
||||
"title": "Historical Data",
|
||||
"building_name": "Building",
|
||||
"device_name": "Device Name",
|
||||
"device_category": "Device Category",
|
||||
"category": "Category",
|
||||
"value": "Value",
|
||||
"date": "Record Time",
|
||||
"point": "Point",
|
||||
"combinations": "Common combinations",
|
||||
"date_range": "Date range",
|
||||
"time_range": "Time interval",
|
||||
"start_date": "Start date",
|
||||
"start_time": "Start time",
|
||||
"end_date": "End date",
|
||||
"end_time": "End time"
|
||||
},
|
||||
"alarm": {
|
||||
"title": "Warning",
|
||||
"notify": "Notification",
|
||||
"number": "ID",
|
||||
"category": "Category",
|
||||
"device_name": "Device name",
|
||||
"message": "Message",
|
||||
"confirm": "Confirm"
|
||||
},
|
||||
"alert": {
|
||||
"query_title": "Alarm record query",
|
||||
"setting_title": "Alarm settings",
|
||||
"offnormal": "off normal",
|
||||
"normal": "normal",
|
||||
"unacked": "unacked",
|
||||
"acked": "acked",
|
||||
"30days": "Last 30 days",
|
||||
"start_date": "Start date",
|
||||
"end_date": "End date",
|
||||
"building_and_floor": "Building - floor",
|
||||
"uuid": "Exception ID",
|
||||
"alarmClass": "Exception Category",
|
||||
"device_name": "Device name",
|
||||
"device_number": "Device number",
|
||||
"date": "Occurrence date",
|
||||
"time": "Occurrence time",
|
||||
"error_msg": "Abnormal cause",
|
||||
"ack_state": "Ack Confirm",
|
||||
"repair_order_number": "Repair order number",
|
||||
"repair_order": "Repair order",
|
||||
"form_number": "Form Number",
|
||||
"start_time": "Estimated Start Time",
|
||||
"item": "Item",
|
||||
"maintainance": "Maintainance",
|
||||
"repair": "Repair",
|
||||
"repair_item": "Repair Item",
|
||||
"repair_item_code": "Repair Item Code (Device Number)",
|
||||
"responsible_vendor": "Responsible Vendor",
|
||||
"status": "Status",
|
||||
"not_completed": "Not completed",
|
||||
"completed": "Completed",
|
||||
"worker_id": "Worker ID",
|
||||
"notice": "Notice",
|
||||
"result_description": "Result Description",
|
||||
"upload_file": "Upload File",
|
||||
"enable": "Enable",
|
||||
"not_enabled": "Not enabled",
|
||||
"qualifications": "Qualifications",
|
||||
"upper_limit": "Upper limit",
|
||||
"lower_limit": "Lower limit",
|
||||
"highDelay": "Max duration (s)",
|
||||
"lowDelay": "Min duration (s)",
|
||||
"warning_method": "Warning method",
|
||||
"warning_time": "Warning time",
|
||||
"operation": "Function",
|
||||
"alarm_settings": "Abnormal alarm settings",
|
||||
"time_setting": "Time setting",
|
||||
"yes": "yes",
|
||||
"no": "no",
|
||||
"no_notify": "No notification",
|
||||
"notify_name": "Name",
|
||||
"notify_phone": "Phone number",
|
||||
"notify_email": "email",
|
||||
"notify_items": "Notification items",
|
||||
"notify_list": "Notification list",
|
||||
"choose": "choose"
|
||||
},
|
||||
"operation": {
|
||||
"title": "Operation and maintenance management",
|
||||
"project": "Project",
|
||||
"location": "Location",
|
||||
"uuid": "Exception ID",
|
||||
"form_number": "Form Number",
|
||||
"device_name": "Device name",
|
||||
"status": "Status",
|
||||
"staff": "Staff",
|
||||
"start_time": "Estimated start time",
|
||||
"upload": "File upload",
|
||||
"finish_time": "Completion time",
|
||||
"operation": "Function",
|
||||
"vendor": "company",
|
||||
"contact_person": "Contact person",
|
||||
"phone": "Phone",
|
||||
"email": "email",
|
||||
"created_at": "Creation date",
|
||||
"maintainance": "Maintainance",
|
||||
"repair": "Repair",
|
||||
"company_info": "Company Info",
|
||||
"repair_item": "Repair Item",
|
||||
"repair_item_code": "Repair Item Code (Device Number)",
|
||||
"responsible_vendor": "Responsible Vendor",
|
||||
"not_completed": "Not completed",
|
||||
"completed": "Completed",
|
||||
"worker_id": "Worker ID",
|
||||
"notice": "Notice",
|
||||
"result_description": "Result Description",
|
||||
"upload_file": "Upload File",
|
||||
"name": "Name",
|
||||
"city": "City",
|
||||
"address": "Address",
|
||||
"tax_id_number": "tax ID number",
|
||||
"remark": "Remark",
|
||||
"date": "Date",
|
||||
"serial": "Order number",
|
||||
"today": "Today",
|
||||
"yesterday": "Yesterday",
|
||||
"start_created_at": "Start date",
|
||||
"end_created_at": "End date",
|
||||
"enter_text": "Please enter text",
|
||||
"enter_serial": "Please enter the order number"
|
||||
},
|
||||
"graphManagement": {
|
||||
"title": "Data and Publication Management",
|
||||
"category": "Category",
|
||||
"new_category": "New category",
|
||||
"index": "serial number",
|
||||
"oriOrgName": "file",
|
||||
"operation": "Function"
|
||||
},
|
||||
"assetManagement": {
|
||||
"title": "Asset Management",
|
||||
"add_category": "Add category",
|
||||
"system_name": "Name",
|
||||
"system_value": "Code",
|
||||
"system_parent": "Category",
|
||||
"device_number": "Device number",
|
||||
"device_name": "Device name",
|
||||
"asset_number": "Asset number",
|
||||
"floor": "Location",
|
||||
"add_floor": "Add floor",
|
||||
"add_floor_text": "Floor map must be uploaded first",
|
||||
"device_coordinate": "Coordinate",
|
||||
"brand_and_modal": "Brand/Model",
|
||||
"brand": "Brand",
|
||||
"modal": "Model",
|
||||
"company_and_contact": "Company/Contact Person",
|
||||
"company": "Company",
|
||||
"buying_date": "Purchase time",
|
||||
"oriFile": "File upload",
|
||||
"created_at": "Creation time",
|
||||
"operation": "Function",
|
||||
"device_list": "Device list",
|
||||
"edit_device": "Edit device",
|
||||
"add_device": "Add device",
|
||||
"operate_text": "Display name",
|
||||
"fill_text": "Please fill it in by system personnel",
|
||||
"equipment_point": "Equipment point",
|
||||
"add_sensor": "Add new sensor",
|
||||
"associated_device": "Associated devices",
|
||||
"choose": "Choose",
|
||||
"index": "serial number",
|
||||
"floor_plan": "Floor plan"
|
||||
},
|
||||
"accountManagement": {
|
||||
"account_title": "Account Management",
|
||||
"role_title": "Role Management",
|
||||
"index": "serial number",
|
||||
"name": "Name",
|
||||
"account": "Account",
|
||||
"password": "Password",
|
||||
"role": "Role",
|
||||
"role_name": "Role name",
|
||||
"role_permissions": "Role permissions",
|
||||
"role_permissions_setting": "Role permissions settings",
|
||||
"permission_name": "Permission name",
|
||||
"basic_permissions": "Basic permissions",
|
||||
"production_permissions": "Production setting permissions",
|
||||
"email": "email",
|
||||
"phone": "Phone",
|
||||
"created_at": "Created time",
|
||||
"operation": "Function",
|
||||
"name_placeholder": "Please enter user name",
|
||||
"role_placeholder": "Please enter the role name",
|
||||
"change_password": "Change password",
|
||||
"choose": "Choose"
|
||||
},
|
||||
"button": {
|
||||
"add": "Add",
|
||||
"cancel": "Cancel",
|
||||
"query": "Query",
|
||||
"search": "Search",
|
||||
"view": "View",
|
||||
"reset": "Reset",
|
||||
"export": "Export",
|
||||
"enter_text": "After entering text, press Enter",
|
||||
"required": "Required",
|
||||
"submit": "Submit",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"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",
|
||||
"rename": "Rename",
|
||||
"download": "Download"
|
||||
}
|
||||
}
|
19
src/main.js
@ -5,6 +5,10 @@ import "./assets/btn.css";
|
||||
import "./assets/pagination.css";
|
||||
|
||||
import { createApp } from "vue";
|
||||
import { createI18n } from "vue-i18n";
|
||||
import tw from "./config/tw.json";
|
||||
import cn from "./config/cn.json";
|
||||
import us from "./config/us.json";
|
||||
import Antd from "ant-design-vue";
|
||||
import { createPinia } from "pinia";
|
||||
import App from "./App.vue";
|
||||
@ -13,17 +17,32 @@ 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";
|
||||
|
||||
import { focusPlugin } from "@/directives/focusPlugin";
|
||||
import { draggable } from "@/directives/draggable";
|
||||
const messages = {
|
||||
tw,
|
||||
cn,
|
||||
us,
|
||||
};
|
||||
|
||||
const storedLanguage = localStorage.getItem("CviLanguage") || "us";
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: storedLanguage,
|
||||
fallbackLocale: 'us',
|
||||
messages,
|
||||
});
|
||||
const app = createApp(App);
|
||||
app.use(createPinia());
|
||||
app.use(router);
|
||||
app.use(Antd);
|
||||
app.use(i18n);
|
||||
|
||||
// 组装成一个对象
|
||||
const allGlobalComponents = { SvgIcon, FontAwesomeIcon };
|
||||
|
@ -7,6 +7,7 @@ import AccountManagement from "@/views/accountManagement/AccountManagement.vue";
|
||||
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 Login from "@/views/login/Login.vue";
|
||||
import useUserInfoStore from "@/stores/useUserInfoStore";
|
||||
import useGetCookie from "@/hooks/useGetCookie";
|
||||
@ -63,6 +64,11 @@ const router = createRouter({
|
||||
name: "productSetting",
|
||||
component: ProductSetting,
|
||||
},
|
||||
{
|
||||
path: "/energyManagement",
|
||||
name: "energyManagement",
|
||||
component: EnergyManagement,
|
||||
},
|
||||
{
|
||||
path: "/mytestfile/mjm",
|
||||
name: "mytestfile",
|
||||
|
@ -4,7 +4,9 @@ import AssetTable from "./components/AssetTable.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1 class="text-2xl font-extrabold mb-2">資產管理</h1>
|
||||
<h1 class="text-2xl font-extrabold mb-2">
|
||||
{{ $t("assetManagement.title") }}
|
||||
</h1>
|
||||
<AssetSubList />
|
||||
<AssetTable />
|
||||
</template>
|
||||
|
@ -52,12 +52,12 @@ const deleteItem = async (id) => {
|
||||
|
||||
<template>
|
||||
<div class="mt-4">
|
||||
<AssetSubListAddModal
|
||||
<!-- <AssetSubListAddModal
|
||||
:openModal="openModal"
|
||||
:onCancel="onCancel"
|
||||
:getData="getSubList"
|
||||
:editRecord="editRecord"
|
||||
/>
|
||||
/> -->
|
||||
<ButtonConnectedGroup
|
||||
:items="items"
|
||||
:onclick="
|
||||
|
@ -3,7 +3,8 @@ import { ref, defineProps, onMounted, watch } from "vue";
|
||||
import * as yup from "yup";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import { getAssetMainList, postAssetSubList } from "@/apis/asset";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
openModal: Function,
|
||||
onCancel: Function,
|
||||
@ -18,9 +19,9 @@ const getMainSystems = async () => {
|
||||
};
|
||||
|
||||
let subSysSchema = yup.object({
|
||||
system_key: yup.string().required("必填"), // 名稱
|
||||
system_value: yup.string().required("必填"), // 代稱
|
||||
system_parent_id: yup.number().required("必填"), // 大類id
|
||||
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
|
||||
});
|
||||
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset } =
|
||||
@ -73,11 +74,11 @@ const onOk = async () => {
|
||||
|
||||
<template>
|
||||
<button class="btn btn-sm btn-success mr-3" @click.stop.prevent="openModal">
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />新增
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
||||
</button>
|
||||
<Modal
|
||||
id="asset_add_sub_item"
|
||||
title="新增類別"
|
||||
:title="t('assetManagement.add_category')"
|
||||
:open="open"
|
||||
:onCancel="onCancel"
|
||||
width="300"
|
||||
@ -85,7 +86,7 @@ const onOk = async () => {
|
||||
<template #modalContent>
|
||||
<form ref="form" class="mt-5 flex flex-col items-center">
|
||||
<Input name="system_key" :value="formState">
|
||||
<template #topLeft>名稱</template>
|
||||
<template #topLeft>{{ $t("assetManagement.system_name") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.system_key }}
|
||||
@ -93,7 +94,7 @@ const onOk = async () => {
|
||||
>
|
||||
</Input>
|
||||
<Input name="system_value" :value="formState">
|
||||
<template #topLeft>代號</template>
|
||||
<template #topLeft>{{ $t("assetManagement.system_value") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.system_value }}
|
||||
@ -108,7 +109,9 @@ const onOk = async () => {
|
||||
:value="formState"
|
||||
selectClass="border-info focus-within:border-info"
|
||||
>
|
||||
<template #topLeft>所屬大類</template>
|
||||
<template #topLeft>{{
|
||||
$t("assetManagement.system_parent")
|
||||
}}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.system_parent_id }}
|
||||
@ -123,14 +126,14 @@ const onOk = async () => {
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
取消
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.prevent="onOk"
|
||||
>
|
||||
確定
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
|
@ -1,12 +1,13 @@
|
||||
<script setup>
|
||||
import { getAssetList, getAssetSingle, deleteAssetItem } from "@/apis/asset";
|
||||
import { onMounted, ref, watch, inject } from "vue";
|
||||
import { onMounted, ref, watch, inject, computed } from "vue";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import AssetTableAddModal from "./AssetTableAddModal.vue";
|
||||
import { getOperationCompanyList } from "@/apis/operation";
|
||||
import { getAssetFloorList } from "@/apis/asset";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast } = inject("app_toast");
|
||||
|
||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||
@ -50,58 +51,60 @@ onMounted(async () => {
|
||||
getAssetData();
|
||||
});
|
||||
|
||||
const columns = [
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: "設備編號",
|
||||
title: t("assetManagement.device_number"),
|
||||
key: "device_number",
|
||||
class: "break-all",
|
||||
},
|
||||
{
|
||||
title: "設備名稱",
|
||||
title: t("assetManagement.device_name"),
|
||||
key: "full_name",
|
||||
filter: true,
|
||||
class: "break-all",
|
||||
},
|
||||
{
|
||||
title: "資產編號",
|
||||
title: t("assetManagement.asset_number"),
|
||||
key: "asset_number",
|
||||
},
|
||||
{
|
||||
title: "設備位置",
|
||||
title: t("assetManagement.floor"),
|
||||
key: "floor",
|
||||
filter: true,
|
||||
sort: true,
|
||||
},
|
||||
{
|
||||
title: "圖面標識",
|
||||
title: t("assetManagement.device_coordinate"),
|
||||
key: "device_coordinate",
|
||||
},
|
||||
{
|
||||
title: "品牌 / 型號",
|
||||
title: t("assetManagement.brand_and_modal"),
|
||||
key: "brandAndModal",
|
||||
},
|
||||
{
|
||||
title: "廠商 / 聯絡人",
|
||||
title: t("assetManagement.company_and_contact"),
|
||||
key: "companyAndContact",
|
||||
},
|
||||
{
|
||||
title: "建置時間",
|
||||
title: t("assetManagement.buying_date"),
|
||||
key: "buying_date",
|
||||
sort: true,
|
||||
},
|
||||
{
|
||||
title: "檔案上傳",
|
||||
title: t("assetManagement.oriFile"),
|
||||
key: "oriFile",
|
||||
},
|
||||
{
|
||||
title: "建立時間",
|
||||
title: t("assetManagement.created_at"),
|
||||
key: "created_at",
|
||||
sort: true,
|
||||
},
|
||||
{
|
||||
title: "功能",
|
||||
title: t("assetManagement.operation"),
|
||||
key: "operation",
|
||||
width: 200,
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
watch(
|
||||
() => searchParams,
|
||||
@ -156,7 +159,7 @@ const remove = async (id) => {
|
||||
|
||||
<template>
|
||||
<div class="flex justify-start items-center mt-10">
|
||||
<h3 class="text-xl mr-5">設備列表</h3>
|
||||
<h3 class="text-xl mr-5">{{ $t("assetManagement.device_list") }}</h3>
|
||||
<AssetTableAddModal
|
||||
:openModal="openModal"
|
||||
:onCancel="onCancel"
|
||||
@ -208,13 +211,13 @@ const remove = async (id) => {
|
||||
class="btn btn-sm btn-success text-white mr-2"
|
||||
@click.stop.prevent="() => edit(record.main_id)"
|
||||
>
|
||||
修改
|
||||
{{ $t("button.edit") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-error text-white"
|
||||
@click.stop.prevent="() => remove(record.main_id)"
|
||||
>
|
||||
刪除
|
||||
{{ $t("button.delete") }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
|
@ -103,11 +103,11 @@ const closeModal = () => {
|
||||
|
||||
<template>
|
||||
<button class="btn btn-sm btn-success mr-3" @click.stop.prevent="openModal">
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />新增
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
||||
</button>
|
||||
<Modal
|
||||
id="asset_add_table_item"
|
||||
:title="editRecord?.main_id ? '編輯設備' : '新增設備'"
|
||||
:title="editRecord?.main_id ? $t('assetManagement.edit_device') : $t('assetManagement.add_device')"
|
||||
:open="open"
|
||||
:onCancel="closeModal"
|
||||
width="1600"
|
||||
@ -130,14 +130,14 @@ const closeModal = () => {
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="closeModal"
|
||||
>
|
||||
取消
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.prevent="onOk"
|
||||
>
|
||||
確定
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
|
@ -7,6 +7,8 @@ import { getOperationCompanyList } from "@/apis/operation";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import OperationTableModal from "@/views/operation/components/OperationTableModal.vue";
|
||||
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();
|
||||
@ -51,7 +53,7 @@ const buying_date = ref([
|
||||
key: "buying_date",
|
||||
value: "",
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: "購買日期",
|
||||
placeholder: t("assetManagement.buying_date"),
|
||||
},
|
||||
]);
|
||||
|
||||
@ -99,7 +101,7 @@ const openCompanyAddModal = () => {
|
||||
<template>
|
||||
<!-- information -->
|
||||
<Input :value="formState" width="270" name="full_name">
|
||||
<template #topLeft>設備名稱</template>
|
||||
<template #topLeft>{{ $t("assetManagement.device_name") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.full_name }}
|
||||
@ -107,7 +109,7 @@ const openCompanyAddModal = () => {
|
||||
></Input
|
||||
>
|
||||
<Input :value="formState" width="270" name="operate_text">
|
||||
<template #topLeft>顯示名稱</template>
|
||||
<template #topLeft>{{ $t("assetManagement.operate_text") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.operate_text }}
|
||||
@ -115,7 +117,9 @@ const openCompanyAddModal = () => {
|
||||
></Input
|
||||
>
|
||||
<Input :value="formState" width="270" name="device_number">
|
||||
<template #topLeft>Tag_Name (請由系統人員填寫)</template>
|
||||
<template #topLeft
|
||||
>Tag_Name ({{ $t("assetManagement.fill_text") }})</template
|
||||
>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.device_number }}
|
||||
@ -136,7 +140,7 @@ const openCompanyAddModal = () => {
|
||||
name="device_coordinate"
|
||||
:disabled="true"
|
||||
>
|
||||
<template #topLeft>平面圖座標</template>
|
||||
<template #topLeft>{{ $t("assetManagement.device_coordinate") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.device_coordinate }}
|
||||
@ -144,7 +148,7 @@ const openCompanyAddModal = () => {
|
||||
></Input
|
||||
>
|
||||
<Input :value="formState" width="270" name="asset_number">
|
||||
<template #topLeft>固定資產編號</template>
|
||||
<template #topLeft>{{ $t("assetManagement.asset_number") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.asset_number }}
|
||||
@ -152,7 +156,7 @@ const openCompanyAddModal = () => {
|
||||
></Input
|
||||
>
|
||||
<DateGroup :items="buying_date" width="270" :withLine="false">
|
||||
<template #topLeft>購買日期</template>
|
||||
<template #topLeft>{{ $t("assetManagement.buying_date") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.buying_date }}
|
||||
@ -160,7 +164,7 @@ const openCompanyAddModal = () => {
|
||||
>
|
||||
</DateGroup>
|
||||
<Input :value="formState" width="270" name="brand">
|
||||
<template #topLeft>品牌</template>
|
||||
<template #topLeft>{{ $t("assetManagement.brand") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.brand }}
|
||||
@ -168,7 +172,7 @@ const openCompanyAddModal = () => {
|
||||
></Input
|
||||
>
|
||||
<Input :value="formState" width="270" name="device_model">
|
||||
<template #topLeft>型號</template>
|
||||
<template #topLeft>{{ $t("assetManagement.modal") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.device_model }}
|
||||
@ -184,7 +188,7 @@ const openCompanyAddModal = () => {
|
||||
:options="companyOptions"
|
||||
:required="true"
|
||||
>
|
||||
<template #topLeft>負責廠商</template>
|
||||
<template #topLeft>{{ $t("assetManagement.company") }}</template>
|
||||
</Select>
|
||||
<OperationTableModal type="asset" />
|
||||
<button
|
||||
@ -192,7 +196,7 @@ const openCompanyAddModal = () => {
|
||||
class="btn btn-success btn-sm ml-2 mt-7"
|
||||
@click="openCompanyAddModal"
|
||||
>
|
||||
新增
|
||||
{{ $t("button.add") }}
|
||||
</button>
|
||||
</div>
|
||||
<Upload
|
||||
@ -203,7 +207,7 @@ const openCompanyAddModal = () => {
|
||||
class="col-span-2"
|
||||
:baseUrl="FILE_BASEURL"
|
||||
>
|
||||
<template #topLeft>上傳檔案</template>
|
||||
<template #topLeft>{{ $t("assetManagement.oriFile") }}</template>
|
||||
</Upload>
|
||||
<AssetTableModalLeftInfoIoT />
|
||||
</template>
|
||||
|
@ -1,11 +1,12 @@
|
||||
<script setup>
|
||||
import { getAssetIOTList } from "@/apis/asset";
|
||||
import { ref, onMounted, inject, watch } from "vue";
|
||||
|
||||
import { ref, computed, inject, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { formState } = inject("asset_table_modal_form");
|
||||
const tableColumns = [
|
||||
const tableColumns = computed(() => [
|
||||
{
|
||||
title: "編號",
|
||||
title: t("assetManagement.index"),
|
||||
key: "index",
|
||||
},
|
||||
{
|
||||
@ -13,7 +14,7 @@ const tableColumns = [
|
||||
key: "tag",
|
||||
},
|
||||
{
|
||||
title: "名稱",
|
||||
title: t("assetManagement.system_name"),
|
||||
key: "full_name",
|
||||
},
|
||||
// {
|
||||
@ -21,15 +22,15 @@ const tableColumns = [
|
||||
// key: "status",
|
||||
// },
|
||||
{
|
||||
title: "功能",
|
||||
title: t("assetManagement.operation"),
|
||||
key: "operation",
|
||||
width: 100,
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
const modalColumns = [
|
||||
const modalColumns = computed(() => [
|
||||
{
|
||||
title: "勾選",
|
||||
title: t("assetManagement.choose"),
|
||||
key: "check",
|
||||
},
|
||||
{
|
||||
@ -37,10 +38,10 @@ const modalColumns = [
|
||||
key: "tag",
|
||||
},
|
||||
{
|
||||
title: "名稱",
|
||||
title: t("assetManagement.system_name"),
|
||||
key: "full_name",
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
// TODO: 抓取IOT
|
||||
const iotData = ref([]);
|
||||
@ -111,13 +112,15 @@ const deleteItem = (id) => {
|
||||
<!-- IoT -->
|
||||
<div class="flex flex-col mt-8 col-span-2">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="label-text-alt text-lg">設備點位</span>
|
||||
<span class="label-text-alt text-lg">
|
||||
{{ $t("assetManagement.equipment_point") }}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-success"
|
||||
@click.stop.prevent="openModal"
|
||||
>
|
||||
新增感測器
|
||||
{{ $t("assetManagement.add_sensor") }}
|
||||
</button>
|
||||
</div>
|
||||
<Table
|
||||
@ -147,7 +150,7 @@ const deleteItem = (id) => {
|
||||
</div>
|
||||
<Modal
|
||||
id="asset_add_IoT_item"
|
||||
title="關聯設備"
|
||||
:title="t('assetManagement.associated_device')"
|
||||
:onCancel="onCancel"
|
||||
width="900"
|
||||
>
|
||||
@ -175,14 +178,14 @@ const deleteItem = (id) => {
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
取消
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.prevent="onOk"
|
||||
>
|
||||
確定
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template></Modal
|
||||
>
|
||||
|
@ -5,7 +5,8 @@ import { getAssetFloorList, postAssetFloor } 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 FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||
|
||||
const { updateRightFields, formErrorMsg, formState } = inject(
|
||||
@ -109,8 +110,8 @@ const FloorFormState = ref({
|
||||
});
|
||||
|
||||
const floorScheme = yup.object({
|
||||
building_tag: yup.string().required("必填"),
|
||||
full_name: yup.string().required("必填"),
|
||||
building_tag: yup.string().required(t("button.required")),
|
||||
full_name: yup.string().required(t("button.required")),
|
||||
floorFile: yup.array(),
|
||||
});
|
||||
|
||||
@ -166,14 +167,14 @@ const onCancel = () => {
|
||||
:options="floors"
|
||||
:isBottomLabelExist="false"
|
||||
>
|
||||
<template #topLeft>樓層</template>
|
||||
<template #topLeft>{{ t("assetManagement.floor") }}</template>
|
||||
</Select>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-success mt-10 ml-5"
|
||||
@click="openModal"
|
||||
>
|
||||
新增樓層
|
||||
{{ t("assetManagement.add_floor") }}
|
||||
</button>
|
||||
</div>
|
||||
<!-- <button type="button" class="btn btn-success mt-10">確認座標</button> -->
|
||||
@ -194,14 +195,19 @@ const onCancel = () => {
|
||||
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"
|
||||
>
|
||||
<p class="text-2xl">須先上傳樓層地圖</p>
|
||||
<p class="text-2xl">{{ t("assetManagement.add_floor_text") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Modal id="asset_add_floor" title="平面圖" :onCancel="onCancel" width="400">
|
||||
<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>名稱</template>
|
||||
<template #topLeft>{{ $t("assetManagement.system_name") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ floorFormErrorMsg.full_name }}
|
||||
@ -215,7 +221,7 @@ const onCancel = () => {
|
||||
:multiple="false"
|
||||
class="col-span-2"
|
||||
>
|
||||
<template #topLeft>上傳檔案</template>
|
||||
<template #topLeft>{{ $t("assetManagement.oriFile") }}</template>
|
||||
</Upload>
|
||||
</form>
|
||||
</template>
|
||||
@ -225,10 +231,10 @@ const onCancel = () => {
|
||||
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
|
||||
>
|
||||
|
@ -2,36 +2,39 @@
|
||||
import ButtonGroup from "@/components/customUI/ButtonGroup.vue";
|
||||
import Account from "./components/Account.vue";
|
||||
import Role from "./components/Role.vue";
|
||||
import { computed, onMounted, ref, provide, onBeforeMount } from "vue";
|
||||
import { computed, watch, onBeforeMount } from "vue";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t, locale } = useI18n();
|
||||
const changeComponent = (e, item) => {
|
||||
changeActiveBtn(item);
|
||||
};
|
||||
|
||||
const { items, changeActiveBtn, setItems } = useActiveBtn();
|
||||
|
||||
onBeforeMount(() => {
|
||||
const initializeItems = () => {
|
||||
setItems([
|
||||
{
|
||||
title: "帳號管理",
|
||||
title: t("accountManagement.account_title"),
|
||||
key: "account",
|
||||
active: true,
|
||||
component: Account,
|
||||
},
|
||||
{
|
||||
title: "角色管理",
|
||||
title: t("accountManagement.role_title"),
|
||||
key: "role",
|
||||
active: false,
|
||||
component: Role,
|
||||
},
|
||||
// {
|
||||
// title: "角色權限",
|
||||
// key: "auth",
|
||||
// active: false,
|
||||
// component: Auth,
|
||||
// },
|
||||
]);
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
initializeItems();
|
||||
});
|
||||
|
||||
watch(locale, () => {
|
||||
initializeItems();
|
||||
});
|
||||
|
||||
const activeTab = computed(() => {
|
||||
@ -40,7 +43,9 @@ const activeTab = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1 class="text-2xl font-extrabold mb-2">帳號管理</h1>
|
||||
<h1 class="text-2xl font-extrabold mb-2">
|
||||
{{ $t("accountManagement.account_title") }}
|
||||
</h1>
|
||||
<ButtonGroup
|
||||
:items="items"
|
||||
:withLine="true"
|
||||
|
@ -6,46 +6,47 @@ import {
|
||||
getAccountOneUser,
|
||||
delAccount,
|
||||
} from "@/apis/account";
|
||||
import { onMounted, ref, inject } from "vue";
|
||||
import { onMounted, ref, inject, computed } from "vue";
|
||||
import AccountPasswordModal from "./AccountPasswordModal.vue";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast } = inject("app_toast");
|
||||
|
||||
const columns = [
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: "編號",
|
||||
title: t("accountManagement.index"),
|
||||
key: "index",
|
||||
},
|
||||
{
|
||||
title: "姓名",
|
||||
title: t("accountManagement.name"),
|
||||
key: "full_name",
|
||||
filter: true,
|
||||
},
|
||||
{
|
||||
title: "帳號",
|
||||
title: t("accountManagement.account"),
|
||||
key: "account",
|
||||
},
|
||||
{
|
||||
title: "角色",
|
||||
title: t("accountManagement.role"),
|
||||
key: "role_full_name",
|
||||
},
|
||||
{
|
||||
title: "email",
|
||||
title: t("accountManagement.email"),
|
||||
key: "email",
|
||||
},
|
||||
{
|
||||
title: "手機",
|
||||
title: t("accountManagement.phone"),
|
||||
key: "phone",
|
||||
},
|
||||
{
|
||||
title: "建立時間",
|
||||
title: t("accountManagement.created_at"),
|
||||
key: "created_at",
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
title: t("accountManagement.operation"),
|
||||
key: "operation",
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
const dataSource = ref([]);
|
||||
const loading = ref(false);
|
||||
@ -145,17 +146,18 @@ const removeAccount = async (id) => {
|
||||
class="btn btn-success mr-3"
|
||||
@click.stop.prevent="() => openModal(null)"
|
||||
>
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />新增
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />
|
||||
{{ $t("button.add") }}
|
||||
</button>
|
||||
<div class="flex items-center mb-8">
|
||||
<Input
|
||||
placeholder="請輸入使用者名稱"
|
||||
:placeholder="t('accountManagement.name_placeholder')"
|
||||
name="Full_name"
|
||||
:value="searchData"
|
||||
class="mr-3"
|
||||
/>
|
||||
<Input
|
||||
placeholder="請輸入角色名稱"
|
||||
:placeholder="t('accountManagement.role_placeholder')"
|
||||
name="Role_full_name"
|
||||
:value="searchData"
|
||||
/>
|
||||
@ -163,13 +165,13 @@ const removeAccount = async (id) => {
|
||||
class="btn btn-outline-success ml-5"
|
||||
@click.stop.prevent="onSearch"
|
||||
>
|
||||
搜尋
|
||||
{{ $t("button.search") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-outline-success mx-4"
|
||||
@click.stop.prevent="onReset"
|
||||
>
|
||||
重置
|
||||
{{ $t("button.reset") }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@ -180,7 +182,7 @@ const removeAccount = async (id) => {
|
||||
class="btn btn-sm btn-success text-white mr-2"
|
||||
@click.stop.prevent="() => getUser(record.userinfo_guid)"
|
||||
>
|
||||
修改
|
||||
{{ $t("button.edit") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-info text-white mr-2"
|
||||
@ -192,13 +194,13 @@ const removeAccount = async (id) => {
|
||||
})
|
||||
"
|
||||
>
|
||||
變更密碼
|
||||
{{ $t("accountManagement.change_password") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-error text-white"
|
||||
@click.stop.prevent="() => removeAccount(record.userinfo_guid)"
|
||||
>
|
||||
刪除
|
||||
{{ $t("button.delete") }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
|
@ -4,7 +4,8 @@ import * as yup from "yup";
|
||||
import "yup-phone-lite";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import { getAccountRoleList, postAccountUser } from "@/apis/account";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast } = inject("app_toast");
|
||||
|
||||
const props = defineProps({
|
||||
@ -15,18 +16,21 @@ const props = defineProps({
|
||||
|
||||
let userSchema = ref(
|
||||
yup.object({
|
||||
Account: yup.string().required("必填"),
|
||||
Name: yup.string().required("必填"),
|
||||
Email: yup.string().email("請輸入正確的 Email 地址").required("必填"),
|
||||
Phone: yup.string().phone("TW", "請輸入正確電話號碼格式").required("必填"),
|
||||
RoleId: yup.string().required("必填"),
|
||||
Account: yup.string().required(t("button.required")),
|
||||
Name: yup.string().required(t("button.required")),
|
||||
Email: yup
|
||||
.string()
|
||||
.email(t("button.email_format"))
|
||||
.required(t("button.required")),
|
||||
Phone: yup
|
||||
.string()
|
||||
.phone("TW", t("button.phone_format"))
|
||||
.required(t("button.required")),
|
||||
RoleId: yup.string().required(t("button.required")),
|
||||
Password: yup
|
||||
.string()
|
||||
.required("必填")
|
||||
.matches(
|
||||
/^(?=.{8})(?=.*[A-Za-z])(?=.*\d)/g,
|
||||
"密碼長度至少8碼,必須包含英文及數字"
|
||||
),
|
||||
.required(t("button.required"))
|
||||
.matches(/^(?=.{8})(?=.*[A-Za-z])(?=.*\d)/g, t("button.password_format")),
|
||||
})
|
||||
);
|
||||
|
||||
@ -40,32 +44,38 @@ watch(
|
||||
let newSchema;
|
||||
if (Boolean(newValue?.Id)) {
|
||||
newSchema = yup.object({
|
||||
Account: yup.string().required("必填"),
|
||||
Name: yup.string().required("必填"),
|
||||
Email: yup.string().email("請輸入正確的 Email 地址").required("必填"),
|
||||
Account: yup.string().required(t("button.required")),
|
||||
Name: yup.string().required(t("button.required")),
|
||||
Email: yup
|
||||
.string()
|
||||
.email(t("button.email_format"))
|
||||
.required(t("button.required")),
|
||||
Phone: yup
|
||||
.string()
|
||||
.phone("TW", "請輸入正確電話號碼格式")
|
||||
.required("必填"),
|
||||
RoleId: yup.string().required("必填"),
|
||||
.phone("TW", t("button.phone_format"))
|
||||
.required(t("button.required")),
|
||||
RoleId: yup.string().required(t("button.required")),
|
||||
Password: yup.string(),
|
||||
});
|
||||
} else {
|
||||
newSchema = yup.object({
|
||||
Account: yup.string().required("必填"),
|
||||
Name: yup.string().required("必填"),
|
||||
Email: yup.string().email("請輸入正確的 Email 地址").required("必填"),
|
||||
Account: yup.string().required(t("button.required")),
|
||||
Name: yup.string().required(t("button.required")),
|
||||
Email: yup
|
||||
.string()
|
||||
.email(t("button.email_format"))
|
||||
.required(t("button.required")),
|
||||
Phone: yup
|
||||
.string()
|
||||
.phone("TW", "請輸入正確電話號碼格式")
|
||||
.required("必填"),
|
||||
RoleId: yup.string().required("必填"),
|
||||
.phone("TW", t("button.phone_format"))
|
||||
.required(t("button.required")),
|
||||
RoleId: yup.string().required(t("button.required")),
|
||||
Password: yup
|
||||
.string()
|
||||
.required("必填")
|
||||
.required(t("button.required"))
|
||||
.matches(
|
||||
/^(?=.{8})(?=.*[A-Za-z])(?=.*\d)/g,
|
||||
"密碼長度至少8碼,必須包含英文及數字"
|
||||
t("button.password_format")
|
||||
),
|
||||
});
|
||||
}
|
||||
@ -111,7 +121,7 @@ const onOk = async () => {
|
||||
<template>
|
||||
<Modal
|
||||
id="account_user_modal"
|
||||
:title="formState?.Id ? '編輯' : '新增'"
|
||||
:title="formState?.Id ? t('button.edit') : t('button.add')"
|
||||
:onCancel="onCancel"
|
||||
width="710"
|
||||
>
|
||||
@ -123,7 +133,7 @@ const onOk = async () => {
|
||||
name="Account"
|
||||
:disabled="Boolean(formState?.Id)"
|
||||
>
|
||||
<template #topLeft>帳號</template>
|
||||
<template #topLeft>{{ $t("accountManagement.account") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.Account }}
|
||||
@ -137,7 +147,7 @@ const onOk = async () => {
|
||||
name="Password"
|
||||
type="password"
|
||||
>
|
||||
<template #topLeft>密碼</template>
|
||||
<template #topLeft>{{ $t("accountManagement.password") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.Password }}
|
||||
@ -145,7 +155,7 @@ const onOk = async () => {
|
||||
></Input
|
||||
>
|
||||
<Input :value="formState" class="my-2" name="Name">
|
||||
<template #topLeft>姓名</template>
|
||||
<template #topLeft>{{ $t("accountManagement.name") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.Name }}
|
||||
@ -154,7 +164,7 @@ const onOk = async () => {
|
||||
</Input>
|
||||
|
||||
<Input class="my-2" :value="formState" name="Email">
|
||||
<template #topLeft>Email</template>
|
||||
<template #topLeft>{{ $t("accountManagement.email") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.Email }}
|
||||
@ -162,7 +172,7 @@ const onOk = async () => {
|
||||
>
|
||||
</Input>
|
||||
<Input class="my-2" :value="formState" name="Phone">
|
||||
<template #topLeft>電話</template>
|
||||
<template #topLeft>{{ $t("accountManagement.phone") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.Phone }}
|
||||
@ -178,7 +188,9 @@ const onOk = async () => {
|
||||
Attribute="full_name"
|
||||
:options="roleList"
|
||||
>
|
||||
<template #topLeft>角色權限</template>
|
||||
<template #topLeft>{{
|
||||
$t("accountManagement.role_permissions")
|
||||
}}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.RoleId }}
|
||||
@ -193,14 +205,14 @@ const onOk = async () => {
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
取消
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.stop.prevent="onOk"
|
||||
>
|
||||
確定
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
|
@ -4,7 +4,8 @@ import * as yup from "yup";
|
||||
import "yup-phone-lite";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import { changePassword } from "@/apis/account";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast } = inject("app_toast");
|
||||
|
||||
const props = defineProps({
|
||||
@ -16,7 +17,7 @@ const formState = ref({
|
||||
});
|
||||
|
||||
let userSchema = yup.object({
|
||||
Password: yup.string().required("必填"),
|
||||
Password: yup.string().required(t("button.required")),
|
||||
});
|
||||
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset } =
|
||||
@ -41,7 +42,7 @@ const onOk = async () => {
|
||||
<template>
|
||||
<Modal
|
||||
id="account_user_password_modal"
|
||||
title="變更密碼"
|
||||
:title="t('accountManagement.change_password')"
|
||||
:onCancel="onCancel"
|
||||
width="710"
|
||||
>
|
||||
@ -49,7 +50,9 @@ const onOk = async () => {
|
||||
<p class="mt-10 text-3xl">{{ account.Name }}</p>
|
||||
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
||||
<Input :value="formState" class="my-2" name="Password" type="password">
|
||||
<template #topLeft>變更密碼</template>
|
||||
<template #topLeft>{{
|
||||
$t("accountManagement.change_password")
|
||||
}}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.Password }}
|
||||
@ -64,14 +67,14 @@ const onOk = async () => {
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
取消
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.stop.prevent="onOk"
|
||||
>
|
||||
確定
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
|
@ -3,27 +3,28 @@ import Table from "@/components/customUI/Table.vue";
|
||||
import Input from "@/components/customUI/Input.vue";
|
||||
import { getAccountRoleList, delRole } from "@/apis/account";
|
||||
import RoleAuthModal from "./RoleAuthModal.vue";
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
const columns = [
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: "編號",
|
||||
title: t("accountManagement.index"),
|
||||
key: "index",
|
||||
},
|
||||
{
|
||||
title: "名稱",
|
||||
title: t("accountManagement.role_name"),
|
||||
key: "full_name",
|
||||
},
|
||||
{
|
||||
title: "建立時間",
|
||||
title: t("accountManagement.created_at"),
|
||||
key: "created_at",
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
title: t("accountManagement.operation"),
|
||||
key: "operation",
|
||||
width: 400,
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
const dataSource = ref([]);
|
||||
const loading = ref(false);
|
||||
@ -97,7 +98,7 @@ const onSearch = () => {
|
||||
<template #beforeTable>
|
||||
<div class="flex items-center mb-8">
|
||||
<Input
|
||||
placeholder="請輸入角色名稱"
|
||||
:placeholder="t('accountManagement.role_placeholder')"
|
||||
name="Full_name"
|
||||
:value="searchRole"
|
||||
/>
|
||||
@ -105,10 +106,10 @@ const onSearch = () => {
|
||||
class="btn btn-outline-info mx-3"
|
||||
@click.stop.prevent="onSearch"
|
||||
>
|
||||
搜尋
|
||||
{{ $t("button.search") }}
|
||||
</button>
|
||||
<button class="btn btn-success ml-10" @click.stop.prevent="add">
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />新增
|
||||
<font-awesome-icon :icon="['fas', 'plus']" /> {{ $t("button.add") }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@ -119,19 +120,19 @@ const onSearch = () => {
|
||||
class="btn btn-sm btn-info text-white mr-2"
|
||||
@click.stop.prevent="() => view(record)"
|
||||
>
|
||||
查看
|
||||
{{ $t("button.view") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white mr-2"
|
||||
@click.stop.prevent="() => edit(record)"
|
||||
>
|
||||
修改
|
||||
{{ $t("button.edit") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-error text-white"
|
||||
@click.stop.prevent="() => remove(record.role_guid)"
|
||||
>
|
||||
刪除
|
||||
{{ $t("button.delete") }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
|
@ -8,9 +8,10 @@ import {
|
||||
getAccountRoleAuthList,
|
||||
postAccountRole,
|
||||
} from "@/apis/account";
|
||||
import { defineProps, onMounted, ref, watch } from "vue";
|
||||
import { defineProps, onMounted, ref, watch, computed } from "vue";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
selectedRole: String,
|
||||
cancelModal: Function,
|
||||
@ -20,20 +21,20 @@ const props = defineProps({
|
||||
|
||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
||||
|
||||
const columns = [
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: "選擇",
|
||||
title: t("accountManagement.choose"),
|
||||
key: "SaveCheckAuth",
|
||||
},
|
||||
{
|
||||
title: "編號",
|
||||
title: t("accountManagement.index"),
|
||||
key: "index",
|
||||
},
|
||||
{
|
||||
title: "權限名稱",
|
||||
title: t("accountManagement.index"),
|
||||
key: "subName",
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
const rawData = ref([]);
|
||||
const dataSource = ref([]);
|
||||
@ -58,13 +59,13 @@ watch(SaveCheckAuth, (newValue, oldValue) => {
|
||||
if (newValue.includes("PF10") && !oldValue.includes("PF10")) {
|
||||
setItems([
|
||||
{
|
||||
title: "基礎權限",
|
||||
title: t("accountManagement.basic_permissions"),
|
||||
key: "PF",
|
||||
active: selectedBtn.value.authCode === "PF",
|
||||
authCode: "PF",
|
||||
},
|
||||
{
|
||||
title: "生產設定權限",
|
||||
title: t("accountManagement.production_permissions"),
|
||||
key: "PS",
|
||||
active: selectedBtn.value.authCode === "PS",
|
||||
authCode: "PS",
|
||||
@ -77,7 +78,7 @@ watch(SaveCheckAuth, (newValue, oldValue) => {
|
||||
);
|
||||
setItems([
|
||||
{
|
||||
title: "基礎權限",
|
||||
title: t("accountManagement.basic_permissions"),
|
||||
key: "PF",
|
||||
active: true,
|
||||
authCode: "PF",
|
||||
@ -133,7 +134,7 @@ watch(
|
||||
onMounted(() => {
|
||||
setItems([
|
||||
{
|
||||
title: "基礎權限",
|
||||
title: t("accountManagement.basic_permissions"),
|
||||
key: "PF",
|
||||
active: true,
|
||||
authCode: "PF",
|
||||
@ -144,13 +145,17 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal id="role_auth_modal" title="角色權限設定" :onCancel="onCancel">
|
||||
<Modal
|
||||
id="role_auth_modal"
|
||||
:title="t('accountManagement.role_permissions_setting')"
|
||||
:onCancel="onCancel"
|
||||
>
|
||||
<template #modalContent>
|
||||
<form class="modal_form" ref="form">
|
||||
<Input
|
||||
v-if="!disabled"
|
||||
label="角色名稱"
|
||||
placeholder="請輸入角色名稱"
|
||||
:label="t('accountManagement.role_name')"
|
||||
:placeholder="t('accountManagement.role_placeholder')"
|
||||
class="mt-5"
|
||||
name="Name"
|
||||
:value="selectedRole"
|
||||
@ -189,18 +194,11 @@ onMounted(() => {
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button
|
||||
class="btn btn-sm bg-dark btn-neutral text-white mr-2"
|
||||
@click="onCancel"
|
||||
>
|
||||
關閉
|
||||
<button class="btn btn-outline-success mr-2" @click="onCancel">
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button
|
||||
v-if="!disabled"
|
||||
class="btn btn-sm btn-success text-white"
|
||||
@click="onOk"
|
||||
>
|
||||
確認
|
||||
<button v-if="!disabled" class="btn btn-outline-success" @click="onOk">
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
|
@ -2,30 +2,39 @@
|
||||
import ButtonGroup from "@/components/customUI/ButtonGroup.vue";
|
||||
import AlertQuery from "./components/AlertQuery/AlertQuery.vue";
|
||||
import AlertSetting from "./components/AlertSetting/AlertSetting.vue";
|
||||
import { computed, onMounted, ref, provide, onBeforeMount } from "vue";
|
||||
import { computed, watch, onBeforeMount } from "vue";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t, locale } = useI18n();
|
||||
const changeComponent = (e, item) => {
|
||||
changeActiveBtn(item);
|
||||
};
|
||||
|
||||
const { items, changeActiveBtn, setItems } = useActiveBtn();
|
||||
|
||||
onBeforeMount(() => {
|
||||
const initializeItems = () => {
|
||||
setItems([
|
||||
{
|
||||
title: "告警記錄查詢",
|
||||
title: t("alert.query_title"),
|
||||
key: "Query",
|
||||
active: true,
|
||||
component: AlertQuery,
|
||||
},
|
||||
{
|
||||
title: "告警設定",
|
||||
title: t("alert.setting_title"),
|
||||
key: "Setting",
|
||||
active: false,
|
||||
component: AlertSetting,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
initializeItems();
|
||||
});
|
||||
|
||||
watch(locale, () => {
|
||||
initializeItems();
|
||||
});
|
||||
|
||||
const activeTab = computed(() => {
|
||||
|
@ -1,41 +0,0 @@
|
||||
<script setup>
|
||||
import { defineProps, ref, watch, inject, computed } from "vue";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
|
||||
const { searchParams } = useSearchParam();
|
||||
|
||||
const isSearchDisabled = ref(true);
|
||||
const search = async () => {
|
||||
|
||||
};
|
||||
|
||||
const exportFile = async () => {
|
||||
|
||||
};
|
||||
|
||||
const submitBtns = computed(() => [
|
||||
{
|
||||
title: "查詢",
|
||||
key: "submit",
|
||||
active: false,
|
||||
onClick: search,
|
||||
disabled: isSearchDisabled.value,
|
||||
},
|
||||
{
|
||||
title: "匯出",
|
||||
key: "export",
|
||||
active: false,
|
||||
onClick: exportFile,
|
||||
disabled: isSearchDisabled.value,
|
||||
},
|
||||
]);
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ButtonGroup :items="submitBtns" :withLine="false" class="ml-2 mr-8 xl:mr-10" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -5,9 +5,7 @@ import AlertTableModal from "./AlertTableModal.vue";
|
||||
import { ref, provide, onMounted } from "vue";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import useAlarmData from "@/hooks/baja/useAlarmData";
|
||||
import {
|
||||
getAlertFormId,
|
||||
} from "@/apis/alert";
|
||||
import { getAlertFormId } from "@/apis/alert";
|
||||
import {
|
||||
getOperationDeviceList,
|
||||
getOperationCompanyList,
|
||||
@ -55,7 +53,9 @@ const getFormId = async (uuid) => {
|
||||
};
|
||||
|
||||
const getModalDevList = async () => {
|
||||
const sub_system_tags = searchParams.value.system_tag.map(tag => tag.split('_')[1]);
|
||||
const sub_system_tags = searchParams.value.system_tag.map(
|
||||
(tag) => tag.split("_")[1]
|
||||
);
|
||||
const res = await getOperationDeviceList({
|
||||
list_sub_system_tag: sub_system_tags,
|
||||
device_building_tag: store.buildings[0].building_tag,
|
||||
@ -102,18 +102,20 @@ const search = async () => {
|
||||
async (result) => {
|
||||
alarmData.value = result.data;
|
||||
// 確保所有 alarm 都包含 formId
|
||||
alarmData.value = alarmData.value.map(alarm => ({
|
||||
alarmData.value = alarmData.value.map((alarm) => ({
|
||||
...alarm,
|
||||
formId: null // 初始設置 formId 為 null
|
||||
formId: null, // 初始設置 formId 為 null
|
||||
}));
|
||||
|
||||
const uuids = alarmData.value.map(alarm => ({ uuid: alarm.uuid }));
|
||||
const uuids = alarmData.value.map((alarm) => ({ uuid: alarm.uuid }));
|
||||
const formIds = await getFormId(uuids);
|
||||
|
||||
if (Array.isArray(formIds)) {
|
||||
formIds.forEach((form) => {
|
||||
if (form && form.uuid) {
|
||||
const index = alarmData.value.findIndex(alarm => alarm.uuid === form.uuid);
|
||||
const index = alarmData.value.findIndex(
|
||||
(alarm) => alarm.uuid === form.uuid
|
||||
);
|
||||
if (index !== -1) {
|
||||
alarmData.value[index].formId = form.formId || null;
|
||||
}
|
||||
@ -150,11 +152,17 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
provide("alert_modal", { model_data, search, updateEditRecord });
|
||||
provide("alert_table", { openModal, updateEditRecord, dataSource, search, tableLoading });
|
||||
provide("alert_table", {
|
||||
openModal,
|
||||
updateEditRecord,
|
||||
dataSource,
|
||||
search,
|
||||
tableLoading,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1 class="text-2xl font-extrabold mb-2">告警紀錄查詢</h1>
|
||||
<h1 class="text-2xl font-extrabold mb-2">{{ $t("alert.query_title") }}</h1>
|
||||
<AlertTableModal :editRecord="editRecord" />
|
||||
<div>
|
||||
<AlertSearch />
|
||||
|
@ -15,7 +15,7 @@ const { search } = inject("alert_table");
|
||||
<AlertSearchNormalBtns />
|
||||
<AlertSearchAckBtns />
|
||||
<AlertSearchTimeRange />
|
||||
<button class="btn btn-success ml-8" @click.stop.prevent="search">查詢</button>
|
||||
<button class="btn btn-success ml-8" @click.stop.prevent="search">{{ $t("button.query")}}</button>
|
||||
</div>
|
||||
<div class="w-full flex flex-wrap items-center justify-start">
|
||||
<AlertSearchTypesButton />
|
||||
|
@ -2,7 +2,8 @@
|
||||
import { onMounted, watch } from "vue";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t, locale } = useI18n();
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
const {
|
||||
items,
|
||||
@ -11,19 +12,27 @@ const {
|
||||
selectedBtn
|
||||
} = useActiveBtn();
|
||||
|
||||
onMounted(() => {
|
||||
const initializeItems = () => {
|
||||
setItems([
|
||||
{
|
||||
title: "未確認",
|
||||
title: t("alert.unacked"),
|
||||
key: "unacked",
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
title: "已確認",
|
||||
title: t("alert.acked"),
|
||||
key: "acked",
|
||||
active: false,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initializeItems();
|
||||
});
|
||||
|
||||
watch(locale, () => {
|
||||
initializeItems();
|
||||
});
|
||||
|
||||
watch(
|
||||
|
@ -2,25 +2,32 @@
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import { onMounted, watch } from "vue";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t, locale } = useI18n();
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
|
||||
const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
|
||||
|
||||
onMounted(() => {
|
||||
const initializeItems = () => {
|
||||
setItems([
|
||||
{
|
||||
title: "未復歸",
|
||||
title: t("alert.offnormal"),
|
||||
key: "offnormal",
|
||||
active: true,
|
||||
},
|
||||
{
|
||||
title: "已賦歸",
|
||||
title: t("alert.normal"),
|
||||
key: "normal",
|
||||
active: false,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initializeItems();
|
||||
});
|
||||
|
||||
watch(locale, () => {
|
||||
initializeItems();
|
||||
});
|
||||
|
||||
// 監聽按鈕變化
|
||||
|
@ -2,21 +2,26 @@
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t, locale } = useI18n();
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
|
||||
const dateRange = ref([
|
||||
{
|
||||
key: "start_at",
|
||||
value: searchParams.value.start_created_at ? dayjs(searchParams.value.start_created_at).valueOf() : dayjs().subtract(30, 'day').valueOf(),
|
||||
value: searchParams.value.start_created_at
|
||||
? dayjs(searchParams.value.start_created_at).valueOf()
|
||||
: dayjs().subtract(30, "day").valueOf(),
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: "起始日期",
|
||||
placeholder: t("alert.start_date"),
|
||||
},
|
||||
{
|
||||
key: "end_at",
|
||||
value: searchParams.value.end_created_at ? dayjs(searchParams.value.end_created_at).valueOf() : dayjs().valueOf(),
|
||||
value: searchParams.value.end_created_at
|
||||
? dayjs(searchParams.value.end_created_at).valueOf()
|
||||
: dayjs().valueOf(),
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: "結束日期",
|
||||
placeholder: t("alert.end_date"),
|
||||
},
|
||||
]);
|
||||
|
||||
@ -24,15 +29,15 @@ const changeTimeRange = () => {
|
||||
const newRange = [
|
||||
{
|
||||
key: "start_at",
|
||||
value: dayjs().subtract(30, 'day').startOf('day').valueOf(), // 向前推30天並設置為當天的00:00:00.000
|
||||
value: dayjs().subtract(30, "day").startOf("day").valueOf(), // 向前推30天並設置為當天的00:00:00.000
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: "起始日期",
|
||||
placeholder: t("alert.start_date"),
|
||||
},
|
||||
{
|
||||
key: "end_at",
|
||||
value: dayjs().endOf('day').valueOf(), // 設置為今日的23:59:59.999
|
||||
value: dayjs().endOf("day").valueOf(), // 設置為今日的23:59:59.999
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: "結束日期",
|
||||
placeholder: t("alert.end_date"),
|
||||
},
|
||||
];
|
||||
|
||||
@ -47,7 +52,10 @@ const changeTimeRange = () => {
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化日期範圍
|
||||
if (!searchParams.value.start_created_at || !searchParams.value.end_created_at) {
|
||||
if (
|
||||
!searchParams.value.start_created_at ||
|
||||
!searchParams.value.end_created_at
|
||||
) {
|
||||
changeTimeRange();
|
||||
}
|
||||
});
|
||||
@ -58,18 +66,29 @@ watch(
|
||||
() => {
|
||||
changeParams({
|
||||
...searchParams.value,
|
||||
start_created_at: dayjs(dateRange.value[0].value).startOf('day').valueOf(),
|
||||
end_created_at: dayjs(dateRange.value[1].value).endOf('day').valueOf(),
|
||||
start_created_at: dayjs(dateRange.value[0].value)
|
||||
.startOf("day")
|
||||
.valueOf(),
|
||||
end_created_at: dayjs(dateRange.value[1].value).endOf("day").valueOf(),
|
||||
});
|
||||
},
|
||||
{ deep: true } // 確保在初始化立即觸發
|
||||
);
|
||||
|
||||
watch(locale, () => {
|
||||
dateRange.value[0].placeholder = t("alert.start_date");
|
||||
dateRange.value[1].placeholder = t("alert.end_date");
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center">
|
||||
<button type="button" class="btn btn-outline-success mr-3" @click="changeTimeRange">
|
||||
近30天
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-success mr-3"
|
||||
@click="changeTimeRange"
|
||||
>
|
||||
{{ $t("alert.30days") }}
|
||||
</button>
|
||||
<DateGroup :items="dateRange" :withLine="true" />
|
||||
</div>
|
||||
|
@ -3,7 +3,8 @@ import useBuildingStore from "@/stores/useBuildingStore";
|
||||
import Checkbox from "@/components/customUI/Checkbox.vue";
|
||||
import { computed, onMounted, ref, watch } from "vue";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
|
||||
const store = useBuildingStore();
|
||||
@ -65,7 +66,7 @@ watch(searchParams, (newValue) => {
|
||||
class="btn btn-success mr-4"
|
||||
@click.stop.prevent="changeCheckedItem"
|
||||
>
|
||||
{{ checkedItem.length === store.subSys.length ? "取消全選" : "全選" }}
|
||||
{{ checkedItem.length === store.subSys.length ? t("button.deselect_all") : t("button.select_all") }}
|
||||
</button>
|
||||
<Checkbox
|
||||
v-for="sub in store.subSys"
|
||||
|
@ -1,53 +1,53 @@
|
||||
<script setup>
|
||||
import { inject } from "vue";
|
||||
import {
|
||||
postChgAck,
|
||||
} from "@/apis/alert";
|
||||
import { inject, computed } from "vue";
|
||||
import { postChgAck } from "@/apis/alert";
|
||||
import { Button } from "ant-design-vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { dataSource, openModal, search, tableLoading } = inject("alert_table");
|
||||
|
||||
const columns = [
|
||||
const columns = computed(() => [
|
||||
{
|
||||
key: "building_tag",
|
||||
title: "棟別-樓層",
|
||||
title: t("alert.building_and_floor"),
|
||||
},
|
||||
{
|
||||
key: "uuid",
|
||||
title: "異常ID",
|
||||
title: t("alert.uuid"),
|
||||
},
|
||||
{
|
||||
key: "alarmClass",
|
||||
title: "異常類別",
|
||||
title: t("alert.alarmClass"),
|
||||
},
|
||||
{
|
||||
key: "full_name",
|
||||
title: "設備名稱",
|
||||
title: t("alert.device_name"),
|
||||
},
|
||||
{
|
||||
key: "device_number",
|
||||
title: "設備編號",
|
||||
title: t("alert.device_number"),
|
||||
},
|
||||
{
|
||||
key: "timestamp_date",
|
||||
title: "發生日期",
|
||||
title: t("alert.date"),
|
||||
},
|
||||
{
|
||||
key: "timestamp_time",
|
||||
title: "發生時間",
|
||||
title: t("alert.time"),
|
||||
},
|
||||
{
|
||||
key: "msg",
|
||||
title: "異常原因",
|
||||
title: t("alert.error_msg"),
|
||||
},
|
||||
{
|
||||
key: "ackState",
|
||||
title: "Ack 確認",
|
||||
title: t("alert.ack_state"),
|
||||
},
|
||||
{
|
||||
key: "repairOrder",
|
||||
title: "派工 / 維運單號",
|
||||
title: t("alert.repair_order_number"),
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
const chgAck = async (devUuid) => {
|
||||
const res = await postChgAck(devUuid);
|
||||
@ -55,7 +55,6 @@ const chgAck = async (devUuid) => {
|
||||
search?.();
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -63,8 +62,11 @@ const chgAck = async (devUuid) => {
|
||||
<template #bodyCell="{ record, column, index }">
|
||||
<template v-if="column.key === 'ackState'">
|
||||
<template v-if="record.ackState === 'Unacked'">
|
||||
<button class="btn btn-sm btn-success text-white whitespace-nowrap mr-2" @click.stop.prevent="() => chgAck(record.uuid)">
|
||||
未確認
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white whitespace-nowrap mr-2"
|
||||
@click.stop.prevent="() => chgAck(record.uuid)"
|
||||
>
|
||||
{{ $t("alert.unacked") }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
@ -72,11 +74,15 @@ const chgAck = async (devUuid) => {
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="column.key === 'repairOrder'">
|
||||
<button class="btn btn-sm btn-success text-white whitespace-nowrap mr-2"
|
||||
@click.stop.prevent="() => openModal(record)">
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white whitespace-nowrap mr-2"
|
||||
@click.stop.prevent="() => openModal(record)"
|
||||
>
|
||||
<span v-if="record.formId">{{ record.formId }}</span>
|
||||
<span v-else>
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />維修單
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />{{
|
||||
$t("alert.repair_order_number")
|
||||
}}
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
|
@ -1,13 +1,12 @@
|
||||
<script setup>
|
||||
import { ref, defineProps, watch, inject } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import {
|
||||
postOperationRecord,
|
||||
} from "@/apis/alert";
|
||||
import { postOperationRecord } from "@/apis/alert";
|
||||
import * as yup from "yup";
|
||||
import "yup-phone-lite";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||
|
||||
const props = defineProps({
|
||||
@ -22,12 +21,11 @@ const dateItem = ref([
|
||||
name: "start_time",
|
||||
value: dayjs(),
|
||||
dateFormat: "yyyy-MM-dd",
|
||||
placeholder: "請輸入預計開始日期",
|
||||
placeholder: t("alert.start_time"),
|
||||
},
|
||||
]);
|
||||
|
||||
const formState = ref(
|
||||
{
|
||||
const formState = ref({
|
||||
formId: null,
|
||||
uuid: "",
|
||||
work_type: 2,
|
||||
@ -40,8 +38,7 @@ const formState = ref(
|
||||
notice: "",
|
||||
description: "",
|
||||
lorf: [],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const { model_data, updateEditRecord, search } = inject("alert_modal") || {
|
||||
model_data: [],
|
||||
@ -50,12 +47,12 @@ const { model_data, updateEditRecord, search } = inject("alert_modal") || {
|
||||
};
|
||||
|
||||
let alertSchema = yup.object({
|
||||
start_time: yup.date().required("必填"),
|
||||
fix_do: yup.string().required("必填"),
|
||||
fix_do_code: yup.string().required("必填"),
|
||||
fix_firm: yup.string().required("必填"),
|
||||
status: yup.number().required("必填"),
|
||||
work_person_id: yup.string().required("必填"),
|
||||
start_time: yup.date().required(t("button.required")),
|
||||
fix_do: yup.string().required(t("button.required")),
|
||||
fix_do_code: yup.string().required(t("button.required")),
|
||||
fix_firm: yup.string().required(t("button.required")),
|
||||
status: yup.number().required(t("button.required")),
|
||||
work_person_id: yup.string().required(t("button.required")),
|
||||
notice: yup.string().nullable(true),
|
||||
description: yup.string().nullable(true),
|
||||
});
|
||||
@ -71,14 +68,15 @@ const onOk = async () => {
|
||||
const formData = new FormData(form.value);
|
||||
formData.delete("oriFile");
|
||||
|
||||
formState.value?.lorf.forEach(
|
||||
(file, index) => {
|
||||
formState.value?.lorf.forEach((file, index) => {
|
||||
formData.append(`lorf[${index}].id`, file.id ? file.id : "");
|
||||
formData.append(`lorf[${index}].file`, file.id ? null : file);
|
||||
formData.append(`lorf[${index}].save_file_name`,file.id ? file.save_file_name : "");
|
||||
formData.append(`lorf[${index}].ori_file_name`, file.name);
|
||||
}
|
||||
formData.append(
|
||||
`lorf[${index}].save_file_name`,
|
||||
file.id ? file.save_file_name : ""
|
||||
);
|
||||
formData.append(`lorf[${index}].ori_file_name`, file.name);
|
||||
});
|
||||
|
||||
formData.append(
|
||||
"start_time",
|
||||
@ -140,7 +138,9 @@ watch(
|
||||
}
|
||||
// 處理 start_time
|
||||
if (key === "start_time") {
|
||||
formState.value.start_time = value ? dayjs(value).format("YYYY-MM-DD") : dayjs().format("YYYY-MM-DD");
|
||||
formState.value.start_time = value
|
||||
? dayjs(value).format("YYYY-MM-DD")
|
||||
: dayjs().format("YYYY-MM-DD");
|
||||
dateItem.value[0].value = value;
|
||||
}
|
||||
// 維修項目
|
||||
@ -159,85 +159,144 @@ watch(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal id="alert_action_item" title="維修單" :onCancel="onCancel" width="710">
|
||||
<Modal
|
||||
id="alert_action_item"
|
||||
:title="t('alert.repair_order')"
|
||||
:onCancel="onCancel"
|
||||
width="710"
|
||||
>
|
||||
<template #modalContent>
|
||||
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
||||
<Input v-if="formState.value && formState.value.formId" class="my-2" :value="formState" name="formId" readonly>
|
||||
<template #topLeft>表單編號</template>
|
||||
<Input
|
||||
v-if="formState.value && formState.value.formId"
|
||||
class="my-2"
|
||||
:value="formState"
|
||||
name="formId"
|
||||
readonly
|
||||
>
|
||||
<template #topLeft>{{ $t("alert.form_number") }}</template>
|
||||
</Input>
|
||||
<Input :value="formState" class="my-2" name="uuid" readonly>
|
||||
<template #topLeft>異常編號</template>
|
||||
<template #topLeft>{{ $t("alert.uuid") }}</template>
|
||||
</Input>
|
||||
<DateGroup class="my-2" :items="dateItem" inputClass="w-full shadow-none" :required="true">
|
||||
<template #topLeft>預計開始時間</template>
|
||||
<DateGroup
|
||||
class="my-2"
|
||||
:items="dateItem"
|
||||
inputClass="w-full shadow-none"
|
||||
:required="true"
|
||||
>
|
||||
<template #topLeft>{{ $t("alert.start_time") }}</template>
|
||||
<template #bottomLeft>
|
||||
<span class="text-error text-base">
|
||||
{{ formErrorMsg.start_time }}
|
||||
</span>
|
||||
</template>
|
||||
</DateGroup>
|
||||
<Select :value="formState" class="my-2" selectClass="border-info focus-within:border-info" name="work_type"
|
||||
Attribute="title" :options="[
|
||||
<Select
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
selectClass="border-info focus-within:border-info"
|
||||
name="work_type"
|
||||
Attribute="title"
|
||||
:options="[
|
||||
{
|
||||
key: 1,
|
||||
value: 1,
|
||||
title: '保養',
|
||||
title: $t('alert.maintenance'),
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
value: 2,
|
||||
title: '維修',
|
||||
title: $t('alert.repair'),
|
||||
},
|
||||
]" :required="true" :disabled="true">
|
||||
<template #topLeft>項目</template>
|
||||
]"
|
||||
:required="true"
|
||||
:disabled="true"
|
||||
>
|
||||
<template #topLeft>{{ $t("alert.item") }}</template>
|
||||
</Select>
|
||||
<Input class="my-2" :value="formState" name="fix_do" :required="true">
|
||||
<template #topLeft>維修項目</template>
|
||||
<template #bottomLeft><span class="text-error text-base">
|
||||
<template #topLeft>{{ $t("alert.repair_item") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.fix_do }}
|
||||
</span>
|
||||
</template>
|
||||
</Input>
|
||||
<Select :value="formState" class="my-2" selectClass="border-info focus-within:border-info" name="fix_do_code"
|
||||
Attribute="device_name" :options="model_data.model_devList" :required="true" :disabled="true">
|
||||
<template #topLeft>維修項目代碼(設備編號)</template>
|
||||
<template #bottomLeft><span class="text-error text-base">
|
||||
<Select
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
selectClass="border-info focus-within:border-info"
|
||||
name="fix_do_code"
|
||||
Attribute="device_name"
|
||||
:options="model_data.model_devList"
|
||||
:required="true"
|
||||
:disabled="true"
|
||||
>
|
||||
<template #topLeft>{{ $t("repair_item_code") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.fix_do_code }}
|
||||
</span></template>
|
||||
</span></template
|
||||
>
|
||||
</Select>
|
||||
<Select :value="formState" class="my-2" selectClass="border-info focus-within:border-info" name="fix_firm"
|
||||
Attribute="name" :options="model_data.model_companyList" :required="true">
|
||||
<template #topLeft>負責廠商</template>
|
||||
<template #bottomLeft><span class="text-error text-base">
|
||||
<Select
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
selectClass="border-info focus-within:border-info"
|
||||
name="fix_firm"
|
||||
Attribute="name"
|
||||
:options="model_data.model_companyList"
|
||||
:required="true"
|
||||
>
|
||||
<template #topLeft>{{ $t("responsible_vendor") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.fix_firm }}
|
||||
</span></template>
|
||||
</span></template
|
||||
>
|
||||
</Select>
|
||||
<RadioGroup class="my-2" name="status" :value="formState" :items="[
|
||||
<RadioGroup
|
||||
class="my-2"
|
||||
name="status"
|
||||
:value="formState"
|
||||
:items="[
|
||||
{
|
||||
key: 0,
|
||||
value: 0,
|
||||
title: '未完成',
|
||||
title: $t('alert.not_completed'),
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
value: 1,
|
||||
title: '已完成',
|
||||
title: $t('alert.completed'),
|
||||
},
|
||||
]" :required="true">
|
||||
<template #topLeft>狀態</template>
|
||||
]"
|
||||
:required="true"
|
||||
>
|
||||
<template #topLeft>{{ $t("alert.status") }}</template>
|
||||
</RadioGroup>
|
||||
<Select :value="formState" class="my-2" selectClass="border-info focus-within:border-info" name="work_person_id"
|
||||
Attribute="full_name" :options="model_data.model_userList" :required="true">
|
||||
<template #topLeft>工作人員編號</template>
|
||||
<template #bottomLeft><span class="text-error text-base">
|
||||
<Select
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
selectClass="border-info focus-within:border-info"
|
||||
name="work_person_id"
|
||||
Attribute="full_name"
|
||||
:options="model_data.model_userList"
|
||||
:required="true"
|
||||
>
|
||||
<template #topLeft>{{ $t("alert.worker_id") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.work_person_id }}
|
||||
</span></template>
|
||||
</span></template
|
||||
>
|
||||
</Select>
|
||||
<Textarea :value="formState" name="notice" class="w-full my-2">
|
||||
<template #topLeft>注意事項</template>
|
||||
<template #topLeft>{{ $t("alert.notice") }}</template>
|
||||
</Textarea>
|
||||
<Textarea :value="formState" name="description" class="w-full my-2">
|
||||
<template #topLeft>結果描述</template>
|
||||
<template #topLeft>{{ $t("alert.result_description") }}</template>
|
||||
</Textarea>
|
||||
<Upload
|
||||
class="my-2"
|
||||
@ -247,16 +306,24 @@ watch(
|
||||
:multiple="true"
|
||||
:baseUrl="`${FILE_BASEURL}/upload/operation`"
|
||||
>
|
||||
<template #topLeft>上傳檔案</template>
|
||||
<template #topLeft>{{ $t("alert.upload_file") }}</template>
|
||||
</Upload>
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button type="reset" class="btn btn-outline-success mr-2" @click.prevent="onCancel">
|
||||
取消
|
||||
<button
|
||||
type="reset"
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-outline-success" @click.stop.prevent="onOk">
|
||||
確定
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.stop.prevent="onOk"
|
||||
>
|
||||
{{ $t("button.submit")}}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
|
@ -1,23 +1,24 @@
|
||||
<script setup>
|
||||
import { inject, defineProps, watch, ref } from "vue";
|
||||
|
||||
import { defineProps, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
SaveCheckAuth: Object,
|
||||
NoticeData: Object,
|
||||
onChange: Function,
|
||||
});
|
||||
|
||||
const columns = [
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: "選擇",
|
||||
title: t('alert.choose'),
|
||||
key: "SaveCheckAuth",
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: "通知項目",
|
||||
title: t('alert.notify_items'),
|
||||
key: "system_key",
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -1,60 +1,64 @@
|
||||
<script setup>
|
||||
import { ref, inject, watch } from "vue";
|
||||
import { ref, inject, watch, computed } from "vue";
|
||||
import { getAlarmMemberList, deleteAlarmMember } from "@/apis/alert";
|
||||
import AlertNotifyTableAddModal from "./AlertNotifyTableAddModal.vue";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast } = inject("app_toast");
|
||||
const tableData = ref([]);
|
||||
const editRecord = ref(null);
|
||||
const { noticeList } = inject("notify_table");
|
||||
|
||||
const fetchTableData = async () => {
|
||||
|
||||
const noticeMap = noticeList.value.reduce((acc, notice) => {
|
||||
acc[notice.system_value] = notice.system_key;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const res = await getAlarmMemberList();
|
||||
tableData.value = res.data.map(member => ({
|
||||
tableData.value = res.data.map((member) => ({
|
||||
...member,
|
||||
notify_items: member.notices
|
||||
.filter(n => noticeMap[n])
|
||||
.map(n => noticeMap[n])
|
||||
.join('\n') || '無通知',
|
||||
notify_items:
|
||||
member.notices
|
||||
.filter((n) => noticeMap[n])
|
||||
.map((n) => noticeMap[n])
|
||||
.join("\n") || t("alert.no_notify"),
|
||||
}));
|
||||
};
|
||||
|
||||
watch(noticeList, (newVal) => {
|
||||
watch(
|
||||
noticeList,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
fetchTableData();
|
||||
}
|
||||
}, { immediate: true });
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const columns = [
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: "姓名",
|
||||
title: t("alert.notify_name"),
|
||||
key: "name",
|
||||
},
|
||||
{
|
||||
title: "手機號碼",
|
||||
title: t("alert.notify_phone"),
|
||||
key: "phone",
|
||||
},
|
||||
{
|
||||
title: "email",
|
||||
title: t("alert.notify_email"),
|
||||
key: "email",
|
||||
},
|
||||
{
|
||||
title: "通知項目",
|
||||
title: t("alert.notify_items"),
|
||||
key: "notify_items",
|
||||
},
|
||||
{
|
||||
title: "功能",
|
||||
title: t("alert.operation"),
|
||||
key: "operation",
|
||||
width: 200,
|
||||
},
|
||||
];
|
||||
|
||||
]);
|
||||
|
||||
const openModal = (record) => {
|
||||
if (record) {
|
||||
@ -78,23 +82,32 @@ const remove = async (id) => {
|
||||
openToast("error", res.msg);
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-start items-center mt-10">
|
||||
<h3 class="text-xl mr-5">通知名單</h3>
|
||||
<AlertNotifyTableAddModal :openModal="openModal" :onCancel="onCancel" :editRecord="editRecord"
|
||||
:fetchTableData="fetchTableData" />
|
||||
<h3 class="text-xl mr-5">{{ $t("alert.notify_list") }}</h3>
|
||||
<AlertNotifyTableAddModal
|
||||
:openModal="openModal"
|
||||
:onCancel="onCancel"
|
||||
:editRecord="editRecord"
|
||||
:fetchTableData="fetchTableData"
|
||||
/>
|
||||
</div>
|
||||
<Table :columns="columns" :dataSource="tableData" class="w-3/4 mt-3">
|
||||
<template #bodyCell="{ record, column, index }">
|
||||
<template v-if="column.key === 'operation'">
|
||||
<button class="btn btn-sm btn-success text-white mr-2" @click.stop.prevent="() => openModal(record)">
|
||||
修改
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white mr-2"
|
||||
@click.stop.prevent="() => openModal(record)"
|
||||
>
|
||||
{{ $t("button.edit") }}
|
||||
</button>
|
||||
<button class="btn btn-sm btn-error text-white" @click.stop.prevent="() => remove(record.id)">
|
||||
刪除
|
||||
<button
|
||||
class="btn btn-sm btn-error text-white"
|
||||
@click.stop.prevent="() => remove(record.id)"
|
||||
>
|
||||
{{ $t("button.delete") }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
|
@ -4,7 +4,8 @@ import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import AlertNoticesTable from "./AlertNoticesTable.vue";
|
||||
import { postAlertMember } from "@/apis/alert";
|
||||
import * as yup from "yup";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast } = inject("app_toast");
|
||||
const { noticeList } = inject("notify_table");
|
||||
const props = defineProps({
|
||||
@ -15,9 +16,15 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
let scheme = yup.object({
|
||||
name: yup.string().required("必填"),
|
||||
phone: yup.string().phone("TW", "請輸入正確電話號碼格式").required("必填"),
|
||||
email: yup.string().email("請輸入正確的 Email 地址").required("必填"),
|
||||
name: yup.string().required(t("button.required")),
|
||||
phone: yup
|
||||
.string()
|
||||
.phone("TW", t("button.phone_format"))
|
||||
.required(t("button.required")),
|
||||
email: yup
|
||||
.string()
|
||||
.email(t("button.email_format"))
|
||||
.required(t("button.required")),
|
||||
});
|
||||
|
||||
const form = ref(null);
|
||||
@ -31,8 +38,9 @@ const formState = ref({
|
||||
|
||||
const SaveCheckAuth = ref([]);
|
||||
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset } =
|
||||
useFormErrorMessage(scheme.value);
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset } = useFormErrorMessage(
|
||||
scheme.value
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.editRecord,
|
||||
@ -60,8 +68,7 @@ const onChange = (value, checked) => {
|
||||
const onOk = async () => {
|
||||
const values = await handleSubmit(scheme, formState.value);
|
||||
|
||||
const res = await postAlertMember(
|
||||
{
|
||||
const res = await postAlertMember({
|
||||
...values,
|
||||
notices: SaveCheckAuth.value ? SaveCheckAuth.value : [],
|
||||
});
|
||||
@ -82,13 +89,19 @@ const closeModal = () => {
|
||||
|
||||
<template>
|
||||
<button class="btn btn-sm btn-success mr-3" @click.stop.prevent="openModal">
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />新增
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
||||
</button>
|
||||
<Modal id="notify_add_table_item" title="通知名單" :open="open" :onCancel="closeModal" width="300">
|
||||
<Modal
|
||||
id="notify_add_table_item"
|
||||
:title="t('alert.notify_list')"
|
||||
:open="open"
|
||||
:onCancel="closeModal"
|
||||
width="300"
|
||||
>
|
||||
<template #modalContent>
|
||||
<form ref="form" class="mt-5 flex flex-col items-center">
|
||||
<Input :value="formState" class="w-full" name="name">
|
||||
<template #topLeft>姓名</template>
|
||||
<template #topLeft>{{ $t("alert.notify_name") }}</template>
|
||||
<template #bottomLeft>
|
||||
<span class="text-error text-base">
|
||||
{{ formErrorMsg.name }}
|
||||
@ -96,7 +109,7 @@ const closeModal = () => {
|
||||
</template>
|
||||
</Input>
|
||||
<Input class="w-full" :value="formState" name="phone">
|
||||
<template #topLeft>電話</template>
|
||||
<template #topLeft>{{ $t("alert.notify_phone") }}</template>
|
||||
<template #bottomLeft>
|
||||
<span class="text-error text-base">
|
||||
{{ formErrorMsg.phone }}
|
||||
@ -104,7 +117,7 @@ const closeModal = () => {
|
||||
</template>
|
||||
</Input>
|
||||
<Input class="w-full" :value="formState" name="email">
|
||||
<template #topLeft>Email</template>
|
||||
<template #topLeft>{{ $t("alert.notify_email") }}</template>
|
||||
<template #bottomLeft>
|
||||
<span class="text-error text-base">
|
||||
{{ formErrorMsg.email }}
|
||||
@ -112,10 +125,12 @@ const closeModal = () => {
|
||||
</template>
|
||||
</Input>
|
||||
<div class="w-5/6 mt-5">
|
||||
<p class="text-light text-base">
|
||||
通知項目
|
||||
</p>
|
||||
<AlertNoticesTable :SaveCheckAuth="SaveCheckAuth" :NoticeData="[noticeList[2]]" :onChange="onChange"/>
|
||||
<p class="text-light text-base">{{ $t("alert.notify_items") }}</p>
|
||||
<AlertNoticesTable
|
||||
:SaveCheckAuth="SaveCheckAuth"
|
||||
:NoticeData="[noticeList[2]]"
|
||||
:onChange="onChange"
|
||||
/>
|
||||
<span class="text-error text-base">
|
||||
{{ formErrorMsg.notices }}
|
||||
</span>
|
||||
@ -123,11 +138,19 @@ const closeModal = () => {
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button type="reset" class="btn btn-outline-success mr-2" @click.prevent="closeModal">
|
||||
取消
|
||||
<button
|
||||
type="reset"
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="closeModal"
|
||||
>
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-outline-success" @click.prevent="onOk">
|
||||
確定
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.prevent="onOk"
|
||||
>
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
|
@ -1,10 +1,16 @@
|
||||
<script setup>
|
||||
import { onMounted, ref, watch, inject } from "vue";
|
||||
import { getOutliersList, getOutliersDevList, getOutliersPoints } from "@/apis/alert";
|
||||
import { onMounted, ref, watch, inject, computed } from "vue";
|
||||
import {
|
||||
getOutliersList,
|
||||
getOutliersDevList,
|
||||
getOutliersPoints,
|
||||
} from "@/apis/alert";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import AlertOutliersTableAddModal from "./AlertOutliersTableAddModal.vue";
|
||||
import AlertOutliersTimeModal from "./AlertOutliersTimeModal.vue";
|
||||
import dayjs from "dayjs";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { noticeList } = inject("notify_table");
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
|
||||
@ -15,110 +21,120 @@ const dev_data = ref({
|
||||
});
|
||||
const editRecord = ref(null);
|
||||
|
||||
const columns = [
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: "設備編號",
|
||||
title: t("alert.device_number"),
|
||||
key: "device_number",
|
||||
class: "break-all",
|
||||
},
|
||||
{
|
||||
title: "設備名稱",
|
||||
title: t("alert.device_name"),
|
||||
key: "device_name",
|
||||
},
|
||||
{
|
||||
title: "項目",
|
||||
title: t("alert.item"),
|
||||
key: "points",
|
||||
},
|
||||
{
|
||||
title: "啟用",
|
||||
title: t("alert.enable"),
|
||||
key: "enable",
|
||||
},
|
||||
{
|
||||
title: "限定條件",
|
||||
title: t("alert.qualifications"),
|
||||
key: "factor_name",
|
||||
},
|
||||
{
|
||||
title: "上限(>=)",
|
||||
title: `${t("alert.upper_limit")} (>=)`,
|
||||
key: "highLimit",
|
||||
},
|
||||
{
|
||||
title: "下限(<=)",
|
||||
title: `${t("alert.lower_limit")} (<=)`,
|
||||
key: "lowLimit",
|
||||
},
|
||||
{
|
||||
title: "上限持續秒數",
|
||||
title: t("alert.highDelay"),
|
||||
key: "highDelay",
|
||||
},
|
||||
{
|
||||
title: "下限持續秒數",
|
||||
title: t("alert.lowDelay"),
|
||||
key: "lowDelay",
|
||||
},
|
||||
{
|
||||
title: "警示方式",
|
||||
title: t("alert.warning_method"),
|
||||
key: "warning_method",
|
||||
},
|
||||
{
|
||||
title: "警示時間",
|
||||
title: t("alert.warning_time"),
|
||||
key: "warning_time",
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: "功能",
|
||||
title: t("alert.operation"),
|
||||
key: "operation",
|
||||
width: 200,
|
||||
width: 150,
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
||||
const getDevList = async () => {
|
||||
const res = await getOutliersDevList({
|
||||
device_name_tag: searchParams.value?.subSys_id
|
||||
device_name_tag: searchParams.value?.subSys_id,
|
||||
});
|
||||
return res.data.map((d) => ({
|
||||
...d,
|
||||
key: d.device_number
|
||||
key: d.device_number,
|
||||
}));
|
||||
};
|
||||
|
||||
const getAlarmPoints = async () => {
|
||||
const res = await getOutliersPoints({
|
||||
device_name_tag: searchParams.value?.subSys_id
|
||||
device_name_tag: searchParams.value?.subSys_id,
|
||||
});
|
||||
return res.data.map((d) => ({
|
||||
...d,
|
||||
key: d.points
|
||||
key: d.points,
|
||||
}));
|
||||
};
|
||||
|
||||
const getOutliersData = async () => {
|
||||
const res = await getOutliersList({
|
||||
device_name_tag: searchParams.value?.subSys_id
|
||||
device_name_tag: searchParams.value?.subSys_id,
|
||||
});
|
||||
if (res.isSuccess) {
|
||||
tableData.value = res.data.map(item => {
|
||||
const matchedDevice = dev_data.value.devList.find(dev => dev.device_number === item.device_number);
|
||||
const matchedPoints = dev_data.value.alarmPoints.find(p => p.points === item.points);
|
||||
const matchedFactor = matchedPoints?.factor && item.factor ? matchedPoints?.factor?.find(f => f.id === item.factor) : null;
|
||||
const warningMethodKeys = item.notices?.map(noticeValue => {
|
||||
const matchedNotice = noticeList.value.find(n => n.system_value === noticeValue);
|
||||
return matchedNotice ? matchedNotice.system_key : '';
|
||||
}).filter(key => key !== '').join('\n');
|
||||
tableData.value = res.data.map((item) => {
|
||||
const matchedDevice = dev_data.value.devList.find(
|
||||
(dev) => dev.device_number === item.device_number
|
||||
);
|
||||
const matchedPoints = dev_data.value.alarmPoints.find(
|
||||
(p) => p.points === item.points
|
||||
);
|
||||
const matchedFactor =
|
||||
matchedPoints?.factor && item.factor
|
||||
? matchedPoints?.factor?.find((f) => f.id === item.factor)
|
||||
: null;
|
||||
const warningMethodKeys = item.notices
|
||||
?.map((noticeValue) => {
|
||||
const matchedNotice = noticeList.value.find(
|
||||
(n) => n.system_value === noticeValue
|
||||
);
|
||||
return matchedNotice ? matchedNotice.system_key : "";
|
||||
})
|
||||
.filter((key) => key !== "")
|
||||
.join("\n");
|
||||
|
||||
return {
|
||||
...item,
|
||||
device_name: matchedDevice ? matchedDevice.device_name : '',
|
||||
points: matchedPoints ? matchedPoints.full_name : '',
|
||||
device_name: matchedDevice ? matchedDevice.device_name : "",
|
||||
points: matchedPoints ? matchedPoints.full_name : "",
|
||||
is_bool: matchedPoints ? matchedPoints.is_bool : 1,
|
||||
factor_name: matchedFactor ? matchedFactor.full_name : '',
|
||||
warning_method: warningMethodKeys
|
||||
factor_name: matchedFactor ? matchedFactor.full_name : "",
|
||||
warning_method: warningMethodKeys,
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getAllOptions = async () => {
|
||||
const [devices, points] = await Promise.all([
|
||||
getDevList(),
|
||||
getAlarmPoints(),
|
||||
]);
|
||||
const [devices, points] = await Promise.all([getDevList(), getAlarmPoints()]);
|
||||
dev_data.value.devList = devices;
|
||||
dev_data.value.alarmPoints = points;
|
||||
};
|
||||
@ -155,12 +171,11 @@ const openTimeModal = () => {
|
||||
const onTimeCancel = () => {
|
||||
outliers_time_item.close();
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-start items-center mt-10">
|
||||
<h3 class="text-xl mr-5">異常設定</h3>
|
||||
<h3 class="text-xl mr-5">{{ $t("alert.alarm_settings") }}</h3>
|
||||
<AlertOutliersTableAddModal
|
||||
:openModal="openModal"
|
||||
:onCancel="onCancel"
|
||||
@ -168,10 +183,10 @@ const onTimeCancel = () => {
|
||||
:getData="getOutliersData"
|
||||
:OptionsData="dev_data"
|
||||
/>
|
||||
<!-- <AlertOutliersTimeModal
|
||||
<AlertOutliersTimeModal
|
||||
:openModal="openTimeModal"
|
||||
:onCancel="onTimeCancel"
|
||||
/> -->
|
||||
/>
|
||||
</div>
|
||||
<Table :columns="columns" :dataSource="tableData" class="mt-3">
|
||||
<template #bodyCell="{ record, column, index }">
|
||||
@ -180,19 +195,22 @@ const onTimeCancel = () => {
|
||||
class="btn btn-sm btn-success text-white mr-2"
|
||||
@click.stop.prevent="() => openModal(record)"
|
||||
>
|
||||
修改
|
||||
{{ $t("button.edit") }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'enable'">
|
||||
{{ record.enable === 1 ? '是' : '否' }}
|
||||
{{ record.enable === 1 ? t("alert.yes") : t("alert.no") }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'warning_method'">
|
||||
<span class="whitespace-pre">{{ record.warning_method }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'warning_time'">
|
||||
<button class="btn btn-sm btn-success text-white pb-3"
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white pb-3"
|
||||
:disabled="!record.enable"
|
||||
@click.stop.prevent="() => openTimeModal(record)"
|
||||
>
|
||||
限制告警時間
|
||||
{{ $t("alert.time_setting") }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
|
@ -5,7 +5,8 @@ import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import AlertNoticesTable from "./AlertNoticesTable.vue";
|
||||
import { postOutliersSetting } from "@/apis/alert";
|
||||
import * as yup from "yup";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast } = inject("app_toast");
|
||||
const { noticeList } = inject("notify_table");
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
@ -20,23 +21,23 @@ const props = defineProps({
|
||||
|
||||
const form = ref(null);
|
||||
const formState = ref({
|
||||
"id": 0,
|
||||
"device_number": "",
|
||||
"device_name_tag": searchParams.value?.subSys_id,
|
||||
"points": "",
|
||||
"enable": 0,
|
||||
"is_bool": 1,
|
||||
"factor": null,
|
||||
"highLimit": null,
|
||||
"lowLimit": null,
|
||||
"highDelay": null,
|
||||
"lowDelay": null,
|
||||
"notices": []
|
||||
id: 0,
|
||||
device_number: "",
|
||||
device_name_tag: searchParams.value?.subSys_id,
|
||||
points: "",
|
||||
enable: 0,
|
||||
is_bool: 1,
|
||||
factor: null,
|
||||
highLimit: null,
|
||||
lowLimit: null,
|
||||
highDelay: null,
|
||||
lowDelay: null,
|
||||
notices: [],
|
||||
});
|
||||
|
||||
let scheme = yup.object({
|
||||
device_number: yup.string().required("必填"),
|
||||
points: yup.string().required("必填"),
|
||||
device_number: yup.string().required(t("button.required")),
|
||||
points: yup.string().required(t("button.required")),
|
||||
factor: yup.number().nullable(),
|
||||
enable: yup.number().required(),
|
||||
highLimit: yup.number().nullable(),
|
||||
@ -45,8 +46,9 @@ let scheme = yup.object({
|
||||
lowDelay: yup.number().nullable(),
|
||||
});
|
||||
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset } =
|
||||
useFormErrorMessage(scheme.value);
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset } = useFormErrorMessage(
|
||||
scheme.value
|
||||
);
|
||||
|
||||
const SaveCheckAuth = ref([]);
|
||||
const isBool = ref(1);
|
||||
@ -69,15 +71,17 @@ watch(
|
||||
);
|
||||
|
||||
const onPointsChange = (selectedPoint) => {
|
||||
const pointData = props.OptionsData.alarmPoints.find(p => p.points === selectedPoint);
|
||||
const pointData = props.OptionsData.alarmPoints.find(
|
||||
(p) => p.points === selectedPoint
|
||||
);
|
||||
if (pointData) {
|
||||
isBool.value = pointData.is_bool;
|
||||
formState.value.is_bool = pointData.is_bool;
|
||||
if (pointData.factor && Array.isArray(pointData.factor)) {
|
||||
factorData.value = pointData.factor.map((d) => ({
|
||||
...d,
|
||||
key: d.id
|
||||
}));;
|
||||
key: d.id,
|
||||
}));
|
||||
} else {
|
||||
factorData.value = [];
|
||||
formState.value.factor = 0;
|
||||
@ -120,74 +124,141 @@ const closeModal = () => {
|
||||
factorData.value = [];
|
||||
isBool.value = 1;
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="btn btn-sm btn-success mr-3" @click.stop.prevent="openModal">
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />新增
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
||||
</button>
|
||||
<Modal id="outliers_add_table_item" title="異常設定" :open="open" :onCancel="closeModal" width="710">
|
||||
<Modal
|
||||
id="outliers_add_table_item"
|
||||
:title="t('alert.alarm_settings')"
|
||||
:open="open"
|
||||
:onCancel="closeModal"
|
||||
width="710"
|
||||
>
|
||||
<template #modalContent>
|
||||
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
||||
<Select :value="formState" class="my-2" selectClass="border-info focus-within:border-info" name="device_number"
|
||||
Attribute="device_name" :options="OptionsData.devList">
|
||||
<template #topLeft>設備名稱</template>
|
||||
<template #bottomLeft><span class="text-error text-base">
|
||||
<Select
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
selectClass="border-info focus-within:border-info"
|
||||
name="device_number"
|
||||
Attribute="device_name"
|
||||
:options="OptionsData.devList"
|
||||
>
|
||||
<template #topLeft>{{ $t("alert.device_name") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.device_number }}
|
||||
</span></template>
|
||||
</span></template
|
||||
>
|
||||
</Select>
|
||||
<Select :value="formState" class="my-2" selectClass="border-info focus-within:border-info" name="points"
|
||||
Attribute="full_name" :options="OptionsData.alarmPoints" :onChange="onPointsChange">
|
||||
<template #topLeft>項目</template>
|
||||
<template #bottomLeft><span class="text-error text-base">
|
||||
<Select
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
selectClass="border-info focus-within:border-info"
|
||||
name="points"
|
||||
Attribute="full_name"
|
||||
:options="OptionsData.alarmPoints"
|
||||
:onChange="onPointsChange"
|
||||
>
|
||||
<template #topLeft>{{ $t("alert.item") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.points }}
|
||||
</span></template>
|
||||
</span></template
|
||||
>
|
||||
</Select>
|
||||
<RadioGroup class="my-2" name="enable" :value="formState" :items="[
|
||||
<RadioGroup
|
||||
class="my-2"
|
||||
name="enable"
|
||||
:value="formState"
|
||||
:items="[
|
||||
{
|
||||
key: 1,
|
||||
value: 1,
|
||||
title: '啟用',
|
||||
title: $t('alert.enable'),
|
||||
},
|
||||
{
|
||||
key: 0,
|
||||
value: 0,
|
||||
title: '不啟用',
|
||||
title: $t('alert.not_enabled'),
|
||||
},
|
||||
]" :required="true">
|
||||
<template #topLeft>狀態</template>
|
||||
]"
|
||||
:required="true"
|
||||
>
|
||||
<template #topLeft>{{ $t("alert.status") }}</template>
|
||||
</RadioGroup>
|
||||
<Select :value="formState" class="my-2" selectClass="border-info focus-within:border-info" name="factor"
|
||||
Attribute="full_name" :options="factorData" v-if="factorData.length !== 0">
|
||||
<template #topLeft>限定條件</template>
|
||||
<Select
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
selectClass="border-info focus-within:border-info"
|
||||
name="factor"
|
||||
Attribute="full_name"
|
||||
:options="factorData"
|
||||
v-if="factorData.length !== 0"
|
||||
>
|
||||
<template #topLeft>{{ $t("alert.qualifications") }}</template>
|
||||
</Select>
|
||||
<InputNumber :value="formState" class="my-2" name="highLimit" v-if="!isBool">
|
||||
<template #topLeft>上限(>=)</template>
|
||||
<InputNumber
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
name="highLimit"
|
||||
v-if="!isBool"
|
||||
>
|
||||
<template #topLeft>{{ $t("alert.upper_limit") }}(>=)</template>
|
||||
</InputNumber>
|
||||
<InputNumber :value="formState" class="my-2" name="lowLimit" v-if="!isBool">
|
||||
<template #topLeft>下限(<=)</template>
|
||||
<InputNumber
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
name="lowLimit"
|
||||
v-if="!isBool"
|
||||
>
|
||||
<template #topLeft>{{ $t("alert.lower_limit") }}(<=)</template>
|
||||
</InputNumber>
|
||||
<InputNumber :value="formState" class="my-2" name="highDelay" v-if="!isBool">
|
||||
<template #topLeft>上限持續秒數</template>
|
||||
<InputNumber
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
name="highDelay"
|
||||
v-if="!isBool"
|
||||
>
|
||||
<template #topLeft>{{ $t("alert.highDelay") }}</template>
|
||||
</InputNumber>
|
||||
<InputNumber :value="formState" class="my-2" name="lowDelay" v-if="!isBool">
|
||||
<template #topLeft>下限持續秒數</template>
|
||||
<InputNumber
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
name="lowDelay"
|
||||
v-if="!isBool"
|
||||
>
|
||||
<template #topLeft>{{ $t("alert.lowDelay") }}</template>
|
||||
</InputNumber>
|
||||
<div class="w-full mt-5">
|
||||
<p class="text-light text-lg ml-1">
|
||||
警示方式
|
||||
{{ $t("alert.warning_method") }}
|
||||
</p>
|
||||
<AlertNoticesTable :SaveCheckAuth="SaveCheckAuth" :NoticeData="noticeList" :onChange="onNoticesChange" />
|
||||
<AlertNoticesTable
|
||||
:SaveCheckAuth="SaveCheckAuth"
|
||||
:NoticeData="noticeList"
|
||||
:onChange="onNoticesChange"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button type="reset" class="btn btn-outline-success mr-2" @click.prevent="closeModal">
|
||||
取消
|
||||
<button
|
||||
type="reset"
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="closeModal"
|
||||
>
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-outline-success" @click.prevent="onOk">
|
||||
確定
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.prevent="onOk"
|
||||
>
|
||||
{{ $t("buttton.submit") }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
|
@ -10,29 +10,73 @@ const closeModal = () => {
|
||||
props.onCancel();
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: "排程名稱",
|
||||
key: "schedule_name",
|
||||
},
|
||||
{
|
||||
title: "時段",
|
||||
key: "schedule_time",
|
||||
},
|
||||
{
|
||||
title: "狀態",
|
||||
key: "enable",
|
||||
},
|
||||
{
|
||||
title: "功能",
|
||||
key: "operation",
|
||||
width: 150,
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal id="outliers_time_item" :open="open" :onCancel="closeModal" width="710">
|
||||
<Modal
|
||||
id="outliers_time_item"
|
||||
:open="open"
|
||||
:onCancel="closeModal"
|
||||
width="800"
|
||||
>
|
||||
<template #modalTitle>
|
||||
<p>限制告警時間設定</p>
|
||||
<p>警示時間設定</p>
|
||||
<button class="fixed right-10 top-5" @click.prevent="closeModal">
|
||||
<font-awesome-icon :icon="['fas', 'times']" size="1x" class="text-[#a5abb1]" />
|
||||
<font-awesome-icon
|
||||
:icon="['fas', 'times']"
|
||||
size="1x"
|
||||
class="text-[#a5abb1]"
|
||||
/>
|
||||
</button>
|
||||
</template>
|
||||
<template #modalContent>
|
||||
<iframe :src="'/ord?station:%7Cslot:/NTPC/F3/Pd/DP/U2F/NA/DP$2d260ML/N4|view:GraphicM?fullScreen=true'
|
||||
"></iframe>
|
||||
<Table
|
||||
:columns="columns"
|
||||
:dataSource="tableData"
|
||||
class="mt-3"
|
||||
:withStyle="false"
|
||||
>
|
||||
<template #bodyCell="{ record, column, index }">
|
||||
<template v-if="column.key === 'operation'">
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white mr-2"
|
||||
@click.stop.prevent="() => openModal(record)"
|
||||
>
|
||||
修改
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-error text-white"
|
||||
@click.stop.prevent="() => remove(record.userinfo_guid)"
|
||||
>
|
||||
刪除
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ record[column.key] }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
margin-top: 20px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 0px 50px 5px #35eded47;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
@ -16,11 +16,10 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
provide("notify_table", { noticeList });
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1 class="text-2xl font-extrabold mb-2">告警設定</h1>
|
||||
<h1 class="text-2xl font-extrabold mb-2">{{ $t("alert.setting_title") }}</h1>
|
||||
<div>
|
||||
<AlertSubList />
|
||||
<AlertOutliersTable />
|
||||
|
@ -1,13 +1,9 @@
|
||||
<script setup>
|
||||
import Forge from "@/components/forge/Forge.vue";
|
||||
import DashboardProduct from "./components/DashboardProduct.vue";
|
||||
import DashboardFormulaTemp from "./components/DashboardFormulaTemp.vue";
|
||||
import DashboardImmediateTemp from "./components/DashboardImmediateTemp.vue";
|
||||
import DashboardFrozenTemp from "./components/DashboardFrozenTemp.vue";
|
||||
import DashboardEnergy from "./components/DashboardEnergy.vue";
|
||||
import DashboardAlert from "./components/DashboardAlert.vue";
|
||||
import DashboardMoreModal from "./components/DashboardMoreModal.vue";
|
||||
import DashboardForgeOptionButton from "./components/DashboardForgeOptionButton.vue";
|
||||
import DashboardStat from "./components/DashboardStat.vue";
|
||||
import DashboardElecChart from "./components/DashboardElecChart.vue";
|
||||
import DashboardSysCard from "./components/DashboardSysCard.vue";
|
||||
import DashboardSysProgress from "./components/DashboardSysProgress.vue";
|
||||
import { getDashboardInit } from "@/apis/dashboard";
|
||||
import { onMounted, ref, provide, watch } from "vue";
|
||||
|
||||
@ -45,43 +41,46 @@ provide("dashboard_items", {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <Forge /> -->
|
||||
<DashboardMoreModal />
|
||||
<div class="flex justify-between my-10">
|
||||
<div
|
||||
class="w-1/4 h-full flex flex-col justify-start z-10 border-dashboard px-12"
|
||||
>
|
||||
<div class="">
|
||||
<DashboardProduct />
|
||||
</div>
|
||||
<div class="">
|
||||
<DashboardProductComplete />
|
||||
</div>
|
||||
<div class="mt-10">
|
||||
<DashboardImmediateTemp />
|
||||
</div>
|
||||
<div class="mt-10">
|
||||
<DashboardFrozenTemp />
|
||||
<div class="flex flex-wrap items-center">
|
||||
<!-- 建築圖 -->
|
||||
<div class="w-full xl:w-1/3">
|
||||
<div class="area-img-box">
|
||||
<Forge />
|
||||
</div>
|
||||
</div>
|
||||
<Forge :fullScreen="true" :initialData="initialData" />
|
||||
<div
|
||||
class="w-1/4 flex flex-col justify-start border-dashboard z-20"
|
||||
>
|
||||
<DashboardForgeOptionButton :initialData="initialData" />
|
||||
<div class="">
|
||||
<DashboardFormulaTemp />
|
||||
|
||||
<div class="w-full xl:w-2/3">
|
||||
<!-- 用電數據 -->
|
||||
<DashboardStat />
|
||||
<!-- 用電圖表 -->
|
||||
<DashboardElecChart />
|
||||
</div>
|
||||
<DashboardEnergy />
|
||||
<div class="mt-10">
|
||||
<DashboardAlert />
|
||||
|
||||
<!-- 設備小卡 -->
|
||||
<div class="w-full lg:w-2/3">
|
||||
<DashboardSysCard />
|
||||
</div>
|
||||
<!--狀態、進度-->
|
||||
<div class="w-full lg:w-1/3">
|
||||
<DashboardSysProgress />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.border-dashboard {
|
||||
@apply border border-light-info bg-dark-info bg-opacity-70 p-3 first:mt-0 last:mb-0;
|
||||
.area-img-box {
|
||||
@apply border border-light-info bg-dark-info w-full h-[400px] block relative rounded-sm mb-4;
|
||||
}
|
||||
|
||||
.area-img-box::before {
|
||||
@apply absolute left-0 right-0 -top-[4px] m-auto h-[8px] w-[140px] bg-no-repeat z-10;
|
||||
content: "";
|
||||
background: url(@ASSET/img/area-img-box-line-top.png) center center;
|
||||
}
|
||||
|
||||
.area-img-box::after {
|
||||
@apply absolute left-0 right-0 -bottom-[4px] m-auto h-[8px] w-[140px] bg-no-repeat z-10;
|
||||
content: "";
|
||||
background: url(@ASSET/img/area-img-box-line-bottom.png) center center;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,40 +0,0 @@
|
||||
<script setup>
|
||||
import useAlarmStore from "@/stores/useAlarmStore";
|
||||
import dayjs from "dayjs";
|
||||
import { computed } from "vue";
|
||||
|
||||
const store = useAlarmStore();
|
||||
const alarms = computed(() =>
|
||||
store.alarmData.slice(0, 5).map((d) => ({
|
||||
...d,
|
||||
timestamp_date: dayjs(d.timestamp_date).format("YYYY.MM.DD"),
|
||||
timestamp_time: d.timestamp_time.split(":").slice(0, 2).join(":"),
|
||||
}))
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="overflow-x-auto">
|
||||
<h3 class="text-info font-bold text-xl text-center">異常資料 Top 5</h3>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr class="border-b-2 border-info text-base">
|
||||
<th>日期</th>
|
||||
<th>時間</th>
|
||||
<th>設備名稱</th>
|
||||
<th>備註</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-base">
|
||||
<tr v-for="alarm in alarms" :key="alarm.uuid">
|
||||
<td>{{ alarm.timestamp_date }}</td>
|
||||
<td>{{ alarm.timestamp_time }}</td>
|
||||
<td>{{ alarm.full_name }}</td>
|
||||
<td>{{ alarm.msg }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|