|
@@ -1,13 +1,6 @@
|
|
|
<template>
|
|
|
<div>
|
|
|
- <v-card elevation="5" class="pa-10">
|
|
|
- <TextInput
|
|
|
- v-model="content"
|
|
|
- :item="textItem"
|
|
|
- @enter="handleSearch"
|
|
|
- @appendInnerClick="handleSearch"
|
|
|
- ></TextInput>
|
|
|
- </v-card>
|
|
|
+ <CtFilter :items="filterItems" @reset="handleReset" @search="handleSearch" />
|
|
|
|
|
|
<v-card elevation="5" class="mt-3">
|
|
|
<CtTable
|
|
@@ -18,74 +11,73 @@
|
|
|
:disable-sort="true"
|
|
|
:elevation="0"
|
|
|
:isTools="false"
|
|
|
- :noDataText="!content ? '请先输入您的描述信息定位人才' : '暂无数据'"
|
|
|
:items-per-page="-1"
|
|
|
- height="calc(100vh - 400px)"
|
|
|
+ height="calc(100vh - 150px)"
|
|
|
:showFixedLastItem="true"
|
|
|
itemKey="id"
|
|
|
>
|
|
|
+ <template #status="{ item }">
|
|
|
+ <v-chip size="small" variant="elevated" :color="item.status === 'active' ? 'primary' : 'error'">{{ item.status === 'active' ? '已启用' : '已禁用' }}</v-chip>
|
|
|
+ </template>
|
|
|
<template #actions="{ item }">
|
|
|
<v-btn variant="text" color="primary" @click.stop="handleAnnotation(item)">标注</v-btn>
|
|
|
- <v-btn variant="text" color="error" @click.stop="handleDisabled(item)">禁用</v-btn>
|
|
|
- <v-btn variant="text" color="#008970" @click.stop="handleEnable(item)">启用</v-btn>
|
|
|
- <v-btn variant="text" color="error" @click.stop="handleDelete(item)">删除</v-btn>
|
|
|
+ <v-btn v-if="item.status === 'active'" variant="text" color="warning" @click.stop="handleAction(item.id, 'inactive')">禁用</v-btn>
|
|
|
+ <v-btn v-if="item.status === 'inactive'" variant="text" color="#007cd6" @click.stop="handleAction(item.id, 'active')">启用</v-btn>
|
|
|
+ <!-- <v-btn variant="text" color="error" @click.stop="handleDelete(item)">删除</v-btn> -->
|
|
|
</template>
|
|
|
</CtTable>
|
|
|
</v-card>
|
|
|
|
|
|
<!-- 标注 -->
|
|
|
- <CtDialog :visible="showAnnotationDialog" title="人才标注" :footer="false" widthType="1" @close="showAnnotationDialog = false">
|
|
|
+ <CtDialog :visible="showDialog" title="人才标注" :footer="false" widthType="1" @close="showDialog = false">
|
|
|
<v-row>
|
|
|
<v-col cols="4">
|
|
|
<v-card elevation="3" class="pa-3" style="height: 100%">
|
|
|
- <p>名片</p>
|
|
|
- <p>门墩儿新任命</p>
|
|
|
+ <p :class="{'active': previewUrl}" @click="handlePreview">
|
|
|
+ 名片
|
|
|
+ <v-icon v-if="previewUrl">mdi-magnify-expand</v-icon>
|
|
|
+ </p>
|
|
|
+ <img v-if="previewUrl" width="100%" :src="previewUrl" />
|
|
|
+ <p class="mb-3" :class="{'mt-3': !previewUrl}">门墩儿新任命</p>
|
|
|
<p>门墩儿用户简历</p>
|
|
|
</v-card>
|
|
|
</v-col>
|
|
|
<v-col cols="8">
|
|
|
- <v-card elevation="3" class="pa-3">
|
|
|
+ <v-card elevation="3" class="pa-3" height="100%">
|
|
|
<div class="base-info pa-3">
|
|
|
+ <div class="mb-6">
|
|
|
+ {{ talentItem.name_zh }}
|
|
|
+ <span v-if="talentItem.name_en">({{ talentItem.name_en }})</span>
|
|
|
+ </div>
|
|
|
<div class="d-flex align-center">
|
|
|
- <div v-for="(val, index) in talentInfoKeys" :key="index" class="common-width">
|
|
|
+ <div v-for="(val, index) in talentInfoKeys" :key="index" class="common-width info-item">
|
|
|
<p v-ellipse-tooltip>{{ talentItem[val.key] }}</p>
|
|
|
<p v-ellipse-tooltip>{{ talentItem[val.value] }}</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="d-flex align-center my-6">
|
|
|
- <div class="common-width">{{ talentItem.phone }}</div>
|
|
|
- <div class="common-width">{{ talentItem.email }}</div>
|
|
|
+ <div class="common-width pr-3" v-ellipse-tooltip>{{ talentItem.mobile }}</div>
|
|
|
+ <div style="flex: 1;">{{ talentItem.email }}</div>
|
|
|
</div>
|
|
|
- <div>{{ talentItem.address }}</div>
|
|
|
+ <div>{{ talentItem.address_zh }}</div>
|
|
|
</div>
|
|
|
<div class="my-5">
|
|
|
- <p>标签标注</p>
|
|
|
- <v-chip
|
|
|
- v-for="(item, index) in talentItem.automaticDimensionTags" :key="index"
|
|
|
- class="chip mx-2 mt-4"
|
|
|
- label color="primary"
|
|
|
- >
|
|
|
- {{ item }}
|
|
|
- <v-icon size="18" color="primary" style="margin-left: 6px;" @click="closeClick(index, 'automaticDimensionTags')">mdi-close-circle</v-icon>
|
|
|
- </v-chip>
|
|
|
- </div>
|
|
|
- <div>
|
|
|
<p>人才标签</p>
|
|
|
<v-chip
|
|
|
- v-for="(item, index) in talentItem.manualAnnotationTags" :key="index"
|
|
|
+ v-for="(item, index) in talentSelectedTags" :key="index"
|
|
|
class="chip mx-2 mt-4 cursor-pointer"
|
|
|
label color="primary"
|
|
|
>
|
|
|
{{ item.name }}
|
|
|
- <v-icon size="18" color="primary" style="margin-left: 6px;" @click="closeClick(item, 'manualAnnotationTags')">mdi-close-circle</v-icon>
|
|
|
+ <v-icon size="18" color="primary" style="margin-left: 6px;" @click="closeClick(item)">mdi-close-circle</v-icon>
|
|
|
</v-chip>
|
|
|
|
|
|
- <div class="mt-5">
|
|
|
+ <div :class="{'mt-5': talentSelectedTags?.length > 0}">
|
|
|
<v-chip
|
|
|
v-for="(item, index) in tagList" :key="index"
|
|
|
class="chip mx-2 mt-4 cursor-pointer"
|
|
|
- label color="primary"
|
|
|
- :disabled="talentItem.manualAnnotationTags.includes(item)"
|
|
|
+ label color="#248dbb"
|
|
|
+ :disabled="talentSelectedTags.includes(item)"
|
|
|
@click="handleAdd(item)"
|
|
|
>
|
|
|
<v-icon icon="mdi-plus" start></v-icon>
|
|
@@ -93,10 +85,10 @@
|
|
|
</v-chip>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <v-divider class="mt-5"></v-divider>
|
|
|
+ <v-divider></v-divider>
|
|
|
<div class="d-flex justify-space-evenly my-5">
|
|
|
<v-btn width="120" color="warning" elevation="5" @click="handleClose">取 消</v-btn>
|
|
|
- <v-btn width="120" color="primary" elevation="5" @click="handleSave">保 存</v-btn>
|
|
|
+ <v-btn width="120" color="primary" elevation="5" @click="handleUpdate">更 新</v-btn>
|
|
|
</div>
|
|
|
</v-card>
|
|
|
</v-col>
|
|
@@ -105,12 +97,14 @@
|
|
|
</div>
|
|
|
|
|
|
<Loading :visible="annotationLoading"></Loading>
|
|
|
+
|
|
|
+ <PreviewImage v-if="showPreview" :initialIndex="0" :urlList="[previewUrl]" @close="showPreview = !showPreview" />
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
defineOptions({ name: 'NewTalentMapAnnotation' })
|
|
|
import { ref } from 'vue'
|
|
|
-import { getLabelingList, getCardList } from '@/api/recruit/enterprise/talentMap/labeling'
|
|
|
+import { getCardList, updateTalentStatus, getTalentCardByImagePath, getTalentTagById, updateTalentTags } from '@/api/recruit/enterprise/talentMap/labeling'
|
|
|
import { getTalentTagList } from '@/api/recruit/enterprise/talentMap/tag'
|
|
|
import Snackbar from '@/plugins/snackbar'
|
|
|
import Confirm from '@/plugins/confirm'
|
|
@@ -118,45 +112,54 @@ import { useI18n } from '@/hooks/web/useI18n'
|
|
|
|
|
|
const { t } = useI18n()
|
|
|
const loading = ref(false)
|
|
|
-const showAnnotationDialog = ref(false)
|
|
|
-const content = ref('查找所有在上海的五星级酒店担任总经理职位的人才')
|
|
|
-const items = ref([
|
|
|
- {
|
|
|
- "姓名": "张三",
|
|
|
- "邮箱": "zhang@example.com",
|
|
|
- "职位": "总经理",
|
|
|
- "酒店名称": "上海四季酒店"
|
|
|
- },
|
|
|
- {
|
|
|
- "姓名": "李四",
|
|
|
- "邮箱": "li@example.com",
|
|
|
- "职位": "执行总经理",
|
|
|
- "酒店名称": "上海浦东丽思卡尔顿酒店"
|
|
|
- }
|
|
|
-])
|
|
|
+const showDialog = ref(false)
|
|
|
+const items = ref([])
|
|
|
const headers = [
|
|
|
- { title: '姓名', key: '姓名', sortable: false },
|
|
|
- { title: '酒店名称', key: '酒店名称', sortable: false },
|
|
|
- { title: '职位', key: '职位', sortable: false },
|
|
|
- { title: '邮箱', key: '邮箱', sortable: false },
|
|
|
+ { title: '姓名', key: 'name_zh', sortable: false },
|
|
|
+ // { title: '英文名', key: 'name_en', sortable: false },
|
|
|
+ { title: '职位', key: 'title_zh', sortable: false },
|
|
|
+ { title: '酒店', key: 'hotel_zh', sortable: false },
|
|
|
+ { title: '人才状态', key: 'status', sortable: false },
|
|
|
+ { title: '创建时间', key: 'created_at', sortable: false },
|
|
|
{ title: '操作', key: 'actions', sortable: false, align: 'center' }
|
|
|
]
|
|
|
-const textItem = ref({
|
|
|
- type: 'text',
|
|
|
- value: '',
|
|
|
- label: '请输入您的描述信息定位人才',
|
|
|
- placeholder: '回车开始人才匹配',
|
|
|
- clearable: true,
|
|
|
- hideDetails: true,
|
|
|
- appendInnerIcon: 'mdi-magnify'
|
|
|
+const filterItems = ref({
|
|
|
+ options: [
|
|
|
+ {
|
|
|
+ type: 'text',
|
|
|
+ key: 'title_zh',
|
|
|
+ value: '',
|
|
|
+ label: '职位',
|
|
|
+ clearable: true,
|
|
|
+ hideDetails: true,
|
|
|
+ width: 200
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'text',
|
|
|
+ key: 'hotel_zh',
|
|
|
+ value: '',
|
|
|
+ label: '酒店',
|
|
|
+ clearable: true,
|
|
|
+ hideDetails: true,
|
|
|
+ width: 200
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'text',
|
|
|
+ key: 'name_zh',
|
|
|
+ value: '',
|
|
|
+ label: '中文名',
|
|
|
+ clearable: true,
|
|
|
+ hideDetails: true,
|
|
|
+ width: 200
|
|
|
+ }
|
|
|
+ ]
|
|
|
})
|
|
|
|
|
|
+// 获取名片列表
|
|
|
const getList = async () => {
|
|
|
loading.value = true
|
|
|
try {
|
|
|
- const data = await getLabelingList({ query_requirement: content.value })
|
|
|
- const card = await getCardList()
|
|
|
- console.log(data, '列表========', card)
|
|
|
+ const data = await getCardList()
|
|
|
items.value = data || []
|
|
|
} finally {
|
|
|
loading.value = false
|
|
@@ -164,35 +167,25 @@ const getList = async () => {
|
|
|
}
|
|
|
getList()
|
|
|
|
|
|
-
|
|
|
// 搜索
|
|
|
-const handleSearch = async () => {
|
|
|
- console.log('搜索', content.value)
|
|
|
- if (!content.value) return Snackbar.warning('请输入您的描述信息定位人才')
|
|
|
- getList()
|
|
|
+const handleSearch = async (obj) => {
|
|
|
+ console.log(obj, '搜索')
|
|
|
+ // query.value = obj
|
|
|
+ // getList()
|
|
|
+}
|
|
|
+// 重置
|
|
|
+const handleReset = (obj) => {
|
|
|
+ console.log(obj, '重置')
|
|
|
+ // query.value = obj
|
|
|
+ // getList()
|
|
|
}
|
|
|
|
|
|
// 标注
|
|
|
-const talentItem = ref({
|
|
|
- name: '张三',
|
|
|
- enName: 'Tom',
|
|
|
- postName: '总经理',
|
|
|
- enPostName: 'General Manager',
|
|
|
- inaugurationHotel: '南京洲际酒店',
|
|
|
- enInaugurationHotel: 'Nanjing InterContinental Hotel',
|
|
|
- groupName: '洲际集团',
|
|
|
- enGroupName: 'IHG',
|
|
|
- phone: '13800138000',
|
|
|
- email: 'zhangsan@163.com',
|
|
|
- address: '南京市人民路123号',
|
|
|
- automaticDimensionTags: ['富强', '民主', '文明', '和谐'],
|
|
|
- manualAnnotationTags: [],
|
|
|
-})
|
|
|
+const talentItem = ref({})
|
|
|
const talentInfoKeys = [
|
|
|
- { key: 'name', value: 'enName' },
|
|
|
- { key: 'postName', value: 'enPostName' },
|
|
|
- { key: 'inaugurationHotel', value: 'enInaugurationHotel' },
|
|
|
- { key: 'groupName', value: 'enGroupName' },
|
|
|
+ { key: 'title_zh', value: 'title_en' },
|
|
|
+ { key: 'hotel_zh', value: 'hotel_en' },
|
|
|
+ { key: 'brand_zh', value: 'brand_en' },
|
|
|
]
|
|
|
|
|
|
// 获取人才标签
|
|
@@ -207,59 +200,87 @@ const getTagList = async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// 名片预览
|
|
|
+const showPreview = ref(false)
|
|
|
+const handlePreview = () => {
|
|
|
+ showPreview.value = true
|
|
|
+}
|
|
|
+
|
|
|
// 标注
|
|
|
+const talentSelectedTags = ref([])
|
|
|
+const previewUrl = ref(null)
|
|
|
const annotationLoading = ref(false)
|
|
|
const handleAnnotation = async (item) => {
|
|
|
+ if (!item || !item.id) return
|
|
|
+
|
|
|
+ talentItem.value = item
|
|
|
+ // 获取所有标签列表
|
|
|
await getTagList()
|
|
|
- showAnnotationDialog.value = true
|
|
|
-}
|
|
|
|
|
|
-// 删除
|
|
|
-const closeClick = (index, dataKey) => {
|
|
|
- if (dataKey === 'manualAnnotationTags') {
|
|
|
- const dataIndex = talentItem.value[dataKey].findIndex((i) => i === index)
|
|
|
- if (dataIndex !== -1) index = dataIndex
|
|
|
+ // 获取名片预览
|
|
|
+ if (item.image_path) {
|
|
|
+ const data = await getTalentCardByImagePath(item.image_path)
|
|
|
+ previewUrl.value = URL.createObjectURL(data)
|
|
|
}
|
|
|
- talentItem.value[dataKey].splice(index, 1)
|
|
|
+
|
|
|
+ // 获取人才标签
|
|
|
+ const tagData = await getTalentTagById(item.id)
|
|
|
+ console.log(tagData, '人才标签')
|
|
|
+
|
|
|
+ showDialog.value = true
|
|
|
}
|
|
|
|
|
|
-// 手动标注添加
|
|
|
-const handleAdd = (item) => {
|
|
|
- talentItem.value.manualAnnotationTags.push(item)
|
|
|
+// 标签删除
|
|
|
+const closeClick = (item) => {
|
|
|
+ const index = talentSelectedTags.value.findIndex((i) => i === item)
|
|
|
+ if (index !== -1) talentSelectedTags.value.splice(index, 1)
|
|
|
}
|
|
|
|
|
|
-// 禁用
|
|
|
-const handleDisabled = (item) => {
|
|
|
- console.log(item, '禁用')
|
|
|
- Confirm(t('common.confirmTitle'), '是否确定禁用此人才数据?').then(async () => {
|
|
|
- Snackbar.success(t('common.operationSuccessful'))
|
|
|
- })
|
|
|
+// 标签添加
|
|
|
+const handleAdd = (item) => {
|
|
|
+ talentSelectedTags.value.push(item)
|
|
|
}
|
|
|
|
|
|
-// 启用
|
|
|
-const handleEnable = (item) => {
|
|
|
- console.log(item, '启用')
|
|
|
- Confirm(t('common.confirmTitle'), '是否确定启用此人才数据?').then(async () => {
|
|
|
- Snackbar.success(t('common.operationSuccessful'))
|
|
|
+// 启用、禁用
|
|
|
+const handleAction = (id, status) => {
|
|
|
+ Confirm(t('common.confirmTitle'), `是否确定${status === 'active' ? '启用' : '禁用'}?`).then(async () => {
|
|
|
+ try {
|
|
|
+ await updateTalentStatus(id, { status })
|
|
|
+ Snackbar.success(t('common.operationSuccessful'))
|
|
|
+ getList()
|
|
|
+ } catch {}
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// 删除
|
|
|
-const handleDelete = (item) => {
|
|
|
- console.log(item, '删除')
|
|
|
- Confirm(t('common.confirmTitle'), '是否确定删除?').then(async () => {
|
|
|
- // await deleteInvoiceTitle(id)
|
|
|
- Snackbar.success(t('common.delMsg'))
|
|
|
- // getList()
|
|
|
- })
|
|
|
-}
|
|
|
+// const handleDelete = (item) => {
|
|
|
+// console.log(item, '删除')
|
|
|
+// Confirm(t('common.confirmTitle'), '是否确定删除?').then(async () => {
|
|
|
+// // await deleteInvoiceTitle(id)
|
|
|
+// Snackbar.success(t('common.delMsg'))
|
|
|
+// // getList()
|
|
|
+// })
|
|
|
+// }
|
|
|
|
|
|
+// 更新人才标签
|
|
|
const handleClose = () => {
|
|
|
- showAnnotationDialog.value = false
|
|
|
+ showDialog.value = false
|
|
|
+ talentItem.value = {}
|
|
|
+ talentSelectedTags.value = []
|
|
|
+ previewUrl.value = ''
|
|
|
}
|
|
|
|
|
|
-const handleSave = async () => {
|
|
|
- handleClose()
|
|
|
+const handleUpdate = async () => {
|
|
|
+ const tags = talentSelectedTags.value.map(e => {
|
|
|
+ return { talent: e.id, tag: e.name }
|
|
|
+ })
|
|
|
+
|
|
|
+ try {
|
|
|
+ await updateTalentTags(tags)
|
|
|
+ Snackbar.success('人才标签更新成功')
|
|
|
+ handleClose()
|
|
|
+ getList()
|
|
|
+ } catch {}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
@@ -269,7 +290,20 @@ const handleSave = async () => {
|
|
|
border-radius: 6px;
|
|
|
}
|
|
|
.common-width {
|
|
|
- width: 25%;
|
|
|
- max-width: 25%;
|
|
|
+ width: 33.3%;
|
|
|
+ max-width: 33.3%;
|
|
|
+}
|
|
|
+.info-item {
|
|
|
+ padding-right: 12px;
|
|
|
+ &:nth-child(3n) {
|
|
|
+ padding-right: 0;
|
|
|
+ }
|
|
|
+ p {
|
|
|
+ height: 24px;
|
|
|
+ }
|
|
|
+}
|
|
|
+.active {
|
|
|
+ color: var(--v-primary-base);
|
|
|
+ cursor: pointer;
|
|
|
}
|
|
|
</style>
|