feat(Tab设置+UI主题): 迭代09 Tab设置统一管理 + 迭代10 UI适配DSH主题
迭代09 (R-011): Tab 设置统一管理所有会话 tab
- settings.tabs 布尔对象 → 统一有序数组 (kind: builtin|strategy)
- 旧配置 schema union 兼容 + 读取归一化无感迁移
- strategies 收窄为 {id,name},策略行 name join 自动跟随改名
- 策略增删联动 tabs 条目 (add 追加末尾 / remove 联动删除)
- 客户端统一注册读 tabs 数组,内置与策略可任意混排
- 设置页 Tab 设置子 tab:混排表格 + 拖动手柄 + 显隐开关 + 落点立即持久化
- 策略分组瘦身:仅 新增/重命名/删除 + 指引提示
- 回归测试: test-r011-tabs (23) + test-r011-api (12)
迭代10 (R-012): UI 适配 DSH 浅色/深色/跟随系统主题
- 10 个视图文件 141 处硬编码色 → var(--dsw-alias-*, fallback)
- 语义映射: bg-layer/label/border/state/button-contrast 等
- 淡底 color-mix 自适应;遮罩 bg-mask-1;阴影 shadow-lv3
This commit is contained in:
+31
-61
@@ -23,21 +23,8 @@ import { MarketDataProvider } from './market/MarketDataProvider.jsx';
|
||||
|
||||
export const inject = ['slots', 'connection'];
|
||||
|
||||
async function fetchStrategies() {
|
||||
try {
|
||||
const res = await fetch('/odl/api/strategies', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
const data = await res.json();
|
||||
return data && data.ok ? data.value : [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchTabConfig() {
|
||||
async function fetchTabs() {
|
||||
try {
|
||||
const res = await fetch('/odl/api/tabs', {
|
||||
method: 'POST',
|
||||
@@ -45,29 +32,21 @@ async function fetchTabConfig() {
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data && data.ok) {
|
||||
return {
|
||||
allPositions: data.value.allPositions !== false,
|
||||
tradeRecords: data.value.tradeRecords !== false,
|
||||
watchlist: data.value.watchlist !== false,
|
||||
};
|
||||
}
|
||||
if (data && data.ok && Array.isArray(data.value)) return data.value;
|
||||
} catch (e) { /* ignore */ }
|
||||
return { allPositions: true, tradeRecords: true, watchlist: true };
|
||||
return null;
|
||||
}
|
||||
|
||||
function strategyTabId(strategyId) {
|
||||
return 'odl-strategy-' + strategyId;
|
||||
}
|
||||
|
||||
const GENERAL_TABS = [
|
||||
{ key: 'allPositions', id: 'odl-all-positions', label: '全部持仓', order: 10,
|
||||
render: (withConnection) => withConnection((props) => createElement(AllPositionsTab, props)) },
|
||||
{ key: 'tradeRecords', id: 'odl-trade-records', label: '交易记录', order: 11,
|
||||
render: (withConnection) => withConnection((props) => createElement(TradeRecordsTab, props)) },
|
||||
{ key: 'watchlist', id: 'odl-watchlist', label: '关注列表', order: 12,
|
||||
render: (withConnection) => withConnection((props) => createElement(PlaceholderTab, { ...props, title: '关注列表' })) },
|
||||
];
|
||||
// R-011:内置 tab 渲染映射(refKey → render);order 由 settings.tabs 统一管理
|
||||
const GENERAL_RENDER = {
|
||||
allPositions: (withConnection) => withConnection((props) => createElement(AllPositionsTab, props)),
|
||||
tradeRecords: (withConnection) => withConnection((props) => createElement(TradeRecordsTab, props)),
|
||||
watchlist: (withConnection) => withConnection((props) => createElement(PlaceholderTab, { ...props, title: '关注列表' })),
|
||||
};
|
||||
|
||||
export function apply(ctx) {
|
||||
const connection = ctx.get('connection');
|
||||
@@ -109,40 +88,31 @@ export function apply(ctx) {
|
||||
tabDisposers = [];
|
||||
};
|
||||
|
||||
const registerGeneralTabs = (tabConfig) => {
|
||||
for (const tab of GENERAL_TABS) {
|
||||
if (tabConfig[tab.key] === false) continue;
|
||||
const dispose = ctx.slots.register({
|
||||
name: 'conversation.view',
|
||||
id: tab.id,
|
||||
order: tab.order,
|
||||
label: () => tab.label,
|
||||
}, tab.render(withConnection));
|
||||
tabDisposers.push(dispose);
|
||||
}
|
||||
};
|
||||
|
||||
const registerStrategyTabs = (strategies) => {
|
||||
const visible = strategies.filter((s) => s.visible !== false);
|
||||
visible.forEach((s, idx) => {
|
||||
const dispose = ctx.slots.register({
|
||||
name: 'conversation.view',
|
||||
id: strategyTabId(s.id),
|
||||
order: 13 + idx,
|
||||
label: () => s.name,
|
||||
}, withConnection((props) => createElement(StrategyTab, { ...props, strategyId: s.id, strategyName: s.name })));
|
||||
tabDisposers.push(dispose);
|
||||
});
|
||||
return visible.length;
|
||||
};
|
||||
|
||||
// R-011:统一注册 —— 读 settings.tabs 有序数组,按 order 排序、过滤 visible
|
||||
// builtin 走内置 render(GENERAL_RENDER),strategy 走 StrategyTab
|
||||
const registerAllTabs = async () => {
|
||||
const [strategies, tabConfig] = await Promise.all([fetchStrategies(), fetchTabConfig()]);
|
||||
const tabs = await fetchTabs();
|
||||
if (disposed) return;
|
||||
disposeAllTabs();
|
||||
registerGeneralTabs(tabConfig);
|
||||
const n = registerStrategyTabs(strategies);
|
||||
ctx.logger?.info?.('[one-divine-lot] 已注册通用 tabs + ' + n + ' 个策略 tab');
|
||||
if (!tabs) return;
|
||||
const sorted = tabs.slice().sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
||||
let n = 0;
|
||||
for (const t of sorted) {
|
||||
if (t.visible === false) continue;
|
||||
const render = t.kind === 'builtin'
|
||||
? GENERAL_RENDER[t.refKey]
|
||||
: (withConnection) => withConnection((props) => createElement(StrategyTab, { ...props, strategyId: t.refId, strategyName: t.name }));
|
||||
if (!render) continue;
|
||||
const dispose = ctx.slots.register({
|
||||
name: 'conversation.view',
|
||||
id: t.kind === 'builtin' ? t.id : strategyTabId(t.refId),
|
||||
order: t.order ?? 0,
|
||||
label: () => t.name ?? t.id,
|
||||
}, render(withConnection));
|
||||
tabDisposers.push(dispose);
|
||||
n++;
|
||||
}
|
||||
ctx.logger?.info?.('[one-divine-lot] 已注册 ' + n + ' 个会话 tab');
|
||||
};
|
||||
|
||||
registerAllTabs();
|
||||
|
||||
Reference in New Issue
Block a user