|
@@ -4,38 +4,36 @@
|
|
|
<div class="d-flex justify-space-between">
|
|
|
<div class="d-flex mb-3">
|
|
|
<!-- 职位 -->
|
|
|
- <v-select
|
|
|
- v-model="positionValue"
|
|
|
+ <v-autocomplete
|
|
|
+ v-model="query.jobId"
|
|
|
:items="positionItems"
|
|
|
density="compact"
|
|
|
variant="outlined"
|
|
|
item-title="label"
|
|
|
- item-value="value"
|
|
|
- hide-details
|
|
|
+ item-value="value"
|
|
|
+ clearable
|
|
|
+ hide-details
|
|
|
+ label="职位"
|
|
|
color="primary"
|
|
|
- style="width: 150px;"
|
|
|
+ style="width: 300px;"
|
|
|
class="mr-3"
|
|
|
- @update:model-value="handlePositionChange"
|
|
|
- ></v-select>
|
|
|
- <!-- 状态 -->
|
|
|
+ ></v-autocomplete>
|
|
|
<v-select
|
|
|
- v-model="stateValue"
|
|
|
- :items="stateItems"
|
|
|
+ v-model="query.status"
|
|
|
+ :items="statusList"
|
|
|
density="compact"
|
|
|
variant="outlined"
|
|
|
item-title="label"
|
|
|
- item-value="value"
|
|
|
- hide-details
|
|
|
+ item-value="value"
|
|
|
+ clearable
|
|
|
+ hide-details
|
|
|
+ label="面试状态"
|
|
|
color="primary"
|
|
|
- style="width: 150px;"
|
|
|
- @update:model-value="handleStateChange"
|
|
|
+ style="width: 300px;"
|
|
|
></v-select>
|
|
|
+ <v-btn color="primary" class="half-button ml-3" @click="handleSearch">查 询</v-btn>
|
|
|
+ <v-btn class="half-button ml-3" variant="outlined" color="primary" @click="handleReset">重 置</v-btn>
|
|
|
</div>
|
|
|
- <!-- <div class="mr-3 mb-3 px-2 py-1" style="background-color: var(--color-999); color: #fff; border-radius: 5px;">
|
|
|
- <v-icon size="30">mdi mdi-view-list-outline</v-icon>
|
|
|
- <v-icon size="30">mdi mdi-circle-small</v-icon>
|
|
|
- </div> -->
|
|
|
- <div></div>
|
|
|
</div>
|
|
|
<v-divider class="mb-3"></v-divider>
|
|
|
<div class="d-flex">
|
|
@@ -43,136 +41,266 @@
|
|
|
<div class="d-flex justify-space-between px-5">
|
|
|
<div v-if="selectDateValue">
|
|
|
<span>{{ timesTampChange(selectDateValue).slice(0, 10) }}</span>
|
|
|
- <span class="ml-2" style="cursor: pointer;" @click="selectDateValue = null">{{ $t('common.cleanUp') }}</span>
|
|
|
+ <span class="ml-2" style="cursor: pointer;" @click="handleClear">{{ $t('common.cleanUp') }}</span>
|
|
|
</div>
|
|
|
- <div v-else class="color999">{{ $t('interview.noDateSelected') }}</div>
|
|
|
+ <div v-else class="color-999">{{ $t('interview.noDateSelected') }}</div>
|
|
|
<v-btn color="primary" variant="text" size="small" @click="selectDateValue = new Date()">{{ $t('interview.today') }}</v-btn>
|
|
|
</div>
|
|
|
<v-date-picker
|
|
|
v-model="selectDateValue"
|
|
|
color="primary"
|
|
|
- show-adjacent-months
|
|
|
:hide-header="true"
|
|
|
- @update:modelValue="handleCurrentChange"
|
|
|
+ @update:modelValue="handleChangeDate"
|
|
|
class="mr-3"
|
|
|
- ></v-date-picker>
|
|
|
+ >
|
|
|
+ </v-date-picker>
|
|
|
</div>
|
|
|
<v-divider style="height: auto;" class="mr-5" vertical></v-divider>
|
|
|
<div style="flex: 1;overflow: hidden;">
|
|
|
- <div
|
|
|
- class="listItem d-flex align-center justify-space-between pa-3 mb-3"
|
|
|
- style="width: 100%;min-width: 900px;overflow: auto;height: 76px;border: 1px solid #e5e6eb;border-radius: 5px;"
|
|
|
- v-for="(item, index) in dataList" :key="'item_' + index"
|
|
|
- >
|
|
|
- <div class="d-flex align-center">
|
|
|
- <span class="mr-2">{{ item.date }}</span>
|
|
|
- <span class="mr-5 fz16" style="color: orange;">{{ item.time }}</span>
|
|
|
- <v-avatar class="mr-2" size=50 :image="item?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
|
|
|
- <div class="d-flex flex-column mr-3" style="width: 110px;">
|
|
|
- <span class="ellipsis mb-1">{{ item?.name }}</span>
|
|
|
- <span class="ellipsis" style="color: var(--color-999);">{{ item?.job }}</span>
|
|
|
+ <div v-if="items.length">
|
|
|
+ <div
|
|
|
+ class="listItem d-flex align-center justify-space-between pa-3 mb-3"
|
|
|
+ v-for="(item, index) in items" :key="'item_' + index"
|
|
|
+ >
|
|
|
+ <div class="d-flex align-center">
|
|
|
+ <span class="mr-5 font-size-16" style="color: orange;">{{ timesTampChange(item.time) }}</span>
|
|
|
+ <v-avatar class="mr-2" size=40 :image="item?.person?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></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>
|
|
|
- <span style="min-width: 100px;">离职-随时到岗</span>
|
|
|
- </div>
|
|
|
- <span style="min-width: 120px;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>
|
|
|
- </span>
|
|
|
- <!-- 面试类型: 线下面试 -->
|
|
|
- <span v-if="item.interviewType === 1">
|
|
|
- <v-icon class="mx-3" size="20" color="primary">mdi-account-multiple-outline</v-icon>
|
|
|
- <span>{{ $t('interview.offlineInterview') }}</span>
|
|
|
- </span>
|
|
|
- <!-- 面试类型: 线上面试 -->
|
|
|
- <span class="d-flex" v-else>
|
|
|
- <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 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>
|
|
|
+ </span>
|
|
|
+ <!-- 面试类型: 线下面试 -->
|
|
|
+ <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>
|
|
|
- <!-- 面试状态: '待接受'/'已取消' -->
|
|
|
- <span :style="{ 'color': item.interviewStatus ? 'orange' :'var(--color-999)'}">
|
|
|
- <v-icon size="30">mdi mdi-circle-small</v-icon>
|
|
|
- <span>{{ $t(item.interviewStatus ? 'interview.waitingForAcceptance' :'interview.canceled') }}</span>
|
|
|
- </span>
|
|
|
- <span>
|
|
|
- <span class="fz15 primaryColor">{{ item.interviewStatus ? '修改面试' :'重新邀请' }}</span>
|
|
|
- <v-icon class="mx-3" size="20" color="primary">mdi-dots-horizontal</v-icon>
|
|
|
- </span>
|
|
|
+ <!-- 面试类型: 线上面试 -->
|
|
|
+ <span class="d-flex" v-else>
|
|
|
+ <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>
|
|
|
+ <!-- 面试状态: '待接受'/'已取消' -->
|
|
|
+ <span :style="{ 'color': item.status !== '99' ? 'orange' :'var(--color-999)'}">
|
|
|
+ <v-icon size="30">mdi mdi-circle-small</v-icon>
|
|
|
+ <span>{{ statusList.find(e => e.value === item.status)?.label }}</span>
|
|
|
+ </span>
|
|
|
+ <span>
|
|
|
+ <span v-if="editStatus.indexOf(item.status)" class="font-size-15 color-primary" @click="handleActionClick(2, item)">修改面试</span>
|
|
|
+ <v-menu>
|
|
|
+ <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"
|
|
|
+ :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>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <CtPagination
|
|
|
+ v-if="total > 0"
|
|
|
+ :total="total"
|
|
|
+ :page="query.pageNo"
|
|
|
+ :limit="query.pageSize"
|
|
|
+ @handleChange="handleChangePage"
|
|
|
+ ></CtPagination>
|
|
|
</div>
|
|
|
+ <Empty v-else :elevation="false"></Empty>
|
|
|
</div>
|
|
|
</div>
|
|
|
</v-card>
|
|
|
+
|
|
|
+ <!-- 修改面试 -->
|
|
|
+ <CtDialog :visible="showInvite" :widthType="2" titleClass="text-h6" title="面试邀请" @close="handleClose" @submit="handleSubmit">
|
|
|
+ <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">
|
|
|
+ <TextInput v-model="cancelQuery.reason" :item="textItems"></TextInput>
|
|
|
+ </CtDialog>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { timesTampChange } from '@/utils/date'
|
|
|
-import { ref } from 'vue'
|
|
|
defineOptions({ name: 'enterprise-interview'})
|
|
|
+import { ref } from 'vue'
|
|
|
+import { getInterviewInvitePage, saveInterviewInvite, cancelInterviewInvite } from '@/api/recruit/enterprise/interview'
|
|
|
+import InvitePage from './components/invite.vue'
|
|
|
+import { getDict } from '@/hooks/web/useDictionaries'
|
|
|
+import Snackbar from '@/plugins/snackbar'
|
|
|
+import { getJobAdvertised } from '@/api/enterprise'
|
|
|
+import { dealDictArrayData } from '@/utils/position'
|
|
|
+import { timesTampChange, getStartAndEndOfDay } from '@/utils/date'
|
|
|
|
|
|
-const dataList = ref([
|
|
|
- {
|
|
|
- avatar: 'http://menduner.citupro.com:6868/admin-api/infra/file/24/get/241e594d4473872eabb312673f42241a2e9598298cb7d9d791cc9c8cb65fb058.jpg',
|
|
|
- name: '王瑶',
|
|
|
- job: '软件测试',
|
|
|
- phone: '18406571583',
|
|
|
- date: '2024-06-14',
|
|
|
- time: '11:00',
|
|
|
- interviewType: 1,
|
|
|
- interviewStatus: 1,
|
|
|
- },
|
|
|
- {
|
|
|
- name: '黄小姐',
|
|
|
- job: '软件测试-实习岗位',
|
|
|
- phone: '',
|
|
|
- date: '2024-06-14',
|
|
|
- time: '15:00',
|
|
|
- interviewType: 2,
|
|
|
- interviewStatus: 0,
|
|
|
- },
|
|
|
+const cancelInvite = ref(false)
|
|
|
+const showInvite = ref(false)
|
|
|
+const inviteRef = ref()
|
|
|
+const items = ref([])
|
|
|
+const cancelQuery = ref({
|
|
|
+ id: null,
|
|
|
+ reason: null
|
|
|
+})
|
|
|
+const editStatus = ['99', '1', '0']
|
|
|
+const statusList = ref()
|
|
|
+const itemData = ref({})
|
|
|
+// 状态
|
|
|
+const actionItems = ref([
|
|
|
+ // { title: '沟通', value: 1 },
|
|
|
+ // { title: '修改面试', value: 2 },
|
|
|
+ { title: '取消面试', value: 3 },
|
|
|
+ // { title: '面试记录', value: 4 }
|
|
|
])
|
|
|
+const total = ref(0)
|
|
|
+const query = ref({
|
|
|
+ pageSize: 10,
|
|
|
+ pageNo: 1,
|
|
|
+ status: null,
|
|
|
+ jobId: null,
|
|
|
+ time: []
|
|
|
+})
|
|
|
+const textItems = ref({
|
|
|
+ type: 'text',
|
|
|
+ label: '取消原因 *',
|
|
|
+ clearable: true
|
|
|
+})
|
|
|
+
|
|
|
+// 状态字典
|
|
|
+const getStatusList = async () => {
|
|
|
+ const { data } = await getDict('menduner_interview_invite_status')
|
|
|
+ statusList.value = data
|
|
|
+}
|
|
|
+getStatusList()
|
|
|
+
|
|
|
+// 列表
|
|
|
+const getData = async () => {
|
|
|
+ const { list, total: number } = await getInterviewInvitePage(query.value)
|
|
|
+ items.value = list
|
|
|
+ total.value = number
|
|
|
+}
|
|
|
+getData()
|
|
|
+
|
|
|
+// 分页
|
|
|
+const handleChangePage = (e) => {
|
|
|
+ query.value.pageNo = e
|
|
|
+ getData()
|
|
|
+}
|
|
|
|
|
|
-const selectDateValue = ref(null) // new Date(); new Date('2018-03-02')
|
|
|
-const handleCurrentChange = (val, val1) => {
|
|
|
- console.log('1', val, val1)
|
|
|
+// 日期选择
|
|
|
+const selectDateValue = ref(null)
|
|
|
+const handleChangeDate = () => {
|
|
|
+ const time = getStartAndEndOfDay(selectDateValue.value)
|
|
|
+ if (!time || !time.length) return delete query.value.time
|
|
|
+ query.value.time = time
|
|
|
+ query.value.pageNo = 1
|
|
|
+ getData()
|
|
|
+}
|
|
|
+// 清除
|
|
|
+const handleClear = () => {
|
|
|
+ query.value.pageNo = 1
|
|
|
+ selectDateValue.value = null
|
|
|
+ delete query.value.time
|
|
|
+ getData()
|
|
|
+}
|
|
|
+
|
|
|
+const handleSearch = () => {
|
|
|
+ query.value.pageNo = 1
|
|
|
+ getData()
|
|
|
+}
|
|
|
+
|
|
|
+const handleReset = () => {
|
|
|
+ query.value = {
|
|
|
+ pageSize: 10,
|
|
|
+ pageNo: 1,
|
|
|
+ status: null,
|
|
|
+ jobId: null,
|
|
|
+ time: []
|
|
|
+ }
|
|
|
+ selectDateValue.value = null
|
|
|
+ getData()
|
|
|
}
|
|
|
|
|
|
// 职位
|
|
|
-const positionValue = ref('0')
|
|
|
-const positionItems = ref([
|
|
|
- { label: '全部职位', value: '0' },
|
|
|
- { label: '软件测试(10-11K)', value: '1' },
|
|
|
-])
|
|
|
-const handlePositionChange = (val) => {
|
|
|
- console.log('1', val)
|
|
|
+const positionItems = ref([])
|
|
|
+const getPositionList = async () => {
|
|
|
+ const data = await getJobAdvertised({ hire: false })
|
|
|
+ if (!data.length) return
|
|
|
+ const list = dealDictArrayData([], data)
|
|
|
+ positionItems.value = list.map(e => {
|
|
|
+ return { label: `${e.name}${e.areaName ? '_' + e.areaName : ''} ${e.payFrom}-${e.payTo}/${e.payName}`, value: e.id }
|
|
|
+ })
|
|
|
}
|
|
|
-// 状态
|
|
|
-const stateValue = ref('0')
|
|
|
-const stateItems = ref([
|
|
|
- { label: '全部状态', value: '0' },
|
|
|
- { label: '待接受', value: '1' },
|
|
|
- { label: '待面试', value: '1' },
|
|
|
- { label: '即将面试', value: '1' },
|
|
|
- { label: '面试时间到', value: '1' },
|
|
|
- { label: '已完成', value: '1' },
|
|
|
- { label: '待反馈', value: '1' },
|
|
|
- { label: '已反馈', value: '1' },
|
|
|
-])
|
|
|
-const handleStateChange = (val) => {
|
|
|
- console.log('1', val)
|
|
|
+getPositionList()
|
|
|
+
|
|
|
+// 操作按钮
|
|
|
+const handleActionClick = (value, item) => {
|
|
|
+ // 修改
|
|
|
+ if (value === 2) {
|
|
|
+ itemData.value = item
|
|
|
+ showInvite.value = true
|
|
|
+ }
|
|
|
+ // 取消
|
|
|
+ if (value === 3) {
|
|
|
+ cancelQuery.value.id = item.id
|
|
|
+ cancelInvite.value = true
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 修改面试
|
|
|
+const handleClose = () => {
|
|
|
+ itemData.value = {}
|
|
|
+ showInvite.value = false
|
|
|
+}
|
|
|
+
|
|
|
+const handleSubmit = async () => {
|
|
|
+ const query = inviteRef.value.getQuery()
|
|
|
+ if (!Object.keys(query).length) return
|
|
|
+ await saveInterviewInvite(query)
|
|
|
+ Snackbar.success('操作成功')
|
|
|
+ handleClose()
|
|
|
+ getData()
|
|
|
+}
|
|
|
+
|
|
|
+// 取消面试
|
|
|
+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('操作成功')
|
|
|
+ handleCancelClose()
|
|
|
+ getData()
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
-.fz14 { font-size: 14px; }
|
|
|
-.fz15 { font-size: 15px; }
|
|
|
-.fz16 { font-size: 16px; }
|
|
|
-.color666 { color: var(--color-666); }
|
|
|
-.color999 { color: var(--color-999); }
|
|
|
-.primaryColor { color: var(--v-primary-base); }
|
|
|
.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);
|