feat: 为CK页面增加标签筛选
This commit is contained in:
@@ -3,11 +3,12 @@ import type { BasicSummary, CookieCheckResult, CookieDuplicateResponse, CookieIt
|
||||
|
||||
export const cookieApi = {
|
||||
list: () => api.get<CookieItem[], CookieItem[]>('/cookies'),
|
||||
listPaged: (params: PageParams & { include_cookie?: boolean; account_names?: string }) =>
|
||||
listPaged: (params: PageParams & { include_cookie?: boolean; account_names?: string; tag?: string }) =>
|
||||
api.get<PaginatedResponse<CookieItem>, PaginatedResponse<CookieItem>>('/cookies', { params }),
|
||||
summary: () => api.get<BasicSummary, BasicSummary>('/cookies/summary'),
|
||||
listOperations: (params: PageParams) =>
|
||||
listOperations: (params: PageParams & { tag?: string }) =>
|
||||
api.get<PaginatedResponse<CookieOperationItem>, PaginatedResponse<CookieOperationItem>>('/cookies/operations', { params }),
|
||||
listOperationTags: () => api.get<string[], string[]>('/cookies/operations/tags'),
|
||||
duplicates: () => api.get<CookieDuplicateResponse, CookieDuplicateResponse>('/cookies/duplicates'),
|
||||
get: (id: number) => api.get<CookieItem, CookieItem>(`/cookies/${id}`),
|
||||
exportCsv: (format?: string, accountNames?: string) => api.get<Blob, Blob>('/cookies/export', {
|
||||
|
||||
@@ -256,6 +256,7 @@ export interface CookieItem {
|
||||
export interface CookieOperationItem {
|
||||
id: number;
|
||||
account_username: string;
|
||||
tag: string;
|
||||
ck_check_status: string;
|
||||
ck_checked_at: string | null;
|
||||
created_at: string | null;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Button, Input, Popconfirm, Space, Table, Tag, Typography } from 'antd';
|
||||
import { Button, Input, Popconfirm, Select, Space, Table, Tag, Typography } from 'antd';
|
||||
import { message } from '../utils/antdMessage';
|
||||
import { LoadingOutlined, ReloadOutlined, SearchOutlined } from '@ant-design/icons';
|
||||
import { cookieApi, type CookieOperationCheckResult, type CookieOperationItem } from '../api/modules';
|
||||
@@ -12,6 +12,8 @@ export default function CookieOperationsPage() {
|
||||
const [items, setItems] = useState<CookieOperationItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [search, setSearch] = useState('');
|
||||
const [tagFilter, setTagFilter] = useState('');
|
||||
const [tags, setTags] = useState<string[]>([]);
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||
const [checkingIds, setCheckingIds] = useState<Set<number>>(new Set());
|
||||
const [reloginIds, setReloginIds] = useState<Set<number>>(new Set());
|
||||
@@ -30,6 +32,7 @@ export default function CookieOperationsPage() {
|
||||
page: currentPage,
|
||||
page_size: pageSize,
|
||||
search: search.trim() || undefined,
|
||||
tag: tagFilter || undefined,
|
||||
});
|
||||
setItems(data.items);
|
||||
setTotal(data.total);
|
||||
@@ -50,12 +53,18 @@ export default function CookieOperationsPage() {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [currentPage, pageSize, search]);
|
||||
}, [currentPage, pageSize, search, tagFilter]);
|
||||
|
||||
useEffect(() => {
|
||||
void loadItems();
|
||||
}, [loadItems]);
|
||||
|
||||
useEffect(() => {
|
||||
cookieApi.listOperationTags().then(setTags).catch(() => {
|
||||
// 标签加载失败不影响 CK 检测与重登。
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleCheck = async (ids: number[]) => {
|
||||
if (ids.length === 0) return;
|
||||
setCheckingIds((previous) => new Set([...previous, ...ids]));
|
||||
@@ -103,6 +112,13 @@ export default function CookieOperationsPage() {
|
||||
const selectedIds = selectedRowKeys.map((key) => Number(key));
|
||||
const columns = [
|
||||
{ title: '账号', dataIndex: 'account_username', ellipsis: true },
|
||||
{
|
||||
title: '标签',
|
||||
dataIndex: 'tag',
|
||||
width: 140,
|
||||
ellipsis: true,
|
||||
render: (tag: string) => tag ? <Tag color="blue">{tag}</Tag> : <Text type="secondary">-</Text>,
|
||||
},
|
||||
{
|
||||
title: '有效性',
|
||||
width: 110,
|
||||
@@ -168,18 +184,33 @@ export default function CookieOperationsPage() {
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
</div>
|
||||
<Input.Search
|
||||
allowClear
|
||||
prefix={<SearchOutlined />}
|
||||
placeholder="搜索账号"
|
||||
style={{ width: 280, marginBottom: 12 }}
|
||||
value={search}
|
||||
onChange={(event) => {
|
||||
setSearch(event.target.value);
|
||||
setCurrentPage(1);
|
||||
setSelectedRowKeys([]);
|
||||
}}
|
||||
/>
|
||||
<Space wrap style={{ marginBottom: 12 }}>
|
||||
<Select
|
||||
allowClear
|
||||
showSearch
|
||||
placeholder="按标签筛选"
|
||||
style={{ width: 160 }}
|
||||
value={tagFilter || undefined}
|
||||
onChange={(value) => {
|
||||
setTagFilter(value || '');
|
||||
setCurrentPage(1);
|
||||
setSelectedRowKeys([]);
|
||||
}}
|
||||
options={tags.map((tag) => ({ value: tag, label: tag }))}
|
||||
/>
|
||||
<Input.Search
|
||||
allowClear
|
||||
prefix={<SearchOutlined />}
|
||||
placeholder="搜索账号或标签"
|
||||
style={{ width: 280 }}
|
||||
value={search}
|
||||
onChange={(event) => {
|
||||
setSearch(event.target.value);
|
||||
setCurrentPage(1);
|
||||
setSelectedRowKeys([]);
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
<Table
|
||||
rowKey="id"
|
||||
size="small"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { Table, Button, Card, Row, Col, Statistic, Tag, Popconfirm, Space, Typography, Input, theme, Dropdown, Tooltip, Modal, Alert, Empty } from 'antd';
|
||||
import { Table, Button, Card, Row, Col, Statistic, Tag, Popconfirm, Space, Typography, Input, Select, theme, Dropdown, Tooltip, Modal, Alert, Empty } from 'antd';
|
||||
import { message } from '../utils/antdMessage';
|
||||
import { DownloadOutlined, DeleteOutlined, CopyOutlined, SearchOutlined, LoadingOutlined, ReloadOutlined, FilterOutlined, ScanOutlined } from '@ant-design/icons';
|
||||
import { cookieApi, type BasicSummary, type CookieCheckResult, type CookieDuplicateGroup, type CookieDuplicateResponse, type CookieItem } from '../api/modules';
|
||||
import { accountApi, cookieApi, type BasicSummary, type CookieCheckResult, type CookieDuplicateGroup, type CookieDuplicateResponse, type CookieItem } from '../api/modules';
|
||||
import { usePermissions } from '../hooks/usePermissions';
|
||||
import { formatTime } from '../utils/time';
|
||||
import { getErrorMessage } from '../utils/error';
|
||||
@@ -53,6 +53,8 @@ export default function CookiePage() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [tagFilter, setTagFilter] = useState('');
|
||||
const [tags, setTags] = useState<string[]>([]);
|
||||
const [customNamesText, setCustomNamesText] = useState('');
|
||||
const [customAccountNames, setCustomAccountNames] = useState<string[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
@@ -181,6 +183,7 @@ export default function CookiePage() {
|
||||
page: currentPage,
|
||||
page_size: pageSize,
|
||||
search: searchText.trim() || undefined,
|
||||
tag: tagFilter || undefined,
|
||||
account_names: customAccountNames.length > 0 ? customAccountNames.join('\n') : undefined,
|
||||
include_cookie: false,
|
||||
});
|
||||
@@ -208,7 +211,7 @@ export default function CookiePage() {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [currentPage, pageSize, searchText, customAccountNames]);
|
||||
}, [currentPage, pageSize, searchText, tagFilter, customAccountNames]);
|
||||
|
||||
const loadSummary = useCallback(async () => {
|
||||
try {
|
||||
@@ -227,6 +230,12 @@ export default function CookiePage() {
|
||||
loadSummary();
|
||||
}, [loadSummary]);
|
||||
|
||||
useEffect(() => {
|
||||
accountApi.listTags().then(setTags).catch(() => {
|
||||
// 标签加载失败不影响 Cookie 管理。
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleExport = async (format: string = 'csv') => {
|
||||
try {
|
||||
const blob = await cookieApi.exportCsv(
|
||||
@@ -626,9 +635,22 @@ export default function CookiePage() {
|
||||
</Space>
|
||||
</Space>
|
||||
</Card>
|
||||
<div style={{ marginBottom: 12 }}>
|
||||
<Space wrap style={{ marginBottom: 12 }}>
|
||||
<Select
|
||||
allowClear
|
||||
showSearch
|
||||
placeholder="按标签筛选"
|
||||
style={{ width: 160 }}
|
||||
value={tagFilter || undefined}
|
||||
onChange={(value) => {
|
||||
setTagFilter(value || '');
|
||||
setCurrentPage(1);
|
||||
setSelectedRowKeys([]);
|
||||
}}
|
||||
options={tags.map((tag) => ({ value: tag, label: tag }))}
|
||||
/>
|
||||
<Input.Search
|
||||
placeholder="搜索账号或分配客服"
|
||||
placeholder="搜索账号、标签或分配客服"
|
||||
allowClear
|
||||
value={searchText}
|
||||
onChange={(e) => {
|
||||
@@ -639,7 +661,7 @@ export default function CookiePage() {
|
||||
size="small"
|
||||
prefix={<SearchOutlined />}
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
<Table
|
||||
rowSelection={{
|
||||
selectedRowKeys,
|
||||
|
||||
Reference in New Issue
Block a user