优化半自动履约客户页与后台操作布局
用户侧统一显示等待客服处理并精简平台标题;移除 Cookie 等专业表述;后台操作改为两行行内按钮,取消操作下拉。
This commit is contained in:
@@ -366,7 +366,7 @@ export async function createSemiAutoMock(
|
||||
pageDescription,
|
||||
loginInstructions:
|
||||
platform === 'weibo'
|
||||
? '真实登录:使用微博绑定手机号接收验证码登录,或使用微博 App 扫码登录。登录仅用于本次订单履约,不会在页面展示账号 Cookie。'
|
||||
? '真实登录:使用微博绑定手机号接收验证码登录,或使用微博 App 扫码登录。登录信息仅用于本次订单履约。'
|
||||
: connectorMode === 'mock'
|
||||
? 'Mock 登录:点击获取二维码后,页面轮询两次会自动模拟登录成功。'
|
||||
: '真实登录:点击获取二维码后,请使用虎牙 App 扫码并在手机上确认登录。',
|
||||
|
||||
@@ -172,7 +172,7 @@ function displayStatus(task: TaskRow) {
|
||||
return { key: 'failed', label: '需客服处理' }
|
||||
}
|
||||
if (task.login_status === 'authenticated') {
|
||||
return { key: 'waiting_operator', label: 'CK 已获取,等待客服处理' }
|
||||
return { key: 'waiting_operator', label: '等待客服处理' }
|
||||
}
|
||||
if (task.login_status === 'scanned') return { key: 'login_scanned', label: '已扫码,确认中' }
|
||||
if (task.login_status === 'qr_pending') return { key: 'waiting_login', label: '等待客户登录' }
|
||||
|
||||
@@ -141,7 +141,7 @@ test(
|
||||
)
|
||||
const authenticatedList = await listAdminSemiAutoTasks({ keyword: orderNo })
|
||||
assert.equal(authenticatedList.items[0]?.loginStatus, 'authenticated')
|
||||
assert.equal(authenticatedList.items[0]?.displayStatus.label, 'CK 已获取,等待客服处理')
|
||||
assert.equal(authenticatedList.items[0]?.displayStatus.label, '等待客服处理')
|
||||
const exported = await exportAdminSemiAutoCredential(taskId)
|
||||
assert.equal(exported.credentialId, credentialId)
|
||||
assert.equal(exported.platform, 'huya')
|
||||
|
||||
@@ -2,10 +2,7 @@ import {
|
||||
CheckCircleOutlined,
|
||||
ChromeOutlined,
|
||||
CopyOutlined,
|
||||
DownOutlined,
|
||||
KeyOutlined,
|
||||
LinkOutlined,
|
||||
MoreOutlined,
|
||||
ReloadOutlined,
|
||||
SendOutlined,
|
||||
UserSwitchOutlined,
|
||||
@@ -17,7 +14,6 @@ import {
|
||||
Card,
|
||||
Descriptions,
|
||||
Drawer,
|
||||
Dropdown,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
@@ -288,76 +284,6 @@ export default function AdminSemiAutoTasksPage() {
|
||||
})
|
||||
}
|
||||
|
||||
function credentialMenu(task: SemiAutoTask) {
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
key: 'copy',
|
||||
icon: <KeyOutlined />,
|
||||
label: '复制最终 CK',
|
||||
disabled: !task.credentialReady || credentialExportMutation.isPending,
|
||||
},
|
||||
{
|
||||
key: 'inject',
|
||||
icon: <ChromeOutlined />,
|
||||
label: '注入浏览器',
|
||||
disabled: !taskCanUseCredential(task) || injectionMutation.isPending,
|
||||
},
|
||||
],
|
||||
onClick: ({ key }: { key: string }) => {
|
||||
if (key === 'copy') {
|
||||
confirmCopyCredential(task)
|
||||
return
|
||||
}
|
||||
if (key === 'inject') injectionMutation.mutate(task)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function taskMenu(task: SemiAutoTask) {
|
||||
const active = task.executionStatus !== 'completed' && task.executionStatus !== 'cancelled'
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
key: 'copy',
|
||||
icon: <LinkOutlined />,
|
||||
label: '复制客户链接',
|
||||
},
|
||||
{
|
||||
key: 'sync',
|
||||
icon: <ReloadOutlined />,
|
||||
label: '刷新客户状态',
|
||||
disabled: syncMutation.isPending,
|
||||
},
|
||||
...(!task.assignedAdminUserId
|
||||
? [
|
||||
{
|
||||
key: 'claim',
|
||||
icon: <UserSwitchOutlined />,
|
||||
label: '领取任务',
|
||||
disabled: claimMutation.isPending,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(active
|
||||
? [
|
||||
{
|
||||
key: 'cancel',
|
||||
danger: true,
|
||||
label: '取消任务',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
onClick: ({ key }: { key: string }) => {
|
||||
if (key === 'copy') void copyCustomerLink(task)
|
||||
if (key === 'sync') syncMutation.mutate(task.id)
|
||||
if (key === 'claim') claimMutation.mutate(task.id)
|
||||
if (key === 'cancel') confirmCancel(task)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function openComplete(task: SemiAutoTask) {
|
||||
setCompleteTask(task)
|
||||
setCompleteFiles([])
|
||||
@@ -461,46 +387,88 @@ export default function AdminSemiAutoTasksPage() {
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 340,
|
||||
width: 520,
|
||||
render: (_, task) => (
|
||||
<Space wrap>
|
||||
<Button size="small" onClick={() => setDetailTaskId(task.id)}>
|
||||
详情
|
||||
</Button>
|
||||
{taskNeedsCustomerReauth(task) ? (
|
||||
<Space direction="vertical" size={6} style={{ width: '100%' }}>
|
||||
<Space wrap>
|
||||
<Button size="small" onClick={() => setDetailTaskId(task.id)}>
|
||||
详情
|
||||
</Button>
|
||||
{taskNeedsCustomerReauth(task) ? (
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
icon={<SendOutlined />}
|
||||
loading={reauthMutation.isPending}
|
||||
onClick={() => confirmReauth(task)}
|
||||
>
|
||||
重发授权
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
icon={<SendOutlined />}
|
||||
loading={reauthMutation.isPending}
|
||||
onClick={() => confirmReauth(task)}
|
||||
icon={<KeyOutlined />}
|
||||
disabled={!task.credentialReady}
|
||||
loading={credentialExportMutation.isPending}
|
||||
onClick={() => confirmCopyCredential(task)}
|
||||
>
|
||||
重发授权
|
||||
复制 CK
|
||||
</Button>
|
||||
) : null}
|
||||
<Dropdown menu={credentialMenu(task)} trigger={['click']}>
|
||||
<Button size="small" icon={<KeyOutlined />}>
|
||||
CK 操作 <DownOutlined />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
{task.executionStatus !== 'completed' &&
|
||||
task.executionStatus !== 'cancelled' &&
|
||||
!taskNeedsCustomerReauth(task) ? (
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
icon={<CheckCircleOutlined />}
|
||||
icon={<ChromeOutlined />}
|
||||
disabled={!taskCanUseCredential(task)}
|
||||
onClick={() => openComplete(task)}
|
||||
loading={injectionMutation.isPending}
|
||||
onClick={() => injectionMutation.mutate(task)}
|
||||
>
|
||||
完成
|
||||
注入浏览器
|
||||
</Button>
|
||||
) : null}
|
||||
<Dropdown menu={taskMenu(task)} trigger={['click']}>
|
||||
<Button size="small" icon={<MoreOutlined />}>
|
||||
更多
|
||||
</Space>
|
||||
<Space wrap>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<CopyOutlined />}
|
||||
onClick={() => void copyCustomerLink(task)}
|
||||
>
|
||||
客户链接
|
||||
</Button>
|
||||
</Dropdown>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<ReloadOutlined />}
|
||||
loading={syncMutation.isPending}
|
||||
onClick={() => syncMutation.mutate(task.id)}
|
||||
>
|
||||
刷新
|
||||
</Button>
|
||||
{!task.assignedAdminUserId ? (
|
||||
<Button
|
||||
size="small"
|
||||
icon={<UserSwitchOutlined />}
|
||||
loading={claimMutation.isPending}
|
||||
onClick={() => claimMutation.mutate(task.id)}
|
||||
>
|
||||
领取
|
||||
</Button>
|
||||
) : null}
|
||||
{task.executionStatus !== 'completed' &&
|
||||
task.executionStatus !== 'cancelled' &&
|
||||
!taskNeedsCustomerReauth(task) ? (
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
icon={<CheckCircleOutlined />}
|
||||
disabled={!taskCanUseCredential(task)}
|
||||
onClick={() => openComplete(task)}
|
||||
>
|
||||
完成
|
||||
</Button>
|
||||
) : null}
|
||||
{task.executionStatus !== 'completed' && task.executionStatus !== 'cancelled' ? (
|
||||
<Button size="small" danger onClick={() => confirmCancel(task)}>
|
||||
取消
|
||||
</Button>
|
||||
) : null}
|
||||
</Space>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
@@ -690,11 +658,22 @@ export default function AdminSemiAutoTasksPage() {
|
||||
>
|
||||
刷新客户状态
|
||||
</Button>
|
||||
<Dropdown menu={credentialMenu(detail.task)} trigger={['click']}>
|
||||
<Button icon={<KeyOutlined />}>
|
||||
CK 操作 <DownOutlined />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
<Button
|
||||
icon={<KeyOutlined />}
|
||||
disabled={!detail.task.credentialReady}
|
||||
loading={credentialExportMutation.isPending}
|
||||
onClick={() => confirmCopyCredential(detail.task)}
|
||||
>
|
||||
复制 CK
|
||||
</Button>
|
||||
<Button
|
||||
icon={<ChromeOutlined />}
|
||||
disabled={!taskCanUseCredential(detail.task)}
|
||||
loading={injectionMutation.isPending}
|
||||
onClick={() => injectionMutation.mutate(detail.task)}
|
||||
>
|
||||
注入浏览器
|
||||
</Button>
|
||||
</Space>
|
||||
</Card>
|
||||
<Card size="small" title="时间线">
|
||||
|
||||
@@ -251,14 +251,13 @@ export default function CustomerFulfillmentPage() {
|
||||
}
|
||||
|
||||
const config = selectedTask?.semiAutoConfig || {}
|
||||
const title = config.pageTitle || (isWeibo ? '微博账号授权' : '账号授权与角色绑定')
|
||||
const displayTitle = isWeibo ? '微博账号登录授权' : title
|
||||
const displayTitle = isWeibo ? '微博账号登录授权' : '虎牙账号登录授权'
|
||||
const loginQrVisible =
|
||||
selectedTask && ['qr_pending', 'scanned'].includes(selectedTask.loginStatus)
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<CustomerAuthFrame eyebrow="账号授权" title="正在读取订单信息">
|
||||
<CustomerAuthFrame title="正在读取订单信息">
|
||||
<div className="customer-auth-loading">
|
||||
<Spin indicator={<LoadingOutlined spin />} />
|
||||
<span>正在读取订单履约信息...</span>
|
||||
@@ -269,7 +268,7 @@ export default function CustomerFulfillmentPage() {
|
||||
|
||||
if (loadError || tasks.length === 0) {
|
||||
return (
|
||||
<CustomerAuthFrame eyebrow="账号授权" title="暂时无法打开订单">
|
||||
<CustomerAuthFrame title="暂时无法打开订单">
|
||||
<div className="customer-auth-state is-error">
|
||||
<h2>订单信息读取失败</h2>
|
||||
<p>{loadError || '该订单没有需要客户授权或绑定的任务。'}</p>
|
||||
@@ -280,11 +279,7 @@ export default function CustomerFulfillmentPage() {
|
||||
|
||||
if (selectedTask?.executionStatus === 'completed') {
|
||||
return (
|
||||
<CustomerAuthFrame
|
||||
platform={selectedTask.platform}
|
||||
eyebrow={isWeibo ? '微博账号授权' : '虎牙账号授权'}
|
||||
title={displayTitle}
|
||||
>
|
||||
<CustomerAuthFrame platform={selectedTask.platform} title={displayTitle}>
|
||||
<CustomerTaskFacts task={selectedTask} />
|
||||
<div className="customer-auth-state is-success">
|
||||
<div className="customer-auth-state-icon">
|
||||
@@ -309,11 +304,7 @@ export default function CustomerFulfillmentPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<CustomerAuthFrame
|
||||
platform={selectedTask?.platform}
|
||||
eyebrow={isWeibo ? '微博账号授权' : '虎牙账号授权'}
|
||||
title={displayTitle}
|
||||
>
|
||||
<CustomerAuthFrame platform={selectedTask?.platform} title={displayTitle}>
|
||||
<div className="customer-auth-content-stack">
|
||||
{tasks.length > 1 ? (
|
||||
<div className="customer-auth-task-switcher">
|
||||
@@ -503,7 +494,6 @@ export default function CustomerFulfillmentPage() {
|
||||
|
||||
function CustomerAuthFrame(input: {
|
||||
platform?: CustomerSemiAutoTask['platform']
|
||||
eyebrow: string
|
||||
title: string
|
||||
children: ReactNode
|
||||
}) {
|
||||
@@ -515,9 +505,6 @@ function CustomerAuthFrame(input: {
|
||||
<header className="customer-auth-hero">
|
||||
<div className="customer-auth-brand">
|
||||
<div>
|
||||
{input.platform !== 'weibo' ? (
|
||||
<span className="customer-auth-eyebrow">{input.eyebrow}</span>
|
||||
) : null}
|
||||
<h1>{input.title}</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1502,21 +1502,14 @@
|
||||
box-shadow: inset 0 0 24px rgba(255, 138, 0, 0.06);
|
||||
}
|
||||
|
||||
.customer-auth-eyebrow {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
color: rgba(255, 255, 255, 0.78);
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.customer-auth-brand h1 {
|
||||
margin: 0;
|
||||
color: #fff;
|
||||
font-size: clamp(26px, 4.2vw, 38px);
|
||||
line-height: 1.25;
|
||||
font-weight: 700;
|
||||
font-size: clamp(18px, 2.3vw, 22px);
|
||||
line-height: 1.3;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0;
|
||||
white-space: nowrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
@@ -2087,12 +2080,8 @@
|
||||
background-position: 58% center;
|
||||
}
|
||||
|
||||
.customer-auth-eyebrow {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.customer-auth-brand h1 {
|
||||
font-size: 26px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.customer-auth-warning {
|
||||
|
||||
Reference in New Issue
Block a user