improve auto-connect loading states

This commit is contained in:
kris
2026-03-28 08:58:59 +08:00
parent 693be5bca9
commit f05a43fee3
2 changed files with 68 additions and 0 deletions

View File

@@ -1149,16 +1149,37 @@ async function ensureAutoSession(options = {}) {
return Boolean(appState.session); return Boolean(appState.session);
} }
appState.autoConnectAttempted = true; appState.autoConnectAttempted = true;
renderAll();
try { try {
await loginWithAutoSession(backendUrl); await loginWithAutoSession(backendUrl);
return true; return true;
} catch (error) { } catch (error) {
appState.autoConnectError = formatActionErrorMessage(error, "自动连接失败"); appState.autoConnectError = formatActionErrorMessage(error, "自动连接失败");
persistSession(null); persistSession(null);
renderAll();
return false; return false;
} }
} }
function isAutoConnectionPending() {
return !appState.session
&& appState.autoConnectAttempted
&& !appState.autoConnectSuppressed
&& !appState.autoConnectError;
}
function renderAutoConnectingScreen(screenTitle, nextStepText) {
return screenShell(
screenTitle,
"正在自动连接工作区,成功后会自动替换成真实内容。",
`${button("连接状态", "open-auth", "primary")}`,
renderEmptyState(
"正在自动连接工作区",
nextStepText || "如果超过几秒仍未进入真实页面,可以点右上角查看连接状态。"
)
);
}
async function refreshFromAuthModal() { async function refreshFromAuthModal() {
const modal = document.querySelector(".auth-modal-backdrop"); const modal = document.querySelector(".auth-modal-backdrop");
const visible = modal && !modal.classList.contains("hidden"); const visible = modal && !modal.classList.contains("hidden");
@@ -3878,6 +3899,9 @@ function renderAdminWorkbenchScreen() {
function renderDashboardScreen() { function renderDashboardScreen() {
if (!appState.session) { if (!appState.session) {
if (isAutoConnectionPending()) {
return renderAutoConnectingScreen("项目总台", "会话签发成功后,这里会自动切成真实项目总台。");
}
return screenShell( return screenShell(
"项目总台", "项目总台",
"先自动连接工作区再加载项目、对标、Agent 和生产状态。", "先自动连接工作区再加载项目、对标、Agent 和生产状态。",
@@ -3907,6 +3931,9 @@ function renderDashboardScreen() {
function renderProjectsScreen() { function renderProjectsScreen() {
if (!appState.dashboard) { if (!appState.dashboard) {
if (isAutoConnectionPending()) {
return renderAutoConnectingScreen("我的项目", "工作区就绪后,这里会自动加载真实项目和导入队列。");
}
return screenShell("我的项目", "先完成工作区自动连接,再加载项目。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("项目未加载", "自动连接成功后,这里会显示真实项目和导入队列。")); return screenShell("我的项目", "先完成工作区自动连接,再加载项目。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("项目未加载", "自动连接成功后,这里会显示真实项目和导入队列。"));
} }
const projects = safeArray(appState.dashboard.projects); const projects = safeArray(appState.dashboard.projects);
@@ -4109,6 +4136,9 @@ function renderDiscoveryRelationsSection(linkedAccounts, similarCandidates) {
function renderDiscoveryScreen() { function renderDiscoveryScreen() {
if (!appState.dashboard) { if (!appState.dashboard) {
if (isAutoConnectionPending()) {
return renderAutoConnectingScreen("找对标", "工作区就绪后,这里会自动显示当前平台的账号列表和详情。");
}
return screenShell("找对标", "完成工作区自动连接后才能加载真实对标账号。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("对标库未加载", "自动连接成功后,这里会显示当前平台的账号列表和详情。")); return screenShell("找对标", "完成工作区自动连接后才能加载真实对标账号。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("对标库未加载", "自动连接成功后,这里会显示当前平台的账号列表和详情。"));
} }
const query = appState.discoveryQuery.toLowerCase(); const query = appState.discoveryQuery.toLowerCase();
@@ -4282,6 +4312,9 @@ function renderDiscoveryScreen() {
function renderTrackingScreen() { function renderTrackingScreen() {
if (!appState.dashboard) { if (!appState.dashboard) {
if (isAutoConnectionPending()) {
return renderAutoConnectingScreen("跟踪账号", "工作区就绪后,这里会自动加载真实跟踪对象和日报。");
}
return screenShell("跟踪账号", "完成工作区自动连接后才能生成真实日报。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("日报未加载", "当前还没有可用的对标账号数据。")); return screenShell("跟踪账号", "完成工作区自动连接后才能生成真实日报。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("日报未加载", "当前还没有可用的对标账号数据。"));
} }
const currentPlatform = getCurrentPlatformValue(); const currentPlatform = getCurrentPlatformValue();
@@ -4428,6 +4461,9 @@ function renderAutomationScreen() {
function renderOwnedScreen() { function renderOwnedScreen() {
if (!appState.dashboard) { if (!appState.dashboard) {
if (isAutoConnectionPending()) {
return renderAutoConnectingScreen("我的账号", "工作区就绪后,这里会自动显示当前账号和建议动作。");
}
return screenShell("我的账号", "先自动连接工作区。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("我的账号未加载", "自动连接成功后,这里会展示当前账号和建议动作。")); return screenShell("我的账号", "先自动连接工作区。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("我的账号未加载", "自动连接成功后,这里会展示当前账号和建议动作。"));
} }
const me = appState.me || appState.session?.account || {}; const me = appState.me || appState.session?.account || {};
@@ -4507,6 +4543,9 @@ function renderOwnedScreen() {
function renderPlaybookScreen() { function renderPlaybookScreen() {
if (!appState.dashboard) { if (!appState.dashboard) {
if (isAutoConnectionPending()) {
return renderAutoConnectingScreen("Agent", "工作区就绪后,这里会自动加载真实 Agent 列表和模型。");
}
return screenShell("Agent", "先自动连接工作区。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("Agent 未加载", "自动连接成功后,这里会展示真实 Agent 列表和模型。")); return screenShell("Agent", "先自动连接工作区。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("Agent 未加载", "自动连接成功后,这里会展示真实 Agent 列表和模型。"));
} }
const assistants = safeArray(appState.dashboard.assistants); const assistants = safeArray(appState.dashboard.assistants);
@@ -4681,6 +4720,9 @@ function renderPlaybookScreen() {
function renderProductionScreen() { function renderProductionScreen() {
if (!appState.dashboard) { if (!appState.dashboard) {
if (isAutoConnectionPending()) {
return renderAutoConnectingScreen("生产中心", "工作区就绪后,这里会自动加载真实任务和作品。");
}
return screenShell("生产中心", "先自动连接工作区。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("生产中心未加载", "自动连接成功后,这里会展示真实任务和作品。")); return screenShell("生产中心", "先自动连接工作区。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("生产中心未加载", "自动连接成功后,这里会展示真实任务和作品。"));
} }
const jobs = safeArray(appState.dashboard.recent_jobs); const jobs = safeArray(appState.dashboard.recent_jobs);
@@ -4822,6 +4864,9 @@ function renderProductionScreen() {
function renderReviewScreen() { function renderReviewScreen() {
if (!appState.dashboard) { if (!appState.dashboard) {
if (isAutoConnectionPending()) {
return renderAutoConnectingScreen("发布与复盘", "工作区就绪后,这里会自动生成复盘入口和最近完成任务。");
}
return screenShell("发布与复盘", "先自动连接工作区。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("复盘未加载", "自动连接成功后,这里会先用最近任务生成一版复盘入口。")); return screenShell("发布与复盘", "先自动连接工作区。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("复盘未加载", "自动连接成功后,这里会先用最近任务生成一版复盘入口。"));
} }
if (!backendSupports("/v2/reviews")) { if (!backendSupports("/v2/reviews")) {
@@ -4889,6 +4934,9 @@ function renderReviewScreen() {
function renderCreditsScreen() { function renderCreditsScreen() {
if (!appState.dashboard) { if (!appState.dashboard) {
if (isAutoConnectionPending()) {
return renderAutoConnectingScreen("额度", "工作区就绪后,这里会自动展示真实额度和运营看板。");
}
return screenShell("额度", "先自动连接工作区。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("额度未加载", "自动连接成功后,这里会展示真实额度和运营看板。")); return screenShell("额度", "先自动连接工作区。", `${button("自动连接", "open-auth", "primary")}`, renderEmptyState("额度未加载", "自动连接成功后,这里会展示真实额度和运营看板。"));
} }
const jobs = safeArray(appState.dashboard.recent_jobs); const jobs = safeArray(appState.dashboard.recent_jobs);

View File

@@ -55,3 +55,23 @@ test("projects screen uses an adaptive project grid instead of a fixed three-col
assert.match(CSS, /\.project-status-grid\s*\{[\s\S]*repeat\(auto-fit,\s*minmax\(260px,\s*1fr\)\)/); assert.match(CSS, /\.project-status-grid\s*\{[\s\S]*repeat\(auto-fit,\s*minmax\(260px,\s*1fr\)\)/);
assert.match(CSS, /\.entity-card\.pad\s*\{[\s\S]*padding:\s*15px/); assert.match(CSS, /\.entity-card\.pad\s*\{[\s\S]*padding:\s*15px/);
}); });
test("dashboard and project screens distinguish auto-connecting from truly disconnected", () => {
const dashboard = extractBetween(APP, "function renderDashboardScreen()", "function renderProjectsScreen()");
const projects = extractBetween(APP, "function renderProjectsScreen()", "function getActiveDetailTab(");
const helper = extractBetween(APP, "function isAutoConnectionPending()", "async function refreshFromAuthModal()");
assert.match(APP, /function isAutoConnectionPending\(\)/);
assert.match(APP, /function renderAutoConnectingScreen\(/);
assert.match(helper, /正在自动连接工作区/);
assert.match(dashboard, /isAutoConnectionPending\(\)/);
assert.match(dashboard, /renderAutoConnectingScreen\("项目总台"/);
assert.match(projects, /isAutoConnectionPending\(\)/);
assert.match(projects, /renderAutoConnectingScreen\("我的项目"/);
});
test("ensureAutoSession re-renders immediately when auto-connect starts", () => {
const source = extractBetween(APP, "async function ensureAutoSession(options = {})", "async function refreshFromAuthModal()");
assert.match(source, /appState\.autoConnectAttempted = true;/);
assert.match(source, /renderAll\(\);/);
});