boxed-signup.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. <template>
  2. <div>
  3. <div class="absolute inset-0">
  4. <img src="/assets/images/auth/bg-gradient.png" alt="image" class="h-full w-full object-cover" />
  5. </div>
  6. <div
  7. class="relative flex min-h-screen items-center justify-center bg-[url(/assets/images/auth/map.png)] bg-cover bg-center bg-no-repeat px-6 py-10 dark:bg-[#060818] sm:px-16"
  8. >
  9. <img src="/assets/images/auth/coming-soon-object1.png" alt="image" class="absolute left-0 top-1/2 h-full max-h-[893px] -translate-y-1/2" />
  10. <img src="/assets/images/auth/coming-soon-object2.png" alt="image" class="absolute left-24 top-0 h-40 md:left-[30%]" />
  11. <img src="/assets/images/auth/coming-soon-object3.png" alt="image" class="absolute right-0 top-0 h-[300px]" />
  12. <img src="/assets/images/auth/polygon-object.svg" alt="image" class="absolute bottom-0 end-[28%]" />
  13. <div
  14. class="relative w-full rounded-md bg-[linear-gradient(45deg,#fff9f9_0%,rgba(255,255,255,0)_25%,rgba(255,255,255,0)_75%,_#fff9f9_100%)] p-2 dark:bg-[linear-gradient(52.22deg,#0E1726_0%,rgba(14,23,38,0)_18.66%,rgba(14,23,38,0)_51.04%,rgba(14,23,38,0)_80.07%,#0E1726_100%)]"
  15. >
  16. <!-- max-w-[870px] -->
  17. <div class="relative flex flex-col justify-center rounded-md bg-white/60 backdrop-blur-lg dark:bg-black/50 px-6 lg:min-h-[758px] py-20">
  18. <!-- <div class="absolute top-6 end-6">
  19. <div class="dropdown">
  20. <Popper :placement="store.state.rtlClass === 'rtl' ? 'bottom-start' : 'bottom-end'" offsetDistance="8">
  21. <button
  22. type="button"
  23. class="flex items-center gap-2.5 rounded-lg border border-white-dark/30 bg-white px-2 py-1.5 text-white-dark hover:border-primary hover:text-primary dark:bg-black"
  24. >
  25. <div>
  26. <img :src="currentFlag" alt="image" class="h-5 w-5 rounded-full object-cover" />
  27. </div>
  28. <div class="text-base font-bold uppercase">{{ store.state.locale }}</div>
  29. <span class="shrink-0">
  30. <icon-caret-down />
  31. </span>
  32. </button>
  33. <template #content="{ close }">
  34. <ul class="!px-2 text-dark dark:text-white-dark grid grid-cols-2 gap-2 font-semibold dark:text-white-light/90 w-[280px]">
  35. <template v-for="item in store.state.languageList" :key="item.code">
  36. <li>
  37. <button
  38. type="button"
  39. class="w-full hover:text-primary"
  40. :class="{ 'bg-primary/10 text-primary': i18n.locale === item.code }"
  41. @click="changeLanguage(item), close()"
  42. >
  43. <img
  44. class="w-5 h-5 object-cover rounded-full"
  45. :src="`/assets/images/flags/${item.code.toUpperCase()}.svg`"
  46. alt=""
  47. />
  48. <span class="ltr:ml-3 rtl:mr-3">{{ item.name }}</span>
  49. </button>
  50. </li>
  51. </template>
  52. </ul>
  53. </template>
  54. </Popper>
  55. </div>
  56. </div> -->
  57. <!-- mx-automax-w-[440px] -->
  58. <div class="w-full">
  59. <div class="mb-10">
  60. <h1 class="text-3xl font-extrabold uppercase !leading-snug text-primary md:text-4xl">Account registration</h1>
  61. <p class="text-base font-bold leading-normal text-white-dark">Enter your email and password to register</p>
  62. </div>
  63. <el-form :model="companyForm" :rules="stockRules" ref="companyFormRef" label-width="140px">
  64. <el-row>
  65. <el-col :span="8">
  66. <el-form-item label="营业执照:" prop="license">
  67. <el-upload
  68. class="avatar-uploader"
  69. :action="uploadUrl"
  70. :show-file-list="false"
  71. :on-success="handleAvatarSuccess"
  72. :before-upload="beforeAvatarUpload"
  73. >
  74. <img v-if="companyForm.license" :src="companyForm.license" class="avatar w-28 h-28" />
  75. <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
  76. </el-upload>
  77. </el-form-item>
  78. </el-col>
  79. <el-col :span="8">
  80. <el-form-item label="身份证正面:" prop="id_card_front">
  81. <el-upload
  82. class="avatar-uploader"
  83. :action="uploadUrl"
  84. :show-file-list="false"
  85. :on-success="handleFrontSuccess"
  86. :before-upload="beforeFrontUpload"
  87. >
  88. <img v-if="companyForm.id_card_front" :src="companyForm.id_card_front" class="avatar w-28 h-28" />
  89. <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
  90. </el-upload>
  91. <!-- <p>姓名:{{ companyForm.value.id_card_front.name }}</p>
  92. <p>身份证号码:{{ companyForm.value.id_card_front.id_card_number }}</p> -->
  93. </el-form-item>
  94. </el-col>
  95. <el-col :span="8">
  96. <el-form-item label="身份证反面:" prop="id_card_opposite">
  97. <el-upload
  98. class="avatar-uploader"
  99. :action="uploadUrl"
  100. :show-file-list="false"
  101. :on-success="handleOppositeSuccess"
  102. :before-upload="beforeOppositeUpload"
  103. >
  104. <img v-if="companyForm.id_card_opposite" :src="companyForm.id_card_opposite" class="avatar w-28 h-28" />
  105. <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
  106. </el-upload>
  107. </el-form-item>
  108. </el-col>
  109. <el-col :span="10" v-if="!companyForm.id">
  110. <el-form-item label="套餐:" prop="company_package_id">
  111. <el-select v-model="companyForm.company_package_id" clearable placeholder="请选择套餐">
  112. <el-option v-for="item in packageList" :key="item.id" :label="item.name" :value="item.id" />
  113. </el-select>
  114. </el-form-item>
  115. </el-col>
  116. <el-col :span="10">
  117. <el-form-item label="公司名称:" prop="name">
  118. <el-input v-model="companyForm.name" clearable disabled placeholder="请输入公司名称"></el-input>
  119. </el-form-item>
  120. </el-col>
  121. <el-col :span="10">
  122. <el-form-item label="统一社会信用代码:" prop="credit_code">
  123. <el-input v-model="companyForm.credit_code" clearable disabled placeholder="请输入统一社会信用代码"></el-input>
  124. </el-form-item>
  125. </el-col>
  126. <el-col :span="10">
  127. <el-form-item label="法人:" prop="user_name">
  128. <el-input v-model="companyForm.user_name" disabled clearable placeholder="请输入联系人"></el-input>
  129. </el-form-item>
  130. </el-col>
  131. <el-col :span="10">
  132. <el-form-item label="法人电话:" prop="phone">
  133. <el-input v-model="companyForm.phone" clearable placeholder="请输入联系人电话"></el-input>
  134. </el-form-item>
  135. </el-col>
  136. <el-col :span="10">
  137. <el-form-item label="验证码:" prop="check_code">
  138. <el-input v-model="companyForm.check_code" clearable placeholder="请输入验证码">
  139. <template #append>
  140. <el-button v-if="showloginCode" type="primary" @click="getCode" class="w-40">获取验证码</el-button>
  141. <div v-else>{{ companyForm.count }}</div>
  142. </template>
  143. </el-input>
  144. </el-form-item>
  145. </el-col>
  146. <el-col :span="10">
  147. <el-form-item label="行业:" prop="industry">
  148. <el-input v-model="companyForm.industry" clearable placeholder="请输入行业"></el-input>
  149. </el-form-item>
  150. </el-col>
  151. <el-col :span="10">
  152. <el-form-item label="邮箱:" prop="email">
  153. <el-input v-model="companyForm.email" clearable placeholder="请输入邮箱"></el-input>
  154. </el-form-item>
  155. </el-col>
  156. <el-col :span="10">
  157. <el-form-item label="邮编:" prop="postal_code">
  158. <el-input v-model="companyForm.postal_code" clearable placeholder="请输入邮箱"></el-input>
  159. </el-form-item>
  160. </el-col>
  161. <el-col :span="10">
  162. <el-form-item label="官网:" prop="website">
  163. <el-input v-model="companyForm.website" clearable placeholder="请输入官网地址"></el-input>
  164. </el-form-item>
  165. </el-col>
  166. <el-col :span="10">
  167. <el-form-item label="地址:" prop="">
  168. <el-cascader class="w-full" size="default" :options="options" clearable v-model="selectedOptions"> </el-cascader>
  169. <span>
  170. <el-input class="!w-full mt-3" v-model="companyForm.address" placeholder="请输入"></el-input>
  171. </span>
  172. </el-form-item>
  173. </el-col>
  174. </el-row>
  175. </el-form>
  176. <div class="flex justify-center">
  177. <button
  178. type="submit"
  179. @click="submitWarehouseForm"
  180. class="btn btn-gradient !mt-6 w-1/5 border-0 uppercase shadow-[0_10px_20px_-10px_rgba(67,97,238,0.44)]"
  181. >
  182. Sign Up
  183. </button>
  184. </div>
  185. <!-- <form class="space-y-5 dark:text-white" @submit.prevent="router.push('/')">
  186. <div>
  187. <label for="Name">Name</label>
  188. <div class="relative text-white-dark">
  189. <input id="Name" type="text" placeholder="Enter Name" class="form-input ps-10 placeholder:text-white-dark" />
  190. <span class="absolute start-4 top-1/2 -translate-y-1/2">
  191. <icon-user :fill="true" />
  192. </span>
  193. </div>
  194. </div>
  195. <div>
  196. <label for="Email">Email</label>
  197. <div class="relative text-white-dark">
  198. <input id="Email" type="email" placeholder="Enter Email" class="form-input ps-10 placeholder:text-white-dark" />
  199. <span class="absolute start-4 top-1/2 -translate-y-1/2">
  200. <icon-mail :fill="true" />
  201. </span>
  202. </div>
  203. </div>
  204. <div>
  205. <label for="Password">Password</label>
  206. <div class="relative text-white-dark">
  207. <input id="Password" type="password" placeholder="Enter Password" class="form-input ps-10 placeholder:text-white-dark" />
  208. <span class="absolute start-4 top-1/2 -translate-y-1/2">
  209. <icon-lock-dots :fill="true" />
  210. </span>
  211. </div>
  212. </div>
  213. <div>
  214. <label class="flex cursor-pointer items-center">
  215. <input type="checkbox" class="form-checkbox bg-white dark:bg-black" />
  216. <span class="text-white-dark">Subscribe to weekly newsletter</span>
  217. </label>
  218. </div>
  219. <button type="submit" class="btn btn-gradient !mt-6 w-full border-0 uppercase shadow-[0_10px_20px_-10px_rgba(67,97,238,0.44)]">
  220. Sign Up
  221. </button>
  222. </form>
  223. <div class="relative my-7 text-center md:mb-9">
  224. <span class="absolute inset-x-0 top-1/2 h-px w-full -translate-y-1/2 bg-white-light dark:bg-white-dark"></span>
  225. <span class="relative bg-white px-2 font-bold uppercase text-white-dark dark:bg-dark dark:text-white-light">or</span>
  226. </div>
  227. <div class="mb-10 md:mb-[60px]">
  228. <ul class="flex justify-center gap-3.5 text-white">
  229. <li>
  230. <a
  231. href="javascript:"
  232. class="inline-flex h-8 w-8 items-center justify-center rounded-full p-0 transition hover:scale-110"
  233. style="background: linear-gradient(135deg, rgba(239, 18, 98, 1) 0%, rgba(67, 97, 238, 1) 100%)"
  234. >
  235. <icon-instagram />
  236. </a>
  237. </li>
  238. <li>
  239. <a
  240. href="javascript:"
  241. class="inline-flex h-8 w-8 items-center justify-center rounded-full p-0 transition hover:scale-110"
  242. style="background: linear-gradient(135deg, rgba(239, 18, 98, 1) 0%, rgba(67, 97, 238, 1) 100%)"
  243. >
  244. <icon-facebook-circle />
  245. </a>
  246. </li>
  247. <li>
  248. <a
  249. href="javascript:"
  250. class="inline-flex h-8 w-8 items-center justify-center rounded-full p-0 transition hover:scale-110"
  251. style="background: linear-gradient(135deg, rgba(239, 18, 98, 1) 0%, rgba(67, 97, 238, 1) 100%)"
  252. >
  253. <icon-twitter :fill="true" />
  254. </a>
  255. </li>
  256. <li>
  257. <a
  258. href="javascript:"
  259. class="inline-flex h-8 w-8 items-center justify-center rounded-full p-0 transition hover:scale-110"
  260. style="background: linear-gradient(135deg, rgba(239, 18, 98, 1) 0%, rgba(67, 97, 238, 1) 100%)"
  261. >
  262. <icon-google />
  263. </a>
  264. </li>
  265. </ul>
  266. </div>
  267. <div class="text-center dark:text-white">
  268. Already have an account ?
  269. <router-link to="/auth/boxed-signin" class="uppercase text-primary underline transition hover:text-black dark:hover:text-white">
  270. SIGN IN
  271. </router-link>
  272. </div>-->
  273. </div>
  274. </div>
  275. </div>
  276. </div>
  277. </div>
  278. </template>
  279. <script lang="ts" setup>
  280. import { computed, reactive, ref, onMounted, Ref } from 'vue';
  281. import { useI18n } from 'vue-i18n';
  282. import appSetting from '@/app-setting';
  283. import { useRouter } from 'vue-router';
  284. import { useMeta } from '@/composables/use-meta';
  285. import IconInstagram from '@/components/icon/icon-instagram.vue';
  286. import IconFacebookCircle from '@/components/icon/icon-facebook-circle.vue';
  287. import IconGoogle from '@/components/icon/icon-google.vue';
  288. import { Plus } from '@element-plus/icons-vue';
  289. import * as API_Company from '@/api/company';
  290. import * as API_Package from '@/api/package';
  291. import type { UploadProps } from 'element-plus';
  292. import { ElMessageBox, FormInstance, ElMessage } from 'element-plus';
  293. import { provinceAndCityData, pcTextArr, regionData, pcaTextArr, codeToText } from 'element-china-area-data';
  294. import { useStore } from 'vuex';
  295. useMeta({ title: 'Register Boxed' });
  296. const router = useRouter();
  297. const store = useStore();
  298. const options = ref<any[]>(regionData);
  299. const selectedOptions = ref<any[]>([]);
  300. const companyForm = ref<any>({
  301. role_id: '',
  302. name: '',
  303. money: '',
  304. day: '',
  305. phone: '',
  306. use_able: false,
  307. check_code: '',
  308. check_code_key: '',
  309. remark: '',
  310. address: '',
  311. license: '',
  312. company_package_id: '',
  313. license_msg: {},
  314. id_card_front: '',
  315. id_card_msg_front: {},
  316. id_card_opposite: '',
  317. id_card_msg_opposite: {},
  318. id_card_num: '',
  319. id_card_time: '',
  320. });
  321. const stockRules = ref<any>({
  322. role_id: [{ required: true, message: '请选择权限!', trigger: 'change' }],
  323. name: [{ required: true, message: '请输入公司名称!', trigger: 'blur' }],
  324. credit_code: [{ required: true, message: '请输入统一社会信用代码!', trigger: 'blur' }],
  325. industry: [{ required: true, message: '请输入行业!', trigger: 'blur' }],
  326. user_name: [{ required: true, message: '请输入联系人姓名!', trigger: 'blur' }],
  327. phone: [
  328. { required: true, message: '请输入联系人电话!', trigger: 'blur' },
  329. { pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号', trigger: 'blur' },
  330. ],
  331. email: [
  332. { required: true, message: '请输入邮箱!', trigger: 'blur' },
  333. { type: 'email', message: '请输入有效的邮箱地址', trigger: ['blur', 'change'] },
  334. ],
  335. postal_code: [
  336. { required: true, message: '请输入邮编!', trigger: 'blur' },
  337. { pattern: /^[1-9]\d{5}$/, message: '请输入有效的邮政编码', trigger: 'blur' },
  338. ],
  339. });
  340. /* 营业执照 */
  341. const handleAvatarSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {
  342. API_Company.checkLicense({ image_url: response.url }).then((res) => {
  343. companyForm.value.name = res.enterprise_name;
  344. companyForm.value.credit_code = res.credit_code;
  345. companyForm.value.license_msg = res;
  346. companyForm.value.license = response.url;
  347. });
  348. };
  349. const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
  350. if (rawFile.size / 1024 / 1024 > 10) {
  351. ElMessage.error('Avatar picture size can not exceed 10MB!');
  352. return false;
  353. }
  354. return true;
  355. };
  356. /* 身份证正面 */
  357. const handleFrontSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {
  358. API_Company.checkIdCardOpposite({ image_url: response.url }, 'front').then((res) => {
  359. if(res.name!==companyForm.value.name){
  360. ElMessage.error('请上传法人身份证!');
  361. return
  362. }
  363. companyForm.value.user_name = res.name;
  364. companyForm.value.id_card_num = res.id_card_number;
  365. companyForm.value.id_card_msg_front = res;
  366. companyForm.value.id_card_front = response.url;
  367. });
  368. };
  369. const beforeFrontUpload: UploadProps['beforeUpload'] = (rawFile) => {
  370. if (rawFile.size / 1024 / 1024 > 10) {
  371. ElMessage.error('Avatar picture size can not exceed 10MB!');
  372. return false;
  373. }
  374. return true;
  375. };
  376. /* 身份证反面 */
  377. const handleOppositeSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {
  378. API_Company.checkIdCardOpposite({ image_url: response.url }, 'opposite').then((res) => {
  379. companyForm.value.id_card_num = res.valid_date;
  380. companyForm.value.id_card_msg_opposite = res;
  381. companyForm.value.id_card_opposite = response.url;
  382. });
  383. };
  384. const beforeOppositeUpload: UploadProps['beforeUpload'] = (rawFile) => {
  385. if (rawFile.size / 1024 / 1024 > 10) {
  386. ElMessage.error('Avatar picture size can not exceed 10MB!');
  387. return false;
  388. }
  389. return true;
  390. };
  391. const uploadUrl = ref(import.meta.env.VITE_API_URL + '/uploaders');
  392. /* 获取套餐列表 */
  393. const packageList = ref<any>([]);
  394. const GET_PackageList = () => {
  395. API_Package.packageList({ page_no: 1, page_size: 1000, use_able: true }).then((res) => {
  396. packageList.value = res.records;
  397. });
  398. };
  399. // multi language
  400. const i18n = reactive(useI18n());
  401. const changeLanguage = (item: any) => {
  402. i18n.locale = item.code;
  403. appSetting.toggleLanguage(item);
  404. };
  405. const currentFlag = computed(() => {
  406. return `/assets/images/flags/${i18n.locale.toUpperCase()}.svg`;
  407. });
  408. const companyFormRef = ref();
  409. const submitWarehouseForm = () => {
  410. companyFormRef.value.validate((valid: boolean) => {
  411. if (valid) {
  412. const { id } = companyForm.value;
  413. const params = { ...companyForm.value };
  414. params.province = selectedOptions.value.join(',');
  415. API_Company.addCompany(params).then((res) => {
  416. ElMessageBox.confirm(`此为登录密码,请详细记录:${res}`, '提示', {
  417. confirmButtonText: '确定',
  418. cancelButtonText: '取消',
  419. type: 'warning',
  420. })
  421. .then(() => {
  422. router.push({ path: '/login' }).catch(() => {});
  423. })
  424. .catch(() => {});
  425. /* ElMessage.success('添加成功!'); */
  426. });
  427. } else {
  428. ElMessage.error('表单填写有误,请核对!');
  429. /* store.dispatch('showMessage', { message: '表单填写有误,请核对!', type: 'error' }); */
  430. return false;
  431. }
  432. });
  433. };
  434. const showloginCode = ref(true);
  435. let timer: Ref<number | null> = ref(null);
  436. let count = ref<number>(0);
  437. /* 获取手机号验证码 */
  438. const getCode = () => {
  439. if (companyForm.value.phone === '') {
  440. ElMessage.error('请先输入手机号!');
  441. return;
  442. }
  443. API_Company.loginGetCheckCode(companyForm.value.phone).then((res) => {
  444. ElMessage.success('验证码已发送,五分钟后失效!');
  445. companyForm.value.check_code_key = res;
  446. /* const TIME_COUNT = 60; */
  447. /* if (!timer.value) {
  448. showloginCode.value = false;
  449. count.value = TIME_COUNT;
  450. timer.value = window.setInterval(() => {
  451. if (count.value > 0 && count.value <= TIME_COUNT) {
  452. count.value -= 1;
  453. } else {
  454. showloginCode.value = true;
  455. clearInterval(timer.value as number);
  456. timer.value = null;
  457. }
  458. }, 1000);
  459. } */
  460. });
  461. };
  462. onMounted(() => {
  463. GET_PackageList()
  464. })
  465. </script>
  466. <style>
  467. .avatar-uploader .el-upload {
  468. border: 1px dashed var(--el-border-color);
  469. border-radius: 6px;
  470. cursor: pointer;
  471. position: relative;
  472. overflow: hidden;
  473. transition: var(--el-transition-duration-fast);
  474. }
  475. .avatar-uploader .el-upload:hover {
  476. border-color: var(--el-color-primary);
  477. }
  478. .el-icon.avatar-uploader-icon {
  479. font-size: 28px;
  480. color: #8c939d;
  481. width: 112px;
  482. height: 112px;
  483. text-align: center;
  484. }
  485. </style>