fix:修复若干bug
This commit is contained in:
@@ -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
|
||||||
|
ref="logContainer"
|
||||||
|
class="log-container"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-for="log in executionLogs"
|
v-for="logItem in logList"
|
||||||
:key="log.id"
|
: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 })
|
||||||
|
|
||||||
// 更新任务完成状态
|
|
||||||
const updateTaskStatus = () => {
|
|
||||||
if (!dailyTaskData.value?.complete) return
|
|
||||||
|
|
||||||
const completed = dailyTaskData.value.complete
|
if (logList.value.length > LOG_MAX) {
|
||||||
dailyTasks.value.forEach(task => {
|
logList.value.splice(0, logList.value.length - LOG_MAX)
|
||||||
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?.('检查每日任务完成情况')
|
||||||
|
|
||||||
// 初始化设置
|
// 1) 已完成任务 → 生成要跳过的指令"指纹"
|
||||||
const initSettings = () => {
|
const officialList = [
|
||||||
const saved = localStorage.getItem('taskSettings')
|
{ id: 1, name: '登录一次游戏', cmds: [] },
|
||||||
if (saved) {
|
{ id: 2, name: '分享一次游戏', cmds: ['system_mysharecallback'] },
|
||||||
try {
|
{ id: 3, name: '赠送好友3次金币', cmds: ['friend_batch'] },
|
||||||
taskSettings.value = { ...taskSettings.value, ...JSON.parse(saved) }
|
{ id: 4, name: '进行2次招募', cmds: ['hero_recruit'] },
|
||||||
} catch (error) {
|
{ id: 5, name: '领取5次挂机奖励', cmds: ['system_claimhangupreward'] },
|
||||||
console.error('加载设置失败:', error)
|
{ 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 {
|
||||||
|
await callWithRetry(() => tokenStore.sendMessageWithPromise(tokenId, it.cmd, it.params || {}))
|
||||||
|
if (it.successMsg) logFn?.(it.successMsg, 'success')
|
||||||
|
} 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生命周期
|
// 辅助函数 - 基于参考代码
|
||||||
onMounted(() => {
|
const fpOf = (item) => {
|
||||||
initSettings()
|
return `${item.cmd}|${JSON.stringify(item.params ?? {})}`
|
||||||
updateTaskStatus()
|
}
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(`daily-settings:${roleId}`, JSON.stringify(s))
|
||||||
|
} 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
for (let i = 0; i < 4; i++) {
|
console.log('🕐 开始加钟操作...')
|
||||||
|
hangUp.value.isExtending = true
|
||||||
|
message.info('正在加钟...')
|
||||||
|
|
||||||
|
// 按照参考代码的逻辑,发送4次分享回调请求
|
||||||
|
const promises = []
|
||||||
|
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)
|
||||||
|
|
||||||
|
// 4. 获取最新角色信息
|
||||||
|
setTimeout(() => {
|
||||||
|
tokenStore.sendMessage(tokenId, 'role_getroleinfo')
|
||||||
|
}, 600)
|
||||||
|
|
||||||
|
// 5. 显示完成消息并重置状态
|
||||||
|
setTimeout(() => {
|
||||||
|
message.success('挂机奖励领取完成')
|
||||||
|
hangUp.value.isClaiming = false
|
||||||
|
}, 1200)
|
||||||
|
|
||||||
|
console.log('🎁 挂机奖励领取操作序列已启动')
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('🎁 领取挂机奖励失败:', error)
|
||||||
|
message.error('领取挂机奖励失败: ' + (error.message || '未知错误'))
|
||||||
|
hangUp.value.isClaiming = false
|
||||||
}
|
}
|
||||||
|
|
||||||
message.info('正在加钟...')
|
|
||||||
}
|
|
||||||
|
|
||||||
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')
|
|
||||||
|
|
||||||
message.info('领取挂机奖励')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 俱乐部排位报名
|
// 俱乐部排位报名
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 })
|
||||||
|
|
||||||
|
// 从源URL重新获取token
|
||||||
|
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'
|
||||||
|
})
|
||||||
|
} 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, {
|
localTokenStore.updateGameToken(roleId, {
|
||||||
token: newToken,
|
token: data.token,
|
||||||
regeneratedAt: new Date().toISOString()
|
server: data.server || oldTokenData.server,
|
||||||
|
regeneratedAt: new Date().toISOString(),
|
||||||
|
lastRefreshed: new Date().toISOString()
|
||||||
})
|
})
|
||||||
message.success('Token已重新生成')
|
|
||||||
|
// 如果当前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重新获取失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 清除之前的超时
|
||||||
|
if (climbTimeout.value) {
|
||||||
|
clearTimeout(climbTimeout.value)
|
||||||
|
climbTimeout.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保在操作开始前设置状态
|
||||||
|
console.log('🗼 设置爬塔状态为true')
|
||||||
|
isClimbing.value = true
|
||||||
|
|
||||||
|
// 设置超时保护,15秒后自动重置状态
|
||||||
|
climbTimeout.value = setTimeout(() => {
|
||||||
|
console.log('🗼 超时保护触发,自动重置爬塔状态')
|
||||||
|
isClimbing.value = false
|
||||||
|
climbTimeout.value = null
|
||||||
|
}, 15000)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isClimbing.value = true
|
|
||||||
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);
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ export const useTokenStore = defineStore('tokens', () => {
|
|||||||
const selectedToken = computed(() =>
|
const selectedToken = computed(() =>
|
||||||
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) => {
|
||||||
@@ -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 // 解析出的数据覆盖手动输入
|
||||||
}
|
}
|
||||||
|
|
||||||
const newToken = addToken(tokenData)
|
try {
|
||||||
|
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
|
actualToken = base64Token
|
||||||
} catch {
|
} else {
|
||||||
// 如果不是JSON,直接使用解码后的字符串
|
throw new Error(`Token无效: ${parseResult.error}`)
|
||||||
actualToken = decoded
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.warn('Base64解析失败,使用原始token:', error.message)
|
|
||||||
actualToken = base64Token
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用固定的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 } // 只保存命令名
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,6 +645,26 @@ export const useTokenStore = defineStore('tokens', () => {
|
|||||||
const getWebSocketClient = (tokenId) => {
|
const getWebSocketClient = (tokenId) => {
|
||||||
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
|
||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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 })
|
||||||
|
|
||||||
// 军团
|
// 军团
|
||||||
@@ -141,15 +143,24 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
<span class="form-tip">
|
<div class="form-tips">
|
||||||
接口应返回包含token字段的JSON数据
|
<span class="form-tip">
|
||||||
</span>
|
接口应返回包含token字段的JSON数据
|
||||||
|
</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);
|
||||||
|
|||||||
Reference in New Issue
Block a user