优化快手电子凭证:客服核销页、店铺展示与筛选
- 客服角色仅展示「券码核销」Tab,本地列表直接核销 - 卖家列改为店铺名+ID 展示,并返回 shopName - 新增轻量店铺列表接口,支持按店铺下拉筛选 - 修复 asRecord 空值导致的前端构建类型错误
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
consumeAdminKuaishouIndustryVoucherByCode,
|
||||
disagreeAdminKuaishouIndustryRefund,
|
||||
listAdminKuaishouIndustryRefunds,
|
||||
listAdminKuaishouIndustryShops,
|
||||
listAdminKuaishouIndustryVouchers,
|
||||
resendAdminKuaishouIndustryVoucherCode,
|
||||
reverseAdminKuaishouIndustryVoucher,
|
||||
@@ -35,6 +36,16 @@ router.get(
|
||||
),
|
||||
)
|
||||
|
||||
router.get(
|
||||
'/kuaishou-industry/shops',
|
||||
requireAdminRoles(['admin', 'operator', 'support']),
|
||||
createJsonHandler(() => listAdminKuaishouIndustryShops(), {
|
||||
successMessage: 'ok',
|
||||
errorMessage: '查询快手行业店铺失败',
|
||||
scope: '[admin/kuaishou-industry/shops]',
|
||||
}),
|
||||
)
|
||||
|
||||
router.post(
|
||||
'/kuaishou-industry/refunds/list',
|
||||
requireAdminRoles(['admin', 'operator']),
|
||||
|
||||
@@ -32,9 +32,12 @@ export async function listAdminKuaishouIndustryVouchers(
|
||||
input: KuaishouIndustryVoucherAdminListQuery = {},
|
||||
) {
|
||||
const result = await listKuaishouIndustryVouchersForAdmin(input)
|
||||
const shopNameBySellerId = buildKuaishouIndustryShopNameMap()
|
||||
|
||||
return {
|
||||
items: result.items.map(mapAdminKuaishouIndustryVoucher),
|
||||
items: result.items.map((voucher) =>
|
||||
mapAdminKuaishouIndustryVoucher(voucher, shopNameBySellerId),
|
||||
),
|
||||
pagination: {
|
||||
page: result.page,
|
||||
pageSize: result.pageSize,
|
||||
@@ -43,6 +46,27 @@ export async function listAdminKuaishouIndustryVouchers(
|
||||
}
|
||||
}
|
||||
|
||||
/** 轻量店铺选项:仅展示名与 ID,供筛选/下拉使用(不含 token 等敏感字段) */
|
||||
export function listAdminKuaishouIndustryShops() {
|
||||
const shops = listKuaishouIndustryShopConfigs(getKuaishouIndustrySourceConfig())
|
||||
|
||||
return {
|
||||
shops: shops
|
||||
.map((shop) => {
|
||||
const sellerId = String(shop.sellerId || '').trim()
|
||||
const shopId = String(shop.shopId || '').trim()
|
||||
const shopName = String(shop.customShopName || shop.shopName || '').trim()
|
||||
return {
|
||||
sellerId,
|
||||
shopId,
|
||||
shopName,
|
||||
enabled: shop.enabled !== false,
|
||||
}
|
||||
})
|
||||
.filter((shop) => shop.sellerId || shop.shopId),
|
||||
}
|
||||
}
|
||||
|
||||
export async function listAdminKuaishouIndustryRefunds(input: JsonObject = {}) {
|
||||
assertRequired(input.beginTime, '开始时间未填写')
|
||||
assertRequired(input.endTime, '结束时间未填写')
|
||||
@@ -370,7 +394,11 @@ function sanitizeOpenApiRequest(request: JsonObject | undefined): JsonObject | n
|
||||
}
|
||||
}
|
||||
|
||||
function mapAdminKuaishouIndustryVoucher(voucher: KuaishouIndustryVoucherRow) {
|
||||
function mapAdminKuaishouIndustryVoucher(
|
||||
voucher: KuaishouIndustryVoucherRow,
|
||||
shopNameBySellerId: Map<string, string> = buildKuaishouIndustryShopNameMap(),
|
||||
) {
|
||||
const sellerId = String(voucher.seller_id || '').trim()
|
||||
return {
|
||||
id: voucher.id,
|
||||
voucherCode: voucher.voucher_code,
|
||||
@@ -378,7 +406,8 @@ function mapAdminKuaishouIndustryVoucher(voucher: KuaishouIndustryVoucherRow) {
|
||||
orderId: voucher.order_id,
|
||||
taskId: voucher.task_id,
|
||||
unitIndex: voucher.unit_index,
|
||||
sellerId: voucher.seller_id,
|
||||
sellerId,
|
||||
shopName: shopNameBySellerId.get(sellerId) || '',
|
||||
tokenMasked: maskSecret(voucher.token),
|
||||
status: voucher.status,
|
||||
validStartTime: Number(voucher.valid_start_time || 0) || 0,
|
||||
@@ -397,6 +426,29 @@ function mapAdminKuaishouIndustryVoucher(voucher: KuaishouIndustryVoucherRow) {
|
||||
}
|
||||
}
|
||||
|
||||
function buildKuaishouIndustryShopNameMap(): Map<string, string> {
|
||||
const map = new Map<string, string>()
|
||||
const shops = listKuaishouIndustryShopConfigs(getKuaishouIndustrySourceConfig())
|
||||
|
||||
for (const shop of shops) {
|
||||
const name = String(shop.customShopName || shop.shopName || '').trim()
|
||||
if (!name) {
|
||||
continue
|
||||
}
|
||||
|
||||
const sellerId = String(shop.sellerId || '').trim()
|
||||
const shopId = String(shop.shopId || '').trim()
|
||||
if (sellerId) {
|
||||
map.set(sellerId, name)
|
||||
}
|
||||
if (shopId && shopId !== sellerId) {
|
||||
map.set(shopId, name)
|
||||
}
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
function mapTaskReference(task: TaskRow) {
|
||||
return {
|
||||
taskId: task.id,
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
Descriptions,
|
||||
Input,
|
||||
InputNumber,
|
||||
Popconfirm,
|
||||
Select,
|
||||
Space,
|
||||
Table,
|
||||
@@ -33,7 +34,7 @@ import {
|
||||
checkAdminKuaishouIndustryVoucherAvailable,
|
||||
consumeAdminKuaishouIndustryVoucher,
|
||||
disagreeAdminKuaishouIndustryRefund,
|
||||
fetchAdminKuaishouIndustrySourceConfig,
|
||||
fetchAdminKuaishouIndustryShops,
|
||||
fetchAdminKuaishouIndustryVouchers,
|
||||
listAdminKuaishouIndustryRefunds,
|
||||
resendAdminKuaishouIndustryVoucherCode,
|
||||
@@ -44,11 +45,11 @@ import type {
|
||||
AdminKuaishouIndustryRefundApprovePayload,
|
||||
AdminKuaishouIndustryRefundDisagreePayload,
|
||||
AdminKuaishouIndustryRefundListPayload,
|
||||
AdminKuaishouIndustryShopConfig,
|
||||
AdminKuaishouIndustryShopOption,
|
||||
AdminKuaishouIndustryVoucher,
|
||||
AdminKuaishouIndustryVoucherToolPayload,
|
||||
} from '@/types/admin'
|
||||
import { hasAdminRole } from '@/utils/admin-auth'
|
||||
import { getAdminRole, hasAdminRole } from '@/utils/admin-auth'
|
||||
import {
|
||||
ADMIN_DEFAULT_PAGE_SIZE,
|
||||
buildAdminLocalTablePagination,
|
||||
@@ -58,7 +59,7 @@ import { formatAdminDateTime } from '@/utils/admin-time'
|
||||
import { stringifyDisplayJson } from '@/utils/date-time'
|
||||
|
||||
type DateRangeValue = [Dayjs | null, Dayjs | null] | null
|
||||
type IndustryTab = 'vouchers' | 'refunds' | 'result'
|
||||
type IndustryTab = 'support-consume' | 'vouchers' | 'refunds' | 'result'
|
||||
type IndustryActionScope = 'vouchers' | 'refunds'
|
||||
type IndustryActionKind =
|
||||
| 'check'
|
||||
@@ -109,10 +110,14 @@ type RefundListState = {
|
||||
const DEFAULT_ETICKET_TYPE = 'DINING_OPEN_TICKET'
|
||||
|
||||
export default function AdminKuaishouIndustryPage() {
|
||||
const [activeTab, setActiveTab] = useState<IndustryTab>('vouchers')
|
||||
const isSupportOnly = getAdminRole() === 'support'
|
||||
const canManageRefund = hasAdminRole('operator')
|
||||
const [activeTab, setActiveTab] = useState<IndustryTab>(
|
||||
isSupportOnly ? 'support-consume' : 'vouchers',
|
||||
)
|
||||
const [voucherLoading, setVoucherLoading] = useState(false)
|
||||
const [actionLoading, setActionLoading] = useState('')
|
||||
const [consumingVoucherId, setConsumingVoucherId] = useState<number | null>(null)
|
||||
const [vouchers, setVouchers] = useState<AdminKuaishouIndustryVoucher[]>([])
|
||||
const [voucherTotal, setVoucherTotal] = useState(0)
|
||||
const [voucherFilters, setVoucherFilters] = useState<VoucherFilterState>({
|
||||
@@ -158,7 +163,17 @@ export default function AdminKuaishouIndustryPage() {
|
||||
})
|
||||
const [lastActionResult, setLastActionResult] = useState<IndustryActionResultView | null>(null)
|
||||
const [refundRows, setRefundRows] = useState<Array<Record<string, unknown>>>([])
|
||||
const [industryShops, setIndustryShops] = useState<AdminKuaishouIndustryShopConfig[]>([])
|
||||
const [industryShops, setIndustryShops] = useState<AdminKuaishouIndustryShopOption[]>([])
|
||||
const shopFilterOptions = useMemo(
|
||||
() =>
|
||||
industryShops
|
||||
.filter((shop) => shop.sellerId)
|
||||
.map((shop) => ({
|
||||
label: formatShopOptionLabel(shop),
|
||||
value: shop.sellerId,
|
||||
})),
|
||||
[industryShops],
|
||||
)
|
||||
const refundShopOptions = useMemo(
|
||||
() =>
|
||||
industryShops
|
||||
@@ -169,6 +184,24 @@ export default function AdminKuaishouIndustryPage() {
|
||||
})),
|
||||
[industryShops],
|
||||
)
|
||||
const shopNameBySellerId = useMemo(() => {
|
||||
const map = new Map<string, string>()
|
||||
for (const shop of industryShops) {
|
||||
const name = String(shop.shopName || '').trim()
|
||||
if (!name) {
|
||||
continue
|
||||
}
|
||||
const sellerId = String(shop.sellerId || '').trim()
|
||||
const shopId = String(shop.shopId || '').trim()
|
||||
if (sellerId) {
|
||||
map.set(sellerId, name)
|
||||
}
|
||||
if (shopId) {
|
||||
map.set(shopId, name)
|
||||
}
|
||||
}
|
||||
return map
|
||||
}, [industryShops])
|
||||
|
||||
const columns = useMemo<TableColumnsType<AdminKuaishouIndustryVoucher>>(
|
||||
() => [
|
||||
@@ -209,14 +242,14 @@ export default function AdminKuaishouIndustryPage() {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '卖家',
|
||||
minWidth: 160,
|
||||
render: (_, row) => (
|
||||
<div className="cell-stack">
|
||||
<span>{row.sellerId || '-'}</span>
|
||||
<span className="muted">Token:{row.tokenMasked || '-'}</span>
|
||||
</div>
|
||||
),
|
||||
title: '店铺',
|
||||
minWidth: 180,
|
||||
render: (_, row) =>
|
||||
renderShopCell(
|
||||
row.shopName || shopNameBySellerId.get(String(row.sellerId || '').trim()) || '',
|
||||
row.sellerId,
|
||||
isSupportOnly ? '' : row.tokenMasked,
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '核销',
|
||||
@@ -228,30 +261,69 @@ export default function AdminKuaishouIndustryPage() {
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '更新时间',
|
||||
width: 170,
|
||||
render: (_, row) => formatAdminDateTime(row.updatedAt),
|
||||
},
|
||||
...(isSupportOnly
|
||||
? []
|
||||
: [
|
||||
{
|
||||
title: '更新时间',
|
||||
width: 170,
|
||||
render: (_: unknown, row: AdminKuaishouIndustryVoucher) =>
|
||||
formatAdminDateTime(row.updatedAt),
|
||||
},
|
||||
]),
|
||||
{
|
||||
title: '操作',
|
||||
fixed: 'right',
|
||||
width: 120,
|
||||
render: (_, row) => (
|
||||
<Button size="small" type="primary" ghost onClick={() => selectVoucher(row)}>
|
||||
选中
|
||||
</Button>
|
||||
),
|
||||
fixed: 'right' as const,
|
||||
width: isSupportOnly ? 100 : 120,
|
||||
render: (_: unknown, row: AdminKuaishouIndustryVoucher) => {
|
||||
if (isSupportOnly) {
|
||||
const canConsume = row.status === 'UNUSED'
|
||||
return (
|
||||
<Popconfirm
|
||||
title="确认核销该券码?"
|
||||
description={row.voucherCode}
|
||||
okText="核销"
|
||||
cancelText="取消"
|
||||
disabled={!canConsume}
|
||||
onConfirm={() => consumeVoucherRow(row)}
|
||||
>
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
icon={<CheckCircleOutlined />}
|
||||
disabled={!canConsume}
|
||||
loading={consumingVoucherId === row.id}
|
||||
>
|
||||
核销
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Button size="small" type="primary" ghost onClick={() => selectVoucher(row)}>
|
||||
选中
|
||||
</Button>
|
||||
)
|
||||
},
|
||||
},
|
||||
],
|
||||
[],
|
||||
[consumingVoucherId, isSupportOnly, shopNameBySellerId],
|
||||
)
|
||||
|
||||
const refundColumns = useMemo<TableColumnsType<Record<string, unknown>>>(
|
||||
() => [
|
||||
{ title: '售后单', dataIndex: 'refundId', minWidth: 160 },
|
||||
{ title: '订单', dataIndex: 'oid', minWidth: 160 },
|
||||
{ title: '卖家', dataIndex: 'sellerId', minWidth: 140 },
|
||||
{
|
||||
title: '店铺',
|
||||
dataIndex: 'sellerId',
|
||||
minWidth: 180,
|
||||
render: (_, row) => {
|
||||
const sellerId = String(row.sellerId || '').trim()
|
||||
return renderShopCell(shopNameBySellerId.get(sellerId) || '', sellerId)
|
||||
},
|
||||
},
|
||||
{ title: '状态', dataIndex: 'status', width: 100 },
|
||||
{ title: '协商', dataIndex: 'negotiateStatus', width: 100 },
|
||||
{ title: '金额', dataIndex: 'refundFee', width: 100 },
|
||||
@@ -271,18 +343,18 @@ export default function AdminKuaishouIndustryPage() {
|
||||
),
|
||||
},
|
||||
],
|
||||
[],
|
||||
[shopNameBySellerId],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
void loadVouchers()
|
||||
void loadIndustryShops()
|
||||
void loadVouchers()
|
||||
}, [])
|
||||
|
||||
async function loadIndustryShops() {
|
||||
try {
|
||||
const response = await fetchAdminKuaishouIndustrySourceConfig()
|
||||
const shops = response.data.source.shops || []
|
||||
const response = await fetchAdminKuaishouIndustryShops()
|
||||
const shops = response.data.shops || []
|
||||
setIndustryShops(shops)
|
||||
|
||||
const enabledSellerIds = shops
|
||||
@@ -292,7 +364,8 @@ export default function AdminKuaishouIndustryPage() {
|
||||
applyDefaultSellerId(enabledSellerIds[0] as string)
|
||||
}
|
||||
} catch (error) {
|
||||
showError(error instanceof Error ? error.message : '读取快手授权店铺失败')
|
||||
// 店铺下拉失败不阻断列表,仅回退为手动输入店铺 ID
|
||||
console.warn(error instanceof Error ? error.message : '读取店铺列表失败')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,6 +445,50 @@ export default function AdminKuaishouIndustryPage() {
|
||||
}
|
||||
}
|
||||
|
||||
async function consumeVoucherRow(row: AdminKuaishouIndustryVoucher) {
|
||||
if (row.status !== 'UNUSED') {
|
||||
showError('仅未使用券码可核销')
|
||||
return
|
||||
}
|
||||
|
||||
setConsumingVoucherId(row.id)
|
||||
setActionLoading('consume')
|
||||
try {
|
||||
const response = await consumeAdminKuaishouIndustryVoucher({
|
||||
sellerId: row.sellerId,
|
||||
oid: row.oid,
|
||||
orderId: row.oid,
|
||||
taskId: row.taskId || '',
|
||||
voucherCode: row.voucherCode,
|
||||
eticketType: DEFAULT_ETICKET_TYPE,
|
||||
consumeType: 'consume',
|
||||
serialNum: row.consumeSerialNum || undefined,
|
||||
etickets: [{ id: row.voucherCode, code: row.voucherCode, num: 1 }],
|
||||
})
|
||||
if (!isSupportOnly) {
|
||||
publishActionResult(
|
||||
buildIndustryActionResultView('consume', '手动核销', response.data),
|
||||
)
|
||||
}
|
||||
showSuccess(`券码 ${row.voucherCode} 已核销`)
|
||||
await loadVouchers()
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : '核销失败'
|
||||
if (!isSupportOnly) {
|
||||
publishActionResult(
|
||||
buildIndustryActionResultView('consume', '手动核销', {
|
||||
success: false,
|
||||
error: message,
|
||||
}),
|
||||
)
|
||||
}
|
||||
showError(message)
|
||||
} finally {
|
||||
setConsumingVoucherId(null)
|
||||
setActionLoading('')
|
||||
}
|
||||
}
|
||||
|
||||
async function runResendCode() {
|
||||
setActionLoading('resend')
|
||||
try {
|
||||
@@ -403,7 +520,7 @@ export default function AdminKuaishouIndustryPage() {
|
||||
const [begin, end] = refundDateRange || []
|
||||
const sellerId = resolveRefundSellerId(refundListForm.sellerId)
|
||||
if (!sellerId && refundShopOptions.length > 1) {
|
||||
showError('请先选择快手售后退款店铺 sellerId')
|
||||
showError('请先选择快手售后退款店铺')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -429,7 +546,7 @@ export default function AdminKuaishouIndustryPage() {
|
||||
async function runApproveRefund() {
|
||||
const sellerId = resolveRefundSellerId(approveForm.sellerId)
|
||||
if (!sellerId && refundShopOptions.length > 1) {
|
||||
showError('请先选择快手售后退款店铺 sellerId')
|
||||
showError('请先选择快手售后退款店铺')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -445,7 +562,7 @@ export default function AdminKuaishouIndustryPage() {
|
||||
async function runDisagreeRefund() {
|
||||
const sellerId = resolveRefundSellerId(disagreeForm.sellerId)
|
||||
if (!sellerId && refundShopOptions.length > 1) {
|
||||
showError('请先选择快手售后退款店铺 sellerId')
|
||||
showError('请先选择快手售后退款店铺')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -537,33 +654,47 @@ export default function AdminKuaishouIndustryPage() {
|
||||
showSuccess('已填入退款操作区')
|
||||
}
|
||||
|
||||
const tabItems: TabsProps['items'] = [
|
||||
{
|
||||
key: 'vouchers',
|
||||
label: '券码操作',
|
||||
children: renderVoucherTab(),
|
||||
},
|
||||
...(canManageRefund
|
||||
? [
|
||||
{
|
||||
key: 'refunds',
|
||||
label: '售后退款',
|
||||
children: renderRefundTab(),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
key: 'result',
|
||||
label: lastActionResult ? `接口结果 · ${lastActionResult.title}` : '接口结果',
|
||||
children: renderResultTab(),
|
||||
},
|
||||
]
|
||||
const tabItems: TabsProps['items'] = isSupportOnly
|
||||
? [
|
||||
{
|
||||
key: 'support-consume',
|
||||
label: '券码核销',
|
||||
children: renderSupportConsumeTab(),
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
key: 'vouchers',
|
||||
label: '券码操作',
|
||||
children: renderVoucherTab(),
|
||||
},
|
||||
...(canManageRefund
|
||||
? [
|
||||
{
|
||||
key: 'refunds',
|
||||
label: '售后退款',
|
||||
children: renderRefundTab(),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
key: 'result',
|
||||
label: lastActionResult ? `接口结果 · ${lastActionResult.title}` : '接口结果',
|
||||
children: renderResultTab(),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="page-stack kuaishou-industry-page">
|
||||
<PageHeader
|
||||
title="快手电子凭证"
|
||||
description={canManageRefund ? '行业电子凭证接口工具与售后处理' : '行业电子凭证接口工具'}
|
||||
description={
|
||||
isSupportOnly
|
||||
? '客服核销:查询本地券码并执行核销'
|
||||
: canManageRefund
|
||||
? '行业电子凭证接口工具与售后处理'
|
||||
: '行业电子凭证接口工具'
|
||||
}
|
||||
/>
|
||||
|
||||
<Tabs
|
||||
@@ -574,47 +705,63 @@ export default function AdminKuaishouIndustryPage() {
|
||||
</div>
|
||||
)
|
||||
|
||||
function renderVoucherTab() {
|
||||
function renderVoucherFilters() {
|
||||
return (
|
||||
<div className="page-stack">
|
||||
<Card>
|
||||
<Space wrap className="filter-form">
|
||||
<Input
|
||||
<Card>
|
||||
<Space wrap className="filter-form">
|
||||
<Input
|
||||
allowClear
|
||||
placeholder="订单号 oid"
|
||||
value={voucherFilters.oid}
|
||||
onChange={(event) =>
|
||||
setVoucherFilters({
|
||||
...voucherFilters,
|
||||
oid: event.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
allowClear
|
||||
placeholder="券码"
|
||||
value={voucherFilters.voucherCode}
|
||||
onChange={(event) =>
|
||||
setVoucherFilters({
|
||||
...voucherFilters,
|
||||
voucherCode: event.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
allowClear
|
||||
placeholder="任务 ID"
|
||||
value={voucherFilters.taskId}
|
||||
onChange={(event) =>
|
||||
setVoucherFilters({
|
||||
...voucherFilters,
|
||||
taskId: event.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
{shopFilterOptions.length > 0 ? (
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="订单号 oid"
|
||||
value={voucherFilters.oid}
|
||||
onChange={(event) =>
|
||||
setVoucherFilters({
|
||||
...voucherFilters,
|
||||
oid: event.target.value,
|
||||
showSearch
|
||||
optionFilterProp="label"
|
||||
placeholder="按店铺筛选"
|
||||
style={{ minWidth: 240 }}
|
||||
value={voucherFilters.sellerId || undefined}
|
||||
options={shopFilterOptions}
|
||||
onChange={(value) => {
|
||||
void loadVouchers({
|
||||
page: 1,
|
||||
sellerId: String(value || '').trim(),
|
||||
})
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
allowClear
|
||||
placeholder="券码"
|
||||
value={voucherFilters.voucherCode}
|
||||
onChange={(event) =>
|
||||
setVoucherFilters({
|
||||
...voucherFilters,
|
||||
voucherCode: event.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
allowClear
|
||||
placeholder="任务 ID"
|
||||
value={voucherFilters.taskId}
|
||||
onChange={(event) =>
|
||||
setVoucherFilters({
|
||||
...voucherFilters,
|
||||
taskId: event.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
allowClear
|
||||
placeholder="卖家 ID"
|
||||
placeholder="店铺 ID"
|
||||
value={voucherFilters.sellerId}
|
||||
onChange={(event) =>
|
||||
setVoucherFilters({
|
||||
@@ -623,50 +770,71 @@ export default function AdminKuaishouIndustryPage() {
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="券码状态"
|
||||
style={{ width: 132 }}
|
||||
value={voucherFilters.status || undefined}
|
||||
options={[
|
||||
{ label: '未使用', value: 'UNUSED' },
|
||||
{ label: '已核销', value: 'CONSUMED' },
|
||||
{ label: '已销毁', value: 'DESTROYED' },
|
||||
]}
|
||||
onChange={(value) => setVoucherFilters({ ...voucherFilters, status: value || '' })}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SearchOutlined />}
|
||||
loading={voucherLoading}
|
||||
onClick={() => loadVouchers({ page: 1 })}
|
||||
>
|
||||
查询
|
||||
</Button>
|
||||
</Space>
|
||||
</Card>
|
||||
)}
|
||||
<Select
|
||||
allowClear
|
||||
placeholder="券码状态"
|
||||
style={{ width: 132 }}
|
||||
value={voucherFilters.status || undefined}
|
||||
options={[
|
||||
{ label: '未使用', value: 'UNUSED' },
|
||||
{ label: '已核销', value: 'CONSUMED' },
|
||||
{ label: '已销毁', value: 'DESTROYED' },
|
||||
]}
|
||||
onChange={(value) => setVoucherFilters({ ...voucherFilters, status: value || '' })}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<SearchOutlined />}
|
||||
loading={voucherLoading}
|
||||
onClick={() => loadVouchers({ page: 1 })}
|
||||
>
|
||||
查询
|
||||
</Button>
|
||||
</Space>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
function renderVoucherTable(scrollX = 1180) {
|
||||
return (
|
||||
<Table
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={vouchers}
|
||||
loading={voucherLoading}
|
||||
scroll={{ x: scrollX }}
|
||||
pagination={buildAdminTablePagination({
|
||||
current: voucherFilters.page,
|
||||
pageSize: voucherFilters.pageSize,
|
||||
total: voucherTotal,
|
||||
onChange: (page, pageSize) => loadVouchers({ page, pageSize }),
|
||||
})}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function renderSupportConsumeTab() {
|
||||
return (
|
||||
<div className="page-stack">
|
||||
{renderVoucherFilters()}
|
||||
<Card title="本地券码">{renderVoucherTable(1080)}</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function renderVoucherTab() {
|
||||
return (
|
||||
<div className="page-stack">
|
||||
{renderVoucherFilters()}
|
||||
|
||||
<div className="kuaishou-industry-split">
|
||||
<Card title="本地券码">
|
||||
<Table
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={vouchers}
|
||||
loading={voucherLoading}
|
||||
scroll={{ x: 1180 }}
|
||||
pagination={buildAdminTablePagination({
|
||||
current: voucherFilters.page,
|
||||
pageSize: voucherFilters.pageSize,
|
||||
total: voucherTotal,
|
||||
onChange: (page, pageSize) => loadVouchers({ page, pageSize }),
|
||||
})}
|
||||
/>
|
||||
</Card>
|
||||
<Card title="本地券码">{renderVoucherTable()}</Card>
|
||||
|
||||
<Card title="接口操作">
|
||||
<div className="kuaishou-industry-tool-grid">
|
||||
<Input
|
||||
placeholder="卖家 ID"
|
||||
placeholder="店铺 ID"
|
||||
value={toolForm.sellerId}
|
||||
onChange={(event) => updateToolForm({ sellerId: event.target.value })}
|
||||
/>
|
||||
@@ -1115,7 +1283,7 @@ export default function AdminKuaishouIndustryPage() {
|
||||
return (
|
||||
<Input
|
||||
allowClear
|
||||
placeholder="卖家 ID"
|
||||
placeholder="店铺 ID"
|
||||
value={normalizedValue}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
/>
|
||||
@@ -1123,6 +1291,28 @@ export default function AdminKuaishouIndustryPage() {
|
||||
}
|
||||
}
|
||||
|
||||
function renderShopCell(
|
||||
shopName?: string | null,
|
||||
sellerId?: string | null,
|
||||
tokenMasked?: string | null,
|
||||
) {
|
||||
const name = String(shopName || '').trim()
|
||||
const id = String(sellerId || '').trim()
|
||||
const token = String(tokenMasked || '').trim()
|
||||
|
||||
if (!name && !id) {
|
||||
return <span>-</span>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="cell-stack">
|
||||
<span>{name || id}</span>
|
||||
{name && id ? <span className="muted">ID:{id}</span> : null}
|
||||
{token ? <span className="muted">Token:{token}</span> : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function extractRefundRows(
|
||||
response: Record<string, unknown> | null,
|
||||
): Array<Record<string, unknown>> {
|
||||
@@ -1409,11 +1599,12 @@ function getVoucherStatusLabel(status: string) {
|
||||
return status || '-'
|
||||
}
|
||||
|
||||
function formatShopOptionLabel(shop: AdminKuaishouIndustryShopConfig) {
|
||||
const name = shop.customShopName || shop.shopName || '未命名店铺'
|
||||
const tokenStatus = shop.hasAccessToken ? '' : ' / 未授权'
|
||||
function formatShopOptionLabel(shop: AdminKuaishouIndustryShopOption) {
|
||||
const name = String(shop.shopName || '').trim() || '未命名店铺'
|
||||
const id = shop.sellerId || shop.shopId || ''
|
||||
const disabledSuffix = shop.enabled === false ? ' · 已停用' : ''
|
||||
|
||||
return `${name} / ${shop.sellerId}${tokenStatus}`
|
||||
return id ? `${name}(${id})${disabledSuffix}` : `${name}${disabledSuffix}`
|
||||
}
|
||||
|
||||
function formatTimestamp(value: unknown) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
AdminKuaishouIndustryRefundApprovePayload,
|
||||
AdminKuaishouIndustryRefundDisagreePayload,
|
||||
AdminKuaishouIndustryRefundListPayload,
|
||||
AdminKuaishouIndustryShopListResult,
|
||||
AdminKuaishouIndustryVoucherConsumeResult,
|
||||
AdminKuaishouIndustryVoucherListResult,
|
||||
AdminKuaishouIndustryVoucherResendResult,
|
||||
@@ -17,6 +18,10 @@ export function fetchAdminKuaishouIndustryVouchers(params?: Record<string, unkno
|
||||
)
|
||||
}
|
||||
|
||||
export function fetchAdminKuaishouIndustryShops() {
|
||||
return apiGet<AdminKuaishouIndustryShopListResult>('/api/v1/admin/kuaishou-industry/shops')
|
||||
}
|
||||
|
||||
export function listAdminKuaishouIndustryRefunds(
|
||||
payload: AdminKuaishouIndustryRefundListPayload,
|
||||
) {
|
||||
|
||||
@@ -32,6 +32,8 @@ export type {
|
||||
AdminKuaishouIndustryOpenApiResult,
|
||||
AdminKuaishouIndustryVoucher,
|
||||
AdminKuaishouIndustryVoucherListResult,
|
||||
AdminKuaishouIndustryShopOption,
|
||||
AdminKuaishouIndustryShopListResult,
|
||||
AdminKuaishouIndustryVoucherConsumeResult,
|
||||
AdminKuaishouIndustryVoucherResendResult,
|
||||
AdminKuaishouIndustryRefundListPayload,
|
||||
|
||||
@@ -19,6 +19,8 @@ export interface AdminKuaishouIndustryVoucher {
|
||||
taskId: number | null
|
||||
unitIndex: number
|
||||
sellerId: string
|
||||
/** 来自行业店铺配置的展示名(customShopName 优先) */
|
||||
shopName: string
|
||||
tokenMasked: string
|
||||
status: string
|
||||
validStartTime: number
|
||||
@@ -41,6 +43,18 @@ export interface AdminKuaishouIndustryVoucherListResult {
|
||||
pagination: AdminPagination
|
||||
}
|
||||
|
||||
/** 电子凭证页筛选用的轻量店铺选项(无 token 等敏感字段) */
|
||||
export interface AdminKuaishouIndustryShopOption {
|
||||
sellerId: string
|
||||
shopId: string
|
||||
shopName: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export interface AdminKuaishouIndustryShopListResult {
|
||||
shops: AdminKuaishouIndustryShopOption[]
|
||||
}
|
||||
|
||||
export interface AdminKuaishouIndustryVoucherConsumeResult {
|
||||
success: boolean
|
||||
voucher: AdminKuaishouIndustryVoucher
|
||||
|
||||
Reference in New Issue
Block a user