Explorar o código

页面管理更新

Xiao_123 hai 5 meses
pai
achega
b757a3fe52

+ 0 - 2
.env.local

@@ -5,14 +5,12 @@ VITE_DEV=true
 
 # 请求路径
 VITE_BASE_URL='https://www.menduner.com:1443/' # 生产环境
-# VITE_BASE_URL='http://192.168.3.80' # 测试环境
 
 # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
 VITE_UPLOAD_TYPE=server
 
 # 上传路径
 VITE_UPLOAD_URL='https://www.menduner.com:1443/admin-api/infra/file/upload' # 生产环境
-# VITE_UPLOAD_URL='http://192.168.3.80/admin-api/infra/file/upload' # 测试环境
 
 # 接口地址
 VITE_API_URL=/admin-api

+ 3 - 0
.env.localDev

@@ -14,6 +14,9 @@ VITE_UPLOAD_TYPE=server
 # VITE_UPLOAD_URL='https://www.menduner.com:1443/admin-api/infra/file/upload' # 生产环境
 VITE_UPLOAD_URL='http://192.168.3.80/admin-api/infra/file/upload' # 测试环境
 
+# 预览路径
+VITE_PREVIEW_URL = 'http://192.168.3.91:8012'
+
 # 接口地址
 VITE_API_URL=/admin-api
 

+ 1 - 1
src/api/menduner/common/index.ts

@@ -8,6 +8,6 @@ export const commonApi = {
   },
   // 附件简历解析
   resumeParser: async (params: any) => {
-    return await request.get({ url: `/menduner/system/online/resume/parser`, params })
+    return await request.get({ url: `/menduner/system/online/resume/parser2`, params })
   },
 }

+ 5 - 0
src/api/menduner/system/person/index.ts

@@ -93,6 +93,11 @@ export const PersonInfoApi = {
     return await request.get({ url: `/menduner/system/job-interested/page`, params })
   },
 
+  // 培训经历分页
+  getPersonTrainingPage: async (params: any) => {
+    return await request.get({ url: `/menduner/system/train-exp/page`, params })
+  },
+
   // 获得钱包充值记录分页
   getPersonWalletRechargePage: async (params: any) => {
     return await request.get({ url: `/pay/wallet-recharge/page`, params })

+ 5 - 0
src/api/menduner/system/talentMap/index.ts

@@ -5,5 +5,10 @@ export const TalentMap = {
   // 简历解析
   resumeParse: async (params) => {
     return await request.get({ url: `/menduner/system/online/resume/parser`, params })
+  },
+
+  // 获得人才地图-人才基本信息分页
+  getTalentMapInfoPage: async (params) => {
+    return await request.get({ url: `/menduner/system/talent-map-info/page`, params })
   }
 }

+ 6 - 1
src/components/Pagination/index.vue

@@ -10,7 +10,7 @@
     :total="+total"
     :small="isSmall"
     class="float-right mb-15px mt-15px"
-    layout="total, sizes, prev, pager, next, jumper"
+    :layout="layout"
     next-text="下一页"
     prev-text="上一页"
     @size-change="handleSizeChange"
@@ -52,6 +52,11 @@ const props = defineProps({
   pagerCount: {
     type: Number,
     default: document.body.clientWidth < 992 ? 5 : 7
+  },
+  // 控制器
+  layout: {
+    type: String,
+    default: 'total, sizes, prev, pager, next, jumper'
   }
 })
 

+ 0 - 30
src/views/menduner/system/talentMap/details/components/collect.vue

@@ -1,30 +0,0 @@
-<template>
-  <div>
-    <el-descriptions class="margin-top" :column="1" border>
-      <el-descriptions-item label="职位收藏数量">{{ info.position || 0 }}</el-descriptions-item>
-      <el-descriptions-item label="企业收藏数量">{{ info.enterprise || 0 }}</el-descriptions-item>
-    </el-descriptions>
-  </div>
-</template>
-
-<script setup>
-/* 职位、企业收藏数量 */
-defineOptions({ name: 'PersonAccount'})
-import { PersonInfoApi } from '@/api/menduner/system/person'
-import { formatDate } from '@/utils/formatTime'
-import { DICT_TYPE } from '@/utils/dict'
-
-const props = defineProps({
-  userId: String
-})
-
-const info = ref({
-  position: 0,
-  enterprise: 0
-})
-const getInfo = async () => {
-  info.value.position = await PersonInfoApi.getPersonJobFavoriteCount(props.userId)
-  info.value.enterprise = await PersonInfoApi.getPersonEnterpriseSubscribeCount(props.userId)
-}
-getInfo()
-</script>

+ 0 - 134
src/views/menduner/system/talentMap/details/components/deliveryJob.vue

@@ -1,134 +0,0 @@
-<template>
-  <el-table v-loading="loading" :data="tableData" :stripe="true" class="m-t-20px">
-    <el-table-column label="职位名称" align="center" prop="job.name" />
-    <el-table-column label="发布企业" align="center" prop="enterprise.anotherName">
-      <template #default="scope">{{ scope.row.enterprise.anotherName || scope.row.enterprise.name}}</template>
-    </el-table-column>
-    <el-table-column label="地区" align="center" prop="job.areaName">
-      <template #default="scope">{{ !scope.row.job.areaId ? '全国' : scope.row.job.areaName }}</template>
-    </el-table-column>
-    <el-table-column label="薪资" align="center" prop="job.payFrom">
-      <template #default="scope">
-        <span v-if="scope.row.job.payFrom && scope.row.job.payTo">
-          {{ scope.row.job.payFrom }} - {{ scope.row.job.payTo }}/{{ payUnit.find(e => e.value === Number(scope.row.job.payUnit))?.label }}
-        </span>
-        <span v-else>面议</span>
-      </template>
-    </el-table-column>
-    <el-table-column label="投递附件名称" align="center" prop="cvRel.title" />
-    <el-table-column label="投递时间" align="center" prop="cvRel.createTime" :formatter="dateFormatter" width="180px" />
-    <el-table-column label="投递状态" align="center">
-      <template #default="scope">
-        {{ scope.row.cvRel.status === '0' ? '未查看' : '已查看'}}
-      </template>
-    </el-table-column>
-    <el-table-column label="操作" align="center">
-      <template #default="scope">
-        <el-button link type="primary" @click="openDetail(scope.row)" class="m-r-10px">详情</el-button>
-        <el-link type="primary" download :href="scope.row.cvRel.url" :underline="false" target="_blank">附件下载</el-link>
-      </template>
-    </el-table-column>
-  </el-table>
-
-  <Pagination
-    :total="total"
-    v-model:page="queryParams.pageNo"
-    v-model:limit="queryParams.pageSize"
-    @pagination="getList"
-  />
-
-  <Dialog title="职位详情" v-model="dialogVisible">
-    <el-descriptions :column="1" border>
-      <el-descriptions-item label="职位名称">{{ itemData.name }}</el-descriptions-item>
-      <el-descriptions-item label="发布企业">{{ itemData.enterpriseName }}</el-descriptions-item>
-      <el-descriptions-item label="职位类型">{{ itemData.positionName }}</el-descriptions-item>
-      <el-descriptions-item label="工作城市">{{ !itemData.areaId ? '全国' : itemData.areaName }}</el-descriptions-item>
-      <el-descriptions-item label="详细地址">{{ itemData.address }}</el-descriptions-item>
-      <el-descriptions-item label="薪资">
-        <span v-if="itemData.payFrom && itemData.payTo">
-          {{ itemData.payFrom }} - {{ itemData.payTo }}/{{ payUnit.find(e => e.value === Number(itemData.payUnit))?.label }}
-        </span>
-        <span v-else>面议</span>
-      </el-descriptions-item>
-      <el-descriptions-item label="投递附件名称">
-        <el-link type="primary" download :href="itemData.title" :underline="false" target="_blank">{{ itemData.title }}</el-link>
-      </el-descriptions-item>
-      <el-descriptions-item label="投递时间">
-        {{ itemData.time ? timesTampChange(itemData.time, 'Y-M-D h:m:s') : '' }}
-      </el-descriptions-item>
-      <el-descriptions-item label="投递状态">
-        {{ itemData.cvStatus === '0' ? '未查看' : '已查看' }}
-      </el-descriptions-item>
-      <el-descriptions-item label="学历要求">
-        <dict-tag :type="DICT_TYPE.MENDUNER_EDUCATION_TYPE" :value="itemData.eduType" />
-      </el-descriptions-item>
-      <el-descriptions-item label="工作经验">
-        <dict-tag :type="DICT_TYPE.MENDUNER_EXP_TYPE" :value="itemData.expType" />
-      </el-descriptions-item>
-      <el-descriptions-item label="刷新时间">{{ timesTampChange(itemData.updateTime, 'Y-M-D h:m') }}</el-descriptions-item>
-      <el-descriptions-item label="到期时间">
-        {{ itemData.expireTime ? timesTampChange(itemData.expireTime, 'Y-M-D') : '长期有效' }}
-      </el-descriptions-item>
-      <el-descriptions-item label="职位关键字">
-        <el-tag type="primary" v-for="k in itemData.tagList" :key="k" class="m-r-5px m-b-5px">{{ k }}</el-tag>
-      </el-descriptions-item>
-    </el-descriptions>
-    <template #footer>
-      <el-button @click="dialogVisible = false; itemData = {}">取 消</el-button>
-    </template>
-  </Dialog>
-</template>
-
-<script setup>
-defineOptions({ name: 'PersonEduList'})
-import { PersonInfoApi } from '@/api/menduner/system/person'
-import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
-import { dateFormatter } from '@/utils/formatTime'
-import { timesTampChange } from '@/utils/transform/date'
-import { dealDictObjData } from '@/utils/transform/position'
-
-const props = defineProps({
-  userId: String
-})
-
-const loading = ref(false)
-const tableData = ref([])
-const total = ref(0)
-const payUnit = getIntDictOptions(DICT_TYPE.MENDUNER_PAY_UNIT)
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  userId: props.userId
-})
-
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await PersonInfoApi.getPersonJobDeliveryList(queryParams)
-    tableData.value = data.list.map(e => {
-      e.job = dealDictObjData({}, e.job)
-      return e
-    })
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
-}
-getList()
-
-// 职位详情
-const itemData = ref({})
-const dialogVisible = ref(false)
-const openDetail = (item) => {
-  itemData.value = { 
-    ...item.job, 
-    enterpriseName: item.enterprise.anotherName || item.enterprise.name, 
-    url: item.cvRel.url, 
-    title: item.cvRel.title, 
-    time: item.cvRel.createTime,
-    cvStatus: item.cvRel.status
-  }
-  console.log(itemData.value, '===========')
-  dialogVisible.value = true
-}
-</script>

+ 64 - 17
src/views/menduner/system/talentMap/details/components/edu.vue

@@ -1,17 +1,30 @@
 <template>
   <el-card shadow="never" class="m-t-10px">
     <template #header>
-      <CardTitle title="教育经历" />
+      <div style="display: flex; justify-content: space-between;">
+        <CardTitle title="教育经历" />
+        <el-button link type="primary" @click="handleAdd"><Icon icon="ep:plus" class="mr-5px" />新增</el-button>
+      </div>
     </template>
     <el-table :data="list" :stripe="true">
-      <el-table-column label="学校名称" align="center" prop="eduCollege" />
-      <el-table-column label="专业名称" align="center" prop="eduMajor" />
-      <el-table-column label="在校日期" align="center" prop="startDate">
-        <template #default="{ row }">{{ row.startDate }} - {{ row.endDate }}</template>
+      <el-table-column label="学校名称" align="center" prop="schoolName" />
+      <el-table-column label="专业名称" align="center" prop="major" />
+      <el-table-column label="学历" align="center" prop="educationType">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.MENDUNER_EDUCATION_TYPE" :value="scope.row.educationType" />
+        </template>
+      </el-table-column>
+      <el-table-column label="在校日期" align="center" prop="startTime">
+        <template #default="scope">
+          {{ scope.row.startTime ? timesTampChange(scope.row.startTime, 'Y-M') : '' }}
+          <span v-if="scope.row.startTime && scope.row.endTime">至</span>
+          {{ scope.row.endTime ? timesTampChange(scope.row.endTime, 'Y-M') : '' }}
+        </template>
       </el-table-column>
       <el-table-column label="操作" align="center" prop="actions">
         <template #default="{ row, $index }">
           <el-button link type="primary" @click="handleEdit(row, $index)">编辑</el-button>
+          <el-button link type="danger" @click="handleDelete($index)">删除</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -24,17 +37,23 @@
       :rules="formRules"
       label-width="80px"
     >
-      <el-form-item label="学校名称" prop="eduCollege" required>
-        <el-input v-model="formData.eduCollege" />
+      <el-form-item label="学校名称" prop="schoolName" required>
+        <el-input v-model="formData.schoolName" />
       </el-form-item>
-      <el-form-item label="专业名称" prop="eduMajor" required>
-        <el-input v-model="formData.eduMajor" />
+      <el-form-item label="专业名称" prop="major" required>
+        <el-input v-model="formData.major" />
       </el-form-item>
-      <el-form-item label="开始时间" prop="startDate" required>
-        <el-input v-model="formData.startDate" placeholder="请填写开始时间" />
+      <el-form-item label="学历" prop="educationType" required>
+        <el-select v-model="formData.educationType" placeholder="请选择学历">
+          <el-option
+            v-for="dict in getIntDictOptions(DICT_TYPE.MENDUNER_EDUCATION_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value.toString()" />
+        </el-select>
       </el-form-item>
-      <el-form-item label="结束时间" prop="endDate" required>
-        <el-input v-model="formData.endDate" placeholder="请填写结束时间" />
+      <el-form-item label="开始日期" prop="startTime" required>
+        <el-date-picker v-model="formData.startTime" type="month" :disabledDate="disabledDates" value-format="x" placeholder="选择在校开始日期" />
+      </el-form-item>
+      <el-form-item label="结束日期" prop="endTime" required>
+        <el-date-picker v-model="formData.endTime" type="month" :disabledDate="disabledDates" value-format="x" placeholder="选择在校结束日期" />
       </el-form-item>
     </el-form>
     <template #footer>
@@ -47,33 +66,61 @@
 <script setup>
 defineOptions({ name: 'TalentMapEdu'})
 import { cloneDeep } from 'lodash-es'
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { timesTampChange } from '@/utils/transform/date'
 
 const props = defineProps({
   data: Array
 })
 const list = ref([])
-list.value = cloneDeep(props.data)
+watch(() => props.data, (newVal) => {
+  list.value = cloneDeep(newVal)
+}, { deep: true })
 
+const message = useMessage() // 消息弹窗
 const editIndex = ref(0)
 const formRef = ref()
 const dialogVisible = ref(false)
 const formData = ref({})
+const type = ref('add')
 const formRules = reactive({
-  eduCollege: [{ required: true, message: '学校名称不能为空', trigger: 'blur' }],
-  eduMajor: [{ required: true, message: '专业名称不能为空', trigger: 'blur' }],
+  schoolName: [{ required: true, message: '学校名称不能为空', trigger: 'blur' }],
+  major: [{ required: true, message: '专业名称不能为空', trigger: 'blur' }],
+  educationType: [{ required: true, message: '学历不能为空', trigger: 'blur' }],
   startDate: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
   endDate: [{ required: true, message: '结束时间不能为空', trigger: 'blur' }]
 })
 
+// 未来的时间不可选
+const disabledDates = (date) => {
+  return date.getTime() > Date.now()
+}
+
+// 新增
+const handleAdd = () => {
+  type.value = 'add'
+  dialogVisible.value = true
+}
+
+// 编辑
 const handleEdit = (row, index) => {
+  type.value = 'edit'
   editIndex.value = index
   formData.value = cloneDeep(row)
   dialogVisible.value = true
 }
 
+// 删除
+const handleDelete = async (index) => {
+  try {
+    await message.delConfirm()
+    delete list.value[index]
+  } catch {}
+}
+
 const submitForm = async () => {
   await formRef.value.validate()
-  list.value[editIndex.value] = formData.value
+  type.value === 'add' ? list.value.push(formData.value) : list.value[editIndex.value] = formData.value
   formData.value = {}
   editIndex.value = 0
   dialogVisible.value = false

+ 0 - 20
src/views/menduner/system/talentMap/details/components/evaluation.vue

@@ -1,20 +0,0 @@
-<template>
-  <div>
-    <el-card shadow="never">
-      <template #header>
-        <CardTitle title="个人优势" />
-      </template>
-      <el-input v-model="evaluation" :rows="8" type="textarea" placeholder="请输入您的个人优势" />
-    </el-card>
-  </div>
-</template>
-
-<script setup>
-defineOptions({ name: 'TalentMapEvaluation'})
-
-const props = defineProps({
-  data: Object
-})
-
-const evaluation = computed(() => props.data.contMyDesc)
-</script>

+ 59 - 22
src/views/menduner/system/talentMap/details/components/exp.vue

@@ -1,20 +1,25 @@
 <template>
   <el-card shadow="never" class="m-t-10px">
     <template #header>
-      <CardTitle title="工作经历" />
+      <div style="display: flex; justify-content: space-between;">
+        <CardTitle title="工作经历" />
+        <el-button link type="primary" @click="handleAdd"><Icon icon="ep:plus" class="mr-5px" />新增</el-button>
+      </div>
     </template>
     <el-table :data="list" :stripe="true">
-      <el-table-column label="任职企业" align="center" prop="jobCpy" />
-      <el-table-column label="职位名称" align="center" prop="jobPosition" />
+      <el-table-column label="任职企业" align="center" prop="enterpriseName" />
+      <el-table-column label="职位名称" align="center" prop="positionName" />
       <el-table-column label="在职时间" align="center" prop="startTime">
-        <template #default="{ row }">
-          {{ row.startDate }} - {{ row.endDate }}
+        <template #default="scope">
+          {{ scope.row.startTime ? timesTampChange(scope.row.startTime, 'Y-M') : '' }}至
+          {{ scope.row.endTime ? timesTampChange(scope.row.endTime, 'Y-M') : '至今' }}
         </template>
       </el-table-column>
-      <!-- <el-table-column label="工作内容" align="center" prop="jobContent" :show-overflow-tooltip="true" /> -->
+      <!-- <el-table-column label="工作内容" align="center" prop="content" :show-overflow-tooltip="true" /> -->
       <el-table-column label="操作" align="center" prop="actions">
         <template #default="{ row, $index }">
           <el-button link type="primary" @click="handleEdit(row, $index)">编辑</el-button>
+          <el-button link type="danger" @click="handleDelete($index)">删除</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -27,20 +32,21 @@
       :rules="formRules"
       label-width="80px"
     >
-      <el-form-item label="任职企业" prop="jobCpy" required>
-        <el-input v-model="formData.jobCpy" />
+      <el-form-item label="任职企业" prop="enterpriseName" required>
+        <el-input v-model="formData.enterpriseName" />
       </el-form-item>
-      <el-form-item label="职位名称" prop="jobPosition" required>
-        <el-input v-model="formData.jobPosition" />
+      <el-form-item label="职位名称" prop="positionName" required>
+        <el-input v-model="formData.positionName" />
       </el-form-item>
-      <el-form-item label="开始时间" prop="startDate" required>
-        <el-input v-model="formData.startDate" placeholder="请填写开始时间" />
+      <el-form-item label="开始日期" prop="startTime" required>
+        <el-date-picker v-model="formData.startTime" type="month" :disabledDate="disabledDates"  value-format="x" placeholder="选择在职开始日期" />
       </el-form-item>
-      <el-form-item label="结束时间" prop="endDate" required>
-        <el-input v-model="formData.endDate" placeholder="请填写结束时间" />
+      <el-form-item label="结束日期" prop="endTime">
+        <el-date-picker v-model="formData.endTime" :disabled="sofar" :disabledDate="disabledDates" type="month"  value-format="x" placeholder="选择在职结束日期" />
+        <el-checkbox v-model="sofar" label="至今" size="large" class="m-l-10px" @change="handleSofar" />
       </el-form-item>
-      <el-form-item label="工作内容" prop="jobContent">
-        <el-input v-model="formData.jobContent" :rows="8" type="textarea" />
+      <el-form-item label="工作内容" prop="content">
+        <el-input v-model="formData.content" :rows="8" type="textarea" />
       </el-form-item>
     </el-form>
     <template #footer>
@@ -53,36 +59,67 @@
 <script setup>
 defineOptions({ name: 'TalentMapExp'})
 import { cloneDeep } from 'lodash-es'
+import { timesTampChange } from '@/utils/transform/date'
 
 const props = defineProps({
   data: Array
 })
 const list = ref([])
-list.value = cloneDeep(props.data)
+watch(() => props.data, (newVal) => {
+  list.value = cloneDeep(newVal)
+}, { deep: true })
 
+const message = useMessage() // 消息弹窗
 const editIndex = ref(0)
 const formRef = ref()
 const dialogVisible = ref(false)
 const formData = ref({})
+const type = ref('add')
+const sofar = ref(false)
 const formRules = reactive({
-  jobCpy: [{ required: true, message: '任职企业不能为空', trigger: 'blur' }],
-  jobPosition: [{ required: true, message: '职位名称不能为空', trigger: 'blur' }],
-  startDate: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
-  endDate: [{ required: true, message: '结束时间不能为空', trigger: 'blur' }]
+  enterpriseName: [{ required: true, message: '任职企业不能为空', trigger: 'blur' }],
+  positionName: [{ required: true, message: '职位名称不能为空', trigger: 'blur' }],
+  startTime: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
+  // endTime: [{ required: true, message: '结束时间不能为空', trigger: 'blur' }]
 })
 
+// 未来的时间不可选
+const disabledDates = (date) => {
+  return date.getTime() > Date.now()
+}
+
+const handleAdd = () => {
+  type.value = 'add'
+  dialogVisible.value = true
+}
+
 const handleEdit = (row, index) => {
+  type.value = 'edit'
   editIndex.value = index
   formData.value = cloneDeep(row)
+  sofar.value = !row.endTime
   dialogVisible.value = true
 }
 
+// 删除
+const handleDelete = async (index) => {
+  try {
+    await message.delConfirm()
+    delete list.value[index]
+  } catch {}
+}
+
 const submitForm = async () => {
   await formRef.value.validate()
-  list.value[editIndex.value] = formData.value
+  if (!sofar.value && !formData.value.endTime) return message.warning('请选择结束时间')
+  type.value === 'add' ? list.value.push(formData.value) : list.value[editIndex.value] = formData.value
   formData.value = {}
   editIndex.value = 0
   dialogVisible.value = false
   console.log(list.value, 'submit')
 }
+
+const handleSofar = (e) => {
+  if (e) formData.value.endTime = null
+}
 </script>

+ 113 - 93
src/views/menduner/system/talentMap/details/components/info.vue

@@ -1,108 +1,128 @@
 <template>
-  <div>
-    <el-card shadow="never">
-      <template #header>
-        <CardTitle title="基本信息" />
-      </template>
-      <!-- <div style="margin-bottom: 12px; text-align: end;">
-        <div v-if="isEdit">
-          <el-button @click="isEdit = false">取消</el-button>
-          <el-button type="primary" @click="isEdit = false">保存</el-button>
-        </div>
-        <el-button v-else type="primary" @click="isEdit = true">修改</el-button>
-      </div> -->
-      <el-descriptions class="margin-top" :column="2" border>
-        <el-descriptions-item label="用户头像">
-          <el-image v-if="data.avatarData" class="h-100px w-100px" :src="data.avatarData" fit="contain" hide-on-click-modal :preview-src-list="[data.avatarData]"/>
-        </el-descriptions-item>
-        <el-descriptions-item label="性别">{{ data.genderInf }}</el-descriptions-item>
-        <el-descriptions-item label="用户名">{{ data.name }}</el-descriptions-item>
-        <el-descriptions-item label="联系电话">{{ data.phone }}</el-descriptions-item>
-        <el-descriptions-item label="工作年限">{{ data.workYear }}年</el-descriptions-item>
-        <el-descriptions-item label="出生日期">{{ data.birthday }}</el-descriptions-item>
-        <el-descriptions-item label="婚姻状况">{{ data.marital_status }}</el-descriptions-item>
-        <el-descriptions-item label="学历">{{ data.degree }}</el-descriptions-item>
-        <el-descriptions-item label="邮箱">{{ data.email }}</el-descriptions-item>
-        <el-descriptions-item label="现居">{{ data.city }}</el-descriptions-item>
-      </el-descriptions>
-    </el-card>
-  </div>
+  <el-card shadow="never">
+    <template #header>
+      <CardTitle title="基本信息" />
+    </template>
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="100px"
+    >
+      <el-form-item label="用户头像" prop="avatar">
+        <UploadImg v-model="formData.avatar" height="140px" />
+      </el-form-item>
+      <el-form-item label="用户姓名" prop="name">
+        <el-input v-model="formData.name" />
+      </el-form-item>
+      <el-form-item label="性别" prop="sex">
+        <el-select v-model="formData.sex" placeholder="请选择性别">
+          <el-option
+            v-for="dict in getIntDictOptions(DICT_TYPE.MENDUNER_SEX)" :key="dict.value" :label="dict.label" :value="dict.value.toString()" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="联系电话" prop="phone">
+        <el-input v-model="formData.phone" />
+      </el-form-item>
+      <el-form-item label="邮箱" prop="email">
+        <el-input v-model="formData.email" />
+      </el-form-item>
+      <el-form-item label="出生日期" prop="birthday">
+        <el-date-picker v-model="formData.birthday" type="date" :disabledDate="disabledDates" value-format="x" placeholder="选择出生日期" />
+      </el-form-item>
+      <el-form-item label="学历" prop="eduType">
+        <el-select v-model="formData.eduType" placeholder="请选择学历">
+          <el-option
+            v-for="dict in getIntDictOptions(DICT_TYPE.MENDUNER_EDUCATION_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value.toString()" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="工作经验" prop="expType">
+        <el-select v-model="formData.expType" placeholder="请选择工作经验">
+          <el-option
+            v-for="dict in getIntDictOptions(DICT_TYPE.MENDUNER_EXP_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value.toString()" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="婚姻状况" prop="maritalStatus">
+        <el-select v-model="formData.maritalStatus" placeholder="请选择婚姻状况">
+          <el-option
+            v-for="dict in getIntDictOptions(DICT_TYPE.MENDUNER_MARITAL_STATUS)" :key="dict.value" :label="dict.label" :value="dict.value.toString()" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="首次工作时间" prop="firstWorkTime">
+        <el-date-picker v-model="formData.firstWorkTime" type="date" :disabledDate="disabledDates" value-format="x" placeholder="选择首次工作时间" />
+      </el-form-item>
+      <el-form-item label="现居城市" prop="areaId">
+        <el-cascader v-model="formData.areaId" :options="areaTreeData" :props="{ label: 'name', value: 'id', emitPath: false, checkStrictly: true }"  class="!w-240px" />
+      </el-form-item>
+      <el-form-item label="个人优势" prop="advantage">
+        <el-input v-model="formData.advantage" :rows="8" type="textarea" placeholder="请输入您的个人优势" />
+      </el-form-item>
+    </el-form>
+    <!-- <div class="text-right mt-2">
+      <el-button @click="submitForm" type="primary">保 存</el-button>
+    </div> -->
+  </el-card>
 </template>
 
 <script setup>
 defineOptions({ name: 'EnterpriseDetailsInfo'})
-// import { DICT_TYPE } from '@/utils/dict'
-// import { formatDate } from '@/utils/formatTime'
-// import { PersonInfoApi } from '@/api/menduner/system/person'
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { cloneDeep } from 'lodash-es'
+import { timesTampChange } from '@/utils/transform/date'
+import { getDict } from '@/hooks/web/useDictionaries'
 
 const props = defineProps({
-  // id: String,
-  // userId: String,
   data: Object
 })
 
-// const isEdit = ref(false)
+const formRef = ref()
+const formData = ref({
+  avatar: undefined,
+  sex: undefined,
+  phone: undefined,
+  email: undefined,
+  birthday: undefined,
+  eduType: undefined,
+  expType: undefined,
+  maritalStatus: undefined,
+  major: undefined,
+  firstWorkTime: undefined,
+  areaId: undefined,
+  advantage: undefined
+})
+const formRules = reactive({
+  // schoolName: [{ required: true, message: '学校名称不能为空', trigger: 'blur' }],
+  // major: [{ required: true, message: '专业名称不能为空', trigger: 'blur' }],
+  // educationType: [{ required: true, message: '学历不能为空', trigger: 'blur' }],
+  // startDate: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
+  // endDate: [{ required: true, message: '结束时间不能为空', trigger: 'blur' }]
+})
 
-// 获取人才详情
-// const info = ref({
-//   id: "1843909447483768834", 
-//   userId: "804044393148452864", 
-//   foreignName: "Oscar", 
-//   name: "沈和威", 
-//   sex: "0", 
-//   avatar: "https://www.mendunerhr.com/images/1726804566749_804044393148452864_mini.jpg", 
-//   phone: null, 
-//   email: "812320386@qq.com", 
-//   wxCode: null, 
-//   birthday: 828547200000, 
-//   maritalStatus: null, 
-//   areaId: null, 
-//   regId: null, 
-//   jobType: "0", 
-//   jobStatus: "2", 
-//   firstWorkTime: 1356969600000, 
-//   advantage: null, 
-//   expType: "7", 
-//   eduType: "3", 
-//   tagList: null, 
-//   createTime: 1728457245120, 
-//   updateTime: 1732688920332
-// })
-// const getInfo = async () => {
-//   const data = await PersonInfoApi.getPersonDetails(props.id)
-//   info.value = data
-// }
-// if (props.id) getInfo()
+watch(() => props.data, (newVal) => {
+  formData.value = cloneDeep(newVal)
+}, { deep: true })
 
-// 会员套餐列表
-// const packageList = ref([])
-// const getPackageList = async () => {
-//   const data = await PersonInfoApi.getPersonPackageList()
-//   packageList.value = data
-// }
-// getPackageList()
+// 未来的时间不可选
+const disabledDates = (date) => {
+  return date.getTime() > Date.now()
+}
+
+// 地区列表
+const areaTreeData = ref([])
+const getDictData = async () => {
+  const { data } = await getDict('areaTreeData', {}, 'areaTreeData')
+  const obj = data.find(e => e.name === '中国')
+  const list = obj?.children ? obj.children.map(e =>{
+    // 市辖区直接显示区
+    const municipality = e.children && e.children.length && e.children[0].name === '市辖区'
+    if (municipality && e.children[0].children?.length) e.children = e.children[0].children
+    return e
+  }) : []
+  areaTreeData.value = list.length ? list : []
+}
+getDictData()
 
-// 获取用户详情
-// const user = ref({
-//   id: "804044393148452864", 
-//   username: "13539831035", 
-//   password: "$2a$04$3QZFv24b5l8OxwHijZZQY.GDxXZZbdYvl8Y8cJXtBxQ2UDsCPnSBe", 
-//   email: "812320386@qq.com", 
-//   phone: "13539831035", 
-//   avatar: "https://www.mendunerhr.com/images/1726804566749_804044393148452864_mini.jpg", 
-//   status: "0", 
-//   registerIp: null, 
-//   registerTerminal: null, 
-//   vipFlag: null, 
-//   vipExpireDate: null, 
-//   inviteCode: null, 
-//   loginIp: "", 
-//   loginDate: null, 
-//   createTime: 1728291312047
-// })
-// const getUserDetails = async () => {
-//   const data = await PersonInfoApi.getUserDetails(props.userId)
-//   user.value = data
+// const submitForm = async () => {
+//   await formRef.value.validate()
 // }
-// getUserDetails()
 </script>

+ 0 - 70
src/views/menduner/system/talentMap/details/components/interviewInvite.vue

@@ -1,70 +0,0 @@
-<template>
-  <ContentWrap>
-    <el-table v-loading="loading" :data="list" :stripe="true">
-      <el-table-column label="投递岗位" align="center" prop="job.name" />
-      <el-table-column label="岗位薪资" align="center" prop="job.payFrom">
-        <template #default="{ row }">
-          {{ !row.job.payFrom && !row.job.payTo ? '面议' : `${row.job.payFrom}-${row.job.payTo}/${payUnit.find(e => e.value === Number(row.job.payUnit))?.label}` }}
-        </template>
-      </el-table-column>
-      <el-table-column label="发布企业" align="center" prop="enterprise.name">
-        <template #default="{ row }">{{ row.enterprise.anotherName || row.enterprise.name }}</template>
-      </el-table-column>
-      <el-table-column label="面试时间" align="center" prop="time">
-        <template #default="{ row }">{{ timesTampChange(row.time, 'Y-M-D h:m') }}</template>
-      </el-table-column>
-      <el-table-column label="面试地点" align="center" prop="address" />
-      <el-table-column label="联系人" align="center" prop="contact.name" />
-      <el-table-column label="联系电话" align="center" prop="phone" />
-      <el-table-column label="状态" align="center" prop="status">
-        <template #default="{ row }">
-          <dict-tag :type="DICT_TYPE.MENDUNER_INTERVIEW_INVITE_STATUS" :value="row.status" />
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
-</template>
-
-<script lang="ts" setup>
-import { timesTampChange } from '@/utils/transform/date'
-import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
-import { PersonInfoApi } from '@/api/menduner/system/person'
-defineOptions({ name: 'PersonInterviewInvite' })
-
-const props = defineProps({
-  userId: String
-})
-
-const loading = ref(true) // 列表的加载中
-const total = ref(0) // 列表的总页数
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  userId: props.userId
-})
-
-const payUnit = getIntDictOptions(DICT_TYPE.MENDUNER_PAY_UNIT)
-const list = ref([]) // 列表的数据
-const getList = async () => {
-  loading.value = true
-  try {
-    const data = await PersonInfoApi.getInterviewInvitePage(queryParams)
-    list.value = data.list
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 初始化 **/
-onMounted(() => {
-  getList()
-})
-</script>

+ 82 - 55
src/views/menduner/system/talentMap/details/components/jobIntention.vue

@@ -1,69 +1,96 @@
 <template>
-  <el-table v-loading="loading" :data="tableData" :stripe="true" class="m-t-20px">
-    <el-table-column label="期望职位" align="center" prop="positionName" />
-    <el-table-column label="期望行业" align="center" prop="industryNameList">
-      <template #default="scope">
-        {{  scope.row.industryNameList && scope.row.industryNameList.length ? scope.row.industryNameList.join(',') : '' }}
-      </template>
-    </el-table-column>
-    <el-table-column label="薪酬最低要求" align="center" prop="payFrom" />
-    <el-table-column label="薪酬最高要求" align="center" prop="payTo" />
-    <el-table-column label="求职类型" align="center" prop="jobType">
-      <template #default="scope">
-        <dict-tag :type="DICT_TYPE.MENDUNER_JOB_TYPE" :value="scope.row.jobType" />
-      </template>
-    </el-table-column>
-    <el-table-column label="工作城市" align="center" prop="workAreaName" />
-    <el-table-column label="其它感兴趣城市" align="center" prop="interestedAreaNameList">
-      <template #default="scope">
-        {{  scope.row.interestedAreaNameList && scope.row.interestedAreaNameList.length ? scope.row.interestedAreaNameList.join(',') : '' }}
-      </template>
-    </el-table-column>
-    <el-table-column
-      label="创建时间"
-      align="center"
-      prop="createTime"
-      :formatter="dateFormatter"
-      width="180px"
-    />
-  </el-table>
+  <el-card shadow="never" class="m-t-10px">
+    <template #header>
+      <div style="display: flex; justify-content: space-between;">
+        <CardTitle title="求职意向" />
+        <el-button link type="primary" @click="handleAdd"><Icon icon="ep:plus" class="mr-5px" />新增</el-button>
+      </div>
+    </template>
+    <el-table :data="list" :stripe="true">
+      <el-table-column label="意向职位" align="center" prop="name" />
+      <el-table-column label="操作" align="center" prop="actions">
+        <template #default="{ row, $index }">
+          <el-button link type="primary" @click="handleEdit(row, $index)">编辑</el-button>
+          <el-button link type="danger" @click="handleDelete($index)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </el-card>
 
-  <Pagination
-    :total="total"
-    v-model:page="queryParams.pageNo"
-    v-model:limit="queryParams.pageSize"
-    @pagination="getList"
-  />
+  <Dialog title="求职意向编辑" v-model="dialogVisible">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="80px"
+    >
+      <el-form-item label="意向职位" prop="name" required>
+        <el-input v-model="formData.name" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary">确 定</el-button>
+      <el-button @click="dialogVisible = false; formData = {}">取 消</el-button>
+    </template>
+  </Dialog>
 </template>
 
 <script setup>
-defineOptions({ name: 'PersonJobIntentionList'})
-import { PersonInfoApi } from '@/api/menduner/system/person'
-import { DICT_TYPE } from '@/utils/dict'
-import { dateFormatter } from '@/utils/formatTime'
+defineOptions({ name: 'TalentMapJobIntention'})
+import { cloneDeep } from 'lodash-es'
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { timesTampChange } from '@/utils/transform/date'
 
 const props = defineProps({
-  userId: String
+  data: Array
 })
+const list = ref([])
+watch(() => props.data, (newVal) => {
+  if (!newVal || !newVal.length) return list.value = []
+  const arr = newVal.map(e => {
+    return { name: e }
+  })
+  list.value = cloneDeep(arr)
+}, { deep: true, immediate: true })
 
-const loading = ref(false)
-const tableData = ref([])
-const total = ref(0)
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  userId: props.userId
+const message = useMessage() // 消息弹窗
+const editIndex = ref(0)
+const formRef = ref()
+const dialogVisible = ref(false)
+const formData = ref({})
+const type = ref('add')
+const formRules = reactive({
+  name: [{ required: true, message: '意向职位不能为空', trigger: 'blur' }]
 })
 
-const getList = async () => {
-  loading.value = true
+// 新增
+const handleAdd = () => {
+  type.value = 'add'
+  dialogVisible.value = true
+}
+
+// 编辑
+const handleEdit = (row, index) => {
+  type.value = 'edit'
+  editIndex.value = index
+  formData.value = cloneDeep(row)
+  dialogVisible.value = true
+}
+
+// 删除
+const handleDelete = async (index) => {
   try {
-    const data = await PersonInfoApi.getPersonJobIntentionPage(queryParams)
-    tableData.value = data.list
-    total.value = data.total
-  } finally {
-    loading.value = false
-  }
+    await message.delConfirm()
+    delete list.value[index]
+  } catch {}
+}
+
+const submitForm = async () => {
+  await formRef.value.validate()
+  type.value === 'add' ? list.value.push(formData.value) : list.value[editIndex.value] = formData.value
+  formData.value = {}
+  editIndex.value = 0
+  dialogVisible.value = false
+  console.log(list.value, 'jobIntention')
 }
-getList()
 </script>

+ 142 - 0
src/views/menduner/system/talentMap/details/components/search.vue

@@ -0,0 +1,142 @@
+<template>
+    <!-- 搜索工作栏 -->
+  <ContentWrap>
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="姓名" prop="name">
+        <el-input v-model="queryParams.name" placeholder="请输入姓名" clearable @keyup.enter="handleQuery" />
+      </el-form-item>
+      <el-form-item label="联系电话" prop="phone">
+        <el-input v-model="queryParams.phone" placeholder="请输入联系电话" clearable @keyup.enter="handleQuery" />
+      </el-form-item>
+      <el-form-item label="性别" prop="sex">
+        <el-select v-model="queryParams.sex" placeholder="请选择性别" clearable class="!w-192px">
+          <el-option v-for="dict in getIntDictOptions(DICT_TYPE.MENDUNER_SEX)" :key="dict.value" :label="dict.label" :value="dict.value" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="求职状态" prop="jobStatus">
+        <el-select v-model="queryParams.jobStatus" placeholder="请选择求职状态" clearable class="!w-192px">
+          <el-option v-for="dict in getIntDictOptions(DICT_TYPE.MENDUNER_JOB_SEEK_STATUS)" :key="dict.value" :label="dict.label" :value="dict.value" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="学历" prop="eduType">
+        <el-select v-model="queryParams.eduType" placeholder="请选择学历" clearable class="!w-192px">
+          <el-option v-for="dict in getIntDictOptions(DICT_TYPE.MENDUNER_EDUCATION_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="工作经验" prop="expType">
+        <el-select v-model="queryParams.expType" placeholder="请选择工作经验" clearable class="!w-192px">
+          <el-option v-for="dict in getIntDictOptions(DICT_TYPE.MENDUNER_EXP_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery" type="primary"><Icon icon="ep:search" /> 搜索</el-button>
+        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list" :stripe="true">
+      <!-- <el-table-column label="用户头像" align="center" prop="avatar" width="90px" fixed="left">
+        <template #default="scope">
+          <el-image v-if="scope.row.person?.avatar" class="h-80px w-80px" :src="scope.row.person?.avatar" lazy preview-teleported :preview-src-list="[scope.row.person?.avatar]" fit="contain" />
+        </template>
+      </el-table-column> -->
+      <el-table-column label="姓名" align="center" prop="person.name" fixed="left" />
+      <el-table-column label="联系电话" align="center" prop="user.phone" width="120px" />
+      <el-table-column label="求职状态" align="center" prop="person.jobStatus" width="130px">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.MENDUNER_JOB_SEEK_STATUS" :value="scope.row.person?.jobStatus" />
+        </template>
+      </el-table-column>
+      <el-table-column label="学历" align="center" prop="person.eduType">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.MENDUNER_EDUCATION_TYPE" :value="scope.row.person?.eduType" />
+        </template>
+      </el-table-column>
+      <el-table-column label="工作经验" align="center" prop="person.expType">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.MENDUNER_EXP_TYPE" :value="scope.row.person?.expType" />
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center">
+        <template #default="scope">
+          <el-button link type="primary" @click="handleDetail(scope.row)">详情</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      layout="total, prev, pager, next"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+</template>
+
+<script setup>
+defineOptions({ name: 'TalentMapSearch' })
+import { TalentMap } from '@/api/menduner/system/talentMap'
+import { PersonInfoApi } from '@/api/menduner/system/person'
+import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
+
+const emit = defineEmits(['detail'])
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const loading = ref(false) // 列表的加载中
+const list = ref([]) // 列表的数据
+const total = ref(0) // 列表的总页数
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  name: '樊登登',
+  phone: undefined,
+  expType: undefined,
+  eduType: undefined,
+  jobStatus: undefined
+})
+const queryFormRef = ref() // 搜索的表单
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await PersonInfoApi.getPersonInfoPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+const handleDetail = (row) => {
+  emit('detail', row.person.id, row.user.id)
+}
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+</script>

+ 19 - 13
src/views/menduner/system/talentMap/details/components/tags.vue

@@ -1,42 +1,48 @@
 <!--  -->
 <template>
   <div>
-    <div v-if="tagList?.length">
+    <div>
       <el-tag
-        v-for="item of tagList"
-        :key="item.id"
+        v-for="(item, index) of list"
+        :key="index"
         closable
         size="large"
         class="m-r-10px m-b-10px"
-      >{{ item.name }}</el-tag>
+      >{{ item.skillsName }}</el-tag>
     </div>
   </div>
 </template>
 
 <script setup>
 defineOptions({name: 'Tags'})
-const tagList = ref([
+import { cloneDeep } from 'lodash-es'
+
+const props = defineProps({
+  data: Array
+})
+
+const tagList = [
   {
-    name: "技术专家", 
+    skillsName: "技术专家", 
     id: "1"
   }, 
   {
-    name: "管理精英", 
+    skillsName: "管理精英", 
     id: "2"
   }, 
   {
-    name: "创新先锋", 
+    skillsName: "创新先锋", 
     id: "3"
   }, 
   {
-    name: "市场高手", 
+    skillsName: "市场高手", 
     id: "4"
   }, 
   {
-    name: "团队领袖", 
+    skillsName: "团队领袖", 
     id: "5"
   }
-])
+]
+
+const list = ref(props.data && props.data.length ? cloneDeep(props.data) : tagList)
 </script>
-<style lang="scss" scoped>
-</style>

+ 117 - 0
src/views/menduner/system/talentMap/details/components/training.vue

@@ -0,0 +1,117 @@
+<template>
+  <el-card shadow="never" class="m-t-10px">
+    <template #header>
+      <div style="display: flex; justify-content: space-between;">
+        <CardTitle title="培训经历" />
+        <el-button link type="primary" @click="handleAdd"><Icon icon="ep:plus" class="mr-5px" />新增</el-button>
+      </div>
+    </template>
+    <el-table :data="list" :stripe="true">
+      <el-table-column label="培训中心" align="center" prop="orgName" />
+      <el-table-column label="培训课程" align="center" prop="course" />
+      <el-table-column label="培训时间" align="center" prop="startTime">
+        <template #default="scope">
+          {{ scope.row.startTime ? timesTampChange(scope.row.startTime, 'Y-M') : '' }}
+          <span v-if="scope.row.startTime && scope.row.endTime">至</span>
+          {{ scope.row.endTime ? timesTampChange(scope.row.endTime, 'Y-M') : '' }}
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" prop="actions">
+        <template #default="{ row, $index }">
+          <el-button link type="primary" @click="handleEdit(row, $index)">编辑</el-button>
+          <el-button link type="danger" @click="handleDelete($index)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </el-card>
+
+  <Dialog title="培训经历编辑" v-model="dialogVisible">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="80px"
+    >
+      <el-form-item label="培训中心" prop="orgName" required>
+        <el-input v-model="formData.orgName" />
+      </el-form-item>
+      <el-form-item label="培训课程" prop="course" required>
+        <el-input v-model="formData.course" />
+      </el-form-item>
+      <el-form-item label="开始日期" prop="startTime" required>
+        <el-date-picker v-model="formData.startTime" :disabledDate="disabledDates" type="month"  value-format="x" placeholder="选择培训开始日期" />
+      </el-form-item>
+      <el-form-item label="结束日期" prop="endTime" required>
+        <el-date-picker v-model="formData.endTime" :disabledDate="disabledDates" type="month"  value-format="x" placeholder="选择在培训束日期" />
+      </el-form-item>
+      <el-form-item label="培训内容" prop="content">
+        <el-input v-model="formData.content" :rows="8" type="textarea" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary">确 定</el-button>
+      <el-button @click="dialogVisible = false; formData = {}">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+
+<script setup>
+defineOptions({ name: 'TalentMapTraining'})
+import { cloneDeep } from 'lodash-es'
+import { timesTampChange } from '@/utils/transform/date'
+
+const props = defineProps({
+  data: Array
+})
+const list = ref([])
+watch(() => props.data, (newVal) => {
+  list.value = cloneDeep(newVal)
+}, { deep: true })
+
+const message = useMessage() // 消息弹窗
+const editIndex = ref()
+const formRef = ref()
+const dialogVisible = ref(false)
+const formData = ref({})
+const type = ref('add')
+const formRules = reactive({
+  orgName: [{ required: true, message: '培训中心不能为空', trigger: 'blur' }],
+  course: [{ required: true, message: '培训课程不能为空', trigger: 'blur' }],
+  startDate: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
+  endDate: [{ required: true, message: '结束时间不能为空', trigger: 'blur' }]
+})
+
+// 未来的时间不可选
+const disabledDates = (date) => {
+  return date.getTime() > Date.now()
+}
+
+const handleAdd = () => {
+  type.value = 'add'
+  dialogVisible.value = true
+}
+
+const handleEdit = (row, index) => {
+  type.value = 'edit'
+  editIndex.value = index
+  formData.value = cloneDeep(row)
+  dialogVisible.value = true
+}
+
+// 删除
+const handleDelete = async (index) => {
+  try {
+    await message.delConfirm()
+    delete list.value[index]
+  } catch {}
+}
+
+const submitForm = async () => {
+  await formRef.value.validate()
+  type.value === 'add' ? list.value.push(formData.value) : list.value[editIndex.value] = formData.value
+  formData.value = {}
+  editIndex.value = 0
+  dialogVisible.value = false
+  console.log(list.value, 'submit')
+}
+</script>

+ 130 - 21
src/views/menduner/system/talentMap/details/index.vue

@@ -2,18 +2,55 @@
   <div>
     <el-row :gutter="10">
       <el-col :span="14">
-        <el-card shadow="never">
-          <IFrame :src="fileUrl" />
-        </el-card>
+        <el-tabs type="border-card">
+          <el-tab-pane label="简历解析" v-loading="loading">
+            <div v-if="fileUrl">
+              <div class="text-right m-b-10px">
+                <el-button type="primary" @click="handleResetUpload">重新上传简历</el-button>
+              </div>
+              <IFrame :src="fileUrl" />
+            </div>
+            <el-upload
+              v-else
+              ref="uploadRef"
+              v-model:file-list="fileList"
+              :action="uploadUrl"
+              :auto-upload="false"
+              :data="data"
+              :limit="1"
+              :on-change="handleChange"
+              :on-error="submitFormError"
+              :on-exceed="handleExceed"
+              :on-success="submitFormSuccess"
+              :http-request="httpRequest"
+              accept=".pdf, doc, .docx"
+              drag
+              style="flex: 1;"
+            >
+              <i class="el-icon-upload"></i>
+              <div class="el-upload__text">上传附件, 将文件拖到此处,或 <em>点击上传</em></div>
+              <template #tip>
+                <div class="el-upload__tip" style="color: red">
+                  提示:仅允许导入 pdf、doc、docx 格式文件!
+                </div>
+              </template>
+            </el-upload>
+          </el-tab-pane>
+          <el-tab-pane label="人员搜索">
+            <Search @detail="handleDetail" />
+          </el-tab-pane>
+        </el-tabs>
       </el-col>
+
       <el-col :span="10">
-        <div>
+        <div :style="{'height': height, 'overflow-y': 'auto'}">
           <el-tabs type="border-card">
-            <el-tab-pane v-if="Object.keys(result).length" label="简历解析内容">
-              <Info :data="result"/>
-              <Evaluation :data="result" class="m-t-10px" />
-              <Edu :data="result.educationObjs"  />
-              <Exp :data="result.jobExpObjs" />
+            <el-tab-pane label="简历解析内容">
+              <Info :data="result?.person"/>
+              <JobIntention :data="result?.person?.jobInterestedList" />
+              <Edu :data="result?.eduList"  />
+              <Exp :data="result?.workList" />
+              <Training :data="result?.trainList" />
             </el-tab-pane>
             <el-tab-pane label="标签">
               <el-card shadow="never">
@@ -36,14 +73,14 @@
                   <el-button type="primary" class="m-l-12px" @click="saveTags">保存</el-button>
                   <el-button @click="addNewTag = false">关闭</el-button>
                 </div>
-                <Tags :id="id" />
+                <Tags v-if="Object.keys(result).length" :data="result?.resume?.skillsObjs" />
               </el-card>
-              <el-card shadow="never" class="m-t-10px">
+              <!-- <el-card shadow="never" class="m-t-10px">
                 <template #header>
                   <CardTitle title="推荐标签" />
                 </template>
                 <TagsRecommend :id="id" />
-              </el-card>
+              </el-card> -->
             </el-tab-pane>
           </el-tabs>
         </div>
@@ -55,16 +92,20 @@
 <script setup>
 defineOptions({ name: 'TalentMapDetail'})
 import { useTagsViewStore } from '@/store/modules/tagsView'
-import { ElMessage } from 'element-plus'
+import { TalentMap } from '@/api/menduner/system/talentMap'
+import { useUpload } from '@/components/UploadFile/src/useUpload'
+import { commonApi } from '@/api/menduner/common'
+import { PersonInfoApi } from '@/api/menduner/system/person'
+import { Base64 } from 'js-base64'
+
 import Info from './components/info.vue'
 import Edu from './components/edu.vue'
 import Exp from './components/exp.vue'
-import Attachment from './components/attachment.vue'
-import DeliveryJob from './components/deliveryJob.vue'
-import Evaluation from './components/evaluation.vue'
+import JobIntention from './components/jobIntention.vue'
+import Training from './components/training.vue'
 import Tags from './components/tags.vue'
 import TagsRecommend from './components/tagsRecommend.vue'
-import { TalentMap } from '@/api/menduner/system/talentMap'
+import Search from './components/search.vue'
 
 const addNewTag = ref(false)
 const newTagText = ref('')
@@ -72,16 +113,84 @@ const saveTags = () => {
   addNewTag.value = false
 }
 
+const loading = ref(false)
 const result = ref({})
-const fileUrl = ref('')
+const fileUrl = ref('https://minio.menduner.com/dev/person/229988673960153088/attachment/ee3eb21f45e13ede3557a03d18585ed80c5b4212ac5634e3436e309afaa8fe6a.pdf')
+const uploadRef = ref()
+const fileList = ref([])
+const data = ref({ path: '' })
+const { uploadUrl, httpRequest } = useUpload()
 
 /** 初始化 */
+const message = useMessage() // 消息弹窗
+const height = ref(0)
 const { currentRoute } = useRouter() // 路由
 const route = useRoute()
 const { id } = route.params
 onMounted(async () => {
-  const parserData = localStorage.getItem('resumeParserData') ? JSON.parse(localStorage.getItem('resumeParserData')) : {}
-  fileUrl.value = parserData.url
-  result.value = parserData.data
+  height.value = document.documentElement.clientHeight + 'px'
 })
+
+// 简历解析
+const getResumeParser = async (url) => {
+  loading.value = true
+  try {
+    const data = await commonApi.resumeParser({ fileUrl: url })
+    console.log(data, 'resumeParserData')
+    result.value = data
+  } finally {
+    loading.value = false
+  }
+}
+getResumeParser(fileUrl.value)
+
+// 文件上传
+const baseUrl = import.meta.env.VITE_PREVIEW_URL
+const handleChange = async (file) => {
+  // data.value.path = file.name
+  // unref(uploadRef)?.submit()
+  // if (!fileList.value.length) return
+
+  // const url = fileList.value[0].response.data
+  // fileUrl.value = !url.includes('.pdf') ?  `${baseUrl}/onlinePreview?url=${encodeURIComponent(Base64.encode(url))}` : url
+  // await getResumeParser(url)
+}
+
+/** 上传错误提示 */
+const submitFormError = () => {
+  message.error('上传失败,请您重新上传!')
+}
+
+/** 文件数超出提示 */
+const handleExceed = () => {
+  message.error('最多只能上传一个文件!')
+}
+
+const submitFormSuccess = () => {
+  // 清理
+  unref(uploadRef)?.clearFiles()
+}
+
+// 重新上传简历
+const handleResetUpload = () => {
+  fileUrl.value = ''
+  data.value.path = ''
+  fileList.value = []
+  result.value = {}
+}
+
+// 查看详情
+const apiArr = [
+  { api: PersonInfoApi.getPersonDetails, key: 'person' },
+  { api: PersonInfoApi.getPersonEduPage, key: 'eduList', type: 'array' },
+  { api: PersonInfoApi.getPersonExpPage, key: 'workList', type: 'array' },
+  { api: PersonInfoApi.getPersonTrainingPage, key: 'trainList', type: 'array' }
+]
+const handleDetail = async (id, userId) => {
+  if (!id) return message.warning('请先选择人才!')
+  apiArr.forEach(async (val) => {
+    const data = await val.api(val.type === 'array' ? { pageSize: 100, pageNo: 1, userId } : id)
+    result.value[val.key] = val.type === 'array' ? data.list : data || {}
+  })
+}
 </script>

+ 14 - 121
src/views/menduner/system/talentMap/index.vue

@@ -25,7 +25,7 @@
   <ContentWrap>
     <el-table v-loading="loading" :data="list" :stripe="true">
       <el-table-column label="姓名" align="center" prop="name" />
-      <el-table-column label="标签个数" align="center" prop="num" />
+      <el-table-column label="性别" align="center" prop="sexStr" />
       <el-table-column label="操作" align="center">
         <template #default="scope">
           <el-button link type="primary" @click="openDetail(scope.row)">查看</el-button>
@@ -41,57 +41,18 @@
       @pagination="getList"
     />
   </ContentWrap>
-
-  <!-- 表单弹窗:添加/修改 -->
-  <TalentForm ref="formRef" @success="getList" />
-
-  <Dialog v-model="dialogVisible" title="新增人才" style="width: 47%;">
-    <div style="display: flex;">
-      <el-upload
-        ref="uploadRef"
-        v-model:file-list="fileList"
-        :action="uploadUrl"
-        :auto-upload="false"
-        :data="data"
-        :limit="1"
-        :on-change="handleChange"
-        :on-error="submitFormError"
-        :on-exceed="handleExceed"
-        :on-success="submitFormSuccess"
-        :http-request="httpRequest"
-        accept=".pdf"
-        drag
-        style="flex: 1;"
-      >
-        <i class="el-icon-upload"></i>
-        <div class="el-upload__text">上传附件, 将文件拖到此处,或 <em>点击上传</em></div>
-        <template #tip>
-          <div class="el-upload__tip" style="color: red">
-            提示:仅允许导入 pdf 格式文件!
-          </div>
-        </template>
-      </el-upload>
-      <div class="m-l-10px info" @click="dialogVisible = false; formRef.open()">
-        点击填写基本信息
-      </div>
-    </div>
-  </Dialog>
 </template>
 
 <script setup>
-import download from '@/utils/download'
-import { commonApi } from '@/api/menduner/common'
-import File from '@/components/Upload/file.vue'
-import { useUpload } from '@/components/UploadFile/src/useUpload'
-import TalentForm from './talentForm.vue'
+import { TalentMap } from '@/api/menduner/system/talentMap'
 
-/** 猎寻服务 列表 */
+/** 人才地图 列表 */
 defineOptions({ name: 'TalentMap' })
 
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
 
-const loading = ref(true) // 列表的加载中
+const loading = ref(false) // 列表的加载中
 const list = ref([]) // 列表的数据
 const total = ref(0) // 列表的总页数
 const queryParams = reactive({
@@ -99,53 +60,18 @@ const queryParams = reactive({
   pageSize: 10,
   name: undefined,
 })
-const formRef = ref()
 const queryFormRef = ref() // 搜索的表单
-const exportLoading = ref(false) // 导出的加载中
-
-const uploadRef = ref()
-const dialogVisible = ref(false)
-const data = ref({ path: '' })
-const fileList = ref([]) // 文件列表
-const { uploadUrl, httpRequest } = useUpload()
-
-/** 上传错误提示 */
-const submitFormError = () => {
-  message.error('上传失败,请您重新上传!')
-}
-
-/** 文件数超出提示 */
-const handleExceed = () => {
-  message.error('最多只能上传一个文件!')
-}
-
-const submitFormSuccess = () => {
-  // 清理
-  dialogVisible.value = false
-  unref(uploadRef)?.clearFiles()
-}
-
-// 文件上传
-const handleChange = (file) => {
-  data.value.path = file.name
-  unref(uploadRef)?.submit()
-  if (fileList.value.length) handleUploadResume(fileList.value[0].response.data, fileList.value[0].name)
-}
 
 /** 查询列表 */
 const getList = async () => {
-  // list.value = [{id: '1843909421273563137', userId: '800018973600124928', name: '测试', num: '23'}]
-  loading.value = false
-  
-  // loading.value = true
-  // try {
-  //   const data = await HuntApi.getHuntPage(queryParams)
-  //   // list.value = data.list
-  //   list.value = [{name: '测试'}]
-  //   total.value = data.total
-  // } finally {
-  //   loading.value = false
-  // }
+  loading.value = true
+  try {
+    const data = await TalentMap.getTalentMapInfoPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
 }
 
 /** 搜索按钮操作 */
@@ -180,44 +106,11 @@ const handleDelete = async (id) => {
 }
 
 const handleAdd = () => {
-  dialogVisible.value = true
+  push({ name: 'TalentMapDetail' })
 }
 
-// 上传附件
-const handleUploadResume = async (url, filename) => {
-  if (url) {
-    const res = await commonApi.resumeParser({ fileUrl: url })
-    if (res?.status?.code === 200) {
-      localStorage.setItem('resumeParserData', JSON.stringify({
-        data: res.result,
-        url: url,
-        filename: filename,
-        id: Date.now()
-      }))
-      // 路由跳转
-      push({ name: 'TalentMapDetail' })
-    }
-  }
-}
 /** 初始化 **/
 onMounted(() => {
   getList()
 })
-</script>
-
-<style lang="scss">
-.info {
-  display: flex;
-  width: 50%;
-  height: 99px;
-  cursor: pointer; 
-  border: 1px dashed #ccc; 
-  border-radius: 6px; 
-  align-items: center; 
-  justify-content: center;
-
-  &:hover {
-    border: 1px dashed #409eff;
-  }
-}
-</style>
+</script>

+ 1 - 1
src/views/menduner/system/web/WebContentForm.vue

@@ -57,7 +57,7 @@ const maxWidth = ref(0)
 const maxHeight = ref(0)
 
 const imgSizeList = {
-  'pcLeft': [528, 919],
+  'pcLeft': [869, 1512],
   'pcAdvertisement': [900, 530],
   'pcHomeCarousel': [792, 392],
   'pcLoginCarousel': [792, 392],

+ 1 - 1
src/views/menduner/system/web/index.vue

@@ -59,7 +59,7 @@ const loading = ref(false)
 const tab = ref(2)
 let info = reactive({})
 const tabList = [
-  { label: 'PC首页左侧广告图', value: 0, key: 'pcLeft', size: '宽528px*高919px' },
+  { label: 'PC首页左侧广告图', value: 0, key: 'pcLeft', size: '宽869px*高1512px' },
   { label: 'PC首页弹窗广告图', value: 1, key: 'pcAdvertisement', size: '宽900px*高530px' },
   { label: 'PC首页轮播图', value: 2, key: 'pcHomeCarousel', size: '宽792px*高392px' },
   { label: 'PC登录页轮播图', value: 3, key: 'pcLoginCarousel', size: '宽792px*高392px' },