Commit 29c7bfa7 by pangchong

feat(editor): 优化批量翻译功能及日志展示

- 将批量翻译按钮中的加载状态文本调整为“进行中…”
- 批量翻译弹窗添加多个新配置项,包括标签后缀、并行处理模式及保存到Milvus功能
- 增加翻译日志面板,实现动态日志滚动及高亮显示成功与失败信息
- 批量翻译状态信息中新增详细日志输出,提升用户反馈体验
- 使用监听器滚动日志容器,确保最新日志自动可见
- 在批量翻译逻辑中添加日志记录函数,详细跟踪翻译状态和错误
- 优化UI布局,删除冗余div标签,提升代码整洁度
parent f2d4548f
import { type TranslateXmlOptions, type TranslateXmlResponse, type TranslationStatusResponse } from '../constants'
import { type TranslateXmlResponse, type TranslationStatusResponse } from '../constants'
import { useEditorStore } from '@/store/editor'
import { serializeTreeToXml, parseXmlToTreeAsync } from '@/utils/xmlParser'
import { getBaseOrigin } from '@/utils/render'
export function useBatchTranslate() {
const editorStore = useEditorStore()
......@@ -12,6 +10,15 @@ export function useBatchTranslate() {
const progressText = ref('正在准备翻译文档...')
const resultFileId = ref('')
const resultDownloadUrl = ref('')
const logs = ref<Array<{ time: string; text: string }>>([])
const addLog = (text: string) => {
const time = new Date().toLocaleTimeString('zh-CN', { hour12: false })
if (logs.value.length > 0 && logs.value[logs.value.length - 1].text === text) {
return
}
logs.value.push({ time, text })
}
const availableTags = ref<string[]>(['PARAC', 'TITLEC'])
const availableTagOptions = computed(() => {
......@@ -58,6 +65,7 @@ export function useBatchTranslate() {
progressText.value = '正在准备翻译文档...'
resultFileId.value = ''
resultDownloadUrl.value = ''
logs.value = []
if (pollInterval) {
clearInterval(pollInterval)
pollInterval = null
......@@ -98,6 +106,7 @@ export function useBatchTranslate() {
progressText.value = '翻译完成!'
loading.value = false
completed.value = true
addLog('翻译成功完成!已生成翻译对照包。')
// 构造下载链接
let dlUrl = statusRes.download_url
if (dlUrl) {
......@@ -111,16 +120,20 @@ export function useBatchTranslate() {
clearInterval(pollInterval)
pollInterval = null
loading.value = false
window.$message.error(`翻译失败: ${statusRes.error || statusRes.message || '服务器内部错误'}`)
const errMsg = statusRes.error || statusRes.message || '服务器内部错误'
addLog(`翻译处理失败: ${errMsg}`)
window.$message.error(`翻译失败: ${errMsg}`)
} else {
// 更新进度
const currentProgress = statusRes.progress || 0
progress.value = Math.max(30, Math.min(95, Math.floor(currentProgress)))
progressText.value = statusRes.message || '翻译正在进行中...'
addLog(statusRes.message || '大语言模型正在逐句翻译中,请稍候...')
}
}
} catch (e) {
console.error('轮询状态异常:', e)
addLog('轮询翻译进度时连接异常,正在自动重试...')
}
}, 1500)
}
......@@ -134,6 +147,7 @@ export function useBatchTranslate() {
loading.value = true
progress.value = 5
progressText.value = '正在序列化当前 XML...'
addLog('初始化翻译环境,正在将当前 XML 树序列化为文本结构...')
try {
const xmlContent = serializeTreeToXml(editorStore.xmlTree, 0, true)
......@@ -141,6 +155,8 @@ export function useBatchTranslate() {
progress.value = 15
progressText.value = '正在上传文件至翻译服务器...'
addLog(`文档序列化完成,大小: ${Math.round(blob.size / 1024)} KB。准备上传...`)
addLog(`请求配置: 模型 = ${form.value.model_name}, 目标标签 = [${form.value.target_tags.join(', ')}]`)
const formData = new FormData()
formData.append('files', blob, 'document.xml')
......@@ -169,6 +185,8 @@ export function useBatchTranslate() {
resultFileId.value = res.file_id
progress.value = 30
progressText.value = '翻译任务已创建,正在排队等待处理...'
addLog(`文件上传成功。翻译服务已成功创建任务,任务 ID: ${res.file_id}`)
addLog('任务已进入服务器执行队列,排队处理中...')
// 开始轮询翻译状态
startPolling(res.file_id)
......@@ -177,7 +195,9 @@ export function useBatchTranslate() {
}
} catch (err: any) {
loading.value = false
window.$message.error(`翻译出错: ${err.message || err}`)
const errMsg = err.message || err
addLog(`翻译遇到错误终止: ${errMsg}`)
window.$message.error(`翻译出错: ${errMsg}`)
}
}
......@@ -239,6 +259,7 @@ export function useBatchTranslate() {
reset,
startTranslation,
downloadResult,
importResult
importResult,
logs
}
}
<template>
<CommonModal
v-model="showModal"
title="批量 XML 翻译"
:width="600"
:show-confirm="false"
:show-cancel="false"
>
<CommonModal v-model="showModal" title="批量 XML 翻译" :width="600" :show-confirm="false" :show-cancel="false">
<template #header-extra>
<n-tag :type="statusType" size="small">{{ statusLabel }}</n-tag>
</template>
<div class="py-2">
<n-form v-if="!loading && !completed" label-placement="left" label-width="120" size="medium">
<n-form-item label="翻译模型">
<CommonSelect v-model:value="form.model_name" :options="modelOptions" />
......@@ -44,17 +37,33 @@
</n-form-item>
</n-form>
<!-- 翻译进行中/进度条 -->
<div v-else-if="loading" class="flex flex-col items-center justify-center py-6 gap-4">
<div class="text-sm font-semibold text-color1">{{ progressText }}</div>
<div class="w-full px-8">
<n-progress
type="line"
:percentage="progress"
:indicator-placement="'inside'"
processing
status="info"
/>
<!-- 翻译进行中:极客控制台日志与动态 Loading 环 -->
<div v-else-if="loading" class="flex flex-col items-center gap-4">
<div class="flex items-center space-x-3">
<n-spin size="medium" />
<span class="text-sm font-semibold text-color1">{{ progressText }}</span>
</div>
<div class="w-full mt-2">
<div ref="logContainerRef" class="bg-fill-1 rounded-lg p-3 border border-divider shadow-inner text-[11px] font-mono leading-relaxed">
<div v-for="(log, idx) in logs" :key="idx" class="flex items-start space-x-1.5 py-0.5 select-text animate-fade-in">
<span class="text-colorText3 select-none">[{{ log.time }}]</span>
<span
class="flex-1 whitespace-pre-wrap"
:class="{
'text-success font-semibold': log.text.includes('成功') || log.text.includes('完成'),
'text-danger font-semibold': log.text.includes('失败') || log.text.includes('错误'),
'text-colorText2':
!log.text.includes('成功') &&
!log.text.includes('完成') &&
!log.text.includes('失败') &&
!log.text.includes('错误')
}"
>
{{ log.text }}
</span>
</div>
</div>
</div>
</div>
......@@ -66,15 +75,12 @@
<div class="text-base font-bold text-success">XML 文档批量翻译完成!</div>
<div class="text-xs text-color3">所有指定标签的文本已翻译成功。你可以直接导入当前编辑器或下载文件。</div>
</div>
</div>
<template #footer>
<div class="flex justify-end gap-3 w-full">
<!-- 翻译中状态:允许隐入后台 -->
<template v-if="loading">
<CommonButton type="primary" secondary @click="showModal = false">
后台运行 (隐藏)
</CommonButton>
<CommonButton type="primary" secondary @click="showModal = false">后台运行 (隐藏)</CommonButton>
</template>
<!-- 翻译完成状态 -->
<template v-else-if="completed">
......@@ -93,6 +99,7 @@
</template>
<script setup lang="ts">
import { ref, watch, nextTick } from 'vue'
import { CheckmarkCircleOutline } from '@vicons/ionicons5'
import { useBatchTranslate } from './functionals'
......@@ -113,9 +120,23 @@ const {
reset,
startTranslation,
downloadResult,
importResult
importResult,
logs
} = useBatchTranslate()
const logContainerRef = ref<HTMLElement | null>(null)
watch(
() => logs.value.length,
() => {
nextTick(() => {
if (logContainerRef.value) {
logContainerRef.value.scrollTop = logContainerRef.value.scrollHeight
}
})
}
)
defineExpose({
open,
loading,
......@@ -124,3 +145,19 @@ defineExpose({
completed
})
</script>
<style scoped>
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fadeIn 0.25s ease-out forwards;
}
</style>
......@@ -59,7 +59,7 @@
<language-outline v-else />
</n-icon>
</template>
<span v-if="batchTranslateModalRef?.loading">批量翻译 ({{ batchTranslateModalRef.progress }}%)</span>
<span v-if="batchTranslateModalRef?.loading">批量翻译 (进行中...)</span>
<span v-else-if="batchTranslateModalRef?.completed">批量翻译 (已完成)</span>
<span v-else>批量翻译</span>
</CommonButton>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment