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
+44 -55
View File
@@ -1,98 +1,87 @@
import { useMemo, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { Plus, Search, Trash2 } from 'lucide-react';
import { Breadcrumb, Button, Input, Modal, Select, Table, Tag, type TableColumn } from '@/components/ui';
import { adminApi, type DictionaryItem } from '@/api/adminApi';
type SensitiveLevel = 'low' | 'medium' | 'high';
type SensitiveWordItem = {
id: string;
word: string;
category: string;
level: SensitiveLevel;
createdAt: string;
updatedAt: string;
type SensitiveWordItem = DictionaryItem & {
word?: string;
level?: string;
};
const levelLabelMap: Record<SensitiveLevel, string> = {
const levelLabelMap: Record<string, string> = {
low: '低',
medium: '中',
high: '高',
block: '拦截',
};
const levelToneMap: Record<SensitiveLevel, 'success' | 'warning' | 'danger'> = {
const levelToneMap: Record<string, 'success' | 'warning' | 'danger'> = {
low: 'success',
medium: 'warning',
high: 'danger',
block: 'danger',
};
const initialItems: SensitiveWordItem[] = [
{ id: 'SW20260630001', word: '高息贷款', category: '金融营销', level: 'high', createdAt: '2026-06-20 09:12:18', updatedAt: '2026-06-28 16:24:10' },
{ id: 'SW20260630002', word: '中奖链接', category: '欺诈风险', level: 'high', createdAt: '2026-06-19 13:40:22', updatedAt: '2026-06-26 11:09:45' },
{ id: 'SW20260630003', word: '限时返利', category: '营销规范', level: 'medium', createdAt: '2026-06-18 10:30:00', updatedAt: '2026-06-24 15:18:32' },
{ id: 'SW20260630004', word: '免费领取', category: '普通营销', level: 'low', createdAt: '2026-06-17 17:06:51', updatedAt: '2026-06-21 09:05:14' },
];
const levelOptions = [
{ label: '低', value: 'low' },
{ label: '中', value: 'medium' },
{ label: '高', value: 'high' },
{ label: '拦截', value: 'block' },
];
function nowText() {
return new Date().toLocaleString('zh-CN', { hour12: false }).replace(/\//g, '-');
}
export function AdminSensitiveWordsPage() {
const [items, setItems] = useState(initialItems);
const [items, setItems] = useState<SensitiveWordItem[]>([]);
const [keyword, setKeyword] = useState('');
const [word, setWord] = useState('');
const [category, setCategory] = useState('');
const [level, setLevel] = useState<SensitiveLevel>('medium');
const [level, setLevel] = useState('medium');
const [modalOpen, setModalOpen] = useState(false);
const [error, setError] = useState('');
function loadData() {
adminApi.listSensitiveWords({ keyword })
.then((data) => {
setItems(data as SensitiveWordItem[]);
setError('');
})
.catch((failure: Error) => setError(failure.message || '敏感词加载失败'));
}
useEffect(() => {
loadData();
}, []);
const filteredItems = useMemo(() => items.filter((item) => {
const text = [item.word, item.category, levelLabelMap[item.level]].join(' ');
const text = [item.word, item.level, item.status].join(' ');
return !keyword || text.includes(keyword);
}), [items, keyword]);
const columns = useMemo<Array<TableColumn<SensitiveWordItem>>>(() => [
{ key: 'word', title: '敏感词', width: '180px', render: (record) => <strong>{record.word}</strong> },
{ key: 'category', title: '分类', width: '160px', render: (record) => record.category },
{ key: 'level', title: '级别', width: '120px', render: (record) => <Tag tone={levelToneMap[record.level]}>{levelLabelMap[record.level]}</Tag> },
{ key: 'createdAt', title: '创建时间', width: '190px', render: (record) => record.createdAt },
{ key: 'updatedAt', title: '更新时间', width: '190px', render: (record) => record.updatedAt },
{ key: 'level', title: '级别', width: '120px', render: (record) => <Tag tone={levelToneMap[record.level ?? 'medium'] ?? 'warning'}>{levelLabelMap[record.level ?? 'medium'] ?? record.level}</Tag> },
{ key: 'status', title: '状态', width: '120px', render: (record) => record.status ?? '-' },
{ key: 'createdAt', title: '创建时间', width: '190px', render: (record) => record.createdAt ?? '-' },
{
key: 'actions',
title: '操作',
width: '110px',
align: 'right',
render: (record) => (
<Button icon={<Trash2 size={15} />} onClick={() => setItems((current) => current.filter((item) => item.id !== record.id))} size="sm" variant="danger">
<Button icon={<Trash2 size={15} />} onClick={() => adminApi.deleteSensitiveWord(record.id).then(loadData).catch((failure: Error) => setError(failure.message))} size="sm" variant="danger">
</Button>
),
},
], []);
function resetForm() {
setWord('');
setCategory('');
setLevel('medium');
}
function addItem() {
const time = nowText();
const nextItem: SensitiveWordItem = {
id: `SW${Date.now()}`,
word: word || '待补充敏感词',
category: category || '未分类',
level,
createdAt: time,
updatedAt: time,
};
setItems((current) => [nextItem, ...current]);
resetForm();
setModalOpen(false);
adminApi.createSensitiveWord({ word, level, status: 'active' })
.then(() => {
setWord('');
setLevel('medium');
setModalOpen(false);
loadData();
})
.catch((failure: Error) => setError(failure.message || '添加敏感词失败'));
}
return (
@@ -104,17 +93,18 @@ export function AdminSensitiveWordsPage() {
</div>
<Button icon={<Plus size={16} />} onClick={() => setModalOpen(true)}></Button>
</div>
{error ? <p className="form-error">{error}</p> : null}
<div className="surface admin-security-filter">
<Input
label="搜索"
onChange={(event) => setKeyword(event.target.value)}
placeholder="搜索敏感词、分类或级别"
placeholder="搜索敏感词、级别或状态"
prefix={<Search size={16} />}
value={keyword}
/>
<div className="admin-security-filter__actions">
<Button icon={<Search size={16} />}></Button>
<Button icon={<Search size={16} />} onClick={loadData}></Button>
<Button onClick={() => setKeyword('')} variant="ghost"></Button>
</div>
</div>
@@ -127,7 +117,7 @@ export function AdminSensitiveWordsPage() {
footer={(
<>
<Button onClick={() => setModalOpen(false)} variant="ghost"></Button>
<Button onClick={addItem}></Button>
<Button disabled={!word} onClick={addItem}></Button>
</>
)}
onClose={() => setModalOpen(false)}
@@ -136,10 +126,9 @@ export function AdminSensitiveWordsPage() {
>
<div className="admin-security-form">
<Input label="敏感词" onChange={(event) => setWord(event.target.value)} placeholder="请输入敏感词" value={word} />
<Input label="分类" onChange={(event) => setCategory(event.target.value)} placeholder="请输入分类" value={category} />
<Select
label="风险级别"
onChange={(event) => setLevel(event.target.value as SensitiveLevel)}
onChange={(event) => setLevel(event.target.value)}
options={levelOptions}
value={level}
/>