Parcourir la source

Merge branch 'employeeScreen' of https://git.sundot.cn/chongqing/chongqing-web into employeeScreen

lixiangrui il y a 4 semaines
Parent
commit
bea8043cb2

+ 2 - 2
src/api/score/index.js

@@ -27,8 +27,8 @@ export function exportDimension(params) {
27
 export function listIndicator(params) {
27
 export function listIndicator(params) {
28
   return request({ url: '/score/indicator/list', method: 'get', params })
28
   return request({ url: '/score/indicator/list', method: 'get', params })
29
 }
29
 }
30
-export function treeIndicator(dimensionId) {
31
-  return request({ url: '/score/indicator/tree', method: 'get', params: { dimensionId } })
30
+export function treeIndicator(params) {
31
+  return request({ url: '/score/indicator/tree', method: 'get', params })
32
 }
32
 }
33
 export function getIndicator(id) {
33
 export function getIndicator(id) {
34
   return request({ url: `/score/indicator/${id}`, method: 'get' })
34
   return request({ url: `/score/indicator/${id}`, method: 'get' })

+ 3 - 2
src/api/system/user.js

@@ -137,10 +137,11 @@ export function updateAuthRole(data) {
137
 }
137
 }
138
 
138
 
139
 // 查询部门下拉树结构
139
 // 查询部门下拉树结构
140
-export function deptTreeSelect() {
140
+export function deptTreeSelect(params) {
141
   return request({
141
   return request({
142
     url: '/system/user/deptTree',
142
     url: '/system/user/deptTree',
143
-    method: 'get'
143
+    method: 'get',
144
+    params: params
144
   })
145
   })
145
 }
146
 }
146
 //获取所有部门和班组下人员
147
 //获取所有部门和班组下人员

+ 9 - 3
src/views/portraitManagement/components/page.vue

@@ -75,8 +75,9 @@ const handleTabClick = (index) => {
75
     align-items: center;
75
     align-items: center;
76
     height: 88px;
76
     height: 88px;
77
     background: #2b394d86;
77
     background: #2b394d86;
78
-    position: relative;
79
     margin-bottom: 2px;
78
     margin-bottom: 2px;
79
+    position: relative;
80
+    justify-content: center;
80
     &::before {
81
     &::before {
81
       content: '';
82
       content: '';
82
       position: absolute;
83
       position: absolute;
@@ -91,8 +92,13 @@ const handleTabClick = (index) => {
91
     }
92
     }
92
   }
93
   }
93
   .left-tab {
94
   .left-tab {
95
+    position: absolute;
96
+    left: 0;
97
+    top: 0;
94
     display: flex;
98
     display: flex;
95
     padding: 0 20px;
99
     padding: 0 20px;
100
+    z-index: 1;
101
+    height: 88px;
96
     .tab-item {
102
     .tab-item {
97
       padding: 0 20px;
103
       padding: 0 20px;
98
       height: 88px;
104
       height: 88px;
@@ -101,7 +107,7 @@ const handleTabClick = (index) => {
101
       font-size: 16px;
107
       font-size: 16px;
102
       cursor: pointer;
108
       cursor: pointer;
103
       transition: all 0.3s;
109
       transition: all 0.3s;
104
-     
110
+
105
       &:hover {
111
       &:hover {
106
         background: rgba(15, 70, 250, 0.1);
112
         background: rgba(15, 70, 250, 0.1);
107
         color: #fff;
113
         color: #fff;
@@ -114,13 +120,13 @@ const handleTabClick = (index) => {
114
     }
120
     }
115
   }
121
   }
116
   .title {
122
   .title {
117
-    flex: 1;
118
     height: 88px;
123
     height: 88px;
119
     line-height: 88px;
124
     line-height: 88px;
120
     text-align: center;
125
     text-align: center;
121
     color: #fff;
126
     color: #fff;
122
     font-size: 48px;
127
     font-size: 48px;
123
     font-weight: bold;
128
     font-weight: bold;
129
+    white-space: nowrap;
124
   }
130
   }
125
   .content {
131
   .content {
126
     flex: 1;
132
     flex: 1;

+ 157 - 0
src/views/portraitManagement/components/rollingTable.vue

@@ -0,0 +1,157 @@
1
+<template>
2
+  <div class="rolling-table">
3
+    <div class="table-header">
4
+      <div class="header-row">
5
+        <div class="header-cell" v-for="(col, index) in columns" :key="index">{{ col.label }}</div>
6
+      </div>
7
+    </div>
8
+    <div class="table-body">
9
+      <div class="scroll-content" ref="scrollContentRef">
10
+        <div class="body-row" v-for="(row, rowIndex) in scrollData" :key="rowIndex">
11
+          <div class="body-cell" v-for="(col, cellIndex) in columns" :key="cellIndex">{{ row[col.prop] }}</div>
12
+        </div>
13
+      </div>
14
+    </div>
15
+  </div>
16
+</template>
17
+
18
+<script setup>
19
+import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
20
+
21
+const props = defineProps({
22
+  columns: {
23
+    type: Array,
24
+    default: () => []
25
+  },
26
+  data: {
27
+    type: Array,
28
+    default: () => []
29
+  },
30
+  duration: {
31
+    type: Number,
32
+    default: 20
33
+  }
34
+})
35
+
36
+const scrollContentRef = ref(null)
37
+let animationId = null
38
+
39
+const scrollData = computed(() => {
40
+  if (!props.data || props.data.length === 0) return []
41
+  return [...props.data, ...props.data]
42
+})
43
+
44
+const startScroll = () => {
45
+  const content = scrollContentRef.value
46
+  if (!content || props.data.length <= 0) return
47
+
48
+  let lastTime = performance.now()
49
+  let currentScrollY = 0
50
+  content.style.transform = 'translateY(0px)'
51
+
52
+  const scroll = (currentTime) => {
53
+    if (!content) {
54
+      animationId = requestAnimationFrame(scroll)
55
+      return
56
+    }
57
+
58
+    const halfHeight = content.scrollHeight / 2
59
+    if (halfHeight <= 0) {
60
+      animationId = requestAnimationFrame(scroll)
61
+      return
62
+    }
63
+
64
+    const step = halfHeight / (props.duration * 60)
65
+    const delta = currentTime - lastTime
66
+    lastTime = currentTime
67
+
68
+    currentScrollY += step * (delta / 16.67)
69
+
70
+    if (currentScrollY >= halfHeight) {
71
+      currentScrollY = 0
72
+    }
73
+
74
+    content.style.transform = `translateY(${-currentScrollY}px)`
75
+
76
+    animationId = requestAnimationFrame(scroll)
77
+  }
78
+
79
+  animationId = requestAnimationFrame(scroll)
80
+}
81
+
82
+const stopScroll = () => {
83
+  if (animationId) {
84
+    cancelAnimationFrame(animationId)
85
+    animationId = null
86
+  }
87
+}
88
+
89
+onMounted(() => {
90
+  if (props.data.length > 0) {
91
+    nextTick(() => {
92
+      startScroll()
93
+    })
94
+  }
95
+})
96
+
97
+onUnmounted(() => {
98
+  stopScroll()
99
+})
100
+</script>
101
+
102
+<style lang="scss" scoped>
103
+.rolling-table {
104
+  width: 100%;
105
+  overflow: hidden;
106
+}
107
+
108
+.table-header {
109
+  .header-row {
110
+    display: flex;
111
+    background: linear-gradient(90deg, #1a2058, #1c2a6e);
112
+    border-radius: 6px;
113
+    margin-bottom: 4px;
114
+  }
115
+  .header-cell {
116
+    flex: 1;
117
+    padding: 10px 6px;
118
+    text-align: center;
119
+    color: #70CFE7;
120
+    font-size: 13px;
121
+    font-weight: bold;
122
+    white-space: nowrap;
123
+  }
124
+}
125
+
126
+.table-body {
127
+  overflow: hidden;
128
+  max-height: 300px;
129
+}
130
+
131
+.scroll-content {
132
+  display: flex;
133
+  flex-direction: column;
134
+  gap: 2px;
135
+  will-change: transform;
136
+}
137
+
138
+.body-row {
139
+  display: flex;
140
+  background: linear-gradient(90deg, #1E2553, #242D66, #381C4F);
141
+  border-radius: 4px;
142
+  margin-bottom: 2px;
143
+
144
+  &:last-child {
145
+    margin-bottom: 0;
146
+  }
147
+}
148
+
149
+.body-cell {
150
+  flex: 1;
151
+  padding: 8px 6px;
152
+  text-align: center;
153
+  color: #fff;
154
+  font-size: 13px;
155
+  white-space: nowrap;
156
+}
157
+</style>

+ 70 - 67
src/views/portraitManagement/groupProfile/component/profile.vue

@@ -10,50 +10,21 @@
10
             <div class="radar-list">
10
             <div class="radar-list">
11
               <div class="radar-item" v-for="(item, index) in radarData" :key="index">
11
               <div class="radar-item" v-for="(item, index) in radarData" :key="index">
12
                 <span class="item-label">{{ item.name }}</span>
12
                 <span class="item-label">{{ item.name }}</span>
13
-                <div class="progress-bar">
14
-                  <div class="progress-fill" :style="{ width: item.value + '%' }"></div>
13
+                <div class="progress-row">
14
+                  <div class="progress-bar">
15
+                    <div class="progress-fill" :style="{ width: item.value + '%', background: `linear-gradient(to right, transparent, ${item.color})` }">
16
+                      <span class="progress-end"></span>
17
+                    </div>
18
+                  </div>
19
+                  <span class="item-value">{{ item.value }}</span>
15
                 </div>
20
                 </div>
16
-                <span class="item-value">{{ item.value }}</span>
17
               </div>
21
               </div>
18
             </div>
22
             </div>
19
           </div>
23
           </div>
20
         </InfoCard>
24
         </InfoCard>
21
         <InfoCard title="团队成员">
25
         <InfoCard title="团队成员">
22
           <div class="table-container">
26
           <div class="table-container">
23
-            <table class="member-table">
24
-              <thead>
25
-                <tr>
26
-                  <th>姓名</th>
27
-                  <th>年龄</th>
28
-                  <th>司龄</th>
29
-                  <th>性别</th>
30
-                  <th>民族</th>
31
-                  <th>政治面貌</th>
32
-                  <th>职务</th>
33
-                  <th>岗位资质</th>
34
-                  <th>职业技能等级</th>
35
-                  <th>开机年限</th>
36
-                  <th>平均年限</th>
37
-                  <th>综合得分</th>
38
-                </tr>
39
-              </thead>
40
-              <tbody>
41
-                <tr v-for="(member, index) in teamMembers" :key="index">
42
-                  <td>{{ member.name }}</td>
43
-                  <td>{{ member.age }}</td>
44
-                  <td>{{ member.seniority }}</td>
45
-                  <td>{{ member.gender }}</td>
46
-                  <td>{{ member.nation }}</td>
47
-                  <td>{{ member.political }}</td>
48
-                  <td>{{ member.position }}</td>
49
-                  <td>{{ member.qualification }}</td>
50
-                  <td>{{ member.skillLevel }}</td>
51
-                  <td>{{ member.operateYears }}</td>
52
-                  <td>{{ member.avgYears }}</td>
53
-                  <td>{{ member.totalScore }}</td>
54
-                </tr>
55
-              </tbody>
56
-            </table>
27
+            <RollingTable :columns="memberColumns" :data="teamMembers" :duration="20" />
57
           </div>
28
           </div>
58
         </InfoCard>
29
         </InfoCard>
59
       </div>
30
       </div>
@@ -145,6 +116,7 @@
145
 import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue'
116
 import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue'
146
 import * as echarts from 'echarts'
117
 import * as echarts from 'echarts'
147
 import InfoCard from '../../components/card.vue'
118
 import InfoCard from '../../components/card.vue'
119
+import RollingTable from '../../components/rollingTable.vue'
148
 
120
 
149
 
121
 
150
 const props = defineProps({
122
 const props = defineProps({
@@ -157,13 +129,13 @@ const props = defineProps({
157
 
129
 
158
 
130
 
159
 const radarData = ref([
131
 const radarData = ref([
160
-  { name: '通道安全防控力', value: 86 },
161
-  { name: '通道安全防控力', value: 90 },
162
-  { name: '通道安全防控力', value: 72 },
163
-  { name: '通道安全防控力', value: 68 },
164
-  { name: '通道安全防控力', value: 78 },
165
-  { name: '通道安全防控力', value: 80 },
166
-  { name: '通道协同作战能力', value: 72 }
132
+  { name: '通道安全防控力', value: 86, color: '#00e5ff' },
133
+  { name: '通道安全防控力', value: 90, color: '#00e5ff' },
134
+  { name: '通道安全防控力', value: 72, color: '#ff4757' },
135
+  { name: '通道安全防控力', value: 68, color: '#ff4757' },
136
+  { name: '通道安全防控力', value: 78, color: '#3742fa' },
137
+  { name: '通道安全防控力', value: 80, color: '#3742fa' },
138
+  { name: '通道协同作战能力', value: 72, color: '#ff4757' }
167
 ])
139
 ])
168
 
140
 
169
 const teamMembers = ref([
141
 const teamMembers = ref([
@@ -174,6 +146,21 @@ const teamMembers = ref([
174
   { name: '马建国', age: 24, seniority: 5, gender: '男', nation: '汉', political: '党员', position: '安检员', qualification: '前传、特检、人身', skillLevel: '三级', operateYears: 6, avgYears: 78, totalScore: 78 }
146
   { name: '马建国', age: 24, seniority: 5, gender: '男', nation: '汉', political: '党员', position: '安检员', qualification: '前传、特检、人身', skillLevel: '三级', operateYears: 6, avgYears: 78, totalScore: 78 }
175
 ])
147
 ])
176
 
148
 
149
+const memberColumns = [
150
+  { label: '姓名', prop: 'name' },
151
+  { label: '年龄', prop: 'age' },
152
+  { label: '司龄', prop: 'seniority' },
153
+  { label: '性别', prop: 'gender' },
154
+  { label: '民族', prop: 'nation' },
155
+  { label: '政治面貌', prop: 'political' },
156
+  { label: '职务', prop: 'position' },
157
+  { label: '岗位资质', prop: 'qualification' },
158
+  { label: '职业技能等级', prop: 'skillLevel' },
159
+  { label: '开机年限', prop: 'operateYears' },
160
+  { label: '平均年限', prop: 'avgYears' },
161
+  { label: '综合得分', prop: 'totalScore' }
162
+]
163
+
177
 // 监听查询参数变化,重新请求数据
164
 // 监听查询参数变化,重新请求数据
178
 watch(() => props.queryParams, (newParams) => {
165
 watch(() => props.queryParams, (newParams) => {
179
   fetchData(newParams)
166
   fetchData(newParams)
@@ -888,41 +875,57 @@ onUnmounted(() => {
888
       flex: 1;
875
       flex: 1;
889
       display: flex;
876
       display: flex;
890
       flex-direction: column;
877
       flex-direction: column;
891
-      gap: 12px;
878
+      gap: 16px;
892
       padding: 10px 0;
879
       padding: 10px 0;
893
       
880
       
894
       .radar-item {
881
       .radar-item {
895
         display: flex;
882
         display: flex;
896
-        align-items: center;
897
-        gap: 10px;
883
+        flex-direction: column;
884
+        gap: 6px;
898
         font-size: 13px;
885
         font-size: 13px;
899
         
886
         
900
         .item-label {
887
         .item-label {
901
-          width: 140px;
902
           color: #a0c4ff;
888
           color: #a0c4ff;
903
-          flex-shrink: 0;
904
         }
889
         }
905
         
890
         
906
-        .progress-bar {
907
-          flex: 1;
908
-          height: 10px;
909
-          background: rgba(15, 70, 250, 0.2);
910
-          border-radius: 4px;
911
-          overflow: hidden;
891
+        .progress-row {
892
+          display: flex;
893
+          align-items: center;
894
+          gap: 10px;
912
           
895
           
913
-          .progress-fill {
914
-            height: 100%;
915
-            background: linear-gradient(90deg, #0f46fa, #bd03fb);
916
-            border-radius: 4px;
917
-            transition: width 0.3s ease;
896
+          .progress-bar {
897
+            flex: 1;
898
+            height: 5px;
899
+            background: rgba(255, 255, 255, 0.1);
900
+            border-radius: 0;
901
+            overflow: visible;
902
+            
903
+            .progress-fill {
904
+              height: 100%;
905
+              border-radius: 0;
906
+              transition: width 0.3s ease;
907
+              position: relative;
908
+              
909
+              .progress-end {
910
+                position: absolute;
911
+                right: -4px;
912
+                top: 50%;
913
+                transform: translateY(-50%);
914
+                width: 3px;
915
+                height: 9px;
916
+                background: #fff;
917
+                border-radius: 2px;
918
+              }
919
+            }
920
+          }
921
+          
922
+          .item-value {
923
+            width: 45px;
924
+            text-align: right;
925
+            color: #fff;
926
+            font-weight: bold;
927
+            font-size: 13px;
918
           }
928
           }
919
-        }
920
-        
921
-        .item-value {
922
-          width: 45px;
923
-          text-align: right;
924
-          color: #fff;
925
-          font-weight: bold;
926
         }
929
         }
927
       }
930
       }
928
     }
931
     }

+ 25 - 84
src/views/portraitManagement/stationProfile/component/profile.vue

@@ -7,59 +7,12 @@
7
 
7
 
8
       <div class="content-row">
8
       <div class="content-row">
9
         <InfoCard title="团队成员">
9
         <InfoCard title="团队成员">
10
-          <div class="team-members-table">
11
-            <table>
12
-              <thead>
13
-                <tr>
14
-                  <th>部门</th>
15
-                  <th>员工数量</th>
16
-                  <th>党员数量</th>
17
-                  <th>平均年龄</th>
18
-                  <th>平均工龄</th>
19
-                  <th>职业资格证书等级</th>
20
-                  <th>平均升级年龄</th>
21
-                  <th>综合得分</th>
22
-                </tr>
23
-              </thead>
24
-              <tbody>
25
-                <tr>
26
-                  <td>旅检一部</td>
27
-                  <td>800</td>
28
-                  <td>7</td>
29
-                  <td>25</td>
30
-                  <td>3</td>
31
-                  <td>16</td>
32
-                  <td>3</td>
33
-                  <td>90</td>
34
-                </tr>
35
-                <tr>
36
-                  <td>旅检二部</td>
37
-                  <td>756</td>
38
-                  <td>2</td>
39
-                  <td>28</td>
40
-                  <td>8</td>
41
-                  <td>18</td>
42
-                  <td>5</td>
43
-                  <td>88</td>
44
-                </tr>
45
-                <tr>
46
-                  <td>旅检三部</td>
47
-                  <td>708</td>
48
-                  <td>7</td>
49
-                  <td>25</td>
50
-                  <td>3</td>
51
-                  <td>23</td>
52
-                  <td>3</td>
53
-                  <td>86</td>
54
-                </tr>
55
-              </tbody>
56
-            </table>
57
-          </div>
10
+          <RollingTable 
11
+            :columns="teamColumns"
12
+            :data="teamData"
13
+          />
58
         </InfoCard>
14
         </InfoCard>
59
-      </div>
60
-
61
-      <div class="content-row">
62
-        <InfoCard title="成员基本情况分布">
15
+         <InfoCard title="成员基本情况分布">
63
           <div class="member-distribution">
16
           <div class="member-distribution">
64
             <div class="dist-item">
17
             <div class="dist-item">
65
               <h4>性别分布</h4>
18
               <h4>性别分布</h4>
@@ -77,7 +30,9 @@
77
         </InfoCard>
30
         </InfoCard>
78
       </div>
31
       </div>
79
 
32
 
80
-      <div class="content-row">
33
+      
34
+
35
+      <div class="content-row" style="width: 50%;">
81
         <InfoCard title="成员职位情况分布">
36
         <InfoCard title="成员职位情况分布">
82
           <div class="position-distribution">
37
           <div class="position-distribution">
83
             <div class="dist-item">
38
             <div class="dist-item">
@@ -103,6 +58,7 @@
103
 import { ref, onMounted, onUnmounted, nextTick } from 'vue'
58
 import { ref, onMounted, onUnmounted, nextTick } from 'vue'
104
 import * as echarts from 'echarts'
59
 import * as echarts from 'echarts'
105
 import InfoCard from '../../components/card.vue'
60
 import InfoCard from '../../components/card.vue'
61
+import RollingTable from '../../components/rollingTable.vue'
106
 
62
 
107
 
63
 
108
 
64
 
@@ -113,6 +69,22 @@ const props = defineProps({
113
   }
69
   }
114
 })
70
 })
115
 
71
 
72
+const teamColumns = [
73
+  { label: '部门', prop: 'dept' },
74
+  { label: '员工数量', prop: 'empCount' },
75
+  { label: '党员数量', prop: 'partyCount' },
76
+  { label: '平均年龄', prop: 'avgAge' },
77
+  { label: '平均工龄', prop: 'avgWorkYears' },
78
+  { label: '职业资格证书等级', prop: 'certLevel' },
79
+  { label: '平均升级年龄', prop: 'avgUpgradeAge' },
80
+  { label: '综合得分', prop: 'totalScore' }
81
+]
82
+
83
+const teamData = ref([
84
+  { dept: '旅检一部', empCount: '800', partyCount: '7', avgAge: '25', avgWorkYears: '3', certLevel: '16', avgUpgradeAge: '3', totalScore: '90' },
85
+  { dept: '旅检二部', empCount: '756', partyCount: '2', avgAge: '28', avgWorkYears: '8', certLevel: '18', avgUpgradeAge: '5', totalScore: '88' },
86
+  { dept: '旅检三部', empCount: '708', partyCount: '7', avgAge: '25', avgWorkYears: '3', certLevel: '23', avgUpgradeAge: '3', totalScore: '86' }
87
+])
116
 
88
 
117
 const genderDistChartRef = ref(null)
89
 const genderDistChartRef = ref(null)
118
 let genderDistChart = null
90
 let genderDistChart = null
@@ -384,37 +356,6 @@ onUnmounted(() => {
384
     }
356
     }
385
   }
357
   }
386
 
358
 
387
-  .team-members-table {
388
-    width: 100%;
389
-    overflow-x: auto;
390
-
391
-    table {
392
-      width: 100%;
393
-      border-collapse: collapse;
394
-      color: #fff;
395
-
396
-      th, td {
397
-        padding: 12px 15px;
398
-        text-align: center;
399
-        border: 1px solid rgba(15, 70, 250, 0.3);
400
-      }
401
-
402
-      th {
403
-        background: rgba(15, 70, 250, 0.2);
404
-        color: #a0c4ff;
405
-        font-weight: 600;
406
-      }
407
-
408
-      td {
409
-        background: rgba(33, 33, 58, 0.5);
410
-      }
411
-
412
-      tbody tr:hover td {
413
-        background: rgba(15, 70, 250, 0.3);
414
-      }
415
-    }
416
-  }
417
-
418
   .member-distribution,
359
   .member-distribution,
419
   .position-distribution {
360
   .position-distribution {
420
     display: flex;
361
     display: flex;

+ 170 - 447
src/views/portraitManagement/stationProfile/component/runData.vue

@@ -4,35 +4,41 @@
4
       <InfoCard title="当天开航每小时各区过检人数组合图">
4
       <InfoCard title="当天开航每小时各区过检人数组合图">
5
         <div ref="hourlyPassChartRef" class="hourly-pass-chart"></div>
5
         <div ref="hourlyPassChartRef" class="hourly-pass-chart"></div>
6
       </InfoCard>
6
       </InfoCard>
7
-    </div>
7
+      <div class="info-panel">
8
+   
9
+        <div class="panel-body">
10
+          <div class="datetime-row">
11
+            <span class="info-date">{{ currentDate }}</span>
8
 
12
 
9
-    <div class="content-row">
10
-      <InfoCard title="每日各时段查获物品趋势图">
11
-        <div ref="dailyTrendChartRef" class="daily-trend-chart"></div>
12
-      </InfoCard>
13
-      <InfoCard title="各区每日查获物品数量对比">
14
-        <div ref="areaCompareChartRef" class="area-compare-chart"></div>
15
-      </InfoCard>
16
-    </div>
13
+          </div>
14
+          <div class="date-row">
15
+            <span class="info-weekday">{{ currentWeekday }}</span>
16
+            <span class="time-row">{{ currentTime }}</span>
17
+          </div>
17
 
18
 
18
-    <div class="content-row">
19
-      <InfoCard title="各区当日查获物品分布">
20
-        <div class="area-distribution">
21
-          <div class="dist-item" v-for="(item, index) in areaDistData" :key="index">
22
-            <div :ref="el => areaDistChartRefs[index] = el" class="dist-chart"></div>
23
-            <div class="dist-label">{{ item.name }}</div>
19
+       
20
+          <div class="duty-item">
21
+            <span class="duty-label">值班领导:</span>
22
+            <span class="duty-value">{{ dutyLeader }}</span>
23
+          </div>
24
+          <div class="duty-item">
25
+            <span class="duty-label">安全板块:</span>
26
+            <span class="duty-value">{{ safetySection }}</span>
27
+          </div>
28
+          <div class="duty-item">
29
+            <span class="duty-label">服务板块:</span>
30
+            <span class="duty-value">{{ serviceSection }}</span>
31
+          </div>
32
+          <div class="duty-item">
33
+            <span class="duty-label">运行板块:</span>
34
+            <span class="duty-value">{{ operationSection }}</span>
35
+          </div>
36
+          <div class="duty-item">
37
+            <span class="duty-label">备勤值班:</span>
38
+            <span class="duty-value">{{ standbyDuty }}</span>
24
           </div>
39
           </div>
25
         </div>
40
         </div>
26
-      </InfoCard>
27
-    </div>
28
-
29
-    <div class="content-row">
30
-      <InfoCard title="近一周各区查获总数趋势">
31
-        <div ref="weeklyTrendChartRef" class="weekly-trend-chart"></div>
32
-      </InfoCard>
33
-      <InfoCard title="当月各类型物品查获对比">
34
-        <div ref="monthlyTypeChartRef" class="monthly-type-chart"></div>
35
-      </InfoCard>
41
+      </div>
36
     </div>
42
     </div>
37
 
43
 
38
     <div class="content-row">
44
     <div class="content-row">
@@ -65,11 +71,7 @@
65
       </InfoCard>
71
       </InfoCard>
66
     </div>
72
     </div>
67
 
73
 
68
-    <div class="content-row">
69
-      <InfoCard title="每日查获数量(总表)">
70
-        <div ref="dailySeizedBarRef" class="daily-bar-chart"></div>
71
-      </InfoCard>
72
-    </div>
74
+
73
 
75
 
74
     <div class="content-row">
76
     <div class="content-row">
75
       <InfoCard title="查获工作区域分布">
77
       <InfoCard title="查获工作区域分布">
@@ -112,30 +114,50 @@ const props = defineProps({
112
   queryParams: {
114
   queryParams: {
113
     type: Object,
115
     type: Object,
114
     default: () => ({})
116
     default: () => ({})
117
+  },
118
+  dutyLeader: {
119
+    type: String,
120
+    default: '张某某'
121
+  },
122
+  safetySection: {
123
+    type: String,
124
+    default: '李某某'
125
+  },
126
+  serviceSection: {
127
+    type: String,
128
+    default: '王某某'
129
+  },
130
+  operationSection: {
131
+    type: String,
132
+    default: '赵某某'
133
+  },
134
+  standbyDuty: {
135
+    type: String,
136
+    default: '刘某某'
115
   }
137
   }
116
 })
138
 })
117
 
139
 
118
-const areaDistData = ref([
119
-  { name: 'T3国内出发(4层)' },
120
-  { name: 'T3(8层出发)' },
121
-  { name: 'T3国际出发' },
122
-  { name: 'T3国际转国内' },
123
-  { name: '旅检A区' }
124
-])
125
-
126
-const areaDistChartRefs = ref([])
127
-const areaDistCharts = ref([])
140
+const currentDate = ref('')
141
+const currentWeekday = ref('')
142
+const currentTime = ref('')
143
+const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
144
+let dateTimer = null
145
+
146
+const updateDateTime = () => {
147
+  const now = new Date()
148
+  const year = now.getFullYear()
149
+  const month = String(now.getMonth() + 1).padStart(2, '0')
150
+  const day = String(now.getDate()).padStart(2, '0')
151
+  currentDate.value = `${year}年${month}月${day}日`
152
+  currentWeekday.value = weekdays[now.getDay()]
153
+  const hours = String(now.getHours()).padStart(2, '0')
154
+  const minutes = String(now.getMinutes()).padStart(2, '0')
155
+  const seconds = String(now.getSeconds()).padStart(2, '0')
156
+  currentTime.value = `${hours}:${minutes}:${seconds}`
157
+}
128
 
158
 
129
 const hourlyPassChartRef = ref(null)
159
 const hourlyPassChartRef = ref(null)
130
 let hourlyPassChart = null
160
 let hourlyPassChart = null
131
-const dailyTrendChartRef = ref(null)
132
-let dailyTrendChart = null
133
-const areaCompareChartRef = ref(null)
134
-let areaCompareChart = null
135
-const weeklyTrendChartRef = ref(null)
136
-let weeklyTrendChart = null
137
-const monthlyTypeChartRef = ref(null)
138
-let monthlyTypeChart = null
139
 const seizedSmallChartRef = ref(null)
161
 const seizedSmallChartRef = ref(null)
140
 let seizedSmallChart = null
162
 let seizedSmallChart = null
141
 const seizedChartRef = ref(null)
163
 const seizedChartRef = ref(null)
@@ -144,8 +166,7 @@ const dailySeizedLineRef = ref(null)
144
 let dailySeizedLine = null
166
 let dailySeizedLine = null
145
 const dailySeizedAreaRef = ref(null)
167
 const dailySeizedAreaRef = ref(null)
146
 let dailySeizedArea = null
168
 let dailySeizedArea = null
147
-const dailySeizedBarRef = ref(null)
148
-let dailySeizedBar = null
169
+
149
 const workAreaDistChartRef = ref(null)
170
 const workAreaDistChartRef = ref(null)
150
 let workAreaDistChart = null
171
 let workAreaDistChart = null
151
 const unsafeItemDistChartRef = ref(null)
172
 const unsafeItemDistChartRef = ref(null)
@@ -164,12 +185,12 @@ let securityTestAreaChart = null
164
 const initHourlyPassChart = () => {
185
 const initHourlyPassChart = () => {
165
   if (!hourlyPassChartRef.value) return
186
   if (!hourlyPassChartRef.value) return
166
   hourlyPassChart = echarts.init(hourlyPassChartRef.value)
187
   hourlyPassChart = echarts.init(hourlyPassChartRef.value)
167
-  const hours = Array.from({length: 24}, (_, i) => `${i.toString().padStart(2, '0')}:00`)
188
+  const hours = Array.from({ length: 24 }, (_, i) => `${i.toString().padStart(2, '0')}:00`)
168
   const data1 = [450, 420, 380, 390, 410, 430, 460, 480, 520, 560, 600, 620, 650, 680, 700, 680, 650, 620, 580, 540, 500, 480, 450, 420]
189
   const data1 = [450, 420, 380, 390, 410, 430, 460, 480, 520, 560, 600, 620, 650, 680, 700, 680, 650, 620, 580, 540, 500, 480, 450, 420]
169
   const data2 = [320, 310, 290, 300, 310, 330, 350, 370, 400, 430, 460, 480, 500, 520, 530, 510, 490, 470, 440, 410, 380, 360, 340, 320]
190
   const data2 = [320, 310, 290, 300, 310, 330, 350, 370, 400, 430, 460, 480, 500, 520, 530, 510, 490, 470, 440, 410, 380, 360, 340, 320]
170
   const data3 = [250, 240, 220, 230, 240, 260, 280, 300, 330, 360, 390, 410, 430, 450, 460, 440, 420, 400, 370, 340, 310, 290, 270, 250]
191
   const data3 = [250, 240, 220, 230, 240, 260, 280, 300, 330, 360, 390, 410, 430, 450, 460, 440, 420, 400, 370, 340, 310, 290, 270, 250]
171
   const lineData = [5.2, 4.8, 4.5, 4.7, 5.0, 5.3, 5.6, 5.8, 6.2, 6.5, 6.8, 7.0, 7.2, 7.5, 7.3, 7.1, 6.8, 6.5, 6.2, 5.8, 5.5, 5.2, 5.0, 4.8]
192
   const lineData = [5.2, 4.8, 4.5, 4.7, 5.0, 5.3, 5.6, 5.8, 6.2, 6.5, 6.8, 7.0, 7.2, 7.5, 7.3, 7.1, 6.8, 6.5, 6.2, 5.8, 5.5, 5.2, 5.0, 4.8]
172
-  
193
+
173
   const option = {
194
   const option = {
174
     tooltip: {
195
     tooltip: {
175
       trigger: 'axis',
196
       trigger: 'axis',
@@ -273,291 +294,6 @@ const initHourlyPassChart = () => {
273
   hourlyPassChart.setOption(option)
294
   hourlyPassChart.setOption(option)
274
 }
295
 }
275
 
296
 
276
-const initDailyTrendChart = () => {
277
-  if (!dailyTrendChartRef.value) return
278
-  dailyTrendChart = echarts.init(dailyTrendChartRef.value)
279
-  const option = {
280
-    tooltip: {
281
-      trigger: 'axis',
282
-      backgroundColor: 'rgba(13,80,122,0.95)',
283
-      borderColor: '#70CFE7',
284
-      textStyle: { color: '#fff' }
285
-    },
286
-    legend: {
287
-      data: ['打火机', '管制刀具', '其他违禁品'],
288
-      textStyle: { color: '#a0c4ff' },
289
-      top: 0
290
-    },
291
-    grid: {
292
-      left: '3%',
293
-      right: '4%',
294
-      bottom: '3%',
295
-      containLabel: true
296
-    },
297
-    xAxis: {
298
-      type: 'category',
299
-      data: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '24:00'],
300
-      axisLabel: { color: '#a0c4ff' },
301
-      axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } }
302
-    },
303
-    yAxis: {
304
-      type: 'value',
305
-      axisLabel: { color: '#a0c4ff' },
306
-      axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } },
307
-      splitLine: { lineStyle: { color: 'rgba(15,70,250,0.2)' } }
308
-    },
309
-    series: [
310
-      {
311
-        name: '打火机',
312
-        type: 'line',
313
-        smooth: true,
314
-        data: [12, 8, 25, 45, 50, 35, 20],
315
-        itemStyle: { color: '#ff9f43' },
316
-        areaStyle: {
317
-          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
318
-            { offset: 0, color: 'rgba(255,159,67,0.3)' },
319
-            { offset: 1, color: 'rgba(255,159,67,0.05)' }
320
-          ])
321
-        }
322
-      },
323
-      {
324
-        name: '管制刀具',
325
-        type: 'line',
326
-        smooth: true,
327
-        data: [5, 3, 15, 25, 28, 18, 10],
328
-        itemStyle: { color: '#ff6b6b' },
329
-        areaStyle: {
330
-          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
331
-            { offset: 0, color: 'rgba(255,107,107,0.3)' },
332
-            { offset: 1, color: 'rgba(255,107,107,0.05)' }
333
-          ])
334
-        }
335
-      },
336
-      {
337
-        name: '其他违禁品',
338
-        type: 'line',
339
-        smooth: true,
340
-        data: [20, 15, 40, 60, 70, 45, 30],
341
-        itemStyle: { color: '#4da6ff' },
342
-        areaStyle: {
343
-          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
344
-            { offset: 0, color: 'rgba(77,166,255,0.3)' },
345
-            { offset: 1, color: 'rgba(77,166,255,0.05)' }
346
-          ])
347
-        }
348
-      }
349
-    ]
350
-  }
351
-  dailyTrendChart.setOption(option)
352
-}
353
-
354
-const initAreaCompareChart = () => {
355
-  if (!areaCompareChartRef.value) return
356
-  areaCompareChart = echarts.init(areaCompareChartRef.value)
357
-  const option = {
358
-    tooltip: {
359
-      trigger: 'axis',
360
-      backgroundColor: 'rgba(13,80,122,0.95)',
361
-      borderColor: '#70CFE7',
362
-      textStyle: { color: '#fff' }
363
-    },
364
-    legend: {
365
-      data: ['T3国内出发(4层)', 'T3(8层出发)', 'T3国际出发', 'T3国际转国内', '旅检A区'],
366
-      textStyle: { color: '#a0c4ff' },
367
-      top: 0
368
-    },
369
-    grid: {
370
-      left: '3%',
371
-      right: '4%',
372
-      bottom: '15%',
373
-      containLabel: true
374
-    },
375
-    xAxis: {
376
-      type: 'category',
377
-      data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
378
-      axisLabel: { color: '#a0c4ff' },
379
-      axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } }
380
-    },
381
-    yAxis: {
382
-      type: 'value',
383
-      axisLabel: { color: '#a0c4ff' },
384
-      axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } },
385
-      splitLine: { lineStyle: { color: 'rgba(15,70,250,0.2)' } }
386
-    },
387
-    series: [
388
-      {
389
-        name: 'T3国内出发(4层)',
390
-        type: 'bar',
391
-        data: [120, 132, 140, 125, 150, 160, 145],
392
-        itemStyle: { color: '#4da6ff' },
393
-        barWidth: '15%'
394
-      },
395
-      {
396
-        name: 'T3(8层出发)',
397
-        type: 'bar',
398
-        data: [90, 100, 110, 95, 120, 130, 115],
399
-        itemStyle: { color: '#7eff7e' },
400
-        barWidth: '15%'
401
-      },
402
-      {
403
-        name: 'T3国际出发',
404
-        type: 'bar',
405
-        data: [70, 80, 85, 75, 90, 100, 88],
406
-        itemStyle: { color: '#bd03fb' },
407
-        barWidth: '15%'
408
-      },
409
-      {
410
-        name: 'T3国际转国内',
411
-        type: 'bar',
412
-        data: [45, 55, 60, 50, 65, 70, 62],
413
-        itemStyle: { color: '#ff9f43' },
414
-        barWidth: '15%'
415
-      },
416
-      {
417
-        name: '旅检A区',
418
-        type: 'bar',
419
-        data: [55, 65, 70, 60, 75, 80, 72],
420
-        itemStyle: { color: '#ffd93d' },
421
-        barWidth: '15%'
422
-      }
423
-    ]
424
-  }
425
-  areaCompareChart.setOption(option)
426
-}
427
-
428
-const initAreaDistCharts = () => {
429
-  const colors = [
430
-    ['#4da6ff', '#0f46fa'],
431
-    ['#7eff7e', '#2ecc71'],
432
-    ['#bd03fb', '#8a06e8'],
433
-    ['#ff9f43', '#ee5a24'],
434
-    ['#ffd93d', '#ff9f43']
435
-  ]
436
-  areaDistData.value.forEach((item, index) => {
437
-    if (!areaDistChartRefs.value[index]) return
438
-    const chart = echarts.init(areaDistChartRefs.value[index])
439
-    areaDistCharts.value.push(chart)
440
-    const option = {
441
-      series: [{
442
-        type: 'pie',
443
-        radius: ['40%', '70%'],
444
-        data: [
445
-          { value: 40, name: '打火机', itemStyle: { color: colors[index][0] } },
446
-          { value: 30, name: '管制刀具', itemStyle: { color: colors[index][1] } },
447
-          { value: 30, name: '其他违禁品', itemStyle: { color: '#a0c4ff' } }
448
-        ],
449
-        label: { show: true, color: '#fff', fontSize: 10 },
450
-        labelLine: { show: true }
451
-      }]
452
-    }
453
-    chart.setOption(option)
454
-  })
455
-}
456
-
457
-const initWeeklyTrendChart = () => {
458
-  if (!weeklyTrendChartRef.value) return
459
-  weeklyTrendChart = echarts.init(weeklyTrendChartRef.value)
460
-  const option = {
461
-    tooltip: {
462
-      trigger: 'axis',
463
-      backgroundColor: 'rgba(13,80,122,0.95)',
464
-      borderColor: '#70CFE7',
465
-      textStyle: { color: '#fff' }
466
-    },
467
-    legend: {
468
-      data: ['T3国内出发(4层)', 'T3(8层出发)', 'T3国际出发'],
469
-      textStyle: { color: '#a0c4ff' },
470
-      top: 0
471
-    },
472
-    grid: {
473
-      left: '3%',
474
-      right: '4%',
475
-      bottom: '15%',
476
-      containLabel: true
477
-    },
478
-    xAxis: {
479
-      type: 'category',
480
-      data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
481
-      axisLabel: { color: '#a0c4ff' },
482
-      axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } }
483
-    },
484
-    yAxis: {
485
-      type: 'value',
486
-      axisLabel: { color: '#a0c4ff' },
487
-      axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } },
488
-      splitLine: { lineStyle: { color: 'rgba(15,70,250,0.2)' } }
489
-    },
490
-    series: [
491
-      {
492
-        name: 'T3国内出发(4层)',
493
-        type: 'line',
494
-        smooth: true,
495
-        data: [450, 480, 500, 420, 520, 580, 550],
496
-        itemStyle: { color: '#4da6ff' },
497
-        areaStyle: {
498
-          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
499
-            { offset: 0, color: 'rgba(77,166,255,0.3)' },
500
-            { offset: 1, color: 'rgba(77,166,255,0.05)' }
501
-          ])
502
-        }
503
-      },
504
-      {
505
-        name: 'T3(8层出发)',
506
-        type: 'line',
507
-        smooth: true,
508
-        data: [350, 380, 400, 320, 420, 480, 450],
509
-        itemStyle: { color: '#7eff7e' },
510
-        areaStyle: {
511
-          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
512
-            { offset: 0, color: 'rgba(126,255,126,0.3)' },
513
-            { offset: 1, color: 'rgba(126,255,126,0.05)' }
514
-          ])
515
-        }
516
-      },
517
-      {
518
-        name: 'T3国际出发',
519
-        type: 'line',
520
-        smooth: true,
521
-        data: [280, 300, 320, 260, 340, 380, 360],
522
-        itemStyle: { color: '#bd03fb' },
523
-        areaStyle: {
524
-          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
525
-            { offset: 0, color: 'rgba(189,3,251,0.3)' },
526
-            { offset: 1, color: 'rgba(189,3,251,0.05)' }
527
-          ])
528
-        }
529
-      }
530
-    ]
531
-  }
532
-  weeklyTrendChart.setOption(option)
533
-}
534
-
535
-const initMonthlyTypeChart = () => {
536
-  if (!monthlyTypeChartRef.value) return
537
-  monthlyTypeChart = echarts.init(monthlyTypeChartRef.value)
538
-  const option = {
539
-    tooltip: {
540
-      trigger: 'item',
541
-      backgroundColor: 'rgba(13,80,122,0.95)',
542
-      borderColor: '#70CFE7',
543
-      textStyle: { color: '#fff' }
544
-    },
545
-    series: [{
546
-      type: 'pie',
547
-      radius: ['40%', '70%'],
548
-      data: [
549
-        { value: 35, name: '打火机', itemStyle: { color: '#ff9f43' } },
550
-        { value: 25, name: '管制刀具', itemStyle: { color: '#ff6b6b' } },
551
-        { value: 20, name: '易燃易爆', itemStyle: { color: '#ffd93d' } },
552
-        { value: 20, name: '其他', itemStyle: { color: '#a0c4ff' } }
553
-      ],
554
-      label: { show: true, color: '#fff', fontSize: 11 },
555
-      labelLine: { show: true }
556
-    }]
557
-  }
558
-  monthlyTypeChart.setOption(option)
559
-}
560
-
561
 const initSeizedSmallChart = () => {
297
 const initSeizedSmallChart = () => {
562
   if (!seizedSmallChartRef.value) return
298
   if (!seizedSmallChartRef.value) return
563
   seizedSmallChart = echarts.init(seizedSmallChartRef.value)
299
   seizedSmallChart = echarts.init(seizedSmallChartRef.value)
@@ -741,58 +477,7 @@ const initDailySeizedArea = () => {
741
   dailySeizedArea.setOption(option)
477
   dailySeizedArea.setOption(option)
742
 }
478
 }
743
 
479
 
744
-const initDailySeizedBar = () => {
745
-  if (!dailySeizedBarRef.value) return
746
-  dailySeizedBar = echarts.init(dailySeizedBarRef.value)
747
-  const option = {
748
-    tooltip: {
749
-      trigger: 'axis',
750
-      backgroundColor: 'rgba(13,80,122,0.95)',
751
-      borderColor: '#70CFE7',
752
-      textStyle: { color: '#fff' }
753
-    },
754
-    legend: {
755
-      data: ['查获数量'],
756
-      textStyle: { color: '#a0c4ff' },
757
-      top: 0
758
-    },
759
-    grid: {
760
-      left: '3%',
761
-      right: '4%',
762
-      bottom: '15%',
763
-      containLabel: true
764
-    },
765
-    xAxis: {
766
-      type: 'category',
767
-      data: ['T3国内出发(4层)', 'T3(8层出发)', 'T3国际出发', 'T3国际转国内', '旅检A区', 'T3要客服务区东', 'T3要客服务区西', 'T1要客服务区东', 'T2要客服务区东', '旅检B区', '重检C区'],
768
-      axisLabel: { 
769
-        color: '#a0c4ff',
770
-        rotate: 30,
771
-        fontSize: 10
772
-      },
773
-      axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } }
774
-    },
775
-    yAxis: {
776
-      type: 'value',
777
-      axisLabel: { color: '#a0c4ff' },
778
-      axisLine: { lineStyle: { color: 'rgba(15,70,250,0.3)' } },
779
-      splitLine: { lineStyle: { color: 'rgba(15,70,250,0.2)' } }
780
-    },
781
-    series: [{
782
-      name: '查获数量',
783
-      type: 'bar',
784
-      data: [172, 200, 182, 82, 79, 149, 169, 92, 163, 144, 180],
785
-      itemStyle: {
786
-        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
787
-          { offset: 0, color: '#bd03fb' },
788
-          { offset: 1, color: '#0f46fa' }
789
-        ])
790
-      },
791
-      barWidth: '40%'
792
-    }]
793
-  }
794
-  dailySeizedBar.setOption(option)
795
-}
480
+
796
 
481
 
797
 const initWorkAreaDistChart = () => {
482
 const initWorkAreaDistChart = () => {
798
   if (!workAreaDistChartRef.value) return
483
   if (!workAreaDistChartRef.value) return
@@ -818,7 +503,7 @@ const initWorkAreaDistChart = () => {
818
     xAxis: {
503
     xAxis: {
819
       type: 'category',
504
       type: 'category',
820
       data: ['T3国内出发(4层)', 'T3(8层出发)', 'T3国际出发', 'T3国际转国内', '旅检A区', 'T3要客服务区东', 'T3要客服务区西', 'T1要客服务区东', 'T2要客服务区东', '旅检B区', '重检C区'],
505
       data: ['T3国内出发(4层)', 'T3(8层出发)', 'T3国际出发', 'T3国际转国内', '旅检A区', 'T3要客服务区东', 'T3要客服务区西', 'T1要客服务区东', 'T2要客服务区东', '旅检B区', '重检C区'],
821
-      axisLabel: { 
506
+      axisLabel: {
822
         color: '#a0c4ff',
507
         color: '#a0c4ff',
823
         rotate: 30,
508
         rotate: 30,
824
         fontSize: 10
509
         fontSize: 10
@@ -914,7 +599,7 @@ const initUnsafePostDistChart = () => {
914
     xAxis: {
599
     xAxis: {
915
       type: 'category',
600
       type: 'category',
916
       data: ['境外/非通道', '值机柜台', '验证/通道', '人身检查', 'X光/行李/开检', '开箱/货检'],
601
       data: ['境外/非通道', '值机柜台', '验证/通道', '人身检查', 'X光/行李/开检', '开箱/货检'],
917
-      axisLabel: { 
602
+      axisLabel: {
918
         color: '#a0c4ff',
603
         color: '#a0c4ff',
919
         rotate: 30,
604
         rotate: 30,
920
         fontSize: 10
605
         fontSize: 10
@@ -1005,7 +690,7 @@ const initSecurityTestAreaChart = () => {
1005
     xAxis: {
690
     xAxis: {
1006
       type: 'category',
691
       type: 'category',
1007
       data: ['T3国内出发(4层)', 'T3(8层出发)', 'T3国际出发', 'T3国际转国内', 'T1要客服务区西', 'T1要客服务区东'],
692
       data: ['T3国内出发(4层)', 'T3(8层出发)', 'T3国际出发', 'T3国际转国内', 'T1要客服务区西', 'T1要客服务区东'],
1008
-      axisLabel: { 
693
+      axisLabel: {
1009
         color: '#a0c4ff',
694
         color: '#a0c4ff',
1010
         rotate: 30,
695
         rotate: 30,
1011
         fontSize: 10
696
         fontSize: 10
@@ -1036,15 +721,11 @@ const initSecurityTestAreaChart = () => {
1036
 
721
 
1037
 const handleResize = () => {
722
 const handleResize = () => {
1038
   if (hourlyPassChart) hourlyPassChart.resize()
723
   if (hourlyPassChart) hourlyPassChart.resize()
1039
-  if (dailyTrendChart) dailyTrendChart.resize()
1040
-  if (areaCompareChart) areaCompareChart.resize()
1041
-  if (weeklyTrendChart) weeklyTrendChart.resize()
1042
-  if (monthlyTypeChart) monthlyTypeChart.resize()
1043
   if (seizedSmallChart) seizedSmallChart.resize()
724
   if (seizedSmallChart) seizedSmallChart.resize()
1044
   if (seizedChart) seizedChart.resize()
725
   if (seizedChart) seizedChart.resize()
1045
   if (dailySeizedLine) dailySeizedLine.resize()
726
   if (dailySeizedLine) dailySeizedLine.resize()
1046
   if (dailySeizedArea) dailySeizedArea.resize()
727
   if (dailySeizedArea) dailySeizedArea.resize()
1047
-  if (dailySeizedBar) dailySeizedBar.resize()
728
+
1048
   if (workAreaDistChart) workAreaDistChart.resize()
729
   if (workAreaDistChart) workAreaDistChart.resize()
1049
   if (unsafeItemDistChart) unsafeItemDistChart.resize()
730
   if (unsafeItemDistChart) unsafeItemDistChart.resize()
1050
   if (unsafeTypeDistChart) unsafeTypeDistChart.resize()
731
   if (unsafeTypeDistChart) unsafeTypeDistChart.resize()
@@ -1052,23 +733,19 @@ const handleResize = () => {
1052
   if (securityTestItemChart) securityTestItemChart.resize()
733
   if (securityTestItemChart) securityTestItemChart.resize()
1053
   if (securityTestPassChart) securityTestPassChart.resize()
734
   if (securityTestPassChart) securityTestPassChart.resize()
1054
   if (securityTestAreaChart) securityTestAreaChart.resize()
735
   if (securityTestAreaChart) securityTestAreaChart.resize()
1055
-  areaDistCharts.value.forEach(chart => chart && chart.resize())
1056
 }
736
 }
1057
 
737
 
1058
 onMounted(() => {
738
 onMounted(() => {
739
+  updateDateTime()
740
+  dateTimer = setInterval(updateDateTime, 1000)
1059
   nextTick(() => {
741
   nextTick(() => {
1060
     setTimeout(() => {
742
     setTimeout(() => {
1061
       initHourlyPassChart()
743
       initHourlyPassChart()
1062
-      initDailyTrendChart()
1063
-      initAreaCompareChart()
1064
-      initAreaDistCharts()
1065
-      initWeeklyTrendChart()
1066
-      initMonthlyTypeChart()
1067
       initSeizedSmallChart()
744
       initSeizedSmallChart()
1068
       initSeizedChart()
745
       initSeizedChart()
1069
       initDailySeizedLine()
746
       initDailySeizedLine()
1070
       initDailySeizedArea()
747
       initDailySeizedArea()
1071
-      initDailySeizedBar()
748
+
1072
       initWorkAreaDistChart()
749
       initWorkAreaDistChart()
1073
       initUnsafeItemDistChart()
750
       initUnsafeItemDistChart()
1074
       initUnsafeTypeDistChart()
751
       initUnsafeTypeDistChart()
@@ -1082,17 +759,14 @@ onMounted(() => {
1082
 })
759
 })
1083
 
760
 
1084
 onUnmounted(() => {
761
 onUnmounted(() => {
762
+  clearInterval(dateTimer)
1085
   window.removeEventListener('resize', handleResize)
763
   window.removeEventListener('resize', handleResize)
1086
   if (hourlyPassChart) hourlyPassChart.dispose()
764
   if (hourlyPassChart) hourlyPassChart.dispose()
1087
-  if (dailyTrendChart) dailyTrendChart.dispose()
1088
-  if (areaCompareChart) areaCompareChart.dispose()
1089
-  if (weeklyTrendChart) weeklyTrendChart.dispose()
1090
-  if (monthlyTypeChart) monthlyTypeChart.dispose()
1091
   if (seizedSmallChart) seizedSmallChart.dispose()
765
   if (seizedSmallChart) seizedSmallChart.dispose()
1092
   if (seizedChart) seizedChart.dispose()
766
   if (seizedChart) seizedChart.dispose()
1093
   if (dailySeizedLine) dailySeizedLine.dispose()
767
   if (dailySeizedLine) dailySeizedLine.dispose()
1094
   if (dailySeizedArea) dailySeizedArea.dispose()
768
   if (dailySeizedArea) dailySeizedArea.dispose()
1095
-  if (dailySeizedBar) dailySeizedBar.dispose()
769
+
1096
   if (workAreaDistChart) workAreaDistChart.dispose()
770
   if (workAreaDistChart) workAreaDistChart.dispose()
1097
   if (unsafeItemDistChart) unsafeItemDistChart.dispose()
771
   if (unsafeItemDistChart) unsafeItemDistChart.dispose()
1098
   if (unsafeTypeDistChart) unsafeTypeDistChart.dispose()
772
   if (unsafeTypeDistChart) unsafeTypeDistChart.dispose()
@@ -1100,7 +774,6 @@ onUnmounted(() => {
1100
   if (securityTestItemChart) securityTestItemChart.dispose()
774
   if (securityTestItemChart) securityTestItemChart.dispose()
1101
   if (securityTestPassChart) securityTestPassChart.dispose()
775
   if (securityTestPassChart) securityTestPassChart.dispose()
1102
   if (securityTestAreaChart) securityTestAreaChart.dispose()
776
   if (securityTestAreaChart) securityTestAreaChart.dispose()
1103
-  areaDistCharts.value.forEach(chart => chart && chart.dispose())
1104
 })
777
 })
1105
 </script>
778
 </script>
1106
 
779
 
@@ -1111,49 +784,17 @@ onUnmounted(() => {
1111
   gap: 20px;
784
   gap: 20px;
1112
 
785
 
1113
   .content-row {
786
   .content-row {
787
+    padding: 0 20px;
1114
     display: flex;
788
     display: flex;
1115
     gap: 20px;
789
     gap: 20px;
1116
-    
1117
-    > .info-card {
1118
-      flex: 1;
1119
-      min-width: 0;
1120
-    }
1121
-  }
1122
-
1123
-  .hourly-pass-chart,
1124
-  .daily-trend-chart,
1125
-  .area-compare-chart {
1126
-    width: 100%;
1127
-    height: 350px;
1128
-  }
1129
 
790
 
1130
-  .area-distribution {
1131
-    display: flex;
1132
-    gap: 20px;
1133
-    justify-content: space-around;
1134
-    flex-wrap: wrap;
1135
-
1136
-    .dist-item {
791
+    >.info-card {
1137
       flex: 1;
792
       flex: 1;
1138
-      min-width: 150px;
1139
-      text-align: center;
1140
-
1141
-      .dist-chart {
1142
-        width: 100%;
1143
-        height: 200px;
1144
-      }
1145
-
1146
-      .dist-label {
1147
-        color: #a0c4ff;
1148
-        font-size: 12px;
1149
-        margin-top: 10px;
1150
-        text-align: center;
1151
-      }
793
+      min-width: 0;
1152
     }
794
     }
1153
   }
795
   }
1154
 
796
 
1155
-  .weekly-trend-chart,
1156
-  .monthly-type-chart {
797
+  .hourly-pass-chart {
1157
     width: 100%;
798
     width: 100%;
1158
     height: 350px;
799
     height: 350px;
1159
   }
800
   }
@@ -1221,7 +862,7 @@ onUnmounted(() => {
1221
     height: 350px;
862
     height: 350px;
1222
   }
863
   }
1223
 
864
 
1224
-  .daily-bar-chart,
865
+
1225
   .work-area-dist-chart {
866
   .work-area-dist-chart {
1226
     width: 100%;
867
     width: 100%;
1227
     height: 400px;
868
     height: 400px;
@@ -1236,5 +877,87 @@ onUnmounted(() => {
1236
     width: 100%;
877
     width: 100%;
1237
     height: 350px;
878
     height: 350px;
1238
   }
879
   }
880
+
881
+  .info-panel {
882
+    width: 300px;
883
+    min-width: 300px;
884
+    background: linear-gradient(135deg, rgba(15, 70, 250, 0.15), rgba(189, 3, 251, 0.15));
885
+    border: 1px solid rgba(15, 70, 250, 0.3);
886
+    border-radius: 20px;
887
+    display: flex;
888
+    flex-direction: column;
889
+    overflow: hidden;
890
+
891
+
892
+    .panel-body {
893
+      padding: 15px 20px;
894
+      flex: 1;
895
+      display: flex;
896
+      flex-direction: column;
897
+      gap: 10px;
898
+      justify-content: space-between;
899
+    }
900
+
901
+    .datetime-row {
902
+      display: flex;
903
+      align-items: baseline;
904
+      gap: 12px;
905
+
906
+      .info-date {
907
+        font-size: 22px;
908
+        font-weight: bold;
909
+        color: #fff;
910
+        letter-spacing: 2px;
911
+      }
912
+    }
913
+
914
+    .date-row {
915
+      display: flex;
916
+      align-items: center;
917
+      .info-date {
918
+        font-size: 22px;
919
+        font-weight: bold;
920
+        color: #fff;
921
+        letter-spacing: 2px;
922
+      }
923
+
924
+      .info-weekday {
925
+        font-size: 16px;
926
+        color: #a0c4ff;
927
+      }
928
+    }
929
+
930
+    .time-row {
931
+      font-size: 36px;
932
+      font-weight: bold;
933
+      color: #00f5ff;
934
+      letter-spacing: 4px;
935
+      text-shadow: 0 0 20px rgba(0, 245, 255, 0.5);
936
+    }
937
+
938
+    .divider {
939
+      height: 1px;
940
+      background: linear-gradient(90deg, transparent, rgba(15, 70, 250, 0.5), rgba(189, 3, 251, 0.5), transparent);
941
+      margin: 4px 0;
942
+    }
943
+
944
+    .duty-item {
945
+      display: flex;
946
+      align-items: center;
947
+      line-height: 28px;
948
+
949
+      .duty-label {
950
+        font-size: 16px;
951
+        color: #a0c4ff;
952
+        white-space: nowrap;
953
+      }
954
+
955
+      .duty-value {
956
+        font-size: 16px;
957
+        color: #fff;
958
+        margin-left: 4px;
959
+      }
960
+    }
961
+  }
1239
 }
962
 }
1240
 </style>
963
 </style>

+ 16 - 16
src/views/portraitManagement/stationProfile/index.vue

@@ -69,7 +69,7 @@ const pentagonItems = ref([
69
       left: { start: 'transparent', end: '#0f46fa' }
69
       left: { start: 'transparent', end: '#0f46fa' }
70
     },
70
     },
71
     bgGradientStart: 'rgba(33,33,58,0.95)',
71
     bgGradientStart: 'rgba(33,33,58,0.95)',
72
-    bgGradientEnd: 'rgba(189,3,251,0.15)'
72
+    bgGradientEnd: 'rgba(15,70,250,0.2)'
73
   },
73
   },
74
   {
74
   {
75
     value: '150',
75
     value: '150',
@@ -79,8 +79,8 @@ const pentagonItems = ref([
79
     cornerRadius: 20,
79
     cornerRadius: 20,
80
     vertices: {
80
     vertices: {
81
       topLeft: { x: 10, y: 0 },
81
       topLeft: { x: 10, y: 0 },
82
-      topRight: { x: 270, y: 0 },
83
-      bottomRight: { x: 270, y: 200 },
82
+      topRight: { x: 290, y: 0 },
83
+      bottomRight: { x: 290, y: 200 },
84
       bottomLeft: { x: -30, y: 200 }
84
       bottomLeft: { x: -30, y: 200 }
85
     },
85
     },
86
     cardContentStyle: {
86
     cardContentStyle: {
@@ -92,8 +92,8 @@ const pentagonItems = ref([
92
       bottom: { start: '#9903C1', end: 'transparent' },
92
       bottom: { start: '#9903C1', end: 'transparent' },
93
       left: { start: 'transparent', end: '#0f46fa' }
93
       left: { start: 'transparent', end: '#0f46fa' }
94
     },
94
     },
95
-    bgGradientStart: 'rgba(33,33,58,0.98)',
96
-    bgGradientEnd: 'rgba(189,3,251,0.25)'
95
+    bgGradientStart: 'rgba(33,33,58,0.95)',
96
+    bgGradientEnd: 'rgba(15,70,250,0.2)'
97
   },
97
   },
98
   {
98
   {
99
     value: '80',
99
     value: '80',
@@ -102,8 +102,8 @@ const pentagonItems = ref([
102
     vertical: true,
102
     vertical: true,
103
     cornerRadius: 20,
103
     cornerRadius: 20,
104
     vertices: {
104
     vertices: {
105
-      topLeft: { x: -20, y: 0 },
106
-      topRight: { x: 300, y: 0 },
105
+      topLeft: { x: 0, y: 0 },
106
+      topRight: { x: 290, y: 0 },
107
       bottomRight: { x: 320, y: 200 },
107
       bottomRight: { x: 320, y: 200 },
108
       bottomLeft: { x: 0, y: 200 }
108
       bottomLeft: { x: 0, y: 200 }
109
     },
109
     },
@@ -111,13 +111,13 @@ const pentagonItems = ref([
111
       gap: '10px'
111
       gap: '10px'
112
     },
112
     },
113
     edgeGradients: {
113
     edgeGradients: {
114
-      top: { start: '#7eff7e', end: 'transparent' },
114
+      top: { start: '#0f46fa', end: 'transparent' },
115
       right: { start: 'transparent', end: '#9903C1' },
115
       right: { start: 'transparent', end: '#9903C1' },
116
       bottom: { start: '#9903C1', end: 'transparent' },
116
       bottom: { start: '#9903C1', end: 'transparent' },
117
-      left: { start: 'transparent', end: '#7eff7e' }
117
+      left: { start: 'transparent', end: '#0f46fa' }
118
     },
118
     },
119
     bgGradientStart: 'rgba(33,33,58,0.95)',
119
     bgGradientStart: 'rgba(33,33,58,0.95)',
120
-    bgGradientEnd: 'rgba(126,255,126,0.15)'
120
+    bgGradientEnd: 'rgba(15,70,250,0.2)'
121
   },
121
   },
122
   {
122
   {
123
     value: '70',
123
     value: '70',
@@ -126,10 +126,10 @@ const pentagonItems = ref([
126
     vertical: true,
126
     vertical: true,
127
     cornerRadius: 20,
127
     cornerRadius: 20,
128
     vertices: {
128
     vertices: {
129
-      topLeft: { x: 20, y: 0 },
130
-      topRight: { x: 300, y: 0 },
131
-      bottomRight: { x: 300, y: 200 },
132
-      bottomLeft: { x: 40, y: 200 }
129
+      topLeft: { x: 0, y: 0 },
130
+      topRight: { x: 280, y: 0 },
131
+      bottomRight: { x: 310, y: 200 },
132
+      bottomLeft: { x: 30, y: 200 }
133
     },
133
     },
134
     cardContentStyle: {
134
     cardContentStyle: {
135
       gap: '10px'
135
       gap: '10px'
@@ -150,10 +150,10 @@ const pentagonItems = ref([
150
     vertical: true,
150
     vertical: true,
151
     cornerRadius: 20,
151
     cornerRadius: 20,
152
     vertices: {
152
     vertices: {
153
-      topLeft: { x: 0, y: 0 },
153
+      topLeft: { x: -10, y: 0 },
154
       topRight: { x: 300, y: 0 },
154
       topRight: { x: 300, y: 0 },
155
       bottomRight: { x: 300, y: 200 },
155
       bottomRight: { x: 300, y: 200 },
156
-      bottomLeft: { x: 0, y: 200 }
156
+      bottomLeft: { x:20, y: 200 }
157
     },
157
     },
158
     cardContentStyle: {
158
     cardContentStyle: {
159
       gap: '10px'
159
       gap: '10px'

+ 99 - 37
src/views/score/dimension/index.vue

@@ -3,24 +3,37 @@
3
     <el-row :gutter="16" style="height:100%">
3
     <el-row :gutter="16" style="height:100%">
4
       <!-- 左侧:维度列表 -->
4
       <!-- 左侧:维度列表 -->
5
       <el-col :span="7">
5
       <el-col :span="7">
6
+        <el-card shadow="never">
7
+          <template #header>
8
+            <span>配分层级</span>
9
+          </template>
10
+          <div>
11
+            <el-select v-model="scoreLevel" placeholder="请选择层级" clearable style="width:100%" @change="loadTree">
12
+              <el-option v-for="dict in score_level" :key="dict.value" :label="dict.label" :value="dict.value" />
13
+            </el-select>
14
+          </div>
15
+        </el-card>
6
         <el-card class="dim-card" shadow="never">
16
         <el-card class="dim-card" shadow="never">
7
           <template #header>
17
           <template #header>
8
             <div class="card-header">
18
             <div class="card-header">
9
               <span>评分维度(6个)</span>
19
               <span>评分维度(6个)</span>
10
-              <el-button type="primary" size="small" icon="Plus" @click="handleAddDim" v-hasPermi="['score:dimension:add']">新增</el-button>
20
+              <el-button type="primary" size="small" icon="Plus" @click="handleAddDim"
21
+                v-hasPermi="['score:dimension:add']">新增</el-button>
11
             </div>
22
             </div>
12
           </template>
23
           </template>
13
           <div v-loading="dimLoading">
24
           <div v-loading="dimLoading">
14
             <div v-for="dim in dimList" :key="dim.id"
25
             <div v-for="dim in dimList" :key="dim.id"
15
-                 :class="['dim-item', { active: selectedDim && selectedDim.id === dim.id }]"
16
-                 @click="selectDimension(dim)">
26
+              :class="['dim-item', { active: selectedDim && selectedDim.id === dim.id }]" @click="selectDimension(dim)">
17
               <div class="dim-name">
27
               <div class="dim-name">
18
-                <el-tag :type="dim.status === '0' ? '' : 'info'" size="small" style="margin-right:6px">{{ dim.name }}</el-tag>
28
+                <el-tag :type="dim.status === '0' ? '' : 'info'" size="small" style="margin-right:6px">{{ dim.name
29
+                  }}</el-tag>
19
                 <span class="dim-weight">{{ dim.weight }}%</span>
30
                 <span class="dim-weight">{{ dim.weight }}%</span>
20
               </div>
31
               </div>
21
               <div class="dim-actions">
32
               <div class="dim-actions">
22
-                <el-button link size="small" icon="Edit" @click.stop="handleEditDim(dim)" v-hasPermi="['score:dimension:edit']" />
23
-                <el-button link size="small" icon="Delete" type="danger" @click.stop="handleDeleteDim(dim)" v-hasPermi="['score:dimension:remove']" />
33
+                <el-button link size="small" icon="Edit" @click.stop="handleEditDim(dim)"
34
+                  v-hasPermi="['score:dimension:edit']" />
35
+                <el-button link size="small" icon="Delete" type="danger" @click.stop="handleDeleteDim(dim)"
36
+                  v-hasPermi="['score:dimension:remove']" />
24
               </div>
37
               </div>
25
             </div>
38
             </div>
26
           </div>
39
           </div>
@@ -32,20 +45,21 @@
32
 
45
 
33
       <!-- 右侧:指标树 -->
46
       <!-- 右侧:指标树 -->
34
       <el-col :span="17">
47
       <el-col :span="17">
35
-        <el-card shadow="never">
48
+        <el-card shadow="never" >
36
           <template #header>
49
           <template #header>
37
             <div class="card-header">
50
             <div class="card-header">
38
               <span>{{ selectedDim ? selectedDim.name + ' — 指标树' : '请选择左侧维度' }}</span>
51
               <span>{{ selectedDim ? selectedDim.name + ' — 指标树' : '请选择左侧维度' }}</span>
39
               <div v-if="selectedDim">
52
               <div v-if="selectedDim">
40
-                <el-button size="small" icon="Plus" type="primary" @click="handleAddIndicator(null)" v-hasPermi="['score:indicator:add']">新增二级指标</el-button>
53
+                <el-button size="small" icon="Plus" type="primary" @click="handleAddIndicator(null)"
54
+                  v-hasPermi="['score:indicator:add']">新增二级指标</el-button>
41
                 <el-button size="small" icon="Refresh" @click="loadTree" />
55
                 <el-button size="small" icon="Refresh" @click="loadTree" />
42
               </div>
56
               </div>
43
             </div>
57
             </div>
44
           </template>
58
           </template>
45
 
59
 
60
+
46
           <el-table v-loading="treeLoading" :data="indicatorTree" row-key="id"
61
           <el-table v-loading="treeLoading" :data="indicatorTree" row-key="id"
47
-                    :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
48
-                    border default-expand-all>
62
+            :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" border default-expand-all>
49
             <el-table-column label="指标名称" prop="name" min-width="220" />
63
             <el-table-column label="指标名称" prop="name" min-width="220" />
50
             <el-table-column label="层级" align="center" width="70">
64
             <el-table-column label="层级" align="center" width="70">
51
               <template #default="{ row }">
65
               <template #default="{ row }">
@@ -56,7 +70,8 @@
56
             </el-table-column>
70
             </el-table-column>
57
             <el-table-column label="类型" align="center" width="80">
71
             <el-table-column label="类型" align="center" width="80">
58
               <template #default="{ row }">
72
               <template #default="{ row }">
59
-                <el-tag v-if="row.type" size="small" :type="row.type === '1' ? 'success' : row.type === '2' ? 'danger' : 'info'">
73
+                <el-tag v-if="row.type" size="small"
74
+                  :type="row.type === '1' ? 'success' : row.type === '2' ? 'danger' : 'info'">
60
                   {{ row.type === '1' ? '加分' : row.type === '2' ? '扣分' : '记录' }}
75
                   {{ row.type === '1' ? '加分' : row.type === '2' ? '扣分' : '记录' }}
61
                 </el-tag>
76
                 </el-tag>
62
               </template>
77
               </template>
@@ -64,7 +79,7 @@
64
             <el-table-column label="分值" align="center" width="90">
79
             <el-table-column label="分值" align="center" width="90">
65
               <template #default="{ row }">
80
               <template #default="{ row }">
66
                 <span v-if="row.scoreValue != null"
81
                 <span v-if="row.scoreValue != null"
67
-                      :style="{ color: row.scoreValue > 0 ? '#67c23a' : row.scoreValue < 0 ? '#f56c6c' : '' }">
82
+                  :style="{ color: row.scoreValue > 0 ? '#67c23a' : row.scoreValue < 0 ? '#f56c6c' : '' }">
68
                   {{ row.scoreValue > 0 ? '+' : '' }}{{ row.scoreValue }}
83
                   {{ row.scoreValue > 0 ? '+' : '' }}{{ row.scoreValue }}
69
                 </span>
84
                 </span>
70
               </template>
85
               </template>
@@ -72,21 +87,22 @@
72
             <el-table-column label="叠加规则" prop="cascadeRule" min-width="180" show-overflow-tooltip />
87
             <el-table-column label="叠加规则" prop="cascadeRule" min-width="180" show-overflow-tooltip />
73
             <el-table-column label="状态" align="center" width="80">
88
             <el-table-column label="状态" align="center" width="80">
74
               <template #default="{ row }">
89
               <template #default="{ row }">
75
-                <el-switch v-model="row.status" active-value="0" inactive-value="1"
76
-                           @change="handleStatusChange(row)" v-hasPermi="['score:indicator:edit']" />
90
+                <el-switch v-model="row.status" active-value="0" inactive-value="1" @change="handleStatusChange(row)"
91
+                  v-hasPermi="['score:indicator:edit']" />
77
               </template>
92
               </template>
78
             </el-table-column>
93
             </el-table-column>
79
             <el-table-column label="操作" align="center" width="160">
94
             <el-table-column label="操作" align="center" width="160">
80
               <template #default="{ row }">
95
               <template #default="{ row }">
81
                 <el-button v-if="row.level < 4" link type="primary" size="small" icon="Plus"
96
                 <el-button v-if="row.level < 4" link type="primary" size="small" icon="Plus"
82
-                           @click="handleAddIndicator(row)" v-hasPermi="['score:indicator:add']">子级</el-button>
83
-                <el-button link type="primary" size="small" icon="Edit"
84
-                           @click="handleEditIndicator(row)" v-hasPermi="['score:indicator:edit']">修改</el-button>
85
-                <el-button link type="danger" size="small" icon="Delete"
86
-                           @click="handleDeleteIndicator(row)" v-hasPermi="['score:indicator:remove']">删除</el-button>
97
+                  @click="handleAddIndicator(row)" v-hasPermi="['score:indicator:add']">子级</el-button>
98
+                <el-button link type="primary" size="small" icon="Edit" @click="handleEditIndicator(row)"
99
+                  v-hasPermi="['score:indicator:edit']">修改</el-button>
100
+                <el-button link type="danger" size="small" icon="Delete" @click="handleDeleteIndicator(row)"
101
+                  v-hasPermi="['score:indicator:remove']">删除</el-button>
87
               </template>
102
               </template>
88
             </el-table-column>
103
             </el-table-column>
89
           </el-table>
104
           </el-table>
105
+
90
         </el-card>
106
         </el-card>
91
       </el-col>
107
       </el-col>
92
     </el-row>
108
     </el-row>
@@ -145,12 +161,10 @@
145
           </el-radio-group>
161
           </el-radio-group>
146
         </el-form-item>
162
         </el-form-item>
147
         <el-form-item label="分值" prop="scoreValue">
163
         <el-form-item label="分值" prop="scoreValue">
148
-          <el-input-number v-model="indForm.scoreValue" :precision="2" style="width:100%"
149
-                           placeholder="正数=加分,负数=扣分" />
164
+          <el-input-number v-model="indForm.scoreValue" :precision="2" style="width:100%" placeholder="正数=加分,负数=扣分" />
150
         </el-form-item>
165
         </el-form-item>
151
         <el-form-item label="叠加规则">
166
         <el-form-item label="叠加规则">
152
-          <el-input v-model="indForm.cascadeRule" type="textarea" :rows="2"
153
-                    placeholder="如:返航/二次清舱-10、航班延误-8..." />
167
+          <el-input v-model="indForm.cascadeRule" type="textarea" :rows="2" placeholder="如:返航/二次清舱-10、航班延误-8..." />
154
         </el-form-item>
168
         </el-form-item>
155
         <el-form-item label="排序">
169
         <el-form-item label="排序">
156
           <el-input-number v-model="indForm.sortOrder" :min="0" style="width:100%" />
170
           <el-input-number v-model="indForm.sortOrder" :min="0" style="width:100%" />
@@ -174,7 +188,7 @@
174
 </template>
188
 </template>
175
 
189
 
176
 <script setup>
190
 <script setup>
177
-import { ref, reactive, computed, onMounted } from 'vue'
191
+import { ref, reactive, computed, onMounted, getCurrentInstance } from 'vue'
178
 import { ElMessage, ElMessageBox } from 'element-plus'
192
 import { ElMessage, ElMessageBox } from 'element-plus'
179
 import {
193
 import {
180
   listDimension, addDimension, updateDimension, delDimension,
194
   listDimension, addDimension, updateDimension, delDimension,
@@ -183,6 +197,11 @@ import {
183
 
197
 
184
 defineOptions({ name: 'ScoreDimension' })
198
 defineOptions({ name: 'ScoreDimension' })
185
 
199
 
200
+const { proxy } = getCurrentInstance()
201
+const { score_level } = proxy.useDict('score_level')
202
+
203
+const scoreLevel = ref('')
204
+
186
 // ===== 维度 =====
205
 // ===== 维度 =====
187
 const dimLoading = ref(false)
206
 const dimLoading = ref(false)
188
 const dimList = ref([])
207
 const dimList = ref([])
@@ -251,7 +270,7 @@ const parentIndicatorName = ref('')
251
 async function loadTree() {
270
 async function loadTree() {
252
   if (!selectedDim.value) return
271
   if (!selectedDim.value) return
253
   treeLoading.value = true
272
   treeLoading.value = true
254
-  const r = await treeIndicator(selectedDim.value.id).finally(() => treeLoading.value = false)
273
+  const r = await treeIndicator({ dimensionId: selectedDim.value.id, scoreLevel: scoreLevel.value }).finally(() => treeLoading.value = false)
255
   indicatorTree.value = r.data || []
274
   indicatorTree.value = r.data || []
256
 }
275
 }
257
 
276
 
@@ -292,18 +311,61 @@ onMounted(loadDimensions)
292
 </script>
311
 </script>
293
 
312
 
294
 <style scoped>
313
 <style scoped>
295
-.score-dimension-page { height: calc(100vh - 130px); }
296
-.dim-card { height: 100%; }
297
-.card-header { display: flex; justify-content: space-between; align-items: center; }
314
+.score-dimension-page {
315
+  /* height: calc(100vh - 130px); */
316
+}
317
+
318
+.dim-card {
319
+  height: 100%;
320
+}
321
+
322
+.card-header {
323
+  display: flex;
324
+  justify-content: space-between;
325
+  align-items: center;
326
+}
327
+
298
 .dim-item {
328
 .dim-item {
299
-  display: flex; justify-content: space-between; align-items: center;
300
-  padding: 10px 12px; border-radius: 6px; cursor: pointer;
301
-  margin-bottom: 6px; border: 1px solid #ebeef5; transition: all .2s;
329
+  display: flex;
330
+  justify-content: space-between;
331
+  align-items: center;
332
+  padding: 10px 12px;
333
+  border-radius: 6px;
334
+  cursor: pointer;
335
+  margin-bottom: 6px;
336
+  border: 1px solid #ebeef5;
337
+  transition: all .2s;
338
+}
339
+
340
+.dim-item:hover {
341
+  border-color: #409eff;
342
+  background: #f0f7ff;
343
+}
344
+
345
+.dim-item.active {
346
+  border-color: #409eff;
347
+  background: #ecf5ff;
348
+}
349
+
350
+.dim-name {
351
+  display: flex;
352
+  align-items: center;
353
+}
354
+
355
+.dim-weight {
356
+  font-size: 12px;
357
+  color: #909399;
358
+}
359
+
360
+.dim-actions {
361
+  display: flex;
362
+  gap: 4px;
363
+}
364
+
365
+.weight-total {
366
+  text-align: right;
367
+  margin-top: 12px;
368
+  font-size: 13px;
369
+  color: #606266;
302
 }
370
 }
303
-.dim-item:hover { border-color: #409eff; background: #f0f7ff; }
304
-.dim-item.active { border-color: #409eff; background: #ecf5ff; }
305
-.dim-name { display: flex; align-items: center; }
306
-.dim-weight { font-size: 12px; color: #909399; }
307
-.dim-actions { display: flex; gap: 4px; }
308
-.weight-total { text-align: right; margin-top: 12px; font-size: 13px; color: #606266; }
309
 </style>
371
 </style>

+ 159 - 47
src/views/score/event/index.vue

@@ -10,6 +10,9 @@
10
       <el-form-item label="队室/班组" prop="teamName">
10
       <el-form-item label="队室/班组" prop="teamName">
11
         <el-input v-model="queryParams.teamName" placeholder="请输入队室/班组" clearable @keyup.enter="handleQuery" />
11
         <el-input v-model="queryParams.teamName" placeholder="请输入队室/班组" clearable @keyup.enter="handleQuery" />
12
       </el-form-item>
12
       </el-form-item>
13
+      <el-form-item label="通道/小组" prop="groupName">
14
+        <el-input v-model="queryParams.groupName" placeholder="请输入通道/小组" clearable @keyup.enter="handleQuery" />
15
+      </el-form-item>
13
       <el-form-item label="维度" prop="dimensionId">
16
       <el-form-item label="维度" prop="dimensionId">
14
         <el-select v-model="queryParams.dimensionId" placeholder="全部维度" clearable style="width:150px">
17
         <el-select v-model="queryParams.dimensionId" placeholder="全部维度" clearable style="width:150px">
15
           <el-option v-for="d in dimensionOptions" :key="d.id" :label="d.name" :value="d.id" />
18
           <el-option v-for="d in dimensionOptions" :key="d.id" :label="d.name" :value="d.id" />
@@ -22,8 +25,8 @@
22
         </el-select>
25
         </el-select>
23
       </el-form-item>
26
       </el-form-item>
24
       <el-form-item label="事件时间">
27
       <el-form-item label="事件时间">
25
-        <el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD"
26
-          range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" clearable />
28
+        <el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-"
29
+          start-placeholder="开始日期" end-placeholder="结束日期" clearable />
27
       </el-form-item>
30
       </el-form-item>
28
       <el-form-item>
31
       <el-form-item>
29
         <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
32
         <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
@@ -36,13 +39,16 @@
36
         <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['score:event:add']">新增</el-button>
39
         <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['score:event:add']">新增</el-button>
37
       </el-col>
40
       </el-col>
38
       <el-col :span="1.5">
41
       <el-col :span="1.5">
39
-        <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['score:event:edit']">修改</el-button>
42
+        <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate"
43
+          v-hasPermi="['score:event:edit']">修改</el-button>
40
       </el-col>
44
       </el-col>
41
       <el-col :span="1.5">
45
       <el-col :span="1.5">
42
-        <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['score:event:remove']">删除</el-button>
46
+        <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete"
47
+          v-hasPermi="['score:event:remove']">删除</el-button>
43
       </el-col>
48
       </el-col>
44
       <el-col :span="1.5">
49
       <el-col :span="1.5">
45
-        <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['score:event:export']">导出</el-button>
50
+        <el-button type="warning" plain icon="Download" @click="handleExport"
51
+          v-hasPermi="['score:event:export']">导出</el-button>
46
       </el-col>
52
       </el-col>
47
       <el-col :span="1.5">
53
       <el-col :span="1.5">
48
         <el-upload :show-file-list="false" accept=".xlsx,.xls" :auto-upload="false" :on-change="handleImportFile">
54
         <el-upload :show-file-list="false" accept=".xlsx,.xls" :auto-upload="false" :on-change="handleImportFile">
@@ -93,20 +99,30 @@
93
         </template>
99
         </template>
94
       </el-table-column>
100
       </el-table-column>
95
       <el-table-column label="事件描述" align="center" prop="eventDesc" show-overflow-tooltip />
101
       <el-table-column label="事件描述" align="center" prop="eventDesc" show-overflow-tooltip />
96
-      <el-table-column label="操作" align="center" width="120">
102
+      <el-table-column label="操作" align="center" width="180">
97
         <template #default="{ row }">
103
         <template #default="{ row }">
98
-          <el-button link type="primary" icon="Edit" @click="handleUpdate(row)" v-hasPermi="['score:event:edit']">修改</el-button>
99
-          <el-button link type="danger" icon="Delete" @click="handleDelete(row)" v-hasPermi="['score:event:remove']">删除</el-button>
104
+          <el-button link type="primary" icon="Edit" @click="handleUpdate(row)"
105
+            v-hasPermi="['score:event:edit']">修改</el-button>
106
+          <el-button link type="danger" icon="Delete" @click="handleDelete(row)"
107
+            v-hasPermi="['score:event:remove']">删除</el-button>
100
         </template>
108
         </template>
101
       </el-table-column>
109
       </el-table-column>
102
     </el-table>
110
     </el-table>
103
-    <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
111
+    <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
112
+      v-model:limit="queryParams.pageSize" @pagination="getList" />
104
 
113
 
105
     <!-- 新增/修改对话框 -->
114
     <!-- 新增/修改对话框 -->
106
     <el-dialog :title="dialogTitle" v-model="dialogVisible" width="680px" append-to-body>
115
     <el-dialog :title="dialogTitle" v-model="dialogVisible" width="680px" append-to-body>
107
       <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
116
       <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
108
         <el-row :gutter="16">
117
         <el-row :gutter="16">
109
           <el-col :span="12">
118
           <el-col :span="12">
119
+            <el-form-item label="配分层级" prop="org">
120
+              <el-select v-model="form.org" placeholder="请选择层级" style="width:100%">
121
+                <el-option v-for="dict in score_level" :key="dict.value" :label="dict.label" :value="dict.value" />
122
+              </el-select>
123
+            </el-form-item>
124
+          </el-col>
125
+          <el-col :span="12">
110
             <el-form-item label="维度" prop="dimensionId">
126
             <el-form-item label="维度" prop="dimensionId">
111
               <el-select v-model="form.dimensionId" placeholder="请选择维度" style="width:100%" @change="onDimensionChange">
127
               <el-select v-model="form.dimensionId" placeholder="请选择维度" style="width:100%" @change="onDimensionChange">
112
                 <el-option v-for="d in dimensionOptions" :key="d.id" :label="d.name" :value="d.id" />
128
                 <el-option v-for="d in dimensionOptions" :key="d.id" :label="d.name" :value="d.id" />
@@ -114,23 +130,25 @@
114
             </el-form-item>
130
             </el-form-item>
115
           </el-col>
131
           </el-col>
116
           <el-col :span="12">
132
           <el-col :span="12">
117
-            <el-form-item label="二级指标" prop="level2Name">
118
-              <el-select v-model="form.level2Name" placeholder="请选择" style="width:100%" clearable @change="onLevel2Change">
119
-                <el-option v-for="n in level2Options" :key="n" :label="n" :value="n" />
133
+            <el-form-item label="二级指标" prop="level2Id">
134
+              <el-select v-model="form.level2Id" placeholder="请选择" style="width:100%" clearable
135
+                @change="onLevel2Change">
136
+                <el-option v-for="n in level2Options" :key="n.id" :label="n.name" :value="n.id" />
120
               </el-select>
137
               </el-select>
121
             </el-form-item>
138
             </el-form-item>
122
           </el-col>
139
           </el-col>
123
           <el-col :span="12">
140
           <el-col :span="12">
124
-            <el-form-item label="三级指标" prop="level3Name">
125
-              <el-select v-model="form.level3Name" placeholder="请选择" style="width:100%" clearable @change="onLevel3Change">
126
-                <el-option v-for="n in level3Options" :key="n" :label="n" :value="n" />
141
+            <el-form-item label="三级指标" prop="level3Id">
142
+              <el-select v-model="form.level3Id" placeholder="请选择" style="width:100%" clearable
143
+                @change="onLevel3Change">
144
+                <el-option v-for="n in level3Options" :key="n.id" :label="n.name" :value="n.id" />
127
               </el-select>
145
               </el-select>
128
             </el-form-item>
146
             </el-form-item>
129
           </el-col>
147
           </el-col>
130
           <el-col :span="12">
148
           <el-col :span="12">
131
             <el-form-item label="四级指标">
149
             <el-form-item label="四级指标">
132
-              <el-select v-model="form.level4Name" placeholder="请选择" style="width:100%" clearable>
133
-                <el-option v-for="n in level4Options" :key="n" :label="n" :value="n" />
150
+              <el-select v-model="form.level4Id" placeholder="请选择" style="width:100%" clearable @change="onLevel4Change">
151
+                <el-option v-for="n in level4Options" :key="n.id" :label="n.name" :value="n.id" />
134
               </el-select>
152
               </el-select>
135
             </el-form-item>
153
             </el-form-item>
136
           </el-col>
154
           </el-col>
@@ -146,35 +164,45 @@
146
             </el-form-item>
164
             </el-form-item>
147
           </el-col>
165
           </el-col>
148
           <el-col :span="12">
166
           <el-col :span="12">
149
-            <el-form-item label="责任人" prop="personName">
150
-              <el-input v-model="form.personName" placeholder="请输入责任人姓名" />
167
+            <el-form-item label="部门名称">
168
+              <el-select v-model="form.deptId" placeholder="请选择部门" style="width:100%" clearable filterable
169
+                @change="handleDeptChange">
170
+                <el-option v-for="d in deptOptions" :key="d.deptId" :label="d.deptName" :value="d.deptId" />
171
+              </el-select>
151
             </el-form-item>
172
             </el-form-item>
152
           </el-col>
173
           </el-col>
153
           <el-col :span="12">
174
           <el-col :span="12">
154
-            <el-form-item label="部门名称">
155
-              <el-input v-model="form.deptName" placeholder="请输入部门名称" />
175
+            <el-form-item label="队室/班组">
176
+              <el-select v-model="form.teamId" placeholder="请选择队室/班组" style="width:100%" clearable
177
+                :disabled="form.org < 2" @change="handleTeamChange">
178
+                <el-option v-for="t in teamOptions" :key="t.id" :label="t.label" :value="t.id" />
179
+              </el-select>
156
             </el-form-item>
180
             </el-form-item>
157
           </el-col>
181
           </el-col>
158
           <el-col :span="12">
182
           <el-col :span="12">
159
-            <el-form-item label="队室/班组">
160
-              <el-input v-model="form.teamName" placeholder="请输入队室/班组" />
183
+            <el-form-item label="通道/小组">
184
+              <el-select v-model="form.groupId" placeholder="请选择通道/小组" style="width:100%" clearable
185
+                :disabled="form.org < 3" @change="handleGroupChange">
186
+                <el-option v-for="g in groupOptions" :key="g.id" :label="g.label" :value="g.id" />
187
+              </el-select>
161
             </el-form-item>
188
             </el-form-item>
162
           </el-col>
189
           </el-col>
163
           <el-col :span="12">
190
           <el-col :span="12">
164
-            <el-form-item label="小组">
165
-              <el-input v-model="form.groupName" placeholder="请输入小组" />
191
+            <el-form-item label="责任人">
192
+              <el-select v-model="form.personId" placeholder="请选择责任人" style="width:100%" clearable
193
+                :disabled="form.org < 4">
194
+                <el-option v-for="p in personOptions" :key="p.userId" :label="p.nickName" :value="p.userId" />
195
+              </el-select>
166
             </el-form-item>
196
             </el-form-item>
167
           </el-col>
197
           </el-col>
168
           <el-col :span="12">
198
           <el-col :span="12">
169
             <el-form-item label="基础分值" prop="scoreValue">
199
             <el-form-item label="基础分值" prop="scoreValue">
170
-              <el-input-number v-model="form.scoreValue" :precision="2" style="width:100%"
171
-                               placeholder="正数=加分 负数=扣分" />
200
+              <el-input-number v-model="form.scoreValue" :precision="2" style="width:100%" placeholder="正数=加分 负数=扣分" />
172
             </el-form-item>
201
             </el-form-item>
173
           </el-col>
202
           </el-col>
174
           <el-col :span="12">
203
           <el-col :span="12">
175
             <el-form-item label="叠加分值">
204
             <el-form-item label="叠加分值">
176
-              <el-input-number v-model="form.cascadeScore" :precision="2" style="width:100%"
177
-                               placeholder="叠加后果产生的分值" />
205
+              <el-input-number v-model="form.cascadeScore" :precision="2" style="width:100%" placeholder="叠加后果产生的分值" />
178
             </el-form-item>
206
             </el-form-item>
179
           </el-col>
207
           </el-col>
180
           <el-col :span="24">
208
           <el-col :span="24">
@@ -198,17 +226,23 @@
198
 </template>
226
 </template>
199
 
227
 
200
 <script setup>
228
 <script setup>
201
-import { ref, reactive, onMounted } from 'vue'
229
+import { ref, reactive, computed, onMounted, getCurrentInstance } from 'vue'
202
 import { ElMessage, ElMessageBox } from 'element-plus'
230
 import { ElMessage, ElMessageBox } from 'element-plus'
231
+import { listUser } from '@/api/system/user'
203
 import {
232
 import {
204
   listScoreEvent, addScoreEvent, updateScoreEvent, delScoreEvent,
233
   listScoreEvent, addScoreEvent, updateScoreEvent, delScoreEvent,
205
   exportScoreEvent, importScoreEvent
234
   exportScoreEvent, importScoreEvent
206
 } from '@/api/score/index'
235
 } from '@/api/score/index'
207
 import { allDimension, treeIndicator } from '@/api/score/index'
236
 import { allDimension, treeIndicator } from '@/api/score/index'
237
+import { listDept } from '@/api/system/dept'
238
+import { deptTreeSelect } from '@/api/system/user'
208
 import { parseTime } from '@/utils/ruoyi'
239
 import { parseTime } from '@/utils/ruoyi'
209
 
240
 
210
 defineOptions({ name: 'ScoreEvent' })
241
 defineOptions({ name: 'ScoreEvent' })
211
 
242
 
243
+const { proxy } = getCurrentInstance()
244
+const { score_level } = proxy.useDict('score_level')
245
+
212
 const loading = ref(false), list = ref([]), total = ref(0), showSearch = ref(true)
246
 const loading = ref(false), list = ref([]), total = ref(0), showSearch = ref(true)
213
 const dateRange = ref([]), queryRef = ref(null), formRef = ref(null)
247
 const dateRange = ref([]), queryRef = ref(null), formRef = ref(null)
214
 const dialogVisible = ref(false), dialogTitle = ref('')
248
 const dialogVisible = ref(false), dialogTitle = ref('')
@@ -218,9 +252,13 @@ const indicatorTree = ref([])  // 当前维度的指标树(扁平+嵌套)
218
 const level2Options = ref([])
252
 const level2Options = ref([])
219
 const level3Options = ref([])
253
 const level3Options = ref([])
220
 const level4Options = ref([])
254
 const level4Options = ref([])
255
+const deptOptions = ref([])
256
+const teamOptions = ref([])
257
+const groupOptions = ref([])
258
+const personOptions = ref([])
221
 
259
 
222
-const queryParams = reactive({ pageNum: 1, pageSize: 10, personName: '', deptName: '', teamName: '', dimensionId: null, sourceType: '' })
223
-const form = reactive({ id: null, dimensionId: null, dimensionName: '', indicatorId: null, level2Name: '', level3Name: '', level4Name: '', eventTime: '', location: '', personName: '', deptName: '', teamName: '', groupName: '', scoreValue: 0, cascadeScore: 0, eventDesc: '', remark: '' })
260
+const queryParams = reactive({ pageNum: 1, pageSize: 10, personName: '', deptName: '', teamName: '', dimensionId: null, sourceType: '', org: '' })
261
+const form = reactive({ id: null, dimensionId: null, dimensionName: '', indicatorId: null, level2Id: null, level2Name: '', level3Id: null, level3Name: '', level4Id: null, level4Name: '', eventTime: '', location: '', personId: null, deptName: '', deptId: null, teamId: null, groupId: null, scoreValue: 0, cascadeScore: 0, eventDesc: '', remark: '', org: '' })
224
 const rules = {
262
 const rules = {
225
   dimensionId: [{ required: true, message: '请选择维度', trigger: 'change' }],
263
   dimensionId: [{ required: true, message: '请选择维度', trigger: 'change' }],
226
   personName: [{ required: true, message: '请输入责任人', trigger: 'blur' }],
264
   personName: [{ required: true, message: '请输入责任人', trigger: 'blur' }],
@@ -228,11 +266,62 @@ const rules = {
228
   scoreValue: [{ required: true, message: '请输入分值', trigger: 'blur' }]
266
   scoreValue: [{ required: true, message: '请输入分值', trigger: 'blur' }]
229
 }
267
 }
230
 
268
 
269
+
270
+
231
 async function loadDimensions() {
271
 async function loadDimensions() {
232
   const r = await allDimension()
272
   const r = await allDimension()
233
   dimensionOptions.value = r.data || []
273
   dimensionOptions.value = r.data || []
234
 }
274
 }
235
 
275
 
276
+async function loadDepts() {
277
+  const r = await listDept()
278
+  deptOptions.value = (r.data || []).filter(d => d.deptType === 'BRIGADE')
279
+}
280
+
281
+async function handleDeptChange(deptId, preserve) {
282
+  if (!preserve) {
283
+    form.teamId = null; form.groupId = null; form.personId = null
284
+    groupOptions.value = []; personOptions.value = []
285
+  }
286
+  if (deptId) {
287
+    const r = await deptTreeSelect({ parentId: deptId })
288
+    teamOptions.value = r.data || []
289
+    if (preserve && form.teamId) {
290
+      await handleTeamChange(form.teamId, true)
291
+    }
292
+  } else {
293
+    teamOptions.value = []
294
+  }
295
+}
296
+
297
+async function handleTeamChange(val, preserve) {
298
+  if (!preserve) {
299
+    form.groupId = null; form.personId = null
300
+    personOptions.value = []
301
+  }
302
+  const team = teamOptions.value.find(t => t.id === val)
303
+  if (team) {
304
+    const r = await deptTreeSelect({ parentId: team.id })
305
+    groupOptions.value = r.data || []
306
+    if (preserve && form.groupId) {
307
+      await handleGroupChange(form.groupId, true)
308
+    }
309
+  } else {
310
+    groupOptions.value = []
311
+  }
312
+}
313
+
314
+async function handleGroupChange(val, preserve) {
315
+  if (!preserve) form.personId = null
316
+  const group = groupOptions.value.find(g => g.id === val)
317
+  if (group) {
318
+    const r = await listUser({ deptId: group.id })
319
+    personOptions.value = r.rows || []
320
+  } else {
321
+    personOptions.value = []
322
+  }
323
+}
324
+
236
 function getList() {
325
 function getList() {
237
   loading.value = true
326
   loading.value = true
238
   const p = { ...queryParams }
327
   const p = { ...queryParams }
@@ -245,8 +334,9 @@ function resetQuery() { dateRange.value = []; queryRef.value?.resetFields(); han
245
 function handleSelectionChange(sel) { ids.value = sel.map(s => s.id); single.value = sel.length !== 1; multiple.value = !sel.length }
334
 function handleSelectionChange(sel) { ids.value = sel.map(s => s.id); single.value = sel.length !== 1; multiple.value = !sel.length }
246
 
335
 
247
 function resetForm() {
336
 function resetForm() {
248
-  Object.assign(form, { id: null, dimensionId: null, dimensionName: '', indicatorId: null, level2Name: '', level3Name: '', level4Name: '', eventTime: '', location: '', personName: '', deptName: '', teamName: '', groupName: '', scoreValue: 0, cascadeScore: 0, eventDesc: '', remark: '' })
337
+  Object.assign(form, { id: null, dimensionId: null, dimensionName: '', indicatorId: null, level2Id: null, level2Name: '', level3Id: null, level3Name: '', level4Id: null, level4Name: '', eventTime: '', location: '', personId: null, deptName: '', deptId: null, teamId: null, groupId: null, scoreValue: 0, cascadeScore: 0, eventDesc: '', remark: '', org: '' })
249
   level2Options.value = []; level3Options.value = []; level4Options.value = []
338
   level2Options.value = []; level3Options.value = []; level4Options.value = []
339
+  teamOptions.value = []; groupOptions.value = []; personOptions.value = []
250
 }
340
 }
251
 
341
 
252
 function handleAdd() { resetForm(); dialogTitle.value = '新增配分事项'; dialogVisible.value = true }
342
 function handleAdd() { resetForm(); dialogTitle.value = '新增配分事项'; dialogVisible.value = true }
@@ -256,6 +346,7 @@ function handleUpdate(row) {
256
   if (record) {
346
   if (record) {
257
     Object.assign(form, record)
347
     Object.assign(form, record)
258
     if (record.dimensionId) onDimensionChange(record.dimensionId, true)
348
     if (record.dimensionId) onDimensionChange(record.dimensionId, true)
349
+    if (record.deptId) handleDeptChange(record.deptId, true)
259
   }
350
   }
260
   dialogTitle.value = '修改配分事项'; dialogVisible.value = true
351
   dialogTitle.value = '修改配分事项'; dialogVisible.value = true
261
 }
352
 }
@@ -263,27 +354,45 @@ function handleUpdate(row) {
263
 async function onDimensionChange(dimId, preserveSelection) {
354
 async function onDimensionChange(dimId, preserveSelection) {
264
   const dim = dimensionOptions.value.find(d => d.id === dimId)
355
   const dim = dimensionOptions.value.find(d => d.id === dimId)
265
   if (dim) form.dimensionName = dim.name
356
   if (dim) form.dimensionName = dim.name
266
-  const r = await treeIndicator(dimId)
357
+  const r = await treeIndicator({ dimensionId: dimId })
267
   indicatorTree.value = r.data || []
358
   indicatorTree.value = r.data || []
268
-  level2Options.value = indicatorTree.value.map(n => n.name)
269
-  if (!preserveSelection) { form.level2Name = ''; form.level3Name = ''; form.level4Name = '' }
359
+  level2Options.value = indicatorTree.value.map(n => ({name: n.name,id: n.id, scoreValue: n.scoreValue}))
360
+  if (!preserveSelection) { form.level2Id = null; form.level2Name = ''; form.level3Id = null; form.level3Name = ''; form.level4Id = null; form.level4Name = ''; form.scoreValue = 0 }
270
   level3Options.value = []; level4Options.value = []
361
   level3Options.value = []; level4Options.value = []
271
-  if (preserveSelection && form.level2Name) onLevel2Change(form.level2Name, true)
362
+  if (preserveSelection && form.level2Id) onLevel2Change(form.level2Id, true)
272
 }
363
 }
273
 
364
 
274
 function onLevel2Change(val, preserveSelection) {
365
 function onLevel2Change(val, preserveSelection) {
275
-  const node = indicatorTree.value.find(n => n.name === val)
276
-  level3Options.value = node ? (node.children || []).map(n => n.name) : []
277
-  if (!preserveSelection) { form.level3Name = ''; form.level4Name = '' }
366
+  const node = indicatorTree.value.find(n => n.id === val)
367
+  if (node) {
368
+    form.level2Name = node.name
369
+    if (node.scoreValue !== undefined) form.scoreValue = node.scoreValue
370
+  }
371
+  level3Options.value = node ? (node.children || []).map(n => ({name: n.name,id: n.id, scoreValue: n.scoreValue})) : []
372
+  if (!preserveSelection) { form.level3Id = null; form.level3Name = ''; form.level4Id = null; form.level4Name = ''; if (node && node.scoreValue === undefined) form.scoreValue = 0 }
278
   level4Options.value = []
373
   level4Options.value = []
279
-  if (preserveSelection && form.level3Name) onLevel3Change(form.level3Name, true)
374
+  if (preserveSelection && form.level3Id) onLevel3Change(form.level3Id, true)
280
 }
375
 }
281
 
376
 
282
 function onLevel3Change(val, preserveSelection) {
377
 function onLevel3Change(val, preserveSelection) {
283
-  const level2Node = indicatorTree.value.find(n => n.name === form.level2Name)
284
-  const node = level2Node ? (level2Node.children || []).find(n => n.name === val) : null
285
-  level4Options.value = node ? (node.children || []).map(n => n.name) : []
286
-  if (!preserveSelection) form.level4Name = ''
378
+  const level2Node = indicatorTree.value.find(n => n.id === form.level2Id)
379
+  const node = level2Node ? (level2Node.children || []).find(n => n.id === val) : null
380
+  if (node) {
381
+    form.level3Name = node.name
382
+    if (node.scoreValue !== undefined) form.scoreValue = node.scoreValue
383
+  }
384
+  level4Options.value = node ? (node.children || []).map(n => ({name: n.name,id: n.id, scoreValue: n.scoreValue})) : []
385
+  if (!preserveSelection) { form.level4Id = null; form.level4Name = ''; if (node && node.scoreValue === undefined) form.scoreValue = 0 }
386
+}
387
+
388
+function onLevel4Change(val) {
389
+  const level2Node = indicatorTree.value.find(n => n.id === form.level2Id)
390
+  const level3Node = level2Node ? (level2Node.children || []).find(n => n.id === form.level3Id) : null
391
+  const node = level3Node ? (level3Node.children || []).find(n => n.id === val) : null
392
+  if (node) {
393
+    form.level4Name = node.name
394
+    if (node.scoreValue !== undefined) form.scoreValue = node.scoreValue
395
+  }
287
 }
396
 }
288
 
397
 
289
 async function submitForm() {
398
 async function submitForm() {
@@ -303,7 +412,10 @@ function handleDelete(row) {
303
 function handleExport() {
412
 function handleExport() {
304
   const p = { ...queryParams }
413
   const p = { ...queryParams }
305
   if (dateRange.value?.length === 2) { p['params[beginTime]'] = dateRange.value[0]; p['params[endTime]'] = dateRange.value[1] }
414
   if (dateRange.value?.length === 2) { p['params[beginTime]'] = dateRange.value[0]; p['params[endTime]'] = dateRange.value[1] }
306
-  exportScoreEvent(p)
415
+  // exportScoreEvent(p)
416
+  proxy.download('score/event/export', {
417
+    ...p
418
+  }, `配分事项_${new Date().getTime()}.xlsx`)
307
 }
419
 }
308
 
420
 
309
 async function handleImportFile(file) {
421
 async function handleImportFile(file) {
@@ -316,5 +428,5 @@ async function handleImportFile(file) {
316
   }
428
   }
317
 }
429
 }
318
 
430
 
319
-onMounted(() => { loadDimensions(); getList() })
431
+onMounted(() => { loadDimensions(); loadDepts(); getList() })
320
 </script>
432
 </script>