Files
lislgosms/src/apps/admin/AdminEnterpriseBlacklistPage.tsx
T

161 lines
7.8 KiB
TypeScript

import { useEffect, useMemo, useState } from 'react';
import { Plus, Search, Trash2 } from 'lucide-react';
import { Breadcrumb, Button, Input, Modal, Select, Table, Tag, Textarea, type TableColumn } from '@/components/ui';
import { adminApi, type DictionaryItem, type EnterpriseApplication, type TenantOption } from '@/api/adminApi';
type EnterpriseBlacklistItem = DictionaryItem & {
tenantId?: string;
applicationId?: string;
tenant?: TenantOption;
application?: EnterpriseApplication;
phoneNumber?: string;
reason?: string | null;
};
export function AdminEnterpriseBlacklistPage() {
const [items, setItems] = useState<EnterpriseBlacklistItem[]>([]);
const [tenants, setTenants] = useState<TenantOption[]>([]);
const [applications, setApplications] = useState<EnterpriseApplication[]>([]);
const [enterpriseKeyword, setEnterpriseKeyword] = useState('');
const [applicationKeyword, setApplicationKeyword] = useState('');
const [phoneKeyword, setPhoneKeyword] = useState('');
const [reasonKeyword, setReasonKeyword] = useState('');
const [status, setStatus] = useState('all');
const [appliedFilters, setAppliedFilters] = useState({ enterpriseKeyword: '', applicationKeyword: '', phoneNumber: '', reasonKeyword: '', status: 'all' });
const [formTenantId, setFormTenantId] = useState('');
const [formApplicationId, setFormApplicationId] = useState('');
const [phone, setPhone] = useState('');
const [reason, setReason] = useState('');
const [modalOpen, setModalOpen] = useState(false);
const [error, setError] = useState('');
function loadData(filters = appliedFilters) {
Promise.all([
adminApi.listEnterpriseBlacklist(filters),
adminApi.listTenants(),
adminApi.listEnterpriseApplications(),
])
.then(([blacklist, tenantItems, applicationItems]) => {
setItems(blacklist as EnterpriseBlacklistItem[]);
setTenants(tenantItems);
setApplications(applicationItems);
setError('');
})
.catch((failure: Error) => setError(failure.message || '企业黑名单加载失败'));
}
useEffect(() => {
loadData();
}, []);
const modalApplications = applications.filter((application) => application.tenantId === formTenantId && application.status !== 'deleted');
const columns = useMemo<Array<TableColumn<EnterpriseBlacklistItem>>>(() => [
{ key: 'enterprise', title: '企业名称', width: '180px', render: (record) => <strong>{record.tenant?.name ?? record.tenantId}</strong> },
{ key: 'application', title: '应用名称', width: '180px', render: (record) => <span>{record.application?.name ?? record.applicationId}</span> },
{ key: 'phone', title: '手机号码', width: '150px', render: (record) => <strong>{record.phoneNumber}</strong> },
{ key: 'createdAt', title: '入库时间', width: '170px', render: (record) => record.createdAt ?? '-' },
{ key: 'reason', title: '入库原因', render: (record) => record.reason ?? '-' },
{ key: 'status', title: '状态', width: '130px', render: (record) => <Tag tone={record.status === 'active' ? 'success' : 'neutral'}>{record.status === 'active' ? '生效中' : record.status === 'deleted' ? '已删除' : record.status ?? '-'}</Tag> },
{
key: 'actions',
title: '操作',
width: '130px',
align: 'right',
render: (record) => (
<Button icon={<Trash2 size={15} />} onClick={() => adminApi.deleteEnterpriseBlacklist(record.id).then(() => loadData()).catch((failure: Error) => setError(failure.message))} size="sm" variant="danger">
删除
</Button>
),
},
], [appliedFilters]);
function addItem() {
adminApi.createEnterpriseBlacklist({ tenantId: formTenantId, applicationId: formApplicationId, phoneNumber: phone, reason, status: 'active' })
.then(() => {
setFormTenantId('');
setFormApplicationId('');
setPhone('');
setReason('');
setModalOpen(false);
loadData();
})
.catch((failure: Error) => setError(failure.message || '添加企业黑名单失败'));
}
return (
<section className="page-stack admin-security-page">
<div className="page-heading">
<div>
<Breadcrumb items={['安全控制', '企业黑名单']} />
<h1>企业黑名单</h1>
</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) => setEnterpriseKeyword(event.target.value)}
placeholder="请输入企业名称"
prefix={<Search size={16} />}
value={enterpriseKeyword}
/>
<Input
label="企业应用"
onChange={(event) => setApplicationKeyword(event.target.value)}
placeholder="请输入企业应用名称"
prefix={<Search size={16} />}
value={applicationKeyword}
/>
<Input label="手机号码" onChange={(event) => setPhoneKeyword(event.target.value)} placeholder="请输入手机号码" value={phoneKeyword} />
<Input label="入库原因" onChange={(event) => setReasonKeyword(event.target.value)} placeholder="请输入入库原因" value={reasonKeyword} />
<Select
label="状态"
onChange={(event) => setStatus(event.target.value)}
options={[{ label: '全部状态', value: 'all' }, { label: '生效中', value: 'active' }]}
value={status}
/>
<div className="admin-security-filter__actions">
<Button icon={<Search size={16} />} onClick={() => { const filters = { enterpriseKeyword: enterpriseKeyword.trim(), applicationKeyword: applicationKeyword.trim(), phoneNumber: phoneKeyword.trim(), reasonKeyword: reasonKeyword.trim(), status }; setAppliedFilters(filters); loadData(filters); }}>查询</Button>
<Button onClick={() => { const filters = { enterpriseKeyword: '', applicationKeyword: '', phoneNumber: '', reasonKeyword: '', status: 'all' }; setEnterpriseKeyword(''); setApplicationKeyword(''); setPhoneKeyword(''); setReasonKeyword(''); setStatus('all'); setAppliedFilters(filters); loadData(filters); }} variant="ghost">重置</Button>
</div>
</div>
<div className="surface admin-security-table-card">
<Table columns={columns} data={items} emptyText="暂无企业黑名单记录" rowKey="id" />
</div>
<Modal
footer={(
<>
<Button onClick={() => setModalOpen(false)} variant="ghost">取消</Button>
<Button disabled={!formTenantId || !formApplicationId || !phone} onClick={addItem}>确认添加</Button>
</>
)}
onClose={() => setModalOpen(false)}
open={modalOpen}
title="添加企业黑名单"
>
<div className="admin-security-form">
<Select
label="企业"
onChange={(event) => { setFormTenantId(event.target.value); setFormApplicationId(''); }}
options={[{ label: '请选择企业', value: '' }, ...tenants.map((item) => ({ label: item.name, value: item.id }))]}
value={formTenantId}
/>
<Select
label="短信应用"
onChange={(event) => setFormApplicationId(event.target.value)}
options={[{ label: formTenantId ? '请选择应用' : '请先选择企业', value: '' }, ...modalApplications.map((item) => ({ label: item.name, value: item.id }))]}
value={formApplicationId}
/>
<Input label="手机号码" onChange={(event) => setPhone(event.target.value)} placeholder="请输入手机号码" value={phone} />
<Textarea label="入库原因" onChange={(event) => setReason(event.target.value)} placeholder="请输入入库原因" rows={3} value={reason} />
</div>
</Modal>
</section>
);
}