Files
order_site/apps/frontend/src/pages/admin/cost-center/shared.ts
T
yml2213 ce775ca102 feat(cost-center): 第三方卡券渠道完整接入成本模型
- 商品来源筛选补 tpv: 前缀,卡券商品列表不再为空
- 成本模型新增 third_party_voucher_cost,覆盖预览/成本项/概览作用域
- 前后端成本模型枚举与下拉同步
2026-10-31 20:17:28 +08:00

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)
}