style(web): 侧栏仅保留模块菜单并补图标;内容区统一 Card wrapper 与页面背景区分
douyin-release-gate / verify (push) Failing after 2m44s
douyin-release-gate / verify (push) Failing after 2m44s
- 侧栏 menuHeaderRender=false,折叠菜单只剩模块链接(标题由 TopNav 呈现) - 菜单补齐 10 个常用语义图标(账号导入/我的账号/监控账号/竞品分析/工作台/采集设置/任务中心/运行环境/网络出口/网关管理) - 列表与详情/新建/编辑页主内容统一 <Card variant=outlined> 包裹,对齐 pro 官方预览的灰底+白卡层次(17 处) - metadata.ts → metadata.tsx(菜单 icon 需 JSX);ScanEyeOutlined/ChatSettingsOutlined 在当前 @ant-design/icons 不存在,改用 EyeOutlined/CommentOutlined
This commit is contained in:
@@ -3,6 +3,7 @@ import { history } from '@umijs/max';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Form,
|
||||
Input,
|
||||
Modal,
|
||||
@@ -223,7 +224,7 @@ export default function AccountManagementList({ mode }: { mode: 'owned' | 'monit
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
{contextHolder}
|
||||
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Typography.Text type="secondary">
|
||||
@@ -266,6 +267,6 @@ export default function AccountManagementList({ mode }: { mode: 'owned' | 'monit
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ import { Dropdown } from 'antd';
|
||||
import { LogoutOutlined } from '@ant-design/icons';
|
||||
import { menu } from '@/utils/metadata';
|
||||
|
||||
// 菜单结构与 web.archived/src/app/Layout.jsx 完全一致(运营 / 资源 两组),页头标题规则对齐 pageMetadataRules。
|
||||
// /login 独立页:不走 ProLayout(对齐归档版布局分支)。
|
||||
// 菜单结构对齐 web.archived/src/app/Layout.jsx(运营 / 资源 两组);侧栏仅保留模块链接菜单,标题由 TopNav 呈现。
|
||||
// Umi 4(react-router 6)全局布局:location 用 useLocation(),子路由用 <Outlet/> 渲染
|
||||
// (渲染器不传 children prop,用 children 会导致整页空白且无任何报错)。
|
||||
export default function Layout() {
|
||||
@@ -20,21 +19,11 @@ export default function Layout() {
|
||||
fixSiderbar
|
||||
route={{ routes: menu } as any}
|
||||
location={location}
|
||||
// 不渲染侧栏品牌区(logo/标题),折叠菜单仅保留模块链接。
|
||||
menuHeaderRender={false}
|
||||
menuItemRender={(item, dom) => (
|
||||
<a onClick={() => item.path && history.push(item.path)}>{dom}</a>
|
||||
)}
|
||||
menuHeaderRender={(logo, title) => (
|
||||
<a
|
||||
style={{ display: 'flex', alignItems: 'center', gap: 8 }}
|
||||
onClick={() => history.push('/accounts')}
|
||||
>
|
||||
{logo}
|
||||
<span>
|
||||
<div style={{ fontWeight: 600, fontSize: 14 }}>{title}</div>
|
||||
<div style={{ fontSize: 12, opacity: 0.6 }}>运营控制台</div>
|
||||
</span>
|
||||
</a>
|
||||
)}
|
||||
avatarProps={{
|
||||
icon: <LogoutOutlined />,
|
||||
size: 'small',
|
||||
|
||||
@@ -477,7 +477,7 @@ export default function Page() {
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
{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 },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ export default function Page() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
{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>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
Card,
|
||||
Form,
|
||||
Input,
|
||||
Modal,
|
||||
@@ -157,7 +158,7 @@ export default function Page() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
{contextHolder}
|
||||
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<Button
|
||||
@@ -206,6 +207,6 @@ export default function Page() {
|
||||
</Typography.Text>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function Page() {
|
||||
if (!attempt) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Typography.Title level={4} style={{ marginBottom: 0 }}>
|
||||
Attempt 详情
|
||||
@@ -66,6 +66,6 @@ export default function Page() {
|
||||
</Space>
|
||||
</Space>
|
||||
</Card>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function Page() {
|
||||
if (error || !runtime) return <Alert type="error" showIcon message={error?.message || '运行环境不存在'} />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
<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>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// 动作保持显式动词(start/stop/recycle),启停禁用条件与归档版一致。
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { history } from '@umijs/max';
|
||||
import { Alert, Button, Popconfirm, Space, Table, Tag, Typography, message } from 'antd';
|
||||
import { Alert, Button, Card, Popconfirm, Space, Table, Tag, Typography, message } from 'antd';
|
||||
import { CopyOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { browserAction, getList } from '@/services/api';
|
||||
@@ -160,7 +160,7 @@ export default function Page() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
{contextHolder}
|
||||
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Typography.Text type="secondary">启动、停止并回收隔离的指纹浏览器运行环境</Typography.Text>
|
||||
@@ -179,6 +179,6 @@ export default function Page() {
|
||||
pagination={false}
|
||||
locale={{ emptyText: '暂无运行环境,创建第一个隔离 Profile。' }}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ export default function Page() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
{contextHolder}
|
||||
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Typography.Text type="secondary">为已授权且暂停的账号绑定网关、指纹与网络出口</Typography.Text>
|
||||
@@ -201,6 +201,6 @@ export default function Page() {
|
||||
</Space>
|
||||
</Form>
|
||||
</Card>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ export default function Page() {
|
||||
!['not_started', 'running', 'failed'].includes(material.transcription_status);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
{contextHolder}
|
||||
<Card title="作品与指标" extra={<Typography.Text type="secondary" style={{ fontSize: 12 }}>共 {workPageInfo.total} 条</Typography.Text>}>
|
||||
<Typography.Paragraph type="secondary" style={{ marginTop: 0 }}>时间窗口、指标阈值和“不可用”字段由后端原样保留。</Typography.Paragraph>
|
||||
@@ -408,6 +408,6 @@ export default function Page() {
|
||||
>
|
||||
当前素材文稿尚未保存,确定放弃并切换吗?
|
||||
</Modal>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ export default function Page() {
|
||||
if (error) return <Alert type="error" showIcon message={error.message} action={<Button size="small" onClick={load}>重试</Button>} />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
{contextHolder}
|
||||
<Typography.Paragraph type="secondary">按配置类别管理 UTC 采集窗口、指标采集和已批准的服务。</Typography.Paragraph>
|
||||
<Card>
|
||||
@@ -158,6 +158,6 @@ export default function Page() {
|
||||
</div>
|
||||
</Form>
|
||||
</Card>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1001,7 +1001,7 @@ export default function Page() {
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
{contextHolder}
|
||||
<Tabs
|
||||
activeKey={tab}
|
||||
@@ -1062,6 +1062,6 @@ export default function Page() {
|
||||
>
|
||||
当前私信文案尚未发送,放弃后再切换吗?
|
||||
</Modal>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ export default function Page() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
{contextHolder}
|
||||
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
@@ -222,6 +222,6 @@ export default function Page() {
|
||||
{binding?.network_exit_id || '当前机器直连'} 的只读确认快照。版本变化后必须重新确认。
|
||||
</p>
|
||||
</Modal>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 网关管理:语义对齐 web.archived GatewaysPage.jsx(注册/编辑 Modal、令牌显隐复制、删除)。
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Alert, Button, Form, Input, Modal, Popconfirm, Space, Table, Typography, message } from 'antd';
|
||||
import { Alert, Button, Card, Form, Input, Modal, Popconfirm, Space, Table, Typography, message } from 'antd';
|
||||
import { CopyOutlined, EyeInvisibleOutlined, EyeOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { create, getList, remove, update } from '@/services/api';
|
||||
@@ -170,7 +170,7 @@ export default function Page() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
{contextHolder}
|
||||
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Typography.Text type="secondary">注册宿主机 native browser gateway;令牌须与 gateway 进程的 GATEWAY_TOKEN 一致</Typography.Text>
|
||||
@@ -223,7 +223,7 @@ export default function Page() {
|
||||
onCancel={() => setEditing(null)}
|
||||
onSubmit={updateGateway}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export default function Page() {
|
||||
|
||||
const known = Object.hasOwn(healthText, exit.health_status ?? '');
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<Typography.Title level={4} style={{ marginBottom: 0 }}>网络出口详情</Typography.Title>
|
||||
@@ -71,6 +71,6 @@ export default function Page() {
|
||||
</Descriptions>
|
||||
</Space>
|
||||
</Card>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// 网络出口:语义对齐 web.archived NetworkExitsPage.jsx(列表 + 创建/编辑 Modal + 检测/启停/删除动作 + 出口详情页)。
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { history } from '@umijs/max';
|
||||
import { Alert, Button, Form, Input, InputNumber, Modal, Popconfirm, Select, Space, Table, Tag, Typography, message } from 'antd';
|
||||
import { Alert, Button, Card, Form, Input, InputNumber, Modal, Popconfirm, Select, Space, Table, Tag, Typography, message } from 'antd';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { create, getOne, getList, networkExitAction, remove, update } from '@/services/api';
|
||||
@@ -278,7 +278,7 @@ export default function Page() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
{contextHolder}
|
||||
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Typography.Text type="secondary">维护固定出口、健康观测与账号绑定</Typography.Text>
|
||||
@@ -332,6 +332,6 @@ export default function Page() {
|
||||
onCancel={() => setEditing(null)}
|
||||
onSubmit={updateExit}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ export default function Page() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
{contextHolder}
|
||||
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
@@ -253,6 +253,6 @@ export default function Page() {
|
||||
<Typography.Text type="secondary">尚未产生执行尝试。</Typography.Text>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// 任务中心列表:语义对齐 web.archived TasksPage.jsx 的 TaskList。
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { history } from '@umijs/max';
|
||||
import { Alert, Button, Input, Select, Space, Table, Tag, Typography } from 'antd';
|
||||
import { Alert, Button, Card, Input, Select, Space, Table, Tag, Typography } from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { getList } from '@/services/api';
|
||||
import { dateTime } from '@/utils/helpers';
|
||||
@@ -110,7 +110,7 @@ export default function Page() {
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card variant="outlined">
|
||||
<Typography.Paragraph type="secondary">逐条处理暂停、版本变化与未知执行结果;不会自动重试或更换出口</Typography.Paragraph>
|
||||
<Space wrap style={{ marginBottom: 16 }}>
|
||||
<Input
|
||||
@@ -138,6 +138,6 @@ export default function Page() {
|
||||
pagination={false}
|
||||
locale={{ emptyText: accountFilter || stateFilter ? '没有符合筛选条件的任务' : '尚无任务' }}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
import type { MenuDataItem } from '@ant-design/pro-components';
|
||||
import {
|
||||
CloudDownloadOutlined,
|
||||
UserOutlined,
|
||||
EyeOutlined,
|
||||
LineChartOutlined,
|
||||
CommentOutlined,
|
||||
SettingOutlined,
|
||||
ProfileOutlined,
|
||||
LaptopOutlined,
|
||||
ClusterOutlined,
|
||||
GatewayOutlined,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
// 页头标题/副标题规则:语义对齐 web.archived/src/app/Layout.jsx 的 pageMetadataRules。
|
||||
const rules: Array<{ test: (path: string) => boolean; title: string; subtitle: string }> = [
|
||||
@@ -25,26 +37,27 @@ const rules: Array<{ test: (path: string) => boolean; title: string; subtitle: s
|
||||
{ test: (p) => p.startsWith('/gateways/'), title: '网关详情', subtitle: '查看网关连接信息和运行状态。' },
|
||||
];
|
||||
|
||||
// 菜单结构:与 web.archived/src/app/Layout.jsx 完全一致(运营 / 资源 两组)。
|
||||
// 菜单结构:与 web.archived/src/app/Layout.jsx 完全一致(运营 / 资源 两组);
|
||||
// icon 选用 antd 图标库里语义常见、用户一眼可识别的隐喻。
|
||||
export const menu: MenuDataItem[] = [
|
||||
{
|
||||
name: '运营',
|
||||
children: [
|
||||
{ path: '/accounts/import', name: '账号导入' },
|
||||
{ path: '/accounts', name: '我的账号' },
|
||||
{ path: '/accounts/monitoring', name: '监控账号' },
|
||||
{ path: '/creator/competitors', name: '竞品分析' },
|
||||
{ path: '/creator/workbench', name: '运营工作台' },
|
||||
{ path: '/creator/settings', name: '采集设置' },
|
||||
{ path: '/tasks', name: '任务中心' },
|
||||
{ path: '/accounts/import', name: '账号导入', icon: <CloudDownloadOutlined /> },
|
||||
{ path: '/accounts', name: '我的账号', icon: <UserOutlined /> },
|
||||
{ path: '/accounts/monitoring', name: '监控账号', icon: <EyeOutlined /> },
|
||||
{ path: '/creator/competitors', name: '竞品分析', icon: <LineChartOutlined /> },
|
||||
{ path: '/creator/workbench', name: '运营工作台', icon: <CommentOutlined /> },
|
||||
{ path: '/creator/settings', name: '采集设置', icon: <SettingOutlined /> },
|
||||
{ path: '/tasks', name: '任务中心', icon: <ProfileOutlined /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '资源',
|
||||
children: [
|
||||
{ path: '/browsers', name: '运行环境' },
|
||||
{ path: '/network-exits', name: '网络出口' },
|
||||
{ path: '/gateways', name: '网关管理' },
|
||||
{ path: '/browsers', name: '运行环境', icon: <LaptopOutlined /> },
|
||||
{ path: '/network-exits', name: '网络出口', icon: <ClusterOutlined /> },
|
||||
{ path: '/gateways', name: '网关管理', icon: <GatewayOutlined /> },
|
||||
],
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user