interview.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. "use strict";
  2. const common_vendor = require("../../common/vendor.js");
  3. const common_config = require("../../common/config.js");
  4. const _sfc_main = {
  5. data() {
  6. return {
  7. currentStep: 0,
  8. photos: [null, null, null, null, null, null],
  9. // 存储6张照片:左手正面、左手反面、左手握拳、右手正面、右手反面、右手握拳
  10. instructions: [
  11. "请将左手掌正面朝上,保持手指自然伸展,确保整个手掌清晰可见",
  12. "请将左手掌反面朝上,保持手指自然伸展,确保整个手背清晰可见",
  13. "请握紧左手拳头,使拳面朝向相机,确保整个拳头清晰可见",
  14. "请将右手掌正面朝上,保持手指自然伸展,确保整个手掌清晰可见",
  15. "请将右手掌反面朝上,保持手指自然伸展,确保整个手背清晰可见",
  16. "请握紧右手拳头,使拳面朝向相机,确保整个拳头清晰可见"
  17. ],
  18. previewTexts: [
  19. "左手掌正面照片预览区",
  20. "左手掌反面照片预览区",
  21. "左手握拳照片预览区",
  22. "右手掌正面照片预览区",
  23. "右手掌反面照片预览区",
  24. "右手握拳照片预览区"
  25. ],
  26. photoTypes: ["left_palm", "left_back", "left_fist", "right_palm", "right_back", "right_fist"],
  27. // 照片类型标识
  28. isH5: false,
  29. showCamera: false,
  30. mediaStream: null,
  31. cameraContext: null,
  32. cameraMode: "normal",
  33. // 添加相机模式
  34. cameraInitRetries: 0,
  35. // 添加重试计数器
  36. gestureOverlays: [
  37. /* '/static/images/palm_overlay.png', // 手掌正面引导图
  38. '/static/images/back_overlay.png', // 手掌反面引导图
  39. '/static/images/fist_overlay.png' // 握拳引导图 */
  40. ],
  41. gestureHints: [
  42. "请将左手掌对准轮廓线",
  43. "请将左手背对准轮廓线",
  44. "请将左手拳头对准轮廓线",
  45. "请将右手掌对准轮廓线",
  46. "请将右手背对准轮廓线",
  47. "请将右手拳头对准轮廓线"
  48. ],
  49. photoLinks: [null, null, null, null, null, null],
  50. // 存储上传后的图片链接
  51. cameraPosition: "front"
  52. // 修改默认为前置摄像头
  53. };
  54. },
  55. computed: {
  56. canGoNext() {
  57. return this.photos[this.currentStep] !== null;
  58. }
  59. },
  60. onLoad() {
  61. common_vendor.index.authorize({
  62. scope: "scope.camera",
  63. success: () => {
  64. console.log("相机权限已授权");
  65. },
  66. fail: () => {
  67. common_vendor.index.showModal({
  68. title: "提示",
  69. content: "请授权相机权限以完成手部照片采集",
  70. success: (res) => {
  71. if (res.confirm) {
  72. common_vendor.index.openSetting();
  73. }
  74. }
  75. });
  76. }
  77. });
  78. },
  79. beforeDestroy() {
  80. this.stopMediaStream();
  81. },
  82. methods: {
  83. takePhoto() {
  84. if (this.isH5) {
  85. this.startCamera();
  86. } else {
  87. this.showCamera = true;
  88. this.cameraInitRetries = 0;
  89. this.$nextTick(() => {
  90. try {
  91. this.cameraContext = common_vendor.index.createCameraContext();
  92. console.log("相机上下文创建成功");
  93. } catch (err) {
  94. console.error("创建相机上下文失败:", err);
  95. this.handleCameraInitError();
  96. }
  97. });
  98. }
  99. },
  100. uploadPhoto(filePathOrData, type) {
  101. console.log(`准备上传${type}类型的手部照片`);
  102. common_vendor.index.showLoading({
  103. title: "上传中..."
  104. });
  105. const userInfo = common_vendor.index.getStorageSync("userInfo");
  106. userInfo ? JSON.parse(userInfo).openid || "" : "";
  107. const tenant_id = common_vendor.index.getStorageSync("tenant_id") || "1";
  108. if (!tenant_id) {
  109. common_vendor.index.hideLoading();
  110. common_vendor.index.showToast({
  111. title: "用户信息不完整,请重新登录",
  112. icon: "none"
  113. });
  114. return;
  115. }
  116. if (this.isH5 && filePathOrData.startsWith("data:image")) {
  117. const base64Data = filePathOrData.split(",")[1];
  118. const blob = this.base64ToBlob(base64Data, "image/jpeg");
  119. const formData = new FormData();
  120. formData.append("photo_file", blob, `${type}.jpg`);
  121. formData.append("application_id", common_vendor.index.getStorageSync("appId"));
  122. formData.append("tenant_id", tenant_id);
  123. formData.append("description", `手部照片-${type}`);
  124. console.log("H5上传参数:", {
  125. application_id: common_vendor.index.getStorageSync("appId"),
  126. tenant_id,
  127. description: `手部照片-${type}`
  128. });
  129. fetch(`${common_config.apiBaseUrl}/job/upload_posture_photo`, {
  130. method: "POST",
  131. body: formData
  132. }).then((response) => response.json()).then((result) => {
  133. console.log("照片上传成功:", result);
  134. const index = this.photoTypes.indexOf(type);
  135. if (index !== -1 && result.data && result.data.url) {
  136. this.$set(this.photoLinks, index, result.data.url);
  137. console.log(`已保存${type}类型照片链接:`, result.data.url);
  138. }
  139. common_vendor.index.hideLoading();
  140. common_vendor.index.showToast({
  141. title: "照片上传成功",
  142. icon: "success"
  143. });
  144. }).catch((error) => {
  145. console.error("照片上传失败:", error);
  146. common_vendor.index.hideLoading();
  147. common_vendor.index.showToast({
  148. title: "照片上传失败,请重试",
  149. icon: "none"
  150. });
  151. });
  152. } else {
  153. console.log(`小程序环境上传文件路径:`, filePathOrData);
  154. console.log("小程序上传参数:", {
  155. application_id: common_vendor.index.getStorageSync("appId"),
  156. tenant_id,
  157. description: `手部照片-${type}`
  158. });
  159. common_vendor.index.uploadFile({
  160. url: `${common_config.apiBaseUrl}/job/upload_posture_photo`,
  161. filePath: filePathOrData,
  162. name: "photo_file",
  163. // 确保文件参数名称正确
  164. formData: {
  165. // 使用formData替代data以确保参数正确传递
  166. "application_id": common_vendor.index.getStorageSync("appId"),
  167. "tenant_id": tenant_id,
  168. "description": `手部照片-${type}`
  169. },
  170. success: (uploadRes) => {
  171. console.log("照片上传成功:", uploadRes);
  172. let result;
  173. try {
  174. if (typeof uploadRes.data === "string") {
  175. result = JSON.parse(uploadRes.data);
  176. } else {
  177. result = uploadRes.data;
  178. }
  179. console.log("解析后的上传结果:", result);
  180. const index = this.photoTypes.indexOf(type);
  181. if (index !== -1 && result.data && result.data.url) {
  182. this.$set(this.photoLinks, index, result.data.url);
  183. console.log(`已保存${type}类型照片链接:`, result.data.url);
  184. }
  185. } catch (e) {
  186. console.error("解析上传结果失败:", e);
  187. }
  188. common_vendor.index.hideLoading();
  189. common_vendor.index.showToast({
  190. title: "照片上传成功",
  191. icon: "success"
  192. });
  193. },
  194. fail: (err) => {
  195. console.error("照片上传失败:", err);
  196. common_vendor.index.hideLoading();
  197. common_vendor.index.showToast({
  198. title: "照片上传失败,请重试",
  199. icon: "none"
  200. });
  201. },
  202. complete: () => {
  203. common_vendor.index.hideLoading();
  204. }
  205. });
  206. }
  207. },
  208. prevStep() {
  209. if (this.currentStep > 0) {
  210. this.currentStep--;
  211. }
  212. },
  213. nextStep() {
  214. if (this.currentStep < 5) {
  215. this.currentStep++;
  216. } else if (this.currentStep === 5 && this.photos[5]) {
  217. this.submitAllPhotos();
  218. }
  219. },
  220. previewPhoto(index) {
  221. if (this.photos[index]) {
  222. common_vendor.index.previewImage({
  223. urls: [this.photos[index]],
  224. current: this.photos[index]
  225. });
  226. }
  227. },
  228. submitAllPhotos() {
  229. if (this.photos.every((photo) => photo !== null)) {
  230. common_vendor.index.showLoading({
  231. title: "处理中..."
  232. });
  233. setTimeout(() => {
  234. common_vendor.index.hideLoading();
  235. common_vendor.index.showToast({
  236. title: "手部照片采集完成",
  237. icon: "success",
  238. duration: 2e3,
  239. success: () => {
  240. setTimeout(() => {
  241. common_vendor.index.navigateTo({
  242. url: "/pages/success/success"
  243. });
  244. }, 2e3);
  245. }
  246. });
  247. }, 500);
  248. } else {
  249. common_vendor.index.showToast({
  250. title: "请完成所有照片拍摄",
  251. icon: "none"
  252. });
  253. }
  254. },
  255. // H5环境下启动摄像头
  256. startCamera() {
  257. this.showCamera = true;
  258. if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
  259. const facingMode = this.cameraPosition === "front" ? "user" : "environment";
  260. navigator.mediaDevices.getUserMedia({
  261. video: { facingMode }
  262. }).then((stream) => {
  263. this.mediaStream = stream;
  264. this.$nextTick(() => {
  265. const videoElement = this.$refs.videoElement;
  266. if (videoElement) {
  267. videoElement.srcObject = stream;
  268. }
  269. });
  270. }).catch((error) => {
  271. console.error("获取摄像头失败:", error);
  272. common_vendor.index.showToast({
  273. title: "无法访问摄像头,请检查权限设置",
  274. icon: "none"
  275. });
  276. this.showCamera = false;
  277. });
  278. } else {
  279. common_vendor.index.showToast({
  280. title: "您的浏览器不支持摄像头功能",
  281. icon: "none"
  282. });
  283. this.showCamera = false;
  284. }
  285. },
  286. // H5环境下拍照
  287. capturePhoto() {
  288. console.log("执行H5拍照方法");
  289. const videoElement = this.$refs.videoElement;
  290. const canvasElement = this.$refs.canvasElement;
  291. if (videoElement && canvasElement) {
  292. try {
  293. const context = canvasElement.getContext("2d");
  294. canvasElement.width = videoElement.videoWidth;
  295. canvasElement.height = videoElement.videoHeight;
  296. context.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
  297. const photoData = canvasElement.toDataURL("image/jpeg");
  298. this.$set(this.photos, this.currentStep, photoData);
  299. this.uploadPhoto(photoData, this.photoTypes[this.currentStep]);
  300. this.stopMediaStream();
  301. this.showCamera = false;
  302. } catch (err) {
  303. console.error("H5拍照失败:", err);
  304. common_vendor.index.showToast({
  305. title: "拍照失败,请重试",
  306. icon: "none"
  307. });
  308. }
  309. }
  310. },
  311. // 小程序环境下拍照
  312. capturePhotoMP() {
  313. console.log("执行小程序拍照方法");
  314. if (!this.cameraContext) {
  315. console.error("相机上下文不存在");
  316. this.handleCameraInitError();
  317. return;
  318. }
  319. try {
  320. common_vendor.index.showLoading({
  321. title: "拍照中...",
  322. mask: true
  323. });
  324. this.cameraContext.takePhoto({
  325. quality: "high",
  326. success: (res) => {
  327. console.log("拍照成功:", res);
  328. common_vendor.index.hideLoading();
  329. this.$set(this.photos, this.currentStep, res.tempImagePath);
  330. this.uploadPhoto(res.tempImagePath, this.photoTypes[this.currentStep]);
  331. this.showCamera = false;
  332. },
  333. fail: (err) => {
  334. common_vendor.index.hideLoading();
  335. console.error("拍照失败:", err);
  336. common_vendor.index.showToast({
  337. title: "拍照失败,请重试",
  338. icon: "none"
  339. });
  340. this.fallbackToChooseImage();
  341. }
  342. });
  343. } catch (err) {
  344. common_vendor.index.hideLoading();
  345. console.error("takePhoto方法执行失败:", err);
  346. this.fallbackToChooseImage();
  347. }
  348. },
  349. // 添加相机初始化错误处理
  350. handleCameraInitError() {
  351. if (this.cameraInitRetries < 3) {
  352. this.cameraInitRetries++;
  353. common_vendor.index.showToast({
  354. title: `相机初始化失败,正在重试(${this.cameraInitRetries}/3)`,
  355. icon: "none"
  356. });
  357. setTimeout(() => {
  358. this.showCamera = false;
  359. setTimeout(() => {
  360. this.takePhoto();
  361. }, 500);
  362. }, 1e3);
  363. } else {
  364. common_vendor.index.showToast({
  365. title: "相机初始化失败,将使用系统相机",
  366. icon: "none"
  367. });
  368. this.fallbackToChooseImage();
  369. }
  370. },
  371. // 添加备用的系统相机方法
  372. fallbackToChooseImage() {
  373. this.showCamera = false;
  374. common_vendor.index.chooseImage({
  375. count: 1,
  376. sourceType: ["camera"],
  377. success: (res) => {
  378. this.$set(this.photos, this.currentStep, res.tempFilePaths[0]);
  379. this.uploadPhoto(res.tempFilePaths[0], this.photoTypes[this.currentStep]);
  380. },
  381. fail: (err) => {
  382. console.error("选择图片失败:", err);
  383. common_vendor.index.showToast({
  384. title: "拍照失败,请重试",
  385. icon: "none"
  386. });
  387. }
  388. });
  389. },
  390. // 修改相机错误处理方法
  391. handleCameraError(e) {
  392. console.error("相机错误:", e.detail);
  393. const errorInfo = JSON.stringify(e.detail);
  394. console.log("详细错误信息:", errorInfo);
  395. common_vendor.index.showToast({
  396. title: "相机启动失败,将使用系统相机",
  397. icon: "none"
  398. });
  399. this.fallbackToChooseImage();
  400. },
  401. // 取消拍照 - 更新为同时支持H5和小程序
  402. cancelCamera() {
  403. if (this.isH5) {
  404. this.stopMediaStream();
  405. }
  406. this.showCamera = false;
  407. },
  408. // 停止摄像头流
  409. stopMediaStream() {
  410. if (this.mediaStream) {
  411. this.mediaStream.getTracks().forEach((track) => {
  412. track.stop();
  413. });
  414. this.mediaStream = null;
  415. }
  416. },
  417. // base64转blob工具方法
  418. base64ToBlob(base64, mimeType) {
  419. const byteCharacters = atob(base64);
  420. const byteArrays = [];
  421. for (let offset = 0; offset < byteCharacters.length; offset += 512) {
  422. const slice = byteCharacters.slice(offset, offset + 512);
  423. const byteNumbers = new Array(slice.length);
  424. for (let i = 0; i < slice.length; i++) {
  425. byteNumbers[i] = slice.charCodeAt(i);
  426. }
  427. const byteArray = new Uint8Array(byteNumbers);
  428. byteArrays.push(byteArray);
  429. }
  430. return new Blob(byteArrays, { type: mimeType });
  431. },
  432. retryCamera() {
  433. this.showCamera = false;
  434. setTimeout(() => {
  435. this.takePhoto();
  436. }, 500);
  437. },
  438. // 可以添加一个方法来切换蒙层显示
  439. toggleGestureOverlay() {
  440. },
  441. // 切换摄像头
  442. switchCamera() {
  443. this.cameraPosition = this.cameraPosition === "front" ? "back" : "front";
  444. if (this.isH5 && this.showCamera) {
  445. this.stopMediaStream();
  446. this.startCamera();
  447. }
  448. }
  449. }
  450. };
  451. function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  452. return common_vendor.e({
  453. a: common_vendor.t($data.instructions[$data.currentStep]),
  454. b: $data.photos[$data.currentStep]
  455. }, $data.photos[$data.currentStep] ? {
  456. c: $data.photos[$data.currentStep]
  457. } : {
  458. d: common_vendor.t($data.previewTexts[$data.currentStep])
  459. }, {
  460. e: common_vendor.f($data.photos, (photo, index, i0) => {
  461. return common_vendor.e({
  462. a: photo
  463. }, photo ? {
  464. b: photo,
  465. c: common_vendor.o(($event) => $options.previewPhoto(index), index)
  466. } : {
  467. d: common_vendor.t(index + 1)
  468. }, {
  469. e: index,
  470. f: $data.currentStep === index ? 1 : "",
  471. g: photo ? 1 : ""
  472. });
  473. }),
  474. f: common_vendor.t($data.photos[$data.currentStep] ? "重新拍摄" : "拍摄照片"),
  475. g: common_vendor.o((...args) => $options.takePhoto && $options.takePhoto(...args)),
  476. h: $data.currentStep === 0,
  477. i: common_vendor.o((...args) => $options.prevStep && $options.prevStep(...args)),
  478. j: common_vendor.t($data.currentStep === 5 && $data.photos[5] ? "完成" : "下一步"),
  479. k: !$options.canGoNext,
  480. l: common_vendor.o((...args) => $options.nextStep && $options.nextStep(...args)),
  481. m: !$data.isH5 && $data.showCamera
  482. }, !$data.isH5 && $data.showCamera ? {
  483. n: $data.cameraPosition,
  484. o: common_vendor.o((...args) => $options.handleCameraError && $options.handleCameraError(...args)),
  485. p: $data.cameraMode,
  486. q: $data.gestureOverlays[$data.currentStep],
  487. r: common_vendor.t($data.gestureHints[$data.currentStep]),
  488. s: common_vendor.o((...args) => $options.capturePhotoMP && $options.capturePhotoMP(...args)),
  489. t: common_vendor.o((...args) => $options.cancelCamera && $options.cancelCamera(...args))
  490. } : {}, {
  491. v: $data.isH5
  492. }, $data.isH5 ? {
  493. w: $data.gestureOverlays[$data.currentStep],
  494. x: common_vendor.t($data.gestureHints[$data.currentStep]),
  495. y: common_vendor.o((...args) => $options.capturePhoto && $options.capturePhoto(...args)),
  496. z: common_vendor.o((...args) => $options.switchCamera && $options.switchCamera(...args)),
  497. A: common_vendor.o((...args) => $options.cancelCamera && $options.cancelCamera(...args)),
  498. B: $data.showCamera
  499. } : {});
  500. }
  501. const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
  502. wx.createPage(MiniProgramPage);