perf: streamline view data loading
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { Archive, ArchiveRestore, CalendarHeart, ChevronDown, Pencil, Pin, Trash2, X } from 'lucide-vue-next'
|
||||
import { csrfHeader } from './lib/csrf'
|
||||
import { calendarModeLabel, countdownDayText, countdownKindLabel, dateKey, formatApiErrorDetail, readCountdownCache, writeCountdownCache } from './lib/mvp-utils'
|
||||
import { calendarModeLabel, countdownDayText, countdownKindLabel, dateKey, formatApiErrorDetail, getCountdownCacheGeneration, invalidateCountdownCache, isCountdownCacheGenerationCurrent, loadCountdownCache, readCountdownCache } from './lib/mvp-utils'
|
||||
|
||||
type Countdown = {
|
||||
id: string; title: string; event_date: string; display_date: string; kind: 'countdown'|'anniversary'|'birthday'
|
||||
@@ -100,23 +100,28 @@ async function request(path:string, options:RequestInit={}) {
|
||||
return response.status === 204 ? null : response.json()
|
||||
}
|
||||
async function safe(work:()=>Promise<void>) { busy.value=true; error.value=''; try { await work() } catch(reason) { error.value=reason instanceof Error ? reason.message : '请求失败' } finally { busy.value=false } }
|
||||
async function load() {
|
||||
async function fetchCountdowns() {
|
||||
const [active, archivedItems] = await Promise.all([
|
||||
request('/countdowns') as Promise<Countdown[]>,
|
||||
request('/countdowns?archived=true') as Promise<Countdown[]>,
|
||||
])
|
||||
return { items: active, archived: archivedItems }
|
||||
}
|
||||
async function load(force = false) {
|
||||
const generation = getCountdownCacheGeneration()
|
||||
const cached = readCountdownCache<Countdown>()
|
||||
if (cached) { items.value=cached.items; archived.value=cached.archived }
|
||||
if (!cached) busy.value=true
|
||||
error.value=''
|
||||
try {
|
||||
const active = await request('/countdowns') as Countdown[]
|
||||
items.value=active
|
||||
writeCountdownCache(items.value, archived.value)
|
||||
busy.value=false
|
||||
const archivedItems = await request('/countdowns?archived=true') as Countdown[]
|
||||
archived.value=archivedItems
|
||||
writeCountdownCache(items.value, archived.value)
|
||||
const data = await loadCountdownCache(fetchCountdowns, { force })
|
||||
if (!isCountdownCacheGenerationCurrent(generation)) return
|
||||
items.value=data.items
|
||||
archived.value=data.archived
|
||||
} catch(reason) {
|
||||
error.value=reason instanceof Error ? reason.message : '请求失败'
|
||||
if (isCountdownCacheGenerationCurrent(generation)) error.value=reason instanceof Error ? reason.message : '请求失败'
|
||||
} finally {
|
||||
busy.value=false
|
||||
if (isCountdownCacheGenerationCurrent(generation)) busy.value=false
|
||||
}
|
||||
}
|
||||
function edit(item:Countdown) {
|
||||
@@ -141,13 +146,13 @@ async function save() {
|
||||
if (form.value.calendar_mode==='lunar') { payload.lunar_month=form.value.leap_month ? -form.value.lunar_month : form.value.lunar_month; payload.lunar_day=form.value.lunar_day }
|
||||
const path=editingId.value ? `/countdowns/${editingId.value}` : '/countdowns'
|
||||
await request(path,{ method:editingId.value?'PATCH':'POST', body:JSON.stringify(payload) })
|
||||
closeDialog(); await load(); emit('notice',editingId.value?'倒数日已更新':'倒数日已添加')
|
||||
invalidateCountdownCache(); closeDialog(); await load(true); emit('notice',editingId.value?'倒数日已更新':'倒数日已添加')
|
||||
})
|
||||
}
|
||||
async function pin(item:Countdown){await safe(async()=>{await request(`/countdowns/${item.id}/pin`,{method:'POST'});detailItem.value=null;await load();emit('notice','已置顶')})}
|
||||
async function archiveItem(item:Countdown){await safe(async()=>{await request(`/countdowns/${item.id}`,{method:'DELETE'});detailItem.value=null;await load();emit('notice','已归档')})}
|
||||
async function restore(item:Countdown){await safe(async()=>{await request(`/countdowns/${item.id}/restore`,{method:'POST'});await load();emit('notice','已恢复')})}
|
||||
async function purge(item:Countdown){if(!confirm(`永久删除“${item.title}”?这个操作不能撤销。`))return;await safe(async()=>{await request(`/countdowns/${item.id}/purge`,{method:'DELETE'});await load();emit('notice','已永久删除')})}
|
||||
async function pin(item:Countdown){await safe(async()=>{await request(`/countdowns/${item.id}/pin`,{method:'POST'});invalidateCountdownCache();detailItem.value=null;await load(true);emit('notice','已置顶')})}
|
||||
async function archiveItem(item:Countdown){await safe(async()=>{await request(`/countdowns/${item.id}`,{method:'DELETE'});invalidateCountdownCache();detailItem.value=null;await load(true);emit('notice','已归档')})}
|
||||
async function restore(item:Countdown){await safe(async()=>{await request(`/countdowns/${item.id}/restore`,{method:'POST'});invalidateCountdownCache();await load(true);emit('notice','已恢复')})}
|
||||
async function purge(item:Countdown){if(!confirm(`永久删除“${item.title}”?这个操作不能撤销。`))return;await safe(async()=>{await request(`/countdowns/${item.id}/purge`,{method:'DELETE'});invalidateCountdownCache();await load(true);emit('notice','已永久删除')})}
|
||||
function formatDate(value:string){const [y,m,d]=value.split('-');return `${y}年${Number(m)}月${Number(d)}日`}
|
||||
function formatDateShort(value:string){const [y,m,d]=value.split('-');return `${y}/${Number(m)}/${Number(d)}`}
|
||||
function repeatLabel(value:Countdown['repeat_rule']){return({none:'不重复',weekly:'每周',monthly:'每月',yearly:'每年'})[value]}
|
||||
@@ -167,7 +172,7 @@ function trapDetailFocus(event: KeyboardEvent) {
|
||||
function openFromEmpty(){openCountdownComposer()}
|
||||
function openCountdownComposer(origin?: { x: number; y: number }){if(origin)composerOrigin.value=origin;detailItem.value=null;editingId.value=null;editingItem.value=null;showAdvanced.value=false;form.value=freshForm();open.value=true;focusDialog()}
|
||||
defineExpose({ openCountdownComposer })
|
||||
onMounted(load)
|
||||
onMounted(() => { void load() })
|
||||
onBeforeUnmount(() => { previousFocus = null })
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user