refactor: 添加角色选择
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
58df855801
commit
8e05abbd2c
|
|
@ -90,16 +90,17 @@ const teacherName = computed(() => {
|
|||
return userInfo.nickname || '教师'
|
||||
})
|
||||
|
||||
const teacherSubject = computed(() => {
|
||||
const userInfo = userStore.info
|
||||
// 从角色信息中获取学科,假设角色名包含学科信息
|
||||
const roleName = userInfo.roles?.[0]?.name
|
||||
if (roleName) {
|
||||
// 提取学科信息,例如"语文教师"中的"语文"
|
||||
const subjectMatch = roleName.match(/^(.+?)教师$/)
|
||||
return subjectMatch ? subjectMatch[1] : '学科'
|
||||
}
|
||||
return '学科'
|
||||
const currentRoleText = computed(() => {
|
||||
const role = userStore.selectedRole
|
||||
if (!role)
|
||||
return '暂无角色'
|
||||
const parts = [
|
||||
role.grade,
|
||||
role.class,
|
||||
role.subject,
|
||||
role.role,
|
||||
].filter(Boolean)
|
||||
return parts.join(' ') || '教师'
|
||||
})
|
||||
|
||||
// 计算当前选中的班级名称
|
||||
|
|
@ -245,13 +246,6 @@ const showClassPicker = ref(false)
|
|||
|
||||
// 班级选择处理
|
||||
function handleClassChange() {
|
||||
if (homeStore.classOptions.length === 0) {
|
||||
uni.showToast({
|
||||
title: '暂无班级数据',
|
||||
icon: 'none',
|
||||
})
|
||||
return
|
||||
}
|
||||
showClassPicker.value = true
|
||||
}
|
||||
|
||||
|
|
@ -259,8 +253,6 @@ function handleClassChange() {
|
|||
watch(() => homeStore.selectedClassKey, () => {
|
||||
// 选择班级后重新获取统计数据
|
||||
fetchExamStats()
|
||||
// 关闭弹窗
|
||||
showClassPicker.value = false
|
||||
})
|
||||
|
||||
// 设置按钮处理
|
||||
|
|
@ -400,7 +392,7 @@ onMounted(async () => {
|
|||
</view>
|
||||
<view class="flex flex-col">
|
||||
<text class="text-base text-white font-bold">{{ teacherName }}</text>
|
||||
<text class="text-xs text-blue-100 opacity-80">{{ teacherSubject }}教师</text>
|
||||
<text class="text-xs text-blue-100 opacity-80">{{ currentRoleText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
@ -625,26 +617,54 @@ onMounted(async () => {
|
|||
|
||||
<wd-popup v-model="showClassPicker" position="bottom" custom-class="rounded-t-3xl" :z-index="100">
|
||||
<view class="bg-white pb-safe">
|
||||
<view class="border-b border-slate-100 p-4 text-center">
|
||||
<text class="text-lg text-slate-800 font-bold">切换班级</text>
|
||||
</view>
|
||||
<scroll-view scroll-y class="box-border max-h-80 p-4">
|
||||
<view class="space-y-2">
|
||||
<view
|
||||
v-for="classItem in homeStore.classOptions"
|
||||
:key="classItem.value"
|
||||
class="flex items-center justify-between rounded-xl p-4 transition-colors"
|
||||
:class="homeStore.selectedClassId === classItem.value ? 'bg-blue-50 border border-blue-100' : 'bg-slate-50'"
|
||||
@tap="homeStore.selectedClassId = classItem.value;showClassPicker = false"
|
||||
>
|
||||
<text class="text-base" :class="homeStore.selectedClassId === classItem.value ? 'text-blue-600 font-bold' : 'text-slate-700'">{{ classItem.label }}</text>
|
||||
<!-- 班级列表 -->
|
||||
<view class="border-b border-slate-100 p-4">
|
||||
<text class="mb-2 block text-sm text-slate-600 font-bold">切换班级</text>
|
||||
<scroll-view scroll-y class="box-border max-h-40">
|
||||
<view v-if="homeStore.classOptions.length > 0" class="space-y-1">
|
||||
<view
|
||||
v-if="homeStore.selectedClassId === classItem.value"
|
||||
class="i-mingcute:check-circle-fill text-xl text-blue-500"
|
||||
/>
|
||||
v-for="classItem in homeStore.classOptions"
|
||||
:key="classItem.value"
|
||||
class="flex items-center justify-between rounded-lg px-3 py-2 transition-colors"
|
||||
:class="homeStore.selectedClassId === classItem.value ? 'bg-blue-50 text-blue-600' : 'hover:bg-slate-50 text-slate-600'"
|
||||
@tap="homeStore.selectedClassId = classItem.value;showClassPicker = false"
|
||||
>
|
||||
<text class="text-sm font-medium">{{ classItem.label }}</text>
|
||||
<view
|
||||
v-if="homeStore.selectedClassId === classItem.value"
|
||||
class="i-mingcute:check-line text-lg"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view v-else class="py-4 text-center text-sm text-slate-400">
|
||||
当前角色无可选班级
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 角色选择区域 -->
|
||||
<view v-if="userStore.teacherInfo?.role_class_list && userStore.teacherInfo.role_class_list.length > 0" class="border-b border-slate-100 p-4">
|
||||
<text class="mb-2 block text-sm text-slate-600 font-bold">切换角色</text>
|
||||
<scroll-view scroll-y class="max-h-40">
|
||||
<view class="space-y-1">
|
||||
<view
|
||||
v-for="(roleItem, index) in userStore.teacherInfo.role_class_list"
|
||||
:key="index"
|
||||
class="flex items-center justify-between rounded-lg px-3 py-2 transition-colors"
|
||||
:class="userStore.selectedRole === roleItem ? 'bg-blue-50 text-blue-600' : 'hover:bg-slate-50 text-slate-600'"
|
||||
@tap="userStore.setSelectedRole(roleItem)"
|
||||
>
|
||||
<view class="flex items-center gap-2 text-sm">
|
||||
<text class="font-medium">{{ roleItem.grade }}{{ roleItem.class }}</text>
|
||||
<text>{{ roleItem.subject }}</text>
|
||||
<text>{{ roleItem.role }}</text>
|
||||
</view>
|
||||
<view v-if="userStore.selectedRole === roleItem" class="i-mingcute:check-line text-lg" />
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view class="p-4">
|
||||
<wd-button block type="default" size="large" custom-class="!rounded-xl !bg-slate-100 !text-slate-600 !border-none" @click="showClassPicker = false">
|
||||
关闭
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import type { ModelTeacherAnalysisClassInfo, ModelTeacherAnalysisExamInfo } from '@/api/data-contracts'
|
||||
import type { ModelTeacherAnalysisExamInfo } from '@/api/data-contracts'
|
||||
import { whenever } from '@vueuse/core'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { teacherScoreAnalysisApi } from '@/api'
|
||||
import { useUserStorage } from '@/composables/useUserStorage'
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
export interface SelectOption {
|
||||
label: string
|
||||
|
|
@ -14,23 +15,18 @@ export interface SelectOption {
|
|||
gradeKey?: number
|
||||
}
|
||||
|
||||
// 班级信息类型
|
||||
export interface ClassItem {
|
||||
label: string
|
||||
value: number
|
||||
gradeKey: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 首页状态管理
|
||||
*/
|
||||
export const useHomeStore = defineStore(
|
||||
'homeStore',
|
||||
() => {
|
||||
// 获取 userStore
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 数据存储
|
||||
const loading = ref(false)
|
||||
const examDataMap = ref<Map<number, ModelTeacherAnalysisExamInfo>>(new Map())
|
||||
const classList = ref<ClassItem[]>([])
|
||||
|
||||
// 已选择的数据(使用 useUserStorage 实现基于用户的持久化)
|
||||
const selectedClassId = useUserStorage<number>('home_selectedClassId', 0)
|
||||
|
|
@ -46,14 +42,53 @@ export const useHomeStore = defineStore(
|
|||
}))
|
||||
})
|
||||
|
||||
// 计算属性:班级选项
|
||||
// 计算属性:班级选项(带角色权限筛选)
|
||||
const classOptions = computed((): SelectOption[] => {
|
||||
return classList.value.map((item, index) => ({
|
||||
label: item.label,
|
||||
if (!selectedExamId.value) {
|
||||
return []
|
||||
}
|
||||
const examData = examDataMap.value.get(selectedExamId.value)
|
||||
if (!examData?.classes) {
|
||||
return []
|
||||
}
|
||||
const allClasses = examData.classes.map((item, index) => ({
|
||||
label: `${item.grade_name || ''} ${item.class_name || ''}`.trim(),
|
||||
value: index,
|
||||
classKey: item.value,
|
||||
gradeKey: item.gradeKey,
|
||||
}))
|
||||
classKey: item.class_key,
|
||||
gradeKey: Number(item.grade_key),
|
||||
})) || []
|
||||
|
||||
// 如果没有选择角色,返回所有班级
|
||||
const currentRole = userStore.selectedRole
|
||||
if (!currentRole) {
|
||||
return allClasses
|
||||
}
|
||||
|
||||
// 根据角色类型筛选班级
|
||||
return allClasses.filter((classItem) => {
|
||||
// 1. 任课老师 (role_key: 1): 只能访问特定科目的特定班级
|
||||
if (currentRole.role_key === 1) {
|
||||
return currentRole.grade_key === classItem.gradeKey && currentRole.class_key === classItem.classKey
|
||||
}
|
||||
|
||||
// 2. 班主任 (role_key: 2): 可以访问管理的班级
|
||||
if (currentRole.role_key === 2) {
|
||||
return currentRole.grade_key === classItem.gradeKey && currentRole.class_key === classItem.classKey
|
||||
}
|
||||
|
||||
// 3. 年级主任 (role_key: 3): 可以访问对应年级的所有班级
|
||||
if (currentRole.role_key === 3) {
|
||||
return currentRole.grade_key === classItem.gradeKey
|
||||
}
|
||||
|
||||
// 4. 学科组长 (role_key: 4): 可以访问对应科目的所有班级(在当前角色的年级内)
|
||||
if (currentRole.role_key === 4) {
|
||||
// 学科组长可以看当前角色年级的所有班级
|
||||
return currentRole.grade_key === classItem.gradeKey
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
})
|
||||
|
||||
const selectedClassKey = computed(() => {
|
||||
|
|
@ -64,7 +99,7 @@ export const useHomeStore = defineStore(
|
|||
return classOptions.value.find(item => item.value === selectedClassId.value)?.gradeKey || 0
|
||||
})
|
||||
|
||||
// 计算属性:根据选中考试获取科目选项
|
||||
// 计算属性:根据选中考试获取科目选项(带角色权限筛选)
|
||||
const subjectOptions = computed((): SelectOption[] => {
|
||||
if (!selectedExamId.value) {
|
||||
return []
|
||||
|
|
@ -73,12 +108,45 @@ export const useHomeStore = defineStore(
|
|||
if (!examData?.subjects) {
|
||||
return []
|
||||
}
|
||||
return examData.subjects.map(subject => ({
|
||||
|
||||
// 先生成所有科目选项
|
||||
const allSubjects = examData.subjects.map(subject => ({
|
||||
label: subject.subject_name || '',
|
||||
value: subject.exam_subject_id || 0,
|
||||
examSubjectId: subject.exam_subject_id || 0,
|
||||
subjectId: subject.subject_id || 0,
|
||||
}))
|
||||
|
||||
// 如果没有选择角色,返回所有科目
|
||||
const currentRole = userStore.selectedRole
|
||||
if (!currentRole) {
|
||||
return allSubjects
|
||||
}
|
||||
|
||||
// 根据角色类型筛选科目
|
||||
return allSubjects.filter((subjectItem) => {
|
||||
// 1. 任课老师 (role_key: 1): 只能访问特定科目
|
||||
if (currentRole.role_key === 1) {
|
||||
return currentRole.subject_id === subjectItem.subjectId
|
||||
}
|
||||
|
||||
// 2. 班主任 (role_key: 2): 可以访问所有科目
|
||||
if (currentRole.role_key === 2) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 3. 年级主任 (role_key: 3): 可以访问所有科目
|
||||
if (currentRole.role_key === 3) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 4. 学科组长 (role_key: 4): 只能访问负责的科目
|
||||
if (currentRole.role_key === 4) {
|
||||
return currentRole.subject_id === subjectItem.subjectId
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
|
|
@ -90,31 +158,19 @@ export const useHomeStore = defineStore(
|
|||
const response = await teacherScoreAnalysisApi.dataList()
|
||||
|
||||
if (response) {
|
||||
// 处理班级数据并建立班级到年级的映射
|
||||
classList.value = (response.class_list || []).map((item: ModelTeacherAnalysisClassInfo) => {
|
||||
if (!item.class_key || !item.grade_key) {
|
||||
return null
|
||||
}
|
||||
const classKey = item.class_key
|
||||
const gradeKey = item.grade_key
|
||||
|
||||
return {
|
||||
label: `${item.grade || ''} ${item.class || ''}`.trim(),
|
||||
value: classKey,
|
||||
gradeKey,
|
||||
}
|
||||
}).filter(item => item !== null) as ClassItem[]
|
||||
|
||||
// 处理考试数据,存储到 map 中
|
||||
examDataMap.value.clear()
|
||||
;(response.exam_list || []).forEach((exam: ModelTeacherAnalysisExamInfo) => {
|
||||
examDataMap.value.clear();
|
||||
(response.exam_list || []).forEach((exam: ModelTeacherAnalysisExamInfo) => {
|
||||
if (exam.exam_id) {
|
||||
examDataMap.value.set(exam.exam_id, exam)
|
||||
}
|
||||
})
|
||||
|
||||
if (!selectedExamId.value) {
|
||||
selectedExamId.value = examOptions.value[0]?.value as number
|
||||
}
|
||||
|
||||
console.log('数据获取完成:')
|
||||
console.log('classList数量:', classList.value.length)
|
||||
console.log('examDataMap数量:', examDataMap.value.size)
|
||||
}
|
||||
}
|
||||
|
|
@ -132,8 +188,10 @@ export const useHomeStore = defineStore(
|
|||
*/
|
||||
const onExamChange = (examId: number | null) => {
|
||||
selectedExamId.value = examId
|
||||
selectedSubjectId.value = subjectOptions.value[0].subjectId || null
|
||||
selectedExamSubjectId.value = subjectOptions.value[0].examSubjectId || null
|
||||
if (subjectOptions.value.length > 0) {
|
||||
selectedSubjectId.value = subjectOptions.value[0].subjectId || null
|
||||
selectedExamSubjectId.value = subjectOptions.value[0].examSubjectId || null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -158,7 +216,6 @@ export const useHomeStore = defineStore(
|
|||
* 重置store状态
|
||||
*/
|
||||
const reset = () => {
|
||||
classList.value = []
|
||||
examDataMap.value.clear()
|
||||
loading.value = false
|
||||
clearSelections()
|
||||
|
|
@ -166,8 +223,10 @@ export const useHomeStore = defineStore(
|
|||
|
||||
// 监听考试ID变化,自动选择第一个科目
|
||||
whenever(selectedExamId, (examId) => {
|
||||
selectedSubjectId.value = subjectOptions.value[0].subjectId || null
|
||||
selectedExamSubjectId.value = subjectOptions.value[0].examSubjectId || null
|
||||
if (subjectOptions.value.length > 0) {
|
||||
selectedSubjectId.value = subjectOptions.value[0].subjectId || null
|
||||
selectedExamSubjectId.value = subjectOptions.value[0].examSubjectId || null
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import type { ModelSysUser, ModelTeacherListResponse, ModelUserProfileResponse } from '@/api'
|
||||
import type { ModelSysUser, ModelTeacherListResponse, ModelTeacherRoleClassInfo, ModelUserProfileResponse } from '@/api'
|
||||
import type { LoginRequest, SysUser } from '@/service/types'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, ref } from 'vue'
|
||||
import { authApi } from '@/api'
|
||||
import { useUserStorage } from '@/composables/useUserStorage'
|
||||
import {
|
||||
authCodeLoginUsingPost,
|
||||
authLoginUsingPost,
|
||||
|
|
@ -24,6 +25,8 @@ export const useUserStore = defineStore(
|
|||
const info = ref<Partial<ModelSysUser>>({})
|
||||
// 教师信息
|
||||
const teacherInfo = ref<Partial<ModelTeacherListResponse>>({})
|
||||
// 当前选择的角色
|
||||
const selectedRole = useUserStorage<ModelTeacherRoleClassInfo>('selectedRole', null)
|
||||
// 访问令牌
|
||||
const accessToken = ref('')
|
||||
// 刷新令牌
|
||||
|
|
@ -46,6 +49,18 @@ export const useUserStore = defineStore(
|
|||
const setUserInfo = (newInfo: ModelUserProfileResponse) => {
|
||||
info.value = newInfo.sys_user
|
||||
teacherInfo.value = newInfo.teacher
|
||||
// 自动选择第一个角色作为默认值
|
||||
if (newInfo.teacher?.role_class_list && newInfo.teacher.role_class_list.length > 0) {
|
||||
selectedRole.value = newInfo.teacher.role_class_list[0]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前选择的角色
|
||||
* @param role 角色信息
|
||||
*/
|
||||
const setSelectedRole = (role: ModelTeacherRoleClassInfo) => {
|
||||
selectedRole.value = role
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -242,11 +257,14 @@ export const useUserStore = defineStore(
|
|||
isLogin,
|
||||
isTeacher,
|
||||
info,
|
||||
teacherInfo,
|
||||
selectedRole,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
tokenExpireTime,
|
||||
getUserInfo,
|
||||
setUserInfo,
|
||||
setSelectedRole,
|
||||
setLoginStatus,
|
||||
setToken,
|
||||
isTokenNearExpiry,
|
||||
|
|
|
|||
Loading…
Reference in New Issue