testTopic.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. <template>
  2. <div class="test-topic">
  3. <!-- 测试标题 -->
  4. <div class="test-title" @click="handleClick">
  5. <el-icon><ArrowLeftBold /></el-icon>
  6. {{ topicTitle }}
  7. </div>
  8. <!-- 测试内容 -->
  9. <div class="test-content">
  10. <!-- 左侧 题目 -->
  11. <div class="content-left">
  12. <div class="question-container">
  13. <!-- 添加题目进度显示 -->
  14. <div class="question-progress">
  15. 第 {{ currentQuestionIndex + 1 }}/{{ questions.length }} 题
  16. </div>
  17. <!-- 添加题目内容 -->
  18. <div class="question-content">
  19. <!-- 题目 -->
  20. <div class="question-title" v-html="questions[currentQuestionIndex]?.qcontent"></div>
  21. <!-- 选项 -->
  22. <div class="question-options">
  23. <el-radio-group
  24. v-model="selectedOption"
  25. size="large"
  26. class="radio-button-group custom-radio-group"
  27. >
  28. <el-radio-button
  29. v-for="option in questions[currentQuestionIndex]?.questionOptionsList || []"
  30. :key="option.oid"
  31. :label="option.oid"
  32. >{{ option.ovalue }}. {{ option.oname }}</el-radio-button
  33. >
  34. </el-radio-group>
  35. </div>
  36. </div>
  37. <!-- 添加题目导航按钮盒子 -->
  38. <div class="question-navigation">
  39. <el-button
  40. type="text"
  41. border
  42. class="prev-question-btn"
  43. @click="handlePrevQuestion"
  44. >上一题</el-button
  45. >
  46. <!-- 下一题按钮样式 最后一题显示为提交 -->
  47. <el-button
  48. type="primary"
  49. class="next-question-btn"
  50. @click="currentQuestionIndex === questions.length - 1 ? handleSubmit() : handleNextQuestion()"
  51. >{{ currentQuestionIndex === questions.length - 1 ? '提交' : '下一题' }}</el-button
  52. >
  53. </div>
  54. </div>
  55. </div>
  56. <!-- 右侧答题卡 -->
  57. <div class="content-right">
  58. <div class="center-box">
  59. <div class="box-title">答题卡</div>
  60. <div class="box-content">
  61. <div class="number-buttons">
  62. <el-button
  63. v-for="num in questions.length"
  64. :key="num"
  65. class="circle-btn"
  66. :class="{
  67. clicked: clickedNumbers.includes(num),
  68. current: num === currentQuestionIndex + 1
  69. }"
  70. @click="handleButtonClick(num)"
  71. >{{ num }}</el-button
  72. >
  73. </div>
  74. </div>
  75. </div>
  76. </div>
  77. </div>
  78. </div>
  79. </template>
  80. <script setup>
  81. import { ref,watch ,onMounted} from 'vue'
  82. import { ArrowLeftBold } from '@element-plus/icons-vue'
  83. import { useRouter,useRoute } from 'vue-router'
  84. import {QuestionTopicList, QuestionTopicSave} from '@/api/question/test.js'
  85. import { ElMessage } from 'element-plus'
  86. const router = useRouter()
  87. const route = useRoute()
  88. const selectedOption = ref(null)
  89. const clickedNumbers = ref([])
  90. // 当前题目索引
  91. const currentQuestionIndex = ref(0)
  92. const userAnswers = ref([]) // 存储用户答案
  93. const userQuestAnswers = ref([]) // 存储用户答案
  94. const handleClick = () => {
  95. router.push({
  96. path: '/evaluation',
  97. })
  98. }
  99. // 渲染列表标题
  100. const topicTitle = ref(
  101. route.query.title
  102. )
  103. // 添加题目数据
  104. const qrId = ref()
  105. const questResultId = ref()
  106. const questions = ref([])
  107. onMounted(()=>{
  108. // 获取路由参数中的id
  109. qrId.value = route.query.id
  110. if (qrId.value) {
  111. // 将id作为参数传递给QuestionTopicList接口
  112. QuestionTopicList({ qrId: qrId.value }).then(res=>{
  113. questResultId.value = res.data.qrResultId
  114. // 将接口返回的题目数据赋值给questions
  115. questions.value = res.data.questionnaire.questionList || []
  116. // 初始化用户答案数组
  117. userAnswers.value = new Array(questions.value.length).fill(null)
  118. // 初始化currentQuestionIndex
  119. if (questions.value.length > 0) {
  120. currentQuestionIndex.value = 0
  121. }
  122. console.log(questions.value);
  123. }).catch(err => {
  124. console.error('获取题目失败:', err)
  125. })
  126. }
  127. })
  128. // 上一题按钮逻辑
  129. const handlePrevQuestion = () => {
  130. if (currentQuestionIndex.value > 0) {
  131. // 保存当前题目的答案
  132. userAnswers.value[currentQuestionIndex.value] = {
  133. qid: questions.value[currentQuestionIndex.value].qid,
  134. oid: selectedOption.value
  135. };
  136. currentQuestionIndex.value--;
  137. // 加载上一题的答案
  138. selectedOption.value = userAnswers.value[currentQuestionIndex.value]?.oid || null;
  139. }
  140. }
  141. // 下一题按钮逻辑
  142. const handleNextQuestion = () => {
  143. if (currentQuestionIndex.value < questions.value.length - 1) {
  144. // 保存当前题目的答案
  145. userAnswers.value[currentQuestionIndex.value] = {
  146. qid: questions.value[currentQuestionIndex.value].qid,
  147. oid: selectedOption.value
  148. };
  149. currentQuestionIndex.value++;
  150. // 加载下一题的答案
  151. selectedOption.value = userAnswers.value[currentQuestionIndex.value]?.oid || null;
  152. }
  153. }
  154. // 提交
  155. const handleSubmit = () => {
  156. // 保存最后一题答案
  157. userAnswers.value[currentQuestionIndex.value] = {
  158. qid: questions.value[currentQuestionIndex.value].qid,
  159. oid: selectedOption.value
  160. };
  161. // 检查是否所有题目都已回答
  162. const allAnswered = userAnswers.value.every(answer => answer?.oid !== null && answer?.oid !== undefined)
  163. if(!allAnswered){
  164. ElMessage.error('检测到有题目未完成,请回答所有题目后再提交')
  165. return;
  166. }
  167. console.log("userAnswers.value----", qrId.value,questResultId.value)
  168. // 答完全部答完跳转到提交页面
  169. QuestionTopicSave({
  170. "qrId": Number(qrId.value),
  171. "qrResultId": Number(questResultId.value),
  172. "questionList": userAnswers.value
  173. }).then(res=>{
  174. ElMessage.success('提交成功')
  175. router.push('/testSubmit')
  176. })
  177. }
  178. // 答题卡
  179. const handleButtonClick = (num) => {
  180. // 切换到对应题目
  181. currentQuestionIndex.value = num - 1
  182. // 加载该题目的答案
  183. selectedOption.value = userAnswers.value[currentQuestionIndex.value]?.oid || null
  184. }
  185. // 监听选项变化,自动更新答题卡状态
  186. watch(selectedOption, (newVal) => {
  187. if (newVal !== null) {
  188. const currentQuestionNumber = currentQuestionIndex.value + 1
  189. // 保存当前题目的答案
  190. userAnswers.value[currentQuestionIndex.value] = {
  191. qid: questions.value[currentQuestionIndex.value].qid,
  192. oid: newVal
  193. }
  194. if (!clickedNumbers.value.includes(currentQuestionNumber)) {
  195. clickedNumbers.value.push(currentQuestionNumber)
  196. }
  197. }
  198. })
  199. </script>
  200. <style scoped lang="scss">
  201. @use 'sass:math';
  202. // 定义rpx转换函数
  203. @function rpx($px) {
  204. @return math.div($px, 750) * 100vw;
  205. }
  206. .test-topic {
  207. position: fixed;
  208. top: 0;
  209. left: 0;
  210. right: 0;
  211. bottom: 0;
  212. display: flex;
  213. flex-direction: column;
  214. background-color: #e2ddfc;
  215. gap: rpx(0);
  216. }
  217. .test-title {
  218. width: 100%;
  219. height: rpx(30);
  220. font-size: rpx(10);
  221. cursor: pointer;
  222. color: black;
  223. display: flex; /* 设置flex布局 */
  224. align-items: center; /* 垂直居中 */
  225. padding-left: rpx(15); /* 添加左侧内边距 */
  226. gap: rpx(5); /* 设置图标和文字间距 */
  227. }
  228. .test-content {
  229. width: 100%;
  230. height: 100%;
  231. display: flex; /* 使用flex布局使子元素并排 */
  232. gap: rpx(10); /* 左右盒子间距 */
  233. }
  234. // 左侧盒子样式 题目
  235. .content-left {
  236. flex: 1.5;
  237. border-radius: rpx(5);
  238. padding: rpx(10);
  239. }
  240. .question-container {
  241. width: 100%;
  242. height: 100%;
  243. padding-left: rpx(40);
  244. box-sizing: border-box;
  245. position: relative; // 相对定位
  246. }
  247. /* 添加题目进度样式 */
  248. .question-progress {
  249. font-size: rpx(12);
  250. font-weight: bold;
  251. color: #9e78e7;
  252. text-align: left;
  253. }
  254. // 题目
  255. .question-content {
  256. // margin-top: 20px;
  257. ::v-deep(.el-radio-button) {
  258. // 添加选项选中状态样式
  259. &.is-active .el-radio-button__inner {
  260. background-color: #9e78e7;
  261. border-color: #9e78e7;
  262. color: white;
  263. }
  264. }
  265. }
  266. .question-title {
  267. font-size: rpx(12);
  268. text-align: left;
  269. color: black;
  270. margin-bottom: 15px;
  271. line-height: 1.5;
  272. }
  273. .question-options {
  274. display: flex;
  275. flex-direction: column;
  276. gap: 10px;
  277. }
  278. .radio-button-group {
  279. display: flex;
  280. flex-direction: column;
  281. text-align: left;
  282. gap: 10px;
  283. }
  284. .el-radio-button {
  285. width: 100%;
  286. margin-top: rpx(10);
  287. justify-content: flex-start;
  288. }
  289. .el-radio-button ::v-deep(.el-radio-button__inner) {
  290. width: fit-content; //按钮宽度跟随文字多少变化
  291. height: rpx(25);
  292. display: flex;
  293. align-items: center;
  294. text-align: left;
  295. font-size: rpx(8);
  296. border-radius: rpx(5);
  297. color: black;
  298. border: 1px solid white;
  299. background: rgb(255, 255, 255, 0.5);
  300. }
  301. // 上一题 下一题按钮
  302. /* 添加导航按钮样式 */
  303. .question-navigation {
  304. display: flex;
  305. margin-top: rpx(20); // 与题目内容保持间距
  306. // 绝对定位到底部
  307. position: absolute;
  308. bottom: rpx(25);
  309. left: rpx(40);
  310. right: 0;
  311. // 上一题按钮样式
  312. ::v-deep(.prev-question-btn) {
  313. border: 1px solid #9e78e7; // 添加边框
  314. color: #9e78e7; // 设置文字颜色
  315. font-size: rpx(8);
  316. background-color: transparent; // 透明背景
  317. &:hover {
  318. background-color: #9e78e7; // hover效果
  319. color: white;
  320. }
  321. }
  322. // 下一题按钮样式
  323. ::v-deep(.next-question-btn) {
  324. background-color: #9e78e7; // 更换为紫色
  325. border-color: #9e78e7;
  326. font-size: rpx(8);
  327. &:hover {
  328. background-color: #8a63d2; // hover加深颜色
  329. border-color: #8a63d2;
  330. }
  331. }
  332. }
  333. .question-navigation .el-button {
  334. width: rpx(80); // 固定按钮宽度
  335. height: rpx(20);
  336. border-radius: rpx(5);
  337. }
  338. // 右侧盒子样式 答题卡
  339. .content-right {
  340. flex: 1;
  341. border-radius: rpx(5);
  342. padding: rpx(10);
  343. display: flex; /* 使用flex布局 */
  344. flex-direction: column; /* 垂直方向排列 */
  345. align-items: center; /* 水平居中 */
  346. justify-content: center; /* 垂直居中 */
  347. }
  348. // 答题卡
  349. .center-box {
  350. width: rpx(200); /* 宽度 */
  351. height: 100%; /* 高度 */
  352. background-color: #fefefe80;
  353. border-radius: rpx(5);
  354. border: 1px solid white;
  355. display: flex;
  356. flex-direction: column; /* 垂直排列子元素 */
  357. padding: rpx(10);
  358. gap: rpx(5); /* 标题和内容间距 */
  359. box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
  360. }
  361. // 标题盒子样式
  362. .box-title {
  363. width: 100%;
  364. height: rpx(20);
  365. font-size: rpx(11);
  366. font-weight: bold;
  367. border-radius: rpx(3);
  368. line-height: rpx(20);
  369. }
  370. // 内容盒子样式
  371. .box-content {
  372. height: 100%;
  373. border-radius: rpx(3);
  374. padding: rpx(5);
  375. overflow: auto; /* 内容溢出时显示滚动条 */
  376. }
  377. // 数字按钮容器
  378. .number-buttons {
  379. display: flex;
  380. justify-content: flex-start; // 将center改为flex-start实现左对齐
  381. gap: rpx(8);
  382. flex-wrap: wrap; // 添加换行属性
  383. }
  384. // 圆形按钮样式
  385. .circle-btn {
  386. width: rpx(25);
  387. height: rpx(25);
  388. margin-left: 0;
  389. border-radius: 50%; // 圆形显示
  390. display: flex;
  391. align-items: center;
  392. justify-content: center;
  393. font-size: rpx(10);
  394. background-color: transparent;
  395. border: 1px solid white;
  396. color: #909090;
  397. min-width: auto; // 移除Element Plus按钮默认最小宽度
  398. }
  399. .circle-btn.current {
  400. border: 2px solid #9e78e7;
  401. }
  402. .circle-btn:focus,
  403. .circle-btn:active,
  404. .circle-btn.clicked {
  405. color: white;
  406. background-color: #9e78e7;
  407. }
  408. .circle-btn:hover{
  409. border: 2px solid #9e78e7;
  410. }
  411. </style>