feat: complete drainage review and admin search workflows
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Plus, Search, Trash2 } from 'lucide-react';
|
||||
import { Breadcrumb, Button, Input, Modal, Select, Table, Textarea, type TableColumn } from '@/components/ui';
|
||||
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 & {
|
||||
@@ -16,9 +16,12 @@ export function AdminEnterpriseBlacklistPage() {
|
||||
const [items, setItems] = useState<EnterpriseBlacklistItem[]>([]);
|
||||
const [tenants, setTenants] = useState<TenantOption[]>([]);
|
||||
const [applications, setApplications] = useState<EnterpriseApplication[]>([]);
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [filterTenantId, setFilterTenantId] = useState('');
|
||||
const [filterApplicationId, setFilterApplicationId] = useState('');
|
||||
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('');
|
||||
@@ -26,9 +29,9 @@ export function AdminEnterpriseBlacklistPage() {
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
function loadData() {
|
||||
function loadData(filters = appliedFilters) {
|
||||
Promise.all([
|
||||
adminApi.listEnterpriseBlacklist({ tenantId: filterTenantId || undefined, applicationId: filterApplicationId || undefined, keyword }),
|
||||
adminApi.listEnterpriseBlacklist(filters),
|
||||
adminApi.listTenants(),
|
||||
adminApi.listEnterpriseApplications(),
|
||||
])
|
||||
@@ -45,11 +48,6 @@ export function AdminEnterpriseBlacklistPage() {
|
||||
loadData();
|
||||
}, []);
|
||||
|
||||
const filteredItems = useMemo(() => items.filter((item) => {
|
||||
const text = [item.tenant?.name, item.application?.name, item.phoneNumber, item.reason, item.status].join(' ');
|
||||
return !keyword || text.includes(keyword);
|
||||
}), [items, keyword]);
|
||||
const filterApplications = applications.filter((application) => application.tenantId === filterTenantId && application.status !== 'deleted');
|
||||
const modalApplications = applications.filter((application) => application.tenantId === formTenantId && application.status !== 'deleted');
|
||||
|
||||
const columns = useMemo<Array<TableColumn<EnterpriseBlacklistItem>>>(() => [
|
||||
@@ -58,19 +56,19 @@ export function AdminEnterpriseBlacklistPage() {
|
||||
{ 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) => record.status ?? '-' },
|
||||
{ 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 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' })
|
||||
@@ -98,32 +96,35 @@ export function AdminEnterpriseBlacklistPage() {
|
||||
|
||||
<div className="surface admin-security-filter">
|
||||
<Input
|
||||
label="搜索"
|
||||
onChange={(event) => setKeyword(event.target.value)}
|
||||
placeholder="搜索企业、应用、手机号或原因"
|
||||
label="企业名称"
|
||||
onChange={(event) => setEnterpriseKeyword(event.target.value)}
|
||||
placeholder="请输入企业名称"
|
||||
prefix={<Search size={16} />}
|
||||
value={keyword}
|
||||
value={enterpriseKeyword}
|
||||
/>
|
||||
<Select
|
||||
label="企业"
|
||||
onChange={(event) => { setFilterTenantId(event.target.value); setFilterApplicationId(''); }}
|
||||
options={[{ label: '全部企业', value: '' }, ...tenants.map((item) => ({ label: item.name, value: item.id }))]}
|
||||
value={filterTenantId}
|
||||
<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) => setFilterApplicationId(event.target.value)}
|
||||
options={[{ label: filterTenantId ? '全部应用' : '请先选择企业', value: '' }, ...filterApplications.map((item) => ({ label: item.name, value: item.id }))]}
|
||||
value={filterApplicationId}
|
||||
label="状态"
|
||||
onChange={(event) => setStatus(event.target.value)}
|
||||
options={[{ label: '全部状态', value: 'all' }, { label: '生效中', value: 'active' }, { label: '已删除', value: 'deleted' }]}
|
||||
value={status}
|
||||
/>
|
||||
<div className="admin-security-filter__actions">
|
||||
<Button icon={<Search size={16} />} onClick={loadData}>查询</Button>
|
||||
<Button onClick={() => { setKeyword(''); setFilterTenantId(''); setFilterApplicationId(''); }} variant="ghost">重置</Button>
|
||||
<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={filteredItems} emptyText="暂无企业黑名单记录" rowKey="id" />
|
||||
<Table columns={columns} data={items} emptyText="暂无企业黑名单记录" rowKey="id" />
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
|
||||
Reference in New Issue
Block a user