123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987 |
- <template>
- <div class="message" :class="{'default-width': !isEnterprise, 'py-3': !isEnterprise}" :style="`height: calc(100vh - ${isEnterprise ? '130px' : '50px'});`">
- <div class="message-left d-flex flex-column">
- <div class="message-left-search d-flex align-center px-3 justify-space-between" >
- <div>
- <v-icon class="mr-3">mdi-history</v-icon>
- 最近联系人
- </div>
- <div>
- <v-btn
- density="compact"
- :color="showDelete ? '' : 'red'"
- :icon="showDelete ? 'mdi-close' : 'mdi-trash-can-outline'"
- variant="text"
- @click="showDelete = !showDelete"
- >
- </v-btn>
- </div>
- <!-- {{ connected ? '连接成功': '连接失败' }} -->
- </div>
- <div class="message-chat-box mt-5">
- <v-overlay
- :model-value="!IM.connected"
- contained
- class="align-center justify-center"
- >
- <v-progress-circular
- color="primary"
- size="64"
- indeterminate
- ></v-progress-circular>
- </v-overlay>
- <div v-if="conversationList.length">
- <v-list density="compact" mandatory @update:selected="handleChange">
- <v-list-item
- v-for="(val, i) in conversationList"
- :key="i"
- :value="val"
- color="primary"
- class="mb-2"
- :active="val.channel.channelID === info?.channel?.channelID"
- :title="val.userInfoVo ? (val.userInfoVo.userInfoResp?.name ? val.userInfoVo.userInfoResp.name : val.userInfoVo.userInfoResp?.phone) : '系统消息'"
- :subtitle="timesTampChange(+val.timestamp.padEnd(13, '0'))"
- >
- <template v-slot:title="{ title }">
- <div v-if="!isEnterprise" class="mt-2 d-flex align-center">
- {{ title }}
- <div class="ml-3 color-666 font-size-14 enterprise-name ellipsis" v-ellipse-tooltip :style="{'color': val.channel.channelID === info?.channel?.channelID ? '#00B760' : '#666'}">
- {{ formatName(val.userInfoVo?.userInfoResp?.enterpriseAnotherName) }}
- <span class="line" v-if="val.userInfoVo?.userInfoResp?.postNameCn && val.userInfoVo?.userInfoResp?.enterpriseAnotherName"></span>
- {{ val.userInfoVo?.userInfoResp?.postNameCn }}
- </div>
- </div>
- <div v-else class="mt-2" v-ellipse-tooltip>{{ title }}</div>
- </template>
- <template v-slot:subtitle="{ subtitle }">
- <div class="mt-2">{{ subtitle }}</div>
- </template>
- <template v-slot:prepend>
- <v-avatar :image="getUserAvatar(val?.userInfoVo?.userInfoResp?.avatar, val?.userInfoVo?.userInfoResp?.sex, !val.userInfoVo ? true : false)"></v-avatar>
- </template>
- <template v-slot:append>
- <v-badge
- v-if="val.unread > 0"
- color="error"
- :content="val.unread"
- inline
- ></v-badge>
- <v-btn v-show="showDelete" density="compact" icon="mdi-trash-can-outline" variant="text" color="red" @click.stop="handleDelete(val)"></v-btn>
- </template>
- </v-list-item>
- </v-list>
- <div class="message-no-more-text mt-3">没有更多了</div>
- </div>
- <div v-else class="left-noData">
- <Empty :elevation="false" message="暂无30天内联系人" width="300" height="150"></Empty>
- </div>
- </div>
- </div>
- <div class="message-right">
- <div v-if="showRightNoData" class="right-noData">
- <Empty :elevation="false" message="与您进行过沟通的 Boss 都会在左侧列表中显示"></Empty>
- </div>
- <Chatting
- ref="chatRef"
- :items="messageItems"
- :info="info"
- :interview="interview"
- :has-more="hasMore"
- :updateConversation="updateConversation"
- :updateUnreadCount="updateUnreadCount"
- :resetUnread="resetUnread"
- @handleSend="handleUpdate"
- @handleMore="handleGetMore"
- @handleAgree="handleAgree"
- @handleRefuse="handleRefuse"
- @handlePreview="handlePreview"
- @handleSendResume="handleSendResume"
- @handleBack="handleBack"
- >
- <template #tools>
- <v-btn
- v-for="tool in tools"
- :key="tool.name"
- size="small"
- class="mr-3"
- :disabled="tool.disabled"
- :color="tool.color"
- @click="tool.handle(tool)"
- >
- <v-progress-circular
- v-if="tool.loading"
- :width="2"
- :size="16"
- color="white"
- class="mr-2"
- indeterminate
- ></v-progress-circular>
- <v-icon v-else class="mr-2">{{ tool.icon }}</v-icon>
- {{ tool.disabled ? tool.disabledText : tool.name }}
- </v-btn>
- </template>
- </Chatting>
- </div>
- </div>
- <!-- 附件上传 -->
- <CtDialog
- :visible="showUploadDialog"
- :widthType="2"
- :footer="true"
- title="附件简历上传"
- titleClass="text-h6"
- @close="showUploadDialog = false"
- @submit="handleSubmitAttachment"
- >
- <div class="color-warning mb-3" style="font-size: 13px;">* 仅支持.doc, .docx, .pdf文件且大小不能超过20MB</div>
- <CtForm ref="CtFormRef" :items="formItems">
- <template #uploadFile="{ item }">
- <TextInput v-model="item.value" :item="item" @click="openFileInput"></TextInput>
- <File ref="uploadFile" @success="handleUploadResume"></File>
- </template>
- </CtForm>
- <!-- 学生-实习到岗信息 -->
- <studentDeliveryForm v-if="baseInfo?.type && Number(baseInfo.type) === 1" ref="studentDeliveryFormRef" />
- </CtDialog>
- <!-- 面试邀请 -->
- <CtDialog :visible="showInvite" :widthType="4" titleClass="text-h6" title="邀请面试" @close="showInvite = false" @submit="handleSubmit">
- <InvitePage v-if="showInvite" ref="inviteRef" :item-data="itemData" :position="positionList"></InvitePage>
- </CtDialog>
- <TipDialog :visible="showTip" icon="mdi-check-circle-outline" message="面试邀请发送成功" @close="showTip = false">
- <div class="color-primary text-decoration-underline cursor-pointer" @click="handleToInterviewManagement">点击到面试中查看。</div>
- </TipDialog>
- <!-- 求简历-选择求简历的职位 -->
- <CtDialog :visible="showSelectPosition" :widthType="2" titleClass="text-h6" title="选择要求简历的职位" @close="showSelectPosition = false" @submit="handleRequestResumeSubmit">
- <CtForm v-if="showSelectPosition" ref="requestFromRef" :items="requestFormItems"></CtForm>
- </CtDialog>
- <!-- 发送简历-选择要发送的职位 -->
- <CtDialog :visible="openPositionSelectDialog" :widthType="2" titleClass="text-h6" title="请选择要投递的职位" @close="openPositionSelectDialog = false" @submit="selectPositionSubmit">
- <div style="position: relative; min-height: 200px">
- <v-radio-group v-model="selectJobId">
- <div v-for="val in entPositionList" :key="val.value" class="d-flex align-center radioBox" >
- <v-radio :label="val.label" :value="val.value" color="primary"></v-radio>
- <span class="defaultLink mx-3" style="font-size: 14px;" @click.stop="positionDetail(val)">预览</span>
- </div>
- </v-radio-group>
- </div>
- <v-btn
- v-if="entPositionTotal > 5"
- variant="text"
- color="primary"
- @click="changePositionData"
- >
- {{ entPositionListLastData ? '没有更多职位了~ 再选一遍' : '换一批'}} <v-icon size="16">mdi-refresh</v-icon>
- </v-btn>
- </CtDialog>
- <!-- 选择附件简历投递 -->
- <CtDialog :visible="showResume" :widthType="2" titleClass="text-h6" title="发送简历" @close="showResume = false; selectResume = null; enRequestPositionInfo = {}" @submit="handleSubmitResume">
- <div style="position: relative;">
- <v-radio-group v-model="selectResume">
- <div v-for="val in resumeList" :key="val.id" class="d-flex align-center radioBox">
- <v-radio :label="val.title" :value="val.id" color="primary"></v-radio>
- <span class="defaultLink mx-3" style="font-size: 14px;" @click.stop="previewFile(val.url)">预览</span>
- </div>
- </v-radio-group>
- </div>
- <studentDeliveryForm v-if="isStudent" ref="studentDeliveryFormRef" />
- </CtDialog>
- <Loading :visible="pageLoading" />
- </template>
- <script setup>
- defineOptions({ name: 'personal-message-index'})
- import InvitePage from '@/views/recruit/enterprise/interviewManagement/components/invite'
- import { ref, inject, watch, nextTick, computed } from 'vue'
- import { useRoute } from 'vue-router'
- import Chatting from './components/chatting.vue'
- import { initConnect, send, initChart, getMoreMessages, checkConversation } from '@/hooks/web/useIM'
- import { useI18n } from '@/hooks/web/useI18n'
- import { getPositionDetails, jobCvRelCheckSend, jobCvRelSend, jobCvRelHireSend, getJobAdvertisedSearch } from '@/api/position'
- import { getInterviewInviteListByInviteUserId, getMessageType } from '@/api/common'
- // import { getUserInfo } from '@/api/personal/user'
- import { getBaseInfo } from '@/api/common'
- import { getJobAdvertised } from '@/api/enterprise'
- import { saveInterviewInvite } from '@/api/recruit/enterprise/interview'
- import { savePersonResumeCv } from '@/api/recruit/personal/resume'
- import { userInterviewInviteReject, userInterviewInviteConsent } from '@/api/recruit/personal/personalCenter'
- import { getPersonResumeCv } from '@/api/recruit/personal/resume'
- import { formatName } from '@/utils/getText'
- import { useIMStore } from '@/store/im'
- import { useUserStore } from '@/store/user'
- import Snackbar from '@/plugins/snackbar'
- import Confirm from '@/plugins/confirm'
- import { getUserAvatar } from '@/utils/avatar'
- import { dealDictArrayData } from '@/utils/position'
- import { previewFile } from '@/utils'
- import { timesTampChange } from '@/utils/date'
- import { useRouter } from 'vue-router'
- import studentDeliveryForm from '@/views/recruit/personal/components/studentDeliveryForm.vue'
- import { getIsEnterprise } from '@/utils/auth'
- const { t } = useI18n()
- const chatRef = ref()
- const IM = useIMStore()
- // 自己的信息
- const { entBaseInfo } = useUserStore()
- const isEnterprise = inject('isEnterprise')
- // 实例
- const route = useRoute()
- const channelItem = ref(null)
- const messageItems = ref([])
- const pageSize = ref(1)
- const hasMore = ref(false)
- const studentDeliveryFormRef = ref()
- const baseInfo = ref(localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {})
- const isStudent = ref(baseInfo.value?.type && Number(baseInfo.value.type) === 1)
- const positionList = ref([])
- const showTip = ref(false)
- const showInvite = ref(false)
- // 企业-求简历
- const showSelectPosition = ref(false)
- const requestFromRef = ref()
- const requestFormItems = ref({
- options: [
- {
- type: 'autocomplete',
- key: 'jobId',
- value: null,
- label: '招聘职位 *',
- outlined: true,
- clearable: false,
- itemText: 'label',
- itemValue: 'value',
- rules: [v => !!v || '请选择招聘职位'],
- items: positionList
- }
- ]
- })
- const showDelete = ref(false)
- const itemData = ref({})
- const inviteRef = ref()
- // 发送简历
- const showResume = ref(false)
- const resumeList = ref([])
- const selectResume = ref(null)
- // 众聘 介绍人个人id
- const isEmployment = ref('-1')
- // 上传附件简历
- const CtFormRef = ref()
- const formItems = ref({
- options: [
- {
- type: 'text',
- key: 'title',
- value: '',
- label: '附件简历名称 *',
- rules: [v => !!v || '请输入附件简历名称']
- },
- {
- slotName: 'uploadFile',
- key: 'url',
- value: '',
- truthValue: '',
- label: '点击上传附件简历 *',
- outline: true,
- accept: '.doc, .docx, .pdf',
- prependInnerIcon: 'mdi-file-document-outline',
- rules: [v => !!v || '请上传您的附件简历']
- }
- ]
- })
- // 求职者面试列表
- const interview = ref([])
- const showRightNoData = ref(false)
- const info = ref({})
- const enterpriseTools = ref([
- { name: '求简历', key: 'requestResume', icon: 'mdi-email', color:"primary", loading: false, handle: handleInvite, disabled: false, disabledText: '简历已接收' },
- { name: '面试邀约', key: 'interviewInvite', icon: 'mdi-email', color:"primary", loading: false, handle: handleInvite, disabled: false, disabledText: '面试邀约' }
- ])
- const userTools = ref([
- {
- name: '发送简历',
- key: 'sendResume',
- icon: 'mdi-email',
- color:"primary",
- loading: false,
- handle: handleSendResume,
- disabled: false,
- disabledText: '简历已投递'
- }
- ])
- // const tools = isEnterprise ? enterpriseTools.value : userTools.value
- const tools = computed(() => {
- return isEnterprise ? enterpriseTools.value : userTools.value
- })
- const positionInfo = ref({})
- // const isSendResume = ref(false)
- if (!IM) {
- console.log('IM is disconnected')
- }
- // 参与招聘会的职位进入需传递招聘会id
- // const jobFairId = ref('')
- // if (route.query?.jobFairId) jobFairId.value = route.query.jobFairId
- // 职位进入
- if (route.query.id) {
- const api = route.query.enterprise ? getPositionDetails : getBaseInfo // getBaseInfo getUserInfo
- // const res = await api({ id: route.query.id })
- const res = await api(route.query.enterprise ? { id: route.query.id } : { userId: route.query.id })
- if (!res) {
- Snackbar.error('个人资料为空')
- } else {
- const query = route.query.enterprise ? [res.contact?.userId, res.contact?.enterpriseId] : [res?.userId]
- nextTick(async () => {
- const { channel } = await checkConversation(...query)
- const items = [
- {
- channel,
- userInfoVo: {
- userInfoResp: route.query.enterprise ? res.contact : { ...res, userId: res?.userId}
- }
- }
- ]
- handleChange(items)
- })
- }
- }
- const {
- conversationList,
- updateConversation,
- updateUnreadCount,
- deleteConversations,
- resetUnread
- } = initConnect(async (successful) => {
- if (!successful) {
- Snackbar.error('发送失败')
- return
- }
- chatRef.value.reset()
- // 发送成功
- const { list } = await getMoreMessages(1, channelItem.value)
- // updateConversation()
- messageItems.value = list.value
- chatRef.value.scrollBottom()
- })
- const getInterviewInviteList = async () => {
- if (!info.value.userId) return
- const data = await getInterviewInviteListByInviteUserId(info.value.userId)
- interview.value = data.slice(0, 1)
- }
- // 在当前频道中有新消息时更新未读消息数量
- const updateUnreadMessageCount = (val) => {
- const obj = val.find(e => e.userInfoVo.userId === info.value.userId)
- if (!obj?.unread || obj.unread === 0) return
- delete info.value.unread
- Object.assign(info.value, { unread: obj.unread, enterpriseId: entBaseInfo?.enterpriseId })
- }
- watch(
- () => conversationList.value,
- async (val) => {
- // 数据发生变化
- if (channelItem.value && IM.fromChannel === channelItem.value.channelID) {
- // 更新
- const { list } = await getMoreMessages(1, channelItem.value)
- messageItems.value = list.value
- if (Object.keys(info.value).length) updateUnreadMessageCount(val)
- chatRef.value.scrollBottom()
- }
- },
- {
- deep: true,
- immediate: true
- }
- )
- // 获取职位信息
- async function getMessageTypeSync () {
- const data = await getMessageType({
- fromUid: IM.uid,
- channelId: channelItem.value?.channelID,
- type: 102,
- page: {
- current: 1,
- size: 1,
- orders: [
- { column: 'message_seq', asc: false }
- ]
- }
- })
- if (!data.records || !data.records.length) {
- positionInfo.value = {}
- handleChangeSendResumeStatus(false)
- return
- }
- const _item = data.records.pop()
- const _itemJSON = JSON.parse(_item.payload)
- const _content = JSON.parse(_itemJSON.content)
- positionInfo.value = _content.positionInfo
- const check = await jobCvRelCheckSend({ jobId: _content.positionInfo.id })
- handleChangeSendResumeStatus(check)
- }
- // 修改发送状态
- function handleChangeSendResumeStatus (status) {
- if (!isEnterprise) {
- const item = userTools.value.find(e => e.key === 'sendResume')
- item.disabled = status
- }
- }
- async function handleChange (items) {
- try {
- chatRef.value.changeOverlay(true)
- const { userInfoVo, channel: myChannel, unread } = items.pop()
- info.value = userInfoVo?.userInfoResp ?? { name: '系统消息' }
- Object.assign(info.value, {
- channel: myChannel,
- unread
- })
- if (myChannel.channelID === 'system') {
- channelItem.value = myChannel
- const { list, more } = await getMoreMessages(1, channelItem.value)
- messageItems.value = list.value
- hasMore.value = more
- chatRef.value.scrollBottom()
- // 点开窗口消除未读数量
- await resetUnread(channelItem.value, entBaseInfo?.enterpriseId)
- await updateConversation()
- updateUnreadCount()
- return
- }
- // 个人端获取面试信息
- if (!isEnterprise) getInterviewInviteList()
- const userId = userInfoVo.userInfoResp.userId
- const enterpriseId = userInfoVo.userInfoResp.enterpriseId || undefined
- const { channel, list, more } = await initChart(userId, enterpriseId)
- // console.log('--------',list)
- channelItem.value = channel.value
- messageItems.value = list.value
- hasMore.value = more
- chatRef.value.scrollBottom()
- // 点开窗口消除未读数量
- await resetUnread(channel.value, entBaseInfo?.enterpriseId)
- await updateConversation()
- updateUnreadCount()
- // 获取最近职位记录
- getMessageTypeSync()
- } catch (error) {
- messageItems.value = []
- } finally {
- chatRef.value.changeOverlay(false)
- }
- }
- // 普通消息
- const handleUpdate = (val) => {
- send(val.value, channelItem.value)
- }
- // 选择文件
- const uploadFile = ref()
- const openFileInput = () => {
- uploadFile.value.trigger()
- }
- // 上传附件
- const handleUploadResume = async (url, title, filename) => {
- const obj = formItems.value.options.find(e => e.key === 'url')
- obj.value = filename
- obj.truthValue = url
- }
- const changePositionData = () => {
- entPositionListParams.value.pageNo = entPositionListLastData.value ? 1 : entPositionListParams.value.pageNo + 1
- selectJobId.value = ''
- getRecruitPositionList()
- }
- const positionDetail = (val) => {
- const id = val.value
- if (!id) return
- window.open(`/recruit/personal/position/details/${id}`)
- }
- // 选中职位并投递
- const selectJobId = ref('')
- const selectPositionSubmit = async () => {
- // 投递
- openPositionSelectDialog.value = false
- handleSendResume(handleSendResumeItem)
- }
- const pageLoading = ref(false)
- const entPositionTotal = ref(0)
- const entPositionList = ref([])
- const entPositionListParams = ref({ pageNo: 1, pageSize: 5 })
- const openPositionSelectDialog = ref(false)
- const entPositionListLastData = computed(() => entPositionListParams.value.pageNo * entPositionListParams.value.pageSize >= entPositionTotal.value)
- // 职位列表
- const getRecruitPositionList = async () => {
- const enterpriseId = info.value?.enterpriseId || null
- if (!enterpriseId) return Snackbar.warning('访问企业错误!')
- pageLoading.value = true
- const { list, total: number } = await getJobAdvertisedSearch({ ...entPositionListParams.value, enterpriseId })
- if (!list.length) return Snackbar.warning('企业暂无招聘中的职位,无法进行投递!')
- entPositionTotal.value = number
- entPositionList.value = list.map(j => {
- const e = j?.job || null
- if (!e) return e
- const salary = e.payFrom && e.payTo ? `${e.payFrom ? e.payFrom + '-' : ''}${e.payTo}${e.payName ? '/' + e.payName : ''}` : '面议'
- return {
- label: `${formatName(e.name)}_${e.areaName ? e.area?.str : '全国'} ${salary}`,
- value: e.id,
- data: e
- }
- }).filter(Boolean)
- setTimeout(() => { pageLoading.value = false }, 300)
- }
- // 获取简历
- const showUploadDialog = ref(false)
- const enRequestPositionInfo = ref({}) // 企业求简历时选中的职位信息
- let handleSendResumeItem = null
- async function handleSendResume (item) {
- const jobId = enRequestPositionInfo.value && enRequestPositionInfo.value?.id ? enRequestPositionInfo.value?.id : positionInfo.value.id
- if (!jobId && !selectJobId.value) {
- // 没有基于职位接收到的沟通,弹出职位列表让求职者选择。否则无法投递简历。
- handleSendResumeItem = item
- await getRecruitPositionList()
- if (entPositionTotal.value) openPositionSelectDialog.value = true
- return
- }
- try {
- item.loading = true
- // 获取简历列表
- const result = await getPersonResumeCv()
- if (result.length === 0) {
- Snackbar.warning(t('resume.resumeYetSubmit'))
- showUploadDialog.value = true
- return
- }
- resumeList.value = result
- if (item?.content?.query?.positionInfo?.data && Object.keys(item?.content?.query?.positionInfo?.data).length) enRequestPositionInfo.value = item?.content?.query?.positionInfo?.data
- showResume.value = true
- } finally {
- item.loading = false
- }
- }
- // 撤回简历
- async function handleBack (val) {
- console.log(val)
- try {
- // 撤回简历
- // 撤回聊天
- // await messageBack({
- // channelId: val.channel_id,
- // messageId: val.message_id,
- // nickName: baseInfo.name
- // // enterpriseId: ''
- // })
- } catch (error) {
- console.log(error)
- }
- }
- /**
- * 发送简历
- * text param
- * {
- * remark: 备注
- * query: {} 自定义参数 access -1 未确定 0 拒绝 1 同意
- * type: 1 => 发送简历
- * 2 => 索要简历
- * 3 => 信息描述
- * }
- */
- // 没有上传过简历的弹窗上传并发送给对方
- const handleSubmitAttachment = async () => {
- const { valid } = await CtFormRef.value.formRef.validate()
- if (!valid) return
- const obj = {}
- formItems.value.options.forEach(e => {
- obj[e.key] = e.truthValue || e.value
- })
- if (!obj.title || !obj.url) return
- // 学生实习到岗信息
- let practice = {}
- if (isStudent.value) {
- practice = studentDeliveryFormRef.value.getQueryParams()
- console.log(practice, '上传简历-到岗信息')
- }
- if (isStudent.value && (!practice?.practiceStartTime || !practice?.practiceEndTime)) return Snackbar.warning('请完善实习到岗信息')
- // 保存附件
- await savePersonResumeCv(obj)
- // 简历投递至简历库
- if (isEmployment.value !== '-1') {
- let params = {
- jobId: positionInfo.value.id || selectJobId.value,
- url: obj.url,
- recommendUserId: isEmployment.value
- }
- // 如果是学生则需要带上实习信息
- if (practice && Object.keys(practice).length > 0) params = Object.assign(params, practice)
- await jobCvRelHireSend(params)
- } else {
- const jobId = enRequestPositionInfo.value && enRequestPositionInfo.value?.id ? enRequestPositionInfo.value?.id : positionInfo.value.id || selectJobId.value
- const type = (enRequestPositionInfo.value && Object.keys(enRequestPositionInfo.value).length ? enRequestPositionInfo.value.hire : positionInfo.value.hire) ? 1 : 0
- let params = {
- jobId,
- title: obj.title,
- url: obj.url,
- type
- }
- // 如果是学生则需要带上实习信息
- if (practice && Object.keys(practice).length > 0) params = Object.assign(params, practice)
- await jobCvRelSend(params)
- }
- handleChangeSendResumeStatus(true)
- showUploadDialog.value = false
- const text = {
- remark: '发送简历',
- query: {
- src: obj.url,
- title: obj.title
- },
- type: 1
- }
- if (enRequestPositionInfo.value) text.query.positionInfo = enRequestPositionInfo.value
- send (JSON.stringify(text), channelItem.value, 105)
- enRequestPositionInfo.value = {}
- }
- async function handleSubmitResume () {
- if (!selectResume.value) {
- Snackbar.warning(t('resume.selectResumeToSubmit'))
- return
- }
- const _info = resumeList.value.find((item) => item.id === selectResume.value)
- // 学生实习到岗信息
- let practice = {}
- if (isStudent.value) {
- practice = studentDeliveryFormRef.value.getQueryParams()
- console.log(practice, '选择简历-到岗信息')
- }
- if (isStudent.value && (!practice?.practiceStartTime || !practice?.practiceEndTime)) return Snackbar.warning('请完善实习到岗信息')
- // 简历投递至简历库
- if (isEmployment.value !== '-1') {
- let params = {
- jobId: positionInfo.value.id || selectJobId.value,
- url: _info.url,
- recommendUserId: isEmployment.value
- }
- // 如果是学生则需要带上实习信息
- if (practice && Object.keys(practice).length > 0) params = Object.assign(params, practice)
- await jobCvRelHireSend(params)
- } else {
- const jobId = enRequestPositionInfo.value && enRequestPositionInfo.value?.id ? enRequestPositionInfo.value?.id : positionInfo.value.id || selectJobId.value
- const type = (enRequestPositionInfo.value && Object.keys(enRequestPositionInfo.value).length ? enRequestPositionInfo.value.hire : positionInfo.value.hire) ? 1 : 0
- let params = {
- jobId,
- title: _info.title,
- url: _info.url,
- type
- }
- // 如果是学生则需要带上实习信息
- if (practice && Object.keys(practice).length > 0) params = Object.assign(params, practice)
- await jobCvRelSend(params)
- }
- handleChangeSendResumeStatus(true)
- showResume.value = false
- const text = {
- remark: '发送简历',
- query: {
- src: _info.url,
- title: _info.title,
- id: _info.id,
- },
- type: 1
- }
- if (enRequestPositionInfo.value) text.query.positionInfo = enRequestPositionInfo.value
- send (JSON.stringify(text), channelItem.value, 105)
- enRequestPositionInfo.value = {}
- }
- // 简历预览
- const handlePreview = (val) => {
- previewFile(val.content.query.src)
- }
- const handleGetMore = async () => {
- // 当前滚动条高度
- const scrollHeight = chatRef.value.chatRef.scrollHeight
- // 当前滚动条距离
- const scrollTop = chatRef.value.chatRef.scrollTop
- try {
- chatRef.value.changeLoading(true)
- pageSize.value++
- const { list, more } = await getMoreMessages(pageSize.value, channelItem.value)
- messageItems.value.unshift(...list.value)
- hasMore.value = more
- nextTick(() => {
- // 渲染后高度
- const _scrollHeight = chatRef.value.chatRef.scrollHeight
- chatRef.value.chatRef.scrollTop = _scrollHeight - scrollHeight - scrollTop
- })
- } finally {
- chatRef.value.changeLoading(false)
- }
- }
- const handleDelete = async ({ channel }) => {
- await deleteConversations(channel, entBaseInfo?.enterpriseId)
- await updateConversation()
- updateUnreadCount()
- }
- // 没有企业ID则enterpriseId为undefined
- // 发送消息体 { text, type: 2 }
- // 面试邀约
- const getPositionList = async () => {
- const data = await getJobAdvertised({ status: 0, exTime: 0 }) // 0开启 1关闭 不带则全部
- if (!data.length) return
- const list = dealDictArrayData([], data)
- positionList.value = list.map(e => {
- const salary = e.payFrom && e.payTo ? `${e.payFrom ? e.payFrom + '-' : ''}${e.payTo}${e.payName ? '/' + e.payName : ''}` : '面议'
- return {
- label: `${formatName(e.name)}_${e.areaName ? e.area?.str : '全国'} ${salary}`,
- value: e.id,
- data: e
- }
- })
- }
- async function handleInvite (item) {
- item.loading = true
- positionList.value = []
- try {
- await getPositionList()
- if (!positionList.value.length) return Snackbar.warning('请先发布职位')
- if (item.key === 'requestResume') return showSelectPosition.value = true
- showInvite.value = true
- } catch (error) {
- console.log(error)
- } finally {
- item.loading = false
- }
- }
- // 企业-发送面试邀请
- const handleSubmit = async () => {
- const { valid } = await inviteRef.value.CtFormRef.formRef.validate()
- if (!valid) {
- return
- }
- const query = inviteRef.value.getQuery()
- if (!query.time) {
- Snackbar.warning('时间不能为空')
- return
- }
- query.userId = info.value.userId
- query.positionInfo = positionList.value.find(e => e.value === query.jobId)
- // 需要id
- const data = await saveInterviewInvite(query)
- // 保留邀请id
- query.id = data.id
- showTip.value = true
- send(JSON.stringify(query), channelItem.value, 101)
- showInvite.value = false
- }
- const router = useRouter()
- const handleToInterviewManagement = () => {
- router.push('/recruit/enterprise/interviewManagement')
- }
- // 企业-求简历
- const handleRequestResumeSubmit = async () => {
- const { valid } = await requestFromRef.value.formRef.validate()
- if (!valid) return
- const jobId = requestFormItems.value.options.find(e => e.key === 'jobId').value
- const positionInfo = positionList.value.find(e => e.value === jobId)
- const text = {
- remark: '求简历',
- query: {
- src: '',
- title: '',
- id: '',
- positionInfo
- },
- type: 2
- }
- send (JSON.stringify(text), channelItem.value, 105)
- showSelectPosition.value = false
- }
- const handleAgree = (val) => {
- if (!val.id) return
- const query = {
- id: val.id
- }
- const type = getIsEnterprise() ? 'entBaseInfo' : 'baseInfo'
- const baseInfo = localStorage.getItem(type)
- if (baseInfo) {
- const { phone } = JSON.parse(baseInfo)
- query.phone = phone
- }
- Confirm(t('common.confirmTitle'), '是否确定接收此面试邀请?').then(async () => {
- await userInterviewInviteConsent(query)
- Snackbar.success(t('common.operationSuccessful'))
- getInterviewInviteList()
- send(JSON.stringify({ id: val.id }), channelItem.value, 104)
- })
- }
- // 拒绝面试邀请
- const handleRefuse = (val) => {
- if (!val.id) return
- Confirm(t('common.confirmTitle'), '您是否确定要拒绝此面试邀请?').then(async () => {
- await userInterviewInviteReject(val.id)
- Snackbar.success(t('common.operationSuccessful'))
- getInterviewInviteList()
- send(JSON.stringify({ id: val.id }), channelItem.value, 103)
- })
- }
- </script>
- <style scoped lang="scss">
- .message {
- display: flex;
- &-left {
- position: relative;
- flex-shrink: 0;
- height: 100%;;
- width: 360px;
- background-color: #fff;
- border-radius: 8px;
- margin-right: 12px;
- .message-left-search {
- width: 100%;
- height: 60px;
- background: linear-gradient(90deg, #f5fcfc, #fcfbfa);
- border-radius: 8px 8px 0 0;
- }
- .message-chat-box {
- height: 0;
- flex: 1;
- overflow: auto;
- padding-bottom: 20px;
- .chat-item {
- position: relative;
- width: 100%;
- height: 78px;
- padding: 14px 12px;
- cursor: pointer;
- &:hover {
- background-color: #f8f8f8;
- }
- .chat-item-time {
- position: absolute;
- right: 12px;
- top: 50%;
- transform: translateY(-50%);
- }
- .title-box {
- max-width: 114px;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- display: inline-block;
- }
- }
- }
- .message-no-more-text {
- color: var(--color-999);
- font-size: 14px;
- text-align: center
- }
- .left-noData {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- }
- }
- &-right {
- height: 100%;
- flex: 1;
- width: 0;
- position: relative;
- background-color: #fff;
- border-radius: 8px;
- .right-noData {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- }
- }
- }
- .enterprise-name {
- max-width: 150px;
- .line {
- display: inline-block;
- width: 1px;
- height: 12px;
- vertical-align: middle;
- background-color: #e0e0e0;
- margin: 0 3px;
- }
- }
- .radioBox {
- &:hover {
- border-radius: 2px;
- background-color: var(--color-f8);
- }
- }
- </style>
|