248 lines
7.6 KiB
Vue
248 lines
7.6 KiB
Vue
<script setup lang="ts">
|
|
import type { StudentInfo } from '@/service/types'
|
|
import { useQuery } from '@tanstack/vue-query'
|
|
import { whenever } from '@vueuse/core'
|
|
import { computed, onMounted, ref } from 'vue'
|
|
import StudentListItem from '@/components/student/StudentListItem.vue'
|
|
import { teacherAnalysisAttentionStudentUsingPost, teacherAnalysisExportStudentListUsingPost, teacherAnalysisStudentListUsingPost } from '@/service/laoshichengjifenxi'
|
|
import { useHomeStore } from '@/store/home'
|
|
|
|
const homeStore = useHomeStore()
|
|
const searchQuery = ref('')
|
|
|
|
// 检查是否可以加载学生数据
|
|
const canLoadStudents = computed(() => {
|
|
return homeStore.selectedClassId && homeStore.selectedExamId && homeStore.selectedExamSubjectId && homeStore.selectedGradeKey
|
|
})
|
|
|
|
// 使用 TanStack Query 获取学生数据
|
|
const {
|
|
data: studentsData,
|
|
isLoading: loading,
|
|
error,
|
|
refetch,
|
|
} = useQuery({
|
|
queryKey: computed(() => [
|
|
'students',
|
|
homeStore.selectedClassId,
|
|
homeStore.selectedExamId,
|
|
homeStore.selectedExamSubjectId,
|
|
homeStore.selectedGradeKey,
|
|
searchQuery.value,
|
|
]),
|
|
queryFn: async () => {
|
|
const response = await teacherAnalysisStudentListUsingPost({
|
|
body: {
|
|
class_id: homeStore.selectedClassId!,
|
|
exam_subject_id: homeStore.selectedExamSubjectId!,
|
|
grade_id: homeStore.selectedGradeKey!,
|
|
keyword: searchQuery.value || undefined,
|
|
},
|
|
})
|
|
|
|
return response || { attentioned_students: [], other_students: [] }
|
|
},
|
|
enabled: computed(() => !!canLoadStudents.value),
|
|
staleTime: 30000,
|
|
gcTime: 300000,
|
|
})
|
|
|
|
// 计算学生列表
|
|
const attentionedStudents = computed(() => studentsData.value?.attentioned_students || [])
|
|
const otherStudents = computed(() => studentsData.value?.other_students || [])
|
|
|
|
// 跳转到学生详情
|
|
function goToStudentDetail(student: StudentInfo) {
|
|
uni.navigateTo({
|
|
url: `/pages/student/detail?info_id=${student.info_id}&student_number=${student.student_number}&student_name=${student.student_name}`,
|
|
})
|
|
}
|
|
|
|
// 切换关注状态
|
|
async function toggleAttention(student: StudentInfo, isAttentioned: boolean) {
|
|
if (!student.student_number) {
|
|
return uni.showToast({ title: '学生信息缺少学号', icon: 'error' })
|
|
}
|
|
|
|
uni.showLoading({ title: '处理中...' })
|
|
|
|
try {
|
|
await teacherAnalysisAttentionStudentUsingPost({
|
|
body: { student_number: student.student_number, is_attention: !isAttentioned },
|
|
})
|
|
uni.showToast({ title: '操作成功', icon: 'success' })
|
|
refetch()
|
|
}
|
|
catch (error) {
|
|
console.error('关注学生失败:', error)
|
|
uni.showToast({ title: '操作失败', icon: 'error' })
|
|
}
|
|
finally {
|
|
uni.hideLoading()
|
|
}
|
|
}
|
|
|
|
// 下载功能
|
|
async function handleDownload() {
|
|
try {
|
|
uni.showLoading({ title: '准备下载...' })
|
|
|
|
const response = await teacherAnalysisExportStudentListUsingPost({
|
|
body: {
|
|
class_id: homeStore.selectedClassId!,
|
|
exam_subject_id: homeStore.selectedExamSubjectId!,
|
|
grade_id: homeStore.selectedGradeKey!,
|
|
},
|
|
})
|
|
|
|
if (response) {
|
|
// 构建完整的下载URL
|
|
const downloadUrl = `${import.meta.env.VITE_API_BASE_URL || ''}${response}`
|
|
|
|
// 在H5环境下直接打开下载链接
|
|
// #ifdef H5
|
|
window.open(downloadUrl, '_blank')
|
|
// #endif
|
|
|
|
// 在小程序和APP环境下使用uni.downloadFile
|
|
// #ifndef H5
|
|
uni.downloadFile({
|
|
url: downloadUrl,
|
|
success: (res) => {
|
|
if (res.statusCode === 200) {
|
|
uni.showToast({ title: '下载成功', icon: 'success' })
|
|
}
|
|
},
|
|
fail: () => {
|
|
uni.showToast({ title: '下载失败', icon: 'error' })
|
|
},
|
|
})
|
|
// #endif
|
|
|
|
uni.showToast({ title: '开始下载', icon: 'success' })
|
|
}
|
|
else {
|
|
uni.showToast({ title: '获取下载链接失败', icon: 'error' })
|
|
}
|
|
}
|
|
catch (error) {
|
|
console.error('下载学生数据失败:', error)
|
|
uni.showToast({ title: '下载失败', icon: 'error' })
|
|
}
|
|
finally {
|
|
uni.hideLoading()
|
|
}
|
|
}
|
|
|
|
// 分享功能
|
|
const handleShare = () => uni.showToast({ title: '分享功能开发中', icon: 'none' })
|
|
|
|
// 处理查询错误
|
|
whenever(() => error.value, (err) => {
|
|
if (err) {
|
|
console.error('加载学生数据失败:', err)
|
|
uni.showToast({ title: '加载失败', icon: 'error' })
|
|
}
|
|
})
|
|
|
|
onMounted(async () => {
|
|
if (homeStore.examOptions.length === 0) {
|
|
await homeStore.fetchOptions()
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<view class="min-h-screen bg-gray-50">
|
|
<!-- 标题栏 -->
|
|
<view class="flex items-center justify-between border-b border-gray-100 bg-white px-4 py-3">
|
|
<text class="text-lg text-gray-800 font-semibold">学生名单</text>
|
|
<view class="flex items-center gap-3">
|
|
<view class="i-fluent:arrow-download-20-regular text-lg text-gray-600" @click="handleDownload" />
|
|
<view class="i-fluent:share-20-regular text-lg text-gray-600" @click="handleShare" />
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 选择器区域 -->
|
|
<view class="border-b border-gray-100 bg-white px-4">
|
|
<wd-drop-menu>
|
|
<wd-drop-menu-item v-model="homeStore.selectedClassId" :options="homeStore.classOptions" />
|
|
<wd-drop-menu-item v-model="homeStore.selectedExamId" :options="homeStore.examOptions" />
|
|
<wd-drop-menu-item v-model="homeStore.selectedExamSubjectId" :options="homeStore.subjectOptions" />
|
|
</wd-drop-menu>
|
|
</view>
|
|
|
|
<!-- 搜索框 -->
|
|
<view class="border-b border-gray-100 bg-white px-4">
|
|
<wd-search v-model="searchQuery" placeholder="请输入学生姓名" hide-cancel />
|
|
</view>
|
|
|
|
<!-- 学生列表 -->
|
|
<view v-if="!loading && (attentionedStudents.length > 0 || otherStudents.length > 0)">
|
|
<!-- 重点关注的学生 -->
|
|
<view v-if="attentionedStudents.length > 0">
|
|
<view class="flex items-center bg-white px-4 py-3">
|
|
<view class="i-fluent:star-20-filled mr-2 text-yellow-500" />
|
|
<text class="text-sm text-gray-700 font-medium">重点关注的学生</text>
|
|
</view>
|
|
|
|
<StudentListItem
|
|
v-for="(student, index) in attentionedStudents"
|
|
:key="student.info_id"
|
|
:student="student"
|
|
:index="index + 1"
|
|
:is-attentioned="true"
|
|
@click="goToStudentDetail"
|
|
@toggle-attention="toggleAttention(student, true)"
|
|
/>
|
|
</view>
|
|
|
|
<!-- 其他学生 -->
|
|
<view v-if="otherStudents.length > 0">
|
|
<view class="mt-2 flex items-center bg-white px-4 py-3">
|
|
<text class="text-sm text-gray-700 font-medium">学生列表</text>
|
|
</view>
|
|
|
|
<StudentListItem
|
|
v-for="(student, index) in otherStudents"
|
|
:key="student.info_id"
|
|
:student="student"
|
|
:index="attentionedStudents.length + index + 1"
|
|
:is-attentioned="false"
|
|
@click="goToStudentDetail"
|
|
@toggle-attention="toggleAttention(student, false)"
|
|
/>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 空状态 -->
|
|
<wd-status-tip
|
|
v-if="!loading && !canLoadStudents"
|
|
image="search"
|
|
tip="请选择班级、考试和科目"
|
|
/>
|
|
|
|
<wd-status-tip
|
|
v-else-if="!loading && canLoadStudents && attentionedStudents.length === 0 && otherStudents.length === 0"
|
|
image="search"
|
|
tip="暂无学生数据"
|
|
/>
|
|
|
|
<!-- 加载状态 -->
|
|
<view v-if="loading" class="flex items-center justify-center py-20">
|
|
<wd-loading />
|
|
<text class="ml-2 text-gray-500">加载中...</text>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<route lang="jsonc" type="home">
|
|
{
|
|
"layout": "tabbar",
|
|
"style": {
|
|
"navigationStyle": "custom",
|
|
"navigationBarTitleText": "学生名单"
|
|
}
|
|
}
|
|
</route>
|