ソースを参照

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

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

+ 10 - 10
src/router/index.js

@@ -107,17 +107,17 @@ export const constantRoutes = [
107 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 52
 </template>
53 53
 
54 54
 <script setup>
55
-import { ref, nextTick, onMounted, onUnmounted } from 'vue'
55
+import { ref, nextTick, onMounted } from 'vue'
56 56
 import { searchPortraitUsers } from '@/api/score/index'
57 57
 import { getDeptList, getDeptUserTree } from '@/api/item/items'
58 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 268
 onMounted(async () => {
292
-  setStyle()
293
-
294 269
   if (props.deptType === 'user') {
295 270
     const res = await getDeptUserTree()
296 271
     departments.value = res.data
@@ -308,7 +283,7 @@ onMounted(async () => {
308 283
         const path = buildPathForNode(departments.value, defaultNode.deptId || defaultNode.id)
309 284
         const name = defaultNode.label
310 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 287
         searchHandler(curQuery.value)
313 288
       } else {
314 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 310
 defineExpose({
339 311
   getDefQuery() {
340
-
341 312
     return {
342 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 188
         <div class="tooltip-section">
189
-          <div class="section-list">
189
+          <!--<div class="section-list">
190 190
             <div v-if="addList.length" class="list-item" v-for="(item, i) in addList" :key="i">
191 191
               <span>{{ item.level3Name }}:</span>
192 192
               <span class="add-text">{{ item.totalScore }}分</span>
193 193
             </div>
194 194
             <div v-else class="p-empty">暂无加分记录</div>
195 195
           </div>
196
+          -->
196 197
           <div class="section-total add">
197 198
             <span>合计加分:</span>
198 199
             <span>{{ addTotal }}分</span>
@@ -200,13 +201,14 @@
200 201
         </div>
201 202
         <!-- 扣分区域 -->
202 203
         <div class="tooltip-section">
203
-          <div class="section-list">
204
+        <!--   <div class="section-list">
204 205
             <div v-if="deductList.length" class="list-item" v-for="(item, i) in deductList" :key="i">
205 206
               <span>{{ item.level3Name }}:</span>
206 207
               <span class="deduct-text">{{ item.totalScore }}分</span>
207 208
             </div>
208 209
             <div v-if="!deductList.length" class="p-empty">暂无扣分记录</div>
209 210
           </div>
211
+          -->
210 212
           <div class="section-total deduct">
211 213
             <span>合计扣分:</span>
212 214
             <span>{{ deductTotal }}分</span>
@@ -229,6 +231,8 @@ import { useECharts } from '@/hooks/useEcharts'
229 231
 import useUserStore from '@/store/modules/user'
230 232
 import { useRouter } from 'vue-router'
231 233
 
234
+defineOptions({ name: 'ProfileEmployeeProfile' })
235
+
232 236
 const router = useRouter()
233 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 380
 import { getDeptUserTree } from '@/api/item/items'
381 381
 import { useDict } from '@/utils/dict'
382 382
 
383
-defineOptions({ name: 'EmployeeProfile' })
383
+// defineOptions({ name: 'EmployeeProfile' })
384 384
 
385 385
 const route = useRoute()
386 386
 const currentTime = ref('year')

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

@@ -45,6 +45,28 @@
45 45
         <el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="-"
46 46
           start-placeholder="开始日期" end-placeholder="结束日期" clearable />
47 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 70
       <el-form-item>
49 71
         <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
50 72
         <el-button icon="Refresh" @click="resetQuery">重置</el-button>
@@ -89,6 +111,9 @@
89 111
       <el-table-column label="责任人" align="center" prop="personName" width="80" />
90 112
       <el-table-column label="部门" align="center" prop="deptName" />
91 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 117
       <el-table-column label="基础分值" align="center" prop="scoreValue" width="90">
93 118
         <template #default="{ row }">
94 119
           <span :style="{ color: row.scoreValue > 0 ? '#67c23a' : row.scoreValue < 0 ? '#f56c6c' : '' }">
@@ -118,7 +143,7 @@
118 143
           </el-tag>
119 144
         </template>
120 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 147
       <el-table-column label="操作" align="center" width="180">
123 148
         <template #default="{ row }">
124 149
           <el-button link type="primary" icon="Edit" @click="handleUpdate(row)"
@@ -180,8 +205,19 @@
180 205
             </el-form-item>
181 206
           </el-col>
182 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 221
             </el-form-item>
186 222
           </el-col>
187 223
           <el-col :span="12">
@@ -217,6 +253,13 @@
217 253
             </el-form-item>
218 254
           </el-col>
219 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 263
             <el-form-item label="基础分值" prop="scoreValue">
221 264
               <el-input-number v-model="form.scoreValue" :precision="2" style="width:100%" placeholder="正数=加分 负数=扣分" />
222 265
             </el-form-item>
@@ -251,6 +294,8 @@ import { ref, reactive, computed, watch, onMounted, getCurrentInstance } from 'v
251 294
 import { ElMessage, ElMessageBox } from 'element-plus'
252 295
 import { useRoute } from 'vue-router'
253 296
 import { listUser, getDeptOrgInfo, getUserOrgInfo } from '@/api/system/user'
297
+import { listPosition } from '@/api/system/position'
298
+import { listAllTree } from '@/api/system/post'
254 299
 import {
255 300
   listScoreEvent, addScoreEvent, updateScoreEvent, delScoreEvent,
256 301
   exportScoreEvent, importScoreEvent
@@ -263,7 +308,7 @@ import { parseTime } from '@/utils/ruoyi'
263 308
 defineOptions({ name: 'ScoreEvent' })
264 309
 
265 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 313
 const loading = ref(false), list = ref([]), total = ref(0), showSearch = ref(true)
269 314
 const dateRange = ref([]), queryRef = ref(null), formRef = ref(null)
@@ -282,9 +327,13 @@ const queryDeptOptions = ref([])
282 327
 const queryTeamOptions = ref([])
283 328
 const queryGroupOptions = ref([])
284 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 337
 const rules = computed(() => {
289 338
   return {
290 339
     org: [{ required: true, message: '请选择配分层级', trigger: 'submit' }],
@@ -360,6 +409,29 @@ async function loadAllPersons() {
360 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 435
 function handleBrigadeChange() {
364 436
 }
365 437
 
@@ -456,18 +528,22 @@ function resetQuery() {
456 528
     groupName: '', 
457 529
     dimensionId: null, 
458 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 538
   queryRef.value?.resetFields()
463 539
   handleQuery()
464 540
 }
465 541
 function handleSelectionChange(sel) { ids.value = sel.map(s => s.id); single.value = sel.length !== 1; multiple.value = !sel.length }
466 542
 
467 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 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 549
 function handleAdd() { resetForm(); dialogTitle.value = '新增配分事项'; dialogVisible.value = true }
@@ -619,5 +695,5 @@ watch(() => route.query, (query) => {
619 695
   isInit = false
620 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 699
 </script>