software copyright

This commit is contained in:
jiahong
2026-03-22 15:24:40 +08:00
parent e303bb868a
commit 60f336e345
155 changed files with 127262 additions and 0 deletions
@@ -0,0 +1,344 @@
/**
* 自然写互动课堂PC端应用软件 V1.0
*
* index.ts - Pinia状态管理(全局Store
*
* 功能说明:
* - 用户认证状态管理
* - 课堂状态管理(当前课堂/学生列表/笔迹数据)
* - 设备连接状态管理
* - 作业批改状态管理
* - WebSocket实时数据同步
* - 持久化存储(electron-store
*/
import { defineStore } from 'pinia';
import { ref, computed, reactive } from 'vue';
/* ======================== 类型定义 ======================== */
/** 应用视图模式 */
type ViewMode = 'prepare' | 'lesson' | 'grade' | 'report';
/** 设备信息 */
interface DeviceState {
id: string;
name: string;
type: 'usb' | 'ble';
status: 'connected' | 'disconnected' | 'error';
battery: number;
}
/** 学生在线状态 */
interface StudentOnlineState {
studentId: string;
name: string;
penId: string;
online: boolean;
lastActive: number;
strokeCount: number;
}
/** 课堂互动数据 */
interface ClassroomLiveData {
classroomId: string;
className: string;
startTime: number;
onlineStudents: StudentOnlineState[];
totalStrokes: number;
isRecording: boolean;
}
/** 批改任务 */
interface GradeTask {
assignmentId: string;
studentId: string;
studentName: string;
status: 'pending' | 'ai_graded' | 'reviewed' | 'completed';
aiScore: number;
teacherScore: number;
feedback: string;
}
/* ======================== 用户Store ======================== */
/**
* 用户认证与信息状态管理
*/
export const useUserStore = defineStore('user', () => {
/** 是否已登录 */
const isLoggedIn = ref(false);
/** 当前用户信息 */
const userInfo = ref<{
userId: string;
name: string;
role: string;
phone: string;
schoolId: string;
schoolName: string;
avatar: string;
} | null>(null);
/** 登录时间 */
const loginTime = ref(0);
/** Token过期时间 */
const tokenExpiresAt = ref(0);
/** 用户角色显示名 */
const roleLabel = computed(() => {
const roleMap: Record<string, string> = {
admin: '管理员',
teacher: '教师',
student: '学生',
parent: '家长'
};
return roleMap[userInfo.value?.role || ''] || '未知';
});
/**
* 登录成功后设置用户状态
*/
function setLoggedIn(user: typeof userInfo.value, expiresAt: number): void {
isLoggedIn.value = true;
userInfo.value = user;
loginTime.value = Date.now();
tokenExpiresAt.value = expiresAt;
console.log(`[Store] 用户登录: ${user?.name} (${user?.role})`);
}
/**
* 退出登录
*/
function logout(): void {
isLoggedIn.value = false;
userInfo.value = null;
loginTime.value = 0;
tokenExpiresAt.value = 0;
console.log('[Store] 用户已退出');
}
return { isLoggedIn, userInfo, loginTime, tokenExpiresAt, roleLabel, setLoggedIn, logout };
});
/* ======================== 课堂Store ======================== */
/**
* 课堂状态管理
* 管理当前课堂的实时数据
*/
export const useClassroomStore = defineStore('classroom', () => {
/** 当前视图模式 */
const viewMode = ref<ViewMode>('prepare');
/** 当前课堂数据 */
const liveData = ref<ClassroomLiveData | null>(null);
/** 是否在课堂中 */
const isInClass = ref(false);
/** WebSocket连接状态 */
const wsConnected = ref(false);
/** 在线学生数 */
const onlineCount = computed(() =>
liveData.value?.onlineStudents.filter(s => s.online).length || 0
);
/** 总学生数 */
const totalStudents = computed(() =>
liveData.value?.onlineStudents.length || 0
);
/** 在线率 */
const onlineRate = computed(() => {
const total = totalStudents.value;
return total > 0 ? Math.round((onlineCount.value / total) * 100) : 0;
});
/**
* 开始课堂
*/
function startClass(classroomId: string, className: string, students: StudentOnlineState[]): void {
liveData.value = {
classroomId,
className,
startTime: Date.now(),
onlineStudents: students,
totalStrokes: 0,
isRecording: false
};
isInClass.value = true;
viewMode.value = 'lesson';
console.log(`[Store] 课堂开始: ${className}, 学生${students.length}`);
}
/**
* 结束课堂
*/
function endClass(): void {
const duration = liveData.value ? Date.now() - liveData.value.startTime : 0;
console.log(`[Store] 课堂结束, 时长=${Math.round(duration / 60000)}分钟, ` +
`笔迹=${liveData.value?.totalStrokes}`);
isInClass.value = false;
liveData.value = null;
}
/**
* 更新学生在线状态
*/
function updateStudentStatus(studentId: string, online: boolean): void {
const student = liveData.value?.onlineStudents.find(s => s.studentId === studentId);
if (student) {
student.online = online;
student.lastActive = Date.now();
}
}
/**
* 累加笔迹数据计数
*/
function addStrokeCount(count: number): void {
if (liveData.value) {
liveData.value.totalStrokes += count;
}
}
/**
* 切换视图模式
*/
function setViewMode(mode: ViewMode): void {
viewMode.value = mode;
console.log(`[Store] 视图切换: ${mode}`);
}
return {
viewMode, liveData, isInClass, wsConnected,
onlineCount, totalStudents, onlineRate,
startClass, endClass, updateStudentStatus, addStrokeCount, setViewMode
};
});
/* ======================== 设备Store ======================== */
/**
* 设备连接状态管理
*/
export const useDeviceStore = defineStore('device', () => {
/** 已连接设备列表 */
const devices = ref<DeviceState[]>([]);
/** 正在扫描BLE */
const isScanning = ref(false);
/** 已连接设备数 */
const connectedCount = computed(() =>
devices.value.filter(d => d.status === 'connected').length
);
/**
* 添加或更新设备
*/
function upsertDevice(device: DeviceState): void {
const idx = devices.value.findIndex(d => d.id === device.id);
if (idx >= 0) {
devices.value[idx] = device;
} else {
devices.value.push(device);
}
}
/**
* 移除设备
*/
function removeDevice(deviceId: string): void {
devices.value = devices.value.filter(d => d.id !== deviceId);
}
/**
* 更新设备电量
*/
function updateBattery(deviceId: string, battery: number): void {
const device = devices.value.find(d => d.id === deviceId);
if (device) {
device.battery = battery;
}
}
return { devices, isScanning, connectedCount, upsertDevice, removeDevice, updateBattery };
});
/* ======================== 批改Store ======================== */
/**
* 作业批改状态管理
*/
export const useGradeStore = defineStore('grade', () => {
/** 当前批改的作业ID */
const currentAssignmentId = ref('');
/** 批改任务列表 */
const gradeTasks = ref<GradeTask[]>([]);
/** 当前批改的学生索引 */
const currentTaskIndex = ref(0);
/** 待批改数 */
const pendingCount = computed(() =>
gradeTasks.value.filter(t => t.status === 'ai_graded' || t.status === 'pending').length
);
/** 已完成数 */
const completedCount = computed(() =>
gradeTasks.value.filter(t => t.status === 'completed' || t.status === 'reviewed').length
);
/** 总体进度百分比 */
const progressPercent = computed(() => {
const total = gradeTasks.value.length;
return total > 0 ? Math.round((completedCount.value / total) * 100) : 0;
});
/** 当前批改任务 */
const currentTask = computed(() => gradeTasks.value[currentTaskIndex.value] || null);
/**
* 加载批改任务列表
*/
function loadTasks(assignmentId: string, tasks: GradeTask[]): void {
currentAssignmentId.value = assignmentId;
gradeTasks.value = tasks;
currentTaskIndex.value = 0;
console.log(`[Store] 加载批改任务: ${tasks.length}份作业`);
}
/**
* 提交教师批改结果
*/
function submitGrade(studentId: string, score: number, feedback: string): void {
const task = gradeTasks.value.find(t => t.studentId === studentId);
if (task) {
task.teacherScore = score;
task.feedback = feedback;
task.status = 'reviewed';
console.log(`[Store] 批改完成: ${task.studentName}, 分数=${score}`);
}
}
/**
* 切换到下一个待批改任务
*/
function nextTask(): boolean {
for (let i = currentTaskIndex.value + 1; i < gradeTasks.value.length; i++) {
if (gradeTasks.value[i].status !== 'completed' && gradeTasks.value[i].status !== 'reviewed') {
currentTaskIndex.value = i;
return true;
}
}
return false;
}
/**
* 切换到上一个任务
*/
function prevTask(): boolean {
if (currentTaskIndex.value > 0) {
currentTaskIndex.value--;
return true;
}
return false;
}
return {
currentAssignmentId, gradeTasks, currentTaskIndex,
pendingCount, completedCount, progressPercent, currentTask,
loadTasks, submitGrade, nextTask, prevTask
};
});