优化积分明细与商户中心UI排版

This commit is contained in:
yml2213
2026-08-04 11:07:52 +08:00
parent 627d5cafa6
commit 71bdbf527c
+99 -49
View File
@@ -3,12 +3,14 @@ import { useLocation } from 'react-router-dom'
import { import {
Button, Button,
Card, Card,
Col,
Descriptions, Descriptions,
Form, Form,
Input, Input,
InputNumber, InputNumber,
Modal, Modal,
Popconfirm, Popconfirm,
Row,
Select, Select,
Space, Space,
Table, Table,
@@ -19,7 +21,9 @@ import {
} from 'antd' } from 'antd'
import { import {
ApiOutlined, ApiOutlined,
ClockCircleOutlined,
CopyOutlined, CopyOutlined,
DollarOutlined,
LinkOutlined, LinkOutlined,
PlusOutlined, PlusOutlined,
ReloadOutlined, ReloadOutlined,
@@ -28,6 +32,7 @@ import {
import type { ColumnsType } from 'antd/es/table' import type { ColumnsType } from 'antd/es/table'
import { merchantApi } from '../api' import { merchantApi } from '../api'
import { formatDateTime } from '../utils/time' import { formatDateTime } from '../utils/time'
import { PageHeader } from '../components/PageHeader'
import type { import type {
ApiClient, ApiClient,
ApiCredential, ApiCredential,
@@ -553,13 +558,13 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
] ]
const ledgerColumns: ColumnsType<WalletLedgerEntry> = [ const ledgerColumns: ColumnsType<WalletLedgerEntry> = [
{ title: '流水号', dataIndex: 'entry_no', width: 300, render: (v) => <Typography.Text code copyable={{ tooltips: false }}>{v}</Typography.Text> }, { title: '流水号', dataIndex: 'entry_no', width: 260, render: (v) => <Typography.Text code copyable={{ tooltips: false }}>{v}</Typography.Text> },
{ title: '类型', dataIndex: 'type', width: 80, render: ledgerTypeTag }, { title: '类型', dataIndex: 'type', width: 90, render: ledgerTypeTag },
{ title: '金额', dataIndex: 'amount', width: 120, render: moneyWithSign }, { title: '变动积分', dataIndex: 'amount', width: 130, render: moneyWithSign },
{ title: '余额', dataIndex: 'balance_after', width: 120, render: money }, { title: '结余积分', dataIndex: 'balance_after', width: 130, render: (v) => <span style={{ fontWeight: 600, color: '#0f172a' }}>{money(v)}</span> },
{ title: '关联单号', dataIndex: 'reference_no', width: 200, ellipsis: true, render: (v) => v ? <Typography.Text code ellipsis style={{ maxWidth: '100%' }}>{v}</Typography.Text> : '-' }, { title: '关联单号', dataIndex: 'reference_no', width: 200, ellipsis: true, render: (v) => v ? <Typography.Text code ellipsis style={{ maxWidth: '100%' }}>{v}</Typography.Text> : '-' },
{ title: '备注', dataIndex: 'note', width: 150, ellipsis: { showTitle: false }, render: (v) => <Typography.Text ellipsis title={v}>{v || '-'}</Typography.Text> }, { title: '备注说明', dataIndex: 'note', width: 220, ellipsis: { showTitle: false }, render: (v) => <Typography.Text ellipsis title={v}>{v || '-'}</Typography.Text> },
{ title: '时间', dataIndex: 'created_at', width: 170, render: formatDateTime }, { title: '变动时间', dataIndex: 'created_at', width: 170, render: formatDateTime },
] ]
const apiClientColumns: ColumnsType<ApiClient> = [ const apiClientColumns: ColumnsType<ApiClient> = [
@@ -645,38 +650,88 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
const walletContent = ( const walletContent = (
<Space direction="vertical" style={{ width: '100%' }} size="middle"> <Space direction="vertical" style={{ width: '100%' }} size="middle">
<Space style={{ width: '100%', justifyContent: 'space-between' }} size="large"> <Row gutter={[16, 16]}>
<Descriptions size="small" bordered column={3}> <Col xs={24} sm={8}>
<Descriptions.Item label="可用余额">{money(wallet?.available_balance ?? 0)}</Descriptions.Item> <div className="metric-card-box">
<Descriptions.Item label="冻结余额">{money(wallet?.frozen_balance ?? 0)}</Descriptions.Item> <div className="metric-card-header">
<Descriptions.Item label="币种">{wallet?.currency || 'POINT'}</Descriptions.Item> <span className="metric-card-title"></span>
</Descriptions> <div className="metric-card-icon metric-card-icon--emerald"><WalletOutlined /></div>
{canFinance && ( </div>
<Button <div className="metric-card-value" style={{ color: '#16a34a' }}>
type="primary" {(wallet?.available_balance ?? 0).toLocaleString('zh-CN')}
icon={<WalletOutlined />} <span style={{ fontSize: 13, fontWeight: 500, marginLeft: 4, color: '#64748b' }}></span>
onClick={() => { </div>
walletForm.resetFields() {canFinance && (
walletForm.setFieldsValue({ idempotency_key: `manual-${Date.now()}` }) <div className="metric-card-footer">
setWalletOpen(true) <span>/</span>
}} <Button
> type="link"
size="small"
</Button> icon={<WalletOutlined />}
)} style={{ padding: 0, height: 'auto' }}
</Space> onClick={() => {
walletForm.resetFields()
walletForm.setFieldsValue({ idempotency_key: `manual-${Date.now()}` })
setWalletOpen(true)
}}
>
/
</Button>
</div>
)}
</div>
</Col>
<Col xs={24} sm={8}>
<div className="metric-card-box">
<div className="metric-card-header">
<span className="metric-card-title"></span>
<div className="metric-card-icon"><ClockCircleOutlined /></div>
</div>
<div className="metric-card-value">
{(wallet?.frozen_balance ?? 0).toLocaleString('zh-CN')}
<span style={{ fontSize: 13, fontWeight: 500, marginLeft: 4, color: '#64748b' }}></span>
</div>
<div className="metric-card-footer">
<span>/</span>
<span style={{ fontWeight: 600, color: '#0f172a' }}>0 </span>
</div>
</div>
</Col>
<Col xs={24} sm={8}>
<div className="metric-card-box">
<div className="metric-card-header">
<span className="metric-card-title"></span>
<div className="metric-card-icon metric-card-icon--purple"><DollarOutlined /></div>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 4 }}>
<Tag color="blue" style={{ fontSize: 13, padding: '2px 8px', borderRadius: 4, margin: 0, fontWeight: 600 }}>
{wallet?.currency || 'POINT'}
</Tag>
<span className="status-tag status-tag--green">
<span className="status-dot"></span>
</span>
</div>
<div className="metric-card-footer">
<span></span>
<span style={{ fontWeight: 600, color: '#0f172a' }}></span>
</div>
</div>
</Col>
</Row>
{canFinance && ( {canFinance && (
<Card size="small" style={{ background: '#fafafa' }}> <Card size="small" style={{ background: '#f8fafc', border: '1px solid #e2e8f0', borderRadius: 8 }}>
<Form form={walletFilterForm} layout="inline" onFinish={handleWalletFilter}> <Form form={walletFilterForm} layout="inline" onFinish={handleWalletFilter}>
<Form.Item name="reference_no" label="关联单号"> <Form.Item name="reference_no" label="关联单号">
<Input placeholder="店铺单号 / 充值单号" allowClear style={{ width: 220 }} /> <Input placeholder="店铺单号 / 充值单号" allowClear style={{ width: 220, borderRadius: 6 }} />
</Form.Item> </Form.Item>
<Form.Item name="type" label="收支"> <Form.Item name="type" label="收支类型">
<Select placeholder="请选择" allowClear style={{ width: 120 }}> <Select placeholder="全部类型" allowClear style={{ width: 140 }}>
<Select.Option value="credit"></Select.Option> <Select.Option value="credit"> (Credit)</Select.Option>
<Select.Option value="debit"></Select.Option> <Select.Option value="debit"> (Debit)</Select.Option>
<Select.Option value="refund">退</Select.Option> <Select.Option value="refund">退 (Refund)</Select.Option>
<Select.Option value="adjust"></Select.Option> <Select.Option value="adjust"> (Adjust)</Select.Option>
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
@@ -796,21 +851,16 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
return ( return (
<div> <div>
<Space style={{ marginBottom: 16, width: '100%', justifyContent: 'space-between' }}> <PageHeader
<div> title={title}
<Typography.Title level={4} style={{ margin: 0 }}> subtitle={merchant ? `${merchant.name} (${merchant.code}) · 当前身份: ${merchantRole ? roleText(merchantRole) : '-'}` : '商户配置与管理中心'}
{title} breadcrumbs={[{ title: title }]}
</Typography.Title> extra={
{!fixedTab && ( <Button icon={<ReloadOutlined />} loading={loading} onClick={loadAll}>
<Typography.Text type="secondary">
{merchant ? `${merchant.name} / ${merchant.code}` : '加载中'} · {merchantRole ? roleText(merchantRole) : '-'} </Button>
</Typography.Text> }
)} />
</div>
<Button icon={<ReloadOutlined />} loading={loading} onClick={loadAll}>
</Button>
</Space>
{fixedTab ? fixedTabContent : ( {fixedTab ? fixedTabContent : (
<Tabs <Tabs