stepItem.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. <template>
  2. <div class="step_exam_item">
  3. <div class="left_line"></div>
  4. <div
  5. class="step_item"
  6. v-for="(item, index) in menuList"
  7. :key="index"
  8. :class="{ active: index === activeIndex, success: item.status === 1 }"
  9. >
  10. <div class="step_title" @click="MenuChange(index, item)">
  11. <div class="num_box">
  12. <!-- 假设 el-icon-success 是全局注册的组件或 CSS 类 -->
  13. <span v-if="item.status === 1" class="el-icon-success"></span>
  14. <span v-else class="step_num">{{ index + 1 }}</span>
  15. </div>
  16. <span>{{ item.name }}</span>
  17. </div>
  18. <div class="step_desc" v-if="item.name=='成绩查询'">
  19. <div class="menu_item" :class="childMenuPath==menu.path?'menu_item_active':''" v-for="menu in item.menu" @click="MenuSelect(menu)">
  20. {{menu.name}}
  21. </div>
  22. </div>
  23. <div v-else class="step_desc" @click="MenuChange(index, item)">
  24. {{ item.desc }}
  25. </div>
  26. </div>
  27. </div>
  28. </template>
  29. <script setup lang="ts">
  30. import { ref, computed, onMounted, watch } from 'vue'
  31. import { useRoute, useRouter } from 'vue-router'
  32. import { useExamStore } from '@/store/exam'
  33. const examStore = useExamStore()
  34. // --- 响应式数据 ---
  35. const route = useRoute()//读取当前路由对象
  36. const router = useRouter()//访问全局路由方法
  37. const stepOnIndex = ref<number>(0)//步骤索引
  38. const childMenuPath = ref<string>('')//子级菜单路径
  39. const menuListRaw = ref<any[]>([]) // 用于存储计算后的菜单列表,以便 watch 能检测到变化(如果需要)
  40. // --- 计算属性 ---
  41. // 1. 考试科目 ID
  42. const examSubjectId = computed(() => {
  43. // 替换为: return makeTemplateStore.examTempDetail?.id
  44. return examStore.currentExam?.id
  45. })
  46. // 2. 考试状态字符串
  47. const examStateStr = computed(() => {
  48. // 替换为: return makeTemplateStore.examStateStr
  49. return examStore.currentExam?.examStateStr
  50. })
  51. // 3. 考试类型 1 选择题 2 填空题 3 Ai作文
  52. const examType=computed(() => {
  53. return examStore.currentExam?.examType // 默认为选择题
  54. })
  55. // 5. 动态菜单列表
  56. const menuList = computed(() => {
  57. console.log('打印菜单examType', examSubjectId.value, examType.value)
  58. let list: any[] = []
  59. if(examType.value==1)
  60. {
  61. //选择题判分
  62. list = [
  63. { name: '试题结构', number: '1', path: 'question', desc: '建立考试试题结构、设置标答、分组等信息', status: 0 },
  64. { name: '扫描学生', number: '2', path: 'scanList', desc: '扫描学生,处理扫描异常学生等', status: 0 },
  65. { name: '成绩查询', number: '3', path: 'analysis', desc: '设置每个主观题下的划分区信息及位置', status: 0 ,
  66. menu:[
  67. { name: '成绩单', path: 'score' },
  68. { name: '错题分析', path: 'errorAnalysis' },
  69. { name: '选项明细', path: 'optionDetail' },
  70. { name: '水平分布', path: 'levelDistribution' },
  71. { name: '班级对比', path: 'classComparison' },
  72. { name: '小题分析', path: 'questionAnalysis' },
  73. { name: '分组分析', path: 'groupAnalysis' },
  74. { name: '选项分析', path: 'optionAnalysis' },
  75. { name: '命题分析', path: 'propositionAnalysis' },
  76. ]
  77. },
  78. ];
  79. }
  80. else if(examType.value==2)
  81. {
  82. //填空题判分
  83. }
  84. else
  85. {
  86. //Ai作文
  87. }
  88. // // 更新状态
  89. // const stateStr = String(examStateStr.value || '')
  90. // list.forEach((item) => {
  91. // if (stateStr.includes(item.number)) {
  92. // item.status = 1
  93. // } else {
  94. // item.status = 0
  95. // }
  96. // })
  97. return list
  98. })
  99. // 6. 激活的菜单索引
  100. const activeIndex = computed(() => {
  101. const currentPath = route.path
  102. // 特殊处理 scanStudentHome 页面
  103. if (currentPath.includes('scanStudentHome')) {
  104. const stepActiveIndex = menuList.value.findIndex(item => item.name === '扫描学生')
  105. return stepActiveIndex !== -1 ? stepActiveIndex : stepOnIndex.value
  106. }
  107. // 根据路由路径找到匹配的菜单项索引
  108. const pathMatchIndex = menuList.value.findIndex(item => currentPath.includes(item.path))
  109. return pathMatchIndex !== -1 ? pathMatchIndex : stepOnIndex.value
  110. })
  111. // --- 方法 ---
  112. // 获取模板数据
  113. const GetThirdCardData = () => {
  114. if (!examSubjectId.value) return
  115. const param = {
  116. examSubjectId: examSubjectId.value
  117. }
  118. // 假设 $api 已全局挂载或在 utils 中引入
  119. // import { examApi } from '@/api/exam'
  120. ;(window as any).$api?.exam.getExamThirdCardData(param).then((res: any) => {
  121. console.log('获取模板相关数据 包含第三方卡和系统卡', res)
  122. if (res.code === 200) {
  123. localStorage.setItem('templateData', JSON.stringify(res.data.examCardPageVOS))
  124. localStorage.setItem('usedCardType', res.data.usedCardType)
  125. // 替换为 Store Commit/Action
  126. // makeTemplateStore.setMarkType(res.data.markType)
  127. // makeTemplateStore.setUsedCardType(res.data.usedCardType)
  128. console.warn('请在此处调用 Store 的 action 更新 markType 和 usedCardType')
  129. }
  130. })
  131. }
  132. // // 获取考试流程状态
  133. // const GetExamState = () => {
  134. // const param = {
  135. // examSubjectId: route.query.id
  136. // }
  137. // ;(window as any).$api?.reviewBlock.getExamFlowStatus(param).then((res: any) => {
  138. // console.log('打印考试流程状态返回', res)
  139. // if (res.code === 200) {
  140. // const newStateStr = res.data.finished.join(',')
  141. // // 替换为 Store Commit/Action
  142. // // makeTemplateStore.setExamStateStr(newStateStr)
  143. // console.warn('请在此处调用 Store 的 action 更新 examStateStr')
  144. // }
  145. // })
  146. // }
  147. // 菜单点击事件
  148. const MenuChange = (index: number, item: any) => {
  149. console.log('点击菜单', index, item)
  150. stepOnIndex.value = index
  151. childMenuPath.value = ''
  152. // 触发父组件事件
  153. // 在 <script setup> 中,需要使用 defineEmits
  154. router.push('/exam/'+item.path)
  155. }
  156. //子级菜单选择事件
  157. const MenuSelect = (menu: any) => {
  158. console.log('选择的子菜单', menu)
  159. stepOnIndex.value = 2;
  160. childMenuPath.value = menu.path;
  161. // 执行路由跳转
  162. router.push(`/exam/analysis/${menu.path}`)
  163. }
  164. // --- 生命周期 & 监听 ---
  165. // 定义 emits
  166. const emit = defineEmits(['menuChange'])
  167. watch(examStateStr, (newVal) => {
  168. // 由于 menuList 是 computed 且依赖 examStateStr,它会自动重新计算
  169. // 如果这里有其他副作用,请保留
  170. console.log('考试状态发生变化', newVal)
  171. })
  172. onMounted(() => {
  173. // 初始化 stepOnIndex
  174. if (route.path.includes('scanStudentHome')) {
  175. stepOnIndex.value = 0
  176. }
  177. })
  178. </script>
  179. <style lang="scss" scoped>
  180. .step_exam_item {
  181. position: relative;
  182. height: 100%;
  183. .left_line {
  184. position: absolute;
  185. top: 0;
  186. left: 15.55px;
  187. z-index: 6;
  188. height: 100%;
  189. width: 0;
  190. border-left: 1px dashed #C0C4CC;
  191. }
  192. .step_item {
  193. width: 100%;
  194. position: relative;
  195. z-index: 7;
  196. border-top-left-radius: 10px;
  197. border-bottom-left-radius: 10px;
  198. cursor: pointer;
  199. .step_title {
  200. display: flex;
  201. align-items: center;
  202. justify-content: flex-start;
  203. width: 100%;
  204. height: 34px;
  205. font-size: 16px;
  206. color: #333333;
  207. line-height: 34px;
  208. font-weight: 600;
  209. border-top-left-radius: 10px;
  210. }
  211. .num_box {
  212. width: 30px;
  213. height: 100%;
  214. display: flex;
  215. justify-content: center;
  216. align-items: center;
  217. .el-icon-success::before {
  218. font-size: 19px;
  219. }
  220. }
  221. .step_num {
  222. display: block;
  223. width: 17px;
  224. height: 17px;
  225. font-size: 12px;
  226. text-align: center;
  227. font-weight: 500;
  228. line-height: 17px;
  229. border-radius: 50%;
  230. background-color: #B0B2B7;
  231. color: #fff;
  232. }
  233. .step_desc {
  234. padding: 0px 5px 6px 20px;
  235. font-size: 12px;
  236. line-height: 16px;
  237. color: #999999;
  238. .menu_item
  239. {
  240. font-weight: 400;
  241. font-size: 14px;
  242. color: #666666;
  243. line-height: 35px;
  244. text-indent: 10px;
  245. }
  246. .menu_item_active
  247. {
  248. color: #2e64fa;
  249. }
  250. }
  251. &.success {
  252. border-color: #fff;
  253. .num_box > span {
  254. color: #15bc83;
  255. }
  256. }
  257. &.active {
  258. border-color: #2e64fa;
  259. background-color: rgba(46, 100, 250, 0.1);
  260. box-shadow: inset 1px -1px 0px 1.5px #2e64fa;
  261. .step_title {
  262. background-color: #2e64fa;
  263. color: #fff;
  264. }
  265. .step_num {
  266. background-color: #ffff;
  267. color: #2e64fa;
  268. }
  269. .step_desc {
  270. background-color: rgba(46, 100, 250, 0.1);
  271. }
  272. }
  273. }
  274. /* 小屏幕笔记本的样式 常见分辨率:1366x768 */
  275. @media (max-height: 768px) {
  276. .step_item {
  277. margin-bottom: 0px;
  278. .step_title {
  279. font-size: 14px;
  280. line-height: 30px;
  281. font-weight: 700;
  282. }
  283. .step_desc {
  284. padding: 0px 5px 6px 20px;
  285. font-size: 10px;
  286. line-height: 15px;
  287. }
  288. }
  289. }
  290. /* 中等屏幕笔记本的样式(14-15英寸) 常见分辨率:1600x900, 1920x1080*/
  291. @media (min-height: 769px) and (max-height: 1080px) {
  292. .step_item {
  293. margin-bottom: 15px;
  294. .step_title {
  295. font-size: 16px;
  296. line-height: 30px;
  297. }
  298. .step_desc {
  299. padding: 0px 5px 6px 20px;
  300. font-size: 12px;
  301. line-height: 15px;
  302. }
  303. }
  304. }
  305. /* 大屏幕笔记本的样式(17-20英寸) 常见分辨率:2560x1440*/
  306. @media screen and (min-height: 1081px) {
  307. .step_item {
  308. margin-bottom: 25px;
  309. .step_title {
  310. font-size: 17px;
  311. line-height: 30px;
  312. }
  313. .step_desc {
  314. padding: 10px 5px 10px 20px;
  315. font-size: 14px;
  316. line-height: 15px;
  317. }
  318. }
  319. }
  320. }
  321. </style>