camera.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784
  1. "use strict";
  2. const common_vendor = require("../../common/vendor.js");
  3. const api_user = require("../../api/user.js");
  4. const common_config = require("../../common/config.js");
  5. const common_assets = require("../../common/assets.js");
  6. const _sfc_main = {
  7. data() {
  8. return {
  9. cameraContext: null,
  10. devicePosition: "front",
  11. interviewStarted: true,
  12. showEndModal: false,
  13. currentQuestionIndex: 0,
  14. currentStep: 1,
  15. stepTexts: ["测试准备", "回答问题", "测试结束"],
  16. progressWidth: 50,
  17. remainingTime: "00:27",
  18. selectedOption: null,
  19. selectedOptions: [],
  20. showResult: false,
  21. isAnswerCorrect: false,
  22. questions: [],
  23. // 改为空数组,将通过API获取
  24. interviewId: null,
  25. // 存储当前面试ID
  26. useVideo: false,
  27. timerInterval: null,
  28. score: 0,
  29. totalQuestions: 0,
  30. interviewCompleted: false,
  31. digitalHumanUrl: "",
  32. // 数字人URL
  33. loading: true,
  34. // 添加加载状态
  35. loadError: false,
  36. // 添加加载错误状态
  37. errorMessage: "",
  38. // 添加错误消息
  39. answers: [],
  40. // 存储用户的所有答案
  41. currentQuestionDetail: null,
  42. // 当前题目详情
  43. isSubmitting: false,
  44. // 是否正在提交答案
  45. openQuestionAnswer: "",
  46. // 存储开放问题的答案
  47. currentAnswer: null,
  48. // 存储当前答案以便提交
  49. questionStartTime: null,
  50. // 存储问题开始时间
  51. mode: "normal",
  52. // 相机模式
  53. currentScrollId: "question-0",
  54. // 当前滚动到的问题ID
  55. questionGroups: [],
  56. // 存储分组后的题目
  57. currentGroupIndex: 0,
  58. // 当前显示的组索引
  59. showContinueButton: false,
  60. // 是否显示继续按钮
  61. scrollToBottom: false,
  62. // 新增:是否需要滚动到底部
  63. scrollTop: 0,
  64. // 控制 scroll-view 的滚动位置
  65. _clickTimer: null,
  66. _isClicking: false,
  67. showPromptModal: false
  68. // 添加新的数据属性
  69. };
  70. },
  71. computed: {
  72. currentQuestion() {
  73. console.log(this.questions[this.currentQuestionIndex]);
  74. return this.questions[this.currentQuestionIndex];
  75. }
  76. },
  77. onLoad(options) {
  78. if (options && options.id) {
  79. this.interviewId = options.id;
  80. this.fetchInterviewData();
  81. } else {
  82. this.fetchInterviewList();
  83. }
  84. },
  85. onReady() {
  86. this.cameraContext = common_vendor.index.createCameraContext();
  87. if (this.useVideo) {
  88. this.aiVideoContext = common_vendor.index.createVideoContext("aiInterviewer");
  89. }
  90. this.initDigitalHuman();
  91. },
  92. methods: {
  93. // 获取面试列表
  94. async fetchInterviewList() {
  95. try {
  96. this.loading = true;
  97. const res2 = await api_user.getQuestions({
  98. position_id: JSON.parse(common_vendor.index.getStorageSync("selectedJob")).id,
  99. page: 1,
  100. limit: 999,
  101. tenant_id: JSON.parse(common_vendor.index.getStorageSync("userInfo")).tenant_id || 1
  102. });
  103. console.log(res2.items);
  104. this.interviewId = res2.items;
  105. this.fetchInterviewData(res2.items);
  106. } catch (error) {
  107. console.error("获取面试列表失败:", error);
  108. this.handleLoadError("获取面试列表失败");
  109. }
  110. },
  111. // 获取面试详情数据
  112. async fetchInterviewData(data) {
  113. try {
  114. this.loading = true;
  115. if (data && Array.isArray(data)) {
  116. const formattedQuestions = data.filter((q) => q.question_form !== 0).map((q, index) => ({
  117. id: q.id || index + 1,
  118. text: q.question || "未知问题",
  119. options: q.options || [],
  120. correctAnswer: q.correctAnswer || 0,
  121. isImportant: q.is_system || false,
  122. explanation: q.explanation || "",
  123. questionType: q.question_form,
  124. questionTypeName: q.question_form_name || "单选题",
  125. is_required_correct: q.is_required_correct || false,
  126. correctAnswers: q.correct_answers || [],
  127. difficulty: q.difficulty || 1,
  128. difficultyName: q.difficulty_name || "初级",
  129. imageUrl: q.question_image_url || "",
  130. // 添加图片URL字段
  131. category: q.category || 0
  132. // 添加 category 字段
  133. }));
  134. this.processQuestionGroups(formattedQuestions);
  135. } else {
  136. this.processInterviewData(res);
  137. }
  138. console.log(this.questions);
  139. this.totalQuestions = this.questions.length;
  140. } catch (error) {
  141. console.error("获取面试详情失败:", error);
  142. this.handleLoadError("获取面试详情失败");
  143. } finally {
  144. this.loading = false;
  145. }
  146. },
  147. // 处理面试数据
  148. processInterviewData(data) {
  149. this.questions = [];
  150. if (data && data.question_form !== 0) {
  151. const formattedQuestion = {
  152. id: data.id || 1,
  153. text: data.question || "未知问题",
  154. options: data.options || [],
  155. correctAnswer: data.correctAnswer || 0,
  156. isImportant: data.is_system || false,
  157. explanation: data.explanation || "",
  158. questionType: data.question_form,
  159. // 1-单选题,2-多选题,3-看图答题
  160. questionTypeName: data.question_form_name || "单选题",
  161. is_required_correct: data.is_required_correct || false,
  162. correctAnswers: data.correct_answers || [],
  163. difficulty: data.difficulty || 1,
  164. difficultyName: data.difficulty_name || "初级",
  165. imageUrl: data.question_image_url || ""
  166. // 添加图片URL字段
  167. };
  168. this.questions.push(formattedQuestion);
  169. this.totalQuestions = this.questions.length;
  170. } else {
  171. this.handleLoadError("没有可用的选择题");
  172. }
  173. },
  174. // 处理加载错误
  175. handleLoadError(message) {
  176. this.loadError = true;
  177. this.loading = false;
  178. this.errorMessage = message || "加载失败";
  179. common_vendor.index.showToast({
  180. title: message || "加载失败",
  181. icon: "none",
  182. duration: 2e3
  183. });
  184. },
  185. startTimer() {
  186. if (this.questions.length === 0)
  187. return;
  188. this.questionStartTime = /* @__PURE__ */ new Date();
  189. let seconds = 60;
  190. this.remainingTime = `01:00`;
  191. this.timerInterval = setInterval(() => {
  192. seconds--;
  193. if (seconds <= 0) {
  194. clearInterval(this.timerInterval);
  195. if (!this.showResult) {
  196. this.checkAnswer();
  197. setTimeout(() => {
  198. if (this.currentQuestionIndex < this.questions.length - 1) {
  199. this.goToNextQuestion();
  200. } else {
  201. common_vendor.index.navigateTo({
  202. url: "/pages/interview-question/interview-question"
  203. });
  204. }
  205. }, 1500);
  206. }
  207. }
  208. const min = Math.floor(seconds / 60).toString().padStart(2, "0");
  209. const sec = (seconds % 60).toString().padStart(2, "0");
  210. this.remainingTime = `${min}:${sec}`;
  211. }, 1e3);
  212. },
  213. resetTimer() {
  214. clearInterval(this.timerInterval);
  215. },
  216. selectOption(index) {
  217. if (this.currentQuestionIndex === this.questions.length - 1 && this.showContinueButton) {
  218. common_vendor.index.showToast({
  219. title: "该题目已完成,请继续下一部分",
  220. icon: "none",
  221. duration: 2e3
  222. });
  223. return;
  224. }
  225. if (this.showResult)
  226. return;
  227. console.log("selectOption", index);
  228. if (this.currentQuestion.questionType === 2) {
  229. const optionIndex = this.selectedOptions.indexOf(index);
  230. if (optionIndex > -1) {
  231. this.selectedOptions.splice(optionIndex, 1);
  232. } else {
  233. this.selectedOptions.push(index);
  234. }
  235. } else if (this.currentQuestion.questionType === 1 || this.currentQuestion.questionType === 3 || this.currentQuestion.questionType === 4) {
  236. this.selectedOption = index;
  237. this.playAiSpeaking();
  238. setTimeout(() => {
  239. this.checkAnswer();
  240. this.saveAnswer();
  241. this.submitCurrentAnswer().then(() => {
  242. setTimeout(() => {
  243. this.goToNextQuestion();
  244. }, 800);
  245. }).catch((error) => {
  246. console.error("提交答案失败:", error);
  247. setTimeout(() => {
  248. this.goToNextQuestion();
  249. }, 1e3);
  250. });
  251. }, 500);
  252. } else {
  253. this.selectedOption = index;
  254. const answer = {
  255. questionId: this.currentQuestion.id,
  256. questionType: this.currentQuestion.questionType,
  257. answer: index
  258. };
  259. const existingIndex = this.answers.findIndex((a) => a.questionId === answer.questionId);
  260. if (existingIndex > -1) {
  261. this.answers[existingIndex] = answer;
  262. } else {
  263. this.answers.push(answer);
  264. }
  265. this.playAiSpeaking();
  266. setTimeout(() => {
  267. this.checkAnswer();
  268. this.saveAnswer();
  269. this.submitCurrentAnswer().then(() => {
  270. setTimeout(() => {
  271. this.goToNextQuestion();
  272. }, 800);
  273. }).catch((error) => {
  274. console.error("提交答案失败:", error);
  275. setTimeout(() => {
  276. this.goToNextQuestion();
  277. }, 1e3);
  278. });
  279. }, 500);
  280. }
  281. },
  282. checkAnswer() {
  283. if (this.showResult)
  284. return;
  285. clearInterval(this.timerInterval);
  286. if (!this.currentQuestion) {
  287. console.error("当前问题不存在");
  288. return;
  289. }
  290. if (this.currentQuestion.questionType === 0) {
  291. this.isAnswerCorrect = true;
  292. } else if (this.currentQuestion.questionType === 2) {
  293. const sortedSelected = [...this.selectedOptions].sort();
  294. const sortedCorrect = [...this.currentQuestion.correctAnswers].sort();
  295. if (sortedSelected.length !== sortedCorrect.length) {
  296. this.isAnswerCorrect = false;
  297. } else {
  298. this.isAnswerCorrect = sortedSelected.every((value, index) => value === sortedCorrect[index]);
  299. }
  300. } else {
  301. this.isAnswerCorrect = this.selectedOption === this.currentQuestion.correctAnswer;
  302. }
  303. this.showResult = true;
  304. this.saveAnswer();
  305. if (this.currentQuestionIndex === this.questions.length - 1) {
  306. this.$nextTick(() => {
  307. this.$forceUpdate();
  308. });
  309. }
  310. },
  311. // 修改 nextQuestion 方法,只处理多选题
  312. nextQuestion() {
  313. if (this.currentQuestion.questionType !== 2)
  314. return;
  315. this.checkAnswer();
  316. this.saveAnswer();
  317. this.submitCurrentAnswer().then(() => {
  318. setTimeout(() => {
  319. if (this.currentQuestionIndex >= this.questions.length - 1) {
  320. if (this.currentGroupIndex < this.questionGroups.length - 1) {
  321. this.showContinueButton = true;
  322. setTimeout(() => {
  323. this.scrollTop = 0;
  324. this.$nextTick(() => {
  325. setTimeout(() => {
  326. this.scrollTop = 1e4;
  327. }, 100);
  328. });
  329. }, 200);
  330. } else {
  331. common_vendor.index.navigateTo({
  332. url: "/pages/interview-question/interview-question"
  333. });
  334. }
  335. } else {
  336. this.goToNextQuestion();
  337. }
  338. }, 500);
  339. }).catch((error) => {
  340. console.error("提交答案失败:", error);
  341. setTimeout(() => {
  342. if (this.currentQuestionIndex >= this.questions.length - 1) {
  343. if (this.currentGroupIndex < this.questionGroups.length - 1) {
  344. this.showContinueButton = true;
  345. setTimeout(() => {
  346. this.scrollTop = 1e4;
  347. }, 200);
  348. } else {
  349. common_vendor.index.navigateTo({
  350. url: "/pages/interview-question/interview-question"
  351. });
  352. }
  353. } else {
  354. this.goToNextQuestion();
  355. }
  356. }, 1e3);
  357. });
  358. },
  359. toggleSettings() {
  360. common_vendor.index.showToast({
  361. title: "设置功能开发中",
  362. icon: "none"
  363. });
  364. },
  365. back(target) {
  366. if (this.timerInterval) {
  367. clearInterval(this.timerInterval);
  368. }
  369. if (target) {
  370. common_vendor.index.navigateTo({
  371. url: target
  372. });
  373. return;
  374. }
  375. },
  376. error(e) {
  377. console.error(e.detail);
  378. common_vendor.index.showToast({
  379. title: "相机启动失败,请检查权限设置",
  380. icon: "none"
  381. });
  382. },
  383. playAiSpeaking() {
  384. if (this.useVideo && this.aiVideoContext) {
  385. this.aiVideoContext.play();
  386. }
  387. if (this.digitalHumanUrl) {
  388. const speakText = this.currentQuestion ? this.currentQuestion.text : "";
  389. this.interactWithDigitalHuman(speakText);
  390. }
  391. },
  392. pauseAiSpeaking() {
  393. if (this.useVideo && this.aiVideoContext) {
  394. this.aiVideoContext.pause();
  395. }
  396. },
  397. // 修改 restartTest 方法,添加可选的跳转目标
  398. restartTest(target) {
  399. this.currentQuestionIndex = 0;
  400. this.score = 0;
  401. this.showEndModal = false;
  402. this.showResult = false;
  403. this.selectedOption = null;
  404. this.selectedOptions = [];
  405. this.resetTimer();
  406. if (target) {
  407. common_vendor.index.navigateTo({
  408. url: target
  409. });
  410. }
  411. },
  412. // 在methods中添加测试方法
  413. testEndScreen() {
  414. this.interviewCompleted = true;
  415. this.showEndModal = false;
  416. },
  417. // 初始化数字人
  418. initDigitalHuman() {
  419. this.digitalHumanUrl = "";
  420. },
  421. // 与数字人交互的方法
  422. interactWithDigitalHuman(message) {
  423. const webview = this.$mp.page.$getAppWebview().children()[0];
  424. if (webview) {
  425. webview.evalJS(`receiveMessage('${message}')`);
  426. }
  427. },
  428. // 添加 navigateToInterview 方法
  429. navigateToInterview() {
  430. this.showEndModal = false;
  431. common_vendor.index.navigateTo({
  432. url: "/pages/interview/interview",
  433. success: () => {
  434. console.log("成功跳转到interview页面");
  435. },
  436. fail: (err) => {
  437. console.error("跳转失败:", err);
  438. common_vendor.index.showToast({
  439. title: "跳转失败,请手动返回首页",
  440. icon: "none"
  441. });
  442. }
  443. });
  444. },
  445. // 添加 handleCameraError 方法
  446. handleCameraError(e) {
  447. console.error("相机错误:", e.detail);
  448. common_vendor.index.showToast({
  449. title: "相机启动失败,请检查权限设置",
  450. icon: "none"
  451. });
  452. },
  453. // 修改 isOptionSelected 方法
  454. isOptionSelected(question, qIndex, optionIndex) {
  455. const answer = this.answers.find((a) => a.questionId === question.id);
  456. if (qIndex === this.currentQuestionIndex) {
  457. if (question.questionType === 1 || question.questionType === 3 || question.questionType === 4) {
  458. return this.selectedOption === optionIndex;
  459. } else if (question.questionType === 2) {
  460. return this.selectedOptions.includes(optionIndex);
  461. }
  462. } else if (answer) {
  463. if (question.questionType === 1 || question.questionType === 3 || question.questionType === 4) {
  464. return answer.answer === optionIndex;
  465. } else if (question.questionType === 2 && Array.isArray(answer.answer)) {
  466. return answer.answer.includes(optionIndex);
  467. }
  468. }
  469. return false;
  470. },
  471. // 修改 handleOptionClick 方法
  472. handleOptionClick(qIndex, index) {
  473. if (qIndex < this.currentQuestionIndex) {
  474. common_vendor.index.showToast({
  475. title: "该题目已完成,无法修改",
  476. icon: "none",
  477. duration: 2e3
  478. });
  479. return;
  480. }
  481. if (qIndex === this.questions.length - 1 && this.showContinueButton) {
  482. common_vendor.index.showToast({
  483. title: "该题目已完成,请继续下一部分",
  484. icon: "none",
  485. duration: 2e3
  486. });
  487. return;
  488. }
  489. if (qIndex > this.currentQuestionIndex) {
  490. const firstUnansweredIndex = this.currentQuestionIndex;
  491. common_vendor.index.showToast({
  492. title: `请先完成第 ${firstUnansweredIndex + 1} 题`,
  493. icon: "none",
  494. duration: 2e3
  495. });
  496. this.currentScrollId = "question-" + firstUnansweredIndex;
  497. return;
  498. }
  499. if (this._isClicking) {
  500. return;
  501. }
  502. this._isClicking = true;
  503. if (this._clickTimer) {
  504. clearTimeout(this._clickTimer);
  505. }
  506. try {
  507. this.selectOption(index);
  508. this._clickTimer = setTimeout(() => {
  509. this._isClicking = false;
  510. }, 1e3);
  511. } catch (error) {
  512. console.error("处理选项点击失败:", error);
  513. common_vendor.index.showToast({
  514. title: "操作失败,请重试",
  515. icon: "none"
  516. });
  517. this._isClicking = false;
  518. }
  519. },
  520. // 处理题目分组
  521. processQuestionGroups(questions) {
  522. const group1 = [];
  523. const group2 = [];
  524. questions.forEach((q) => {
  525. if (q.category === 5) {
  526. group1.push(q);
  527. } else {
  528. group2.push(q);
  529. }
  530. });
  531. this.questionGroups = [group2, group1].filter((group) => group.length > 0);
  532. this.questions = this.questionGroups[0] || [];
  533. },
  534. // 处理继续按钮点击
  535. handleContinue() {
  536. if (this.currentGroupIndex < this.questionGroups.length - 1) {
  537. if (this.currentGroupIndex === 0) {
  538. this.showPromptModal = true;
  539. } else {
  540. this.proceedToNextGroup();
  541. }
  542. } else {
  543. common_vendor.index.navigateTo({
  544. url: "/pages/interview-question/interview-question"
  545. });
  546. }
  547. },
  548. // 添加新的方法处理弹窗确认
  549. handlePromptConfirm() {
  550. this.showPromptModal = false;
  551. this.proceedToNextGroup();
  552. },
  553. // 添加新的方法处理组切换逻辑
  554. proceedToNextGroup() {
  555. this.currentGroupIndex++;
  556. this.questions = this.questionGroups[this.currentGroupIndex];
  557. this.currentQuestionIndex = 0;
  558. this.showContinueButton = false;
  559. this.currentScrollId = "question-0";
  560. this.scrollTop = 0;
  561. this.selectedOption = null;
  562. this.selectedOptions = [];
  563. this.showResult = false;
  564. },
  565. // 保存当前题目的答案
  566. saveAnswer() {
  567. let answer;
  568. if (this.currentQuestion.questionType === 0) {
  569. answer = {
  570. questionId: this.currentQuestion.id,
  571. questionType: this.currentQuestion.questionType,
  572. answer: this.openQuestionAnswer,
  573. answerDuration: this.getAnswerDuration()
  574. // 添加答题时长
  575. };
  576. } else if (this.currentQuestion.questionType === 1 || this.currentQuestion.questionType === 3 || this.currentQuestion.questionType === 4) {
  577. answer = {
  578. questionId: this.currentQuestion.id,
  579. questionType: this.currentQuestion.questionType,
  580. answer: this.selectedOption,
  581. answerDuration: this.getAnswerDuration()
  582. // 添加答题时长
  583. };
  584. } else {
  585. answer = {
  586. questionId: this.currentQuestion.id,
  587. questionType: this.currentQuestion.questionType,
  588. answer: this.selectedOptions,
  589. answerDuration: this.getAnswerDuration()
  590. // 添加答题时长
  591. };
  592. }
  593. const existingIndex = this.answers.findIndex((a) => a.questionId === answer.questionId);
  594. if (existingIndex > -1) {
  595. this.answers[existingIndex] = answer;
  596. } else {
  597. this.answers.push(answer);
  598. }
  599. this.currentAnswer = answer;
  600. console.log("已保存答案:", this.answers);
  601. },
  602. // 获取答题时长(秒)
  603. getAnswerDuration() {
  604. const remainingTimeArr = this.remainingTime.split(":");
  605. const remainingSeconds = parseInt(remainingTimeArr[0]) * 60 + parseInt(remainingTimeArr[1]);
  606. return 30 - remainingSeconds;
  607. },
  608. // 提交当前答案
  609. async submitCurrentAnswer() {
  610. if (!this.currentAnswer || this.isSubmitting)
  611. return;
  612. try {
  613. this.isSubmitting = true;
  614. common_vendor.index.showLoading({
  615. title: "正在提交答案..."
  616. });
  617. let answerContent = "";
  618. let answerOptions = [];
  619. if (this.currentAnswer.questionType === 0) {
  620. answerContent = this.currentAnswer.answer;
  621. answerOptions = [];
  622. } else if (this.currentAnswer.questionType === 1 || this.currentAnswer.questionType === 3 || this.currentAnswer.questionType === 4) {
  623. const selectedIndex = this.currentAnswer.answer;
  624. const selectedOption = this.currentQuestion.options[selectedIndex];
  625. console.log("selectedOption", selectedOption);
  626. const optionId = selectedOption.id ? selectedOption.id : selectedIndex;
  627. answerContent = optionId.toString();
  628. answerOptions = [optionId];
  629. } else if (this.currentAnswer.questionType === 2) {
  630. const selectedIndices = this.currentAnswer.answer;
  631. const selectedOptionIds = selectedIndices.map((index) => {
  632. const option = this.currentQuestion.options[index];
  633. return option.id ? option.id : index;
  634. });
  635. answerOptions = selectedOptionIds;
  636. }
  637. const submitData = {
  638. position_id: JSON.parse(common_vendor.index.getStorageSync("selectedJob")).id,
  639. applicant_id: JSON.parse(common_vendor.index.getStorageSync("userInfo")).id,
  640. job_position_question_id: this.currentAnswer.questionId,
  641. // answer_content: answerContent,
  642. answer_options: answerOptions,
  643. answer_duration: this.currentAnswer.answerDuration || 0,
  644. tenant_id: JSON.parse(common_vendor.index.getStorageSync("userInfo")).tenant_id || 1
  645. };
  646. console.log("提交数据:", submitData);
  647. const res2 = await this.$http.post(`${common_config.apiBaseUrl}/api/job/submit_answer`, submitData);
  648. console.log("提交答案响应:", res2);
  649. return res2;
  650. } catch (error) {
  651. console.error("提交答案失败:", error);
  652. common_vendor.index.showToast({
  653. title: "提交答案失败,请重试",
  654. icon: "none"
  655. });
  656. throw error;
  657. } finally {
  658. common_vendor.index.hideLoading();
  659. this.isSubmitting = false;
  660. }
  661. },
  662. // 修改 goToNextQuestion 方法
  663. async goToNextQuestion() {
  664. if (this.currentQuestionIndex >= this.questions.length - 1) {
  665. this.showContinueButton = true;
  666. setTimeout(() => {
  667. this.scrollTop = 0;
  668. this.$nextTick(() => {
  669. setTimeout(() => {
  670. this.scrollTop = 1e4;
  671. }, 100);
  672. });
  673. }, 200);
  674. return;
  675. }
  676. this.currentQuestionIndex++;
  677. this.currentScrollId = "question-" + this.currentQuestionIndex;
  678. this.showResult = false;
  679. this.selectedOption = null;
  680. this.selectedOptions = [];
  681. this.openQuestionAnswer = "";
  682. if (this.questions[this.currentQuestionIndex]) {
  683. this.progressWidth = (this.currentQuestionIndex + 1) / this.questions.length * 100;
  684. this.resetTimer();
  685. this.playAiSpeaking();
  686. setTimeout(() => {
  687. this.pauseAiSpeaking();
  688. }, 2e3);
  689. }
  690. }
  691. },
  692. // 添加生命周期钩子,确保在组件销毁时清除计时器
  693. beforeDestroy() {
  694. if (this.timerInterval) {
  695. clearInterval(this.timerInterval);
  696. }
  697. }
  698. };
  699. if (!Array) {
  700. const _component_uni_load_more = common_vendor.resolveComponent("uni-load-more");
  701. _component_uni_load_more();
  702. }
  703. function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  704. return common_vendor.e({
  705. a: !$data.digitalHumanUrl
  706. }, !$data.digitalHumanUrl ? {
  707. b: $data.mode,
  708. c: common_vendor.o((...args) => $options.handleCameraError && $options.handleCameraError(...args))
  709. } : $data.digitalHumanUrl ? {
  710. e: $data.digitalHumanUrl
  711. } : {
  712. f: common_assets._imports_0
  713. }, {
  714. d: $data.digitalHumanUrl,
  715. g: common_vendor.f($data.questions, (question, qIndex, i0) => {
  716. return common_vendor.e({
  717. a: common_vendor.t(qIndex + 1),
  718. b: common_vendor.t(question.questionTypeName),
  719. c: common_vendor.t(question.text),
  720. d: question.is_required_correct
  721. }, question.is_required_correct ? {} : {}, {
  722. e: question.questionType === 3 && question.imageUrl
  723. }, question.questionType === 3 && question.imageUrl ? {
  724. f: question.imageUrl
  725. } : {}, {
  726. g: question.questionType !== 0
  727. }, question.questionType !== 0 ? {
  728. h: common_vendor.f(question.options, (option, index, i1) => {
  729. return {
  730. a: common_vendor.t(option.option_text || (typeof option === "string" ? option : JSON.stringify(option))),
  731. b: index,
  732. c: $options.isOptionSelected(question, qIndex, index) ? 1 : "",
  733. d: common_vendor.o(($event) => $options.handleOptionClick(qIndex, index), index)
  734. };
  735. }),
  736. i: qIndex !== $data.currentQuestionIndex ? 1 : ""
  737. } : {}, {
  738. j: qIndex === $data.currentQuestionIndex && question.questionType === 2
  739. }, qIndex === $data.currentQuestionIndex && question.questionType === 2 ? {
  740. k: common_vendor.t("下一题"),
  741. l: common_vendor.o(($event) => $options.nextQuestion(), question.id),
  742. m: $data.selectedOptions.length === 0
  743. } : {}, {
  744. n: question.id,
  745. o: "question-" + qIndex
  746. });
  747. }),
  748. h: common_vendor.t($data.questions.length),
  749. i: $data.showContinueButton
  750. }, $data.showContinueButton ? {
  751. j: common_vendor.t($data.currentGroupIndex < $data.questionGroups.length - 1 ? "继续下一部分" : "完成测试"),
  752. k: common_vendor.o((...args) => $options.handleContinue && $options.handleContinue(...args))
  753. } : {}, {
  754. l: $data.currentScrollId,
  755. m: $data.scrollTop,
  756. n: $data.showEndModal
  757. }, $data.showEndModal ? {
  758. o: common_vendor.o((...args) => $options.navigateToInterview && $options.navigateToInterview(...args))
  759. } : {}, {
  760. p: $data.interviewCompleted
  761. }, $data.interviewCompleted ? {
  762. q: common_assets._imports_0,
  763. r: common_vendor.o((...args) => $options.back && $options.back(...args))
  764. } : {}, {
  765. s: $data.loading
  766. }, $data.loading ? {
  767. t: common_vendor.p({
  768. status: "loading",
  769. contentText: {
  770. contentdown: "加载中..."
  771. }
  772. })
  773. } : {}, {
  774. v: !$data.loading && $data.loadError
  775. }, !$data.loading && $data.loadError ? {
  776. w: common_vendor.o((...args) => $options.fetchInterviewData && $options.fetchInterviewData(...args))
  777. } : {}, {
  778. x: $data.showPromptModal
  779. }, $data.showPromptModal ? {
  780. y: common_vendor.o((...args) => $options.handlePromptConfirm && $options.handlePromptConfirm(...args))
  781. } : {});
  782. }
  783. const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
  784. wx.createPage(MiniProgramPage);