feat(worker): 彻底重构售后问题单与工单仲裁中心视觉与交互体验

This commit is contained in:
yml2213
2026-09-12 19:16:12 +08:00
parent 402c10e9b6
commit fb08f4552e
2 changed files with 1178 additions and 311 deletions
@@ -1,4 +1,16 @@
import { ReloadOutlined } from '@ant-design/icons'
import {
AlertOutlined,
AuditOutlined,
CheckCircleFilled,
CheckCircleOutlined,
ClockCircleOutlined,
CloseCircleOutlined,
DollarCircleOutlined,
FormOutlined,
ReloadOutlined,
SafetyCertificateOutlined,
UserOutlined,
} from '@ant-design/icons'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import {
App,
@@ -122,6 +134,7 @@ export default function WorkerAfterSalesPage() {
}
return counts
}, [allCases])
const afterSalesCases = useMemo(
() =>
allCases
@@ -140,27 +153,109 @@ export default function WorkerAfterSalesPage() {
isMobile ? 'worker-after-sales-page worker-after-sales-mobile' : 'worker-after-sales-page'
}
>
{/* 待办紧急提醒横幅 */}
{pendingCount > 0 ? (
<Alert
className="worker-after-sales-reminder"
type="warning"
showIcon
message={`你有 ${pendingCount} 条售后单待提交说明,请尽快处理,逾期可能影响结算与等级`}
/>
<div className="worker-as-pending-alert">
<div className="worker-as-alert-left">
<span className="worker-as-alert-icon-wrap">
<AlertOutlined />
</span>
<div className="worker-as-alert-text">
<div className="worker-as-alert-title">
您有 <strong>{pendingCount}</strong> 笔售后单待提交说明
</div>
<div className="worker-as-alert-desc">
买家已发起售后投诉,请尽快提交完成凭据或情况答辩,逾期可能影响您的资金与接单等级。
</div>
</div>
</div>
<Button
type="primary"
className="worker-as-alert-btn"
onClick={() => setStatusFilter('awaiting_worker_response')}
>
立即处理待办 ({pendingCount})
</Button>
</div>
) : null}
{/* 4卡片争议处理概览网格 */}
<div className="worker-as-kpi-grid">
<div
className={`worker-as-kpi-card is-awaiting ${statusFilter === 'awaiting_worker_response' ? 'is-active' : ''}`}
onClick={() =>
setStatusFilter(
statusFilter === 'awaiting_worker_response' ? 'all' : 'awaiting_worker_response',
)
}
>
<div className="worker-as-kpi-icon-wrap amber">
<AlertOutlined />
</div>
<div className="worker-as-kpi-info">
<div className="worker-as-kpi-label">
待我举证
{pendingCount > 0 && <span className="worker-as-kpi-pulse-dot" />}
</div>
<div className="worker-as-kpi-value">{countByFilter.awaiting_worker_response || 0}</div>
<div className="worker-as-kpi-sub">
{pendingCount > 0 ? `${pendingCount} 笔需紧急说明` : '暂无待处理'}
</div>
</div>
</div>
<div
className={`worker-as-kpi-card is-review ${statusFilter === 'under_review' ? 'is-active' : ''}`}
onClick={() => setStatusFilter(statusFilter === 'under_review' ? 'all' : 'under_review')}
>
<div className="worker-as-kpi-icon-wrap blue">
<AuditOutlined />
</div>
<div className="worker-as-kpi-info">
<div className="worker-as-kpi-label">平台仲裁中</div>
<div className="worker-as-kpi-value">{countByFilter.under_review || 0}</div>
<div className="worker-as-kpi-sub">客服公正审核中</div>
</div>
</div>
<div
className={`worker-as-kpi-card is-recovery ${statusFilter === 'recovery_pending' ? 'is-active' : ''}`}
onClick={() =>
setStatusFilter(statusFilter === 'recovery_pending' ? 'all' : 'recovery_pending')
}
>
<div className="worker-as-kpi-icon-wrap rose">
<DollarCircleOutlined />
</div>
<div className="worker-as-kpi-info">
<div className="worker-as-kpi-label">待追缴/扣款</div>
<div className="worker-as-kpi-value">{countByFilter.recovery_pending || 0}</div>
<div className="worker-as-kpi-sub">存在待追缴或抵扣款</div>
</div>
</div>
<div
className={`worker-as-kpi-card is-closed ${statusFilter === 'closed' ? 'is-active' : ''}`}
onClick={() => setStatusFilter(statusFilter === 'closed' ? 'all' : 'closed')}
>
<div className="worker-as-kpi-icon-wrap emerald">
<CheckCircleFilled />
</div>
<div className="worker-as-kpi-info">
<div className="worker-as-kpi-label">已结案归档</div>
<div className="worker-as-kpi-value">{countByFilter.closed || 0}</div>
<div className="worker-as-kpi-sub">历史完结记录</div>
</div>
</div>
</div>
{/* 桌面端与移动端顶部筛选与控制栏 */}
<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>
)}
<span className="worker-as-total-badge">共 {allCases.length} 笔工单记录</span>
</div>
<div className="worker-after-sales-header-center">
@@ -197,7 +292,9 @@ export default function WorkerAfterSalesPage() {
</div>
{afterSalesCasesQuery.isLoading ? (
<Typography.Text type="secondary">加载中…</Typography.Text>
<div className="worker-as-loading-state">
<Typography.Text type="secondary">正在加载售后问题单…</Typography.Text>
</div>
) : afterSalesCasesQuery.isError ? (
<Alert
type="error"
@@ -206,10 +303,12 @@ export default function WorkerAfterSalesPage() {
description="请刷新后重试;若问题持续存在,请联系客服。"
/>
) : afterSalesCases.length === 0 ? (
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
description="暂无售后问题单,请保持验收资料真实完整"
/>
<div className="worker-as-empty-card">
<Empty
image={Empty.PRESENTED_IMAGE_SIMPLE}
description="暂无匹配的售后问题单,请保持验收资料真实完整"
/>
</div>
) : (
<div className="worker-after-sales-list">
{afterSalesCases.map((item) => (
@@ -260,180 +359,294 @@ function WorkerAfterSalesItem({
Number(item.recoveryAmount || 0) > 0 ||
Number(item.pendingDepositDeductionAmount || 0) > 0 ||
Number(item.availableDeductionAmount || 0) > 0 ||
Number(item.debtAmount || 0) > 0
Number(item.debtAmount || 0) > 0 ||
Number(item.refundedAmount || 0) > 0
return (
<>
<article
className={`worker-after-sales-item${item.status === 'awaiting_worker_response' ? ' is-awaiting' : ''}`}
>
<div className="worker-after-sales-item-head">
<Space size={6} wrap>
<Tag color={resolveWorkerAfterSalesStatusColor(item.status)}>
<article className={`worker-as-case-card is-status-${item.status}`}>
{/* 卡片头部:状态、问题类型、工单标题、订单单号 */}
<div className="worker-as-card-header">
<div className="worker-as-card-header-left">
<span className={`worker-as-status-badge is-${item.status}`}>
<span className="worker-as-status-dot" />
{formatWorkerAfterSalesStatus(item.status)}
</Tag>
<Tag color={resolveWorkerAfterSalesProblemTypeColor(item.problemType)}>
</span>
<Tag
color={resolveWorkerAfterSalesProblemTypeColor(item.problemType)}
className="worker-as-problem-tag"
>
{formatWorkerAfterSalesProblemType(item.problemType)}
</Tag>
</Space>
<Typography.Text strong ellipsis={{ tooltip: item.productName || item.caseNo }}>
{item.productName || item.caseNo}
</Typography.Text>
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
发起 {formatDateTime(item.createdAt)}
</Typography.Text>
</div>
<Space size={12} wrap className="worker-after-sales-item-meta">
<Typography.Text
type="secondary"
style={{ fontSize: 12 }}
copyable={{ text: item.workOrderNo || item.caseNo }}
>
工单号 {item.workOrderNo || item.caseNo}
</Typography.Text>
<Typography.Text
type="secondary"
style={{ fontSize: 12 }}
copyable={{ text: item.caseNo }}
>
售后单号 {item.caseNo}
</Typography.Text>
{item.shareQuantity ? (
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
拼单 {item.shareQuantity} 份
<Typography.Text
strong
className="worker-as-product-title"
ellipsis={{ tooltip: item.productName || item.caseNo }}
>
{item.productName || item.caseNo}
</Typography.Text>
) : null}
</Space>
<div className="worker-after-sales-block">
<div className="worker-after-sales-block-label">投诉内容</div>
<Typography.Paragraph style={{ marginBottom: item.complaintFiles.length ? 8 : 0 }}>
{item.complaintNote || '-'}
</Typography.Paragraph>
{item.complaintFiles.length > 0 ? (
<ImagePreviewList files={item.complaintFiles} size={48} maxVisible={5} />
) : null}
</div>
{item.workerResponse || item.workerResponseFiles.length > 0 ? (
<div className="worker-after-sales-block is-mine">
<div className="worker-after-sales-block-label">
我提交的说明{item.respondedAt ? ` · ${formatDateTime(item.respondedAt)}` : ''}
</div>
{item.workerResponse ? (
<Typography.Paragraph
style={{ marginBottom: item.workerResponseFiles.length ? 8 : 0 }}
>
{item.workerResponse}
</Typography.Paragraph>
) : null}
{item.workerResponseFiles.length > 0 ? (
<ImagePreviewList files={item.workerResponseFiles} size={48} maxVisible={5} />
{item.shareQuantity ? (
<Tag className="worker-as-share-tag">拼单 {item.shareQuantity} 份</Tag>
) : null}
</div>
) : null}
{resolutionLabel || item.resolutionNote || hasRecoveryDetail ? (
<div className="worker-after-sales-block is-result">
<div className="worker-after-sales-block-label">处理结果</div>
<Space direction="vertical" size={6} style={{ width: '100%' }}>
{resolutionLabel ? (
<Space size={8} wrap>
<Tag color={resolveWorkerAfterSalesResolutionColor(item.resolutionAction)}>
{resolutionLabel}
</Tag>
{item.resolvedAt ? (
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
{formatDateTime(item.resolvedAt)}
</Typography.Text>
) : null}
</Space>
) : null}
{item.resolutionNote ? (
<Typography.Paragraph style={{ marginBottom: 0 }}>
{item.resolutionNote}
</Typography.Paragraph>
) : null}
{hasRecoveryDetail ? (
<Space size={12} wrap>
{Number(item.recoveryAmount || 0) > 0 ? (
<Typography.Text type="danger" strong>
追缴 {formatMoney(item.recoveryAmount)}
</Typography.Text>
) : null}
{Number(item.pendingDepositDeductionAmount || 0) > 0 ? (
<Typography.Text type="secondary">
待解冻资金抵扣 {formatMoney(item.pendingDepositDeductionAmount)}
</Typography.Text>
) : null}
{Number(item.availableDeductionAmount || 0) > 0 ? (
<Typography.Text type="secondary">
余额抵扣 {formatMoney(item.availableDeductionAmount)}
</Typography.Text>
) : null}
{Number(item.debtAmount || 0) > 0 ? (
<Typography.Text type="danger" strong>
待追缴 {formatMoney(item.debtAmount)},后续验收报酬将自动抵扣
</Typography.Text>
) : null}
{Number(item.refundedAmount || 0) > 0 ? (
<Typography.Text type="success" strong>
已退还追缴 {formatMoney(item.refundedAmount)} 至可用余额
</Typography.Text>
) : null}
</Space>
) : null}
</Space>
</div>
) : item.status === 'under_review' ? (
<Typography.Text type="secondary">已提交说明,等待客服处理</Typography.Text>
) : null}
{item.status === 'awaiting_worker_response' ? (
<div className="worker-after-sales-item-actions">
<Button
type="primary"
onClick={() => {
setFiles([])
form.resetFields()
setResponseOpen(true)
<div className="worker-as-card-header-right">
<span className="worker-as-meta-time">
<ClockCircleOutlined style={{ marginRight: 4 }} />
发起 {formatDateTime(item.createdAt)}
</span>
<Typography.Text
className="worker-as-meta-code"
type="secondary"
copyable={{
text: item.workOrderNo || item.caseNo,
tooltips: ['复制工单号', '已复制'],
}}
>
提交说明
</Button>
工单 {item.workOrderNo || item.caseNo}
</Typography.Text>
<Typography.Text
className="worker-as-meta-code"
type="secondary"
copyable={{ text: item.caseNo, tooltips: ['复制售后单号', '已复制'] }}
>
售后单 {item.caseNo}
</Typography.Text>
</div>
</div>
{/* 主体对比区域:买家投诉诉求 vs 打手答辩举证 (并排双列) */}
<div className="worker-as-card-body">
{/* 左列:买家投诉诉求 */}
<div className="worker-as-col worker-as-buyer-col">
<div className="worker-as-col-header">
<div className="worker-as-avatar-pill buyer">
<UserOutlined />
</div>
<span className="worker-as-col-title">买家投诉诉求</span>
</div>
<div className="worker-as-box buyer-box">
<div className="worker-as-box-text">
{item.complaintNote ? (
<p className="worker-as-complaint-text">{item.complaintNote}</p>
) : (
<p className="worker-as-empty-text">买家未填写文字描述</p>
)}
</div>
{item.complaintFiles.length > 0 && (
<div className="worker-as-box-files">
<div className="worker-as-files-label">
买家举证材料 ({item.complaintFiles.length}):
</div>
<ImagePreviewList files={item.complaintFiles} size={54} maxVisible={6} />
</div>
)}
</div>
</div>
{/* 右列:打手答辩与举证 */}
<div className="worker-as-col worker-as-worker-col">
<div className="worker-as-col-header">
<div className="worker-as-avatar-pill worker">
<SafetyCertificateOutlined />
</div>
<span className="worker-as-col-title">我的答辩与说明</span>
{item.respondedAt && (
<span className="worker-as-col-subtitle">
提交于 {formatDateTime(item.respondedAt)}
</span>
)}
</div>
{item.workerResponse || item.workerResponseFiles.length > 0 ? (
<div className="worker-as-box worker-box">
<div className="worker-as-box-text">
{item.workerResponse ? (
<p className="worker-as-worker-text">{item.workerResponse}</p>
) : null}
</div>
{item.workerResponseFiles.length > 0 && (
<div className="worker-as-box-files">
<div className="worker-as-files-label">
补救举证材料 ({item.workerResponseFiles.length}):
</div>
<ImagePreviewList files={item.workerResponseFiles} size={54} maxVisible={6} />
</div>
)}
</div>
) : item.status === 'awaiting_worker_response' ? (
<div className="worker-as-box awaiting-cta-box">
<div className="worker-as-cta-content">
<div className="worker-as-cta-icon-wrap">
<AlertOutlined className="worker-as-cta-icon" />
</div>
<div className="worker-as-cta-info">
<h4 className="worker-as-cta-title">等待您提交说明材料</h4>
<p className="worker-as-cta-desc">
买家已发起售后申诉,请针对上述问题提交文字说明或补救凭据,以便平台客服公允判定。
</p>
</div>
</div>
<Button
type="primary"
className="worker-as-submit-btn"
icon={<FormOutlined />}
onClick={() => {
setFiles([])
form.resetFields()
setResponseOpen(true)
}}
>
立即提交说明与凭据
</Button>
</div>
) : item.status === 'under_review' ? (
<div className="worker-as-box neutral-state-box">
<ClockCircleOutlined className="worker-as-neutral-icon" />
<p className="worker-as-neutral-text">
未补充文字材料,平台客服正在结合买家举证与订单原始验收凭据进行裁决审核。
</p>
</div>
) : (
<div className="worker-as-box neutral-state-box is-closed">
<CheckCircleOutlined className="worker-as-neutral-icon" />
<p className="worker-as-neutral-text">本工单已结案(未补充答辩记录)</p>
</div>
)}
</div>
</div>
{/* 底部横幅:平台仲裁裁决与资金明细 */}
{resolutionLabel ||
item.resolutionNote ||
hasRecoveryDetail ||
item.status === 'under_review' ? (
<div
className={`worker-as-resolution-banner ${item.status === 'under_review' && !resolutionLabel ? 'is-pending' : ''}`}
>
<div className="worker-as-resolution-head">
<div className="worker-as-resolution-head-left">
<span className="worker-as-gavel-icon">
<AuditOutlined />
</span>
<span className="worker-as-resolution-title">平台仲裁判定</span>
{resolutionLabel ? (
<Tag
color={resolveWorkerAfterSalesResolutionColor(item.resolutionAction)}
className="worker-as-resolution-tag"
>
{item.resolutionAction === 'upheld' && (
<CloseCircleOutlined style={{ marginRight: 4 }} />
)}
{item.resolutionAction === 'not_upheld' && (
<CheckCircleOutlined style={{ marginRight: 4 }} />
)}
{resolutionLabel}
</Tag>
) : item.status === 'under_review' ? (
<Tag color="blue" className="worker-as-resolution-tag">
客服审核中
</Tag>
) : null}
{item.resolvedAt ? (
<span className="worker-as-resolution-time">
裁决时间:{formatDateTime(item.resolvedAt)}
</span>
) : null}
</div>
</div>
{item.resolutionNote && (
<div className="worker-as-resolution-note">
<span className="note-label">判定说明:</span>
<span className="note-content">{item.resolutionNote}</span>
</div>
)}
{hasRecoveryDetail && (
<div className="worker-as-financial-breakdown">
<span className="financial-label">资金处理明细:</span>
<div className="financial-chips-wrap">
{Number(item.recoveryAmount || 0) > 0 && (
<span className="financial-chip is-recovery">
<span className="chip-name">追缴金额</span>
<span className="chip-amount">{formatMoney(item.recoveryAmount)}</span>
</span>
)}
{Number(item.pendingDepositDeductionAmount || 0) > 0 && (
<span className="financial-chip is-deposit">
<span className="chip-name">待解冻资金抵扣</span>
<span className="chip-amount">
{formatMoney(item.pendingDepositDeductionAmount)}
</span>
</span>
)}
{Number(item.availableDeductionAmount || 0) > 0 && (
<span className="financial-chip is-available">
<span className="chip-name">余额抵扣</span>
<span className="chip-amount">
{formatMoney(item.availableDeductionAmount)}
</span>
</span>
)}
{Number(item.debtAmount || 0) > 0 && (
<span className="financial-chip is-debt">
<span className="chip-name">待追缴欠款</span>
<span className="chip-amount">{formatMoney(item.debtAmount)}</span>
<span className="chip-hint">(后续报酬自动抵扣)</span>
</span>
)}
{Number(item.refundedAmount || 0) > 0 && (
<span className="financial-chip is-refunded">
<span className="chip-name">退还追缴</span>
<span className="chip-amount">{formatMoney(item.refundedAmount)}</span>
<span className="chip-hint">(已入可用余额)</span>
</span>
)}
</div>
</div>
)}
</div>
) : null}
</article>
{/* 提交说明材料弹窗 */}
<Modal
title={`售后说明 · ${item.productName || item.caseNo}`}
title={`提交售后说明 · ${item.productName || item.caseNo}`}
open={responseOpen}
width={isMobile ? '92%' : 560}
width={isMobile ? '94%' : 580}
onCancel={() => setResponseOpen(false)}
onOk={() => form.submit()}
okText="提交说明"
okText="确认提交说明"
destroyOnHidden
className="worker-as-submit-modal"
>
<Alert
type="warning"
showIcon
message="请针对售后问题说明情况或提交补救材料,原验收资料不可在此修改。"
style={{ marginBottom: 12 }}
message="请针对售后问题如实说明情况或提交补救凭证。原订单验收资料不可在此修改。"
style={{ marginBottom: 14 }}
/>
<div className="worker-after-sales-block">
<div className="worker-after-sales-block-label">
投诉内容 · {formatWorkerAfterSalesProblemType(item.problemType)}
{/* 弹窗内展示买家投诉内容供参考 */}
<div className="worker-as-modal-buyer-summary">
<div className="modal-summary-title">
<UserOutlined style={{ marginRight: 6, color: '#ef4444' }} />
买家投诉内容 · {formatWorkerAfterSalesProblemType(item.problemType)}
</div>
<Typography.Paragraph style={{ marginBottom: item.complaintFiles.length ? 8 : 0 }}>
{item.complaintNote || '-'}
</Typography.Paragraph>
{item.complaintFiles.length > 0 ? (
<ImagePreviewList files={item.complaintFiles} size={44} maxVisible={4} />
) : null}
<div className="modal-summary-content">
{item.complaintNote || '(买家未填写文字描述)'}
</div>
{item.complaintFiles.length > 0 && (
<div className="modal-summary-files">
<ImagePreviewList files={item.complaintFiles} size={46} maxVisible={4} />
</div>
)}
</div>
<Form form={form} layout="vertical" onFinish={submit} style={{ marginTop: 12 }}>
<Form form={form} layout="vertical" onFinish={submit} style={{ marginTop: 14 }}>
<Form.Item
label="说明"
label="我的说明与答辩"
name="response"
extra="说明与补救材料至少填写一项"
rules={[
@@ -441,7 +654,7 @@ function WorkerAfterSalesItem({
validator: (_, value) =>
String(value || '').trim() || files.length > 0
? Promise.resolve()
: Promise.reject(new Error('请填写说明或至少上传一张图片')),
: Promise.reject(new Error('请填写说明或至少上传一张补救材料图片')),
},
]}
>
@@ -449,10 +662,10 @@ function WorkerAfterSalesItem({
rows={4}
maxLength={2000}
showCount
placeholder="请说明当时的处理情况"
placeholder="请详细客观说明当时订单处理情况、与买家沟通情况或补救事实…"
/>
</Form.Item>
<Form.Item label="补救材料图片">
<Form.Item label="补救举证材料图片 (支持粘贴直接上传)">
<ImageUpload
scene="worker-acceptance"
scope="worker"
+796 -142
View File
@@ -2791,24 +2791,247 @@ body {
color: #111827;
}
/* ===== 售后问题单(打手端) ===== */
/* ===== 售后争议与工单仲裁中心 (打手端) ===== */
.worker-after-sales-page {
max-width: 1240px;
margin: 0 auto;
width: 100%;
display: flex;
flex-direction: column;
gap: 12px;
gap: 16px;
padding-bottom: 32px;
}
.worker-after-sales-reminder {
border-radius: 10px;
}
.worker-after-sales-toolbar {
/* 待办紧急提醒横幅 */
.worker-as-pending-alert {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
gap: 16px;
padding: 14px 20px;
background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%);
border: 1px solid #fde68a;
border-radius: 14px;
box-shadow: 0 4px 14px -2px rgba(245, 158, 11, 0.12);
}
.worker-as-alert-left {
display: flex;
align-items: center;
gap: 14px;
}
.worker-as-alert-icon-wrap {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 10px;
background: #f59e0b;
color: #ffffff;
font-size: 20px;
flex-shrink: 0;
box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3);
}
.worker-as-alert-title {
font-size: 15px;
font-weight: 700;
color: #92400e;
line-height: 1.4;
}
.worker-as-alert-title strong {
font-size: 17px;
color: #b45309;
}
.worker-as-alert-desc {
font-size: 13px;
color: #b45309;
margin-top: 2px;
line-height: 1.4;
}
.worker-as-alert-btn {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%) !important;
border: none !important;
font-weight: 600 !important;
height: 38px !important;
padding: 0 18px !important;
border-radius: 8px !important;
box-shadow: 0 2px 8px rgba(217, 119, 6, 0.25) !important;
flex-shrink: 0;
}
/* 4卡片争议处理概览网格 */
.worker-as-kpi-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 14px;
}
.worker-as-kpi-card {
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 14px;
padding: 16px 18px;
display: flex;
align-items: center;
gap: 14px;
cursor: pointer;
transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.03);
}
.worker-as-kpi-card:hover {
transform: translateY(-2px);
border-color: #cbd5e1;
box-shadow: 0 6px 16px -2px rgba(15, 23, 42, 0.08);
}
.worker-as-kpi-card.is-active {
border-color: #2563eb;
background: linear-gradient(135deg, #eff6ff 0%, #ffffff 100%);
box-shadow: 0 4px 14px rgba(37, 99, 235, 0.12);
}
.worker-as-kpi-icon-wrap {
display: flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
border-radius: 12px;
font-size: 20px;
flex-shrink: 0;
}
.worker-as-kpi-icon-wrap.amber {
background: #fffbeb;
color: #d97706;
}
.worker-as-kpi-icon-wrap.blue {
background: #eff6ff;
color: #2563eb;
}
.worker-as-kpi-icon-wrap.rose {
background: #fff1f2;
color: #e11d48;
}
.worker-as-kpi-icon-wrap.emerald {
background: #ecfdf5;
color: #059669;
}
.worker-as-kpi-info {
flex: 1;
min-width: 0;
}
.worker-as-kpi-label {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: #64748b;
font-weight: 500;
}
.worker-as-kpi-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-as-kpi-value {
font-size: 22px;
font-weight: 800;
color: #0f172a;
line-height: 1.2;
margin: 2px 0;
font-feature-settings: 'tnum';
}
.worker-as-kpi-sub {
font-size: 11px;
color: #94a3b8;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* 头部筛选工具栏 */
.worker-after-sales-desktop-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
background: #ffffff;
padding: 14px 20px;
border-radius: 14px;
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: 18px !important;
font-weight: 700 !important;
color: #0f172a !important;
letter-spacing: -0.02em;
}
.worker-as-total-badge {
display: inline-flex;
align-items: center;
font-size: 12px;
font-weight: 500;
color: #64748b;
background: #f1f5f9;
padding: 2px 9px;
border-radius: 9999px;
}
.worker-after-sales-header-center {
flex: 1;
max-width: 460px;
}
.worker-after-sales-segmented {
background: #f1f5f9;
padding: 3px;
border-radius: 8px;
}
.worker-after-sales-filter-count {
@@ -2823,72 +3046,596 @@ body {
color: #1677ff;
font-size: 11px;
line-height: 1;
font-weight: 600;
}
.worker-after-sales-header-right {
flex-shrink: 0;
}
.worker-as-loading-state,
.worker-as-empty-card {
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 14px;
padding: 48px 20px;
text-align: center;
}
/* 售后列表与卡片 */
.worker-after-sales-list {
display: flex;
flex-direction: column;
gap: 12px;
gap: 16px;
}
.worker-after-sales-item {
.worker-as-case-card {
background: #ffffff;
border: 1px solid #e2e8f0;
border-left: 3px solid #cbd5e1;
border-radius: 14px;
padding: 14px 16px;
display: flex;
flex-direction: column;
gap: 10px;
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.04);
border-radius: 16px;
overflow: hidden;
box-shadow: 0 2px 8px -2px rgba(15, 23, 42, 0.04);
transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.worker-after-sales-item.is-awaiting {
border-left-color: #fa8c16;
background: #fffdf8;
.worker-as-case-card:hover {
border-color: #cbd5e1;
box-shadow: 0 8px 24px -4px rgba(15, 23, 42, 0.08);
}
.worker-after-sales-item-head {
.worker-as-case-card.is-status-awaiting_worker_response {
border-left: 4px solid #f59e0b;
}
.worker-as-case-card.is-status-under_review {
border-left: 4px solid #3b82f6;
}
.worker-as-case-card.is-status-recovery_pending {
border-left: 4px solid #ef4444;
}
.worker-as-case-card.is-status-closed {
border-left: 4px solid #10b981;
}
/* 卡片顶部元数据条 */
.worker-as-card-header {
display: flex;
align-items: center;
gap: 8px 10px;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
padding: 12px 18px;
background: #fafbfc;
border-bottom: 1px solid #f1f5f9;
}
.worker-as-card-header-left {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.worker-after-sales-item-meta {
margin-top: -4px;
}
.worker-after-sales-block {
background: #f8fafc;
border: 1px solid #eef2f7;
border-radius: 10px;
padding: 10px 12px;
}
.worker-after-sales-block.is-mine {
background: #f6ffed;
border-color: #e6f4dc;
}
.worker-after-sales-block.is-result {
background: #fff7e6;
border-color: #ffe7ba;
}
.worker-after-sales-block-label {
.worker-as-status-badge {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 12px;
font-weight: 700;
padding: 2px 8px;
border-radius: 6px;
}
.worker-as-status-badge.is-awaiting_worker_response {
background: #fffbeb;
color: #b45309;
border: 1px solid #fde68a;
}
.worker-as-status-badge.is-under_review {
background: #eff6ff;
color: #1d4ed8;
border: 1px solid #bfdbfe;
}
.worker-as-status-badge.is-recovery_pending {
background: #fef2f2;
color: #b91c1c;
border: 1px solid #fecaca;
}
.worker-as-status-badge.is-closed {
background: #ecfdf5;
color: #047857;
border: 1px solid #a7f3d0;
}
.worker-as-status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
.worker-as-problem-tag {
border-radius: 6px;
font-weight: 500;
margin: 0 !important;
}
.worker-as-product-title {
font-size: 15px !important;
font-weight: 700 !important;
color: #0f172a !important;
max-width: 340px;
}
.worker-as-share-tag {
background: #f1f5f9 !important;
color: #475569 !important;
border: 1px solid #e2e8f0 !important;
border-radius: 6px !important;
font-size: 11px !important;
margin: 0 !important;
}
.worker-as-card-header-right {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.worker-as-meta-time {
font-size: 12px;
color: #94a3b8;
display: inline-flex;
align-items: center;
}
.worker-as-meta-code {
font-size: 12px !important;
color: #64748b !important;
background: #ffffff;
border: 1px solid #e2e8f0;
padding: 2px 8px;
border-radius: 6px;
cursor: pointer;
transition: all 0.15s;
}
.worker-as-meta-code:hover {
border-color: #cbd5e1;
color: #0f172a !important;
}
/* 主体对比区域 (双列并排) */
.worker-as-card-body {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
padding: 18px 20px;
}
.worker-as-col {
display: flex;
flex-direction: column;
gap: 10px;
}
.worker-as-col-header {
display: flex;
align-items: center;
gap: 8px;
}
.worker-as-avatar-pill {
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border-radius: 50%;
font-size: 12px;
}
.worker-as-avatar-pill.buyer {
background: #fee2e2;
color: #ef4444;
}
.worker-as-avatar-pill.worker {
background: #dcfce7;
color: #16a34a;
}
.worker-as-col-title {
font-size: 13px;
font-weight: 700;
color: #334155;
}
.worker-as-col-subtitle {
font-size: 12px;
color: #94a3b8;
margin-left: auto;
}
.worker-as-box {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 14px 16px;
min-height: 120px;
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 12px;
}
.worker-as-box.buyer-box {
background: #fafbfc;
border-color: #e2e8f0;
}
.worker-as-box.worker-box {
background: #f0fdf4;
border-color: #bbf7d0;
}
.worker-as-complaint-text {
margin: 0;
font-size: 14px;
line-height: 1.6;
color: #1e293b;
white-space: pre-wrap;
word-break: break-word;
}
.worker-as-worker-text {
margin: 0;
font-size: 14px;
line-height: 1.6;
color: #14532d;
white-space: pre-wrap;
word-break: break-word;
}
.worker-as-empty-text {
margin: 0;
font-size: 13px;
color: #94a3b8;
font-style: italic;
}
.worker-as-box-files {
border-top: 1px solid rgba(0, 0, 0, 0.05);
padding-top: 10px;
margin-top: 4px;
}
.worker-as-files-label {
font-size: 12px;
font-weight: 600;
color: #64748b;
margin-bottom: 6px;
}
.worker-after-sales-item.is-awaiting .worker-after-sales-item-actions {
/* 待提交答辩 CTA 提示框 */
.worker-as-box.awaiting-cta-box {
background: #fffdf5;
border: 1.5px dashed #f59e0b;
display: flex;
justify-content: flex-end;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 20px;
gap: 14px;
}
.worker-after-sales-mobile .worker-after-sales-toolbar .ant-segmented {
overflow-x: auto;
.worker-as-cta-content {
display: flex;
align-items: center;
gap: 12px;
text-align: left;
}
.worker-as-cta-icon-wrap {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 10px;
background: #fef3c7;
color: #d97706;
font-size: 20px;
flex-shrink: 0;
}
.worker-as-cta-title {
margin: 0 0 2px 0;
font-size: 14px;
font-weight: 700;
color: #92400e;
}
.worker-as-cta-desc {
margin: 0;
font-size: 12px;
color: #b45309;
line-height: 1.4;
}
.worker-as-submit-btn {
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%) !important;
border: none !important;
font-weight: 600 !important;
height: 36px !important;
padding: 0 20px !important;
border-radius: 8px !important;
box-shadow: 0 2px 6px rgba(217, 119, 6, 0.25) !important;
align-self: center;
}
/* 审核中 / 结案中性状态框 */
.worker-as-box.neutral-state-box {
background: #f8fafc;
border: 1px dashed #cbd5e1;
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
padding: 16px;
}
.worker-as-neutral-icon {
font-size: 18px;
color: #94a3b8;
flex-shrink: 0;
}
.worker-as-neutral-text {
margin: 0;
font-size: 13px;
color: #64748b;
line-height: 1.5;
}
/* 底部仲裁裁决横幅 */
.worker-as-resolution-banner {
background: #f8fafc;
border-top: 1px solid #e2e8f0;
padding: 14px 20px;
display: flex;
flex-direction: column;
gap: 10px;
}
.worker-as-resolution-banner.is-pending {
background: #f0f9ff;
border-top-color: #bae6fd;
}
.worker-as-resolution-head {
display: flex;
align-items: center;
justify-content: space-between;
}
.worker-as-resolution-head-left {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.worker-as-gavel-icon {
display: flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
border-radius: 6px;
background: #1e293b;
color: #ffffff;
font-size: 12px;
}
.worker-as-resolution-title {
font-size: 13px;
font-weight: 700;
color: #0f172a;
}
.worker-as-resolution-tag {
font-weight: 600;
border-radius: 6px;
margin: 0 !important;
}
.worker-as-resolution-time {
font-size: 12px;
color: #94a3b8;
}
.worker-as-resolution-note {
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 8px 12px;
font-size: 13px;
line-height: 1.5;
}
.worker-as-resolution-note .note-label {
font-weight: 600;
color: #475569;
}
.worker-as-resolution-note .note-content {
color: #1e293b;
}
/* 资金处理明细标签 */
.worker-as-financial-breakdown {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
margin-top: 2px;
}
.worker-as-financial-breakdown .financial-label {
font-size: 12px;
font-weight: 600;
color: #64748b;
flex-shrink: 0;
}
.financial-chips-wrap {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.financial-chip {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
border-radius: 8px;
font-size: 12px;
font-weight: 600;
line-height: 1.2;
}
.financial-chip .chip-name {
opacity: 0.85;
}
.financial-chip .chip-amount {
font-weight: 800;
font-feature-settings: 'tnum';
}
.financial-chip .chip-hint {
font-size: 11px;
font-weight: normal;
opacity: 0.75;
}
.financial-chip.is-recovery {
background: #fef2f2;
color: #dc2626;
border: 1px solid #fecaca;
}
.financial-chip.is-deposit {
background: #fff7ed;
color: #ea580c;
border: 1px solid #ffedd5;
}
.financial-chip.is-available {
background: #f1f5f9;
color: #475569;
border: 1px solid #e2e8f0;
}
.financial-chip.is-debt {
background: #fef2f2;
color: #b91c1c;
border: 1px solid #f87171;
}
.financial-chip.is-refunded {
background: #ecfdf5;
color: #059669;
border: 1px solid #a7f3d0;
}
/* 弹窗样式 */
.worker-as-modal-buyer-summary {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 10px;
padding: 12px 14px;
}
.worker-as-modal-buyer-summary .modal-summary-title {
font-size: 13px;
font-weight: 700;
color: #334155;
display: flex;
align-items: center;
margin-bottom: 6px;
}
.worker-as-modal-buyer-summary .modal-summary-content {
font-size: 13px;
color: #1e293b;
line-height: 1.5;
margin-bottom: 8px;
}
.worker-as-modal-buyer-summary .modal-summary-files {
border-top: 1px solid #e2e8f0;
padding-top: 8px;
}
/* 响应式调整 */
@media (max-width: 992px) {
.worker-as-kpi-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 768px) {
.worker-as-kpi-grid {
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.worker-as-kpi-card {
padding: 12px 14px;
gap: 10px;
}
.worker-as-kpi-icon-wrap {
width: 38px;
height: 38px;
font-size: 18px;
}
.worker-as-kpi-value {
font-size: 18px;
}
.worker-as-pending-alert {
flex-direction: column;
align-items: flex-start;
}
.worker-as-alert-btn {
width: 100%;
}
.worker-after-sales-desktop-header {
flex-direction: column;
align-items: stretch;
}
.worker-after-sales-header-center {
max-width: 100%;
}
.worker-as-card-body {
grid-template-columns: 1fr;
padding: 14px;
}
.worker-as-card-header {
padding: 10px 14px;
}
.worker-as-product-title {
max-width: 100%;
}
}
/* ==========================================================================
@@ -3360,99 +4107,6 @@ body {
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;