123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- <template>
- <Dialog title="人才标注" v-model="dialogVisible" class="!w-90%">
- <el-row :gutter="20">
- <el-col :span="9">
- <div v-if="sourceList && sourceList.length > 0" class="!h-80vh overflow-y-auto">
- <el-card v-for="val in sourceList" :key="val.id" class="mb-10px">
- <template #header>
- <CardTitle :title="typeObj[val.task_type] + val.source_date" />
- </template>
- <el-image
- v-if="['名片', '杂项'].includes(val.task_type)"
- width="100%"
- :preview-src-list="[val.minio_path]"
- class="cursor-pointer"
- :src="val.minio_path"
- />
- <!-- 门墩儿招聘 -->
- <template v-if="val.task_type === '招聘'">
- <el-tabs v-model="activeName" type="border-card">
- <el-tab-pane label="基本信息" name="info">
- <Info :id="personId" :user-id="userId" />
- <expExtend :user-id="userId" defaultShowAll class="m-t-20px" />
- </el-tab-pane>
- <el-tab-pane label="附件简历" name="Attachment">
- <Attachment showPreview :user-id="userId" />
- </el-tab-pane>
- </el-tabs>
- </template>
- <template v-if="val.task_type === '简历'">
- <IFrame :src="val.minio_path" />
- </template>
- <template v-if="val.task_type === '新任命'">
- <iframe
- :id="val.id"
- class="markdownContent"
- src=""
- frameborder="0"
- ></iframe>
- </template>
- </el-card>
- </div>
- <el-card v-else>
- <el-empty description="暂无来源" />
- </el-card>
- </el-col>
- <el-col :span="15">
- <div class="!h-100% overflow-y-auto">
- <el-tabs type="border-card">
- <el-tab-pane label="人才信息">
- <FormPage ref="baseInfoRef" formType="edit" :itemData="talentItem" />
- <div class="text-right mt-12px">
- <el-button @click="handleSave" type="primary" :disabled="saveLoading">保 存</el-button>
- <el-button @click="dialogVisible = false">取消</el-button>
- </div>
- </el-tab-pane>
- <el-tab-pane label="人才标签">
- <el-card shadow="never">
- <div class="my-5">
- <div class="mt-4 px-3 pb-3" style="border: 1px dashed #67c23a; border-radius: 4px;">
- <div v-if="talentSelectedTags?.length">
- <el-tag
- v-for="(item, index) in talentSelectedTags"
- :key="index"
- closable
- size="large"
- type="success"
- class="mr-14px mt-14px"
- @close="closeClick(item)"
- >
- {{ item.name }}
- </el-tag>
- </div>
- <div v-else style="color: #777; text-align: center; line-height: 50px; margin-top: 12px;">请添加标签</div>
- </div>
-
- <div :class="{'mt-5': talentSelectedTags?.length > 0}">
- <el-tag
- v-for="(item, index) in tagList" :key="index"
- size="large"
- type="primary"
- class="mr-14px mt-14px"
- :class="{'cursor-pointer': !talentSelectedTags.find(k => k.name === item.name)}"
- :effect="talentSelectedTags.find(k => k.name === item.name) ? 'info' : 'default'"
- @click="handleAdd(item)"
- >
- + {{ item.name }}
- </el-tag>
- </div>
- </div>
- </el-card>
- <div class="text-right mt-12px">
- <el-button @click="submitForm" type="primary" :disabled="loading">保 存</el-button>
- <el-button @click="dialogVisible = false">取消</el-button>
- </div>
- </el-tab-pane>
- </el-tabs>
- </div>
- </el-col>
- </el-row>
- </Dialog>
- </template>
- <script setup>
- import { talentLabelingApi } from '@/api/menduner/system/talentMap/labeling'
- import { talentTagApi } from '@/api/menduner/system/talentMap/tag'
- import { getDict } from '@/hooks/web/useDictionaries'
- import { cloneDeep } from 'lodash-es'
- import { TalentMap } from '@/api/menduner/system/talentMap'
- import FormPage from '@/views/menduner/system/talentMap/components/FormPage.vue'
- import { marked } from 'marked'
- import { generateUUID } from '@/utils'
- import Info from '@/views/menduner/system/person/details/components/info.vue'
- import expExtend from '@/views/menduner/system/person/details/components/expExtend.vue'
- import Attachment from '@/views/menduner/system/person/details/components/attachment.vue'
- const message = useMessage() // 消息弹窗
- const loading = ref(false)
- const dialogVisible = ref(false) // 弹窗的是否展示
- const talentItem = ref({})
- const talentSelectedTags = ref([])
- const tagList = ref([])
- const typeObj = {
- '招聘': '门墩儿招聘',
- '新任命': '门墩儿新任命',
- '名片': '名片',
- '简历': '简历',
- '杂项': '杂项'
- }
- // 获取人才标签
- const getTagList = async () => {
- loading.value = true
- try {
- const data = await talentTagApi.getTalentTagList()
- tagList.value = data || []
- } finally {
- loading.value = false
- }
- }
- // 获取人才标签
- const getTalentTagById = async() => {
- const id = talentItem.value?.id
- if (!id) {
- talentSelectedTags.value = []
- return
- }
- const tagData = await talentLabelingApi.getTalentTagById(id)
- talentSelectedTags.value = tagData ? tagData.map((i) => {
- return { id: i.talent, name: i.tag }
- }) : []
- }
- // markdown回显
- const showPage = (id, html) => {
- // 将 data-src 转化为 src
- html = html.replace(/data-src/g, 'src')
- .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/g, '')
- .replace(/https/g, 'http')
- nextTick(() => {
- const iframe = document.getElementById(id)
- if (!iframe) return
- const doc = iframe.contentDocument || iframe.document
- // 设置 iframe 中请求不发送 referrer,以绕过图片防盗链
- const htmlArr = html.split('</head>')
- const html_src_add = htmlArr[0] + '<meta name="referrer" content="never"></head>' + htmlArr[1]
- doc.open()
- doc.write(html_src_add)
- doc.close()
- // 设置图片宽高
- let iwindow = iframe.contentWindow;
- iframe.addEventListener('load',function () {
- let idoc = iwindow.document;
- let imgs = idoc.getElementsByTagName('img')
- for (let i = 0; i < imgs.length; i++) {
- const img = imgs[i]
- if (img) {
- if (img.width >= img.height) {
- img.width = iframe.clientWidth / 2
- } else {
- img.height = iframe.clientHeight / 2
- let left = (iframe.clientWidth - img.width) / 2
- img.style.marginLeft = left + "px"
- }
- }
- }
- })
- })
- }
- const handleShowPage = async (id, url) => {
- await nextTick()
- const response = await fetch(url)
- const text = await response.text()
- const doc = `
- <!DOCTYPE html>
- <html class="">
- <head></head>
- <body>
- ${marked(text)}
- </body>
- </html>
- `
- showPage(id, doc)
- }
- /** 打开弹窗 */
- const personId = ref('')
- const userId = ref('')
- const activeName = ref('info')
- const sourceList = ref([])
- const open = async (data) => {
- sourceList.value = []
- personId.value = null
- userId.value = null
- dialogVisible.value = true
- talentItem.value = data
- if (data?.origin_source) {
- const list = typeof data.origin_source === 'string' ? JSON.parse(data.origin_source) : data.origin_source
- // 时间排序
- const sortedList = list.sort((a, b) => new Date(b.source_date) - new Date(a.source_date))
- sourceList.value = sortedList.map(e => {
- const item = { ...e, id: generateUUID() }
- if (e.task_type === '新任命') {
- handleShowPage(item.id, item.minio_path)
- }
- if (e.task_type === '招聘') { // 门墩儿招聘-人员信息在组件中通过id和userId获取
- const obj = typeof e.minio_path === 'string' ? JSON.parse(e.minio_path) : e.minio_path
- personId.value = obj?.id || ''
- userId.value = obj?.userId || ''
- activeName.value = 'info'
- }
-
- return item
- })
- }
- // 获取所有人才标签
- await getTagList()
- // 获取人才标签
- await getTalentTagById()
- }
- defineExpose({ open }) // 提供 open 方法,用于打开弹窗
- // 标签删除
- const closeClick = (item) => {
- const index = talentSelectedTags.value.findIndex((i) => i === item)
- if (index !== -1) talentSelectedTags.value.splice(index, 1)
- }
- // 标签添加
- const handleAdd = (item) => {
- talentSelectedTags.value.push(item)
- }
- /** 人才标签保存 */
- const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
- const submitForm = async () => {
- if (!talentSelectedTags.value || !talentSelectedTags.value.length) return message.warning('请选择要更新的人才标签')
- loading.value = true
- const tags = talentSelectedTags.value.map(e => {
- return { talent: talentItem.value.id, tag: e.name }
- })
- // 提交请求
- try {
- await talentLabelingApi.updateTalentTags(tags)
- message.success('人才标签更新成功')
- dialogVisible.value = false
- // 发送操作成功的事件
- emit('success')
- } finally {
- loading.value = false
- }
- }
- // 保存人才信息
- const baseInfoRef = ref(null)
- const saveLoading = ref(false)
- const handleSave = async () => {
- saveLoading.value = true
- const formQuery = baseInfoRef.value.formQuery
- if (!formQuery || !Object.keys(formQuery).length) return saveLoading.value = true
- // 数组转为字符串保存
- if (Array.isArray(formQuery?.mobile)) {
- formQuery.mobile = formQuery.mobile.filter(i => Boolean(i)).map(j => String(j).replace(/,|,/g, '')).join(',');
- }
- const params = {
- ...formQuery,
- origin_source: talentItem.value?.origin_source,
- image_path: talentItem.value?.image_path
- }
- try {
- await talentLabelingApi.updateBusinessCard(params, talentItem.value.id)
- dialogVisible.value = false
- message.success('保存成功')
- emit('success')
- } finally {
- saveLoading.value = false
- }
- }
- </script>
- <style scoped lang="scss">
- .base-info {
- background-color: #f7f8fa;
- border-radius: 6px;
- padding: 15px;
- }
- :deep {
- .el-descriptions__content {
- color: #303133;
- }
- .el-descriptions__body {
- background-color: #f7f8fa;
- }
- .markdownContent {
- width: 100%;
- max-width: 100%;
- height: 600px;
- border: none;
- overflow: hidden;
- }
- }
- </style>
|