fix: 首頁與歷史資料頁圖表顯示優化

首頁:

1. 室內/冷藏與溫度/濕度位置互換

2. 碳排圖表改為顯示「每日」資料

3. 平面圖點擊 icons 顯示彈跳視窗

歷史資料頁:

1. 調整折線圖 X 軸過密的顯示問題
This commit is contained in:
MJM_2025_05\polly 2025-07-31 11:01:07 +08:00
parent 995cd77bef
commit 2e15353384
19 changed files with 5488 additions and 214 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
* text=auto
*.html text eol=lf

341
package-lock.json generated
View File

@ -40,6 +40,7 @@
"autoprefixer": "^10.4.16", "autoprefixer": "^10.4.16",
"daisyui": "^4.4.17", "daisyui": "^4.4.17",
"postcss": "^8.4.31", "postcss": "^8.4.31",
"rollup-plugin-visualizer": "^6.0.3",
"sass": "^1.69.5", "sass": "^1.69.5",
"sass-loader": "^13.3.2", "sass-loader": "^13.3.2",
"tailwindcss": "^3.3.5", "tailwindcss": "^3.3.5",
@ -1815,6 +1816,44 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/cliui/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/cliui/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/clone": { "node_modules/clone": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
@ -1837,6 +1876,26 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT"
},
"node_modules/combined-stream": { "node_modules/combined-stream": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@ -2159,6 +2218,16 @@
"node": ">=0.10" "node": ">=0.10"
} }
}, },
"node_modules/define-lazy-prop": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
"integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/define-property": { "node_modules/define-property": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
@ -2282,6 +2351,13 @@
"integrity": "sha512-KD6CWjf1BnQG+NsXuyiTDDT1eV13sKuYsOUioXkQweYTQIbgHkXPry9K7M+7cKtYHnSUPitVaLrXYB1jTkkYrw==", "integrity": "sha512-KD6CWjf1BnQG+NsXuyiTDDT1eV13sKuYsOUioXkQweYTQIbgHkXPry9K7M+7cKtYHnSUPitVaLrXYB1jTkkYrw==",
"dev": true "dev": true
}, },
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/emojis-list": { "node_modules/emojis-list": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
@ -2763,6 +2839,16 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-value": { "node_modules/get-value": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
@ -3119,6 +3205,22 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/is-docker": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
"integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"dev": true,
"license": "MIT",
"bin": {
"is-docker": "cli.js"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-extendable": { "node_modules/is-extendable": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
@ -3137,6 +3239,16 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/is-glob": { "node_modules/is-glob": {
"version": "4.0.3", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@ -3190,6 +3302,19 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
"integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-docker": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/isarray": { "node_modules/isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -4002,6 +4127,24 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"node_modules/open": {
"version": "8.4.2",
"resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
"integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"define-lazy-prop": "^2.0.0",
"is-docker": "^2.1.1",
"is-wsl": "^2.2.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/optimist": { "node_modules/optimist": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
@ -4629,6 +4772,16 @@
"node": ">= 0.12" "node": ">= 0.12"
} }
}, },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/requirejs": { "node_modules/requirejs": {
"version": "2.3.6", "version": "2.3.6",
"resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz", "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
@ -4711,6 +4864,60 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/rollup-plugin-visualizer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-6.0.3.tgz",
"integrity": "sha512-ZU41GwrkDcCpVoffviuM9Clwjy5fcUxlz0oMoTXTYsK+tcIFzbdacnrr2n8TXcHxbGKKXtOdjxM2HUS4HjkwIw==",
"dev": true,
"license": "MIT",
"dependencies": {
"open": "^8.0.0",
"picomatch": "^4.0.2",
"source-map": "^0.7.4",
"yargs": "^17.5.1"
},
"bin": {
"rollup-plugin-visualizer": "dist/bin/cli.js"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"rolldown": "1.x || ^1.0.0-beta",
"rollup": "2.x || 3.x || 4.x"
},
"peerDependenciesMeta": {
"rolldown": {
"optional": true
},
"rollup": {
"optional": true
}
}
},
"node_modules/rollup-plugin-visualizer/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/rollup-plugin-visualizer/node_modules/source-map": {
"version": "0.7.6",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
"integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">= 12"
}
},
"node_modules/run-parallel": { "node_modules/run-parallel": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@ -5224,6 +5431,44 @@
"safe-buffer": "~5.2.0" "safe-buffer": "~5.2.0"
} }
}, },
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/string-width/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/string-width/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": { "node_modules/strip-ansi": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
@ -6384,6 +6629,63 @@
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD" "license": "0BSD"
}, },
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrap-ansi/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/wrap-ansi/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wrappy": { "node_modules/wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@ -6411,6 +6713,16 @@
} }
} }
}, },
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yaml": { "node_modules/yaml": {
"version": "2.3.4", "version": "2.3.4",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz",
@ -6420,6 +6732,35 @@
"node": ">= 14" "node": ">= 14"
} }
}, },
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/yup": { "node_modules/yup": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/yup/-/yup-1.4.0.tgz", "resolved": "https://registry.npmjs.org/yup/-/yup-1.4.0.tgz",

View File

@ -41,6 +41,7 @@
"autoprefixer": "^10.4.16", "autoprefixer": "^10.4.16",
"daisyui": "^4.4.17", "daisyui": "^4.4.17",
"postcss": "^8.4.31", "postcss": "^8.4.31",
"rollup-plugin-visualizer": "^6.0.3",
"sass": "^1.69.5", "sass": "^1.69.5",
"sass-loader": "^13.3.2", "sass-loader": "^13.3.2",
"tailwindcss": "^3.3.5", "tailwindcss": "^3.3.5",

View File

@ -16,6 +16,8 @@
"formats": "档案格式" "formats": "档案格式"
}, },
"dashboard": { "dashboard": {
"production_quantity": "生产量",
"today_production_rate": "今日生产完成率",
"yesterday_today": "昨天/今天", "yesterday_today": "昨天/今天",
"elec_consumption_comparison": "用电量比较", "elec_consumption_comparison": "用电量比较",
"elec_consumption_comparison_trend": "用电量比较趋势", "elec_consumption_comparison_trend": "用电量比较趋势",
@ -45,8 +47,10 @@
"last_month": "上月", "last_month": "上月",
"this_year": "今年", "this_year": "今年",
"last_year": "去年", "last_year": "去年",
"refrig_temp": "冷藏温度", "refrig_chart": "冷藏趨勢",
"indoor_temp": "室内温度", "indoor_chart": "室內趨勢",
"temperature": "温度",
"humidity": "湿度",
"alerts_data": "异常资料" "alerts_data": "异常资料"
}, },
"history": { "history": {
@ -91,7 +95,7 @@
"year_elec_consumption": "今年用电度数(kWh)", "year_elec_consumption": "今年用电度数(kWh)",
"interval_elec_consumption": "区间用电度数(kWh)", "interval_elec_consumption": "区间用电度数(kWh)",
"monthly_elec_consumption": "每月用电分析", "monthly_elec_consumption": "每月用电分析",
"monthly_carbon_emission_and_reduction": "每月碳排当量 (kgCO2e)", "daily_carbon_emission_and_reduction": "每日碳排当量 (kgCO2e)",
"monthly_bill_power": "每月计费度数 (kWh)", "monthly_bill_power": "每月计费度数 (kWh)",
"interval_bill_degree": "区间计费度数", "interval_bill_degree": "区间计费度数",
"peak": "尖峰", "peak": "尖峰",

View File

@ -16,6 +16,8 @@
"formats": "檔案格式" "formats": "檔案格式"
}, },
"dashboard": { "dashboard": {
"production_quantity": "生產量",
"today_production_rate": "今日生產完成率",
"yesterday_today": "昨天/今天", "yesterday_today": "昨天/今天",
"elec_consumption_comparison": "用電量比較", "elec_consumption_comparison": "用電量比較",
"elec_consumption_comparison_trend": "用電量比較趨勢", "elec_consumption_comparison_trend": "用電量比較趨勢",
@ -45,9 +47,11 @@
"last_month": "上月", "last_month": "上月",
"this_year": "今年", "this_year": "今年",
"last_year": "去年", "last_year": "去年",
"refrig_temp":"冷藏溫度", "refrig_chart": "冷藏趨勢",
"indoor_temp":"室內溫度", "indoor_chart": "室內趨勢",
"alerts_data":"異常資料" "temperature": "溫度",
"humidity": "濕度",
"alerts_data": "異常資料"
}, },
"history": { "history": {
"title": "歷史資料", "title": "歷史資料",
@ -91,7 +95,7 @@
"year_elec_consumption": "今年用電度數(kWh)", "year_elec_consumption": "今年用電度數(kWh)",
"interval_elec_consumption": "區間用電度數(kWh)", "interval_elec_consumption": "區間用電度數(kWh)",
"monthly_elec_consumption": "每月用電分析", "monthly_elec_consumption": "每月用電分析",
"monthly_carbon_emission_and_reduction": "每月碳排當量 (kgCO2e)", "daily_carbon_emission_and_reduction": "每日碳排當量 (kgCO2e)",
"monthly_bill_power": "每月計費度數 (kWh)", "monthly_bill_power": "每月計費度數 (kWh)",
"interval_bill_degree": "區間計費度數", "interval_bill_degree": "區間計費度數",
"peak": "尖峰", "peak": "尖峰",
@ -155,8 +159,8 @@
"latest_elec_consumption": "最新用電度數", "latest_elec_consumption": "最新用電度數",
"daily_elec": "每日用電度數", "daily_elec": "每日用電度數",
"line_voltage": "線電壓", "line_voltage": "線電壓",
"electric_current" : "電流", "electric_current": "電流",
"monthly_elec_bill":"每月電費分析" "monthly_elec_bill": "每月電費分析"
}, },
"alarm": { "alarm": {
"title": "顯示警告", "title": "顯示警告",
@ -182,7 +186,7 @@
"alarmClass": "告警條件", "alarmClass": "告警條件",
"device_name": "設備名稱", "device_name": "設備名稱",
"device_number": "設備編號", "device_number": "設備編號",
"device_point_name":"點位名稱", "device_point_name": "點位名稱",
"date": "發生日期", "date": "發生日期",
"time": "發生時間", "time": "發生時間",
"error_msg": "異常原因", "error_msg": "異常原因",
@ -389,28 +393,28 @@
"restore": "復原", "restore": "復原",
"stop_edit": "停止修改", "stop_edit": "停止修改",
"start_edit": "開始修改", "start_edit": "開始修改",
"convert":"轉換" "convert": "轉換"
}, },
"msg": { "msg": {
"sure_to_delete": "是否確認刪除該項目?", "sure_to_delete": "是否確認刪除該項目?",
"sure_to_delete_permanent": "是否確認永久刪除該項目?", "sure_to_delete_permanent": "是否確認永久刪除該項目?",
"delete_success": "刪除成功", "delete_success": "刪除成功",
"delete_failed": "刪除失敗", "delete_failed": "刪除失敗",
"mqtt_refresh":"重新設定成功" "mqtt_refresh": "重新設定成功"
}, },
"setting": { "setting": {
"MQTT_parse": "MQTT 解析", "MQTT_parse": "MQTT 解析",
"schema":"架構", "schema": "架構",
"point":"點位", "point": "點位",
"description":"描述", "description": "描述",
"IoT_point_name":"IoT 點位名稱", "IoT_point_name": "IoT 點位名稱",
"IoT_point_code":"IoT 點位代號", "IoT_point_code": "IoT 點位代號",
"number_of_decimal_places":"小數位數", "number_of_decimal_places": "小數位數",
"boolean_value":"布林值", "boolean_value": "布林值",
"hide_point":"點位顯示", "hide_point": "點位顯示",
"schema_name":"架構名稱", "schema_name": "架構名稱",
"IoT_point_structure" :"IoT點位結構", "IoT_point_structure": "IoT點位結構",
"system_point_name":"系統點位名稱", "system_point_name": "系統點位名稱",
"json_format_text": "請貼上 JSON 格式數據", "json_format_text": "請貼上 JSON 格式數據",
"json_click_text": "請在左側輸入JSON並點選轉換按鈕" "json_click_text": "請在左側輸入JSON並點選轉換按鈕"
} }

View File

@ -16,6 +16,8 @@
"formats": "File formats" "formats": "File formats"
}, },
"dashboard": { "dashboard": {
"production_quantity": "Production Quantity",
"today_production_rate": "Today's Production Completion Rate",
"yesterday_today": "Yesterday / Today's", "yesterday_today": "Yesterday / Today's",
"elec_consumption_comparison": "Electricity Consumption Comparison", "elec_consumption_comparison": "Electricity Consumption Comparison",
"elec_consumption_comparison_trend": "Electricity Consumption Comparison Trend", "elec_consumption_comparison_trend": "Electricity Consumption Comparison Trend",
@ -45,8 +47,10 @@
"last_month": "Last month", "last_month": "Last month",
"this_year": "This year", "this_year": "This year",
"last_year": "Last year", "last_year": "Last year",
"refrig_temp": "Refrigeration temperature", "refrig_chart": "Refrigeration chart",
"indoor_temp": "Indoor temperature", "indoor_chart": "Indoor chart",
"temperature": "Temp.",
"humidity": "Hum.",
"alerts_data": "Abnormal data" "alerts_data": "Abnormal data"
}, },
"history": { "history": {
@ -91,7 +95,7 @@
"year_elec_consumption": "This year's electricity consumption (kWh)", "year_elec_consumption": "This year's electricity consumption (kWh)",
"interval_elec_consumption": "Interval electricity consumption (kWh)", "interval_elec_consumption": "Interval electricity consumption (kWh)",
"monthly_elec_consumption": "Monthly electricity consumption analysis", "monthly_elec_consumption": "Monthly electricity consumption analysis",
"monthly_carbon_emission_and_reduction": "Monthly carbon emission equivalent (kgCO2e)", "daily_carbon_emission_and_reduction": "Daily carbon emission equivalent (kgCO2e)",
"monthly_bill_power": "Monthly billing power (kWh)", "monthly_bill_power": "Monthly billing power (kWh)",
"interval_bill_degree": "Interval billing degree", "interval_bill_degree": "Interval billing degree",
"peak": "Peak", "peak": "Peak",

View File

@ -1,93 +1,80 @@
import { createRouter, createWebHashHistory } from "vue-router"; import { createRouter, createWebHashHistory } from "vue-router";
import Dashboard from "@/views/dashboard/Dashboard.vue";
import History from "@/views/history/History.vue";
import Operation from "@/views/operation/Operation.vue";
import GraphManagement from "@/views/graphManagement/GraphManagement.vue";
import AccountManagement from "@/views/accountManagement/AccountManagement.vue";
import AssetManagement from "@/views/AssetManagement/AssetManagement.vue";
import AlertManagement from "@/views/alert/AlertManagement.vue";
import EnergyManagement from "@/views/energyManagement/EnergyManagement.vue";
import SettingManagement from "@/views/setting/SettingManagement.vue";
import Login from "@/views/login/Login.vue";
import useUserInfoStore from "@/stores/useUserInfoStore"; import useUserInfoStore from "@/stores/useUserInfoStore";
import useGetCookie from "@/hooks/useGetCookie"; import useGetCookie from "@/hooks/useGetCookie";
import System from "@/views/system/System.vue"; import System from "@/views/system/System.vue";
import SystemFloor from "@/views/system/SystemFloor.vue"; import SystemFloor from "@/views/system/SystemFloor.vue";
import Test from "@/views/Test.vue";
import SystemMain from "@/views/system/SystemMain.vue";
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL), history: createWebHashHistory(import.meta.env.BASE_URL),
// linkActiveClass: "is-active",
routes: [ routes: [
{ {
path: "/login", path: "/login",
name: "login", name: "login",
component: Login, component: () => import("@/views/login/Login.vue"),
}, },
{ {
path: "/dashboard", path: "/dashboard",
index: true, index: true,
name: "dashboard", name: "dashboard",
component: Dashboard, component: () => import("@/views/dashboard/Dashboard.vue"),
}, },
{ {
path: "/system/:main_system_id/:sub_system_id", path: "/system/:main_system_id/:sub_system_id",
name: "system", name: "system",
component: System, component: () => import("@/views/system/System.vue"),
children: [ children: [
{ {
path: ":floor_id", path: ":floor_id",
name: "sub_system", name: "sub_system",
component: SystemMain, component: () => import("@/views/system/SystemMain.vue"),
}, },
], ],
}, },
{ {
path: "/historyData", path: "/historyData",
name: "history", name: "history",
component: History, component: () => import("@/views/history/History.vue"),
}, },
{ {
path: "/operation", path: "/operation",
name: "operation", name: "operation",
component: Operation, component: () => import("@/views/operation/Operation.vue"),
}, },
{ {
path: "/graphManagement", path: "/graphManagement",
name: "graphManagement", name: "graphManagement",
component: GraphManagement, component: () => import("@/views/graphManagement/GraphManagement.vue"),
}, },
{ {
path: "/accountManagement", path: "/accountManagement",
name: "accountManagement", name: "accountManagement",
component: AccountManagement, component: () =>
import("@/views/accountManagement/AccountManagement.vue"),
}, },
{ {
path: "/assetManagement", path: "/assetManagement",
name: "assetManagement", name: "assetManagement",
component: AssetManagement, component: () => import("@/views/AssetManagement/AssetManagement.vue"),
}, },
{ {
path: "/alert", path: "/alert",
name: "alert", name: "alert",
component: AlertManagement, component: () => import("@/views/alert/AlertManagement.vue"),
}, },
{ {
path: "/energyManagement", path: "/energyManagement",
name: "energyManagement", name: "energyManagement",
component: EnergyManagement, component: () => import("@/views/energyManagement/EnergyManagement.vue"),
}, },
{ {
path: "/setting/:main_system_id/:sub_system_id/:type", path: "/setting/:main_system_id/:sub_system_id/:type",
name: "setting", name: "setting",
component: SettingManagement, component: () => import("@/views/setting/SettingManagement.vue"),
}, },
{ {
path: "/mytestfile/mjm", path: "/mytestfile/mjm",
name: "mytestfile", name: "mytestfile",
component: Test, component: () => import("@/views/Test.vue"),
}, },
], ],
}); });

View File

@ -8,6 +8,7 @@ import useBuildingStore from "@/stores/useBuildingStore";
const store = useBuildingStore(); const store = useBuildingStore();
const { t, locale } = useI18n(); const { t, locale } = useI18n();
const taipower_data = ref([]); const taipower_data = ref([]);
const elecUseDayData = ref([]);
const carbonValue = ref(null); const carbonValue = ref(null);
const carbonData = ref(null); const carbonData = ref(null);
const search_data = computed(() => { const search_data = computed(() => {
@ -75,7 +76,7 @@ const getData = async (value) => {
const res = await getTaipower(value); const res = await getTaipower(value);
if (res.isSuccess) { if (res.isSuccess) {
taipower_data.value = res.data taipower_data.value = res.data
? res.data.sort((a, b) => a.month.localeCompare(b.month)) ? res.data.sort((a, b) => a.day.localeCompare(b.month))
: []; : [];
} }
}; };
@ -107,6 +108,7 @@ watch(
JSON.stringify(newValue) !== JSON.stringify(oldValue) JSON.stringify(newValue) !== JSON.stringify(oldValue)
) { ) {
getData(newValue); getData(newValue);
getElecUseDayData(newValue);
} }
}, },
{ {
@ -119,12 +121,12 @@ watch(
watch( watch(
taipower_data, taipower_data,
() => { () => {
// //
const months = taipower_data.value.map((item) => item.month); const days = taipower_data.value.map((item) => item.day);
const carbonTotal = taipower_data.value.map((item) => item.carbon); const carbonTotal = taipower_data.value.map((item) => item.carbon);
// xAxis series //
defaultChartOption.value.xAxis.data = months; defaultChartOption.value.xAxis.data = days;
defaultChartOption.value.series[0].data = carbonTotal; defaultChartOption.value.series[0].data = carbonTotal;
}, },
{ deep: true } { deep: true }
@ -144,7 +146,7 @@ onMounted(() => {
<div class="mb-3 relative"> <div class="mb-3 relative">
<h3 class="font-bold text-xl text-center"> <h3 class="font-bold text-xl text-center">
<span class="text-info"> <span class="text-info">
{{ $t("energy.monthly_carbon_emission_and_reduction") }} {{ $t("energy.daily_carbon_emission_and_reduction") }}
</span> </span>
</h3> </h3>
<DashboardEmissionModal :carbonData="carbonData" :getData="getCarbonData" /> <DashboardEmissionModal :carbonData="carbonData" :getData="getCarbonData" />

View File

@ -2,12 +2,14 @@
import LineChart from "@/components/chart/LineChart.vue"; import LineChart from "@/components/chart/LineChart.vue";
import { SECOND_CHART_COLOR } from "@/constant"; import { SECOND_CHART_COLOR } from "@/constant";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { ref, watch, onUnmounted } from "vue"; import { ref, watch, onMounted, onUnmounted, computed } from "vue";
import useActiveBtn from "@/hooks/useActiveBtn"; import useActiveBtn from "@/hooks/useActiveBtn";
import { getDashboardTemp } from "@/apis/dashboard"; import { getDashboardTemp } from "@/apis/dashboard";
import useSearchParams from "@/hooks/useSearchParam"; import useSearchParams from "@/hooks/useSearchParam";
import useBuildingStore from "@/stores/useBuildingStore"; import useBuildingStore from "@/stores/useBuildingStore";
import { useI18n } from "vue-i18n";
const { t, locale } = useI18n();
const { searchParams } = useSearchParams(); const { searchParams } = useSearchParams();
const buildingStore = useBuildingStore(); const buildingStore = useBuildingStore();
const intervalType = "immediateTemp"; const intervalType = "immediateTemp";
@ -17,6 +19,9 @@ const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
const data = ref([]); const data = ref([]);
const other_real_temp_chart = ref(null); const other_real_temp_chart = ref(null);
const currentOptionType = ref(1); // 1: , 2:
const noData = ref(false);
const defaultChartOption = ref({ const defaultChartOption = ref({
tooltip: { tooltip: {
trigger: "axis", trigger: "axis",
@ -36,72 +41,50 @@ const defaultChartOption = ref({
containLabel: true, containLabel: true,
}, },
xAxis: { xAxis: {
// type: 'time',
type: "category", type: "category",
splitLine: { splitLine: { show: false },
show: false, axisLabel: { color: "#ffffff" },
},
axisLabel: {
color: "#ffffff",
},
data: [], data: [],
}, },
yAxis: { yAxis: {
type: "value", type: "value",
splitLine: { splitLine: { show: false },
show: false, axisLabel: { color: "#ffffff" },
},
axisLabel: {
color: "#ffffff",
},
}, },
series: [], series: [],
}); });
const getData = async (tempOption) => { const getData = async () => {
const res = await getDashboardTemp({ const res = await getDashboardTemp({
building_guid: buildingStore.selectedBuilding.building_guid, building_guid: buildingStore.selectedBuilding.building_guid,
tempOption, // 1 2: tempOption: 2, // tempOption 1: 2:
timeInterval: 1, // =>1.4.8 timeInterval: 1,
option: 2, // 2: option: currentOptionType.value, // option1: 2:
}); });
if (res.isSuccess) { if (res.isSuccess) {
if (tempOption === 1) { const key = "冷藏溫度";
console.log("室內溼度資料:", res.data["室溫"]); const label = currentOptionType.value === 1 ? "溫度" : "濕度";
data.value = res.data["室溫"] || []; console.log(`冷藏${label}資料:`, res.data[key]);
} else { data.value = res.data[key] || [];
console.log("冷藏溼度資料:", res.data["冷藏溫度"]); noData.value = !data.value || data.value.length === 0;
data.value = res.data["冷藏溫度"] || []; } else {
} noData.value = true;
} }
}; };
// const buttonItems = computed(() => [
{ key: 1, title: t("dashboard.temperature"), active: true },
{ key: 2, title: t("dashboard.humidity"), active: false },
]);
//
watch( watch(
() => buildingStore.sysConfig, () => locale.value,
(newValue) => { () => {
if (newValue) { setItems(buttonItems.value);
// sysConfig
const itemsArr = buildingStore.sysConfig?.humiture_options
? Object.entries(buildingStore.sysConfig.humiture_options).map(
([key, title], index) => ({
key: Number(key),
title,
active: index === 0,
})
)
: [];
setItems(itemsArr);
} else {
//
if (timeoutTimer.value) {
clearInterval(timeoutTimer.value);
}
}
}, },
{ { immediate: true }
immediate: true,
}
); );
watch( watch(
@ -111,18 +94,15 @@ watch(
clearInterval(timeoutTimer.value); clearInterval(timeoutTimer.value);
} }
if (newValue?.key) { if (newValue?.key === 1 || newValue?.key === 2) {
getData(newValue.key); currentOptionType.value = newValue.key;
// getData();
timeoutTimer.value = setInterval(() => { timeoutTimer.value = setInterval(() => {
getData(newValue.key); getData();
}, 60 * 1000); }, 60 * 1000);
} }
}, },
{ { immediate: true, deep: true }
immediate: true,
deep: true,
}
); );
watch( watch(
@ -170,7 +150,6 @@ watch(
); );
onUnmounted(() => { onUnmounted(() => {
//
if (timeoutTimer.value) { if (timeoutTimer.value) {
clearInterval(timeoutTimer.value); clearInterval(timeoutTimer.value);
} }
@ -178,18 +157,23 @@ onUnmounted(() => {
</script> </script>
<template> <template>
<h3 class="text-info text-xl text-center">濕度趨勢</h3> <h3 class="text-info text-xl text-center">
{{ $t("dashboard.refrig_chart") }}
</h3>
<div className="my-3 w-full flex justify-center relative"> <div className="my-3 w-full flex justify-center relative">
<ButtonConnectedGroup <ButtonConnectedGroup
:items="items" :items="items"
:onclick=" :onclick="(e, item) => changeActiveBtn(item)"
(e, item) => {
changeActiveBtn(item);
}
"
/> />
</div> </div>
<div
v-if="noData"
class="text-center text-white text-lg min-h-[260px] flex items-center justify-center"
>
無資料
</div>
<LineChart <LineChart
v-else
id="dashboard_other_real_temp" id="dashboard_other_real_temp"
class="min-h-[260px] max-h-fit" class="min-h-[260px] max-h-fit"
:option="defaultChartOption" :option="defaultChartOption"

View File

@ -55,7 +55,7 @@ const defaultChartOption = ref({
}); });
const timeoutTimer = ref(""); const timeoutTimer = ref("");
const indoor_temp_chart = ref(null); const indoor_chart = ref(null);
const data = ref([]); const data = ref([]);
const getData = async (timeInterval) => { const getData = async (timeInterval) => {
@ -75,7 +75,7 @@ watch(
data, data,
(newValue) => { (newValue) => {
newValue.length > 0 && newValue.length > 0 &&
indoor_temp_chart.value.chart.setOption({ indoor_chart.value.chart.setOption({
legend: { legend: {
data: newValue.map(({ full_name }) => full_name), data: newValue.map(({ full_name }) => full_name),
}, },
@ -143,14 +143,14 @@ onUnmounted(() => {
<template> <template>
<h3 class="text-info font-bold text-xl text-center mb-3 relative"> <h3 class="text-info font-bold text-xl text-center mb-3 relative">
<span>{{ $t("dashboard.indoor_temp") }}</span> <span>{{ $t("dashboard.indoor_chart") }}</span>
</h3> </h3>
<LineChart <LineChart
id="dashboard_indoor_temp" id="dashboard_indoor_chart"
class="min-h-[300px] max-h-fit" class="min-h-[300px] max-h-fit"
:option="defaultChartOption" :option="defaultChartOption"
ref="indoor_temp_chart" ref="indoor_chart"
/> />
</template> </template>

View File

@ -120,7 +120,9 @@ onMounted(() => {
</script> </script>
<template> <template>
<div> <div>
<h3 class="text-info text-xl text-center mb-3">生產量</h3> <h3 class="text-info text-xl text-center mb-3">
{{ $t("dashboard.production_quantity") }}
</h3>
<div class="w-full grid grid-cols-3"> <div class="w-full grid grid-cols-3">
<div> <div>
<GaugeChart <GaugeChart

View File

@ -44,15 +44,17 @@ onMounted(() => {
</script> </script>
<template> <template>
<DashboardProductCompleteModal/> <DashboardProductCompleteModal />
<div class="mb-3 relative"> <div class="mb-3 relative">
<h3 class="text-info text-xl text-center">今日生產完成率 </h3> <h3 class="text-info text-xl text-center">
{{ $t("dashboard.today_production_rate") }} ()
</h3>
<button <button
type="button" type="button"
class="btn btn-xs btn-success absolute top-0 right-0" class="btn btn-xs btn-success absolute top-0 right-0"
@click.stop="openModal" @click.stop="openModal"
> >
設定 {{ $t("button.edit") }}
</button> </button>
</div> </div>
<div className="my-3 w-full flex justify-center relative"> <div className="my-3 w-full flex justify-center relative">

View File

@ -146,7 +146,7 @@ onUnmounted(() => {
<template> <template>
<h3 class="text-info font-bold text-xl text-center mb-3 relative"> <h3 class="text-info font-bold text-xl text-center mb-3 relative">
<span>{{ $t("dashboard.refrig_temp") }}</span> <span>{{ $t("dashboard.refrig_chart") }}</span>
</h3> </h3>
<LineChart <LineChart

View File

@ -2,12 +2,14 @@
import LineChart from "@/components/chart/LineChart.vue"; import LineChart from "@/components/chart/LineChart.vue";
import { SECOND_CHART_COLOR } from "@/constant"; import { SECOND_CHART_COLOR } from "@/constant";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { ref, watch, onUnmounted } from "vue"; import { ref, watch, onMounted, onUnmounted, computed } from "vue";
import useActiveBtn from "@/hooks/useActiveBtn"; import useActiveBtn from "@/hooks/useActiveBtn";
import { getDashboardTemp } from "@/apis/dashboard"; import { getDashboardTemp } from "@/apis/dashboard";
import useSearchParams from "@/hooks/useSearchParam"; import useSearchParams from "@/hooks/useSearchParam";
import useBuildingStore from "@/stores/useBuildingStore"; import useBuildingStore from "@/stores/useBuildingStore";
import { useI18n } from "vue-i18n";
const { t, locale } = useI18n();
const { searchParams } = useSearchParams(); const { searchParams } = useSearchParams();
const buildingStore = useBuildingStore(); const buildingStore = useBuildingStore();
const intervalType = "immediateTemp"; const intervalType = "immediateTemp";
@ -17,6 +19,9 @@ const { items, changeActiveBtn, setItems, selectedBtn } = useActiveBtn();
const data = ref([]); const data = ref([]);
const other_real_temp_chart = ref(null); const other_real_temp_chart = ref(null);
const currentOptionType = ref(1); // 1: , 2:
const noData = ref(false);
const defaultChartOption = ref({ const defaultChartOption = ref({
tooltip: { tooltip: {
trigger: "axis", trigger: "axis",
@ -36,24 +41,15 @@ const defaultChartOption = ref({
containLabel: true, containLabel: true,
}, },
xAxis: { xAxis: {
// type: 'time',
type: "category", type: "category",
splitLine: { splitLine: { show: false },
show: false, axisLabel: { color: "#ffffff" },
},
axisLabel: {
color: "#ffffff",
},
data: [], data: [],
}, },
yAxis: { yAxis: {
type: "value", type: "value",
splitLine: { splitLine: { show: false },
show: false, axisLabel: { color: "#ffffff" },
},
axisLabel: {
color: "#ffffff",
},
}, },
series: [], series: [],
}); });
@ -61,47 +57,33 @@ const defaultChartOption = ref({
const getData = async (tempOption) => { const getData = async (tempOption) => {
const res = await getDashboardTemp({ const res = await getDashboardTemp({
building_guid: buildingStore.selectedBuilding.building_guid, building_guid: buildingStore.selectedBuilding.building_guid,
tempOption, // 1 2: tempOption, // tempOption 1: 2:
timeInterval: 1, // =>1.4.8 timeInterval: 1,
option: 1, // 1: option: currentOptionType.value, // option1: 2:
}); });
if (res.isSuccess) { if (res.isSuccess) {
if (tempOption === 1) { const key = "室溫";
console.log("室內溫度資料:", res.data["室溫"]); const label = currentOptionType.value === 1 ? "溫度" : "濕度";
data.value = res.data["室溫"] || []; // console.log(`${label}:`, res.data[key]);
} else { data.value = res.data[key] || [];
console.log("冷藏溫度資料:", res.data["冷藏溫度"]); noData.value = !data.value || data.value.length === 0;
data.value = res.data["冷藏溫度"] || []; } else {
} noData.value = true;
} }
}; };
// const buttonItems = computed(() => [
{ key: 1, title: t("dashboard.temperature"), active: true },
{ key: 2, title: t("dashboard.humidity"), active: false },
]);
watch( watch(
() => buildingStore.sysConfig, () => locale.value,
(newValue) => { () => {
if (newValue) { setItems(buttonItems.value);
// sysConfig
const itemsArr = buildingStore.sysConfig?.humiture_options
? Object.entries(buildingStore.sysConfig.humiture_options).map(
([key, title], index) => ({
key: Number(key),
title,
active: index === 0,
})
)
: [];
setItems(itemsArr);
} else {
//
if (timeoutTimer.value) {
clearInterval(timeoutTimer.value);
}
}
}, },
{ { immediate: true }
immediate: true,
}
); );
watch( watch(
@ -111,18 +93,15 @@ watch(
clearInterval(timeoutTimer.value); clearInterval(timeoutTimer.value);
} }
if (newValue?.key) { if (newValue?.key === 1 || newValue?.key === 2) {
getData(newValue.key); currentOptionType.value = newValue.key; // 1:, 2:
// getData(1); // tempOption 1
timeoutTimer.value = setInterval(() => { timeoutTimer.value = setInterval(() => {
getData(newValue.key); getData(1);
}, 60 * 1000); }, 60 * 1000);
} }
}, },
{ { immediate: true, deep: true }
immediate: true,
deep: true,
}
); );
watch( watch(
@ -170,7 +149,6 @@ watch(
); );
onUnmounted(() => { onUnmounted(() => {
//
if (timeoutTimer.value) { if (timeoutTimer.value) {
clearInterval(timeoutTimer.value); clearInterval(timeoutTimer.value);
} }
@ -178,17 +156,21 @@ onUnmounted(() => {
</script> </script>
<template> <template>
<h3 class="text-info text-xl text-center">溫度趨勢</h3> <h3 class="text-info text-xl text-center">
{{ $t("dashboard.indoor_chart") }}
</h3>
<div className="my-3 w-full flex justify-center relative"> <div className="my-3 w-full flex justify-center relative">
<ButtonConnectedGroup <ButtonConnectedGroup
:items="items" :items="items"
:onclick=" :onclick="(e, item) => changeActiveBtn(item)"
(e, item) => {
changeActiveBtn(item);
}
"
/> />
</div> </div>
<div
v-if="noData"
class="text-center text-white text-lg min-h-[260px] flex items-center justify-center"
>
無資料
</div>
<LineChart <LineChart
id="dashboard_other_real_temp" id="dashboard_other_real_temp"
class="min-h-[260px] max-h-fit" class="min-h-[260px] max-h-fit"

View File

@ -109,7 +109,7 @@ onMounted(() => {
<div class="bg-slate-800 p-3"> <div class="bg-slate-800 p-3">
<div class="flex items-center"> <div class="flex items-center">
<div class="text-white mb-3 text-base"> <div class="text-white mb-3 text-base">
{{ $t("energy.monthly_carbon_emission_and_reduction") }} {{ $t("energy.daily_carbon_emission_and_reduction") }}
</div> </div>
<CarbonEmissionModal <CarbonEmissionModal
:carbonData="carbonData" :carbonData="carbonData"

View File

@ -3,7 +3,7 @@ import HistorySidebar from "./components/HistorySidebar.vue";
import HistorySearch from "./components/HistorySearch.vue"; import HistorySearch from "./components/HistorySearch.vue";
import HistoryTable from "./components/HistoryTable.vue"; import HistoryTable from "./components/HistoryTable.vue";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { ref, provide } from "vue"; import { ref, provide, watch } from "vue";
const tableData = ref([]); const tableData = ref([]);
const loading = ref(false); const loading = ref(false);
@ -21,6 +21,10 @@ provide("history_table_data", {
loading, loading,
updateLoading, updateLoading,
}); });
watch(tableData, (newVal) => {
console.log("🔍 提供端 tableData 改變:", newVal);
});
</script> </script>
<template> <template>

View File

@ -11,7 +11,6 @@ const { t } = useI18n();
const { tableData } = inject("history_table_data"); const { tableData } = inject("history_table_data");
const history_chart = ref(null); const history_chart = ref(null);
//
const defaultChartOption = { const defaultChartOption = {
tooltip: { tooltip: {
trigger: "axis", trigger: "axis",
@ -51,7 +50,7 @@ const defaultChartOption = {
formatter: (value) => formatter: (value) =>
searchParams.value.Type == 2 searchParams.value.Type == 2
? dayjs(value).format("HH:mm") ? dayjs(value).format("HH:mm")
: dayjs(value).format("MM-DD"), // : dayjs(value).format("MM-DD"),
}, },
data: [], data: [],
}, },
@ -59,15 +58,12 @@ const defaultChartOption = {
type: "value", type: "value",
splitLine: { show: false }, splitLine: { show: false },
axisLabel: { color: "#ffffff" }, axisLabel: { color: "#ffffff" },
// interval: 100, //Y
min: "dataMin", min: "dataMin",
max: "dataMax", max: "dataMax",
// splitArea: { show: false },
}, },
series: [], series: [],
}; };
//
const formatChartData = (data) => { const formatChartData = (data) => {
return data.reduce((acc, item) => { return data.reduce((acc, item) => {
const seriesKey = `${item.device_name || ""}_${item.item_name || ""}`; const seriesKey = `${item.device_name || ""}_${item.item_name || ""}`;
@ -86,19 +82,33 @@ const formatChartData = (data) => {
}, {}); }, {});
}; };
// tableData
watch( watch(
tableData, tableData,
(newData) => { (newData) => {
if (newData?.length > 0) { if (newData?.length > 0) {
const formattedData = formatChartData(newData); const formattedData = formatChartData(newData);
const xDataRaw = formattedData[Object.keys(formattedData)[0]].timestamps;
const totalPoints = xDataRaw.length;
const tickCount = 30;
const interval = Math.max(1, Math.floor(totalPoints / (tickCount - 1)));
const sampledTime = xDataRaw.filter((_, idx) => idx % interval === 0);
if (sampledTime[sampledTime.length - 1] !== xDataRaw[totalPoints - 1]) {
sampledTime.push(xDataRaw[totalPoints - 1]);
}
const series = Object.keys(formattedData).map((seriesKey, index) => { const series = Object.keys(formattedData).map((seriesKey, index) => {
const { maxValue, minValue, averageValue } = formattedData[seriesKey]; const { timestamps, values, maxValue, minValue, averageValue } =
formattedData[seriesKey];
const timeToValue = Object.fromEntries(
timestamps.map((t, i) => [t, values[i]])
);
return { return {
name: seriesKey, name: seriesKey,
type: "line", type: "line",
data: formattedData[seriesKey].values, data: sampledTime.map((t) => timeToValue[t] ?? null),
showSymbol: false, showSymbol: false,
itemStyle: { itemStyle: {
color: SECOND_CHART_COLOR[index % SECOND_CHART_COLOR.length], color: SECOND_CHART_COLOR[index % SECOND_CHART_COLOR.length],
@ -127,7 +137,6 @@ watch(
}; };
}); });
//
history_chart.value?.chart.setOption( history_chart.value?.chart.setOption(
{ {
...defaultChartOption, ...defaultChartOption,
@ -137,10 +146,7 @@ watch(
}, },
xAxis: { xAxis: {
...defaultChartOption.xAxis, ...defaultChartOption.xAxis,
data: data: sampledTime,
Object.keys(formattedData).length > 0
? formattedData[Object.keys(formattedData)[0]].timestamps
: [],
}, },
series, series,
}, },
@ -161,4 +167,4 @@ watch(
/> />
</template> </template>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

4949
stats.html Normal file

File diff suppressed because one or more lines are too long

View File

@ -4,7 +4,7 @@ module.exports = {
"./node_modules/vue-tailwind-datepicker/**/*.js", "./node_modules/vue-tailwind-datepicker/**/*.js",
], ],
purge: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"], purge: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
darkMode: false, // or 'media' or 'class' darkMode: false,
theme: { theme: {
extend: { extend: {
backgroundImage: { backgroundImage: {