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() {
|
function openDashboardProjectSwitcher() {
|
||||||
const options = getProjectOptions();
|
const options = getProjectOptions();
|
||||||
if (!options.length) {
|
if (!options.length) {
|
||||||
@@ -3225,36 +3245,29 @@ function openDashboardProjectSwitcher() {
|
|||||||
},
|
},
|
||||||
{ name: "projectId", label: "当前项目", type: "select", value: getSelectedProject()?.id || "", options }
|
{ name: "projectId", label: "当前项目", type: "select", value: getSelectedProject()?.id || "", options }
|
||||||
],
|
],
|
||||||
onOpen: ({ fields }) => {
|
onOpen: ({ fields, submit }) => {
|
||||||
const select = fields.querySelector('[data-action-field="projectId"]');
|
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) => {
|
fields.querySelectorAll("[data-project-choice]").forEach((button) => {
|
||||||
button.addEventListener("click", () => {
|
button.addEventListener("click", async () => {
|
||||||
|
const nextProjectId = button.dataset.projectChoice || "";
|
||||||
if (select) {
|
if (select) {
|
||||||
select.value = button.dataset.projectChoice || "";
|
select.value = nextProjectId;
|
||||||
}
|
}
|
||||||
fields.querySelectorAll("[data-project-choice]").forEach((item) => item.classList.remove("active"));
|
fields.querySelectorAll("[data-project-choice]").forEach((item) => item.classList.remove("active"));
|
||||||
button.classList.add("active");
|
button.classList.add("active");
|
||||||
|
if (isMobileViewport && nextProjectId) {
|
||||||
|
closeActionModal();
|
||||||
|
await applySelectedProject(nextProjectId);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSubmit: async (payload) => {
|
onSubmit: async (payload) => {
|
||||||
appState.selectedProjectId = payload.projectId || "";
|
await applySelectedProject(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();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -7050,7 +7063,7 @@ function renderSettingsScreen() {
|
|||||||
return screenShell(
|
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">
|
<div class="hero-card mobile-secondary-card">
|
||||||
<h3>设置与帮助</h3>
|
<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", () => {
|
test("mobile project sheets support direct project picking and zoom-safe form controls", () => {
|
||||||
const projectSwitcher = extractBetween(APP, "function openDashboardProjectSwitcher()", "function openDashboardActionReasonAction(");
|
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()");
|
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, /data-project-choice=/);
|
||||||
assert.match(projectSwitcher, /onOpen:\s*\(/);
|
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(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]*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/);
|
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