feat(DailyTasks): 进入日常页面时自动加载阵容数据- 新增 loadTeamDataWithConnection 函数,用于检查 WebSocket 连接状态并加载阵容数据
- 在页面进入时调用该函数,确保 WebSocket 连接成功后再加载阵容数据 - 增加重试机制,提高加载成功率 - 优化错误处理,提升用户体验
This commit is contained in:
@@ -55,7 +55,7 @@
|
|||||||
<!-- 一键执行按钮 -->
|
<!-- 一键执行按钮 -->
|
||||||
<button
|
<button
|
||||||
class="execute-button"
|
class="execute-button"
|
||||||
:disabled="busy"
|
:disabled="busy || !isConnected"
|
||||||
@click="runDailyFix"
|
@click="runDailyFix"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@@ -73,6 +73,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
执行中...
|
执行中...
|
||||||
</span>
|
</span>
|
||||||
|
<span v-else-if="!isConnected">WebSocket未连接</span>
|
||||||
<span v-else>一键补差</span>
|
<span v-else>一键补差</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -158,6 +159,12 @@
|
|||||||
v-model:value="settings.claimEmail"
|
v-model:value="settings.claimEmail"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="switch-row">
|
||||||
|
<span class="switch-label">黑市购买物品</span>
|
||||||
|
<n-switch
|
||||||
|
v-model:value="settings.blackMarketPurchase"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="switch-row">
|
<div class="switch-row">
|
||||||
<span class="switch-label">付费招募</span>
|
<span class="switch-label">付费招募</span>
|
||||||
@@ -181,6 +188,14 @@
|
|||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<n-icon><Calendar /></n-icon>
|
<n-icon><Calendar /></n-icon>
|
||||||
<span>每日任务详情</span>
|
<span>每日任务详情</span>
|
||||||
|
<button
|
||||||
|
class="refresh-button"
|
||||||
|
:disabled="busy"
|
||||||
|
@click="handleRefreshTaskStatus"
|
||||||
|
>
|
||||||
|
<n-icon><Refresh /></n-icon>
|
||||||
|
刷新状态
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -238,7 +253,8 @@
|
|||||||
class="log-message"
|
class="log-message"
|
||||||
:class="{
|
:class="{
|
||||||
error: logItem.type === 'error',
|
error: logItem.type === 'error',
|
||||||
success: logItem.type === 'success'
|
success: logItem.type === 'success',
|
||||||
|
warning: logItem.type === 'warning'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
{{ logItem.message }}
|
{{ logItem.message }}
|
||||||
@@ -258,7 +274,8 @@ import {
|
|||||||
Calendar,
|
Calendar,
|
||||||
CheckmarkCircle,
|
CheckmarkCircle,
|
||||||
EllipseOutline,
|
EllipseOutline,
|
||||||
DocumentText
|
DocumentText,
|
||||||
|
Refresh
|
||||||
} from '@vicons/ionicons5'
|
} from '@vicons/ionicons5'
|
||||||
|
|
||||||
const tokenStore = useTokenStore()
|
const tokenStore = useTokenStore()
|
||||||
@@ -271,7 +288,7 @@ const showLog = ref(false)
|
|||||||
const busy = ref(false)
|
const busy = ref(false)
|
||||||
const logContainer = ref(null)
|
const logContainer = ref(null)
|
||||||
|
|
||||||
// 任务设置 - 基于参考代码
|
// 任务设置
|
||||||
const settings = reactive({
|
const settings = reactive({
|
||||||
arenaFormation: 1,
|
arenaFormation: 1,
|
||||||
bossFormation: 1,
|
bossFormation: 1,
|
||||||
@@ -281,10 +298,11 @@ const settings = reactive({
|
|||||||
openBox: true,
|
openBox: true,
|
||||||
arenaEnable: true,
|
arenaEnable: true,
|
||||||
claimHangUp: true,
|
claimHangUp: true,
|
||||||
claimEmail: true
|
claimEmail: true,
|
||||||
|
blackMarketPurchase: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// 每日任务列表 - 基于参考代码
|
// 每日任务列表
|
||||||
const tasks = 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 },
|
||||||
@@ -302,7 +320,7 @@ const tasks = ref([
|
|||||||
const formationOptions = [1,2,3,4].map(v => ({ label: `阵容${v}`, value: v }))
|
const formationOptions = [1,2,3,4].map(v => ({ label: `阵容${v}`, value: v }))
|
||||||
const bossTimesOptions = [0,1,2,3,4].map(v => ({ label: `${v}次`, value: v }))
|
const bossTimesOptions = [0,1,2,3,4].map(v => ({ label: `${v}次`, value: v }))
|
||||||
|
|
||||||
// 计算属性 - 基于参考代码逻辑
|
// 计算属性
|
||||||
const roleInfo = computed(() => {
|
const roleInfo = computed(() => {
|
||||||
return tokenStore.selectedTokenRoleInfo
|
return tokenStore.selectedTokenRoleInfo
|
||||||
})
|
})
|
||||||
@@ -311,12 +329,18 @@ const roleDailyPoint = computed(() => {
|
|||||||
return roleInfo.value?.role?.dailyTask?.dailyPoint ?? 0
|
return roleInfo.value?.role?.dailyTask?.dailyPoint ?? 0
|
||||||
})
|
})
|
||||||
|
|
||||||
// 进度 - 基于参考代码
|
|
||||||
const dailyPoint = computed(() => Math.min(roleDailyPoint.value, 100))
|
const dailyPoint = computed(() => Math.min(roleDailyPoint.value, 100))
|
||||||
const isFull = computed(() => dailyPoint.value >= 100)
|
const isFull = computed(() => dailyPoint.value >= 100)
|
||||||
const progressColor = computed(() => isFull.value ? '#10b981' : '#3b82f6')
|
const progressColor = computed(() => isFull.value ? '#10b981' : '#3b82f6')
|
||||||
|
|
||||||
// 日志系统 - 基于参考代码的log函数
|
// WebSocket连接状态
|
||||||
|
const isConnected = computed(() => {
|
||||||
|
if (!tokenStore.selectedToken) return false
|
||||||
|
const status = tokenStore.getWebSocketStatus(tokenStore.selectedToken.id)
|
||||||
|
return status === 'connected'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 日志系统
|
||||||
const logList = ref([])
|
const logList = ref([])
|
||||||
const LOG_MAX = 500
|
const LOG_MAX = 500
|
||||||
|
|
||||||
@@ -335,82 +359,468 @@ const log = (message, type = 'info') => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 超时执行器 - 使用tokenStore的sendMessageWithPromise
|
// 同步服务器任务完成状态
|
||||||
const callWithRetry = async (fn, opt = {}) => {
|
|
||||||
const timeoutMs = opt?.timeoutMs ?? 10000 // 降低超时时间到10秒
|
|
||||||
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 onRaw = (evt) => {
|
|
||||||
const err = evt?._raw?.error
|
|
||||||
if (err) log(String(err), 'error')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 同步服务器任务完成状态 - 基于参考代码的 syncCompleteFromServer 函数
|
|
||||||
const syncCompleteFromServer = (resp) => {
|
const syncCompleteFromServer = (resp) => {
|
||||||
if (!resp?.role?.dailyTask?.complete) return
|
if (!resp?.role?.dailyTask?.complete) {
|
||||||
|
log('角色信息中无任务完成数据', 'warning')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const complete = resp.role.dailyTask.complete
|
const complete = resp.role.dailyTask.complete
|
||||||
const isDone = (v) => v === -1
|
const isDone = (v) => v === -1
|
||||||
|
|
||||||
|
log('开始同步任务完成状态...')
|
||||||
|
log(`服务器返回的任务完成数据: ${JSON.stringify(complete)}`)
|
||||||
|
|
||||||
|
let syncedCount = 0
|
||||||
|
let completedCount = 0
|
||||||
|
|
||||||
|
// 先重置所有任务为未完成,然后根据服务器数据更新
|
||||||
|
tasks.value.forEach(task => {
|
||||||
|
task.completed = false
|
||||||
|
})
|
||||||
|
|
||||||
|
// 同步服务器返回的完成状态
|
||||||
Object.keys(complete).forEach(k => {
|
Object.keys(complete).forEach(k => {
|
||||||
const id = Number(k)
|
const id = Number(k)
|
||||||
const idx = tasks.value.findIndex(t => t.id === id)
|
const idx = tasks.value.findIndex(t => t.id === id)
|
||||||
if (idx >= 0) tasks.value[idx].completed = isDone(complete[k])
|
|
||||||
})
|
if (idx >= 0) {
|
||||||
|
const isCompleted = isDone(complete[k])
|
||||||
|
tasks.value[idx].completed = isCompleted
|
||||||
|
syncedCount++
|
||||||
|
|
||||||
|
if (isCompleted) {
|
||||||
|
completedCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新角色信息 - 使用tokenStore的方法
|
log(`任务${id} "${tasks.value[idx].name}": ${isCompleted ? '已完成' : '未完成'}`,
|
||||||
|
isCompleted ? 'success' : 'info')
|
||||||
|
} else {
|
||||||
|
log(`服务器返回未知任务ID: ${id} (完成值: ${complete[k]})`, 'warning')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 检查本地定义但服务器未返回的任务
|
||||||
|
tasks.value.forEach(task => {
|
||||||
|
if (!(task.id.toString() in complete)) {
|
||||||
|
log(`本地任务${task.id} "${task.name}" 在服务器数据中缺失`, 'warning')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
log(`任务状态同步完成: ${completedCount}/${syncedCount} 已完成`)
|
||||||
|
log(`当前进度: ${roleDailyPoint.value}/100`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新角色信息
|
||||||
const refreshRoleInfo = async () => {
|
const refreshRoleInfo = async () => {
|
||||||
if (!tokenStore.selectedToken) {
|
if (!tokenStore.selectedToken) {
|
||||||
throw new Error('没有选中的Token')
|
throw new Error('没有选中的Token')
|
||||||
}
|
}
|
||||||
const tokenId = tokenStore.selectedToken.id
|
|
||||||
|
|
||||||
try {
|
const tokenId = tokenStore.selectedToken.id
|
||||||
// 直接使用sendMessageWithPromise方法
|
|
||||||
log('正在获取角色信息...')
|
log('正在获取角色信息...')
|
||||||
|
|
||||||
// 先检查tokenStore方法是否存在
|
try {
|
||||||
if (typeof tokenStore.sendMessageWithPromise !== 'function') {
|
const response = await tokenStore.sendGetRoleInfo(tokenId)
|
||||||
throw new Error('sendMessageWithPromise方法不存在')
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await tokenStore.sendMessageWithPromise(tokenId, 'role_getroleinfo', {}, 8000)
|
|
||||||
log('角色信息获取成功', 'success')
|
log('角色信息获取成功', 'success')
|
||||||
|
|
||||||
// 更新gameData以便其他组件使用
|
// 同步任务状态
|
||||||
if (response) {
|
if (response) {
|
||||||
tokenStore.gameData.roleInfo = response
|
syncCompleteFromServer(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response
|
return response
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log(`获取角色信息失败: ${error.message}`, 'error')
|
log(`获取角色信息失败: ${error.message}`, 'error')
|
||||||
console.error('详细错误信息:', error)
|
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 一键补差 - 简化测试版本
|
// 执行单个游戏指令的封装
|
||||||
|
const executeGameCommand = async (tokenId, cmd, params = {}, description = '', timeout = 8000) => {
|
||||||
|
try {
|
||||||
|
if (description) log(`执行: ${description}`)
|
||||||
|
|
||||||
|
const result = await tokenStore.sendMessageWithPromise(tokenId, cmd, params, timeout)
|
||||||
|
|
||||||
|
if (description) log(`${description} - 成功`, 'success')
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
if (description) log(`${description} - 失败: ${error.message}`, 'error')
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否今日可用(简化版本)
|
||||||
|
const isTodayAvailable = (statisticsTime) => {
|
||||||
|
if (!statisticsTime) return true
|
||||||
|
|
||||||
|
// 如果有时间戳,检查是否为今天
|
||||||
|
const today = new Date().toDateString()
|
||||||
|
const recordDate = new Date(statisticsTime).toDateString()
|
||||||
|
|
||||||
|
return today !== recordDate
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取今日BOSS ID
|
||||||
|
const getTodayBossId = () => {
|
||||||
|
const DAY_BOSS_MAP = [9904, 9905, 9901, 9902, 9903, 9904, 9905] // 周日~周六
|
||||||
|
const dayOfWeek = new Date().getDay()
|
||||||
|
return DAY_BOSS_MAP[dayOfWeek]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 智能阵容切换辅助函数
|
||||||
|
const switchToFormationIfNeeded = async (tokenId, targetFormation, formationName, logFn) => {
|
||||||
|
try {
|
||||||
|
// 首先尝试从本地缓存获取当前阵容信息
|
||||||
|
const cachedTeamInfo = tokenStore.gameData?.presetTeam?.presetTeamInfo
|
||||||
|
let currentFormation = cachedTeamInfo?.useTeamId
|
||||||
|
|
||||||
|
if (currentFormation) {
|
||||||
|
logFn(`从缓存获取当前阵容: ${currentFormation}`)
|
||||||
|
} else {
|
||||||
|
// 缓存中没有数据,从服务器获取
|
||||||
|
logFn(`缓存中无阵容信息,从服务器获取...`)
|
||||||
|
const teamInfo = await executeGameCommand(tokenId, 'presetteam_getinfo', {}, '获取阵容信息')
|
||||||
|
currentFormation = teamInfo?.presetTeamInfo?.useTeamId
|
||||||
|
logFn(`从服务器获取当前阵容: ${currentFormation}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentFormation === targetFormation) {
|
||||||
|
logFn(`当前已是${formationName}${targetFormation},无需切换`, 'success')
|
||||||
|
return false // 不需要切换
|
||||||
|
}
|
||||||
|
|
||||||
|
logFn(`当前阵容: ${currentFormation}, 目标阵容: ${targetFormation},开始切换...`)
|
||||||
|
await executeGameCommand(tokenId, 'presetteam_saveteam',
|
||||||
|
{ teamId: targetFormation }, `切换到${formationName}${targetFormation}`)
|
||||||
|
|
||||||
|
logFn(`成功切换到${formationName}${targetFormation}`, 'success')
|
||||||
|
return true // 已切换
|
||||||
|
} catch (error) {
|
||||||
|
logFn(`阵容检查失败,直接切换: ${error.message}`, 'warning')
|
||||||
|
// 如果检查失败,还是执行切换操作
|
||||||
|
try {
|
||||||
|
await executeGameCommand(tokenId, 'presetteam_saveteam',
|
||||||
|
{ teamId: targetFormation }, `强制切换到${formationName}${targetFormation}`)
|
||||||
|
return true
|
||||||
|
} catch (fallbackError) {
|
||||||
|
logFn(`强制切换也失败: ${fallbackError.message}`, 'error')
|
||||||
|
throw fallbackError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每日任务执行器
|
||||||
|
const executeDailyTasks = async (roleInfoResp, logFn, progressFn) => {
|
||||||
|
const tokenId = tokenStore.selectedToken.id
|
||||||
|
const roleData = roleInfoResp?.role
|
||||||
|
|
||||||
|
if (!roleData) {
|
||||||
|
throw new Error('角色数据不存在')
|
||||||
|
}
|
||||||
|
|
||||||
|
logFn('开始执行每日任务补差')
|
||||||
|
|
||||||
|
// 检查已完成的任务
|
||||||
|
const completedTasks = roleData.dailyTask?.complete ?? {}
|
||||||
|
const isTaskCompleted = (taskId) => completedTasks[taskId] === -1
|
||||||
|
|
||||||
|
// 统计数据
|
||||||
|
const statistics = roleData.statistics ?? {}
|
||||||
|
const statisticsTime = roleData.statisticsTime ?? {}
|
||||||
|
|
||||||
|
// 构建任务列表
|
||||||
|
const taskList = []
|
||||||
|
|
||||||
|
// 1. 基础任务(根据完成状态决定是否执行)
|
||||||
|
|
||||||
|
// 分享游戏 (任务ID: 2)
|
||||||
|
if (!isTaskCompleted(2)) {
|
||||||
|
taskList.push({
|
||||||
|
name: '分享一次游戏',
|
||||||
|
execute: () => executeGameCommand(tokenId, 'system_mysharecallback',
|
||||||
|
{ isSkipShareCard: true, type: 2 }, '分享游戏')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 赠送好友金币 (任务ID: 3)
|
||||||
|
if (!isTaskCompleted(3)) {
|
||||||
|
taskList.push({
|
||||||
|
name: '赠送好友金币',
|
||||||
|
execute: () => executeGameCommand(tokenId, 'friend_batch', {}, '赠送好友金币')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 招募 (任务ID: 4)
|
||||||
|
if (!isTaskCompleted(4)) {
|
||||||
|
taskList.push({
|
||||||
|
name: '免费招募',
|
||||||
|
execute: () => executeGameCommand(tokenId, 'hero_recruit',
|
||||||
|
{ recruitType: 3, recruitNumber: 1 }, '免费招募')
|
||||||
|
})
|
||||||
|
|
||||||
|
if (settings.payRecruit) {
|
||||||
|
taskList.push({
|
||||||
|
name: '付费招募',
|
||||||
|
execute: () => executeGameCommand(tokenId, 'hero_recruit',
|
||||||
|
{ recruitType: 1, recruitNumber: 1 }, '付费招募')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点金 (任务ID: 6)
|
||||||
|
if (!isTaskCompleted(6) && isTodayAvailable(statisticsTime['buy:gold'])) {
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
taskList.push({
|
||||||
|
name: `免费点金 ${i + 1}/3`,
|
||||||
|
execute: () => executeGameCommand(tokenId, 'system_buygold',
|
||||||
|
{ buyNum: 1 }, `免费点金 ${i + 1}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 挂机奖励 (任务ID: 5)
|
||||||
|
if (!isTaskCompleted(5) && settings.claimHangUp) {
|
||||||
|
// 先加钟4次
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
taskList.push({
|
||||||
|
name: `挂机加钟 ${i + 1}/4`,
|
||||||
|
execute: () => executeGameCommand(tokenId, 'system_mysharecallback',
|
||||||
|
{ isSkipShareCard: true, type: 2 }, `挂机加钟 ${i + 1}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 然后领取奖励
|
||||||
|
taskList.push({
|
||||||
|
name: '领取挂机奖励',
|
||||||
|
execute: () => executeGameCommand(tokenId, 'system_claimhangupreward', {}, '领取挂机奖励')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 最后再加1次钟
|
||||||
|
taskList.push({
|
||||||
|
name: '挂机加钟 5/5',
|
||||||
|
execute: () => executeGameCommand(tokenId, 'system_mysharecallback',
|
||||||
|
{ isSkipShareCard: true, type: 2 }, '挂机加钟 5')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开宝箱 (任务ID: 7)
|
||||||
|
if (!isTaskCompleted(7) && settings.openBox) {
|
||||||
|
taskList.push({
|
||||||
|
name: '开启木质宝箱',
|
||||||
|
execute: () => executeGameCommand(tokenId, 'item_openbox',
|
||||||
|
{ itemId: 2001, number: 10 }, '开启木质宝箱10个')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 盐罐 (任务ID: 14)
|
||||||
|
if (!isTaskCompleted(14) && settings.claimBottle) {
|
||||||
|
taskList.push({
|
||||||
|
name: '领取盐罐奖励',
|
||||||
|
execute: () => executeGameCommand(tokenId, 'bottlehelper_claim', {}, '领取盐罐奖励')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 竞技场 (任务ID: 13)
|
||||||
|
if (!isTaskCompleted(13) && settings.arenaEnable) {
|
||||||
|
taskList.push({
|
||||||
|
name: '竞技场战斗',
|
||||||
|
execute: async () => {
|
||||||
|
logFn('开始竞技场战斗流程')
|
||||||
|
|
||||||
|
// 智能切换到竞技场阵容
|
||||||
|
await switchToFormationIfNeeded(tokenId, settings.arenaFormation, '竞技场阵容', logFn)
|
||||||
|
|
||||||
|
// 开始竞技场
|
||||||
|
await executeGameCommand(tokenId, 'arena_startarea', {}, '开始竞技场')
|
||||||
|
|
||||||
|
// 进行3场战斗
|
||||||
|
for (let i = 1; i <= 3; i++) {
|
||||||
|
logFn(`竞技场战斗 ${i}/3`)
|
||||||
|
|
||||||
|
// 获取目标
|
||||||
|
const targets = await executeGameCommand(tokenId, 'arena_getareatarget',
|
||||||
|
{ refresh: false }, `获取竞技场目标${i}`)
|
||||||
|
|
||||||
|
const targetId = targets?.roleList?.[0]?.roleId
|
||||||
|
if (targetId) {
|
||||||
|
await executeGameCommand(tokenId, 'fight_startareaarena',
|
||||||
|
{ targetId }, `竞技场战斗${i}`, 10000)
|
||||||
|
} else {
|
||||||
|
logFn(`竞技场战斗${i} - 未找到目标`, 'warning')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 战斗间隔
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. BOSS战斗
|
||||||
|
if (settings.bossTimes > 0) {
|
||||||
|
// 军团BOSS
|
||||||
|
const alreadyLegionBoss = statistics['legion:boss'] ?? 0
|
||||||
|
const remainingLegionBoss = Math.max(settings.bossTimes - alreadyLegionBoss, 0)
|
||||||
|
|
||||||
|
if (remainingLegionBoss > 0) {
|
||||||
|
// 为军团BOSS智能切换阵容
|
||||||
|
taskList.push({
|
||||||
|
name: '军团BOSS阵容检查',
|
||||||
|
execute: () => switchToFormationIfNeeded(tokenId, settings.bossFormation, 'BOSS阵容', logFn)
|
||||||
|
})
|
||||||
|
|
||||||
|
for (let i = 0; i < remainingLegionBoss; i++) {
|
||||||
|
taskList.push({
|
||||||
|
name: `军团BOSS ${i + 1}/${remainingLegionBoss}`,
|
||||||
|
execute: () => executeGameCommand(tokenId, 'fight_startlegionboss', {}, `军团BOSS ${i + 1}`, 12000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每日BOSS
|
||||||
|
const todayBossId = getTodayBossId()
|
||||||
|
if (remainingLegionBoss === 0) {
|
||||||
|
// 如果没有军团BOSS,为每日BOSS切换阵容
|
||||||
|
taskList.push({
|
||||||
|
name: '每日BOSS阵容检查',
|
||||||
|
execute: () => switchToFormationIfNeeded(tokenId, settings.bossFormation, 'BOSS阵容', logFn)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
taskList.push({
|
||||||
|
name: `每日BOSS ${i + 1}/3`,
|
||||||
|
execute: () => executeGameCommand(tokenId, 'fight_startboss',
|
||||||
|
{ bossId: todayBossId }, `每日BOSS ${i + 1}`, 12000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 固定奖励领取
|
||||||
|
const fixedRewards = [
|
||||||
|
{ name: '福利签到', cmd: 'system_signinreward' },
|
||||||
|
{ name: '俱乐部签到', cmd: 'legion_signin' },
|
||||||
|
{ name: '领取每日礼包', cmd: 'discount_claimreward' },
|
||||||
|
{ name: '领取免费礼包', cmd: 'card_claimreward' },
|
||||||
|
{ name: '领取永久卡礼包', cmd: 'card_claimreward', params: { cardId: 4003 } }
|
||||||
|
]
|
||||||
|
|
||||||
|
if (settings.claimEmail) {
|
||||||
|
fixedRewards.push({ name: '领取邮件奖励', cmd: 'mail_claimallattachment' })
|
||||||
|
}
|
||||||
|
|
||||||
|
fixedRewards.forEach(reward => {
|
||||||
|
taskList.push({
|
||||||
|
name: reward.name,
|
||||||
|
execute: () => executeGameCommand(tokenId, reward.cmd, reward.params || {}, reward.name)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 5. 免费活动
|
||||||
|
// 免费钓鱼
|
||||||
|
if (isTodayAvailable(statisticsTime['artifact:normal:lottery:time'])) {
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
taskList.push({
|
||||||
|
name: `免费钓鱼 ${i + 1}/3`,
|
||||||
|
execute: () => executeGameCommand(tokenId, 'artifact_lottery',
|
||||||
|
{ lotteryNumber: 1, newFree: true, type: 1 }, `免费钓鱼 ${i + 1}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 灯神免费扫荡
|
||||||
|
const kingdoms = ['魏国', '蜀国', '吴国', '群雄']
|
||||||
|
for (let gid = 1; gid <= 4; gid++) {
|
||||||
|
if (isTodayAvailable(statisticsTime[`genie:daily:free:${gid}`])) {
|
||||||
|
taskList.push({
|
||||||
|
name: `${kingdoms[gid-1]}灯神免费扫荡`,
|
||||||
|
execute: () => executeGameCommand(tokenId, 'genie_sweep',
|
||||||
|
{ genieId: gid }, `${kingdoms[gid-1]}灯神免费扫荡`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 灯神免费扫荡卷
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
taskList.push({
|
||||||
|
name: `领取免费扫荡卷 ${i + 1}/3`,
|
||||||
|
execute: () => executeGameCommand(tokenId, 'genie_buysweep', {}, `领取免费扫荡卷 ${i + 1}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 黑市购买任务 (任务ID: 12)
|
||||||
|
if (!isTaskCompleted(12) && settings.blackMarketPurchase) {
|
||||||
|
taskList.push({
|
||||||
|
name: '黑市购买1次物品',
|
||||||
|
execute: () => executeGameCommand(tokenId, 'store_purchase', { goodsId: 1 }, '黑市购买1次物品')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 任务奖励领取
|
||||||
|
for (let taskId = 1; taskId <= 10; taskId++) {
|
||||||
|
taskList.push({
|
||||||
|
name: `领取任务奖励${taskId}`,
|
||||||
|
execute: () => executeGameCommand(tokenId, 'task_claimdailypoint',
|
||||||
|
{ taskId }, `领取任务奖励${taskId}`, 5000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 日常和周常奖励
|
||||||
|
taskList.push(
|
||||||
|
{
|
||||||
|
name: '领取日常任务奖励',
|
||||||
|
execute: () => executeGameCommand(tokenId, 'task_claimdailyreward', {}, '领取日常任务奖励')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '领取周常任务奖励',
|
||||||
|
execute: () => executeGameCommand(tokenId, 'task_claimweekreward', {}, '领取周常任务奖励')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 执行任务列表
|
||||||
|
const totalTasks = taskList.length
|
||||||
|
logFn(`共有 ${totalTasks} 个任务待执行`)
|
||||||
|
|
||||||
|
for (let i = 0; i < taskList.length; i++) {
|
||||||
|
const task = taskList[i]
|
||||||
|
|
||||||
|
try {
|
||||||
|
await task.execute()
|
||||||
|
|
||||||
|
// 更新进度
|
||||||
|
const progress = Math.floor(((i + 1) / totalTasks) * 100)
|
||||||
|
if (progressFn) progressFn(tokenId, progress)
|
||||||
|
|
||||||
|
// 任务间隔
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500))
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logFn(`任务执行失败: ${task.name} - ${error.message}`, 'error')
|
||||||
|
// 继续执行下一个任务
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保进度为100%
|
||||||
|
if (progressFn) progressFn(tokenId, 100)
|
||||||
|
logFn('所有任务执行完成', 'success')
|
||||||
|
|
||||||
|
// 最后刷新一次角色信息
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||||
|
await refreshRoleInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一键补差主函数
|
||||||
const runDailyFix = async () => {
|
const runDailyFix = async () => {
|
||||||
if (!tokenStore.selectedToken || busy.value) {
|
if (!tokenStore.selectedToken || busy.value) {
|
||||||
log('没有选中Token或正在执行中', 'error')
|
message.warning('没有选中Token或正在执行中')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isConnected.value) {
|
||||||
|
message.error('WebSocket连接未建立,请检查连接状态')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,209 +828,63 @@ const runDailyFix = async () => {
|
|||||||
showLog.value = true
|
showLog.value = true
|
||||||
logList.value = []
|
logList.value = []
|
||||||
|
|
||||||
log('开始执行任务...')
|
|
||||||
|
|
||||||
const tokenId = tokenStore.selectedToken.id
|
|
||||||
log(`当前Token ID: ${tokenId}`)
|
|
||||||
|
|
||||||
// 检查tokenStore方法
|
|
||||||
log(`检查tokenStore方法:`)
|
|
||||||
log(`- sendMessageWithPromise: ${typeof tokenStore.sendMessageWithPromise}`)
|
|
||||||
log(`- getWebSocketStatus: ${typeof tokenStore.getWebSocketStatus}`)
|
|
||||||
log(`- selectedToken: ${!!tokenStore.selectedToken}`)
|
|
||||||
|
|
||||||
// 检查WebSocket连接状态
|
|
||||||
const wsStatus = tokenStore.getWebSocketStatus(tokenId)
|
|
||||||
log(`WebSocket状态: ${JSON.stringify(wsStatus)}`)
|
|
||||||
|
|
||||||
// 修复状态检查逻辑 - wsStatus可能直接是字符串,也可能是对象
|
|
||||||
const actualStatus = typeof wsStatus === 'string' ? wsStatus : wsStatus?.status
|
|
||||||
log(`实际状态: ${actualStatus}`)
|
|
||||||
|
|
||||||
if (actualStatus !== 'connected') {
|
|
||||||
log('WebSocket未连接,无法继续执行', 'error')
|
|
||||||
busy.value = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
log('尝试获取游戏内角色信息...')
|
log('=== 开始执行一键补差任务 ===')
|
||||||
log('发送 role_getroleinfo 命令到游戏服务器...')
|
|
||||||
log('期望响应: role_getroleinforesp')
|
|
||||||
|
|
||||||
// 先检查WebSocket客户端是否能接收到消息
|
|
||||||
tokenStore.setMessageListener((message) => {
|
// 1. 获取角色信息
|
||||||
if (message?.cmd) {
|
const roleInfo = await refreshRoleInfo()
|
||||||
log(`收到游戏消息: ${message.cmd}`, 'info')
|
|
||||||
|
if (!roleInfo?.role) {
|
||||||
|
throw new Error('获取角色信息失败或数据异常')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
log(`当前每日任务进度: ${roleInfo.role.dailyTask?.dailyPoint || 0}/100`)
|
||||||
|
|
||||||
|
// 2. 执行任务
|
||||||
|
log('第二步: 开始执行每日任务...')
|
||||||
|
await executeDailyTasks(roleInfo, log, (tokenId, progress) => {
|
||||||
|
log(`任务进度: ${progress}%`)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 增加超时时间,因为游戏服务器响应可能较慢
|
log('=== 任务执行完成 ===', 'success')
|
||||||
const roleInfo = await tokenStore.sendMessageWithPromise(tokenId, 'role_getroleinfo', {}, 10000)
|
message.success('每日任务补差执行完成')
|
||||||
|
|
||||||
if (roleInfo) {
|
// 3. 最终刷新角色信息
|
||||||
log('游戏角色信息获取成功!', 'success')
|
setTimeout(async () => {
|
||||||
|
try {
|
||||||
// 显示角色基本信息(如果存在)
|
await refreshRoleInfo()
|
||||||
if (roleInfo.role) {
|
log('最终角色信息刷新完成', 'success')
|
||||||
log(`角色等级: ${roleInfo.role.level || '未知'}`)
|
} catch (error) {
|
||||||
log(`角色名称: ${roleInfo.role.name || '未知'}`)
|
log(`最终刷新失败: ${error.message}`, 'warning')
|
||||||
log(`每日任务进度: ${roleInfo.role.dailyTask?.dailyPoint || 0}/100`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 现在尝试执行一个简单的游戏指令
|
|
||||||
log('尝试执行游戏内签到...')
|
|
||||||
const signInResult = await tokenStore.sendMessageWithPromise(tokenId, 'system_signinreward', {}, 8000)
|
|
||||||
|
|
||||||
if (signInResult) {
|
|
||||||
log('游戏签到执行成功!', 'success')
|
|
||||||
log(`签到奖励: ${signInResult.reward ? '有奖励' : '无奖励或已签到'}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
message.success('游戏指令测试成功!')
|
|
||||||
} else {
|
|
||||||
log('未收到游戏角色数据', 'error')
|
|
||||||
message.warning('未收到游戏服务器响应')
|
|
||||||
}
|
}
|
||||||
|
}, 3000)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log(`游戏指令执行失败: ${error.message}`, 'error')
|
log(`任务执行失败: ${error.message}`, 'error')
|
||||||
|
console.error('详细错误信息:', error)
|
||||||
// 分析可能的原因
|
message.error(`任务执行失败: ${error.message}`)
|
||||||
if (error.message.includes('超时') || error.message.includes('timeout')) {
|
|
||||||
log('可能原因:游戏服务器响应超时', 'error')
|
|
||||||
log('建议:检查网络连接或稍后重试', 'error')
|
|
||||||
} else if (error.message.includes('Unknown cmd')) {
|
|
||||||
log('可能原因:游戏指令未在WebSocket客户端中注册', 'error')
|
|
||||||
} else {
|
|
||||||
log('可能原因:WebSocket连接异常或游戏服务器拒绝请求', 'error')
|
|
||||||
}
|
|
||||||
|
|
||||||
message.error(`游戏指令执行失败: ${error.message}`)
|
|
||||||
} finally {
|
} finally {
|
||||||
busy.value = false
|
busy.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 简化的执行器函数 - 直接使用tokenStore方法
|
// 刷新任务状态
|
||||||
const U = async (roleInfoResp, logFn, progress) => {
|
const handleRefreshTaskStatus = async () => {
|
||||||
const tokenId = tokenStore.selectedToken.id
|
if (!isConnected.value) {
|
||||||
|
message.warning('WebSocket未连接,无法刷新任务状态')
|
||||||
logFn?.('开始执行每日任务')
|
return
|
||||||
|
|
||||||
// 检查已完成的任务
|
|
||||||
const completedTasks = roleInfoResp.role?.dailyTask?.complete ?? {}
|
|
||||||
const isTaskCompleted = (taskId) => completedTasks[taskId] === -1
|
|
||||||
|
|
||||||
// 执行任务列表
|
|
||||||
const taskList = []
|
|
||||||
|
|
||||||
// 基础任务
|
|
||||||
if (!isTaskCompleted(2)) { // 分享游戏
|
|
||||||
taskList.push({ name: '分享一次游戏', cmd: 'system_mysharecallback', params: { isSkipShareCard: true, type: 2 } })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isTaskCompleted(3)) { // 赠送好友
|
|
||||||
taskList.push({ name: '赠送好友金币', cmd: 'friend_batch' })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isTaskCompleted(4)) { // 招募
|
|
||||||
taskList.push({ name: '免费招募', cmd: 'hero_recruit' })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isTaskCompleted(6)) { // 点金
|
|
||||||
taskList.push({ name: '免费点金', cmd: 'system_buygold' })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isTaskCompleted(5) && settings.claimHangUp) { // 挂机奖励
|
|
||||||
taskList.push({ name: '领取挂机奖励', cmd: 'system_claimhangupreward' })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isTaskCompleted(7) && settings.openBox) { // 开宝箱
|
|
||||||
taskList.push({ name: '开启宝箱', cmd: 'item_openbox', params: { itemId: 2001, number: 10 } })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isTaskCompleted(14) && settings.claimBottle) { // 盐罐
|
|
||||||
taskList.push({ name: '领取盐罐奖励', cmd: 'bottlehelper_claim' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 常规奖励
|
|
||||||
taskList.push(
|
|
||||||
{ name: '福利签到', cmd: 'system_signinreward' },
|
|
||||||
{ name: '俱乐部签到', cmd: 'legion_signin' },
|
|
||||||
{ name: '领取每日礼包', cmd: 'discount_claimreward' },
|
|
||||||
{ name: '领取免费礼包', cmd: 'card_claimreward' }
|
|
||||||
)
|
|
||||||
|
|
||||||
if (settings.claimEmail) {
|
|
||||||
taskList.push({ name: '领取邮件奖励', cmd: 'mail_claimallattachment' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 任务奖励领取
|
|
||||||
for (let i = 1; i <= 10; i++) {
|
|
||||||
taskList.push({ name: `领取任务奖励${i}`, cmd: 'task_claimdailypoint', params: { taskId: i } })
|
|
||||||
}
|
|
||||||
taskList.push(
|
|
||||||
{ name: '领取日常任务奖励', cmd: 'task_claimdailyreward' },
|
|
||||||
{ name: '领取周常任务奖励', cmd: 'task_claimweekreward' }
|
|
||||||
)
|
|
||||||
|
|
||||||
// 竞技场
|
|
||||||
if (!isTaskCompleted(13) && settings.arenaEnable) {
|
|
||||||
logFn?.('开始竞技场战斗')
|
|
||||||
try {
|
|
||||||
// 获取队伍信息
|
|
||||||
const teamInfo = await tokenStore.sendMessageWithPromise(tokenId, 'presetteam_getinfo', {}, 5000)
|
|
||||||
|
|
||||||
// 切换阵容(如果需要)
|
|
||||||
if (teamInfo?.presetTeamInfo?.useTeamId !== settings.arenaFormation) {
|
|
||||||
await tokenStore.sendMessageWithPromise(tokenId, 'presetteam_saveteam', { teamId: settings.arenaFormation }, 5000)
|
|
||||||
logFn?.(`切换到竞技场阵容 ${settings.arenaFormation}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 开始竞技场
|
|
||||||
await tokenStore.sendMessageWithPromise(tokenId, 'arena_startarea', {}, 5000)
|
|
||||||
|
|
||||||
// 进行3场战斗
|
|
||||||
for (let i = 1; i <= 3; i++) {
|
|
||||||
logFn?.(`竞技场战斗 ${i}/3`)
|
|
||||||
const targets = await tokenStore.sendMessageWithPromise(tokenId, 'arena_getareatarget', {}, 5000)
|
|
||||||
const targetId = targets?.roleList?.[0]?.roleId
|
|
||||||
|
|
||||||
if (targetId) {
|
|
||||||
await tokenStore.sendMessageWithPromise(tokenId, 'fight_startareaarena', { targetId }, 8000)
|
|
||||||
logFn?.(`完成竞技场战斗 ${i}`, 'success')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logFn?.(`竞技场战斗失败: ${error.message}`, 'error')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行任务列表
|
|
||||||
const total = taskList.length
|
|
||||||
for (let i = 0; i < taskList.length; i++) {
|
|
||||||
const task = taskList[i]
|
|
||||||
logFn?.(task.name)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await tokenStore.sendMessageWithPromise(tokenId, task.cmd, task.params || {}, 5000)
|
log('手动刷新任务状态...')
|
||||||
logFn?.(`${task.name} 成功`, 'success')
|
await refreshRoleInfo()
|
||||||
|
message.success('任务状态刷新成功')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logFn?.(`${task.name} 失败: ${error.message}`, 'error')
|
log(`刷新失败: ${error.message}`, 'error')
|
||||||
|
message.error(`刷新失败: ${error.message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新进度
|
|
||||||
const progress_pct = Math.floor(((i + 1) / total) * 100)
|
|
||||||
if (progress && tokenId) progress(tokenId, progress_pct)
|
|
||||||
|
|
||||||
// 小延迟避免过快
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 300))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 确保进度为100%
|
|
||||||
if (progress && tokenId) progress(tokenId, 100)
|
|
||||||
logFn?.('所有任务执行完成', 'success')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 辅助函数
|
// 辅助函数
|
||||||
@@ -646,7 +910,7 @@ const saveSettings = (roleId, s) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听设置变化 - 基于参考代码的watch逻辑
|
// 监听设置变化
|
||||||
watch(settings, (cur) => {
|
watch(settings, (cur) => {
|
||||||
const role = getCurrentRole()
|
const role = getCurrentRole()
|
||||||
if (role) saveSettings(role.roleId, cur)
|
if (role) saveSettings(role.roleId, cur)
|
||||||
@@ -655,29 +919,41 @@ watch(settings, (cur) => {
|
|||||||
// 监听token选择变化
|
// 监听token选择变化
|
||||||
watch(() => tokenStore.selectedToken, async (newToken, oldToken) => {
|
watch(() => tokenStore.selectedToken, async (newToken, oldToken) => {
|
||||||
if (newToken && newToken !== oldToken) {
|
if (newToken && newToken !== oldToken) {
|
||||||
|
log(`切换到Token: ${newToken.name}`)
|
||||||
|
|
||||||
// 加载新token的设置
|
// 加载新token的设置
|
||||||
const saved = loadSettings(newToken.id)
|
const saved = loadSettings(newToken.id)
|
||||||
if (saved) Object.assign(settings, saved)
|
if (saved) Object.assign(settings, saved)
|
||||||
|
|
||||||
// 同步任务状态
|
// 如果WebSocket已连接,尝试获取最新角色信息
|
||||||
syncCompleteFromServer(tokenStore.selectedTokenRoleInfo)
|
if (isConnected.value) {
|
||||||
|
try {
|
||||||
|
await refreshRoleInfo()
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('切换token后获取角色信息失败:', error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
// 生命周期 - 基于参考代码的onMounted函数
|
// 监听角色信息变化,自动同步任务状态
|
||||||
|
watch(() => tokenStore.selectedTokenRoleInfo, (newRoleInfo) => {
|
||||||
|
if (newRoleInfo?.role?.dailyTask?.complete) {
|
||||||
|
log('角色信息更新,同步任务状态')
|
||||||
|
syncCompleteFromServer(newRoleInfo)
|
||||||
|
}
|
||||||
|
}, { immediate: true, deep: true })
|
||||||
|
|
||||||
|
// 生命周期
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 首次拉取角色信息(如果有选中的token)
|
log('组件初始化完成')
|
||||||
if (tokenStore.selectedToken) {
|
|
||||||
|
// 首次拉取角色信息(如果有选中的token且已连接)
|
||||||
|
if (tokenStore.selectedToken && isConnected.value) {
|
||||||
try {
|
try {
|
||||||
await refreshRoleInfo()
|
await refreshRoleInfo()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('初始化时获取角色信息失败:', error.message)
|
console.warn('初始化时获取角色信息失败:', error.message)
|
||||||
// 如果获取失败,尝试发送普通消息(不等待响应)
|
|
||||||
try {
|
|
||||||
tokenStore.sendMessage(tokenStore.selectedToken.id, 'role_getroleinfo', {})
|
|
||||||
} catch (sendError) {
|
|
||||||
console.warn('发送角色信息请求失败:', sendError.message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,13 +963,11 @@ onMounted(async () => {
|
|||||||
if (saved) Object.assign(settings, saved)
|
if (saved) Object.assign(settings, saved)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同步完成态(使用现有的角色信息)
|
// 初始化时的任务状态同步会通过 watch selectedTokenRoleInfo 自动处理
|
||||||
syncCompleteFromServer(tokenStore.selectedTokenRoleInfo)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 清理监听 - 基于参考代码的onBeforeUnmount
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
tokenStore.setMessageListener(undefined)
|
log('组件即将卸载')
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -851,7 +1125,34 @@ onBeforeUnmount(() => {
|
|||||||
.modal-header {
|
.modal-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
gap: var(--spacing-sm);
|
gap: var(--spacing-sm);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-xs);
|
||||||
|
padding: var(--spacing-xs) var(--spacing-sm);
|
||||||
|
border: 1px solid var(--border-light);
|
||||||
|
border-radius: var(--border-radius-medium);
|
||||||
|
background: white;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all var(--transition-fast);
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-content {
|
.settings-content {
|
||||||
@@ -941,7 +1242,7 @@ onBeforeUnmount(() => {
|
|||||||
background: var(--bg-tertiary);
|
background: var(--bg-tertiary);
|
||||||
border-radius: var(--border-radius-medium);
|
border-radius: var(--border-radius-medium);
|
||||||
padding: var(--spacing-md);
|
padding: var(--spacing-md);
|
||||||
max-height: 300px;
|
max-height: 400px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -968,6 +1269,10 @@ onBeforeUnmount(() => {
|
|||||||
&.success {
|
&.success {
|
||||||
color: var(--success-color);
|
color: var(--success-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
color: #f59e0b;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 响应式设计
|
// 响应式设计
|
||||||
|
|||||||
@@ -10,22 +10,22 @@
|
|||||||
<h3>队伍阵容</h3>
|
<h3>队伍阵容</h3>
|
||||||
<p>当前使用的战斗阵容</p>
|
<p>当前使用的战斗阵容</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="team-selector">
|
<div class="team-selector">
|
||||||
<button
|
<button
|
||||||
v-for="teamId in availableTeams"
|
v-for="teamId in availableTeams"
|
||||||
:key="teamId"
|
:key="teamId"
|
||||||
:class="[
|
:disabled="loading || switching"
|
||||||
'team-button',
|
:class="['team-button', { active: currentTeam === teamId }]"
|
||||||
{ active: currentTeam === teamId }
|
|
||||||
]"
|
|
||||||
@click="selectTeam(teamId)"
|
@click="selectTeam(teamId)"
|
||||||
>
|
>
|
||||||
{{ teamId }}
|
{{ teamId }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="team-button refresh-button"
|
class="team-button refresh-button"
|
||||||
|
:disabled="loading"
|
||||||
title="刷新队伍数据"
|
title="刷新队伍数据"
|
||||||
@click="refreshTeamData"
|
@click="refreshTeamData(true)"
|
||||||
>
|
>
|
||||||
🔄
|
🔄
|
||||||
</button>
|
</button>
|
||||||
@@ -36,236 +36,227 @@
|
|||||||
<div class="team-display">
|
<div class="team-display">
|
||||||
<div class="current-team-info">
|
<div class="current-team-info">
|
||||||
<span class="label">当前阵容</span>
|
<span class="label">当前阵容</span>
|
||||||
<span class="team-number">阵容 {{ currentTeam }}</span>
|
<span class="team-number">
|
||||||
|
<template v-if="!loading">阵容 {{ currentTeam }}</template>
|
||||||
|
<template v-else>加载中…</template>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="heroes-container">
|
<div class="heroes-container">
|
||||||
<div class="heroes-grid">
|
<div v-if="!loading" class="heroes-inline">
|
||||||
<div
|
<div
|
||||||
v-for="hero in currentTeamHeroes"
|
v-for="hero in currentTeamHeroes"
|
||||||
:key="hero.id || hero.name"
|
:key="hero.id || hero.name"
|
||||||
class="hero-card"
|
class="hero-item"
|
||||||
>
|
>
|
||||||
|
<div class="hero-circle">
|
||||||
<img
|
<img
|
||||||
v-if="hero.avatar"
|
v-if="hero.avatar"
|
||||||
:src="hero.avatar"
|
:src="hero.avatar"
|
||||||
:alt="hero.name"
|
:alt="hero.name"
|
||||||
class="hero-avatar"
|
class="hero-avatar"
|
||||||
>
|
>
|
||||||
<div
|
<div v-else class="hero-placeholder">
|
||||||
v-else
|
|
||||||
class="hero-placeholder"
|
|
||||||
>
|
|
||||||
{{ hero.name?.substring(0, 2) || '?' }}
|
{{ hero.name?.substring(0, 2) || '?' }}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<span class="hero-name">{{ hero.name || '未知' }}</span>
|
<span class="hero-name">{{ hero.name || '未知' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div v-if="!loading && !currentTeamHeroes.length" class="empty-team">
|
||||||
v-if="!currentTeamHeroes.length"
|
|
||||||
class="empty-team"
|
|
||||||
>
|
|
||||||
<p>暂无队伍信息</p>
|
<p>暂无队伍信息</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="loading" class="empty-team"><p>正在加载队伍信息…</p></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch, onMounted } from 'vue'
|
import { ref, computed, watch, onMounted } from 'vue'
|
||||||
import { useTokenStore } from '@/stores/tokenStore'
|
import { useTokenStore } from '@/stores/tokenStore'
|
||||||
import { useMessage } from 'naive-ui'
|
import { useMessage, NTag } from 'naive-ui'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 集成英雄字典(游戏ID -> { name, type })
|
||||||
|
* 你也可以独立出一个 heroDict.ts 后 import;按你的要求,这里整合到同一文件。
|
||||||
|
*/
|
||||||
|
const HERO_DICT: Record<number, { name: string; type: string }> = {
|
||||||
|
101: { name: '司马懿', type: '魏国' }, 102: { name: '郭嘉', type: '魏国' }, 103: { name: '关羽', type: '蜀国' },
|
||||||
|
104: { name: '诸葛亮', type: '蜀国' }, 105: { name: '周瑜', type: '吴国' }, 106: { name: '太史慈', type: '吴国' },
|
||||||
|
107: { name: '吕布', type: '群雄' }, 108: { name: '华佗', type: '群雄' }, 109: { name: '甄姬', type: '魏国' },
|
||||||
|
110: { name: '黄月英', type: '蜀国' }, 111: { name: '孙策', type: '吴国' }, 112: { name: '贾诩', type: '群雄' },
|
||||||
|
113: { name: '曹仁', type: '魏国' }, 114: { name: '姜维', type: '蜀国' }, 115: { name: '孙坚', type: '吴国' },
|
||||||
|
116: { name: '公孙瓒', type: '群雄' }, 117: { name: '典韦', type: '魏国' }, 118: { name: '赵云', type: '蜀国' },
|
||||||
|
119: { name: '大乔', type: '吴国' }, 120: { name: '张角', type: '群雄' }, 201: { name: '徐晃', type: '魏国' },
|
||||||
|
202: { name: '荀彧', type: '魏国' }, 203: { name: '典韦', type: '魏国' }, 204: { name: '张飞', type: '蜀国' },
|
||||||
|
205: { name: '赵云', type: '蜀国' }, 206: { name: '庞统', type: '蜀国' }, 207: { name: '鲁肃', type: '吴国' },
|
||||||
|
208: { name: '陆逊', type: '吴国' }, 209: { name: '甘宁', type: '吴国' }, 210: { name: '貂蝉', type: '群雄' },
|
||||||
|
211: { name: '董卓', type: '群雄' }, 212: { name: '张角', type: '群雄' }, 213: { name: '张辽', type: '魏国' },
|
||||||
|
214: { name: '夏侯惇', type: '魏国' }, 215: { name: '许褚', type: '魏国' }, 216: { name: '夏侯渊', type: '魏国' },
|
||||||
|
217: { name: '魏延', type: '蜀国' }, 218: { name: '黄忠', type: '蜀国' }, 219: { name: '马超', type: '蜀国' },
|
||||||
|
220: { name: '马岱', type: '蜀国' }, 221: { name: '吕蒙', type: '吴国' }, 222: { name: '黄盖', type: '吴国' },
|
||||||
|
223: { name: '蔡文姬', type: '魏国' }, 224: { name: '小乔', type: '吴国' }, 225: { name: '袁绍', type: '群雄' },
|
||||||
|
226: { name: '华雄', type: '群雄' }, 227: { name: '颜良', type: '群雄' }, 228: { name: '文丑', type: '群雄' },
|
||||||
|
301: { name: '周泰', type: '吴国' }, 302: { name: '许攸', type: '魏国' }, 303: { name: '于禁', type: '魏国' },
|
||||||
|
304: { name: '张星彩', type: '蜀国' }, 305: { name: '关银屏', type: '蜀国' }, 306: { name: '关平', type: '蜀国' },
|
||||||
|
307: { name: '程普', type: '吴国' }, 308: { name: '张昭', type: '吴国' }, 309: { name: '陆绩', type: '吴国' },
|
||||||
|
310: { name: '吕玲绮', type: '群雄' }, 311: { name: '潘凤', type: '群雄' }, 312: { name: '邢道荣', type: '群雄' },
|
||||||
|
313: { name: '祝融夫人', type: '群雄' }, 314: { name: '孟获', type: '群雄' }
|
||||||
|
}
|
||||||
|
|
||||||
const tokenStore = useTokenStore()
|
const tokenStore = useTokenStore()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
// 响应式数据
|
// 状态
|
||||||
|
const loading = ref(false)
|
||||||
|
const switching = ref(false)
|
||||||
const currentTeam = ref(1)
|
const currentTeam = ref(1)
|
||||||
const availableTeams = ref([1, 2, 3, 4])
|
const availableTeams = ref<number[]>([1, 2, 3, 4])
|
||||||
|
|
||||||
// 计算属性
|
// —— 缓存优先的 presetTeam 原始数据 ——
|
||||||
const presetTeamInfo = computed(() => {
|
const presetTeamRaw = computed(() => tokenStore.gameData?.presetTeam ?? null)
|
||||||
return tokenStore.gameData?.presetTeam || null
|
|
||||||
})
|
|
||||||
|
|
||||||
|
// 统一结构:输出 { useTeamId, teams }
|
||||||
|
function normalizePresetTeam(raw: any): { useTeamId: number; teams: Record<number, { teamInfo: Record<string, any> }> } {
|
||||||
|
if (!raw) return { useTeamId: 1, teams: {} }
|
||||||
|
const root = raw.presetTeamInfo ?? raw
|
||||||
|
const findUseIdRec = (obj: any): number | null => {
|
||||||
|
if (!obj || typeof obj !== 'object') return null
|
||||||
|
if (typeof obj.useTeamId === 'number') return obj.useTeamId
|
||||||
|
for (const k of Object.keys(obj)) {
|
||||||
|
const v = findUseIdRec(obj[k])
|
||||||
|
if (v) return v
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const useTeamId = root.useTeamId ?? root.presetTeamInfo?.useTeamId ?? findUseIdRec(root) ?? 1
|
||||||
|
|
||||||
|
const dict = root.presetTeamInfo ?? root
|
||||||
|
const teams: Record<number, { teamInfo: Record<string, any> }> = {}
|
||||||
|
const ids = Object.keys(dict || {}).filter(k => /^\d+$/.test(k))
|
||||||
|
for (const idStr of ids) {
|
||||||
|
const id = Number(idStr)
|
||||||
|
const node = dict[idStr]
|
||||||
|
if (!node) { teams[id] = { teamInfo: {} }; continue }
|
||||||
|
if (node.teamInfo) {
|
||||||
|
teams[id] = { teamInfo: node.teamInfo }
|
||||||
|
} else if (node.heroes) {
|
||||||
|
const ti: Record<string, any> = {}
|
||||||
|
node.heroes.forEach((h: any, idx: number) => { ti[String(idx + 1)] = h })
|
||||||
|
teams[id] = { teamInfo: ti }
|
||||||
|
} else if (typeof node === 'object') {
|
||||||
|
const hasHero = Object.values(node).some((v: any) => v && typeof v === 'object' && 'heroId' in v)
|
||||||
|
teams[id] = { teamInfo: hasHero ? node : {} }
|
||||||
|
} else {
|
||||||
|
teams[id] = { teamInfo: {} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { useTeamId: Number(useTeamId) || 1, teams }
|
||||||
|
}
|
||||||
|
|
||||||
|
const presetTeam = computed(() => normalizePresetTeam(presetTeamRaw.value))
|
||||||
|
|
||||||
|
// —— 英雄列表 ——
|
||||||
const currentTeamHeroes = computed(() => {
|
const currentTeamHeroes = computed(() => {
|
||||||
if (!presetTeamInfo.value) {
|
const team = presetTeam.value.teams[currentTeam.value]?.teamInfo
|
||||||
console.log('👥 TeamStatus: presetTeamInfo 为空')
|
if (!team) return []
|
||||||
return []
|
const heroes: Array<{ id: number; name: string; type: string; position: number; level?: number; avatar?: string }> = []
|
||||||
}
|
for (const [pos, hero] of Object.entries(team)) {
|
||||||
|
const hid = (hero as any)?.heroId ?? (hero as any)?.id
|
||||||
console.log('👥 TeamStatus: 当前队伍信息结构:', {
|
if (!hid) continue
|
||||||
presetTeamInfo: presetTeamInfo.value,
|
const meta = HERO_DICT[Number(hid)]
|
||||||
currentTeam: currentTeam.value,
|
|
||||||
hasPresetTeamInfo: !!presetTeamInfo.value.presetTeamInfo,
|
|
||||||
presetTeamInfoKeys: presetTeamInfo.value.presetTeamInfo ? Object.keys(presetTeamInfo.value.presetTeamInfo) : []
|
|
||||||
})
|
|
||||||
|
|
||||||
// 尝试多种可能的数据结构
|
|
||||||
let teamData = null
|
|
||||||
|
|
||||||
// 方式1: 标准结构 presetTeamInfo[teamId].teamInfo
|
|
||||||
if (presetTeamInfo.value.presetTeamInfo?.[currentTeam.value]?.teamInfo) {
|
|
||||||
teamData = presetTeamInfo.value.presetTeamInfo[currentTeam.value].teamInfo
|
|
||||||
console.log('👥 TeamStatus: 使用标准结构获取队伍数据')
|
|
||||||
}
|
|
||||||
// 方式2: 直接在presetTeamInfo[teamId]下
|
|
||||||
else if (presetTeamInfo.value.presetTeamInfo?.[currentTeam.value]) {
|
|
||||||
const teamInfo = presetTeamInfo.value.presetTeamInfo[currentTeam.value]
|
|
||||||
if (typeof teamInfo === 'object' && !Array.isArray(teamInfo)) {
|
|
||||||
teamData = teamInfo
|
|
||||||
console.log('👥 TeamStatus: 使用直接结构获取队伍数据')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 方式3: 查找任何包含英雄数据的结构
|
|
||||||
else if (presetTeamInfo.value.presetTeamInfo) {
|
|
||||||
for (const [key, value] of Object.entries(presetTeamInfo.value.presetTeamInfo)) {
|
|
||||||
if (value && typeof value === 'object') {
|
|
||||||
// 查找包含heroId或类似字段的数据
|
|
||||||
if (value.teamInfo || value.heroes || value.formation ||
|
|
||||||
Object.values(value).some(v => v && v.heroId)) {
|
|
||||||
teamData = value.teamInfo || value.heroes || value.formation || value
|
|
||||||
console.log(`👥 TeamStatus: 在 ${key} 中找到队伍数据`)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!teamData) {
|
|
||||||
console.log('👥 TeamStatus: 未找到队伍数据')
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('👥 TeamStatus: 解析队伍数据:', teamData)
|
|
||||||
|
|
||||||
// 转换队伍信息为英雄数组
|
|
||||||
const heroes = []
|
|
||||||
|
|
||||||
// 处理不同的数据格式
|
|
||||||
if (Array.isArray(teamData)) {
|
|
||||||
// 数组格式
|
|
||||||
teamData.forEach((hero, index) => {
|
|
||||||
if (hero && (hero.heroId || hero.id)) {
|
|
||||||
heroes.push({
|
heroes.push({
|
||||||
id: hero.heroId || hero.id,
|
id: Number(hid),
|
||||||
name: getHeroName(hero.heroId || hero.id),
|
name: meta?.name ?? `英雄${hid}`,
|
||||||
position: index + 1,
|
type: meta?.type ?? '',
|
||||||
level: hero.level || 1
|
position: Number(pos),
|
||||||
|
level: (hero as any)?.level ?? 1,
|
||||||
|
avatar: (hero as any)?.avatar
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
heroes.sort((a, b) => a.position - b.position)
|
||||||
} else if (typeof teamData === 'object') {
|
|
||||||
// 对象格式(position => hero)
|
|
||||||
for (const [position, hero] of Object.entries(teamData)) {
|
|
||||||
if (hero && (hero.heroId || hero.id)) {
|
|
||||||
heroes.push({
|
|
||||||
id: hero.heroId || hero.id,
|
|
||||||
name: getHeroName(hero.heroId || hero.id),
|
|
||||||
position: position,
|
|
||||||
level: hero.level || 1
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('👥 TeamStatus: 解析出的英雄列表:', heroes)
|
|
||||||
return heroes
|
return heroes
|
||||||
})
|
})
|
||||||
|
|
||||||
// 从presetTeamInfo获取可用队伍数量
|
// —— 命令封装 ——
|
||||||
|
const executeGameCommand = async (tokenId: string | number, cmd: string, params: any = {}, description = '', timeout = 8000) => {
|
||||||
|
try {
|
||||||
|
return await tokenStore.sendMessageWithPromise(tokenId, cmd, params, timeout)
|
||||||
|
} catch (error: any) {
|
||||||
|
const msg = error?.message ?? String(error)
|
||||||
|
if (description) message.error(`${description}失败:${msg}`)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// —— 数据加载:缓存优先,可强制刷新 ——
|
||||||
|
const getTeamInfoWithCache = async (force = false) => {
|
||||||
|
if (!tokenStore.selectedToken) {
|
||||||
|
message.warning('请先选择Token')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const tokenId = tokenStore.selectedToken.id
|
||||||
|
|
||||||
|
if (!force) {
|
||||||
|
const cached = tokenStore.gameData?.presetTeam?.presetTeamInfo
|
||||||
|
if (cached) return cached
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const result = await executeGameCommand(tokenId, 'presetteam_getinfo', {}, '获取阵容信息')
|
||||||
|
tokenStore.$patch((state) => {
|
||||||
|
state.gameData = { ...(state.gameData ?? {}), presetTeam: result }
|
||||||
|
})
|
||||||
|
return result?.presetTeamInfo ?? null
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// —— UI 同步 ——
|
||||||
const updateAvailableTeams = () => {
|
const updateAvailableTeams = () => {
|
||||||
if (!presetTeamInfo.value?.presetTeamInfo) return
|
const ids = Object.keys(presetTeam.value.teams).map(Number).filter(n => !Number.isNaN(n)).sort((a, b) => a - b)
|
||||||
|
availableTeams.value = ids.length ? ids : [1, 2, 3, 4]
|
||||||
const teams = Object.keys(presetTeamInfo.value.presetTeamInfo)
|
|
||||||
.map(Number)
|
|
||||||
.filter(num => !isNaN(num))
|
|
||||||
.sort((a, b) => a - b)
|
|
||||||
|
|
||||||
if (teams.length > 0) {
|
|
||||||
availableTeams.value = teams
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新当前队伍
|
|
||||||
const updateCurrentTeam = () => {
|
|
||||||
if (presetTeamInfo.value?.presetTeamInfo?.useTeamId) {
|
|
||||||
currentTeam.value = presetTeamInfo.value.presetTeamInfo.useTeamId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取英雄名称(这里需要英雄数据字典)
|
|
||||||
const getHeroName = (heroId) => {
|
|
||||||
// 暂时返回英雄ID,后续可以添加英雄名称映射
|
|
||||||
const heroNames = {
|
|
||||||
1: '剑士',
|
|
||||||
2: '法师',
|
|
||||||
3: '弓手',
|
|
||||||
4: '盗贼',
|
|
||||||
5: '牧师'
|
|
||||||
// 更多英雄映射...
|
|
||||||
}
|
|
||||||
|
|
||||||
return heroNames[heroId] || `英雄${heroId}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// 选择队伍
|
|
||||||
const selectTeam = (teamId) => {
|
|
||||||
if (!tokenStore.selectedToken) {
|
|
||||||
message.warning('请先选择Token')
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
const updateCurrentTeam = () => { currentTeam.value = presetTeam.value.useTeamId || 1 }
|
||||||
|
|
||||||
|
// —— 交互 ——
|
||||||
|
const selectTeam = async (teamId: number) => {
|
||||||
|
if (switching.value || loading.value) return
|
||||||
|
if (!tokenStore.selectedToken) { message.warning('请先选择Token'); return }
|
||||||
|
const prev = currentTeam.value
|
||||||
|
switching.value = true
|
||||||
|
try {
|
||||||
|
await executeGameCommand(tokenStore.selectedToken.id, 'presetteam_saveteam', { teamId }, `切换到阵容 ${teamId}`)
|
||||||
currentTeam.value = teamId
|
currentTeam.value = teamId
|
||||||
|
message.success(`已切换到阵容 ${teamId}`)
|
||||||
// 发送切换队伍的消息
|
await refreshTeamData(true)
|
||||||
const tokenId = tokenStore.selectedToken.id
|
} catch (e) {
|
||||||
tokenStore.sendMessage(tokenId, 'presetteam_saveteam', { teamId })
|
currentTeam.value = prev
|
||||||
|
} finally {
|
||||||
message.info(`切换到阵容 ${teamId}`)
|
switching.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听预设队伍信息变化
|
const refreshTeamData = async (force = false) => { await getTeamInfoWithCache(force) }
|
||||||
watch(presetTeamInfo, (newValue) => {
|
|
||||||
if (newValue) {
|
// —— 首次挂载:先查缓存,再兜底拉接口 ——
|
||||||
updateAvailableTeams()
|
onMounted(async () => {
|
||||||
updateCurrentTeam()
|
await refreshTeamData(false)
|
||||||
|
updateAvailableTeams(); updateCurrentTeam()
|
||||||
|
if (!presetTeamRaw.value) {
|
||||||
|
await refreshTeamData(true)
|
||||||
|
updateAvailableTeams(); updateCurrentTeam()
|
||||||
}
|
}
|
||||||
}, { deep: true, immediate: true })
|
|
||||||
|
|
||||||
// 刷新队伍数据
|
|
||||||
const refreshTeamData = () => {
|
|
||||||
if (!tokenStore.selectedToken) {
|
|
||||||
message.warning('请先选择Token')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokenId = tokenStore.selectedToken.id
|
|
||||||
console.log('👥 手动刷新队伍数据')
|
|
||||||
|
|
||||||
// 发送多个可能的队伍相关命令
|
|
||||||
const commands = [
|
|
||||||
'presetteam_getteam',
|
|
||||||
'role_gettargetteam',
|
|
||||||
'role_getroleinfo' // 角色信息中可能包含队伍数据
|
|
||||||
]
|
|
||||||
|
|
||||||
commands.forEach(cmd => {
|
|
||||||
tokenStore.sendMessage(tokenId, cmd, {})
|
|
||||||
console.log(`👥 发送命令: ${cmd}`)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
message.info('正在刷新队伍数据...')
|
// —— 监听缓存变化(其他地方写入也能联动) ——
|
||||||
}
|
watch(() => presetTeamRaw.value, () => { updateAvailableTeams(); updateCurrentTeam() }, { deep: true })
|
||||||
|
|
||||||
// 生命周期
|
|
||||||
onMounted(() => {
|
|
||||||
// 获取队伍信息
|
|
||||||
refreshTeamData()
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -275,187 +266,121 @@ onMounted(() => {
|
|||||||
padding: var(--spacing-lg);
|
padding: var(--spacing-lg);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
transition: all var(--transition-normal);
|
transition: all var(--transition-normal);
|
||||||
|
&:hover { box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); transform: translateY(-2px); }
|
||||||
&:hover {
|
|
||||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
}
|
||||||
|
.card-header { display: flex; align-items: flex-start; gap: var(--spacing-md); margin-bottom: var(--spacing-lg); }
|
||||||
|
.team-icon { width: 32px; height: 32px; object-fit: contain; flex-shrink: 0; }
|
||||||
|
.team-info { flex: 1;
|
||||||
|
h3 { font-size: var(--font-size-md); font-weight: var(--font-weight-semibold); color: var(--text-primary); margin: 0 0 var(--spacing-xs) 0; }
|
||||||
|
p { font-size: var(--font-size-sm); color: var(--text-secondary); margin: 0; }
|
||||||
}
|
}
|
||||||
|
.team-selector { display: flex; gap: var(--spacing-xs); }
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: var(--spacing-md);
|
|
||||||
margin-bottom: var(--spacing-lg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.team-icon {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
object-fit: contain;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.team-info {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: var(--font-size-md);
|
|
||||||
font-weight: var(--font-weight-semibold);
|
|
||||||
color: var(--text-primary);
|
|
||||||
margin: 0 0 var(--spacing-xs) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: var(--font-size-sm);
|
|
||||||
color: var(--text-secondary);
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.team-selector {
|
|
||||||
display: flex;
|
|
||||||
gap: var(--spacing-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.team-button {
|
.team-button {
|
||||||
width: 32px;
|
width: 32px; height: 32px; border: none; border-radius: 50%;
|
||||||
height: 32px;
|
background: var(--bg-tertiary); color: var(--text-secondary);
|
||||||
border: none;
|
font-size: var(--font-size-sm); font-weight: var(--font-weight-medium);
|
||||||
border-radius: 50%;
|
cursor: pointer; transition: all var(--transition-fast);
|
||||||
background: var(--bg-tertiary);
|
&:hover { background: var(--bg-secondary); }
|
||||||
color: var(--text-secondary);
|
&.active { background: var(--primary-color); color: white; }
|
||||||
font-size: var(--font-size-sm);
|
&.refresh-button { background: var(--success-color, #10b981); color: white; &:hover { background: var(--success-color-dark, #059669); } }
|
||||||
font-weight: var(--font-weight-medium);
|
&:disabled { opacity: .6; cursor: not-allowed; }
|
||||||
cursor: pointer;
|
|
||||||
transition: all var(--transition-fast);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
}
|
}
|
||||||
|
.card-content .current-team-info {
|
||||||
&.active {
|
display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--spacing-lg);
|
||||||
background: var(--primary-color);
|
.label { font-size: var(--font-size-sm); color: var(--text-secondary); }
|
||||||
color: white;
|
.team-number { font-size: var(--font-size-lg); font-weight: var(--font-weight-bold); color: var(--text-primary); }
|
||||||
}
|
}
|
||||||
|
|
||||||
&.refresh-button {
|
|
||||||
background: var(--success-color, #10b981);
|
|
||||||
color: white;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: var(--success-color-dark, #059669);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content {
|
|
||||||
.current-team-info {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: var(--spacing-lg);
|
|
||||||
|
|
||||||
.label {
|
|
||||||
font-size: var(--font-size-sm);
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.team-number {
|
|
||||||
font-size: var(--font-size-lg);
|
|
||||||
font-weight: var(--font-weight-bold);
|
|
||||||
color: var(--text-primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.heroes-container {
|
.heroes-container {
|
||||||
background: var(--bg-tertiary);
|
background: var(--bg-tertiary);
|
||||||
border-radius: var(--border-radius-medium);
|
border-radius: var(--border-radius-medium);
|
||||||
padding: var(--spacing-md);
|
padding: var(--spacing-md);
|
||||||
min-height: 120px;
|
min-height: 60px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heroes-grid {
|
.heroes-inline {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: var(--spacing-sm);
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: var(--spacing-md);
|
|
||||||
justify-content: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-card {
|
.hero-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: var(--spacing-xs);
|
gap: 4px;
|
||||||
padding: var(--spacing-sm);
|
min-width: 50px;
|
||||||
border-radius: var(--border-radius-medium);
|
|
||||||
background: white;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
transition: all var(--transition-fast);
|
|
||||||
min-width: 80px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
|
||||||
transform: translateY(-1px);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-avatar {
|
.hero-circle {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 2px solid var(--border-color, #e5e5e5);
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-avatar {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-placeholder {
|
.hero-placeholder {
|
||||||
width: 40px;
|
width: 100%;
|
||||||
height: 40px;
|
height: 100%;
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--primary-color);
|
background: var(--primary-color);
|
||||||
color: white;
|
color: white;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: var(--font-size-sm);
|
font-size: 12px;
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-name {
|
.hero-name {
|
||||||
font-size: var(--font-size-xs);
|
font-size: 11px;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: var(--font-weight-medium);
|
font-weight: var(--font-weight-medium);
|
||||||
|
max-width: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
.empty-team { text-align: center; color: var(--text-secondary); p { margin: 0; font-size: var(--font-size-sm); }
|
||||||
.empty-team {
|
|
||||||
text-align: center;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: var(--font-size-sm);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 响应式设计
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.card-header {
|
.card-header {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--spacing-sm);
|
gap: var(--spacing-sm);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-selector {
|
.team-selector {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
.heroes-inline {
|
||||||
.heroes-grid {
|
justify-content: center;
|
||||||
flex-direction: column;
|
gap: var(--spacing-xs);
|
||||||
gap: var(--spacing-sm);
|
}
|
||||||
|
.hero-item {
|
||||||
|
min-width: 45px;
|
||||||
|
}
|
||||||
|
.hero-circle {
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
.hero-name {
|
||||||
|
font-size: 10px;
|
||||||
|
max-width: 45px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, watch } from 'vue'
|
import {computed, onMounted, ref, watch} from 'vue'
|
||||||
import {useTokenStore} from '@/stores/tokenStore'
|
import {useTokenStore} from '@/stores/tokenStore'
|
||||||
import {useMessage} from 'naive-ui'
|
import {useMessage} from 'naive-ui'
|
||||||
|
|
||||||
@@ -72,66 +72,43 @@ const lastClimbResult = ref(null) // 最后一次爬塔结果
|
|||||||
// 计算属性 - 从gameData中获取塔相关信息
|
// 计算属性 - 从gameData中获取塔相关信息
|
||||||
const roleInfo = computed(() => {
|
const roleInfo = computed(() => {
|
||||||
const data = tokenStore.gameData?.roleInfo || null
|
const data = tokenStore.gameData?.roleInfo || null
|
||||||
console.log('🗼 TowerStatus roleInfo 计算属性更新:', data)
|
|
||||||
if (data?.role?.tower) {
|
|
||||||
console.log('🗼 TowerStatus 发现tower数据:', data.role.tower)
|
|
||||||
} else {
|
|
||||||
console.log('🗼 TowerStatus 没有找到tower数据, gameData:', tokenStore.gameData)
|
|
||||||
}
|
|
||||||
return data
|
return data
|
||||||
})
|
})
|
||||||
|
|
||||||
const currentFloor = computed(() => {
|
const currentFloor = computed(() => {
|
||||||
const tower = roleInfo.value?.role?.tower
|
const tower = roleInfo.value?.role?.tower
|
||||||
console.log('🗼 TowerStatus currentFloor 计算属性更新')
|
|
||||||
console.log('🗼 TowerStatus 输入的tower数据:', tower)
|
|
||||||
console.log('🗼 TowerStatus 完整的roleInfo:', roleInfo.value)
|
|
||||||
|
|
||||||
if (!tower) {
|
if (!tower) {
|
||||||
console.log('🗼 没有tower对象,显示默认值')
|
|
||||||
return "0 - 0"
|
return "0 - 0"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tower.id && tower.id !== 0) {
|
if (!tower.id && tower.id !== 0) {
|
||||||
console.log('🗼 没有塔ID或ID无效,显示默认值, tower.id:', tower.id)
|
|
||||||
return "0 - 0"
|
return "0 - 0"
|
||||||
}
|
}
|
||||||
|
|
||||||
const towerId = tower.id
|
const towerId = tower.id
|
||||||
const floor = Math.floor(towerId / 10) + 1
|
const floor = Math.floor(towerId / 10) + 1
|
||||||
const layer = towerId % 10 + 1
|
const layer = towerId % 10 + 1
|
||||||
const result = `${floor} - ${layer}`
|
return `${floor} - ${layer}`
|
||||||
console.log(`🗼 计算层数: towerId=${towerId} -> floor=${floor}, layer=${layer} -> ${result}`)
|
|
||||||
return result
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const towerEnergy = computed(() => {
|
const towerEnergy = computed(() => {
|
||||||
const tower = roleInfo.value?.role?.tower
|
const tower = roleInfo.value?.role?.tower
|
||||||
console.log('🗼 TowerStatus towerEnergy 计算属性更新')
|
|
||||||
console.log('🗼 TowerStatus tower对象:', tower)
|
|
||||||
|
|
||||||
const energy = tower?.energy || 0
|
const energy = tower?.energy || 0
|
||||||
console.log('🗼 TowerStatus 计算出的energy:', energy)
|
|
||||||
return energy
|
return energy
|
||||||
})
|
})
|
||||||
|
|
||||||
const canClimb = computed(() => {
|
const canClimb = computed(() => {
|
||||||
const hasEnergy = towerEnergy.value > 0
|
const hasEnergy = towerEnergy.value > 0
|
||||||
const notClimbing = !isClimbing.value
|
const notClimbing = !isClimbing.value
|
||||||
console.log(`🗼 canClimb 计算: hasEnergy=${hasEnergy}, notClimbing=${notClimbing}, result=${hasEnergy && notClimbing}`)
|
|
||||||
return 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
|
||||||
@@ -149,36 +126,27 @@ const startTowerClimb = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 确保在操作开始前设置状态
|
// 确保在操作开始前设置状态
|
||||||
console.log('🗼 设置爬塔状态为true')
|
|
||||||
isClimbing.value = true
|
isClimbing.value = true
|
||||||
|
|
||||||
// 设置超时保护,15秒后自动重置状态
|
// 设置超时保护,15秒后自动重置状态
|
||||||
climbTimeout.value = setTimeout(() => {
|
climbTimeout.value = setTimeout(() => {
|
||||||
console.log('🗼 超时保护触发,自动重置爬塔状态')
|
|
||||||
isClimbing.value = false
|
isClimbing.value = false
|
||||||
climbTimeout.value = null
|
climbTimeout.value = null
|
||||||
}, 15000)
|
}, 15000)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tokenId = tokenStore.selectedToken.id
|
const tokenId = tokenStore.selectedToken.id
|
||||||
console.log('🗼 使用Token ID:', tokenId)
|
|
||||||
|
|
||||||
message.info('开始爬塔挑战...')
|
|
||||||
|
|
||||||
// 发送爬塔命令
|
// 发送爬塔命令
|
||||||
console.log('🗼 发送爬塔命令...')
|
|
||||||
await tokenStore.sendMessageWithPromise(tokenId, 'fight_starttower', {}, 10000)
|
await tokenStore.sendMessageWithPromise(tokenId, 'fight_starttower', {}, 10000)
|
||||||
|
|
||||||
console.log('🗼 爬塔命令发送成功')
|
|
||||||
message.success('爬塔命令已发送')
|
message.success('爬塔命令已发送')
|
||||||
|
|
||||||
// 立即查询塔信息以获取最新状态
|
// 立即查询塔信息以获取最新状态
|
||||||
console.log('🗼 爬塔完成,立即查询塔信息')
|
|
||||||
await getTowerInfo()
|
await getTowerInfo()
|
||||||
|
|
||||||
// 再延迟查询一次确保数据同步
|
// 再延迟查询一次确保数据同步
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
console.log('🗼 延迟查询塔信息')
|
|
||||||
await getTowerInfo()
|
await getTowerInfo()
|
||||||
|
|
||||||
// 清除超时并重置状态
|
// 清除超时并重置状态
|
||||||
@@ -186,12 +154,10 @@ const startTowerClimb = async () => {
|
|||||||
clearTimeout(climbTimeout.value)
|
clearTimeout(climbTimeout.value)
|
||||||
climbTimeout.value = null
|
climbTimeout.value = null
|
||||||
}
|
}
|
||||||
console.log('🗼 延迟查询完成,重置爬塔状态')
|
|
||||||
isClimbing.value = false
|
isClimbing.value = false
|
||||||
}, 3000)
|
}, 2000)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('🗼 爬塔失败:', error)
|
|
||||||
message.error('爬塔失败: ' + (error.message || '未知错误'))
|
message.error('爬塔失败: ' + (error.message || '未知错误'))
|
||||||
|
|
||||||
// 发生错误时立即重置状态
|
// 发生错误时立即重置状态
|
||||||
@@ -199,7 +165,6 @@ const startTowerClimb = async () => {
|
|||||||
clearTimeout(climbTimeout.value)
|
clearTimeout(climbTimeout.value)
|
||||||
climbTimeout.value = null
|
climbTimeout.value = null
|
||||||
}
|
}
|
||||||
console.log('🗼 发生错误,立即重置爬塔状态')
|
|
||||||
isClimbing.value = false
|
isClimbing.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,12 +174,11 @@ const startTowerClimb = async () => {
|
|||||||
|
|
||||||
// 重置爬塔状态的方法
|
// 重置爬塔状态的方法
|
||||||
const resetClimbingState = () => {
|
const resetClimbingState = () => {
|
||||||
console.log('🗼 用户手动重置爬塔状态')
|
|
||||||
if (climbTimeout.value) {
|
if (climbTimeout.value) {
|
||||||
clearTimeout(climbTimeout.value)
|
clearTimeout(climbTimeout.value)
|
||||||
climbTimeout.value = null
|
climbTimeout.value = null
|
||||||
}
|
}
|
||||||
isClimbing.value = falsexian1xian
|
isClimbing.value = false
|
||||||
message.info('爬塔状态已重置')
|
message.info('爬塔状态已重置')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,36 +190,19 @@ const getTowerInfo = async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const tokenId = tokenStore.selectedToken.id
|
const tokenId = tokenStore.selectedToken.id
|
||||||
console.log('🗼 getTowerInfo: 开始获取塔信息, tokenId:', tokenId)
|
|
||||||
|
|
||||||
// 检查WebSocket连接状态
|
// 检查WebSocket连接状态
|
||||||
const wsStatus = tokenStore.getWebSocketStatus(tokenId)
|
const wsStatus = tokenStore.getWebSocketStatus(tokenId)
|
||||||
console.log('🗼 getTowerInfo: WebSocket状态:', wsStatus)
|
|
||||||
|
|
||||||
if (wsStatus !== 'connected') {
|
if (wsStatus !== 'connected') {
|
||||||
console.warn('🗼 getTowerInfo: WebSocket未连接,无法获取数据')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 首先获取角色信息,这包含了塔的数据
|
// 首先获取角色信息,这包含了塔的数据
|
||||||
console.log('🗼 getTowerInfo: 正在请求角色信息...')
|
|
||||||
const roleResult = tokenStore.sendMessage(tokenId, 'role_getroleinfo')
|
const roleResult = tokenStore.sendMessage(tokenId, 'role_getroleinfo')
|
||||||
console.log('🗼 getTowerInfo: 角色信息请求结果:', roleResult)
|
|
||||||
|
|
||||||
// 直接请求塔信息
|
// 直接请求塔信息
|
||||||
console.log('🗼 getTowerInfo: 正在请求塔信息...')
|
|
||||||
const towerResult = tokenStore.sendMessage(tokenId, 'tower_getinfo')
|
const towerResult = tokenStore.sendMessage(tokenId, 'tower_getinfo')
|
||||||
console.log('🗼 getTowerInfo: 塔信息请求结果:', towerResult)
|
|
||||||
|
|
||||||
// 检查当前gameData状态
|
|
||||||
console.log('🗼 getTowerInfo: 当前gameData:', tokenStore.gameData)
|
|
||||||
console.log('🗼 getTowerInfo: 当前roleInfo:', tokenStore.gameData?.roleInfo)
|
|
||||||
console.log('🗼 getTowerInfo: 当前tower数据:', tokenStore.gameData?.roleInfo?.role?.tower)
|
|
||||||
|
|
||||||
if (!roleResult && !towerResult) {
|
if (!roleResult && !towerResult) {
|
||||||
console.error('🗼 getTowerInfo: 所有请求都失败了')
|
console.error('🗼 getTowerInfo: 所有请求都失败了')
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('🗼 getTowerInfo: 获取塔信息失败:', error)
|
console.error('🗼 getTowerInfo: 获取塔信息失败:', error)
|
||||||
}
|
}
|
||||||
@@ -327,24 +274,15 @@ watch(() => tokenStore.gameData.towerResult, (newResult, oldResult) => {
|
|||||||
|
|
||||||
// 生命周期
|
// 生命周期
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
console.log('🗼 TowerStatus 组件已挂载')
|
|
||||||
console.log('🗼 当前选中Token:', tokenStore.selectedToken?.name)
|
|
||||||
console.log('🗼 当前选中Token ID:', tokenStore.selectedToken?.id)
|
|
||||||
console.log('🗼 当前WebSocket状态:', wsStatus.value)
|
|
||||||
console.log('🗼 当前游戏数据:', tokenStore.gameData)
|
|
||||||
console.log('🗼 当前roleInfo:', tokenStore.gameData?.roleInfo)
|
|
||||||
console.log('🗼 当前tower数据:', tokenStore.gameData?.roleInfo?.role?.tower)
|
|
||||||
|
|
||||||
// 检查WebSocket客户端
|
// 检查WebSocket客户端
|
||||||
if (tokenStore.selectedToken) {
|
if (tokenStore.selectedToken) {
|
||||||
const client = tokenStore.getWebSocketClient(tokenStore.selectedToken.id)
|
const client = tokenStore.getWebSocketClient(tokenStore.selectedToken.id)
|
||||||
console.log('🗼 WebSocket客户端:', client)
|
|
||||||
console.log('🗼 WebSocket客户端状态:', client ? 'exists' : 'null')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 组件挂载时获取塔信息
|
// 组件挂载时获取塔信息
|
||||||
if (tokenStore.selectedToken && wsStatus.value === 'connected') {
|
if (tokenStore.selectedToken && wsStatus.value === 'connected') {
|
||||||
console.log('🗼 条件满足,开始获取塔信息')
|
|
||||||
getTowerInfo()
|
getTowerInfo()
|
||||||
} else if (!tokenStore.selectedToken) {
|
} else if (!tokenStore.selectedToken) {
|
||||||
console.log('🗼 没有选中的Token,无法获取塔信息')
|
console.log('🗼 没有选中的Token,无法获取塔信息')
|
||||||
|
|||||||
@@ -712,9 +712,23 @@ export const useTokenStore = defineStore('tokens', () => {
|
|||||||
return sendMessage(tokenId, 'heart_beat')
|
return sendMessage(tokenId, 'heart_beat')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送获取角色信息请求
|
// 发送获取角色信息请求(异步处理)
|
||||||
const sendGetRoleInfo = (tokenId, params = {}) => {
|
const sendGetRoleInfo = async (tokenId, params = {}) => {
|
||||||
return sendMessageWithPromise(tokenId, 'role_getroleinfo', params)
|
try {
|
||||||
|
const roleInfo = await sendMessageWithPromise(tokenId, 'role_getroleinfo', params, 10000)
|
||||||
|
|
||||||
|
// 手动更新游戏数据(因为响应可能不会自动触发消息处理)
|
||||||
|
if (roleInfo) {
|
||||||
|
gameData.value.roleInfo = roleInfo
|
||||||
|
gameData.value.lastUpdated = new Date().toISOString()
|
||||||
|
console.log('📊 角色信息已通过 Promise 更新')
|
||||||
|
}
|
||||||
|
|
||||||
|
return roleInfo
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ 获取角色信息失败 [${tokenId}]:`, error.message)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送获取数据版本请求
|
// 发送获取数据版本请求
|
||||||
|
|||||||
@@ -382,27 +382,29 @@ export class XyzwWebSocketClient {
|
|||||||
|
|
||||||
/** Promise 版发送 */
|
/** Promise 版发送 */
|
||||||
sendWithPromise(cmd, params = {}, timeoutMs = 5000) {
|
sendWithPromise(cmd, params = {}, timeoutMs = 5000) {
|
||||||
const respKey = `${cmd}_${this.seq + 1}`
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!this.connected && !this.socket) {
|
if (!this.connected && !this.socket) {
|
||||||
return reject(new Error("WebSocket 连接已关闭"))
|
return reject(new Error("WebSocket 连接已关闭"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 生成唯一的请求ID
|
||||||
|
const requestId = `${cmd}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||||
|
|
||||||
// 设置 Promise 状态
|
// 设置 Promise 状态
|
||||||
this.promises[respKey] = { resolve, reject }
|
this.promises[requestId] = { resolve, reject, originalCmd: cmd }
|
||||||
|
|
||||||
// 超时处理
|
// 超时处理
|
||||||
const timer = setTimeout(() => {
|
const timer = setTimeout(() => {
|
||||||
delete this.promises[respKey]
|
delete this.promises[requestId]
|
||||||
reject(new Error(`请求超时: ${cmd} (${timeoutMs}ms)`))
|
reject(new Error(`请求超时: ${cmd} (${timeoutMs}ms)`))
|
||||||
}, timeoutMs)
|
}, timeoutMs)
|
||||||
|
|
||||||
// 发送消息
|
// 发送消息
|
||||||
this.send(cmd, params, {
|
this.send(cmd, params, {
|
||||||
respKey,
|
respKey: requestId,
|
||||||
onSent: () => {
|
onSent: () => {
|
||||||
clearTimeout(timer)
|
// 消息发送成功后,不要清除超时器,让它继续等待响应
|
||||||
|
// 只有在收到响应或超时时才清除
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -513,13 +515,11 @@ export class XyzwWebSocketClient {
|
|||||||
|
|
||||||
// 命令到响应的映射 - 处理响应命令与原始命令不匹配的情况
|
// 命令到响应的映射 - 处理响应命令与原始命令不匹配的情况
|
||||||
const responseToCommandMap = {
|
const responseToCommandMap = {
|
||||||
|
// 1:1 响应映射(优先级高)
|
||||||
'role_getroleinforesp': 'role_getroleinfo',
|
'role_getroleinforesp': 'role_getroleinfo',
|
||||||
'system_signinrewardresp': 'system_signinreward',
|
|
||||||
'hero_recruitresp': 'hero_recruit',
|
'hero_recruitresp': 'hero_recruit',
|
||||||
'friend_batchresp': 'friend_batch',
|
'friend_batchresp': 'friend_batch',
|
||||||
'system_claimhanguprewardresp': 'system_claimhangupreward',
|
'system_claimhanguprewardresp': 'system_claimhangupreward',
|
||||||
'task_claimdailyrewardresp': 'task_claimdailyreward',
|
|
||||||
'task_claimweekrewardresp': 'task_claimweekreward',
|
|
||||||
'item_openboxresp': 'item_openbox',
|
'item_openboxresp': 'item_openbox',
|
||||||
'bottlehelper_claimresp': 'bottlehelper_claim',
|
'bottlehelper_claimresp': 'bottlehelper_claim',
|
||||||
'bottlehelper_startresp': 'bottlehelper_start',
|
'bottlehelper_startresp': 'bottlehelper_start',
|
||||||
@@ -532,23 +532,46 @@ export class XyzwWebSocketClient {
|
|||||||
'arena_getareatargetresp': 'arena_getareatarget',
|
'arena_getareatargetresp': 'arena_getareatarget',
|
||||||
'presetteam_getinforesp': 'presetteam_getinfo',
|
'presetteam_getinforesp': 'presetteam_getinfo',
|
||||||
'presetteam_saveteamresp': 'presetteam_saveteam',
|
'presetteam_saveteamresp': 'presetteam_saveteam',
|
||||||
|
'presetteam_getteamresp': 'presetteam_getteam',
|
||||||
'mail_claimallattachmentresp': 'mail_claimallattachment',
|
'mail_claimallattachmentresp': 'mail_claimallattachment',
|
||||||
'store_buyresp': 'store_purchase'
|
'store_buyresp': 'store_purchase',
|
||||||
|
'system_getdatabundleverresp': 'system_getdatabundlever',
|
||||||
|
'tower_claimrewardresp': 'tower_claimreward',
|
||||||
|
'fight_starttowerresp': 'fight_starttower',
|
||||||
|
|
||||||
|
// 特殊响应映射 - 有些命令有独立响应,有些用同步响应
|
||||||
|
'task_claimdailyrewardresp': 'task_claimdailyreward',
|
||||||
|
'task_claimweekrewardresp': 'task_claimweekreward',
|
||||||
|
|
||||||
|
// 同步响应映射(优先级低)
|
||||||
|
'syncresp': ['system_mysharecallback', 'task_claimdailypoint'],
|
||||||
|
'syncrewardresp': ['system_buygold', 'discount_claimreward', 'card_claimreward',
|
||||||
|
'artifact_lottery', 'genie_sweep', 'genie_buysweep','system_signinreward']
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取原始命令名
|
// 获取原始命令名(支持一对一和一对多映射)
|
||||||
const originalCmd = responseToCommandMap[cmd] || cmd
|
let originalCmds = responseToCommandMap[cmd]
|
||||||
|
if (!originalCmds) {
|
||||||
|
originalCmds = [cmd] // 如果没有映射,使用响应命令本身
|
||||||
|
} else if (typeof originalCmds === 'string') {
|
||||||
|
originalCmds = [originalCmds] // 转换为数组
|
||||||
|
}
|
||||||
|
|
||||||
// 查找对应的 Promise
|
// 查找对应的 Promise - 遍历所有等待中的 Promise
|
||||||
for (const [key, promise] of Object.entries(this.promises)) {
|
for (const [requestId, promiseData] of Object.entries(this.promises)) {
|
||||||
// 检查是否匹配原始命令或响应命令
|
// 检查 Promise 是否匹配当前响应的任一原始命令
|
||||||
if (key.startsWith(originalCmd) || key.startsWith(cmd) || cmd === key) {
|
if (originalCmds.includes(promiseData.originalCmd)) {
|
||||||
delete this.promises[key]
|
delete this.promises[requestId]
|
||||||
|
|
||||||
|
// 获取响应数据,优先使用 rawData(ProtoMsg 自动解码),然后 decodedBody(手动解码),最后 body
|
||||||
|
const responseBody = packet.rawData !== undefined ? packet.rawData :
|
||||||
|
packet.decodedBody !== undefined ? packet.decodedBody :
|
||||||
|
packet.body
|
||||||
|
|
||||||
if (packet.code === 0 || packet.code === undefined) {
|
if (packet.code === 0 || packet.code === undefined) {
|
||||||
promise.resolve(packet.body || packet)
|
promiseData.resolve(responseBody || packet)
|
||||||
} else {
|
} else {
|
||||||
promise.reject(new Error(`服务器错误: ${packet.code} - ${packet.hint || '未知错误'}`))
|
promiseData.reject(new Error(`服务器错误: ${packet.code} - ${packet.hint || '未知错误'}`))
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,6 +267,77 @@ const bulkActionOptions = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// 等待WebSocket连接并加载阵容数据
|
||||||
|
const loadTeamDataWithConnection = async (tokenId, maxRetries = 3, retryDelay = 2000) => {
|
||||||
|
console.log('每日页面进入,开始检查WebSocket连接状态...')
|
||||||
|
|
||||||
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||||
|
try {
|
||||||
|
// 检查WebSocket连接状态
|
||||||
|
const wsStatus = tokenStore.getWebSocketStatus(tokenId)
|
||||||
|
console.log(`第${attempt}次检查,WebSocket状态:`, wsStatus)
|
||||||
|
|
||||||
|
if (wsStatus !== 'connected') {
|
||||||
|
console.log('WebSocket未连接,尝试建立连接...')
|
||||||
|
|
||||||
|
// 尝试建立WebSocket连接
|
||||||
|
const tokenData = tokenStore.gameTokens.find(t => t.id === tokenId)
|
||||||
|
if (tokenData && tokenData.token) {
|
||||||
|
// 触发WebSocket连接
|
||||||
|
tokenStore.createWebSocketConnection(tokenId, tokenData.token, tokenData.wsUrl)
|
||||||
|
|
||||||
|
// 等待连接建立
|
||||||
|
await new Promise(resolve => setTimeout(resolve, retryDelay))
|
||||||
|
|
||||||
|
// 再次检查连接状态
|
||||||
|
const newStatus = tokenStore.getWebSocketStatus(tokenId)
|
||||||
|
if (newStatus !== 'connected') {
|
||||||
|
if (attempt < maxRetries) {
|
||||||
|
console.log(`连接未建立,${retryDelay / 1000}秒后重试...`)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
throw new Error('WebSocket连接超时')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('未找到有效的Token数据或WebSocket URL')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebSocket已连接,开始加载阵容数据
|
||||||
|
console.log('WebSocket已连接,开始加载阵容数据...')
|
||||||
|
const result = await tokenStore.sendMessageWithPromise(
|
||||||
|
tokenId,
|
||||||
|
'presetteam_getinfo',
|
||||||
|
{},
|
||||||
|
8000
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
// 更新到游戏数据缓存中
|
||||||
|
tokenStore.$patch((state) => {
|
||||||
|
state.gameData = { ...(state.gameData ?? {}), presetTeam: result }
|
||||||
|
})
|
||||||
|
console.log('阵容数据加载成功:', result)
|
||||||
|
message.success('阵容数据已更新')
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`第${attempt}次尝试失败:`, error)
|
||||||
|
|
||||||
|
if (attempt < maxRetries) {
|
||||||
|
console.log(`${retryDelay / 1000}秒后进行第${attempt + 1}次重试...`)
|
||||||
|
await new Promise(resolve => setTimeout(resolve, retryDelay))
|
||||||
|
} else {
|
||||||
|
console.error('所有重试均失败,阵容数据加载失败')
|
||||||
|
message.warning(`阵容数据加载失败: ${error.message || '未知错误'}`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 方法
|
// 方法
|
||||||
const refreshTasks = async () => {
|
const refreshTasks = async () => {
|
||||||
if (!selectedRoleId.value) {
|
if (!selectedRoleId.value) {
|
||||||
@@ -567,6 +638,11 @@ onMounted(async () => {
|
|||||||
await gameRolesStore.fetchGameRoles()
|
await gameRolesStore.fetchGameRoles()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 页面进入时手动调用阵容加载接口,确保WebSocket连接后再调用
|
||||||
|
if (tokenStore.selectedToken) {
|
||||||
|
await loadTeamDataWithConnection(tokenStore.selectedToken.id)
|
||||||
|
}
|
||||||
|
|
||||||
// 设置默认选中的角色
|
// 设置默认选中的角色
|
||||||
if (gameRolesStore.selectedRole) {
|
if (gameRolesStore.selectedRole) {
|
||||||
selectedRoleId.value = gameRolesStore.selectedRole.id
|
selectedRoleId.value = gameRolesStore.selectedRole.id
|
||||||
|
|||||||
Reference in New Issue
Block a user