feat(worker-platform): 接单平台全平台UI/UX视觉重构与体验升级
1. 全局导航侧边栏 (WorkerLayout): - 打造专属品牌Logo标识与实时在线脉冲呼吸灯 - 打手身份卡片化:展示打手名称、在线状态徽章、会员等级及可用余额药丸徽标 - 导航菜单选中项增加橙色微光背景与指示条,售后问题单菜单支持待处理红点角标 2. 我的订单管理 (WorkerOrderDesktopList): - 桌面端整合头部:标题 + 实时在办监控徽标 + 居中搜索框 + 刷新按钮单行整合 - 新增4栏工单态势KPI看板:代练进行中、等待验收、异常与问题单、已验收完结,支持点击卡片即时联动筛选 - 胶囊化状态过滤与结算收益金额说明 3. 售后问题单 (WorkerAfterSalesPage): - 桌面端整合头部:待说明紧急红点药丸徽标、分段筛选与刷新按钮 4. 个人中心 (WorkerProfileDesktopSections): - 打造全新打手Hero身份卡:大号头像环、等级徽标、UID、手机、邀请码一键复制芯片、会员权益微标签与晋级进度条 - 4栏立体资产看板:可用余额、冻结押金、待解冻金额、累计结算收益 - 快捷操作按资金管理、安全与设置、其他服务结构化分组
This commit is contained in:
@@ -148,32 +148,52 @@ export default function WorkerAfterSalesPage() {
|
||||
message={`你有 ${pendingCount} 条售后单待提交说明,请尽快处理,逾期可能影响结算与等级`}
|
||||
/>
|
||||
) : null}
|
||||
<div className="worker-after-sales-toolbar">
|
||||
<Segmented
|
||||
options={STATUS_FILTERS.map((filter) => ({
|
||||
value: filter.value,
|
||||
label: (
|
||||
<Space size={4}>
|
||||
{filter.label}
|
||||
{countByFilter[filter.value] > 0 ? (
|
||||
<span className="worker-after-sales-filter-count">
|
||||
{countByFilter[filter.value]}
|
||||
</span>
|
||||
) : null}
|
||||
</Space>
|
||||
),
|
||||
}))}
|
||||
value={statusFilter}
|
||||
onChange={(value) => setStatusFilter(value as StatusFilter)}
|
||||
block={isMobile}
|
||||
/>
|
||||
<Button
|
||||
icon={<ReloadOutlined />}
|
||||
loading={afterSalesCasesQuery.isFetching}
|
||||
onClick={() => afterSalesCasesQuery.refetch()}
|
||||
>
|
||||
刷新
|
||||
</Button>
|
||||
<div className="worker-after-sales-desktop-header">
|
||||
<div className="worker-after-sales-header-left">
|
||||
<Typography.Title level={3} className="worker-after-sales-heading">
|
||||
售后问题单
|
||||
</Typography.Title>
|
||||
{pendingCount > 0 ? (
|
||||
<span className="worker-after-sales-alert-pill">
|
||||
<span className="worker-after-sales-pulse-dot" />
|
||||
{pendingCount} 条待提交说明
|
||||
</span>
|
||||
) : (
|
||||
<span className="worker-after-sales-normal-pill">无待办售后单</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="worker-after-sales-header-center">
|
||||
<Segmented
|
||||
className="worker-after-sales-segmented"
|
||||
options={STATUS_FILTERS.map((filter) => ({
|
||||
value: filter.value,
|
||||
label: (
|
||||
<Space size={4}>
|
||||
<span>{filter.label}</span>
|
||||
{countByFilter[filter.value] > 0 ? (
|
||||
<span className="worker-after-sales-filter-count">
|
||||
{countByFilter[filter.value]}
|
||||
</span>
|
||||
) : null}
|
||||
</Space>
|
||||
),
|
||||
}))}
|
||||
value={statusFilter}
|
||||
onChange={(value) => setStatusFilter(value as StatusFilter)}
|
||||
block={isMobile}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="worker-after-sales-header-right">
|
||||
<Button
|
||||
icon={<ReloadOutlined />}
|
||||
loading={afterSalesCasesQuery.isFetching}
|
||||
onClick={() => afterSalesCasesQuery.refetch()}
|
||||
>
|
||||
刷新
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{afterSalesCasesQuery.isLoading ? (
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { ReloadOutlined, SearchOutlined } from '@ant-design/icons'
|
||||
import {
|
||||
CheckCircleOutlined,
|
||||
ClockCircleOutlined,
|
||||
ReloadOutlined,
|
||||
SearchOutlined,
|
||||
ThunderboltOutlined,
|
||||
WarningOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { Button, Card, Empty, Input, Space, Table, Tabs, Typography } from 'antd'
|
||||
import type { TableColumnsType } from 'antd'
|
||||
|
||||
@@ -52,16 +59,167 @@ export function WorkerOrderDesktopList({
|
||||
onKeywordSearch: () => void
|
||||
onPageChange: (page: number, pageSize: number) => void
|
||||
}) {
|
||||
const inProgressCount = Number(overviewByStatus.get('in_progress')?.orderCount || 0)
|
||||
const inProgressSettlement = Number(overviewByStatus.get('in_progress')?.settlementAmount || 0)
|
||||
|
||||
const pendingCount = Number(overviewByStatus.get('pending_acceptance')?.orderCount || 0)
|
||||
const pendingSettlement = Number(
|
||||
overviewByStatus.get('pending_acceptance')?.settlementAmount || 0,
|
||||
)
|
||||
|
||||
const problemCount = Number(overviewByStatus.get('problem')?.orderCount || 0)
|
||||
const openCount = Number(overviewByStatus.get('open')?.orderCount || 0)
|
||||
const evidenceCount = Number(overviewByStatus.get('vip_evidence_pending')?.orderCount || 0)
|
||||
const abnormalTotal = problemCount + openCount + evidenceCount
|
||||
|
||||
const acceptedCount = Number(overviewByStatus.get('accepted')?.orderCount || 0)
|
||||
const acceptedSettlement = Number(overviewByStatus.get('accepted')?.settlementAmount || 0)
|
||||
|
||||
return (
|
||||
<section className="page-stack">
|
||||
<div className="worker-page-head">
|
||||
<Typography.Title level={3}>我的订单</Typography.Title>
|
||||
<Button icon={<ReloadOutlined />} loading={refreshing} onClick={onRefresh}>
|
||||
刷新
|
||||
</Button>
|
||||
{/* 桌面端全新整合头部 */}
|
||||
<div className="worker-orders-desktop-header">
|
||||
<div className="worker-orders-header-left">
|
||||
<Typography.Title level={3} className="worker-orders-heading">
|
||||
我的订单
|
||||
</Typography.Title>
|
||||
<span className="worker-orders-live-badge">
|
||||
<span className="worker-orders-pulse-dot" />
|
||||
在办工单实时监控
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="worker-orders-header-center">
|
||||
<Space.Compact className="worker-orders-desktop-search">
|
||||
<Input
|
||||
allowClear
|
||||
value={keywordInput}
|
||||
placeholder="搜索订单号 / 商品名 / 游戏ID / 游戏昵称"
|
||||
prefix={<SearchOutlined style={{ color: '#94a3b8' }} />}
|
||||
onChange={(event) => onKeywordInputChange(event.target.value)}
|
||||
onPressEnter={onKeywordSearch}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SearchOutlined />}
|
||||
loading={searching}
|
||||
onClick={onKeywordSearch}
|
||||
>
|
||||
搜索
|
||||
</Button>
|
||||
</Space.Compact>
|
||||
</div>
|
||||
|
||||
<div className="worker-orders-header-right">
|
||||
<Button icon={<ReloadOutlined />} loading={refreshing} onClick={onRefresh}>
|
||||
刷新
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card bordered={false}>
|
||||
{/* 4 栏工单态势 KPI 仪表盘 */}
|
||||
<div className="worker-orders-kpi-grid">
|
||||
{/* 卡片 1: 代练中 */}
|
||||
<div
|
||||
className={`worker-orders-kpi-card ${status === 'in_progress' ? 'active' : ''}`}
|
||||
onClick={() => onStatusChange(status === 'in_progress' ? '' : 'in_progress')}
|
||||
>
|
||||
<div className="worker-orders-kpi-top">
|
||||
<div className="worker-orders-kpi-icon-wrap in-progress">
|
||||
<ThunderboltOutlined />
|
||||
</div>
|
||||
<div className="worker-orders-kpi-info">
|
||||
<span className="worker-orders-kpi-title">代练进行中</span>
|
||||
<strong className="worker-orders-kpi-val">
|
||||
{inProgressCount}
|
||||
<span className="worker-orders-kpi-unit"> 单</span>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div className="worker-orders-kpi-bottom">
|
||||
<span className="worker-orders-kpi-sub-label">待结算预估:</span>
|
||||
<span className="worker-orders-kpi-sub-val">{formatMoney(inProgressSettlement)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 卡片 2: 待验收 */}
|
||||
<div
|
||||
className={`worker-orders-kpi-card ${status === 'pending_acceptance' ? 'active' : ''}`}
|
||||
onClick={() =>
|
||||
onStatusChange(status === 'pending_acceptance' ? '' : 'pending_acceptance')
|
||||
}
|
||||
>
|
||||
<div className="worker-orders-kpi-top">
|
||||
<div className="worker-orders-kpi-icon-wrap pending">
|
||||
<ClockCircleOutlined />
|
||||
</div>
|
||||
<div className="worker-orders-kpi-info">
|
||||
<span className="worker-orders-kpi-title">已提交待验收</span>
|
||||
<strong className="worker-orders-kpi-val">
|
||||
{pendingCount}
|
||||
<span className="worker-orders-kpi-unit"> 单</span>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div className="worker-orders-kpi-bottom">
|
||||
<span className="worker-orders-kpi-sub-label">待审核报酬:</span>
|
||||
<span className="worker-orders-kpi-sub-val">{formatMoney(pendingSettlement)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 卡片 3: 异常单 */}
|
||||
<div
|
||||
className={`worker-orders-kpi-card ${
|
||||
status === 'problem' || status === 'open' || status === 'vip_evidence_pending'
|
||||
? 'active'
|
||||
: ''
|
||||
}`}
|
||||
onClick={() => onStatusChange(status === 'problem' ? '' : 'problem')}
|
||||
>
|
||||
<div className="worker-orders-kpi-top">
|
||||
<div className="worker-orders-kpi-icon-wrap problem">
|
||||
<WarningOutlined />
|
||||
</div>
|
||||
<div className="worker-orders-kpi-info">
|
||||
<span className="worker-orders-kpi-title">异常与问题工单</span>
|
||||
<strong className="worker-orders-kpi-val">
|
||||
{abnormalTotal}
|
||||
<span className="worker-orders-kpi-unit"> 单</span>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div className="worker-orders-kpi-bottom">
|
||||
<span className="worker-orders-kpi-sub-label">
|
||||
超时 {openCount} · 补图 {evidenceCount} · 问题 {problemCount}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 卡片 4: 已验收 */}
|
||||
<div
|
||||
className={`worker-orders-kpi-card ${status === 'accepted' ? 'active' : ''}`}
|
||||
onClick={() => onStatusChange(status === 'accepted' ? '' : 'accepted')}
|
||||
>
|
||||
<div className="worker-orders-kpi-top">
|
||||
<div className="worker-orders-kpi-icon-wrap accepted">
|
||||
<CheckCircleOutlined />
|
||||
</div>
|
||||
<div className="worker-orders-kpi-info">
|
||||
<span className="worker-orders-kpi-title">已验收完结</span>
|
||||
<strong className="worker-orders-kpi-val">
|
||||
{acceptedCount}
|
||||
<span className="worker-orders-kpi-unit"> 单</span>
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div className="worker-orders-kpi-bottom">
|
||||
<span className="worker-orders-kpi-sub-label">已结算到账:</span>
|
||||
<span className="worker-orders-kpi-sub-val">{formatMoney(acceptedSettlement)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card bordered={false} className="worker-orders-card">
|
||||
<Space direction="vertical" size={16} style={{ width: '100%' }}>
|
||||
<Tabs
|
||||
className="worker-order-tabs"
|
||||
@@ -88,26 +246,13 @@ export function WorkerOrderDesktopList({
|
||||
onChange={(nextKey) => onStatusChange(nextKey === 'all' ? '' : nextKey)}
|
||||
/>
|
||||
|
||||
<Space.Compact style={{ width: '100%', maxWidth: 460 }}>
|
||||
<Input
|
||||
allowClear
|
||||
value={keywordInput}
|
||||
placeholder="搜索订单号 / 商品名 / 游戏ID / 游戏昵称"
|
||||
prefix={<SearchOutlined />}
|
||||
onChange={(event) => onKeywordInputChange(event.target.value)}
|
||||
onPressEnter={onKeywordSearch}
|
||||
/>
|
||||
<Button icon={<SearchOutlined />} loading={searching} onClick={onKeywordSearch}>
|
||||
搜索
|
||||
</Button>
|
||||
</Space.Compact>
|
||||
|
||||
<Table<WorkOrder>
|
||||
rowKey="workOrderId"
|
||||
size="small"
|
||||
size="middle"
|
||||
loading={loading}
|
||||
dataSource={orders}
|
||||
columns={columns}
|
||||
className="worker-orders-table"
|
||||
locale={{
|
||||
emptyText: error ? (
|
||||
<Empty description={error instanceof Error ? error.message : '读取我的订单失败'} />
|
||||
@@ -125,7 +270,7 @@ export function WorkerOrderDesktopList({
|
||||
total: pagination?.total || 0,
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: [10, 20, 50, 100],
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
showTotal: (total) => `共 ${total} 条工单`,
|
||||
onChange: onPageChange,
|
||||
}}
|
||||
scroll={{ x: 1440 }}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import {
|
||||
BankOutlined,
|
||||
ClockCircleOutlined,
|
||||
CopyOutlined,
|
||||
CustomerServiceOutlined,
|
||||
GlobalOutlined,
|
||||
LockOutlined,
|
||||
LogoutOutlined,
|
||||
SafetyCertificateOutlined,
|
||||
TrophyOutlined,
|
||||
UserOutlined,
|
||||
WalletOutlined,
|
||||
} from '@ant-design/icons'
|
||||
import { Avatar, Button, Card, Descriptions, Progress, Space, Tag, Tooltip, Typography } from 'antd'
|
||||
import { Button, Card, Descriptions, Progress, Space, Tag, Tooltip, Typography } from 'antd'
|
||||
|
||||
import type {
|
||||
WorkerLevelProgress,
|
||||
@@ -17,12 +20,10 @@ import type {
|
||||
WorkerWithdrawalAccount,
|
||||
} from '@/types/worker-platform'
|
||||
import { formatDateTime } from '@/utils/date-time'
|
||||
|
||||
import {
|
||||
formatMoney,
|
||||
formatWorkerStatus,
|
||||
formatWithdrawChannel,
|
||||
resolveWorkerInitial,
|
||||
formatWorkerStatus,
|
||||
resolveWorkerStatusColor,
|
||||
} from './worker-profile-view-utils'
|
||||
|
||||
@@ -35,88 +36,155 @@ export function WorkerProfileDesktopIdentity({
|
||||
worker: WorkerUser
|
||||
summary: WorkerProfileSummary | undefined
|
||||
levelProgress: WorkerLevelProgress | null | undefined
|
||||
onCopyInviteCode: () => void
|
||||
onCopyInviteCode?: () => void
|
||||
}) {
|
||||
const nextRequired =
|
||||
levelProgress && levelProgress.nextThreshold
|
||||
? Math.max(0, levelProgress.nextThreshold - (summary?.acceptedOrderCount || 0))
|
||||
: 0
|
||||
|
||||
return (
|
||||
<Card size="small">
|
||||
<Space size={16} wrap align="start">
|
||||
<Avatar size={64}>{resolveWorkerInitial(worker)}</Avatar>
|
||||
<Space direction="vertical" size={2}>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
{worker.displayName || worker.username}
|
||||
</Typography.Title>
|
||||
<Typography.Text type="secondary">账号:{worker.username}</Typography.Text>
|
||||
<Typography.Text type="secondary">手机号:{worker.phone || '-'}</Typography.Text>
|
||||
</Space>
|
||||
<Space size={[8, 8]} wrap>
|
||||
<Tag color={resolveWorkerStatusColor(worker.status)}>
|
||||
{formatWorkerStatus(worker.status)}
|
||||
</Tag>
|
||||
<Tag color={worker.workerType === 'internal' ? 'blue' : 'default'}>
|
||||
{worker.workerType === 'internal' ? '内部打手' : '外部打手'}
|
||||
</Tag>
|
||||
<Tag>{worker.level?.name || '未分级'}</Tag>
|
||||
</Space>
|
||||
</Space>
|
||||
<Space direction="vertical" size={4} style={{ marginTop: 12, width: '100%' }}>
|
||||
<Space wrap size={[8, 8]}>
|
||||
<Typography.Text type="secondary">我的邀请码:</Typography.Text>
|
||||
<Typography.Text strong>{worker.inviteCode || '-'}</Typography.Text>
|
||||
<Button
|
||||
size="small"
|
||||
type="link"
|
||||
icon={<CopyOutlined />}
|
||||
disabled={!worker.inviteCode}
|
||||
onClick={onCopyInviteCode}
|
||||
>
|
||||
复制
|
||||
</Button>
|
||||
</Space>
|
||||
{levelProgress && levelProgress.nextThreshold !== null ? (
|
||||
<Space direction="vertical" size={0} style={{ width: '100%' }}>
|
||||
<Typography.Text type="secondary">
|
||||
已累计完成 {summary?.acceptedOrderCount || 0} 单,距下一等级还需{' '}
|
||||
{Math.max(0, levelProgress.nextThreshold - (summary?.acceptedOrderCount || 0))} 单
|
||||
</Typography.Text>
|
||||
<Progress
|
||||
percent={levelProgress.progressPercent}
|
||||
size="small"
|
||||
format={(percent) => `${percent}%`}
|
||||
/>
|
||||
</Space>
|
||||
) : (
|
||||
<Typography.Text type="secondary">
|
||||
已累计完成 {summary?.acceptedOrderCount || 0} 单,当前已是最高等级
|
||||
</Typography.Text>
|
||||
)}
|
||||
</Space>
|
||||
</Card>
|
||||
<div className="worker-profile-hero-card">
|
||||
<div className="worker-profile-hero-top">
|
||||
<div className="worker-profile-hero-user">
|
||||
<div className="worker-profile-avatar">
|
||||
{(worker.displayName || worker.username).slice(0, 1)}
|
||||
</div>
|
||||
<div className="worker-profile-user-info">
|
||||
<div className="worker-profile-name-row">
|
||||
<strong className="worker-profile-name">
|
||||
{worker.displayName || worker.username}
|
||||
</strong>
|
||||
<Tag
|
||||
color={resolveWorkerStatusColor(worker.status)}
|
||||
className="worker-profile-status-tag"
|
||||
>
|
||||
{formatWorkerStatus(worker.status)}
|
||||
</Tag>
|
||||
{worker.level?.name ? (
|
||||
<span className="worker-profile-level-badge">{worker.level.name}</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="worker-profile-meta-row">
|
||||
<span className="worker-profile-meta-item">账号: {worker.username}</span>
|
||||
<span className="worker-profile-meta-divider">·</span>
|
||||
<span className="worker-profile-meta-item">UID: {worker.workerId}</span>
|
||||
{worker.phone ? (
|
||||
<>
|
||||
<span className="worker-profile-meta-divider">·</span>
|
||||
<span className="worker-profile-meta-item">手机: {worker.phone}</span>
|
||||
</>
|
||||
) : null}
|
||||
{worker.inviteCode ? (
|
||||
<>
|
||||
<span className="worker-profile-meta-divider">·</span>
|
||||
<span className="worker-profile-meta-item">
|
||||
邀请码: <strong>{worker.inviteCode}</strong>
|
||||
{onCopyInviteCode ? (
|
||||
<Button
|
||||
size="small"
|
||||
type="link"
|
||||
icon={<CopyOutlined />}
|
||||
style={{ padding: '0 4px', height: 'auto' }}
|
||||
onClick={onCopyInviteCode}
|
||||
/>
|
||||
) : null}
|
||||
</span>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="worker-profile-privileges">
|
||||
<div className="worker-profile-priv-chip">
|
||||
<span className="priv-label">免押额度</span>
|
||||
<strong className="priv-val">{formatMoney(worker.effectiveDepositFreeAmount)}</strong>
|
||||
</div>
|
||||
<div className="worker-profile-priv-chip">
|
||||
<span className="priv-label">最大接单</span>
|
||||
<strong className="priv-val">
|
||||
{worker.level?.permissions.maxActiveOrders || 0} 单
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="worker-profile-progress-bar-wrap">
|
||||
<div className="worker-profile-progress-header">
|
||||
<span className="worker-profile-progress-title">
|
||||
已完成 <strong>{summary?.acceptedOrderCount || 0}</strong> 单
|
||||
</span>
|
||||
{levelProgress && levelProgress.nextThreshold ? (
|
||||
<span className="worker-profile-progress-sub">距下一级还需 {nextRequired} 单</span>
|
||||
) : (
|
||||
<span className="worker-profile-progress-sub">当前已是最高等级</span>
|
||||
)}
|
||||
</div>
|
||||
<Progress
|
||||
percent={levelProgress ? levelProgress.progressPercent : 100}
|
||||
strokeColor={{ '0%': '#ff6a00', '100%': '#ff944d' }}
|
||||
size={['100%', 8]}
|
||||
showInfo={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function WorkerProfileDesktopWallet({ worker }: { worker: WorkerUser }) {
|
||||
const items = [
|
||||
['可用余额', worker.wallet.availableAmount, '可用于抢单冻结或提现申请'],
|
||||
['冻结押金', worker.wallet.frozenDepositAmount, '抢单后冻结,验收后进入待解冻'],
|
||||
[
|
||||
'待解冻金额',
|
||||
worker.wallet.pendingUnfreezeAmount,
|
||||
'验收报酬与押金按配置天数解冻到账,期间不可提现',
|
||||
],
|
||||
['累计结算', worker.wallet.totalSettledAmount, '已结算入账的报酬合计'],
|
||||
] as const
|
||||
{
|
||||
key: 'available',
|
||||
label: '可用余额',
|
||||
amount: worker.wallet.availableAmount,
|
||||
note: '可用于抢单冻结与提现',
|
||||
icon: <WalletOutlined />,
|
||||
cls: 'kpi-wallet',
|
||||
},
|
||||
{
|
||||
key: 'frozen',
|
||||
label: '冻结押金',
|
||||
amount: worker.wallet.frozenDepositAmount,
|
||||
note: '抢单后暂扣,验收后解冻',
|
||||
icon: <SafetyCertificateOutlined />,
|
||||
cls: 'kpi-quota',
|
||||
},
|
||||
{
|
||||
key: 'pending',
|
||||
label: '待解冻金额',
|
||||
amount: worker.wallet.pendingUnfreezeAmount,
|
||||
note: '按配置天数释放到可用余额',
|
||||
icon: <ClockCircleOutlined />,
|
||||
cls: 'kpi-tasks',
|
||||
},
|
||||
{
|
||||
key: 'settled',
|
||||
label: '累计结算收益',
|
||||
amount: worker.wallet.totalSettledAmount,
|
||||
note: '已结算入账的报酬合计',
|
||||
icon: <TrophyOutlined />,
|
||||
cls: 'kpi-pool',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<Card size="small" className="worker-profile-wallet-card">
|
||||
<div className="worker-profile-wallet-strip">
|
||||
{items.map(([label, amount, note]) => (
|
||||
<div key={label} className="worker-profile-wallet-item">
|
||||
<span className="worker-profile-wallet-label">{label}</span>
|
||||
<strong className="worker-profile-wallet-value">{formatMoney(amount)}</strong>
|
||||
<small className="worker-profile-wallet-note">{note}</small>
|
||||
<div className="worker-profile-wallet-grid">
|
||||
{items.map((item) => (
|
||||
<div key={item.key} className="worker-profile-wallet-kpi-card">
|
||||
<div className="worker-profile-kpi-top">
|
||||
<div className={`worker-profile-kpi-icon ${item.cls}`}>{item.icon}</div>
|
||||
<div className="worker-profile-kpi-info">
|
||||
<span className="worker-profile-kpi-label">{item.label}</span>
|
||||
<strong className="worker-profile-kpi-val">{formatMoney(item.amount)}</strong>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
<div className="worker-profile-kpi-bottom">
|
||||
<span className="worker-profile-kpi-note">{item.note}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -152,60 +220,77 @@ export function WorkerProfileDesktopActions({
|
||||
onLogout: () => void
|
||||
}) {
|
||||
return (
|
||||
<Card title="快捷操作" size="small">
|
||||
<Space wrap size={[12, 12]}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<WalletOutlined />}
|
||||
disabled={!canUseFinanceActions}
|
||||
onClick={onRecharge}
|
||||
>
|
||||
充值申请
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<BankOutlined />}
|
||||
disabled={!canUseFinanceActions}
|
||||
onClick={onWithdraw}
|
||||
>
|
||||
提现申请
|
||||
</Button>
|
||||
{canAddWithdrawalAccount ? (
|
||||
<Button
|
||||
icon={<BankOutlined />}
|
||||
disabled={!canUseFinanceActions}
|
||||
onClick={onAddWithdrawalAccount}
|
||||
>
|
||||
添加提现信息
|
||||
</Button>
|
||||
) : null}
|
||||
{needsAlipayQrCode ? (
|
||||
<Button
|
||||
icon={<BankOutlined />}
|
||||
disabled={!canUseFinanceActions}
|
||||
onClick={onAddAlipayQrCode}
|
||||
>
|
||||
补充支付宝收款码
|
||||
</Button>
|
||||
) : null}
|
||||
<Tooltip title={renameHint}>
|
||||
<Button icon={<UserOutlined />} disabled={renameDisabled} onClick={onRename}>
|
||||
修改昵称
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button icon={<LockOutlined />} onClick={onChangePassword}>
|
||||
修改密码
|
||||
</Button>
|
||||
<Button icon={<GlobalOutlined />} onClick={onManageDevices}>
|
||||
登录设备
|
||||
</Button>
|
||||
<Button icon={<CustomerServiceOutlined />} onClick={onContact}>
|
||||
联系管理员
|
||||
</Button>
|
||||
<Button danger icon={<LogoutOutlined />} onClick={onLogout}>
|
||||
退出登录
|
||||
</Button>
|
||||
</Space>
|
||||
<Card title="快捷操作与服务" size="small" className="worker-profile-actions-card">
|
||||
<div className="worker-profile-actions-grouped">
|
||||
<div className="worker-profile-actions-group">
|
||||
<span className="worker-profile-group-title">资金管理</span>
|
||||
<Space wrap size={[10, 10]}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<WalletOutlined />}
|
||||
disabled={!canUseFinanceActions}
|
||||
onClick={onRecharge}
|
||||
>
|
||||
充值申请
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<BankOutlined />}
|
||||
disabled={!canUseFinanceActions}
|
||||
onClick={onWithdraw}
|
||||
>
|
||||
提现申请
|
||||
</Button>
|
||||
{canAddWithdrawalAccount ? (
|
||||
<Button
|
||||
icon={<BankOutlined />}
|
||||
disabled={!canUseFinanceActions}
|
||||
onClick={onAddWithdrawalAccount}
|
||||
>
|
||||
添加提现信息
|
||||
</Button>
|
||||
) : null}
|
||||
{needsAlipayQrCode ? (
|
||||
<Button
|
||||
icon={<BankOutlined />}
|
||||
disabled={!canUseFinanceActions}
|
||||
onClick={onAddAlipayQrCode}
|
||||
>
|
||||
补充支付宝收款码
|
||||
</Button>
|
||||
) : null}
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<div className="worker-profile-actions-group">
|
||||
<span className="worker-profile-group-title">安全与设置</span>
|
||||
<Space wrap size={[10, 10]}>
|
||||
<Tooltip title={renameHint}>
|
||||
<Button icon={<UserOutlined />} disabled={renameDisabled} onClick={onRename}>
|
||||
修改昵称
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button icon={<LockOutlined />} onClick={onChangePassword}>
|
||||
修改密码
|
||||
</Button>
|
||||
<Button icon={<GlobalOutlined />} onClick={onManageDevices}>
|
||||
登录设备
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<div className="worker-profile-actions-group">
|
||||
<span className="worker-profile-group-title">其他服务</span>
|
||||
<Space wrap size={[10, 10]}>
|
||||
<Button icon={<CustomerServiceOutlined />} onClick={onContact}>
|
||||
联系客服
|
||||
</Button>
|
||||
<Button danger icon={<LogoutOutlined />} onClick={onLogout}>
|
||||
退出登录
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -226,7 +311,7 @@ export function WorkerProfileDesktopAccountDetails({
|
||||
onAddWithdrawalAccount: () => void
|
||||
}) {
|
||||
return (
|
||||
<Card size="small">
|
||||
<Card size="small" className="worker-profile-details-card">
|
||||
<Descriptions column={{ xs: 1, md: 2 }} bordered size="small">
|
||||
<Descriptions.Item label="账号">{worker.username}</Descriptions.Item>
|
||||
<Descriptions.Item label="昵称">{worker.displayName || '-'}</Descriptions.Item>
|
||||
@@ -260,17 +345,16 @@ export function WorkerProfileDesktopAccountDetails({
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="提现信息" span={2}>
|
||||
{withdrawalAccounts.length > 0 ? (
|
||||
<Space direction="vertical" size={2}>
|
||||
<Space direction="vertical" size={4}>
|
||||
{withdrawalAccounts.map((account) => (
|
||||
<Space key={account.accountChannel} wrap size={4}>
|
||||
<Typography.Text>{formatWithdrawChannel(account.accountChannel)}</Typography.Text>
|
||||
<Typography.Text>{account.accountName}</Typography.Text>
|
||||
<Space key={account.accountChannel} wrap size={6}>
|
||||
<Tag color="blue">{formatWithdrawChannel(account.accountChannel)}</Tag>
|
||||
<Typography.Text strong>{account.accountName}</Typography.Text>
|
||||
<Typography.Text type="secondary">
|
||||
{account.accountChannel === 'wechat'
|
||||
? '已上传收款二维码'
|
||||
: `${account.accountNoMasked} / ${account.alipayQrCodeImage ? '已上传收款二维码' : '未上传收款二维码'}`}
|
||||
? '已上传收款码'
|
||||
: `${account.accountNoMasked} / ${account.alipayQrCodeImage ? '已上传收款码' : '未上传收款码'}`}
|
||||
</Typography.Text>
|
||||
<Typography.Text type="secondary">收款人姓名和账号已锁定</Typography.Text>
|
||||
</Space>
|
||||
))}
|
||||
</Space>
|
||||
|
||||
Reference in New Issue
Block a user