Merge branch 'main' into dev
This commit is contained in:
@@ -29,6 +29,7 @@ import {
|
||||
Typography,
|
||||
} from 'antd'
|
||||
import PercentTable from '@/components/common/PercentTable'
|
||||
import { buildAdminTablePagination } from '@/utils/admin-pagination'
|
||||
import type { TableColumnsType } from 'antd'
|
||||
import { useState } from 'react'
|
||||
|
||||
@@ -880,18 +881,16 @@ export default function ConsignmentInventoryPanel() {
|
||||
dataSource={items}
|
||||
loading={inventoriesQuery.isLoading}
|
||||
scroll={{ x: 1590 }}
|
||||
pagination={{
|
||||
pagination={buildAdminTablePagination({
|
||||
current: page,
|
||||
pageSize,
|
||||
total,
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
showTotal: (t) => `共 ${t} 件物品`,
|
||||
onChange: (p, ps) => {
|
||||
setPage(p)
|
||||
setPageSize(ps)
|
||||
},
|
||||
}}
|
||||
})}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
App,
|
||||
} from 'antd'
|
||||
import PercentTable from '@/components/common/PercentTable'
|
||||
import { buildAdminTablePagination } from '@/utils/admin-pagination'
|
||||
import type { TableColumnsType } from 'antd'
|
||||
import { useState } from 'react'
|
||||
|
||||
@@ -474,17 +475,16 @@ export default function ConsignmentSubmissionsPanel() {
|
||||
dataSource={submissions}
|
||||
loading={submissionsQuery.isLoading}
|
||||
scroll={{ x: 1490 }}
|
||||
pagination={{
|
||||
pagination={buildAdminTablePagination({
|
||||
current: submissionPage,
|
||||
pageSize: submissionPageSize,
|
||||
total: submissionTotal,
|
||||
showSizeChanger: true,
|
||||
showTotal: (t) => `共 ${t} 条提报记录`,
|
||||
onChange: (p, ps) => {
|
||||
setSubmissionPage(p)
|
||||
setSubmissionPageSize(ps)
|
||||
},
|
||||
}}
|
||||
})}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
|
||||
@@ -44,7 +44,11 @@ import {
|
||||
import type { KuaishouMatchSource, WorkProductMatchLog } from '@/types/worker-platform'
|
||||
import type { AdminKuaishouIndustryShopOption } from '@/types/admin'
|
||||
import { formatAdminDateTime } from '@/utils/admin-time'
|
||||
import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination'
|
||||
import {
|
||||
ADMIN_DEFAULT_PAGE_SIZE,
|
||||
buildAdminLocalTablePagination,
|
||||
buildAdminTablePagination,
|
||||
} from '@/utils/admin-pagination'
|
||||
import { formatMoney } from './shared'
|
||||
|
||||
export default function ProductMatchPanel() {
|
||||
@@ -366,7 +370,7 @@ export default function ProductMatchPanel() {
|
||||
loading={sourcesQuery.isLoading}
|
||||
columns={sourceColumns}
|
||||
dataSource={sources}
|
||||
pagination={{ pageSize: 10, showSizeChanger: false }}
|
||||
pagination={buildAdminLocalTablePagination()}
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Modal,
|
||||
Popconfirm,
|
||||
Radio,
|
||||
Select,
|
||||
@@ -42,7 +41,6 @@ import {
|
||||
fetchAdminWorkProductRules,
|
||||
reprocessAdminKuaishouWorkOrderMatches,
|
||||
saveAdminWorkProductRule,
|
||||
syncAdminWorkProductRulePrice,
|
||||
} from '@/services/admin'
|
||||
import type {
|
||||
CollectField,
|
||||
@@ -73,7 +71,6 @@ export default function ProductRulesPanel() {
|
||||
const hasInitializedRuleSelection = useRef(false)
|
||||
|
||||
const [editingRule, setEditingRule] = useState<WorkProductRule | null>(null)
|
||||
const [syncingPrice, setSyncingPrice] = useState(false)
|
||||
const [exportingRules, setExportingRules] = useState(false)
|
||||
const [activeMappingId, setActiveMappingId] = useState<number | 'new' | null>(null)
|
||||
const [sourcePrefill, setSourcePrefill] = useState<SourcePrefill | null>(null)
|
||||
@@ -296,9 +293,7 @@ export default function ProductRulesPanel() {
|
||||
itemTitleMatchType: rule.match.itemTitleMatchType,
|
||||
skuNickMatchType: rule.match.skuNickMatchType,
|
||||
categoryId: rule.categoryId || undefined,
|
||||
pricingMode: rule.unitPriceFen > 0 ? 'unit' : 'fixed',
|
||||
rewardAmount: rule.rewardAmount / 100,
|
||||
unitPrice: rule.unitPriceFen / 100,
|
||||
pricingMode: rule.pricingMode || 'fixed',
|
||||
sharingEnabled: rule.sharing?.enabled || false,
|
||||
sharingAutoFromOrder: rule.sharing?.autoFromOrder || false,
|
||||
sharingTotalQuantity: hasManualSharingOverride ? rule.sharing?.totalQuantity : undefined,
|
||||
@@ -342,9 +337,6 @@ export default function ProductRulesPanel() {
|
||||
itemTitleMatchType?: 'contains' | 'exact'
|
||||
skuNickMatchType?: 'contains' | 'exact'
|
||||
categoryId?: number
|
||||
rewardAmount?: number
|
||||
pricingMode?: string
|
||||
unitPrice?: number
|
||||
sharingEnabled?: boolean
|
||||
sharingAutoFromOrder?: boolean
|
||||
sharingTotalQuantity?: number
|
||||
@@ -358,10 +350,10 @@ export default function ProductRulesPanel() {
|
||||
fieldsText?: string
|
||||
enabled?: boolean
|
||||
autoCreate?: boolean
|
||||
pricingMode?: 'fixed' | 'unit'
|
||||
sortOrder?: number
|
||||
}) {
|
||||
try {
|
||||
const unitMode = values.pricingMode === 'unit'
|
||||
const isFriendGift = values.acceptanceMode === 'friend_gift'
|
||||
if (isFriendGift && values.sharingEnabled === true) {
|
||||
message.error('好友赠送验收模式不支持拼单,请先关闭拼单')
|
||||
@@ -372,12 +364,11 @@ export default function ProductRulesPanel() {
|
||||
? Number(values.sharingTotalQuantity || 0) > 0 ||
|
||||
Number(values.sharingUnitReward || 0) > 0
|
||||
: hasManualSharingInput(values.sharingTotalQuantity, values.sharingUnitReward)
|
||||
const { pricingMode: _pricingMode, giftCooldownUnit, giftCooldownValue, ...payload } = values
|
||||
const { giftCooldownUnit, giftCooldownValue, ...payload } = values
|
||||
const res = await saveAdminWorkProductRule({
|
||||
...payload,
|
||||
ruleId: editingRule?.ruleId,
|
||||
ruleKey: editingRule?.ruleKey || '',
|
||||
unitPrice: unitMode ? Number(values.unitPrice || 0) : 0,
|
||||
acceptanceMode: isFriendGift ? 'friend_gift' : 'standard',
|
||||
giftCooldownHours:
|
||||
isFriendGift && giftCooldownUnit !== 'minutes' ? Number(giftCooldownValue || 0) : 0,
|
||||
@@ -385,10 +376,16 @@ export default function ProductRulesPanel() {
|
||||
isFriendGift && giftCooldownUnit === 'minutes' ? Number(giftCooldownValue || 0) : 0,
|
||||
sharingEnabled: isFriendGift ? false : values.sharingEnabled === true,
|
||||
sharingAutoFromOrder:
|
||||
unitMode && values.sharingEnabled === true && !hasManualSharingOverride,
|
||||
values.pricingMode === 'unit' &&
|
||||
values.sharingEnabled === true &&
|
||||
!hasManualSharingOverride,
|
||||
})
|
||||
message.success(editingRule ? '接单模板已更新' : '接单模板已创建')
|
||||
const savedRule = res.data?.rule
|
||||
if (res.data?.costConfigurationRequired) {
|
||||
message.warning('模板已保存为停用。请到【商品与成本中心】配置成本后再启用')
|
||||
} else {
|
||||
message.success(editingRule ? '接单模板已更新' : '接单模板已创建')
|
||||
}
|
||||
if (savedRule) {
|
||||
editRule(savedRule)
|
||||
} else {
|
||||
@@ -401,42 +398,15 @@ export default function ProductRulesPanel() {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ['admin-worker-platform-summary'],
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
predicate: (query) => String(query.queryKey[0] || '').startsWith('admin-cost-center'),
|
||||
}),
|
||||
])
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : '保存失败')
|
||||
}
|
||||
}
|
||||
|
||||
function confirmSyncRulePrice() {
|
||||
if (!editingRule) return
|
||||
Modal.confirm({
|
||||
title: '同步模板价格',
|
||||
content:
|
||||
'将同步接单大厅中该模板的待抢单价格。部分拼单仅调整剩余份额价格,已加入份额报酬保持不变。',
|
||||
okText: '确认同步',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
setSyncingPrice(true)
|
||||
try {
|
||||
const response = await syncAdminWorkProductRulePrice(editingRule.ruleId)
|
||||
const result = response.data
|
||||
message.success(
|
||||
`已同步 ${result.updatedCount} 单:待抢单 ${result.updatedNormalCount} 单,部分拼单 ${result.updatedPartialSharingCount} 单`,
|
||||
)
|
||||
await Promise.all([
|
||||
queryClient.invalidateQueries({ queryKey: ['admin-worker-platform-product-rules'] }),
|
||||
queryClient.invalidateQueries({ queryKey: ['admin-worker-platform-orders'] }),
|
||||
])
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : '同步价格失败')
|
||||
throw error
|
||||
} finally {
|
||||
setSyncingPrice(false)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function toggleRuleEnabled(rule: WorkProductRule, enabled: boolean) {
|
||||
try {
|
||||
const hasManualSharingOverride = hasManualSharingInput(
|
||||
@@ -450,11 +420,12 @@ export default function ProductRulesPanel() {
|
||||
platform: rule.platform,
|
||||
productName: rule.productName,
|
||||
categoryId: rule.categoryId || undefined,
|
||||
rewardAmount: rule.rewardAmount / 100,
|
||||
unitPrice: rule.unitPriceFen / 100,
|
||||
pricingMode: rule.pricingMode,
|
||||
sharingEnabled: rule.sharing?.enabled || false,
|
||||
sharingAutoFromOrder:
|
||||
rule.unitPriceFen > 0 && rule.sharing?.enabled === true && !hasManualSharingOverride,
|
||||
rule.pricingMode === 'unit' &&
|
||||
rule.sharing?.enabled === true &&
|
||||
!hasManualSharingOverride,
|
||||
sharingTotalQuantity: hasManualSharingOverride ? rule.sharing?.totalQuantity : undefined,
|
||||
sharingUnitReward: hasManualSharingOverride
|
||||
? (rule.sharing?.unitReward || 0) / 100
|
||||
@@ -473,6 +444,9 @@ export default function ProductRulesPanel() {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ['admin-worker-platform-product-rules'],
|
||||
})
|
||||
await queryClient.invalidateQueries({
|
||||
predicate: (query) => String(query.queryKey[0] || '').startsWith('admin-cost-center'),
|
||||
})
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : '修改状态失败')
|
||||
}
|
||||
@@ -782,19 +756,22 @@ export default function ProductRulesPanel() {
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '接单金额',
|
||||
title: '接单价格',
|
||||
key: 'reward',
|
||||
width: '20%',
|
||||
render: (_, row) => (
|
||||
<div className="cell-stack">
|
||||
<Typography.Text strong style={{ color: '#3f8600', fontSize: 12 }}>
|
||||
{row.sharing?.autoFromOrder ? '随订单计算' : formatMoney(row.rewardAmount)}
|
||||
</Typography.Text>
|
||||
{row.unitPriceFen > 0 ? (
|
||||
<Typography.Text type="secondary" style={{ fontSize: 11 }}>
|
||||
按件: {formatMoney(row.unitPriceFen)}
|
||||
{(row.pricingMode === 'unit' ? row.unitPriceFen : row.rewardAmount) > 0 ? (
|
||||
<Typography.Text strong style={{ color: '#3f8600', fontSize: 12 }}>
|
||||
{row.pricingMode === 'unit'
|
||||
? `${formatMoney(row.unitPriceFen)} / 件`
|
||||
: formatMoney(row.rewardAmount)}
|
||||
</Typography.Text>
|
||||
) : null}
|
||||
) : (
|
||||
<Tag color="orange" style={{ width: 'fit-content', margin: 0 }}>
|
||||
待成本配置
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
@@ -891,11 +868,6 @@ export default function ProductRulesPanel() {
|
||||
+ 新建模板
|
||||
</Button>
|
||||
) : null}
|
||||
{editingRule ? (
|
||||
<Button loading={syncingPrice} size="middle" onClick={confirmSyncRulePrice}>
|
||||
同步价格
|
||||
</Button>
|
||||
) : null}
|
||||
<Button type="primary" size="middle" htmlType="submit" form="product-rule-form">
|
||||
{editingRule ? '保存模板' : '创建模板'}
|
||||
</Button>
|
||||
@@ -913,10 +885,9 @@ export default function ProductRulesPanel() {
|
||||
matchType: 'contains',
|
||||
itemTitleMatchType: 'exact',
|
||||
skuNickMatchType: 'exact',
|
||||
pricingMode: 'fixed',
|
||||
rewardAmount: 25,
|
||||
enabled: true,
|
||||
enabled: false,
|
||||
autoCreate: true,
|
||||
pricingMode: 'fixed',
|
||||
sharingEnabled: false,
|
||||
acceptanceMode: 'standard',
|
||||
giftCooldownUnit: 'hours',
|
||||
@@ -926,23 +897,21 @@ export default function ProductRulesPanel() {
|
||||
}}
|
||||
onFinish={saveRule}
|
||||
>
|
||||
{editingRule ? (
|
||||
<Alert
|
||||
type="info"
|
||||
showIcon
|
||||
style={{ marginBottom: 16 }}
|
||||
message="全系统标准价格已统一收敛至【商品与成本中心】"
|
||||
description={
|
||||
<span>
|
||||
接单单价由商品与成本中心维护并提供同步发单功能。在此处价格为只读展示;如需改价或同步工单价格,请前往
|
||||
<a href="#/admin/cost-center" style={{ marginLeft: 4, fontWeight: 500 }}>
|
||||
【商品与成本中心】
|
||||
</a>
|
||||
进行修改。
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
<Alert
|
||||
type="info"
|
||||
showIcon
|
||||
style={{ marginBottom: 16 }}
|
||||
message="接单模板维护计价方式和履约规则"
|
||||
description={
|
||||
<span>
|
||||
在这里选择“固定金额”或“按 SKU 按件”;具体金额与价格同步统一由
|
||||
<a href="#/admin/cost-center" style={{ margin: '0 4px', fontWeight: 500 }}>
|
||||
商品与成本中心
|
||||
</a>
|
||||
管理。新模板默认停用,配置成本并启用模板后自动出现在商品目录。
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* 基础信息 */}
|
||||
<div className="form-grid-3col">
|
||||
@@ -965,58 +934,27 @@ export default function ProductRulesPanel() {
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
{/* Row 2: 计价方式 + 单价/金额 + 任务时限 */}
|
||||
{/* Row 2: 任务时限 */}
|
||||
<div className="form-grid-3col">
|
||||
<Form.Item label="计价方式" name="pricingMode">
|
||||
<Form.Item label="任务时限" name="timeoutMinutes">
|
||||
<InputNumber min={0} step={5} addonAfter="分钟" className="full-width" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="计价方式"
|
||||
name="pricingMode"
|
||||
rules={[{ required: true, message: '请选择计价方式' }]}
|
||||
extra="固定金额按整单;按 SKU 按件会把成本中心金额乘以订单件数"
|
||||
>
|
||||
<Radio.Group
|
||||
optionType="button"
|
||||
buttonStyle="solid"
|
||||
className="full-width"
|
||||
options={[
|
||||
{ value: 'fixed', label: '固定金额' },
|
||||
{ value: 'unit', label: '按件计价' },
|
||||
{ value: 'unit', label: '按 SKU 按件' },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item noStyle shouldUpdate>
|
||||
{({ getFieldValue }) =>
|
||||
getFieldValue('pricingMode') === 'unit' ? (
|
||||
<Form.Item
|
||||
label="按件单价"
|
||||
name="unitPrice"
|
||||
rules={[{ required: true, message: '请填写单价' }]}
|
||||
extra={editingRule ? '标准单价由商品与成本中心统一管理' : undefined}
|
||||
>
|
||||
<InputNumber
|
||||
min={0.01}
|
||||
step={1}
|
||||
addonAfter="元"
|
||||
className="full-width"
|
||||
disabled={Boolean(editingRule)}
|
||||
/>
|
||||
</Form.Item>
|
||||
) : (
|
||||
<Form.Item
|
||||
label="接单金额"
|
||||
name="rewardAmount"
|
||||
extra={editingRule ? '标准单价由商品与成本中心统一管理' : undefined}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
step={1}
|
||||
addonAfter="元"
|
||||
className="full-width"
|
||||
disabled={Boolean(editingRule)}
|
||||
/>
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="任务时限" name="timeoutMinutes">
|
||||
<InputNumber min={0} step={5} addonAfter="分钟" className="full-width" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
{/* 验收模式:好友赠送模式与拼单互斥 */}
|
||||
@@ -1126,11 +1064,19 @@ export default function ProductRulesPanel() {
|
||||
const sharingEnabled = getFieldValue('sharingEnabled')
|
||||
const unitMode = getFieldValue('pricingMode') === 'unit'
|
||||
const friendGiftMode = getFieldValue('acceptanceMode') === 'friend_gift'
|
||||
const hasConfiguredPrice =
|
||||
Number(editingRule?.unitPriceFen || 0) > 0 ||
|
||||
Number(editingRule?.rewardAmount || 0) > 0
|
||||
|
||||
return (
|
||||
<div className="switches-inline-group span-2">
|
||||
<Form.Item label="启用模板" name="enabled" valuePropName="checked">
|
||||
<Switch size="small" />
|
||||
<Form.Item
|
||||
label="启用模板"
|
||||
name="enabled"
|
||||
valuePropName="checked"
|
||||
tooltip={!hasConfiguredPrice ? '请先在商品与成本中心配置成本' : undefined}
|
||||
>
|
||||
<Switch size="small" disabled={!hasConfiguredPrice} />
|
||||
</Form.Item>
|
||||
<Form.Item label="自动建单" name="autoCreate" valuePropName="checked">
|
||||
<Switch size="small" />
|
||||
@@ -1139,9 +1085,15 @@ export default function ProductRulesPanel() {
|
||||
label="启用拼单"
|
||||
name="sharingEnabled"
|
||||
valuePropName="checked"
|
||||
tooltip={friendGiftMode ? '好友赠送模式不支持拼单' : undefined}
|
||||
tooltip={
|
||||
friendGiftMode
|
||||
? '好友赠送模式不支持拼单'
|
||||
: !hasConfiguredPrice
|
||||
? '请先在商品与成本中心配置成本'
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Switch size="small" disabled={friendGiftMode} />
|
||||
<Switch size="small" disabled={friendGiftMode || !hasConfiguredPrice} />
|
||||
</Form.Item>
|
||||
|
||||
{sharingEnabled ? (
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
Tag,
|
||||
} from 'antd'
|
||||
import PercentTable from '@/components/common/PercentTable'
|
||||
import { buildAdminTablePagination } from '@/utils/admin-pagination'
|
||||
import type { ColumnsType } from 'antd/es/table'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
@@ -635,16 +636,15 @@ export default function RiskControlPanel() {
|
||||
dataSource={bans?.items || []}
|
||||
loading={bansQuery.isLoading}
|
||||
locale={{ emptyText: '暂无封禁记录' }}
|
||||
pagination={{
|
||||
pagination={buildAdminTablePagination({
|
||||
current: banPage,
|
||||
pageSize: banPageSize,
|
||||
total: bans?.total || 0,
|
||||
showSizeChanger: true,
|
||||
onChange: (nextPage, nextPageSize) => {
|
||||
setBanPage(nextPage)
|
||||
setBanPageSize(nextPageSize)
|
||||
},
|
||||
}}
|
||||
})}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
@@ -690,16 +690,15 @@ export default function RiskControlPanel() {
|
||||
columns={eventColumns}
|
||||
dataSource={events?.items || []}
|
||||
loading={eventsQuery.isLoading}
|
||||
pagination={{
|
||||
pagination={buildAdminTablePagination({
|
||||
current: page,
|
||||
pageSize,
|
||||
total: events?.total || 0,
|
||||
showSizeChanger: true,
|
||||
onChange: (nextPage, nextPageSize) => {
|
||||
setPage(nextPage)
|
||||
setPageSize(nextPageSize)
|
||||
},
|
||||
}}
|
||||
})}
|
||||
/>
|
||||
</Card>
|
||||
</Space>
|
||||
|
||||
Reference in New Issue
Block a user