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