feat(distribution): 补齐退款加价回退、本站自卖与开通默认规则
退款时压缩卖方加价,人工/自动批次仅完结单可入账,号主收入进主站钱包;仲裁后重算释放并支持作废未入账 DS。开通时四种模式按默认生效,对外口径合并为 35/40/25,不再单独展示通道费。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -94,9 +94,6 @@ export interface OnboardDistributionSitePayload {
|
||||
tax_enabled?: boolean
|
||||
tax_rate_bps?: number
|
||||
settlement_hold_days?: number
|
||||
distributor_receive_ratio?: number
|
||||
platform_sell_ratio?: number
|
||||
service_fee_value?: number
|
||||
}
|
||||
|
||||
export interface OnboardDistributionSiteResult {
|
||||
@@ -254,6 +251,13 @@ export async function settleDistributionBatch(id: number) {
|
||||
return data.data
|
||||
}
|
||||
|
||||
export async function cancelDistributionBatch(id: number) {
|
||||
const { data } = await apiClient.post<ApiResponse<DistributionSettlementBatch>>(
|
||||
`/admin/distribution/settlement-batches/${id}/cancel`
|
||||
)
|
||||
return data.data
|
||||
}
|
||||
|
||||
export async function fetchDistributionSettlementItems(batchId: number) {
|
||||
const { data } = await apiClient.get<ApiResponse<{ items: DistributionSettlementItem[] }>>(
|
||||
`/admin/distribution/settlement-batches/${batchId}/items`
|
||||
|
||||
@@ -16,6 +16,11 @@ export const DISTRIBUTION_MODES = [
|
||||
label: '三方分账',
|
||||
hint: '分站卖另一分站的货',
|
||||
},
|
||||
{
|
||||
value: 'site_self_sell',
|
||||
label: '本站自卖',
|
||||
hint: '分站卖本站货,卖出站拿 75%',
|
||||
},
|
||||
] as const
|
||||
|
||||
const MODE_LABELS: Record<string, string> = Object.fromEntries(
|
||||
@@ -46,10 +51,6 @@ export function formatRatioPercent(value?: number) {
|
||||
return `${n.toFixed(n % 1 === 0 ? 0 : 1)}%`
|
||||
}
|
||||
|
||||
export function formatPaymentFeeBps(bps?: number) {
|
||||
return `${Number(bps || 0) / 100}%`
|
||||
}
|
||||
|
||||
export function ruleSplitParts(rule: DistributionRule) {
|
||||
switch (rule.mode) {
|
||||
case 'two_party_distributor_receive':
|
||||
@@ -67,6 +68,8 @@ export function ruleSplitParts(rule: DistributionRule) {
|
||||
{ label: '收号站', value: formatRatioPercent(rule.distributor_a_ratio) },
|
||||
{ label: '卖出站', value: formatRatioPercent(rule.distributor_b_ratio) },
|
||||
]
|
||||
case 'site_self_sell':
|
||||
return [{ label: '本站卖出', value: formatRatioPercent(rule.distributor_sell_ratio) }]
|
||||
default:
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ onMounted(async () => {
|
||||
<div>
|
||||
<h2 class="page-title">分销订单</h2>
|
||||
<p class="page-subtitle">
|
||||
查看分销订单的利润基数、主站承担的通道费,以及冷却期释放状态。不要把订单履约结算和分销入账混为一谈。
|
||||
查看分销订单的利润基数与冷却期释放状态。不要把订单履约结算和分销入账混为一谈。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -171,12 +171,6 @@ onMounted(async () => {
|
||||
{{ formatCent(row.split_base_amount_cent) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="通道费" min-width="110" align="right">
|
||||
<template #default="{ row }">
|
||||
<div>{{ formatCent(row.payment_fee_cent) }}</div>
|
||||
<small class="muted">主站承担</small>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="服务费" min-width="90" align="right">
|
||||
<template #default="{ row }">
|
||||
{{ formatCent(row.service_fee_cent) }}
|
||||
@@ -255,9 +249,4 @@ onMounted(async () => {
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: #9ca3af;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
import {
|
||||
DISTRIBUTION_MODES,
|
||||
distributionModeLabel,
|
||||
formatPaymentFeeBps,
|
||||
formatServiceFee,
|
||||
ruleSplitParts,
|
||||
} from '@/features/admin/utils/distributionLabels'
|
||||
@@ -94,7 +93,7 @@ function resetCreateForm() {
|
||||
createForm.site_id = selectedSiteId.value
|
||||
createForm.mode = selectedMode.value || 'two_party_distributor_receive'
|
||||
createForm.receive_ratio = 35
|
||||
createForm.sell_ratio = 40
|
||||
createForm.sell_ratio = createForm.mode === 'site_self_sell' ? 75 : 40
|
||||
createForm.service_fee_value = 25
|
||||
createForm.payment_fee_bps = 50
|
||||
createForm.tax_enabled = false
|
||||
@@ -102,6 +101,12 @@ function resetCreateForm() {
|
||||
createForm.activate = true
|
||||
}
|
||||
|
||||
function handleModeChange(mode: string) {
|
||||
createForm.sell_ratio = mode === 'site_self_sell' ? 75 : 40
|
||||
createForm.receive_ratio = 35
|
||||
createForm.service_fee_value = 25
|
||||
}
|
||||
|
||||
function handleOpenCreate() {
|
||||
resetCreateForm()
|
||||
createVisible.value = true
|
||||
@@ -123,6 +128,8 @@ function buildCreatePayload(): CreateRulePayload {
|
||||
} else if (createForm.mode === 'two_party_platform_receive') {
|
||||
payload.platform_receive_ratio = Number(createForm.receive_ratio)
|
||||
payload.distributor_sell_ratio = Number(createForm.sell_ratio)
|
||||
} else if (createForm.mode === 'site_self_sell') {
|
||||
payload.distributor_sell_ratio = Number(createForm.sell_ratio)
|
||||
} else {
|
||||
payload.distributor_a_ratio = Number(createForm.receive_ratio)
|
||||
payload.distributor_b_ratio = Number(createForm.sell_ratio)
|
||||
@@ -132,11 +139,17 @@ function buildCreatePayload(): CreateRulePayload {
|
||||
|
||||
async function handleCreate() {
|
||||
const total =
|
||||
Number(createForm.receive_ratio) +
|
||||
Number(createForm.sell_ratio) +
|
||||
Number(createForm.service_fee_value)
|
||||
createForm.mode === 'site_self_sell'
|
||||
? Number(createForm.sell_ratio) + Number(createForm.service_fee_value)
|
||||
: Number(createForm.receive_ratio) +
|
||||
Number(createForm.sell_ratio) +
|
||||
Number(createForm.service_fee_value)
|
||||
if (Math.abs(total - 100) > 0.05) {
|
||||
ElMessage.warning(`收号 + 卖出 + 服务费应为 100%,当前为 ${total}%`)
|
||||
ElMessage.warning(
|
||||
createForm.mode === 'site_self_sell'
|
||||
? `本站分成 + 服务费应为 100%,当前为 ${total}%`
|
||||
: `收号 + 卖出 + 服务费应为 100%,当前为 ${total}%`
|
||||
)
|
||||
return
|
||||
}
|
||||
creating.value = true
|
||||
@@ -167,7 +180,7 @@ onMounted(async () => {
|
||||
<div>
|
||||
<h2 class="page-title">分销规则配置</h2>
|
||||
<p class="page-subtitle">
|
||||
按模式配置利润分成。利润 = 净付款 − 号主 − 加价;通道费由主站承担,只记账不进分成。
|
||||
按模式配置利润分成。利润 = 净付款 − 号主 − 加价,再按收号 / 卖出 / 服务费切分。
|
||||
</p>
|
||||
</div>
|
||||
<div class="header-filters">
|
||||
@@ -208,7 +221,7 @@ onMounted(async () => {
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="rules-alert"
|
||||
title="默认 35% 收号 / 40% 卖出 / 25% 服务费。站点专属规则优先于全局默认;通道费默认 0.5%,从主站服务费与角色分成中承担。"
|
||||
title="默认 35% 收号 / 40% 卖出 / 25% 服务费。站点专属规则优先于全局默认。"
|
||||
/>
|
||||
|
||||
<el-card shadow="never">
|
||||
@@ -240,14 +253,6 @@ onMounted(async () => {
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="通道费" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<div class="fee-cell">
|
||||
<span>{{ formatPaymentFeeBps(row.payment_fee_bps) }}</span>
|
||||
<small>主站承担</small>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="税费" min-width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.tax_enabled" type="warning" size="small">
|
||||
@@ -287,7 +292,7 @@ onMounted(async () => {
|
||||
:closable="false"
|
||||
type="info"
|
||||
show-icon
|
||||
:title="`${createModeMeta?.label}:${createModeMeta?.hint}。分成三项合计应为 100%。`"
|
||||
:title="`${createModeMeta?.label}:${createModeMeta?.hint}。${createForm.mode === 'site_self_sell' ? '本站分成与服务费合计应为 100%。' : '分成三项合计应为 100%。'}`"
|
||||
style="margin-bottom: 16px"
|
||||
/>
|
||||
<el-form :model="createForm" label-width="120px">
|
||||
@@ -307,7 +312,7 @@ onMounted(async () => {
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分销模式">
|
||||
<el-select v-model="createForm.mode" style="width: 100%">
|
||||
<el-select v-model="createForm.mode" style="width: 100%" @change="handleModeChange">
|
||||
<el-option
|
||||
v-for="item in DISTRIBUTION_MODES"
|
||||
:key="item.value"
|
||||
@@ -316,7 +321,10 @@ onMounted(async () => {
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="createForm.mode === 'three_party' ? '收号站分成' : '收号分成'">
|
||||
<el-form-item
|
||||
v-if="createForm.mode !== 'site_self_sell'"
|
||||
:label="createForm.mode === 'three_party' ? '收号站分成' : '收号分成'"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="createForm.receive_ratio"
|
||||
:min="0"
|
||||
@@ -325,7 +333,15 @@ onMounted(async () => {
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="createForm.mode === 'three_party' ? '卖出站分成' : '卖出分成'">
|
||||
<el-form-item
|
||||
:label="
|
||||
createForm.mode === 'site_self_sell'
|
||||
? '本站分成'
|
||||
: createForm.mode === 'three_party'
|
||||
? '卖出站分成'
|
||||
: '卖出分成'
|
||||
"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="createForm.sell_ratio"
|
||||
:min="0"
|
||||
@@ -343,18 +359,6 @@ onMounted(async () => {
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="通道费率 BPS">
|
||||
<el-input-number
|
||||
v-model="createForm.payment_fee_bps"
|
||||
:min="0"
|
||||
:max="10000"
|
||||
:step="1"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<div class="form-hint">
|
||||
当前 {{ formatPaymentFeeBps(createForm.payment_fee_bps) }},由主站承担,不从利润里扣。
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="征收税费">
|
||||
<el-switch v-model="createForm.tax_enabled" />
|
||||
</el-form-item>
|
||||
@@ -447,22 +451,4 @@ onMounted(async () => {
|
||||
background: #eff6ff;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.fee-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.fee-cell small {
|
||||
color: #9ca3af;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.form-hint {
|
||||
margin-top: 4px;
|
||||
color: #6b7280;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
fetchDistributionSettlementItems,
|
||||
createDistributionSettlementBatch,
|
||||
settleDistributionBatch,
|
||||
cancelDistributionBatch,
|
||||
fetchDistributionSites,
|
||||
type DistributionSettlementBatch,
|
||||
type DistributionSettlementItem,
|
||||
@@ -104,6 +105,21 @@ async function handleSettle(row: DistributionSettlementBatch) {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCancel(row: DistributionSettlementBatch) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`作废批次 ${row.batch_no} 后,其中订单可重新进入自动释放或新的人工批次。`,
|
||||
'作废未入账批次',
|
||||
{ type: 'warning', confirmButtonText: '确认作废', cancelButtonText: '取消' }
|
||||
)
|
||||
await cancelDistributionBatch(row.id)
|
||||
ElMessage.success('批次已作废')
|
||||
await loadBatches()
|
||||
} catch {
|
||||
// cancelled or error handled
|
||||
}
|
||||
}
|
||||
|
||||
async function handleViewItems(row: DistributionSettlementBatch) {
|
||||
currentBatch.value = row
|
||||
itemsDialogVisible.value = true
|
||||
@@ -119,6 +135,7 @@ async function handleViewItems(row: DistributionSettlementBatch) {
|
||||
|
||||
function batchStatus(row: DistributionSettlementBatch) {
|
||||
if (row.status === 'settled') return { label: '已释放入账', type: 'success' as const }
|
||||
if (row.status === 'cancelled') return { label: '已作废', type: 'info' as const }
|
||||
if (row.status === 'pending' || row.status === 'draft')
|
||||
return { label: '待入账', type: 'warning' as const }
|
||||
return { label: row.status, type: 'info' as const }
|
||||
@@ -193,17 +210,25 @@ onMounted(() => {
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<el-table-column label="操作" width="220" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="handleViewItems(row)">查看明细</el-button>
|
||||
<el-button
|
||||
v-if="row.status !== 'settled'"
|
||||
v-if="row.status !== 'settled' && row.status !== 'cancelled'"
|
||||
link
|
||||
type="success"
|
||||
@click="handleSettle(row)"
|
||||
>
|
||||
立即释放入账
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status !== 'settled' && row.status !== 'cancelled'"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleCancel(row)"
|
||||
>
|
||||
作废
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
@@ -63,9 +63,6 @@ const onboardForm = reactive({
|
||||
admin_password: '',
|
||||
admin_nickname: '',
|
||||
member_type: 'operator',
|
||||
distributor_receive_ratio: 35,
|
||||
platform_sell_ratio: 40,
|
||||
service_fee_value: 25,
|
||||
settlement_hold_days: 7,
|
||||
display_account_enabled: true,
|
||||
tax_enabled: false,
|
||||
@@ -105,9 +102,6 @@ function handleOpenOnboard() {
|
||||
onboardForm.admin_password = ''
|
||||
onboardForm.admin_nickname = ''
|
||||
onboardForm.member_type = 'operator'
|
||||
onboardForm.distributor_receive_ratio = 35
|
||||
onboardForm.platform_sell_ratio = 40
|
||||
onboardForm.service_fee_value = 25
|
||||
onboardForm.settlement_hold_days = 7
|
||||
onboardForm.display_account_enabled = true
|
||||
onboardForm.tax_enabled = false
|
||||
@@ -129,9 +123,6 @@ async function handleOnboardSubmit() {
|
||||
admin_password: onboardForm.admin_password,
|
||||
admin_nickname: onboardForm.admin_nickname.trim() || undefined,
|
||||
member_type: onboardForm.member_type,
|
||||
distributor_receive_ratio: Number(onboardForm.distributor_receive_ratio),
|
||||
platform_sell_ratio: Number(onboardForm.platform_sell_ratio),
|
||||
service_fee_value: Number(onboardForm.service_fee_value),
|
||||
settlement_hold_days: Number(onboardForm.settlement_hold_days),
|
||||
display_account_enabled: onboardForm.display_account_enabled,
|
||||
tax_enabled: onboardForm.tax_enabled,
|
||||
@@ -189,8 +180,7 @@ onMounted(() => {
|
||||
<div>
|
||||
<h2 class="page-title">分销站点管理</h2>
|
||||
<p class="page-subtitle">
|
||||
开通分销站、配置结算冷却期。利润按 35% 收号 / 40% 卖出 / 25%
|
||||
服务费切分,通道费由主站承担。
|
||||
开通分销站、配置结算冷却期。利润按 35% 收号 / 40% 卖出 / 25% 服务费切分。
|
||||
</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
@@ -325,50 +315,15 @@ onMounted(() => {
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div class="form-section-title">3. 分站收号规则与冷却期</div>
|
||||
<div class="form-section-title">3. 结算冷却期与展示</div>
|
||||
<el-alert
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="此处只生成「两方 · 分站收号」草稿。利润 = 净付款 − 号主 − 加价;通道费 0.5% 由主站承担。另外两种模式请到规则页按全局默认启用或单独配置。"
|
||||
title="开通后四种分成模式均按默认生效:收号 35% / 卖出 40% / 服务费 25%(本站自卖为 75% + 25%)。之后可在「分销规则」页单独调整。"
|
||||
style="margin-bottom: 12px"
|
||||
/>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="分站收号分成 (%)">
|
||||
<el-input-number
|
||||
v-model="onboardForm.distributor_receive_ratio"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="0.1"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="主站卖出分成 (%)">
|
||||
<el-input-number
|
||||
v-model="onboardForm.platform_sell_ratio"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="0.1"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="平台服务费 (%)">
|
||||
<el-input-number
|
||||
v-model="onboardForm.service_fee_value"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="1"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结算冷却期 (天)">
|
||||
<el-input-number
|
||||
@@ -380,19 +335,15 @@ onMounted(() => {
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="展示主站账号">
|
||||
<el-switch v-model="onboardForm.display_account_enabled" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否征税">
|
||||
<el-switch v-model="onboardForm.tax_enabled" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="是否征税">
|
||||
<el-switch v-model="onboardForm.tax_enabled" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="onboardDialogVisible = false">取消</el-button>
|
||||
@@ -425,10 +376,9 @@ onMounted(() => {
|
||||
<el-descriptions-item label="分销后台地址">
|
||||
<code>http://{{ windowHost }}:5190/login</code>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="初始规则">
|
||||
分站收号 {{ onboardResult.rule.distributor_receive_ratio }}% · 主站卖出
|
||||
{{ onboardResult.rule.platform_sell_ratio }}% · 服务费
|
||||
{{ onboardResult.rule.service_fee_value }}%(草稿,通道费主站承担)
|
||||
<el-descriptions-item label="初始分成规则">
|
||||
四种模式均已按默认生效:收号 35% / 卖出 40% / 服务费 25%(本站自卖 75% +
|
||||
25%),可到规则页调整。
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="结算冷却期">
|
||||
{{ onboardResult.site.settlement_hold_days }} 天
|
||||
|
||||
Reference in New Issue
Block a user