update
This commit is contained in:
@@ -242,7 +242,7 @@ export const downloadDirectory = async (req: Request, res: Response): Promise<vo
|
|||||||
console.error(`Error reading file stream ${currentRemotePath}:`, streamErr);
|
console.error(`Error reading file stream ${currentRemotePath}:`, streamErr);
|
||||||
// 如何通知 Archiver 或中断? Archiver 的 error 事件应该会捕获?
|
// 如何通知 Archiver 或中断? Archiver 的 error 事件应该会捕获?
|
||||||
if (!archive.destroyed) { // 检查 archive 是否已被销毁
|
if (!archive.destroyed) { // 检查 archive 是否已被销毁
|
||||||
archive.abort(); // 尝试中止 archive
|
archive.abort(); // 尝试终止 archive
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export class TransfersService {
|
|||||||
// 异步启动传输,不阻塞当前请求
|
// 异步启动传输,不阻塞当前请求
|
||||||
this.processTransferTask(taskId, abortController.signal).catch(error => { // +++ 传递 signal +++
|
this.processTransferTask(taskId, abortController.signal).catch(error => { // +++ 传递 signal +++
|
||||||
console.error(`[TransfersService] Error processing task ${taskId} in background:`, error);
|
console.error(`[TransfersService] Error processing task ${taskId} in background:`, error);
|
||||||
// 如果不是因为中止操作导致的错误,则更新状态
|
// 如果不是因为终止操作导致的错误,则更新状态
|
||||||
if (error.name !== 'AbortError') {
|
if (error.name !== 'AbortError') {
|
||||||
this.updateOverallTaskStatus(taskId, 'failed', `Background processing error: ${error.message}`);
|
this.updateOverallTaskStatus(taskId, 'failed', `Background processing error: ${error.message}`);
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ export class TransfersService {
|
|||||||
const abortController = this.taskAbortControllers.get(taskId);
|
const abortController = this.taskAbortControllers.get(taskId);
|
||||||
if (abortController) {
|
if (abortController) {
|
||||||
console.info(`[TransfersService] Cancelling task ${taskId}.`);
|
console.info(`[TransfersService] Cancelling task ${taskId}.`);
|
||||||
abortController.abort(); // 触发中止信号
|
abortController.abort(); // 触发终止信号
|
||||||
|
|
||||||
// 更新主任务状态
|
// 更新主任务状态
|
||||||
// 假设 'cancelling' 和 'cancelled' 是有效的状态
|
// 假设 'cancelling' 和 'cancelled' 是有效的状态
|
||||||
|
|||||||
@@ -94,7 +94,28 @@ const fetchTransferTasks = async () => {
|
|||||||
// 假设后端API路径为 /api/v1/transfers/status,且返回数据结构为 { data: TransferTask[] }
|
// 假设后端API路径为 /api/v1/transfers/status,且返回数据结构为 { data: TransferTask[] }
|
||||||
// 请根据实际API调整这里的类型和数据访问
|
// 请根据实际API调整这里的类型和数据访问
|
||||||
const response = await apiClient.get<{ data: TransferTask[] }>('/transfers/status');
|
const response = await apiClient.get<{ data: TransferTask[] }>('/transfers/status');
|
||||||
transferTasks.value = Array.isArray(response.data.data) ? response.data.data : (Array.isArray(response.data) ? response.data : []);
|
const rawTasks = Array.isArray(response.data.data) ? response.data.data : (Array.isArray(response.data) ? response.data : []);
|
||||||
|
transferTasks.value = rawTasks.map(task => {
|
||||||
|
// 优先信任后端已经是 'cancelled' 或其他最终状态
|
||||||
|
if (['completed', 'failed', 'cancelled', 'partially-completed'].includes(task.status)) {
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
// 对于仍在进行中或正在取消中的任务
|
||||||
|
if (['in-progress', 'cancelling', 'queued', 'connecting', 'transferring'].includes(task.status)) {
|
||||||
|
// 如果它有子任务,并且所有子任务都已是 'cancelled'
|
||||||
|
if (task.subTasks && task.subTasks.length > 0 && task.subTasks.every((st: TransferSubTask) => st.status === 'cancelled')) {
|
||||||
|
// 则认为主任务也应该被标记为 'cancelled'
|
||||||
|
// 这有助于处理后端主任务状态更新延迟或遗漏的情况
|
||||||
|
return { ...task, status: 'cancelled' as TransferTask['status'] };
|
||||||
|
}
|
||||||
|
// 如果任务状态是 'cancelling' 但它没有子任务 (或子任务列表为空)
|
||||||
|
// 这种情况也应视为已取消
|
||||||
|
else if (task.status === 'cancelling' && (!task.subTasks || task.subTasks.length === 0)) {
|
||||||
|
return { ...task, status: 'cancelled' as TransferTask['status'] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return task;
|
||||||
|
});
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("Failed to fetch transfer tasks:", error);
|
console.error("Failed to fetch transfer tasks:", error);
|
||||||
errorLoading.value = error.response?.data?.message || error.message || t('transferProgressModal.error.unknown', '未知错误');
|
errorLoading.value = error.response?.data?.message || error.message || t('transferProgressModal.error.unknown', '未知错误');
|
||||||
@@ -193,7 +214,7 @@ const isTaskCancelling = (taskStatus: TransferTask['status']): boolean => {
|
|||||||
|
|
||||||
const handleCancelTask = async (taskId: string) => {
|
const handleCancelTask = async (taskId: string) => {
|
||||||
// 可以在这里添加一个确认对话框
|
// 可以在这里添加一个确认对话框
|
||||||
// const confirmed = window.confirm(t('transferProgressModal.confirmCancel', '您确定要中止此传输任务吗?'));
|
// const confirmed = window.confirm(t('transferProgressModal.confirmCancel', '您确定要终止此传输任务吗?'));
|
||||||
// if (!confirmed) return;
|
// if (!confirmed) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -207,13 +228,20 @@ const handleCancelTask = async (taskId: string) => {
|
|||||||
|
|
||||||
await apiClient.post(`/transfers/cancel/${taskId}`);
|
await apiClient.post(`/transfers/cancel/${taskId}`);
|
||||||
// 可以添加成功提示
|
// 可以添加成功提示
|
||||||
// uiNotificationsStore.showSuccess(t('transferProgressModal.cancelRequested', '已发送中止请求。'));
|
// uiNotificationsStore.showSuccess(t('transferProgressModal.cancelRequested', '已发送终止请求。'));
|
||||||
|
|
||||||
|
// 前端优化:立即将任务状态设置为 'cancelling' 以提供即时反馈
|
||||||
|
// 这样用户点击后能马上看到状态变为“终止中”,后续轮询会从后端获取权威状态。
|
||||||
|
const taskBeingCancelled = transferTasks.value.find(t => t.taskId === taskId);
|
||||||
|
if (taskBeingCancelled && ['queued', 'in-progress', 'connecting', 'transferring'].includes(taskBeingCancelled.status)) {
|
||||||
|
taskBeingCancelled.status = 'cancelling';
|
||||||
|
}
|
||||||
|
|
||||||
// 立即刷新一次列表,或者等待下一次轮询
|
// 立即刷新一次列表,或者等待下一次轮询
|
||||||
fetchTransferTasks();
|
fetchTransferTasks();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(`Failed to cancel task ${taskId}:`, error);
|
console.error(`Failed to cancel task ${taskId}:`, error);
|
||||||
// uiNotificationsStore.showError(error.response?.data?.message || error.message || t('transferProgressModal.error.cancelFailed', '中止任务失败。'));
|
// uiNotificationsStore.showError(error.response?.data?.message || error.message || t('transferProgressModal.error.cancelFailed', '终止任务失败。'));
|
||||||
// 如果任务状态之前被临时修改,可能需要回滚
|
// 如果任务状态之前被临时修改,可能需要回滚
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -273,10 +301,10 @@ const handleCancelTask = async (taskId: string) => {
|
|||||||
@click="handleCancelTask(task.taskId)"
|
@click="handleCancelTask(task.taskId)"
|
||||||
:disabled="isTaskCancelling(task.status)"
|
:disabled="isTaskCancelling(task.status)"
|
||||||
class="px-2 py-0.5 text-xs bg-red-500 hover:bg-red-600 text-white rounded-md focus:outline-none focus:ring-2 focus:ring-red-400 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
class="px-2 py-0.5 text-xs bg-red-500 hover:bg-red-600 text-white rounded-md focus:outline-none focus:ring-2 focus:ring-red-400 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||||
:title="isTaskCancelling(task.status) ? t('transferProgressModal.cancellingTooltip', '中止中...') : t('transferProgressModal.cancelTaskTooltip', '中止任务')"
|
:title="isTaskCancelling(task.status) ? t('transferProgressModal.cancellingTooltip', '终止中...') : t('transferProgressModal.cancelTaskTooltip', '终止任务')"
|
||||||
>
|
>
|
||||||
<i v-if="isTaskCancelling(task.status)" class="fas fa-spinner fa-spin mr-1"></i>
|
<i v-if="isTaskCancelling(task.status)" class="fas fa-spinner fa-spin mr-1"></i>
|
||||||
{{ isTaskCancelling(task.status) ? t('transferProgressModal.cancellingButton', '中止中') : t('transferProgressModal.cancelButton', '中止') }}
|
{{ isTaskCancelling(task.status) ? t('transferProgressModal.cancellingButton', '终止中') : t('transferProgressModal.cancelButton', '终止') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1393,7 +1393,7 @@
|
|||||||
"errorLoading": "転送タスクの読み込みに失敗しました:{error}",
|
"errorLoading": "転送タスクの読み込みに失敗しました:{error}",
|
||||||
"error": {
|
"error": {
|
||||||
"unknown": "不明なエラー",
|
"unknown": "不明なエラー",
|
||||||
"cancelFailed": "タスクの中止に失敗しました。"
|
"cancelFailed": "タスクの终止に失敗しました。"
|
||||||
},
|
},
|
||||||
"noTasks": "現在アクティブな転送タスクはありません。",
|
"noTasks": "現在アクティブな転送タスクはありません。",
|
||||||
"task": {
|
"task": {
|
||||||
@@ -1409,15 +1409,15 @@
|
|||||||
"partiallyCompleted": "一部完了",
|
"partiallyCompleted": "一部完了",
|
||||||
"connecting": "接続中",
|
"connecting": "接続中",
|
||||||
"transferring": "転送中",
|
"transferring": "転送中",
|
||||||
"cancelling": "中止中",
|
"cancelling": "终止中",
|
||||||
"cancelled": "中止済み"
|
"cancelled": "终止済み"
|
||||||
},
|
},
|
||||||
"confirmCancel": "この転送タスクを中止してもよろしいですか?",
|
"confirmCancel": "この転送タスクを终止してもよろしいですか?",
|
||||||
"cancelRequested": "中止リクエストを送信しました。",
|
"cancelRequested": "终止リクエストを送信しました。",
|
||||||
"cancelTaskTooltip": "タスクを中止",
|
"cancelTaskTooltip": "タスクを终止",
|
||||||
"cancellingTooltip": "中止中...",
|
"cancellingTooltip": "终止中...",
|
||||||
"cancelButton": "中止",
|
"cancelButton": "终止",
|
||||||
"cancellingButton": "中止中",
|
"cancellingButton": "终止中",
|
||||||
"subTasks": {
|
"subTasks": {
|
||||||
"titleToggle": "{count} 個のサブタスクを表示",
|
"titleToggle": "{count} 個のサブタスクを表示",
|
||||||
"noSubTasks": "サブタスクはありません。"
|
"noSubTasks": "サブタスクはありません。"
|
||||||
|
|||||||
@@ -1405,7 +1405,7 @@
|
|||||||
"errorLoading": "加载传输任务失败:{error}",
|
"errorLoading": "加载传输任务失败:{error}",
|
||||||
"error": {
|
"error": {
|
||||||
"unknown": "未知错误",
|
"unknown": "未知错误",
|
||||||
"cancelFailed": "中止任务失败。"
|
"cancelFailed": "终止任务失败。"
|
||||||
},
|
},
|
||||||
"noTasks": "当前没有活动的传输任务。",
|
"noTasks": "当前没有活动的传输任务。",
|
||||||
"task": {
|
"task": {
|
||||||
@@ -1421,15 +1421,15 @@
|
|||||||
"partiallyCompleted": "部分完成",
|
"partiallyCompleted": "部分完成",
|
||||||
"connecting": "连接中",
|
"connecting": "连接中",
|
||||||
"transferring": "传输中",
|
"transferring": "传输中",
|
||||||
"cancelling": "中止中",
|
"cancelling": "正在终止...",
|
||||||
"cancelled": "已中止"
|
"cancelled": "已终止"
|
||||||
},
|
},
|
||||||
"confirmCancel": "您确定要中止此传输任务吗?",
|
"confirmCancel": "您确定要终止此传输任务吗?",
|
||||||
"cancelRequested": "已发送中止请求。",
|
"cancelRequested": "已发送终止请求。",
|
||||||
"cancelTaskTooltip": "中止任务",
|
"cancelTaskTooltip": "终止任务",
|
||||||
"cancellingTooltip": "中止中...",
|
"cancellingTooltip": "正在终止......",
|
||||||
"cancelButton": "中止",
|
"cancelButton": "终止",
|
||||||
"cancellingButton": "中止中",
|
"cancellingButton": "正在终止...",
|
||||||
"subTasks": {
|
"subTasks": {
|
||||||
"titleToggle": "查看 {count} 个子任务",
|
"titleToggle": "查看 {count} 个子任务",
|
||||||
"noSubTasks": "没有子任务。"
|
"noSubTasks": "没有子任务。"
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ export const openNewSession = (
|
|||||||
|
|
||||||
if (sessionToUpdate) {
|
if (sessionToUpdate) {
|
||||||
if (sessionToUpdate.connectionId !== backendCID) {
|
if (sessionToUpdate.connectionId !== backendCID) {
|
||||||
console.warn(`[SessionActions/ssh:connected] 后端CID ${backendCID} 与会话 ${originalFrontendSessionIdForHandler} 的期望CID ${sessionToUpdate.connectionId} 不匹配。中止SID更新。`);
|
console.warn(`[SessionActions/ssh:connected] 后端CID ${backendCID} 与会话 ${originalFrontendSessionIdForHandler} 的期望CID ${sessionToUpdate.connectionId} 不匹配。终止SID更新。`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user