持久化(P1-c):新建 usePersistedRef composable,Tasks/AuditLog/ProjectDetail 等接入 localStorage
AuditLog(P1-d):后端 list_tool_executions 加 WHERE 筛选+返 {items,total,has_more},前端对接+长列折叠+筛选持久化
数据源解耦(P1-g):ProjectDetail projectTasks 按 project_id 独立加载 + ChatInput @项目联想独立加载(不读 store.tasks 当前页)
GitChanges(12a):后端 get_module_commits 加 git rev-list --count 返 total,前端显真实总数
原12大改:Dashboard 统计卡压底行(1)/Projects 列表卡片视图(2)/project_event 埋点排序(3)/TaskDetail 重设计(4)/IdeaDetail 重设计(5)/KnowledgeDetail 重设计(6)/界面持久化+侧栏Ctrl+B+审批数字键(7)/ProjectDetail 三栏改两栏(10)
P2打磨:文件浏览器(FileTree去重/FilePreview行号.md Diff/selectedFilePath归位)/settings反馈(假保存/端口校验)/AI会话(try-catch/scrollIntoView)/后端计数(move_queue事件/timeline total/workflow分页/import_batch分块)/杂项(TopBar/ConfirmDialog键盘/CIStatus i18n/ToolResultBody/ModuleNode/ApprovalDialog全选)
57 lines
2.1 KiB
TypeScript
57 lines
2.1 KiB
TypeScript
//! localStorage 持久化 ref — 读初始化 + 写回 localStorage(JSON)
|
|
//!
|
|
//! 背景:Tasks/Projects/Ideas 等列表页的筛选/分页状态散布在多个 ref,各自手写
|
|
//! localStorage.getItem/setItem 易漏且风格不一。本 composable 收敛为声明式一行:
|
|
//! const page = usePersistedRef('tasks.page', 1)
|
|
//!
|
|
//! 行为:
|
|
//! - 初始化时读 localStorage,存在且 JSON 解析成功则用其值,否则用 default
|
|
//! - watch ref 深度变化,JSON.stringify 写回 localStorage(try/catch 防脏数据/配额)
|
|
//! - SSR/无 localStorage 环境(SSR 不适用于 Tauri webview,但防御性兜底)返回纯 ref
|
|
//!
|
|
//! 设计取舍:
|
|
//! - 不做跨 tab 同步(storage 事件)——单实例 Tauri 应用无此需求
|
|
//! - 不做类型校验(解析出的值类型可能漂移)——调用方 default 决定类型,JSON 解析成功即采用,
|
|
//! 若需严格类型校验可在调用方业务层判断(当前所有调用点均为 string/number,无需求)
|
|
|
|
import { ref, watch, type Ref } from 'vue'
|
|
|
|
/**
|
|
* 创建一个持久化到 localStorage 的 ref。
|
|
*
|
|
* @param key localStorage 键名(建议带模块前缀,如 'tasks.page')
|
|
* @param defaultValue 默认值(localStorage 无值或解析失败时使用)
|
|
* @returns 与普通 ref 行为一致的 Ref<T>
|
|
*/
|
|
export function usePersistedRef<T>(key: string, defaultValue: T): Ref<T> {
|
|
// 初始化:读 localStorage,失败/不存在回落到 default
|
|
const initial = readPersisted(key, defaultValue)
|
|
const r = ref(initial) as Ref<T>
|
|
|
|
// 写回:深度 watch,JSON.stringify 失败静默(防循环引用等异常)
|
|
watch(
|
|
r,
|
|
(val) => {
|
|
try {
|
|
localStorage.setItem(key, JSON.stringify(val))
|
|
} catch {
|
|
/* 忽略:配额溢出 / 序列化失败 / 隐私模式禁用 localStorage */
|
|
}
|
|
},
|
|
{ deep: true },
|
|
)
|
|
|
|
return r
|
|
}
|
|
|
|
/** 读 localStorage 并 JSON 解析,失败/不存在返回 defaultValue。仅本模块内部使用。 */
|
|
function readPersisted<T>(key: string, defaultValue: T): T {
|
|
try {
|
|
const raw = localStorage.getItem(key)
|
|
if (raw === null) return defaultValue
|
|
return JSON.parse(raw) as T
|
|
} catch {
|
|
return defaultValue
|
|
}
|
|
}
|