register.vue 18 KB

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