退款时压缩卖方加价,人工/自动批次仅完结单可入账,号主收入进主站钱包;仲裁后重算释放并支持作废未入账 DS。开通时四种模式按默认生效,对外口径合并为 35/40/25,不再单独展示通道费。 Co-authored-by: Cursor <cursoragent@cursor.com>
307 lines
11 KiB
TypeScript
307 lines
11 KiB
TypeScript
import { useEffect, useState, useCallback } from 'react'
|
|
import { Card, Table, Typography, Space, Select, Tag, message, Row, Col, Divider, Alert, Button } from 'antd'
|
|
import {
|
|
SafetyCertificateOutlined,
|
|
DollarCircleOutlined,
|
|
ThunderboltOutlined,
|
|
CheckCircleOutlined,
|
|
LockOutlined,
|
|
} from '@ant-design/icons'
|
|
import { listRules, listSites, updateRuleStatus, getMe, type Rule, type Site } from '../api'
|
|
import { useMember } from '../MemberContext'
|
|
|
|
const MODE_LABELS: Record<string, string> = {
|
|
two_party_distributor_receive: '分销收号模式 (主站售出 / 散户在本站上传)',
|
|
two_party_platform_receive: '分销卖号模式 (分站售出 / 主站共享号源池)',
|
|
three_party: '分销跨站协同模式 (分站A收号 + 分站B卖出)',
|
|
site_self_sell: '分站自收自卖模式',
|
|
}
|
|
|
|
export default function Rules() {
|
|
const member = useMember()
|
|
const [sites, setSites] = useState<Site[]>([])
|
|
const [siteID, setSiteID] = useState<number | undefined>(member?.site_id)
|
|
const [rules, setRules] = useState<Rule[]>([])
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
const load = useCallback(async (site?: number) => {
|
|
setLoading(true)
|
|
try {
|
|
setRules(await listRules(site))
|
|
} catch {
|
|
message.error('分成规则加载失败')
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
async function init() {
|
|
let currentMember = member
|
|
if (!currentMember) {
|
|
try {
|
|
currentMember = await getMe()
|
|
} catch {
|
|
// ignore
|
|
}
|
|
}
|
|
const activeSiteID = currentMember?.site_id
|
|
if (activeSiteID) {
|
|
setSiteID(activeSiteID)
|
|
void load(activeSiteID)
|
|
}
|
|
|
|
if (currentMember?.is_superadmin) {
|
|
try {
|
|
const rows = await listSites()
|
|
setSites(rows)
|
|
if (!activeSiteID) {
|
|
void load(undefined)
|
|
}
|
|
} catch {
|
|
// ignore
|
|
}
|
|
}
|
|
}
|
|
void init()
|
|
}, [member, load])
|
|
|
|
async function toggleStatus(rule: Rule) {
|
|
const next = rule.status === 'active' ? 'disabled' : 'active'
|
|
try {
|
|
await updateRuleStatus(rule.id, next)
|
|
message.success(`规则已${next === 'active' ? '启用' : '停用'}`)
|
|
void load(siteID)
|
|
} catch {
|
|
message.error('状态更新失败')
|
|
}
|
|
}
|
|
|
|
const isSuperadmin = member?.is_superadmin
|
|
|
|
return (
|
|
<Space direction="vertical" className="page" style={{ width: '100%' }} size="middle">
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
|
<div>
|
|
<Typography.Title level={3} style={{ marginBottom: 4 }}>
|
|
{isSuperadmin ? '分成规则配置与管理' : '分销商合作协议与分成政策'}
|
|
</Typography.Title>
|
|
<Typography.Text type="secondary">
|
|
{isSuperadmin
|
|
? '主站超管可维护各交易模式的分账比例、服务费与税费政策'
|
|
: '查看当前分销站签约的收益权益、分成政策与资金结算恒等式说明'}
|
|
</Typography.Text>
|
|
</div>
|
|
{isSuperadmin && sites.length > 1 && (
|
|
<Select
|
|
allowClear
|
|
placeholder="切换分销网站"
|
|
style={{ width: 220 }}
|
|
value={siteID}
|
|
options={sites.map((site) => ({ label: `${site.site_name} (${site.site_code})`, value: site.id }))}
|
|
onChange={(value) => {
|
|
setSiteID(value)
|
|
void load(value)
|
|
}}
|
|
/>
|
|
)}
|
|
</div>
|
|
|
|
{/* 4 Core Rights Cards */}
|
|
<Card title="分销商核心权益与收益构成">
|
|
<Row gutter={[16, 16]}>
|
|
<Col xs={24} md={6}>
|
|
<div
|
|
style={{
|
|
background: '#f6ffed',
|
|
border: '1px solid #b7eb8f',
|
|
borderRadius: 8,
|
|
padding: '16px',
|
|
height: '100%',
|
|
}}
|
|
>
|
|
<Space direction="vertical" style={{ width: '100%' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
<ThunderboltOutlined style={{ color: '#52c41a', fontSize: 20 }} />
|
|
<Typography.Text strong style={{ fontSize: 16, color: '#389e0d' }}>
|
|
自主加价 100% 独享
|
|
</Typography.Text>
|
|
</div>
|
|
<Typography.Text type="secondary" style={{ fontSize: 13 }}>
|
|
分销站在主站基准价上自行上浮的全部溢价差额,<strong>100% 独立结算至商户账户</strong>,主站不参与任何分润抽成。
|
|
</Typography.Text>
|
|
</Space>
|
|
</div>
|
|
</Col>
|
|
|
|
<Col xs={24} md={6}>
|
|
<div
|
|
style={{
|
|
background: '#e6f4ff',
|
|
border: '1px solid #91caff',
|
|
borderRadius: 8,
|
|
padding: '16px',
|
|
height: '100%',
|
|
}}
|
|
>
|
|
<Space direction="vertical" style={{ width: '100%' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
<DollarCircleOutlined style={{ color: '#1677ff', fontSize: 20 }} />
|
|
<Typography.Text strong style={{ fontSize: 16, color: '#0958d9' }}>
|
|
卖号分成 40%
|
|
</Typography.Text>
|
|
</div>
|
|
<Typography.Text type="secondary" style={{ fontSize: 13 }}>
|
|
租客通过本分销站点租赁主站共享号源池账号,获得扣除号主底价后的<strong>主站提价利润池 40% 分成</strong>。
|
|
</Typography.Text>
|
|
</Space>
|
|
</div>
|
|
</Col>
|
|
|
|
<Col xs={24} md={6}>
|
|
<div
|
|
style={{
|
|
background: '#f9f0ff',
|
|
border: '1px solid #d3adf7',
|
|
borderRadius: 8,
|
|
padding: '16px',
|
|
height: '100%',
|
|
}}
|
|
>
|
|
<Space direction="vertical" style={{ width: '100%' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
<SafetyCertificateOutlined style={{ color: '#722ed1', fontSize: 20 }} />
|
|
<Typography.Text strong style={{ fontSize: 16, color: '#531dab' }}>
|
|
收号分成 35%
|
|
</Typography.Text>
|
|
</div>
|
|
<Typography.Text type="secondary" style={{ fontSize: 13 }}>
|
|
散户号主通过本站上传账号并上架,在全网产生租赁订单后,本站长期享受该账号产生的<strong>主站提价利润 35% 分成</strong>。
|
|
</Typography.Text>
|
|
</Space>
|
|
</div>
|
|
</Col>
|
|
|
|
<Col xs={24} md={6}>
|
|
<div
|
|
style={{
|
|
background: '#fffbe6',
|
|
border: '1px solid #ffe58f',
|
|
borderRadius: 8,
|
|
padding: '16px',
|
|
height: '100%',
|
|
}}
|
|
>
|
|
<Space direction="vertical" style={{ width: '100%' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
<CheckCircleOutlined style={{ color: '#fa8c16', fontSize: 20 }} />
|
|
<Typography.Text strong style={{ fontSize: 16, color: '#d46b08' }}>
|
|
底价与结算保障
|
|
</Typography.Text>
|
|
</div>
|
|
<Typography.Text type="secondary" style={{ fontSize: 13 }}>
|
|
号主本金 100% 刚兑保障。利润按收号 / 卖出 / 服务费切分,订单完成并过冷却期后系统自动释放入账至商户钱包。
|
|
</Typography.Text>
|
|
</Space>
|
|
</div>
|
|
</Col>
|
|
</Row>
|
|
</Card>
|
|
|
|
{/* Rules Table */}
|
|
<Card title="当前生效的交易分账规则版本">
|
|
<Table
|
|
rowKey="id"
|
|
loading={loading}
|
|
dataSource={rules}
|
|
pagination={false}
|
|
scroll={{ x: 950 }}
|
|
columns={[
|
|
{
|
|
title: '业务交易模式',
|
|
dataIndex: 'mode',
|
|
width: 280,
|
|
render: (v: string) => (
|
|
<div>
|
|
<strong>{MODE_LABELS[v] ?? v}</strong>
|
|
<div style={{ fontSize: 11, color: '#8c8c8c' }}>模式标识: {v}</div>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
title: '版本号',
|
|
dataIndex: 'version',
|
|
width: 90,
|
|
render: (v: number) => <Tag color="blue">v{v}</Tag>,
|
|
},
|
|
{
|
|
title: '状态',
|
|
dataIndex: 'status',
|
|
width: 100,
|
|
render: (v: string) => (
|
|
<Tag color={v === 'active' ? 'success' : 'default'}>
|
|
{v === 'active' ? '生效中' : '已停用'}
|
|
</Tag>
|
|
),
|
|
},
|
|
{
|
|
title: '分成权益比例',
|
|
render: (_: unknown, r: Rule) => {
|
|
const parts: string[] = []
|
|
if (r.distributor_receive_ratio > 0) parts.push(`分销收号: ${r.distributor_receive_ratio}%`)
|
|
if (r.distributor_sell_ratio > 0) parts.push(`分销卖出: ${r.distributor_sell_ratio}%`)
|
|
if (r.platform_receive_ratio > 0) parts.push(`主站收号: ${r.platform_receive_ratio}%`)
|
|
if (r.platform_sell_ratio > 0) parts.push(`主站卖出: ${r.platform_sell_ratio}%`)
|
|
if (r.distributor_a_ratio > 0) parts.push(`分销A: ${r.distributor_a_ratio}%`)
|
|
if (r.distributor_b_ratio > 0) parts.push(`分销B: ${r.distributor_b_ratio}%`)
|
|
return (
|
|
<Space direction="vertical" size={2}>
|
|
{parts.map((p, idx) => (
|
|
<Tag color="cyan" key={idx}>
|
|
{p}
|
|
</Tag>
|
|
))}
|
|
</Space>
|
|
)
|
|
},
|
|
},
|
|
{
|
|
title: '平台服务费',
|
|
width: 110,
|
|
render: (_: unknown, r: Rule) => <span>{r.service_fee_value}%</span>,
|
|
},
|
|
{
|
|
title: '生效时间',
|
|
dataIndex: 'effective_from',
|
|
width: 160,
|
|
render: (v: string) => (
|
|
<span style={{ fontSize: 12, color: '#595959' }}>
|
|
{v ? new Date(v).toLocaleString('zh-CN', { hour12: false }) : '-'}
|
|
</span>
|
|
),
|
|
},
|
|
...(isSuperadmin
|
|
? [
|
|
{
|
|
title: '操作',
|
|
width: 100,
|
|
render: (_: unknown, r: Rule) => (
|
|
<Button
|
|
type="link"
|
|
size="small"
|
|
danger={r.status === 'active'}
|
|
onClick={() => void toggleStatus(r)}
|
|
>
|
|
{r.status === 'active' ? '停用' : '启用'}
|
|
</Button>
|
|
),
|
|
},
|
|
]
|
|
: []),
|
|
]}
|
|
/>
|
|
</Card>
|
|
</Space>
|
|
)
|
|
}
|