register.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. <template>
  2. <div class="login-box py-5">
  3. <v-card class="pa-5" :class="isMobile? 'mobileBox' : 'default-width'" :elevation="isMobile? '0' : '3'">
  4. <!-- 标题 -->
  5. <div class="mt-3" v-if="!isMobile">
  6. <v-btn v-if="pageType !== 'noLoginToRegister'" color="primary" variant="text" @click="router.push('/recruitHome')">{{ `<< 回到首页` }}</v-btn>
  7. <div v-else style="height: 30px;"></div>
  8. </div>
  9. <!-- 表单 -->
  10. <div class="CtFormClass" :style="{width: isMobile ? '' : '600px'}">
  11. <div v-if="failureReason" class="mb-8" style="color: red;">
  12. <span class="mr-5">《审核不通过》</span>
  13. <span>原因:{{ failureReason }}</span>
  14. </div>
  15. <!-- 标题 -->
  16. <div class="mb-10" :class="isMobile ? 'mt-0': 'mt-n8'" style="font-size: 22px; font-weight: bold; text-align: center;">{{ $t('enterprise.registeringNewEnterprise') }}</div>
  17. <CtForm ref="CtFormRef" :items="formItems" style="width: 100%;">
  18. <template #businessLicense>
  19. <!-- 上传照片 -->
  20. <div class="d-flex flex-column mb-6">
  21. <div style="color: var(--color-999);">
  22. <span v-if="!prepareValue" class="mr-1" style="color: var(--v-error-base);">*</span>
  23. <span>上传营业执照</span>
  24. <span>支持jpg、jpeg、png格式,图片大小不得超过10M</span>
  25. </div>
  26. <div class="file-box">
  27. <Img
  28. class="mt-3"
  29. tips="上传图片"
  30. :value="licenseUrl"
  31. :showSnackbar="false"
  32. @imgClick="showPreview = !showPreview"
  33. :showCursor="true"
  34. @success="handleUploadImg"
  35. @delete="handleDeleteImg"
  36. ></Img>
  37. </div>
  38. </div>
  39. </template>
  40. <template #contacts>
  41. <!-- 联系人 -->
  42. <v-btn class="mb-5" style="width: 100%; text-align: center;" variant="text" color="primary" prepend-icon="mdi-plus-box" @click="handleAddContact">添加联系人</v-btn>
  43. </template>
  44. </CtForm>
  45. <div class="note">
  46. <h4>注意事项:</h4>
  47. <span>企业名称为对外展示的企业名称,建议填写公司营业执照上的名称,请区分总公司和分公司</span>
  48. </div>
  49. </div>
  50. <div class="text-center">
  51. <!-- 提交 -->
  52. <v-btn
  53. :loading="loginLoading"
  54. color="primary" class="white--text my-8" min-width="350"
  55. @click="handleCommit"
  56. >
  57. {{ $t('common.complete') }}
  58. </v-btn>
  59. </div>
  60. </v-card>
  61. <PreviewImg v-if="showPreview" :current="current" :list="[licenseUrl]" @close="showPreview = !showPreview"></PreviewImg>
  62. <Loading :visible="loading"></Loading>
  63. <CtDialog :visible="showContactList" title="添加联系人" :footer="true" widthType="3" @close="showContactList = false" @submit="contactSubmit">
  64. <div style="min-height: 50vh;">
  65. <div>
  66. <div v-for="(item, index) in contactCopy" :key="index" class="contactItemCard">
  67. <div class="mb-3 pl-3" style="font-size: 13px; color: 00897B; border-left: 5px solid #00897B;">{{ index ? '联系人' + index : '管理员' }}</div>
  68. <TextUI v-model="item.contactName" :item="{...contactNameObj}"></TextUI>
  69. <TextUI v-model="item.phone" :item="{...phoneObj}"></TextUI>
  70. <TextUI v-model="item.email" :item="{...emailObj}"></TextUI>
  71. <TextUI v-model="item.password" :item="{...passwordObj}" @blur="passwordChange(item)"></TextUI>
  72. <TextUI v-model="item.passwordConfirm" :item="{...passwordConfirmObj}" @blur="passwordChange(item)"></TextUI>
  73. <v-btn v-if="index" style="width: 100%; text-align: center;" variant="text" color="error" @click="delContact(index)">删除</v-btn>
  74. </div>
  75. </div>
  76. <v-btn class="mb-5 mt-3" style="width: 100%; text-align: center;" variant="text" color="primary" prepend-icon="mdi-plus-box" @click="addMore">继续添加</v-btn>
  77. </div>
  78. </CtDialog>
  79. </div>
  80. </template>
  81. <script setup>
  82. defineOptions({name: 'enterprise-enterpriseRegister-register'})
  83. import CtForm from '@/components/CtForm'
  84. import Snackbar from '@/plugins/snackbar'
  85. import { useI18n } from '@/hooks/web/useI18n'
  86. import { useRouter } from 'vue-router'; const router = useRouter()
  87. import { enterpriseRegisterApply } from '@/api/personal/user'
  88. import { onMounted, ref, computed } from 'vue';
  89. import { checkEmail } from '@/utils/validate'
  90. import { getBusinessLicenseOCR } from '@/api/common'
  91. import Confirm from '@/plugins/confirm'
  92. import TextUI from '@/components/FormUI/TextInput'
  93. const { t } = useI18n()
  94. const CtFormRef = ref()
  95. const loginLoading = ref(false)
  96. // 图片预览
  97. const loading = ref(false)
  98. const showPreview = ref(false)
  99. const current = ref(0)
  100. const business = ref({})
  101. let licenseUrl = ref('')
  102. const email = localStorage.getItem('loginAccount') || ''
  103. // 组件挂载后添加事件监听器
  104. const isMobile = ref(false)
  105. onMounted(() => {
  106. const userAgent = navigator.userAgent
  107. 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)
  108. })
  109. import { useRoute } from 'vue-router'; const route = useRoute()
  110. const pageType = route?.query?.type || '' // type: noLoginToRegister:->登录页注册企业
  111. // const handleSecondConfirm = (value, item) => {
  112. // debugger
  113. // // const obj = formItems.value.options.find(e => e.key === 'passwordConfirm')
  114. // item.type = item.type === 'password' ? 'text' : 'password'
  115. // item.appendInnerIcon = item.type === 'password' ? 'mdi-eye-off-outline' : 'mdi-eye-outline'
  116. // }
  117. // const handlePassword = (value, item) => {
  118. // // const obj = formItems.value.options.find(e => e.key === 'password')
  119. // item.type = item.type === 'password' ? 'text' : 'password'
  120. // item.appendInnerIcon = item.type === 'password' ? 'mdi-eye-off-outline' : 'mdi-eye-outline'
  121. // }
  122. const showContactList = ref(false)
  123. const contactInfo = { contactName: '', phone: '', email: '', password: '', passwordConfirm:'' }
  124. let contactList = [{ ...contactInfo }]
  125. const contactCopy = ref([])
  126. const contactSubmit = () => {
  127. let falseValueIndex = null
  128. let falseKey = null
  129. contactCopy.value.forEach((e, index) => {
  130. if (falseValueIndex !== null) return
  131. if (e && Object.keys(e).length) {
  132. Object.keys(e).forEach(key => {
  133. if (falseValueIndex !== null) return
  134. if (!e[key]) {
  135. falseValueIndex = index
  136. falseKey = key
  137. }
  138. })
  139. }
  140. })
  141. const textList = {
  142. contactName: '姓名',
  143. phone: '联系电话',
  144. email: '企业邮箱',
  145. password: '登录密码',
  146. passwordConfirm: '登录密码',
  147. }
  148. if (falseValueIndex || falseValueIndex === 0) {
  149. const text = falseValueIndex ? `请完善联系人${falseValueIndex}的【${textList[falseKey]}】` : `请完善管理员的【${textList[falseKey]}】`
  150. Confirm(t('common.confirmTitle'), text, { hideCancelBtn: true })
  151. return
  152. }
  153. contactList = [...contactCopy.value]
  154. showContactList.value = false
  155. saveRegisterInfo()
  156. }
  157. const handleAddContact = () => {
  158. contactCopy.value = [...contactList]
  159. showContactList.value = true
  160. }
  161. const addMore = () => {
  162. contactCopy.value.push({...contactInfo})
  163. }
  164. const delContact = (index) => {
  165. Confirm('系统提示', '是否确认删除?').then(async () => {
  166. contactCopy.value.splice(index, 1)
  167. })
  168. }
  169. const passwordChange = (item) => {
  170. if (item.password && item.passwordConfirm && item.password !== item.passwordConfirm) {
  171. Snackbar.warning('两次输入的密码不一致')
  172. }
  173. }
  174. // 注册信息保存
  175. const enterpriseRegisterInfo = ref(localStorage.getItem('enterpriseRegisterInfo') ? JSON.parse(localStorage.getItem('enterpriseRegisterInfo')) : {})
  176. const saveRegisterInfo = () => {
  177. const obj = {
  178. licenseUrl: licenseUrl.value,
  179. ocr: business.value
  180. }
  181. formItems.value.options.forEach(e => {
  182. if (e.key) obj[e.key] = e.value
  183. })
  184. obj.contactList = contactList
  185. localStorage.setItem('enterpriseRegisterInfo', JSON.stringify(obj))
  186. }
  187. const codeLabelTet = '企业统一社会信用代码(可从上传的营业执照自动识别)'
  188. // 是否筹建中
  189. const isPrepareChange = () => {
  190. const code = formItems.value.options.find(e => e.key === 'code')
  191. if (code) {
  192. code.label = prepareValue.value ? codeLabelTet : codeLabelTet + ' *'
  193. code.rules = prepareValue.value ? [] : [v => !!v || '请输入企业统一社会信用代码']
  194. }
  195. saveRegisterInfo()
  196. }
  197. const formItems = ref({
  198. options: [
  199. {
  200. type: 'ifRadio',
  201. key: 'prepare',
  202. value: false,
  203. label: '是否筹建中 *',
  204. width: 100,
  205. items: [
  206. { label: '是', value: true },
  207. { label: '否', value: false }
  208. ],
  209. change: isPrepareChange
  210. },
  211. {
  212. slotName: 'businessLicense'
  213. },
  214. {
  215. type: 'text',
  216. key: 'name',
  217. value: '',
  218. label: '企业名称(需要与营业执照完全一致,可从上传的营业执照自动识别)*',
  219. counter: 50,
  220. rules: [v => !!v || '请输入企业名称'],
  221. blur: saveRegisterInfo
  222. },
  223. {
  224. type: 'text',
  225. key: 'anotherName',
  226. value: '',
  227. label: '企业别称',
  228. counter: 50,
  229. blur: saveRegisterInfo
  230. },
  231. {
  232. type: 'text',
  233. key: 'code',
  234. value: '',
  235. counter: 18,
  236. label: codeLabelTet + ' *',
  237. rules: [v => !!v || '请输入企业统一社会信用代码'],
  238. blur: saveRegisterInfo
  239. },
  240. {
  241. slotName: 'contacts'
  242. },
  243. {
  244. type: 'textarea',
  245. key: 'description',
  246. value: '',
  247. clearable: true,
  248. resize: true,
  249. counter: 500,
  250. rows: 2,
  251. label: '备注/说明',
  252. blur: saveRegisterInfo
  253. },
  254. ]
  255. })
  256. // 是否筹建中
  257. const prepareValue = computed(() => {
  258. return formItems.value.options.find(e => e.key === 'prepare').value
  259. })
  260. // 识别营业执照图片
  261. const getOcr = async () => {
  262. loading.value = true
  263. try {
  264. const data = await getBusinessLicenseOCR(licenseUrl.value)
  265. if (data && Object.keys(data).length) {
  266. formItems.value.options.find(e => e.key === 'code').value = data.code
  267. formItems.value.options.find(e => e.key === 'name').value = data.name
  268. business.value = data
  269. saveRegisterInfo()
  270. } else {
  271. licenseUrl.value = ''
  272. Confirm(t('common.confirmTitle'), '营业执照图片识别失败,请重新上传清晰合法图片', { hideCancelBtn: true })
  273. }
  274. } catch (error) {
  275. licenseUrl.value = ''
  276. Confirm(t('common.confirmTitle'), error, { hideCancelBtn: true })
  277. } finally {
  278. loading.value = false
  279. }
  280. }
  281. // 上传
  282. const handleUploadImg = (url) => {
  283. licenseUrl.value = url
  284. if (licenseUrl.value) getOcr()
  285. }
  286. const handleDeleteImg = () => {
  287. licenseUrl.value = ''
  288. business.value = {}
  289. formItems.value.options.find(e => e.key === 'code').value = ''
  290. formItems.value.options.find(e => e.key === 'name').value = ''
  291. saveRegisterInfo()
  292. }
  293. // 提交 企业注册
  294. const handleCommit = async () => {
  295. const { valid } = await CtFormRef.value.formRef.validate()
  296. if (!valid) return
  297. const businessLicenseUrl = licenseUrl.value;
  298. if (!prepareValue.value && !businessLicenseUrl) return Snackbar.warning('请上传营业执照图片')
  299. if (!contactList || !contactList.length || !contactList[0].contactName) return Snackbar.warning('请添加联系人信息')
  300. const params = {
  301. businessLicenseUrl,
  302. prepare: prepareValue.value,
  303. }
  304. formItems.value.options.forEach(e => {
  305. if (e.key) params[e.key] = e.value
  306. })
  307. if (business.value && Object.keys(business.value).length) params.ocr = business.value
  308. // 联系人
  309. params.contacts = contactList
  310. params.contactName = contactList[0].contactName
  311. params.phone = contactList[0].phone
  312. params.email = contactList[0].email
  313. await enterpriseRegisterApply(params)
  314. localStorage.removeItem('loginAccount')
  315. localStorage.removeItem('enterpriseRegisterInfo')
  316. Snackbar.success(t('common.submittedSuccessfully'))
  317. router.push({ path: '/recruit/entRegister/inReview' })
  318. }
  319. // 不通过的企业注册申请 重新发起
  320. const failureReason = ref('')
  321. const info = JSON.parse(localStorage.getItem('userApplyInfo'))
  322. // 审核不通过的数据回显
  323. if (info && Object.keys(info).length && info.status === '2') {
  324. failureReason.value = info?.reason || ''
  325. licenseUrl.value = info?.businessLicenseUrl
  326. // prepareValue.value = info?.prepare || false
  327. formItems.value.options.forEach(e => {
  328. if (e.key === 'passwordConfirm') e.value = info.password
  329. else e.value = info[e.key]
  330. })
  331. isPrepareChange()
  332. } else {
  333. // 表单信息保存
  334. if (enterpriseRegisterInfo.value && Object.keys(enterpriseRegisterInfo.value).length) {
  335. licenseUrl.value = enterpriseRegisterInfo.value.licenseUrl
  336. business.value = enterpriseRegisterInfo.value?.ocr
  337. contactList = enterpriseRegisterInfo.value?.contactList || []
  338. formItems.value.options.forEach(e => {
  339. if (e.key === 'passwordConfirm') e.value = enterpriseRegisterInfo.value.password
  340. else e.value = enterpriseRegisterInfo.value[e.key]
  341. })
  342. isPrepareChange()
  343. }
  344. }
  345. const contactNameObj = {
  346. type: 'text',
  347. key: 'contactName',
  348. value: '',
  349. label: '联系人姓名 *',
  350. rules: [v => !!v || '请输入联系人姓名'],
  351. }
  352. const phoneObj = {
  353. type: 'phoneNumber',
  354. key: 'phone',
  355. value: '',
  356. label: '联系电话 *',
  357. rules: [v => !!v || '请输入联系电话'],
  358. }
  359. const emailObj = {
  360. type: 'text',
  361. key: 'email',
  362. value: email ? email : '',
  363. label: '企业邮箱 *(此邮箱将用于日后“登录邮箱”)',
  364. // label: '企业邮箱 * (此邮箱将作为企业登录的账号)',
  365. rules: [
  366. value => {
  367. if (value) return true
  368. return '请输入企业邮箱'
  369. },
  370. value => {
  371. if (checkEmail(value)) return true
  372. return '请输入正确的企业邮箱'
  373. }
  374. ],
  375. }
  376. const passwordObj = {
  377. type: 'password',
  378. key: 'password',
  379. value: '',
  380. password: true,
  381. appendInnerIcon: 'mdi-eye-off-outline',
  382. label: '账户登录密码 *',
  383. placeholder: '请输入账户登录密码',
  384. // appendInnerClick: handlePassword,
  385. rules: [
  386. value => {
  387. if (value) return true
  388. return '请输入账户登录密码'
  389. },
  390. value => {
  391. if (/^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{8,16}$/.test(value)) return true
  392. return '请输入8-16位数由数字、大小写字母组成的密码'
  393. }
  394. ],
  395. }
  396. const passwordConfirmObj = {
  397. type: 'password',
  398. key: 'passwordConfirm',
  399. value: '',
  400. password: true,
  401. appendInnerIcon: 'mdi-eye-off-outline',
  402. label: '请再次输入账户登录密码 *',
  403. placeholder: '请再次输入账户登录密码',
  404. // appendInnerClick: handleSecondConfirm,
  405. rules: [
  406. value => {
  407. if (value) return true
  408. return '请再次输入密码'
  409. },
  410. ],
  411. }
  412. </script>
  413. <style lang="scss" scoped>
  414. .CtFormClass {
  415. margin: 0 auto;
  416. }
  417. .note {
  418. color: var(--color-666);
  419. font-size: 14px;
  420. line-height: 32px;
  421. }
  422. .login-box {
  423. position: relative;
  424. width: 100%;
  425. height: 100%;
  426. background-image: url('https://www.mendunerhr.com/images/userfiles/92d7e4a755e2428b94aab3636d5047f3/images/recruitment/adImages/2018/11/1920x940.jpg');
  427. background-size: cover;
  428. }
  429. .file-box {
  430. display: flex;
  431. flex-wrap: wrap; /* 允许换行 */
  432. width: 100%; /* 设置容器宽度 */
  433. .file-item {
  434. height: 80px;
  435. width: 100px;
  436. border-radius: 5px;
  437. margin-right: 8px;
  438. margin-top: 12px;
  439. // border: 1px solid rgb(188, 188, 188);
  440. border: 1px solid rgba(188, 188, 188, 0.5);
  441. }
  442. .file-input-box {
  443. position: relative;
  444. border: 1px solid rgb(188, 188, 188);
  445. cursor: pointer;
  446. .icon {
  447. position: absolute;
  448. top: 45%;
  449. left: 50%;
  450. transform: translate(-50%, -50%);
  451. color: var(--color-999);
  452. }
  453. }
  454. // 验证是否为空
  455. .verifyAct {
  456. color: var(--v-error-base);
  457. border: 1px solid var(--v-error-base);
  458. .icon { color: var(--v-error-base); }
  459. }
  460. }
  461. .PrepareBox {
  462. margin-top: 74px;
  463. margin-left: 32px;
  464. }
  465. .mobileBox {
  466. width: calc(100vw - 16px);
  467. margin: 0 auto;
  468. .resume-header {
  469. margin-bottom: 12px;
  470. }
  471. }
  472. .contactItemCard {
  473. background-color: var(--default-bgc);
  474. border-radius: 8px;
  475. padding: 16px 24px;
  476. margin-bottom: 20px;
  477. }
  478. </style>