/** * 集中管理所有 localStorage 持久化。 * * - key 在此注册,各页面只通过 storage.xxx.get/set 调用。 * - 类型安全,不再散落 try/catch。 */ function kv(key: string) { return { get(fallback: T): T { try { const raw = localStorage.getItem(key) if (raw !== null) return JSON.parse(raw) as T } catch { /* ignore */ } return fallback }, set(val: T) { try { localStorage.setItem(key, JSON.stringify(val)) } catch { /* ignore */ } }, } } export const storage = { /** 查询轮询 / SSE 配置 */ queryConfig: kv('tf-stocks-query-config'), /** 策略池 (screener) */ strategyPool: kv('strategy-pool'), /** 自选列表列配置 */ watchlistColumns: kv('watchlist_columns'), /** 个股日K信息条指标配置 */ stockInfoBarFields: kv('stock_info_bar_fields'), /** 策略结果列表列配置 */ screenerResultColumns: kv('screener_result_columns'), /** 自选列表视图模式 table | card */ watchlistView: kv('watchlist_view'), /** 自选列表日K蜡烛图显示状态 */ watchlistCandle: kv('watchlist_showCandle'), /** 策略结果列表日K蜡烛图显示状态 */ screenerCandle: kv('screener_showCandle'), /** 自选列表板块筛选 */ watchlistBoardFilter: kv('watchlist_boardFilter'), /** Screener 卡片尺寸 */ screenerCardSize: kv('screener-card-size'), /** 连板梯队板块筛选 */ limitLadderBoard: kv('limit-ladder-board-filter'), /** 连板梯队 ext 字段配置 */ limitLadderExtFields: kv>('limit-ladder-ext-fields'), /** 连板梯队 概念/行业 显示开关 */ limitLadderShowExt: kv<{ concept: boolean; industry: boolean }>('limit-ladder-show-ext'), /** 连板梯队 涨停/跌停 切换方向 */ limitLadderDirection: kv<'up' | 'down'>('limit-ladder-direction'), /** 连板梯队 封单显示模式: vol=按成交量(手), amount=按金额(元) */ limitLadderSealMode: kv<'vol' | 'amount'>('limit-ladder-seal-mode'), /** 策略创建草稿(新建专用) */ strategyDraft: kv<{ name: string; description: string; direction: string; style?: string; rules: string; code: string; step: number; strategyId: string } | null>('strategy-draft'), /** 策略修改草稿(AI修改专用,不影响创建按钮) */ strategyModify: kv<{ name: string; description: string; direction: string; style?: string; rules: string; code: string; step: number; strategyId: string } | null>('strategy-modify'), /** 策略构建器草稿(旧版兼容,逐渐废弃) */ strategyBuilderDraft: kv<{ name: string; description: string; direction: string; style?: string; rules: string; code: string; step: number; strategyId: string } | null>('strategy-builder-draft'), /** 已保存策略的原始规则(策略ID → 规则文本) */ strategyRules: kv>('strategy-rules'), /** 策略回测快捷区间按钮配置 */ strategyBacktestQuickRanges: kv('strategy-backtest-quick-ranges'), /** 策略回测最后一次成功结果和参数 */ strategyBacktestLast: kv<{ selectedStrategy: string | null symbols: string start: string end: string matching: 'close_t' | 'open_t+1' entryFill: 'close_t' | 'open_t+1' exitFill: 'close_t' | 'open_t+1' fees: string slippage: string maxPositions: string maxExposure: string initialCapital: string positionSizing: 'equal' | 'score_weight' mode: 'position' | 'full' holdingDays: string params?: Record overrides?: Record result: any } | null>('strategy-backtest-last'), /** 概念分析页面字段配置 */ conceptAnalysisConfig: kv>('concept-analysis-config'), /** 行业分析页面字段配置 */ industryAnalysisConfig: kv>('industry-analysis-config'), /** 数据页画像卡片显隐 (卡片key → 是否显示) */ dataCardVisible: kv>('data-card-visible'), /** 数据页画像卡片顺序 (卡片key 数组, 长度=卡片总数) */ dataCardOrder: kv('data-card-order'), } as const