فهرست منبع

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

zhengnaiwen_citu 4 ماه پیش
والد
کامیت
9ea20c243c
43فایلهای تغییر یافته به همراه593 افزوده شده و 382 حذف شده
  1. 1 0
      src/api/common/index.js
  2. 1 0
      src/api/recruit/enterprise/enterpriseInvite.js
  3. 82 73
      src/components/CtTooltip/index.js
  4. 9 10
      src/components/Enterprise/hotPromoted.vue
  5. 9 16
      src/components/Position/item.vue
  6. 1 4
      src/components/Position/longCompany.vue
  7. 1 4
      src/components/Position/longStrip.vue
  8. 16 22
      src/components/PositionLongStrip/item.vue
  9. 1 1
      src/components/PreviewImg/previewImage.vue
  10. 3 2
      src/components/headSearch/index.vue
  11. 1 1
      src/config/axios/service.js
  12. 1 1
      src/permission.js
  13. 40 2
      src/router/modules/common.js
  14. 0 45
      src/router/modules/components/headhunting.js
  15. 4 0
      src/store/user.js
  16. 1 0
      src/styles/index.css
  17. 0 0
      src/styles/index.min.css
  18. 3 14
      src/styles/recruit/company.css
  19. 1 1
      src/styles/recruit/company.min.css
  20. 3 14
      src/styles/recruit/company.scss
  21. 1 1
      src/utils/auth.js
  22. 5 3
      src/utils/position.js
  23. 3 3
      src/views/recruit/enterprise/hirePosition/components/jobRequirements.vue
  24. 3 4
      src/views/recruit/enterprise/positionManagement/components/jobRequirements.vue
  25. 212 0
      src/views/recruit/enterprise/systemManagement/groupAccount/inviteConfirmEnt-old.vue
  26. 75 74
      src/views/recruit/enterprise/systemManagement/groupAccount/inviteConfirmEnt.vue
  27. 1 4
      src/views/recruit/personal/PersonalCenter/jobFeedback/components/interview/item.vue
  28. 1 4
      src/views/recruit/personal/PersonalCenter/jobFeedback/components/seenMe.vue
  29. 23 16
      src/views/recruit/personal/company/components/companyItem.vue
  30. 3 3
      src/views/recruit/personal/company/index.vue
  31. 8 4
      src/views/recruit/personal/companyDetail/components/positions.vue
  32. 1 1
      src/views/recruit/personal/home/components/advertisement/preferred.vue
  33. 4 4
      src/views/recruit/personal/home/components/hotJobs.vue
  34. 5 3
      src/views/recruit/personal/home/components/hotPromotedPositions.vue
  35. 3 1
      src/views/recruit/personal/home/components/popularEnterprises.vue
  36. 1 2
      src/views/recruit/personal/position/components/conditionFilter.vue
  37. 20 11
      src/views/recruit/personal/position/components/conditionFilter/commonPath.vue
  38. 17 7
      src/views/recruit/personal/position/components/details.vue
  39. 2 11
      src/views/recruit/personal/position/components/dict.js
  40. 1 1
      src/views/recruit/personal/position/components/poster.vue
  41. 10 10
      src/views/recruit/personal/position/index.vue
  42. 1 5
      src/views/recruit/personal/recommend/components/positionList.vue
  43. 15 0
      src/views/recruit/personal/recommend/index.vue

+ 1 - 0
src/api/common/index.js

@@ -229,6 +229,7 @@ export const uploadFile = async (data) => {
 // 获取当前登录的企业用户信息
 export const getEnterprisingUserInfo = async (params) => {
   return await request.get({
+    tokenIndex: 1, // 使用招聘token
     url: '/app-api/menduner/system/recruit/user/get',
     params
   })

+ 1 - 0
src/api/recruit/enterprise/enterpriseInvite.js

@@ -26,6 +26,7 @@ export const enterpriseInviteRefresh = async (code) => {
 // 同意加入
 export const enterpriseInviteRecordConsent = async (params) => {
   return await request.post({
+    tokenIndex: 1, // 使用招聘token
     url: '/app-api/menduner/system/recruit/enterprise-invite-record/consent',
     params
   })

+ 82 - 73
src/components/CtTooltip/index.js

@@ -9,10 +9,17 @@ import MyToolTip from './ToolTip.vue'
 // 位置定位
 function calculationLocation(el, target, placements) {
   if (!el || !target) return;
+  console.log('el:', el)
+  console.log('target:', target)
+
   el.tooltipPosition.y = 0;
   el.tooltipPosition.x = 0;
-  let el_dom = el.$el.nextElementSibling.getBoundingClientRect()
+  let el_dom = el.$el?.nextElementSibling?.getBoundingClientRect() || target?.w_tipInstance?.$el?.nextElementSibling?.getBoundingClientRect() || null
   let target_dom = target.getBoundingClientRect()
+  if(!el_dom) {
+    console.log('el_dom:不存在', el_dom)
+    return
+  }
 
   if (placements === "left") {
     el.tooltipPosition.x = target_dom.x - el_dom.width - 10
@@ -38,6 +45,7 @@ function getElStyleAttr(element, attr) {
   return styles[attr]
 }
 
+// const positionXY = getPosition(el)
 // const getPosition = (target) => {
 //   const x = target.offsetLeft + target.offsetWidth/2
 //   const y = target.offsetTop + target.offsetHeight/2
@@ -47,78 +55,79 @@ function getElStyleAttr(element, attr) {
 const isOverflow = (target) => {
   const scrollWidth = target.scrollWidth
   const offsetWidth = target.offsetWidth
-  // const range = document.createRange()
-  // range.setStart(target, 0)
-  // range.setEnd(target, target.childNodes.length)
-  // const rangeWidth = range.getBoundingClientRect().width
-  // const padding = (parseInt(getElStyleAttr(target, 'paddingLeft'), 10) || 0) + (parseInt(getElStyleAttr(target, 'paddingRight'), 10) || 0)
-  // return (rangeWidth + padding > target.offsetWidth) || scrollWidth > offsetWidth
-  return scrollWidth > offsetWidth
+  const range = document.createRange()
+  range.setStart(target, 0)
+  range.setEnd(target, target.childNodes.length)
+  const rangeWidth = range.getBoundingClientRect().width
+  const padding = (parseInt(getElStyleAttr(target, 'paddingLeft'), 10) || 0) + (parseInt(getElStyleAttr(target, 'paddingRight'), 10) || 0)
+  return (rangeWidth + padding > target.offsetWidth) || scrollWidth > offsetWidth
+  // return scrollWidth > offsetWidth
 }
 
-export const ellipsisTooltip = {
-  // mounted(el, binding) {
-  //   //获取指令的参数
-  //   const {
-  //       value: {
-  //           placement, content, destroyOnLeave
-  //       } = {}
-  //   } = binding;
-  //   // 加上超出...样式
-  //   el.style.overflow = "hidden";
-  //   el.style.textOverflow = "ellipsis";
-  //   el.style.whiteSpace = "nowrap";
-  //   //鼠标移开时 清除元素
-  //   const onMouseLeave = () => {
-  //     if (el.w_tipInstance) {
-  //       el.w_tipInstance.hiddenTip()
-  //       el.w_tooltip.remove()
-  //       el.w_tipInstance = null
-  //       el.w_tooltip = null
-  //     }
-  //   };
-  //   const onMouseEnter = () => {
-  //     // 判断内容长度 需要展示
-  //     if (isOverflow(el)) {
-  //       // const positionXY = getPosition(el)
-  //       const directiveList = allPlacements.filter(placement => binding.modifiers[placement])
-  //       const placements = directiveList.length ? directiveList : allPlacements
-  //       if (!el.w_tooltip) {
-  //         // 创建tooltip实例
-  //         const vm = createApp(MyToolTip)
-  //         // 创建根元素
-  //         el.w_tooltip = document.createElement('div')
-  //         // 挂载到页面
-  //         document.body.appendChild(el.w_tooltip)
-  //         el.w_tooltip.id = `tooltip_${Math.floor(Math.random() * 10000)}`
-  //         el.w_tipInstance = vm.mount(el.w_tooltip)
-  //       }
-  //       // 设置 tooltip 显示方向
-  //       el.w_tipInstance.placements = placement || placements[0] || 'top'
-  //       // 设置显示内容
-  //       el.w_tipInstance.setContent(content || el.innerText)
-  //       // 使 tooltip 显示
-  //       el.w_tipInstance.showTip()
-  //       nextTick(() => {
-  //         // 计算 tooltip 在页面中的位置
-  //         calculationLocation(el.w_tipInstance, el, placements[0])
-  //       })
-  //       el._scrollHandler = () => {
-  //         // 重新定位位置
-  //         if (el.w_tipInstance && el.w_tipInstance.tooltipShow) calculationLocation(el.w_tipInstance, el, placements[0])
-  //       }
-  //       window.addEventListener('scroll', el._scrollHandler)
-  //       const _destroyOnLeave = destroyOnLeave || true
-  //       if (_destroyOnLeave) el.addEventListener("mouseleave", onMouseLeave);
-  //     }
-  //   };
-  //   el.addEventListener("mouseenter", onMouseEnter);
-  // },
-  // unmounted(el) {
-  //   if (el.w_tooltip) {
-  //     document.body.removeChild(el.w_tooltip)
-  //   }
-  //   window.removeEventListener('scroll', el._scrollHandler)
-  // }
-}
+export const ellipsisTooltip = localStorage.getItem('useEllipseTooltip') ? {
+  mounted(el, binding) {
+    //获取指令的参数
+    const {
+        value: {
+            placement, content, destroyOnLeave
+        } = {}
+    } = binding;
+    // 加上超出...样式
+    el.style.overflow = "hidden";
+    el.style.textOverflow = "ellipsis";
+    el.style.whiteSpace = "nowrap";
+    //鼠标移开时 清除元素
+    const onMouseLeave = () => {
+      if (el.w_tipInstance) {
+        el.w_tipInstance.hiddenTip()
+        el.w_tooltip.remove()
+        el.w_tipInstance = null
+        el.w_tooltip = null
+      }
+    };
+    const onMouseEnter = () => {
+      // 判断内容长度 需要展示
+      if (isOverflow(el)) {
+        const directiveList = allPlacements.filter(placement => binding.modifiers[placement])
+        const placements = directiveList.length ? directiveList : allPlacements
+        console.log('w_tooltip1:', el.w_tooltip)
+        // if (!el.w_tooltip) {}
+        // 创建tooltip实例
+        const vm = createApp(MyToolTip)
+        // 创建根元素
+        el.w_tooltip = document.createElement('div')
+        // 挂载到页面
+        document.body.appendChild(el.w_tooltip)
+        el.w_tooltip.id = `tooltip_${Math.floor(Math.random() * 10000)}`
+        el.w_tipInstance = vm.mount(el.w_tooltip)
+        console.log('w_tooltip2:', el.w_tooltip)
+        console.log('w_tipInstance:', el.w_tipInstance)
+        // 设置 tooltip 显示方向
+        el.w_tipInstance.placements = placement || placements[0] || 'top'
+        // 设置显示内容
+        el.w_tipInstance.setContent(content || el.innerText)
+        // 使 tooltip 显示
+        el.w_tipInstance.showTip()
+        nextTick(() => {
+          // 计算 tooltip 在页面中的位置
+          calculationLocation(el.w_tipInstance, el, placements[0])
+        })
+        el._scrollHandler = () => {
+          // 重新定位位置
+          if (el.w_tipInstance && el.w_tipInstance.tooltipShow) calculationLocation(el.w_tipInstance, el, placements[0])
+        }
+        window.addEventListener('scroll', el._scrollHandler)
+        const _destroyOnLeave = destroyOnLeave || true
+        if (_destroyOnLeave) el.addEventListener("mouseleave", onMouseLeave);
+      }
+    };
+    el.addEventListener("mouseenter", onMouseEnter);
+  },
+  unmounted(el) {
+    if (el.w_tooltip) {
+      document.body.removeChild(el.w_tooltip)
+    }
+    window.removeEventListener('scroll', el._scrollHandler)
+  }
+} : {}
 

+ 9 - 10
src/components/Enterprise/hotPromoted.vue

@@ -24,7 +24,6 @@
           <span class="welfareTag" v-for="(k, i) in item.enterprise.welfareList" :key="i">{{ spaces(i ? 4 : 0) + k }}</span>
         </div>
         <!-- 职位列表 -->
-        <!-- :class="{'company-job-item-hover': k.active}" -->
         <ul class="company-job-list pt-3">
           <li v-for="(k, i) in item.jobList" :key="i" @mouseenter="k.active = true" @mouseleave="k.active = false" @click="handleClickPosition(k)">
             <v-card :elevation="k.active ? 5 : 0" class="company-job-item cursor-pointer mb-3">
@@ -33,16 +32,16 @@
                 <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>
               </div>
-              <div style="height: 24px; overflow: hidden; color: #808080;">
-                <span v-for="(j, index) in desc" :key="index">
-                  <span v-if="k[j] || j === 'areaName'" class="mr-1 font-size-13">
-                    {{ j === 'areaName' ? !k.areaId ? '全国' : k.area?.str : k[j] }}
-                    <!-- {{ (j === 'areaName' && !k.areaId) ? '全国' : k[j] }} -->
+              <div class="d-flex font-size-13" style="height: 24px; overflow: hidden; color: #808080; line-height: 24px;">
+                <div class="text-truncate mr-3" style="flex: 1">
+                  <span v-for="(j, index) in desc" :key="index">
+                    <span v-if="k[j] || j === 'areaName'" class="mr-1">
+                      {{ j === 'areaName' ? !k.areaId ? '全国' : k.area?.str : k[j] }}
+                    </span>
+                    <span v-if="index !== desc.length - 1 && (k[j] || j === 'areaName') && k[desc[index + 1]]" class="septal-line ml-1"></span>
                   </span>
-                  <span v-if="index !== desc.length - 1 && (k[j] || j === 'areaName') && k[desc[index + 1]]" class="septal-line ml-1"></span>
-                  <!-- <span v-if="k[j] && index !== desc.length - 1 && (k[desc[index + 1]] || (j === 'areaName' && !k.areaId))" class="septal-line ml-1"></span> -->
-                </span>
-                <span class="font-size-13 float-right">{{ timesTampChange(k.updateTime, 'Y-M-D') }}</span>
+                </div>
+                <div style="width: 73px;">{{ timesTampChange(k.updateTime, 'Y-M-D') }}</div>
               </div>
             </v-card>
           </li>

+ 9 - 16
src/components/Position/item.vue

@@ -1,8 +1,11 @@
 <template>
   <div class="d-flex">
     <div class="position-box">
-      <div class="sub-li" v-for="(item, index) in props.items" :key="index" :style="{'height': tab === 3 && item.hire ? '180px' : '149px'}">
-        <div class="job-info" @click.stop="handlePosition(item)" @mouseenter="item.active = true" @mouseleave="item.active = false">
+      <div class="sub-li" v-for="(item, index) in props.items" :key="index" 
+        :style="{'height': tab === 3 && item.hire ? '180px' : '149px'}"
+        :class="item.active ? 'elevation-8' : 'elevation-3'"
+        @click.stop="handlePosition(item)" @mouseenter="item.active = true" @mouseleave="item.active = false">
+        <div class="job-info">
           <div class="sub-li-top">
             <div class="sub-li-info">
               <p :class="['name', {'default-active': item.active }]">{{ formatName(item.name) }}</p>
@@ -10,14 +13,13 @@
             </div>
           </div>
           <div class="d-flex justify-space-between align-center">
-            <div>
-              <span v-for="(j, i) in desc" :key="i" class="font-size-13" style="color: #808080;">
+            <div class="text-truncate" style="color: #808080;" :style="{'width': !item.payFrom && !item.payTo ? '290px' : '190px'}">
+              <span v-for="(j, i) in desc" :key="i" class="font-size-13" style="">
                 <span
                   v-if="item[j.value] || j.value === 'areaName'"
                   class="mr-1 d-inline-block"
                 >
                 {{ j.value === 'areaName' ? !item.areaId ? '全国' : item.area?.str : item[j.value] }}
-                  <!-- {{ (j.value === 'areaName' && !item.areaId) ? '全国' :  item[j.value] }} -->
                 </span>
                 <span
                   v-if="i !== desc.length - 1 && (item[j.value] || (j.value === 'areaName' && !item.areaId)) && item[desc[i + 1].value]"
@@ -40,13 +42,13 @@
           </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, isOpenWindow)">
+        <div class="sub-li-bottom">
           <div class="user-info">
             <div class="d-flex align-center">
               <v-avatar size="35">
                 <v-img :src="item.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" />
               </v-avatar>
-              <span v-ellipse-tooltip.top class="names ml-2 font-size-14 ellipsis" style="max-width: 88%;">
+              <span v-ellipse-tooltip.top class="names ml-2 font-size-14 text-truncate" style="max-width: 88%;">
                 {{ formatName(item.anotherName || item.enterpriseName) }}
                 <span class="color-999 font-size-13 ml-3">
                   <span>{{ item.industryName }}</span>
@@ -127,9 +129,6 @@ const handlePosition = (item) => {
   &:nth-child(3n) {
     margin-right: 0;
   }
-  &:hover {
-    box-shadow: 0 2px 12px 0 rgba(153, 153, 153, 1);
-  }
 }
 .job-info {
   position: relative;
@@ -200,15 +199,9 @@ const handlePosition = (item) => {
 .user-info {
   // display: flex;
   padding: 10px 20px 12px;
-  // align-items: center;
-  // justify-content: space-between;
-  background-color: #b2dfdb2b;
 }
 .names {
   font-weight: 500;
   color: #404040;
-  &:hover {
-    color: var(--v-error-base);
-  }
 }
 </style>

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

@@ -1,6 +1,6 @@
 <template>
   <div>
-    <div class="sub-li mb-3 elevation-2" v-for="item in list" :key="item.id" @mouseenter="item.active = true" @mouseleave="item.active = false">
+    <div class="sub-li mb-3" :class="item.active ? 'elevation-8' : 'elevation-3'" v-for="item in list" :key="item.id" @mouseenter="item.active = true" @mouseleave="item.active = false">
       <div class="company-info-top">
         <div class="company-info">
           <div class="float-left mr-5">
@@ -68,9 +68,6 @@ const handleCancel = async (item) => {
   &:nth-child(4n) {
     margin-right: 0;
   }
-  &:hover {
-    box-shadow: 0px 3px 5px -1px var(--v-shadow-key-umbra-opacity, rgba(0, 0, 0, 0.2)), 0px 5px 8px 0px var(--v-shadow-key-penumbra-opacity, rgba(0, 0, 0, 0.14)), 0px 1px 14px 0px var(--v-shadow-key-ambient-opacity, rgba(0, 0, 0, 0.12)) !important;
-  }
 }
 .company-info {
   width: 100%;

+ 1 - 4
src/components/Position/longStrip.vue

@@ -1,6 +1,6 @@
 <template>
   <div>
-    <div class="position-item mb-3 job-closed elevation-2" style="position: relative;" 
+    <div class="position-item mb-3 job-closed" style="position: relative;" :class="val.active ? 'elevation-8' : 'elevation-3'"
       v-for="(val, i) in props.items" :key="i" @mouseenter="val.active = true" @mouseleave="val.active = false"
     >
       <div class="info-header">
@@ -155,9 +155,6 @@ const loginClose = () => {
   height: 144px;
   background-color: #fff;
   border-radius: 12px;
-  &:hover {
-    box-shadow: 0px 3px 5px -1px var(--v-shadow-key-umbra-opacity, rgba(0, 0, 0, 0.2)), 0px 5px 8px 0px var(--v-shadow-key-penumbra-opacity, rgba(0, 0, 0, 0.14)), 0px 1px 14px 0px var(--v-shadow-key-ambient-opacity, rgba(0, 0, 0, 0.12)) !important;
-  }
   .info-header {
     height: 48px;
     background: linear-gradient(90deg,#f5fcfc,#fcfbfa);

+ 16 - 22
src/components/PositionLongStrip/item.vue

@@ -1,10 +1,13 @@
 <!-- 搜索页面的职位详情-长条 -->
 <template>
-  <div class="positionItem" v-for="(item, index) in list" :key="index" @mouseenter="item.active = true" @mouseleave="item.active = false">
+  <div class="positionItem px-3 pt-3" v-for="(item, index) in list" :key="index" @mouseenter="item.active = true" @mouseleave="item.active = false">
     <div class="position-and-company">
       <!-- 职位 -->
       
-      <div class="position" @mouseenter="item.positionActive = true" @mouseleave="item.positionActive = false" @click.stop="handlePosition(item)">
+      <div
+        class="position cursor-pointer rounded-lg" :class="item.positionActive ? 'elevation-5' : ''"
+        @mouseenter="item.positionActive = true" @mouseleave="item.positionActive = false" @click.stop="handlePosition(item)"
+      >
         <div class="d-flex">
           <div v-if="item?.job?.hire" class="mr-3">
             <svg-icon name="pin" size="45"></svg-icon>
@@ -40,7 +43,9 @@
         </div>
       </div>
       <!-- 公司 -->
-      <div class="company" @click.stop="jumpToEnterpriseDetail(item.enterprise.id, true)">
+      <div
+        class="company rounded-lg pa-4 cursor-pointer" :class="item.companyActive ? 'elevation-5' : ''"
+        @click.stop="jumpToEnterpriseDetail(item.enterprise.id, true)" @mouseenter="item.companyActive = true" @mouseleave="item.companyActive = false">
         <div class="float-left">
           <v-img :src="item?.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :alt="item.enterprise.anotherName" :width="40" style="height: 40px;border-radius: 4px;"/>
         </div>
@@ -59,16 +64,16 @@
       </div>
     </div>
     <!-- 底部 -->
-    <div class="footer">
+    <div class="footer mt-1">
       <div class="footer-left">
         <template v-for="(jobTag, jobTagsIndex) in item.job.tagList" :key="jobTagsIndex">
-          <span class="textColor666 mx-1" v-if="jobTagsIndex">|</span>
-          <span class="textColor666 dis">{{ jobTag }}</span>
+          <span class="color-666 mx-1" v-if="jobTagsIndex">|</span>
+          <span class="color-666 dis">{{ jobTag }}</span>
         </template>
       </div>
       <div class="footer-right" v-if="item.contact">
         <v-avatar size="x-small" :image="getUserAvatar(item.contact?.avatar, item.contact?.sex)"></v-avatar>
-        <span class="mx-2 textColor666">
+        <span class="mx-2 color-666">
           {{ item.contact?.name }}
           <span v-if="item?.contact?.postNameCn">|</span>
           {{ item.contact?.postNameCn }}
@@ -137,35 +142,29 @@ const handlePosition = (item) => {
 .company-info {
   float: left;
   margin-left: 16px;
-  // width: 282px;
 }
 .company-info p {
   height: 18px;
-  font-size: 13px;
+  font-size: 14px;
   font-weight: 400;
   color: var(--color-999);
 }
-.textColor666 { color: var(--color-666); }
 .positionItem {
-  // width: 850px;
   width: 100%;
   margin-bottom: 12px;
   border-radius: 12px;
   padding: 0;
   overflow: hidden;
-  cursor: pointer;
   transition: all .2s linear;
   background-color: #fff;
-  &:hover {
-    box-shadow: 0 16px 40px 0 rgba(153, 153, 153, .3);
-  }
+  box-shadow: 0 2px 20px 0 rgba(37, 39, 48, .2);
   .position-and-company {
     display: flex;
-    padding: 16px 20px;
     width: 100%;
     .position {
       width: 500px;
-      padding-right: 30px;
+      padding: 15px 20px 16px 20px;
+      margin-right: 30px;
     }
   }
   .footer {
@@ -189,11 +188,6 @@ const handlePosition = (item) => {
       white-space: nowrap;
     }
   }
-  .ellipsisStyle {
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-  }
 }
 .salary {
   font-size: 16px;

+ 1 - 1
src/components/PreviewImg/previewImage.vue

@@ -19,7 +19,7 @@
         </i>
       </span>
       <!-- ARROW -->
-      <template v-if="!isSingle">
+      <template v-if="urlList.length > 1">
         <span
           class="el-image-viewer__btn el-image-viewer__prev"
           :class="{ 'is-disabled': !infinite && isFirst }"

+ 3 - 2
src/components/headSearch/index.vue

@@ -37,8 +37,9 @@
         @click:clear="handleSearch"
         @keyup.enter="handleSearch"
       ></v-text-field>
-      <!-- <div class="searchBtn" @click="handleSearch">搜索</div> -->
-      <v-btn class="searchBtn" @click="handleSearch">搜索</v-btn>
+      <v-hover v-slot="{ isHovering, props }">
+        <v-btn v-bind="props" v-ripple.center class="searchBtn" @click="handleSearch" :class="isHovering ? 'elevation-10' : 'elevation-5'">搜索</v-btn>
+      </v-hover>
     </div>
   </div>
 </template>

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

@@ -69,7 +69,7 @@ service.interceptors.request.use(
     let isToken = (config.headers || {}).isToken === false
     // token类型
     const tokenIndex = config.tokenIndex ? config.tokenIndex : getIsEnterprise() ? 1 : 2
-    // console.log('令牌类型', tokenIndex === 1 ? '企业:' : '个人:', getToken(tokenIndex))
+    console.log('令牌类型', tokenIndex === 1 ? '企业:' : '个人:', getToken(tokenIndex))
     whiteList.some((v) => {
       if (config.url) {
         config.url.indexOf(v) > -1

+ 1 - 1
src/permission.js

@@ -19,6 +19,7 @@ let isRefresh = true
 //            3.personalCommon: 无需登录也能访问的页面
 // 路由守卫
 router.beforeEach(async (to, from, next) => {
+  localStorage.setItem('routerTest', to.path) // 本地环境保存代码热更新会导致路径缺失问题
   // 获取商城装修模版
   const mallStore = useMallStore()
   const enterpriseStore = useEnterpriseStore()
@@ -26,7 +27,6 @@ router.beforeEach(async (to, from, next) => {
     await mallStore.getMallDiyTemplate()
   }
   
-  localStorage.setItem('routerTest', to.path) // 本地环境保存代码热更新会导致路径缺失问题
   const tokenIndex = getIsEnterprise() ? 1: 2
   start()
   // loadStart()

+ 40 - 2
src/router/modules/common.js

@@ -1,6 +1,6 @@
 // 公共路由(任何身份都可以访问的路由 如:商城)
 
-import headhunting from './components/headhunting'
+import Layout from '@/layout'
 
 const common = [
   {
@@ -102,7 +102,45 @@ const common = [
       hideSide: true
     }
   },
-  ...headhunting
+  {
+    path: '/headhunting',
+    component: Layout,
+    meta: {
+      title: '门墩儿猎寻服务'
+    },
+    children: [
+      {
+        path:'/headhunting',
+        component: () => import('@/views/headhunting/index.vue')
+      }
+    ]
+  },
+  {
+    path: '/headhunting/service',
+    component: Layout,
+    meta: {
+      title: '我们的服务'
+    },
+    children: [
+      {
+        path: '/headhunting/service',
+        component: () => import('@/views/headhunting/service.vue')
+      }
+    ]
+  },
+  {
+    path: '/headhunting/service/details',
+    component: Layout,
+    meta: {
+      title: '门墩儿猎寻服务'
+    },
+    children: [
+      {
+        path: '/headhunting/service/details',
+        component: () => import('@/views/headhunting/details.vue')
+      }
+    ]
+  }
 ]
 
 export default common

+ 0 - 45
src/router/modules/components/headhunting.js

@@ -1,45 +0,0 @@
-import Layout from '@/layout'
-
-const headhunting = [
-  {
-    path: '/headhunting',
-    component: Layout,
-    meta: {
-      title: '门墩儿猎寻服务'
-    },
-    children: [
-      {
-        path:'/headhunting',
-        component: () => import('@/views/headhunting/index.vue')
-      }
-    ]
-  },
-  {
-    path: '/headhunting/service',
-    component: Layout,
-    meta: {
-      title: '我们的服务'
-    },
-    children: [
-      {
-        path: '/headhunting/service',
-        component: () => import('@/views/headhunting/service.vue')
-      }
-    ]
-  },
-  {
-    path: '/headhunting/service/details',
-    component: Layout,
-    meta: {
-      title: '门墩儿猎寻服务'
-    },
-    children: [
-      {
-        path: '/headhunting/service/details',
-        component: () => import('@/views/headhunting/details.vue')
-      }
-    ]
-  }
-]
-
-export default headhunting

+ 4 - 0
src/store/user.js

@@ -173,6 +173,10 @@ export const useUserStore = defineStore('user',
         localStorage.setItem('accountInfo', JSON.stringify(data))
         localStorage.setItem('expiresTime', data.expiresTime)
         updateEventList(false)
+
+        // 企业受邀加入企业,只保存token等操作。
+        if (res?.onlySetToken) return
+        
         await this.updatePasswordCheck() // 检查密码是否需要修改
         await this.getEnterpriseInfo()
         await this.getEnterpriseUserAccountInfo()

+ 1 - 0
src/styles/index.css

@@ -8,6 +8,7 @@
   --v-primary-lighten2: #4DB6AC;
   --v-primary-lighten3: #80CBC4;
   --v-primary-lighten4: #B2DFDB;
+  --v-primary-lighten5: #b2dfdb9c;
   --color-222: #222;
   --color-333: #333;
   --color-666: #666;

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
src/styles/index.min.css


+ 3 - 14
src/styles/recruit/company.css

@@ -37,23 +37,19 @@
   min-width: calc((100% - 24px) / 3);
   max-width: calc((100% - 24px) / 3);
   margin: 0 12px 12px 0;
-  height: 160px;
+  height: 183px;
   border-radius: 12px;
   padding: 0;
   overflow: hidden;
   transition: all .2s linear;
   background-color: #fff;
-  cursor: pointer;
+  box-shadow: 0 2px 20px 0 rgba(37, 39, 48, 0.2);
 }
 
 .sub-li:nth-child(3n) {
   margin-right: 0;
 }
 
-.sub-li:hover {
-  box-shadow: 0 16px 40px 0 rgba(153, 153, 153, 0.3);
-}
-
 .company-info {
   float: left;
   margin-left: 16px;
@@ -62,9 +58,8 @@
 
 .company-info-top {
   display: flex;
-  height: 90px;
+  height: 70px;
   line-height: 90px;
-  padding: 0 20px;
   align-items: center;
   overflow: hidden;
 }
@@ -98,7 +93,6 @@
 .name {
   position: relative;
   line-height: 22px;
-  color: #404040;
   margin-right: 8px;
   overflow: hidden;
   text-overflow: ellipsis;
@@ -115,8 +109,3 @@
   text-align: right;
   flex: 1;
 }
-
-.job-hover:hover {
-  color: var(--v-primary-base);
-  background-color: #f2f4f7;
-}

+ 1 - 1
src/styles/recruit/company.min.css

@@ -1 +1 @@
-.label-title{width:64px;font-weight:500;margin-right:24px;color:var(--color-222)}.label-content{flex:1}.label-color{color:var(--color-222);font-size:14px;margin-right:24px;display:inline-block;cursor:pointer}.label-color:hover{color:var(--v-primary-base)}.actives{color:var(--v-primary-base);font-weight:600}.company-box{display:flex;flex-wrap:wrap}.sub-li{position:relative;width:calc((100% - 24px) / 3);min-width:calc((100% - 24px) / 3);max-width:calc((100% - 24px) / 3);margin:0 12px 12px 0;height:160px;border-radius:12px;padding:0;overflow:hidden;transition:all .2s linear;background-color:#fff;cursor:pointer}.sub-li:nth-child(3n){margin-right:0}.sub-li:hover{box-shadow:0 16px 40px 0 rgba(153,153,153,0.3)}.company-info{float:left;margin-left:16px;width:282px}.company-info-top{display:flex;height:90px;line-height:90px;padding:0 20px;align-items:center;overflow:hidden}.company-info h3{height:22px;font-size:18px;font-weight:700;color:var(--color-333);line-height:22px;margin:0 0 4px 0;padding:0;max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.company-info p{height:18px;font-size:13px;font-weight:400;color:var(--color-999);line-height:18px}.company-info-bottom{height:70px}.name{position:relative;line-height:22px;color:#404040;margin-right:8px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:all linear .2s}.salary{font-size:16px;float:right;color:var(--v-error-base);line-height:22px;max-width:none;text-align:right;flex:1}.job-hover:hover{color:var(--v-primary-base);background-color:#f2f4f7}
+.label-title{width:64px;font-weight:500;margin-right:24px;color:var(--color-222)}.label-content{flex:1}.label-color{color:var(--color-222);font-size:14px;margin-right:24px;display:inline-block;cursor:pointer}.label-color:hover{color:var(--v-primary-base)}.actives{color:var(--v-primary-base);font-weight:600}.company-box{display:flex;flex-wrap:wrap}.sub-li{position:relative;width:calc((100% - 24px) / 3);min-width:calc((100% - 24px) / 3);max-width:calc((100% - 24px) / 3);margin:0 12px 12px 0;height:183px;border-radius:12px;padding:0;overflow:hidden;transition:all .2s linear;background-color:#fff;box-shadow:0 2px 20px 0 rgba(37,39,48,0.2)}.sub-li:nth-child(3n){margin-right:0}.company-info{float:left;margin-left:16px;width:282px}.company-info-top{display:flex;height:70px;line-height:90px;align-items:center;overflow:hidden}.company-info h3{height:22px;font-size:18px;font-weight:700;color:var(--color-333);line-height:22px;margin:0 0 4px 0;padding:0;max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.company-info p{height:18px;font-size:13px;font-weight:400;color:var(--color-999);line-height:18px}.company-info-bottom{height:70px}.name{position:relative;line-height:22px;margin-right:8px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:all linear .2s}.salary{font-size:16px;float:right;color:var(--v-error-base);line-height:22px;max-width:none;text-align:right;flex:1}

+ 3 - 14
src/styles/recruit/company.scss

@@ -36,19 +36,16 @@
   min-width: calc((100% - 24px) / 3);
   max-width: calc((100% - 24px) / 3);
   margin: 0 12px 12px 0;
-  height: 160px;
+  height: 183px;
   border-radius: 12px;
   padding: 0;
   overflow: hidden;
   transition: all .2s linear;
   background-color: #fff;
-  cursor: pointer;
+  box-shadow: 0 2px 20px 0 rgba(37, 39, 48, .2);
   &:nth-child(3n) {
     margin-right: 0;
   }
-  &:hover {
-    box-shadow: 0 16px 40px 0 rgba(153, 153, 153, .3);
-  }
 }
 .company-info {
   float: left;
@@ -57,9 +54,8 @@
 }
 .company-info-top {
   display: flex;
-  height: 90px;
+  height: 70px;
   line-height: 90px;
-  padding: 0 20px;
   align-items: center;
   overflow: hidden;
 }
@@ -84,14 +80,11 @@
   line-height: 18px;
 }
 .company-info-bottom {
-  // width: 100%;
   height: 70px;
-  // margin: 10px 15px;
 }
 .name {
   position: relative;
   line-height: 22px;
-  color: #404040;
   margin-right: 8px;
   overflow: hidden;
   text-overflow: ellipsis;
@@ -106,8 +99,4 @@
   max-width: none;
   text-align: right;
   flex: 1;
-}
-.job-hover:hover {
-  color: var(--v-primary-base);
-  background-color: #f2f4f7;
 }

+ 1 - 1
src/utils/auth.js

@@ -12,7 +12,7 @@ export const isEnterprise = () => {
   const testUsePath = localStorage.getItem('routerTest')
   if (currentRoute?.path === '/' && (testUsePath?.includes(substr) || testUsePath?.includes('/enterpriseVerification'))) bool = true
   // console.log('currentRoute', currentRoute.path)
-  // console.log('isEnterpriseBool', bool, currentRoute.path)
+  console.log('=========================', bool, currentRoute.path, testUsePath)
   return bool
 }
 

+ 5 - 3
src/utils/position.js

@@ -47,7 +47,8 @@ export const dealDictArrayData = (res, list) => {
       const valueKey = data.nameKey ? data.nameKey : 'label'
       const idKey = data.valueKey ? data.valueKey : 'value'
       if (!dictObj[data.value] || !Object.keys(dictObj[data.value]).length) return
-      const result = dictObj[data.value].find(val => val[idKey] === item[e])
+      // 工作经验、学历为null则是不限
+      const result = ['expType', 'eduType'].includes(e) && !item[e] ? { label: `${e === 'expType' ? '工作经验' : '学历' }不限` } : dictObj[data.value].find(val => val[idKey] === item[e])
       if (!result) return
       item[data.label] = result[valueKey] || ''
     })
@@ -63,7 +64,8 @@ export const dealDictObjData = (res, obj) => {
     if (!data) return
     const valueKey = data.nameKey ? data.nameKey : 'label'
     const idKey = data.valueKey ? data.valueKey : 'value'
-    const result = dictObj[data.value]?.find(val => val[idKey] === obj[e])
+    // 工作经验、学历为null则是不限
+    const result = ['expType', 'eduType'].includes(e) && !obj[e] ? { label: `${e === 'expType' ? '工作经验' : '学历' }不限` } : dictObj[data.value]?.find(val => val[idKey] === obj[e])
     if (!result) return
     res[data.label] = result[valueKey]
     res = { ...obj, ...res }
@@ -124,6 +126,6 @@ export const jumpToEnterpriseDetail = async (id, isOpenWindow = false, tabKey =
   if (isOpenWindow) {
     window.open(url)
   } else {
-    router.push({ path: url})
+    router.push({ path: url })
   }
 }

+ 3 - 3
src/views/recruit/enterprise/hirePosition/components/jobRequirements.vue

@@ -260,7 +260,7 @@ const getDictData = async () => {
   items.value.options.forEach(async (e) => {
     if (e.dictTypeName) {
       const { data } = await getDict(e.dictTypeName)
-      e.items = data
+      e.items = ['eduType', 'expType'].indexOf(e.key) !== -1 ? [{ label: '不限', value: -1 }, ...data] : data
     }
   })
 }
@@ -282,7 +282,7 @@ const getQuery = async () => {
     if (e.noParam) return
     if (e.key === 'tagList') {
       obj[e.key] = tag.value.length ? tag.value : []
-    }
+    } else if (['areaId', 'eduType', 'expType'].includes(e.key) && e.value === -1) obj[e.key] = null
     else obj[e.key] = e.value
   })
   if (obj.areaId === -1) obj.areaId = null
@@ -303,7 +303,7 @@ watch(
       if (e.key === 'tagList' && val[e.key] && val[e.key].length) {
         tag.value = val[e.key] && val[e.key].length ? val[e.key] : []
       }
-      if (e.key === 'areaId' && !val[e.key]) e.value = -1
+      if (['areaId', 'eduType', 'expType'].includes(e.key) && !val[e.key]) e.value = -1
       if (!val.payFrom && !val.payTo) {
         salary.value = true
         handleChangeSalary(salary.value)

+ 3 - 4
src/views/recruit/enterprise/positionManagement/components/jobRequirements.vue

@@ -259,7 +259,7 @@ const getDictData = async () => {
   items.value.options.forEach(async (e) => {
     if (e.dictTypeName) {
       const { data } = await getDict(e.dictTypeName)
-      e.items = data
+      e.items = ['eduType', 'expType'].indexOf(e.key) !== -1 ? [{ label: '不限', value: -1 }, ...data] : data
     }
   })
 }
@@ -281,10 +281,9 @@ const getQuery = async () => {
     if (e.noParam) return
     if (e.key === 'tagList') {
       obj[e.key] = tag.value.length ? tag.value : []
-    }
+    } else if (['areaId', 'eduType', 'expType'].includes(e.key) && e.value === -1) obj[e.key] = null
     else obj[e.key] = e.value
   })
-  if (obj.areaId === -1) obj.areaId = null
   query = Object.assign(query, obj)
   return query
 }
@@ -302,7 +301,7 @@ watch(
       if (e.key === 'tagList' && val[e.key] && val[e.key].length) {
         tag.value = val[e.key] && val[e.key].length ? val[e.key] : []
       }
-      if (e.key === 'areaId' && !val[e.key]) e.value = -1
+      if (['areaId', 'eduType', 'expType'].includes(e.key) && !val[e.key]) e.value = -1
       if (!val.payFrom && !val.payTo) {
         salary.value = true
         handleChangeSalary(salary.value)

+ 212 - 0
src/views/recruit/enterprise/systemManagement/groupAccount/inviteConfirmEnt-old.vue

@@ -0,0 +1,212 @@
+<!-- 备份-已废弃 -->
+<template>
+  <div style="background-color: #f0f0f0;">
+    <div class="inviteView text-center" :style="{'width': isMobile ? '100%' : '750px'}">
+      <div class="invite-title">
+        <div style="color: var(--v-primary-base);" class="mb-3">【门墩儿招聘集团邀请】</div>
+        <div style="font-size: 24px">
+          <div>
+            <span class="color-333">{{ enterpriseInfo.name }}</span>
+            <span class="color-333"> 邀请您加入 </span>
+          </div>
+          <div class="color-333">{{ enterpriseInfo.enterpriseName }}</div>
+          <div>集团</div>
+        </div>
+      </div>
+      <div class="mt-10 d-flex flex-column align-center">
+        <template v-if="joinSuccess">
+          <div>加入成功!</div>
+          <v-btn class="mt-10" color="warning" to="/recruitHome">{{ $t('common.toHome') }}</v-btn>
+        </template>
+        <template v-else>
+          <div v-if="showCompanySelect">
+            <companySelect ref="companySelectRef" :list="enterpriseList" v-model="enterpriseId"></companySelect>
+            <v-btn :loading="loginLoading" color="warning" class="white--text mt-3" min-width="350" @click="join(enterpriseId)" :style="{'width': isMobile ? '100%' : '350px' }">
+              {{ $t('common.confirm') }}
+            </v-btn>
+          </div>
+          <div v-show="!showCompanySelect">
+            <phoneFrom ref="phoneRef" openVerify @verifySuccess="handleConfirmJoin" :style="{'width': isMobile ? '100%' : '350px' }"></phoneFrom>
+            <v-btn :loading="loginLoading" color="warning" class="white--text mt-3" min-width="350" @click="handleConfirmJoin" :style="{'width': isMobile ? '100%' : '350px' }">
+              {{ quickRegister ? $t('login.register') : $t('common.confirmJoin') }}
+            </v-btn>
+            <div class="mt-3" style="font-size: 13px;text-align: center;">
+              <div>注:请使用已经申请好的企业账户加入集团</div>
+              <div style="color: red; cursor: pointer;">
+                <span
+                  v-if="quickRegister"
+                  @click="quickRegister = false"
+                >
+                  已有企业账号,去登录
+                </span>
+                <span
+                  v-else
+                  @click="handleRegister"
+                >
+                  没有企业账号?去注册
+                </span>
+              </div>
+            </div>
+          </div>
+        </template>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'inviteConfirmEnt'})
+import { useRouter } from 'vue-router'; const router = useRouter()
+import { ref, onMounted, reactive } from 'vue'
+import { useRoute } from 'vue-router'; const route = useRoute()
+import { enterpriseInviteRecordConsent, getEnterpriseInfoByCode } from '@/api/recruit/enterprise/enterpriseInvite.js'
+import Snackbar from '@/plugins/snackbar'
+import { getUserBindEnterpriseList, getUserRegisterEnterpriseApply } from '@/api/personal/user'
+import companySelect from '@/views/login/components/companySelect.vue'
+import phoneFrom from '@/components/VerificationCode'
+import { getToken, setToken, setRefreshToken } from '@/utils/auth'
+import { smsLogin, logout, switchLoginOfEnterprise, logoutToken } from '@/api/common'
+
+const joinSuccess = ref(false)
+const code = route.query?.code || ''
+
+// 组件挂载后添加事件监听器  
+const isMobile = ref(false)
+onMounted(() => {
+  const userAgent = navigator.userAgent
+  isMobile.value = /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i.test(userAgent)
+})
+
+const phoneRef = ref()
+const loginLoading = ref(false)
+const showCompanySelect = ref(false)
+let phoneParams = reactive({})
+const enterpriseId = ref('')
+
+const handleConfirmJoin = async () => {
+  phoneParams = { ...phoneRef.value.loginData }
+  const { valid } = await phoneRef.value.phoneForm.validate()
+  if (!valid) return
+  if (!phoneParams.captchaVerification) {
+    phoneRef.value.getCode(true)
+    return
+  }
+  loginLoading.value = true
+  try {
+    const res = await smsLogin(phoneParams)
+    setToken(res.accessToken)
+    setRefreshToken(res.refreshToken)
+    quickRegister.value ? getApplyInfo() : getEnterpriseList()
+  } catch (error) {
+    phoneRef.value && phoneRef.value.clearCaptcha()
+    Snackbar.error(error?.msg || error)
+  } finally {
+    loginLoading.value = false
+  }
+}
+
+const companySelectRef = ref()
+const enterpriseList = ref([])
+const getEnterpriseList = async() => {
+  try {
+    loginLoading.value = true
+    enterpriseList.value = []
+    const data = await getUserBindEnterpriseList() // 申请通过才有数据,否则空数组
+    if (!data?.length) {
+      // Snackbar.warning('未查询到该用户下存在企业')
+      getApplyInfo()
+      return
+    }
+    if (data.length > 1) {
+      showCompanySelect.value = true
+      enterpriseList.value = data
+    } else {
+      join(data[0].enterpriseId)
+    }
+  } catch (error) {
+    Snackbar.error(error?.msg || error)
+    logoutFun()
+  } finally {
+    loginLoading.value = false
+  }
+}
+
+// 查看用户是否有在申请中的数据
+const getApplyInfo = async () => {
+  const data = await getUserRegisterEnterpriseApply()
+  const bool = data && Object.keys(data).length // 已经有数据说明已经申请过了
+  const path = bool ? '/recruit/entRegister/inReview' : '/recruit/entRegister'
+  router.push({ path })
+  if (!bool) Snackbar.warning('未查询到该用户下存在企业,请优先提交企业申请')
+}
+
+// 执行加入操作
+const join = async (enterpriseId) => {
+  if (!enterpriseId) return Snackbar.warning('请选择要加入的企业')
+  if (enterpriseInfo.value?.id === enterpriseId) return Snackbar.error('您在尝试加入自己的企业邀请,此操作不被允许!')
+  try {
+    loginLoading.value = true
+    await logout() // changeRole // 先退出个人登录
+    const data = await switchLoginOfEnterprise({ enterpriseId })
+    setToken(data.accessToken)
+    await enterpriseInviteRecordConsent(code)
+    Snackbar.success('加入成功')
+    joinSuccess.value = true
+  } catch (error) {
+    phoneRef.value && phoneRef.value.clearCaptcha()
+    Snackbar.error(error?.msg || error)
+  } finally {
+    loginLoading.value = false
+    logoutFun()
+  }
+}
+
+const quickRegister = ref(false)
+const handleRegister = () => {
+  quickRegister.value = true
+}
+
+const logoutFun = async () => {
+  if (!getToken(1)) {
+    localStorage.clear()
+    return
+  }
+  try {
+    await await logoutToken(getToken(1))
+    localStorage.clear()
+  } catch (error) {
+    console.log('登出失败!', error)
+  }
+}
+logoutFun() // 清除之前的token
+
+const enterpriseInfo = ref({})
+// 根据邀请码获取企业信息
+const getEnterpriseInfo = async () => {
+  try {
+    const data = await getEnterpriseInfoByCode({ code })
+    enterpriseInfo.value = data
+  } catch (error) {
+    console.error('error', error)
+    Snackbar.error('链接失效')
+  }
+}
+getEnterpriseInfo()
+
+</script>
+
+<style scoped lang="scss">
+.inviteView {
+  min-height: 100vh;
+  display: block;
+  overflow: hidden;
+  margin: 0 auto;
+  padding: 45px;
+  background-color: #fff;
+}
+.invite-title {
+  font-size: 20px;
+  font-weight: 700;
+  color: var(--color-333);
+}
+</style>

+ 75 - 74
src/views/recruit/enterprise/systemManagement/groupAccount/inviteConfirmEnt.vue

@@ -18,16 +18,18 @@
           <v-btn class="mt-10" color="warning" to="/recruitHome">{{ $t('common.toHome') }}</v-btn>
         </template>
         <template v-else>
-          <div v-if="showCompanySelect">
-            <companySelect ref="companySelectRef" :list="enterpriseList" v-model="enterpriseId"></companySelect>
-            <v-btn :loading="loginLoading" color="warning" class="white--text mt-3" min-width="350" @click="join(enterpriseId)" :style="{'width': isMobile ? '100%' : '350px' }">
-              {{ $t('common.confirm') }}
-            </v-btn>
-          </div>
-          <div v-show="!showCompanySelect">
-            <phoneFrom ref="phoneRef" openVerify @verifySuccess="handleConfirmJoin" :style="{'width': isMobile ? '100%' : '350px' }"></phoneFrom>
-            <v-btn :loading="loginLoading" color="warning" class="white--text mt-3" min-width="350" @click="handleConfirmJoin" :style="{'width': isMobile ? '100%' : '350px' }">
-              {{ quickRegister ? $t('login.register') : $t('common.confirmJoin') }}
+          <div>
+            <div style="font-size: 13px;text-align: left;" class="mb-2"><span style="color: red;">*</span> 请输入要加入的企业邮箱及密码</div>
+            <passwordFrom
+              ref="entPassRef"
+              placeholder="请输入企业邮箱"
+              :isCounter="true"
+              :validEmail="true"
+              style="text-align: left;"
+              @handleEnter="handleLogin"
+            ></passwordFrom>
+            <v-btn :loading="loginLoading" color="warning" class="white--text mt-3" min-width="350" @click="handleLogin" :style="{'width': isMobile ? '100%' : '350px' }">
+              {{ $t('common.confirmJoin') }}
             </v-btn>
             <div class="mt-3" style="font-size: 13px;text-align: center;">
               <div>注:请使用已经申请好的企业账户加入集团</div>
@@ -51,20 +53,30 @@
       </div>
     </div>
   </div>
+  <Verify
+    ref="verify"
+    captchaType="blockPuzzle"
+    :imgSize="{ width: '400px', height: '200px' }"
+    mode="pop"
+    @success="verifySuccess"
+  />
 </template>
 
 <script setup>
 defineOptions({ name: 'inviteConfirmEnt'})
 import { useRouter } from 'vue-router'; const router = useRouter()
-import { ref, onMounted, reactive } from 'vue'
+import { ref, onMounted } from 'vue'
 import { useRoute } from 'vue-router'; const route = useRoute()
 import { enterpriseInviteRecordConsent, getEnterpriseInfoByCode } from '@/api/recruit/enterprise/enterpriseInvite.js'
 import Snackbar from '@/plugins/snackbar'
-import { getUserBindEnterpriseList, getUserRegisterEnterpriseApply } from '@/api/personal/user'
-import companySelect from '@/views/login/components/companySelect.vue'
-import phoneFrom from '@/components/VerificationCode'
-import { getToken, setToken, setRefreshToken } from '@/utils/auth'
-import { smsLogin, logout, switchLoginOfEnterprise, logoutToken } from '@/api/common'
+import passwordFrom from '@/views/login/components/passwordPage.vue'
+import Verify from '@/components/Verifition'
+import { getToken } from '@/utils/auth'
+import { useUserStore } from '@/store/user'
+import {
+  logoutToken,
+  passwordLogin
+} from '@/api/common'
 
 const joinSuccess = ref(false)
 const code = route.query?.code || ''
@@ -76,85 +88,74 @@ onMounted(() => {
   isMobile.value = /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i.test(userAgent)
 })
 
-const phoneRef = ref()
-const loginLoading = ref(false)
-const showCompanySelect = ref(false)
-let phoneParams = reactive({})
-const enterpriseId = ref('')
+const entPassRef = ref()
+// 获取验证码
+const verify = ref()
+const getCode = async () => {
+  // 弹出验证码 // 已开启:则展示验证码;只有完成验证码的情况,才进行登录
+  verify.value.show()
+}
+const captchaStr = ref('')
+const verifySuccess = (params) => {
+  captchaStr.value = params.captchaVerification
+  handleLogin()
+}
 
-const handleConfirmJoin = async () => {
-  phoneParams = { ...phoneRef.value.loginData }
-  const { valid } = await phoneRef.value.phoneForm.validate()
+// const entBaseInfo = ref()
+// // 获取当前登录的企业用户信息
+// const getLoginEnterpriseInfo = async () => {
+//   try {
+//     const result = await getEnterprisingUserInfo()
+//     entBaseInfo.value = result
+    
+//     // 是否为企业账号管理员
+//     // const isAdmin = result.userType === '1'
+//   } catch (error) {
+//     console.error('error', error)
+//   }
+// }
+
+const loginLoading = ref(false)
+const handleLogin = async () => {
+  const { valid } = await entPassRef.value.passwordForm.validate()
   if (!valid) return
-  if (!phoneParams.captchaVerification) {
-    phoneRef.value.getCode(true)
+  const params = {
+    ...entPassRef.value.loginData,
+    captchaVerification: captchaStr.value,
+    isEnterprise: true,
+  }
+  params.account = params.phone
+
+  if (!params.captchaVerification) {
+    getCode() // 验证码组件
     return
   }
   loginLoading.value = true
   try {
-    const res = await smsLogin(phoneParams)
-    setToken(res.accessToken)
-    setRefreshToken(res.refreshToken)
-    quickRegister.value ? getApplyInfo() : getEnterpriseList()
-  } catch (error) {
-    phoneRef.value && phoneRef.value.clearCaptcha()
-    Snackbar.error(error?.msg || error)
-  } finally {
-    loginLoading.value = false
-  }
-}
+    const res = await passwordLogin(params)
+    const obj = res ? { ...res, type: 'emailLogin', onlySetToken: true } : {}
+    await useUserStore().changeRole(obj)
+    await join()
 
-const companySelectRef = ref()
-const enterpriseList = ref([])
-const getEnterpriseList = async() => {
-  try {
-    loginLoading.value = true
-    enterpriseList.value = []
-    const data = await getUserBindEnterpriseList() // 申请通过才有数据,否则空数组
-    if (!data?.length) {
-      // Snackbar.warning('未查询到该用户下存在企业')
-      getApplyInfo()
-      return
-    }
-    if (data.length > 1) {
-      showCompanySelect.value = true
-      enterpriseList.value = data
-    } else {
-      join(data[0].enterpriseId)
-    }
   } catch (error) {
     Snackbar.error(error?.msg || error)
-    logoutFun()
   } finally {
+    captchaStr.value = ''
     loginLoading.value = false
   }
 }
 
-// 查看用户是否有在申请中的数据
-const getApplyInfo = async () => {
-  const data = await getUserRegisterEnterpriseApply()
-  const bool = data && Object.keys(data).length // 已经有数据说明已经申请过了
-  const path = bool ? '/recruit/entRegister/inReview' : '/recruit/entRegister'
-  router.push({ path })
-  if (!bool) Snackbar.warning('未查询到该用户下存在企业,请优先提交企业申请')
-}
-
 // 执行加入操作
-const join = async (enterpriseId) => {
-  if (!enterpriseId) return Snackbar.warning('请选择要加入的企业')
-  if (enterpriseInfo.value?.id === enterpriseId) return Snackbar.error('您在尝试加入自己的企业邀请,此操作不被允许!')
+const join = async () => {
   try {
     loginLoading.value = true
-    await logout() // changeRole // 先退出个人登录
-    const data = await switchLoginOfEnterprise({ enterpriseId })
-    setToken(data.accessToken)
-    await enterpriseInviteRecordConsent(code)
+    await enterpriseInviteRecordConsent({ code })
     Snackbar.success('加入成功')
     joinSuccess.value = true
   } catch (error) {
-    phoneRef.value && phoneRef.value.clearCaptcha()
     Snackbar.error(error?.msg || error)
   } finally {
+    captchaStr.value = ''
     loginLoading.value = false
     logoutFun()
   }
@@ -162,7 +163,7 @@ const join = async (enterpriseId) => {
 
 const quickRegister = ref(false)
 const handleRegister = () => {
-  quickRegister.value = true
+  router.push('/register/selected')
 }
 
 const logoutFun = async () => {

+ 1 - 4
src/views/recruit/personal/PersonalCenter/jobFeedback/components/interview/item.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="position-item mb-3 job-closed elevation-2"
+  <div class="position-item mb-3 job-closed" :class="val.active ? 'elevation-8' : 'elevation-3'"
     style="position: relative;"
     v-for="(val, i) in props.items" :key="i" @mouseenter="val.active = true" @mouseleave="val.active = false"
   >
@@ -119,9 +119,6 @@ const handleRefuse = (val) => {
   height: 160px;
   background-color: #fff;
   border-radius: 12px;
-  &:hover {
-    box-shadow: 0px 3px 5px -1px var(--v-shadow-key-umbra-opacity, rgba(0, 0, 0, 0.2)), 0px 5px 8px 0px var(--v-shadow-key-penumbra-opacity, rgba(0, 0, 0, 0.14)), 0px 1px 14px 0px var(--v-shadow-key-ambient-opacity, rgba(0, 0, 0, 0.12)) !important;
-  }
   .info-header {
     height: 48px;
     background: linear-gradient(90deg,#f5fcfc,#fcfbfa);

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

@@ -2,7 +2,7 @@
   <div style="position: relative; height: 60vh;">
     <div v-if="userInfo?.entitlement?.viewersList && userInfo?.vipFlag && userInfo?.vipExpireDate && userInfo?.vipExpireDate > Date.now()">
       <div v-if="items.length" class="mt-3">
-        <div class="positionItem elevation-2" v-for="(item, index) in items" :key="index" @mouseenter="item.active = true" @mouseleave="item.active = false">
+        <div class="positionItem" :class="item.active ? 'elevation-8' : 'elevation-3'" v-for="(item, index) in items" :key="index" @mouseenter="item.active = true" @mouseleave="item.active = false">
           <div class="position-and-company">
             <div class="position">
               <div class="float-left">
@@ -129,9 +129,6 @@ const goBuy = () => {
   overflow: hidden;
   transition: all .2s linear;
   background-color: #fff;
-  &:hover {
-    box-shadow: 0px 3px 5px -1px var(--v-shadow-key-umbra-opacity, rgba(0, 0, 0, 0.2)), 0px 5px 8px 0px var(--v-shadow-key-penumbra-opacity, rgba(0, 0, 0, 0.14)), 0px 1px 14px 0px var(--v-shadow-key-ambient-opacity, rgba(0, 0, 0, 0.12)) !important;
-  }
   .position-and-company {
     display: flex;
     padding: 16px 20px;

+ 23 - 16
src/views/recruit/personal/company/components/companyItem.vue

@@ -1,9 +1,11 @@
 <template>
   <div class="company-box">
     <div class="sub-li" v-for="item in list" :key="item.enterprise.id">
-      <div class="company-info-top" @click.stop="jumpToEnterpriseDetail(item.enterprise.id, true)" @mouseenter="item.active = true" @mouseleave="item.active = false">
+      <div
+        class="company-info-top ma-3 rounded-lg px-3 cursor-pointer" :class="item.active ? 'elevation-5' : ''"
+        @click.stop="jumpToEnterpriseDetail(item.enterprise.id, true)" @mouseenter="item.active = true" @mouseleave="item.active = false">
         <div class="float-left">
-          <v-img :src="item.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :alt="item.enterprise.anotherName" :width="40" style="height: 40px;border-radius: 4px;"/>
+          <v-img :src="item.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :alt="item.enterprise.anotherName" :width="50" :height="50" />
         </div>
         <div class="company-info">
           <h3 :class="{'default-active': item.active }">{{ formatName(item.enterprise.anotherName || item.enterprise.name) }}</h3>
@@ -11,23 +13,28 @@
         </div>
       </div>
       <v-divider class="mx-4"></v-divider>
-      <div class="company-info-bottom mx-4 mt-2">
-        <div v-if="item?.job && Object.keys(item.job).length" class="job-hover" @click.stop="handleClickPosition(item.job)">
-          <div class="mb-1 d-flex">
-            <p :class="['mr-3', 'cursor-pointer', 'name']" :style="{'max-width': !item.job.payFrom && !item.job.payTo ? '270px' : '220px'}">{{ formatName(item.job.name) }}</p>
-            <span v-if="!item.job.payFrom && !item.job.payTo" class="salary">面议</span>
-            <span v-else class="salary">{{ item.job.payFrom ? item.job.payFrom + '-' : '' }}{{ item.job.payTo }}{{ item.job.payName ? '/' + item.job.payName : '' }}</span>
-          </div>
-          <div style="height: 24px; overflow: hidden; color: #808080;">
-            <span v-for="(j, index) in desc" :key="index">
-              <span v-if="item.job[j] || j === 'areaName'" class="mr-1 font-size-13">
-                {{ j === 'areaName' ? !item.job.areaId ? '全国' : item.job.area.str : item.job[j] }}
+      <v-hover v-slot="{ isHovering, props }">
+        <div class="company-info-bottom mx-3 mt-2 rounded-lg pa-3 cursor-pointer" v-bind="props" :class="isHovering && (item?.job && Object.keys(item.job).length) ? 'elevation-5' : ''">
+          <div v-if="item?.job && Object.keys(item.job).length" @click.stop="handleClickPosition(item.job)">
+            <div class="mb-1 d-flex">
+              <p
+                class="mr-3 cursor-pointer name"
+                :style="{'max-width': !item.job.payFrom && !item.job.payTo ? '270px' : '200px', 'color': isHovering ? 'var(--v-primary-base)' : '#404040'}"
+              >{{ formatName(item.job.name) }}</p>
+              <span v-if="!item.job.payFrom && !item.job.payTo" class="salary">面议</span>
+              <span v-else class="salary">{{ item.job.payFrom ? item.job.payFrom + '-' : '' }}{{ item.job.payTo }}{{ item.job.payName ? '/' + item.job.payName : '' }}</span>
+            </div>
+            <div style="height: 24px; overflow: hidden; color: #808080;">
+              <span v-for="(j, index) in desc" :key="index">
+                <span v-if="item.job[j] || j === 'areaName'" class="mr-1 font-size-13">
+                  {{ j === 'areaName' ? !item.job.areaId ? '全国' : item.job.area.str : item.job[j] }}
+                </span>
+                <span v-if="index !== desc.length - 1 && (item.job[desc[index + 1]] || j === 'areaName')" class="septal-line ml-1"></span>
               </span>
-              <span v-if="index !== desc.length - 1 && (item.job[desc[index + 1]] || j === 'areaName')" class="septal-line ml-1"></span>
-            </span>
+            </div>
           </div>
         </div>
-      </div>
+      </v-hover>
     </div>
   </div>
 </template>

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

@@ -1,8 +1,8 @@
 <template>
   <div class="default-width">
-    <buttons :current="2" style="position: sticky;"></buttons>
+    <buttons :current="2" style="position: sticky;" class="mx-4"></buttons>
     <div class="company-content">
-      <div class="white-bgc pb-3 pt-5">
+      <div class="white-bgc pb-3 pt-5 mx-4">
         <headSearch v-model="content" placeholder="搜索公司关键字" @handleSearch="val => handleSearch(val, 'name')"></headSearch>
         <div class="px-5 mt-3 clear-parent">
           <!-- <areaType :isClear="clear" @handleClick="handleSearch"></areaType> -->
@@ -11,7 +11,7 @@
           <div class="clear" @click="handleClear">清空筛选条件</div>
         </div>
       </div>
-      <div v-if="items.length">
+      <div v-if="items.length" class="mx-4">
         <companyItem class="mt-3" :list="items"></companyItem>
         <MPagination
           :total="total"

+ 8 - 4
src/views/recruit/personal/companyDetail/components/positions.vue

@@ -13,7 +13,7 @@
     </div>
     <div class="d-flex mt-1 justify-space-between">
       <conditionFilter v-if="show" ref="conditionFilterRef" :showFilterList="showFilterList" @reset="handleReset" @change="handleQueryChange"></conditionFilter>
-      <div style="width: 200px;" class="mt-2">
+      <div style="width: 220px;" class="mt-2">
         <v-text-field
           v-model="query.content"
           variant="outlined" 
@@ -206,14 +206,18 @@ const handleSearch = (key, { values = [] }) => {
 const list = ref([])
 // 职位列表
 const getPositionList = async (isSearch) => {
-  query = {
+  const queryParams = {
     ...query,
     ...pageInfo.value,
     enterpriseId: props.info.enterprise.id
   }
-  delete query.key
+  delete queryParams.key
+  for (const key in queryParams) {
+    if (['expType', 'eduType'].includes(key) && queryParams[key] === '9999') delete queryParams[key]
+  }
+
   if (isSearch) query.pageNo = 1
-  const { list: arr, total: number } = await getJobAdvertisedSearch(query)
+  const { list: arr, total: number } = await getJobAdvertisedSearch(queryParams)
   total.value = number
   list.value = arr.map(e => {
     e.active = false

+ 1 - 1
src/views/recruit/personal/home/components/advertisement/preferred.vue

@@ -88,7 +88,7 @@
           </div>
           <div class="job-class">
             <div class="job-title">工作经验</div>
-            <p class="font-size-14">{{ k.job.expName || '不限' }}</p>
+            <p class="font-size-14">{{ k.job.expName ? (k.job.expName.indexOf('不限') !== -1 ? '不限' : k.job.expName) : '不限' }}</p>
           </div>
           <div class="job-class">
             <div class="job-title">职位更新</div>

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

@@ -1,15 +1,15 @@
 <template>
   <div class="default-width mb-6 d-flex align-center justify-center">
-    <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 class="color-primary font-weight-bold" style="width: 80px; min-width: 80px;">{{ $t('position.popularPosition') }}:</span>
+    <div class="overflow-hidden py-3 pl-2" style="height: 60px; ">
       <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' : ''"
+          class="mr-2 mb-4 tag" 
+          :class="isHovering ? 'elevation-5' : 'elevation-1'"
           @click.stop="handleClick(item)"
         >
           {{ item.nameCn }}

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

@@ -11,14 +11,16 @@
       <v-tab :value="2">{{ $t('position.latest') }}</v-tab>
       <v-tab :value="3">{{ $t('position.hire') }}</v-tab>
     </v-tabs>
-    <v-window v-model="tab" class="mt-5">
+    <v-window v-model="tab" class="pt-5 px-2 pb-2">
       <v-window-item v-for="v in 3" :value="v" :key="v">
         <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" elevation="5" @click.stop="handleToMore">{{ $t('position.moreBtn') }}</v-btn>
+    <div class="text-center mt-4" style="border-top: 1px solid #ccc; padding-top: 30px;">
+      <v-hover v-slot="{ isHovering, props }">
+        <v-btn v-bind="props" v-ripple.center class="buttons btnColor" :class="isHovering ? 'elevation-10' : 'elevation-5'" @click.stop="handleToMore">{{ $t('position.moreBtn') }}</v-btn>
+      </v-hover>
     </div>
   </div>
 </template>

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

@@ -9,7 +9,9 @@
     <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" elevation="5" color="primary" @click.stop="handleToMore">{{ $t('enterprise.moreBtn') }}</v-btn>
+      <v-hover v-slot="{ isHovering, props }">
+        <v-btn v-bind="props" v-ripple.center class="buttons btnColor" :class="isHovering ? 'elevation-10' : 'elevation-5'" @click.stop="handleToMore">{{ $t('enterprise.moreBtn') }}</v-btn>
+      </v-hover>
     </div>
   </div>
 </template>

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

@@ -11,6 +11,7 @@
           :isSlot="item.isSlot"
           :itemKey="item.itemKey"
           :itemText="item.itemText"
+          :dictData="item.toFilterDictDataList"
           :displayDictName="item.displayDictName"
           :displayApiType="item.displayApiType"
           :provideData="item.provideData || []"
@@ -95,8 +96,6 @@ const assembleList = async ({ key, idsStr }) => {
 watch(
   () => route.query, 
   (newVal = {}, oldVal = {}) => {
-    // console.log('1oldVal', oldVal)
-    // console.log('2newVal', newVal)
     const newKeyList = Object.keys(newVal).length ? [...Object.keys(newVal)] : null
 
     // 回显已选筛选-标签

+ 20 - 11
src/views/recruit/personal/position/components/conditionFilter/commonPath.vue

@@ -9,12 +9,12 @@
     <v-list v-else>
       <v-list-item
         v-for="item in items" :key="item.id" :value="item[itemKey]"
-        :active="selectIdArr.includes(item[itemKey])"
+        :active="selectIdArr.includes(Number(item[itemKey]))"
         color="primary"
         @click="handle(item, item[itemKey])"
       >
-        <template v-if="selectIdArr.includes(item[itemKey])" v-slot:append>
-          <v-icon icon="mdi-check"></v-icon>
+        <template v-if="selectIdArr.includes(Number(item[itemKey]))" v-slot:append>
+          <v-icon color="primary" icon="mdi-check"></v-icon>
         </template>
         <v-list-item-title>{{ item[itemText] }}</v-list-item-title>
       </v-list-item>
@@ -72,6 +72,10 @@ const props = defineProps({
     type: String,
     default: '类型名称'
   },
+  dictData: {
+    type: Array,
+    default: () => []
+  }
 })
 
 const btnTitle = computed(() => {
@@ -83,17 +87,19 @@ let items = ref()
 const selectIdArr = ref([])
 
 const handle = (item, value) => {
-  // const value = item[props.itemKey]
   if (props.isSlot) {
     selectIdArr.value = value
   } else {
     if (selectIdArr.value.includes(value)) {
       selectIdArr.value = selectIdArr.value.filter(i => i !== value)
     } else {
-      // if (props.idName === 'expType') selectIdArr.value = value === '2' ? [value] : (selectIdArr.value.push(value) && selectIdArr.value.filter(i => i !== '2')) // 选中经验不限时清空其它选中项,选中其他的咬去掉经验不限
-      //
-      if (props.isSingle) selectIdArr.value = [value] // 单选
-      else selectIdArr.value.push(value)
+      // 选中经验不限时清空其它选中项,选中其他的咬去掉经验不限
+      if (!props.isSingle && ['expType', 'eduType'].includes(props.idName)) {
+        selectIdArr.value = value === '9999' ? [value] : (selectIdArr.value.push(value) && selectIdArr.value.filter(i => i.toString() !== '9999'))
+      } else {
+        if (props.isSingle) selectIdArr.value = [value] // 单选
+        else selectIdArr.value.push(value)
+      }
     }
   }
   emits('inputChange', {
@@ -112,13 +118,16 @@ else if (props.provideData?.length) { // 自定义下拉数据
   items.value = props.provideData
   show.value = true
 }
-else if (props.displayDictName) {
+else if ((!props.dictData || !props.dictData.length) && props.displayDictName) {
   getDict(props.displayDictName, props.displayParams, props.displayApiType || 'dict').then(({ data }) => {
     data = data?.length && data || []
-    items.value = data
+    items.value = ['expType', 'eduType'].includes(props.idName) ? [{ label: '不限', value: '9999', id: 9999 }, ...data] : data
     show.value = true
   })
-} else console.log('error->字典参数未传递!!')
+} else {
+  items.value = props.dictData || []
+  show.value = true
+}
 
 
 watch(

+ 17 - 7
src/views/recruit/personal/position/components/details.vue

@@ -174,6 +174,7 @@ import { checkPersonBaseInfo } from '@/utils/check'
 import dialogExtend from '@/plugins/dialogExtend'
 import { formatName } from '@/utils/getText'
 
+const emit = defineEmits(['preview'])
 const props = defineProps({
   defaultWidth: {
     type: Boolean,
@@ -191,6 +192,11 @@ const props = defineProps({
     type: [String, Number],
     default: ''
   },
+  // 是否为推荐职位引用
+  isRecommend: {
+    type: Boolean,
+    default: false
+  }
 })
 
 const { t } = useI18n()
@@ -259,6 +265,13 @@ const cleanedHtml = (text) => {
   return cleaned
 }
 
+// 职位详情分享图片下载文件名
+const fileName = computed(() => {
+  const { name, areaName, payFrom, payTo } = info.value
+  const salary = payFrom && payTo ? `${payFrom ? '_' + payFrom + '-' : ''}${payTo}` : '-面议'
+  return `${name}${areaName ? '_' + areaName : ''}${salary}${positionInfo.value.payName ? '-' + positionInfo.value.payName : ''}`
+})
+
 const share = ref()
 // 生成图片
 const generateAndDownloadImage = async () => {
@@ -269,6 +282,10 @@ const generateAndDownloadImage = async () => {
     const image = canvas.toDataURL().replace(/^data:image\/(png|jpg);base64,/, '')
     previewSrc.value = `data:image/png;base64,${image}`
     loading.value = false
+    if (props.isRecommend) {
+      emit('preview', previewSrc.value, fileName.value)
+      return
+    }
     showPreview.value = true
   } catch (error) {
     console.error('Error generating image:', error)
@@ -276,13 +293,6 @@ const generateAndDownloadImage = async () => {
   }
 }
 
-// 职位详情分享图片下载文件名
-const fileName = computed(() => {
-  const { name, areaName, payFrom, payTo } = info.value
-  const salary = payFrom && payTo ? `${payFrom ? '_' + payFrom + '-' : ''}${payTo}` : '-面议'
-  return `${name}${areaName ? '_' + areaName : ''}${salary}${positionInfo.value.payName ? '-' + positionInfo.value.payName : ''}`
-})
-
 // 相似职位
 const similarList = ref([])
 const getSimilarPositionList = async () => {

+ 2 - 11
src/views/recruit/personal/position/components/dict.js

@@ -82,16 +82,7 @@ const dictList = [
     title: '公司规模',
     path: commonPath,
     toFilterDictDataList: []
-  },
-  // {
-  //   displayDictName: 'menduner_financing_status',
-  //   key: 'financingStatus',
-  //   itemKey: 'value',
-  //   itemText: 'label',
-  //   title: '融资情况',
-  //   path: commonPath,
-  //   toFilterDictDataList: []
-  // }
+  }
 ]
 export const filterList = dictList
 
@@ -101,7 +92,7 @@ const getDictList = async () => {
     const toFilterDictName = dictListItem.toFilterDictName || dictListItem.displayDictName
     if (!toFilterDictName) return
     const { data } = await getDict(toFilterDictName, dictListItem.params, dictListItem.toFilterApiType)
-    dictListItem.toFilterDictDataList = data
+    dictListItem.toFilterDictDataList = ['eduType', 'expType'].includes(dictListItem.key) ? [{ label: '不限', value: '9999', id: 9999 }, ...data] : data
   })
 }
 

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

@@ -29,7 +29,7 @@
           <v-avatar style="border: 2px solid #fff;" size="68">
             <img crossOrigin="anonymous" :src="info.enterprise.logoUrl" alt="" style="width: 68px; height: 68px;">
           </v-avatar>
-          <div class="enterprise-name ml-5 ellipsis" style="width: 65%;">{{ formatName(info.enterprise?.anotherName || info.enterprise?.name) }}</div>
+          <div class="enterprise-name ml-5" style="width: 65%;">{{ formatName(info.enterprise?.anotherName || info.enterprise?.name) }}</div>
           <div style="flex: 1;" class="text-right enterprise-name">{{ info.areaName }}</div>
         </div>
         <div class="mx-5 mt-3">

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

@@ -1,21 +1,21 @@
 <!-- 检索列表页 - 职位检索 -->
 <template>
   <div class="default-width">
-    <buttons :current="1" style="position: sticky;"></buttons>
+    <buttons :current="1" style="position: sticky;" class="mx-4"></buttons>
     <div class="position-content">
-      <div class="pb-3 pt-5" style="z-index: 998; background-color: #fff">
+      <div class="pb-3 pt-5 mx-4" style="z-index: 998; background-color: #fff">
         <div class="stickyBox mb-5">
           <headSearch
             v-model="headSearchText"
             @handleSearch="val => handleQueryChange('content', val)"
-          ></headSearch>
-        </div>
-        <!-- <cityFilter class="mx-5 mb-3" ref="cityFilterRef" @change="handleQueryChange"></cityFilter> -->
-        <div class="d-flex justify-space-between mx-5">
-          <conditionFilter ref="conditionFilterRef" showCitySelect :showFilterList="showFilterList" @reset="handleReset" @change="handleQueryChange"></conditionFilter>
-        </div>
+        ></headSearch>
       </div>
-      <div class="mt-3">
+      <!-- <cityFilter class="mx-5 mb-3" ref="cityFilterRef" @change="handleQueryChange"></cityFilter> -->
+      <div class="d-flex justify-space-between mx-5">
+        <conditionFilter ref="conditionFilterRef" showCitySelect :showFilterList="showFilterList" @reset="handleReset" @change="handleQueryChange"></conditionFilter>
+      </div>
+    </div>
+      <div class="mt-3 mx-4">
         <Empty v-if="!items?.length" :message="noParams? '请输入或选择对应的条件查找职位' : '没有相关的职位,请更换搜索条件后再试'"></Empty>
         <PositionLongStrip v-else :items="items"></PositionLongStrip>
         <CtPagination
@@ -79,7 +79,7 @@ const getData = async () => {
         else routerParams[key] = routeQuery[key]
       }
       else if (passingOneId.includes(key)) routerParams[key] = +routeQuery[key] // 传给后端单选且传递整型
-      else routerParams[key] = routeQuery[key].split('_') // 传给后端Arr
+      else routerParams[key] = routeQuery[key].split('_').filter(e => e !== '9999') // 传给后端Arr
     })
   }
   // 没有筛选条件不请求数据

+ 1 - 5
src/views/recruit/personal/recommend/components/positionList.vue

@@ -3,7 +3,7 @@
     <div class="position-box">
       <div class="sub-li"
         v-for="(item, index) in list" :key="index"
-        :class="{'chosen': chosenIndex === index}"
+        :class="[{'chosen': chosenIndex === index}, item.active ? 'elevation-8' : 'elevation-3']"
         :style="`margin-top: ${index ? '12px' : '0'}`"
         @mouseenter="item.active = true" @mouseleave="item.active = false"
         @click="handleClick(item, index)"
@@ -111,10 +111,6 @@ const handleClick = (item, index) => {
   // transition: all .2s linear;
   background-color: #fff;
   border: 1px solid #fff;
-  &:hover {
-    box-shadow: 0 16px 40px 0 rgba(153, 153, 153, .3);
-    .salary { color: var(--v-error-base) !important; }
-  }
 }
 .job-info {
   position: relative;

+ 15 - 0
src/views/recruit/personal/recommend/index.vue

@@ -18,12 +18,16 @@
               :defaultWidth="false"
               :showContentRight="false"
               :propJobId="jobId"
+              :isRecommend="true"
+              @preview="handlePreview"
             ></positionItemDetail>
           </div>
         </div>
       </div>
     </template>
   </div>
+
+  <PreviewImage v-if="showPreview" :urlList="[previewSrc]" :fileName="fileName" @close="showPreview = !showPreview" />
 </template>
 
 <script setup>
@@ -77,6 +81,17 @@ const handleChangePage = () => {
   getList()
 }
 
+// 分享海报预览
+const showPreview = ref(false)
+const previewSrc = ref('')
+const fileName = ref('')
+const handlePreview = (url, filename) => {
+  if (!url) return Snackbar.warning('海报生成失败,请重新点击分享生成海报')
+  previewSrc.value = url
+  fileName.value = filename
+  showPreview.value = true
+}
+
 </script>
 
 <style scoped lang="scss">

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است