优化钱包界面:改名积分明细,添加筛选功能,优化表格列宽
- 侧边栏和页面标题改为'积分明细' - 新增关联单号、收支类型筛选功能 - 后端 ledger API 支持 reference_no 和 type 筛选参数 - 表格列宽优化,流水号完整展示,备注列限制宽度 - 隐藏固定 tab 模式下的商户介绍文字
This commit is contained in:
@@ -204,7 +204,9 @@ func (h *MerchantHandler) GetWallet(c *gin.Context) {
|
|||||||
|
|
||||||
func (h *MerchantHandler) ListWalletLedger(c *gin.Context) {
|
func (h *MerchantHandler) ListWalletLedger(c *gin.Context) {
|
||||||
page, size := pageParams(c)
|
page, size := pageParams(c)
|
||||||
list, total, err := h.fulfillmentSvc.ListWalletLedger(middleware.GetMerchantID(c), page, size)
|
referenceNo := c.Query("reference_no")
|
||||||
|
entryType := c.Query("type")
|
||||||
|
list, total, err := h.fulfillmentSvc.ListWalletLedger(middleware.GetMerchantID(c), page, size, referenceNo, entryType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.ServerError(c, err.Error())
|
response.ServerError(c, err.Error())
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -694,9 +694,15 @@ func (s *FulfillmentService) GetWallet(merchantID uint) (*model.WalletAccount, e
|
|||||||
return &wallet, nil
|
return &wallet, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FulfillmentService) ListWalletLedger(merchantID uint, page, size int) ([]model.WalletLedgerEntry, int64, error) {
|
func (s *FulfillmentService) ListWalletLedger(merchantID uint, page, size int, referenceNo, entryType string) ([]model.WalletLedgerEntry, int64, error) {
|
||||||
page, size = normalizePage(page, size)
|
page, size = normalizePage(page, size)
|
||||||
tx := s.db.Model(&model.WalletLedgerEntry{}).Where("merchant_id = ?", merchantID)
|
tx := s.db.Model(&model.WalletLedgerEntry{}).Where("merchant_id = ?", merchantID)
|
||||||
|
if referenceNo != "" {
|
||||||
|
tx = tx.Where("reference_no LIKE ?", "%"+referenceNo+"%")
|
||||||
|
}
|
||||||
|
if entryType != "" {
|
||||||
|
tx = tx.Where("type = ?", entryType)
|
||||||
|
}
|
||||||
var total int64
|
var total int64
|
||||||
if err := tx.Count(&total).Error; err != nil {
|
if err := tx.Count(&total).Error; err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ function AppRoutes() {
|
|||||||
<Route path="merchant-center" element={<MerchantCenter />} />
|
<Route path="merchant-center" element={<MerchantCenter />} />
|
||||||
<Route path="merchant-products" element={<MerchantCenter fixedTab="products" title="商品" />} />
|
<Route path="merchant-products" element={<MerchantCenter fixedTab="products" title="商品" />} />
|
||||||
<Route path="merchant-orders" element={<MerchantCenter fixedTab="orders" title="发货订单" />} />
|
<Route path="merchant-orders" element={<MerchantCenter fixedTab="orders" title="发货订单" />} />
|
||||||
<Route path="merchant-wallet" element={<MerchantCenter fixedTab="wallet" title="钱包" />} />
|
<Route path="merchant-wallet" element={<MerchantCenter fixedTab="wallet" title="积分明细" />} />
|
||||||
<Route path="merchant-members" element={<MerchantCenter fixedTab="members" title="成员" />} />
|
<Route path="merchant-members" element={<MerchantCenter fixedTab="members" title="成员" />} />
|
||||||
<Route path="merchant-callbacks" element={<MerchantCenter fixedTab="callbacks" title="回调" />} />
|
<Route path="merchant-callbacks" element={<MerchantCenter fixedTab="callbacks" title="回调" />} />
|
||||||
<Route path="merchant-api-keys" element={<MerchantCenter fixedTab="api" title="API 密钥" />} />
|
<Route path="merchant-api-keys" element={<MerchantCenter fixedTab="api" title="API 密钥" />} />
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ const adminSections: SidebarSection[] = [
|
|||||||
key: 'funds',
|
key: 'funds',
|
||||||
label: '资金',
|
label: '资金',
|
||||||
icon: <WalletOutlined />,
|
icon: <WalletOutlined />,
|
||||||
children: [{ key: 'merchant-wallet', label: '钱包', path: '/merchant-wallet' }],
|
children: [{ key: 'merchant-wallet', label: '积分明细', path: '/merchant-wallet' }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'open-api',
|
key: 'open-api',
|
||||||
@@ -116,7 +116,7 @@ const merchantSections: SidebarSection[] = [
|
|||||||
key: 'funds',
|
key: 'funds',
|
||||||
label: '资金',
|
label: '资金',
|
||||||
icon: <WalletOutlined />,
|
icon: <WalletOutlined />,
|
||||||
children: [{ key: 'merchant-wallet', label: '钱包', path: '/merchant-wallet' }],
|
children: [{ key: 'merchant-wallet', label: '积分明细', path: '/merchant-wallet' }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'staff',
|
key: 'staff',
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
|
|||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation } from 'react-router-dom'
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
Card,
|
||||||
Descriptions,
|
Descriptions,
|
||||||
Form,
|
Form,
|
||||||
Input,
|
Input,
|
||||||
@@ -112,6 +113,7 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
|||||||
const [testOrderResult, setTestOrderResult] = useState<CreateTestOrderResult | null>(null)
|
const [testOrderResult, setTestOrderResult] = useState<CreateTestOrderResult | null>(null)
|
||||||
const [productForm] = Form.useForm()
|
const [productForm] = Form.useForm()
|
||||||
const [walletForm] = Form.useForm()
|
const [walletForm] = Form.useForm()
|
||||||
|
const [walletFilterForm] = Form.useForm()
|
||||||
const [apiClientForm] = Form.useForm()
|
const [apiClientForm] = Form.useForm()
|
||||||
const [callbackForm] = Form.useForm()
|
const [callbackForm] = Form.useForm()
|
||||||
const [memberForm] = Form.useForm()
|
const [memberForm] = Form.useForm()
|
||||||
@@ -192,11 +194,12 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
|||||||
page = ledger.page,
|
page = ledger.page,
|
||||||
size = ledger.size,
|
size = ledger.size,
|
||||||
role = merchantRole,
|
role = merchantRole,
|
||||||
|
filters?: { reference_no?: string; type?: string },
|
||||||
) => {
|
) => {
|
||||||
const walletData = await merchantApi.wallet()
|
const walletData = await merchantApi.wallet()
|
||||||
setWallet(walletData)
|
setWallet(walletData)
|
||||||
if (role === 'owner' || role === 'finance') {
|
if (role === 'owner' || role === 'finance') {
|
||||||
const ledgerData = await merchantApi.ledger({ page, size })
|
const ledgerData = await merchantApi.ledger({ page, size, ...filters })
|
||||||
setLedger(ledgerData)
|
setLedger(ledgerData)
|
||||||
} else {
|
} else {
|
||||||
setLedger({ list: [], total: 0, page, size })
|
setLedger({ list: [], total: 0, page, size })
|
||||||
@@ -337,6 +340,19 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleWalletFilter = async () => {
|
||||||
|
const values = await walletFilterForm.validateFields()
|
||||||
|
loadWallet(1, ledger.size, merchantRole, {
|
||||||
|
reference_no: values.reference_no || undefined,
|
||||||
|
type: values.type || undefined,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleWalletFilterReset = () => {
|
||||||
|
walletFilterForm.resetFields()
|
||||||
|
loadWallet(1, ledger.size, merchantRole, {})
|
||||||
|
}
|
||||||
|
|
||||||
const openTestOrderCreate = () => {
|
const openTestOrderCreate = () => {
|
||||||
testOrderForm.resetFields()
|
testOrderForm.resetFields()
|
||||||
testOrderForm.setFieldsValue({
|
testOrderForm.setFieldsValue({
|
||||||
@@ -515,13 +531,13 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
|||||||
]
|
]
|
||||||
|
|
||||||
const ledgerColumns: ColumnsType<WalletLedgerEntry> = [
|
const ledgerColumns: ColumnsType<WalletLedgerEntry> = [
|
||||||
{ title: '流水号', dataIndex: 'entry_no', width: 210, ellipsis: true },
|
{ title: '流水号', dataIndex: 'entry_no', width: 300, render: (v) => <Typography.Text code copyable={{ tooltips: false }}>{v}</Typography.Text> },
|
||||||
{ title: '类型', dataIndex: 'type', width: 90, render: ledgerTypeTag },
|
{ title: '类型', dataIndex: 'type', width: 80, render: ledgerTypeTag },
|
||||||
{ title: '金额', dataIndex: 'amount', width: 110, render: moneyWithSign },
|
{ title: '金额', dataIndex: 'amount', width: 120, render: moneyWithSign },
|
||||||
{ title: '余额', dataIndex: 'balance_after', width: 110, render: money },
|
{ title: '余额', dataIndex: 'balance_after', width: 120, render: money },
|
||||||
{ title: '关联单号', dataIndex: 'reference_no', ellipsis: true },
|
{ title: '关联单号', dataIndex: 'reference_no', width: 200, ellipsis: true, render: (v) => v ? <Typography.Text code ellipsis style={{ maxWidth: '100%' }}>{v}</Typography.Text> : '-' },
|
||||||
{ title: '备注', dataIndex: 'note', ellipsis: true, render: (v) => v || '-' },
|
{ title: '备注', dataIndex: 'note', width: 150, ellipsis: { showTitle: false }, render: (v) => <Typography.Text ellipsis title={v}>{v || '-'}</Typography.Text> },
|
||||||
{ title: '时间', dataIndex: 'created_at', width: 180, render: formatDateTime },
|
{ title: '时间', dataIndex: 'created_at', width: 170, render: formatDateTime },
|
||||||
]
|
]
|
||||||
|
|
||||||
const apiClientColumns: ColumnsType<ApiClient> = [
|
const apiClientColumns: ColumnsType<ApiClient> = [
|
||||||
@@ -612,14 +628,15 @@ 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' }}>
|
<Space style={{ width: '100%', justifyContent: 'space-between' }} size="large">
|
||||||
<Descriptions size="small" bordered column={3} style={{ flex: 1 }}>
|
<Descriptions size="small" bordered column={3}>
|
||||||
<Descriptions.Item label="可用余额">{money(wallet?.available_balance ?? 0)}</Descriptions.Item>
|
<Descriptions.Item label="可用余额">{money(wallet?.available_balance ?? 0)}</Descriptions.Item>
|
||||||
<Descriptions.Item label="冻结余额">{money(wallet?.frozen_balance ?? 0)}</Descriptions.Item>
|
<Descriptions.Item label="冻结余额">{money(wallet?.frozen_balance ?? 0)}</Descriptions.Item>
|
||||||
<Descriptions.Item label="币种">{wallet?.currency || 'POINT'}</Descriptions.Item>
|
<Descriptions.Item label="币种">{wallet?.currency || 'POINT'}</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
{canFinance && (
|
{canFinance && (
|
||||||
<Button
|
<Button
|
||||||
|
type="primary"
|
||||||
icon={<WalletOutlined />}
|
icon={<WalletOutlined />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
walletForm.resetFields()
|
walletForm.resetFields()
|
||||||
@@ -631,6 +648,29 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
|
{canFinance && (
|
||||||
|
<Card size="small" style={{ background: '#fafafa' }}>
|
||||||
|
<Form form={walletFilterForm} layout="inline" onFinish={handleWalletFilter}>
|
||||||
|
<Form.Item name="reference_no" label="关联单号">
|
||||||
|
<Input placeholder="店铺单号 / 充值单号" allowClear style={{ width: 220 }} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="type" label="收支">
|
||||||
|
<Select placeholder="请选择" allowClear style={{ width: 120 }}>
|
||||||
|
<Select.Option value="credit">入账</Select.Option>
|
||||||
|
<Select.Option value="debit">扣款</Select.Option>
|
||||||
|
<Select.Option value="refund">退款</Select.Option>
|
||||||
|
<Select.Option value="adjust">调整</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item>
|
||||||
|
<Space>
|
||||||
|
<Button onClick={handleWalletFilterReset}>重置</Button>
|
||||||
|
<Button type="primary" htmlType="submit">查询</Button>
|
||||||
|
</Space>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
{canFinance ? (
|
{canFinance ? (
|
||||||
<Table
|
<Table
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
@@ -638,7 +678,14 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
|||||||
columns={ledgerColumns}
|
columns={ledgerColumns}
|
||||||
dataSource={ledger.list}
|
dataSource={ledger.list}
|
||||||
tableLayout="fixed"
|
tableLayout="fixed"
|
||||||
pagination={pageConfig(ledger, loadWallet)}
|
scroll={{ x: 1300 }}
|
||||||
|
pagination={pageConfig(ledger, (page, size) => {
|
||||||
|
const values = walletFilterForm.getFieldsValue()
|
||||||
|
loadWallet(page, size, merchantRole, {
|
||||||
|
reference_no: values.reference_no || undefined,
|
||||||
|
type: values.type || undefined,
|
||||||
|
})
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Typography.Text type="secondary">当前角色可查看余额,钱包流水仅财务或负责人可见。</Typography.Text>
|
<Typography.Text type="secondary">当前角色可查看余额,钱包流水仅财务或负责人可见。</Typography.Text>
|
||||||
@@ -705,9 +752,11 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
|||||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||||
{title}
|
{title}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
<Typography.Text type="secondary">
|
{!fixedTab && (
|
||||||
{merchant ? `${merchant.name} / ${merchant.code}` : '加载中'} · {merchantRole ? roleText(merchantRole) : '-'}
|
<Typography.Text type="secondary">
|
||||||
</Typography.Text>
|
{merchant ? `${merchant.name} / ${merchant.code}` : '加载中'} · {merchantRole ? roleText(merchantRole) : '-'}
|
||||||
|
</Typography.Text>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Button icon={<ReloadOutlined />} loading={loading} onClick={loadAll}>
|
<Button icon={<ReloadOutlined />} loading={loading} onClick={loadAll}>
|
||||||
刷新
|
刷新
|
||||||
|
|||||||
Reference in New Issue
Block a user