Add settings page with synced appearance and password change

- Store app settings as one JSON document on the server, merged per key and
  pushed to other devices; cache locally to avoid a flash on load
- Settings page groups appearance, account, shortcuts, shares, API tokens and
  trash; full screen with a section list on phones
- Font size and content width settings
- Change the login password, signing out other sessions
- Settings writes and password changes only accept a login session
This commit is contained in:
eezd committed 2026-10-17 16:23:39 +08:00
1 parent 5b326a70bf
commit eb63b14c8e
25 files changed
+915 -290

No files matched your search

+53
View File
@@ -0,0 +1,53 @@
// 应用设置:服务器上一份 JSON,全部设备共用;修改立即保存,其他设备经事件推送同步。
// 本地缓存上次的值,启动时先套用,避免页面先按默认值显示再跳变
import { reactive } from 'vue'
import { api } from './api'
export const FONT_SIZES = [14, 15, 16, 17, 18, 20, 22]
// 0 为全宽
export const CONTENT_WIDTHS = [
{ label: '窄', value: 680 },
{ label: '标准', value: 760 },
{ label: '宽', value: 960 },
{ label: '全宽', value: 0 },
]
const DEFAULTS = { fontSize: 16, contentWidth: 760 }
export type Settings = typeof DEFAULTS
const CACHE_KEY = 'rawmark.settings'
export const settings = reactive<Settings>(sanitize(JSON.parse(localStorage.getItem(CACHE_KEY) ?? '{}')))
// 服务器上的设置可能由旧版本、API 或插件写入,取值不合法的项退回默认值
function sanitize(raw: Record<string, unknown>): Settings {
const pick = <T>(v: unknown, allowed: T[], def: T) => (allowed.includes(v as T) ? (v as T) : def)
return {
fontSize: pick(raw.fontSize, FONT_SIZES, DEFAULTS.fontSize),
contentWidth: pick(
raw.contentWidth,
CONTENT_WIDTHS.map((w) => w.value),
DEFAULTS.contentWidth,
),
}
}
function applied(next: Settings) {
Object.assign(settings, next)
localStorage.setItem(CACHE_KEY, JSON.stringify(settings))
const root = document.documentElement.style
root.setProperty('--rm-font-size', settings.fontSize + 'px')
root.setProperty('--rm-content-width', settings.contentWidth ? settings.contentWidth + 'px' : 'none')
}
applied({ ...settings })
export async function loadSettings() {
applied(sanitize(await api.settings()))
}
/** 修改设置:先在本地生效再保存,保存失败时由调用方提示并重新载入 */
export async function updateSettings(patch: Partial<Settings>) {
applied({ ...settings, ...patch })
await api.patchSettings(patch)
}