Browse Source

Merge branch 'jobFair' into groupJobFair

Xiao_123 4 tháng trước cách đây
mục cha
commit
d1e5c730a7
100 tập tin đã thay đổi với 1468 bổ sung600 xóa
  1. 0 3
      .env.production
  2. 1 0
      components.d.ts
  3. 7 7
      src/App.vue
  4. 7 0
      src/api/position.js
  5. 10 1
      src/api/recruit/enterprise/enterpriseInvite.js
  6. 16 0
      src/api/recruit/enterprise/jobFair.js
  7. 9 0
      src/api/recruit/personal/personalCenter/student.js
  8. 3 3
      src/api/recruit/personal/resume/index.js
  9. 1 1
      src/api/recruit/personal/shareJob/index.js
  10. BIN
      src/assets/logo.png
  11. 0 6
      src/assets/logo.svg
  12. 1 1
      src/assets/svg/wallet.svg
  13. 9 29
      src/components/Enterprise/hotPromoted.vue
  14. 3 11
      src/components/Enterprise/info.vue
  15. 1 1
      src/components/FormUI/autocomplete/index.vue
  16. 1 1
      src/components/Loading/index.vue
  17. 13 19
      src/components/Position/item.vue
  18. 1 1
      src/components/Position/longCompany.vue
  19. 19 10
      src/components/Position/longStrip.vue
  20. 2 12
      src/components/Position/similarPositions.vue
  21. 3 2
      src/components/PositionLongStrip/item.vue
  22. 3 1
      src/components/headSearch/index.vue
  23. 1 1
      src/components/personalRecharge/initPay.vue
  24. 1 1
      src/components/personalRecharge/pay.vue
  25. 1 1
      src/config/axios/service.js
  26. 2 2
      src/layout/company/navBar.vue
  27. 3 3
      src/layout/personal/navBar.vue
  28. 5 3
      src/layout/personal/slider.vue
  29. 1 1
      src/locales/en.js
  30. 1 1
      src/locales/zh-CN.js
  31. 2 1
      src/permission.js
  32. 1 1
      src/plugins/dialogExtend/components/jobFairAd.vue
  33. 1 1
      src/plugins/vuetify.js
  34. 57 30
      src/router/modules/components/recruit/personCenter.js
  35. 43 0
      src/router/modules/recruit.js
  36. 1 1
      src/router/modules/remaining.js
  37. 25 0
      src/store/personCenter.js
  38. 27 27
      src/store/user.js
  39. 13 13
      src/styles/index.css
  40. 0 0
      src/styles/index.min.css
  41. 150 74
      src/styles/index.scss
  42. 5 5
      src/styles/personal/navBar.css
  43. 0 0
      src/styles/personal/navBar.min.css
  44. 5 5
      src/styles/personal/navBar.scss
  45. 15 5
      src/styles/protocol/index.css
  46. 1 1
      src/styles/protocol/index.min.css
  47. 1 1
      src/styles/protocol/index.scss
  48. 2 2
      src/styles/recruit/company.css
  49. 1 1
      src/styles/recruit/company.min.css
  50. 3 3
      src/styles/recruit/company.scss
  51. 2 2
      src/styles/recruit/position/index.css
  52. 1 1
      src/styles/recruit/position/index.min.css
  53. 2 2
      src/styles/recruit/position/index.scss
  54. 2 2
      src/utils/check.js
  55. 1 1
      src/utils/position.js
  56. 1 1
      src/utils/whiteList.js
  57. 1 1
      src/version.js
  58. 1 1
      src/views/entrances/navBar/index.vue
  59. 1 1
      src/views/headhunting/components/nav.vue
  60. 1 1
      src/views/integral/pointsManagement/components/integralShow.vue
  61. 1 1
      src/views/mall/components/prizeDraw/grid.vue
  62. 1 1
      src/views/mall/components/prizeDraw/slotMachine.vue
  63. 1 1
      src/views/mallCopy/index.vue
  64. 84 0
      src/views/recruit/components/contactUs/index.vue
  65. 39 20
      src/views/recruit/components/jobFairEntShare/index.vue
  66. 4 4
      src/views/recruit/components/message/components/chatting.vue
  67. 153 40
      src/views/recruit/components/message/index.vue
  68. 1 1
      src/views/recruit/entRegister/register.vue
  69. 1 1
      src/views/recruit/enterprise/hirePosition/components/add.vue
  70. 8 0
      src/views/recruit/enterprise/interviewManagement/components/invite.vue
  71. 3 1
      src/views/recruit/enterprise/interviewManagement/components/item.vue
  72. 63 29
      src/views/recruit/enterprise/jobFair/details.vue
  73. 7 23
      src/views/recruit/enterprise/jobFair/editJob/index.vue
  74. 240 6
      src/views/recruit/enterprise/jobFair/index.vue
  75. 23 10
      src/views/recruit/enterprise/jobFair/job/item.vue
  76. 2 2
      src/views/recruit/enterprise/membershipPackage/dynamic/balance.vue
  77. 2 2
      src/views/recruit/enterprise/membershipPackage/dynamic/package-copy.vue
  78. 3 3
      src/views/recruit/enterprise/membershipPackage/dynamic/package.vue
  79. 126 14
      src/views/recruit/enterprise/positionManagement/components/add.vue
  80. 1 1
      src/views/recruit/enterprise/positionManagement/components/item.vue
  81. 9 1
      src/views/recruit/enterprise/positionManagement/components/jobRequirements.vue
  82. 7 0
      src/views/recruit/enterprise/resume/components/invite.vue
  83. 24 15
      src/views/recruit/enterprise/resume/components/table.vue
  84. 3 3
      src/views/recruit/enterprise/search/retrieval/index.vue
  85. 0 2
      src/views/recruit/enterprise/staffChangePassword/index.vue
  86. 5 58
      src/views/recruit/enterprise/systemManagement/groupAccount/inviteConfirm.vue
  87. 76 24
      src/views/recruit/enterprise/talentMap/index.vue
  88. 6 2
      src/views/recruit/enterprise/talentPool/components/details.vue
  89. 2 2
      src/views/recruit/enterprise/talentPool/components/details/jobIntention.vue
  90. 1 1
      src/views/recruit/enterprise/tradingOrder/components/pointsAndBalance.vue
  91. 28 2
      src/views/recruit/personal/PersonalCenter/index.vue
  92. 18 2
      src/views/recruit/personal/PersonalCenter/jobFeedback/components/delivery.vue
  93. 3 4
      src/views/recruit/personal/PersonalCenter/jobFeedback/components/interested.vue
  94. 5 6
      src/views/recruit/personal/PersonalCenter/jobFeedback/components/interview/item.vue
  95. 1 1
      src/views/recruit/personal/PersonalCenter/memberBenefits/membershipPackage/index.vue
  96. 0 7
      src/views/recruit/personal/PersonalCenter/resume/analysis/index.vue
  97. 3 3
      src/views/recruit/personal/PersonalCenter/resume/attachment/index.vue
  98. 1 1
      src/views/recruit/personal/PersonalCenter/resume/template/index.vue
  99. 11 0
      src/views/recruit/personal/PersonalCenter/student/EnterpriseRecommendationLetter/index.vue
  100. 11 0
      src/views/recruit/personal/PersonalCenter/student/InternshipButler/index.vue

+ 0 - 3
.env.production

@@ -5,15 +5,12 @@ VITE_NODE_ENV = 'production'
 VITE_APP_TITLE = 门墩儿
 
 # 访问路径
-# VITE_ACCESS_BASE_URL = 'http://menduner.citupro.com:7878' # 发布测试环境 
 VITE_ACCESS_BASE_URL = 'https://www.menduner.com' # 发布生产环境 
 # 
 # 请求路径
-# VITE_BASE_URL = 'http://menduner.citupro.com:7878' # 发布测试环境 
 VITE_BASE_URL = 'https://www.menduner.com' # 发布生产环境 
 
 # 预览路径
-# VITE_PREVIEW_URL = 'http://192.168.3.91:8012' # 发布测试环境 
 VITE_PREVIEW_URL = 'https://kkfileview.menduner.com/' # 发布生产环境 
 
 # 租户id

+ 1 - 0
components.d.ts

@@ -31,6 +31,7 @@ declare module 'vue' {
     CtTextField: typeof import('./src/components/CtVuetify/CtTextField/index.vue')['default']
     DatePicker: typeof import('./src/components/DatePicker/index.vue')['default']
     Echarts: typeof import('./src/components/Echarts/index.vue')['default']
+    ElCascader: typeof import('element-plus/es')['ElCascader']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     Empty: typeof import('./src/components/Empty/index.vue')['default']
     File: typeof import('./src/components/Upload/file.vue')['default']

+ 7 - 7
src/App.vue

@@ -57,7 +57,7 @@ const checkVersion = () => {
   const baseUrl = import.meta.env.VITE_BASE_URL || ''
   // const env_v = import.meta.env?.VITE_VERSION || ''
   const tenantId = import.meta.env?.VITE_TENANTCODE || ''
-  const get_v = localStorage.getItem('RES_VERSION') || ''
+  // const get_v = localStorage.getItem('RES_VERSION') || ''
   // 
   if (!baseUrl || !vue_version || !tenantId) return
 
@@ -69,14 +69,14 @@ const checkVersion = () => {
     if (ConfirmDone) {
       return
     }
-    if (version === get_v && version !== vue_version) { // reload(true)刷新不成功,清除缓存刷新
-      localStorage.clear()
-      window.location.replace(window.location.href)
-      return
-    }
+    // if (version === get_v && version !== vue_version) { // reload(true)刷新不成功,清除缓存刷新
+    //   localStorage.clear()
+    //   window.location.replace(window.location.href)
+    //   return
+    // }
     ConfirmDone = true  // 后续不弹Confirm
     Confirm('系统提示', '发现新版本,将立即刷新页面', { hideCancelBtn: true }).then(() => {
-      localStorage.setItem('RES_VERSION', version)
+      // localStorage.setItem('RES_VERSION', version)
       window.location.reload(true)
     })
   }).catch(err => {

+ 7 - 0
src/api/position.js

@@ -289,4 +289,11 @@ export const getJobAdvertisedShare = async (data) => {
     url: '/app-api/menduner/system/job/advertised/share',
     data
   })
+}
+
+// 撤销已投递的简历(未查看的才可以撤销)
+export const cancelDeliveryResume = async (id) => {
+  return await request.post({
+    url: '/app-api/menduner/system/job-cv-rel/withdraw?id=' + id
+  })
 }

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

@@ -23,7 +23,7 @@ export const enterpriseInviteRefresh = async (code) => {
   })
 }
 
-// 同意加入
+// 企业者-同意加入
 export const enterpriseInviteRecordConsent = async (params) => {
   return await request.post({
     tokenIndex: 1, // 使用招聘token
@@ -32,6 +32,15 @@ export const enterpriseInviteRecordConsent = async (params) => {
   })
 }
 
+// 求职者-同意加入
+export const enterpriseInvitePersonRecordConsent = async (params) => {
+  return await request.post({
+    tokenIndex: 2, // 使用求职token
+    url: '/app-api/menduner/system/enterprise-invite-record/consent',
+    params
+  })
+}
+
 // 获得企业邀请记录分页
 export const enterpriseInviteRecordPage = async (params) => {
   return await request.get({

+ 16 - 0
src/api/recruit/enterprise/jobFair.js

@@ -98,4 +98,20 @@ export const updateJoinDate = async (data) => {
     url: '/app-api/flames/pracice/submit/record/update',
     data
   })
+}
+
+// 招聘会企业权益
+export const getJobFairRights = async (jobFairId) => {
+  return await request.get({
+    url: '/app-api/menduner/system/recruit/job-fair/admission/get',
+    params: { jobFairId }
+  })
+}
+
+// 根据招聘会id获取未加入招聘会的职位列表
+export const getJobFairPositionList = async (params) => {
+  return await request.get({
+    url: '/app-api/menduner/system/recruit/job-fair/get/not-joined-job/page',
+    params
+  })
 }

+ 9 - 0
src/api/recruit/personal/personalCenter/student.js

@@ -0,0 +1,9 @@
+import request from '@/config/axios'
+
+// 学生实习企业列表
+export const practiceProcess = async (data) => {
+  return await request.post({
+		url: '/app-api/flames/student/practice/process',
+		data
+	})
+}

+ 3 - 3
src/api/recruit/personal/resume/index.js

@@ -216,7 +216,7 @@ export const schoolList = async (data) => {
 // 院系列表
 export const departmentList = async (data) => {
   return await request.post({
-    url: 'app-api/flames/school/department/list',
+    url: '/app-api/flames/school/department/list',
     data
   })
 }
@@ -224,7 +224,7 @@ export const departmentList = async (data) => {
 // 获取学生基本信息
 export const getStudentInfo = async (data) => {
   return await request.post({
-    url: 'app-api/menduner/system/person/resume/student/get',
+    url: '/app-api/menduner/system/person/resume/student/get',
     data
   })
 }
@@ -294,4 +294,4 @@ export const resumePersonFillAll = async () => {
   return await request.post({
     url: '/app-api/menduner/system/person/resume/person/fill/all'
   })
-}
+}

+ 1 - 1
src/api/recruit/personal/shareJob/index.js

@@ -37,4 +37,4 @@ export const saveStudentSimpleInfo = async (data) => {
     url: '/app-api/menduner/system/person/resume/student/save',
     data
   })
-}
+}

BIN
src/assets/logo.png


+ 0 - 6
src/assets/logo.svg

@@ -1,6 +0,0 @@
-<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M261.126 140.65L164.624 307.732L256.001 466L377.028 256.5L498.001 47H315.192L261.126 140.65Z" fill="#1697F6"/>
-<path d="M135.027 256.5L141.365 267.518L231.64 111.178L268.731 47H256H14L135.027 256.5Z" fill="#AEDDFF"/>
-<path d="M315.191 47C360.935 197.446 256 466 256 466L164.624 307.732L315.191 47Z" fill="#1867C0"/>
-<path d="M268.731 47C76.0026 47 141.366 267.518 141.366 267.518L268.731 47Z" fill="#7BC6FF"/>
-</svg>

+ 1 - 1
src/assets/svg/wallet.svg

@@ -1 +1 @@
-<svg t="1723186975364" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10136" width="200" height="200"><path d="M86.6 332.7l67.2-67.2h694.4l89.6 44.8V457L612 471l-11.5 221.5L937.8 713v157.3L893 937.5H153.8l-67.2-44.8z" fill="#00897b" p-id="10137"></path><path d="M893 249.8v-61C893 120 837 64 768.2 64H189c-68.8 0-124.8 56-124.8 124.8v668.7c0 56.3 46.1 102.4 102.4 102.4h691.1c56.3 0 102.4-46.1 102.4-102.4v-512c0.1-43.9-28.1-81.2-67.1-95.7z m-784-51.5h560v-44.8H117.5c13.1-26.4 40.1-44.8 71.5-44.8h579.1c44.1 0 80 35.9 80 80v54.4H166.6c-21.3 0-41.2 6.7-57.6 17.9v-62.7z m806.4 492.8H660.2c-19.9 0-36-16.2-36-36V525.5c0-19.9 16.2-36 36-36h255.2v201.6z m0-246.4H660.2c-44.6 0-80.8 36.3-80.8 80.8V655c0 44.6 36.3 80.8 80.8 80.8h255.2v121.6c0 31.8-25.9 57.6-57.6 57.6H166.6c-31.8 0-57.6-25.9-57.6-57.6V345.6c0-31.8 25.9-57.6 57.6-57.6h691.1c31.8 0 57.6 25.9 57.6 57.6v99.1z" fill="#00897b" p-id="10138"></path><path d="M713.8 153.5h89.6v44.8h-89.6zM769.8 646.3c30.9 0 56-25.1 56-56s-25.1-56-56-56-56 25.1-56 56 25.1 56 56 56z" fill="#00897b" p-id="10139"></path></svg>
+<svg t="1723186975364" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10136" width="200" height="200"><path d="M86.6 332.7l67.2-67.2h694.4l89.6 44.8V457L612 471l-11.5 221.5L937.8 713v157.3L893 937.5H153.8l-67.2-44.8z" fill="#00B760" p-id="10137"></path><path d="M893 249.8v-61C893 120 837 64 768.2 64H189c-68.8 0-124.8 56-124.8 124.8v668.7c0 56.3 46.1 102.4 102.4 102.4h691.1c56.3 0 102.4-46.1 102.4-102.4v-512c0.1-43.9-28.1-81.2-67.1-95.7z m-784-51.5h560v-44.8H117.5c13.1-26.4 40.1-44.8 71.5-44.8h579.1c44.1 0 80 35.9 80 80v54.4H166.6c-21.3 0-41.2 6.7-57.6 17.9v-62.7z m806.4 492.8H660.2c-19.9 0-36-16.2-36-36V525.5c0-19.9 16.2-36 36-36h255.2v201.6z m0-246.4H660.2c-44.6 0-80.8 36.3-80.8 80.8V655c0 44.6 36.3 80.8 80.8 80.8h255.2v121.6c0 31.8-25.9 57.6-57.6 57.6H166.6c-31.8 0-57.6-25.9-57.6-57.6V345.6c0-31.8 25.9-57.6 57.6-57.6h691.1c31.8 0 57.6 25.9 57.6 57.6v99.1z" fill="#00B760" p-id="10138"></path><path d="M713.8 153.5h89.6v44.8h-89.6zM769.8 646.3c30.9 0 56-25.1 56-56s-25.1-56-56-56-56 25.1-56 56 25.1 56 56 56z" fill="#00B760" p-id="10139"></path></svg>

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

@@ -8,11 +8,7 @@
             <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>
           <div class="company-info cursor-pointer">
-            <!-- <h3 v-ellipse-tooltip>{{ formatName(item.enterprise.anotherName || item.enterprise.name) }}</h3> -->
-            <h3 :ref="el => { if(el) companyNameRefs[index] = el }">
-              {{ formatName(item.enterprise.anotherName || item.enterprise.name) }}
-              <v-tooltip v-if="isTextOverflow[index]" activator="parent" location="top">{{ formatName(item.enterprise.anotherName || item.enterprise.name) }}</v-tooltip>
-            </h3>
+            <h3 v-ellipse-tooltip>{{ formatName(item.enterprise.anotherName || item.enterprise.name) }}</h3>
             <p>
               {{ item?.enterprise.scaleName }}
               <span class="septal-line" v-if="item.enterprise.industryName"></span>
@@ -21,8 +17,8 @@
           </div>
         </div>
 
-        <div v-if="item?.enterprise?.welfareTagStr" style="padding: 6px 0; border-bottom: 1px solid #EBEBEB">
-          <div v-ellipse-tooltip class="px-5 welfareTag ellipsis-tag">{{ item.enterprise.welfareTagStr }}</div>
+        <div class="mx-4 py-2" style="height: 35px;" :style="{'border-bottom': item?.enterprise?.welfareTagStr ? '1px solid #EBEBEB' : 'none'}">
+          <div v-ellipse-tooltip class="welfareTag ellipsis-tag">{{ item.enterprise.welfareTagStr }}</div>
         </div>
 
         <!-- 职位列表 -->
@@ -75,25 +71,11 @@ const props = defineProps({
 
 const router = useRouter()
 
-const isTextOverflow = ref({})
-const companyNameRefs = ref({})
-// 检查文本是否溢出
-const checkTextOverflow = () => {
-  Object.entries(companyNameRefs.value).forEach(([index, element]) => {
-    if (element) {
-      isTextOverflow.value[index] = element.scrollWidth > element.clientWidth
-    }
-  })
-}
-
 const list = ref([])
 watch(
   () => props.items, 
   (newVal) => {
     list.value = newVal
-    nextTick(() => {
-      checkTextOverflow()
-    })
   },
   { immediate: true },
   { deep: true }
@@ -108,7 +90,6 @@ const handleClickPosition = (k) => {
 // 查看更多职位
 const handleMoreEnterprise = (item) => {
   if (!item.enterprise.id) return
-  // window.open(`/recruit/personal/company/details/${item.enterprise.id}?key=recruitmentPositions`)
   const name = formatName(item.enterprise.anotherName || item.enterprise.name)
   window.open(`/recruit/personal/position?content=${name.includes('&') ? encodeURIComponent(name) : name}`)
 }
@@ -125,7 +106,7 @@ const handleMoreEnterprise = (item) => {
   min-width: calc((100% - 24px) / 3);
   max-width: calc((100% - 24px) / 3);
   margin: 0 12px 12px 0;
-  height: 370px;
+  height: 373px;
   border-radius: 12px;
   padding: 0;
   overflow: hidden;
@@ -140,7 +121,7 @@ const handleMoreEnterprise = (item) => {
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
-  color: #cec149 !important;
+  color: #00B760 !important;
 }
 .company-info {
   float: left;
@@ -157,14 +138,14 @@ const handleMoreEnterprise = (item) => {
   border-radius: 8px;
 }
 .welfareTag {
-  color: #CEC149;
+  color: #00B760;
   font-size: 13px;
 }
 .company-info h3 {
   height: 22px;
   font-size: 18px;
   font-weight: 700;
-  color: #404040;
+  color: #0E100F;
   line-height: 22px;
   margin: 0 0 4px 0;
   padding: 0;
@@ -200,7 +181,7 @@ ul li {
   font-size: 16px;
   float: right;
   font-weight: 700;
-  color: #CEC149;
+  color: #00B760;
   line-height: 22px;
   max-width: none;
   text-align: right;
@@ -208,7 +189,6 @@ ul li {
 }
 .name {
   position: relative;
-  // max-width: 200px;
   line-height: 22px;
   font-weight: 700;
   color: #404040;
@@ -234,4 +214,4 @@ ul li {
     }
   }
 }
-</style>
+</style>

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

@@ -2,9 +2,9 @@
   <div class="info-box">
     <h4 class="mb-3">{{ $t('enterprise.companyInfo') }}</h4>
     <div style="height: 50px; line-height: 50px;">
-      <v-img class="float-left" :src="props.info.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" :width="45" height="45"></v-img>
+      <v-img class="float-left" :src="props.info.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" rounded contain :width="45" height="45"></v-img>
       <div class="ml-3 float-left">
-        <p class="enterprise-name cursor-pointer" @click.stop="jumpToEnterpriseDetail(props.info.enterprise.id, true)">{{ formatName(props.info.enterprise.anotherName || props.info.enterprise.name) }}</p>
+        <p class="enterprise-name cursor-pointer" v-ellipse-tooltip @click.stop="jumpToEnterpriseDetail(props.info.enterprise.id, true)">{{ formatName(props.info.enterprise.anotherName || props.info.enterprise.name) }}</p>
       </div>
     </div>
     <div class="mt-3 border-bottom-dashed" style="font-size: 14px;">
@@ -53,13 +53,8 @@ getData()
   padding: 20px 15px;
 }
 .enterprise-name {
-  width: 165px;
   font-weight: 500;
-  max-width: 165px;
-  vertical-align: middle;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-  overflow: hidden;
+  max-width: 166px;
   &:hover {
     color: var(--v-primary-base);
   }
@@ -72,9 +67,6 @@ getData()
   white-space: nowrap;
   text-overflow: ellipsis;
   overflow: hidden;
-  &:hover {
-    color: var(--v-primary-base);
-  }
 }
 .more-position {
   color: var(--v-primary-base);

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

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

+ 1 - 1
src/components/Loading/index.vue

@@ -39,7 +39,7 @@ defineProps({
 //   width: 120px;
 //   height: 22px;
 //   border-radius: 20px;
-//   color: #00897b;
+//   color: #00B760;
 //   border: 2px solid;
 //   position: relative;
 // }

+ 13 - 19
src/components/Position/item.vue

@@ -1,10 +1,13 @@
 <template>
   <div class="d-flex">
     <div class="position-box">
-      <div class="sub-li" v-for="(item, index) in props.items" :key="index" 
+      <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">
+        @mouseenter="item.active = true" @mouseleave="item.active = false"
+        @click.stop="handlePosition(item)"
+      >
         <div class="job-info">
           <div class="sub-li-top">
             <div class="sub-li-info">
@@ -13,8 +16,8 @@
             </div>
           </div>
           <div class="d-flex justify-space-between align-center">
-            <div v-ellipse-tooltip 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="">
+            <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">
                 <span
                   v-if="item[j.value] || j.value === 'areaName'"
                   class="mr-1 d-inline-block"
@@ -42,13 +45,14 @@
           </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 class="names ml-2 font-size-14 text-truncate" style="max-width: 88%;">
+              <span v-ellipse-tooltip class="names ml-2 font-size-14 ellipsis" style="max-width: 88%;">
                 {{ formatName(item.anotherName || item.enterpriseName) }}
                 <span class="color-999 font-size-13 ml-3">
                   <span>{{ item.industryName }}</span>
@@ -66,7 +70,6 @@
 
 <script setup>
 defineOptions({ name: 'position-card-item' })
-import { ref, watch } from 'vue'
 import { commissionCalculation, jumpToEnterpriseDetail } from '@/utils/position'
 import { timesTampChange } from '@/utils/date'
 import { formatName } from '@/utils/getText'
@@ -85,16 +88,7 @@ const props = defineProps({
     default: true
   }
 })
-// const list = ref([])
-// watch(
-//   () => props.items, 
-//   (newVal) => {
-//     console.log(123, newVal)
-//     list.value = newVal
-//   },
-//   { immediate: true },
-//   { deep: true }
-// )
+
 const desc = [
   { mdi: 'mdi-map-marker-outline', value: 'areaName' },
   { mdi: 'mdi-school-outline', value: 'eduName' },
@@ -155,7 +149,7 @@ const handlePosition = (item) => {
 .salary {
   font-size: 16px;
   font-weight: 700;
-  color: #CEC149;
+  color: #00B760;
   line-height: 22px;
   flex: none;
 }
@@ -180,7 +174,7 @@ const handlePosition = (item) => {
   text-overflow: ellipsis;
   white-space: nowrap;
   font-weight: 600;
-  color: #345768;
+  color: #0E100F;
   &:hover {
     color: var(--v-primary-base);
   }

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

@@ -83,7 +83,7 @@ const handleCancel = async (item) => {
   height: 22px;
   font-size: 16px;
   font-weight: 400;
-  color: var(--color-222);
+  color: #0E100F;
   line-height: 22px;
   margin: 0 0 4px 0;
   padding: 0;

+ 19 - 10
src/components/Position/longStrip.vue

@@ -5,6 +5,7 @@
     >
       <div class="info-header">
         <div v-if="val.active && val.job.status === '0'" class="header-btn">
+          <v-btn v-if="showCancelDeliveryResumeBtn && val.cvRel?.status === '0'" color="primary" size="small" @click.stop="emits('cancelDeliveryResume', val.cvRel.id)">撤销投递简历</v-btn>
           <v-btn v-if="props.showCancelBtn" class="half-button ml-3" color="primary" size="small" @click.stop="handleCancel(val)">取消收藏</v-btn>
           <v-btn class="half-button ml-3" color="primary" size="small" @click.stop="toDetails(val)">立即沟通</v-btn>
         </div>
@@ -19,27 +20,27 @@
       </div>
       <div class="info-content" >
         <div class="job-info">
-          <div v-ellipse-tooltip class="job-name ellipsis" :class="{'cursor-pointer': val.job.status === '0'}">
-            <svg-icon v-if="val.job.bizId" name="jobFair" size="18" class="mr-1"></svg-icon>
+          <div class="job-name ellipsis" :class="{'cursor-pointer': val.job.status === '0'}" v-ellipse-tooltip>
+            <!-- <svg-icon v-if="val.job.bizId" name="jobFair" size="18" class="mr-1"></svg-icon> -->
             <span class="mr-3" :class="{'info-name': val.job.status === '0'}" @click.stop="handleToPositionDetails(val)">{{ formatName(val.job.name) }}</span>
             <span>
               [{{ !val.job.areaId ? '全国' : val.job.area?.str }}]
             </span>
           </div>
           <div class="job-other">
-            <span v-if="!val.job.payFrom && !val.job.payTo" class="salary">面议</span>
-            <span v-else class="salary">{{ val.job.payFrom ? val.job.payFrom + '-' : ''}}{{ val.job.payTo }}{{ val.job.payName ? '/' + val.job.payName : '' }}</span>
+            <span v-if="!val.job.payFrom && !val.job.payTo" class="salary color-primary">面议</span>
+            <span v-else class="salary color-primary">{{ val.job.payFrom ? val.job.payFrom + '-' : ''}}{{ val.job.payTo }}{{ val.job.payName ? '/' + val.job.payName : '' }}</span>
             <v-chip v-if="val.job?.expName" class="mx-3" color="primary" label size="small">{{ val.job.expName }}</v-chip>
             <v-chip v-if="val.job?.eduName" color="primary" label size="small">{{ val.job.eduName }}</v-chip>
           </div>
         </div>
         <div class="company-info ml-3" style="flex: 1;">
           <div style="height: 50px; width: 50px;">
-            <v-img width="50" height="50" :src="val.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/7.png'"></v-img>
+            <v-img width="50" height="50" :src="val.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/7.png'" rounded contain></v-img>
           </div>
           <div class="ml-3">
-            <div v-ellipse-tooltip class="cursor-pointer ellipsis info-name" style="max-width: 480px;" @click.stop="jumpToEnterpriseDetail(val.enterprise.id)">{{ formatName(val.enterprise.anotherName || val.enterprise.name) }}</div>
-            <div v-ellipse-tooltip class="mt-3 ellipsis color-666 font-size-13" style="max-width: 260px;">
+            <div class="cursor-pointer info-name" v-ellipse-tooltip @click.stop="jumpToEnterpriseDetail(val.enterprise.id)">{{ formatName(val.enterprise.anotherName || val.enterprise.name) }}</div>
+            <div class="mt-3 ellipsis color-666 font-size-13" style="max-width: 260px;">
               <span v-for="(k, i) in desc" :key="k">
                 {{ val.enterprise[k] }}
                 <span v-if="i !== desc.length - 1 && val.enterprise[k] && val.enterprise[desc[i + 1]]" class="septal-line"></span>
@@ -71,16 +72,22 @@ import dialogExtend from '@/plugins/dialogExtend'
 import { formatName } from '@/utils/getText'
 import { jumpToEnterpriseDetail } from '@/utils/position'
 
-const emits = defineEmits(['refresh'])
+const emits = defineEmits(['refresh', 'cancelDeliveryResume'])
 const { t } = useI18n()
 const props = defineProps({
   items: {
     type: Array,
     default: () => []
   },
+  // 是否展示取消收藏按钮
   showCancelBtn: {
     type: Boolean,
     default: false
+  },
+  // 是否显示取消投递简历按钮
+  showCancelDeliveryResumeBtn: {
+    type: Boolean,
+    default: false
   }
 })
 
@@ -98,7 +105,9 @@ const handleCancel = async (item) => {
 // 职位详情
 const handleToPositionDetails = (item) => {
   if (item.job.status === '1') return
-  router.push(`/recruit/personal/position/details/${item.job.id}`)
+  let path = `/recruit/personal/position/details/${item.job.id}`
+  if (item.cvRel?.jobFairId) path += `?jobFairId=${item.cvRel.jobFairId}`
+  router.push(path)
 }
 
 let toDetailsInfo = {}
@@ -193,7 +202,7 @@ const loginClose = () => {
         width: 100%;
         height: 22px;
         line-height: 22px;
-        color: var(--color-222);
+        color: #0E100F;
         margin-bottom: 12px;
       }
       .job-other {

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

@@ -43,16 +43,14 @@ const handlePosition = (item) => {
   font-weight: 500;
   display: inline-block;
   vertical-align: middle;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-  overflow: hidden;
+  color: #0E100F;
   &:hover {
     color: var(--v-primary-base);
   }
 }
 .recruit-salary {
   float: right;
-  color: var(--v-error-base);
+  color: var(--v-primary-base);
   font-weight: 500;
   height: auto;
   vertical-align: sub;
@@ -67,9 +65,6 @@ const handlePosition = (item) => {
   display: inline-block;
   max-width: 150px;
   vertical-align: middle;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-  overflow: hidden;
   color: var(--color-666);
   font-size: 13px;
   margin-left: 5px;
@@ -81,9 +76,4 @@ const handlePosition = (item) => {
   color: #555;
   font-size: 13px;
 }
-.more-btn {
-  // position: absolute;
-  // bottom: 18px;
-  // margin-top: 20px;
-}
 </style>

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

@@ -130,6 +130,7 @@ const handlePosition = (item) => {
   white-space: nowrap;
   font-size: 16px;
   font-weight: 600;
+  color: #0E100F;
   &:hover {
     color: var(--v-primary-base);
   }
@@ -192,8 +193,8 @@ const handlePosition = (item) => {
 .salary {
   font-size: 16px;
   font-weight: 700;
-  color: var(--v-error-base);
+  color: var(--v-primary-base);
   line-height: 22px;
   flex: none;
 }
-</style>
+</style>

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

@@ -97,7 +97,9 @@ const handleClickJob = (val) => {
 .search {
   height: 50px;
   width: 800px;
-  border: 2px solid var(--v-primary-base);
+  border-top: 2px solid var(--v-primary-base);
+  border-left: 2px solid var(--v-primary-base);
+  border-bottom: 2px solid var(--v-primary-base);
   border-radius: 5px;
   .jobBox {
     cursor: pointer;

+ 1 - 1
src/components/personalRecharge/initPay.vue

@@ -262,7 +262,7 @@ const mlArr = [0, 82, 230, 378, 526, 674, 754]
   border-radius: 6px;
   background-color: #f7f8fa;
   &-left {
-    border: 1px solid #00897B;
+    border: 1px solid #00B760;
     border-radius: 6px;
     padding: 5px;
     width: 182px;

+ 1 - 1
src/components/personalRecharge/pay.vue

@@ -212,7 +212,7 @@ const mlArr = [0, 157, 366, 575, 725]
   border-radius: 6px;
   background-color: #f7f8fa;
   &-left {
-    border: 1px solid #00897B;
+    border: 1px solid #00B760;
     border-radius: 6px;
     padding: 5px;
     width: 182px;

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

@@ -266,7 +266,7 @@ service.interceptors.response.use(
       Snackbar.error(t('sys.api.errMsg901'))
       return Promise.reject(new Error(msg))
     }
-    if ([1100017019, 1100016002, 1100021016, 1100017022].includes(code)) {
+    if ([1100017019, 1100016002, 1100021016, 1100017022, 1100056008, 1100056005].includes(code)) {
       // 1100017019邮箱未注册、1100016002手机号未注册过、1100021016注册企业申请中
       // 未注册过的手机号将code码返回
       return Promise.reject(data)

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

@@ -3,13 +3,13 @@
     <v-toolbar class="banner font-size-14 pl-0" density="compact" style="height: 50px;">
       <div class="innerBox d-flex justify-space-between">
         <div class="nav-logo" style="cursor: pointer;" @click="handleLogoClick">
-          <v-img src="../../assets/logo.png"  aspect-ratio="16/9" cover :width="97" style="height: 40px"></v-img>
+          <v-img src="../../assets/logo.png"  aspect-ratio="16/9" contain :width="97" style="height: 40px"></v-img>
         </div>
         
         <div class="d-flex user-nav align-center">
           <!-- 企业logo、企业名称 -->
           <div class="d-flex align-center cursor-pointer">
-            <v-img @click="enterpriseClick(2)" rounded width="40" height="40" :src="baseInfo?.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" ></v-img>
+            <v-img @click="enterpriseClick(2)" rounded contain width="40" height="40" :src="baseInfo?.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'" ></v-img>
             <span @click="enterpriseClick(1)" class="ml-3 commonHover">{{ formatName(baseInfo?.enterpriseAnotherName || baseInfo?.enterpriseName) }}</span>
           </div>
 

+ 3 - 3
src/layout/personal/navBar.vue

@@ -8,7 +8,7 @@
       <div class="innerBox d-flex justify-space-between" style="min-width: 1240px;">
         <div>
           <div class="nav-logo mr-3 mt-1 cursor-pointer d-flex align-center" @click="router.push('/recruitHome')">
-            <v-img src="../../assets/logo.png"  aspect-ratio="16/9" cover :width="97" style="height: 40px"></v-img>
+            <v-img src="../../assets/logo.png"  aspect-ratio="16/9" contain :width="97" style="height: 40px"></v-img>
             <span class="ml-2" style="margin-top: 6px;" :class="{'active-route' : route.path === '/recruitHome'}">首页</span>
           </div>
           <!-- <div class="nav-city">
@@ -38,7 +38,7 @@
         
         <div class="d-flex" style="height: 50px;">
           <div class="btns d-flex align-center" v-if="!isLogin && showLoginBtn">
-            <v-btn color="primary" style="color: #fff !important; background-color: #00897B;" @click="handleLogin">{{ $t('login.loginOrRegister') }}</v-btn>
+            <v-btn color="primary" style="color: #fff !important; background-color: #00B760;" @click="handleLogin">{{ $t('login.loginOrRegister') }}</v-btn>
           </div>
           
           <!-- 头像用户名 -->
@@ -135,7 +135,6 @@ import { getUserBindEnterpriseList, getUserRegisterEnterpriseApply } from '@/api
 import MessageNotification from '../message.vue'
 import { getUserAvatar } from '@/utils/avatar'
 import defineListPage from '@/views/entrances/navBar/components/defineListPage'
-import { update } from 'lodash'
 import { formatName } from '@/utils/getText'
 
 defineOptions({ name: 'personal-navbar' })
@@ -204,6 +203,7 @@ const position = [
   '/recruit/personal/recommend',
   '/recruit/personal/position',
   '/recruit/personal/company',
+  '/recruit/personal/jobFair',
   '/recruit/personal/company/details',
   '/recruit/personal/position/details'
 ]

+ 5 - 3
src/layout/personal/slider.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="slider-box" :style="{'height': getToken() ? '180px' : '92px'}">
+  <div class="slider-box" :style="{'height': getToken() ? '225px' : '137px'}">
     <div v-for="(item, index) in list" :key="index" class="slider-box-item" @click="handleClick(item, index)">
       <v-btn size="30" class="icons" icon variant="text">
         <v-icon class="icons" size="30">{{ item.mdi }}</v-icon>
@@ -23,13 +23,15 @@ import { getToken } from '@/utils/auth'
 const router = useRouter()
 const defaultList = [
   { mdi: 'mdi-arrow-up-bold', tips: '返回顶部' },
-  { mdi: 'mdi-qrcode', tips: '微信公众号', showImg: 'https://minio.citupro.com/dev/menduner/official-account-code4.jpg' }
+  { mdi: 'mdi-qrcode', tips: '微信公众号', showImg: 'https://minio.citupro.com/dev/menduner/official-account-code4.jpg' },
+  { mdi: 'mdi-headphones', tips: '联系我们', path: '/contactUs' }
 ]
 const hasTokenList = [
   { mdi: 'mdi-arrow-up-bold', tips: '返回顶部' },
   { mdi: 'mdi-qrcode', tips: '微信公众号', showImg: 'https://minio.citupro.com/dev/menduner/official-account-code4.jpg' },
   { mdi: 'mdi-bell-outline', tips: '通知', path: '/recruit/personal/message' },
-  { mdi: 'mdi-list-box-outline', tips: '在线简历', path: '/recruit/personal/personalCenter/resume/online' }
+  { mdi: 'mdi-list-box-outline', tips: '在线简历', path: '/recruit/personal/personalCenter/resume/online' },
+  { mdi: 'mdi-headphones', tips: '联系我们', path: '/contactUs' }
 ]
 const list = getToken() ? hasTokenList : defaultList
 

+ 1 - 1
src/locales/en.js

@@ -60,7 +60,7 @@ export default {
     api: {
       operationFailed: 'Operation failed',
       errorTip: 'Error Tip',
-      errorMessage: 'The operation failed, the system is abnormal!',
+      errorMessage: 'The system is under maintenance, please try again later!',
       timeoutMessage: 'Login timed out, please log in again!',
       apiTimeoutMessage: 'The interface request timed out, please refresh the page and try again!',
       apiRequestFailed: 'The interface request failed, please try again later!',

+ 1 - 1
src/locales/zh-CN.js

@@ -60,7 +60,7 @@ export default {
     api: {
       operationFailed: '操作失败',
       errorTip: '错误提示',
-      errorMessage: '操作失败,系统异常!',
+      errorMessage: '系统正在维护中,请稍后再试!',
       timeoutMessage: '登录超时,请重新登录!',
       apiTimeoutMessage: '接口请求超时,请刷新页面重试!',
       apiRequestFailed: '请求出错,请稍候重试',

+ 2 - 1
src/permission.js

@@ -36,7 +36,8 @@ router.beforeEach(async (to, from, next) => {
   if (!localStorage.getItem('mallTemplate')) {
     await mallStore.getMallDiyTemplate()
   }
-  
+
+  localStorage.setItem('routerTest', to.path) // 本地环境保存代码热更新会导致路径缺失问题
   const tokenIndex = getIsEnterprise() ? 1: 2
   if (to.path === '/enterpriseVerification') {
     const res = JSON.parse(localStorage.getItem('emailLoginInfo') || "false")

+ 1 - 1
src/plugins/dialogExtend/components/jobFairAd.vue

@@ -8,7 +8,7 @@
       @update:modelValue="handleChange"
     >
       <div style="cursor: pointer; margin: 0 auto; position: relative;">
-        <v-img src="https://minio.menduner.com/dev/fed8685fb4fec65347c2e3756db230ddd9c8f3538998c5678efe5acb51fb74e1.jpg" :width="adImgWidth" style="height: auto;border-radius: 4px;" @click="adClick"></v-img>
+        <v-img src="https://minio.menduner.com/dev/1a10e21687a6a222b99970907bde2fb2147d9879cb76b614b32dd2512fff2927.jpg" :width="adImgWidth" style="height: auto;border-radius: 4px;" @click="adClick"></v-img>
         <span style="color: #fff; font-size: 32px; position: absolute; right: 0px; top: 0px;" class="mdi mdi-close-circle-outline cursor-pointer px-3" @click="dialog = false"></span>
       </div>
     </v-dialog>

+ 1 - 1
src/plugins/vuetify.js

@@ -21,7 +21,7 @@ const myCustomLightTheme = {
   colors: {
     background: '#FFFFFF',
     surface: '#FFFFFF',
-    primary: '#00897B',
+    primary: '#00B760',
     secondary: '#26A69A',
     error: '#ff5252',
     info: '#2196F3',

+ 57 - 30
src/router/modules/components/recruit/personCenter.js

@@ -163,30 +163,6 @@ const personCenter = [
                   enName: 'Edit Password'
                 }
               },
-              // {
-              //   path: '/recruit/personal/personalCenter/accountSettings/accountBinding',
-              //   component: () => import('@/views/recruit/personal/PersonalCenter/accountSettings/accountBinding'),
-              //   meta: {
-              //     title: '账号绑定',
-              //     enName: 'Account binding'
-              //   }
-              // },
-              // {
-              //   path: '/recruit/personal/personalCenter/accountSettings/realAuthentication',
-              //   component: () => import('@/views/recruit/personal/PersonalCenter/accountSettings/realAuthentication'),
-              //   meta: {
-              //     title: '实名认证',
-              //     enName: 'Real name authentication'
-              //   }
-              // }
-              // {
-              //   path: '/recruit/personal/personalCenter/accountSettings/privacySettings',
-              //   component: () => import('@/views/recruit/personal/PersonalCenter/accountSettings/privacySettings'),
-              //   meta: {
-              //     title: '隐私设置',
-              //     enName: 'Privacy settings'
-              //   }
-              // }
             ]
           },
           // 收货地址
@@ -200,16 +176,67 @@ const personCenter = [
               icon: 'mdi-map-marker-outline'
             }
           },
+          // 学生专区
           {
-            path: '/recruit/personal/personalCenter/studentInformation',
-            name: 'Student Information',
+            path: '/recruit/personal/personalCenter/student',
+            redirect: '/recruit/personal/personalCenter/student/information',
+            name: 'Student',
             permissionName: 'studentInformation',
-            component: () => import('@/views/recruit/personal/PersonalCenter/studentInformation'),
             meta: {
-              title: '学生信息',
+              title: '学生专区',
               enName: 'Student Information',
               icon: 'mdi-account-school-outline'
-            }
+            },
+            children: [
+              {
+                path: '/recruit/personal/personalCenter/student/information',
+                component: () => import('@/views/recruit/personal/PersonalCenter/student/information/index.vue'),
+                meta: {
+                  title: '学生信息',
+                  enName: 'Student Information'
+                }
+              },
+              {
+                path: '/recruit/personal/personalCenter/student/internshipCompany',
+                component: () => import('@/views/recruit/personal/PersonalCenter/student/intershipCompany/index.vue'),
+                meta: {
+                  title: '实习企业',
+                  enName: 'Internship Company'
+                }
+              },
+              {
+                path: '/recruit/personal/personalCenter/student/internshipReport',
+                component: () => import('@/views/recruit/personal/PersonalCenter/student/InternshipReport/index.vue'),
+                meta: {
+                  title: '实习报告',
+                  enName: 'Internship Report'
+                }
+              },
+              {
+                path: '/recruit/personal/personalCenter/student/internshipCertificate',
+                component: () => import('@/views/recruit/personal/PersonalCenter/student/InternshipCertificate/index.vue'),
+                meta: {
+                  title: '实习证书',
+                  enName: 'Internship Certificate'
+                }
+              },
+              {
+                path: '/recruit/personal/personalCenter/student/enterpriseRecommendationLetter',
+                component: () => import('@/views/recruit/personal/PersonalCenter/student/EnterpriseRecommendationLetter/index.vue'),
+                meta: {
+                  title: '企业推荐信',
+                  enName: 'Recommendation Letter'
+                }
+              },
+              {
+                path: '/recruit/personal/personalCenter/student/internshipButler',
+                component: () => import('@/views/recruit/personal/PersonalCenter/student/InternshipButler/index.vue'),
+                meta: {
+                  title: '实习管家',
+                  enName: 'Internship Butler'
+                }
+              }
+            ]
           }
         ]
       }
@@ -217,4 +244,4 @@ const personCenter = [
   }
 ]
 
-export default personCenter
+export default personCenter

+ 43 - 0
src/router/modules/recruit.js

@@ -224,6 +224,49 @@ const recruit = [
       }
     ]
   },
+  {
+    path: '/contactUs',
+    component: Layout,
+    children: [
+      {
+        path: '/contactUs',
+        component: () => import('@/views/recruit/components/contactUs/index.vue'),
+        name: 'ContactUs',
+        meta: {
+          title: '联系我们'
+        }
+      }
+    ]
+  },
+  {
+      path: '/recruit/personal/jobFair',
+      component: Layout,
+      name: 'personalJobFair',
+      meta: {
+        title: '招聘会'
+      },
+      children: [
+        {
+          path: '/recruit/personal/jobFair',
+          component: () => import('@/views/recruit/personal/jobFair/index.vue')
+        },
+      ]
+    },
+    {
+      path: '/recruit/personal/jobFair/:id',
+      component: () => import('@/views/recruit/personal/jobFair/details/index.vue'),
+      name: 'jobFairDetails'
+    },
+    {
+      path: '/recruit/personal/jobFair/enterprises/:id',
+      component: () => import('@/views/recruit/personal/jobFair/details/enterprises.vue'),
+      name: 'jobFairEnterprises'
+    },
+    {
+      path: '/recruit/personal/jobFair/entPosition/:id',
+      component: () => import('@/views/recruit/personal/jobFair/details/entJobCard.vue'),
+      name: 'jobFairEntPosition'
+    },
 ]
 
 setLoginType(recruit, 'personalCommon'),

+ 1 - 1
src/router/modules/remaining.js

@@ -157,4 +157,4 @@ const routeArray = [
   ...common,
   ...remainingRouter
 ]
-export default routeArray
+export default routeArray

+ 25 - 0
src/store/personCenter.js

@@ -0,0 +1,25 @@
+import { defineStore } from 'pinia'
+
+export const usePersonCenterStore = defineStore('personCenter',
+	{
+		state: () => ({
+			initialIndex: 0,
+			urlsList: []
+		}),
+		actions: {
+			setPreviewData (arr, index) {
+				this.urlsList = arr
+				this.initialIndex = index
+			},
+			clearPreviewData () {
+				this.urlsList = []
+				this.initialIndex = 0
+			}
+		}
+	},
+	{
+		persist: true,
+		devtools: true
+	}
+)
+

+ 27 - 27
src/store/user.js

@@ -2,12 +2,12 @@ import { defineStore } from 'pinia'
 import { setToken, removeToken, setRefreshToken, getToken } from '@/utils/auth'
 import {
   smsLogin,
-  passwordLogin, 
+  passwordLogin,
   getBaseInfo,
-  switchLoginOfEnterprise, 
-  getEnterprisingUserInfo, 
-  logoutToken, 
-  logout 
+  switchLoginOfEnterprise,
+  getEnterprisingUserInfo,
+  logoutToken,
+  logout
 } from '@/api/common'
 import { getUserInfo } from '@/api/personal/user'
 import { getEntUpdatePasswordCheck } from '@/api/recruit/enterprise/information'
@@ -18,6 +18,7 @@ import { timesTampChange } from '@/utils/date'
 import { updateEventList } from '@/utils/eventList'
 import { getBaseInfoDictOfName } from '@/utils/getText'
 import { checkPersonBaseInfo } from '@/utils/check'
+import { getStudentInfo } from '@/api/recruit/personal/resume'
 // import Confirm from '@/plugins/confirm'
 
 // import { useIMStore } from './im'
@@ -35,7 +36,8 @@ export const useUserStore = defineStore('user',
       baseInfo: localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {}, // 人才信息
       entBaseInfo: localStorage.getItem('entBaseInfo') ? JSON.parse(localStorage.getItem('entBaseInfo')) : {}, // 企业个人信息
       userAccount: {}, // 用户账户信息
-      enterpriseUserAccount: {} // 企业账户信息
+      enterpriseUserAccount: {}, // 企业账户信息
+      studentInfo: localStorage.getItem('studentInfo') ? JSON.parse(localStorage.getItem('studentInfo')) : {}, // 学生信息
     }),
     actions: {
       // 个人用户注册并登录
@@ -99,7 +101,6 @@ export const useUserStore = defineStore('user',
       // 获取当前登录账户信息
       async getUserInfos () {
         try {
-          // const api = getIsEnterprise() ? getEnterprisingUserInfo : getUserInfo
           const data = await getUserInfo({ id: this.accountInfo.userId })
           this.userInfo = data
           localStorage.setItem('userInfo', JSON.stringify(data))
@@ -111,17 +112,17 @@ export const useUserStore = defineStore('user',
       // 获取当前登录账户的基本信息(人才信息)
       async getUserBaseInfos (userId = null, option) {
         try {
-          // const api = getIsEnterprise() ? null : getBaseInfo
           let data = await getBaseInfo({ userId: userId || this.accountInfo.userId })
           data = data || {}
-          // if (!data) return localStorage.setItem('baseInfo', '{}')
           this.baseInfo = await this.getFieldText(data)
           localStorage.setItem('baseInfo', JSON.stringify(this.baseInfo))
           localStorage.setItem('necessaryInfoReady', checkPersonBaseInfo(this.baseInfo) ? 'ready' : 'fddeaddc47868b') // 校验是否完善人才必填信息
-          if (option?.chooseRole) {
-            // 刚注册时让用户选择学生用户还是求职者用户,角色不同填写的基本信息不同。
-            localStorage.setItem('chooseRole', 'showChooseRole')
-          }
+          // if (option?.chooseRole && import.meta.env.VITE_NODE_ENV !== 'production') {
+          //   // 刚注册时让用户选择学生用户还是求职者用户,角色不同填写的基本信息不同。
+          //   localStorage.setItem('chooseRole', 'showChooseRole')
+          // }
+          // 当前角色若为学生则获取学生信息
+          if (data?.type && Number(data.type) === 1) this.getStudentInformation()
         } catch (error) {
           Snackbar.error(error)
         }
@@ -188,7 +189,7 @@ export const useUserStore = defineStore('user',
         await this.updatePasswordCheck() // 检查密码是否需要修改
         await this.getEnterpriseInfo()
         await this.getEnterpriseUserAccountInfo()
-        
+
         Snackbar.success(res?.type === 'emailLogin' ? '登录成功' : '切换成功')
         let href = '/recruit/enterprise'
         // 是否存在重定向
@@ -205,7 +206,7 @@ export const useUserStore = defineStore('user',
       async getEnterpriseInfo (check) {
         const result = await getEnterprisingUserInfo()
         this.entBaseInfo = result
-        
+
         // 是否为企业账号管理员
         this.isAdmin = result.userType === '1'
         localStorage.setItem('isAdmin', result.userType)
@@ -232,19 +233,7 @@ export const useUserStore = defineStore('user',
           if (!data?.bizFlag) {  // 企业登录免费职位广告提示,除了true都弹窗
             localStorage.setItem('positionAd', 'showPositionAd')
           }
-          // // 检验必填信息
-          // const keyArr = ['industryId', 'financingStatus', 'scale', 'introduce', 'logoUrl'] // 必填信息列表
-          // let href = '/recruit/enterprise/systemManagement/entInfoSetting'
-          // const valid = Object.keys(data).length && keyArr.every(e => {
-          //   const bool = data[e] && data[e] !== 0
-          //   if (!bool && e === 'logoUrl') href = '/recruit/enterprise/systemManagement/entInfoSetting?tabKey=2'
-          //   return bool
-          // })
-          // if (!valid) {
-          //   localStorage.setItem('checkEnterpriseBaseInfoFalseHref', href)
-          // }
         } catch (error) {
-          // console.log(error)
         }
       },
 
@@ -270,6 +259,17 @@ export const useUserStore = defineStore('user',
           // 强制修改密码
           localStorage.setItem('entUpdatePassword', bool ? 'needChange' : 'doNotNeedChange')
         }
+      },
+
+      // 获取学生信息
+      async getStudentInformation () {
+        try {
+          const data = await getStudentInfo()
+          this.studentInfo = data
+          localStorage.setItem('studentInfo', data ? JSON.stringify(data) : '{}')
+        } catch (error) {
+          Snackbar.error(error.msg)
+        }
       }
     }
   },

+ 13 - 13
src/styles/index.css

@@ -2,7 +2,7 @@
 :root {
   --zIndex-dialog: 999;
   --default-bgc: #f2f4f7;
-  --v-primary-base: #00897B;
+  --v-primary-base: #00B760;
   --v-error-base: #fe574a;
   --v-primary-lighten1: #26A69A;
   --v-primary-lighten2: #4DB6AC;
@@ -23,14 +23,14 @@
   --zIndex-breadcrumbs: 999;
   --el-fill-color-blank: #ffffff00 !important;
   --el-input-text-color: #3b3b3b !important;
-  --el-color-primary: #00897B !important;
+  --el-color-primary: #00B760 !important;
   --el-border-color: #999 !important;
   --el-input-border: #333 !important;
   --el-border-color-hover: #333 !important;
   --el-text-color-regular: #333 !important;
   --el-fill-color-light: #f3f3f3 !important;
   --el-input-border: 1px solid #000 !important;
-  --el-color-info: #00897B !important;
+  --el-color-info: #00B760 !important;
   --el-cascader-tag-background: #e2f0ef !important;
 }
 
@@ -83,7 +83,7 @@
 }
 
 .color-primary {
-  color: #00897B;
+  color: #00B760;
 }
 
 .color-warning {
@@ -164,12 +164,12 @@
 }
 
 .defaultLink {
-  color: #008978;
+  color: #00B760;
   cursor: pointer;
 }
 
 .defaultLink-i {
-  color: #008978 !important;
+  color: #00B760 !important;
   cursor: pointer !important;
 }
 
@@ -230,7 +230,7 @@
 .resume-title {
   font-weight: 700;
   font-size: 18px;
-  border-left: 5px solid #00897B;
+  border-left: 5px solid #00B760;
   padding-left: 12px;
   line-height: 17px;
 }
@@ -256,10 +256,10 @@
 
 .home-title-line {
   width: 223px;
-  background: linear-gradient(to right, #12ebb0, #427daa);
+  background-color: #00B760;
   height: 9px;
   position: absolute;
-  top: 37px;
+  top: 45px;
   left: -50%;
   transform: translateX(5%);
 }
@@ -267,16 +267,16 @@
 .btnColor {
   color: #fff;
   opacity: .7;
-  background-color: #00887A !important;
+  background-color: #00B760 !important;
 }
 
 .commonHover:hover {
-  color: #00887A;
-  border-bottom: 2px solid #00887A;
+  color: #00B760;
+  border-bottom: 2px solid #00B760;
 }
 
 .commonHover2:hover {
-  color: #00887A;
+  color: #00B760;
   font-weight: 700;
 }
 

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
src/styles/index.min.css


+ 150 - 74
src/styles/index.scss

@@ -1,7 +1,8 @@
+@charset "UTF-8";
 :root {
   --zIndex-dialog: 999;
   --default-bgc: #f2f4f7;
-  --v-primary-base: #00897B;
+  --v-primary-base: #00B760;
   --v-error-base: #fe574a;
   --v-primary-lighten1: #26A69A;
   --v-primary-lighten2: #4DB6AC;
@@ -20,65 +21,141 @@
   --color-f2f4f7: #f2f4f7;
   --color-d5e6e8: #d5e6e8;
   --zIndex-breadcrumbs: 999;
-  // el
-  // --el-component-size: 40px !important;
   --el-fill-color-blank: #ffffff00 !important;
   --el-input-text-color: #3b3b3b !important;
-  --el-color-primary: #00897B !important;
+  --el-color-primary: #00B760 !important;
   --el-border-color: #999 !important;
   --el-input-border: #333 !important;
   --el-border-color-hover: #333 !important;
   --el-text-color-regular: #333 !important;
   --el-fill-color-light: #f3f3f3 !important;
   --el-input-border: 1px solid #000 !important;
-  --el-color-info: #00897B !important;
+  --el-color-info: #00B760 !important;
   --el-cascader-tag-background: #e2f0ef !important;
 }
 
-.color-222 { color: #222; }
-.color-333 { color: #333; }
-.color-666 { color: #666; }
-.color-777 { color: #777; }
-.color-999 { color: #999; }
-.color-ccc { color: #ccc; }
-.color-f3f3f3 { color: #f3f3f3; }
-.color-f2f4f742 { color: #f2f4f742; }
-.color-f8f8f8 { color: #f8f8f8; }
-.color-f2f4f7 { color: #f2f4f7; }
-.color-d5e6e8 { color: #d5e6e8; }
-.color-error { color: #fe574a; }
-.color-primary { color: #00897B; }
-.color-warning { color: #fb8c00; }
-.color-success { color: #4caf50; }
-.color-white { color: #fff; }
-
-.font-size-12 { font-size: 12px; }
-.font-size-13 { font-size: 13px; }
-.font-size-14 { font-size: 14px; }
-.font-size-15 { font-size: 15px; }
-.font-size-16 { font-size: 16px; }
-.font-size-17 { font-size: 17px; }
-.font-size-18 { font-size: 18px; }
-.font-size-19 { font-size: 19px; }
-.font-size-20 { font-size: 20px; }
-
-.border-bottom-primary { border-bottom: 1px solid var(--v-primary-base); }
-.border-bottom-error { border-bottom: 1px solid var(--v-error-base); }
-.border-bottom-warning { border-bottom: 1px solid var(--v-warning-base); }
-
-// 长按钮
+.color-222 {
+  color: #222;
+}
+
+.color-333 {
+  color: #333;
+}
+
+.color-666 {
+  color: #666;
+}
+
+.color-777 {
+  color: #777;
+}
+
+.color-999 {
+  color: #999;
+}
+
+.color-ccc {
+  color: #ccc;
+}
+
+.color-f3f3f3 {
+  color: #f3f3f3;
+}
+
+.color-f2f4f742 {
+  color: #f2f4f742;
+}
+
+.color-f8f8f8 {
+  color: #f8f8f8;
+}
+
+.color-f2f4f7 {
+  color: #f2f4f7;
+}
+
+.color-d5e6e8 {
+  color: #d5e6e8;
+}
+
+.color-error {
+  color: #fe574a;
+}
+
+.color-primary {
+  color: #00B760;
+}
+
+.color-warning {
+  color: #fb8c00;
+}
+
+.color-success {
+  color: #4caf50;
+}
+
+.color-white {
+  color: #fff;
+}
+
+.font-size-12 {
+  font-size: 12px;
+}
+
+.font-size-13 {
+  font-size: 13px;
+}
+
+.font-size-14 {
+  font-size: 14px;
+}
+
+.font-size-15 {
+  font-size: 15px;
+}
+
+.font-size-16 {
+  font-size: 16px;
+}
+
+.font-size-17 {
+  font-size: 17px;
+}
+
+.font-size-18 {
+  font-size: 18px;
+}
+
+.font-size-19 {
+  font-size: 19px;
+}
+
+.font-size-20 {
+  font-size: 20px;
+}
+
+.border-bottom-primary {
+  border-bottom: 1px solid var(--v-primary-base);
+}
+
+.border-bottom-error {
+  border-bottom: 1px solid var(--v-error-base);
+}
+
+.border-bottom-warning {
+  border-bottom: 1px solid var(--v-warning-base);
+}
+
 .buttons {
   height: 36px;
   width: 224px;
 }
 
-// 短按钮
 .half-button {
   height: 36px;
   width: 88px;
 }
 
-// 个人端-默认宽度
 .default-width {
   width: 1184px;
   min-width: 1184px;
@@ -87,17 +164,15 @@
 }
 
 .defaultLink {
-  // color:#5aabff;
-  color:#008978;
-  cursor:pointer;
-  // text-decoration: underline;
+  color: #00B760;
+  cursor: pointer;
 }
+
 .defaultLink-i {
-  color:#008978 !important;
-  cursor:pointer !important;
+  color: #00B760 !important;
+  cursor: pointer !important;
 }
 
-// 高亮
 .default-active {
   color: var(--v-primary-base) !important;
 }
@@ -119,7 +194,6 @@
   overflow: hidden;
 }
 
-// 超出2行显示省略号
 .ellipsis-2 {
   display: -webkit-box;
   -webkit-box-orient: vertical;
@@ -127,7 +201,6 @@
   overflow: hidden;
 }
 
-// 分隔线
 .septal-line {
   display: inline-block;
   width: 1px;
@@ -137,7 +210,6 @@
   margin: 0 10px;
 }
 
-// 居中
 .absolute-center {
   position: absolute;
   top: 50%;
@@ -145,7 +217,6 @@
   transform: translate(-50%, -50%);
 }
 
-// 个人简历
 .resume-box {
   border-radius: 5px;
   padding: 20px 30px;
@@ -162,7 +233,7 @@
 .resume-title {
   font-weight: 700;
   font-size: 18px;
-  border-left: 5px solid #00897B;
+  border-left: 5px solid #00B760;
   padding-left: 12px;
   line-height: 17px;
 }
@@ -174,7 +245,6 @@
 
 .card-box {
   min-width: calc(100vw - 272px);
-  // width: 100%;
   height: 100%;
   min-height: 70vh;
 }
@@ -182,38 +252,38 @@
 .home-title {
   font-size: 30px;
   font-weight: 600;
-  color: rgb(51, 51, 51);
+  color: #333333;
   margin: 0 15px;
   position: relative;
-  &-line {
-    width: 223px;
-    background: linear-gradient(to right, rgb(18, 235, 176), rgb(66, 125, 170));
-    height: 9px;
-    position: absolute;
-    top: 37px;
-    left: -50%;
-    transform: translateX(5%);
-  }
+}
+
+.home-title-line {
+  width: 223px;
+  // background: linear-gradient(to right, #12ebb0, #427daa);
+  background-color: #00B760;
+  height: 9px;
+  position: absolute;
+  top: 45px;
+  left: -50%;
+  transform: translateX(5%);
 }
 
 .btnColor {
   color: #fff;
   opacity: .7;
-  background-color: #00887A !important;
+  background-color: #00B760 !important;
 }
 
 .commonHover:hover {
-  color: #00887A;
-  // font-weight: 700;
-  // text-decoration: underline;
-  border-bottom: 2px solid #00887A;
+  color: #00B760;
+  border-bottom: 2px solid #00B760;
 }
+
 .commonHover2:hover {
-  color: #00887A;
+  color: #00B760;
   font-weight: 700;
 }
 
-// 已关闭职位遮罩层
 .close-position::after {
   content: '';
   position: absolute;
@@ -232,18 +302,24 @@
 
 /* 均匀间隔显示的文本 */
 .justified-text {
-  width: 70px; /* 设定固定长度 */
-  text-align: justify; /* 使文本两端对齐 */
-  text-justify: inter-word; /* 为了兼容性 */
+  width: 70px;
+  /* 设定固定长度 */
+  text-align: justify;
+  /* 使文本两端对齐 */
+  text-justify: inter-word;
+  /* 为了兼容性 */
   height: 100%;
   line-height: 100%;
 }
+
 .justified-text:after {
   content: '';
   display: inline-block;
-  width: 100%; /* 设定额外间隔 */
+  width: 100%;
+  /* 设定额外间隔 */
 }
+
 .l-s-10 {
   letter-spacing: 10px;
   margin-right: -10px;
-}
+}

+ 5 - 5
src/styles/personal/navBar.css

@@ -132,15 +132,15 @@
 }
 
 .routeActive .aLink {
-  color: #008979 !important;
+  color: #00B760 !important;
   font-weight: 700;
   text-decoration: underline !important;
 }
 
 .active-route {
-  color: #008979;
+  color: #00B760;
   font-weight: 700;
-  border-bottom: 2px solid #008979;
+  border-bottom: 2px solid #00B760;
 }
 
 .menuList {
@@ -154,7 +154,7 @@
 }
 
 .menuList-first-title:hover {
-  color: #008979;
+  color: #00B760;
   font-weight: 700;
-  border-bottom: 2px solid #008979;
+  border-bottom: 2px solid #00B760;
 }

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
src/styles/personal/navBar.min.css


+ 5 - 5
src/styles/personal/navBar.scss

@@ -121,15 +121,15 @@
 .routeActive { // 选中样式
   color: var(--color-333) !important;
   .aLink {
-    color: #008979 !important;
+    color: #00B760 !important;
     font-weight: 700;
     text-decoration: underline !important; // 多个下划线
   }
 }
 .active-route {
-  color: #008979;
+  color: #00B760;
   font-weight: 700;
-  border-bottom: 2px solid #008979;
+  border-bottom: 2px solid #00B760;
 }
 
 .menuList {
@@ -141,7 +141,7 @@
   }
 }
 .menuList-first-title:hover {
-  color: #008979;
+  color: #00B760;
   font-weight: 700;
-  border-bottom: 2px solid #008979;
+  border-bottom: 2px solid #00B760;
 }

+ 15 - 5
src/styles/protocol/index.css

@@ -52,7 +52,9 @@ del, ins, u, s, a, a:hover {
 
 .conter, .conttle {
   width: 100%;
-  overflow: hidden;
+  overflow-x: hidden;
+  height: 100vh;
+  background-color: var(--default-bgc);
 }
 
 .large {
@@ -63,13 +65,22 @@ del, ins, u, s, a, a:hover {
 
 .Protocol {
   width: 1040px;
-  margin: 0 auto;
+  margin: 60px auto;
   background-color: #fff;
   padding: 40px 40px 90px;
   box-sizing: border-box;
   font-family: "微软雅黑";
 }
 
+.mobileProtocol {
+  width: 100%;
+  margin: 0 auto;
+  background-color: #fff;
+  padding: 5% 5% 15px;
+  box-sizing: border-box;
+  font-family: "微软雅黑";
+}
+
 .Protocol p {
   color: var(--color-333);
   font-size: 14px;
@@ -80,7 +91,6 @@ del, ins, u, s, a, a:hover {
   width: 100%;
   font-size: 22px;
   color: var(--color-333);
-  margin-top: 45px;
   margin-bottom: 32px;
   font-weight: 700;
   text-align: center;
@@ -100,7 +110,7 @@ del, ins, u, s, a, a:hover {
   margin-top: 0px;
   margin-bottom: 0px;
   padding: 0px;
-  color: var(--color-333);
+  color: #333333;
   font-size: 14px;
   line-height: 30px;
   font-family: 微软雅黑;
@@ -115,7 +125,7 @@ del, ins, u, s, a, a:hover {
   margin: 0px;
   padding: 0px;
   font-size: 14px;
-  color: #00897B;
+  color: #00B760;
   line-height: 30px;
   font-family: 微软雅黑;
   white-space: normal;

+ 1 - 1
src/styles/protocol/index.min.css

@@ -1 +1 @@
-html,body,h1,h2,h3,h4,h5,h6,div,dl,dt,dd,ul,ol,li,p,blockquote,pre,hr,figure,table,caption,th,td,form,fieldset,legend,input,button,textarea,menu{margin:0;padding:0}header,footer,section,article,aside,nav,hgroup,address,figure,figcaption,menu,details{display:block}table{border-collapse:collapse;border-spacing:0}caption,th{text-align:left;font-weight:normal}html,body,fieldset,img,iframe,abbr{border:0}i,cite,em,var,address,dfn{font-style:normal}[hidefocus],summary{outline:0}li{list-style:none}h1,h2,h3,h4,h5,h6,small{font-size:100%}a,button{cursor:pointer}h1,h2,h3,h4,h5,h6,em,strong,b{font-weight:bold}del,ins,u,s,a,a:hover{text-decoration:none}.conter,.conttle{width:100%;overflow:hidden}.large{width:100%;height:1px;background-color:var(--v-primary-base)}.Protocol{width:1040px;margin:0 auto;background-color:#fff;padding:40px 40px 90px;box-sizing:border-box;font-family:"微软雅黑"}.Protocol p{color:var(--color-333);font-size:14px;line-height:30px}.segment{width:100%;font-size:22px;color:var(--color-333);margin-top:45px;margin-bottom:32px;font-weight:700;text-align:center}.subtitle,.crosshead{color:var(--v-primary-base);font-size:14px;line-height:30px}.crosshead{color:var(--color-333)}.text-color{margin-top:0px;margin-bottom:0px;padding:0px;color:#333;font-size:14px;line-height:30px;font-family:微软雅黑;white-space:normal}.text-font{font-family:宋体, SimSun}.text-size{margin:0px;padding:0px;font-size:14px;color:#00897B;line-height:30px;font-family:微软雅黑;white-space:normal}.text-p{text-align:justify;font-family:Calibri;font-size:14px;white-space:normal}
+html,body,h1,h2,h3,h4,h5,h6,div,dl,dt,dd,ul,ol,li,p,blockquote,pre,hr,figure,table,caption,th,td,form,fieldset,legend,input,button,textarea,menu{margin:0;padding:0}header,footer,section,article,aside,nav,hgroup,address,figure,figcaption,menu,details{display:block}table{border-collapse:collapse;border-spacing:0}caption,th{text-align:left;font-weight:normal}html,body,fieldset,img,iframe,abbr{border:0}i,cite,em,var,address,dfn{font-style:normal}[hidefocus],summary{outline:0}li{list-style:none}h1,h2,h3,h4,h5,h6,small{font-size:100%}a,button{cursor:pointer}h1,h2,h3,h4,h5,h6,em,strong,b{font-weight:bold}del,ins,u,s,a,a:hover{text-decoration:none}.conter,.conttle{width:100%;overflow-x:hidden;height:100vh;background-color:var(--default-bgc)}.large{width:100%;height:1px;background-color:var(--v-primary-base)}.Protocol{width:1040px;margin:60px auto;background-color:#fff;padding:40px 40px 90px;box-sizing:border-box;font-family:"微软雅黑"}.mobileProtocol{width:100%;margin:0 auto;background-color:#fff;padding:5% 5% 15px;box-sizing:border-box;font-family:"微软雅黑"}.Protocol p{color:var(--color-333);font-size:14px;line-height:30px}.segment{width:100%;font-size:22px;color:var(--color-333);margin-bottom:32px;font-weight:700;text-align:center}.subtitle,.crosshead{color:var(--v-primary-base);font-size:14px;line-height:30px}.crosshead{color:var(--color-333)}.text-color{margin-top:0px;margin-bottom:0px;padding:0px;color:#333;font-size:14px;line-height:30px;font-family:微软雅黑;white-space:normal}.text-font{font-family:宋体, SimSun}.text-size{margin:0px;padding:0px;font-size:14px;color:#00B760;line-height:30px;font-family:微软雅黑;white-space:normal}.text-p{text-align:justify;font-family:Calibri;font-size:14px;white-space:normal}

+ 1 - 1
src/styles/protocol/index.scss

@@ -120,7 +120,7 @@ del,ins,u,s,a,a:hover {
   margin: 0px; 
   padding: 0px; 
   font-size: 14px; 
-  color: #00897B; 
+  color: #00B760; 
   line-height: 30px; 
   font-family: 微软雅黑; 
   white-space: normal;

+ 2 - 2
src/styles/recruit/company.css

@@ -68,7 +68,7 @@
   height: 22px;
   font-size: 18px;
   font-weight: 700;
-  color: var(--color-333);
+  color: #0E100F;
   line-height: 22px;
   margin: 0 0 4px 0;
   padding: 0;
@@ -103,7 +103,7 @@
 .salary {
   font-size: 16px;
   float: right;
-  color: var(--v-error-base);
+  color: var(--v-primary-base);
   line-height: 22px;
   max-width: none;
   text-align: right;

+ 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: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}
+.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:#0E100F;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-primary-base);line-height:22px;max-width:none;text-align:right;flex:1}

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

@@ -63,7 +63,7 @@
   height: 22px;
   font-size: 18px;
   font-weight: 700;
-  color: var(--color-333);
+  color: #0E100F;
   line-height: 22px;
   margin: 0 0 4px 0;
   padding: 0;
@@ -94,9 +94,9 @@
 .salary {
   font-size: 16px;
   float: right;
-  color: var(--v-error-base);
+  color: var(--v-primary-base);
   line-height: 22px;
   max-width: none;
   text-align: right;
   flex: 1;
-}
+}

+ 2 - 2
src/styles/recruit/position/index.css

@@ -11,7 +11,7 @@
 
 .banner-title h1 {
   display: inline-block;
-  color: #37576c;
+  color: #0E100F;
   font-size: 28px;
   margin-top: 1px;
   vertical-align: middle;
@@ -24,7 +24,7 @@
 
 .salary {
   min-width: 100px;
-  color: var(--v-error-base);
+  color: var(--v-primary-base);
   line-height: 41px;
   font-weight: 600;
   height: auto;

+ 1 - 1
src/styles/recruit/position/index.min.css

@@ -1 +1 @@
-.banner{background-color:#fff;padding:18px 0 20px}.banner-title{line-height:40px;font-size:28px;font-weight:600}.banner-title h1{display:inline-block;color:#37576c;font-size:28px;margin-top:1px;vertical-align:middle}.button-item{min-width:110px;height:36px}.salary{min-width:100px;color:var(--v-error-base);line-height:41px;font-weight:600;height:auto;display:inline-block;vertical-align:sub}.refresh-time{color:var(--color-666);font-size:14px;text-align:right;line-height:32px}.banner-tags span{font-weight:600}.radius{border-radius:8px}.content-left{width:810px;padding:20px 20px}.content-right{flex:1;padding:20px 20px 20px 0}.label-text{color:#7f7a7a;font-weight:600}.value-text{color:#000;font-weight:400}.requirement{white-space:pre-wrap;word-break:break-all;line-height:28px;color:var(--color-333);font-size:15px;text-align:justify;letter-spacing:0}.contact{height:60px;line-height:60px}.contact-name{font-size:20px;font-weight:500;color:var(--color-222);line-height:28px}.contact-info{font-size:15px;color:var(--color-666);line-height:21px;margin-top:8px}
+.banner{background-color:#fff;padding:18px 0 20px}.banner-title{line-height:40px;font-size:28px;font-weight:600}.banner-title h1{display:inline-block;color:#0E100F;font-size:28px;margin-right:30px;margin-top:1px;vertical-align:middle}.button-item{min-width:110px;height:36px}.salary{min-width:100px;color:var(--v-primary-base);line-height:41px;font-weight:600;height:auto;display:inline-block;vertical-align:sub}.refresh-time{color:var(--color-666);font-size:14px;text-align:right;line-height:32px}.banner-tags span{font-weight:600}.radius{border-radius:8px}.content-left{width:810px;padding:20px 20px}.content-right{flex:1;padding:20px 20px 20px 0}.label-text{color:#7f7a7a;font-weight:600}.value-text{color:#000;font-weight:400}.requirement{white-space:pre-wrap;word-break:break-all;line-height:28px;color:var(--color-333);font-size:15px;text-align:justify;letter-spacing:0}.contact{height:60px;line-height:60px}.contact-name{font-size:20px;font-weight:500;color:var(--color-222);line-height:28px}.contact-info{font-size:15px;color:var(--color-666);line-height:21px;margin-top:8px}

+ 2 - 2
src/styles/recruit/position/index.scss

@@ -9,7 +9,7 @@
 }
 .banner-title h1 {
   display: inline-block;
-  color: #37576c;
+  color: #0E100F;
   font-size: 28px;
   // margin-right: 30px;
   margin-top: 1px;
@@ -22,7 +22,7 @@
 }
 .salary {
   min-width: 100px;
-  color: var(--v-error-base);
+  color: var(--v-primary-base);
   line-height: 41px;
   font-weight: 600;
   height: auto;

+ 2 - 2
src/utils/check.js

@@ -2,7 +2,7 @@
 export const checkPersonBaseInfo = (baseInfo) => {
   const info = baseInfo ? baseInfo : localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {}
   if (!info || !Object.keys(info).length) return false
-  if (info.type && Number(info.type) === 1) return true // 0是求职者,1是学生
+  if (info.type === '1') return true // 学生信息管理。 type:'0'是求职者,'1'是学生
   const keyArr = [ // 必填项目
     'name',
     'sex',
@@ -16,4 +16,4 @@ export const checkPersonBaseInfo = (baseInfo) => {
   const necessaryInfoReady = keyArr.every(e => info[e] && info[e] !== 0)
   return necessaryInfoReady
   // return true // return true关闭弹窗
-}
+}

+ 1 - 1
src/utils/position.js

@@ -128,4 +128,4 @@ export const jumpToEnterpriseDetail = async (id, isOpenWindow = false, tabKey =
   } else {
     router.push({ path: url })
   }
-}
+}

+ 1 - 1
src/utils/whiteList.js

@@ -26,4 +26,4 @@ export const showImprovePersonaInfo = (url) => {
     // '/recruit/entRegister/inReview',
   ]
   return checkWhiteListFun(url, list)
-}
+}

+ 1 - 1
src/version.js

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

+ 1 - 1
src/views/entrances/navBar/index.vue

@@ -7,7 +7,7 @@
     <div class="innerBox d-flex justify-space-between">
       <div class="d-flex align-center">
         <div class="nav-logo" style="cursor: pointer;" @click="handleLogoClick">
-          <v-img src="@/assets/logo.png"  aspect-ratio="16/9" cover :width="90" style="height: 40px;"></v-img>
+          <v-img src="@/assets/logo.png"  aspect-ratio="16/9" contain :width="90" style="height: 40px;"></v-img>
         </div>
         <div class="menuList ml-10">
           <div v-for="val in menuList" :key="val.name">

+ 1 - 1
src/views/headhunting/components/nav.vue

@@ -88,7 +88,7 @@ const handleSubmit = async () => {
   Curtain('message', {
     message: '提交成功,我们会尽快与您联系',
     name: 'submit',
-    color: '#00897B',
+    color: '#00B760',
     iconFontSize: 300
   })
 }

+ 1 - 1
src/views/integral/pointsManagement/components/integralShow.vue

@@ -98,7 +98,7 @@ const handleClickMall = () => {
 }
 .item-value {
   font-size: 42px; 
-  color: #10897bba;
+  color: #00B760;
   line-height: 50px;
 }
 .rules {

+ 1 - 1
src/views/mall/components/prizeDraw/grid.vue

@@ -28,7 +28,7 @@ const props = defineProps({ lotteryId: [Number, String], disabled: Boolean })
 const myLucky = ref()
 // 背景样式
 const blocks = [
-  { padding: '10px', background: '#00897B', borderRadius: 6 }
+  { padding: '10px', background: '#00B760', borderRadius: 6 }
 ]
 const prizes = ref([
   { x: 0, y: 0, imgs: [] },

+ 1 - 1
src/views/mall/components/prizeDraw/slotMachine.vue

@@ -38,7 +38,7 @@ const props = defineProps({ lotteryId: [Number, String], height: [Number, String
 const myLucky = ref()
 // 背景样式
 const blocks = [
-  { padding: '10px', background: '#00897B', borderRadius: 6 },
+  { padding: '10px', background: '#00B760', borderRadius: 6 },
 ]
 // 转速与方向
 const slots = [

+ 1 - 1
src/views/mallCopy/index.vue

@@ -11,7 +11,7 @@
           </span>
         </div>
         <div class="d-flex justify-space-between align-end my-1">
-          <span v-if="getToken()" style="font-size: 42px; color: #10897bba; line-height: 50px;" class="ml-10 cursor-pointer" @click="router.push('/recruit/personal/personalCenter/wallet?key=1')">{{ accountData.point }}</span>
+          <span v-if="getToken()" style="font-size: 42px; color: #00B760; line-height: 50px;" class="ml-10 cursor-pointer" @click="router.push('/recruit/personal/personalCenter/wallet?key=1')">{{ accountData.point }}</span>
           <span v-else style="font-size: 30px; line-height: 50px;" class="ml-10 cursor-pointer color-primary" @click="handleLogin">请先登录</span>
         </div>
         <div v-if="getToken()" class="color-666 font-size-14 cursor-pointer text-end mr-5 commonHover2" @click="handleToTaskCenter">赚取积分</div>

+ 84 - 0
src/views/recruit/components/contactUs/index.vue

@@ -0,0 +1,84 @@
+<template>
+  <v-card class="card-box px-5" :class="{'mt-16': route.path === '/contactUs', 'mx-3': route.path === '/contactUs'}" style="padding-top: 115px;">
+		<div class="social-media common-width">
+			<h5 class="social-media-header">
+				<span class="inner-text">联系我们</span>
+			</h5>
+			<div class="d-flex justify-space-between">
+				<div class="left">
+					<h2>欢迎关注,了解门墩儿的新动态。</h2>
+					<p class="mt-3" style="font-size: .9rem;">门墩儿在所有主流社交媒体都有账号。欢迎关注我们,以便了解关于发展趋势和领导力问题的最新见解。</p>
+					<div class="d-flex align-center social-contact pa-8 mt-5 justify-space-between">
+						<div class="ml-3">
+							<p>联系方式</p>
+							<p  class="mt-3 mb-2">手&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;机:18621329797</p>
+							<p>邮&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;箱:peter.pan@menduner.com</p>
+						</div>
+						<div class="mr-10">
+							<div style="width: 120px; height: 120px;">
+								<v-img cover aspect-ratio="1/1" src="https://minio.menduner.com/dev/menduner/contact.png" width="120" height="120"></v-img>
+							</div>
+							<div class="font-size-15 mt-2">潘青海 Peter Pan</div>
+						</div>
+					</div>
+				</div>
+        <v-card elevation="3" width="300" height="304" class="d-flex flex-column align-center pt-4">
+          <div style="width: 200px; height: 200px;">
+            <v-img src="https://minio.citupro.com/dev/menduner/official-account-code4.jpg" width="200" height="200"></v-img>
+          </div>
+          <p>关注"门墩儿"微信公众号</p>
+          <p>了解更多行业资讯</p>
+        </v-card>
+			</div>
+		</div>
+	</v-card>
+</template>
+
+<script setup>
+defineOptions({ name: 'contactUs'})
+import { useRoute } from 'vue-router'
+
+const route = useRoute()
+</script>
+
+<style scoped lang="scss">
+.common-width {
+  width: 100%;
+  max-width: 1100px;
+}
+.social-media {
+  margin: auto;
+  padding-bottom: 100px;
+  &-header {
+    position: relative;
+    text-transform: uppercase;
+    text-align: center;
+    margin: 0 0 52px;
+    .inner-text {
+      display: inline-block;
+      background-color: #fff;
+      padding: 0 18px;
+      color: #4c4c4e;
+      position: relative;
+      z-index: 2;
+    }
+    &::after {
+      content: "";
+      position: absolute;
+      width: 100%;
+      height: 0;
+      border-top: 1px dotted #747476;
+      left: 0;
+      bottom: 50%;
+      margin-bottom: 2px;
+    }
+  }
+  .left {
+    width: 63%;
+    color: #4c4c4e;
+    .social-contact {
+      background-color: #e8f0f7;
+    }
+  }
+}
+</style>

+ 39 - 20
src/views/recruit/components/jobFairEntShare/index.vue

@@ -23,9 +23,10 @@ const props = defineProps({
 	enterpriseName: String,
 	logoUrl: String,
 	positionList: Array,
-	jobFairId: String,
-	enterpriseId: String,
-	bgImg: String
+	jobFairId: [String, Number],
+	enterpriseId: [String, Number],
+	bgImg: String,
+  backgroundColor: String
 })
 
 const loading = ref(false)
@@ -66,8 +67,8 @@ const drawCanvas = () => {
       thirdImg.onload = () => {
         // 企业头像
         ctx.save()
-        const secondImgWidth = 80
-        const secondImgHeight = 80
+        const secondImgWidth = 120
+        const secondImgHeight = 120
         const x = (canvas.width - secondImgWidth) / 2
         const y = canvas.height - secondImgHeight - 460
         
@@ -80,7 +81,7 @@ const drawCanvas = () => {
         // 企业名称
         const maxTextWidth = 400
         const text = props.enterpriseName
-        const fontStyle = 'bold 18px Arial'
+        const fontStyle = 'bold 24px Arial'
         ctx.font = fontStyle
         let truncatedText = text
         while (ctx.measureText(truncatedText + '...').width > maxTextWidth && truncatedText.length > 0) {
@@ -96,10 +97,12 @@ const drawCanvas = () => {
         const tagPaddingLeftRight = 20
         const tagPaddingTopBottom = 10
         const tagRadius = 8
-        const tagSpacing = 30
-        let tagY = textY + tagSpacing
+        const tagSpacing = 22
+        let tagY = textY + tagSpacing + 10
         props.positionList.forEach((tag) => {
           let truncatedTag = tag
+          ctx.font = '18px Arial' // 绘制前设置字体,否则tagWidth受前面的font fontStyle影响
+          
           while (ctx.measureText(truncatedTag + '...').width > maxTextWidth - 2 * tagPaddingLeftRight && truncatedTag.length > 0) {
             truncatedTag = truncatedTag.slice(0, -1)
           }
@@ -108,7 +111,7 @@ const drawCanvas = () => {
           const tagWidth = ctx.measureText(truncatedTag).width + 2 * tagPaddingLeftRight
           const tagX = x + (secondImgWidth - tagWidth) / 2
 
-          ctx.fillStyle = '#246a6c'
+          ctx.fillStyle = '#00B760'
           ctx.beginPath()
           ctx.moveTo(tagX + tagRadius, tagY)
           ctx.lineTo(tagX + tagWidth - tagRadius, tagY)
@@ -122,7 +125,6 @@ const drawCanvas = () => {
           ctx.closePath()
           ctx.fill()
 
-          ctx.font = '16px Arial'
           ctx.fillStyle = '#fff'
           ctx.fillText(truncatedTag, tagX + tagPaddingLeftRight, tagY + tagPaddingTopBottom + 10)
 
@@ -137,17 +139,34 @@ const drawCanvas = () => {
     }
     
     // 保存分享参数
-    const result = await saveShareQuery({ id: '14304' })
-    const query = {
-      scene: 'id=' + result,
-      path: 'pagesB/positionDetail/index',
-      width: 200,
-      autoColor: false,
-      checkPath: true,
-      hyaline: false
+    const params = {
+      jobFairId: props.jobFairId,
+      enterpriseId: props.enterpriseId,
+      entName: props.enterpriseName,
+      backgroundColor: props.backgroundColor
     }
-    const data = await getJobAdvertisedShareQrcode(query)
-    secondImg.src = 'data:image/png;base64,' + data
+    try {
+      const result = await saveShareQuery(params)
+      const query = {
+        scene: 'id=' + result,
+        path: 'pagesB/jobFair/positionClassification',
+        width: 200,
+        autoColor: false,
+        checkPath: true,
+        hyaline: false
+      }
+
+      try {
+        const data = await getJobAdvertisedShareQrcode(query)
+        secondImg.src = 'data:image/png;base64,' + data 
+      } catch {
+        loading.value = false
+      }
+      
+    } catch {
+      loading.value = false
+    }
+    
   }
 
   img.onerror = (error) => {

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

@@ -27,10 +27,10 @@
     <div class="py-3 px-7" v-if="interview.length && info.channel?.channelID !== 'system'">
       <div v-for="val in interview" :key="val.id" class="color-666">
         <div class="d-flex justify-space-between">
-          <div class="font-weight-bold color-primary">
-            <span>{{ formatName(val.job.name) }}</span>
-            <span v-if="!val.job.payFrom && !val.job.payTo" class="ml-3">面议</span>
-            <span v-else class="ml-3">{{ val.job.payFrom ? val.job.payFrom + '-' : '' }}{{ val.job.payTo }}</span>
+          <div class="font-weight-bold">
+            <span style="color: #0E100F;">{{ formatName(val.job.name) }}</span>
+            <span v-if="!val.job.payFrom && !val.job.payTo" class="ml-3 color-primary">面议</span>
+            <span v-else class="ml-3 color-primary">{{ val.job.payFrom ? val.job.payFrom + '-' : '' }}{{ val.job.payTo }}</span>
           </div>
           <div :style="{'color': ['5', '98', '99'].includes(val.status) ? 'var(--v-error-base)' : 'var(--v-primary-base)'}">{{ statusList.find(e => e.value === val.status)?.label }}</div>
         </div>

+ 153 - 40
src/views/recruit/components/message/index.vue

@@ -45,7 +45,7 @@
               <template v-slot:title="{ title }">
                 <div v-if="!isEnterprise" class="mt-2 d-flex align-center">
                   {{ title }}
-                  <div class="ml-3 color-666 font-size-14 enterprise-name ellipsis" v-ellipse-tooltip :style="{'color': val.channel.channelID === info?.channel?.channelID ? '#00897B' : '#666'}">
+                  <div class="ml-3 color-666 font-size-14 enterprise-name ellipsis" v-ellipse-tooltip :style="{'color': val.channel.channelID === info?.channel?.channelID ? '#00B760' : '#666'}">
                     {{ formatName(val.userInfoVo?.userInfoResp?.enterpriseAnotherName) }}
                     <span class="line" v-if="val.userInfoVo?.userInfoResp?.postNameCn && val.userInfoVo?.userInfoResp?.enterpriseAnotherName"></span>
                     {{ val.userInfoVo?.userInfoResp?.postNameCn }}
@@ -135,13 +135,15 @@
     @close="showUploadDialog = false"
     @submit="handleSubmitAttachment"
   >
+    <div class="color-warning mb-3" style="font-size: 13px;">* 仅支持.doc, .docx, .pdf文件且大小不能超过20MB</div>
     <CtForm ref="CtFormRef" :items="formItems">
       <template #uploadFile="{ item }">
         <TextInput v-model="item.value" :item="item" @click="openFileInput"></TextInput>
         <File ref="uploadFile" @success="handleUploadResume"></File>
       </template>
     </CtForm>
-    <div class="color-666" style="font-size: 13px;">* 仅支持.doc, .docx, .pdf文件且大小不能超过20MB</div>
+    <!-- 学生-实习到岗信息 -->
+    <studentDeliveryForm v-if="baseInfo?.type && Number(baseInfo.type) === 1" ref="studentDeliveryFormRef" />
   </CtDialog>
 
   <!-- 面试邀请 -->
@@ -156,10 +158,28 @@
   <CtDialog :visible="showSelectPosition" :widthType="2" titleClass="text-h6" title="选择要求简历的职位" @close="showSelectPosition = false" @submit="handleRequestResumeSubmit">
     <CtForm v-if="showSelectPosition" ref="requestFromRef" :items="requestFormItems"></CtForm>
   </CtDialog>
+  <!-- 发送简历-选择要发送的职位 -->
+  <CtDialog :visible="openPositionSelectDialog" :widthType="2" titleClass="text-h6" title="请选择要投递的职位" @close="openPositionSelectDialog = false" @submit="selectPositionSubmit">
+    <div style="position: relative; min-height: 200px">
+      <v-radio-group v-model="selectJobId">
+        <div v-for="val in rightEntPositionList" :key="val.value" class="d-flex align-center radioBox" >
+          <v-radio :label="val.label" :value="val.value"  color="primary"></v-radio>
+          <span class="defaultLink mx-3" style="font-size: 14px;" @click.stop="positionDetail(val)">预览</span>
+        </div>
+      </v-radio-group>
+    </div>
+    <v-btn
+      variant="text"
+      color="primary"
+      @click="changePositionData"
+    >
+      {{ positionListIsEnd ? '没有更多职位了~ 再选一遍' : '换一批'}} <v-icon size="16">mdi-refresh</v-icon>
+    </v-btn>
+  </CtDialog>
 
   <!-- 选择附件简历投递 -->
   <CtDialog :visible="showResume" :widthType="2" titleClass="text-h6" title="发送简历" @close="showResume = false; selectResume = null; enRequestPositionInfo = {}" @submit="handleSubmitResume">
-    <div style="position: relative; min-height: 200px">
+    <div style="position: relative;">
       <v-radio-group v-model="selectResume">
         <div v-for="val in resumeList" :key="val.id" class="d-flex align-center radioBox">
           <v-radio :label="val.title" :value="val.id"  color="primary"></v-radio>
@@ -167,7 +187,9 @@
         </div>
       </v-radio-group>
     </div>
+    <studentDeliveryForm v-if="isStudent" ref="studentDeliveryFormRef" />
   </CtDialog>
+	<Loading :visible="pageLoading" />
 </template>
 
 <script setup>
@@ -180,7 +202,7 @@ import { useRoute } from 'vue-router'
 import Chatting from './components/chatting.vue'
 import { initConnect, send, initChart, getMoreMessages, checkConversation } from '@/hooks/web/useIM'
 import { useI18n } from '@/hooks/web/useI18n'
-import { getPositionDetails, jobCvRelCheckSend, jobCvRelSend, jobCvRelHireSend } from '@/api/position'
+import { getPositionDetails, jobCvRelCheckSend, jobCvRelSend, jobCvRelHireSend, getJobAdvertisedSearch } from '@/api/position'
 import { getInterviewInviteListByInviteUserId, getMessageType } from '@/api/common'
 // import { getUserInfo } from '@/api/personal/user'
 import { getBaseInfo } from '@/api/common'
@@ -201,6 +223,7 @@ import { dealDictArrayData } from '@/utils/position'
 import { previewFile } from '@/utils'
 import { timesTampChange } from '@/utils/date'
 import { useRouter } from 'vue-router'
+import studentDeliveryForm from '@/views/recruit/personal/components/studentDeliveryForm.vue'
 
 const { t } = useI18n()
 const chatRef = ref()
@@ -217,6 +240,9 @@ const channelItem = ref(null)
 const messageItems = ref([])
 const pageSize = ref(1)
 const hasMore = ref(false)
+const studentDeliveryFormRef = ref()
+const baseInfo = ref(localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {})
+const isStudent = ref(baseInfo.value?.type && Number(baseInfo.value.type) === 1)
 
 const positionList = ref([])
 const showTip = ref(false)
@@ -411,6 +437,8 @@ async function getMessageTypeSync () {
     }
   })
   if (!data.records || !data.records.length) { 
+    positionInfo.value = {}
+    handleChangeSendResumeStatus(false)
     return
   }
   const _item = data.records.pop()
@@ -491,10 +519,68 @@ const handleUploadResume = async (url, title, filename) => {
   obj.truthValue = url
 }
 
+const changePositionData = () => {
+  positionListParams.value.pageNo = positionListIsEnd.value ? 1 : positionListParams.value.pageNo + 1
+  getRecruitPositionList()
+}
+
+const positionDetail = (val) => {
+  const id = val.value
+  if (!id) return
+  window.open(`/recruit/personal/position/details/${id}`)
+}
+
+// 选中职位并投递
+const selectJobId = ref('')
+const selectPositionSubmit = async () => {
+  // 投递
+  openPositionSelectDialog.value = false
+  handleSendResume(handleSendResumeItem)
+}
+
+const rightEntPositionTotal = ref(0)
+const rightEntPositionList = ref([])
+const positionListParams = ref({ pageNo: 1, pageSize: 5 })
+const openPositionSelectDialog = ref(false)
+const pageLoading = ref(false)
+const positionListIsEnd = computed(() => positionListParams.value.pageNo * positionListParams.value.pageSize >=  rightEntPositionTotal.value)
+// 职位列表
+const getRecruitPositionList = async () => {
+  const enterpriseId = info.value?.enterpriseId || null
+  if (!enterpriseId) return Snackbar.warning('访问企业错误!')
+
+  pageLoading.value = true
+  const { list, total: number } = await getJobAdvertisedSearch({ ...positionListParams.value, enterpriseId })
+  if (!list.length) return Snackbar.warning('企业暂无招聘中的职位,无法进行投递!')
+
+  rightEntPositionTotal.value = number
+  rightEntPositionList.value = list.map(j => {
+    const e = j?.job || null
+    if (!e) return e
+    const salary = e.payFrom && e.payTo ? `${e.payFrom ? e.payFrom + '-' : ''}${e.payTo}${e.payName ? '/' + e.payName : ''}` : '面议'
+    return {
+      label: `${formatName(e.name)}_${e.areaName ? e.area?.str : '全国'} ${salary}`,
+      value: e.id,
+      data: e
+    }
+  }).filter(Boolean)
+  
+  setTimeout(() => { pageLoading.value = false }, 300)
+}
+
 // 获取简历
 const showUploadDialog = ref(false)
 const enRequestPositionInfo = ref({}) // 企业求简历时选中的职位信息
+let handleSendResumeItem = null
 async function handleSendResume (item) {
+  const jobId = enRequestPositionInfo.value && enRequestPositionInfo.value?.id ? enRequestPositionInfo.value?.id : positionInfo.value.id
+  if (!jobId && !selectJobId.value && import.meta.env.VITE_NODE_ENV !== 'production') {
+    // 没有基于职位接收到的沟通,弹出职位列表让求职者选择。否则无法投递简历。
+    handleSendResumeItem = item
+    await getRecruitPositionList()
+    if (rightEntPositionTotal.value) openPositionSelectDialog.value = true
+    return
+  }
   try {
     item.loading = true
     // 获取简历列表
@@ -550,85 +636,112 @@ const handleSubmitAttachment = async () => {
     obj[e.key] = e.truthValue || e.value
   })
   if (!obj.title || !obj.url) return
-  await savePersonResumeCv(obj)
-  const text = {
-    remark: '发送简历',
-    query: {
-      src: obj.url,
-      title: obj.title
-    },
-    type: 1
+
+  // 学生实习到岗信息
+  let practice = {}
+  if (isStudent.value) {
+    practice = studentDeliveryFormRef.value.getQueryParams()
+    console.log(practice, '上传简历-到岗信息')
   }
-  if (enRequestPositionInfo.value) text.query.positionInfo = enRequestPositionInfo.value
-  send (JSON.stringify(text), channelItem.value, 105)
+  if (isStudent.value && (!practice?.practiceStartTime || !practice?.practiceEndTime)) return Snackbar.warning('请完善实习到岗信息')
+
+  // 保存附件
+  await savePersonResumeCv(obj)
 
   // 简历投递至简历库
   if (isEmployment.value !== '-1') {
-    await jobCvRelHireSend({
-      jobId: positionInfo.value.id,
+    let params = {
+      jobId: positionInfo.value.id || selectJobId.value,
       url: obj.url,
       recommendUserId: isEmployment.value
-    })
+    }
+    // 如果是学生则需要带上实习信息
+    if (practice && Object.keys(practice).length > 0) params = Object.assign(params, practice)
+    await jobCvRelHireSend(params)
   } else {
-    const jobId = enRequestPositionInfo.value && enRequestPositionInfo.value?.id ? enRequestPositionInfo.value?.id : positionInfo.value.id
+    const jobId = enRequestPositionInfo.value && enRequestPositionInfo.value?.id ? enRequestPositionInfo.value?.id : positionInfo.value.id || selectJobId.value
     const type = (enRequestPositionInfo.value && Object.keys(enRequestPositionInfo.value).length ? enRequestPositionInfo.value.hire : positionInfo.value.hire) ? 1 : 0
-    const params = {
+    let params = {
       jobId,
       title: obj.title,
       url: obj.url,
       type
     }
-    // 参与招聘会的职位需传递招聘会id
-    // if (jobFairId.value) params.jobFairId = jobFairId.value
-    
+    // 如果是学生则需要带上实习信息
+    if (practice && Object.keys(practice).length > 0) params = Object.assign(params, practice)
     await jobCvRelSend(params)
   }
   handleChangeSendResumeStatus(true)
   showUploadDialog.value = false
-  enRequestPositionInfo.value = {}
-}
 
-async function handleSubmitResume () {
-  if (!selectResume.value) {
-    Snackbar.error(t('resume.selectResumeToSubmit'))
-    return
-  }
-  const _info = resumeList.value.find((item) => item.id === selectResume.value)
   const text = {
     remark: '发送简历',
     query: {
-      src: _info.url,
-      title: _info.title,
-      id: _info.id,
+      src: obj.url,
+      title: obj.title
     },
     type: 1
   }
   if (enRequestPositionInfo.value) text.query.positionInfo = enRequestPositionInfo.value
   send (JSON.stringify(text), channelItem.value, 105)
 
+  enRequestPositionInfo.value = {}
+}
+
+async function handleSubmitResume () {
+  if (!selectResume.value) {
+    Snackbar.warning(t('resume.selectResumeToSubmit'))
+    return
+  }
+  const _info = resumeList.value.find((item) => item.id === selectResume.value)
+
+  // 学生实习到岗信息
+  let practice = {}
+  if (isStudent.value) {
+    practice = studentDeliveryFormRef.value.getQueryParams()
+    console.log(practice, '选择简历-到岗信息')
+  }
+  if (isStudent.value && (!practice?.practiceStartTime || !practice?.practiceEndTime)) return Snackbar.warning('请完善实习到岗信息')
+
   // 简历投递至简历库
   if (isEmployment.value !== '-1') {
-    await jobCvRelHireSend({
-      jobId: positionInfo.value.id,
+    let params = {
+      jobId: positionInfo.value.id || selectJobId.value,
       url: _info.url,
       recommendUserId: isEmployment.value
-    })
+    }
+    // 如果是学生则需要带上实习信息
+    if (practice && Object.keys(practice).length > 0) params = Object.assign(params, practice)
+    await jobCvRelHireSend(params)
   } else {
-    const jobId = enRequestPositionInfo.value && enRequestPositionInfo.value?.id ? enRequestPositionInfo.value?.id : positionInfo.value.id
+    const jobId = enRequestPositionInfo.value && enRequestPositionInfo.value?.id ? enRequestPositionInfo.value?.id : positionInfo.value.id || selectJobId.value
     const type = (enRequestPositionInfo.value && Object.keys(enRequestPositionInfo.value).length ? enRequestPositionInfo.value.hire : positionInfo.value.hire) ? 1 : 0
-    const params = {
+    let params = {
       jobId,
       title: _info.title,
       url: _info.url,
       type
     }
-    // 参与招聘会的职位需传递招聘会id
-    // if (jobFairId.value) params.jobFairId = jobFairId.value
+    // 如果是学生则需要带上实习信息
+    if (practice && Object.keys(practice).length > 0) params = Object.assign(params, practice)
     
     await jobCvRelSend(params)
   }
   handleChangeSendResumeStatus(true)
   showResume.value = false
+
+  const text = {
+    remark: '发送简历',
+    query: {
+      src: _info.url,
+      title: _info.title,
+      id: _info.id,
+    },
+    type: 1
+  }
+  if (enRequestPositionInfo.value) text.query.positionInfo = enRequestPositionInfo.value
+  send (JSON.stringify(text), channelItem.value, 105)
+  
   enRequestPositionInfo.value = {}
 }
 

+ 1 - 1
src/views/recruit/entRegister/register.vue

@@ -83,7 +83,7 @@
           </div>
           <div v-for="(item, index) in contactCopy" :key="index" class="contactItemCard">
             <div class="d-flex justify-space-between pb-2">
-              <div class="mb-3 pl-3" style="font-size: 13px; color: 00897B; border-left: 5px solid #00897B;">{{ index ? '联系人' + index : '管理员' }}</div>
+              <div class="mb-3 pl-3" style="font-size: 13px; color: 00897B; border-left: 5px solid #00B760;">{{ index ? '联系人' + index : '管理员' }}</div>
               <v-btn v-if="index > 1" color="error" density="compact" variant="text" @click="delContact(index)">删除</v-btn>
               <div v-else style="font-size: 12px; color: #999">不可删除</div>
             </div>

+ 1 - 1
src/views/recruit/enterprise/hirePosition/components/add.vue

@@ -46,7 +46,7 @@ const loading = ref(false)
 
 const list = [
   {
-    color: '#00897B',
+    color: '#00B760',
     icon: 'mdi-numeric-1',
     title: t('position.positionInformation'),
     desc: '',

+ 8 - 0
src/views/recruit/enterprise/interviewManagement/components/invite.vue

@@ -101,7 +101,15 @@ const getQuery = () => {
   obj.type = 1
   obj.id = props.itemData.id
   obj.userId = props.itemData.userId
+  
   if (props.itemData.jobFairId) obj.jobFairId = props.itemData.jobFairId
+
+  // 有实习时间的则为学生,需传递实习时间
+  if (props.itemData?.practiceStartTime && props.itemData?.practiceEndTime) {
+    obj.practiceStartTime = props.itemData.practiceStartTime
+    obj.practiceEndTime = props.itemData.practiceEndTime
+  }
+
   return obj
 }
 

+ 3 - 1
src/views/recruit/enterprise/interviewManagement/components/item.vue

@@ -6,7 +6,9 @@
       <div class="d-flex flex-column mr-3" style="width: 200px;">
         <span class="ellipsis mb-1">{{ item?.person?.name || item?.phone }}</span>
         <span class="d-flex justify-center" style="max-width: 200px;">
-          <svg-icon v-if="item.jobFairId" class="mr-1" name="jobFair" size="20"></svg-icon>
+          <div>
+            <svg-icon v-if="item.jobFairId" class="mr-1" name="jobFair" size="20"></svg-icon>
+          </div>
           <p v-ellipse-tooltip class="color-999">{{ formatName(item?.job?.name) }}</p>
         </span>
       </div>

+ 63 - 29
src/views/recruit/enterprise/jobFair/details.vue

@@ -1,12 +1,19 @@
 <template>
-  <v-card class="card-box pa-4">
+  <v-card class="card-box pa-4" :style="`background-color: ${jobFairInfo?.backgroundColour}`">
     <div class="position-relative">
-      <div class="text-end mb-3">
-        <v-btn color="primary" @click="handleAdd">新增职位</v-btn>
-        <v-btn color="primary" class="mx-3" variant="outlined" @click="handleJoin">选择已发布的职位加入招聘会</v-btn>
-        <v-btn color="primary" v-if="bgImg" variant="outlined" prepend-icon="mdi-share-all" @click="handleShare">我的分享海报</v-btn>
+      <div class="text-center my-6 mx-10 px-15 font-weight-bold font-size-20" style="color: #fff">{{ jobFairInfo?.title?.replace(/<\/?p[^>]*>/gi, '') }}</div>
+      <div class="d-flex justify-space-between mb-3 align-center">
+        <div>
+          <span v-if="jobNum && Number(jobNum)" style="color: #fff;">剩余可发布职位数:{{ jobNum }}个</span>
+        </div>
+        <div>
+          <!-- <v-btn color="#fff" :style="`color: ${jobFairInfo?.backgroundColour || 'var(--v-primary-base)'}`" @click="handleAdd">新增职位</v-btn> -->
+          <v-btn color="#fff" class="ml-3" :style="`color: ${jobFairInfo?.backgroundColour || 'var(--v-primary-base)'}`" prepend-icon="mdi-refresh" @click="getJobList()">刷新</v-btn>
+          <v-btn color="#fff" class="mx-3" :style="`color: ${jobFairInfo?.backgroundColour || 'var(--v-primary-base)'}`" @click="handleJoin">选择已发布的职位加入招聘会</v-btn>
+          <v-btn color="#fff" v-if="jobFairInfo?.contentImg" :style="`color: ${jobFairInfo?.backgroundColour || 'var(--v-primary-base)'}`" prepend-icon="mdi-share" @click="handleShare">我的分享海报</v-btn>
+        </div>
       </div>
-      <JobItem :items="jobList" @refresh="getJobList"></JobItem>
+      <JobItem :items="jobList" @refresh="getJobList(), getJobNum()"></JobItem>
 
       <v-navigation-drawer v-model="showDrawer" location="right" temporary width="600">
         <Loading :visible="positionLoading" :contained="true"></Loading>
@@ -24,9 +31,11 @@
             variant="outlined"
             hide-details
             color="primary"
+            clearable
             single-line
-            @click:append-inner="getPositionList"
-            @keyup.enter="getPositionList"
+            @click:clear="handleSearch"
+            @click:append-inner="handleSearch"
+            @keyup.enter="handleSearch"
           ></v-text-field>
         </div>
         <div class="pa-3" v-if="positionItems.length">
@@ -69,12 +78,21 @@
         <JobFairEntShare 
           :show="showShare" 
           :enterpriseName="enterpriseName" 
-          :logoUrl="logoUrl" 
+          :logoUrl="logoUrl"
+          :jobFairId="id"
+          :enterpriseId="entBaseInfo?.id"
           :positionList="positionList" 
-          :bgImg="bgImg" 
+          :bgImg="jobFairInfo?.contentImg"
+          :backgroundColor="jobFairInfo?.backgroundColour" 
           @success="handlePreview"
         ></JobFairEntShare>
       </div>
+      <!-- 招聘会分享按钮 -->
+      <!-- <div v-if="jobFairInfo?.contentImg" style="position: absolute; right: 12px; top: -16px; z-index: 999;">
+        <div class="cursor-pointer pa-2 white-bgc" style="border-radius: 8px;" @click.stop="handleShare">
+          <v-icon color="primary" size="26">mdi-share</v-icon>
+        </div>
+      </div> -->
     </div>
   </v-card>
 
@@ -84,14 +102,13 @@
 <script setup>
 defineOptions({ name: 'jobFairJob'})
 import { ref } from 'vue'
-import { getJobFairPosition, getJobFair } from '@/api/recruit/enterprise/jobFair'
+import { getJobFairPosition, getJobFair, getJobFairRights, joinJobFairPosition, getJobFairPositionList } from '@/api/recruit/enterprise/jobFair'
 import { dealDictArrayData } from '@/utils/position.js'
 import JobItem from './job/item.vue'
 import { useRouter, useRoute } from 'vue-router'
 import { useI18n } from '@/hooks/web/useI18n'
 import Snackbar from '@/plugins/snackbar'
-import { getEnterprisePubJobTypePermission } from '@/api/recruit/enterprise/position'
-import { getJobAdvertisedList } from '@/api/position'
+// import { getEnterprisePubJobTypePermission } from '@/api/recruit/enterprise/position'
 import { formatName } from '@/utils/getText'
 import JobFairEntShare from '@/views/recruit/components/jobFairEntShare'
 
@@ -121,7 +138,6 @@ const enterpriseName = ref(formatName(entBaseInfo.value.enterpriseAnotherName ||
 const logoUrl = ref(entBaseInfo.value.logoUrl)
 const previewSrc  = ref('')
 const positionList = ref([])
-const bgImg = ref('')
 
 // 职位列表
 const getJobList = async () => {
@@ -130,11 +146,11 @@ const getJobList = async () => {
   jobList.value = dealDictArrayData([], data)
 }
 
-const handleAdd = async () => {
-  const data = await getEnterprisePubJobTypePermission()
-  if (!data || !data.length) return Snackbar.warning('没有该操作权限,请联系平台管理员升级后再试')
-  router.push(`/recruit/enterprise/jobFair/details/${id}/edit`)
-}
+// const handleAdd = async () => {
+//   const data = await getEnterprisePubJobTypePermission()
+//   if (!data || !data.length) return Snackbar.warning('没有该操作权限,请联系平台管理员升级后再试')
+//   router.push(`/recruit/enterprise/jobFair/details/${id}/edit`)
+// }
 
 const showDrawer = ref(false)
 const handleJoin = async () => {
@@ -147,8 +163,13 @@ const handleChangePage = (index) => {
   getPositionList()
 }
 
-const handleTo = (val) => {
-  router.push(`/recruit/enterprise/jobFair/details/${id}/edit?id=${val.id}`)
+// 添加至招聘会
+const handleTo = async (val) => {
+  await joinJobFairPosition({ jobFairId: id, jobId: val.id })
+  Snackbar.success('加入成功')
+  showDrawer.value = false
+  getJobNum()
+  getJobList()
 }
 
 // 获取职位列表
@@ -156,9 +177,7 @@ const getPositionList = async () => {
   positionLoading.value = true
   const query = {
     ...positionPageInfo.value,
-    status: 0,
-    hasExpiredData: false,
-    hire: false
+    jobFairId: id
   }
   if ( positionSearch.value) {
     Object.assign(query, {
@@ -166,21 +185,36 @@ const getPositionList = async () => {
     })
   }
   try {
-    const { list, total } = await getJobAdvertisedList(query)
+    const { list, total } = await getJobFairPositionList(query)
     positionTotal.value = total
     positionItems.value = list.length ? dealDictArrayData([], list) : []
   } finally {
     positionLoading.value = false
   }
 }
+const handleSearch = () => {
+  positionPageInfo.value.pageNo = 1
+  getPositionList()
+}
+
 getJobList()
 
-const getJob = async () => {
+// 可发布职位数
+const jobNum = ref(0)
+const getJobNum = async () => {
+  const result = await getJobFairRights(id)
+  jobNum.value = result?.num || 0
+}
+getJobNum()
+
+// 获取招聘会信息
+const jobFairInfo = ref({})
+const getJobFairInfo = async () => {
   const data = await getJobFair(id)
   if (!data) return
-  bgImg.value = data.contentImg
+  jobFairInfo.value = data || {}
 }
-getJob()
+getJobFairInfo()
 
 // 分享海报预览
 const handlePreview = (val) => {
@@ -190,7 +224,7 @@ const handlePreview = (val) => {
 }
 
 const handleShare = () => {
-  positionList.value = jobList.value && jobList.value.length > 0 ? jobList.value.map(e => formatName(e.name)).slice(0, 2) : []
+  positionList.value = jobList.value && jobList.value.length > 0 ? jobList.value.map(e => formatName(e.name)).slice(0, 3) : []
   showShare.value = true
 }
 </script>

+ 7 - 23
src/views/recruit/enterprise/jobFair/editJob/index.vue

@@ -22,11 +22,7 @@ import CtForm from '@/components/CtForm'
 import Add from '@/views/recruit/enterprise/positionManagement/components/add.vue'
 import { useI18n } from '@/hooks/web/useI18n'
 import { schoolMajorByName, schoolMajorById } from '@/api/recruit/personal/resume'
-import {
-  saveJobAdvertisedExtend,
-  joinJobFairPosition,
-  getJobAdvertisedExtend
-} from '@/api/recruit/enterprise/jobFair'
+import { saveJobAdvertisedExtend, joinJobFairPosition, getJobAdvertisedExtend } from '@/api/recruit/enterprise/jobFair'
 import Snackbar from '@/plugins/snackbar'
 
 
@@ -42,8 +38,8 @@ const items = ref({
       type: 'text',
       key: 'dept',
       value: null,
+      col: 6,
       label: '招聘部门 ',
-      // rules: [v => !!v || '请选择招聘部门']
     },
     {
       type: 'autocomplete',
@@ -52,8 +48,9 @@ const items = ref({
       value: null,
       label: '专业要求 ',
       itemText: 'nameCn',
+      col: 6,
       itemValue: 'id',
-      // rules: [v => !!v || '专业要求'],
+      flexStyle: 'ml-3',
       noDataText: '请输入检索专业',
       items: []
     },
@@ -63,7 +60,6 @@ const items = ref({
       value: null,
       label: '工作频率 ',
       col: 6,
-      // rules: [v => !!v || '请选择工作频率'],
       items: [
         { label: '每周', value: 'week' },
         { label: '每月', value: 'month' },
@@ -77,23 +73,10 @@ const items = ref({
       flexStyle: 'ml-3',
       col: 6,
       label: '出勤天数 ',
-      // rules: [v => v > 0 || '请填写正确的出勤天数']
-    },
-    {
-      type: 'ifRadio',
-      key: 'hot',
-      value: 1,
-      label: '热门职位 ',
-      items: [
-        { label: '是', value: 1 },
-        { label: '否', value: 0 },
-      ],
-      // rules: [v => !!v || '请选择是否热门']
-    },
+    }
   ]
 })
 
-// console.log(route)
 if (route.query.id) {
   // 编辑
   initPosition(route.query.id)
@@ -150,8 +133,9 @@ const afterAdd = async (jobId) => {
       r[v.key] = v.type === 'number' ? +v.value : v.value
       return r
     }, { jobId })
+
+    console.log(query, 'query')
     await saveJobAdvertisedExtend(query)
-    console.log('招聘会职位扩展信息保存成功')
     await joinJobFairPosition({
       jobFairId: route.params.id,
       jobId

+ 240 - 6
src/views/recruit/enterprise/jobFair/index.vue

@@ -6,13 +6,58 @@
         <div class="px-5 py-3">
           <div class="color-666">活动时间:{{ timesTampChange(k.startTime, 'Y-M-D') }}至{{ timesTampChange(k.endTime, 'Y-M-D') }}</div>
           <div class="text-end">
-            <v-btn color="primary" variant="outlined" @click.stop="handleBlockEnterprise(k.id)">立即加入</v-btn>
+            <v-btn color="primary" variant="outlined" @click.stop="handleBlockEnterprise(k)">立即加入</v-btn>
           </div>
         </div>
       </v-card>
     </div>
-    <Empty v-else message="暂无进行中的招聘会,去看看其他吧~" />
+    <Empty v-else :elevation="false" message="暂无进行中的招聘会,去看看其他吧~" />
   </v-card>
+
+  <CtDialog
+    :visible="showDialog"
+    :widthType="3"
+    :footer="false"
+    titleClass="text-h6"
+    title="购买招聘会门票"
+    @close="handleClose"
+  >
+    <div class="color-warning mb-5">
+      <p>您暂时无法参加该招聘会</p>
+      <p>您可以通过下方扫码购买招聘会门票参与~</p>
+    </div>
+    <div v-if="payType" id="codeBox" class="code pa-0 resume-box">
+      <div class="resume-header px-3 pt-5">
+        <div class="resume-title">扫码支付</div>
+      </div>
+      <div class="d-flex justify-center mt-3">
+        <div id="codeItem" class="d-flex flex-column align-center my-2">
+          <div class="d-flex align-center">
+            <div class="color-666 font-weight-bold">支付方式:</div>
+            <v-chip-group v-model="payType" selected-class="text-primary" column mandatory @update:modelValue="payTypeChange">
+              <v-chip filter v-for="k in payTypeList" :key="k.code" :value="k.code" class="mr-3" label>
+                {{ k.name }}
+                <svg-icon v-if="k.icon" class="ml-1" :name="k.icon" :size="k.size"></svg-icon>
+              </v-chip>
+            </v-chip-group>
+          </div>
+          <div class="code-right">
+            <div class="price">
+              <span class="font-size-13">¥</span>
+              {{ FenYuanTransform(info?.admissionPrice || 0) }}元
+            </div>
+          </div>
+          <div class="code-left" v-if="payQrCodeTxt">
+            <QrCode :text="payQrCodeTxt" :disabled="!remainderTimer" :width="170" @refresh="getUnpaidOrderList" />
+          </div>
+          <div class="mt-52" style="color: var(--v-error-base);">
+            扫码支付时请勿离开
+            <span v-if="remainderZhShow">{{ remainderZhShow }}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+  </CtDialog>
 </template>
 
 <script setup>
@@ -23,6 +68,10 @@ import { getJobFairList } from '@/api/recruit/enterprise/jobFair'
 import { getCheckJobFairPermission } from '@/api/recruit/enterprise/jobFair'
 import Snackbar from '@/plugins/snackbar'
 import { timesTampChange } from '@/utils/date'
+import { FenYuanTransform } from '@/utils/position'
+import { getEnableCodeList, payOrderSubmit, getOrderPayStatus, getUnpaidOrder } from '@/api/common'
+import { definePayTypeList, qrCodePay } from '@/utils/payType'
+import { createTradeOrder } from '@/api/position'
 
 const router = useRouter()
 const list = ref([])
@@ -33,11 +82,179 @@ const getList = async () => {
 }
 getList()
 
+const timer = ref(null)
+const showDialog = ref(false)
+
+// 1.支付方式
+const payType = ref('')
+const payTypeList = ref([])
+const codeList = ref([])
+const getCodeList = async () => {
+  try {
+    const list = await getEnableCodeList({ appId: 11 })
+    codeList.value = list || []
+  } catch (error) {
+    console.log(error)
+  } finally {
+    if (definePayTypeList?.length && codeList.value?.length) {
+      codeList.value.forEach(code => {
+        const item = definePayTypeList.find(p => p.code === code)
+        if (item) {
+          if (!payType.value) {
+            const bool = qrCodePay.includes(code)
+            if (bool) {
+              payType.value = code
+              getUnpaidOrderList()
+            }
+          }
+          payTypeList.value.push(item)
+        }
+      })
+    }
+  }
+}
+
+const payQrCodeTxt = ref('')
+
+// 2.发起支付
+const payOrder = ref({})
+let maxCount = 0
+const getUnpaidOrderList = async () => {
+  // 查询是否有未支付的订单
+  const data = await getUnpaidOrder({ spuId: info.value.id, type: 5 })
+  if (!data) {
+    // 没有则创建订单
+    await createTradeOrder({ price: (info.value.admissionPrice - 0), spuId: info.value.id, spuName: info.value.title, type: 5 })
+    if (maxCount > 3) return // 避免死循环
+    maxCount++
+    setTimeout(() => {
+      getUnpaidOrderList()
+    }, 1000)
+  }
+  payOrder.value = data?.payOrder || null
+  paySubmit()
+}
+
+const payStatus = async () => {
+  try {
+    const data = await getOrderPayStatus({ id: payOrder.value.id })
+    if ((data?.status - 0) === 10) {
+      // 支付成功
+      if (timer.value) clearInterval(timer.value); timer.value = null
+      setTimeout(() => {
+        // 清除定时器
+        clearTimer()
+        // 支付成功,跳转招聘会详情
+        Snackbar.success('购买成功')
+        handleBlockEnterprise(info.value)
+      }, 2000);
+    }
+  } catch (error) {
+    console.log(error)
+  }
+}
+
+const payTypeChange = (val) => {
+  payType.value = val
+  getUnpaidOrderList()
+}
+
+const paySubmit = async () => {
+  if (!payType.value) return
+  try {
+    // 提交支付订单
+    const params = {
+      channelCode: payType.value, // 支付渠道
+      id: payOrder.value.id
+    }
+    const res = await payOrderSubmit(params)
+    payQrCodeTxt.value = res?.displayContent || '' // 生成二维码内容
+
+     // 弹窗关闭后不执行
+    if (!showDialog.value) {
+      if (timer.value) clearInterval(timer.value); timer.value = null
+      return
+    }
+
+    initIntervalFun()
+    if (timer.value) clearInterval(timer.value); timer.value = null
+    timer.value = setInterval(() => { payStatus() }, 1000) // 轮巡查询用户是否支付
+  } catch (error) {
+    console.log(error)
+  }
+}
+
+const remainderTimer = ref(null)
+const countdownTime = 60000 * 3 // 倒计时三分钟
+let remainder = 0
+// 初始化倒计时
+const initIntervalFun = () => {
+  remainder = countdownTime // 初始倒计时时间
+  if (remainderTimer.value) clearInterval(remainderTimer.value); remainderTimer.value = null // 每一次点击都清除上一个轮询
+  // 倒计时计算
+  remainderCalc()
+  remainderTimer.value = setInterval(() => { remainderCalc() }, 1000)
+
+  if (timer.value) clearInterval(timer.value); timer.value = null
+  timer.value = setInterval(() => { payStatus() }, 2000) // 轮巡查询用户是否支付
+}
+
+const formatDuration = (remainder) => {
+  // 将毫秒转换为秒
+  var seconds = Math.floor(remainder / 1000)
+  // 计算分钟和剩余的秒数
+  var minutes = Math.floor(seconds / 60)
+  var remainingSeconds = seconds % 60
+  // 格式化分钟和秒数,确保秒数为两位数(如果小于10,则前面补0)
+  minutes = minutes.toString().padStart(2, '0')
+  remainingSeconds = remainingSeconds.toString().padStart(2, '0')
+  // 返回格式化的字符串
+  return `${minutes}分${remainingSeconds}秒`
+}
+
+const remainderZhShow = ref('')
+const clearTimer = () => {
+  if (timer.value) clearInterval(timer.value); timer.value = null
+  if (remainderTimer.value) clearInterval(remainderTimer.value); remainderTimer.value = null
+  remainderZhShow.value = ''
+}
+
+const remainderCalc = () => {
+  remainder -= 1000
+  remainderZhShow.value = formatDuration(remainder)
+  if (remainder <= 0) clearTimer()
+}
+
+const handleClose = () => {
+  clearTimer()
+  payType.value = ''
+  payQrCodeTxt.value = ''
+  payTypeList.value = []
+  showDialog.value = false
+  info.value = {}
+}
+
 // 立即加入
-const handleBlockEnterprise = async (id) => {
-  const data = await getCheckJobFairPermission(id)
-  if (!data) return Snackbar.warning('您没有权限参加该招聘会') // 只有返回true才能进入双选会
-  router.push(`/recruit/enterprise/jobFair/details/${id}/index`)
+const info = ref(0)
+const handleBlockEnterprise = async (val) => {
+  try {
+    const data = await getCheckJobFairPermission(val.id)
+    if (data) router.push(`/recruit/enterprise/jobFair/details/${val.id}`)
+  } catch (error) {
+    // 权限被禁用
+    if (error?.code === 1100056008) return Snackbar.warning(error.msg)
+    // 没有权限参加招聘会,购买门票
+    if (error?.code === 1100056005) {
+      // 没有设置门票金额则提示无权限参加
+      if (!val?.admissionPrice || val?.admissionPrice <= 0) return Snackbar.warning(error.msg)
+      // 设置门票金额则提示购买门票
+      Snackbar.warning('您暂时无法参加该招聘会,请先购买门票')
+      info.value = val
+      if (timer.value) clearInterval(timer.value); timer.value = null
+      await getCodeList()
+      showDialog.value = true
+    }
+  }
 }
 </script>
 
@@ -48,4 +265,21 @@ const handleBlockEnterprise = async (id) => {
   gap: 20px;
   min-height: auto;
 }
+.code {
+  background-color: #f7f8fa;
+  border-radius: 6px;
+  margin: 0 auto;
+  &-left {
+    border: 1px solid #00B760;
+    border-radius: 6px;
+    padding: 5px;
+  }
+  &-right {
+    .price {
+      font-size: 30px;
+      font-weight: 700;
+      color: var(--v-error-base);
+    }
+  }
+}
 </style>

+ 23 - 10
src/views/recruit/enterprise/jobFair/job/item.vue

@@ -1,10 +1,11 @@
 <template>
   <div>
     <div v-if="items.length">
-      <div v-for="val in items" :key="val.id" class="itemBox mb-3" style="height: 134px;">
+      <div v-for="val in items" :key="val.id" class="itemBox mb-3 white-bgc" style="height: 134px;">
         <div class="d-flex justify-space-between" style="padding: 10px 20px;">
           <div class="position">
             <div class="d-flex align-center">
+              <svg-icon name="jobFair" class="mr-1" size="25"></svg-icon>
               <span class="position-name">{{ formatName(val.name) }}</span>
             </div>
             <div class="mt-3 other-info">
@@ -20,7 +21,10 @@
               <span>{{ val.positionName }}</span>
             </div>
           </div>
-          <div class="text-center color-primary d-flex flex-column justify-center cursor-pointer" @click="handleToResume(val)">
+          <div 
+            class="text-center color-primary d-flex flex-column justify-center cursor-pointer"
+            @click="handleToResume(val)"
+          >
             <div class="font-weight-bold font-size-18">{{ val.count || 0 }}</div>
             <div class="font-size-14">已投递简历</div>
           </div>
@@ -35,7 +39,7 @@
         </div>
       </div>
     </div>
-    <Empty v-else :elevation="false"></Empty>
+    <div v-else class="empty-text">暂无数据,去发布职位看看吧~</div>
   </div>
 
   <Loading :visible="loading"></Loading>
@@ -70,13 +74,15 @@ const handleEdit = async (val) => {
 
 
 const handleRemove = ({ id }) => {
-  Confirm('确定要移出该职位吗?', '提示').then(async () => {
-    await quitJobFairPosition({
-      jobFairId: route.params.id,
-      jobId: id
-    })
-    Snackbar.success('移出成功')
-    emit('refresh')
+  Confirm('系统提示', '是否确认将此职位移出招聘会?').then(async () => {
+    try {
+      await quitJobFairPosition({
+        jobFairId: route.params.id,
+        jobId: id
+      })
+      Snackbar.success('移出成功')
+      emit('refresh')
+    } catch {}
   })
   
 }
@@ -88,9 +94,16 @@ const handleToResume = (val) => {
 </script>
 
 <style scoped lang="scss">
+.empty-text {
+  height: 50vh;
+  line-height: 50vh;
+  text-align: center;
+  color: #fff;
+}
 .itemBox {
   position: relative;
   border: 1px solid #e5e6eb;
+  border-radius: 4px;
 }
 .position-name {
   color: var(--color-333);

+ 2 - 2
src/views/recruit/enterprise/membershipPackage/dynamic/balance.vue

@@ -324,7 +324,7 @@ const mlArr = [119, 344, 567, 791, 1298, 1298, 1298, 1298]
   color: var(--color-666);
 }
 .active {
-  border: 2px solid #00897B;
+  border: 2px solid #00B760;
   .priceBox {
     color: var(--v-primary-base);
   }
@@ -352,7 +352,7 @@ const mlArr = [119, 344, 567, 791, 1298, 1298, 1298, 1298]
   border-radius: 6px;
   margin: 0 auto;
   &-left {
-    border: 1px solid #00897B;
+    border: 1px solid #00B760;
     border-radius: 6px;
     padding: 5px;
   }

+ 2 - 2
src/views/recruit/enterprise/membershipPackage/dynamic/package-copy.vue

@@ -332,7 +332,7 @@ const remainderCalc = () => {
     }
   }
   .active {
-    border: 1px solid #00897B;
+    border: 1px solid #00B760;
   }
 }
 .code {
@@ -340,7 +340,7 @@ const remainderCalc = () => {
   border-radius: 6px;
   margin: 0 auto;
   &-left {
-    border: 1px solid #00897B;
+    border: 1px solid #00B760;
     border-radius: 6px;
     padding: 5px;
   }

+ 3 - 3
src/views/recruit/enterprise/membershipPackage/dynamic/package.vue

@@ -11,7 +11,7 @@
         </template>
         <template v-else>
           <h4 class="mt-5" :style="{'color': isSelected ? '#fff' : '#333'}">{{ val.name }}</h4>
-          <div :style="{'color': isSelected ? '#fff' : '#00897B'}">
+          <div :style="{'color': isSelected ? '#fff' : '#00B760'}">
             <span>¥</span>
             <span style="font-size: 35px;">{{ val.price / 100 }}</span>
             <span> 元</span>
@@ -301,14 +301,14 @@ const remainderCalc = () => {
   background-color: #f7f8fa;
 }
 .active {
-  background-color: #00897B;
+  background-color: #00B760;
 }
 .code {
   background-color: #f7f8fa;
   border-radius: 6px;
   margin: 0 auto;
   &-left {
-    border: 1px solid #00897B;
+    border: 1px solid #00B760;
     border-radius: 6px;
     padding: 5px;
   }

+ 126 - 14
src/views/recruit/enterprise/positionManagement/components/add.vue

@@ -11,7 +11,13 @@
           <div>
             <h2 class="mt-n1 headline font-weight-regular">{{ val.title }}</h2>
             <div class="mb-4 desc">{{ val.desc }}</div>
-            <component :is="val.path" :ref="val.ref" :itemData="itemData"></component>
+            <component :is="val.path" :ref="val.ref" :itemData="itemData" @changeType="handleChangeType"></component>
+          </div>
+        </v-timeline-item>
+        <v-timeline-item v-if="showExtend" dot-color="light-blue darken-1" icon="mdi-numeric-3">
+          <div>
+            <h2 class="mt-n1 headline font-weight-regular">其他</h2>
+            <CtForm ref="formPageRef" class="mt-3" :items="items" style="width: 650px;"></CtForm>
           </div>
         </v-timeline-item>
         <slot name="timeline"></slot>
@@ -49,6 +55,8 @@ import Snackbar from '@/plugins/snackbar'
 import { useI18n } from '@/hooks/web/useI18n'
 import { useUserStore } from '@/store/user'
 import { createTradeOrder } from '@/api/position'
+import { saveJobAdvertisedExtend, getJobAdvertisedExtend } from '@/api/recruit/enterprise/jobFair'
+import { schoolMajorByName, schoolMajorById } from '@/api/recruit/personal/resume'
 
 // 添加只为之后是否需要额外操作
 const props = defineProps({
@@ -70,7 +78,7 @@ const payInfo = ref({})
 
 const list = [
   {
-    color: '#00897B',
+    color: '#00B760',
     icon: 'mdi-numeric-1',
     title: t('position.positionInformation'),
     desc: '',
@@ -87,6 +95,114 @@ const list = [
   }
 ]
 
+const formPageRef = ref()
+const items = ref({
+  options: [
+    {
+      type: 'text',
+      key: 'dept',
+      value: null,
+      col: 6,
+      label: '招聘部门 '
+    },
+    {
+      type: 'autocomplete',
+      key: 'majorId',
+      search: getMajorList,
+      value: null,
+      label: '专业要求 ',
+      itemText: 'nameCn',
+      col: 6,
+      itemValue: 'id',
+      flexStyle: 'ml-3',
+      noDataText: '请输入检索专业',
+      items: []
+    },
+    {
+      type: 'autocomplete',
+      key: 'frequency-dateType',
+      value: null,
+      label: '工作频率 ',
+      col: 6,
+      items: [
+        { label: '每周', value: 'week' },
+        { label: '每月', value: 'month' },
+        { label: '每年', value: 'year' }
+      ]
+    },
+    {
+      type: 'number',
+      key: 'frequency-day',
+      value: null,
+      flexStyle: 'ml-3',
+      col: 6,
+      label: '出勤天数 '
+    }
+  ]
+})
+
+// 是否展示扩展信息
+const showExtend = ref(false)
+const handleChangeType = (show) => {
+  console.log(show, '招聘类型是否为实习')
+  showExtend.value = show
+  if (!show) {
+    items.value.options.forEach(e => e.value = null)
+  }
+}
+
+async function initPosition (jobId) {
+  const res = await getJobAdvertisedExtend(jobId)
+  console.log(res, '获取扩展信息')
+  if (!res) return
+  items.value.options.forEach(e => {
+    if (e.key.includes('frequency') && res.frequency) {
+      const keys = e.key.split('-')
+      e.value = res[keys[0]][keys[1]]
+      return
+    }
+    if (e.key === 'majorId') {
+      getMajorById(res.majorId)
+    }
+    e.value = res[e.key]
+  })
+  showExtend.value = true
+}
+
+async function getMajorList (name) {
+  if (!name) {
+    return
+  }
+  const res = await schoolMajorByName({ name })
+  items.value.options.find(e => e.key === 'majorId').items = res
+}
+
+async function getMajorById (id) {
+  if (!id) return
+  const res = await schoolMajorById({ id })
+  items.value.options.find(e => e.key === 'majorId').items = [res]
+}
+
+// 保存扩展信息
+const handleSaveExtend = async (jobId) => {
+  const query = items.value.options.reduce((r, v) => {
+    if (v.key.includes('frequency')) {
+      const keys = v.key.split('-')
+      if (!r[keys[0]]) {
+        r[keys[0]] = {}
+      }
+      r[keys[0]][keys[1]] = v.type === 'number' ? +v.value : v.value
+      return r
+    }
+    if (v.key === 'majorId') {
+      r.major = v.items.find(e => e.id === v.value)?.nameCn || ''
+    }
+    r[v.key] = v.type === 'number' ? +v.value : v.value
+    return r
+  }, { jobId })
+  console.log(query, 'extendInfo')
+  await saveJobAdvertisedExtend(query)
+}
 
 let submitParams = {}
 // 发布
@@ -100,18 +216,11 @@ const handleSave = async () => {
     window.scrollTo({ top: 0, behavior: 'smooth' })
     return
   }
-  // if (!requirement?.areaId) return Snackbar.warning('请选择工作城市')
   if (!baseInfo || !requirement) return Snackbar.warning('请将信息填写完整')
   
   submitParams = Object.assign(baseInfo, requirement, { currency_type: 0, source: props.isFair ? '2' : '0', bizId: props.isFair ? route.params.id : null }) // currency_type: 写死0(人民币)  source: 0职位管理|1招聘会
-  console.log('submitParams', submitParams)
   if (route.query && route.query.id) submitParams.id = route.query.id // 有id则为编辑
-  if (props.valid) {
-    const { valid } = await props.valid()
-    if (!valid) {
-      return
-    }
-  }
+  console.log('发布职位参数', submitParams)
   saveEmit()
 }
 
@@ -130,10 +239,10 @@ const saveEmit = async () => {
       Snackbar.warning('当前可发布职位额度不足,请扫码支付')
       return
     }
-    if (props.afterAdd) {
-      await props.afterAdd(res)
-      return
-    }
+
+    // 保存扩展信息
+    if (showExtend.value) await handleSaveExtend(res)
+
     Snackbar.success(submitParams.id ? t('common.editSuccessMsg') : '发布成功')
     handleCancel()
   } catch (error) {
@@ -172,6 +281,9 @@ const getPositionDetail = async (id) => {
   const data = await getJobDetails({ id })
   if (!data && !Object.keys(data).length) return
   itemData.value = {...data, ...dealDictObjData({}, data)}
+
+  // 招聘类型为实习则获取扩展信息
+  if (data.type === '3') initPosition(id)
 }
 
 // 有id为编辑

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

@@ -21,7 +21,7 @@
           </div>
           <div class="d-flex align-center" :class="{'cursor-pointer': tab === 1, 'ml-15': tab === 1}" @click="handleDetail(val)">
             <span class="position-name">{{ formatName(val.name) }}</span>
-            <svg-icon v-if="val.bizId" name="jobFair" class="ml-1" size="25"></svg-icon>
+            <svg-icon v-if="val.jobFairIds?.length" name="jobFair" class="ml-1" size="25"></svg-icon>
           </div>
           <div :class="['mt-3', 'other-info', 'ellipsis', {'ml-10': tab === 1}]">
             <span>{{ !val.areaId ? '全国' : val.area?.str }}</span>

+ 9 - 1
src/views/recruit/enterprise/positionManagement/components/jobRequirements.vue

@@ -73,6 +73,7 @@ import { getDict } from '@/hooks/web/useDictionaries'
 import { getTagTreeDataApi } from '@/api/enterprise'
 import { getEnterprisePubJobTypePermission } from '@/api/recruit/enterprise/position'
 
+const emits = defineEmits(['changeType'])
 const props = defineProps({
   itemData: Object
 })
@@ -112,6 +113,12 @@ const rules = {
   payUnit: [v => !!v || '请选择计薪时段'],
 }
 
+const handleChangeType = (val) => {
+  const type = items.value.options.find(e => e.key === 'type')
+  const obj = type.items.find(e => Number(e.value) === Number(val))
+  emits('changeType', obj[type.itemText] === '实习')
+}
+
 const formPageRef = ref()
 let query = reactive({})
 const items = ref({
@@ -125,7 +132,8 @@ const items = ref({
       itemValue: 'value',
       col: 4,
       rules: [v => !!v || '招聘类型'],
-      items: []
+      items: [],
+      change: handleChangeType
     },
     {
       type: 'autocomplete',

+ 7 - 0
src/views/recruit/enterprise/resume/components/invite.vue

@@ -109,6 +109,13 @@ const getQuery = () => {
     obj[item.key] = item.value
   })
   if (props.itemData?.jobFairId) obj.jobFairId = props.itemData.jobFairId
+
+  // 有实习时间的则为学生,需传递实习时间
+  if (props.itemData?.practiceStartTime && props.itemData?.practiceEndTime) {
+    obj.practiceStartTime = props.itemData.practiceStartTime
+    obj.practiceEndTime = props.itemData.practiceEndTime
+  }
+
   return obj
 }
 

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

@@ -38,7 +38,7 @@
         <span v-else>{{ item.status ? props.statusList.find(i => i.value === item.status)?.label : '' }}</span>
       </template>
       <template #actions="{ item }">
-        <v-btn color="primary" variant="text" @click="handlePreviewResume(item)">查看附件</v-btn>
+        <!-- <v-btn color="primary" variant="text" @click="handlePreviewResume(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>
@@ -57,7 +57,14 @@
               </v-list-item-title>
             </v-list-item>
           </v-list>
-        </v-menu>
+        </v-menu> -->
+
+        <v-btn icon variant="text" v-for="(k, index) in actionItems(item)" :key="index" @click.stop="k.click(item)">
+          <v-icon color="primary">{{ k.icon }}</v-icon>
+          <v-tooltip :text="k.title" location="top" activator="parent">
+            <span>{{ k.title }}</span>
+          </v-tooltip>
+        </v-btn>
       </template>
     </CtTable>
 
@@ -120,16 +127,16 @@ const headers = ref([
   { title: '状态', key: 'status', sortable: false },
   { title: '操作', value: 'actions', sortable: false }
 ])
-const unfit = { title: '类型', key: 'unfitType', sortable: false, value: item => item.type && item.type === '0' ? '简历不适合' : '面试不适合' }
-const delivery = { title: '类型', key: 'deliveryType', sortable: false, value: item => item?.job?.hire ? '赏金职位' : '普通职位' }
+// const unfit = { title: '类型', key: 'unfitType', sortable: false, value: item => item.type && item.type === '0' ? '简历不适合' : '面试不适合' }
+// const delivery = { title: '类型', key: 'deliveryType', sortable: false, value: item => item?.job?.hire ? '赏金职位' : '普通职位' }
 
 watch(
   () => props.tab,
   (val) => {
-    const obj = val !== 4 ? delivery : unfit
-    const index = headers.value.indexOf(obj)
-    if (index === -1) headers.value.splice(-1, 0, obj)
-    else headers.value.splice(index, 1)
+    // const obj = val !== 4 ? delivery : unfit
+    // const index = headers.value.indexOf(obj)
+    // if (index === -1) headers.value.splice(-1, 0, obj)
+    // else headers.value.splice(index, 1)
 
     // 不合适不需要展示状态
     if (val === 4) {
@@ -236,6 +243,8 @@ const handleEditSubmit = async () => {
   if (!valid) return
   const query = inviteRef.value.getQuery()
   if (!query?.time) return Snackbar.warning('请选择面试时间')
+  if (itemData.value?.practiceStartTime) query.practiceStartTime = itemData.value.practiceStartTime
+  if (itemData.value?.practiceEndTime) query.practiceEndTime = itemData.value.practiceEndTime
   await saveInterviewInvite(query)
   showTip.value = true
   handleEditClose()
@@ -268,13 +277,13 @@ const handleDownloadAttachment = (k) => {
 
 const actionItems = (item) => {
   const arr = []
-  if (props.tab === 0) arr.push({ title: '邀请面试', click: handleInterviewInvite }, { title: '立即沟通', click: handleToCommunicate })
-  if (props.tab === 1 && ['3', '4'].includes(item.status)) arr.push({ title: '入职', click: handleEnterByEnterprise })
-  if (!item.inTalentPool) arr.push({ title: '加入储备', click: handleJoinToTalentPool })
-  if ([0, 1].includes(props.tab)) arr.push({ title: '不合适', click: handleEliminate })
-  if (props.tab === 4) arr.push({ title: '取消不合适', click: handleCancelEliminate })
-  if (props.tab === 2 && item?.job?.hire) arr.push({ title: '结算', click: handleSettlement })
-  return [{ title: '下载附件', click: handleDownloadAttachment }, ...arr]
+  if (props.tab === 0) arr.push({ title: '邀请面试', click: handleInterviewInvite, icon: 'mdi-account-clock-outline' }, { title: '立即沟通', click: handleToCommunicate, icon: 'mdi-comment-processing-outline' })
+  if (props.tab === 1 && ['3', '4'].includes(item.status)) arr.push({ title: '入职', click: handleEnterByEnterprise, icon: 'mdi-account-arrow-right-outline' })
+  if (!item.inTalentPool) arr.push({ title: '加入储备', click: handleJoinToTalentPool, icon: 'mdi-account-star-outline' })
+  if ([0, 1].includes(props.tab)) arr.push({ title: '不合适', click: handleEliminate, icon: 'mdi-account-remove-outline' })
+  if (props.tab === 4) arr.push({ title: '取消不合适', click: handleCancelEliminate, icon: 'mdi-account-check-outline' })
+  if (props.tab === 2 && item?.job?.hire) arr.push({ title: '结算', click: handleSettlement, icon: 'mdi-currency-cny' })
+  return [{ title: '查看附件', click: handlePreviewResume, icon: 'mdi-eye-outline' }, { title: '下载附件', click: handleDownloadAttachment, icon: 'mdi-arrow-down-bold-circle-outline' }, ...arr]
 }
 </script>
 

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

@@ -79,7 +79,7 @@
         </template>
         
         <div class="px-5 pb-8">
-          <div v-if="item.workList.length">
+          <div v-if="item?.workList?.length">
             <div class="second-title">工作经验</div>
             <v-timeline density="compact" align="start" side="end" truncate-line="both">
               <v-timeline-item v-for="(j, i) in item.workList" :key="i" dot-color="primary" size="small">
@@ -296,7 +296,7 @@ const getData = async () => {
   loading.value = true
   try {
     const res = await getPersonSearchPage(Object.assign(queryParams.value, query.value))
-    if (!res.list.length) {
+    if (!res?.list?.length) {
       items.value = []
       total.value = 0
       return
@@ -380,7 +380,7 @@ const positionList = ref([])
 const getJobList = async () => {
   const { total, list } = await getJobAdvertisedList({ pageNo: 1, pageSize: 10, hasExpiredData: false, status: 0 })
   jobNum.value = total
-  if (!list.length) {
+  if (!list?.length) {
     positionList.value = []
     jobNum.value = 0
     return

+ 0 - 2
src/views/recruit/enterprise/staffChangePassword/index.vue

@@ -12,8 +12,6 @@
       </div>
     </div>
   </v-card>
-
-  <Loading :visible="overlay"></Loading>
 </template>
 
 <script setup>

+ 5 - 58
src/views/recruit/enterprise/systemManagement/groupAccount/inviteConfirm.vue

@@ -33,17 +33,6 @@
       </div>
     </div>
   </div>
-  <!-- 快速填写简易人才信息 -->
-  <!-- <CtDialog
-    :visible="showSimpleInput"
-    :widthType="2"
-    titleClass="text-h6"
-    title="补充基本信息"
-    @close="showSimpleInput = false"
-    @submit="simpleInfoSubmit"
-  >
-    <infoForm ref="formRef"></infoForm>
-  </CtDialog> -->
 </template>
 
 <script setup>
@@ -51,7 +40,7 @@ defineOptions({ name: 'inviteConfirm'})
 import { ref, onMounted } from 'vue'
 import phoneFrom from '@/components/VerificationCode'
 import { useRoute } from 'vue-router'; const route = useRoute()
-import { enterpriseInviteRecordConsent, getEnterpriseInfoByCode } from '@/api/recruit/enterprise/enterpriseInvite.js'
+import { enterpriseInvitePersonRecordConsent, getEnterpriseInfoByCode } from '@/api/recruit/enterprise/enterpriseInvite.js'
 import Snackbar from '@/plugins/snackbar'
 import { getToken } from '@/utils/auth'
 import { logout } from '@/api/common'
@@ -88,25 +77,23 @@ const handleLogin = async () => {
     const setInfo = { email: joinPersonEmail, enterpriseId: enterpriseInfo.value.enterpriseName }
     // 如果用户登录后点击关闭填写简易人才信息,再次点击登录仅弹出填写,不再调用登录
     if (copyLoginData === (params.phone + params.code.toString()) && userId) {
-      // getUserBaseInfos(userId)
-      // return
       if (!checkPersonBaseInfo()) { // 强制填写个人信息
         dialogExtend('necessaryInfoDialog', { setInfo }).then(() => {
           join()
         })
-        return
       } else {
         join()
       }
+      return
     }
     if (!params.captchaVerification) {
       phoneRef.value.getCode(true)
       return
     }
+    await logoutFun() // 登录前清楚个人信息
     const data = await useUserStore().handleSmsLogin(params)
     copyLoginData = params.phone + params.code.toString()
     userId = data.userId
-    // getUserBaseInfos(data.userId)
     if (!checkPersonBaseInfo()) { // 强制填写个人信息
       dialogExtend('necessaryInfoDialog', { setInfo }).then(() => {
         join()
@@ -123,37 +110,12 @@ const handleLogin = async () => {
   }
 }
 
-// 查询人才信息
-// const showSimpleInput = ref(false)
-// const getUserBaseInfos = async (userId) => {
-//   loginLoading.value = true
-//   try {
-//     const data = await getBaseInfo({ userId })
-//     if (!data) {
-//       showSimpleInput.value = true; Snackbar.warning('请先完善个人基本信息')
-//       return 
-//     }
-//     const keyArr = ['name', 'phone', 'jobStatus', 'expType', 'eduType'] // 必填人才信息
-//     const simpleInfoReady = Object.keys(data).length && keyArr.every(e => data[e] && data[e] !== 0) // 校验必填人才信息
-//     if (!simpleInfoReady) {
-//       showSimpleInput.value = true; Snackbar.warning('请先完善个人基本信息')
-//       return
-//     }
-//     join()
-//   } catch (error) {
-//     logoutFun()
-//     Snackbar.error('获取用户信息失败! ' + error)
-//   } finally {
-//     loginLoading.value = false
-//   }
-// }
-
 // 调用加入接口
 const join = async () => {
   loginLoading.value = true
   try {
     const params = { code, email: joinPersonEmail }
-    await enterpriseInviteRecordConsent(params)
+    await enterpriseInvitePersonRecordConsent(params)
     joinSuccess.value = true
     Snackbar.success('加入成功')
   } catch (error) {
@@ -168,7 +130,7 @@ const join = async () => {
 }
 
 const logoutFun = async () => {
-  if (!getToken(1)) {
+  if (!getToken(2)) { // 没有求职者token
     localStorage.clear()
     return
   }
@@ -181,21 +143,6 @@ const logoutFun = async () => {
 }
 // logoutFun() // 清除之前的token
 
-// 提交简易人才信息
-// const formRef = ref()
-// const simpleInfoSubmit = async () => {
-//   try {
-//     const obj = await formRef.value.getQuery()
-//     if (!obj) return
-//     await savePersonSimpleInfo(obj)
-//     join()
-//     if (!isMobile.value) await useUserStore().getUserBaseInfos() // 网页打开需更新用户信息
-//     // showSimpleInput.value = false
-//   } catch (error) {
-//     console.error('error', error)
-//   }
-// }
-
 const enterpriseInfo = ref({})
 // 根据邀请码获取企业信息
 const getEnterpriseInfo = async () => {

+ 76 - 24
src/views/recruit/enterprise/talentMap/index.vue

@@ -1,28 +1,80 @@
 <template>
-  <div :class="{'disable': info && info?.entitlement && !info?.entitlement?.personMap}">
-    <v-card class="card-box pa-5" style="height: 100%;">
-      <div class="d-flex flex-column align-center" :class="{'v-center': init}">
-        <TextUI
-          v-model="content"
-          :item="textItem"
-          @keyup.enter="handleConfirm"
-          @appendInnerClick="handleConfirm"
-        ></TextUI>
-        <div class="align-center">
-          <template v-if="init && defaultLabelsShow?.length">
-            <v-btn
-              v-for="(val, index) in defaultLabelsShow" :key="val + index"
-              class="mr-3 my-2 py-0 px-2"
-              density="comfortable"
-              color="primary" variant="tonal"
-              @click="clickChip(index)"
-            >
-              {{ val }}
-            </v-btn>
-            <!-- <v-btn class="ml-0 my-1 py-0 px-2" density="comfortable" variant="tonal" color="error" @click="moreLabels">
-              更多人才标签...
-            </v-btn> -->
-          </template>
+<div>
+  <v-card class="card-box pa-5" style="height: 100%;">
+    <div class="d-flex flex-column align-center" :class="{'v-center': init}">
+      <TextUI
+        v-model="content"
+        :item="textItem"
+        @keyup.enter="handleConfirm"
+        @appendInnerClick="handleConfirm"
+      ></TextUI>
+      <div class="align-center">
+        <template v-if="init && defaultLabelsShow?.length">
+          <v-btn
+            v-for="(val, index) in defaultLabelsShow" :key="val + index"
+            class="mr-3 my-2 py-0 px-2"
+            density="comfortable"
+            color="primary" variant="tonal"
+            @click="clickChip(index)"
+          >
+            {{ val }}
+          </v-btn>
+          <!-- <v-btn class="ml-0 my-1 py-0 px-2" density="comfortable" variant="tonal" color="error" @click="moreLabels">
+            更多人才标签...
+          </v-btn> -->
+        </template>
+      </div>
+    </div>
+    <template v-if="!init">
+      <span class="mr-2 color-666" style="width: 68px; min-width: 68px;">人才标签:</span>
+      <v-btn
+        v-for="(val, index) in chosenLabels" :key="val + index"
+        class="mr-3 my-2 py-0 px-2"
+        density="comfortable"
+        color="primary" variant="tonal"
+      >
+        {{ val }}
+        <v-icon class="ml-1" style="margin-top: 1px;" @click="deleteChip(index)">mdi-close</v-icon>
+      </v-btn>
+      <!-- <v-btn
+        class="mr-3 my-2 py-0 px-0"
+        density="comfortable"
+        color="primary" variant="tonal"
+        @click="moreLabels"
+      >
+        <v-icon>mdi-plus-box</v-icon>
+      </v-btn> -->
+      <v-btn icon="mdi-plus" class="mr-8" variant="outlined" size="x-small" @click="moreLabels"></v-btn>
+      <span v-if="chosenLabels?.length" class="my-2" style="font-size: 14px;cursor: pointer;color: var(--color-999);" @click="resetLabel">清空标签</span>
+    </template>
+    <!-- 人员信息表单 -->
+    <CtTable
+      v-if="dataList?.length && (chosenLabels?.length || content)"
+      class="mt-3"
+      :items="dataList"
+      :headers="headers"
+      :loading="loading"
+      :elevation="0"
+      height="calc(100vh - 380px)"
+      :isTools="false"
+      :showPage="true"
+      :total="total"
+      :page-info="pageInfo"
+      itemKey="id"
+      @pageHandleChange="handleChangePage"
+    >
+      <template #name="{ item }">
+        <div class="d-flex align-center cursor-pointer" @click="talentPoolDetails(item)">
+          <v-badge
+            v-if="item?.sex === '1' || item?.sex === '2'"
+            bordered
+            offset-y="6"
+            :color="badgeColor(item)"
+            :icon="badgeIcon(item)">
+            <v-avatar size="40" :image="getUserAvatar(item.avatar, item.sex)"></v-avatar>
+          </v-badge>
+          <v-avatar v-else size="40" :image="getUserAvatar(item.avatar, item.sex)"></v-avatar>
+          <span class="defaultLink ml-3">{{ item?.name }}</span>
         </div>
       </div>
       <template v-if="!init">

+ 6 - 2
src/views/recruit/enterprise/talentPool/components/details.vue

@@ -1,7 +1,7 @@
 <!-- 人才库 - 人才详情 -->
 <template>
-  <div class="d-flex justify-center mb-8">
-    <div v-if="Object.keys(cvData).length" style="width: 940px;background: #fff;" class="px-8 pb-12 pt-3 my-n3 mr-3">
+  <div v-if="Object.keys(cvData).length" class="d-flex justify-center mb-8">
+    <div style="width: 940px;background: #fff;" class="px-8 pb-12 pt-3 my-n3 mr-3">
       <!-- 基本信息 -->
       <baseInfo class="mt-5" :data="cvData.person"></baseInfo>
       <!-- 个人优势 -->
@@ -54,6 +54,7 @@
       </v-list>
     </div>
   </div>
+  <Loading :visible="loading"></Loading>
 </template>
 
 <script setup>
@@ -85,6 +86,7 @@ const operateItems = [
 
 // 获取人才详情
 const cvData = ref({})
+const loading = ref(false)
 const getCvDetail = async () => {
   const { id } = route.params
   if (!id) {
@@ -94,8 +96,10 @@ const getCvDetail = async () => {
     }, 2000)
     return
   }
+  loading.value = true
   const data = await getPersonCvDetail(id)
   cvData.value = data
+  loading.value = false
 }
 getCvDetail()
 

+ 2 - 2
src/views/recruit/enterprise/talentPool/components/details/jobIntention.vue

@@ -11,11 +11,11 @@
         <div v-if="!k.payFrom && !k.payTo">面议</div>
         <div v-else>{{k.payFrom ? k.payFrom + '-' : ''}}{{k.payTo}}</div>
         <div class="line">|</div>
-        <div class="color-999 text-truncate" style="max-width: 300px;">{{ k.industry?.map(e => e.nameCn).join('、') }}</div>
+        <div class="color-999" v-ellipse-tooltip style="max-width: 300px;">{{ k.industry?.map(e => e.nameCn).join('、') }}</div>
         <div class="line" v-if="k.industry?.length">|</div>
         <div class="color-999">{{ k.jobTypeName }}</div>
         <div class="line" v-if="k.jobTypeName && k.workArea">|</div>
-        <div class="color-999 text-truncate" style="max-width: 160px;">
+        <div v-ellipse-tooltip class="color-999" style="max-width: 160px;">
           {{ k.interestedArea && k.interestedArea.length ? k.workArea + ',' + k.interestedArea.map(e => e.name).join(',') : k.workArea }}
         </div>
       </div>

+ 1 - 1
src/views/recruit/enterprise/tradingOrder/components/pointsAndBalance.vue

@@ -207,7 +207,7 @@ watch(
   border-radius: 6px;
   margin: 0 auto;
   &-left {
-    border: 1px solid #00897B;
+    border: 1px solid #00B760;
     border-radius: 6px;
     padding: 5px;
   }

+ 28 - 2
src/views/recruit/personal/PersonalCenter/index.vue

@@ -44,14 +44,24 @@
       <router-view></router-view>
     </v-card>
   </div>
+
+  <PreviewImage v-if="showPreview" :initialIndex="initialIndex" :urlList="urlsList" @close="handleClosePreview" />
 </template>
 
 <script setup>
 defineOptions({ name: 'person-center'})
-import { computed } from 'vue'
+import { computed, ref } from 'vue'
 import { getCurrentLocaleLang } from '@/utils/lang.js'
 import personCenterRoute from '@/router/modules/components/recruit/personCenter'
 import { useUserStore } from '@/store/user'
+import { usePersonCenterStore } from '@/store/personCenter'
+
+const userStore = useUserStore()
+const info = localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {}
+
+const menuHide = { // 是否隐藏
+  studentInformation: (info?.type !== '1') // 学生信息管理。 type:'0'是求职者,'1'是学生
+}
 
 const info = localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {}
 
@@ -89,7 +99,23 @@ const getList = (arr, obj = []) => {
   return obj
 }
 
-const userStore = useUserStore()
+const personCenterStore = usePersonCenterStore()
+const showPreview = ref(false)
+const initialIndex = ref(0)
+const urlsList = ref([])
+personCenterStore.$subscribe((mutation, state) => {
+  if (state.urlsList.length > 0) {
+    urlsList.value = state.urlsList
+    initialIndex.value = state.initialIndex
+    showPreview.value = true
+  }
+})
+
+const handleClosePreview = () => {
+  personCenterStore.clearPreviewData()
+  showPreview.value = false
+}
+
 // 更新账户信息
 const updateAccountInfo = async () => {
   await userStore.getUserAccountInfo()

+ 18 - 2
src/views/recruit/personal/PersonalCenter/jobFeedback/components/delivery.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <div v-if="items.length">
-      <LongStrip :items="items"></LongStrip>
+      <LongStrip :items="items" :showCancelDeliveryResumeBtn="true" @cancelDeliveryResume="handleCancelDeliveryResume"></LongStrip>
       <CtPagination
         :total="total"
         :page="page.pageNo"
@@ -18,7 +18,9 @@
 defineOptions({ name: 'position-delivery' })
 import { ref } from 'vue'
 import { dealDictObjData } from '@/utils/position'
-import { getJobDeliveryList } from '@/api/position'
+import { getJobDeliveryList, cancelDeliveryResume } from '@/api/position'
+import Confirm from '@/plugins/confirm'
+import Snackbar from '@/plugins/snackbar'
 
 const total = ref(0)
 const items = ref([])
@@ -45,6 +47,20 @@ const handleChangePage = (index) => {
   page.value.pageNo = index
   getPositionList()
 }
+
+// 撤销投递简历
+const handleCancelDeliveryResume = async (id) => {
+  Confirm('系统提示', '是否确认撤销对该职位投递的简历?').then(async () => {
+    try {
+      await cancelDeliveryResume(id)
+      Snackbar.success('撤销投递简历成功')
+      page.value.pageNo = 1
+      getPositionList()
+    } catch {
+      Snackbar.error('撤销投递简历失败,请稍后再试')
+    }
+  })
+}
 </script>
 
 <style scoped lang="scss">

+ 3 - 4
src/views/recruit/personal/PersonalCenter/jobFeedback/components/interested.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <v-divider></v-divider>
-    <v-tabs v-model="tabVal" align-tabs="start" color="primary" bg-color="#fff" @update:modelValue="handleUpdateTab">
+    <v-tabs v-model="tabVal" align-tabs="start" color="primary" bg-color="#fff" @update:modelValue="tabChange">
       <v-tab :value="0">{{  favoriteCount > 0 ? `${$t('position.positionCollection')}(${favoriteCount})` : $t('position.positionCollection') }}</v-tab>
       <v-tab :value="1">{{ subscribeCount > 0 ? `${$t('position.companyCollection')}(${subscribeCount})` : $t('position.companyCollection') }}</v-tab>
     </v-tabs>
@@ -38,9 +38,8 @@ const getSubscribeCount = async () => {
 }
 getSubscribeCount()
 
-// 更新收藏数量
-const handleUpdateTab = (val) => {
-  if (val === 0) getFavoriteCount()
+const tabChange = () => {
+  if (tabVal.value === 0) getFavoriteCount()
   else getSubscribeCount()
 }
 </script>

+ 5 - 6
src/views/recruit/personal/PersonalCenter/jobFeedback/components/interview/item.vue

@@ -9,9 +9,6 @@
         <v-btn class="ml-3" color="error" size="small" @click.stop="handleRefuse(val)">拒绝</v-btn>
       </div>
       <div v-if="val.job.status === '1'" class="font-size-14 header-btn color-error">职位已关闭</div>
-      <!-- <div v-if="tab === '1' || tab === '98'" class="float-right font-size-13" :style="{'padding': '12px 12px 0 0', 'color': tab === '1' ? 'var(--v-primary-base)' : 'var(--v-error-base)'}">
-        您已于{{ timesTampChange(val.updateTime, 'Y-M-D h:m') }}{{ tab === '1' ? '接受' : '拒绝'}}了此面试邀请
-      </div> -->
       <div class="img-box">
           <v-avatar :image="getUserAvatar(val.contact.avatar, val.contact.sex)" size="x-small"></v-avatar>
           <span class="name">
@@ -30,14 +27,14 @@
         </div>
         <div class="job-info color-666">
           <div class="job-name ellipsis" style="max-width: 410px;">
-            <svg-icon v-if="val.jobFairId" name="jobFair" size="18" class="mr-1"></svg-icon>
+            <!-- <svg-icon v-if="val.jobFairId" name="jobFair" size="18" class="mr-1"></svg-icon> -->
             <span class="mr-3" :class="{'cursor-pointer': val.job.status === '0', 'position-name': val.job.status === '0'}" @click.stop="handleToPositionDetails(val)">{{ formatName(val.job.name) }}</span>
             <span v-if="!val.job.payFrom && !val.job.payTo">面议</span>
             <span v-else>{{ val.job.payFrom ? val.job.payFrom + '-' : '' }}{{ val.job.payTo }}{{ val.job.payName ? '/' + val.job.payName : '' }}</span>
           </div>
           <div class="job-other d-flex align-center">
             <div style="width: 30px;height: 30px;">
-              <v-img width="30" height="30" :src="val.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'"></v-img>
+              <v-img width="30" height="30" rounded contain :src="val.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/company-avatar.png'"></v-img>
             </div>
             <div class="ellipsis" style="max-width: 400px;">
               <span class="mx-2 enterprise-name" @click.stop="jumpToEnterpriseDetail(val.enterprise.id, true)">{{ formatName(val.enterprise.anotherName || val.enterprise.name) }}</span>
@@ -85,7 +82,9 @@ const router = useRouter()
 // 职位详情
 const handleToPositionDetails = (item) => {
   if (item.job.status === '1') return
-  router.push(`/recruit/personal/position/details/${item.job.id}`)
+  let path = `/recruit/personal/position/details/${item.job.id}`
+  if (item?.jobFairId) path += `?jobFairId=${item.jobFairId}`
+  router.push(path)
 }
 
 // 同意

+ 1 - 1
src/views/recruit/personal/PersonalCenter/memberBenefits/membershipPackage/index.vue

@@ -1,6 +1,6 @@
 <!-- 购买套餐 -->
 <template>
-  <div class="card-box pa-3">
+  <div class="pa-3">
     <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f7f8fa">
       <v-tab :value="0">套餐列表</v-tab>
     </v-tabs>

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

@@ -170,7 +170,6 @@ const getValue = async () => {
       SnackText = SnackText ? SnackText : query.SnackText
     }
   }
-  // console.log('id:', id)
   if (id) {
     Snackbar.warning(SnackText || '请填写完整后提交!')
     return
@@ -194,11 +193,7 @@ const getValue = async () => {
         obj[newKey] = data[key]
       }
     })
-    // const defaultObj = { avatar: "", person: {}, advantage: { content: '' }, tag: { tagList: [] }, jobInterested: [], eduExp: [], workExp: [], trainExp: [] } // 必传字段
-    // const defaultObj = { avatar: null, person: null, advantage: null, tag: null, jobInterested: null, eduExp: null, workExp: null, trainExp: null } // 必传字段
-    // obj = { ...defaultObj, ...obj }
   }
-  // console.log('123456:', obj)
   return obj && Object.keys(obj).length ? JSON.stringify(obj) : null
 }
 
@@ -210,8 +205,6 @@ const submit = async () => {
     loading.value = true
     await saveResumeInfo(obj)
     await useUserStore().getUserBaseInfos() // 更新用户信息
-    // Snackbar.success(t('common.saveMsg'))
-    // setTimeout(() => { window.location.reload() }, 1000) // 刷新页面
     Confirm('系统提示', `提交成功,是否前往在线简历查看?`, { sureText: '立即前往' }).then(() => {
       window.location.href = '/recruit/personal/personalCenter/resume/online'
     })

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

@@ -20,9 +20,9 @@
         @selected="handleSelected"
       >
         <template #actions="{ item }">
-          <v-btn color="primary" @click.stop="previewFile(item0.url)" variant="text">预览</v-btn>
-          <v-btn color="error" @click.stop="handleDownload(item)" variant="text">下载</v-btn>
-          <v-btn v-if="!item.defaultStatus" color="success" @click.stop="handleDelete(item)" variant="text">{{ $t('common.delete') }}</v-btn>
+          <v-btn color="primary" @click.stop="previewFile(item.url)" variant="text">预览</v-btn>
+          <v-btn color="#00897B" @click.stop="handleDownload(item)" variant="text">下载</v-btn>
+          <v-btn v-if="!item.defaultStatus" color="error" @click.stop="handleDelete(item)" variant="text">{{ $t('common.delete') }}</v-btn>
         </template>
       </CtTable>
       <div v-if="props.analysis" class="d-flex  flex-column align-center mt-15">

+ 1 - 1
src/views/recruit/personal/PersonalCenter/resume/template/index.vue

@@ -60,7 +60,7 @@ const headers = [
 ]
 
 const vip = computed(() => {
-  return userStore.userInfo?.entitlement?.resumeTemplate
+  return userStore.userInfo?.entitlement?.vipFlag && (userStore.userInfo?.vipExpireDate > Date.now()) && userStore.userInfo?.entitlement?.resumeTemplate
 })
 
 

+ 11 - 0
src/views/recruit/personal/PersonalCenter/student/EnterpriseRecommendationLetter/index.vue

@@ -0,0 +1,11 @@
+<template>
+	<div>xxx</div>
+</template>
+
+<script setup>
+defineOptions({ name: 'EnterpriseRecommendationLetter' })
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 11 - 0
src/views/recruit/personal/PersonalCenter/student/InternshipButler/index.vue

@@ -0,0 +1,11 @@
+<template>
+	<div>xxx</div>
+</template>
+
+<script setup>
+defineOptions({ name: 'InternshipButler'})
+</script>
+
+<style scoped lang="scss">
+
+</style>

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác