import { useMemo, useState } from 'react' import { useQuery } from '@tanstack/react-query' import { Alert, App, Button, Col, Form, Input, InputNumber, Modal, Row, Select, Space, Switch, Tag, Typography, type TableColumnsType, } from 'antd' import { EditOutlined, ExportOutlined, EyeOutlined, ReloadOutlined, ShopOutlined, } from '@ant-design/icons' import { fetchAdminDistributionSites, updateAdminDistributionSite } from '@/features/admin/api' import { AdminPageHeader } from '@/features/admin/components/AdminPageHeader' import { DataTable } from '@/features/admin/components/DataTable' import { DetailDrawer, type DetailSection } from '@/features/admin/components/DetailDrawer' import { FilterBar, FilterField } from '@/features/admin/components/FilterBar' import { StatusTag } from '@/features/admin/components/StatusTag' import { announcementModeMap, distributionMemberTypeMap, distributionSiteStatusMap, distributionSiteStatusOptions, readStatusMeta, } from '@/features/admin/labels' import type { AdminDistributionSite, AdminDistributionSiteUpdatePayload, } from '@/features/admin/types' import { useAdminSessionStore } from '@/stores/adminSession' import { formatDateTime, formatText } from '@/shared/utils/format' interface SiteFormValues { site_name: string domain: string status: 'active' | 'disabled' display_account_enabled: boolean listing_qrcode_url?: string receive_ratio_delta: number receive_cost_delta: number tax_enabled: boolean tax_rate_bps: number settlement_hold_days: number } function formatBps(bps?: number | null) { if (bps == null) return '—' return `${(bps / 100).toFixed(2)}%` } function hasJson(value?: string) { const text = value?.trim() return Boolean(text && text !== '{}' && text !== '[]' && text !== 'null') } function siteUrl(domain?: string) { const text = domain?.trim() if (!text) return '' return /^https?:\/\//.test(text) ? text : `https://${text}` } function adminName(site: AdminDistributionSite) { if (site.admin_nickname || site.admin_username) { return site.admin_nickname || site.admin_username || '' } return site.admin_user_id != null ? `管理员 #${site.admin_user_id}` : '未绑定站长' } function buildSections(site: AdminDistributionSite): DetailSection[] { const brandFields = [ { key: 'logo', label: 'Logo', value: site.logo_url }, { key: 'favicon', label: 'Favicon', value: site.favicon_url }, { key: 'qrcode', label: '发布二维码', value: site.listing_qrcode_url }, ].filter(field => field.value) return [ { key: 'overview', title: '站点概览', icon: , fields: [ { key: 'name', label: '站点名称', value: site.site_name }, { key: 'code', label: '站点标识', value: {site.site_code} }, { key: 'domain', label: '专属域名', value: site.domain ? ( {site.domain} ) : ( '—' ), }, { key: 'status', label: '站点状态', value: , }, { key: 'display_account', label: '展示账号', value: site.display_account_enabled ? '开启' : '关闭', }, { key: 'announcement_mode', label: '公告模式', value: announcementModeMap[site.announcement_mode] ?? formatText(site.announcement_mode), }, { key: 'created', label: '创建时间', value: formatDateTime(site.created_at) }, { key: 'updated', label: '更新时间', value: formatDateTime(site.updated_at) }, ], }, { key: 'manager', title: '站长与成员', fields: [ { key: 'admin', label: '绑定站长', value: (
{adminName(site)} {site.admin_user_id != null ? `ID ${site.admin_user_id}` : '—'}
), }, { key: 'member_type', label: '成员角色', value: site.member_type ? distributionMemberTypeMap[site.member_type] ?? site.member_type : '平台全量可见', }, { key: 'support_group', label: '默认客服群', value: site.default_support_group_id != null ? `群 #${site.default_support_group_id}` : '未绑定', }, { key: 'counts', label: '用户 / 下单用户', value: `${site.user_count} / ${site.order_user_count}`, }, ], }, { key: 'settlement', title: '分成与结算', fields: [ { key: 'ratio_delta', label: '收号比例系数', value: {site.receive_ratio_delta} 个点, }, { key: 'cost_delta', label: '收号成本系数', value: {site.receive_cost_delta} 个点, }, { key: 'hold_days', label: '结算冷却', value: `${site.settlement_hold_days} 天`, }, { key: 'tax', label: '税费', value: site.tax_enabled ? formatBps(site.tax_rate_bps) : '未启用', }, ], }, { key: 'assets', title: '品牌与页面配置', fields: [ ...(brandFields.length > 0 ? brandFields.map(field => ({ key: field.key, label: field.label, value: ( {field.value} ), })) : [ { key: 'brand_empty', label: '品牌素材', value: '沿用主站品牌素材', }, ]), { key: 'configs', label: '页面配置', span: 2, value: ( 轮播 公告 页脚 回收入口 合作反馈 卖场文案 ), }, ], }, ] } function buildColumns( onOpen: (site: AdminDistributionSite) => void, onEdit: (site: AdminDistributionSite) => void, canManage: boolean ): TableColumnsType { return [ { title: '站点', dataIndex: 'site_name', width: 280, fixed: 'left', render: (_, row) => (
{row.site_code} {row.domain || '未绑定专属域名'}
), }, { title: '站长', key: 'admin', width: 190, render: (_, row) => (
{adminName(row)} {row.member_type ? distributionMemberTypeMap[row.member_type] ?? row.member_type : '平台账号'} {row.admin_user_id != null ? `ID ${row.admin_user_id}` : '—'}
), }, { title: '状态', dataIndex: 'status', width: 100, render: value => , }, { title: '用户', key: 'users', width: 130, align: 'right', render: (_, row) => (
{row.user_count} 下单 {row.order_user_count}
), }, { title: '分成 / 结算', key: 'settlement', width: 190, render: (_, row) => (
比例 {row.receive_ratio_delta} 点 成本 {row.receive_cost_delta} 点 冷却 {row.settlement_hold_days} 天 ·{' '} {row.tax_enabled ? formatBps(row.tax_rate_bps) : '无税'}
), }, { title: '创建时间', dataIndex: 'created_at', width: 160, render: value => formatDateTime(value), }, { title: '操作', key: 'actions', width: 150, fixed: 'right', render: (_, row) => ( {canManage ? ( ) : null} ), }, ] } export function AdminDistributionSitesPage() { const { message, modal } = App.useApp() const [form] = Form.useForm() const [keyword, setKeyword] = useState('') const [status, setStatus] = useState('') const [selected, setSelected] = useState(null) const [detailOpen, setDetailOpen] = useState(false) const [editing, setEditing] = useState(null) const [saving, setSaving] = useState(false) const permissions = useAdminSessionStore(state => state.admin?.permissions) const canView = Boolean( permissions?.includes('*') || permissions?.includes('distribution:site_view') || permissions?.includes('qrcode:view') ) const canManage = Boolean( permissions?.includes('*') || permissions?.includes('distribution:site_manage') ) const { data, isPending, isFetching, isError, error, refetch } = useQuery({ queryKey: ['admin-distribution-sites'], queryFn: fetchAdminDistributionSites, enabled: canView, }) const sites = data ?? [] const filtered = useMemo(() => { const text = keyword.trim().toLowerCase() return sites.filter(site => { if (status && site.status !== status) return false if (!text) return true return [site.site_name, site.site_code, site.domain, site.admin_username, site.admin_nickname] .filter(Boolean) .some(value => String(value).toLowerCase().includes(text)) }) }, [sites, keyword, status]) const limited = Boolean(editing?.member_type) const errorText = error instanceof Error ? error.message : '请稍后重试或重新登录后台。' const distributionAdminUrl = import.meta.env.VITE_DISTRIBUTION_ADMIN_URL const hasActiveFilters = Boolean(keyword || status) const openDetail = (site: AdminDistributionSite) => { setSelected(site) setDetailOpen(true) } const openEditor = (site: AdminDistributionSite) => { setEditing(site) form.setFieldsValue({ site_name: site.site_name, domain: site.domain, status: site.status === 'disabled' ? 'disabled' : 'active', display_account_enabled: site.display_account_enabled, listing_qrcode_url: site.listing_qrcode_url, receive_ratio_delta: site.receive_ratio_delta, receive_cost_delta: site.receive_cost_delta, tax_enabled: site.tax_enabled, tax_rate_bps: site.tax_rate_bps, settlement_hold_days: site.settlement_hold_days, }) } const closeEditor = () => { if (saving) return setEditing(null) } const applyUpdated = (updated: AdminDistributionSite) => { setSelected(current => (current?.id === updated.id ? updated : current)) } const saveSite = async () => { if (!editing) return let values: SiteFormValues try { values = await form.validateFields() } catch { return } const payload: AdminDistributionSiteUpdatePayload = limited ? { receive_ratio_delta: Number(values.receive_ratio_delta), receive_cost_delta: Number(values.receive_cost_delta), } : { site_name: values.site_name?.trim(), domain: values.domain?.trim(), status: values.status, display_account_enabled: values.display_account_enabled, listing_qrcode_url: values.listing_qrcode_url?.trim(), receive_ratio_delta: Number(values.receive_ratio_delta), receive_cost_delta: Number(values.receive_cost_delta), tax_enabled: values.tax_enabled, tax_rate_bps: values.tax_enabled ? Number(values.tax_rate_bps) : 0, settlement_hold_days: Number(values.settlement_hold_days), } modal.confirm({ title: `确认更新「${editing.site_name}」?`, content: limited ? '站长账号只能调整本站分成系数,其他站点资料不会被修改。' : '站点名称、域名、分成系数与结算冷却会立即影响前台展示和后续订单结算,请确认已核对。', okText: '确认更新', cancelText: '再检查一下', onOk: async () => { setSaving(true) try { const updated = await updateAdminDistributionSite(editing.id, payload) message.success('分销站点已更新') applyUpdated(updated) setEditing(null) await refetch() } catch (err) { message.error(err instanceof Error ? err.message : '更新失败,请稍后重试') } finally { setSaving(false) } }, }) } const toggleStatus = (site: AdminDistributionSite) => { const next = site.status === 'active' ? 'disabled' : 'active' const actionText = next === 'active' ? '启用' : '停用' modal.confirm({ title: `确认${actionText}「${site.site_name}」?`, content: next === 'active' ? '启用后站点域名与前台页面会恢复对外服务。' : '停用后站点前台会停止服务,请先确认站内订单与结算已经处理完毕。', okText: `确认${actionText}`, okButtonProps: { danger: next === 'disabled' }, cancelText: '取消', onOk: async () => { try { const updated = await updateAdminDistributionSite(site.id, { status: next }) message.success(`站点已${actionText}`) applyUpdated(updated) await refetch() } catch (err) { message.error(err instanceof Error ? err.message : `${actionText}失败,请稍后重试`) } }, }) } const columns = useMemo( () => buildColumns(openDetail, openEditor, canManage), [canManage] ) const detailFooter = selected ? ( {canManage ? ( ) : null} {canManage && !selected.member_type ? ( ) : null} ) : null return (
共 {sites.length} 个站点 运营中 {sites.filter(item => item.status === 'active').length} {isFetching && !isPending ? 正在更新 : null} } extra={ {distributionAdminUrl ? ( ) : null} } /> {!canView ? ( ) : null} { setKeyword('') setStatus('') } : undefined } loading={isFetching} extra={ {filtered.length === sites.length ? '显示全部站点' : `筛选出 ${filtered.length} / ${sites.length} 个站点`} } > setKeyword(event.target.value)} /> )} ) : null}
) }