Pārlūkot izejas kodu

feat(绩效管理): 重构考核指标模块并添加汇总图表功能

重构非干部考核指标数据结构,将核心指标和其他指标合并为可配置的指标组
添加指标编辑对话框,支持奖励和惩罚明细的录入
在月度考核汇总页面新增多个图表展示考核数据分布
优化响应式布局,适配不同屏幕尺寸
huoyi 1 mēnesi atpakaļ
vecāks
revīzija
c7f47d3951

+ 187 - 43
src/views/performanceManage/monthlyAssess/index.vue

@@ -212,40 +212,22 @@
212 212
         </el-row>
213 213
 
214 214
         <!-- 考核指标区域 -->
215
-        <div class="section-title blue">考核指标</div>
215
+        <div class="section-title blue" style="cursor: pointer;" @click="addIndicator(0)">考核指标</div>
216 216
         <div class="indicators-box">
217
-          <!-- 核心指标 -->
218
-          <div class="indicator-group">
219
-            <div class="indicator-group-title">核心指标</div>
220
-            <div class="indicator-item" v-for="(item, index) in nonCadreForm.coreIndicators" :key="index">
217
+          <div v-for="(group, groupIndex) in nonCadreForm.indicatorGroups" :key="groupIndex" class="indicator-group">
218
+            <div class="indicator-group-title">{{ group.title }}</div>
219
+            <div v-for="(item, itemIndex) in group.items" :key="itemIndex" class="indicator-item">
221 220
               <div class="indicator-name">{{ item.name }}</div>
222 221
               <div class="indicator-value">{{ item.score }}/次</div>
223 222
               <div class="indicator-count">{{ item.count }}次</div>
224 223
               <div class="indicator-source">{{ item.source }}</div>
225 224
               <div class="indicator-total">{{ item.total }}</div>
226 225
               <div class="indicator-actions">
227
-                <el-button type="primary" link icon="Edit" @click="editIndicator('core', index)">编辑</el-button>
228
-                <el-button type="danger" link icon="Delete" @click="deleteIndicator('core', index)">删除</el-button>
226
+                <el-button type="primary" link icon="Edit" @click="editIndicator(groupIndex, itemIndex)"></el-button>
227
+                <el-button type="danger" link icon="Delete" @click="deleteIndicator(groupIndex, itemIndex)"></el-button>
229 228
               </div>
230 229
             </div>
231
-            <el-button type="primary" plain icon="Plus" @click="addIndicator('core')" style="margin-top: 10px;">添加核心指标</el-button>
232
-          </div>
233
-
234
-          <!-- 其他指标 -->
235
-          <div class="indicator-group" style="margin-top: 20px;">
236
-            <div class="indicator-group-title">其他指标</div>
237
-            <div class="indicator-item" v-for="(item, index) in nonCadreForm.otherIndicators" :key="index">
238
-              <div class="indicator-name">{{ item.name }}</div>
239
-              <div class="indicator-value">{{ item.score }}/次</div>
240
-              <div class="indicator-count">{{ item.count }}次</div>
241
-              <div class="indicator-source">{{ item.source }}</div>
242
-              <div class="indicator-total">{{ item.total }}</div>
243
-              <div class="indicator-actions">
244
-                <el-button type="primary" link icon="Edit" @click="editIndicator('other', index)">编辑</el-button>
245
-                <el-button type="danger" link icon="Delete" @click="deleteIndicator('other', index)">删除</el-button>
246
-              </div>
247
-            </div>
248
-            <el-button type="primary" plain icon="Plus" @click="addIndicator('other')" style="margin-top: 10px;">添加其他指标</el-button>
230
+            <el-button type="primary" plain icon="Plus" @click="addIndicator(groupIndex)" style="margin-top: 10px;">添加指标</el-button>
249 231
           </div>
250 232
         </div>
251 233
 
@@ -523,6 +505,69 @@
523 505
         </div>
524 506
       </template>
525 507
     </el-dialog>
508
+
509
+    <!-- 第二个模态框:添加/编辑扣分指标 -->
510
+    <el-dialog :title="indicatorDialog.title" v-model="indicatorDialog.visible" width="60%">
511
+      <el-form label-width="150px" class="indicator-form">
512
+        <el-form-item label="指标名称">
513
+          <div style="display: flex; align-items: center; gap: 10px; width: 100%;">
514
+            <el-select v-model="indicatorDialog.form.name" placeholder="下拉菜单" filterable style="flex: 1;" @change="onIndicatorNameChange">
515
+              <el-option v-for="item in indicatorNameOptions" :key="item.value" :label="item.label" :value="item.value" />
516
+            </el-select>
517
+          
518
+          </div>
519
+        </el-form-item>
520
+
521
+        <el-form-item label="分值/单位">
522
+          <div style="display: flex; align-items: center; gap: 10px;">
523
+            <span style="font-size: 24px; font-weight: bold;">{{ indicatorDialog.form.score }}/次</span>
524
+           
525
+          </div>
526
+        </el-form-item>
527
+
528
+        <el-form-item label="发生次数">
529
+          <div style="display: flex; align-items: center; gap: 10px;">
530
+            <el-input-number v-model="indicatorDialog.form.count" :min="1" style="width: 200px;" @change="updateTotal" />
531
+          
532
+          </div>
533
+        </el-form-item>
534
+
535
+        <el-form-item label="检查部门">
536
+          <el-select v-model="indicatorDialog.form.source" placeholder="请选择" style="width: 200px;">
537
+            <el-option v-for="item in sourceOptions" :key="item.value" :label="item.label" :value="item.value" />
538
+          </el-select>
539
+        </el-form-item>
540
+
541
+        <el-form-item label="奖励明细">
542
+          <el-input v-model="indicatorDialog.form.rewardDetails" type="textarea" :rows="2" />
543
+        </el-form-item>
544
+
545
+        <el-form-item label="奖励">
546
+          <div style="display: flex; align-items: center; gap: 10px;">
547
+            <el-input-number v-model="indicatorDialog.form.reward" :min="0" :precision="2" style="width: 200px;" />
548
+            <span>元</span>
549
+          </div>
550
+        </el-form-item>
551
+
552
+        <el-form-item label="惩罚明细">
553
+          <el-input v-model="indicatorDialog.form.penaltyDetails" type="textarea" :rows="2" />
554
+        </el-form-item>
555
+
556
+        <el-form-item label="扣罚">
557
+          <div style="display: flex; align-items: center; gap: 10px;">
558
+            <el-input-number v-model="indicatorDialog.form.penalty" :min="0" :precision="2" style="width: 200px;" />
559
+            <span>元</span>
560
+          </div>
561
+        </el-form-item>
562
+      </el-form>
563
+
564
+      <template #footer>
565
+        <div class="dialog-footer">
566
+          <el-button @click="indicatorDialog.visible = false">取消</el-button>
567
+          <el-button type="primary" @click="saveIndicator">确定</el-button>
568
+        </div>
569
+      </template>
570
+    </el-dialog>
526 571
   </div>
527 572
 </template>
528 573
 
@@ -585,14 +630,43 @@ const nonCadreForm = reactive({
585 630
   applicationMethodRemark: '',
586 631
   isExempted: false,
587 632
   exemptionRemark: '',
588
-  coreIndicators: [
589
-    { name: '核心指标细项', score: -3, count: 3, source: 'SOC', total: -9 }
590
-  ],
591
-  otherIndicators: [
592
-    { name: '其他指标细项', score: -3, count: 1, source: '品控', total: -3 }
633
+  // 两级结构的考核指标
634
+  indicatorGroups: [
635
+    {
636
+      title: '核心指标',
637
+      items: [
638
+        { name: '核心指标细项', score: -3, count: 3, source: 'SOC', total: -9, rewardDetails: '', reward: 0, penaltyDetails: '', penalty: 0 }
639
+      ]
640
+    },
641
+    {
642
+      title: '其他指标',
643
+      items: [
644
+        { name: '其他指标细项', score: -3, count: 1, source: '品控', total: -3, rewardDetails: '', reward: 0, penaltyDetails: '', penalty: 0 }
645
+      ]
646
+    }
593 647
   ]
594 648
 })
595 649
 
650
+// 第二个模态框状态
651
+const indicatorDialog = reactive({
652
+  visible: false,
653
+  title: '',
654
+  mode: 'add', // add or edit
655
+  groupIndex: null,
656
+  itemIndex: null,
657
+  form: {
658
+    name: '',
659
+    score: -3,
660
+    count: 1,
661
+    source: '其他',
662
+    total: 0,
663
+    rewardDetails: '',
664
+    reward: 0,
665
+    penaltyDetails: '',
666
+    penalty: 0
667
+  }
668
+})
669
+
596 670
 // 干部表单数据
597 671
 const cadreForm = reactive({
598 672
   employeeName: '',
@@ -644,6 +718,21 @@ const applicationMethodOptions = [
644 718
   { label: '特殊处理', value: 'special' }
645 719
 ]
646 720
 
721
+// 指标名称选项
722
+const indicatorNameOptions = [
723
+  { label: '核心指标细项', value: '核心指标细项', score: -3 },
724
+  { label: '其他指标细项', value: '其他指标细项', score: -3 },
725
+  { label: '红线指标细项', value: '红线指标细项', score: -5 },
726
+  { label: '安全指标细项', value: '安全指标细项', score: -4 }
727
+]
728
+
729
+// 检查部门选项
730
+const sourceOptions = [
731
+  { label: 'SOC', value: 'SOC' },
732
+  { label: '品控', value: '品控' },
733
+  { label: '其他', value: '其他' }
734
+]
735
+
647 736
 // 非干部表单验证规则
648 737
 const nonCadreRules = {
649 738
   employeeName: [{ required: true, message: '员工姓名不能为空', trigger: 'blur' }],
@@ -820,32 +909,77 @@ const getResultText = (value) => {
820 909
   return map[value] || ''
821 910
 }
822 911
 
823
-// 添加指标
824
-const addIndicator = (type) => {
825
-  ElMessage.success('添加功能开发中')
912
+// 添加指标 - 打开新增模态框
913
+const addIndicator = (groupIndex) => {
914
+  indicatorDialog.visible = true
915
+  indicatorDialog.title = '添加扣分指标'
916
+  indicatorDialog.mode = 'add'
917
+  indicatorDialog.groupIndex = groupIndex
918
+  indicatorDialog.itemIndex = null
919
+  indicatorDialog.form = {
920
+    name: '',
921
+    score: -3,
922
+    count: 1,
923
+    source: '其他',
924
+    total: 0,
925
+    rewardDetails: '',
926
+    reward: 0,
927
+    penaltyDetails: '',
928
+    penalty: 0
929
+  }
826 930
 }
827 931
 
828
-// 编辑指标
829
-const editIndicator = (type, index) => {
830
-  ElMessage.success('编辑功能开发中')
932
+// 编辑指标 - 打开编辑模态框
933
+const editIndicator = (groupIndex, itemIndex) => {
934
+  const item = nonCadreForm.indicatorGroups[groupIndex].items[itemIndex]
935
+  indicatorDialog.visible = true
936
+  indicatorDialog.title = '编辑扣分指标'
937
+  indicatorDialog.mode = 'edit'
938
+  indicatorDialog.groupIndex = groupIndex
939
+  indicatorDialog.itemIndex = itemIndex
940
+  indicatorDialog.form = { ...item }
831 941
 }
832 942
 
833 943
 // 删除指标
834
-const deleteIndicator = (type, index) => {
944
+const deleteIndicator = (groupIndex, itemIndex) => {
835 945
   ElMessageBox.confirm('确认删除该指标吗?', '提示', {
836 946
     confirmButtonText: '确定',
837 947
     cancelButtonText: '取消',
838 948
     type: 'warning'
839 949
   }).then(() => {
840
-    if (type === 'core') {
841
-      nonCadreForm.coreIndicators.splice(index, 1)
842
-    } else {
843
-      nonCadreForm.otherIndicators.splice(index, 1)
844
-    }
950
+    nonCadreForm.indicatorGroups[groupIndex].items.splice(itemIndex, 1)
845 951
     ElMessage.success('删除成功')
846 952
   }).catch(() => {})
847 953
 }
848 954
 
955
+// 指标名称变化时更新分值
956
+const onIndicatorNameChange = (value) => {
957
+  const selected = indicatorNameOptions.find(item => item.value === value)
958
+  if (selected) {
959
+    indicatorDialog.form.score = selected.score
960
+    updateTotal()
961
+  }
962
+}
963
+
964
+// 更新总分
965
+const updateTotal = () => {
966
+  indicatorDialog.form.total = indicatorDialog.form.score * indicatorDialog.form.count
967
+}
968
+
969
+// 保存指标
970
+const saveIndicator = () => {
971
+  if (indicatorDialog.mode === 'add') {
972
+    // 新增
973
+    nonCadreForm.indicatorGroups[indicatorDialog.groupIndex].items.push({ ...indicatorDialog.form })
974
+    ElMessage.success('添加成功')
975
+  } else {
976
+    // 编辑
977
+    Object.assign(nonCadreForm.indicatorGroups[indicatorDialog.groupIndex].items[indicatorDialog.itemIndex], indicatorDialog.form)
978
+    ElMessage.success('修改成功')
979
+  }
980
+  indicatorDialog.visible = false
981
+}
982
+
849 983
 // 监听Tab切换
850 984
 const handleTabChange = () => {
851 985
   queryParams.pageNum = 1
@@ -946,7 +1080,7 @@ onMounted(() => {
946 1080
 
947 1081
 .section-title.blue {
948 1082
   color: #409eff;
949
-  border-left: 4px solid #409eff;
1083
+  // border-left: 4px solid #409eff;
950 1084
 }
951 1085
 
952 1086
 .indicators-box {
@@ -1012,4 +1146,14 @@ onMounted(() => {
1012 1146
 .exemption-hint {
1013 1147
   font-size: 12px;
1014 1148
 }
1149
+
1150
+.indicator-form {
1151
+  padding: 20px 0;
1152
+}
1153
+
1154
+.indicator-form :deep(.el-form-item__label) {
1155
+  font-size: 20px;
1156
+  font-weight: 500;
1157
+  color: #303133;
1158
+}
1015 1159
 </style>

+ 279 - 2
src/views/performanceManage/monthlyAssessSum/index.vue

@@ -64,9 +64,9 @@
64 64
       </div>
65 65
     </div>
66 66
 
67
-    <!-- 干部月度考核分类结果汇总 -->
67
+    <!-- 干部月度考核分类结果汇总 -->
68 68
     <div class="main-section">
69
-      <h2 class="section-title">干部月度考核分类结果汇总</h2>
69
+      <h2 class="section-title">干部月度考核分类结果汇总</h2>
70 70
       
71 71
       <!-- 汇总统计表格 -->
72 72
       <el-card class="summary-table-card">
@@ -104,6 +104,78 @@
104 104
         </el-card>
105 105
       </div>
106 106
     </div>
107
+
108
+    <!-- 第一个遍历:表格和两个饼状图 -->
109
+    <div class="traversal-section">
110
+      <div v-for="(item, index) in traversalData1" :key="index" class="traversal-container">
111
+        <div class="traversal-header">{{ item.title }}</div>
112
+        <div class="traversal-content">
113
+          <!-- 左边表格 -->
114
+          <div class="table-section">
115
+            <el-table :data="item.tableData" border style="width: 100%;">
116
+              <el-table-column prop="brigade" label="大队" align="center" min-width="100" />
117
+              <el-table-column prop="assessmentGroup" label="考核组" align="center" min-width="100" />
118
+              <el-table-column prop="groupCount" label="考核组人数" align="center" min-width="120" />
119
+              <el-table-column prop="calculatedImprovement" label="测算待改进人数" align="center" min-width="140" />
120
+              <el-table-column prop="improvementCount" label="待改进人数" align="center" min-width="120" />
121
+              <el-table-column prop="exemption1" label="豁免" align="center" min-width="80" />
122
+              <el-table-column prop="actualImprovement" label="实际待改进人数" align="center" min-width="140" />
123
+              <el-table-column prop="unqualifiedCount" label="不称职人数" align="center" min-width="120" />
124
+              <el-table-column prop="exemption2" label="豁免" align="center" min-width="80" />
125
+              <el-table-column prop="actualUnqualified" label="实际不称职人数" align="center" min-width="140" />
126
+            </el-table>
127
+          </div>
128
+          
129
+          <!-- 右边两个饼状图 -->
130
+          <div class="chart-section">
131
+            <div class="pie-chart-container">
132
+              <div class="pie-chart-title">考核结果分布</div>
133
+              <div :ref="el => setTraversalChartRef(el, `pieChart1_${index}`)" class="pie-chart"></div>
134
+            </div>
135
+            <div class="pie-chart-container">
136
+              <div class="pie-chart-title">改进情况分布</div>
137
+              <div :ref="el => setTraversalChartRef(el, `pieChart2_${index}`)" class="pie-chart"></div>
138
+            </div>
139
+          </div>
140
+        </div>
141
+      </div>
142
+    </div>
143
+
144
+    <!-- 第二个遍历:表格、柱状图和饼状图 -->
145
+    <div class="traversal-section">
146
+      <div v-for="(item, index) in traversalData2" :key="index" class="traversal-container">
147
+        <div class="traversal-header">{{ item.title }}</div>
148
+        <div class="traversal-content">
149
+          <!-- 左边表格 -->
150
+          <div class="table-section">
151
+            <el-table :data="item.tableData" border style="width: 100%;">
152
+              <el-table-column prop="brigade" label="大队" align="center" min-width="100" />
153
+              <el-table-column prop="assessmentGroup" label="考核组" align="center" min-width="100" />
154
+              <el-table-column prop="groupCount" label="考核组人数" align="center" min-width="120" />
155
+              <el-table-column prop="calculatedImprovement" label="测算待改进人数" align="center" min-width="140" />
156
+              <el-table-column prop="improvementCount" label="待改进人数" align="center" min-width="120" />
157
+              <el-table-column prop="exemption1" label="豁免" align="center" min-width="80" />
158
+              <el-table-column prop="actualImprovement" label="实际待改进人数" align="center" min-width="140" />
159
+              <el-table-column prop="unqualifiedCount" label="不称职人数" align="center" min-width="120" />
160
+              <el-table-column prop="exemption2" label="豁免" align="center" min-width="80" />
161
+              <el-table-column prop="actualUnqualified" label="实际不称职人数" align="center" min-width="140" />
162
+            </el-table>
163
+          </div>
164
+          
165
+          <!-- 右边柱状图和饼状图 -->
166
+          <div class="chart-section">
167
+            <div class="bar-chart-container">
168
+              <div class="chart-title">考核分数分布</div>
169
+              <div :ref="el => setTraversalChartRef(el, `barChart_${index}`)" class="bar-chart"></div>
170
+            </div>
171
+            <div class="pie-chart-container">
172
+              <div class="pie-chart-title">岗位分布</div>
173
+              <div :ref="el => setTraversalChartRef(el, `pieChart3_${index}`)" class="pie-chart"></div>
174
+            </div>
175
+          </div>
176
+        </div>
177
+      </div>
178
+    </div>
107 179
   </div>
108 180
 </template>
109 181
 
@@ -133,6 +205,9 @@ const brigadePieChart2 = ref(null)
133 205
 const positionPieChart1 = ref(null)
134 206
 const positionPieChart2 = ref(null)
135 207
 
208
+// 遍历图表引用
209
+const traversalChartsRefs = ref({})
210
+
136 211
 // 表格数据
137 212
 const summaryTableData = ref([
138 213
   { range: '90-100分', simulationScore: '85', team1: '15', team2: '20', team3: '25' },
@@ -155,6 +230,30 @@ const classificationTableData = ref([
155 230
   }
156 231
 ])
157 232
 
233
+// 遍历数据
234
+const traversalData1 = ref([
235
+  {
236
+    title: '大队考核统计',
237
+    tableData: [
238
+      { brigade: '一大队', assessmentGroup: '一组', groupCount: 50, calculatedImprovement: 8, improvementCount: 6, exemption1: 1, actualImprovement: 5, unqualifiedCount: 2, exemption2: 0, actualUnqualified: 2 },
239
+      { brigade: '一大队', assessmentGroup: '二组', groupCount: 45, calculatedImprovement: 7, improvementCount: 5, exemption1: 1, actualImprovement: 4, unqualifiedCount: 1, exemption2: 0, actualUnqualified: 1 },
240
+      { brigade: '二大队', assessmentGroup: '一组', groupCount: 55, calculatedImprovement: 9, improvementCount: 7, exemption1: 1, actualImprovement: 6, unqualifiedCount: 3, exemption2: 1, actualUnqualified: 2 },
241
+      { brigade: '二大队', assessmentGroup: '二组', groupCount: 48, calculatedImprovement: 6, improvementCount: 5, exemption1: 0, actualImprovement: 5, unqualifiedCount: 2, exemption2: 0, actualUnqualified: 2 }
242
+    ]
243
+  }
244
+])
245
+
246
+const traversalData2 = ref([
247
+  {
248
+    title: '岗位考核统计',
249
+    tableData: [
250
+      { brigade: '安检员', assessmentGroup: '一组', groupCount: 60, calculatedImprovement: 10, improvementCount: 8, exemption1: 1, actualImprovement: 7, unqualifiedCount: 3, exemption2: 0, actualUnqualified: 3 },
251
+      { brigade: '安检员', assessmentGroup: '二组', groupCount: 55, calculatedImprovement: 8, improvementCount: 6, exemption1: 1, actualImprovement: 5, unqualifiedCount: 2, exemption2: 0, actualUnqualified: 2 },
252
+      { brigade: '设备操作员', assessmentGroup: '一组', groupCount: 40, calculatedImprovement: 5, improvementCount: 4, exemption1: 0, actualImprovement: 4, unqualifiedCount: 1, exemption2: 0, actualUnqualified: 1 }
253
+    ]
254
+  }
255
+])
256
+
158 257
 // 图表实例
159 258
 let overallBarChartInstance = null
160 259
 let participantPieChartInstance = null
@@ -281,6 +380,91 @@ const initCharts = () => {
281 380
   })
282 381
 }
283 382
 
383
+// 设置遍历图表引用
384
+const setTraversalChartRef = (el, key) => {
385
+  if (el) {
386
+    traversalChartsRefs.value[key] = el
387
+  }
388
+}
389
+
390
+// 初始化遍历图表
391
+const initTraversalCharts = () => {
392
+  nextTick(() => {
393
+    // 第一个遍历的饼状图
394
+    traversalData1.value.forEach((item, index) => {
395
+      // 饼状图1:考核结果分布
396
+      const pieChart1Key = `pieChart1_${index}`
397
+      if (traversalChartsRefs.value[pieChart1Key]) {
398
+        const pieChart1 = echarts.init(traversalChartsRefs.value[pieChart1Key])
399
+        pieChart1.setOption({
400
+          tooltip: { trigger: 'item' },
401
+          series: [{
402
+            type: 'pie',
403
+            radius: '70%',
404
+            data: [
405
+              { value: 120, name: '优秀' },
406
+              { value: 280, name: '合格' },
407
+              { value: 75, name: '待改进' }
408
+            ]
409
+          }]
410
+        })
411
+      }
412
+
413
+      // 饼状图2:改进情况分布
414
+      const pieChart2Key = `pieChart2_${index}`
415
+      if (traversalChartsRefs.value[pieChart2Key]) {
416
+        const pieChart2 = echarts.init(traversalChartsRefs.value[pieChart2Key])
417
+        pieChart2.setOption({
418
+          tooltip: { trigger: 'item' },
419
+          series: [{
420
+            type: 'pie',
421
+            radius: '70%',
422
+            data: [
423
+              { value: 15, name: '已改进' },
424
+              { value: 60, name: '待改进' },
425
+              { value: 5, name: '未改进' }
426
+            ]
427
+          }]
428
+        })
429
+      }
430
+    })
431
+
432
+    // 第二个遍历的图表
433
+    traversalData2.value.forEach((item, index) => {
434
+      // 柱状图:考核分数分布
435
+      const barChartKey = `barChart_${index}`
436
+      if (traversalChartsRefs.value[barChartKey]) {
437
+        const barChart = echarts.init(traversalChartsRefs.value[barChartKey])
438
+        barChart.setOption({
439
+          tooltip: { trigger: 'axis' },
440
+          xAxis: { type: 'category', data: ['90-100分', '80-89分', '70-79分', '60-69分', '60分以下'] },
441
+          yAxis: { type: 'value' },
442
+          series: [{ type: 'bar', data: [85, 120, 95, 60, 20], itemStyle: { color: '#3b82f6' } }]
443
+        })
444
+      }
445
+
446
+      // 饼状图:岗位分布
447
+      const pieChart3Key = `pieChart3_${index}`
448
+      if (traversalChartsRefs.value[pieChart3Key]) {
449
+        const pieChart3 = echarts.init(traversalChartsRefs.value[pieChart3Key])
450
+        pieChart3.setOption({
451
+          tooltip: { trigger: 'item' },
452
+          series: [{
453
+            type: 'pie',
454
+            radius: '70%',
455
+            data: [
456
+              { value: 150, name: '安检员' },
457
+              { value: 120, name: '设备操作员' },
458
+              { value: 80, name: '管理人员' },
459
+              { value: 50, name: '其他' }
460
+            ]
461
+          }]
462
+        })
463
+      }
464
+    })
465
+  })
466
+}
467
+
284 468
 // 窗口大小变化时重绘图表
285 469
 const handleResize = () => {
286 470
   const charts = [
@@ -334,6 +518,7 @@ const handleExport = async () => {
334 518
 
335 519
 onMounted(() => {
336 520
   initCharts()
521
+  initTraversalCharts()
337 522
   window.addEventListener('resize', handleResize)
338 523
   getList()
339 524
 })
@@ -441,6 +626,70 @@ onUnmounted(() => {
441 626
   }
442 627
 }
443 628
 
629
+/* 遍历样式 */
630
+.traversal-section {
631
+  margin-bottom: 30px;
632
+}
633
+
634
+.traversal-container {
635
+  border: 1px solid #dcdfe6;
636
+  border-radius: 8px;
637
+  overflow: hidden;
638
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
639
+  margin-bottom: 20px;
640
+}
641
+
642
+.traversal-header {
643
+  background-color: #f5f7fa;
644
+  padding: 15px 20px;
645
+  font-size: 18px;
646
+  font-weight: 600;
647
+  color: #303133;
648
+  border-bottom: 1px solid #dcdfe6;
649
+  text-align: left;
650
+}
651
+
652
+.traversal-content {
653
+  display: grid;
654
+  grid-template-columns: 1fr 1fr;
655
+  gap: 20px;
656
+  padding: 20px;
657
+}
658
+
659
+.table-section {
660
+  min-height: 400px;
661
+}
662
+
663
+.chart-section {
664
+  display: grid;
665
+  grid-template-columns: 1fr 1fr;
666
+  gap: 15px;
667
+  height: 400px;
668
+}
669
+
670
+.pie-chart-container, .bar-chart-container {
671
+  display: flex;
672
+  flex-direction: column;
673
+  border: 1px solid #e4e7ed;
674
+  border-radius: 6px;
675
+  padding: 10px;
676
+  background-color: #fff;
677
+}
678
+
679
+.pie-chart-title, .chart-title {
680
+  font-size: 14px;
681
+  font-weight: 600;
682
+  color: #606266;
683
+  margin-bottom: 10px;
684
+  text-align: center;
685
+}
686
+
687
+.pie-chart, .bar-chart {
688
+  flex: 1;
689
+  min-height: 300px;
690
+}
691
+
692
+/* 响应式布局 */
444 693
 @media (max-width: 1200px) {
445 694
   .chart-row {
446 695
     grid-template-columns: 1fr;
@@ -449,5 +698,33 @@ onUnmounted(() => {
449 698
   .pie-charts-container {
450 699
     grid-template-columns: 1fr;
451 700
   }
701
+  
702
+  .traversal-content {
703
+    grid-template-columns: 1fr;
704
+    gap: 15px;
705
+  }
706
+  
707
+  .chart-section {
708
+    grid-template-columns: 1fr;
709
+    height: auto;
710
+  }
711
+  
712
+  .pie-chart, .bar-chart {
713
+    min-height: 250px;
714
+  }
715
+}
716
+
717
+@media (max-width: 768px) {
718
+  .traversal-content {
719
+    padding: 15px;
720
+  }
721
+  
722
+  .chart-section {
723
+    gap: 10px;
724
+  }
725
+  
726
+  .pie-chart-container, .bar-chart-container {
727
+    padding: 8px;
728
+  }
452 729
 }
453 730
 </style>