优化: 前端扫描文案 i18n + parseStack 抽公共 + alert/confirm 换组件

- 扫描/目录状态文案走 $t(Projects/ProjectDetail,zh-CN+en 同步)
- parseStack 抽公共到 src/utils/project.ts(删两份本地实现)
- 8 处原生 alert/confirm 全换(confirm→ConfirmDialog / alert→Arco Message),控制流语义不变

Sprint 20 审查 ⑥⑦⑧。vue-tsc 0 error,验证 safe 0 issue
This commit is contained in:
2026-06-14 14:22:57 +08:00
parent d5a641797c
commit 02ff88fc61
8 changed files with 126 additions and 36 deletions

View File

@@ -90,7 +90,7 @@
<div class="info-item" v-if="currentProject.path">
<span class="label">{{ $t('projectDetail.dirStatusLabel') }}</span>
<span :class="pathExists === false ? 'path-missing' : 'path-ok'">
{{ pathExists === null ? '检测中…' : pathExists ? '✓ 目录存在' : '⚠ 目录已移走,建议重定位' }}
{{ pathExists === null ? $t('projectDetail.dirChecking') : pathExists ? $t('projectDetail.dirExists') : $t('projectDetail.dirMissing') }}
</span>
</div>
@@ -193,6 +193,9 @@
</div>
</div>
</div>
<!-- 确认弹层删除/重定位确认替代原生 window.confirm/alert -->
<ConfirmDialog :visible="confirmState.visible" :msg="confirmState.msg" @result="answerConfirm" />
</div>
</template>
@@ -200,11 +203,14 @@
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { Message } from '@arco-design/web-vue'
import { open } from '@tauri-apps/plugin-dialog'
import { useProjectStore } from '../stores/project'
import { projectApi } from '../api'
import { formatDate } from '../utils/time'
import { parseStack } from '../utils/project'
import { projectStatusLabel, projectStageInfo, taskStatusLabel, taskStatusClass } from '../constants/project'
import ConfirmDialog from '../components/ConfirmDialog.vue'
import type { WorkflowEventPayload } from '../api/types'
const route = useRoute()
@@ -215,6 +221,21 @@ const logListRef = ref<HTMLElement | null>(null)
const showApprovalDialog = ref(false)
let unlisten: (() => void) | null = null
// ── 确认弹层(替代原生 window.confirm后者在 Tauri webview 带 localhost 来源信息无法去除) ──
const confirmState = ref({ visible: false, msg: '', resolve: null as null | ((v: boolean) => void) })
function confirmDialog(msg: string): Promise<boolean> {
return new Promise(resolve => {
confirmState.value.msg = msg
confirmState.value.visible = true
confirmState.value.resolve = resolve
})
}
function answerConfirm(ok: boolean) {
confirmState.value.visible = false
confirmState.value.resolve?.(ok)
confirmState.value.resolve = null
}
// ── 阶段定义(labelKey 对应 i18n projectDetail.stage*) ──
const stages = [
{ key: 'idea', labelKey: 'stageIdea' },
@@ -317,17 +338,6 @@ async function handleSync() {
// ── 代码目录绑定 ──
const pathExists = ref<boolean | null>(null)
// 解析技术栈 JSON 数组字符串(防崩)
function parseStack(stack: string | null): string[] {
if (!stack) return []
try {
const arr = JSON.parse(stack)
return Array.isArray(arr) ? arr : []
} catch {
return []
}
}
// 检查绑定目录是否存在(详情页「目录是否还在」)
async function checkPath() {
const p = currentProject.value
@@ -351,16 +361,19 @@ async function relocateDir() {
try {
const conflict = await projectApi.checkBinding(dir, p.id)
if (conflict) {
alert(`该目录已被项目「${conflict.name}」绑定`)
Message.warning(t('projectDetail.dirConflict', { name: conflict.name }))
return
}
} catch { /* ignore */ }
if (!confirm(`将项目目录${currentProject.value?.path ? '重定位' : '绑定'}到:\n${dir}\n\n技术栈将重新探测。确认?`)) return
const confirmMsg = p.path
? t('projectDetail.relocateConfirmRelocate', { dir })
: t('projectDetail.relocateConfirmBind', { dir })
if (!await confirmDialog(confirmMsg)) return
await store.relocateProjectPath(p.id, dir)
await checkPath()
} catch (e: any) {
console.error('重定位失败:', e)
alert('重定位失败: ' + (e?.toString() ?? '未知错误'))
Message.error(t('projectDetail.relocateFailed', { msg: e?.toString() ?? '未知错误' }))
}
}
@@ -374,7 +387,7 @@ async function handleApproval(decision: string) {
async function handleDelete() {
const p = currentProject.value
if (!p) return
if (!confirm(t('projectDetail.confirmDelete', { name: p.name }))) return
if (!await confirmDialog(t('projectDetail.confirmDelete', { name: p.name }))) return
await store.deleteProject(p.id)
router.push('/projects')
}