Files
skt-vuetify-templates/src/stores/loginAnnouncements.ts
T
skytek_xinliang b37f4363eb 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.
2026-05-05 11:54:19 +08:00

210 lines
5.5 KiB
TypeScript

import { defineStore } from 'pinia'
import { computed, ref, watch } from 'vue'
export interface LoginAnnouncementItem {
id: string | number
date: string
school: string
title: string
tab?: string
detail: string
}
export interface LoginAnnouncementListItem {
id: string | number
date: string
school: string
title: string
tab?: string
}
export interface LoginMobileAnnouncementItem {
id: string | number
content: string
title?: string
createdAt?: string
}
const storageKey = 'sk_playground_login_announcements'
const defaultItems: LoginAnnouncementItem[] = [
{
id: 'announcement-1',
date: '2024-03-19',
school: '市立實踐國中',
title: '臺北市立實踐國中徵求113學年度教學支援工作人員',
tab: 'junior',
detail: '公告內容:本校辦理本土語教學支援工作人員甄選,請於期限內完成報名與資料繳交。',
},
{
id: 'announcement-2',
date: '2023-12-12',
school: '市立華江高中',
title: '臺北市立華江高級中學112學年度第二學期本土語教學支援人員甄選',
tab: 'senior',
detail: '公告內容:甄選包含書面審查與面試,相關時間地點請參閱簡章附件。',
},
{
id: 'announcement-3',
date: '2023-12-05',
school: '市立麗山高中',
title: '內湖區麗山高中誠徵閩南語教支人員數名',
tab: 'senior',
detail: '公告內容:需具備相關教學經驗,錄取後依課務需求排課。',
},
{
id: 'announcement-4',
date: '2023-11-28',
school: '市立永吉國中',
title: '公告本市學校本土語教學支援人員報名資訊',
tab: 'junior',
detail: '公告內容:統一受理報名,請依公告流程檢附文件並完成線上登錄。',
},
{
id: 'announcement-5',
date: '2023-11-21',
school: '市立百齡高中',
title: '112學年度本土語文教學支援工作人員甄選簡章',
tab: 'senior',
detail: '公告內容:簡章含資格條件、甄選方式、成績計算與錄取標準。',
},
{
id: 'announcement-6',
date: '2023-11-10',
school: '市立成德國中',
title: '本土語教學支援工作人員甄選(第二次)',
tab: 'junior',
detail: '公告內容:第二次甄選開放補件,報名截止日以公告為準。',
},
]
function readItems(): LoginAnnouncementItem[] {
if (typeof window === 'undefined') return defaultItems
try {
const raw = window.localStorage.getItem(storageKey)
if (!raw) return defaultItems
const parsed = JSON.parse(raw)
return Array.isArray(parsed) ? (parsed as LoginAnnouncementItem[]) : defaultItems
} catch {
return defaultItems
}
}
function writeItems(items: LoginAnnouncementItem[]) {
if (typeof window === 'undefined') return
try {
window.localStorage.setItem(storageKey, JSON.stringify(items))
} catch {
return
}
}
async function mockFetchMobileAnnouncementsApi(): Promise<LoginMobileAnnouncementItem[]> {
return [
{
id: 'mobile-announcement-1',
content: '系統正常運行中',
title: '系統公告',
createdAt: '2026-02-11',
},
]
}
export const useLoginAnnouncementsStore = defineStore('loginAnnouncements', () => {
const items = ref<LoginAnnouncementItem[]>(readItems())
const selectedId = ref<string | number | null>(null)
const mobileAnnouncements = ref<LoginMobileAnnouncementItem[]>([])
const listItems = computed<LoginAnnouncementListItem[]>(() =>
items.value.map((item) => ({
id: item.id,
date: item.date,
school: item.school,
title: item.title,
tab: item.tab,
}))
)
const boardConfig = computed(() => ({
title: '學校公告區',
tabs: [
{ label: '全部', value: '__all__' },
{ label: '國中', value: 'junior' },
{ label: '高中', value: 'senior' },
],
items: listItems.value,
systemAnnouncements: mobileAnnouncements.value,
itemsPerPage: 5,
dateHeader: '公告時間',
schoolHeader: '公告學校',
titleHeader: '公告標題',
paginationLabel: '總筆數:',
}))
const selectedAnnouncement = computed(() => {
if (selectedId.value === null) return null
return items.value.find((item) => item.id === selectedId.value) ?? null
})
const selectedAnnouncementDetail = computed(() => {
return selectedAnnouncement.value?.detail ?? ''
})
const mobileAnnouncementConfig = computed(() => ({
items: mobileAnnouncements.value,
show: mobileAnnouncements.value.length > 0,
viewAllText: '查看全部',
listTitle: '系統公告',
closeText: '關閉',
emptyText: '目前沒有公告',
}))
const hydrate = () => {
items.value = readItems()
}
const replaceAll = (nextItems: LoginAnnouncementItem[]) => {
items.value = Array.isArray(nextItems) ? nextItems : []
}
const selectById = (id: string | number) => {
selectedId.value = id
}
const clearSelection = () => {
selectedId.value = null
}
const fetchMobileAnnouncements = async () => {
const result = await mockFetchMobileAnnouncementsApi()
mobileAnnouncements.value = Array.isArray(result) ? result : []
}
const fetchMobileAnnouncement = async () => {
await fetchMobileAnnouncements()
}
watch(
items,
(val) => {
writeItems(val)
},
{ deep: true }
)
return {
items,
listItems,
boardConfig,
mobileAnnouncementConfig,
selectedAnnouncement,
selectedAnnouncementDetail,
hydrate,
replaceAll,
selectById,
clearSelection,
fetchMobileAnnouncements,
fetchMobileAnnouncement,
}
})