feat(web): 页面标题移至右上角、拆卡片嵌套、筛选与表格双卡片
douyin-release-gate / verify (push) Failing after 3m49s

- 布局:PageContainer extra 渲染标题+副标题(内容区右上角),卡内重复标题/副标题移除
- 详情页(账号/编辑/草稿/Attempt/环境/出口)外层 Card 拆除,区块卡平铺在灰底上
- 任务中心、竞品分析改为筛选卡+表格卡双卡片;工作台外层 Card 拆除,操作记录表包 Card
- 浏览器实测 13 个页面:0 嵌套卡片、表格均在 Card 内、标题无重复;生产构建通过
This commit is contained in:
2026-09-23 08:42:31 +08:00
parent 5053f8972a
commit caf4b0fdcb
13 changed files with 83 additions and 78 deletions
+14 -3
View File
@@ -1,8 +1,8 @@
import { history, Outlet, useLocation } from '@umijs/max';
import { PageContainer, ProLayout } from '@ant-design/pro-components';
import { Dropdown } from 'antd';
import { Dropdown, Typography } from 'antd';
import { LogoutOutlined } from '@ant-design/icons';
import { menu } from '@/utils/metadata';
import { menu, pageMetadata } from '@/utils/metadata';
// 菜单结构对齐 web.archived/src/app/Layout.jsx(运营 / 资源 两组);侧栏仅保留模块链接菜单,标题由 TopNav 呈现。
// Umi 4(react-router 6)全局布局:location 用 useLocation(),子路由用 <Outlet/> 渲染
@@ -48,7 +48,18 @@ export default function Layout() {
),
}}
>
<PageContainer title={false} subTitle={false} ghost>
{/* 页面标题在卡片外面(内容区右上角);卡片内不再重复渲染标题。 */}
<PageContainer
ghost
title={false}
subTitle={false}
extra={
<div style={{ textAlign: 'right' }}>
<Typography.Title level={4} style={{ marginBottom: 2 }}>{pageMetadata(location.pathname).title}</Typography.Title>
<Typography.Text type="secondary" style={{ fontSize: 12 }}>{pageMetadata(location.pathname).subtitle}</Typography.Text>
</div>
}
>
<Outlet />
</PageContainer>
</ProLayout>
+2 -2
View File
@@ -477,7 +477,7 @@ export default function Page() {
);
return (
<Card variant="outlined">
<div>
{contextHolder}
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
@@ -497,6 +497,6 @@ export default function Page() {
{ key: 'strategy', label: '自动响应策略', children: strategyTab },
]}
/>
</Card>
</div>
);
}
+2 -2
View File
@@ -223,7 +223,7 @@ export default function Page() {
];
return (
<Card variant="outlined">
<div>
{contextHolder}
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12 }}>
<div style={{ minWidth: 0 }}>
@@ -255,6 +255,6 @@ export default function Page() {
>
<p>{`暂停账号 ${account.name}?待领取任务将进入 hold,恢复账号不会自动恢复既有 hold。`}</p>
</Modal>
</Card>
</div>
);
}
+1 -1
View File
@@ -43,7 +43,7 @@ export default function Page() {
}
return (
<Card title="创建社媒账号">
<Card variant="outlined">
{error ? (
<Alert type="error" showIcon style={{ marginBottom: 16 }} message={conflictMessage(error, '该平台的账号 ID 已存在;表单内容已保留。')} />
) : null}
+2 -5
View File
@@ -35,11 +35,8 @@ export default function Page() {
if (!attempt) return null;
return (
<Card variant="outlined">
<div>
<div style={{ marginBottom: 16 }}>
<Typography.Title level={4} style={{ marginBottom: 0 }}>
Attempt 详情
</Typography.Title>
<Typography.Text type="secondary" copyable>
{attempt.id}
</Typography.Text>
@@ -66,6 +63,6 @@ export default function Page() {
</Space>
</Space>
</Card>
</Card>
</div>
);
}
+2 -2
View File
@@ -47,7 +47,7 @@ export default function Page() {
if (error || !runtime) return <Alert type="error" showIcon message={error?.message || '运行环境不存在'} />;
return (
<Card variant="outlined">
<div>
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography.Title level={4} style={{ marginBottom: 0 }}>
{`${runtime.name}(${runtime.alias})`}
@@ -106,6 +106,6 @@ export default function Page() {
</Space>
</Space>
</Card>
</Card>
</div>
);
}
+4 -5
View File
@@ -102,13 +102,12 @@ export default function Page() {
}
return (
<Card variant="outlined">
<div>
{contextHolder}
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography.Text type="secondary">为已授权且暂停的账号绑定网关、指纹与网络出口</Typography.Text>
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'flex-end' }}>
<Button onClick={() => history.push('/browsers')}>返回运行环境</Button>
</div>
<Card>
<Card variant="outlined">
<Form form={form} layout="vertical" onFinish={submit}>
<Space size={16} wrap style={{ display: 'flex' }}>
<Form.Item name="name" label="环境名称" required rules={[{ required: true, message: '请输入环境名称' }]} extra="展示名称,支持中文,如:店铺一号">
@@ -201,6 +200,6 @@ export default function Page() {
</Space>
</Form>
</Card>
</Card>
</div>
);
}
+23 -26
View File
@@ -262,14 +262,11 @@ export default function Page() {
!['not_started', 'running', 'failed'].includes(material.transcription_status);
return (
<Card variant="outlined">
<div>
{contextHolder}
<Card title="作品与指标" extra={<Typography.Text type="secondary" style={{ fontSize: 12 }}>共 {workPageInfo.total} 条</Typography.Text>}>
<Typography.Paragraph type="secondary" style={{ marginTop: 0 }}>时间窗口、指标阈值和“不可用”字段由后端原样保留。</Typography.Paragraph>
<div style={{ marginBottom: 16, background: '#fafafa', padding: 16, borderRadius: 8 }}>
<Typography.Text strong>筛选作品</Typography.Text>
<Typography.Text type="secondary" style={{ marginLeft: 8, fontSize: 12 }}>调整条件后自动刷新</Typography.Text>
<Space size={12} wrap style={{ marginTop: 12, display: 'flex' }}>
<Space direction="vertical" size={16} style={{ width: '100%' }}>
<Card title="筛选作品" extra={<Typography.Text type="secondary" style={{ fontSize: 12 }}>调整条件后自动刷新</Typography.Text>}>
<Space size={12} wrap style={{ display: 'flex' }}>
<Select
style={{ width: 140 }}
placeholder="全部平台"
@@ -292,30 +289,30 @@ export default function Page() {
<Input type="date" style={{ width: 160 }} value={filters.publishedAfter} onChange={(e) => setFilters((f) => ({ ...f, publishedAfter: e.target.value }))} />
<Input type="date" style={{ width: 160 }} value={filters.publishedBefore} onChange={(e) => setFilters((f) => ({ ...f, publishedBefore: e.target.value }))} />
</Space>
</div>
</Card>
<Card title="作品与指标">
{error ? (
<Alert type="error" showIcon style={{ marginBottom: 16 }} message={error.message} action={<Button size="small" onClick={load}>重试</Button>} />
) : null}
<Table rowKey="id" columns={columns} dataSource={works} loading={pending} pagination={false} locale={{ emptyText: '暂无符合条件的作品。' }} />
<div style={{ marginTop: 12, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
当前页 {works.length} 条 · 共 {workPageInfo.total} 条
</Typography.Text>
<Space>
<Button size="small" onClick={() => setWorkPage((page) => Math.max(1, page - 1))} disabled={workPage === 1 || pending}>
上一页
</Button>
<Button size="small" onClick={() => setWorkPage((page) => page + 1)} disabled={!workPageInfo.hasNext || pending}>
下一页
</Button>
</Space>
</div>
{workDetail ? (
{workPageInfo.total > works.length ? (
<div style={{ marginTop: 12, display: 'flex', justifyContent: 'flex-end' }}>
<Space>
<Button size="small" onClick={() => setWorkPage((page) => Math.max(1, page - 1))} disabled={workPage === 1 || pending}>
上一页
</Button>
<Button size="small" onClick={() => setWorkPage((page) => page + 1)} disabled={!workPageInfo.hasNext || pending}>
下一页
</Button>
</Space>
</div>
) : null}
</Card>
{workDetail ? (
<Card
size="small"
title={<span>作品详情 <Typography.Text type="secondary" style={{ fontSize: 12, fontWeight: 'normal' }}>{workDetail.work_key || workDetail.id}</Typography.Text></span>}
extra={<Button size="small" onClick={() => setWorkDetail(null)}>关闭</Button>}
style={{ marginTop: 16 }}
>
<Space direction="vertical" style={{ width: '100%' }} size={16}>
{workDetail.cover_url ? <img src={workDetail.cover_url} alt="作品封面" style={{ maxHeight: 256, borderRadius: 4, border: '1px solid #f0f0f0' }} /> : null}
@@ -346,7 +343,7 @@ export default function Page() {
</Card>
) : null}
{material ? (
<Card size="small" title="素材处理" style={{ marginTop: 16 }}>
<Card size="small" title="素材处理">
<Typography.Paragraph type="secondary" style={{ marginTop: 0 }}>每一步都保留真实状态;失败不会被显示为成功。</Typography.Paragraph>
<Button type="primary" onClick={processMaterial} loading={materialPending || material.download_status === 'running'} style={{ marginBottom: 16 }}>
开始处理素材
@@ -397,7 +394,7 @@ export default function Page() {
</Space>
</Card>
) : null}
</Card>
</Space>
<Modal
open={Boolean(pendingSwitchWork)}
title="放弃未保存的素材文稿"
@@ -408,6 +405,6 @@ export default function Page() {
>
当前素材文稿尚未保存,确定放弃并切换吗?
</Modal>
</Card>
</div>
);
}
+4 -4
View File
@@ -74,10 +74,10 @@ export default function Page() {
if (error) return <Alert type="error" showIcon message={error.message} action={<Button size="small" onClick={load}>重试</Button>} />;
return (
<Card variant="outlined">
<div>
{contextHolder}
<Typography.Paragraph type="secondary">按配置类别管理 UTC 采集窗口、指标采集和已批准的服务。</Typography.Paragraph>
<Card>
<Typography.Paragraph type="secondary" style={{ marginBottom: 16 }}>按配置类别管理 UTC 采集窗口、指标采集和已批准的服务。</Typography.Paragraph>
<Card variant="outlined">
<Form form={form} layout="vertical" onFinish={save} initialValues={{}}>
<Tabs
items={[
@@ -158,6 +158,6 @@ export default function Page() {
</div>
</Form>
</Card>
</Card>
</div>
);
}
+12 -10
View File
@@ -990,18 +990,20 @@ export default function Page() {
];
const operationsTab = (
<Table
rowKey="id"
loading={pending}
pagination={false}
dataSource={error ? [] : visibleData}
columns={operationsColumns}
locale={{ emptyText: '暂无操作记录。' }}
/>
<Card variant="outlined">
<Table
rowKey="id"
loading={pending}
pagination={false}
dataSource={error ? [] : visibleData}
columns={operationsColumns}
locale={{ emptyText: '暂无操作记录。' }}
/>
</Card>
);
return (
<Card variant="outlined">
<div>
{contextHolder}
<Tabs
activeKey={tab}
@@ -1062,6 +1064,6 @@ export default function Page() {
>
当前私信文案尚未发送,放弃后再切换吗?
</Modal>
</Card>
</div>
);
}
+5 -8
View File
@@ -110,15 +110,12 @@ export default function Page() {
}
return (
<Card variant="outlined">
<div>
{contextHolder}
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ minWidth: 0 }}>
<Typography.Title level={4} style={{ marginBottom: 0 }}>草稿核对</Typography.Title>
<Typography.Text type="secondary" ellipsis>
{draft.account.platform_account_key} · 草稿版本 {draft.version}
</Typography.Text>
</div>
<Typography.Text type="secondary" ellipsis>
{draft.account.platform_account_key} · 草稿版本 {draft.version}
</Typography.Text>
<Button onClick={() => history.push(`/accounts/${draft.account_id}`)}>返回账号详情</Button>
</div>
{browsersError ? (
@@ -222,6 +219,6 @@ export default function Page() {
{binding?.network_exit_id || '当前机器直连'} 的只读确认快照。版本变化后必须重新确认。
</p>
</Modal>
</Card>
</div>
);
}
+2 -3
View File
@@ -42,10 +42,9 @@ export default function Page() {
const known = Object.hasOwn(healthText, exit.health_status ?? '');
return (
<Card variant="outlined">
<div>
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div style={{ minWidth: 0 }}>
<Typography.Title level={4} style={{ marginBottom: 0 }}>网络出口详情</Typography.Title>
<Typography.Text type="secondary" ellipsis>
{`${exit.protocol}://${exit.host}:${exit.port}`}
</Typography.Text>
@@ -71,6 +70,6 @@ export default function Page() {
</Descriptions>
</Space>
</Card>
</Card>
</div>
);
}
+10 -7
View File
@@ -110,9 +110,9 @@ export default function Page() {
];
return (
<Card variant="outlined">
<Typography.Paragraph type="secondary">逐条处理暂停、版本变化与未知执行结果;不会自动重试或更换出口</Typography.Paragraph>
<Space wrap style={{ marginBottom: 16 }}>
<div>
<Card variant="outlined" style={{ marginBottom: 16 }}>
<Space wrap>
<Input
style={{ width: 280 }}
placeholder="账号 ID"
@@ -128,8 +128,10 @@ export default function Page() {
onChange={setStateFilter}
options={Object.entries(stateText).map(([value, label]) => ({ value, label }))}
/>
</Space>
{error ? <Alert type="error" showIcon style={{ marginBottom: 16 }} message={error.message} action={<Button size="small" onClick={load}>重试</Button>} /> : null}
</Space>
</Card>
<Card variant="outlined">
{error ? <Alert type="error" showIcon style={{ marginBottom: 16 }} message={error.message} action={<Button size="small" onClick={load}>重试</Button>} /> : null}
<Table
rowKey="id"
columns={columns}
@@ -137,7 +139,8 @@ export default function Page() {
loading={pending}
pagination={false}
locale={{ emptyText: accountFilter || stateFilter ? '没有符合筛选条件的任务' : '尚无任务' }}
/>
</Card>
/>
</Card>
</div>
);
}