feat(teacher): 实现班级学生列表接口集成和分页功能
添加学生列表API接口并集成到班级页面 实现学生列表的分页加载、搜索和筛选功能 优化列表加载性能,添加请求取消控制
Showing
2 changed files
with
137 additions
and
136 deletions
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-06-23 11:46:21 | 2 | * @Date: 2025-06-23 11:46:21 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-06-25 15:48:41 | 4 | + * @LastEditTime: 2025-06-26 10:35:45 |
| 5 | * @FilePath: /mlaj/src/api/teacher.js | 5 | * @FilePath: /mlaj/src/api/teacher.js |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | */ | 7 | */ |
| ... | @@ -11,6 +11,7 @@ const Api = { | ... | @@ -11,6 +11,7 @@ const Api = { |
| 11 | TEACHER_GRADE_CLASS_LIST: '/srv/?a=user&t=teacher_grade_class_group_list', | 11 | TEACHER_GRADE_CLASS_LIST: '/srv/?a=user&t=teacher_grade_class_group_list', |
| 12 | TEACHER_FIND_SETTINGS: '/srv/?a=task&t=teacher_find_settings', | 12 | TEACHER_FIND_SETTINGS: '/srv/?a=task&t=teacher_find_settings', |
| 13 | TEACHER_ADD_TASK: '/srv/?a=task&t=teacher_add', | 13 | TEACHER_ADD_TASK: '/srv/?a=task&t=teacher_add', |
| 14 | + STUDENT_LIST: '/srv/?a=user&t=student_list', | ||
| 14 | } | 15 | } |
| 15 | 16 | ||
| 16 | /** | 17 | /** |
| ... | @@ -43,3 +44,14 @@ export const getTeacherFindSettingsAPI = (params) => fn(fetch.get(Api.TEACHER_FI | ... | @@ -43,3 +44,14 @@ export const getTeacherFindSettingsAPI = (params) => fn(fetch.get(Api.TEACHER_FI |
| 43 | * @returns {Object} data { id } | 44 | * @returns {Object} data { id } |
| 44 | */ | 45 | */ |
| 45 | export const setTeacherTaskAPI = (params) => fn(fetch.post(Api.TEACHER_ADD_TASK, params)) | 46 | export const setTeacherTaskAPI = (params) => fn(fetch.post(Api.TEACHER_ADD_TASK, params)) |
| 47 | + | ||
| 48 | +/** | ||
| 49 | + * 获取学员列表 | ||
| 50 | + * @param {*} grade_id 年级ID | ||
| 51 | + * @param {*} class_id 班级ID | ||
| 52 | + * @param {*} keyword 搜索 | ||
| 53 | + * @param {*} limit | ||
| 54 | + * @param {*} page | ||
| 55 | + * @returns {Object} data { count, user_list[{id, name, avatar, mobile, class_list[{id, class_name}], last_checkin_time, last_checkin_time_desc}] } | ||
| 56 | + */ | ||
| 57 | +export const getStudentListAPI = (params) => fn(fetch.get(Api.STUDENT_LIST, params)) | ... | ... |
| ... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
| 2 | * @Author: hookehuyr hookehuyr@gmail.com | 2 | * @Author: hookehuyr hookehuyr@gmail.com |
| 3 | * @Date: 2025-01-20 10:00:00 | 3 | * @Date: 2025-01-20 10:00:00 |
| 4 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 4 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 5 | - * @LastEditTime: 2025-06-25 17:22:37 | 5 | + * @LastEditTime: 2025-06-26 11:06:33 |
| 6 | * @FilePath: /mlaj/src/views/teacher/myClassPage.vue | 6 | * @FilePath: /mlaj/src/views/teacher/myClassPage.vue |
| 7 | * @Description: 我的班级页面 | 7 | * @Description: 我的班级页面 |
| 8 | --> | 8 | --> |
| ... | @@ -82,19 +82,19 @@ | ... | @@ -82,19 +82,19 @@ |
| 82 | <!-- 标题和搜索 --> | 82 | <!-- 标题和搜索 --> |
| 83 | <div class="p-4 border-b border-gray-100 bg-white"> | 83 | <div class="p-4 border-b border-gray-100 bg-white"> |
| 84 | <div class="flex items-center justify-between mb-3"> | 84 | <div class="flex items-center justify-between mb-3"> |
| 85 | - <h3 class="text-lg font-bold text-gray-800">班级成员 ({{ studentList.length }})</h3> | 85 | + <h3 class="text-lg font-bold text-gray-800">班级成员 ({{ studentCount }})</h3> |
| 86 | <div @click="showSortPopup = true" class="flex items-center text-sm text-gray-600 cursor-pointer"> | 86 | <div @click="showSortPopup = true" class="flex items-center text-sm text-gray-600 cursor-pointer"> |
| 87 | <span>{{ sortFilter }}</span> | 87 | <span>{{ sortFilter }}</span> |
| 88 | <van-icon name="arrow-down" size="14" class="ml-1" /> | 88 | <van-icon name="arrow-down" size="14" class="ml-1" /> |
| 89 | </div> | 89 | </div> |
| 90 | </div> | 90 | </div> |
| 91 | - <van-search v-model="searchKeyword" placeholder="请搜索" shape="round" @search="handleSearch" @input="handleSearch" /> | 91 | + <van-search v-model="searchKeyword" placeholder="请搜索" shape="round" @search="handleSearch" @input="handleSearchInput" /> |
| 92 | </div> | 92 | </div> |
| 93 | 93 | ||
| 94 | <!-- 学生列表 --> | 94 | <!-- 学生列表 --> |
| 95 | <div class="p-4"> | 95 | <div class="p-4"> |
| 96 | <van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"> | 96 | <van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"> |
| 97 | - <div v-for="student in filteredStudentList" :key="student.id" | 97 | + <div v-for="student in studentList" :key="student.id" |
| 98 | class="flex items-center justify-between py-3 border-gray-50 bg-white rounded-xl p-4 text-center shadow-sm mb-4" | 98 | class="flex items-center justify-between py-3 border-gray-50 bg-white rounded-xl p-4 text-center shadow-sm mb-4" |
| 99 | @click="handleStudentClick(student)"> | 99 | @click="handleStudentClick(student)"> |
| 100 | <div class="flex items-center flex-1"> | 100 | <div class="flex items-center flex-1"> |
| ... | @@ -107,7 +107,7 @@ | ... | @@ -107,7 +107,7 @@ |
| 107 | <font-awesome-icon v-else icon="mars" color="#ec4899" class="mr-2" style="font-size: 0.85rem;" /> | 107 | <font-awesome-icon v-else icon="mars" color="#ec4899" class="mr-2" style="font-size: 0.85rem;" /> |
| 108 | </div> | 108 | </div> |
| 109 | <div class="text-sm text-gray-500" style="text-align: left;"> | 109 | <div class="text-sm text-gray-500" style="text-align: left;"> |
| 110 | - <span v-for="(item, index) in student.class_list" :key="index" class="mr-2">{{ item }}</span> | 110 | + <span v-for="(item, index) in student.class_list" :key="index" class="mr-2">{{ item.class_name }}</span> |
| 111 | </div> | 111 | </div> |
| 112 | </div> | 112 | </div> |
| 113 | </div> | 113 | </div> |
| ... | @@ -116,7 +116,7 @@ | ... | @@ -116,7 +116,7 @@ |
| 116 | <van-icon name="phone-o" size="12" class="mr-1" /> | 116 | <van-icon name="phone-o" size="12" class="mr-1" /> |
| 117 | <span>{{ formatPhone(student.mobile) }}</span> | 117 | <span>{{ formatPhone(student.mobile) }}</span> |
| 118 | </div> | 118 | </div> |
| 119 | - <div class="text-xs text-gray-400">{{ student.last_checkin_time }}</div> | 119 | + <div class="text-xs text-gray-400">{{ student.last_checkin_time_desc }}</div> |
| 120 | </div> | 120 | </div> |
| 121 | <van-icon name="arrow" color="#d1d5db" size="16" class="ml-2" /> | 121 | <van-icon name="arrow" color="#d1d5db" size="16" class="ml-2" /> |
| 122 | </div> | 122 | </div> |
| ... | @@ -154,13 +154,13 @@ | ... | @@ -154,13 +154,13 @@ |
| 154 | </template> | 154 | </template> |
| 155 | 155 | ||
| 156 | <script setup> | 156 | <script setup> |
| 157 | -import { ref, computed, onMounted } from 'vue' | 157 | +import { ref, computed, onMounted, onUnmounted } from 'vue' |
| 158 | import { useRouter } from 'vue-router' | 158 | import { useRouter } from 'vue-router' |
| 159 | import AppLayout from '@/layouts/AppLayout.vue' | 159 | import AppLayout from '@/layouts/AppLayout.vue' |
| 160 | import { useTitle } from '@vueuse/core'; | 160 | import { useTitle } from '@vueuse/core'; |
| 161 | import { useAuth } from '@/contexts/auth' | 161 | import { useAuth } from '@/contexts/auth' |
| 162 | 162 | ||
| 163 | -import { getTeacherGradeClassListAPI } from "@/api/teacher"; | 163 | +import { getTeacherGradeClassListAPI, getStudentListAPI } from "@/api/teacher"; |
| 164 | 164 | ||
| 165 | const router = useRouter() | 165 | const router = useRouter() |
| 166 | const route = useRoute() | 166 | const route = useRoute() |
| ... | @@ -197,105 +197,18 @@ const taskCompletionRate = ref(76) | ... | @@ -197,105 +197,18 @@ const taskCompletionRate = ref(76) |
| 197 | const learningProgress = ref(92) | 197 | const learningProgress = ref(92) |
| 198 | 198 | ||
| 199 | // 学生列表 | 199 | // 学生列表 |
| 200 | -const studentList = ref([ | 200 | +const studentList = ref([]) |
| 201 | - { | 201 | + |
| 202 | - id: 1, | 202 | +const studentCount = ref(0); |
| 203 | - name: '张明', | ||
| 204 | - gender: 'male', | ||
| 205 | - class_list: ['高一(3)班', '高二(1)班', '高二(2)班'], | ||
| 206 | - mobile: '13812345678', | ||
| 207 | - last_checkin_time: '5分钟前活跃', | ||
| 208 | - avatar: 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' | ||
| 209 | - }, | ||
| 210 | - { | ||
| 211 | - id: 2, | ||
| 212 | - name: '李华', | ||
| 213 | - gender: 'female', | ||
| 214 | - class_list: ['高一(3)班', '高二(1)班', '高二(2)班'], | ||
| 215 | - mobile: '13987654321', | ||
| 216 | - last_checkin_time: '10分钟前活跃', | ||
| 217 | - avatar: 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' | ||
| 218 | - }, | ||
| 219 | - { | ||
| 220 | - id: 3, | ||
| 221 | - name: '王强', | ||
| 222 | - gender: 'male', | ||
| 223 | - class_list: ['高一(2)班', '高二(1)班', '高二(2)班'], | ||
| 224 | - mobile: '13512349876', | ||
| 225 | - last_checkin_time: '15分钟前活跃', | ||
| 226 | - avatar: 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' | ||
| 227 | - }, | ||
| 228 | - { | ||
| 229 | - id: 4, | ||
| 230 | - name: '赵敏', | ||
| 231 | - gender: 'female', | ||
| 232 | - class_list: ['高一(1)班', '高二(1)班', '高二(2)班'], | ||
| 233 | - mobile: '13643214321', | ||
| 234 | - last_checkin_time: '30分钟前活跃', | ||
| 235 | - avatar: 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' | ||
| 236 | - }, | ||
| 237 | - { | ||
| 238 | - id: 1, | ||
| 239 | - name: '张明', | ||
| 240 | - gender: 'male', | ||
| 241 | - class_list: ['高一(3)班', '高二(1)班', '高二(2)班'], | ||
| 242 | - mobile: '13812345678', | ||
| 243 | - last_checkin_time: '5分钟前活跃', | ||
| 244 | - avatar: 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' | ||
| 245 | - }, | ||
| 246 | - { | ||
| 247 | - id: 2, | ||
| 248 | - name: '李华', | ||
| 249 | - gender: 'female', | ||
| 250 | - class_list: ['高一(3)班'], | ||
| 251 | - mobile: '13987654321', | ||
| 252 | - last_checkin_time: '10分钟前活跃', | ||
| 253 | - avatar: 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' | ||
| 254 | - }, | ||
| 255 | - { | ||
| 256 | - id: 3, | ||
| 257 | - name: '王强', | ||
| 258 | - gender: 'male', | ||
| 259 | - class_list: ['高一(2)班'], | ||
| 260 | - mobile: '13512349876', | ||
| 261 | - last_checkin_time: '15分钟前活跃', | ||
| 262 | - avatar: 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' | ||
| 263 | - }, | ||
| 264 | - { | ||
| 265 | - id: 4, | ||
| 266 | - name: '赵敏', | ||
| 267 | - gender: 'female', | ||
| 268 | - class_list: ['高一(1)班'], | ||
| 269 | - mobile: '13643214321', | ||
| 270 | - last_checkin_time: '30分钟前活跃', | ||
| 271 | - avatar: 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' | ||
| 272 | - } | ||
| 273 | -]) | ||
| 274 | 203 | ||
| 275 | // 列表加载状态 | 204 | // 列表加载状态 |
| 276 | const loading = ref(false) | 205 | const loading = ref(false) |
| 277 | const finished = ref(false) | 206 | const finished = ref(false) |
| 207 | +const limit = ref(3) | ||
| 208 | +const page = ref(0) | ||
| 278 | 209 | ||
| 279 | -/** | 210 | +// 请求控制器,用于取消重复请求 |
| 280 | - * 过滤后的学生列表 | 211 | +let abortController = null; |
| 281 | - */ | ||
| 282 | -const filteredStudentList = computed(() => { | ||
| 283 | - let filtered = studentList.value | ||
| 284 | - | ||
| 285 | - // 按搜索关键词筛选 | ||
| 286 | - if (searchKeyword.value) { | ||
| 287 | - filtered = filtered.filter(student => | ||
| 288 | - student.name.toLowerCase().includes(searchKeyword.value.toLowerCase()) | ||
| 289 | - ) | ||
| 290 | - } | ||
| 291 | - | ||
| 292 | - // 排序 | ||
| 293 | - if (sortFilter.value === '按姓名') { | ||
| 294 | - filtered.sort((a, b) => a.name.localeCompare(b.name)) | ||
| 295 | - } | ||
| 296 | - | ||
| 297 | - return filtered | ||
| 298 | -}) | ||
| 299 | 212 | ||
| 300 | /** | 213 | /** |
| 301 | * 格式化手机号 | 214 | * 格式化手机号 |
| ... | @@ -308,52 +221,61 @@ const formatPhone = (phone) => { | ... | @@ -308,52 +221,61 @@ const formatPhone = (phone) => { |
| 308 | } | 221 | } |
| 309 | 222 | ||
| 310 | /** | 223 | /** |
| 224 | + * 重置分页参数并重新加载数据 | ||
| 225 | + */ | ||
| 226 | +const resetAndReload = () => { | ||
| 227 | + page.value = 0; | ||
| 228 | + studentList.value = []; | ||
| 229 | + finished.value = false; | ||
| 230 | + loading.value = true; | ||
| 231 | + onLoad(); | ||
| 232 | +} | ||
| 233 | + | ||
| 234 | +/** | ||
| 311 | * 处理年级筛选变化 | 235 | * 处理年级筛选变化 |
| 312 | - * @param {string} value - 选中的年级 | 236 | + * @param {string} val - 选中的年级 |
| 313 | */ | 237 | */ |
| 314 | const handleGradeChange = async (val) => { | 238 | const handleGradeChange = async (val) => { |
| 315 | console.log('val', val); | 239 | console.log('val', val); |
| 316 | selectGradeValue.value = val; | 240 | selectGradeValue.value = val; |
| 241 | + // 重置班级和课程选择 | ||
| 242 | + selectClassValue.value = null; | ||
| 243 | + selectCourseValue.value = null; | ||
| 244 | + | ||
| 317 | // 根据年级ID 更新过滤列表 | 245 | // 根据年级ID 更新过滤列表 |
| 318 | - getFilterList(val); | 246 | + await getFilterList(val); |
| 319 | - // 重置分页参数 | 247 | + |
| 320 | - // page.value = 0 | 248 | + // 重置分页参数并重新加载数据 |
| 321 | - // checkinDataList.value = [] | 249 | + resetAndReload(); |
| 322 | - // finished.value = false | ||
| 323 | - // // 重新加载数据 | ||
| 324 | - // onLoad() | ||
| 325 | } | 250 | } |
| 326 | 251 | ||
| 327 | /** | 252 | /** |
| 328 | * 处理班级筛选变化 | 253 | * 处理班级筛选变化 |
| 329 | - * @param {string} value - 选中的班级 | 254 | + * @param {string} val - 选中的班级 |
| 330 | */ | 255 | */ |
| 331 | -const handleClassChange = (val) => { | 256 | +const handleClassChange = async (val) => { |
| 332 | console.log('val', val); | 257 | console.log('val', val); |
| 333 | selectClassValue.value = val; | 258 | selectClassValue.value = val; |
| 259 | + // 重置课程选择 | ||
| 260 | + selectCourseValue.value = null; | ||
| 261 | + | ||
| 334 | // 根据年级ID和班级ID 更新过滤列表 | 262 | // 根据年级ID和班级ID 更新过滤列表 |
| 335 | - getFilterList(selectGradeValue.value, val); | 263 | + await getFilterList(selectGradeValue.value, val); |
| 336 | - // 重置分页参数 | 264 | + |
| 337 | - // page.value = 0 | 265 | + // 重置分页参数并重新加载数据 |
| 338 | - // checkinDataList.value = [] | 266 | + resetAndReload(); |
| 339 | - // finished.value = false | ||
| 340 | - // // 重新加载数据 | ||
| 341 | - // onLoad() | ||
| 342 | } | 267 | } |
| 343 | 268 | ||
| 344 | /** | 269 | /** |
| 345 | * 处理课程筛选变化 | 270 | * 处理课程筛选变化 |
| 346 | - * @param {string} value - 选中的课程 | 271 | + * @param {string} val - 选中的课程 |
| 347 | */ | 272 | */ |
| 348 | const handleCourseChange = (val) => { | 273 | const handleCourseChange = (val) => { |
| 349 | console.log('val', val); | 274 | console.log('val', val); |
| 350 | selectCourseValue.value = val; | 275 | selectCourseValue.value = val; |
| 351 | - // 重置分页参数 | 276 | + |
| 352 | - // page.value = 0 | 277 | + // 重置分页参数并重新加载数据 |
| 353 | - // checkinDataList.value = [] | 278 | + resetAndReload(); |
| 354 | - // finished.value = false | ||
| 355 | - // // 重新加载数据 | ||
| 356 | - // onLoad() | ||
| 357 | } | 279 | } |
| 358 | 280 | ||
| 359 | /** | 281 | /** |
| ... | @@ -374,12 +296,33 @@ const onSortSelect = (option) => { | ... | @@ -374,12 +296,33 @@ const onSortSelect = (option) => { |
| 374 | handleSortChange(option.value) | 296 | handleSortChange(option.value) |
| 375 | } | 297 | } |
| 376 | 298 | ||
| 299 | +// 搜索防抖定时器 | ||
| 300 | +let searchTimer = null; | ||
| 301 | + | ||
| 302 | +/** | ||
| 303 | + * 处理搜索输入 - 只更新显示值,不触发搜索 | ||
| 304 | + * @param {string} value - 搜索关键词 | ||
| 305 | + */ | ||
| 306 | +const handleSearchInput = (value) => { | ||
| 307 | + // 只更新输入框显示,不触发搜索 | ||
| 308 | + // searchKeyword 通过 v-model 自动更新 | ||
| 309 | +} | ||
| 310 | + | ||
| 377 | /** | 311 | /** |
| 378 | - * 处理搜索 | 312 | + * 处理搜索 - 立即搜索,无需防抖 |
| 379 | * @param {string} value - 搜索关键词 | 313 | * @param {string} value - 搜索关键词 |
| 380 | */ | 314 | */ |
| 381 | const handleSearch = (value) => { | 315 | const handleSearch = (value) => { |
| 382 | - console.log('搜索:', value) | 316 | + console.log('搜索:', value); |
| 317 | + | ||
| 318 | + // 清除之前的定时器 | ||
| 319 | + if (searchTimer) { | ||
| 320 | + clearTimeout(searchTimer); | ||
| 321 | + } | ||
| 322 | + | ||
| 323 | + // 立即执行搜索 | ||
| 324 | + searchKeyword.value = value; | ||
| 325 | + resetAndReload(); | ||
| 383 | } | 326 | } |
| 384 | 327 | ||
| 385 | /** | 328 | /** |
| ... | @@ -397,11 +340,42 @@ const handleStudentClick = (student) => { | ... | @@ -397,11 +340,42 @@ const handleStudentClick = (student) => { |
| 397 | /** | 340 | /** |
| 398 | * 加载更多数据 | 341 | * 加载更多数据 |
| 399 | */ | 342 | */ |
| 400 | -const onLoad = () => { | 343 | +const onLoad = async () => { |
| 401 | - setTimeout(() => { | 344 | + // 取消之前的请求 |
| 402 | - loading.value = false | 345 | + if (abortController) { |
| 403 | - finished.value = true | 346 | + abortController.abort(); |
| 404 | - }, 1000) | 347 | + } |
| 348 | + | ||
| 349 | + // 创建新的请求控制器 | ||
| 350 | + abortController = new AbortController(); | ||
| 351 | + | ||
| 352 | + const nextPage = page.value; | ||
| 353 | + | ||
| 354 | + try { | ||
| 355 | + const res = await getStudentListAPI({ | ||
| 356 | + limit: limit.value, | ||
| 357 | + page: nextPage, | ||
| 358 | + grade_id: selectGradeValue.value, | ||
| 359 | + class_id: selectClassValue.value, | ||
| 360 | + course_id: selectCourseValue.value, | ||
| 361 | + keyword: searchKeyword.value, | ||
| 362 | + }); | ||
| 363 | + | ||
| 364 | + if (res.code) { | ||
| 365 | + // 整理数据结构 | ||
| 366 | + studentList.value = [...studentList.value, ...res.data.user_list]; | ||
| 367 | + finished.value = res.data.user_list.length < limit.value; | ||
| 368 | + page.value = nextPage + 1; | ||
| 369 | + studentCount.value = res.data.count; | ||
| 370 | + } | ||
| 371 | + } catch (error) { | ||
| 372 | + // 如果是取消请求的错误,不需要处理 | ||
| 373 | + if (error.name !== 'AbortError') { | ||
| 374 | + console.error('加载学生列表失败:', error); | ||
| 375 | + } | ||
| 376 | + } finally { | ||
| 377 | + loading.value = false; | ||
| 378 | + } | ||
| 405 | } | 379 | } |
| 406 | 380 | ||
| 407 | const gradeList = ref([]); | 381 | const gradeList = ref([]); |
| ... | @@ -449,11 +423,26 @@ const getFilterList = async (grade_id=null, class_id=null) => { | ... | @@ -449,11 +423,26 @@ const getFilterList = async (grade_id=null, class_id=null) => { |
| 449 | * 组件挂载时初始化数据 | 423 | * 组件挂载时初始化数据 |
| 450 | */ | 424 | */ |
| 451 | onMounted(async () => { | 425 | onMounted(async () => { |
| 452 | - // 这里可以调用API获取实际数据 | ||
| 453 | - console.log('我的班级页面已加载') | ||
| 454 | // 获取老师的年级、班级、课程列表信息 | 426 | // 获取老师的年级、班级、课程列表信息 |
| 455 | getFilterList(); | 427 | getFilterList(); |
| 456 | }) | 428 | }) |
| 429 | + | ||
| 430 | +/** | ||
| 431 | + * 组件卸载时清理资源 | ||
| 432 | + */ | ||
| 433 | +onUnmounted(() => { | ||
| 434 | + // 清理搜索防抖定时器 | ||
| 435 | + if (searchTimer) { | ||
| 436 | + clearTimeout(searchTimer); | ||
| 437 | + searchTimer = null; | ||
| 438 | + } | ||
| 439 | + | ||
| 440 | + // 取消进行中的请求 | ||
| 441 | + if (abortController) { | ||
| 442 | + abortController.abort(); | ||
| 443 | + abortController = null; | ||
| 444 | + } | ||
| 445 | +}) | ||
| 457 | </script> | 446 | </script> |
| 458 | 447 | ||
| 459 | <style lang="less"> | 448 | <style lang="less"> | ... | ... |
-
Please register or login to post a comment