import { useCallback, useEffect, useState } from 'react'; import { Button, Cascader, Drawer, Form, Input, InputNumber, Modal, Select, Space, Table, Tabs, Tag, Typography, message, } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { CITY_PARTNER_SCOPE_LABELS, CityPartnerScopeType, PARTNER_PERMISSION_KEYS, PARTNER_PERMISSION_LABELS, type PartnerPermissionKey, } from '@dukang/shared-types'; import { request, type Paginated } from '../lib/api'; import { CHINA_REGION_OPTIONS } from '../lib/china-region'; import { ADMIN_OPTIONS_PAGE_SIZE, fmtTime } from '../lib/constants'; import { useAdminList } from '../lib/useAdminList'; type Row = { id: string; companyName: string; phone: string; name: string; contactPhone?: string | null; cityId?: string | null; cityName?: string | null; scopeType?: string; orderCommissionRate?: number; redeemCommissionRate?: number; storeCount: number; accountCount: number; createdAt: string; }; type PartnerDetail = Row & { address?: string; districtCodes?: string[] | null; bindingStatus?: string; bankAccountName?: string | null; bankAccountNo?: string | null; bankBranch?: string | null; managedWarehouseId?: string | null; children?: Array<{ id: string; phone: string; name: string; staffRole?: string; permissions?: string[]; status: string; }>; }; type CityOption = { id: string; name: string; code: string }; type WarehouseOption = { id: string; name: string }; const SCOPE_OPTIONS = Object.entries(CITY_PARTNER_SCOPE_LABELS).map(([value, label]) => ({ value, label })); const PERM_OPTIONS = PARTNER_PERMISSION_KEYS.map((k: PartnerPermissionKey) => ({ value: k, label: PARTNER_PERMISSION_LABELS[k] })); function flattenDistrictCodes(values: string[] | string[][] | undefined): string[] { if (!values?.length) return []; if (Array.isArray(values[0])) { return (values as string[][]).map((path) => path[path.length - 1]).filter(Boolean); } return values as string[]; } export default function PartnersPage() { const [form] = Form.useForm(); const [editForm] = Form.useForm(); const [createForm] = Form.useForm(); const [subForm] = Form.useForm(); const [filters, setFilters] = useState>({}); const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList( '/admin/partners', () => { const qs = new URLSearchParams(); if (filters.companyName) qs.set('companyName', filters.companyName); if (filters.contactPhone) qs.set('contactPhone', filters.contactPhone); return qs; }, [filters], ); const [detail, setDetail] = useState(null); const [drawerOpen, setDrawerOpen] = useState(false); const [createOpen, setCreateOpen] = useState(false); const [subOpen, setSubOpen] = useState(false); const [cities, setCities] = useState([]); const [warehouses, setWarehouses] = useState([]); const [createScopeType, setCreateScopeType] = useState(CityPartnerScopeType.CITY_WIDE); const [editScopeType, setEditScopeType] = useState(CityPartnerScopeType.CITY_WIDE); const [createCityId, setCreateCityId] = useState(); const loadCities = useCallback(async () => { const res = await request>(`/admin/cities?pageSize=${ADMIN_OPTIONS_PAGE_SIZE}`); setCities(res.items); }, []); const loadWarehouses = useCallback(async (cityId: string) => { const rows = await request(`/admin/cities/${cityId}/warehouses`); setWarehouses(rows.map((w) => ({ id: w.id, name: (w as { name: string }).name }))); }, []); useEffect(() => { void loadCities(); }, [loadCities]); async function openPartner(id: string) { const d = await request(`/admin/partners/${id}`); setDetail(d); setEditScopeType((d.scopeType as CityPartnerScopeType) || CityPartnerScopeType.CITY_WIDE); if (d.cityId) await loadWarehouses(d.cityId); editForm.setFieldsValue({ name: d.name, phone: d.phone, companyName: d.companyName, contactPhone: d.contactPhone ?? d.phone, address: d.address ?? '', scopeType: d.scopeType, districtCodes: d.districtCodes ?? [], orderCommissionRate: (d.orderCommissionRate ?? 0) * 100, redeemCommissionRate: (d.redeemCommissionRate ?? 0.03) * 100, bindingStatus: d.bindingStatus, managedWarehouseId: d.managedWarehouseId, bankAccountName: d.bankAccountName ?? '', bankAccountNo: d.bankAccountNo ?? '', bankBranch: d.bankBranch ?? '', }); setDrawerOpen(true); } async function savePartner() { if (!detail) return; const v = await editForm.validateFields(); const body = { ...v, orderCommissionRate: Number(v.orderCommissionRate ?? 0) / 100, redeemCommissionRate: Number(v.redeemCommissionRate ?? 3) / 100, districtCodes: editScopeType === CityPartnerScopeType.DISTRICT ? flattenDistrictCodes(v.districtCodes) : undefined, }; await request(`/admin/partners/${detail.id}`, { method: 'PUT', body: JSON.stringify(body) }); message.success('已保存'); setDrawerOpen(false); void reload(); } const columns: ColumnsType = [ { title: '公司名', dataIndex: 'companyName', ellipsis: true }, { title: '城市', dataIndex: 'cityName', width: 100 }, { title: '主账号', dataIndex: 'phone', width: 130 }, { title: '管辖', dataIndex: 'scopeType', width: 100, render: (v) => (v ? CITY_PARTNER_SCOPE_LABELS[v as keyof typeof CITY_PARTNER_SCOPE_LABELS] || v : '—'), }, { title: '门店', dataIndex: 'storeCount', width: 70 }, { title: '子账号', dataIndex: 'accountCount', width: 80, render: (n) => Math.max(0, n - 1) }, { title: '创建', dataIndex: 'createdAt', width: 160, render: fmtTime }, { title: '操作', width: 120, render: (_, row) => ( ), }, ]; return (
开城合伙人
{ setFilters(v); setPage(1); }}>
{ setPage(p); setPageSize(ps); }, }} /> setDrawerOpen(false)} extra={} > {detail && ( ({ value: w.id, label: w.name }))} /> ), }, { key: 'staff', label: '子账号', children: ( <>
{s} }, { title: '权限', dataIndex: 'permissions', render: (p: string[] | undefined) => p?.map((k) => PARTNER_PERMISSION_LABELS[k as keyof typeof PARTNER_PERMISSION_LABELS] || k).join('、') || '—', }, ]} /> ), }, ]} /> )} setCreateOpen(false)} onOk={async () => { const v = await createForm.validateFields(); await request('/admin/partners', { method: 'POST', body: JSON.stringify({ ...v, orderCommissionRate: Number(v.orderCommissionRate ?? 0) / 100, redeemCommissionRate: Number(v.redeemCommissionRate ?? 3) / 100, districtCodes: createScopeType === CityPartnerScopeType.DISTRICT ? flattenDistrictCodes(v.districtCodes) : undefined, }), }); message.success('已创建'); setCreateOpen(false); createForm.resetFields(); void reload(); }} >
({ value: w.id, label: w.name }))} /> )}
setSubOpen(false)} onOk={async () => { if (!detail) return; const v = await subForm.validateFields(); await request('/admin/partner-accounts', { method: 'POST', body: JSON.stringify({ ...v, parentAccountId: detail.id }), }); message.success('已创建'); setSubOpen(false); void openPartner(detail.id); }} >