diff --git a/docs/console_information_design.zh-CN.md b/docs/console_information_design.zh-CN.md
new file mode 100644
index 0000000..c93c4e1
--- /dev/null
+++ b/docs/console_information_design.zh-CN.md
@@ -0,0 +1,28 @@
+# 日常管理控制台
+
+本次只调整信息组织和操作呈现,保留现有账户、策略权限及确认流程,不增加服务、依赖或费用。
+
+## 日常需要回答的问题
+
+| 页面 | 主要内容 | 次级内容 |
+| --- | --- | --- |
+| 运行总览(默认) | 全部已配置账户、配置开关、实际读回、记录时间;明确待确认和不一致 | 筛选、搜索、进入指定账户;确实需要人工处理的事项 |
+| 账户管理 | 选择平台和账户;查看当前策略、保存状态、实际开关和调度 | 展开策略设置后调整;高级资金参数和提交前核对继续保留 |
+| 研究与确认 | 选择意向平台,查看待确认候选和确认结果 | 风险解释、研究记录、诊断详情按需展开 |
+
+不把研究时间当作页面刷新时间;不把“配置启用”当作“运行正常”。读回缺失、过期或未明确关联账户时,保持待确认。记录时间明确表示读回时间,不冒充成交时间或最新成功周期。首页不展示原始 ticket、SHA、JSON、设计预览风险数据,也不伪造收益、余额或健康状态。
+
+研究数据仍保留,移到单独页面;现有实盘恢复、停用、策略切换和研究确认保持各自的权限与提交保护。查看、搜索、筛选、刷新均为只读操作。
+
+## 布局与文案
+
+- 电脑:固定窄侧栏,主区域约 1120 px;四项配置统计、紧凑状态提示、账户列表。
+- 手机:顶部三项导航;统计为两列;账户列表变为纵向信息块,不依赖横向滚动;账户详情同样纵向排列。
+- 导航固定为“运行总览 / 账户管理 / 研究与确认”。首页标题“运行总览”,说明“查看已保存的配置、实际读回与最近运行记录。”。
+- 状态与操作均有中英文;每项操作使用语义化按钮和清晰焦点。主要触控目标至少 44 px。
+- 配色:背景 `#f6f7f9`、正文 `#172d35`、辅助文字 `#667680`、主色 `#10766a`、边线 `#dfe6ea`;待确认用中性色,不自动标成故障;配置不一致用浅琥珀色。
+- 字体沿用系统无衬线字体;主标题约 32 px,正文 14–15 px;手机主标题 26 px。间距以 8 px 为基本单位,圆角约 8 px,不使用装饰图表。
+
+## 交互与验证
+
+验证默认总览、准确定位账户、配置/实际状态分离、搜索和筛选、空结果、读取失败、刷新时间、研究切页、折叠详情、语言切换以及电脑/手机布局。线上仅检查读取和导航;提交类操作在隔离测试数据中验证,不能为了页面测试触发实盘。
diff --git a/tests/console_runtime_state_validation.mjs b/tests/console_runtime_state_validation.mjs
index 5839223..bb40cf5 100644
--- a/tests/console_runtime_state_validation.mjs
+++ b/tests/console_runtime_state_validation.mjs
@@ -356,12 +356,13 @@ for (const sample of [
assert.equal(fn('ibkr',{}),sample.expected);
});
-test('account overview exposes desired/applied/application columns and per-row application status', () => {
+test('account details keep saved, deployed and application state separate', () => {
const html = readFileSync(new URL('../web/strategy-switch-console/index.html', import.meta.url), 'utf8');
const app = readFileSync(new URL('../web/strategy-switch-console/app.js', import.meta.url), 'utf8');
assert.ok(html.includes('data-i18n="configuredSwitch"'));
- assert.ok(html.includes('data-i18n="deployedSwitch"'));
- assert.ok(html.includes('data-i18n="applicationStatus"'));
+ assert.ok(html.includes('class="account-facts"'));
+ assert.ok(app.includes('["deployedSwitch", accountDeploymentText(platform, account)]'));
+ assert.ok(app.includes('["applicationStatus", accountApplicationText(platform, account)]'));
const overview = app.slice(app.indexOf('function renderAccountOverview'), app.indexOf('function renderControls'));
assert.ok(overview.includes('currentRuntimeTargetText(platform, account)'));
assert.ok(overview.includes('accountDeploymentText(platform, account)'));
@@ -552,6 +553,7 @@ test('opening account settings selects the exact account without submitting or e
const fn = frontendFunction('openAccountSettings', { state, el: id => nodes[id], render: () => events.push('render'), Event });
fn('ibkr', { key: 'second-account' });
assert.equal(state.selected, 'ibkr');
+ assert.equal(state.view, 'accounts');
assert.equal(nodes['account-select'].value, 'second-account');
assert.equal(nodes['strategy-settings'].open, true);
assert.equal(state.forms.ibkr.runtimeTargetMode, 'current');
@@ -568,7 +570,7 @@ test('decisions and account observations stay outside collapsed strategy setting
}
assert.ok(html.indexOf('id="promotion-decision-panel"') < html.indexOf('id="account-overview"'));
assert.ok(html.indexOf('id="quick-form"') > settings);
- assert.ok(html.includes('data-i18n="accountNextStep"'));
+ assert.ok(source.includes('["accountDetailAction", accountNextStep(platform, account).label]'));
assert.equal((html.match(/id="dispatch-button"/g) || []).length, 1);
});
@@ -625,3 +627,81 @@ test('hiding all platforms also hides account observations outside the settings
platformMeta: { ibkr: { console_visible: false } }, hasPrivateConfig: () => true })();
assert.equal(nodes['switch-view'].hidden, true);
});
+
+for (const sample of [
+ { configured: undefined, application: 'deploymentUnverified', unknown: true, attention: true },
+ { configured: true, application: 'deploymentUnverified', unknown: true, attention: true },
+ { configured: false, application: 'deploymentUnverified', unknown: true, attention: true },
+ { configured: true, application: 'settingsNotApplied', unknown: false, attention: true },
+ { configured: false, application: 'switchesApplied', unknown: false, attention: false },
+ { configured: true, application: 'switchesApplied', observation: 'attention', unknown: false, attention: true },
+]) test('overview keeps saved settings separate from actual state '+JSON.stringify(sample), () => {
+ const context = {
+ hasPrivateConfig: () => true,
+ platformMeta: { binance: { label: 'Binance' }, hidden: { console_visible: false } },
+ optionsFor: () => [{ key: 'account', label: 'Account' }],
+ currentStrategyForAccount: () => 'example', strategyLabel: () => 'Example',
+ runtimeTargetStateForAccount: () => ({ known: sample.configured !== undefined, enabled: sample.configured }),
+ accountApplicationText: () => sample.application,
+ accountMonitoringRecord: () => ({ freshness: { data_status: 'ready' }, execution_observation: { code: sample.observation } }),
+ accountMonitoringAge: () => '1 minute ago', accountMonitoringText: () => 'record', t: x => x,
+ };
+ const rows = frontendFunction('overviewAccounts', context)();
+ assert.equal(rows.length, 1, 'hidden platforms never appear');
+ assert.equal(rows[0].configured, sample.configured === undefined ? 'unknown' : sample.configured ? 'enabled' : 'disabled');
+ assert.equal(rows[0].unknown, sample.unknown);
+ assert.equal(rows[0].attention, sample.attention);
+ assert.equal(frontendFunction('overviewAccounts', {...context, hasPrivateConfig: () => false})().length, 0);
+});
+
+test('overview filters preserve unknown and search only the chosen scope', () => {
+ const rows = [
+ { platformLabel: 'Binance', account: { label: 'Main' }, strategy: 'Crypto', configured: 'enabled', attention: true },
+ { platformLabel: 'IBKR', account: { label: 'Second' }, strategy: 'Trend', configured: 'disabled', attention: false },
+ { platformLabel: 'LongBridge', account: { label: 'SG' }, strategy: 'Trend', configured: 'unknown', attention: true },
+ ];
+ const filter = frontendFunction('filterOverviewAccounts', {});
+ assert.equal(filter(rows, 'disabled', '').length, 1);
+ assert.equal(filter(rows, 'enabled', '').length, 1);
+ assert.equal(filter(rows, 'attention', '').length, 2);
+ assert.equal(filter(rows, 'all', ' bINAnCe ').length, 1);
+ assert.equal(filter(rows, 'enabled', 'Trend').length, 0);
+ assert.equal(filter(rows, 'all', 'not-found').length, 0);
+});
+
+for (const view of ['overview', 'accounts', 'research']) test(`workspace navigation isolates ${view} from the other jobs`, () => {
+ const nodes = {};
+ const nav = { hidden: true };
+ const buttons = ['overview', 'accounts', 'research'].map(workspace => ({
+ dataset: { workspace }, attributes: {},
+ setAttribute(key, value) { this.attributes[key] = value; },
+ removeAttribute(key) { delete this.attributes[key]; },
+ }));
+ const state = { view, appReady: true, auth: { allowed: true }, refreshing: false };
+ const context = { state, t: key => key, el: id => nodes[id] ??= {},
+ document: { querySelector: () => nav, querySelectorAll: () => buttons } };
+ const render = frontendFunction('renderWorkspace', context);
+ render();
+ for (const name of ['overview', 'accounts', 'research']) assert.equal(nodes[`${name}-view`].hidden, name !== view);
+ assert.equal(nodes['platform-strip'].hidden, view === 'overview');
+ assert.equal(nodes['health-view'].hidden, view !== 'research');
+ assert.equal(buttons.filter(button => button.attributes['aria-current'] === 'page').length, 1);
+ assert.equal(nav.hidden, false);
+ state.auth.allowed = false;
+ render();
+ assert.equal(nav.hidden, true, 'private navigation must disappear on logout');
+});
+
+test('viewing an account does not expand editing or change the configured switch', () => {
+ const state = { selected: 'longbridge', forms: { binance: { runtimeTargetMode: 'current' } } };
+ const nodes = {
+ 'strategy-settings': { open: true }, 'workspace-title': { scrollIntoView() {} },
+ 'account-select': { value: '', dispatchEvent() {}, focus() {} },
+ };
+ const fn = frontendFunction('openAccountSettings', { state, el: id => nodes[id], render() {}, Event });
+ fn('binance', { key: 'default' }, false);
+ assert.equal(state.view, 'accounts');
+ assert.equal(nodes['account-select'].value, 'default');
+ assert.equal(nodes['strategy-settings'].open, false);
+ assert.equal(state.forms.binance.runtimeTargetMode, 'current');
+});
diff --git a/tests/strategy_switch_worker_validation.mjs b/tests/strategy_switch_worker_validation.mjs
index cbd605c..3cf6222 100644
--- a/tests/strategy_switch_worker_validation.mjs
+++ b/tests/strategy_switch_worker_validation.mjs
@@ -56,7 +56,7 @@ assert.ok(__test.currentStrategiesTimeoutMs >= 8000);
const renderPlatformsBody = indexHtml.match(/function renderPlatforms\(\) \{([\s\S]*?)\n \}/)?.[1] || "";
assert.ok(!renderPlatformsBody.includes("syncStrategyForAccount("));
assert.equal(indexHtml.includes(".innerHTML"), false);
-assert.ok(indexHtml.includes('
'));
+assert.match(indexHtml, //);
assert.ok(indexHtml.includes('id="boot-screen"'));
assert.ok(indexHtml.includes('id="app-shell"'));
assert.equal(indexHtml.includes('runtime-authority-status'), false);
@@ -120,8 +120,8 @@ assert.ok(indexHtml.includes('switchSurface.classList.toggle("summary-hidden", !
assert.equal(indexHtml.match(/Generated by inject_platform_config\.py/g)?.length, 1);
assert.ok(indexHtml.includes(''));
assert.ok(indexHtml.includes(''));
-assert.ok(indexHtml.includes('/app.js?v=operator-console-v3'));
-assert.ok(indexHtml.includes('/app.css?v=operator-console-v3'));
+assert.ok(indexHtml.includes('/app.js?v=console-clarity-v1'));
+assert.ok(indexHtml.includes('/app.css?v=console-clarity-v1'));
assert.equal(indexHtml.includes('
-
+
+
Q
QuantStrategyLab
-
低频决策台
+
管理控制台
+
@@ -45,12 +51,42 @@
管理你的量化平台
-
- 决策与账户
- —
+
+ —
+
+
+ - 已配置账户
- —
+ - 配置已启用
- —
+ - 配置已停用
- —
+ - 实际状态待确认
- —
+
+
+
+
+
@@ -89,6 +125,10 @@
恢复事项
登录后查看需要恢复或复核的事项。
+
+
+
+ 研究候选与当前运行分开管理。接受候选只记录意向,不启用交易。
+
+
目标平台
@@ -154,10 +196,10 @@
登录后查看账号配置。
- 左右滑动查看实际状态与下一步
+
-
-
| 账户实例 | 配置策略 | 配置开关 | 实际开关 | 自动调度 | 应用状态 | 记录时间 | 下一步 |
|---|
+
实际开关和调度以记录时间为准,不代表已经成交。
@@ -175,11 +217,6 @@
01
选择范围平台、账号、策略与目标环境
-
-
+
高级详情
@@ -404,6 +442,6 @@
自动化任务
-
+
diff --git a/web/strategy-switch-console/page_asset.js b/web/strategy-switch-console/page_asset.js
index 16d98bf..bd750d7 100644
--- a/web/strategy-switch-console/page_asset.js
+++ b/web/strategy-switch-console/page_asset.js
@@ -1,2 +1,2 @@
// Generated by python/scripts/sync_strategy_switch_page_asset.py; do not edit by hand.
-export const PAGE_HTML = "\n\n\n
\n
\n
\n
QuantStrategyLab 控制台\n
\n
\n\n\n\n\n\n
\n \n
QuantStrategyLab
\n
低频决策台
\n
\n \n \n\n
\n \n
初始化控制台\n
读取策略配置
\n
正在读取登录状态、账号配置和当前状态。
\n
\n
\n \n\n
\n \n Q\n 管理你的量化平台
\n 配置策略,关注运行,处理重要决策。
\n 登录\n \n \n\n
\n \n \n \n\n \n \n \n
待办确认
\n
这里只显示需要你亲自确认的事项。
\n
\n \n 等待登录\n 最近计算:—\n
\n \n \n
✓\n
\n 登录后查看待处理事项和运行状态。\n —\n
\n
\n \n 0待处理\n 0待复核\n 0暂停中\n 0监控对象\n
\n \n \n\n\n \n
恢复事项
\n
登录后查看需要恢复或复核的事项。
\n
\n
\n \n\n \n\n \n \n 目标平台\n \n
\n\n 登录后查看账号配置。
\n 左右滑动查看实际状态与下一步
\n\n \n
| 账户实例 | 配置策略 | 配置开关 | 实际开关 | 自动调度 | 应用状态 | 记录时间 | 下一步 |
|---|
\n
\n\n 实际开关和调度以记录时间为准,不代表已经成交。
\n \n 策略设置需要启停或调整策略时展开
\n \n
恢复当前实盘目标只保存启用开关,保留现有策略和资金配置。运行端仍会核验恢复记录。
\n
\n
\n
\n \n \n
\n\n
\n
\n
登录后才可提交受限配置。
\n
\n
\n\n \n \n \n \n \n 高级详情
\n \n
\n
运行概览
\n
默认只显示需要关注的问题。
\n
\n
\n
\n
\n 正在读取监测记录…
\n \n \n 监测详情
\n \n \n \n 策略健康摘要
\n —\n —\n 登录后读取私有策略健康快照。
\n \n 0策略总数\n 0健康\n 0观察\n 0需要复核\n 0严重\n
\n \n
需要关注
\n
\n \n \n
\n
\n \n \n \n 查看诊断详情
\n 仅在排查问题或查看研究记录时使用。
\n \n
市场研究
\n
登录后查看市场研究和观察记录。
\n
\n
\n \n
系统建议
\n
登录后查看系统建议。
\n
\n
\n\n \n
执行与成交记录
\n
登录后查看执行和成交记录。
\n
\n
\n \n
自动化任务
\n
登录后查看自动化任务。
\n
\n
\n \n \n \n\n \n \n\n\n";
+export const PAGE_HTML = "\n\n\n
\n
\n
\n
QuantStrategyLab 控制台\n
\n
\n\n\n\n\n\n
\n \n
Q\n
QuantStrategyLab
\n
管理控制台
\n
\n \n \n \n\n
\n \n
初始化控制台\n
读取策略配置
\n
正在读取登录状态、账号配置和当前状态。
\n
\n
\n \n\n
\n \n Q\n 管理你的量化平台
\n 配置策略,关注运行,处理重要决策。
\n 登录\n \n \n\n
\n \n \n\n \n \n - 已配置账户
- —
\n - 配置已启用
- —
\n - 配置已停用
- —
\n - 实际状态待确认
- —
\n
\n \n \n
\n\n \n \n \n
待办确认
\n
这里只显示需要你亲自确认的事项。
\n
\n \n 等待登录\n 最近计算:—\n
\n \n \n
✓\n
\n 登录后查看待处理事项和运行状态。\n —\n
\n
\n \n 0待处理\n 0待复核\n 0暂停中\n 0监控对象\n
\n \n \n\n\n \n
恢复事项
\n
登录后查看需要恢复或复核的事项。
\n
\n
\n \n \n \n 研究候选与当前运行分开管理。接受候选只记录意向,不启用交易。
\n \n\n \n \n\n \n \n \n 目标平台\n \n
\n\n 登录后查看账号配置。
\n \n\n \n\n 实际开关和调度以记录时间为准,不代表已经成交。
\n \n 策略设置需要启停或调整策略时展开
\n \n
恢复当前实盘目标只保存启用开关,保留现有策略和资金配置。运行端仍会核验恢复记录。
\n
\n
\n
\n \n \n
\n\n
\n
\n
登录后才可提交受限配置。
\n
\n
\n\n \n \n \n \n \n \n 高级详情
\n \n
\n
运行概览
\n
默认只显示需要关注的问题。
\n
\n
\n
\n
\n 正在读取监测记录…
\n \n \n 监测详情
\n \n \n \n 策略健康摘要
\n —\n —\n 登录后读取私有策略健康快照。
\n \n 0策略总数\n 0健康\n 0观察\n 0需要复核\n 0严重\n
\n \n
需要关注
\n
\n \n \n
\n
\n \n \n \n 查看诊断详情
\n 仅在排查问题或查看研究记录时使用。
\n \n
市场研究
\n
登录后查看市场研究和观察记录。
\n
\n
\n \n
系统建议
\n
登录后查看系统建议。
\n
\n
\n\n \n
执行与成交记录
\n
登录后查看执行和成交记录。
\n
\n
\n \n
自动化任务
\n
登录后查看自动化任务。
\n
\n
\n \n \n \n\n \n \n\n\n";