Kaynağa Gözat

团队管理

yanghao 1 ay önce
ebeveyn
işleme
be810b044a
1 değiştirilmiş dosya ile 141 ekleme ve 173 silme
  1. 141 173
      src/views/expert/list/index.vue

+ 141 - 173
src/views/expert/list/index.vue

@@ -100,12 +100,12 @@
                 <template v-if="cell.type === 'members'">
                   <button
                     v-for="member in cell.members"
-                    :key="member"
+                    :key="member.id"
                     type="button"
                     class="member-link"
                     @click="handleMemberClick(member)"
                   >
-                    {{ member }}
+                    {{ member.expertName }}
                   </button>
                 </template>
 
@@ -536,7 +536,7 @@ const closeDialog = () => {
 type CellItem =
   | {
       type: 'members'
-      members: string[]
+      members: ExpertItem[]
     }
   | {
       type: 'text'
@@ -563,6 +563,7 @@ type ExpertRecord = {
 }
 
 type ExpertProfile = {
+  id: number
   name: string
   role: string
   team: string
@@ -575,89 +576,31 @@ type ExpertProfile = {
   records: ExpertRecord[]
 }
 
-const tableData: TableRow[] = [
-  {
-    teamName: '第一梯队',
-    education: '博士',
-    searchText: '朱桂林 软件工程 50项成果',
-    tableWidth: '64%',
-    backgroundColor: '#eef6ff',
-    columnWidths: [1.5, 1.5, 1.5, 1.5, 1.5],
-    cells: [
-      {
-        type: 'members',
-        members: [
-          '朱桂林',
-          '孙乐臻',
-          '王军',
-          '符云',
-          '王伟',
-          '王伟',
-          '李四',
-
-          '孙乐臻',
-          '王军',
-          '符云',
-          '王伟',
-          '王伟',
-          '李四'
-        ]
-      },
-      { type: 'text', text: '' },
-      { type: 'text', text: '' },
-      { type: 'text', text: '', tone: 'danger' },
-      { type: 'text', text: '' }
-    ]
-  },
-  {
-    teamName: '第二梯队',
-    education: '硕士',
-    searchText: '孙乐臻 金融风险 30项业绩',
-    tableWidth: '75%',
-    backgroundColor: '#f2faef',
-    columnWidths: [1.8, 1.8, 1.8, 1.8, 1.8],
-    cells: [
-      { type: 'members', members: ['朱桂林', '孙乐臻', '王军', '符云', '王伟', '王伟', '李四'] },
-      { type: 'text', text: '' },
-      { type: 'text', text: '' },
-      { type: 'text', text: '' },
-      { type: 'text', text: '' }
-    ]
-  },
-  {
-    teamName: '第三梯队',
-    education: '本科',
-    searchText: '王军 互联网产品 20项成果',
-    tableWidth: '100%',
-    backgroundColor: '#fff8e8',
-    columnWidths: [2, 2, 2, 2, 2],
-    cells: [
-      {
-        type: 'members',
-        members: [
-          '朱桂林',
-          '孙乐臻',
-          '王军',
-          '符云',
-          '王伟',
-          '王伟',
-          '李四',
-          '朱桂林',
-          '孙乐臻',
-          '王军',
-          '符云',
-          '王伟',
-          '王伟',
-          '李四'
-        ]
-      },
-      { type: 'text', text: '' },
-      { type: 'text', text: '' },
-      { type: 'text', text: '' },
-      { type: 'text', text: '' }
-    ]
-  }
-]
+type ExpertItem = {
+  id: number
+  deptId: number
+  expertName: string
+  echelon: string
+  expertRank: number[]
+  professionalTitle: string
+  jobTitle: string
+  formerPost: string
+  teamIds: number[]
+  avatar: string
+  birthday: number | null
+  school: string
+  education: string
+  specialDirection: number[]
+  achievement: string
+  softwareSkills: number[]
+}
+
+type ExpertGroup = {
+  echelonLabel: string
+  num: number
+  sort: number
+  experts: ExpertItem[]
+}
 
 const selectedMember = ref('')
 const searchQuery = ref('')
@@ -666,77 +609,6 @@ const selectedTeam = ref('')
 const drawerVisible = ref(false)
 const activeExpert = ref<ExpertProfile | null>(null)
 
-const expertProfileMap: Record<string, ExpertProfile> = {
-  朱桂林: {
-    name: '朱桂林',
-    role: '技术专家',
-    team: '第一梯队',
-    age: 52,
-    education: '博士',
-    direction: ['材料工程', '可靠性验证', '失效分析'],
-    achievement:
-      '高温井下材料可靠性验证、材料失效分析专家评审;参与 6 个科研项目评审,2 个项目验收专家组。',
-    tasks: ['立项预审', '中期检查', '结题验收', '成果评审', '专项咨询'],
-    tags: ['技术专家', '第一梯队', '启用'],
-    records: [
-      {
-        date: '2026-08-01',
-        event: '中期检查评审',
-        project: '智能检测算法研发',
-        status: '已完成',
-        statusType: 'success'
-      },
-      {
-        date: '2026-07-18',
-        event: '立项预审',
-        project: '平台数据治理标准',
-        status: '已反馈意见',
-        statusType: 'primary'
-      }
-    ]
-  },
-  孙乐臻: {
-    name: '孙乐臻',
-    role: '经营专家',
-    team: '第二梯队',
-    age: 46,
-    education: '硕士',
-    direction: ['经营管理', '投资评估', '风险控制'],
-    achievement: '参与多个经营分析专项评审,长期负责重点项目投资效益与风险识别。',
-    tasks: ['立项预审', '预算审查', '经营诊断', '专项咨询'],
-    tags: ['经营专家', '第二梯队', '启用'],
-    records: [
-      {
-        date: '2026-08-05',
-        event: '预算审查',
-        project: '区域经营提升计划',
-        status: '已完成',
-        statusType: 'success'
-      }
-    ]
-  },
-  王军: {
-    name: '王军',
-    role: '技术专家',
-    team: '第三梯队',
-    age: 41,
-    education: '本科',
-    direction: ['互联网产品', '数字化平台', '需求架构'],
-    achievement: '参与多个数字平台项目方案论证和成果评审,具备丰富的产品落地经验。',
-    tasks: ['方案评审', '中期检查', '成果评审'],
-    tags: ['技术专家', '第三梯队', '启用'],
-    records: [
-      {
-        date: '2026-07-26',
-        event: '成果评审',
-        project: '协同应用平台升级',
-        status: '已完成',
-        statusType: 'success'
-      }
-    ]
-  }
-}
-
 const wordCloudWords = [
   { text: '地质', size: 24, rotate: -8, color: '#7fcd72', left: '8%', top: '52%' },
   { text: '油藏', size: 30, rotate: 6, color: '#8ea4d8', left: '26%', top: '38%' },
@@ -760,6 +632,68 @@ const teamQueryParams = reactive({
   teamName: undefined
 })
 
+const rowStylePresets = [
+  { tableWidth: '64%', backgroundColor: '#eef6ff', columnWidths: [1.5, 1.5, 1.5, 1.5, 1.5] },
+  { tableWidth: '75%', backgroundColor: '#f2faef', columnWidths: [1.8, 1.8, 1.8, 1.8, 1.8] },
+  { tableWidth: '100%', backgroundColor: '#fff8e8', columnWidths: [2, 2, 2, 2, 2] }
+]
+
+const getDictLabel = (type: string, value: string | number | undefined | null) => {
+  if (value === undefined || value === null || value === '') return ''
+  return getStrDictOptions(type).find((item) => String(item.value) === String(value))?.label || ''
+}
+
+const getDictLabels = (type: string, values: Array<string | number> | undefined) => {
+  if (!values?.length) return []
+  return values.map((value) => getDictLabel(type, value)).filter(Boolean)
+}
+
+const teamNameMap = computed(() => {
+  const map = new Map<number, string>()
+  ;(teamList.value || []).forEach((team) => {
+    map.set(team.id, team.teamName)
+  })
+  ;(selectedTeams.value || []).forEach((team) => {
+    map.set(team.id, team.teamName)
+  })
+  return map
+})
+
+const getTeamNames = (teamIds: number[] = []) =>
+  teamIds.map((id) => teamNameMap.value.get(id)).filter(Boolean) as string[]
+
+const getExpertAge = (birthday: number | null) => {
+  if (!birthday) return 0
+  const today = new Date()
+  const birth = new Date(birthday)
+  let age = today.getFullYear() - birth.getFullYear()
+  const monthDiff = today.getMonth() - birth.getMonth()
+  if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
+    age--
+  }
+  return Math.max(age, 0)
+}
+
+const buildExpertProfile = (expert: ExpertItem, groupLabel?: string): ExpertProfile => {
+  const rankLabels = getDictLabels(DICT_TYPE.RD_EXPERT_RANK, expert.expertRank)
+  const directionLabels = getDictLabels(DICT_TYPE.RD_EXPERT_TECHNICAL, expert.specialDirection)
+  const softwareSkillLabels = getDictLabels(DICT_TYPE.RD_EXPERT_SOFTWARE, expert.softwareSkills)
+  const teamNames = getTeamNames(expert.teamIds)
+  return {
+    id: expert.id,
+    name: expert.expertName,
+    role: rankLabels.join('、') || '专家',
+    team: groupLabel || getDictLabel(DICT_TYPE.RD_EXPERT_ECHELON, expert.echelon) || '未分组',
+    age: getExpertAge(expert.birthday),
+    education: getDictLabel(DICT_TYPE.RD_EXPERT_EDUCATION, expert.education) || '-',
+    direction: directionLabels.length ? directionLabels : ['暂无'],
+    achievement: expert.achievement || '暂无',
+    tasks: softwareSkillLabels.length ? softwareSkillLabels : [expert.jobTitle || '暂无'],
+    tags: [groupLabel, ...teamNames, expert.professionalTitle].filter(Boolean),
+    records: []
+  }
+}
+
 const getColumnPercent = (row: TableRow, index: number) => {
   if (!row.columnWidths || row.columnWidths.length === 0) {
     return '0'
@@ -772,8 +706,50 @@ const getColumnPercent = (row: TableRow, index: number) => {
   return ((value / total) * 100).toFixed(4)
 }
 
+const tableData = computed<TableRow[]>(() => {
+  return (list.value as ExpertGroup[]).map((group, index) => {
+    const preset = rowStylePresets[index] || rowStylePresets[rowStylePresets.length - 1]
+    const educationLabels = Array.from(
+      new Set(
+        group.experts.map((expert) => getDictLabel(DICT_TYPE.RD_EXPERT_EDUCATION, expert.education))
+      )
+    ).filter(Boolean)
+    const searchText = group.experts
+      .map((expert) =>
+        [
+          expert.expertName,
+          expert.school,
+          expert.jobTitle,
+          expert.formerPost,
+          expert.achievement
+        ].join(' ')
+      )
+      .join(' ')
+      .toLowerCase()
+
+    return {
+      teamName: group.echelonLabel,
+      education: educationLabels.join('、') || '-',
+      searchText,
+      tableWidth: preset.tableWidth,
+      backgroundColor: preset.backgroundColor,
+      columnWidths: preset.columnWidths,
+      cells: [
+        {
+          type: 'members',
+          members: group.experts
+        },
+        { type: 'text', text: '' },
+        { type: 'text', text: '' },
+        { type: 'text', text: '', tone: 'danger' },
+        { type: 'text', text: '' }
+      ]
+    }
+  })
+})
+
 const filteredTableData = computed(() => {
-  return tableData.filter((row) => {
+  return tableData.value.filter((row) => {
     const query = searchQuery.value.trim().toLowerCase()
     const nameMatch = row.searchText.toLowerCase().includes(query)
     const teamMatch = row.teamName.toLowerCase().includes(query)
@@ -792,20 +768,12 @@ const filteredTableData = computed(() => {
   })
 })
 
-const handleMemberClick = (member: string) => {
-  selectedMember.value = member
-  activeExpert.value = expertProfileMap[member] || {
-    name: member,
-    role: '专家',
-    team: '待分配梯队',
-    age: 45,
-    education: '研究生',
-    direction: ['领域研究', '项目评审'],
-    achievement: '已录入专家基础信息,详细履历可继续补充。',
-    tasks: ['立项预审', '专项咨询'],
-    tags: ['专家', '待补充'],
-    records: []
-  }
+const handleMemberClick = (member: ExpertItem) => {
+  selectedMember.value = member.expertName
+  const group = (list.value as ExpertGroup[]).find((item) =>
+    item.experts.some((expert) => expert.id === member.id)
+  )
+  activeExpert.value = buildExpertProfile(member, group?.echelonLabel)
   drawerVisible.value = true
 }
 
@@ -908,7 +876,7 @@ const getList = async () => {
   try {
     queryParams.expertName = searchQuery.value?.trim() || undefined
     const data = await getExpertList(queryParams)
-    list.value = data.list
+    list.value = data
   } finally {
     loading.value = false
   }