Bläddra i källkod

企业注册营业执照识别,求职者实名认证

Xiao_123 7 månader sedan
förälder
incheckning
438e72b8d7

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

@@ -322,3 +322,10 @@ export const getAccountBalance = async () => {
     url: '/app-api/pay/wallet/get'
   })
 }
+
+// 企业营业执照图片识别文字
+export const getBusinessLicenseOCR = async (url) => {
+  return await request.post({
+    url: '/app-api/menduner/system/recruit/enterprise/business/ocr?url=' + url
+  })
+}

+ 7 - 0
src/api/enterprise.js

@@ -178,4 +178,11 @@ export const getTagTreeDataApi = async (params) => {
     url: '/admin-api/menduner/system/tag/get/by/type',
     params
   })
+}
+
+// 检查招聘者账号密码是否需要修改
+export const checkRecruiterPassword = async () => {
+  return await request.get({
+    url: '/app-api/menduner/system/recruit/user/check/password'
+  })
 }

+ 16 - 0
src/api/personal/user.js

@@ -39,3 +39,19 @@ export const getUserRegisterEnterpriseApply = async (params) => {
     params
   })
 }
+
+// 求职者实名认证
+export const saveUserAuthentication = async (data) => {
+  return await request.post({
+    url: '/app-api/menduner/system/user-auth/save',
+    data
+  })
+}
+
+// 获取求职者实名认证信息
+export const getUserAuthentication = async (params) => {
+  return await request.get({
+    url: '/app-api/menduner/system/user-auth/get',
+    params
+  })
+}

+ 0 - 5
src/components/Position/similarPositions.vue

@@ -11,9 +11,6 @@
         <span class="float-right enterprise-address">{{ item.areaName }}</span>
       </div>
     </div>
-    <!-- <div class="text-center more-btn">
-      <v-btn color="primary" variant="outlined" class="buttons" :to="`/recruit/personal/company/details/${props.info.enterpriseId}?key=recruitmentPositions`">{{ $t('position.allBtn') }}</v-btn>
-    </div> -->
   </div>
 </template>
 
@@ -35,8 +32,6 @@ const handlePosition = (item) => {
 
 <style lang="scss" scoped>
 .position-box {
-  // position: relative;
-  // height: 430px;
   background-color: var(--color-f3);
   border-radius: 8px;
   padding: 20px 15px;

+ 11 - 4
src/components/Upload/img.vue

@@ -20,7 +20,7 @@
 <script setup>
 // 图片上传
 defineOptions({ name: 'upload-img'})
-import { ref } from 'vue'
+import { ref, watch } from 'vue'
 import { uploadFile } from '@/api/common'
 import { useI18n } from '@/hooks/web/useI18n'
 import Snackbar from '@/plugins/snackbar'
@@ -29,12 +29,19 @@ const emit = defineEmits(['success', 'delete', 'imgClick'])
 const props = defineProps({
   value: String,
   tips: String,
-  showCursor: Boolean
+  showCursor: Boolean,
+  showSnackbar: {
+    type: Boolean,
+    default: true
+  }
 })
 
 const { t } = useI18n()
 const src = ref('')
-if (props.value) src.value = props.value
+
+watch(() => props.value, (newVal) => {
+  src.value = newVal
+})
 
 // 选择文件
 const fileInput = ref()
@@ -59,7 +66,7 @@ const handleUploadFile = async (e) => {
   const { data } = await uploadFile(formData)
   if (!data) return Snackbar.error('上传失败')
   src.value = data
-  Snackbar.success(t('common.uploadSucMsg'))
+  if (props.showSnackbar) Snackbar.success(t('common.uploadSucMsg'))
   emit('success', data)
 }
 

+ 8 - 8
src/router/modules/components/recruit/personCenter.js

@@ -133,14 +133,14 @@ const personCenter = [
               //     enName: 'Account binding'
               //   }
               // },
-              // {
-              //   path: '/recruit/personal/personalCenter/accountSettings/realAuthentication',
-              //   component: () => import('@/views/recruit/personal/PersonalCenter/accountSettings/realAuthentication'),
-              //   meta: {
-              //     title: '实名认证',
-              //     enName: 'Real name authentication'
-              //   }
-              // },
+              {
+                path: '/recruit/personal/personalCenter/accountSettings/realAuthentication',
+                component: () => import('@/views/recruit/personal/PersonalCenter/accountSettings/realAuthentication'),
+                meta: {
+                  title: '实名认证',
+                  enName: 'Real name authentication'
+                }
+              }
               // {
               //   path: '/recruit/personal/personalCenter/accountSettings/privacySettings',
               //   component: () => import('@/views/recruit/personal/PersonalCenter/accountSettings/privacySettings'),

+ 88 - 38
src/views/recruit/entRegister/register.vue

@@ -15,27 +15,30 @@
         <!-- 标题 -->
         <div class="mb-10 mt-n8" style="font-size: 22px; font-weight: bold; text-align: center;">{{ $t('enterprise.registeringNewEnterprise') }}</div>
         <CtForm ref="CtFormRef" :items="formItems" style="width: 100%;">
-          <template #prepare>
-            <v-checkbox
-              v-model="isPrepare"
-              label="筹建中"
-              color="primary"
-              class="ml-1"
-              style="width: 150px; max-height: 38px;"
-              @change="isPrepareChange"
-            ></v-checkbox>
+          <template #businessLicense>
+            <!-- 上传照片 -->
+            <div class="d-flex flex-column mb-6">
+              <div style="color: var(--color-999);">
+                <span v-if="!prepareValue" class="mr-1" style="color: var(--v-error-base);">*</span>
+                <span>上传营业执照</span>
+                <span>支持jpg、jpeg、png格式,图片大小不得超过10M</span>
+              </div>
+              <div class="file-box">
+                <Img 
+                  class="mt-3" 
+                  v-model="licenseUrl" 
+                  tips="上传图片" 
+                  :showSnackbar="false" 
+                  @imgClick="showPreview = !showPreview" 
+                  :showCursor="true" 
+                  @success="handleUploadImg" 
+                  @delete="handleDeleteImg"
+                ></Img>
+              </div>
+            </div>
           </template>
         </CtForm>
-        <!-- 上传照片 -->
-        <div style="color: var(--color-999);">
-          <span v-if="!isPrepare" class="mr-1" style="color: var(--v-error-base);">*</span>
-          <span>上传营业执照</span>
-          <span>支持jpg、jpeg、png格式,图片大小不得超过10M</span>
-        </div>
-        <div class="file-box">
-          <Img class="mt-3" :value="licenseUrl" tips="上传图片" @imgClick="showPreview = !showPreview" :showCursor="true" @success="val => licenseUrl = val" @delete="licenseUrl = ''"></Img>
-        </div>
-        <div class="note mt-10">
+        <div class="note">
           <h4>注意事项:</h4>
           <span>企业名称为对外展示的企业名称,建议填写公司营业执照上的名称,请区分总公司和分公司</span>
         </div>
@@ -52,24 +55,28 @@
       </div>
     </v-card>
     <PreviewImg v-if="showPreview" :current="current" :list="[licenseUrl]" @close="showPreview = !showPreview"></PreviewImg>
+
+    <Loading :visible="loading"></Loading>
   </div>
 </template>
 
 <script setup>
+defineOptions({name: 'enterprise-enterpriseRegister-register'})
 import CtForm from '@/components/CtForm'
 import Snackbar from '@/plugins/snackbar'
 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 { onMounted, ref, computed } from 'vue';
 import { checkEmail } from '@/utils/validate'
-defineOptions({name: 'enterprise-enterpriseRegister-register'})
+import { getBusinessLicenseOCR } from '@/api/common'
+
 const { t } = useI18n()
 const CtFormRef = ref()
-
 const loginLoading = ref(false)
 
 // 图片预览
+const loading = ref(false)
 const showPreview = ref(false)
 const current = ref(0)
 const email = localStorage.getItem('loginAccount') || ''
@@ -86,12 +93,11 @@ import { useRoute } from 'vue-router'; const route = useRoute()
 const pageType = route?.query?.type || '' // type: noLoginToRegister:->登录页注册企业
 
 // 是否筹建中
-const isPrepare = ref(false)
 const isPrepareChange = () => {
   const code = formItems.value.options.find(e => e.key === 'code')
   if (code) {
-    code.label = isPrepare.value ? '企业统一社会信用代码' : '企业统一社会信用代码 *'
-    code.rules = isPrepare.value ? [] : [v => !!v || '请输入企业统一社会信用代码']
+    code.label = prepareValue.value ? '企业统一社会信用代码' : '企业统一社会信用代码 *'
+    code.rules = prepareValue.value ? [] : [v => !!v || '请输入企业统一社会信用代码']
   }
 }
 
@@ -109,6 +115,21 @@ const handlePassword = () => {
 
 const formItems = ref({
   options: [
+    {
+      type: 'ifRadio',
+      key: 'prepare',
+      value: false,
+      label: '是否筹建中 *',
+      width: 100,
+      items: [
+        { label: '是', value: true },
+        { label: '否', value: false }
+      ],
+      change: isPrepareChange
+    },
+    {
+      slotName: 'businessLicense'
+    },
     {
       type: 'text',
       key: 'name',
@@ -118,7 +139,6 @@ const formItems = ref({
       rules: [v => !!v || '请输入企业名称']
     },
     {
-      slotName: 'prepare',
       type: 'text',
       key: 'code',
       value: '',
@@ -129,23 +149,21 @@ const formItems = ref({
     {
       type: 'text',
       key: 'contactName',
-      value: '',
+      value: '林小姐',
       label: '联系人姓名 *',
-      // col: 6,
-      // flexStyle: 'mr-3',
       rules: [v => !!v || '请输入联系人姓名']
     },
     {
       type: 'phoneNumber',
       key: 'phone',
-      value: '',
+      value: '13229740091',
       label: '联系电话 *',
       rules: [v => !!v || '请输入联系电话']
     },
     {
       type: 'text',
       key: 'email',
-      value: email ? email : '',
+      value: email ? email : 'lin@qq.com',
       label: '企业邮箱 *',
       rules: [
         value => {
@@ -161,7 +179,7 @@ const formItems = ref({
     {
       type: 'password',
       key: 'password',
-      value: '',
+      value: 'Citu123456',
       appendInnerIcon: 'mdi-eye-off-outline',
       label: '账户登录密码 *',
       placeholder: '请输入账户登录密码',
@@ -180,7 +198,7 @@ const formItems = ref({
     {
       type: 'password',
       key: 'passwordConfirm',
-      value: '',
+      value: 'Citu123456',
       appendInnerIcon: 'mdi-eye-off-outline',
       label: '请再次输入账户登录密码 *',
       placeholder: '请再次输入账户登录密码',
@@ -209,8 +227,40 @@ const formItems = ref({
   ]
 })
 
+// 是否筹建中
+const prepareValue = computed(() => {
+  return formItems.value.options.find(e => e.key === 'prepare').value
+})
+
+// 识别营业执照图片
+const business = ref({})
+const getOcr = async () => {
+  loading.value = true
+  try {
+    const data = await getBusinessLicenseOCR(licenseUrl.value)
+    if (data && Object.keys(data).length) {
+      formItems.value.options.find(e => e.key === 'code').value = data.code
+      formItems.value.options.find(e => e.key === 'name').value = data.name
+      business.value = data
+    }
+  } finally {
+    loading.value = false
+  }
+}
+
 // 上传
 let licenseUrl = ref('')
+const handleUploadImg = (url) => {
+  licenseUrl.value = url
+  if (licenseUrl.value) getOcr()
+}
+
+const handleDeleteImg = () => {
+  licenseUrl.value = ''
+  business.value = {}
+  formItems.value.options.find(e => e.key === 'code').value = ''
+  formItems.value.options.find(e => e.key === 'name').value = ''
+}
 
 // 提交 企业注册
 const handleCommit = async () => {
@@ -218,14 +268,13 @@ const handleCommit = async () => {
   if (!valid) return
 
   const businessLicenseUrl = licenseUrl.value;
-  if (!isPrepare.value && !businessLicenseUrl) return Snackbar.warning('请上传营业执照图片')
+  if (!prepareValue.value && !businessLicenseUrl) return Snackbar.warning('请上传营业执照图片')
   const params = {
     businessLicenseUrl,
-    prepare: isPrepare.value,
+    prepare: prepareValue.value,
   }
   formItems.value.options.forEach(e => { params[e.key] = e.value })
-  // 邮箱登录密码校验
-  // if (params.password !== params.passwordConfirm) return Snackbar.warning('两次输入的密码不一致,请确认')
+  if (business.value && Object.keys(business.value).length) params.ocr = business.value
 
   await enterpriseRegisterApply(params)
   localStorage.removeItem('loginAccount')
@@ -240,7 +289,7 @@ const info = JSON.parse(localStorage.getItem('userApplyInfo'))
 if (info && Object.keys(info).length && info.status === '2') {
   failureReason.value = info?.reason || ''
   licenseUrl.value = info?.businessLicenseUrl
-  isPrepare.value = info?.prepare || false
+  // prepareValue.value = info?.prepare || false
   isPrepareChange()
   formItems.value.options.forEach(e => {
     if (e.key === 'passwordConfirm') e.value = info.password
@@ -249,6 +298,7 @@ if (info && Object.keys(info).length && info.status === '2') {
 }
 
 </script>
+
 <style lang="scss" scoped>
 .CtFormClass {
   margin: 0 auto;

+ 135 - 5
src/views/recruit/personal/PersonalCenter/accountSettings/realAuthentication.vue

@@ -1,12 +1,123 @@
 <template>
-  <div>
+  <div class="pl-1 pt-1">
     <h3>{{ $t('setting.realNameAuthentication') }}</h3>
-    <v-divider class="mb-4"></v-divider>
-    <div class="tips mt-2 mb-1">一经实名认证后将无法修改,请填写真实的实名信息!</div>
+    <div class="tips">一经实名认证后将无法修改,请填写真实的实名信息!</div>
+    <v-divider class="my-4"></v-divider>
+    <div v-if="authentication" class="ml-3">
+      <div class="topTip" v-if="info.status === '0'">审核中,请耐心等待</div>
+      <div v-if="info.status === '1'">
+        <v-icon color="primary">mdi-check-circle</v-icon>
+        已通过实名认证
+      </div>
+      <div class="topTip" v-if="info.status === '2'">
+        认证已被驳回,原因:{{ info.reason }}
+      </div>
+      <div class="box mt-5">
+        <div v-if="info.status === '1'">姓名:{{ info.name }}</div>
+        <div class="my-5" v-if="info.status === '1'">身份证号:{{ maskNumber(info.idNumber) }}</div>
+        <div class="d-flex" v-if="info.status !== '1'">
+          <span>人像照</span>
+          <div class="ml-10" style="width: 120px; height: 120px;">
+            <v-img :src="info.frontUrl" width="120" height="120" rounded alt=""/>
+          </div>
+        </div>
+        <div class="d-flex mt-5" v-if="info.status !== '1'">
+          <span>国徽照</span>
+          <div class="ml-10" style="width: 120px; height: 120px;">
+            <v-img :src="info.backUrl" width="120" height="120" rounded alt=""/>
+          </div>
+        </div>
+      </div>
+      <v-btn v-if="info.status === '2'" class="buttons mt-5" color="primary" @click="handleAgain">重新认证</v-btn>
+    </div>
+    <div v-else>
+      <div class="topTip" v-if="info.status !== '2'">为了您在平台有更好的操作体验,请进行实名认证</div>
+      <div class="d-flex align-center justify-center flex-column mt-5">
+        <CtForm ref="CtFormRef" :items="formItems" style="width: 300px;">
+          <template #backUrl="{ item }">
+            <div class="color-666 font-size-14 mr-5">{{ item.label }}</div>
+            <Img :value="item.value" @success="val => item.value = val" @delete="item.value = ''" @imgClick="handlePreview(item.value)"></Img>
+          </template>
+          <template #frontUrl="{ item }">
+            <div class="mt-5 d-flex">
+              <div class="color-666 font-size-14 mr-5">{{ item.label }}</div>
+              <Img :value="item.value" @success="val => item.value = val" @delete="item.value = ''" @imgClick="handlePreview(item.value)"></Img>
+            </div>
+          </template>
+        </CtForm>
+        <v-btn class="buttons mt-5" color="primary" @click="handleSave">{{ $t('common.submit') }}</v-btn>
+      </div>
+    </div>
   </div>
+
+  <PreviewImg v-if="showPreview" :list="imgList" @close="showPreview = !showPreview"></PreviewImg>
 </template>
 
 <script setup name="realAuthentication">
+import { ref } from 'vue'
+import { maskNumber } from '@/utils/validate'
+import { getUserAuthentication, saveUserAuthentication } from '@/api/personal/user'
+import Snackbar from '@/plugins/snackbar'
+
+const query = ref({})
+const info = ref({})
+const authentication = ref(false)
+const formItems = ref({
+  options: [
+    {
+      key: 'frontUrl',
+      slotName: 'frontUrl',
+      value: '',
+      label: '身份证-人像照 *',
+      rules: [v => !!v || '请上传']
+    },
+    {
+      slotName: 'backUrl',
+      key: 'backUrl',
+      value: '',
+      label: '身份证-国徽照 *',
+      rules: [v => !!v || '请上传']
+    }
+  ]
+})
+
+// 获取实名信息
+const getAuthInfo = async () => {
+  const data = await getUserAuthentication()
+  if (!data) return authentication.value = false
+  authentication.value = true
+  info.value = data
+}
+getAuthInfo()
+
+// 图片预览
+const showPreview = ref(false)
+const imgList = ref([])
+const handlePreview = (url) => {
+  imgList.value = [url]
+  showPreview.value = true
+}
+
+// 重新提交
+const handleAgain = () => {
+  formItems.value.options.forEach(item => {
+    item.value = info.value[item.key]
+  })
+  query.value.id = info.value.id
+  authentication.value = false
+}
+
+// 提交
+const handleSave = async () => {
+  formItems.value.options.forEach(item => {
+    query.value[item.key] = item.value
+  })
+  if (!query.value.backUrl || !query.value.frontUrl) return Snackbar.warning('请上传您的身份证照片')
+  await saveUserAuthentication(query.value)
+  Snackbar.success('提交成功')
+  query.value = {}
+  getAuthInfo()
+}
 </script>
 
 <style lang="scss" scoped>
@@ -14,11 +125,30 @@ h3 {
   font-size: 20px;
   text-align: left;
   font-weight: 600;
-  padding-bottom: 25px;
 }
 .tips {
   color: grey;
   font-size: 14px;
-  font-weight: 600;
+}
+.topTip {
+  background-color: #fff6ec;
+  color: var(--v-error-base);
+  padding: 12px 20px;
+  margin: 10px 0 40px;
+  font-size: 14px;
+}
+.box {
+  background-color: #f7f8fa;
+  border-radius: 6px;
+  color: var(--color-666);
+  font-size: 14px;
+  padding: 25px;
+}
+.upload {
+  width: 120px;
+  height: 120px;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+  cursor: pointer;
 }
 </style>

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

@@ -255,7 +255,8 @@ const loginClose = () => {
 const similarList = ref([])
 const getSimilarPositionList = async () => {
   if (!Object.keys(positionInfo).length) return
-  const { list } = await getSimilarPosition({ pageNo: 1, pageSize: 4, positionId: positionInfo.value.positionId, expType: positionInfo.value.expType, eduType: positionInfo.value.eduType })
+  const { list } = await getSimilarPosition({ pageNo: 1, pageSize: 4, id })
+  if (!list.length) return
   const items = list.splice(0, 4)
   similarList.value = dealDictArrayData([], items)
 }