camera.js 26 KB

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