import { useCallback, useEffect, useState } from 'react' import { Button, Form, Input, InputNumber, Modal, Select, Space, Table, Tag, Typography, message, } from 'antd' import { PlusOutlined, ReloadOutlined, TeamOutlined } from '@ant-design/icons' import type { ColumnsType } from 'antd/es/table' import dayjs from 'dayjs' import { useNavigate } from 'react-router-dom' import { platformApi } from '../api' import type { Merchant, MerchantMember, PageResult } from '../types' const memberRoleOptions = [ { value: 'owner', label: '负责人' }, { value: 'operator', label: '运营' }, { value: 'finance', label: '财务' }, { value: 'viewer', label: '只读' }, ] const featureOptions = [ { value: 'products', label: '商品' }, { value: 'orders', label: '订单' }, { value: 'wallet', label: '钱包' }, { value: 'api', label: 'API' }, { value: 'callbacks', label: '回调' }, ] export default function PlatformMerchants() { const navigate = useNavigate() const [data, setData] = useState>({ list: [], total: 0, page: 1, size: 10 }) const [loading, setLoading] = useState(false) const [createOpen, setCreateOpen] = useState(false) const [settingsOpen, setSettingsOpen] = useState(false) const [memberOpen, setMemberOpen] = useState(false) const [selectedMerchant, setSelectedMerchant] = useState(null) const [createForm] = Form.useForm() const [settingsForm] = Form.useForm() const [memberForm] = Form.useForm() const load = useCallback(async (page = data.page, size = data.size) => { setLoading(true) try { const result = await platformApi.merchants({ page, size }) setData(result) } catch (e) { message.error(e instanceof Error ? e.message : '加载失败') } finally { setLoading(false) } }, [data.page, data.size]) useEffect(() => { load() }, [load]) const submitCreate = async () => { const values = await createForm.validateFields() try { await platformApi.createMerchant({ ...values, features: featureListToText(values.features), fee_type: values.fee_type, fee_rate_bp: Number(values.fee_rate_bp || 0), fee_fixed_amount: yuanToCents(values.fee_fixed_yuan), }) message.success('商户已创建') setCreateOpen(false) createForm.resetFields() load() } catch (e) { message.error(e instanceof Error ? e.message : '创建失败') } } const submitSettings = async () => { if (!selectedMerchant) return const values = await settingsForm.validateFields() try { await platformApi.updateMerchant(selectedMerchant.id, { name: values.name, status: values.status, contact_name: values.contact_name, contact_info: values.contact_info, features: featureListToText(values.features), fee_type: values.fee_type, fee_rate_bp: Number(values.fee_rate_bp || 0), fee_fixed_amount: yuanToCents(values.fee_fixed_yuan), }) message.success('商户设置已更新') setSettingsOpen(false) load() } catch (e) { message.error(e instanceof Error ? e.message : '更新失败') } } const submitMember = async () => { if (!selectedMerchant) return const values = await memberForm.validateFields() try { await platformApi.addMember(selectedMerchant.id, { user_id: values.user_id, role: values.role as MerchantMember['role'], is_default: values.is_default, }) message.success('成员已添加') setMemberOpen(false) } catch (e) { message.error(e instanceof Error ? e.message : '添加失败') } } const columns: ColumnsType = [ { title: 'ID', dataIndex: 'id', width: 80 }, { title: '编码', dataIndex: 'code', width: 180, render: (v) => {v} }, { title: '名称', dataIndex: 'name', ellipsis: true }, { title: '联系人', dataIndex: 'contact_name', width: 120, render: (v) => v || '-' }, { title: '联系方式', dataIndex: 'contact_info', width: 160, render: (v) => v || '-' }, { title: '手续费', key: 'fee', width: 160, render: (_, r) => r.fee_type === 'fixed' ? `固定 ¥${centsToYuan(r.fee_fixed_amount).toFixed(2)}/单` : `${(r.fee_rate_bp / 100).toFixed(2)}%`, }, { title: '功能', dataIndex: 'features', width: 220, render: (v) => ( {featuresToList(v).map((feature) => ( {featureText(feature)} ))} ), }, { title: '状态', dataIndex: 'status', width: 90, render: (v) => v === 'active' ? 启用 : 禁用 }, { title: '创建时间', dataIndex: 'created_at', width: 160, render: (v) => dayjs(v).format('YYYY-MM-DD HH:mm') }, { title: '操作', key: 'action', width: 190, render: (_, record) => ( ), }, ] return (
商户管理 平台管理员维护租户与授权关系。
`共 ${total} 条`, onChange: load, }} /> setCreateOpen(false)} destroyOnClose width={680}>
prev.fee_type !== cur.fee_type}> {({ getFieldValue }) => getFieldValue('fee_type') === 'fixed' ? ( ) : ( ) }
setSettingsOpen(false)} destroyOnClose width={680}>
prev.fee_type !== cur.fee_type}> {({ getFieldValue }) => getFieldValue('fee_type') === 'fixed' ? ( ) : ( ) }
setMemberOpen(false)} destroyOnClose >
) } function featuresToList(features?: string) { return (features || '') .split(/[,\s]+/) .map((item) => item.trim()) .filter(Boolean) } function featureListToText(features?: string[]) { return (features || []).join(',') } function featureText(feature: string) { return featureOptions.find((item) => item.value === feature)?.label || feature } function yuanToCents(value?: number | null) { return Math.round(Number(value || 0) * 100) } function centsToYuan(value?: number | null) { return Number(((value || 0) / 100).toFixed(2)) }