From 1534aee0339dee8000cdd26c21797cf3ad391f7a Mon Sep 17 00:00:00 2001 From: riku <risaku@163.com> Date: 星期一, 06 五月 2024 17:33:23 +0800 Subject: [PATCH] 新增折线图模块功能 --- src/utils/chart/chart-option.js | 100 +++++ src/assets/main.css | 6 src/components/SliderBar.vue | 51 ++ src/views/historymode/HistoryMode.vue | 29 + package-lock.json | 59 +++ src/assets/border.css | 11 src/components/monitor/FactorCheckbox.vue | 64 +++ src/components.d.ts | 8 src/components/search/SearchBar.vue | 17 src/assets/3dmap.css | 89 ---- src/components/map/MapToolbox.vue | 16 src/components/monitor/FactorLegend.vue | 123 ++++++ src/components/core/CoreMenu.vue | 63 +++ src/model/Legend.js | 73 --- src/components/monitor/FactorRadio.vue | 4 src/constant/factor-name.js | 2 package.json | 1 src/components/core/CoreHeader.vue | 2 src/constant/checkbox-options.js | 186 +++++++++ src/styles/base.scss | 4 src/components/BaseCard.vue | 20 src/views/historymode/component/TrendAnalysis.vue | 41 ++ src/components/monitor/LineChart.vue | 115 ++++++ 23 files changed, 889 insertions(+), 195 deletions(-) diff --git a/package-lock.json b/package-lock.json index e5db76b..e21b6be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@fortawesome/vue-fontawesome": "^3.0.6", "@vueuse/core": "^10.9.0", "axios": "^1.6.8", + "echarts": "^5.5.0", "element-plus": "^2.6.2", "moment": "^2.30.1", "pinia": "^2.1.7", @@ -1712,6 +1713,20 @@ "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true + }, + "node_modules/echarts": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.5.0.tgz", + "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.5.0" + } + }, + "node_modules/echarts/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" }, "node_modules/editorconfig": { "version": "1.0.4", @@ -4424,6 +4439,19 @@ "engines": { "node": ">=10" } + }, + "node_modules/zrender": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.5.0.tgz", + "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==", + "dependencies": { + "tslib": "2.3.0" + } + }, + "node_modules/zrender/node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" } }, "dependencies": { @@ -5582,6 +5610,22 @@ "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true + }, + "echarts": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.5.0.tgz", + "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==", + "requires": { + "tslib": "2.3.0", + "zrender": "5.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + } + } }, "editorconfig": { "version": "1.0.4", @@ -7604,6 +7648,21 @@ "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "zrender": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/zrender/-/zrender-5.5.0.tgz", + "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==", + "requires": { + "tslib": "2.3.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + } + } } } } diff --git a/package.json b/package.json index cac16c5..b754274 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@fortawesome/vue-fontawesome": "^3.0.6", "@vueuse/core": "^10.9.0", "axios": "^1.6.8", + "echarts": "^5.5.0", "element-plus": "^2.6.2", "moment": "^2.30.1", "pinia": "^2.1.7", diff --git a/src/assets/3dmap.css b/src/assets/3dmap.css index 489d0bf..8f4779b 100644 --- a/src/assets/3dmap.css +++ b/src/assets/3dmap.css @@ -95,14 +95,7 @@ /* color: #009414; */ } -.map-date-selector { - position: relative; - left: 0; - right: 0; - top: 0px; - /* padding: 0 4px; */ - /* color: ffffffbd; */ -} + .map-animation-status { position: absolute; @@ -159,86 +152,6 @@ right: 0; top: 0; z-index: 1000px; -} - -.map-mode-change { - position: absolute; - left: 10px; - top: 104px; - z-index: 1000px; - padding: 1px; - font-size: 1rem; - font-weight: 900; - /* background-color: #ffffffad; */ -} - -.map-mode-change a:hover { - color: antiquewhite; -} - -.map-mode-change .mode-btn { - white-space: nowrap; -} - -.map-mode-change .btn-selected { - color: #ffffff; - padding: 4px 16px; - cursor: pointer; - border: 2px solid var(--border-color); - background-color: #23dad1; - transform: skew(-30deg); - text-decoration: none; - /* -moz-border-radius: 6px; Old Firefox */ - /* box-shadow: 5px 0px 2.5px #888888; */ -} - -.map-mode-change .btn-selected div { - transform: skew(30deg); -} - -.map-mode-change .btn-unselected { - color: #5555557e; - padding: 4px 16px; - cursor: pointer; - background-color: #ffffff; - border: 2px solid #00000015; - transform: skew(-30deg); - text-decoration: none; - /* -moz-border-radius: 6px; Old Firefox */ - /* box-shadow: 5px 0px 2.5px #888888; */ -} - -.map-mode-change .btn-unselected div { - transform: skew(30deg); -} - -.map-date-selector .label-date { - display: box; - display: -webkit-box; - /* OLD - iOS 6-, Safari 3.1-6 */ - display: -moz-box; - /* OLD - Firefox 19- (buggy but mostly works) */ - display: -ms-flexbox; - /* TWEENER - IE 10 */ - display: -webkit-flex; - /* NEW - Chrome */ - display: flex; - align-items: center; - -webkit-align-items: center; - box-align: center; - -moz-box-align: center; - -webkit-box-align: center; - /* margin-left: 0.5rem; */ - /* margin-top: 0.8rem; */ - /* background: #d14646; */ - height: auto; -} - -.map-date-selector .label-date .label-date-title { - /* margin-right: 15px; */ - /* color: #000000; */ - font-size: 1rem; - line-height: 1.11rem; } .map-legend { diff --git a/src/assets/border.css b/src/assets/border.css index a8a5118..533bde8 100644 --- a/src/assets/border.css +++ b/src/assets/border.css @@ -41,7 +41,7 @@ width: 100%; height: 100%; background-color: var(--border-color); - z-index: 1000px; + /* z-index: 1000px; */ } /* 鑳屾櫙 */ @@ -49,13 +49,13 @@ position: relative; color: var(--font-color); background-color: var(--bg-color); - z-index: 1000px; + /* z-index: 1000; */ } .ff-border-top img.ff-img { width: 16px; height: 16px; - margin-bottom: 6px; + /* margin-bottom: 6px; */ } /* 涓夎鏍峰紡 */ @@ -97,8 +97,9 @@ position: absolute; color: var(--font-color); width: calc(100% - var(--border-width) * 2); - min-height: calc((var(--bevel-length-2) - var(--border-width)) * 1); + /* min-height: calc((var(--bevel-length-2) - var(--border-width)) * 1); */ /* background-color: brown; */ + z-index: 10; } /*****************杈规鍩烘湰灞炴�� - end **************************/ @@ -366,7 +367,7 @@ /* 鍐呭 */ .ff-content { position: relative; - display: inline-block; + display: block; } /* .ff-content .ff-border-top { diff --git a/src/assets/main.css b/src/assets/main.css index 6cae802..36e347e 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -8,6 +8,10 @@ font-weight: normal; } -.fy-container { +.p-events-auto { pointer-events: auto; +} + +.p-events-none { + pointer-events: none; } \ No newline at end of file diff --git a/src/components.d.ts b/src/components.d.ts index f936b4c..1d653d4 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -12,6 +12,9 @@ CoreHeader: typeof import('./components/core/CoreHeader.vue')['default'] CoreMenu: typeof import('./components/core/CoreMenu.vue')['default'] ElButton: typeof import('element-plus/es')['ElButton'] + ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] + ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup'] + ElCol: typeof import('element-plus/es')['ElCol'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] ElDropdown: typeof import('element-plus/es')['ElDropdown'] @@ -25,7 +28,11 @@ ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] ElRow: typeof import('element-plus/es')['ElRow'] ElSelect: typeof import('element-plus/es')['ElSelect'] + ElSlider: typeof import('element-plus/es')['ElSlider'] + FactorCheckbox: typeof import('./components/monitor/FactorCheckbox.vue')['default'] + FactorLegend: typeof import('./components/monitor/FactorLegend.vue')['default'] FactorRadio: typeof import('./components/monitor/FactorRadio.vue')['default'] + LineChart: typeof import('./components/monitor/LineChart.vue')['default'] MapToolbox: typeof import('./components/map/MapToolbox.vue')['default'] OptionDevice: typeof import('./components/search/OptionDevice.vue')['default'] OptionMission: typeof import('./components/search/OptionMission.vue')['default'] @@ -34,5 +41,6 @@ RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] SearchBar: typeof import('./components/search/SearchBar.vue')['default'] + SliderBar: typeof import('./components/SliderBar.vue')['default'] } } diff --git a/src/components/BaseCard.vue b/src/components/BaseCard.vue index a24f197..a01f0a0 100644 --- a/src/components/BaseCard.vue +++ b/src/components/BaseCard.vue @@ -6,6 +6,12 @@ <slot name="content"></slot> </div> </div> + <div class="ff-footer"> + <slot name="footer"></slot> + </div> + <div v-if="size != 'small'" class="ff-triangle"> + <div class="ff-triangle-border"></div> + </div> </div> </template> @@ -23,18 +29,26 @@ }, /** * 鏍峰紡鏈濆悜 - * left | right + * left | right | top-left */ direction: { type: String, default: 'left' + }, + /** + * 閫夋嫨鏃犺竟妗嗘柟鍚� + * r锛堝彸渚ф棤杈规锛� | t锛堥《閮ㄦ棤杈规锛� + */ + borderless: { + type: String } }, computed: { wrapClz() { - let clz = 'ff-content fy-container'; - clz += ` ff-content-${this.size}`; + let clz = 'ff-content p-events-auto'; clz += ` ff-content-${this.direction}`; + clz += ` ff-content-${this.size}`; + clz += `${this.borderless ? '-borderless-' + this.borderless : ''}`; return clz; } } diff --git a/src/components/SliderBar.vue b/src/components/SliderBar.vue new file mode 100644 index 0000000..4bb44f7 --- /dev/null +++ b/src/components/SliderBar.vue @@ -0,0 +1,51 @@ +<template> + <el-row justify="center" align="middle" class="wrap"> + <el-form-item label="鏁版嵁閲�"> + <el-select + v-model="pageSize" + placeholder="鏁版嵁閲�" + size="small" + class="w-60" + > + <el-option label="200" :value="200" /> + <el-option label="500" :value="500" /> + </el-select> + </el-form-item> + <div class="slider-wrap m-l-16"> + <el-slider v-model="progress" :marks="marks" /> + </div> + </el-row> +</template> +<script> +export default { + data() { + return { + pageSize: 200, + progress: 0, + marks: { + 0: { + style: { + color: 'white' + }, + label: '0%' + }, + 100: { + style: { + color: 'white' + }, + label: '100%' + } + } + }; + } +}; +</script> +<style scoped> +.wrap { + background-color: transparent; + height: 60px; +} +.slider-wrap { + min-width: 400px; +} +</style> diff --git a/src/components/core/CoreHeader.vue b/src/components/core/CoreHeader.vue index 2c2eff7..381807a 100644 --- a/src/components/core/CoreHeader.vue +++ b/src/components/core/CoreHeader.vue @@ -1,5 +1,5 @@ <template> - <div class="map-title ff-title flexbox-col align-items fy-container"> + <div class="map-title ff-title flexbox-col align-items p-events-auto"> <div class="map-title-content"> <div class="ff-border-bottom"></div> <div class="ff-border-top"> diff --git a/src/components/core/CoreMenu.vue b/src/components/core/CoreMenu.vue index f11b751..920f38f 100644 --- a/src/components/core/CoreMenu.vue +++ b/src/components/core/CoreMenu.vue @@ -1,9 +1,12 @@ <template> - <div class="map-mode-change flexbox flex-space-between fy-container"> + <div class="map-mode-change p-events-auto"> <template v-for="(item, index) in menu" :key="index"> <a :class="btnClz(item.selected)" @click="navTo(index)"> <div>{{ item.name }}</div> </a> + <!-- <a class="btn-selected mode-btn m-r-8" @click="navTo(index)"> + <div>{{ item.name }}</div> + </a> --> </template> <!-- <a class="mode-btn btn-selected"> <div>姹℃煋婧簮</div> @@ -69,7 +72,9 @@ computed: {}, methods: { btnClz(selected) { - return 'mode-btn ' + (selected ? 'btn-selected ' : 'btn-unselected ') + 'm-r-8'; + return ( + 'mode-btn ' + (selected ? 'btn-selected ' : 'btn-unselected ') + 'm-r-8' + ); }, navTo(index) { const m = this.menu; @@ -82,4 +87,56 @@ } }; </script> -<style scoped></style> +<style scoped> +.map-mode-change { + display: inline-flex; + position: relative; + /* left: 10px; */ + /* top: 104px; */ + padding: 0 10px; + font-size: 1rem; + font-weight: 900; + /* background-color: #ffffffad; */ +} + +.map-mode-change a:hover { + color: antiquewhite; +} + +.mode-btn { + white-space: nowrap; + background-color: green; +} + +.map-mode-change .btn-selected { + color: #ffffff; + padding: 4px 16px; + cursor: pointer; + border: 2px solid var(--border-color); + background-color: #23dad1; + transform: skew(-30deg); + text-decoration: none; + /* -moz-border-radius: 6px; Old Firefox */ + /* box-shadow: 5px 0px 2.5px #888888; */ +} + +.map-mode-change .btn-selected div { + transform: skew(30deg); +} + +.map-mode-change .btn-unselected { + color: #5555557e; + padding: 4px 16px; + cursor: pointer; + background-color: #ffffff; + border: 2px solid #00000015; + transform: skew(-30deg); + text-decoration: none; + /* -moz-border-radius: 6px; Old Firefox */ + /* box-shadow: 5px 0px 2.5px #888888; */ +} + +.map-mode-change .btn-unselected div { + transform: skew(30deg); +} +</style> diff --git a/src/components/map/MapToolbox.vue b/src/components/map/MapToolbox.vue index ba324db..cabd44c 100644 --- a/src/components/map/MapToolbox.vue +++ b/src/components/map/MapToolbox.vue @@ -1,6 +1,6 @@ <template> <el-dropdown - class="fy-container dropdown-wrap" + class="p-events-auto dropdown-wrap" trigger="click" size="small" @command="handleCommand" @@ -12,8 +12,16 @@ </el-button> <template #dropdown> <el-dropdown-menu> - <el-dropdown-item v-for="(item, index) in toolItem" :key="index" :command="index"> - <el-button :type="item.value ? 'primary' : 'info'" plain size="default"> + <el-dropdown-item + v-for="(item, index) in toolItem" + :key="index" + :command="index" + > + <el-button + :type="item.value ? 'primary' : 'info'" + plain + size="default" + > <font-awesome-icon :icon="item.icon" class="m-r-4" /> {{ item.label + ': ' + (item.value ? '寮�' : '鍏�') }} </el-button> @@ -100,8 +108,6 @@ top: 10px; left: 2px; } - - .el-button { margin: initial !important; diff --git a/src/components/monitor/FactorCheckbox.vue b/src/components/monitor/FactorCheckbox.vue new file mode 100644 index 0000000..43c1b93 --- /dev/null +++ b/src/components/monitor/FactorCheckbox.vue @@ -0,0 +1,64 @@ +<template> + <BaseCard direction="top-left" borderless="t"> + <template #content> + <el-checkbox-group + v-model="checkbox" + size="default" + @change="handleChange" + > + <el-checkbox + v-for="(item, i) in options" + :key="i" + :value="item.value" + >{{ item.label }}</el-checkbox + > + </el-checkbox-group> + </template> + </BaseCard> +</template> + +<script> +// 鐩戞祴鍥犲瓙鍗曢�夋 +import { checkboxOptions } from '@/constant/checkbox-options'; +import { TYPE0 } from '@/constant/device-type'; + +export default { + props: { + deviceType: { + type: String, + // type0: 杞﹁浇鎴栨棤浜烘満; type1:鏃犱汉鑸� + default: TYPE0 + } + }, + emits: ['change'], + data() { + return { + checkbox: [checkboxOptions(TYPE0)[0].value] + }; + }, + computed: { + options() { + return checkboxOptions(this.deviceType); + } + }, + watch: { + deviceType(nV, oV) { + if (nV != oV) { + this.checkbox = this.options[0].value; + } + } + }, + methods: { + handleChange(value) { + this.$emit('change', value); + } + } +}; +</script> +<style scoped> +.el-checkbox { + --el-checkbox-text-color: white; + margin-right: 6px; + /* height: initial; */ +} +</style> diff --git a/src/components/monitor/FactorLegend.vue b/src/components/monitor/FactorLegend.vue new file mode 100644 index 0000000..23e576f --- /dev/null +++ b/src/components/monitor/FactorLegend.vue @@ -0,0 +1,123 @@ +<template> + <BaseCard> + <template #content> + <el-row justify="space-between" align="middle"> + <el-row align="middle"> + <img src="@/assets/mipmap/data_chart.png" class="ff-img m-r-4" /> + <span>璧拌埅鍥句緥</span> + </el-row> + <span>{{ factor.factorName }}</span> + </el-row> + <div + v-for="(item, index) in legends" + :key="index" + class="flexbox align-items margin-top" + > + <div class="rectangle" :style="'background-color: ' + item.color"></div> + <el-row v-if="item.max"> + <span class="w-40 text-right">{{ item.min }}</span> + <span class="w-20 text-center">~</span> + <span class="w-40 text-right">{{ item.max }}</span> + <span class="w-50 m-l-8">{{ item.unit }}</span> + </el-row> + <el-row v-else> + <span class="w-40 text-right"></span> + <span class="w-20 text-center">></span> + <span class="w-40 text-right">{{ item.min }}</span> + <span class="w-50 m-l-8">{{ item.unit }}</span> + </el-row> + </div> + </template> + </BaseCard> +</template> + +<script> +import { Legend } from '@/model/Legend'; +import { factorUnit } from '../../constant/factor-unit'; +import { Factor } from '@/model/Factor'; + +export default { + props: { + factor: { + type: Factor, + default: () => new Factor() + } + }, + data() { + return {}; + }, + computed: { + legends() { + const res = this.factor + ? this.refreshLegend( + this.factor.factorName, + this.factor.legendType, + this.factor.min, + this.factor.max + ) + : []; + return res; + } + }, + methods: { + /** + * 鑾峰彇鍒嗘瀽鍥句緥 + */ + refreshLegend(name, type, min, max) { + var r = Legend._legend_r[name]; + var c = Legend._legend_c[name]; + // 娌℃湁鎵惧埌鏍囧噯鍥句緥鐨勫洜瀛愶紝榛樿浣跨敤鑷畾涔夎寖鍥村浘渚� + if (r == undefined) { + type = Legend.C_TYPE; + } + var range = []; + if (type != Legend.S_TYPE && min != undefined && max != undefined) { + var count = Legend._custom.length; + var per = (max - min) / count; + for (let i = 0; i < count; i++) { + range.push([(min + per * i).toFixed(1), Legend._custom[i]]); + } + } else { + for (let i = 0; i < r.length; i++) { + range.push([r[i], c[i]]); + } + } + + const legendList = []; + for (let i = 0; i < range.length; i++) { + const r = range[i]; + const nextR = range[i + 1]; + + var color = r[1]; + var bgColor = + 'rgba(' + + color[0] * 255 + + ', ' + + color[1] * 255 + + ', ' + + color[2] * 255 + + ', ' + + color[3] + + ')'; + legendList.push({ + color: bgColor, + min: r[0], + max: nextR ? nextR[0] : undefined, + unit: factorUnit[name] + }); + } + + return legendList; + } + } +}; +</script> +<style scoped> +.text-right { + text-align: right; +} + +.text-center { + text-align: center; +} +</style> diff --git a/src/components/monitor/FactorRadio.vue b/src/components/monitor/FactorRadio.vue index 5282125..afdd4d5 100644 --- a/src/components/monitor/FactorRadio.vue +++ b/src/components/monitor/FactorRadio.vue @@ -1,5 +1,5 @@ <template> - <BaseCard class="map-factor-selector"> + <BaseCard> <template #content> <el-radio-group v-model="radio" size="default" @change="handleChange"> <el-radio v-for="(item, i) in options" :key="i" :value="item.value">{{ @@ -41,7 +41,7 @@ } } }, - method: { + methods: { handleChange(value) { this.$emit('change', value); // todo 鍦板浘3d鍥惧儚鍒囨崲灞曠ず鐩戞祴鍥犲瓙 diff --git a/src/components/monitor/LineChart.vue b/src/components/monitor/LineChart.vue new file mode 100644 index 0000000..ccaa3e2 --- /dev/null +++ b/src/components/monitor/LineChart.vue @@ -0,0 +1,115 @@ +<template> + <BaseCard size="medium"> + <template #content> + <div ref="lineChart" class="line-chart"></div> + </template> + <template #footer> + <!-- 鍗曢〉鏁版嵁閲�--> + <SliderBar></SliderBar> + </template> + </BaseCard> +</template> + +<script> +import * as echarts from 'echarts'; +import { FactorDatas } from '@/model/FactorDatas'; +import { factorName } from '@/constant/factor-name'; +import { factorLineOption } from '@/utils/chart/chart-option'; + +export default { + props: { + factorDatas: { + type: FactorDatas + // default: () => new FactorDatas() + }, + selectFactorType: { + type: Array, + default: () => ['1'] + } + }, + data() { + return { + lineChart: null, + option: null + }; + }, + watch: { + factorDatas: { + handler() { + this.refreshChart(); + }, + deep: true + }, + selectFactorType: { + handler() { + this.refreshChart(); + }, + deep: true + } + }, + computed: { + /** + * 鑾峰彇妯潗鏍� + */ + xAxis() { + return this.factorDatas.times.map((v) => { + return v.split(' ')[1]; + }); + }, + /** + * 鑾峰彇鐩戞祴鏁版嵁绾靛潗鏍� + */ + allSeries() { + const res = []; + for (const key in this.factorDatas.factor) { + if (Object.hasOwnProperty.call(this.factorDatas.factor, key)) { + const e = this.factorDatas.factor[key]; + res.push({ + key: key, + name: factorName[e.factorName], + type: 'line', + data: e.datas.map((v) => v.factorData), + showAllSymbol: true, + animationDelay: function (idx) { + return idx * 10; + } + }); + } + } + return res; + }, + showSeries() { + return this.allSeries.filter((s) => { + return this.selectFactorType.includes(s.key); + }); + }, + legends() { + return this.showSeries.map((s) => { + return s.name; + }); + } + }, + methods: { + initChart() { + this.lineChart = echarts.init(this.$refs.lineChart); + }, + refreshChart() { + const option = factorLineOption( + this.xAxis, + this.showSeries, + this.legends + ); + this.lineChart.setOption(option, { notMerge: true }); + } + }, + mounted() { + this.initChart(); + } +}; +</script> +<style scoped> +.line-chart { + /* width: 200px; */ + height: 280px; +} +</style> diff --git a/src/components/search/SearchBar.vue b/src/components/search/SearchBar.vue index 003c1a0..68ee8ed 100644 --- a/src/components/search/SearchBar.vue +++ b/src/components/search/SearchBar.vue @@ -1,5 +1,5 @@ <template> - <BaseCard class="map-date-selector flexbox-col align-items"> + <BaseCard class=""> <template #content> <el-form :inline="true"> <OptionMission v-model="formSearch.missionCode"></OptionMission> @@ -41,7 +41,7 @@ } } }, - method: { + methods: { handleClick() { this.$emit('search', this.formSearch); } @@ -49,6 +49,17 @@ }; </script> <style lang="scss"> -.fy-container { +.map-date-selector { + display: inline-block; + position: relative; + /* left: 0; + right: 0; + top: 0px; */ + /* padding: 0 4px; */ + /* color: ffffffbd; */ + /* background-color: antiquewhite; */ +} + +.p-events-auto { } </style> diff --git a/src/constant/checkbox-options.js b/src/constant/checkbox-options.js new file mode 100644 index 0000000..86b1a78 --- /dev/null +++ b/src/constant/checkbox-options.js @@ -0,0 +1,186 @@ +import { TYPE0, TYPE1, TYPE2, TYPE4 } from '@/constant/device-type'; + +// 鐩戞祴鍥犲瓙鍗曢�夋閫夐」 +function checkboxOptions(deviceType) { + switch (deviceType) { + case TYPE0: + return option1; + case TYPE1: + return option3; + case TYPE2: + return option2; + case TYPE4: + return option4; + default: + return option1; + } +} + +const option1 = [ + { + label: 'NO2', + name: 'NO2', + value: '1' + }, + { + label: 'CO', + name: 'CO', + value: '2' + }, + { + label: 'H2S', + name: 'H2S', + value: '3' + }, + { + label: 'SO2', + name: 'SO2', + value: '4' + }, + { + label: 'O3', + name: 'NO2', + value: '5' + }, + { + label: 'PM2.5', + name: 'PM25', + value: '6' + }, + { + label: 'PM10', + name: 'PM10', + value: '7' + }, + { + label: 'TVOC', + name: 'VOC', + value: '10' + }, + + // { + // label: "NOI", + // name: "NOI", + // value: "11" + // }, + { + label: '杞﹂��', + name: 'VELOCITY', + value: '14' + }, + { + label: '椋庨��', + name: 'WIND_SPEED', + value: '16' + }, + { + label: '椋庡悜', + name: 'WIND_DIRECTION', + value: '17' + }, + { + label: '楂樺害', + name: 'HEIGHT', + value: '18' + } +]; + +const option2 = [ + { + label: '娓╁害', + name: '娓╁害', + value: '1' + }, + { + label: '鐢靛鐜�', + name: '鐢靛鐜�', + value: '2' + }, + { + label: '娴婂害', + name: '娴婂害', + value: '3' + }, + { + label: '婧惰В姘�', + name: '婧惰В姘�', + value: '4' + }, + { + label: 'PH', + name: 'PH', + value: '5' + } +]; + +const option3 = [ + { + label: 'NO2', + name: 'NO2', + value: '1' + }, + { + label: 'CO', + name: 'CO', + value: '2' + }, + { + label: 'H2S', + name: 'H2S', + value: '3' + }, + { + label: 'SO2', + name: 'SO2', + value: '4' + }, + { + label: 'O3', + name: 'NO2', + value: '5' + }, + { + label: 'PM2.5', + name: 'PM25', + value: '6' + }, + { + label: 'PM10', + name: 'PM10', + value: '7' + }, + { + label: 'TVOC', + name: 'VOC', + value: '10' + }, + { + label: '椋庨��', + name: 'WIND_SPEED', + value: '16' + }, + { + label: '椋庡悜', + name: 'WIND_DIRECTION', + value: '17' + } +]; + +const option4 = [ + { + label: 'A鐩哥數娴�', + name: 'EA', + value: '1' + }, + { + label: 'B鐩哥數娴�', + name: 'EB', + value: '2' + }, + { + label: 'C鐩哥數娴�', + name: 'EC', + value: '3' + } +]; +export { checkboxOptions }; diff --git a/src/constant/factor-name.js b/src/constant/factor-name.js index d8b551a..360e5f0 100644 --- a/src/constant/factor-name.js +++ b/src/constant/factor-name.js @@ -12,7 +12,7 @@ TEMPERATURE: '娓╁害', HUMIDITY: '婀垮害', VOC: 'TVOC', //5 - // 'NOI': 'NOI', //9 + NOI: 'NOI', //9 LNG: '缁忓害', LAT: '绾害', VELOCITY: '杞﹂��', diff --git a/src/model/Legend.js b/src/model/Legend.js index 054ad4b..304934e 100644 --- a/src/model/Legend.js +++ b/src/model/Legend.js @@ -215,79 +215,6 @@ i = this._custom.length - 1; } return this._custom[i]; - }, - - /** - * 鑾峰彇鍒嗘瀽鍥句緥 - */ - refreshLegend: function (eId, name, animation, type, min, max) { - var legend = $('#' + eId); - legend.empty(); - - var r = this._legend_r[name]; - var c = this._legend_c[name]; - // 娌℃湁鎵惧埌鏍囧噯鍥句緥鐨勫洜瀛愶紝榛樿浣跨敤鑷畾涔夎寖鍥村浘渚� - if (r == undefined) { - type = this.C_TYPE; - } - var range = []; - if (type != this.S_TYPE && min != undefined && max != undefined) { - var count = this._custom.length; - var per = (max - min) / count; - for (let i = 0; i < count; i++) { - range.push([(min + per * i).toFixed(1), this._custom[i]]); - } - } else { - for (let i = 0; i < r.length; i++) { - range.push([r[i], c[i]]); - } - } - - for (let i = 0; i < range.length; i++) { - const r = range[i]; - const nextR = range[i + 1]; - var div1 = $('<div></div>'); - div1.addClass('flexbox align-items margin-top'); - var div2 = $('<div></div>'); - div2.addClass('rectangle'); - - var color = r[1]; - var bgcolor = - 'rgba(' + - color[0] * 255 + - ', ' + - color[1] * 255 + - ', ' + - color[2] * 255 + - ', ' + - color[3] + - ')'; - div2.css('background-color', bgcolor); - var div3 = $('<div></div>'); - var d; - if (nextR != undefined) { - d = r[0] + ' ~' + nextR[0] + ' ' + Util.factorUnit2[name]; - } else { - d = - ' > ' + - r[0] + - ' ' + - Util.factorUnit2[name]; - } - div3.append(d); - div1.append(div2); - div1.append(div3); - legend.append(div1); - } - - if (animation == false) { - return; - } - legend.hide('fast', function () { - setTimeout(() => { - legend.show('fast'); - }, 500); - }); } }; diff --git a/src/styles/base.scss b/src/styles/base.scss index d993f48..8e29e01 100644 --- a/src/styles/base.scss +++ b/src/styles/base.scss @@ -38,7 +38,7 @@ r: 'right', b: 'bottom' ); -$size: (4, 8, 10, 16); +$size: (2, 4, 8, 10, 16); @each $dName, $dValue in $direction { @each $i in $size { .p-#{$dName}-#{$i} { @@ -71,7 +71,7 @@ default: var(--el-component-size-default), large: var(--el-component-size-large) ); -$ws: (20, 40, 60, 80, 100, 120, 150, 300); +$ws: (20, 40, 50, 60, 80, 100, 120, 150, 300); @each $name, $value in $csize { .w-#{$name} { width: #{$value}; diff --git a/src/utils/chart/chart-option.js b/src/utils/chart/chart-option.js new file mode 100644 index 0000000..bd9de70 --- /dev/null +++ b/src/utils/chart/chart-option.js @@ -0,0 +1,100 @@ +/** + * 鑾峰彇鍚堥�傜殑瀛椾綋澶у皬 + */ +function fGetChartFontSize() { + const dpr = window.devicePixelRatio; + let fontSize = 12; + if (dpr == 2) { + fontSize = 17; + } else if (dpr == 3) { + fontSize = 24; + } else if (dpr > 3) { + fontSize = 24; + } + return fontSize; +} + +function factorLineOption(_xAxis, _series, legends) { + var fontSize = fGetChartFontSize(); + return { + animationEasing: 'elasticOut', + animationDelayUpdate: function (idx) { + return idx * 5; + }, + // toolbox: { + // bottom: 0, + // feature: { + // dataZoom: {}, + // magicType: { + // type: ['line', 'bar'] + // }, + // restore: {} + // } + // }, + tooltip: { + textStyle: { + fontSize: fontSize + } + }, + legend: { + type: 'scroll', + data: legends, + left: 0, + textStyle: { + fontSize: fontSize, + color: 'white' + } + }, + xAxis: { + name: '鏃堕棿', + data: _xAxis, + axisLabel: { + textStyle: { + fontSize: fontSize + }, + color: '#ffffff', + textBorderColor: '#fff' + }, + axisTick: { + lineStyle: { + color: 'white' + }, + intervel: 0, + inside: false + }, + + nameTextStyle: { + color: '#ffffff' + }, + axisLine: { + lineStyle: { + color: '#ffffff' + } + } + }, + yAxis: { + name: '娴撳害(渭g/m鲁)', + axisLabel: { + textStyle: { + fontSize: fontSize + } + }, + axisLine: { + show: true, + lineStyle: { + color: 'white' + } + }, + axisTick: { + show: true, + lineStyle: { + color: 'white' + } + }, + minInterval: 1 + }, + series: _series + }; +} + +export { factorLineOption }; diff --git a/src/views/historymode/HistoryMode.vue b/src/views/historymode/HistoryMode.vue index 50dc1f0..179e2cd 100644 --- a/src/views/historymode/HistoryMode.vue +++ b/src/views/historymode/HistoryMode.vue @@ -1,12 +1,21 @@ <template> - <div class="fy-container"> + <div class="p-events-none m-t-2"> <el-row justify="center"> <SearchBar search-time="" @search="fetchHistroyData"></SearchBar> </el-row> - <FactorRadio - :device-type="deviceType" - @change="(e) => (factorType = e)" - ></FactorRadio> + <el-row class="m-t-2"> + <FactorRadio + :device-type="deviceType" + @change="(e) => (factorType = e)" + ></FactorRadio> + </el-row> + <el-row class="m-t-2"> + <FactorLegend + class="m-t-2" + :factor="factorDatas.factor[factorType]" + ></FactorLegend> + </el-row> + <TrendAnalysis :factor-datas="factorDatas"></TrendAnalysis> </div> </template> @@ -18,8 +27,10 @@ import moment from 'moment'; import { TYPE0 } from '@/constant/device-type'; import { FactorDatas } from '@/model/FactorDatas'; +import TrendAnalysis from './component/TrendAnalysis.vue'; export default { + components: { TrendAnalysis }, setup() { const { loading, fetchData } = useFetchData(10000); return { loading, fetchData }; @@ -60,7 +71,7 @@ // this.factorMode = factorMode; // this.factorType = factorType; // this.factorName = factorName; - // this.factorDatas.refreshHeight(this.factorType + 1 + ''); + this.factorDatas.refreshHeight(this.factorType); // this.refreshLegend(this.factorDatas); // this.mapMaker.setFactorType(factorType); // if (!this.mapMaker.runStatus()) { @@ -108,7 +119,7 @@ }, fetchRealTimeData() { // fixme 2024.5.3 姝ゅ鍒濆鑾峰彇鐨勬暟鎹紝鍙傛暟搴旇鐢眘earchbar鍐冲畾锛屽悗缁慨鏀� - this.fetchData((page, pageSize) => { + this.fetchData((page) => { return monitorDataApi .fetchHistroyData({ deviceCode: '0a0000000001', @@ -133,6 +144,8 @@ }; </script> <style scoped> -.fy-container { +.p-events-auto { + /* background-color: antiquewhite; */ + /* padding-top: 1px; */ } </style> diff --git a/src/views/historymode/component/TrendAnalysis.vue b/src/views/historymode/component/TrendAnalysis.vue new file mode 100644 index 0000000..d5de2d0 --- /dev/null +++ b/src/views/historymode/component/TrendAnalysis.vue @@ -0,0 +1,41 @@ +<template> + <el-row class="wrap"> + <el-col span="10"> + <FactorCheckbox + :device-type="deviceType" + @change="(e) => (selectFactorType = e)" + ></FactorCheckbox> + <LineChart + :factor-datas="factorDatas" + :select-factor-type="selectFactorType" + ></LineChart> + </el-col> + </el-row> +</template> + +<script> +/** + * 鐩戞祴瑕佺礌瓒嬪娍鍒嗘瀽 + */ +import { FactorDatas } from '@/model/FactorDatas'; + +export default { + props: { + deviceType: { + type: String + }, + factorDatas: FactorDatas + }, + data() { + return { + selectFactorType: ['1'] + }; + } +}; +</script> +<style scoped> +.wrap { + /* display: flex; + flex-direction: column; */ +} +</style> -- Gitblit v1.9.3