pcm-agents 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (293) hide show
  1. package/dist/cjs/{components-chat-message_32.cjs.entry.js → components-chat-message_33.cjs.entry.js} +9057 -10028
  2. package/dist/cjs/components-chat-message_33.cjs.entry.js.map +1 -0
  3. package/dist/cjs/components-oem-virtual-chat-modal.cjs.entry.js +1829 -0
  4. package/dist/cjs/components-oem-virtual-chat-modal.cjs.entry.js.map +1 -0
  5. package/dist/cjs/components-oem-virtual-chat-modal.entry.cjs.js.map +1 -0
  6. package/dist/cjs/config.store-BIjicJY9.js +262 -0
  7. package/dist/cjs/config.store-BIjicJY9.js.map +1 -0
  8. package/dist/cjs/{conversation-utils-Na0f0AoH.js → conversation-utils-srlCwYy5.js} +4 -4
  9. package/dist/cjs/{conversation-utils-Na0f0AoH.js.map → conversation-utils-srlCwYy5.js.map} +1 -1
  10. package/dist/cjs/{index-BPngQTFc.js → index-CJQRBB6e.js} +7 -261
  11. package/dist/cjs/index-CJQRBB6e.js.map +1 -0
  12. package/dist/cjs/index-C_qhED9Z.js +22 -26
  13. package/dist/cjs/{message-utils-DbTqMOA1.js → index-D5RimYc5.js} +2 -23
  14. package/dist/cjs/index-D5RimYc5.js.map +1 -0
  15. package/dist/cjs/index.cjs.js +2 -1
  16. package/dist/cjs/index.cjs.js.map +1 -1
  17. package/dist/cjs/loader.cjs.js +1 -1
  18. package/dist/cjs/message-utils-C8eJnzLy.js +26 -0
  19. package/dist/cjs/message-utils-C8eJnzLy.js.map +1 -0
  20. package/dist/cjs/oem-cast-modal.cjs.entry.js +427 -0
  21. package/dist/cjs/oem-cast-modal.cjs.entry.js.map +1 -0
  22. package/dist/cjs/oem-cast-modal.entry.cjs.js.map +1 -0
  23. package/dist/cjs/{pcm-htws-modal.cjs.entry.js → oem-htws-modal.cjs.entry.js} +138 -116
  24. package/dist/cjs/oem-htws-modal.cjs.entry.js.map +1 -0
  25. package/dist/cjs/oem-htws-modal.entry.cjs.js.map +1 -0
  26. package/dist/cjs/{pcm-jd-modal.cjs.entry.js → oem-jd-modal.cjs.entry.js} +83 -376
  27. package/dist/cjs/oem-jd-modal.cjs.entry.js.map +1 -0
  28. package/dist/cjs/oem-jd-modal.entry.cjs.js.map +1 -0
  29. package/dist/cjs/{pcm-zygh-modal.cjs.entry.js → oem-jlpp-modal.cjs.entry.js} +165 -101
  30. package/dist/cjs/oem-jlpp-modal.cjs.entry.js.map +1 -0
  31. package/dist/cjs/oem-jlpp-modal.entry.cjs.js.map +1 -0
  32. package/dist/cjs/{pcm-jlzz-modal.cjs.entry.js → oem-jlzz-modal.cjs.entry.js} +16 -203
  33. package/dist/cjs/oem-jlzz-modal.cjs.entry.js.map +1 -0
  34. package/dist/{pcm-agents/pcm-jlzz-modal.entry.esm.js.map → cjs/oem-jlzz-modal.entry.cjs.js.map} +1 -1
  35. package/dist/cjs/{pcm-jlpp-modal.cjs.entry.js → oem-mnct-modal.cjs.entry.js} +70 -97
  36. package/dist/cjs/oem-mnct-modal.cjs.entry.js.map +1 -0
  37. package/dist/cjs/oem-mnct-modal.entry.cjs.js.map +1 -0
  38. package/dist/cjs/{pcm-mnms-modal.cjs.entry.js → oem-mnms-modal.cjs.entry.js} +159 -43
  39. package/dist/cjs/oem-mnms-modal.cjs.entry.js.map +1 -0
  40. package/dist/cjs/oem-mnms-modal.entry.cjs.js.map +1 -0
  41. package/dist/cjs/{pcm-qgqjl-modal.cjs.entry.js → oem-qgqjl-modal.cjs.entry.js} +48 -18
  42. package/dist/cjs/oem-qgqjl-modal.cjs.entry.js.map +1 -0
  43. package/dist/cjs/oem-qgqjl-modal.entry.cjs.js.map +1 -0
  44. package/dist/cjs/pcm-agents.cjs.js +1 -1
  45. package/dist/cjs/pcm-hr-chat-modal.cjs.entry.js +4 -3
  46. package/dist/cjs/pcm-hr-chat-modal.cjs.entry.js.map +1 -1
  47. package/dist/cjs/pcm-hr-chat-modal.entry.cjs.js.map +1 -1
  48. package/dist/cjs/pcm-jlsx-modal.cjs.entry.js +27 -25
  49. package/dist/cjs/pcm-jlsx-modal.cjs.entry.js.map +1 -1
  50. package/dist/cjs/pcm-jlsx-modal.entry.cjs.js.map +1 -1
  51. package/dist/cjs/{sentry-reporter-B1jvgkp7.js → sentry-reporter-uulX7WVC.js} +5 -5
  52. package/dist/cjs/{sentry-reporter-B1jvgkp7.js.map → sentry-reporter-uulX7WVC.js.map} +1 -1
  53. package/dist/collection/components/oem-cast-modal/oem-cast-modal.css +6 -1
  54. package/dist/components/components-chat-message.js +1 -1
  55. package/dist/components/components-digital-human.js +1 -1
  56. package/dist/components/components-export-records-modal.js +1 -1
  57. package/dist/components/components-mobile-input-btn.js +1 -1
  58. package/dist/components/components-mobile-upload-btn.js +1 -1
  59. package/dist/components/components-oem-virtual-chat-modal.js +1 -1
  60. package/dist/components/components-position-selector.js +1 -1
  61. package/dist/components/components-thousand-resume-wrapper.js +1 -1
  62. package/dist/components/components-upload.js +1 -1
  63. package/dist/components/components-voice-input-btn.js +1 -1
  64. package/dist/components/index.js +1 -1
  65. package/dist/components/oem-cast-modal.js +1 -1
  66. package/dist/components/oem-cast-modal.js.map +1 -1
  67. package/dist/components/oem-htws-modal.js +5 -5
  68. package/dist/components/oem-jd-modal.js +5 -5
  69. package/dist/components/oem-jlpp-modal.js +7 -7
  70. package/dist/components/oem-jlsx-modal.js +5 -5
  71. package/dist/components/oem-jlzz-modal.js +5 -5
  72. package/dist/components/oem-mnct-modal.js +7 -7
  73. package/dist/components/oem-mnms-modal.js +8 -8
  74. package/dist/components/oem-qgqjl-modal.js +9 -9
  75. package/dist/components/oem-zygh-modal.js +5 -5
  76. package/dist/components/{p-D7D_3MQL.js → p--IeRQcxT.js} +3 -3
  77. package/dist/components/{p-D7D_3MQL.js.map → p--IeRQcxT.js.map} +1 -1
  78. package/dist/components/{p-fAgc1_6t.js → p-14CtQGo8.js} +4 -4
  79. package/dist/components/{p-fAgc1_6t.js.map → p-14CtQGo8.js.map} +1 -1
  80. package/dist/components/{p-li4R8x26.js → p-2BiWHTOV.js} +3 -3
  81. package/dist/components/{p-li4R8x26.js.map → p-2BiWHTOV.js.map} +1 -1
  82. package/dist/components/{p-C9XyS38z.js → p-5TyoLbXy.js} +5 -5
  83. package/dist/components/{p-C9XyS38z.js.map → p-5TyoLbXy.js.map} +1 -1
  84. package/dist/components/{p-Cc0Eazm9.js → p-BJKIlCmI.js} +3 -3
  85. package/dist/components/{p-Cc0Eazm9.js.map → p-BJKIlCmI.js.map} +1 -1
  86. package/dist/components/{p-DhrczBoo.js → p-BUA1GYVe.js} +3 -3
  87. package/dist/components/{p-DhrczBoo.js.map → p-BUA1GYVe.js.map} +1 -1
  88. package/dist/components/{p-0Ij65ybD.js → p-BUVNyxhI.js} +3 -3
  89. package/dist/components/{p-0Ij65ybD.js.map → p-BUVNyxhI.js.map} +1 -1
  90. package/dist/components/{p-CjhA27Iq.js → p-BWdXlms4.js} +3 -3
  91. package/dist/components/{p-CjhA27Iq.js.map → p-BWdXlms4.js.map} +1 -1
  92. package/dist/components/{p-B24U704n.js → p-BdgdgjaW.js} +3 -3
  93. package/dist/components/{p-B24U704n.js.map → p-BdgdgjaW.js.map} +1 -1
  94. package/dist/components/{p-C6gAMnwa.js → p-BdzFXB1t.js} +3 -3
  95. package/dist/components/{p-C6gAMnwa.js.map → p-BdzFXB1t.js.map} +1 -1
  96. package/dist/components/{p-BR37xNkD.js → p-Bxg4nW6-.js} +3 -3
  97. package/dist/components/{p-BR37xNkD.js.map → p-Bxg4nW6-.js.map} +1 -1
  98. package/dist/components/{p-BI0i2rY4.js → p-c8fMPeu_.js} +3 -3
  99. package/dist/components/{p-BI0i2rY4.js.map → p-c8fMPeu_.js.map} +1 -1
  100. package/dist/components/{p-B5DJYdYb.js → p-wAqiaji9.js} +3 -3
  101. package/dist/components/{p-B5DJYdYb.js.map → p-wAqiaji9.js.map} +1 -1
  102. package/dist/components/{p-CzbmmB8j.js → p-wTdMzyVB.js} +3 -3
  103. package/dist/components/{p-CzbmmB8j.js.map → p-wTdMzyVB.js.map} +1 -1
  104. package/dist/components/pcm-1zhanshi-mnms-modal.js +5 -5
  105. package/dist/components/pcm-app-chat-modal.js +1 -1
  106. package/dist/components/pcm-ats-mnms-modal.js +5 -5
  107. package/dist/components/pcm-card.js +1 -1
  108. package/dist/components/pcm-hr-chat-modal.js +2 -2
  109. package/dist/components/pcm-htws-modal.js +8 -8
  110. package/dist/components/pcm-hyzj-modal.js +5 -5
  111. package/dist/components/pcm-jd-modal.js +6 -6
  112. package/dist/components/pcm-jlpp-modal.js +8 -8
  113. package/dist/components/pcm-jlsx-modal.js +5 -5
  114. package/dist/components/pcm-jlzz-modal.js +6 -6
  115. package/dist/components/pcm-mnct-modal.js +7 -7
  116. package/dist/components/pcm-mnms-modal.js +8 -8
  117. package/dist/components/pcm-mnms-zp-modal.js +8 -8
  118. package/dist/components/pcm-msbg-modal.js +5 -5
  119. package/dist/components/pcm-qgqjl-modal.js +9 -9
  120. package/dist/components/pcm-virtual-chat-modal.js +1 -1
  121. package/dist/components/pcm-zsk-chat-modal.js +2 -2
  122. package/dist/components/pcm-zygh-modal.js +7 -7
  123. package/dist/components/pcm-zygh-v2-modal.js +7 -7
  124. package/dist/esm/{components-chat-message_32.entry.js → components-chat-message_33.entry.js} +9957 -10929
  125. package/dist/esm/components-chat-message_33.entry.js.map +1 -0
  126. package/dist/esm/components-oem-virtual-chat-modal.entry.js +1827 -0
  127. package/dist/esm/components-oem-virtual-chat-modal.entry.js.map +1 -0
  128. package/dist/esm/config.store-C--BQ-cn.js +259 -0
  129. package/dist/esm/config.store-C--BQ-cn.js.map +1 -0
  130. package/dist/esm/{conversation-utils-BSUtuMqH.js → conversation-utils-B8KFe8e5.js} +4 -4
  131. package/dist/esm/{conversation-utils-BSUtuMqH.js.map → conversation-utils-B8KFe8e5.js.map} +1 -1
  132. package/dist/esm/{message-utils-BVjB8bxU.js → index-B_xjnWkF.js} +3 -23
  133. package/dist/esm/index-B_xjnWkF.js.map +1 -0
  134. package/dist/esm/{index-Bq0K-WqZ.js → index-DA8Myhft.js} +25 -29
  135. package/dist/esm/{index-Bq0K-WqZ.js.map → index-DA8Myhft.js.map} +1 -1
  136. package/dist/esm/{index-ffawiHP3.js → index-mY4LYmpx.js} +7 -260
  137. package/dist/esm/index-mY4LYmpx.js.map +1 -0
  138. package/dist/esm/index.js +3 -2
  139. package/dist/esm/index.js.map +1 -1
  140. package/dist/esm/loader.js +3 -3
  141. package/dist/esm/message-utils-DnoxSHNP.js +24 -0
  142. package/dist/esm/message-utils-DnoxSHNP.js.map +1 -0
  143. package/dist/esm/oem-cast-modal.entry.js +425 -0
  144. package/dist/esm/oem-cast-modal.entry.js.map +1 -0
  145. package/dist/esm/{pcm-htws-modal.entry.js → oem-htws-modal.entry.js} +138 -116
  146. package/dist/esm/oem-htws-modal.entry.js.map +1 -0
  147. package/dist/esm/{pcm-jd-modal.entry.js → oem-jd-modal.entry.js} +83 -376
  148. package/dist/esm/oem-jd-modal.entry.js.map +1 -0
  149. package/dist/esm/{pcm-zygh-modal.entry.js → oem-jlpp-modal.entry.js} +165 -101
  150. package/dist/esm/oem-jlpp-modal.entry.js.map +1 -0
  151. package/dist/esm/{pcm-jlzz-modal.entry.js → oem-jlzz-modal.entry.js} +16 -203
  152. package/dist/esm/{pcm-jlzz-modal.entry.js.map → oem-jlzz-modal.entry.js.map} +1 -1
  153. package/dist/esm/{pcm-jlpp-modal.entry.js → oem-mnct-modal.entry.js} +70 -97
  154. package/dist/esm/oem-mnct-modal.entry.js.map +1 -0
  155. package/dist/esm/{pcm-mnms-modal.entry.js → oem-mnms-modal.entry.js} +159 -43
  156. package/dist/esm/oem-mnms-modal.entry.js.map +1 -0
  157. package/dist/esm/{pcm-qgqjl-modal.entry.js → oem-qgqjl-modal.entry.js} +48 -18
  158. package/dist/esm/oem-qgqjl-modal.entry.js.map +1 -0
  159. package/dist/esm/pcm-agents.js +3 -3
  160. package/dist/esm/pcm-hr-chat-modal.entry.js +4 -3
  161. package/dist/esm/pcm-hr-chat-modal.entry.js.map +1 -1
  162. package/dist/esm/pcm-jlsx-modal.entry.js +6 -4
  163. package/dist/esm/pcm-jlsx-modal.entry.js.map +1 -1
  164. package/dist/esm/{sentry-reporter-q_g26pa7.js → sentry-reporter-st9SkiOU.js} +3 -3
  165. package/dist/esm/{sentry-reporter-q_g26pa7.js.map → sentry-reporter-st9SkiOU.js.map} +1 -1
  166. package/dist/pcm-agents/components-oem-virtual-chat-modal.entry.esm.js.map +1 -0
  167. package/dist/pcm-agents/index.esm.js +1 -1
  168. package/dist/pcm-agents/index.esm.js.map +1 -1
  169. package/dist/pcm-agents/oem-cast-modal.entry.esm.js.map +1 -0
  170. package/dist/pcm-agents/oem-htws-modal.entry.esm.js.map +1 -0
  171. package/dist/pcm-agents/oem-jd-modal.entry.esm.js.map +1 -0
  172. package/dist/pcm-agents/oem-jlpp-modal.entry.esm.js.map +1 -0
  173. package/dist/{cjs/pcm-jlzz-modal.entry.cjs.js.map → pcm-agents/oem-jlzz-modal.entry.esm.js.map} +1 -1
  174. package/dist/pcm-agents/oem-mnct-modal.entry.esm.js.map +1 -0
  175. package/dist/pcm-agents/oem-mnms-modal.entry.esm.js.map +1 -0
  176. package/dist/pcm-agents/oem-qgqjl-modal.entry.esm.js.map +1 -0
  177. package/dist/pcm-agents/p-0cedc9bf.entry.js +2 -0
  178. package/dist/pcm-agents/{p-b778f4cc.entry.js.map → p-0cedc9bf.entry.js.map} +1 -1
  179. package/dist/pcm-agents/p-24ce19fc.entry.js +2 -0
  180. package/dist/pcm-agents/p-24ce19fc.entry.js.map +1 -0
  181. package/dist/pcm-agents/p-30b6a88d.entry.js +2 -0
  182. package/dist/pcm-agents/p-30b6a88d.entry.js.map +1 -0
  183. package/dist/pcm-agents/p-557664fc.entry.js +2 -0
  184. package/dist/pcm-agents/{p-340ef557.entry.js.map → p-557664fc.entry.js.map} +1 -1
  185. package/dist/pcm-agents/p-58493403.entry.js +2 -0
  186. package/dist/pcm-agents/p-58493403.entry.js.map +1 -0
  187. package/dist/pcm-agents/p-5ca45877.entry.js +2 -0
  188. package/dist/pcm-agents/p-5ca45877.entry.js.map +1 -0
  189. package/dist/pcm-agents/p-724d9471.entry.js +2 -0
  190. package/dist/pcm-agents/p-724d9471.entry.js.map +1 -0
  191. package/dist/pcm-agents/p-783b46c0.entry.js +2 -0
  192. package/dist/pcm-agents/{p-be4fdac7.entry.js.map → p-783b46c0.entry.js.map} +1 -1
  193. package/dist/pcm-agents/p-81d12737.entry.js +2 -0
  194. package/dist/pcm-agents/p-81d12737.entry.js.map +1 -0
  195. package/dist/pcm-agents/{p-BrEqs5mi.js → p-B23366TU.js} +2 -2
  196. package/dist/pcm-agents/{p-BrEqs5mi.js.map → p-B23366TU.js.map} +1 -1
  197. package/dist/pcm-agents/{p-BVjB8bxU.js → p-B_xjnWkF.js} +2 -2
  198. package/dist/pcm-agents/p-B_xjnWkF.js.map +1 -0
  199. package/dist/pcm-agents/p-C7m_4Gmk.js +2 -0
  200. package/dist/pcm-agents/p-C7m_4Gmk.js.map +1 -0
  201. package/dist/pcm-agents/p-CcIrA6cr.js +2 -0
  202. package/dist/pcm-agents/p-CcIrA6cr.js.map +1 -0
  203. package/dist/pcm-agents/{p-Bq0K-WqZ.js → p-DA8Myhft.js} +2 -2
  204. package/dist/pcm-agents/{p-Bq0K-WqZ.js.map → p-DA8Myhft.js.map} +1 -1
  205. package/dist/pcm-agents/p-DnoxSHNP.js +2 -0
  206. package/dist/pcm-agents/p-DnoxSHNP.js.map +1 -0
  207. package/dist/pcm-agents/p-b9368b8e.entry.js +8 -0
  208. package/dist/pcm-agents/p-b9368b8e.entry.js.map +1 -0
  209. package/dist/pcm-agents/p-d0a7e02b.entry.js +2 -0
  210. package/dist/{cjs/pcm-qgqjl-modal.cjs.entry.js.map → pcm-agents/p-d0a7e02b.entry.js.map} +1 -1
  211. package/dist/pcm-agents/p-f4d5841a.entry.js +2 -0
  212. package/dist/pcm-agents/p-f4d5841a.entry.js.map +1 -0
  213. package/dist/pcm-agents/p-rR0ghCa0.js +2 -0
  214. package/dist/pcm-agents/{p-Oknq-jtc.js.map → p-rR0ghCa0.js.map} +1 -1
  215. package/dist/pcm-agents/pcm-agents.esm.js +1 -1
  216. package/dist/pcm-agents/pcm-hr-chat-modal.entry.esm.js.map +1 -1
  217. package/dist/pcm-agents/pcm-jlsx-modal.entry.esm.js.map +1 -1
  218. package/package.json +1 -1
  219. package/dist/cjs/components-chat-message_32.cjs.entry.js.map +0 -1
  220. package/dist/cjs/index-BPngQTFc.js.map +0 -1
  221. package/dist/cjs/message-utils-DbTqMOA1.js.map +0 -1
  222. package/dist/cjs/pcm-ats-mnms-modal.cjs.entry.js +0 -238
  223. package/dist/cjs/pcm-ats-mnms-modal.cjs.entry.js.map +0 -1
  224. package/dist/cjs/pcm-ats-mnms-modal.entry.cjs.js.map +0 -1
  225. package/dist/cjs/pcm-htws-modal.cjs.entry.js.map +0 -1
  226. package/dist/cjs/pcm-htws-modal.entry.cjs.js.map +0 -1
  227. package/dist/cjs/pcm-jd-modal.cjs.entry.js.map +0 -1
  228. package/dist/cjs/pcm-jd-modal.entry.cjs.js.map +0 -1
  229. package/dist/cjs/pcm-jlpp-modal.cjs.entry.js.map +0 -1
  230. package/dist/cjs/pcm-jlpp-modal.entry.cjs.js.map +0 -1
  231. package/dist/cjs/pcm-jlzz-modal.cjs.entry.js.map +0 -1
  232. package/dist/cjs/pcm-mnct-modal.cjs.entry.js +0 -457
  233. package/dist/cjs/pcm-mnct-modal.cjs.entry.js.map +0 -1
  234. package/dist/cjs/pcm-mnct-modal.entry.cjs.js.map +0 -1
  235. package/dist/cjs/pcm-mnms-modal.cjs.entry.js.map +0 -1
  236. package/dist/cjs/pcm-mnms-modal.entry.cjs.js.map +0 -1
  237. package/dist/cjs/pcm-qgqjl-modal.entry.cjs.js.map +0 -1
  238. package/dist/cjs/pcm-zygh-modal.cjs.entry.js.map +0 -1
  239. package/dist/cjs/pcm-zygh-modal.entry.cjs.js.map +0 -1
  240. package/dist/cjs/pcm-zygh-v2-modal.cjs.entry.js +0 -332
  241. package/dist/cjs/pcm-zygh-v2-modal.cjs.entry.js.map +0 -1
  242. package/dist/cjs/pcm-zygh-v2-modal.entry.cjs.js.map +0 -1
  243. package/dist/esm/components-chat-message_32.entry.js.map +0 -1
  244. package/dist/esm/index-ffawiHP3.js.map +0 -1
  245. package/dist/esm/message-utils-BVjB8bxU.js.map +0 -1
  246. package/dist/esm/pcm-ats-mnms-modal.entry.js +0 -236
  247. package/dist/esm/pcm-ats-mnms-modal.entry.js.map +0 -1
  248. package/dist/esm/pcm-htws-modal.entry.js.map +0 -1
  249. package/dist/esm/pcm-jd-modal.entry.js.map +0 -1
  250. package/dist/esm/pcm-jlpp-modal.entry.js.map +0 -1
  251. package/dist/esm/pcm-mnct-modal.entry.js +0 -455
  252. package/dist/esm/pcm-mnct-modal.entry.js.map +0 -1
  253. package/dist/esm/pcm-mnms-modal.entry.js.map +0 -1
  254. package/dist/esm/pcm-qgqjl-modal.entry.js.map +0 -1
  255. package/dist/esm/pcm-zygh-modal.entry.js.map +0 -1
  256. package/dist/esm/pcm-zygh-v2-modal.entry.js +0 -330
  257. package/dist/esm/pcm-zygh-v2-modal.entry.js.map +0 -1
  258. package/dist/pcm-agents/p-06359d3b.entry.js +0 -2
  259. package/dist/pcm-agents/p-06359d3b.entry.js.map +0 -1
  260. package/dist/pcm-agents/p-340ef557.entry.js +0 -2
  261. package/dist/pcm-agents/p-4facfdd8.entry.js +0 -2
  262. package/dist/pcm-agents/p-4facfdd8.entry.js.map +0 -1
  263. package/dist/pcm-agents/p-60c7f37f.entry.js +0 -2
  264. package/dist/pcm-agents/p-60c7f37f.entry.js.map +0 -1
  265. package/dist/pcm-agents/p-7e68b5b1.entry.js +0 -8
  266. package/dist/pcm-agents/p-7e68b5b1.entry.js.map +0 -1
  267. package/dist/pcm-agents/p-BCHVxmVd.js +0 -2
  268. package/dist/pcm-agents/p-BCHVxmVd.js.map +0 -1
  269. package/dist/pcm-agents/p-BVjB8bxU.js.map +0 -1
  270. package/dist/pcm-agents/p-Oknq-jtc.js +0 -2
  271. package/dist/pcm-agents/p-ae371752.entry.js +0 -2
  272. package/dist/pcm-agents/p-ae371752.entry.js.map +0 -1
  273. package/dist/pcm-agents/p-af179f1a.entry.js +0 -2
  274. package/dist/pcm-agents/p-af179f1a.entry.js.map +0 -1
  275. package/dist/pcm-agents/p-b778f4cc.entry.js +0 -2
  276. package/dist/pcm-agents/p-bbb30b82.entry.js +0 -2
  277. package/dist/pcm-agents/p-bbb30b82.entry.js.map +0 -1
  278. package/dist/pcm-agents/p-be4fdac7.entry.js +0 -2
  279. package/dist/pcm-agents/p-c053e88e.entry.js +0 -2
  280. package/dist/pcm-agents/p-c053e88e.entry.js.map +0 -1
  281. package/dist/pcm-agents/p-d2905eef.entry.js +0 -2
  282. package/dist/pcm-agents/p-d2905eef.entry.js.map +0 -1
  283. package/dist/pcm-agents/p-eadc6905.entry.js +0 -2
  284. package/dist/pcm-agents/p-eadc6905.entry.js.map +0 -1
  285. package/dist/pcm-agents/pcm-ats-mnms-modal.entry.esm.js.map +0 -1
  286. package/dist/pcm-agents/pcm-htws-modal.entry.esm.js.map +0 -1
  287. package/dist/pcm-agents/pcm-jd-modal.entry.esm.js.map +0 -1
  288. package/dist/pcm-agents/pcm-jlpp-modal.entry.esm.js.map +0 -1
  289. package/dist/pcm-agents/pcm-mnct-modal.entry.esm.js.map +0 -1
  290. package/dist/pcm-agents/pcm-mnms-modal.entry.esm.js.map +0 -1
  291. package/dist/pcm-agents/pcm-qgqjl-modal.entry.esm.js.map +0 -1
  292. package/dist/pcm-agents/pcm-zygh-modal.entry.esm.js.map +0 -1
  293. package/dist/pcm-agents/pcm-zygh-v2-modal.entry.esm.js.map +0 -1
@@ -0,0 +1,1829 @@
1
+ 'use strict';
2
+
3
+ var index = require('./index-C_qhED9Z.js');
4
+ var index$1 = require('./index-CJQRBB6e.js');
5
+ var messageUtils = require('./message-utils-C8eJnzLy.js');
6
+ var errorEvent = require('./error-event-J_ZWIqWF.js');
7
+ var config_store = require('./config.store-BIjicJY9.js');
8
+ var sentryReporter = require('./sentry-reporter-uulX7WVC.js');
9
+ require('./exports-Dc-pQh4A.js');
10
+
11
+ const componentsOemVirtualChatModalCss = ":host{display:block;font-size:16px}.modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background-color:rgba(0, 0, 0, 0.5);display:flex;justify-content:center;align-items:center;overflow:hidden;padding:0;z-index:1000}.fullscreen-overlay{padding:0;position:absolute;width:100%;height:100%}.modal-container{background:#fff;border-radius:0;width:100%;height:100%;display:flex;flex-direction:column;position:relative;margin:0;overflow:hidden}.modal-container.fullscreen{width:100%;max-width:none;height:100%;border-radius:0;margin:0;display:flex;flex-direction:column;max-height:100%}.digital-human-background{position:absolute;top:0;left:0;right:0;bottom:0;z-index:1;overflow:hidden;display:flex;justify-content:center}.digital-human-background-video{width:100%;height:100%;object-fit:cover;object-position:center;max-width:1200px}.modal-container:not(.digital-human-mode){background:white;border-radius:8px;width:100%;max-width:900px;height:90vh;max-height:800px}.modal-content-layer{position:relative;z-index:10;height:100%;display:flex;flex-direction:column;background:linear-gradient(\r\n 180deg,\r\n rgba(0, 0, 0, 0.3) 0%,\r\n rgba(0, 0, 0, 0.1) 20%,\r\n rgba(0, 0, 0, 0.1) 80%,\r\n rgba(0, 0, 0, 0.4) 100%\r\n )}.main-content{display:flex;flex-direction:column;align-items:center;overflow:hidden;padding:24px 24px 40px 24px;position:absolute;bottom:0px;width:-webkit-fill-available}.chat-history-section{width:100%;max-width:1240px;text-align:center;color:white}.ai-message-item{padding:12px 20px;border-radius:12px;margin-bottom:12px;animation:fadeIn 0.5s ease-in-out}.recording-preview-top-right{position:absolute;top:20px;right:20px;width:180px;height:120px;border-radius:12px;overflow:hidden;border:2px solid rgba(255, 255, 255, 0.5);box-shadow:0 4px 12px rgba(0, 0, 0, 0.3);background-color:#000;z-index:10}.user-video-preview{width:100%;height:100%;object-fit:cover}.recording-status-bar{display:flex;align-items:center;justify-content:space-between;background-color:white;padding:8px 12px;border-radius:999px;margin-top:4px;width:100%;max-width:400px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.1);animation:slideInUp 0.5s ease-out;line-height:40px}.audio-waveform{display:flex;align-items:center;height:24px}.waveform-bar{width:4px;margin:0 2px;background-color:#16a34a;border-radius:2px;height:6px;animation:waveform-animation 1.2s infinite ease-in-out}.waveform-bar:nth-child(1){animation-delay:0.1s}.waveform-bar:nth-child(2){animation-delay:0.3s}.waveform-bar:nth-child(3){animation-delay:0.5s}.waveform-bar:nth-child(4){animation-delay:0.2s}.waveform-bar:nth-child(5){animation-delay:0.4s}.waveform-bar:nth-child(6){animation-delay:0.1s}.waveform-bar:nth-child(7){animation-delay:0.3s}.waveform-bar:nth-child(8){animation-delay:0.5s}@keyframes waveform-animation{0%,100%{height:6px}50%{height:35px}}.recording-timer{font-size:14px;color:#595959;font-family:'Courier New', Courier, monospace}.finish-recording-btn{background-color:#16a34a;border:none;border-radius:50%;width:32px;height:32px;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);transition:background-color 0.3s,\r\n transform 0.2s}.finish-recording-btn:hover{background-color:#148a3f}.finish-recording-btn:active{transform:scale(0.95)}@keyframes fadeIn{from{opacity:0}to{opacity:1}}@keyframes slideInUp{from{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.status-indicator-text{display:flex;align-items:center;gap:8px;font-size:14px;color:#595959;width:100%;justify-content:center}.status-indicator-text.loading .loading-spinner-small{width:16px;height:16px;border:2px solid rgba(0, 0, 0, 0.1);border-top-color:#595959;border-radius:50%;animation:spin 0.8s linear infinite}.status-indicator-text.error{color:#ff4d4f}.status-indicator-text.ready{color:#16a34a}@keyframes spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}.loading-spinner-small{width:20px;height:20px;border:2px solid #e5e7eb;border-top-color:#6b7280;border-radius:50%;animation:spin 0.8s linear infinite}.upload-button{background:transparent;border:none;cursor:pointer;padding:8px;display:flex;align-items:center;justify-content:center;color:#666;border-radius:4px;transition:background-color 0.2s}.upload-button:hover{background-color:rgba(0, 0, 0, 0.04)}.upload-button svg{width:20px;height:20px}.file-input{display:none}.selected-file{font-size:12px;color:#666;margin-left:8px;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.input-wrapper{flex:1;display:flex;align-items:center;border:1px solid #ddd;border-radius:4px;padding:0 4px;background:white}.input-wrapper input{border:none;flex:1;padding:8px;outline:none}.input-wrapper:focus-within{border-color:#bbb}.file-preview{padding:8px 16px;border-top:1px solid #eee;background-color:#f9f9f9}.recording-section{border-top:1px solid #eee;display:flex;flex-direction:column;align-items:center;padding:10px 20px 0px 20px;border-radius:14px 14px 0 0;flex:0 0 auto}.recording-section .video-preview{width:100%;height:200px;max-width:400px;position:relative;margin-bottom:10px;border:1px solid #ddd;border-radius:12px;overflow:hidden}.recording-section video{width:100%;height:100%;object-fit:cover}.recording-status{position:absolute;top:10px;left:10px;background-color:rgba(0, 0, 0, 0.6);color:white;padding:4px 8px;border-radius:4px;display:flex;align-items:center;gap:5px;font-size:14px;z-index:0}.recording-status .recording-dot{display:inline-block;width:10px;height:10px;background-color:red;border-radius:50%;margin-right:5px;animation:blink 1s infinite}.recording-status.warning{color:#ff4d4f;animation:blink 1s infinite}@keyframes blink{0%{opacity:1}50%{opacity:0.5}100%{opacity:1}}.recording-section .stop-recording-button{background-color:#f44336;color:white;border:none;cursor:pointer;font-weight:bold}.recording-section .stop-recording-button:hover{background-color:#d32f2f}.fullscreen{width:100vw;border-radius:0;height:100vh;display:flex;flex-direction:column;overflow-y:auto}.recording-controls{margin-top:10px;height:53px;width:100%;max-width:400px;display:flex;justify-content:center}.recording-controls .waiting-message{text-align:center;color:white;font-size:16px;background-color:#0d75fb;border-radius:6px;width:95%;display:flex;justify-content:center;align-items:center;cursor:pointer}.recording-controls .waiting-message.loading{background:#faad14}.recording-controls .waiting-message p{margin:0;font-size:16px;color:white;font-weight:500}.recording-controls .stop-recording-button{background-color:#dc3545;color:white;border:none;cursor:pointer;font-size:16px}.recording-controls .stop-recording-button:hover{background-color:#c82333}.recording-controls .stop-recording-button.disabled{background:#ccc;cursor:not-allowed}.recording-controls .stop-recording-button.disabled:hover{background:#ccc}.progress-container{display:flex;justify-content:space-between;align-items:center;width:100%;max-width:400px;margin-top:10px;padding:0 5px}.progress-bar-container{height:4px;background-color:#e5e5e5;border-radius:2px;overflow:hidden;margin-right:10px;width:75px}.progress-bar{height:100%;background-image:linear-gradient(111deg, #4a9fff 0%, #1058ff 100%);border-radius:2px;transition:width 0.3s ease}.progress-text{font-size:14px;color:#666;white-space:nowrap}.text-input-area{display:flex;flex-direction:column;width:100%;height:100%;padding:0px 16px 16px 16px;border-radius:8px;border:none;}.text-answer-input{flex:1;min-height:80px;padding:12px 12px 0px 12px;border:1px solid #ddd;border-radius:8px 8px 0 0;resize:none;font-size:16px;background-color:#fff;border-bottom:none;outline:none;}.input-toolbar{display:flex;justify-content:flex-end;align-items:center;padding:8px 12px;background-color:#fff;border:1px solid #ddd;border-top:none;border-radius:0 0 8px 8px}.text-answer-input:focus{border-color:rgb(74, 144, 226);border-bottom:none}.text-answer-input:focus+.input-toolbar{border-color:rgb(74, 144, 226);border-top:none}.toolbar-actions{width:32px;height:32px;display:flex;justify-content:center;align-items:center;margin-right:10px;border:1px solid #d9d9d9;border-radius:6px}.toolbar-actions:hover{background-color:#f0f0f0}.toolbar-button{background:transparent;border:none;color:#666;cursor:pointer;padding:0;margin:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center}.toolbar-button>div,.toolbar-button>svg{display:flex;justify-content:center;align-items:center}.submit-text-button{padding:6px 16px;background-color:#4a90e2;color:white;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:background-color 0.2s}.submit-text-button:hover:not(.disabled){background-color:#3a7bc8}.submit-text-button.disabled{background-color:#b3b3b3;cursor:not-allowed}.toolbar-button.recording{background-color:rgba(255, 0, 0, 0.1);color:#ff3b30;animation:pulse 1.5s infinite}.toolbar-button.converting{background-color:rgba(0, 122, 255, 0.1);color:#007aff}.toolbar-button .recording-time{font-size:12px;margin-left:4px}.converting-indicator{display:flex;justify-content:center;align-items:center}.converting-indicator svg{animation:spin 1.5s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(255, 0, 0, 0.4)}70%{box-shadow:0 0 0 6px rgba(255, 0, 0, 0)}100%{box-shadow:0 0 0 0 rgba(255, 0, 0, 0)}}@media screen and (max-width: 768px){.pc-layout{width:95%}.modal-overlay{padding:0}.modal-container.fullscreen{width:100%;height:100%;max-height:100%;border-radius:16px 16px 0 0;padding:env(safe-area-inset-top) 0 env(safe-area-inset-bottom)}}.workspace-section{width:100%;padding:0px 16px 16px}.workspace-toolbar{display:flex;justify-content:space-between;align-items:end;gap:12px;position:relative}.workspace-button{display:flex;align-items:center;gap:6px;padding:8px 16px;border:1px solid #d9d9d9;border-radius:6px;background:white;color:#666;font-size:14px;cursor:pointer;transition:all 0.2s ease;box-shadow:0 1px 2px rgba(0, 0, 0, 0.05)}.workspace-button:hover{border-color:#4096ff;color:#4096ff;box-shadow:0 2px 4px rgba(64, 150, 255, 0.1)}.workspace-button svg{flex-shrink:0}.digital-human-wrapper{display:flex;align-items:center;justify-content:center;width:120px;height:80px;border:1px solid #d9d9d9;border-radius:8px;background:#fafafa;overflow:hidden;position:absolute;right:0px}.history-drawer-content{height:100%;display:flex;flex-direction:column}.conversation-list{flex:1;overflow-y:auto;padding:8px 0}.loading-conversations,.empty-conversations{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:40px 20px;color:#999}.loading-conversations .loading-spinner-small{margin-bottom:12px}.conversation-item{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;margin:0 8px 4px 8px;border-radius:8px;cursor:pointer;transition:all 0.2s ease;border:1px solid transparent}.conversation-item:hover{background:#f5f5f5}.conversation-item.active{background:#e6f7ff;border-color:#1890ff}.conversation-info{flex:1;min-width:0}.conversation-title{font-size:14px;font-weight:500;color:#262626;margin-bottom:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.conversation-meta{display:flex;align-items:center;gap:8px;font-size:12px;color:#8c8c8c}.conversation-time{flex-shrink:0}.message-count{flex-shrink:0}.conversation-status{padding:2px 6px;border-radius:4px;font-size:11px;font-weight:500}.conversation-status.completed{background:#f6ffed;color:#52c41a}.conversation-status.running{background:#fff7e6;color:#fa8c16}.current-indicator{flex-shrink:0;color:#1890ff;margin-left:8px}.ai-message-item{box-sizing:border-box;margin:0 auto;animation:messageSlideIn 0.3s ease-out;width:100%;padding:40px}.ai-message-item.streaming{position:relative}@keyframes messageSlideIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.ai-message-content{box-sizing:border-box;color:#fff;padding:40px 48px;border-radius:32px;font-family:\"Microsoft YaHei\", \"PingFang SC\", sans-serif;font-size:32px;font-weight:500;line-height:1.6;word-wrap:break-word;backdrop-filter:blur(100px);border:1px solid rgba(255, 255, 255, 0.2);box-shadow:0 4px 20px rgba(0, 0, 0, 0.2);margin-bottom:16px;max-width:100%;text-align:left}.typing-indicator{display:flex;align-items:center;gap:8px;padding:16px 48px;margin-top:-16px}.typing-indicator span{width:16px;height:16px;border-radius:50%;background:#4facfe;animation:typingPulse 1.4s ease-in-out infinite;opacity:0.6}.typing-indicator span:nth-child(1){animation-delay:0s}.typing-indicator span:nth-child(2){animation-delay:0.2s}.typing-indicator span:nth-child(3){animation-delay:0.4s}@keyframes typingPulse{0%,60%,100%{transform:scale(1);opacity:0.4}30%{transform:scale(1.3);opacity:1}}@media screen and (max-width: 768px){.ai-message-item{padding:30px}.ai-message-content{padding:32px 40px;font-size:30px}.typing-indicator{padding:12px 40px}}@media screen and (max-width: 480px){.ai-message-item{padding:0}.ai-message-content{padding:28px 36px;font-size:28px;line-height:1.5}.typing-indicator{padding:10px 36px}.typing-indicator span{width:12px;height:12px}}.waveform-bar{width:3px;background:#4facfe;border-radius:2px;min-height:2px;animation:waveformAnimation 1.2s ease-in-out infinite alternate;transform-origin:bottom}@keyframes waveformAnimation{0%{transform:scaleY(0.2)}100%{transform:scaleY(1)}}.recording-timer{font-size:14px;color:#666;white-space:nowrap}.close-button{position:absolute;top:16px;left:16px;z-index:20;background:rgba(255, 255, 255, 0.9);backdrop-filter:blur(4px);border:1px solid rgba(0, 0, 0, 0.1);border-radius:8px;width:40px;height:40px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all 0.2s ease;box-shadow:0 2px 8px rgba(0, 0, 0, 0.1)}.close-button:hover{background:rgba(255, 255, 255, 1);border-color:rgba(0, 0, 0, 0.15);box-shadow:0 4px 12px rgba(0, 0, 0, 0.15);transform:translateY(-1px)}.close-button:active{transform:translateY(0);box-shadow:0 2px 6px rgba(0, 0, 0, 0.1)}.close-button svg{color:#666;transition:color 0.2s ease}.close-button:hover svg{color:#333}.end-interview-button-container{display:flex;align-items:center;justify-content:flex-start;width:100%;max-width:400px;margin-bottom:4px;padding-left:0}.end-interview-button{padding:5px 10px;background-color:#ff4d4f;color:white;border:none;border-radius:20px;font-size:14px;font-weight:500;cursor:pointer;transition:all 0.3s ease;box-shadow:0 2px 8px rgba(255, 77, 79, 0.3);white-space:nowrap}.end-interview-button:hover{background-color:#ff7875;box-shadow:0 4px 12px rgba(255, 77, 79, 0.4);transform:translateY(-1px)}.end-interview-button:active{transform:translateY(0);box-shadow:0 2px 8px rgba(255, 77, 79, 0.3)}.end-interview-button:disabled{background-color:#f0f0f0;color:#bfbfbf;cursor:not-allowed;box-shadow:none;transform:none}";
12
+
13
+ const hostCss = ":host{font-size:16px;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;font-weight:normal;font-style:normal;font-variant:normal;line-height:1.5;letter-spacing:normal;word-spacing:normal;color:#333;text-align:left;text-indent:0;text-transform:none;white-space:normal;word-wrap:normal;word-break:normal;visibility:visible;list-style:none;list-style-image:none;list-style-position:outside;list-style-type:disc;border-collapse:separate;border-spacing:0;caption-side:top;empty-cells:show;table-layout:auto;quotes:auto;cursor:auto;direction:ltr;orphans:2;widows:2;page-break-inside:auto;text-decoration:none;text-shadow:none;box-sizing:border-box;writing-mode:horizontal-tb;text-orientation:mixed}";
14
+
15
+ const ComponentsOemVirtualChatModal = class {
16
+ constructor(hostRef) {
17
+ index.registerInstance(this, hostRef);
18
+ this.streamComplete = index.createEvent(this, "streamComplete");
19
+ this.conversationStart = index.createEvent(this, "conversationStart");
20
+ this.interviewComplete = index.createEvent(this, "interviewComplete");
21
+ this.recordingError = index.createEvent(this, "recordingError");
22
+ this.recordingStatusChange = index.createEvent(this, "recordingStatusChange");
23
+ this.tokenInvalid = index.createEvent(this, "tokenInvalid");
24
+ this.modalClosed = index.createEvent(this, "modalClosed");
25
+ }
26
+ /**
27
+ * SDK鉴权密钥
28
+ */
29
+ token;
30
+ /**
31
+ * 是否显示聊天模态框
32
+ */
33
+ isOpen = false;
34
+ /**
35
+ * 聊天消息历史
36
+ */
37
+ messages = [];
38
+ /**
39
+ * 聊天框的页面层级
40
+ */
41
+ zIndex;
42
+ /**
43
+ * 会话ID,传入继续对话,否则创建新会话
44
+ */
45
+ conversationId;
46
+ /**
47
+ * 是否正在加载回复
48
+ */
49
+ isLoading = false;
50
+ /**
51
+ * 当前正在流式输出的消息
52
+ */
53
+ currentStreamingMessage = null;
54
+ isLoadingHistory = false;
55
+ get hostElement() { return index.getElement(this); }
56
+ /**
57
+ * 一轮对话结束时的回调
58
+ */
59
+ streamComplete;
60
+ /**
61
+ * 新会话开始的回调,只会在一轮对话开始时触发一次
62
+ */
63
+ conversationStart;
64
+ /**
65
+ * 默认发送文本
66
+ */
67
+ defaultQuery = '你好!聘才猫';
68
+ /**
69
+ * 视频录制最大时长(秒)
70
+ */
71
+ maxRecordingTime = 120;
72
+ /**
73
+ * 录制倒计时提醒时间(秒)
74
+ * 当剩余时间小于此值时,显示倒计时警告
75
+ */
76
+ countdownWarningTime = 30;
77
+ // 添加视频录制相关状态
78
+ isRecording = false;
79
+ recordingStream = null;
80
+ recordedBlob = null;
81
+ mediaRecorder = null;
82
+ recordingTimeLeft = 0;
83
+ showRecordingUI = false;
84
+ recordingTimer = null;
85
+ recordingStartTime = 0;
86
+ waitingToRecord = false;
87
+ waitingTimer = null;
88
+ _visibilityChangeHandler = null;
89
+ waitingTimeLeft = 10; // 等待时间(秒)
90
+ // 添加一个新的私有属性来存储视频元素的引用
91
+ videoRef = null;
92
+ /**
93
+ * 当前轮数
94
+ */
95
+ currentQuestionNumber = 0;
96
+ /**
97
+ * 当聊天完成时触发
98
+ */
99
+ interviewComplete;
100
+ showCountdownWarning = false;
101
+ /**
102
+ * 是否以全屏模式打开,移动端建议设置为true
103
+ */
104
+ fullscreen = false;
105
+ // 添加新的状态来跟踪视频上传
106
+ isUploadingVideo = false;
107
+ /**
108
+ * 数字人视频相关状态
109
+ */
110
+ digitalHumanVideoUrl = '';
111
+ digitalHumanDefaultVideoUrl = '';
112
+ virtualmanKey = '';
113
+ isPlayingDigitalHumanVideo = false;
114
+ digitalHumanVideoReady = false;
115
+ digitalHumanOpeningContents = [];
116
+ isPlayingWelcomeVideo = false;
117
+ isGeneratingDigitalHumanVideo = false;
118
+ /**
119
+ * 待播放的生成视频URL(当开场白视频播放时,暂存生成的视频URL)
120
+ */
121
+ pendingGeneratedVideoUrl = '';
122
+ // 视频预加载缓存管理
123
+ preloadedVideos = new Set();
124
+ preloadingVideos = new Map();
125
+ /**
126
+ * 是否正在等待数字人视频播放完成
127
+ */
128
+ waitingForDigitalHuman = false;
129
+ /**
130
+ * 虚拟数字人ID,指定则开启虚拟数字人功能
131
+ */
132
+ digitalId;
133
+ /**
134
+ * 数字人开场白索引,用于选择开场白和开场视频(可选:0, 1, 2)
135
+ * 0、您好,我是聘才猫 AI 面试助手。很高兴为你主持这场面试!在开始前,请确保:身处安静、光线充足的环境。网络顺畅,摄像头和麦克风工作正常。现在我正在查看本次面试的相关信息,为您生成专属面试题,马上就好,请稍等片刻。</br>
136
+ * 1、您好,我是您的 AI 面试助手。欢迎参加本次AI面试!为了获得最佳效果,请确认:您在安静、明亮的环境中。您的网络稳定,摄像头和麦克风已开启。我们正在后台为您准备本次专属面试内容,很快开始,请稍候。<br>
137
+ * 2、您好,我是您的 AI 面试助手。面试马上开始。趁此片刻,请快速确认:周围安静吗?光线足够吗?网络没问题?摄像头和麦克风准备好了吗?我们正在为您加载个性化的面试环节,稍等就好!
138
+ */
139
+ openingIndex = 0;
140
+ /**
141
+ * 等待录制时间(秒)默认10
142
+ */
143
+ waitingTime = 10;
144
+ /**
145
+ * 数字人视频播放速率(0.5-2.0之间),默认1.0
146
+ */
147
+ playbackRate = 1.0;
148
+ /**
149
+ * 是否显示结束面试按钮
150
+ */
151
+ showEndInterviewButton = false;
152
+ /**
153
+ * 录制错误事件
154
+ */
155
+ recordingError;
156
+ /**
157
+ * 录制状态变化事件
158
+ */
159
+ recordingStatusChange;
160
+ /**
161
+ * SDK密钥验证失败事件
162
+ */
163
+ tokenInvalid;
164
+ /**
165
+ * 当点击模态框关闭时触发
166
+ */
167
+ modalClosed;
168
+ /**
169
+ * 自定义智能体inputs输入参数
170
+ */
171
+ customInputs = {};
172
+ /**
173
+ * 机器人ID
174
+ */
175
+ botId;
176
+ /**
177
+ * 智能体头像URL(从后端获取)
178
+ */
179
+ agentLogo = '';
180
+ // 添加新的状态属性来跟踪任务是否已完成
181
+ isTaskCompleted = false;
182
+ tokenInvalidListener;
183
+ deviceError = null;
184
+ /**
185
+ * 面试流程状态管理说明:
186
+ *
187
+ * 核心状态:
188
+ * - isTaskCompleted: 任务是否已完成(面试正常结束)
189
+ * - isEndingInterview: 是否正在执行结束面试流程(用户主动结束)
190
+ * - isRecording: 是否正在录制视频
191
+ * - waitingToRecord: 是否正在等待开始录制
192
+ * - waitingForDigitalHuman: 是否正在等待数字人视频播放完成
193
+ * - isLoading: 是否正在等待AI回复
194
+ * - isUploadingVideo: 是否正在上传视频
195
+ * - isGeneratingDigitalHumanVideo: 是否正在生成数字人视频
196
+ *
197
+ * 流程控制原则:
198
+ * 1. 一旦 isTaskCompleted 或 isEndingInterview 为 true,所有录制和等待流程都应停止
199
+ * 2. isEndingInterview 设置为 true 后,只允许发送结束面试消息
200
+ * 3. 结束面试按钮在 isTaskCompleted 或 isEndingInterview 为 true 时置灰禁用
201
+ * 4. 所有可能触发新录制的地方都需要检查这两个状态
202
+ */
203
+ // 添加二次确认相关状态
204
+ showConfirmModal = false;
205
+ skipConfirmThisInterview = false;
206
+ // 添加关闭确认相关状态
207
+ showCloseConfirmModal = false;
208
+ // 添加结束面试确认相关状态
209
+ showEndInterviewConfirmModal = false;
210
+ /**
211
+ * 标记是否正在执行结束面试流程
212
+ */
213
+ isEndingInterview = false;
214
+ // 添加内部状态标志,用于控制异步操作的执行
215
+ isComponentActive = true;
216
+ handleTokenChange(newToken) {
217
+ // 当传入的 token 变化时,更新 authStore 中的 token
218
+ if (newToken && newToken !== index$1.authStore.getToken()) {
219
+ index$1.authStore.setToken(newToken);
220
+ }
221
+ }
222
+ /**
223
+ * 公共方法:设置组件活动状态
224
+ * 可被外部调用来控制组件是否继续执行异步操作
225
+ */
226
+ async setComponentActive(active) {
227
+
228
+ this.isComponentActive = active;
229
+ }
230
+ componentWillLoad() {
231
+ // 将 zIndex 存入配置缓存
232
+ if (this.zIndex) {
233
+ config_store.configStore.setItem('modal-zIndex', this.zIndex);
234
+ }
235
+ else {
236
+ // 如果没有提供 zIndex,尝试从缓存中读取
237
+ const cachedZIndex = config_store.configStore.getItem('modal-zIndex');
238
+ if (cachedZIndex) {
239
+ this.zIndex = cachedZIndex;
240
+ }
241
+ else {
242
+ this.zIndex = 1000;
243
+ config_store.configStore.setItem('modal-zIndex', 1000);
244
+ }
245
+ }
246
+ if (this.token) {
247
+ index$1.authStore.setToken(this.token);
248
+ }
249
+ // 添加全局token无效事件监听器
250
+ this.tokenInvalidListener = () => {
251
+ this.tokenInvalid.emit();
252
+ };
253
+ document.addEventListener('pcm-token-invalid', this.tokenInvalidListener);
254
+ // 监听页面可见性变化,处理关屏/切后台回来后流程卡住的问题
255
+ this._visibilityChangeHandler = () => {
256
+ if (document.visibilityState === 'visible') {
257
+ this.handlePageResume();
258
+ }
259
+ };
260
+ document.addEventListener('visibilitychange', this._visibilityChangeHandler);
261
+ // 确保 customInputs 是一个对象
262
+ if (!this.customInputs) {
263
+ this.customInputs = {};
264
+ }
265
+ // 如果没有设置助手头像,尝试获取智能体头像
266
+ if (this.botId) {
267
+ this.fetchAgentLogo();
268
+ }
269
+ // 添加数字人初始化日志
270
+ console.log('componentWillLoad - 数字人相关状态:', {
271
+ digitalId: this.digitalId,
272
+ isOpen: this.isOpen,
273
+ conversationId: this.conversationId,
274
+ virtualmanKey: this.virtualmanKey,
275
+ });
276
+ // 如果组件加载时已经是打开状态,则直接开始对话
277
+ if (this.isOpen) {
278
+ if (this.conversationId) {
279
+ // 在下一个事件循环中加载历史消息,避免在componentWillLoad中进行异步操作
280
+ setTimeout(() => this.loadHistoryMessages(), 0);
281
+ }
282
+ else {
283
+ // 初始化数字人功能
284
+ if (this.digitalId) {
285
+
286
+ this.initializeDigitalHuman();
287
+ }
288
+ // 在下一个事件循环中发送初始消息,避免在componentWillLoad中进行异步操作
289
+ setTimeout(() => {
290
+ this.sendMessageToAPI(this.defaultQuery);
291
+ }, 0);
292
+ }
293
+ }
294
+ }
295
+ // 添加获取智能体信息的方法
296
+ async fetchAgentLogo() {
297
+ if (!this.botId)
298
+ return;
299
+ try {
300
+ const agentInfo = await index$1.fetchAgentInfo(this.botId);
301
+ if (agentInfo && agentInfo.logo) {
302
+ this.agentLogo = agentInfo.logo;
303
+ }
304
+ }
305
+ catch (error) {
306
+ sentryReporter.SentryReporter.captureError(error, {
307
+ action: 'fetchAgentLogo',
308
+ component: 'components-oem-virtual-chat-modal',
309
+ title: '获取智能体信息失败',
310
+ });
311
+ errorEvent.ErrorEventBus.emitError({
312
+ error: error,
313
+ message: '获取智能体信息失败',
314
+ });
315
+ }
316
+ }
317
+ async sendMessageToAPI(message, videoUrl) {
318
+ // 如果任务已完成或正在结束面试,不允许发送消息
319
+ if (this.isTaskCompleted && !this.isEndingInterview) {
320
+
321
+ return;
322
+ }
323
+ // 发送新消息时重置状态
324
+ this.waitingForDigitalHuman = false;
325
+ this.digitalHumanVideoReady = false;
326
+ this.isLoading = true;
327
+ let answer = '';
328
+ let llmText = '';
329
+ const now = new Date();
330
+ const time = `${now.getHours()}:${now.getMinutes().toString().padStart(2, '0')}`;
331
+ // 修改消息处理逻辑,移除文件上传相关代码
332
+ const queryText = message.trim() || '请开始';
333
+ // 如果有视频URL,添加到customInputs中
334
+ if (videoUrl) {
335
+ this.customInputs = {
336
+ ...this.customInputs,
337
+ video_url: videoUrl,
338
+ };
339
+ }
340
+ // 如果没有设置url_callback,则使用默认值
341
+ if (!this.customInputs.url_callback) {
342
+ this.customInputs = {
343
+ ...this.customInputs,
344
+ url_callback: 'https://tagents.ylzhaopin.com/agents/api/test/callback',
345
+ };
346
+ }
347
+ // 创建新的消息对象
348
+ const newMessage = {
349
+ id: `temp-${Date.now()}`, // 临时ID,将被服务器返回的ID替换
350
+ conversation_id: this.conversationId, // 会话ID
351
+ inputs: this.customInputs || {}, // 输入参数
352
+ query: queryText, // 用户输入的消息内容
353
+ answer: '', // 初始为空
354
+ message_files: [], // 消息附件
355
+ feedback: {}, // 反馈
356
+ retriever_resources: [], // 检索资源
357
+ created_at: Math.floor(Date.now() / 1000).toString(), // 创建时间
358
+ agent_thoughts: [], // 代理思考过程
359
+ status: 'normal', // 消息状态
360
+ error: null, // 错误信息
361
+ // 添加组件内部使用的字段
362
+ time: time, // 消息时间(显示用)
363
+ isStreaming: true, // 是否正在流式输出
364
+ };
365
+ // 设置当前流式消息
366
+ this.currentStreamingMessage = newMessage;
367
+ // 准备请求数据
368
+ const requestData = {
369
+ response_mode: 'streaming',
370
+ conversation_id: this.conversationId,
371
+ query: queryText,
372
+ bot_id: this.botId, // 添加 botId 到请求数据中
373
+ };
374
+ // 合并基本输入参数和自定义输入参数
375
+ requestData.inputs = {
376
+ // 合并自定义输入参数
377
+ ...this.customInputs,
378
+ };
379
+ await index$1.sendSSERequest({
380
+ url: '/sdk/v1/chat/chat-messages',
381
+ method: 'POST',
382
+ data: requestData,
383
+ onMessage: data => {
384
+
385
+
386
+ if (data.conversation_id && !this.conversationId) {
387
+ this.conversationId = data.conversation_id;
388
+ this.conversationStart.emit({
389
+ conversation_id: data.conversation_id,
390
+ event: data.event,
391
+ message_id: data.message_id,
392
+ id: data.id,
393
+ });
394
+ }
395
+ // 检查是否有 node_finished 事件和 LLMText
396
+ if (data.event === 'node_finished' && data.data.inputs && data.data.inputs.LLMText) {
397
+ llmText = data.data.inputs.LLMText;
398
+ }
399
+ // 添加对任务结束的判断
400
+ if (data.event === 'node_finished' && data.data.title && data.data.title.includes('聘才猫任务结束')) {
401
+
402
+ // 设置标志,表示任务已结束
403
+ this.isTaskCompleted = true;
404
+ // 获取当前AI回复内容,优先使用LLMText,否则使用answer
405
+ const aiResponse = llmText || answer || this.currentStreamingMessage?.answer || '';
406
+ // 触发面试完成事件
407
+ this.interviewComplete.emit({
408
+ conversation_id: this.conversationId,
409
+ current_question_number: this.currentQuestionNumber,
410
+ ai_response: aiResponse, // 添加AI回复内容
411
+ });
412
+ }
413
+ if (data.event === 'node_finished' && data.data.title && data.data.title.includes('聘才猫数字人题目')) {
414
+ const digital_human_list = JSON.parse(data.data.process_data.digital_human_list);
415
+
416
+ this.precreateDigitalHumanVideos(digital_human_list);
417
+ }
418
+ if (data.event === 'error') {
419
+ const errorMessage = data.data?.message || data.message || '请求失败';
420
+ this.messages = [
421
+ ...this.messages,
422
+ {
423
+ ...newMessage,
424
+ answer: errorMessage,
425
+ error: data.data,
426
+ isStreaming: false,
427
+ },
428
+ ];
429
+ this.currentStreamingMessage = null;
430
+ this.isLoading = false;
431
+ errorEvent.ErrorEventBus.emitError({
432
+ error: data.data,
433
+ message: errorMessage,
434
+ });
435
+ return;
436
+ }
437
+ if (data.event === 'message') {
438
+ if (data.event === 'agent_message' || data.event === 'message') {
439
+ if (data.answer) {
440
+ answer += data.answer;
441
+ // 更新消息时保持完整的消息结构
442
+ const updatedMessage = {
443
+ ...this.currentStreamingMessage,
444
+ answer,
445
+ id: data.message_id || this.currentStreamingMessage.id,
446
+ isStreaming: true,
447
+ // 如果服务器返回了其他字段,也更新它们
448
+ retriever_resources: data.retriever_resources || this.currentStreamingMessage.retriever_resources,
449
+ agent_thoughts: data.agent_thoughts || this.currentStreamingMessage.agent_thoughts,
450
+ };
451
+ this.currentStreamingMessage = updatedMessage;
452
+ }
453
+ }
454
+ }
455
+ if (data.event === 'message_end') {
456
+ this.streamComplete.emit({
457
+ conversation_id: data.conversation_id || '',
458
+ event: data.event,
459
+ message_id: data.message_id,
460
+ id: data.id,
461
+ });
462
+ }
463
+ },
464
+ onError: error => {
465
+ console.error('发生错误:', error);
466
+ errorEvent.ErrorEventBus.emitError({
467
+ error: error,
468
+ message: '消息发送失败,请稍后再试',
469
+ });
470
+ sentryReporter.SentryReporter.captureError(error, {
471
+ action: 'sendMessageToAPI',
472
+ component: 'components-oem-virtual-chat-modal',
473
+ title: '消息发送失败',
474
+ });
475
+ this.messages = [
476
+ ...this.messages,
477
+ {
478
+ ...newMessage,
479
+ answer: '消息发送失败,请检查网络。',
480
+ error: error,
481
+ isStreaming: false,
482
+ },
483
+ ];
484
+ this.currentStreamingMessage = null;
485
+ this.isLoading = false;
486
+ },
487
+ onComplete: async () => {
488
+ this.isLoading = false;
489
+ // 根据currentQuestionNumber判断是否清空customInputs
490
+ if (this.currentQuestionNumber === 0) {
491
+ // 发送第一条消息后,清空指定的 customInputs 字段
492
+ setTimeout(() => {
493
+ if (this.customInputs) {
494
+ // 只清除指定的字段,其他字段保留
495
+ delete this.customInputs.job_info;
496
+ delete this.customInputs.file_url;
497
+ delete this.customInputs.file_name;
498
+ }
499
+ }, 1000); // 给一些时间让第一条消息处理完成
500
+ }
501
+ // 获取最新的AI回复内容
502
+ const latestAIMessage = this.currentStreamingMessage;
503
+ latestAIMessage.isStreaming = false;
504
+ if (this.digitalId && latestAIMessage && latestAIMessage.answer) {
505
+ // 如果开启了数字人功能,生成数字人视频
506
+
507
+ this.generateDigitalHumanVideo(latestAIMessage.answer);
508
+ }
509
+ this.currentStreamingMessage = null;
510
+ // 更新消息列表
511
+ this.messages = [...this.messages, latestAIMessage];
512
+ // 增加计数
513
+ this.currentQuestionNumber++;
514
+ // 如果任务已完成或正在结束面试,不执行后续流程
515
+ if (this.isTaskCompleted || this.isEndingInterview) {
516
+
517
+ return;
518
+ }
519
+ // 如果开启了数字人,等待数字人视频播放完成
520
+ if (this.digitalId) {
521
+ this.waitingForDigitalHuman = true;
522
+
523
+ }
524
+ },
525
+ });
526
+ }
527
+ // 修改加载历史消息的方法
528
+ async loadHistoryMessages() {
529
+ if (!this.conversationId)
530
+ return;
531
+ this.isLoadingHistory = true;
532
+
533
+ let conversationStatus = false;
534
+ this.digitalHumanVideoReady = true;
535
+ try {
536
+ // 首先获取会话状态
537
+ const conversationStatusResponse = await index$1.sendHttpRequest({
538
+ url: `/sdk/v1/chat/conversation`,
539
+ method: 'GET',
540
+ data: {
541
+ conversation_id: this.conversationId,
542
+ },
543
+ });
544
+ // 处理会话状态
545
+ if (conversationStatusResponse.success && conversationStatusResponse.data && conversationStatusResponse.data.run_status == '结束') {
546
+ conversationStatus = true;
547
+ }
548
+ // 获取历史消息
549
+ const result = await index$1.sendHttpRequest({
550
+ url: '/sdk/v1/chat/messages',
551
+ method: 'GET',
552
+ data: {
553
+ conversation_id: this.conversationId,
554
+ bot_id: this.botId,
555
+ limit: 20,
556
+ },
557
+ });
558
+ if (result.success && result.data) {
559
+ const historyData = result.data.data || [];
560
+ const formattedMessages = historyData.map(msg => {
561
+ const time = new Date(parseInt(msg.created_at) * 1000);
562
+ const hours = time.getHours().toString().padStart(2, '0');
563
+ const minutes = time.getMinutes().toString().padStart(2, '0');
564
+ const timeStr = `${hours}:${minutes}`;
565
+ // 创建完整的消息对象,保持统一结构
566
+ return {
567
+ id: msg.id,
568
+ conversation_id: msg.conversation_id,
569
+ inputs: msg.inputs || {},
570
+ query: msg.query || '',
571
+ answer: msg.answer || '',
572
+ message_files: msg.message_files || [],
573
+ feedback: msg.feedback || {},
574
+ retriever_resources: msg.retriever_resources || [],
575
+ created_at: msg.created_at,
576
+ agent_thoughts: msg.agent_thoughts || [],
577
+ status: msg.status || 'normal',
578
+ error: msg.error,
579
+ // 组件内部使用的字段
580
+ time: timeStr,
581
+ isStreaming: false,
582
+ };
583
+ });
584
+ this.messages = formattedMessages;
585
+ // 根据历史消息数量设置currentQuestionNumber
586
+ this.currentQuestionNumber = formattedMessages.length || 0;
587
+ }
588
+ }
589
+ catch (error) {
590
+ console.error('加载历史消息或会话状态失败:', error);
591
+ sentryReporter.SentryReporter.captureError(error, {
592
+ action: 'loadHistoryMessages',
593
+ component: 'components-oem-virtual-chat-modal',
594
+ title: '加载历史消息失败',
595
+ });
596
+ errorEvent.ErrorEventBus.emitError({
597
+ error: error,
598
+ message: '加载历史消息失败,请刷新重试',
599
+ });
600
+ }
601
+ finally {
602
+ this.isLoadingHistory = false;
603
+ if (this.conversationId && this.messages.length > 0 && !conversationStatus && !this.isEndingInterview) {
604
+ // 不播放欢迎视频
605
+ this.isPlayingDigitalHumanVideo = false;
606
+ this.isPlayingWelcomeVideo = false;
607
+ this.digitalHumanVideoReady = true;
608
+ // 先初始化数字人,等待获取到默认视频URL后再设置
609
+ this.initializeDigitalHuman().then(() => {
610
+ // 确保在初始化完成后设置视频URL
611
+ if (this.digitalHumanDefaultVideoUrl) {
612
+ this.digitalHumanVideoUrl = this.digitalHumanDefaultVideoUrl;
613
+ }
614
+ });
615
+ setTimeout(() => {
616
+ // 开始等待录制
617
+ this.startWaitingToRecord();
618
+ }, 200);
619
+ }
620
+ else if (conversationStatus) {
621
+ // 如果会话已结束,设置任务完成状态
622
+ this.isTaskCompleted = true;
623
+ // 确保数字人视频状态正确设置
624
+ if (this.digitalId) {
625
+ this.isPlayingDigitalHumanVideo = false;
626
+ this.isPlayingWelcomeVideo = false;
627
+ this.digitalHumanVideoReady = true;
628
+ // 先初始化数字人,等待获取到默认视频URL后再设置
629
+ this.initializeDigitalHuman().then(() => {
630
+ // 确保在初始化完成后设置视频URL
631
+ if (this.digitalHumanDefaultVideoUrl) {
632
+ this.digitalHumanVideoUrl = this.digitalHumanDefaultVideoUrl;
633
+ }
634
+ });
635
+ }
636
+ }
637
+ }
638
+ }
639
+ // 开始等待录制
640
+ startWaitingToRecord() {
641
+ // 如果任务已完成或正在结束面试,不开始录制流程
642
+ if (this.isTaskCompleted || this.isEndingInterview) {
643
+
644
+ return;
645
+ }
646
+ // 清除可能存在的计时器
647
+ if (this.waitingTimer) {
648
+ clearInterval(this.waitingTimer);
649
+ }
650
+ if (this.recordingTimer) {
651
+ clearInterval(this.recordingTimer);
652
+ }
653
+ this.waitingToRecord = true;
654
+ this.waitingTimeLeft = this.waitingTime;
655
+ this.waitingTimer = setInterval(() => {
656
+ this.waitingTimeLeft--;
657
+ if (this.waitingTimeLeft <= 0) {
658
+ clearInterval(this.waitingTimer);
659
+ this.waitingTimer = null;
660
+ this.waitingToRecord = false;
661
+ this.startRecording();
662
+ }
663
+ }, 1000);
664
+ }
665
+ // 开始录制视频
666
+ async startRecording() {
667
+ // 如果任务已完成或正在结束面试,不开始录制
668
+ if (this.isTaskCompleted || this.isEndingInterview) {
669
+
670
+ return;
671
+ }
672
+ try {
673
+ const stream = await navigator.mediaDevices.getUserMedia({
674
+ audio: true,
675
+ video: {
676
+ width: { ideal: 1280 },
677
+ height: { ideal: 720 },
678
+ frameRate: { ideal: 30 },
679
+ },
680
+ });
681
+ // 如果成功获取到媒体流,清除之前的设备错误
682
+ this.deviceError = null;
683
+ this.recordingStream = stream;
684
+ this.showRecordingUI = true;
685
+ this.showCountdownWarning = false;
686
+ // 重置视频引用
687
+ this.videoRef = null;
688
+ // 确保视频元素获取到流
689
+ this.setupVideoPreview(stream);
690
+ // 检测浏览器支持的MIME类型
691
+ const mimeType = index$1.getSupportedMimeType();
692
+ // 创建MediaRecorder实例
693
+ let mediaRecorder;
694
+ try {
695
+ mediaRecorder = new MediaRecorder(stream, {
696
+ mimeType: mimeType,
697
+ videoBitsPerSecond: 800000, // 800kbps 视频比特率限制
698
+ audioBitsPerSecond: 64000, // 64kbps 音频比特率限制
699
+ });
700
+ }
701
+ catch (e) {
702
+ // 如果指定MIME类型失败,尝试使用默认设置
703
+ console.warn('指定的MIME类型不受支持,使用默认设置:', e);
704
+ try {
705
+ mediaRecorder = new MediaRecorder(stream, {
706
+ videoBitsPerSecond: 800000, // 即使降级也保持比特率限制
707
+ audioBitsPerSecond: 64000,
708
+ });
709
+ }
710
+ catch (recorderError) {
711
+ // 最后的降级选项,不设置任何参数
712
+ try {
713
+ mediaRecorder = new MediaRecorder(stream);
714
+ }
715
+ catch (finalError) {
716
+ // 通知父组件录制器创建失败
717
+ this.recordingError.emit({
718
+ type: 'recorder_creation_failed',
719
+ message: '无法创建媒体录制器,您的浏览器可能不支持此功能',
720
+ details: finalError,
721
+ });
722
+ this.showRecordingUI = false;
723
+ return;
724
+ }
725
+ }
726
+ }
727
+ this.mediaRecorder = mediaRecorder;
728
+ const chunks = [];
729
+ mediaRecorder.ondataavailable = event => {
730
+ if (event.data.size > 0) {
731
+ chunks.push(event.data);
732
+ }
733
+ };
734
+ mediaRecorder.onerror = event => {
735
+ // 通知父组件录制过程中发生错误
736
+ this.recordingError.emit({
737
+ type: 'recording_error',
738
+ message: '录制过程中发生错误',
739
+ details: event,
740
+ });
741
+ this.stopRecording();
742
+ };
743
+ mediaRecorder.onstop = () => {
744
+ try {
745
+ // 根据实际使用的MIME类型创建Blob
746
+ const blobType = mimeType || 'video/mp4';
747
+ const blob = new Blob(chunks, { type: blobType });
748
+ if (blob.size === 0) {
749
+ // 通知父组件录制的视频为空
750
+ this.recordingError.emit({
751
+ type: 'empty_recording',
752
+ message: '录制的视频为空',
753
+ });
754
+ this.showRecordingUI = false;
755
+ return;
756
+ }
757
+ this.recordedBlob = blob;
758
+ // 通知父组件录制已完成
759
+ this.recordingStatusChange.emit({
760
+ status: 'stopped',
761
+ details: {
762
+ duration: Math.floor((Date.now() - this.recordingStartTime) / 1000),
763
+ size: blob.size,
764
+ type: blob.type,
765
+ },
766
+ });
767
+ this.uploadRecordedVideo();
768
+ }
769
+ catch (error) {
770
+ // 通知父组件处理录制视频时出错
771
+ this.recordingError.emit({
772
+ type: 'processing_error',
773
+ message: '处理录制视频时出错',
774
+ details: error,
775
+ });
776
+ this.showRecordingUI = false;
777
+ sentryReporter.SentryReporter.captureError(error, {
778
+ action: 'uploadRecordedVideo',
779
+ component: 'pcm-hr-chat-modal',
780
+ title: '处理录制视频时出错',
781
+ });
782
+ }
783
+ };
784
+ // 开始录制
785
+ try {
786
+ mediaRecorder.start();
787
+ this.isRecording = true;
788
+ this.recordingStartTime = Date.now();
789
+ this.recordingTimeLeft = this.maxRecordingTime;
790
+ // 通知父组件录制已开始
791
+ this.recordingStatusChange.emit({
792
+ status: 'started',
793
+ details: {
794
+ maxDuration: this.maxRecordingTime,
795
+ mimeType: mediaRecorder.mimeType,
796
+ },
797
+ });
798
+ }
799
+ catch (startError) {
800
+ // 通知父组件开始录制失败
801
+ this.recordingError.emit({
802
+ type: 'start_failed',
803
+ message: '开始录制失败,请检查您的设备权限',
804
+ details: startError,
805
+ });
806
+ this.showRecordingUI = false;
807
+ sentryReporter.SentryReporter.captureError(startError, {
808
+ action: 'startRecording',
809
+ component: 'pcm-hr-chat-modal',
810
+ title: '开始录制失败',
811
+ });
812
+ return;
813
+ }
814
+ // 设置录制计时器
815
+ this.recordingTimer = setInterval(() => {
816
+ const elapsedTime = Math.floor((Date.now() - this.recordingStartTime) / 1000);
817
+ this.recordingTimeLeft = Math.max(0, this.maxRecordingTime - elapsedTime);
818
+ // 检查是否需要显示倒计时警告
819
+ if (this.recordingTimeLeft <= this.countdownWarningTime && !this.showCountdownWarning) {
820
+ this.showCountdownWarning = true;
821
+ }
822
+ // 时间到自动停止录制
823
+ if (this.recordingTimeLeft <= 0) {
824
+ this.stopRecording();
825
+ }
826
+ }, 1000);
827
+ }
828
+ catch (error) {
829
+ console.error('无法访问摄像头或麦克风:', error);
830
+ // 设置设备错误状态
831
+ let errorMessage = '设备访问失败';
832
+ if (error.name === 'NotAllowedError') {
833
+ errorMessage = '请允许访问摄像头和麦克风权限';
834
+ }
835
+ else if (error.name === 'NotFoundError') {
836
+ errorMessage = '未检测到摄像头或麦克风设备';
837
+ }
838
+ else if (error.name === 'NotReadableError') {
839
+ errorMessage = '您的浏览器不支持视频录制,请切换到微信或Chrome、Edge使用';
840
+ }
841
+ else if (error.name === 'OverconstrainedError') {
842
+ errorMessage = '摄像头不支持指定的配置';
843
+ }
844
+ else {
845
+ errorMessage = '无法访问摄像头或麦克风,请检查设备连接';
846
+ }
847
+ this.deviceError = errorMessage;
848
+ this.showRecordingUI = false;
849
+ errorEvent.ErrorEventBus.emitError({
850
+ error: error,
851
+ message: errorMessage,
852
+ });
853
+ sentryReporter.SentryReporter.captureError(error, {
854
+ action: 'startRecording',
855
+ component: 'components-oem-virtual-chat-modal',
856
+ title: errorMessage,
857
+ });
858
+ }
859
+ }
860
+ // 添加新方法来设置视频预览
861
+ setupVideoPreview(stream) {
862
+ // 延迟执行以确保DOM已更新
863
+ setTimeout(() => {
864
+ const videoElement = this.hostElement.shadowRoot?.querySelector('.user-video-preview');
865
+ if (videoElement && stream) {
866
+ // 先尝试使用标准方法
867
+ try {
868
+ videoElement.srcObject = stream;
869
+ videoElement.play().catch(err => {
870
+ console.error('视频播放失败:', err);
871
+ sentryReporter.SentryReporter.captureError(err, {
872
+ action: 'setupVideoPreview',
873
+ component: 'components-oem-virtual-chat-modal',
874
+ title: '视频播放失败',
875
+ });
876
+ errorEvent.ErrorEventBus.emitError({
877
+ error: err,
878
+ message: '视频播放失败',
879
+ });
880
+ });
881
+ }
882
+ catch (e) {
883
+ console.warn('设置srcObject失败,尝试替代方法:', e);
884
+ // 对于不支持srcObject的旧浏览器,使用URL.createObjectURL
885
+ try {
886
+ // 使用类型断言解决TypeScript错误
887
+ const objectUrl = URL.createObjectURL(stream);
888
+ videoElement.src = objectUrl;
889
+ // 确保在视频元素不再使用时释放URL
890
+ videoElement.onended = () => {
891
+ URL.revokeObjectURL(objectUrl);
892
+ };
893
+ }
894
+ catch (urlError) {
895
+ console.error('创建对象URL失败:', urlError);
896
+ sentryReporter.SentryReporter.captureError(urlError, {
897
+ action: 'setupVideoPreview',
898
+ component: 'components-oem-virtual-chat-modal',
899
+ title: '创建对象URL失败',
900
+ });
901
+ errorEvent.ErrorEventBus.emitError({
902
+ error: urlError,
903
+ message: '创建对象URL失败',
904
+ });
905
+ }
906
+ }
907
+ }
908
+ else {
909
+ console.warn('未找到视频元素或媒体流无效');
910
+ }
911
+ }, 100);
912
+ }
913
+ // 停止录制
914
+ stopRecording() {
915
+ if (this.mediaRecorder && this.isRecording) {
916
+ this.mediaRecorder.stop();
917
+ this.isRecording = false;
918
+ // 清理计时器
919
+ if (this.recordingTimer) {
920
+ clearInterval(this.recordingTimer);
921
+ this.recordingTimer = null;
922
+ }
923
+ // 停止并释放媒体流
924
+ if (this.recordingStream) {
925
+ this.recordingStream.getTracks().forEach(track => track.stop());
926
+ this.recordingStream = null;
927
+ }
928
+ // 清理视频引用
929
+ this.videoRef = null;
930
+ }
931
+ // 清理待播放的视频URL(即使没有正在录制,也需要清理待播放状态)
932
+ this.pendingGeneratedVideoUrl = '';
933
+ }
934
+ // 处理停止录制按钮点击
935
+ handleStopRecording = () => {
936
+ // 如果已经设置了跳过确认,直接停止录制
937
+ if (this.skipConfirmThisInterview) {
938
+ this.stopRecording();
939
+ return;
940
+ }
941
+ // 否则显示确认模态框
942
+ this.showConfirmModal = true;
943
+ };
944
+ // 处理"本次面试不再提醒"复选框变化
945
+ handleSkipConfirmChange = (event) => {
946
+ const checkbox = event.target;
947
+ this.skipConfirmThisInterview = checkbox.checked;
948
+ };
949
+ // 处理确认模态框的取消事件
950
+ handleConfirmModalCancel = () => {
951
+ this.showConfirmModal = false;
952
+ };
953
+ // 处理确认模态框的OK事件
954
+ handleConfirmModalOk = () => {
955
+ this.showConfirmModal = false;
956
+ this.stopRecording();
957
+ };
958
+ // 处理确认模态框的Cancel事件
959
+ handleConfirmModalCancelEvent = () => {
960
+ this.showConfirmModal = false;
961
+ };
962
+ // 上传录制的视频
963
+ async uploadRecordedVideo() {
964
+ if (!this.recordedBlob)
965
+ return;
966
+ try {
967
+ this.isUploadingVideo = true; // 开始上传时设置状态
968
+ this.showRecordingUI = false; // 隐藏视频预览
969
+ // 根据Blob类型确定文件扩展名
970
+ const fileExtension = this.recordedBlob.type.includes('webm') ? 'webm' : 'mp4';
971
+ const fileName = `answer.${fileExtension}`;
972
+ // 创建File对象
973
+ const file = new File([this.recordedBlob], fileName, { type: this.recordedBlob.type });
974
+ // 使用uploadFileToBackend上传文件
975
+ const fileInfo = await index$1.uploadFileToBackend(file, {}, {
976
+ tags: ['other'],
977
+ });
978
+ // 使用 cos_key 作为视频标识符
979
+ // 调用音频转文字API
980
+ const transcriptionText = await index$1.convertAudioToText(fileInfo.cos_key);
981
+ // 检查是否是结束面试流程
982
+ if (this.isEndingInterview) {
983
+
984
+ // 如果是结束面试流程,发送结束面试消息
985
+ await this.sendEndInterviewMessage();
986
+ }
987
+ else {
988
+ // 正常流程:发送"下一题"请求,可以附带转录文本
989
+ this.sendMessageToAPI(transcriptionText || '下一题', fileInfo.cos_key);
990
+ }
991
+ }
992
+ catch (error) {
993
+ console.error('视频上传或处理错误:', error);
994
+ this.isEndingInterview = false; // 出错时重置标志
995
+ sentryReporter.SentryReporter.captureError(error, {
996
+ action: 'uploadRecordedVideo',
997
+ component: 'components-oem-virtual-chat-modal',
998
+ title: '视频上传或处理失败',
999
+ });
1000
+ errorEvent.ErrorEventBus.emitError({
1001
+ error: error,
1002
+ message: '视频上传或处理失败',
1003
+ });
1004
+ }
1005
+ finally {
1006
+ this.isUploadingVideo = false; // 上传完成后重置状态
1007
+ this.showRecordingUI = false;
1008
+ this.recordedBlob = null;
1009
+ }
1010
+ }
1011
+ // 处理关闭按钮点击
1012
+ handleCloseClick = () => {
1013
+ // 显示关闭确认对话框
1014
+ this.showCloseConfirmModal = true;
1015
+ };
1016
+ // 处理关闭确认对话框的确认事件
1017
+ handleCloseConfirmOk = () => {
1018
+ this.showCloseConfirmModal = false;
1019
+ this.stopRecording();
1020
+ this.modalClosed.emit();
1021
+ };
1022
+ // 处理关闭确认对话框的取消事件
1023
+ handleCloseConfirmCancel = () => {
1024
+ this.showCloseConfirmModal = false;
1025
+ };
1026
+ // 确保组件卸载时释放资源
1027
+ disconnectedCallback() {
1028
+ document.removeEventListener('pcm-token-invalid', this.tokenInvalidListener);
1029
+ // 清理页面可见性监听器
1030
+ if (this._visibilityChangeHandler) {
1031
+ document.removeEventListener('visibilitychange', this._visibilityChangeHandler);
1032
+ this._visibilityChangeHandler = null;
1033
+ }
1034
+ // 清理其他计时器
1035
+ if (this.waitingTimer) {
1036
+ clearInterval(this.waitingTimer);
1037
+ this.waitingTimer = null;
1038
+ }
1039
+ if (this.recordingTimer) {
1040
+ clearInterval(this.recordingTimer);
1041
+ this.recordingTimer = null;
1042
+ }
1043
+ // 停止录制
1044
+ this.stopRecording();
1045
+ // 清理视频预加载缓存
1046
+ this.preloadedVideos.clear();
1047
+ this.preloadingVideos.clear();
1048
+ // 清理待播放的视频URL
1049
+ this.pendingGeneratedVideoUrl = '';
1050
+ }
1051
+ /**
1052
+ * 预创建数字人视频
1053
+ */
1054
+ async precreateDigitalHumanVideos(digital_human_list) {
1055
+ if (!this.virtualmanKey) {
1056
+ console.warn('VirtualmanKey尚未加载,无法预创建视频。');
1057
+ // 可以在此处添加逻辑,等待virtualmanKey加载后再执行
1058
+ return;
1059
+ }
1060
+ for (const text of digital_human_list) {
1061
+ if (!text || !text.trim())
1062
+ continue;
1063
+ try {
1064
+ // 注意:这里只创建任务,不等待视频生成完成
1065
+ await index$1.sendHttpRequest({
1066
+ url: '/sdk/v1/virtual-human/create-video',
1067
+ method: 'POST',
1068
+ data: {
1069
+ VirtualmanKey: this.virtualmanKey,
1070
+ InputSsml: text,
1071
+ SpeechParam: {
1072
+ Speed: 1,
1073
+ },
1074
+ VideoParam: {
1075
+ Format: 'TransparentWebm',
1076
+ },
1077
+ DriverType: 'Text',
1078
+ },
1079
+ });
1080
+ }
1081
+ catch (error) {
1082
+ console.error(`为文本 "${text}" 创建视频任务失败:`, error);
1083
+ // 可以选择性地报告错误
1084
+ sentryReporter.SentryReporter.captureError(error, {
1085
+ action: 'precreateDigitalHumanVideos',
1086
+ component: 'components-oem-virtual-chat-modal',
1087
+ title: `为文本 "${text}" 创建视频任务失败`,
1088
+ });
1089
+ }
1090
+ }
1091
+
1092
+ }
1093
+ /**
1094
+ * 初始化数字人功能
1095
+ */
1096
+ async initializeDigitalHuman() {
1097
+ if (!this.digitalId)
1098
+ return;
1099
+ try {
1100
+
1101
+ const response = await index$1.sendHttpRequest({
1102
+ url: '/sdk/v1/virtual-human/avatar-detail',
1103
+ method: 'POST',
1104
+ data: {
1105
+ avatar_id: this.digitalId
1106
+ }
1107
+ });
1108
+ if (response.success) {
1109
+ const { placeholder_video_url, virtualman_key, opening_contents } = response.data;
1110
+ if (placeholder_video_url) {
1111
+ this.digitalHumanDefaultVideoUrl = placeholder_video_url;
1112
+ this.digitalHumanVideoUrl = placeholder_video_url;
1113
+ }
1114
+ if (virtualman_key) {
1115
+ this.virtualmanKey = virtualman_key;
1116
+ }
1117
+ // 处理开场白内容(JSON格式)
1118
+ if (opening_contents && Array.isArray(opening_contents) && opening_contents.length > 0) {
1119
+ this.digitalHumanOpeningContents = opening_contents;
1120
+ // 验证并调整开场白索引
1121
+ const validIndex = Math.max(0, Math.min(2, this.openingIndex || 0)); // 限制索引范围0-2
1122
+ const selectedOpeningContent = this.digitalHumanOpeningContents[validIndex] || this.digitalHumanOpeningContents[0];
1123
+ // 按顺序准备预加载列表:1.选择的欢迎视频 2.默认占位视频 3.其他视频
1124
+ const orderedVideosToPreload = [];
1125
+ // 1. 首先加载选择的欢迎视频
1126
+ if (selectedOpeningContent.video_url) {
1127
+ orderedVideosToPreload.push(selectedOpeningContent.video_url);
1128
+ }
1129
+ // 2. 然后加载默认占位视频(如果不同于选择的欢迎视频)
1130
+ if (placeholder_video_url && placeholder_video_url !== selectedOpeningContent.video_url) {
1131
+ orderedVideosToPreload.push(placeholder_video_url);
1132
+ }
1133
+ // 使用顺序预加载
1134
+ this.handleVideoUrlReceivedSequential(orderedVideosToPreload, '数字人初始化(顺序)');
1135
+ // 播放选择的欢迎视频 - 检查是否已经在继续对话模式下设置了状态
1136
+ if (selectedOpeningContent.video_url && !this.digitalHumanVideoReady) {
1137
+
1138
+ this.playDigitalHumanVideo(selectedOpeningContent.video_url, true);
1139
+ }
1140
+ }
1141
+ else {
1142
+ // 没有欢迎视频,只预加载默认占位视频
1143
+ if (placeholder_video_url) {
1144
+ this.handleVideoUrlReceivedSequential([placeholder_video_url], '数字人初始化(仅默认视频)');
1145
+ }
1146
+ }
1147
+ }
1148
+ }
1149
+ catch (error) {
1150
+ console.error('初始化数字人失败:', error);
1151
+ sentryReporter.SentryReporter.captureError(error, {
1152
+ action: 'initializeDigitalHuman',
1153
+ component: 'components-oem-virtual-chat-modal',
1154
+ title: '数字人初始化失败',
1155
+ });
1156
+ }
1157
+ }
1158
+ /**
1159
+ * 生成数字人视频
1160
+ */
1161
+ async generateDigitalHumanVideo(text) {
1162
+ // 如果组件已停用,直接跳过生成视频
1163
+ if (!this.isComponentActive) {
1164
+ console.warn('组件已停用,跳过数字人视频生成');
1165
+ return;
1166
+ }
1167
+ if (!text.trim() || !this.virtualmanKey) {
1168
+ // 条件不满足时,取消等待状态并直接开始录制流程
1169
+ if (this.waitingForDigitalHuman && !this.isTaskCompleted) {
1170
+
1171
+ this.waitingForDigitalHuman = false;
1172
+ this.startWaitingToRecord();
1173
+ }
1174
+ return;
1175
+ }
1176
+
1177
+ // 设置正在生成视频的状态
1178
+ this.isGeneratingDigitalHumanVideo = true;
1179
+ try {
1180
+ // 创建视频任务
1181
+ const createResponse = await index$1.sendHttpRequest({
1182
+ url: '/sdk/v1/virtual-human/create-video',
1183
+ method: 'POST',
1184
+ data: {
1185
+ VirtualmanKey: this.virtualmanKey,
1186
+ InputSsml: text,
1187
+ SpeechParam: {
1188
+ Speed: 1
1189
+ },
1190
+ VideoParam: {
1191
+ Format: 'TransparentWebm',
1192
+ },
1193
+ DriverType: "Text",
1194
+ }
1195
+ });
1196
+
1197
+ if (!createResponse.success) {
1198
+ messageUtils.showMessage(createResponse.message, 'error');
1199
+ throw new Error(`创建视频任务失败: ${createResponse.message}`);
1200
+ }
1201
+ const taskId = createResponse.data.Payload.TaskId;
1202
+
1203
+ // 轮询查询进度
1204
+ const videoUrl = await this.pollVideoProgress(taskId);
1205
+ // 轮询完成后,再次检查组件是否仍处于活动状态
1206
+ if (!this.isComponentActive) {
1207
+ console.warn('组件已停用,跳过视频播放');
1208
+ return;
1209
+ }
1210
+ if (videoUrl) {
1211
+ // 立即预加载新生成的视频(单个视频,不需要顺序处理)
1212
+ await this.preloadVideo(videoUrl);
1213
+ // 预加载后再次检查组件状态
1214
+ if (!this.isComponentActive) {
1215
+ console.warn('组件已停用,跳过视频播放');
1216
+ return;
1217
+ }
1218
+ // 检查是否正在播放开场白视频
1219
+ if (this.isPlayingWelcomeVideo) {
1220
+ // 如果正在播放开场白视频,保存生成的视频URL,等待开场白播放完成
1221
+
1222
+ this.pendingGeneratedVideoUrl = videoUrl;
1223
+ }
1224
+ else {
1225
+ // 否则立即播放生成的视频
1226
+ await this.playDigitalHumanVideo(videoUrl);
1227
+
1228
+ }
1229
+ }
1230
+ else {
1231
+ // 如果没有获取到视频URL,抛出错误
1232
+ throw new Error('未能获取到数字人视频URL');
1233
+ }
1234
+ }
1235
+ catch (error) {
1236
+ console.error('数字人视频生成失败:', error);
1237
+ sentryReporter.SentryReporter.captureError(error, {
1238
+ action: 'generateDigitalHumanVideo',
1239
+ component: 'components-oem-virtual-chat-modal',
1240
+ title: '数字人视频生成失败',
1241
+ });
1242
+ // 数字人视频生成失败时,取消等待状态并直接开始录制流程
1243
+ if (this.waitingForDigitalHuman && !this.isTaskCompleted) {
1244
+
1245
+ this.waitingForDigitalHuman = false;
1246
+ this.startWaitingToRecord();
1247
+ }
1248
+ }
1249
+ finally {
1250
+ // 无论成功还是失败,都重置生成状态
1251
+ this.isGeneratingDigitalHumanVideo = false;
1252
+ }
1253
+ }
1254
+ /**
1255
+ * 轮询查询视频生成进度
1256
+ */
1257
+ async pollVideoProgress(taskId) {
1258
+ const maxAttempts = 45;
1259
+ const pollInterval = 2000;
1260
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
1261
+ try {
1262
+ const response = await index$1.sendHttpRequest({
1263
+ url: '/sdk/v1/virtual-human/query-progress',
1264
+ method: 'POST',
1265
+ data: {
1266
+ TaskId: taskId
1267
+ }
1268
+ });
1269
+ if (!response.success) {
1270
+ throw new Error(`查询进度失败: ${response.message}`);
1271
+ }
1272
+ const payload = response.data.Payload;
1273
+ if (payload.Status === 'SUCCESS' && payload.Progress === 100) {
1274
+ return payload.MediaUrl;
1275
+ }
1276
+ else if (payload.Status === 'FAILED') {
1277
+ throw new Error(`视频生成失败: ${payload.FailMessage}`);
1278
+ }
1279
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
1280
+ }
1281
+ catch (error) {
1282
+ console.error('轮询视频进度失败:', error);
1283
+ break;
1284
+ }
1285
+ }
1286
+ throw new Error('视频生成超时');
1287
+ }
1288
+ /**
1289
+ * 播放数字人视频
1290
+ */
1291
+ async playDigitalHumanVideo(videoUrl, isWelcomeVideo = false) {
1292
+
1293
+ try {
1294
+ // 确保视频已预加载(如果未预加载则立即预加载)
1295
+ await this.preloadVideo(videoUrl);
1296
+ // 只通过状态来控制video元素
1297
+ this.digitalHumanVideoUrl = videoUrl;
1298
+ this.isPlayingDigitalHumanVideo = true;
1299
+ this.digitalHumanVideoReady = true;
1300
+ this.isPlayingWelcomeVideo = isWelcomeVideo;
1301
+ }
1302
+ catch (error) {
1303
+ console.error('播放数字人视频失败:', error);
1304
+ // 数字人视频播放失败时,取消等待状态并直接开始录制流程
1305
+ if (this.waitingForDigitalHuman && !this.isTaskCompleted) {
1306
+
1307
+ this.waitingForDigitalHuman = false;
1308
+ this.startWaitingToRecord();
1309
+ }
1310
+ }
1311
+ }
1312
+ /**
1313
+ * 智能视频预加载管理器
1314
+ * 确保每个视频只预加载一次,避免重复请求
1315
+ */
1316
+ async preloadVideo(videoUrl) {
1317
+ if (!videoUrl || !videoUrl.trim()) {
1318
+ return Promise.resolve();
1319
+ }
1320
+ const normalizedUrl = videoUrl.trim();
1321
+ const fileName = normalizedUrl.split('/').pop() || normalizedUrl;
1322
+ // 如果已经预加载过,直接返回
1323
+ if (this.preloadedVideos.has(normalizedUrl)) {
1324
+
1325
+ return Promise.resolve();
1326
+ }
1327
+ // 如果正在预加载中,返回现有的Promise
1328
+ if (this.preloadingVideos.has(normalizedUrl)) {
1329
+
1330
+ return this.preloadingVideos.get(normalizedUrl);
1331
+ }
1332
+ // 创建新的预加载Promise
1333
+ const preloadPromise = this.executeVideoPreload(normalizedUrl);
1334
+ this.preloadingVideos.set(normalizedUrl, preloadPromise);
1335
+ try {
1336
+ await preloadPromise;
1337
+ // 预加载成功,添加到已预加载集合
1338
+ this.preloadedVideos.add(normalizedUrl);
1339
+
1340
+ }
1341
+ catch (error) {
1342
+ console.error(`💥 视频预加载失败: ${fileName.substring(0, 30)}${fileName.length > 30 ? '...' : ''}`, error);
1343
+ // 预加载失败时不抛出错误,避免阻塞后续流程
1344
+ }
1345
+ finally {
1346
+ // 清理正在预加载的记录
1347
+ this.preloadingVideos.delete(normalizedUrl);
1348
+ }
1349
+ }
1350
+ /**
1351
+ * 执行实际的视频预加载操作
1352
+ */
1353
+ async executeVideoPreload(videoUrl) {
1354
+ return new Promise((resolve, reject) => {
1355
+ const preloadVideo = document.createElement('video');
1356
+ preloadVideo.preload = 'auto';
1357
+ preloadVideo.src = videoUrl;
1358
+ preloadVideo.muted = true; // 确保可以自动播放
1359
+ preloadVideo.crossOrigin = 'anonymous'; // 处理跨域问题
1360
+ let isResolved = false;
1361
+ const handleSuccess = () => {
1362
+ if (isResolved)
1363
+ return;
1364
+ isResolved = true;
1365
+ cleanup();
1366
+
1367
+ resolve();
1368
+ };
1369
+ const handleError = (event) => {
1370
+ if (isResolved)
1371
+ return;
1372
+ isResolved = true;
1373
+ cleanup();
1374
+ console.error('视频预加载失败:', videoUrl, event);
1375
+ reject(new Error('视频预加载失败'));
1376
+ };
1377
+ const cleanup = () => {
1378
+ preloadVideo.removeEventListener('canplaythrough', handleSuccess);
1379
+ preloadVideo.removeEventListener('loadeddata', handleSuccess);
1380
+ preloadVideo.removeEventListener('error', handleError);
1381
+ preloadVideo.removeEventListener('abort', handleError);
1382
+ preloadVideo.src = '';
1383
+ };
1384
+ // 监听多种成功事件,提高兼容性
1385
+ preloadVideo.addEventListener('canplaythrough', handleSuccess);
1386
+ preloadVideo.addEventListener('loadeddata', handleSuccess);
1387
+ // 监听错误事件
1388
+ preloadVideo.addEventListener('error', handleError);
1389
+ preloadVideo.addEventListener('abort', handleError);
1390
+ // 设置超时机制,避免长时间等待
1391
+ setTimeout(() => {
1392
+ if (!isResolved) {
1393
+ console.warn('视频预加载超时:', videoUrl);
1394
+ handleError('timeout');
1395
+ }
1396
+ }, 15000); // 15秒超时
1397
+ // 开始加载
1398
+ preloadVideo.load();
1399
+ });
1400
+ }
1401
+ /**
1402
+ * 顺序预加载多个视频
1403
+ * 上一个视频加载完毕后立刻加载下一个视频
1404
+ */
1405
+ async sequentialPreloadVideos(videoUrls) {
1406
+ if (!videoUrls || videoUrls.length === 0) {
1407
+ return;
1408
+ }
1409
+ const validUrls = videoUrls.filter(url => url && url.trim());
1410
+ if (validUrls.length === 0) {
1411
+ return;
1412
+ }
1413
+
1414
+
1415
+ validUrls.forEach((url, index) => {
1416
+ url.split('/').pop() || url;
1417
+
1418
+ });
1419
+ for (let i = 0; i < validUrls.length; i++) {
1420
+ const url = validUrls[i];
1421
+ const fileName = url.split('/').pop() || url;
1422
+ try {
1423
+ await this.preloadVideo(url);
1424
+
1425
+ }
1426
+ catch (error) {
1427
+ console.warn(`❌ [${i + 1}/${validUrls.length}] 预加载失败: ${fileName.substring(0, 50)}${fileName.length > 50 ? '...' : ''}`, error);
1428
+ // 即使单个视频失败,也继续加载下一个
1429
+ }
1430
+ }
1431
+
1432
+ }
1433
+ /**
1434
+ * 顺序视频URL处理器
1435
+ * 一旦获得视频URL,按顺序预加载
1436
+ */
1437
+ handleVideoUrlReceivedSequential(videoUrls, context = '') {
1438
+ if (!videoUrls || videoUrls.length === 0)
1439
+ return;
1440
+ const validUrls = videoUrls.filter(url => url && url.trim());
1441
+ if (validUrls.length === 0)
1442
+ return;
1443
+
1444
+ // 异步顺序预加载,不阻塞主流程
1445
+ this.sequentialPreloadVideos(validUrls).catch(error => {
1446
+ console.warn(`⚠️ 顺序预加载视频失败 (${context}):`, error);
1447
+ });
1448
+ }
1449
+ /**
1450
+ * 处理页面从后台恢复(关屏/切后台回来)
1451
+ * 检测并恢复可能卡住的流程
1452
+ */
1453
+ handlePageResume() {
1454
+ // 如果任务已完成或未打开,不需要处理
1455
+ if (this.isTaskCompleted || !this.isOpen || this.isEndingInterview) {
1456
+ return;
1457
+ }
1458
+ console.log('页面恢复可见,检查流程状态:', {
1459
+ waitingForDigitalHuman: this.waitingForDigitalHuman,
1460
+ isPlayingDigitalHumanVideo: this.isPlayingDigitalHumanVideo,
1461
+ waitingToRecord: this.waitingToRecord,
1462
+ isRecording: this.isRecording,
1463
+ showRecordingUI: this.showRecordingUI,
1464
+ });
1465
+ // 场景1: 数字人视频正在播放但可能被浏览器暂停/中断
1466
+ // 尝试恢复视频播放,如果3秒后仍未结束则跳过
1467
+ if (this.isPlayingDigitalHumanVideo) {
1468
+ const videoEl = this.hostElement.shadowRoot?.querySelector('.digital-human-background-video');
1469
+ if (videoEl) {
1470
+ // 尝试恢复播放
1471
+ const playPromise = videoEl.play();
1472
+ if (playPromise) {
1473
+ playPromise.catch(() => {
1474
+ console.warn('恢复数字人视频播放失败,跳过视频');
1475
+ this.skipDigitalHumanVideo();
1476
+ });
1477
+ }
1478
+ // 设置安全超时:如果3秒后视频还没结束,直接跳过
1479
+ setTimeout(() => {
1480
+ if (this.isPlayingDigitalHumanVideo) {
1481
+ console.warn('数字人视频恢复后3秒仍未结束,强制跳过');
1482
+ this.skipDigitalHumanVideo();
1483
+ }
1484
+ }, 3000);
1485
+ }
1486
+ else {
1487
+ // 视频元素不存在,直接跳过
1488
+ this.skipDigitalHumanVideo();
1489
+ }
1490
+ return;
1491
+ }
1492
+ // 场景2: 等待数字人但视频已经不在播放状态(ended事件丢失)
1493
+ if (this.waitingForDigitalHuman && !this.isPlayingDigitalHumanVideo) {
1494
+ console.warn('检测到waitingForDigitalHuman卡住(视频未在播放),直接开始录制流程');
1495
+ this.waitingForDigitalHuman = false;
1496
+ this.startWaitingToRecord();
1497
+ return;
1498
+ }
1499
+ // 场景3: 正在录制中,检查MediaStream是否仍然有效
1500
+ if (this.isRecording && this.recordingStream) {
1501
+ const tracks = this.recordingStream.getTracks();
1502
+ const allEnded = tracks.length > 0 && tracks.every(track => track.readyState === 'ended');
1503
+ if (allEnded) {
1504
+ console.warn('检测到录制的MediaStream已被系统回收,停止录制');
1505
+ this.deviceError = '设备被系统中断,请重新录制';
1506
+ this.stopRecording();
1507
+ }
1508
+ }
1509
+ }
1510
+ /**
1511
+ * 跳过当前数字人视频播放,直接进入录制流程
1512
+ */
1513
+ skipDigitalHumanVideo() {
1514
+ this.digitalHumanVideoUrl = this.digitalHumanDefaultVideoUrl;
1515
+ this.isPlayingDigitalHumanVideo = false;
1516
+ this.isPlayingWelcomeVideo = false;
1517
+ if (this.waitingForDigitalHuman && !this.isTaskCompleted && !this.isEndingInterview) {
1518
+ this.waitingForDigitalHuman = false;
1519
+ this.startWaitingToRecord();
1520
+ }
1521
+ }
1522
+ /**
1523
+ * 处理数字人视频播放结束
1524
+ */
1525
+ handleVideoElementEnded = async () => {
1526
+ if (this.isPlayingDigitalHumanVideo) {
1527
+
1528
+ // 只通过状态来控制video元素,恢复默认视频
1529
+ this.digitalHumanVideoUrl = this.digitalHumanDefaultVideoUrl;
1530
+ this.isPlayingDigitalHumanVideo = false;
1531
+ const wasWelcomeVideo = this.isPlayingWelcomeVideo;
1532
+ this.isPlayingWelcomeVideo = false;
1533
+ // 如果是开场白视频播放完成,检查是否有待播放的生成视频
1534
+ if (wasWelcomeVideo && this.pendingGeneratedVideoUrl) {
1535
+
1536
+ const videoUrl = this.pendingGeneratedVideoUrl;
1537
+ this.pendingGeneratedVideoUrl = ''; // 清空待播放视频URL
1538
+ await this.playDigitalHumanVideo(videoUrl);
1539
+ return; // 播放生成的视频,不执行后续的录制流程
1540
+ }
1541
+ // 只有非欢迎视频播放完成后,才开始录制流程
1542
+ // 如果任务已完成或正在结束面试,不开始录制流程
1543
+ if (this.waitingForDigitalHuman && !this.isTaskCompleted && !this.isEndingInterview && !wasWelcomeVideo) {
1544
+ this.waitingForDigitalHuman = false;
1545
+ this.startWaitingToRecord();
1546
+ }
1547
+ }
1548
+ };
1549
+ /**
1550
+ * 处理面试结束按钮点击 - 显示二次确认模态框
1551
+ */
1552
+ handleInterviewEnd = () => {
1553
+ // 如果已经在结束流程中或任务已完成,不允许再次点击
1554
+ if (this.isEndingInterview || this.isTaskCompleted) {
1555
+
1556
+ return;
1557
+ }
1558
+
1559
+ this.showEndInterviewConfirmModal = true;
1560
+ };
1561
+ /**
1562
+ * 确认结束面试
1563
+ */
1564
+ handleConfirmEndInterview = async () => {
1565
+ try {
1566
+
1567
+ this.showEndInterviewConfirmModal = false;
1568
+ // 设置结束面试标志
1569
+ this.isEndingInterview = true;
1570
+ // 停止所有可能正在进行的流程
1571
+ // 1. 清除等待录制计时器
1572
+ if (this.waitingTimer) {
1573
+ clearInterval(this.waitingTimer);
1574
+ this.waitingTimer = null;
1575
+ }
1576
+ this.waitingToRecord = false;
1577
+ // 2. 清除等待数字人标志
1578
+ this.waitingForDigitalHuman = false;
1579
+ // 3. 如果正在录制,直接停止录制(不显示确认框)
1580
+ if (this.isRecording) {
1581
+
1582
+ this.stopRecording(); // 直接调用 stopRecording,不通过 handleStopRecording
1583
+ // uploadRecordedVideo 会在上传完成后检查 isEndingInterview 标志
1584
+ // 并发送结束面试消息
1585
+ }
1586
+ else {
1587
+ // 如果没有录制,直接发送结束面试消息
1588
+ await this.sendEndInterviewMessage();
1589
+ }
1590
+ }
1591
+ catch (error) {
1592
+ console.error('点击结束发送消息失败:', error);
1593
+ this.isEndingInterview = false;
1594
+ sentryReporter.SentryReporter.captureError(error, {
1595
+ action: 'handleConfirmEndInterview',
1596
+ component: 'components-oem-virtual-chat-modal',
1597
+ title: '点击结束发送消息失败',
1598
+ });
1599
+ errorEvent.ErrorEventBus.emitError({
1600
+ error: error,
1601
+ message: '点击结束发送消息失败',
1602
+ });
1603
+ }
1604
+ };
1605
+ /**
1606
+ * 发送结束面试消息
1607
+ */
1608
+ sendEndInterviewMessage = async () => {
1609
+ try {
1610
+ const endMessage = '请结束面试';
1611
+ console.log('面试结束消息已发送:', {
1612
+ conversation_id: this.conversationId,
1613
+ message: endMessage,
1614
+ });
1615
+ // 发送消息到API
1616
+ await this.sendMessageToAPI(endMessage);
1617
+ // 重置标志
1618
+ this.isEndingInterview = false;
1619
+ }
1620
+ catch (error) {
1621
+ console.error('发送结束面试消息失败:', error);
1622
+ this.isEndingInterview = false;
1623
+ throw error;
1624
+ }
1625
+ };
1626
+ /**
1627
+ * 取消结束面试
1628
+ */
1629
+ handleCancelEndInterview = () => {
1630
+
1631
+ this.showEndInterviewConfirmModal = false;
1632
+ };
1633
+ /**
1634
+ * 计算已回答的题数
1635
+ */
1636
+ getAnsweredQuestionCount = () => {
1637
+ // 统计用户已经回答的问题数量(有answer的消息)
1638
+ return this.messages.filter(msg => msg.answer && msg.answer.trim()).length;
1639
+ };
1640
+ renderChatHistory() {
1641
+ // 如果正在播放欢迎视频,优先显示欢迎语
1642
+ if (this.isPlayingWelcomeVideo) {
1643
+ // 验证并调整开场白索引
1644
+ const validIndex = Math.max(0, Math.min(2, this.openingIndex || 0)); // 限制索引范围0-2
1645
+ const selectedOpeningContent = this.digitalHumanOpeningContents[validIndex] || this.digitalHumanOpeningContents[0];
1646
+ const welcomeText = selectedOpeningContent.text;
1647
+ return (index.h("div", { class: "ai-message-item" }, index.h("div", { class: "ai-message-content" }, welcomeText)));
1648
+ }
1649
+ // 如果正在生成数字人视频,显示查看面试信息的提示
1650
+ if (this.isGeneratingDigitalHumanVideo || this.isLoading) {
1651
+ return (index.h("div", { class: "ai-message-item" }, index.h("div", { class: "ai-message-content" }, "\u8BF7\u7A0D\u7B49\uFF0C\u6211\u67E5\u770B\u4E00\u4E0B\u672C\u6B21\u9762\u8BD5\u7684\u4FE1\u606F...")));
1652
+ }
1653
+ // 优先显示正在流式输出的消息
1654
+ if (this.currentStreamingMessage && this.currentStreamingMessage.answer) {
1655
+ return (index.h("div", { class: "ai-message-item streaming" }, index.h("div", { class: "ai-message-content", innerHTML: this.currentStreamingMessage.answer }), this.currentStreamingMessage.isStreaming && (index.h("div", { class: "typing-indicator" }, index.h("span", null), index.h("span", null), index.h("span", null)))));
1656
+ }
1657
+ // 找到最后一条有AI回复的消息
1658
+ const lastMessageWithAnswer = [...this.messages].reverse().find(msg => msg.answer && msg.answer.trim());
1659
+ if (lastMessageWithAnswer) {
1660
+ return (index.h("div", { class: "ai-message-item" }, index.h("div", { class: "ai-message-content", innerHTML: lastMessageWithAnswer.answer })));
1661
+ }
1662
+ return (index.h("div", { class: "ai-message-item" }, index.h("div", { class: "ai-message-content" }, "\u8BF7\u7A0D\u7B49\uFF0C\u6211\u67E5\u770B\u4E00\u4E0B\u672C\u6B21\u9762\u8BD5\u7684\u4FE1\u606F...")));
1663
+ }
1664
+ render() {
1665
+ if (!this.isOpen)
1666
+ return null;
1667
+ const modalStyle = {
1668
+ zIndex: String(this.zIndex),
1669
+ };
1670
+ const containerClass = {
1671
+ 'modal-container': true,
1672
+ 'fullscreen': this.fullscreen,
1673
+ 'digital-human-mode': !!this.digitalId,
1674
+ };
1675
+ const overlayClass = {
1676
+ 'modal-overlay': true,
1677
+ 'fullscreen-overlay': this.fullscreen,
1678
+ };
1679
+ return (index.h("div", { class: overlayClass, style: modalStyle }, index.h("div", { class: containerClass }, index.h("button", { class: "close-button", onClick: this.handleCloseClick }, index.h("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "currentColor" }, index.h("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }))), this.digitalId && this.digitalHumanVideoUrl && (index.h("div", { class: "digital-human-background" }, index.h("video", { autoplay: true, playsinline: true, loop: !this.isPlayingDigitalHumanVideo, muted: !this.isPlayingDigitalHumanVideo, src: this.digitalHumanVideoUrl, class: "digital-human-background-video", onEnded: this.handleVideoElementEnded, onPlay: e => {
1680
+ // 浏览器切换 src 后可能重置 playbackRate,在 play 事件里补设一次
1681
+ const el = e.target;
1682
+ const isDefaultVideo = this.digitalHumanVideoUrl === this.digitalHumanDefaultVideoUrl;
1683
+ el.playbackRate = isDefaultVideo ? 1.0 : this.playbackRate;
1684
+ }, ref: el => {
1685
+ if (el) {
1686
+ // 只有非默认占位视频才应用语速控制
1687
+ const isDefaultVideo = this.digitalHumanVideoUrl === this.digitalHumanDefaultVideoUrl;
1688
+ el.playbackRate = isDefaultVideo ? 1.0 : this.playbackRate;
1689
+ // 微信环境下,确保视频URL变化时重新加载
1690
+ // 如果src已经设置但视频未加载,手动触发load
1691
+ if (el.src && el.readyState === 0) {
1692
+ el.load();
1693
+ }
1694
+ }
1695
+ } }))), this.renderVideoPreview(), index.h("div", { class: "modal-content-layer" }, index.h("div", { class: "main-content" }, index.h("div", { class: "chat-history-section" }, this.renderChatHistory()), this.showEndInterviewButton && !this.isTaskCompleted && this.conversationId && (index.h("div", { class: "end-interview-button-container" }, index.h("button", { class: "end-interview-button", onClick: this.handleInterviewEnd, disabled: this.isEndingInterview || !this.isRecording, title: this.isEndingInterview ? '正在结束面试...' : !this.isRecording ? '请在录制时点击' : '结束面试' }, this.isEndingInterview ? '正在结束...' : '结束面试'))), this.renderRecordingStatusBar())), index.h("div", { style: { position: 'relative', zIndex: '100' } }, index.h("components-confirm-modal", { isOpen: this.showConfirmModal, modalTitle: "\u786E\u8BA4\u5B8C\u6210\u56DE\u7B54\uFF1F", okText: "\u786E\u8BA4\u5B8C\u6210", cancelText: "\u7EE7\u7EED\u5F55\u5236", okType: "danger", onOk: this.handleConfirmModalOk, onCancel: this.handleConfirmModalCancelEvent, onClosed: this.handleConfirmModalCancel }, index.h("div", { style: { marginBottom: '16px' } }, index.h("div", { style: {
1696
+ fontSize: '14px',
1697
+ color: '#1890ff',
1698
+ fontWeight: '500',
1699
+ marginBottom: '16px',
1700
+ textAlign: 'center',
1701
+ } }, "\u5F53\u524D\u5F55\u5236\u65F6\u957F\uFF1A", (() => {
1702
+ const elapsedSeconds = this.recordingStartTime > 0 ? Math.floor((Date.now() - this.recordingStartTime) / 1000) : 0;
1703
+ const minutes = Math.floor(elapsedSeconds / 60);
1704
+ const seconds = elapsedSeconds % 60;
1705
+ return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
1706
+ })()), index.h("div", { style: {
1707
+ display: 'flex',
1708
+ alignItems: 'center',
1709
+ gap: '8px',
1710
+ padding: '12px 16px',
1711
+ background: '#fff7e6',
1712
+ border: '1px solid #ffec99',
1713
+ borderRadius: '6px',
1714
+ color: '#d46b08',
1715
+ fontSize: '14px',
1716
+ marginBottom: '16px',
1717
+ } }, index.h("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "currentColor" }, index.h("path", { d: "M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z" })), index.h("span", null, "\u6CE8\u610F\uFF1A\u5F55\u5236\u4ECD\u5728\u8FDB\u884C\u4E2D")), index.h("div", { style: {
1718
+ fontSize: '16px',
1719
+ color: '#595959',
1720
+ lineHeight: '1.5',
1721
+ marginBottom: '16px',
1722
+ } }, "\u70B9\u51FB\"\u786E\u8BA4\u5B8C\u6210\"\u5C06\u7ED3\u675F\u672C\u9898\u56DE\u7B54"), index.h("label", { style: {
1723
+ display: 'flex',
1724
+ alignItems: 'center',
1725
+ gap: '8px',
1726
+ cursor: 'pointer',
1727
+ padding: '8px 0',
1728
+ userSelect: 'none',
1729
+ } }, index.h("input", { type: "checkbox", style: {
1730
+ width: '16px',
1731
+ height: '16px',
1732
+ accentColor: '#1890ff',
1733
+ cursor: 'pointer',
1734
+ margin: '0',
1735
+ }, checked: this.skipConfirmThisInterview, onChange: this.handleSkipConfirmChange }), index.h("span", { style: {
1736
+ fontSize: '14px',
1737
+ color: '#595959',
1738
+ cursor: 'pointer',
1739
+ lineHeight: '1.4',
1740
+ } }, "\u672C\u6B21\u9762\u8BD5\u4E0D\u518D\u63D0\u9192"))))), index.h("div", { style: { position: 'relative', zIndex: '100' } }, index.h("components-confirm-modal", { isOpen: this.showCloseConfirmModal, modalTitle: "\u786E\u8BA4\u5173\u95ED\uFF1F", okText: "\u786E\u8BA4\u5173\u95ED", cancelText: "\u53D6\u6D88", okType: "danger", onOk: this.handleCloseConfirmOk, onCancel: this.handleCloseConfirmCancel, onClosed: this.handleCloseConfirmCancel }, index.h("div", { style: { marginBottom: '16px' } }, index.h("div", { style: {
1741
+ fontSize: '16px',
1742
+ color: '#595959',
1743
+ lineHeight: '1.5',
1744
+ marginBottom: '16px',
1745
+ } }, this.isTaskCompleted ? '面试已结束,确认要关闭吗?' : '您的面试未完成,确认要关闭吗?')))), index.h("div", { style: { position: 'relative', zIndex: '100' } }, index.h("components-confirm-modal", { isOpen: this.showEndInterviewConfirmModal, modalTitle: "\u786E\u8BA4\u7ED3\u675F\u9762\u8BD5\uFF1F", okText: "\u786E\u8BA4\u7ED3\u675F", cancelText: "\u53D6\u6D88", okType: "danger", onOk: this.handleConfirmEndInterview, onCancel: this.handleCancelEndInterview, onClosed: this.handleCancelEndInterview }, index.h("div", { style: { marginBottom: '16px' } }, (() => {
1746
+ const answeredCount = this.getAnsweredQuestionCount();
1747
+ return (index.h("div", null, index.h("div", { style: {
1748
+ fontSize: '14px',
1749
+ color: '#1890ff',
1750
+ fontWeight: '500',
1751
+ marginBottom: '16px',
1752
+ textAlign: 'center',
1753
+ } }, "\u5F53\u524D\u7B2C ", answeredCount, " \u9898"), answeredCount < 3 && (index.h("div", { style: {
1754
+ display: 'flex',
1755
+ alignItems: 'flex-start',
1756
+ gap: '8px',
1757
+ padding: '12px 16px',
1758
+ background: '#fff2e8',
1759
+ border: '1px solid #ffbb96',
1760
+ borderRadius: '6px',
1761
+ color: '#d4380d',
1762
+ fontSize: '14px',
1763
+ marginBottom: '16px',
1764
+ } }, index.h("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "currentColor", style: { flexShrink: '0', marginTop: '2px' } }, index.h("path", { d: "M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z" })), index.h("span", null, index.h("strong", null, "\u8B66\u544A\uFF1A"), "\u5F53\u524D\u5DF2\u56DE\u7B54\u9898\u6570\u5C0F\u4E8E 3 \u9898\uFF0C\u7ED3\u675F\u9762\u8BD5\u53EF\u80FD\u65E0\u6CD5\u6B63\u5E38\u5B8C\u6210\u9762\u8BD5\u6D41\u7A0B\uFF0C\u5EFA\u8BAE\u7EE7\u7EED\u5B8C\u6210\u66F4\u591A\u9898\u76EE\u540E\u518D\u7ED3\u675F\u9762\u8BD5\u3002"))), index.h("div", { style: {
1765
+ fontSize: '14px',
1766
+ color: '#595959',
1767
+ lineHeight: '1.6',
1768
+ textAlign: 'center',
1769
+ } }, "\u786E\u8BA4\u8981\u7ED3\u675F\u9762\u8BD5\u5417\uFF1F\u7ED3\u675F\u540E\u5C06\u751F\u6210\u9762\u8BD5\u62A5\u544A\u3002")));
1770
+ })()))))));
1771
+ }
1772
+ renderVideoPreview() {
1773
+ if (!this.showRecordingUI) {
1774
+ return null;
1775
+ }
1776
+ return (index.h("div", { class: "recording-preview-top-right" }, index.h("video", { class: "user-video-preview", autoPlay: true, playsInline: true, muted: true, style: { transform: 'scaleX(-1)' }, ref: el => {
1777
+ if (el && this.recordingStream && !this.videoRef) {
1778
+ this.videoRef = el;
1779
+ }
1780
+ } })));
1781
+ }
1782
+ renderRecordingStatusBar() {
1783
+ if (this.showRecordingUI) {
1784
+ const minutes = Math.floor(this.recordingTimeLeft / 60)
1785
+ .toString()
1786
+ .padStart(2, '0');
1787
+ const seconds = (this.recordingTimeLeft % 60).toString().padStart(2, '0');
1788
+ return (index.h("div", { class: "recording-status-bar" }, index.h("div", { class: "audio-waveform" }, index.h("div", { class: "waveform-bar" }), index.h("div", { class: "waveform-bar" }), index.h("div", { class: "waveform-bar" }), index.h("div", { class: "waveform-bar" }), index.h("div", { class: "waveform-bar" }), index.h("div", { class: "waveform-bar" }), index.h("div", { class: "waveform-bar" }), index.h("div", { class: "waveform-bar" })), index.h("span", { class: "recording-timer" }, minutes, ":", seconds), index.h("button", { class: "finish-recording-btn", onClick: () => this.handleStopRecording() }, index.h("img", { src: "https://pcm-resource-1312611446.cos.ap-guangzhou.myqcloud.com/sdk/icon/gou.png", width: "16", height: "16", alt: "\u5B8C\u6210", style: { filter: 'brightness(0) invert(1)' } }))));
1789
+ }
1790
+ return index.h("div", { class: "recording-status-bar" }, this.renderStatusIndicator());
1791
+ }
1792
+ renderStatusIndicator() {
1793
+ // 优先显示设备错误
1794
+ if (this.deviceError) {
1795
+ return (index.h("div", { class: "status-indicator-text error" }, index.h("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "currentColor" }, index.h("path", { d: "M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z" })), index.h("span", null, this.deviceError)));
1796
+ }
1797
+ // 任务完成
1798
+ if (this.isTaskCompleted) {
1799
+ return (index.h("div", { class: "status-indicator-text" }, index.h("span", null, "\u9762\u8BD5\u5DF2\u5B8C\u6210")));
1800
+ }
1801
+ // 正在生成数字人视频
1802
+ if (this.waitingForDigitalHuman && this.digitalId) {
1803
+ return (index.h("div", { class: "status-indicator-text loading" }, index.h("div", { class: "loading-spinner-small" }), index.h("span", null, "\u9762\u8BD5\u5B98\u601D\u8003\u4E2D...")));
1804
+ }
1805
+ // 上传视频中
1806
+ if (this.isUploadingVideo) {
1807
+ return (index.h("div", { class: "status-indicator-text loading" }, index.h("div", { class: "loading-spinner-small" }), index.h("span", null, "\u6B63\u5728\u63D0\u4EA4\u56DE\u7B54...")));
1808
+ }
1809
+ // 等待题目
1810
+ if (this.isLoading || this.currentStreamingMessage) {
1811
+ return (index.h("div", { class: "status-indicator-text loading" }, index.h("div", { class: "loading-spinner-small" }), index.h("span", null, "\u9898\u76EE\u751F\u6210\u4E2D...")));
1812
+ }
1813
+ // 等待录制
1814
+ if (this.waitingToRecord) {
1815
+ return (index.h("div", { class: "status-indicator-text" }, index.h("span", null, this.waitingTimeLeft, " \u79D2\u540E\u5F00\u59CB\u60A8\u7684\u56DE\u7B54")));
1816
+ }
1817
+ // 准备就绪
1818
+ return (index.h("div", { class: "status-indicator-text ready" }, index.h("img", { src: "https://pcm-resource-1312611446.cos.ap-guangzhou.myqcloud.com/sdk/icon/gou.png", width: "16", height: "16", alt: "\u51C6\u5907\u5C31\u7EEA" }), index.h("span", null, "\u51C6\u5907\u5C31\u7EEA")));
1819
+ }
1820
+ static get watchers() { return {
1821
+ "token": ["handleTokenChange"]
1822
+ }; }
1823
+ };
1824
+ ComponentsOemVirtualChatModal.style = componentsOemVirtualChatModalCss + hostCss;
1825
+
1826
+ exports.components_oem_virtual_chat_modal = ComponentsOemVirtualChatModal;
1827
+ //# sourceMappingURL=components-oem-virtual-chat-modal.entry.cjs.js.map
1828
+
1829
+ //# sourceMappingURL=components-oem-virtual-chat-modal.cjs.entry.js.map