Pārlūkot izejas kodu

Merge branch 'dev' of https://git.citupro.com/zhengnaiwen_citu/menduner into dev

zhengnaiwen_citu 9 mēneši atpakaļ
vecāks
revīzija
5c49206e4e
49 mainītis faili ar 609 papildinājumiem un 322 dzēšanām
  1. 7 0
      src/api/position.js
  2. 7 0
      src/api/recruit/enterprise/interview/index.js
  3. 8 2
      src/components/CtForm/index.vue
  4. 4 4
      src/components/Enterprise/components/positions.vue
  5. 29 9
      src/components/Enterprise/details.vue
  6. 20 3
      src/components/Enterprise/info.vue
  7. 1 0
      src/components/FormUI/autocomplete/index.vue
  8. 126 19
      src/components/FormUI/datePicker/index.vue
  9. 11 3
      src/components/Position/longStrip.vue
  10. 2 2
      src/components/Position/similarPositions.vue
  11. 2 1
      src/components/PositionLongStrip/item.vue
  12. 6 3
      src/hooks/web/useIM.js
  13. 2 1
      src/layout/company/navBar.vue
  14. 23 10
      src/layout/personal/navBar.vue
  15. 7 0
      src/styles/personal/navBar.scss
  16. 10 0
      src/utils/avatar.js
  17. 8 17
      src/utils/date.js
  18. 1 1
      src/utils/eventList.js
  19. 2 14
      src/utils/prefixUrl.js
  20. 25 25
      src/views/publicRecruitment/myRecommendation.vue
  21. 4 3
      src/views/recruit/components/message/components/chatting.vue
  22. 3 2
      src/views/recruit/components/message/index.vue
  23. 9 8
      src/views/recruit/enterprise/elite/components/table.vue
  24. 6 10
      src/views/recruit/enterprise/informationManagement/informationSettingsComponents/basicInfo.vue
  25. 4 7
      src/views/recruit/enterprise/informationManagement/informationSettingsComponents/businessInformation.vue
  26. 2 1
      src/views/recruit/enterprise/informationSetting/index.vue
  27. 2 1
      src/views/recruit/enterprise/interview/components/item.vue
  28. 71 44
      src/views/recruit/enterprise/interview/index.vue
  29. 7 0
      src/views/recruit/enterprise/memberCenter/myPoints/index.vue
  30. 4 5
      src/views/recruit/enterprise/positionManagement/components/baseInfo.vue
  31. 14 9
      src/views/recruit/enterprise/positionManagement/components/details.vue
  32. 5 1
      src/views/recruit/enterprise/positionManagement/index.vue
  33. 1 1
      src/views/recruit/enterprise/talentPool/components/filter.vue
  34. 11 6
      src/views/recruit/personal/PersonalCenter/components/interview/index.vue
  35. 31 15
      src/views/recruit/personal/PersonalCenter/components/interview/item.vue
  36. 2 1
      src/views/recruit/personal/PersonalCenter/components/interviewSchedule.vue
  37. 2 1
      src/views/recruit/personal/PersonalCenter/components/seenMe.vue
  38. 4 3
      src/views/recruit/personal/PersonalCenter/dynamic/left.vue
  39. 1 0
      src/views/recruit/personal/PersonalCenter/dynamic/right.vue
  40. 7 0
      src/views/recruit/personal/PersonalCenter/index.vue
  41. 13 1
      src/views/recruit/personal/home/index.vue
  42. 7 0
      src/views/recruit/personal/myWallet/index.vue
  43. 44 18
      src/views/recruit/personal/position/components/details.vue
  44. 12 18
      src/views/recruit/personal/remuse/components/basicInfo.vue
  45. 10 14
      src/views/recruit/personal/remuse/components/educationExp.vue
  46. 11 12
      src/views/recruit/personal/remuse/components/projectExperience.vue
  47. 10 12
      src/views/recruit/personal/remuse/components/trainingExperience.vue
  48. 10 14
      src/views/recruit/personal/remuse/components/workExperience.vue
  49. 1 1
      src/views/recruit/personal/shareJob/sendResume/simple.vue

+ 7 - 0
src/api/position.js

@@ -194,4 +194,11 @@ export const topJobAdvertised = async (id) => {
   return await request.post({
     url: `/app-admin-api/menduner/system/job-advertised/top?ids=${id}`
   })
+}
+
+// 招聘端-职位详情-认证情况
+export const getEnterpriseAuthDetails = async (enterpriseId) => {
+  return await request.get({
+    url: `/app-api/menduner/system/enterprise/get/auth?enterpriseId=${enterpriseId}`
+  })
 }

+ 7 - 0
src/api/recruit/enterprise/interview/index.js

@@ -45,4 +45,11 @@ export const feedbackInterviewInvite = async (data) => {
     url: '/app-admin-api/menduner/system/interview-invite/feedback',
     data
   })
+}
+
+// 获取当前企业有面试的日期列表
+export const getEnterpriseInterviewCountByTime = async () => {
+  return await request.get({
+    url: '/app-admin-api/menduner/system/interview-invite/get/count/by/time'
+  })
 }

+ 8 - 2
src/components/CtForm/index.vue

@@ -63,6 +63,13 @@
                 :item="item"
                 @change="handleChange(item)"
               ></nestedListGroupUI>
+              <datePickerUI
+                v-if="item.type === 'datePicker'"
+                v-model="item.value"
+                :item="item"
+                @change="handleChange(item)"
+              ></datePickerUI>
+              <DatePicker v-if="item.type === 'vueDatePicker'" v-model="item.value" :options="item.options" :width="item.width" :class="item.class"></DatePicker>
               <v-file-input
                 v-if="item.type === 'upload'"
                 :prepend-icon="item.prependIcon || ''"
@@ -90,8 +97,6 @@
                   <slot :name="item.selfAppend" :data="item.value"></slot>
                 </template>
               </v-file-input>
-              <DatePicker v-if="item.type === 'datePicker'" v-model="item.value" :options="item.options" :width="item.width" :class="item.class"></DatePicker>
-              
               <template v-if="item.slotName">
                 <slot :name="item.slotName" :item="item"></slot>
               </template>
@@ -114,6 +119,7 @@ import radioGroupUI from './../FormUI/radioGroup'
 import checkboxUI from './../FormUI/checkbox'
 import textareaUI from './../FormUI/textArea'
 import nestedListGroupUI from './../FormUI/nestedListGroup'
+import datePickerUI from './../FormUI/datePicker'
 import DatePicker from '@/components/DatePicker'
 import { ref, defineExpose } from 'vue'
 const emit = defineEmits(['change', 'inputUpdateAutocomplete'])// 定义一个或多个自定义事件

+ 4 - 4
src/components/Enterprise/components/positions.vue

@@ -58,7 +58,7 @@
           <div class="update-time">{{ timesTampChange(val.job.updateTime) }} 刷新</div>
         </div>
         <div v-else class="account-info">
-          <v-avatar :image="val.contact.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
+          <v-avatar :image="getUserAvatar(val.contact.avatar, val.contact.sex)"></v-avatar>
           <span class="account-label">{{ val.contact.name }} · {{ val.contact.postNameCn }}</span>
           <span>
             <v-btn class="half-button" color="primary" size="small" @click="toDetails(val)">立即沟通</v-btn>
@@ -83,7 +83,8 @@ import { useRoute, useRouter } from 'vue-router'
 import { timesTampChange } from '@/utils/date'
 import { getDict } from '@/hooks/web/useDictionaries'
 import { dealDictObjData } from '@/utils/position'
-import { prologue } from '@/hooks/web/useIM'
+import { prologue, defaultText } from '@/hooks/web/useIM'
+import { getUserAvatar } from '@/utils/avatar'
 import { getJobAdvertisedPositionCount, getJobAreaByEnterpriseId, getJobAdvertisedSearch } from '@/api/position'
 import MPagination from '@/components/CtPagination'
 import expType from '@/views/recruit/personal/position/components/conditionFilter/expType.vue'
@@ -210,8 +211,7 @@ const desc = [
 const toDetails = async (info) => {
   const userId = info.contact.userId
   const enterpriseId = info.contact.enterpriseId
-  const text = '您好,我对该职位很感兴趣,希望能有机会与您进一步沟通。'
-  await prologue({userId, enterpriseId, text})
+  await prologue({userId, enterpriseId, defaultText})
   let url = `/recruit/personal/message?id=${info.job.id}`
   if (info.contact.enterpriseId) {
     url += `&enterprise=${info.contact.enterpriseId}`

+ 29 - 9
src/components/Enterprise/details.vue

@@ -7,9 +7,16 @@
           <div class="ml-4">
             <div class="contact-name">
               {{ info.enterprise.name }}
-              <v-icon color="primary" size="24">mdi-shield-check</v-icon>
+              <v-icon :color="statusInfo.color" size="20">{{ statusInfo.mdi }}</v-icon>
+              <span :style="{'color': statusInfo.color,'font-size': '14px'}">{{ statusInfo.label }}</span>
+            </div>
+            <div class="contact-info">
+              {{ info.business.type }}
+              <span v-if="info.business.type && info.scaleName">·</span> 
+              {{ info.scaleName }}
+              <span v-if="info.industryName && info.scaleName">·</span> 
+              {{ info.industryName }}
             </div>
-            <div class="contact-info">{{ info.business.type }} · {{ info.scaleName }} · {{ info.industryName }}</div>
           </div>
         </div>
         <div class="float-right d-flex">
@@ -40,7 +47,7 @@
           <div class="content-right">
             <div class="welfare">
               <h4>工作时间及福利</h4>
-              <div class="my-3" style="color: var(--color-666);font-size: 14px;">
+              <div v-if="info.enterprise?.workTime" class="my-3" style="color: var(--color-666);font-size: 14px;">
                 <v-icon size="17" color="#ccc" class="mr-2">mdi-clock</v-icon>{{ info.enterprise.workTime }}
               </div>
               <div class="welfare-tags">
@@ -49,13 +56,10 @@
             </div>
             <div class="welfare my-3">
               <h4>工商信息</h4>
-              <div :class="['mt-2', 'business-item']" v-for="(val, index) in businessList" :key="val.value">
+              <div :class="['mt-2', 'business-item']" v-for="val in businessList" :key="val.value">
                 <div>{{ val.label }}</div>
                 <div class="business-value ellipsis">{{ info.business[val.value] }}</div>
-                <div :class="['my-3', {'border-bottom-dashed': index === businessList.length - 1 }]"></div>
-              </div>
-              <div class="business-source">
-                数据来源:企查查
+                <div :class="['my-3']"></div>
               </div>
             </div>
           </div>
@@ -67,9 +71,10 @@
 
 <script setup>
 defineOptions({ name: 'enterprise-details'})
-import { ref } from 'vue'
+import { ref, computed } from 'vue'
 import EnterpriseIntroduction from './components/introduction.vue'
 import recruitmentPositions from './components/positions.vue'
+import { getEnterpriseAuthDetails } from '@/api/position'
 import { getEnterpriseDetails, getEnterpriseSubscribeCheck, getEnterpriseSubscribe, getEnterpriseUnsubscribe, enterpriseClick } from '@/api/enterprise'
 import { timesTampChange } from '@/utils/date'
 import { dealDictObjData } from '@/utils/position'
@@ -101,8 +106,16 @@ const handleEnterpriseClick = async () => {
 }
 handleEnterpriseClick()
 
+const statusList = [
+  { label: '未认证', color: '#fb8c00', value: null, mdi: 'mdi-shield-remove' },
+  { label: '审核中', color: '#fb8c00', value: '0', mdi: 'mdi-shield-half-full' },
+  { label: '已认证', color: 'var(--v-primary-base)', value: '1', mdi: 'mdi-shield-check' },
+  { label: '已驳回', color: '#fe574a', value: '2', mdi: 'mdi-shield-off' }
+]
+
 // 企业详情
 const info = ref({})
+const authInfo = ref({})
 const getDetails = async () => {
   if (!props.id) return
   const data = await getEnterpriseDetails({ id: props.id })
@@ -111,9 +124,16 @@ const getDetails = async () => {
 
   info.value = { ...data, ...dealDictObjData({}, data.enterprise) }
   getCollectionStatus(props.id)
+  // 企业实名认证信息
+  authInfo.value = await getEnterpriseAuthDetails(props.id)
 }
 getDetails()
 
+const statusInfo = computed(() => {
+  const obj = (authInfo.value && Object.keys(authInfo.value).length) ? statusList.find(e => e.value === authInfo.value.status) : statusList[0]
+  return obj
+})
+
 // 效验求职者是否关注该企业
 const isCollection = ref(false)
 const getCollectionStatus = async (id) => {

+ 20 - 3
src/components/Enterprise/info.vue

@@ -5,8 +5,8 @@
       <v-img class="float-left" :src="props.info.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :width="45" height="45"></v-img>
       <div class="ml-3 float-left">
         <p class="enterprise-name cursor-pointer" @click="handleEnterprise(0)">{{ props.info.enterprise.anotherName }}</p>
-        <v-icon color="primary" size="20">mdi-shield-check</v-icon> <!-- mdi-shield-remove -->
-        <span style="color: var(--v-primary-base);font-size: 14px;">已认证</span>
+        <v-icon :color="statusInfo.color" size="20">{{ statusInfo.mdi }}</v-icon>
+        <span :style="{'color': statusInfo.color,'font-size': '14px'}">{{ statusInfo.label }}</span>
       </div>
     </div>
     <div class="mt-3 border-bottom-dashed" style="font-size: 14px;">
@@ -23,8 +23,10 @@
 
 <script setup>
 defineOptions({ name: 'enterprise-info' })
-import { ref } from 'vue'
+import { ref, computed } from 'vue'
 import { dealDictObjData } from '@/utils/position'
+import { getEnterpriseAuthDetails } from '@/api/position'
+
 const props = defineProps({
   info: {
     type: Object,
@@ -37,13 +39,28 @@ const list = [
   { icon: 'mdi-account-multiple', label: 'scaleName' },
   { icon: 'mdi-family-tree', label: 'industryName' }
 ]
+const statusList = [
+  { label: '未认证', color: '#fb8c00', value: null, mdi: 'mdi-shield-remove' },
+  { label: '审核中', color: '#fb8c00', value: '0', mdi: 'mdi-shield-half-full' },
+  { label: '已认证', color: 'var(--v-primary-base)', value: '1', mdi: 'mdi-shield-check' },
+  { label: '已驳回', color: '#fe574a', value: '2', mdi: 'mdi-shield-off' }
+]
+
 const obj = ref({})
+const authInfo = ref({})
 const getData = async () => {
   const prise = props.info.enterprise
   obj.value = dealDictObjData({}, prise)
+  // 企业实名认证信息
+  authInfo.value = await getEnterpriseAuthDetails(props.info.enterprise.id)
 }
 getData()
 
+const statusInfo = computed(() => {
+  const obj = (authInfo.value && Object.keys(authInfo.value).length) ? statusList.find(e => e.value === authInfo.value.status) : statusList[0]
+  return obj
+})
+
 const handleEnterprise = (val) => {
   const key = val ? 'recruitmentPositions' : 'briefIntroduction'
   window.open(`/recruit/personal/company/details/${props.info.enterprise.id}?key=${key}`)

+ 1 - 0
src/components/FormUI/autocomplete/index.vue

@@ -20,6 +20,7 @@
       :hide-no-data="item.hideNoData"
       :no-data-text="item.noDataText || 'No data available'"
       :hide-selected="item.hideSelected"
+      :hide-details="item.hireDetails || false"
       @update:search="v => item.search ? debouncedCallbackSearch(v) : null"
       @update:modelValue="modelValueUpDate"
     ></v-autocomplete>

+ 126 - 19
src/components/FormUI/datePicker/index.vue

@@ -1,38 +1,145 @@
 <template>
   <div :style="{ width: item.width ? item.width + 'px' : '100%' }">
-    <v-date-input
+    <VueDatePicker
       v-model="value"
-      :label="item.label || '请选择'" 
-      variant="outlined"
-      prepend-icon=""
-      prepend-inner-icon="$calendar"
-      view-mode="month"
-      color="primary"
-      density="compact"
-      :disabled="item.disabled"
-      :rules="item.rules"
-      :hide-actions="true"
+      ref="datepicker"
+      :options="item.options || {}"
+      locale="zh-CN"
+      :disabled="item.disabled || false"
+      :range="item.range || false"
+      :model-type="timestamp"
+      :month-picker="month"
+      :time-picker="time"
+      :year-picker="year"
+      auto-apply
+      text-input
+      :show-now-button="item.showToday"
+      now-button-label="今天"
+      :enable-time-picker="item.enableTimePicker ?? false"
+      :clearable="item.clearable ?? true"
+      :day-names="['一', '二', '三', '四', '五', '六', '七']"
+      v-bind="$attrs"
+      :class="{'detailMargin': detailMargin}"
+      @open="handleOpen"
+      @closed="handleClosed"
       @update:modelValue="modelValueUpDate"
-    ></v-date-input>
+    >
+      <template #trigger>
+        <v-text-field
+          v-model="formatText"
+          variant="outlined"
+          :density="item.dense || 'compact'"
+          type="text"
+          :rules="rules"
+          :disabled="item.disabled"
+          :style="{width: item.width}"
+          :color="item.color || 'primary'"
+          :label="item.label"
+          :placeholder="item.placeholder || item.label"
+          :autofocus="item.autofocus"
+          :required="item.required"
+          :suffix="item.suffix"
+          :append-icon="item.appendIcon"
+          :append-inner-icon="item.appendInnerIcon"
+          :clearable="item.clearable"
+          :readonly="item.readonly"
+          :counter="item.counter"
+          :prepend-inner-icon="item.prependInnerIcon"
+          hide-spin-buttons
+          :class="item.class"
+          :hide-details="hideDetails || false"
+          @click:clear="handleClear"
+          @click="inputClick"
+          @blur="inputBlur"
+        ></v-text-field>
+      </template>
+    </VueDatePicker>
   </div>
 </template>
 <script setup>
-import { ref, watch } from 'vue';
-defineOptions({ name:'FormUI-v-date-input'})
+import { timesTampChange } from '@/utils/date'
+import { computed, ref, watch } from 'vue'
+defineOptions({ name:'FormUI-v-text-field'})
 
-const props = defineProps({item: Object, modelValue: String})
-const emit = defineEmits(['update:modelValue'])
+const props = defineProps({item: Object})
+const emit = defineEmits(['update:modelValue', 'change'])
 const item = props.item
+
 const value = ref(props.modelValue)
 
-watch(() => props.modelValue, (newVal) => {
-  value.value = newVal
+watch(() => item.value, (newVal) => {
+  value.value = newVal - 0
+  if (value.value) getFormatText()
 })
+
+const timestamp = 'timestamp' // 固定不能变
+// const datepicker = ref()
+const formatText = ref('')
+
 const modelValueUpDate = (val) => {
   value.value = val
+  getFormatText()
   emit('update:modelValue', value.value)
+  emit('change', value.value)
 }
+
+const getFormatText = () => {
+  const format = item.format || 'Y-M-D'
+  formatText.value = timesTampChange(value.value, format)
+}
+
+const handleClear = () => {
+  value.value = null
+  emit('change', value.value)
+}
+
+const rules = ref(item.rules)
+const handleOpen = () => {
+  rules.value = []
+}
+const handleClosed = () => {
+  rules.value = item.rules
+}
+
+const hideDetails = ref(item.hideDetails || false)
+const detailMargin = ref(false)
+const inputClick = () => {
+  if (item.hideDetails) return
+  hideDetails.value = true
+  detailMargin.value = true
+}
+// const inputFocus = () => {
+//   if (item.hideDetails) return
+//   hideDetails.value = true
+//   detailMargin.value = true
+// }
+const inputBlur = () => {
+  if (item.hideDetails) return
+  hideDetails.value = item.hideDetails || false
+  detailMargin.value = false
+}
+
+// dateType: 默认 date, 即年月日
+const year = computed(() => {
+  return item.dateType === 'year'
+})
+const month = computed(() => {
+  return item.dateType === 'month'
+})
+const time = computed(() => {
+  return item.dateType === 'time'
+})
+
+if (!item.format) item.format = year.value ? 'Y' : month.value ? 'Y-M' : time.value ? 'Y-M-D h:m:s' : 'Y-M-D'
+if (item.value) value.value = item.value - 0; getFormatText()
 </script>
 <style lang="scss" scoped>
-
+// .removeDetailHeight {}
+::v-deep .dp--menu-wrapper {
+  // top: 50px !important;
+  left: 0 !important;
+}
+.detailMargin {
+  margin-bottom: 22px;
+}
 </style>

+ 11 - 3
src/components/Position/longStrip.vue

@@ -3,11 +3,10 @@
     <div class="position-item mb-3 job-closed" v-for="(val, i) in props.items" :key="i" @mouseenter="val.active = true" @mouseleave="val.active = false">
       <div class="info-header">
         <div v-if="val.active" class="header-btn">
-          <!-- <v-btn class="half-button" color="primary" size="small">继续沟通</v-btn> -->
           <v-btn v-if="props.showCancelBtn" class="half-button ml-3" color="primary" size="small" @click="handleCancel(val)">取消感兴趣</v-btn>
         </div>
         <div class="img-box">
-          <v-avatar :image="val.contact.avatar || 'https://minio.citupro.com/dev/menduner/7.png'" size="x-small"></v-avatar>
+          <v-avatar :image="getUserAvatar(val.contact.avatar, val.contact.sex)" size="x-small"></v-avatar>
           <span class="name">
             <span class="mx-3">{{ val.contact.name }}</span>
             <span class="gray">{{ val.contact.postNameCn }}</span>
@@ -17,7 +16,7 @@
       <div class="info-content">
         <div class="job-info">
           <div class="job-name cursor-pointer">
-            <span class="mr-3 info-name">{{ val.job.name }}</span>
+            <span class="mr-3 info-name" @click="handleToPositionDetails(val)">{{ val.job.name }}</span>
             <span v-if="val?.job?.areaName">[{{ val.job.areaName }}]</span>
           </div>
           <div class="job-other">
@@ -45,6 +44,8 @@ defineOptions({ name: 'longStrip'})
 import { getPersonJobUnfavorite } from '@/api/position'
 import { useI18n } from '@/hooks/web/useI18n'
 import Snackbar from '@/plugins/snackbar'
+import { getUserAvatar } from '@/utils/avatar'
+import { useRouter } from 'vue-router'
 
 const emits = defineEmits(['refresh'])
 const { t } = useI18n()
@@ -59,6 +60,8 @@ const props = defineProps({
   }
 })
 
+const router = useRouter()
+
 const desc = ['industryName', 'financingName', 'scaleName']
 
 const handleCancel = async (item) => {
@@ -67,6 +70,11 @@ const handleCancel = async (item) => {
   emits('refresh')
   Snackbar.success(t('common.operationSuccessful'))
 }
+
+// 职位详情
+const handleToPositionDetails = (item) => {
+  router.push(`/recruit/personal/position/details/${item.job.id}`)
+}
 </script>
 
 <style scoped lang="scss">

+ 2 - 2
src/components/Position/similarPositions.vue

@@ -10,9 +10,9 @@
         <span class="float-right enterprise-address">{{ item.areaName }}</span>
       </div>
     </div>
-    <div class="text-center more-btn">
+    <!-- <div class="text-center more-btn">
       <v-btn color="primary" variant="outlined" class="buttons" :to="`/recruit/personal/company/details/${props.info.enterpriseId}?key=recruitmentPositions`">{{ $t('position.allBtn') }}</v-btn>
-    </div>
+    </div> -->
   </div>
 </template>
 

+ 2 - 1
src/components/PositionLongStrip/item.vue

@@ -57,7 +57,7 @@
         </template>
       </div>
       <div class="footer-right">
-        <v-avatar size="x-small" :image="item.contact.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
+        <v-avatar size="x-small" :image="getUserAvatar(item.contact.avatar, item.contact.sex)"></v-avatar>
         <span class="mx-2 textColor666">
           {{ item.contact.name }}
           <span v-if="item?.contact?.postNameCn">|</span>
@@ -72,6 +72,7 @@
 import { commissionCalculation } from '@/utils/position'
 defineOptions({ name: 'long-strip-position-card-item' })
 import { ref, watch } from 'vue'
+import { getUserAvatar } from '@/utils/avatar'
 const props = defineProps({
   items: {
     type: Object,

+ 6 - 3
src/hooks/web/useIM.js

@@ -21,6 +21,9 @@ import {
   // Message, StreamItem, ChannelTypeGroup, MessageStatus, SyncOptions, MessageExtra, MessageContent
 } from "wukongimjssdk"
 
+// 默认招呼语
+export const defaultText = '您好,关注到您发布该职位信息,请问有机会与您进一步沟通吗?'
+
 const { ObjectContent } = initRegister(101)
 const { ObjectContent: ObjectContent2 } = initRegister(102)
 const { ObjectContent: ObjectContent3 } = initRegister(103)
@@ -37,7 +40,7 @@ const contentType = {
 }
 
 // 注册消息体
-function initRegister (contentType) {
+function initRegister (type) {
   class ObjectContent extends MessageContent {
     constructor(text) {
       super();
@@ -49,7 +52,7 @@ function initRegister (contentType) {
     }
     get contentType() {
         // 这里需要实现具体的逻辑
-        return contentType; // 示例实现
+        return type; // 示例实现
     }
     decodeJSON(content) {
         this.content = content.text;
@@ -61,7 +64,7 @@ function initRegister (contentType) {
     }
   }
   // 注册101类型为面试
-  WKSDK.shared().register(contentType, () => new ObjectContent())
+  WKSDK.shared().register(type, () => new ObjectContent())
   return {
     ObjectContent
   }

+ 2 - 1
src/layout/company/navBar.vue

@@ -32,7 +32,7 @@
               <template v-slot:activator="{ props }">
                 <div class="d-flex ml-5 pl-2 align-center cursor-pointer" v-bind="props">
                   <v-avatar>
-                    <v-img alt="" :src="baseInfo?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-img>
+                    <v-img alt="" :src="getUserAvatar(baseInfo?.avatar, baseInfo?.sex)"></v-img>
                   </v-avatar>
                   <div class="ml-2">{{ baseInfo?.name ?? $t('sys.tourist') }}</div>
                 </div>
@@ -99,6 +99,7 @@ import { useUserStore } from '@/store/user'; const userStore = useUserStore()
 import { useRouter } from 'vue-router'; const router = useRouter()
 import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
 import MessageNotification from '../message.vue'
+import { getUserAvatar } from '@/utils/avatar'
 defineOptions({ name: 'personal-navbar' })
 
 defineProps({

+ 23 - 10
src/layout/personal/navBar.vue

@@ -10,17 +10,17 @@
           <div class="nav-logo" style="cursor: pointer;" @click="handleLogoClick">
             <v-img src="../../assets/logo.png"  aspect-ratio="16/9" cover :width="90" style="height: 40px"></v-img>
           </div>
-          <div class="nav-city">
+          <!-- <div class="nav-city">
             <p class="nav-city-box">
               <v-icon color="primary">mdi-map-marker</v-icon>
               <span class="nav-city-selected">广州</span>
               <span class="switchover-city nav-city-selected">[{{ $t('sys.switchCities') }}]</span>
             </p>
-          </div>
+          </div> -->
           <div class="nav">
             <ul>
-              <li v-for="k in list" :key="k.text">
-                <a :href="k.path" style="font-size: 14px;">{{ k.text }}</a>
+              <li v-for="(k, listIndex) in list" :key="k.text" :class="{'routeActive': routeActive === listIndex}">
+                <a :href="k.path" style="font-size: 14px;" class="aLink">{{ k.text }}</a>
               </li>
             </ul>
           </div>
@@ -42,7 +42,7 @@
               <template v-slot:activator="{ props }">
                 <div class="d-flex ml-3 align-center cursor-pointer" v-bind="props" @click="handleToPersonalCenter">
                   <v-avatar>
-                    <v-img alt="John" :src="baseInfo?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-img>
+                    <v-img alt="John" :src="getUserAvatar(baseInfo?.avatar, baseInfo?.sex)"></v-img>
                   </v-avatar>
                   <div class="ml-3">{{ baseInfo?.name || $t('sys.tourist') }}</div>
                 </div>
@@ -93,7 +93,7 @@
             >
               <v-btn class="ml-1" size="small" icon="mdi-bell-outline" @click="router.push({ path: '/recruit/personal/message' })"></v-btn>
             </v-badge> -->
-            <message-notification v-if="showBall" path="/recruit/personal/message"></message-notification>
+            <message-notification v-if="showBall" :path="paths[3]" :class="{'routeActive': routeActive === 3}"></message-notification>
             
           </div>
         </div>
@@ -110,15 +110,17 @@
 </template>
 
 <script setup>
-import { onMounted, ref } from 'vue'
+import { computed, onMounted, ref } from 'vue'
 import { getToken } from '@/utils/auth'
 import { useUserStore } from '@/store/user'
 // import { useLocaleStore } from '@/store/locale'
 import { useI18n } from '@/hooks/web/useI18n'
 import CtDialog from '@/components/CtDialog'
+import { useRoute } from 'vue-router'; const route = useRoute()
 import { useRouter } from 'vue-router'; const router = useRouter()
 import { getUserBindEnterpriseList, getUserRegisterEnterpriseApply } from '@/api/personal/user'
 import MessageNotification from '../message.vue'
+import { getUserAvatar } from '@/utils/avatar'
 
 // import { useIMStore } from '@/store/im'
 defineOptions({ name: 'personal-navbar' })
@@ -144,12 +146,23 @@ const { t } = useI18n()
 // const localeStore = useLocaleStore()
 const userStore = useUserStore()
 
+const paths = [
+  '/recruitHome',
+  '/recruit/personal/position',
+  '/recruit/personal/company',
+  '/recruit/personal/message',
+  ]
 const list = ref([
-  { text: t('common.home'), path: '/recruitHome' },
-  { text: t('common.position'), path: '/recruit/personal/position' },
-  { text: t('common.company'), path: '/recruit/personal/company' }
+  { text: t('common.home'), path: paths[0] },
+  { text: t('common.position'), path: paths[1] },
+  { text: t('common.company'), path: paths[2] }
 ])
 
+const routeActive = computed(() => {
+  const index = paths.findIndex(item => item === route.path)
+  return index
+})
+
 const handleLogoClick = () => { window.open('/') } // 点击logo
 const handleSubmit = () => { toEnterprise(radios.value) }
 

+ 7 - 0
src/styles/personal/navBar.scss

@@ -92,6 +92,13 @@
   text-decoration: none;
   color: var(--v-primary-base);
 }
+.routeActive { // 选中样式
+  color: var(--v-error-base) !important;
+  .aLink {
+    color: var(--v-error-base) !important;
+    text-decoration: underline !important; // 多个下划线
+  }
+}
 .user-nav {
   // position: absolute;
   // right: 0;

+ 10 - 0
src/utils/avatar.js

@@ -0,0 +1,10 @@
+const male = 'https://minio.citupro.com/dev/menduner/11.png'
+const female = 'https://minio.citupro.com/dev/menduner/7.png'
+
+// 根据性别返回默认头像
+export const getUserAvatar = (avatar, sex) => {
+  if (avatar) return avatar
+  if (!sex) return female
+  if (sex === '1') return male
+  else return female
+}

+ 8 - 17
src/utils/date.js

@@ -41,25 +41,16 @@ export const getDayBounds = (timestamp) => {
 }
 
 // 传入 Wed May 01 2024 00:00:00 GMT+0800 (中国标准时间) 输出 [2024-07-18 00:00:00, 2024-07-18 23:59:59]
-export const getStartAndEndOfDay = (dateString) => {
-  const date = new Date(dateString + ' UTC')
-  if (isNaN(date.getTime())) {
-    throw new Error('Invalid date string')
-  }
-
-  const startDate = new Date(date.getFullYear(), date.getMonth(), date.getDate())
-  const endDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59)
+export const getStartAndEndOfDay = (dateTimeStr) => {
+  const date = new Date(dateTimeStr)
 
-  function formatDate(dateObj) {
-    let month = ('0' + (dateObj.getMonth() + 1)).slice(-2)
-    let day = ('0' + dateObj.getDate()).slice(-2)
-    let hours = ('0' + dateObj.getHours()).slice(-2)
-    let minutes = ('0' + dateObj.getMinutes()).slice(-2)
-    let seconds = ('0' + dateObj.getSeconds()).slice(-2)
-    return dateObj.getFullYear() + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
-  }
+  const year = date.getFullYear()
+  const month = String(date.getMonth() + 1).padStart(2, '0')
+  const day = String(date.getDate()).padStart(2, '0')
 
-  return [formatDate(startDate), formatDate(endDate)]
+  const startTime = `${year}-${month}-${day} 00:00:00`
+  const endTime = `${year}-${month}-${day} 23:59:59`
+  return [startTime, endTime]
 }
 
 // 传入一组时间戳,返回 [最早时间点,最晚时间点]

+ 1 - 1
src/utils/eventList.js

@@ -29,7 +29,7 @@ export const updateEventList = (type) => {
       // 更新账户信息
       if (type) await store.getUserAccountInfo()
       else await store.getEnterpriseUserAccountInfo()
-    }, 300000)
+    }, 200000)
 
   } else {
     clearInterval(timer.value)

+ 2 - 14
src/utils/prefixUrl.js

@@ -22,21 +22,9 @@ export const getSuffixAfterPrefix = (str) => {
 // 展示积分
 export function showNextAction (list) { // , currentIndex = 0
   const arr = list.reduce((newArr, action) => {
-    if (action.match) newArr.push(`+${action.point}  恭喜您【${action.title}】获得${action.point}积分`)
+    // if (action.match) newArr.push(`+${action.point}  恭喜您【${action.title}】获得${action.point}积分`)
+    if (action.match) newArr.push(`+${action.point}  恭喜您获得${action.point}积分`)
     return newArr
   }, [])
   if (arr?.length) Curtain('point', { list: arr })
-  // if (currentIndex < list.length) {
-  //   const action = list[currentIndex]
-  //   if (action.match) {
-  //     Snackbar.point(`+${action.point}  恭喜您${action.title}获得${action.point}积分`)
-  //     setTimeout(() => {
-  //       showNextAction(list, currentIndex + 1)
-  //     }, 3000)
-  //   } else {
-  //     setTimeout(() => {
-  //       showNextAction(list, currentIndex + 1)
-  //     }, 0)
-  //   }
-  // }
 }

+ 25 - 25
src/views/publicRecruitment/myRecommendation.vue

@@ -12,14 +12,14 @@
         </div>
         <div class="topTip">推荐好友入职得赏金</div>
         <!-- 数据 -->
-        <!-- <TablePage :items="items"></TablePage>
+        <TablePage :items="items"></TablePage>
         <CtPagination
           v-if="total > 0"
           :total="total"
           :page="query.pageNo"
           :limit="query.pageSize"
           @handleChange="handleChangePage"
-        ></CtPagination> -->
+        ></CtPagination>
       </div>
     </div>
     <!-- 滚动区域 -->
@@ -36,9 +36,9 @@ import { getDict } from '@/hooks/web/useDictionaries'
 import { useUserStore } from '@/store/user'
 import { 
   getHireJobCvRelCount, 
-  // getHireJobCvRelPage
+  getHireJobCvRelPage
 } from '@/api/publicRecruitment'
-// import TablePage from './components/table.vue'
+import TablePage from './components/table.vue'
 import bountyDisplay from './components/bountyDisplay.vue'
 
 const active = ref(0)
@@ -63,18 +63,18 @@ const getData = async () => {
 }
 
 // 列表
-// const items = ref([])
-// const total = ref(0)
-// const query = ref({
-//   pageSize: 10,
-//   pageNo: 1,
-//   status: null
-// })
-// const getTableList = async () => {
-//   const res = await getHireJobCvRelPage(query.value)
-//   items.value = res.list
-//   total.value = res.total
-// }
+const items = ref([])
+const total = ref(0)
+const query = ref({
+  pageSize: 10,
+  pageNo: 1,
+  status: null
+})
+const getTableList = async () => {
+  const res = await getHireJobCvRelPage(query.value)
+  items.value = res.list
+  total.value = res.total
+}
 
 // 状态
 const getStatusData = () => {
@@ -83,25 +83,25 @@ const getStatusData = () => {
     statisticsList.value = data.map(e => {
       return { ...e, count: 0 }
     })
-    // query.value.status = data[0].value
+    query.value.status = data[0].value
     getData()
-    // getTableList()
+    getTableList()
   })
 }
 getStatusData()
 
 // 分页
-// const handleChangePage = (e) => {
-//   query.value.pageNo = e
-//   getTableList()
-// }
+const handleChangePage = (e) => {
+  query.value.pageNo = e
+  getTableList()
+}
 
 // 钻取
 const handleStatisticsItem = (item, index) => {
   active.value = index
-  // query.value.pageNo = 1
-  // query.value.status = item.value
-  // getTableList()
+  query.value.pageNo = 1
+  query.value.status = item.value
+  getTableList()
 }
 </script>
 

+ 4 - 3
src/views/recruit/components/message/components/chatting.vue

@@ -63,7 +63,7 @@
             <v-card
               color="teal"
               variant="tonal"
-              class="mx-auto"
+              class="mx-auto mb-5"
               width="400"
               min-height="150"
               :elevation="3"
@@ -101,7 +101,7 @@
             <div style="width: 40px; height: 40px;">
               <v-avatar>
                 <v-img
-                  :src="(val.from_uid === IM.uid ? mAvatar : info.avatar) || 'https://minio.citupro.com/dev/menduner/7.png'"
+                  :src="(val.from_uid === IM.uid ? mAvatar : getUserAvatar(info.avatar, info.sex)) || 'https://minio.citupro.com/dev/menduner/7.png'"
                   :width="40"
                   height="40"
                   rounded
@@ -228,6 +228,7 @@ import { useI18n } from '@/hooks/web/useI18n'
 import { useRouter } from 'vue-router';
 import { getDict } from '@/hooks/web/useDictionaries'
 // import { getDictValueWithLabel } from '@/utils/position'
+import { getUserAvatar } from '@/utils/avatar'
 
 import { useUserStore } from '@/store/user'
 const isEnterprise = inject('isEnterprise')
@@ -264,7 +265,7 @@ const IM = useIMStore()
 const userStore = useUserStore()
 const loading = ref(false)
 
-const mAvatar = userStore.baseInfo?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'
+const mAvatar = getUserAvatar(userStore.baseInfo?.avatar, userStore.baseInfo?.sex)
 
 const chatRef = ref()
 const inputVal = ref('')

+ 3 - 2
src/views/recruit/components/message/index.vue

@@ -39,14 +39,14 @@
               color="primary"
               class="mb-2"
               :active="val.channel.channelID === info?.channel?.channelID"
-              :title="val.userInfoVo ? val.userInfoVo.userInfoResp.name : '系统消息'"
+              :title="val.userInfoVo ? (val.userInfoVo.userInfoResp.name ? val.userInfoVo.userInfoResp.name : '游客') : '系统消息'"
               :subtitle="timesTampChange(+val.timestamp.padEnd(13, '0'))"
             >
               <template v-slot:subtitle="{ subtitle }">
                 <div class="mt-2">{{ subtitle }}</div>
               </template>
               <template v-slot:prepend>
-                <v-avatar :image="val.userInfoVo ? val.userInfoVo.userInfoResp.avatar : 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
+                <v-avatar :image="getUserAvatar(val?.userInfoVo?.userInfoResp?.avatar, val?.userInfoVo?.userInfoResp?.sex)"></v-avatar>
               </template>
               <template v-slot:append>
                 <v-badge
@@ -138,6 +138,7 @@ import { getUserInfo } from '@/api/personal/user'
 import { useIMStore } from '@/store/im'
 import { useUserStore } from '@/store/user'
 import Snackbar from '@/plugins/snackbar'
+import { getUserAvatar } from '@/utils/avatar'
 
 import { getJobAdvertised } from '@/api/enterprise'
 import { dealDictArrayData } from '@/utils/position'

+ 9 - 8
src/views/recruit/enterprise/elite/components/table.vue

@@ -17,7 +17,7 @@
             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-avatar size="40" :image="getUserAvatar(item.person.avatar, item.person.sex)"></v-avatar>
           </v-badge>
           <span class="defaultLink ml-3">{{ item?.person?.name }}</span>
         </div>
@@ -28,7 +28,7 @@
       </template>
       <template v-slot:item.actions="{ item }">
         <v-btn v-if="tab === 0" color="primary" variant="text" @click="handlePreviewResume(item)">查看附件</v-btn>
-        <v-btn v-if="tab === 0" color="primary" variant="text" @click="handleInterviewInvite(item)">邀请面试</v-btn>
+        <!-- <v-btn v-if="tab === 0" color="primary" variant="text" @click="handleInterviewInvite(item)">邀请面试</v-btn> -->
         <v-btn v-if="tab === 0 || tab === 1" color="primary" variant="text" @click="handleEliminate(item)">不合适</v-btn>
         <v-btn v-if="tab === 1 && (item.status === '3' || item.status === '4')" color="primary" variant="text" @click="handleEnterByEnterprise(item)">入职</v-btn>
         <v-btn v-if="tab === 4" color="primary" variant="text" @click="handleCancelEliminate(item)">取消不合适</v-btn>
@@ -55,6 +55,7 @@ import { useI18n } from '@/hooks/web/useI18n'
 import { useUserStore } from '@/store/user'
 import Snackbar from '@/plugins/snackbar'
 import InvitePage from './invite.vue'
+import { getUserAvatar } from '@/utils/avatar'
 // import PublicPage from './public.vue'
 
 const { t } = useI18n()
@@ -86,7 +87,7 @@ const headers = ref([
   { title: '状态', key: 'status', sortable: false },
   { title: '操作', value: 'actions', sortable: false }
 ])
-const unfit = { title: '类型', key: 'unfitType', sortable: false, value: item => item.type === '0' ? '简历不适' : '面试不适' }
+const unfit = { title: '类型', key: 'unfitType', sortable: false, value: item => item.type === '0' ? '简历不适' : '面试不适' }
 const delivery = { title: '类型', key: 'deliveryType', sortable: false, value: item => item.type === '0' ? '普通职位' : '赏金职位' }
 
 const list = [0, 4]
@@ -155,11 +156,11 @@ const handlePreviewResume = async ({ url, id }) => {
 // 邀请面试
 const itemData = ref({})
 // const inviteType = ref(false)
-const handleInterviewInvite = (item) => {
-  // if (item?.job?.hire) inviteType.value = true
-  itemData.value = item
-  showInvite.value = true
-}
+// const handleInterviewInvite = (item) => {
+//   // if (item?.job?.hire) inviteType.value = true
+//   itemData.value = item
+//   showInvite.value = true
+// }
 
 const handleEditClose = () => {
   showInvite.value = false

+ 6 - 10
src/views/recruit/enterprise/informationManagement/informationSettingsComponents/basicInfo.vue

@@ -134,23 +134,19 @@ const formItems = ref({
       value: null,
       col: 6,
       class: 'mb-3',
-      label: '上班时间(示例:上午09:00 - 下午17:00) *',
-      rules: [v => !!v || '请填写上班时间']
+      label: '上班时间(示例:上午09:00 - 下午17:00)'
     },
     {
       type: 'datePicker',
       key: 'openTime',
+      dateType: 'date', // 时间类型 year month date time
       value: null,
+      label: '开业时间 *',
       col: 6,
-      class: 'mb-3',
       flexStyle: 'mr-3',
-      rules: [v => !!v || '请选择开业时间'],
-      options: {
-        type: 'date',
-        format: 'timestamp',
-        placeholder: '开业时间 *',
-        clearable: false
-      },
+      outlined: true,
+      rules: [v => !!v || '请选择开业时间']
+      // options: {},
     },
     {
       slotName: 'prepare',

+ 4 - 7
src/views/recruit/enterprise/informationManagement/informationSettingsComponents/businessInformation.vue

@@ -56,17 +56,14 @@ const formItems = ref({
     {
       type: 'datePicker',
       key: 'establishmentTime',
+      dateType: 'date', // 时间类型 year month date time
       value: null,
-      default: null,
+      label: '成立时间 *',
       col: 6,
       flexStyle: 'mr-3',
-      class: 'mb-3',
-      options: {
-        // type: 'month',
-        format: 'timestamp',
-        placeholder: '成立时间 *',
-      },
+      outlined: true,
       rules: [v => !!v || '请选择成立时间']
+      // options: {},
     },
     {
       type: 'text',

+ 2 - 1
src/views/recruit/enterprise/informationSetting/index.vue

@@ -4,7 +4,7 @@
       <template #avatar="{ item }">
         <div style="color: #7a7a7a;">头像</div>
         <div class="avatarsBox" @mouseover="showIcon = true" @mouseleave="showIcon = false">
-          <v-avatar class="elevation-5" size=80 :image="item.value || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
+          <v-avatar class="elevation-5" size=80 :image="getUserAvatar(item.value, baseInfo?.sex)"></v-avatar>
           <div v-show="showIcon" @click="openFileInput" v-bind="$attrs" class="mdi mdi-camera-outline">
             <input
               type="file"
@@ -31,6 +31,7 @@ import { useI18n } from '@/hooks/web/useI18n'
 import { getDict } from '@/hooks/web/useDictionaries'
 import { useUserStore } from '@/store/user'
 import Snackbar from '@/plugins/snackbar'
+import { getUserAvatar } from '@/utils/avatar'
 
 const { t } = useI18n()
 const userStore = useUserStore()

+ 2 - 1
src/views/recruit/enterprise/interview/components/item.vue

@@ -2,7 +2,7 @@
   <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="item?.person?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
+      <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>
@@ -85,6 +85,7 @@ import { completedInterviewInvite, cancelInterviewInvite, saveInterviewInvite, n
 import InvitePage from './invite.vue'
 import Snackbar from '@/plugins/snackbar'
 import Confirm from '@/plugins/confirm'
+import { getUserAvatar } from '@/utils/avatar'
 
 defineProps({
   items: Array,

+ 71 - 44
src/views/recruit/enterprise/interview/index.vue

@@ -1,42 +1,15 @@
-<!-- 面试 -->
 <template>
-  <v-card class="pa-5" style="height: 100%;font-size: 14px;">
+  <v-card class="pa-5" style="height: calc(100vh - 130px);font-size: 14px;">
     <div class="d-flex justify-space-between">
       <div class="d-flex mb-3">
-        <!-- 职位 -->
-        <v-autocomplete 
-          v-model="query.jobId" 
-          :items="positionItems" 
-          density="compact" 
-          variant="outlined" 
-          item-title="label" 
-          item-value="value"
-          clearable
-          hide-details
-          label="职位"
-          color="primary"
-          style="width: 300px;"
-          class="mr-3"
-        ></v-autocomplete>
-        <v-select 
-          v-model="query.status" 
-          :items="statusList" 
-          density="compact" 
-          variant="outlined" 
-          item-title="label" 
-          item-value="value"
-          clearable
-          hide-details
-          label="面试状态"
-          color="primary"
-          style="width: 300px;"
-        ></v-select>
+        <Autocomplete class="mr-3" v-model="query.jobId" :item="jobItem"></Autocomplete>
+        <Autocomplete v-model="query.status" :item="statusItem"></Autocomplete>
         <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>
     <v-divider class="mb-3"></v-divider>
-    <div class="d-flex">
+    <div class="d-flex" style="height: calc(100vh - 228px);">
       <div>
         <div class="d-flex justify-space-between px-5">
           <div v-if="selectDateValue">
@@ -44,21 +17,30 @@
             <span class="ml-2" style="cursor: pointer;" @click="handleClear">{{ $t('common.cleanUp') }}</span>
           </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>
+          <v-btn color="primary" variant="text" size="small" @click="selectDateValue = new Date(); handleChangeDate()">{{ $t('interview.today') }}</v-btn>
         </div>
-        <v-date-picker
+        <VueDatePicker 
+          class="mr-5"
           v-model="selectDateValue"
-          color="primary"
-          :hide-header="true"
-          @update:modelValue="handleChangeDate"
-          class="mr-3"
+          inline auto-apply
+          locale="zh-CN"
+          :enable-time-picker="false"
+          :day-names="['一', '二', '三', '四', '五', '六', '七']"
+          :markers="markers"
+          hide-offset-dates
+          @update:model-value="handleChangeDate"
         >
-        </v-date-picker>
+          <template #marker>
+            <span class="custom-marker"></span>
+          </template>
+      </VueDatePicker>
       </div>
       <v-divider style="height: auto;" class="mr-5" vertical></v-divider>
-      <div style="flex: 1;overflow: hidden;">
+      <div style="flex: 1;">
         <div v-if="items.length">
-          <itemPage :items="items" :statusList="statusList" :positionItems="positionItems" @refresh="handleRefresh"></itemPage>
+          <div style="height: calc(100vh - 318px);overflow: auto;padding-right: 12px;">
+            <itemPage :items="items" :statusList="statusList" :positionItems="positionItems" @refresh="handleRefresh"></itemPage>
+          </div>
           <CtPagination
             v-if="total > 0"
             :total="total"
@@ -76,7 +58,7 @@
 <script setup>
 defineOptions({ name: 'enterprise-interview'})
 import { ref } from 'vue'
-import { getInterviewInvitePage } from '@/api/recruit/enterprise/interview'
+import { getInterviewInvitePage, getEnterpriseInterviewCountByTime } from '@/api/recruit/enterprise/interview'
 import { getDict } from '@/hooks/web/useDictionaries'
 import { getJobAdvertised } from '@/api/enterprise'
 import { dealDictArrayData } from '@/utils/position'
@@ -87,12 +69,27 @@ const items = ref([])
 const statusList = ref()
 const total = ref(0)
 const query = ref({
-  pageSize: 10,
+  pageSize: 20,
   pageNo: 1,
   status: null,
   jobId: null,
   time: []
 })
+const positionItems = ref([])
+
+const jobItem = ref({ width: 300, items: positionItems, clearable: true, hireDetails: true, label: '职位' })
+const statusItem = ref({ width: 300, items: statusList, clearable: true, hireDetails: true, label: '面试状态' })
+
+// 获取有面试的日期列表
+const markers = ref([])
+const getCountByTime = async () => {
+  const data = await getEnterpriseInterviewCountByTime()
+  if (!data || !data.length) return
+  markers.value = data.map(e => {
+    return { date: e.key, type: 'dot' }
+  })
+}
+getCountByTime()
 
 // 状态字典
 const getStatusList = async () => {
@@ -155,9 +152,8 @@ const handleReset = () => {
 }
 
 // 职位
-const positionItems = ref([])
 const getPositionList = async () => {
-  const data = await getJobAdvertised({ hire: false })
+  const data = await getJobAdvertised()
   if (!data.length) return
   const list = dealDictArrayData([], data)
   positionItems.value = list.map(e => {
@@ -168,4 +164,35 @@ getPositionList()
 </script>
 
 <style scoped lang="scss">
+:deep(.dp__menu) {
+  border: none !important;
+}
+.custom-marker {
+  position: absolute;
+  bottom: 0;
+  right: 50%;
+  transform: translateX(50%);
+  height: 8px;
+  width: 8px;
+  border-radius: 100%;
+  background-color: var(--v-primary-base);
+}
+/* 滚动条样式 */
+::-webkit-scrollbar {
+  -webkit-appearance: none;
+  width: 4px;
+  height: 0px;
+}
+/* 滚动条内的轨道 */
+::-webkit-scrollbar-track {
+  background: rgba(0, 0, 0, 0.1);
+  border-radius: 0;
+}
+/* 滚动条内的滑块 */
+::-webkit-scrollbar-thumb {
+  cursor: pointer;
+  border-radius: 5px;
+  background: rgba(0, 0, 0, 0.15);
+  transition: color 0.2s ease;
+}
 </style>

+ 7 - 0
src/views/recruit/enterprise/memberCenter/myPoints/index.vue

@@ -5,6 +5,13 @@
 <script setup>
 defineOptions({ name: 'myWallet'})
 import IntegralPage from '@/views/integral/pointsManagement'
+import { useUserStore } from '@/store/user'
+
+const store = useUserStore()
+const updateAccountInfo = async () => {
+  await store.getEnterpriseUserAccountInfo()
+}
+updateAccountInfo()
 
 </script>
 

+ 4 - 5
src/views/recruit/enterprise/positionManagement/components/baseInfo.vue

@@ -169,13 +169,12 @@ const items = ref({
     {
       type: 'datePicker',
       key: 'expireTime',
+      dateType: 'date', // 时间类型 year month date time
       value: null,
-      class: 'mb-3',
-      options: {
-        format: 'timestamp',
-        placeholder: '到期时间 *',
-      },
+      label: '到期时间 *',
+      outlined: true,
       rules: [v => !!v || '请选择到期时间']
+      // options: {},
     },
     {
       type: 'textarea',

+ 14 - 9
src/views/recruit/enterprise/positionManagement/components/details.vue

@@ -1,9 +1,12 @@
 <template>
   <div class="default-width mb-5">
     <div class="banner px-6">
-      <div class="banner-title">
-        <h1 class="ellipsis">{{ info.name }}</h1>
-        <span class="salary">{{ info.payFrom }}-{{ info.payTo }}/{{ info.payName }}</span>
+      <div class="banner-title d-flex justify-space-between">
+        <div class="d-flex align-center">
+          <h1 class="ellipsis">{{ info.name }}</h1>
+          <span class="salary">{{ info.payFrom }}-{{ info.payTo }}/{{ info.payName }}</span>
+          <publicRecruitment v-if="info.hire" class="ml-5" width="50" height="50"></publicRecruitment>
+        </div>
         <span class="refresh-time">{{ timesTampChange(info.updateTime) }} {{ $t('common.refresh') }} <v-icon color="warning" size="20">mdi-alert-outline</v-icon></span>
       </div>
       <div class="banner-tags mt-4">
@@ -15,7 +18,11 @@
       <div class="banner-tools my-4">
         <v-chip size="small" label v-for="(k, i) in info.tagList" :key="i" class="mr-1" color="primary">{{ k }}</v-chip>
       </div>
-      <div class="d-flex justify-end mb-5">
+      <div class="d-flex justify-space-between mb-5">
+        <div>
+          <v-chip v-if="info.hire && info.hirePrice && info.hirePrice > 0" label color="primary">赏金:{{ commissionCalculation(info.hirePrice, 1) }}元</v-chip>
+          <v-chip v-if="info.hire && info.hirePoint && info.hirePoint > 0" label color="primary" class="ml-1">积分:{{ commissionCalculation(info.hirePoint, 1) }}点</v-chip>
+        </div>
         <div class="banner-tools-btns">
           <v-btn class="button-item radius" color="primary" variant="outlined" @click="handleEdit">{{ $t('common.edit') }}</v-btn>
         </div>
@@ -38,9 +45,6 @@
                   <div class="contact-info">{{ info.enterprise.name }} · {{ info.contact.postNameCn }}</div>
                 </div>
               </div>
-              <!-- <div class="float-right">
-                <v-chip color="primary" label>{{ $t('position.currentOnline') }}</v-chip>
-              </div> -->
             </div>
             <v-divider class="my-3"></v-divider>
             <div>
@@ -77,6 +81,7 @@ import { useRouter } from 'vue-router'
 import { timesTampChange } from '@/utils/date'
 import { getJobDetails } from '@/api/position'
 import { dealDictObjData } from '@/utils/position'
+import { commissionCalculation } from '@/utils/position'
 
 const router = useRouter()
 const { id } = router.currentRoute.value.params
@@ -100,8 +105,8 @@ const desc = [
 ]
 
 const handleEdit = () => {
-  if (!enterprise.value.id) return
-  window.open(`/recruit/enterprise/position/edit?id=${enterprise.value.id}`)
+  if (!id) return
+  window.open(`/recruit/enterprise/position/edit?id=${id}`)
 }
 </script>
 

+ 5 - 1
src/views/recruit/enterprise/positionManagement/index.vue

@@ -40,6 +40,9 @@ import { useRouter } from 'vue-router'; const router = useRouter()
 import { getJobAdvertisedList } from '@/api/position'
 import { dealDictArrayData } from '@/utils/position'
 import { useI18n } from '@/hooks/web/useI18n'
+import { useUserStore } from '@/store/user'
+
+const store = useUserStore()
 
 const { t } = useI18n()
 const total = ref(0)
@@ -72,8 +75,9 @@ const textItem = ref({
   appendInnerIcon: 'mdi-magnify'
 })
 
-const handleAdd = () => {
+const handleAdd = async () => {
   router.push('/recruit/enterprise/position/add')
+  await store.getEnterpriseUserAccountInfo()
 }
 
 

+ 1 - 1
src/views/recruit/enterprise/talentPool/components/filter.vue

@@ -135,7 +135,7 @@ const formItems = ref({
       label: '期望城市'
     },
     {
-      type: 'datePicker',
+      type: 'vueDatePicker',
       key: 'firstWorkTime',
       value: null,
       default: null,

+ 11 - 6
src/views/recruit/personal/PersonalCenter/components/interview/index.vue

@@ -2,13 +2,10 @@
   <div>
     <v-divider></v-divider>
       <v-tabs class="mb-3" v-model="query.status" align-tabs="start" color="primary" bg-color="#fff" @update:model-value="handleUpdate">
-        <v-tab :value="0">待接受</v-tab>
-        <v-tab :value="1">待面试</v-tab>
-        <v-tab :value="3">已完成</v-tab>
-        <v-tab :value="98">已拒绝</v-tab>
+        <v-tab v-for="val in tabList" :key="val.value" :value="val.value">{{ val.label }}</v-tab>
       </v-tabs>
       <div v-if="items.length">
-        <ItemPage :items="items" @refresh="getData"></ItemPage>
+        <ItemPage :items="items" @refresh="getData" :tab="query.status"></ItemPage>
         <CtPagination
           v-if="total > 0"
           :total="total"
@@ -27,15 +24,23 @@ defineOptions({ name: 'interview-index'})
 import { ref } from 'vue'
 import { getUserInterviewInvitePage } from '@/api/recruit/personal/personalCenter'
 import { dealDictObjData } from '@/utils/position'
+import { getDict } from '@/hooks/web/useDictionaries'
 import ItemPage from './item.vue'
 
 const query = ref({
   pageNo: 1,
   pageSize: 10,
-  status: 0
+  status: '0'
 })
 const total = ref(0)
 const items = ref([])
+const tabList = ref([])
+
+const getTabList = async () => {
+  const { data } = await getDict('menduner_interview_invite_status')
+  tabList.value = data
+}
+getTabList()
 
 const getData = async () => {
   const { list, total: number } = await getUserInterviewInvitePage(query.value)

+ 31 - 15
src/views/recruit/personal/PersonalCenter/components/interview/item.vue

@@ -5,8 +5,11 @@
           <v-btn color="primary" size="small" @click="handleAgree(val)">同意</v-btn>
           <v-btn class="ml-3" color="error" size="small" @click="handleRefuse(val)">拒绝</v-btn>
         </div>
+        <div v-if="tab === '1' || tab === '98'" class="float-right font-size-13" :style="{'padding': '12px 12px 0 0', 'color': tab === '1' ? 'var(--v-primary-base)' : 'var(--v-error-base)'}">
+          您已于{{ timesTampChange(val.updateTime, 'Y-M-D h:m') }}{{ tab === '1' ? '接受' : '拒绝'}}了此面试邀请
+        </div>
         <div class="img-box">
-          <v-avatar :image="val.contact.avatar || 'https://minio.citupro.com/dev/menduner/7.png'" size="x-small"></v-avatar>
+          <v-avatar :image="getUserAvatar(val.contact.avatar, val.contact.sex)" size="x-small"></v-avatar>
           <span class="name">
             <span class="mx-3">{{ val.contact.name }}</span>
             <span class="gray">{{ val.contact.postNameCn }}</span>
@@ -16,14 +19,18 @@
         </div>
       </div>
       <div class="info-content">
-        <div class="job-info">
+        <div class="font-size-16 color-333 mr-5" style="width: 322px;">
+          <div>面试时间:{{ timesTampChange(val.time, 'Y-M-D h:m') }}</div>
+          <div class="mt-3 ellipsis" style="max-width: 322px;">面试地点:{{ val.address }}</div>
+        </div>
+        <div class="job-info color-666">
           <div class="job-name ellipsis" style="max-width: 410px;">
-            <span class="mr-3">{{ val.job.name }}</span>
+            <span class="mr-3 cursor-pointer position-name" @click="handleToPositionDetails(val)">{{ val.job.name }}</span>
             <span>{{ val.job.payFrom }}-{{ val.job.payTo }}/{{ val.job.payName }}</span>
           </div>
           <div class="job-other d-flex align-center">
             <div style="width: 30px;height: 30px;">
-              <v-img width="30" height="30" :src="val.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/7.png'"></v-img>
+              <v-img width="30" height="30" :src="val.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'"></v-img>
             </div>
             <div class="ellipsis" style="max-width: 400px;">
               <span class="mx-2 enterprise-name" @click="handleToEnterprise(val)">{{ val.enterprise.name }}</span>
@@ -35,10 +42,6 @@
             </div>
           </div>
         </div>
-        <div class="font-size-14 color-666" style="width: 322px;">
-          <div>面试时间:{{ timesTampChange(val.time, 'Y-M-D h:m') }}</div>
-          <div class="mt-3 ellipsis" style="max-width: 322px;">面试地点:{{ val.address }}</div>
-        </div>
       </div>
   </div>
 </template>
@@ -50,6 +53,8 @@ import { timesTampChange } from '@/utils/date'
 import { userInterviewInviteReject, userInterviewInviteConsent } from '@/api/recruit/personal/personalCenter'
 import Snackbar from '@/plugins/snackbar'
 import Confirm from '@/plugins/confirm'
+import { getUserAvatar } from '@/utils/avatar'
+import { useRouter } from 'vue-router'
 
 const { t } = useI18n()
 const emits = defineEmits(['refresh'])
@@ -57,14 +62,25 @@ const props = defineProps({
   items: {
     type: Array,
     default: () => []
+  },
+  tab: {
+    type: String,
+    default: '0'
   }
 })
 
+const router = useRouter()
+
 // 企业详情
 const handleToEnterprise = (item) => {
   const id = item.enterprise.id
   if (!id) return
-  window.open(`/recruit/personal/company/details/${id}?key=briefIntroduction`)
+  router.push(`/recruit/personal/company/details/${id}?key=briefIntroduction`)
+}
+
+// 职位详情
+const handleToPositionDetails = (item) => {
+  router.push(`/recruit/personal/position/details/${item.job.id}`)
 }
 
 // 同意
@@ -133,22 +149,19 @@ const handleRefuse = (val) => {
     padding: 16px 24px;
     justify-content: space-between;
     .job-info {
-      width: 430px;
-      max-width: 430px;
+      width: 360px;
+      max-width: 360px;
       font-weight: 500;
-      font-size: 16px;
+      font-size: 14px;
       margin-right: 12px;
       .job-name {
         height: 22px;
         line-height: 22px;
-        color: var(--color-222);
         margin-bottom: 12px;
       }
       .job-other {
-        color: var(--color-666);
         height: 22px;
         line-height: 22px;
-        font-size: 14px;
         .enterprise-name {
           cursor: pointer;
           &:hover {
@@ -159,4 +172,7 @@ const handleRefuse = (val) => {
     }
   }
 }
+.position-name:hover {
+  color: var(--v-primary-base);
+}
 </style>

+ 2 - 1
src/views/recruit/personal/PersonalCenter/components/interviewSchedule.vue

@@ -40,7 +40,7 @@
             <span class="c-base">{{ val?.job?.payFrom || '--' }}-{{ val?.job?.payTo || '--' }}/{{ val?.job?.payName || '--' }}</span>
           </div>
           <div class="img-box">
-            <v-avatar size="small" :image="val.contact.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
+            <v-avatar size="small" :image="getUserAvatar(val.contact.avatar, val.contact.sex)"></v-avatar>
             <span class="name">
               <span class="mx-3">{{ val.contact.name }}</span>
               <span class="septal-line"></span>
@@ -59,6 +59,7 @@ import { getDict } from '@/hooks/web/useDictionaries'
 import { getText } from '@/utils/getText'
 import { timesTampChange } from '@/utils/date'
 import { ref } from 'vue'
+import { getUserAvatar } from '@/utils/avatar'
 defineOptions({name: 'PersonalCenter-interviewSchedule'})
 const emit = defineEmits(['handleMore'])
 const props = defineProps({

+ 2 - 1
src/views/recruit/personal/PersonalCenter/components/seenMe.vue

@@ -5,7 +5,7 @@
         <div class="position-and-company">
           <div class="position">
             <div class="float-left">
-              <v-img :src="item?.contact.avatar || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :width="40" style="height: 40px;border-radius: 4px;"/>
+              <v-img :src="getUserAvatar(item?.contact.avatar, item?.contact.sex)" :width="40" style="height: 40px;border-radius: 4px;"/>
             </div>
             <div class="company-info">
               <h3 :class="{'default-active': item.active }" class="title1">{{ item.contact.name }}</h3>
@@ -45,6 +45,7 @@ import { getInterestedMePage } from '@/api/recruit/personal/personalCenter'
 import { dealDictObjData } from '@/utils/position'
 import { timesTampChange } from '@/utils/date'
 import Empty from '@/components/Empty'
+import { getUserAvatar } from '@/utils/avatar'
 
 const total = ref(0)
 const items = ref([])

+ 4 - 3
src/views/recruit/personal/PersonalCenter/dynamic/left.vue

@@ -7,7 +7,7 @@
         offset-y="50" 
         :color="baseInfo?.sex ? (baseInfo?.sex === '1' ? '#1867c0' : 'error') : 'error'" 
         :icon="baseInfo?.sex ? (baseInfo?.sex === '1' ? 'mdi-gender-male' : 'mdi-gender-female') : 'mdi-gender-female'">
-        <v-avatar size="x-large" :image="baseInfo?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
+        <v-avatar size="x-large" :image="getUserAvatar(baseInfo?.avatar, baseInfo?.sex)"></v-avatar>
       </v-badge>
       <div class="ml-5 content">
         <div class="username">
@@ -69,15 +69,16 @@ import { useUserStore } from '@/store/user'
 import { updateJobStatus } from '@/api/recruit/personal/resume'
 import { useI18n } from '@/hooks/web/useI18n'
 import Snackbar from '@/plugins/snackbar'
-import communication from '../components/communication.vue'
+// import communication from '../components/communication.vue'
 import delivery from '../components/delivery.vue'
 import interview from '../components/interview/index.vue'
 import interested from '../components/interested.vue'
 import seenMe from '../components/seenMe.vue'
+import { getUserAvatar } from '@/utils/avatar'
 
 const { t } = useI18n()
 const list = [
-  { title: t('position.throughCommunication'), path: communication },
+  // { title: t('position.throughCommunication'), path: communication },
   { title: t('position.delivered'), path: delivery },
   { title: t('position.interview'), path: interview },
   { title: t('position.interested'), path: interested },

+ 1 - 0
src/views/recruit/personal/PersonalCenter/dynamic/right.vue

@@ -129,6 +129,7 @@ const openFileInput = () => {
 // 上传附件
 const typeList = ['pdf', 'doc', 'docx']
 const handleUploadFile = async (e) => {
+  if (!e.target.files.length) return
   const file = e.target.files[0]
   const size = file.size
   if (size / (1024*1024) > 10) {

+ 7 - 0
src/views/recruit/personal/PersonalCenter/index.vue

@@ -14,6 +14,13 @@ defineOptions({ name: 'personal-center'})
 
 import LeftPage from './dynamic/left.vue'
 import RightPage from './dynamic/right.vue'
+import { useUserStore } from '@/store/user'
+
+const store = useUserStore()
+const updateAccountInfo = async () => {
+  await store.getUserAccountInfo()
+}
+updateAccountInfo()
 </script>
 
 <style scoped lang="scss">

+ 13 - 1
src/views/recruit/personal/home/index.vue

@@ -14,7 +14,7 @@
     </div>
   </div>
   <!-- 快速填写简易人才信息-弹窗 -->
-  <simplePage v-if="showSimplePage" :closeable="true" closeText="跳过"></simplePage>
+  <simplePage v-if="showSimplePage" :closeable="true" closeText="暂时跳过" @simpleInfoReady="handleUpdateInfo"></simplePage>
 </template>
 
 <script setup>
@@ -30,6 +30,13 @@ import PopularEnterprises from './components/popularEnterprises.vue'
 import advertisementPage from './components/advertisement.vue'
 import { useRouter } from 'vue-router'
 import { nextTick, ref } from 'vue'
+import { useUserStore } from '@/store/user'
+
+const store = useUserStore()
+const updateBaseInfo = async () => {
+  await store.getUserBaseInfos()
+}
+updateBaseInfo()
 
 const router = useRouter()
 const simple = localStorage.getItem('simpleCompleteDialogHaveBeenShow')
@@ -41,6 +48,11 @@ nextTick(() => {
 const handleSearch = (val) => {
   if (val) router.push(`/recruit/personal/position?content=${val}`)
 }
+
+// 更新用户基本信息
+const handleUpdateInfo = async () => {
+  await store.getUserBaseInfos(null)
+}
 </script>
 
 <style lang="scss" scoped>

+ 7 - 0
src/views/recruit/personal/myWallet/index.vue

@@ -5,6 +5,13 @@
 <script setup>
 defineOptions({ name: 'myWallet'})
 import IntegralPage from '@/views/integral/pointsManagement'
+import { useUserStore } from '@/store/user'
+
+const store = useUserStore()
+const updateAccountInfo = async () => {
+  await store.getUserAccountInfo()
+}
+updateAccountInfo()
 
 </script>
 

+ 44 - 18
src/views/recruit/personal/position/components/details.vue

@@ -10,8 +10,8 @@
         <span class="refresh-time">{{ timesTampChange(info.updateTime) }} {{ $t('common.refresh') }} <v-icon color="warning" size="20">mdi-alert-outline</v-icon></span>
       </div>
       <div class="banner-tags mt-4">
-        <span v-for="k in desc" :key="k.mdi" class="mr-10">
-          <span v-if="positionInfo[k.value]">
+        <span v-for="k in desc" :key="k.mdi">
+          <span v-if="positionInfo[k.value]" class="mr-10">
             <v-icon color="var(--color-666)" size="20">{{ k.mdi }}</v-icon>
             <span class="ml-1">{{ positionInfo[k.value] }}</span>
           </span>
@@ -70,7 +70,7 @@
             <v-divider class="my-3"></v-divider>
             <div class="contact" v-if="Object.keys(info).length">
               <div class="float-left d-flex align-center">
-                <v-img :src="info.contact.avatar || 'https://minio.citupro.com/dev/menduner/7.png'" :width="45" style="height: 45px;"></v-img>
+                <v-img :src="getUserAvatar(info.contact.avatar, info.contact.sex)" :width="45" style="height: 45px;"></v-img>
                 <div class="ml-2">
                   <div class="contact-name">{{ info.contact.name }}</div>
                   <div class="contact-info">
@@ -112,8 +112,9 @@
       </div>
     </div>
 
-    <!-- 弹窗提示去上传简历 -->
-    <promptToUpload v-model="dialog" @handleToUpload="handleToUpload"></promptToUpload>
+    <!-- 简历上传 -->
+    <input type="file" ref="fileInput" accept=".pdf, .doc, .docx" style="display: none;" @change="handleUploadFile"/>
+
     <!-- 选择简历 -->
     <selectResumeDialog v-model="showResume" :list="resumeList" @submit="handleSubmit" @close="handleClose"></selectResumeDialog>
     <!-- 复制分享链接 -->
@@ -149,6 +150,7 @@ defineOptions({ name: 'position-details' })
 import { computed, ref } from 'vue'
 import { useRouter } from 'vue-router'
 import { timesTampChange } from '@/utils/date'
+import { uploadFile } from '@/api/common'
 import { getPersonResumeCv } from '@/api/recruit/personal/resume'
 import { useI18n } from '@/hooks/web/useI18n'
 import { getPositionDetails, getSimilarPosition, getJobFavoriteCheck, getPersonJobFavorite, getPersonJobUnfavorite, jobCvRelCheckSend, jobCvRelSend } from '@/api/position'
@@ -157,10 +159,10 @@ import similarPositions from '@/components/Position/similarPositions.vue'
 import EnterpriseInfo from '@/components/Enterprise/info.vue'
 import Snackbar from '@/plugins/snackbar'
 import Dialog from '@/components/CtDialog'
-import promptToUpload from './jobDetails/promptToUpload'
 import selectResumeDialog from './jobDetails/selectResumeDialog'
 import { getToken } from '@/utils/auth'
-import { prologue } from '@/hooks/web/useIM'
+import { prologue, defaultText } from '@/hooks/web/useIM'
+import { getUserAvatar } from '@/utils/avatar'
 
 const { t } = useI18n()
 const router = useRouter()
@@ -181,7 +183,6 @@ const info = ref({})
 const positionInfo = ref({})
 const getPositionDetail = async () => {
   const data = await getPositionDetails({ id })
-  console.log('daaa', data)
   info.value = data
   positionInfo.value = { ...dealDictObjData({}, info.value), ...info.value }
   getSimilarPositionList()
@@ -207,7 +208,6 @@ const getCollectionStatus = async () => {
   if (!getToken()) return isCollection.value = false
   const data = await getJobFavoriteCheck({ jobId: id })
   isCollection.value = data
-  console.log('isCollection', data)
 }
 getCollectionStatus()
 
@@ -246,14 +246,37 @@ const handleCollection = async () => {
   await getCollectionStatus()
 }
 
-const dialog = ref(false)
-const showResume = ref(false)
-// 去上传附件
-const handleToUpload = () => {
-  dialog.value = false
-  window.open('/recruit/personal/personalCenter')
+// 投递简历时,若当前用户没有简历列表则弹窗上传简历以及投递
+const typeList = ['pdf', 'doc', 'docx']
+const handleUploadFile = async (e) => {
+  if (!e.target.files.length) return
+  const file = e.target.files[0]
+  const size = file.size
+  if (size / (1024*1024) > 10) {
+    Snackbar.warning(t('common.fileSizeExceed'))
+    return
+  }
+  const arr = file.name.split('.')
+  if (typeList.indexOf(arr[arr.length - 1]) < 0) {
+    Snackbar.warning(t('common.fileFormatIncorrect'))
+    return
+  }
+  const formData = new FormData()
+  formData.append('file', file)
+  const { data } = await uploadFile(formData)
+  if (!data) return
+
+  await jobCvRelSend({ jobId: id, title: arr[0], url: data, type: info.value.hire ? 1 : 0 })
+  setTimeout(() => {
+    Snackbar.success(t('resume.deliverySuccess'))
+    deliveryCheck()
+  }, 3000)
 }
 
+const showResume = ref(false)
+const clicked = ref(false)
+const fileInput = ref()
+
 // 效验是否有投递简历
 const resumeList = ref([])
 const selectResume = ref()
@@ -263,7 +286,11 @@ const handleDelivery = async () => {
   resumeList.value = result
   // 没有上传过简历的先去上传
   if (!result.length) {
-    dialog.value = true
+    Snackbar.warning('您还未上传过简历,请先上传简历')
+    if (clicked.value) return
+    clicked.value = true
+    fileInput.value.click()
+    clicked.value = false
     return
   }
   showResume.value = true
@@ -290,9 +317,8 @@ const handleSubmit = async (val) =>{
 const toDetails = async (info) => {
   const userId = info.contact.userId
   const enterpriseId = info.contact.enterpriseId
-  const text = '您好,我对该职位很感兴趣,希望能有机会与您进一步沟通。'
   const textObj = {
-    text: text,
+    text: defaultText,
     positionInfo: positionInfo.value
   }
   await prologue({userId, enterpriseId, text: JSON.stringify(textObj)})

+ 12 - 18
src/views/recruit/personal/remuse/components/basicInfo.vue

@@ -13,7 +13,7 @@
           offset-y="33" 
           :color="baseInfo?.sex ? (baseInfo?.sex === '1' ? '#1867c0' : 'error') : 'error'" 
           :icon="baseInfo?.sex ? (baseInfo?.sex === '1' ? 'mdi-gender-male' : 'mdi-gender-female') : 'mdi-gender-female'">
-          <v-avatar size=80 :image="baseInfo?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'">
+          <v-avatar size=80 :image="getUserAvatar(baseInfo?.avatar, baseInfo?.sex)">
           </v-avatar>
           <div v-show="showIcon" @click="openFileInput" v-bind="$attrs" class="mdi mdi-camera-outline">
             <input
@@ -123,6 +123,7 @@ import { getTimeStamp, timesTampChange } from '@/utils/date'
 import { updatePersonAvatar, saveResumeBasicInfo } from '@/api/recruit/personal/resume'
 import { useUserStore } from '@/store/user'
 import { uploadFile } from '@/api/common'
+import { getUserAvatar } from '@/utils/avatar'
 import { useI18n } from '@/hooks/web/useI18n'
 import { ref } from 'vue';
 defineOptions({name: 'resume-components-basicInfo'})
@@ -191,18 +192,13 @@ const items = ref({
     {
       type: 'datePicker',
       key: 'birthday',
+      // dateType: 'date', // 时间类型 year month date time
       value: null,
-      default: null,
+      label: '出生日期 *',
       col: 6,
-      class: 'mb-3',
-      rules: [v => !!v || '请选择出生日期'],
-      options: {
-        type: 'date',
-        format: 'timestamp',
-        placeholder: '出生日期 *',
-        clearable: false,
-        // disabled: true,
-      },
+      outlined: true,
+      rules: [v => !!v || '请选择出生日期']
+      // options: {},
     },
     {
       type: 'phoneNumber',
@@ -337,17 +333,15 @@ const items = ref({
     },
     {
       type: 'datePicker',
+      dateType: 'month',
       key: 'firstWorkTime',
       value: null,
-      default: null,
+      label: '首次工作时间 *',
       col: 6,
-      class: 'mb-3',
-      options: {
-        type: 'month',
-        format: 'timestamp',
-        placeholder: '首次工作时间 *',
-      },
+      outlined: true,
+      // clearable: true,
       rules: [v => !!v || '请选择首次工作时间']
+      // options: {},
     },
   ]
 })

+ 10 - 14
src/views/recruit/personal/remuse/components/educationExp.vue

@@ -154,30 +154,26 @@ const formItems = ref({
     {
       type: 'datePicker',
       key: 'startTime',
+      dateType: 'month', // 时间类型 year month date time
       value: null,
-      default: null,
+      label: '起始时间 *',
       col: 6,
-      class: 'mb-3',
-      options: {
-        type: 'month',
-        format: 'timestamp',
-        placeholder: '起始时间 *',
-      },
+      outlined: true,
+      clearable: true,
       rules: [v => !!v || '请选择起始时间']
+      // options: {},
     },
     {
       type: 'datePicker',
       key: 'endTime',
+      dateType: 'month', // 时间类型 year month date time
       value: null,
-      default: null,
+      label: '结束时间 *',
       col: 6,
-      class: 'mb-3',
-      options: {
-        type: 'month',
-        format: 'timestamp',
-        placeholder: '结束时间 *',
-      },
+      outlined: true,
+      clearable: true,
       rules: [v => !!v || '请选择结束时间']
+      // options: {},
     },
     {
       type: 'textarea',

+ 11 - 12
src/views/recruit/personal/remuse/components/projectExperience.vue

@@ -63,29 +63,28 @@ const items = ref({
     {
       type: 'datePicker',
       key: 'startTime',
+      dateType: 'month', // 时间类型 year month date time
       value: null,
+      label: '项目开始时间 *',
       col: 6,
-      class: 'mb-3',
-      options: {
-        type: 'month',
-        format: 'timestamp',
-        placeholder: '项目开始时间 *',
-      },
       flexStyle: 'mr-3',
+      outlined: true,
+      clearable: true,
       rules: [v => !!v || '请选择项目开始时间']
+      // options: {},
     },
     {
       type: 'datePicker',
       key: 'endTime',
+      dateType: 'month', // 时间类型 year month date time
       value: null,
+      label: '项目结束时间 *',
       col: 6,
-      class: 'mb-3',
-      options: {
-        type: 'month',
-        format: 'timestamp',
-        placeholder: '项目结束时间 *',
-      },
+      flexStyle: 'mr-3',
+      outlined: true,
+      clearable: true,
       rules: [v => !!v || '请选择项目结束时间']
+      // options: {},
     },
     {
       type: 'textarea',

+ 10 - 12
src/views/recruit/personal/remuse/components/trainingExperience.vue

@@ -78,29 +78,27 @@ const items = ref({
     {
       type: 'datePicker',
       key: 'startTime',
+      dateType: 'month', // 时间类型 year month date time
       value: null,
+      label: '培训开始时间 *',
       col: 6,
-      class: 'mb-3',
-      options: {
-        type: 'month',
-        format: 'timestamp',
-        placeholder: '培训开始时间 *',
-      },
       flexStyle: 'mr-3',
+      outlined: true,
+      clearable: true,
       rules: [v => !!v || '请选择培训开始时间']
+      // options: {},
     },
     {
       type: 'datePicker',
       key: 'endTime',
+      dateType: 'month', // 时间类型 year month date time
       value: null,
+      label: '培训结束时间 *',
       col: 6,
-      class: 'mb-3',
-      options: {
-        type: 'month',
-        format: 'timestamp',
-        placeholder: '培训结束时间 *',
-      },
+      outlined: true,
+      clearable: true,
       rules: [v => !!v || '请选择培训结束时间']
+      // options: {},
     },
     {
       type: 'textarea',

+ 10 - 14
src/views/recruit/personal/remuse/components/workExperience.vue

@@ -120,30 +120,26 @@ const formItems = ref({
     {
       type: 'datePicker',
       key: 'startTime',
+      dateType: 'month', // 时间类型 year month date time
       value: null,
-      default: null,
+      label: '起始时间 *',
       col: 6,
-      class: 'mb-3',
-      options: {
-        type: 'month',
-        format: 'timestamp',
-        placeholder: '起始时间 *',
-      },
+      outlined: true,
+      clearable: true,
       rules: [v => !!v || '请选择起始时间']
+      // options: {},
     },
     {
       type: 'datePicker',
       key: 'endTime',
+      dateType: 'month', // 时间类型 year month date time
       value: null,
-      default: null,
+      label: '结束时间 *',
       col: 6,
-      class: 'mb-3',
-      options: {
-        type: 'month',
-        format: 'timestamp',
-        placeholder: '结束时间 *',
-      },
+      outlined: true,
+      clearable: true,
       rules: [v => !!v || '请选择结束时间']
+      // options: {},
     },
     {
       type: 'textarea',

+ 1 - 1
src/views/recruit/personal/shareJob/sendResume/simple.vue

@@ -67,7 +67,7 @@ const getUserInfoVerify = () => {
 // 查询用户基本信息-失败 
 const getUserInfoFail = () => {
   if (timer.value) clearInterval(timer.value); timer.value = null
-  Snackbar.success(t('login.getUserInfoFailed')+','+t('login.loginAgain'))
+  Snackbar.error(t('login.getUserInfoFailed')+','+t('login.loginAgain'))
 }
 
 const formRef = ref()