完善虎牙外部单展示、状态恢复与礼包配置

This commit is contained in:
yml2213
2026-11-11 12:40:06 +08:00
parent 3a48d9c624
commit b18e46d72d
18 changed files with 1216 additions and 120 deletions
+17 -1
View File
@@ -1187,6 +1187,16 @@ export interface HuyaConfig {
bind_gid: string;
bind_act_id: string;
pay_channel: string;
/** 积分中心 queryAllocation 页面码 */
points_page_code: string;
/** 积分中心 sourceId */
points_source_id: string;
/** 积分中心 scene,默认 7 */
points_scene: string;
/** 可选覆盖:页签 pageId;留空自动从响应发现 */
points_page_id: string;
/** 可选覆盖:页签 ID,逗号分隔;留空自动拉全部页签 */
points_tab_ids: string;
updated_at: string | null;
}
@@ -1471,7 +1481,7 @@ export interface HuyaCollabOrder {
uid: string;
yyuid: string;
hy_username: string;
/** 虎牙号昵称(探针给不出,暂时为空) */
/** 虎牙平台账号昵称(绑定探针通过后由用户资料接口补读) */
nickname: string;
has_cookie: boolean;
has_raw_cookie: boolean;
@@ -1662,6 +1672,8 @@ export interface HuyaCollabStatusBatchRequest {
reason?: string;
/** 目标不是「向前」时必须显式 true */
rollback?: boolean;
/** 仅超级管理员:批次里的 void 行强制恢复,必须填写 reason */
force?: boolean;
}
/** 批量改状态里的一行结论 */
@@ -1861,6 +1873,8 @@ export interface HuyaCollabStatusRequest {
status: HuyaCollabStatus;
reason?: string;
rollback?: boolean;
/** 仅超级管理员:从「已作废」强制恢复,必须填写 reason */
force?: boolean;
expected_version?: number;
}
@@ -1954,6 +1968,8 @@ export interface HuyaCollabCkPreview {
uid: string;
yyuid: string;
hy_username: string;
/** 虎牙平台账号昵称(CK 验证时补读;不是绑定游戏账号昵称) */
nickname: string;
ck_status: HuyaCollabCkStatus;
bind_game_account: string;
bind_game_role: string;
+231 -52
View File
@@ -17,7 +17,7 @@ import type { TableProps } from 'antd';
import {
AuditOutlined, CheckCircleOutlined, CloudUploadOutlined, CopyOutlined, DeleteOutlined, EditOutlined,
FileAddOutlined, FileExcelOutlined, GiftOutlined, InboxOutlined, LinkOutlined, PlusOutlined, ReloadOutlined,
SafetyCertificateOutlined, ShopOutlined, StopOutlined, SwapOutlined, SyncOutlined, UndoOutlined,
SafetyCertificateOutlined, SettingOutlined, ShopOutlined, StopOutlined, SwapOutlined, SyncOutlined, UndoOutlined,
} from '@ant-design/icons';
import {
fileApi,
@@ -38,6 +38,7 @@ import {
type HuyaCollabShop,
type HuyaCollabStatus,
type HuyaCollabSummary,
type HuyaConfig,
} from '../api/modules';
import HuyaCollabShopModal from '../components/HuyaCollabShopModal';
import HuyaCollabShopPicker from '../components/HuyaCollabShopPicker';
@@ -165,8 +166,8 @@ const SLOT_META: Record<HuyaCollabImageSlot, { label: string; keysKey: 'bind_ima
const IMPORT_PREVIEW_ROWS = 20;
// 「礼包列表」的本地缓存键:一个活动期间就那一批礼包,没必要每贴一份 CK 都去虎牙拉一次;
// v2 = 2026-10-19 切到积分中心 queryAllocation 后换键,避免继续用旧 getActPrizeList 的缓存。
const GIFTS_CACHE_KEY = 'huya.collab.gifts.v2';
// v3 = 2026-11-11 起自动合并积分中心全部页签,换键避免继续用只含「精英专享」的旧缓存。
const GIFTS_CACHE_KEY = 'huya.collab.gifts.v3';
const RECHARGE_THRESHOLD_KEY = 'huya.collab.rechargeThreshold';
const RECHARGE_THRESHOLD_DEFAULT = 100;
@@ -504,6 +505,7 @@ export default function HuyaCollabPage() {
const navigate = useNavigate();
const canEdit = can('huya:collab:edit');
const canManage = can('huya:collab:manage');
const isAdmin = user?.role === 'super_admin';
// 单行复制与批量导出共用这个权限点(默认运营和客服都有)
const canExportCk = can('huya:collab:ck_export');
@@ -522,6 +524,10 @@ export default function HuyaCollabPage() {
const [frequentGifts, setFrequentGifts] = useState<HuyaCollabFrequentGift[]>([]);
const [shopModalOpen, setShopModalOpen] = useState(false);
const [giftModalOpen, setGiftModalOpen] = useState(false);
const [pointsConfigOpen, setPointsConfigOpen] = useState(false);
const [pointsConfigLoading, setPointsConfigLoading] = useState(false);
const [pointsConfigSaving, setPointsConfigSaving] = useState(false);
const [pointsConfigForm] = Form.useForm<HuyaConfig>();
const [filters, setFilters] = useState<HuyaCollabOrderQuery>({
include_void: false,
archive_scope: 'active',
@@ -532,6 +538,8 @@ export default function HuyaCollabPage() {
const [realtimePending, setRealtimePending] = useState(false);
const [realtimePaused, setRealtimePaused] = useState(readRealtimePaused);
const [selectedIds, setSelectedIds] = useState<number[]>([]);
const selectedHasVoid = selectedIds.length > 0
&& rows.some((row) => selectedIds.includes(row.id) && row.status === 'void');
// 点顶部统计卡筛选后滚到列表,避免用户还要自己往下找
const listAnchorRef = useRef<HTMLDivElement | null>(null);
@@ -581,7 +589,7 @@ export default function HuyaCollabPage() {
const [ckTarget, setCkTarget] = useState<{ order: HuyaCollabOrder; text: string } | null>(null);
// 单行状态弹窗:set=人工往前切(可跳步)/ rollback=回退 / void=作废
const [statusAction, setStatusAction] = useState<{
order: HuyaCollabOrder; mode: 'void' | 'rollback' | 'set'; target?: HuyaCollabStatus;
order: HuyaCollabOrder; mode: 'void' | 'rollback' | 'set' | 'force'; target?: HuyaCollabStatus;
} | null>(null);
const [statusReason, setStatusReason] = useState('');
const [rollbackTarget, setRollbackTarget] = useState<HuyaCollabStatus>('to_recharge');
@@ -590,6 +598,7 @@ export default function HuyaCollabPage() {
const [batchStatusTarget, setBatchStatusTarget] = useState<HuyaCollabStatus>('to_recharge');
const [batchStatusReason, setBatchStatusReason] = useState('');
const [batchStatusRollback, setBatchStatusRollback] = useState(false);
const [batchStatusForce, setBatchStatusForce] = useState(false);
// 充值核账弹窗:按「绑定账号 + 绑定角色」整组核账,阈值默认 100
const [rechargeOpen, setRechargeOpen] = useState(false);
const [rechargeIds, setRechargeIds] = useState<number[]>([]);
@@ -661,11 +670,43 @@ export default function HuyaCollabPage() {
useEffect(() => { void load(); }, [load]);
const openPointsConfig = async () => {
setPointsConfigOpen(true);
setPointsConfigLoading(true);
try {
pointsConfigForm.setFieldsValue(await huyaApi.getConfig());
} catch (e: unknown) {
message.error(getErrorMessage(e));
} finally {
setPointsConfigLoading(false);
}
};
const savePointsConfig = async () => {
setPointsConfigSaving(true);
try {
const values = await pointsConfigForm.validateFields();
await huyaApi.updateConfig({
points_page_code: values.points_page_code,
points_source_id: values.points_source_id,
points_scene: values.points_scene,
points_page_id: values.points_page_id,
points_tab_ids: values.points_tab_ids,
});
message.success('积分中心配置已保存,下次刷新礼包时生效');
setPointsConfigOpen(false);
} catch (e: unknown) {
message.error(getErrorMessage(e));
} finally {
setPointsConfigSaving(false);
}
};
const realtimeEnabled = can('huya:collab:view');
// 暂停后连 WebSocket 也不建:既不自动重拉,也不做断线兜底轮询。
const realtimeSyncEnabled = realtimeEnabled && !realtimePaused;
const collabModalOpen = Boolean(
createOpen || supplementTarget || detail || statusAction || batchStatusOpen || rechargeOpen || archiveOpen || ckTarget || ckExportOpen || shopModalOpen || giftModalOpen,
createOpen || supplementTarget || detail || statusAction || batchStatusOpen || rechargeOpen || archiveOpen || ckTarget || ckExportOpen || shopModalOpen || giftModalOpen || pointsConfigOpen,
);
const toggleRealtimeSync = (nextEnabled: boolean) => {
@@ -746,7 +787,7 @@ export default function HuyaCollabPage() {
const emptyPreview = useCallback(
(message: string): HuyaCollabCkPreview => ({
ok: false, message, uid: '', yyuid: '', hy_username: '', ck_status: 'invalid',
ok: false, message, uid: '', yyuid: '', hy_username: '', nickname: '', ck_status: 'invalid',
bind_game_account: '', bind_game_role: '', bind_game_channel: '',
has_role: false, gifts: [], gifts_message: '', uid_used_by: [],
}),
@@ -768,7 +809,9 @@ export default function HuyaCollabPage() {
writeGiftCache(preview.gifts);
}
if (preview.ok && preview.has_role) {
message.success(`CK 有效:绑定角色 ${preview.bind_game_role}`);
message.success(
`CK 有效:${preview.nickname || '虎牙昵称未读到'} · 角色 ${preview.bind_game_role}`,
);
// 「记住的礼包」如果不在实时列表、也不在标准字典里就清掉重选。
const options = preview.gifts.length > 0 ? preview.gifts : giftsRef.current;
const available = new Set([
@@ -962,13 +1005,18 @@ export default function HuyaCollabPage() {
openRecharge(selectedIds);
return;
}
if (batchStatusForce && !batchStatusReason.trim()) {
message.warning('强制恢复已作废订单必须填写原因');
return;
}
setBusy(true);
try {
const result = await huyaApi.batchUpdateCollabOrderStatus({
ids: selectedIds,
status: batchStatusTarget,
reason: batchStatusReason,
rollback: batchStatusRollback,
rollback: batchStatusForce ? false : batchStatusRollback,
force: batchStatusForce,
});
const failures = result.results.filter((item) => !item.ok);
if (failures.length === 0) {
@@ -1320,6 +1368,7 @@ export default function HuyaCollabPage() {
* - `void`:作废(必须填原因);
* - `rollback`:回退到选定状态(`rollback=true`,会清掉后面阶段的时间戳);
* - `set`:人工往前切,允许跳步(如 待补资料 → 已上脚本),不用勾回退。
* - `force`:超级管理员从「已作废」强制恢复(必须填原因)。
*/
const submitStatusAction = async () => {
if (!statusAction) return;
@@ -1336,17 +1385,24 @@ export default function HuyaCollabPage() {
message.warning('作废必须填原因');
return;
}
if (mode === 'force' && !statusReason.trim()) {
message.warning('强制恢复已作废订单必须填写原因');
return;
}
setBusy(true);
try {
applyUpdated(await huyaApi.updateCollabOrderStatus(order.id, {
status: nextStatus,
reason: statusReason,
rollback: mode === 'rollback',
force: mode === 'force',
expected_version: order.version,
}));
message.success(
mode === 'void'
? '已作废'
: mode === 'force'
? '已强制恢复'
: mode === 'rollback'
? '已回退'
: `已改为「${STATUS_META[nextStatus].label}」`,
@@ -2043,14 +2099,29 @@ export default function HuyaCollabPage() {
),
},
{
title: '绑定账号 / 角色', width: 210,
title: '游戏账号 / 角色 / 虎牙昵称', width: 280,
...sortProps('bind'),
render: (_, row) => (
<Space direction="vertical" size={0}>
<Text strong>{row.bind_game_account || <Text type="secondary">未读到</Text>}</Text>
{row.bind_game_role
? <Tag color="green" style={{ marginTop: 2 }}>角色 {row.bind_game_role}</Tag>
: <Text type="secondary" style={{ fontSize: 12 }}>未读到绑定角色</Text>}
<Space direction="vertical" size={1} style={{ width: '100%' }}>
<Space size={4} wrap={false} style={{ maxWidth: '100%' }}>
<Text strong ellipsis style={{ maxWidth: 112 }}>
{row.bind_game_account || '游戏账号未读到'}
</Text>
<Text type="secondary">/</Text>
<Text
type={row.bind_game_role ? undefined : 'secondary'}
ellipsis
style={{
color: row.bind_game_role ? '#389e0d' : undefined,
maxWidth: 140,
}}
>
{row.bind_game_role ? `角色 ${row.bind_game_role}` : '角色未读到'}
</Text>
</Space>
<Text type="secondary" style={{ fontSize: 12 }} ellipsis>
虎牙昵称:{row.nickname || '未读取(重新验证 CK 后补全)'}
</Text>
{row.bind_game_channel && (
<Text type="secondary" style={{ fontSize: 12 }}>{row.bind_game_channel}</Text>
)}
@@ -2293,9 +2364,15 @@ export default function HuyaCollabPage() {
{ key: 'rebuild', label: '从 App 原始凭据重建', icon: <ReloadOutlined />, disabled: !row.has_raw_cookie },
{ key: 'disable', label: row.ck_status === 'disabled' ? '恢复这份 CK' : '停用这份 CK', icon: <StopOutlined /> },
{ type: 'divider' },
{ key: 'set', label: '改状态…', icon: <CheckCircleOutlined /> },
{ key: 'rollback', label: '回退状态…', icon: <UndoOutlined /> },
{ key: 'void', label: '作废…', icon: <StopOutlined />, danger: true },
...(row.status === 'void'
? (isAdmin
? [{ key: 'force', label: '管理员强制恢复…', icon: <UndoOutlined />, danger: true }]
: [])
: [
{ key: 'set', label: '改状态…', icon: <CheckCircleOutlined /> },
{ key: 'rollback', label: '回退状态…', icon: <UndoOutlined /> },
{ key: 'void', label: '作废…', icon: <StopOutlined />, danger: true },
]),
...(row.status === 'done'
? [
{ type: 'divider' as const },
@@ -2323,6 +2400,11 @@ export default function HuyaCollabPage() {
setStatusReason('');
setStatusAction({ order: row, mode: 'rollback' });
}
if (key === 'force') {
setRollbackTarget('to_recharge');
setStatusReason('');
setStatusAction({ order: row, mode: 'force' });
}
if (key === 'void') {
setStatusReason('');
setStatusAction({ order: row, mode: 'void' });
@@ -2463,7 +2545,7 @@ export default function HuyaCollabPage() {
value={filters.gift_name || undefined}
onChange={(value?: string) => { setPage(1); setFilters((prev) => ({ ...prev, gift_name: value })); }} />
<Input.Search allowClear style={{ width: 340 }}
placeholder="搜订单号 / 店铺 / 绑定账号 / 角色 / 虎牙号 / 礼包 / 备注"
placeholder="搜订单号 / 店铺 / 游戏账号 / 角色 / 虎牙昵称 / 礼包 / 备注"
onSearch={(value) => { setPage(1); setFilters((prev) => ({ ...prev, keyword: value })); }} />
<Space size={4}>
<Text type="secondary">显示已作废</Text>
@@ -2526,6 +2608,7 @@ export default function HuyaCollabPage() {
setBatchStatusTarget(key as HuyaCollabStatus);
setBatchStatusReason('');
setBatchStatusRollback(false);
setBatchStatusForce(false);
setBatchStatusOpen(true);
},
}}
@@ -2579,6 +2662,11 @@ export default function HuyaCollabPage() {
{canManage && !isArchiveView && (
<Button icon={<GiftOutlined />} onClick={() => setGiftModalOpen(true)}>礼包管理</Button>
)}
{canManage && !isArchiveView && (
<Button icon={<SettingOutlined />} onClick={() => void openPointsConfig()}>
积分中心配置
</Button>
)}
{canEdit && !isArchiveView && (
<Button
type="primary"
@@ -2723,7 +2811,7 @@ export default function HuyaCollabPage() {
<Descriptions size="small" column={2} bordered
items={[
{ key: 'uid', label: '虎牙号', children: detail.uid || '-' },
{ key: 'username', label: '登录名 / 昵称', children: `${detail.hy_username || '-'} / ${detail.nickname || '-'}` },
{ key: 'username', label: '登录名 / 虎牙昵称', children: `${detail.hy_username || '-'} / ${detail.nickname || '-'}` },
{ key: 'ck', label: 'CK 状态',
children: (
<Space>
@@ -2906,30 +2994,43 @@ export default function HuyaCollabPage() {
{canEdit && !detail.is_archived && (
<Space wrap>
{detail.next_status && (
<Button icon={<CheckCircleOutlined />} disabled={busy}
onClick={() => void advance(detail)}>
标记为「{STATUS_META[detail.next_status].label}」
</Button>
)}
<Button icon={<SwapOutlined />} disabled={busy}
onClick={() => {
// 默认给「下一步」,终点态(抢购成功 / 已作废 / 抢购失败)退回当前状态,让人自己挑
setRollbackTarget(detail.next_status ?? detail.status);
setStatusReason('');
setStatusAction({ order: detail, mode: 'set' });
}}>
改状态…
</Button>
<Button icon={<UndoOutlined />} disabled={busy}
onClick={() => { setRollbackTarget('to_recharge'); setStatusReason(''); setStatusAction({ order: detail, mode: 'rollback' }); }}>
回退状态
</Button>
{detail.status !== 'void' && (
<Button danger icon={<StopOutlined />} disabled={busy}
onClick={() => { setStatusReason(''); setStatusAction({ order: detail, mode: 'void' }); }}>
作废
</Button>
{detail.status === 'void' ? (
isAdmin && (
<Button danger icon={<UndoOutlined />} disabled={busy}
onClick={() => {
setRollbackTarget('to_recharge');
setStatusReason('');
setStatusAction({ order: detail, mode: 'force' });
}}>
管理员强制恢复…
</Button>
)
) : (
<>
{detail.next_status && (
<Button icon={<CheckCircleOutlined />} disabled={busy}
onClick={() => void advance(detail)}>
标记为「{STATUS_META[detail.next_status].label}」
</Button>
)}
<Button icon={<SwapOutlined />} disabled={busy}
onClick={() => {
// 默认给「下一步」,终点态退回当前状态,让人自己挑
setRollbackTarget(detail.next_status ?? detail.status);
setStatusReason('');
setStatusAction({ order: detail, mode: 'set' });
}}>
改状态…
</Button>
<Button icon={<UndoOutlined />} disabled={busy}
onClick={() => { setRollbackTarget('to_recharge'); setStatusReason(''); setStatusAction({ order: detail, mode: 'rollback' }); }}>
回退状态
</Button>
<Button danger icon={<StopOutlined />} disabled={busy}
onClick={() => { setStatusReason(''); setStatusAction({ order: detail, mode: 'void' }); }}>
作废
</Button>
</>
)}
</Space>
)}
@@ -3126,13 +3227,14 @@ export default function HuyaCollabPage() {
<Alert
type="success"
showIcon
message={`CK 有效 · 绑定角色 ${ckPreview.bind_game_role}`}
message={`CK 有效 · ${ckPreview.nickname || '虎牙昵称未读到'} · 角色 ${ckPreview.bind_game_role}`}
description={
<>
<div>
{`订单编号里的账号:${ckPreview.bind_game_account || '-'}`
{`游戏账号:${ckPreview.bind_game_account || '-'}`
+ `(${ckPreview.bind_game_channel || '-'})`
+ `;虎牙号 ${ckPreview.uid}`
+ `;虎牙昵称:${ckPreview.nickname || '-'}`
+ `;虎牙号:${ckPreview.uid}`
+ (giftSourceText ? `;${giftSourceText}` : '')}
</div>
{ckPreview.uid_used_by.length > 0 && (
@@ -3482,18 +3584,32 @@ export default function HuyaCollabPage() {
<Modal
title={statusAction?.mode === 'void'
? `作废订单 ${statusAction.order.order_no}`
: statusAction?.mode === 'set'
? `改状态:订单 ${statusAction.order.order_no}`
: `回退订单 ${statusAction?.order.order_no ?? ''}`}
: statusAction?.mode === 'force'
? `管理员强制恢复订单 ${statusAction.order.order_no}`
: statusAction?.mode === 'set'
? `改状态:订单 ${statusAction.order.order_no}`
: `回退订单 ${statusAction?.order.order_no ?? ''}`}
open={!!statusAction}
onCancel={() => setStatusAction(null)}
onOk={() => void submitStatusAction()}
confirmLoading={busy}
okButtonProps={{ danger: statusAction?.mode === 'void' }}
okText={statusAction?.mode === 'void' ? '作废' : statusAction?.mode === 'set' ? '改状态' : '回退'}
okButtonProps={{ danger: statusAction?.mode === 'void' || statusAction?.mode === 'force' }}
okText={statusAction?.mode === 'void'
? '作废'
: statusAction?.mode === 'force'
? '强制恢复'
: statusAction?.mode === 'set' ? '改状态' : '回退'}
destroyOnHidden
>
<Space direction="vertical" size={8} style={{ width: '100%' }}>
{statusAction?.mode === 'force' && (
<Alert
type="warning"
showIcon
message="仅在确认属于误作废时使用"
description="强制恢复只跳过「已作废不可逆」限制;资料完整性、待充值条件等校验仍会执行。操作人和原因都会写入审计。"
/>
)}
{statusAction && statusAction.mode !== 'void' && (
<Select
style={{ width: '100%' }}
@@ -3506,6 +3622,8 @@ export default function HuyaCollabPage() {
placeholder={
statusAction?.mode === 'void'
? '作废原因(必填)'
: statusAction?.mode === 'force'
? '强制恢复原因(必填)'
: rollbackTarget === 'void'
? '作废原因(必填)'
: statusAction?.mode === 'rollback'
@@ -3521,6 +3639,11 @@ export default function HuyaCollabPage() {
{statusAction?.mode === 'rollback' && (
<Text type="secondary">回退会清掉后面阶段的时间戳,并在审计里留痕。</Text>
)}
{statusAction?.mode === 'force' && (
<Text type="secondary">
恢复后原「作废原因」会保留在审计时间线里;该操作仅限超级管理员。
</Text>
)}
</Space>
</Modal>
@@ -3574,10 +3697,17 @@ export default function HuyaCollabPage() {
/>
<Input.TextArea rows={2} value={batchStatusReason}
onChange={(event) => setBatchStatusReason(event.target.value)}
placeholder={batchStatusTarget === 'void' ? '作废原因(必填)' : '改状态说明(可选)'} />
placeholder={batchStatusTarget === 'void'
? '作废原因(必填)'
: batchStatusForce ? '强制恢复原因(必填)' : '改状态说明(可选)'} />
<Checkbox checked={batchStatusRollback} onChange={(event) => setBatchStatusRollback(event.target.checked)}>
回退(目标不是「往前」时必须勾选,比如 已上脚本 → 待充值)
</Checkbox>
{isAdmin && selectedHasVoid && (
<Checkbox checked={batchStatusForce} onChange={(event) => setBatchStatusForce(event.target.checked)}>
强制恢复所选批次中的「已作废」订单(仅超级管理员,必须填写原因)
</Checkbox>
)}
<Text type="secondary">
逐行独立提交:某一行前置校验没过(例如资料不全进不了「待充值」)只跳过那一行,
结果会在弹窗里逐条列出来。一次最多 50 行。
@@ -3637,6 +3767,55 @@ export default function HuyaCollabPage() {
</Space>
</Modal>
<Modal
title="积分中心配置"
open={pointsConfigOpen}
onCancel={() => setPointsConfigOpen(false)}
onOk={() => void savePointsConfig()}
confirmLoading={pointsConfigSaving}
okText="保存"
width={680}
destroyOnHidden
>
<Space direction="vertical" size={10} style={{ width: '100%' }}>
<Alert
type="info"
showIcon
message="页签默认自动发现,通常只需在活动入口变化时改前三个参数"
description="保存后回到新建/补资料里点一次「强制刷新礼包」即可拉取最新商品;pageId 和页签 IDs 留空表示自动。"
/>
<Form form={pointsConfigForm} layout="vertical" disabled={pointsConfigLoading}>
<Row gutter={12}>
<Col span={12}>
<Form.Item label="pageCode" name="points_page_code">
<Input placeholder="heping_baodian_credit_center_page" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="sourceId" name="points_source_id">
<Input placeholder="yellowcarlist" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="scene" name="points_scene">
<Input placeholder="7" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="pageId(可选)" name="points_page_id">
<Input placeholder="自动发现,如 343" />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="页签 IDs(可选)" name="points_tab_ids">
<Input placeholder="自动拉全部,如 431,433" />
</Form.Item>
</Col>
</Row>
</Form>
</Space>
</Modal>
<HuyaCollabShopModal
open={shopModalOpen}
shops={shops}
+1 -1
View File
@@ -627,7 +627,7 @@ export default function HuyaElitePage() {
</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('bind_gid', '绑定 gid')}{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>
<Modal open={configOpen} title="精英宝典设置" onCancel={() => setConfigOpen(false)} onOk={() => void saveConfig()} confirmLoading={configSaving} okText="保存" cancelText="取消" width={560}>{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('bind_gid', '绑定 gid')}{configField('room_pid', '直播间 PID')}{configField('pay_channel', '支付渠道')}{configField('points_page_code', '积分中心 pageCode')}{configField('points_source_id', '积分中心 sourceId')}{configField('points_scene', '积分中心 scene')}{configField('points_page_id', '页签 pageId(可选)')}{configField('points_tab_ids', '页签 IDs(可选)')}<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>
<HuyaExternalCkImportModal open={ckImportOpen} onClose={() => setCkImportOpen(false)} tags={importTags} scope={SCOPE} onImported={(result) => { localStorage.setItem('huya_elite_workbench_account_ids', JSON.stringify(result.account_ids)); void load(); }} />
</div>;
}
+25
View File
@@ -1846,6 +1846,31 @@ export default function HuyaTasksPage() {
<Input placeholder="9271" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="积分中心 pageCode" name="points_page_code">
<Input placeholder="heping_baodian_credit_center_page" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="积分中心 sourceId" name="points_source_id">
<Input placeholder="yellowcarlist" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="积分中心 scene" name="points_scene">
<Input placeholder="7" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="页签 pageId(可选)" name="points_page_id">
<Input placeholder="留空自动发现,如 343" />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item label="页签 IDs(可选,逗号分隔)" name="points_tab_ids">
<Input placeholder="留空自动拉全部,如 431,433" />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item label="支付渠道" name="pay_channel">
<Select