feat(distribution): 完善分销收号资产双视图、商户提现、自主加价、品牌装修与数据大盘

This commit is contained in:
yml2213
2026-09-08 16:05:30 +08:00
parent fd4e490ed3
commit 2c4e87475a
19 changed files with 3145 additions and 108 deletions
+134 -11
View File
@@ -1,11 +1,13 @@
import { useEffect, useState, useMemo } from 'react'
import { Card, Table, Typography, Space, Select, Tag, message, Button, Input, Modal } from 'antd'
import { SyncOutlined, SearchOutlined, CheckCircleOutlined, StopOutlined } from '@ant-design/icons'
import { Card, Table, Typography, Space, Select, Tag, message, Button, Input, Modal, InputNumber } from 'antd'
import { SyncOutlined, SearchOutlined, CheckCircleOutlined, StopOutlined, EditOutlined, DollarCircleOutlined } from '@ant-design/icons'
import {
listAccountDisplays,
listSites,
updateAccountDisplay,
syncSiteAccounts,
updateAccountMarkup,
batchUpdateAccountMarkup,
formatMoney,
type AccountDisplay,
type Site,
@@ -28,6 +30,12 @@ export default function Accounts() {
const [syncing, setSyncing] = useState(false)
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
// Markup modal state
const [markupModalVisible, setMarkupModalVisible] = useState(false)
const [targetAccount, setTargetAccount] = useState<AccountDisplay | null>(null)
const [markupYuan, setMarkupYuan] = useState<number>(0)
const [markupSaving, setMarkupSaving] = useState(false)
async function load(site?: number, statusFilter?: string) {
if (!site) return
setLoading(true)
@@ -111,6 +119,51 @@ export default function Accounts() {
})
}
function handleOpenMarkup(item?: AccountDisplay) {
if (item) {
setTargetAccount(item)
setMarkupYuan((item.markup_amount_cent ?? 0) / 100)
} else {
if (selectedRowKeys.length === 0) {
message.warning('请先勾选需要批量加价的账号商品')
return
}
setTargetAccount(null)
setMarkupYuan(0)
}
setMarkupModalVisible(true)
}
async function handleSaveMarkup() {
if (!siteID) return
const markupCent = Math.round(markupYuan * 100)
if (markupCent < 0) {
message.error('加价金额不能小于0')
return
}
setMarkupSaving(true)
try {
if (targetAccount) {
await updateAccountMarkup(targetAccount.id, markupCent)
message.success('商品加价设置成功!')
} else {
const listingIDs = items
.filter((it) => selectedRowKeys.includes(it.id))
.map((it) => it.listing_id)
await batchUpdateAccountMarkup(siteID, listingIDs, markupCent)
message.success(`已成功为选中的 ${listingIDs.length} 个商品批量设置加价!`)
setSelectedRowKeys([])
}
setMarkupModalVisible(false)
void load(siteID, status)
} catch (error: unknown) {
const respMessage = (error as { response?: { data?: { message?: string } } })?.response?.data?.message
message.error(respMessage || '加价设置保存失败')
} finally {
setMarkupSaving(false)
}
}
const filteredItems = useMemo(() => {
const kw = keyword.trim().toLowerCase()
if (!kw) return items
@@ -191,6 +244,13 @@ export default function Accounts() {
{selectedRowKeys.length > 0 && (
<Space>
<Typography.Text type="secondary">已勾选 {selectedRowKeys.length} 项</Typography.Text>
<Button
size="small"
icon={<DollarCircleOutlined />}
onClick={() => handleOpenMarkup()}
>
批量加价
</Button>
<Button
size="small"
icon={<CheckCircleOutlined />}
@@ -285,6 +345,21 @@ export default function Accounts() {
</div>
),
},
{
title: '自主加价',
dataIndex: 'markup_amount_cent',
width: 120,
render: (v: number | undefined) => {
if (v && v > 0) {
return (
<Tag color="gold" style={{ fontWeight: 'bold' }}>
+{formatMoney(v)}
</Tag>
)
}
return <span style={{ color: '#aaa', fontSize: 12 }}>无加价</span>
},
},
{
title: '主站状态',
dataIndex: 'listing_status',
@@ -306,21 +381,69 @@ export default function Accounts() {
},
{
title: '操作',
width: 120,
width: 160,
render: (_: unknown, row: AccountDisplay) => (
<Button
type="link"
size="small"
danger={row.display_status === 'enabled'}
onClick={() => toggle(row)}
>
{row.display_status === 'enabled' ? '下架' : '恢复展示'}
</Button>
<Space size={2}>
<Button
type="link"
size="small"
icon={<EditOutlined />}
onClick={() => handleOpenMarkup(row)}
>
加价
</Button>
<Button
type="link"
size="small"
danger={row.display_status === 'enabled'}
onClick={() => toggle(row)}
>
{row.display_status === 'enabled' ? '下架' : '展示'}
</Button>
</Space>
),
},
]}
/>
</Card>
<Modal
title={targetAccount ? `设置加价 - ${targetAccount.title || targetAccount.listing_no}` : `批量加价 (${selectedRowKeys.length} 个账号商品)`}
open={markupModalVisible}
onCancel={() => setMarkupModalVisible(false)}
onOk={handleSaveMarkup}
confirmLoading={markupSaving}
okText="保存加价"
destroyOnClose
>
<Space direction="vertical" style={{ width: '100%', padding: '12px 0' }}>
<Typography.Paragraph type="secondary" style={{ marginBottom: 8 }}>
自主加价金额将直接叠加在分销前台展示的每小时租金上,该部分溢价产生的收益将在成单结算时归当前分销商所有。
</Typography.Paragraph>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<Typography.Text strong>每小时加价金额:</Typography.Text>
<InputNumber
min={0}
max={1000}
precision={2}
style={{ width: 180 }}
value={markupYuan}
onChange={(val) => setMarkupYuan(val ?? 0)}
addonAfter="元/小时"
placeholder="0.00"
/>
</div>
{markupYuan > 0 && targetAccount && (
<div style={{ marginTop: 8, padding: '8px 12px', background: '#f6ffed', borderRadius: 4, border: '1px solid #b7eb8f', fontSize: 12 }}>
原租金 {formatMoney(targetAccount.price_cent)}/时 ➔ 分销前台标价为{' '}
<strong style={{ color: '#cf1322' }}>
¥{(((targetAccount.price_cent ?? 0) + Math.round(markupYuan * 100)) / 100).toFixed(2)}
</strong>
/时
</div>
)}
</Space>
</Modal>
</Space>
)
}