fix: connect remaining sms pages to real backend

This commit is contained in:
hectorzhao
2026-07-02 19:30:04 +08:00
parent 06562e42dd
commit 131f344ac4
50 changed files with 2074 additions and 6400 deletions
+71 -114
View File
@@ -1,130 +1,61 @@
import { useMemo, useState } from 'react';
import { Plus, Search, Trash2 } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react';
import { Plus, Search } from 'lucide-react';
import { Breadcrumb, Button, Input, Modal, Select, Table, type TableColumn } from '@/components/ui';
import { adminApi, type DictionaryItem } from '@/api/adminApi';
type PhoneSegment = {
id: string;
segment: string;
carrier: string;
province: string;
city: string;
createdAt: string;
updatedAt: string;
type PhoneSegment = DictionaryItem & {
prefix?: string;
carrier?: string;
province?: string | null;
city?: string | null;
};
const initialSegments: PhoneSegment[] = [
{ id: 'SEG20260630001', segment: '1367556', carrier: '中国移动', province: '四川省', city: '成都市', createdAt: '2026-06-11 09:30:12', updatedAt: '2026-06-28 15:40:00' },
{ id: 'SEG20260630002', segment: '1860763', carrier: '中国联通', province: '重庆市', city: '重庆市', createdAt: '2026-06-12 10:18:44', updatedAt: '2026-06-27 11:22:13' },
{ id: 'SEG20260630003', segment: '1525066', carrier: '中国电信', province: '江苏省', city: '南京市', createdAt: '2026-06-15 14:22:31', updatedAt: '2026-06-26 17:05:39' },
{ id: 'SEG20260630004', segment: '1501234', carrier: '中国移动', province: '广东省', city: '深圳市', createdAt: '2026-06-18 16:10:25', updatedAt: '2026-06-24 09:15:26' },
];
function createSegmentId() {
return `SEG${Date.now()}`;
}
type SegmentFormModalProps = {
item?: PhoneSegment;
onClose: () => void;
onSubmit: (item: PhoneSegment) => void;
};
function SegmentFormModal({ item, onClose, onSubmit }: SegmentFormModalProps) {
const [form, setForm] = useState<PhoneSegment>(() => item ?? {
id: createSegmentId(),
segment: '',
carrier: '中国移动',
province: '',
city: '',
createdAt: '2026-06-30 10:00:00',
updatedAt: '2026-06-30 10:00:00',
});
function updateField<Key extends keyof PhoneSegment>(key: Key, value: PhoneSegment[Key]) {
setForm((current) => ({ ...current, [key]: value }));
}
return (
<Modal
footer={(
<>
<Button onClick={onClose} variant="ghost"></Button>
<Button onClick={() => onSubmit({ ...form, updatedAt: '2026-06-30 10:00:00' })}></Button>
</>
)}
onClose={onClose}
open
title={item ? '编辑手机号段' : '新增手机号段'}
>
<div className="admin-system-modal-form">
<Input label="手机号段" maxLength={7} onChange={(event) => updateField('segment', event.target.value)} placeholder="手机号码前7位" value={form.segment} />
<Select
label="运营商"
onChange={(event) => updateField('carrier', event.target.value)}
options={[
{ label: '中国移动', value: '中国移动' },
{ label: '中国联通', value: '中国联通' },
{ label: '中国电信', value: '中国电信' },
]}
value={form.carrier}
/>
<Input label="省份" onChange={(event) => updateField('province', event.target.value)} value={form.province} />
<Input label="城市" onChange={(event) => updateField('city', event.target.value)} value={form.city} />
</div>
</Modal>
);
}
export function AdminPhoneSegmentsPage() {
const [segments, setSegments] = useState(initialSegments);
const [segments, setSegments] = useState<PhoneSegment[]>([]);
const [keyword, setKeyword] = useState('');
const [editingSegment, setEditingSegment] = useState<PhoneSegment | null>(null);
const [creating, setCreating] = useState(false);
const [prefix, setPrefix] = useState('');
const [carrier, setCarrier] = useState('中国移动');
const [province, setProvince] = useState('');
const [city, setCity] = useState('');
const [error, setError] = useState('');
function loadData() {
adminApi.listPhoneSegments()
.then((items) => {
setSegments(items as PhoneSegment[]);
setError('');
})
.catch((failure: Error) => setError(failure.message || '手机号段加载失败'));
}
useEffect(() => {
loadData();
}, []);
const filteredSegments = useMemo(
() => segments.filter((segment) => [segment.segment, segment.carrier, segment.province, segment.city].some((value) => value.includes(keyword))),
() => segments.filter((segment) => [segment.prefix, segment.carrier, segment.province, segment.city].some((value) => String(value ?? '').includes(keyword))),
[keyword, segments],
);
function upsertSegment(nextSegment: PhoneSegment) {
setSegments((current) => {
const exists = current.some((item) => item.id === nextSegment.id);
if (exists) {
return current.map((item) => (item.id === nextSegment.id ? nextSegment : item));
}
return [nextSegment, ...current];
});
setEditingSegment(null);
setCreating(false);
function createSegment() {
adminApi.createPhoneSegment({ prefix, carrier, province, city })
.then(() => {
setPrefix('');
setProvince('');
setCity('');
setCreating(false);
loadData();
})
.catch((failure: Error) => setError(failure.message || '手机号段新增失败'));
}
const columns = useMemo<Array<TableColumn<PhoneSegment>>>(() => [
{ key: 'segment', title: '手机号段(手机号码前7位)', width: '230px', render: (record) => <strong>{record.segment}</strong> },
{ key: 'carrier', title: '运营商', width: '150px', render: (record) => record.carrier },
{ key: 'province', title: '省份', width: '140px', render: (record) => record.province },
{ key: 'city', title: '城市', width: '140px', render: (record) => record.city },
{ key: 'createdAt', title: '创建时间', width: '190px', render: (record) => record.createdAt },
{ key: 'updatedAt', title: '更新时间', width: '190px', render: (record) => record.updatedAt },
{
key: 'actions',
title: '操作',
width: '150px',
align: 'right',
render: (record) => (
<div className="admin-system-actions">
<Button onClick={() => setEditingSegment(record)} size="sm" variant="ghost"></Button>
<Button
icon={<Trash2 size={15} />}
onClick={() => setSegments((current) => current.filter((item) => item.id !== record.id))}
size="sm"
variant="danger"
>
</Button>
</div>
),
},
{ key: 'segment', title: '手机号段(手机号码前7位)', width: '230px', render: (record) => <strong>{record.prefix}</strong> },
{ key: 'carrier', title: '运营商', width: '150px', render: (record) => record.carrier ?? '-' },
{ key: 'province', title: '省份', width: '140px', render: (record) => record.province ?? '-' },
{ key: 'city', title: '城市', width: '140px', render: (record) => record.city ?? '-' },
{ key: 'createdAt', title: '创建时间', width: '190px', render: (record) => record.createdAt ?? '-' },
], []);
return (
@@ -135,6 +66,7 @@ export function AdminPhoneSegmentsPage() {
<h1></h1>
</div>
</div>
{error ? <p className="form-error">{error}</p> : null}
<div className="surface admin-system-toolbar">
<Input onChange={(event) => setKeyword(event.target.value)} placeholder="搜索手机号段、运营商、省份或城市" prefix={<Search size={16} />} value={keyword} />
@@ -145,8 +77,33 @@ export function AdminPhoneSegmentsPage() {
<Table columns={columns} data={filteredSegments} emptyText="暂无手机号段" rowKey="id" />
</div>
{creating ? <SegmentFormModal onClose={() => setCreating(false)} onSubmit={upsertSegment} /> : null}
{editingSegment ? <SegmentFormModal item={editingSegment} onClose={() => setEditingSegment(null)} onSubmit={upsertSegment} /> : null}
<Modal
footer={(
<>
<Button onClick={() => setCreating(false)} variant="ghost"></Button>
<Button disabled={!prefix} onClick={createSegment}></Button>
</>
)}
onClose={() => setCreating(false)}
open={creating}
title="新增手机号段"
>
<div className="admin-system-modal-form">
<Input label="手机号段" maxLength={7} onChange={(event) => setPrefix(event.target.value)} placeholder="手机号码前7位" value={prefix} />
<Select
label="运营商"
onChange={(event) => setCarrier(event.target.value)}
options={[
{ label: '中国移动', value: '中国移动' },
{ label: '中国联通', value: '中国联通' },
{ label: '中国电信', value: '中国电信' },
]}
value={carrier}
/>
<Input label="省份" onChange={(event) => setProvince(event.target.value)} value={province} />
<Input label="城市" onChange={(event) => setCity(event.target.value)} value={city} />
</div>
</Modal>
</section>
);
}