fix:修复若干bug

This commit is contained in:
steve
2025-09-03 04:18:19 +08:00
parent fe47c5257f
commit 767c4149b1
11 changed files with 1194 additions and 404 deletions

View File

@@ -2,7 +2,11 @@
<div class="daily-task-container"> <div class="daily-task-container">
<div class="task-header"> <div class="task-header">
<div class="header-left"> <div class="header-left">
<img src="/icons/174023274867420.png" alt="每日任务" class="task-icon"> <img
src="/icons/174023274867420.png"
alt="每日任务"
class="task-icon"
>
<div class="title-container"> <div class="title-container">
<h3>每日任务</h3> <h3>每日任务</h3>
<p>当前进度</p> <p>当前进度</p>
@@ -12,14 +16,20 @@
<div class="header-right"> <div class="header-right">
<div <div
class="status-indicator" class="status-indicator"
:class="{ completed: isAllCompleted }" :class="{ completed: isFull }"
@click="showTaskDetails = true" @click="showTaskDetails = true"
> >
<div class="status-dot" :class="{ completed: isAllCompleted }"></div> <div
class="status-dot"
:class="{ completed: isFull }"
/>
<span>任务详情</span> <span>任务详情</span>
</div> </div>
<button class="settings-button" @click="showSettings = true"> <button
class="settings-button"
@click="showSettings = true"
>
<n-icon><Settings /></n-icon> <n-icon><Settings /></n-icon>
</button> </button>
</div> </div>
@@ -29,7 +39,7 @@
<div class="progress-container"> <div class="progress-container">
<n-progress <n-progress
type="line" type="line"
:percentage="progressPercentage" :percentage="dailyPoint"
:height="8" :height="8"
:border-radius="4" :border-radius="4"
:color="progressColor" :color="progressColor"
@@ -45,12 +55,21 @@
<!-- 一键执行按钮 --> <!-- 一键执行按钮 -->
<button <button
class="execute-button" class="execute-button"
:disabled="isExecuting" :disabled="busy"
@click="executeAllTasks" @click="runDailyFix"
> >
<span v-if="isExecuting" class="loading-text"> <span
<svg class="loading-icon" viewBox="0 0 24 24"> v-if="busy"
<path fill="currentColor" d="M12 22c5.421 0 10-4.579 10-10h-2c0 4.337-3.663 8-8 8s-8-3.663-8-8c0-4.336 3.663-8 8-8V2C6.579 2 2 6.58 2 12c0 5.421 4.579 10 10 10z"/> class="loading-text"
>
<svg
class="loading-icon"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M12 22c5.421 0 10-4.579 10-10h-2c0 4.337-3.663 8-8 8s-8-3.663-8-8c0-4.336 3.663-8 8-8V2C6.579 2 2 6.58 2 12c0 5.421 4.579 10 10 10z"
/>
</svg> </svg>
执行中... 执行中...
</span> </span>
@@ -77,10 +96,9 @@
<div class="setting-item"> <div class="setting-item">
<label class="setting-label">竞技场阵容</label> <label class="setting-label">竞技场阵容</label>
<n-select <n-select
v-model:value="taskSettings.arenaFormation" v-model:value="settings.arenaFormation"
:options="formationOptions" :options="formationOptions"
size="small" size="small"
@update:value="saveSettings"
/> />
</div> </div>
@@ -88,10 +106,9 @@
<div class="setting-item"> <div class="setting-item">
<label class="setting-label">BOSS阵容</label> <label class="setting-label">BOSS阵容</label>
<n-select <n-select
v-model:value="taskSettings.bossFormation" v-model:value="settings.bossFormation"
:options="formationOptions" :options="formationOptions"
size="small" size="small"
@update:value="saveSettings"
/> />
</div> </div>
@@ -99,10 +116,9 @@
<div class="setting-item"> <div class="setting-item">
<label class="setting-label">BOSS次数</label> <label class="setting-label">BOSS次数</label>
<n-select <n-select
v-model:value="taskSettings.bossTimes" v-model:value="settings.bossTimes"
:options="bossTimesOptions" :options="bossTimesOptions"
size="small" size="small"
@update:value="saveSettings"
/> />
</div> </div>
@@ -111,48 +127,42 @@
<div class="switch-row"> <div class="switch-row">
<span class="switch-label">领罐子</span> <span class="switch-label">领罐子</span>
<n-switch <n-switch
v-model:value="taskSettings.claimBottle" v-model:value="settings.claimBottle"
@update:value="saveSettings"
/> />
</div> </div>
<div class="switch-row"> <div class="switch-row">
<span class="switch-label">领挂机</span> <span class="switch-label">领挂机</span>
<n-switch <n-switch
v-model:value="taskSettings.claimHangUp" v-model:value="settings.claimHangUp"
@update:value="saveSettings"
/> />
</div> </div>
<div class="switch-row"> <div class="switch-row">
<span class="switch-label">竞技场</span> <span class="switch-label">竞技场</span>
<n-switch <n-switch
v-model:value="taskSettings.arenaEnable" v-model:value="settings.arenaEnable"
@update:value="saveSettings"
/> />
</div> </div>
<div class="switch-row"> <div class="switch-row">
<span class="switch-label">开宝箱</span> <span class="switch-label">开宝箱</span>
<n-switch <n-switch
v-model:value="taskSettings.openBox" v-model:value="settings.openBox"
@update:value="saveSettings"
/> />
</div> </div>
<div class="switch-row"> <div class="switch-row">
<span class="switch-label">领取邮件奖励</span> <span class="switch-label">领取邮件奖励</span>
<n-switch <n-switch
v-model:value="taskSettings.claimEmail" v-model:value="settings.claimEmail"
@update:value="saveSettings"
/> />
</div> </div>
<div class="switch-row"> <div class="switch-row">
<span class="switch-label">付费招募</span> <span class="switch-label">付费招募</span>
<n-switch <n-switch
v-model:value="taskSettings.payRecruit" v-model:value="settings.payRecruit"
@update:value="saveSettings"
/> />
</div> </div>
</div> </div>
@@ -176,7 +186,7 @@
<div class="task-list"> <div class="task-list">
<div <div
v-for="task in dailyTasks" v-for="task in tasks"
:key="task.id" :key="task.id"
class="task-item" class="task-item"
> >
@@ -214,21 +224,24 @@
</div> </div>
</template> </template>
<div class="log-container" ref="logContainer">
<div <div
v-for="log in executionLogs" ref="logContainer"
:key="log.id" class="log-container"
>
<div
v-for="logItem in logList"
:key="logItem.time + logItem.message"
class="log-item" class="log-item"
> >
<span class="log-time">{{ formatTime(log.time) }}</span> <span class="log-time">{{ logItem.time }}</span>
<span <span
class="log-message" class="log-message"
:class="{ :class="{
error: log.type === 'error', error: logItem.type === 'error',
success: log.type === 'success' success: logItem.type === 'success'
}" }"
> >
{{ log.message }} {{ logItem.message }}
</span> </span>
</div> </div>
</div> </div>
@@ -237,7 +250,7 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, watch, nextTick, onMounted } from 'vue' import { ref, reactive, computed, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
import { useTokenStore } from '@/stores/tokenStore' import { useTokenStore } from '@/stores/tokenStore'
import { useMessage } from 'naive-ui' import { useMessage } from 'naive-ui'
import { import {
@@ -255,12 +268,11 @@ const message = useMessage()
const showSettings = ref(false) const showSettings = ref(false)
const showTaskDetails = ref(false) const showTaskDetails = ref(false)
const showLog = ref(false) const showLog = ref(false)
const isExecuting = ref(false) const busy = ref(false)
const logContainer = ref(null) const logContainer = ref(null)
const executionLogs = ref([])
// 任务设置 // 任务设置 - 基于参考代码
const taskSettings = ref({ const settings = reactive({
arenaFormation: 1, arenaFormation: 1,
bossFormation: 1, bossFormation: 1,
bossTimes: 4, bossTimes: 4,
@@ -272,8 +284,8 @@ const taskSettings = ref({
claimEmail: true claimEmail: true
}) })
// 每日任务列表 // 每日任务列表 - 基于参考代码
const dailyTasks = ref([ const tasks = ref([
{ id: 1, name: '登录一次游戏', completed: false, loading: false }, { id: 1, name: '登录一次游戏', completed: false, loading: false },
{ id: 2, name: '分享一次游戏', completed: false, loading: false }, { id: 2, name: '分享一次游戏', completed: false, loading: false },
{ id: 3, name: '赠送好友3次金币', completed: false, loading: false }, { id: 3, name: '赠送好友3次金币', completed: false, loading: false },
@@ -287,65 +299,35 @@ const dailyTasks = ref([
]) ])
// 选项配置 // 选项配置
const formationOptions = [ const formationOptions = [1,2,3,4].map(v => ({ label: `阵容${v}`, value: v }))
{ label: '阵容1', value: 1 }, const bossTimesOptions = [0,1,2,3,4].map(v => ({ label: `${v}`, value: v }))
{ label: '阵容2', value: 2 },
{ label: '阵容3', value: 3 },
{ label: '阵容4', value: 4 }
]
const bossTimesOptions = [ // 计算属性 - 基于参考代码逻辑
{ label: '0次', value: 0 },
{ label: '1次', value: 1 },
{ label: '2次', value: 2 },
{ label: '3次', value: 3 },
{ label: '4次', value: 4 }
]
// 计算属性
const roleInfo = computed(() => { const roleInfo = computed(() => {
return tokenStore.gameData?.roleInfo return tokenStore.selectedTokenRoleInfo
}) })
const dailyTaskData = computed(() => { const roleDailyPoint = computed(() => {
return roleInfo.value?.role?.dailyTask return roleInfo.value?.role?.dailyTask?.dailyPoint ?? 0
}) })
const progressPercentage = computed(() => { // 进度 - 基于参考代码
const current = dailyTaskData.value?.dailyPoint || 0 const dailyPoint = computed(() => Math.min(roleDailyPoint.value, 100))
return current > 100 ? 100 : current const isFull = computed(() => dailyPoint.value >= 100)
}) const progressColor = computed(() => isFull.value ? '#10b981' : '#3b82f6')
const isAllCompleted = computed(() => { // 日志系统 - 基于参考代码的log函数
return progressPercentage.value === 100 const logList = ref([])
}) const LOG_MAX = 500
const progressColor = computed(() => { const log = (message, type = 'info') => {
return progressPercentage.value === 100 ? '#10b981' : '#3b82f6' const time = new Date().toLocaleTimeString()
}) logList.value.push({ time, message, type })
// 更新任务完成状态 if (logList.value.length > LOG_MAX) {
const updateTaskStatus = () => { logList.value.splice(0, logList.value.length - LOG_MAX)
if (!dailyTaskData.value?.complete) return
const completed = dailyTaskData.value.complete
dailyTasks.value.forEach(task => {
const taskStatus = completed[task.id.toString()]
task.completed = taskStatus === -1 // -1 表示已完成
})
}
// 添加日志
const addLog = (message, type = 'info') => {
const log = {
id: Date.now() + Math.random(),
time: new Date().toLocaleTimeString(),
message,
type
} }
executionLogs.value.push(log)
// 自动滚动到底部
nextTick(() => { nextTick(() => {
if (logContainer.value) { if (logContainer.value) {
logContainer.value.scrollTop = logContainer.value.scrollHeight logContainer.value.scrollTop = logContainer.value.scrollHeight
@@ -353,113 +335,320 @@ const addLog = (message, type = 'info') => {
}) })
} }
// 保存设置 // 超时执行器 - 基于参考代码的 callWithRetry 函数
const saveSettings = () => { const callWithRetry = async (fn, opt = {}) => {
// 这里可以保存到 localStorage 或发送到服务器 const timeoutMs = opt?.timeoutMs ?? 30000
localStorage.setItem('taskSettings', JSON.stringify(taskSettings.value)) const retries = opt?.retries ?? 1
const delayMs = opt?.delayMs ?? 600
for (let attempt = 0; attempt <= retries; attempt++) {
try {
const r = await Promise.race([
fn(),
new Promise((_, rej) => setTimeout(() => rej(new Error(`请求超时(${timeoutMs}ms)`)), timeoutMs))
])
return r
} catch (err) {
if (attempt === retries) throw err
await new Promise(res => setTimeout(res, delayMs * (attempt + 1)))
}
}
throw new Error('unexpected')
} }
// 格式化时间 // 消息错误处理 - 基于参考代码的onRaw函数
const formatTime = (timeString) => { const onRaw = (evt) => {
return timeString const err = evt?._raw?.error
if (err) log(String(err), 'error')
} }
// 执行所有任务 // 同步服务器任务完成状态 - 基于参考代码的 syncCompleteFromServer 函数
const executeAllTasks = async () => { const syncCompleteFromServer = (resp) => {
if (!tokenStore.selectedToken || isExecuting.value) return if (!resp?.role?.dailyTask?.complete) return
const complete = resp.role.dailyTask.complete
const isDone = (v) => v === -1
isExecuting.value = true Object.keys(complete).forEach(k => {
const id = Number(k)
const idx = tasks.value.findIndex(t => t.id === id)
if (idx >= 0) tasks.value[idx].completed = isDone(complete[k])
})
}
// 刷新角色信息 - 基于参考代码
const refreshRoleInfo = async () => {
if (!tokenStore.selectedToken) {
throw new Error('没有选中的Token')
}
const tokenId = tokenStore.selectedToken.id
const e = await callWithRetry(() =>
tokenStore.sendMessageWithPromise(tokenId, 'role_getroleinfo', {})
)
return e
}
// 一键补差 - 基于参考代码的runDailyFix函数
const runDailyFix = async () => {
if (!tokenStore.selectedToken || busy.value) return
// 设置消息监听和禁用显示
tokenStore.setMessageListener(onRaw)
tokenStore.setShowMsg(false)
busy.value = true
showLog.value = true showLog.value = true
executionLogs.value = [] logList.value = []
addLog('开始执行任务...') log('开始执行任务...')
try { try {
const tokenId = tokenStore.selectedToken.id const e = await refreshRoleInfo()
log('获取角色信息成功')
// 获取角色信息 await U(e, log)
addLog('获取角色信息...')
await tokenStore.sendMessageWithPromise(tokenId, 'role_getroleinfo')
addLog('获取角色信息成功', 'success')
// 执行各种任务 log('任务执行完成', 'success')
if (taskSettings.value.claimHangUp) {
addLog('领取挂机奖励...')
await tokenStore.sendMessageWithPromise(tokenId, 'system_claimhangupreward')
}
if (taskSettings.value.claimBottle) {
addLog('领取盐罐奖励...')
await tokenStore.sendMessageWithPromise(tokenId, 'bottlehelper_claim')
}
if (taskSettings.value.payRecruit) {
addLog('进行招募...')
await tokenStore.sendMessageWithPromise(tokenId, 'hero_recruit', {
byClub: false,
recruitNumber: 1,
recruitType: 3
})
}
if (taskSettings.value.openBox) {
addLog('开启宝箱...')
await tokenStore.sendMessageWithPromise(tokenId, 'item_openbox', {
itemId: 2001,
number: 3
})
}
if (taskSettings.value.arenaEnable) {
addLog('进行竞技场战斗...')
await tokenStore.sendMessageWithPromise(tokenId, 'fight_startareaarena', {
targetId: 530479307
})
}
if (taskSettings.value.claimEmail) {
addLog('领取邮件奖励...')
await tokenStore.sendMessageWithPromise(tokenId, 'mail_claimallattachment', {
category: 0
})
}
// 最后再次获取角色信息更新状态
addLog('更新角色信息...')
await tokenStore.sendMessageWithPromise(tokenId, 'role_getroleinfo')
addLog('任务执行完成!', 'success')
message.success('任务处理完成') message.success('任务处理完成')
} catch (error) { } catch (e) {
addLog(`任务执行失败: ${error.message}`, 'error') const msg = e?.message ?? (typeof e === 'string' ? e : JSON.stringify(e))
log(`任务执行失败: ${msg}`, 'error')
message.error('任务执行失败') message.error('任务执行失败')
} finally { } finally {
isExecuting.value = false tokenStore.setShowMsg(true)
busy.value = false
} }
} }
// 监听角色信息变化 // 执行器U函数 - 基于完整参考代码重写
watch(dailyTaskData, () => { const U = async (roleInfoResp, logFn, progress) => {
updateTaskStatus() const tokenId = tokenStore.selectedToken.id
}, { deep: true, immediate: true })
// 初始化设置 logFn?.('检查每日任务完成情况')
const initSettings = () => {
const saved = localStorage.getItem('taskSettings') // 1) 已完成任务 → 生成要跳过的指令"指纹"
if (saved) { const officialList = [
{ id: 1, name: '登录一次游戏', cmds: [] },
{ id: 2, name: '分享一次游戏', cmds: ['system_mysharecallback'] },
{ id: 3, name: '赠送好友3次金币', cmds: ['friend_batch'] },
{ id: 4, name: '进行2次招募', cmds: ['hero_recruit'] },
{ id: 5, name: '领取5次挂机奖励', cmds: ['system_claimhangupreward'] },
{ id: 6, name: '进行3次点金', cmds: ['system_buygold'] },
{ id: 7, name: '开启3次宝箱', cmds: ['item_openbox'] },
{ id: 12, name: '黑市购买1次物品请设置采购清单', cmds: ['store_purchase'] },
{ id: 13, name: '进行1场竞技场战斗' },
{ id: 14, name: '收获1个任意盐罐', cmds: ['bottlehelper_stop', 'bottlehelper_start', 'bottlehelper_claim'] }
]
const completedMap = roleInfoResp.role?.dailyTask?.complete ?? {}
const doneIds = new Set()
const skipFp = new Set()
for (const k of Object.keys(completedMap)) {
const id = Number(k)
if (completedMap[k] === -1) {
doneIds.add(id)
const found = officialList.find(x => x.id === id)
if (found?.cmds) {
for (const c of found.cmds) skipFp.add(fpOf({ cmd: c, respKey: '' }))
}
logFn?.(`${officialList.find(x=>x.id===id)?.name ?? id} 完成`, 'success')
}
}
// 2) 组装动态命令段(依据统计与设置)
const cmds = []
// 2.1 切换 BOSS 阵容
cmds.push({ cmd: 'presetteam_getinfo', respKey: 'presetteam_getinforesp', sendMsg: '准备切换阵容' })
cmds.push({ cmd: 'presetteam_saveteam', respKey: 'presetteam_saveteamresp', params: { teamId: settings.bossFormation }, sendMsg: '切换攻打BOSS阵容中', successMsg: `成功切换阵容 ${settings.bossFormation}` })
// 2.2 军团 BOSS 次数
const alreadyLegion = Number(roleInfoResp.role?.statistics?.['legion:boss'] ?? 0)
const leftLegion = isTodayAvailable(roleInfoResp.role?.statisticsTime?.['legion:boss']) ? settings.bossTimes : Math.max(settings.bossTimes - alreadyLegion, 0)
for (let i = 0; i < leftLegion; i++) cmds.push({ cmd: 'fight_startlegionboss', respKey: 'fight_startlegionbossresp', sendMsg: '攻打每日俱乐部BOSS', successMsg: '攻打每日俱乐部BOSS成功' })
// 2.3 免费"钓鱼"
if (isTodayAvailable(roleInfoResp.role?.statisticsTime?.['artifact:normal:lottery:time'])) {
for (let i = 0; i < 3; i++) cmds.push({ cmd: 'artifact_lottery', respKey: 'syncrewardresp', sendMsg: '每日免费钓鱼', successMsg: '每日免费钓鱼成功' })
}
// 2.4 灯神免费扫荡1..4
const kingdoms = ['魏国','蜀国','吴国','群雄']
for (let gid = 1; gid <= 4; gid++) {
if (isTodayAvailable(roleInfoResp.role?.statisticsTime?.[`genie: daily: free: ${gid}`])) {
cmds.push({ cmd: 'genie_sweep', respKey: 'syncrewardresp', params: { genieId: gid }, sendMsg: `领取 ${kingdoms[gid-1]} 免费灯神扫荡`, successMsg: `领取 ${kingdoms[gid-1]} 免费灯神扫荡成功` })
}
}
// 2.5 用户开关
if (settings.claimBottle) cmds.push({ cmd: 'bottlehelper_claim', respKey: 'syncrewardresp', sendMsg: '领取盐罐奖励', successMsg: '领取盐罐奖励成功' })
if (settings.payRecruit) cmds.push({ cmd: 'hero_recruit', respKey: 'hero_recruitresp', params: { recruitType: 1 }, sendMsg: '招募付费1次', successMsg: '招募1次成功' })
if (settings.openBox) cmds.push({ cmd: 'item_openbox', respKey: 'item_openboxresp', params: { itemId: 2001, number: 10 }, sendMsg: '开启木质宝箱10个', successMsg: '开启木质宝箱10个成功' })
if (isTodayAvailable(roleInfoResp.role?.statisticsTime?.['buy:gold'])) {
for (let i = 0; i < 3; i++) cmds.push({ cmd: 'system_buygold', respKey: 'syncrewardresp', sendMsg: '免费点金', successMsg: '免费点金成功' })
}
if (settings.claimHangUp) {
for (let i = 0; i < 4; i++) cmds.push({ cmd: 'system_mysharecallback', respKey: 'syncresp', params: { isSkipShareCard: true, type: 2 }, sendMsg: '挂机加钟', successMsg: '加钟成功' })
cmds.push({ cmd: 'system_claimhangupreward', respKey: 'system_claimhanguprewardresp', sendMsg: '领取挂机奖励', successMsg: '领取挂机奖励成功' })
cmds.push({ cmd: 'system_mysharecallback', respKey: 'syncresp', params: { isSkipShareCard: true, type: 2 }, sendMsg: '挂机加钟', successMsg: '加钟成功' })
}
if (settings.claimEmail) cmds.push({ cmd: 'mail_claimallattachment', respKey: 'mail_claimallattachmentresp', sendMsg: '领取邮件奖励', successMsg: '邮件奖励领取成功' })
// 2.6 固定段
const dow = new Date().getDay()
const DAY_BOSS_MAP = [9904, 9905, 9901, 9902, 9903, 9904, 9905]
for (let i = 0; i < 3; i++) cmds.push({ cmd: 'fight_startboss', respKey: 'fight_startbossresp', params: { bossId: DAY_BOSS_MAP[dow] }, sendMsg: '攻打每日BOSS', successMsg: '攻打每日BOSS成功' })
for (let i = 0; i < 3; i++) cmds.push({ cmd: 'genie_buysweep', respKey: 'syncrewardresp', sendMsg: '领取每日免费扫荡卷', successMsg: '领取每日免费扫荡卷成功' })
cmds.push(
{ cmd: 'system_signinreward', respKey: 'syncrewardresp', sendMsg: '福利签到', successMsg: '同步奖励' },
{ cmd: 'discount_claimreward', respKey: 'syncrewardresp', sendMsg: '领取每日礼包', successMsg: '同步奖励' },
{ cmd: 'legion_signin', respKey: 'legion_signinresp', sendMsg: '俱乐部签到', successMsg: '同步奖励' },
{ cmd: 'card_claimreward', respKey: 'syncrewardresp', sendMsg: '领取每日免费礼包', successMsg: '同步奖励' },
{ cmd: 'card_claimreward', respKey: 'syncrewardresp', params: { cardId: 4003 }, sendMsg: '领取永久卡礼包', successMsg: '同步奖励' },
{ cmd: 'system_mysharecallback', respKey: 'syncresp', sendMsg: '分享一次游戏', successMsg: '分享一次游戏成功' },
{ cmd: 'friend_batch', respKey: 'friend_batchresp', sendMsg: '赠送好友金币', successMsg: '赠送好友金币成功' },
{ cmd: 'hero_recruit', respKey: 'hero_recruitresp', sendMsg: '免费招募1次', successMsg: '招募1次成功' }
)
// 领取积分taskId 1..10
for (let i = 1; i <= 10; i++) cmds.push({ cmd: 'task_claimdailypoint', respKey: 'syncresp', params: { taskId: i }, sendMsg: '领取任务奖励', successMsg: '领取任务奖励成功' })
// 领取日/周奖励
cmds.push({ cmd: 'task_claimdailyreward', respKey: 'syncrewardresp', sendMsg: '领取任务奖励', successMsg: '领取任务奖励成功' })
cmds.push({ cmd: 'task_claimweekreward', respKey: 'syncrewardresp', sendMsg: '领取任务奖励', successMsg: '领取任务奖励成功' })
// 3) 过滤掉"已完成任务对应的指令"与真正重复项(按指纹)
const uniq = new Map()
for (const it of cmds) {
const fp = fpOf(it)
if (skipFp.has(fpOf({ cmd: it.cmd, respKey: '', params: it.params }))) continue
if (!uniq.has(fp)) uniq.set(fp, it)
}
const queue = Array.from(uniq.values())
// 4) 竞技场(单独先跑)
if (settings.arenaEnable && completedMap[13] !== -1) {
logFn?.('竞技场自动战斗')
await callWithRetry(() => tokenStore.sendMessageWithPromise(tokenId, 'presetteam_getinfo', {}))
.then(async (resp) => {
if (resp?.presetTeamInfo?.useTeamId === settings.arenaFormation) {
logFn?.(`当前阵容为阵容 ${settings.arenaFormation}`)
} else {
await callWithRetry(() => tokenStore.sendMessageWithPromise(tokenId, 'presetteam_saveteam', { teamId: settings.arenaFormation }))
logFn?.(`切换竞技场阵容为阵容 ${settings.arenaFormation}`)
}
})
await callWithRetry(() => tokenStore.sendMessageWithPromise(tokenId, 'arena_startarea', {}))
for (let i = 0; i < 3; i++) {
const target = await callWithRetry(() => tokenStore.sendMessageWithPromise(tokenId, 'arena_getareatarget', {}))
const targetId = target?.roleList?.[0]?.roleId
if (targetId) await callWithRetry(() => tokenStore.sendMessageWithPromise(tokenId, 'fight_startareaarena', { targetId }))
}
} else {
logFn?.('今日竞技场任务已完成', 'success')
}
// 5) 依次执行队列 + 进度
const total = queue.length || 1
let done = 0
for (const it of queue) {
if (it.sendMsg) logFn?.(it.sendMsg)
try { try {
taskSettings.value = { ...taskSettings.value, ...JSON.parse(saved) } await callWithRetry(() => tokenStore.sendMessageWithPromise(tokenId, it.cmd, it.params || {}))
} catch (error) { if (it.successMsg) logFn?.(it.successMsg, 'success')
console.error('加载设置失败:', error) } catch (err) {
logFn?.('任务执行失败', 'error')
} finally {
done += 1
const pct = Math.min(100, Math.floor(done * 100 / total))
if (progress && tokenId) progress(tokenId, pct)
} }
} }
// 收尾:确保 100%
if (progress && tokenId) progress(tokenId, 100)
}
// 辅助函数 - 基于参考代码
const fpOf = (item) => {
return `${item.cmd}|${JSON.stringify(item.params ?? {})}`
}
const getCurrentRole = () => {
return tokenStore.selectedToken ? { roleId: tokenStore.selectedToken.id } : null
}
const loadSettings = (roleId) => {
try {
const raw = localStorage.getItem(`daily-settings:${roleId}`)
return raw ? JSON.parse(raw) : null
} catch (error) {
console.error('Failed to load settings:', error)
return null
}
} }
// 生命周期 const saveSettings = (roleId, s) => {
onMounted(() => { try {
initSettings() localStorage.setItem(`daily-settings:${roleId}`, JSON.stringify(s))
updateTaskStatus() } catch (error) {
console.error('Failed to save settings:', error)
}
}
const isTodayAvailable = (v) => { return !!v }
// 监听设置变化 - 基于参考代码的watch逻辑
watch(settings, (cur) => {
const role = getCurrentRole()
if (role) saveSettings(role.roleId, cur)
}, { deep: true })
// 监听token选择变化
watch(() => tokenStore.selectedToken, async (newToken, oldToken) => {
if (newToken && newToken !== oldToken) {
// 加载新token的设置
const saved = loadSettings(newToken.id)
if (saved) Object.assign(settings, saved)
// 同步任务状态
syncCompleteFromServer(tokenStore.selectedTokenRoleInfo)
}
}, { immediate: true })
// 生命周期 - 基于参考代码的onMounted函数
onMounted(async () => {
// 首次拉取角色信息如果有选中的token
if (tokenStore.selectedToken) {
try {
await refreshRoleInfo()
} catch (error) {
console.warn('初始化时获取角色信息失败:', error.message)
// 如果获取失败,尝试发送普通消息(不等待响应)
try {
tokenStore.sendMessage(tokenStore.selectedToken.id, 'role_getroleinfo', {})
} catch (sendError) {
console.warn('发送角色信息请求失败:', sendError.message)
}
}
}
const role = getCurrentRole()
if (role) {
const saved = loadSettings(role.roleId)
if (saved) Object.assign(settings, saved)
}
// 同步完成态(使用现有的角色信息)
syncCompleteFromServer(tokenStore.selectedTokenRoleInfo)
})
// 清理监听 - 基于参考代码的onBeforeUnmount
onBeforeUnmount(() => {
tokenStore.setMessageListener(undefined)
}) })
</script> </script>
@@ -589,8 +778,8 @@ onMounted(() => {
} }
&:disabled { &:disabled {
background: var(--bg-tertiary); background: #e5e7eb;
color: var(--text-tertiary); color: #9ca3af;
cursor: not-allowed; cursor: not-allowed;
} }
} }

View File

@@ -13,13 +13,20 @@
<!-- 盐罐机器人状态 --> <!-- 盐罐机器人状态 -->
<div class="status-card bottle-helper"> <div class="status-card bottle-helper">
<div class="card-header"> <div class="card-header">
<img src="/icons/173746572831736.png" alt="盐罐图标" class="status-icon"> <img
src="/icons/173746572831736.png"
alt="盐罐图标"
class="status-icon"
>
<div class="status-info"> <div class="status-info">
<h3>盐罐机器人</h3> <h3>盐罐机器人</h3>
<p>剩余时间</p> <p>剩余时间</p>
</div> </div>
<div class="status-badge" :class="{ active: bottleHelper.isRunning }"> <div
<div class="status-dot"></div> class="status-badge"
:class="{ active: bottleHelper.isRunning }"
>
<div class="status-dot" />
<span>{{ bottleHelper.isRunning ? '运行中' : '已停止' }}</span> <span>{{ bottleHelper.isRunning ? '运行中' : '已停止' }}</span>
</div> </div>
</div> </div>
@@ -40,13 +47,20 @@
<!-- 挂机状态 --> <!-- 挂机状态 -->
<div class="status-card hang-up"> <div class="status-card hang-up">
<div class="card-header"> <div class="card-header">
<img src="/icons/174061875626614.png" alt="挂机图标" class="status-icon"> <img
src="/icons/174061875626614.png"
alt="挂机图标"
class="status-icon"
>
<div class="status-info"> <div class="status-info">
<h3>挂机时间</h3> <h3>挂机时间</h3>
<p>已挂机{{ formatTime(hangUp.elapsedTime) }}</p> <p>已挂机{{ formatTime(hangUp.elapsedTime) }}</p>
</div> </div>
<div class="status-badge" :class="{ active: hangUp.isActive }"> <div
<div class="status-dot"></div> class="status-badge"
:class="{ active: hangUp.isActive }"
>
<div class="status-dot" />
<span>{{ hangUp.isActive ? '挂机中' : '已完成' }}</span> <span>{{ hangUp.isActive ? '挂机中' : '已完成' }}</span>
</div> </div>
</div> </div>
@@ -55,11 +69,49 @@
{{ formatTime(hangUp.remainingTime) }} {{ formatTime(hangUp.remainingTime) }}
</div> </div>
<div class="action-row"> <div class="action-row">
<button class="action-button secondary" @click="extendHangUp"> <button
加钟 class="action-button secondary"
:disabled="hangUp.isExtending"
@click="extendHangUp"
>
<span
v-if="hangUp.isExtending"
class="loading-text"
>
<svg
class="loading-icon"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M12 22c5.421 0 10-4.579 10-10h-2c0 4.337-3.663 8-8 8s-8-3.663-8-8c0-4.336 3.663-8 8-8V2C6.579 2 2 6.58 2 12c0 5.421 4.579 10 10 10z"
/>
</svg>
加钟中...
</span>
<span v-else>加钟</span>
</button> </button>
<button class="action-button primary" @click="claimHangUpReward"> <button
领取奖励 class="action-button primary"
:disabled="hangUp.isClaiming"
@click="claimHangUpReward"
>
<span
v-if="hangUp.isClaiming"
class="loading-text"
>
<svg
class="loading-icon"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M12 22c5.421 0 10-4.579 10-10h-2c0 4.337-3.663 8-8 8s-8-3.663-8-8c0-4.336 3.663-8 8-8V2C6.579 2 2 6.58 2 12c0 5.421 4.579 10 10 10z"
/>
</svg>
领取中...
</span>
<span v-else>领取奖励</span>
</button> </button>
</div> </div>
</div> </div>
@@ -68,13 +120,20 @@
<!-- 俱乐部排位 --> <!-- 俱乐部排位 -->
<div class="status-card legion-match"> <div class="status-card legion-match">
<div class="card-header"> <div class="card-header">
<img src="/icons/1733492491706152.png" alt="俱乐部图标" class="status-icon"> <img
src="/icons/1733492491706152.png"
alt="俱乐部图标"
class="status-icon"
>
<div class="status-info"> <div class="status-info">
<h3>俱乐部排位</h3> <h3>俱乐部排位</h3>
<p>赛事状态</p> <p>赛事状态</p>
</div> </div>
<div class="status-badge" :class="{ active: legionMatch.isRegistered }"> <div
<div class="status-dot"></div> class="status-badge"
:class="{ active: legionMatch.isRegistered }"
>
<div class="status-dot" />
<span>{{ legionMatch.isRegistered ? '已报名' : '未报名' }}</span> <span>{{ legionMatch.isRegistered ? '已报名' : '未报名' }}</span>
</div> </div>
</div> </div>
@@ -96,22 +155,35 @@
<!-- 俱乐部签到 --> <!-- 俱乐部签到 -->
<div class="status-card legion-signin"> <div class="status-card legion-signin">
<div class="card-header"> <div class="card-header">
<img src="/icons/1733492491706148.png" alt="签到图标" class="status-icon"> <img
src="/icons/1733492491706148.png"
alt="签到图标"
class="status-icon"
>
<div class="status-info"> <div class="status-info">
<h3>俱乐部签到</h3> <h3>俱乐部签到</h3>
<p>每日签到状态</p> <p>每日签到状态</p>
</div> </div>
<div class="status-badge" :class="{ active: legionSignin.isSignedIn }"> <div
<div class="status-dot"></div> class="status-badge"
:class="{ active: legionSignin.isSignedIn }"
>
<div class="status-dot" />
<span>{{ legionSignin.isSignedIn ? '已签到' : '待签到' }}</span> <span>{{ legionSignin.isSignedIn ? '已签到' : '待签到' }}</span>
</div> </div>
</div> </div>
<div class="card-content"> <div class="card-content">
<p class="club-name" v-if="legionSignin.clubName"> <p
v-if="legionSignin.clubName"
class="club-name"
>
当前俱乐部<br> 当前俱乐部<br>
<strong>{{ legionSignin.clubName }}</strong> <strong>{{ legionSignin.clubName }}</strong>
</p> </p>
<p class="description" v-else> <p
v-else
class="description"
>
尚未加入任何俱乐部 尚未加入任何俱乐部
</p> </p>
<button <button
@@ -127,13 +199,17 @@
<!-- 咸鱼大冲关 --> <!-- 咸鱼大冲关 -->
<div class="status-card study"> <div class="status-card study">
<div class="card-header"> <div class="card-header">
<img src="/icons/1736425783912140.png" alt="学习图标" class="status-icon"> <img
src="/icons/1736425783912140.png"
alt="学习图标"
class="status-icon"
>
<div class="status-info"> <div class="status-info">
<h3>咸鱼大冲关</h3> <h3>咸鱼大冲关</h3>
<p>每日知识挑战</p> <p>每日知识挑战</p>
</div> </div>
<div class="status-badge weekly"> <div class="status-badge weekly">
<div class="status-dot"></div> <div class="status-dot" />
<span>每周任务</span> <span>每周任务</span>
</div> </div>
</div> </div>
@@ -146,9 +222,18 @@
:disabled="study.isAnswering" :disabled="study.isAnswering"
@click="startStudy" @click="startStudy"
> >
<span v-if="study.isAnswering" class="loading-text"> <span
<svg class="loading-icon" viewBox="0 0 24 24"> v-if="study.isAnswering"
<path fill="currentColor" d="M12 22c5.421 0 10-4.579 10-10h-2c0 4.337-3.663 8-8 8s-8-3.663-8-8c0-4.336 3.663-8 8-8V2C6.579 2 2 6.58 2 12c0 5.421 4.579 10 10 10z"/> class="loading-text"
>
<svg
class="loading-icon"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M12 22c5.421 0 10-4.579 10-10h-2c0 4.337-3.663 8-8 8s-8-3.663-8-8c0-4.336 3.663-8 8-8V2C6.579 2 2 6.58 2 12c0 5.421 4.579 10 10 10z"
/>
</svg> </svg>
答题中... 答题中...
</span> </span>
@@ -156,7 +241,6 @@
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
@@ -183,7 +267,9 @@ const hangUp = ref({
remainingTime: 0, remainingTime: 0,
elapsedTime: 0, elapsedTime: 0,
lastTime: 0, lastTime: 0,
hangUpTime: 0 hangUpTime: 0,
isExtending: false, // 加钟状态
isClaiming: false // 领取奖励状态
}) })
const legionMatch = ref({ const legionMatch = ref({
@@ -333,40 +419,108 @@ const handleBottleHelper = () => {
message.info(bottleHelper.value.isRunning ? '重启盐罐机器人' : '启动盐罐机器人') message.info(bottleHelper.value.isRunning ? '重启盐罐机器人' : '启动盐罐机器人')
} }
// 挂机操作 // 挂机操作 - 参考HangUpStatus逻辑优化
const extendHangUp = () => { const extendHangUp = async () => {
if (!tokenStore.selectedToken) return if (!tokenStore.selectedToken) {
message.warning('请先选择Token')
return
}
const tokenId = tokenStore.selectedToken.id const tokenId = tokenStore.selectedToken.id
// 发送4次分享回调请求来加钟 try {
console.log('🕐 开始加钟操作...')
hangUp.value.isExtending = true
message.info('正在加钟...')
// 按照参考代码的逻辑发送4次分享回调请求
const promises = []
for (let i = 0; i < 4; i++) { for (let i = 0; i < 4; i++) {
const promise = new Promise((resolve) => {
setTimeout(() => {
console.log(`🕐 发送第${i+1}次加钟请求`)
const result = tokenStore.sendMessage(tokenId, 'system_mysharecallback', {
isSkipShareCard: true,
type: 2
})
resolve(result)
}, i * 300) // 增加间隔时间确保稳定性
})
promises.push(promise)
}
// 等待所有请求完成
await Promise.all(promises)
console.log('🕐 所有加钟请求已发送')
// 延迟获取最新角色信息
setTimeout(() => {
console.log('🕐 加钟后获取最新角色信息')
tokenStore.sendMessage(tokenId, 'role_getroleinfo')
}, 1500)
// 延迟显示完成消息和重置状态
setTimeout(() => {
message.success('加钟操作已完成,请查看挂机剩余时间')
hangUp.value.isExtending = false
}, 2500)
} catch (error) {
console.error('🕐 加钟操作失败:', error)
message.error('加钟操作失败: ' + (error.message || '未知错误'))
hangUp.value.isExtending = false
}
}
const claimHangUpReward = async () => {
if (!tokenStore.selectedToken) {
message.warning('请先选择Token')
return
}
const tokenId = tokenStore.selectedToken.id
try {
console.log('🎁 开始领取挂机奖励...')
hangUp.value.isClaiming = true
message.info('正在领取挂机奖励...')
// 参考HangUpStatus的S函数逻辑
// 1. 发送初始分享回调
tokenStore.sendMessage(tokenId, 'system_mysharecallback')
// 2. 领取挂机奖励
setTimeout(() => {
tokenStore.sendMessage(tokenId, 'system_claimhangupreward')
}, 200)
// 3. 发送跳过分享卡片的回调
setTimeout(() => { setTimeout(() => {
tokenStore.sendMessage(tokenId, 'system_mysharecallback', { tokenStore.sendMessage(tokenId, 'system_mysharecallback', {
isSkipShareCard: true, isSkipShareCard: true,
type: 2 type: 2
}) })
}, i * 200) }, 400)
}
message.info('正在加钟...') // 4. 获取最新角色信息
} setTimeout(() => {
const claimHangUpReward = () => {
if (!tokenStore.selectedToken) return
const tokenId = tokenStore.selectedToken.id
// 领取挂机奖励
tokenStore.sendMessage(tokenId, 'system_mysharecallback')
tokenStore.sendMessage(tokenId, 'system_claimhangupreward')
tokenStore.sendMessage(tokenId, 'system_mysharecallback', {
isSkipShareCard: true,
type: 2
})
tokenStore.sendMessage(tokenId, 'role_getroleinfo') tokenStore.sendMessage(tokenId, 'role_getroleinfo')
}, 600)
message.info('领取挂机奖励') // 5. 显示完成消息并重置状态
setTimeout(() => {
message.success('挂机奖励领取完成')
hangUp.value.isClaiming = false
}, 1200)
console.log('🎁 挂机奖励领取操作序列已启动')
} catch (error) {
console.error('🎁 领取挂机奖励失败:', error)
message.error('领取挂机奖励失败: ' + (error.message || '未知错误'))
hangUp.value.isClaiming = false
}
} }
// 俱乐部排位报名 // 俱乐部排位报名

View File

@@ -1,7 +1,11 @@
<template> <template>
<div class="team-status-card"> <div class="team-status-card">
<div class="card-header"> <div class="card-header">
<img src="/icons/Ob7pyorzmHiJcbab2c25af264d0758b527bc1b61cc3b.png" alt="队伍图标" class="team-icon"> <img
src="/icons/Ob7pyorzmHiJcbab2c25af264d0758b527bc1b61cc3b.png"
alt="队伍图标"
class="team-icon"
>
<div class="team-info"> <div class="team-info">
<h3>队伍阵容</h3> <h3>队伍阵容</h3>
<p>当前使用的战斗阵容</p> <p>当前使用的战斗阵容</p>
@@ -10,18 +14,18 @@
<button <button
v-for="teamId in availableTeams" v-for="teamId in availableTeams"
:key="teamId" :key="teamId"
@click="selectTeam(teamId)"
:class="[ :class="[
'team-button', 'team-button',
{ active: currentTeam === teamId } { active: currentTeam === teamId }
]" ]"
@click="selectTeam(teamId)"
> >
{{ teamId }} {{ teamId }}
</button> </button>
<button <button
@click="refreshTeamData"
class="team-button refresh-button" class="team-button refresh-button"
title="刷新队伍数据" title="刷新队伍数据"
@click="refreshTeamData"
> >
🔄 🔄
</button> </button>
@@ -58,7 +62,10 @@
</div> </div>
</div> </div>
<div v-if="!currentTeamHeroes.length" class="empty-team"> <div
v-if="!currentTeamHeroes.length"
class="empty-team"
>
<p>暂无队伍信息</p> <p>暂无队伍信息</p>
</div> </div>
</div> </div>

View File

@@ -94,7 +94,7 @@
type="warning" type="warning"
@click="regenerateToken(roleId)" @click="regenerateToken(roleId)"
> >
重生成 刷新Token
</n-button> </n-button>
<n-button <n-button
size="tiny" size="tiny"
@@ -240,20 +240,85 @@ const toggleWebSocket = (roleId, tokenData) => {
} }
const regenerateToken = (roleId) => { const regenerateToken = (roleId) => {
const oldTokenData = localTokenStore.getGameToken(roleId)
if (!oldTokenData) {
message.error('找不到对应的Token数据')
return
}
// 检查是否有源URL可以重新获取
if (!oldTokenData.sourceUrl) {
message.warning('该Token没有配置源地址无法重新生成。请手动重新导入Token。')
return
}
dialog.info({ dialog.info({
title: '重新生成Token', title: '重新获取Token',
content: '确定要为此角色重新生成游戏Token吗', content: '确定要从源地址重新获取此角色的Token吗',
positiveText: '确定', positiveText: '确定',
negativeText: '取消', negativeText: '取消',
onPositiveClick: () => { onPositiveClick: async () => {
const oldTokenData = localTokenStore.getGameToken(roleId) try {
if (oldTokenData) { // 显示加载状态
const newToken = 'game_token_' + Date.now() + '_' + Math.random().toString(36).substr(2, 16) const loadingMsg = message.loading('正在重新获取Token...', { duration: 0 })
localTokenStore.updateGameToken(roleId, {
token: newToken, // 从源URL重新获取token
regeneratedAt: new Date().toISOString() let response
const sourceUrl = oldTokenData.sourceUrl
// 使用与TokenImport相同的跨域处理逻辑
const isLocalUrl = sourceUrl.startsWith(window.location.origin) ||
sourceUrl.startsWith('/') ||
sourceUrl.startsWith('http://localhost') ||
sourceUrl.startsWith('http://127.0.0.1')
if (isLocalUrl) {
response = await fetch(sourceUrl)
} else {
try {
response = await fetch(sourceUrl, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
mode: 'cors'
}) })
message.success('Token已重新生成') } catch (corsError) {
throw new Error(`跨域请求被阻止。请确保目标服务器支持CORS。错误详情: ${corsError.message}`)
}
}
if (!response.ok) {
throw new Error(`请求失败: ${response.status} ${response.statusText}`)
}
const data = await response.json()
if (!data.token) {
throw new Error('返回数据中未找到token字段')
}
// 更新token
localTokenStore.updateGameToken(roleId, {
token: data.token,
server: data.server || oldTokenData.server,
regeneratedAt: new Date().toISOString(),
lastRefreshed: new Date().toISOString()
})
// 如果当前token有连接需要重新连接
if (localTokenStore.getWebSocketStatus(roleId) === 'connected') {
localTokenStore.closeWebSocketConnection(roleId)
setTimeout(() => {
localTokenStore.createWebSocketConnection(roleId, data.token, oldTokenData.wsUrl)
}, 500)
}
loadingMsg.destroy()
message.success('Token已成功重新获取')
} catch (error) {
console.error('重新获取Token失败:', error)
message.error(error.message || 'Token重新获取失败')
} }
} }
}) })

View File

@@ -2,14 +2,22 @@
<div class="tower-status-card"> <div class="tower-status-card">
<div class="card-header"> <div class="card-header">
<div class="header-info"> <div class="header-info">
<img src="/icons/1733492491706148.png" alt="爬塔图标" class="tower-icon"> <img
src="/icons/1733492491706148.png"
alt="爬塔图标"
class="tower-icon"
>
<div class="tower-info"> <div class="tower-info">
<h3>咸将塔</h3> <h3>咸将塔</h3>
<p>一个不小心就过了</p> <p>一个不小心就过了</p>
</div> </div>
</div> </div>
<div class="energy-display"> <div class="energy-display">
<img src="/icons/xiaoyugan.png" alt="小鱼干" class="energy-icon"> <img
src="/icons/xiaoyugan.png"
alt="小鱼干"
class="energy-icon"
>
<span class="energy-count">{{ towerEnergy }}</span> <span class="energy-count">{{ towerEnergy }}</span>
</div> </div>
</div> </div>
@@ -33,7 +41,16 @@
:disabled="!canClimb" :disabled="!canClimb"
@click="startTowerClimb" @click="startTowerClimb"
> >
开始爬塔 {{ isClimbing.value ? '爬塔中...' : '开始爬塔' }}
</button>
<!-- 调试用的重置按钮只在开发环境显示 -->
<button
v-if="isClimbing.value"
class="reset-button"
@click="resetClimbingState"
>
重置状态
</button> </button>
</div> </div>
</div> </div>
@@ -49,6 +66,8 @@ const message = useMessage()
// 响应式数据 // 响应式数据
const isClimbing = ref(false) const isClimbing = ref(false)
const climbTimeout = ref(null) // 用于超时重置状态
const lastClimbResult = ref(null) // 最后一次爬塔结果
// 计算属性 - 从gameData中获取塔相关信息 // 计算属性 - 从gameData中获取塔相关信息
const roleInfo = computed(() => { const roleInfo = computed(() => {
@@ -97,11 +116,22 @@ const towerEnergy = computed(() => {
}) })
const canClimb = computed(() => { const canClimb = computed(() => {
return towerEnergy.value > 0 && !isClimbing.value const hasEnergy = towerEnergy.value > 0
const notClimbing = !isClimbing.value
console.log(`🗼 canClimb 计算: hasEnergy=${hasEnergy}, notClimbing=${notClimbing}, result=${hasEnergy && notClimbing}`)
return hasEnergy && notClimbing
}) })
// 方法 // 方法
const startTowerClimb = async () => { const startTowerClimb = async () => {
console.log('🗼 开始爬塔按钮被点击')
console.log('🗼 当前状态:', {
canClimb: canClimb.value,
isClimbing: isClimbing.value,
towerEnergy: towerEnergy.value,
hasSelectedToken: !!tokenStore.selectedToken
})
if (!tokenStore.selectedToken) { if (!tokenStore.selectedToken) {
message.warning('请先选择Token') message.warning('请先选择Token')
return return
@@ -112,15 +142,34 @@ const startTowerClimb = async () => {
return return
} }
try { // 清除之前的超时
if (climbTimeout.value) {
clearTimeout(climbTimeout.value)
climbTimeout.value = null
}
// 确保在操作开始前设置状态
console.log('🗼 设置爬塔状态为true')
isClimbing.value = true isClimbing.value = true
// 设置超时保护15秒后自动重置状态
climbTimeout.value = setTimeout(() => {
console.log('🗼 超时保护触发,自动重置爬塔状态')
isClimbing.value = false
climbTimeout.value = null
}, 15000)
try {
const tokenId = tokenStore.selectedToken.id const tokenId = tokenStore.selectedToken.id
console.log('🗼 使用Token ID:', tokenId)
message.info('开始爬塔挑战...') message.info('开始爬塔挑战...')
// 发送爬塔命令 // 发送爬塔命令
console.log('🗼 发送爬塔命令...')
await tokenStore.sendMessageWithPromise(tokenId, 'fight_starttower', {}, 10000) await tokenStore.sendMessageWithPromise(tokenId, 'fight_starttower', {}, 10000)
console.log('🗼 爬塔命令发送成功')
message.success('爬塔命令已发送') message.success('爬塔命令已发送')
// 立即查询塔信息以获取最新状态 // 立即查询塔信息以获取最新状态
@@ -131,14 +180,42 @@ const startTowerClimb = async () => {
setTimeout(async () => { setTimeout(async () => {
console.log('🗼 延迟查询塔信息') console.log('🗼 延迟查询塔信息')
await getTowerInfo() await getTowerInfo()
// 清除超时并重置状态
if (climbTimeout.value) {
clearTimeout(climbTimeout.value)
climbTimeout.value = null
}
console.log('🗼 延迟查询完成,重置爬塔状态')
isClimbing.value = false
}, 3000) }, 3000)
} catch (error) { } catch (error) {
console.error('爬塔失败:', error) console.error('🗼 爬塔失败:', error)
message.error('爬塔失败: ' + (error.message || '未知错误')) message.error('爬塔失败: ' + (error.message || '未知错误'))
} finally {
// 发生错误时立即重置状态
if (climbTimeout.value) {
clearTimeout(climbTimeout.value)
climbTimeout.value = null
}
console.log('🗼 发生错误,立即重置爬塔状态')
isClimbing.value = false isClimbing.value = false
} }
// 注意:不要在这里设置 isClimbing.value = false
// 因为我们要等待延迟查询完成后再重置状态
}
// 重置爬塔状态的方法
const resetClimbingState = () => {
console.log('🗼 用户手动重置爬塔状态')
if (climbTimeout.value) {
clearTimeout(climbTimeout.value)
climbTimeout.value = null
}
isClimbing.value = falsexian1xian
message.info('爬塔状态已重置')
} }
const getTowerInfo = async () => { const getTowerInfo = async () => {
@@ -218,6 +295,36 @@ watch(() => tokenStore.selectedToken, (newToken, oldToken) => {
} }
}) })
// 监听爬塔结果
watch(() => tokenStore.gameData.towerResult, (newResult, oldResult) => {
if (newResult && newResult.timestamp !== oldResult?.timestamp) {
console.log('🗼 收到新的爬塔结果:', newResult)
// 显示爬塔结果消息
if (newResult.success) {
message.success('咸将塔挑战成功!')
if (newResult.autoReward) {
setTimeout(() => {
message.success(`自动领取第${newResult.rewardFloor}层奖励`)
}, 1000)
}
} else {
message.error('咸将塔挑战失败')
}
// 重置爬塔状态
setTimeout(() => {
console.log('🗼 爬塔结果处理完成,重置状态')
if (climbTimeout.value) {
clearTimeout(climbTimeout.value)
climbTimeout.value = null
}
isClimbing.value = false
}, 2000)
}
}, { deep: true })
// 生命周期 // 生命周期
onMounted(() => { onMounted(() => {
console.log('🗼 TowerStatus 组件已挂载') console.log('🗼 TowerStatus 组件已挂载')
@@ -346,6 +453,9 @@ onMounted(() => {
.card-actions { .card-actions {
margin-top: var(--spacing-lg); margin-top: var(--spacing-lg);
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
} }
@@ -375,6 +485,24 @@ onMounted(() => {
} }
} }
.reset-button {
width: 100%;
padding: var(--spacing-xs) var(--spacing-sm);
font-size: var(--font-size-xs);
font-weight: var(--font-weight-medium);
border: 1px solid var(--warning-color);
border-radius: var(--border-radius-small);
background: transparent;
color: var(--warning-color);
cursor: pointer;
transition: all var(--transition-fast);
&:hover {
background: var(--warning-color);
color: white;
}
}
.debug-info { .debug-info {
margin-top: var(--spacing-sm); margin-top: var(--spacing-sm);
padding: var(--spacing-xs); padding: var(--spacing-xs);

View File

@@ -119,9 +119,6 @@ router.beforeEach((to, from, next) => {
// 检查是否需要Token // 检查是否需要Token
if (to.meta.requiresToken && !tokenStore.hasTokens) { if (to.meta.requiresToken && !tokenStore.hasTokens) {
next('/tokens') next('/tokens')
} else if (to.name === 'TokenImport' && tokenStore.hasTokens && tokenStore.selectedToken) {
// 如果已有token且已选择重定向到控制台
next('/dashboard')
} else if (to.path === '/' && tokenStore.hasTokens) { } else if (to.path === '/' && tokenStore.hasTokens) {
// 首页重定向逻辑 // 首页重定向逻辑
if (tokenStore.selectedToken) { if (tokenStore.selectedToken) {

View File

@@ -27,6 +27,11 @@ export const useTokenStore = defineStore('tokens', () => {
gameTokens.value.find(token => token.id === selectedTokenId.value) gameTokens.value.find(token => token.id === selectedTokenId.value)
) )
// 获取当前选中token的角色信息
const selectedTokenRoleInfo = computed(() => {
return gameData.value.roleInfo
})
// Token管理 // Token管理
const addToken = (tokenData) => { const addToken = (tokenData) => {
const newToken = { const newToken = {
@@ -118,7 +123,7 @@ export const useTokenStore = defineStore('tokens', () => {
// 辅助函数:尝试解析队伍数据 // 辅助函数:尝试解析队伍数据
const tryParseTeamData = (data, cmd) => { const tryParseTeamData = (data, cmd) => {
console.log(`👥 尝试解析队伍数据 [${cmd}]:`, data) // 静默解析,不打印详细日志
// 查找队伍相关字段 // 查找队伍相关字段
const teamFields = [] const teamFields = []
@@ -164,7 +169,7 @@ export const useTokenStore = defineStore('tokens', () => {
} }
}) })
} else { } else {
console.log(`👥 未找到明显的队伍字段,完整数据结构:`, analyzeDataStructure(data)) // 未找到队伍数据
} }
} }
@@ -182,46 +187,28 @@ export const useTokenStore = defineStore('tokens', () => {
message.decodedBody !== undefined ? message.decodedBody : message.decodedBody !== undefined ? message.decodedBody :
message.body message.body
console.log(`📋 处理消息 [${tokenId}] ${cmd}:`, { // 简化消息处理日志(移除详细结构信息)
hasRawData: message.rawData !== undefined, if (cmd !== '_sys/ack') { // 过滤心跳消息
hasDecodedBody: message.decodedBody !== undefined, console.log(`📋 处理 [${tokenId}] ${cmd}`, body ? '✓' : '✗')
hasBody: message.body !== undefined,
bodyType: body ? typeof body : 'undefined',
bodyContent: body,
originalCmd: message.cmd,
fullMessage: message
})
// 记录所有消息的原始命令名
console.log(`📨 收到消息 [${tokenId}] 原始cmd: "${message.cmd}", 处理cmd: "${cmd}"`)
// 特别记录所有包含tower的消息
if (cmd && cmd.includes('tower')) {
console.log(`🗼 发现塔相关消息 [${tokenId}] ${cmd}:`, message)
} }
// 过滤塔相关消息的详细打印
// 处理角色信息 - 支持多种可能的响应命令 // 处理角色信息 - 支持多种可能的响应命令
if (cmd === 'role_getroleinfo' || cmd === 'role_getroleinforesp' || cmd.includes('role') && cmd.includes('info')) { if (cmd === 'role_getroleinfo' || cmd === 'role_getroleinforesp' || cmd.includes('role') && cmd.includes('info')) {
console.log(`📊 匹配到角色信息命令: ${cmd}`) console.log(`📊 角色信息 [${tokenId}]`)
if (body) { if (body) {
gameData.value.roleInfo = body gameData.value.roleInfo = body
gameData.value.lastUpdated = new Date().toISOString() gameData.value.lastUpdated = new Date().toISOString()
console.log('📊 角色信息已更新:', body) console.log('📊 角色信息已更新')
console.log('📊 角色信息类型:', typeof body)
console.log('📊 角色信息内容概览:', Object.keys(body || {}))
// 特别检查塔信息 // 检查塔信息
if (body.role?.tower) { if (body.role?.tower) {
console.log('🗼 在角色信息中找到塔信息:', body.role.tower) // 塔信息已更新
} else if (body.tower) {
console.log('🗼 在响应根级别找到塔信息:', body.tower)
} else {
console.log('🗼 未找到塔信息在角色数据中')
console.log('📊 角色数据结构:', body.role ? Object.keys(body.role) : '没有role对象')
} }
} else { } else {
console.log('📊 角色信息响应body为空') console.log('📊 角色信息响应为空')
} }
} }
@@ -229,7 +216,7 @@ export const useTokenStore = defineStore('tokens', () => {
else if (cmd === 'legion_getinfo') { else if (cmd === 'legion_getinfo') {
if (body) { if (body) {
gameData.value.legionInfo = body gameData.value.legionInfo = body
console.log('🏛️ 军团信息已更新:', body) console.log('🏛️ 军团信息已更新')
} }
} }
@@ -239,7 +226,7 @@ export const useTokenStore = defineStore('tokens', () => {
cmd === 'presetteam_saveteam' || cmd === 'presetteam_saveteamresp' || cmd === 'presetteam_saveteam' || cmd === 'presetteam_saveteamresp' ||
cmd === 'role_gettargetteam' || cmd === 'role_gettargetteamresp' || cmd === 'role_gettargetteam' || cmd === 'role_gettargetteamresp' ||
(cmd && cmd.includes('presetteam')) || (cmd && cmd.includes('team'))) { (cmd && cmd.includes('presetteam')) || (cmd && cmd.includes('team'))) {
console.log(`👥 匹配到队伍信息命令: ${cmd}`) console.log(`👥 队伍信息 [${tokenId}] ${cmd}`)
if (body) { if (body) {
// 更新队伍数据 // 更新队伍数据
@@ -268,67 +255,133 @@ export const useTokenStore = defineStore('tokens', () => {
} }
gameData.value.lastUpdated = new Date().toISOString() gameData.value.lastUpdated = new Date().toISOString()
console.log('👥 队伍信息已更新:', { console.log('👥 队伍信息已更新')
cmd: cmd,
updatedData: gameData.value.presetTeam,
bodyKeys: Object.keys(body),
bodyContent: body
})
// 详细日志队伍数据结构 // 简化队伍数据结构日志
if (gameData.value.presetTeam.presetTeamInfo) { if (gameData.value.presetTeam.presetTeamInfo) {
console.log('👥 队伍详细结构:', { const teamCount = Object.keys(gameData.value.presetTeam.presetTeamInfo).length
teamCount: Object.keys(gameData.value.presetTeam.presetTeamInfo).length, console.log(`👥 队伍数量: ${teamCount}`)
teamIds: Object.keys(gameData.value.presetTeam.presetTeamInfo),
useTeamId: gameData.value.presetTeam.presetTeamInfo.useTeamId,
sampleTeam: gameData.value.presetTeam.presetTeamInfo[1] || gameData.value.presetTeam.presetTeamInfo[Object.keys(gameData.value.presetTeam.presetTeamInfo)[0]]
})
} }
} else { } else {
console.log('👥 队伍信息响应body为空') console.log('👥 队伍信息响应为空')
} }
} }
// 处理爬塔响应 // 处理爬塔响应(静默处理,保持功能)
else if (cmd === 'fight_starttower' || cmd === 'fight_starttowerresp') { else if (cmd === 'fight_starttower' || cmd === 'fight_starttowerresp') {
if (body) { if (body) {
console.log('🗼 爬塔响应:', body) // 判断爬塔结果
const battleData = body.battleData
if (battleData) {
const curHP = battleData.result?.sponsor?.ext?.curHP
const isSuccess = curHP > 0
// 保存爬塔结果到gameData中供组件使用
if (!gameData.value.towerResult) {
gameData.value.towerResult = {}
}
gameData.value.towerResult = {
success: isSuccess,
curHP: curHP,
towerId: battleData.options?.towerId,
timestamp: Date.now()
}
gameData.value.lastUpdated = new Date().toISOString()
if (isSuccess) {
// 检查是否需要自动领取奖励
const towerId = battleData.options?.towerId
if (towerId !== undefined) {
const layer = towerId % 10
const floor = Math.floor(towerId / 10)
// 如果是新层数的第一层(layer=0),检查是否有奖励可领取
if (layer === 0) {
setTimeout(() => {
const connection = wsConnections.value[tokenId]
if (connection && connection.status === 'connected' && connection.client) {
// 检查角色信息中的奖励状态
const roleInfo = gameData.value.roleInfo
const towerRewards = roleInfo?.role?.tower?.reward
if (towerRewards && !towerRewards[floor]) {
// 保存奖励信息
gameData.value.towerResult.autoReward = true
gameData.value.towerResult.rewardFloor = floor
connection.client.send('tower_claimreward', { rewardId: floor })
}
}
}, 1500)
}
}
}
}
// 爬塔后立即更新角色信息和塔信息 // 爬塔后立即更新角色信息和塔信息
setTimeout(() => { setTimeout(() => {
console.log('🗼 爬塔后自动更新数据')
try { try {
const connection = wsConnections.value[tokenId] const connection = wsConnections.value[tokenId]
if (connection && connection.status === 'connected' && connection.client) { if (connection && connection.status === 'connected' && connection.client) {
// 获取最新角色信息
console.log('🗼 正在请求角色信息...')
connection.client.send('role_getroleinfo', {}) connection.client.send('role_getroleinfo', {})
} else {
console.warn('🗼 WebSocket未连接无法更新数据')
} }
} catch (error) { } catch (error) {
console.warn('爬塔后更新数据失败:', error) // 忽略更新数据错误
} }
}, 1000) }, 1000)
} }
} }
// 处理心跳响应 // 处理奖励领取响应(静默处理)
else if (cmd === 'tower_claimreward' || cmd === 'tower_claimrewardresp') {
if (body) {
// 奖励领取成功后更新角色信息
setTimeout(() => {
const connection = wsConnections.value[tokenId]
if (connection && connection.status === 'connected' && connection.client) {
connection.client.send('role_getroleinfo', {})
}
}, 500)
}
}
// 处理加钟相关响应
else if (cmd === 'system_mysharecallback' || cmd === 'syncresp' || cmd === 'system_claimhangupreward' || cmd === 'system_claimhanguprewardresp') {
console.log(`🕐 加钟/挂机 [${tokenId}] ${cmd}`)
// 加钟操作完成后,延迟更新角色信息
if (cmd === 'syncresp' || cmd === 'system_mysharecallback') {
setTimeout(() => {
const connection = wsConnections.value[tokenId]
if (connection && connection.status === 'connected' && connection.client) {
connection.client.send('role_getroleinfo', {})
}
}, 800)
}
// 挂机奖励领取完成后更新角色信息
if (cmd === 'system_claimhanguprewardresp') {
setTimeout(() => {
const connection = wsConnections.value[tokenId]
if (connection && connection.status === 'connected' && connection.client) {
connection.client.send('role_getroleinfo', {})
}
}, 500)
}
}
// 处理心跳响应(静默处理,不打印日志)
else if (cmd === '_sys/ack') { else if (cmd === '_sys/ack') {
console.log(`💗 心跳响应 [${tokenId}]`) // 心跳响应 - 静默处理
return
} }
// 处理其他消息 // 处理其他消息
else { else {
console.log(`📋 收到游戏消息 [${tokenId}] ${cmd}:`, body) console.log(`📋 游戏消息 [${tokenId}] ${cmd}`)
// 特别关注队伍相关的未处理消息 // 特别关注队伍相关的未处理消息
if (cmd && (cmd.includes('team') || cmd.includes('preset') || cmd.includes('formation'))) { if (cmd && (cmd.includes('team') || cmd.includes('preset') || cmd.includes('formation'))) {
console.log(`👥 未处理队伍相关消息 [${tokenId}] ${cmd}:`, { console.log(`👥 未处理队伍消息 [${tokenId}] ${cmd}`)
originalMessage: message,
parsedBody: body,
messageKeys: Object.keys(message || {}),
bodyStructure: body ? analyzeDataStructure(body) : null
})
// 尝试自动解析队伍数据 // 尝试自动解析队伍数据
if (body && typeof body === 'object') { if (body && typeof body === 'object') {
@@ -336,42 +389,74 @@ export const useTokenStore = defineStore('tokens', () => {
} }
} }
// 特别关注塔相关的未处理消息 // 特别关注塔相关的未处理消息(静默处理)
if (cmd && cmd.includes('tower')) { if (cmd && cmd.includes('tower')) {
console.log(`🗼 未处理的塔相关消息 [${tokenId}] ${cmd}:`, { // 未处理塔消息
originalMessage: message,
parsedBody: body,
messageKeys: Object.keys(message || {})
})
} }
} }
} catch (error) { } catch (error) {
console.error('处理游戏消息失败:', error) console.error(`处理消息失败 [${tokenId}]:`, error.message)
} }
} }
// Base64解析功能 // 验证token有效性
const validateToken = (token) => {
if (!token) return false
if (typeof token !== 'string') return false
if (token.trim().length === 0) return false
// 简单检查token应该至少有一定长度
if (token.trim().length < 10) return false
return true
}
// Base64解析功能增强版
const parseBase64Token = (base64String) => { const parseBase64Token = (base64String) => {
try { try {
// 输入验证
if (!base64String || typeof base64String !== 'string') {
throw new Error('Token字符串无效')
}
// 移除可能的前缀和空格 // 移除可能的前缀和空格
const cleanBase64 = base64String.replace(/^data:.*base64,/, '').trim() const cleanBase64 = base64String.replace(/^data:.*base64,/, '').trim()
if (cleanBase64.length === 0) {
throw new Error('Token字符串为空')
}
// 解码base64 // 解码base64
const decoded = atob(cleanBase64) let decoded
try {
decoded = atob(cleanBase64)
} catch (decodeError) {
// 如果不是有效的Base64作为纯文本token处理
decoded = base64String.trim()
}
// 尝试解析为JSON // 尝试解析为JSON
let tokenData let tokenData
try { try {
tokenData = JSON.parse(decoded) tokenData = JSON.parse(decoded)
} catch { } catch {
// 如果不是JSON,当作纯token字符串处理 // 不是JSON格式,作为纯token处理
tokenData = { token: decoded } tokenData = { token: decoded }
} }
// 提取实际token
const actualToken = tokenData.token || tokenData.gameToken || decoded
// 验证token有效性
if (!validateToken(actualToken)) {
throw new Error(`提取的token无效: "${actualToken}"`)
}
return { return {
success: true, success: true,
data: tokenData data: {
...tokenData,
actualToken // 添加提取出的实际token
}
} }
} catch (error) { } catch (error) {
return { return {
@@ -385,22 +470,41 @@ export const useTokenStore = defineStore('tokens', () => {
const parseResult = parseBase64Token(base64String) const parseResult = parseBase64Token(base64String)
if (!parseResult.success) { if (!parseResult.success) {
return parseResult return {
success: false,
error: parseResult.error,
message: `Token "${name}" 导入失败: ${parseResult.error}`
}
} }
const tokenData = { const tokenData = {
name, name,
token: parseResult.data.token || parseResult.data.gameToken || base64String, token: parseResult.data.actualToken, // 使用验证过的实际token
...additionalInfo, ...additionalInfo,
...parseResult.data // 解析出的数据覆盖手动输入 ...parseResult.data // 解析出的数据覆盖手动输入
} }
try {
const newToken = addToken(tokenData) const newToken = addToken(tokenData)
// 添加更多验证信息到成功消息
const tokenInfo = parseResult.data.actualToken
const displayToken = tokenInfo.length > 20 ?
`${tokenInfo.substring(0, 10)}...${tokenInfo.substring(tokenInfo.length - 6)}` :
tokenInfo
return { return {
success: true, success: true,
data: newToken, data: newToken,
message: `Token "${name}" 导入成功` message: `Token "${name}" 导入成功`,
details: `实际Token: ${displayToken}`
}
} catch (error) {
return {
success: false,
error: error.message,
message: `Token "${name}" 添加失败: ${error.message}`
}
} }
} }
@@ -411,25 +515,21 @@ export const useTokenStore = defineStore('tokens', () => {
} }
try { try {
// 解析Base64获取实际Token // 使用统一的token解析逻辑
let actualToken = base64Token const parseResult = parseBase64Token(base64Token)
// 尝试解析Base64获取实际token let actualToken
try { if (parseResult.success) {
const cleanBase64 = base64Token.replace(/^data:.*base64,/, '').trim() actualToken = parseResult.data.actualToken
const decoded = atob(cleanBase64) // Token解析成功
} else {
// 尝试解析为JSON获取token字段 // Token解析失败使用原始token
try { // 如果解析失败尝试直接使用原始token
const tokenData = JSON.parse(decoded) if (validateToken(base64Token)) {
actualToken = tokenData.token || tokenData.gameToken || decoded
} catch {
// 如果不是JSON直接使用解码后的字符串
actualToken = decoded
}
} catch (error) {
console.warn('Base64解析失败使用原始token:', error.message)
actualToken = base64Token actualToken = base64Token
} else {
throw new Error(`Token无效: ${parseResult.error}`)
}
} }
// 使用固定的WebSocket基础地址将token带入占位符 // 使用固定的WebSocket基础地址将token带入占位符
@@ -498,13 +598,17 @@ export const useTokenStore = defineStore('tokens', () => {
// 设置消息监听 // 设置消息监听
wsClient.setMessageListener((message) => { wsClient.setMessageListener((message) => {
console.log(`📨 收到消息 [${tokenId}]:`, message) // 只打印消息命令,不打印完整结构
const cmd = message?.cmd || 'unknown'
if (cmd !== '_sys/ack') { // 过滤心跳消息
console.log(`📨 [${tokenId}] ${cmd}`)
}
// 更新连接状态中的最后接收消息 // 更新连接状态中的最后接收消息
if (wsConnections.value[tokenId]) { if (wsConnections.value[tokenId]) {
wsConnections.value[tokenId].lastMessage = { wsConnections.value[tokenId].lastMessage = {
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
data: message data: { cmd: message?.cmd } // 只保存命令名
} }
} }
@@ -542,6 +646,26 @@ export const useTokenStore = defineStore('tokens', () => {
return wsConnections.value[tokenId]?.client || null return wsConnections.value[tokenId]?.client || null
} }
// 设置消息监听器
const setMessageListener = (listener) => {
if (selectedToken.value) {
const connection = wsConnections.value[selectedToken.value.id]
if (connection && connection.client) {
connection.client.setMessageListener(listener)
}
}
}
// 设置是否显示消息
const setShowMsg = (show) => {
if (selectedToken.value) {
const connection = wsConnections.value[selectedToken.value.id]
if (connection && connection.client) {
connection.client.setShowMsg(show)
}
}
}
// 发送消息到WebSocket // 发送消息到WebSocket
const sendMessage = (tokenId, cmd, params = {}, options = {}) => { const sendMessage = (tokenId, cmd, params = {}, options = {}) => {
@@ -559,11 +683,11 @@ export const useTokenStore = defineStore('tokens', () => {
} }
client.send(cmd, params, options) client.send(cmd, params, options)
console.log(`📤 发送消息 [${tokenId}]: ${cmd}`, params) console.log(`📤 [${tokenId}] ${cmd}`)
return true return true
} catch (error) { } catch (error) {
console.error(`❌ 发送消息失败 [${tokenId}]:`, error) console.error(`❌ 发送失败 [${tokenId}] ${cmd}:`, error.message)
return false return false
} }
} }
@@ -641,7 +765,7 @@ export const useTokenStore = defineStore('tokens', () => {
// 可能的塔层数字段(根据实际数据结构调整) // 可能的塔层数字段(根据实际数据结构调整)
const level = tower.level || tower.currentLevel || tower.floor || tower.stage const level = tower.level || tower.currentLevel || tower.floor || tower.stage
console.log('🗼 当前塔层数:', level, '塔信息:', tower) // 当前塔层数
return level return level
} catch (error) { } catch (error) {
console.error('❌ 获取塔层数失败:', error) console.error('❌ 获取塔层数失败:', error)
@@ -729,7 +853,7 @@ export const useTokenStore = defineStore('tokens', () => {
try { try {
gameTokens.value = JSON.parse(savedTokens) gameTokens.value = JSON.parse(savedTokens)
} catch (error) { } catch (error) {
console.error('解析Token数据失败:', error) console.error('解析Token数据失败:', error.message)
gameTokens.value = [] gameTokens.value = []
} }
} }
@@ -752,6 +876,7 @@ export const useTokenStore = defineStore('tokens', () => {
// 计算属性 // 计算属性
hasTokens, hasTokens,
selectedToken, selectedToken,
selectedTokenRoleInfo,
// Token管理方法 // Token管理方法
addToken, addToken,
@@ -770,6 +895,8 @@ export const useTokenStore = defineStore('tokens', () => {
getWebSocketClient, getWebSocketClient,
sendMessage, sendMessage,
sendMessageWithPromise, sendMessageWithPromise,
setMessageListener,
setShowMsg,
sendHeartbeat, sendHeartbeat,
sendGetRoleInfo, sendGetRoleInfo,
sendGetDataBundleVersion, sendGetDataBundleVersion,
@@ -787,6 +914,20 @@ export const useTokenStore = defineStore('tokens', () => {
// 塔信息方法 // 塔信息方法
getCurrentTowerLevel, getCurrentTowerLevel,
getTowerInfo getTowerInfo,
// 调试工具方法
validateToken,
debugToken: (tokenString) => {
console.log('🔍 Token调试信息:')
console.log('原始Token:', tokenString)
const parseResult = parseBase64Token(tokenString)
console.log('解析结果:', parseResult)
if (parseResult.success) {
console.log('实际Token:', parseResult.data.actualToken)
console.log('Token有效性:', validateToken(parseResult.data.actualToken))
}
return parseResult
}
} }
}) })

View File

@@ -87,6 +87,7 @@ export function registerDefaultCommands(reg) {
.register("system_buygold", { buyNum: 1 }) .register("system_buygold", { buyNum: 1 })
.register("system_claimhangupreward") .register("system_claimhangupreward")
.register("system_signinreward") .register("system_signinreward")
.register("system_mysharecallback", { isSkipShareCard: true, type: 2 })
// 任务相关 // 任务相关
.register("task_claimdailypoint", { taskId: 1 }) .register("task_claimdailypoint", { taskId: 1 })
@@ -107,6 +108,7 @@ export function registerDefaultCommands(reg) {
// 商店 // 商店
.register("store_goodslist", { storeId: 1 }) .register("store_goodslist", { storeId: 1 })
.register("store_buy", { goodsId: 1 }) .register("store_buy", { goodsId: 1 })
.register("store_purchase", { goodsId: 1 })
.register("store_refresh", { storeId: 1 }) .register("store_refresh", { storeId: 1 })
// 军团 // 军团
@@ -142,14 +144,23 @@ export function registerDefaultCommands(reg) {
// 神器抽奖 // 神器抽奖
.register("artifact_lottery", { lotteryNumber: 1, newFree: true, type: 1 }) .register("artifact_lottery", { lotteryNumber: 1, newFree: true, type: 1 })
// 灯神相关
.register("genie_sweep", { genieId: 1 })
.register("genie_buysweep")
// 礼包相关
.register("discount_claimreward", { discountId: 1 })
.register("card_claimreward", { cardId: 1 })
// 爬塔相关 // 爬塔相关
.register("tower_getinfo") .register("tower_getinfo")
.register("tower_claimreward") .register("tower_claimreward")
// 队伍相关 // 队伍相关
.register("presetteam_getinfo")
.register("presetteam_getteam") .register("presetteam_getteam")
.register("presetteam_setteam") .register("presetteam_setteam")
.register("presetteam_saveteam") .register("presetteam_saveteam", { teamId: 1 })
.register("role_gettargetteam") .register("role_gettargetteam")
// 排名相关 // 排名相关
@@ -184,12 +195,7 @@ export class XyzwWebSocketClient {
this.promises = Object.create(null) this.promises = Object.create(null)
this.registry = registerDefaultCommands(new CommandRegistry(this.utils, this.enc)) this.registry = registerDefaultCommands(new CommandRegistry(this.utils, this.enc))
console.log('🔧 WebSocket客户端初始化:', { // WebSocket客户端初始化
url: this.url,
hasUtils: !!this.utils,
hasEnc: !!this.enc,
hasEncoder: !!this.utils?.encode
})
// 状态回调 // 状态回调
this.onConnect = null this.onConnect = null
@@ -199,16 +205,16 @@ export class XyzwWebSocketClient {
/** 初始化连接 */ /** 初始化连接 */
init() { init() {
console.log(`🔗 连接 WebSocket: ${this.url}`) console.log(`🔗 连接: ${this.url.split('?')[0]}`)
this.socket = new WebSocket(this.url) this.socket = new WebSocket(this.url)
this.socket.onopen = () => { this.socket.onopen = () => {
console.log(` WebSocket 连接成功`) console.log(`✅ 连接成功`)
this.connected = true this.connected = true
console.log(`🔄 启动心跳机制,间隔: ${this.heartbeatInterval}ms`) // 启动心跳机制
this._setupHeartbeat() this._setupHeartbeat()
console.log(`🔄 启动消息队列处理`) // 启动消息队列处理
this._processQueueLoop() this._processQueueLoop()
if (this.onConnect) this.onConnect() if (this.onConnect) this.onConnect()
} }
@@ -223,32 +229,32 @@ export class XyzwWebSocketClient {
packet = this.utils?.parse ? this.utils.parse(evt.data, "auto") : evt.data packet = this.utils?.parse ? this.utils.parse(evt.data, "auto") : evt.data
} else if (evt.data instanceof Blob) { } else if (evt.data instanceof Blob) {
// 处理Blob数据 // 处理Blob数据
console.log('📦 收到Blob数据, 大小:', evt.data.size) // 收到Blob数据
evt.data.arrayBuffer().then(buffer => { evt.data.arrayBuffer().then(buffer => {
try { try {
packet = this.utils?.parse ? this.utils.parse(buffer, "auto") : buffer packet = this.utils?.parse ? this.utils.parse(buffer, "auto") : buffer
console.log('📦 Blob解析结果:', packet) // Blob解析完成
// 处理消息体解码ProtoMsg会自动解码 // 处理消息体解码ProtoMsg会自动解码
if (packet instanceof Object && packet.rawData !== undefined) { if (packet instanceof Object && packet.rawData !== undefined) {
console.log('✅ ProtoMsg消息使用rawData:', packet.rawData) // ProtoMsg消息
} else if (packet.body && packet.body instanceof Uint8Array) { } else if (packet.body && packet.body instanceof Uint8Array) {
try { try {
if (this.utils && this.utils.bon && this.utils.bon.decode) { if (this.utils && this.utils.bon && this.utils.bon.decode) {
const decodedBody = this.utils.bon.decode(packet.body) const decodedBody = this.utils.bon.decode(packet.body)
console.log('✅ 手动解码消息体成功:', decodedBody) // 手动解码成功
// 不修改packet.body而是创建一个新的属性存储解码后的数据 // 不修改packet.body而是创建一个新的属性存储解码后的数据
packet.decodedBody = decodedBody packet.decodedBody = decodedBody
} else { } else {
console.warn('⚠️ BON解码器不可用:', this.utils) // BON解码器不可用
} }
} catch (error) { } catch (error) {
console.warn('❌ 消息体解码失败:', error) // 消息体解码失败
} }
} }
if (this.showMsg) { if (this.showMsg) {
console.log(`📨 收到消息(Blob解析后):`, packet) // 收到Blob消息
} }
// 回调处理 // 回调处理
@@ -260,7 +266,7 @@ export class XyzwWebSocketClient {
this._handlePromiseResponse(packet) this._handlePromiseResponse(packet)
} catch (error) { } catch (error) {
console.error('Blob解析失败:', error) console.error('Blob解析失败:', error.message)
} }
}) })
return // 异步处理,直接返回 return // 异步处理,直接返回
@@ -280,14 +286,14 @@ export class XyzwWebSocketClient {
try { try {
if (this.utils && this.utils.bon && this.utils.bon.decode) { if (this.utils && this.utils.bon && this.utils.bon.decode) {
const decodedBody = this.utils.bon.decode(packet.body) const decodedBody = this.utils.bon.decode(packet.body)
console.log('✅ 手动解码消息体成功:', decodedBody) // 手动解码成功
// 不修改packet.body而是创建一个新的属性存储解码后的数据 // 不修改packet.body而是创建一个新的属性存储解码后的数据
packet.decodedBody = decodedBody packet.decodedBody = decodedBody
} else { } else {
console.warn('⚠️ BON解码器不可用:', this.utils) // BON解码器不可用
} }
} catch (error) { } catch (error) {
console.warn('❌ 消息体解码失败:', error) // 消息体解码失败
} }
} }
@@ -300,7 +306,7 @@ export class XyzwWebSocketClient {
this._handlePromiseResponse(packet) this._handlePromiseResponse(packet)
} catch (error) { } catch (error) {
console.error(`消息处理失败:`, error) console.error(`消息处理失败:`, error.message)
} }
} }

View File

@@ -101,7 +101,7 @@
</n-button> </n-button>
<n-button <n-button
size="large" size="large"
@click="router.push('/tokens')" @click="handleManageTokens"
> >
管理Token 管理Token
</n-button> </n-button>
@@ -366,13 +366,30 @@ const handleUserAction = (key) => {
} }
} }
const handleManageTokens = () => {
console.log('🔘 点击管理Token按钮')
console.log('📊 当前Token状态:', {
hasTokens: tokenStore.hasTokens,
selectedToken: tokenStore.selectedToken?.name,
tokenCount: tokenStore.gameTokens.length
})
try {
router.push('/tokens')
console.log('✅ 成功导航到 /tokens')
} catch (error) {
console.error('❌ 导航失败:', error)
message.error('导航到Token管理页面失败')
}
}
const handleQuickAction = (action) => { const handleQuickAction = (action) => {
switch (action.action) { switch (action.action) {
case 'game-features': case 'game-features':
router.push('/game-features') router.push('/game-features')
break break
case 'add-token': case 'add-token':
router.push('/tokens') handleManageTokens()
break break
case 'execute-tasks': case 'execute-tasks':
router.push('/game-features') router.push('/game-features')

View File

@@ -30,8 +30,7 @@
<div <div
v-if="showFeedback" v-if="showFeedback"
class="feedback-section" class="feedback-section"
> />
</div>
<!-- 功能模块网格 --> <!-- 功能模块网格 -->
<div class="features-grid-section"> <div class="features-grid-section">

View File

@@ -14,8 +14,8 @@
<n-button <n-button
circle circle
size="small" size="small"
@click="toggleTheme"
class="theme-toggle" class="theme-toggle"
@click="toggleTheme"
> >
<template #icon> <template #icon>
<n-icon v-if="isDarkTheme"> <n-icon v-if="isDarkTheme">
@@ -56,7 +56,6 @@
URL获取 URL获取
</n-radio-button> </n-radio-button>
</n-radio-group> </n-radio-group>
</div> </div>
<!-- 手动输入表单 --> <!-- 手动输入表单 -->
@@ -175,9 +174,14 @@
clearable clearable
/> />
<template #feedback> <template #feedback>
<div class="form-tips">
<span class="form-tip"> <span class="form-tip">
接口应返回包含token字段的JSON数据 接口应返回包含token字段的JSON数据
</span> </span>
<span class="form-tip cors-tip">
注意如果是跨域URL服务器需要支持CORS否则会被浏览器阻止
</span>
</div>
</template> </template>
</n-form-item> </n-form-item>
@@ -240,6 +244,17 @@
<div class="section-header"> <div class="section-header">
<h2>我的Token列表 ({{ tokenStore.gameTokens.length }})</h2> <h2>我的Token列表 ({{ tokenStore.gameTokens.length }})</h2>
<div class="header-actions"> <div class="header-actions">
<n-button
v-if="tokenStore.selectedToken"
type="success"
@click="goToDashboard"
>
<template #icon>
<n-icon><Home /></n-icon>
</template>
返回控制台
</n-button>
<n-button <n-button
v-if="!showImportForm" v-if="!showImportForm"
type="primary" type="primary"
@@ -386,15 +401,6 @@
<Key /> <Key />
</n-icon> </n-icon>
</template> </template>
<template #extra>
<n-button
type="primary"
size="large"
@click="showImportForm = true"
>
导入第一个Token
</n-button>
</template>
</n-empty> </n-empty>
</div> </div>
</div> </div>
@@ -457,7 +463,8 @@ import {
Key, Key,
Refresh, Refresh,
Sunny, Sunny,
Moon Moon,
Home
} from '@vicons/ionicons5' } from '@vicons/ionicons5'
const router = useRouter() const router = useRouter()
@@ -572,10 +579,20 @@ const handleImport = async () => {
if (result.success) { if (result.success) {
message.success(result.message) message.success(result.message)
// 显示token详情信息如果有
if (result.details) {
console.log('Token导入详情:', result.details)
}
resetImportForm() resetImportForm()
showImportForm.value = false showImportForm.value = false
} else { } else {
message.error(result.error || result.message) const errorMsg = result.error || result.message || 'Token导入失败'
message.error(errorMsg)
console.error('Token导入错误详情:', {
error: result.error,
message: result.message,
originalToken: importForm.base64Token?.substring(0, 50) + '...'
})
} }
} catch (error) { } catch (error) {
// 表单验证失败 // 表单验证失败
@@ -592,8 +609,33 @@ const handleUrlImport = async () => {
await urlFormRef.value.validate() await urlFormRef.value.validate()
isImporting.value = true isImporting.value = true
// 获取Token数据 // 获取Token数据 - 处理跨域问题
const response = await fetch(urlForm.url) let response
// 检查是否为本地或相同域名的URL
const isLocalUrl = urlForm.url.startsWith(window.location.origin) ||
urlForm.url.startsWith('/') ||
urlForm.url.startsWith('http://localhost') ||
urlForm.url.startsWith('http://127.0.0.1')
if (isLocalUrl) {
// 本地URL直接请求
response = await fetch(urlForm.url)
} else {
// 跨域URL - 尝试CORS请求
try {
response = await fetch(urlForm.url, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
mode: 'cors'
})
} catch (corsError) {
throw new Error(`跨域请求被阻止。请确保目标服务器支持CORS或使用浏览器扩展/代理服务器。错误详情: ${corsError.message}`)
}
}
if (!response.ok) { if (!response.ok) {
throw new Error(`请求失败: ${response.status} ${response.statusText}`) throw new Error(`请求失败: ${response.status} ${response.statusText}`)
} }
@@ -618,10 +660,21 @@ const handleUrlImport = async () => {
if (result.success) { if (result.success) {
message.success(result.message) message.success(result.message)
// 显示token详情信息如果有
if (result.details) {
console.log('URL Token导入详情:', result.details)
}
resetUrlForm() resetUrlForm()
showImportForm.value = false showImportForm.value = false
} else { } else {
message.error(result.error || result.message) const errorMsg = result.error || result.message || 'URL Token导入失败'
message.error(errorMsg)
console.error('URL Token导入错误详情:', {
error: result.error,
message: result.message,
sourceUrl: urlForm.url,
receivedToken: data?.token?.substring(0, 50) + '...'
})
} }
} catch (error) { } catch (error) {
console.error('URL获取Token失败:', error) console.error('URL获取Token失败:', error)
@@ -641,7 +694,30 @@ const refreshToken = async (token) => {
refreshingTokens.value.add(token.id) refreshingTokens.value.add(token.id)
try { try {
const response = await fetch(token.sourceUrl) // 使用与导入相同的跨域处理逻辑
let response
const isLocalUrl = token.sourceUrl.startsWith(window.location.origin) ||
token.sourceUrl.startsWith('/') ||
token.sourceUrl.startsWith('http://localhost') ||
token.sourceUrl.startsWith('http://127.0.0.1')
if (isLocalUrl) {
response = await fetch(token.sourceUrl)
} else {
try {
response = await fetch(token.sourceUrl, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
mode: 'cors'
})
} catch (corsError) {
throw new Error(`跨域请求被阻止。请确保目标服务器支持CORS。错误详情: ${corsError.message}`)
}
}
if (!response.ok) { if (!response.ok) {
throw new Error(`请求失败: ${response.status} ${response.statusText}`) throw new Error(`请求失败: ${response.status} ${response.statusText}`)
} }
@@ -1031,11 +1107,22 @@ onMounted(() => {
} }
} }
.form-tips {
display: flex;
flex-direction: column;
gap: var(--spacing-xs);
}
.form-tip { .form-tip {
color: var(--text-tertiary); color: var(--text-tertiary);
font-size: var(--font-size-sm); font-size: var(--font-size-sm);
} }
.cors-tip {
color: var(--warning-color);
font-weight: var(--font-weight-medium);
}
.connection-actions { .connection-actions {
display: flex; display: flex;
gap: var(--spacing-xs); gap: var(--spacing-xs);