feat: streamline mobile project switching
This commit is contained in:
@@ -3164,6 +3164,26 @@ function buildDashboardHomeModel() {
|
||||
};
|
||||
}
|
||||
|
||||
async function applySelectedProject(projectId = "") {
|
||||
appState.selectedProjectId = projectId || "";
|
||||
setBusy(true, "正在切换项目视图...");
|
||||
try {
|
||||
if (backendSupports("/v2/storage/status")) {
|
||||
await loadStorageStatus(appState.selectedProjectId || "");
|
||||
} else {
|
||||
appState.storageStatus = null;
|
||||
}
|
||||
await loadAgentControlSurfaces(appState.selectedProjectId || "");
|
||||
if (appState.selectedOnelinerSessionId) {
|
||||
await loadOneLinerMessages(appState.selectedOnelinerSessionId);
|
||||
}
|
||||
} finally {
|
||||
setBusy(false, "");
|
||||
}
|
||||
rememberAction("当前项目已切换", `已切换到「${getSelectedProject()?.name || "所选项目"}」,你现在看到的首页、Agent 和任务都会跟随更新。`, "green");
|
||||
renderAll();
|
||||
}
|
||||
|
||||
function openDashboardProjectSwitcher() {
|
||||
const options = getProjectOptions();
|
||||
if (!options.length) {
|
||||
@@ -3225,36 +3245,29 @@ function openDashboardProjectSwitcher() {
|
||||
},
|
||||
{ name: "projectId", label: "当前项目", type: "select", value: getSelectedProject()?.id || "", options }
|
||||
],
|
||||
onOpen: ({ fields }) => {
|
||||
onOpen: ({ fields, submit }) => {
|
||||
const select = fields.querySelector('[data-action-field="projectId"]');
|
||||
const isMobileViewport = typeof window !== "undefined" && window.matchMedia?.("(max-width: 760px)")?.matches;
|
||||
if (submit && isMobileViewport) {
|
||||
submit.hidden = true;
|
||||
}
|
||||
fields.querySelectorAll("[data-project-choice]").forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
button.addEventListener("click", async () => {
|
||||
const nextProjectId = button.dataset.projectChoice || "";
|
||||
if (select) {
|
||||
select.value = button.dataset.projectChoice || "";
|
||||
select.value = nextProjectId;
|
||||
}
|
||||
fields.querySelectorAll("[data-project-choice]").forEach((item) => item.classList.remove("active"));
|
||||
button.classList.add("active");
|
||||
if (isMobileViewport && nextProjectId) {
|
||||
closeActionModal();
|
||||
await applySelectedProject(nextProjectId);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
onSubmit: async (payload) => {
|
||||
appState.selectedProjectId = payload.projectId || "";
|
||||
setBusy(true, "正在切换项目视图...");
|
||||
try {
|
||||
if (backendSupports("/v2/storage/status")) {
|
||||
await loadStorageStatus(appState.selectedProjectId || "");
|
||||
} else {
|
||||
appState.storageStatus = null;
|
||||
}
|
||||
await loadAgentControlSurfaces(appState.selectedProjectId || "");
|
||||
if (appState.selectedOnelinerSessionId) {
|
||||
await loadOneLinerMessages(appState.selectedOnelinerSessionId);
|
||||
}
|
||||
} finally {
|
||||
setBusy(false, "");
|
||||
}
|
||||
rememberAction("当前项目已切换", `已切换到「${getSelectedProject()?.name || "所选项目"}」,你现在看到的首页、Agent 和任务都会跟随更新。`, "green");
|
||||
renderAll();
|
||||
await applySelectedProject(payload.projectId || "");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -7050,7 +7063,7 @@ function renderSettingsScreen() {
|
||||
return screenShell(
|
||||
"设置",
|
||||
"这里不放系统治理内容,只处理当前用户需要理解的连接、界面和帮助信息。",
|
||||
`${button("连接状态", "open-auth")} ${isSuperAdmin() ? button("管理员配置台", "goto-admin-workbench", "primary") : button("刷新", "refresh-data", "primary")}`,
|
||||
`${isSuperAdmin() ? button("管理员配置台", "goto-admin-workbench", "primary") : button("刷新", "refresh-data", "primary")}`,
|
||||
`
|
||||
<div class="hero-card mobile-secondary-card">
|
||||
<h3>设置与帮助</h3>
|
||||
|
||||
@@ -116,10 +116,18 @@ test("project creation and switching use in-app sheets instead of browser prompt
|
||||
|
||||
test("mobile project sheets support direct project picking and zoom-safe form controls", () => {
|
||||
const projectSwitcher = extractBetween(APP, "function openDashboardProjectSwitcher()", "function openDashboardActionReasonAction(");
|
||||
const applySelectedProject = extractBetween(APP, "async function applySelectedProject(projectId = \"\")", "function openDashboardProjectSwitcher()");
|
||||
const createProject = extractBetween(APP, "async function createProject()", "function openPreferredModelAction()");
|
||||
assert.match(APP, /async function applySelectedProject\(projectId = ""\)/);
|
||||
assert.match(projectSwitcher, /data-project-choice=/);
|
||||
assert.match(projectSwitcher, /onOpen:\s*\(/);
|
||||
assert.match(projectSwitcher, /select\.value = button\.dataset\.projectChoice/);
|
||||
assert.match(projectSwitcher, /select\.value = nextProjectId/);
|
||||
assert.match(projectSwitcher, /window\.matchMedia\?\.\("\(max-width: 760px\)"\)\?\.matches/);
|
||||
assert.match(projectSwitcher, /submit\.hidden = true/);
|
||||
assert.match(projectSwitcher, /closeActionModal\(\);/);
|
||||
assert.match(projectSwitcher, /await applySelectedProject\(nextProjectId\);/);
|
||||
assert.match(applySelectedProject, /loadStorageStatus\(appState\.selectedProjectId \|\| ""\)/);
|
||||
assert.match(applySelectedProject, /loadAgentControlSurfaces\(appState\.selectedProjectId \|\| ""\)/);
|
||||
assert.match(createProject, /onOpen:\s*\(/);
|
||||
assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.field-stack input,\s*[\s\S]*\.field-stack textarea,\s*[\s\S]*\.field-stack select\s*\{[\s\S]*min-height:\s*46px/);
|
||||
assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.field-stack input,\s*[\s\S]*\.field-stack textarea,\s*[\s\S]*\.field-stack select\s*\{[\s\S]*font-size:\s*16px/);
|
||||
|
||||
Reference in New Issue
Block a user