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