lifanagju_citu 7 місяців тому
батько
коміт
c67c5671a3

+ 0 - 41
src/views/recruit/enterprise/personnelManagement/components/commonStyle.vue

@@ -1,41 +0,0 @@
-<template>
-  <v-menu 
-    open-on-hover 
-    :close-delay="1" 
-    :open-delay="0" 
-    v-bind="$attrs" 
-    location="bottom" 
-    max-height="400"
-    :close-on-content-click="closeOnContentClick"
-  >
-    <template v-slot:activator="{ isActive, props }">
-      <v-btn
-        class="mr-3 py-0 px-2"
-        density="comfortable"
-        :append-icon="isActive ? 'mdi mdi-menu-up' : 'mdi mdi-menu-down'"
-        color="primary" variant="tonal"
-        v-bind="props"
-      >
-        {{ defineProps.btnTitle }}
-      </v-btn>
-    </template>
-    <slot></slot>
-  </v-menu>
-</template>
-<script setup>
-
-defineOptions({name: 'conditionFilter-index-page'})
-const defineProps = defineProps({
-  btnTitle: {
-    type: String,
-    default: 'Text'
-  },
-  closeOnContentClick: {
-    type: Boolean,
-    default: true
-  }
-})
-</script>
-
-<style lang="scss" scoped>
-</style>

+ 0 - 116
src/views/recruit/enterprise/personnelManagement/components/invite.vue

@@ -1,116 +0,0 @@
-<template>
-  <CtForm ref="CtFormRef" :items="formItems" style="height: 520px;">
-    <template #time="{ item }">
-      <VueDatePicker 
-        v-model="item.value"
-        placeholder="面试时间 *"
-        class="mb-4"
-        model-type="timestamp"
-        :disabled-dates="disabledDates"
-        :day-names="['一', '二', '三', '四', '五', '六', '七']"
-        select-text="确认"
-        cancel-text="取消"
-        :time-picker-inline="true"
-        locale="zh-CN"
-        :text-input="{ format: 'MM.dd.yyyy HH:mm' }" />
-    </template>
-  </CtForm>
-</template>
-
-<script setup>
-defineOptions({ name: 'formPage'})
-import { ref } from 'vue'
-
-const props = defineProps({
-  itemData: {
-    type: Object,
-    default: () => {}
-  }
-})
-
-// 过去的日期不可选
-const disabledDates = (date) => {
-  const currentDate = new Date()
-  currentDate.setDate(currentDate.getDate() - 1)
-  return date.getTime() < currentDate.getTime()
-}
-
-const CtFormRef = ref()
-const formItems = ref({
-  options: [
-    {
-      slotName: 'time',
-      key: 'time',
-      value: null,
-      rules: [v => !!v || '请选择面试时间'],
-    },
-    {
-      type: 'text',
-      key: 'position',
-      value: '',
-      noParam: true,
-      disabled: true,
-      label: '面试岗位'
-    },
-    {
-      type: 'text',
-      key: 'address',
-      value: '',
-      label: '面试地点 *',
-      rules: [v => !!v || '请输入面试地点'],
-    },
-    {
-      type: 'text',
-      key: 'invitePhone',
-      value: null,
-      label: '联系电话 *',
-      outlined: true,
-      rules: [v => !!v || '请填写联系电话']
-    },
-    {
-      type: 'textarea',
-      key: 'remark',
-      value: '',
-      label: '备注事项',
-      counter: 140,
-      rules: [
-        value => {
-          if (value?.length <= 140) return true
-          return '请输入备注事项,最多140字'
-        }
-      ]
-    }
-  ]
-})
-
-if (Object.keys(props.itemData).length) {
-  const obj = formItems.value.options.find(e => e.key === 'position')
-  const salary = props.itemData?.job?.payFrom && props.itemData?.job?.payTo ? `${props.itemData?.job?.payFrom ? props.itemData?.job?.payFrom + '-' : ''}${props.itemData?.job?.payTo}${props.itemData?.job?.payName ? '/' + props.itemData?.job?.payName : ''}` : '面议'
-  obj.value = `${props.itemData?.job?.name}${props.itemData?.job?.areaName ? '_' + props.itemData?.job?.areaName : ''} ${salary}`
-  formItems.value.options.find(e => e.key === 'address').value = props.itemData.job?.address
-}
-
-const getQuery = () => {
-  const obj = {
-    type: 1,
-    jobId: props.itemData.job.id,
-    userId: props.itemData.userId,
-    latitude: props.itemData.job?.latitude,
-    longitude: props.itemData.job?.longitude
-  }
-  formItems.value.options.forEach(item => {
-    if (item.noParam) return
-    obj[item.key] = item.value
-  })
-  return obj
-}
-
-defineExpose({
-  CtFormRef,
-  getQuery
-})
-</script>
-
-<style scoped lang="scss">
-
-</style>

+ 0 - 138
src/views/recruit/enterprise/personnelManagement/components/screen.vue

@@ -1,138 +0,0 @@
-<template>
-  <div class="d-flex align-center">
-    <CommonStyle v-for="(val, i) in list" :key="i" :btnTitle="val.title">
-      <v-list>
-        <v-list-item
-          v-for="(item, index) in val.items"
-          :key="index"
-          :active="val.selected.includes(item.value)"
-          color="primary"
-          :value="item.value"
-          @click="handleClick(item, val)"
-        >
-          <v-list-item-title>{{ item.label }}</v-list-item-title>
-        </v-list-item>
-      </v-list>
-    </CommonStyle>
-    <div class="mr-5" v-if="props.tab === 0">
-      <v-radio-group v-model="selected" inline style="height: 28px;" @update:modelValue="handleChangeSelected">
-        <v-radio v-model="selected" label="新投递" value="0" color="primary" hide-details density="compact" class="mr-3"></v-radio>
-        <v-radio v-model="selected" label="已查看" value="1" color="primary" hide-details density="compact"></v-radio>
-      </v-radio-group>
-    </div>
-    <span class="reset-text cursor-pointer ml-3" @click="handleReset">重置</span>
-  </div>
-</template>
-
-<script setup>
-defineOptions({ name: 'screen-page'})
-import { ref, watch } from 'vue'
-import { getJobAdvertised } from '@/api/enterprise'
-import { getDict } from '@/hooks/web/useDictionaries'
-import { dealDictArrayData } from '@/utils/position'
-import CommonStyle from './commonStyle.vue'
-
-const emit = defineEmits(['search', 'reset', 'select'])
-const props = defineProps({
-  tab: Number
-})
-
-const selected = ref()
-
-const list = ref([
-  {
-    title: '应聘岗位',
-    defaultTitle: '应聘岗位',
-    key: 'jobId',
-    selected: [],
-    api: getJobAdvertised,
-    items: []
-  },
-  {
-    title: '学历',
-    defaultTitle: '学历',
-    key: 'eduType',
-    dictTypeName: 'menduner_education_type',
-    selected: [],
-    items: []
-  },
-  {
-    title: '工作经验',
-    defaultTitle: '工作经验',
-    key: 'expType',
-    dictTypeName: 'menduner_exp_type',
-    selected: [],
-    items: []
-  },
-  {
-    title: '求职状态',
-    defaultTitle: '求职状态',
-    key: 'jobStatus',
-    dictTypeName: 'menduner_job_seek_status',
-    selected: [],
-    items: []
-  }
-])
-
-// 获取字典数据
-list.value.forEach(k => {
-  if (k.dictTypeName) {
-    getDict(k.dictTypeName).then(({ data }) => {
-      data = data?.length && data || []
-      k.items = data
-    })
-  }
-  if (k.api) {
-    k.api({ hire: false }).then(data => {
-      if (data.length) {
-        const list = dealDictArrayData([], data)
-        k.items = list.map(e => {
-          const salary = e.payFrom && e.payTo ? `${e.payFrom ? e.payFrom + '-' : ''}${e.payTo}${e.payName ? '/' + e.payName : ''}` : '面议'
-          return { label: `${e.name}${e.areaName ? '_' + e.areaName : ''} ${salary}`, value: e.id }
-        })
-      }
-    })
-  }
-})
-
-// 单击
-const handleClick = (item, val) => {
-  const obj = val.selected.includes(item.value)
-  val.selected = obj ? val.selected.filter(i => i !== item.value) : [item.value]
-  val.title = obj ? val.defaultTitle : item.label
-  emit('search', { key: val.key, value: obj ? '' : item.value })
-}
-
-// 重置
-const handleReset = () => {
-  list.value.map(e => {
-    e.selected = []
-    e.title = e.defaultTitle
-    return e
-  })
-  selected.value = ''
-  emit('reset')
-}
-
-// 新投递&已查看选择
-const handleChangeSelected = (e) => {
-  emit('select', e)
-}
-
-watch(
-  () => props.tab,
-  () => {
-    handleReset()
-  }
-)
-</script>
-
-<style scoped lang="scss">
-.reset-text {
-  font-size: 14px;
-  color: var(--color-666);
-  &:hover {
-    color: var(--v-primary-base);
-  }
-}
-</style>

+ 0 - 176
src/views/recruit/enterprise/personnelManagement/components/table.vue

@@ -1,176 +0,0 @@
-<template>
-  <div>
-    <v-data-table
-      class="mt-3"
-      :items="items"
-      :headers="headers"
-      hover
-      :disable-sort="true"
-      height="60vh"
-      no-data-text="暂无数据"
-      item-value="id"
-    >
-      <template #bottom></template>
-      <template v-slot:item.name="{ item }">
-        <div class="d-flex align-center cursor-pointer" @click="handleToPersonDetail(item)">
-          <v-badge
-            v-if="item?.person?.sex === '1' || item?.person?.sex === '2'"
-            bordered
-            offset-y="6"
-            :color="badgeColor(item)"
-            :icon="badgeIcon(item)">
-            <v-avatar size="40" :image="item.person.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
-          </v-badge>
-          <v-avatar v-else size="40" :image="item.person.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
-          <span class="defaultLink ml-3">{{ item?.person?.name }}</span>
-        </div>
-      </template>
-      <template v-slot:item.actions="{ item }">
-        <div v-if="tab === 0">
-          <v-btn color="primary" variant="text" @click="handlePreviewResume(item)">查看附件</v-btn>
-          <v-btn color="primary" variant="text" @click="handleInterviewInvite(item)">邀请面试</v-btn>
-        </div>
-        <v-btn v-if="tab === 0 || tab === 1" color="primary" variant="text" @click="handleEliminate(item)">不合适</v-btn>
-        <div v-if="tab === 1">
-          <v-btn color="primary" variant="text" @click="handleEnterByEnterprise(item)">入职</v-btn>
-        </div>
-        <v-btn v-if="tab === 3" color="primary" variant="text" @click="handleCancelEliminate(item)">取消不合适</v-btn>
-      </template>
-    </v-data-table>
-
-    <!-- 邀请面试 -->
-    <CtDialog :visible="showInvite" :widthType="2" titleClass="text-h6" title="面试信息" @close="handleEditClose" @submit="handleEditSubmit">
-      <InvitePage v-if="showInvite" ref="inviteRef" :itemData="itemData"></InvitePage>
-    </CtDialog>
-  </div>
-</template>
-
-<script setup>
-defineOptions({ name: 'table-page'})
-import { ref, computed, watch } from 'vue'
-import { previewFile } from '@/utils'
-import { personJobCvLook, joinEliminate, personEntryByEnterprise, personCvUnfitCancel } from '@/api/recruit/enterprise/personnel'
-import { saveInterviewInvite } from '@/api/recruit/enterprise/interview'
-import { useI18n } from '@/hooks/web/useI18n'
-import Snackbar from '@/plugins/snackbar'
-import InvitePage from './invite.vue'
-
-const { t } = useI18n()
-const emit = defineEmits(['refresh'])
-const props = defineProps({
-  tab: Number,
-  items: Array,
-  statusList: Array
-})
-const badgeColor = computed(() => (item) => {
-  return (item.person && item.person.sex) ? (item.person.sex === '1' ? '#1867c0' : 'error') : 'error'
-})
-
-const badgeIcon = computed(() => (item) => {
-  return (item.person && item.person.sex) ? (item.person.sex === '1' ? 'mdi-gender-male' : 'mdi-gender-female') : 'mdi-gender-female'
-})
-
-const inviteRef = ref()
-const showInvite = ref(false)
-const headers = ref([
-  { title: '姓名', value: 'name', sortable: false },
-  { title: '应聘职位', value: 'job.name', sortable: false },
-  { title: '求职状态', key: 'person.jobStatusName', sortable: false },
-  { title: '工作经验', key: 'person.expName', sortable: false },
-  { title: '最高学历', key: 'person.eduName', sortable: false },
-  { title: '岗位薪资', key: 'job', value: item => item.job.payFrom && item.job.payTo ? `${item.job.payFrom ? item.job.payFrom + '-' : ''}${item.job.payTo}${item.job.payName ? '/' + item.job.payName : ''}` : '面议', sortable: false },
-  { title: '状态', key: 'status', sortable: false, value: item => item.status ? props.statusList.find(i => i.value === item.status).label : '' },
-  { title: '操作', value: 'actions', sortable: false }
-])
-const unfit = { title: '类型', key: 'unfitType', sortable: false, value: item => item.type === '0' ? '简历不合适' : '面试不合适' }
-const delivery = { title: '类型', key: 'deliveryType', sortable: false, value: item => item.status === '0' ? '新投递' : '已查看' }
-
-const list = [0, 3]
-watch(
-  () => props.tab,
-  (val) => {
-    if (list.indexOf(val) !== -1) {
-      headers.value.splice(-1, 0, val === 0 ? delivery : unfit)
-    } else {
-      const index = headers.value.indexOf(item => item.key === val === 0 ? 'deliveryType' : 'unfitType')
-      if (index !== -1) headers.value.splice(index, 1)
-    }
-  },
-  { immediate: true }
-)
-
-// 人才详情
-const handleToPersonDetail = ({ userId, id }) => {
-  if (!userId || !id) return
-  window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}`)
-}
-
-// 入职
-const handleEnterByEnterprise = async (item) => {
-  if (!item.id) return
-  await personEntryByEnterprise(item.id)
-  Snackbar.success(t('common.operationSuccessful'))
-  emit('refresh')
-}
-
-// 不合适
-const handleEliminate = async (item) => {
-  if (!item.id || !item?.job?.id) return
-  const query = {
-    bizId: item.id,
-    jobId: item.job.id,
-    userId: item.userId,
-    type: props.tab === 0 ? '0' : '1' // 投递简历0 已邀约1
-  }
-  await joinEliminate(query)
-  Snackbar.success(t('common.operationSuccessful'))
-  emit('refresh')
-}
-
-// 取消不合适
-const handleCancelEliminate = async (item) => {
-  if (!item.id) return
-  await personCvUnfitCancel(item.id)
-  Snackbar.success(t('common.operationSuccessful'))
-  emit('refresh')
-}
-
-// 查看简历
-const handlePreviewResume = async ({ url, id }) => {
-  if (!url || !id) return
-  await personJobCvLook(id)
-  previewFile(url)
-}
-
-// 邀请面试
-const itemData = ref({})
-const handleInterviewInvite = (item) => {
-  itemData.value = item
-  showInvite.value = true
-}
-
-const handleEditClose = () => {
-  showInvite.value = false
-  itemData.value = {}
-}
-
-const handleEditSubmit = async () => {
-  const { valid } = await inviteRef.value.CtFormRef.formRef.validate()
-  if (!valid) return
-  const query = inviteRef.value.getQuery()
-  if (!query?.time) return Snackbar.warning('请选择面试时间')
-  await saveInterviewInvite(query)
-  Snackbar.success(t('common.operationSuccessful'))
-  handleEditClose()
-  emit('refresh')
-}
-</script>
-
-<style scoped lang="scss">
-:deep(.v-table > .v-table__wrapper > table > thead) {
-  background-color: #f7f8fa !important;
-}
-:deep(.v-selection-control__input) {
-  color: var(--v-primary-base) !important;
-}
-</style>

+ 0 - 129
src/views/recruit/enterprise/personnelManagement/index.vue

@@ -1,129 +0,0 @@
-<!-- 精英管理 -->
-<template>
-  <v-card class="pa-3 card-box">
-    <div class="d-flex justify-space-between">
-      <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f7f8fa">
-        <v-tab v-for="k in tabList" :value="k.value" :key="k.value">{{ k.label }}</v-tab>
-      </v-tabs>
-      <TextInput v-model="textItems.value" :item="textItems" @appendInnerClick="handleSearch" @enter="handleSearch"></TextInput>
-    </div>
-    <Screen :tab="tab" @search="handleScreen" @reset="handleScreenReset" @select="handleSelect"></Screen>
-
-    <v-window v-model="tab" class="mt-1">
-      <v-window-item v-for="k in tabList" :value="k.value" :key="k.value">
-        <TablePage :items="items" :tab="k.value" :statusList="statusList" @refresh="getList"></TablePage>
-        <CtPagination
-          v-if="total > 0"
-          :total="total"
-          :page="query.pageNo"
-          :limit="query.pageSize"
-          @handleChange="handleChangePage"
-        ></CtPagination>
-      </v-window-item>
-    </v-window>
-  </v-card>
-</template>
-
-<script setup>
-defineOptions({ name: 'enterprise-personnelManagement-management'})
-import { ref } from 'vue'
-import { getPersonCvPage } from '@/api/enterprise'
-import { personCvUnfitPage } from '@/api/recruit/enterprise/personnel'
-import { dealDictObjData } from '@/utils/position'
-import { getDict } from '@/hooks/web/useDictionaries'
-import { getInterviewInvitePage } from '@/api/recruit/enterprise/interview'
-import TablePage from './components/table.vue'
-import Screen from './components/screen.vue'
-
-const total = ref(0)
-const query = ref({
-  pageNo: 1,
-  pageSize: 10,
-  status: null,
-  type: 0
-})
-const tab = ref(0)
-const tabList = ref([
-  { label: '投递简历', value: 0, api: getPersonCvPage, status: null },
-  { label: '已邀约', value: 1, api: getInterviewInvitePage, status: '0' },
-  { label: '已入职', value: 2, api: getInterviewInvitePage, status: '2' },
-  { label: '不合适', value: 3, api: personCvUnfitPage },
-])
-const textItems = ref({
-  type: 'text',
-  value: '',
-  width: 250,
-  label: '搜索姓名',
-  clearable: true,
-  appendInnerIcon: 'mdi-magnify'
-})
-
-// 状态字典
-const statusList = ref([])
-getDict('menduner_interview_invite_status').then(({data}) => {
-  if (data && data.length) statusList.value = data
-})
-
-// 获取牛人列表
-const items = ref([])
-const getList = async () => {
-  const api = tabList.value[tab.value].api
-  if (tab.value !== 0) query.value.status = tabList.value[tab.value].status
-  const { list, total: number } = await api(query.value)
-  if (!list.length) {
-    items.value = []
-    total.value = 0
-    return
-  }
-  total.value = number
-  items.value = list.map(e => {
-    let obj = e
-    obj.person = Object.assign(e.person, dealDictObjData({}, e.person))
-    obj.job = Object.assign(e.job, dealDictObjData({}, e.job))
-    return obj
-  })
-}
-getList()
-
-// 分页
-const handleChangePage = (i) => {
-  query.value.pageNo = i
-  getList()
-}
-
-// 牛人姓名检索
-const handleSearch = () => {
-  if (textItems.value.value) query.value.name = textItems.value.value
-  else delete query.value.name
-  query.value.pageNo = 1
-  getList()
-}
-
-// 下拉筛选
-const handleScreen = ({ value, key }) => {
-  if (value) query.value[key] = value
-  else delete query.value[key]
-  getList()
-}
-
-// 下拉筛选重置
-const handleScreenReset = () => {
-  query.value = {
-    pageSize: 10,
-    pageNo: 1,
-    status: tab.value
-  }
-  if (tab.value === 0) query.value.status = null
-  if (textItems.value.value) query.value.name = textItems.value.value
-  getList()
-}
-
-const handleSelect = (e) => {
-  query.value.pageNo = 1
-  query.value.status = e
-  getList()
-}
-</script>
-
-<style scoped lang="scss">
-</style>

+ 8 - 25
src/views/recruit/enterprise/resume/components/invite.vue

@@ -1,21 +1,5 @@
 <template>
   <CtForm ref="CtFormRef" :items="formItems" style="height: 520px;">
-    <template #time="{ item }">
-      <VueDatePicker 
-        v-model="item.value"
-        placeholder="面试时间 *"
-        class="mb-4"
-        model-type="timestamp"
-        :time-picker-inline="true"
-        :disabled-dates="disabledDates"
-        :day-names="['一', '二', '三', '四', '五', '六', '七']"
-        select-text="确认"
-        cancel-text="取消"
-        locale="zh-CN"
-        :text-input="{ format: 'MM.dd.yyyy HH:mm' }"
-      >
-      </VueDatePicker>
-    </template>
   </CtForm>
 </template>
 
@@ -30,12 +14,6 @@ const props = defineProps({
   }
 })
 
-// 过去的日期不可选
-const disabledDates = (date) => {
-  const currentDate = new Date()
-  currentDate.setDate(currentDate.getDate() - 1)
-  return date.getTime() < currentDate.getTime()
-}
 
 const CtFormRef = ref()
 const formItems = ref({
@@ -53,10 +31,15 @@ const formItems = ref({
       ]
     },
     {
-      slotName: 'time',
+      type: 'datePicker',
+      mode: 'datetime',
+      labelWidth: 78,
       key: 'time',
-      value: null,
-      rules: [v => !!v || '请选择面试时间'],
+      label: '面试时间 *',
+      format: "YYYY/MM/DD HH:mm",
+      flexStyle: 'mb-7',
+      disabledDate: true,
+      rules: [v => !!v || '请选择面试时间']
     },
     {
       type: 'text',

+ 1 - 1
src/views/recruit/enterprise/resume/components/table.vue

@@ -42,7 +42,7 @@
     </v-data-table>
 
     <!-- 邀请面试 -->
-    <CtDialog :visible="showInvite" :widthType="2" titleClass="text-h6" title="面试信息" @close="handleEditClose" @submit="handleEditSubmit">
+    <CtDialog :visible="showInvite" :widthType="4" titleClass="text-h6" title="面试信息" @close="handleEditClose" @submit="handleEditSubmit">
       <InvitePage v-if="showInvite" ref="inviteRef" :itemData="itemData"></InvitePage>
     </CtDialog>
   </div>