Forráskód Böngészése

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

zhengnaiwen_citu 4 hónapja
szülő
commit
8b454a1863
65 módosított fájl, 695 hozzáadás és 1543 törlés
  1. 2 0
      .env.demo
  2. 1 0
      .env.localDev
  3. 2 0
      .env.production
  4. 58 1
      src/components/CtTable/index.vue
  5. 6 2
      src/components/Enterprise/hotPromoted.vue
  6. 11 2
      src/components/FormUI/combobox/index.vue
  7. 5 1
      src/components/Position/item.vue
  8. 4 4
      src/components/Position/longCompany.vue
  9. 1 1
      src/config/axios/service.js
  10. 4 0
      src/layout/company/side.vue
  11. 4 4
      src/layout/enterprise.vue
  12. 18 19
      src/layout/personal/navBar.vue
  13. 1 1
      src/permission.js
  14. 11 0
      src/plugins/dialogExtend/components/analyzeTestData.js
  15. 27 14
      src/plugins/dialogExtend/components/infoForm.vue
  16. 7 2
      src/utils/position.js
  17. 1 1
      src/version.js
  18. 1 0
      src/views/recruit/enterprise/invoiceManagement/index.vue
  19. 133 53
      src/views/recruit/enterprise/newlyAppointed/index.vue
  20. 1 1
      src/views/recruit/enterprise/resume/components/commonStyle.vue
  21. 6 6
      src/views/recruit/enterprise/resume/components/screen.vue
  22. 3 12
      src/views/recruit/enterprise/resume/components/table.vue
  23. 11 2
      src/views/recruit/enterprise/resume/index.vue
  24. 3 3
      src/views/recruit/enterprise/search/recommend/index.vue
  25. 3 3
      src/views/recruit/enterprise/search/retrieval/index.vue
  26. 3 3
      src/views/recruit/enterprise/talentMap/index.vue
  27. 9 4
      src/views/recruit/enterprise/talentPool/components/details.vue
  28. 3 3
      src/views/recruit/enterprise/talentPool/index copy.vue
  29. 3 3
      src/views/recruit/enterprise/talentPool/index.vue
  30. 1 0
      src/views/recruit/enterprise/tradingOrder/components/trading/balance.vue
  31. 1 0
      src/views/recruit/enterprise/tradingOrder/components/trading/recharge.vue
  32. 1 0
      src/views/recruit/enterprise/tradingOrder/components/trading/transaction.vue
  33. 3 0
      src/views/recruit/personal/PersonalCenter/index.vue
  34. 0 1
      src/views/recruit/personal/PersonalCenter/jobFeedback/components/seenMe.vue
  35. 36 7
      src/views/recruit/personal/PersonalCenter/resume/analysis/components/educationExp.vue
  36. 36 7
      src/views/recruit/personal/PersonalCenter/resume/analysis/components/trainingExperience.vue
  37. 36 7
      src/views/recruit/personal/PersonalCenter/resume/analysis/components/workExperience.vue
  38. 9 7
      src/views/recruit/personal/PersonalCenter/resume/analysis/index.vue
  39. 59 3
      src/views/recruit/personal/PersonalCenter/resume/attachment/index.vue
  40. 0 101
      src/views/recruit/personal/PersonalCenter/resume/online/analysis/avatar.vue
  41. 0 119
      src/views/recruit/personal/PersonalCenter/resume/online/analysis/base.vue
  42. 0 279
      src/views/recruit/personal/PersonalCenter/resume/online/analysis/basicInfo.vue
  43. 0 61
      src/views/recruit/personal/PersonalCenter/resume/online/analysis/eduExp.vue
  44. 0 234
      src/views/recruit/personal/PersonalCenter/resume/online/analysis/educationExp.vue
  45. 0 24
      src/views/recruit/personal/PersonalCenter/resume/online/analysis/evaluation.vue
  46. 0 59
      src/views/recruit/personal/PersonalCenter/resume/online/analysis/selfEvaluation.vue
  47. 0 112
      src/views/recruit/personal/PersonalCenter/resume/online/analysis/trainingExperience.vue
  48. 0 57
      src/views/recruit/personal/PersonalCenter/resume/online/analysis/workExp.vue
  49. 0 203
      src/views/recruit/personal/PersonalCenter/resume/online/analysis/workExperience.vue
  50. 30 6
      src/views/recruit/personal/PersonalCenter/resume/online/components/educationExp.vue
  51. 29 6
      src/views/recruit/personal/PersonalCenter/resume/online/components/trainingExperience.vue
  52. 29 6
      src/views/recruit/personal/PersonalCenter/resume/online/components/workExperience.vue
  53. 4 48
      src/views/recruit/personal/PersonalCenter/resume/online/index.vue
  54. 7 1
      src/views/recruit/personal/PersonalCenter/tradeOrder/index.vue
  55. 3 3
      src/views/recruit/personal/company/index.vue
  56. 17 16
      src/views/recruit/personal/companyDetail/index.vue
  57. 5 3
      src/views/recruit/personal/home/components/advertisement/index.vue
  58. 4 6
      src/views/recruit/personal/home/components/homeJobTypeCard/index.vue
  59. 16 8
      src/views/recruit/personal/home/components/hotJobs.vue
  60. 10 7
      src/views/recruit/personal/home/components/hotPromotedPositions.vue
  61. 5 1
      src/views/recruit/personal/home/components/popularEnterprises.vue
  62. 1 1
      src/views/recruit/personal/home/index.vue
  63. 1 1
      src/views/recruit/personal/position/components/conditionFilter/commonStyle.vue
  64. 9 3
      src/views/recruit/personal/position/components/details.vue
  65. 1 1
      src/views/recruit/personal/position/index.vue

+ 2 - 0
.env.demo

@@ -1,5 +1,7 @@
 NODE_ENV = 'production'
 
+VITE_NODE_ENV = 'test'
+
 VITE_APP_TITLE = 门墩儿
 
 # 访问路径

+ 1 - 0
.env.localDev

@@ -1,4 +1,5 @@
 NODE_ENV = 'development'
+VITE_NODE_ENV = 'development'
 
 VITE_APP_TITLE = 门墩儿
 

+ 2 - 0
.env.production

@@ -1,5 +1,7 @@
 NODE_ENV = 'production'
 
+VITE_NODE_ENV = 'production'
+
 VITE_APP_TITLE = 门墩儿
 
 # 访问路径

+ 58 - 1
src/components/CtTable/index.vue

@@ -54,7 +54,7 @@
 
 <script setup>
 defineOptions({ name: 'CtTable'})
-import { ref, computed, useSlots, watch } from 'vue'
+import { ref, computed, useSlots, watch, onMounted } from 'vue'
 
 const selected = ref([])
 const emit = defineEmits(['pageHandleChange', 'del', 'edit', 'add', 'selected'])
@@ -157,6 +157,20 @@ const headerSlot = computed(() => {
   })
 })
 
+onMounted(() => {
+  const wrapper = table.value.$el.querySelector('.v-table__wrapper');
+  
+  const observer = new ResizeObserver(() => {
+    if (wrapper.scrollWidth > wrapper.clientWidth) {
+      wrapper.classList.add('hasScroll');
+    } else {
+      wrapper.classList.remove('hasScroll');
+    }
+  });
+
+  observer.observe(wrapper);
+});
+
 const edit = (item) => {
   emit('edit', item)
 }
@@ -187,4 +201,47 @@ const handleSelect = (e) => {
     white-space: nowrap !important;
   }
 
+  :deep {
+    .v-table__wrapper {
+      position: relative;
+      
+      &::-webkit-scrollbar:horizontal {
+        height: 8px;
+      }
+      
+      &:not(:hover)::-webkit-scrollbar:horizontal {
+        display: none;
+      }
+    }
+
+    .v-table__wrapper:not(:hover), 
+    .v-table__wrapper::-webkit-scrollbar-thumb:horizontal {
+      background: transparent;
+    }
+
+    table > tbody > tr > td:last-child,
+    table > thead > tr > th:last-child {
+      position: sticky !important;
+      position: -webkit-sticky !important;
+      right: 0;
+      z-index: 1;
+      background: white !important;
+      box-shadow: none;
+    }
+
+    .v-table__wrapper.hasScroll {
+      table > tbody > tr > td:last-child,
+      table > thead > tr > th:last-child {
+        border-left: 1px solid #e0e0e0 !important;
+        // box-shadow: inset 10px 0 10px -10px rgba(0, 0, 0, .35) !important;
+      }
+
+      table > thead > tr > th:last-child {
+        z-index: 10 !important;
+        // box-shadow: inset 10px 0 10px -10px rgba(0, 0, 0, .35) !important;
+        // border-bottom: 1px solid #e0e0e0 !important;
+      }
+    }
+  }
+
 </style>

+ 6 - 2
src/components/Enterprise/hotPromoted.vue

@@ -3,7 +3,7 @@
     <div class="sub-li" v-for="(item, index) in list" :key="index">
       <div v-if="item">
         <!-- 公司信息 -->
-        <div class="company-info-top align-center" @click.stop="jumpToEnterpriseDetail(item.enterprise.id, true)">
+        <div class="company-info-top align-center" @click.stop="jumpToEnterpriseDetail(item.enterprise.id, false)">
           <div class="float-left">
             <v-img :src="item?.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" alt="" width="77" height="77" style="border-radius: 4px;"/>
           </div>
@@ -20,6 +20,7 @@
           </div>
         </div>
         <div class="px-5 py-1 ellipsis-tag" :style="{'height': '33px', 'border-bottom': item.enterprise.welfareList && item.enterprise.welfareList.length ? '1px solid #EBEBEB' : 'none'}">
+          <v-tooltip v-if="item.enterprise?.welfareList?.length" activator="parent" location="top">{{ item.enterprise.welfareList.toString() }}</v-tooltip>
           <span class="welfareTag mr-5" v-for="(k, i) in item.enterprise.welfareList" :key="i">{{ k }}</span>
         </div>
         <!-- 职位列表 -->
@@ -27,6 +28,7 @@
           <li class="company-job-item" v-for="(k, i) in item.jobList" :key="i" :class="{'company-job-item-hover': k.active}" @mouseenter="k.active = true" @mouseleave="k.active = false" @click="handleClickPosition(k)">
             <div class="job-info" @mouseenter="k.active = true" @mouseleave="k.active = false" @click.stop="handleClickPosition(k)">
               <div class="mb-2 d-flex">
+                <v-tooltip v-if="isTextOverflow[index]" activator="parent" location="top">{{ formatName(k.name) }}</v-tooltip>
                 <p :class="['name', 'cursor-pointer', {'default-active': k.active }]" :style="{'max-width': !k.payFrom && !k.payTo ? '290px' : '200px'}">{{ formatName(k.name) }}</p>
                 <span v-if="!k.payFrom && !k.payTo" class="salary">面议</span>
                 <span v-else class="salary">{{ k.payFrom ? k.payFrom + '-' : '' }}{{ k.payTo }}{{ k.payName ? '/' + k.payName : '' }}</span>
@@ -59,6 +61,7 @@ import { ref, watch, nextTick } from 'vue'
 import { timesTampChange } from '@/utils/date'
 import { formatName } from '@/utils/getText'
 import { jumpToEnterpriseDetail } from '@/utils/position'
+import { useRouter } from 'vue-router'
 
 const props = defineProps({
   items: {
@@ -67,6 +70,7 @@ const props = defineProps({
   }
 })
 
+const router = useRouter()
 const companyNameRefs = ref({})
 const isTextOverflow = ref({})
 
@@ -95,7 +99,7 @@ const desc = ['areaName', 'eduName', 'expName']
 
 // 职位详情
 const handleClickPosition = (k) => {
-  window.open(`/recruit/personal/position/details/${k.id}`)
+  router.push(`/recruit/personal/position/details/${k.id}`)
 }
 
 // 查看更多职位

+ 11 - 2
src/components/FormUI/combobox/index.vue

@@ -23,7 +23,7 @@
 </template>
 <script setup>
 import { debounce } from 'lodash'
-import { ref } from 'vue';
+import { ref, watch } from 'vue';
 defineOptions({ name:'FormUI-v-combobox'})
 
 const props = defineProps({item: Object, modelValue: [String, Number]})
@@ -31,7 +31,16 @@ const emit = defineEmits(['update:modelValue', 'change', 'search'])
 const item = props.item
 const searchDebouncedTime = item.searchDebouncedTime === 0 ? 0 : 500
 
-const value = ref(props.modelValue)
+const value = ref()
+watch(
+  () => props.modelValue,
+  (newVal) => {
+    value.value = newVal
+  },
+  { immediate: true },
+  { deep: true }
+)
+
 const modelValueUpDate = (val) => {
   value.value = val
   emit('update:modelValue', value.value)

+ 5 - 1
src/components/Position/item.vue

@@ -40,7 +40,7 @@
           </div>
           <div v-if="tab === 2" class="font-size-14 mb-3 text-end" style="color: #345768;">发布时间:{{ timesTampChange(item.createTime, 'Y-M-D h:m') }}</div>
         </div>
-        <div class="sub-li-bottom" @click.stop="jumpToEnterpriseDetail(item.enterpriseId, true)">
+        <div class="sub-li-bottom" @click.stop="jumpToEnterpriseDetail(item.enterpriseId, isOpenWindow)">
           <div class="user-info">
             <div class="d-flex align-center">
               <v-avatar size="35">
@@ -77,6 +77,10 @@ const props = defineProps({
   tab: {
     type: Number,
     default: 1
+  },
+  isOpenWindow: {
+    type: Boolean,
+    default: true
   }
 })
 // const list = ref([])

+ 4 - 4
src/components/Position/longCompany.vue

@@ -6,7 +6,9 @@
           <div class="float-left mr-5">
             <v-img :src="item.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :alt="item.anotherName" :width="40" style="height: 40px;border-radius: 4px;"/>
           </div>
-          <h3 :class="{'default-active': item.active }" class="cursor-pointer" @click.stop="jumpToEnterpriseDetail(item.id, true)">{{ formatName(item.anotherName || item.name) }}</h3>
+          <h3 :class="{'default-active': item.active }" class="cursor-pointer" @click.stop="jumpToEnterpriseDetail(item.id, true)">
+            {{ formatName(item.anotherName || item.name) }}
+          </h3>
           <p>{{ item.industryName }}<span v-if="item.industryName && item.scaleName" class="mx-2">|</span>{{ item.scaleName }}</p>
         </div>
         <div v-if="item.active">
@@ -71,9 +73,7 @@ const handleCancel = async (item) => {
   }
 }
 .company-info {
-  float: left;
-  margin-left: 16px;
-  width: 282px;
+  width: 100%;
 }
 .company-info-top {
   display: flex;

+ 1 - 1
src/config/axios/service.js

@@ -141,7 +141,7 @@ service.interceptors.request.use(
         config
         // browserInfo: getBrowserInfo()
       }
-      console.log('加密参数', content)
+      // console.log('加密参数', content)
       errorData.push({
         time: header.timestamp,
         url: config.url,

+ 4 - 0
src/layout/company/side.vue

@@ -50,6 +50,7 @@ import enterpriseRoute from '@/router/modules/components/recruit/enterprise'
 const list = computed(() => {
   return getList(enterpriseRoute)
 })
+console.log(import.meta.env.VITE_NODE_ENV, '当前环境变量============')
 
 const info = localStorage.getItem('entBaseInfo') ? JSON.parse(localStorage.getItem('entBaseInfo')) : {}
 const getList = (arr, obj = []) => {
@@ -89,6 +90,9 @@ const getList = (arr, obj = []) => {
   if (info && Object.keys(info).length && !info?.entitlement?.personMap) obj = obj.filter(e => !e.isPersonMap)
   // 全员猎寻是否可看
   if (info && Object.keys(info).length && !info?.entitlement?.hireJob) obj = obj.filter(e => !e.hireJob)
+
+  // 生产环境隐藏门墩儿新任命
+  if (import.meta.env.VITE_NODE_ENV === 'production') obj = obj.filter(e => e.path !== '/recruit/enterprise/newlyAppointed')
   return obj
 }
 

+ 4 - 4
src/layout/enterprise.vue

@@ -3,8 +3,8 @@
     <Headers class="headers"></Headers>
     <div class="content d-flex">
       <side class="content-sticky" v-if="!router.currentRoute.value?.meta?.hideSide"></side>
-      <div class="content-box d-flex flex-column" :style="`width: ${ !isInWhiteList(route.path, whiteList) ? 'calc(100vw - 247px)' : '100%'}`">
-        <div v-if="!isInWhiteList(route.path, whiteList)" class="breadcrumbs_sticky">
+      <div class="content-box d-flex flex-column" :style="`width: ${ !isInWhiteList(route.path) ? 'calc(100vw - 230px)' : '100%'}`">
+        <div v-if="!isInWhiteList(route.path)" class="breadcrumbs_sticky">
           <div class=" d-flex align-center justify-space-between">
             <v-breadcrumbs :items="system.breadcrumbs" elevation="3">
               <template v-slot:item="{ item }">
@@ -15,7 +15,7 @@
           <v-divider></v-divider>
         </div>
         <div class="box pa-3">
-          <div v-if="!isInWhiteList(route.path, whiteList)" class="box-content">
+          <div v-if="!isInWhiteList(route.path)" class="box-content">
             <router-view :key="key"></router-view>
           </div>
           <div v-else class="full">
@@ -54,7 +54,7 @@ const whiteList = [
   '/recruit/enterprise/systemManagement/groupAccount/invite/1'
 ]
 // 查询是否在白名单内,在则不展示面包屑
-const isInWhiteList = (url, whiteList)=> {
+const isInWhiteList = (url)=> {
   const path = url.split('?')[0]
   for (const item of whiteList) {
     if (path.startsWith(item)) {

+ 18 - 19
src/layout/personal/navBar.vue

@@ -25,7 +25,12 @@
                 <defineListPage v-bind="$attrs" :title="val.title" :list="val.children" @emitClick="handleClick" :closeOnContentClick="true"></defineListPage>
               </template>
               <template v-else>
-                <span class="cursor-pointer menuList-first-title" :class="{'active-route' : route.path === val.path || route.path.includes(val.path)}" @click="handleClick(val, true)">{{ val.title }}</span>
+                <span 
+                  class="cursor-pointer menuList-first-title"
+                  :class="{'active-route' : route.path === val.path || route.path.includes(val.path)}" 
+                  @click.stop="handleClick(val, true)">
+                  {{ val.title }}
+                </span>
               </template>
             </div>
           </div>
@@ -168,31 +173,25 @@ const paths = [ // 有选中样式-路由列表
   '/recruit/personal/personalCenter/wallet', // 6
 ]
 
-const navList = [
-  // {
-  //   title: '招聘',
-  //   children: [
-  //     {
-  //       title: '门墩儿招聘',
-  //       appList: [
-  //         [{ title: '职位', path: paths[1] }, { title: '公司', path: paths[2] }]
-  //       ]
-  //     }
-  //   ]
-  // },
-  { title: '门墩儿招聘', path: '/recruit/personal/recommend', noLeaving: true },
+const navList = ref([
+  { title: '门墩儿招聘', path: '/recruit/personal' },
   { title: '门墩儿猎头', path: '/headhunting' },
   // { title: '门墩儿商城', path: '/pointsExchange' },
-  { title: '门墩儿商城', path: '/mall' },
+  { title: '门墩儿商城', path: '/pointsExchange', isEdit: true },
   { title: '火苗儿校企' },
-  // { title: '产业联合会' },
-  // { title: '数据' },
   { title: '了解门墩儿', path: '/about' }
-]
+])
+
+const mode = import.meta.env.VITE_NODE_ENV
+console.log(import.meta.env.VITE_NODE_ENV, '当前环境变量===========')
+
+const mall = navList.value.find(item => item.isEdit)
+// 区分生产环境展示地址
+mall.path = mode === 'production' ? '/pointsExchange' : '/mall'
 
 const handleClick = (e, status) => {
   if (!e.path) return
-  if (status && !e.noLeaving) window.open(e.path)
+  if (status) window.open(e.path)
   else router.push(e.path)
 }
 

+ 1 - 1
src/permission.js

@@ -33,7 +33,7 @@ router.beforeEach(async (to, from, next) => {
   } else if (getToken(tokenIndex)) {
     // 强制修改密码
     if (localStorage.getItem('entUpdatePassword') === 'needChange') fullScreen('entUpdatePassword')
-    // 强制填写个人信息
+    // 强制填写个人信息 fddeaddc47868b/ready
     else if (localStorage.getItem('necessaryInfoReady') === 'fddeaddc47868b' && tokenIndex === 2) dialogExtend('necessaryInfoDialog')
     // 企业登录免费职位广告提示
     else if (localStorage.getItem('positionAd')) {

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 11 - 0
src/plugins/dialogExtend/components/analyzeTestData.js


+ 27 - 14
src/plugins/dialogExtend/components/infoForm.vue

@@ -24,7 +24,7 @@
       </template>
       <template #analysis>
         <div style="text-align: right; width: 100%;">
-          <v-btn variant="text" color="primary" :loading="analyzeLoading" @click="handleImportAttachment">导入简历快速填写</v-btn>
+          <v-btn variant="text" color="primary" :loading="analyzeLoading" @click="handleImportAttachment">导入简历快速填写 ({{ attachmentCount }}/5)</v-btn>
         </div>
       </template>
     </CtForm>
@@ -64,6 +64,7 @@ import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
 import uploadForm from './upload.vue'
 // import { analyzeTestData } from './analyzeTestData.js'
 
+
 const props = defineProps({
   option: {
     type: Object,
@@ -76,12 +77,10 @@ let query = reactive({})
 
 // 企业名称下拉列表
 let enterpriseName = null
-// const enterpriseNameInput = ref('')
 const getEnterpriseData = async (name) => {
   const item = items.value.options.find(e => e.key === 'enterpriseId')
   if (!item) return
   if (item.items?.length && (enterpriseName === name)) return // 防抖
-  // item[item.itemTextName] = 
   enterpriseName = name
   if (name === null || name === '') { item.items = [] }
   else {
@@ -269,6 +268,10 @@ const items = ref({
   ]
 })
 
+if (import.meta.env.VITE_NODE_ENV === 'production') {
+  items.value.options = items.value.options.filter(e => e.slotName !== 'analysis')
+}
+
 
 // 选择文件
 const fileInput = ref()
@@ -283,7 +286,6 @@ const openFileInput = () => {
 // 上传头像
 const accept = ['jpg', 'png', 'jpeg']
 const handleUploadFile = async (e) => {
-  console.log('handleUploadFile:', e)
   const file = e.target.files[0]
   if (!file) return
 
@@ -328,7 +330,6 @@ const getDictData = async (dictTypeName, key) => {
     const apiType = dictTypeName === 'positionSecondData' ? 'positionSecondData' : 'dict'
     const { data } = await getDict(dictTypeName, apiType === 'dict' ? null : {}, apiType)
     item.items = data
-    // console.log(dictTypeName, '字典内容', data)
   }
 }
 
@@ -336,8 +337,8 @@ const userInfo = ref(localStorage.getItem('userInfo') ? JSON.parse(localStorage.
 const baseInfo = ref(localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {})
 items.value.options.forEach((e) => {
   if (e.dictTypeName) getDictData(e.dictTypeName, e.key) // 查字典set options
-  if (baseInfo.value && baseInfo.value[e.key]) e.value = baseInfo.value[e.key] // 人才信息回显
   if (userInfo.value && userInfo.value[e.key]) e.value = userInfo.value[e.key] // 人才信息回显
+  if (baseInfo.value && baseInfo.value[e.key]) e.value = baseInfo.value[e.key] // 人才信息回显
   if (e.key === 'sex' && e.value === '0') e.value = e.default
   if (setInfo.value[e.key]) e.value = setInfo.value[e.key]
 })
@@ -384,11 +385,15 @@ const getQuery = async () => {
 
 // 填充
 const handleAnalyzeFill = (data) => {
-  const person = data?.person || null
-  if (!person && !Object.keys(person).length) return Snackbar.warning('无可用内容!')
-  if (person.enterpriseName) getEnterpriseData(person.enterpriseName)
+  const person = data?.person || {}
+  if (!Object.keys(person).length) return Snackbar.warning('无可用内容!')
+  if (data.lastEmployed) {
+    if (data.lastEmployed.enterpriseName) person.enterpriseId = person.enterpriseName = data.lastEmployed.enterpriseName
+    if (data.lastEmployed.positionName) person.positionId = person.positionName = positionName = data.lastEmployed.positionName
+  }
+  if (data.lastPositionId) person.interestedPositionList = [data.lastPositionId]
   items.value.options.forEach((e) => {
-    if (person[e.key]) e.value = person[e.key]
+    if (e.key && person[e.key]) e.value = person[e.key]
   })
 }
 
@@ -404,15 +409,23 @@ const uploadFileSubmit = async () => {
   await savePersonResumeCv(query)
   openUploadDialog.value = false
   const data = await resumeParser2({ fileUrl: obj.url })
-  // const data = JSON.parse(JSON.stringify(analyzeTestData))
-  console.log('resumeParser2:', data)
   handleAnalyzeFill(data)
+  getAttachmentList()
   analyzeLoading.value = false
 }
 
-const handleImportAttachment = async () => {
+// handleAnalyzeFill(JSON.parse(JSON.stringify(analyzeTestData)))
+
+const attachmentCount = ref(0)
+const getAttachmentList = async () => {
   const data = await getPersonResumeCv() // 获取附件
-  if (data?.length >= 5) return Snackbar.warning('导入解析简历尝试不得超过5次!')
+  attachmentCount.value = data?.length || 0
+}
+getAttachmentList()
+
+const handleImportAttachment = async () => {
+  await getAttachmentList()
+  if (attachmentCount.value >= 5) return Snackbar.warning('导入解析简历尝试不得超过5次!')
   openUploadDialog.value = true
 }
 

+ 7 - 2
src/utils/position.js

@@ -1,4 +1,4 @@
-import { reactive, ref } from 'vue'
+import { reactive, ref, nextTick } from 'vue'
 import { getDict } from '@/hooks/web/useDictionaries'
 import { getPublicRatio } from '@/api/recruit/enterprise/position'
 
@@ -120,5 +120,10 @@ export const jumpToEnterpriseDetail = async (id, isOpenWindow = false, tabKey =
 
   // 不在优选集团中跳转企业详情
   const key = tabKey ? 'recruitmentPositions' : 'briefIntroduction'
-  isOpenWindow ? window.open(`/recruit/personal/company/details/${id}?key=${key}`) : router.push(`/recruit/personal/company/details/${id}?key=${key}`)
+  if (isOpenWindow) {
+    window.open(`/recruit/personal/company/details/${id}?key=${key}`)
+  } else {
+    await router.push(`/recruit/personal/company/details/${id}?key=${key}`)
+    await nextTick()
+  }
 }

+ 1 - 1
src/version.js

@@ -1,2 +1,2 @@
 // 版本号
-export const vue_version = 'v25.01.13.1231'
+export const vue_version = 'v25.01.14.1833'

+ 1 - 0
src/views/recruit/enterprise/invoiceManagement/index.vue

@@ -9,6 +9,7 @@
       :isTools="true"
       :showPage="true"
       :total="total"
+      height="calc(100vh - 315px)"
       :page-info="query"
       @pageHandleChange="handleChangePage"
       @add="handle"

+ 133 - 53
src/views/recruit/enterprise/newlyAppointed/index.vue

@@ -1,67 +1,106 @@
 <template>
-	<CtFilter :items="formItems" @reset="handleReset" @search="handleSearch" />
-
-	<v-card class="mt-3" elevation="5">
-		<CtTable
-			:items="items"
-			class="pa-3"
-			:headers="headers"
-			:loading="loading"
-			:disable-sort="true"
-			:elevation="0"
-			:isTools="false"
-			:showPage="true"
-			:total="total"
-			:pageInfo="query"
-			itemKey="id"
-			@pageHandleChange="handleChangePage"
-		>
-			<template #workTerritory="{ item }">
-				<div class="ellipsis" style="max-width: 150px;">
-					{{ item.workTerritory }}
-					<v-tooltip activator="parent" location="top">{{ item.workTerritory }}</v-tooltip>
+	<div>
+		<CtFilter :items="formItems" @reset="handleReset" @search="handleSearch" />
+
+		<v-card elevation="5" class="mt-3">
+			<CtTable
+				:items="items"
+				class="pa-3"
+				:headers="headers"
+				:loading="loading"
+				:disable-sort="true"
+				:elevation="0"
+				:isTools="false"
+				height="calc(100vh - 400px)"
+				:showPage="true"
+				:total="total"
+				:pageInfo="query"
+				itemKey="id"
+				@pageHandleChange="handleChangePage"
+			>
+				<template #workTerritory="{ item }">
+					<div class="ellipsis" style="max-width: 150px;">
+						{{ item.workTerritory }}
+						<v-tooltip activator="parent" location="top">{{ item.workTerritory }}</v-tooltip>
+					</div>
+				</template>
+				<template #workHistory="{ item }">
+					<div class="ellipsis" style="max-width: 150px;">
+						{{ item.workHistory }}
+						<v-tooltip activator="parent" location="top">{{ item.workHistory }}</v-tooltip>
+					</div>
+				</template>
+				<template #actions="{ item }">
+					<v-btn variant="text" color="primary" @click.stop="handleDetail(item)">详 情</v-btn>
+					<v-btn :disabled="!item.userPersonList || !item.userPersonList.length" variant="text" color="primary" @click.stop="handleContactInformation(item)">
+						联系方式
+					</v-btn>
+				</template>
+			</CtTable>
+		</v-card>
+
+		<v-navigation-drawer v-model="showDetail" absolute location="right" rounded temporary width="700" class="pa-5">
+			<div style="width: 300px; height: 300px; margin: 0 auto;">
+				<v-img :src="detail?.picUrl" width="300" height="300" />
+			</div>
+			<div class="mt-10" v-if="detail?.detailIntroduction" v-html="detail?.detailIntroduction.replace(/\n/g, '</br>')"></div>
+		</v-navigation-drawer>
+
+		<!-- 无权限提示 -->
+		<CtDialog :visible="showDialog" :widthType="4" :footer="false" titleClass="text-h6" title="系统提示" @close="showDialog = false">
+			<div class="d-flex align-center flex-column">
+				<div class="color-warning">
+					<p>很抱歉,您当前没有权限查看门墩儿新任命的相关信息</p>
+					<p>请用微信扫描下方企业微信联系门墩儿管理员开通权限</p>
 				</div>
-			</template>
-			<template #workHistory="{ item }">
-				<div class="ellipsis" style="max-width: 150px;">
-					{{ item.workHistory }}
-					<v-tooltip activator="parent" location="top">{{ item.workHistory }}</v-tooltip>
+				<div style="width: 150px; height: 150px;">
+					<v-img src="https://minio.menduner.com/dev/menduner/contact.png"></v-img>
 				</div>
-			</template>
-			<template #actions="{ item }">
-				<v-btn variant="text" color="primary" @click.stop="handleDetail(item)">详 情</v-btn>
-			</template>
-		</CtTable>
-	</v-card>
-
-	<v-navigation-drawer v-model="showDetail" absolute location="right" rounded temporary width="700" class="pa-5">
-		<div style="width: 300px; height: 300px; margin: 0 auto;">
-			<v-img :src="detail?.picUrl" width="300" height="300" />
-		</div>
-		<div class="mt-10" v-if="detail?.detailIntroduction" v-html="detail?.detailIntroduction.replace(/\n/g, '</br>')"></div>
-	</v-navigation-drawer>
-
-	<!-- 无权限提示 -->
-	<CtDialog :visible="showDialog" :widthType="4" :footer="false" titleClass="text-h6" title="系统提示" @close="showDialog = false">
-		<div class="d-flex align-center flex-column">
-			<div class="color-warning">
-				<p>很抱歉,您当前没有权限查看门墩儿新任命的相关信息</p>
-				<p>请用微信扫描下方企业微信联系门墩儿管理员开通权限</p>
-			</div>
-			<div style="width: 150px; height: 150px;">
-				<v-img src="https://minio.menduner.com/dev/menduner/contact.png"></v-img>
+				<div class="text-center ml-5">潘青海先生(Peter Pan)</div>
 			</div>
-			<div class="text-center ml-5">潘青海先生(Peter Pan)</div>
-		</div>
-  </CtDialog>
+		</CtDialog>
+
+		<!-- 联系方式 -->
+		<CtDialog :visible="showContactDialog" title="当前联系方式匹配人员" :footer="false" widthType="0" @close="showContactDialog = false">
+			<CtTable
+				:loading="false"
+				:items="contactList"
+				:headers="contactHeaders"
+				:elevation="0"
+				:isTools="false"
+				:showPage="false"
+				itemKey="user.id"
+			>
+				<template #name="{ item }">
+					<div class="d-flex align-center cursor-pointer">
+						<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="getUserAvatar(item.person.avatar, item.person.sex)"></v-avatar>
+						</v-badge>
+						<v-avatar v-else size="40" :image="getUserAvatar(item.person?.avatar, item.person?.sex)"></v-avatar>
+						<span class="ml-3">{{ item?.person?.name || item?.phone }}</span>
+					</div>
+				</template>
+				<template #actions="{ item }">
+					<v-btn variant="text" color="primary" @click.stop="handleContactInformation({ userPersonList: [item] })">联系方式</v-btn>
+				</template>
+			</CtTable>
+		</CtDialog>
+	</div>
 </template>
 
 <script setup>
 defineOptions({ name: 'newlyAppointedTable'})
-import { ref } from 'vue'
+import { ref, computed } from 'vue'
 import { getNewAppointmentsPage, getNewAppointmentsDetail } from '@/api/recruit/enterprise/newlyAppointed'
 import { useUserStore } from '@/store/user'
 import Snackbar from '@/plugins/snackbar'
+import { getUserAvatar } from '@/utils/avatar'
+import { dealDictObjData } from '@/utils/position'
 
 const store = useUserStore()
 const loading = ref(false)
@@ -217,6 +256,47 @@ const handleDetail = async (item) => {
 	detail.value = result
 	showDetail.value = true
 }
+
+// 查看联系方式
+const contactList = ref([])
+const showContactDialog = ref(false)
+const contactHeaders = ref([
+  { title: '中文名', key: 'name', sortable: false },
+  { title: '英文名', key: 'person.foreignName', sortable: false },
+	{ title: '求职状态', key: 'person.jobStatusName', sortable: false },
+	{ title: '工作经验', key: 'person.expName', sortable: false },
+	{ title: '最高学历', key: 'person.eduName', sortable: false },
+	{ title: '联系电话', key: 'phone', sortable: false, value: item => item?.person?.phone || item.user.phone },
+  { title: '电子邮箱', key: 'email', sortable: false, value: item => item?.person?.email || item.user.email },
+  { title: '操作', key: 'actions', sortable: false, align: 'center' }
+])
+const handleContactInformation = async ({ userPersonList }) => {
+	await store.getEnterpriseInfo(true)
+	// 没有权限提示联系门墩儿
+	if (!info.value?.entitlement?.newAppointment) {
+		showDialog.value = true
+		return
+	}
+	// 只匹配到一个用户的直接查看详情
+	if (userPersonList.length === 1) {
+		window.open(`/recruit/enterprise/talentPool/details/${userPersonList[0].user.id}`)
+		return
+	}
+	// 匹配到多个用户,展示列表
+	contactList.value = userPersonList.map(e => {
+		if (e?.person) e.person = Object.assign(e.person, dealDictObjData({}, e.person))
+		return e
+	})
+	showContactDialog.value = true
+}
+
+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'
+})
 </script>
 
 <style scoped lang="scss">

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

@@ -10,7 +10,7 @@
   >
     <template v-slot:activator="{ isActive, props }">
       <v-btn
-        class="mr-3 py-0 px-2"
+        class="mr-3 py-0 px-2 font-size-16"
         density="comfortable"
         :append-icon="isActive ? 'mdi mdi-menu-up' : 'mdi mdi-menu-down'"
         color="primary" variant="tonal"

+ 6 - 6
src/views/recruit/enterprise/resume/components/screen.vue

@@ -147,12 +147,12 @@ const handleChangeBounty = (e) => {
   emit('change', e)
 }
 
-watch(
-  () => props.tab,
-  () => {
-    handleReset()
-  }
-)
+// watch(
+//   () => props.tab,
+//   () => {
+//     handleReset()
+//   }
+// )
 </script>
 
 <style scoped lang="scss">

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

@@ -6,9 +6,9 @@
       :headers="headers"
       :loading="false"
       :elevation="0"
-      height="60vh"
       :disableSort="true"
       :isTools="false"
+      height="calc(100vh - 360px)"
       :showPage="true"
       :total="total"
       :page-info="pageInfo"
@@ -35,15 +35,6 @@
       </template>
       <template #actions="{ item }">
         <v-btn color="primary" variant="text" @click="handlePreviewResume(item)">查看附件</v-btn>
-        <!-- <v-btn v-if="tab === 0" :color="item.jobClosed ? 'grey' : 'primary'" variant="text" @click="handleInterviewInvite(item)">邀请面试<v-tooltip v-if="item.jobClosed" activator="parent" location="top">职位已关闭</v-tooltip></v-btn>
-        <v-btn v-if="tab === 0" :color="item.jobClosed ? 'grey' : 'primary'" variant="text" @click="handleToCommunicate(item)">立即沟通<v-tooltip v-if="item.jobClosed" activator="parent" location="top">职位已关闭</v-tooltip></v-btn>
-        <v-btn color="primary" variant="text" @click="handleDownloadAttachment(item)">下载附件</v-btn>
-        <v-btn v-if="tab === 0 || tab === 1" color="primary" variant="text" @click="handleEliminate(item)">不合适</v-btn>
-        <v-btn v-if="!item.inTalentPool" color="primary" variant="text" @click="handleJoinToTalentPool(item)">加入储备</v-btn>
-        <v-btn v-if="tab === 1 && ['3', '4'].includes(item.status)" color="primary" variant="text" @click="handleEnterByEnterprise(item)">入职</v-btn>
-        <v-btn v-if="tab === 4" color="primary" variant="text" @click="handleCancelEliminate(item)">取消不合适</v-btn>
-        <v-btn v-if="tab === 2 && item?.job?.hire" color="primary" variant="text" @click="handleSettlement(item)">结算</v-btn> -->
-
         <v-menu v-if="actionItems(item).length">
           <template v-slot:activator="{ props }">
             <v-icon v-bind="props" class="mx-3" size="20" color="primary">mdi-dots-horizontal</v-icon>
@@ -96,7 +87,7 @@ import { formatName } from '@/utils/getText'
 
 const showTip = ref(false)
 const { t } = useI18n()
-const emit = defineEmits(['refresh'])
+const emit = defineEmits(['refresh', 'page'])
 const props = defineProps({
   tab: Number,
   items: Array,
@@ -158,7 +149,7 @@ const handleToPersonDetail = async ({ userId, id }) => {
   } catch (err) {
     console.log(err)
   }
-  window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}&isMark=1`)
+  window.open(`/recruit/enterprise/talentPool/details/${userId}`)
 }
 
 // 加入人才库 

+ 11 - 2
src/views/recruit/enterprise/resume/index.vue

@@ -2,7 +2,7 @@
 <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-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f7f8fa" @update:model-value="handleChangeTab">
         <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>
@@ -131,7 +131,7 @@ const handleScreenReset = () => {
     query.value.status = null
     query.value.type = null
   }
-  if (textItems.value.value) query.value.name = textItems.value.value
+  textItems.value.value = ''
   getList()
 }
 
@@ -147,6 +147,15 @@ const handleChangeBounty = (e) => {
   query.value.pageNo = 1
   getList()
 }
+
+const handleChangeTab = (val) => {
+  query.value.pageNo = 1
+  if (val === 0) {
+    query.value.status = null
+    query.value.type = null
+  } else query.value.status = val
+  getList()
+}
 </script>
 
 <style scoped lang="scss">

+ 3 - 3
src/views/recruit/enterprise/search/recommend/index.vue

@@ -181,9 +181,9 @@ const handleCommunicate = async (item) => {
 }
 
 // 人才详情
-const handleToPersonDetail = ({ userId, id }) => {
-  if (!userId || !id) return
-  window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}`)
+const handleToPersonDetail = ({ userId }) => {
+  if (!userId) return
+  window.open(`/recruit/enterprise/talentPool/details/${userId}`)
 }
 
 const handleToInterviewManagement = () => {

+ 3 - 3
src/views/recruit/enterprise/search/retrieval/index.vue

@@ -274,9 +274,9 @@ const handleToInterviewManagement = () => {
 }
 
 // 人才详情
-const handleToPersonDetail = ({ userId, id }) => {
-  if (!userId || !id) return
-  window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}`)
+const handleToPersonDetail = ({ userId }) => {
+  if (!userId) return
+  window.open(`/recruit/enterprise/talentPool/details/${userId}`)
 }
 
 const badgeColor = computed(() => (item) => {

+ 3 - 3
src/views/recruit/enterprise/talentMap/index.vue

@@ -249,9 +249,9 @@ const textItem = ref({
 })
 
 // 人才详情
-const talentPoolDetails = ({ userId, id }) => {
-  if (!userId || !id) return
-  window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}`)
+const talentPoolDetails = ({ userId }) => {
+  if (!userId) return
+  window.open(`/recruit/enterprise/talentPool/details/${userId}`)
 }
 
 </script>

+ 9 - 4
src/views/recruit/enterprise/talentPool/components/details.vue

@@ -86,10 +86,15 @@ const operateItems = [
 // 获取人才详情
 const cvData = ref({})
 const getCvDetail = async () => {
-  const { id } = route.query
-  const { id: userId } = router.currentRoute.value.params
-  if (!id || !userId) return
-  const data = await getPersonCvDetail(userId || id)
+  const { id } = route.params
+  if (!id) {
+    Snackbar.warning('缺少简历id')
+    setTimeout(() => {
+      window.close()
+    }, 2000)
+    return
+  }
+  const data = await getPersonCvDetail(id)
   cvData.value = data
 }
 getCvDetail()

+ 3 - 3
src/views/recruit/enterprise/talentPool/index copy.vue

@@ -280,9 +280,9 @@ const openDrawer = () => {
 
 
 // 人才详情
-const talentPoolDetails = ({ userId, id }) => {
-  if (!userId || !id) return
-  window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}`)
+const talentPoolDetails = ({ userId }) => {
+  if (!userId) return
+  window.open(`/recruit/enterprise/talentPool/details/${userId}`)
 }
 </script>
 

+ 3 - 3
src/views/recruit/enterprise/talentPool/index.vue

@@ -144,9 +144,9 @@ const badgeIcon = computed(() => (item) => {
 })
 
 // 人才详情
-const talentPoolDetails = ({ userId, id }) => {
-  if (!userId || !id) return
-  window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}`)
+const talentPoolDetails = ({ userId }) => {
+  if (!userId) return
+  window.open(`/recruit/enterprise/talentPool/details/${userId}`)
 }
 </script>
 

+ 1 - 0
src/views/recruit/enterprise/tradingOrder/components/trading/balance.vue

@@ -5,6 +5,7 @@
     :headers="headers"
     :loading="false"
     :elevation="0"
+    height="calc(100vh - 310px)"
     :isTools="false"
     :showPage="true"
     :total="total"

+ 1 - 0
src/views/recruit/enterprise/tradingOrder/components/trading/recharge.vue

@@ -6,6 +6,7 @@
     :loading="false"
     :elevation="0"
     :isTools="false"
+    height="calc(100vh - 310px)"
     :showPage="true"
     :total="total"
     :page-info="query"

+ 1 - 0
src/views/recruit/enterprise/tradingOrder/components/trading/transaction.vue

@@ -4,6 +4,7 @@
     :items="dataList"
     :headers="headers"
     :loading="false"
+    height="calc(100vh - 310px)"
     :elevation="0"
     :isTools="false"
     :showPage="true"

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

@@ -57,6 +57,7 @@ import { useUserStore } from '@/store/user'
 const list = computed(() => {
   return getList(personCenterRoute[0].children[0].children)
 })
+console.log(import.meta.env.VITE_NODE_ENV, '当前环境变量===========')
 
 const getList = (arr, obj = []) => {
   arr.forEach(element => {
@@ -75,6 +76,8 @@ const getList = (arr, obj = []) => {
     }
     obj.push(data)
   })
+  // 生产环境暂时不展示收货地址
+  if (import.meta.env.VITE_NODE_ENV === 'production') obj = obj.filter(e => e.path !== '/recruit/personal/personalCenter/shippingAddress')
   return obj
 }
 

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

@@ -110,7 +110,6 @@ const goBuy = () => {
 .company {
   flex: 1;
   display: flex;
-  justify-content: end;
 }
 .company-info {
   float: left;

+ 36 - 7
src/views/recruit/personal/PersonalCenter/resume/analysis/components/educationExp.vue

@@ -7,15 +7,20 @@
 defineOptions({ name: 'resumeAnalysis-educationExp'})
 import { debounce } from 'lodash'
 import { getDict } from '@/hooks/web/useDictionaries'
-import { ref, reactive, watch } from 'vue'
+import { ref, reactive, watch, nextTick } from 'vue'
 import { schoolSearchByName, schoolMajorByName } from '@/api/recruit/personal/resume'
 import { dealCanBeInputtedSave, dealCanBeInputtedValueAndLabel } from '@/utils/getText'
+import Snackbar from '@/plugins/snackbar'
 
 const props = defineProps({
   id: {
     type: String,
     default: ''
   },
+  text: {
+    type: String,
+    default: ''
+  },
   data: {
     type: Object,
     default: () => {}
@@ -65,6 +70,27 @@ const debouncedCallbackMajor = debounce(newValue => {
   getMajorListData(newValue)
 }, 500)
 
+
+const startTimeChange = (v) => {
+  const item1 = items.value.options.find(e => e.key === 'startTime')
+  const item2 = items.value.options.find(e => e.key === 'endTime')
+  if (item1?.value && item2?.value && item1.value > item2.value) {
+    Snackbar.warning('开始时间不能大于结束时间!')
+    nextTick(() => {
+      item1.value = item2.value
+    })
+  }
+}
+const endTimeChange = (v) => {
+  const item1 = items.value.options.find(e => e.key === 'startTime')
+  const item2 = items.value.options.find(e => e.key === 'endTime')
+  if (item1?.value && item2?.value && item1.value > item2.value) {
+    Snackbar.warning('结束时间不能小于开始时间')
+    nextTick(() => {
+      item2.value = item1.value
+    })
+  }
+}
 const items = ref({
   options: [
     {
@@ -132,28 +158,28 @@ const items = ref({
       key: 'startTime',
       mode: 'month', // 时间类型 year month date time
       value: null,
-      default: '2014-01',
-      format: 'YYYY/MM',
+      default: new Date(2014, 1).getTime(),
       labelWidth: 80,
       label: '开始时间 *',
       defaultValue: new Date(2014, 1),
       disabledFutureDates: true,
       col: 6,
-      rules: [v => !!v || '请选择起始时间']
+      rules: [v => !!v || '请选择起始时间'],
+      change: startTimeChange
     },
     {
       type: 'datePicker',
       key: 'endTime',
       mode: 'month', // 时间类型 year month date time
       value: null,
-      default: '2018-01',
-      format: 'YYYY/MM',
+      default: new Date(2018, 1).getTime(),
       defaultValue: new Date(2018, 1),
       disabledFutureDates: true,
       labelWidth: 80,
       label: '结束时间 *',
       col: 6,
-      rules: [v => !!v || '请选择结束时间']
+      rules: [v => !!v || '请选择结束时间'],
+      change: endTimeChange
     },
     {
       type: 'textarea',
@@ -199,6 +225,9 @@ const submit = async () => {
     }
     else obj[e.key] = e.value
   })
+  if (obj.startTime > obj.endTime) {
+    return { id: props.id, data: null, SnackText: `${props.text} 开始时间不能大于结束时间!`}
+  }
   return { id: props.id, data: obj}
 }
 

+ 36 - 7
src/views/recruit/personal/PersonalCenter/resume/analysis/components/trainingExperience.vue

@@ -5,18 +5,44 @@
 
 <script setup>
 defineOptions({ name: 'resumeAnalysis-trainingExperience'})
-import { ref, watch } from 'vue'
+import { ref, watch, nextTick } from 'vue'
+import Snackbar from '@/plugins/snackbar'
 const props = defineProps({
   id: {
     type: String,
     default: ''
   },
+  text: {
+    type: String,
+    default: ''
+  },
   data: {
     type: Object,
     default: () => {}
   }
 })
 
+const startTimeChange = (v) => {
+  const item1 = items.value.options.find(e => e.key === 'startTime')
+  const item2 = items.value.options.find(e => e.key === 'endTime')
+  if (item1?.value && item2?.value && item1.value > item2.value) {
+    Snackbar.warning('开始时间不能大于结束时间!')
+    nextTick(() => {
+      item1.value = item2.value
+    })
+  }
+}
+const endTimeChange = (v) => {
+  const item1 = items.value.options.find(e => e.key === 'startTime')
+  const item2 = items.value.options.find(e => e.key === 'endTime')
+  if (item1?.value && item2?.value && item1.value > item2.value) {
+    Snackbar.warning('结束时间不能小于开始时间')
+    nextTick(() => {
+      item2.value = item1.value
+    })
+  }
+}
+
 const CtFormRef = ref()
 const items = ref({
   options: [
@@ -41,28 +67,28 @@ const items = ref({
       type: 'datePicker',
       key: 'startTime',
       mode: 'month',
-      value: '2020-01',
+      value: new Date(2020, 1).getTime(),
       labelWidth: 80,
-      format: 'YYYY/MM',
       defaultValue: new Date(2020, 1),
       label: '开始时间 *',
       disabledFutureDates: true,
       col: 6,
       flexStyle: 'mr-3',
-      rules: [v => !!v || '请选择培训开始时间']
+      rules: [v => !!v || '请选择培训开始时间'],
+      change: startTimeChange
     },
     {
       type: 'datePicker',
       key: 'endTime',
       mode: 'month',
-      value: '2022-01',
-      format: 'YYYY/MM',
+      value: new Date(2022, 1).getTime(),
       labelWidth: 80,
       defaultValue: new Date(2022, 1),
       label: '结束时间 *',
       disabledFutureDates: true,
       col: 6,
-      rules: [v => !!v || '请选择培训结束时间']
+      rules: [v => !!v || '请选择培训结束时间'],
+      change: endTimeChange
     },
     {
       type: 'textarea',
@@ -100,6 +126,9 @@ const submit = async () => {
   if (!valid) return { id: props.id, data: null}
   const obj = {}
   items.value.options.forEach(e => obj[e.key] = e.value)
+  if (obj.startTime > obj.endTime) {
+    return { id: props.id, data: null, SnackText: `${props.text} 开始时间不能大于结束时间!`}
+  }
   return { id: props.id, data: obj}
 }
 

+ 36 - 7
src/views/recruit/personal/PersonalCenter/resume/analysis/components/workExperience.vue

@@ -22,14 +22,19 @@
 <script setup>
 defineOptions({ name: 'resumeAnalysis-workExperience'})
 import { getDict } from '@/hooks/web/useDictionaries'
-import { ref, reactive, watch } from 'vue'
+import { ref, reactive, watch, nextTick } from 'vue'
 import { dealCanBeInputtedSave, dealCanBeInputtedValueAndLabel } from '@/utils/getText'
+import Snackbar from '@/plugins/snackbar'
 
 const props = defineProps({
   id: {
     type: String,
     default: ''
   },
+  text: {
+    type: String,
+    default: ''
+  },
   data: {
     type: Object,
     default: () => {}
@@ -71,6 +76,27 @@ const handleSoFarChange = (bool, item) => {
   }
 }
 
+const startTimeChange = (v) => {
+  const item1 = items.value.options.find(e => e.key === 'startTime')
+  const item2 = items.value.options.find(e => e.key === 'endTime')
+  if (item1?.value && item2?.value && item1.value > item2.value) {
+    Snackbar.warning('开始时间不能大于结束时间!')
+    nextTick(() => {
+      item1.value = item2.value
+    })
+  }
+}
+const endTimeChange = (v) => {
+  const item1 = items.value.options.find(e => e.key === 'startTime')
+  const item2 = items.value.options.find(e => e.key === 'endTime')
+  if (item1?.value && item2?.value && item1.value > item2.value) {
+    Snackbar.warning('结束时间不能小于开始时间')
+    nextTick(() => {
+      item2.value = item1.value
+    })
+  }
+}
+
 const items = ref({
   options: [
     {
@@ -112,11 +138,11 @@ const items = ref({
       value: null,
       label: '开始时间 *',
       labelWidth: 100,
-      default: '2020-01',
+      default: new Date(2020, 1).getTime(),
       defaultValue: new Date(2020, 1),
       disabledFutureDates: true,
-      format: 'YYYY/MM',
-      rules: [v => !!v || '请选择起始时间']
+      rules: [v => !!v || '请选择起始时间'],
+      change: startTimeChange
     },
     {
       type: 'datePicker',
@@ -124,13 +150,13 @@ const items = ref({
       slotName: 'endTime',
       mode: 'month', // 时间类型 year month date time
       value: null,
-      format: 'YYYY/MM',
-      default: '2022-01',
+      default: new Date(2022, 1).getTime(),
       defaultValue: new Date(2022, 1),
       label: '结束时间 *',
       disabledFutureDates: true,
       labelWidth: 100,
-      rules: [v => !!v || '请选择结束时间']
+      rules: [v => !!v || '请选择结束时间'],
+      change: endTimeChange
     },
     {
       type: 'textarea',
@@ -180,6 +206,9 @@ const submit = async () => {
     }
     else obj[e.key] = e.value
   })
+  if (obj.endTime && obj.startTime > obj.endTime) {
+    return { id: props.id, data: null, SnackText: `${props.text}开始时间不能大于结束时间!`}
+  }
   return { id: props.id, data: obj}
 }
 

+ 9 - 7
src/views/recruit/personal/PersonalCenter/resume/analysis/index.vue

@@ -36,7 +36,7 @@
                 <v-btn v-if="item.id !== 'person'" variant="text" color="error" density="compact" prepend-icon="mdi-delete-outline" @click="del(item)">删除</v-btn>
                 <v-btn v-else disabled variant="text" color="error" density="compact">不可删除</v-btn>
               </div>
-              <component ref="componentRef" :id="item.id" :is="item.path" :data="item.data" />
+              <component ref="componentRef" :id="item.id" :is="item.path" :text="item.text" :data="item.data" />
             </div>
             <!-- 保存 -->
             <div style="position: absolute; bottom: 0; background-color: #fff; z-index: 99; width: 100%; border-top: 1px solid #e4e7eb;" class="py-3 ml-n3 text-center">
@@ -76,6 +76,7 @@ import { saveResumeInfo, resumeParser2 } from '@/api/recruit/personal/resume'
 import { useUserStore } from '@/store/user'
 import attachmentPage from '../attachment'
 import { Base64 } from 'js-base64'
+// import { analyzeTestData } from './analyzeTestData.js'
 const { t } = useI18n()
 const props = defineProps({
   data: {
@@ -124,7 +125,7 @@ const transformToLIst = async (result) => {
         for (let index = 0; index < result[key].length; index++) {
           const obj = {...exampleList[key]}
           obj.id = obj.id + '_' + index
-          obj.text = result[key].length > 1 ? obj.text + (index+1) : obj.text
+          obj.text = result[key].length > 1 ? obj.text + '('+ (index+1) +')' : obj.text
           obj.data = result[key][index]
           formLIst.value.push(obj)
         }
@@ -133,7 +134,8 @@ const transformToLIst = async (result) => {
   }
 }
 
-const result = ref({}) // ref(JSON.parse(JSON.stringify(dataObj))) // 测试
+
+const result = ref({}) // ref(JSON.parse(JSON.stringify(analyzeTestData))) // 测试
 const fileUrl = ref('')
 const showSelect = ref(true)
 const handleAnalysis = async (url) => {
@@ -146,7 +148,6 @@ const handleAnalysis = async (url) => {
   try {
     const data = await resumeParser2({ fileUrl: url })
     result.value = data || {}
-    // result.value = {person: data.person} || {} // 测试
     await transformToLIst(result.value)
   } catch (error) {
     console.log(error)
@@ -157,7 +158,7 @@ const handleAnalysis = async (url) => {
 
 const componentRef = ref()
 const getValue = async () => {
-  let id = ''
+  let id = ''; let SnackText = ''
   let data = {}
   for (let index = 0; index < componentRef.value.length; index++) {
     const e = componentRef.value[index]
@@ -166,11 +167,12 @@ const getValue = async () => {
       data[query.id] = query.data
     } else {
       id = id ? id : query.id
+      SnackText = SnackText ? SnackText : query.SnackText
     }
   }
-  console.log('id:', id)
+  // console.log('id:', id)
   if (id) {
-    Snackbar.warning('请填写完整后提交!')
+    Snackbar.warning(SnackText || '请填写完整后提交!')
     return
   }
   // 处理data

+ 59 - 3
src/views/recruit/personal/PersonalCenter/resume/attachment/index.vue

@@ -6,7 +6,46 @@
     </div>
     <p class="font-size-14 color-999">最多只能上传5份附件简历</p>
     <div v-if="attachmentList.length" class="mt-5">
-      <div
+      <table style="width: 100%;">
+        <tr>
+          <th>名称</th>
+          <th>文件类型</th>
+          <th>上传时间</th>
+          <th>操作</th>
+        </tr>
+        <tr
+          v-for="(k, i) in attachmentList" :key="i"
+          :class="['mx-n2', 'px-2']"
+          @click.self="checkboxClick(i, !k.choose)"
+        >
+          <td>
+            <div class="d-flex flex" style="max-width: 250px;">
+              <v-checkbox-btn
+                v-if="props.analysis"
+                v-model="k.choose"
+                :label="k.title"
+                color="primary"
+                class="pe-2"
+                style="color: #333;"
+                @update:modelValue="bool => checkboxClick(i, bool)"
+              ></v-checkbox-btn>
+              <!-- <span @click="checkboxClick(i, !k.choose)">{{ k.title }}</span> -->
+              <span v-else>{{ k.title }}</span>
+            </div>
+          </td>
+          <td style="text-align: center;">.{{ k.fileType }}</td>
+          <td style="text-align: center;">{{ timesTampChange(k.createTime, 'Y-M-D h:m') }}</td>
+          <td style="text-align: center; width: 268px;">
+            <div>
+              <v-btn variant="text" color="primary" prepend-icon="mdi-eye-outline" @click="previewFile(k.url)">预览</v-btn>
+              <v-btn variant="text" color="primary" prepend-icon="mdi-arrow-down-bold-outline" @click="handleDownload(k)">下载</v-btn>
+              <v-btn variant="text" color="primary" prepend-icon="mdi-trash-can-outline" @click="handleDelete(k)">{{ $t('common.delete') }}</v-btn>
+            </div>
+          </td>
+        </tr>
+      </table>
+
+      <!-- <div
         :class="['position-item', 'mx-n2', 'px-2']" 
         v-for="(k, i) in attachmentList" 
         :key="i" 
@@ -23,7 +62,8 @@
           <v-btn variant="text" color="primary" prepend-icon="mdi-arrow-down-bold-outline" @click="handleDownload(k)">下载</v-btn>
           <v-btn variant="text" color="primary" prepend-icon="mdi-trash-can-outline" @click="handleDelete(k)">{{ $t('common.delete') }}</v-btn>
         </div>
-      </div>
+      </div> -->
+
       <div v-if="props.analysis" class="d-flex  flex-column align-center mt-15">
         <v-btn class="buttons" color="primary" @click="handleAnalysis">开始解析</v-btn>
         <v-btn class="mt-2" variant="text" color="primary" to="/recruit/personal/personalCenter/resume/online">返回在线简历</v-btn>
@@ -59,6 +99,7 @@ import Confirm from '@/plugins/confirm'
 import { useI18n } from '@/hooks/web/useI18n'
 import { getPersonResumeCv, deletePersonResumeCv, savePersonResumeCv } from '@/api/recruit/personal/resume'
 import { getBlob, saveAs, previewFile } from '@/utils'
+import { timesTampChange } from '@/utils/date'
 
 const emit = defineEmits(['analysis'])
 const props = defineProps({
@@ -98,7 +139,14 @@ const formItems = ref({
 const attachmentList = ref([])
 const getList = async () => {
   const data = await getPersonResumeCv()
-  attachmentList.value = data
+  attachmentList.value = data.map(item => {
+    const list = item.url.split('.')
+    const fileType = list?.length ? list[list.length-1] : null
+    return {
+      ...item,
+      fileType
+    }
+  })
 }
 getList()
 
@@ -198,4 +246,12 @@ const handleAnalysis = () => {
   left: 50%;
   transform: translate(-50%, -50%);
 }
+th {
+  height: 40px;
+  color: grey;
+}
+:deep(.v-label) {
+  // color: #333 !important;
+  opacity: 1;
+}
 </style>

+ 0 - 101
src/views/recruit/personal/PersonalCenter/resume/online/analysis/avatar.vue

@@ -1,101 +0,0 @@
-<!--  -->
-<template>
-  <div class="avatarsBox" @mouseover="showIcon = true" @mouseleave="showIcon = false">
-    <div style="width: 130px; height: 130px;">
-      <v-img :src="getUserAvatar(avatarResult, '1')" width="130" height="130" style="border-radius: 6px;"></v-img>
-      <div v-show="showIcon" @click="openFileInput" class="mdi mdi-camera-outline camera">
-        <input
-          type="file"
-          ref="fileInput"
-          accept="image/png, image/jpg, image/jpeg"
-          style="display: none;"
-          @change="handleUploadFile"
-        />
-      </div>
-    </div>
-  </div>
-  <!-- <Loading :visible="overlay"></Loading> -->
-  <!-- 图片裁剪 -->
-  <ImgCropper :visible="isShowCopper" :image="selectPic" :cropBoxResizable="true" @submit="handleHideCopper" :aspectRatio="1 / 1" @close="isShowCopper = false, selectPic = ''"></ImgCropper>
-</template>
-
-<script setup>
-defineOptions({ name: 'resumeAnalysis-avatar'})
-import { getUserAvatar } from '@/utils/avatar'
-// import { blobToJson } from '@/utils'
-import { ref } from 'vue'
-const props = defineProps({
-  id: {
-    type: String,
-    default: ''
-  },
-  data: {
-    type: String,
-    default: ''
-  }
-})
-
-const showIcon = ref(false)
-
-// 选择文件
-const fileInput = ref()
-const clicked = ref(false)
-const openFileInput = () => {
-  if (clicked.value) return
-  clicked.value = true
-  fileInput.value.click()
-  clicked.value = false
-}
-
-// 上传头像
-const selectPic = ref('')
-const isShowCopper = ref(false)
-const accept = ['jpg', 'png', 'jpeg']
-const handleUploadFile = async (e) => {
-  const file = e.target.files[0]
-  const arr = file.name.split('.')
-  const fileType = arr?.length ? arr[arr.length-1] : ''
-  if (!accept.includes(fileType)) return Snackbar.warning('请上传图片格式')
-  const reader = new FileReader()
-  reader.readAsDataURL(file)
-  reader.onload = () => {
-    selectPic.value = String(reader.result)
-    isShowCopper.value = true
-  }
-}
-
-const avatarResult = ref(props.data)
-
-// 图片裁剪
-const handleHideCopper = async (res) => {
-  isShowCopper.value = false
-  avatarResult.value = res?.result.blobURL || ''
-  // avatarResult.value = res?.result.dataURL || '' // dataURL文件名过长或者过大不成功
-}
-
-const submit = async () => {
-  return { id: props.id, data: avatarResult.value}
-}
-
-defineExpose({
-  id: props.id,
-  submit
-})
-</script>
-<style lang="scss" scoped>
-.avatarsBox {
-  height: 150px;
-  width: 120px;
-  position: relative;
-  cursor: pointer;
-  margin: 0 32px;
-  .camera {
-    color: #fff;
-    font-size: 42px;
-    position: absolute;
-    top: 50%;
-    left: 50%;
-    transform: translate(-50%, -50%);
-  }
-}
-</style>

+ 0 - 119
src/views/recruit/personal/PersonalCenter/resume/online/analysis/base.vue

@@ -1,119 +0,0 @@
-<template>
-  <div class="resume-box mb-3 elevation-2" id="basicInfo">
-    <div class="resume-header">
-      <div class="resume-title">{{ $t('resume.basicInfo') }}</div>
-    </div>
-    <div class="d-flex align-start mt-5">
-      <!-- 头像 -->
-      <div class="avatarsBox">
-        <div style="width: 130px; height: 130px;">
-          <v-img :src="data.avatarData" width="130" height="130" style="border-radius: 6px;"></v-img>
-        </div>
-      </div>
-      <!-- 基础信息 -->
-      <div style="flex: 1;" class="mr-8">
-        <div>
-          <span class="ml-50" style="font-size: 20px; font-weight: 600;color: var(--color-666);">
-            {{ data?.name || data?.phone }}
-          </span>
-          <div class="mt-3 d-flex">
-            <div class="listBox ml-50">
-              <div>
-                <span>性别:</span>
-                <span>{{ data?.genderInf || '' }}</span>
-              </div>
-              <div>
-                <span></span>
-                <span>{{ baseInfo?.phone || userInfo?.phone || $t('common.currentlyUnavailable') }}</span>
-              </div>
-              <div>
-                <span class="mdi mdi-email-outline"></span>
-                <span>{{ baseInfo?.email || $t('common.currentlyUnavailable') }}</span>
-              </div>
-              <div>
-                <span class="mdi mdi-calendar-blank-outline"></span>
-                <span>{{ baseInfo?.expTypeText || $t('common.currentlyUnavailable') }}</span>
-              </div>
-              <div>
-                <span class="mdi mdi-school-outline"></span>
-                <span>{{ baseInfo?.eduTypeText || $t('common.currentlyUnavailable') }}</span>
-              </div>
-              <div>
-                <span class="mdi mdi-tag-outline"></span>
-                <span>{{ baseInfo?.jobStatusText || $t('common.currentlyUnavailable') }}</span>
-              </div>
-              <div>
-                <span class="mdi mdi-cake-variant-outline"></span>
-                <span>{{ baseInfo?.birthdayText || $t('common.currentlyUnavailable') }}</span>
-              </div>
-              <div>
-                <span class="mdi mdi-home-map-marker"></span>
-                <span>{{ baseInfo?.regName || $t('common.currentlyUnavailable') }}</span>
-              </div>
-              <div>
-                <span class="mdi mdi-account-heart"></span>
-                <span>{{ baseInfo?.maritalText || $t('common.currentlyUnavailable') }}</span>
-              </div>
-              <div>
-                <span>{{ $t('resume.firstWorkTime') }}:</span>
-                <span>{{ baseInfo?.firstWorkTimeText || $t('common.currentlyUnavailable') }}</span>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup>
-defineProps({
-  data: Object
-})
-</script>
-
-<style lang="scss" scoped>
-.jobTypeCardBox {
-  position: absolute;
-  top: -22px;
-  left: 0;
-}
-.ml-50 {
-  margin-left: 50px;
-}
-.avatarsBox {
-  height: 150px;
-  width: 120px;
-  position: relative;
-  cursor: pointer;
-  margin: 0 32px;
-  .camera {
-    color: #fff;
-    font-size: 42px;
-    position: absolute;
-    top: 50%;
-    left: 50%;
-    transform: translate(-50%, -50%);
-  }
-}
-
-.listBox {
-  display: flex;
-  flex-wrap: wrap; /* 允许换行 */
-  width: 100%; /* 设置容器宽度 */
-  overflow: hidden;
-  color: var(--color-666);
-  div {
-    width: 50%;
-    margin-bottom: 10px;
-    span {
-      height: 32px;
-      line-height: 32px;
-    }
-    .mdi {
-      font-size: 22px;
-      margin-right: 8px;
-    }
-  }
-}
-</style>

+ 0 - 279
src/views/recruit/personal/PersonalCenter/resume/online/analysis/basicInfo.vue

@@ -1,279 +0,0 @@
-<template>
-  <CtForm ref="CtFormRef" :items="items" style="width: 100%;"></CtForm>
-</template>
-
-<script setup>
-defineOptions({ name: 'resumeAnalysis-basicInfo'})
-import { ref, watch } from 'vue'
-import { checkEmail } from '@/utils/validate'
-import { getDict } from '@/hooks/web/useDictionaries'
-const props = defineProps({
-  id: {
-    type: String,
-    default: ''
-  },
-  data: {
-    type: Object,
-    default: () => ({})
-  }
-})
-
-const CtFormRef = ref()
-const userInfo = ref(JSON.parse(localStorage.getItem('userInfo')))
-const items = ref({
-  options: [
-    {
-      type: 'text',
-      key: 'name',
-      value: null,
-      default: null,
-      label: '中文名 *',
-      col: 6,
-      outlined: true,
-      rules: [
-        value => {
-          if (value) return true
-          return '请输入您的中文名'
-        },
-        value => {
-          var regex = /^[\u4e00-\u9fa5]+$/
-          if (regex.test(value)) return true
-          return '请输入正确的中文名'
-        }
-      ]
-    },
-    {
-      type: 'text',
-      key: 'foreignName',
-      value: null,
-      label: '英文名',
-      col: 6,
-      outlined: true,
-      clearable: true,
-      rules: [
-        value => {
-          if (!value) return true
-          var regex = /^[A-Za-z]+(?:\s[A-Za-z]+)?$/
-          if (regex.test(value)) return true
-          return '请输入正确的英文名'
-        }
-      ]
-    },
-    {
-      type: 'ifRadio',
-      key: 'sex',
-      value: null, // '1' ? '男' : '女'
-      default: '1',
-      label: '性别 *',
-      col: 6,
-      width: 50,
-      dictTypeName: 'menduner_sex',
-      items: [],
-    },
-    {
-      type: 'phoneNumber',
-      key: 'phone',
-      value: null,
-      default: userInfo?.value?.phone || '',
-      label: '电话号码 *',
-      col: 6,
-      outlined: true,
-      rules: [v => !!v || '请填写联系手机号']
-    },
-    {
-      type: 'datePicker',
-      mode: 'date',
-      labelWidth: 80,
-      key: 'birthday',
-      value: '2000-01-01',
-      defaultValue: new Date(2000, 1, 1),
-      label: '出生日期 *',
-      disabledFutureDates: true,
-      col: 6,
-      format: 'YYYY/MM/DD',
-      flexStyle: 'mb-7',
-      outlined: true,
-      rules: [v => !!v || '请选择出生日期']
-    },
-    {
-      type: 'datePicker',
-      mode: 'month',
-      key: 'firstWorkTime',
-      value: '2000-01',
-      disabledFutureDates: true,
-      defaultValue: new Date(2000, 1),
-      format: 'YYYY/MM',
-      labelWidth: 90,
-      flexStyle: 'mb-7',
-      label: '首次工作时间',
-      col: 6,
-      outlined: true
-    },
-    {
-      type: 'text',
-      key: 'email',
-      value: null,
-      default: null,
-      label: '常用邮箱 *',
-      col: 6,
-      outlined: true,
-      rules: [
-        value => {
-          if (value) return true
-          return '请输入联系邮箱'
-        },
-        value => {
-          if (value && !checkEmail(value)) return '请输入正确的电子邮箱'
-          return true
-        }
-      ]
-    },
-    {
-      type: 'autocomplete',
-      key: 'expType',
-      value: null,
-      default: null,
-      label: '工作年限 *',
-      col: 6,
-      outlined: true,
-      itemText: 'label',
-      itemValue: 'value',
-      dictTypeName: 'menduner_exp_type',
-      rules: [v => !!v || '请选择工作年限'],
-      items: []
-    },
-    {
-      type: 'autocomplete',
-      key: 'eduType',
-      value: null,
-      default: null,
-      label: '最高学历 *',
-      col: 6,
-      outlined: true,
-      itemText: 'label',
-      itemValue: 'value',
-      dictTypeName: 'menduner_education_type',
-      rules: [v => !!v || '请选择最高学历'],
-      items: []
-    },
-    {
-      type: 'autocomplete',
-      key: 'jobType',
-      value: null,
-      default: null,
-      label: '求职类型 *',
-      col: 6,
-      outlined: true,
-      itemText: 'label',
-      itemValue: 'value',
-      dictTypeName: 'menduner_job_type',
-      rules: [v => !!v || '请选择求职类型'],
-      items: []
-    },
-    {
-      type: 'autocomplete',
-      key: 'jobStatus',
-      value: null,
-      default: null,
-      label: '求职状态 *',
-      col: 6,
-      outlined: true,
-      itemText: 'label',
-      itemValue: 'value',
-      dictTypeName: 'menduner_job_seek_status',
-      rules: [v => !!v || '请选择求职状态'],
-      items: []
-    },
-    {
-      type: 'autocomplete',
-      key: 'maritalStatus',
-      value: null,
-      default: null,
-      label: '婚姻状况',
-      col: 6,
-      outlined: true,
-      itemText: 'label',
-      itemValue: 'value',
-      dictTypeName: 'menduner_marital_status',
-      items: []
-    },
-    {
-      type: 'cascade',
-      key: 'areaId',
-      value: null,
-      default: null,
-      label: '所在城市',
-      itemText: 'name',
-      itemValue: 'id',
-      required: true,
-      checkStrictly: true,
-      clearable: false,
-      col: 6,
-      items: [],
-    },
-    {
-      type: 'cascade',
-      key: 'regId',
-      value: null,
-      default: null,
-      label: '户籍地',
-      itemText: 'name',
-      itemValue: 'id',
-      checkStrictly: true,
-      required: false,
-      clearable: true,
-      col: 6,
-      items: [],
-    },
-    
-  ]
-})
-
-items.value.options.forEach(async (e, index) => {
-  if ((index + 2) % 2 === 0) e.flexStyle = 'mr-3'
-  if (e.dictTypeName) {
-    const { data } = await getDict(e.dictTypeName)
-    e.items = data
-  }
-})
-
-watch(
-  () => props.data,
-  (newVal) => {
-    if (newVal && Object.keys(newVal)) {
-      items.value.options.forEach(e => {
-        if (newVal[e.key]) e.value = newVal[e.key]
-        else e.value = e.default || null
-      })
-    }
-  },
-  { immediate: true },
-)
-
-const submit = async () => {
-  const { valid } = await CtFormRef.value.formRef.validate()
-  if (!valid) return { id: props.id, data: null}
-  const obj = {}
-  items.value.options.forEach(e => obj[e.key] = e.value)
-  return { id: props.id, data: obj}
-}
-
-defineExpose({
-  id: props.id,
-  submit
-})
-
-getDict('areaTreeData', null, 'areaTreeData').then(({ data }) => {
-  data = data?.length && data || []
-  if (!data?.length) return console.error('areaTreeData获取失败!')
-  const chinaTreeData = data
-  if (!chinaTreeData?.length) return console.error('chinaTreeData获取失败!')
-  const workAreaProvince = items.value.options.find(e => e.key === 'areaId')
-  const regAreaProvince = items.value.options.find(e => e.key === 'regId')
-  if (workAreaProvince?.items) workAreaProvince.items = chinaTreeData
-  if (regAreaProvince?.items) regAreaProvince.items = chinaTreeData
-})
-</script>
-
-<style lang="scss" scoped>
-</style>

+ 0 - 61
src/views/recruit/personal/PersonalCenter/resume/online/analysis/eduExp.vue

@@ -1,61 +0,0 @@
-<template>
-  <div class="resume-box elevation-2 mb-3">
-    <div class="resume-header mb-3">
-      <div class="resume-title">{{ $t('resume.educationExp') }}</div>
-    </div>
-    <div v-for="(item, index) in data.educationObjs" :key="'educationExp' + index" :class="[' mx-n2', {'mt-5': index }]">
-      <div class="educExpItem">
-        <div class="level1 d-flex align-center justify-space-between" style="height: 40px;">
-          <div>
-            <span style="font-size: 18px; font-weight: bold;">{{ item.eduCollege }}</span>
-            <span class="color6 font15 ml-5">
-              <span>{{ item.startDate }}</span>
-              <span class="mx-1">至</span>
-              <span>{{ item.endDate }}</span>
-            </span>
-          </div>
-        </div>
-        <div class="level2 my-2">
-          <span class="color6 font15">{{ item.eduMajor }}</span>
-          <span class="septal-line"  v-if="item.eduMajor && item.eduDegree"></span>
-          <span class="color6 font15">{{ item.eduDegree }}</span>
-        </div>
-        <div class="level3">
-          <span class="color6 font15">
-            在校经历:
-            <p v-if="item.eduContent" v-html="item.eduContent.replace(/\n/g, '</br>')"></p>
-            <span v-else>暂无描述</span>
-          </span>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup name="EduExp">
-defineProps({
-  data: Object
-})
-</script>
-
-<style scoped lang="scss">
-.font15 { font-size: 15px;; }
-.color9 { color: var(--color-999); }
-.color6 { color: var(--color-666); }
-.educExpItem {
-  cursor: pointer;
-  border-radius: 6px;
-  padding: 2px 10px 8px;
-  &:hover {
-    background-color: var(--color-f8);
-  }
-}
-.educExpItem-edit {
-  border-radius: 6px;
-  padding: 2px 10px 8px;
-  background-color: var(--color-f8);
-}
-:deep(.el-input--large .el-input__wrapper) {
-  background-color: #f8f8f8;
-}
-</style>

+ 0 - 234
src/views/recruit/personal/PersonalCenter/resume/online/analysis/educationExp.vue

@@ -1,234 +0,0 @@
-<!--  -->
-<template>
-  <CtForm ref="CtFormRef" :items="items" style="width: 100%;"></CtForm>
-</template>
-
-<script setup>
-defineOptions({ name: 'resumeAnalysis-educationExp'})
-import { debounce } from 'lodash'
-import { getDict } from '@/hooks/web/useDictionaries'
-import { ref, reactive, watch } from 'vue'
-import { schoolSearchByName, schoolMajorByName } from '@/api/recruit/personal/resume'
-import { dealCanBeInputtedSave, dealCanBeInputtedValueAndLabel } from '@/utils/getText'
-
-const props = defineProps({
-  id: {
-    type: String,
-    default: ''
-  },
-  data: {
-    type: Object,
-    default: () => {}
-  }
-})
-
-const CtFormRef = ref()
-const dictItemsObj = reactive({})
-
-// 学校下拉列表
-const schoolNameInput = ref('')
-const getSchoolListData = async (name) => {
-  const item = items.value.options.find(e => e.key === 'schoolId')
-  if (!item) return
-  if (item.items?.length && (schoolNameInput.value === name)) return // 防抖
-  item[item.itemTextName] = schoolNameInput.value = name
-
-  if (name === null || name === '') { item.items = [] }
-  else {
-    const data = await schoolSearchByName({ name })
-    item.items = data
-  }
-}
-const debouncedCallbackSchool = debounce(newValue => {
-  if (!newValue) return
-  getSchoolListData(newValue)
-}, 500)
-
-// 专业下拉列表
-const majorNameInput = ref('')
-const getMajorListData = async (name) => {
-  const item = items.value.options.find(e => e.key === 'majorId')
-  if (name === '') { // 此接口不支持传空值
-    item.items = []
-    return
-  }
-  if (item.items?.length && (majorNameInput.value === name)) return // 防抖
-  item[item.itemTextName] = majorNameInput.value = name
-  
-  if (name === null || name === '') { item.items = [] }
-  else {
-    const data = await schoolMajorByName({ name })
-    item.items = data
-  }
-}
-const debouncedCallbackMajor = debounce(newValue => {
-  getMajorListData(newValue)
-}, 500)
-
-const items = ref({
-  options: [
-    {
-      type: 'combobox',
-      key: 'schoolId',
-      value: null,
-      default: null,
-      label: '学校名称 *',
-      col: 6,
-      outlined: true,
-      clearable: true,
-      canBeInputted: true, //
-      itemTextName: 'schoolName',
-      itemText: 'value',
-      itemValue: 'key',
-      rules: [v => !!v || '请选择学校名称'],
-      search: debouncedCallbackSchool,
-      items: []
-    },
-    {
-      type: 'combobox',
-      key: 'majorId',
-      value: null,
-      default: null,
-      label: '所学专业 *',
-      col: 6,
-      outlined: true,
-      clearable: true,
-      canBeInputted: true, //
-      itemTextName: 'major',
-      itemText: 'nameCn',
-      itemValue: 'id',
-      rules: [v => !!v || '请选择所学专业'],
-      search: debouncedCallbackMajor,
-      items: []
-    },
-    {
-      type: 'autocomplete',
-      key: 'educationType',
-      value: null,
-      default: null,
-      label: '学历 *',
-      col: 6,
-      outlined: true,
-      itemText: 'label',
-      itemValue: 'value',
-      rules: [v => !!v || '请选择学历'],
-      items: []
-    },
-    {
-      type: 'autocomplete',
-      key: 'educationSystemType',
-      value: null,
-      default: null,
-      label: '学制类型 *',
-      col: 6,
-      outlined: true,
-      itemText: 'label',
-      itemValue: 'value',
-      rules: [v => !!v || '请选择学制类型'],
-      items: dictItemsObj.educationSystemType,
-    },
-    {
-      type: 'datePicker',
-      key: 'startTime',
-      mode: 'month', // 时间类型 year month date time
-      value: null,
-      default: '2014-01',
-      format: 'YYYY/MM',
-      labelWidth: 80,
-      label: '开始时间 *',
-      defaultValue: new Date(2014, 1),
-      disabledFutureDates: true,
-      col: 6,
-      rules: [v => !!v || '请选择起始时间']
-    },
-    {
-      type: 'datePicker',
-      key: 'endTime',
-      mode: 'month', // 时间类型 year month date time
-      value: null,
-      default: '2018-01',
-      format: 'YYYY/MM',
-      defaultValue: new Date(2018, 1),
-      disabledFutureDates: true,
-      labelWidth: 80,
-      label: '结束时间 *',
-      col: 6,
-      rules: [v => !!v || '请选择结束时间']
-    },
-    {
-      type: 'textarea',
-      key: 'content',
-      value: null,
-      default: null,
-      rows: 5,
-      flexStyle: 'mt-5',
-      resize: true,
-      counter: 1600,
-      label: '在校经历',
-      outlined: true
-    },
-  ]
-})
-
-watch(
-  () => props.data,
-  (newVal) => {
-    if (newVal && Object.keys(newVal)) {
-      schoolNameInput.value = newVal.schoolName || null
-      majorNameInput.value = newVal.major || null
-      //
-      items.value.options.forEach(e => {
-        if (newVal[e.key]) e.value = newVal[e.key]
-        else e.value = e.default || null
-        if (e.canBeInputted) { // 特殊处理可输入下拉框
-          dealCanBeInputtedValueAndLabel(e, newVal)
-        }
-      })
-    }
-  },
-  { immediate: true },
-)
-
-const submit = async () => {
-  const { valid } = await CtFormRef.value.formRef.validate()
-  if (!valid) return { id: props.id, data: null}
-  const obj = {}
-  items.value.options.forEach(e => {
-    if (e.canBeInputted) { // 特殊处理可输入下拉框
-      dealCanBeInputtedSave(e, obj)
-    }
-    else obj[e.key] = e.value
-  })
-  return { id: props.id, data: obj}
-}
-
-defineExpose({
-  id: props.id,
-  submit
-})
-
-// 左侧加mr
-items.value.options.forEach((e, index) => {
-  if (((index + 2) % 2 === 0) && Boolean(e.col) && e.col !== 12) e.flexStyle = 'mr-3'
-})
-
-// 获取字典内容
-const dictList = [
-  { type: 'menduner_education_type', key: 'educationType' },
-  { type: 'menduner_education_system_type', key: 'educationSystemType' }
-]
-const getDictData = async (obj) => {
-  const item = items.value.options.find(e => e.key === obj.key)
-  if (item) { //  && !item.items?.length
-    const { data } = await getDict(obj.type)
-    item.items = data || []
-    dictItemsObj[obj.key] = data || []
-  }
-}
-const getOptions = () => {
-  dictList.forEach(obj =>  getDictData(obj))
-}
-getOptions()
-</script>
-<style lang="scss" scoped>
-</style>

+ 0 - 24
src/views/recruit/personal/PersonalCenter/resume/online/analysis/evaluation.vue

@@ -1,24 +0,0 @@
-<template>
-  <div class="resume-box elevation-2 mb-3" id="selfEvaluation">
-    <div class="resume-header">
-      <div class="resume-title">{{ $t('resume.personalAdvantages') }}</div>
-    </div>
-    <div class="content-list mt-3">
-      <div v-if="data.contMyDesc" class="resumeNoDataText" v-html="data.contMyDesc.replace(/\n/g, '</br>')"></div>
-    </div>
-  </div>
-</template>
-
-<script setup>
-defineOptions({ name: 'Evaluation'})
-
-defineProps({
-  data: Object
-})
-</script>
-
-<style scoped lang="scss">
-.content-list {
-  white-space: pre-line;
-}
-</style>

+ 0 - 59
src/views/recruit/personal/PersonalCenter/resume/online/analysis/selfEvaluation.vue

@@ -1,59 +0,0 @@
-<!--  -->
-<template>
-  <div>
-    <v-textarea 
-      v-model="advantage" 
-      :label="$t('resume.dataDefaultPrompt') + $t('resume.personalAdvantages') + '...'" 
-      variant="outlined" 
-      counter="300" 
-      color="primary" 
-      validate-on="input"
-      :rules="advantageRules"
-    ></v-textarea>
-  </div>
-</template>
-
-<script setup>
-defineOptions({ name: 'resumeAnalysis-selfEvaluation'})
-// import Snackbar from '@/plugins/snackbar'
-import DOMPurify from 'dompurify'
-import { ref } from 'vue'
-const props = defineProps({
-  id: {
-    type: String,
-    default: ''
-  },
-  data: {
-    type: String,
-    default: ''
-  }
-})
-
-const advantage = ref(props.data)
-const advantageRules = ref([
-  value => {
-    if (value) return true
-    return '请输入您的个人优势'
-  },
-  value => {
-    if (value?.length <= 300) return true
-    return '请输入2-300个字符'
-  }
-])
-
-const submit = async () => {
-  advantage.value = DOMPurify.sanitize(advantage.value)
-  if (!advantage.value) {
-    // Snackbar.warning('请先输入个人优势!')
-    return { id: props.id, data: null}
-  }
-  return { id: props.id, data: { content: advantage.value}}
-}
-
-defineExpose({
-  id: props.id,
-  submit
-})
-</script>
-<style lang="scss" scoped>
-</style>

+ 0 - 112
src/views/recruit/personal/PersonalCenter/resume/online/analysis/trainingExperience.vue

@@ -1,112 +0,0 @@
-<!--  -->
-<template>
-  <CtForm ref="CtFormRef" :items="items" style="width: 100%;"></CtForm>
-</template>
-
-<script setup>
-defineOptions({ name: 'resumeAnalysis-trainingExperience'})
-import { ref, watch } from 'vue'
-const props = defineProps({
-  id: {
-    type: String,
-    default: ''
-  },
-  data: {
-    type: Object,
-    default: () => {}
-  }
-})
-
-const CtFormRef = ref()
-const items = ref({
-  options: [
-    {
-      type: 'text',
-      key: 'orgName',
-      value: '',
-      col: 6,
-      label: '培训中心 *',
-      flexStyle: 'mr-3',
-      rules: [v => !!v || '请输入培训中心']
-    },
-    {
-      type: 'text',
-      key: 'course',
-      value: '',
-      col: 6,
-      label: '培训课程 *',
-      rules: [v => !!v || '请输入培训课程']
-    },
-    {
-      type: 'datePicker',
-      key: 'startTime',
-      mode: 'month',
-      value: '2020-01',
-      labelWidth: 80,
-      format: 'YYYY/MM',
-      defaultValue: new Date(2020, 1),
-      label: '开始时间 *',
-      disabledFutureDates: true,
-      col: 6,
-      flexStyle: 'mr-3',
-      rules: [v => !!v || '请选择培训开始时间']
-    },
-    {
-      type: 'datePicker',
-      key: 'endTime',
-      mode: 'month',
-      value: '2022-01',
-      format: 'YYYY/MM',
-      labelWidth: 80,
-      defaultValue: new Date(2022, 1),
-      label: '结束时间 *',
-      disabledFutureDates: true,
-      col: 6,
-      rules: [v => !!v || '请选择培训结束时间']
-    },
-    {
-      type: 'textarea',
-      key: 'content',
-      value: '',
-      label: '培训描述',
-      rows: 5,
-      flexStyle: 'mt-5',
-      resize: true,
-      counter: 2000,
-      rules: [
-        value => {
-          if (value?.length <= 2000) return true
-          return '请输入2-2000个字符'
-        }
-      ]
-    }
-  ]
-})
-
-watch(
-  () => props.data,
-  (newVal) => {
-    if (newVal && Object.keys(newVal)) {
-      items.value.options.forEach(e => {
-        if (newVal[e.key]) e.value = newVal[e.key]
-      })
-    }
-  },
-  { immediate: true }
-)
-
-const submit = async () => {
-  const { valid } = await CtFormRef.value.formRef.validate()
-  if (!valid) return { id: props.id, data: null}
-  const obj = {}
-  items.value.options.forEach(e => obj[e.key] = e.value)
-  return { id: props.id, data: obj}
-}
-
-defineExpose({
-  id: props.id,
-  submit
-})
-</script>
-<style lang="scss" scoped>
-</style>

+ 0 - 57
src/views/recruit/personal/PersonalCenter/resume/online/analysis/workExp.vue

@@ -1,57 +0,0 @@
-<template>
-  <div class="resume-box elevation-2 mb-3">
-    <div class="resume-header mb-3">
-      <div class="resume-title">
-        {{ $t('resume.workExperience') }}
-      </div>
-    </div>
-    <div v-for="(item, index) in data.jobExpObjs" :key="'workExperience' + index">
-      <div class="educExpItem">
-        <div class="level1 d-flex align-center justify-space-between" style="height: 40px;">
-          <div>
-            <span style="font-size: 18px; font-weight: bold;">{{ item.jobCpy }}</span>
-            <span class="color6 font15 ml-5">
-              <span>{{ item.startDate }}</span>
-              <span class="mx-1">至</span>
-              <span>{{ item.endDate }}</span>
-            </span>
-          </div>
-        </div>
-        <div class="level2 my-2">
-          <span class="color6 font15">{{ item.jobPosition }}</span>
-        </div>
-        <div class="level3">
-          <span class="color6 font15">{{ $t('resume.jobContent') }}<p v-if="item.jobContent" v-html="item.jobContent.replace(/\n/g, '</br>')"></p></span>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup name="WorkExp">
-defineProps({
-  data: Object
-})
-</script>
-
-<style scoped lang="scss">
-.font15 { font-size: 15px;; }
-.color9 { color: var(--color-999); }
-.color6 { color: var(--color-666); }
-.educExpItem {
-  cursor: pointer;
-  border-radius: 6px;
-  padding: 2px 10px 8px;
-  &:hover {
-    background-color: var(--color-f8);
-  }
-}
-.educExpItem-edit {
-  border-radius: 6px;
-  padding: 2px 10px 8px;
-  background-color: var(--color-f8);
-}
-:deep(.el-input--large .el-input__wrapper) {
-  background-color: #f8f8f8;
-}
-</style>

+ 0 - 203
src/views/recruit/personal/PersonalCenter/resume/online/analysis/workExperience.vue

@@ -1,203 +0,0 @@
-<!--  -->
-<template>
-  <CtForm ref="CtFormRef" :items="items" style="width: 100%;">
-    <template #endTime="{ item }">
-      <div>
-        <v-checkbox-btn
-          v-model="item.soFar"
-          color="primary"
-          :label="$t('sys.soFar')"
-          class="ml-2"
-          :disabled="false"
-          :style="`line-height: ${item.dense === 'default' ? 56 : item.dense === 'comfortable' ? 48 : 40 }px;`"
-          style="width: 80px;"
-          hide-details
-          @update:modelValue="v => handleSoFarChange(v, item)"
-        ></v-checkbox-btn>
-      </div>
-    </template>
-  </CtForm>
-</template>
-
-<script setup>
-defineOptions({ name: 'resumeAnalysis-workExperience'})
-import { getDict } from '@/hooks/web/useDictionaries'
-import { ref, reactive, watch } from 'vue'
-import { dealCanBeInputtedSave, dealCanBeInputtedValueAndLabel } from '@/utils/getText'
-
-const props = defineProps({
-  id: {
-    type: String,
-    default: ''
-  },
-  data: {
-    type: Object,
-    default: () => {}
-  }
-})
-
-const CtFormRef = ref()
-const dictItemsObj = reactive({})
-dictItemsObj.educationSystemType = [{ label: '全日制', value: '0' }, { label: '非全日制', value: '1' }]
-
-
-// 企业名称下拉列表
-const enterpriseNameInput = ref('')
-const getEnterpriseData = async (name) => {
-  const item = items.value.options.find(e => e.key === 'enterpriseId')
-  if (!item) return
-  if (item.items?.length && (enterpriseNameInput.value === name)) return // 防抖
-  item[item.itemTextName] = enterpriseNameInput.value = name
-  if (name === null || name === '') { item.items = [] }
-  else {
-    const data = await enterpriseSearchByName({ name })
-    item.items = data
-  }
-}
-
-const positionSearch = (name) => {
-  const item = items.value.options.find(e => e.key === 'positionId')
-  if (!item) return
-  item[item.itemTextName] = name
-}
-
-// 《至今》复选框事件
-const handleSoFarChange = (bool, item) => {
-  const opObj = items.value.options.find(e => e.key === item.key)
-  if (opObj) {
-    opObj.value = null
-    opObj.soFar = bool
-    opObj.disabled = bool ? true : false
-  }
-}
-
-const items = ref({
-  options: [
-    {
-      type: 'combobox',
-      key: 'enterpriseId',
-      value: null,
-      default: null,
-      label: '企业名称 *',
-      outlined: true,
-      clearable: true,
-      canBeInputted: true, //
-      itemTextName: 'enterpriseName',
-      itemText: 'value',
-      itemValue: 'key',
-      rules: [v => !!v || '请选择企业名称'],
-      search: getEnterpriseData,
-      items: []
-    },
-    {
-      type: 'combobox',
-      key: 'positionId',
-      value: null,
-      default: null,
-      label: '职位名称 *',
-      outlined: true,
-      clearable: true,
-      canBeInputted: true, //
-      itemTextName: 'positionName',
-      itemText: 'nameCn',
-      itemValue: 'id',
-      rules: [v => !!v || '请选择职位名称'],
-      search: val => positionSearch(val),
-      items: []
-    },
-    {
-      type: 'datePicker',
-      key: 'startTime',
-      mode: 'month', // 时间类型 year month date time
-      value: null,
-      label: '开始时间 *',
-      labelWidth: 100,
-      default: '2020-01',
-      defaultValue: new Date(2020, 1),
-      disabledFutureDates: true,
-      format: 'YYYY/MM',
-      rules: [v => !!v || '请选择起始时间']
-    },
-    {
-      type: 'datePicker',
-      key: 'endTime',
-      slotName: 'endTime',
-      mode: 'month', // 时间类型 year month date time
-      value: null,
-      format: 'YYYY/MM',
-      default: '2022-01',
-      defaultValue: new Date(2022, 1),
-      label: '结束时间 *',
-      disabledFutureDates: true,
-      labelWidth: 100,
-      rules: [v => !!v || '请选择结束时间']
-    },
-    {
-      type: 'textarea',
-      key: 'content',
-      value: null,
-      default: null,
-      rows: 10,
-      resize: true,
-      flexStyle: 'mt-5',
-      counter: 2000,
-      label: '工作内容 *',
-      outlined: true,
-      rules: [v => !!v || '请输入工作内容']
-    },
-  ]
-})
-// 左侧加mr
-// items.value.options.forEach((e, index) => {
-//   if (((index + 2) % 2 === 0) && Boolean(e.col) && e.col !== 12) e.flexStyle = 'mr-3'
-// })
-
-watch(
-  () => props.data,
-  (newVal) => {
-    if (newVal && Object.keys(newVal)) {
-      enterpriseNameInput.value = newVal.enterpriseName
-      //
-      items.value.options.forEach(e => {
-        if (newVal[e.key]) e.value = newVal[e.key]
-        if (e.canBeInputted) { // 特殊处理可输入下拉框
-          dealCanBeInputtedValueAndLabel(e, newVal)
-        }
-      })
-      if (!newVal.endTime) handleSoFarChange(true, {key: 'endTime'})
-    }
-  },
-  { immediate: true },
-)
-
-const submit = async () => {
-  const { valid } = await CtFormRef.value.formRef.validate()
-  if (!valid) return { id: props.id, data: null }
-  const obj = {}
-  items.value.options.forEach(e => {
-    if (e.canBeInputted) { // 特殊处理可输入下拉框
-      dealCanBeInputtedSave(e, obj)
-    }
-    else obj[e.key] = e.value
-  })
-  return { id: props.id, data: obj}
-}
-
-defineExpose({
-  id: props.id,
-  submit
-})
-
-// 岗位treeChildren
-let positionTreeChildrenData = []
-getDict('positionTreeData', null, 'positionTreeData').then(({ data }) => {
-  data = data?.length && data || []
-  data.forEach(e => {
-    if (e?.children?.length) positionTreeChildrenData = positionTreeChildrenData.concat(e.children)
-  })
-  items.value.options.find(e => e.key === 'positionId').items = positionTreeChildrenData
-})
-
-</script>
-<style lang="scss" scoped>
-</style>

+ 30 - 6
src/views/recruit/personal/PersonalCenter/resume/online/components/educationExp.vue

@@ -103,6 +103,27 @@ const debouncedCallbackMajor = debounce(newValue => {
   getMajorListData(newValue)
 }, 500)
 
+const startTimeChange = (v) => {
+  const item1 = formItems.value.options.find(e => e.key === 'startTime')
+  const item2 = formItems.value.options.find(e => e.key === 'endTime')
+  if (item1?.value && item2?.value && item1.value > item2.value) {
+    Snackbar.warning('开始时间不能大于结束时间!')
+    nextTick(() => {
+      item1.value = item2.value
+    })
+  }
+}
+const endTimeChange = (v) => {
+  const item1 = formItems.value.options.find(e => e.key === 'startTime')
+  const item2 = formItems.value.options.find(e => e.key === 'endTime')
+  if (item1?.value && item2?.value && item1.value > item2.value) {
+    Snackbar.warning('结束时间不能小于开始时间')
+    nextTick(() => {
+      item2.value = item1.value
+    })
+  }
+}
+
 const formItems = ref({
   options: [
     {
@@ -170,28 +191,30 @@ const formItems = ref({
       key: 'startTime',
       mode: 'month', // 时间类型 year month date time
       value: null,
-      default: '2014-01',
-      format: 'YYYY/MM',
+      default: new Date(2014, 1).getTime(),
+      // format: 'YYYY/MM',
       labelWidth: 120,
       label: '开始时间 *',
       defaultValue: new Date(2014, 1),
       disabledFutureDates: true,
       col: 6,
-      rules: [v => !!v || '请选择起始时间']
+      rules: [v => !!v || '请选择起始时间'],
+      change: startTimeChange
     },
     {
       type: 'datePicker',
       key: 'endTime',
       mode: 'month', // 时间类型 year month date time
       value: null,
-      default: '2018-01',
-      format: 'YYYY/MM',
+      default: new Date(2018, 1).getTime(),
+      // format: 'YYYY/MM',
       defaultValue: new Date(2018, 1),
       disabledFutureDates: true,
       labelWidth: 120,
       label: '结束时间 *',
       col: 6,
-      rules: [v => !!v || '请选择结束时间']
+      rules: [v => !!v || '请选择结束时间'],
+      change: endTimeChange
     },
     {
       type: 'textarea',
@@ -267,6 +290,7 @@ const handleSave = async () => {
     else obj[e.key] = e.value
   })
   if (!obj.startTime || !obj.endTime) return Snackbar.warning('请选择起始时间!')
+  if (obj.startTime > obj.endTime) return Snackbar.warning('开始时间不能大于结束时间!')
   if (editId.value) obj.id = editId.value
   await saveResumeEduExp(obj)
   Snackbar.success('保存成功!')

+ 29 - 6
src/views/recruit/personal/PersonalCenter/resume/online/components/trainingExperience.vue

@@ -57,6 +57,28 @@ const formPageRef = ref()
 const type = ref('')
 const editId = ref(null)
 const loading = ref(false)
+
+const startTimeChange = (v) => {
+  const item1 = items.value.options.find(e => e.key === 'startTime')
+  const item2 = items.value.options.find(e => e.key === 'endTime')
+  if (item1?.value && item2?.value && item1.value > item2.value) {
+    Snackbar.warning('开始时间不能大于结束时间!')
+    nextTick(() => {
+      item1.value = item2.value
+    })
+  }
+}
+const endTimeChange = (v) => {
+  const item1 = items.value.options.find(e => e.key === 'startTime')
+  const item2 = items.value.options.find(e => e.key === 'endTime')
+  if (item1?.value && item2?.value && item1.value > item2.value) {
+    Snackbar.warning('结束时间不能小于开始时间')
+    nextTick(() => {
+      item2.value = item1.value
+    })
+  }
+}
+
 const items = ref({
   options: [
     {
@@ -80,28 +102,28 @@ const items = ref({
       type: 'datePicker',
       key: 'startTime',
       mode: 'month',
-      value: '2020-01',
+      value: new Date(2014, 1).getTime(),
       labelWidth: 140,
-      format: 'YYYY/MM',
       defaultValue: new Date(2020, 1),
       label: '培训开始时间 *',
       disabledFutureDates: true,
       col: 6,
       flexStyle: 'mr-3',
-      rules: [v => !!v || '请选择培训开始时间']
+      rules: [v => !!v || '请选择培训开始时间'],
+      change: startTimeChange
     },
     {
       type: 'datePicker',
       key: 'endTime',
       mode: 'month',
-      value: '2022-01',
-      format: 'YYYY/MM',
+      value: new Date(2022, 1).getTime(),
       labelWidth: 140,
       defaultValue: new Date(2022, 1),
       label: '培训结束时间 *',
       disabledFutureDates: true,
       col: 6,
-      rules: [v => !!v || '请选择培训结束时间']
+      rules: [v => !!v || '请选择培训结束时间'],
+      change: endTimeChange
     },
     {
       type: 'textarea',
@@ -151,6 +173,7 @@ const handleSave = async () => {
     obj[e.key] = e.value
   })
   if (!obj.startTime || !obj.endTime) return Snackbar.warning('请选择培训起始时间!')
+  if (obj.startTime > obj.endTime) return Snackbar.warning('开始时间不能大于结束时间!')
   if (editId.value) obj.id = editId.value
   try {
     await saveResumeTrainExp(obj)

+ 29 - 6
src/views/recruit/personal/PersonalCenter/resume/online/components/workExperience.vue

@@ -110,6 +110,27 @@ const handleSoFarChange = (bool, item) => {
   }
 }
 
+const startTimeChange = (v) => {
+  const item1 = formItems.value.options.find(e => e.key === 'startTime')
+  const item2 = formItems.value.options.find(e => e.key === 'endTime')
+  if (item1?.value && item2?.value && item1.value > item2.value) {
+    Snackbar.warning('开始时间不能大于结束时间!')
+    nextTick(() => {
+      item1.value = item2.value
+    })
+  }
+}
+const endTimeChange = (v) => {
+  const item1 = formItems.value.options.find(e => e.key === 'startTime')
+  const item2 = formItems.value.options.find(e => e.key === 'endTime')
+  if (item1?.value && item2?.value && item1.value > item2.value) {
+    Snackbar.warning('结束时间不能小于开始时间')
+    nextTick(() => {
+      item2.value = item1.value
+    })
+  }
+}
+
 const formItems = ref({
   options: [
     {
@@ -127,7 +148,8 @@ const formItems = ref({
       itemValue: 'key',
       rules: [v => !!v || '请选择企业名称'],
       search: getEnterpriseData,
-      items: []
+      items: [],
+      change: startTimeChange
     },
     {
       type: 'combobox',
@@ -144,7 +166,8 @@ const formItems = ref({
       itemValue: 'id',
       rules: [v => !!v || '请选择职位名称'],
       search: val => positionSearch(val),
-      items: []
+      items: [],
+      change: endTimeChange
     },
     {
       type: 'datePicker',
@@ -153,10 +176,9 @@ const formItems = ref({
       value: null,
       label: '开始时间 *',
       labelWidth: 120,
-      default: '2020-01',
+      default: new Date(2020, 1).getTime(),
       defaultValue: new Date(2020, 1),
       disabledFutureDates: true,
-      format: 'YYYY/MM',
       col: 6,
       rules: [v => !!v || '请选择起始时间']
     },
@@ -166,8 +188,7 @@ const formItems = ref({
       slotName: 'endTime',
       mode: 'month', // 时间类型 year month date time
       value: null,
-      format: 'YYYY/MM',
-      default: '2022-01',
+      default: new Date(2022, 1).getTime(),
       defaultValue: new Date(2022, 1),
       label: '结束时间 *',
       disabledFutureDates: true,
@@ -256,6 +277,8 @@ const handleSave = async () => {
   })
   const endTimeSofar = formItems.value.options.find(e => e.key === 'endTime').soFar
   if ((!endTimeSofar && (!obj.startTime || !obj.endTime)) || (endTimeSofar && !obj.startTime)) return Snackbar.warning(endTimeSofar && !obj.startTime ? '请选择开始时间' : '请选择起始时间!')
+  if (obj.endTime && obj.startTime > obj.endTime) return Snackbar.warning('开始时间不能大于结束时间!')
+
   if (editId.value) obj.id = editId.value
   await saveResumeWorkExp(obj)
   Snackbar.success('保存成功!')

+ 4 - 48
src/views/recruit/personal/PersonalCenter/resume/online/index.vue

@@ -3,7 +3,7 @@
     <div class="tabHeader">
       <div class="d-flex align-center justify-space-between">
         <ProgressBar :num="completeNum" :total="items?.length" style="width: 100%;"></ProgressBar>
-        <v-btn variant="text" color="primary" @click="handleImportAttachment">导入简历快速填写</v-btn>
+        <v-btn v-if="mode !== 'production'" variant="text" color="primary" @click="handleImportAttachment">导入简历快速填写</v-btn>
       </div>
       <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f7f8fa">
         <v-tab v-for="k in items" :key="k.path" :value="k.value" @click="handleClick(k)">
@@ -26,10 +26,6 @@
       />
     </div>
   </div>
-
-  <Loading :visible="loading"></Loading>
-  <selectResumeDialog v-model="showAttachment" title="请选择已有的简历导入" :list="attachmentList" @submit="handleAttachmentSubmit" @close="showAttachment = false" />
-  <resumeAnalysis v-model="showAnalysis" :data="result" :fileUrl="encodeURIComponent(fileUrl)" @close="analysisClose" />
 </template>
 
 <script setup>
@@ -45,18 +41,13 @@ import workExperience from './components/workExperience.vue'
 // import projectExperience from './components/projectExperience.vue'
 import vocationalSkills from './components/vocationalSkills.vue'
 import { resumePersonFillAll, getPersonResumeCv, resumeParser2 } from '@/api/recruit/personal/resume'
-import Snackbar from '@/plugins/snackbar'
-import { useRouter } from 'vue-router'
-import selectResumeDialog from '@/views/recruit/personal/position/components/jobDetails/selectResumeDialog.vue'
-import resumeAnalysis from './resumeAnalysis.vue'
-import { Base64 } from 'js-base64'
-// import { dataObj } from './test.js'
 
-const router = useRouter()
+const mode = import.meta.env.VITE_NODE_ENV
+console.log(import.meta.env.VITE_NODE_ENV, '当前环境变量===========')
+
 const { t } = useI18n()
 const scrollBox = ref()
 const tab = ref(0)
-const loading = ref(false)
 const paths = [basicInfo, selfEvaluation, jobIntention, educationExp, workExperience, vocationalSkills, trainingExperience]
 const items = ref([
   { text: t('resume.basicInfo'), id: 'basicInfo', status: false },
@@ -119,43 +110,8 @@ const getAttachmentList = async () => {
 getAttachmentList()
 
 // 导入附件简历
-const showAttachment = ref(false)
 const handleImportAttachment = () => {
   window.open('/recruit/personal/resume/analysis')
-  // if (!attachmentList.value.length) {
-  //   Snackbar.warning('请先上传附件简历')
-  //   router.push('/recruit/personal/personalCenter/resume/attachment')
-  //   return
-  // }
-  // showAnalysis.value = false
-  // showAttachment.value = true
-}
-
-const result = ref({}) // ref(JSON.parse(JSON.stringify(dataObj))) // 测试
-const fileUrl = ref('')
-const showAnalysis = ref(false)
-const handleAttachmentSubmit = async (val) => {
-  if (!val) return
-  const url = attachmentList.value.find(e => e.id === val).url
-  const baseUrl = import.meta.env.VITE_PREVIEW_URL
-  fileUrl.value = !url.includes('.pdf') ?  `${baseUrl}/onlinePreview?url=${encodeURIComponent(Base64.encode(url))}` : url
-  showAttachment.value = false
-  loading.value = true
-  try {
-    const data = await resumeParser2({ fileUrl: fileUrl.value })
-    result.value = data || {}
-    // result.value = {person: data.person} || {} // 测试
-    showAnalysis.value = true
-  } catch (error) {
-    console.log(error)
-  } finally {
-    loading.value = false
-  }
-}
-
-const analysisClose = () => {
-  result.value = {}
-  showAnalysis.value = false
 }
 
 </script>

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

@@ -16,13 +16,19 @@ import PointExchangeRecord from './dynamic/pointExchangeRecord.vue'
 import MyPrize from './dynamic/myPrize.vue'
 import { useRoute } from 'vue-router'
 
+const mode = import.meta.env.VITE_NODE_ENV
+console.log(import.meta.env.VITE_NODE_ENV, '当前环境变量===========')
 const tab = ref(0)
-const items = shallowRef([
+const items = shallowRef(mode === 'production' ? [
+  { label: '余额充值、购买会员订单', value: 0, path: RechargeVipOrder }
+] : [
   { label: '余额充值、购买会员订单', value: 0, path: RechargeVipOrder },
   { label: '商城交易订单', value: 1, path: MallOrder },
   { label: '积分兑换记录', value: 2, path: PointExchangeRecord },
   { label: '我的奖品', value: 3, path: MyPrize }
 ])
+
+
 const route = useRoute()
 const { key } = route.query
 if (key) tab.value = Number(key)

+ 3 - 3
src/views/recruit/personal/company/index.vue

@@ -71,7 +71,7 @@ const handleSearch = async (val, key) => {
   pages.value.pageNo = 1
   query.value[key] = val
   dealRouteQuery(query.value)
-  await getCompanyData()
+  // await getCompanyData()
 }
 
 const inputChange = async({ idName: key, values }) => { // areaIds
@@ -83,7 +83,7 @@ const inputChange = async({ idName: key, values }) => { // areaIds
   }
   pages.value.pageNo = 1
   dealRouteQuery(query.value)
-  await getCompanyData()
+  // await getCompanyData()
 }
 
 const noParams = ref(true)
@@ -120,7 +120,7 @@ const handleClear = () => {
   }
   if (query.value.name || query.value.areaIds?.length) {
     dealRouteQuery(query.value)
-    getCompanyData()
+    // getCompanyData()
   } else {
     router.push(route.path)
     items.value = []

+ 17 - 16
src/views/recruit/personal/companyDetail/index.vue

@@ -1,13 +1,11 @@
 <template>
   <div class="default-width banner px-6">
     <div v-if="Object.keys(info).length">
-      <div class="banner-title" v-if="Object.keys(info).length">
+      <div class="banner-title pt-3" v-if="Object.keys(info).length">
         <div class="float-left d-flex align-center">
           <v-img width="60" height="60" :src="info.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'"></v-img>
           <div class="ml-4">
-            <div class="contact-name">
-              {{ formatName(info.enterprise.anotherName || info.enterprise.name) }}
-            </div>
+            <div class="contact-name">{{ formatName(info.enterprise.anotherName || info.enterprise.name) }}</div>
             <div class="contact-info">
               {{ info.scaleName }}
               <span v-if="info.industryName && info.scaleName">·</span> 
@@ -16,24 +14,22 @@
           </div>
         </div>
         <div class="float-right d-flex">
-          <div class="tools-box text-center" v-if="info.jobAdvertisedCount">
-            <div class="tools-box-number">{{ info.jobAdvertisedCount }}</div>
-            <div class="tools-box-text">职位在招</div>
-          </div>
-          <!-- 是否关注该企业 -->
-          <v-tooltip location="bottom">
-            <template v-slot:activator="{ props }">
-              <v-icon v-bind="props" class="ml-5 mr-2" size="25" :color="isCollection ? 'error' : ''" @click.stop="handleFollow">{{ isCollection ? 'mdi-heart' : 'mdi-heart-outline' }}</v-icon>
-            </template>
-            <span>关注该企业</span>
-          </v-tooltip>
+          <v-btn color="primary" variant="text" size="large" @click.stop="handleReturn" prepend-icon="mdi-chevron-triple-left">返回上一页</v-btn>
         </div>
       </div>
+      <div class="text-end mb-3">
+        <v-tooltip location="bottom">
+          <template v-slot:activator="{ props }">
+            <v-icon v-bind="props" class="ml-5 mr-2" size="25" :color="isCollection ? 'error' : ''" @click.stop="handleFollow">{{ isCollection ? 'mdi-heart' : 'mdi-heart-outline' }}</v-icon>
+          </template>
+          <span>关注该企业</span>
+        </v-tooltip>
+      </div>
       <v-divider></v-divider>
       <div class="mt-3">
         <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f3f3f3" @update:model-value="handleTabClick">
           <v-tab :value="1">公司简介</v-tab>
-          <v-tab :value="2">在招职位{{ info.jobAdvertisedCount ? `(${info.jobAdvertisedCount})` : '' }}</v-tab>
+          <v-tab :value="2">在招职位</v-tab>
         </v-tabs>
         <div class="d-flex" v-if="Object.keys(info).length">
           <div class="content-left">
@@ -94,6 +90,11 @@ const handleTabClick = () => {
   router.push(`${route.path}?key=${tab.value === 1 ? 'briefIntroduction' : 'recruitmentPositions'}`)
 }
 
+// 返回上一页
+const handleReturn = () => {
+  router.history?.length ? router.go(-1) : router.push('/recruitHome')
+}
+
 // 企业埋点
 const handleEnterpriseClick = async () => {
   if (id) return

+ 5 - 3
src/views/recruit/personal/home/components/advertisement/index.vue

@@ -9,9 +9,11 @@
     </div>
 
     <div no-gutters class="mt-5 d-flex flex-wrap" style="width: 100%;">
-      <v-card v-for="(k, i) in list" :key="i" class="col-item" @click.stop="jumpToEnterpriseDetail(k.link, true)">
-        <v-img :src="k.img"/>
-      </v-card>
+      <v-hover v-slot="{ isHovering, props }" v-for="(k, i) in list" :key="i">
+        <v-card  v-bind="props" class="col-item" :class="isHovering ? 'elevation-10' : 'elevation-3'" @click.stop="jumpToEnterpriseDetail(k.link, true)">
+          <v-img :src="k.img"/>
+        </v-card>
+      </v-hover>
     </div>
   </div>
 </template>

+ 4 - 6
src/views/recruit/personal/home/components/homeJobTypeCard/index.vue

@@ -34,9 +34,6 @@
     </v-card>
     <v-card v-if="rightObj.show" class="right-item-box elevation-5" :style="{'left': itemLeft + 'px'}">
       <div class="rightCard d-flex">
-        <div class="px-3 pt-2">
-          <v-icon color="primary" size="30" @click.stop="handleMouseLeave">mdi-close-circle-outline</v-icon>
-        </div>
         <div class="rightContent">
           <div
             v-if="!rightObj.data.children?.length"
@@ -51,6 +48,9 @@
           >
           {{ val.nameCn }}</div>
         </div>
+        <div class="px-3 pt-2">
+          <v-icon color="primary" size="40" @click.stop="handleMouseLeave">mdi-close-circle-outline</v-icon>
+        </div>
       </div>
     </v-card>
     <v-card height="392px" class="card rightCardBox">
@@ -75,9 +75,7 @@
 import { getPositionTreeClick } from '@/api/common/index'
 import { getDict } from '@/hooks/web/useDictionaries'
 import { reactive, ref } from 'vue';
-import { useRouter } from 'vue-router'
 import { getWebContent } from '@/api/common'
-const router = useRouter()
 defineOptions({ name:'common-components-homepage-jobTypeCard'})
 
 // 轮播图片
@@ -96,7 +94,7 @@ getSystemWebContent()
 const handleJobClick = async (val) => {
   if (!val?.id) return
   await getPositionTreeClick({ id: val.id }) // 埋点
-  router.push(`/recruit/personal/position?positionId=${val.id}`)
+  window.open(`/recruit/personal/position?positionId=${val.id}`)
   // const obj = selectItems.value.includes(val.id)
   // if (props.isSingle) {
   //   selectItems.value = obj ? [] : [val.id]

+ 16 - 8
src/views/recruit/personal/home/components/hotJobs.vue

@@ -1,27 +1,35 @@
 <template>
   <div class="default-width mb-6 d-flex align-center justify-center">
-    <span class="mr-2 color-primary" style="width: 80px; min-width: 80px;">{{ $t('position.popularPosition') }}:</span>
+    <span class="mr-2 color-primary font-weight-bold" style="width: 80px; min-width: 80px;">{{ $t('position.popularPosition') }}:</span>
     <div style="overflow: hidden; height: 40px; ">
-      <span v-for="(item, index) in jobs" :key="index" label size="small" class="mr-2 my-1 tag"  @click.stop="handleClick(item)">{{ item.nameCn }}</span>
+      <v-hover v-slot="{ isHovering, props }" v-for="(item, index) in jobs" :key="index">
+        <span 
+          v-bind="props"
+          v-ripple.center
+          label
+          size="small"
+          class="mr-2 my-1 tag" 
+          :class="isHovering ? 'elevation-1' : ''"
+          @click.stop="handleClick(item)"
+        >
+          {{ item.nameCn }}
+        </span>
+      </v-hover>
     </div>
   </div>
 </template>
 
 <script setup>
 import { getHotPositionList } from '@/api/common/index'
-import { useRouter } from 'vue-router'
 import Snackbar from '@/plugins/snackbar'
 import { ref } from 'vue';
 defineOptions({ name:'personal-hotJobs-list'})
-const router = useRouter()
-
 
 const handleClick = (item) => { 
   if (!item?.id) {
-    console.log('岗位信息失效 岗位id不存在')
-    return Snackbar.warning('岗位信息失效')
+    return Snackbar.warning('岗位信息失效,请更换岗位查看详情')
   }
-  router.push({ path: '/recruit/personal/position',query: { positionId: item.id } })
+  window.open('/recruit/personal/position?positionId=' + item.id)
  }
 
 // 获取行业树形

+ 10 - 7
src/views/recruit/personal/home/components/hotPromotedPositions.vue

@@ -13,7 +13,7 @@
     </v-tabs>
     <v-window v-model="tab" class="mt-5">
       <v-window-item v-for="v in 3" :value="v" :key="v">
-        <PositionCard v-if="items.filter(Boolean) && items.length" :items="items" :tab="tab" @position="handlePosition" @enterprise="handleEnterprise"></PositionCard>
+        <PositionCard v-if="items.filter(Boolean) && items.length" :isOpenWindow="false" :items="items" :tab="tab" @position="handlePosition" @enterprise="handleEnterprise"></PositionCard>
         <Empty v-else class="mb-3" :elevation="false"></Empty>
       </v-window-item>
       <!-- <v-window-item :value="1">
@@ -21,16 +21,16 @@
         <Empty v-else class="mb-3" :elevation="false"></Empty>
       </v-window-item>
       <v-window-item :value="2">
-        <PositionCard v-if="items.filter(Boolean) && items.length" :items="items" :tab="tab" @position="handlePosition" @enterprise="handleEnterprise"></PositionCard>
+        <PositionCard v-if="items.filter(Boolean) && items.length" :isOpenWindow="false" :items="items" :tab="tab" @position="handlePosition"></PositionCard>
         <Empty v-else class="mb-3" :elevation="false"></Empty>
       </v-window-item>
       <v-window-item :value="3">
-        <PositionCard v-if="items.filter(Boolean) && items.length" :items="items" :tab="tab" @position="handlePosition" @enterprise="handleEnterprise"></PositionCard>
+        <PositionCard v-if="items.filter(Boolean) && items.length" :isOpenWindow="false" :items="items" :tab="tab" @position="handlePosition"></PositionCard>
         <Empty v-else class="mb-3" :elevation="false"></Empty>
       </v-window-item> -->
     </v-window>
     <div class="text-center mt-5" style="border-top: 1px solid #ccc; padding-top: 30px;">
-      <v-btn class="buttons btnColor" to="/recruit/personal/position">{{ $t('position.moreBtn') }}</v-btn>
+      <v-btn class="buttons btnColor" @click.stop="handleToMore">{{ $t('position.moreBtn') }}</v-btn>
     </div>
   </div>
 </template>
@@ -40,7 +40,9 @@ import PositionCard from '@/components/Position/item.vue'
 import { ref } from 'vue'
 import { getPromotedPosition, getLatestPosition, getHirePosition } from '@/api/position'
 import { dealDictArrayData } from '@/utils/position'
+import { useRouter } from 'vue-router'
 
+const router = useRouter()
 const tab = ref(1)
 const items = ref([])
 
@@ -56,10 +58,11 @@ getPositionList()
 // 职位详情
 const handlePosition = (item) => {
   if (!item.id) return
-  window.open(`/recruit/personal/position/details/${item.id}`)
+  router.push(`/recruit/personal/position/details/${item.id}`)
 }
-const handleEnterprise = (item) => {
-  window.open(`/recruit/personal/company/details/${item.enterpriseId}?key=briefIntroduction`)
+
+const handleToMore = () => {
+  window.open('/recruit/personal/position')
 }
 </script>
 

+ 5 - 1
src/views/recruit/personal/home/components/popularEnterprises.vue

@@ -9,7 +9,7 @@
     <HotPromoted v-if="items.length" class="mt-5" :items="items"></HotPromoted>
     <Empty v-else :elevation="false" class="mt-3" message="暂无精选企业"></Empty>
     <div v-if="items.length" class="text-center">
-      <v-btn class="buttons btnColor" color="primary" to="/recruit/personal/company">{{ $t('enterprise.moreBtn') }}</v-btn>
+      <v-btn class="buttons btnColor" color="primary" @click.stop="handleToMore">{{ $t('enterprise.moreBtn') }}</v-btn>
     </div>
   </div>
 </template>
@@ -33,4 +33,8 @@ const getHotEnterpriseList = async () => {
   })
 }
 getHotEnterpriseList()
+
+const handleToMore = () => {
+  window.open('/recruit/personal/company')
+}
 </script>

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

@@ -97,7 +97,7 @@ const handleLeftClick = (val) => {
 
 const router = useRouter()
 const handleSearch = (val) => {
-  if (val) router.push(`/recruit/personal/position?content=${val}`)
+  window.open(val ? `/recruit/personal/position?content=${val}` : `/recruit/personal/position`)
 }
 
 // const store = useUserStore()

+ 1 - 1
src/views/recruit/personal/position/components/conditionFilter/commonStyle.vue

@@ -10,7 +10,7 @@
   >
     <template v-slot:activator="{ isActive, props }">
       <v-btn
-        class="mr-3 py-0 px-2"
+        class="mr-3 py-0 px-2 font-size-16"
         :class="defineProps.btnClass"
         density="comfortable"
         :append-icon="isActive ? 'mdi mdi-menu-up' : 'mdi mdi-menu-down'"

+ 9 - 3
src/views/recruit/personal/position/components/details.vue

@@ -1,11 +1,12 @@
 <template>
   <div style="position: relative;">
     <div class="banner px-6" id="share" :class="{'default-width': defaultWidth}">
-      <div class="banner-title d-flex justify-space-between">
-        <div class="d-flex align-center">
+      <div class="banner-title d-flex justify-space-between align-center">
+        <div class="d-flex align-center justify-between">
           <h1>{{ formatName(info.name) }}</h1>
-          <svg-icon v-if="info.hire" class="ml-5" name="pin" size="50"></svg-icon>
+          <svg-icon class="ml-5" name="pin" size="50"></svg-icon>
         </div>
+        <v-btn color="primary" variant="text" size="large" @click.stop="handleReturn" prepend-icon="mdi-chevron-triple-left">返回上一页</v-btn>
       </div>
       <div class="text-end">
         <span v-if="!info.payFrom && !info.payTo" class="salary font-size-20">面议</span>
@@ -263,6 +264,11 @@ const loginClose = () => {
   Snackbar.warning(loginCloseWarningWord)
 }
 
+// 返回上一页
+const handleReturn = () => {
+  router.history?.length ? router.go(-1) : router.push('/recruitHome')
+}
+
 // 富文本内容处理,去除多余的换行空格等
 const cleanedHtml = (text) => {
   let cleaned = text.replace(/\n/g, '</br>')

+ 1 - 1
src/views/recruit/personal/position/index.vue

@@ -119,7 +119,7 @@ const updateRouter = () => {
   query.date = new Date().getTime() // 用于前端刷新路由参数
   router.push({ path: route.path, query })
   pageInfo.pageNo = 1
-  getData()
+  // getData()
 }
 
 // 参数改变

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott