feat(cost-center): 统一接单模板成本并自动同步商品

This commit is contained in:
yml2213
2026-11-02 16:14:51 +08:00
parent 55e3c4e21b
commit 290890e848
22 changed files with 963 additions and 508 deletions
@@ -95,7 +95,7 @@ export function ProductCatalogPanel() {
const queryClient = useQueryClient()
const { canConfig, productSourceFilter, refreshAll } = useCostCenter()
const [productForm] = Form.useForm<ProductFormValues>()
// 初始化/回填只处理最近 30 天,避免很早以前的乱 SKU 再次进入商品与映射。
// 历史修复只处理最近 30 天,避免很早以前的乱 SKU 再次进入商品与映射。
const [backfillRange, setBackfillRange] = useState<[Dayjs, Dayjs]>([
dayjs().subtract(29, 'day').startOf('day'),
dayjs().endOf('day'),
@@ -177,7 +177,7 @@ export function ProductCatalogPanel() {
const data = response.data
bootstrapSyncOptionsRef.current.includeDisabledTemplates = false
modal.confirm({
title: '从接单模板全量同步商品与映射',
title: '修复接单模板历史商品与映射',
width: 720,
content: (
<Space direction="vertical" size={8}>
@@ -276,7 +276,7 @@ export function ProductCatalogPanel() {
? `(含停用模板 ${applied.disabledTemplateCount} 个,已建为归档商品)`
: ''
message.success(
`已同步 ${applied.templateCount} 个模板商品:新建 ${applied.createdProductCount} 个、刷新 ${applied.updatedProductCount} 个、复用 ${applied.reusedProductCount} 个${disabledText};新增 ${applied.createdMappingCount} 条映射,同步 ${applied.templateCostVersionCount} 条模板发单金额,回填 ${applied.mappedOrderItemCount} 个订单项${rebuildText}${conflictText}`,
`已同步 ${applied.templateCount} 个模板商品:新建 ${applied.createdProductCount} 个、刷新 ${applied.updatedProductCount} 个、复用 ${applied.reusedProductCount} 个${disabledText};新增 ${applied.createdMappingCount} 条映射,回填 ${applied.mappedOrderItemCount} 个订单项${rebuildText}${conflictText}`,
)
refreshAll()
} catch (error) {
@@ -338,6 +338,7 @@ export function ProductCatalogPanel() {
}
function openSetProductPrice(item: ProductCatalogItem) {
const isTemplateProduct = item.canonicalSku.startsWith('tpl:')
setPricingProduct(item)
priceForm.resetFields()
priceForm.setFieldsValue({
@@ -345,9 +346,9 @@ export function ProductCatalogPanel() {
item.activeCostAmountFen !== null && item.activeCostAmountFen !== undefined
? (item.activeCostAmountFen / 100).toFixed(2)
: undefined,
pricingMode: isTemplateProduct ? item.templatePricingMode || 'fixed' : undefined,
effectiveScope: 'immediate',
changeReason: '在商品与成本中心统一调整价格',
syncOpenOrders: true,
syncSnapshots: true,
})
setPriceModalOpen(true)
@@ -355,10 +356,10 @@ export function ProductCatalogPanel() {
async function submitProductPrice(values: {
amountYuan: number | string
pricingMode?: 'fixed' | 'unit'
effectiveScope: 'immediate' | 'custom'
effectiveFromCustom?: string
changeReason?: string
syncOpenOrders?: boolean
}) {
if (!pricingProduct) return
const yuan = Number(values.amountYuan)
@@ -376,9 +377,10 @@ export function ProductCatalogPanel() {
try {
const response = await setAdminProductPrice(pricingProduct.id, {
amountFen,
pricingMode: values.pricingMode,
effectiveFrom,
changeReason: values.changeReason || '在商品与成本中心统一调整价格',
syncOpenOrders: values.syncOpenOrders !== false,
syncOpenOrders: true,
})
const result = response.data
let extraHint = ''
@@ -386,6 +388,12 @@ export function ProductCatalogPanel() {
extraHint = `,已同步 ${result.syncedOrders.updatedCount} 笔工单大厅待抢单`
}
message.success(`标准价格配置成功(¥${(amountFen / 100).toFixed(2)})${extraHint}`)
if (pricingProduct.canonicalSku.startsWith('tpl:') && !pricingProduct.templateEnabled) {
message.info('成本已配置;请回接单模板启用该模板后开始自动接单')
}
void queryClient.invalidateQueries({
queryKey: ['admin-worker-platform-product-rules'],
})
setPriceModalOpen(false)
refreshAll()
} catch (error) {
@@ -536,6 +544,16 @@ export function ProductCatalogPanel() {
<Typography.Text type="secondary" style={{ fontSize: 11 }}>
单位:{row.unitName || '件'}
</Typography.Text>
{row.canonicalSku.startsWith('tpl:') ? (
<Tag color={row.templateEnabled ? 'green' : 'orange'} style={{ marginInlineEnd: 0 }}>
{row.templateEnabled ? '模板已启用' : '模板待启用'}
</Tag>
) : null}
{row.templatePricingMode ? (
<Tag color="geekblue" style={{ marginInlineEnd: 0 }}>
{row.templatePricingMode === 'unit' ? '按件计价' : '固定金额'}
</Tag>
) : null}
</Space>
</div>
),
@@ -720,7 +738,7 @@ export function ProductCatalogPanel() {
loading={bootstrapPreviewing || bootstrapApplying}
onClick={() => void runTemplateBootstrap()}
>
从接单模板初始化
修复历史商品/映射
</Button>
</>
) : null}
@@ -949,16 +967,38 @@ export function ProductCatalogPanel() {
</div>
</div>
{pricingProduct.canonicalSku.startsWith('tpl:') ? (
<Form.Item
name="pricingMode"
label="接单计价方式"
rules={[{ required: true, message: '请选择计价方式' }]}
extra="保存后会同步到接单模板;固定金额按整单,按件单价按 SKU 数量计算"
>
<Radio.Group optionType="button" buttonStyle="solid">
<Radio.Button value="fixed">固定金额</Radio.Button>
<Radio.Button value="unit">按件计价</Radio.Button>
</Radio.Group>
</Form.Item>
) : null}
<Form.Item
name="amountYuan"
label={
pricingProduct.source === 'affiliate_dash' ? '采购单价(元)' : '标准结算单价(元)'
pricingProduct.source === 'affiliate_dash'
? '采购单价(元)'
: pricingProduct.canonicalSku.startsWith('tpl:')
? pricingProduct.templateSharingEnabled
? '拼单总成本(元)'
: '接单成本(元)'
: '标准结算单价(元)'
}
rules={[{ required: true, message: '请填写价格' }]}
extra={
pricingProduct.source === 'affiliate_dash'
? '上游每发货 1 件扣除的采购成本金额'
: '接单打手发单/验收的基础结算单价'
: pricingProduct.canonicalSku.startsWith('tpl:')
? '固定金额按整单计;按件计价时填写每个 SKU 的接单成本'
: '接单打手发单/验收的基础结算单价'
}
>
<InputNumber
@@ -983,8 +1023,13 @@ export function ProductCatalogPanel() {
只影响之后的新订单;历史订单的成本与成交价已固化,不会被改价覆盖
</div>
</Radio>
<Radio value="custom">
<Radio value="custom" disabled={pricingProduct.canonicalSku.startsWith('tpl:')}>
<strong>指定生效时间(不能早于当前时间)</strong>
{pricingProduct.canonicalSku.startsWith('tpl:') ? (
<div style={{ color: '#cf1322', fontSize: 12, marginTop: 2 }}>
接单模板当前只支持立即生效,避免模板价格与成本版本不同步
</div>
) : null}
</Radio>
</Radio.Group>
</Form.Item>
@@ -1010,17 +1055,6 @@ export function ProductCatalogPanel() {
<Input placeholder="例如:成本中心统一定价调整 / 上游采购成本调整" />
</Form.Item>
{pricingProduct.canonicalSku.startsWith('tpl:') ? (
<Form.Item name="syncOpenOrders" valuePropName="checked" style={{ marginBottom: 12 }}>
<Checkbox>
<strong>立即同步至接单大厅待抢单工单(推荐)</strong>
<div style={{ color: '#64748b', fontSize: 12 }}>
接单大厅中所有未领取的该模板工单将立即同步为新价格;已领取的工单保持原价不变。
</div>
</Checkbox>
</Form.Item>
) : null}
{pricingProduct.source === 'affiliate_dash' ? (
<Form.Item name="syncSnapshots" valuePropName="checked" style={{ marginBottom: 12 }}>
<Checkbox>
@@ -1,18 +1,18 @@
/**
* 发单价格 Tab:只读展示「接单模板 → 发单金额」的当前生效版本。
* 发单价格 Tab:展示由标准商品成本自动投影出的接单模板当前生效版本。
*
* 接单平台的成本以打手报酬为主,金额由接单模板(work_product_rules)决定,
* 这里只做核对视图;改价请改模板后点「同步模板发单金额」。
* 这里只做核对视图;改价在「标准商品」中完成并自动同步。
* 人工与自动发货的成本分别在各自 SKU 配置里维护,不在这里重复配置。
*/
import { SearchOutlined, SyncOutlined } from '@ant-design/icons'
import { Alert, App, Button, Card, Input, Select, Space, Tag, Typography } from 'antd'
import { SearchOutlined } from '@ant-design/icons'
import { Alert, Card, Input, Select, Space, Tag, Typography } from 'antd'
import PercentTable from '@/components/common/PercentTable'
import type { TableColumnsType } from 'antd'
import { useQuery } from '@tanstack/react-query'
import { useState } from 'react'
import { fetchAdminCostVersions, syncAdminWorkOrderTemplateCostVersions } from '@/services/admin'
import { fetchAdminCostVersions } from '@/services/admin'
import type { CostVersion } from '@/types/admin'
import { ADMIN_DEFAULT_PAGE_SIZE, buildAdminTablePagination } from '@/utils/admin-pagination'
import { formatAdminDateTime } from '@/utils/admin-time'
@@ -22,14 +22,12 @@ import { useCostCenter } from './CostCenterContext'
import { formatRate } from './shared'
export function WorkOrderPricePanel() {
const { message } = App.useApp()
const { canConfig, refreshAll } = useCostCenter()
const { canConfig } = useCostCenter()
const [keyword, setKeyword] = useState('')
const [status, setStatus] = useState('active')
const [page, setPage] = useState(1)
const [pageSize, setPageSize] = useState(ADMIN_DEFAULT_PAGE_SIZE)
const [syncing, setSyncing] = useState(false)
const versionsQuery = useQuery({
queryKey: ['admin-cost-center-cost-versions', 'work_order', keyword, status, page, pageSize],
@@ -46,22 +44,6 @@ export function WorkOrderPricePanel() {
const versions = versionsQuery.data?.data.items || []
const total = versionsQuery.data?.data.pagination.total || 0
async function syncTemplateCosts() {
setSyncing(true)
try {
const response = await syncAdminWorkOrderTemplateCostVersions()
const result = response.data
message.success(
`已同步 ${result.activeVersionCount} 条模板发单金额(新建 ${result.createdCount},停用 ${result.retiredCount}),关联 ${result.linkedSnapshotCount} 条成本快照`,
)
refreshAll()
} catch (error) {
message.error(error instanceof Error ? error.message : '同步接单模板发单金额失败')
} finally {
setSyncing(false)
}
}
const columns: TableColumnsType<CostVersion> = [
{
title: '接单规则 / 模板',
@@ -176,24 +158,14 @@ export function WorkOrderPricePanel() {
<Tag color="blue">{total} 条</Tag>
</Space>
}
extra={
canConfig ? (
<Button
icon={<SyncOutlined />}
loading={syncing}
onClick={() => void syncTemplateCosts()}
>
同步模板发单金额
</Button>
) : null
}
extra={canConfig ? <Tag color="green">自动同步</Tag> : null}
>
<Alert
type="info"
showIcon
style={{ marginBottom: 12 }}
message="接单平台的成本只看这张表;改价请改接单模板,再点「同步模板发单金额」"
description="人工客服的成本来自「人工 SKU 配置」里的消耗积分,自动发货的成本在「自动发货 SKU」里维护,二者都不在这里配置;系统内置成本项只有人工履约、自动发货采购、接单平台报酬 3 条。"
message="接单平台成本的唯一写入口是【标准商品 → 配置成本】"
description="改价保存后会自动更新成本版本、接单模板运行价格和本表;未领取的大厅工单同步新价,已领取工单保持原价。人工客服和自动发货成本分别在各自 SKU 配置中维护。"
/>
<Space style={{ marginBottom: 16 }} wrap>
<Input
@@ -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,6 @@ 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,
sharingEnabled: rule.sharing?.enabled || false,
sharingAutoFromOrder: rule.sharing?.autoFromOrder || false,
sharingTotalQuantity: hasManualSharingOverride ? rule.sharing?.totalQuantity : undefined,
@@ -342,9 +336,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
@@ -361,7 +352,6 @@ export default function ProductRulesPanel() {
sortOrder?: number
}) {
try {
const unitMode = values.pricingMode === 'unit'
const isFriendGift = values.acceptanceMode === 'friend_gift'
if (isFriendGift && values.sharingEnabled === true) {
message.error('好友赠送验收模式不支持拼单,请先关闭拼单')
@@ -372,12 +362,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 +374,18 @@ export default function ProductRulesPanel() {
isFriendGift && giftCooldownUnit === 'minutes' ? Number(giftCooldownValue || 0) : 0,
sharingEnabled: isFriendGift ? false : values.sharingEnabled === true,
sharingAutoFromOrder:
unitMode && values.sharingEnabled === true && !hasManualSharingOverride,
editingRule?.unitPriceFen !== undefined &&
editingRule.unitPriceFen > 0 &&
editingRule.rewardAmount === 0 &&
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,8 +420,6 @@ export default function ProductRulesPanel() {
platform: rule.platform,
productName: rule.productName,
categoryId: rule.categoryId || undefined,
rewardAmount: rule.rewardAmount / 100,
unitPrice: rule.unitPriceFen / 100,
sharingEnabled: rule.sharing?.enabled || false,
sharingAutoFromOrder:
rule.unitPriceFen > 0 && rule.sharing?.enabled === true && !hasManualSharingOverride,
@@ -473,6 +441,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 : '修改状态失败')
}
@@ -787,14 +758,22 @@ export default function ProductRulesPanel() {
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)}
</Typography.Text>
) : null}
{row.rewardAmount > 0 || row.unitPriceFen > 0 ? (
<>
<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)}
</Typography.Text>
) : null}
</>
) : (
<Tag color="orange" style={{ width: 'fit-content', margin: 0 }}>
待成本配置
</Tag>
)}
</div>
),
},
@@ -891,11 +870,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,9 +887,7 @@ export default function ProductRulesPanel() {
matchType: 'contains',
itemTitleMatchType: 'exact',
skuNickMatchType: 'exact',
pricingMode: 'fixed',
rewardAmount: 25,
enabled: true,
enabled: false,
autoCreate: true,
sharingEnabled: false,
acceptanceMode: 'standard',
@@ -926,23 +898,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>
固定金额、按件单价和价格同步统一由
<a href="#/admin/cost-center" style={{ margin: '0 4px', fontWeight: 500 }}>
商品与成本中心
</a>
管理。新模板默认停用,配置成本并启用模板后自动出现在商品目录。
</span>
}
/>
{/* 基础信息 */}
<div className="form-grid-3col">
@@ -965,55 +935,8 @@ export default function ProductRulesPanel() {
</Form.Item>
</div>
{/* Row 2: 计价方式 + 单价/金额 + 任务时限 */}
{/* Row 2: 任务时限 */}
<div className="form-grid-3col">
<Form.Item label="计价方式" name="pricingMode">
<Radio.Group
optionType="button"
buttonStyle="solid"
className="full-width"
options={[
{ value: 'fixed', label: '固定金额' },
{ value: 'unit', label: '按件计价' },
]}
/>
</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>
@@ -1124,13 +1047,23 @@ export default function ProductRulesPanel() {
<Form.Item noStyle shouldUpdate>
{({ getFieldValue }) => {
const sharingEnabled = getFieldValue('sharingEnabled')
const unitMode = getFieldValue('pricingMode') === 'unit'
const unitMode =
Number(editingRule?.unitPriceFen || 0) > 0 &&
Number(editingRule?.rewardAmount || 0) === 0
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 +1072,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 ? (
@@ -64,17 +64,6 @@ export function applyAdminTemplateProductBootstrap(payload?: {
)
}
export function syncAdminWorkOrderTemplateCostVersions() {
return apiPost<{
ruleCount: number
unitPriceRuleCount: number
activeVersionCount: number
createdCount: number
retiredCount: number
linkedSnapshotCount: number
}>('/api/v1/admin/cost-center/work-order-template-costs/sync')
}
export function backfillAdminWorkOrderProductSkus(payload?: {
dateFrom?: string
dateTo?: string
@@ -493,6 +482,7 @@ export function setAdminProductPrice(
productId: number,
payload: {
amountFen: number
pricingMode?: 'fixed' | 'unit'
effectiveFrom?: string
changeReason?: string
syncOpenOrders?: boolean
@@ -503,6 +493,7 @@ export function setAdminProductPrice(
costVersionId: number
amountFen: number
effectiveFrom: string
pricingMode?: 'fixed' | 'unit'
syncedOrders?: {
candidateCount: number
updatedCount: number
@@ -290,8 +290,6 @@ export function saveAdminWorkProductRule(payload: {
categoryId?: number
enabled?: boolean
autoCreate?: boolean
rewardAmount?: number
unitPrice?: number
sharingEnabled?: boolean
sharingAutoFromOrder?: boolean
sharingTotalQuantity?: number
@@ -305,7 +303,12 @@ export function saveAdminWorkProductRule(payload: {
fieldsText?: string
sortOrder?: number
}) {
return apiPost<{ rule: WorkProductRule }>('/api/v1/admin/worker-platform/product-rules', payload)
return apiPost<{
rule: WorkProductRule
productId?: number
productStatus?: 'active' | 'archived'
costConfigurationRequired?: boolean
}>('/api/v1/admin/worker-platform/product-rules', payload)
}
export function syncAdminWorkProductRulePrice(ruleId: number) {
+3 -3
View File
@@ -51,6 +51,9 @@ export interface ProductCatalogItem {
costVersionCount: number
activeCostAmountFen?: number | null
activeCostEffectiveFrom?: string | null
templatePricingMode?: 'fixed' | 'unit' | null
templateEnabled?: boolean | null
templateSharingEnabled?: boolean | null
/** 自动发货履约任务数,实时取自 fulfillment_tasks。 */
affiliateTaskCount: number
affiliateLastFulfilledAt: string | null
@@ -231,9 +234,6 @@ export interface TemplateProductBootstrapResult {
resetOrderItemCount: number
mappingConflictCount: number
mappingConflictItemCount: number
templateCostVersionCount: number
templateCostCreatedCount: number
templateCostRetiredCount: number
}
export interface CostLine {