重构:统一商品渠道投影并收敛权威文档

This commit is contained in:
yml2213
2026-11-08 19:44:27 +08:00
parent 0a595acfa9
commit 1634502363
67 changed files with 2147 additions and 444 deletions
@@ -66,7 +66,7 @@ export function AffiliateSkuPanel() {
queryKey: ['admin-cost-center-products', 'affiliate_dash', page, pageSize],
queryFn: () =>
fetchAdminProductCatalog({
carrierType: 'affiliate_dash',
channel: 'affiliate_dash',
page,
pageSize,
}),
@@ -34,7 +34,7 @@ type Props = {
providers: string[]
canConfig: boolean
diagnostics?: MappingDiagnostics
sourceLabel: string
channelLabel: string
expandedRowKeys: string[]
detailPageSize: number
onKeywordChange: (value: string) => void
@@ -60,7 +60,7 @@ export default function ChannelMappingCatalog({
providers,
canConfig,
diagnostics,
sourceLabel,
channelLabel,
expandedRowKeys,
detailPageSize,
onKeywordChange,
@@ -241,7 +241,7 @@ export default function ChannelMappingCatalog({
<Alert
type="success"
showIcon
message={`${sourceLabel}的订单项已全部归一`}
message={`${channelLabel}的订单项已全部归一`}
description={`${Number(diagnostics?.productTotal || 0)} 个标准商品、${Number(diagnostics?.mappingTotal || 0)} 条映射已覆盖全部订单;新订单若命中不到映射,会在每日成本同步后出现在「未归一」队列。`}
/>
)}
@@ -3,7 +3,7 @@
*
* 采用「诊断优先」结构:
* 1. 顶部统计卡:标准商品 / 映射条数 / 未归一订单项 / 未归一 SKU 组;
* 2. 未归一队列:按来源 + 店铺 + 平台 SKU 聚合成可操作的行,行内直接建映射;
* 2. 未归一队列:按渠道 + 店铺 + 平台 SKU 聚合成可操作的行,行内直接建映射;
* 3. 映射目录:只保留 4 列(标准商品 / 分类 / 渠道覆盖 / 映射),匹配方式进展开行。
*
* 2026-10-25 拆分:本文件只保留查询、状态与动作,视图拆到
@@ -44,7 +44,7 @@ import {
export function ChannelMappingPanel() {
const { message, modal } = App.useApp()
const queryClient = useQueryClient()
const { canConfig, productSourceFilter, options, refreshAll } = useCostCenter()
const { canConfig, channelFilter, options, refreshAll } = useCostCenter()
const [mappingForm] = Form.useForm<MappingFormValues>()
const [view, setView] = useState<'catalog' | 'queue'>('catalog')
@@ -67,11 +67,10 @@ export function ChannelMappingPanel() {
const providers = options?.providers || []
const diagnosticsQuery = useQuery({
queryKey: ['admin-cost-center-mapping-diagnostics', productSourceFilter],
queryKey: ['admin-cost-center-mapping-diagnostics', channelFilter],
queryFn: () =>
fetchAdminChannelMappingDiagnostics({
carrierType: productSourceFilter,
channel: productSourceFilter,
channel: channelFilter,
}),
})
const diagnostics = diagnosticsQuery.data?.data
@@ -79,7 +78,7 @@ export function ChannelMappingPanel() {
const mappingsQuery = useQuery({
queryKey: [
'admin-cost-center-mappings',
productSourceFilter,
channelFilter,
mappingKeyword,
mappingProvider,
mappingPage,
@@ -89,7 +88,7 @@ export function ChannelMappingPanel() {
fetchAdminChannelProductMappings({
keyword: mappingKeyword || undefined,
provider: mappingProvider || undefined,
carrierType: productSourceFilter,
channel: channelFilter,
page: mappingPage,
pageSize: mappingPageSize,
}),
@@ -99,7 +98,7 @@ export function ChannelMappingPanel() {
const queueQuery = useQuery({
queryKey: [
'admin-cost-center-unmapped-mappings',
productSourceFilter,
channelFilter,
queueKeyword,
queueProvider,
queuePage,
@@ -107,7 +106,7 @@ export function ChannelMappingPanel() {
],
queryFn: () =>
fetchAdminUnmappedMappingGroups({
channel: productSourceFilter,
channel: channelFilter,
provider: queueProvider || undefined,
keyword: queueKeyword || undefined,
page: queuePage,
@@ -126,7 +125,13 @@ export function ChannelMappingPanel() {
const productOptions =
options?.products
.filter((item) => !productSourceFilter || item.source === productSourceFilter)
.filter(
(item) =>
!channelFilter ||
(channelFilter === 'unrouted'
? item.activePrimaryChannel === null
: item.activePrimaryChannel === channelFilter),
)
.map((item) => ({
value: item.id,
label: `${item.canonicalName}(${item.canonicalSku})`,
@@ -274,9 +279,9 @@ export function ChannelMappingPanel() {
})
}
const sourceLabel = productSourceFilter
? (FULFILLMENT_MODE_LABELS[productSourceFilter] ?? productSourceFilter)
: '全部来源'
const channelLabel = channelFilter
? (FULFILLMENT_MODE_LABELS[channelFilter] ?? channelFilter)
: '全部渠道'
return (
<>
@@ -284,7 +289,7 @@ export function ChannelMappingPanel() {
title={
<Space size={6} wrap>
<Typography.Text strong>渠道映射</Typography.Text>
<Tag color="blue">{sourceLabel}</Tag>
<Tag color="blue">{channelLabel}</Tag>
</Space>
}
extra={
@@ -344,7 +349,7 @@ export function ChannelMappingPanel() {
providers={providers}
canConfig={canConfig}
diagnostics={diagnostics}
sourceLabel={sourceLabel}
channelLabel={channelLabel}
expandedRowKeys={expandedMappingProductIds}
detailPageSize={mappingDetailPageSize}
onKeywordChange={(value) => {
@@ -387,7 +392,7 @@ export function ChannelMappingPanel() {
provider={queueProvider}
providers={providers}
canConfig={canConfig}
sourceLabel={sourceLabel}
channelLabel={channelLabel}
onKeywordChange={(value) => {
setQueueKeyword(value)
setQueuePage(1)
@@ -34,7 +34,7 @@ type Props = {
provider: string
providers: string[]
canConfig: boolean
sourceLabel: string
channelLabel: string
onKeywordChange: (value: string) => void
onProviderChange: (value: string) => void
onPageChange: (page: number, pageSize: number) => void
@@ -54,7 +54,7 @@ export default function ChannelMappingQueue({
provider,
providers,
canConfig,
sourceLabel,
channelLabel,
onKeywordChange,
onProviderChange,
onPageChange,
@@ -151,7 +151,7 @@ export default function ChannelMappingQueue({
<Alert
type="success"
showIcon
message={`${sourceLabel}没有未归一的平台 SKU`}
message={`${channelLabel}没有未归一的平台 SKU`}
description="订单项全部命中了标准商品映射;这里为空即代表销售与成本都能按商品归集。"
/>
) : (
@@ -1,7 +1,7 @@
/**
* 成本中心页面上下文。
*
* 只存放「所有面板都要知道」的三件事:权限、当前来源筛选、刷新动作。
* 只存放「所有面板都要知道」的三件事:权限、当前渠道筛选、刷新动作。
* 具体数据由各面板自己用 react-query 取——曾经这里塞过 products / versions /
* exceptions 等空数组当作「统一数据源」,实际没人读,已删除。
*/
@@ -15,16 +15,14 @@ export type CostCenterTab = 'products' | 'mappings' | 'cost' | 'exceptions'
export type CostCenterContextValue = {
canConfig: boolean
/** 当前来源筛选:all / affiliate_dash / work_order / manual。 */
/** 当前渠道筛选。 */
scope: CostScope
setScope: (scope: CostScope) => void
activeTab: CostCenterTab
setActiveTab: (tab: CostCenterTab) => void
scopeLabel: string
/** 商品/映射按 canonical_sku 前缀筛选。 */
productSourceFilter?: ProductSource
/** 成本项/版本/异常按履约方式筛选。 */
channelFilter?: 'affiliate_dash' | 'work_order' | 'third_party_voucher' | 'manual'
/** 商品、映射、成本项、版本和异常共用的当前渠道筛选。 */
channelFilter?: Exclude<CostScope, 'all'>
options?: {
providers: string[]
@@ -34,6 +32,7 @@ export type CostCenterContextValue = {
canonicalName: string
unitName: string
source: ProductSource
activePrimaryChannel: string | null
}>
costItems: Array<{
id: number
@@ -1,5 +1,5 @@
/**
* 成本中心外壳:来源筛选、总览统计与固定 Tab。
* 成本中心外壳:当前渠道筛选、总览统计与固定 Tab。
*
* 结构(2026-10-25 调整):
* 1. 来源不再是「假 Tab」,改为工具栏 Segmented —— 只筛数据,不改变导航;
@@ -39,7 +39,7 @@ import { CostCenterContext, type CostCenterTab } from './CostCenterContext'
import type { CostScope } from './shared'
const SCOPES: Array<{ key: CostScope; label: string; hint: string }> = [
{ key: 'all', label: '全部', hint: '不区分来源,查看全部商品、映射与成本。' },
{ key: 'all', label: '全部', hint: '不区分当前渠道,查看全部商品、映射与成本。' },
{
key: 'affiliate_dash',
label: '自动发货',
@@ -60,6 +60,16 @@ const SCOPES: Array<{ key: CostScope; label: string; hint: string }> = [
label: '人工客服',
hint: '客服手工处理的商品;成本按内部 SKU 的消耗积分结算。',
},
{
key: 'on_account',
label: '统一结算',
hint: '统一结算商品;不进入人工客服或自动履约。',
},
{
key: 'unrouted',
label: '未路由',
hint: '尚未配置有效主渠道的商品。',
},
]
const TABS: readonly CostCenterTab[] = ['products', 'mappings', 'cost', 'exceptions']
@@ -137,11 +147,7 @@ export default function CostCenterShell() {
const scopeLabel = SCOPES.find((item) => item.key === scope)?.label ?? '全部'
const scopeHint = SCOPES.find((item) => item.key === scope)?.hint ?? ''
const productSourceFilter = scope === 'all' ? undefined : scope
const channelFilter =
scope === 'all'
? undefined
: (scope as 'affiliate_dash' | 'work_order' | 'third_party_voucher' | 'manual')
const channelFilter = scope === 'all' ? undefined : scope
return (
<CostCenterContext.Provider
value={{
@@ -151,7 +157,6 @@ export default function CostCenterShell() {
activeTab,
setActiveTab,
scopeLabel,
productSourceFilter,
channelFilter,
options,
refreshAll,
@@ -163,7 +168,7 @@ export default function CostCenterShell() {
<Typography.Text type="secondary">统计口径</Typography.Text>
<Tag color={scope === 'all' ? 'default' : 'blue'}>{scopeLabel}</Tag>
{scope !== 'all' ? (
<Typography.Text type="secondary">(跟随上方来源筛选变化)</Typography.Text>
<Typography.Text type="secondary">(跟随上方渠道筛选变化)</Typography.Text>
) : null}
<Button
size="small"
@@ -1,16 +1,16 @@
/**
* 成本配置:按来源展示「成本怎么算」。
* 成本配置:按履约渠道展示「成本怎么算」。
*
* 三个来源的成本形态完全不同,所以这里不做统一表格,而是按来源切换:
* 各渠道的成本形态不同,所以这里不做统一表格,而是按渠道切换:
* 自动发货 → 上游 SKU 采购价(按 aff:<skuCode> 归一)
* 接单平台 → 接单模板发单金额(只读核对,改价去接单模板页)
* 第三方卡券 → 卡券商品采购价(按 tpv:<sku> 归一,只统计)
* 人工客服 → 内部 SKU 的履约渠道与消耗积分
*
* 来源在页面顶部工具栏统一选择;选「全部」时这里给出四个入口,
* 渠道在页面顶部工具栏统一选择;选「全部」时这里给出四个入口,
* 避免把三套配置竖着堆在一页里。
*/
import { Button, Card, Col, Row, Space, Typography } from 'antd'
import { Alert, Button, Card, Col, Row, Space, Typography } from 'antd'
import { AffiliateSkuPanel } from './AffiliateSkuPanel'
import { useCostCenter } from './CostCenterContext'
@@ -47,17 +47,42 @@ const CONFIG_ENTRIES: Array<{
]
export default function CostConfigPanel() {
const { scope, setScope } = useCostCenter()
const { scope, setScope, setActiveTab } = useCostCenter()
if (scope === 'affiliate_dash') return <AffiliateSkuPanel />
if (scope === 'work_order') return <WorkOrderPricePanel />
if (scope === 'third_party_voucher') return <VoucherSkuPanel />
if (scope === 'manual') return <ManualSkuConfigPanel />
if (scope === 'on_account') {
return (
<Alert
type="info"
showIcon
message="统一结算没有独立商品价格入口"
description="统一结算商品不进入人工或自动履约;实际成本以统一结算账单确认结果为准。"
/>
)
}
if (scope === 'unrouted') {
return (
<Alert
type="warning"
showIcon
message="未路由商品暂时没有履约成本口径"
description="先为标准商品配置明确主渠道,再维护对应渠道的成本。"
action={
<Button size="small" onClick={() => setActiveTab('products')}>
去标准商品
</Button>
}
/>
)
}
return (
<Space direction="vertical" size={12} style={{ width: '100%' }}>
<Typography.Text type="secondary">
成本配置按来源分开维护,选择一个来源查看;也可以在页面顶部直接切换来源。
成本配置按履约渠道分开维护;也可以在页面顶部直接切换渠道。
</Typography.Text>
<Row gutter={[12, 12]}>
{CONFIG_ENTRIES.map((entry) => (
@@ -94,7 +94,7 @@ export function ProductCatalogPanel() {
const { message, modal } = App.useApp()
const queryClient = useQueryClient()
const navigate = useNavigate()
const { canConfig, productSourceFilter, refreshAll, setScope, setActiveTab } = useCostCenter()
const { canConfig, channelFilter, refreshAll, setScope, setActiveTab } = useCostCenter()
const [productForm] = Form.useForm<ProductFormValues>()
// 历史修复只处理最近 30 天,避免很早以前的乱 SKU 再次进入商品与映射。
const [backfillRange, setBackfillRange] = useState<[Dayjs, Dayjs]>([
@@ -120,7 +120,7 @@ export function ProductCatalogPanel() {
const [priceForm] = Form.useForm()
// 游戏分类与二级目录结构仅为接单平台(work_order)独有,自动发货与人工客服不使用
const isWorkOrder = productSourceFilter === 'work_order'
const isWorkOrder = channelFilter === 'work_order'
const showCategoryBar = isWorkOrder
useEffect(() => {
@@ -133,7 +133,7 @@ export function ProductCatalogPanel() {
const productsQuery = useQuery({
queryKey: [
'admin-cost-center-products',
productSourceFilter,
channelFilter,
productKeyword,
isWorkOrder ? productRootCategoryId : null,
isWorkOrder ? productSubCategoryId : null,
@@ -143,7 +143,7 @@ export function ProductCatalogPanel() {
queryFn: () =>
fetchAdminProductCatalog({
keyword: productKeyword || undefined,
carrierType: productSourceFilter,
channel: channelFilter,
rootCategoryId: isWorkOrder ? (productRootCategoryId ?? undefined) : undefined,
categoryId: isWorkOrder ? (productSubCategoryId ?? undefined) : undefined,
page: productPage,
@@ -451,7 +451,7 @@ export function ProductCatalogPanel() {
function deleteProduct(item: ProductCatalogItem) {
const affiliateHint =
item.source === 'affiliate_dash'
item.sourceKind === 'affiliate_dash'
? '这是自动发货 SKU,删除后再次同步会自动生成;只想隐藏它请改为归档。'
: ''
modal.confirm({
@@ -516,12 +516,6 @@ export function ProductCatalogPanel() {
{row.templatePricingMode === 'unit' ? '按件计价' : '固定金额'}
</Tag>
) : null}
{row.activePrimaryChannel && row.activePrimaryChannel !== row.source ? (
<Tag color="purple" style={{ marginInlineEnd: 0 }}>
当前主渠道:
{FULFILLMENT_MODE_LABELS[row.activePrimaryChannel] || row.activePrimaryChannel}
</Tag>
) : null}
</Space>
</div>
),
@@ -540,14 +534,40 @@ export function ProductCatalogPanel() {
),
},
{
title: '已绑定渠道',
title: '当前履约',
width: 160,
render: (_, row) => (
<Space direction="vertical" size={2}>
{row.activePrimaryChannel ? (
<Tag
color={row.activePrimaryChannel === 'manual' ? 'orange' : 'purple'}
style={{ marginInlineEnd: 0 }}
>
{FULFILLMENT_MODE_LABELS[row.activePrimaryChannel] || row.activePrimaryChannel}
</Tag>
) : (
<Tag style={{ marginInlineEnd: 0 }}>未路由</Tag>
)}
{row.fallbackChannels?.length ? (
<Typography.Text type="secondary" style={{ fontSize: 11 }}>
兜底:
{row.fallbackChannels
.map((channel) => FULFILLMENT_MODE_LABELS[channel] || channel)
.join(' / ')}
</Typography.Text>
) : null}
</Space>
),
},
{
title: '平台映射',
dataIndex: 'mappingCount',
width: 130,
render: (value: number, row) => {
if (row.source === 'affiliate_dash') {
if (row.sourceKind === 'affiliate_dash') {
return <Tag color="default">直连无需映射</Tag>
}
if (row.source === 'manual') {
if (row.sourceKind === 'manual') {
return <Tag color="default">内部SKU无需映射</Tag>
}
return value > 0 ? (
@@ -562,7 +582,7 @@ export function ProductCatalogPanel() {
dataIndex: 'costVersionCount',
width: 140,
render: (value: number, row) => {
if (row.source === 'manual') {
if (row.sourceKind === 'manual') {
return (
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
以人工 SKU 配置为准
@@ -596,7 +616,7 @@ export function ProductCatalogPanel() {
>
{value > 0
? `${value} 个版本 · 改价`
: row.source === 'work_order'
: row.sourceKind === 'work_order'
? '配置结算成本'
: '未配置 · 立即配置'}
</Button>
@@ -634,7 +654,7 @@ export function ProductCatalogPanel() {
render: (_, row) =>
canConfig ? (
<Space size={2} wrap>
{row.source === 'manual' ? (
{row.sourceKind === 'manual' ? (
<Button
type="link"
size="small"
@@ -704,7 +724,7 @@ export function ProductCatalogPanel() {
extra={
canConfig ? (
<Space size={6} wrap>
{productSourceFilter === 'work_order' ? (
{channelFilter === 'work_order' ? (
<>
<AdminRangePicker
value={backfillRange}
@@ -727,7 +747,7 @@ export function ProductCatalogPanel() {
loading={productsQuery.isFetching}
onClick={() => void productsQuery.refetch()}
/>
{productSourceFilter === 'manual' ? (
{channelFilter === 'manual' ? (
<Button
type="primary"
icon={<PlusOutlined />}
@@ -737,7 +757,7 @@ export function ProductCatalogPanel() {
>
新增人工 SKU
</Button>
) : productSourceFilter === 'third_party_voucher' ? (
) : channelFilter === 'third_party_voucher' ? (
<Button
type="primary"
icon={<PlusOutlined />}
@@ -758,7 +778,7 @@ export function ProductCatalogPanel() {
) : null
}
>
{productSourceFilter === 'manual' ? (
{channelFilter === 'manual' ? (
<Alert
type="info"
showIcon
@@ -863,7 +883,7 @@ export function ProductCatalogPanel() {
dataSource={products}
loading={productsQuery.isFetching}
rowClassName={(row) => (row.mappingCount > 0 ? 'product-rule-row-mapped' : '')}
scroll={{ x: 1180 }}
scroll={{ x: 1320 }}
pagination={buildAdminTablePagination({
current: productPage,
pageSize: productPageSize,
@@ -55,7 +55,7 @@ export default function VoucherSkuPanel() {
queryKey: ['cost-center-voucher-products', page, pageSize],
queryFn: () =>
fetchAdminProductCatalog({
carrierType: 'third_party_voucher',
channel: 'third_party_voucher',
page,
pageSize,
}),
@@ -3,7 +3,7 @@
*
* 口径(2026-10-23 重写):
* 1. 履约渠道唯一真源是 order_items.fulfillment_channel + status,前端只读不改;
* 2. 商品来源由 canonical_sku 前缀推导,前端不再存「商品载体」字段;
* 2. 商品来源来自 sourceKind,当前渠道来自统一商品投影,两者不能混用;
* 3. 成本项用 cost_model 表达「成本怎么算」,没有「适用载体」这一列。
*/
import type { Dayjs } from 'dayjs'
@@ -16,8 +16,15 @@ import type {
ProductSource,
} from '@/types/admin'
/** 顶部来源切换:全部 / 自动发货 / 接单平台 / 第三方卡券 / 人工。 */
export type CostScope = 'all' | 'affiliate_dash' | 'work_order' | 'third_party_voucher' | 'manual'
/** 顶部当前渠道切换。 */
export type CostScope =
| 'all'
| 'affiliate_dash'
| 'work_order'
| 'third_party_voucher'
| 'manual'
| 'on_account'
| 'unrouted'
export type CostItemPreset = {
key: string
@@ -86,12 +93,14 @@ export const COST_MODEL_OPTIONS = [
{ value: 'other', label: '其他' },
]
/** 商品来源:由 canonical_sku 前缀推导。 */
/** 商品来源:来自 product_catalog.source_kind。 */
export const PRODUCT_SOURCE_OPTIONS = [
{ value: 'affiliate_dash', label: '自动发货' },
{ value: 'work_order', label: '接单平台' },
{ value: 'third_party_voucher', label: '第三方卡券' },
{ value: 'manual', label: '人工客服' },
{ value: 'on_account', label: '统一结算' },
{ value: 'unclassified', label: '未分类' },
]
export const PRODUCT_SOURCE_TAG_COLORS: Record<ProductSource, string> = {
@@ -99,6 +108,8 @@ export const PRODUCT_SOURCE_TAG_COLORS: Record<ProductSource, string> = {
work_order: 'blue',
third_party_voucher: 'cyan',
manual: 'orange',
on_account: 'gold',
unclassified: 'default',
'': 'default',
}
+12 -2
View File
@@ -8,8 +8,15 @@ export type FulfillmentChannel =
| 'manual'
| 'on_account'
/** 商品来源:由 canonical_sku 前缀推导,空串表示未分类。 */
export type ProductSource = 'affiliate_dash' | 'work_order' | 'third_party_voucher' | 'manual' | ''
/** 商品来源:来自稳定 source_kind,空串表示未分类。 */
export type ProductSource =
| 'affiliate_dash'
| 'work_order'
| 'third_party_voucher'
| 'manual'
| 'on_account'
| 'unclassified'
| ''
/**
* 成本模型:只表达「这笔成本怎么算」,与履约方式正交。
@@ -45,6 +52,7 @@ export interface ProductCatalogItem {
canonicalName: string
category: string
source: ProductSource
sourceKind: ProductSource
rootCategoryId: number | null
rootCategoryName: string
subCategoryId: number | null
@@ -60,6 +68,7 @@ export interface ProductCatalogItem {
templateEnabled?: boolean | null
templateSharingEnabled?: boolean | null
activePrimaryChannel?: FulfillmentChannel | null
fallbackChannels?: FulfillmentChannel[]
/** 自动发货履约任务数,实时取自 fulfillment_tasks。 */
affiliateTaskCount: number
affiliateLastFulfilledAt: string | null
@@ -164,6 +173,7 @@ export interface CostCenterOptions {
canonicalName: string
unitName: string
source: ProductSource
activePrimaryChannel: FulfillmentChannel | null
}>
costItems: Array<{
id: number
@@ -0,0 +1,41 @@
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import test from 'node:test'
test('标准商品列表按当前渠道筛选,并单独展示来源、主渠道和兜底渠道', async () => {
const source = await readFile(
new URL('../src/pages/admin/cost-center/ProductCatalogPanel.tsx', import.meta.url),
'utf8',
)
assert.ok(source.includes('channel: channelFilter'))
assert.ok(!source.includes('carrierType: productSourceFilter'))
assert.ok(source.includes("title: '来源'"))
assert.ok(source.includes("title: '当前履约'"))
assert.ok(source.includes('row.activePrimaryChannel'))
assert.ok(source.includes('row.fallbackChannels'))
assert.ok(source.includes('未路由'))
})
test('渠道映射的统计、目录、队列和商品下拉都使用当前渠道', async () => {
const [panel, catalog, queue] = await Promise.all([
readFile(
new URL('../src/pages/admin/cost-center/ChannelMappingPanel.tsx', import.meta.url),
'utf8',
),
readFile(
new URL('../src/pages/admin/cost-center/ChannelMappingCatalog.tsx', import.meta.url),
'utf8',
),
readFile(
new URL('../src/pages/admin/cost-center/ChannelMappingQueue.tsx', import.meta.url),
'utf8',
),
])
assert.ok(panel.includes('channel: channelFilter'))
assert.ok(panel.includes('item.activePrimaryChannel === channelFilter'))
assert.ok(!panel.includes('carrierType: productSourceFilter'))
assert.ok(catalog.includes('channelLabel'))
assert.ok(queue.includes('channelLabel'))
})
@@ -52,7 +52,7 @@ test('人工商品不再允许手工创建或执行旧批量归档', async () =>
assert.ok(!productSource.includes('归档跨渠道重复商品'))
assert.ok(!productSource.includes('归档纯历史人工商品'))
assert.ok(productSource.includes("productSourceFilter === 'manual'"))
assert.ok(productSource.includes("channelFilter === 'manual'"))
assert.ok(productSource.includes('manualNew=1'))
assert.ok(productSource.includes('人工标准商品由人工 SKU 配置自动创建'))
assert.ok(serviceSource.includes('cost_manual_product_managed_by_config'))
@@ -43,6 +43,6 @@ test('第三方卡券新增 SKU 一次完成商品、规则和采购价', async
assert.ok(service.includes("'/api/v1/admin/cost-center/voucher-products'"))
assert.ok(!service.includes('/voucher-products/ensure-rules'))
assert.ok(panel.includes("searchParams.get('voucherNew')"))
assert.ok(productCatalog.includes("productSourceFilter === 'third_party_voucher'"))
assert.ok(productCatalog.includes("channelFilter === 'third_party_voucher'"))
assert.ok(productCatalog.includes('voucherNew=1'))
})