certification.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. <template>
  2. <view>
  3. <uni-nav-bar :fixed="true" shadow left-icon="left" title="技师认证" :statusBar="true" @clickLeft="handleToBack" />
  4. <uni-section title="身份证" type="line">
  5. <view class="idcard_wrap">
  6. <image
  7. :src="idcardData[0] ? idcardData[0] : idcard[0]"
  8. @tap="handleOpenIdcardImage"
  9. :data-id="0"
  10. mode="aspectFill"
  11. style="width: 340upx !important; height: 200upx !important; border-radius: 12px; margin-right: 12px"
  12. ></image>
  13. <image
  14. :src="idcardData[1] ? idcardData[1] : idcard[1]"
  15. @tap="handleOpenIdcardImage"
  16. :data-id="1"
  17. mode="aspectFill"
  18. style="width: 340upx !important; height: 200upx !important; border-radius: 12px"
  19. ></image>
  20. </view>
  21. </uni-section>
  22. <uni-section title="手持身份证照片" type="line">
  23. <view class="idcard_wrap">
  24. <image
  25. :src="idcardData[2] ? idcardData[2] : idcard[0]"
  26. @tap="handleOpenIdcardImage"
  27. :data-id="2"
  28. mode="aspectFill"
  29. style="width: 340upx !important; height: 200upx !important; border-radius: 12px"
  30. ></image>
  31. </view>
  32. </uni-section>
  33. <uni-section title="工作照" type="line">
  34. <view>
  35. <view class="picture_list_wrap">
  36. <view class="picture_list" v-for="(i, index) in workPicture" :key="index">
  37. <view>
  38. <image src="/static/account/certification/clear.png" class="picture_close" @tap="handleClearPicture" :data-index="index"></image>
  39. <image :src="i" class="picture" mode="aspectFill"></image>
  40. </view>
  41. <!-- <button size="mini" style="color: #ff6868">裁剪</button> -->
  42. </view>
  43. <image src="/static/account/certification/add.png" class="picture" @tap="handleOpenImage" v-if="workPicture.length < 6"></image>
  44. </view>
  45. </view>
  46. </uni-section>
  47. <uni-section title="生活照" type="line">
  48. <view class="picture_list_wrap">
  49. <view class="picture_list" v-for="(i, index) in lifePicture" :key="index">
  50. <view>
  51. <image src="/static/account/certification/clear.png" class="picture_close" @tap="handleClearLifePicture" :data-index="index"></image>
  52. <image :src="i" class="picture" mode="aspectFill"></image>
  53. </view>
  54. <!-- <button size="mini" style="color: #fff; background: #ff6868" @tap="handleCropping(index, 'life')">裁剪</button> -->
  55. </view>
  56. <image src="/static/account/certification/add.png" class="picture" @tap="handleOpenLifeImage" v-if="lifePicture.length < 6"></image>
  57. </view>
  58. </uni-section>
  59. <uni-section title="证书" type="line">
  60. <view class="picture_list_wrap">
  61. <view class="picture_list" v-for="(i, index) in certificatesPicture" :key="index">
  62. <image src="/static/account/certification/clear.png" class="picture_close" @tap="handleClearCertificatesPicture" :data-id="index"></image>
  63. <image :src="i" class="picture" mode="aspectFill"></image>
  64. </view>
  65. <image src="/static/account/certification/add.png" class="picture" @tap="handleOpenCertificatesImage" v-if="certificatesPicture.length < 6"></image>
  66. </view>
  67. </uni-section>
  68. <uni-popup ref="picturePopup" background-color="#fff" @change="change">
  69. <view class="picture_popup_container">
  70. <!-- <bt-cropper ref="cropper" :imageSrc="imageSrc" :ratio="1"></bt-cropper> -->
  71. <!-- <uni-cropper
  72. :src="imageSrc"
  73. @complete="handleCrop"
  74. :disable-scale="true"
  75. :disable-rotate="true"
  76. ></uni-cropper> -->
  77. <okingtz-cropper
  78. @uploadSuccess="uploadSuccess"
  79. :image="imageSrc"
  80. :cropWidth="750"
  81. :cropHeight="750"
  82. :maxCropper="true"
  83. :canScale="false"
  84. selectButtonText=""
  85. saveButtonText="裁剪"
  86. ></okingtz-cropper>
  87. </view>
  88. </uni-popup>
  89. <uni-popup ref="lifePicturePopup" background-color="#fff" @change="change">
  90. <view class="picture_popup_container">
  91. <okingtz-cropper
  92. @uploadSuccess="uploadLifeSuccess"
  93. :image="lifeImageSrc"
  94. :cropWidth="750"
  95. :cropHeight="750"
  96. :maxCropper="true"
  97. :canScale="false"
  98. selectButtonText=""
  99. saveButtonText="裁剪"
  100. ></okingtz-cropper>
  101. </view>
  102. </uni-popup>
  103. <view class="agreement_wrap" @tap="handleIsAgreement">
  104. <image src="/static/common/radio_selected.png" v-if="isAgreement"></image>
  105. <image src="/static/common/radio_unselect.png" v-else></image>
  106. 已阅读并同意
  107. <text style="color: #14a5ec" @tap.stop="handleToAgreement">《入驻协议》</text>
  108. </view>
  109. <view class="notice">每次修改信息后需要24小时审核</view>
  110. <view class="footer">
  111. <image mode="widthFix" src="/static/common/footer.png" />
  112. </view>
  113. <view class="footer_btn_wrap">
  114. <view class="footer_btn" @tap="handleSave">保 存</view>
  115. </view>
  116. </view>
  117. </template>
  118. <style>
  119. .container {
  120. /** 外层一定要指定大小 */
  121. }
  122. </style>
  123. <script setup>
  124. import { ref, reactive } from 'vue';
  125. import { onLoad, onShow } from '@dcloudio/uni-app';
  126. import request from '/common/request.js';
  127. import auth from '/common/auth.js';
  128. import { apiBaseUrl } from '/common/config.js';
  129. import OkingtzCropper from '@/uni_modules/okingtz-cropper/components/okingtz-cropper/okingtz-cropper';
  130. const statusBarHeight = ref();
  131. const idcard = reactive(['/static/account/certification/idcard_before.png', '/static/account/certification/idcard_after.png']);
  132. const idcardData = ref(['', '', '']);
  133. const isAgreement = ref(true);
  134. const userData = ref({});
  135. const picturePopup = ref();
  136. const imageSrc = ref();
  137. const cropper = ref();
  138. const workPicture = ref([]);
  139. const workPictureOriginal = ref([]);
  140. const workOpenIndex = ref(0);
  141. const currentPictureBlob = ref();
  142. const lifeImageSrc = ref();
  143. const lifePicturePopup = ref();
  144. const lifePicture = ref([]);
  145. const lifePictureOriginal = ref([]);
  146. const lifeOpenIndex = ref(0);
  147. const certificatesPicture = ref([]);
  148. const handleToBack = () => {
  149. const canNavBack = getCurrentPages();
  150. if (canNavBack && canNavBack.length > 1) uni.navigateBack();
  151. else history.back();
  152. };
  153. const handleCropping = (index, type) => {
  154. if (type == 'life') {
  155. handleLifePictureToggle(index);
  156. }
  157. };
  158. const getBase64Image = (src) => {
  159. return new Promise((resolve) => {
  160. let xhr = new XMLHttpRequest();
  161. xhr.open('get', src, true);
  162. xhr.responseType = 'blob';
  163. xhr.onload = function () {
  164. if (this.status == 200) {
  165. let blob = this.response;
  166. let oFileReader = new FileReader();
  167. oFileReader.onloadend = function (e) {
  168. const base64 = e.target.result;
  169. resolve(base64);
  170. };
  171. oFileReader.readAsDataURL(blob);
  172. }
  173. };
  174. xhr.send();
  175. });
  176. };
  177. const handleOpenIdcardImage = (e) => {
  178. const id = e.currentTarget.dataset.id;
  179. uni.chooseImage({
  180. count: 1,
  181. success: (chooseImageRes) => {
  182. console.log('选择照片', chooseImageRes);
  183. const tempFilePaths = chooseImageRes.tempFilePaths;
  184. uni.showLoading({
  185. content: '加载中',
  186. mask: true
  187. });
  188. uni.uploadFile({
  189. url: `${apiBaseUrl}/api/UpImg/upimg`,
  190. filePath: tempFilePaths[0],
  191. header: {
  192. 'XX-Token': uni.getStorageSync('token'),
  193. 'XX-Device-Type': 'web'
  194. },
  195. name: 'file',
  196. formData: {},
  197. success: (uploadFileRes) => {
  198. console.log('结果', uploadFileRes);
  199. console.log('结果', JSON.parse(uploadFileRes.data));
  200. idcardData.value[id] = JSON.parse(uploadFileRes.data).data.url;
  201. },
  202. complete: () => {
  203. setTimeout(function () {
  204. uni.hideLoading();
  205. }, 100);
  206. }
  207. });
  208. }
  209. });
  210. };
  211. const handleToAgreement = () => {
  212. uni.navigateTo({ url: '/pages/account/help?id=11' });
  213. };
  214. const handleIsAgreement = () => {
  215. isAgreement.value = !isAgreement.value;
  216. };
  217. const handleSave = () => {
  218. if (!isAgreement) {
  219. uni.showToast({
  220. title: '请阅读并同意《入驻协议》',
  221. duration: 2000,
  222. icon: 'none'
  223. });
  224. return;
  225. }
  226. request({
  227. url: '/api/user/applyJsPost',
  228. method: 'POST',
  229. data: {
  230. ...userData.value,
  231. identity: idcardData.value,
  232. pictures: lifePicture.value,
  233. work: workPicture.value,
  234. life_original: lifePictureOriginal.value,
  235. work_original: workPictureOriginal.value,
  236. certificates: certificatesPicture.value
  237. },
  238. success: (res) => {
  239. console.log(res);
  240. if (res.data.code == 1) {
  241. uni.showToast({
  242. title: '提交成功',
  243. duration: 2000,
  244. icon: 'none'
  245. });
  246. setTimeout(function () {
  247. uni.navigateBack();
  248. }, 1000);
  249. } else {
  250. uni.showToast({
  251. title: res.data.msg,
  252. duration: 2000,
  253. icon: 'none'
  254. });
  255. }
  256. },
  257. fail: () => {},
  258. complete: () => {
  259. setTimeout(function () {
  260. uni.hideLoading();
  261. }, 2000);
  262. }
  263. });
  264. };
  265. const handleClearPicture = (e) => {
  266. uni.showModal({
  267. title: '提示',
  268. content: '确定删除该图片吗?',
  269. success(res) {
  270. // 用户确定要删除
  271. if (res.confirm) {
  272. const index = e.currentTarget.dataset.index;
  273. workPicture.value.splice(index,1);
  274. workPictureOriginal.value.splice(index,1);
  275. //that.arr2.splice(e.currentTarget.dataset.id,1)
  276. }
  277. }
  278. });
  279. };
  280. const handleClearLifePicture = (e) => {
  281. uni.showModal({
  282. title: '提示',
  283. content: '确定删除该图片吗?',
  284. success(res) {
  285. // 用户确定要删除
  286. if (res.confirm) {
  287. const index = e.currentTarget.dataset.index;
  288. lifePicture.value.splice(index, 1);
  289. lifePictureOriginal.value.splice(index, 1);
  290. //that.arr2.splice(e.currentTarget.dataset.id,1)
  291. }
  292. }
  293. });
  294. };
  295. const handleClearCertificatesPicture = (e) => {
  296. uni.showModal({
  297. title: '提示',
  298. content: '确定删除该图片吗?',
  299. success(res) {
  300. // 用户确定要删除
  301. if (res.confirm) {
  302. const id = e.currentTarget.dataset.id;
  303. certificatesPicture.value.splice(id, 1);
  304. }
  305. }
  306. });
  307. };
  308. const uploadSuccess = (tempFilePath) => {
  309. uni.showToast({
  310. title: '保存成功'
  311. });
  312. // 4.根据自己的业务场景处理tempFilePath ;接口保存,或者上传至云空间
  313. console.log('tempFilePath_->', tempFilePath);
  314. const file = { tempFilePaths: [tempFilePath] };
  315. // const blob = base64ToBlob(tempFilePath);
  316. updateImage(file, false);
  317. // console.log("blob", blob);
  318. // workPicture.value[workOpenIndex] = blob;
  319. // handleWorkPictureToggleClose();
  320. };
  321. const uploadLifeSuccess = (tempFilePath) => {
  322. uni.showToast({
  323. title: '保存成功'
  324. });
  325. console.log('tempFilePath_->', tempFilePath);
  326. const file = { tempFilePaths: [tempFilePath] };
  327. updateLifeImage(file, false);
  328. // handleLifePictureToggleClose();
  329. };
  330. const base64ToBlob = (base64) => {
  331. let arr = base64.split(',');
  332. let mime = arr[0].match(/:(.*?);/)[1];
  333. let bstr = atob(arr[1]);
  334. let n = bstr.length;
  335. let u8arr = new Uint8Array(n);
  336. while (n--) {
  337. u8arr[n] = bstr.charCodeAt(n);
  338. }
  339. return new Blob([u8arr], {
  340. type: mime
  341. });
  342. };
  343. const change = (e) => {
  344. console.log('弹窗', e);
  345. };
  346. const handleWorkPictureToggleClose = () => {
  347. picturePopup.value.close();
  348. };
  349. const handleLifePictureToggleClose = () => {
  350. lifePicturePopup.value.close();
  351. };
  352. const handleWorkPictureToggle = (index) => {
  353. imageSrc.value = currentPictureBlob.value;
  354. workOpenIndex.value = index;
  355. picturePopup.value.open('bottom');
  356. };
  357. const handleLifePictureToggle = async (index) => {
  358. lifeImageSrc.value = currentPictureBlob.value;
  359. lifeOpenIndex.value = index;
  360. lifePicturePopup.value.open('bottom');
  361. };
  362. const handleOpenImage = (original = true) => {
  363. uni.chooseImage({
  364. count: 1,
  365. success: (res) => {
  366. console.log('选择照片', res);
  367. updateImage(res, original);
  368. }
  369. });
  370. };
  371. const handleOpenLifeImage = (original = true) => {
  372. uni.chooseImage({
  373. count: 1,
  374. success: (res) => {
  375. console.log('选择照片', res);
  376. updateLifeImage(res, original);
  377. }
  378. });
  379. };
  380. const handleOpenCertificatesImage = () => {
  381. uni.chooseImage({
  382. count: 1,
  383. success: (res) => {
  384. for (var i = 0; i < res.tempFilePaths.length; i++) {
  385. uni.showLoading({
  386. content: '加载中',
  387. mask: true
  388. });
  389. uni.uploadFile({
  390. url: `${apiBaseUrl}/api/UpImg/upimg`,
  391. filePath: res.tempFilePaths[i],
  392. header: {
  393. 'XX-Token': uni.getStorageSync('token'),
  394. 'XX-Device-Type': 'web'
  395. },
  396. name: 'file',
  397. formData: {},
  398. success: (uploadFileRes) => {
  399. console.log('照片结果', uploadFileRes);
  400. console.log('照片结果2', JSON.parse(uploadFileRes.data));
  401. const pictrue = JSON.parse(uploadFileRes.data);
  402. certificatesPicture.value.push(pictrue.data.url);
  403. // certificatesPicture.value[certificatesPicture.value.length - 1] = pictrue.data.url;
  404. // certificatesPicture.value.push(pictrue.data.url);
  405. },
  406. complete: () => {
  407. setTimeout(function () {
  408. uni.hideLoading();
  409. }, 100);
  410. }
  411. });
  412. }
  413. }
  414. });
  415. };
  416. const updateImage = (res, original) => {
  417. for (var i = 0; i < res.tempFilePaths.length; i++) {
  418. uni.showLoading({
  419. content: '加载中',
  420. mask: true
  421. });
  422. console.log('提交上传', res.tempFilePaths);
  423. // if (original) if (original) currentPictureBlob.value = res.tempFilePaths[i];
  424. uni.uploadFile({
  425. url: `${apiBaseUrl}/api/UpImg/upimg`,
  426. filePath: res.tempFilePaths[i],
  427. header: {
  428. 'XX-Token': uni.getStorageSync('token'),
  429. 'XX-Device-Type': 'web'
  430. },
  431. name: 'file',
  432. formData: {},
  433. success: (uploadFileRes) => {
  434. console.log('照片结果', uploadFileRes);
  435. console.log('照片结果2', JSON.parse(uploadFileRes.data));
  436. const pictrue = JSON.parse(uploadFileRes.data);
  437. // if (original) {
  438. workPictureOriginal.value.push(pictrue.data.url);
  439. workPicture.value.push(pictrue.data.url);
  440. // handleWorkPictureToggle(workPicture.value.length - 1);
  441. // } else {
  442. // workPicture.value[workOpenIndex.value] = pictrue.data.url;
  443. // handleWorkPictureToggleClose();
  444. // }
  445. },
  446. complete: () => {
  447. setTimeout(function () {
  448. uni.hideLoading();
  449. }, 100);
  450. }
  451. });
  452. }
  453. };
  454. const updateLifeImage = (res, original = true) => {
  455. for (var i = 0; i < res.tempFilePaths.length; i++) {
  456. uni.showLoading({
  457. content: '加载中',
  458. mask: true
  459. });
  460. // if (original) currentPictureBlob.value = res.tempFilePaths[i];
  461. uni.uploadFile({
  462. url: `${apiBaseUrl}/api/UpImg/upimg`,
  463. filePath: res.tempFilePaths[i],
  464. header: {
  465. 'XX-Token': uni.getStorageSync('token'),
  466. 'XX-Device-Type': 'web'
  467. },
  468. name: 'file',
  469. formData: {},
  470. success: (uploadFileRes) => {
  471. const pictrue = JSON.parse(uploadFileRes.data);
  472. // if (original) {
  473. lifePictureOriginal.value.push(pictrue.data.url);
  474. lifePicture.value.push(pictrue.data.url);
  475. console.log('生活照',lifePictureOriginal.value);
  476. // handleLifePictureToggle(lifePicture.value.length - 1);
  477. // lifePicture.value[lifeOpenIndex.value] = pictrue.data.url;
  478. // } else {
  479. // lifePicture.value[lifeOpenIndex.value] = pictrue.data.url;
  480. // handleLifePictureToggleClose();
  481. // }
  482. },
  483. complete: () => {
  484. setTimeout(function () {
  485. uni.hideLoading();
  486. }, 100);
  487. }
  488. });
  489. }
  490. };
  491. const getUserInfo = () => {
  492. request({
  493. url: '/api/userjs/getInfo',
  494. success: (res) => {
  495. console.log('用户信息', res);
  496. if (res.data?.data?.work) {
  497. workPicture.value = res.data?.data?.work;
  498. }
  499. if (res.data?.data?.work_original) workPictureOriginal.value = [...res.data?.data?.work_original];
  500. else workPictureOriginal.value = [...res.data?.data?.work];
  501. if (res.data?.data?.pictures) {
  502. lifePicture.value = res.data?.data?.pictures;
  503. }
  504. if (res.data?.data?.life_original) lifePictureOriginal.value = [...res.data?.data?.life_original];
  505. else lifePictureOriginal.value = [...res.data?.data?.pictures];
  506. console.log('lifePictureOriginal1',lifePictureOriginal.value)
  507. if (res.data?.data?.identity) {
  508. idcardData.value = res.data?.data?.identity;
  509. }
  510. if (res.data?.data?.certificates) {
  511. certificatesPicture.value = res.data?.data?.certificates;
  512. }
  513. userData.value = res.data?.data;
  514. },
  515. fail: () => {},
  516. complete: () => {
  517. uni.hideLoading();
  518. }
  519. });
  520. };
  521. const handleDeleteImage = (e) => {
  522. console.log('删除', e);
  523. };
  524. onLoad((option) => {
  525. statusBarHeight.value = uni.getSystemInfoSync().statusBarHeight;
  526. renderData();
  527. });
  528. onShow(() => {
  529. uni.$emit('fastOrderNum', false);
  530. uni.$emit('receiveOrdernum', false);
  531. });
  532. async function renderData() {
  533. if (auth()) {
  534. uni.pageScrollTo({ scrollTop: 0, duration: 0 });
  535. getUserInfo();
  536. }
  537. }
  538. </script>
  539. <style lang="scss" scoped>
  540. :deep(.uni-section) {
  541. padding: 20upx 0;
  542. .uni-section-content {
  543. padding: 20upx;
  544. }
  545. .uni-progress {
  546. display: none;
  547. }
  548. }
  549. .idcard_wrap {
  550. display: flex;
  551. uni-image {
  552. width: 185px !important;
  553. height: 102px !important;
  554. }
  555. }
  556. .picture_popup_container {
  557. // height: 100vh;
  558. padding-bottom: 45px;
  559. padding-top: 45px;
  560. :deep(.uni-content-info) {
  561. .cropper-config {
  562. justify-content: space-around;
  563. }
  564. }
  565. }
  566. .picture_list_wrap {
  567. display: flex;
  568. flex-wrap: wrap;
  569. .picture {
  570. width: 222upx;
  571. height: 222upx;
  572. margin-right: 20upx !important;
  573. margin-bottom: 20upx;
  574. &:nth-child(3n) {
  575. margin-right: 0 !important;
  576. }
  577. }
  578. .picture_list {
  579. position: relative;
  580. display: flex;
  581. flex-direction: column;
  582. &:nth-child(3n) {
  583. .picture {
  584. margin-right: 0 !important;
  585. }
  586. }
  587. .picture_close {
  588. width: 45upx;
  589. height: 45upx;
  590. position: absolute;
  591. right: 18upx;
  592. z-index: 1;
  593. opacity: 0.8;
  594. }
  595. }
  596. }
  597. .agreement_wrap {
  598. clear: both;
  599. width: 100%;
  600. height: 50px;
  601. margin: 40upx auto 0;
  602. display: block;
  603. position: relative;
  604. text-align: center;
  605. line-height: 50px;
  606. font-size: 14px;
  607. color: #666;
  608. uni-image {
  609. width: 14px;
  610. height: 14px;
  611. position: relative;
  612. top: 2px;
  613. margin-right: 5px;
  614. }
  615. }
  616. .notice {
  617. margin: 0 auto;
  618. text-align: center;
  619. height: 40px;
  620. line-height: 20px;
  621. color: #999;
  622. font-size: 12px;
  623. font-weight: normal;
  624. }
  625. </style>