-
+
@@ -30,7 +30,7 @@
⚠ {{ pathWarning }}
{{ scanProjectType }}
@@ -107,6 +107,9 @@
{{ $t('projects.empty') }}
+
+
+
@@ -118,7 +121,9 @@ 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 as statusLabel, projectBadgeClass as stageClass } from '../constants/project'
+import ConfirmDialog from '../components/ConfirmDialog.vue'
import type { ProjectRecord } from '../api/types'
const router = useRouter()
@@ -128,6 +133,21 @@ const { t } = useI18n()
// 状态映射统一走 ../constants/project(与 ProjectDetail/Tasks/Dashboard 一致)
// formatDate 由 ../utils/time 提供(统一毫秒字符串解析,根治 Invalid Date)
+// ── 确认弹层(替代原生 window.confirm,后者在 Tauri webview 带 localhost 来源信息无法去除) ──
+const confirmState = ref({ visible: false, msg: '', resolve: null as null | ((v: boolean) => void) })
+function confirmDialog(msg: string): Promise
{
+ 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
+}
+
// ── 新建项目 ──
const showCreateModal = ref(false)
const newName = ref('')
@@ -139,17 +159,6 @@ const scanning = ref(false)
const scanError = ref('')
const scanProjectType = ref('')
-// 解析技术栈 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 pickDir() {
try {
@@ -163,7 +172,7 @@ async function pickDir() {
// 查重(已被其他项目绑定则警告,禁止创建)
try {
const conflict = await projectApi.checkBinding(selected as string)
- if (conflict) pathWarning.value = `该目录已被项目「${conflict.name}」绑定`
+ if (conflict) pathWarning.value = t('projects.dirConflict', { name: conflict.name })
} catch { /* ignore */ }
} catch (e) {
console.error('选择目录失败:', e)
@@ -190,9 +199,9 @@ async function aiScan() {
detectedStack.value = result.stack
if (result.description) newDesc.value = result.description
scanProjectType.value = result.project_type ?? ''
- if (!result.description) scanError.value = 'AI 未能生成描述,可手动填写'
+ if (!result.description) scanError.value = t('projects.aiScanNoDesc')
} catch (e: any) {
- scanError.value = e?.toString() ?? 'AI 扫描失败'
+ scanError.value = e?.toString() ?? t('projects.aiScanFailed')
} finally {
scanning.value = false
}
@@ -214,7 +223,7 @@ async function submitCreate() {
const showTrashModal = ref(false)
async function handleDelete(project: ProjectRecord) {
- if (!confirm(t('projects.confirmDelete', { name: project.name }))) return
+ if (!await confirmDialog(t('projects.confirmDelete', { name: project.name }))) return
await store.deleteProject(project.id)
}
@@ -228,7 +237,7 @@ async function handleRestore(id: string) {
}
async function handlePurge(project: ProjectRecord) {
- if (!confirm(t('projects.confirmPurge', { name: project.name }))) return
+ if (!await confirmDialog(t('projects.confirmPurge', { name: project.name }))) return
await store.purgeProject(project.id)
}