index.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. <template>
  2. <StudyLayout>
  3. <div class="grid h-full w-full grid-cols-10 gap-6 overflow-hidden p-3">
  4. <div
  5. class="col-span-3 flex h-full flex-col overflow-hidden rounded-xl bg-white shadow-md"
  6. >
  7. <div class="bg-[var(--czr-main-sub-color)] px-4 py-3 text-white">
  8. <div class="flex items-center text-base font-bold">
  9. <i class="fas fa-book mr-2"></i>
  10. 知识点分类
  11. <div class="ml-auto flex space-x-1 font-normal">
  12. <div
  13. id="expandAll"
  14. class="text-subject-color flex items-center text-xs hover:underline"
  15. @click="ref_tree.expandAll()"
  16. >
  17. <i class="fas fa-plus-square mr-1"></i>
  18. <span>全部展开</span>
  19. </div>
  20. <div
  21. id="collapseAll"
  22. class="text-subject-color flex items-center text-xs hover:underline"
  23. @click="ref_tree.collapseAll()"
  24. >
  25. <i class="fas fa-minus-square mr-1"></i>
  26. <span>全部折叠</span>
  27. </div>
  28. </div>
  29. </div>
  30. </div>
  31. <div class="border-b border-b-[#e5e7eb] p-3">
  32. <div class="relative">
  33. <div class="">
  34. <q-input
  35. v-model="state.tree.text"
  36. outlined
  37. placeholder="搜索知识点..."
  38. :dense="true"
  39. >
  40. <template v-slot:prepend>
  41. <q-icon name="search" />
  42. </template>
  43. <template v-slot:append>
  44. <q-icon
  45. v-if="state.tree.text !== ''"
  46. name="clear"
  47. class="cursor-pointer"
  48. @click="state.tree.text = ''"
  49. />
  50. </template>
  51. </q-input>
  52. </div>
  53. </div>
  54. </div>
  55. <div class="flex-1 overflow-y-auto p-2" v-loading="state.tree.loading">
  56. <q-tree
  57. ref="ref_tree"
  58. :nodes="state.tree.data"
  59. node-key="value"
  60. label-key="label"
  61. v-model:selected="state.selected"
  62. :filter="state.tree.text"
  63. no-transition
  64. >
  65. <template v-slot:default-header="prop">
  66. <div class="flex w-full items-center text-sm">
  67. <div class="">
  68. {{ prop.node.label }}
  69. </div>
  70. <div class="ml-auto break-keep">
  71. {{ prop.node.total }}/{{ prop.node.totalMake }}题
  72. </div>
  73. </div>
  74. </template>
  75. </q-tree>
  76. </div>
  77. </div>
  78. <div
  79. class="col-span-7 flex h-full flex-col overflow-hidden rounded-xl bg-white shadow-md"
  80. >
  81. <div class="bg-[var(--czr-main-sub-color)] px-4 py-3 text-white">
  82. <div class="relative flex items-center text-base font-bold">
  83. <i class="fas fa-book mr-2"></i>
  84. 题目列表
  85. <span class="text-base" v-if="state.selected">
  86. ({{ treeMapCpt.get(state.selected)?.label }} - 共{{
  87. state.question.total
  88. }}题)
  89. </span>
  90. <div class="absolute right-2 ml-auto flex">
  91. <q-select
  92. class="select ml-4 w-[100px]"
  93. rounded
  94. standout="focus"
  95. v-model="state.question.type"
  96. :options="['错题', '正确题']"
  97. :dense="true"
  98. @update:modelValue="initQuestion"
  99. >
  100. </q-select>
  101. </div>
  102. </div>
  103. </div>
  104. <div
  105. class="flex-1 overflow-y-auto p-4"
  106. id="questionsContainer"
  107. v-loading="state.question.loading"
  108. >
  109. <template v-if="state.question.data?.length > 0">
  110. <listCom :data="state.question.data" />
  111. <!-- 分页控件 -->
  112. <div class="mt-6 flex justify-center">
  113. <div class="q-pa-lg flex-center flex">
  114. <q-pagination
  115. v-model="state.question.page"
  116. :max="Math.ceil(state.question.total / state.question.size)"
  117. :max-pages="6"
  118. boundary-numbers
  119. @update:modelValue="initQuestion"
  120. />
  121. </div>
  122. </div>
  123. </template>
  124. <template v-else>
  125. <div
  126. class="flex size-full items-center justify-center text-base font-semibold text-gray-700"
  127. >
  128. 暂无数据
  129. </div>
  130. </template>
  131. </div>
  132. </div>
  133. </div>
  134. </StudyLayout>
  135. </template>
  136. <script setup lang="ts">
  137. import { computed, onBeforeMount, onMounted, reactive, ref, watch } from 'vue'
  138. import StudyLayout from '@/views/study/components/study-layout.vue'
  139. import { Plus } from '@element-plus/icons-vue'
  140. import {
  141. trainingCampQuestionList,
  142. trainingCampQuestionTypeTree,
  143. } from '@/api/modules/study'
  144. import { useAppStore } from '@/stores'
  145. import { useRoute } from 'vue-router'
  146. import listCom from './list.vue'
  147. import { v4 } from 'uuid'
  148. const AppStore = useAppStore()
  149. const route = useRoute()
  150. const state: any = reactive({
  151. selected: null,
  152. tree: {
  153. text: '',
  154. loading: false,
  155. data: [],
  156. },
  157. question: {
  158. uuid: '',
  159. loading: false,
  160. type: '错题',
  161. data: [],
  162. total: 0,
  163. page: 1,
  164. size: 10,
  165. },
  166. })
  167. const ref_tree = ref()
  168. const treeMapCpt = computed(() => {
  169. const map = new Map()
  170. const deep = (arr) => {
  171. arr.forEach((v) => {
  172. map.set(v.value, v)
  173. if (v.children?.length > 0) {
  174. deep(v.children)
  175. }
  176. })
  177. }
  178. deep(state.tree.data)
  179. return map
  180. })
  181. const initTree = () => {
  182. state.tree.loading = true
  183. state.tree.data = []
  184. trainingCampQuestionTypeTree({
  185. category: AppStore.studentInfo?.grade,
  186. subject: route.name,
  187. studentId: AppStore.studentInfo?.studentId,
  188. level: 1,
  189. })
  190. .then(({ data }: any) => {
  191. const deep = (arr, parent: any = []) => {
  192. return arr.map((v) => {
  193. v.total = v.size || 0
  194. v.totalMake = v.questionSize || 0
  195. if (v.children?.length > 0) {
  196. v.children = deep(v.children, [...parent, v])
  197. } else {
  198. parent.forEach((p) => {
  199. p.total += v.size
  200. p.totalMake += v.questionSize
  201. })
  202. }
  203. return v
  204. })
  205. }
  206. state.tree.data = deep(data)
  207. })
  208. .finally(() => {
  209. state.tree.loading = false
  210. })
  211. }
  212. const initQuestion = () => {
  213. if (!state.selected) {
  214. return
  215. }
  216. state.question.loading = true
  217. state.question.data = []
  218. const uuid = v4()
  219. state.question.uuid = uuid + ''
  220. trainingCampQuestionList({
  221. pageNum: state.question.page,
  222. pageSize: state.question.size,
  223. category: AppStore.studentInfo?.grade,
  224. subject: route.meta.subjectId,
  225. correct: state.question.type !== '错题',
  226. questionTypeCode: treeMapCpt.value.get(state.selected).questionTypeCode,
  227. studentId: AppStore.studentInfo?.studentId,
  228. })
  229. .then(({ rows, total }: any) => {
  230. if (uuid === state.question.uuid) {
  231. state.question.data = rows
  232. state.question.total = total
  233. }
  234. })
  235. .finally(() => {
  236. state.question.loading = false
  237. })
  238. }
  239. watch(
  240. () => state.selected,
  241. (n) => {
  242. initQuestion()
  243. },
  244. )
  245. onMounted(() => {
  246. initTree()
  247. })
  248. onBeforeMount(() => {
  249. document.documentElement.style.setProperty(
  250. '--czr-quasar-color',
  251. 'var(--czr-main-color)',
  252. )
  253. })
  254. </script>
  255. <style lang="scss" scoped>
  256. $primary: red;
  257. .search-input:focus {
  258. box-shadow: 0 0 0 3px rgba(var(--czr-main-color-rgb), 0.2);
  259. }
  260. :deep(.select) {
  261. .ellipsis {
  262. color: #ffffff;
  263. }
  264. .q-select__dropdown-icon {
  265. color: #ffffff;
  266. }
  267. }
  268. :deep(.date) {
  269. .q-placeholder {
  270. color: #ffffff;
  271. }
  272. }
  273. :deep(.focus) {
  274. background: rgba(0, 0, 0, 0.1) !important;
  275. }
  276. :deep(.q-tree__node--selected),
  277. :deep(.q-tree__node--selected .q-tree__node-header-content) {
  278. color: var(--czr-main-color) !important;
  279. }
  280. // FILE (create it): src/quasar-variables.sass
  281. </style>