From 223619501ad1623f3ccafb37cc7cd1434f90cbdd Mon Sep 17 00:00:00 2001 From: steve <1050403040@qq.com> Date: Thu, 4 Sep 2025 01:44:05 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E8=8B=A5?= =?UTF-8?q?=E5=B9=B2bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/TeamStatus.vue | 159 ++++++++++++++++++++++++---- src/components/TokenManager.vue | 177 +++++++++++++++++++++++++++++--- src/stores/tokenStore.js | 21 ++-- src/utils/xyzwWebSocket.js | 4 +- src/views/GameFeatures.vue | 6 +- src/views/TokenImport.vue | 78 +++++++++++--- vite.config.js | 7 -- 7 files changed, 388 insertions(+), 64 deletions(-) diff --git a/src/components/TeamStatus.vue b/src/components/TeamStatus.vue index 7447315..f1c985b 100644 --- a/src/components/TeamStatus.vue +++ b/src/components/TeamStatus.vue @@ -22,12 +22,18 @@ {{ teamId }} @@ -84,7 +90,7 @@ import { useMessage, NTag } from 'naive-ui' * 集成英雄字典(游戏ID -> { name, type }) * 你也可以独立出一个 heroDict.ts 后 import;按你的要求,这里整合到同一文件。 */ -const HERO_DICT: Record = { +const HERO_DICT = { 101: { name: '司马懿', type: '魏国' }, 102: { name: '郭嘉', type: '魏国' }, 103: { name: '关羽', type: '蜀国' }, 104: { name: '诸葛亮', type: '蜀国' }, 105: { name: '周瑜', type: '吴国' }, 106: { name: '太史慈', type: '吴国' }, 107: { name: '吕布', type: '群雄' }, 108: { name: '华佗', type: '群雄' }, 109: { name: '甄姬', type: '魏国' }, @@ -115,16 +121,22 @@ const message = useMessage() const loading = ref(false) const switching = ref(false) const currentTeam = ref(1) -const availableTeams = ref([1, 2, 3, 4]) +const availableTeams = ref([1, 2, 3, 4]) + +// WebSocket连接状态 +const wsStatus = computed(() => { + if (!tokenStore.selectedToken) return 'disconnected' + return tokenStore.getWebSocketStatus(tokenStore.selectedToken.id) +}) // —— 缓存优先的 presetTeam 原始数据 —— const presetTeamRaw = computed(() => tokenStore.gameData?.presetTeam ?? null) // 统一结构:输出 { useTeamId, teams } -function normalizePresetTeam(raw: any): { useTeamId: number; teams: Record }> } { +function normalizePresetTeam(raw) { if (!raw) return { useTeamId: 1, teams: {} } const root = raw.presetTeamInfo ?? raw - const findUseIdRec = (obj: any): number | null => { + const findUseIdRec = (obj) => { if (!obj || typeof obj !== 'object') return null if (typeof obj.useTeamId === 'number') return obj.useTeamId for (const k of Object.keys(obj)) { @@ -136,7 +148,7 @@ function normalizePresetTeam(raw: any): { useTeamId: number; teams: Record }> = {} + const teams = {} const ids = Object.keys(dict || {}).filter(k => /^\d+$/.test(k)) for (const idStr of ids) { const id = Number(idStr) @@ -146,10 +158,10 @@ function normalizePresetTeam(raw: any): { useTeamId: number; teams: Record = {} - node.heroes.forEach((h: any, idx: number) => { ti[String(idx + 1)] = h }) + node.heroes.forEach((h, idx) => { ti[String(idx + 1)] = h }) teams[id] = { teamInfo: ti } } else if (typeof node === 'object') { - const hasHero = Object.values(node).some((v: any) => v && typeof v === 'object' && 'heroId' in v) + const hasHero = Object.values(node).some((v) => v && typeof v === 'object' && 'heroId' in v) teams[id] = { teamInfo: hasHero ? node : {} } } else { teams[id] = { teamInfo: {} } @@ -164,7 +176,7 @@ const presetTeam = computed(() => normalizePresetTeam(presetTeamRaw.value)) const currentTeamHeroes = computed(() => { const team = presetTeam.value.teams[currentTeam.value]?.teamInfo if (!team) return [] - const heroes: Array<{ id: number; name: string; type: string; position: number; level?: number; avatar?: string }> = [] + const heroes = [] for (const [pos, hero] of Object.entries(team)) { const hid = (hero as any)?.heroId ?? (hero as any)?.id if (!hid) continue @@ -183,10 +195,10 @@ const currentTeamHeroes = computed(() => { }) // —— 命令封装 —— -const executeGameCommand = async (tokenId: string | number, cmd: string, params: any = {}, description = '', timeout = 8000) => { +const executeGameCommand = async (tokenId, cmd, params = {}, description = '', timeout = 8000) => { try { return await tokenStore.sendMessageWithPromise(tokenId, cmd, params, timeout) - } catch (error: any) { + } catch (error) { const msg = error?.message ?? String(error) if (description) message.error(`${description}失败:${msg}`) throw error @@ -213,6 +225,9 @@ const getTeamInfoWithCache = async (force = false) => { state.gameData = { ...(state.gameData ?? {}), presetTeam: result } }) return result?.presetTeamInfo ?? null + } catch (error) { + console.error('获取阵容信息失败:', error) + return null } finally { loading.value = false } @@ -226,7 +241,7 @@ const updateAvailableTeams = () => { const updateCurrentTeam = () => { currentTeam.value = presetTeam.value.useTeamId || 1 } // —— 交互 —— -const selectTeam = async (teamId: number) => { +const selectTeam = async (teamId) => { if (switching.value || loading.value) return if (!tokenStore.selectedToken) { message.warning('请先选择Token'); return } const prev = currentTeam.value @@ -245,13 +260,52 @@ const selectTeam = async (teamId: number) => { const refreshTeamData = async (force = false) => { await getTeamInfoWithCache(force) } -// —— 首次挂载:先查缓存,再兜底拉接口 —— +// —— 首次挂载:检查连接状态后获取数据 —— onMounted(async () => { - await refreshTeamData(false) - updateAvailableTeams(); updateCurrentTeam() - if (!presetTeamRaw.value) { - await refreshTeamData(true) + // 组件挂载时获取队伍信息 + if (tokenStore.selectedToken && wsStatus.value === 'connected') { + await refreshTeamData(false) updateAvailableTeams(); updateCurrentTeam() + if (!presetTeamRaw.value) { + await refreshTeamData(true) + updateAvailableTeams(); updateCurrentTeam() + } + } else if (!tokenStore.selectedToken) { + console.log('🛡️ 没有选中的Token,无法获取队伍信息') + } else { + console.log('🛡️ WebSocket未连接,等待连接后自动获取队伍信息') + } +}) + +// —— 监听WebSocket连接状态变化 —— +watch(wsStatus, (newStatus, oldStatus) => { + console.log(`🛡️ WebSocket状态变化: ${oldStatus} -> ${newStatus}`) + + if (newStatus === 'connected' && oldStatus !== 'connected' && tokenStore.selectedToken) { + console.log('🛡️ WebSocket已连接,自动获取队伍信息') + // 延迟一点时间让WebSocket完全就绪 + setTimeout(async () => { + await refreshTeamData(false) + updateAvailableTeams(); updateCurrentTeam() + if (!presetTeamRaw.value) { + await refreshTeamData(true) + updateAvailableTeams(); updateCurrentTeam() + } + }, 1000) + } +}) + +// —— 监听Token变化 —— +watch(() => tokenStore.selectedToken, async (newToken, oldToken) => { + if (newToken && newToken.id !== oldToken?.id) { + console.log('🛡️ Token已切换,重新获取队伍信息') + + // 检查WebSocket是否已连接 + const status = tokenStore.getWebSocketStatus(newToken.id) + if (status === 'connected') { + await refreshTeamData(true) // 切换Token时强制刷新 + updateAvailableTeams(); updateCurrentTeam() + } } }) @@ -282,9 +336,76 @@ watch(() => presetTeamRaw.value, () => { updateAvailableTeams(); updateCurrentTe cursor: pointer; transition: all var(--transition-fast); &:hover { background: var(--bg-secondary); } &.active { background: var(--primary-color); color: white; } - &.refresh-button { background: var(--success-color, #10b981); color: white; &:hover { background: var(--success-color-dark, #059669); } } &:disabled { opacity: .6; cursor: not-allowed; } } + +.refresh-button { + display: flex; + align-items: center; + gap: 6px; + height: 32px; + padding: 0 12px; + border: 1px solid var(--border-color, #e5e7eb); + border-radius: 8px; + background: var(--bg-primary, #ffffff); + color: var(--text-secondary, #6b7280); + font-size: 13px; + font-weight: 500; + cursor: pointer; + transition: all var(--transition-fast, 0.15s ease); + + &:hover { + background: var(--bg-secondary, #f9fafb); + border-color: var(--border-hover, #d1d5db); + color: var(--text-primary, #374151); + transform: translateY(-1px); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + } + + &:active { + transform: translateY(0); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; + box-shadow: none; + + &:hover { + background: var(--bg-primary, #ffffff); + border-color: var(--border-color, #e5e7eb); + color: var(--text-secondary, #6b7280); + transform: none; + box-shadow: none; + } + } + + .refresh-icon { + width: 14px; + height: 14px; + transition: transform var(--transition-fast, 0.15s ease); + } + + &:not(:disabled):hover .refresh-icon { + transform: rotate(180deg); + } + + &:disabled .refresh-icon { + animation: spin 1s linear infinite; + } + + .refresh-text { + font-size: 13px; + line-height: 1; + } +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} .card-content .current-team-info { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--spacing-lg); .label { font-size: var(--font-size-sm); color: var(--text-secondary); } diff --git a/src/components/TokenManager.vue b/src/components/TokenManager.vue index 2242394..bcda8d7 100644 --- a/src/components/TokenManager.vue +++ b/src/components/TokenManager.vue @@ -89,20 +89,18 @@ > {{ getWSStatus(roleId) === 'connected' ? '断开WS' : '连接WS' }} - - 刷新Token - - - 删除 - + + + + @@ -156,14 +154,19 @@