暫無描述

index.vue 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. <script setup lang="ts">
  2. import { onMounted, ref } from 'vue'
  3. import { myEndExamList, myPendingExamList } from '@/api/exam'
  4. const examList = ref([
  5. {
  6. id: 1,
  7. status: '待考试',
  8. },
  9. {
  10. id: 2,
  11. status: '已考试',
  12. },
  13. ])
  14. const activeId = ref(1)
  15. const waitingForTheExam = ref([])
  16. const endExamList = ref([])
  17. // 滚动加载状态管理
  18. const isLoading = ref(false)
  19. const pendingTotal = ref(0)
  20. const endTotal = ref(0)
  21. const examStatusColorMap: any = {
  22. pending: {
  23. text: '待考试',
  24. color: '#215ACD',
  25. },
  26. ongoing: {
  27. text: '进行中',
  28. color: '#EB5E12',
  29. },
  30. absent: {
  31. text: '缺考',
  32. color: '#A4AAB2',
  33. },
  34. }
  35. function getStatus(row: any) {
  36. let status = ''
  37. if (new Date(row.examStartTime) > new Date()) {
  38. status = 'pending'
  39. }
  40. else if (new Date(row.examEndTime) > new Date()) {
  41. status = 'ongoing'
  42. }
  43. else {
  44. status = 'absent'
  45. }
  46. return status
  47. }
  48. // const reviewStatusOptions = [
  49. // { label: '待批改', value: 'unreviewed', color: '#EB5E12' },
  50. // { label: '批改中', value: 'reviewing', color: '#FFC107' },
  51. // { label: '已批改', value: 'reviewed', color: '#38B865' },
  52. // ]
  53. const endStatusColorMap = {
  54. 0: {
  55. text: '已出分',
  56. color: '#38B865',
  57. },
  58. 1: {
  59. text: '待出分',
  60. color: '#215ACD',
  61. },
  62. }
  63. function getEndStatus(row: any) {
  64. if (row.reviewStatus === 'unreviewed') {
  65. return endStatusColorMap[1]
  66. }
  67. return endStatusColorMap[0]
  68. }
  69. function toExamDetail(data) {
  70. if (data.Status === '未开始') {
  71. uni.navigateTo({
  72. url: '/pages/knowledge/exam/explanation/index',
  73. })
  74. }
  75. else if (data.Status === '进行中') {
  76. uni.navigateTo({
  77. url: '/pages/knowledge/exam/examcontent/index',
  78. })
  79. }
  80. }
  81. function ExamtakenDetail(data) {
  82. if (data.Status === '待出分') {
  83. uni.navigateTo({
  84. url: '/pages/knowledge/exam/examresults/scoresnotreleased',
  85. })
  86. }
  87. else if (data.Status === '已出分') {
  88. uni.navigateTo({
  89. url: '/pages/knowledge/exam/examresults/index',
  90. })
  91. }
  92. }
  93. const page = ref({
  94. currentPage: 1,
  95. pageSize: 10,
  96. })
  97. async function getExamList(loadMore = false) {
  98. try {
  99. isLoading.value = true
  100. if (activeId.value === 1) {
  101. const res: any = await myPendingExamList({
  102. pageNum: page.value.currentPage,
  103. pageSize: page.value.pageSize,
  104. })
  105. if (res.code === 200) {
  106. if (loadMore) {
  107. waitingForTheExam.value = [...waitingForTheExam.value, ...(res?.rows || [])]
  108. }
  109. else {
  110. waitingForTheExam.value = res?.rows
  111. }
  112. pendingTotal.value = res?.total
  113. }
  114. }
  115. else if (activeId.value === 2) {
  116. const res: any = await myEndExamList({
  117. pageNum: page.value.currentPage,
  118. pageSize: page.value.pageSize,
  119. })
  120. if (res.code === 200) {
  121. if (loadMore) {
  122. endExamList.value = [...endExamList.value, ...(res?.rows || [])]
  123. }
  124. else {
  125. endExamList.value = res?.rows
  126. }
  127. endTotal.value = res?.total
  128. }
  129. }
  130. }
  131. catch (err) {
  132. uni.showToast({
  133. title: '加载失败',
  134. icon: 'none',
  135. })
  136. }
  137. finally {
  138. isLoading.value = false
  139. }
  140. }
  141. function changeTab(id) {
  142. if (id === activeId.value) {
  143. return
  144. }
  145. activeId.value = id
  146. page.value.currentPage = 1
  147. getExamList()
  148. }
  149. // 滚动加载更多
  150. async function handleLoadMore() {
  151. if (isLoading.value)
  152. return
  153. const currentList = activeId.value === 1 ? waitingForTheExam.value : endExamList.value
  154. const currentTotal = activeId.value === 1 ? pendingTotal.value : endTotal.value
  155. if (currentList.length >= currentTotal) {
  156. uni.showToast({
  157. title: '没有更多数据了',
  158. icon: 'none',
  159. })
  160. return
  161. }
  162. page.value.currentPage++
  163. await getExamList(true)
  164. }
  165. function goDetail(item) {
  166. if (activeId.value === 1) {
  167. uni.navigateTo({
  168. url: `/pages/knowledge/exam/start?id=${item.id}`,
  169. })
  170. }
  171. else {
  172. ExamtakenDetail(item)
  173. }
  174. }
  175. onMounted(async () => {
  176. await getExamList()
  177. })
  178. </script>
  179. <template>
  180. <view class="knowledgebase_exam">
  181. <view class="header_nav">
  182. <view
  183. v-for="(item, index) in examList"
  184. :key="index"
  185. class="header_nav_item" :class="[{ active: item.id === activeId }]"
  186. @click="changeTab(item.id)"
  187. >
  188. {{ item.status }}
  189. <view v-if="item.id === activeId" class="active_line" />
  190. </view>
  191. </view>
  192. <scroll-view
  193. class="content"
  194. :scroll-y="true"
  195. @scrolltolower="handleLoadMore"
  196. >
  197. <view v-if="activeId === 1">
  198. <view
  199. v-for="(item, index) in waitingForTheExam"
  200. :key="index"
  201. class="content_item"
  202. @click="goDetail(item)"
  203. >
  204. <view class="content_item_title">
  205. <span class="content_item_title_name">{{ item.examName || '' }}</span>
  206. <span
  207. class="content_item_title_status"
  208. :style="{ 'background-color': examStatusColorMap[getStatus(item)].color }"
  209. >{{ examStatusColorMap[getStatus(item)].text }}</span>
  210. </view>
  211. <view class="content_item_time">
  212. {{ item.examStartTime }}
  213. </view>
  214. </view>
  215. </view>
  216. <view v-else>
  217. <view
  218. v-for="(item, index) in endExamList"
  219. :key="`else-${index}`"
  220. class="content_item"
  221. @click="goDetail(item)"
  222. >
  223. <view class="content_item_title">
  224. <span class="content_item_title_name">{{ item.examName || '' }}</span>
  225. <span
  226. class="content_item_title_status"
  227. :style="{ 'background-color': getEndStatus(item)?.color }"
  228. >{{ getEndStatus(item)?.text }}</span>
  229. </view>
  230. <view v-if="getEndStatus(item)?.text === '已出分'" class="content_item_result_score">
  231. <view class="content_item_result">
  232. 考试结果:<text
  233. :style="{ color: item.examScore >= item.totalScore ? '#38B865' : '#EB5E12' }"
  234. >
  235. {{ item.examScore >= item.totalScore ? '及格' : '不及格' }}
  236. </text>
  237. </view>
  238. <view class="content_item_score">
  239. 分数:<text>{{ item.examScore }}</text>
  240. </view>
  241. </view>
  242. <view class="content_item_time">
  243. {{ item.examStartTime }}
  244. </view>
  245. </view>
  246. </view>
  247. <!-- 加载中状态 -->
  248. <view v-if="isLoading" class="load-more">
  249. <text style="font-size: 24rpx; color: #86909c">加载中...</text>
  250. </view>
  251. <!-- 无更多数据提示 -->
  252. <view
  253. v-if="!isLoading && (activeId === 1 ? waitingForTheExam.length : endExamList.length) > 0 && (activeId === 1 ? waitingForTheExam.length >= pendingTotal : endExamList.length >= endTotal)"
  254. class="load-more"
  255. >
  256. <text style="font-size: 24rpx; color: #86909c">- 暂无更多数据 -</text>
  257. </view>
  258. </scroll-view>
  259. </view>
  260. </template>
  261. <style scoped lang="scss">
  262. html,
  263. body {
  264. overflow: hidden;
  265. }
  266. .knowledgebase_exam {
  267. height: 100vh;
  268. display: flex;
  269. flex-direction: column;
  270. align-items: center;
  271. padding: 24rpx;
  272. box-sizing: border-box;
  273. min-height: 0;
  274. }
  275. .header_nav {
  276. width: 100%;
  277. display: flex;
  278. gap: 24rpx;
  279. .header_nav_item {
  280. font-size: 32rpx;
  281. font-weight: 400;
  282. color: #4e5969;
  283. display: flex;
  284. flex-direction: column;
  285. align-items: center;
  286. gap: 8rpx;
  287. }
  288. .active {
  289. font-weight: 500;
  290. color: #31373d;
  291. }
  292. .active_line {
  293. width: 40%;
  294. height: 4rpx;
  295. border-radius: 8rpx;
  296. background-color: #215acd;
  297. }
  298. }
  299. .content {
  300. flex: 1;
  301. width: 100%;
  302. display: flex;
  303. flex-direction: column;
  304. gap: 24rpx;
  305. margin-top: 46rpx;
  306. overflow-y: auto;
  307. overflow-x: hidden;
  308. box-sizing: border-box;
  309. padding-bottom: 150rpx;
  310. .content_item {
  311. // height: 150rpx;
  312. border-bottom: 1px solid #eeeeee;
  313. display: flex;
  314. flex-direction: column;
  315. gap: 30rpx;
  316. padding-bottom: 24rpx;
  317. box-sizing: border-box;
  318. // justify-content: center;
  319. // gap: 12rpx;
  320. .content_item_title {
  321. display: flex;
  322. gap: 16rpx;
  323. align-items: flex-start;
  324. .content_item_title_name {
  325. font-weight: 500;
  326. font-style: 65 Medium;
  327. font-size: 32rpx;
  328. color: #31373d;
  329. overflow: hidden;
  330. text-overflow: ellipsis;
  331. display: -webkit-box;
  332. -webkit-box-orient: vertical;
  333. -webkit-line-clamp: 2;
  334. line-clamp: 2;
  335. max-width: calc(100% - 120rpx);
  336. }
  337. .content_item_title_status {
  338. padding: 10rpx;
  339. border-radius: 8rpx;
  340. font-size: 24rpx;
  341. font-weight: 400;
  342. font-style: 55 Regular;
  343. color: #ffffff;
  344. white-space: nowrap;
  345. }
  346. }
  347. .content_item_result_score {
  348. display: flex;
  349. gap: 24rpx;
  350. color: #31373d;
  351. font-size: 28rpx;
  352. }
  353. .content_item_time {
  354. font-size: 28rpx;
  355. font-weight: 400;
  356. font-style: 55 Regular;
  357. color: #86909c;
  358. }
  359. }
  360. :last-child {
  361. border-bottom: none;
  362. }
  363. }
  364. .load-more {
  365. width: 100%;
  366. height: 80rpx;
  367. display: flex;
  368. justify-content: center;
  369. align-items: center;
  370. margin-top: 20rpx;
  371. margin-bottom: 20rpx;
  372. }
  373. </style>