refactor(ui): 全站 message.useMessage 统一 App.useApp,删除 contextHolder(走查 P1-#4)

This commit is contained in:
2026-09-28 07:22:18 +08:00
parent e52d8c797b
commit 96a4692c96
17 changed files with 106 additions and 311 deletions
@@ -3,21 +3,7 @@
// 数据走 GET /creator/accounts/monitor-views(画像 + 作品数/最近发布聚合),对齐竞品列表画像形态。
import { useCallback, useEffect, useState } from 'react';
import { history } from '@umijs/max';
import {
Flex,
Alert,
Button,
Card,
Form,
Modal,
Popconfirm,
Select,
Space,
Table,
Tag,
Typography,
message,
} from 'antd';
import { Alert, App, Button, Card, Flex, Form, Modal, Popconfirm, Select, Space, Table, Tag, Typography } from 'antd';
import { PlusOutlined, ReloadOutlined } from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import { remove, creatorUpdate, creatorGet } from '@/services/api';
@@ -43,7 +29,7 @@ export default function AccountManagementList() {
const [tagForm] = Form.useForm<{ tags: string[] }>();
const [tagBusy, setTagBusy] = useState(false);
const [actionBusy, setActionBusy] = useState('');
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const load = useCallback(async () => {
setPending(true);
@@ -54,8 +40,7 @@ export default function AccountManagementList() {
(rows ?? []).map((account: any) => ({
...account,
name: account.name,
tags: account.tags ?? [],
})),
tags: account.tags ?? [] })),
);
} catch (loadError) {
setError(loadError);
@@ -113,8 +98,7 @@ export default function AccountManagementList() {
{account.platform_account_key}
</Typography.Paragraph>
</div>
),
},
) },
{
title: '作品数据',
dataIndex: 'work_count',
@@ -125,13 +109,11 @@ export default function AccountManagementList() {
最近发布 {account.latest_published_at ? dateTime(account.latest_published_at) : '—'}
</Typography.Text>
</Flex>
),
},
) },
{
title: '标签',
dataIndex: 'tags',
render: (tags?: string[]) => (tags?.length ? tags.map((tag) => <Tag key={tag}>{tag}</Tag>) : <Typography.Text type="secondary">未设置</Typography.Text>),
},
render: (tags?: string[]) => (tags?.length ? tags.map((tag) => <Tag key={tag}>{tag}</Tag>) : <Typography.Text type="secondary">未设置</Typography.Text>) },
{
title: '状态',
dataIndex: 'status',
@@ -142,8 +124,7 @@ export default function AccountManagementList() {
</Tag>
<Typography.Text type="secondary">{account.authorization_status === 'authorized' ? '已授权' : '已撤销'}</Typography.Text>
</Flex>
),
},
) },
{
title: '操作',
key: 'actions',
@@ -169,13 +150,11 @@ export default function AccountManagementList() {
</Button>
</Popconfirm>
</Space>
),
},
) },
];
return (
<Card variant="outlined">
{contextHolder}
<Flex justify="space-between" align="center" style={{ marginBottom: 16 }}>
<Typography.Text type="secondary">共 {rows.length} 个自有账号</Typography.Text>
<Space>
@@ -2,25 +2,7 @@
// 展示作者头像与粉丝/关注/作品/最近发布等账号画像属性(migration 038 字段 + work 聚合)。
import { useCallback, useEffect, useState } from 'react';
import { history } from '@umijs/max';
import {
Alert,
Avatar,
Button,
Card,
Descriptions,
Empty,
Flex,
Form,
Listy,
Modal,
Popconfirm,
Select,
Space,
Spin,
Tag,
Typography,
message,
} from 'antd';
import { Alert, App, Avatar, Button, Card, Descriptions, Empty, Flex, Form, Listy, Modal, Popconfirm, Select, Space, Spin, Tag, Typography } from 'antd';
import { CloudUploadOutlined, ReloadOutlined } from '@ant-design/icons';
import { getList, remove, creatorAction, creatorUpdate } from '@/services/api';
import { conflictMessage, dateTime, platformLabel } from '@/utils/helpers';
@@ -50,8 +32,7 @@ const syncStatusMeta: Record<string, { color?: string; label: string }> = {
running: { color: 'processing', label: '同步中' },
paused: { label: '已暂停' },
failed: { color: 'error', label: '同步失败' },
blocked: { color: 'warning', label: '同步受阻' },
};
blocked: { color: 'warning', label: '同步受阻' } };
function formatCount(value?: number | null): string {
if (value === null || value === undefined) return '—';
@@ -66,7 +47,7 @@ export default function MonitoringAccountList() {
const [tagForm] = Form.useForm<{ tags: string[] }>();
const [tagBusy, setTagBusy] = useState(false);
const [actionBusy, setActionBusy] = useState('');
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const load = useCallback(async () => {
setPending(true);
@@ -152,7 +133,6 @@ export default function MonitoringAccountList() {
return (
<Card variant="outlined">
{contextHolder}
<Flex justify="space-between" align="center" style={{ marginBottom: 16 }}>
<Typography.Text type="secondary">共 {rows.length} 个监控账号</Typography.Text>
<Space>
@@ -232,8 +212,7 @@ export default function MonitoringAccountList() {
<Typography.Text type="danger" style={{ fontSize: 12 }}>{row.sync_error}</Typography.Text>
) : (
'—'
),
},
) },
]}
/>
</div>
+5 -24
View File
@@ -4,30 +4,14 @@
// 指标 GET /creator/works/:id/metrics,评论 GET /creator/comments?work_id=(定时采集由后端调度器维护)。
import { useCallback, useEffect, useMemo, useState } from 'react';
import { Line } from '@ant-design/charts';
import {
Alert,
Button,
Card,
Col,
Drawer,
Empty,
Flex,
InputNumber,
Row,
Space,
Spin,
Table,
Typography,
message,
} from 'antd';
import { Alert, App, Button, Card, Col, Drawer, Empty, Flex, InputNumber, Row, Space, Spin, Table, Typography } from 'antd';
import {
CommentOutlined,
EyeOutlined,
FireOutlined,
LikeOutlined,
ReloadOutlined,
ShareAltOutlined,
} from '@ant-design/icons';
ShareAltOutlined } from '@ant-design/icons';
import { creatorGet, getList } from '@/services/api';
import { conflictMessage, dateTime } from '@/utils/helpers';
@@ -49,8 +33,7 @@ function GrowthChart({ metrics }: { metrics: any[] }) {
.map((series) => ({
time: dateTime(point.collected_at),
metric: series.label,
value: point[series.key],
})),
value: point[series.key] })),
),
[metrics],
);
@@ -88,7 +71,7 @@ export default function WorksGallery({ sourceId, sourceType, emptyHint }: WorksG
const [commentPage, setCommentPage] = useState(1);
const [commentTotal, setCommentTotal] = useState(0);
const [commentsPending, setCommentsPending] = useState(false);
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const loadWorks = useCallback(async () => {
setWorksPending(true);
@@ -149,7 +132,6 @@ export default function WorksGallery({ sourceId, sourceType, emptyHint }: WorksG
return (
<>
{contextHolder}
<Card
title="作品列表"
extra={
@@ -290,8 +272,7 @@ export default function WorksGallery({ sourceId, sourceType, emptyHint }: WorksG
setCommentPage(page);
loadComments(detailWork.id, page);
},
hideOnSinglePage: true,
}}
hideOnSinglePage: true }}
columns={[
{ title: '作者', dataIndex: 'author_name', width: 140, render: (v) => v || '—' },
{ title: '内容', dataIndex: 'content', ellipsis: true },
+9 -34
View File
@@ -2,24 +2,7 @@
// 布局改用 antd Tabs(账号资料 / 自动响应策略)——归档版三区块信息量超过单页栅格,Tabs 是 antd 标准组织方式。
import { useCallback, useEffect, useState } from 'react';
import { history, useParams } from '@umijs/max';
import {
Flex,
Alert,
Button,
Card,
Checkbox,
Descriptions,
Form,
Input,
InputNumber,
Popconfirm,
Select,
Space,
Tabs,
Tag,
Typography,
message,
} from 'antd';
import { Alert, App, Button, Card, Checkbox, Descriptions, Flex, Form, Input, InputNumber, Popconfirm, Select, Space, Tabs, Tag, Typography } from 'antd';
import { creatorAction, creatorCreate, creatorGet, creatorRequest, creatorUpdate, getList } from '@/services/api';
import { conflictMessage, dateTime } from '@/utils/helpers';
@@ -41,8 +24,7 @@ function profileForm(profile: any) {
note: profile.note || '',
business_status: profile.business_status || 'normal',
reply_requirements: profile.reply_requirements || '',
cooldown_seconds: profile.cooldown_seconds || 86400,
};
cooldown_seconds: profile.cooldown_seconds || 86400 };
}
export default function Page() {
@@ -61,7 +43,7 @@ export default function Page() {
const [relationError, setRelationError] = useState<any>(null);
const [listener, setListener] = useState<any>(null);
const [listenerError, setListenerError] = useState<any>(null);
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const selected = profiles.find((item) => item.id === selectedID);
@@ -115,8 +97,7 @@ export default function Page() {
try {
const result = await creatorUpdate(`/creator/accounts/${encodeURIComponent(selected.id)}/profile`, {
...values,
cooldown_seconds: Number(values.cooldown_seconds),
});
cooldown_seconds: Number(values.cooldown_seconds) });
setProfiles((items) => items.map((item) => (item.id === selected.id ? result : item)));
form.setFieldsValue(profileForm(result));
messageApi.success('账号资料已保存。');
@@ -170,8 +151,7 @@ export default function Page() {
setBusy(true);
try {
const result = await creatorAction(`/creator/accounts/${encodeURIComponent(selected.id)}/big-account`, {
enabled: !selected.big_account,
});
enabled: !selected.big_account });
setProfiles((items) => items.map((item) => (item.id === selected.id ? result : item)));
form.setFieldsValue(profileForm(result));
messageApi.success(result.big_account ? '已开启大号模式。' : '已关闭大号模式。');
@@ -192,13 +172,11 @@ export default function Page() {
candidate_texts: String(values.candidate_texts || '')
.split(',')
.map((item: string) => item.trim())
.filter(Boolean),
};
.filter(Boolean) };
await creatorCreate('/creator/relations', {
big_account_id: selected.id,
small_account_id: values.execution_account_id,
enabled: true,
});
enabled: true });
const result = editingStrategyID
? await creatorUpdate(`/creator/strategies/${encodeURIComponent(editingStrategyID)}`, payload)
: await creatorCreate(`/creator/accounts/${encodeURIComponent(selected.id)}/strategies`, payload);
@@ -235,8 +213,7 @@ export default function Page() {
await creatorCreate('/creator/relations', {
big_account_id: relation.big_account_id,
small_account_id: relation.small_account_id,
enabled: false,
});
enabled: false });
setRelations((items) => items.filter((item) => item.small_account_id !== relation.small_account_id));
messageApi.success('账号关系已解除;已有策略不会自动恢复。');
} catch (actionError) {
@@ -451,8 +428,7 @@ export default function Page() {
enabled: strategy.enabled,
event_types: strategy.event_types || [],
action: strategy.action,
candidate_texts: (strategy.candidate_texts || []).join(', '),
});
candidate_texts: (strategy.candidate_texts || []).join(', ') });
}}
disabled={busy}
>
@@ -479,7 +455,6 @@ export default function Page() {
return (
<div>
{contextHolder}
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<Typography.Title level={4} style={{ marginBottom: 0 }}>
+4 -20
View File
@@ -6,20 +6,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { history, useParams } from '@umijs/max';
import { Line } from '@ant-design/charts';
import {
Flex,
Alert,
Button,
Card,
Descriptions,
Form,
Input,
Modal,
Space,
Tag,
Typography,
message,
} from 'antd';
import { Alert, App, Button, Card, Descriptions, Flex, Form, Input, Modal, Space, Tag, Typography } from 'antd';
import { accountAction, createDraft, getOne, getList, creatorGet, creatorAction } from '@/services/api';
import { accountReadiness, conflictMessage, dateTime, platformLabel } from '@/utils/helpers';
import { usePageActions, usePageInfo } from '@/components/PageActions';
@@ -31,8 +18,7 @@ const collectionStatusMeta: Record<string, { color?: string; label: string }> =
succeeded: { color: 'success', label: '采集中' },
running: { color: 'processing', label: '采集运行中' },
failed: { color: 'error', label: '采集失败' },
blocked: { color: 'warning', label: '采集受阻' },
};
blocked: { color: 'warning', label: '采集受阻' } };
// 粉丝趋势:把账号画像时序展开成长表(时间 × 指标 × 数值)供折线图渲染。
const accountSeriesMeta = [
@@ -56,8 +42,7 @@ function AccountTrendChart({ metrics }: { metrics: any[] }) {
.map((series) => ({
time: dateTime(point.collected_at),
metric: series.label,
value: point[series.key],
})),
value: point[series.key] })),
),
[metrics],
);
@@ -89,7 +74,7 @@ export default function Page() {
const [draftBusy, setDraftBusy] = useState(false);
const [collectionStatus, setCollectionStatus] = useState<any>(null);
const [accountMetrics, setAccountMetrics] = useState<any[]>([]);
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const readiness = account ? accountReadiness(account, binding, browsersError) : null;
@@ -246,7 +231,6 @@ export default function Page() {
return (
<Flex vertical gap={16}>
{contextHolder}
<Card title="账号状态" extra={pauseResume}>
<Flex vertical gap={16}>
<Descriptions column={1} size="small">
+8 -30
View File
@@ -1,21 +1,6 @@
// 账号导入:语义对齐 web.archived AccountImportPage(分享链接解析任务列表 + 新建导入任务 Modal)。
import { useCallback, useEffect, useState } from 'react';
import {
Alert,
Avatar,
Button,
Card,
Flex,
Form,
Input,
Modal,
Select,
Space,
Table,
Tag,
Typography,
message,
} from 'antd';
import { Alert, App, Avatar, Button, Card, Flex, Form, Input, Modal, Select, Space, Table, Tag, Typography } from 'antd';
import { PlusOutlined, ReloadOutlined } from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import { create, getList, creatorAction } from '@/services/api';
@@ -47,7 +32,7 @@ export default function Page() {
const [form] = Form.useForm<FormValues>();
const [busy, setBusy] = useState(false);
const [retryID, setRetryID] = useState<string | null>(null);
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const loadShareJobs = useCallback(async () => {
setPending(true);
@@ -90,8 +75,7 @@ export default function Page() {
try {
const variables: Record<string, unknown> = {
platform: sharePlatform,
share_url: extractedShareURL,
};
share_url: extractedShareURL };
if (values.tags?.length) variables.tags = values.tags;
await create('creator-competitor-share-jobs', variables);
await loadShareJobs();
@@ -144,8 +128,7 @@ export default function Page() {
) : (
inner
);
},
},
} },
{
title: '分享链接',
dataIndex: 'share_url',
@@ -157,8 +140,7 @@ export default function Page() {
</Typography.Link>
<Typography.Text type="secondary" style={{ fontSize: 12 }}>{dateTime(job.created_at)}</Typography.Text>
</div>
),
},
) },
{ title: '平台', dataIndex: 'platform', render: (value?: string) => platformLabel(value) },
{
title: '状态',
@@ -166,14 +148,12 @@ export default function Page() {
render: (value?: string) => {
const status = shareJobStatus[value ?? ''] || { label: value || '未知', tone: 'default' as const };
return <Tag color={status.tone === 'error' ? 'error' : status.tone === 'success' ? 'success' : status.tone === 'warning' ? 'warning' : 'default'}>{status.label}</Tag>;
},
},
} },
{ title: '尝试次数', dataIndex: 'attempts', render: (value?: number) => <Typography.Text type="secondary">{value ?? 0} / 3</Typography.Text> },
{
title: '失败原因',
dataIndex: 'failure_reason',
render: (value?: string) => (value ? <Typography.Text type="danger" style={{ fontSize: 12 }}>{value}</Typography.Text> : '—'),
},
render: (value?: string) => (value ? <Typography.Text type="danger" style={{ fontSize: 12 }}>{value}</Typography.Text> : '—') },
{
title: '操作',
key: 'actions',
@@ -185,13 +165,11 @@ export default function Page() {
</Button>
) : (
'—'
),
},
) },
];
return (
<Card variant="outlined">
{contextHolder}
<Flex justify="space-between" align="center" style={{ marginBottom: 16 }}>
<Typography.Text type="secondary">共 {jobs.length} 个导入任务</Typography.Text>
<Space>
@@ -4,18 +4,7 @@
// 作品列表走 GET /creator/works?source_id=<competitor_id>&source_type=competitor,定时采集由后端调度器维护。
import { useCallback, useEffect, useState } from 'react';
import { history, useParams } from '@umijs/max';
import {
Alert,
Avatar,
Button,
Card,
Descriptions,
Flex,
Space,
Tag,
Typography,
message,
} from 'antd';
import { Alert, App, Avatar, Button, Card, Descriptions, Flex, Space, Tag, Typography } from 'antd';
import { creatorAction, getOne } from '@/services/api';
import { conflictMessage, dateTime, platformLabel } from '@/utils/helpers';
import { usePageActions, usePageInfo } from '@/components/PageActions';
@@ -45,8 +34,7 @@ const syncStatusMeta: Record<string, { color?: string; label: string }> = {
running: { color: 'processing', label: '同步中' },
paused: { label: '已暂停' },
failed: { color: 'error', label: '同步失败' },
blocked: { color: 'warning', label: '同步受阻' },
};
blocked: { color: 'warning', label: '同步受阻' } };
function formatCount(value?: number | null): string {
if (value === null || value === undefined) return '—';
@@ -59,7 +47,7 @@ export default function Page() {
const [pending, setPending] = useState(true);
const [error, setError] = useState<any>(null);
const [actionBusy, setActionBusy] = useState(false);
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const loadCompetitor = useCallback(async () => {
setPending(true);
@@ -150,7 +138,6 @@ export default function Page() {
return (
<Flex vertical gap={16}>
{contextHolder}
<Card title="账号画像">
<Descriptions
column={{ xs: 1, sm: 2, md: 3 }}
@@ -171,8 +158,7 @@ export default function Page() {
<Typography.Text type="danger" style={{ fontSize: 12 }}>{competitor.sync_error}</Typography.Text>
) : (
'—'
),
},
) },
]}
/>
</Card>
+1 -1
View File
@@ -1,7 +1,7 @@
// 运行环境详情:语义对齐 web.archived BrowsersPage.jsx 的 BrowserDetail。
import { useEffect, useState } from 'react';
import { history, useParams } from '@umijs/max';
import { Flex, Alert, App, Button, Card, Descriptions, Space, Tag, Typography } from 'antd';
import { Alert, App, Button, Card, Descriptions, Flex, Space, Tag, Typography } from 'antd';
import { CopyOutlined } from '@ant-design/icons';
import { getOne } from '@/services/api';
+9 -17
View File
@@ -2,7 +2,7 @@
// 动作保持显式动词(start/stop/recycle),启停禁用条件与归档版一致。
import { useCallback, useEffect, useState } from 'react';
import { history } from '@umijs/max';
import { Alert, Button, Card, Flex, Popconfirm, Space, Table, Tag, Typography, message } from 'antd';
import { Alert, App, Button, Card, Flex, Popconfirm, Space, Table, Tag, Typography } from 'antd';
import { CopyOutlined, PlusOutlined } from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import { browserAction, getList } from '@/services/api';
@@ -17,8 +17,7 @@ const stateText: Record<string, string> = {
gateway_unreachable: '网关不可达',
runtime_missing: '运行实例缺失',
recorded: '已记录',
missing: '未部署',
};
missing: '未部署' };
const blockText: Record<string, string> = {
account_revoked: '授权已撤销',
account_paused: '账号已暂停',
@@ -26,8 +25,7 @@ const blockText: Record<string, string> = {
network_exit_missing: '固定出口缺失',
network_exit_unhealthy: '固定出口不健康',
runtime_stop_pending: '停止结果待确认',
runtime_missing: '运行实例缺失',
};
runtime_missing: '运行实例缺失' };
const scheduleBlock = (runtime: any) =>
runtime.schedule_status === 'ready' ? '' : blockText[runtime.schedule_block_reason] || runtime.schedule_block_reason || '不可调度';
@@ -37,7 +35,7 @@ export default function Page() {
const [pending, setPending] = useState(true);
const [error, setError] = useState<any>(null);
const [busy, setBusy] = useState('');
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const load = useCallback(async () => {
setPending(true);
@@ -83,8 +81,7 @@ export default function Page() {
{runtime.alias} · seed {runtime.fingerprint?.seed}
</Typography.Paragraph>
</div>
),
},
) },
{
title: '状态',
dataIndex: 'state',
@@ -93,8 +90,7 @@ export default function Page() {
return (
<Tag color={state === 'running' ? 'success' : state === 'stopped' ? 'warning' : 'default'}>{known ? stateText[state] : `未知状态:${state}`}</Tag>
);
},
},
} },
{
title: '固定绑定',
dataIndex: 'account_id',
@@ -109,8 +105,7 @@ export default function Page() {
<Tag color={block ? 'warning' : 'success'}>{block || '可调度'}</Tag>
</div>
);
},
},
} },
{
title: 'CDP',
dataIndex: 'endpoint',
@@ -124,8 +119,7 @@ export default function Page() {
</Space>
) : (
<Typography.Text type="secondary">—</Typography.Text>
),
},
) },
{
title: '操作',
key: 'actions',
@@ -155,13 +149,11 @@ export default function Page() {
</Popconfirm>
</Space>
);
},
},
} },
];
return (
<Card variant="outlined">
{contextHolder}
<Flex justify="space-between" align="center" style={{ marginBottom: 16 }}>
<Typography.Text type="secondary">共 {runtimes.length} 个运行环境</Typography.Text>
<Button type="primary" icon={<PlusOutlined />} onClick={() => history.push('/browsers/new')}>
+5 -9
View File
@@ -2,7 +2,7 @@
// 高级指纹参数用 antd Collapse 收纳;空值不提交,收敛到 seed 驱动。
import { useEffect, useState } from 'react';
import { history } from '@umijs/max';
import { Alert, Button, Card, Checkbox, Collapse, Form, Input, InputNumber, Select, Space, Typography, message } from 'antd';
import { Alert, App, Button, Card, Checkbox, Collapse, Form, Input, InputNumber, Select, Space, Typography } from 'antd';
import { create, getList } from '@/services/api';
import { conflictMessage } from '@/utils/helpers';
@@ -21,8 +21,7 @@ function fingerprintPayload(form: any) {
brand_version: form.brand_version,
lang: form.lang,
accept_lang: form.accept_lang,
timezone: form.timezone,
};
timezone: form.timezone };
for (const [key, value] of Object.entries(advanced)) if (value) payload[key] = value;
if (form.hardware_concurrency) payload.hardware_concurrency = Number(form.hardware_concurrency);
if (form.disable_spoofing?.length > 0) payload.disable_spoofing = form.disable_spoofing.join(',');
@@ -44,7 +43,7 @@ export default function Page() {
const [accounts, setAccounts] = useState<any[]>([]);
const [networkExits, setNetworkExits] = useState<any[]>([]);
const [submitBusy, setSubmitBusy] = useState(false);
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
useEffect(() => {
Promise.allSettled([
@@ -88,8 +87,7 @@ export default function Page() {
gateway: values.gateway,
account_id: values.account_id,
network_exit_id: values.network_exit_id || '',
fingerprint: fingerprintPayload(values),
});
fingerprint: fingerprintPayload(values) });
messageApi.success('运行环境已创建。');
history.push('/browsers');
} catch (reason: any) {
@@ -103,7 +101,6 @@ export default function Page() {
return (
<div>
{contextHolder}
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'flex-end' }}>
<Button onClick={() => history.push('/browsers')}>返回运行环境</Button>
</div>
@@ -188,8 +185,7 @@ export default function Page() {
<Checkbox.Group options={spoofingFields.map((field) => ({ value: field, label: field }))} />
</Form.Item>
</Space>
),
},
) },
]}
/>
<Space>
+6 -12
View File
@@ -3,7 +3,7 @@
// 平台筛选已随 migration 038(platform 收敛 douyin)移除。
import { useEffect, useState } from 'react';
import { CommentOutlined, LikeOutlined, ShareAltOutlined } from '@ant-design/icons';
import { Flex, Alert, Button, Card, Col, Drawer, Empty, Input, InputNumber, Modal, Row, Select, Space, Spin, Table, Tag, Typography, message } from 'antd';
import { Alert, App, Button, Card, Col, Drawer, Empty, Flex, Input, InputNumber, Modal, Row, Select, Space, Spin, Table, Tag, Typography } from 'antd';
import { creatorAction, creatorGet, creatorUpdate, getList } from '@/services/api';
import { conflictMessage, dateTime } from '@/utils/helpers';
@@ -18,8 +18,7 @@ const publishedAtStatusMeta: Record<string, { color?: string; label: string }> =
verified: { color: 'green', label: '已核验' },
future: { color: 'orange', label: '未来时间' },
pending_verification: { label: '待核验' },
invalid: { color: 'red', label: '无效时间' },
};
invalid: { color: 'red', label: '无效时间' } };
interface Filters {
minLikes?: number;
@@ -55,7 +54,7 @@ export default function Page() {
const [rewriteTitle, setRewriteTitle] = useState('');
const [rewriteScript, setRewriteScript] = useState('');
const [pendingSwitchWork, setPendingSwitchWork] = useState<string | null>(null);
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
async function load() {
setPending(true);
@@ -71,9 +70,7 @@ export default function Page() {
min_shares: filters.minShares === undefined ? undefined : Number(filters.minShares),
published_at_status: filters.publishedAtStatus,
published_after: filters.publishedAfter ? `${filters.publishedAfter}T00:00:00Z` : undefined,
published_before: filters.publishedBefore ? `${filters.publishedBefore}T23:59:59Z` : undefined,
},
});
published_before: filters.publishedBefore ? `${filters.publishedBefore}T23:59:59Z` : undefined } });
setWorks(result.data);
setWorkPageInfo({ total: result.total, hasNext: Boolean(result.hasNext) });
} catch (loadError) {
@@ -163,8 +160,7 @@ export default function Page() {
setMaterialPending(true);
try {
const result = await creatorAction(`/creator/works/${encodeURIComponent(material.work_id)}/material/rewrite/confirm`, {
requirement: rewriteRequirement,
});
requirement: rewriteRequirement });
setMaterial(result);
messageApi.info('已确认仿写要求,请在可编辑字段中复核并保存结果。');
} catch (actionError) {
@@ -196,8 +192,7 @@ export default function Page() {
try {
const result = await creatorUpdate(`/creator/works/${encodeURIComponent(material.work_id)}/material/rewrite`, {
title: rewriteTitle,
script: rewriteScript,
});
script: rewriteScript });
setMaterial(result);
messageApi.info('仿写标题和口播已保存,可继续人工复核。');
} catch (actionError) {
@@ -215,7 +210,6 @@ export default function Page() {
return (
<>
{contextHolder}
<Flex vertical gap={16}>
<Card title="筛选作品" extra={<Typography.Text type="secondary" style={{ fontSize: 12 }}>调整条件后自动刷新</Typography.Text>}>
<Space size={12} wrap style={{ display: 'flex' }}>
+6 -11
View File
@@ -1,7 +1,7 @@
// 采集设置:语义对齐 web.archived CreatorSettingsPage.jsx(提交 d97cade)。
// 四类配置用 antd Tabs 组织;仅用 antd 默认组件。
import { useCallback, useEffect, useState } from 'react';
import { Alert, Button, Card, Checkbox, Form, Input, InputNumber, Tabs, Typography, message } from 'antd';
import { Alert, App, Button, Card, Checkbox, Form, Input, InputNumber, Tabs, Typography } from 'antd';
import { creatorGet, creatorUpdate } from '@/services/api';
import { conflictMessage } from '@/utils/helpers';
@@ -36,7 +36,7 @@ export default function Page() {
const [pending, setPending] = useState(true);
const [error, setError] = useState<any>(null);
const [busy, setBusy] = useState(false);
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const load = useCallback(async () => {
setPending(true);
@@ -75,7 +75,6 @@ export default function Page() {
return (
<div>
{contextHolder}
<Typography.Paragraph type="secondary" style={{ marginBottom: 16 }}>按配置类别管理 UTC 采集窗口、指标采集和已批准的服务。</Typography.Paragraph>
<Card variant="outlined">
<Form form={form} layout="vertical" onFinish={save} initialValues={{}}>
@@ -93,8 +92,7 @@ export default function Page() {
<InputNumber min={1} style={{ width: 200 }} />
</Form.Item>
</div>
),
},
) },
{
key: 'metrics',
label: '指标采集',
@@ -113,8 +111,7 @@ export default function Page() {
<InputNumber min={1} style={{ width: 200 }} />
</Form.Item>
</div>
),
},
) },
{
key: 'ai',
label: 'AI 服务',
@@ -130,8 +127,7 @@ export default function Page() {
<Checkbox>已完成 AI 配置审批</Checkbox>
</Form.Item>
</div>
),
},
) },
{
key: 'transcription',
label: '转写服务',
@@ -147,8 +143,7 @@ export default function Page() {
<Checkbox>已完成转写配置审批</Checkbox>
</Form.Item>
</div>
),
},
) },
]}
/>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
+13 -25
View File
@@ -2,7 +2,7 @@
// 六个页签:评论/线索/规则/事件监听/私信/操作记录;实时更新走 /creator/updates SSE;仅用 antd 默认组件。
import { useEffect, useRef, useState } from 'react';
import type { ReactNode } from 'react';
import { Flex, Alert, Button, Card, Checkbox, Col, Empty, Input, Modal, Row, Select, Space, Spin, Table, Tabs, Tag, Typography, message } from 'antd';
import { Alert, App, Button, Card, Checkbox, Col, Empty, Flex, Input, Modal, Row, Select, Space, Spin, Table, Tabs, Tag, Typography } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { create, creatorAction, creatorCreate, creatorGet, creatorSubscribe, creatorSyncConversation, creatorUpdate, getList } from '@/services/api';
import { conflictMessage, dateTime } from '@/utils/helpers';
@@ -53,8 +53,7 @@ function operationStatus(state?: string, reason?: string): string {
failed: '失败(平台明确拒绝或执行前失败)',
blocked: '已阻止(未发送)',
processing: '处理中(尚未获得最终结果)',
uncertain: '结果不明(禁止自动重试)',
};
uncertain: '结果不明(禁止自动重试)' };
return `${labels[state || ''] || state || '未知状态'}${reason ? `:${reason}` : ''}`;
}
@@ -129,7 +128,7 @@ export default function Page() {
const [pendingDMSwitch, setPendingDMSwitch] = useState<{ kind: 'account' | 'conversation'; id: string } | null>(null);
const loadSequence = useRef(0);
const messageSequence = useRef(0);
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const visibleData = dataTab === tab ? data : [];
const visibleConversations = conversationAccountID === dmAccountID ? conversations : [];
@@ -245,8 +244,7 @@ export default function Page() {
setMessages(items);
setMessagePageInfo({
total: Array.isArray(result) ? items.length : (result.total ?? items.length),
hasNext: Array.isArray(result) ? false : Boolean(result.has_next),
});
hasNext: Array.isArray(result) ? false : Boolean(result.has_next) });
})
.catch((loadError) => {
if (sequence === messageSequence.current) setMessageError(loadError);
@@ -378,8 +376,7 @@ export default function Page() {
topic: ruleForm.topic,
include_keywords: parseKeywords(ruleForm.include_keywords),
exclude_keywords: parseKeywords(ruleForm.exclude_keywords),
ai_requirement: ruleForm.ai_requirement,
};
ai_requirement: ruleForm.ai_requirement };
const result = editingRuleID
? await creatorUpdate(`/creator/rules/${encodeURIComponent(editingRuleID)}`, variables)
: (await create('creator-rules', variables)).data;
@@ -418,8 +415,7 @@ export default function Page() {
text: draft?.text || '',
target_uid: comment.author_uid || '',
target_comment_id: comment.id,
operation_key: draft?.operation_key || '',
});
operation_key: draft?.operation_key || '' });
messageApi.info(mode === 'dm' ? '请填写发送账号和私信文案,再进行逐次确认。' : '请填写发送账号和文案,再进行逐次确认。');
}
@@ -448,8 +444,7 @@ export default function Page() {
account_id: reply.account_id,
target_uid: reply.target_uid,
...(replyMode === 'comment' ? { target_comment_id: reply.target_comment_id } : {}),
text: reply.text,
});
text: reply.text });
const result = await creatorAction(`/creator/operations/${encodeURIComponent(created.id)}/execute`);
messageApi[result.state === 'succeeded' ? 'success' : 'warning'](operationStatus(result.state, result.reason));
if (result.state === 'succeeded') {
@@ -532,8 +527,7 @@ export default function Page() {
platform: dmConfirm.platform,
account_id: dmAccountID,
target_uid: dmConfirm.peer_uid,
text: dmText,
});
text: dmText });
const result = await creatorAction(`/creator/operations/${encodeURIComponent(created.id)}/execute`);
messageApi[result.state === 'succeeded' ? 'success' : 'warning'](operationStatus(result.state, result.reason));
if (result.state === 'succeeded') {
@@ -826,8 +820,7 @@ export default function Page() {
topic: rule.topic,
include_keywords: (rule.include_keywords || []).join(', '),
exclude_keywords: (rule.exclude_keywords || []).join(', '),
ai_requirement: rule.ai_requirement,
});
ai_requirement: rule.ai_requirement });
}}
disabled={busy}
>
@@ -877,8 +870,7 @@ export default function Page() {
padding: '8px 8px',
textAlign: 'left',
whiteSpace: 'normal',
background: conversation.id === conversationID ? '#e6f4ff' : undefined,
}}
background: conversation.id === conversationID ? '#e6f4ff' : undefined }}
type={conversation.id === conversationID ? 'primary' : 'default'}
ghost={conversation.id === conversationID}
onClick={() => switchConversation(conversation.id)}
@@ -935,8 +927,7 @@ export default function Page() {
maxWidth: '80%',
background: msg.direction === 'outbound' ? '#e6f4ff' : '#fafafa',
borderRadius: 6,
padding: 12,
}}
padding: 12 }}
>
<Typography.Paragraph style={{ marginBottom: 0 }}>{msg.text || `[${msg.message_type}]`}</Typography.Paragraph>
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
@@ -992,8 +983,7 @@ export default function Page() {
statusTag(
item.state === 'succeeded' ? 'success' : item.state === 'uncertain' || item.state === 'processing' ? 'warning' : item.state === 'failed' ? 'error' : 'default',
operationStatus(item.state, item.reason),
),
},
) },
{ title: '时间', dataIndex: 'created_at', render: (value: string) => <Typography.Text type="secondary" style={{ fontSize: 12 }}>{dateTime(value)}</Typography.Text> },
{ title: '原因', dataIndex: 'reason', render: (value?: string) => <Typography.Text type="secondary" style={{ fontSize: 12 }}>{value || '—'}</Typography.Text> },
];
@@ -1013,7 +1003,6 @@ export default function Page() {
return (
<Card variant="outlined">
{contextHolder}
<Tabs
activeKey={tab}
onChange={switchTab}
@@ -1031,8 +1020,7 @@ export default function Page() {
? eventsTab
: key === 'dms'
? dmsTab
: operationsTab,
}))}
: operationsTab }))}
/>
<Modal
open={Boolean(confirm)}
+2 -15
View File
@@ -1,19 +1,7 @@
// 草稿核对:语义对齐 web.archived DraftPage.jsx(只读快照 / 版本链 / 显式核对与入队 / 关联任务)。
import { useCallback, useEffect, useState } from 'react';
import { history, useParams } from '@umijs/max';
import {
Flex,
Alert,
Button,
Card,
Checkbox,
Descriptions,
Modal,
Space,
Tag,
Typography,
message,
} from 'antd';
import { Alert, App, Button, Card, Checkbox, Descriptions, Flex, Modal, Space, Tag, Typography } from 'antd';
import { confirmDraft, enqueueConfirmation, getOne, getList } from '@/services/api';
import { conflictMessage } from '@/utils/helpers';
@@ -37,7 +25,7 @@ export default function Page() {
const [reviewed, setReviewed] = useState(false);
const [confirmOpen, setConfirmOpen] = useState(false);
const [busy, setBusy] = useState(false);
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const load = useCallback(async () => {
setPending(true);
@@ -112,7 +100,6 @@ export default function Page() {
return (
<div>
{contextHolder}
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography.Text type="secondary" ellipsis>
{draft.account.platform_account_key} · 草稿版本 {draft.version}
+5 -9
View File
@@ -1,6 +1,6 @@
// 网关管理:语义对齐 web.archived GatewaysPage.jsx(注册/编辑 Modal、令牌显隐复制、删除)。
import { useCallback, useEffect, useState } from 'react';
import { Alert, Button, Card, Flex, Form, Input, Modal, Popconfirm, Space, Table, Typography, message } from 'antd';
import { Alert, App, Button, Card, Flex, Form, Input, Modal, Popconfirm, Space, Table, Typography } 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';
@@ -15,8 +15,7 @@ function GatewayFormModal({
busy,
error,
onCancel,
onSubmit,
}: {
onSubmit }: {
open: boolean;
initial: any | null;
busy: boolean;
@@ -79,7 +78,7 @@ export default function Page() {
const [editing, setEditing] = useState<any | null>(null);
const [busy, setBusy] = useState('');
const [created, setCreated] = useState<any>(null);
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const load = useCallback(async () => {
setPending(true);
@@ -148,8 +147,7 @@ export default function Page() {
{
title: '令牌',
dataIndex: 'token',
render: (token: string, gateway) => <TokenCell token={token} name={gateway.name} />,
},
render: (token: string, gateway) => <TokenCell token={token} name={gateway.name} /> },
{
title: '操作',
key: 'actions',
@@ -165,13 +163,11 @@ export default function Page() {
</Button>
</Popconfirm>
</Space>
),
},
) },
];
return (
<Card variant="outlined">
{contextHolder}
<Flex justify="space-between" align="center" style={{ marginBottom: 16 }}>
<Typography.Text type="secondary">共 {gateways.length} 个网关</Typography.Text>
<Button type="primary" icon={<PlusOutlined />} onClick={() => setCreateOpen(true)}>
+8 -15
View File
@@ -1,7 +1,7 @@
// 网络出口:语义对齐 web.archived NetworkExitsPage.jsx(列表 + 创建/编辑 Modal + 检测/启停/删除动作 + 出口详情页)。
import { useCallback, useEffect, useMemo, useState } from 'react';
import { history } from '@umijs/max';
import { Alert, Button, Card, Flex, Form, Input, InputNumber, Modal, Popconfirm, Select, Space, Table, Tag, Typography, message } from 'antd';
import { Alert, App, Button, Card, Flex, Form, Input, InputNumber, Modal, Popconfirm, Select, Space, Table, Tag, Typography } 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';
@@ -11,8 +11,7 @@ const healthText: Record<string, string> = {
unchecked: '未检测',
healthy: '健康',
unhealthy: '不健康',
disabled: '已停用',
};
disabled: '已停用' };
const protocolOptions = ['http', 'https', 'socks4', 'socks5'].map((value) => ({ value, label: value }));
function healthTag(status?: string) {
@@ -39,8 +38,7 @@ function ExitFormModal({
busy,
error,
onCancel,
onSubmit,
}: {
onSubmit }: {
open: boolean;
initial: any | null;
title: string;
@@ -87,8 +85,7 @@ function ExitFormModal({
validator: (_rule, value) =>
!value || getFieldValue('username')
? Promise.resolve()
: Promise.reject(new Error('填写密码时必须同时填写用户名')),
}),
: Promise.reject(new Error('填写密码时必须同时填写用户名')) }),
]}
>
<Input maxLength={255} style={{ width: 160 }} />
@@ -114,7 +111,7 @@ export default function Page() {
const [createOpen, setCreateOpen] = useState(false);
const [editing, setEditing] = useState<any | null>(null);
const [busy, setBusy] = useState('');
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const load = useCallback(async () => {
setPending(true);
@@ -225,8 +222,7 @@ export default function Page() {
{`${exit.protocol}://${exit.host}:${exit.port}`}
</Typography.Paragraph>
</div>
),
},
) },
{ title: '状态', dataIndex: 'health_status', render: (value?: string) => healthTag(value) },
{ title: '用户名', dataIndex: 'username', render: (value?: string) => value || '无' },
{ title: '密码', dataIndex: 'password', render: (value?: string) => value || '无' },
@@ -242,8 +238,7 @@ export default function Page() {
<Typography.Text ellipsis style={{ maxWidth: 200, display: 'inline-block' }}>
{accountsByExit.get(exit.id)?.join('、') || '未绑定'}
</Typography.Text>
),
},
) },
{
title: '操作',
key: 'actions',
@@ -273,13 +268,11 @@ export default function Page() {
</Button>
</Popconfirm>
</Space>
),
},
) },
];
return (
<Card variant="outlined">
{contextHolder}
<Flex justify="space-between" align="center" style={{ marginBottom: 16 }}>
<Typography.Text type="secondary">共 {exits.length} 个网络出口</Typography.Text>
<Button type="primary" icon={<PlusOutlined />} onClick={() => setCreateOpen(true)}>
+9 -17
View File
@@ -2,7 +2,7 @@
// 四卡改用 Tabs(当前状态 / 确认快照 / 执行关联 / 唯一安全动作)——antd 默认组件的信息组织方式。
import { useCallback, useEffect, useState } from 'react';
import { history, useParams } from '@umijs/max';
import { Flex, Alert, Button, Card, Descriptions, Select, Space, Tabs, Tag, Typography, message } from 'antd';
import { Alert, App, Button, Card, Descriptions, Flex, Select, Space, Tabs, Tag, Typography } from 'antd';
import { getOne, taskAction } from '@/services/api';
import { dateTime } from '@/utils/helpers';
@@ -13,8 +13,7 @@ const stateText: Record<string, string> = {
failed: '失败',
needs_confirmation: '需要人工确认',
policy_hold: '策略暂停',
cancelled: '已取消',
};
cancelled: '已取消' };
const stateColor: Record<string, string> = {
queued: 'default',
executing: 'processing',
@@ -22,8 +21,7 @@ const stateColor: Record<string, string> = {
failed: 'error',
needs_confirmation: 'warning',
policy_hold: 'warning',
cancelled: 'default',
};
cancelled: 'default' };
const holdText: Record<string, string> = {
account_paused: '账号已暂停',
@@ -43,8 +41,7 @@ const holdText: Record<string, string> = {
confirmation_missing: '确认快照缺失',
confirmation_version_changed: '确认版本已变化',
binding_version_changed: '环境或出口版本已变化',
legacy_confirmation_required: '历史任务结果未知,等待人工核验',
};
legacy_confirmation_required: '历史任务结果未知,等待人工核验' };
const holdLabel = (reason?: string) => (reason ? holdText[reason] || `未知停止原因:${reason}` : '无');
function actionErrorText(error: any): string {
@@ -61,7 +58,7 @@ export default function Page() {
const [error, setError] = useState<any>(null);
const [verification, setVerification] = useState('not_executed');
const [busy, setBusy] = useState(false);
const [messageApi, contextHolder] = message.useMessage();
const { message: messageApi } = App.useApp();
const load = useCallback(async () => {
setPending(true);
@@ -117,8 +114,7 @@ export default function Page() {
<Descriptions.Item label="人工核验">{task.verification_result || '尚未记录'}</Descriptions.Item>
</Descriptions>
</Flex>
),
},
) },
{
key: 'confirmation',
label: '确认快照',
@@ -142,8 +138,7 @@ export default function Page() {
</Button>
</Space>
</Flex>
),
},
) },
{
key: 'execution',
label: '执行关联',
@@ -169,8 +164,7 @@ export default function Page() {
) : null}
</Space>
</Flex>
),
},
) },
{
key: 'action',
label: '唯一安全动作',
@@ -217,13 +211,11 @@ export default function Page() {
) : null}
{task.allowed_action ? null : <Typography.Text type="secondary">当前没有可安全执行的动作。</Typography.Text>}
</Flex>
),
},
) },
];
return (
<Card variant="outlined">
{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>