545 lines
13 KiB
Markdown
545 lines
13 KiB
Markdown
|
|
# 问题修复 - 重试时保留成功Token进度 v3.12.7
|
|||
|
|
|
|||
|
|
**版本**: v3.12.7
|
|||
|
|
**日期**: 2025-10-08
|
|||
|
|
**类型**: 问题修复
|
|||
|
|
|
|||
|
|
## 问题描述
|
|||
|
|
|
|||
|
|
用户反馈:
|
|||
|
|
|
|||
|
|
> "在失败重试阶段,执行进度并未能显示失败token的进度"
|
|||
|
|
|
|||
|
|
**现象**:
|
|||
|
|
- 点击"重试失败"按钮后
|
|||
|
|
- 执行进度界面显示所有Token都是"等待中"状态
|
|||
|
|
- 看不到之前成功的Token的进度
|
|||
|
|
- 看不到正在重试的Token的执行状态
|
|||
|
|
- 统计信息显示:成功0、失败0、跳过0
|
|||
|
|
|
|||
|
|
**预期行为**:
|
|||
|
|
- 之前成功的Token应该显示为"已完成"状态
|
|||
|
|
- 正在重试的Token应该显示为"执行中"状态
|
|||
|
|
- 统计信息应该保留之前的成功数量
|
|||
|
|
|
|||
|
|
## 问题分析
|
|||
|
|
|
|||
|
|
### 根本原因
|
|||
|
|
|
|||
|
|
在 `startBatchExecution` 函数中,无论是全新开始还是重试,都会完全重置 `taskProgress`:
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// 🆕 如果不是继续执行,重置taskProgress
|
|||
|
|
if (!continueFromSaved) {
|
|||
|
|
taskProgress.value = {} // ❌ 完全清空,包括成功的token
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 问题流程
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
第1次执行批量任务:
|
|||
|
|
- 100个Token
|
|||
|
|
- 成功:97个
|
|||
|
|
- 失败:3个
|
|||
|
|
- taskProgress 包含所有100个Token的进度
|
|||
|
|
|
|||
|
|
用户点击"重试失败":
|
|||
|
|
- 调用 retryFailedTasks()
|
|||
|
|
- 筛选出3个失败的Token
|
|||
|
|
- 调用 startBatchExecution(failedTokenIds, tasks, true)
|
|||
|
|
↓
|
|||
|
|
在 startBatchExecution 中:
|
|||
|
|
- isRetry = true
|
|||
|
|
- continueFromSaved = false
|
|||
|
|
- 执行:taskProgress.value = {} ← 清空所有进度
|
|||
|
|
↓
|
|||
|
|
结果:
|
|||
|
|
- 97个成功Token的进度信息丢失 ❌
|
|||
|
|
- UI上看不到任何历史进度 ❌
|
|||
|
|
- 统计数据重置为0 ❌
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 数据流对比
|
|||
|
|
|
|||
|
|
**期望的数据流**:
|
|||
|
|
```
|
|||
|
|
初始状态(执行完成后):
|
|||
|
|
taskProgress = {
|
|||
|
|
'token1': { status: 'completed', ... }, ← 保留
|
|||
|
|
'token2': { status: 'completed', ... }, ← 保留
|
|||
|
|
'token3': { status: 'failed', ... }, ← 删除并重新初始化
|
|||
|
|
'token4': { status: 'completed', ... }, ← 保留
|
|||
|
|
...
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
重试后:
|
|||
|
|
taskProgress = {
|
|||
|
|
'token1': { status: 'completed', ... }, ← 保留
|
|||
|
|
'token2': { status: 'completed', ... }, ← 保留
|
|||
|
|
'token3': { status: 'executing', ... }, ← 新初始化
|
|||
|
|
'token4': { status: 'completed', ... }, ← 保留
|
|||
|
|
...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**实际的数据流(修复前)**:
|
|||
|
|
```
|
|||
|
|
初始状态(执行完成后):
|
|||
|
|
taskProgress = {
|
|||
|
|
'token1': { status: 'completed', ... },
|
|||
|
|
'token2': { status: 'completed', ... },
|
|||
|
|
'token3': { status: 'failed', ... },
|
|||
|
|
'token4': { status: 'completed', ... },
|
|||
|
|
...
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
重试后:
|
|||
|
|
taskProgress = {
|
|||
|
|
'token3': { status: 'executing', ... }, ← 只有这个
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 解决方案
|
|||
|
|
|
|||
|
|
### 修改1:重试时保留成功Token的进度
|
|||
|
|
|
|||
|
|
只删除要重试的Token的进度,保留其他Token的进度:
|
|||
|
|
|
|||
|
|
#### 修改前
|
|||
|
|
```javascript
|
|||
|
|
// 🆕 如果不是继续执行,重置taskProgress
|
|||
|
|
if (!continueFromSaved) {
|
|||
|
|
taskProgress.value = {}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 修改后
|
|||
|
|
```javascript
|
|||
|
|
// 🆕 如果不是继续执行,重置taskProgress
|
|||
|
|
if (!continueFromSaved) {
|
|||
|
|
// 如果是重试模式,保留成功的token进度,只重置失败的token
|
|||
|
|
if (isRetry) {
|
|||
|
|
// 保留已有的进度数据,只清空要重试的token
|
|||
|
|
targetTokens.forEach(tokenId => {
|
|||
|
|
if (taskProgress.value[tokenId]) {
|
|||
|
|
delete taskProgress.value[tokenId]
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
} else {
|
|||
|
|
// 全新开始,清空所有进度
|
|||
|
|
taskProgress.value = {}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 修改2:重试时保留统计数据
|
|||
|
|
|
|||
|
|
保持原有的成功数量,只重置失败计数:
|
|||
|
|
|
|||
|
|
#### 修改前
|
|||
|
|
```javascript
|
|||
|
|
// 🆕 初始化统计(如果不是继续执行)
|
|||
|
|
if (!continueFromSaved) {
|
|||
|
|
executionStats.value = {
|
|||
|
|
total: targetTokens.length,
|
|||
|
|
success: 0,
|
|||
|
|
failed: 0,
|
|||
|
|
skipped: 0,
|
|||
|
|
startTime: Date.now(),
|
|||
|
|
endTime: null
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 修改后
|
|||
|
|
```javascript
|
|||
|
|
// 🆕 初始化统计(如果不是继续执行)
|
|||
|
|
if (!continueFromSaved) {
|
|||
|
|
if (isRetry) {
|
|||
|
|
// 重试模式:保持原有的total和success,重置失败和跳过计数
|
|||
|
|
executionStats.value = {
|
|||
|
|
total: executionStats.value.total,
|
|||
|
|
success: executionStats.value.success,
|
|||
|
|
failed: 0, // 重置失败计数,重新统计
|
|||
|
|
skipped: executionStats.value.skipped,
|
|||
|
|
startTime: Date.now(),
|
|||
|
|
endTime: null
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 全新开始:重置所有统计
|
|||
|
|
executionStats.value = {
|
|||
|
|
total: targetTokens.length,
|
|||
|
|
success: 0,
|
|||
|
|
failed: 0,
|
|||
|
|
skipped: 0,
|
|||
|
|
startTime: Date.now(),
|
|||
|
|
endTime: null
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 修改文件
|
|||
|
|
|
|||
|
|
### src/stores/batchTaskStore.js
|
|||
|
|
|
|||
|
|
**修改位置1**: Line 331-345
|
|||
|
|
- 重试时保留成功Token的进度
|
|||
|
|
- 只删除要重试的Token的进度
|
|||
|
|
|
|||
|
|
**修改位置2**: Line 350-377
|
|||
|
|
- 重试时保留统计数据
|
|||
|
|
- 只重置失败计数
|
|||
|
|
|
|||
|
|
## 用户体验改进
|
|||
|
|
|
|||
|
|
### 修改前
|
|||
|
|
|
|||
|
|
**UI显示**:
|
|||
|
|
```
|
|||
|
|
执行进度:
|
|||
|
|
|
|||
|
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|||
|
|
│ Token1 │ │ Token2 │ │ Token3 │
|
|||
|
|
│ 等待中 │ │ 等待中 │ │ 等待中 │ ← 所有都是等待中
|
|||
|
|
└─────────────┘ └─────────────┘ └─────────────┘
|
|||
|
|
... (其余97个看不到)
|
|||
|
|
|
|||
|
|
统计:
|
|||
|
|
总计:3 成功:0 失败:0 ← 丢失了之前的97个成功
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**问题**:
|
|||
|
|
- ❌ 看不到之前成功的97个Token
|
|||
|
|
- ❌ 统计数据显示0,让用户困惑
|
|||
|
|
- ❌ 看起来像是重新执行所有任务
|
|||
|
|
|
|||
|
|
### 修改后
|
|||
|
|
|
|||
|
|
**UI显示**:
|
|||
|
|
```
|
|||
|
|
执行进度:
|
|||
|
|
|
|||
|
|
✅ 已完成 ✅ 已完成 🔄 执行中
|
|||
|
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|||
|
|
│ Token1 │ │ Token2 │ │ Token3 │
|
|||
|
|
│ 已完成 │ │ 已完成 │ │ 执行中 │ ← 状态清晰
|
|||
|
|
│ 成功: 7 │ │ 成功: 7 │ │ 进度: 42% │
|
|||
|
|
└─────────────┘ └─────────────┘ └─────────────┘
|
|||
|
|
... (包括其余97个已完成的)
|
|||
|
|
|
|||
|
|
统计:
|
|||
|
|
总计:100 成功:97 失败:0 ← 保留了之前的成功数
|
|||
|
|
重试轮数:3/5
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**改进**:
|
|||
|
|
- ✅ 显示所有Token的状态
|
|||
|
|
- ✅ 成功的Token显示为"已完成"
|
|||
|
|
- ✅ 重试的Token显示为"执行中"
|
|||
|
|
- ✅ 统计数据准确反映实际情况
|
|||
|
|
|
|||
|
|
## 技术实现
|
|||
|
|
|
|||
|
|
### 数据结构变化
|
|||
|
|
|
|||
|
|
**修复前(重试时)**:
|
|||
|
|
```javascript
|
|||
|
|
// 执行完第1次后
|
|||
|
|
taskProgress = {
|
|||
|
|
'token1': { status: 'completed', progress: 100, ... }, // 97个
|
|||
|
|
'token2': { status: 'completed', progress: 100, ... },
|
|||
|
|
'token98': { status: 'failed', error: '...', ... }, // 3个
|
|||
|
|
'token99': { status: 'failed', error: '...', ... },
|
|||
|
|
'token100': { status: 'failed', error: '...', ... }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 点击重试后
|
|||
|
|
taskProgress = {} // ❌ 全部清空
|
|||
|
|
|
|||
|
|
// 重新初始化
|
|||
|
|
taskProgress = {
|
|||
|
|
'token98': { status: 'pending', progress: 0, ... },
|
|||
|
|
'token99': { status: 'pending', progress: 0, ... },
|
|||
|
|
'token100': { status: 'pending', progress: 0, ... }
|
|||
|
|
}
|
|||
|
|
// 丢失了97个成功的token ❌
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**修复后(重试时)**:
|
|||
|
|
```javascript
|
|||
|
|
// 执行完第1次后
|
|||
|
|
taskProgress = {
|
|||
|
|
'token1': { status: 'completed', progress: 100, ... }, // 97个
|
|||
|
|
'token2': { status: 'completed', progress: 100, ... },
|
|||
|
|
'token98': { status: 'failed', error: '...', ... }, // 3个
|
|||
|
|
'token99': { status: 'failed', error: '...', ... },
|
|||
|
|
'token100': { status: 'failed', error: '...', ... }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 点击重试后
|
|||
|
|
// 只删除失败的token
|
|||
|
|
delete taskProgress['token98']
|
|||
|
|
delete taskProgress['token99']
|
|||
|
|
delete taskProgress['token100']
|
|||
|
|
|
|||
|
|
taskProgress = {
|
|||
|
|
'token1': { status: 'completed', progress: 100, ... }, // ✅ 保留
|
|||
|
|
'token2': { status: 'completed', progress: 100, ... }, // ✅ 保留
|
|||
|
|
// ... 其余95个成功的也保留
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重新初始化失败的token
|
|||
|
|
taskProgress = {
|
|||
|
|
'token1': { status: 'completed', progress: 100, ... }, // ✅ 保留
|
|||
|
|
'token2': { status: 'completed', progress: 100, ... }, // ✅ 保留
|
|||
|
|
'token98': { status: 'pending', progress: 0, ... }, // ✅ 新初始化
|
|||
|
|
'token99': { status: 'pending', progress: 0, ... }, // ✅ 新初始化
|
|||
|
|
'token100': { status: 'pending', progress: 0, ... } // ✅ 新初始化
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 统计数据处理
|
|||
|
|
|
|||
|
|
**修复前**:
|
|||
|
|
```javascript
|
|||
|
|
// 第1次执行完成
|
|||
|
|
executionStats = {
|
|||
|
|
total: 100,
|
|||
|
|
success: 97,
|
|||
|
|
failed: 3,
|
|||
|
|
skipped: 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重试时
|
|||
|
|
executionStats = {
|
|||
|
|
total: 3, // ❌ 错误:应该是100
|
|||
|
|
success: 0, // ❌ 丢失:应该保留97
|
|||
|
|
failed: 0,
|
|||
|
|
skipped: 0
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**修复后**:
|
|||
|
|
```javascript
|
|||
|
|
// 第1次执行完成
|
|||
|
|
executionStats = {
|
|||
|
|
total: 100,
|
|||
|
|
success: 97,
|
|||
|
|
failed: 3,
|
|||
|
|
skipped: 0
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重试时
|
|||
|
|
executionStats = {
|
|||
|
|
total: 100, // ✅ 保留:总数不变
|
|||
|
|
success: 97, // ✅ 保留:之前的成功数
|
|||
|
|
failed: 0, // ✅ 重置:重新统计重试结果
|
|||
|
|
skipped: 0 // ✅ 保留:之前的跳过数
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重试完成后(假设3个都成功了)
|
|||
|
|
executionStats = {
|
|||
|
|
total: 100,
|
|||
|
|
success: 100, // ✅ 97 + 3 = 100
|
|||
|
|
failed: 0,
|
|||
|
|
skipped: 0
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 重试场景测试
|
|||
|
|
|
|||
|
|
### 场景1:部分失败后重试全部成功
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
第1次执行:
|
|||
|
|
- 总计:100
|
|||
|
|
- 成功:97
|
|||
|
|
- 失败:3
|
|||
|
|
|
|||
|
|
点击"重试失败 (3个)":
|
|||
|
|
- taskProgress 保留97个成功的
|
|||
|
|
- 删除3个失败的
|
|||
|
|
- 重新初始化3个失败的
|
|||
|
|
|
|||
|
|
重试执行中:
|
|||
|
|
- UI显示100个Token卡片
|
|||
|
|
- 97个显示"已完成"
|
|||
|
|
- 3个显示"执行中"
|
|||
|
|
- 统计:总计100,成功97,失败0
|
|||
|
|
|
|||
|
|
重试完成(全部成功):
|
|||
|
|
- UI显示100个Token卡片
|
|||
|
|
- 100个都显示"已完成"
|
|||
|
|
- 统计:总计100,成功100,失败0 ✅
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 场景2:部分失败后重试仍有失败
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
第1次执行:
|
|||
|
|
- 总计:100
|
|||
|
|
- 成功:97
|
|||
|
|
- 失败:3
|
|||
|
|
|
|||
|
|
第1次重试:
|
|||
|
|
- 成功:2(从失败变成功)
|
|||
|
|
- 失败:1(仍然失败)
|
|||
|
|
- 统计:总计100,成功99,失败1
|
|||
|
|
|
|||
|
|
第2次重试:
|
|||
|
|
- 成功:1(最后1个成功)
|
|||
|
|
- 失败:0
|
|||
|
|
- 统计:总计100,成功100,失败0 ✅
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 场景3:多轮重试
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
第1次执行:
|
|||
|
|
- 成功:95,失败:5
|
|||
|
|
|
|||
|
|
第1次重试(自动):
|
|||
|
|
- 成功:3,失败:2
|
|||
|
|
- 统计:总计100,成功98,失败2
|
|||
|
|
|
|||
|
|
第2次重试(自动):
|
|||
|
|
- 成功:1,失败:1
|
|||
|
|
- 统计:总计100,成功99,失败1
|
|||
|
|
|
|||
|
|
第3次重试(自动):
|
|||
|
|
- 成功:0,失败:1
|
|||
|
|
- 统计:总计100,成功99,失败1
|
|||
|
|
- 达到最大重试次数,停止重试
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 边界情况处理
|
|||
|
|
|
|||
|
|
### 情况1:全部失败后重试
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
第1次执行:
|
|||
|
|
- 成功:0
|
|||
|
|
- 失败:100
|
|||
|
|
|
|||
|
|
重试:
|
|||
|
|
- taskProgress 原本就是空的(或只有失败的)
|
|||
|
|
- 删除所有失败的
|
|||
|
|
- 重新初始化所有token
|
|||
|
|
- 行为正常 ✅
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 情况2:全部成功(无需重试)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
第1次执行:
|
|||
|
|
- 成功:100
|
|||
|
|
- 失败:0
|
|||
|
|
|
|||
|
|
点击重试:
|
|||
|
|
- failedTokenIds = []
|
|||
|
|
- 提示"没有失败的任务需要重试"
|
|||
|
|
- 不执行重试
|
|||
|
|
- 行为正常 ✅
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 情况3:连续重试
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
第1次执行:
|
|||
|
|
- 成功:97,失败:3
|
|||
|
|
|
|||
|
|
第1次重试:
|
|||
|
|
- 成功:2,失败:1
|
|||
|
|
- taskProgress 保留99个(97+2)
|
|||
|
|
- 删除1个失败的
|
|||
|
|
- 统计正确 ✅
|
|||
|
|
|
|||
|
|
第2次重试:
|
|||
|
|
- 成功:1,失败:0
|
|||
|
|
- taskProgress 保留100个
|
|||
|
|
- 统计正确 ✅
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 代码逻辑说明
|
|||
|
|
|
|||
|
|
### 删除失败Token的逻辑
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
if (isRetry) {
|
|||
|
|
// 遍历要重试的token(失败的token)
|
|||
|
|
targetTokens.forEach(tokenId => {
|
|||
|
|
// 如果该token在taskProgress中存在,删除它
|
|||
|
|
if (taskProgress.value[tokenId]) {
|
|||
|
|
delete taskProgress.value[tokenId]
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**为什么使用 delete?**
|
|||
|
|
- `delete` 从对象中完全移除属性
|
|||
|
|
- 不留痕迹,确保后续初始化时是全新的状态
|
|||
|
|
- 避免旧数据影响新的执行
|
|||
|
|
|
|||
|
|
**为什么检查 `if (taskProgress.value[tokenId])`?**
|
|||
|
|
- 防御性编程,避免删除不存在的属性
|
|||
|
|
- 虽然理论上失败的token一定存在,但加上检查更安全
|
|||
|
|
|
|||
|
|
### 保留统计数据的逻辑
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
if (isRetry) {
|
|||
|
|
executionStats.value = {
|
|||
|
|
total: executionStats.value.total, // 保持不变
|
|||
|
|
success: executionStats.value.success, // 保持已有的成功数
|
|||
|
|
failed: 0, // 重置为0,重新统计
|
|||
|
|
skipped: executionStats.value.skipped, // 保持已有的跳过数
|
|||
|
|
startTime: Date.now(), // 更新为当前时间
|
|||
|
|
endTime: null // 清空结束时间
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**为什么重置 failed 为 0?**
|
|||
|
|
- 失败的token正在重试
|
|||
|
|
- 重试后可能成功,也可能仍然失败
|
|||
|
|
- 需要重新统计重试的结果
|
|||
|
|
|
|||
|
|
**为什么保留 success 和 skipped?**
|
|||
|
|
- 这些是之前已经确定的结果
|
|||
|
|
- 不会因为重试而改变
|
|||
|
|
|
|||
|
|
## 相关版本
|
|||
|
|
|
|||
|
|
- **v3.7.0**: 首次添加自动重试功能
|
|||
|
|
- **v3.12.6**: 增强重试日志输出
|
|||
|
|
- **v3.12.7**: 修复重试时保留成功Token进度(本版本)
|
|||
|
|
|
|||
|
|
## 总结
|
|||
|
|
|
|||
|
|
**问题**:
|
|||
|
|
- ❌ 重试时清空所有进度,丢失成功Token信息
|
|||
|
|
- ❌ UI显示不完整,只能看到重试的Token
|
|||
|
|
- ❌ 统计数据重置,无法反映真实情况
|
|||
|
|
|
|||
|
|
**修复**:
|
|||
|
|
- ✅ 重试时保留成功Token的进度
|
|||
|
|
- ✅ 只删除和重新初始化失败Token
|
|||
|
|
- ✅ 保留统计数据中的成功数和总数
|
|||
|
|
- ✅ 只重置失败计数
|
|||
|
|
|
|||
|
|
**效果**:
|
|||
|
|
- ✅ UI完整显示所有Token状态
|
|||
|
|
- ✅ 成功Token显示为"已完成"
|
|||
|
|
- ✅ 重试Token显示为"执行中"
|
|||
|
|
- ✅ 统计数据准确反映实际情况
|
|||
|
|
- ✅ 用户体验大幅提升
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**状态**: ✅ 已修复
|
|||
|
|
**版本**: v3.12.7
|
|||
|
|
|