feat(accounts): 增加高价值资产一键筛选与高级自定义筛选功能
1. 后端新增高价值套装状态筛选(双套均可换/可换白日梦/可换萌兔/已拥有/高价值待救/未查资料),兼容 MySQL 原生 JSON 与 SQLite unicode 转义存储; 2. 后端新增多维资产与状态区间筛选(积分范围、鱼翅范围、宝典开通、电竞开通、角色绑定状态),并支持与批量打标签/删除联动; 3. 统计接口 accounts_summary 新增 high_value 聚合数据,毫秒级返回各类高价值资产数量; 4. 前端账号管理页面新增「高价值一键筛选」胶囊区(带数量徽标与激活状态,即点即滤); 5. 前端新增可折叠「高级自定义筛选」面板,支持套装状态下拉、积分快捷区间(≥300、≥600)、鱼翅快捷区间(>0、≥6)、宝典与角色绑定状态多条件组合; 6. 补充 6 个高价值筛选与资产区间单元测试,全量用例通过。
This commit is contained in:
@@ -18,7 +18,25 @@ import type {
|
||||
export const accountApi = {
|
||||
list: (params?: { assigned_only?: boolean; tag?: string; has_cookie?: boolean; include_sensitive?: boolean }) =>
|
||||
api.get<AccountItem[], AccountItem[]>('/accounts', { params }),
|
||||
listPaged: (params: PageParams & { assigned_only?: boolean; tag?: string; has_cookie?: boolean; include_sensitive?: boolean; status?: string; ck_status?: string; bind_status?: string }) =>
|
||||
listPaged: (
|
||||
params: PageParams & {
|
||||
assigned_only?: boolean;
|
||||
tag?: string;
|
||||
has_cookie?: boolean;
|
||||
include_sensitive?: boolean;
|
||||
status?: string;
|
||||
ck_status?: string;
|
||||
bind_status?: string;
|
||||
high_value_filter?: string;
|
||||
min_points?: number;
|
||||
max_points?: number;
|
||||
min_gold?: number;
|
||||
max_gold?: number;
|
||||
elite_opened?: boolean;
|
||||
esports_opened?: boolean;
|
||||
role_bind_status?: string;
|
||||
},
|
||||
) =>
|
||||
api.get<PaginatedResponse<AccountItem>, PaginatedResponse<AccountItem>>('/accounts', { params }),
|
||||
summary: () => api.get<BasicSummary, BasicSummary>('/accounts/summary'),
|
||||
import: (text: string, tag?: string) =>
|
||||
|
||||
@@ -31,6 +31,17 @@ export interface AccountBulkSelection {
|
||||
tag?: string;
|
||||
assigned_only?: boolean;
|
||||
has_cookie?: boolean;
|
||||
status?: string;
|
||||
ck_status?: string;
|
||||
bind_status?: string;
|
||||
high_value_filter?: string;
|
||||
min_points?: number;
|
||||
max_points?: number;
|
||||
min_gold?: number;
|
||||
max_gold?: number;
|
||||
elite_opened?: boolean;
|
||||
esports_opened?: boolean;
|
||||
role_bind_status?: string;
|
||||
}
|
||||
|
||||
export interface AccountBulkTagRequest extends AccountBulkSelection {
|
||||
@@ -64,11 +75,24 @@ export interface AuditLogQuery {
|
||||
success?: boolean;
|
||||
}
|
||||
|
||||
export interface HighValueSummary {
|
||||
both_available: number;
|
||||
can_bairimeng: number;
|
||||
can_mengtu: number;
|
||||
unqueried: number;
|
||||
high_points: number;
|
||||
has_gold: number;
|
||||
elite_opened: number;
|
||||
high_value_broken: number;
|
||||
}
|
||||
|
||||
export interface BasicSummary {
|
||||
/** 生命周期状态分布(键是 6 状态枚举值);归档默认不计入 */
|
||||
by_status?: Record<string, number>;
|
||||
/** 不含归档的账号数 */
|
||||
visible_total?: number;
|
||||
/** 高价值及核心资产统计 */
|
||||
high_value?: HighValueSummary;
|
||||
/** CK 已被判失效的账号数 */
|
||||
ck_invalid_count?: number;
|
||||
/** 没有 CK 的账号数 */
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Button,
|
||||
Modal,
|
||||
Input,
|
||||
InputNumber,
|
||||
Select,
|
||||
Popconfirm,
|
||||
Typography,
|
||||
@@ -21,7 +22,18 @@ import {
|
||||
} from 'antd';
|
||||
import { message } from '../utils/antdMessage';
|
||||
import type { TableProps } from 'antd';
|
||||
import { ImportOutlined, DeleteOutlined, TagOutlined, FilterOutlined, EyeOutlined, EyeInvisibleOutlined, ThunderboltOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
ImportOutlined,
|
||||
DeleteOutlined,
|
||||
TagOutlined,
|
||||
FilterOutlined,
|
||||
EyeOutlined,
|
||||
EyeInvisibleOutlined,
|
||||
ThunderboltOutlined,
|
||||
DownOutlined,
|
||||
UpOutlined,
|
||||
ClearOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { accountApi, douyuApi, type AccountExchangeRecord, type AccountExchangeRecords, type AccountItem, type BasicSummary } from '../api/modules';
|
||||
import { usePermissions } from '../hooks/usePermissions';
|
||||
import { formatTime, formatTimeShort } from '../utils/time';
|
||||
@@ -73,6 +85,18 @@ const STATUS_CARDS = [
|
||||
{ value: 'disabled', label: '已停用', color: '#8c8c8c' },
|
||||
];
|
||||
|
||||
/** 高价值套装筛选项 */
|
||||
const HIGH_VALUE_OPTIONS = [
|
||||
{ value: 'both_available', label: '🌟 双套均可换 (高价值)' },
|
||||
{ value: 'can_bairimeng', label: '👗 可换白日梦爱丽丝' },
|
||||
{ value: 'can_mengtu', label: '🐰 可换狂战萌兔' },
|
||||
{ value: 'owned_bairimeng', label: '✓ 已拥有白日梦' },
|
||||
{ value: 'owned_mengtu', label: '✓ 已拥有萌兔' },
|
||||
{ value: 'both_owned', label: '✓ 两套均已拥有' },
|
||||
{ value: 'high_value_broken', label: '⚠️ 高价值但CK失效' },
|
||||
{ value: 'unqueried', label: '❓ 未查过资料' },
|
||||
];
|
||||
|
||||
|
||||
/** 单个作用域的绑定小标签:精/电/店。 */
|
||||
function BindChip({ scope, status }: { scope: string; status?: string }) {
|
||||
@@ -141,6 +165,34 @@ export default function AccountsPage() {
|
||||
const [ckFilter, setCkFilter] = useState<string>('');
|
||||
const [bindFilter, setBindFilter] = useState<string>('');
|
||||
const [searchText, setSearchText] = useState('');
|
||||
|
||||
// 高价值资产与高级自定义筛选状态
|
||||
const [highValueFilter, setHighValueFilter] = useState<string>('');
|
||||
const [minPoints, setMinPoints] = useState<number | undefined>(undefined);
|
||||
const [maxPoints, setMaxPoints] = useState<number | undefined>(undefined);
|
||||
const [minGold, setMinGold] = useState<number | undefined>(undefined);
|
||||
const [maxGold, setMaxGold] = useState<number | undefined>(undefined);
|
||||
const [eliteOpened, setEliteOpened] = useState<boolean | undefined>(undefined);
|
||||
const [esportsOpened, setEsportsOpened] = useState<boolean | undefined>(undefined);
|
||||
const [roleBindStatus, setRoleBindStatus] = useState<string>('');
|
||||
const [advFilterOpen, setAdvFilterOpen] = useState(false);
|
||||
|
||||
const resetAdvancedFiltersState = () => {
|
||||
setHighValueFilter('');
|
||||
setMinPoints(undefined);
|
||||
setMaxPoints(undefined);
|
||||
setMinGold(undefined);
|
||||
setMaxGold(undefined);
|
||||
setEliteOpened(undefined);
|
||||
setEsportsOpened(undefined);
|
||||
setRoleBindStatus('');
|
||||
};
|
||||
|
||||
const resetAdvancedFilters = () => {
|
||||
resetAdvancedFiltersState();
|
||||
setCurrentPage(1);
|
||||
};
|
||||
|
||||
// 兑换记录预览:点列表那一格**按需拉取**(不进列表响应),弹窗按作用域分组
|
||||
const [exRecords, setExRecords] = useState<AccountExchangeRecords | null>(null);
|
||||
const [exLoading, setExLoading] = useState(false);
|
||||
@@ -181,18 +233,26 @@ export default function AccountsPage() {
|
||||
const loadAccounts = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const params: { tag?: string; status?: string; ck_status?: string; bind_status?: string } = {};
|
||||
if (tagFilter) params.tag = tagFilter;
|
||||
if (statusFilter) params.status = statusFilter;
|
||||
if (ckFilter) params.ck_status = ckFilter;
|
||||
if (bindFilter) params.bind_status = bindFilter;
|
||||
const data = await accountApi.listPaged({
|
||||
...params,
|
||||
const params: Parameters<typeof accountApi.listPaged>[0] = {
|
||||
page: currentPage,
|
||||
page_size: pageSize,
|
||||
search: searchText.trim() || undefined,
|
||||
include_sensitive: canViewFull && sensitiveVisible,
|
||||
});
|
||||
};
|
||||
if (tagFilter) params.tag = tagFilter;
|
||||
if (statusFilter) params.status = statusFilter;
|
||||
if (ckFilter) params.ck_status = ckFilter;
|
||||
if (bindFilter) params.bind_status = bindFilter;
|
||||
if (highValueFilter) params.high_value_filter = highValueFilter;
|
||||
if (minPoints !== undefined) params.min_points = minPoints;
|
||||
if (maxPoints !== undefined) params.max_points = maxPoints;
|
||||
if (minGold !== undefined) params.min_gold = minGold;
|
||||
if (maxGold !== undefined) params.max_gold = maxGold;
|
||||
if (eliteOpened !== undefined) params.elite_opened = eliteOpened;
|
||||
if (esportsOpened !== undefined) params.esports_opened = esportsOpened;
|
||||
if (roleBindStatus) params.role_bind_status = roleBindStatus;
|
||||
|
||||
const data = await accountApi.listPaged(params);
|
||||
setAccounts(data.items);
|
||||
setTotal(data.total);
|
||||
} catch (e: unknown) {
|
||||
@@ -200,7 +260,25 @@ export default function AccountsPage() {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [canViewFull, currentPage, pageSize, searchText, sensitiveVisible, tagFilter, statusFilter, ckFilter, bindFilter]);
|
||||
}, [
|
||||
canViewFull,
|
||||
currentPage,
|
||||
pageSize,
|
||||
searchText,
|
||||
sensitiveVisible,
|
||||
tagFilter,
|
||||
statusFilter,
|
||||
ckFilter,
|
||||
bindFilter,
|
||||
highValueFilter,
|
||||
minPoints,
|
||||
maxPoints,
|
||||
minGold,
|
||||
maxGold,
|
||||
eliteOpened,
|
||||
esportsOpened,
|
||||
roleBindStatus,
|
||||
]);
|
||||
|
||||
const loadSummary = useCallback(async () => {
|
||||
try {
|
||||
@@ -235,7 +313,34 @@ export default function AccountsPage() {
|
||||
all_matching: selectedAllMatching,
|
||||
search: searchText.trim(),
|
||||
tag: tagFilter,
|
||||
}), [searchText, selectedAllMatching, selectedRowKeys, tagFilter]);
|
||||
status: statusFilter || undefined,
|
||||
ck_status: ckFilter || undefined,
|
||||
bind_status: bindFilter || undefined,
|
||||
high_value_filter: highValueFilter || undefined,
|
||||
min_points: minPoints,
|
||||
max_points: maxPoints,
|
||||
min_gold: minGold,
|
||||
max_gold: maxGold,
|
||||
elite_opened: eliteOpened,
|
||||
esports_opened: esportsOpened,
|
||||
role_bind_status: roleBindStatus || undefined,
|
||||
}), [
|
||||
searchText,
|
||||
selectedAllMatching,
|
||||
selectedRowKeys,
|
||||
tagFilter,
|
||||
statusFilter,
|
||||
ckFilter,
|
||||
bindFilter,
|
||||
highValueFilter,
|
||||
minPoints,
|
||||
maxPoints,
|
||||
minGold,
|
||||
maxGold,
|
||||
eliteOpened,
|
||||
esportsOpened,
|
||||
roleBindStatus,
|
||||
]);
|
||||
|
||||
const clearSelection = () => {
|
||||
setSelectedRowKeys([]);
|
||||
@@ -244,7 +349,21 @@ export default function AccountsPage() {
|
||||
|
||||
useEffect(() => {
|
||||
clearSelection();
|
||||
}, [searchText, tagFilter]);
|
||||
}, [
|
||||
searchText,
|
||||
tagFilter,
|
||||
statusFilter,
|
||||
ckFilter,
|
||||
bindFilter,
|
||||
highValueFilter,
|
||||
minPoints,
|
||||
maxPoints,
|
||||
minGold,
|
||||
maxGold,
|
||||
eliteOpened,
|
||||
esportsOpened,
|
||||
roleBindStatus,
|
||||
]);
|
||||
|
||||
const handleImport = async () => {
|
||||
if (!importText.trim()) {
|
||||
@@ -619,6 +738,157 @@ export default function AccountsPage() {
|
||||
});
|
||||
}
|
||||
|
||||
const hasCustomFilters = Boolean(
|
||||
highValueFilter ||
|
||||
minPoints !== undefined ||
|
||||
maxPoints !== undefined ||
|
||||
minGold !== undefined ||
|
||||
maxGold !== undefined ||
|
||||
eliteOpened !== undefined ||
|
||||
esportsOpened !== undefined ||
|
||||
roleBindStatus
|
||||
);
|
||||
|
||||
const PRESET_CHIPS = [
|
||||
{
|
||||
key: 'both_available',
|
||||
label: '双套均可换',
|
||||
icon: '🌟',
|
||||
count: summary.high_value?.both_available,
|
||||
color: '#52c41a',
|
||||
active: highValueFilter === 'both_available' && minPoints === undefined && maxPoints === undefined && minGold === undefined && maxGold === undefined && eliteOpened === undefined && esportsOpened === undefined && !roleBindStatus,
|
||||
onClick: () => {
|
||||
if (highValueFilter === 'both_available') {
|
||||
setHighValueFilter('');
|
||||
} else {
|
||||
resetAdvancedFiltersState();
|
||||
setHighValueFilter('both_available');
|
||||
}
|
||||
setCurrentPage(1);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'can_bairimeng',
|
||||
label: '可换白日梦',
|
||||
icon: '👗',
|
||||
count: summary.high_value?.can_bairimeng,
|
||||
color: '#1677ff',
|
||||
active: highValueFilter === 'can_bairimeng' && minPoints === undefined && maxPoints === undefined && minGold === undefined && maxGold === undefined && eliteOpened === undefined && esportsOpened === undefined && !roleBindStatus,
|
||||
onClick: () => {
|
||||
if (highValueFilter === 'can_bairimeng') {
|
||||
setHighValueFilter('');
|
||||
} else {
|
||||
resetAdvancedFiltersState();
|
||||
setHighValueFilter('can_bairimeng');
|
||||
}
|
||||
setCurrentPage(1);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'can_mengtu',
|
||||
label: '可换萌兔',
|
||||
icon: '🐰',
|
||||
count: summary.high_value?.can_mengtu,
|
||||
color: '#722ed1',
|
||||
active: highValueFilter === 'can_mengtu' && minPoints === undefined && maxPoints === undefined && minGold === undefined && maxGold === undefined && eliteOpened === undefined && esportsOpened === undefined && !roleBindStatus,
|
||||
onClick: () => {
|
||||
if (highValueFilter === 'can_mengtu') {
|
||||
setHighValueFilter('');
|
||||
} else {
|
||||
resetAdvancedFiltersState();
|
||||
setHighValueFilter('can_mengtu');
|
||||
}
|
||||
setCurrentPage(1);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'high_points',
|
||||
label: '积分≥300',
|
||||
icon: '💎',
|
||||
count: summary.high_value?.high_points,
|
||||
color: '#fa8c16',
|
||||
active: minPoints === 300 && maxPoints === undefined && !highValueFilter && minGold === undefined && maxGold === undefined && eliteOpened === undefined && esportsOpened === undefined && !roleBindStatus,
|
||||
onClick: () => {
|
||||
if (minPoints === 300) {
|
||||
setMinPoints(undefined);
|
||||
} else {
|
||||
resetAdvancedFiltersState();
|
||||
setMinPoints(300);
|
||||
}
|
||||
setCurrentPage(1);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'has_gold',
|
||||
label: '有鱼翅',
|
||||
icon: '💰',
|
||||
count: summary.high_value?.has_gold,
|
||||
color: '#faad14',
|
||||
active: minGold === 0.01 && maxGold === undefined && !highValueFilter && minPoints === undefined && maxPoints === undefined && eliteOpened === undefined && esportsOpened === undefined && !roleBindStatus,
|
||||
onClick: () => {
|
||||
if (minGold === 0.01) {
|
||||
setMinGold(undefined);
|
||||
} else {
|
||||
resetAdvancedFiltersState();
|
||||
setMinGold(0.01);
|
||||
}
|
||||
setCurrentPage(1);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'elite_opened',
|
||||
label: '宝典已开通',
|
||||
icon: '📜',
|
||||
count: summary.high_value?.elite_opened,
|
||||
color: '#13c2c2',
|
||||
active: eliteOpened === true && !highValueFilter && minPoints === undefined && maxPoints === undefined && minGold === undefined && maxGold === undefined && esportsOpened === undefined && !roleBindStatus,
|
||||
onClick: () => {
|
||||
if (eliteOpened === true) {
|
||||
setEliteOpened(undefined);
|
||||
} else {
|
||||
resetAdvancedFiltersState();
|
||||
setEliteOpened(true);
|
||||
}
|
||||
setCurrentPage(1);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'high_value_broken',
|
||||
label: '高价值待救',
|
||||
icon: '⚠️',
|
||||
count: summary.high_value?.high_value_broken,
|
||||
color: '#f5222d',
|
||||
tooltip: '可换高价值套装但 CK 失效,需重登抢救',
|
||||
active: highValueFilter === 'high_value_broken' && minPoints === undefined && maxPoints === undefined && minGold === undefined && maxGold === undefined && eliteOpened === undefined && esportsOpened === undefined && !roleBindStatus,
|
||||
onClick: () => {
|
||||
if (highValueFilter === 'high_value_broken') {
|
||||
setHighValueFilter('');
|
||||
} else {
|
||||
resetAdvancedFiltersState();
|
||||
setHighValueFilter('high_value_broken');
|
||||
}
|
||||
setCurrentPage(1);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'unqueried',
|
||||
label: '未查资料',
|
||||
icon: '🔍',
|
||||
count: summary.high_value?.unqueried,
|
||||
color: '#8c8c8c',
|
||||
active: highValueFilter === 'unqueried' && minPoints === undefined && maxPoints === undefined && minGold === undefined && maxGold === undefined && eliteOpened === undefined && esportsOpened === undefined && !roleBindStatus,
|
||||
onClick: () => {
|
||||
if (highValueFilter === 'unqueried') {
|
||||
setHighValueFilter('');
|
||||
} else {
|
||||
resetAdvancedFiltersState();
|
||||
setHighValueFilter('unqueried');
|
||||
}
|
||||
setCurrentPage(1);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
|
||||
@@ -765,6 +1035,287 @@ export default function AccountsPage() {
|
||||
})}
|
||||
</Row>
|
||||
|
||||
{/* 高价值资产一键筛选 & 高级自定义筛选面板 */}
|
||||
<Card
|
||||
size="small"
|
||||
style={{ marginBottom: 12, borderRadius: 6 }}
|
||||
styles={{ body: { padding: '8px 12px' } }}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: 8 }}>
|
||||
<Space wrap size={[6, 6]} style={{ alignItems: 'center' }}>
|
||||
<Text type="secondary" style={{ fontSize: 13, marginRight: 4 }}>
|
||||
<ThunderboltOutlined style={{ color: '#fa8c16', marginRight: 4 }} />
|
||||
高价值一键筛选:
|
||||
</Text>
|
||||
{PRESET_CHIPS.map((chip) => {
|
||||
const btn = (
|
||||
<Button
|
||||
key={chip.key}
|
||||
size="small"
|
||||
type={chip.active ? 'primary' : 'default'}
|
||||
style={{
|
||||
borderRadius: 14,
|
||||
fontSize: 12,
|
||||
height: 26,
|
||||
padding: '0 10px',
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
borderColor: chip.active ? chip.color : undefined,
|
||||
backgroundColor: chip.active ? chip.color : undefined,
|
||||
color: chip.active ? '#fff' : undefined,
|
||||
opacity: (chip.count === 0 && !chip.active) ? 0.5 : 1,
|
||||
}}
|
||||
onClick={chip.onClick}
|
||||
>
|
||||
<span>{chip.icon} {chip.label}</span>
|
||||
<span
|
||||
style={{
|
||||
marginLeft: 5,
|
||||
padding: '0 5px',
|
||||
borderRadius: 10,
|
||||
fontSize: 11,
|
||||
lineHeight: '16px',
|
||||
background: chip.active ? 'rgba(255,255,255,0.25)' : '#f0f0f0',
|
||||
color: chip.active ? '#fff' : '#666',
|
||||
fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
{chip.count ?? 0}
|
||||
</span>
|
||||
</Button>
|
||||
);
|
||||
return chip.tooltip ? (
|
||||
<Tooltip key={chip.key} title={chip.tooltip}>
|
||||
{btn}
|
||||
</Tooltip>
|
||||
) : btn;
|
||||
})}
|
||||
</Space>
|
||||
|
||||
<Space size={8}>
|
||||
{hasCustomFilters && (
|
||||
<Button size="small" type="link" danger onClick={resetAdvancedFilters} style={{ padding: 0 }}>
|
||||
清除高价值筛选
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
size="small"
|
||||
type={advFilterOpen ? 'primary' : 'default'}
|
||||
ghost={advFilterOpen}
|
||||
icon={advFilterOpen ? <UpOutlined /> : <DownOutlined />}
|
||||
onClick={() => setAdvFilterOpen(!advFilterOpen)}
|
||||
>
|
||||
{advFilterOpen ? '收起高级筛选' : '高级自定义筛选'}
|
||||
{hasCustomFilters && !advFilterOpen && (
|
||||
<Tag color="blue" style={{ marginLeft: 4, marginRight: 0, padding: '0 4px', fontSize: 11 }}>
|
||||
已生效
|
||||
</Tag>
|
||||
)}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
{/* 折叠展开的高级筛选面板 */}
|
||||
{advFilterOpen && (
|
||||
<div style={{ marginTop: 10, paddingTop: 10, borderTop: '1px dashed #f0f0f0' }}>
|
||||
<Row gutter={[16, 10]} align="middle">
|
||||
<Col>
|
||||
<Space size={6} align="center">
|
||||
<Text type="secondary" style={{ fontSize: 13 }}>套装状态:</Text>
|
||||
<Select
|
||||
allowClear
|
||||
size="small"
|
||||
placeholder="全部状态"
|
||||
style={{ width: 180 }}
|
||||
value={highValueFilter || undefined}
|
||||
onChange={(val) => {
|
||||
setHighValueFilter(val || '');
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
options={HIGH_VALUE_OPTIONS}
|
||||
/>
|
||||
</Space>
|
||||
</Col>
|
||||
|
||||
<Col>
|
||||
<Space size={4} align="center">
|
||||
<Text type="secondary" style={{ fontSize: 13 }}>积分范围:</Text>
|
||||
<InputNumber
|
||||
size="small"
|
||||
placeholder="最小"
|
||||
min={0}
|
||||
value={minPoints}
|
||||
onChange={(v) => {
|
||||
setMinPoints(v ?? undefined);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
style={{ width: 75 }}
|
||||
/>
|
||||
<span>~</span>
|
||||
<InputNumber
|
||||
size="small"
|
||||
placeholder="最大"
|
||||
min={0}
|
||||
value={maxPoints}
|
||||
onChange={(v) => {
|
||||
setMaxPoints(v ?? undefined);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
style={{ width: 75 }}
|
||||
/>
|
||||
<Button
|
||||
size="small"
|
||||
type="dashed"
|
||||
style={{ fontSize: 11, padding: '0 6px' }}
|
||||
onClick={() => {
|
||||
setMinPoints(300);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
>
|
||||
≥300
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
type="dashed"
|
||||
style={{ fontSize: 11, padding: '0 6px' }}
|
||||
onClick={() => {
|
||||
setMinPoints(600);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
>
|
||||
≥600
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
|
||||
<Col>
|
||||
<Space size={4} align="center">
|
||||
<Text type="secondary" style={{ fontSize: 13 }}>鱼翅余额:</Text>
|
||||
<InputNumber
|
||||
size="small"
|
||||
placeholder="最小"
|
||||
min={0}
|
||||
step={1}
|
||||
value={minGold}
|
||||
onChange={(v) => {
|
||||
setMinGold(v ?? undefined);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
style={{ width: 75 }}
|
||||
/>
|
||||
<span>~</span>
|
||||
<InputNumber
|
||||
size="small"
|
||||
placeholder="最大"
|
||||
min={0}
|
||||
step={1}
|
||||
value={maxGold}
|
||||
onChange={(v) => {
|
||||
setMaxGold(v ?? undefined);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
style={{ width: 75 }}
|
||||
/>
|
||||
<Button
|
||||
size="small"
|
||||
type="dashed"
|
||||
style={{ fontSize: 11, padding: '0 6px' }}
|
||||
onClick={() => {
|
||||
setMinGold(0.01);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
>
|
||||
>0
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
type="dashed"
|
||||
style={{ fontSize: 11, padding: '0 6px' }}
|
||||
onClick={() => {
|
||||
setMinGold(6);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
>
|
||||
≥6
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
|
||||
<Col>
|
||||
<Space size={6} align="center">
|
||||
<Text type="secondary" style={{ fontSize: 13 }}>宝典:</Text>
|
||||
<Select
|
||||
size="small"
|
||||
allowClear
|
||||
placeholder="全部"
|
||||
style={{ width: 95 }}
|
||||
value={eliteOpened === undefined ? undefined : String(eliteOpened)}
|
||||
onChange={(val) => {
|
||||
setEliteOpened(val === undefined ? undefined : val === 'true');
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
options={[
|
||||
{ value: 'true', label: '宝典已开' },
|
||||
{ value: 'false', label: '宝典未开' },
|
||||
]}
|
||||
/>
|
||||
</Space>
|
||||
</Col>
|
||||
|
||||
<Col>
|
||||
<Space size={6} align="center">
|
||||
<Text type="secondary" style={{ fontSize: 13 }}>电竞:</Text>
|
||||
<Select
|
||||
size="small"
|
||||
allowClear
|
||||
placeholder="全部"
|
||||
style={{ width: 95 }}
|
||||
value={esportsOpened === undefined ? undefined : String(esportsOpened)}
|
||||
onChange={(val) => {
|
||||
setEsportsOpened(val === undefined ? undefined : val === 'true');
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
options={[
|
||||
{ value: 'true', label: '电竞已开' },
|
||||
{ value: 'false', label: '电竞未开' },
|
||||
]}
|
||||
/>
|
||||
</Space>
|
||||
</Col>
|
||||
|
||||
<Col>
|
||||
<Space size={6} align="center">
|
||||
<Text type="secondary" style={{ fontSize: 13 }}>角色绑定:</Text>
|
||||
<Select
|
||||
size="small"
|
||||
allowClear
|
||||
placeholder="全部"
|
||||
style={{ width: 100 }}
|
||||
value={roleBindStatus || undefined}
|
||||
onChange={(val) => {
|
||||
setRoleBindStatus(val || '');
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
options={BIND_OPTIONS}
|
||||
/>
|
||||
</Space>
|
||||
</Col>
|
||||
|
||||
<Col>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<ClearOutlined />}
|
||||
onClick={resetAdvancedFilters}
|
||||
disabled={!hasCustomFilters}
|
||||
>
|
||||
重置自定义条件
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Table
|
||||
tableLayout="fixed"
|
||||
title={() => selectedRowKeys.length > 0 ? (
|
||||
|
||||
Reference in New Issue
Block a user