优化: CR-08 i18n batch2全量(ProjectDetail5处+TaskDetail16key+AiChat3处+store error fallback 23处+useAiSend 3处)+@/i18n路径统一+vite别名
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button class="btn btn-ghost" @click="handleSync">{{ $t('projectDetail.sync') }}</button>
|
||||
<button class="btn btn-ghost" type="button" @click="handleImportDir">导入目录</button>
|
||||
<button class="btn btn-ghost" type="button" @click="handleImportDir">{{ $t('projectDetail.importDir') }}</button>
|
||||
<button class="btn btn-danger" @click="handleDelete">{{ $t('projectDetail.delete') }}</button>
|
||||
<button class="btn btn-primary" @click="showNewTaskModal = true">{{ $t('projectDetail.newTask') }}</button>
|
||||
</div>
|
||||
@@ -198,7 +198,7 @@
|
||||
:disabled="multiDecisions.length === 0"
|
||||
@click="handleApprovalMulti"
|
||||
>
|
||||
确认({{ multiDecisions.length }})
|
||||
{{ $t('projectDetail.approvalConfirm', { count: multiDecisions.length }) }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
@@ -217,7 +217,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button class="btn btn-ghost" @click="handleCancelApproval">取消</button>
|
||||
<button class="btn btn-ghost" @click="handleCancelApproval">{{ $t('projectDetail.approvalCancel') }}</button>
|
||||
<button class="btn btn-ghost" @click="showApprovalDialog = false">{{ $t('projectDetail.approvalLater') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -383,25 +383,24 @@ async function relocateDir() {
|
||||
}
|
||||
|
||||
// 导入历史项目(选已存在目录 → 后端创建实体+绑定+探测栈+读 README 首段一步完成)
|
||||
// 文案硬编码:projectDetail i18n 不在本次任务白名单,改用内联中文(避免引用不存在的 key)
|
||||
async function handleImportDir() {
|
||||
try {
|
||||
const selected = await open({ directory: true, multiple: false })
|
||||
if (!selected || Array.isArray(selected)) return
|
||||
const dir = selected as string
|
||||
if (!await confirmDialog(`导入目录为新项目?\n${dir}\n\n将自动用目录名作项目名,探测技术栈,并读 README 首段填描述。`)) return
|
||||
if (!await confirmDialog(t('projectDetail.importConfirm', { dir }))) return
|
||||
const record = await store.importProject({ path: dir })
|
||||
if (!record) {
|
||||
// store 已 toast 错误
|
||||
if (store.error) Message.error(store.error)
|
||||
return
|
||||
}
|
||||
Message.success(`已导入项目「${record.name}」`)
|
||||
Message.success(t('projectDetail.importSuccess', { name: record.name }))
|
||||
// 跳转到新导入项目的详情页
|
||||
router.push(`/projects/${record.id}`)
|
||||
} catch (e: any) {
|
||||
console.error('导入失败:', e)
|
||||
Message.error(`导入失败: ${e?.toString() ?? '未知错误'}`)
|
||||
Message.error(t('projectDetail.importFailed', { msg: e?.toString() ?? t('common.unknownError') }))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -375,7 +375,7 @@ import { aiApi, knowledgeApi } from '../api'
|
||||
import { useAppSettingsStore } from '../stores/appSettings'
|
||||
import { useConfirm } from '../composables/useConfirm'
|
||||
import type { AiProviderConfig } from '../api/types'
|
||||
import i18n from '../i18n'
|
||||
import i18n from '@/i18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
const appSettings = useAppSettingsStore()
|
||||
|
||||
@@ -3,33 +3,33 @@
|
||||
<!-- 页面头部 -->
|
||||
<header class="page-header">
|
||||
<div class="header-left">
|
||||
<router-link to="/tasks" class="back-link">← 返回任务列表</router-link>
|
||||
<router-link to="/tasks" class="back-link">{{ $t('taskDetail.backToList') }}</router-link>
|
||||
<h1>{{ task?.title ?? '...' }}</h1>
|
||||
<span v-if="task" class="status-tag" :class="taskStatusClass(task.status)">{{ $t(taskStatusLabel(task.status)) }}</span>
|
||||
<span v-if="task" class="priority-badge" :class="priorityClass(task.priority)">{{ priorityLabel(task.priority) }}</span>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button class="btn btn-ghost" type="button" @click="refresh">刷新</button>
|
||||
<button class="btn btn-ghost" type="button" @click="refresh">{{ $t('taskDetail.refresh') }}</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 加载/错误态 -->
|
||||
<div v-if="loading" class="empty-hint">加载中...</div>
|
||||
<div v-if="loading" class="empty-hint">{{ $t('taskDetail.loading') }}</div>
|
||||
<div v-else-if="errorMsg" class="empty-hint error-hint">⚠ {{ errorMsg }}</div>
|
||||
|
||||
<!-- 主体 -->
|
||||
<div v-else-if="task" class="detail-grid">
|
||||
<section class="panel">
|
||||
<div class="panel-header">
|
||||
<h2>任务信息</h2>
|
||||
<h2>{{ $t('taskDetail.infoTitle') }}</h2>
|
||||
</div>
|
||||
<div class="task-info">
|
||||
<div class="info-item">
|
||||
<span class="label">标题</span>
|
||||
<span class="label">{{ $t('taskDetail.title') }}</span>
|
||||
<span class="value">{{ task.title }}</span>
|
||||
</div>
|
||||
<div class="info-item info-block">
|
||||
<span class="label">描述</span>
|
||||
<span class="label">{{ $t('taskDetail.description') }}</span>
|
||||
<!-- B-24:任务描述 Markdown 渲染(## 标题 / - 列表 等),v-html 经 useMarkdown DOMPurify sanitize -->
|
||||
<span
|
||||
class="value description ai-md"
|
||||
@@ -39,19 +39,19 @@
|
||||
<span v-else class="value description">—</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">状态</span>
|
||||
<span class="label">{{ $t('taskDetail.status') }}</span>
|
||||
<span class="value">
|
||||
<span class="status-tag" :class="taskStatusClass(task.status)">{{ $t(taskStatusLabel(task.status)) }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">优先级</span>
|
||||
<span class="label">{{ $t('taskDetail.priority') }}</span>
|
||||
<span class="value">
|
||||
<span class="priority-badge" :class="priorityClass(task.priority)">{{ priorityLabel(task.priority) }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">关联项目</span>
|
||||
<span class="label">{{ $t('taskDetail.project') }}</span>
|
||||
<span class="value">
|
||||
<router-link v-if="task.project_id" :to="`/projects/${task.project_id}`" class="project-link">
|
||||
{{ projectName }}
|
||||
@@ -60,7 +60,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">分支</span>
|
||||
<span class="label">{{ $t('taskDetail.branch') }}</span>
|
||||
<span class="value">
|
||||
<span v-if="task.branch_name" class="branch-tag">
|
||||
<span class="branch-icon">⑂</span>{{ task.branch_name }}
|
||||
@@ -69,23 +69,23 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">负责人</span>
|
||||
<span class="label">{{ $t('taskDetail.assignee') }}</span>
|
||||
<span class="value">{{ task.assignee ?? '—' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">基础分支</span>
|
||||
<span class="label">{{ $t('taskDetail.baseBranch') }}</span>
|
||||
<span class="value">{{ task.base_branch ?? '—' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">工作流定义</span>
|
||||
<span class="label">{{ $t('taskDetail.workflowDef') }}</span>
|
||||
<span class="value mono">{{ task.workflow_def_id ?? '—' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">创建时间</span>
|
||||
<span class="label">{{ $t('taskDetail.createdAt') }}</span>
|
||||
<span class="value">{{ formatDate(task.created_at) }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">更新时间</span>
|
||||
<span class="label">{{ $t('taskDetail.updatedAt') }}</span>
|
||||
<span class="value">{{ formatDate(task.updated_at) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,6 +96,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { taskApi, projectApi } from '../api'
|
||||
import { formatDate } from '../utils/time'
|
||||
@@ -108,6 +109,7 @@ import {
|
||||
} from '../constants/project'
|
||||
import type { TaskRecord, ProjectRecord } from '../api/types'
|
||||
|
||||
const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
|
||||
const loading = ref(true)
|
||||
@@ -141,7 +143,7 @@ async function load() {
|
||||
projects.value = ps
|
||||
} catch (e: any) {
|
||||
task.value = null
|
||||
errorMsg.value = e?.toString() ?? '加载失败'
|
||||
errorMsg.value = e?.toString() ?? t('taskDetail.loadFailed')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user