|
@@ -0,0 +1,368 @@
|
|
|
+<template>
|
|
|
+ <div v-loading="saveLoading">
|
|
|
+ <el-row :gutter="10">
|
|
|
+ <el-col :span="12">
|
|
|
+ <ContentWrap>
|
|
|
+ <div>
|
|
|
+ <!-- 简历解析 -->
|
|
|
+ <template v-if="dataType === 'file'">
|
|
|
+ <div v-if="fileUrl" style="position: relative;">
|
|
|
+ <div class="text-right m-b-10px">
|
|
|
+ <el-button v-if="!isEdit" @click="handleText">查看文本信息</el-button>
|
|
|
+ <el-button type="primary" @click="handleResetUpload">重新上传简历</el-button>
|
|
|
+ </div>
|
|
|
+ <IFrame :src="fileUrl" />
|
|
|
+ <el-drawer
|
|
|
+ v-model="drawer"
|
|
|
+ modal-class="drawer"
|
|
|
+ size="75%"
|
|
|
+ direction="ltr"
|
|
|
+ title="简历解析(可复制文本使用)"
|
|
|
+ >
|
|
|
+ <p v-for="(text, index) in resumeTxt" :key="text + index">{{ text }}</p>
|
|
|
+ </el-drawer>
|
|
|
+ </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
|
|
|
+ class="flex-1"
|
|
|
+ >
|
|
|
+ <i class="el-icon-upload"></i>
|
|
|
+ <div class="el-upload__text">上传附件, 将文件拖到此处,或 <em>点击上传</em></div>
|
|
|
+ <template #tip>
|
|
|
+ <div class="el-upload__tip color-red">
|
|
|
+ 提示:仅允许导入 pdf、doc、docx 格式文件!
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-upload>
|
|
|
+ </template>
|
|
|
+ <!-- 名片解析 -->
|
|
|
+ <template v-else-if="dataType === 'card'">
|
|
|
+ <div class="image">
|
|
|
+ <el-image v-if="cardImgUrl" class="!w-100%" :src="cardImgUrl" />
|
|
|
+ <div v-else>
|
|
|
+ <UploadImg
|
|
|
+ v-model="cardImgUrl"
|
|
|
+ :limit="1"
|
|
|
+ :uploadSuccessTip="false"
|
|
|
+ drag
|
|
|
+ buttonUpload
|
|
|
+ @handle-change="cardUploadChange"
|
|
|
+ height="32px" width="104px"
|
|
|
+ style="margin: 0 auto; width: 104px;margin-top: 40%;"
|
|
|
+ >
|
|
|
+ <template #tip>{{ cardImgUrl ? '' : '请上传名片' }}</template>
|
|
|
+ </UploadImg>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <!-- 网页解析 -->
|
|
|
+ <template v-else-if="dataType === 'web'">
|
|
|
+ <webAnalysis />
|
|
|
+ </template>
|
|
|
+ <!-- 门墩儿人才库 -->
|
|
|
+ <template v-else>
|
|
|
+ <Search @detail="handleDetail" :detailButTxt="detailButTxt" :searchName="formData?.name_zh || formData?.name_en || ''" />
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </ContentWrap>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <ContentWrap>
|
|
|
+ <div ref="content" :style="{'height': height, 'overflow-y': 'auto'}">
|
|
|
+ <FormPage ref="FormPageRef" :itemData="formData" />
|
|
|
+ <div class="text-center m-t-30px">
|
|
|
+ <el-affix position="bottom" :offset="20">
|
|
|
+ <el-button type="primary" @click="handleSave" size="large" class="!w-120px" :disabled="analysisLoading">保 存</el-button>
|
|
|
+ </el-affix>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </ContentWrap>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <MergeForm ref="mergeFormRef" @refresh="null" />
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+defineOptions({ name: 'TalentMapGatherDetails'})
|
|
|
+import { dateFormatter } from '@/utils/formatTime'
|
|
|
+import { talentLabelingApi } from '@/api/menduner/system/talentMap/labeling'
|
|
|
+import { TalentMap } from '@/api/menduner/system/talentMap'
|
|
|
+import MergeForm from '@/views/menduner/system/talentMap/components/merge.vue'
|
|
|
+import FormPage from '@/views/menduner/system/talentMap/components/FormPage.vue'
|
|
|
+import { timesTampChange, timestampToAge } from '@/utils/transform/date'
|
|
|
+import { useUpload } from '@/components/UploadFile/src/useUpload'
|
|
|
+import { commonApi } from '@/api/menduner/common'
|
|
|
+import { Base64 } from 'js-base64'
|
|
|
+import Search from './components/search.vue'
|
|
|
+import webAnalysis from './components/webAnalysis.vue'
|
|
|
+
|
|
|
+const { back } = useRouter()
|
|
|
+const loading = ref(false)
|
|
|
+const activeName = ref('resumeAnalysis')
|
|
|
+const saveLoading = ref(false)
|
|
|
+const { uploadUrl, httpRequest } = useUpload()
|
|
|
+
|
|
|
+/** 初始化 */
|
|
|
+// const InfoRef = ref()
|
|
|
+const content = ref()
|
|
|
+const message = useMessage() // 消息弹窗
|
|
|
+const height = ref(0)
|
|
|
+const route = useRoute()
|
|
|
+const { id } = route.params
|
|
|
+const dataType = ref(route?.query?.type) // 查询参数
|
|
|
+const baseUrl = import.meta.env.VITE_PREVIEW_URL
|
|
|
+const isEdit = ref(false)
|
|
|
+
|
|
|
+// 更新
|
|
|
+const formLoading = ref(false)
|
|
|
+const FormPageRef = ref(null)
|
|
|
+const mergeFormRef = ref() // 合并表单 Ref
|
|
|
+const handleSave = async () => {
|
|
|
+ const params = { ...FormPageRef.value.formQuery, type: dataType.value }
|
|
|
+ if (!params.name_zh) return message.warning('请填写姓名!')
|
|
|
+
|
|
|
+ // 数组转为字符串保存
|
|
|
+ if (Array.isArray(params?.mobile)) {
|
|
|
+ params.mobile = params.mobile.filter(i => Boolean(i)).map(j => String(j).replace(/,|,/g, '')).join(',');
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(params, 'handleSubmit')
|
|
|
+ try {
|
|
|
+ formLoading.value = true
|
|
|
+ let result = {}
|
|
|
+
|
|
|
+ if (analysisType.value === 'create') {
|
|
|
+ if (cardFileQuery.value) {
|
|
|
+ cardFileQuery.value.append('card_data', JSON.stringify(params)) // 名片
|
|
|
+ result = await talentLabelingApi.createBusinessCard(cardFileQuery.value)
|
|
|
+ } else {
|
|
|
+ // 结构化数据源 不传递文件
|
|
|
+ result = await talentLabelingApi.createBusinessCardPost(params)
|
|
|
+ }
|
|
|
+ message.success('新增成功')
|
|
|
+ back()
|
|
|
+
|
|
|
+ if (result.code === 202 || result.message.includes('疑似重复')) {
|
|
|
+ if (!result.data?.main_card?.id) return
|
|
|
+
|
|
|
+ await message.confirm('发现与当前名片的疑似重复数据,去处理')
|
|
|
+ mergeFormRef.value.open(result.data?.main_card?.id)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ await talentLabelingApi.updateBusinessCard(params, formData.value.id)
|
|
|
+ message.success('更新成功')
|
|
|
+ back()
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.log('更新失败', error)
|
|
|
+ } finally {
|
|
|
+ cardFileQuery.value = null
|
|
|
+ formLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 简历解析
|
|
|
+const fileUrl = ref('') // https://minio.menduner.com/dev/person/229988673960153088/attachment/ee3eb21f45e13ede3557a03d18585ed80c5b4212ac5634e3436e309afaa8fe6a.pdf
|
|
|
+const uploadRef = ref()
|
|
|
+const fileList = ref([])
|
|
|
+const data = ref({ path: '' })
|
|
|
+// 文件上传
|
|
|
+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
|
|
|
+ //
|
|
|
+ if (FormPageRef.value?.changeLoading) FormPageRef.value.changeLoading(true)
|
|
|
+ message.warning('正在解析...')
|
|
|
+ const data = await commonApi.resumeParser({ fileUrl: fileUrl.value })
|
|
|
+ resumeAnalysisToForm(data) // 简历解析
|
|
|
+ if (FormPageRef.value?.changeLoading) FormPageRef.value.changeLoading(false)
|
|
|
+}
|
|
|
+const submitFormError = () => {
|
|
|
+ message.error('上传失败,请您重新上传!')
|
|
|
+}
|
|
|
+const handleExceed = () => {
|
|
|
+ message.error('最多只能上传一个文件!')
|
|
|
+}
|
|
|
+const submitFormSuccess = (e) => {
|
|
|
+ // 清理
|
|
|
+ // unref(uploadRef)?.clearFiles()
|
|
|
+}
|
|
|
+
|
|
|
+const drawer = ref(false)
|
|
|
+const resumeTxt = ref([])
|
|
|
+// 查看文本信息
|
|
|
+const handleText = () => {
|
|
|
+ drawer.value = true
|
|
|
+}
|
|
|
+// 重新上传简历
|
|
|
+const handleResetUpload = async () => {
|
|
|
+ await message.confirm('是否确定重新上传简历?确定后将清空当前信息')
|
|
|
+ fileUrl.value = ''
|
|
|
+ data.value.path = ''
|
|
|
+ fileList.value = []
|
|
|
+ resumeAnalysisToForm('reset') // 简历解析
|
|
|
+}
|
|
|
+
|
|
|
+const detailButTxt = '应用'
|
|
|
+// 搜索-查看详情
|
|
|
+const handleDetail = async (userId) => {
|
|
|
+ if (!userId) return message.warning('请先选择人才!')
|
|
|
+ try {
|
|
|
+ const data = await TalentMap.getTalentMapDetail(userId)
|
|
|
+ // 去除id
|
|
|
+ resumeAnalysisToForm(data) // 简历解析
|
|
|
+ message.success(`${detailButTxt}成功`)
|
|
|
+ } catch {}
|
|
|
+}
|
|
|
+
|
|
|
+// 简历解析数据解构赋值
|
|
|
+const resumeAnalysisToForm = (data) => {
|
|
|
+ if (data === 'reset') {
|
|
|
+ // 重置表单
|
|
|
+ resumeTxt.value = ''
|
|
|
+ if (FormPageRef.value?.resetFormData) FormPageRef.value.resetFormData()
|
|
|
+ }
|
|
|
+ formData.value = {
|
|
|
+ name_zh: data?.person?.name || '',
|
|
|
+ email: data?.person?.email || '',
|
|
|
+ mobile: data?.person?.phone || '',
|
|
|
+ birthday: data?.person?.birthday ? timesTampChange(data.person.birthday, 'Y-M-D') : '',
|
|
|
+ age: data?.person?.birthday ? timestampToAge(data.person.birthday) : null,
|
|
|
+ career_path: data?.workList ? data.workList.map(e => {
|
|
|
+ return {
|
|
|
+ hotel_zh: e?.enterpriseName || null,
|
|
|
+ title_zh: e?.positionName || null,
|
|
|
+ date: e?.startTime ? timesTampChange(e.startTime, 'Y-M-D') : null
|
|
|
+ }
|
|
|
+ }) : null,
|
|
|
+ created_at: data?.person?.createTime ? timesTampChange(data.person.createTime, 'Y-M-D') : null,
|
|
|
+ updated_at: data?.person?.updateTime ? timesTampChange(data.person.updateTime, 'Y-M-D') : null,
|
|
|
+ }
|
|
|
+ resumeTxt.value = data?.resume?.rawText?.split('\n') || ''
|
|
|
+ if (FormPageRef.value?.setFormData) FormPageRef.value.setFormData(formData.value)
|
|
|
+}
|
|
|
+
|
|
|
+// 名片解析
|
|
|
+const createAnalysisNum = ref(0)
|
|
|
+const cardFileQuery = ref(null)
|
|
|
+const cardUploadRow = ref(null)
|
|
|
+const cardImgUrl = ref(null)
|
|
|
+const cardUploadChange = (raw) => {
|
|
|
+ cardUploadRow.value = raw
|
|
|
+}
|
|
|
+
|
|
|
+/** 编辑 */
|
|
|
+const handleEdit = async (item) => {
|
|
|
+ formData.value = item
|
|
|
+ dataType.value = item.type || 'menduner'
|
|
|
+ try {
|
|
|
+ if (dataType.value === 'card') {
|
|
|
+ if (!item?.image_path) {
|
|
|
+ cardUploadRow.value = null
|
|
|
+ cardImgUrl.value = null
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const data = await talentLabelingApi.getBusinessCardImage(item.image_path)
|
|
|
+ cardUploadRow.value = data?.type ? new File([data], item.image_path, { type: data.type }) : null
|
|
|
+ cardImgUrl.value = data?.type ? URL.createObjectURL(data) : null
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.log('打印->getBusinessCardImage', error)
|
|
|
+ } finally {
|
|
|
+ if (FormPageRef.value?.setFormData) FormPageRef.value.setFormData(formData.value)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 解析中
|
|
|
+const analysisLoading = ref(false)
|
|
|
+const formData = ref({})
|
|
|
+const handleAnalysis = async () => {
|
|
|
+ // 开始解析
|
|
|
+ analysisLoading.value = true
|
|
|
+ cardFileQuery.value = null
|
|
|
+ formData.value = null
|
|
|
+ const type = dataType.value
|
|
|
+ try {
|
|
|
+ // if (type === 'menduner') { // 门墩儿人才库
|
|
|
+ // } else
|
|
|
+ if (type === 'file') { // 简历解析
|
|
|
+ if (!fileUrl.value) return message.warning('获取文件失败,请重新上传!')
|
|
|
+ const data = await commonApi.resumeParser({ fileUrl: fileUrl.value })
|
|
|
+ resumeAnalysisToForm(data) // 简历解析
|
|
|
+ } else if (type === 'card') { // 名片解析
|
|
|
+ if (!cardImgUrl.value) {
|
|
|
+ message.warning('请先上传名片!')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ cardFileQuery.value = new FormData()
|
|
|
+ cardFileQuery.value.append('image', cardUploadRow.value)
|
|
|
+ message.warning('正在解析...')
|
|
|
+
|
|
|
+ const res = await talentLabelingApi.businessCardParse(cardFileQuery.value)
|
|
|
+ formData.value = res?.data || res
|
|
|
+ message.success('名片解析成功')
|
|
|
+ }
|
|
|
+ // else if (type === 'web') {}
|
|
|
+
|
|
|
+ if (FormPageRef.value?.setFormData) FormPageRef.value.setFormData(formData.value)
|
|
|
+ } catch (error) {
|
|
|
+ console.log('解析失败', error)
|
|
|
+ cardFileQuery.value = null
|
|
|
+ } finally {
|
|
|
+ analysisLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ height.value = document.documentElement.clientHeight + 'px'
|
|
|
+ if (id && id !== 'add') {
|
|
|
+ // 编辑
|
|
|
+ isEdit.value = true
|
|
|
+ const item = route?.query?.item ? JSON.parse(route.query.item) : null
|
|
|
+ if (item) {
|
|
|
+ handleEdit(item)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 新增
|
|
|
+ const data = route?.query?.item ? JSON.parse(route.query.data) : null
|
|
|
+ // 处理传递的数据
|
|
|
+ if (route?.query?.type === 'file') {
|
|
|
+ fileUrl.value = route?.query?.fileUrl ? decodeURIComponent(route.query.fileUrl) : null
|
|
|
+ //
|
|
|
+ resumeAnalysisToForm(data) // 简历解析
|
|
|
+ }
|
|
|
+ if (route?.query?.type === 'card') {
|
|
|
+ cardImgUrl.value = route?.query?.cardImgUrl ? decodeURIComponent(route.query.cardImgUrl) : null
|
|
|
+ formData.value = data
|
|
|
+ if (FormPageRef.value?.setFormData) FormPageRef.value.setFormData(formData.value)
|
|
|
+ }
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+</script>
|
|
|
+<style lang="scss" scoped>
|
|
|
+:deep(.drawer) {
|
|
|
+ position: absolute;
|
|
|
+}
|
|
|
+</style>
|