瀏覽代碼

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

zhengnaiwen_citu 9 月之前
父節點
當前提交
6fd94ee386
共有 100 個文件被更改,包括 1118 次插入432 次删除
  1. 16 0
      src/api/enterprise.js
  2. 0 0
      src/api/recruit/enterprise/talentPool/index.js
  3. 6 6
      src/components/Enterprise/details.vue
  4. 1 1
      src/components/Enterprise/hotPromoted.vue
  5. 6 1
      src/components/FormUI/TextInput/index.vue
  6. 2 0
      src/components/FormUI/textArea/index.vue
  7. 6 1
      src/components/Position/longStrip.vue
  8. 1 1
      src/components/pay/index.vue
  9. 5 1
      src/components/personalRecharge/index.vue
  10. 5 1
      src/components/personalRecharge/initPay.vue
  11. 5 5
      src/layout/company/navBar.vue
  12. 6 3
      src/layout/enterprise.vue
  13. 11 13
      src/layout/personal/navBar.vue
  14. 1 1
      src/layout/personal/slider.vue
  15. 8 4
      src/permission.js
  16. 93 133
      src/router/modules/components/recruit/enterprise.js
  17. 80 14
      src/router/modules/components/recruit/personCenter.js
  18. 0 65
      src/router/modules/components/recruit/personal.js
  19. 9 7
      src/store/user.js
  20. 173 1
      src/utils/headhuntingData.js
  21. 21 14
      src/views/headhunting/components/content.vue
  22. 32 24
      src/views/headhunting/components/serviceContent.vue
  23. 23 7
      src/views/headhunting/details.vue
  24. 83 0
      src/views/headhunting/drill/consultant.vue
  25. 50 0
      src/views/headhunting/drill/service.vue
  26. 1 1
      src/views/integral/pointsManagement/components/integralShow.vue
  27. 2 0
      src/views/login/components/passwordPage.vue
  28. 1 1
      src/views/login/index.vue
  29. 5 3
      src/views/mall/index.vue
  30. 5 1
      src/views/mall/purchasePackage/index.vue
  31. 0 1
      src/views/publicRecruitment/components/table.vue
  32. 12 3
      src/views/publicRecruitment/index.vue
  33. 1 1
      src/views/publicRecruitment/myRecommendation.vue
  34. 24 13
      src/views/recruit/entRegister/register.vue
  35. 0 0
      src/views/recruit/enterprise/entInfoSetting/index.vue
  36. 0 0
      src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/authentication.vue
  37. 0 0
      src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/basicInfo.vue
  38. 2 1
      src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/businessInformation.vue
  39. 0 0
      src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/enterpriseAlbum.vue
  40. 0 0
      src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/enterpriseLabel.vue
  41. 0 0
      src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/enterpriseLogo.vue
  42. 0 0
      src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/welfareLabel.vue
  43. 2 1
      src/views/recruit/enterprise/interviewManagement/index.vue
  44. 1 0
      src/views/recruit/enterprise/invoiceManagement/index.vue
  45. 0 20
      src/views/recruit/enterprise/memberCenter/myAccount/index.vue
  46. 0 0
      src/views/recruit/enterprise/membershipPackage/components/open.vue
  47. 0 0
      src/views/recruit/enterprise/membershipPackage/components/package.vue
  48. 5 4
      src/views/recruit/enterprise/membershipPackage/index.vue
  49. 2 1
      src/views/recruit/enterprise/personnelManagement/components/invite.vue
  50. 2 1
      src/views/recruit/enterprise/personnelManagement/components/screen.vue
  51. 3 1
      src/views/recruit/enterprise/personnelManagement/components/table.vue
  52. 0 13
      src/views/recruit/enterprise/purchasePackage/index.vue
  53. 82 0
      src/views/recruit/enterprise/staffChangePassword/index.vue
  54. 21 1
      src/views/recruit/enterprise/staffInfoSetting/index.vue
  55. 51 0
      src/views/recruit/enterprise/systemManagement/groupAccount/index.vue
  56. 0 0
      src/views/recruit/enterprise/talentMap/components/filter copy.vue
  57. 11 3
      src/views/recruit/enterprise/talentMap/components/filter.vue
  58. 34 10
      src/views/recruit/enterprise/talentMap/index.vue
  59. 0 0
      src/views/recruit/enterprise/talentPool/components/details.vue
  60. 0 0
      src/views/recruit/enterprise/talentPool/components/details/attachmentResume.vue
  61. 3 1
      src/views/recruit/enterprise/talentPool/components/details/baseInfo.vue
  62. 0 0
      src/views/recruit/enterprise/talentPool/components/details/dict.js
  63. 0 0
      src/views/recruit/enterprise/talentPool/components/details/educationExp.vue
  64. 0 0
      src/views/recruit/enterprise/talentPool/components/details/jobIntention.vue
  65. 0 0
      src/views/recruit/enterprise/talentPool/components/details/projectExperience.vue
  66. 0 0
      src/views/recruit/enterprise/talentPool/components/details/trainingExperience.vue
  67. 0 0
      src/views/recruit/enterprise/talentPool/components/details/vocationalSkills.vue
  68. 0 0
      src/views/recruit/enterprise/talentPool/components/details/workExperience.vue
  69. 0 0
      src/views/recruit/enterprise/talentPool/components/filter.vue
  70. 4 4
      src/views/recruit/enterprise/talentPool/index copy.vue
  71. 6 6
      src/views/recruit/enterprise/talentPool/index.vue
  72. 0 0
      src/views/recruit/enterprise/talentRecruitment/components/commonStyle.vue
  73. 2 1
      src/views/recruit/enterprise/talentRecruitment/components/invite.vue
  74. 0 0
      src/views/recruit/enterprise/talentRecruitment/components/public.vue
  75. 0 0
      src/views/recruit/enterprise/talentRecruitment/components/screen.vue
  76. 4 2
      src/views/recruit/enterprise/talentRecruitment/components/table.vue
  77. 0 0
      src/views/recruit/enterprise/talentRecruitment/index.vue
  78. 2 2
      src/views/recruit/enterprise/tradingOrder/components/pointsAndBalance.vue
  79. 0 0
      src/views/recruit/enterprise/tradingOrder/components/public.vue
  80. 2 2
      src/views/recruit/enterprise/tradingOrder/components/trading.vue
  81. 0 0
      src/views/recruit/enterprise/tradingOrder/components/trading/recharge.vue
  82. 0 0
      src/views/recruit/enterprise/tradingOrder/components/trading/transaction.vue
  83. 30 0
      src/views/recruit/enterprise/tradingOrder/index.vue
  84. 14 0
      src/views/recruit/personal/PersonalCenter/bountyRewards/index.vue
  85. 9 4
      src/views/recruit/personal/PersonalCenter/index.vue
  86. 4 5
      src/views/recruit/personal/PersonalCenter/jobFeedback/components/seenMe.vue
  87. 2 2
      src/views/recruit/personal/PersonalCenter/memberBenefits/membershipPackage/index.vue
  88. 0 0
      src/views/recruit/personal/PersonalCenter/memberBenefits/taskCenter/components/daily.vue
  89. 0 0
      src/views/recruit/personal/PersonalCenter/memberBenefits/taskCenter/components/signIn.vue
  90. 0 0
      src/views/recruit/personal/PersonalCenter/memberBenefits/taskCenter/components/suggest.vue
  91. 1 2
      src/views/recruit/personal/PersonalCenter/memberBenefits/taskCenter/index.vue
  92. 100 0
      src/views/recruit/personal/PersonalCenter/resume/attachment/index.vue
  93. 15 2
      src/views/recruit/personal/PersonalCenter/resume/online/components/basicInfo.vue
  94. 0 0
      src/views/recruit/personal/PersonalCenter/resume/online/components/dict.js
  95. 0 0
      src/views/recruit/personal/PersonalCenter/resume/online/components/educationExp.vue
  96. 0 0
      src/views/recruit/personal/PersonalCenter/resume/online/components/jobIntention.vue
  97. 0 0
      src/views/recruit/personal/PersonalCenter/resume/online/components/projectExperience.vue
  98. 0 0
      src/views/recruit/personal/PersonalCenter/resume/online/components/selfEvaluation.vue
  99. 0 0
      src/views/recruit/personal/PersonalCenter/resume/online/components/trainingExperience.vue
  100. 10 16
      src/views/recruit/personal/PersonalCenter/resume/online/components/vocationalSkills.vue

+ 16 - 0
src/api/enterprise.js

@@ -79,6 +79,22 @@ export const saveUserInfo = async (data) => {
   })
 }
 
+// 招聘端-绑定-修改用户邮箱
+export const entUpdateEmail = async (data) => {
+  return await request.put({
+    url: '/app-api/menduner/system/recruit/user/update-email',
+    data
+  })
+}
+
+// 招聘端-重置用户密码
+export const entUpdatePassword = async (data) => {
+  return await request.put({
+    url: '/app-api/menduner/system/recruit/user/update-password',
+    data
+  })
+}
+
 // 招聘端-获取人才的在线简历详情
 export const getPersonCvDetail = async (userId, id) => {
   return await request.get({

+ 0 - 0
src/api/recruit/enterprise/resumeManagement/talentPool/index.js → src/api/recruit/enterprise/talentPool/index.js


+ 6 - 6
src/components/Enterprise/details.vue

@@ -45,22 +45,22 @@
             <recruitmentPositions v-else :info="info"/>
           </div>
           <div class="content-right">
-            <div v-if="info.enterprise?.workTime && info?.enterprise?.welfareList.length" class="welfare mb-3">
+            <div class="welfare mb-3">
               <h4>工作时间及福利</h4>
-              <div v-if="info.enterprise?.workTime" class="my-3" style="color: var(--color-666);font-size: 14px;">
-                <v-icon size="17" color="#ccc" class="mr-2">mdi-clock</v-icon>{{ info.enterprise.workTime }}
+              <div class="my-3" style="color: var(--color-666);font-size: 14px;">
+                <v-icon size="17" color="#ccc" class="mr-2">mdi-clock</v-icon>{{ info.enterprise?.workTime || '暂无' }}
               </div>
               <div class="welfare-tags">
                 <v-chip size="small" label v-for="(k, i) in info?.enterprise?.welfareList?.slice(0, 6)" :key="i" class="mb-2 welfare-tags-item ellipsis" color="primary">{{ k }}</v-chip>
               </div>
             </div>
-            <div class="welfare" v-if="info?.business">
+            <div class="welfare">
               <h4>工商信息</h4>
               <div :class="['mt-2', 'business-item']" v-for="val in businessList" :key="val.value">
                 <div>{{ val.label }}</div>
                 <div class="business-value ellipsis">
-                  {{ info.business[val.value] || '暂无' }}
-                  <span v-if="val.value === 'registeredCapital' && info.business[val.value]">万元</span>
+                  {{ info?.business ? info.business[val.value] : '暂无' }}
+                  <span v-if="info?.business && val.value === 'registeredCapital' && info?.business[val.value]">万元</span>
                 </div>
                 <div :class="['my-3']"></div>
               </div>

+ 1 - 1
src/components/Enterprise/hotPromoted.vue

@@ -18,7 +18,7 @@
             </p>
           </div>
         </div>
-        <div class="px-5 py-1 ellipsis" :style="{'height': '33px', 'border-bottom': item.enterprise.welfareList && item.enterprise.welfareList.length ? '1px solid #EBEBEB;' : 'none'}">
+        <div class="px-5 py-1 ellipsis" :style="{'height': '33px', 'border-bottom': item.enterprise.welfareList && item.enterprise.welfareList.length ? '1px solid #EBEBEB' : 'none'}">
           <span class="welfareTag mr-5" v-for="(k, i) in item.enterprise.welfareList" :key="i">{{ k }}</span>
         </div>
         <!-- 职位列表 -->

+ 6 - 1
src/components/FormUI/TextInput/index.vue

@@ -30,6 +30,7 @@
       @keyup.enter="handleKeyup"
       @click:clear="handleClear"
       @blur="handleBlur"
+      @focus="handleFocus"
     >
       <slot name="default"></slot>
     </v-text-field>
@@ -42,7 +43,7 @@ import { ref, watch } from 'vue';
 defineOptions({ name:'FormUI-v-text-field'})
 
 const props = defineProps({item: Object, modelValue: [String, Number]})
-const emit = defineEmits(['update:modelValue', 'change', 'appendClick', 'appendInnerClick', 'enter', 'blur'])
+const emit = defineEmits(['update:modelValue', 'change', 'appendClick', 'appendInnerClick', 'enter', 'blur', 'focus'])
 const item = props.item
 const value = ref(props.modelValue)
 const searchDebouncedTime = item?.searchDebouncedTime === 0 ? ref(0) : ref(500)
@@ -101,6 +102,10 @@ const handleBlur = () => {
   emit('blur', props.item, value.value)
 }
 
+const handleFocus = () => {
+  emit('focus', props.item, value.value)
+}
+
 const handleWheel = (event, item) => {
   if (item.type !== 'number') return
   event.preventDefault()

+ 2 - 0
src/components/FormUI/textArea/index.vue

@@ -11,6 +11,8 @@
       :validate-on="item.validateOn"
       :rows="item.rows || 3"
       :disabled="item.disabled"
+      :autofocus="item.autofocus"
+      :auto-grow="item.autoGrow"
       :density="item.dense || 'compact'"
       :placeholder="item.placeholder || item.label"
       :no-resize="!item.resize"

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

@@ -31,7 +31,7 @@
             <v-img width="50" height="50" :src="val.enterprise.logoUrl || 'https://minio.citupro.com/dev/menduner/7.png'"></v-img>
           </div>
           <div class="ml-3">
-            <div class="cursor-pointer info-name">{{ val.enterprise.name }}</div>
+            <div class="cursor-pointer info-name" @click="handleToEnterprise(val)">{{ 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] }}
@@ -81,6 +81,11 @@ const handleCancel = async (item) => {
 const handleToPositionDetails = (item) => {
   router.push(`/recruit/personal/position/details/${item.job.id}`)
 }
+
+// 企业详情
+const handleToEnterprise = (item) => {
+  router.push(`/recruit/personal/company/details/${item.enterprise.id}?key=briefIntroduction`)
+}
 </script>
 
 <style scoped lang="scss">

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

@@ -81,7 +81,7 @@ import { createTradeOrder } from '@/api/position'
 import { useSharedState } from '@/store/sharedState'
 import { rechargeOrderCreate } from '@/api/recruit/enterprise/member/points'
 // import { FenYuanTransform } from '@/utils/position'
-import Recharge from '@/views/recruit/enterprise/memberCenter/myMembers/components/pointsAndBalance.vue'
+import Recharge from '@/views/recruit/enterprise/tradingOrder/components/pointsAndBalance.vue'
 
 const emit = defineEmits(['payTypeChange', 'paySuccess', 'stopInterval'])
 const props = defineProps({

+ 5 - 1
src/components/personalRecharge/index.vue

@@ -1,5 +1,9 @@
 <template>
   <div class="resume-box" :class="{'fullShow': fullShow}">
+    <div class="color-primary mb-5 text-decoration-underline cursor-pointer" @click="router.go(-1)">
+      <v-icon>mdi-chevron-triple-left</v-icon>
+      回到我的钱包
+    </div>
     <div class="resume-header" v-if="showTitle">
       <div class="resume-title">
         余额充值
@@ -68,7 +72,7 @@ defineProps({
   }
 })
 
-import { useRoute } from 'vue-router'; const route = useRoute()
+import { useRoute, useRouter } from 'vue-router'; const route = useRoute(); const router = useRouter()
 const fullShow = ref(route.path === '/personalRecharge' ? true : false)
 
 const current = ref(0)

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

@@ -55,6 +55,10 @@ import { setWalletRecharge } from '@/api/recruit/personal/myWallet.js'
 import { onUnmounted, ref, nextTick, watch } from 'vue'
 const emit = defineEmits(['payTypeChange', 'paySuccess', 'stopInterval'])
 const props = defineProps({
+  returnUrl: {
+    type: String,
+    default: '/recruit/personal/personalCenter/wallet'
+  },
   info: {
     type: Object,
     default: () => ({ id: ''})
@@ -101,7 +105,7 @@ const payStatus = async () => {
         // getUnpaidOrderList() // 重新创建新的支付订单
         // 返回指定页面
         if (route.fullPath === props.returnUrl) router.go(0)
-        else if (props.returnUrl) router.push(props.returnUrl)
+        else if (props.returnUrl) router.push(props.returnUrl) // 跳转到充值记录
         Snackbar.success('支付成功')
       }, 2000);
     }

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

@@ -17,7 +17,7 @@
             <v-img @click="enterpriseClick(2)" rounded 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">{{ baseInfo?.enterpriseAnotherName || baseInfo?.enterpriseName || '--' }}</span>
           </div>
-          <div class="ml-3 cursor-pointer border-left border-right px-3 commonHover" @click="router.push('/recruitHome')">我要求职</div>
+          <div class="ml-3 cursor-pointer border-left border-right px-3 commonHover" @click="handleLogout">我要求职</div>
           <div class="cursor-pointer border-right px-3 commonHover" @click="router.push('/recruit/enterprise/hirePosition')">全员猎聘</div>
           <div class="d-flex align-center px-3 border-right">
             <div>{{ $t('enterprise.account.remainingPoints') }}:{{ enterpriseUserAccount?.balance || 0 }}点</div>
@@ -111,17 +111,17 @@ onMounted(() => {
 })
 
 const handleToVip = () => {
-  router.push({ path: '/recruit/enterprise/memberCenter/myMembers' })
+  router.push({ path: '/recruit/enterprise/membershipPackage' })
 }
 
 const handleLogoClick = () => { window.open('/recruitHome') } // 点击logo
 
 const enterpriseClick = (tabKey = 1) => {
-  const path = '/recruit/enterprise/informationManagement/informationSettings'
+  const path = '/recruit/enterprise/entInfoSetting'
   router.push({ path, query: { tabKey } })
 }
 
-// 退出登录
+// 退出登录、切换求职者
 const handleLogout = async () => {
   await userStore.userLogout(2)
   router.push({ path: '/recruitHome' })
@@ -131,7 +131,7 @@ const enterpriseList = ref([])
 const menuList = ref([
   // { title: t('enterprise.account.myAccount'), icon: 'mdi-account', change: () => router.push({ path: '/recruit/enterprise/memberCenter/myAccount' }) },
   // { title: t('vipPackage.purchasePackage'), icon: 'mdi-gift-outline', change: () => window.open('/recruit/enterprise/purchasePackage') },
-  // { title: t('enterprise.personalInformationSettings'), icon: 'mdi-account-cog', change: () => router.push({ path: '/recruit/enterprise/informationSettings' }) },
+  // { title: t('enterprise.personalInformationSettings'), icon: 'mdi-account-cog', change: () => router.push({ path: '/recruit/enterprise/staffInfoSetting' }) },
   // { title: t('setting.switchToOtherCompany'), icon: 'mdi-home-switch', hidden: enterpriseList.value?.length < 2, change: () => handleSwitchToAnotherEnterprise },
   // { title: t('enterprise.registeringNewEnterprise'), icon: 'mdi-home-plus-outline', change: () => handleRegisteringNewEnterprise },
   // { title: t('setting.switchToJobSeeker'), icon: 'mdi-swap-horizontal', change: handleLogout },

+ 6 - 3
src/layout/enterprise.vue

@@ -44,6 +44,7 @@ const key = computed(() => {
 
 const whiteList = [
   '/recruit/enterprise/resumeManagement/talentPool/details/details',
+  '/recruit/enterprise/talentPool/details/details',
   '/recruit/enterprise/purchasePackage',
   '/recruit/enterprise/systemManagement/groupAccount/invite/0',
   '/recruit/enterprise/systemManagement/groupAccount/invite/1'
@@ -66,9 +67,11 @@ const getTitle = (list) => {
     const text = item.meta.title
     const obj = { text,  to: item.path }
     return obj
-  }).filter(e => e)
-  arr[arr.length - 1].disabled = true
-  arr[arr.length - 1].link = true
+  }).filter(e => e) || []
+  if (arr?.length) {
+    arr[arr.length - 1].disabled = true
+    arr[arr.length - 1].link = true
+  }
   breadcrumbs.value = arr
 }
 

+ 11 - 13
src/layout/personal/navBar.vue

@@ -39,9 +39,13 @@
           <div class="d-flex align-center color-333" v-if="getToken()">
             <span class="cursor-pointer mr-5 commonHover" @click="handleSwitch">我要招聘</span>
             <span class="cursor-pointer commonHover" :class="{'active-route': routeActive === 6}" @click="router.push({ path: paths[6] })">
-              现金:{{ userAccount?.balance && userAccount?.balance > 0 ? (userAccount?.balance / 100.0).toFixed(2) : 0 }}
+              <span class="mr-3">
+                现金:{{ userAccount?.balance && userAccount?.balance > 0 ? (userAccount?.balance / 100.0).toFixed(2) : 0 }}
+                <span style="color: #00000000;">1</span>
+                积分:{{ userAccount?.point || 0 }}
+              </span>
+              <span class="mr-3 ml-3"></span>
             </span>
-            <span class="mr-3 ml-3 cursor-pointer commonHover" :class="{'active-route': routeActive === 7}" @click="router.push({ path: paths[7] })">积分:{{ userAccount?.point || 0 }}</span>
 
             <v-menu open-on-hover>
               <template v-slot:activator="{ props }">
@@ -100,10 +104,10 @@
     </v-toolbar>
 
     <CtDialog :visible="show" title="请选择要切换的公司账号" :footer="true" widthType="2" @close="show = false" @submit="switchSubmit">
-      <v-radio-group v-model="radios">
+      <v-radio-group v-model="radios" :hide-details="true">
         <v-radio v-for="item in enterpriseList" :key="item.enterpriseId" color="primary" :label="item.enterpriseName" :value="item.enterpriseId"></v-radio>
       </v-radio-group>
-      <v-btn :loading="loading1" class="mt-2 mb-3" style="width: 100%;" color="primary" variant="tonal" @click="newRegistering">{{ btnType ? '注册新企业' : '查看申请进度' }}</v-btn>
+      <v-btn :loading="loading1" class="ml-6 mb-3" color="primary" variant="text" @click="newRegistering">{{ btnType ? '注册新企业' : '查看申请进度' }}</v-btn>
     </CtDialog>
   </div>
 </template>
@@ -152,8 +156,7 @@ const paths = [ // 有选中样式-路由列表
   '/recruit/personal/message', // 3
   '/recruit/personal/myWallet', // 4
   '/recruit/personal/personalCenter', // 5
-  '/recruit/personal/myWallet/myBalance', // 6
-  '/recruit/personal/myWallet/myIntegral', // 7
+  '/recruit/personal/personalCenter/wallet', // 6
 ]
 
 const navList = [
@@ -243,18 +246,13 @@ const handleToPersonalCenter = () => {
 // 退出登录
 const handleLogout = async () => {
   await userStore.userLogout(1)
+  showBall.value = false
   if (route.path === '/recruitHome') location.reload()
   else router.push({ path: '/recruitHome' })
 }
 
 const items = ref([
-  // { title: t('vipPackage.purchasePackage'), icon: 'mdi-gift-outline', change: () => router.push({ path: '/recruit/personal/purchasePackage' }) },
-  // { title: t('resume.onlineResume'), icon: 'mdi-list-box-outline', change: () => router.push({ path: '/recruit/personal/resume' }) },
-  // { title: t('setting.accountSettings'), icon: 'mdi-cog-outline', change: () => router.push({ path: '/recruit/personal/accountSettings/accountBinding' }) },
-  // { title: t('setting.switchToRecruit'), icon: 'mdi-swap-horizontal', change: handleSwitch },
-  { title: t('publicRecruitment.bountyRewards'), icon: 'mdi-google-circles-extended', change: () => router.push({ path: '/publicRecruitment' }) },
-  // { title: t('points.wallet'), icon: ' mdi-database-outline', change: () => router.push({ path: '/recruit/personal/myWallet' }) },
-  // { title: t('taskCenter.taskCenter'), icon: 'mdi-calendar-check-outline', change: () => router.push({ path: '/recruit/personal/TaskCenter' }) },
+  { title: t('setting.accountSettings'), icon: 'mdi-cog-outline', change: () => router.push({ path: '/recruit/personal/personalCenter/accountSettings' }) },
   { title: t('setting.logOut'), icon: 'mdi-logout', change: handleLogout }
 ])
 

+ 1 - 1
src/layout/personal/slider.vue

@@ -24,7 +24,7 @@ const list = [
   { mdi: 'mdi-arrow-up-bold', tips: '返回顶部' },
   { mdi: 'mdi-qrcode', tips: '微信公众号', showImg: 'https://minio.citupro.com/dev/static/mendunerCode.jpg' },
   { mdi: 'mdi-bell-outline', tips: '消息', path: '/recruit/personal/message' },
-  { mdi: 'mdi-list-box-outline', tips: '在线简历', path: '/recruit/personal/resume' }
+  { mdi: 'mdi-list-box-outline', tips: '在线简历', path: '/recruit/personal/personalCenter/resume/online' }
 ]
 
 const handleClick = (item, index) => {

+ 8 - 4
src/permission.js

@@ -18,16 +18,20 @@ router.beforeEach(async (to, from, next) => {
   start()
   // loadStart()
   if (to.path === '/enterpriseVerification') {
-    useUserStore().changeRole()
+    const res = JSON.parse(localStorage.getItem('emailLoginInfo') || "false")
+    const obj = res ? { ...res, type: 'emailLogin' } : {}
+    useUserStore().changeRole(obj)
     next()
   } else if (getToken(tokenIndex)) {
     // 企业信息完成度提示
     if (localStorage.getItem('checkEnterpriseBaseInfoFalseHref')) {
       const href = localStorage.getItem('checkEnterpriseBaseInfoFalseHref')
       localStorage.setItem('checkEnterpriseBaseInfoFalseHref', '')
-      Confirm('系统提示', '企业信息设置未完善,是否前往完善?').then(() => {
-        window.location.href = href
-      })
+      setTimeout(() => {
+        Confirm('系统提示', '企业信息设置未完善,是否前往完善?').then(() => {
+          window.location.href = href
+        })
+      }, 4000);
     }
     if (to.path === '/login') {
       next({ path: '/recruitHome' })

+ 93 - 133
src/router/modules/components/recruit/enterprise.js

@@ -18,45 +18,69 @@ const enterprise = [
   {
     path: '/recruit/enterprise',
     show: true,
-    redirect: '/recruit/enterprise/resumeManagement/talentPool',
+    redirect: '/recruit/enterprise/talentRecruitment',
   },
   {
-    path: '/recruit/enterprise/resumeManagement',
-    redirect: '/recruit/enterprise/resumeManagement/talentPool',
     component: Layout,
-    name: 'Resume Management',
+    name: 'Talent Recruitment',
     meta: {
-      title: '简历管理',
-      enName: 'Resume Management',
+      title: '人才招聘',
+      enName: 'Talent Recruitment',
       icon: 'mdi-account-settings-outline'
     },
     children: [
       {
-        path: '/recruit/enterprise/resumeManagement/talentPool',
+        path: '/recruit/enterprise/talentRecruitment',
         meta: {
-          title: '人才库',
-          enName: 'Talent Pool'
+          title: '简历管理',
+          enName: 'Resume Management'
         },
-        component: () => import('@/views/recruit/enterprise/resumeManagement/talentPool/index.vue')
+        component: () => import('@/views/recruit/enterprise/talentRecruitment/index.vue')
       },
       {
-        path: '/recruit/enterprise/resumeManagement/talentPool/details/:id',
+        path: '/recruit/enterprise/chatTools',
+        meta: {
+          title: '在线沟通',
+          enName: 'Online Communication',
+        },
+        component: () => import('@/views/recruit/enterprise/chatTools/index.vue')
+      },
+      {
+        path: '/recruit/enterprise/interviewManagement',
+        meta: {
+          title: '面试管理',
+          enName: 'Interview management',
+          // icon: 'mdi-account-multiple-check'
+        },
+        component: () => import('@/views/recruit/enterprise/interviewManagement/index.vue')
+      }
+    ]
+  },
+  {
+    path: '/recruit/enterprise/talentPool',
+    component: Layout,
+    name: 'Elite Reserve',
+    meta: {
+      title: '精英储备',
+      enName: 'Job Management',
+      icon: 'mdi-account-supervisor-circle'
+    },
+    children: [
+      {
+        path: '/recruit/enterprise/talentPool',
+        show: true,
+        component: () => import('@/views/recruit/enterprise/talentPool/index.vue')
+      },
+      {
+        path: '/recruit/enterprise/talentPool/details/:id',
         show: true, // 侧边栏不展示
-        component: () => import('@/views/recruit/enterprise/resumeManagement/talentPool/components/details'),
+        component: () => import('@/views/recruit/enterprise/talentPool/components/details'),
         name: 'talentPoolDetails',
         meta: {
           title: '人才详情',
           hideSide: true
         }
       },
-      {
-        path: '/recruit/enterprise/resumeManagement/elite',
-        meta: {
-          title: '精英人才',
-          enName: 'Elite talents'
-        },
-        component: () => import('@/views/recruit/enterprise/resumeManagement/elite/index.vue')
-      }
     ]
   },
   {
@@ -96,23 +120,6 @@ const enterprise = [
       }
     ]
   },
-  {
-    path: '/recruit/enterprise/resumeManagement/talentMap',
-    component: Layout,
-    name: 'Talent Map',
-    meta: {
-      title: '人才地图',
-      enName: 'Talent Map',
-      icon: 'mdi-map-check'
-    },
-    children: [
-      {
-        path: '/recruit/enterprise/resumeManagement/talentMap',
-        show: true,
-        component: () => import('@/views/recruit/enterprise/resumeManagement/talentMap/index.vue')
-      }
-    ]
-  },
   {
     path: '/recruit/enterprise/hirePosition',
     component: Layout,
@@ -150,36 +157,19 @@ const enterprise = [
     ]
   },
   {
-    path: '/recruit/enterprise/chatTools',
+    path: '/recruit/enterprise/talentMap',
     component: Layout,
-    name: 'chatTools',
-    meta: {
-      title: '聊天工具',
-      enName: 'Chat Tools',
-      icon: 'mdi-bell-outline'
-    },
-    children: [
-      {
-        path: '/recruit/enterprise/chatTools',
-        show: true,
-        component: () => import('@/views/recruit/enterprise/chatTools/index.vue')
-      }
-    ]
-  },
-  {
-    path: '/recruit/enterprise/interviewManagement',
-    component: Layout,
-    name: 'interviewManagement',
+    name: 'Talent Map',
     meta: {
-      title: '面试管理',
-      enName: 'Interview management',
-      icon: 'mdi-account-multiple-check'
+      title: '人才地图',
+      enName: 'Talent Map',
+      icon: 'mdi-map-check'
     },
     children: [
       {
-        path: '/recruit/enterprise/interviewManagement',
+        path: '/recruit/enterprise/talentMap',
         show: true,
-        component: () => import('@/views/recruit/enterprise/interviewManagement/index.vue')
+        component: () => import('@/views/recruit/enterprise/talentMap/index.vue')
       }
     ]
   },
@@ -200,36 +190,6 @@ const enterprise = [
       }
     ]
   },
-  {
-    path: '/recruit/enterprise/informationManagement',
-    component: Layout,
-    redirect: '/recruit/enterprise/informationManagement/informationSettings',
-    name: 'informationManagement',
-    meta: {
-      title: '信息管理',
-      enName: 'Information Management',
-      icon: 'mdi-tune'
-    },
-    children: [
-      {
-        path: '/recruit/enterprise/informationManagement/informationSettings',
-        meta: {
-          title: '企业信息设置',
-          isAdmin: true,
-          enName: 'Enterprise Information Settings'
-        },
-        component: () => import('@/views/recruit/enterprise/informationManagement/informationSettings.vue')
-      },
-      {
-        path: '/recruit/enterprise/informationSettings',
-        meta: {
-          title: '个人信息设置',
-          enName: 'Personal information settings'
-        },
-        component: () => import('@/views/recruit/enterprise/informationSetting/index.vue')
-      }
-    ]
-  },
   {
     path: '/recruit/enterprise/systemManagement',
     component: Layout,
@@ -242,6 +202,7 @@ const enterprise = [
       isAdmin: true // 企业管理员菜单
     },
     children: [
+      // 集团 
       {
         path: '/recruit/enterprise/systemManagement/groupAccount',
         meta: {
@@ -268,6 +229,8 @@ const enterprise = [
       //   },
       //   component: () => import('@/views/recruit/enterprise/systemManagement/userManagement/index.vue')
       // },
+
+      // 岗位管理 
       {
         path: '/recruit/enterprise/systemManagement/postManagement',
         meta: {
@@ -292,70 +255,67 @@ const enterprise = [
         },
         component: () => import('@/views/recruit/enterprise/systemManagement/postManagement/save.vue')
       },
+      // 企业信息 
+      {
+        path: '/recruit/enterprise/entInfoSetting',
+        meta: {
+          title: '企业信息',
+          enName: 'Group Account '
+        },
+        component: () => import('@/views/recruit/enterprise/entInfoSetting/index.vue')
+      },
+      // 员工信息 
+      {
+        path: '/recruit/enterprise/staffInfoSetting',
+        meta: {
+          title: '员工信息',
+          enName: 'Group Account '
+        },
+        component: () => import('@/views/recruit/enterprise/staffInfoSetting/index.vue')
+      },
+      {
+        path: '/recruit/enterprise/staffChangePassword',
+        show: true,
+        meta: {
+          title: '修改登录密码',
+        },
+        component: () => import('@/views/recruit/enterprise/staffChangePassword/index.vue')
+      },
     ]
   },
   {
-    path: '/recruit/enterprise/memberCenter',
     component: Layout,
-    redirect: '/recruit/enterprise/memberCenter/myMembers',
     name: 'enterpriseMemberCenter',
     meta: {
-      title: '会员中心',
+      title: '财务中心',
       enName: 'system Management',
       icon: 'mdi-account',
       // isAdmin: true // 企业管理员菜单
     },
     children: [
       {
-        path: '/recruit/enterprise/memberCenter/myMembers',
+        path: '/recruit/enterprise/membershipPackage',
         meta: {
-          title: '我的会员',
-          enName: 'My Members'
+          title: '会员权益',
+          enName: 'Membership Benefits'
         },
-        component: () => import('@/views/recruit/enterprise/memberCenter/myMembers/index.vue')
+        component: () => import('@/views/recruit/enterprise/membershipPackage/index.vue')
       },
-      // {
-      //   path: '/recruit/enterprise/memberCenter/myAccount',
-      //   meta: {
-      //     title: '我的账户',
-      //     enName: 'Account '
-      //   },
-      //   component: () => import('@/views/recruit/enterprise/memberCenter/myAccount/index.vue')
-      // },
       {
-        path: '/recruit/enterprise/memberCenter/tradingOrder',
+        path: '/recruit/enterprise/tradingOrder',
         meta: {
-          title: '我的订单',
-          enName: 'tradingOrder '
+          title:'订单管理',
+          enName: 'Order management'
         },
-        component: () => import('@/views/recruit/enterprise/memberCenter/tradingOrder/index.vue')
+        component: () => import('@/views/recruit/enterprise/tradingOrder/index.vue')
       },
       {
-        path: '/recruit/enterprise/memberCenter/invoiceHeader',
+        path: '/recruit/enterprise/invoiceManagement',
         meta: {
-          title: '发票抬头管理',
-          enName: 'InvoiceHeaderManagement '
-        },
-        component: () => import('@/views/recruit/enterprise/memberCenter/invoice/header.vue')
-      }
-    ]
-  },
-  {
-    path: '/recruit/enterprise/purchasePackage',
-    component: Layout,
-    name: 'enterprisePurchasePackage',
-    show: true,
-    meta: {
-      title: '购买套餐',
-    },
-    children: [
-      {
-        path: '/recruit/enterprise/purchasePackage',
-        component: () => import('@/views/recruit/enterprise/purchasePackage/index'),
-        meta: {
-          title: '购买套餐',
-          hideSide: true
+          title: '发票管理',
+          enName: 'Invoice Management '
         },
+        component: () => import('@/views/recruit/enterprise/invoiceManagement/index.vue')
       }
     ]
   }

+ 80 - 14
src/router/modules/components/recruit/personCenter.js

@@ -15,6 +15,7 @@ const personCenter = [
         redirect: '/recruit/personal/personalCenter/jobFeedback',
         component: () => import('@/views/recruit/personal/PersonalCenter/index'),
         children: [
+          // 求职反馈
           {
             path: '/recruit/personal/personalCenter/jobFeedback',
             name: 'Job Feedback',
@@ -25,26 +26,87 @@ const personCenter = [
               icon: 'mdi-account-sync-outline'
             }
           },
+          // 简历管理
+          {
+            path: '/recruit/personal/personalCenter/resume',
+            redirect: '/recruit/personal/personalCenter/resume/online',
+            name: 'Resume Management',
+            meta: {
+              title: '简历管理',
+              enName: 'Resume Management',
+              icon: 'mdi-list-box-outline'
+            },
+            children: [
+              {
+                path: '/recruit/personal/personalCenter/resume/online',
+                component: () => import('@/views/recruit/personal/PersonalCenter/resume/online/index.vue'),
+                meta: {
+                  enName: 'Online Resume',
+                  title: '在线简历'
+                }
+              },
+              {
+                path: '/recruit/personal/personalCenter/resume/attachment',
+                component: () => import('@/views/recruit/personal/PersonalCenter/resume/attachment/index.vue'),
+                meta: {
+                  enName: 'Attachment Resume',
+                  title: '附件简历'
+                }
+              }
+            ]
+          },
+          // 赏金奖励
+          {
+            path: '/recruit/personal/personalCenter/bountyRewards',
+            name: 'bountyRewards',
+            component: () => import('@/views/recruit/personal/PersonalCenter/bountyRewards/index.vue'),
+            meta: {
+              title: '赏金奖励',
+              enName: 'Bounty Rewards',
+              icon: 'mdi-octagram-outline'
+            }
+          },
+          // 我的钱包
           {
             path: '/recruit/personal/personalCenter/wallet',
             name: 'My Wallet',
-            component: () => import('@/views/recruit/personal/PersonalCenter/wallet/index.vue'),
             meta: {
               title: '我的钱包',
               enName: 'My Wallet',
               icon: 'mdi-wallet-outline'
-            }
+            },
+            component: () => import('@/views/recruit/personal/PersonalCenter/wallet/index.vue'),
           },
+          // 会员福利
           {
-            path: '/recruit/personal/personalCenter/taskCenter',
-            name: 'Task Center',
-            component: () => import('@/views/recruit/personal/PersonalCenter/taskCenter/index.vue'),
+            path: '/recruit/personal/personalCenter/memberBenefits',
+            redirect: '/recruit/personal/personalCenter/memberBenefits/taskCenter',
+            name: 'Member Benefits',
             meta: {
-              title: '任务中心',
-              enName: 'Task Center',
-              icon: 'mdi-movie-star-outline'
-            }
+              title: '会员福利',
+              enName: 'Member Benefits',
+              icon: 'mdi-gamepad-circle-outline'
+            },
+            children: [
+              {
+                path: '/recruit/personal/personalCenter/memberBenefits/taskCenter',
+                component: () => import('@/views/recruit/personal/PersonalCenter/memberBenefits/taskCenter/index.vue'),
+                meta: {
+                  enName: 'Task Center',
+                  title: '任务中心'
+                }
+              },
+              {
+                path: '/recruit/personal/personalCenter/memberBenefits/membershipPackage',
+                component: () => import('@/views/recruit/personal/PersonalCenter/memberBenefits/membershipPackage/index.vue'),
+                meta: {
+                  enName: 'Membership Package',
+                  title: '会员套餐'
+                }
+              }
+            ]
           },
+          // 账号设置
           {
             path: '/recruit/personal/personalCenter/accountSettings',
             redirect: '/recruit/personal/personalCenter/accountSettings/editPassword',
@@ -59,28 +121,32 @@ const personCenter = [
                 path: '/recruit/personal/personalCenter/accountSettings/editPassword',
                 component: () => import('@/views/recruit/personal/PersonalCenter/accountSettings/editPassword'),
                 meta: {
-                  title: '修改密码'
+                  title: '修改密码',
+                  enName: 'Edit Password'
                 }
               },
               // {
               //   path: '/recruit/personal/personalCenter/accountSettings/accountBinding',
               //   component: () => import('@/views/recruit/personal/PersonalCenter/accountSettings/accountBinding'),
               //   meta: {
-              //     title: '账号绑定'
+              //     title: '账号绑定',
+              //     enName: 'Account binding'
               //   }
               // },
               // {
               //   path: '/recruit/personal/personalCenter/accountSettings/realAuthentication',
               //   component: () => import('@/views/recruit/personal/PersonalCenter/accountSettings/realAuthentication'),
               //   meta: {
-              //     title: '实名认证'
+              //     title: '实名认证',
+              //     enName: 'Real name authentication'
               //   }
               // },
               // {
               //   path: '/recruit/personal/personalCenter/accountSettings/privacySettings',
               //   component: () => import('@/views/recruit/personal/PersonalCenter/accountSettings/privacySettings'),
               //   meta: {
-              //     title: '隐私设置'
+              //     title: '隐私设置',
+              //     enName: 'Privacy settings'
               //   }
               // }
             ]
@@ -88,7 +154,7 @@ const personCenter = [
         ]
       }
     ]
-  },
+  }
 ]
 
 export default personCenter

+ 0 - 65
src/router/modules/components/recruit/personal.js

@@ -4,20 +4,6 @@ import Layout from '@/layout'
 import personCenter from './personCenter'
 
 const personal = [
-  {
-    path: '/recruit/personal/resume',
-    component: Layout,
-    name: 'resume',
-    meta: {
-      title: '在线简历'
-    },
-    children: [
-      {
-        path: '/recruit/personal/resume',
-        component: () => import('@/views/recruit/personal/remuse/index.vue')
-      }
-    ]
-  },
   // {
   //   path: '/recruit/personal/personalCenter',
   //   component: Layout,
@@ -39,23 +25,6 @@ const personal = [
   //     }
   //   ]
   // },
-  {
-    path: '/publicRecruitment',
-    component: Layout,
-    name: 'myPublicRecruitment',
-    meta: {
-      title: '赏金奖励'
-    },
-    children: [
-      {
-        path: '/publicRecruitment',
-        component: () => import('@/views/publicRecruitment/index'),
-        meta: {
-          title: '赏金奖励'
-        },
-      }
-    ]
-  },
   {
     path: '/recruit/personal/myWallet',
     component: Layout,
@@ -94,40 +63,6 @@ const personal = [
       },
     ]
   },
-  {
-    path: '/recruit/personal/TaskCenter',
-    component: Layout,
-    name: 'personalTaskCenter',
-    meta: {
-      title: '任务中心'
-    },
-    children: [
-      {
-        path: '/recruit/personal/TaskCenter',
-        component: () => import('@/views/recruit/personal/taskCenter/index'),
-        meta: {
-          title: '任务中心'
-        },
-      }
-    ]
-  },
-  {
-    path: '/recruit/personal/purchasePackage',
-    component: Layout,
-    name: 'personalPurchasePackage',
-    meta: {
-      title: '购买套餐'
-    },
-    children: [
-      {
-        path: '/recruit/personal/purchasePackage',
-        component: () => import('@/views/recruit/personal/purchasePackage'),
-        meta: {
-          title: '购买套餐'
-        }
-      }
-    ]
-  },
   {
     path: '/integral/personalIntegralRules',
     component: Layout,

+ 9 - 7
src/store/user.js

@@ -56,8 +56,10 @@ export const useUserStore = defineStore('user',
           data.account = data.phone
           passwordLogin(data).then(res => {
             if (data.isEnterprise) { // 企业邮箱登录
-              this.changeRole('emailLogin', res)
+              localStorage.setItem('emailLoginInfo', JSON.stringify(res))
+              window.location.href = '/enterpriseVerification'
             } else {
+              // 个人手机号登录
               setToken(res.accessToken)
               setRefreshToken(res.refreshToken)
               this.accountInfo = res
@@ -122,9 +124,9 @@ export const useUserStore = defineStore('user',
         localStorage.clear()
       },
       // 切换为招聘者
-      async changeRole (type, res) {
+      async changeRole (res) {
         let data
-        if (type === 'emailLogin') {
+        if (res?.type === 'emailLogin') {
           data = res
         } else {
           // 先退出个人登录
@@ -140,9 +142,9 @@ export const useUserStore = defineStore('user',
         await this.getEnterpriseInfo()
         await this.getEnterpriseUserAccountInfo()
         updateEventList(false)
-        Snackbar.success('切换成功')
+        Snackbar.success(res?.type === 'emailLogin' ? '登录成功' : '切换成功')
         await this.checkEnterpriseBaseInfo() // 校验企业必填信息
-        window.location.href = '/enterprise'
+        setTimeout(() => { window.location.href = '/enterprise' }, 1000)
       },
       // 获取当前登录的企业用户信息
       async getEnterpriseInfo () {
@@ -171,10 +173,10 @@ export const useUserStore = defineStore('user',
           const data = await getEnterpriseBaseInfo()
           // 检验必填信息
           const keyArr = ['industryId', 'financingStatus', 'scale', 'introduce', 'logoUrl'] // 必填信息列表
-          let href = '/recruit/enterprise/informationManagement/informationSettings'
+          let href = '/recruit/enterprise/entInfoSetting'
           const valid = Object.keys(data).length && keyArr.every(e => {
             const bool = data[e] && data[e] !== 0
-            if (!bool && e === 'logoUrl') href = '/recruit/enterprise/informationManagement/informationSettings?tabKey=2'
+            if (!bool && e === 'logoUrl') href = '/recruit/enterprise/entInfoSetting?tabKey=2'
             return bool
           })
           if (!valid) {

+ 173 - 1
src/utils/headhuntingData.js

@@ -2,6 +2,7 @@
 export const serviceData = [
   {
     title: '高级管理精英甄选',
+    id: 'selection-senior-management-elite',
     startDesc: '我们深知企业在转型升级过程中,高级管理人才的重要性。因此,门墩儿猎头凭借丰富的行业经验、专业的人才筛选能力和严谨的服务态度,为企业量身定制高级管理人才解决方案。我们的高级管理精英甄选业务涵盖各行各业,包括但不限于金融、互联网、房地产、制造业等领域。我们通过深入了解企业发展战略、企业文化及岗位需求,运用先进的猎头技术和丰富的人才资源库,为企业精准挖掘、评估和推荐具备以下特质的高级管理人才:',
     endDesc: '门墩儿猎头始终秉承“客户至上、诚信为本”的服务宗旨,以专业、高效、贴心的服务,为企业输送了一批又一批高级管理精英,助力企业实现战略目标。我们相信,在门墩儿猎头的助力下,企业将不断迈向新的征程。',
     children: [
@@ -13,7 +14,8 @@ export const serviceData = [
   },
   {
     title: '人才发展储备策略',
-    startDesc: '我们深知企业在转型升级过程中,高级管理人才的重要性。因此,门墩儿猎头凭借丰富的行业经验、专业的人才筛选能力和严谨的服务态度,为企业量身定制高级管理人才解决方案。我们的高级管理精英甄选业务涵盖各行各业,包括但不限于金融、互联网、房地产、制造业等领域。我们通过深入了解企业发展战略、企业文化及岗位需求,运用先进的猎头技术和丰富的人才资源库,为企业精准挖掘、评估和推荐具备以下特质的高级管理人才:',
+    id: 'talent-development-reserve-strategy',
+    startDesc: '门墩儿猎头深知企业在长远发展过程中,人才储备的重要性。因此,我们为企业量身打造了一套全面的人才发展储备策略,旨在帮助企业构建坚实的人才梯队,确保企业在不同发展阶段都能拥有充足的人才支持。',
     startTitle: '门墩儿猎头的人才发展储备策略主要包括以下几个方面:',
     endDesc: '通过门墩儿猎头的人才发展储备策略,企业不仅能够有效应对人才流失的风险,还能在激烈的市场竞争中保持核心竞争力,实现可持续发展。我们致力于成为企业信赖的人才战略合作伙伴,为企业的发展壮大提供源源不断的动力。',
     children: [
@@ -26,6 +28,7 @@ export const serviceData = [
   },
   {
     title: '管理者能力提升方案',
+    id: 'manager-capability-enhancement-plan',
     startDesc: '门墩儿猎头深刻认识到,优秀的管理者是推动企业持续发展的关键因素。为此,我们精心打造了一套管理者能力提升方案,旨在帮助企业提升管理团队的整体素质,增强企业的管理效能,从而在复杂多变的市场环境中保持领先地位。',
     startTitle: '门墩儿猎头的管理者能力提升方案包括以下核心内容:',
     endDesc: '门墩儿猎头的管理者能力提升方案,不仅帮助企业培养了一批具有国际视野和本土实战经验的管理人才,还为企业注入了强大的管理动力,助力企业在激烈的市场竞争中保持领先地位,实现长远发展。我们坚信,通过门墩儿猎头的专业服务,企业的管理团队将不断迈向新的高度。',
@@ -39,6 +42,7 @@ export const serviceData = [
   },
   {
     title: '卓越成长轨迹',
+    id: 'excellent-growth-trajectory',
     startDesc: '门墩儿猎头的卓越成长轨迹服务,是一项旨在帮助企业规划和实现关键人才职业发展路径的全方位服务。该服务通过精心设计的人才成长路线图,助力企业培育和保留核心人才,确保企业在不断变化的市场环境中保持竞争优势和持续发展的动力。',
     startTitle: '以下是卓越成长轨迹服务的详细描述:',
     endDesc: '门墩儿猎头的卓越成长轨迹服务,不仅为企业打造了一支高素质、高效率的人才队伍,也为企业创造了一个持续学习、不断进步的组织文化。我们的服务帮助企业实现人才与企业共同成长,为企业的长远发展奠定了坚实的人才基础。',
@@ -53,6 +57,7 @@ export const serviceData = [
   },
   {
     title: '高管信誉与资质验证',
+    id: 'senior-management-elite-verification',
     startDesc: '门墩儿猎头深知企业在招募高级管理人员时,高管信誉与资质的重要性。为此,我们推出了高管信誉与资质验证服务,这是一项专业的背景调查和资质审核服务,旨在为企业提供准确、可靠的高管候选人评估,确保企业能够招募到具备优秀职业道德和过硬专业能力的高层领导。',
     startTitle: '以下是高管信誉与资质验证服务的详细描述:',
     endDesc: '门墩儿猎头的高管信誉与资质验证服务,为企业招募高管提供了坚实的安全保障,帮助企业规避了因高管个人问题带来的风险,确保了企业高层管理的稳定性和企业声誉的完整性。通过我们的专业服务,企业能够更加放心地选拔和任用高层管理人才,为企业的长远发展保驾护航。',
@@ -64,5 +69,172 @@ export const serviceData = [
       '5. 综合报告:门墩儿猎头根据调查和审核结果,提供一份详尽的综合报告,为企业决策提供客观、全面的参考依据。',
       '6. 法律合规性:我们的服务严格遵守相关法律法规,确保调查过程的合法性、合规性,保护候选人的隐私权益。'
     ]
+  },
+  {
+    title: '酒店集团总部',
+    id: 'hotel-group-headquarters',
+    startDesc: '酒店集团总部致力于品牌建设和文化传承,通过打造独特的品牌形象和企业文化,提升集团的竞争力和影响力。',
+    endDesc: '总部作为整个酒店集团的核心管理机构,在战略规划、资源调配、协调控制、品牌管理等方面发挥着重要作用。通过不断优化组织架构、提升管理水平、加强品牌建设和文化传承等措施,酒店集团总部能够推动集团的持续发展和壮大。',
+    children: [
+      '1、在品牌建设方面,总部会注重品牌的宣传和推广,提升品牌的知名度和美誉度;在文化传承方面,总部会注重企业文化的建设和传播,营造积极向上的工作氛围和团队精神。',
+      '2、集团总部通过制定标准化的运营流程和管理制度,确保各分店在运营过程中能够遵循统一的标准和要求。',
+      '3、同时,总部还会对各分店进行定期的培训和指导,提升分店的管理水平和服务质量。',
+      '4、在运营管理方面,酒店集团总部注重数据分析和市场反馈,及时调整经营策略和资源配置,以适应市场变化和客户需求。',
+      '5、此外,总部还会加强与各分店之间的沟通和协作,确保信息的畅通和资源的共享。'
+    ]
+  },
+  {
+    title: '业主公司',
+    id: 'owner-company',
+    startDesc: '随着市场的不断变化,业主公司需要密切关注行业动态,如政策调整、市场需求变化等,以便及时调整经营策略。近年来服务行业逐渐从规模扩张转向高质量发展,业主公司可以注重提升服务质量、优化存量管理,以满足市场需求。',
+    endDesc: '因此针对不同岗位的需求,提供定期的专业技能培训课程,确保员工具备行业所需的专业知识和技能。随着公司业务的多元化发展,跨领域人才的需求也越来越大。鼓励员工跨部门学习和交流,培养具备多领域知识和技能的复合型人才;同时,也可以引进外部专业人才,为公司注入新的活力和创新思维。',
+    children: [
+      '1、为了降低单一业务带来的风险,业主公司可以考虑多元化发展策略,如涉足物业管理、商业运营、社区服务等多个领域。',
+      '2、随着科技的进步,数字化转型已成为企业发展的必然趋势。业主公司可以加强信息化建设,利用大数据、人工智能等技术提升管理效率和服务水平。',
+      '3、因此针对不同岗位的需求,提供定期的专业技能培训课程,确保员工具备行业所需的专业知识和技能。'
+    ]
+  },
+  {
+    title: '连锁酒店及公寓',
+    id: 'chain-hotels-and-apartments',
+    startDesc: '我国酒店业的连锁化率不断提升,预计未来将有更多单体酒店转向连锁经营,许多知名品牌积极扩张,市场竞争日益激烈,连锁酒店将更加注重服务创新,提供个性化的服务,以满足不同客户的需求,通过引入智能化技术和设备,提高服务效率和管理水平,提升客户体验,随着环保意识的提高,连锁酒店将更加注重环保和可持续发展,积极采用环保技术。',
+    startTitle: '公寓作为一种居住形态,通常提供独立的空间和相对完善的生活设施,满足不同人群的居住需求。',
+    children: [
+      '1、公寓行业可细分为长租公寓、短租公寓、主题公寓等多个领域。',
+      '2、租期灵活,可以满足不同租客的居住需求,通常提供舒适的居住环境和完善的生活设施,如家具、家电等,运营商提供统一的管理和服务,包括清洁、维修等,为租客提供便捷的生活体验,作为一种综合物业形态,既具有居住功能又具有投资价值,租金收益稳定可观。'
+    ]
+  },
+  {
+    title: '单体酒店',
+    id: 'single-hotel',
+    startDesc: '单体酒店这种酒店形式的特点是单独、分散地存在于各个城市和地区,独立地进行营销活动和管理活动,不属于任何酒店集团,也不以任何形式加入任何联盟。',
+    endDesc: '随着酒店市场的竞争加剧,单体酒店将更加注重差异化竞争,通过提供独特的服务、打造特色主题等方式来吸引客户,虽然单体酒店本身不属于任何品牌,但越来越多的单体酒店开始意识到品牌建设的重要性,通过提升服务质量、加强市场宣传等方式来塑造品牌形象,随着数字化技术的发展,单体酒店也将加快数字化转型步伐,利用互联网、大数据等技术手段来提升管理效率、优化客户体验。单体酒店作为传统酒店形式的一种,具有独特的经营特点和市场优势。在未来发展中,单体酒店需要不断创新和提升服务质量,以应对日益激烈的市场竞争和消费者需求的变化。',
+    children: [
+      '1、完全由业主自主经营,不受其他酒店集团或连锁品牌的约束,具有较高的经营自主权,遍布各个城市和地区,数量众多,分布广泛,为游客提供了多样化的住宿选择,由于不受统一标准限制,单体酒店更容易根据当地文化和市场需求提供个性化的服务,满足不同游客的住宿需求。',
+      '2、与连锁酒店相比,单体酒店在品牌建设和市场推广方面相对较弱,更多地依赖于地理位置、服务质量和口碑来吸引客户。'
+    ]
+  },
+  {
+    title: '新餐饮',
+    id: 'new-catering',
+    startDesc: '新餐饮业是指基于线上线下一体化、供应链垂直整合、餐饮零售化等理念,以新技术、新模式、新业态为主要特点的新型餐饮行业。',
+    endDesc: '随着消费者健康意识的提高,新餐饮业将更加注重提供健康、营养且美味的菜品,满足消费者对健康饮食的需求。人工智能技术将进一步渗透新餐饮业,智能化餐厅将成为未来的主流趋势,包括智能点餐、智能推荐、智能配送等全链条智能化服务。新餐饮业以其独特的理念和特点,正在不断改变着餐饮行业的面貌。未来,随着技术的不断进步和消费者需求的不断变化,新餐饮业将迎来更加广阔的发展前景。',
+    children: [
+      '1、它不仅是传统餐饮业的延伸和升级,更是对餐饮行业供给侧结构性改革的积极响应。',
+      '2、打破了传统餐饮业的物理空间限制,通过线上平台(如外卖平台、自建APP、小程序等)实现订单接收、支付、配送等全流程数字化管理,同时结合线下门店提供优质的用餐体验,实现线上线下无缝对接。'
+    ]
+  },
+  {
+    title: '高端民宿',
+    id: 'high-end-cottage',
+    startDesc: '以豪华、舒适和服务一流为特点的住宿场所。它们通常位于独特的地理位置,如度假胜地、高山、海滨等,为客人提供超越一般旅行住宿的奢华体验。',
+    endDesc: '随着旅游市场的不断发展和消费者需求的不断变化,高端民宿将继续保持其独特魅力,并呈现出多元化、品质化、数字化等发展趋势。',
+    children: [
+      '1、高端民宿不仅注重住宿设施的豪华与品质,还强调服务的个性化和独特性,旨在为客人打造一个独一无二的高端住宿环境。通常配备高档家具、家电以及豪华的室内装饰,如私人泳池、温泉浴缸、健身房等,满足客人对高品质生活的追求。',
+      '2、选址上,高端民宿倾向于位于自然风光优美或具有独特文化底蕴的地区,如风景名胜区、海岛、古镇等,让客人在享受住宿的同时,也能领略到独特的自然风光和人文景观。',
+      '3、为了满足不同消费者的需求,高端民宿将呈现多元化的发展态势,如结合当地文化特色打造主题民宿、提供特色餐饮和娱乐活动等。'
+    ]
+  },
+  {
+    title: '高端康养',
+    id: 'high-end-consumption',
+    startDesc: '高品质、全方位身心健康服务和生活体验的一种康养模式。',
+    endDesc: '作为一种高品质、全方位的康养模式,具有显著的特点和广阔的发展前景。在未来发展中,高端康养将更加注重市场需求、科技融合、国际化发展、生态化建设和个性化服务升级等方面的发展趋势。',
+    children: [
+      '1、高端康养通常融合了先进的医疗技术、健康管理理念、自然生态环境以及个性化服务等元素,旨在为客户打造一个全方位、高品质的康养生活。',
+      '2、提供定制化的健康管理方案、专业的医疗团队和先进的医疗设备,确保客户得到全面、精准的健康服务,于自然风光优美、空气质量优良的地区,如海滨、山区、温泉等,为客户营造宁静、舒适的康养环境。',
+      '3、除了基本的住宿设施外,还配备有医疗中心、康复中心、活动中心、图书馆、健身房等多样化的配套设施,满足客户在健康、娱乐、社交等多方面的需求。',
+      '4、随着人口老龄化问题的加剧和人们生活水平的提高,对高品质康养服务的需求不断增长。高端康养市场将迎来更大的发展机遇。'
+    ]
+  },
+  {
+    title: '会展业',
+    id: 'exhibition-industry',
+    startDesc: '会展业是一个新兴的服务行业,具有广泛的影响面和高度关联性。',
+    endDesc: '会展企业将通过兼并重组等方式实现规模化经营,同时加强与国际会展业的交流与合作,提升国际竞争力,会展业在全球范围内呈现出蓬勃发展的态势。',
+    children: [
+      '1、它涵盖了会议、展览、博览会、交易会、展销会、展示会等多种形式的集体活动,是围绕特定主题,多人在特定时空的集聚交流活动。会展业不仅是信息传递和交流的平台,也是促进经济、文化、科技等多领域交流与合作的重要渠道。',
+      '2、能带来直接的场租费、搭建费等收入,还能拉动数十个相关行业的发展,如商业购物、餐饮、住宿、娱乐、交通、通讯、广告、旅游、印刷、房地产等,形成显著的经济效应。',
+      '3、会展业是文化、科技、经贸等多领域交融的载体,能够促进不同领域之间的交流与融合。'
+    ]
+  },
+  {
+    title: '邮轮产业',
+    id: 'cruise-industry',
+    startDesc: '以跨国旅行为核心,通过丰富的旅游产品吸引游客,以航线经营为手段,提供海上观光旅游及相关服务,由交通运输、船舶制造、港口服务、旅游观光、餐饮、购物和银行保险等多个行业组合而成的复合型产业。',
+    endDesc: '随着中国市场的开放和发展,越来越多的国际邮轮公司进入中国市场,中国本土邮轮公司也在逐步崛起。',
+    children: [
+      '1、融合了多个服务行业,形成了强大的经济集聚效应,带动了相关产业链的发展。',
+      '2、邮轮旅游通常涉及多个国家和地区,形成了跨国、跨洋的旅游网络,游客可以在一次旅行中体验多个目的地的文化和风景,作为移动的海上度假村,汇集了来自不同国家和地区的游客和船员,形成了独特的多元文化交融环境。',
+      '3、邮轮旅游通常提供高端、豪华的住宿、餐饮和娱乐设施,为游客带来独特的奢华体验。'
+    ]
+  },
+  {
+    title: '旅游及地产',
+    id: 'tourism-and-real-estate',
+    startDesc: '旅游产业是为国内外游客提供出行、住宿、餐饮、游览、购物、娱乐等服务活动的一系列相关行业的统称。',
+    endDesc: '旅游地产市场受到多种因素的影响,如政策调整、市场需求变化等,具有一定的风险性,但同时也存在较大的发展机遇。',
+    children: [
+      '1、它具有综合性强、关联性高、拉动性大的特点,在政治、经济、社会、文化、生态等领域显示出了巨大发展活力,对国民经济与社会的发展具有突出贡献和拉动作用。',
+      '2、涉及多个行业,包括交通、住宿、餐饮、购物、娱乐等,形成了一个综合性的服务体系,各个环节紧密相连,相互影响,任何一个环节的改变都可能对整个产业产生影响。',
+      '3、旅游产业的发展能够带动相关产业的发展,如交通运输、商业零售、餐饮住宿等,形成经济联动效应。',
+      '4、旅游地产的开发和运营需要依托丰富的旅游资源,包括自然景观、人文景观等,投资回报期相对较长,需要长期稳定的运营和管理。'
+    ]
+  },
+  {
+    title: '体育与休闲业',
+    id: 'sports-and-leisure-industry',
+    startDesc: '体育休闲产业为社会各部门提供的与体育活动密切相关的产业领域,它涵盖了体育产品和服务,以及与这些产品和服务相关的经营活动的总和。',
+    endDesc: '体育与休闲业在促进人民健康、丰富社会文化生活、增强社会凝聚力等方面具有显著的社会效益。',
+    children: [
+      '1、这一产业不仅包括传统的体育运动项目,还融合了休闲、娱乐、旅游等多个元素,为人们提供多样化的身心放松和健康生活方式。',
+      '2、具有广泛的参与群体,不同年龄、性别、职业的人们都可以参与其中,享受运动的乐趣,该行业提供的不仅仅是体育设施和产品,更是一种健康、积极的生活方式,强调服务的全面性和个性化。',
+      '3、不断推出新的运动项目、服务模式和产品,以满足消费者日益多样化的需求。例如,智能健身设备、VR体验等新兴技术的应用,为休闲体育带来更多可能性。'
+    ]
+  },
+  {
+    title: '医院与博物馆',
+    id: 'hospital-and-museum',
+    startDesc: '医院是按照法律法规和行业规范,为病员开展必要的医学检查、治疗措施、护理技术、接诊服务、康复设备、救治运输等服务,以救死扶伤、治病救人为主要目的的医疗机构,汇聚了各类医学专业人才,能够提供从预防、诊断、治疗到康复的全方位医疗服务。博物馆作为公共文化机构,具有公益性质,向全社会开放,为公众提供知识、教育和娱乐服务。',
+    endDesc: '医院和博物馆作为社会的重要机构,在定义、特点和发展趋势上各有侧重但又相互关联。医院致力于提供高质量的医疗服务,而博物馆则致力于传承和弘扬人类文化和历史。两者都在不断适应时代的变化和发展需求,为社会和公众提供更好的服务。'
+  }
+]
+
+// 顾问
+export const consultantData = [
+  {
+    id: 'simon',
+    title: '田森先生(Simon Tian)',
+    job: '创始人兼首席执行官',
+    country: '苏州',
+    avatar: 'https://minio.citupro.com/dev/menduner/consultant/simon.png',
+    desc: [
+      '2018年10月创办门墩儿人力资源一体化技术平台,田森博士为酒店行业提供了专业的人才招聘和寻访服务。通过搭建线上线下相结合的学习平台、组织行业交流活动、提供定制化培训方案等方式,帮助酒店从业者提升专业技能和职业素养。同时他还致力于人才数据的开发和利用,为酒店业的人力资源管理提供了更加科学、智能的解决方案。',
+      '田森博士凭借其丰富的酒店行业工作经历和卓越的学术背景,在酒店运营管理、市场营销、人力资源管理及人才数据开发等方面展现出了深厚的专业知识和创新能力。他的工作成果不仅为酒店业的发展做出了积极贡献,也为行业内的其他从业者树立了榜样。'
+    ],
+    edu: ['香港理工大学酒店及旅游管理博士', '香港理工大学信息管理理学硕士', '瑞士恺撒里兹大学国际饭店管理研究生', '中国旅游饭店国家级星评员', '中山大学校外兼职研究生导师', '苏州大学旅游管理专业学位研究生教育指导委员会委员'],
+  },
+  {
+    id: 'peter',
+    title: '潘青海先生(Peter Pan)',
+    job: '猎头顾问',
+    country: '北京',
+    avatar: 'https://minio.citupro.com/dev/menduner/consultant/peter.png',
+    desc: [
+      '潘青海先生在酒店及酒店式服务公寓领域积累的16年房务背景经验,积累了丰富的专业知识和实践经验。拥有深入的理解和实战经验。这段经历不仅让他掌握了高效运营客房部门的技巧,还培养了他对细节的关注和对服务质量的执着追求。2018年9月加入门墩儿人力资源一体化技术平台,是公司创始团队成员之一,并承担起日常运营的重任。他利用自己的专业技能优化了公司的运营流程,提高了工作效率。注重团队协作,通过有效的沟通和协调,确保与同事和客户之间的顺畅衔接,为公司的整体运营提供了坚实的保障。',
+      '在招聘和猎头业务方面,展现出了敏锐的洞察力和精准的判断力。利用自己的行业资源和人脉,积极开拓招聘渠道,与各大集团、酒店及业主公司建立了良好的合作关系。',
+      '注重客户需求分析,为客户提供个性化的解决方案,赢得了客户的信任和好评。在他的带领下门墩儿的销售业绩持续攀升,市场份额不断扩大,为公司的发展奠定了坚实的基础。'
+    ]
+  },
+  {
+    id: 'julie',
+    title: '姚嘉庆女士(Julie)',
+    job: '猎头顾问',
+    country: '北京',
+    avatar: 'https://minio.citupro.com/dev/menduner/consultant/julie.png',
+    desc: [
+      '在酒店行业深耕十五年之久,是一位拥有丰富市场销售经验与深厚行业洞察力的专业人士。多年的酒店从业经历使姚嘉庆女士具备了敏锐的市场洞察力、卓越的团队领导力和深厚的行业资源积累。她能够迅速适应不同市场环境的变化,灵活调整销售策略,确保酒店业绩的持续增长。',
+      '姚嘉庆女士对酒店行业及其他相关行业的市场动态、企业需求、人才分布等有深入的了解,这有助于她更准确地把握客户需求,提高寻访效率。她具备出色的人才评估能力,能够准确地判断候选人的专业技能、职业素养、性格特质等是否符合客户企业的要求。同时,她也是一位注重持续学习和自我提升的顶尖猎头顾问,在快速变化的市场动向中不断掌握新的行业知识、招聘技巧和行业动态,以保持自己的专业竞争力。'
+    ]
   }
 ]

+ 21 - 14
src/views/headhunting/components/content.vue

@@ -23,8 +23,8 @@
             v-for="(val, index) in service" 
             :key="index" 
             :style="{'border-left': index !== 0 ? '1px solid #56738f' : 'none'}"
+            @click="handleClick('service', val.id, val.title)"
           >{{ val.title }}</li>
-            <!-- @click="handleClick('service', val.title)" -->
         </ul>
       </div>
     </div>
@@ -37,7 +37,7 @@
     <div class="country-offices common-width mb-10">
       <h2 class="section-header--country-page">我们的顾问</h2>
       <div class="d-flex consultant-box">
-        <div v-for="(val, i) in consultant" :key="i" class="consultant-item">
+        <div v-for="(val, i) in consultant" :key="i" class="consultant-item" @click="handleClick('consultant', val.id)">
           <div class="consultant-item__img" :style="{'background-image': `url('${val.avatar}')`, 'width': '100%', 'height': '100%' }"></div>
           <div class="consultant-item__name">{{ val.enName }}</div>
           <p class="consultant-item__country">{{ val.country }}</p>
@@ -112,6 +112,10 @@
 
 <script setup>
 defineOptions({ name: 'headhunting-carousel'})
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
+
 // 数值示例
 const list = [
   { title: '70家', desc: '全球分支机构' },
@@ -121,10 +125,10 @@ const list = [
 // 服务
 const service = [
   { title: '高级管理精英甄选', id: 'selection-senior-management-elite' },
-  { title: '人才发展储备策略' },
-  { title: '管理者能力提升方案' },
-  { title: '卓越成长轨迹' },
-  { title: '高管信誉与资质验证' }
+  { title: '人才发展储备策略', id: 'talent-development-reserve-strategy' },
+  { title: '管理者能力提升方案', id: 'manager-capability-enhancement-plan' },
+  { title: '卓越成长轨迹', id: 'excellent-growth-trajectory' },
+  { title: '高管信誉与资质验证', id: 'senior-management-elite-verification' }
 ]
 // 办公地点
 const countryOffice = [
@@ -134,9 +138,9 @@ const countryOffice = [
 ]
 // 我们的顾问
 const consultant = [
-  { country: '苏州', enName: '田森', avatar: 'https://cn.spencerstuart.com/-/media/consultant-photos-new/hong-kong/young_jeremy-web-5d.jpg' },
-  { country: '北京', enName: '潘青海', avatar: 'https://cn.spencerstuart.com/-/media/consultant-photos-new/singapore/ng_siewkiang-web-5a.jpg' },
-  { country: '北京', enName: '姚嘉庆', avatar: 'https://cn.spencerstuart.com/-/media/consultant-photos-new/hong-kong/au_alice-web-2d.jpg' }
+  { country: '苏州', enName: '田森先生(Simon Tian)', id: 'simon', avatar: 'https://minio.citupro.com/dev/menduner/consultant/simon.png' },
+  { country: '北京', enName: '潘青海先生(Peter Pan)', id: 'peter', avatar: 'https://minio.citupro.com/dev/menduner/consultant/peter.png' },
+  { country: '北京', enName: '姚嘉庆女士(Julie)', id: 'julie', avatar: 'https://minio.citupro.com/dev/menduner/consultant/julie.png' }
 ]
 // 我们的见解
 const articles = [
@@ -150,9 +154,10 @@ const social = [
   { showTooltip: true, desc: '关注门墩儿官方微信公众号,获悉热点资讯和行业洞见。', url: 'https://cn.spencerstuart.com/-/media/wechat.jpg?h=75&amp;w=75&amp;la=zh-CN&amp;hash=B9D5E58A097630C3C5A878D5ED49B4A6' }
 ]
 
-// const handleClick = (type, title) => {
-//   window.open()
-// }
+const handleClick = (type, id) => {
+  if (!type && !id) return
+  router.push(`/headhunting/service/details?type=${type}&key=${id}`)
+}
 </script>
 
 <style scoped lang="scss">
@@ -343,7 +348,7 @@ const social = [
 }
 .consultant-box {
   width: 100%;
-  height: 200px;
+  height: 250px;
   .consultant-item {
     width: calc((100% - 60px) / 3);
     max-width: 350px;
@@ -355,19 +360,21 @@ const social = [
     .consultant-item__img {
       background-repeat: no-repeat;
       background-position: center center;
-      background-size: cover;
+      background-size: contain;
     }
     .consultant-item__name {
       font-family: FFScalaWebBold, Georgia, Utopia, Charter, serif;
       font-weight: 700;
       color: #4c4c4e;
       margin-top: 10px;
+      margin-left: 55px;
     }
     .consultant-item__country {
       font-family: FFScalaWebItalic, Georgia, Utopia, Charter, sans-serif;
       font-style: italic;
       font-weight: 400;
       color: #818183;
+      margin-left: 55px;
     }
   }
 }

+ 32 - 24
src/views/headhunting/components/serviceContent.vue

@@ -22,7 +22,7 @@
       </div>
       <div class="service-content mt-5">
         <div class="service-item" v-for="(item, index) in service" :key="index" 
-          @mouseenter="item.active = true" @mouseleave="item.active = false"
+          @mouseenter="item.active = true" @mouseleave="item.active = false" @click="handleClick('service', item.id)"
         >
           <div v-if="!item.active" class="service-title">
             {{ item.title }}
@@ -43,10 +43,10 @@
           </div>
           <div class="industry-item mt-5 industry-right ml-15 d-flex">
             <ul style="list-style-type: none; width: 50%;">
-              <li v-for="(val, index) in k.items1" :key="index" class="right-item">{{ val.title }}</li>
+              <li v-for="(val, index) in k.items1" :key="index" class="right-item" @click="handleClick('industry', val.id)">{{ val.title }}</li>
             </ul>
             <ul v-if="k?.items2" style="list-style-type: none; width: 50%;">
-              <li v-for="(val, index) in k.items2" :key="index" class="right-item">{{ val.title }}</li>
+              <li v-for="(val, index) in k.items2" :key="index" class="right-item" @click="handleClick('industry', val.id)">{{ val.title }}</li>
             </ul>
           </div>
         </div>
@@ -58,18 +58,21 @@
 <script setup>
 defineOptions({ name: 'headhunting-serviceContent'})
 import { ref } from 'vue'
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
 
 // 我们的服务
 const service = ref([
-  { title: '高管搜索与招募', desc: '我们协助世界一流企业构建成功的领导团队。', active: false },
-  { title: '董事会服务', desc: '我我们协助建立专业合规、经验丰富且多元化的董事会,并为董事会交接、业绩及治理等问题提供建议。', active: false },
-  { title: '首席执行官继任计划', desc: '我们在CEO继任领域拥有数十年的顾问经验,致力于协助董事会制定、规划和执行CEO继任的全过程。', active: false },
-  { title: '高管评估服务', desc: '我们的综合评估方法能提供翔实数据和深入见解,从而评价和预测领导者的表现、契合度和影响力。', active: false },
-  { title: '领导力咨询服务', desc: '我们提供甄选和培养领导者、优化团队效能,以及评估和改进企业文化所需的见解。', active: false },
-  { title: '董事会服务', desc: '我们提供的高管和董事人选,能够制定明确的数字化战略,并驾驭企业转型所需的人才、运营及文化变革。', active: false },
-  { title: '中国业务部', desc: '我们的中国业务部为中国企业进入德国和欧洲拓展业务提供高管寻聘和领导力咨询服务,同时陪伴德国企业在中国市场的发展。', active: false },
-  { title: '多样性', desc: '我们致力于广泛发展关于多文化问题的专业基础,确保我们能为客户提供当前候选人名单中最具资格的人才。', active: false },
-  { title: '家族业务', desc: '作为家族控股公司和家族支配公司的受托顾问,我们具有家族客户所注重的敏感、谨慎和灵活的特征', active: false }
+  { title: '高级管理精英甄选', desc: '我们协助世界一流企业构建成功的领导团队。', active: false, id: 'selection-senior-management-elite' },
+  { title: '人才发展储备策略', desc: '我我们协助建立专业合规、经验丰富且多元化的董事会,并为董事会交接、业绩及治理等问题提供建议。', id: 'talent-development-reserve-strategy' },
+  { title: '管理者能力提升方案', desc: '我们在CEO继任领域拥有数十年的顾问经验,致力于协助董事会制定、规划和执行CEO继任的全过程。', id: 'manager-capability-enhancement-plan' },
+  { title: '卓越成长轨迹', desc: '我们的综合评估方法能提供翔实数据和深入见解,从而评价和预测领导者的表现、契合度和影响力。', active: false, id: 'excellent-growth-trajectory' },
+  { title: '高管信誉与资质验证', desc: '我们提供甄选和培养领导者、优化团队效能,以及评估和改进企业文化所需的见解。', active: false, id: 'senior-management-elite-verification' },
+  { title: '董事会服务', desc: '我们提供的高管和董事人选,能够制定明确的数字化战略,并驾驭企业转型所需的人才、运营及文化变革。', active: false, id: 'senior-management-elite-verification' },
+  { title: '中国业务部', desc: '我们的中国业务部为中国企业进入德国和欧洲拓展业务提供高管寻聘和领导力咨询服务,同时陪伴德国企业在中国市场的发展。', active: false, id: 'senior-management-elite-verification' },
+  { title: '多样性', desc: '我们致力于广泛发展关于多文化问题的专业基础,确保我们能为客户提供当前候选人名单中最具资格的人才。', active: false, id: 'senior-management-elite-verification' },
+  { title: '家族业务', desc: '作为家族控股公司和家族支配公司的受托顾问,我们具有家族客户所注重的敏感、谨慎和灵活的特征', active: false, id: 'senior-management-elite-verification' }
 ])
 
 const list = [
@@ -78,10 +81,10 @@ const list = [
     desc: '最佳的领导力决策,源于对行业动态及企业关键需求的深入理解。我们的顾问在各自领域拥有丰富的实践经验。凭借一套行之有效的高管寻访流程,我们能够帮助您找到与企业独特需求无缝对接,且适应竞争环境的领导者。凭借我们的经验、国际声望,以及与卓越领导者的深厚关系,我们能够在全球范围内寻觅炙手可热的候选人。',
     btnTitle: '查看所有行业',
     items1: [
-      { title: '酒店集团总部' },
-      { title: '业主公司' },
-      { title: '连锁酒店及公寓' },
-      { title: '单体酒店' }
+      { title: '酒店集团总部', id: 'hotel-group-headquarters' },
+      { title: '业主公司', id: 'owner-company' },
+      { title: '连锁酒店及公寓', id: 'chain-hotels-and-apartments' },
+      { title: '单体酒店', id: 'single-hotel' }
     ]
   },
   {
@@ -89,19 +92,24 @@ const list = [
     desc: '最佳的领导力决策,源于对行业动态及企业关键需求的深入理解。我们的顾问在各自领域拥有丰富的实践经验。凭借一套行之有效的高管寻访流程,我们能够帮助您找到与企业独特需求无缝对接,且适应竞争环境的领导者。凭借我们的经验、国际声望,以及与卓越领导者的深厚关系,我们能够在全球范围内寻觅炙手可热的候选人。',
     btnTitle: '查看所有职能',
     items1: [
-      { title: '新餐饮' },
-      { title: '高端民宿' },
-      { title: '高端康养' },
-      { title: '会展业' }
+      { title: '新餐饮', id: 'new-catering' },
+      { title: '高端民宿', id: 'high-end-cottage' },
+      { title: '高端康养', id: 'high-end-consumption' },
+      { title: '会展业', id: 'exhibition-industry' }
     ],
     items2: [
-      { title: '游轮产业' },
-      { title: '旅游及地产' },
-      { title: '体育与休闲业' },
-      { title: '医院与博物馆' }
+      { title: '游轮产业', id: 'cruise-industry' },
+      { title: '旅游及地产', id: 'tourism-and-real-estate' },
+      { title: '体育与休闲业', id: 'sports-and-leisure-industry' },
+      { title: '医院与博物馆', id: 'hospital-and-museum' }
     ]
   }
 ]
+
+const handleClick = (type, id) => {
+  if (!type && !id) return
+  router.push(`/headhunting/service/details?type=${type}&key=${id}`)
+}
 </script>
 
 <style scoped lang="scss">

+ 23 - 7
src/views/headhunting/details.vue

@@ -1,18 +1,34 @@
 <template>
-  <div>xxx</div>
+  <div>
+    <navBar @click="handleClickNav"></navBar>
+    <div>
+      <service v-if="query.type === 'service' || query.type === 'industry'" :id="query.key" :type="query.type" class="content-box"></service>
+      <consultant v-if="query.type === 'consultant'" :id="query.key" class="content-box"></consultant>
+    </div>
+  </div>
 </template>
 
 <script setup>
 defineOptions({ name: 'headhuntingDetails' })
 import { useRouter } from 'vue-router'
+import navBar from './components/nav.vue'
+import service from './drill/service.vue'
+import consultant from './drill/consultant.vue'
 
-const router  = useRouter()
-router.beforeEach((to, from, next) => {
-  document.title = to.meta.title
-  next()
-})
+const router = useRouter()
+const query = router.currentRoute.value.query
+
+const handleClickNav = (path) => {
+  if (!path) return
+  router.push(path)
+}
 </script>
 
 <style scoped lang="scss">
-
+.content-box {
+  width: 1000px;
+  max-width: 1000px;
+  min-width: 1000px;
+  margin: 0 auto;
+}
 </style>

+ 83 - 0
src/views/headhunting/drill/consultant.vue

@@ -0,0 +1,83 @@
+<template>
+  <div style="color: #4c4c4e;">
+    <div class="mt-5">
+      <v-breadcrumbs :items="paths" class="pa-0 ma-0"></v-breadcrumbs>
+    </div>
+    <div class="d-flex mt-10 justify-space-between">
+      <div>
+        <v-img :src="data.avatar" width="250" height="350"></v-img>
+        <p class="title-job">{{ data.job }}</p>
+      </div>
+      <div class="mt-10">
+        <h1 class="name">{{ data.title }}</h1>
+        <p class="tips mt-3">Consultant</p>
+        <p class="country">{{ data.country }}</p>
+        <ul v-if="data?.edu" class="mt-5">
+          <li v-for="(edu, index) in data.edu" :key="index">{{ edu }}</li>
+        </ul>
+      </div>
+    </div>
+    <div class="mt-10" style="flex: 1;">
+      <p v-for="(k, i) in data.desc" :key="i" class="desc">{{ k }}</p>
+    </div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'headhunting-drill-consultant'})
+import { ref } from 'vue'
+import { consultantData } from '@/utils/headhuntingData'
+
+const props = defineProps({
+  id: {
+    type: String,
+    default: ''
+  }
+})
+
+const data = ref(consultantData.find(e => e.id === props.id))
+
+const paths = [
+  { title: '主页', disabled: false, href: '/headhunting' },
+  { title: '我们的顾问', disabled: true, href: '' },
+  { title: data.value.title, disabled: true, href: '' }
+]
+</script>
+
+<style scoped lang="scss">
+.info {
+  width: 400px;
+}
+.desc {
+  margin-bottom: 20px;
+}
+li {
+  list-style-type: none;
+}
+.title-name {
+  font-family: FFScalaWebBold, Georgia, Utopia, Charter, serif;
+  font-weight: 700;
+}
+.title-job {
+  font-family: FFScalaWebItalic, Georgia, Utopia, Charter, sans-serif;
+  font-style: italic;
+  font-weight: 400;
+}
+.name {
+  font-family: FFScalaWebBold, Georgia, Utopia, Charter, sans-serif;
+  font-style: normal;
+  font-weight: 700;
+  color: #00695C;
+}
+.tips {
+  font-family: FFScalaWebItalic, Georgia, Utopia, Charter, serif;
+  font-style: italic;
+  font-weight: 400;
+}
+.country {
+  font-family: FFScalaWebItalic, Georgia, Utopia, Charter, sans-serif;
+  font-style: italic;
+  font-weight: 400;
+  color: #00695C;
+}
+</style>

+ 50 - 0
src/views/headhunting/drill/service.vue

@@ -0,0 +1,50 @@
+<template>
+  <div style="color: #4c4c4e;">
+    <div class="mt-5">
+      <v-breadcrumbs :items="paths" class="pa-0 ma-0"></v-breadcrumbs>
+    </div>
+    <h1 class="my-5">{{ data.title }}</h1>
+    <p class="font-weight-bold">{{ data.startDesc }}</p>
+    <p v-if="data.startTitle" class="font-weight-bold mt-1">{{ data.startTitle }}</p>
+    <ul v-if="data?.children && data?.children.length">
+      <li v-for="(val, index) in data.children" :key="index">{{ val }}</li>
+    </ul>
+    <p v-if="data?.endDesc" :class="{'mt-5': type !== 'service'}">{{ data.endDesc }}</p>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'headhunting-drill-service' })
+import { ref } from 'vue'
+import { serviceData } from '@/utils/headhuntingData'
+
+const props = defineProps({
+  id: {
+    type: String,
+    default: ''
+  },
+  type: {
+    type: String,
+    default: ''
+  }
+})
+
+const data = ref(serviceData.find(e => e.id === props.id))
+
+const paths = [
+  { title: '主页', disabled: false, href: '/headhunting' },
+  { title: '我们的服务', disabled: false, href: '/headhunting/service' },
+  { title: data.value.title, disabled: true, href: '' }
+]
+</script>
+
+<style scoped lang="scss">
+ul {
+  list-style: none;
+  margin-left: 20px;
+  margin-top: 10px;
+  li {
+    margin-bottom: 1.5rem;
+  }
+}
+</style>

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

@@ -6,8 +6,8 @@
         <div>
           <span class="ml-10 item-title">{{ val.title }}</span>
           <span v-if="val.showRules" class="ml-5 rules cursor-pointer" @click="integralRulesClick">
-            <v-icon>mdi-help-circle-outline</v-icon>
             {{ $t('points.integralRules') }}
+            <v-icon style="font-size: 16px; color: var(--color-666); line-height: 16px; margin-left: 1px;">mdi-help-circle-outline</v-icon>
           </span>
         </div>
         <div>

+ 2 - 0
src/views/login/components/passwordPage.vue

@@ -72,6 +72,8 @@ const loginData = reactive({
 
 // 设置默认账号密码便于开发快捷登录
 if (window.location.hostname === 'localhost' || window.location.hostname === '192.168.3.152') {
+  // loginData.phone = '18400000022@qq.com'
+  // loginData.password = 'Citu123456'
   loginData.phone = '13229740092'
   loginData.password = 'Citu123456'
 }

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

@@ -79,7 +79,7 @@ const handleLogin = async () => {
       const isEnterprise = pattern.test(params.phone)
       params.isEnterprise = isEnterprise
     }
-    if (params.isEnterprise) router.push({ path: '/enterpriseVerification' }) // 先跳转到会使用企业token的路由
+    // if (params.isEnterprise) router.push({ path: '/enterpriseVerification' }) // 先跳转到会使用企业token的路由
     await userStore[api](params)
     // 跳转
     if (params.isEnterprise) return // 企业邮箱登录

+ 5 - 3
src/views/mall/index.vue

@@ -5,7 +5,10 @@
       <div class="statisticsBox">
         <div class="mt-2">
           <span style="font-size: 20px; color: var(--color-333); line-height: 28px; font-weight: bold;" class="ml-10">您当前可用积分</span>
-          <span style="font-size: 14px; color: var(--color-666); line-height: 24px; cursor: pointer;" class="ml-2" @click="toPointsDetails">积分明细</span>
+          <span @click="toPointsDetails" style="cursor: pointer;">
+            <span style="font-size: 14px; color: var(--color-666); line-height: 24px;" class="ml-2">积分明细</span>
+            <v-icon style="font-size: 16px; color: var(--color-666); line-height: 16px; margin-left: 2px;">mdi-help-circle-outline</v-icon>
+          </span>
         </div>
         <div class="d-flex justify-space-between align-end my-1">
           <span style="font-size: 42px; color: #10897bba; line-height: 50px;" class="ml-10 cursor-pointer" @click="toPointsDetails">{{ accountData.point }}</span>
@@ -40,8 +43,7 @@ import { useRouter } from 'vue-router'; const router = useRouter()
 defineOptions({name: 'personal-pointsMall'})
 
 const toPointsDetails = () => {
-  // '/recruit/personal/myWallet' : '/recruit/enterprise/memberCenter/myAccount'
-  router.push({ path: '/recruit/personal/myWallet' })
+  router.push({ path: '/recruit/personal/personalCenter/wallet' })
 }
 
 const getPositionList = () => {

+ 5 - 1
src/views/mall/purchasePackage/index.vue

@@ -1,6 +1,6 @@
 <!-- 购买套餐 -->
 <template>
-  <div class="default-width py-3">
+  <div class="py-3" :class="customClass">
     <div class="white-bgc resume-box">
       <div class="resume-header" v-if="showTitle">
         <div class="resume-title">购买会员</div>
@@ -51,6 +51,10 @@ defineProps({
   showTitle: {
     type: Boolean,
     default: true
+  },
+  customClass: {
+    type: String,
+    default: 'default-width'
   }
 })
 

+ 0 - 1
src/views/publicRecruitment/components/table.vue

@@ -5,7 +5,6 @@
     :items="items"
     :headers="headers"
     hover
-    height="60vh"
     item-value="id"
   >
     <template #bottom></template>

+ 12 - 3
src/views/publicRecruitment/index.vue

@@ -1,6 +1,6 @@
 <!-- 赏金与积分 -->
 <template>
-  <div class="default-width py-3">
+  <div class="py-3" :class="customClass">
     <v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#fff" @update:model-value="getPositionList">
       <v-tab :value="1">{{ $t('publicRecruitment.myRecommendation') }}</v-tab>
       <!-- <v-tab :value="2">{{ $t('publicRecruitment.withdrawalRecords') }}</v-tab> -->
@@ -17,12 +17,21 @@
 </template>
 
 <script setup>
+defineOptions({name: 'personal-myPublicRecruitment-index'})
+import { ref } from 'vue'
 import myRecommendation from './myRecommendation.vue'
 // import myRegistration from './myRegistration.vue'
+
+defineProps({
+  customClass: {
+    type: String,
+    default: 'default-width'
+  }
+})
+
 import { useRoute } from 'vue-router'; const route = useRoute()
 import { useRouter } from 'vue-router'; const router = useRouter()
-import { ref } from 'vue'
-defineOptions({name: 'personal-myPublicRecruitment-index'})
+
 const tab = ref(+route.query?.tab || 1)
 const getPositionList = () => {
   if (route.query) router.replace({ path: route.path }) // 不留记录的清除跳转带过来的参数

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

@@ -23,7 +23,7 @@
       </div>
     </div>
     <!-- 滚动区域 -->
-    <div class="pa-3 white-bgc" style="height: 300px; border-radius: 5px; width: 360px;">
+    <div class="pa-3 white-bgc" style="height: 300px; border-radius: 5px; width: 260px;">
       <bountyDisplay></bountyDisplay>
     </div>
   </div>

+ 24 - 13
src/views/recruit/entRegister/register.vue

@@ -62,6 +62,7 @@ import { useI18n } from '@/hooks/web/useI18n'
 import { useRouter } from 'vue-router'; const router = useRouter()
 import { enterpriseRegisterApply } from '@/api/personal/user'
 import { onMounted, ref } from 'vue';
+import { checkEmail } from '@/utils/validate'
 defineOptions({name: 'enterprise-enterpriseRegister-register'})
 const { t } = useI18n()
 const CtFormRef = ref()
@@ -139,9 +140,8 @@ const formItems = ref({
           return '请输入联系邮箱'
         },
         value => {
-          const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
-          if (pattern.test(value)) return true
-          return '请输入邮箱'
+          if (checkEmail(value)) return true
+          return '请输入正确的电子邮箱'
         }
       ]
     },
@@ -151,7 +151,16 @@ const formItems = ref({
       value: '',
       label: '邮箱登录密码(用于企业招聘邮箱登录) *',
       placeholder: '请输入邮箱登录密码(用于企业招聘邮箱登录)',
-      rules: [v => !!v || '请输入邮箱登录密码(用于企业招聘邮箱登录)']
+      rules: [
+        value => {
+          if (value) return true
+          return '请输入邮箱登录密码(用于企业招聘邮箱登录)'
+        },
+        value => {
+          if (!(/^[\s]+$/.test(value))) return true
+          return '请输入邮箱登录密码(用于企业招聘邮箱登录)'
+        }
+      ]
     },
     {
       type: 'text',
@@ -159,7 +168,16 @@ const formItems = ref({
       value: '',
       label: '请再次输入邮箱登录密码 *',
       placeholder: '请再次输入邮箱登录密码',
-      rules: [v => !!v || '请再次输入邮箱登录密码']
+      rules: [
+        value => {
+          if (value) return true
+          return '请再次输入邮箱登录密码'
+        },
+        value => {
+          if (!(/^[\s]+$/.test(value))) return true
+          return '请再次输入邮箱登录密码'
+        }
+      ]
     },
     {
       type: 'textarea',
@@ -177,10 +195,6 @@ const formItems = ref({
 // 上传
 let licenseUrl = ref('')
 
-const isOnlySpaces = (str) => {
-  return /^[\s]+$/.test(str) ? null : str // 判断是否只有空格
-}
-
 // 提交 企业注册
 const handleCommit = async () => {
   const { valid } = await CtFormRef.value.formRef.validate()
@@ -194,10 +208,7 @@ const handleCommit = async () => {
   }
   formItems.value.options.forEach(e => { params[e.key] = e.value })
   // 邮箱登录密码校验
-  const pa1 = isOnlySpaces(params.password); const pa2 = isOnlySpaces(params.passwordConfirm);
-  if (pa1 && !pa2) return Snackbar.warning('请输入确认邮箱登录密码')
-  if (!pa1 && pa2) return Snackbar.warning('请输入邮箱登录密码')
-  if (pa1 !== pa2) return Snackbar.warning('两次输入的邮箱登录密码不一致,请确认输入内容')
+  if (params.password !== params.passwordConfirm) return Snackbar.warning('两次输入的密码不一致,请确认')
 
   await enterpriseRegisterApply(params)
   Snackbar.success(t('common.submittedSuccessfully'))

+ 0 - 0
src/views/recruit/enterprise/informationManagement/informationSettings.vue → src/views/recruit/enterprise/entInfoSetting/index.vue


+ 0 - 0
src/views/recruit/enterprise/informationManagement/informationSettingsComponents/authentication.vue → src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/authentication.vue


+ 0 - 0
src/views/recruit/enterprise/informationManagement/informationSettingsComponents/basicInfo.vue → src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/basicInfo.vue


+ 2 - 1
src/views/recruit/enterprise/informationManagement/informationSettingsComponents/businessInformation.vue → src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/businessInformation.vue

@@ -13,7 +13,7 @@ import CtForm from '@/components/CtForm'
 import { getEnterpriseBusiness, updateEnterpriseBusiness } from '@/api/enterprise'
 import Snackbar from '@/plugins/snackbar'
 import { reactive, ref } from 'vue'
-import { getDict } from '@/hooks/web/useDictionaries'
+// import { getDict } from '@/hooks/web/useDictionaries'
 const emit = defineEmits(['complete'])
 
 defineOptions({name: 'informationSettingsComponents-businessInformation'})
@@ -165,6 +165,7 @@ let completeStatus = false
 const getBaseInfo = async () => {
   try {
     const data = await getEnterpriseBusiness()
+    emit('complete', { status: data && Object.keys(data).length ? true : false, id: 'businessInformation' })
     if (!data) return
     query.id = data.id
     completeStatus = true

+ 0 - 0
src/views/recruit/enterprise/informationManagement/informationSettingsComponents/enterpriseAlbum.vue → src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/enterpriseAlbum.vue


+ 0 - 0
src/views/recruit/enterprise/informationManagement/informationSettingsComponents/enterpriseLabel.vue → src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/enterpriseLabel.vue


+ 0 - 0
src/views/recruit/enterprise/informationManagement/informationSettingsComponents/enterpriseLogo.vue → src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/enterpriseLogo.vue


+ 0 - 0
src/views/recruit/enterprise/informationManagement/informationSettingsComponents/welfareLabel.vue → src/views/recruit/enterprise/entInfoSetting/informationSettingsComponents/welfareLabel.vue


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

@@ -157,7 +157,8 @@ const getPositionList = async () => {
   if (!data.length) return
   const list = dealDictArrayData([], data)
   positionItems.value = list.map(e => {
-    return { label: `${e.name}${e.areaName ? '_' + e.areaName : ''} ${e.payFrom ? e.payFrom + '-' : ''}${e.payTo}${e.payName ? '/' + e.payName : ''}`, value: e.id }
+    const salary = e.payFrom && e.payTo ? `${e.payFrom ? e.payFrom + '-' : ''}${e.payTo}${e.payName ? '/' + e.payName : ''}` : '面议'
+    return { label: `${e.name}${e.areaName ? '_' + e.areaName : ''} ${salary}`, value: e.id }
   })
 }
 getPositionList()

+ 1 - 0
src/views/recruit/enterprise/memberCenter/invoice/header.vue → src/views/recruit/enterprise/invoiceManagement/index.vue

@@ -1,3 +1,4 @@
+<!-- 发票管理 -->
 <template>
   <v-card class="card-box pa-5">
     <CtTable

+ 0 - 20
src/views/recruit/enterprise/memberCenter/myAccount/index.vue

@@ -1,20 +0,0 @@
-<template>
-  <IntegralPage :type="1"></IntegralPage>
-</template>
-
-<script setup>
-defineOptions({ name: 'myWallet'})
-import IntegralPage from '@/views/integral/pointsManagement'
-import { useUserStore } from '@/store/user'
-
-const store = useUserStore()
-const updateAccountInfo = async () => {
-  await store.getEnterpriseUserAccountInfo()
-}
-updateAccountInfo()
-
-</script>
-
-<style scoped lang="scss">
-
-</style>

+ 0 - 0
src/views/recruit/enterprise/memberCenter/myMembers/components/open.vue → src/views/recruit/enterprise/membershipPackage/components/open.vue


+ 0 - 0
src/views/recruit/enterprise/memberCenter/myMembers/components/package.vue → src/views/recruit/enterprise/membershipPackage/components/package.vue


+ 5 - 4
src/views/recruit/enterprise/memberCenter/myMembers/index.vue → src/views/recruit/enterprise/membershipPackage/index.vue

@@ -2,11 +2,13 @@
 <template>
   <v-card class="card-box pa-3" style="min-width: 1100px;">
     <v-tabs v-model="tab" align-tabs="center" color="rgb(195 15 15)">
-      <v-tab :value="0" class="font-weight-bold font-size-18">套餐购买</v-tab>
-      <v-tab :value="1" class="font-weight-bold font-size-18">充值</v-tab>
+      <v-tab :value="0" class="font-weight-bold font-size-18">套餐列表</v-tab>
+      <v-tab :value="1" class="font-weight-bold font-size-18">当前套餐</v-tab>
     </v-tabs>
     <Package v-if="tab === 0"></Package>
-    <PointsAndBalance v-else></PointsAndBalance>
+    <div v-if="tab === 1">
+      <empty :elevation="false" message="无购买的套餐内容" class="mt-15"></empty>
+    </div>
   </v-card>
 </template>
 
@@ -14,7 +16,6 @@
 defineOptions({name: 'enterprise-memberCenter-myAccount'})
 import { ref } from 'vue'
 import Package from './components/package.vue'
-import PointsAndBalance from './components/pointsAndBalance.vue'
 
 const tab = ref(0)
 </script>

+ 2 - 1
src/views/recruit/enterprise/personnelManagement/components/invite.vue

@@ -72,7 +72,8 @@ const formItems = ref({
 
 if (Object.keys(props.itemData).length) {
   const obj = formItems.value.options.find(e => e.key === 'position')
-  obj.value = `${props.itemData?.job?.name}${props.itemData?.job?.areaName ? '_' + props.itemData?.job?.areaName : ''} ${props.itemData?.job?.payFrom ? props.itemData?.job?.payFrom + '-' : ''}${props.itemData?.job?.payTo}${props.itemData?.job?.payName ? '/' + props.itemData?.job?.payName : ''}`
+  const salary = props.itemData?.job?.payFrom && props.itemData?.job?.payTo ? `${props.itemData?.job?.payFrom ? props.itemData?.job?.payFrom + '-' : ''}${props.itemData?.job?.payTo}${props.itemData?.job?.payName ? '/' + props.itemData?.job?.payName : ''}` : '面议'
+  obj.value = `${props.itemData?.job?.name}${props.itemData?.job?.areaName ? '_' + props.itemData?.job?.areaName : ''} ${salary}`
   formItems.value.options.find(e => e.key === 'address').value = props.itemData.job?.address
 }
 

+ 2 - 1
src/views/recruit/enterprise/personnelManagement/components/screen.vue

@@ -87,7 +87,8 @@ list.value.forEach(k => {
       if (data.length) {
         const list = dealDictArrayData([], data)
         k.items = list.map(e => {
-          return { label: `${e.name}${e.areaName ? '_' + e.areaName : ''} ${e.payFrom ? e.payFrom + '-' : ''}${e.payTo}${e.payName ? '/' + e.payName : ''}`, value: e.id }
+          const salary = e.payFrom && e.payTo ? `${e.payFrom ? e.payFrom + '-' : ''}${e.payTo}${e.payName ? '/' + e.payName : ''}` : '面议'
+          return { label: `${e.name}${e.areaName ? '_' + e.areaName : ''} ${salary}`, value: e.id }
         })
       }
     })

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

@@ -13,12 +13,14 @@
       <template v-slot:item.name="{ item }">
         <div class="d-flex align-center cursor-pointer" @click="handleToPersonDetail(item)">
           <v-badge
+            v-if="item?.person?.sex === '1' || item?.person?.sex === '2'"
             bordered
             offset-y="6"
             :color="badgeColor(item)"
             :icon="badgeIcon(item)">
             <v-avatar size="40" :image="item.person.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
           </v-badge>
+          <v-avatar v-else size="40" :image="item.person.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
           <span class="defaultLink ml-3">{{ item?.person?.name }}</span>
         </div>
       </template>
@@ -99,7 +101,7 @@ watch(
 // 人才详情
 const handleToPersonDetail = ({ userId, id }) => {
   if (!userId || !id) return
-  window.open(`/recruit/enterprise/resumeManagement/talentPool/details/${userId}?id=${id}`)
+  window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}`)
 }
 
 // 入职

+ 0 - 13
src/views/recruit/enterprise/purchasePackage/index.vue

@@ -1,13 +0,0 @@
-<!-- 购买套餐 -->
-<template>
-  <PurchasePackage :type="1"></PurchasePackage>
-</template>
-
-<script setup>
-defineOptions({name: 'enterprise-purchasePackage'})
-import PurchasePackage from '@/views/mall/purchasePackage'
-
-</script>
-
-<style lang="scss" scoped>
-</style>

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

@@ -0,0 +1,82 @@
+<!--  -->
+<template>
+  <v-card class="pa-5 card-box d-flex flex-column align-center">
+    <div class="resume-header mt-15 mb-8" style="width: 700px;">
+      <div class="resume-title">修改登录密码</div>
+    </div>
+    <CtForm ref="CtFormRef" :items="formItems" style="width: 700px;"></CtForm>
+    <v-btn class="buttons mt-5" color="primary" @click.stop="handleCommit">{{ $t('common.save') }}</v-btn>
+    <v-btn class="mt-3" color="primary" variant="text" @click="router.go(-1)">返回</v-btn>
+  </v-card>
+
+  <Loading :visible="overlay"></Loading>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+import Snackbar from '@/plugins/snackbar'
+import { useI18n } from '@/hooks/web/useI18n'
+import { useRouter } from 'vue-router'; const router = useRouter()
+import { entUpdatePassword } from '@/api/enterprise'
+defineOptions({name: 'staff-changePassword'})
+
+const { t } = useI18n()
+
+const CtFormRef = ref()
+const formItems = ref({
+  options: [
+    {
+      type: 'text',
+      key: 'password',
+      value: '',
+      label: '请输入新密码 *',
+      rules: [
+        value => {
+          if (value) return true
+          return '请输入新密码'
+        },
+        value => {
+          if (!(/^[\s]+$/.test(value))) return true
+          return '请输入新密码'
+        }
+      ]
+    },
+    {
+      type: 'text',
+      key: 'passwordConfirm',
+      value: '',
+      label: '请再次输入新密码 *',
+      rules: [
+        value => {
+          if (value) return true
+          return '请再次输入新密码'
+        },
+        value => {
+          if (!(/^[\s]+$/.test(value))) return true
+          return '请再次输入新密码'
+        }
+      ]
+    },
+  ]
+})
+
+
+const handleCommit = async () => {
+  const { valid } = await CtFormRef.value.formRef.validate()
+  if (!valid) return
+  
+  const params = {}
+  const baseInfo = JSON.parse(localStorage.getItem('entBaseInfo') || "null") || null
+  if (baseInfo?.id) params.id = baseInfo.id
+
+  formItems.value.options.forEach(e => { params[e.key] = e.value })
+  // 邮箱登录密码校验
+  if (params.password !== params.passwordConfirm) return Snackbar.warning('两次输入的密码不一致,请确认')
+
+  await entUpdatePassword(params)
+  Snackbar.success(t('common.submittedSuccessfully'))
+  router.go(-1)
+}
+</script>
+<style lang="scss" scoped>
+</style>

+ 21 - 1
src/views/recruit/enterprise/informationSetting/index.vue → src/views/recruit/enterprise/staffInfoSetting/index.vue

@@ -19,6 +19,7 @@
       </template>
     </CtForm>
     <v-btn class="buttons mt-5" color="primary" @click.stop="handleSubmit">{{ $t('common.save') }}</v-btn>
+    <v-btn class="mt-3" color="primary" variant="text" to="/recruit/enterprise/staffChangePassword">修改登录密码</v-btn>
   </v-card>
 
   <Loading :visible="overlay"></Loading>
@@ -35,6 +36,7 @@ import { getDict } from '@/hooks/web/useDictionaries'
 import { useUserStore } from '@/store/user'
 import Snackbar from '@/plugins/snackbar'
 import { getUserAvatar } from '@/utils/avatar'
+// import { checkEmail } from '@/utils/validate'
 
 const { t } = useI18n()
 const userStore = useUserStore()
@@ -81,12 +83,24 @@ const formItems = ref({
       type: 'text',
       key: 'email',
       value: '',
-      label: '电子邮箱'
+      label: '电子邮箱',
+      disabled: true,
+      // rules: [
+      //   value => {
+      //     if (value) return true
+      //     return '请输入联系邮箱'
+      //   },
+      //   value => {
+      //     if (checkEmail(value)) return true
+      //     return '请输入正确的电子邮箱'
+      //   }
+      // ]
     }
   ]
 })
 
 // 用户基本信息
+// let emailChange = false
 const baseInfo = ref(JSON.parse(localStorage.getItem('entBaseInfo')) || {})
 const query = ref({})
 // 获取字典数据以及字段回显
@@ -101,6 +115,10 @@ formItems.value.options.forEach(item => {
     item.value = baseInfo.value[item.key]
     query.value.id = baseInfo.value.id
   }
+  // if (item.key === 'email') {
+  //   item.disabled = checkEmail(item.value)
+  //   emailChange = !item.disabled
+  // }
 })
 // 监听store变化
 userStore.$subscribe((mutation, state) => {
@@ -153,12 +171,14 @@ const handleSubmit = async () => {
     query.value[item.key] = item.value
   })
   await saveUserInfo(query.value)
+  // if (query.value?.email && emailChange) await entUpdateEmail({ email: query.value.email })
   setTimeout(async () => {
     await userStore.getEnterpriseInfo()
     Snackbar.success(t('common.submittedSuccessfully'))
     overlay.value = false
   }, 1000)
 }
+
 </script>
 
 <style scoped lang="scss">

+ 51 - 0
src/views/recruit/enterprise/systemManagement/groupAccount/index.vue

@@ -46,6 +46,7 @@
             <v-btn color="primary" variant="text" @click="handleBinding(item)">{{ $t('enterprise.userManagement.jobBinding') }}</v-btn>
             <v-btn v-if="item.status === '1' && item.userType !== '1'" color="primary" variant="text" @click="handleAction('', 0, item)">{{ $t('enterprise.userManagement.enable') }}</v-btn>
             <v-btn v-if="item.status === '0' && item.userType !== '1'" color="primary" variant="text" @click="handleAction('', 1, item)">{{ $t('enterprise.userManagement.disable') }}</v-btn>
+            <v-btn color="primary" variant="text" @click="handleChangeEmail(item)">修改员工邮箱</v-btn>
           </template>
         </CtTable>
       </v-col>
@@ -55,6 +56,9 @@
   <CtDialog :visible="show" :widthType="2" titleClass="text-h6" :title="$t('enterprise.userManagement.selectBinding')" @close="handleClose" @submit="handleSubmit">
     <CtForm ref="formPageRef" :items="formItems"></CtForm>
   </CtDialog>
+  <CtDialog :visible="showEditEmail" :widthType="2" titleClass="text-h6" title="修改员工邮箱" @close="showEditEmail = false" @submit="handleEditEmailSubmit">
+    <CtForm ref="editEmailFormRef" class="mt-3" :items="emailFormItems"></CtForm>
+  </CtDialog>
 </template>
 
 <script setup>
@@ -67,6 +71,8 @@ import { getEnterpriseTree } from '@/api/recruit/enterprise/system/group'
 import { getEnterpriseUserList, systemUserEnable, systemUserDisable, systemUserBindingPost } from '@/api/recruit/enterprise/system/user'
 import Confirm from '@/plugins/confirm'
 import Snackbar from '@/plugins/snackbar'
+import { checkEmail } from '@/utils/validate'
+import { entUpdateEmail } from '@/api/enterprise'
 // import { useRouter } from 'vue-router'; const router = useRouter()
 
 const { t } = useI18n()
@@ -208,6 +214,51 @@ const handleSubmit = async () => {
   handleClose()
   getUserList()
 }
+
+const emailFormItems = ref({
+  options: [
+    {
+      type: 'text',
+      key: 'email',
+      value: '',
+      label: '电子邮箱',
+      disabled: false,
+      rules: [
+        value => {
+          if (value) return true
+          return '请输入联系邮箱'
+        },
+        value => {
+          if (checkEmail(value)) return true
+          return '请输入正确的电子邮箱'
+        }
+      ]
+    }
+  ]
+})
+// 修改员工邮箱
+const showEditEmail = ref(false)
+const editEmailFormRef = ref()
+let editEmailItem = {}
+const handleChangeEmail = async (item) => {
+  emailFormItems.value.options.forEach(e => { if (item[e.key]) { e.value = item[e.key] } })
+  editEmailItem = item
+  showEditEmail.value = true
+}
+// 提交
+const handleEditEmailSubmit = async () => {
+  const { valid } = await editEmailFormRef.value.formRef.validate()
+  if (!valid) return
+  if (!editEmailItem?.id) return
+  const obj = { id: editEmailItem.id }
+  emailFormItems.value.options.forEach(e => { obj[e.key] = e.value })
+  await entUpdateEmail(obj)
+  setTimeout(async () => {
+    showEditEmail.value = false
+    getUserList()
+    Snackbar.success(t('common.submittedSuccessfully'))
+  }, 1000)
+}
 </script>
 
 <style scoped lang="scss">

+ 0 - 0
src/views/recruit/enterprise/resumeManagement/talentMap/components/filter copy.vue → src/views/recruit/enterprise/talentMap/components/filter copy.vue


+ 11 - 3
src/views/recruit/enterprise/resumeManagement/talentMap/components/filter.vue → src/views/recruit/enterprise/talentMap/components/filter.vue

@@ -32,10 +32,12 @@ const formItems = ref({
       counter: 500,
       rows: 5,
       resize: true,
-      label: '匹配内容',
+      autoGrow: true,
+      label: '请输入匹配内容',
       placeholder: '请输入要匹配内容',
       clearable: true,
       outlined: true,
+      autofocus: false,
     },
     {
       type: 'autocomplete',
@@ -81,6 +83,8 @@ const resetValue = () => {
 const setValue = (query) => {
   formItems.value.options.forEach(e => {
     if (query[e.key] !== undefined) e.value = query[e.key]
+    // console.log('CtFormRef', CtFormRef.value)
+    // if (e.key === 'content') e.autofocus = true
   })
 }
 
@@ -89,9 +93,13 @@ const handleReset = () => {
   emit('confirm', {})
 }
 
+const open = (query) => {
+  if (Object.keys(query).length) setValue(query)
+  else resetValue()
+}
+
 defineExpose({
-  setValue,
-  resetValue,
+  open,
 })
 </script>
 

+ 34 - 10
src/views/recruit/enterprise/resumeManagement/talentMap/index.vue → src/views/recruit/enterprise/talentMap/index.vue

@@ -1,8 +1,8 @@
 <template>
   <v-card class="card-box pa-5" style="height: 100%;">
-    <div class="d-flex justify-space-between">
-      <div></div>
-      <v-btn color="primary" class="half-button" variant="tonal" @click="openDrawer">职位匹配</v-btn>
+    <div class="d-flex justify-center">
+      <TextUI v-model="query.content" :item="textItem" @click="openDrawer" @appendInnerClick="openDrawer"></TextUI>
+      <!-- <v-btn color="primary" class="half-button" variant="tonal" @click="openDrawer">职位匹配</v-btn> -->
     </div>
     <!-- 人员信息表单 -->
     <v-data-table
@@ -17,22 +17,27 @@
     >
       <template #bottom></template>
       <template v-slot:[`item.name`]="{ item }">
-        <div class="d-flex align-center cursor-pointer" @click="null">
+        <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>
       </template>
       <template v-slot:[`item.advantage`]="{ item }">
         <template v-if="item.advantage">
-          <v-btn color="primary" variant="tonal" @click="advantageDetail(item.advantage)">查看</v-btn>
+          <v-btn color="primary" variant="text" @click="advantageDetail(item.advantage)">查看TA的优势</v-btn>
         </template>
       </template>
+      <template v-slot:[`item.actions`]="{ item }">
+        <v-btn color="primary" variant="text" @click="talentPoolDetails(item)">查看</v-btn>
+      </template>
     </v-data-table>
     <CtPagination
       v-if="total > 0"
@@ -67,6 +72,8 @@ import { dealDictArrayData } from '@/utils/position'
 import { getUserAvatar } from '@/utils/avatar'
 import { timesTampChange } from '@/utils/date'
 import FilterPage from './components/filter.vue'
+import TextUI from '@/components/FormUI/TextInput'
+
 import { computed, reactive, ref } from 'vue'
 
 const screen = ref(false)
@@ -91,6 +98,7 @@ const headers = [
   { title: '个人优势', key: 'advantage', sortable: false },
   { title: '工作年限', key: 'expName', sortable: false },
   { title: '最高学历', key: 'eduName', sortable: false },
+  { title: '操作', value: 'actions', sortable: false }
 ]
 
 // 获取数据
@@ -98,9 +106,9 @@ const getData = async () => {
   const obj = { ...pageInfo, ...query }
   // console.log('obj', obj)
   loading.value = true
-  const { list, total: number } = await getRecruitPersonMapPage(obj)
-  total.value = number
-  dataList.value = list?.length ? dealDictArrayData([], list) : []
+  const res = await getRecruitPersonMapPage(obj)
+  total.value = res?.total ? res.total-0 : 0
+  dataList.value = res?.list?.length ? dealDictArrayData([], res?.list) : []
   loading.value = false
 }
 getData()
@@ -128,8 +136,7 @@ const advantageDetail = (advantage) => {
 const FilterPageRef = ref()
 const openDrawer = () => {
   screen.value = true
-  if (Object.keys(query).length) FilterPageRef.value?.setValue(query)
-  else FilterPageRef.value?.resetValue()
+  FilterPageRef.value?.open(query)
 }
 
 const badgeColor = computed(() => (item) => {
@@ -139,6 +146,23 @@ const badgeColor = computed(() => (item) => {
 const badgeIcon = computed(() => (item) => {
   return (item && item.sex) ? (item.sex === '1' ? 'mdi-gender-male' : 'mdi-gender-female') : 'mdi-gender-female'
 })
+
+const textItem = ref({
+  type: 'text',
+  width: 600,
+  value: '',
+  label: '职位匹配',
+  clearable: false,
+  readonly: true,
+  appendInnerIcon: 'mdi-magnify'
+})
+
+// 人才详情
+const talentPoolDetails = ({ userId, id }) => {
+  if (!userId || !id) return
+  window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}`)
+}
+
 </script>
 
 <style scoped lang="scss">

+ 0 - 0
src/views/recruit/enterprise/resumeManagement/talentPool/components/details.vue → src/views/recruit/enterprise/talentPool/components/details.vue


+ 0 - 0
src/views/recruit/enterprise/resumeManagement/talentPool/components/details/attachmentResume.vue → src/views/recruit/enterprise/talentPool/components/details/attachmentResume.vue


+ 3 - 1
src/views/recruit/enterprise/resumeManagement/talentPool/components/details/baseInfo.vue → src/views/recruit/enterprise/talentPool/components/details/baseInfo.vue

@@ -3,7 +3,8 @@
   <div class="d-flex">
     <!-- 头像 -->
     <div class="avatarsBox">
-      <v-badge 
+      <v-badge
+        v-if="info?.sex === '1' || info?.sex === '2'"
         bordered 
         offset-x="-25" 
         offset-y="33" 
@@ -11,6 +12,7 @@
         :icon="info?.sex ? (info?.sex === '1' ? 'mdi-gender-male' : 'mdi-gender-female') : 'mdi-gender-female'">
         <v-avatar size=80 :image="info?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
       </v-badge>
+      <v-avatar v-else size=80 :image="info?.avatar || 'https://minio.citupro.com/dev/menduner/7.png'"></v-avatar>
     </div>
     <!-- 信息 -->
     <div style="flex: 1;">

+ 0 - 0
src/views/recruit/enterprise/resumeManagement/talentPool/components/details/dict.js → src/views/recruit/enterprise/talentPool/components/details/dict.js


+ 0 - 0
src/views/recruit/enterprise/resumeManagement/talentPool/components/details/educationExp.vue → src/views/recruit/enterprise/talentPool/components/details/educationExp.vue


+ 0 - 0
src/views/recruit/enterprise/resumeManagement/talentPool/components/details/jobIntention.vue → src/views/recruit/enterprise/talentPool/components/details/jobIntention.vue


+ 0 - 0
src/views/recruit/enterprise/resumeManagement/talentPool/components/details/projectExperience.vue → src/views/recruit/enterprise/talentPool/components/details/projectExperience.vue


+ 0 - 0
src/views/recruit/enterprise/resumeManagement/talentPool/components/details/trainingExperience.vue → src/views/recruit/enterprise/talentPool/components/details/trainingExperience.vue


+ 0 - 0
src/views/recruit/enterprise/resumeManagement/talentPool/components/details/vocationalSkills.vue → src/views/recruit/enterprise/talentPool/components/details/vocationalSkills.vue


+ 0 - 0
src/views/recruit/enterprise/resumeManagement/talentPool/components/details/workExperience.vue → src/views/recruit/enterprise/talentPool/components/details/workExperience.vue


+ 0 - 0
src/views/recruit/enterprise/resumeManagement/talentPool/components/filter.vue → src/views/recruit/enterprise/talentPool/components/filter.vue


+ 4 - 4
src/views/recruit/enterprise/resumeManagement/talentPool/index copy.vue → src/views/recruit/enterprise/talentPool/index copy.vue

@@ -11,7 +11,7 @@
       <v-btn class="ml-8" :disabled="!selectAll" color="primary" variant="tonal" size="small">邀请面试</v-btn>
       <v-btn class="mx-3" :disabled="!selectAll" color="primary" variant="tonal" size="small">简历回复</v-btn>
       <v-btn :disabled="!selectAll" color="primary" variant="tonal" size="small">批量导出</v-btn>
-      <v-btn class="mx-3" :disabled="!selectAll" color="primary" variant="tonal" size="small">踢出人才库</v-btn>
+      <v-btn class="mx-3" :disabled="!selectAll" color="primary" variant="tonal" size="small">移除</v-btn>
       <v-btn :disabled="!selectAll" color="primary" variant="tonal" size="small">移动到回收站</v-btn>
       <v-btn class="ml-3" :disabled="!selectAll" color="primary" variant="tonal" size="small">加入黑名单</v-btn>
     </div>
@@ -46,7 +46,7 @@
           <div>
             <!-- <v-btn color="primary" variant="tonal" @click.stop="{}">和TA聊聊</v-btn>
             <v-btn class="ml-3" color="primary" @click.stop="{}">邀请面试</v-btn> -->
-            <v-btn class="ml-3" color="primary" variant="tonal" @click.stop="handleRemove(val)">踢出人才库</v-btn>
+            <v-btn class="ml-3" color="primary" variant="tonal" @click.stop="handleRemove(val)">移除</v-btn>
           </div>
         </div>
         <div class="d-flex mx-5 bottom cursor-pointer">
@@ -105,7 +105,7 @@ import Snackbar from '@/plugins/snackbar'
 import TextUI from '@/components/FormUI/TextInput'
 import FilterPage from './components/filter.vue'
 import { dealDictArrayData } from '@/utils/position'
-import { getTalentPoolPage } from '@/api/recruit/enterprise/resumeManagement/talentPool'
+import { getTalentPoolPage } from '@/api/recruit/enterprise/talentPool'
 import { removeFormTalentPool } from '@/api/recruit/enterprise/personnel'
 
 
@@ -282,7 +282,7 @@ const openDrawer = () => {
 // 人才详情
 const talentPoolDetails = ({ userId, id }) => {
   if (!userId || !id) return
-  window.open(`/recruit/enterprise/resumeManagement/talentPool/details/${userId}?id=${id}`)
+  window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}`)
 }
 </script>
 

+ 6 - 6
src/views/recruit/enterprise/resumeManagement/talentPool/index.vue → src/views/recruit/enterprise/talentPool/index.vue

@@ -17,12 +17,14 @@
         <template v-slot:[`item.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>
         </template>
@@ -32,7 +34,7 @@
           </template>
         </template>
         <template v-slot:[`item.actions`]="{ item }">
-          <v-btn color="primary" variant="text" @click="handleRemove(item)">踢出人才库</v-btn>
+          <v-btn color="primary" variant="text" @click="handleRemove(item)">移除</v-btn>
           <v-btn color="primary" variant="text" @click="talentPoolDetails(item)">人才详情</v-btn>
         </template>
       </v-data-table>
@@ -64,7 +66,7 @@ import { computed, reactive, ref } from 'vue'
 import Snackbar from '@/plugins/snackbar'
 import FilterPage from './components/filter.vue'
 import { dealDictArrayData } from '@/utils/position'
-import { getTalentPoolPage } from '@/api/recruit/enterprise/resumeManagement/talentPool'
+import { getTalentPoolPage } from '@/api/recruit/enterprise/talentPool'
 import { removeFormTalentPool } from '@/api/recruit/enterprise/personnel'
 import { timesTampChange } from '@/utils/date'
 import { getUserAvatar } from '@/utils/avatar'
@@ -82,11 +84,9 @@ const pageInfo = reactive({ pageNo: 1, pageSize: 10 })
 // 获取数据
 const getData = async () => {
   const obj = { ...pageInfo, ...query }
-  console.log('obj', obj)
-  const { list, total: number } = await getTalentPoolPage(pageInfo)
+  const { list, total: number } = await getTalentPoolPage(obj)
   total.value = number
   dataList.value = list?.length ? dealDictArrayData([], list) : []
-  console.log('items.value', dataList.value)
 }
 getData()
 
@@ -148,7 +148,7 @@ const badgeIcon = computed(() => (item) => {
 // 人才详情
 const talentPoolDetails = ({ userId, id }) => {
   if (!userId || !id) return
-  window.open(`/recruit/enterprise/resumeManagement/talentPool/details/${userId}?id=${id}`)
+  window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}`)
 }
 </script>
 

+ 0 - 0
src/views/recruit/enterprise/resumeManagement/elite/components/commonStyle.vue → src/views/recruit/enterprise/talentRecruitment/components/commonStyle.vue


+ 2 - 1
src/views/recruit/enterprise/resumeManagement/elite/components/invite.vue → src/views/recruit/enterprise/talentRecruitment/components/invite.vue

@@ -84,7 +84,8 @@ const formItems = ref({
 
 if (Object.keys(props.itemData).length) {
   const obj = formItems.value.options.find(e => e.key === 'position')
-  obj.value = `${props.itemData?.job?.name}${props.itemData?.job?.areaName ? '_' + props.itemData?.job?.areaName : ''} ${props.itemData?.job?.payFrom ? props.itemData?.job?.payFrom + '-' : ''}${props.itemData?.job?.payTo}${props.itemData?.job?.payName ? '/' + props.itemData?.job?.payName : ''}`
+  const salary = props.itemData?.job?.payFrom && props.itemData?.job?.payTo ? `${props.itemData?.job?.payFrom ? props.itemData?.job?.payFrom + '-' : ''}${props.itemData?.job?.payTo}${props.itemData?.job?.payName ? '/' + props.itemData?.job?.payName : ''}` : '面议'
+  obj.value = `${props.itemData?.job?.name}${props.itemData?.job?.areaName ? '_' + props.itemData?.job?.areaName : ''} ${salary}`
   formItems.value.options.find(e => e.key === 'address').value = props.itemData.job?.address
 
   const baseInfo = localStorage.getItem('entBaseInfo')

+ 0 - 0
src/views/recruit/enterprise/resumeManagement/elite/components/public.vue → src/views/recruit/enterprise/talentRecruitment/components/public.vue


+ 0 - 0
src/views/recruit/enterprise/resumeManagement/elite/components/screen.vue → src/views/recruit/enterprise/talentRecruitment/components/screen.vue


+ 4 - 2
src/views/recruit/enterprise/resumeManagement/elite/components/table.vue → src/views/recruit/enterprise/talentRecruitment/components/table.vue

@@ -13,12 +13,14 @@
       <template v-slot:[`item.name`]="{ item }">
         <div class="d-flex align-center cursor-pointer" @click="handleToPersonDetail(item)">
           <v-badge
+            v-if="item?.person?.sex === '1' || item?.person?.sex === '2'"
             bordered
             offset-y="6"
             :color="badgeColor(item)"
             :icon="badgeIcon(item)">
             <v-avatar size="40" :image="getUserAvatar(item.person.avatar, item.person.sex)"></v-avatar>
           </v-badge>
+          <v-avatar v-else size="40" :image="getUserAvatar(item.person.avatar, item.person.sex)"></v-avatar>
           <span class="defaultLink ml-3">{{ item?.person?.name }}</span>
         </div>
       </template>
@@ -87,7 +89,7 @@ const headers = ref([
   { title: '求职状态', key: 'person.jobStatusName', sortable: false },
   { title: '工作经验', key: 'person.expName', sortable: false },
   { title: '最高学历', key: 'person.eduName', sortable: false },
-  { title: '岗位薪资', key: 'job', value: item => `${item.job.payFrom ? item.job.payFrom + '-' : ''}${item.job.payTo}${item.job.payName ? '/' + item.job.payName : ''}`, sortable: false },
+  { title: '岗位薪资', key: 'job', value: item => item.job.payFrom && item.job.payTo ? `${item.job.payFrom ? item.job.payFrom + '-' : ''}${item.job.payTo}${item.job.payName ? '/' + item.job.payName : ''}` : '面议', sortable: false },
   { title: '状态', key: 'status', sortable: false },
   { title: '操作', value: 'actions', sortable: false }
 ])
@@ -117,7 +119,7 @@ watch(
 // 人才详情
 const handleToPersonDetail = ({ userId, id }) => {
   if (!userId || !id) return
-  window.open(`/recruit/enterprise/resumeManagement/talentPool/details/${userId}?id=${id}`)
+  window.open(`/recruit/enterprise/talentPool/details/${userId}?id=${id}`)
 }
 
 // 加入人才库 

+ 0 - 0
src/views/recruit/enterprise/resumeManagement/elite/index.vue → src/views/recruit/enterprise/talentRecruitment/index.vue


+ 2 - 2
src/views/recruit/enterprise/memberCenter/myMembers/components/pointsAndBalance.vue → src/views/recruit/enterprise/tradingOrder/components/pointsAndBalance.vue

@@ -172,7 +172,7 @@ const stopInterval = async () => {
   padding: 2px 18px;
   color: var(--color-666);
 }
-.active {
+  // .active {
   // border: 2px solid #cf990c;
   // background: linear-gradient(rgb(255, 242, 214) 8.86%, rgb(255, 225, 177) 100%);;
   // .priceBox {
@@ -182,7 +182,7 @@ const stopInterval = async () => {
   //   color: var(--v-error-base);
   //   background-color: #fff4e7;
   // }
-}
+  // }
 .custom-input-num {
   border: none;
   outline: none;

+ 0 - 0
src/views/recruit/enterprise/memberCenter/myMembers/components/public.vue → src/views/recruit/enterprise/tradingOrder/components/public.vue


+ 2 - 2
src/views/recruit/enterprise/memberCenter/tradingOrder/index.vue → src/views/recruit/enterprise/tradingOrder/components/trading.vue

@@ -13,8 +13,8 @@
 <script setup>
 defineOptions({name: 'enterprise-memberCenter-tradingOrder'})
 import { ref } from 'vue'
-import Transaction from './components/transaction.vue'
-import Recharge from './components/recharge.vue'
+import Transaction from './trading/transaction.vue'
+import Recharge from './trading/recharge.vue'
 
 const tab = ref('tab_transaction')
 </script>

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


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


+ 30 - 0
src/views/recruit/enterprise/tradingOrder/index.vue

@@ -0,0 +1,30 @@
+<!-- 购买套餐 -->
+<template>
+  <v-card class="card-box pa-3" style="min-width: 1100px;">
+    <div class="tabsBox">
+      <v-tabs v-model="tab" align-tabs="center" color="var(--v-primary-base)">
+        <v-tab :value="0" class="font-weight-bold font-size-18">充值</v-tab>
+        <v-tab :value="1" class="font-weight-bold font-size-18">交易</v-tab>
+      </v-tabs>
+    </div>
+    <PointsAndBalance v-if="tab === 0"></PointsAndBalance>
+    <trading v-else></trading>
+  </v-card>
+</template>
+
+<script setup>
+defineOptions({name: 'enterprise-memberCenter-myAccount'})
+import { ref } from 'vue'
+import PointsAndBalance from './components/pointsAndBalance.vue'
+import trading from './components/trading.vue'
+
+const tab = ref(0)
+</script>
+
+<style lang="scss" scoped>
+.tabsBox {
+  :deep(.v-slide-group__content) {
+    background: linear-gradient(45deg, #e2f0ef, transparent);
+  }
+}
+</style>

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

@@ -0,0 +1,14 @@
+<template>
+  <div>
+    <BountyRewardsPage customClass=""></BountyRewardsPage>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'bountyRewards'})
+import BountyRewardsPage from '@/views/publicRecruitment/index'
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 9 - 4
src/views/recruit/personal/PersonalCenter/index.vue

@@ -1,8 +1,9 @@
 <template>
-  <div class="pt-3 default-width d-flex parent">
+  <div class="pt-3 d-flex parent">
     <v-card class="left">
       <v-list class="side-box" color="primary">
         <v-list-subheader class="title mb-3">个人中心</v-list-subheader>
+        <v-divider></v-divider>
         <template v-for="(item, index) in list">
           <template v-if="!item.children.length">
             <v-list-item
@@ -88,7 +89,11 @@ updateAccountInfo()
 
 <style scoped lang="scss">
 .parent {
-  height: calc(100vh - 150px);
+  width: 1300px;
+  max-width: 1300px;
+  min-width: 1300px;
+  margin: 0 auto;
+  height: calc(100vh - 100px);
   overflow-y: auto;
 }
 .page {
@@ -99,8 +104,8 @@ updateAccountInfo()
 }
 .left {
   position: sticky;
-  width: 250px;
-  height: calc(100vh - 173px);
+  width: 220px;
+  height: calc(100vh - 113px);
 }
 .title {
   color: var(--color-333);

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

@@ -8,7 +8,7 @@
               <v-img :src="getUserAvatar(item?.contact.avatar, item?.contact.sex)" :width="40" style="height: 40px;border-radius: 4px;"/>
             </div>
             <div class="company-info">
-              <h3 :class="{'default-active': item.active }" class="title1">{{ item.contact.name }}</h3>
+              <h3 class="title1">{{ item.contact.name }}</h3>
               <p class="mt-2">{{ item?.post?.nameCn }}</p>
             </div>
           </div>
@@ -46,7 +46,9 @@ import { dealDictObjData } from '@/utils/position'
 import { timesTampChange } from '@/utils/date'
 import Empty from '@/components/Empty'
 import { getUserAvatar } from '@/utils/avatar'
+import { useRouter } from 'vue-router'
 
+const router = useRouter()
 const total = ref(0)
 const items = ref([])
 const page = ref({
@@ -73,7 +75,7 @@ const handleChangePage = (e) => {
 const handleEnterprise = (item) => {
   const id = item.enterprise.id
   if (!id) return
-  window.open(`/recruit/personal/company/details/${id}?key=briefIntroduction`)
+  router.push(`/recruit/personal/company/details/${id}?key=briefIntroduction`)
 }
 
 </script>
@@ -88,9 +90,6 @@ const handleEnterprise = (item) => {
   text-overflow: ellipsis;
   white-space: nowrap;
   font-size: 16px;
-  &:hover {
-    color: var(--v-primary-base);
-  }
 }
 .company {
   flex: 1;

+ 2 - 2
src/views/recruit/personal/purchasePackage/index.vue → src/views/recruit/personal/PersonalCenter/memberBenefits/membershipPackage/index.vue

@@ -1,9 +1,9 @@
 <template>
-  <PurchasePackage></PurchasePackage>
+  <PurchasePackage customClass=""></PurchasePackage>
 </template>
 
 <script setup>
-defineOptions({ name: 'personal-purchasePackage'})
+defineOptions({ name: 'person-center-purchasePackage'})
 import PurchasePackage from '@/views/mall/purchasePackage'
 
 </script>

+ 0 - 0
src/views/recruit/personal/taskCenter/components/daily.vue → src/views/recruit/personal/PersonalCenter/memberBenefits/taskCenter/components/daily.vue


+ 0 - 0
src/views/recruit/personal/taskCenter/components/signIn.vue → src/views/recruit/personal/PersonalCenter/memberBenefits/taskCenter/components/signIn.vue


+ 0 - 0
src/views/recruit/personal/taskCenter/components/suggest.vue → src/views/recruit/personal/PersonalCenter/memberBenefits/taskCenter/components/suggest.vue


+ 1 - 2
src/views/recruit/personal/taskCenter/index.vue → src/views/recruit/personal/PersonalCenter/memberBenefits/taskCenter/index.vue

@@ -1,13 +1,12 @@
 <!-- 任务中心 -->
 <template>
-  <div class="default-width py-3">
+  <div class="py-3">
     <!-- 签到 -->
     <signIn class="white-bgc borderRadius5 mb-3 pa-3"></signIn>
     <!-- 推荐任务 -->
     <suggest class="white-bgc borderRadius5 mb-3 pa-3"></suggest>
     <!-- 每日任务 -->
     <daily class="white-bgc borderRadius5 mb-3 pa-3"></daily>
-    <!-- <div class="white-bgc borderRadius5 mt-3 pa-3"></div> -->
   </div>
 </template>
 

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

@@ -0,0 +1,100 @@
+<template>
+  <div class="resume-box">
+    <div class="resume-header mb-3">
+      <div class="resume-title">附件简历</div>
+      <v-btn variant="text" color="primary" prepend-icon="mdi-plus-box" @click="openFileInput">
+        上传
+        <File ref="uploadFile" @success="handleUploadResume"></File>
+      </v-btn>
+    </div>
+    <div v-if="attachmentList.length">
+      <div
+        :class="['position-item', 'mx-n2', 'px-2']" 
+        v-for="(k, i) in attachmentList" 
+        :key="i" 
+        @mouseenter="k.active = true" 
+        @mouseleave="k.active = false"
+      >
+        <div>{{ k.title }}</div>
+        <div class="float-right" v-if="k.active">
+          <v-btn variant="text" color="primary" prepend-icon="mdi-eye-outline" @click="previewFile(k.url)">预览</v-btn>
+          <v-btn variant="text" color="primary" prepend-icon="mdi-square-edit-outline" @click="handleDownload(k)">下载</v-btn>
+          <v-btn variant="text" color="primary" prepend-icon="mdi-trash-can-outline" @click="handleDelete(k)">{{ $t('common.delete') }}</v-btn>
+        </div>
+      </div>
+    </div>
+    <div v-else class="resumeNoDataText">请上传您的附件简历</div>
+  </div>
+</template>
+
+<script setup>
+defineOptions({ name: 'person-center-resume-annex'})
+import { ref } from 'vue'
+import Snackbar from '@/plugins/snackbar'
+import Confirm from '@/plugins/confirm'
+import { useI18n } from '@/hooks/web/useI18n'
+import { getPersonResumeCv, savePersonResumeCv, deletePersonResumeCv } from '@/api/recruit/personal/resume'
+import { getBlob, saveAs } from '@/utils'
+import { previewFile } from '@/utils'
+
+const { t } = useI18n()
+
+// 获取附件
+const attachmentList = ref([])
+const getList = async () => {
+  const data = await getPersonResumeCv()
+  attachmentList.value = data
+}
+getList()
+
+// 选择文件
+const uploadFile = ref()
+const openFileInput = () => {
+  if (attachmentList.value.length >= 5) return Snackbar.warning(t('resume.uploadFiveCopies'))
+  uploadFile.value.trigger()
+}
+
+// 上传附件
+const handleUploadResume = async (url, title) => {
+  if (!url || !title) return
+  Snackbar.success(t('common.uploadSucMsg'))
+  await savePersonResumeCv({ title, url })
+  getList()
+}
+
+// 删除
+const handleDelete = ({ id }) => {
+  Confirm(t('common.confirmTitle'), t('resume.deleteAttachment')).then(async () => {
+    await deletePersonResumeCv(id)
+    Snackbar.success(t('common.delMsg'))
+    getList()
+  })
+}
+
+// 下载附件
+const handleDownload = (k) => {
+  getBlob(k.url).then(blob => {
+    saveAs(blob, k.title)
+  })
+}
+</script>
+
+<style scoped lang="scss">
+.position-item {
+  display: flex;
+  justify-content: space-between;
+  cursor: pointer;
+  border-radius: 6px;
+  line-height: 40px;
+  font-size: 15px;
+  &:hover {
+    background-color: var(--color-f8);
+  }
+  span {
+    font-size: 15px;
+  }
+  .grey-text {
+    color: var(--color-999);
+  }
+}
+</style>

+ 15 - 2
src/views/recruit/personal/remuse/components/basicInfo.vue → src/views/recruit/personal/PersonalCenter/resume/online/components/basicInfo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="resume-box mb-3">
+  <div class="resume-box mb-3 elevation-2">
     <div class="resume-header">
       <div class="resume-title">{{ $t('resume.basicInfo') }}</div>
       <v-btn v-if="!isEdit" variant="text" color="primary" prepend-icon="mdi-square-edit-outline" @click="isEdit = true">{{ $t('common.edit') }}</v-btn>
@@ -7,7 +7,8 @@
     <div class="d-flex align-start mt-5">
       <!-- 头像 -->
       <div class="avatarsBox" @mouseover="showIcon = true" @mouseleave="showIcon = false">
-        <v-badge 
+        <v-badge
+          v-if="baseInfo?.sex === '1' || baseInfo?.sex === '2'"
           bordered 
           :color="baseInfo?.sex ? (baseInfo?.sex === '1' ? '#1867c0' : 'error') : 'error'" 
           :icon="baseInfo?.sex ? (baseInfo?.sex === '1' ? 'mdi-gender-male' : 'mdi-gender-female') : 'mdi-gender-female'">
@@ -22,6 +23,18 @@
             />
           </div>
         </v-badge>
+        <div v-else style="width: 130px; height: 130px;">
+          <v-img :src="getUserAvatar(baseInfo?.avatar, baseInfo?.sex)" width="130" height="130" style="border-radius: 6px;"></v-img>
+          <div v-show="showIcon" @click="openFileInput" class="mdi mdi-camera-outline camera">
+            <input
+              type="file"
+              ref="fileInput"
+              accept="image/png, image/jpg, image/jpeg"
+              style="display: none;"
+              @change="handleUploadFile"
+            />
+          </div>
+        </div>
       </div>
       <!-- 基础信息 -->
       <div style="flex: 1;" class="mr-8">

+ 0 - 0
src/views/recruit/personal/remuse/components/dict.js → src/views/recruit/personal/PersonalCenter/resume/online/components/dict.js


+ 0 - 0
src/views/recruit/personal/remuse/components/educationExp.vue → src/views/recruit/personal/PersonalCenter/resume/online/components/educationExp.vue


+ 0 - 0
src/views/recruit/personal/remuse/components/jobIntention.vue → src/views/recruit/personal/PersonalCenter/resume/online/components/jobIntention.vue


+ 0 - 0
src/views/recruit/personal/remuse/components/projectExperience.vue → src/views/recruit/personal/PersonalCenter/resume/online/components/projectExperience.vue


+ 0 - 0
src/views/recruit/personal/remuse/components/selfEvaluation.vue → src/views/recruit/personal/PersonalCenter/resume/online/components/selfEvaluation.vue


+ 0 - 0
src/views/recruit/personal/remuse/components/trainingExperience.vue → src/views/recruit/personal/PersonalCenter/resume/online/components/trainingExperience.vue


+ 10 - 16
src/views/recruit/personal/remuse/components/vocationalSkills.vue → src/views/recruit/personal/PersonalCenter/resume/online/components/vocationalSkills.vue

@@ -15,20 +15,13 @@
     <div v-else-if="!dataList?.length" class="resumeNoDataText">{{ $t('resume.dataDefaultPrompt') }}{{ $t('resume.vocationalSkills') }}...</div>
     <!-- 展示 -->
     <div v-else>
-      <div
-        :class="['dataList-item']" 
-        v-for="(k, i) in dataList" 
-        :key="i" 
-        @mouseenter="k.active = true" 
-        @mouseleave="k.active = false"
-      >
-        <span>{{ getText(k.skillId, skillList, 'nameCn', 'id') }}</span>
-        <span class="septal-line"></span>
-        <span style="color: var(--color-999);">{{ getText(k.level, skillLevelArr) }}</span>
-        <span class="float-right" v-if="k.active">
-          <v-btn variant="text" color="primary" prepend-icon="mdi-square-edit-outline" @click="handleEdit(k)">{{ $t('common.edit') }}</v-btn>
-          <v-btn variant="text" color="primary" prepend-icon="mdi-trash-can-outline" @click="handleDelete(k)">{{ $t('common.delete') }}</v-btn>
-        </span>
+      <div>
+        <v-chip v-for="k in dataList" class="mr-3" :key="k.id">
+          <span class="cursor-pointer" @click="handleEdit(k)">{{ getText(k.skillId, skillList, 'nameCn', 'id') }}/{{ getText(k.level, skillLevelArr) }}</span>
+          <template v-slot:append>
+            <v-icon class="ml-2" @click="handleDelete(k)">mdi-close-circle</v-icon>
+          </template>
+        </v-chip>
       </div>
     </div>
   </div>
@@ -48,7 +41,6 @@ const isEdit = ref(false)
 const formPageRef = ref()
 const type = ref('')
 const editId = ref(null)
-// const skills = [{ label: '英语四级', value: '0' }, { label: '英语六级', value: '1' }, { label: 'office办公软件', value: '2' }]
 
 const formItems = ref({
   options: [
@@ -63,6 +55,7 @@ const formItems = ref({
       itemValue: 'id',
       closeOnContentClick: true,
       flexStyle: 'mr-3',
+      disabled: false,
       col: 8,
       rules: [v => !!v || '请选择技能名称'],
       items: [],
@@ -128,7 +121,7 @@ const handleSave = async () => {
 const handleEdit = (item) => {
   editId.value = item.id
   formItems.value.options.forEach(e => {
-    // if (item[e.key]) e.value = item[e.key].toString()
+    if (e.key === 'skillId') e.disabled = true
     e.value = item[e.key]
   })
   isEdit.value = true
@@ -138,6 +131,7 @@ const handleAdd = () => {
   isEdit.value = true
   type.value = 'add'
   formItems.value.options.forEach(e => {
+    if (e.key === 'skillId') e.disabled = false
     e.value = null
   })
   isEdit.value = true

部分文件因文件數量過多而無法顯示