123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- <template>
- <div class="listItem d-flex align-center pa-3 mb-3" v-for="(item, index) in items" :key="'item_' + index">
- <div class="d-flex align-center">
- <div class="mr-5 font-size-16" style="color: orange; width: 96px;">{{ timesTampChange(item.time, 'Y-M-D h:m') }}</div>
- <v-avatar class="mr-2" size=40 :image="getUserAvatar(item?.person?.avatar, item?.person?.sex)"></v-avatar>
- <div class="d-flex flex-column mr-3" style="width: 110px;">
- <span class="ellipsis mb-1">{{ item?.person?.name }}</span>
- <span class="ellipsis" style="color: var(--color-999);">{{ item?.job?.name }}</span>
- </div>
- </div>
- <div class="d-flex align-center right-item">
- <div style="min-width: 80px;text-align: center;">
- <v-icon v-if="item?.phone" class="mx-1" size="20" color="primary">mdi-phone-outline</v-icon>
- <span>{{ item?.phone || '-' }}</span>
- </div>
- <div>
- <!-- 面试类型: 线下面试 -->
- <span v-if="item.type === '1'">
- <v-icon class="mx-3" size="20" color="primary">mdi-account-multiple-outline</v-icon>
- <span>{{ $t('interview.offlineInterview') }}</span>
- </span>
- <!-- 面试类型: 线上面试 -->
- <span v-else class="d-flex">
- <v-icon class="mx-3 mt-2" size="20" color="primary">mdi mdi-video-account</v-icon>
- <span class="d-flex flex-column">
- <span>{{ $t('interview.onlineInterview') }}</span>
- <span style="color: var(--color-999);">腾讯会议</span>
- </span>
- </span>
- </div>
- <!-- 面试状态: '待接受'/'已取消' -->
- <div :style="{ 'color': colorData[item.status] }">
- <v-icon v-if="statusList.find(e => e.value === item.status)?.label" size="30">mdi mdi-circle-small</v-icon>
- <span>
- {{ statusList.find(e => e.value === item.status)?.label }}
- <span v-if="item?.job?.status === '1'" style="font-size: 13px; color: grey;">[职位已关闭]</span>
- </span>
- </div>
- <div>
- <template v-if="item.job?.status !== '1'" >
- <span v-if="editStatus.indexOf(item.status) !== -1" class="font-size-15 color-primary" @click="handleActionClick('edit', item)">修改面试</span>
- <span v-if="againStatus.indexOf(item.status) !== -1" class="font-size-15 color-primary" @click="handleActionClick('edit', item)">重新邀约</span>
- </template>
- <span v-if="item.status === '1'" class="font-size-15 color-primary" @click="handleActionClick('completed', item)">完成面试</span>
- <span v-if="item.status === '3'" class="font-size-15 color-primary" @click="handleActionClick('feedback', item)">填写反馈</span>
- <v-menu v-if="actionItems(item).length">
- <template v-slot:activator="{ props }">
- <v-icon v-bind="props" class="mx-3" size="20" color="primary">mdi-dots-horizontal</v-icon>
- </template>
- <v-list>
- <v-list-item
- v-for="(k, index) in actionItems(item)"
- :key="index"
- :value="index"
- color="primary"
- @click="handleActionClick(k.value, item)"
- >
- <v-list-item-title>{{ k.title }}</v-list-item-title>
- </v-list-item>
- </v-list>
- </v-menu>
- </div>
- </div>
- </div>
- <!-- 修改面试、重新邀约 -->
- <CtDialog :visible="showInvite" :widthType="2" titleClass="text-h6" title="面试信息" @close="handleEditClose" @submit="handleEditSubmit">
- <InvitePage v-if="showInvite" ref="inviteRef" :itemData="itemData" :position="positionItems"></InvitePage>
- </CtDialog>
- <!-- 取消面试 -->
- <CtDialog :visible="cancelInvite" :widthType="2" titleClass="text-h6" title="取消面试" @close="handleCancelClose" @submit="handleCancelSubmit">
- <TextArea v-model="cancelQuery.reason" :item="textItems"></TextArea>
- </CtDialog>
- <!-- 爽约、填写反馈 -->
- <CtDialog :visible="show" :widthType="2" titleClass="text-h6" :title="currentAction === 'feedback' ? '填写反馈' : '填写爽约原因'" @close="handleClose" @submit="handleSubmit">
- <TextArea v-if="currentAction === 'feedback'" v-model="query.evaluate" :item="textItems2"></TextArea>
- <TextArea v-else v-model="query.reason" :item="textItems2"></TextArea>
- </CtDialog>
- </template>
- <script setup>
- defineOptions({ name: 'interview-item'})
- import { ref } from 'vue'
- import { timesTampChange } from '@/utils/date'
- import { useI18n } from '@/hooks/web/useI18n'
- import { completedInterviewInvite, cancelInterviewInvite, saveInterviewInvite, noAttendInterviewInvite, feedbackInterviewInvite } from '@/api/recruit/enterprise/interview'
- import InvitePage from './invite.vue'
- import Snackbar from '@/plugins/snackbar'
- import Confirm from '@/plugins/confirm'
- import { getUserAvatar } from '@/utils/avatar'
- defineProps({
- items: Array,
- statusList: Array,
- positionItems: Array
- })
- const emit = defineEmits(['refresh', 'action'])
- const { t } = useI18n()
- const editStatus = ['0'] // 修改面试状态
- const againStatus = ['98', '99'] // 重新邀约状态
- const actions = ref([
- { title: '完成面试', value: 'completed' },
- { title: '取消面试', value: 'cancel' },
- { title: '填写反馈', value: 'feedback' },
- { title: '爽约', value: 'attended' },
- { title: '修改面试', value: 'edit' }
- ])
- const colorData = {
- '0': 'orange',
- '1': 'green',
- '2': 'green',
- '3': 'var(--v-primary-base)',
- '4': 'var(--color-999)',
- '5': 'var(--v-error-base)',
- '98': 'var(--v-error-base)',
- '99': 'var(--color-999)'
- }
- // 邀请
- const itemData = ref({})
- const showInvite = ref(false)
- const inviteRef = ref()
- // 取消
- const cancelInvite = ref(false)
- const cancelQuery = ref({
- id: null,
- reason: null
- })
- const textItems = ref({
- label: '取消原因 *',
- clearable: true
- })
- // 爽约、反馈
- const currentAction = ref('feedback')
- const show = ref(false)
- const query = ref({})
- const textItems2 = ref({
- label: '反馈 *',
- clearable: true
- })
- const obj = {
- '0': [1],
- '1': [4, 1, 3],
- '2': [3]
- }
- const actionItems = (item) => {
- const status = item?.status
- const jobClosed = item.job?.status === '1'
- const type = jobClosed && obj[status] ? [0] : obj[status]
- if (!type || !type.length) return []
- let data = type.map(e => actions.value[e])
- return data
- }
- // 完成面试
- const handleFinish = (item) => {
- if (!item.id) return
- Confirm(t('common.confirmTitle'), '是否确认已完成面试?').then(async () => {
- await completedInterviewInvite(item.id)
- Snackbar.success(t('common.operationSuccessful'))
- emit('refresh')
- })
- }
- // 操作按钮
- const handleActionClick = (value, item) => {
- if (item.job?.status === '1' && (value === 'edit')) return Snackbar.warning('职位已关闭!')
- // 修改、重新邀约
- if (value === 'edit') {
- itemData.value = item
- showInvite.value = true
- }
- // 取消
- if (value === 'cancel') {
- cancelQuery.value.id = item.id
- cancelInvite.value = true
- }
- // 完成
- if (value === 'completed') handleFinish(item)
- // 爽约、反馈
- if (value === 'feedback' || value === 'attended') {
- currentAction.value = value
- textItems2.value.label = value === 'feedback' ? '反馈 *' : '爽约原因 *'
- query.value = value === 'feedback' ? { id: item.id, evaluate: null } : { id: item.id, reason: null }
- show.value = true
- }
- }
- // 修改面试、重新邀约
- const handleEditClose = () => {
- itemData.value = {}
- showInvite.value = false
- }
- const handleEditSubmit = async () => {
- const query = inviteRef.value.getQuery()
- if (!Object.keys(query).length) return
- await saveInterviewInvite(query)
- Snackbar.success(t('common.operationSuccessful'))
- handleEditClose()
- emit('refresh')
- }
- // 取消面试
- const handleCancelClose = () => {
- cancelInvite.value = false
- cancelQuery.value = {
- id: null,
- reason: null
- }
- }
- const handleCancelSubmit = async () => {
- if (!cancelQuery.value.reason) return Snackbar.warning('请填写取消原因')
- await cancelInterviewInvite(cancelQuery.value)
- Snackbar.success(t('common.operationSuccessful'))
- handleCancelClose()
- emit('refresh')
- }
- // 爽约、反馈
- const handleClose = () => {
- show.value = false
- query.value = {}
- }
- const handleSubmit = async () => {
- const key = currentAction.value === 'feedback' ? 'evaluate' : 'reason'
- if (!query.value[key]) return Snackbar.warning('请填写您的' + (currentAction.value === 'feedback' ? '反馈' : '爽约原因'))
- const api = currentAction.value === 'feedback' ? feedbackInterviewInvite : noAttendInterviewInvite
- await api(query.value)
- Snackbar.success(t('common.operationSuccessful'))
- emit('refresh')
- handleClose()
- }
- </script>
- <style scoped lang="scss">
- .listItem {
- cursor: pointer;
- width: 100%;
- min-width: 600px;
- overflow: auto;
- height: 76px;
- border: 1px solid #e5e6eb;
- border-radius: 5px;
-
- &:hover {
- background-color: var(--color-f8);
- }
- .right-item {
- width: 100%;
- div {
- width: 25%;
- }
- }
- }
- </style>
|