QuestionnaireCommit.vue 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075
  1. <template>
  2. <div class="main_container">
  3. <div class="page_item top_header">
  4. <el-button style="margin-right: 92px;" class="button_default_background" @click="router.go(-1)">
  5. <el-icon class="img"><ArrowLeftBold /></el-icon>
  6. 返回
  7. </el-button>
  8. <!-- 待完善 -->
  9. <!-- 如该任务勾选了所有单科统一模板后,则会出现"整卷填写”和“按题填写”的页面,默认按照整卷填写 -->
  10. <TabSearch tabType="box"
  11. :disabled="QuestionnaireModeDisabled"
  12. :defaultTabKey="defaultQuestionnaireMode" :tabList="[
  13. {
  14. label:'整卷填写',
  15. key:'full',
  16. },
  17. {
  18. label:'按题填写',
  19. key:'split',
  20. }]"
  21. @tabChange="HandleQuestionnaireModeChange"
  22. />
  23. <div class="info_tip">
  24. <span class="process_tip">总进度:{{ questionnaireProgress }}</span>
  25. <span class="time_tip">
  26. <img :src="redTimeIcon" alt="">
  27. 剩余时间:{{ deadlineCountDown?.day + '天' + deadlineCountDown?.hours + '时' + deadlineCountDown?.minutes + '分' }}</span>
  28. </div>
  29. </div>
  30. <div class="page_jg_20"> </div>
  31. <div class="center_content">
  32. <!-- 左侧 教师问卷列表 -->
  33. <div class="page_item left_teacher_nav">
  34. <div class="page_search">
  35. <div class="search_content">
  36. <div class="content_left">
  37. <div class="grade_title" style="color: #000;">授课老师</div>
  38. </div>
  39. </div>
  40. </div>
  41. <ul class="teacher_list">
  42. <li v-for="(teacher,index) in teacherList" :key="teacher.id"
  43. :class="['teacher_card',{active:currentActiveTeacherIndex === index},
  44. {disabled:IsSubjectQuestionnaireUnFinish(teacher.evaluationStatus)}]"
  45. @click="IsSubjectQuestionnaireUnFinish(teacher.evaluationStatus) ? '' : HandleSubjectTeacherChange(index,teacher)">
  46. <div class="teacher_tag" :style="{backgroundColor:subjectToColor(teacher.evaluationSubjectName)?.color}">
  47. {{ teacher.evaluationSubjectName }}
  48. </div>
  49. <div class="info_text">
  50. <div class="teacher_name">{{ teacher.teacherName }}</div>
  51. <div class="status"
  52. :style="{color:fillOutStatusToColor(teacher.evaluationStatus)?.color}"
  53. >{{ fillOutStatusToColor(teacher.evaluationStatus)?.label }}</div>
  54. </div>
  55. </li>
  56. </ul>
  57. </div>
  58. <!-- 中间 问卷内容 -->
  59. <div class="center_questionnaire">
  60. <!-- 问卷头部 -->
  61. <div class="questionnaire_head">
  62. <h1 class="head_title">{{ questionaireMsg?.assessmentTitle }}</h1>
  63. <p class="head_desc">{{ questionaireMsg?.assessmentIllustrate }}</p>
  64. </div>
  65. <!-- 整卷填写问卷内容 -->
  66. <template v-if="defaultQuestionnaireMode == 'full'">
  67. <QuestionnaireQuestionList
  68. :allowEdit="!isCurrentQuestionnaireTaskFinish"
  69. :questionaireData="questionaireMsg.questionData"
  70. v-model:questionListRef="questionListRef"
  71. @formListAnswerChange="HandleQuestionnaireFormChange"/>
  72. <div class="operation">
  73. <el-button :class="[isCurrentQuestionnaireTaskFinish ? 'is-disabled' : 'button_background']"
  74. @click="isCurrentQuestionnaireTaskFinish ? '' : HandleFinishReview()">评价完成</el-button>
  75. </div>
  76. </template>
  77. <!-- 按题填写问卷内容 -->
  78. <template v-if="defaultQuestionnaireMode == 'split'" >
  79. <!-- 单选 0-->
  80. <SingleSelectQuestionBatchFill
  81. v-if="currentEditQuestion.questionType === 0"
  82. v-model:isFinishEdit="isQuestionEdit"
  83. :titleIndex="currentSelectedQuestionIndex"
  84. :titleMsg="currentEditQuestion"
  85. @questionAnswerChange="HandleQuestionAnswerChange"
  86. :allowEdit="!isCurrentQuestionnaireTaskFinish"
  87. :customTableColumns="questionTableColumns"
  88. :defaultTableData="questionTableData"
  89. />
  90. <!-- 多选 1 -->
  91. <BatchSelectQuestionBatchFill
  92. v-if="currentEditQuestion.questionType === 1"
  93. v-model:isFinishEdit="isQuestionEdit"
  94. :titleIndex="currentSelectedQuestionIndex"
  95. :titleMsg="currentEditQuestion"
  96. @questionAnswerChange="HandleQuestionAnswerChange"
  97. :allowEdit="!isCurrentQuestionnaireTaskFinish"
  98. :customTableColumns="questionTableColumns"
  99. :defaultTableData="questionTableData"
  100. />
  101. <!-- 评分题 5-->
  102. <template v-if="currentEditQuestion.questionType === 5" >
  103. <!-- 数字评分 -->
  104. <ScoreQuestionBatchFill
  105. v-if="currentEditQuestion.evaluationType === 0"
  106. v-model:isFinishEdit="isQuestionEdit"
  107. :titleIndex="currentSelectedQuestionIndex"
  108. :titleMsg="currentEditQuestion"
  109. @questionAnswerChange="HandleQuestionAnswerChange"
  110. :allowEdit="!isCurrentQuestionnaireTaskFinish"
  111. />
  112. <!-- 星星评分 -->
  113. <StarRationQuestionBatchFill
  114. v-if="currentEditQuestion.evaluationType === 1"
  115. v-model:isFinishEdit="isQuestionEdit"
  116. :titleIndex="currentSelectedQuestionIndex"
  117. :titleMsg="currentEditQuestion"
  118. :allowEdit="!isCurrentQuestionnaireTaskFinish"
  119. @questionAnswerChange="HandleQuestionAnswerChange"
  120. />
  121. </template>
  122. <!-- 问答题 2-->
  123. <QuestionAndAnswerBatchFill
  124. v-if="currentEditQuestion.questionType === 2"
  125. v-model:isFinishEdit="isQuestionEdit"
  126. :titleIndex="currentSelectedQuestionIndex"
  127. :titleMsg="currentEditQuestion"
  128. :allowEdit="!isCurrentQuestionnaireTaskFinish"
  129. @questionAnswerChange="HandleQuestionAnswerChange"
  130. />
  131. <!-- 下一题 能否点击 —— 只针对必填项 需要变量判断 ———— 待完善--- -->
  132. <div class="operation">
  133. <el-button :class="[isQuestionEdit ? 'button_background' : 'is-disabled']"
  134. @click="isQuestionEdit ? HandleFinishCurrentQuestion() : ''">
  135. {{(currentSelectedQuestionIndex + 1) < questionaireMsg.questionData?.length ? '下一题' : '提交'}}
  136. </el-button>
  137. </div>
  138. </template>
  139. </div>
  140. <!-- 右侧 问卷题目列表 -->
  141. <div class="page_item right_title_nav">
  142. <div class="page_search">
  143. <div class="search_content">
  144. <div class="content_left">
  145. <div class="grade_title">问卷目录</div>
  146. </div>
  147. </div>
  148. </div>
  149. <ul class="question_list_brief">
  150. <li v-for="(question,index) in questionaireMsg.questionData" :key="question.id">
  151. <span @click="defaultQuestionnaireMode=='full' ?
  152. ScrollToItem(question.id) :
  153. (IsSplitQuestionnaireUnFinish(question.status) ? '' : HandleChangeQuestion(index,question)) "
  154. :title="question.questionTitleName"
  155. class="one_line_ellipsis"
  156. :class="[{active:defaultQuestionnaireMode=='split' && currentSelectedQuestionIndex == index,
  157. disabled:defaultQuestionnaireMode=='split' && IsSplitQuestionnaireUnFinish(question.status)}]">
  158. {{ index + 1 }}.{{ question.questionTitleName }}
  159. </span>
  160. <!-- <img v-if="defaultQuestionnaireMode=='split' && IsSplitQuestionnaireFinish(question.status)" :src="finishIcon" alt=""> -->
  161. <img v-if="IsSplitQuestionnaireFinish(question.studentAnswerStatus)" :src="finishIcon" alt="">
  162. <!-- question.studentAnswerStatus 学生答题是否完成 0 否 1 是-->
  163. </li>
  164. </ul>
  165. </div>
  166. </div>
  167. </div>
  168. </template>
  169. <script setup lang="ts">
  170. import { ref,onMounted, reactive, computed,watch } from 'vue';
  171. import { useRouter } from 'vue-router';
  172. import TabSearch from '@/baseComponents/TabSearch.vue';
  173. import QuestionnaireQuestionList from '@/components/QuestionnaireQuestionList.vue';
  174. import SingleSelectQuestionBatchFill from '@/components/singleSelectQuestionBatchFill/SingleSelectQuestionBatchFill.vue';
  175. import ScoreQuestionBatchFill from '@/components/scoreQuestionBatchFill/ScoreQuestionBatchFill.vue';
  176. import StarRationQuestionBatchFill from '@/components/starRationQuestionBatchFill/StarRationQuestionBatchFill.vue';
  177. import QuestionAndAnswerBatchFill from '@/components/questionAndAnswerBatchFill/QuestionAndAnswerBatchFill.vue';
  178. import BatchSelectQuestionBatchFill from '@/components/batchSelectQuestionBatchFill/BatchSelectQuestionBatchFill.vue';
  179. import { subjectToColor,fillOutStatusToColor,
  180. IsSubjectQuestionnaireUnFinish,IsSplitQuestionnaireFinish,IsSplitQuestionnaireUnFinish
  181. } from '@/components/commonDictConvert';
  182. import { useTimeCountDown } from '@/utils/dateTransform';
  183. import {getExcelColumn} from '@/utils/numberConvertExcelColumn'
  184. import { getEvaluationQuestionDataApi } from '@/api/questionnaireCommit'
  185. import redTimeIcon from '@/assets/icon/red_time_icon.svg'
  186. import finishIcon from '@/assets/studentQuestionnaire/select_icon.png'
  187. interface TeacherItm {
  188. id: string|number
  189. evaluationClassName: string //班级
  190. evaluationStatus: number //评价状态 0-待评价 1-进行中 2-已评价
  191. subjectType: number // 科目类型 0-班主任 1 科目(单科)
  192. teacherName: string //教师名称
  193. evaluationSubjectName:string //科目名称
  194. }
  195. const router = useRouter()
  196. // 倒计时相关参数
  197. const {
  198. DestoryInterval,
  199. deadlineCountDown,
  200. StartCounDown
  201. } = useTimeCountDown()
  202. /**
  203. * 1. 左侧教师相关变量
  204. */
  205. // 待评价教师列表
  206. const teacherList = ref<TeacherItm[]>([])
  207. /**
  208. * 2. 问卷整体数据
  209. */
  210. // 当前问卷任务是否全部完成 ———— 以及问卷是否结束
  211. const isCurrentQuestionnaireTaskFinish = ref(false) //true false
  212. // 问卷填写截止时间
  213. // const deadlineTime = ref('2026-07-28 12:30:00')
  214. const deadlineTime = computed(()=>{
  215. return questionaireMsg.value.deadlineTime
  216. })
  217. // 每个问卷的进度
  218. const questionnaireProgress= computed(()=>{
  219. return questionaireMsg.value.progress
  220. })
  221. // 模拟所有教师的问卷数据(目前只有两个)
  222. const mockTeacherQuestionnaireData = ref([
  223. // {
  224. // "id": "661146080469061632",
  225. // "createdAt": "2026-07-15 09:58",
  226. // "schoolId": "10007",
  227. // "teacherId": "510448965049651259",
  228. // "teacherName": "李敏",
  229. // "assessmentTitle": "大学生消费情况调查问卷-复制",
  230. // "assessmentIllustrate": "您好!这次问卷只是针对中小学现状的调查,为心理教育提供依据。本调查问卷无记名,完全保密,请同学们必须如实填写,以利于调查问卷的有效性!谢谢合作!",
  231. // "assessmentScore": "15.00",
  232. // "assessmentSortCode": 104,
  233. // "useStatus": 0,
  234. // "questionCount": null,
  235. // "fullScore": null,
  236. // "questionData": [
  237. // {
  238. // "id": "663805618586464256",
  239. // "createdAt": "2026-07-22 18:06:32",
  240. // "updatedAt": "2026-07-22 18:06:32",
  241. // "schoolId": "10007",
  242. // "assessmentId": "661146080469061632",
  243. // "questionClass": 0,
  244. // "questionTitleName": "您的性别*",
  245. // "questionType": 0,
  246. // "questionAnswerType": 1,
  247. // "questionSortRole": 0,
  248. // "questionSortCode": 1,
  249. // "questionScore": null,
  250. // "evaluationType": 0,
  251. // "answerData": [
  252. // {
  253. // "id": "663805618636795904",
  254. // "createdAt": "2026-07-22 18:06:32",
  255. // "updatedAt": "2026-07-22 18:06:32",
  256. // "schoolId": "10007",
  257. // "assessmentId": "661146080469061632",
  258. // "questionId": "663805618586464256",
  259. // "questionClass": null,
  260. // "questionSortCode": 1,
  261. // "answerName": "男",
  262. // "answerScore": null
  263. // },
  264. // {
  265. // "id": "663805618645184512",
  266. // "createdAt": "2026-07-22 18:06:32",
  267. // "updatedAt": "2026-07-22 18:06:32",
  268. // "schoolId": "10007",
  269. // "assessmentId": "661146080469061632",
  270. // "questionId": "663805618586464256",
  271. // "questionClass": null,
  272. // "questionSortCode": 2,
  273. // "answerName": "女",
  274. // "answerScore": null
  275. // }
  276. // ],
  277. // status:'done',
  278. // },
  279. // {
  280. // "id": "663805618678738944",
  281. // "createdAt": "2026-07-22 18:06:32",
  282. // "updatedAt": "2026-07-22 18:06:32",
  283. // "schoolId": "10007",
  284. // "assessmentId": "661146080469061632",
  285. // "questionClass": 0,
  286. // "questionTitleName": "每月消消消消消消消消消消消消消消消消消消消消消费多******用于哪些方面*",
  287. // "questionType": 1,
  288. // "questionAnswerType": 1,
  289. // "questionSortRole": 1,
  290. // "questionSortCode": 2,
  291. // "questionScore": null,
  292. // "evaluationType": 0,
  293. // "answerData": [
  294. // {
  295. // "id": "663805618703904768",
  296. // "createdAt": "2026-07-22 18:06:32",
  297. // "updatedAt": "2026-07-22 18:06:32",
  298. // "schoolId": "10007",
  299. // "assessmentId": "661146080469061632",
  300. // "questionId": "663805618678738944",
  301. // "questionClass": null,
  302. // "questionSortCode": 1,
  303. // "answerName": "伙食",
  304. // "answerScore": null
  305. // },
  306. // {
  307. // "id": "663805618708099072",
  308. // "createdAt": "2026-07-22 18:06:32",
  309. // "updatedAt": "2026-07-22 18:06:32",
  310. // "schoolId": "10007",
  311. // "assessmentId": "661146080469061632",
  312. // "questionId": "663805618678738944",
  313. // "questionClass": null,
  314. // "questionSortCode": 2,
  315. // "answerName": "购置衣物",
  316. // "answerScore": null
  317. // },
  318. // {
  319. // "id": "663805618708099073",
  320. // "createdAt": "2026-07-22 18:06:32",
  321. // "updatedAt": "2026-07-22 18:06:32",
  322. // "schoolId": "10007",
  323. // "assessmentId": "661146080469061632",
  324. // "questionId": "663805618678738944",
  325. // "questionClass": null,
  326. // "questionSortCode": 3,
  327. // "answerName": "日常交际",
  328. // "answerScore": null
  329. // }
  330. // ],
  331. // status:'done',
  332. // },
  333. // {
  334. // "id": "663805618745847808",
  335. // "createdAt": "2026-07-22 18:06:32",
  336. // "updatedAt": "2026-07-22 18:06:32",
  337. // "schoolId": "10007",
  338. // "assessmentId": "661146080469061632",
  339. // "questionClass": 0,
  340. // "questionTitleName": "你希望老师在哪里改进*",
  341. // "questionType": 2,
  342. // "questionAnswerType": 1,
  343. // "questionSortRole": 0,
  344. // "questionSortCode": 3,
  345. // "questionScore": null,
  346. // "evaluationType": 0,
  347. // "answerData": [],
  348. // status:'pending',
  349. // },
  350. // {
  351. // "id": "663805618766819328",
  352. // "createdAt": "2026-07-22 18:06:32",
  353. // "updatedAt": "2026-07-22 18:06:32",
  354. // "schoolId": "10007",
  355. // "assessmentId": "661146080469061632",
  356. // "questionClass": 1,
  357. // "questionTitleName": "你对本次问卷调查的感受",
  358. // "questionType": 5,
  359. // "questionAnswerType": 1,
  360. // "questionSortRole": 0,
  361. // "questionSortCode": 4,
  362. // "questionScore": "10.00",
  363. // "evaluationType": 0,
  364. // "answerData": [],
  365. // status:'unfinish',
  366. // },
  367. // {
  368. // "id": "663805618787790848",
  369. // "createdAt": "2026-07-22 18:06:32",
  370. // "updatedAt": "2026-07-22 18:06:32",
  371. // "schoolId": "10007",
  372. // "assessmentId": "661146080469061632",
  373. // "questionClass": 1,
  374. // "questionTitleName": "你对本次问卷调查的感受",
  375. // "questionType": 5,
  376. // "questionAnswerType": 1,
  377. // "questionSortRole": 0,
  378. // "questionSortCode": 5,
  379. // "questionScore": "5.00",
  380. // "evaluationType": 1,
  381. // "answerData": [],
  382. // status:'unfinish',
  383. // }
  384. // ]
  385. // },
  386. // {
  387. // "id": "661148998005952512",
  388. // "createdAt": "2026-07-15 10:10",
  389. // "schoolId": "10007",
  390. // "teacherId": "510448965049651259",
  391. // "teacherName": "李敏",
  392. // "assessmentTitle": "必填项传参测试",
  393. // "assessmentIllustrate": "必填项传参测试",
  394. // "assessmentScore": "0.00",
  395. // "assessmentSortCode": 101,
  396. // "useStatus": 1,
  397. // "questionCount": null,
  398. // "fullScore": null,
  399. // "questionData": [
  400. // {
  401. // "id": "661213148149264384",
  402. // "createdAt": "2026-07-15 14:24:59",
  403. // "updatedAt": "2026-07-15 14:24:59",
  404. // "schoolId": "10007",
  405. // "assessmentId": "661148998005952512",
  406. // "questionClass": 0,
  407. // "questionTitleName": "项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出",
  408. // "questionType": 1,
  409. // "questionAnswerType": 1,
  410. // "questionSortRole": 1,
  411. // "questionSortCode": 1,
  412. // "questionScore": null,
  413. // "evaluationType": 0,
  414. // "answerData": [
  415. // {
  416. // "id": "661213148170235904",
  417. // "createdAt": "2026-07-15 14:24:59",
  418. // "updatedAt": "2026-07-15 14:24:59",
  419. // "schoolId": "10007",
  420. // "assessmentId": "661148998005952512",
  421. // "questionId": "661213148149264384",
  422. // "questionClass": null,
  423. // "questionSortCode": 1,
  424. // "answerName": "项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出",
  425. // "answerScore": null
  426. // },
  427. // {
  428. // "id": "661213148174430208",
  429. // "createdAt": "2026-07-15 14:24:59",
  430. // "updatedAt": "2026-07-15 14:24:59",
  431. // "schoolId": "10007",
  432. // "assessmentId": "661148998005952512",
  433. // "questionId": "661213148149264384",
  434. // "questionClass": null,
  435. // "questionSortCode": 2,
  436. // "answerName": "项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出",
  437. // "answerScore": null
  438. // },
  439. // {
  440. // "id": "661213148178624512",
  441. // "createdAt": "2026-07-15 14:24:59",
  442. // "updatedAt": "2026-07-15 14:24:59",
  443. // "schoolId": "10007",
  444. // "assessmentId": "661148998005952512",
  445. // "questionId": "661213148149264384",
  446. // "questionClass": null,
  447. // "questionSortCode": 3,
  448. // "answerName": "项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出",
  449. // "answerScore": null
  450. // },
  451. // {
  452. // "id": "661213148182818816",
  453. // "createdAt": "2026-07-15 14:24:59",
  454. // "updatedAt": "2026-07-15 14:24:59",
  455. // "schoolId": "10007",
  456. // "assessmentId": "661148998005952512",
  457. // "questionId": "661213148149264384",
  458. // "questionClass": null,
  459. // "questionSortCode": 4,
  460. // "answerName": "项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出",
  461. // "answerScore": null
  462. // }
  463. // ]
  464. // },
  465. // {
  466. // "id": "661213148224761856",
  467. // "createdAt": "2026-07-15 14:24:59",
  468. // "updatedAt": "2026-07-15 14:24:59",
  469. // "schoolId": "10007",
  470. // "assessmentId": "661148998005952512",
  471. // "questionClass": 0,
  472. // "questionTitleName": "必填项测试1",
  473. // "questionType": 0,
  474. // "questionAnswerType": 1,
  475. // "questionSortRole": 1,
  476. // "questionSortCode": 2,
  477. // "questionScore": null,
  478. // "evaluationType": 0,
  479. // "answerData": [
  480. // {
  481. // "id": "661213148249927680",
  482. // "createdAt": "2026-07-15 14:24:59",
  483. // "updatedAt": "2026-07-15 14:24:59",
  484. // "schoolId": "10007",
  485. // "assessmentId": "661148998005952512",
  486. // "questionId": "661213148224761856",
  487. // "questionClass": null,
  488. // "questionSortCode": 1,
  489. // "answerName": "选项1",
  490. // "answerScore": null
  491. // },
  492. // {
  493. // "id": "661213148254121984",
  494. // "createdAt": "2026-07-15 14:24:59",
  495. // "updatedAt": "2026-07-15 14:24:59",
  496. // "schoolId": "10007",
  497. // "assessmentId": "661148998005952512",
  498. // "questionId": "661213148224761856",
  499. // "questionClass": null,
  500. // "questionSortCode": 2,
  501. // "answerName": "选项2",
  502. // "answerScore": null
  503. // }
  504. // ]
  505. // },
  506. // {
  507. // "id": "661213148283482112",
  508. // "createdAt": "2026-07-15 14:24:59",
  509. // "updatedAt": "2026-07-15 14:24:59",
  510. // "schoolId": "10007",
  511. // "assessmentId": "661148998005952512",
  512. // "questionClass": 0,
  513. // "questionTitleName": "选项溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出测试",
  514. // "questionType": 0,
  515. // "questionAnswerType": 1,
  516. // "questionSortRole": 0,
  517. // "questionSortCode": 3,
  518. // "questionScore": null,
  519. // "evaluationType": 0,
  520. // "answerData": [
  521. // {
  522. // "id": "661213148308647936",
  523. // "createdAt": "2026-07-15 14:24:59",
  524. // "updatedAt": "2026-07-15 14:24:59",
  525. // "schoolId": "10007",
  526. // "assessmentId": "661148998005952512",
  527. // "questionId": "661213148283482112",
  528. // "questionClass": null,
  529. // "questionSortCode": 1,
  530. // "answerName": "选项溢出测试选项溢出测试溢出测试选项溢出测试溢出测试选项溢出测试溢出测试选项溢出测试溢出测试选项溢出测试溢出测试选项溢出测试溢出测试选项溢出测试选项溢出测试选项溢出测试选项溢出测试",
  531. // "answerScore": null
  532. // },
  533. // {
  534. // "id": "661213148312842240",
  535. // "createdAt": "2026-07-15 14:24:59",
  536. // "updatedAt": "2026-07-15 14:24:59",
  537. // "schoolId": "10007",
  538. // "assessmentId": "661148998005952512",
  539. // "questionId": "661213148283482112",
  540. // "questionClass": null,
  541. // "questionSortCode": 2,
  542. // "answerName": "选项溢出测试选项溢出测试选项溢溢出测试选项溢出测试溢出测试选项溢出测试溢出测试选项溢出测试溢出测试选项溢出测试溢出测试选项溢出测试出测试选项溢出测试选项溢出测试",
  543. // "answerScore": null
  544. // },
  545. // {
  546. // "id": "661213148312842241",
  547. // "createdAt": "2026-07-15 14:24:59",
  548. // "updatedAt": "2026-07-15 14:24:59",
  549. // "schoolId": "10007",
  550. // "assessmentId": "661148998005952512",
  551. // "questionId": "661213148283482112",
  552. // "questionClass": null,
  553. // "questionSortCode": 3,
  554. // "answerName": "选项溢出测试选项溢出测试选项溢出测试溢出测试选项溢出测试溢出测试选项溢出测试溢出测试选项溢出测试溢出测试选项溢出测试溢出测试选项溢出测试溢出测试选项溢出测试选项溢出测试选项溢出测试",
  555. // "answerScore": null
  556. // },
  557. // {
  558. // "id": "661213148317036544",
  559. // "createdAt": "2026-07-15 14:24:59",
  560. // "updatedAt": "2026-07-15 14:24:59",
  561. // "schoolId": "10007",
  562. // "assessmentId": "661148998005952512",
  563. // "questionId": "661213148283482112",
  564. // "questionClass": null,
  565. // "questionSortCode": 4,
  566. // "answerName": "4354",
  567. // "answerScore": null
  568. // },
  569. // {
  570. // "id": "661213148321230848",
  571. // "createdAt": "2026-07-15 14:24:59",
  572. // "updatedAt": "2026-07-15 14:24:59",
  573. // "schoolId": "10007",
  574. // "assessmentId": "661148998005952512",
  575. // "questionId": "661213148283482112",
  576. // "questionClass": null,
  577. // "questionSortCode": 5,
  578. // "answerName": "789",
  579. // "answerScore": null
  580. // }
  581. // ]
  582. // }
  583. // ]
  584. // },
  585. ])
  586. // 模拟问卷响应数据
  587. const mockResData = computed(()=>mockTeacherQuestionnaireData.value?.[currentActiveTeacherIndex.value])
  588. // 最终渲染的整体问卷数据
  589. const questionaireMsg = computed(()=>{
  590. let addedQuestionList = mockResData.value?.questionData?.map(item=>{
  591. let options = item.answerData.map(opt=>{
  592. return{
  593. answerCode: opt.answerCode,
  594. content: opt.answerName,
  595. score: opt.answerScore,
  596. id: opt.answerId
  597. }
  598. })
  599. let extraConfig = []
  600. if(Number(item.questionAnswerType) === 1){
  601. extraConfig.push('required')
  602. }
  603. if(Number(item.questionSortRole) === 1){
  604. extraConfig.push('isHorizontal')
  605. }
  606. return {
  607. ...item,
  608. id:item.questionId,
  609. extraConfig, // 是否必答 required, 是否横排 isHorizontal
  610. options,
  611. answer:undefined
  612. }
  613. })
  614. return{
  615. assessmentTitle:mockResData.value?.assessmentName, //问卷标题
  616. assessmentIllustrate:mockResData.value?.assessmentIllustrate, //问卷简介
  617. // 问卷试题列表
  618. questionData:addedQuestionList,
  619. progress:mockResData.value?.progress,
  620. deadlineTime:mockResData.value?.endDateTime,
  621. }
  622. })
  623. // 历史选择的问卷填写方式
  624. // //无 '' , full 整卷填写 , split 按题填写
  625. const HistoryQuestionnaireFillMode = ref('')
  626. // 默认的问卷填写模式
  627. const defaultQuestionnaireMode = ref('full') // split full
  628. // 问卷填写模式是否允许修改
  629. const QuestionnaireModeDisabled = ref(false)
  630. // 问卷填写模式变更
  631. const HandleQuestionnaireModeChange = (val)=>{
  632. console.log('当前选中的值',val)
  633. defaultQuestionnaireMode.value = val
  634. }
  635. // 监听从接口获取到的 历史问卷填写方式,变更当前页面的模式和模式的禁选状态
  636. watch(()=>HistoryQuestionnaireFillMode.value,(newVal)=>{
  637. if(newVal){
  638. defaultQuestionnaireMode.value = newVal
  639. QuestionnaireModeDisabled.value = true
  640. }
  641. })
  642. /**
  643. * 3. 整卷填写相关变量 和操作
  644. */
  645. // 问卷试题 dom
  646. const questionListRef = ref()
  647. // 当前选中的待评价科目教师
  648. const currentActiveTeacherIndex = ref(0)
  649. // 整卷填写 教师问卷的答案
  650. const fullQuestionnaireAnswer = ref({})
  651. // 切换 评价的科目教师
  652. const HandleSubjectTeacherChange = (index,teacher)=>{
  653. currentActiveTeacherIndex.value = index
  654. currentSelectedQuestionIndex.value = 0
  655. }
  656. // 问卷试题定位—— 滚动定位到试题中的某一题
  657. const ScrollToItem = (id)=>{
  658. const targetElement = questionListRef.value.querySelector(`[id="${id}"]`);
  659. if (targetElement) {
  660. // 平滑滚动到目标元素,并使其居中显示
  661. targetElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
  662. }
  663. }
  664. // 接收问卷表单填写信息的变更
  665. const HandleQuestionnaireFormChange = (val)=>{
  666. fullQuestionnaireAnswer.value = val
  667. }
  668. // 问卷整卷提交
  669. const HandleFinishReview = ()=>{
  670. fullQuestionnaireAnswer.value = {}
  671. }
  672. /**
  673. * 4. 按题填写相关变量 和操作
  674. */
  675. const isQuestionEdit = ref(false) // 当前试题是否已经编辑完成
  676. const currentSelectedQuestionIndex = ref(0) // 当前选中的试题索引
  677. // 当前问卷题目的 教师评价答案列表
  678. const singleQuestionAnswer = ref([])
  679. // 当前编辑的试题
  680. const currentEditQuestion = computed(()=>{
  681. return questionaireMsg.value?.questionData?.[currentSelectedQuestionIndex.value]
  682. })
  683. // 按题填写的表格数据渲染 _ 不同题目类型渲染不同的内容
  684. const questionTableColumns = ref<any[]>([]) //按题填写的表列数据
  685. const questionTableData = ref<any[]>([]) //按题填写的表行数据
  686. watch(()=>currentEditQuestion.value?.questionType,(newVal)=>{
  687. console.log('teacherList',teacherList.value)
  688. console.log('questionaireMsg',questionaireMsg.value)
  689. switch(newVal){
  690. case 0:{ //单选
  691. let rowKeys = { }
  692. // 带答案的数据渲染 处理 ————
  693. // 待完善
  694. // —— 针对原始数据
  695. // 行循环
  696. // 列循环
  697. // 列循环
  698. // 行循环
  699. // 20 份答卷
  700. // 同一题目
  701. // 不同选项
  702. // 每个人的答案
  703. // 列循环
  704. // 表列(头)获取
  705. questionTableColumns.value = currentEditQuestion.value?.options?.map((item,index)=>{
  706. rowKeys[item.id] = ''
  707. return {
  708. name:item.id,
  709. label:`${getExcelColumn(index)}. `+item.content,
  710. width:'200',
  711. fixed:'',
  712. align:'left',
  713. minWdth:'',
  714. custom:true,
  715. }
  716. }) || []
  717. questionTableData.value = teacherList.value.map(item=>{
  718. return {
  719. teacher:item.teacherName,
  720. ...rowKeys //答案需要循环遍历
  721. }
  722. })
  723. console.log('准备渲染的表数据___',questionTableData.value)
  724. }
  725. case 1:{ //多选
  726. let rowKeys = { }
  727. questionTableColumns.value = currentEditQuestion.value?.options?.map((item,index)=>{
  728. rowKeys[item.id] = ''
  729. return {
  730. name:item.id,
  731. label:`${getExcelColumn(index)}. `+item.content,
  732. width:'200',
  733. fixed:'',
  734. align:'left',
  735. minWdth:'',
  736. custom:true,
  737. }
  738. }) || []
  739. questionTableData.value = teacherList.value.map(item=>{
  740. return {
  741. teacher:item.teacherName,
  742. ...rowKeys //答案需要循环遍历
  743. }
  744. })
  745. console.log('准备渲染的表数据___',questionTableColumns.value, 'dfdfff',questionTableData.value)
  746. break
  747. }
  748. case 5:{
  749. //评分题
  750. break
  751. }
  752. case 2:{ //问答题
  753. break
  754. }
  755. }
  756. },{immediate:true})
  757. // 切换当前选中的试题索引
  758. const HandleChangeQuestion = (index,question)=>{
  759. console.log('当前索引--',index)
  760. isQuestionEdit.value = false
  761. currentSelectedQuestionIndex.value = index
  762. }
  763. // 接收问卷试题答案的变更
  764. const HandleQuestionAnswerChange = (val)=>{
  765. singleQuestionAnswer.value = val
  766. }
  767. // 当前题目 有关所有教师的评价数据提交
  768. const HandleFinishCurrentQuestion = ()=>{
  769. console.log('当前题目的教师评价数据--',singleQuestionAnswer.value )
  770. if((currentSelectedQuestionIndex.value + 1) < questionaireMsg.value.questionData?.length){
  771. currentSelectedQuestionIndex.value += 1
  772. }
  773. singleQuestionAnswer.value = []
  774. isQuestionEdit.value = false
  775. }
  776. //
  777. // 处理页面的倒计时
  778. const HandleTimeDeadline = ()=>{
  779. DestoryInterval()
  780. // 获取到剩余时间后,开始执行倒计时
  781. StartCounDown(deadlineTime.value,1000)
  782. }
  783. onMounted( async ()=>{
  784. // 从父级获取 待评价教师的数据列表
  785. let localTeacherList = sessionStorage.getItem('teacherList')
  786. if(localTeacherList){
  787. teacherList.value = JSON.parse(localTeacherList || '[]')
  788. let teacherIds = teacherList.value.map(item=>{
  789. return item.id
  790. })
  791. let res = await getEvaluationQuestionDataApi(teacherIds)
  792. if(res?.code == 200){
  793. mockTeacherQuestionnaireData.value = res?.data
  794. }
  795. }
  796. // HandleTimeDeadline()
  797. })
  798. </script>
  799. <style lang="scss" scoped>
  800. .main_container{
  801. > *{
  802. flex-shrink: 0;
  803. }
  804. }
  805. .top_header{
  806. display: flex;
  807. align-items: center;
  808. justify-content: space-between;
  809. padding:10px 20px;
  810. .info_tip{
  811. margin-right: 240px;
  812. margin-left: 10px;
  813. flex: 1 1;
  814. display: flex;
  815. justify-content: flex-end;
  816. gap:40px;
  817. align-items: center;
  818. font-size: 14px;
  819. line-height: 24px;
  820. .process_tip{
  821. color: #2E64FA;
  822. }
  823. .time_tip{
  824. display: flex;
  825. align-items: center;
  826. gap:4px;
  827. color:#f56C6c;
  828. }
  829. }
  830. :deep(.button_default_background){
  831. padding:8px 10px;
  832. >span{
  833. gap:4px;
  834. }
  835. }
  836. }
  837. .center_content{
  838. display: flex;
  839. gap:20px;
  840. align-items: flex-start;
  841. height: calc(100% - 56px - 20px);
  842. overflow: auto;
  843. .left_teacher_nav{
  844. width: 160px;
  845. max-height: 100%;
  846. overflow: auto;
  847. .teacher_list{
  848. display: flex;
  849. flex-direction: column;
  850. gap:16px;
  851. margin-top: 16px;
  852. .teacher_card{
  853. display: flex;
  854. gap:8px;
  855. cursor: pointer;
  856. &.disabled{
  857. cursor:not-allowed
  858. }
  859. &.active{
  860. .status,
  861. .teacher_name{
  862. color:#2E64FA !important;
  863. }
  864. .teacher_name{
  865. font-weight: 600;
  866. }
  867. }
  868. .teacher_tag{
  869. display: flex;
  870. justify-content: center;
  871. align-items: center;
  872. width: 44px;
  873. height: 44px;
  874. border-radius: 4px 4px 4px 4px;
  875. font-weight: 600;
  876. font-size: 12px;
  877. color: #FFFFFF;
  878. }
  879. .info_text{
  880. display: flex;
  881. flex-direction: column;
  882. justify-content: space-between;
  883. .teacher_name{
  884. font-size: 14px;
  885. color: #333333;
  886. }
  887. .status{
  888. font-size: 12px;
  889. }
  890. }
  891. }
  892. }
  893. }
  894. .right_title_nav{
  895. width: 240px;
  896. max-height: 100%;
  897. overflow: auto;
  898. padding:20px 10px;
  899. .question_list_brief{
  900. display: flex;
  901. flex-direction: column;
  902. font-size: 14px;
  903. li{
  904. display: flex;
  905. align-items: center;
  906. justify-content: space-between;
  907. gap:8px;
  908. padding:10px 0;
  909. cursor: pointer;
  910. .disabled{
  911. cursor: not-allowed;
  912. }
  913. .active{
  914. color: #2E64FA;
  915. }
  916. img{
  917. width:16px;
  918. height:16px;
  919. }
  920. }
  921. }
  922. }
  923. .center_questionnaire {
  924. flex: 1 1;
  925. display: flex;
  926. flex-direction: column;
  927. border-radius: 10px;
  928. background-color: white;
  929. overflow: auto;
  930. height: 100%;
  931. padding:20px 0 116px 0;
  932. /* --- 头部样式 --- */
  933. .questionnaire_head {
  934. text-align: center;
  935. margin-bottom: 30px;
  936. .head_title {
  937. font-size: 24px;
  938. line-height: 34px;
  939. font-weight: bold;
  940. color: #000;
  941. margin-bottom: 10px;
  942. }
  943. .head_desc {
  944. font-size: 14px;
  945. color: #999999;
  946. line-height: 1.6;
  947. margin: 0;
  948. padding:0 60px;
  949. }
  950. }
  951. .operation{
  952. flex: 1 1;
  953. display: flex;
  954. align-items: flex-end;
  955. justify-content: center;
  956. .el-button{
  957. margin-top: 60px;
  958. width: 160px;
  959. height: 48px;
  960. }
  961. }
  962. }
  963. }
  964. </style>