Przeglądaj źródła

Merge branch 'personnelPerformance' into dev

huoyi 1 miesiąc temu
rodzic
commit
abcd046e0d

+ 8 - 0
src/api/system/classificationAssessIndicator.js

@@ -46,3 +46,11 @@ export function queryAssessCategoryTree(query) {
46 46
         params: query
47 47
     })
48 48
 }
49
+//获取分类树及关联指标(支持模糊查询)
50
+export function queryAssessCategoryTreeAndIndicator(data) {
51
+    return request({
52
+        url: '/system/performanceIndicatorCategory/getCategoryTreeWithIndicatorList',
53
+        method: 'post',
54
+        data
55
+    })
56
+}

+ 178 - 24
src/views/performanceManage/monthlyAssess/index.vue

@@ -47,7 +47,7 @@
47 47
     <div v-if="currentTab === 'non-cadre'">
48 48
       <el-table v-loading="loading" :data="nonCadreList" border fit highlight-current-row
49 49
         style="width: 100%; margin-top: 20px;">
50
-        <el-table-column label="大队" prop="brigade" align="center" min-width="100" />
50
+        <el-table-column label="大队" prop="brigadeName" align="center" min-width="100" />
51 51
         <el-table-column label="用工形式" prop="employmentType" align="center" min-width="100">
52 52
           <template #default="scope">
53 53
             <dict-tag :options="employment_type" :value="scope.row.employmentType" />
@@ -188,7 +188,7 @@
188 188
           <el-col :span="8">
189 189
             <el-form-item label="考核月份" prop="assessmentMonth" label-width="100px">
190 190
               <el-date-picker v-model="nonCadreForm.assessmentMonth" type="month" placeholder="请选择考核月份"
191
-                value-format="YYYY-MM" style="width: 100%" />
191
+                value-format="YYYY-MM" style="width: 100%" @change="handleAssessmentMonthChange" />
192 192
             </el-form-item>
193 193
           </el-col>
194 194
           <el-col :span="8">
@@ -259,15 +259,14 @@
259 259
         </el-row>
260 260
 
261 261
         <!-- 考核指标区域 -->
262
-        <div class="section-title blue" style="cursor: pointer;" @click="addIndicator">考核指标</div>
262
+        <div class="section-title blue" style="cursor: pointer;" @click="addIndicator">考核指标+</div>
263 263
         <div class="indicators-box" v-if="nonCadreForm.indicatorGroups.length > 0">
264 264
           <div v-for="(group, groupIndex) in nonCadreForm.indicatorGroups" :key="groupIndex" class="indicator-group">
265 265
             <div class="indicator-group-title">{{ group.title }}</div>
266 266
             <div v-for="(item, itemIndex) in group.items" :key="itemIndex" class="indicator-item">
267 267
               <div class="indicator-name">{{ item.indicatorName }}</div>
268
-              <div class="indicator-value">{{ item.score }}/次</div>
268
+              <div class="indicator-value" v-if="item.categoryNameOne != '红线指标'">{{ item.score }}/次</div>
269 269
               <div class="indicator-count">{{ item.occurCount }}次</div>
270
-
271 270
               <div class="indicator-total">{{ item.scoreResult }}</div>
272 271
               <div class="indicator-actions">
273 272
                 <el-button type="primary" link icon="Edit" @click="editIndicator(groupIndex, itemIndex)"></el-button>
@@ -290,7 +289,7 @@
290 289
               <el-form-item label="红线指标依据">
291 290
                 <span class="detail-link"
292 291
                   @click="showDetailModal('红线指标依据', formatAccordList(nonCadreForm.redLineIndexAccordList))">
293
-                  {{ formatAccordList(nonCadreForm.redLineIndexAccordList) || '查看详情' }}
292
+                  查看详情
294 293
                 </span>
295 294
               </el-form-item>
296 295
             </el-col>
@@ -306,7 +305,7 @@
306 305
               <el-form-item label="核心指标依据">
307 306
                 <span class="detail-link"
308 307
                   @click="showDetailModal('核心指标依据', formatAccordList(nonCadreForm.coreIndexAccordList))">
309
-                  {{ formatAccordList(nonCadreForm.coreIndexAccordList) || '查看详情' }}
308
+                  查看详情
310 309
                 </span>
311 310
               </el-form-item>
312 311
             </el-col>
@@ -322,7 +321,7 @@
322 321
               <el-form-item label="其他指标中的安全指标(仅含SOC/站品控检查扣分)依据">
323 322
                 <span class="detail-link"
324 323
                   @click="showDetailModal('其他指标中的安全指标依据', formatAccordList(nonCadreForm.otherIndexSafetyScoreWithSocStationQcAccordList))">
325
-                  {{ formatAccordList(nonCadreForm.otherIndexSafetyScoreWithSocStationQcAccordList) || '查看详情' }}
324
+                  查看详情
326 325
                 </span>
327 326
               </el-form-item>
328 327
             </el-col>
@@ -338,7 +337,7 @@
338 337
               <el-form-item label="其他指标中的非安全指标依据">
339 338
                 <span class="detail-link"
340 339
                   @click="showDetailModal('其他指标中的非安全指标依据', formatAccordList(nonCadreForm.otherIndexNonSafetyAccordList))">
341
-                  {{ formatAccordList(nonCadreForm.otherIndexNonSafetyAccordList) || '查看详情' }}
340
+                  查看详情
342 341
                 </span>
343 342
               </el-form-item>
344 343
             </el-col>
@@ -354,7 +353,7 @@
354 353
               <el-form-item label="SOC/站品控检查的涉及核心、安全指标扣分依据">
355 354
                 <span class="detail-link"
356 355
                   @click="showDetailModal('SOC/站品控检查的涉及核心、安全指标扣分依据', formatAccordList(nonCadreForm.socStationQcInvolvedCoreSafetyAccordList))">
357
-                  {{ formatAccordList(nonCadreForm.socStationQcInvolvedCoreSafetyAccordList) || '查看详情' }}
356
+                  查看详情
358 357
                 </span>
359 358
               </el-form-item>
360 359
             </el-col>
@@ -391,7 +390,7 @@
391 390
               <el-form-item label="奖励明细">
392 391
                 <span class="detail-link"
393 392
                   @click="showDetailModal('奖励明细', formatAccordList(nonCadreForm.rewardAccordList))">
394
-                  {{ formatAccordList(nonCadreForm.rewardAccordList) || '查看详情' }}
393
+                  查看详情
395 394
                 </span>
396 395
               </el-form-item>
397 396
             </el-col>
@@ -399,7 +398,7 @@
399 398
               <el-form-item label="惩罚明细">
400 399
                 <span class="detail-link"
401 400
                   @click="showDetailModal('惩罚明细', formatAccordList(nonCadreForm.punishmentAccordList))">
402
-                  {{ formatAccordList(nonCadreForm.punishmentAccordList) || '查看详情' }}
401
+                  查看详情
403 402
                 </span>
404 403
               </el-form-item>
405 404
             </el-col>
@@ -484,16 +483,14 @@
484 483
       <el-form label-width="150px" class="indicator-form" :model="indicatorDialog.form" :rules="indicatorDialog.rules">
485 484
         <el-form-item label="指标名称" prop="indicatorId" required>
486 485
 
487
-          <el-select v-model="indicatorDialog.form.indicatorId" placeholder="搜索指标名称" filterable remote reserve-keyword
488
-            :remote-method="searchIndicators" :loading="indicatorDialog.loading" style="flex: 1;"
489
-            @change="onIndicatorNameChange">
490
-            <el-option v-for="item in indicatorDialog.indicatorOptions" :key="item.id" :label="item.name"
491
-              :value="item.id" />
492
-          </el-select>
486
+          <el-cascader v-model="indicatorDialog.form.indicatorId" placeholder="搜索指标名称" filterable clearable
487
+            :options="indicatorDialog.cascaderOptions" :props="indicatorDialog.cascaderProps" style="flex: 1;"
488
+            @change="onIndicatorCascaderChange">
489
+          </el-cascader>
493 490
 
494 491
         </el-form-item>
495 492
 
496
-        <el-form-item label="分值/单位">
493
+        <el-form-item label="分值/单位" v-if="indicatorDialog.form.categoryNameOne != '红线指标'">
497 494
           <div style="display: flex; align-items: center; gap: 10px;">
498 495
             <span style="font-size: 24px; font-weight: bold;">{{ indicatorDialog.form.score }}/次</span>
499 496
 
@@ -569,13 +566,14 @@
569 566
 </template>
570 567
 
571 568
 <script setup>
572
-import { ref, reactive, onMounted, getCurrentInstance, watch } from 'vue'
569
+import { ref, reactive, onMounted, getCurrentInstance, watch, nextTick } from 'vue'
573 570
 import { ElMessage, ElMessageBox } from 'element-plus'
574 571
 
575 572
 // API导入(需要根据实际API路径调整)
576 573
 import { listCadreAssessment, generateCadreAssessment, listNonCadreAssessment, addNonCadreAssessment, updateNonCadreAssessment, deleteNonCadreAssessment, exportNonCadreAssessment, generateNonCadreAssessment, getNonCadreAssessment } from '@/api/performance/monthlyAssess.js'
577 574
 import { selectUserLeaderListByCondition, listUserPerformance } from '@/api/system/user.js'
578 575
 import { listIndicator } from '@/api/system/classificationAssess.js'
576
+import { queryAssessCategoryTreeAndIndicator } from '@/api/system/classificationAssessIndicator.js'
579 577
 
580 578
 const { proxy } = getCurrentInstance()
581 579
 const { post, work_area, employment_type, assessment_team, base_performance_indicator_qc_dept_type } = proxy.useDict('post', 'work_area', 'employment_type', 'assessment_team', 'base_performance_indicator_qc_dept_type')
@@ -712,6 +710,14 @@ const indicatorDialog = reactive({
712 710
   itemIndex: null,
713 711
   loading: false,
714 712
   indicatorOptions: [],
713
+  cascaderOptions: [],
714
+  cascaderProps: {
715
+    value: 'id',
716
+    label: 'name',
717
+    children: 'indicatorList',
718
+    checkStrictly: false,
719
+    emitPath: false
720
+  },
715 721
   rules: {
716 722
     indicatorId: [
717 723
       { required: true, message: '请选择指标名称', trigger: 'change' }
@@ -752,6 +758,82 @@ async function searchIndicators(query) {
752 758
   }
753 759
 }
754 760
 
761
+async function loadIndicatorCascaderOptions() {
762
+  try {
763
+    const res = await queryAssessCategoryTreeAndIndicator({})
764
+    const treeData = res.data || []
765
+    indicatorDialog.cascaderOptions = transformIndicatorTree(treeData)
766
+  } catch (error) {
767
+    console.error('加载指标树失败:', error)
768
+    indicatorDialog.cascaderOptions = []
769
+  }
770
+}
771
+
772
+function transformIndicatorTree(treeData) {
773
+  return treeData.map(node => {
774
+    const transformed = {
775
+      id: node.id,
776
+      name: node.name,
777
+      indicatorList: []
778
+    }
779
+
780
+    if (node.children && node.children.length > 0) {
781
+      transformed.indicatorList = node.children.map(child => {
782
+        const transformedChild = {
783
+          id: child.id,
784
+          name: child.name,
785
+          indicatorList: []
786
+        }
787
+
788
+        if (child.indicatorList && child.indicatorList.length > 0) {
789
+          transformedChild.indicatorList = child.indicatorList.map(indicator => ({
790
+            ...indicator, name: indicator.name, id: indicator.code, indicatorId: indicator.id, indicatorName: indicator.name, occurCount: 1, personnelMonthlyAssessmentIndicatorRewardPunishmentDetailList: []
791
+          }))
792
+        }
793
+
794
+        return transformedChild
795
+      })
796
+    }
797
+
798
+    if (node.indicatorList && node.indicatorList.length > 0) {
799
+      transformed.indicatorList = node.indicatorList.map(indicator => ({
800
+        ...indicator, name: indicator.name, id: indicator.code, indicatorId: indicator.id, indicatorName: indicator.name, occurCount: 1, personnelMonthlyAssessmentIndicatorRewardPunishmentDetailList: []
801
+      }))
802
+    }
803
+
804
+    return transformed
805
+  })
806
+}
807
+
808
+function onIndicatorCascaderChange(value) {
809
+  const selected = findIndicatorById(indicatorDialog.cascaderOptions, value)
810
+  if (selected) {
811
+    indicatorDialog.form = {
812
+      ...selected,
813
+      name: selected.name,
814
+      id: selected.code,
815
+      indicatorId: selected.id,
816
+      indicatorName: selected.name,
817
+      occurCount: 1,
818
+      personnelMonthlyAssessmentIndicatorRewardPunishmentDetailList: []
819
+    }
820
+    updateTotal()
821
+  }
822
+}
823
+
824
+function findIndicatorById(tree, targetId) {
825
+  for (const node of tree) {
826
+    if (node.id === targetId) {
827
+      return node
828
+    }
829
+    if (node.indicatorList && node.indicatorList.length > 0) {
830
+      const found = findIndicatorById(node.indicatorList, targetId)
831
+      if (found) return found
832
+    }
833
+  }
834
+  return null
835
+}
836
+
755 837
 
756 838
 
757 839
 // 弹窗配置
@@ -850,6 +932,7 @@ async function handleUserChange(userId) {
850 932
     nonCadreForm.deputySupervisorName = ''
851 933
     nonCadreForm.deputyManagerId = ''
852 934
     nonCadreForm.deputyManagerName = ''
935
+    checkAndLoadExistingAssessment()
853 936
     return
854 937
   }
855 938
 
@@ -877,6 +960,72 @@ async function handleUserChange(userId) {
877 960
     nonCadreForm.deputyManagerId = ''
878 961
     nonCadreForm.deputyManagerName = ''
879 962
   }
963
+
964
+  checkAndLoadExistingAssessment()
965
+}
966
+
967
+async function handleAssessmentMonthChange() {
968
+  checkAndLoadExistingAssessment()
969
+}
970
+
971
+async function checkAndLoadExistingAssessment() {
972
+  if (!nonCadreForm.userId || !nonCadreForm.assessmentMonth) {
973
+    return
974
+  }
975
+
976
+  try {
977
+    const assessmentMonth = nonCadreForm.assessmentMonth.replace('-', '')
978
+    const res = await listNonCadreAssessment({
979
+      userId: nonCadreForm.userId,
980
+      assessmentMonth: assessmentMonth
981
+    })
982
+
983
+    const rows = res.rows || []
984
+    if (rows.length > 0) {
985
+      const assessmentId = rows[0].id
986
+      await loadAssessmentDetail(assessmentId)
987
+    }
988
+  } catch (error) {
989
+    console.error('检查已有考核数据失败:', error)
990
+  }
991
+}
992
+
993
+async function loadAssessmentDetail(id) {
994
+  try {
995
+    const res = await getNonCadreAssessment(id)
996
+    const detailList = res.data.personnelMonthlyAssessmentIndicatorDetailList || []
997
+    const indicatorGroupsMap = {}
998
+    detailList.forEach(item => {
999
+      const categoryKey = item.categoryCodeOne || item.categoryNameOne || '未分类'
1000
+      if (!indicatorGroupsMap[categoryKey]) {
1001
+        indicatorGroupsMap[categoryKey] = {
1002
+          title: item.categoryNameOne || '未分类',
1003
+          items: []
1004
+        }
1005
+      }
1006
+      indicatorGroupsMap[categoryKey].items.push({
1007
+        ...item
1008
+      })
1009
+    })
1010
+    const indicatorGroups = Object.values(indicatorGroupsMap)
1011
+
1012
+    Object.keys(res.data).forEach(key => {
1013
+      if (key === 'assessmentMonth' && res.data[key]) {
1014
+        const val = res.data[key]
1015
+        if (typeof val === 'string' && val.length === 6 && /^\d+$/.test(val)) {
1016
+          nonCadreForm[key] = val.substring(0, 4) + '-' + val.substring(4, 6)
1017
+        } else {
1018
+          nonCadreForm[key] = val
1019
+        }
1020
+      } else if (key !== 'personnelMonthlyAssessmentIndicatorDetailList') {
1021
+        nonCadreForm[key] = res.data[key]
1022
+      }
1023
+    })
1024
+    nonCadreForm.indicatorGroups = indicatorGroups
1025
+  } catch (error) {
1026
+    console.error('获取考核详情失败:', error)
1027
+    ElMessage.error('获取考核详情失败')
1028
+  }
880 1029
 }
881 1030
 
882 1031
 // 获取数据列表
@@ -1031,7 +1180,7 @@ const submitForm = async () => {
1031 1180
       if (submitData.assessmentMonth) {
1032 1181
         submitData.assessmentMonth = submitData.assessmentMonth.replace('-', '')
1033 1182
       }
1034
-      
1183
+
1035 1184
       if (submitData.indicatorGroups && submitData.indicatorGroups.length > 0) {
1036 1185
         submitData.personnelMonthlyAssessmentIndicatorDetailList = submitData.indicatorGroups.flatMap(group =>
1037 1186
           group.items.map(item => ({
@@ -1149,16 +1298,21 @@ const addIndicator = () => {
1149 1298
     amount: 0,
1150 1299
     personnelMonthlyAssessmentIndicatorRewardPunishmentDetailList: []
1151 1300
   }
1301
+  loadIndicatorCascaderOptions()
1152 1302
 }
1153 1303
 
1154 1304
 // 编辑指标 - 打开编辑模态框
1155
-const editIndicator = (groupIndex, itemIndex) => {
1305
+const editIndicator = async (groupIndex, itemIndex) => {
1156 1306
   const item = nonCadreForm.indicatorGroups[groupIndex].items[itemIndex]
1157 1307
   indicatorDialog.visible = true
1158 1308
   indicatorDialog.title = '编辑扣分指标'
1159 1309
   indicatorDialog.mode = 'edit'
1160 1310
   indicatorDialog.groupIndex = groupIndex
1161 1311
   indicatorDialog.itemIndex = itemIndex
1312
+
1313
+  await loadIndicatorCascaderOptions()
1314
+  await nextTick()
1315
+
1162 1316
   indicatorDialog.form = { ...item }
1163 1317
   if (!indicatorDialog.form.personnelMonthlyAssessmentIndicatorRewardPunishmentDetailList) {
1164 1318
     indicatorDialog.form.personnelMonthlyAssessmentIndicatorRewardPunishmentDetailList = []
@@ -1222,9 +1376,9 @@ const saveIndicator = () => {
1222 1376
     return
1223 1377
   }
1224 1378
 
1225
-  const rewardPunishmentType = indicatorDialog.form.score > 0 ? '1' : '0'
1379
+
1226 1380
   list.forEach(detail => {
1227
-    detail.rewardPunishmentType = rewardPunishmentType
1381
+    detail.rewardPunishmentType = indicatorDialog.form.rewardPunishmentType
1228 1382
   })
1229 1383
 
1230 1384
   if (indicatorDialog.mode === 'add') {