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:
2026-09-02 14:00:56 +08:00
parent 35f2ad1a23
commit 4ac8a29805
15 changed files with 629 additions and 385 deletions
+21 -21
View File
@@ -42,9 +42,9 @@ function fmtNum(v, digits = 2) {
/** 方向样式:红买绿卖 */
function directionStyle(direction) {
if (direction === 'buy') return { color: '#d32f2f', fontWeight: 'bold' };
if (direction === 'sell') return { color: '#2e7d32', fontWeight: 'bold' };
return { color: '#999' };
if (direction === 'buy') return { color: 'var(--dsw-alias-state-error-primary, #d32f2f)', fontWeight: 'bold' };
if (direction === 'sell') return { color: 'var(--dsw-alias-state-success-primary, #2e7d32)', fontWeight: 'bold' };
return { color: 'var(--dsw-alias-label-tertiary, #999)' };
}
function directionText(direction) {
if (direction === 'buy') return '买入';
@@ -309,7 +309,7 @@ export function TradeRecordsTab() {
<select
value={strategyFilter}
onChange={(e) => setStrategyFilter(e.target.value)}
style={{ fontSize: 12, padding: '2px 6px', border: '1px solid #ccc', borderRadius: 4, color: '#555' }}
style={{ fontSize: 12, padding: '2px 6px', border: '1px solid var(--dsw-alias-border-l2, #ccc)', borderRadius: 4, color: 'var(--dsw-alias-label-secondary, #555)' }}
title="按策略过滤"
>
<option value="">全部策略</option>
@@ -322,14 +322,14 @@ export function TradeRecordsTab() {
</div>
</div>
{attributionSaved && (
<div style={{ marginBottom: 8, fontSize: 12, color: attributionSaved.startsWith('保存失败') ? '#c62828' : '#1b5e20', background: attributionSaved.startsWith('保存失败') ? '#fdecea' : '#e8f5e9', padding: '4px 8px', borderRadius: 4 }}>
<div style={{ marginBottom: 8, fontSize: 12, color: attributionSaved.startsWith('保存失败') ? 'var(--dsw-alias-state-error-primary, #c62828)' : 'var(--dsw-alias-state-success-primary, #1b5e20)', background: attributionSaved.startsWith('保存失败') ? 'color-mix(in srgb, var(--dsw-alias-state-error-primary, #c62828) 10%, var(--dsw-alias-bg-layer-1, #fff))' : 'color-mix(in srgb, var(--dsw-alias-state-success-primary, #2e7d32) 10%, var(--dsw-alias-bg-layer-1, #fff))', padding: '4px 8px', borderRadius: 4 }}>
{attributionSaved}
</div>
)}
<LoadState loading={loading && !orders} error={error} onRetry={load} autoRetry={2}>
{!orders?.length ? (
<div style={{ padding: 32, textAlign: 'center', color: '#999' }}>
<div style={{ padding: 32, textAlign: 'center', color: 'var(--dsw-alias-label-tertiary, #999)' }}>
<div style={{ fontSize: 15, marginBottom: 8 }}>
{range ? range.start + ' ~ ' + range.end + (isHistoryRange ? '(本地历史)' : '') : ''}
</div>
@@ -340,9 +340,9 @@ export function TradeRecordsTab() {
) : (
<TradeTable rows={rows} toggleSort={toggleSort} sortKey={sortKey} sortDir={sortDir} arrow={arrow} toggleExpand={toggleExpand} expanded={expanded} attributionCandidates={attributionCandidates} loadCandidates={loadCandidates} setAttribution={setAttribution} />
)}
<div style={{ marginTop: 8, fontSize: 12, color: '#999' }}>
<span style={{ color: '#d32f2f' }}> </span>
<span style={{ marginLeft: 8, color: '#2e7d32' }}> </span>
<div style={{ marginTop: 8, fontSize: 12, color: 'var(--dsw-alias-label-tertiary, #999)' }}>
<span style={{ color: 'var(--dsw-alias-state-error-primary, #d32f2f)' }}> </span>
<span style={{ marginLeft: 8, color: 'var(--dsw-alias-state-success-primary, #2e7d32)' }}> </span>
<span style={{ marginLeft: 12 }}>点击委托行展开成交明细 · 点击表头排序</span>
{isHistoryRange && <span style={{ marginLeft: 12 }}>· 历史数据 = 本地积累</span>}
</div>
@@ -356,7 +356,7 @@ function TradeTable({ rows, toggleSort, sortKey, sortDir, arrow, toggleExpand, e
return (
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 13 }}>
<thead>
<tr style={{ textAlign: 'left', borderBottom: '2px solid #ddd' }}>
<tr style={{ textAlign: 'left', borderBottom: '2px solid var(--dsw-alias-border-l2, #ddd)' }}>
<th style={thStyle}> </th>
<th onClick={() => toggleSort('insertTime')} style={thClickStyle}>时间{arrow('insertTime')}</th>
<th onClick={() => toggleSort('code')} style={thClickStyle}>代码{arrow('code')}</th>
@@ -404,9 +404,9 @@ function OrderRows({ order, expanded, onToggle, attributionCandidates, onLoadCan
<tr
onClick={onToggle}
style={{
borderBottom: '1px solid #eee',
borderBottom: '1px solid var(--dsw-alias-border-l1, #eee)',
cursor: hasTrades ? 'pointer' : 'default',
background: expanded ? '#f5f5f5' : 'transparent',
background: expanded ? 'var(--dsw-alias-interactive-bg-hover, #f5f5f5)' : 'transparent',
}}
>
<td style={{ ...tdStyle, textAlign: 'center', width: 28 }}>
@@ -420,7 +420,7 @@ function OrderRows({ order, expanded, onToggle, attributionCandidates, onLoadCan
transition: 'transform .15s ease',
transform: expanded ? 'rotate(90deg)' : 'rotate(0deg)',
cursor: 'pointer',
color: '#2e7d32',
color: 'var(--dsw-alias-state-success-primary, #2e7d32)',
verticalAlign: 'middle',
}}
>
@@ -434,7 +434,7 @@ function OrderRows({ order, expanded, onToggle, attributionCandidates, onLoadCan
/>
</svg>
) : (
<span style={{ color: '#ccc', fontSize: 10 }}>·</span>
<span style={{ color: 'var(--dsw-alias-border-l3, #ccc)', fontSize: 10 }}>·</span>
)}
</td>
<td style={tdStyle}>{fmtTime(order.insertTime)}</td>
@@ -461,11 +461,11 @@ function OrderRows({ order, expanded, onToggle, attributionCandidates, onLoadCan
</td>
</tr>
{expanded && hasTrades && (
<tr style={{ borderBottom: '1px solid #eee', background: '#fafafa' }}>
<tr style={{ borderBottom: '1px solid var(--dsw-alias-border-l1, #eee)', background: 'var(--dsw-alias-bg-layer-2, #fafafa)' }}>
<td colSpan={16} style={{ padding: '4px 8px 12px' }}>
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 12, marginTop: 4 }}>
<thead>
<tr style={{ borderBottom: '1px solid #ddd', color: '#666' }}>
<tr style={{ borderBottom: '1px solid var(--dsw-alias-border-l2, #ddd)', color: 'var(--dsw-alias-label-secondary, #666)' }}>
<th style={{ ...tdStyle, textAlign: 'left' }}>成交时间</th>
<th style={{ ...tdStyle, textAlign: 'right' }}>成交价</th>
<th style={{ ...tdStyle, textAlign: 'right' }}>成交量</th>
@@ -475,12 +475,12 @@ function OrderRows({ order, expanded, onToggle, attributionCandidates, onLoadCan
</thead>
<tbody>
{order.trades.map((t) => (
<tr key={t.tradeId} style={{ borderBottom: '1px solid #f0f0f0' }}>
<tr key={t.tradeId} style={{ borderBottom: '1px solid var(--dsw-alias-border-l1, #f0f0f0)' }}>
<td style={tdStyle}>{fmtTime(t.tradeTime)}</td>
<td style={{ ...tdStyle, textAlign: 'right' }}>{fmtNum(t.price)}</td>
<td style={{ ...tdStyle, textAlign: 'right' }}>{fmtNum(t.volume, 0)}</td>
<td style={{ ...tdStyle, textAlign: 'right' }}>{fmtNum(t.amount)}</td>
<td style={{ ...tdStyle, textAlign: 'right', color: '#999' }}>{t.tradeId}</td>
<td style={{ ...tdStyle, textAlign: 'right', color: 'var(--dsw-alias-label-tertiary, #999)' }}>{t.tradeId}</td>
</tr>
))}
</tbody>
@@ -517,10 +517,10 @@ function AttributionSelect({ order, candidates, onLoadCandidates, setAttribution
style={{
fontSize: 12,
padding: '2px 4px',
border: '1px solid ' + (current ? '#2e7d32' : '#ccc'),
border: '1px solid ' + (current ? 'var(--dsw-alias-state-success-primary, #2e7d32)' : 'var(--dsw-alias-border-l2, #ccc)'),
borderRadius: 4,
background: current ? '#e8f5e9' : '#fff',
color: current ? '#1b5e20' : '#999',
background: current ? 'color-mix(in srgb, var(--dsw-alias-state-success-primary, #2e7d32) 12%, var(--dsw-alias-bg-layer-1, #fff))' : 'var(--dsw-alias-bg-layer-1, #fff)',
color: current ? 'var(--dsw-alias-state-success-primary, #1b5e20)' : 'var(--dsw-alias-label-tertiary, #999)',
maxWidth: 120,
}}
title="设置该委托的策略归属(可随时修改)"