@@ -0,0 +1,209 @@
import { useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
import {
Button , Card , Input , InputNumber , Modal , QRCode , Select , Space , Table , Tag , Tooltip , Typography ,
} from 'antd' ;
import type { TableProps } from 'antd' ;
import {
CheckCircleOutlined , ColumnWidthOutlined , GiftOutlined , ImportOutlined , QrcodeOutlined ,
ReloadOutlined , SearchOutlined , SettingOutlined , ShoppingOutlined , StopOutlined ,
} from '@ant-design/icons' ;
import { huyaApi , type HuyaAccountItem , type HuyaConfig , type HuyaGoodsItem , type HuyaTaskItem } from '../api/modules' ;
import RealtimeLogPanel from '../components/RealtimeLogPanel' ;
import { usePermissions } from '../hooks/usePermissions' ;
import { useWebSocketLogs } from '../hooks/useWebSocketLogs' ;
import { getErrorMessage } from '../utils/error' ;
import { message } from '../utils/antdMessage' ;
const { Text } = Typography ;
const SCOPE = 'elite' as const ;
const ACT_ID = 25135 ;
const DEFAULT_SID = '2203' ;
const LAYOUT_KEY = 'huya_elite_layout_mode' ;
const TASK_LABELS : Record < string , string > = {
query_act_tasks : '读取宝典任务' , get_bind_qr : '获取绑定二维码' , query_game_name : '查询游戏角色' ,
confirm_bind : '确认绑定角色' , query_points : '查询积分' , refresh_goods : '刷新商品' ,
exchange_goods : '兑换商品' , query_exchange_records : '查询兑换记录' , create_recharge_order : '生成开通支付码' ,
} ;
const TASK_COLORS : Record < string , string > = {
planned : 'default' , pending : 'default' , running : 'processing' , success : 'success' ,
failed : 'error' , error : 'error' , stopped : 'warning' ,
} ;
const TASK_STATUS_LABELS : Record < string , string > = {
planned : '已计划' , pending : '等待中' , running : '执行中' , success : '成功' ,
failed : '失败' , error : '异常' , stopped : '已停止' ,
} ;
function savedInterval ( ) : number {
const value = Number ( localStorage . getItem ( 'huya_elite_refresh_interval' ) ) ;
return Number . isInteger ( value ) && value >= 5 && value <= 60 ? value : 15 ;
}
function resultText ( task : HuyaTaskItem | undefined , key : string ) : string {
const value = task ? . result ? . [ key ] ;
return value == null ? '' : String ( value ) ;
}
function latestTask ( tasks : HuyaTaskItem [ ] , accountId : number ) : HuyaTaskItem | undefined {
return tasks . filter ( ( task ) = > task . account_id === accountId ) . sort ( ( a , b ) = > b . id - a . id ) [ 0 ] ;
}
export default function HuyaElitePage() {
const { can } = usePermissions ( ) ;
const canConfig = can ( 'huya:config' ) ;
const [ pool , setPool ] = useState < HuyaAccountItem [ ] > ( [ ] ) ;
const [ accounts , setAccounts ] = useState < HuyaAccountItem [ ] > ( [ ] ) ;
const [ selectedIds , setSelectedIds ] = useState < number [ ] > ( [ ] ) ;
const [ tasks , setTasks ] = useState < HuyaTaskItem [ ] > ( [ ] ) ;
const [ goods , setGoods ] = useState < HuyaGoodsItem [ ] > ( [ ] ) ;
const [ selectedGoodsId , setSelectedGoodsId ] = useState ( '' ) ;
const [ search , setSearch ] = useState ( '' ) ;
const [ loading , setLoading ] = useState ( false ) ;
const [ starting , setStarting ] = useState ( false ) ;
const [ concurrency , setConcurrency ] = useState ( 3 ) ;
const [ refreshInterval , setRefreshInterval ] = useState ( savedInterval ) ;
const [ layoutMode , setLayoutMode ] = useState < 'split' | 'stack' > ( ( ) = > localStorage . getItem ( LAYOUT_KEY ) === 'stack' ? 'stack' : 'split' ) ;
const [ activeBatches , setActiveBatches ] = useState < string [ ] > ( [ ] ) ;
const [ importOpen , setImportOpen ] = useState ( false ) ;
const [ importSearch , setImportSearch ] = useState ( '' ) ;
const [ importTag , setImportTag ] = useState ( '' ) ;
const [ importTags , setImportTags ] = useState < string [ ] > ( [ ] ) ;
const [ importSelected , setImportSelected ] = useState < number [ ] > ( [ ] ) ;
const [ qrTask , setQrTask ] = useState < HuyaTaskItem | null > ( null ) ;
const [ config , setConfig ] = useState < HuyaConfig | null > ( null ) ;
const [ configDraft , setConfigDraft ] = useState < HuyaConfig | null > ( null ) ;
const [ configOpen , setConfigOpen ] = useState ( false ) ;
const [ configLoading , setConfigLoading ] = useState ( false ) ;
const [ configSaving , setConfigSaving ] = useState ( false ) ;
const tasksLoading = useRef ( false ) ;
const logs = useWebSocketLogs ( ) ;
const load = useCallback ( async ( ) = > {
setLoading ( true ) ;
try {
const [ poolResult , workbenchResult , goodsResult , taskResult ] = await Promise . all ( [
huyaApi . listAccounts ( { include_cookie : false } ) , huyaApi . listWorkbenchAccounts ( SCOPE ) ,
huyaApi . listGoods ( ) , huyaApi . listTasks ( undefined , SCOPE ) ,
] ) ;
setPool ( poolResult ) ;
const byId = new Map ( poolResult . map ( ( item ) = > [ item . id , item ] ) ) ;
let storedIds : number [ ] = workbenchResult . account_ids ;
if ( ! workbenchResult . configured ) {
try { storedIds = JSON . parse ( localStorage . getItem ( 'huya_elite_workbench_account_ids' ) || '[]' ) as number [ ] ; } catch { storedIds = [ ] ; }
}
setAccounts ( storedIds . map ( ( id ) = > byId . get ( Number ( id ) ) ) . filter ( ( item ) : item is HuyaAccountItem = > Boolean ( item ) ) ) ;
setSelectedIds ( ( prev ) = > prev . filter ( ( id ) = > storedIds . includes ( id ) ) ) ;
setGoods ( goodsResult ) ;
setTasks ( taskResult ) ;
} catch ( error ) { message . error ( getErrorMessage ( error ) ) ; } finally { setLoading ( false ) ; }
} , [ ] ) ;
const loadTasks = useCallback ( async ( ) = > {
if ( tasksLoading . current ) return ;
tasksLoading . current = true ;
try { setTasks ( await huyaApi . listTasks ( undefined , SCOPE ) ) ; } catch { /* 下一轮刷新 */ } finally { tasksLoading . current = false ; }
} , [ ] ) ;
useEffect ( ( ) = > { void load ( ) ; } , [ load ] ) ;
useEffect ( ( ) = > { if ( importOpen ) void huyaApi . listTags ( ) . then ( setImportTags ) . catch ( ( ) = > { } ) ; } , [ importOpen ] ) ;
useEffect ( ( ) = > {
const active = tasks . some ( ( task ) = > [ 'planned' , 'pending' , 'running' ] . includes ( task . status ) ) ;
const timer = window . setInterval ( ( ) = > { void loadTasks ( ) ; } , ( active ? 3 : refreshInterval ) * 1000 ) ;
return ( ) = > window . clearInterval ( timer ) ;
} , [ loadTasks , refreshInterval , tasks ] ) ;
const saveAccounts = async ( ids : number [ ] ) = > {
const saved = await huyaApi . updateWorkbenchAccounts ( ids , SCOPE ) ;
localStorage . setItem ( 'huya_elite_workbench_account_ids' , JSON . stringify ( saved . account_ids ) ) ;
const next = saved . account_ids . map ( ( id ) = > pool . find ( ( item ) = > item . id === id ) ) . filter ( ( item ) : item is HuyaAccountItem = > Boolean ( item ) ) ;
setAccounts ( next ) ;
setSelectedIds ( ( prev ) = > prev . filter ( ( id ) = > saved . account_ids . includes ( id ) ) ) ;
} ;
const startTask = async ( taskType : string ) = > {
if ( ! selectedIds . length ) { message . warning ( '请先勾选账号' ) ; return ; }
if ( taskType === 'exchange_goods' && ! selectedGoodsId ) { message . warning ( '请先选择兑换商品' ) ; return ; }
setStarting ( true ) ;
try {
const payload = taskType === 'exchange_goods'
? { sid : Number ( config ? . sid || DEFAULT_SID ) , product_id : Number ( selectedGoodsId ) , act_id : ACT_ID }
: taskType === 'query_act_tasks' ? { act_id : ACT_ID } : { } ;
const created = await huyaApi . createTasks ( { account_ids : selectedIds , task_type : taskType , handbook_scope : SCOPE , concurrency , payload } ) ;
setActiveBatches ( ( prev ) = > [ . . . new Set ( [ . . . prev , created . batch_id ] ) ] ) ;
logs . connectBatch ( created . batch_id , ` /api/huya/ws/ ${ created . batch_id } ` , {
clear : false ,
onTask : ( raw ) = > setTasks ( ( prev ) = > [ raw as unknown as HuyaTaskItem , . . . prev . filter ( ( item ) = > item . id !== Number ( ( raw as { id? : number } ) . id ) ) ] . slice ( 0 , 300 ) ) ,
onResult : ( ) = > { setActiveBatches ( ( prev ) = > prev . filter ( ( id ) = > id !== created . batch_id ) ) ; void loadTasks ( ) ; } ,
} ) ;
message . success ( ` 已创建 ${ created . count } 个任务 ` ) ;
window . setTimeout ( ( ) = > { void loadTasks ( ) ; } , 400 ) ;
} catch ( error ) { message . error ( getErrorMessage ( error ) ) ; } finally { setStarting ( false ) ; }
} ;
const openSettings = async ( ) = > {
if ( ! canConfig ) return ;
setConfigOpen ( true ) ;
if ( config ) { setConfigDraft ( { . . . config } ) ; return ; }
setConfigLoading ( true ) ;
try { const current = await huyaApi . getConfig ( ) ; setConfig ( current ) ; setConfigDraft ( { . . . current } ) ; }
catch ( error ) { setConfigOpen ( false ) ; message . error ( getErrorMessage ( error ) ) ; }
finally { setConfigLoading ( false ) ; }
} ;
const saveConfig = async ( ) = > {
if ( ! configDraft ) return ;
setConfigSaving ( true ) ;
try { const saved = await huyaApi . updateConfig ( configDraft ) ; setConfig ( saved ) ; setConfigDraft ( { . . . saved } ) ; setConfigOpen ( false ) ; message . success ( '配置已保存' ) ; }
catch ( error ) { message . error ( getErrorMessage ( error ) ) ; } finally { setConfigSaving ( false ) ; }
} ;
const stop = async ( ) = > {
try { await Promise . all ( activeBatches . map ( ( batchId ) = > huyaApi . stopBatch ( batchId ) ) ) ; setActiveBatches ( [ ] ) ; message . success ( '已停止当前批次' ) ; }
catch ( error ) { message . error ( getErrorMessage ( error ) ) ; }
} ;
const openQrTask = async ( task : HuyaTaskItem ) = > {
setQrTask ( task ) ;
try { setQrTask ( await huyaApi . getTask ( task . id ) ) ; } catch { /* 使用列表结果 */ }
} ;
const filteredAccounts = useMemo ( ( ) = > {
const value = search . trim ( ) . toLowerCase ( ) ;
return accounts . filter ( ( item ) = > ! value || [ item . uid , item . nickname , item . username , item . tag , item . game_name ] . some ( ( v ) = > String ( v || '' ) . toLowerCase ( ) . includes ( value ) ) ) ;
} , [ accounts , search ] ) ;
const filteredGoods = useMemo ( ( ) = > goods . map ( ( item ) = > ( { . . . item , label : ` ${ item . name } / ${ item . price ? ? '-' } 分 / ${ item . remain_text || '动态库存' } ` } ) ) , [ goods ] ) ;
const exchangeCount = tasks . filter ( ( task ) = > task . task_type === 'exchange_goods' ) . length ;
const accountColumns : TableProps < HuyaAccountItem > [ 'columns' ] = [
{ title : '#' , width : 45 , render : ( _value , _row , index ) = > < Text type = "secondary" > { index + 1 } < / Text > } ,
{ title : '账号' , width : 190 , render : ( _value , row ) = > < Space direction = "vertical" size = { 0 } > < Text strong ellipsis > { row . nickname || row . username || row . uid } < / Text > < Text type = "secondary" style = { { fontSize : 12 } } > UID { row . uid || '-' } < / Text > < / Space > } ,
{ title : '游戏名' , width : 180 , render : ( _value , row ) = > row . game_name || row . game_channel || < Text type = "secondary" > 未 查 询 < / Text > } ,
{ title : '积分' , dataIndex : 'points' , width : 80 , render : ( value : number | null ) = > value ? ? < Text type = "secondary" > - < / Text > } ,
{ title : '最近操作' , ellipsis : true , render : ( _value , row ) = > { const task = latestTask ( tasks , row . id ) ; return task ? < Space direction = "vertical" size = { 0 } > < Text > { TASK_LABELS [ task . task_type ] || task . task_type } < / Text > < Text type = "secondary" ellipsis > { task . message || '-' } < / Text > < / Space > : < Text type = "secondary" > 暂 无 < / Text > ; } } ,
{ title : '状态' , width : 90 , render : ( _value , row ) = > { const task = latestTask ( tasks , row . id ) ; const status = task ? . status || row . status || '' ; return < Tag color = { TASK_COLORS [ status ] || 'default' } > { TASK_STATUS_LABELS [ status ] || status || '待操作' } < / Tag > ; } } ,
] ;
const operationBody = (
< Space direction = "vertical" size = { 10 } style = { { width : '100%' } } >
< div className = "huya-operation-section" > < div className = "huya-operation-title" > < QrcodeOutlined / > < span > 绑 定 与 查 询 < / span > < / div > < div className = "huya-action-grid" >
< Button size = "small" icon = { < QrcodeOutlined / > } onClick = { ( ) = > void startTask ( 'get_bind_qr' ) } disabled = { starting || ! selectedIds . length } > 绑 定 二 维 码 < / Button > < Button size = "small" icon = { < SearchOutlined / > } onClick = { ( ) = > void startTask ( 'query_game_name' ) } disabled = { starting || ! selectedIds . length } > 查 询 角 色 < / Button > < Button size = "small" icon = { < CheckCircleOutlined / > } onClick = { ( ) = > void startTask ( 'confirm_bind' ) } disabled = { starting || ! selectedIds . length } > 确 认 绑 定 < / Button > < Button size = "small" icon = { < SearchOutlined / > } onClick = { ( ) = > void startTask ( 'query_points' ) } disabled = { starting || ! selectedIds . length } > 刷 新 积 分 < / Button > < Button size = "small" onClick = { ( ) = > void startTask ( 'query_act_tasks' ) } disabled = { starting || ! selectedIds . length } > 宝 典 任 务 < / Button > < Button size = "small" onClick = { ( ) = > void startTask ( 'query_exchange_records' ) } disabled = { starting || ! selectedIds . length } > 兑 换 记 录 < / Button >
< / div > < / div >
< div className = "huya-operation-section" > < div className = "huya-operation-title" > < ShoppingOutlined / > < span > 兑 换 < / span > < / div > < Select size = "small" showSearch optionFilterProp = "label" value = { selectedGoodsId || undefined } onChange = { setSelectedGoodsId } options = { filteredGoods . map ( ( item ) = > ( { value : item.product_id , label : item.label } ) ) } placeholder = "选择兑换商品" style = { { width : '100%' } } / > < div className = "huya-action-grid" > < Button size = "small" icon = { < ReloadOutlined / > } onClick = { ( ) = > void startTask ( 'refresh_goods' ) } disabled = { starting || ! selectedIds . length } > 刷 新 商 品 < / Button > < Button size = "small" type = "primary" icon = { < ShoppingOutlined / > } onClick = { ( ) = > void startTask ( 'exchange_goods' ) } disabled = { starting || ! selectedIds . length || ! selectedGoodsId } > 兑 换 商 品 < / Button > < / div > < / div >
< div className = "huya-operation-section" > < div className = "huya-operation-title" > < GiftOutlined / > < span > 开 通 宝 典 < / span > < / div > < Button block size = "small" icon = { < QrcodeOutlined / > } onClick = { ( ) = > void startTask ( 'create_recharge_order' ) } disabled = { starting || ! selectedIds . length } > 生 成 开 通 支 付 码 < / Button > < / div >
< RealtimeLogPanel logs = { logs . logs } connected = { logs . connected } title = "实时日志" emptyText = "暂无任务日志" height = { 150 } collapsible defaultVisible = { false } / >
< / Space >
) ;
const accountsBody = < > < Space style = { { marginBottom : 8 , width : '100%' , justifyContent : 'space-between' } } wrap > < Input size = "small" allowClear prefix = { < SearchOutlined / > } placeholder = "搜索账号/昵称/游戏名" value = { search } onChange = { ( event ) = > setSearch ( event . target . value ) } style = { { width : 240 } } / > < Space > < Button size = "small" icon = { < ImportOutlined / > } onClick = { ( ) = > setImportOpen ( true ) } > 导 入 账 号 < / Button > { selectedIds . length > 0 && < Button size = "small" danger onClick = { ( ) = > void saveAccounts ( accounts . map ( ( item ) = > item . id ) . filter ( ( id ) = > ! selectedIds . includes ( id ) ) ) } > 移 出 选 中 < / Button > } < / Space > < / Space > < div className = "huya-account-table" > < Table rowKey = "id" size = "small" loading = { loading } rowSelection = { { selectedRowKeys : selectedIds , onChange : ( keys ) = > setSelectedIds ( keys . map ( Number ) ) } } columns = { accountColumns } dataSource = { filteredAccounts } pagination = { { pageSize : 20 , showSizeChanger : true , showTotal : ( total ) = > ` 共 ${ total } 条 ` } } scroll = { { x : 760 , y : 'calc(100vh - 300px)' } } onRow = { ( row ) = > ( { onClick : ( ) = > { const task = latestTask ( tasks , row . id ) ; if ( task ? . task_type === 'get_bind_qr' ) void openQrTask ( task ) ; } } ) } / > < / div > < / > ;
const configField = ( key : Exclude < keyof HuyaConfig , 'updated_at' > , label : string ) = > configDraft && < Space key = { key } style = { { width : '100%' , justifyContent : 'space-between' } } > < Text > { label } < / Text > < Input value = { configDraft [ key ] || '' } onChange = { ( event ) = > setConfigDraft ( { . . . configDraft , [ key ] : event . target . value } ) } style = { { width : 300 } } / > < / Space > ;
return < div style = { { height : '100%' , display : 'flex' , flexDirection : 'column' , overflow : 'hidden' } } > < style > { ` .huya-operation-section{border:1px solid rgba(128,128,128,.22);border-radius:6px;padding:8px}.huya-operation-title{display:flex;gap:6px;align-items:center;font-weight:600;margin-bottom:8px}.huya-action-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px}.huya-account-table{flex:1;min-height:0;overflow:hidden}.huya-account-table .ant-table-wrapper,.huya-account-table .ant-spin-nested-loading,.huya-account-table .ant-spin-container{height:100%} ` } < / style >
< Space style = { { justifyContent : 'space-between' , width : '100%' , marginBottom : 8 , flexShrink : 0 } } > < div > < h2 style = { { margin : 0 } } > 虎 牙 精 英 宝 典 工 作 台 < / h2 > < Text type = "secondary" > 绑 定 、 开 通 、 积 分 与 兑 换 任 务 < / Text > < / div > < Space > < Tooltip title = { layoutMode === 'split' ? '切换为上下布局' : '切换为左右布局' } > < Button icon = { < ColumnWidthOutlined / > } onClick = { ( ) = > { const next = layoutMode === 'split' ? 'stack' : 'split' ; setLayoutMode ( next ) ; localStorage . setItem ( LAYOUT_KEY , next ) ; } } / > < / Tooltip > < Button icon = { < ReloadOutlined / > } onClick = { ( ) = > void load ( ) } loading = { loading } > 刷 新 < / Button > { canConfig && < Button icon = { < SettingOutlined / > } onClick = { ( ) = > void openSettings ( ) } > 设 置 < / Button > } { ! ! activeBatches . length && < Button danger icon = { < StopOutlined / > } onClick = { ( ) = > void stop ( ) } > 停 止 < / Button > } < / Space > < / Space >
{ layoutMode === 'split' ? < div style = { { flex : 1 , minHeight : 0 , display : 'flex' , gap : 12 , overflow : 'hidden' } } > < Card size = "small" title = "账号" extra = { < Tag color = "blue" > 已 选 { selectedIds . length } / { accounts . length } < / Tag > } style = { { flex : 1 , minWidth : 0 , minHeight : 0 , display : 'flex' , flexDirection : 'column' , overflow : 'hidden' } } styles = { { body : { flex : 1 , minHeight : 0 , padding : '4px 6px' , overflow : 'hidden' , display : 'flex' , flexDirection : 'column' } } } > { accountsBody } < / Card > < Card size = "small" title = "精英宝典操作" extra = { < Space size = { 4 } > < Text type = "secondary" > 并 发 < / Text > < InputNumber size = "small" min = { 1 } max = { 10 } value = { concurrency } onChange = { ( value ) = > setConcurrency ( value || 1 ) } style = { { width : 58 } } / > < / Space > } style = { { width : 360 , flexShrink : 0 , minHeight : 0 , overflow : 'hidden' } } styles = { { body : { padding : 8 , overflowY : 'auto' , height : 'calc(100% - 38px)' } } } > { operationBody } < / Card > < / div > : < > < Card size = "small" title = "精英宝典操作" extra = { < Space size = { 4 } > < Text type = "secondary" > 并 发 < / Text > < InputNumber size = "small" min = { 1 } max = { 10 } value = { concurrency } onChange = { ( value ) = > setConcurrency ( value || 1 ) } style = { { width : 58 } } / > < / Space > } style = { { flexShrink : 0 , marginBottom : 12 } } styles = { { body : { padding : 8 } } } > { operationBody } < / Card > < Card size = "small" title = "账号" extra = { < Tag color = "blue" > 已 选 { selectedIds . length } / { accounts . length } < / Tag > } style = { { flex : 1 , minHeight : 0 , display : 'flex' , flexDirection : 'column' , overflow : 'hidden' } } styles = { { body : { flex : 1 , minHeight : 0 , padding : '4px 6px' , overflow : 'hidden' , display : 'flex' , flexDirection : 'column' } } } > { accountsBody } < / Card > < / > }
< Text type = "secondary" style = { { fontSize : 12 , marginTop : 6 } } > 兑 换 任 务 { exchangeCount } 条 ; 勾 选 账 号 后 可 批 量 执 行 。 < / Text >
< Modal open = { qrTask !== null } title = "绑定二维码" footer = { null } onCancel = { ( ) = > setQrTask ( null ) } centered > { qrTask && < div style = { { textAlign : 'center' } } > { resultText ( qrTask , 'mini_qrcode_image' ) ? < img src = { ` data:image/png;base64, ${ resultText ( qrTask , 'mini_qrcode_image' ) } ` } alt = "绑定二维码" style = { { width : 240 , height : 240 } } / > : < QRCode value = { resultText ( qrTask , 'bind_redirect_url' ) || 'https://zt.huya.com/b02faae1/pc/index.html' } size = { 240 } / > } < p > < Tag color = { TASK_COLORS [ qrTask . status ] || 'default' } > { qrTask . message || qrTask . status } < / Tag > < / p > < / div > } < / Modal >
< Modal open = { importOpen } title = "导入精英宝典账号" onCancel = { ( ) = > setImportOpen ( false ) } onOk = { ( ) = > { void saveAccounts ( [ . . . new Set ( [ . . . accounts . map ( ( item ) = > item . id ) , . . . importSelected ] ) ] ) . then ( ( ) = > { setImportSelected ( [ ] ) ; setImportOpen ( false ) ; } ) . catch ( ( error ) = > message . error ( getErrorMessage ( error ) ) ) ; } } okText = "导入" cancelText = "取消" > < Input.Search allowClear placeholder = "搜索账号" value = { importSearch } onChange = { ( event ) = > setImportSearch ( event . target . value ) } style = { { marginBottom : 8 } } / > < Select allowClear placeholder = "标签" value = { importTag || undefined } onChange = { ( value ) = > setImportTag ( value || '' ) } options = { importTags . map ( ( tag ) = > ( { value : tag , label : tag } ) ) } style = { { width : '100%' , marginBottom : 8 } } / > < Table rowKey = "id" size = "small" dataSource = { pool . filter ( ( item ) = > ! accounts . some ( ( current ) = > current . id === item . id ) && ( ! importSearch || [ item . uid , item . nickname , item . username ] . some ( ( value ) = > String ( value || '' ) . includes ( importSearch ) ) ) && ( ! importTag || item . tag === importTag ) ) } columns = { [ { title : '账号' , render : ( _value , row ) = > row . nickname || row . username || row . uid } , { title : '标签' , dataIndex : 'tag' } ] } pagination = { { pageSize : 8 } } rowSelection = { { selectedRowKeys : importSelected , onChange : ( keys ) = > setImportSelected ( keys . map ( Number ) ) } } / > < / Modal >
< Modal open = { configOpen } title = "精英宝典设置" onCancel = { ( ) = > setConfigOpen ( false ) } onOk = { ( ) = > void saveConfig ( ) } confirmLoading = { configSaving } okText = "保存" cancelText = "取消" width = { 500 } > { configLoading ? < div style = { { padding : 24 , textAlign : 'center' } } > 加 载 中 . . . < / div > : configDraft && < Space direction = "vertical" size = { 10 } style = { { width : '100%' } } > { configField ( 'sid' , '活动 SID' ) } { configField ( 'bind_act_id' , '绑定活动 ID' ) } { configField ( 'outer_act_id' , '外部活动 ID' ) } { configField ( 'room_pid' , '直播间 PID' ) } { configField ( 'pay_channel' , '支付渠道' ) } < Space style = { { width : '100%' , justifyContent : 'space-between' } } > < Text > 空 闲 轮 询 间 隔 ( 秒 ) < / Text > < InputNumber min = { 5 } max = { 60 } value = { refreshInterval } onChange = { ( value ) = > { const next = Math . min ( 60 , Math . max ( 5 , value || 15 ) ) ; setRefreshInterval ( next ) ; localStorage . setItem ( 'huya_elite_refresh_interval' , String ( next ) ) ; } } style = { { width : 300 } } / > < / Space > < / Space > } < / Modal >
< / div > ;
}