import { useEffect, useMemo, useState, type ReactNode } from 'react'; import { Database, ListFilter, Plus, RotateCcw, Search, Trash2 } from 'lucide-react'; import { Breadcrumb, Button, Input, Modal, Pagination, Select, Table, Tabs, Tag, type TableColumn } from '@/components/ui'; import { adminApi, type DictionaryItem } from '@/api/adminApi'; import { formatDateTime } from '@/utils/dateTime'; type PhoneSegment = DictionaryItem & { prefix?: string; carrier?: string; province?: string | null; city?: string | null; }; type CarrierRule = DictionaryItem & { carrier?: string; pattern?: string; priority?: number; remark?: string | null; }; const carrierTone: Record = { 中国移动: 'success', 中国联通: 'info', 中国电信: 'warning', }; const ruleCarrierMeta: Record = { mobile: { label: '中国移动', tone: 'success' }, unicom: { label: '中国联通', tone: 'info' }, telecom: { label: '中国电信', tone: 'warning' }, }; type PhoneSegmentSummaryProps = { icon: ReactNode; label: string; description: string; total: number; }; function PhoneSegmentSummary({ icon, label, description, total }: PhoneSegmentSummaryProps) { return (
{icon}
{label} {total.toLocaleString('zh-CN')}

{description}

); } export function AdminPhoneSegmentsPage() { const pageSize = 25; const [segments, setSegments] = useState([]); const [segmentTotal, setSegmentTotal] = useState(0); const [rules, setRules] = useState([]); const [ruleTotal, setRuleTotal] = useState(0); const [activeTab, setActiveTab] = useState<'segments' | 'rules'>('segments'); const [keyword, setKeyword] = useState(''); const [creating, setCreating] = useState(false); const [creatingRule, setCreatingRule] = useState(false); const [prefix, setPrefix] = useState(''); const [carrier, setCarrier] = useState('中国移动'); const [province, setProvince] = useState(''); const [city, setCity] = useState(''); const [ruleCarrier, setRuleCarrier] = useState('mobile'); const [rulePattern, setRulePattern] = useState(''); const [rulePriority, setRulePriority] = useState('100'); const [ruleRemark, setRuleRemark] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); const [segmentQuery, setSegmentQuery] = useState(''); const [page, setPage] = useState(1); const [rulePage, setRulePage] = useState(1); const [reloadKey, setReloadKey] = useState(0); const [deleteTarget, setDeleteTarget] = useState(null); const [ruleDeleteTarget, setRuleDeleteTarget] = useState(null); useEffect(() => { let cancelled = false; setLoading(true); Promise.all([ adminApi.listPhoneSegments({ keyword: segmentQuery || undefined, page, pageSize }), adminApi.listPhoneCarrierRules({ keyword: segmentQuery || undefined, page: rulePage, pageSize }), ]) .then(([segmentPage, ruleResponse]) => { if (cancelled) return; setSegments(segmentPage.items as PhoneSegment[]); setSegmentTotal(segmentPage.total); setRules(ruleResponse.items as CarrierRule[]); setRuleTotal(ruleResponse.total); setError(''); }) .catch((failure: Error) => { if (!cancelled) setError(failure.message || '手机号段加载失败'); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [page, reloadKey, rulePage, segmentQuery]); const segmentTotalPages = Math.max(1, Math.ceil(segmentTotal / pageSize)); const ruleTotalPages = Math.max(1, Math.ceil(ruleTotal / pageSize)); function createSegment() { adminApi.createPhoneSegment({ prefix, carrier, province, city }) .then(() => { setPrefix(''); setProvince(''); setCity(''); setCreating(false); setPage(1); setReloadKey((current) => current + 1); }) .catch((failure: Error) => setError(failure.message || '手机号段新增失败')); } function createCarrierRule() { adminApi.createPhoneCarrierRule({ carrier: ruleCarrier, pattern: rulePattern, priority: Number(rulePriority) || 100, remark: ruleRemark }) .then(() => { setRulePattern(''); setRuleRemark(''); setCreatingRule(false); setReloadKey((current) => current + 1); }) .catch((failure: Error) => setError(failure.message || '运营商区分规则新增失败')); } function query() { setSegmentQuery(keyword.trim()); setPage(1); setRulePage(1); } function reset() { setKeyword(''); setSegmentQuery(''); setPage(1); setRulePage(1); } function deleteSegment() { if (!deleteTarget) return; adminApi.deletePhoneSegment(deleteTarget.id) .then(() => { setDeleteTarget(null); setReloadKey((current) => current + 1); }) .catch((failure: Error) => setError(failure.message || '手机号段删除失败')); } function deleteCarrierRule() { if (!ruleDeleteTarget) return; adminApi.deletePhoneCarrierRule(ruleDeleteTarget.id) .then(() => { setRuleDeleteTarget(null); setReloadKey((current) => current + 1); }) .catch((failure: Error) => setError(failure.message || '运营商区分规则删除失败')); } const columns = useMemo>>(() => [ { key: 'segment', title: '手机号段(前7位)', width: '190px', render: (record) => {record.prefix} }, { key: 'carrier', title: '运营商', width: '130px', render: (record) => record.carrier ? {record.carrier} : '-' }, { key: 'province', title: '省份', width: '110px', render: (record) => record.province ?? '-' }, { key: 'city', title: '城市', width: '110px', render: (record) => record.city ?? '-' }, { key: 'createdAt', title: '创建时间', width: '170px', render: (record) => formatDateTime(record.createdAt) }, { key: 'actions', title: '操作', width: '90px', align: 'right', render: (record) => }, ], []); const ruleColumns = useMemo>>(() => [ { key: 'carrier', title: '运营商', width: '140px', render: (record) => { const meta = ruleCarrierMeta[record.carrier ?? '']; return meta ? {meta.label} : record.carrier ?? '-'; } }, { key: 'pattern', title: '号码前缀正则', render: (record) => {record.pattern} }, { key: 'priority', title: '优先级', width: '120px', render: (record) => record.priority ?? 100 }, { key: 'remark', title: '备注', render: (record) => record.remark ?? '-' }, { key: 'actions', title: '操作', width: '90px', align: 'right', render: (record) => }, ], []); const queryPanel = (
setKeyword(event.target.value)} onKeyDown={(event) => { if (event.key === 'Enter') query(); }} placeholder={activeTab === 'segments' ? '搜索手机号段、运营商、省份或城市' : '搜索运营商、正则或备注'} prefix={} value={keyword} />
); return (

维护号码前七位归属,用于运营商识别与路由判断

{error ?

{error}

: null}
{ setActiveTab(value as 'segments' | 'rules'); }} value={activeTab} items={[ { label: '手机号段', value: 'segments', content: (
} label="已收录手机号段" total={segmentTotal} /> {queryPanel} = segmentTotalPages || loading} onPrevious={() => setPage((current) => Math.max(1, current - 1))} onNext={() => setPage((current) => Math.min(segmentTotalPages, current + 1))} onPageChange={setPage} total={segmentTotal} totalPages={segmentTotalPages} /> ), }, { label: '运营商区分规则', value: 'rules', content: (
} label="运营商识别规则" total={ruleTotal} /> {queryPanel}
= ruleTotalPages || loading} onPrevious={() => setRulePage((current) => Math.max(1, current - 1))} onNext={() => setRulePage((current) => Math.min(ruleTotalPages, current + 1))} onPageChange={setRulePage} /> ), }, ]} /> )} onClose={() => setCreating(false)} open={creating} title="新增手机号段" >
setPrefix(event.target.value)} placeholder="手机号码前7位" value={prefix} /> setProvince(event.target.value)} value={province} /> setCity(event.target.value)} value={city} />
)} onClose={() => setCreatingRule(false)} open={creatingRule} title="新增运营商区分规则" >
setRulePattern(event.target.value)} placeholder="例如 ^13[4-9]" value={rulePattern} /> setRulePriority(event.target.value)} value={rulePriority} /> setRuleRemark(event.target.value)} value={ruleRemark} />
{deleteTarget ? ( } onClose={() => setDeleteTarget(null)} open title="删除手机号段" >

确认删除手机号段“{deleteTarget.prefix}”吗?删除后号码归属识别将不再使用该记录。

) : null} {ruleDeleteTarget ? ( } onClose={() => setRuleDeleteTarget(null)} open title="删除运营商区分规则" >

确认删除规则“{ruleDeleteTarget.pattern}”吗?删除后运营商识别将不再使用该规则。

) : null} ); }