Bläddra i källkod

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

lifanagju_citu 3 månader sedan
förälder
incheckning
cef5032802
25 ändrade filer med 517 tillägg och 134 borttagningar
  1. 3 0
      components.d.ts
  2. 9 0
      src/api/recruit/personal/personalCenter/student.js
  3. 40 0
      src/router/modules/components/recruit/personCenter.js
  4. 16 16
      src/store/user.js
  5. 21 10
      src/views/recruit/components/jobFairEntShare/index.vue
  6. 1 1
      src/views/recruit/enterprise/jobFair/details.vue
  7. 234 6
      src/views/recruit/enterprise/jobFair/index.vue
  8. 1 0
      src/views/recruit/enterprise/jobFair/job/item.vue
  9. 22 22
      src/views/recruit/enterprise/resume/components/filterPage.vue
  10. 22 15
      src/views/recruit/enterprise/resume/components/table.vue
  11. 4 4
      src/views/recruit/enterprise/resume/index.vue
  12. 0 3
      src/views/recruit/enterprise/systemManagement/groupAccount/inviteConfirm.vue
  13. 1 1
      src/views/recruit/personal/PersonalCenter/index.vue
  14. 0 7
      src/views/recruit/personal/PersonalCenter/resume/analysis/index.vue
  15. 1 1
      src/views/recruit/personal/PersonalCenter/resume/template/index.vue
  16. 11 0
      src/views/recruit/personal/PersonalCenter/student/EnterpriseRecommendationLetter/index.vue
  17. 11 0
      src/views/recruit/personal/PersonalCenter/student/InternshipButler/index.vue
  18. 11 0
      src/views/recruit/personal/PersonalCenter/student/InternshipCertificate/index.vue
  19. 11 0
      src/views/recruit/personal/PersonalCenter/student/InternshipReport/index.vue
  20. 5 4
      src/views/recruit/personal/PersonalCenter/student/information/index.vue
  21. 16 1
      src/views/recruit/personal/PersonalCenter/student/intershipCompany/index.vue
  22. 0 18
      src/views/recruit/personal/home/index.vue
  23. 1 0
      src/views/recruit/personal/jobFair/details/components/jobCard1.vue
  24. 4 2
      src/views/recruit/personal/jobFair/details/entJobCard.vue
  25. 72 23
      src/views/recruit/personal/jobFair/details/enterprises.vue

+ 3 - 0
components.d.ts

@@ -74,4 +74,7 @@ declare module 'vue' {
     VerifySlide: typeof import('./src/components/Verifition/Verify/VerifySlide.vue')['default']
     WangEditor: typeof import('./src/components/FormUI/wangEditor/index.vue')['default']
   }
+  export interface ComponentCustomProperties {
+    vLoading: typeof import('element-plus/es')['ElLoadingDirective']
+  }
 }

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

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

+ 40 - 0
src/router/modules/components/recruit/personCenter.js

@@ -195,6 +195,46 @@ const personCenter = [
                   title: '学生信息',
                   enName: 'Student Information'
                 }
+              },
+              {
+                path: '/recruit/personal/personalCenter/student/internshipCompany',
+                component: () => import('@/views/recruit/personal/PersonalCenter/student/intershipCompany/index.vue'),
+                meta: {
+                  title: '实习企业',
+                  enName: 'Internship Company'
+                }
+              },
+              {
+                path: '/recruit/personal/personalCenter/student/internshipReport',
+                component: () => import('@/views/recruit/personal/PersonalCenter/student/InternshipReport/index.vue'),
+                meta: {
+                  title: '实习报告',
+                  enName: 'Internship Report'
+                }
+              },
+              {
+                path: '/recruit/personal/personalCenter/student/internshipCertificate',
+                component: () => import('@/views/recruit/personal/PersonalCenter/student/InternshipCertificate/index.vue'),
+                meta: {
+                  title: '实习证书',
+                  enName: 'Internship Certificate'
+                }
+              },
+              {
+                path: '/recruit/personal/personalCenter/student/enterpriseRecommendationLetter',
+                component: () => import('@/views/recruit/personal/PersonalCenter/student/EnterpriseRecommendationLetter/index.vue'),
+                meta: {
+                  title: '企业推荐信',
+                  enName: 'Recommendation Letter'
+                }
+              },
+              {
+                path: '/recruit/personal/personalCenter/student/internshipButler',
+                component: () => import('@/views/recruit/personal/PersonalCenter/student/InternshipButler/index.vue'),
+                meta: {
+                  title: '实习管家',
+                  enName: 'Internship Butler'
+                }
               }
             ]
           }

+ 16 - 16
src/store/user.js

@@ -18,6 +18,7 @@ import { timesTampChange } from '@/utils/date'
 import { updateEventList } from '@/utils/eventList'
 import { getBaseInfoDictOfName } from '@/utils/getText'
 import { checkPersonBaseInfo } from '@/utils/check'
+import { getStudentInfo } from '@/api/recruit/personal/resume'
 // import Confirm from '@/plugins/confirm'
 
 // import { useIMStore } from './im'
@@ -34,7 +35,8 @@ export const useUserStore = defineStore('user',
       baseInfo: localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {}, // 人才信息
       entBaseInfo: localStorage.getItem('entBaseInfo') ? JSON.parse(localStorage.getItem('entBaseInfo')) : {}, // 企业个人信息
       userAccount: {}, // 用户账户信息
-      enterpriseUserAccount: {} // 企业账户信息
+      enterpriseUserAccount: {}, // 企业账户信息
+      studentInfo: localStorage.getItem('studentInfo') ? JSON.parse(localStorage.getItem('studentInfo')) : {}, // 学生信息
     }),
     actions: {
       // 个人用户注册并登录
@@ -98,7 +100,6 @@ export const useUserStore = defineStore('user',
       // 获取当前登录账户信息
       async getUserInfos () {
         try {
-          // const api = getIsEnterprise() ? getEnterprisingUserInfo : getUserInfo
           const data = await getUserInfo({ id: this.accountInfo.userId })
           this.userInfo = data
           localStorage.setItem('userInfo', JSON.stringify(data))
@@ -110,10 +111,8 @@ export const useUserStore = defineStore('user',
       // 获取当前登录账户的基本信息(人才信息)
       async getUserBaseInfos (userId = null, option) {
         try {
-          // const api = getIsEnterprise() ? null : getBaseInfo
           let data = await getBaseInfo({ userId: userId || this.accountInfo.userId })
           data = data || {}
-          // if (!data) return localStorage.setItem('baseInfo', '{}')
           this.baseInfo = await this.getFieldText(data)
           localStorage.setItem('baseInfo', JSON.stringify(this.baseInfo))
           localStorage.setItem('necessaryInfoReady', checkPersonBaseInfo(this.baseInfo) ? 'ready' : 'fddeaddc47868b') // 校验是否完善人才必填信息
@@ -121,6 +120,8 @@ export const useUserStore = defineStore('user',
             // 刚注册时让用户选择学生用户还是求职者用户,角色不同填写的基本信息不同。
             localStorage.setItem('chooseRole', 'showChooseRole')
           }
+          // 当前角色若为学生则获取学生信息
+          if (data?.type && Number(data.type) === 1) this.getStudentInformation()
         } catch (error) {
           Snackbar.error(error)
         }
@@ -227,19 +228,7 @@ export const useUserStore = defineStore('user',
           if (!data?.bizFlag) {  // 企业登录免费职位广告提示,除了true都弹窗
             localStorage.setItem('positionAd', 'showPositionAd')
           }
-          // // 检验必填信息
-          // const keyArr = ['industryId', 'financingStatus', 'scale', 'introduce', 'logoUrl'] // 必填信息列表
-          // let href = '/recruit/enterprise/entInfoSetting'
-          // const valid = Object.keys(data).length && keyArr.every(e => {
-          //   const bool = data[e] && data[e] !== 0
-          //   if (!bool && e === 'logoUrl') href = '/recruit/enterprise/entInfoSetting?tabKey=2'
-          //   return bool
-          // })
-          // if (!valid) {
-          //   localStorage.setItem('checkEnterpriseBaseInfoFalseHref', href)
-          // }
         } catch (error) {
-          // console.log(error)
         }
       },
 
@@ -265,6 +254,17 @@ export const useUserStore = defineStore('user',
           // 强制修改密码
           localStorage.setItem('entUpdatePassword', bool ? 'needChange' : 'doNotNeedChange')
         }
+      },
+
+      // 获取学生信息
+      async getStudentInformation () {
+        try {
+          const data = await getStudentInfo()
+          this.studentInfo = data
+          localStorage.setItem('studentInfo', data ? JSON.stringify(data) : '{}')
+        } catch (error) {
+          Snackbar.error(error.msg)
+        }
       }
     }
   },

+ 21 - 10
src/views/recruit/components/jobFairEntShare/index.vue

@@ -144,17 +144,28 @@ const drawCanvas = () => {
       entName: props.enterpriseName,
       backgroundColor: props.backgroundColor
     }
-    const result = await saveShareQuery(params)
-    const query = {
-      scene: 'id=' + result,
-      path: 'pagesB/jobFair/positionClassification',
-      width: 200,
-      autoColor: false,
-      checkPath: true,
-      hyaline: false
+    try {
+      const result = await saveShareQuery(params)
+      const query = {
+        scene: 'id=' + result,
+        path: 'pagesB/jobFair/positionClassification',
+        width: 200,
+        autoColor: false,
+        checkPath: true,
+        hyaline: false
+      }
+
+      try {
+        const data = await getJobAdvertisedShareQrcode(query)
+        secondImg.src = 'data:image/png;base64,' + data 
+      } catch {
+        loading.value = false
+      }
+      
+    } catch {
+      loading.value = false
     }
-    const data = await getJobAdvertisedShareQrcode(query)
-    secondImg.src = 'data:image/png;base64,' + data
+    
   }
 
   img.onerror = (error) => {

+ 1 - 1
src/views/recruit/enterprise/jobFair/details.vue

@@ -195,7 +195,7 @@ const handlePreview = (val) => {
 }
 
 const handleShare = () => {
-  positionList.value = jobList.value && jobList.value.length > 0 ? jobList.value.map(e => formatName(e.name)).slice(0, 2) : []
+  positionList.value = jobList.value && jobList.value.length > 0 ? jobList.value.map(e => formatName(e.name)).slice(0, 3) : []
   showShare.value = true
 }
 </script>

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

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

+ 1 - 0
src/views/recruit/enterprise/jobFair/job/item.vue

@@ -5,6 +5,7 @@
         <div class="d-flex justify-space-between" style="padding: 10px 20px;">
           <div class="position">
             <div class="d-flex align-center">
+              <svg-icon name="jobFair" class="mr-1" size="25"></svg-icon>
               <span class="position-name">{{ formatName(val.name) }}</span>
             </div>
             <div class="mt-3 other-info">

+ 22 - 22
src/views/recruit/enterprise/resume/components/filterPage.vue

@@ -24,7 +24,7 @@ import { getDict } from '@/hooks/web/useDictionaries'
 import { dealDictArrayData } from '@/utils/position'
 import { formatName } from '@/utils/getText'
 import Snackbar from '@/plugins/snackbar'
-// import { getJobFairList } from '@/api/recruit/enterprise/jobFair'
+import { getJobFairList } from '@/api/recruit/enterprise/jobFair'
 
 const emit = defineEmits(['confirm', 'cancel', 'reset'])
 const props = defineProps({ jobId: [String, Number], jobFairId: [String, Number] })
@@ -42,17 +42,17 @@ const formItems = ref({
 			returnObject: true,
       items: []
     },
-		// {
-    //   type: 'autocomplete',
-    //   key: 'jobFairId',
-    //   value: null,
-    //   label: '招聘会',
-    //   itemText: 'label',
-    //   itemValue: 'value',
-		// 	clearable: true,
-		// 	returnObject: true,
-    //   items: []
-    // },
+		{
+      type: 'autocomplete',
+      key: 'jobFairId',
+      value: null,
+      label: '招聘会',
+      itemText: 'label',
+      itemValue: 'value',
+			clearable: true,
+			returnObject: true,
+      items: []
+    },
 		{
 			type: 'text',
 			key: 'name',
@@ -142,16 +142,16 @@ onMounted(() => {
 			return
 		}
 		// 招聘会数据获取
-		// if (k.key === 'jobFairId') {
-		// 	const data = await getJobFairList()
-		// 	k.items = data.map(e => {
-		// 		return { label: e.title.replace(/<\/?p[^>]*>/gi, ''), value: e.id }
-		// 	})
-		// 	if (props.jobFairId) {
-		// 		k.value = k.items.find(e => e.value.toString() === props.jobFairId)
-		// 	}
-		// 	return
-		// }
+		if (k.key === 'jobFairId') {
+			const data = await getJobFairList()
+			k.items = data.map(e => {
+				return { label: e.title.replace(/<\/?p[^>]*>/gi, ''), value: e.id }
+			})
+			if (props.jobFairId) {
+				k.value = k.items.find(e => e.value.toString() === props.jobFairId)
+			}
+			return
+		}
 
 		// 投递职位
 		const data = await getJobAdvertised()

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

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

+ 4 - 4
src/views/recruit/enterprise/resume/index.vue

@@ -127,10 +127,10 @@ onMounted(() => {
     bounty.value = true
   }
   // 招聘会职位
-  // if (route.query?.jobFairId) {
-  //   if (tab.value !== 0) return delete query.value.jobFairId
-  //   query.value.jobFairId = route.query.jobFairId
-  // }
+  if (route.query?.jobFairId) {
+    if (tab.value !== 0) return delete query.value.jobFairId
+    query.value.jobFairId = route.query.jobFairId
+  }
 })
 
 // 分页

+ 0 - 3
src/views/recruit/enterprise/systemManagement/groupAccount/inviteConfirm.vue

@@ -77,8 +77,6 @@ const handleLogin = async () => {
     const setInfo = { email: joinPersonEmail, enterpriseId: enterpriseInfo.value.enterpriseName }
     // 如果用户登录后点击关闭填写简易人才信息,再次点击登录仅弹出填写,不再调用登录
     if (copyLoginData === (params.phone + params.code.toString()) && userId) {
-      // getUserBaseInfos(userId)
-      // return
       if (!checkPersonBaseInfo()) { // 强制填写个人信息
         dialogExtend('necessaryInfoDialog', { setInfo }).then(() => {
           join()
@@ -96,7 +94,6 @@ const handleLogin = async () => {
     const data = await useUserStore().handleSmsLogin(params)
     copyLoginData = params.phone + params.code.toString()
     userId = data.userId
-    // getUserBaseInfos(data.userId)
     if (!checkPersonBaseInfo()) { // 强制填写个人信息
       dialogExtend('necessaryInfoDialog', { setInfo }).then(() => {
         join()

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

@@ -53,6 +53,7 @@ import { getCurrentLocaleLang } from '@/utils/lang.js'
 import personCenterRoute from '@/router/modules/components/recruit/personCenter'
 import { useUserStore } from '@/store/user'
 
+const userStore = useUserStore()
 const info = localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {}
 
 const menuHide = { // 是否隐藏
@@ -88,7 +89,6 @@ const getList = (arr, obj = []) => {
   return obj
 }
 
-const userStore = useUserStore()
 // 更新账户信息
 const updateAccountInfo = async () => {
   await userStore.getUserAccountInfo()

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

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

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

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

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

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

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

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

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

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

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

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

+ 5 - 4
src/views/recruit/personal/PersonalCenter/student/information/index.vue

@@ -16,13 +16,14 @@
 defineOptions({name: 'personal-personCenter-studentInformation-index'})
 import { ref } from 'vue'
 import { saveStudentSimpleInfo } from '@/api/recruit/personal/shareJob'
-import { schoolList, departmentList, getStudentInfo } from '@/api/recruit/personal/resume'
+import { schoolList, departmentList } from '@/api/recruit/personal/resume'
 import { useI18n } from '@/hooks/web/useI18n'
 import Snackbar from '@/plugins/snackbar'
 import { isValidIdCard18 } from '@/utils/validate'
+import { useUserStore } from '@/store/user'
 
 const { t } = useI18n()
-
+const userStore = useUserStore()
 const overlay = ref(false)
 
 const CtFormRef = ref()
@@ -149,7 +150,7 @@ const getDepartmentList = async (e) => {
 
 // 获取学生基本信息
 const studentInfoFun = async () => {
-  const data = await getStudentInfo()
+  const data = JSON.parse(localStorage.getItem('studentInfo') || '{}')
   if (data.schoolId) getDepartmentList(data.schoolId)
   // 回显
   items.value.options.forEach(e => {
@@ -169,8 +170,8 @@ const handleSubmit = async () => {
     params[item.key] = item.value
   })
   await saveStudentSimpleInfo(params)
-  // studentInfoFun()
   setTimeout(async () => {
+    await userStore.getStudentInformation()
     Snackbar.success(t('common.submittedSuccessfully'))
     overlay.value = false
   }, 1000)

+ 16 - 1
src/views/recruit/personal/PersonalCenter/student/intershipCompany/index.vue

@@ -1,9 +1,24 @@
 <template>
-	<div>xxx</div>
+	<div>
+		<v-tabs v-model="tab" align-tabs="start" color="primary" bg-color="#f7f8fa">
+      <v-tab v-for="(k, index) in ['等待中', '进行中', '已结束']" :key="index" :value="index">{{ k }}</v-tab>
+    </v-tabs>
+	</div>
 </template>
 
 <script setup>
 defineOptions({ name: 'PersonalCenterStudentInternshipCompany'})
+import { ref } from 'vue'
+import { practiceProcess } from '@/api/recruit/personal/personalCenter/student.js'
+
+const tab = ref(0)
+const studentInfo = ref(localStorage.getItem('studentInfo') ? JSON.parse(localStorage.getItem('studentInfo')) : {})
+
+const getList = async () => {
+	const data = await practiceProcess({ studentId: studentInfo.value.userId })
+	console.log(data, 'list')
+}
+getList()
 </script>
 
 <style scoped lang="scss">

+ 0 - 18
src/views/recruit/personal/home/index.vue

@@ -117,24 +117,6 @@ const handleSearch = (val) => {
   window.open(val ? `/recruit/personal/position?content=${val}` : `/recruit/personal/position`)
 }
 
-// const store = useUserStore()
-// const simple = ref(localStorage.getItem('simpleCompleteDialogHaveBeenShow'))
-// const showSimplePage = ref(false) // 只提示一次
-// if (!getToken()) showSimplePage.value = false
-// nextTick(() => {
-//   if (getToken()) {
-//     showSimplePage.value = simple.value && JSON.parse(simple.value) ? true : false
-//   }
-// })
-// const handleInfoClose = () => {
-//   localStorage.setItem('simpleCompleteDialogHaveBeenShow', false)
-// }
-// 更新用户基本信息
-// const handleUpdateInfo = async () => {
-//   handleInfoClose()
-//   await store.getUserBaseInfos(null)
-// }
-
 const adImgWidth = ref(document?.documentElement?.clientWidth ?
   Math.floor(document.documentElement.clientWidth/2.2) > 500 ?
   Math.floor(document.documentElement.clientWidth/2.2) : 500

+ 1 - 0
src/views/recruit/personal/jobFair/details/components/jobCard1.vue

@@ -5,6 +5,7 @@
 				<div @click="handleClick(val.id)">
 					<div class="d-flex justify-space-between">
 						<div class="d-flex align-center">
+							<svg-icon name="jobFair" class="mr-1" size="25"></svg-icon>
 							<p class="job-name font-weight-medium" v-ellipse-tooltip>{{ formatName(val.name) }}</p>
 						</div>
 						<p v-if="!val.payFrom && !val.payTo" class="salary">面议</p>

+ 4 - 2
src/views/recruit/personal/jobFair/details/entJobCard.vue

@@ -2,7 +2,9 @@
 <template>
   <div class="position-relative">
     <div v-if="jobFair?.contentImg" class="position-fixed" style="right: 24px; top: 20px; z-index: 999">
-      <v-btn icon="mdi-share-all" color="primary" size="x-large" @click.stop="handleShare"></v-btn>
+      <div class="cursor-pointer pa-2 white-bgc" style="border-radius: 8px;" @click.stop="handleShare">
+        <v-icon color="primary" size="50">mdi-share</v-icon>
+      </div>
     </div>
 
     <div :style="{'background-color': jobFair.backgroundColour || '#fff', 'min-height': '100vh'}" class="pt-10">
@@ -132,7 +134,7 @@ const handlePreview = (src) => {
 }
 
 const handleShare = () => {
-  positionList.value = items.value && items.value.length > 0 ? items.value.map(e => formatName(e.name)).slice(0, 2) : []
+  positionList.value = items.value && items.value.length > 0 ? items.value.map(e => formatName(e.name)).slice(0, 3) : []
   showShare.value = true
 }
 </script>

+ 72 - 23
src/views/recruit/personal/jobFair/details/enterprises.vue

@@ -1,28 +1,38 @@
 <!-- 企业 -->
 <template>
   <div class="position-relative">
-    <!-- 轮播 -->
-    <v-carousel 
-      v-if="jobFair?.pcHeadImg && jobFair?.pcHeadImg.length > 0" 
-      :show-arrows="jobFair?.pcHeadImg.length > 1 ? 'hover' : false" 
-      cycle :hide-delimiters="true" 
-      :style="{'height': isMobile ? '200px' : '500px'}"
-    >
-			<v-carousel-item v-for="(k, i) in jobFair?.pcHeadImg" :key="i">
-				<img :src="k" :lazy-src="k" style="width: 100%; height:100%;">
-			</v-carousel-item>
-		</v-carousel>
-
     <!-- 招聘会分享按钮 -->
     <div v-if="jobFair?.shareImg" class="position-fixed" style="right: 24px; top: 20px; z-index: 999;">
-      <v-btn icon="mdi-share-all" color="primary" size="x-large" @click.stop="handleShare"></v-btn>
+      <div class="cursor-pointer pa-2 white-bgc" style="border-radius: 8px;" @click.stop="handleShare">
+        <v-icon color="primary" size="50">mdi-share</v-icon>
+      </div>
     </div>
 
-    <div :style="{'background-color': jobFair.backgroundColour || '#fff', 'min-height': jobFair?.pcHeadImg && jobFair?.pcHeadImg.length > 0 ? `calc(100vh - ${isMobile ? '200px' : '500px'})` : '100vh'}">
+    <div ref="observedElement">
+      <!-- 轮播 -->
+      <v-carousel
+        v-if="jobFair?.pcHeadImg && jobFair?.pcHeadImg.length > 0" 
+        :show-arrows="jobFair?.pcHeadImg.length > 1 ? 'hover' : false" 
+        cycle :hide-delimiters="true" 
+        style="height: auto;"
+        :style="{'max-height': isMobile ? '200px' : '553px'}"
+      >
+          <v-carousel-item v-for="(k, i) in jobFair?.pcHeadImg" :key="i">
+            <img :src="k" :lazy-src="k" style="width: 100%; height:100%;">
+          </v-carousel-item>
+      </v-carousel>
+    </div>
+
+
+    <div
+      :style="{
+        'background-color': jobFair.backgroundColour || '#fff', 
+        'min-height': jobFair?.pcHeadImg && jobFair?.pcHeadImg.length > 0 ? `calc(100vh - ${carouselHeight}px)` : '100vh'
+      }">
       <div :class="{'default-width': !isMobile}">
-        <SearchBar class="pt-5" v-model="query.keyword" :isMobile="isMobile" placeholder="搜索关键字" @handleSearch="handleSearch" />
+        <SearchBar class="py-5" v-model="query.keyword" :isMobile="isMobile" placeholder="搜索关键字" @handleSearch="handleSearch" />
         <!-- 类别展示 -->
-        <div class="d-flex align-center" v-if="jobFair?.tag && jobFair.tag.length" style="overflow-x: auto;">
+        <!-- <div class="d-flex align-center" v-if="jobFair?.tag && jobFair.tag.length" style="overflow-x: auto;">
           <div 
             v-for="(val, index) in jobFair.tag" 
             :key="index" 
@@ -33,12 +43,18 @@
           >
             {{ val.title }}
           </div>
-        </div>
-
-        <div 
-          v-if="!items.length" 
-          class="text-center" 
-          :style="{'color': jobFair.backgroundColour ? '#fff' : 'var(--v-primary-base)', 'height': `calc(100vh - ${jobFair?.tag && jobFair.tag.length ? '574px': '500px'})`, 'line-height': `calc(100vh - ${jobFair?.tag && jobFair.tag.length ? '574px': '500px'})`}"
+        </div> -->
+        <v-tabs v-model="tab" align-tabs="center" v-if="jobFair?.tag && jobFair.tag.length">
+          <v-tab v-for="(k, i) in jobFair.tag" class="tab-item" :key="i" :value="k.value" @click="handleTabClick(i)">{{ k.title }}</v-tab>
+        </v-tabs>
+        <div
+          v-if="!items.length"
+          class="text-center"
+          :style="{
+            'color': jobFair.backgroundColour ? '#fff' : 'var(--v-primary-base)', 
+            'height':  `calc(100vh - ${carouselHeight}px)`, 
+            'line-height': `calc(100vh - ${carouselHeight}px)`
+          }"
         >
           {{ loadingType === 1 ? loadingText[loadingType] : '暂无数据,去看看其他吧~' }}
         </div>
@@ -70,7 +86,7 @@
 
 <script setup>
 defineOptions({name: 'jobFair-enterprises'})
-import { ref, reactive, onMounted } from 'vue'
+import { ref, reactive, onMounted, onUnmounted } from 'vue'
 import { getJobFairEnterprisePage, getJobFair, getJobFairEntJobPage, saveShareQuery } from '@/api/recruit/personal/jobFair'
 import EntCard from './components/entCard1.vue'
 import JobCard from './components/jobCard1.vue'
@@ -94,10 +110,33 @@ const previewSrc = ref('')
 const canvasWidth = 500
 const canvasHeight = 900
 
+const observedElement = ref(null)
+let resizeObserver
+const carouselHeight = ref(0)
+
+const handleResize = (entries) => {
+  for (let entry of entries) {
+    if (entry.target === observedElement.value) {
+      carouselHeight.value = entry.contentRect.height
+    }
+  }
+}
+
 const isMobile = ref(false)
 onMounted(() => {
   const userAgent = navigator.userAgent
   isMobile.value = /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i.test(userAgent)
+  if (observedElement.value) {
+    resizeObserver = new ResizeObserver(handleResize)
+    resizeObserver.observe(observedElement.value)
+  }
+})
+
+onUnmounted(() => {
+  if (resizeObserver && observedElement.value instanceof Element) {
+    resizeObserver.unobserve(observedElement.value)
+  }
+  resizeObserver.disconnect()
 })
 
 const items = ref([])
@@ -233,6 +272,16 @@ const handleShare = () => {
 </script>
 
 <style scoped lang="scss">
+.tab-item {
+  font-weight: 700;
+  font-size: 1.5rem;
+  color: #fff;
+  padding: 10px 25px;
+}
+:deep(.v-tab__slider) {
+  height: 4px;
+  border-radius: 2px;
+}
 .tag-item {
   font-weight: 700;
   font-size: 24px;