소스 검색

同步火苗儿学生认证信息

lifanagju_citu 2 달 전
부모
커밋
3c1a9875ee

+ 0 - 3
components.d.ts

@@ -74,7 +74,4 @@ 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']
-  }
 }

+ 25 - 1
src/api/recruit/personal/resume/index.js

@@ -205,6 +205,30 @@ export const enterpriseSearchByName = async (params) => {
   })
 }
 
+// 学校列表
+export const schoolList = async (data) => {
+  return await request.post({
+    url: 'app-api/flames/school/list',
+    data
+  })
+}
+
+// 院系列表
+export const departmentList = async (data) => {
+  return await request.post({
+    url: 'app-api/flames/school/department/list',
+    data
+  })
+}
+
+// 获取学生基本信息
+export const getStudentInfo = async (data) => {
+  return await request.post({
+    url: 'app-api/menduner/system/person/resume/student/get',
+    data
+  })
+}
+
 // 保存附件
 export const savePersonResumeCv = async (data) => {
   return await request.post({
@@ -270,4 +294,4 @@ export const resumePersonFillAll = async () => {
   return await request.post({
     url: '/app-api/menduner/system/person/resume/person/fill/all'
   })
-}
+}

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

@@ -29,4 +29,12 @@ export const savePersonSimpleInfo = async (data) => {
     url: '/app-api/menduner/system/person/resume/info/simple/save',
     data
   })
-}
+}
+
+// 保存学生基本信息
+export const saveStudentSimpleInfo = async (data) => {
+  return await request.post({
+    url: '/app-api/menduner/system/person/resume/student/save',
+    data
+  })
+}

+ 5 - 5
src/permission.js

@@ -20,7 +20,7 @@ router.beforeEach(async (to, from, next) => {
   if (!localStorage.getItem('mallTemplate')) {
     await mallStore.getMallDiyTemplate()
   }
-  
+
   localStorage.setItem('routerTest', to.path) // 本地环境保存代码热更新会导致路径缺失问题
   const tokenIndex = getIsEnterprise() ? 1: 2
   start()
@@ -34,15 +34,15 @@ router.beforeEach(async (to, from, next) => {
     // 强制修改密码
     if (localStorage.getItem('entUpdatePassword') === 'needChange') fullScreen('entUpdatePassword')
     // 强制填写个人信息 fddeaddc47868b/ready
-    else if (localStorage.getItem('necessaryInfoReady') === 'fddeaddc47868b' && tokenIndex === 2) dialogExtend('necessaryInfoDialog')
-    
+    else if (localStorage.getItem('chooseRole') === 'showChooseRole' && to.path !== '/register/selectedPersonRole' && tokenIndex === 2) next('/register/selectedPersonRole')
+    else if (localStorage.getItem('necessaryInfoReady') === 'fddeaddc47868b' && tokenIndex === 2 && localStorage.getItem('chooseRole') !== 'showChooseRole') dialogExtend('necessaryInfoDialog')
     // 企业登录-招聘会广告
     else if (!localStorage.getItem('jobFairAd') && tokenIndex === 1) {
       localStorage.setItem('jobFairAd', 'hasBeenShow')
       dialogExtend('jobFairAd')
     }
-      // 企业登录免费职位广告提示
-    else if (localStorage.getItem('positionAd')) {
+    // 企业登录-免费职位广告提示
+    else if (localStorage.getItem('positionAd') && tokenIndex === 1) {
       localStorage.setItem('positionAd', '')
       dialogExtend('positionAd')
     }

+ 10 - 4
src/plugins/dialogExtend/components/necessaryInfoDialog.vue

@@ -11,7 +11,8 @@
       @other="handleLogout"
       @submit="simpleInfoSubmit"
     >
-      <infoForm ref="formRef" :option="option"></infoForm>
+      <studentInfoForm v-if="isStudent" ref="formRef" :option="option"></studentInfoForm>
+      <infoForm v-else ref="formRef" :option="option"></infoForm>
     </CtDialog>
     <Loading :visible="overlay"></Loading>
   </v-app>
@@ -19,8 +20,9 @@
 
 <script setup>
 defineOptions({name: 'dialogExtend-dialog'})
-import infoForm from './infoForm'
-import { savePersonSimpleInfo } from '@/api/recruit/personal/shareJob'
+import infoForm from './infoForm.vue'
+import studentInfoForm from './studentInfoForm.vue'
+import { savePersonSimpleInfo, saveStudentSimpleInfo } from '@/api/recruit/personal/shareJob'
 import CtDialog from '@/components/CtDialog'
 import { useUserStore } from '@/store/user'; const userStore = useUserStore()
 import { useRoute } from 'vue-router'; const route = useRoute()
@@ -48,6 +50,8 @@ const props = defineProps({
 })
 
 const dialog = ref(false)
+const isStudent = ref(localStorage.getItem('chooseRole') === 'student')
+
 // const isMobile = ref(false)
 onMounted(() => {
   const parsedUrl = new URL(window.location.href)
@@ -74,12 +78,14 @@ const simpleInfoSubmit = async () => {
     const obj = await formRef.value.getQuery()
     if (!obj) return
     overlay.value = true
-    await savePersonSimpleInfo(obj)
+    const apiFun = isStudent.value ? saveStudentSimpleInfo : savePersonSimpleInfo
+    await apiFun(obj)
     Snackbar.success('保存成功')
     // const info = localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {}
     // localStorage.setItem('baseInfo', JSON.stringify({ ...info, ...obj }))
     // localStorage.setItem('necessaryInfoReady', 'ready') //
     await useUserStore().getUserBaseInfos() // 更新用户信息
+    if (isStudent.value) localStorage.removeItem('chooseRole')
     dialog.value = false
     overlay.value = false
     props.sure()

+ 343 - 0
src/plugins/dialogExtend/components/studentInfoForm.vue

@@ -0,0 +1,343 @@
+<template>
+  <div style="width: 100%;">
+    <CtForm ref="formPageRef" :items="items">
+      <!-- 头像 -->
+      <template #avatar="{ item }">
+        <div style="color: #7a7a7a; min-width: 52px;">头像:</div>
+        <div class="avatarsBox" @mouseover="showIcon = true" @mouseleave="showIcon = false">
+          <v-avatar class="elevation-5" size=80 :image="getUserAvatar(item.value, male)"></v-avatar>
+          <div v-show="showIcon" @click="openFileInput" v-bind="$attrs" class="mdi mdi-camera-outline">
+            <input
+              type="file"
+              ref="fileInput"
+              accept="image/png, image/jpg, image/jpeg"
+              style="display: none;"
+              @change="handleUploadFile"
+            />
+          </div>
+        </div>
+        <div style="font-size: 14px; color: var(--color-999);">只支持JPG、JPEG、PNG类型的图片,大小不超过20M</div>
+      </template>
+    </CtForm>
+  </div>
+  <ImgCropper :visible="isShowCopper" :image="selectPic" :cropBoxResizable="true" @submit="handleHideCopper" :aspectRatio="1 / 1" @close="isShowCopper = false"></ImgCropper>
+</template>
+
+<script setup>
+import { getDict } from '@/hooks/web/useDictionaries'
+defineOptions({name: 'dialogExtend-InfoForm'})
+import { reactive, ref } from 'vue'
+import { schoolList, departmentList } from '@/api/recruit/personal/resume'
+import { getUserAvatar } from '@/utils/avatar'
+import { uploadFile } from '@/api/common'
+import Snackbar from '@/plugins/snackbar'
+import { useI18n } from '@/hooks/web/useI18n'; const { t } = useI18n()
+import { isValidIdCard18 } from '@/utils/validate'
+
+const props = defineProps({
+  option: {
+    type: Object,
+    default: () => {}
+  }
+})
+const setInfo = ref(props.option?.setInfo ? props.option.setInfo : {})
+const formPageRef = ref()
+let query = reactive({})
+
+// 图片裁剪
+const selectPic = ref('')
+const isShowCopper = ref(false)
+
+const male = ref('1')
+const showIcon = ref(false)
+const items = ref({
+  options: [
+    {
+      slotName: 'avatar',
+      key: 'avatar',
+      value: '',
+      flexStyle: 'align-center mb-3'
+    },
+    {
+      type: 'text',
+      key: 'name',
+      value: '',
+      default: null,
+      label: '姓名 *',
+      outlined: true,
+      rules: [
+        value => {
+          if (value) return true
+          return '请输入您的中文名'
+        },
+        value => {
+          var regex = /^[\u4e00-\u9fa5]+$/
+          if (regex.test(value)) return true
+          return '请输入正确的中文名'
+        }
+      ]
+    },
+    {
+      type: 'autocomplete',
+      key: 'sex',
+      value: '1', // '1' ? '男' : '女'
+      default: '1',
+      label: '性别 *',
+      outlined: true,
+      dictTypeName: 'menduner_sex',
+      rules: [v => !!v || '请选择性别'],
+      items: [],
+      change: val => male.value = val
+    },
+    {
+      type: 'text',
+      key: 'idCardNo',
+      value: '',
+      label: '身份证号码 *',
+      rules: [
+        value => {
+          if (!value) {
+            return '请输入您的身份证号码'
+          }
+          return true
+        },
+        value => {
+          if (!isValidIdCard18(value)) {
+            return '请输入正确的身份证号码'
+          }
+          return true
+        }
+      ]
+    },
+    {
+      type: 'phoneNumber',
+      key: 'phone',
+      value: '',
+      clearable: true,
+      label: '联系手机号 *',
+      rules: [v => !!v || '请填写联系手机号']
+    },
+    {
+      type: 'datePicker',
+      mode: 'date',
+      labelWidth: 80,
+      key: 'birthday',
+      value: '1990-01-01',
+      defaultValue: new Date(1990, 1, 1),
+      label: '出生日期 *',
+      disabledFutureDates: true,
+      format: 'YYYY/MM/DD',
+      flexStyle: 'mb-7',
+      outlined: true,
+      rules: [v => !!v || '请选择出生日期']
+    },
+    {
+      type: 'autocomplete',
+      key: 'schoolId',
+      value: null,
+      default: null,
+      label: '就读学校 *',
+      outlined: true,
+      itemText: 'schoolName',
+      itemValue: 'schoolId',
+      rules: [v => !!v || '请选择就读学校'],
+      items: [],
+      change: e => getDepartmentList(e),
+    },
+    {
+      type: 'autocomplete',
+      key: 'schoolDepartmentName',
+      value: null,
+      default: null,
+      label: '所在院系 *',
+      outlined: true,
+      itemText: 'departmentTitle',
+      itemValue: 'schoolDepartmentId',
+      rules: [v => !!v || '请选择所在院系'],
+      items: []
+    },
+    {
+      type: 'text',
+      key: 'majorName',
+      value: '',
+      default: null,
+      label: '所学专业 *',
+      outlined: true,
+      rules: [v => !!v || '请输入所学专业']
+    },
+    {
+      type: 'text',
+      key: 'schoolClassName',
+      value: '',
+      default: null,
+      label: '所在班级 *',
+      outlined: true,
+      rules: [v => !!v || '请填写所在班级']
+    },
+    {
+      type: 'text',
+      key: 'studentNo',
+      value: '',
+      default: null,
+      label: '学号 *',
+      outlined: true,
+      rules: [v => !!v || '请填写学号']
+    },
+    {
+      type: 'text',
+      key: 'emergencyContactName',
+      value: '',
+      default: null,
+      label: '紧急联系人姓名 *',
+      outlined: true,
+      rules: [v => !!v || '请填写紧急联系人姓名']
+    },
+    {
+      type: 'phoneNumber',
+      key: 'emergencyContactPhone',
+      value: '',
+      clearable: true,
+      label: '紧急联系人手机号 *',
+      rules: [v => !!v || '请填写紧急联系人手机号']
+    },
+  ]
+})
+
+if (import.meta.env.VITE_NODE_ENV === 'production') {
+  items.value.options = items.value.options.filter(e => e.slotName !== 'analysis')
+}
+
+// // 学校下拉列表
+const getSchoolListData = async () => {
+  const item = items.value.options.find(e => e.key === 'schoolId')
+  if (!item) return
+  const { records } = await schoolList({current: 1,size: 9999})
+  item.items = records || []
+}
+getSchoolListData()
+
+const getDepartmentList = async (e) => {
+  const item = items.value.options.find(e => e.key === 'schoolDepartmentName')
+  if (!item) return
+  const query = {
+    page: { size: 9999,	current: 1	},
+    entity: { schoolId: e }
+  }
+  const res = await departmentList(query)
+  const list = res?.records?.length ? res.records : []
+  item.items = list.map(e => e.entity)
+}
+
+// 选择文件
+const fileInput = ref()
+const clicked = ref(false)
+const openFileInput = () => {
+  if (clicked.value) return
+  clicked.value = true
+  fileInput.value.click()
+  clicked.value = false
+}
+
+// 上传头像
+const accept = ['jpg', 'png', 'jpeg']
+const handleUploadFile = async (e) => {
+  const file = e.target.files[0]
+  if (!file) return
+
+  const arr = file.name.split('.')
+  const fileType = arr?.length ? arr[arr.length-1] : ''
+  if (!accept.includes(fileType)) return Snackbar.warning('请上传图片格式文件')
+
+  const size = file.size
+  if (size / (1024*1024) > 20) {
+    Snackbar.warning(t('common.fileSizeExceed'))
+    return
+  }
+
+  const reader = new FileReader()
+  reader.readAsDataURL(file)
+  reader.onload = () => {
+    selectPic.value = String(reader.result)
+    isShowCopper.value = true
+  }
+}
+
+const handleHideCopper = (data) => {
+  isShowCopper.value = false
+  if (data) {
+    const { file } = data
+    if (!file) return
+
+    const formData = new FormData()
+    formData.append('file', file)
+    formData.append('path', 'img')
+    uploadFile(formData).then(async ({ data }) => {
+      if (!data) return
+      items.value.options.find(e => e.key === 'avatar').value = data
+    })
+  }
+}
+
+// 获取字典内容
+const getDictData = async (dictTypeName, key) => {
+  const item = items.value.options.find(e => e.key === key)
+  if (item) {
+    const apiType = dictTypeName === 'positionSecondData' ? 'positionSecondData' : 'dict'
+    const { data } = await getDict(dictTypeName, apiType === 'dict' ? null : {}, apiType)
+    item.items = data
+  }
+}
+
+const userInfo = ref(localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo')) : {})
+const baseInfo = ref(localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {})
+items.value.options.forEach((e) => {
+  if (e.dictTypeName) getDictData(e.dictTypeName, e.key) // 查字典set options
+  if (userInfo.value && userInfo.value[e.key]) e.value = userInfo.value[e.key] // 人才信息回显
+  if (baseInfo.value && baseInfo.value[e.key]) e.value = baseInfo.value[e.key] // 人才信息回显
+  if (e.key === 'sex' && e.value === '0') e.value = e.default
+  if (setInfo.value[e.key]) e.value = setInfo.value[e.key]
+})
+
+const getQuery = async () => {
+  const { valid } = await formPageRef.value.formRef.validate()
+  if (!valid) return false
+  const obj = {}
+  items.value.options.forEach(e => {
+    if (Object.prototype.hasOwnProperty.call(e, 'data')) return obj[e.key] = e.data
+    obj[e.key] = e.value === '' ? null : e.value
+  })
+  if (!obj.avatar) obj.avatar = getUserAvatar(null, obj.sex)
+  query = Object.assign(query, obj)
+  return query
+}
+
+defineExpose({
+  getQuery
+})
+</script>
+<style scoped lang="scss">
+.avatarsBox {
+  height: 80px;
+  width: 80px;
+  position: relative;
+  cursor: pointer;
+  // margin: 32px;
+  // margin-right: 40px;
+  margin: 0 40px 0 32px;
+  .img {
+    width: 100%;
+    height: 100%;
+  }
+  .mdi {
+    font-size: 42px;
+    color: #fff;
+  }
+  div {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    border-radius: 50%;
+  }
+}
+</style>

+ 12 - 1
src/router/modules/components/recruit/personCenter.js

@@ -199,6 +199,17 @@ const personCenter = [
               enName: 'Shipping Address',
               icon: 'mdi-map-marker-outline'
             }
+          },
+          {
+            path: '/recruit/personal/personalCenter/studentInformation',
+            name: 'Student Information',
+            permissionName: 'studentInformation',
+            component: () => import('@/views/recruit/personal/PersonalCenter/studentInformation'),
+            meta: {
+              title: '学生信息',
+              enName: 'Student Information',
+              icon: 'mdi-account-school-outline'
+            }
           }
         ]
       }
@@ -206,4 +217,4 @@ const personCenter = [
   }
 ]
 
-export default personCenter
+export default personCenter

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

@@ -23,6 +23,15 @@ const remainingRouter = [
       title: '注册'
     }
   },
+  {
+    path: '/register/selectedPersonRole',
+    component: () => import('@/views/register/selectedPersonRole.vue'),
+    name: 'registerSelectPersonRole',
+    meta: {
+      hidden: true,
+      title: '个人用户选择角色'
+    }
+  },
   {
     path: '/register/person',
     component: () => import('@/views/register/person.vue'),
@@ -145,4 +154,4 @@ const routeArray = [
   ...common
 ]
 
-export default routeArray
+export default routeArray

+ 15 - 11
src/store/user.js

@@ -2,12 +2,12 @@ import { defineStore } from 'pinia'
 import { setToken, removeToken, setRefreshToken, getToken } from '@/utils/auth'
 import {
   smsLogin,
-  passwordLogin, 
+  passwordLogin,
   getBaseInfo,
-  switchLoginOfEnterprise, 
-  getEnterprisingUserInfo, 
-  logoutToken, 
-  logout 
+  switchLoginOfEnterprise,
+  getEnterprisingUserInfo,
+  logoutToken,
+  logout
 } from '@/api/common'
 import { getUserInfo } from '@/api/personal/user'
 import { getEntUpdatePasswordCheck } from '@/api/recruit/enterprise/information'
@@ -65,7 +65,7 @@ export const useUserStore = defineStore('user',
             localStorage.setItem('expiresTime', res.expiresTime) // token过期时间
             updateEventList(true) // 获取规则配置跟踪列表
             await this.getUserInfos()
-            await this.getUserBaseInfos()
+            await this.getUserBaseInfos('', { chooseRole: data.chooseRole })
             resolve(res)
           }).catch(err => { reject(err) })
         })
@@ -108,7 +108,7 @@ export const useUserStore = defineStore('user',
         }
       },
       // 获取当前登录账户的基本信息(人才信息)
-      async getUserBaseInfos (userId = null) {
+      async getUserBaseInfos (userId = null, option) {
         try {
           // const api = getIsEnterprise() ? null : getBaseInfo
           let data = await getBaseInfo({ userId: userId || this.accountInfo.userId })
@@ -117,6 +117,10 @@ export const useUserStore = defineStore('user',
           this.baseInfo = await this.getFieldText(data)
           localStorage.setItem('baseInfo', JSON.stringify(this.baseInfo))
           localStorage.setItem('necessaryInfoReady', checkPersonBaseInfo(this.baseInfo) ? 'ready' : 'fddeaddc47868b') // 校验是否完善人才必填信息
+          if (option?.chooseRole) {
+            // 刚注册时让用户选择学生用户还是求职者用户,角色不同填写的基本信息不同。
+            localStorage.setItem('chooseRole', 'showChooseRole')
+          }
         } catch (error) {
           Snackbar.error(error)
         }
@@ -152,7 +156,7 @@ export const useUserStore = defineStore('user',
         this.baseInfo = {}
         this.accountInfo = {}
 
-        // 商城模版数据不清缓存
+        // 商城模版数据不清缓存
         const mallTemplate = localStorage.getItem('mallTemplate')
         localStorage.clear()
         localStorage.setItem('mallTemplate', mallTemplate)
@@ -172,14 +176,14 @@ export const useUserStore = defineStore('user',
         localStorage.setItem('accountInfo', JSON.stringify(data))
         localStorage.setItem('expiresTime', data.expiresTime)
         updateEventList(false)
-        
+
         // 企业受邀加入企业,只保存token等操作。
         if (res?.onlySetToken) return
 
         await this.updatePasswordCheck() // 检查密码是否需要修改
         await this.getEnterpriseInfo()
         await this.getEnterpriseUserAccountInfo()
-        
+
         Snackbar.success(res?.type === 'emailLogin' ? '登录成功' : '切换成功')
         let href = '/recruit/enterprise'
         // 是否存在重定向
@@ -196,7 +200,7 @@ export const useUserStore = defineStore('user',
       async getEnterpriseInfo (check) {
         const result = await getEnterprisingUserInfo()
         this.entBaseInfo = result
-        
+
         // 是否为企业账号管理员
         const isAdmin = result.userType === '1'
         localStorage.setItem('isAdmin', isAdmin)

+ 2 - 1
src/utils/check.js

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

+ 2 - 1
src/utils/whiteList.js

@@ -21,8 +21,9 @@ export const showImprovePersonaInfo = (url) => {
     '/invite',
     '/enterpriseInvite',
     '/register/company',
+    '/register/selectedPersonRole',
     // '/recruit/entRegister/joiningEnterprise',
     // '/recruit/entRegister/inReview',
   ]
   return checkWhiteListFun(url, list)
-}
+}

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

@@ -53,6 +53,12 @@ import { getCurrentLocaleLang } from '@/utils/lang.js'
 import personCenterRoute from '@/router/modules/components/recruit/personCenter'
 import { useUserStore } from '@/store/user'
 
+const info = localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')) : {}
+
+const menuHide = { // 是否隐藏
+  studentInformation: (info?.type && Number(info.type) !== 1) // 学生信息管理。 type:0是求职者,1是学生
+}
+
 // 左侧菜单列表
 const list = computed(() => {
   return getList(personCenterRoute[0].children[0].children)
@@ -62,6 +68,7 @@ console.log(import.meta.env.VITE_NODE_ENV, '当前环境变量===========')
 const getList = (arr, obj = []) => {
   arr.forEach(element => {
     if (element.show) return
+    if (menuHide[element.permissionName]) return
     let data = {}
     data = {
       title: element?.meta?.title,

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

@@ -0,0 +1,182 @@
+<template>
+  <div style="padding: 20px 30px">
+    <div class="resume-header mb-3">
+      <div class="resume-title">学生信息认证</div>
+    </div>
+    <div class="d-flex flex-column align-center pt-5">
+      <CtForm ref="CtFormRef" :items="items" style="width: 900px;"></CtForm>
+      <v-btn class="buttons mt-5" color="primary" @click.stop="handleSubmit">{{ $t('common.save') }}</v-btn>
+    </div>
+  </div>
+
+  <Loading :visible="overlay"></Loading>
+</template>
+
+<script setup>
+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 { useI18n } from '@/hooks/web/useI18n'
+import Snackbar from '@/plugins/snackbar'
+import { isValidIdCard18 } from '@/utils/validate'
+
+const { t } = useI18n()
+
+const overlay = ref(false)
+
+const CtFormRef = ref()
+const items = ref({
+  options: [
+    {
+      type: 'autocomplete',
+      key: 'schoolId',
+      value: null,
+      default: null,
+      label: '就读学校 *',
+      outlined: true,
+      itemText: 'schoolName',
+      itemValue: 'schoolId',
+      rules: [v => !!v || '请选择就读学校'],
+      items: [],
+      change: e => getDepartmentList(e),
+    },
+    {
+      type: 'autocomplete',
+      key: 'schoolDepartmentName',
+      value: null,
+      default: null,
+      label: '所在院系 *',
+      outlined: true,
+      itemText: 'departmentTitle',
+      itemValue: 'schoolDepartmentId',
+      rules: [v => !!v || '请选择所在院系'],
+      items: []
+    },
+    {
+      type: 'text',
+      key: 'majorName',
+      value: '',
+      default: null,
+      label: '所学专业 *',
+      outlined: true,
+      rules: [v => !!v || '请输入所学专业']
+    },
+    {
+      type: 'text',
+      key: 'schoolClassName',
+      value: '',
+      default: null,
+      label: '所在班级 *',
+      outlined: true,
+      rules: [v => !!v || '请填写所在班级']
+    },
+    {
+      type: 'text',
+      key: 'studentNo',
+      value: '',
+      default: null,
+      label: '学号 *',
+      outlined: true,
+      rules: [v => !!v || '请填写学号']
+    },
+    {
+      type: 'text',
+      key: 'idCardNo',
+      value: '',
+      label: '身份证号码 *',
+      rules: [
+        value => {
+          if (!value) {
+            return '请输入您的身份证号码'
+          }
+          return true
+        },
+        value => {
+          if (!isValidIdCard18(value)) {
+            return '请输入正确的身份证号码'
+          }
+          return true
+        }
+      ]
+    },
+    {
+      type: 'text',
+      key: 'emergencyContactName',
+      value: '',
+      default: null,
+      label: '紧急联系人姓名 *',
+      outlined: true,
+      rules: [v => !!v || '请填写紧急联系人姓名']
+    },
+    {
+      type: 'phoneNumber',
+      key: 'emergencyContactPhone',
+      value: '',
+      clearable: true,
+      label: '紧急联系人手机号 *',
+      rules: [v => !!v || '请填写紧急联系人手机号']
+    },
+  ]
+})
+
+// 左侧加mr
+items.value.options.forEach((e, index) => {
+  e.col = 6
+  if ((index + 2) % 2 === 0) e.flexStyle = 'mr-3'
+})
+
+// // 学校下拉列表
+const getSchoolListData = async () => {
+  const item = items.value.options.find(e => e.key === 'schoolId')
+  if (!item) return
+  const { records } = await schoolList({current: 1,size: 9999})
+  item.items = records || []
+}
+getSchoolListData()
+
+const getDepartmentList = async (e) => {
+  const item = items.value.options.find(e => e.key === 'schoolDepartmentName')
+  if (!item) return
+  const query = {
+    page: { size: 9999,	current: 1	},
+    entity: { schoolId: e }
+  }
+  const res = await departmentList(query)
+  const list = res?.records?.length ? res.records : []
+  item.items = list.map(e => e.entity)
+}
+
+// 获取学生基本信息
+const getStudentInfoFun = async () => {
+  const data = await getStudentInfo()
+  if (data.schoolId) getDepartmentList(data.schoolId)
+  // 回显
+  items.value.options.forEach(e => {
+    if (data[e.key]) e.value = data[e.key]
+  })
+}
+getStudentInfoFun()
+
+
+// 提交
+const handleSubmit = async () => {
+  const { valid } = await CtFormRef.value.formRef.validate()
+  if (!valid) return
+  overlay.value = true
+  const params = {}
+  items.value.options.forEach(item => {
+    params[item.key] = item.value
+  })
+  await saveStudentSimpleInfo(params)
+  // getStudentInfoFun()
+  setTimeout(async () => {
+    Snackbar.success(t('common.submittedSuccessfully'))
+    overlay.value = false
+  }, 1000)
+}
+
+</script>
+
+<style scoped lang="scss">
+</style>

+ 1 - 1
src/views/register/person.vue

@@ -68,7 +68,7 @@ const handleRegister = async () => {
   try {
     // isLogin:企业注册申请被驳回后,再次提交时需先登录个人账号
     // props.isLogin ? await userStore.handleSmsLogin({ ...phoneRef.value.loginData, autoRegister: true }) : await userStore.handleUserRegister({ ...phoneRef.value.loginData })
-    await userStore.handleSmsLogin({ ...phoneRef.value.loginData, autoRegister: true }) // 自动注册登录
+    await userStore.handleSmsLogin({ ...phoneRef.value.loginData, autoRegister: true, chooseRole: true }) // 自动注册登录
     Snackbar.success(props.isCompany ? '手机号验证成功' : '注册成功')
     if (!props.isCompany) {
       localStorage.setItem('simpleCompleteDialogHaveBeenShow', true) // 个人登录简易基本信息填写弹窗open-status

+ 78 - 0
src/views/register/selectedPersonRole.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="register-box" :style="{'background-image': 'url(' + webContent.loginBgUrl + ')'}">
+    <navBar v-if="!isMobile" :showLoginBtn="false" class="navBar"></navBar>
+    <div class="register-content">
+      <h2 style="color: #666; font-weight: 400;">请选择当前角色</h2>
+      <div class="d-flex mt-16">
+        <div style="width: 50%;" class="cursor-pointer item d-flex flex-column justify-center align-center" @click="handleClickRole(0)">
+          <v-icon color="primary" size="100">mdi-account-circle-outline</v-icon>
+          <span class="color-primary" style="font-size: 24px">职场人士</span>
+        </div>
+        <div style="width: 50%; border-left: 1px solid #ccc;" class="cursor-pointer item d-flex flex-column justify-center align-center" @click="handleClickRole(1)">
+          <v-icon color="primary" size="100">mdi-account-school-outline</v-icon>
+          <span class="color-primary" style="font-size: 24px">在校学生</span>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import navBar from '@/layout/personal/navBar.vue'
+defineOptions({ name: 'register-selectedPersonRole'})
+// import { useRouter } from 'vue-router'; const router = useRouter()
+import { ref, onMounted, nextTick } from 'vue'
+import { webContentStore } from '@/store/webContent'
+
+const webContent = webContentStore()
+const isMobile = ref(false)
+onMounted(async () => {
+  await webContent.getSystemWebContent()
+  const userAgent = navigator.userAgent
+  isMobile.value = /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i.test(userAgent)
+})
+
+const handleClickRole = (type) => {
+  // localStorage.removeItem('chooseRole')
+  if (type) {
+    // 学生用户
+    localStorage.setItem('chooseRole', 'student') // 校验是否完善人才必填信息
+  } else {
+    // 普通用户
+    localStorage.removeItem('chooseRole')
+  }
+  nextTick(() => {
+    window.location.href = '/recruitHome'
+  })
+}
+</script>
+
+<style scoped lang="scss">
+.register-box {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  background-size: cover;
+}
+.register-content {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  translate: -50% -50%;
+  width: 660px;
+  height: 453px;
+  background-color: #fff;
+  border-radius: 8px;
+  text-align: center;
+  padding: 90px 62px;
+}
+.item:hover {
+  span {
+    border-bottom: 1px solid #00897B;
+  }
+}
+.navBar {
+  position: absolute;
+  top: 0;
+}
+</style>