优化操作区域
This commit is contained in:
@@ -120,6 +120,8 @@ export default function AdminKuaishouIndustryPage() {
|
||||
const [actionLoading, setActionLoading] = useState('')
|
||||
const [consumingVoucherId, setConsumingVoucherId] = useState<number | null>(null)
|
||||
const [vouchers, setVouchers] = useState<AdminKuaishouIndustryVoucher[]>([])
|
||||
const [selectedVoucher, setSelectedVoucher] =
|
||||
useState<AdminKuaishouIndustryVoucher | null>(null)
|
||||
const [voucherTotal, setVoucherTotal] = useState(0)
|
||||
const [voucherFilters, setVoucherFilters] = useState<VoucherFilterState>({
|
||||
oid: '',
|
||||
@@ -341,15 +343,21 @@ export default function AdminKuaishouIndustryPage() {
|
||||
)
|
||||
}
|
||||
|
||||
const isSelected = selectedVoucher?.id === row.id
|
||||
return (
|
||||
<Button size="small" type="primary" ghost onClick={() => selectVoucher(row)}>
|
||||
选中
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
ghost={!isSelected}
|
||||
onClick={() => selectVoucher(row)}
|
||||
>
|
||||
{isSelected ? '已选' : '选择'}
|
||||
</Button>
|
||||
)
|
||||
},
|
||||
},
|
||||
],
|
||||
[consumingVoucherId, isSupportOnly, shopNameBySellerId],
|
||||
[consumingVoucherId, isSupportOnly, selectedVoucher?.id, shopNameBySellerId],
|
||||
)
|
||||
|
||||
const refundColumns = useMemo<TableColumnsType<Record<string, unknown>>>(
|
||||
@@ -425,7 +433,14 @@ export default function AdminKuaishouIndustryPage() {
|
||||
page: filters.page,
|
||||
pageSize: filters.pageSize,
|
||||
})
|
||||
setVouchers(response.data.items)
|
||||
const items = response.data.items
|
||||
setVouchers(items)
|
||||
setSelectedVoucher((current) => {
|
||||
if (!current) {
|
||||
return null
|
||||
}
|
||||
return items.find((item) => item.id === current.id) || current
|
||||
})
|
||||
setVoucherTotal(response.data.pagination.total)
|
||||
} catch (error) {
|
||||
showError(error instanceof Error ? error.message : '查询电子凭证失败')
|
||||
@@ -435,18 +450,8 @@ export default function AdminKuaishouIndustryPage() {
|
||||
}
|
||||
|
||||
function selectVoucher(row: AdminKuaishouIndustryVoucher) {
|
||||
setToolForm((current) => ({
|
||||
...current,
|
||||
sellerId: row.sellerId,
|
||||
oid: row.oid,
|
||||
orderId: row.oid,
|
||||
taskId: row.taskId || '',
|
||||
voucherCode: row.voucherCode,
|
||||
eticketType: row.eticketType || current.eticketType || '',
|
||||
serialNum: row.consumeSerialNum || current.serialNum,
|
||||
reason: current.reason || 'USER_APPLY_REFUND',
|
||||
}))
|
||||
showSuccess('已填入券码操作区')
|
||||
setSelectedVoucher(row)
|
||||
showSuccess(`已选择券码 ${row.voucherCode}`)
|
||||
}
|
||||
|
||||
async function runCheckAvailable() {
|
||||
@@ -688,9 +693,17 @@ export default function AdminKuaishouIndustryPage() {
|
||||
}
|
||||
|
||||
function buildVoucherToolPayload(): AdminKuaishouIndustryVoucherToolPayload {
|
||||
const voucherCode = String(toolForm.voucherCode || '').trim()
|
||||
const voucherCode = String(selectedVoucher?.voucherCode || toolForm.voucherCode || '').trim()
|
||||
const oid = String(selectedVoucher?.oid || toolForm.oid || toolForm.orderId || '').trim()
|
||||
return {
|
||||
...toolForm,
|
||||
sellerId: selectedVoucher?.sellerId || toolForm.sellerId,
|
||||
oid,
|
||||
orderId: oid,
|
||||
taskId: selectedVoucher?.taskId || toolForm.taskId,
|
||||
voucherCode,
|
||||
eticketType: selectedVoucher?.eticketType || toolForm.eticketType,
|
||||
serialNum: toolForm.serialNum || selectedVoucher?.consumeSerialNum,
|
||||
etickets: voucherCode ? [{ id: voucherCode, code: voucherCode, num: 1 }] : undefined,
|
||||
}
|
||||
}
|
||||
@@ -878,6 +891,9 @@ export default function AdminKuaishouIndustryPage() {
|
||||
columns={columns}
|
||||
dataSource={vouchers}
|
||||
loading={voucherLoading}
|
||||
rowClassName={(row) =>
|
||||
selectedVoucher?.id === row.id ? 'kuaishou-industry-row-selected' : ''
|
||||
}
|
||||
// 列已压缩;仅在极窄容器时横向滚动
|
||||
scroll={{ x: scrollX || 1064 }}
|
||||
pagination={buildAdminTablePagination({
|
||||
@@ -900,6 +916,18 @@ export default function AdminKuaishouIndustryPage() {
|
||||
}
|
||||
|
||||
function renderVoucherTab() {
|
||||
const hasVoucherTarget = Boolean(
|
||||
selectedVoucher ||
|
||||
String(toolForm.voucherCode || '').trim() ||
|
||||
String(toolForm.taskId || '').trim() ||
|
||||
String(toolForm.oid || toolForm.orderId || '').trim(),
|
||||
)
|
||||
const selectedShopName = selectedVoucher
|
||||
? selectedVoucher.shopName ||
|
||||
shopNameBySellerId.get(String(selectedVoucher.sellerId || '').trim()) ||
|
||||
'未命名店铺'
|
||||
: ''
|
||||
|
||||
return (
|
||||
<div className="page-stack">
|
||||
{renderVoucherFilters()}
|
||||
@@ -909,84 +937,200 @@ export default function AdminKuaishouIndustryPage() {
|
||||
{renderVoucherTable(1064)}
|
||||
</Card>
|
||||
|
||||
<Card title="接口操作">
|
||||
<div className="kuaishou-industry-tool-grid">
|
||||
<Input
|
||||
placeholder="店铺 ID"
|
||||
value={toolForm.sellerId}
|
||||
onChange={(event) => updateToolForm({ sellerId: event.target.value })}
|
||||
/>
|
||||
<Input
|
||||
placeholder="订单号 oid"
|
||||
value={toolForm.oid}
|
||||
onChange={(event) =>
|
||||
updateToolForm({
|
||||
oid: event.target.value,
|
||||
orderId: event.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
placeholder="任务 ID"
|
||||
value={String(toolForm.taskId || '')}
|
||||
onChange={(event) => updateToolForm({ taskId: event.target.value })}
|
||||
/>
|
||||
<Input
|
||||
placeholder="券码"
|
||||
value={toolForm.voucherCode}
|
||||
onChange={(event) => updateToolForm({ voucherCode: event.target.value })}
|
||||
/>
|
||||
<Input
|
||||
placeholder="电子凭证类型(选中券码自动填入)"
|
||||
value={toolForm.eticketType}
|
||||
onChange={(event) => updateToolForm({ eticketType: event.target.value })}
|
||||
/>
|
||||
<Input
|
||||
placeholder="核销序列号"
|
||||
value={toolForm.serialNum}
|
||||
onChange={(event) => updateToolForm({ serialNum: event.target.value })}
|
||||
/>
|
||||
<Input
|
||||
placeholder="核销类型"
|
||||
value={toolForm.consumeType}
|
||||
onChange={(event) => updateToolForm({ consumeType: event.target.value })}
|
||||
/>
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="销毁/冲正原因"
|
||||
value={toolForm.reason || undefined}
|
||||
onChange={(value) => updateToolForm({ reason: value || '' })}
|
||||
options={[
|
||||
{ label: 'USER_APPLY_REFUND(用户退款)', value: 'USER_APPLY_REFUND' },
|
||||
{ label: 'SYS_ADMIN_DESTROY(后台作废)', value: 'SYS_ADMIN_DESTROY' },
|
||||
{ label: 'SUPPLY_DESTROY(供应商作废)', value: 'SUPPLY_DESTROY' },
|
||||
{ label: 'ETICKET_EXPIRED(凭证过期)', value: 'ETICKET_EXPIRED' },
|
||||
]}
|
||||
style={{ minWidth: 220 }}
|
||||
/>
|
||||
<Input
|
||||
placeholder="门店名称"
|
||||
value={toolForm.storeName}
|
||||
onChange={(event) => updateToolForm({ storeName: event.target.value })}
|
||||
/>
|
||||
<Input
|
||||
placeholder="门店地址"
|
||||
value={toolForm.storeAddress}
|
||||
onChange={(event) => updateToolForm({ storeAddress: event.target.value })}
|
||||
/>
|
||||
<Card title="券码操作" className="kuaishou-industry-tool-card">
|
||||
<div className="kuaishou-industry-tool-section">
|
||||
<div className="kuaishou-industry-tool-section-title">当前卡券</div>
|
||||
{selectedVoucher ? (
|
||||
<div className="kuaishou-industry-selected-voucher">
|
||||
<div className="kuaishou-industry-selected-header">
|
||||
<Typography.Text
|
||||
strong
|
||||
copyable={{ text: selectedVoucher.voucherCode }}
|
||||
className="kuaishou-industry-selected-code"
|
||||
>
|
||||
{selectedVoucher.voucherCode}
|
||||
</Typography.Text>
|
||||
<Space size={4}>
|
||||
<Tag color={getVoucherStatusColor(selectedVoucher.status)}>
|
||||
{getVoucherStatusLabel(selectedVoucher.status)}
|
||||
</Tag>
|
||||
<Button type="link" size="small" onClick={() => setSelectedVoucher(null)}>
|
||||
清除
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<div className="kuaishou-industry-selected-meta">
|
||||
<div>
|
||||
<span>订单号</span>
|
||||
<Typography.Text copyable={{ text: selectedVoucher.oid }}>
|
||||
{selectedVoucher.oid || '-'}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
<div>
|
||||
<span>任务</span>
|
||||
<strong>{selectedVoucher.taskNo || selectedVoucher.taskId || '-'}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>店铺</span>
|
||||
<strong>{selectedShopName}</strong>
|
||||
<small>{selectedVoucher.sellerId || '-'}</small>
|
||||
</div>
|
||||
<div>
|
||||
<span>凭证类型</span>
|
||||
<strong title={selectedVoucher.eticketType || undefined}>
|
||||
{selectedVoucher.eticketType || '-'}
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="kuaishou-industry-selected-empty">
|
||||
<strong>尚未选择卡券</strong>
|
||||
<span>请从左侧列表选择,或在下方手动指定。</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Space wrap className="kuaishou-industry-actions">
|
||||
|
||||
<div className="kuaishou-industry-tool-section">
|
||||
<div className="kuaishou-industry-tool-section-title">操作参数</div>
|
||||
<div className="kuaishou-industry-tool-grid">
|
||||
<label className="kuaishou-industry-tool-field">
|
||||
<span>核销类型</span>
|
||||
<Input
|
||||
placeholder="consume"
|
||||
value={toolForm.consumeType}
|
||||
onChange={(event) => updateToolForm({ consumeType: event.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="kuaishou-industry-tool-field">
|
||||
<span>核销序列号(可选)</span>
|
||||
<Input
|
||||
placeholder={selectedVoucher?.consumeSerialNum || '默认使用卡券记录'}
|
||||
value={toolForm.serialNum}
|
||||
onChange={(event) => updateToolForm({ serialNum: event.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="kuaishou-industry-tool-field kuaishou-industry-tool-field-wide">
|
||||
<span>销毁原因</span>
|
||||
<Select
|
||||
value={toolForm.reason || undefined}
|
||||
onChange={(value) => updateToolForm({ reason: value || '' })}
|
||||
options={[
|
||||
{ label: '用户申请退款', value: 'USER_APPLY_REFUND' },
|
||||
{ label: '后台作废', value: 'SYS_ADMIN_DESTROY' },
|
||||
{ label: '供应商作废', value: 'SUPPLY_DESTROY' },
|
||||
{ label: '凭证过期', value: 'ETICKET_EXPIRED' },
|
||||
]}
|
||||
/>
|
||||
</label>
|
||||
<label className="kuaishou-industry-tool-field">
|
||||
<span>门店名称(可选)</span>
|
||||
<Input
|
||||
placeholder="用于核销明细"
|
||||
value={toolForm.storeName}
|
||||
onChange={(event) => updateToolForm({ storeName: event.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="kuaishou-industry-tool-field">
|
||||
<span>门店地址(可选)</span>
|
||||
<Input
|
||||
placeholder="用于核销明细"
|
||||
value={toolForm.storeAddress}
|
||||
onChange={(event) => updateToolForm({ storeAddress: event.target.value })}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Collapse
|
||||
ghost
|
||||
size="small"
|
||||
className="kuaishou-industry-manual-target"
|
||||
items={[
|
||||
{
|
||||
key: 'manual-target',
|
||||
label: selectedVoucher
|
||||
? '手动指定卡券(清除当前选择后可用)'
|
||||
: '手动指定卡券',
|
||||
children: (
|
||||
<div className="kuaishou-industry-tool-grid">
|
||||
<label className="kuaishou-industry-tool-field">
|
||||
<span>店铺 ID</span>
|
||||
<Input
|
||||
disabled={Boolean(selectedVoucher)}
|
||||
value={toolForm.sellerId}
|
||||
onChange={(event) => updateToolForm({ sellerId: event.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="kuaishou-industry-tool-field">
|
||||
<span>订单号 oid</span>
|
||||
<Input
|
||||
disabled={Boolean(selectedVoucher)}
|
||||
value={toolForm.oid}
|
||||
onChange={(event) =>
|
||||
updateToolForm({
|
||||
oid: event.target.value,
|
||||
orderId: event.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<label className="kuaishou-industry-tool-field">
|
||||
<span>任务 ID</span>
|
||||
<Input
|
||||
disabled={Boolean(selectedVoucher)}
|
||||
value={String(toolForm.taskId || '')}
|
||||
onChange={(event) => updateToolForm({ taskId: event.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="kuaishou-industry-tool-field">
|
||||
<span>券码</span>
|
||||
<Input
|
||||
disabled={Boolean(selectedVoucher)}
|
||||
value={toolForm.voucherCode}
|
||||
onChange={(event) =>
|
||||
updateToolForm({ voucherCode: event.target.value })
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<label className="kuaishou-industry-tool-field kuaishou-industry-tool-field-wide">
|
||||
<span>电子凭证类型</span>
|
||||
<Input
|
||||
disabled={Boolean(selectedVoucher)}
|
||||
value={toolForm.eticketType}
|
||||
onChange={(event) =>
|
||||
updateToolForm({ eticketType: event.target.value })
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="kuaishou-industry-actions">
|
||||
<Button
|
||||
icon={<SyncOutlined />}
|
||||
loading={actionLoading === 'check'}
|
||||
disabled={!hasVoucherTarget}
|
||||
onClick={runCheckAvailable}
|
||||
>
|
||||
检查有效性
|
||||
</Button>
|
||||
<Button
|
||||
icon={<SendOutlined />}
|
||||
loading={actionLoading === 'resend'}
|
||||
disabled={!hasVoucherTarget}
|
||||
onClick={runResendCode}
|
||||
>
|
||||
重发发码
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<CheckCircleOutlined />}
|
||||
loading={actionLoading === 'consume'}
|
||||
disabled={!hasVoucherTarget}
|
||||
onClick={runConsume}
|
||||
>
|
||||
手动核销
|
||||
@@ -995,6 +1139,7 @@ export default function AdminKuaishouIndustryPage() {
|
||||
danger
|
||||
icon={<RollbackOutlined />}
|
||||
loading={actionLoading === 'reverse'}
|
||||
disabled={!hasVoucherTarget}
|
||||
onClick={runReverse}
|
||||
>
|
||||
冲正
|
||||
@@ -1004,6 +1149,7 @@ export default function AdminKuaishouIndustryPage() {
|
||||
description="将向快手发起销毁回调,并关闭关联任务。已销毁券会重试回调。"
|
||||
okText="销毁"
|
||||
cancelText="取消"
|
||||
disabled={!canManageRefund || !hasVoucherTarget}
|
||||
onConfirm={() => runDestroy()}
|
||||
>
|
||||
<Button
|
||||
@@ -1011,19 +1157,12 @@ export default function AdminKuaishouIndustryPage() {
|
||||
type="primary"
|
||||
icon={<DeleteOutlined />}
|
||||
loading={actionLoading === 'destroy'}
|
||||
disabled={!canManageRefund}
|
||||
disabled={!canManageRefund || !hasVoucherTarget}
|
||||
>
|
||||
手动销毁
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
<Button
|
||||
icon={<SendOutlined />}
|
||||
loading={actionLoading === 'resend'}
|
||||
onClick={runResendCode}
|
||||
>
|
||||
重发发码
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
{renderInlineActionResult('vouchers')}
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@@ -338,15 +338,144 @@ select {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.kuaishou-industry-row-selected > td {
|
||||
background: #eef6ff !important;
|
||||
}
|
||||
|
||||
.kuaishou-industry-row-selected:hover > td {
|
||||
background: #e5f0ff !important;
|
||||
}
|
||||
|
||||
.kuaishou-industry-tool-card .ant-card-body {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.kuaishou-industry-tool-section + .kuaishou-industry-tool-section {
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #edf0f5;
|
||||
}
|
||||
|
||||
.kuaishou-industry-tool-section-title {
|
||||
margin-bottom: 10px;
|
||||
color: #1f2937;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.kuaishou-industry-selected-voucher {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.kuaishou-industry-selected-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.kuaishou-industry-selected-code {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.kuaishou-industry-selected-meta {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px 16px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.kuaishou-industry-selected-meta > div {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.kuaishou-industry-selected-meta span,
|
||||
.kuaishou-industry-selected-meta small {
|
||||
color: #6b7280;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.kuaishou-industry-selected-meta strong,
|
||||
.kuaishou-industry-selected-meta .ant-typography {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.kuaishou-industry-selected-empty {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
padding: 4px 0;
|
||||
color: #6b7280;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.kuaishou-industry-selected-empty strong {
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.kuaishou-industry-tool-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 10px;
|
||||
margin-bottom: 14px;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.kuaishou-industry-tool-field {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.kuaishou-industry-tool-field > span {
|
||||
color: #4b5563;
|
||||
font-size: 12px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.kuaishou-industry-tool-field-wide {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.kuaishou-industry-tool-field .ant-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.kuaishou-industry-manual-target {
|
||||
margin-top: 14px;
|
||||
border-top: 1px solid #edf0f5;
|
||||
}
|
||||
|
||||
.kuaishou-industry-manual-target .ant-collapse-header {
|
||||
padding: 12px 0 8px;
|
||||
color: #6b7280;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.kuaishou-industry-manual-target .ant-collapse-content-box {
|
||||
padding: 4px 0 0;
|
||||
}
|
||||
|
||||
.kuaishou-industry-actions {
|
||||
margin-top: 2px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #edf0f5;
|
||||
}
|
||||
|
||||
.kuaishou-industry-actions .ant-btn {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.kuaishou-industry-actions > :last-child {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.kuaishou-industry-action-result {
|
||||
|
||||
Reference in New Issue
Block a user