interview.js 18 KB

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