faceSea.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. <template>
  2. <view class="content">
  3. <!-- 拍照区域 -->
  4. <view class="cameraField">
  5. <!-- 上传的人脸图片 -->
  6. <canvas
  7. canvas-id="canvas"
  8. id="canvas"
  9. type="2d"
  10. style="width: 100%; height: 100%"
  11. ></canvas>
  12. <!-- 提示框 -->
  13. <view class="hint" v-if="isShow3">
  14. <image :src="hintImage" />
  15. {{ hintWord }}
  16. </view>
  17. <!-- “上传中”提示框 -->
  18. <view class="uploading" v-if="isShow4">
  19. <image src="../../static/images/等待@2x.png" mode="scaleToFill" />
  20. <text>人脸匹配中...</text>
  21. </view>
  22. <!-- 头像限制框 -->
  23. <image
  24. class="head"
  25. src="../../static/images/head1.png"
  26. mode="scaleToFill"
  27. />
  28. </view>
  29. <!-- 控件区域 -->
  30. <view class="controlField">
  31. <!-- 拍照控件 -->
  32. <view class="control1" v-if="isShow1">
  33. <view>
  34. <image class="light" src="../../static/images/light.png" />
  35. </view>
  36. <view @click="takePhoto()">
  37. <image class="take" src="../../static/images/take.png" />
  38. </view>
  39. <view>
  40. <image class="change" src="../../static/images/change.png" />
  41. </view>
  42. </view>
  43. <!-- 重拍/上传控件 -->
  44. <view class="control2" v-if="isShow2">
  45. <view @click="resetPhoto">重拍</view>
  46. <view @click="upload">使用照片</view>
  47. </view>
  48. </view>
  49. </view>
  50. </template>
  51. <script></script>
  52. <script>
  53. import { EXIF } from "../../node_modules/exif-js/exif";
  54. import { mapState, mapMutations } from "vuex";
  55. import {
  56. pathToBase64,
  57. base64ToPath,
  58. } from "../../js_sdk/mmmm-image-tools/index.js";
  59. export default {
  60. data() {
  61. return {
  62. isShow1: true, //拍照控件
  63. isShow2: false, //重拍/上传控件
  64. isShow3: true, //提示框
  65. isShow4: false, //“上传中”提示框
  66. hintImage: "../../static/images/注意@2x.png", //提示图标
  67. hintWord: "请勿遮挡面部", //提示文字
  68. imgPath: "", //上传的人脸图片
  69. baseStr: "",
  70. tipFlag: "",
  71. maxWidth: "",
  72. maxHeight: "",
  73. };
  74. },
  75. computed: mapState(["position", "userData"]),
  76. onLoad(options) {
  77. //拉取微信授权
  78. this.loginFilter();
  79. },
  80. mounted() {
  81. let that = this;
  82. (function () {
  83. var u = navigator.userAgent;
  84. var isAndroid = u.indexOf("Android") > -1 || u.indexOf("Linux") > -1; //android终端或者uc浏览器
  85. var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
  86. if (isiOS) {
  87. that.tipFlag = "ios";
  88. }
  89. if (isAndroid) {
  90. that.tipFlag = "Android";
  91. }
  92. })();
  93. },
  94. methods: {
  95. ...mapMutations(["getPosition", "getUserData"]),
  96. // 授权获取信息
  97. loginFilter() {
  98. let cardNumber = this.getQueryString("cardNumber");
  99. let name = this.getQueryString("name");
  100. let error = this.getQueryString("error");
  101. let homeWeb =
  102. "https://open.wecard.qq.com/connect/oauth/authorize?app_key=8C41E2FECF2E9925&response_type=code&scope=snsapi_userinfo&ocode=1015730314&redirect_uri=https://jtishfw.ncjti.edu.cn/yinxin/ncjtSecurityManagement/getUserInfo&state=1";
  103. if (!cardNumber) {
  104. window.location.href = homeWeb;
  105. } else {
  106. if (error) {
  107. uni.showToast({
  108. title: "服务器连接失败",
  109. icon: "error",
  110. mask: true,
  111. duration: 3000,
  112. });
  113. setTimeout(function () {
  114. uni.navigateTo({
  115. url: "../index/index",
  116. });
  117. }, 1000);
  118. }
  119. this.$store.state.name = name;
  120. this.$store.state.idnum = cardNumber;
  121. this.judgeGrade();
  122. this.judgeWrite();
  123. }
  124. },
  125. //判断老生无权限
  126. judgeGrade() {
  127. var date = new Date();
  128. var year = date.getFullYear().toString();
  129. let grade = this.getQueryString("grade");
  130. if (grade == year) {
  131. return;
  132. } else {
  133. uni.showToast({
  134. title: "无权限,仅对新生请退出",
  135. icon: "error",
  136. mask: true,
  137. duration: 50000,
  138. });
  139. return;
  140. }
  141. },
  142. //判断是否填写问卷
  143. judgeWrite() {
  144. uni.request({
  145. url: "https://jtishfw.ncjti.edu.cn/yinxin/ncjtSecurityManagement/freshmanSurveyDoneByIdnum",
  146. data: {
  147. idnum: this.$store.state.idnum,
  148. },
  149. header: {
  150. Accept: "application/json",
  151. "Content-Type": "application/json",
  152. "X-Requested-With": "XMLHttpRequest",
  153. },
  154. method: "GET",
  155. sslVerify: true,
  156. success: ({ data, statusCode, header }) => {
  157. console.log(data);
  158. if (data.result === "success") {
  159. uni.navigateTo({
  160. url: "../index/index",
  161. });
  162. }
  163. },
  164. fail: (error) => {},
  165. });
  166. },
  167. //获取当前URL指定参数
  168. getQueryString(name) {
  169. let url = window.location.href; // 获取URL
  170. let pattern = new RegExp("[\?\&]" + name + "=([^\&]+)", "i"); // 正则匹配URL
  171. let matcher = pattern.exec(url);
  172. if (matcher == null || matcher.length < 1) {
  173. return false;
  174. }
  175. return decodeURIComponent(matcher[1]); // 输出指定的参数值 中文也可以
  176. },
  177. //拍照
  178. takePhoto() {
  179. if (this.isShow4) {
  180. this.isShow3 = !this.isShow3;
  181. this.isShow4 = !this.isShow4;
  182. this.isShow1 = !this.isShow1;
  183. this.isShow2 = !this.isShow2;
  184. } else {
  185. this.isShow1 = !this.isShow1;
  186. this.isShow2 = !this.isShow2;
  187. }
  188. this.photo();
  189. },
  190. //重拍
  191. resetPhoto() {
  192. if (this.isShow4) {
  193. this.isShow3 = !this.isShow3;
  194. this.isShow4 = !this.isShow4;
  195. this.isShow1 = !this.isShow1;
  196. this.isShow2 = !this.isShow2;
  197. }
  198. this.photo();
  199. },
  200. //压缩上传的图片
  201. compressUploadImg(img, width, height, ratio) {
  202. // img可以是dataURL或者图片url
  203. let canvas = document.createElement("canvas");
  204. canvas.width = width;
  205. canvas.height = height;
  206. let ctx = canvas.getContext("2d");
  207. let img64 = canvas.toDataURL("image/jpeg", ratio);
  208. console.log(img64);
  209. return (this.$store.state.imageBase = img64); // 压缩后的base64串
  210. },
  211. //安卓机图片修正
  212. async detail(url) {
  213. let maxWidth = this.maxWidth;
  214. let Orientation = 1;
  215. //获取图片META信息
  216. await this.getImageTag(url, "Orientation", function (e) {
  217. if (e != undefined) Orientation = e;
  218. });
  219. var img = null;
  220. var canvas = null;
  221. await this.comprossImage(url, maxWidth, function (e) {
  222. img = e.img;
  223. canvas = e.canvas;
  224. });
  225. // console.log(Orientation,"Orientation")
  226. let baseStr = "";
  227. //如果方向角不为1,都需要进行旋转
  228. switch (Orientation) {
  229. case 6: //需要顺时针(向右)90度旋转
  230. baseStr = this.rotateImg(img, "right", canvas);
  231. break;
  232. case 8: //需要逆时针(向左)90度旋转
  233. baseStr = this.rotateImg(img, "left", canvas);
  234. break;
  235. case 3: //需要180度旋转 转两次
  236. baseStr = this.rotateImg(img, "right", canvas, 2);
  237. break;
  238. default:
  239. baseStr = this.rotateImg(img, "", canvas);
  240. break;
  241. }
  242. // console.log(baseStr, "baseStr");
  243. },
  244. async comprossImage(imgSrc, maxWidth, func) {
  245. if (!imgSrc) return 0;
  246. return new Promise((resolve, reject) => {
  247. uni.getImageInfo({
  248. src: imgSrc,
  249. success(res) {
  250. let img = new Image();
  251. img.src = res.path;
  252. // console.log(img);
  253. let canvas = uni.createCanvasContext("canvas");
  254. let obj = new Object();
  255. obj.img = img;
  256. obj.canvas = canvas;
  257. resolve(func(obj));
  258. let fileObj = document.createElement("file"); //上传文件的对象
  259. },
  260. });
  261. });
  262. },
  263. getImageTag(file, tag, suc) {
  264. if (!file) return 0;
  265. return new Promise((resolve, reject) => {
  266. /* eslint-disable func-names */
  267. // 箭头函数会修改this,所以这里不能用箭头函数
  268. let imgObj = new Image();
  269. imgObj.src = file;
  270. // console.log(imgObj);
  271. uni.getImageInfo({
  272. src: file,
  273. success(res) {
  274. EXIF.getData(imgObj, function () {
  275. EXIF.getAllTags(this);
  276. let or = EXIF.getTag(this, "Orientation"); //这个Orientation 就是我们判断需不需要旋转的值了,有1、3、6、8
  277. resolve(suc(or));
  278. });
  279. },
  280. });
  281. });
  282. },
  283. rotateImg(img, direction, canvas, times = 1) {
  284. console.log("开始旋转");
  285. //最小与最大旋转方向,图片旋转4次后回到原方向
  286. var min_step = 0;
  287. var max_step = 3;
  288. if (img == null) return;
  289. //img的高度和宽度不能在img元素隐藏后获取,否则会出错
  290. var height = img.height;
  291. var width = img.width;
  292. let maxWidth = this.maxWidth;
  293. let maxHeight = this.maxHeight;
  294. var step = 0;
  295. if (step == null) {
  296. step = min_step;
  297. }
  298. if (direction == "right") {
  299. step += times;
  300. //旋转到原位置,即超过最大值
  301. step > max_step && (step = min_step);
  302. } else if (direction == "left") {
  303. step -= times;
  304. step < min_step && (step = max_step);
  305. } else {
  306. //不旋转
  307. step = 0;
  308. }
  309. //旋转角度以弧度值为参数
  310. var degree = (step * 90 * Math.PI) / 180;
  311. var ctx = uni.createCanvasContext("canvas");
  312. // var ctx = canvas.getContext("2d");
  313. // console.log(degree);
  314. // console.log(step);
  315. switch (step) {
  316. case 1:
  317. // console.log("右旋转 90度");
  318. width = maxHeight;
  319. height = maxWidth;
  320. ctx.rotate(degree);
  321. ctx.drawImage(img.src, 0, -height, width, height);
  322. ctx.draw();
  323. break;
  324. case 2:
  325. //console.log('旋转 180度')
  326. width = maxWidth;
  327. height = maxHeight;
  328. ctx.rotate(degree);
  329. ctx.drawImage(img.src, -width, -height, width, height);
  330. ctx.draw();
  331. break;
  332. case 3:
  333. // console.log("左旋转 90度");
  334. width = maxHeight;
  335. height = maxWidth;
  336. ctx.rotate(degree);
  337. ctx.drawImage(img.src, -width, 0, width, height);
  338. ctx.draw();
  339. break;
  340. default:
  341. //不旋转
  342. // canvas.width = width;
  343. // canvas.height = height;
  344. width = maxWidth;
  345. height = maxHeight;
  346. ctx.drawImage(img.src, 0, 0, width, height);
  347. ctx.draw();
  348. break;
  349. }
  350. // let baseStr = canvas.toDataURL("image/jpeg", 1);
  351. // let baseStr = this.$store.state.imageBase;
  352. // return baseStr;
  353. },
  354. //照片
  355. photo() {
  356. if (this.$store.state.idnum) {
  357. let that = this;
  358. uni.chooseImage({
  359. count: 1,
  360. sourceType: ["camera"],
  361. sizeType: ["compressed"],
  362. success: (res) => {
  363. uni.getSystemInfo({
  364. success: function (res2) {
  365. that.maxWidth = res2.windowWidth;
  366. let base = 4 / 3;
  367. that.maxHeight = that.maxWidth * base;
  368. },
  369. });
  370. that.imgPath = res.tempFilePaths[0]; //这就是要的blod
  371. //上传压缩图片
  372. // that.toBase64(that.imgPath);
  373. that.compressUploadImg(
  374. that.imgPath,
  375. that.maxWidth,
  376. that.maxHeight,
  377. 1
  378. );
  379. //图片页面显示
  380. if (that.tipFlag == "ios") {
  381. let canvas = uni.createCanvasContext("canvas");
  382. canvas.drawImage(
  383. that.imgPath,
  384. 0,
  385. 0,
  386. that.maxWidth,
  387. that.maxHeight
  388. );
  389. canvas.draw();
  390. } else {
  391. that.detail(that.imgPath);
  392. }
  393. },
  394. complete: () => {
  395. if (that.imgPath == null) {
  396. this.isShow1 = !this.isShow1;
  397. this.isShow2 = !this.isShow2;
  398. }
  399. },
  400. });
  401. } else {
  402. uni.showToast({
  403. title: "请下拉刷新授权",
  404. icon: "error",
  405. mask: true,
  406. });
  407. }
  408. },
  409. //上传图片
  410. upload() {
  411. this.isShow3 = !this.isShow3;
  412. this.isShow4 = !this.isShow4;
  413. uni.request({
  414. url: "https://jtishfw.ncjti.edu.cn/yinxin/ncjtSecurityManagement/verifyBase64ImagesWithIDNumber",
  415. data: {
  416. idnum: this.$store.state.idnum,
  417. image: this.$store.state.imageBase,
  418. },
  419. header: { "content-type": "application/x-www-form-urlencoded" },
  420. method: "POST",
  421. sslVerify: true,
  422. success: ({ data, statusCode, header }) => {
  423. if (data.error) {
  424. this.isShow3 = !this.isShow3;
  425. this.isShow4 = !this.isShow4;
  426. uni.showToast({
  427. title: "人脸匹配失败",
  428. icon: "error",
  429. mask: true,
  430. duration: 3000,
  431. });
  432. setTimeout(this.back, 1000);
  433. } else {
  434. this.$store.state.sex = data.sex;
  435. this.$store.state.examNumber = data.examNumber;
  436. setTimeout(this.uploadSucceed, 500);
  437. }
  438. },
  439. fail: (error) => {
  440. this.isShow3 = !this.isShow3;
  441. this.isShow4 = !this.isShow4;
  442. uni.showToast({
  443. title: "上传失败",
  444. icon: "error",
  445. mask: true,
  446. duration: 3000,
  447. });
  448. setTimeout(this.back, 2000);
  449. },
  450. });
  451. },
  452. //人脸采集成功
  453. uploadSucceed() {
  454. this.hintWord = "人脸采集成功";
  455. this.hintImage = "../../static/images/成功@2x.png";
  456. this.isShow3 = !this.isShow3;
  457. this.isShow4 = !this.isShow4;
  458. this.isShow2 = !this.isShow2;
  459. setTimeout(this.navigateToConfirm, 2000);
  460. },
  461. //图片转Base64
  462. toBase64(path) {
  463. pathToBase64(path)
  464. .then((base64) => {
  465. this.$store.state.imageBase = base64;
  466. })
  467. .catch((error) => {
  468. console.error(error);
  469. });
  470. },
  471. //跳转人脸采集确认页面
  472. navigateToConfirm() {
  473. uni.navigateTo({
  474. url: "../confirm/confirm",
  475. });
  476. },
  477. back() {
  478. uni.reLaunch({
  479. url: "../faceSearch/faceSea",
  480. });
  481. },
  482. },
  483. };
  484. </script>
  485. <style lang="scss">
  486. @import url("./css/faceSea.min.css");
  487. </style>