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.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.
This commit is contained in:
skytek_xinliang
2026-05-05 11:54:19 +08:00
parent 6eab4d9744
commit b37f4363eb
23 changed files with 1531 additions and 1588 deletions
+157 -1
View File
@@ -1 +1,157 @@
export * from './stores/semesters'
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,
}
})