Brak opisu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

fcbkdatistart.vue 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. <template>
  2. <van-sticky>
  3. <van-nav-bar>
  4. <!-- <template #left>
  5. <van-icon name="arrow-left" size="18" @click="goBack" />
  6. </template> -->
  7. <template #title> 答题考试 </template>
  8. <template #right>
  9. <!-- 提交按钮 -->
  10. <van-button @click="checkBeforeSubmit" round color="linear-gradient(to right, #18FFFF, #304FFE)"
  11. style="height: 30px; width: 80px">交卷</van-button>
  12. </template>
  13. </van-nav-bar>
  14. </van-sticky>
  15. <van-overlay :show="overlayloading">
  16. <div class="wrapper">
  17. <van-loading color="#0094ff"> 加载中... </van-loading>
  18. </div>
  19. </van-overlay>
  20. <div class="quiz-page">
  21. <div class="question-number">
  22. {{ activeIndex + 1 }}/{{ questions.length }}
  23. </div>
  24. <div v-if="questions.length > 0" class="question-content">
  25. <!-- 题干 -->
  26. <p class="kong">
  27. <img
  28. :src="getQuestionTypeImage(currentQuestion.category)"
  29. class="question-type-img"
  30. alt=""
  31. />
  32. {{ currentQuestion.stem }}
  33. </p>
  34. <!-- 单选题 -->
  35. <div v-if="currentQuestion.category === '单选'">
  36. <van-radio-group v-model="userAnswers[currentQuestion.id]">
  37. <van-radio :name="'A'" class="kong">A.{{ currentQuestion.optionA }}</van-radio>
  38. <van-radio :name="'B'" class="kong">B.{{ currentQuestion.optionB }}</van-radio>
  39. <van-radio :name="'C'" class="kong">C.{{ currentQuestion.optionC }}</van-radio>
  40. <van-radio :name="'D'" class="kong" v-if="currentQuestion.optionD">D.
  41. {{ currentQuestion.optionD }}</van-radio>
  42. <van-radio :name="'E'" class="kong" v-if="currentQuestion.optionE">E.
  43. {{ currentQuestion.optionE }}</van-radio>
  44. </van-radio-group>
  45. </div>
  46. <!-- 多选题 -->
  47. <div v-if="currentQuestion.category === '多选'">
  48. <van-checkbox-group v-model="userAnswers[currentQuestion.id]" shape="square">
  49. <van-checkbox :name="'A'" class="kong">A.{{ currentQuestion.optionA }}</van-checkbox>
  50. <van-checkbox :name="'B'" class="kong">B.{{ currentQuestion.optionB }}</van-checkbox>
  51. <van-checkbox :name="'C'" class="kong">C.{{ currentQuestion.optionC }}</van-checkbox>
  52. <van-checkbox :name="'D'" class="kong"
  53. v-if="currentQuestion.optionD">D.{{ currentQuestion.optionD }}</van-checkbox>
  54. <van-checkbox :name="'E'" class="kong"
  55. v-if="currentQuestion.optionE">E.{{ currentQuestion.optionE }}</van-checkbox>
  56. </van-checkbox-group>
  57. </div>
  58. <!-- 判断题 -->
  59. <div v-if="currentQuestion.category === '判断'">
  60. <van-radio-group v-model="userAnswers[currentQuestion.id]">
  61. <van-radio :name="'A'" class="kong">A.正确</van-radio>
  62. <van-radio :name="'B'" class="kong">B.错误</van-radio>
  63. </van-radio-group>
  64. </div>
  65. </div>
  66. <!-- 底部固定栏 -->
  67. <div class="footer">
  68. <van-button @click="prevQuestion" :disabled="activeIndex === 0"
  69. style="height: 40px; width: 45%">上一题</van-button>
  70. <van-button @click="nextQuestion" :disabled="activeIndex === questions.length - 1" style="
  71. height: 40px;
  72. width: 45%;
  73. background-color: var(--van-radio-checked-icon-color);
  74. border-color: var(--van-radio-checked-icon-color);
  75. color: #fff;
  76. ">下一题</van-button>
  77. </div>
  78. <!-- 提交前确认弹窗 -->
  79. <van-dialog v-model:show="confirmSubmitDialog" title="确认交卷" show-cancel-button @confirm="submitForm">
  80. <p :class="{ 'van-dialog__message': true, 'text-center': true }">
  81. <span v-if="hasUnanswered">{{ unansweredText }}</span>
  82. <span v-else>{{ completedText }}</span>
  83. </p>
  84. </van-dialog>
  85. <!-- 结果弹窗 -->
  86. <van-popup v-model:show="showResult" position="top" style="height: 100%">
  87. <van-sticky>
  88. <van-nav-bar title="答题结果" />
  89. </van-sticky>
  90. <div style="
  91. margin-top: 10px;
  92. margin-left: 20px;
  93. margin-bottom: 20px;
  94. font-weight: bold;
  95. ">
  96. 本次得分:{{ totalScore }}
  97. <!--取接口成绩-->
  98. </div>
  99. <van-divider />
  100. <!-- 题干 -->
  101. <div v-for="question in questions" :key="question.id" class="question">
  102. <p>
  103. <span v-if="question.category === '单选'">[单选]</span>
  104. <span v-if="question.category === '多选'">[多选]</span>
  105. <span v-if="question.category === '判断'">[判断]</span>
  106. {{ question.stem }}
  107. </p>
  108. <!-- 显示提交答案 -->
  109. <p>
  110. <span :style="{
  111. color: Array.isArray(userAnswers[question.id])
  112. ? userAnswers[question.id].sort().join('') ===
  113. question.answer
  114. ? '#007aff'
  115. : 'red'
  116. : userAnswers[question.id] === question.answer
  117. ? '#007aff'
  118. : 'red',
  119. }">
  120. 提交答案:{{
  121. Array.isArray(userAnswers[question.id])
  122. ? userAnswers[question.id].sort().join("")
  123. : userAnswers[question.id] || "未作答"
  124. }}
  125. </span>
  126. </p>
  127. <!-- 显示正确答案 -->
  128. <p style="color: #007aff">正确答案:{{ question.answer }}</p>
  129. <div v-if="question.category === '单选'" class="kong">
  130. <div>A. {{ question.optionA }}</div>
  131. <div>B. {{ question.optionB }}</div>
  132. <div>C. {{ question.optionC }}</div>
  133. <div v-if="question.optionD">D. {{ question.optionD }}</div>
  134. <div v-if="question.optionE">E. {{ question.optionE }}</div>
  135. </div>
  136. <div v-if="question.category === '多选'" class="kong">
  137. <div>A. {{ question.optionA }}</div>
  138. <div>B. {{ question.optionB }}</div>
  139. <div>C. {{ question.optionC }}</div>
  140. <div v-if="question.optionD">D. {{ question.optionD }}</div>
  141. <div v-if="question.optionE">E. {{ question.optionE }}</div>
  142. </div>
  143. <div v-if="question.category === '判断'" class="kong">
  144. <div>A.正确</div>
  145. <div>B.错误</div>
  146. </div>
  147. <!-- AI解析按钮和内容 -->
  148. <div style="margin: 10px 0;">
  149. <van-button
  150. type="primary"
  151. size="small"
  152. :loading="analysisLoading[question.id]"
  153. @click="generateAIAnalysis(question,true)"
  154. >
  155. {{ aiAnalysis[question.id] ? '重新解析' : 'AI解析' }}
  156. </van-button>
  157. <!-- 显示AI解析内容 -->
  158. <div v-if="aiAnalysis[question.id]" class="ai-analysis-content">
  159. <div v-html="renderAnalysis(aiAnalysis[question.id])"></div>
  160. </div>
  161. </div>
  162. <van-divider />
  163. </div>
  164. <div style="margin-top: 20px; text-align: center; margin-bottom: 20px">
  165. <van-button class="questionBtn" type="primary" @click="confirmResult">确定</van-button>
  166. </div>
  167. </van-popup>
  168. </div>
  169. </template>
  170. <script setup>
  171. import {
  172. onMounted,
  173. ref,
  174. getCurrentInstance,
  175. computed
  176. } from "vue";
  177. import {
  178. showConfirmDialog,
  179. showSuccessToast,
  180. showFailToast,
  181. showDialog
  182. } from 'vant';
  183. import {
  184. useRouter,
  185. useRoute
  186. } from "vue-router";
  187. import {
  188. examResult,
  189. formNew,
  190. myDefaultCourse,
  191. saveScore,
  192. sortData,
  193. } from "@/api/dati";
  194. const {
  195. proxy
  196. } = getCurrentInstance()
  197. const router = useRouter();
  198. const route = useRoute();
  199. const query = route.query;
  200. // 1. 从本地存储获取当前用户的 userCode(字符串)
  201. const currentUserCode = localStorage.getItem('userCode');
  202. // 2. 包装成数组(即使为空也安全)
  203. const userCodeArray = currentUserCode ? [currentUserCode] : [];
  204. // 3. 按原来的方式生成 userDesc 字符串
  205. const userDesc = userCodeArray.join(',');
  206. const getExamContext = () => ({
  207. id: route.query.examId || '',
  208. testRole: route.query.testRole || '',
  209. checkTime: route.query.checkTime || '',
  210. checkName: route.query.checkName || '',
  211. testType: route.query.testType || '',
  212. addId: route.query.addId || '',
  213. userDesc: userDesc,
  214. });
  215. const userId = localStorage.getItem('userId');
  216. const questions = ref([]);
  217. const userAnswers = ref({});
  218. const activeIndex = ref(0);
  219. const totalScore = ref(0);
  220. const showResult = ref(false);
  221. const confirmSubmitDialog = ref(false);
  222. const hasUnanswered = ref(false);
  223. const unansweredText = "有题目未完成,是否确认交卷?";
  224. const completedText = "已完成所有题目,是否确认交卷?";
  225. const overlayloading = ref(false);
  226. // 在组件挂载时获取试卷
  227. onMounted(async () => {
  228. overlayloading.value = true;
  229. await saveChecUser();
  230. await getForm();
  231. overlayloading.value = false;
  232. });
  233. const handData = ref({});
  234. const saveChecUser = async () => {
  235. const examContext = getExamContext();
  236. console.log("接到的参数:",examContext);
  237. var url = '/sgsafe/ExamHead/saveChecUser';
  238. var param = {
  239. params: JSON.stringify(examContext)
  240. };
  241. const res = await proxy.$axios.post(url, param);
  242. if (res.data.code == '0') {
  243. handData.value = res.data.data;
  244. } else {
  245. showFailToast('操作失败!' + res.data.msg)
  246. }
  247. }
  248. //获取试卷
  249. const getForm = async () => {
  250. console.log("headId为",handData.value.id);
  251. var url = '/sgsafe/ExamLine/query'
  252. const query1 = ref({
  253. headId: handData.value.id
  254. })
  255. var param = {
  256. params: JSON.stringify(query1.value)
  257. }
  258. try {
  259. const res = await proxy.$axios.post(url, param);
  260. if (res.data.code === 0) {
  261. questions.value = res.data.data
  262. } else {
  263. console.log('操作失败!' + res.data.msg);
  264. }
  265. } catch (error) {
  266. console.log('请求出错:', questions);
  267. }
  268. };
  269. // 获取当前题目
  270. const currentQuestion = computed(() => {
  271. return questions.value[activeIndex.value];
  272. });
  273. // 获取题目类型对应的图片路径
  274. import danxuan from '@/assets/img/dx.svg'
  275. import duoxuanImg from '@/assets/img/ksdx.svg'
  276. import panduanImg from '@/assets/img/kspd.svg'
  277. const getQuestionTypeImage = (category) => {
  278. switch (category) {
  279. case "单选": // 单选
  280. return danxuan;
  281. case "多选": // 多选
  282. return duoxuanImg;
  283. case "判断": // 判断
  284. return panduanImg;
  285. default:
  286. return "";
  287. }
  288. };
  289. //返回答题首页
  290. const goBack = () => {
  291. router.push({
  292. path: "/dailyproblem"
  293. });
  294. };
  295. // 切换到下一题
  296. const nextQuestion = () => {
  297. if (activeIndex.value < questions.value.length - 1) {
  298. activeIndex.value++;
  299. }
  300. };
  301. // 切换到上一题
  302. const prevQuestion = () => {
  303. if (activeIndex.value > 0) {
  304. activeIndex.value--;
  305. }
  306. };
  307. const getUserAnswers = () => {
  308. let useranswers = [];
  309. questions.value.forEach((question) => {
  310. const userAnswer = userAnswers.value[question.id]; // 获取用户的答案
  311. let userAnswerString;
  312. if (Array.isArray(userAnswer)) {
  313. // 多选题,将数组转换为字符串
  314. userAnswerString = userAnswer.sort().join(""); // 排序并转换为字符串,如 "ABC"
  315. } else {
  316. // 单选题,直接是字符串
  317. userAnswerString = userAnswer || ""; // 如果未选择答案,则设为空字符串
  318. }
  319. // 将答案保存到 answers 数组中
  320. useranswers.push({
  321. id: question.id, // 题目 ID
  322. userAnswer: userAnswerString, // 用户的答案
  323. });
  324. });
  325. return useranswers;
  326. };
  327. //交卷
  328. const submitForm = async () => {
  329. overlayloading.value = true;
  330. try {
  331. let answers = getUserAnswers();
  332. //console.log('answers.value', answers)
  333. var url = '/sgsafe/ExamLine/appSaveMyScore'
  334. var param = {
  335. json: JSON.stringify(
  336. answers
  337. )
  338. }
  339. try {
  340. const res = await proxy.$axios.post(url, param);
  341. if (res.data.code === 0) {
  342. showSuccessToast("保存成功")
  343. } else {
  344. console.log('操作失败!' + res.data.msg);
  345. }
  346. } catch (error) {
  347. console.log('请求出错:', questions);
  348. }
  349. const courseId = handData.value.id;
  350. const today = new Date();
  351. const year = today.getFullYear();
  352. const month = String(today.getMonth() + 1).padStart(2, '0');
  353. const day = String(today.getDate()).padStart(2, '0');
  354. const todayStr = `${year}-${month}-${day}`;// ✅ 来自 saveChecUser 返回
  355. //开始判卷
  356. var url2 = '/sgsafe/Package/doProc'
  357. var param2 = {
  358. procName: 'safeplat.sxsp_grade_exam_p',
  359. param: JSON.stringify([courseId])
  360. }
  361. try {
  362. const res2 = await proxy.$axios.post(url2, param2);
  363. if (res2.data.code === 0) {
  364. consle.log("courseId:" + courseId + "判卷完成!")
  365. } else {
  366. console.log('操作失败!' + res.data.msg);
  367. }
  368. } catch (error) {
  369. console.log('请求出错:', questions);
  370. }
  371. overlayloading.value = false;
  372. //查询本日答题次数和分数
  373. var url3 = '/sgsafe/DailyExam/appQueryMyScore'
  374. const query3 = ref({
  375. userId: userId,
  376. examDate: todayStr,
  377. headId: courseId,
  378. })
  379. var param3 = {
  380. params: JSON.stringify(query3.value)
  381. }
  382. const res3 = await proxy.$axios.post(url3, param3);
  383. if (res3.data.code === 0) {
  384. console.log(res3.data)
  385. if (res3.data.data.dailyExamList.length==0){
  386. var url4='/sgsafe/ExamHead/queryByheadId'
  387. var param4 = {
  388. headId: courseId
  389. }
  390. const res4 = await proxy.$axios.post(url4, param4);
  391. if (res4.data.code === 0) {
  392. totalScore.value = res4.data.data.totalScore;
  393. }
  394. showConfirmDialog({
  395. message: "判卷完成",
  396. confirmButtonText: "查看本次答题结果",
  397. cancelButtonText: "退出答题"
  398. })
  399. .then(() => {
  400. showResult.value = true;
  401. })
  402. .catch(() => {
  403. router.replace({
  404. path: "/dailyproblem"
  405. });
  406. })
  407. }else {
  408. let times = res3.data.data.dailyExamList[0].examCounts;
  409. totalScore.value = res3.data.data.headScore;
  410. showConfirmDialog({
  411. message: "判卷完成",
  412. confirmButtonText: "查看本次答题结果",
  413. cancelButtonText: times == "3" ? "退出答题" : "继续答题",
  414. })
  415. .then(() => {
  416. showResult.value = true;
  417. })
  418. .catch(() => {
  419. if (times == "3") {
  420. router.replace({
  421. path: "/dailyproblem"
  422. });
  423. } else {
  424. router.push({
  425. path: "/dailyproblem"
  426. });
  427. }
  428. });
  429. }
  430. } else {
  431. console.log('操作失败!' + res3.data.msg);
  432. }
  433. } catch (error) {
  434. console.error("出错:", error);
  435. showFailToast("交卷失败");
  436. }
  437. };
  438. // 确认结果并返回
  439. const confirmResult = () => {
  440. showResult.value = false;
  441. // router.back();
  442. router.back()
  443. };
  444. // 检查是否所有题目都已作答
  445. const checkBeforeSubmit = () => {
  446. hasUnanswered.value = questions.value.some((question) => {
  447. const userAnswer = userAnswers.value[question.id];
  448. return (
  449. !userAnswer || (Array.isArray(userAnswer) && userAnswer.length === 0)
  450. );
  451. });
  452. confirmSubmitDialog.value = true;
  453. };
  454. // AI解析功能
  455. // AI解析相关变量
  456. const aiAnalysis = ref({}); // 存储每道题的AI解析内容
  457. const analysisLoading = ref({}); // 存储每道题的解析加载状态
  458. import { fetchHuaweiResponse } from "@/tools/deepseek.js";
  459. // 动态导入依赖
  460. let marked, DOMPurify;
  461. const initMarkdownLibs = async () => {
  462. try {
  463. // 尝试导入marked
  464. const markedModule = await import('marked');
  465. marked = markedModule.marked || markedModule.default || markedModule;
  466. // 尝试导入DOMPurify
  467. const dompurifyModule = await import('dompurify');
  468. DOMPurify = dompurifyModule.default || dompurifyModule;
  469. } catch (error) {
  470. console.warn('Markdown libraries not available, using plain text', error);
  471. // 如果导入失败,使用基础功能
  472. marked = {
  473. parse: (text) => text
  474. };
  475. DOMPurify = {
  476. sanitize: (html) => html
  477. };
  478. }
  479. };
  480. // 在组件挂载时初始化
  481. onMounted(() => {
  482. initMarkdownLibs();
  483. });
  484. // 生成AI解析
  485. const generateAIAnalysis = async (question, force = false) => {
  486. // 如果该题已有解析且不是强制重新生成,直接返回
  487. if (aiAnalysis.value[question.id] && !force) {
  488. return;
  489. }
  490. // 如果是重新解析,先清空之前的内容
  491. if (force) {
  492. aiAnalysis.value[question.id] = '';
  493. }
  494. // 确保依赖已加载
  495. if (!marked || !DOMPurify) {
  496. await initMarkdownLibs();
  497. }
  498. // 设置加载状态
  499. analysisLoading.value[question.id] = true;
  500. try {
  501. // 构造提示词
  502. let prompt = `请为以下题目提供详细解析:
  503. 题目类型:${question.category}题干:${question.stem}`;
  504. // 添加选项
  505. if (question.optionA) prompt += `\nA. ${question.optionA}`;
  506. if (question.optionB) prompt += `\nB. ${question.optionB}`;
  507. if (question.optionC) prompt += `\nC. ${question.optionC}`;
  508. if (question.optionD) prompt += `\nD. ${question.optionD}`;
  509. if (question.optionE) prompt += `\nE. ${question.optionE}`;
  510. prompt += `\n正确答案:${question.answer}`;
  511. // 添加用户答案(如果已作答)
  512. const userAnswer = userAnswers.value[question.id];
  513. if (userAnswer) {
  514. const userAnswerString = Array.isArray(userAnswer)
  515. ? userAnswer.sort().join("")
  516. : userAnswer;
  517. prompt += `\n用户答案:${userAnswerString}`;
  518. }
  519. prompt += `\n\n请提供以下内容:
  520. 1. 正确答案的解释
  521. 2. 为什么其他选项不正确(如果用户答案错误)
  522. 3. 相关知识点说明`;
  523. // 构造消息对象
  524. const messages = [
  525. {
  526. role: "user",
  527. content: prompt
  528. }
  529. ];
  530. // 调用AI接口
  531. fetchHuaweiResponse(
  532. messages,
  533. (content, isThinking, isEnd) => {
  534. // 实时更新解析内容
  535. aiAnalysis.value[question.id] = content;
  536. // 如果是最终结果,停止加载状态
  537. if (isEnd) {
  538. analysisLoading.value[question.id] = false;
  539. }
  540. },
  541. null
  542. );
  543. } catch (error) {
  544. console.error('AI解析生成失败:', error);
  545. analysisLoading.value[question.id] = false;
  546. aiAnalysis.value[question.id] = '解析生成失败';
  547. }
  548. };
  549. // 解析内容转换为HTML
  550. const renderAnalysis = (content) => {
  551. if (!content) return '';
  552. try {
  553. // 确保依赖已加载
  554. if (!marked || !DOMPurify) {
  555. return content.replace(/\n/g, '<br>');
  556. }
  557. const html = marked.parse ? marked.parse(content) : marked(content);
  558. return DOMPurify.sanitize ? DOMPurify.sanitize(html) : html;
  559. } catch (error) {
  560. console.error('Markdown解析错误:', error);
  561. return content.replace(/\n/g, '<br>');
  562. }
  563. };
  564. </script>
  565. <style scoped>
  566. .quiz-page {
  567. padding: 20px;
  568. }
  569. .question-type-img {
  570. width: 54px;
  571. height: 20px;
  572. }
  573. .question {
  574. margin-left: 20px;
  575. margin-right: 20px;
  576. }
  577. .kong {
  578. margin-bottom: 20px;
  579. }
  580. .footer {
  581. position: fixed;
  582. bottom: 0;
  583. left: 0;
  584. width: 100%;
  585. background-color: #fff;
  586. display: flex;
  587. justify-content: space-around;
  588. align-items: center;
  589. margin-bottom: 10px;
  590. }
  591. .van-dialog__message {
  592. text-align: center;
  593. }
  594. .questionBtn {
  595. width: 40%;
  596. }
  597. /* 遮罩 */
  598. .wrapper {
  599. display: flex;
  600. align-items: center;
  601. justify-content: center;
  602. height: 100%;
  603. }
  604. .van-overlay {
  605. z-index: 2;
  606. background-color: rgba(0, 0, 0, 0.5);
  607. }
  608. .ai-analysis-content {
  609. margin-top: 10px;
  610. padding: 10px;
  611. background-color: #f5f5f5;
  612. border-radius: 4px;
  613. font-size: 14px;
  614. line-height: 1.6;
  615. }
  616. .ai-analysis-content :deep(h1),
  617. .ai-analysis-content :deep(h2),
  618. .ai-analysis-content :deep(h3) {
  619. margin: 10px 0;
  620. font-weight: bold;
  621. font-size: 16px;
  622. }
  623. .ai-analysis-content :deep(p) {
  624. margin: 8px 0;
  625. }
  626. .ai-analysis-content :deep(ul),
  627. .ai-analysis-content :deep ol {
  628. padding-left: 20px;
  629. margin: 8px 0;
  630. }
  631. .ai-analysis-content :deep(li) {
  632. margin: 4px 0;
  633. }
  634. .ai-analysis-content :deep(code) {
  635. padding: 2px 4px;
  636. background-color: #e0e0e0;
  637. border-radius: 3px;
  638. font-family: monospace;
  639. }
  640. .ai-analysis-content :deep(pre) {
  641. padding: 10px;
  642. background-color: #e0e0e0;
  643. border-radius: 4px;
  644. overflow-x: auto;
  645. }
  646. </style>