refactor: 前端本地偏好统一为公共存储层
- 新增 frontend/src/utils/storageKeys.ts 集中维护存储键常量并登记旧键迁移 - 新增 frontend/src/utils/storage.ts 统一 localStorage/sessionStorage 读写与异常容错 - 新增 frontend/src/composables/usePreference.ts 提供 useStringPreference 响应式字符串偏好 - 新增 frontend/src/utils/transactionFilterPrefs.ts 抽出收支筛选偏好字段定义与共享读写 - 存储键统一以 finance. 前缀命名,locale、emailBill.tab、logs.tab 迁移到新键并自动迁移旧值 - 各页面与组件改用公共层,移除重复的 try/catch 与跨组件重复的序列化逻辑 - 认证凭据 token 属登录状态,保持原键名不纳入 finance. 前缀
This commit is contained in:
@@ -4,6 +4,7 @@ import type { Ref } from 'vue'
|
||||
import type { AutoCompleteOption } from 'naive-ui'
|
||||
import { NIcon } from 'naive-ui'
|
||||
import { CloseCircleOutline } from '@vicons/ionicons5'
|
||||
import { readJson, removeKey, writeJson } from '../utils/storage'
|
||||
|
||||
/** 关键词历史配置 */
|
||||
export interface KeywordHistoryConfig {
|
||||
@@ -30,15 +31,10 @@ export function useKeywordHistory(config: KeywordHistoryConfig) {
|
||||
|
||||
/** 从 localStorage 读取历史,忽略损坏数据 */
|
||||
function load() {
|
||||
try {
|
||||
const raw = localStorage.getItem(storageKey)
|
||||
const parsed = raw ? JSON.parse(raw) : []
|
||||
history.value = Array.isArray(parsed)
|
||||
? parsed.filter((item) => typeof item === 'string' && item.trim()).slice(0, max)
|
||||
: []
|
||||
} catch {
|
||||
history.value = []
|
||||
}
|
||||
const parsed = readJson<unknown>(storageKey, [])
|
||||
history.value = Array.isArray(parsed)
|
||||
? parsed.filter((item) => typeof item === 'string' && item.trim()).slice(0, max)
|
||||
: []
|
||||
}
|
||||
|
||||
/** 记录当前关键词:去重(忽略大小写)、最新在前、上限截断 */
|
||||
@@ -47,21 +43,13 @@ export function useKeywordHistory(config: KeywordHistoryConfig) {
|
||||
if (!value) return
|
||||
const rest = history.value.filter((item) => item.toLowerCase() !== value.toLowerCase())
|
||||
history.value = [value, ...rest].slice(0, max)
|
||||
try {
|
||||
localStorage.setItem(storageKey, JSON.stringify(history.value))
|
||||
} catch {
|
||||
// 忽略写入失败
|
||||
}
|
||||
writeJson(storageKey, history.value)
|
||||
}
|
||||
|
||||
/** 清空历史 */
|
||||
function clear() {
|
||||
history.value = []
|
||||
try {
|
||||
localStorage.removeItem(storageKey)
|
||||
} catch {
|
||||
// 忽略删除失败
|
||||
}
|
||||
removeKey(storageKey)
|
||||
}
|
||||
|
||||
// 下拉选项:按当前输入过滤,历史非空时末尾追加「清空历史」
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// 偏好 composable:将字符串型界面偏好封装为响应式 ref,自动读取、校验回退与持久化
|
||||
import { ref, watch } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
import { migrateKey, readString, writeString } from '../utils/storage'
|
||||
|
||||
/** 字符串偏好配置 */
|
||||
export interface StringPreferenceOptions {
|
||||
/** 历史存储键,首次读取时迁移到当前键 */
|
||||
legacyKeys?: string[]
|
||||
/** 合法值校验,返回 false 时回退默认值 */
|
||||
validate?: (value: string) => boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理字符串型界面偏好:初始化时迁移旧键并读取,值变化时自动写入。
|
||||
*
|
||||
* :param key: 当前存储键
|
||||
* :param defaultValue: 默认值(键缺失或值非法时使用)
|
||||
* :param options: 旧键迁移与合法值校验
|
||||
* :returns: 与存储同步的字符串 ref
|
||||
*/
|
||||
export function useStringPreference(
|
||||
key: string,
|
||||
defaultValue: string,
|
||||
options: StringPreferenceOptions = {},
|
||||
): Ref<string> {
|
||||
const { legacyKeys = [], validate } = options
|
||||
// 首次读取前迁移历史键,保证老用户偏好不丢失
|
||||
legacyKeys.forEach((legacy) => migrateKey(legacy, key))
|
||||
|
||||
const initial = readString(key, defaultValue)
|
||||
const value = ref<string>(validate && !validate(initial) ? defaultValue : initial)
|
||||
|
||||
watch(value, (next) => writeString(key, next))
|
||||
return value
|
||||
}
|
||||
Reference in New Issue
Block a user