diff --git a/apps/frontend/src/layouts/WorkerLayout.tsx b/apps/frontend/src/layouts/WorkerLayout.tsx index a085a91f..c7adea38 100644 --- a/apps/frontend/src/layouts/WorkerLayout.tsx +++ b/apps/frontend/src/layouts/WorkerLayout.tsx @@ -6,12 +6,12 @@ import { UserOutlined, } from '@ant-design/icons' import { App, Button, Layout, Menu, Typography } from 'antd' -import { useQueryClient } from '@tanstack/react-query' +import { useQuery, useQueryClient } from '@tanstack/react-query' import type { MenuProps } from 'antd' -import { useEffect } from 'react' +import { useEffect, useMemo } from 'react' import { Outlet, useLocation, useNavigate } from 'react-router' -import { logoutWorker } from '@/services/worker' +import { fetchWorkerAfterSalesCases, fetchWorkerProfile, logoutWorker } from '@/services/worker' import { useIsMobile } from '@/utils/use-is-mobile' import { clearWorkerSession, @@ -50,6 +50,45 @@ export default function WorkerLayout() { const status = getWorkerStatus() const currentKey = resolveSelectedKey(location.pathname) + const profileQuery = useQuery({ + queryKey: ['worker-profile'], + queryFn: () => fetchWorkerProfile(), + retry: false, + staleTime: 10_000, + }) + const worker = profileQuery.data?.data.worker + + const afterSalesQuery = useQuery({ + queryKey: ['worker-after-sales-cases'], + queryFn: fetchWorkerAfterSalesCases, + retry: false, + staleTime: 10_000, + }) + const afterSalesPendingCount = (afterSalesQuery.data?.data.items || []).filter( + (item) => item.status === 'awaiting_worker_response', + ).length + + const dynamicMenuItems: MenuProps['items'] = useMemo( + () => [ + { key: '/hall', icon: , label: '抢单大厅' }, + { key: '/orders', icon: , label: '我的订单' }, + { + key: '/orders/after-sales', + icon: , + label: ( + + 售后问题单 + {afterSalesPendingCount > 0 ? ( + {afterSalesPendingCount} + ) : null} + + ), + }, + { key: '/profile', icon: , label: '个人中心' }, + ], + [afterSalesPendingCount], + ) + useEffect(() => { const navigateToLogin = () => { if (window.location.pathname !== '/') { @@ -170,29 +209,62 @@ export default function WorkerLayout() { return ( - +
-
-
- 王大锤 - 接单平台 +
+
+ 锤 +
+
+ 王大锤电竞 + + + 打手工作台 +
-
- {username} - {formatWorkerStatus(status)} + +
+
+
+ {(worker?.displayName || username).slice(0, 1)} +
+
+ + {worker?.displayName || username} + + ● {formatWorkerStatus(status)} +
+
+ {worker?.level?.name ? ( +
+ {worker.level.name} + {worker.wallet ? ( + + ¥{(Number(worker.wallet.availableAmount || 0) / 100).toFixed(2)} + + ) : null} +
+ ) : null}
+ navigate(String(key))} /> -
+ +
-
diff --git a/apps/frontend/src/pages/worker/WorkerAfterSalesPage.tsx b/apps/frontend/src/pages/worker/WorkerAfterSalesPage.tsx index 4ed8c964..4a4b8aa8 100644 --- a/apps/frontend/src/pages/worker/WorkerAfterSalesPage.tsx +++ b/apps/frontend/src/pages/worker/WorkerAfterSalesPage.tsx @@ -148,32 +148,52 @@ export default function WorkerAfterSalesPage() { message={`你有 ${pendingCount} 条售后单待提交说明,请尽快处理,逾期可能影响结算与等级`} /> ) : null} -
- ({ - value: filter.value, - label: ( - - {filter.label} - {countByFilter[filter.value] > 0 ? ( - - {countByFilter[filter.value]} - - ) : null} - - ), - }))} - value={statusFilter} - onChange={(value) => setStatusFilter(value as StatusFilter)} - block={isMobile} - /> - +
+
+ + 售后问题单 + + {pendingCount > 0 ? ( + + + {pendingCount} 条待提交说明 + + ) : ( + 无待办售后单 + )} +
+ +
+ ({ + value: filter.value, + label: ( + + {filter.label} + {countByFilter[filter.value] > 0 ? ( + + {countByFilter[filter.value]} + + ) : null} + + ), + }))} + value={statusFilter} + onChange={(value) => setStatusFilter(value as StatusFilter)} + block={isMobile} + /> +
+ +
+ +
{afterSalesCasesQuery.isLoading ? ( diff --git a/apps/frontend/src/pages/worker/WorkerOrderDesktopList.tsx b/apps/frontend/src/pages/worker/WorkerOrderDesktopList.tsx index a3269f28..81aba9dc 100644 --- a/apps/frontend/src/pages/worker/WorkerOrderDesktopList.tsx +++ b/apps/frontend/src/pages/worker/WorkerOrderDesktopList.tsx @@ -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 (
-
- 我的订单 - + {/* 桌面端全新整合头部 */} +
+
+ + 我的订单 + + + + 在办工单实时监控 + +
+ +
+ + } + onChange={(event) => onKeywordInputChange(event.target.value)} + onPressEnter={onKeywordSearch} + /> + + +
+ +
+ +
- + {/* 4 栏工单态势 KPI 仪表盘 */} +
+ {/* 卡片 1: 代练中 */} +
onStatusChange(status === 'in_progress' ? '' : 'in_progress')} + > +
+
+ +
+
+ 代练进行中 + + {inProgressCount} + 单 + +
+
+
+ 待结算预估: + {formatMoney(inProgressSettlement)} +
+
+ + {/* 卡片 2: 待验收 */} +
+ onStatusChange(status === 'pending_acceptance' ? '' : 'pending_acceptance') + } + > +
+
+ +
+
+ 已提交待验收 + + {pendingCount} + 单 + +
+
+
+ 待审核报酬: + {formatMoney(pendingSettlement)} +
+
+ + {/* 卡片 3: 异常单 */} +
onStatusChange(status === 'problem' ? '' : 'problem')} + > +
+
+ +
+
+ 异常与问题工单 + + {abnormalTotal} + 单 + +
+
+
+ + 超时 {openCount} · 补图 {evidenceCount} · 问题 {problemCount} + +
+
+ + {/* 卡片 4: 已验收 */} +
onStatusChange(status === 'accepted' ? '' : 'accepted')} + > +
+
+ +
+
+ 已验收完结 + + {acceptedCount} + 单 + +
+
+
+ 已结算到账: + {formatMoney(acceptedSettlement)} +
+
+
+ + onStatusChange(nextKey === 'all' ? '' : nextKey)} /> - - } - onChange={(event) => onKeywordInputChange(event.target.value)} - onPressEnter={onKeywordSearch} - /> - - - rowKey="workOrderId" - size="small" + size="middle" loading={loading} dataSource={orders} columns={columns} + className="worker-orders-table" locale={{ emptyText: error ? ( @@ -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 }} diff --git a/apps/frontend/src/pages/worker/WorkerProfileDesktopSections.tsx b/apps/frontend/src/pages/worker/WorkerProfileDesktopSections.tsx index 21404edc..9ea796f4 100644 --- a/apps/frontend/src/pages/worker/WorkerProfileDesktopSections.tsx +++ b/apps/frontend/src/pages/worker/WorkerProfileDesktopSections.tsx @@ -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 ( - - - {resolveWorkerInitial(worker)} - - - {worker.displayName || worker.username} - - 账号:{worker.username} - 手机号:{worker.phone || '-'} - - - - {formatWorkerStatus(worker.status)} - - - {worker.workerType === 'internal' ? '内部打手' : '外部打手'} - - {worker.level?.name || '未分级'} - - - - - 我的邀请码: - {worker.inviteCode || '-'} - - - {levelProgress && levelProgress.nextThreshold !== null ? ( - - - 已累计完成 {summary?.acceptedOrderCount || 0} 单,距下一等级还需{' '} - {Math.max(0, levelProgress.nextThreshold - (summary?.acceptedOrderCount || 0))} 单 - - `${percent}%`} - /> - - ) : ( - - 已累计完成 {summary?.acceptedOrderCount || 0} 单,当前已是最高等级 - - )} - - +
+
+
+
+ {(worker.displayName || worker.username).slice(0, 1)} +
+
+
+ + {worker.displayName || worker.username} + + + {formatWorkerStatus(worker.status)} + + {worker.level?.name ? ( + {worker.level.name} + ) : null} +
+
+ 账号: {worker.username} + · + UID: {worker.workerId} + {worker.phone ? ( + <> + · + 手机: {worker.phone} + + ) : null} + {worker.inviteCode ? ( + <> + · + + 邀请码: {worker.inviteCode} + {onCopyInviteCode ? ( +
+
+
+ +
+
+ 免押额度 + {formatMoney(worker.effectiveDepositFreeAmount)} +
+
+ 最大接单 + + {worker.level?.permissions.maxActiveOrders || 0} 单 + +
+
+
+ +
+
+ + 已完成 {summary?.acceptedOrderCount || 0} 单 + + {levelProgress && levelProgress.nextThreshold ? ( + 距下一级还需 {nextRequired} 单 + ) : ( + 当前已是最高等级 + )} +
+ +
+
) } 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: , + cls: 'kpi-wallet', + }, + { + key: 'frozen', + label: '冻结押金', + amount: worker.wallet.frozenDepositAmount, + note: '抢单后暂扣,验收后解冻', + icon: , + cls: 'kpi-quota', + }, + { + key: 'pending', + label: '待解冻金额', + amount: worker.wallet.pendingUnfreezeAmount, + note: '按配置天数释放到可用余额', + icon: , + cls: 'kpi-tasks', + }, + { + key: 'settled', + label: '累计结算收益', + amount: worker.wallet.totalSettledAmount, + note: '已结算入账的报酬合计', + icon: , + cls: 'kpi-pool', + }, + ] + return ( - -
- {items.map(([label, amount, note]) => ( -
- {label} - {formatMoney(amount)} - {note} +
+ {items.map((item) => ( +
+
+
{item.icon}
+
+ {item.label} + {formatMoney(item.amount)} +
- ))} -
- +
+ {item.note} +
+
+ ))} +
) } @@ -152,60 +220,77 @@ export function WorkerProfileDesktopActions({ onLogout: () => void }) { return ( - - - - - {canAddWithdrawalAccount ? ( - - ) : null} - {needsAlipayQrCode ? ( - - ) : null} - - - - - - - - + +
+
+ 资金管理 + + + + {canAddWithdrawalAccount ? ( + + ) : null} + {needsAlipayQrCode ? ( + + ) : null} + +
+ +
+ 安全与设置 + + + + + + + +
+ +
+ 其他服务 + + + + +
+
) } @@ -226,7 +311,7 @@ export function WorkerProfileDesktopAccountDetails({ onAddWithdrawalAccount: () => void }) { return ( - + {worker.username} {worker.displayName || '-'} @@ -260,17 +345,16 @@ export function WorkerProfileDesktopAccountDetails({ {withdrawalAccounts.length > 0 ? ( - + {withdrawalAccounts.map((account) => ( - - {formatWithdrawChannel(account.accountChannel)} - {account.accountName} + + {formatWithdrawChannel(account.accountChannel)} + {account.accountName} {account.accountChannel === 'wechat' - ? '已上传收款二维码' - : `${account.accountNoMasked} / ${account.alipayQrCodeImage ? '已上传收款二维码' : '未上传收款二维码'}`} + ? '已上传收款码' + : `${account.accountNoMasked} / ${account.alipayQrCodeImage ? '已上传收款码' : '未上传收款码'}`} - 收款人姓名和账号已锁定 ))} diff --git a/apps/frontend/src/styles/worker.css b/apps/frontend/src/styles/worker.css index 58efeac1..c15fcff1 100644 --- a/apps/frontend/src/styles/worker.css +++ b/apps/frontend/src/styles/worker.css @@ -2890,3 +2890,839 @@ body { .worker-after-sales-mobile .worker-after-sales-toolbar .ant-segmented { overflow-x: auto; } + +/* ========================================================================== + 接单平台全局视觉升级 (Global Worker Platform UI Upgrade) + ========================================================================== */ + +/* ===== 打手工作台专属侧边栏 ===== */ +.worker-shell.admin-shell { + background: #f8fafc; +} + +.worker-sider { + background: #ffffff !important; + border-right: 1px solid #e2e8f0 !important; + box-shadow: 2px 0 12px rgba(15, 23, 42, 0.03) !important; +} + +.worker-sider-brand { + display: flex; + align-items: center; + gap: 10px; + padding: 16px 16px 12px 16px; + border-bottom: 1px solid #f1f5f9; +} + +.worker-sider-brand-logo { + width: 36px; + height: 36px; + border-radius: 10px; + background: linear-gradient(135deg, #ff6a00 0%, #ff8e3c 100%); + display: flex; + align-items: center; + justify-content: center; + color: #ffffff; + font-weight: 800; + font-size: 18px; + box-shadow: 0 4px 10px rgba(255, 106, 0, 0.28); + flex-shrink: 0; +} + +.worker-sider-brand-text { + display: flex; + flex-direction: column; + min-width: 0; +} + +.worker-sider-brand-text strong { + font-size: 15px; + font-weight: 700; + color: #0f172a; + line-height: 1.2; + letter-spacing: -0.01em; +} + +.worker-sider-brand-sub { + display: inline-flex; + align-items: center; + gap: 5px; + font-size: 11px; + color: #059669; + font-weight: 500; + margin-top: 2px; +} + +.worker-sider-pulse-dot { + width: 6px; + height: 6px; + border-radius: 50%; + background: #10b981; + box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7); + animation: worker-hall-pulse 2s infinite cubic-bezier(0.4, 0, 0.6, 1); +} + +.worker-sider-profile-card { + margin: 12px 12px 8px 12px; + padding: 10px 12px; + background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%); + border: 1px solid #e2e8f0; + border-radius: 10px; + display: flex; + flex-direction: column; + gap: 8px; +} + +.worker-sider-profile-top { + display: flex; + align-items: center; + gap: 8px; +} + +.worker-sider-avatar { + width: 32px; + height: 32px; + border-radius: 8px; + background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); + color: #ffffff; + font-weight: 700; + font-size: 14px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + text-transform: uppercase; +} + +.worker-sider-user-meta { + display: flex; + flex-direction: column; + min-width: 0; + flex: 1; +} + +.worker-sider-username { + font-size: 13px; + font-weight: 600; + color: #0f172a; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + line-height: 1.2; +} + +.worker-sider-status-badge { + font-size: 10px; + color: #059669; + display: inline-block; + line-height: 1.2; + margin-top: 1px; +} + +.worker-sider-level-pill { + display: flex; + align-items: center; + justify-content: space-between; + padding: 3px 8px; + background: #ffffff; + border: 1px solid #fed7aa; + border-radius: 6px; + font-size: 11px; +} + +.worker-sider-level-text { + color: #ea580c; + font-weight: 600; +} + +.worker-sider-balance-text { + color: #059669; + font-weight: 600; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; +} + +/* 侧边栏菜单定制 */ +.worker-sider .ant-menu { + background: transparent !important; + border-inline-end: none !important; + padding: 4px 8px; +} + +.worker-sider .ant-menu-item { + border-radius: 8px !important; + margin-block: 4px !important; + font-size: 13px !important; + font-weight: 500 !important; + color: #475569 !important; + height: 40px !important; + line-height: 40px !important; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important; +} + +.worker-sider .ant-menu-item:hover { + background: #f1f5f9 !important; + color: #0f172a !important; +} + +.worker-sider .ant-menu-item-selected { + background: #fff7ed !important; + color: var(--theme-primary) !important; + font-weight: 600 !important; + box-shadow: inset 3px 0 0 var(--theme-primary); +} + +.worker-sider .ant-menu-item-selected .anticon { + color: var(--theme-primary) !important; +} + +.worker-menu-label-with-badge { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; +} + +.worker-menu-badge { + background: #ef4444; + color: #ffffff; + font-size: 11px; + font-weight: 600; + padding: 0 6px; + border-radius: 9999px; + line-height: 16px; + height: 16px; + box-shadow: 0 1px 3px rgba(239, 68, 68, 0.35); +} + +.worker-sider-footer { + margin-top: auto; + padding: 12px; + border-top: 1px solid #f1f5f9; + display: flex; + flex-direction: column; + gap: 8px; +} + +.worker-sider-logout-btn { + border-color: #fee2e2 !important; + color: #dc2626 !important; + background: #fff5f5 !important; + border-radius: 8px !important; + font-size: 12px !important; + font-weight: 500 !important; + transition: all 0.2s ease !important; +} + +.worker-sider-logout-btn:hover { + background: #fee2e2 !important; + border-color: #fca5a5 !important; +} + +/* ===== 我的订单 桌面端头部与 4 栏 KPI 看板 ===== */ +.worker-orders-desktop-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 16px; + background: #ffffff; + padding: 14px 20px; + border-radius: 12px; + border: 1px solid #e2e8f0; + box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); +} + +.worker-orders-header-left { + display: flex; + align-items: center; + gap: 10px; + flex-shrink: 0; +} + +.worker-orders-heading.ant-typography { + margin: 0 !important; + font-size: 20px !important; + font-weight: 700 !important; + color: #0f172a !important; + letter-spacing: -0.02em; +} + +.worker-orders-live-badge { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 12px; + font-weight: 500; + color: #2563eb; + background: #eff6ff; + border: 1px solid #bfdbfe; + padding: 2px 10px; + border-radius: 9999px; +} + +.worker-orders-pulse-dot { + width: 7px; + height: 7px; + border-radius: 50%; + background: #3b82f6; + box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7); + animation: worker-order-pulse 2s infinite cubic-bezier(0.4, 0, 0.6, 1); +} + +@keyframes worker-order-pulse { + 0% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7); + } + 70% { + transform: scale(1); + box-shadow: 0 0 0 6px rgba(59, 130, 246, 0); + } + 100% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); + } +} + +.worker-orders-header-center { + flex: 1; + max-width: 520px; + min-width: 260px; +} + +.worker-orders-desktop-search { + width: 100%; +} + +.worker-orders-desktop-search .ant-input { + border-radius: 8px 0 0 8px; + padding: 6px 14px; + border-color: #cbd5e1; +} + +.worker-orders-desktop-search .ant-input:hover, +.worker-orders-desktop-search .ant-input:focus { + border-color: var(--theme-primary); +} + +.worker-orders-desktop-search .ant-btn { + border-radius: 0 8px 8px 0; + padding: 6px 18px; + height: auto; + font-weight: 500; +} + +.worker-orders-header-right { + display: flex; + align-items: center; + gap: 10px; + flex-shrink: 0; +} + +/* 订单 4 栏 KPI 网格 */ +.worker-orders-kpi-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 14px; + margin-bottom: 16px; +} + +@media (max-width: 1200px) { + .worker-orders-kpi-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 640px) { + .worker-orders-kpi-grid { + grid-template-columns: 1fr; + } +} + +.worker-orders-kpi-card { + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 12px; + padding: 14px 16px; + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 10px; + cursor: pointer; + user-select: none; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 1px 3px rgba(15, 23, 42, 0.03); +} + +.worker-orders-kpi-card:hover { + border-color: #cbd5e1; + box-shadow: 0 6px 16px rgba(15, 23, 42, 0.06); + transform: translateY(-2px); +} + +.worker-orders-kpi-card.active { + border-color: var(--theme-primary); + background: linear-gradient(180deg, #fffbf7 0%, #ffffff 100%); + box-shadow: 0 4px 14px rgba(255, 106, 0, 0.16); + transform: translateY(-2px); +} + +.worker-orders-kpi-top { + display: flex; + align-items: center; + gap: 12px; +} + +.worker-orders-kpi-icon-wrap { + width: 40px; + height: 40px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; +} + +.worker-orders-kpi-icon-wrap.in-progress { + background: #eff6ff; + color: #2563eb; +} + +.worker-orders-kpi-icon-wrap.pending { + background: #faf5ff; + color: #9333ea; +} + +.worker-orders-kpi-icon-wrap.problem { + background: #fef2f2; + color: #dc2626; +} + +.worker-orders-kpi-icon-wrap.accepted { + background: #ecfdf5; + color: #059669; +} + +.worker-orders-kpi-info { + display: flex; + flex-direction: column; + gap: 1px; + min-width: 0; +} + +.worker-orders-kpi-title { + font-size: 12px; + color: #64748b; + font-weight: 500; +} + +.worker-orders-kpi-val { + font-size: 20px; + font-weight: 700; + color: #0f172a; + line-height: 1.2; +} + +.worker-orders-kpi-unit { + font-size: 12px; + font-weight: 400; + color: #64748b; + margin-left: 2px; +} + +.worker-orders-kpi-bottom { + display: flex; + align-items: center; + gap: 6px; + padding-top: 8px; + border-top: 1px solid #f1f5f9; + font-size: 12px; +} + +.worker-orders-kpi-sub-label { + color: #64748b; +} + +.worker-orders-kpi-sub-val { + color: #0f172a; + font-weight: 600; +} + +.worker-orders-card .ant-card-body { + padding: 16px; +} + +.worker-orders-table .ant-table-thead > tr > th { + background: #f8fafc; + color: #475569; + font-weight: 600; + font-size: 13px; +} + +/* ===== 售后问题单 桌面端头部 ===== */ +.worker-after-sales-desktop-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 16px; + background: #ffffff; + padding: 14px 20px; + border-radius: 12px; + border: 1px solid #e2e8f0; + box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); +} + +.worker-after-sales-header-left { + display: flex; + align-items: center; + gap: 10px; + flex-shrink: 0; +} + +.worker-after-sales-heading.ant-typography { + margin: 0 !important; + font-size: 20px !important; + font-weight: 700 !important; + color: #0f172a !important; + letter-spacing: -0.02em; +} + +.worker-after-sales-alert-pill { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 12px; + font-weight: 600; + color: #dc2626; + background: #fef2f2; + border: 1px solid #fecaca; + padding: 2px 10px; + border-radius: 9999px; +} + +.worker-after-sales-pulse-dot { + width: 7px; + height: 7px; + border-radius: 50%; + background: #ef4444; + box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); + animation: worker-after-sales-pulse 2s infinite cubic-bezier(0.4, 0, 0.6, 1); +} + +@keyframes worker-after-sales-pulse { + 0% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); + } + 70% { + transform: scale(1); + box-shadow: 0 0 0 6px rgba(239, 68, 68, 0); + } + 100% { + transform: scale(0.95); + box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); + } +} + +.worker-after-sales-normal-pill { + display: inline-flex; + align-items: center; + font-size: 12px; + font-weight: 500; + color: #059669; + background: #ecfdf5; + border: 1px solid #a7f3d0; + padding: 2px 10px; + border-radius: 9999px; +} + +.worker-after-sales-header-center { + flex: 1; + max-width: 480px; +} + +.worker-after-sales-segmented { + background: #f1f5f9; + padding: 3px; + border-radius: 8px; +} + +.worker-after-sales-header-right { + flex-shrink: 0; +} + +/* ===== 个人中心 桌面端 Hero 与 钱包网格 ===== */ +.worker-profile-hero-card { + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 14px; + padding: 18px 20px; + margin-bottom: 16px; + box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04); + display: flex; + flex-direction: column; + gap: 16px; +} + +.worker-profile-hero-top { + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + flex-wrap: wrap; +} + +.worker-profile-hero-user { + display: flex; + align-items: center; + gap: 14px; +} + +.worker-profile-avatar { + width: 52px; + height: 52px; + border-radius: 14px; + background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%); + color: #ffffff; + font-size: 22px; + font-weight: 800; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 4px 14px rgba(99, 102, 241, 0.35); + flex-shrink: 0; + text-transform: uppercase; +} + +.worker-profile-user-info { + display: flex; + flex-direction: column; + gap: 4px; +} + +.worker-profile-name-row { + display: flex; + align-items: center; + gap: 8px; +} + +.worker-profile-name { + font-size: 18px; + font-weight: 700; + color: #0f172a; +} + +.worker-profile-status-tag { + margin: 0 !important; + font-size: 11px; +} + +.worker-profile-level-badge { + font-size: 11px; + padding: 1px 8px; + border-radius: 9999px; + background: #fff7ed; + color: #c2410c; + border: 1px solid #fed7aa; + font-weight: 600; +} + +.worker-profile-meta-row { + display: flex; + align-items: center; + gap: 6px; + font-size: 12px; + color: #64748b; +} + +.worker-profile-meta-divider { + color: #cbd5e1; +} + +.worker-profile-privileges { + display: flex; + align-items: center; + gap: 10px; +} + +.worker-profile-priv-chip { + display: flex; + flex-direction: column; + padding: 6px 14px; + background: #f8fafc; + border: 1px solid #e2e8f0; + border-radius: 8px; +} + +.worker-profile-priv-chip .priv-label { + font-size: 11px; + color: #64748b; +} + +.worker-profile-priv-chip .priv-val { + font-size: 14px; + font-weight: 700; + color: #0f172a; +} + +.worker-profile-progress-bar-wrap { + display: flex; + flex-direction: column; + gap: 6px; + padding-top: 12px; + border-top: 1px solid #f1f5f9; +} + +.worker-profile-progress-header { + display: flex; + align-items: center; + justify-content: space-between; + font-size: 12px; +} + +.worker-profile-progress-title { + color: #334155; +} + +.worker-profile-progress-sub { + color: #94a3b8; +} + +/* 个人中心 4 栏资产网格 */ +.worker-profile-wallet-grid { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 14px; + margin-bottom: 16px; +} + +@media (max-width: 1200px) { + .worker-profile-wallet-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 640px) { + .worker-profile-wallet-grid { + grid-template-columns: 1fr; + } +} + +.worker-profile-wallet-kpi-card { + background: #ffffff; + border: 1px solid #e2e8f0; + border-radius: 12px; + padding: 14px 16px; + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 10px; + transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 1px 3px rgba(15, 23, 42, 0.03); +} + +.worker-profile-wallet-kpi-card:hover { + border-color: #cbd5e1; + box-shadow: 0 6px 16px rgba(15, 23, 42, 0.06); + transform: translateY(-2px); +} + +.worker-profile-kpi-top { + display: flex; + align-items: center; + gap: 12px; +} + +.worker-profile-kpi-icon { + width: 40px; + height: 40px; + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + flex-shrink: 0; +} + +.worker-profile-kpi-icon.kpi-wallet { + background: #fff7ed; + color: #ea580c; +} + +.worker-profile-kpi-icon.kpi-quota { + background: #eff6ff; + color: #2563eb; +} + +.worker-profile-kpi-icon.kpi-tasks { + background: #faf5ff; + color: #9333ea; +} + +.worker-profile-kpi-icon.kpi-pool { + background: #ecfdf5; + color: #059669; +} + +.worker-profile-kpi-info { + display: flex; + flex-direction: column; + gap: 1px; + min-width: 0; +} + +.worker-profile-kpi-label { + font-size: 12px; + color: #64748b; + font-weight: 500; +} + +.worker-profile-kpi-val { + font-size: 20px; + font-weight: 700; + color: #0f172a; + line-height: 1.2; +} + +.worker-profile-kpi-bottom { + padding-top: 8px; + border-top: 1px solid #f1f5f9; + font-size: 11px; + color: #94a3b8; +} + +/* 分组操作卡片 */ +.worker-profile-actions-card { + margin-bottom: 16px; + border-radius: 12px; +} + +.worker-profile-actions-grouped { + display: flex; + flex-direction: column; + gap: 14px; +} + +.worker-profile-actions-group { + display: flex; + align-items: center; + gap: 16px; + padding: 8px 12px; + background: #f8fafc; + border: 1px solid #f1f5f9; + border-radius: 8px; + flex-wrap: wrap; +} + +.worker-profile-group-title { + font-size: 12px; + font-weight: 600; + color: #475569; + min-width: 70px; +} + +.worker-profile-details-card { + border-radius: 12px; +}