- 商品来源筛选补 tpv: 前缀,卡券商品列表不再为空 - 成本模型新增 third_party_voucher_cost,覆盖预览/成本项/概览作用域 - 前后端成本模型枚举与下拉同步
235 lines
6.6 KiB
TypeScript
235 lines
6.6 KiB
TypeScript
/**
|
|
* 成本中心前端共享层:类型、常量、标签与页面上下文。
|
|
*
|
|
* 口径(2026-10-23 重写):
|
|
* 1. 履约方式唯一真源是 order_items.fulfillment_mode,前端只读不改;
|
|
* 2. 商品来源由 canonical_sku 前缀推导,前端不再存「商品载体」字段;
|
|
* 3. 成本项用 cost_model 表达「成本怎么算」,没有「适用载体」这一列。
|
|
*/
|
|
import type { Dayjs } from 'dayjs'
|
|
|
|
import type {
|
|
ChannelProductMapping,
|
|
CostItem,
|
|
CostModel,
|
|
CostVersion,
|
|
ProductSource,
|
|
} from '@/types/admin'
|
|
|
|
/** 顶部来源切换:全部 / 自动发货 / 接单平台 / 第三方卡券 / 人工。 */
|
|
export type CostScope = 'all' | 'affiliate_dash' | 'work_order' | 'third_party_voucher' | 'manual'
|
|
|
|
export type CostItemPreset = {
|
|
key: string
|
|
label: string
|
|
name: string
|
|
costModel: CostModel
|
|
pricingMode: CostItem['pricingMode']
|
|
unitName: string
|
|
}
|
|
|
|
export type ProductFormValues = {
|
|
canonicalSku: string
|
|
canonicalName: string
|
|
category?: string
|
|
unitName?: string
|
|
status?: 'active' | 'archived'
|
|
}
|
|
|
|
export type MappingFormValues = {
|
|
provider: string
|
|
shopId?: string
|
|
platformSkuCode?: string
|
|
platformSkuName?: string
|
|
matchType?: ChannelProductMapping['matchType']
|
|
canonicalProductId: number
|
|
quantityMultiplier?: number
|
|
confidence?: 'exact' | 'rule' | 'manual'
|
|
source?: 'manual' | 'import' | 'rule'
|
|
enabled?: boolean
|
|
note?: string
|
|
}
|
|
|
|
export type CostItemFormValues = {
|
|
name: string
|
|
costModel: CostModel
|
|
pricingMode: CostItem['pricingMode']
|
|
unitName?: string
|
|
status?: 'active' | 'archived'
|
|
note?: string
|
|
}
|
|
|
|
export type CostVersionFormValues = {
|
|
costItemId: number
|
|
scopeType: CostVersion['scopeType']
|
|
provider?: string
|
|
shopId?: string
|
|
canonicalProductId?: number
|
|
workProductRuleId?: number
|
|
amountYuan?: number
|
|
rateBps?: number
|
|
effectiveFrom?: Dayjs
|
|
effectiveTo?: Dayjs
|
|
status?: 'draft' | 'active'
|
|
changeReason?: string
|
|
}
|
|
|
|
/** 成本模型:只表达「这笔成本怎么算」。 */
|
|
export const COST_MODEL_OPTIONS = [
|
|
{ value: 'affiliate_purchase', label: '上游采购成本' },
|
|
{ value: 'work_order_reward', label: '接单平台打手报酬' },
|
|
{ value: 'third_party_voucher_cost', label: '第三方卡券采购成本' },
|
|
{ value: 'manual_actual', label: '人工客服实际支出' },
|
|
{ value: 'platform_fee', label: '平台服务费' },
|
|
{ value: 'payment_fee', label: '支付通道费' },
|
|
{ value: 'after_sales_loss', label: '售后损失' },
|
|
{ value: 'other', label: '其他' },
|
|
]
|
|
|
|
/** 商品来源:由 canonical_sku 前缀推导。 */
|
|
export const PRODUCT_SOURCE_OPTIONS = [
|
|
{ value: 'affiliate_dash', label: '自动发货' },
|
|
{ value: 'work_order', label: '接单平台' },
|
|
{ value: 'third_party_voucher', label: '第三方卡券' },
|
|
{ value: 'manual', label: '人工客服' },
|
|
]
|
|
|
|
export const PRODUCT_SOURCE_TAG_COLORS: Record<ProductSource, string> = {
|
|
affiliate_dash: 'green',
|
|
work_order: 'blue',
|
|
third_party_voucher: 'cyan',
|
|
manual: 'orange',
|
|
'': 'default',
|
|
}
|
|
|
|
export const PRICING_OPTIONS = [
|
|
{ value: 'per_item', label: '按件' },
|
|
{ value: 'per_order', label: '按单' },
|
|
{ value: 'percent_of_revenue', label: '按收入比例' },
|
|
]
|
|
|
|
export const SCOPE_OPTIONS = [
|
|
{ value: 'global', label: '全局' },
|
|
{ value: 'provider', label: '按来源' },
|
|
{ value: 'shop', label: '按店铺' },
|
|
{ value: 'product', label: '按标准商品' },
|
|
{ value: 'rule', label: '按接单规则' },
|
|
]
|
|
|
|
/** 从「未归一」队列建映射时带入的预填值。 */
|
|
export type MappingPrefill = {
|
|
provider?: string
|
|
shopId?: string
|
|
platformSkuCode?: string
|
|
platformSkuName?: string
|
|
}
|
|
|
|
/** 履约方式展示文案与颜色:渠道映射、成本异常、销售事实共用。 */
|
|
export const FULFILLMENT_MODE_LABELS: Record<string, string> = {
|
|
work_order: '接单平台',
|
|
affiliate_dash: '自动发货',
|
|
third_party_voucher: '第三方卡券',
|
|
legacy_platform: '历史归档',
|
|
manual: '人工客服',
|
|
archived: '历史归档',
|
|
unclassified: '未分类',
|
|
}
|
|
|
|
export const FULFILLMENT_MODE_COLORS: Record<string, string> = {
|
|
work_order: 'blue',
|
|
affiliate_dash: 'green',
|
|
third_party_voucher: 'cyan',
|
|
legacy_platform: 'default',
|
|
manual: 'orange',
|
|
archived: 'default',
|
|
unclassified: 'gold',
|
|
}
|
|
|
|
export const PROVIDER_LABELS: Record<string, string> = {
|
|
'91kaquan': '91 卡券(快手)',
|
|
pdd: '拼多多',
|
|
douyin_mashida: '抖音码速达',
|
|
}
|
|
|
|
export const MAPPING_MATCH_TYPE_OPTIONS: Array<{
|
|
value: ChannelProductMapping['matchType']
|
|
label: string
|
|
}> = [
|
|
{ value: 'sku_code', label: 'SKU 编码(唯一)' },
|
|
{ value: 'sku_code_name', label: 'SKU 编码 + 精确名称' },
|
|
{ value: 'sku_code_series', label: 'SKU 编码 + 名称系列' },
|
|
{ value: 'sku_name_series', label: 'SKU 名称系列兜底' },
|
|
]
|
|
|
|
/** 常用成本项模板:避免每次手填编码/模型/计价方式。 */
|
|
export const COST_ITEM_PRESETS: CostItemPreset[] = [
|
|
{
|
|
key: 'affiliate_purchase',
|
|
label: '上游采购成本',
|
|
name: '自动发货上游采购成本',
|
|
costModel: 'affiliate_purchase',
|
|
pricingMode: 'per_item',
|
|
unitName: '件',
|
|
},
|
|
{
|
|
key: 'work_order_reward',
|
|
label: '接单平台打手报酬',
|
|
name: '接单平台打手报酬',
|
|
costModel: 'work_order_reward',
|
|
pricingMode: 'per_order',
|
|
unitName: '单',
|
|
},
|
|
{
|
|
key: 'manual_actual',
|
|
label: '人工客服实际支出',
|
|
name: '人工客服履约成本',
|
|
costModel: 'manual_actual',
|
|
pricingMode: 'per_order',
|
|
unitName: '单',
|
|
},
|
|
{
|
|
key: 'platform_fee',
|
|
label: '平台服务费',
|
|
name: '平台服务费',
|
|
costModel: 'platform_fee',
|
|
pricingMode: 'percent_of_revenue',
|
|
unitName: '单',
|
|
},
|
|
{
|
|
key: 'payment_fee',
|
|
label: '支付通道费',
|
|
name: '支付通道费',
|
|
costModel: 'payment_fee',
|
|
pricingMode: 'percent_of_revenue',
|
|
unitName: '单',
|
|
},
|
|
{
|
|
key: 'after_sales_loss',
|
|
label: '售后损失',
|
|
name: '售后损失',
|
|
costModel: 'after_sales_loss',
|
|
pricingMode: 'per_order',
|
|
unitName: '单',
|
|
},
|
|
]
|
|
|
|
export function labelOf(options: Array<{ value: string; label: string }>, value: string) {
|
|
return options.find((item) => item.value === value)?.label || value || '-'
|
|
}
|
|
|
|
export function mappingMatchTypeLabel(value: ChannelProductMapping['matchType']) {
|
|
return MAPPING_MATCH_TYPE_OPTIONS.find((item) => item.value === value)?.label || value || '-'
|
|
}
|
|
|
|
export function providerLabel(value: string) {
|
|
return PROVIDER_LABELS[value] || value || '-'
|
|
}
|
|
|
|
export function formatRate(rateBps: number) {
|
|
return `${((Number(rateBps || 0) || 0) / 100).toFixed(2)}%`
|
|
}
|
|
|
|
export function costModelLabel(value: string) {
|
|
return labelOf(COST_MODEL_OPTIONS, value)
|
|
}
|