b37f4363eb
Implement concrete Pinia stores for app UI and domain data instead of placeholder re-exports, including seeded student records and snackbar state. Refresh README guidance for components, plugins, and services to document the current project structure, data flow, and usage conventions.feat(stores): add Pinia domain stores and update docs Implement concrete Pinia stores for app UI and domain data instead of placeholder re-exports, including seeded student records and snackbar state. Refresh README guidance for components, plugins, and services to document the current project structure, data flow, and usage conventions.
158 lines
4.3 KiB
TypeScript
158 lines
4.3 KiB
TypeScript
import { defineStore } from 'pinia'
|
|
import { ref } from 'vue'
|
|
|
|
export interface CourseRecord {
|
|
code: string
|
|
name: string
|
|
credits: number
|
|
score: number
|
|
}
|
|
|
|
export interface SemesterRecord {
|
|
id: number
|
|
studentId: number
|
|
semesterName: string
|
|
courses: CourseRecord[]
|
|
rank: number
|
|
average: number
|
|
}
|
|
|
|
const seedSemesters: SemesterRecord[] = []
|
|
|
|
const randomScore = (min = 60, max = 99) => Math.floor(Math.random() * (max - min + 1)) + min
|
|
|
|
export function generateMockSemesters(studentId: number) {
|
|
const semesters = [
|
|
{ name: '111 學年度第 1 學期', baseId: 1000 },
|
|
{ name: '111 學年度第 2 學期', baseId: 2000 },
|
|
{ name: '112 學年度第 1 學期', baseId: 3000 },
|
|
{ name: '112 學年度第 2 學期', baseId: 4000 },
|
|
{ name: '113 學年度第 1 學期', baseId: 5000 },
|
|
{ name: '113 學年度第 2 學期', baseId: 6000 },
|
|
]
|
|
|
|
const subjects = [
|
|
{ name: '資料結構', credits: 3 },
|
|
{ name: '演算法', credits: 3 },
|
|
{ name: '作業系統', credits: 3 },
|
|
{ name: '計算機組織', credits: 3 },
|
|
{ name: '線性代數', credits: 3 },
|
|
{ name: '機率與統計', credits: 3 },
|
|
{ name: '資料庫系統', credits: 3 },
|
|
{ name: '人工智慧導論', credits: 3 },
|
|
{ name: '網頁程式設計', credits: 3 },
|
|
{ name: '計算機網路', credits: 3 },
|
|
]
|
|
|
|
const count = 5 + (studentId % 2)
|
|
const result: SemesterRecord[] = []
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
const sem = semesters[i]
|
|
if (!sem) continue
|
|
const courseCount = 8 + (studentId % 3)
|
|
const courses: CourseRecord[] = []
|
|
const usedSubjects = new Set<number>()
|
|
|
|
let totalScore = 0
|
|
let totalCredits = 0
|
|
|
|
while (courses.length < courseCount) {
|
|
const idx = Math.floor(Math.random() * subjects.length)
|
|
if (usedSubjects.has(idx)) continue
|
|
usedSubjects.add(idx)
|
|
|
|
const score = randomScore()
|
|
const subject = subjects[idx]
|
|
if (!subject) continue
|
|
|
|
courses.push({
|
|
code: `CS${1000 + idx}`,
|
|
name: subject.name,
|
|
credits: subject.credits,
|
|
score,
|
|
})
|
|
|
|
totalScore += score * subject.credits
|
|
totalCredits += subject.credits
|
|
}
|
|
|
|
result.push({
|
|
id: sem.baseId + studentId,
|
|
studentId,
|
|
semesterName: sem.name,
|
|
courses,
|
|
rank: Math.floor(Math.random() * 20) + 1,
|
|
average: Number((totalScore / totalCredits).toFixed(2)),
|
|
})
|
|
}
|
|
return result
|
|
}
|
|
|
|
for (let i = 1; i <= 20; i++) {
|
|
seedSemesters.push(...generateMockSemesters(i))
|
|
}
|
|
|
|
export const useSemesterStore = defineStore('semesters', () => {
|
|
const semesters = ref<SemesterRecord[]>([...seedSemesters])
|
|
|
|
const getStudentSemesters = (studentId: number) => {
|
|
return semesters.value.filter((s) => s.studentId === studentId)
|
|
}
|
|
|
|
const generateForStudent = (studentId: number) => {
|
|
const newSemesters = generateMockSemesters(studentId)
|
|
semesters.value.push(...newSemesters)
|
|
}
|
|
|
|
const addSemester = (studentId: number) => {
|
|
const newId = Math.max(...semesters.value.map((s) => s.id), 0) + 1
|
|
const newSemester: SemesterRecord = {
|
|
id: newId,
|
|
studentId,
|
|
semesterName: '新學期',
|
|
courses: [],
|
|
rank: 0,
|
|
average: 0,
|
|
}
|
|
semesters.value.push(newSemester)
|
|
return newSemester
|
|
}
|
|
|
|
const updateSemester = (id: number, payload: Partial<SemesterRecord>) => {
|
|
const index = semesters.value.findIndex((s) => s.id === id)
|
|
if (index === -1) return
|
|
const current = semesters.value[index]
|
|
if (!current) return
|
|
|
|
if (payload.courses) {
|
|
const totalScore = payload.courses.reduce((sum, c) => sum + c.score * c.credits, 0)
|
|
const totalCredits = payload.courses.reduce((sum, c) => sum + c.credits, 0)
|
|
payload.average = totalCredits > 0 ? Number((totalScore / totalCredits).toFixed(2)) : 0
|
|
}
|
|
|
|
Object.assign(current, payload)
|
|
}
|
|
|
|
const removeSemester = (id: number) => {
|
|
const index = semesters.value.findIndex((s) => s.id === id)
|
|
if (index !== -1) {
|
|
semesters.value.splice(index, 1)
|
|
}
|
|
}
|
|
|
|
const removeByStudentId = (studentId: number) => {
|
|
semesters.value = semesters.value.filter((s) => s.studentId !== studentId)
|
|
}
|
|
|
|
return {
|
|
semesters,
|
|
getStudentSemesters,
|
|
generateForStudent,
|
|
addSemester,
|
|
updateSemester,
|
|
removeSemester,
|
|
removeByStudentId,
|
|
}
|
|
})
|