| | |
| | | CompQuickSet: typeof import('./src/components/search-option/CompQuickSet.vue')['default'] |
| | | DeviceStatus: typeof import('./src/components/monitor/DeviceStatus.vue')['default'] |
| | | DistrictRanking: typeof import('./src/components/monitor/DistrictRanking.vue')['default'] |
| | | ElAffix: typeof import('element-plus/es')['ElAffix'] |
| | | ElAside: typeof import('element-plus/es')['ElAside'] |
| | | ElBadge: typeof import('element-plus/es')['ElBadge'] |
| | | ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb'] |
| | | ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] |
| | | ElButton: typeof import('element-plus/es')['ElButton'] |
| | | ElCalendar: typeof import('element-plus/es')['ElCalendar'] |
| | | ElCard: typeof import('element-plus/es')['ElCard'] |
| | | ElCascader: typeof import('element-plus/es')['ElCascader'] |
| | | ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] |
| | |
| | | ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] |
| | | ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] |
| | | ElDialog: typeof import('element-plus/es')['ElDialog'] |
| | | ElDivider: typeof import('element-plus/es')['ElDivider'] |
| | | ElDrawer: typeof import('element-plus/es')['ElDrawer'] |
| | | ElEmpty: typeof import('element-plus/es')['ElEmpty'] |
| | | ElForm: typeof import('element-plus/es')['ElForm'] |
| | | ElFormItem: typeof import('element-plus/es')['ElFormItem'] |
| | | ElFrom: typeof import('element-plus/es')['ElFrom'] |
| | | ElHeader: typeof import('element-plus/es')['ElHeader'] |
| | | ElIcon: typeof import('element-plus/es')['ElIcon'] |
| | | ElImage: typeof import('element-plus/es')['ElImage'] |
| | | ElImageViewer: typeof import('element-plus/es')['ElImageViewer'] |
| | | ElInput: typeof import('element-plus/es')['ElInput'] |
| | | ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] |
| | | ElLink: typeof import('element-plus/es')['ElLink'] |
| | | ElMain: typeof import('element-plus/es')['ElMain'] |
| | | ElMenu: typeof import('element-plus/es')['ElMenu'] |
| | |
| | | ElOption: typeof import('element-plus/es')['ElOption'] |
| | | ElPagination: typeof import('element-plus/es')['ElPagination'] |
| | | ElPopover: typeof import('element-plus/es')['ElPopover'] |
| | | ElProgress: typeof import('element-plus/es')['ElProgress'] |
| | | ElRadio: typeof import('element-plus/es')['ElRadio'] |
| | | ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] |
| | | ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] |
| | | ElRow: typeof import('element-plus/es')['ElRow'] |
| | | ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] |
| | | ElSelect: typeof import('element-plus/es')['ElSelect'] |
| | | ElSpace: typeof import('element-plus/es')['ElSpace'] |
| | | ElStatistic: typeof import('element-plus/es')['ElStatistic'] |
| | | ElStep: typeof import('element-plus/es')['ElStep'] |
| | | ElSteps: typeof import('element-plus/es')['ElSteps'] |
| | | ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] |
| | | ElSwitch: typeof import('element-plus/es')['ElSwitch'] |
| | | ElTable: typeof import('element-plus/es')['ElTable'] |
| | |
| | | ElTag: typeof import('element-plus/es')['ElTag'] |
| | | ElText: typeof import('element-plus/es')['ElText'] |
| | | ElTooltip: typeof import('element-plus/es')['ElTooltip'] |
| | | ElTree: typeof import('element-plus/es')['ElTree'] |
| | | ElUpload: typeof import('element-plus/es')['ElUpload'] |
| | | FormCol: typeof import('./src/components/layout/FormCol.vue')['default'] |
| | | FYBgTaskCard: typeof import('./src/components/bg-task/FYBgTaskCard.vue')['default'] |
| | | FYBgTaskDialog: typeof import('./src/components/bg-task/FYBgTaskDialog.vue')['default'] |
| | |
| | | FYReconfrimButton: typeof import('./src/components/button/FYReconfrimButton.vue')['default'] |
| | | FYSearchBar: typeof import('./src/components/search-option/FYSearchBar.vue')['default'] |
| | | FYTable: typeof import('./src/components/table/FYTable.vue')['default'] |
| | | IEpArrowLeft: typeof import('~icons/ep/arrow-left')['default'] |
| | | IEpArrowRight: typeof import('~icons/ep/arrow-right')['default'] |
| | | IEpDownload: typeof import('~icons/ep/download')['default'] |
| | | IEpInfoFilled: typeof import('~icons/ep/info-filled')['default'] |
| | | ItemDevice: typeof import('./src/components/list-item/ItemDevice.vue')['default'] |
| | |
| | | const CompQuickSet: typeof import('./src/components/search-option/CompQuickSet.vue')['default'] |
| | | const DeviceStatus: typeof import('./src/components/monitor/DeviceStatus.vue')['default'] |
| | | const DistrictRanking: typeof import('./src/components/monitor/DistrictRanking.vue')['default'] |
| | | const ElAffix: typeof import('element-plus/es')['ElAffix'] |
| | | const ElAside: typeof import('element-plus/es')['ElAside'] |
| | | const ElBadge: typeof import('element-plus/es')['ElBadge'] |
| | | const ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb'] |
| | | const ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] |
| | | const ElButton: typeof import('element-plus/es')['ElButton'] |
| | | const ElCalendar: typeof import('element-plus/es')['ElCalendar'] |
| | | const ElCard: typeof import('element-plus/es')['ElCard'] |
| | | const ElCascader: typeof import('element-plus/es')['ElCascader'] |
| | | const ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] |
| | |
| | | const ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] |
| | | const ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] |
| | | const ElDialog: typeof import('element-plus/es')['ElDialog'] |
| | | const ElDivider: typeof import('element-plus/es')['ElDivider'] |
| | | const ElDrawer: typeof import('element-plus/es')['ElDrawer'] |
| | | const ElEmpty: typeof import('element-plus/es')['ElEmpty'] |
| | | const ElForm: typeof import('element-plus/es')['ElForm'] |
| | | const ElFormItem: typeof import('element-plus/es')['ElFormItem'] |
| | | const ElFrom: typeof import('element-plus/es')['ElFrom'] |
| | | const ElHeader: typeof import('element-plus/es')['ElHeader'] |
| | | const ElIcon: typeof import('element-plus/es')['ElIcon'] |
| | | const ElImage: typeof import('element-plus/es')['ElImage'] |
| | | const ElImageViewer: typeof import('element-plus/es')['ElImageViewer'] |
| | | const ElInput: typeof import('element-plus/es')['ElInput'] |
| | | const ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] |
| | | const ElLink: typeof import('element-plus/es')['ElLink'] |
| | | const ElMain: typeof import('element-plus/es')['ElMain'] |
| | | const ElMenu: typeof import('element-plus/es')['ElMenu'] |
| | |
| | | const ElOption: typeof import('element-plus/es')['ElOption'] |
| | | const ElPagination: typeof import('element-plus/es')['ElPagination'] |
| | | const ElPopover: typeof import('element-plus/es')['ElPopover'] |
| | | const ElProgress: typeof import('element-plus/es')['ElProgress'] |
| | | const ElRadio: typeof import('element-plus/es')['ElRadio'] |
| | | const ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] |
| | | const ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] |
| | | const ElRow: typeof import('element-plus/es')['ElRow'] |
| | | const ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] |
| | | const ElSelect: typeof import('element-plus/es')['ElSelect'] |
| | | const ElSpace: typeof import('element-plus/es')['ElSpace'] |
| | | const ElStatistic: typeof import('element-plus/es')['ElStatistic'] |
| | | const ElStep: typeof import('element-plus/es')['ElStep'] |
| | | const ElSteps: typeof import('element-plus/es')['ElSteps'] |
| | | const ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] |
| | | const ElSwitch: typeof import('element-plus/es')['ElSwitch'] |
| | | const ElTable: typeof import('element-plus/es')['ElTable'] |
| | |
| | | const ElTag: typeof import('element-plus/es')['ElTag'] |
| | | const ElText: typeof import('element-plus/es')['ElText'] |
| | | const ElTooltip: typeof import('element-plus/es')['ElTooltip'] |
| | | const ElTree: typeof import('element-plus/es')['ElTree'] |
| | | const ElUpload: typeof import('element-plus/es')['ElUpload'] |
| | | const FormCol: typeof import('./src/components/layout/FormCol.vue')['default'] |
| | | const FYBgTaskCard: typeof import('./src/components/bg-task/FYBgTaskCard.vue')['default'] |
| | | const FYBgTaskDialog: typeof import('./src/components/bg-task/FYBgTaskDialog.vue')['default'] |
| | |
| | | const FYReconfrimButton: typeof import('./src/components/button/FYReconfrimButton.vue')['default'] |
| | | const FYSearchBar: typeof import('./src/components/search-option/FYSearchBar.vue')['default'] |
| | | const FYTable: typeof import('./src/components/table/FYTable.vue')['default'] |
| | | const IEpArrowLeft: typeof import('~icons/ep/arrow-left')['default'] |
| | | const IEpArrowRight: typeof import('~icons/ep/arrow-right')['default'] |
| | | const IEpDownload: typeof import('~icons/ep/download')['default'] |
| | | const IEpInfoFilled: typeof import('~icons/ep/info-filled')['default'] |
| | | const ItemDevice: typeof import('./src/components/list-item/ItemDevice.vue')['default'] |
| | |
| | | </template> |
| | | |
| | | <script> |
| | | import dayjs from 'dayjs'; |
| | | import dayjs from 'dayjs' |
| | | |
| | | const MONTH = 'month'; |
| | | const DATE = 'date'; |
| | | const RANGE = 'datetimerange'; |
| | | const RANGE2 = 'daterange'; |
| | | const MONTH = 'month' |
| | | const DATE = 'date' |
| | | const RANGE = 'datetimerange' |
| | | const RANGE2 = 'daterange' |
| | | |
| | | export default { |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: MONTH |
| | | default: MONTH, |
| | | }, |
| | | // è¿åç»æ |
| | | value: Date || Array, |
| | | // æ¯å¦é»è®¤è¿ååå§é项 |
| | | initValue: { |
| | | type: Boolean, |
| | | default: true |
| | | default: true, |
| | | }, |
| | | label: { |
| | | type: String, |
| | | default: 'æ¶é´' |
| | | default: 'æ¶é´', |
| | | }, |
| | | prop: { |
| | | type: String, |
| | | default: 'time' |
| | | } |
| | | default: 'time', |
| | | }, |
| | | }, |
| | | emits: ['update:value', 'change'], |
| | | data() { |
| | | return { |
| | | date: this.value |
| | | }; |
| | | date: this.value, |
| | | } |
| | | }, |
| | | watch: { |
| | | value: { |
| | | handler(newVal, oldVal) { |
| | | if (newVal != oldVal) { |
| | | this.date = newVal |
| | | } |
| | | }, |
| | | immediate: true, |
| | | }, |
| | | }, |
| | | computed: {}, |
| | | methods: { |
| | | handleChange(value) { |
| | | this.$emit('update:value', value); |
| | | this.$emit('change', value); |
| | | } |
| | | this.$emit('update:value', value) |
| | | this.$emit('change', value) |
| | | }, |
| | | }, |
| | | mounted() { |
| | | if (this.initValue) { |
| | | switch (this.type) { |
| | | case RANGE: |
| | | case RANGE2: |
| | | this.date = [dayjs().startOf('month').toDate(), dayjs().toDate()]; |
| | | break; |
| | | this.date = [dayjs().startOf('month').toDate(), dayjs().toDate()] |
| | | break |
| | | case MONTH: |
| | | this.date = dayjs().startOf('month').toDate(); |
| | | break; |
| | | this.date = dayjs().startOf('month').toDate() |
| | | break |
| | | case DATE: |
| | | this.date = dayjs().toDate(); |
| | | break; |
| | | this.date = dayjs().toDate() |
| | | break |
| | | default: |
| | | break; |
| | | break |
| | | } |
| | | this.handleChange(this.date); |
| | | this.handleChange(this.date) |
| | | } |
| | | }, |
| | | } |
| | | }; |
| | | </script> |
| | |
| | | :placeholder="label" |
| | | style="width: 260px" |
| | | > |
| | | <el-option |
| | | v-for="s in filtedBeforeTask" |
| | | :key="s.value" |
| | | :label="s.label" |
| | | :value="s.value" |
| | | /> |
| | | <el-option v-for="s in filtedBeforeTask" :key="s.value" :label="s.label" :value="s.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </template> |
| | | |
| | | <script> |
| | | import taskApi from '@/api/fysp/taskApi'; |
| | | import taskApi from '@/api/fysp/taskApi' |
| | | import dayjs from 'dayjs' |
| | | |
| | | export default { |
| | | props: { |
| | | label: { |
| | | type: String, |
| | | default: 'æ»ä»»å¡' |
| | | default: 'æ»ä»»å¡', |
| | | }, |
| | | // è¿åç»æ |
| | | value: Object, |
| | | // æ¯å¦é»è®¤è¿ååå§é项 |
| | | initValue: { |
| | | type: Boolean, |
| | | default: true |
| | | default: true, |
| | | }, |
| | | // form表åç»å®å±æ§å |
| | | prop: { |
| | | type: String, |
| | | default: 'topTaskId' |
| | | default: 'topTaskId', |
| | | }, |
| | | // é项ç鿡件ï¼çéæä»»å¡ä¹åçç¸åè¡æ¿åºåå
çä»»å¡ |
| | | beforeTask: { |
| | | type: Object, |
| | | default: () => { |
| | | return {}; |
| | | } |
| | | } |
| | | return {} |
| | | }, |
| | | }, |
| | | }, |
| | | emits: ['update:value'], |
| | | data() { |
| | | return { |
| | | selected: {}, |
| | | topTasks: [] |
| | | }; |
| | | topTasks: [], |
| | | } |
| | | }, |
| | | computed: { |
| | | // éæ©æ¡ä¸ä½¿ç¨é¡¶å±ä»»å¡idä½ä¸ºéé¡¹å¼ |
| | | formatedValue() { |
| | | return this.value?.tguid; |
| | | return this.value?.tguid |
| | | }, |
| | | // æä»»å¡ä¹åçç¸åè¡æ¿åºåå
çä»»å¡ |
| | | filtedBeforeTask() { |
| | | const filteredTasks = this.topTasks.filter((t) => { |
| | | return ( |
| | | (!this.beforeTask.provincecode || |
| | | this.beforeTask.provincecode == t.data.provincecode) && |
| | | (!this.beforeTask.citycode || |
| | | this.beforeTask.citycode == t.data.citycode) && |
| | | (!this.beforeTask.districtcode || |
| | | this.beforeTask.districtcode == t.data.districtcode) && |
| | | (!this.beforeTask.starttime || |
| | | t.data.starttime < this.beforeTask.starttime) |
| | | ); |
| | | }); |
| | | (!this.beforeTask.provincecode || this.beforeTask.provincecode == t.data.provincecode) && |
| | | (!this.beforeTask.citycode || this.beforeTask.citycode == t.data.citycode) && |
| | | (!this.beforeTask.districtcode || this.beforeTask.districtcode == t.data.districtcode) && |
| | | (!this.beforeTask.starttime || t.data.starttime < this.beforeTask.starttime) |
| | | ) |
| | | }) |
| | | if (filteredTasks.length > 0) { |
| | | this.handleChange(filteredTasks[0]?.value); |
| | | this.handleChange(filteredTasks[0]?.value) |
| | | } |
| | | return filteredTasks; |
| | | } |
| | | return filteredTasks |
| | | }, |
| | | }, |
| | | methods: { |
| | | //è·åæ¥è¯¢æ¡ä»¶ |
| | |
| | | return { |
| | | value: r.tguid, |
| | | label: r.name, |
| | | data: r |
| | | }; |
| | | }); |
| | | this.topTasks = list; |
| | | if (this.initValue) { |
| | | this.handleChange(list[0].value); |
| | | data: r, |
| | | } |
| | | }); |
| | | }) |
| | | this.topTasks = list.filter((e) => { |
| | | return ( |
| | | e.data.districtname == '徿±åº' && dayjs(e.data.starttime).isBefore(dayjs('2023-12-31')) |
| | | ) |
| | | }) |
| | | if (this.initValue) { |
| | | this.handleChange(list[0].value) |
| | | } |
| | | }) |
| | | }, |
| | | //æ¥è¯¢åä»»å¡ç»è®¡ä¿¡æ¯ |
| | | handleChange(value) { |
| | | const task = this.topTasks.find((t) => t.data.tguid == value); |
| | | const param = task ? task.data : {}; |
| | | const task = this.topTasks.find((t) => t.data.tguid == value) |
| | | const param = task ? task.data : {} |
| | | |
| | | this.$emit('update:value', param); |
| | | } |
| | | this.$emit('update:value', param) |
| | | }, |
| | | }, |
| | | mounted() { |
| | | this.getOptions(); |
| | | this.getOptions() |
| | | }, |
| | | } |
| | | }; |
| | | </script> |
| | |
| | | icon: 'solar:people-nearby-line-duotone', |
| | | name: 'ç管巡æ¥', |
| | | children: [ |
| | | { |
| | | path: '/index/inspection/monitor-control', |
| | | icon: 'solar:eye-scan-line-duotone', |
| | | name: 'çç®¡çæ§', |
| | | }, |
| | | // { |
| | | // path: '/index/inspection/monitor-control', |
| | | // icon: 'solar:eye-scan-line-duotone', |
| | | // name: 'çç®¡çæ§', |
| | | // }, |
| | | { |
| | | path: '/index/inspection/task-manage', |
| | | icon: 'solar:file-text-line-duotone', |
| | | name: 'ä»»å¡ç®¡ç', |
| | | name: 'å·¡æ¥ä»»å¡', |
| | | }, |
| | | |
| | | { |
| | | path: '/index/inspection/pro-check', |
| | | icon: 'solar:check-square-line-duotone', |
| | | name: 'é®é¢å®¡æ ¸', |
| | | }, |
| | | { |
| | | path: '/index/analysis/data-product', |
| | | icon: 'solar:checklist-minimalistic-line-duotone', |
| | | name: 'é®é¢æ´æ¹', |
| | | name: 'æ´æ¹æ¸
å', |
| | | }, |
| | | { |
| | | path: '/index/inspection/punishment-manage', |
| | | icon: 'solar:hand-shake-line-duotone', |
| | | name: 'è¡æ¿å¤ç½', |
| | | }, |
| | | { |
| | | path: '/index/inspection/complaint-manage', |
| | | icon: 'solar:call-chat-line-duotone', |
| | | name: '信访æè¯', |
| | | }, |
| | | ], |
| | | }, |
| | |
| | | path: 'monitor-control', |
| | | component: () => import('@/views/inspection/MonitorControl.vue'), |
| | | }, |
| | | { |
| | | name: 'punishment-manage', |
| | | path: 'punishment-manage', |
| | | component: () => import('@/views/inspection/PunishmentManage.vue'), |
| | | }, |
| | | { |
| | | name: 'complaint-manage', |
| | | path: 'complaint-manage', |
| | | component: () => import('@/views/inspection/ComplaintManage.vue'), |
| | | }, |
| | | ], |
| | | }, |
| | | { |
| | |
| | | { |
| | | name: 'data-product', |
| | | path: 'data-product', |
| | | component: () => import('@/views/analysis/DataProduct.vue'), |
| | | component: () => import('@/views/analysis/data-product/DataProduct.vue'), |
| | | }, |
| | | ], |
| | | }, |
| | |
| | | </script> |
| | | |
| | | <template> |
| | | <el-tag type="info"> |
| | | <el-link @click="requestExceptionData" class="text"><slot /></el-link> |
| | | </el-tag> |
| | | </template> |
| | | |
| | | <style lang="scss" scoped> |
| | |
| | | --> |
| | | <script> |
| | | import dayjs from 'dayjs' |
| | | // æ¶é´èå´å¿«æ·é项 |
| | | const dayStart = dayjs('2023-08-01').startOf('date') |
| | | const dayEnd = dayStart.endOf('date') |
| | | const shortcuts = [ |
| | | { |
| | | text: 'ä»å¤©', |
| | | value: [dayStart.toDate(), dayEnd.toDate()], |
| | | }, |
| | | { |
| | | text: 'æ¬å¨', |
| | | value: [dayStart.startOf('week').toDate(), dayEnd.endOf('week').toDate()], |
| | | }, |
| | | { |
| | | text: 'ä¸å¨', |
| | | value: [dayStart.day(-7).toDate(), dayEnd.day(-1).toDate()], |
| | | }, |
| | | { |
| | | text: 'æ¬æ', |
| | | value: [dayStart.startOf('month').toDate(), dayEnd.endOf('month').toDate()], |
| | | }, |
| | | { |
| | | text: '䏿', |
| | | value: [ |
| | | dayStart.subtract(1, 'month').startOf('month').toDate(), |
| | | dayEnd.subtract(1, 'month').endOf('month').toDate(), |
| | | ], |
| | | }, |
| | | { |
| | | text: 'æ¬å£åº¦', |
| | | value: [dayStart.startOf('quarter').toDate(), dayEnd.endOf('quarter').toDate()], |
| | | }, |
| | | { |
| | | text: 'ä¸å£åº¦', |
| | | value: [ |
| | | dayStart.subtract(1, 'quarter').startOf('quarter').toDate(), |
| | | dayEnd.subtract(1, 'quarter').endOf('quarter').toDate(), |
| | | ], |
| | | }, |
| | | { |
| | | text: 'å»å¹´', |
| | | value: [ |
| | | dayStart.subtract(1, 'year').startOf('year').toDate(), |
| | | dayEnd.subtract(1, 'year').endOf('year').toDate(), |
| | | ], |
| | | }, |
| | | { |
| | | text: 'ä»å¹´', |
| | | value: [dayStart.startOf('year').toDate(), dayEnd.endOf('year').toDate()], |
| | | }, |
| | | ] |
| | | |
| | | export default { |
| | | emits: ['submitTime'], |
| | | props: { |
| | |
| | | ], |
| | | // éä¸çæ¶é´èå´ |
| | | selectedRange: '', |
| | | // æ¶é´èå´å¿«æ·é项 |
| | | shortcuts, |
| | | } |
| | | }, |
| | | |
| | |
| | | <div v-show="showTimePicker" class="time-picker-container"> |
| | | <el-date-picker |
| | | v-model="time" |
| | | :shortcuts="shortcuts" |
| | | type="daterange" |
| | | range-separator="~" |
| | | start-placeholder="å¼å§æ¶é´" |
| | |
| | | <span class="demonstration">æ¶é´ï¼</span> |
| | | <el-date-picker |
| | | v-model="time" |
| | | :shortcuts="shortcuts" |
| | | type="daterange" |
| | | range-separator="~" |
| | | start-placeholder="Start date" |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <BaseProdProcess |
| | | v-model:active="active" |
| | | @onStep1="onStep1" |
| | | @onStep2="onStep2" |
| | | @onStep3="onStep3" |
| | | :loading="loading" |
| | | > |
| | | <template #step1="{ onSearch }"> |
| | | <ProdQueryOptWithMode :loading="loading" @submit="onSearch"></ProdQueryOptWithMode> |
| | | </template> |
| | | <template #step2="{ contentHeight }"> |
| | | <el-table |
| | | id="prod-inspection-table" |
| | | :data="tableData" |
| | | v-loading="loading" |
| | | :height="contentHeight + 'px'" |
| | | table-layout="fixed" |
| | | :show-overflow-tooltip="true" |
| | | size="small" |
| | | border |
| | | > |
| | | <el-table-column fixed="left" prop="index" label="ç¼å·" width="50"> </el-table-column> |
| | | <el-table-column |
| | | fixed="left" |
| | | prop="subTask.scensename" |
| | | label="åç§°" |
| | | :show-overflow-tooltip="true" |
| | | min-width="200" |
| | | > |
| | | </el-table-column> |
| | | <el-table-column |
| | | prop="subTask.planstarttime" |
| | | label="å·¡æ¥æ¶é´" |
| | | :formatter="timeFormat" |
| | | width="90" |
| | | /> |
| | | <!-- <el-table-column prop="provincename" label="ç" width="90" /> |
| | | <el-table-column prop="cityname" label="å¸" width="90" /> |
| | | <el-table-column prop="districtname" label="åºå¿" width="90" /> --> |
| | | <el-table-column prop="subTask.townname" label="è¡é" width="80" /> |
| | | <el-table-column prop="problems.length" label="é®é¢æ°" width="60" /> |
| | | <el-table-column label="é®é¢æè¦" width="300"> |
| | | <template #default="{ row }"> |
| | | <template v-for="(value, index) in row.problems" :key="value.guid"> |
| | | <br v-if="index > 0" />{{ index + 1 + 'ã' + value.problemname }} |
| | | </template> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="unChangeProblems.length" label="æªæ´æ¹æ°" width="60" /> |
| | | <el-table-column label="æªæ´æ¹é®é¢" width="300"> |
| | | <template #default="{ row }"> |
| | | <template v-for="(value, index) in row.unChangeProblems" :key="value.guid"> |
| | | <br v-if="index > 0" />{{ index + 1 + 'ã' + value.problemname }} |
| | | </template> |
| | | </template> |
| | | </el-table-column> |
| | | <!-- <el-table-column |
| | | prop="evaluate.updatedate" |
| | | label="æ´æ°æ¶é´" |
| | | width="140" |
| | | :formatter="timeFormat" |
| | | /> --> |
| | | </el-table> |
| | | </template> |
| | | </BaseProdProcess> |
| | | </template> |
| | | <script setup> |
| | | import { ref, inject } from 'vue' |
| | | import dayjs from 'dayjs' |
| | | import BaseProdProcess from './components/BaseProdProcess.vue' |
| | | import dataprodbaseApi from '@/api/fysp/dataprodbaseApi.js' |
| | | import { conversionFromTable } from '@/utils/excel' |
| | | import { useProdStepChange } from './prod-step-change.js' |
| | | import ProdQueryOptWithMode from './components/ProdQueryOptWithMode.vue' |
| | | |
| | | const { active, changeActive } = useProdStepChange() |
| | | const loading = ref(false) |
| | | const tableData = ref([]) |
| | | |
| | | function onStep1(opt) { |
| | | loading.value = true |
| | | dataprodbaseApi |
| | | .fetchProdInspectionInfo(opt) |
| | | .then((res) => { |
| | | if (res.success) { |
| | | tableData.value = res.data.map((item) => { |
| | | return { |
| | | ...item, |
| | | unChangeProblems: item.problems.filter((p) => !p.ischanged), |
| | | } |
| | | }) |
| | | } |
| | | changeActive() |
| | | }) |
| | | .finally(() => { |
| | | loading.value = false |
| | | }) |
| | | } |
| | | |
| | | function onStep2() { |
| | | changeActive() |
| | | } |
| | | |
| | | function onStep3(val) { |
| | | if (val.downloadType == '1') { |
| | | loading.value = true |
| | | conversionFromTable('prod-inspection-table', 'æ´æ¹æ¸
å') |
| | | loading.value = false |
| | | } |
| | | } |
| | | |
| | | function timeFormat(row, column, cellValue, index) { |
| | | return dayjs(cellValue).format('YYYY-MM-DD') |
| | | } |
| | | </script> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-row> |
| | | <!-- æ¥éª¤1 æ°æ®äº§åçæé项 --> |
| | | <div :class="active == 1 ? 'prod-active' : 'prod-inactive'" ref="step1Ref"> |
| | | <transition name="el-fade-in" @after-leave="handleTransitionContentEnd(1)"> |
| | | <div v-show="showStep1Content"> |
| | | <template v-if="$slots.step1"> |
| | | <slot name="step1" :onSearch="onSearch"></slot> |
| | | </template> |
| | | <template v-else> |
| | | <ProdQueryOpt :loading="loading" @submit="onSearch"> </ProdQueryOpt> |
| | | </template> |
| | | </div> |
| | | </transition> |
| | | <transition name="el-fade-in" @after-leave="handleTransitionThumbnailEnd(1)"> |
| | | <div |
| | | v-show="showStep1Thumbnail" |
| | | class="prod-thumbnail-wrapper" |
| | | :style="{ height: viewHeight + 'px' }" |
| | | @click="changeActive(1)" |
| | | > |
| | | <div class="prod-thumbnail">â ä¿®æ¹é项</div> |
| | | </div> |
| | | </transition> |
| | | </div> |
| | | <!-- æ¥éª¤2 æ°æ®äº§åç»æé¢è§ --> |
| | | <div :class="active == 2 ? 'prod-active' : 'prod-inactive'" ref="step2Ref"> |
| | | <transition name="el-fade-in" @after-leave="handleTransitionContentEnd(2)"> |
| | | <div v-show="showStep2Content"> |
| | | <div ref="titleRef" class="prod-title"> |
| | | <el-text tag="b" size="large">æ°æ®äº§åé¢è§</el-text> |
| | | <el-button type="primary" @click="$emit('onStep2')"> ä¸è½½æ°æ®äº§å </el-button> |
| | | </div> |
| | | <slot name="step2" :contentHeight="contentHeight"></slot> |
| | | </div> |
| | | </transition> |
| | | <transition name="el-fade-in" @after-leave="handleTransitionThumbnailEnd(2)"> |
| | | <div |
| | | v-show="showStep2Thumbnail" |
| | | class="prod-thumbnail-wrapper" |
| | | :style="{ height: viewHeight + 'px' }" |
| | | @click="changeActive(2)" |
| | | > |
| | | <div |
| | | :class=" |
| | | 'prod-thumbnail prod-thumbnail_middle ' + |
| | | (active < 2 ? 'prod-thumbnail-disabled' : '') |
| | | " |
| | | > |
| | | â¡æ°æ®äº§åé¢è§ |
| | | </div> |
| | | </div> |
| | | </transition> |
| | | </div> |
| | | <!-- æ¥éª¤3 æ°æ®äº§å表åä¸è½½ --> |
| | | <div :class="active == 3 ? 'prod-active' : 'prod-inactive'" ref="step3Ref"> |
| | | <transition name="el-fade-in" @after-leave="handleTransitionContentEnd(3)"> |
| | | <div v-show="showStep3Content"> |
| | | <template v-if="$slots.step3"> |
| | | <slot name="step3" :onDownload="onDownload" :queryOpt="queryOpt"></slot> |
| | | </template> |
| | | <template v-else> |
| | | <ProdDownload |
| | | :loading="loading" |
| | | :queryOpt="queryOpt" |
| | | @submit="onDownload" |
| | | ></ProdDownload> |
| | | </template> |
| | | </div> |
| | | </transition> |
| | | <transition name="el-fade-in" @after-leave="handleTransitionThumbnailEnd(3)"> |
| | | <div |
| | | v-show="showStep3Thumbnail" |
| | | class="prod-thumbnail-wrapper" |
| | | :style="{ height: viewHeight + 'px' }" |
| | | @click="changeActive(3)" |
| | | > |
| | | <div |
| | | :class=" |
| | | 'prod-thumbnail prod-thumbnail_end ' + (active < 3 ? 'prod-thumbnail-disabled' : '') |
| | | " |
| | | > |
| | | â¢æ°æ®äº§åä¸è½½ |
| | | </div> |
| | | </div> |
| | | </transition> |
| | | </div> |
| | | </el-row> |
| | | </template> |
| | | <script setup> |
| | | import { computed, inject, ref, watch, onMounted, onUnmounted } from 'vue' |
| | | import ProdQueryOpt from './ProdQueryOpt.vue' |
| | | import ProdDownload from './ProdDownload.vue' |
| | | |
| | | const props = defineProps({ |
| | | active: { |
| | | type: Number, |
| | | default: 1, |
| | | }, |
| | | loading: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:active', 'onStep1', 'onStep2', 'onStep3']) |
| | | |
| | | const contentMaxHeight = inject('contentMaxHeight') |
| | | const viewHeight = inject('viewHeight', contentMaxHeight.value) |
| | | |
| | | const btnDisabled = ref(false) |
| | | |
| | | const titleRef = ref(null) |
| | | const contentHeight = ref('50vh') |
| | | |
| | | function calContentHeight() { |
| | | // console.log(titleRef.value.offsetHeight); |
| | | contentHeight.value = viewHeight - (titleRef.value?.offsetHeight || 0) |
| | | // console.log(contentHeight.value); |
| | | } |
| | | |
| | | // æ°æ®äº§åçæé项 |
| | | const queryOpt = ref({}) |
| | | |
| | | // æ¥éª¤å¼ç¨ |
| | | const step1Ref = ref(null) |
| | | const step2Ref = ref(null) |
| | | const step3Ref = ref(null) |
| | | |
| | | // æ§å¶æ¾ç¤º/éèçç¶æ |
| | | const showStep1Content = ref(props.active === 1) |
| | | const showStep1Thumbnail = ref(props.active != 1) |
| | | const showStep2Content = ref(props.active === 2) |
| | | const showStep2Thumbnail = ref(props.active != 2) |
| | | const showStep3Content = ref(props.active === 3) |
| | | const showStep3Thumbnail = ref(props.active != 3) |
| | | |
| | | // è®°å½å¨ç»æ¯å¦æ£å¨è¿è¡ä¸ |
| | | const isAnimating = ref({}) |
| | | |
| | | // çå¬activeåå |
| | | watch( |
| | | () => props.active, |
| | | (newActive, oldActive) => { |
| | | // æ è®°å¨ç»å¼å§ |
| | | isAnimating.value[oldActive] = true |
| | | isAnimating.value[newActive] = true |
| | | |
| | | // å
éèææå
容ï¼çå¾
å¨ç»ç»æååæ¾ç¤ºæ£ç¡®çå
容 |
| | | if (oldActive === 1) { |
| | | showStep1Content.value = false |
| | | } else if (oldActive === 2) { |
| | | showStep2Content.value = false |
| | | } else if (oldActive === 3) { |
| | | showStep3Content.value = false |
| | | } |
| | | |
| | | if (newActive === 1) { |
| | | showStep1Thumbnail.value = false |
| | | } else if (newActive === 2) { |
| | | showStep2Thumbnail.value = false |
| | | } else if (newActive === 3) { |
| | | showStep3Thumbnail.value = false |
| | | } |
| | | }, |
| | | ) |
| | | |
| | | // å¤çå¨ç»ç»æäºä»¶ |
| | | function handleTransitionThumbnailEnd(stepIndex) { |
| | | // æ£æ¥å¨ç»æ¯å¦ç¡®å®ç»æï¼é¿å
éå¤è§¦åï¼ |
| | | if (isAnimating.value[stepIndex]) { |
| | | isAnimating.value[stepIndex] = false |
| | | |
| | | // setTimeout(() => { |
| | | // å¨ç»ç»æåï¼æ´æ°æ¾ç¤ºç¶æ |
| | | if (stepIndex === 1) { |
| | | showStep1Content.value = props.active === 1 |
| | | } else if (stepIndex === 2) { |
| | | showStep2Content.value = props.active === 2 |
| | | } else if (stepIndex === 3) { |
| | | showStep3Content.value = props.active === 3 |
| | | } |
| | | // }, 50); |
| | | } |
| | | } |
| | | |
| | | function handleTransitionContentEnd(stepIndex) { |
| | | // æ£æ¥å¨ç»æ¯å¦ç¡®å®ç»æï¼é¿å
éå¤è§¦åï¼ |
| | | if (isAnimating.value[stepIndex]) { |
| | | isAnimating.value[stepIndex] = false |
| | | |
| | | // setTimeout(() => { |
| | | // å¨ç»ç»æåï¼æ´æ°æ¾ç¤ºç¶æ |
| | | if (stepIndex === 1) { |
| | | showStep1Thumbnail.value = props.active != 1 |
| | | } else if (stepIndex === 2) { |
| | | showStep2Thumbnail.value = props.active != 2 |
| | | } else if (stepIndex === 3) { |
| | | showStep3Thumbnail.value = props.active != 3 |
| | | } |
| | | // }, 50); |
| | | } |
| | | } |
| | | |
| | | function onSearch(opt) { |
| | | queryOpt.value = opt |
| | | emit('onStep1', opt) |
| | | } |
| | | function onDownload(val) { |
| | | emit('onStep3', val) |
| | | } |
| | | function changeActive(index) { |
| | | let isAnimate = false |
| | | Object.values(isAnimating.value).forEach((item) => { |
| | | isAnimate = isAnimate || item |
| | | }) |
| | | if (!isAnimate && !btnDisabled.value && props.active >= index) { |
| | | emit('update:active', index) |
| | | btnDisabled.value = true |
| | | setTimeout(() => { |
| | | btnDisabled.value = false |
| | | }, 500) |
| | | } |
| | | // emit('update:active', index); |
| | | } |
| | | |
| | | let resizeObserver = null |
| | | |
| | | onMounted(() => { |
| | | if (titleRef.value) { |
| | | resizeObserver = new ResizeObserver(() => { |
| | | calContentHeight() |
| | | }) |
| | | resizeObserver.observe(titleRef.value) |
| | | } |
| | | }) |
| | | // å¨ç»ä»¶å¸è½½æ¶æ¸
ç |
| | | onUnmounted(() => { |
| | | if (resizeObserver && titleRef.value) { |
| | | resizeObserver.unobserve(titleRef) |
| | | } |
| | | }) |
| | | </script> |
| | | <style scoped> |
| | | .prod-active { |
| | | /* width: 66.667%; */ |
| | | width: 90%; |
| | | transition: |
| | | width 0.5s ease, |
| | | box-shadow 0.3s ease; |
| | | /* background-color: #409eff; */ |
| | | margin: 5px 0; |
| | | border-radius: 4px; |
| | | box-shadow: |
| | | -3px 0 6px rgba(0, 0, 0, 0.1), |
| | | 3px 0 6px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .prod-inactive { |
| | | /* width: 16.667%; */ |
| | | width: 5%; |
| | | transition: width 0.5s ease; |
| | | /* background-color: #e4e7ed; */ |
| | | margin: 5px 0; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .prod-title { |
| | | padding: 20px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .prod-thumbnail-wrapper { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | padding: 0 2px; |
| | | } |
| | | |
| | | .prod-thumbnail { |
| | | height: 90%; |
| | | width: 100%; |
| | | background-color: #409eff; |
| | | color: white; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | writing-mode: vertical-rl; |
| | | text-orientation: upright; |
| | | letter-spacing: 8px; |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | border-top-left-radius: 4px; |
| | | border-bottom-left-radius: 4px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .prod-thumbnail_middle { |
| | | border-radius: 0px; |
| | | } |
| | | |
| | | .prod-thumbnail_end { |
| | | border-top-left-radius: 0px; |
| | | border-bottom-left-radius: 0px; |
| | | border-top-right-radius: 4px; |
| | | border-bottom-right-radius: 4px; |
| | | } |
| | | |
| | | .prod-thumbnail-disabled { |
| | | background-color: #e4e7ed; |
| | | color: #c0c4cc; |
| | | cursor: not-allowed; |
| | | } |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-card shadow="never"> |
| | | <template #header> |
| | | <div><el-text tag="b" size="large">æ°æ®äº§åä¸è½½</el-text></div> |
| | | </template> |
| | | <el-form :inline="false" label-position="left" label-width="150px"> |
| | | <el-form-item label="åºå¿"> |
| | | <el-text>{{ opts.districtName }}</el-text> |
| | | </el-form-item> |
| | | <el-form-item label="æ¶é´èå´"> |
| | | <el-text>{{ opts.startTime }} è³ {{ opts.endTime }}</el-text> |
| | | </el-form-item> |
| | | <el-form-item label="åºæ¯ç±»å"> |
| | | <el-text>{{ opts.sceneTypeName }}</el-text> |
| | | </el-form-item> |
| | | <el-form-item label="产åå½¢å¼"> |
| | | <el-radio-group v-model="downloadType"> |
| | | <el-radio |
| | | v-for="item in _downloadTypeOptions" |
| | | :key="item.value" |
| | | :value="item.value" |
| | | :disabled="item.disabled" |
| | | > |
| | | {{ item.label }} |
| | | </el-radio> |
| | | </el-radio-group> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-row justify="end"> |
| | | <el-button |
| | | type="primary" |
| | | size="default" |
| | | :loading="loading" |
| | | @click="submit" |
| | | icon="Download" |
| | | >ä¸è½½</el-button |
| | | > |
| | | </el-row> |
| | | </template> |
| | | </el-card> |
| | | </template> |
| | | <script setup> |
| | | import { ref, computed } from 'vue'; |
| | | import dayjs from 'dayjs'; |
| | | import scene_1 from '@/assets/image/scene_1.png'; |
| | | |
| | | const props = defineProps({ |
| | | // æ°æ®äº§åçæé项 |
| | | queryOpt: { |
| | | type: Object, |
| | | default: () => {} |
| | | }, |
| | | loading: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | downloadTypeOptions: { |
| | | type: Array, |
| | | default: () => [ |
| | | { |
| | | value: '1', |
| | | label: 'Excel表å' |
| | | }, |
| | | { |
| | | value: '2', |
| | | label: 'Wordææ¡£' |
| | | } |
| | | ] |
| | | }, |
| | | // ä¸è½½ç±»åæ¯å¦ææ |
| | | downloadTypeValid: { |
| | | type: Array, |
| | | default: () => ['1'] |
| | | }, |
| | | defaultDownloadType: { |
| | | type: String, |
| | | default: '1' |
| | | } |
| | | }); |
| | | const emit = defineEmits(['submit']); |
| | | |
| | | const downloadType = ref(props.defaultDownloadType); |
| | | const opts = computed(() => { |
| | | if (props.queryOpt instanceof Array && props.queryOpt.length > 0) { |
| | | return props.queryOpt[0]; |
| | | } else { |
| | | return props.queryOpt; |
| | | } |
| | | }); |
| | | |
| | | const _downloadTypeOptions = computed(() => { |
| | | return props.downloadTypeOptions.map((item) => ({ |
| | | ...item, |
| | | disabled: !props.downloadTypeValid.includes(item.value) |
| | | })); |
| | | }); |
| | | |
| | | const submit = () => { |
| | | emit('submit', { |
| | | downloadType: downloadType.value |
| | | }); |
| | | }; |
| | | </script> |
| | | <style scoped> |
| | | /* .image { |
| | | width: 200px; |
| | | height: 200px; |
| | | } */ |
| | | </style> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-card shadow="never"> |
| | | <template #header> |
| | | <div><el-text tag="b" size="large">产åçæé项</el-text></div> |
| | | </template> |
| | | <SearchBar |
| | | v-show="active" |
| | | ref="refSearchBar" |
| | | :btn-show="false" |
| | | :init="false" |
| | | @on-submit="search" |
| | | > |
| | | </SearchBar> |
| | | <template #footer> |
| | | <el-row v-show="active" justify="end"> |
| | | <el-button |
| | | type="primary" |
| | | size="default" |
| | | :loading="loading" |
| | | @click="submit" |
| | | >çæ</el-button |
| | | > |
| | | </el-row> |
| | | </template> |
| | | </el-card> |
| | | </template> |
| | | <script setup> |
| | | import { ref, computed } from 'vue'; |
| | | import dayjs from 'dayjs'; |
| | | |
| | | const props = defineProps({ |
| | | loading: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | active: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }); |
| | | const emit = defineEmits(['submit']); |
| | | |
| | | const refSearchBar = ref(null); |
| | | |
| | | const submit = () => { |
| | | refSearchBar.value.onSubmit(); |
| | | }; |
| | | |
| | | const search = (options) => { |
| | | const opt = { |
| | | topTaskId: options.topTask.tguid, |
| | | topTaskName: options.topTask.name, |
| | | provinceCode: options.topTask.provincecode, |
| | | provinceName: options.topTask.provincename, |
| | | cityCode: options.topTask.citycode, |
| | | cityName: options.topTask.cityname, |
| | | districtCode: options.topTask.districtcode, |
| | | districtName: options.topTask.districtname, |
| | | townCode: options.topTask.towncode, |
| | | townName: options.topTask.townname, |
| | | startTime: dayjs(options.topTask.starttime).format('YYYY-MM-DD HH:mm:ss'), |
| | | endTime: dayjs(options.topTask.endtime) |
| | | .endOf('day') |
| | | .format('YYYY-MM-DD HH:mm:ss'), |
| | | sceneTypeId: options.sceneTypeId, |
| | | sceneTypeName: options.sceneTypeName, |
| | | needCache: true |
| | | }; |
| | | emit('submit', opt); |
| | | }; |
| | | </script> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-card shadow="never"> |
| | | <template #header> |
| | | <div><el-text tag="b" size="large">产åçæé项</el-text></div> |
| | | </template> |
| | | <el-space fill> |
| | | <el-alert type="info" show-icon :closable="false"> |
| | | <p>éæ©æ¬æéè¦ç»è®¡çæ»ä»»å¡ååºæ¯ç±»å</p> |
| | | </el-alert> |
| | | <el-form :inline="true" :model="formSearch"> |
| | | <FYOptionTopTask v-model:value="formSearch.topTask"></FYOptionTopTask> |
| | | <FYOptionScene |
| | | :allOption="false" |
| | | :type="2" |
| | | v-model:value="formSearch.scenetype" |
| | | ></FYOptionScene> |
| | | </el-form> |
| | | </el-space> |
| | | |
| | | <el-space fill> |
| | | <el-alert type="info" show-icon :closable="false"> |
| | | <p>éæ©éè¦è¿è¡å¯¹æ¯çåå²çæ¬</p> |
| | | </el-alert> |
| | | <el-form :inline="true" :model="formSearch2"> |
| | | <FYOptionTopTask |
| | | :beforeTask="formSearch.topTask" |
| | | v-model:value="formSearch2.topTask" |
| | | ></FYOptionTopTask> |
| | | </el-form> |
| | | </el-space> |
| | | <template #footer> |
| | | <el-row v-show="active" justify="end"> |
| | | <el-button |
| | | type="primary" |
| | | size="default" |
| | | :loading="loading" |
| | | @click="submit" |
| | | >çæ</el-button |
| | | > |
| | | </el-row> |
| | | </template> |
| | | </el-card> |
| | | </template> |
| | | <script setup> |
| | | import { ref, computed } from 'vue'; |
| | | import dayjs from 'dayjs'; |
| | | |
| | | const props = defineProps({ |
| | | loading: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | active: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }); |
| | | const emit = defineEmits(['submit']); |
| | | |
| | | const formSearch = ref({ |
| | | topTask: {}, |
| | | scenetype: {} |
| | | }); |
| | | |
| | | const formSearch2 = ref({ |
| | | topTask: {} |
| | | }); |
| | | |
| | | const submit = () => { |
| | | const opt1 = { |
| | | topTaskId: formSearch.value.topTask.tguid, |
| | | topTaskName: formSearch.value.topTask.name, |
| | | provinceCode: formSearch.value.topTask.provincecode, |
| | | provinceName: formSearch.value.topTask.provincename, |
| | | cityCode: formSearch.value.topTask.citycode, |
| | | cityName: formSearch.value.topTask.cityname, |
| | | districtCode: formSearch.value.topTask.districtcode, |
| | | districtName: formSearch.value.topTask.districtname, |
| | | townCode: formSearch.value.topTask.towncode, |
| | | townName: formSearch.value.topTask.townname, |
| | | startTime: dayjs(formSearch.value.topTask.starttime).format( |
| | | 'YYYY-MM-DD HH:mm:ss' |
| | | ), |
| | | endTime: dayjs(formSearch.value.topTask.endtime) |
| | | .endOf('day') |
| | | .format('YYYY-MM-DD HH:mm:ss'), |
| | | sceneTypeId: formSearch.value.scenetype.value, |
| | | sceneTypeName: formSearch.value.scenetype.label, |
| | | needCache: true |
| | | }; |
| | | const opt2 = { |
| | | topTaskId: formSearch2.value.topTask.tguid, |
| | | topTaskName: formSearch2.value.topTask.name, |
| | | provinceCode: formSearch2.value.topTask.provincecode, |
| | | provinceName: formSearch2.value.topTask.provincename, |
| | | cityCode: formSearch2.value.topTask.citycode, |
| | | cityName: formSearch2.value.topTask.cityname, |
| | | districtCode: formSearch2.value.topTask.districtcode, |
| | | districtName: formSearch2.value.topTask.districtname, |
| | | townCode: formSearch2.value.topTask.towncode, |
| | | townName: formSearch2.value.topTask.townname, |
| | | startTime: dayjs(formSearch2.value.topTask.starttime).format( |
| | | 'YYYY-MM-DD HH:mm:ss' |
| | | ), |
| | | endTime: dayjs(formSearch2.value.topTask.endtime) |
| | | .endOf('day') |
| | | .format('YYYY-MM-DD HH:mm:ss'), |
| | | sceneTypeId: formSearch.value.scenetype.value, |
| | | sceneTypeName: formSearch.value.scenetype.label, |
| | | needCache: true |
| | | }; |
| | | emit('submit', [opt1, opt2]); |
| | | }; |
| | | </script> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-card shadow="never"> |
| | | <template #header> |
| | | <div><el-text tag="b" size="large">产åçæé项</el-text></div> |
| | | </template> |
| | | <el-switch |
| | | v-model="mode" |
| | | size="large" |
| | | active-text="ææéæ¶æ®µç»è®¡" |
| | | inactive-text="ææ»ä»»å¡ç»è®¡" |
| | | /> |
| | | <!-- <SearchBar |
| | | v-show="active && !mode" |
| | | ref="refSearchBar" |
| | | :btn-show="false" |
| | | :init="false" |
| | | @on-submit="search" |
| | | > |
| | | </SearchBar> --> |
| | | <el-form :inline="true"> |
| | | <FYOptionScene |
| | | :allOption="false" |
| | | :type="2" |
| | | :initValue="false" |
| | | v-model:value="scenetype" |
| | | ></FYOptionScene> |
| | | <FYOptionTopTask v-show="!mode" v-model:value="topTask"></FYOptionTopTask> |
| | | <!-- åºå¿ --> |
| | | <FYOptionLocation |
| | | v-show="mode" |
| | | :allOption="false" |
| | | :level="3" |
| | | :checkStrictly="false" |
| | | v-model:value="locations" |
| | | ></FYOptionLocation> |
| | | <FYOptionTime |
| | | v-show="mode" |
| | | :initValue="false" |
| | | type="daterange" |
| | | v-model:value="timeRange" |
| | | style="width: 300px" |
| | | ></FYOptionTime> |
| | | </el-form> |
| | | <template #footer> |
| | | <el-row v-show="active" justify="end"> |
| | | <el-button type="primary" size="default" :loading="loading" @click="search">çæ</el-button> |
| | | </el-row> |
| | | </template> |
| | | </el-card> |
| | | </template> |
| | | <script setup> |
| | | import { ref, computed, watch } from 'vue' |
| | | import dayjs from 'dayjs' |
| | | |
| | | const props = defineProps({ |
| | | loading: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | active: { |
| | | type: Boolean, |
| | | default: true, |
| | | }, |
| | | }) |
| | | const emit = defineEmits(['submit']) |
| | | |
| | | const mode = ref(true) |
| | | const scenetype = ref({ |
| | | label: 'é¤é¥®', |
| | | value: '5', |
| | | }) |
| | | const topTask = ref({}) |
| | | |
| | | const locations = ref({}) |
| | | const timeRange = ref([]) |
| | | |
| | | watch( |
| | | () => topTask.value, |
| | | (newVal, oldVal) => { |
| | | if (newVal?.provincecode) { |
| | | locations.value.pCode = newVal.provincecode |
| | | locations.value.pName = newVal.provincename |
| | | locations.value.cCode = newVal.citycode |
| | | locations.value.cName = newVal.cityname |
| | | locations.value.dCode = newVal.districtcode |
| | | locations.value.dName = newVal.districtname |
| | | // locations.value.tCode = newVal.towncode |
| | | // locations.value.tName = newVal.townname |
| | | timeRange.value = [new Date(newVal.starttime), new Date(newVal.endtime)] |
| | | console.log(timeRange.value) |
| | | } |
| | | }, |
| | | { deep: true }, |
| | | ) |
| | | |
| | | const search = (options) => { |
| | | const [st, et] = timeRange.value |
| | | const startTime = dayjs(st).startOf('day').format('YYYY-MM-DD HH:mm:ss') |
| | | const endTime = dayjs(et).endOf('day').format('YYYY-MM-DD HH:mm:ss') |
| | | |
| | | let opt = { |
| | | sceneTypeId: scenetype.value.sceneTypeId, |
| | | sceneTypeName: scenetype.value.sceneTypeName, |
| | | needCache: true, |
| | | } |
| | | // ææéæ¶æ®µç»è®¡ |
| | | if (mode.value) { |
| | | opt = { |
| | | provinceCode: locations.value.pCode, |
| | | provinceName: locations.value.pName, |
| | | cityCode: locations.value.cCode, |
| | | cityName: locations.value.cName, |
| | | districtCode: locations.value.dCode, |
| | | districtName: locations.value.dName, |
| | | townCode: locations.value.tCode, |
| | | townName: locations.value.tName, |
| | | startTime, |
| | | endTime, |
| | | ...opt, |
| | | } |
| | | } |
| | | // ææ»ä»»å¡ç»è®¡ |
| | | else { |
| | | opt = { |
| | | provinceCode: topTask.value.provincecode, |
| | | provinceName: topTask.value.provincename, |
| | | cityCode: topTask.value.citycode, |
| | | cityName: topTask.value.cityname, |
| | | districtCode: topTask.value.districtcode, |
| | | districtName: topTask.value.districtname, |
| | | townCode: topTask.value.towncode, |
| | | townName: topTask.value.townname, |
| | | ...opt, |
| | | } |
| | | } |
| | | |
| | | // const opt = { |
| | | // topTaskId: options.topTask.tguid, |
| | | // topTaskName: options.topTask.name, |
| | | // provinceCode: options.topTask.provincecode, |
| | | // provinceName: options.topTask.provincename, |
| | | // cityCode: options.topTask.citycode, |
| | | // cityName: options.topTask.cityname, |
| | | // districtCode: options.topTask.districtcode, |
| | | // districtName: options.topTask.districtname, |
| | | // townCode: options.topTask.towncode, |
| | | // townName: options.topTask.townname, |
| | | // startTime, |
| | | // endTime, |
| | | // sceneTypeId: options.sceneTypeId, |
| | | // sceneTypeName: options.sceneTypeName, |
| | | // needCache: true |
| | | // }; |
| | | emit('submit', opt) |
| | | } |
| | | </script> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import { ref } from 'vue'; |
| | | /** |
| | | * æ°æ®äº§åæ¥éª¤åæ¢ |
| | | */ |
| | | export function useProdStepChange() { |
| | | const active = ref(1); |
| | | function changeActive() { |
| | | active.value++; |
| | | active.value = active.value > 3 ? 1 : active.value; |
| | | } |
| | | return { |
| | | active, |
| | | changeActive |
| | | }; |
| | | } |
| | |
| | | <el-col :span="8"> |
| | | <el-card shadow="hover" class="dashboard-card green-card" @click="filterByCode('green')"> |
| | | <div class="card-content"> |
| | | <div class="card-title">绿ç åºéºæ°</div> |
| | | <div class="card-value">{{ statistics.greenCount }}</div> |
| | | <div class="card-title">绿ç åºéº</div> |
| | | <div class="card-value">{{ statistics.greenCount }}<el-text>个</el-text></div> |
| | | <div class="card-percentage">{{ statistics.greenPercentage }}%</div> |
| | | </div> |
| | | </el-card> |
| | |
| | | <el-col :span="8"> |
| | | <el-card shadow="hover" class="dashboard-card yellow-card" @click="filterByCode('yellow')"> |
| | | <div class="card-content"> |
| | | <div class="card-title">é»ç åºéºæ°</div> |
| | | <div class="card-value">{{ statistics.yellowCount }}</div> |
| | | <div class="card-title">é»ç åºéº</div> |
| | | <div class="card-value">{{ statistics.yellowCount }}<el-text>个</el-text></div> |
| | | <div class="card-percentage">{{ statistics.yellowPercentage }}%</div> |
| | | </div> |
| | | </el-card> |
| | |
| | | <el-col :span="8"> |
| | | <el-card shadow="hover" class="dashboard-card red-card" @click="filterByCode('red')"> |
| | | <div class="card-content"> |
| | | <div class="card-title">红ç åºéºæ°</div> |
| | | <div class="card-value">{{ statistics.redCount }}</div> |
| | | <div class="card-title">红ç åºéº</div> |
| | | <div class="card-value">{{ statistics.redCount }}<el-text>个</el-text></div> |
| | | <div class="card-percentage">{{ statistics.redPercentage }}%</div> |
| | | </div> |
| | | </el-card> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="complaint-manage"> |
| | | <!-- é¤é¥®åºéºä¿¡è®¿æè¯å¡ç --> |
| | | <el-card class="mb-4"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>é¤é¥®åºéºä¿¡è®¿æè¯</span> |
| | | <div class="filter-group"> |
| | | <FYOptionTime |
| | | class="m-r-8" |
| | | :initValue="false" |
| | | type="daterange" |
| | | v-model:value="complaintDateRange" |
| | | style="width: 300px; margin-bottom: 0px" |
| | | :shortcuts="shortcuts" |
| | | ></FYOptionTime> |
| | | <el-button type="success" icon="Plus" @click="addComplaint">æ°å¢æè¯</el-button> |
| | | <el-button type="info" icon="Upload" @click="importComplaint">æ¹é导å
¥</el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- å¾è¡¨å±ç¤º --> |
| | | <div class="chart-container"> |
| | | <div class="chart-item"> |
| | | <div class="chart-header"> |
| | | <h3>æè¯æ°è¶å¿</h3> |
| | | <div class="chart-summary">æè¯æ»æ°: {{ complaintStats.totalCount }}</div> |
| | | </div> |
| | | <div ref="dailyComplaintChart" class="chart"></div> |
| | | </div> |
| | | <div class="chart-item"> |
| | | <h3>æè¯æ¥æºåå¸</h3> |
| | | <div ref="sourceComplaintChart" class="chart"></div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- æè¯è®°å½è¡¨æ ¼ --> |
| | | <el-table |
| | | :data="filteredComplaintData" |
| | | table-layout="fixed" |
| | | :show-overflow-tooltip="true" |
| | | height="400px" |
| | | border |
| | | > |
| | | <el-table-column prop="shopName" label="æè¯åºéº" /> |
| | | <el-table-column prop="complaintReason" label="æè¯åå " width="180" /> |
| | | <el-table-column prop="complaintRequest" label="æè¯è¯æ±" width="180" /> |
| | | <el-table-column prop="complaintTime" label="æè¯æ¶é´" width="180" /> |
| | | <el-table-column prop="complaintSource" label="æè¯æ¥æº" width="150" /> |
| | | <el-table-column prop="handlingDepartment" label="å¤çé¨é¨" width="150" /> |
| | | <el-table-column prop="complaintResult" label="æè¯ç»æ" width="150" /> |
| | | <el-table-column width="250"> |
| | | <template #header> |
| | | <el-input v-model="complaintKeyword" placeholder="å
³é®åæç´¢" style="width: 120px" /> |
| | | </template> |
| | | <template #default="scope"> |
| | | <el-button size="small" type="primary" @click="editComplaint(scope.row)" |
| | | >ç¼è¾</el-button |
| | | > |
| | | <el-button size="small" type="danger" @click="deleteComplaint(scope.row.id)" |
| | | >å é¤</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <div class="pagination-container"> |
| | | <el-pagination |
| | | v-model:current-page="complaintPagination.currentPage" |
| | | v-model:page-size="complaintPagination.pageSize" |
| | | :page-sizes="[10, 20, 50, 100]" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :total="complaintPagination.total" |
| | | @size-change="handleComplaintSizeChange" |
| | | @current-change="handleComplaintCurrentChange" |
| | | /> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- æ°å¢/ç¼è¾æè¯å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="complaintDialogVisible" title="æè¯ä¿¡æ¯" width="600px"> |
| | | <el-form :model="complaintForm" label-width="100px"> |
| | | <el-form-item label="æè¯åºéº"> |
| | | <el-input v-model="complaintForm.shopName" /> |
| | | </el-form-item> |
| | | <el-form-item label="æè¯åå "> |
| | | <el-input v-model="complaintForm.complaintReason" /> |
| | | </el-form-item> |
| | | <el-form-item label="æè¯è¯æ±"> |
| | | <el-input v-model="complaintForm.complaintRequest" type="textarea" /> |
| | | </el-form-item> |
| | | <el-form-item label="æè¯æ¶é´"> |
| | | <el-date-picker v-model="complaintForm.complaintTime" type="datetime" /> |
| | | </el-form-item> |
| | | <el-form-item label="æè¯æ¥æº"> |
| | | <el-input v-model="complaintForm.complaintSource" /> |
| | | </el-form-item> |
| | | <el-form-item label="å¤çé¨é¨"> |
| | | <el-input v-model="complaintForm.handlingDepartment" /> |
| | | </el-form-item> |
| | | <el-form-item label="æè¯ç»æ"> |
| | | <el-input v-model="complaintForm.complaintResult" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="complaintDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="saveComplaint">ä¿å</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- æè¯æ¹é导å
¥å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="complaintImportDialogVisible" title="æè¯æ¹é导å
¥" width="600px"> |
| | | <div class="import-container"> |
| | | <p class="import-tip">è¯·éæ©è¦å¯¼å
¥çExcelæä»¶</p> |
| | | <el-upload |
| | | class="upload-demo" |
| | | action="#" |
| | | drag |
| | | :auto-upload="false" |
| | | :on-change="handleComplaintFileChange" |
| | | :file-list="complaintImportFileList" |
| | | accept=".xlsx,.xls" |
| | | :limit="1" |
| | | :on-exceed="handleExceed" |
| | | > |
| | | <el-icon class="el-icon--upload"><upload-filled /></el-icon> |
| | | <div class="el-upload__text">æå¨æä»¶æ<em>ç¹å»ä¸ä¼ </em></div> |
| | | <template #tip> |
| | | <div class="el-upload__tip">åªè½ä¸ä¼ Excelæä»¶ï¼ä¸ä¸è¶
è¿5MB</div> |
| | | </template> |
| | | </el-upload> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="complaintImportDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="confirmComplaintImport">导å
¥</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, onUnmounted, watch, computed } from 'vue' |
| | | import * as echarts from 'echarts' |
| | | import dayjs from 'dayjs' |
| | | import { ElMessage } from 'element-plus' |
| | | |
| | | // æ¶é´èå´å¿«æ·é项 |
| | | const dayStart = dayjs('2023-08-01').startOf('date') |
| | | const dayEnd = dayStart.endOf('date') |
| | | const shortcuts = [ |
| | | { |
| | | text: 'ä»å¤©', |
| | | value: [dayStart.toDate(), dayEnd.toDate()], |
| | | }, |
| | | { |
| | | text: 'æ¬å¨', |
| | | value: [dayStart.startOf('week').toDate(), dayEnd.endOf('week').toDate()], |
| | | }, |
| | | { |
| | | text: 'ä¸å¨', |
| | | value: [dayStart.day(-7).toDate(), dayEnd.day(-1).toDate()], |
| | | }, |
| | | { |
| | | text: 'æ¬æ', |
| | | value: [dayStart.startOf('month').toDate(), dayEnd.endOf('month').toDate()], |
| | | }, |
| | | { |
| | | text: '䏿', |
| | | value: [ |
| | | dayStart.subtract(1, 'month').startOf('month').toDate(), |
| | | dayEnd.subtract(1, 'month').endOf('month').toDate(), |
| | | ], |
| | | }, |
| | | { |
| | | text: 'æ¬å£åº¦', |
| | | value: [dayStart.startOf('quarter').toDate(), dayEnd.endOf('quarter').toDate()], |
| | | }, |
| | | { |
| | | text: 'ä¸å£åº¦', |
| | | value: [ |
| | | dayStart.subtract(1, 'quarter').startOf('quarter').toDate(), |
| | | dayEnd.subtract(1, 'quarter').endOf('quarter').toDate(), |
| | | ], |
| | | }, |
| | | { |
| | | text: 'å»å¹´', |
| | | value: [ |
| | | dayStart.subtract(1, 'year').startOf('year').toDate(), |
| | | dayEnd.subtract(1, 'year').endOf('year').toDate(), |
| | | ], |
| | | }, |
| | | { |
| | | text: 'ä»å¹´', |
| | | value: [dayStart.startOf('year').toDate(), dayEnd.endOf('year').toDate()], |
| | | }, |
| | | ] |
| | | |
| | | // å¾è¡¨å¼ç¨ |
| | | const dailyComplaintChart = ref(null) |
| | | const sourceComplaintChart = ref(null) |
| | | |
| | | // 信访æè¯æ°æ® |
| | | const complaintDateRange = ref([dayStart.startOf('month').toDate(), dayEnd.endOf('month').toDate()]) |
| | | const complaintKeyword = ref('') |
| | | const complaintStats = ref({ |
| | | totalCount: 85, |
| | | }) |
| | | const complaintTableData = ref([]) |
| | | const complaintPagination = ref({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }) |
| | | |
| | | // è¿æ»¤åçæè¯æ°æ® |
| | | const filteredComplaintData = computed(() => { |
| | | if (!complaintKeyword.value) { |
| | | return complaintTableData.value |
| | | } |
| | | const keyword = complaintKeyword.value.toLowerCase() |
| | | return complaintTableData.value.filter((item) => { |
| | | return Object.values(item).some((value) => { |
| | | return String(value).toLowerCase().includes(keyword) |
| | | }) |
| | | }) |
| | | }) |
| | | |
| | | // å¯¹è¯æ¡ç¶æ |
| | | const complaintDialogVisible = ref(false) |
| | | const complaintImportDialogVisible = ref(false) |
| | | |
| | | // 导å
¥æä»¶å表 |
| | | const complaintImportFileList = ref([]) |
| | | |
| | | // è¡¨åæ°æ® |
| | | const complaintForm = ref({ |
| | | id: '', |
| | | shopName: '', |
| | | complaintReason: '', |
| | | complaintRequest: '', |
| | | complaintTime: '', |
| | | complaintSource: '', |
| | | handlingDepartment: '', |
| | | complaintResult: '', |
| | | }) |
| | | |
| | | const searchComplaint = () => { |
| | | // 模ææç´¢æè¯æ°æ® |
| | | console.log('æç´¢æè¯æ°æ®', { |
| | | dateRange: complaintDateRange.value, |
| | | keyword: complaintKeyword.value, |
| | | }) |
| | | |
| | | // è®¡ç®æ¶é´èå´ |
| | | const startTime = complaintDateRange.value[0] |
| | | const endTime = complaintDateRange.value[1] |
| | | const startDate = dayjs(startTime) |
| | | const endDate = dayjs(endTime) |
| | | const daysDiff = endDate.diff(startDate, 'day') |
| | | const months = daysDiff / 30 |
| | | |
| | | // çææ°çæ¨¡ææ°æ® |
| | | const totalCount = Math.floor(20 * months) + Math.floor(Math.random() * 5) |
| | | complaintStats.value = { |
| | | totalCount, |
| | | } |
| | | |
| | | // çææ°çæè¯è®°å½ |
| | | const newData = [] |
| | | const shopNames = [ |
| | | 'é²å³é¦', |
| | | 'ç§ç¤è¾¾äºº', |
| | | 'å³ç¾é¤å
', |
| | | 'é¦è¾£å°é¾è¾', |
| | | 'å·èé¦', |
| | | '西é¤å
', |
| | | 'æ¥æåº', |
| | | 'ç«é
åº', |
| | | ] |
| | | const complaintReasons = ['æ²¹çæ°æ°', 'å¤é´åªå£°', 'å¼å³æ±¡æ', 'å«çé®é¢'] |
| | | const sources = ['12345ç线', 'å±
æ°æè¯', 'ç½ç»å¹³å°', 'å
¶ä»'] |
| | | const departments = ['徿±åºç¯ä¿å±', 'é¿å®åºç¯ä¿å±', 'éå®åºç¯ä¿å±', 'æ®éåºç¯ä¿å±'] |
| | | // const results = ['å·²å¤ç', 'å¤çä¸', 'æªå¤ç'] |
| | | const results = ['å·²å¤ç'] |
| | | |
| | | for (let i = 0; i < totalCount; i++) { |
| | | // çæå¨æ¶é´èå´å
çéæºæ¶é´ |
| | | const randomDays = Math.floor(Math.random() * (daysDiff + 1)) |
| | | const randomTime = startDate |
| | | .add(randomDays, 'day') |
| | | .add(Math.floor(Math.random() * 24), 'hour') |
| | | .add(Math.floor(Math.random() * 60), 'minute') |
| | | |
| | | newData.push({ |
| | | id: i + 1, |
| | | shopName: shopNames[Math.floor(Math.random() * shopNames.length)], |
| | | complaintReason: complaintReasons[Math.floor(Math.random() * complaintReasons.length)], |
| | | complaintRequest: |
| | | 'è¦æ±æ´æ¹' + complaintReasons[Math.floor(Math.random() * complaintReasons.length)], |
| | | complaintTime: randomTime.format('YYYY-MM-DD HH:mm'), |
| | | complaintSource: sources[Math.floor(Math.random() * sources.length)], |
| | | handlingDepartment: departments[Math.floor(Math.random() * departments.length)], |
| | | complaintResult: results[Math.floor(Math.random() * results.length)], |
| | | }) |
| | | } |
| | | |
| | | complaintTableData.value = newData |
| | | complaintPagination.value.total = newData.length |
| | | // éæ°åå§åå¾è¡¨ä»¥æ´æ°æ°æ® |
| | | initCharts() |
| | | } |
| | | |
| | | // çå¬æè¯æ¥æèå´åå |
| | | watch( |
| | | () => complaintDateRange.value, |
| | | () => { |
| | | searchComplaint() |
| | | }, |
| | | { deep: true }, |
| | | ) |
| | | |
| | | const addComplaint = () => { |
| | | // é置表å |
| | | complaintForm.value = { |
| | | id: '', |
| | | shopName: '', |
| | | complaintReason: '', |
| | | complaintRequest: '', |
| | | complaintTime: '', |
| | | complaintSource: '', |
| | | handlingDepartment: '', |
| | | complaintResult: '', |
| | | } |
| | | complaintDialogVisible.value = true |
| | | } |
| | | |
| | | const editComplaint = (row) => { |
| | | // å¡«å
表å |
| | | complaintForm.value = { ...row } |
| | | complaintDialogVisible.value = true |
| | | } |
| | | |
| | | const saveComplaint = () => { |
| | | // 模æä¿åæ°æ® |
| | | console.log('ä¿åæè¯æ°æ®', complaintForm.value) |
| | | complaintDialogVisible.value = false |
| | | // è¿éå¯ä»¥æ·»å å®é
çæ°æ®ä¿åé»è¾ |
| | | } |
| | | |
| | | const deleteComplaint = (id) => { |
| | | // 模æå 餿°æ® |
| | | console.log('å é¤æè¯æ°æ®', id) |
| | | // è¿éå¯ä»¥æ·»å å®é
çæ°æ®å é¤é»è¾ |
| | | } |
| | | |
| | | const importComplaint = () => { |
| | | // æå¼å¯¼å
¥å¯¹è¯æ¡ |
| | | complaintImportFileList.value = [] |
| | | complaintImportDialogVisible.value = true |
| | | } |
| | | |
| | | const handleComplaintFileChange = (file, fileList) => { |
| | | complaintImportFileList.value = fileList |
| | | console.log('éæ©çæè¯æä»¶:', file) |
| | | } |
| | | |
| | | const confirmComplaintImport = () => { |
| | | if (complaintImportFileList.value.length === 0) { |
| | | ElMessage.warning('è¯·éæ©è¦å¯¼å
¥çæä»¶') |
| | | return |
| | | } |
| | | |
| | | const file = complaintImportFileList.value[0] |
| | | console.log('å¼å§å¯¼å
¥æè¯æ°æ®:', file.name) |
| | | |
| | | // é¢ç导å
¥é»è¾ |
| | | // è¿éå°å®ç°å®é
çæä»¶è§£æåæ°æ®å¯¼å
¥ |
| | | |
| | | complaintImportDialogVisible.value = false |
| | | ElMessage.success('导å
¥æä½å·²è§¦åï¼é¢ç导å
¥é»è¾') |
| | | } |
| | | |
| | | const handleExceed = (files, fileList) => { |
| | | ElMessage.warning('åªè½ä¸ä¼ ä¸ä¸ªæä»¶') |
| | | } |
| | | |
| | | const handleComplaintSizeChange = (size) => { |
| | | complaintPagination.value.pageSize = size |
| | | // è¿éå¯ä»¥æ·»å å®é
çå页é»è¾ |
| | | } |
| | | |
| | | const handleComplaintCurrentChange = (current) => { |
| | | complaintPagination.value.currentPage = current |
| | | // è¿éå¯ä»¥æ·»å å®é
çå页é»è¾ |
| | | } |
| | | |
| | | // åå§åå¾è¡¨ |
| | | const initCharts = () => { |
| | | // æ¯æ¥æè¯æ°éå¾ |
| | | if (dailyComplaintChart.value && complaintDateRange.value.length > 0) { |
| | | const chart = echarts.init(dailyComplaintChart.value) |
| | | |
| | | // è®¡ç®æ¶é´èå´å¹¶çæxè½´æ°æ® |
| | | const startTime = complaintDateRange.value[0] |
| | | const endTime = complaintDateRange.value[1] |
| | | const startDate = dayjs(startTime) |
| | | const endDate = dayjs(endTime) |
| | | const daysDiff = endDate.diff(startDate, 'day') |
| | | |
| | | let xAxisData = [] |
| | | let seriesData = [] |
| | | |
| | | // å¤çæè¯æ°æ® |
| | | const complaintData = complaintTableData.value |
| | | const dateFormat = daysDiff <= 30 ? 'MM/DD' : 'YYYY/MM' |
| | | |
| | | // çææ¥æèå´ |
| | | if (daysDiff <= 30) { |
| | | // åä¸ä¸ªæå
ï¼ææ¥æ¾ç¤º |
| | | for (let i = 0; i <= daysDiff; i++) { |
| | | const date = startDate.add(i, 'day') |
| | | xAxisData.push(date.format(dateFormat)) |
| | | // 计ç®è¯¥æ¥æçæè¯æ°é |
| | | const count = complaintData.filter((item) => { |
| | | const itemDate = dayjs(item.complaintTime) |
| | | return itemDate.format(dateFormat) === date.format(dateFormat) |
| | | }).length |
| | | seriesData.push(count) |
| | | } |
| | | } else { |
| | | // è¶
è¿ä¸ä¸ªæï¼æææ¾ç¤º |
| | | const startMonth = startDate.startOf('month') |
| | | const endMonth = endDate.endOf('month') |
| | | const monthsDiff = endMonth.diff(startMonth, 'month') |
| | | |
| | | for (let i = 0; i <= monthsDiff; i++) { |
| | | const date = startMonth.add(i, 'month') |
| | | xAxisData.push(date.format(dateFormat)) |
| | | // 计ç®è¯¥æä»½çæè¯æ°é |
| | | const count = complaintData.filter((item) => { |
| | | const itemDate = dayjs(item.complaintTime) |
| | | return itemDate.format(dateFormat) === date.format(dateFormat) |
| | | }).length |
| | | seriesData.push(count) |
| | | } |
| | | } |
| | | |
| | | chart.setOption({ |
| | | xAxis: { |
| | | type: 'category', |
| | | data: xAxisData, |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | }, |
| | | series: [ |
| | | { |
| | | data: seriesData, |
| | | type: 'bar', |
| | | }, |
| | | ], |
| | | }) |
| | | } |
| | | |
| | | // æè¯æ¥æºåå¸å¾ |
| | | if (sourceComplaintChart.value) { |
| | | const chart = echarts.init(sourceComplaintChart.value) |
| | | chart.setOption({ |
| | | series: [ |
| | | { |
| | | type: 'pie', |
| | | data: [ |
| | | { value: 40, name: '12345ç线' }, |
| | | { value: 25, name: 'å±
æ°æè¯' }, |
| | | { value: 15, name: 'ç½ç»å¹³å°' }, |
| | | { value: 5, name: 'å
¶ä»' }, |
| | | ], |
| | | label: { |
| | | show: true, |
| | | formatter: '{b}: {d}%', |
| | | }, |
| | | }, |
| | | ], |
| | | }) |
| | | } |
| | | } |
| | | |
| | | // çå¬çªå£å¤§å°åå |
| | | const handleResize = () => { |
| | | // éæ°è°æ´å¾è¡¨å¤§å° |
| | | if (dailyComplaintChart.value) { |
| | | echarts.init(dailyComplaintChart.value).resize() |
| | | } |
| | | if (sourceComplaintChart.value) { |
| | | echarts.init(sourceComplaintChart.value).resize() |
| | | } |
| | | } |
| | | |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | searchComplaint() |
| | | initCharts() |
| | | window.addEventListener('resize', handleResize) |
| | | }) |
| | | |
| | | // ç»ä»¶å¸è½½æ¶æ¸
çäºä»¶çå¬ |
| | | onUnmounted(() => { |
| | | cleanup() |
| | | }) |
| | | |
| | | // æ¸
ç |
| | | const cleanup = () => { |
| | | window.removeEventListener('resize', handleResize) |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .complaint-manage { |
| | | padding: 20px; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .filter-group { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .chart-container { |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 30px; |
| | | } |
| | | |
| | | .chart-item { |
| | | flex: 1; |
| | | background-color: #fff; |
| | | padding: 20px; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .chart-item h3 { |
| | | margin-bottom: 15px; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .chart-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .chart-summary { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | } |
| | | |
| | | .chart { |
| | | height: 300px; |
| | | } |
| | | |
| | | .pagination-container { |
| | | margin-top: 20px; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .import-container { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .import-tip { |
| | | margin-bottom: 20px; |
| | | color: #606266; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <div class="monitor-control"> |
| | | <!-- æ»è§ç°åºå·¡æ¥å¡ç --> |
| | | <el-card class="mb-4"> |
| | | <template #header> |
| | | <!-- <template #header> |
| | | <div class="card-header"> |
| | | <span>ç°åºå·¡æ¥æ»è§</span> |
| | | <div class="filter-group"> |
| | | <el-form :model="params" label-position="left" label-width="70px"> |
| | | <FYOptionTime |
| | | :initValue="false" |
| | | type="daterange" |
| | | v-model:value="params.timeRange" |
| | | style="width: 300px; margin-bottom: 0px" |
| | | style="width: 300px" |
| | | :shortcuts="shortcuts" |
| | | ></FYOptionTime> |
| | | <!-- åºå¿ --> |
| | | <FYOptionLocation |
| | | class="m-l-8" |
| | | :allOption="false" |
| | | :level="3" |
| | | :checkStrictly="false" |
| | |
| | | v-model:value="params.locations" |
| | | style="width: 300px; margin-bottom: 0px" |
| | | ></FYOptionLocation> |
| | | </el-form> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </template> --> |
| | | |
| | | <el-scrollbar> |
| | | <!-- ç»è®¡æ°æ®åºå --> |
| | | <div class="stats-sections"> |
| | | <!-- 左侧ï¼å·²å·¡æ¥åºéºçãå·¡æ¥ç¹æ¬¡ã夿¥ç¹æ¬¡ --> |
| | |
| | | <div ref="problemTypeChart" class="chart"></div> |
| | | </div> |
| | | </div> |
| | | </el-scrollbar> |
| | | </el-card> |
| | | |
| | | <!-- é¤é¥®åºéºè¡æ¿å¤ç½å¡ç --> |
| | | <el-card class="mb-4"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>é¤é¥®åºéºè¡æ¿å¤ç½</span> |
| | | <div class="filter-group"> |
| | | <FYOptionTime |
| | | class="m-r-8" |
| | | :initValue="false" |
| | | type="daterange" |
| | | v-model:value="punishmentDateRange" |
| | | style="width: 300px; margin-bottom: 0px" |
| | | :shortcuts="shortcuts" |
| | | ></FYOptionTime> |
| | | <el-button type="success" icon="Plus" @click="addPunishment">æ°å¢å¤ç½</el-button> |
| | | <el-button type="info" icon="Upload" @click="importPunishment">æ¹é导å
¥</el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- å¾è¡¨å±ç¤º --> |
| | | <div class="chart-container"> |
| | | <div class="chart-item"> |
| | | <div class="chart-header"> |
| | | <h3>å¤ç½æ°è¶å¿</h3> |
| | | <div class="chart-summary">å¤ç½æ»æ°: {{ punishmentStats.totalCount }}</div> |
| | | </div> |
| | | <div ref="dailyPunishmentChart" class="chart"></div> |
| | | </div> |
| | | <div class="chart-item"> |
| | | <h3>åºéºç±»åå¤ç½åå¸</h3> |
| | | <div ref="shopTypePunishmentChart" class="chart"></div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- å¤ç½è®°å½è¡¨æ ¼ --> |
| | | <el-table |
| | | :data="filteredPunishmentData" |
| | | table-layout="fixed" |
| | | :show-overflow-tooltip="true" |
| | | height="400px" |
| | | border |
| | | > |
| | | <el-table-column prop="shopName" label="å¤ç½åºéº" /> |
| | | <el-table-column prop="punishmentItem" label="å¤ç½äºé¡¹" width="180" /> |
| | | <el-table-column prop="punishmentTime" label="å¤ç½æ¶é´" width="180" /> |
| | | <el-table-column prop="punishmentReason" label="å¤ç½çç±" width="200" /> |
| | | <el-table-column prop="punishmentResult" label="å¤ç½ç»æ" width="150" /> |
| | | <el-table-column prop="punishmentDepartment" label="å¤ç½é¨é¨" width="150" /> |
| | | <el-table-column width="250"> |
| | | <template #header> |
| | | <el-input v-model="punishmentKeyword" placeholder="å
³é®åæç´¢" style="width: 120px" /> |
| | | </template> |
| | | <template #default="scope"> |
| | | <el-button size="small" type="primary" @click="editPunishment(scope.row)" |
| | | >ç¼è¾</el-button |
| | | > |
| | | <el-button size="small" type="danger" @click="deletePunishment(scope.row.id)" |
| | | >å é¤</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <div class="pagination-container"> |
| | | <el-pagination |
| | | v-model:current-page="punishmentPagination.currentPage" |
| | | v-model:page-size="punishmentPagination.pageSize" |
| | | :page-sizes="[10, 20, 50, 100]" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :total="punishmentPagination.total" |
| | | @size-change="handlePunishmentSizeChange" |
| | | @current-change="handlePunishmentCurrentChange" |
| | | /> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- é¤é¥®åºéºä¿¡è®¿æè¯å¡ç --> |
| | | <el-card class="mb-4"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>é¤é¥®åºéºä¿¡è®¿æè¯</span> |
| | | <div class="filter-group"> |
| | | <FYOptionTime |
| | | class="m-r-8" |
| | | :initValue="false" |
| | | type="daterange" |
| | | v-model:value="complaintDateRange" |
| | | style="width: 300px; margin-bottom: 0px" |
| | | :shortcuts="shortcuts" |
| | | ></FYOptionTime> |
| | | <el-button type="success" icon="Plus" @click="addComplaint">æ°å¢æè¯</el-button> |
| | | <el-button type="info" icon="Upload" @click="importComplaint">æ¹é导å
¥</el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- å¾è¡¨å±ç¤º --> |
| | | <div class="chart-container"> |
| | | <div class="chart-item"> |
| | | <div class="chart-header"> |
| | | <h3>æè¯æ°è¶å¿</h3> |
| | | <div class="chart-summary">æè¯æ»æ°: {{ complaintStats.totalCount }}</div> |
| | | </div> |
| | | <div ref="dailyComplaintChart" class="chart"></div> |
| | | </div> |
| | | <div class="chart-item"> |
| | | <h3>æè¯æ¥æºåå¸</h3> |
| | | <div ref="sourceComplaintChart" class="chart"></div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- æè¯è®°å½è¡¨æ ¼ --> |
| | | <el-table |
| | | :data="filteredComplaintData" |
| | | table-layout="fixed" |
| | | :show-overflow-tooltip="true" |
| | | height="400px" |
| | | border |
| | | > |
| | | <el-table-column prop="shopName" label="æè¯åºéº" /> |
| | | <el-table-column prop="complaintReason" label="æè¯åå " width="180" /> |
| | | <el-table-column prop="complaintRequest" label="æè¯è¯æ±" width="180" /> |
| | | <el-table-column prop="complaintTime" label="æè¯æ¶é´" width="180" /> |
| | | <el-table-column prop="complaintSource" label="æè¯æ¥æº" width="150" /> |
| | | <el-table-column prop="handlingDepartment" label="å¤çé¨é¨" width="150" /> |
| | | <el-table-column prop="complaintResult" label="æè¯ç»æ" width="150" /> |
| | | <el-table-column width="250"> |
| | | <template #header> |
| | | <el-input v-model="complaintKeyword" placeholder="å
³é®åæç´¢" style="width: 120px" /> |
| | | </template> |
| | | <template #default="scope"> |
| | | <el-button size="small" type="primary" @click="editComplaint(scope.row)" |
| | | >ç¼è¾</el-button |
| | | > |
| | | <el-button size="small" type="danger" @click="deleteComplaint(scope.row.id)" |
| | | >å é¤</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <div class="pagination-container"> |
| | | <el-pagination |
| | | v-model:current-page="complaintPagination.currentPage" |
| | | v-model:page-size="complaintPagination.pageSize" |
| | | :page-sizes="[10, 20, 50, 100]" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :total="complaintPagination.total" |
| | | @size-change="handleComplaintSizeChange" |
| | | @current-change="handleComplaintCurrentChange" |
| | | /> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- æ°å¢/ç¼è¾å¤ç½å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="punishmentDialogVisible" title="å¤ç½ä¿¡æ¯" width="600px"> |
| | | <el-form :model="punishmentForm" label-width="100px"> |
| | | <el-form-item label="å¤ç½åºéº"> |
| | | <el-input v-model="punishmentForm.shopName" /> |
| | | </el-form-item> |
| | | <el-form-item label="å¤ç½äºé¡¹"> |
| | | <el-input v-model="punishmentForm.punishmentItem" /> |
| | | </el-form-item> |
| | | <el-form-item label="å¤ç½æ¶é´"> |
| | | <el-date-picker v-model="punishmentForm.punishmentTime" type="datetime" /> |
| | | </el-form-item> |
| | | <el-form-item label="å¤ç½çç±"> |
| | | <el-input v-model="punishmentForm.punishmentReason" type="textarea" /> |
| | | </el-form-item> |
| | | <el-form-item label="å¤ç½ç»æ"> |
| | | <el-input v-model="punishmentForm.punishmentResult" /> |
| | | </el-form-item> |
| | | <el-form-item label="å¤ç½é¨é¨"> |
| | | <el-input v-model="punishmentForm.punishmentDepartment" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="punishmentDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="savePunishment">ä¿å</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- æ°å¢/ç¼è¾æè¯å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="complaintDialogVisible" title="æè¯ä¿¡æ¯" width="600px"> |
| | | <el-form :model="complaintForm" label-width="100px"> |
| | | <el-form-item label="æè¯åºéº"> |
| | | <el-input v-model="complaintForm.shopName" /> |
| | | </el-form-item> |
| | | <el-form-item label="æè¯åå "> |
| | | <el-input v-model="complaintForm.complaintReason" /> |
| | | </el-form-item> |
| | | <el-form-item label="æè¯è¯æ±"> |
| | | <el-input v-model="complaintForm.complaintRequest" type="textarea" /> |
| | | </el-form-item> |
| | | <el-form-item label="æè¯æ¶é´"> |
| | | <el-date-picker v-model="complaintForm.complaintTime" type="datetime" /> |
| | | </el-form-item> |
| | | <el-form-item label="æè¯æ¥æº"> |
| | | <el-input v-model="complaintForm.complaintSource" /> |
| | | </el-form-item> |
| | | <el-form-item label="å¤çé¨é¨"> |
| | | <el-input v-model="complaintForm.handlingDepartment" /> |
| | | </el-form-item> |
| | | <el-form-item label="æè¯ç»æ"> |
| | | <el-input v-model="complaintForm.complaintResult" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="complaintDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="saveComplaint">ä¿å</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- å¤ç½æ¹é导å
¥å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="punishmentImportDialogVisible" title="å¤ç½æ¹é导å
¥" width="600px"> |
| | | <div class="import-container"> |
| | | <p class="import-tip">è¯·éæ©è¦å¯¼å
¥çExcelæä»¶</p> |
| | | <el-upload |
| | | class="upload-demo" |
| | | action="#" |
| | | :auto-upload="false" |
| | | :on-change="handlePunishmentFileChange" |
| | | :file-list="punishmentImportFileList" |
| | | accept=".xlsx,.xls" |
| | | :limit="1" |
| | | :on-exceed="handleExceed" |
| | | > |
| | | <el-button type="primary">éæ©æä»¶</el-button> |
| | | <template #tip> |
| | | <div class="el-upload__tip">åªè½ä¸ä¼ Excelæä»¶ï¼ä¸ä¸è¶
è¿5MB</div> |
| | | </template> |
| | | </el-upload> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="punishmentImportDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="confirmPunishmentImport">导å
¥</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- æè¯æ¹é导å
¥å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="complaintImportDialogVisible" title="æè¯æ¹é导å
¥" width="600px"> |
| | | <div class="import-container"> |
| | | <p class="import-tip">è¯·éæ©è¦å¯¼å
¥çExcelæä»¶</p> |
| | | <el-upload |
| | | class="upload-demo" |
| | | action="#" |
| | | :auto-upload="false" |
| | | :on-change="handleComplaintFileChange" |
| | | :file-list="complaintImportFileList" |
| | | accept=".xlsx,.xls" |
| | | :limit="1" |
| | | :on-exceed="handleExceed" |
| | | > |
| | | <el-button type="primary">éæ©æä»¶</el-button> |
| | | <template #tip> |
| | | <div class="el-upload__tip">åªè½ä¸ä¼ Excelæä»¶ï¼ä¸ä¸è¶
è¿5MB</div> |
| | | </template> |
| | | </el-upload> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="complaintImportDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="confirmComplaintImport">导å
¥</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | |
| | | const inspectionTrendChart = ref(null) |
| | | const problemTypeChart = ref(null) |
| | | const rectificationRateChart = ref(null) |
| | | const dailyPunishmentChart = ref(null) |
| | | const shopTypePunishmentChart = ref(null) |
| | | const dailyComplaintChart = ref(null) |
| | | const sourceComplaintChart = ref(null) |
| | | |
| | | // è¡æ¿å¤ç½æ°æ® |
| | | const punishmentDateRange = ref([ |
| | | dayStart.startOf('month').toDate(), |
| | | dayEnd.endOf('month').toDate(), |
| | | ]) |
| | | const punishmentKeyword = ref('') |
| | | const punishmentStats = ref({ |
| | | totalCount: 120, |
| | | }) |
| | | const punishmentTableData = ref([ |
| | | // { |
| | | // id: 1, |
| | | // shopName: 'å³ç¾é¤å
', |
| | | // punishmentItem: 'æ²¹çè¶
æ ææ¾', |
| | | // punishmentTime: '2026-03-10 14:30', |
| | | // punishmentReason: 'æªå®è£
æ²¹çåå设å¤', |
| | | // punishmentResult: 'ç½æ¬¾5000å
', |
| | | // punishmentDepartment: '徿±åºç¯ä¿å±', |
| | | // }, |
| | | // { |
| | | // id: 2, |
| | | // shopName: 'é¦è¾£å°é¾è¾', |
| | | // punishmentItem: 'æ²¹çè¶
æ ææ¾', |
| | | // punishmentTime: '2026-03-08 10:15', |
| | | // punishmentReason: 'æ²¹çååè®¾å¤æªæ£å¸¸è¿è¡', |
| | | // punishmentResult: 'ç½æ¬¾3000å
', |
| | | // punishmentDepartment: '徿±åºç¯ä¿å±', |
| | | // }, |
| | | ]) |
| | | const punishmentPagination = ref({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 2, |
| | | }) |
| | | |
| | | // è¿æ»¤åçå¤ç½æ°æ® |
| | | const filteredPunishmentData = computed(() => { |
| | | if (!punishmentKeyword.value) { |
| | | return punishmentTableData.value |
| | | } |
| | | const keyword = punishmentKeyword.value.toLowerCase() |
| | | return punishmentTableData.value.filter((item) => { |
| | | return Object.values(item).some((value) => { |
| | | return String(value).toLowerCase().includes(keyword) |
| | | }) |
| | | }) |
| | | }) |
| | | |
| | | // 信访æè¯æ°æ® |
| | | const complaintDateRange = ref([dayStart.startOf('month').toDate(), dayEnd.endOf('month').toDate()]) |
| | | const complaintKeyword = ref('') |
| | | const complaintStats = ref({ |
| | | totalCount: 85, |
| | | }) |
| | | const complaintTableData = ref([ |
| | | // { |
| | | // id: 1, |
| | | // shopName: 'é²å³é¦', |
| | | // complaintReason: 'æ²¹çæ°æ°', |
| | | // complaintRequest: 'è¦æ±å®è£
æ²¹çåå设å¤', |
| | | // complaintTime: '2026-03-12 09:20', |
| | | // complaintSource: '12345ç线', |
| | | // handlingDepartment: '徿±åºç¯ä¿å±', |
| | | // complaintResult: 'å·²å¤ç', |
| | | // }, |
| | | // { |
| | | // id: 2, |
| | | // shopName: 'ç§ç¤è¾¾äºº', |
| | | // complaintReason: 'å¤é´æ²¹ç污æ', |
| | | // complaintRequest: 'è¦æ±æ´æ¹', |
| | | // complaintTime: '2026-03-10 22:30', |
| | | // complaintSource: 'å±
æ°æè¯', |
| | | // handlingDepartment: '徿±åºç¯ä¿å±', |
| | | // complaintResult: 'å¤çä¸', |
| | | // }, |
| | | ]) |
| | | const complaintPagination = ref({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 2, |
| | | }) |
| | | |
| | | // è¿æ»¤åçæè¯æ°æ® |
| | | const filteredComplaintData = computed(() => { |
| | | if (!complaintKeyword.value) { |
| | | return complaintTableData.value |
| | | } |
| | | const keyword = complaintKeyword.value.toLowerCase() |
| | | return complaintTableData.value.filter((item) => { |
| | | return Object.values(item).some((value) => { |
| | | return String(value).toLowerCase().includes(keyword) |
| | | }) |
| | | }) |
| | | }) |
| | | |
| | | // å¯¹è¯æ¡ç¶æ |
| | | const punishmentDialogVisible = ref(false) |
| | | const complaintDialogVisible = ref(false) |
| | | const punishmentImportDialogVisible = ref(false) |
| | | const complaintImportDialogVisible = ref(false) |
| | | |
| | | // 导å
¥æä»¶å表 |
| | | const punishmentImportFileList = ref([]) |
| | | const complaintImportFileList = ref([]) |
| | | |
| | | // è¡¨åæ°æ® |
| | | const punishmentForm = ref({ |
| | | id: '', |
| | | shopName: '', |
| | | punishmentItem: '', |
| | | punishmentTime: '', |
| | | punishmentReason: '', |
| | | punishmentResult: '', |
| | | punishmentDepartment: '', |
| | | }) |
| | | |
| | | const complaintForm = ref({ |
| | | id: '', |
| | | shopName: '', |
| | | complaintReason: '', |
| | | complaintRequest: '', |
| | | complaintTime: '', |
| | | complaintSource: '', |
| | | handlingDepartment: '', |
| | | complaintResult: '', |
| | | }) |
| | | |
| | | const refreshInspectionData = () => { |
| | | // 模æå·æ°æ°æ® |
| | |
| | | initCharts() |
| | | } |
| | | |
| | | const searchPunishment = () => { |
| | | // 模ææç´¢å¤ç½æ°æ® |
| | | console.log('æç´¢å¤ç½æ°æ®', { |
| | | dateRange: punishmentDateRange.value, |
| | | keyword: punishmentKeyword.value, |
| | | }) |
| | | |
| | | // è®¡ç®æ¶é´èå´ |
| | | const startTime = punishmentDateRange.value[0] |
| | | const endTime = punishmentDateRange.value[1] |
| | | const startDate = dayjs(startTime) |
| | | const endDate = dayjs(endTime) |
| | | const daysDiff = endDate.diff(startDate, 'day') |
| | | const months = daysDiff / 30 |
| | | |
| | | // çææ°çæ¨¡ææ°æ® |
| | | const totalCount = Math.floor(10 * months) + Math.floor(Math.random() * 3) |
| | | punishmentStats.value = { |
| | | totalCount, |
| | | } |
| | | |
| | | // çææ°çå¤ç½è®°å½ |
| | | const newData = [] |
| | | const shopNames = [ |
| | | 'å³ç¾é¤å
', |
| | | 'é¦è¾£å°é¾è¾', |
| | | 'é²å³é¦', |
| | | 'ç§ç¤è¾¾äºº', |
| | | 'å·èé¦', |
| | | '西é¤å
', |
| | | 'æ¥æåº', |
| | | 'ç«é
åº', |
| | | ] |
| | | const punishmentItems = ['æ²¹çè¶
æ ææ¾', 'æªå®è£
æ²¹çåå设å¤', 'è®¾å¤æªæ£å¸¸è¿è¡', 'åªå£°æ±¡æ'] |
| | | const departments = ['徿±åºç¯ä¿å±', 'é¿å®åºç¯ä¿å±', 'éå®åºç¯ä¿å±', 'æ®éåºç¯ä¿å±'] |
| | | |
| | | for (let i = 0; i < totalCount; i++) { |
| | | // çæå¨æ¶é´èå´å
çéæºæ¶é´ |
| | | const randomDays = Math.floor(Math.random() * (daysDiff + 1)) |
| | | const randomTime = startDate |
| | | .add(randomDays, 'day') |
| | | .add(Math.floor(Math.random() * 24), 'hour') |
| | | .add(Math.floor(Math.random() * 60), 'minute') |
| | | |
| | | newData.push({ |
| | | id: i + 1, |
| | | shopName: shopNames[Math.floor(Math.random() * shopNames.length)], |
| | | punishmentItem: punishmentItems[Math.floor(Math.random() * punishmentItems.length)], |
| | | punishmentTime: randomTime.format('YYYY-MM-DD HH:mm'), |
| | | punishmentReason: |
| | | punishmentItems[Math.floor(Math.random() * punishmentItems.length)] + 'çè¿è§è¡ä¸º', |
| | | punishmentResult: 'ç½æ¬¾' + (Math.floor(Math.random() * 5) + 1) * 1000 + 'å
', |
| | | punishmentDepartment: departments[Math.floor(Math.random() * departments.length)], |
| | | }) |
| | | } |
| | | |
| | | punishmentTableData.value = newData |
| | | punishmentPagination.value.total = newData.length |
| | | // éæ°åå§åå¾è¡¨ä»¥æ´æ°æ°æ® |
| | | initCharts() |
| | | } |
| | | |
| | | const searchComplaint = () => { |
| | | // 模ææç´¢æè¯æ°æ® |
| | | console.log('æç´¢æè¯æ°æ®', { |
| | | dateRange: complaintDateRange.value, |
| | | keyword: complaintKeyword.value, |
| | | }) |
| | | |
| | | // è®¡ç®æ¶é´èå´ |
| | | const startTime = complaintDateRange.value[0] |
| | | const endTime = complaintDateRange.value[1] |
| | | const startDate = dayjs(startTime) |
| | | const endDate = dayjs(endTime) |
| | | const daysDiff = endDate.diff(startDate, 'day') |
| | | const months = daysDiff / 30 |
| | | |
| | | // çææ°çæ¨¡ææ°æ® |
| | | const totalCount = Math.floor(20 * months) + Math.floor(Math.random() * 5) |
| | | complaintStats.value = { |
| | | totalCount, |
| | | } |
| | | |
| | | // çææ°çæè¯è®°å½ |
| | | const newData = [] |
| | | const shopNames = [ |
| | | 'é²å³é¦', |
| | | 'ç§ç¤è¾¾äºº', |
| | | 'å³ç¾é¤å
', |
| | | 'é¦è¾£å°é¾è¾', |
| | | 'å·èé¦', |
| | | '西é¤å
', |
| | | 'æ¥æåº', |
| | | 'ç«é
åº', |
| | | ] |
| | | const complaintReasons = ['æ²¹çæ°æ°', 'å¤é´åªå£°', 'å¼å³æ±¡æ', 'å«çé®é¢'] |
| | | const sources = ['12345ç线', 'å±
æ°æè¯', 'ç½ç»å¹³å°', 'å
¶ä»'] |
| | | const departments = ['徿±åºç¯ä¿å±', 'é¿å®åºç¯ä¿å±', 'éå®åºç¯ä¿å±', 'æ®éåºç¯ä¿å±'] |
| | | const results = ['å·²å¤ç', 'å¤çä¸', 'æªå¤ç'] |
| | | |
| | | for (let i = 0; i < totalCount; i++) { |
| | | // çæå¨æ¶é´èå´å
çéæºæ¶é´ |
| | | const randomDays = Math.floor(Math.random() * (daysDiff + 1)) |
| | | const randomTime = startDate |
| | | .add(randomDays, 'day') |
| | | .add(Math.floor(Math.random() * 24), 'hour') |
| | | .add(Math.floor(Math.random() * 60), 'minute') |
| | | |
| | | newData.push({ |
| | | id: i + 1, |
| | | shopName: shopNames[Math.floor(Math.random() * shopNames.length)], |
| | | complaintReason: complaintReasons[Math.floor(Math.random() * complaintReasons.length)], |
| | | complaintRequest: |
| | | 'è¦æ±æ´æ¹' + complaintReasons[Math.floor(Math.random() * complaintReasons.length)], |
| | | complaintTime: randomTime.format('YYYY-MM-DD HH:mm'), |
| | | complaintSource: sources[Math.floor(Math.random() * sources.length)], |
| | | handlingDepartment: departments[Math.floor(Math.random() * departments.length)], |
| | | complaintResult: results[Math.floor(Math.random() * results.length)], |
| | | }) |
| | | } |
| | | |
| | | complaintTableData.value = newData |
| | | complaintPagination.value.total = newData.length |
| | | // éæ°åå§åå¾è¡¨ä»¥æ´æ°æ°æ® |
| | | initCharts() |
| | | } |
| | | |
| | | // çå¬å¤ç½æ¥æèå´åå |
| | | watch( |
| | | () => punishmentDateRange.value, |
| | | () => { |
| | | searchPunishment() |
| | | }, |
| | | { deep: true }, |
| | | ) |
| | | |
| | | // çå¬æè¯æ¥æèå´åå |
| | | watch( |
| | | () => complaintDateRange.value, |
| | | () => { |
| | | searchComplaint() |
| | | }, |
| | | { deep: true }, |
| | | ) |
| | | |
| | | const addPunishment = () => { |
| | | // é置表å |
| | | punishmentForm.value = { |
| | | id: '', |
| | | shopName: '', |
| | | punishmentItem: '', |
| | | punishmentTime: '', |
| | | punishmentReason: '', |
| | | punishmentResult: '', |
| | | punishmentDepartment: '', |
| | | } |
| | | punishmentDialogVisible.value = true |
| | | } |
| | | |
| | | const editPunishment = (row) => { |
| | | // å¡«å
表å |
| | | punishmentForm.value = { ...row } |
| | | punishmentDialogVisible.value = true |
| | | } |
| | | |
| | | const savePunishment = () => { |
| | | // 模æä¿åæ°æ® |
| | | console.log('ä¿åå¤ç½æ°æ®', punishmentForm.value) |
| | | punishmentDialogVisible.value = false |
| | | // è¿éå¯ä»¥æ·»å å®é
çæ°æ®ä¿åé»è¾ |
| | | } |
| | | |
| | | const deletePunishment = (id) => { |
| | | // 模æå 餿°æ® |
| | | console.log('å é¤å¤ç½æ°æ®', id) |
| | | // è¿éå¯ä»¥æ·»å å®é
çæ°æ®å é¤é»è¾ |
| | | } |
| | | |
| | | const importPunishment = () => { |
| | | // æå¼å¯¼å
¥å¯¹è¯æ¡ |
| | | punishmentImportFileList.value = [] |
| | | punishmentImportDialogVisible.value = true |
| | | } |
| | | |
| | | const handlePunishmentFileChange = (file, fileList) => { |
| | | punishmentImportFileList.value = fileList |
| | | console.log('éæ©çå¤ç½æä»¶:', file) |
| | | } |
| | | |
| | | const confirmPunishmentImport = () => { |
| | | if (punishmentImportFileList.value.length === 0) { |
| | | ElMessage.warning('è¯·éæ©è¦å¯¼å
¥çæä»¶') |
| | | return |
| | | } |
| | | |
| | | const file = punishmentImportFileList.value[0] |
| | | console.log('å¼å§å¯¼å
¥å¤ç½æ°æ®:', file.name) |
| | | |
| | | // é¢ç导å
¥é»è¾ |
| | | // è¿éå°å®ç°å®é
çæä»¶è§£æåæ°æ®å¯¼å
¥ |
| | | |
| | | punishmentImportDialogVisible.value = false |
| | | ElMessage.success('导å
¥æä½å·²è§¦åï¼é¢ç导å
¥é»è¾') |
| | | } |
| | | |
| | | const addComplaint = () => { |
| | | // é置表å |
| | | complaintForm.value = { |
| | | id: '', |
| | | shopName: '', |
| | | complaintReason: '', |
| | | complaintRequest: '', |
| | | complaintTime: '', |
| | | complaintSource: '', |
| | | handlingDepartment: '', |
| | | complaintResult: '', |
| | | } |
| | | complaintDialogVisible.value = true |
| | | } |
| | | |
| | | const editComplaint = (row) => { |
| | | // å¡«å
表å |
| | | complaintForm.value = { ...row } |
| | | complaintDialogVisible.value = true |
| | | } |
| | | |
| | | const saveComplaint = () => { |
| | | // 模æä¿åæ°æ® |
| | | console.log('ä¿åæè¯æ°æ®', complaintForm.value) |
| | | complaintDialogVisible.value = false |
| | | // è¿éå¯ä»¥æ·»å å®é
çæ°æ®ä¿åé»è¾ |
| | | } |
| | | |
| | | const deleteComplaint = (id) => { |
| | | // 模æå 餿°æ® |
| | | console.log('å é¤æè¯æ°æ®', id) |
| | | // è¿éå¯ä»¥æ·»å å®é
çæ°æ®å é¤é»è¾ |
| | | } |
| | | |
| | | const importComplaint = () => { |
| | | // æå¼å¯¼å
¥å¯¹è¯æ¡ |
| | | complaintImportFileList.value = [] |
| | | complaintImportDialogVisible.value = true |
| | | } |
| | | |
| | | const handleComplaintFileChange = (file, fileList) => { |
| | | complaintImportFileList.value = fileList |
| | | console.log('éæ©çæè¯æä»¶:', file) |
| | | } |
| | | |
| | | const confirmComplaintImport = () => { |
| | | if (complaintImportFileList.value.length === 0) { |
| | | ElMessage.warning('è¯·éæ©è¦å¯¼å
¥çæä»¶') |
| | | return |
| | | } |
| | | |
| | | const file = complaintImportFileList.value[0] |
| | | console.log('å¼å§å¯¼å
¥æè¯æ°æ®:', file.name) |
| | | |
| | | // é¢ç导å
¥é»è¾ |
| | | // è¿éå°å®ç°å®é
çæä»¶è§£æåæ°æ®å¯¼å
¥ |
| | | |
| | | complaintImportDialogVisible.value = false |
| | | ElMessage.success('导å
¥æä½å·²è§¦åï¼é¢ç导å
¥é»è¾') |
| | | } |
| | | |
| | | const handleExceed = (files, fileList) => { |
| | | ElMessage.warning('åªè½ä¸ä¼ ä¸ä¸ªæä»¶') |
| | | } |
| | | |
| | | const handlePunishmentSizeChange = (size) => { |
| | | punishmentPagination.value.pageSize = size |
| | | // è¿éå¯ä»¥æ·»å å®é
çå页é»è¾ |
| | | } |
| | | |
| | | const handlePunishmentCurrentChange = (current) => { |
| | | punishmentPagination.value.currentPage = current |
| | | // è¿éå¯ä»¥æ·»å å®é
çå页é»è¾ |
| | | } |
| | | |
| | | const handleComplaintSizeChange = (size) => { |
| | | complaintPagination.value.pageSize = size |
| | | // è¿éå¯ä»¥æ·»å å®é
çå页é»è¾ |
| | | } |
| | | |
| | | const handleComplaintCurrentChange = (current) => { |
| | | complaintPagination.value.currentPage = current |
| | | // è¿éå¯ä»¥æ·»å å®é
çå页é»è¾ |
| | | } |
| | | |
| | | // åå§åå¾è¡¨ |
| | |
| | | series: [ |
| | | { |
| | | type: 'pie', |
| | | radius: ['20%', '45%'], |
| | | center: ['50%', '45%'], |
| | | data: [ |
| | | { value: 30, name: 'æ²¹çå¨çº¿çæµè®¾å¤' }, |
| | | { value: 25, name: 'æ²¹çåå设æ½è®¾å¤' }, |
| | |
| | | { value: 20, name: '空è°å飿ºåªå£°' }, |
| | | { value: 18, name: 'å°è´¦ç®¡ç' }, |
| | | { value: 22, name: 'ä¿¡ç¨æ¿è¯ºèªè¯' }, |
| | | ], |
| | | label: { |
| | | show: true, |
| | | formatter: '{b}: {d}%', |
| | | }, |
| | | }, |
| | | ], |
| | | }) |
| | | } |
| | | |
| | | // æ¯æ¥å¤ç½æ°éå¾ |
| | | if (dailyPunishmentChart.value && punishmentDateRange.value.length > 0) { |
| | | const chart = echarts.init(dailyPunishmentChart.value) |
| | | |
| | | // è®¡ç®æ¶é´èå´å¹¶çæxè½´æ°æ® |
| | | const startTime = punishmentDateRange.value[0] |
| | | const endTime = punishmentDateRange.value[1] |
| | | const startDate = dayjs(startTime) |
| | | const endDate = dayjs(endTime) |
| | | const daysDiff = endDate.diff(startDate, 'day') |
| | | |
| | | let xAxisData = [] |
| | | let seriesData = [] |
| | | |
| | | // å¤çå¤ç½æ°æ® |
| | | const punishmentData = punishmentTableData.value |
| | | const dateFormat = daysDiff <= 30 ? 'MM/DD' : 'YYYY/MM' |
| | | |
| | | // çææ¥æèå´ |
| | | if (daysDiff <= 30) { |
| | | // åä¸ä¸ªæå
ï¼ææ¥æ¾ç¤º |
| | | for (let i = 0; i <= daysDiff; i++) { |
| | | const date = startDate.add(i, 'day') |
| | | xAxisData.push(date.format(dateFormat)) |
| | | // 计ç®è¯¥æ¥æçå¤ç½æ°é |
| | | const count = punishmentData.filter((item) => { |
| | | const itemDate = dayjs(item.punishmentTime) |
| | | return itemDate.format(dateFormat) === date.format(dateFormat) |
| | | }).length |
| | | seriesData.push(count) |
| | | } |
| | | } else { |
| | | // è¶
è¿ä¸ä¸ªæï¼æææ¾ç¤º |
| | | const startMonth = startDate.startOf('month') |
| | | const endMonth = endDate.endOf('month') |
| | | const monthsDiff = endMonth.diff(startMonth, 'month') |
| | | |
| | | for (let i = 0; i <= monthsDiff; i++) { |
| | | const date = startMonth.add(i, 'month') |
| | | xAxisData.push(date.format(dateFormat)) |
| | | // 计ç®è¯¥æä»½çå¤ç½æ°é |
| | | const count = punishmentData.filter((item) => { |
| | | const itemDate = dayjs(item.punishmentTime) |
| | | return itemDate.format(dateFormat) === date.format(dateFormat) |
| | | }).length |
| | | seriesData.push(count) |
| | | } |
| | | } |
| | | |
| | | chart.setOption({ |
| | | xAxis: { |
| | | type: 'category', |
| | | data: xAxisData, |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | }, |
| | | series: [ |
| | | { |
| | | data: seriesData, |
| | | type: 'bar', |
| | | }, |
| | | ], |
| | | }) |
| | | } |
| | | |
| | | // åºéºç±»åå¤ç½åå¸å¾ |
| | | if (shopTypePunishmentChart.value) { |
| | | const chart = echarts.init(shopTypePunishmentChart.value) |
| | | chart.setOption({ |
| | | series: [ |
| | | { |
| | | type: 'pie', |
| | | data: [ |
| | | { value: 50, name: 'ä¸é¤' }, |
| | | { value: 30, name: 'ç§ç¤' }, |
| | | { value: 20, name: '西é¤' }, |
| | | { value: 15, name: 'å
¶ä»' }, |
| | | ], |
| | | label: { |
| | | show: true, |
| | | formatter: '{b}: {d}%', |
| | | }, |
| | | }, |
| | | ], |
| | | }) |
| | | } |
| | | |
| | | // æ¯æ¥æè¯æ°éå¾ |
| | | if (dailyComplaintChart.value && complaintDateRange.value.length > 0) { |
| | | const chart = echarts.init(dailyComplaintChart.value) |
| | | |
| | | // è®¡ç®æ¶é´èå´å¹¶çæxè½´æ°æ® |
| | | const startTime = complaintDateRange.value[0] |
| | | const endTime = complaintDateRange.value[1] |
| | | const startDate = dayjs(startTime) |
| | | const endDate = dayjs(endTime) |
| | | const daysDiff = endDate.diff(startDate, 'day') |
| | | |
| | | let xAxisData = [] |
| | | let seriesData = [] |
| | | |
| | | // å¤çæè¯æ°æ® |
| | | const complaintData = complaintTableData.value |
| | | const dateFormat = daysDiff <= 30 ? 'MM/DD' : 'YYYY/MM' |
| | | |
| | | // çææ¥æèå´ |
| | | if (daysDiff <= 30) { |
| | | // åä¸ä¸ªæå
ï¼ææ¥æ¾ç¤º |
| | | for (let i = 0; i <= daysDiff; i++) { |
| | | const date = startDate.add(i, 'day') |
| | | xAxisData.push(date.format(dateFormat)) |
| | | // 计ç®è¯¥æ¥æçæè¯æ°é |
| | | const count = complaintData.filter((item) => { |
| | | const itemDate = dayjs(item.complaintTime) |
| | | return itemDate.format(dateFormat) === date.format(dateFormat) |
| | | }).length |
| | | seriesData.push(count) |
| | | } |
| | | } else { |
| | | // è¶
è¿ä¸ä¸ªæï¼æææ¾ç¤º |
| | | const startMonth = startDate.startOf('month') |
| | | const endMonth = endDate.endOf('month') |
| | | const monthsDiff = endMonth.diff(startMonth, 'month') |
| | | |
| | | for (let i = 0; i <= monthsDiff; i++) { |
| | | const date = startMonth.add(i, 'month') |
| | | xAxisData.push(date.format(dateFormat)) |
| | | // 计ç®è¯¥æä»½çæè¯æ°é |
| | | const count = complaintData.filter((item) => { |
| | | const itemDate = dayjs(item.complaintTime) |
| | | return itemDate.format(dateFormat) === date.format(dateFormat) |
| | | }).length |
| | | seriesData.push(count) |
| | | } |
| | | } |
| | | |
| | | chart.setOption({ |
| | | xAxis: { |
| | | type: 'category', |
| | | data: xAxisData, |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | }, |
| | | series: [ |
| | | { |
| | | data: seriesData, |
| | | type: 'bar', |
| | | }, |
| | | ], |
| | | }) |
| | | } |
| | | |
| | | // æè¯æ¥æºåå¸å¾ |
| | | if (sourceComplaintChart.value) { |
| | | const chart = echarts.init(sourceComplaintChart.value) |
| | | chart.setOption({ |
| | | series: [ |
| | | { |
| | | type: 'pie', |
| | | data: [ |
| | | { value: 40, name: '12345ç线' }, |
| | | { value: 25, name: 'å±
æ°æè¯' }, |
| | | { value: 15, name: 'ç½ç»å¹³å°' }, |
| | | { value: 5, name: 'å
¶ä»' }, |
| | | ], |
| | | label: { |
| | | show: true, |
| | |
| | | if (rectificationRateChart.value) { |
| | | echarts.init(rectificationRateChart.value).resize() |
| | | } |
| | | if (dailyPunishmentChart.value) { |
| | | echarts.init(dailyPunishmentChart.value).resize() |
| | | } |
| | | if (shopTypePunishmentChart.value) { |
| | | echarts.init(shopTypePunishmentChart.value).resize() |
| | | } |
| | | if (dailyComplaintChart.value) { |
| | | echarts.init(dailyComplaintChart.value).resize() |
| | | } |
| | | if (sourceComplaintChart.value) { |
| | | echarts.init(sourceComplaintChart.value).resize() |
| | | } |
| | | } |
| | | |
| | | // çå¬paramsåå |
| | |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | refreshInspectionData() |
| | | searchPunishment() |
| | | searchComplaint() |
| | | initCharts() |
| | | window.addEventListener('resize', handleResize) |
| | | }) |
| | |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .monitor-control { |
| | | padding: 20px; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | .mb-4 { |
| | | /* width: 600px; */ |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .filter-group { |
| | | display: flex; |
| | | align-items: center; |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | width: 100%; |
| | | gap: 10px; |
| | | } |
| | | |
| | | .mr-2 { |
| | |
| | | |
| | | .stats-sections { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | margin-bottom: 30px; |
| | | } |
| | |
| | | .stats-section { |
| | | flex: 1; |
| | | background-color: #fff; |
| | | padding: 20px; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | /* border: 1px solid #e4e7ed; */ |
| | | } |
| | | |
| | | .stats-section h3 { |
| | |
| | | |
| | | .stats-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); |
| | | gap: 20px; |
| | | grid-template-columns: 1fr 1fr; |
| | | gap: 10px; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | .chart-container { |
| | | display: grid; |
| | | grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 20px; |
| | | margin-bottom: 30px; |
| | | } |
| | |
| | | |
| | | .chart { |
| | | width: 100%; |
| | | height: 300px; |
| | | height: 250px; |
| | | } |
| | | |
| | | .pagination-container { |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="punishment-manage"> |
| | | <!-- é¤é¥®åºéºè¡æ¿å¤ç½å¡ç --> |
| | | <el-card class="mb-4"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>é¤é¥®åºéºè¡æ¿å¤ç½</span> |
| | | <div class="filter-group"> |
| | | <FYOptionTime |
| | | class="m-r-8" |
| | | :initValue="false" |
| | | type="daterange" |
| | | v-model:value="punishmentDateRange" |
| | | style="width: 300px; margin-bottom: 0px" |
| | | :shortcuts="shortcuts" |
| | | ></FYOptionTime> |
| | | <el-button type="success" icon="Plus" @click="addPunishment">æ°å¢å¤ç½</el-button> |
| | | <el-button type="info" icon="Upload" @click="importPunishment">æ¹é导å
¥</el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <!-- å¾è¡¨å±ç¤º --> |
| | | <div class="chart-container"> |
| | | <div class="chart-item"> |
| | | <div class="chart-header"> |
| | | <h3>å¤ç½æ°è¶å¿</h3> |
| | | <div class="chart-summary">å¤ç½æ»æ°: {{ punishmentStats.totalCount }}</div> |
| | | </div> |
| | | <div ref="dailyPunishmentChart" class="chart"></div> |
| | | </div> |
| | | <div class="chart-item"> |
| | | <h3>åºéºç±»åå¤ç½åå¸</h3> |
| | | <div ref="shopTypePunishmentChart" class="chart"></div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- å¤ç½è®°å½è¡¨æ ¼ --> |
| | | <el-table |
| | | :data="filteredPunishmentData" |
| | | table-layout="fixed" |
| | | :show-overflow-tooltip="true" |
| | | height="400px" |
| | | border |
| | | > |
| | | <el-table-column prop="shopName" label="å¤ç½åºéº" /> |
| | | <el-table-column prop="punishmentItem" label="å¤ç½äºé¡¹" width="180" /> |
| | | <el-table-column prop="punishmentTime" label="å¤ç½æ¶é´" width="180" /> |
| | | <el-table-column prop="punishmentReason" label="å¤ç½çç±" width="200" /> |
| | | <el-table-column prop="punishmentResult" label="å¤ç½ç»æ" width="150" /> |
| | | <el-table-column prop="punishmentDepartment" label="å¤ç½é¨é¨" width="150" /> |
| | | <el-table-column width="250"> |
| | | <template #header> |
| | | <el-input v-model="punishmentKeyword" placeholder="å
³é®åæç´¢" style="width: 120px" /> |
| | | </template> |
| | | <template #default="scope"> |
| | | <el-button size="small" type="primary" @click="editPunishment(scope.row)" |
| | | >ç¼è¾</el-button |
| | | > |
| | | <el-button size="small" type="danger" @click="deletePunishment(scope.row.id)" |
| | | >å é¤</el-button |
| | | > |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页 --> |
| | | <div class="pagination-container"> |
| | | <el-pagination |
| | | v-model:current-page="punishmentPagination.currentPage" |
| | | v-model:page-size="punishmentPagination.pageSize" |
| | | :page-sizes="[10, 20, 50, 100]" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | :total="punishmentPagination.total" |
| | | @size-change="handlePunishmentSizeChange" |
| | | @current-change="handlePunishmentCurrentChange" |
| | | /> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- æ°å¢/ç¼è¾å¤ç½å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="punishmentDialogVisible" title="å¤ç½ä¿¡æ¯" width="600px"> |
| | | <el-form :model="punishmentForm" label-width="100px"> |
| | | <el-form-item label="å¤ç½åºéº"> |
| | | <el-input v-model="punishmentForm.shopName" /> |
| | | </el-form-item> |
| | | <el-form-item label="å¤ç½äºé¡¹"> |
| | | <el-input v-model="punishmentForm.punishmentItem" /> |
| | | </el-form-item> |
| | | <el-form-item label="å¤ç½æ¶é´"> |
| | | <el-date-picker v-model="punishmentForm.punishmentTime" type="datetime" /> |
| | | </el-form-item> |
| | | <el-form-item label="å¤ç½çç±"> |
| | | <el-input v-model="punishmentForm.punishmentReason" type="textarea" /> |
| | | </el-form-item> |
| | | <el-form-item label="å¤ç½ç»æ"> |
| | | <el-input v-model="punishmentForm.punishmentResult" /> |
| | | </el-form-item> |
| | | <el-form-item label="å¤ç½é¨é¨"> |
| | | <el-input v-model="punishmentForm.punishmentDepartment" /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="punishmentDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="savePunishment">ä¿å</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- å¤ç½æ¹é导å
¥å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="punishmentImportDialogVisible" title="å¤ç½æ¹é导å
¥" width="600px"> |
| | | <div class="import-container"> |
| | | <p class="import-tip">è¯·éæ©è¦å¯¼å
¥çExcelæä»¶</p> |
| | | <el-upload |
| | | class="upload-demo" |
| | | action="#" |
| | | drag |
| | | :auto-upload="false" |
| | | :on-change="handlePunishmentFileChange" |
| | | :file-list="punishmentImportFileList" |
| | | accept=".xlsx,.xls" |
| | | :limit="1" |
| | | :on-exceed="handleExceed" |
| | | > |
| | | <el-icon class="el-icon--upload"><upload-filled /></el-icon> |
| | | <div class="el-upload__text">æå¨æä»¶æ<em>ç¹å»ä¸ä¼ </em></div> |
| | | <template #tip> |
| | | <div class="el-upload__tip">åªè½ä¸ä¼ Excelæä»¶ï¼ä¸ä¸è¶
è¿5MB</div> |
| | | </template> |
| | | </el-upload> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="punishmentImportDialogVisible = false">åæ¶</el-button> |
| | | <el-button type="primary" @click="confirmPunishmentImport">导å
¥</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, onMounted, onUnmounted, watch, computed } from 'vue' |
| | | import * as echarts from 'echarts' |
| | | import dayjs from 'dayjs' |
| | | import { ElMessage } from 'element-plus' |
| | | |
| | | // æ¶é´èå´å¿«æ·é项 |
| | | const dayStart = dayjs('2023-08-01').startOf('date') |
| | | const dayEnd = dayStart.endOf('date') |
| | | const shortcuts = [ |
| | | { |
| | | text: 'ä»å¤©', |
| | | value: [dayStart.toDate(), dayEnd.toDate()], |
| | | }, |
| | | { |
| | | text: 'æ¬å¨', |
| | | value: [dayStart.startOf('week').toDate(), dayEnd.endOf('week').toDate()], |
| | | }, |
| | | { |
| | | text: 'ä¸å¨', |
| | | value: [dayStart.day(-7).toDate(), dayEnd.day(-1).toDate()], |
| | | }, |
| | | { |
| | | text: 'æ¬æ', |
| | | value: [dayStart.startOf('month').toDate(), dayEnd.endOf('month').toDate()], |
| | | }, |
| | | { |
| | | text: '䏿', |
| | | value: [ |
| | | dayStart.subtract(1, 'month').startOf('month').toDate(), |
| | | dayEnd.subtract(1, 'month').endOf('month').toDate(), |
| | | ], |
| | | }, |
| | | { |
| | | text: 'æ¬å£åº¦', |
| | | value: [dayStart.startOf('quarter').toDate(), dayEnd.endOf('quarter').toDate()], |
| | | }, |
| | | { |
| | | text: 'ä¸å£åº¦', |
| | | value: [ |
| | | dayStart.subtract(1, 'quarter').startOf('quarter').toDate(), |
| | | dayEnd.subtract(1, 'quarter').endOf('quarter').toDate(), |
| | | ], |
| | | }, |
| | | { |
| | | text: 'å»å¹´', |
| | | value: [ |
| | | dayStart.subtract(1, 'year').startOf('year').toDate(), |
| | | dayEnd.subtract(1, 'year').endOf('year').toDate(), |
| | | ], |
| | | }, |
| | | { |
| | | text: 'ä»å¹´', |
| | | value: [dayStart.startOf('year').toDate(), dayEnd.endOf('year').toDate()], |
| | | }, |
| | | ] |
| | | |
| | | // å¾è¡¨å¼ç¨ |
| | | const dailyPunishmentChart = ref(null) |
| | | const shopTypePunishmentChart = ref(null) |
| | | |
| | | // è¡æ¿å¤ç½æ°æ® |
| | | const punishmentDateRange = ref([ |
| | | dayStart.startOf('month').toDate(), |
| | | dayEnd.endOf('month').toDate(), |
| | | ]) |
| | | const punishmentKeyword = ref('') |
| | | const punishmentStats = ref({ |
| | | totalCount: 120, |
| | | }) |
| | | const punishmentTableData = ref([]) |
| | | const punishmentPagination = ref({ |
| | | currentPage: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }) |
| | | |
| | | // è¿æ»¤åçå¤ç½æ°æ® |
| | | const filteredPunishmentData = computed(() => { |
| | | if (!punishmentKeyword.value) { |
| | | return punishmentTableData.value |
| | | } |
| | | const keyword = punishmentKeyword.value.toLowerCase() |
| | | return punishmentTableData.value.filter((item) => { |
| | | return Object.values(item).some((value) => { |
| | | return String(value).toLowerCase().includes(keyword) |
| | | }) |
| | | }) |
| | | }) |
| | | |
| | | // å¯¹è¯æ¡ç¶æ |
| | | const punishmentDialogVisible = ref(false) |
| | | const punishmentImportDialogVisible = ref(false) |
| | | |
| | | // 导å
¥æä»¶å表 |
| | | const punishmentImportFileList = ref([]) |
| | | |
| | | // è¡¨åæ°æ® |
| | | const punishmentForm = ref({ |
| | | id: '', |
| | | shopName: '', |
| | | punishmentItem: '', |
| | | punishmentTime: '', |
| | | punishmentReason: '', |
| | | punishmentResult: '', |
| | | punishmentDepartment: '', |
| | | }) |
| | | |
| | | const searchPunishment = () => { |
| | | // 模ææç´¢å¤ç½æ°æ® |
| | | console.log('æç´¢å¤ç½æ°æ®', { |
| | | dateRange: punishmentDateRange.value, |
| | | keyword: punishmentKeyword.value, |
| | | }) |
| | | |
| | | // è®¡ç®æ¶é´èå´ |
| | | const startTime = punishmentDateRange.value[0] |
| | | const endTime = punishmentDateRange.value[1] |
| | | const startDate = dayjs(startTime) |
| | | const endDate = dayjs(endTime) |
| | | const daysDiff = endDate.diff(startDate, 'day') |
| | | const months = daysDiff / 30 |
| | | |
| | | // çææ°çæ¨¡ææ°æ® |
| | | const totalCount = Math.floor(10 * months) + Math.floor(Math.random() * 3) |
| | | punishmentStats.value = { |
| | | totalCount, |
| | | } |
| | | |
| | | // çææ°çå¤ç½è®°å½ |
| | | const newData = [] |
| | | const shopNames = [ |
| | | 'å³ç¾é¤å
', |
| | | 'é¦è¾£å°é¾è¾', |
| | | 'é²å³é¦', |
| | | 'ç§ç¤è¾¾äºº', |
| | | 'å·èé¦', |
| | | '西é¤å
', |
| | | 'æ¥æåº', |
| | | 'ç«é
åº', |
| | | ] |
| | | const punishmentItems = ['æ²¹çè¶
æ ææ¾', 'æªå®è£
æ²¹çåå设å¤', 'è®¾å¤æªæ£å¸¸è¿è¡', 'åªå£°æ±¡æ'] |
| | | const departments = ['徿±åºç¯ä¿å±', 'é¿å®åºç¯ä¿å±', 'éå®åºç¯ä¿å±', 'æ®éåºç¯ä¿å±'] |
| | | |
| | | for (let i = 0; i < totalCount; i++) { |
| | | // çæå¨æ¶é´èå´å
çéæºæ¶é´ |
| | | const randomDays = Math.floor(Math.random() * (daysDiff + 1)) |
| | | const randomTime = startDate |
| | | .add(randomDays, 'day') |
| | | .add(Math.floor(Math.random() * 24), 'hour') |
| | | .add(Math.floor(Math.random() * 60), 'minute') |
| | | |
| | | newData.push({ |
| | | id: i + 1, |
| | | shopName: shopNames[Math.floor(Math.random() * shopNames.length)], |
| | | punishmentItem: punishmentItems[Math.floor(Math.random() * punishmentItems.length)], |
| | | punishmentTime: randomTime.format('YYYY-MM-DD HH:mm'), |
| | | punishmentReason: |
| | | punishmentItems[Math.floor(Math.random() * punishmentItems.length)] + 'çè¿è§è¡ä¸º', |
| | | punishmentResult: 'ç½æ¬¾' + (Math.floor(Math.random() * 5) + 1) * 1000 + 'å
', |
| | | punishmentDepartment: departments[Math.floor(Math.random() * departments.length)], |
| | | }) |
| | | } |
| | | |
| | | punishmentTableData.value = newData |
| | | punishmentPagination.value.total = newData.length |
| | | // éæ°åå§åå¾è¡¨ä»¥æ´æ°æ°æ® |
| | | initCharts() |
| | | } |
| | | |
| | | // çå¬å¤ç½æ¥æèå´åå |
| | | watch( |
| | | () => punishmentDateRange.value, |
| | | () => { |
| | | searchPunishment() |
| | | }, |
| | | { deep: true }, |
| | | ) |
| | | |
| | | const addPunishment = () => { |
| | | // é置表å |
| | | punishmentForm.value = { |
| | | id: '', |
| | | shopName: '', |
| | | punishmentItem: '', |
| | | punishmentTime: '', |
| | | punishmentReason: '', |
| | | punishmentResult: '', |
| | | punishmentDepartment: '', |
| | | } |
| | | punishmentDialogVisible.value = true |
| | | } |
| | | |
| | | const editPunishment = (row) => { |
| | | // å¡«å
表å |
| | | punishmentForm.value = { ...row } |
| | | punishmentDialogVisible.value = true |
| | | } |
| | | |
| | | const savePunishment = () => { |
| | | // 模æä¿åæ°æ® |
| | | console.log('ä¿åå¤ç½æ°æ®', punishmentForm.value) |
| | | punishmentDialogVisible.value = false |
| | | // è¿éå¯ä»¥æ·»å å®é
çæ°æ®ä¿åé»è¾ |
| | | } |
| | | |
| | | const deletePunishment = (id) => { |
| | | // 模æå 餿°æ® |
| | | console.log('å é¤å¤ç½æ°æ®', id) |
| | | // è¿éå¯ä»¥æ·»å å®é
çæ°æ®å é¤é»è¾ |
| | | } |
| | | |
| | | const importPunishment = () => { |
| | | // æå¼å¯¼å
¥å¯¹è¯æ¡ |
| | | punishmentImportFileList.value = [] |
| | | punishmentImportDialogVisible.value = true |
| | | } |
| | | |
| | | const handlePunishmentFileChange = (file, fileList) => { |
| | | punishmentImportFileList.value = fileList |
| | | console.log('éæ©çå¤ç½æä»¶:', file) |
| | | } |
| | | |
| | | const confirmPunishmentImport = () => { |
| | | if (punishmentImportFileList.value.length === 0) { |
| | | ElMessage.warning('è¯·éæ©è¦å¯¼å
¥çæä»¶') |
| | | return |
| | | } |
| | | |
| | | const file = punishmentImportFileList.value[0] |
| | | console.log('å¼å§å¯¼å
¥å¤ç½æ°æ®:', file.name) |
| | | |
| | | // é¢ç导å
¥é»è¾ |
| | | // è¿éå°å®ç°å®é
çæä»¶è§£æåæ°æ®å¯¼å
¥ |
| | | |
| | | punishmentImportDialogVisible.value = false |
| | | ElMessage.success('导å
¥æä½å·²è§¦åï¼é¢ç导å
¥é»è¾') |
| | | } |
| | | |
| | | const handleExceed = (files, fileList) => { |
| | | ElMessage.warning('åªè½ä¸ä¼ ä¸ä¸ªæä»¶') |
| | | } |
| | | |
| | | const handlePunishmentSizeChange = (size) => { |
| | | punishmentPagination.value.pageSize = size |
| | | // è¿éå¯ä»¥æ·»å å®é
çå页é»è¾ |
| | | } |
| | | |
| | | const handlePunishmentCurrentChange = (current) => { |
| | | punishmentPagination.value.currentPage = current |
| | | // è¿éå¯ä»¥æ·»å å®é
çå页é»è¾ |
| | | } |
| | | |
| | | // åå§åå¾è¡¨ |
| | | const initCharts = () => { |
| | | // æ¯æ¥å¤ç½æ°éå¾ |
| | | if (dailyPunishmentChart.value && punishmentDateRange.value.length > 0) { |
| | | const chart = echarts.init(dailyPunishmentChart.value) |
| | | |
| | | // è®¡ç®æ¶é´èå´å¹¶çæxè½´æ°æ® |
| | | const startTime = punishmentDateRange.value[0] |
| | | const endTime = punishmentDateRange.value[1] |
| | | const startDate = dayjs(startTime) |
| | | const endDate = dayjs(endTime) |
| | | const daysDiff = endDate.diff(startDate, 'day') |
| | | |
| | | let xAxisData = [] |
| | | let seriesData = [] |
| | | |
| | | // å¤çå¤ç½æ°æ® |
| | | const punishmentData = punishmentTableData.value |
| | | const dateFormat = daysDiff <= 30 ? 'MM/DD' : 'YYYY/MM' |
| | | |
| | | // çææ¥æèå´ |
| | | if (daysDiff <= 30) { |
| | | // åä¸ä¸ªæå
ï¼ææ¥æ¾ç¤º |
| | | for (let i = 0; i <= daysDiff; i++) { |
| | | const date = startDate.add(i, 'day') |
| | | xAxisData.push(date.format(dateFormat)) |
| | | // 计ç®è¯¥æ¥æçå¤ç½æ°é |
| | | const count = punishmentData.filter((item) => { |
| | | const itemDate = dayjs(item.punishmentTime) |
| | | return itemDate.format(dateFormat) === date.format(dateFormat) |
| | | }).length |
| | | seriesData.push(count) |
| | | } |
| | | } else { |
| | | // è¶
è¿ä¸ä¸ªæï¼æææ¾ç¤º |
| | | const startMonth = startDate.startOf('month') |
| | | const endMonth = endDate.endOf('month') |
| | | const monthsDiff = endMonth.diff(startMonth, 'month') |
| | | |
| | | for (let i = 0; i <= monthsDiff; i++) { |
| | | const date = startMonth.add(i, 'month') |
| | | xAxisData.push(date.format(dateFormat)) |
| | | // 计ç®è¯¥æä»½çå¤ç½æ°é |
| | | const count = punishmentData.filter((item) => { |
| | | const itemDate = dayjs(item.punishmentTime) |
| | | return itemDate.format(dateFormat) === date.format(dateFormat) |
| | | }).length |
| | | seriesData.push(count) |
| | | } |
| | | } |
| | | |
| | | chart.setOption({ |
| | | xAxis: { |
| | | type: 'category', |
| | | data: xAxisData, |
| | | }, |
| | | yAxis: { |
| | | type: 'value', |
| | | }, |
| | | series: [ |
| | | { |
| | | data: seriesData, |
| | | type: 'bar', |
| | | }, |
| | | ], |
| | | }) |
| | | } |
| | | |
| | | // åºéºç±»åå¤ç½åå¸å¾ |
| | | if (shopTypePunishmentChart.value) { |
| | | const chart = echarts.init(shopTypePunishmentChart.value) |
| | | chart.setOption({ |
| | | series: [ |
| | | { |
| | | type: 'pie', |
| | | data: [ |
| | | { value: 50, name: 'ä¸é¤' }, |
| | | { value: 30, name: 'ç§ç¤' }, |
| | | { value: 20, name: '西é¤' }, |
| | | { value: 15, name: 'å
¶ä»' }, |
| | | ], |
| | | label: { |
| | | show: true, |
| | | formatter: '{b}: {d}%', |
| | | }, |
| | | }, |
| | | ], |
| | | }) |
| | | } |
| | | } |
| | | |
| | | // çå¬çªå£å¤§å°åå |
| | | const handleResize = () => { |
| | | // éæ°è°æ´å¾è¡¨å¤§å° |
| | | if (dailyPunishmentChart.value) { |
| | | echarts.init(dailyPunishmentChart.value).resize() |
| | | } |
| | | if (shopTypePunishmentChart.value) { |
| | | echarts.init(shopTypePunishmentChart.value).resize() |
| | | } |
| | | } |
| | | |
| | | // çå½å¨æ |
| | | onMounted(() => { |
| | | searchPunishment() |
| | | initCharts() |
| | | window.addEventListener('resize', handleResize) |
| | | }) |
| | | |
| | | // ç»ä»¶å¸è½½æ¶æ¸
çäºä»¶çå¬ |
| | | onUnmounted(() => { |
| | | cleanup() |
| | | }) |
| | | |
| | | // æ¸
ç |
| | | const cleanup = () => { |
| | | window.removeEventListener('resize', handleResize) |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .punishment-manage { |
| | | padding: 20px; |
| | | background-color: #f5f7fa; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .card-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .filter-group { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .chart-container { |
| | | display: flex; |
| | | gap: 20px; |
| | | margin-bottom: 30px; |
| | | } |
| | | |
| | | .chart-item { |
| | | flex: 1; |
| | | background-color: #fff; |
| | | padding: 20px; |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .chart-item h3 { |
| | | margin-bottom: 15px; |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #303133; |
| | | } |
| | | |
| | | .chart-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | } |
| | | |
| | | .chart-summary { |
| | | font-size: 14px; |
| | | color: #606266; |
| | | } |
| | | |
| | | .chart { |
| | | height: 300px; |
| | | } |
| | | |
| | | .pagination-container { |
| | | margin-top: 20px; |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .import-container { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .import-tip { |
| | | margin-bottom: 20px; |
| | | color: #606266; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | display: flex; |
| | | justify-content: flex-end; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <el-calendar |
| | | v-model="dateValue" |
| | | :range="dateRange" |
| | | @update:model-value="onDateChange" |
| | | > |
| | | <el-calendar v-model="dateValue" :range="dateRange" @update:model-value="onDateChange"> |
| | | <template #header="{ date }"> |
| | | <div style="width: 100%"> |
| | | <el-row justify="space-between"> |
| | | <el-space> |
| | | <el-tag type="default" |
| | | >å·¡æ¥éï¼{{ |
| | | `${taskStatistic.complete}/${taskStatistic.total}` |
| | | }}</el-tag |
| | | >å·¡æ¥éï¼{{ `${taskStatistic.complete}/${taskStatistic.total}` }}</el-tag |
| | | > |
| | | <el-tag type="default" |
| | | <!-- <el-tag type="default" |
| | | >彿¥æ´æ¹çï¼{{ |
| | | formatPercent( |
| | | taskStatistic.changedProblemNumOnTime / |
| | |
| | | taskStatistic.totalProblemNum |
| | | ) |
| | | }}</el-tag |
| | | > |
| | | > --> |
| | | <el-tag type="default" |
| | | >ç»¼åæ´æ¹çï¼{{ |
| | | formatPercent( |
| | | taskStatistic.changedProblemNum / |
| | | taskStatistic.totalProblemNum |
| | | ) |
| | | >æ´æ¹çï¼{{ |
| | | formatPercent(taskStatistic.changedProblemNum / taskStatistic.totalProblemNum) |
| | | }}</el-tag |
| | | > |
| | | <!-- <el-tag type="default">æ´æ¹ï¼{{ taskStatistic.changed }}</el-tag> --> |
| | | </el-space> |
| | | <el-space> |
| | | <!-- <el-space> |
| | | <el-text>èç¦ç¨æ·ï¼</el-text> |
| | | <el-select |
| | | v-model="selectedUsers" |
| | |
| | | :value="user.userName" |
| | | /> |
| | | </el-select> |
| | | </el-space> |
| | | </el-space> --> |
| | | </el-row> |
| | | <el-row class="m-t-4"> |
| | | <!-- <el-row class="m-t-4"> |
| | | <el-space wrap> |
| | | <el-tag |
| | | type="default" |
| | | v-for="user in taskStatistic.progressPerUser" |
| | | :key="user.userId" |
| | | > |
| | | <!-- {{ |
| | | `${user.userName}ï¼å·¡æ¥é ${ |
| | | user.completeTaskNum |
| | | }ï¼å³æ¶æ´æ¹ç ${formatPercent( |
| | | user.changedProblemNumOnTime / user.totalProblemNum |
| | | )}ï¼å¹³åèæ¶ ${ |
| | | user.avgInspectionTime ? user.avgInspectionTime : '--' |
| | | }` |
| | | }} --> |
| | | {{ |
| | | `${user.userName}ï¼${ |
| | | user.completeTaskNum |
| | |
| | | }} |
| | | </el-tag> |
| | | </el-space> |
| | | </el-row> |
| | | </el-row> --> |
| | | </div> |
| | | </template> |
| | | <template #date-cell="{ data }"> |
| | | <div :class="calendarDayClz(data.day)"> |
| | | <div style="background-color: #f8f4f4">{{ getDay(data.day) }}</div> |
| | | <template v-if="computeDayTask(data.day)"> |
| | | <el-row |
| | | v-if="computeDayTask(data.day).totalTaskNum > 0" |
| | | justify="space-between" |
| | | > |
| | | <el-row v-if="computeDayTask(data.day).totalTaskNum > 0" justify="space-between"> |
| | | <el-space direction="vertical"> |
| | | <el-text size="small" tag="b">å·¡æ¥é</el-text> |
| | | <el-text size="small" |
| | |
| | | <el-text size="small">{{ |
| | | formatPercent( |
| | | computeDayTask(data.day).changedProblemNum / |
| | | computeDayTask(data.day).totalProblemNum |
| | | computeDayTask(data.day).totalProblemNum, |
| | | ) |
| | | }}</el-text> |
| | | </el-space> |
| | |
| | | <el-text |
| | | title="å·¡æ¥äººå" |
| | | size="small" |
| | | :type=" |
| | | selectedUsers.includes(item.userName) ? 'primary' : 'info' |
| | | " |
| | | :type="selectedUsers.includes(item.userName) ? 'primary' : 'info'" |
| | | :tag="selectedUsers.includes(item.userName) ? 'b' : 'span'" |
| | | >{{ item.userName }}</el-text |
| | | > |
| | |
| | | title="å·¡æ¥é" |
| | | size="small" |
| | | style="text-align: center; flex: 1" |
| | | :type=" |
| | | selectedUsers.includes(item.userName) ? 'primary' : 'info' |
| | | " |
| | | :type="selectedUsers.includes(item.userName) ? 'primary' : 'info'" |
| | | :tag="selectedUsers.includes(item.userName) ? 'b' : 'span'" |
| | | >{{ item.completeTaskNum }}</el-text |
| | | > |
| | |
| | | title="彿¥æ´æ¹ç" |
| | | size="small" |
| | | style="text-align: center; flex: 1" |
| | | :type=" |
| | | selectedUsers.includes(item.userName) ? 'primary' : 'info' |
| | | " |
| | | :type="selectedUsers.includes(item.userName) ? 'primary' : 'info'" |
| | | :tag="selectedUsers.includes(item.userName) ? 'b' : 'span'" |
| | | >{{ |
| | | formatPercent( |
| | | item.changedProblemNumOnTime / item.totalProblemNum |
| | | ) |
| | | }}</el-text |
| | | >{{ formatPercent(item.changedProblemNumOnTime / item.totalProblemNum) }}</el-text |
| | | > |
| | | <el-text |
| | | title="å¹³åèæ¶" |
| | | size="small" |
| | | style="text-align: center; flex: 1" |
| | | :type=" |
| | | selectedUsers.includes(item.userName) ? 'primary' : 'info' |
| | | " |
| | | :type="selectedUsers.includes(item.userName) ? 'primary' : 'info'" |
| | | :tag="selectedUsers.includes(item.userName) ? 'b' : 'span'" |
| | | >{{ |
| | | timeUtil.formatSecondsToChinese(item.avgInspectionTime) |
| | | }}</el-text |
| | | >{{ timeUtil.formatSecondsToChinese(item.avgInspectionTime) }}</el-text |
| | | > |
| | | <!-- </el-space> --> |
| | | </el-row> |
| | |
| | | </el-calendar> |
| | | </template> |
| | | <script setup> |
| | | import { ref, computed, onMounted, watch } from 'vue'; |
| | | import taskApi from '@/api/fysp/taskApi'; |
| | | import dayjs from 'dayjs'; |
| | | import timeUtil from '@/utils/time-util'; |
| | | import { ref, computed, onMounted, watch } from 'vue' |
| | | import taskApi from '@/api/fysp/taskApi' |
| | | import dayjs from 'dayjs' |
| | | import timeUtil from '@/utils/time-util' |
| | | |
| | | const props = defineProps({ |
| | | task: { |
| | | type: Object, |
| | | default: () => {} |
| | | default: () => {}, |
| | | }, |
| | | dayTaskList: { |
| | | type: Array, |
| | | default: () => [] |
| | | } |
| | | }); |
| | | const emit = defineEmits(['dateChange']); |
| | | default: () => [], |
| | | }, |
| | | }) |
| | | const emit = defineEmits(['dateChange']) |
| | | // é䏿¥æ |
| | | const selectedUsers = ref([]); |
| | | const selectedUsers = ref([]) |
| | | // const dateValue = ref(new Date()); |
| | | const dateValue = ref(); |
| | | const dateValue = ref() |
| | | // æ¥åèå´ |
| | | const startDay = computed(() => dayjs(props.task.starttime)); |
| | | const endDay = computed(() => dayjs(props.task.endtime)); |
| | | const dateRange = computed(() => [ |
| | | startDay.value.toDate(), |
| | | endDay.value.toDate() |
| | | ]); |
| | | const startDay = computed(() => dayjs(props.task.starttime)) |
| | | const endDay = computed(() => dayjs(props.task.endtime)) |
| | | const dateRange = computed(() => [startDay.value.toDate(), endDay.value.toDate()]) |
| | | |
| | | // æ¥ææ¯å¦å¨ä»»å¡èå´å
|
| | | function isDayEnable(day) { |
| | | const _day = dayjs(day); |
| | | return ( |
| | | _day.isSameOrAfter(startDay.value, 'day') && |
| | | _day.isSameOrBefore(endDay.value, 'day') |
| | | ); |
| | | const _day = dayjs(day) |
| | | return _day.isSameOrAfter(startDay.value, 'day') && _day.isSameOrBefore(endDay.value, 'day') |
| | | } |
| | | |
| | | /********************** æ¥ææ ·å¼ *********************************/ |
| | | function calendarDayClz(day) { |
| | | return ( |
| | | 'calendar-day ' + |
| | | (isDayEnable(day) ? 'calendar-day-enable' : 'calendar-day-disable') |
| | | ); |
| | | return 'calendar-day ' + (isDayEnable(day) ? 'calendar-day-enable' : 'calendar-day-disable') |
| | | } |
| | | function getDay(day) { |
| | | return day.split('-').splice(1, 2).join('-'); |
| | | return day.split('-').splice(1, 2).join('-') |
| | | } |
| | | |
| | | /********************** 任塿°æ® *********************************/ |
| | |
| | | () => props.dayTaskList, |
| | | (nV, oV) => { |
| | | if (nV && dateValue.value) { |
| | | onDateChange(dateValue.value); |
| | | onDateChange(dateValue.value) |
| | | } |
| | | }, |
| | | { immediate: false } |
| | | ); |
| | | { immediate: false }, |
| | | ) |
| | | |
| | | // // è·åæ¥ä»»å¡ç»è®¡ä¿¡æ¯ |
| | | // const dayTaskLoading = ref(false); |
| | |
| | | // } |
| | | |
| | | // æ¥ä»»å¡æ°æ®å±ç¤º |
| | | const compMap = new Map(); |
| | | const compMap = new Map() |
| | | function computeDayTask(day) { |
| | | const key = props.task.tguid + day; |
| | | const key = props.task.tguid + day |
| | | if (compMap.has(key)) { |
| | | return compMap.get(key).value; |
| | | return compMap.get(key).value |
| | | } |
| | | const result = computed(() => { |
| | | return props.dayTaskList.find((v) => { |
| | | return dayjs(v.date).isSame(dayjs(day)); |
| | | }); |
| | | }); |
| | | compMap.set(key, result); |
| | | return result.value; |
| | | return dayjs(v.date).isSame(dayjs(day)) |
| | | }) |
| | | }) |
| | | compMap.set(key, result) |
| | | return result.value |
| | | } |
| | | |
| | | function onDateChange(e) { |
| | | if (isDayEnable(e)) { |
| | | const day = dayjs(e).format('YYYY-MM-DD'); |
| | | const t = computeDayTask(day); |
| | | emit('dateChange', t, day); |
| | | const day = dayjs(e).format('YYYY-MM-DD') |
| | | const t = computeDayTask(day) |
| | | emit('dateChange', t, day) |
| | | } |
| | | } |
| | | |
| | |
| | | changedProblemNum: 0, |
| | | totalProblemNum: 0, |
| | | changedProblemNumOnTime: 0, |
| | | efficientChangedProNum: 0 |
| | | }; |
| | | const userMap = new Map(); |
| | | efficientChangedProNum: 0, |
| | | } |
| | | const userMap = new Map() |
| | | props.dayTaskList.forEach((e) => { |
| | | res.total += e.totalTaskNum; |
| | | res.complete += e.completeTaskNum; |
| | | res.changed += e.changedTaskNum; |
| | | res.changedProblemNum += e.changedProblemNum; |
| | | res.totalProblemNum += e.totalProblemNum; |
| | | res.total += e.totalTaskNum |
| | | res.complete += e.completeTaskNum |
| | | res.changed += e.changedTaskNum |
| | | res.changedProblemNum += e.changedProblemNum |
| | | res.totalProblemNum += e.totalProblemNum |
| | | e.progressPerUser.forEach((user) => { |
| | | if (!userMap.has(user.userId)) { |
| | | userMap.set(user.userId, { |
| | |
| | | changedProblemNumOnTime: 0, |
| | | totalProblemNum: 0, |
| | | totalInspectionTime: 0, |
| | | dayTaskNum: 0 |
| | | }); |
| | | dayTaskNum: 0, |
| | | }) |
| | | } |
| | | res.changedProblemNumOnTime += user.changedProblemNumOnTime |
| | | res.efficientChangedProNum += user.efficientChangedProNum |
| | | const userItem = userMap.get(user.userId); |
| | | userItem.completeTaskNum += user.completeTaskNum; |
| | | userItem.changedProblemNumOnTime += user.changedProblemNumOnTime; |
| | | userItem.totalProblemNum += user.totalProblemNum; |
| | | userItem.totalInspectionTime += user.avgInspectionTime ?? 0; |
| | | userItem.dayTaskNum++; |
| | | }); |
| | | }); |
| | | const userItem = userMap.get(user.userId) |
| | | userItem.completeTaskNum += user.completeTaskNum |
| | | userItem.changedProblemNumOnTime += user.changedProblemNumOnTime |
| | | userItem.totalProblemNum += user.totalProblemNum |
| | | userItem.totalInspectionTime += user.avgInspectionTime ?? 0 |
| | | userItem.dayTaskNum++ |
| | | }) |
| | | }) |
| | | res.progressPerUser = Array.from(userMap.values()).map((user) => ({ |
| | | ...user, |
| | | completeTaskNum: Math.round(user.completeTaskNum * 100) / 100, |
| | | avgInspectionTime: timeUtil.formatSecondsToChinese( |
| | | user.totalInspectionTime / user.dayTaskNum |
| | | ) |
| | | })); |
| | | return res; |
| | | }); |
| | | avgInspectionTime: timeUtil.formatSecondsToChinese(user.totalInspectionTime / user.dayTaskNum), |
| | | })) |
| | | return res |
| | | }) |
| | | |
| | | const formatPercent = (num) => { |
| | | return isNaN(num) ? '0%' : parseInt(num * 100) + '%'; |
| | | }; |
| | | return isNaN(num) ? '0%' : parseInt(num * 100) + '%' |
| | | } |
| | | /********************** åå§å *********************************/ |
| | | |
| | | // watch( |
| | |
| | | <div class="top-cards"> |
| | | <!-- æ¶é´å¨æé项å¡ç --> |
| | | <div class="time-period-card"> |
| | | <div class="card-title">æ¶é´éæ©</div> |
| | | <!-- <div class="card-title">æ¶é´éæ©</div> --> |
| | | <div class="time-controls"> |
| | | <div class="time-tab-container"> |
| | | <div |
| | |
| | | </div> |
| | | </div> |
| | | <div class="cards-container"> |
| | | <!-- è¶
æ æ° --> |
| | | <!-- æµåº¦é¢è¦ --> |
| | | <div class="metric-card"> |
| | | <div class="card-header"> |
| | | <div class="card-title">{{ getPeriodLabel() }}è¶
æ æ°</div> |
| | | <div class="card-title">{{ getPeriodLabel() }}æµåº¦é¢è¦</div> |
| | | <div class="card-icon warning-icon"> |
| | | <svg |
| | | width="20" |
| | |
| | | </svg> |
| | | </div> |
| | | </div> |
| | | <div class="card-value">{{ metrics.overStandardCount }}</div> |
| | | <div class="card-value">{{ metrics.overStandardCount }}<el-text>次</el-text></div> |
| | | <div class="card-trend"> |
| | | <span |
| | | class="trend-arrow" |
| | |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- å¨çº¿ç --> |
| | | <!-- 设å¤å¨çº¿ç --> |
| | | <el-popover placement="right-start" title="设å¤çæ§" width="400" trigger="click"> |
| | | <div class="popover-content"> |
| | | <div class="overview-items-container"> |
| | | <div class="overview-item"> |
| | | <div class="overview-label">é¤é¥®åºéºæ»æ°</div> |
| | | <div class="overview-value">{{ overview.totalShops }}</div> |
| | | </div> |
| | | |
| | | <div class="overview-item"> |
| | | <div class="overview-label">å¨çº¿è®¾å¤æ°</div> |
| | | <div class="overview-value">{{ overview.onlineDevices }}</div> |
| | | </div> |
| | | |
| | | <div class="overview-item"> |
| | | <div class="overview-label">ç¦»çº¿è®¾å¤æ°</div> |
| | | <div class="overview-value">{{ overview.offlineDevices }}</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 设å¤ç¶æé¥¼å¾ --> |
| | | <div class="device-status-chart"> |
| | | <canvas id="deviceStatusChart"></canvas> |
| | | </div> |
| | | </div> |
| | | <template #reference> |
| | | <div class="metric-card"> |
| | | <div class="card-header"> |
| | | <div class="card-title">å¨çº¿ç</div> |
| | | <div class="card-title">设å¤å¨çº¿ç</div> |
| | | <div class="card-icon online-icon"> |
| | | <svg |
| | | width="20" |
| | |
| | | <span class="trend-text">{{ Math.abs(metrics.onlineRateTrend) }}%</span> |
| | | <span class="trend-label">{{ getCompareLabel() }}</span> |
| | | </div> |
| | | <div class="view-details"> |
| | | <span>详æ
</span> |
| | | <svg |
| | | width="12" |
| | | height="12" |
| | | viewBox="0 0 24 24" |
| | | fill="none" |
| | | xmlns="http://www.w3.org/2000/svg" |
| | | > |
| | | <path |
| | | d="M9 18L15 12L9 6" |
| | | stroke="currentColor" |
| | | stroke-width="2" |
| | | stroke-linecap="round" |
| | | stroke-linejoin="round" |
| | | /> |
| | | </svg> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-popover> |
| | | |
| | | <!-- ååå¨è¿è¡æç --> |
| | | <!-- ç¯ä¿¡ç 绿ç ç --> |
| | | <div class="metric-card"> |
| | | <div class="card-header"> |
| | | <div class="card-title">ååå¨è¿è¡æç</div> |
| | | <div class="card-title">ç¯ä¿¡ç 绿ç ç</div> |
| | | <div class="card-icon efficiency-icon"> |
| | | <svg |
| | | width="20" |
| | |
| | | </div> |
| | | |
| | | <!-- å·¡æ¥ç¹æ¬¡ --> |
| | | <div class="metric-card"> |
| | | <div class="card-header"> |
| | | <div class="card-title">å·¡æ¥ç¹æ¬¡</div> |
| | | <div class="card-icon task-icon"> |
| | | <svg |
| | | width="20" |
| | | height="20" |
| | | viewBox="0 0 24 24" |
| | | fill="none" |
| | | xmlns="http://www.w3.org/2000/svg" |
| | | > |
| | | <path |
| | | d="M22 11.08V12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C15.7376 2 19.0503 4.16113 20.7748 7.33007" |
| | | stroke="currentColor" |
| | | stroke-width="2" |
| | | stroke-linecap="round" |
| | | stroke-linejoin="round" |
| | | /> |
| | | <path |
| | | d="M22 4L12 14.01L9 11.01" |
| | | stroke="currentColor" |
| | | stroke-width="2" |
| | | stroke-linecap="round" |
| | | stroke-linejoin="round" |
| | | /> |
| | | </svg> |
| | | </div> |
| | | </div> |
| | | <div class="card-value">{{ metrics.inspectionPoints }}</div> |
| | | <div class="card-trend"> |
| | | <span |
| | | class="trend-arrow" |
| | | :class="{ |
| | | up: metrics.inspectionPointsTrend > 0, |
| | | down: metrics.inspectionPointsTrend < 0, |
| | | }" |
| | | > |
| | | {{ metrics.inspectionPointsTrend > 0 ? 'â' : 'â' }} |
| | | </span> |
| | | <span class="trend-text">{{ Math.abs(metrics.inspectionPointsTrend) }}</span> |
| | | <span class="trend-label">{{ getCompareLabel() }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 主è¦å
å®¹åº --> |
| | | <div class="main-content"> |
| | | <!-- ä¸é¨GISå°å¾åº --> |
| | | <div class="map-section"> |
| | | <div id="map" class="map-container"> |
| | | <BaseMap :showSatellite="true"></BaseMap> |
| | | </div> |
| | | |
| | | <!-- å°å¾ç¹ä½å¼¹çª --> |
| | | <el-dialog v-model="dialogVisible" title="ä¼ä¸å®æ¶æ°æ®" width="400px"> |
| | | <div class="dialog-content"> |
| | | <el-descriptions :column="1" border> |
| | | <el-descriptions-item label="ä¼ä¸åç§°">{{ |
| | | selectedPoint.enterpriseName |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="设å¤ç¼å·">{{ |
| | | selectedPoint.deviceId |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="æ²¹çæµåº¦" |
| | | >{{ selectedPoint.oilSmokeConcentration }} mg/m³</el-descriptions-item |
| | | > |
| | | <el-descriptions-item label="é¢ç²ç©" |
| | | >{{ selectedPoint.particulateMatter }} mg/m³</el-descriptions-item |
| | | > |
| | | <el-descriptions-item label="éç²ç·æ»ç" |
| | | >{{ selectedPoint.nonMethaneHydrocarbon }} mg/m³</el-descriptions-item |
| | | > |
| | | <el-descriptions-item label="çæµæ¶é´">{{ |
| | | selectedPoint.monitoringTime |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="è¶
æ æ
åµ"> |
| | | <el-tag :type="selectedPoint.isOverStandard ? 'danger' : 'success'"> |
| | | {{ selectedPoint.isOverStandard ? 'è¶
æ ' : 'æ£å¸¸' }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="dialogVisible = false">å
³é</el-button> |
| | | <el-button type="primary" @click="viewDetails">æ¥ç详æ
</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </div> |
| | | <!-- å³ä¾§å®æ¶çæµæ»è§åº --> |
| | | <div class="overview-section"> |
| | | <div class="section-header"> |
| | | <h3>设å¤çæ§</h3> |
| | | <!-- <span class="view-more">æ¥çæ´å¤</span> --> |
| | | </div> |
| | | |
| | | <div class="overview-items-container"> |
| | | <div class="overview-item"> |
| | | <div class="overview-label">é¤é¥®åºéºæ»æ°</div> |
| | | <div class="overview-value">{{ overview.totalShops }}</div> |
| | | </div> |
| | | |
| | | <div class="overview-item"> |
| | | <div class="overview-label">å¨çº¿è®¾å¤æ°</div> |
| | | <div class="overview-value">{{ overview.onlineDevices }}</div> |
| | | </div> |
| | | |
| | | <div class="overview-item"> |
| | | <div class="overview-label">ç¦»çº¿è®¾å¤æ°</div> |
| | | <div class="overview-value">{{ overview.offlineDevices }}</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 设å¤ç¶æé¥¼å¾ --> |
| | | <div class="device-status-chart"> |
| | | <canvas id="deviceStatusChart"></canvas> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- å°å¾å¾ä¾ --> |
| | | <div class="map-legend"> |
| | | <div class="legend-header"> |
| | | <h4>å¾ä¾</h4> |
| | | </div> |
| | | <div class="legend-items"> |
| | | <div class="legend-item"> |
| | | <img src="@/assets/exceed.png" alt="æ²¹çæµåº¦è¶
æ " class="legend-icon" /> |
| | | <span class="legend-text">æ²¹çæµåº¦è¶
æ </span> |
| | | </div> |
| | | <div class="legend-item"> |
| | | <img src="@/assets/exception.png" alt="ä¾çµå¼å¸¸" class="legend-icon" /> |
| | | <span class="legend-text">ä¾çµå¼å¸¸</span> |
| | | </div> |
| | | <div class="legend-item"> |
| | | <img src="@/assets/offline.png" alt="è®¾å¤æç½ç»å¼å¸¸" class="legend-icon" /> |
| | | <span class="legend-text">è®¾å¤æç½ç»å¼å¸¸</span> |
| | | </div> |
| | | <div class="legend-item"> |
| | | <img |
| | | src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB4PSI1IiB5PSI4IiB3aWR0aD0iMjIiIGhlaWdodD0iMTYiIHJ4PSIzIiBmaWxsPSIjNTJjNDFhIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNNSA4IFEgMTYgMyAyNyA4IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiMzODllMGQiLz48cGF0aCBkPSJNNSAyNCBRIDE2IDI5IDI3IDI0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiM2NjY2NjYiLz48cmVjdCB4PSI4IiB5PSIxMSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjEwIiByeD0iMiIgZmlsbD0id2hpdGUiLz48cGF0aCBkPSJNMTIgMTQgTCAyMSAxNCIgc3Ryb2tlPSIjNTJjNDFhIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxwYXRoIGQ9Ik0xMiAxNyBMIDE4IDE3IiBzdHJva2U9IiM1MmM0MWEiIHN0cm9rZS13aWR0aD0iMS41Ii8+PHBhdGggZD0iTTEyIDIwIEwgMTUgMjAiIHN0cm9rZT0iIzUyYzQxYSIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48bGluZSB4MT0iMTYiIHkxPSI4IiB4Mj0iMTYiIHkyPSIzIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxjaXJjbGUgY3g9IjE2IiBjeT0iMyIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIvPjxjaXJjbGUgY3g9IjI3IiBjeT0iMTYiIHI9IjMiIGZpbGw9IiNmZmZmZmYiLz48Y2lyY2xlIGN4PSIyNyIgY3k9IjE2IiByPSIxLjUiIGZpbGw9IiM1MmM0MWEiLz48bGluZSB4MT0iNSIgeTE9IjEzIiB4Mj0iNiIgeTI9IjEzIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxsaW5lIHgxPSI1IiB5MT0iMTkiIHgyPSI2IiB5Mj0iMTkiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41Ii8+PC9zdmc+" |
| | | alt="å¨çº¿ç¶æ" |
| | | class="legend-icon" |
| | | /> |
| | | <span class="legend-text">å¨çº¿ç¶æ</span> |
| | | </div> |
| | | <div class="legend-item"> |
| | | <img |
| | | src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB4PSI1IiB5PSI4IiB3aWR0aD0iMjIiIGhlaWdodD0iMTYiIHJ4PSIzIiBmaWxsPSIjOGM4YzhjIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNNSA4IFEgMTYgMyAyNyA4IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiM2NjY2NjYiLz48cGF0aCBkPSJNNSAyNCBRIDE2IDI5IDI3IDI0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiM2NjY2NjYiLz48cmVjdCB4PSI4IiB5PSIxMSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjEwIiByeD0iMiIgZmlsbD0id2hpdGUiLz48bGluZSB4MT0iMTEiIHkxPSIxMiIgeDI9IjIxIiB5Mj0iMjIiIHN0cm9rZT0iIzhjOGM4YyIgc3Ryb2tlLXdpZHRoPSIyIi8+PGxpbmUgeDE9IjExIiB5MT0iMjIiIHgyPSIyMSIgeTI9IjEyIiBzdHJva2U9IiM4YzhjOGMiIHN0cm9rZS13aWR0aD0iMiIvPjxsaW5lIHgxPSIxNiIgeTE9IjgiIHgyPSIxNiIgeTI9IjMiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41Ii8+PGNpcmNsZSBjeD0iMTYiIGN5PSIzIiByPSIxLjUiIGZpbGw9IndoaXRlIi8+PGNpcmNsZSBjeD0iMjciIGN5PSIxNiIgcj0iMyIgZmlsbD0iI2ZmZmZmZiIvPjxjaXJjbGUgY3g9IjI3IiBjeT0iMTYiIHI9IjEuNSIgZmlsbD0iIzhjOGM4YyIvPjxsaW5lIHgxPSI1IiB5MT0iMTMiIHgyPSI2IiB5Mj0iMTMiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41Ii8+PGxpbmUgeDE9IjUiIHkxPSIxOSIgeDI9IjYiIHkyPSIxOSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48L3N2Zz4=" |
| | | alt="ç¦»çº¿ç¶æ" |
| | | class="legend-icon" |
| | | /> |
| | | <span class="legend-text">ç¦»çº¿ç¶æ</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- å·¡æ¥æ
åµç»è®¡å¡ç --> |
| | | <el-scrollbar class="inspection-section"> |
| | | <div class="section-header"> |
| | | <h3>å·¡æ¥æ±æ»</h3> |
| | | </div> |
| | | |
| | | <el-popover placement="right-start" title="ç°åºå·¡æ¥ç»è®¡" width="350" trigger="click"> |
| | | <div class="inspection-popover-content"> |
| | | <!-- å·¡æ¥éç»è®¡ --> |
| | | <div class="inspection-metrics"> |
| | | <div class="inspection-metric-item"> |
| | |
| | | |
| | | <!-- é®é¢æ´æ¹æ
åµ --> |
| | | <div class="inspection-chart-container"> |
| | | <div class="section-header"><h3>æ´æ¹æ±æ»</h3></div> |
| | | <div class="section-header"><h3>é®é¢æ´æ¹</h3></div> |
| | | <canvas id="rectificationChart"></canvas> |
| | | </div> |
| | | |
| | | <!-- é®é¢å®¡æ ¸æ
åµ --> |
| | | <div class="inspection-table-container"> |
| | | <div class="inspection-table-container" style="display: none"> |
| | | <div class="section-header"><h3>å®¡æ ¸æ±æ»</h3></div> |
| | | <div class="inspection-metric-label">é®é¢å®¡æ ¸</div> |
| | | <div class="inspection-table"> |
| | |
| | | {{ inspectionStats.fullyReviewedRectifiedShops }} |
| | | </div> |
| | | </div> |
| | | <!-- <div class="table-row"> |
| | | <div class="table-cell">æ é®é¢åºéºæ°é</div> |
| | | <div class="table-cell value">{{ inspectionStats.noProblemShops }}</div> |
| | | </div> |
| | | <div class="table-row"> |
| | | <div class="table-cell">é®é¢æªå®¡æ ¸åºéºæ°é</div> |
| | | <div class="table-cell value">{{ inspectionStats.unreviewedProblemShops }}</div> |
| | | </div> |
| | | <div class="table-row"> |
| | | <div class="table-cell">é®é¢é¨åå®¡æ ¸åºéºæ°é</div> |
| | | <div class="table-cell value"> |
| | | {{ inspectionStats.partiallyReviewedProblemShops }} |
| | | </div> |
| | | </div> |
| | | <div class="table-row"> |
| | | <div class="table-cell">é®é¢å
¨é¨å®¡æ ¸åºéºæ°é</div> |
| | | <div class="table-cell value">{{ inspectionStats.fullyReviewedProblemShops }}</div> |
| | | </div> |
| | | <div class="table-row"> |
| | | <div class="table-cell">æªæ´æ¹åºéºæ°</div> |
| | | <div class="table-cell value">{{ inspectionStats.unrectifiedShops }}</div> |
| | | </div> |
| | | <div class="table-row"> |
| | | <div class="table-cell">æ´æ¹æªå®¡æ ¸åºéºæ°</div> |
| | | <div class="table-cell value">{{ inspectionStats.unreviewedRectifiedShops }}</div> |
| | | </div> |
| | | <div class="table-row"> |
| | | <div class="table-cell">æ´æ¹é¨åå®¡æ ¸åºéºæ°</div> |
| | | <div class="table-cell value"> |
| | | {{ inspectionStats.partiallyReviewedRectifiedShops }} |
| | | <template #reference> |
| | | <div class="metric-card"> |
| | | <div class="card-header"> |
| | | <div class="card-title">å·¡æ¥ç¹æ¬¡</div> |
| | | <div class="card-icon task-icon"> |
| | | <svg |
| | | width="20" |
| | | height="20" |
| | | viewBox="0 0 24 24" |
| | | fill="none" |
| | | xmlns="http://www.w3.org/2000/svg" |
| | | > |
| | | <path |
| | | d="M22 11.08V12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C15.7376 2 19.0503 4.16113 20.7748 7.33007" |
| | | stroke="currentColor" |
| | | stroke-width="2" |
| | | stroke-linecap="round" |
| | | stroke-linejoin="round" |
| | | /> |
| | | <path |
| | | d="M22 4L12 14.01L9 11.01" |
| | | stroke="currentColor" |
| | | stroke-width="2" |
| | | stroke-linecap="round" |
| | | stroke-linejoin="round" |
| | | /> |
| | | </svg> |
| | | </div> |
| | | </div> |
| | | <div class="table-row"> |
| | | <div class="table-cell">æ´æ¹å
¨é¨å®¡æ ¸åºéºæ°</div> |
| | | <div class="table-cell value">{{ inspectionStats.fullyReviewedRectifiedShops }}</div> |
| | | </div> --> |
| | | <div class="card-value">{{ metrics.inspectionPoints }}</div> |
| | | <div class="card-trend"> |
| | | <span |
| | | class="trend-arrow" |
| | | :class="{ |
| | | up: metrics.inspectionPointsTrend > 0, |
| | | down: metrics.inspectionPointsTrend < 0, |
| | | }" |
| | | > |
| | | {{ metrics.inspectionPointsTrend > 0 ? 'â' : 'â' }} |
| | | </span> |
| | | <span class="trend-text">{{ Math.abs(metrics.inspectionPointsTrend) }}</span> |
| | | <span class="trend-label">{{ getCompareLabel() }}</span> |
| | | </div> |
| | | <div class="view-details"> |
| | | <span>详æ
</span> |
| | | <svg |
| | | width="12" |
| | | height="12" |
| | | viewBox="0 0 24 24" |
| | | fill="none" |
| | | xmlns="http://www.w3.org/2000/svg" |
| | | > |
| | | <path |
| | | d="M9 18L15 12L9 6" |
| | | stroke="currentColor" |
| | | stroke-width="2" |
| | | stroke-linecap="round" |
| | | stroke-linejoin="round" |
| | | /> |
| | | </svg> |
| | | </div> |
| | | </div> |
| | | </el-scrollbar> |
| | | </template> |
| | | </el-popover> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 主è¦å
å®¹åº --> |
| | | <div class="main-content"> |
| | | <!-- ä¸é¨GISå°å¾åº --> |
| | | <div class="map-section"> |
| | | <div id="map" class="map-container"> |
| | | <BaseMap :showSatellite="true"></BaseMap> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="monitor-control-container"> |
| | | <el-button size="large" @click="toggleMonitorControl" class="push-btn"> |
| | | <div style="display: flex; flex-direction: column"> |
| | | <el-icon> |
| | | <ArrowRight v-if="isMonitorControlExpanded" /> |
| | | <ArrowLeft v-else /> |
| | | </el-icon> |
| | | <div>ç°</div> |
| | | <div>åº</div> |
| | | <div>å·¡</div> |
| | | <div>æ¥</div> |
| | | </div> |
| | | </el-button> |
| | | <MonitorControl |
| | | v-if="isMonitorControlExpanded" |
| | | :class="{ 'monitor-control': true, collapsed: !isMonitorControlExpanded }" |
| | | style="height: calc(90vh - 40px)" |
| | | /> |
| | | </div> |
| | | |
| | | <!-- å°å¾å¾ä¾ --> |
| | | <div class="map-legend"> |
| | | <div class="legend-header"> |
| | | <h4>å¾ä¾</h4> |
| | | </div> |
| | | <div class="legend-items"> |
| | | <div class="legend-item"> |
| | | <img src="@/assets/exceed.png" alt="æ²¹çæµåº¦è¶
æ " class="legend-icon" /> |
| | | <span class="legend-text">æ²¹çæµåº¦è¶
æ </span> |
| | | </div> |
| | | <div class="legend-item"> |
| | | <img src="@/assets/exception.png" alt="ä¾çµå¼å¸¸" class="legend-icon" /> |
| | | <span class="legend-text">ä¾çµå¼å¸¸</span> |
| | | </div> |
| | | <div class="legend-item"> |
| | | <img src="@/assets/offline.png" alt="è®¾å¤æç½ç»å¼å¸¸" class="legend-icon" /> |
| | | <span class="legend-text">è®¾å¤æç½ç»å¼å¸¸</span> |
| | | </div> |
| | | <div class="legend-item"> |
| | | <img |
| | | src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB4PSI1IiB5PSI4IiB3aWR0aD0iMjIiIGhlaWdodD0iMTYiIHJ4PSIzIiBmaWxsPSIjNTJjNDFhIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNNSA4IFEgMTYgMyAyNyA4IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiMzODllMGQiLz48cGF0aCBkPSJNNSAyNCBRIDE2IDI5IDI3IDI0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiM2NjY2NjYiLz48cmVjdCB4PSI4IiB5PSIxMSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjEwIiByeD0iMiIgZmlsbD0id2hpdGUiLz48cGF0aCBkPSJNMTIgMTQgTCAyMSAxNCIgc3Ryb2tlPSIjNTJjNDFhIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxwYXRoIGQ9Ik0xMiAxNyBMIDE4IDE3IiBzdHJva2U9IiM1MmM0MWEiIHN0cm9rZS13aWR0aD0iMS41Ii8+PHBhdGggZD0iTTEyIDIwIEwgMTUgMjAiIHN0cm9rZT0iIzUyYzQxYSIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48bGluZSB4MT0iMTYiIHkxPSI4IiB4Mj0iMTYiIHkyPSIzIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxjaXJjbGUgY3g9IjE2IiBjeT0iMyIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIvPjxjaXJjbGUgY3g9IjI3IiBjeT0iMTYiIHI9IjMiIGZpbGw9IiNmZmZmZmYiLz48Y2lyY2xlIGN4PSIyNyIgY3k9IjE2IiByPSIxLjUiIGZpbGw9IiM1MmM0MWEiLz48bGluZSB4MT0iNSIgeTE9IjEzIiB4Mj0iNiIgeTI9IjEzIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuNSIvPjxsaW5lIHgxPSI1IiB5MT0iMTkiIHgyPSI2IiB5Mj0iMTkiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41Ii8+PC9zdmc+" |
| | | alt="å¨çº¿ç¶æ" |
| | | class="legend-icon" |
| | | /> |
| | | <span class="legend-text">å¨çº¿ç¶æ</span> |
| | | </div> |
| | | <div class="legend-item"> |
| | | <img |
| | | src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIHZpZXdCb3g9IjAgMCAzMiAzMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB4PSI1IiB5PSI4IiB3aWR0aD0iMjIiIGhlaWdodD0iMTYiIHJ4PSIzIiBmaWxsPSIjOGM4YzhjIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiLz48cGF0aCBkPSJNNSA4IFEgMTYgMyAyNyA4IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiM2NjY2NjYiLz48cGF0aCBkPSJNNSAyNCBRIDE2IDI5IDI3IDI0IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9IiM2NjY2NjYiLz48cmVjdCB4PSI4IiB5PSIxMSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjEwIiByeD0iMiIgZmlsbD0id2hpdGUiLz48bGluZSB4MT0iMTEiIHkxPSIxMiIgeDI9IjIxIiB5Mj0iMjIiIHN0cm9rZT0iIzhjOGM4YyIgc3Ryb2tlLXdpZHRoPSIyIi8+PGxpbmUgeDE9IjExIiB5MT0iMjIiIHgyPSIyMSIgeTI9IjEyIiBzdHJva2U9IiM4YzhjOGMiIHN0cm9rZS13aWR0aD0iMiIvPjxsaW5lIHgxPSIxNiIgeTE9IjgiIHgyPSIxNiIgeTI9IjMiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41Ii8+PGNpcmNsZSBjeD0iMTYiIGN5PSIzIiByPSIxLjUiIGZpbGw9IndoaXRlIi8+PGNpcmNsZSBjeD0iMjciIGN5PSIxNiIgcj0iMyIgZmlsbD0iI2ZmZmZmZiIvPjxjaXJjbGUgY3g9IjI3IiBjeT0iMTYiIHI9IjEuNSIgZmlsbD0iIzhjOGM4YyIvPjxsaW5lIHgxPSI1IiB5MT0iMTMiIHgyPSI2IiB5Mj0iMTMiIHN0cm9rZT0id2hpdGUiIHN0cm9rZS13aWR0aD0iMS41Ii8+PGxpbmUgeDE9IjUiIHkxPSIxOSIgeDI9IjYiIHkyPSIxOSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIxLjUiLz48L3N2Zz4=" |
| | | alt="ç¦»çº¿ç¶æ" |
| | | class="legend-icon" |
| | | /> |
| | | <span class="legend-text">ç¦»çº¿ç¶æ</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import districtSearch from '@/utils/map/districtsearch.js' |
| | | import marks from '@/utils/map/marks.js' |
| | | import { generateTestShops } from '@/debug/debugdata' |
| | | import MonitorControl from '@/views/inspection/MonitorControl.vue' |
| | | |
| | | export default { |
| | | name: 'DataDashboard', |
| | | components: { |
| | | MonitorControl, |
| | | }, |
| | | data() { |
| | | return { |
| | | activeTime: 'day', |
| | | currentDate: new Date(), |
| | | currentDate: new Date('2023-08-01'), |
| | | timeTabs: [ |
| | | { label: 'æ¥', value: 'day' }, |
| | | { label: 'å¨', value: 'week' }, |
| | | { label: 'æ', value: 'month' }, |
| | | ], |
| | | dialogVisible: false, |
| | | selectedPoint: { |
| | | enterpriseName: '', |
| | | deviceId: '', |
| | |
| | | overStandardTrend: 5, |
| | | onlineRate: 92, |
| | | onlineRateTrend: 2, |
| | | purifierEfficiency: 85, |
| | | purifierEfficiencyTrend: -3, |
| | | purifierEfficiency: 95, |
| | | purifierEfficiencyTrend: 2, |
| | | inspectionPoints: 350, |
| | | inspectionPointsTrend: 50, |
| | | }, |
| | |
| | | }, |
| | | map: null, |
| | | refreshTimer: null, |
| | | isMonitorControlExpanded: true, |
| | | } |
| | | }, |
| | | computed: { |
| | | currentTimeDisplay() { |
| | | const date = this.currentDate |
| | | let weekStart = new Date(date) |
| | | let weekEnd = new Date(date) |
| | | switch (this.activeTime) { |
| | | case 'day': |
| | | return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}` |
| | | case 'week': |
| | | // ç®å计ç®å¨æ¾ç¤ºï¼å®é
项ç®ä¸å¯è½éè¦æ´å¤æçå¨è®¡ç®é»è¾ |
| | | let weekStart = new Date(date) |
| | | weekStart.setDate(date.getDate() - date.getDay() + 1) |
| | | let weekEnd = new Date(date) |
| | | weekEnd.setDate(date.getDate() + (7 - date.getDay())) |
| | | return `${weekStart.getFullYear()}-${String(weekStart.getMonth() + 1).padStart(2, '0')}-${String(weekStart.getDate()).padStart(2, '0')} ~ ${weekEnd.getFullYear()}-${String(weekEnd.getMonth() + 1).padStart(2, '0')}-${String(weekEnd.getDate()).padStart(2, '0')}` |
| | | case 'month': |
| | |
| | | this.initMap() |
| | | this.initDeviceStatusChart() |
| | | this.initRectificationChart() |
| | | this.startAutoRefresh() |
| | | // this.startAutoRefresh() |
| | | }, |
| | | beforeUnmount() { |
| | | if (this.refreshTimer) { |
| | |
| | | } |
| | | }, |
| | | methods: { |
| | | toggleMonitorControl() { |
| | | this.isMonitorControlExpanded = !this.isMonitorControlExpanded |
| | | }, |
| | | handleTimeChange(tab) { |
| | | this.activeTime = tab.value |
| | | // 模æåæ¢æ¶é´å¨æåçæ°æ®æ´æ° |
| | |
| | | // è¿éåºè¯¥æ ¹æ®éæ©çæ¶é´å¨æä»æ¥å£è·åæ°æ® |
| | | // æ¨¡ææ°æ®æ´æ° |
| | | setTimeout(() => { |
| | | const m = Math.floor(Math.random() * 50) + 150 |
| | | this.overview = { |
| | | totalShops: 245, |
| | | onlineDevices: m, |
| | | offlineDevices: 245 - m, |
| | | } |
| | | this.metrics = { |
| | | overStandardCount: Math.floor(Math.random() * 30), |
| | | overStandardTrend: Math.floor(Math.random() * 20) - 10, |
| | | onlineRate: Math.floor(Math.random() * 20) + 80, |
| | | onlineRate: ((this.overview.onlineDevices / this.overview.totalShops) * 100).toFixed(0), |
| | | onlineRateTrend: Math.floor(Math.random() * 10) - 5, |
| | | purifierEfficiency: Math.floor(Math.random() * 30) + 70, |
| | | purifierEfficiency: Math.floor(Math.random() * 20) + 80, |
| | | purifierEfficiencyTrend: Math.floor(Math.random() * 10) - 5, |
| | | inspectionPoints: Math.floor(Math.random() * 100) + 300, |
| | | inspectionPointsTrend: Math.floor(Math.random() * 100) - 50, |
| | |
| | | } |
| | | |
| | | // æ´æ°å¾è¡¨ |
| | | this.initDeviceStatusChart() |
| | | this.initRectificationChart() |
| | | }, 300) |
| | | }, |
| | |
| | | left: '3%', |
| | | right: '4%', |
| | | bottom: '3%', |
| | | top: '5%', |
| | | containLabel: true, |
| | | }, |
| | | xAxis: { |
| | |
| | | /* padding: 16px; */ |
| | | border-radius: 8px; |
| | | /* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */ |
| | | } |
| | | |
| | | /* çæ§æ§å¶å¡ç */ |
| | | .monitor-control { |
| | | /* position: absolute; */ |
| | | width: 500px; |
| | | transition: all 0.3s ease; |
| | | /* top: 0px; */ |
| | | /* right: 0px; */ |
| | | /* z-index: 10; */ |
| | | } |
| | | |
| | | .push-btn { |
| | | z-index: 1; |
| | | width: 2.5rem; |
| | | height: initial; |
| | | margin: initial; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | /* background-color: white; */ |
| | | /* border-color: white; */ |
| | | /* border-top: 1px solid; |
| | | border-left: 1px solid; |
| | | border-bottom: 1px solid; */ |
| | | border-top-right-radius: 0px; |
| | | border-bottom-right-radius: 0px; |
| | | /* box-shadow: var(--el-box-shadow-light); */ |
| | | } |
| | | |
| | | /* æ¶é´å¨æå¡ç */ |
| | |
| | | .metric-card:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .card-header { |
| | |
| | | } |
| | | |
| | | .efficiency-icon { |
| | | color: #722ed1; |
| | | color: #52c41a; |
| | | } |
| | | |
| | | .task-icon { |
| | |
| | | } |
| | | |
| | | /* å³ä¾§å®æ¶çæµæ»è§åº */ |
| | | .overview-section { |
| | | position: absolute; |
| | | bottom: 4px; |
| | | left: 4px; |
| | | width: 320px; |
| | | background-color: rgba(255, 255, 255, 0.9); |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | padding: 20px; |
| | | .popover-content { |
| | | padding: 10px; |
| | | } |
| | | |
| | | .overview-items-container { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding-bottom: 16px; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .overview-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | z-index: 10; |
| | | max-height: calc(100vh - 220px); |
| | | align-items: center; |
| | | flex: 1; |
| | | text-align: center; |
| | | } |
| | | |
| | | .overview-label { |
| | | font-size: 12px; |
| | | color: #86909c; |
| | | font-weight: 500; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .overview-value { |
| | | font-size: 24px; |
| | | font-weight: bold; |
| | | color: #262626; |
| | | } |
| | | |
| | | .device-status-chart { |
| | | flex: 1; |
| | | min-height: 200px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .view-details { |
| | | position: absolute; |
| | | bottom: 12px; |
| | | right: 16px; |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 4px; |
| | | font-size: 12px; |
| | | color: #1890ff; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .view-details:hover { |
| | | text-decoration: underline; |
| | | } |
| | | |
| | | .overview-items-container { |
| | |
| | | color: #262626; |
| | | } |
| | | |
| | | .device-status-chart { |
| | | flex: 1; |
| | | min-height: 100px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | margin-bottom: 16px; |
| | | /* å·¡æ¥æ
åµç»è®¡ */ |
| | | .inspection-popover-content { |
| | | padding: 10px; |
| | | max-height: 400px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | /* å·¡æ¥æ
åµç»è®¡ */ |
| | | .inspection-section { |
| | | .monitor-control-container { |
| | | position: absolute; |
| | | top: 4px; |
| | | right: 4px; |
| | | width: 320px; |
| | | background-color: rgba(255, 255, 255, 0.9); |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | padding: 20px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | z-index: 10; |
| | | max-height: calc(70vh); |
| | | border-top: 1px solid #f0f0f0; |
| | | transition: all 0.3s ease; |
| | | /* background-color: rgba(255, 255, 255, 0.9); */ |
| | | display: flex; |
| | | border-radius: 8px; |
| | | /* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */ |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .monitor-control-container.collapsed { |
| | | width: 60px; |
| | | } |
| | | |
| | | .monitor-control-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 10px 15px; |
| | | border-bottom: 1px solid #e8e8e8; |
| | | height: 40px; |
| | | position: relative; |
| | | } |
| | | |
| | | .monitor-control-header span { |
| | | font-weight: 600; |
| | | color: #333; |
| | | writing-mode: vertical-rl; |
| | | text-orientation: mixed; |
| | | letter-spacing: 2px; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .collapse-btn { |
| | | /* transform: translateY(-50%); */ |
| | | } |
| | | |
| | | .inspection-metrics { |
| | |
| | | .map-legend { |
| | | position: absolute; |
| | | bottom: 4px; |
| | | right: 4px; |
| | | width: 200px; |
| | | left: 4px; |
| | | /* width: 200px; */ |
| | | background-color: rgba(255, 255, 255, 0.9); |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | padding: 16px; |
| | | padding: 4px; |
| | | z-index: 10; |
| | | } |
| | | |
| | |
| | | <div class="data-exception-container"> |
| | | <!-- æç´¢åºå --> |
| | | <div ref="h1" class="search-container"> |
| | | <!-- <div class="search-header"> |
| | | <h3>æ¥è¯¢è¡¨æ ¼</h3> |
| | | </div> --> |
| | | <el-collapse v-model="activeSearchNames" @change="handleSearchCollapseChange"> |
| | | <el-collapse-item name="1"> |
| | | <template #title> |
| | | <div class="search-header"> |
| | | <h3>æ¥è¯¢æ¡ä»¶</h3> |
| | | <span v-if="!isSearchExpanded" class="search-summary"> |
| | | {{ getSearchSummary() }} |
| | | </span> |
| | | </div> |
| | | </template> |
| | | <el-row> |
| | | <div class="search-form"> |
| | | <div class="form-row"> |
| | |
| | | <div class="summary-info"> |
| | | <span>{{ beginTime }} ââ {{ endTime }} æ²¹ççæµå¼å¸¸ä¿¡æ¯æ±æ»</span> |
| | | </div> |
| | | </el-collapse-item> |
| | | </el-collapse> |
| | | </div> |
| | | |
| | | <!-- å¼å¸¸åæ --> |
| | | <div class="analysis-container"> |
| | | <el-collapse v-model="activeNames"> |
| | | <el-collapse-item name="1"> |
| | | <template #title> |
| | | <div class="collapse-title"> |
| | | <h4 class="collapse-header">å¼å¸¸åæ</h4> |
| | | <el-space> |
| | | <h4 class="collapse-header">çæµé¢è¦</h4> |
| | | <el-icon class="header-icon"> |
| | | <i-ep-info-filled /> |
| | | </el-icon> |
| | | </div> |
| | | </template> |
| | | </el-space> |
| | | <el-card class="analysis-card"> |
| | | <el-row :gutter="24"> |
| | | <el-col :span="8"> |
| | |
| | | </div> |
| | | <hr class="item-divider" /> |
| | | <div class="item-shops"> |
| | | <el-scrollbar max-height="80px"> |
| | | <el-scrollbar :height="scrollbarHeight"> |
| | | <el-space wrap> |
| | | <ExceptionText |
| | | v-for="(item, index) in exception0" |
| | | :key="item" |
| | |
| | | @submit-exception-data="getAbnormalDataByClick" |
| | | > |
| | | {{ item.diName }} |
| | | <span v-if="index < exception0.length - 1" class="text-blank">,</span> |
| | | <span v-if="index < exception0.length - 1" class="text-blank"></span> |
| | | </ExceptionText> |
| | | </el-space> |
| | | </el-scrollbar> |
| | | </div> |
| | | </div> |
| | |
| | | </div> |
| | | <hr class="item-divider" /> |
| | | <div class="item-shops"> |
| | | <el-scrollbar max-height="80px"> |
| | | <el-scrollbar :height="scrollbarHeight"> |
| | | <el-space wrap> |
| | | <ExceptionText |
| | | v-for="(item, index) in exception1" |
| | | :key="item" |
| | |
| | | @submit-exception-data="getAbnormalDataByClick" |
| | | > |
| | | {{ item.diName }} |
| | | <span v-if="index < exception1.length - 1" class="text-blank">,</span> |
| | | <span v-if="index < exception1.length - 1" class="text-blank"></span> |
| | | </ExceptionText> |
| | | </el-space> |
| | | </el-scrollbar> |
| | | </div> |
| | | </div> |
| | |
| | | </div> |
| | | <hr class="item-divider" /> |
| | | <div class="item-shops"> |
| | | <el-scrollbar max-height="80px"> |
| | | <el-scrollbar :height="scrollbarHeight"> |
| | | <el-space wrap> |
| | | <ExceptionText |
| | | v-for="(item, index) in exception2" |
| | | :key="item" |
| | |
| | | @submit-exception-data="getAbnormalDataByClick" |
| | | > |
| | | {{ item.diName }} |
| | | <span v-if="index < exception2.length - 1" class="text-blank">,</span> |
| | | <span v-if="index < exception2.length - 1" class="text-blank"></span> |
| | | </ExceptionText> |
| | | </el-space> |
| | | </el-scrollbar> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </el-card> |
| | | </el-collapse-item> |
| | | </el-collapse> |
| | | </div> |
| | | |
| | | <!-- å¼å¸¸æ°æ®è¡¨æ ¼ --> |
| | | <div class="table-container"> |
| | | <el-collapse v-model="activeNames"> |
| | | <el-collapse-item name="1"> |
| | | <template #title> |
| | | <div class="collapse-title"> |
| | | <h4 class="table-title">å¼å¸¸æ°æ®</h4> |
| | | </div> |
| | | </template> |
| | | <el-card v-show="!isNoData"> |
| | | <el-table |
| | | ref="tableH" |
| | |
| | | :data="displayData" |
| | | style="width: 100%" |
| | | border |
| | | :height="tableHeight" |
| | | :cell-class-name="tableCellClassName" |
| | | :show-overflow-tooltip="true" |
| | | > |
| | | <el-table-column prop="diName" label="åºéºåç§°" align="center"> |
| | | <template #default="{ row }"> |
| | | <!-- <template #default="{ row }"> |
| | | <el-tooltip effect="dark" :content="row.diName"> |
| | | <div class="cell ellipsis">{{ row.diName }}</div> |
| | | </el-tooltip> |
| | | </template> |
| | | </template> --> |
| | | </el-table-column> |
| | | <el-table-column prop="devId" label="设å¤ç¼å·" align="center"> |
| | | <template #default="{ row }"> |
| | | <!-- <template #default="{ row }"> |
| | | <el-tooltip effect="dark" :content="row.devId"> |
| | | <div class="cell ellipsis">{{ row.devId }}</div> |
| | | </el-tooltip> |
| | | </template> |
| | | </template> --> |
| | | </el-table-column> |
| | | <el-table-column prop="diSupplier" label="ä¾åºå" align="center"> |
| | | <template #default="{ row }"> |
| | | <!-- <template #default="{ row }"> |
| | | <el-tooltip effect="dark" :content="row.diSupplier"> |
| | | <div class="cell ellipsis">{{ row.diSupplier }}</div> |
| | | </el-tooltip> |
| | | </template> |
| | | </template> --> |
| | | </el-table-column> |
| | | <el-table-column prop="exception" label="å¼å¸¸åç±»" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-table-column prop="exception" label="å¼å¸¸åç±»" align="center" width="90"> |
| | | <!-- <template #default="{ row }"> |
| | | <el-tooltip effect="dark" :content="row.exception"> |
| | | <div class="cell ellipsis">{{ row.exception }}</div> |
| | | </el-tooltip> |
| | | </template> |
| | | </template> --> |
| | | </el-table-column> |
| | | <el-table-column label="å¼å¸¸ç±»å" align="center"> |
| | | <el-table-column label="å¼å¸¸ç±»å" align="center" width="120"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.exceptionType == '0'">æ²¹çæ°æ®è¶
æ </span> |
| | | <span v-else-if="row.exceptionType == '1'">çä¼¼ä¾çµå¼å¸¸</span> |
| | | <span v-else-if="row.exceptionType == '2'">æçº¿</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="region" label="å°åº" align="center"> |
| | | <template #default="{ row }"> |
| | | <el-table-column prop="region" label="å°åº" align="center" width="80"> |
| | | <!-- <template #default="{ row }"> |
| | | <el-tooltip effect="dark" :content="row.region"> |
| | | <div class="cell ellipsis">{{ row.region }}</div> |
| | | </el-tooltip> |
| | | </template> |
| | | </template> --> |
| | | </el-table-column> |
| | | <el-table-column prop="beginTime" label="å¼å§æ¶é´" align="center"> |
| | | <template #default="{ row }"> |
| | | <!-- <template #default="{ row }"> |
| | | <el-tooltip effect="dark" :content="row.beginTime"> |
| | | <div class="cell ellipsis">{{ row.beginTime }}</div> |
| | | </el-tooltip> |
| | | </template> |
| | | </template> --> |
| | | </el-table-column> |
| | | <el-table-column prop="endTime" label="ç»ææ¶é´" align="center"> |
| | | <template #default="{ row }"> |
| | | <!-- <template #default="{ row }"> |
| | | <el-tooltip effect="dark" :content="row.endTime"> |
| | | <div class="cell ellipsis">{{ row.endTime }}</div> |
| | | </el-tooltip> |
| | | </template> |
| | | </template> --> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" align="center" width="120"> |
| | | <template #default="{ row }"> |
| | |
| | | </div> |
| | | </el-card> |
| | | <el-empty v-show="isNoData" :image-size="200" /> |
| | | </el-collapse-item> |
| | | </el-collapse> |
| | | </div> |
| | | |
| | | <!-- å¯¹è¯æ¡ --> |
| | |
| | | selectedRowIndex: -1, |
| | | |
| | | // é»è®¤éæ©çæå 颿¿ç¼å· |
| | | activeNames: ['1'], |
| | | activeNames: [], |
| | | // æç´¢åºåæå ç¶æ |
| | | activeSearchNames: [], |
| | | // æç´¢åºåæ¯å¦å±å¼ |
| | | isSearchExpanded: false, |
| | | // å¼å¸¸æ¶çè¡¨æ ¼ |
| | | abnormalTb: [], |
| | | // å¼å¸¸çèµ·æ¢æ¶é´ |
| | |
| | | exception1: [], |
| | | // ä¿åçå¼å¸¸ç±»å2对åºçåºéºåç§°å设å¤ç¼å· |
| | | exception2: [], |
| | | // å¼å¸¸åºéºæ»å¨åºåé«åº¦ |
| | | scrollbarHeight: 250, |
| | | // å è½½å¨ç» |
| | | loading: false, |
| | | // æ½å±å è½½å¨ç» |
| | |
| | | this.isNextCantouch = false |
| | | } |
| | | }, |
| | | |
| | | // å½éæ©çæ¶é´åçååæ¶ï¼å¼å¸¸åæé¨åçå¼å¸¸åºéºæ°é忥åå |
| | | beginTime() { |
| | | this.getShopNames() |
| | |
| | | window.addEventListener('resize', this.updateChart) |
| | | }, |
| | | methods: { |
| | | // å¤çæç´¢åºåæå åå |
| | | handleSearchCollapseChange(val) { |
| | | this.isSearchExpanded = val.length > 0 |
| | | }, |
| | | // è·åæç´¢æ¡ä»¶æè¦ |
| | | getSearchSummary() { |
| | | let summary = |
| | | 'æ¶é´: ' + |
| | | (this.beginTime ? this.beginTime.substring(0, 10) : 'å
¨é¨') + |
| | | ' è³ ' + |
| | | (this.endTime ? this.endTime.substring(0, 10) : 'å
¨é¨') |
| | | summary += |
| | | ' | åºéº: ' + |
| | | (this.deviceId[1] |
| | | ? this.deviceInfo.find((item) => item.diCode === this.deviceId[1])?.diName || '已鿩' |
| | | : 'å
¨é¨') |
| | | if (this.exceptionValue && this.exceptionValue.length > 0) { |
| | | const exceptionTypes = { |
| | | 0: 'æ²¹çæµåº¦è¶
æ ', |
| | | 1: 'ä¾çµå¼å¸¸', |
| | | 2: 'è®¾å¤æç½ç»å¼å¸¸', |
| | | } |
| | | const selectedTypes = this.exceptionValue |
| | | .map((val) => exceptionTypes[val] || val) |
| | | .join(', ') |
| | | summary += ' | å¼å¸¸ç±»å: ' + selectedTypes |
| | | } else { |
| | | summary += ' | å¼å¸¸ç±»å: å
¨é¨' |
| | | } |
| | | return summary |
| | | }, |
| | | // åè½ï¼å¯¹è¯æ¡è¡¨æ ¼åºå·éå¢ |
| | | // æ¶é´ï¼2023-8-17 |
| | | indexMethod(index) { |
| | |
| | | // ç§»é¤ç©ºæ°æ®ç¶æ |
| | | this.isNoData = false |
| | | this.handleCurrentChange(1) |
| | | // ç¹å»æ¥è¯¢åæå æç´¢åºå |
| | | this.activeSearchNames = [] |
| | | this.isSearchExpanded = false |
| | | }) |
| | | }, |
| | | handleSizeChange(val) { |
| | |
| | | this.total = this.abnormalData.length |
| | | // é»è®¤æ¾ç¤ºç¬¬ä¸é¡µ |
| | | this.handleCurrentChange(1) |
| | | this.activeNames = ['1'] |
| | | // æ»å¨å°å¼å¸¸æ°æ®è¡¨æ ¼ä½ç½® |
| | | this.$nextTick(() => { |
| | | if (this.$refs.tableH) { |
| | | setTimeout(() => { |
| | | this.$refs.tableH.$el.scrollIntoView({ behavior: 'smooth' }) |
| | | }, 200) |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // æ ¹æ®å¼å¸¸ç±»åè¿ååºéºåç§°å设å¤ç¼å· |
| | |
| | | } |
| | | |
| | | .search-header { |
| | | margin-bottom: 20px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | width: 100%; |
| | | } |
| | | |
| | | .search-header h3 { |
| | |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .search-summary { |
| | | font-size: 14px; |
| | | color: #666; |
| | | flex: 1; |
| | | margin-left: 20px; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | } |
| | | |
| | | .search-form { |
| | |
| | | } |
| | | |
| | | .analysis-item { |
| | | height: 180px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |