base.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. <template>
  2. <view>
  3. <uni-card>
  4. <uni-section title="基本信息" type="line">
  5. <uni-forms ref="baseForm" :modelValue="baseFormData" :rules="rules"
  6. style="margin-top: 3vh;margin-left: 3vw;">
  7. <uni-forms-item label="证件照" required name="lrXm">
  8. <lzcOCR class="lzcOCR" :src="zjcaijianSrc" @end="idcardEnd"></lzcOCR>
  9. </uni-forms-item>
  10. <uni-forms-item label="姓名" required name="lrXm">
  11. <uni-easyinput ref="xm" type="text" v-model="baseFormData.lrXm" placeholder="请输入姓名"
  12. :inputBorder="false"></uni-easyinput>
  13. </uni-forms-item>
  14. <uni-forms-item label="手机号" required name="lrCydh">
  15. <uni-easyinput type="text" v-model="baseFormData.lrCydh" placeholder="请输入手机号"
  16. :inputBorder="false"></uni-easyinput>
  17. </uni-forms-item>
  18. <uni-forms-item label="住址" required name="lrHjbcxx">
  19. <uni-easyinput type="text" v-model="baseFormData.lrHjbcxx" placeholder="请输入住址"
  20. :inputBorder="false"></uni-easyinput>
  21. </uni-forms-item>
  22. <!-- <uni-forms-item label="分数">
  23. <uni-easyinput type="text" v-model="score"
  24. :inputBorder="false"></uni-easyinput>
  25. </uni-forms-item> -->
  26. <uni-forms-item label="人像" required>
  27. <image :src="src" @click="takePhoto" mode="widthFix" style="width: 150px;"></image>
  28. </uni-forms-item>
  29. <view v-if="show">
  30. <uni-forms-item label="直接保存">
  31. <uni-data-checkbox v-model="radio" :localdata="radioData"></uni-data-checkbox>
  32. </uni-forms-item>
  33. <text>注意:由于人脸比对不成功可以直接保存人工审核!</text>
  34. </view>
  35. <view>
  36. <button cursor-spacing="22px" class="buttonClass" @click="save()">保存</button>
  37. </view>
  38. </uni-forms>
  39. </uni-section>
  40. </uni-card>
  41. </view>
  42. </template>
  43. <script>
  44. import lzcOCR from '@/components/lzc-OCR/lzc-OCR.vue';
  45. import {
  46. decryptRowData_ECB,
  47. decryptData_ECB
  48. } from '@/common/sm4.js'
  49. import {
  50. showConfirm,
  51. getDictInfo,
  52. toast,
  53. toBase64
  54. } from '@/common/common.js'
  55. import {
  56. UploadOne
  57. } from '@/api/upload.js'
  58. import {
  59. updateKhjbxx,
  60. infoKhjbxx,
  61. GetLrByZjhm,
  62. SaveKhjbxx
  63. } from '@/api/kh.js'
  64. import {
  65. getUser
  66. } from '@/common/auth.js'
  67. import {
  68. setToken,
  69. setOpenid,
  70. getOpenid,
  71. setUser
  72. } from '@/common/auth.js'
  73. import CryptoJS from 'crypto-js';
  74. import config from '@/config.js';
  75. import idCardNoUtil from '@/common/idcard.js'
  76. import {
  77. translate,
  78. base64ToUrl
  79. } from '@/common/image.js'
  80. import {
  81. Register
  82. } from '@/api/login.js'
  83. export default {
  84. components: {
  85. lzcOCR
  86. },
  87. data() {
  88. return {
  89. show: false,
  90. radio: 0,
  91. radioData: [{
  92. text: '否',
  93. value: 0
  94. }, {
  95. text: '是',
  96. value: 1
  97. }],
  98. // 字典
  99. dicts: {
  100. // 民族
  101. C0009: [],
  102. // 性别
  103. C0007: [],
  104. },
  105. // 百度云access_token
  106. token: '',
  107. client_id: config.face_client_id,
  108. client_secret: config.face_client_secret,
  109. // 人像地址
  110. src: "/static/images/head.png",
  111. // 身份拍摄地址
  112. zjSrc: "",
  113. // 身份证裁剪地址
  114. zjcaijianSrc: "/static/images/sfsb.png",
  115. showSrc: "",
  116. // 人员信息
  117. userInfo: {},
  118. // 组
  119. groupId: '',
  120. // 身份证号加密
  121. idcardMD5: '',
  122. // 人像base64
  123. face: '',
  124. // 身份证base64
  125. idcardFace: '',
  126. idcardFaceUrl: '',
  127. // 对比份数
  128. score: 0,
  129. // ocrXm
  130. ocrXm: '',
  131. isSearch: false,
  132. updateFlag: false,
  133. baseFormData: {
  134. lrXm: '',
  135. lrZjhm: '',
  136. lrHjbcxx: '',
  137. lrCydh: '',
  138. },
  139. rules: {
  140. lrHjbcxx: {
  141. rules: [{
  142. required: true,
  143. errorMessage: '住址不能为空'
  144. }]
  145. },
  146. lrXm: {
  147. rules: [{
  148. required: true,
  149. errorMessage: '姓名不能为空'
  150. }]
  151. },
  152. photo: {
  153. rules: [{
  154. required: true,
  155. errorMessage: '请采集人像'
  156. }]
  157. },
  158. lrCydh: {
  159. rules: [{
  160. required: true,
  161. errorMessage: '手机号不能为空'
  162. },
  163. {
  164. format: 'number',
  165. errorMessage: '请输入正确的手机号',
  166. },
  167. {
  168. pattern: '^(((13[0-9]{1})|(15[0-9]{1})|(18[0-9]{1})|(17[0-9]{1}))+\\d{8})$',
  169. errorMessage: '请输入正确的手机号',
  170. }
  171. ]
  172. }
  173. },
  174. }
  175. },
  176. created() {},
  177. onLoad(o) {
  178. this.getDictList(Object.keys(this.dicts), this.dicts)
  179. this.userInfo = getUser()
  180. this.baseFormData.lrHjdz = this.userInfo.dept.locationCode
  181. this.groupId = this.userInfo.dept.locationCode.substring(0, 6)
  182. if (this.userInfo.userType == '07') {
  183. this.updateFlag = true
  184. let info = JSON.parse(o.info)
  185. this.baseFormData.lrHjdz = info.lrHjdz
  186. this.groupId = info.lrHjdz.substring(0, 6)
  187. this.baseFormData = info
  188. this.src = config.baseUrl + info.lrTx
  189. }
  190. },
  191. methods: {
  192. getAccessToken() {
  193. uni.request({
  194. url: '/baiduApi/oauth/2.0/token',
  195. data: {
  196. grant_type: 'client_credentials',
  197. client_id: this.client_id,
  198. client_secret: this.client_secret
  199. },
  200. method: 'POST',
  201. header: {
  202. 'Content-Type': 'application/x-www-form-urlencoded'
  203. },
  204. success: (res) => {
  205. if (res.statusCode == 200) {
  206. this.token = res.data.access_token
  207. this.match()
  208. }
  209. },
  210. error: (err) => {
  211. uni.hideLoading()
  212. }
  213. })
  214. },
  215. // 人脸对比
  216. match() {
  217. let face = this.face
  218. let idcardFace = this.idcardFace
  219. let data = [{
  220. image: face,
  221. image_type: 'BASE64',
  222. liveness_control: 'NORMAL',
  223. }, {
  224. image: idcardFace,
  225. image_type: 'BASE64'
  226. }]
  227. uni.request({
  228. url: '/baiduApi/rest/2.0/face/v3/match?access_token=' + this.token,
  229. data: data,
  230. method: 'POST',
  231. header: {
  232. 'Content-Type': 'application/json'
  233. },
  234. success: (res) => {
  235. if (res.data.error_msg == 'SUCCESS') {
  236. this.score = res.data.result.score;
  237. this.baseFormData.lrTxdb = JSON.stringify({
  238. "lrJmzjhm": this.idcardMD5,
  239. "xsfs": this.score
  240. })
  241. if (this.score >= config.score) {
  242. this.show = false
  243. this.baseFormData.lzzt = 3
  244. this.baseFormData.lrSpyj = '同意'
  245. this.baseFormData.prelrZjz = this.idcardFaceUrl
  246. this.faceSearch()
  247. } else {
  248. // 低于80选项是否人工审核,是的话进记录表
  249. // 身份证头像保存后端
  250. // 人脸库注册人脸、身份证md5
  251. // 修改的时候去人脸库搜索,搜索不到不允许修改
  252. // 修改成功替换原始人脸库照片
  253. // 首次修改搜索身份证
  254. // 后端没入库,人脸库相应删除
  255. // ocr失败身份证原版入库,ocr成功人脸入库
  256. // showConfirm('人像与身份证不符,请重新上传')
  257. this.baseFormData.lzzt = 1
  258. this.baseFormData.lrSpyj = ''
  259. this.show = true
  260. this.baseFormData.prelrZjz = this.zjcaijianSrc
  261. this.faceSearch()
  262. }
  263. } else {
  264. showConfirm(res.data.error_msg)
  265. uni.hideLoading()
  266. }
  267. },
  268. error: (err) => {
  269. uni.hideLoading()
  270. }
  271. })
  272. },
  273. createGroup() {
  274. uni.request({
  275. url: '/baiduApi/rest/2.0/face/v3/faceset/group/add?access_token=' + this.token,
  276. data: {
  277. group_id: this.groupId,
  278. },
  279. method: 'POST',
  280. header: {
  281. 'Content-Type': 'application/x-www-form-urlencoded'
  282. },
  283. success: (res) => {
  284. if (res.statusCode == 200) {
  285. this.faceAdd()
  286. }
  287. },
  288. error: (err) => {
  289. uni.hideLoading()
  290. }
  291. })
  292. },
  293. // 人脸注册
  294. faceAdd() {
  295. // https://cloud.baidu.com/doc/FACE/s/Gk37c1uzc#%E4%BA%BA%E8%84%B8%E6%B3%A8%E5%86%8C
  296. let face = this.face
  297. let data = {
  298. image: face,
  299. image_type: 'BASE64',
  300. group_id: this.groupId,
  301. user_id: this.idcardMD5,
  302. action_type: 'REPLACE', // 操作方式 APPEND: 当user_id在库中已经存在时,对此user_id重复注册时,新注册的图片默认会追加到该user_id下 REPLACE : 当对此user_id重复注册时,则会用新图替换库中该user_id下所有图片 默认使用APPEND
  303. }
  304. uni.request({
  305. url: '/baiduApi/rest/2.0/face/v3/faceset/user/add?access_token=' + this.token,
  306. data: data,
  307. method: 'POST',
  308. header: {
  309. 'Content-Type': 'application/json'
  310. },
  311. success: (res) => {
  312. uni.hideLoading()
  313. if (res.data.error_msg != 'SUCCESS') {
  314. showConfirm(res.data.error_msg)
  315. }
  316. },
  317. error: (err) => {
  318. uni.hideLoading()
  319. }
  320. })
  321. },
  322. // 人脸搜索
  323. faceSearch() {
  324. let face = this.face
  325. let data = {
  326. image: face,
  327. image_type: 'BASE64',
  328. group_id_list: this.groupId,
  329. match_threshold: config.score,
  330. max_user_num: 50
  331. }
  332. uni.request({
  333. url: '/baiduApi/rest/2.0/face/v3/search?access_token=' + this.token,
  334. data: data,
  335. method: 'POST',
  336. header: {
  337. 'Content-Type': 'application/json'
  338. },
  339. success: (res) => {
  340. if (res.data.error_msg == 'SUCCESS') {
  341. // this.isSearch = true
  342. if (res.data.result.user_list.length < 1) {
  343. this.createGroup()
  344. } else {
  345. if (res.data.result.user_list[0].score >= config.score) {
  346. let xs = []
  347. res.data.result.user_list.forEach(e => {
  348. if (e.user_id != this.idcardMD5) {
  349. xs.push({
  350. "lrJmzjhm": e.user_id,
  351. "xsfs": e.score
  352. })
  353. this.baseFormData.lzzt = 2
  354. this.baseFormData.lrSpyj = ''
  355. }
  356. })
  357. this.baseFormData.xslrZjhm = JSON.stringify(xs)
  358. }
  359. // this.faceAdd()
  360. uni.hideLoading()
  361. }
  362. } else {
  363. // this.isSearch = false
  364. // uni.hideLoading()
  365. this.faceAdd()
  366. }
  367. },
  368. error: (err) => {
  369. uni.hideLoading()
  370. }
  371. })
  372. },
  373. save() {
  374. if (this.src == '/static/images/head.png') {
  375. showConfirm('请采集人像')
  376. return
  377. } else {
  378. if (!this.ocrXm) {
  379. showConfirm('修改信息请重新识别身份证人像面')
  380. return
  381. }
  382. if (this.ocrXm != this.baseFormData.lrXm) {
  383. showConfirm('姓名与真实姓名不符,请重新识别身份证人像面')
  384. return
  385. }
  386. // if (!this.isSearch) {
  387. // showConfirm('当前区县暂未开放,请联系相关人员')
  388. // return
  389. // }
  390. if ((this.score >= config.score) || (this.score < config.score && this.radio == '1')) {
  391. uni.showLoading({
  392. title: '正在保存中...'
  393. })
  394. this.$refs['baseForm'].validate().then(res => {
  395. UploadOne(this.baseFormData.prelrZjz, {}).then(re => {
  396. this.baseFormData.lrZjz = re.data.url
  397. UploadOne(this.src, {}).then(re => {
  398. this.baseFormData.lrTx = re.data.url
  399. if (this.userInfo.userType == '07') this.baseFormData.id = this
  400. .userInfo.userId
  401. if (this.updateFlag) {
  402. updateKhjbxx(this.baseFormData).then(r => {
  403. uni.hideLoading()
  404. if (r.code == 200) {
  405. toast('保存成功')
  406. setTimeout(function() {
  407. uni.switchTab({
  408. url: '/pages/index/index'
  409. })
  410. }, 1000)
  411. }
  412. })
  413. } else {
  414. SaveKhjbxx(this.baseFormData).then(r => {
  415. uni.hideLoading()
  416. if (r.code == 200) {
  417. toast('保存成功')
  418. // setOpenid(r.data.openId)
  419. // setUser(r.data.sysUser)
  420. // setToken(r.data.token.access_token)
  421. setTimeout(function() {
  422. uni.switchTab({
  423. url: '/pages/index/index'
  424. })
  425. }, 1000)
  426. }
  427. })
  428. }
  429. })
  430. })
  431. }).catch(err => {
  432. uni.hideLoading()
  433. })
  434. } else {
  435. showConfirm('请重新采集人像')
  436. return
  437. }
  438. }
  439. },
  440. takePhoto() {
  441. if (!this.zjSrc) {
  442. showConfirm('请先识别身份证人像面')
  443. return
  444. }
  445. uni.chooseImage({
  446. count: 1,
  447. mediaType: ['image'],
  448. sizeType: ['compressed'],
  449. sourceType: ['camera'],
  450. success: (res) => {
  451. uni.showLoading({
  452. title: '正在识别中...'
  453. })
  454. let size = res.tempFiles[0].size
  455. let scale = 1
  456. if (size / 1024 / 1024 > 0.9) scale = 0.6
  457. translate(res.tempFilePaths[0], scale, 'blob', this.setSrc)
  458. }
  459. })
  460. },
  461. setSrc(e, blobUrl) {
  462. this.src = blobUrl
  463. uni.getFileInfo({
  464. filePath: blobUrl,
  465. success: (res) => {
  466. let size = res.size
  467. let scale = 1
  468. if (size / 1024 / 1024 > 0.9) {
  469. scale = 0.6
  470. translate(this.src, scale, 'blob', this.setSrc)
  471. } else {
  472. e = e.replace('data:image/jpeg;base64,', "");
  473. this.face = e
  474. this.getAccessToken()
  475. }
  476. },
  477. fail: (err) => {
  478. console.log(err);
  479. }
  480. })
  481. },
  482. // 身份证识别
  483. idcardEnd(words, src) {
  484. if (words.image_status == "other_type_card") {
  485. showConfirm('请识别正确的身份证人像面')
  486. return
  487. }
  488. // 身份证号校验 性别 出生日期
  489. // 修改之后的姓名和ocr返回校验,重新ocr
  490. let id = words.words_result['公民身份号码'].words
  491. let csrq = words.words_result['出生'].words
  492. let sex = words.words_result['性别'].words
  493. let info = idCardNoUtil.getIdCardInfo(id)
  494. if (!idCardNoUtil.checkIdCardNo(id)) {
  495. showConfirm('身份证号识别有误,请重新识别')
  496. return
  497. }
  498. if (this.userType == '07' && this.baseFormData.lrZjhm != id) {
  499. showConfirm('请使用本人身份证重新识别')
  500. return
  501. }
  502. if (info.birthday != csrq) {
  503. showConfirm('身份证出生日期识别有误,请重新识别')
  504. return
  505. }
  506. if (info.gender != sex) {
  507. showConfirm('身份证性别识别有误,请重新识别')
  508. return
  509. }
  510. if (words.image_status == "reversed_side") {
  511. showConfirm('请识别身份证人像面')
  512. return
  513. }
  514. if (words.risk_type != "normal") {
  515. // normal-正常身份证;copy-复印件;temporary-临时身份证;screen-翻拍;unknown-其他未知情况
  516. showConfirm('请识别正确的身份证人像面')
  517. return
  518. }
  519. this.baseFormData.lrXb = getDictInfo(this.dicts.C0007, words.words_result['性别'].words)[0].value
  520. this.baseFormData.lrMz = getDictInfo(this.dicts.C0009, words.words_result['民族'].words)[0].value
  521. this.idcardFace = words.photo
  522. this.idcardFaceUrl = base64ToUrl(words.photo);
  523. this.baseFormData.lrZjhm = id
  524. this.baseFormData.lrCsrq = csrq
  525. this.idcardMD5 = CryptoJS.MD5(this.baseFormData.lrZjhm).toString()
  526. this.baseFormData.lrXm = words.words_result['姓名'].words
  527. this.ocrXm = words.words_result['姓名'].words
  528. this.zjSrc = src
  529. this.zjcaijianSrc = base64ToUrl(words.card_image);
  530. if (this.userInfo.userType != '07') {
  531. GetLrByZjhm({
  532. lrJmzjhm: this.idcardMD5
  533. }).then(res => {
  534. if (res.data) this.baseFormData = decryptData_ECB(res.data, ["lrZjhm", "lrXm", "lrHjbcxx",
  535. "lrXjdzBcxx",
  536. "lrCydh", "lrPoxm", "lrPoZjmh", "jhrXm", "jhrSjhm", "zlrXm", "zlrDh", "cjzh",
  537. "yhzh", "khmc"
  538. ], [2, 1, 4, 4, 3, 1, 2, 1, 2, 1, 3, 5, 5, 5]);
  539. if (res.data && res.data.lrHjdz) this.baseFormData.lrHjdz = res.data
  540. .lrHjdz
  541. if (this.baseFormData.lrTx) this.src = config.baseUrl + this.baseFormData.lrTx
  542. if (res.data) this.updateFlag = true
  543. })
  544. }
  545. },
  546. }
  547. }
  548. </script>
  549. <style scoped>
  550. .lzcOCR {
  551. /* width: 77%;
  552. display: flex;
  553. justify-content: center; */
  554. }
  555. .buttonClass {
  556. margin-top: 4vh;
  557. margin-left: 4vw;
  558. width: 280px;
  559. /* height: 5vh; */
  560. border-radius: 5.8vw;
  561. /* border: 1px solid rgba(176, 179, 199, 1); */
  562. background: #28d87d;
  563. color: white;
  564. height: 5vh;
  565. display: flex;
  566. align-items: center;
  567. justify-content: center;
  568. box-shadow:0rpx 4rpx 10rpx 1rpx rgba(40, 216, 125,0.4);
  569. }
  570. </style>