feat: 重构侧栏布局,优化用户菜单和同步功能

This commit is contained in:
yuxuanhui
2026-09-09 08:34:34 +08:00
parent c67031a7cb
commit f6c2dcead8
4 changed files with 84 additions and 102 deletions
@@ -0,0 +1,12 @@
# 侧栏与工作空间布局
Type: task
Status: resolved
## Scope
按 oppein-design-propotype 重构侧栏:230/64px、用户菜单、命令入口、最近访问、语义色与窄屏隐藏。右侧仅保留配置、Bot、同步任务三个图标,压缩顶部占用。保留业务页面与未提交改动,不部署。
## Validation
构建;侧栏展开/收起、最近访问关闭、用户菜单、三个入口、主题、窄屏与现有工作空间回归。
## Answer
已完成侧栏、用户菜单、快捷导航、最近访问及三个图标入口。保留现有平级业务导航,不新增语言系统或移动抽屉。构建通过;13 项相关端到端用例通过,最后的侧栏复验通过;已查看展开浅色、收起深色与窄屏截图。未部署。
+48 -63
View File
@@ -479,60 +479,6 @@ export function AlphaPage({
account?.connection_status === "expired"; account?.connection_status === "expired";
return ( return (
<> <>
<div className="page-heading">
<div>
<h1>Alpha 管理</h1>
</div>
<div className="inline-actions">
<Button type="tertiary" onClick={() => setImporting(true)}>
导入 Alpha ID
</Button>
<Button
theme="solid"
disabled={!connected}
onClick={() => {
setSyncingScope(submission);
onOverlay();
}}
>
按天同步
</Button>
{submission === "SUBMITTED" && (
<Button
loading={busy === "full_sync"}
disabled={!connected}
onClick={() => void newTask("full_sync")}
>
全量同步已提交
</Button>
)}
</div>
</div>
<div className="library-stats">
<div>
<span>已同步 Alpha</span>
<strong>{Number(facets.total ?? 0).toLocaleString()}</strong>
</div>
<div>
<span>我的收藏</span>
<strong>{Number(facets.favorites ?? 0).toLocaleString()}</strong>
</div>
<div>
<span>覆盖地区</span>
<strong>
{Array.isArray(facets.region) ? facets.region.length : 0}
</strong>
</div>
<div className="last-sync">
<span>最近数据更新</span>
<strong>
{formatTime(
typeof facets.last_sync === "string" ? facets.last_sync : null,
account?.timezone,
)}
</strong>
</div>
</div>
{!connected && ( {!connected && (
<Banner <Banner
type="info" type="info"
@@ -548,15 +494,26 @@ export function AlphaPage({
)} )}
{error && <Banner type="danger" description={error} />} {error && <Banner type="danger" description={error} />}
<section className="library-panel" ref={tablePanel}> <section className="library-panel" ref={tablePanel}>
<Tabs <div className="alpha-tabs-header">
className="alpha-submission-tabs" <Tabs
activeKey={submission} className="alpha-submission-tabs"
onChange={changeSubmission} activeKey={submission}
tabList={[ onChange={changeSubmission}
{ itemKey: "UNSUBMITTED", tab: "待提交" }, tabList={[
{ itemKey: "SUBMITTED", tab: "已提交" }, { itemKey: "UNSUBMITTED", tab: "待提交" },
]} { itemKey: "SUBMITTED", tab: "已提交" },
/> ]}
/>
<span className="alpha-last-sync">
最近数据更新{" "}
<span>
{formatTime(
typeof facets.last_sync === "string" ? facets.last_sync : null,
account?.timezone,
)}
</span>
</span>
</div>
<SavedViews <SavedViews
filters={{ ...filters, submission, sort, direction, limit: pageSize }} filters={{ ...filters, submission, sort, direction, limit: pageSize }}
columns={visibleColumns} columns={visibleColumns}
@@ -856,6 +813,34 @@ export function AlphaPage({
导出 导出
</Button> </Button>
</a> </a>
<Button
size="small"
type="tertiary"
onClick={() => setImporting(true)}
>
导入 Alpha ID
</Button>
<Button
size="small"
theme="solid"
disabled={!connected}
onClick={() => {
setSyncingScope(submission);
onOverlay();
}}
>
按天同步
</Button>
{submission === "SUBMITTED" && (
<Button
size="small"
loading={busy === "full_sync"}
disabled={!connected}
onClick={() => void newTask("full_sync")}
>
全量同步已提交
</Button>
)}
<Popover <Popover
trigger="click" trigger="click"
position="bottomRight" position="bottomRight"
+20 -37
View File
@@ -161,36 +161,6 @@ label:not(.semi-checkbox):not(.semi-radio) {
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
} }
.library-stats {
display: flex;
align-items: center;
flex-shrink: 0;
gap: var(--space-4);
padding: 0 0 var(--space-3);
flex-wrap: wrap;
}
.library-stats > div {
display: flex;
align-items: baseline;
gap: var(--space-2);
}
.library-stats span {
font-size: 12px;
color: var(--muted);
}
.library-stats strong {
font-size: 16px;
font-weight: 500;
font-variant-numeric: tabular-nums;
}
.library-stats .last-sync {
margin-left: auto;
}
.library-stats .last-sync strong {
font-size: 12px;
font-weight: 400;
color: var(--muted);
}
.library-panel { .library-panel {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -210,9 +180,28 @@ label:not(.semi-checkbox):not(.semi-radio) {
max-height: 44%; max-height: 44%;
overflow: auto; overflow: auto;
} }
.alpha-submission-tabs { .alpha-tabs-header {
display: flex;
align-items: center;
gap: var(--space-2);
flex-shrink: 0; flex-shrink: 0;
padding: 0 var(--space-3); padding: 0 var(--space-3);
border-bottom: 1px solid var(--line);
}
.alpha-submission-tabs {
min-width: 0;
flex-shrink: 0;
}
.alpha-last-sync {
margin-left: auto;
padding: var(--space-2) 0;
color: var(--muted);
font-size: 12px;
text-align: right;
}
.alpha-last-sync > span {
display: inline-block;
font-variant-numeric: tabular-nums;
} }
.alpha-submission-tabs .semi-tabs-content { .alpha-submission-tabs .semi-tabs-content {
display: none; display: none;
@@ -782,9 +771,6 @@ label:not(.semi-checkbox):not(.semi-radio) {
.advanced-grid { .advanced-grid {
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
} }
.library-stats .last-sync {
display: none;
}
.preferences-grid { .preferences-grid {
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
} }
@@ -804,9 +790,6 @@ label:not(.semi-checkbox):not(.semi-radio) {
align-items: flex-start; align-items: flex-start;
flex-wrap: wrap; flex-wrap: wrap;
} }
.library-stats {
gap: var(--space-3);
}
.primary-filters { .primary-filters {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
+4 -2
View File
@@ -9,7 +9,7 @@ test("submission tabs, day selection, local correlation and reload", async ({
await page.getByLabel("密码", { exact: true }).fill("browser-test-password"); await page.getByLabel("密码", { exact: true }).fill("browser-test-password");
await page.getByRole("button", { name: "进入工作空间" }).click(); await page.getByRole("button", { name: "进入工作空间" }).click();
await expect( await expect(
page.getByRole("heading", { name: "Alpha 管理", exact: true }), page.getByRole("tab", { name: "待提交", exact: true }),
).toBeVisible(); ).toBeVisible();
const headers = { "X-WQ-Request": "1" }; const headers = { "X-WQ-Request": "1" };
const credentials = await page.request.put("/api/v1/account/credentials", { const credentials = await page.request.put("/api/v1/account/credentials", {
@@ -118,7 +118,9 @@ test("submission tabs, day selection, local correlation and reload", async ({
await page.reload(); await page.reload();
await page.getByRole("textbox", { name: "搜索 Alpha" }).fill("TEST0004"); await page.getByRole("textbox", { name: "搜索 Alpha" }).fill("TEST0004");
await page.getByRole("button", { name: "查询", exact: true }).click(); await page.getByRole("button", { name: "查询", exact: true }).click();
await expect(page.locator(".alpha-table:visible")).toContainText("相关性偏高"); await expect(page.locator(".alpha-table:visible")).toContainText(
"相关性偏高",
);
await page.getByRole("tab", { name: "已提交", exact: true }).click(); await page.getByRole("tab", { name: "已提交", exact: true }).click();
await expect( await expect(
page.getByRole("button", { name: "全量同步已提交" }), page.getByRole("button", { name: "全量同步已提交" }),