恢复测试订单创建
This commit is contained in:
@@ -5,6 +5,7 @@ import type {
|
||||
ApiCredential,
|
||||
CallbackCredential,
|
||||
CallbackSubscription,
|
||||
CreateTestOrderResult,
|
||||
FulfillmentOrder,
|
||||
LoginResult,
|
||||
Merchant,
|
||||
@@ -62,6 +63,12 @@ export const merchantApi = {
|
||||
request.patch(`/merchant/products/${id}`, data).then((r) => r.data.data),
|
||||
orders: (params?: Record<string, unknown>) =>
|
||||
request.get('/merchant/orders', { params }).then((r) => r.data.data as PageResult<FulfillmentOrder>),
|
||||
createTestOrder: (data: {
|
||||
sku: string
|
||||
buyer_reference?: string
|
||||
note?: string
|
||||
fulfillment_status?: 'pending' | 'failed'
|
||||
}) => request.post('/merchant/orders/test', data).then((r) => r.data.data as CreateTestOrderResult),
|
||||
wallet: () =>
|
||||
request.get('/merchant/wallet').then((r) => r.data.data as WalletAccount),
|
||||
ledger: (params?: Record<string, unknown>) =>
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import {
|
||||
ApiOutlined,
|
||||
CopyOutlined,
|
||||
LinkOutlined,
|
||||
PlusOutlined,
|
||||
ReloadOutlined,
|
||||
WalletOutlined,
|
||||
@@ -29,6 +30,7 @@ import type {
|
||||
ApiCredential,
|
||||
CallbackCredential,
|
||||
CallbackSubscription,
|
||||
CreateTestOrderResult,
|
||||
FulfillmentOrder,
|
||||
Merchant,
|
||||
MerchantMember,
|
||||
@@ -75,6 +77,13 @@ const eventOptions = [
|
||||
{ value: 'order.cancelled', label: '订单取消' },
|
||||
]
|
||||
|
||||
const deliveryTestUrl = import.meta.env.VITE_DELIVERY_TEST_URL || 'https://www.jxya.top/test-delivery/dlc/'
|
||||
|
||||
const testOrderStatusOptions = [
|
||||
{ value: 'pending', label: '已支付,可发货' },
|
||||
{ value: 'failed', label: '发货失败,可重试' },
|
||||
]
|
||||
|
||||
export default function MerchantCenter() {
|
||||
const [merchant, setMerchant] = useState<Merchant | null>(null)
|
||||
const [merchantRole, setMerchantRole] = useState<MerchantMember['role']>()
|
||||
@@ -95,16 +104,25 @@ export default function MerchantCenter() {
|
||||
const [callbackOpen, setCallbackOpen] = useState(false)
|
||||
const [callbackCredential, setCallbackCredential] = useState<CallbackCredential | null>(null)
|
||||
const [memberOpen, setMemberOpen] = useState(false)
|
||||
const [testOrderOpen, setTestOrderOpen] = useState(false)
|
||||
const [testOrderProducts, setTestOrderProducts] = useState<MerchantProduct[]>([])
|
||||
const [testOrderProductsLoading, setTestOrderProductsLoading] = useState(false)
|
||||
const [testOrderResult, setTestOrderResult] = useState<CreateTestOrderResult | null>(null)
|
||||
const [productForm] = Form.useForm()
|
||||
const [walletForm] = Form.useForm()
|
||||
const [apiClientForm] = Form.useForm()
|
||||
const [callbackForm] = Form.useForm()
|
||||
const [memberForm] = Form.useForm()
|
||||
const [testOrderForm] = Form.useForm()
|
||||
|
||||
const canManage = merchantRole === 'owner' || merchantRole === 'operator'
|
||||
const canFinance = merchantRole === 'owner' || merchantRole === 'finance'
|
||||
const enabledFeatures = useMemo(() => new Set(featuresToList(merchant?.features)), [merchant?.features])
|
||||
const hasFeature = useCallback((feature: string) => !merchant || enabledFeatures.has(feature), [enabledFeatures, merchant])
|
||||
const testOrderProductOptions = useMemo(() => testOrderProducts.map((item) => ({
|
||||
value: item.sku,
|
||||
label: productOptionLabel(item),
|
||||
})), [testOrderProducts])
|
||||
|
||||
const loadCurrent = useCallback(async () => {
|
||||
const data = await merchantApi.current()
|
||||
@@ -266,6 +284,49 @@ export default function MerchantCenter() {
|
||||
}
|
||||
}
|
||||
|
||||
const openTestOrderCreate = () => {
|
||||
testOrderForm.resetFields()
|
||||
testOrderForm.setFieldsValue({
|
||||
buyer_reference: '测试买家',
|
||||
note: '联调测试订单',
|
||||
fulfillment_status: 'pending',
|
||||
})
|
||||
setTestOrderOpen(true)
|
||||
setTestOrderProductsLoading(true)
|
||||
merchantApi.products({ page: 1, size: 100 })
|
||||
.then((data) => {
|
||||
const activeProducts = (data.list || []).filter((item) => item.status === 'active')
|
||||
setTestOrderProducts(activeProducts)
|
||||
if (activeProducts.length > 0) {
|
||||
testOrderForm.setFieldsValue({ sku: activeProducts[0].sku })
|
||||
} else {
|
||||
message.warning('没有可用商品')
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e instanceof Error ? e.message : '商品加载失败')
|
||||
})
|
||||
.finally(() => setTestOrderProductsLoading(false))
|
||||
}
|
||||
|
||||
const submitTestOrder = async () => {
|
||||
const values = await testOrderForm.validateFields()
|
||||
try {
|
||||
const result = await merchantApi.createTestOrder({
|
||||
sku: values.sku,
|
||||
buyer_reference: values.buyer_reference,
|
||||
note: values.note,
|
||||
fulfillment_status: values.fulfillment_status,
|
||||
})
|
||||
setTestOrderOpen(false)
|
||||
setTestOrderResult(result)
|
||||
message.success('测试订单已创建')
|
||||
loadOrders()
|
||||
} catch (e) {
|
||||
message.error(e instanceof Error ? e.message : '创建失败')
|
||||
}
|
||||
}
|
||||
|
||||
const submitAPIClient = async () => {
|
||||
const values = await apiClientForm.validateFields()
|
||||
try {
|
||||
@@ -470,15 +531,30 @@ export default function MerchantCenter() {
|
||||
label: '履约订单',
|
||||
disabled: !hasFeature('orders'),
|
||||
children: (
|
||||
<Table
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
columns={orderColumns}
|
||||
dataSource={orders.list}
|
||||
tableLayout="fixed"
|
||||
scroll={{ x: 1300 }}
|
||||
pagination={pageConfig(orders, loadOrders)}
|
||||
/>
|
||||
<Space direction="vertical" style={{ width: '100%' }} size="middle">
|
||||
<Space style={{ width: '100%', justifyContent: 'space-between' }}>
|
||||
<Typography.Text type="secondary">订单号用于发货平台查询。</Typography.Text>
|
||||
{canManage && (
|
||||
<Space>
|
||||
<Button icon={<LinkOutlined />} href={deliveryTestUrl} target="_blank" rel="noreferrer">
|
||||
发货测试页
|
||||
</Button>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={openTestOrderCreate}>
|
||||
创建测试订单
|
||||
</Button>
|
||||
</Space>
|
||||
)}
|
||||
</Space>
|
||||
<Table
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
columns={orderColumns}
|
||||
dataSource={orders.list}
|
||||
tableLayout="fixed"
|
||||
scroll={{ x: 1300 }}
|
||||
pagination={pageConfig(orders, loadOrders)}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -627,6 +703,69 @@ export default function MerchantCenter() {
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal title="创建测试订单" open={testOrderOpen} onOk={submitTestOrder} onCancel={() => setTestOrderOpen(false)} destroyOnClose width={560}>
|
||||
<Form form={testOrderForm} layout="vertical" style={{ marginTop: 16 }}>
|
||||
<Form.Item name="sku" label="商品皮肤" rules={[{ required: true, message: '请选择商品' }]}>
|
||||
<Select
|
||||
showSearch
|
||||
loading={testOrderProductsLoading}
|
||||
optionFilterProp="label"
|
||||
options={testOrderProductOptions}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="buyer_reference" label="买家名称" rules={[{ max: 128 }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="fulfillment_status" label="可发货状态" rules={[{ required: true }]}>
|
||||
<Select options={testOrderStatusOptions} />
|
||||
</Form.Item>
|
||||
<Form.Item name="note" label="备注" rules={[{ max: 512 }]}>
|
||||
<Input.TextArea rows={3} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title="测试订单"
|
||||
open={!!testOrderResult}
|
||||
onCancel={() => setTestOrderResult(null)}
|
||||
footer={(
|
||||
<Space>
|
||||
<Button onClick={() => setTestOrderResult(null)}>关闭</Button>
|
||||
<Button
|
||||
icon={<CopyOutlined />}
|
||||
onClick={() => navigator.clipboard.writeText(testOrderResult?.order.order_no || '').then(() => message.success('已复制'))}
|
||||
>
|
||||
复制订单号
|
||||
</Button>
|
||||
<Button type="primary" icon={<LinkOutlined />} href={deliveryTestUrl} target="_blank" rel="noreferrer">
|
||||
发货测试页
|
||||
</Button>
|
||||
</Space>
|
||||
)}
|
||||
>
|
||||
{testOrderResult && (
|
||||
<Descriptions size="small" bordered column={1} style={{ marginTop: 16 }}>
|
||||
<Descriptions.Item label="订单号">
|
||||
<Typography.Text copyable>{testOrderResult.order.order_no}</Typography.Text>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="SKU">
|
||||
<Typography.Text code>{testOrderResult.order.product_sku}</Typography.Text>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="可发货">
|
||||
<Tag color={testOrderResult.can_ship ? 'green' : 'red'}>
|
||||
{testOrderResult.can_ship ? 'can_ship=true' : 'can_ship=false'}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="测试页">
|
||||
<Typography.Link href={deliveryTestUrl} target="_blank" rel="noreferrer">
|
||||
{deliveryTestUrl}
|
||||
</Typography.Link>
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
<Modal title="钱包调整" open={walletOpen} onOk={submitWalletAdjust} onCancel={() => setWalletOpen(false)} destroyOnClose>
|
||||
<Form form={walletForm} layout="vertical" style={{ marginTop: 16 }}>
|
||||
<Form.Item name="amount" label="调整积分" rules={[{ required: true }]}>
|
||||
@@ -775,6 +914,11 @@ function activeStatusTag(value: string) {
|
||||
return value === 'active' ? <Tag color="green">启用</Tag> : <Tag>禁用</Tag>
|
||||
}
|
||||
|
||||
function productOptionLabel(item: MerchantProduct) {
|
||||
const name = item.display_name || item.product?.name || item.sku
|
||||
return name === item.sku ? item.sku : `${name} / ${item.sku}`
|
||||
}
|
||||
|
||||
function paymentStatusTag(value: string) {
|
||||
const item = paymentStatusMap[value] || { color: 'default', text: value }
|
||||
return <Tag color={item.color}>{item.text}</Tag>
|
||||
|
||||
@@ -118,6 +118,12 @@ export interface FulfillmentOrder {
|
||||
cancelled_at?: string | null
|
||||
}
|
||||
|
||||
export interface CreateTestOrderResult {
|
||||
order: FulfillmentOrder
|
||||
can_ship: boolean
|
||||
cannot_ship_reason?: string
|
||||
}
|
||||
|
||||
export interface ApiClient {
|
||||
id: number
|
||||
merchant_id: number
|
||||
|
||||
Reference in New Issue
Block a user