拆分打手订单列表
This commit is contained in:
@@ -19,8 +19,11 @@ export function WorkerOrderDesktopList({
|
|||||||
keyword,
|
keyword,
|
||||||
orders,
|
orders,
|
||||||
pagination,
|
pagination,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
columns,
|
columns,
|
||||||
loading,
|
loading,
|
||||||
|
searching,
|
||||||
refreshing,
|
refreshing,
|
||||||
error,
|
error,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
@@ -36,8 +39,11 @@ export function WorkerOrderDesktopList({
|
|||||||
keyword: string
|
keyword: string
|
||||||
orders: WorkOrder[]
|
orders: WorkOrder[]
|
||||||
pagination: Pagination | undefined
|
pagination: Pagination | undefined
|
||||||
|
page: number
|
||||||
|
pageSize: number
|
||||||
columns: TableColumnsType<WorkOrder>
|
columns: TableColumnsType<WorkOrder>
|
||||||
loading: boolean
|
loading: boolean
|
||||||
|
searching: boolean
|
||||||
refreshing: boolean
|
refreshing: boolean
|
||||||
error: unknown
|
error: unknown
|
||||||
onRefresh: () => void
|
onRefresh: () => void
|
||||||
@@ -91,7 +97,7 @@ export function WorkerOrderDesktopList({
|
|||||||
onChange={(event) => onKeywordInputChange(event.target.value)}
|
onChange={(event) => onKeywordInputChange(event.target.value)}
|
||||||
onPressEnter={onKeywordSearch}
|
onPressEnter={onKeywordSearch}
|
||||||
/>
|
/>
|
||||||
<Button icon={<SearchOutlined />} loading={refreshing} onClick={onKeywordSearch}>
|
<Button icon={<SearchOutlined />} loading={searching} onClick={onKeywordSearch}>
|
||||||
搜索
|
搜索
|
||||||
</Button>
|
</Button>
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
@@ -114,8 +120,8 @@ export function WorkerOrderDesktopList({
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
pagination={{
|
pagination={{
|
||||||
current: pagination?.page || 1,
|
current: pagination?.page || page,
|
||||||
pageSize: pagination?.pageSize || 20,
|
pageSize: pagination?.pageSize || pageSize,
|
||||||
total: pagination?.total || 0,
|
total: pagination?.total || 0,
|
||||||
showSizeChanger: true,
|
showSizeChanger: true,
|
||||||
pageSizeOptions: [10, 20, 50, 100],
|
pageSizeOptions: [10, 20, 50, 100],
|
||||||
|
|||||||
@@ -0,0 +1,438 @@
|
|||||||
|
import {
|
||||||
|
CopyOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
EyeOutlined,
|
||||||
|
MessageOutlined,
|
||||||
|
PictureOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
} from '@ant-design/icons'
|
||||||
|
import { Button, Empty, Input, Pagination, Spin, Tag } from 'antd'
|
||||||
|
|
||||||
|
import { DeadlineCountdown } from '@/components/DeadlineCountdown'
|
||||||
|
import ImagePreviewList from '@/components/files/ImagePreviewList'
|
||||||
|
import type {
|
||||||
|
Pagination as PaginationData,
|
||||||
|
WorkerOrderOverviewItem,
|
||||||
|
WorkOrder,
|
||||||
|
} from '@/types/worker-platform'
|
||||||
|
import { formatDateTime } from '@/utils/date-time'
|
||||||
|
|
||||||
|
import type { WorkerOrderStatusOption } from './WorkerOrderDesktopList'
|
||||||
|
import {
|
||||||
|
canEditAcceptanceImages,
|
||||||
|
canRemindAcceptance,
|
||||||
|
canRequestCancel,
|
||||||
|
canSubmitAcceptance,
|
||||||
|
canSubmitFeedback,
|
||||||
|
formatMoney,
|
||||||
|
formatStatus,
|
||||||
|
getCopyOrderInfoItems,
|
||||||
|
getDisplayOrderNo,
|
||||||
|
getMaterialDetailItems,
|
||||||
|
getMaterialScreenshots,
|
||||||
|
getSettlementLabel,
|
||||||
|
getUniqueAcceptanceFiles,
|
||||||
|
getWorkerAcceptance,
|
||||||
|
getWorkerDraft,
|
||||||
|
hasSubmittedAcceptance,
|
||||||
|
isVipEvidencePending,
|
||||||
|
resolveStatusColor,
|
||||||
|
} from './worker-order-utils'
|
||||||
|
|
||||||
|
export function WorkerOrderMobileList({
|
||||||
|
statusOptions,
|
||||||
|
status,
|
||||||
|
overviewByStatus,
|
||||||
|
keywordInput,
|
||||||
|
keyword,
|
||||||
|
orders,
|
||||||
|
pagination,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
loading,
|
||||||
|
searching,
|
||||||
|
refreshing,
|
||||||
|
error,
|
||||||
|
onStatusChange,
|
||||||
|
onKeywordInputChange,
|
||||||
|
onKeywordSearch,
|
||||||
|
onRefresh,
|
||||||
|
onPageChange,
|
||||||
|
onOpenDetail,
|
||||||
|
onOpenAcceptance,
|
||||||
|
onRemindAcceptance,
|
||||||
|
onOpenCancelRequest,
|
||||||
|
onOpenFeedback,
|
||||||
|
onOpenNote,
|
||||||
|
onCopyOrderInfo,
|
||||||
|
onCopyMaterialImages,
|
||||||
|
}: {
|
||||||
|
statusOptions: readonly WorkerOrderStatusOption[]
|
||||||
|
status: string
|
||||||
|
overviewByStatus: Map<string, WorkerOrderOverviewItem>
|
||||||
|
keywordInput: string
|
||||||
|
keyword: string
|
||||||
|
orders: WorkOrder[]
|
||||||
|
pagination: PaginationData | undefined
|
||||||
|
page: number
|
||||||
|
pageSize: number
|
||||||
|
loading: boolean
|
||||||
|
searching: boolean
|
||||||
|
refreshing: boolean
|
||||||
|
error: unknown
|
||||||
|
onStatusChange: (status: string) => void
|
||||||
|
onKeywordInputChange: (keyword: string) => void
|
||||||
|
onKeywordSearch: () => void
|
||||||
|
onRefresh: () => void
|
||||||
|
onPageChange: (page: number, pageSize: number) => void
|
||||||
|
onOpenDetail: (order: WorkOrder) => void
|
||||||
|
onOpenAcceptance: (order: WorkOrder) => void
|
||||||
|
onRemindAcceptance: (order: WorkOrder) => void
|
||||||
|
onOpenCancelRequest: (order: WorkOrder) => void
|
||||||
|
onOpenFeedback: (order: WorkOrder) => void
|
||||||
|
onOpenNote: (order: WorkOrder) => void
|
||||||
|
onCopyOrderInfo: (order: WorkOrder) => void
|
||||||
|
onCopyMaterialImages: (order: WorkOrder) => void
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="worker-orders-mobile-page">
|
||||||
|
<div className="worker-orders-mobile-status-bar">
|
||||||
|
{statusOptions.map((item) => {
|
||||||
|
const overview = overviewByStatus.get(item.value || 'all')
|
||||||
|
const count = Number(overview?.orderCount || 0)
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={item.value || 'all'}
|
||||||
|
type="button"
|
||||||
|
className={`worker-orders-status-pill ${(status || '') === item.value ? 'active' : ''}`}
|
||||||
|
onClick={() => onStatusChange(item.value)}
|
||||||
|
>
|
||||||
|
<span className="worker-orders-status-pill-main">
|
||||||
|
<span>{item.label}</span>
|
||||||
|
{count > 0 ? (
|
||||||
|
<span className="worker-orders-status-pill-count">{count}</span>
|
||||||
|
) : null}
|
||||||
|
</span>
|
||||||
|
<span className="worker-orders-status-pill-settlement">
|
||||||
|
{getSettlementLabel(item.value)} {formatMoney(overview?.settlementAmount)}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="worker-orders-mobile-search-row">
|
||||||
|
<Input
|
||||||
|
allowClear
|
||||||
|
value={keywordInput}
|
||||||
|
placeholder="搜索订单号 / 商品名 / 游戏ID / 游戏昵称"
|
||||||
|
prefix={<SearchOutlined />}
|
||||||
|
className="worker-orders-mobile-search-input"
|
||||||
|
onChange={(event) => onKeywordInputChange(event.target.value)}
|
||||||
|
onPressEnter={onKeywordSearch}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<SearchOutlined />}
|
||||||
|
loading={searching}
|
||||||
|
onClick={onKeywordSearch}
|
||||||
|
/>
|
||||||
|
<Button icon={<ReloadOutlined />} loading={refreshing} onClick={onRefresh} title="刷新" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="worker-orders-mobile-list">
|
||||||
|
{loading ? (
|
||||||
|
<div className="worker-hall-loading">
|
||||||
|
<Spin size="large" />
|
||||||
|
</div>
|
||||||
|
) : error ? (
|
||||||
|
<Empty description={error instanceof Error ? error.message : '读取我的订单失败'} />
|
||||||
|
) : orders.length === 0 ? (
|
||||||
|
<Empty
|
||||||
|
description={keyword ? `没有找到与 “${keyword}” 相关的订单` : '当前筛选下暂无订单'}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
orders.map((order) => (
|
||||||
|
<WorkerOrderMobileCard
|
||||||
|
key={order.workOrderId}
|
||||||
|
order={order}
|
||||||
|
onOpenDetail={onOpenDetail}
|
||||||
|
onOpenAcceptance={onOpenAcceptance}
|
||||||
|
onRemindAcceptance={onRemindAcceptance}
|
||||||
|
onOpenCancelRequest={onOpenCancelRequest}
|
||||||
|
onOpenFeedback={onOpenFeedback}
|
||||||
|
onOpenNote={onOpenNote}
|
||||||
|
onCopyOrderInfo={onCopyOrderInfo}
|
||||||
|
onCopyMaterialImages={onCopyMaterialImages}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
{(pagination?.total || 0) > 0 ? (
|
||||||
|
<div className="worker-orders-mobile-pagination">
|
||||||
|
<Pagination
|
||||||
|
current={pagination?.page || page}
|
||||||
|
pageSize={pagination?.pageSize || pageSize}
|
||||||
|
total={pagination?.total || 0}
|
||||||
|
simple
|
||||||
|
onChange={onPageChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function WorkerOrderMobileCard({
|
||||||
|
order,
|
||||||
|
onOpenDetail,
|
||||||
|
onOpenAcceptance,
|
||||||
|
onRemindAcceptance,
|
||||||
|
onOpenCancelRequest,
|
||||||
|
onOpenFeedback,
|
||||||
|
onOpenNote,
|
||||||
|
onCopyOrderInfo,
|
||||||
|
onCopyMaterialImages,
|
||||||
|
}: Omit<
|
||||||
|
Parameters<typeof WorkerOrderMobileList>[0],
|
||||||
|
| 'statusOptions'
|
||||||
|
| 'status'
|
||||||
|
| 'overviewByStatus'
|
||||||
|
| 'keywordInput'
|
||||||
|
| 'keyword'
|
||||||
|
| 'orders'
|
||||||
|
| 'pagination'
|
||||||
|
| 'page'
|
||||||
|
| 'pageSize'
|
||||||
|
| 'loading'
|
||||||
|
| 'searching'
|
||||||
|
| 'refreshing'
|
||||||
|
| 'error'
|
||||||
|
| 'onStatusChange'
|
||||||
|
| 'onKeywordInputChange'
|
||||||
|
| 'onKeywordSearch'
|
||||||
|
| 'onRefresh'
|
||||||
|
| 'onPageChange'
|
||||||
|
> & { order: WorkOrder }) {
|
||||||
|
const items = getMaterialDetailItems(order).filter((item) => String(item.value || '').trim())
|
||||||
|
const screenshots = getMaterialScreenshots(order)
|
||||||
|
const acceptanceFiles = getUniqueAcceptanceFiles(getWorkerAcceptance(order))
|
||||||
|
const draftFiles = getUniqueAcceptanceFiles(getWorkerDraft(order))
|
||||||
|
const noteText = getWorkerAcceptance(order)?.note
|
||||||
|
const workerNote = String(order.workerNote || '').trim()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<article className="worker-order-mobile-card">
|
||||||
|
<div className="worker-order-mobile-header">
|
||||||
|
<div className="worker-order-mobile-header-left">
|
||||||
|
{order.isUnread ? (
|
||||||
|
<Tag color="purple" className="worker-order-unread-tag">
|
||||||
|
新
|
||||||
|
</Tag>
|
||||||
|
) : null}
|
||||||
|
{order.categoryName ? (
|
||||||
|
<Tag color="blue" className="worker-order-mobile-tag">
|
||||||
|
{order.categoryName}
|
||||||
|
</Tag>
|
||||||
|
) : null}
|
||||||
|
{order.myShare ? (
|
||||||
|
<Tag color="purple" className="worker-order-mobile-tag">
|
||||||
|
拼单
|
||||||
|
</Tag>
|
||||||
|
) : null}
|
||||||
|
<span className="worker-order-mobile-orderno-text">{getDisplayOrderNo(order)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="worker-order-mobile-header-right">
|
||||||
|
<Tag color={resolveStatusColor(order.status)}>{formatStatus(order.status)}</Tag>
|
||||||
|
{order.status === 'in_progress' && order.deadlineAt ? (
|
||||||
|
<DeadlineCountdown deadlineAt={order.deadlineAt} />
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="worker-order-mobile-main-row">
|
||||||
|
<div className="worker-order-mobile-product-title">{order.productName || '未命名商品'}</div>
|
||||||
|
<div className="worker-order-mobile-price-badge">
|
||||||
|
<span className="worker-order-mobile-price-label">报酬</span>
|
||||||
|
<strong className="worker-order-mobile-price-num">
|
||||||
|
{formatMoney(order.rewardAmount)}
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="worker-order-mobile-sub-row">
|
||||||
|
<span className="worker-order-mobile-sub-item">
|
||||||
|
押金: {formatMoney(order.freezeDepositAmount)}
|
||||||
|
</span>
|
||||||
|
{order.myShare ? (
|
||||||
|
<span className="worker-order-mobile-sub-item">
|
||||||
|
已拼 {order.myShare.quantity} 份 ({formatMoney(order.myShare.shareReward)})
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
<span className="worker-order-mobile-sub-time">{formatDateTime(order.assignedAt)}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{order.status === 'problem' && String(order.problemNote || '').trim() ? (
|
||||||
|
<div className="worker-order-mobile-problem-box">
|
||||||
|
<strong>问题:</strong>
|
||||||
|
<span>{order.problemNote}</span>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{items.length > 0 || screenshots.length > 0 ? (
|
||||||
|
<div className="worker-order-mobile-credentials-box">
|
||||||
|
{items.length > 0 ? (
|
||||||
|
<div className="worker-order-mobile-credentials-grid">
|
||||||
|
{items.map((item) => (
|
||||||
|
<div key={item.key} className="worker-order-mobile-credential-line">
|
||||||
|
<span className="worker-order-mobile-credential-label">{item.label}:</span>
|
||||||
|
<span className="worker-order-mobile-credential-val">{item.value}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{screenshots.length > 0 ? (
|
||||||
|
<div className="worker-order-mobile-screenshots-row">
|
||||||
|
<span className="worker-order-mobile-credential-label">截图:</span>
|
||||||
|
<ImagePreviewList files={screenshots} size={34} />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<div className="worker-order-mobile-copy-toolbar">
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
icon={<CopyOutlined />}
|
||||||
|
disabled={getCopyOrderInfoItems(order).length === 0}
|
||||||
|
onClick={() => onCopyOrderInfo(order)}
|
||||||
|
>
|
||||||
|
复制资料
|
||||||
|
</Button>
|
||||||
|
{screenshots.length > 0 ? (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
icon={<PictureOutlined />}
|
||||||
|
onClick={() => onCopyMaterialImages(order)}
|
||||||
|
>
|
||||||
|
复制图片
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{acceptanceFiles.length > 0 ||
|
||||||
|
(draftFiles.length > 0 && !hasSubmittedAcceptance(order)) ||
|
||||||
|
noteText ? (
|
||||||
|
<div className="worker-order-mobile-acceptance-strip">
|
||||||
|
{acceptanceFiles.length > 0 ? (
|
||||||
|
<div className="worker-order-mobile-acceptance-files">
|
||||||
|
<span className="worker-order-mobile-acceptance-tag">
|
||||||
|
已提交凭据 ({acceptanceFiles.length}张)
|
||||||
|
</span>
|
||||||
|
<ImagePreviewList files={acceptanceFiles} size={32} maxVisible={3} />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{isVipEvidencePending(order) ? <Tag color="purple">VIP 免审核,待补图</Tag> : null}
|
||||||
|
{draftFiles.length > 0 && !hasSubmittedAcceptance(order) ? (
|
||||||
|
<div className="worker-order-mobile-draft-files">
|
||||||
|
<Tag color="processing" icon={<PictureOutlined />}>
|
||||||
|
已暂存 {draftFiles.length} 张图片
|
||||||
|
</Tag>
|
||||||
|
<ImagePreviewList files={draftFiles} size={32} maxVisible={3} />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{noteText ? (
|
||||||
|
<div className="worker-order-mobile-acceptance-text">说明:{noteText}</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<div className="worker-order-mobile-footer-row">
|
||||||
|
<div className="worker-order-mobile-note-block">
|
||||||
|
{workerNote ? (
|
||||||
|
<div
|
||||||
|
className={`worker-note-badge theme-${resolveNoteTheme(workerNote)}`}
|
||||||
|
onClick={() => onOpenNote(order)}
|
||||||
|
>
|
||||||
|
<span className="worker-note-text" title={workerNote}>
|
||||||
|
{workerNote}
|
||||||
|
</span>
|
||||||
|
<EditOutlined className="worker-note-edit-icon" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="worker-order-mobile-add-note-btn"
|
||||||
|
onClick={() => onOpenNote(order)}
|
||||||
|
>
|
||||||
|
<EditOutlined /> 添加备注
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="worker-order-mobile-btn-group">
|
||||||
|
<Button size="small" icon={<EyeOutlined />} onClick={() => onOpenDetail(order)}>
|
||||||
|
详情
|
||||||
|
</Button>
|
||||||
|
{canEditAcceptanceImages(order) ? (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
type={canSubmitAcceptance(order) ? 'primary' : 'default'}
|
||||||
|
icon={<PictureOutlined />}
|
||||||
|
className="worker-order-mobile-action-submit"
|
||||||
|
onClick={() => onOpenAcceptance(order)}
|
||||||
|
>
|
||||||
|
{hasSubmittedAcceptance(order) ? '补充图片' : '提交验收'}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
{canRemindAcceptance(order) ? (
|
||||||
|
<Button size="small" onClick={() => onRemindAcceptance(order)}>
|
||||||
|
催验收
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
{canRequestCancel(order) ? (
|
||||||
|
<Button size="small" danger onClick={() => onOpenCancelRequest(order)}>
|
||||||
|
申请取消
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
{canSubmitFeedback(order) ? (
|
||||||
|
<Button size="small" icon={<MessageOutlined />} onClick={() => onOpenFeedback(order)}>
|
||||||
|
反馈
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveNoteTheme(note?: string) {
|
||||||
|
const text = String(note || '')
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
if (!text) return 'default'
|
||||||
|
if (
|
||||||
|
['问题', '异常', '密码', '封号', '错误', '无法', '投诉', '退', '失败', '被挤'].some((item) =>
|
||||||
|
text.includes(item),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 'danger'
|
||||||
|
if (
|
||||||
|
['未安排', '未', '暂缓', '暂停', '稍后', '延后', '挂起', '待定'].some((item) =>
|
||||||
|
text.includes(item),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 'warning'
|
||||||
|
if (['排队', '等待', '等买家', '等扫码', '等验证', '待扫'].some((item) => text.includes(item)))
|
||||||
|
return 'purple'
|
||||||
|
if (
|
||||||
|
['打单', '做单', '进行', '处理', '上号', '练级', '正在', '开打', '肝'].some((item) =>
|
||||||
|
text.includes(item),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 'processing'
|
||||||
|
if (
|
||||||
|
['已安排', '安排', '已就绪', '就绪', '已联系', '完成', '已好', '搞定', '正常', 'ok'].some(
|
||||||
|
(item) => text.includes(item),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 'success'
|
||||||
|
return 'default'
|
||||||
|
}
|
||||||
@@ -0,0 +1,349 @@
|
|||||||
|
import {
|
||||||
|
CopyOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
EyeOutlined,
|
||||||
|
MessageOutlined,
|
||||||
|
PictureOutlined,
|
||||||
|
} from '@ant-design/icons'
|
||||||
|
import { Button, Space, Tag, Tooltip, Typography } from 'antd'
|
||||||
|
import type { TableColumnsType } from 'antd'
|
||||||
|
|
||||||
|
import { DeadlineCountdown } from '@/components/DeadlineCountdown'
|
||||||
|
import ImagePreviewList from '@/components/files/ImagePreviewList'
|
||||||
|
import type { WorkOrder } from '@/types/worker-platform'
|
||||||
|
import { formatDateTime } from '@/utils/date-time'
|
||||||
|
|
||||||
|
import {
|
||||||
|
canEditAcceptanceImages,
|
||||||
|
canRemindAcceptance,
|
||||||
|
canRequestCancel,
|
||||||
|
canSubmitAcceptance,
|
||||||
|
canSubmitFeedback,
|
||||||
|
formatMoney,
|
||||||
|
formatStatus,
|
||||||
|
getAcceptanceSubmittedAt,
|
||||||
|
getCopyOrderInfoItems,
|
||||||
|
getDisplayOrderNo,
|
||||||
|
getMaterialDetailItems,
|
||||||
|
getMaterialScreenshots,
|
||||||
|
getUniqueAcceptanceFiles,
|
||||||
|
getWorkerAcceptance,
|
||||||
|
getWorkerDraft,
|
||||||
|
hasSubmittedAcceptance,
|
||||||
|
isVipEvidencePending,
|
||||||
|
resolveStatusColor,
|
||||||
|
} from './worker-order-utils'
|
||||||
|
|
||||||
|
type NoteTheme = 'success' | 'processing' | 'danger' | 'warning' | 'purple' | 'default'
|
||||||
|
|
||||||
|
export function buildWorkerOrderTableColumns({
|
||||||
|
onOpenDetail,
|
||||||
|
onOpenAcceptance,
|
||||||
|
onRemindAcceptance,
|
||||||
|
onOpenCancelRequest,
|
||||||
|
onOpenFeedback,
|
||||||
|
onOpenNote,
|
||||||
|
onCopyOrderInfo,
|
||||||
|
onCopyMaterialImages,
|
||||||
|
}: {
|
||||||
|
onOpenDetail: (order: WorkOrder) => void
|
||||||
|
onOpenAcceptance: (order: WorkOrder) => void
|
||||||
|
onRemindAcceptance: (order: WorkOrder) => void
|
||||||
|
onOpenCancelRequest: (order: WorkOrder) => void
|
||||||
|
onOpenFeedback: (order: WorkOrder) => void
|
||||||
|
onOpenNote: (order: WorkOrder) => void
|
||||||
|
onCopyOrderInfo: (order: WorkOrder) => void
|
||||||
|
onCopyMaterialImages: (order: WorkOrder) => void
|
||||||
|
}): TableColumnsType<WorkOrder> {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: '订单信息',
|
||||||
|
width: 270,
|
||||||
|
render: (_, row) => (
|
||||||
|
<div className="cell-stack">
|
||||||
|
<Space size={4} align="center">
|
||||||
|
{row.isUnread ? (
|
||||||
|
<Tag color="purple" className="worker-order-unread-tag">
|
||||||
|
新
|
||||||
|
</Tag>
|
||||||
|
) : null}
|
||||||
|
<Typography.Link
|
||||||
|
strong
|
||||||
|
onClick={() => onOpenDetail(row)}
|
||||||
|
title={row.productName || '未命名商品'}
|
||||||
|
ellipsis
|
||||||
|
>
|
||||||
|
{row.productName || '未命名商品'}
|
||||||
|
</Typography.Link>
|
||||||
|
</Space>
|
||||||
|
<Space size={[4, 4]} wrap>
|
||||||
|
{row.categoryName ? <Tag color="blue">{row.categoryName}</Tag> : null}
|
||||||
|
{row.myShare ? <Tag color="purple">拼单</Tag> : null}
|
||||||
|
</Space>
|
||||||
|
<Typography.Text
|
||||||
|
type="secondary"
|
||||||
|
style={{ fontSize: 12 }}
|
||||||
|
ellipsis={{ tooltip: getDisplayOrderNo(row) }}
|
||||||
|
>
|
||||||
|
{getDisplayOrderNo(row)}
|
||||||
|
</Typography.Text>
|
||||||
|
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||||
|
接单:{formatDateTime(row.assignedAt)}
|
||||||
|
</Typography.Text>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
width: 130,
|
||||||
|
render: (_, row) => (
|
||||||
|
<div className="cell-stack">
|
||||||
|
<Space wrap size={4}>
|
||||||
|
<Tag color={resolveStatusColor(row.status)}>{formatStatus(row.status)}</Tag>
|
||||||
|
{row.status === 'in_progress' && row.deadlineAt ? (
|
||||||
|
<DeadlineCountdown deadlineAt={row.deadlineAt} />
|
||||||
|
) : null}
|
||||||
|
</Space>
|
||||||
|
{row.status === 'problem' && String(row.problemNote || '').trim() ? (
|
||||||
|
<Typography.Text
|
||||||
|
type="danger"
|
||||||
|
ellipsis={{ tooltip: row.problemNote }}
|
||||||
|
style={{ fontSize: 12 }}
|
||||||
|
>
|
||||||
|
{row.problemNote}
|
||||||
|
</Typography.Text>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '奖励 / 押金',
|
||||||
|
width: 140,
|
||||||
|
render: (_, row) => (
|
||||||
|
<div className="cell-stack">
|
||||||
|
<Typography.Text strong style={{ color: '#cf1322' }}>
|
||||||
|
{formatMoney(row.rewardAmount)}
|
||||||
|
</Typography.Text>
|
||||||
|
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||||
|
押金:{formatMoney(row.freezeDepositAmount)}
|
||||||
|
</Typography.Text>
|
||||||
|
{row.myShare ? (
|
||||||
|
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||||
|
{row.myShare.quantity} 份 · {formatMoney(row.myShare.shareReward)}
|
||||||
|
</Typography.Text>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '验收信息',
|
||||||
|
width: 220,
|
||||||
|
render: (_, row) => <AcceptanceCell order={row} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '快捷复制',
|
||||||
|
width: 250,
|
||||||
|
render: (_, row) => {
|
||||||
|
const items = getMaterialDetailItems(row).filter((item) => String(item.value || '').trim())
|
||||||
|
const screenshots = getMaterialScreenshots(row)
|
||||||
|
return (
|
||||||
|
<div className="cell-stack">
|
||||||
|
{items.length > 0 ? (
|
||||||
|
items.map((item) => (
|
||||||
|
<Typography.Text
|
||||||
|
key={item.key}
|
||||||
|
style={{ fontSize: 12 }}
|
||||||
|
ellipsis={{ tooltip: `${item.label}: ${item.value}` }}
|
||||||
|
>
|
||||||
|
{item.label}: {item.value}
|
||||||
|
</Typography.Text>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||||
|
暂无资料信息
|
||||||
|
</Typography.Text>
|
||||||
|
)}
|
||||||
|
{screenshots.length > 0 ? <ImagePreviewList files={screenshots} size={34} /> : null}
|
||||||
|
<Space wrap size={4}>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
icon={<CopyOutlined />}
|
||||||
|
disabled={getCopyOrderInfoItems(row).length === 0}
|
||||||
|
onClick={() => onCopyOrderInfo(row)}
|
||||||
|
>
|
||||||
|
复制信息
|
||||||
|
</Button>
|
||||||
|
{screenshots.length > 0 ? (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
icon={<PictureOutlined />}
|
||||||
|
onClick={() => onCopyMaterialImages(row)}
|
||||||
|
>
|
||||||
|
复制图片
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '备注',
|
||||||
|
width: 180,
|
||||||
|
render: (_, row) => <NoteCell order={row} onOpenNote={onOpenNote} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: 150,
|
||||||
|
render: (_, row) => (
|
||||||
|
<div className="worker-order-actions">
|
||||||
|
<Space size={6}>
|
||||||
|
<Tooltip title="查看详情">
|
||||||
|
<Button
|
||||||
|
aria-label="查看详情"
|
||||||
|
icon={<EyeOutlined />}
|
||||||
|
onClick={() => onOpenDetail(row)}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
{canEditAcceptanceImages(row) ? (
|
||||||
|
<Button
|
||||||
|
icon={<PictureOutlined />}
|
||||||
|
type={canSubmitAcceptance(row) ? 'primary' : 'default'}
|
||||||
|
onClick={() => onOpenAcceptance(row)}
|
||||||
|
>
|
||||||
|
{hasSubmittedAcceptance(row) ? '补充图片' : '验收'}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
{canRemindAcceptance(row) ? (
|
||||||
|
<Button onClick={() => onRemindAcceptance(row)}>催验收</Button>
|
||||||
|
) : null}
|
||||||
|
{canRequestCancel(row) ? (
|
||||||
|
<Button danger onClick={() => onOpenCancelRequest(row)}>
|
||||||
|
申请取消
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
{canSubmitFeedback(row) ? (
|
||||||
|
<Tooltip title="反馈客户资料或订单处理问题">
|
||||||
|
<Button icon={<MessageOutlined />} onClick={() => onOpenFeedback(row)}>
|
||||||
|
反馈
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
) : null}
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function AcceptanceCell({ order }: { order: WorkOrder }) {
|
||||||
|
const submittedAt = getAcceptanceSubmittedAt(order)
|
||||||
|
const acceptanceFiles = getUniqueAcceptanceFiles(getWorkerAcceptance(order))
|
||||||
|
const draftFiles = getUniqueAcceptanceFiles(getWorkerDraft(order))
|
||||||
|
if (acceptanceFiles.length === 0 && !submittedAt && draftFiles.length === 0) {
|
||||||
|
return (
|
||||||
|
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||||
|
未提交
|
||||||
|
</Typography.Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="cell-stack">
|
||||||
|
{acceptanceFiles.length > 0 ? (
|
||||||
|
<ImagePreviewList files={acceptanceFiles} size={38} maxVisible={2} />
|
||||||
|
) : null}
|
||||||
|
{submittedAt ? (
|
||||||
|
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
||||||
|
提交:{formatDateTime(submittedAt)}
|
||||||
|
</Typography.Text>
|
||||||
|
) : null}
|
||||||
|
{isVipEvidencePending(order) ? <Tag color="purple">VIP 免审核,待补图</Tag> : null}
|
||||||
|
{draftFiles.length > 0 && !hasSubmittedAcceptance(order) ? (
|
||||||
|
<div className="acceptance-draft-badge-wrap">
|
||||||
|
<Tag color="processing" icon={<PictureOutlined />}>
|
||||||
|
已暂存 {draftFiles.length} 张图片
|
||||||
|
</Tag>
|
||||||
|
<ImagePreviewList files={draftFiles} size={38} maxVisible={2} />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{getWorkerAcceptance(order)?.note ? (
|
||||||
|
<Typography.Text
|
||||||
|
type="secondary"
|
||||||
|
ellipsis={{ tooltip: getWorkerAcceptance(order)?.note }}
|
||||||
|
style={{ fontSize: 12 }}
|
||||||
|
>
|
||||||
|
{getWorkerAcceptance(order)?.note}
|
||||||
|
</Typography.Text>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NoteCell({
|
||||||
|
order,
|
||||||
|
onOpenNote,
|
||||||
|
}: {
|
||||||
|
order: WorkOrder
|
||||||
|
onOpenNote: (order: WorkOrder) => void
|
||||||
|
}) {
|
||||||
|
const note = String(order.workerNote || '').trim()
|
||||||
|
return (
|
||||||
|
<div className="worker-note-cell">
|
||||||
|
{note ? (
|
||||||
|
<div
|
||||||
|
className={`worker-note-badge theme-${resolveNoteTheme(note)}`}
|
||||||
|
onClick={() => onOpenNote(order)}
|
||||||
|
>
|
||||||
|
<span className="worker-note-text" title={note}>
|
||||||
|
{note}
|
||||||
|
</span>
|
||||||
|
<EditOutlined className="worker-note-edit-icon" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
size="small"
|
||||||
|
icon={<EditOutlined />}
|
||||||
|
className="worker-note-add-btn"
|
||||||
|
onClick={() => onOpenNote(order)}
|
||||||
|
>
|
||||||
|
添加备注
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveNoteTheme(note?: string): NoteTheme {
|
||||||
|
const text = String(note || '')
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
if (!text) return 'default'
|
||||||
|
if (
|
||||||
|
['问题', '异常', '密码', '封号', '错误', '无法', '投诉', '退', '失败', '被挤'].some((item) =>
|
||||||
|
text.includes(item),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 'danger'
|
||||||
|
if (
|
||||||
|
['未安排', '未', '暂缓', '暂停', '稍后', '延后', '挂起', '待定'].some((item) =>
|
||||||
|
text.includes(item),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 'warning'
|
||||||
|
if (['排队', '等待', '等买家', '等扫码', '等验证', '待扫'].some((item) => text.includes(item)))
|
||||||
|
return 'purple'
|
||||||
|
if (
|
||||||
|
['打单', '做单', '进行', '处理', '上号', '练级', '正在', '开打', '肝'].some((item) =>
|
||||||
|
text.includes(item),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 'processing'
|
||||||
|
if (
|
||||||
|
['已安排', '安排', '已就绪', '就绪', '已联系', '完成', '已好', '搞定', '正常', 'ok'].some(
|
||||||
|
(item) => text.includes(item),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 'success'
|
||||||
|
return 'default'
|
||||||
|
}
|
||||||
@@ -1,31 +1,7 @@
|
|||||||
import {
|
|
||||||
CopyOutlined,
|
|
||||||
EditOutlined,
|
|
||||||
EyeOutlined,
|
|
||||||
MessageOutlined,
|
|
||||||
PictureOutlined,
|
|
||||||
ReloadOutlined,
|
|
||||||
SearchOutlined,
|
|
||||||
} from '@ant-design/icons'
|
|
||||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||||
import {
|
import { App, Form } from 'antd'
|
||||||
App,
|
|
||||||
Button,
|
|
||||||
Empty,
|
|
||||||
Form,
|
|
||||||
Input,
|
|
||||||
Pagination,
|
|
||||||
Space,
|
|
||||||
Spin,
|
|
||||||
Tag,
|
|
||||||
Tooltip,
|
|
||||||
Typography,
|
|
||||||
} from 'antd'
|
|
||||||
import type { TableColumnsType } from 'antd'
|
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
import { DeadlineCountdown } from '@/components/DeadlineCountdown'
|
|
||||||
import ImagePreviewList from '@/components/files/ImagePreviewList'
|
|
||||||
import {
|
import {
|
||||||
fetchWorkerCancelRequests,
|
fetchWorkerCancelRequests,
|
||||||
fetchWorkerOrderFeedbacks,
|
fetchWorkerOrderFeedbacks,
|
||||||
@@ -42,12 +18,12 @@ import {
|
|||||||
submitWorkerOrderFeedback,
|
submitWorkerOrderFeedback,
|
||||||
} from '@/services/worker'
|
} from '@/services/worker'
|
||||||
import type { UploadedFile, WorkOrder } from '@/types/worker-platform'
|
import type { UploadedFile, WorkOrder } from '@/types/worker-platform'
|
||||||
import { formatDateTime } from '@/utils/date-time'
|
|
||||||
import { useIsMobile } from '@/utils/use-is-mobile'
|
import { useIsMobile } from '@/utils/use-is-mobile'
|
||||||
import { buildWorkerOrderQuery } from '@/utils/worker-order-query'
|
import { buildWorkerOrderQuery } from '@/utils/worker-order-query'
|
||||||
import WorkerAfterSalesSection from './WorkerAfterSalesSection'
|
import WorkerAfterSalesSection from './WorkerAfterSalesSection'
|
||||||
import { WorkerOrderDesktopList } from './WorkerOrderDesktopList'
|
import { WorkerOrderDesktopList } from './WorkerOrderDesktopList'
|
||||||
import { WorkerOrderDetailDrawer } from './WorkerOrderDetailDrawer'
|
import { WorkerOrderDetailDrawer } from './WorkerOrderDetailDrawer'
|
||||||
|
import { WorkerOrderMobileList } from './WorkerOrderMobileList'
|
||||||
import {
|
import {
|
||||||
type AcceptanceFormValues,
|
type AcceptanceFormValues,
|
||||||
type CancelRequestFormValues,
|
type CancelRequestFormValues,
|
||||||
@@ -55,30 +31,17 @@ import {
|
|||||||
type WorkerNoteFormValues,
|
type WorkerNoteFormValues,
|
||||||
WorkerOrderModals,
|
WorkerOrderModals,
|
||||||
} from './WorkerOrderModals'
|
} from './WorkerOrderModals'
|
||||||
|
import { buildWorkerOrderTableColumns } from './WorkerOrderTableColumns'
|
||||||
import {
|
import {
|
||||||
asRecord,
|
|
||||||
canAutoAcceptOrder,
|
canAutoAcceptOrder,
|
||||||
canEditAcceptanceImages,
|
|
||||||
canRemindAcceptance,
|
|
||||||
canRequestCancel,
|
|
||||||
canSubmitAcceptance,
|
|
||||||
canSubmitFeedback,
|
|
||||||
canSupplementAcceptedEvidence,
|
canSupplementAcceptedEvidence,
|
||||||
formatMoney,
|
|
||||||
formatStatus,
|
|
||||||
getAcceptanceSubmittedAt,
|
|
||||||
getCopyOrderInfoItems,
|
getCopyOrderInfoItems,
|
||||||
getDisplayOrderNo,
|
|
||||||
getMaterialDetailItems,
|
|
||||||
getMaterialScreenshots,
|
getMaterialScreenshots,
|
||||||
getSettlementLabel,
|
|
||||||
getUniqueAcceptanceFiles,
|
getUniqueAcceptanceFiles,
|
||||||
getWorkerAcceptance,
|
getWorkerAcceptance,
|
||||||
getWorkerDraft,
|
getWorkerDraft,
|
||||||
hasSubmittedAcceptance,
|
hasSubmittedAcceptance,
|
||||||
isVipEvidencePending,
|
|
||||||
patchWorkOrderReadState,
|
patchWorkOrderReadState,
|
||||||
resolveStatusColor,
|
|
||||||
toPngBlob,
|
toPngBlob,
|
||||||
} from './worker-order-utils'
|
} from './worker-order-utils'
|
||||||
|
|
||||||
@@ -461,598 +424,71 @@ export default function WorkerOrdersPage() {
|
|||||||
setPage(1)
|
setPage(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns: TableColumnsType<WorkOrder> = [
|
const columns = buildWorkerOrderTableColumns({
|
||||||
{
|
onOpenDetail: openOrderDetail,
|
||||||
title: '订单信息',
|
onOpenAcceptance: openAcceptanceModal,
|
||||||
width: 270,
|
onRemindAcceptance: (order) => {
|
||||||
render: (_, row) => (
|
void submitAcceptanceReminder(order)
|
||||||
<div className="cell-stack">
|
|
||||||
<Space size={4} align="center">
|
|
||||||
{row.isUnread ? (
|
|
||||||
<Tag color="purple" className="worker-order-unread-tag">
|
|
||||||
新
|
|
||||||
</Tag>
|
|
||||||
) : null}
|
|
||||||
<Typography.Link
|
|
||||||
strong
|
|
||||||
onClick={() => openOrderDetail(row)}
|
|
||||||
title={row.productName || '未命名商品'}
|
|
||||||
ellipsis
|
|
||||||
>
|
|
||||||
{row.productName || '未命名商品'}
|
|
||||||
</Typography.Link>
|
|
||||||
</Space>
|
|
||||||
<Space size={[4, 4]} wrap>
|
|
||||||
{row.categoryName ? <Tag color="blue">{row.categoryName}</Tag> : null}
|
|
||||||
{row.myShare ? <Tag color="purple">拼单</Tag> : null}
|
|
||||||
</Space>
|
|
||||||
<Typography.Text
|
|
||||||
type="secondary"
|
|
||||||
style={{ fontSize: 12 }}
|
|
||||||
ellipsis={{ tooltip: getDisplayOrderNo(row) }}
|
|
||||||
>
|
|
||||||
{getDisplayOrderNo(row)}
|
|
||||||
</Typography.Text>
|
|
||||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
|
||||||
接单:{formatDateTime(row.assignedAt)}
|
|
||||||
</Typography.Text>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
onOpenCancelRequest: openCancelRequestModal,
|
||||||
title: '状态',
|
onOpenFeedback: openFeedbackModal,
|
||||||
width: 130,
|
onOpenNote: openNoteModal,
|
||||||
render: (_, row) => (
|
onCopyOrderInfo: copyOrderInfo,
|
||||||
<div className="cell-stack">
|
onCopyMaterialImages: (order) => {
|
||||||
<Space wrap size={4}>
|
void copyMaterialImages(order)
|
||||||
<Tag color={resolveStatusColor(row.status)}>{formatStatus(row.status)}</Tag>
|
|
||||||
{row.status === 'in_progress' && row.deadlineAt ? (
|
|
||||||
<DeadlineCountdown deadlineAt={row.deadlineAt} />
|
|
||||||
) : null}
|
|
||||||
</Space>
|
|
||||||
{row.status === 'problem' && String(row.problemNote || '').trim() ? (
|
|
||||||
<Typography.Text
|
|
||||||
type="danger"
|
|
||||||
ellipsis={{ tooltip: row.problemNote }}
|
|
||||||
style={{ fontSize: 12 }}
|
|
||||||
>
|
|
||||||
{row.problemNote}
|
|
||||||
</Typography.Text>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
})
|
||||||
title: '奖励 / 押金',
|
|
||||||
width: 140,
|
|
||||||
render: (_, row) => (
|
|
||||||
<div className="cell-stack">
|
|
||||||
<Typography.Text strong style={{ color: '#cf1322' }}>
|
|
||||||
{formatMoney(row.rewardAmount)}
|
|
||||||
</Typography.Text>
|
|
||||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
|
||||||
押金:{formatMoney(row.freezeDepositAmount)}
|
|
||||||
</Typography.Text>
|
|
||||||
{row.myShare ? (
|
|
||||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
|
||||||
{row.myShare.quantity} 份 · {formatMoney(row.myShare.shareReward)}
|
|
||||||
</Typography.Text>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '验收信息',
|
|
||||||
width: 220,
|
|
||||||
render: (_, row) => {
|
|
||||||
const submittedAt = getAcceptanceSubmittedAt(row)
|
|
||||||
const acceptanceFiles = getUniqueAcceptanceFiles(getWorkerAcceptance(row))
|
|
||||||
const hasAcceptance = acceptanceFiles.length > 0
|
|
||||||
const draftFiles = getUniqueAcceptanceFiles(getWorkerDraft(row))
|
|
||||||
const draftCount = draftFiles.length
|
|
||||||
const vipEvidencePending = isVipEvidencePending(row)
|
|
||||||
|
|
||||||
if (!hasAcceptance && !submittedAt && draftCount === 0) {
|
|
||||||
return (
|
|
||||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
|
||||||
未提交
|
|
||||||
</Typography.Text>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="cell-stack">
|
|
||||||
{hasAcceptance ? (
|
|
||||||
<ImagePreviewList files={acceptanceFiles} size={38} maxVisible={2} />
|
|
||||||
) : null}
|
|
||||||
{submittedAt ? (
|
|
||||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
|
||||||
提交:{formatDateTime(submittedAt)}
|
|
||||||
</Typography.Text>
|
|
||||||
) : null}
|
|
||||||
{vipEvidencePending ? <Tag color="purple">VIP 免审核,待补图</Tag> : null}
|
|
||||||
{draftCount > 0 && !hasSubmittedAcceptance(row) ? (
|
|
||||||
<div className="acceptance-draft-badge-wrap">
|
|
||||||
<Tag color="processing" icon={<PictureOutlined />}>
|
|
||||||
已暂存 {draftCount} 张图片
|
|
||||||
</Tag>
|
|
||||||
<ImagePreviewList files={draftFiles} size={38} maxVisible={2} />
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{getWorkerAcceptance(row)?.note ? (
|
|
||||||
<Typography.Text
|
|
||||||
type="secondary"
|
|
||||||
ellipsis={{ tooltip: getWorkerAcceptance(row)?.note }}
|
|
||||||
style={{ fontSize: 12 }}
|
|
||||||
>
|
|
||||||
{getWorkerAcceptance(row)?.note}
|
|
||||||
</Typography.Text>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '快捷复制',
|
|
||||||
width: 250,
|
|
||||||
render: (_, row) => {
|
|
||||||
const items = getMaterialDetailItems(row).filter((item) => String(item.value || '').trim())
|
|
||||||
const canCopyInfo = getCopyOrderInfoItems(row).length > 0
|
|
||||||
const screenshots = getMaterialScreenshots(row)
|
|
||||||
return (
|
|
||||||
<div className="cell-stack">
|
|
||||||
{items.length > 0 ? (
|
|
||||||
items.map((item) => (
|
|
||||||
<Typography.Text
|
|
||||||
key={item.key}
|
|
||||||
style={{ fontSize: 12 }}
|
|
||||||
ellipsis={{ tooltip: `${item.label}: ${item.value}` }}
|
|
||||||
>
|
|
||||||
{item.label}: {item.value}
|
|
||||||
</Typography.Text>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
|
|
||||||
暂无资料信息
|
|
||||||
</Typography.Text>
|
|
||||||
)}
|
|
||||||
{screenshots.length > 0 ? <ImagePreviewList files={screenshots} size={34} /> : null}
|
|
||||||
<Space wrap size={4}>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
icon={<CopyOutlined />}
|
|
||||||
disabled={!canCopyInfo}
|
|
||||||
onClick={() => copyOrderInfo(row)}
|
|
||||||
>
|
|
||||||
复制信息
|
|
||||||
</Button>
|
|
||||||
{screenshots.length > 0 ? (
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
icon={<PictureOutlined />}
|
|
||||||
onClick={() => copyMaterialImages(row)}
|
|
||||||
>
|
|
||||||
复制图片
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '备注',
|
|
||||||
width: 180,
|
|
||||||
render: (_, row) => {
|
|
||||||
const note = String(row.workerNote || '').trim()
|
|
||||||
const theme = resolveNoteTheme(note)
|
|
||||||
return (
|
|
||||||
<div className="worker-note-cell">
|
|
||||||
{note ? (
|
|
||||||
<div
|
|
||||||
className={`worker-note-badge theme-${theme}`}
|
|
||||||
onClick={() => openNoteModal(row)}
|
|
||||||
>
|
|
||||||
<span className="worker-note-text" title={note}>
|
|
||||||
{note}
|
|
||||||
</span>
|
|
||||||
<EditOutlined className="worker-note-edit-icon" />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
type="dashed"
|
|
||||||
size="small"
|
|
||||||
icon={<EditOutlined />}
|
|
||||||
className="worker-note-add-btn"
|
|
||||||
onClick={() => openNoteModal(row)}
|
|
||||||
>
|
|
||||||
添加备注
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
width: 150,
|
|
||||||
render: (_, row) => (
|
|
||||||
<div className="worker-order-actions">
|
|
||||||
<Space size={6}>
|
|
||||||
<Tooltip title="查看详情">
|
|
||||||
<Button
|
|
||||||
aria-label="查看详情"
|
|
||||||
icon={<EyeOutlined />}
|
|
||||||
onClick={() => openOrderDetail(row)}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
{canEditAcceptanceImages(row) ? (
|
|
||||||
<Button
|
|
||||||
icon={<PictureOutlined />}
|
|
||||||
type={canSubmitAcceptance(row) ? 'primary' : 'default'}
|
|
||||||
onClick={() => openAcceptanceModal(row)}
|
|
||||||
>
|
|
||||||
{hasSubmittedAcceptance(row) ? '补充图片' : '验收'}
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
{canRemindAcceptance(row) ? (
|
|
||||||
<Button onClick={() => submitAcceptanceReminder(row)}>催验收</Button>
|
|
||||||
) : null}
|
|
||||||
{canRequestCancel(row) ? (
|
|
||||||
<Button danger onClick={() => openCancelRequestModal(row)}>
|
|
||||||
申请取消
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
{canSubmitFeedback(row) ? (
|
|
||||||
<Tooltip title="反馈客户资料或订单处理问题">
|
|
||||||
<Button icon={<MessageOutlined />} onClick={() => openFeedbackModal(row)}>
|
|
||||||
反馈
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WorkerAfterSalesSection isMobile={isMobile} />
|
<WorkerAfterSalesSection isMobile={isMobile} />
|
||||||
{isMobile ? (
|
{isMobile ? (
|
||||||
<div className="worker-orders-mobile-page">
|
<WorkerOrderMobileList
|
||||||
{/* 顶部水平滑动状态切换胶囊条 */}
|
statusOptions={STATUS_OPTIONS}
|
||||||
<div className="worker-orders-mobile-status-bar">
|
status={status}
|
||||||
{STATUS_OPTIONS.map((item) => {
|
overviewByStatus={overviewByStatus}
|
||||||
const countKey = item.value || 'all'
|
keywordInput={keywordInput}
|
||||||
const overview = overviewByStatus.get(countKey)
|
keyword={keyword}
|
||||||
const count = Number(overview?.orderCount || 0)
|
orders={orders}
|
||||||
const settlementAmount = Number(overview?.settlementAmount || 0)
|
pagination={pagination}
|
||||||
const isActive = (status || '') === item.value
|
page={page}
|
||||||
const settlementLabel = getSettlementLabel(item.value)
|
pageSize={pageSize}
|
||||||
return (
|
loading={ordersQuery.isLoading}
|
||||||
<button
|
searching={ordersQuery.isFetching}
|
||||||
key={item.value || 'all'}
|
refreshing={ordersQuery.isFetching || overviewQuery.isFetching}
|
||||||
type="button"
|
error={ordersQuery.error}
|
||||||
className={`worker-orders-status-pill ${isActive ? 'active' : ''}`}
|
onStatusChange={(nextStatus) => {
|
||||||
onClick={() => {
|
setStatus(nextStatus)
|
||||||
setStatus(item.value)
|
setPage(1)
|
||||||
setPage(1)
|
}}
|
||||||
}}
|
onKeywordInputChange={(nextValue) => {
|
||||||
>
|
setKeywordInput(nextValue)
|
||||||
<span className="worker-orders-status-pill-main">
|
if (!nextValue.trim()) {
|
||||||
<span>{item.label}</span>
|
setKeyword('')
|
||||||
{count > 0 ? (
|
setPage(1)
|
||||||
<span className="worker-orders-status-pill-count">{count}</span>
|
}
|
||||||
) : null}
|
}}
|
||||||
</span>
|
onKeywordSearch={applyKeywordSearch}
|
||||||
<span className="worker-orders-status-pill-settlement">
|
onRefresh={() => {
|
||||||
{settlementLabel} {formatMoney(settlementAmount)}
|
void refreshAll()
|
||||||
</span>
|
}}
|
||||||
</button>
|
onPageChange={(nextPage, nextPageSize) => {
|
||||||
)
|
setPage(nextPage)
|
||||||
})}
|
setPageSize(nextPageSize)
|
||||||
</div>
|
}}
|
||||||
|
onOpenDetail={openOrderDetail}
|
||||||
{/* 搜索与刷新栏 */}
|
onOpenAcceptance={openAcceptanceModal}
|
||||||
<div className="worker-orders-mobile-search-row">
|
onRemindAcceptance={(order) => {
|
||||||
<Input
|
void submitAcceptanceReminder(order)
|
||||||
allowClear
|
}}
|
||||||
value={keywordInput}
|
onOpenCancelRequest={openCancelRequestModal}
|
||||||
placeholder="搜索订单号 / 商品名 / 游戏ID / 游戏昵称"
|
onOpenFeedback={openFeedbackModal}
|
||||||
prefix={<SearchOutlined />}
|
onOpenNote={openNoteModal}
|
||||||
className="worker-orders-mobile-search-input"
|
onCopyOrderInfo={copyOrderInfo}
|
||||||
onChange={(event) => {
|
onCopyMaterialImages={(order) => {
|
||||||
const nextValue = event.target.value
|
void copyMaterialImages(order)
|
||||||
setKeywordInput(nextValue)
|
}}
|
||||||
if (!nextValue.trim()) {
|
/>
|
||||||
setKeyword('')
|
|
||||||
setPage(1)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onPressEnter={applyKeywordSearch}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
icon={<SearchOutlined />}
|
|
||||||
loading={ordersQuery.isFetching}
|
|
||||||
onClick={applyKeywordSearch}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
icon={<ReloadOutlined />}
|
|
||||||
loading={ordersQuery.isFetching || overviewQuery.isFetching}
|
|
||||||
onClick={() => refreshAll()}
|
|
||||||
title="刷新"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 移动端紧凑卡片列表 */}
|
|
||||||
<div className="worker-orders-mobile-list">
|
|
||||||
{ordersQuery.isLoading ? (
|
|
||||||
<div className="worker-hall-loading">
|
|
||||||
<Spin size="large" />
|
|
||||||
</div>
|
|
||||||
) : ordersQuery.error ? (
|
|
||||||
<Empty
|
|
||||||
description={
|
|
||||||
ordersQuery.error instanceof Error
|
|
||||||
? ordersQuery.error.message
|
|
||||||
: '读取我的订单失败'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : orders.length === 0 ? (
|
|
||||||
<Empty
|
|
||||||
description={keyword ? `没有找到与 “${keyword}” 相关的订单` : '当前筛选下暂无订单'}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
orders.map((order) => {
|
|
||||||
const items = getMaterialDetailItems(order).filter((item) =>
|
|
||||||
String(item.value || '').trim(),
|
|
||||||
)
|
|
||||||
const screenshots = getMaterialScreenshots(order)
|
|
||||||
const canCopyInfo = getCopyOrderInfoItems(order).length > 0
|
|
||||||
const _submittedAt = getAcceptanceSubmittedAt(order)
|
|
||||||
const acceptanceFiles = getUniqueAcceptanceFiles(getWorkerAcceptance(order))
|
|
||||||
const hasAcceptance = acceptanceFiles.length > 0
|
|
||||||
const draftFiles = getUniqueAcceptanceFiles(getWorkerDraft(order))
|
|
||||||
const draftCount = draftFiles.length
|
|
||||||
const noteText = getWorkerAcceptance(order)?.note
|
|
||||||
const workerNote = String(order.workerNote || '').trim()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<article key={order.workOrderId} className="worker-order-mobile-card">
|
|
||||||
{/* 卡片顶栏:分类 + 订单号 + 状态/倒计时 */}
|
|
||||||
<div className="worker-order-mobile-header">
|
|
||||||
<div className="worker-order-mobile-header-left">
|
|
||||||
{order.isUnread ? (
|
|
||||||
<Tag color="purple" className="worker-order-unread-tag">
|
|
||||||
新
|
|
||||||
</Tag>
|
|
||||||
) : null}
|
|
||||||
{order.categoryName ? (
|
|
||||||
<Tag color="blue" className="worker-order-mobile-tag">
|
|
||||||
{order.categoryName}
|
|
||||||
</Tag>
|
|
||||||
) : null}
|
|
||||||
{order.myShare ? (
|
|
||||||
<Tag color="purple" className="worker-order-mobile-tag">
|
|
||||||
拼单
|
|
||||||
</Tag>
|
|
||||||
) : null}
|
|
||||||
<span className="worker-order-mobile-orderno-text">
|
|
||||||
{getDisplayOrderNo(order)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="worker-order-mobile-header-right">
|
|
||||||
<Tag color={resolveStatusColor(order.status)}>
|
|
||||||
{formatStatus(order.status)}
|
|
||||||
</Tag>
|
|
||||||
{order.status === 'in_progress' && order.deadlineAt ? (
|
|
||||||
<DeadlineCountdown deadlineAt={order.deadlineAt} />
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 商品标题与金额 */}
|
|
||||||
<div className="worker-order-mobile-main-row">
|
|
||||||
<div className="worker-order-mobile-product-title">
|
|
||||||
{order.productName || '未命名商品'}
|
|
||||||
</div>
|
|
||||||
<div className="worker-order-mobile-price-badge">
|
|
||||||
<span className="worker-order-mobile-price-label">报酬</span>
|
|
||||||
<strong className="worker-order-mobile-price-num">
|
|
||||||
{formatMoney(order.rewardAmount)}
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 辅助金额与接单时间 */}
|
|
||||||
<div className="worker-order-mobile-sub-row">
|
|
||||||
<span className="worker-order-mobile-sub-item">
|
|
||||||
押金: {formatMoney(order.freezeDepositAmount)}
|
|
||||||
</span>
|
|
||||||
{order.myShare ? (
|
|
||||||
<span className="worker-order-mobile-sub-item">
|
|
||||||
已拼 {order.myShare.quantity} 份 ({formatMoney(order.myShare.shareReward)}
|
|
||||||
)
|
|
||||||
</span>
|
|
||||||
) : null}
|
|
||||||
<span className="worker-order-mobile-sub-time">
|
|
||||||
{formatDateTime(order.assignedAt)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 问题单提示 */}
|
|
||||||
{order.status === 'problem' && String(order.problemNote || '').trim() ? (
|
|
||||||
<div className="worker-order-mobile-problem-box">
|
|
||||||
<strong>问题:</strong>
|
|
||||||
<span>{order.problemNote}</span>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{/* 买家资料与截图 */}
|
|
||||||
{(items.length > 0 || screenshots.length > 0) && (
|
|
||||||
<div className="worker-order-mobile-credentials-box">
|
|
||||||
{items.length > 0 ? (
|
|
||||||
<div className="worker-order-mobile-credentials-grid">
|
|
||||||
{items.map((item) => (
|
|
||||||
<div key={item.key} className="worker-order-mobile-credential-line">
|
|
||||||
<span className="worker-order-mobile-credential-label">
|
|
||||||
{item.label}:
|
|
||||||
</span>
|
|
||||||
<span className="worker-order-mobile-credential-val">
|
|
||||||
{item.value}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{screenshots.length > 0 ? (
|
|
||||||
<div className="worker-order-mobile-screenshots-row">
|
|
||||||
<span className="worker-order-mobile-credential-label">截图:</span>
|
|
||||||
<ImagePreviewList files={screenshots} size={34} />
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
<div className="worker-order-mobile-copy-toolbar">
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
icon={<CopyOutlined />}
|
|
||||||
disabled={!canCopyInfo}
|
|
||||||
onClick={() => copyOrderInfo(order)}
|
|
||||||
>
|
|
||||||
复制资料
|
|
||||||
</Button>
|
|
||||||
{screenshots.length > 0 ? (
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
icon={<PictureOutlined />}
|
|
||||||
onClick={() => copyMaterialImages(order)}
|
|
||||||
>
|
|
||||||
复制图片
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 验收凭据与暂存状态 */}
|
|
||||||
{(hasAcceptance ||
|
|
||||||
(draftCount > 0 && !hasSubmittedAcceptance(order)) ||
|
|
||||||
noteText) && (
|
|
||||||
<div className="worker-order-mobile-acceptance-strip">
|
|
||||||
{hasAcceptance ? (
|
|
||||||
<div className="worker-order-mobile-acceptance-files">
|
|
||||||
<span className="worker-order-mobile-acceptance-tag">
|
|
||||||
已提交凭据 ({acceptanceFiles.length}张)
|
|
||||||
</span>
|
|
||||||
<ImagePreviewList files={acceptanceFiles} size={32} maxVisible={3} />
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{isVipEvidencePending(order) ? (
|
|
||||||
<Tag color="purple">VIP 免审核,待补图</Tag>
|
|
||||||
) : null}
|
|
||||||
{draftCount > 0 && !hasSubmittedAcceptance(order) ? (
|
|
||||||
<div className="worker-order-mobile-draft-files">
|
|
||||||
<Tag color="processing" icon={<PictureOutlined />}>
|
|
||||||
已暂存 {draftCount} 张图片
|
|
||||||
</Tag>
|
|
||||||
<ImagePreviewList files={draftFiles} size={32} maxVisible={3} />
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{noteText ? (
|
|
||||||
<div className="worker-order-mobile-acceptance-text">
|
|
||||||
说明:{noteText}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 底部备注与操作 */}
|
|
||||||
<div className="worker-order-mobile-footer-row">
|
|
||||||
<div className="worker-order-mobile-note-block">
|
|
||||||
{workerNote ? (
|
|
||||||
<div
|
|
||||||
className={`worker-note-badge theme-${resolveNoteTheme(workerNote)}`}
|
|
||||||
onClick={() => openNoteModal(order)}
|
|
||||||
>
|
|
||||||
<span className="worker-note-text" title={workerNote}>
|
|
||||||
{workerNote}
|
|
||||||
</span>
|
|
||||||
<EditOutlined className="worker-note-edit-icon" />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="worker-order-mobile-add-note-btn"
|
|
||||||
onClick={() => openNoteModal(order)}
|
|
||||||
>
|
|
||||||
<EditOutlined /> 添加备注
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="worker-order-mobile-btn-group">
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
icon={<EyeOutlined />}
|
|
||||||
onClick={() => openOrderDetail(order)}
|
|
||||||
>
|
|
||||||
详情
|
|
||||||
</Button>
|
|
||||||
{canEditAcceptanceImages(order) ? (
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
type={canSubmitAcceptance(order) ? 'primary' : 'default'}
|
|
||||||
icon={<PictureOutlined />}
|
|
||||||
className="worker-order-mobile-action-submit"
|
|
||||||
onClick={() => openAcceptanceModal(order)}
|
|
||||||
>
|
|
||||||
{hasSubmittedAcceptance(order) ? '补充图片' : '提交验收'}
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
{canRemindAcceptance(order) ? (
|
|
||||||
<Button size="small" onClick={() => submitAcceptanceReminder(order)}>
|
|
||||||
催验收
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
{canRequestCancel(order) ? (
|
|
||||||
<Button size="small" danger onClick={() => openCancelRequestModal(order)}>
|
|
||||||
申请取消
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
{canSubmitFeedback(order) ? (
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
icon={<MessageOutlined />}
|
|
||||||
onClick={() => openFeedbackModal(order)}
|
|
||||||
>
|
|
||||||
反馈
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)}
|
|
||||||
|
|
||||||
{(pagination?.total || 0) > 0 ? (
|
|
||||||
<div className="worker-orders-mobile-pagination">
|
|
||||||
<Pagination
|
|
||||||
current={pagination?.page || page}
|
|
||||||
pageSize={pagination?.pageSize || pageSize}
|
|
||||||
total={pagination?.total || 0}
|
|
||||||
simple
|
|
||||||
onChange={(nextPage: number, nextPageSize: number) => {
|
|
||||||
setPage(nextPage)
|
|
||||||
setPageSize(nextPageSize)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<WorkerOrderDesktopList
|
<WorkerOrderDesktopList
|
||||||
statusOptions={STATUS_OPTIONS}
|
statusOptions={STATUS_OPTIONS}
|
||||||
@@ -1062,8 +498,11 @@ export default function WorkerOrdersPage() {
|
|||||||
keyword={keyword}
|
keyword={keyword}
|
||||||
orders={orders}
|
orders={orders}
|
||||||
pagination={pagination}
|
pagination={pagination}
|
||||||
|
page={page}
|
||||||
|
pageSize={pageSize}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
loading={ordersQuery.isLoading}
|
loading={ordersQuery.isLoading}
|
||||||
|
searching={ordersQuery.isFetching}
|
||||||
refreshing={ordersQuery.isFetching || overviewQuery.isFetching}
|
refreshing={ordersQuery.isFetching || overviewQuery.isFetching}
|
||||||
error={ordersQuery.error}
|
error={ordersQuery.error}
|
||||||
onRefresh={() => {
|
onRefresh={() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user