feat: spacing tokens, actionable failure copy, unified focus ring, 44px touch targets, press feedback
ci / gitleaks (push) Successful in 8s
ci / docker (push) Successful in 3m36s

This commit is contained in:
2026-09-23 06:45:51 +08:00
parent 058f353965
commit 629e054823
25 changed files with 251 additions and 181 deletions
+7 -6
View File
@@ -28,6 +28,7 @@ import AppDialog, { type AppDialogOptions } from './components/AppDialog.vue'
import { shanghaiDateKey, useTaskDueClock, watchShanghaiDateRollover } from './lib/task-due-clock'
import { readTodaySectionCollapse, writeTodaySectionCollapse, type TodaySectionCollapse } from './lib/today-section-collapse'
import { loadTaskOpenTotal } from './lib/task-open-total'
import { failureHint } from './failureHint'
type FolderItem = { id: string; name: string }
type TaskList = { id: string; folder_id: string | null; name: string; is_inbox: boolean }
@@ -470,7 +471,7 @@ async function api(path: string, options: RequestInit = {}) {
...options,
})
if (!response.ok) {
let message = '请求失败'
let message = '请求失败,请检查网络后重试'
try { const body = await response.json(); message = formatApiErrorDetail(body.detail) } catch { /* noop */ }
const requestError = new Error(message) as Error & { status: number }
requestError.status = response.status
@@ -483,7 +484,7 @@ function toast(message: string) {
notice.value = message
window.setTimeout(() => { if (notice.value === message) notice.value = '' }, 2400)
}
function fail(reason: unknown) { error.value = reason instanceof Error ? reason.message : '请求失败' }
function fail(reason: unknown) { error.value = failureHint('请求失败', reason) }
async function syncBrowserTimezone(currentTimezone?: string) {
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
@@ -1138,7 +1139,7 @@ async function saveSelectedTaskChanges() {
toast('已保存')
} catch (reason) {
if (recurrenceLoadToken !== selectionToken || selectedTask.value?.id !== taskId) return
selectedRepeatError.value = reason instanceof Error ? reason.message : '保存失败'
selectedRepeatError.value = failureHint('保存失败', reason)
fail(reason)
} finally {
savingSelectedTask.value = false
@@ -1443,7 +1444,7 @@ async function confirmPurgeList() {
focusPurgeListTrigger()
toast('清单已永久删除')
} catch (reason) {
purgeListError.value = reason instanceof Error ? reason.message : '永久删除失败'
purgeListError.value = failureHint('永久删除失败', reason)
} finally {
purgeListSubmitting.value = false
}
@@ -1512,7 +1513,7 @@ async function saveListEditor() {
listEditorFolderId.value = current.folder_id ?? ''
}
listEditorError.value = '名称已保存,但移动文件夹失败,请重试。'
} else listEditorError.value = reason instanceof Error ? reason.message : '保存失败'
} else listEditorError.value = reason instanceof Error ? reason.message : '保存失败,请检查网络后重试'
} finally { listEditorBusy.value = false }
}
async function archiveListFromEditor() {
@@ -1534,7 +1535,7 @@ async function archiveListFromEditor() {
}
toast('清单已归档')
} catch (reason) {
listEditorError.value = reason instanceof Error ? reason.message : '归档失败'
listEditorError.value = failureHint('归档失败', reason)
} finally { listEditorBusy.value = false }
}
function toggleFolder(id: string) { const next = new Set(expandedFolders.value); next.has(id) ? next.delete(id) : next.add(id); expandedFolders.value = next }