Explorar o código

feat: 新增多维度筛选与优化员工画像页面

1.  为扣分类型、区域、工作点、岗位新增搜索筛选条件
2.  调整事件描述列宽度,优化表格展示
3.  注释掉旧版员工画像页面的组件名定义,更新新版组件名
4.  移除SearchBar组件的onUnmounted钩子与多余的样式重置方法
5.  格式化代码缩进与对象空格,优化代码可读性
huoyi hai 1 semana
pai
achega
59c61ea0cb

+ 10 - 10
src/router/index.js

@@ -107,17 +107,17 @@ export const constantRoutes = [
107
       //   meta: { title: '机场安检员考勤管理系统', icon: 'dashboard' }
107
       //   meta: { title: '机场安检员考勤管理系统', icon: 'dashboard' }
108
       // },
108
       // },
109
       // {
109
       // {
110
-	    //   path: '/examQuestionStatistics',
111
-	    //   component: () => import('@/views/dataBigScreen/examQuestionStatistics'),
112
-	    //   name: 'examQuestionStatistics',
113
-	    //   meta: { title: '答题统计', icon: 'dashboard' }
114
-	    // },
110
+      //   path: '/examQuestionStatistics',
111
+      //   component: () => import('@/views/dataBigScreen/examQuestionStatistics'),
112
+      //   name: 'examQuestionStatistics',
113
+      //   meta: { title: '答题统计', icon: 'dashboard' }
114
+      // },
115
       // {
115
       // {
116
-	    //   path: '/abilityPortrait',
117
-	    //   component: () => import('@/views/dataBigScreen/abilityPortrait'),
118
-	    //   name: 'abilityPortrait',
119
-	    //   meta: { title: '能力画像显示大屏', icon: 'dashboard' }
120
-	    // }
116
+      //   path: '/abilityPortrait',
117
+      //   component: () => import('@/views/dataBigScreen/abilityPortrait'),
118
+      //   name: 'abilityPortrait',
119
+      //   meta: { title: '能力画像显示大屏', icon: 'dashboard' }
120
+      // }
121
     ]
121
     ]
122
   },
122
   },
123
 ]
123
 ]

+ 2 - 32
src/views/portraitManagement/components/SearchBar.vue

@@ -52,7 +52,7 @@
52
 </template>
52
 </template>
53
 
53
 
54
 <script setup>
54
 <script setup>
55
-import { ref, nextTick, onMounted, onUnmounted } from 'vue'
55
+import { ref, nextTick, onMounted } from 'vue'
56
 import { searchPortraitUsers } from '@/api/score/index'
56
 import { searchPortraitUsers } from '@/api/score/index'
57
 import { getDeptList, getDeptUserTree } from '@/api/item/items'
57
 import { getDeptList, getDeptUserTree } from '@/api/item/items'
58
 import { listDept } from '@/api/system/dept'
58
 import { listDept } from '@/api/system/dept'
@@ -265,32 +265,7 @@ const handleNodeClick = (node) => {
265
   }
265
   }
266
 }
266
 }
267
 
267
 
268
-const setStyle = () => {
269
-  const root = document.querySelector(':root')
270
-  root.style.setProperty('--el-bg-color-overlay', '#1c1936')
271
-  root.style.setProperty('--el-border-color-extra-light', '#5c676d')
272
-  root.style.setProperty('--el-border-color-light', 'transparent')
273
-  root.style.setProperty('--el-popover-padding', '0px')
274
-  root.style.setProperty('--el-text-color-regular', '#fff')
275
-  root.style.setProperty('--el-fill-color-light', '#5c676d')
276
-  root.style.setProperty('--el-text-color-primary', '#fff')
277
-}
278
-
279
-const resetStyle = () => {
280
-  const root = document.querySelector(':root')
281
-  root.style.setProperty('--el-bg-color-overlay', '#ffffff')
282
-  root.style.setProperty('--el-border-color-extra-light', '#f2f6fc')
283
-  root.style.setProperty('--el-border-color-light', '#e4e7ed')
284
-  root.style.setProperty('--el-popover-padding', '12px')
285
-  root.style.setProperty('--el-text-color-regular', '#606266')
286
-  root.style.setProperty('--el-fill-color-light', '#f5f7fa')
287
-  root.style.setProperty('--el-text-color-primary', '#303133')
288
-
289
-}
290
-
291
 onMounted(async () => {
268
 onMounted(async () => {
292
-  setStyle()
293
-
294
   if (props.deptType === 'user') {
269
   if (props.deptType === 'user') {
295
     const res = await getDeptUserTree()
270
     const res = await getDeptUserTree()
296
     departments.value = res.data
271
     departments.value = res.data
@@ -308,7 +283,7 @@ onMounted(async () => {
308
         const path = buildPathForNode(departments.value, defaultNode.deptId || defaultNode.id)
283
         const path = buildPathForNode(departments.value, defaultNode.deptId || defaultNode.id)
309
         const name = defaultNode.label
284
         const name = defaultNode.label
310
         personName.value = path.length > 0 ? `${path.join(' / ')} / ${name}` : name
285
         personName.value = path.length > 0 ? `${path.join(' / ')} / ${name}` : name
311
-        curQuery.value = { personName: defaultNode.label,id:defaultNode.id }
286
+        curQuery.value = { personName: defaultNode.label, id: defaultNode.id }
312
         searchHandler(curQuery.value)
287
         searchHandler(curQuery.value)
313
       } else {
288
       } else {
314
         const path = buildPathForNode(departments.value, defaultNode.deptId || defaultNode.id)
289
         const path = buildPathForNode(departments.value, defaultNode.deptId || defaultNode.id)
@@ -331,16 +306,11 @@ onMounted(async () => {
331
     }
306
     }
332
   }
307
   }
333
 })
308
 })
334
-onUnmounted(() => {
335
-  resetStyle()
336
-})
337
 
309
 
338
 defineExpose({
310
 defineExpose({
339
   getDefQuery() {
311
   getDefQuery() {
340
-
341
     return {
312
     return {
342
       ...getTimeRange(),
313
       ...getTimeRange(),
343
-
344
     }
314
     }
345
   }
315
   }
346
 })
316
 })

+ 6 - 2
src/views/portraitManagement/employeeProfile/index.vue

@@ -186,13 +186,14 @@
186
 
186
 
187
         <!-- 加分区域 -->
187
         <!-- 加分区域 -->
188
         <div class="tooltip-section">
188
         <div class="tooltip-section">
189
-          <div class="section-list">
189
+          <!--<div class="section-list">
190
             <div v-if="addList.length" class="list-item" v-for="(item, i) in addList" :key="i">
190
             <div v-if="addList.length" class="list-item" v-for="(item, i) in addList" :key="i">
191
               <span>{{ item.level3Name }}:</span>
191
               <span>{{ item.level3Name }}:</span>
192
               <span class="add-text">{{ item.totalScore }}分</span>
192
               <span class="add-text">{{ item.totalScore }}分</span>
193
             </div>
193
             </div>
194
             <div v-else class="p-empty">暂无加分记录</div>
194
             <div v-else class="p-empty">暂无加分记录</div>
195
           </div>
195
           </div>
196
+          -->
196
           <div class="section-total add">
197
           <div class="section-total add">
197
             <span>合计加分:</span>
198
             <span>合计加分:</span>
198
             <span>{{ addTotal }}分</span>
199
             <span>{{ addTotal }}分</span>
@@ -200,13 +201,14 @@
200
         </div>
201
         </div>
201
         <!-- 扣分区域 -->
202
         <!-- 扣分区域 -->
202
         <div class="tooltip-section">
203
         <div class="tooltip-section">
203
-          <div class="section-list">
204
+        <!--   <div class="section-list">
204
             <div v-if="deductList.length" class="list-item" v-for="(item, i) in deductList" :key="i">
205
             <div v-if="deductList.length" class="list-item" v-for="(item, i) in deductList" :key="i">
205
               <span>{{ item.level3Name }}:</span>
206
               <span>{{ item.level3Name }}:</span>
206
               <span class="deduct-text">{{ item.totalScore }}分</span>
207
               <span class="deduct-text">{{ item.totalScore }}分</span>
207
             </div>
208
             </div>
208
             <div v-if="!deductList.length" class="p-empty">暂无扣分记录</div>
209
             <div v-if="!deductList.length" class="p-empty">暂无扣分记录</div>
209
           </div>
210
           </div>
211
+          -->
210
           <div class="section-total deduct">
212
           <div class="section-total deduct">
211
             <span>合计扣分:</span>
213
             <span>合计扣分:</span>
212
             <span>{{ deductTotal }}分</span>
214
             <span>{{ deductTotal }}分</span>
@@ -229,6 +231,8 @@ import { useECharts } from '@/hooks/useEcharts'
229
 import useUserStore from '@/store/modules/user'
231
 import useUserStore from '@/store/modules/user'
230
 import { useRouter } from 'vue-router'
232
 import { useRouter } from 'vue-router'
231
 
233
 
234
+defineOptions({ name: 'ProfileEmployeeProfile' })
235
+
232
 const router = useRouter()
236
 const router = useRouter()
233
 let radarChartInstance = null
237
 let radarChartInstance = null
234
 
238
 

+ 1 - 1
src/views/portraitManagement/employeeProfile/indexOld.vue

@@ -380,7 +380,7 @@ import { searchPortraitUsers, getEmployeePortrait } from '@/api/score/index'
380
 import { getDeptUserTree } from '@/api/item/items'
380
 import { getDeptUserTree } from '@/api/item/items'
381
 import { useDict } from '@/utils/dict'
381
 import { useDict } from '@/utils/dict'
382
 
382
 
383
-defineOptions({ name: 'EmployeeProfile' })
383
+// defineOptions({ name: 'EmployeeProfile' })
384
 
384
 
385
 const route = useRoute()
385
 const route = useRoute()
386
 const currentTime = ref('year')
386
 const currentTime = ref('year')

+ 87 - 11
src/views/score/event/index.vue

@@ -45,6 +45,28 @@
45
         <el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-"
45
         <el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-"
46
           start-placeholder="开始日期" end-placeholder="结束日期" clearable />
46
           start-placeholder="开始日期" end-placeholder="结束日期" clearable />
47
       </el-form-item>
47
       </el-form-item>
48
+      <el-form-item label="扣分类型" prop="scoreType">
49
+        <el-select v-model="queryParams.scoreType" placeholder="全部类型" clearable style="width:150px">
50
+          <el-option v-for="dict in score_type" :key="dict.value" :label="dict.label" :value="dict.value" />
51
+        </el-select>
52
+      </el-form-item>
53
+      <el-form-item label="区域" prop="regionalId">
54
+        <el-select v-model="queryParams.regionalId" placeholder="请选择区域" clearable filterable
55
+          style="width:150px" @change="handleRegionalChange">
56
+          <el-option v-for="item in regionalOptions" :key="item.id" :label="item.positionName" :value="item.id" />
57
+        </el-select>
58
+      </el-form-item>
59
+      <el-form-item label="工作点" prop="channelId">
60
+        <el-select v-model="queryParams.channelId" placeholder="请选择工作点" clearable filterable
61
+          style="width:150px">
62
+          <el-option v-for="item in channelOptions" :key="item.id" :label="item.positionName" :value="item.id" />
63
+        </el-select>
64
+      </el-form-item>
65
+      <el-form-item label="岗位" prop="postId">
66
+        <el-tree-select v-model="queryParams.postId" :data="postTreeData"
67
+          :props="{ value: 'postId', label: 'postName', children: 'children' }" clearable filterable
68
+          placeholder="请选择岗位" style="width:150px" check-strictly />
69
+      </el-form-item>
48
       <el-form-item>
70
       <el-form-item>
49
         <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
71
         <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
50
         <el-button icon="Refresh" @click="resetQuery">重置</el-button>
72
         <el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -89,6 +111,9 @@
89
       <el-table-column label="责任人" align="center" prop="personName" width="80" />
111
       <el-table-column label="责任人" align="center" prop="personName" width="80" />
90
       <el-table-column label="部门" align="center" prop="deptName" />
112
       <el-table-column label="部门" align="center" prop="deptName" />
91
       <el-table-column label="班组" align="center" prop="teamName" />
113
       <el-table-column label="班组" align="center" prop="teamName" />
114
+      <el-table-column label="区域" align="center" prop="regionalName" />
115
+      <el-table-column label="工作点" align="center" prop="channelName" />
116
+      <el-table-column label="岗位" align="center" prop="postName" />
92
       <el-table-column label="基础分值" align="center" prop="scoreValue" width="90">
117
       <el-table-column label="基础分值" align="center" prop="scoreValue" width="90">
93
         <template #default="{ row }">
118
         <template #default="{ row }">
94
           <span :style="{ color: row.scoreValue > 0 ? '#67c23a' : row.scoreValue < 0 ? '#f56c6c' : '' }">
119
           <span :style="{ color: row.scoreValue > 0 ? '#67c23a' : row.scoreValue < 0 ? '#f56c6c' : '' }">
@@ -118,7 +143,7 @@
118
           </el-tag>
143
           </el-tag>
119
         </template>
144
         </template>
120
       </el-table-column>
145
       </el-table-column>
121
-      <el-table-column label="事件描述" align="center" prop="eventDesc" show-overflow-tooltip />
146
+      <el-table-column label="事件描述" width="460" align="center" prop="eventDesc" show-overflow-tooltip />
122
       <el-table-column label="操作" align="center" width="180">
147
       <el-table-column label="操作" align="center" width="180">
123
         <template #default="{ row }">
148
         <template #default="{ row }">
124
           <el-button link type="primary" icon="Edit" @click="handleUpdate(row)"
149
           <el-button link type="primary" icon="Edit" @click="handleUpdate(row)"
@@ -180,8 +205,19 @@
180
             </el-form-item>
205
             </el-form-item>
181
           </el-col>
206
           </el-col>
182
           <el-col :span="12">
207
           <el-col :span="12">
183
-            <el-form-item label="位置">
184
-              <el-input v-model="form.location" placeholder="区域/通道" />
208
+            <el-form-item label="区域">
209
+              <el-select v-model="form.regionalId" placeholder="请选择区域" clearable filterable
210
+                style="width:100%" @change="handleFormRegionalChange">
211
+                <el-option v-for="item in regionalOptions" :key="item.id" :label="item.positionName" :value="item.id" />
212
+              </el-select>
213
+            </el-form-item>
214
+          </el-col>
215
+          <el-col :span="12">
216
+            <el-form-item label="工作点">
217
+              <el-select v-model="form.channelId" placeholder="请选择工作点" clearable filterable
218
+                style="width:100%">
219
+                <el-option v-for="item in formChannelOptions" :key="item.id" :label="item.positionName" :value="item.id" />
220
+              </el-select>
185
             </el-form-item>
221
             </el-form-item>
186
           </el-col>
222
           </el-col>
187
           <el-col :span="12">
223
           <el-col :span="12">
@@ -217,6 +253,13 @@
217
             </el-form-item>
253
             </el-form-item>
218
           </el-col>
254
           </el-col>
219
           <el-col :span="12">
255
           <el-col :span="12">
256
+            <el-form-item label="岗位">
257
+              <el-tree-select v-model="form.postId" :data="postTreeData"
258
+                :props="{ value: 'postId', label: 'postName', children: 'children' }" clearable filterable
259
+                placeholder="请选择岗位" style="width:100%" check-strictly />
260
+            </el-form-item>
261
+          </el-col>
262
+          <el-col :span="12">
220
             <el-form-item label="基础分值" prop="scoreValue">
263
             <el-form-item label="基础分值" prop="scoreValue">
221
               <el-input-number v-model="form.scoreValue" :precision="2" style="width:100%" placeholder="正数=加分 负数=扣分" />
264
               <el-input-number v-model="form.scoreValue" :precision="2" style="width:100%" placeholder="正数=加分 负数=扣分" />
222
             </el-form-item>
265
             </el-form-item>
@@ -251,6 +294,8 @@ import { ref, reactive, computed, watch, onMounted, getCurrentInstance } from 'v
251
 import { ElMessage, ElMessageBox } from 'element-plus'
294
 import { ElMessage, ElMessageBox } from 'element-plus'
252
 import { useRoute } from 'vue-router'
295
 import { useRoute } from 'vue-router'
253
 import { listUser, getDeptOrgInfo, getUserOrgInfo } from '@/api/system/user'
296
 import { listUser, getDeptOrgInfo, getUserOrgInfo } from '@/api/system/user'
297
+import { listPosition } from '@/api/system/position'
298
+import { listAllTree } from '@/api/system/post'
254
 import {
299
 import {
255
   listScoreEvent, addScoreEvent, updateScoreEvent, delScoreEvent,
300
   listScoreEvent, addScoreEvent, updateScoreEvent, delScoreEvent,
256
   exportScoreEvent, importScoreEvent
301
   exportScoreEvent, importScoreEvent
@@ -263,7 +308,7 @@ import { parseTime } from '@/utils/ruoyi'
263
 defineOptions({ name: 'ScoreEvent' })
308
 defineOptions({ name: 'ScoreEvent' })
264
 
309
 
265
 const { proxy } = getCurrentInstance()
310
 const { proxy } = getCurrentInstance()
266
-const { score_level } = proxy.useDict('score_level')
311
+const { score_level, score_type } = proxy.useDict('score_level', 'score_type')
267
 
312
 
268
 const loading = ref(false), list = ref([]), total = ref(0), showSearch = ref(true)
313
 const loading = ref(false), list = ref([]), total = ref(0), showSearch = ref(true)
269
 const dateRange = ref([]), queryRef = ref(null), formRef = ref(null)
314
 const dateRange = ref([]), queryRef = ref(null), formRef = ref(null)
@@ -282,9 +327,13 @@ const queryDeptOptions = ref([])
282
 const queryTeamOptions = ref([])
327
 const queryTeamOptions = ref([])
283
 const queryGroupOptions = ref([])
328
 const queryGroupOptions = ref([])
284
 const queryPersonOptions = ref([])
329
 const queryPersonOptions = ref([])
330
+const regionalOptions = ref([])
331
+const channelOptions = ref([])
332
+const postTreeData = ref([])
285
 
333
 
286
-const queryParams = reactive({ pageNum: 1, pageSize: 10, personId: null, personName: '', deptId: null, deptName: '', teamId: null, teamName: '', groupId: null, groupName: '', dimensionId: null, sourceType: '', org: '' })
287
-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: '' })
334
+const queryParams = reactive({ pageNum: 1, pageSize: 10, personId: null, personName: '', deptId: null, deptName: '', teamId: null, teamName: '', groupId: null, groupName: '', dimensionId: null, sourceType: '', org: '', scoreType: null, regionalId: null, channelId: null, postId: null })
335
+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: '', regionalId: null, channelId: null, postId: null })
336
+const formChannelOptions = ref([])
288
 const rules = computed(() => {
337
 const rules = computed(() => {
289
   return {
338
   return {
290
     org: [{ required: true, message: '请选择配分层级', trigger: 'submit' }],
339
     org: [{ required: true, message: '请选择配分层级', trigger: 'submit' }],
@@ -360,6 +409,29 @@ async function loadAllPersons() {
360
   personOptions.value = r.rows || []
409
   personOptions.value = r.rows || []
361
 }
410
 }
362
 
411
 
412
+async function loadRegions() {
413
+  const r = await listPosition({ positionType: 'REGIONAL' })
414
+  regionalOptions.value = r.rows || []
415
+}
416
+
417
+async function handleRegionalChange(val) {
418
+  queryParams.channelId = null
419
+  channelOptions.value = []
420
+  if (val) {
421
+    const r = await listPosition({ parentId: val })
422
+    channelOptions.value = r.rows || []
423
+  }
424
+}
425
+
426
+async function handleFormRegionalChange(val) {
427
+  form.channelId = null
428
+  formChannelOptions.value = []
429
+  if (val) {
430
+    const r = await listPosition({ parentId: val })
431
+    formChannelOptions.value = r.rows || []
432
+  }
433
+}
434
+
363
 function handleBrigadeChange() {
435
 function handleBrigadeChange() {
364
 }
436
 }
365
 
437
 
@@ -456,18 +528,22 @@ function resetQuery() {
456
     groupName: '', 
528
     groupName: '', 
457
     dimensionId: null, 
529
     dimensionId: null, 
458
     sourceType: '', 
530
     sourceType: '', 
459
-    org: '' 
531
+    org: '', 
532
+    scoreType: null, 
533
+    regionalId: null, 
534
+    channelId: null, 
535
+    postId: null 
460
   })
536
   })
461
-  queryTeamOptions.value = []; queryGroupOptions.value = []; queryPersonOptions.value = []
537
+  queryTeamOptions.value = []; queryGroupOptions.value = []; queryPersonOptions.value = []; channelOptions.value = []
462
   queryRef.value?.resetFields()
538
   queryRef.value?.resetFields()
463
   handleQuery()
539
   handleQuery()
464
 }
540
 }
465
 function handleSelectionChange(sel) { ids.value = sel.map(s => s.id); single.value = sel.length !== 1; multiple.value = !sel.length }
541
 function handleSelectionChange(sel) { ids.value = sel.map(s => s.id); single.value = sel.length !== 1; multiple.value = !sel.length }
466
 
542
 
467
 function resetForm() {
543
 function resetForm() {
468
-  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: '' })
544
+  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: '', regionalId: null, channelId: null, postId: null })
469
   level2Options.value = []; level3Options.value = []; level4Options.value = []
545
   level2Options.value = []; level3Options.value = []; level4Options.value = []
470
-  teamOptions.value = []; groupOptions.value = []; personOptions.value = []
546
+  teamOptions.value = []; groupOptions.value = []; personOptions.value = []; formChannelOptions.value = []
471
 }
547
 }
472
 
548
 
473
 function handleAdd() { resetForm(); dialogTitle.value = '新增配分事项'; dialogVisible.value = true }
549
 function handleAdd() { resetForm(); dialogTitle.value = '新增配分事项'; dialogVisible.value = true }
@@ -619,5 +695,5 @@ watch(() => route.query, (query) => {
619
   isInit = false
695
   isInit = false
620
 }, { immediate: true })
696
 }, { immediate: true })
621
 
697
 
622
-onMounted(() => { loadDimensions(); loadDepts(); loadQueryDepts(); loadAllPersons(); getList() })
698
+onMounted(() => { loadDimensions(); loadDepts(); loadQueryDepts(); loadAllPersons(); loadRegions(); listAllTree().then(res => { postTreeData.value = res.data || [] }); getList() })
623
 </script>
699
 </script>