faceSea.vue 13 KB

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