From b1a0d701cf898c8b7812e66a808a1c91f2bae6cc Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期二, 17 三月 2026 16:44:11 +0800
Subject: [PATCH] 2026.3.17
---
src/views/inspection/MonitorControl.vue | 1418 ++++++++++++++++++++++++
src/api/fytz/creditApi.js | 32
src/components/search-option/CompQuickSet.vue | 122 +-
src/components/core/AppHeader.vue | 1
src/views/LoginPage.vue | 160 ++
components.d.ts | 42
public/徐汇区餐饮监管简报(天钥桥)-2023年8月(1).docx | 0
src/assets/loginPageBg.png | 0
src/components/bg-task/FYBgTaskItem.vue | 218 +++
public/徐汇区餐饮监管简报(天钥桥)-2023年8月(1).doc | 0
src/constants/menu.js | 2
src/router/index.js | 2
src/components/bg-task/FYBgTaskDialog.vue | 31
src/views/analysis/huanxincode/HuanxinCodeManage.vue | 552 +++++++--
src/components/bg-task/FYBgTaskCard.vue | 175 +++
/dev/null | 185 ---
src/stores/bgtaskStore.js | 36
src/views/inspection/report/ReportManage.vue | 180 +++
src/components/SearchBar.vue | 74
src/views/system/SystemManage.vue | 102 -
src/views/analysis/evalution/components/precheck/CompPreCheck.vue | 46
21 files changed, 2,793 insertions(+), 585 deletions(-)
diff --git a/components.d.ts b/components.d.ts
index 3884445..9882412 100644
--- a/components.d.ts
+++ b/components.d.ts
@@ -21,18 +21,14 @@
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']
- ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
- 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']
- ElCheckboxButton: typeof import('element-plus/es')['ElCheckboxButton']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
@@ -45,9 +41,6 @@
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
- ElDropdown: typeof import('element-plus/es')['ElDropdown']
- ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
- ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
@@ -57,23 +50,17 @@
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']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
ElOption: typeof import('element-plus/es')['ElOption']
- ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
- 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']
@@ -88,6 +75,9 @@
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']
+ FYBgTaskItem: typeof import('./src/components/bg-task/FYBgTaskItem.vue')['default']
FYDownloadTableButton: typeof import('./src/components/button/FYDownloadTableButton.vue')['default']
FYForm: typeof import('./src/components/form/FYForm.vue')['default']
FYImageSelectDialog: typeof import('./src/components/FYImageSelectDialog.vue')['default']
@@ -110,11 +100,6 @@
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']
- IEpDataLine: typeof import('~icons/ep/data-line')['default']
- IEpDownload: typeof import('~icons/ep/download')['default']
- IEpGrid: typeof import('~icons/ep/grid')['default']
- IEpInfoFilled: typeof import('~icons/ep/info-filled')['default']
- IEpWarning: typeof import('~icons/ep/warning')['default']
ItemDevice: typeof import('./src/components/list-item/ItemDevice.vue')['default']
ItemMonitorObj: typeof import('./src/components/list-item/ItemMonitorObj.vue')['default']
ItemScene: typeof import('./src/components/list-item/ItemScene.vue')['default']
@@ -146,18 +131,14 @@
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 ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
- 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 ElCheckboxButton: typeof import('element-plus/es')['ElCheckboxButton']
const ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
const ElCol: typeof import('element-plus/es')['ElCol']
const ElCollapse: typeof import('element-plus/es')['ElCollapse']
@@ -170,9 +151,6 @@
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 ElDropdown: typeof import('element-plus/es')['ElDropdown']
- const ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
- const ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
const ElEmpty: typeof import('element-plus/es')['ElEmpty']
const ElForm: typeof import('element-plus/es')['ElForm']
const ElFormItem: typeof import('element-plus/es')['ElFormItem']
@@ -182,23 +160,17 @@
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 ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
const ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
const ElOption: typeof import('element-plus/es')['ElOption']
- const ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
const ElPagination: typeof import('element-plus/es')['ElPagination']
const ElPopover: typeof import('element-plus/es')['ElPopover']
- 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']
@@ -213,6 +185,9 @@
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 FYBgTaskItem: typeof import('./src/components/bg-task/FYBgTaskItem.vue')['default']
const FYDownloadTableButton: typeof import('./src/components/button/FYDownloadTableButton.vue')['default']
const FYForm: typeof import('./src/components/form/FYForm.vue')['default']
const FYImageSelectDialog: typeof import('./src/components/FYImageSelectDialog.vue')['default']
@@ -235,11 +210,6 @@
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 IEpDataLine: typeof import('~icons/ep/data-line')['default']
- const IEpDownload: typeof import('~icons/ep/download')['default']
- const IEpGrid: typeof import('~icons/ep/grid')['default']
- const IEpInfoFilled: typeof import('~icons/ep/info-filled')['default']
- const IEpWarning: typeof import('~icons/ep/warning')['default']
const ItemDevice: typeof import('./src/components/list-item/ItemDevice.vue')['default']
const ItemMonitorObj: typeof import('./src/components/list-item/ItemMonitorObj.vue')['default']
const ItemScene: typeof import('./src/components/list-item/ItemScene.vue')['default']
diff --git "a/public/\345\276\220\346\261\207\345\214\272\351\244\220\351\245\256\347\233\221\347\256\241\347\256\200\346\212\245\357\274\210\345\244\251\351\222\245\346\241\245\357\274\211-2023\345\271\2648\346\234\210\0501\051.doc" "b/public/\345\276\220\346\261\207\345\214\272\351\244\220\351\245\256\347\233\221\347\256\241\347\256\200\346\212\245\357\274\210\345\244\251\351\222\245\346\241\245\357\274\211-2023\345\271\2648\346\234\210\0501\051.doc"
new file mode 100644
index 0000000..880e78d
--- /dev/null
+++ "b/public/\345\276\220\346\261\207\345\214\272\351\244\220\351\245\256\347\233\221\347\256\241\347\256\200\346\212\245\357\274\210\345\244\251\351\222\245\346\241\245\357\274\211-2023\345\271\2648\346\234\210\0501\051.doc"
Binary files differ
diff --git "a/public/\345\276\220\346\261\207\345\214\272\351\244\220\351\245\256\347\233\221\347\256\241\347\256\200\346\212\245\357\274\210\345\244\251\351\222\245\346\241\245\357\274\211-2023\345\271\2648\346\234\210\0501\051.docx" "b/public/\345\276\220\346\261\207\345\214\272\351\244\220\351\245\256\347\233\221\347\256\241\347\256\200\346\212\245\357\274\210\345\244\251\351\222\245\346\241\245\357\274\211-2023\345\271\2648\346\234\210\0501\051.docx"
new file mode 100644
index 0000000..0393681
--- /dev/null
+++ "b/public/\345\276\220\346\261\207\345\214\272\351\244\220\351\245\256\347\233\221\347\256\241\347\256\200\346\212\245\357\274\210\345\244\251\351\222\245\346\241\245\357\274\211-2023\345\271\2648\346\234\210\0501\051.docx"
Binary files differ
diff --git a/src/api/fytz/creditApi.js b/src/api/fytz/creditApi.js
index 12ff2a8..967e7f1 100644
--- a/src/api/fytz/creditApi.js
+++ b/src/api/fytz/creditApi.js
@@ -5,25 +5,31 @@
* 淇$敤璇勪及API鎺ュ彛
*/
export default {
- /**
- * 涓嬭浇鐢ㄦ埛鐜俊鐮�
- * @param {*} userId
- * @param {*} userName
- */
- downloadCode(userId, userName) {
+ fetchCodeUrl(userId, userName) {
return $fytz
.get(`credit/ecCode/download?userId=${userId}`, { responseType: 'blob' })
.then((res) => {
const name = res.headers.get('fileName') || userName
const fileName = Base64.decode(name)
const url = window.URL.createObjectURL(res.data)
- const link = document.createElement('a')
- link.href = url
- link.setAttribute('download', fileName)
- document.body.appendChild(link)
- link.click()
- document.body.removeChild(link)
- window.URL.revokeObjectURL(url)
+ return { fileName, url }
})
},
+ /**
+ * 涓嬭浇鐢ㄦ埛鐜俊鐮�
+ * @param {*} userId
+ * @param {*} userName
+ */
+ downloadCode(userId, userName) {
+ return this.fetchCodeUrl(userId, userName).then((res) => {
+ const { fileName, url } = res
+ const link = document.createElement('a')
+ link.href = url
+ link.setAttribute('download', fileName)
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+ window.URL.revokeObjectURL(url)
+ })
+ },
}
diff --git a/src/assets/loginPageBg.png b/src/assets/loginPageBg.png
new file mode 100644
index 0000000..d2ea680
--- /dev/null
+++ b/src/assets/loginPageBg.png
Binary files differ
diff --git a/src/components/SearchBar.vue b/src/components/SearchBar.vue
index 19f48c3..63dda44 100644
--- a/src/components/SearchBar.vue
+++ b/src/components/SearchBar.vue
@@ -4,21 +4,13 @@
<el-form :inline="true" :model="formSearch">
<el-form-item label="鎬讳换鍔�">
<!-- <el-input v-model="formSearch.topTaskId" placeholder="鎬讳换鍔�" /> -->
- <el-select
- v-model="formSearch.topTaskId"
- placeholder="鎬讳换鍔�"
- style="width: 260px"
- >
- <el-option
- v-for="s in topTasks"
- :key="s.value"
- :label="s.label"
- :value="s.value"
- />
+ <el-select v-model="formSearch.topTaskId" placeholder="鎬讳换鍔�" style="width: 260px">
+ <el-option v-for="s in topTasks" :key="s.value" :label="s.label" :value="s.value" />
</el-select>
</el-form-item>
<FYOptionScene
:allOption="false"
+ :init-value="false"
:type="2"
v-model:value="formSearch.scenetype"
></FYOptionScene>
@@ -36,19 +28,20 @@
</template>
<script>
-import taskApi from '@/api/fysp/taskApi';
+import taskApi from '@/api/fysp/taskApi'
+import dayjs from 'dayjs'
export default {
emits: ['onSubmit'],
props: {
btnShow: {
type: Boolean,
- default: true
+ default: true,
},
init: {
type: Boolean,
- default: true
- }
+ default: true,
+ },
},
data() {
@@ -56,48 +49,53 @@
topTasks: [],
formSearch: {
topTaskId: '',
- scenetype: ''
- }
- };
+ scenetype: {
+ label: '椁愰ギ',
+ value: '5',
+ },
+ },
+ }
},
methods: {
//鑾峰彇鏌ヨ鏉′欢
getOptions() {
taskApi.getTopTask().then((res) => {
- const list = res.map((r) => {
- return {
- value: r.tguid,
- label: r.name,
- data: r
- };
- });
- this.topTasks = list;
- this.formSearch.topTaskId = list[0].value;
+ const list = res
+ .filter((e) => {
+ return e.districtname == '寰愭眹鍖�' && dayjs(e.starttime).isBefore(dayjs('2023-12-31'))
+ })
+ .map((r) => {
+ return {
+ value: r.tguid,
+ label: r.name,
+ data: r,
+ }
+ })
+ this.topTasks = list
+ this.formSearch.topTaskId = list[0].value
if (this.init) {
- this.onSubmit();
+ this.onSubmit()
}
- });
+ })
},
//鏌ヨ瀛愪换鍔$粺璁′俊鎭�
onSubmit() {
- const task = this.topTasks.find(
- (t) => t.data.tguid == this.formSearch.topTaskId
- );
+ const task = this.topTasks.find((t) => t.data.tguid == this.formSearch.topTaskId)
const param = {
topTask: task ? task.data : {},
sceneTypeId: this.formSearch.scenetype.value,
sceneTypeName: this.formSearch.scenetype.label,
- };
+ }
// console.log(param);
- this.$emit('onSubmit', param);
- }
+ this.$emit('onSubmit', param)
+ },
},
mounted() {
- this.getOptions();
+ this.getOptions()
},
- expose: ['onSubmit']
-};
+ expose: ['onSubmit'],
+}
</script>
<style scoped>
diff --git a/src/components/bg-task/FYBgTaskCard.vue b/src/components/bg-task/FYBgTaskCard.vue
new file mode 100644
index 0000000..7c38efa
--- /dev/null
+++ b/src/components/bg-task/FYBgTaskCard.vue
@@ -0,0 +1,175 @@
+<template>
+ <el-card shadow="never" :body-style="{ padding: 0 }">
+ <template #header>
+ <el-row justify="space-between">
+ <div>
+ <div><el-text tag="b" size="large">鍚庡彴浠诲姟</el-text></div>
+ <el-text size="small" type="info"
+ >鏄剧ず褰撳墠姝e湪杩涜鐨勫悗鍙拌�楁椂浠诲姟鐘舵��</el-text
+ >
+ </div>
+ <el-button
+ icon="Refresh"
+ type="primary"
+ size="default"
+ :loading="loading"
+ @click="fetchTask"
+ >鍒锋柊浠诲姟</el-button
+ >
+ </el-row>
+ <!-- <el-row>
+ <el-button type="default" size="default" @click="newTestTask">鏂板娴嬭瘯浠诲姟</el-button>
+ <el-button type="default" size="default" @click="startNewTestTask"
+ >鏂板缓骞惰繍琛屼竴涓祴璇曚换鍔�</el-button
+ >
+ <el-button type="default" size="default" @click="shutDownTask"
+ >寮哄埗鍏抽棴鎵�鏈夋祴璇曚换鍔�</el-button
+ >
+ </el-row> -->
+ </template>
+ <el-scrollbar height="70vh" class="scrollbar">
+ <template v-for="(v, i) in taskList" :key="i">
+ <FYBgTaskItem
+ :model="v"
+ :index="i"
+ @start="startTask"
+ @shutDown="shutDownTask"
+ @remove="removeTask"
+ @gotoResult="gotoResult"
+ ></FYBgTaskItem>
+ </template>
+ </el-scrollbar>
+ </el-card>
+</template>
+<script>
+/**
+ * 鑷姩璇勪及浠诲姟绠$悊
+ */
+import { useFetchData } from '@/composables/fetchData';
+import bgtaskApi from '@/api/fysp/bgtaskApi';
+import { enumBgTask, BG_TASK_TYPE, BG_TASK_STATUS } from '@/enum/bgTask';
+import { useBgtaskStore } from '@/stores/bgtaskStore';
+
+export default {
+ setup() {
+ const { loading, fetchData } = useFetchData();
+ const { registerOnFetchTask } = useBgtaskStore();
+ return { loading, fetchData, registerOnFetchTask };
+ },
+ props: {
+ modelValue: Number
+ },
+ emits: ['update:modelValue'],
+ data() {
+ return {
+ taskList: [],
+ taskIndex: 0
+ };
+ },
+ watch: {
+ taskList: {
+ handler(nV) {
+ let count = 0;
+ for (const e of nV) {
+ if (e.status == BG_TASK_STATUS.RUNNING.name) {
+ count++;
+ }
+ }
+ this.$emit('update:modelValue', count);
+ },
+ deep: true
+ }
+ },
+ methods: {
+ addTask() {},
+ newTestTask() {
+ this.fetchData((page, pageSize) => {
+ return bgtaskApi
+ .newTestTask(`Test-Task-${++this.taskIndex}`)
+ .then((res) => {
+ this.taskList.push(res.data);
+ });
+ });
+ },
+ startNewTestTask() {
+ this.fetchData((page, pageSize) => {
+ return bgtaskApi
+ .startNewTestTask(`Test-Task-${++this.taskIndex}`)
+ .then((res) => {
+ this.taskList.push(res.data);
+ });
+ });
+ },
+
+ _getParam(taskStatus) {
+ return {
+ type: taskStatus.type,
+ id: taskStatus.id
+ };
+ },
+ fetchTask() {
+ this.fetchData((page, pageSize) => {
+ return bgtaskApi
+ .fetchTaskStatus({
+ // type: BG_TASK_TYPE.AUTO_SCORE.name
+ })
+ .then((res) => {
+ this.taskList = res.data;
+ });
+ });
+ },
+ startTask(index, callback) {
+ this.fetchData((page, pageSize) => {
+ const param = this._getParam(this.taskList[index]);
+ return bgtaskApi.startTask(param).then((res) => {
+ this.taskList[index] = res.data;
+ callback(true);
+ });
+ });
+ },
+ shutDownTask(index, callback) {
+ this.fetchData((page, pageSize) => {
+ const param = this._getParam(this.taskList[index]);
+ return bgtaskApi.shutDownTask(param).then((res) => {
+ if (index && res.data && res.data.length == 1) {
+ this.taskList[index] = res.data[0];
+ } else {
+ res.data.forEach((e) => {
+ let v = this.taskList.find((value) => {
+ return value.id == e.id;
+ });
+ const i = this.taskList.indexOf(v);
+ this.taskList[i] = e;
+ });
+ }
+ callback(true);
+ });
+ });
+ },
+ removeTask(index, callback) {
+ this.fetchData((page, pageSize) => {
+ const param = this._getParam(this.taskList[index]);
+ return bgtaskApi.removeTask(param).then((res) => {
+ if (res.data) {
+ this.taskList.splice(index, 1);
+ callback(true);
+ }
+ });
+ });
+ },
+ gotoResult(index) {}
+ },
+ mounted() {
+ this.fetchTask();
+ this.registerOnFetchTask(this.fetchTask);
+ // setInterval(() => {
+ // this.fetchTask();
+ // }, 10000);
+ }
+};
+</script>
+<style scoped>
+.scrollbar {
+ padding: 8px;
+}
+</style>
diff --git a/src/components/bg-task/FYBgTaskDialog.vue b/src/components/bg-task/FYBgTaskDialog.vue
new file mode 100644
index 0000000..f274ac1
--- /dev/null
+++ b/src/components/bg-task/FYBgTaskDialog.vue
@@ -0,0 +1,31 @@
+<template>
+ <el-popover
+ placement="bottom"
+ :width="600"
+ trigger="click"
+ v-model:visible="bgtaskStore.dialogShow"
+ >
+ <template #reference>
+ <el-badge :value="runningNum" :hidden="runningNum == 0" class="m-r-16">
+ <el-button circle>
+ <el-icon v-if="runningNum > 0" color="red" class="is-loading"
+ ><Clock
+ /></el-icon>
+ <el-icon v-else><Clock /></el-icon>
+ </el-button>
+ </el-badge>
+ </template>
+ <!-- <el-button circle icon="Close" type="danger" @click=</el-button> -->
+ <FYBgTaskCard v-model="runningNum"></FYBgTaskCard>
+ </el-popover>
+</template>
+<script setup>
+import { ref } from 'vue';
+import { useBgtaskStore } from '@/stores/bgtaskStore';
+
+const bgtaskStore = useBgtaskStore();
+
+const runningNum = ref(0);
+
+</script>
+<style scoped></style>
diff --git a/src/components/bg-task/FYBgTaskItem.vue b/src/components/bg-task/FYBgTaskItem.vue
new file mode 100644
index 0000000..be619e6
--- /dev/null
+++ b/src/components/bg-task/FYBgTaskItem.vue
@@ -0,0 +1,218 @@
+<template>
+ <el-card class="m-b-8" shadow="always" :body-style="{ padding: '8px' }">
+ <el-row>
+ <el-col :span="4">
+ <div class="status-btn">
+ <el-icon v-if="waiting" color="var(--el-color-info)" :size="50"><VideoPlay /></el-icon>
+ <el-icon v-else-if="running" color="var(--el-color-primary)" :size="50" class="is-loading"
+ ><Loading
+ /></el-icon>
+ <el-icon v-else-if="success" color="var(--el-color-success)" :size="50"
+ ><CircleCheck
+ /></el-icon>
+ <el-icon v-else-if="fail" color="var(--el-color-error)" :size="50"
+ ><CircleClose
+ /></el-icon>
+ <el-icon v-else color="var(--el-color-warning)" :size="50"><Warning /></el-icon>
+ <el-text type="info" size="small" style="position: absolute; bottom: 0">{{
+ nameToLabel(model.status)
+ }}</el-text>
+ </div>
+ </el-col>
+ <el-col :span="20" class="p-l-8">
+ <el-row justify="space-between">
+ <el-text class="m-l-4px w-300px" tag="b" size="large" truncated>{{ model.name }}</el-text>
+ <el-tag>{{ nameToLabel(model.type) }}</el-tag>
+ </el-row>
+ <el-row class="p-v-8" align="bottom">
+ <el-col :span="12">
+ <span class="timer">{{ time }}</span>
+ <el-text type="info" size="small" tag="div">杩愯鏃堕暱</el-text>
+ </el-col>
+ <el-col :span="12">
+ <el-text type="default" size="default" tag="div"
+ >寮�濮嬶細{{ $fm.formatYMDHMS(model.startTime) }}</el-text
+ >
+ <el-text type="default" size="default" tag="div"
+ >缁撴潫锛歿{ $fm.formatYMDHMS(model.endTime) }}</el-text
+ >
+ </el-col>
+ </el-row>
+ <el-row justify="end" align="bottom">
+ <!-- <span class="f-s color-i">ID锛歿{ model.id }}</span> -->
+ <el-row>
+ <FYReconfrimButton v-if="waiting" @confirm="startTask" v-model="startConfirm">
+ <el-button
+ plain
+ icon="VideoPlay"
+ type="primary"
+ size="small"
+ :loading="false"
+ @click="startConfirm = true"
+ >寮�濮嬩换鍔�</el-button
+ >
+ </FYReconfrimButton>
+ <FYReconfrimButton v-if="running" @confirm="stopTask" v-model="stopConfirm">
+ <el-button
+ icon="VideoPause"
+ plain
+ type="danger"
+ size="small"
+ :loading="false"
+ @click="stopConfirm = true"
+ >寮哄埗缁撴潫</el-button
+ >
+ </FYReconfrimButton>
+ <FYReconfrimButton v-if="!running" @confirm="removeTask" v-model="removeConfirm">
+ <el-button
+ icon="Delete"
+ plain
+ type="danger"
+ size="small"
+ :loading="false"
+ @click="removeConfirm = true"
+ >绉婚櫎浠诲姟</el-button
+ >
+ </FYReconfrimButton>
+ <template v-if="success">
+ <el-button
+ v-if="btnType"
+ plain
+ type="success"
+ size="small"
+ :loading="false"
+ @click="download"
+ >涓嬭浇鏂囦欢<el-icon class="m-l-4"><Right /></el-icon
+ ></el-button>
+ <el-button
+ v-else
+ plain
+ type="success"
+ size="small"
+ :loading="false"
+ @click="gotoResult"
+ >鏌ョ湅缁撴灉<el-icon class="m-l-4"><Right /></el-icon
+ ></el-button>
+ </template>
+ </el-row>
+ </el-row>
+ </el-col>
+ </el-row>
+ </el-card>
+</template>
+<script>
+import { nTlBgTask, BG_TASK_STATUS, BG_TASK_TYPE } from '@/enum/bgTask';
+import { useTimer } from '@/composables/timer';
+import downloadApi from '@/api/fysp/downloadApi';
+// import { useTimer } from '@/composables/timer2';
+
+export default {
+ setup() {
+ const { time, startTimer, pauseTimer, stopTimer, count } = useTimer();
+ return { time, startTimer, pauseTimer, stopTimer, count };
+ },
+ props: {
+ model: Object,
+ index: Number
+ },
+ emits: ['start', 'shutDown', 'remove', 'gotoResult'],
+ data() {
+ return {
+ startConfirm: false,
+ stopConfirm: false,
+ removeConfirm: false
+ };
+ },
+ watch: {
+ 'model.status': {
+ handler(nV) {
+ switch (nV) {
+ case BG_TASK_STATUS.WAITING.name:
+ this.stopTimer();
+ break;
+ case BG_TASK_STATUS.RUNNING.name:
+ this.startTimer();
+ break;
+ case BG_TASK_STATUS.SUCCESS.name:
+ case BG_TASK_STATUS.FAIL.name:
+ case BG_TASK_STATUS.SHUTDOWN.name:
+ this.pauseTimer();
+ break;
+ default:
+ this.stopTimer();
+ break;
+ }
+ this.count = this.model.runTime;
+ },
+ immediate: true
+ }
+ },
+ computed: {
+ btnType() {
+ return this.model.type == BG_TASK_TYPE.DOCUMENT.name;
+ },
+ waiting() {
+ return this.model.status == BG_TASK_STATUS.WAITING.name;
+ },
+ running() {
+ return this.model.status == BG_TASK_STATUS.RUNNING.name;
+ },
+ success() {
+ return this.model.status == BG_TASK_STATUS.SUCCESS.name;
+ },
+ fail() {
+ return this.model.status == BG_TASK_STATUS.FAIL.name;
+ },
+ shutdown() {
+ return this.model.status == BG_TASK_STATUS.SHUTDOWN.name;
+ }
+ },
+ methods: {
+ nameToLabel(name) {
+ const t = nTlBgTask(name);
+ return t.label;
+ },
+ startTask() {
+ this.$emit('start', this.index, (res) => {
+ if (res) {
+ this.startTimer();
+ }
+ });
+ },
+ stopTask() {
+ this.$emit('shutDown', this.index, (res) => {
+ if (res) {
+ this.stopTimer();
+ }
+ });
+ },
+ removeTask() {
+ this.$emit('remove', this.index, (res) => {
+ if (res) {
+ // this.stopTimer();
+ }
+ });
+ },
+ gotoResult() {
+ this.$emit('gotoResult', this.index);
+ },
+ download() {
+ downloadApi.downloadFile(this.model.extra);
+ }
+ }
+};
+</script>
+<style scoped>
+.status-btn {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border: var(--el-border);
+ border-radius: var(--el-border-radius-base);
+}
+.timer {
+ font-size: 30px;
+}
+</style>
diff --git a/src/components/core/AppHeader.vue b/src/components/core/AppHeader.vue
index 2d5e225..5c9e81f 100644
--- a/src/components/core/AppHeader.vue
+++ b/src/components/core/AppHeader.vue
@@ -9,6 +9,7 @@
</el-space>
</el-col>
<el-col :span="12" class="logout">
+ <FYBgTaskDialog></FYBgTaskDialog>
<el-button icon="SwitchButton">閫�鍑虹櫥褰�</el-button>
</el-col>
</el-row>
diff --git a/src/components/search-option/CompQuickSet.vue b/src/components/search-option/CompQuickSet.vue
index f2da498..06d630a 100644
--- a/src/components/search-option/CompQuickSet.vue
+++ b/src/components/search-option/CompQuickSet.vue
@@ -19,19 +19,19 @@
type: Array,
default: () => {
return [
- {
- name: '闈欏畨宸ュ湴',
- locations: {
- pCode: '31',
- pName: '涓婃捣甯�',
- cCode: '3100',
- cName: '涓婃捣甯�',
- dCode: '310106',
- dName: '闈欏畨鍖�'
- },
- scenetype: { label: '宸ュ湴', value: '1' },
- sourceType: 2
- },
+ // {
+ // name: '闈欏畨宸ュ湴',
+ // locations: {
+ // pCode: '31',
+ // pName: '涓婃捣甯�',
+ // cCode: '3100',
+ // cName: '涓婃捣甯�',
+ // dCode: '310106',
+ // dName: '闈欏畨鍖�'
+ // },
+ // scenetype: { label: '宸ュ湴', value: '1' },
+ // sourceType: 2
+ // },
{
name: '寰愭眹椁愰ギ',
locations: {
@@ -40,50 +40,50 @@
cCode: '3100',
cName: '涓婃捣甯�',
dCode: '310104',
- dName: '寰愭眹鍖�'
+ dName: '寰愭眹鍖�',
},
scenetype: { label: '椁愰ギ', value: '5' },
- sourceType: 2
+ sourceType: 2,
},
- {
- name: '閲戝北宸ュ湴',
- locations: {
- pCode: '31',
- pName: '涓婃捣甯�',
- cCode: '3100',
- cName: '涓婃捣甯�',
- dCode: '310116',
- dName: '閲戝北鍖�'
- },
- scenetype: { label: '宸ュ湴', value: '1' },
- sourceType: 2
- },
- {
- name: '閲戝北鐮佸ご',
- locations: {
- pCode: '31',
- pName: '涓婃捣甯�',
- cCode: '3100',
- cName: '涓婃捣甯�',
- dCode: '310116',
- dName: '閲戝北鍖�'
- },
- scenetype: { label: '鐮佸ご', value: '2' },
- sourceType: 2
- },
- {
- name: '閲戝北鎼呮媽绔�',
- locations: {
- pCode: '31',
- pName: '涓婃捣甯�',
- cCode: '3100',
- cName: '涓婃捣甯�',
- dCode: '310116',
- dName: '閲戝北鍖�'
- },
- scenetype: { label: '鎼呮媽绔�', value: '3' },
- sourceType: 2
- }
+ // {
+ // name: '閲戝北宸ュ湴',
+ // locations: {
+ // pCode: '31',
+ // pName: '涓婃捣甯�',
+ // cCode: '3100',
+ // cName: '涓婃捣甯�',
+ // dCode: '310116',
+ // dName: '閲戝北鍖�'
+ // },
+ // scenetype: { label: '宸ュ湴', value: '1' },
+ // sourceType: 2
+ // },
+ // {
+ // name: '閲戝北鐮佸ご',
+ // locations: {
+ // pCode: '31',
+ // pName: '涓婃捣甯�',
+ // cCode: '3100',
+ // cName: '涓婃捣甯�',
+ // dCode: '310116',
+ // dName: '閲戝北鍖�'
+ // },
+ // scenetype: { label: '鐮佸ご', value: '2' },
+ // sourceType: 2
+ // },
+ // {
+ // name: '閲戝北鎼呮媽绔�',
+ // locations: {
+ // pCode: '31',
+ // pName: '涓婃捣甯�',
+ // cCode: '3100',
+ // cName: '涓婃捣甯�',
+ // dCode: '310116',
+ // dName: '閲戝北鍖�'
+ // },
+ // scenetype: { label: '鎼呮媽绔�', value: '3' },
+ // sourceType: 2
+ // }
// {
// name: '寰愭眹姹戒慨',
// locations: {
@@ -97,9 +97,9 @@
// scenetype: { label: '姹戒慨', value: '7' },
// sourceType: 1,
// }
- ];
- }
- }
+ ]
+ },
+ },
},
emits: ['quickSet'],
methods: {
@@ -111,10 +111,10 @@
// this.formSearch.locations = set.locations
// this.formSearch.scenetype = set.scenetype
- this.$emit('quickSet', set);
- }
- }
-};
+ this.$emit('quickSet', set)
+ },
+ },
+}
</script>
<style scoped>
.row {
diff --git a/src/constants/menu.js b/src/constants/menu.js
index fc263a2..550f076 100644
--- a/src/constants/menu.js
+++ b/src/constants/menu.js
@@ -103,7 +103,7 @@
{
path: '/index/analysis/huanxincode-manage',
icon: 'solar:archive-down-minimlistic-line-duotone',
- name: '鐜俊鐮佺鐞�',
+ name: '鐜俊鐮�',
},
{
path: '/index/inspection/report-manage',
diff --git a/src/enum/scene copy.js b/src/enum/scene copy.js
deleted file mode 100644
index 328c38d..0000000
--- a/src/enum/scene copy.js
+++ /dev/null
@@ -1,185 +0,0 @@
-import sceneApi from '@/api/fysp/sceneApi';
-import configApi from '@/api/fytz/configApi';
-
-/**
- * 鍦烘櫙绫诲瀷鏋氫妇
- * @param {Number} type 1:椋炵窘鐜绯荤粺锛�2锛氶缇界洃绠$郴缁燂紱
- * @param {Boolean} allOption 鏄惁鍦ㄥご閮ㄦ坊鍔犫�滃叏閮ㄢ�濋�夐」
- */
-async function enumScene(type, allOption = true) {
- let func;
- switch (parseInt(type)) {
- case 1:
- func = _enumScene_1;
- break;
- case 2:
- func = _enumScene_2;
- break;
- default:
- func = _enumScene_1;
- break;
- }
-
- return func().then((res) => {
- if (!allOption) {
- return res.shift();
- } else {
- return res;
- }
- });
-}
-
-function getSceneName(value, type = 1) {
- return enumScene(type).then((res) => {
- return res.find((v) => {
- if (v.value == value) {
- return v;
- }
- });
- });
-}
-
-// 椋炵窘鐜绯荤粺
-let _scene1;
-function _enumScene_1() {
- if (_scene1) {
- return Promise.resolve(_scene1);
- } else {
- return configApi.fetchSceneTypeRange().then((res) => {
- _scene1 = res.data.map((r) => {
- return {
- label: r.second,
- value: r.first
- };
- });
- _scene1.unshift({
- label: '鍏ㄩ儴',
- value: null
- });
- });
- }
- // return [
- // {
- // label: '鍏ㄩ儴',
- // value: null
- // },
- // {
- // label: '椁愰ギ',
- // value: '1'
- // },
- // {
- // label: '宸ュ湴',
- // value: '2'
- // },
- // {
- // label: '鐮佸ご',
- // value: '3'
- // },
- // {
- // label: '鍫嗗満',
- // value: '4'
- // },
- // {
- // label: '鎼呮媽绔�',
- // value: '5'
- // },
- // {
- // label: '宸ヤ笟浼佷笟',
- // value: '6'
- // },
- // {
- // label: '姹戒慨',
- // value: '7'
- // },
- // {
- // label: '瀹為獙瀹�',
- // value: '8'
- // },
- // {
- // label: '鍖荤枟鏈烘瀯',
- // value: '9'
- // }
- // ];
-}
-
-// 椋炵窘鐩戠绯荤粺
-let _scene2;
-function _enumScene_2() {
- if (_scene2) {
- return Promise.resolve(_scene2);
- } else {
- return sceneApi.getAllScene().then((res) => {
- _scene2 = res.map((r) => {
- return {
- label: r.sceneType,
- value: r.sceneTypeId + ''
- };
- });
- _scene2.unshift({
- label: '鍏ㄩ儴',
- value: null
- });
- });
- }
- // return [
- // {
- // label: '鍏ㄩ儴',
- // value: null
- // },
- // {
- // label: '宸ュ湴',
- // value: '1'
- // },
- // {
- // label: '鐮佸ご',
- // value: '2'
- // },
- // {
- // label: '鎼呮媽绔�',
- // value: '3'
- // },
- // {
- // label: '宸ヤ笟浼佷笟',
- // value: '4'
- // },
- // {
- // label: '椁愰ギ',
- // value: '5'
- // },
- // {
- // label: '姹戒慨',
- // value: '6'
- // },
- // {
- // label: '閬撹矾鎵皹鐩戞祴鐐�',
- // value: '9'
- // },
- // {
- // label: '閬撹矾',
- // value: '10'
- // },
- // {
- // label: '鍫嗗満',
- // value: '14'
- // }
- // ];
-}
-
-/**
- * 椋炵窘鐩戠绯荤粺 鍦烘櫙绫诲瀷杞� 椋炵窘鐜绯荤粺
- * @param {*} sceneTypeId
- */
-async function svToTz(sceneTypeId) {
- const sv = await _enumScene_2();
- const tz = await _enumScene_1();
-
- const scType = sv.find((v) => {
- return v.value == sceneTypeId + '';
- });
- const tzType = tz.find((v) => {
- return v.label == scType.label;
- });
- return tzType ? tzType : scType;
-}
-
-export { enumScene, getSceneName, svToTz };
diff --git a/src/router/index.js b/src/router/index.js
index 034c6ca..9a913c5 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -119,7 +119,7 @@
{
name: 'auto-evalution',
path: 'auto-evalution',
- component: () => import('@/views/analysis/evalution/EvalutationRecord.vue'),
+ component: () => import('@/views/analysis/evalution/EvalutationTask.vue'),
},
{
name: 'huanxincode-manage',
diff --git a/src/stores/bgtaskStore.js b/src/stores/bgtaskStore.js
new file mode 100644
index 0000000..12f3814
--- /dev/null
+++ b/src/stores/bgtaskStore.js
@@ -0,0 +1,36 @@
+import { defineStore } from 'pinia';
+import { ref } from 'vue';
+
+export const useBgtaskStore = defineStore('bgtask', () => {
+ // 寮瑰嚭妗嗘樉绀�
+ const dialogShow = ref(false);
+
+ const events = [];
+
+ function toggleShow(show) {
+ if (typeof show === 'boolean') {
+ dialogShow.value = show;
+ } else {
+ dialogShow.value = !dialogShow.value;
+ }
+ }
+
+ function registerOnFetchTask(func) {
+ events.push(func);
+ }
+
+ function fetchTask() {
+ events.forEach((e) => {
+ if (typeof e === 'function') {
+ e();
+ }
+ });
+ }
+
+ return {
+ dialogShow,
+ toggleShow,
+ registerOnFetchTask,
+ fetchTask
+ };
+});
diff --git a/src/views/LoginPage.vue b/src/views/LoginPage.vue
index a38b322..c370e12 100644
--- a/src/views/LoginPage.vue
+++ b/src/views/LoginPage.vue
@@ -1,14 +1,162 @@
<template>
- LoginPage
-
- <el-button type="primary" class="login-btn" @click="login">鐧诲綍</el-button>
+ <div class="login-container">
+ <div class="login-wrapper">
+ <div class="login-header">
+ <h1 class="login-title">椁愰ギ娌圭儫鏅鸿兘鐩戞祴涓庣洃绠′竴浣撳寲骞冲彴</h1>
+ <p class="login-subtitle">娆㈣繋鐧诲綍</p>
+ </div>
+ <div class="login-form">
+ <el-form :model="loginForm" :rules="rules" ref="loginFormRef" label-position="top">
+ <el-form-item label="璐﹀彿" prop="username">
+ <el-input
+ v-model="loginForm.username"
+ placeholder="璇疯緭鍏ヨ处鍙�"
+ prefix-icon="User"
+ size="large"
+ />
+ </el-form-item>
+ <el-form-item label="瀵嗙爜" prop="password">
+ <el-input
+ v-model="loginForm.password"
+ type="password"
+ placeholder="璇疯緭鍏ュ瘑鐮�"
+ prefix-icon="Lock"
+ size="large"
+ show-password
+ />
+ </el-form-item>
+ <el-form-item>
+ <el-button
+ type="primary"
+ class="login-btn"
+ :loading="loading"
+ @click="login"
+ size="large"
+ style="width: 100%"
+ >
+ 鐧诲綍
+ </el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+ </div>
+ </div>
</template>
+
<script setup lang="ts">
+import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
+import type { FormInstance, FormRules } from 'element-plus'
+import { ElMessage } from 'element-plus'
const router = useRouter()
-function login() {
- router.push('/index/monitor/data-dashboard')
- // router.push('/index')
+const loginFormRef = ref<FormInstance>()
+const loading = ref(false)
+
+const loginForm = reactive({
+ username: 'admin',
+ password: '123456',
+})
+
+const rules = reactive<FormRules>({
+ username: [{ required: true, message: '璇疯緭鍏ヨ处鍙�', trigger: 'blur' }],
+ password: [{ required: true, message: '璇疯緭鍏ュ瘑鐮�', trigger: 'blur' }],
+})
+
+async function login() {
+ if (!loginFormRef.value) return
+
+ try {
+ await loginFormRef.value.validate()
+ loading.value = true
+
+ // 妯℃嫙鐧诲綍楠岃瘉
+ setTimeout(() => {
+ if (loginForm.username === 'admin' && loginForm.password === '123456') {
+ router.push('/index/monitor/data-dashboard')
+ } else {
+ ElMessage.error('璐﹀彿鎴栧瘑鐮侀敊璇�')
+ }
+ loading.value = false
+ }, 500)
+ } catch (error) {
+ console.log('楠岃瘉澶辫触:', error)
+ }
}
</script>
+
+<style>
+/* 鍏ㄥ眬鏍峰紡閲嶇疆 */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+html,
+body {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+}
+</style>
+
+<style scoped>
+.login-container {
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ background-image: url('@/assets/loginPageBg.png');
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+ padding-right: 10%;
+}
+
+.login-wrapper {
+ width: 400px;
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 8px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ padding: 32px;
+ animation: fadeIn 0.5s ease-in-out;
+}
+
+.login-header {
+ text-align: center;
+ margin-bottom: 32px;
+}
+
+.login-title {
+ font-size: 20px;
+ font-weight: 600;
+ color: #1a1a1a;
+ margin-bottom: 8px;
+}
+
+.login-subtitle {
+ font-size: 14px;
+ color: #666;
+}
+
+.login-form {
+ width: 100%;
+}
+
+.login-btn {
+ margin-top: 16px;
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(-20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+</style>
diff --git a/src/views/analysis/evalution/components/precheck/CompPreCheck.vue b/src/views/analysis/evalution/components/precheck/CompPreCheck.vue
index 3dba2b8..c3cd704 100644
--- a/src/views/analysis/evalution/components/precheck/CompPreCheck.vue
+++ b/src/views/analysis/evalution/components/precheck/CompPreCheck.vue
@@ -2,7 +2,7 @@
<el-steps :active="stepIndex" finish-status="success" style="" align-center>
<el-step title="璇勪及鑼冨洿" />
<el-step title="鏁版嵁婧愭鏌�" />
- <el-step title="鏉$洰璞佸厤" />
+ <!-- <el-step title="鏉$洰璞佸厤" /> -->
<el-step title="鑷姩璇勪及" />
</el-steps>
<CompCheckArea v-show="stepIndex == 0" v-model="stepIndex" @change="onAreaChange"></CompCheckArea>
@@ -12,13 +12,13 @@
ref="refSource"
@change="onDataSourceChange"
></CompCheckSource>
- <CompCheckExemption
+ <!-- <CompCheckExemption
v-show="stepIndex == 2"
v-model="stepIndex"
@change="onExemptionChange"
- ></CompCheckExemption>
+ ></CompCheckExemption> -->
<CompCheckConfirm
- v-show="stepIndex == 3"
+ v-show="stepIndex == 2"
v-model="stepIndex"
:area-info="area"
:data-source="dataSource"
@@ -28,11 +28,11 @@
</template>
<script>
-import dayjs from 'dayjs';
-import CompCheckArea from './components/CompCheckArea.vue';
-import CompCheckSource from './components/CompCheckSource.vue';
-import CompCheckExemption from './components/CompCheckExemption.vue';
-import CompCheckConfirm from './components/CompCheckConfirm.vue';
+import dayjs from 'dayjs'
+import CompCheckArea from './components/CompCheckArea.vue'
+import CompCheckSource from './components/CompCheckSource.vue'
+import CompCheckExemption from './components/CompCheckExemption.vue'
+import CompCheckConfirm from './components/CompCheckConfirm.vue'
/**
* 鑷姩璇勪及鏉′欢鍚堣鎬ф鏌�
@@ -48,20 +48,20 @@
stepIndex: 0,
area: {
_locations: {},
- _scenetype: {}
+ _scenetype: {},
},
dataSource: {},
// 璞佸厤鏉$洰
- exemptionItems: {}
- };
+ exemptionItems: {},
+ }
},
methods: {
/**
* 鐩戝惉璇勪及鑼冨洿鍙樻洿
*/
onAreaChange(val) {
- const v = val.value;
- this.area = v;
+ const v = val.value
+ this.area = v
const a = {
provincecode: v._locations.pCode,
provincename: v._locations.pName,
@@ -75,23 +75,23 @@
endtime: this.$fm.formatYMDHMS(v.time),
scensetypeid: v._scenetype.value,
online: true,
- sourceType: v.sourceType
- };
- this.$refs.refSource.startCheck(a);
+ sourceType: v.sourceType,
+ }
+ this.$refs.refSource.startCheck(a)
},
onDataSourceChange(val) {
- this.dataSource = val;
+ this.dataSource = val
},
onExemptionChange(val) {
- this.exemptionItems = val;
+ this.exemptionItems = val
},
/**
* 鑷姩璇勪及鍓嶇疆鍚堣鎬ф鏌�
* 妫�鏌ユ墍閫夎寖鍥村唴鍚勯」璇勪及鏁版嵁婧愭槸鍚﹀畬鏁�
*/
onNewTask() {
- this.$emit('startTask');
- }
- }
-};
+ this.$emit('startTask')
+ },
+ },
+}
</script>
diff --git a/src/views/analysis/huanxincode/HuanxinCodeManage.vue b/src/views/analysis/huanxincode/HuanxinCodeManage.vue
index 9a75b38..9140d7c 100644
--- a/src/views/analysis/huanxincode/HuanxinCodeManage.vue
+++ b/src/views/analysis/huanxincode/HuanxinCodeManage.vue
@@ -1,5 +1,31 @@
<template>
<div class="huanxin-code-manage">
+ <FYSearchBar @search="onSearch">
+ <template #options>
+ <!-- 鍖哄幙 -->
+ <FYOptionLocation
+ :initValue="false"
+ :allOption="false"
+ :level="3"
+ :checkStrictly="false"
+ v-model:value="formSearch.locations"
+ ></FYOptionLocation>
+ <!-- 鍦烘櫙绫诲瀷 -->
+ <FYOptionScene
+ :initValue="false"
+ :allOption="false"
+ :type="1"
+ v-model:value="formSearch.scenetype"
+ ></FYOptionScene>
+ <!-- 鏃堕棿 -->
+ <FYOptionTime
+ :initValue="false"
+ type="month"
+ v-model:value="formSearch.time"
+ ></FYOptionTime>
+ </template>
+ <template #buttons v-if="$slots.buttons"> </template>
+ </FYSearchBar>
<!-- 椤堕儴瀹忚鐪嬫澘鍖� -->
<el-row :gutter="20" class="dashboard">
<el-col :span="8">
@@ -39,99 +65,96 @@
</el-button>
</div> -->
- <!-- 涓儴瑙嗗浘鍒囨崲鍖� -->
- <el-tabs v-model="activeView" class="view-tabs">
- <!-- 鍒楄〃瑙嗗浘 -->
- <el-tab-pane label="鍒楄〃瑙嗗浘" name="list">
- <el-table :data="filteredShopList" style="width: 100%">
- <el-table-column prop="shopName" label="搴楅摵鍚嶇О" />
- <el-table-column prop="district" label="鎵�鍦ㄥ尯鍘�" width="120" />
- <el-table-column prop="town" label="鎵�鍦ㄨ闀�" width="150" />
- <el-table-column label="鐜俊鐮�" width="100">
- <template #default="scope">
- <el-tag :type="getCodeType(scope.row.code)">{{ getCodeText(scope.row.code) }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="score" label="褰撳墠璇勫垎" width="120" sortable />
- <el-table-column label="璇勫垎鍙樺寲瓒嬪娍" width="150">
- <template #default="scope">
- <div class="trend">
- <el-icon :class="['trend-icon', scope.row.trend > 0 ? 'up' : 'down']">
- <ArrowUp v-if="scope.row.trend > 0" />
- <ArrowDown v-else />
- </el-icon>
- <span :class="scope.row.trend > 0 ? 'up' : 'down'">
- {{ scope.row.trend > 0 ? '+' : '' }}{{ scope.row.trend }}鍒�
- </span>
- </div>
- </template>
- </el-table-column>
- <el-table-column prop="lastUpdate" label="涓婃鏇存柊鏃堕棿" width="180" />
- <el-table-column label="鎿嶄綔" width="200" fixed="right">
- <template #default="scope">
- <el-button size="small" @click="viewDetails(scope.row)">鏌ョ湅璇︽儏</el-button>
- <!-- <el-button size="small" type="warning" @click="viewRiskWarnings(scope.row)"
- >椋庨櫓棰勮璁板綍</el-button
- > -->
- </template>
- </el-table-column>
- </el-table>
- </el-tab-pane>
-
- <!-- 鍦板浘瑙嗗浘 -->
- <el-tab-pane label="鍦板浘瑙嗗浘" name="map">
- <div class="map-container">
- <div class="map-placeholder">
- <el-empty description="鍦板浘鍔犺浇涓�..." />
- <!-- 杩欓噷搴旇闆嗘垚鐪熷疄鐨勫湴鍥剧粍浠� -->
- </div>
- <div class="map-legend">
- <div class="legend-item">
- <div class="legend-dot green"></div>
- <span>缁跨爜</span>
+ <!-- 搴楅摵鍒楄〃 -->
+ <div class="shop-list">
+ <el-table :data="pagedShopList" style="width: 100%">
+ <el-table-column prop="shopName" label="搴楅摵鍚嶇О" />
+ <el-table-column prop="district" label="鎵�鍦ㄥ尯鍘�" width="120" />
+ <el-table-column prop="town" label="鎵�鍦ㄨ闀�" width="150" />
+ <el-table-column label="鐜俊鐮�" width="100">
+ <template #default="scope">
+ <el-tag :type="getCodeType(scope.row.code)">{{ getCodeText(scope.row.code) }}</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="score" label="褰撳墠璇勫垎" width="120" sortable />
+ <el-table-column label="璇勫垎鍙樺寲瓒嬪娍" width="150">
+ <template #default="scope">
+ <div class="trend">
+ <el-icon :class="['trend-icon', scope.row.trend > 0 ? 'up' : 'down']">
+ <ArrowUp v-if="scope.row.trend > 0" />
+ <ArrowDown v-else />
+ </el-icon>
+ <span :class="scope.row.trend > 0 ? 'up' : 'down'">
+ {{ scope.row.trend > 0 ? '+' : '' }}{{ scope.row.trend }}鍒�
+ </span>
</div>
- <div class="legend-item">
- <div class="legend-dot yellow"></div>
- <span>榛勭爜</span>
- </div>
- <div class="legend-item">
- <div class="legend-dot red"></div>
- <span>绾㈢爜</span>
- </div>
- </div>
- </div>
- </el-tab-pane>
- </el-tabs>
+ </template>
+ </el-table-column>
+ <el-table-column prop="lastUpdate" label="涓婃鏇存柊鏃堕棿" width="180" />
+ <el-table-column label="鎿嶄綔" width="100" fixed="right">
+ <template #default="scope">
+ <el-button size="small" @click="viewDetails(scope.row)">鏌ョ湅璇︽儏</el-button>
+ <!-- <el-button size="small" type="warning" @click="viewRiskWarnings(scope.row)"
+ >椋庨櫓棰勮璁板綍</el-button
+ > -->
+ </template>
+ </el-table-column>
+ </el-table>
+ <!-- 鍒嗛〉缁勪欢 -->
+ <div class="pagination">
+ <el-pagination
+ v-model:current-page="currentPage"
+ v-model:page-size="pageSize"
+ :page-sizes="[10, 20, 50, 100]"
+ layout="total, sizes, prev, pager, next, jumper"
+ :total="filteredShopList.length"
+ @size-change="handleSizeChange"
+ @current-change="handleCurrentChange"
+ />
+ </div>
+ </div>
<!-- 璇︽儏鎶藉眽 -->
- <el-drawer v-model="drawerVisible" title="搴楅摵璇︽儏" direction="rtl" size="70%">
+ <el-drawer
+ v-model="drawerVisible"
+ :title="selectedShop?.shopName || '搴楅摵璇︽儏'"
+ direction="rtl"
+ size="60%"
+ >
<div v-if="selectedShop" class="shop-details">
- <!-- 鐜俊鐮佸ぇ鍥炬爣鍙婂綋鍓嶈瘎鍒� -->
- <div class="code-header">
- <div class="code-icon" :class="selectedShop.code">
- {{ getCodeText(selectedShop.code) }}
+ <el-row justify="space-between" style="flex-wrap: nowrap">
+ <!-- 鐜俊鐮佸ぇ鍥炬爣鍙婂綋鍓嶈瘎鍒� -->
+ <div class="code-header">
+ <div class="score-info">
+ <div class="score-label">褰撳墠璇勫垎</div>
+ <div class="score-value">{{ selectedShop.score }}</div>
+ </div>
+ <div class="code-icon">
+ <el-image
+ class="image"
+ :src="codeImageUrl"
+ :preview-src-list="[codeImageUrl]"
+ :initial-index="0"
+ fit="cover"
+ lazy
+ />
+ </div>
</div>
- <div class="score-info">
- <div class="score-label">褰撳墠璇勫垎</div>
- <div class="score-value">{{ selectedShop.score }}</div>
- </div>
- </div>
- <!-- 璇勫垎缁村害闆疯揪鍥� -->
- <div class="chart-section">
- <h3>璇勫垎缁村害鍒嗘瀽</h3>
- <div class="radar-chart">
- <!-- 杩欓噷搴旇闆嗘垚鐪熷疄鐨勯浄杈惧浘缁勪欢 -->
- <el-empty description="闆疯揪鍥惧姞杞戒腑..." />
+ <!-- 璇勫垎缁村害闆疯揪鍥� -->
+ <div class="chart-section">
+ <h3>璇勫垎缁村害鍒嗘瀽</h3>
+ <div class="radar-chart">
+ <canvas ref="radarChart" width="500" height="400"></canvas>
+ </div>
</div>
- </div>
+ </el-row>
<!-- 璇勫垎鍘嗗彶瓒嬪娍鍥� -->
<div class="chart-section">
<h3>璇勫垎鍘嗗彶瓒嬪娍</h3>
<div class="trend-chart">
- <!-- 杩欓噷搴旇闆嗘垚鐪熷疄鐨勮秼鍔垮浘缁勪欢 -->
- <el-empty description="瓒嬪娍鍥惧姞杞戒腑..." />
+ <canvas ref="trendChart" width="800" height="350"></canvas>
</div>
</div>
@@ -171,16 +194,46 @@
</template>
<script setup>
-import { ref, reactive, computed, onMounted } from 'vue'
+import dayjs from 'dayjs'
+import { ref, reactive, computed, onMounted, watch } from 'vue'
import { Setting, ArrowUp, ArrowDown } from '@element-plus/icons-vue'
+import * as echarts from 'echarts'
+import userApi from '@/api/fytz/userApi'
+import creditApi from '@/api/fytz/creditApi'
+// 鎼滅储琛ㄥ崟
+const formSearch = ref({
+ locations: {
+ aCode: null,
+ aName: null,
+ cCode: '3100',
+ cName: '涓婃捣甯�',
+ dCode: '310104',
+ dName: '寰愭眹鍖�',
+ mCode: null,
+ mName: null,
+ pCode: '31',
+ pName: '涓婃捣甯�',
+ tCode: null,
+ tName: null,
+ },
+ scenetype: {
+ label: '椁愰ギ',
+ value: '1',
+ },
+ time: dayjs('2023-08-01').date(1).toDate(),
+})
// 鐘舵��
-const activeView = ref('list')
const drawerVisible = ref(false)
const modelConfigVisible = ref(false)
const selectedShop = ref(null)
const isAdmin = ref(true) // 妯℃嫙绠$悊鍛樻潈闄�
const filterCode = ref('all')
+// 鍒嗛〉鐩稿叧
+const currentPage = ref(1)
+const pageSize = ref(10)
+// 鐜俊鐮佸浘鐗嘦RL
+const codeImageUrl = ref('')
// 缁熻鏁版嵁
const statistics = reactive({
@@ -228,6 +281,61 @@
'鍗庢尘闀�',
]
+function onSearch() {
+ const f = formSearch.value
+ const area = {}
+ // 琛屾斂鍖哄垝
+ area.provinceCode = f.locations.pCode
+ area.provinceName = f.locations.pName
+ if (area.provinceCode == null) {
+ area.provinceCode = null
+ area.provinceName = null
+ }
+ area.cityCode = f.locations.cCode
+ area.cityName = f.locations.cName
+ area.districtCode = f.locations.dCode
+ area.districtName = f.locations.dName
+ area.townCode = f.locations.tCode
+ area.townName = f.locations.tName
+ // 鍦烘櫙绫诲瀷
+ area.sceneTypes = []
+ f.scenetype.value == null ? (area.sceneTypes = []) : (area.sceneTypes = [f.scenetype.value])
+ // 涓婁笅绾跨姸鎬�
+ area.online = true
+ // 鍏抽敭瀛�
+ area.searchText = ''
+
+ userApi.fetchUser(currentPage.value, pageSize.value, area).then((res) => {
+ if (res) {
+ res.data
+ res.head.totalCount
+
+ shopList.value = res.data.map((item, index) => {
+ const { score, code } = generateRandomScore()
+ return {
+ id: index + 1,
+ guid: item.biGuid,
+ shopName: item.biName,
+ district: item.biDistrictName,
+ town: item.biTownName,
+ code: code,
+ score: score,
+ trend: generateRandomTrend(),
+ lastUpdate: generateRandomDate(),
+ warnings: [
+ {
+ time: generateRandomDate(),
+ content: '鍑�鍖栧櫒杩愯鏃堕暱涓嶈冻',
+ score: 90,
+ handled: true,
+ },
+ ],
+ }
+ })
+ }
+ })
+}
+
// 鐢熸垚2023骞�8鏈堝唴鐨勯殢鏈烘椂闂�
function generateRandomDate() {
const year = 2023
@@ -245,6 +353,23 @@
return array[Math.floor(Math.random() * array.length)]
}
+// 鐢熸垚闅忔満璇勫垎鍜屽搴旂幆淇$爜绛夌骇
+function generateRandomScore() {
+ const score = Math.floor(Math.random() * 101) // 0-100
+ let code
+ if (score >= 90) {
+ code = 'green'
+ } else if (score >= 60) {
+ code = 'yellow'
+ } else {
+ code = 'red'
+ }
+ return {
+ score,
+ code,
+ }
+}
+
// 鐢熸垚闅忔満璇勫垎瓒嬪娍
function generateRandomTrend() {
return Math.floor(Math.random() * 11) - 5 // -5 鍒� 5
@@ -258,7 +383,7 @@
district: '寰愭眹鍖�',
town: getRandomElement(xuhuiTowns),
code: 'green',
- score: 95,
+ score: 90,
trend: generateRandomTrend(),
lastUpdate: generateRandomDate(),
warnings: [
@@ -427,10 +552,18 @@
return shopList.value.filter((shop) => shop.code === filterCode.value)
})
+// 鍒嗛〉鍚庣殑搴楅摵鍒楄〃
+const pagedShopList = computed(() => {
+ const start = (currentPage.value - 1) * pageSize.value
+ const end = start + pageSize.value
+ return filteredShopList.value.slice(start, end)
+})
+
// 鐢熷懡鍛ㄦ湡
onMounted(() => {
// 杩欓噷鍙互浠嶢PI鑾峰彇鏁版嵁
console.log('鐜俊鐮佺鐞嗛〉闈㈠姞杞�')
+ onSearch()
})
// 鏂规硶
@@ -441,7 +574,17 @@
function filterByCode(code) {
filterCode.value = code === filterCode.value ? 'all' : code
- activeView.value = 'list' // 鍒囨崲鍒板垪琛ㄨ鍥�
+ currentPage.value = 1 // 閲嶇疆鍒扮涓�椤�
+}
+
+// 鍒嗛〉鏂规硶
+function handleSizeChange(size) {
+ pageSize.value = size
+ currentPage.value = 1
+}
+
+function handleCurrentChange(current) {
+ currentPage.value = current
}
function getCodeType(code) {
@@ -470,9 +613,183 @@
}
}
+// 闆疯揪鍥惧拰瓒嬪娍鍥惧紩鐢�
+const radarChart = ref(null)
+const trendChart = ref(null)
+let radarChartInstance = null
+let trendChartInstance = null
+
function viewDetails(shop) {
selectedShop.value = shop
drawerVisible.value = true
+
+ // 鑾峰彇鐜俊鐮佸浘鐗�
+ if (shop.guid && shop.shopName) {
+ creditApi.fetchCodeUrl(shop.guid, shop.shopName).then((res) => {
+ if (res && res.url) {
+ codeImageUrl.value = res.url
+ }
+ })
+ }
+
+ // 寤惰繜缁樺埗鍥捐〃锛岀‘淇滵OM宸叉洿鏂�
+ setTimeout(() => {
+ drawRadarChart()
+ drawTrendChart()
+ }, 100)
+}
+
+// 缁樺埗闆疯揪鍥�
+function drawRadarChart() {
+ if (!radarChart.value) return
+
+ // 閿�姣佹棫瀹炰緥
+ if (radarChartInstance) {
+ radarChartInstance.dispose()
+ }
+
+ // 鍒濆鍖杄charts瀹炰緥
+ radarChartInstance = echarts.init(radarChart.value)
+
+ // 闆疯揪鍥炬暟鎹�
+ const labels = [
+ '鍦ㄧ嚎鐩戞祴璁惧',
+ '鍑�鍖栬鏂借澶�',
+ '鍦ㄧ嚎鐩戞祴璁惧缁存姢',
+ '鍑�鍖栬鏂借澶囩淮鎶�',
+ '鍦ㄧ嚎鐩戞祴鏁版嵁閲忕骇',
+ '绌鸿皟鍜岄鏈哄櫔澹�',
+ '鍙扮珯绠$悊',
+ '淇$敤鎵胯鑷瘎',
+ ]
+
+ // 鐢熸垚闅忔満璇勫垎鏁版嵁锛堝疄闄呴」鐩腑搴斾粠API鑾峰彇锛�
+ const data = labels.map(() => Math.floor(Math.random() * 40) + 60) // 60-100鍒�
+
+ // 閰嶇疆椤�
+ const option = {
+ radar: {
+ indicator: labels.map((label) => ({
+ name: label,
+ max: 100,
+ })),
+ radius: '70%',
+ },
+ series: [
+ {
+ type: 'radar',
+ data: [
+ {
+ value: data,
+ name: '璇勫垎缁村害',
+ areaStyle: {
+ color: 'rgba(103, 194, 58, 0.2)',
+ },
+ lineStyle: {
+ color: '#67c23a',
+ },
+ itemStyle: {
+ color: '#67c23a',
+ },
+ },
+ ],
+ },
+ ],
+ }
+
+ // 娓叉煋鍥捐〃
+ radarChartInstance.setOption(option)
+
+ // 鐩戝惉绐楀彛澶у皬鍙樺寲
+ window.addEventListener('resize', () => {
+ radarChartInstance.resize()
+ })
+}
+
+// 缁樺埗瓒嬪娍鍥�
+function drawTrendChart() {
+ if (!trendChart.value) return
+
+ // 閿�姣佹棫瀹炰緥
+ if (trendChartInstance) {
+ trendChartInstance.dispose()
+ }
+
+ // 鍒濆鍖杄charts瀹炰緥
+ trendChartInstance = echarts.init(trendChart.value)
+
+ // 鐢熸垚杩囧幓12涓湀鐨勬爣绛�
+ const labels = []
+ const data = []
+ const now = dayjs()
+
+ for (let i = 11; i >= 0; i--) {
+ const date = now.subtract(i, 'month')
+ labels.push(date.format('YYYY-MM'))
+ // 鐢熸垚闅忔満璇勫垎鏁版嵁锛堝疄闄呴」鐩腑搴斾粠API鑾峰彇锛�
+ data.push(Math.floor(Math.random() * 30) + 70) // 70-100鍒�
+ }
+
+ // 閰嶇疆椤�
+ const option = {
+ tooltip: {
+ trigger: 'axis',
+ },
+ grid: {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ containLabel: true,
+ },
+ xAxis: {
+ type: 'category',
+ boundaryGap: false,
+ data: labels,
+ axisLabel: {
+ rotate: 45,
+ },
+ },
+ yAxis: {
+ type: 'value',
+ min: 60,
+ max: 100,
+ interval: 8,
+ },
+ series: [
+ {
+ name: '璇勫垎',
+ type: 'line',
+ data: data,
+ smooth: true,
+ lineStyle: {
+ color: '#409eff',
+ },
+ itemStyle: {
+ color: '#409eff',
+ },
+ areaStyle: {
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ {
+ offset: 0,
+ color: 'rgba(64, 158, 255, 0.3)',
+ },
+ {
+ offset: 1,
+ color: 'rgba(64, 158, 255, 0.1)',
+ },
+ ]),
+ },
+ },
+ ],
+ }
+
+ // 娓叉煋鍥捐〃
+ trendChartInstance.setOption(option)
+
+ // 鐩戝惉绐楀彛澶у皬鍙樺寲
+ window.addEventListener('resize', () => {
+ trendChartInstance.resize()
+ })
}
function viewRiskWarnings(shop) {
@@ -548,8 +865,13 @@
margin-bottom: 20px;
}
-.view-tabs {
+.shop-list {
margin-bottom: 20px;
+}
+
+.pagination {
+ margin-top: 20px;
+ text-align: right;
}
.trend {
@@ -633,33 +955,13 @@
.code-header {
display: flex;
- align-items: center;
- margin-bottom: 30px;
+ align-items: flex-start;
+ flex-direction: column;
+ margin-bottom: 20px;
}
.code-icon {
- width: 100px;
- height: 100px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 24px;
- font-weight: bold;
- color: white;
margin-right: 30px;
-}
-
-.code-icon.green {
- background-color: #67c23a;
-}
-
-.code-icon.yellow {
- background-color: #e6a23c;
-}
-
-.code-icon.red {
- background-color: #f56c6c;
}
.score-info {
@@ -677,17 +979,26 @@
}
.chart-section {
- margin-bottom: 30px;
+ margin-bottom: 20px;
}
.chart-section h3 {
- margin-bottom: 15px;
- font-size: 18px;
+ margin-bottom: 10px;
+ font-size: 16px;
}
-.radar-chart,
+.radar-chart {
+ width: 500px;
+ height: 500px;
+ border: 1px solid #e4e7ed;
+ border-radius: 4px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
.trend-chart {
- height: 300px;
+ height: 350px;
border: 1px solid #e4e7ed;
border-radius: 4px;
display: flex;
@@ -696,11 +1007,18 @@
}
.warning-section {
- margin-top: 30px;
+ margin-top: 20px;
}
.warning-section h3 {
- margin-bottom: 15px;
- font-size: 18px;
+ margin-bottom: 10px;
+ font-size: 16px;
+}
+
+.image {
+ width: 300px;
+ /* height: 250px; */
+ border-radius: 4px;
+ margin-bottom: 6px;
}
</style>
diff --git a/src/views/inspection/MonitorControl.vue b/src/views/inspection/MonitorControl.vue
index 50ede86..e1a2f1a 100644
--- a/src/views/inspection/MonitorControl.vue
+++ b/src/views/inspection/MonitorControl.vue
@@ -1 +1,1417 @@
-<template>s</template>
+<template>
+ <div class="monitor-control">
+ <!-- 鎬昏鐜板満宸℃煡鍗$墖 -->
+ <el-card class="mb-4">
+ <template #header>
+ <div class="card-header">
+ <span>鐜板満宸℃煡鎬昏</span>
+ <div class="filter-group">
+ <FYOptionTime
+ :initValue="false"
+ type="daterange"
+ v-model:value="params.timeRange"
+ style="width: 300px; margin-bottom: 0px"
+ :shortcuts="shortcuts"
+ ></FYOptionTime>
+ <!-- 鍖哄幙 -->
+ <FYOptionLocation
+ class="m-l-8"
+ :allOption="false"
+ :level="3"
+ :checkStrictly="false"
+ :initValue="false"
+ v-model:value="params.locations"
+ style="width: 300px; margin-bottom: 0px"
+ ></FYOptionLocation>
+ </div>
+ </div>
+ </template>
+
+ <!-- 缁熻鏁版嵁鍖哄煙 -->
+ <div class="stats-sections">
+ <!-- 宸︿晶锛氬凡宸℃煡搴楅摵鐜囥�佸贰鏌ョ偣娆°�佸鏌ョ偣娆� -->
+ <div class="stats-section left-section">
+ <h3>宸℃煡姒傚喌</h3>
+ <div class="chart-item">
+ <div class="progress-container">
+ <el-progress
+ type="dashboard"
+ :percentage="parseFloat(inspectionStats.inspectedRate)"
+ :color="['#409EFF', '#67C23A']"
+ :width="120"
+ />
+ <div class="progress-label">宸插贰鏌ュ簵閾虹巼</div>
+ <div class="progress-value">
+ {{ `${inspectionStats.inspectedShops}/${inspectionStats.totalShops}` }}
+ </div>
+ </div>
+ </div>
+ <div class="stats-grid m-t-16">
+ <el-statistic
+ class="stat-item"
+ :value="inspectionStats.inspectionPoints"
+ title="宸℃煡鐐规"
+ />
+ <el-statistic
+ class="stat-item"
+ :value="inspectionStats.reviewPoints"
+ title="澶嶆煡鐐规"
+ />
+ </div>
+ </div>
+
+ <!-- 鍙充晶锛氶棶棰樻暟銆侀棶棰樻暣鏀规暟銆侀棶棰樻暣鏀圭巼缁熻鍥� -->
+ <div class="stats-section right-section">
+ <h3>闂鏁存敼姒傚喌</h3>
+ <div class="stats-grid">
+ <el-statistic class="stat-item" :value="inspectionStats.problemCount" title="闂鏁�" />
+ <el-statistic
+ class="stat-item"
+ :value="inspectionStats.rectifiedProblems"
+ title="闂鏁存敼鏁�"
+ />
+ </div>
+ <!-- <div class="chart-item"> -->
+ <div ref="rectificationRateChart" class="chart"></div>
+ <!-- </div> -->
+ </div>
+ </div>
+
+ <!-- 鍏朵粬鍥捐〃灞曠ず -->
+ <div class="chart-container">
+ <div class="chart-item">
+ <h3>宸℃煡瀹屾垚鎯呭喌瓒嬪娍</h3>
+ <div ref="inspectionTrendChart" class="chart"></div>
+ </div>
+ <div class="chart-item">
+ <h3>闂绫诲瀷鍒嗗竷</h3>
+ <div ref="problemTypeChart" class="chart"></div>
+ </div>
+ </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="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>
+import { ref, onMounted, onUnmounted, watch, computed } from 'vue'
+import * as echarts from 'echarts'
+import { Icon } from '@iconify/vue'
+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 params = ref({
+ prodBaseTypes: [],
+ prodCheck: '',
+ scenetype: '',
+ topTask: '',
+ locations: {
+ aCode: null,
+ aName: null,
+ cCode: '3100',
+ cName: '涓婃捣甯�',
+ dCode: '310104',
+ dName: '寰愭眹鍖�',
+ mCode: null,
+ mName: null,
+ pCode: '31',
+ pName: '涓婃捣甯�',
+ tCode: null,
+ tName: null,
+ },
+ timeRange: [dayStart.startOf('month').toDate(), dayEnd.endOf('month').toDate()],
+})
+const inspectionStats = ref({
+ // totalShops: 1250,
+ // inspectedShops: 980,
+ // inspectedRate: '78.4%',
+ // inspectionPoints: 2350,
+ // reviewPoints: 450,
+ // problemCount: 320,
+ // rectifiedProblems: 280,
+ // sameDayRectificationRate: '65.2%',
+ // effectiveRectificationRate: '78.5%',
+ // comprehensiveRectificationRate: '82.3%',
+ // auditPassRate: '87.5%',
+})
+
+// 鍥捐〃寮曠敤
+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: '娌圭儫鍑�鍖栬澶囨湭姝e父杩愯',
+ // 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 = () => {
+ // 妯℃嫙鍒锋柊鏁版嵁
+ console.log('鍒锋柊宸℃煡鏁版嵁', params.value)
+ // 鐢熸垚鏂扮殑妯℃嫙鏁版嵁
+ const totalShops = 90 + Math.floor(Math.random() * 10)
+ const inspectedShops = 70 + Math.floor(Math.random() * 10)
+ const inspectedRate = ((inspectedShops / totalShops) * 100).toFixed(1) + '%'
+
+ // 璁$畻鏃堕棿鑼冨洿
+ const startTime = params.value.timeRange[0]
+ const endTime = params.value.timeRange[1]
+ const startDate = dayjs(startTime)
+ const endDate = dayjs(endTime)
+ const daysDiff = endDate.diff(startDate, 'day')
+ const months = daysDiff / 30
+
+ // 鏍规嵁鏃堕棿鑼冨洿璋冩暣鏁版嵁閲忕骇
+ const inspectionPoints = Math.floor(100 * months) + Math.floor(Math.random() * 20)
+ const problemCount = Math.floor(200 * months) + Math.floor(Math.random() * 30)
+ const reviewPoints = Math.floor(inspectionPoints * 0.2) + Math.floor(Math.random() * 10)
+ const rectifiedProblems = Math.floor(problemCount * 0.8) + Math.floor(Math.random() * 20)
+
+ inspectionStats.value = {
+ totalShops,
+ inspectedShops,
+ inspectedRate,
+ inspectionPoints,
+ reviewPoints,
+ problemCount,
+ rectifiedProblems,
+ sameDayRectificationRate: (60 + Math.random() * 20).toFixed(1) + '%',
+ effectiveRectificationRate: (70 + Math.random() * 20).toFixed(1) + '%',
+ comprehensiveRectificationRate: (75 + Math.random() * 15).toFixed(1) + '%',
+ auditPassRate: (80 + Math.random() * 15).toFixed(1) + '%',
+ }
+ // 閲嶆柊鍒濆鍖栧浘琛ㄤ互鏇存柊鏁版嵁
+ 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 = ['娌圭儫瓒呮爣鎺掓斁', '鏈畨瑁呮补鐑熷噣鍖栬澶�', '璁惧鏈甯歌繍琛�', '鍣0姹℃煋']
+ 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 = ['娌圭儫鎵版皯', '澶滈棿鍣0', '寮傚懗姹℃煋', '鍗敓闂']
+ 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)
+
+ // 棰勭暀瀵煎叆閫昏緫
+ // 杩欓噷灏嗗疄鐜板疄闄呯殑鏂囦欢瑙f瀽鍜屾暟鎹鍏�
+
+ 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)
+
+ // 棰勭暀瀵煎叆閫昏緫
+ // 杩欓噷灏嗗疄鐜板疄闄呯殑鏂囦欢瑙f瀽鍜屾暟鎹鍏�
+
+ 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
+ // 杩欓噷鍙互娣诲姞瀹為檯鐨勫垎椤甸�昏緫
+}
+
+// 鍒濆鍖栧浘琛�
+const initCharts = () => {
+ // 宸℃煡瀹屾垚鎯呭喌瓒嬪娍鍥�
+ if (inspectionTrendChart.value) {
+ const chart = echarts.init(inspectionTrendChart.value)
+
+ // 璁$畻鏃堕棿鑼冨洿骞剁敓鎴恱杞存暟鎹�
+ const startTime = params.value.timeRange[0]
+ const endTime = params.value.timeRange[1]
+ const startDate = dayjs(startTime)
+ const endDate = dayjs(endTime)
+ const daysDiff = endDate.diff(startDate, 'day')
+
+ let xAxisData = []
+ let seriesData = []
+
+ if (daysDiff <= 30) {
+ // 鍚屼竴涓湀鍐咃紝鎸夋棩鏄剧ず
+ for (let i = 0; i <= daysDiff; i++) {
+ const date = startDate.add(i, 'day')
+ xAxisData.push(date.format('MM/DD'))
+ seriesData.push(60 + Math.floor(Math.random() * 30)) // 鐢熸垚闅忔満鏁版嵁
+ }
+ } 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('YYYY/MM'))
+ seriesData.push(60 + Math.floor(Math.random() * 30)) // 鐢熸垚闅忔満鏁版嵁
+ }
+ }
+
+ chart.setOption({
+ xAxis: {
+ type: 'category',
+ data: xAxisData,
+ },
+ yAxis: {
+ type: 'value',
+ axisLabel: {
+ formatter: '{value}%',
+ },
+ },
+ series: [
+ {
+ data: seriesData,
+ type: 'bar',
+ },
+ ],
+ })
+ }
+
+ // 闂绫诲瀷鍒嗗竷鍥�
+ if (problemTypeChart.value) {
+ const chart = echarts.init(problemTypeChart.value)
+ chart.setOption({
+ series: [
+ {
+ type: 'pie',
+ data: [
+ { value: 30, name: '娌圭儫鍦ㄧ嚎鐩戞祴璁惧' },
+ { value: 25, name: '娌圭儫鍑�鍖栬鏂借澶�' },
+ { value: 20, name: '娌圭儫鍦ㄧ嚎鐩戞祴璁惧缁存姢' },
+ { value: 15, name: '娌圭儫鍑�鍖栬鏂借澶囩淮鎶�' },
+ { value: 10, 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)
+
+ // 璁$畻鏃堕棿鑼冨洿骞剁敓鎴恱杞存暟鎹�
+ 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)
+
+ // 璁$畻鏃堕棿鑼冨洿骞剁敓鎴恱杞存暟鎹�
+ 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}%',
+ },
+ },
+ ],
+ })
+ }
+
+ // 闂鏁存敼鐜囩粺璁″浘
+ if (rectificationRateChart.value) {
+ const chart = echarts.init(rectificationRateChart.value)
+ chart.setOption({
+ xAxis: {
+ type: 'category',
+ data: ['褰撴棩鏁存敼鐜�', '48灏忔椂鍐呮暣鏀圭巼', '缁煎悎鏁存敼鐜�', '瀹℃牳閫氳繃鐜�'],
+ },
+ yAxis: {
+ type: 'value',
+ axisLabel: {
+ formatter: '{value}%',
+ },
+ },
+ series: [
+ {
+ data: [
+ parseFloat(inspectionStats.value.sameDayRectificationRate),
+ parseFloat(inspectionStats.value.effectiveRectificationRate),
+ parseFloat(inspectionStats.value.comprehensiveRectificationRate),
+ parseFloat(inspectionStats.value.auditPassRate),
+ ],
+ type: 'bar',
+ itemStyle: {
+ color: function (params) {
+ const colors = ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C']
+ return colors[params.dataIndex]
+ },
+ },
+ },
+ ],
+ })
+ }
+}
+
+// 鐩戝惉绐楀彛澶у皬鍙樺寲
+const handleResize = () => {
+ // 閲嶆柊璋冩暣鍥捐〃澶у皬
+ if (inspectionTrendChart.value) {
+ echarts.init(inspectionTrendChart.value).resize()
+ }
+ if (problemTypeChart.value) {
+ echarts.init(problemTypeChart.value).resize()
+ }
+ 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鍙樺寲
+watch(
+ () => params.value,
+ () => {
+ refreshInspectionData()
+ },
+ { deep: true },
+)
+
+// 鐢熷懡鍛ㄦ湡
+onMounted(() => {
+ refreshInspectionData()
+ searchPunishment()
+ searchComplaint()
+ initCharts()
+ window.addEventListener('resize', handleResize)
+})
+
+// 缁勪欢鍗歌浇鏃舵竻鐞嗕簨浠剁洃鍚�
+onUnmounted(() => {
+ cleanup()
+})
+
+// 娓呯悊
+const cleanup = () => {
+ window.removeEventListener('resize', handleResize)
+}
+</script>
+
+<style scoped>
+.monitor-control {
+ 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;
+}
+
+.mr-2 {
+ margin-right: 10px;
+}
+
+.stats-sections {
+ display: flex;
+ 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);
+}
+
+.stats-section h3 {
+ margin-bottom: 15px;
+ font-size: 16px;
+ font-weight: 600;
+ color: #303133;
+}
+
+.stats-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 20px;
+ margin-bottom: 20px;
+}
+
+.stat-item {
+ background-color: #fff;
+ padding: 20px;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.progress-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+}
+
+.progress-label {
+ margin-top: 10px;
+ font-size: 14px;
+ font-weight: 500;
+ color: #606266;
+}
+
+.progress-value {
+ margin-top: 5px;
+ font-size: 18px;
+ font-weight: 600;
+ color: #409eff;
+}
+
+.chart-container {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
+ gap: 20px;
+ margin-bottom: 30px;
+}
+
+.chart-item {
+ 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;
+}
+
+.chart-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 15px;
+}
+
+.chart-summary {
+ font-size: 14px;
+ color: #606266;
+ font-weight: 500;
+}
+
+.chart {
+ width: 100%;
+ height: 300px;
+}
+
+.pagination-container {
+ margin-top: 20px;
+ display: flex;
+ justify-content: flex-end;
+}
+
+.dialog-footer {
+ display: flex;
+ justify-content: flex-end;
+}
+
+.import-container {
+ padding: 20px 0;
+}
+
+.import-tip {
+ margin-bottom: 20px;
+ font-size: 14px;
+ color: #606266;
+}
+
+.upload-demo {
+ margin-bottom: 20px;
+}
+</style>
diff --git a/src/views/inspection/report/ReportManage.vue b/src/views/inspection/report/ReportManage.vue
index 3695ea1..034beb0 100644
--- a/src/views/inspection/report/ReportManage.vue
+++ b/src/views/inspection/report/ReportManage.vue
@@ -1 +1,179 @@
-<template>宸℃煡璇勪及鎶ュ憡</template>
+<template>
+ <div class="report-manage-container">
+ <h2>宸℃煡璇勪及鎶ュ憡</h2>
+
+ <!-- 鏌ヨ鏉′欢鍖哄煙 -->
+ <div class="search-section">
+ <el-form :inline="true" :model="searchForm" class="search-form">
+ <el-form-item label="鍖哄幙">
+ <el-select v-model="searchForm.district" placeholder="璇烽�夋嫨鍖哄幙" style="width: 120px">
+ <el-option
+ v-for="district in districts"
+ :key="district.value"
+ :label="district.label"
+ :value="district.value"
+ />
+ </el-select>
+ </el-form-item>
+ <el-form-item>
+ <el-button type="primary" @click="searchReports">鏌ヨ</el-button>
+ </el-form-item>
+ </el-form>
+ </div>
+
+ <!-- 鎶ュ憡琛ㄦ牸鍖哄煙 -->
+ <div class="table-section">
+ <el-table :data="reportList" style="width: 100%">
+ <el-table-column prop="index" label="绱㈠紩缂栧彿" width="80" />
+ <el-table-column prop="name" label="鎶ュ憡鍚嶇О" min-width="200" />
+ <el-table-column prop="district" label="鍖哄幙" width="120" />
+ <el-table-column prop="reportMonth" label="鎶ュ憡鏃堕棿" width="120" />
+ <el-table-column prop="generateTime" label="鐢熸垚鏃堕棿" width="180" />
+ <el-table-column prop="auditStatus" label="瀹℃牳鐘舵��" width="100">
+ <template #default="scope">
+ <el-tag :type="scope.row.auditStatus === '宸插鏍�' ? 'success' : 'warning'">
+ {{ scope.row.auditStatus }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="120" fixed="right">
+ <template #default="scope">
+ <el-button size="small" type="primary" @click="viewReport(scope.row)">
+ 鏌ョ湅鎶ュ憡
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </div>
+
+ <!-- 鎶ュ憡棰勮鎶藉眽 -->
+ <el-drawer v-model="drawerVisible" title="鎶ュ憡棰勮" direction="rtl" size="80%">
+ <!-- <div class="report-preview">
+ <iframe :src="reportUrl" width="100%" height="800px" frameborder="0"></iframe>
+ </div> -->
+ <div ref="refWord" class="report-preview">
+ <div :id="`word-preview`"></div>
+ </div>
+ </el-drawer>
+ </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import {
+ exportDocx,
+ prepareDocxBlob,
+ preparePdf,
+ previewDocx,
+ downloadDocx,
+ print,
+} from '@/utils/doc'
+
+const refWord = ref(null)
+
+// 涓婃捣甯傚尯鍘垮垪琛�
+const districts = [
+ { value: 'xuhui', label: '寰愭眹鍖�' },
+ { value: 'changning', label: '闀垮畞鍖�' },
+ { value: 'jingan', label: '闈欏畨鍖�' },
+ { value: 'putuo', label: '鏅檧鍖�' },
+ { value: 'hongkou', label: '铏瑰彛鍖�' },
+ { value: 'yangpu', label: '鏉ㄦ郸鍖�' },
+ { value: 'minhang', label: '闂佃鍖�' },
+ { value: 'baoshan', label: '瀹濆北鍖�' },
+ { value: 'pudong', label: '娴︿笢鏂板尯' },
+ { value: 'jiading', label: '鍢夊畾鍖�' },
+ { value: 'jinshan', label: '閲戝北鍖�' },
+ { value: 'songjiang', label: '鏉炬睙鍖�' },
+ { value: 'qingpu', label: '闈掓郸鍖�' },
+ { value: 'fengxian', label: '濂夎搐鍖�' },
+ { value: 'chongming', label: '宕囨槑鍖�' },
+]
+
+// 鏌ヨ琛ㄥ崟
+const searchForm = ref({
+ district: 'xuhui', // 榛樿閫変腑寰愭眹鍖�
+})
+
+// 鎶ュ憡鍒楄〃鏁版嵁
+const reportList = ref([])
+
+// 鎶藉眽鐘舵��
+const drawerVisible = ref(false)
+// 鎶ュ憡棰勮URL
+const reportUrl = ref('')
+
+// 妯℃嫙鏁版嵁鐢熸垚
+const generateMockData = () => {
+ const data = []
+ const baseName = '椁愰ギ鐩戠绠�鎶�'
+ const areas = ['澶╅挜妗�', '寰愬姹�', '琛″北璺�', '榫欏崕']
+
+ for (let i = 1; i <= 10; i++) {
+ const area = areas[Math.floor(Math.random() * areas.length)]
+ const month = Math.floor(Math.random() * 12) + 1
+ const date = new Date()
+ date.setMonth(month - 1)
+
+ data.push({
+ index: i,
+ name: `${searchForm.value.district === 'xuhui' ? '寰愭眹鍖�' : '鍏朵粬鍖�'}${baseName}锛�${area}锛�-2023骞�${month}鏈坄,
+ district: searchForm.value.district === 'xuhui' ? '寰愭眹鍖�' : '鍏朵粬鍖�',
+ reportMonth: `2023骞�${month}鏈坄,
+ generateTime: new Date().toLocaleString('zh-CN'),
+ auditStatus: Math.random() > 0.5 ? '宸插鏍�' : '鏈鏍�',
+ })
+ }
+
+ return data
+}
+
+// 鏌ヨ鎶ュ憡
+const searchReports = () => {
+ reportList.value = generateMockData()
+}
+
+// 鏌ョ湅鎶ュ憡
+const viewReport = (row) => {
+ // 浣跨敤妯℃嫙鐨勬姤鍛婃枃浠�
+ reportUrl.value = '/寰愭眹鍖洪楗洃绠$畝鎶ワ紙澶╅挜妗ワ級-2023骞�8鏈�(1).docx'
+ drawerVisible.value = true
+
+ prepareDocxBlob(reportUrl.value).then((blob) => {
+ previewDocx(blob, document.getElementById(`word-preview`))
+ })
+}
+
+// 椤甸潰鍔犺浇鏃剁敓鎴愬垵濮嬫暟鎹�
+onMounted(() => {
+ searchReports()
+})
+</script>
+
+<style scoped>
+.report-manage-container {
+ padding: 20px;
+}
+
+.search-section {
+ margin-bottom: 20px;
+ padding: 20px;
+ background-color: #f5f7fa;
+ border-radius: 4px;
+}
+
+.search-form {
+ display: flex;
+ align-items: center;
+}
+
+.table-section {
+ background-color: #fff;
+ border-radius: 4px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.report-preview {
+ padding: 20px;
+}
+</style>
diff --git a/src/views/system/SystemManage.vue b/src/views/system/SystemManage.vue
index c429e89..188cfc4 100644
--- a/src/views/system/SystemManage.vue
+++ b/src/views/system/SystemManage.vue
@@ -60,108 +60,6 @@
/>
</div>
</el-tab-pane>
-
- <!-- 椁愰ギ搴楅摵绠$悊 -->
- <el-tab-pane label="椁愰ギ搴楅摵绠$悊" name="restaurants">
- <div class="tab-content">
- <!-- 鎼滅储鍜屾坊鍔犳寜閽� -->
- <div class="search-add-bar">
- <el-input
- v-model="restaurantSearchQuery"
- placeholder="鎼滅储搴楅摵"
- style="width: 200px"
- prefix-icon="el-icon-search"
- />
- <el-button type="primary" @click="openRestaurantDialog">
- <el-icon><Plus /></el-icon> 娣诲姞搴楅摵
- </el-button>
- </div>
-
- <!-- 搴楅摵琛ㄦ牸 -->
- <el-table :data="filteredRestaurants" style="width: 100%">
- <el-table-column prop="id" label="ID" width="80" />
- <el-table-column prop="name" label="搴楅摵鍚嶇О" />
- <el-table-column prop="address" label="鍦板潃" />
- <el-table-column prop="contact" label="鑱旂郴浜�" />
- <el-table-column prop="phone" label="鑱旂郴鐢佃瘽" />
- <el-table-column label="鎿嶄綔" width="250">
- <template #default="scope">
- <el-button size="small" @click="editRestaurant(scope.row)"> 缂栬緫 </el-button>
- <el-button size="small" type="danger" @click="deleteRestaurant(scope.row.id)">
- 鍒犻櫎
- </el-button>
- <el-button size="small" @click="manageDevices(scope.row)"> 璁惧绠$悊 </el-button>
- </template>
- </el-table-column>
- </el-table>
-
- <!-- 鍒嗛〉 -->
- <el-pagination
- v-model:current-page="restaurantCurrentPage"
- v-model:page-size="restaurantPageSize"
- :page-sizes="[10, 20, 50]"
- layout="total, sizes, prev, pager, next, jumper"
- :total="restaurants.length"
- style="margin-top: 20px"
- />
- </div>
- </el-tab-pane>
-
- <!-- 璁惧绠$悊 -->
- <el-tab-pane label="璁惧绠$悊" name="devices">
- <div class="tab-content" v-if="selectedRestaurant">
- <h3>{{ selectedRestaurant.name }} - 璁惧鍒楄〃</h3>
-
- <!-- 鎼滅储鍜屾坊鍔犳寜閽� -->
- <div class="search-add-bar">
- <el-input
- v-model="deviceSearchQuery"
- placeholder="鎼滅储璁惧"
- style="width: 200px"
- prefix-icon="el-icon-search"
- />
- <el-button type="primary" @click="openDeviceDialog">
- <el-icon><Plus /></el-icon> 娣诲姞璁惧
- </el-button>
- </div>
-
- <!-- 璁惧琛ㄦ牸 -->
- <el-table :data="filteredDevices" style="width: 100%">
- <el-table-column prop="id" label="ID" width="80" />
- <el-table-column prop="deviceId" label="璁惧缂栧彿" />
- <el-table-column prop="type" label="璁惧绫诲瀷" />
- <el-table-column prop="status" label="鐘舵��">
- <template #default="scope">
- <el-tag :type="scope.row.status === 'online' ? 'success' : 'danger'">
- {{ scope.row.status === 'online' ? '鍦ㄧ嚎' : '绂荤嚎' }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="installDate" label="瀹夎鏃ユ湡" />
- <el-table-column label="鎿嶄綔" width="200">
- <template #default="scope">
- <el-button size="small" @click="editDevice(scope.row)"> 缂栬緫 </el-button>
- <el-button size="small" type="danger" @click="deleteDevice(scope.row.id)">
- 鍒犻櫎
- </el-button>
- </template>
- </el-table-column>
- </el-table>
-
- <!-- 鍒嗛〉 -->
- <el-pagination
- v-model:current-page="deviceCurrentPage"
- v-model:page-size="devicePageSize"
- :page-sizes="[10, 20, 50]"
- layout="total, sizes, prev, pager, next, jumper"
- :total="devices.length"
- style="margin-top: 20px"
- />
- </div>
- <div class="tab-content" v-else>
- <el-empty description="璇峰厛閫夋嫨涓�涓楗簵閾�" />
- </div>
- </el-tab-pane>
</el-tabs>
</el-card>
--
Gitblit v1.9.3