interview.js 16 KB

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