Prechádzať zdrojové kódy

feat(ui): Chinese localization + dashboard chart alignment fix

Localization:
- MainLayout: sidebar menu (Projects/Experience/Dashboard), logo, version, breadcrumb, backend status
- Router: all route titles in Chinese
- ProjectList: page title, stats cards, table columns, create dialog (boundary conditions), all messages
- ProjectDetail: back button, project info, boundary conditions, plans table, create plan dialog (scan variable editor), all messages
- PlanDetail: back button, download/upload, stats cards, scan variables table, results table columns, all messages
- ExperienceList: page title, stats cards, filters, table columns, detail/edit/import/similar dialogs, all messages
- Dashboard: page title, project/plan selectors, stats cards, chart titles, results table, empty state, all messages, chart tooltips

Chart alignment fix (Dashboard):
- el-col display:flex for equal height columns
- card: flex column layout, min-height:400px, width:100%
- chart-container: flex:1, min-height:320px
- canvas width:100% !important for responsive rendering
- Ensures side-by-side chart cards have consistent height alignment

Build: vite build success (13.06s), vue-tsc type check passed
carlin 1 týždeň pred
rodič
commit
2660df74a9

+ 7 - 7
web/frontend/src/layouts/MainLayout.vue

@@ -3,7 +3,7 @@
     <el-aside width="220px" class="sidebar">
       <div class="logo">
         <span class="logo-icon">AFM</span>
-        <span class="logo-text">Simulation System</span>
+        <span class="logo-text">轴向磁通电机仿真系统</span>
       </div>
       <el-menu
         :default-active="activeMenu"
@@ -14,31 +14,31 @@
       >
         <el-menu-item index="/projects">
           <el-icon><Folder /></el-icon>
-          <span>Projects</span>
+          <span>项目管理</span>
         </el-menu-item>
         <el-menu-item index="/experience">
           <el-icon><Collection /></el-icon>
-          <span>Experience</span>
+          <span>经验库</span>
         </el-menu-item>
         <el-menu-item index="/dashboard">
           <el-icon><DataAnalysis /></el-icon>
-          <span>Dashboard</span>
+          <span>结果分析</span>
         </el-menu-item>
       </el-menu>
       <div class="sidebar-footer">
-        <span class="version">v0.1.0 (Phase 2)</span>
+        <span class="version">v1.0.0</span>
       </div>
     </el-aside>
     <el-container>
       <el-header class="header">
         <div class="header-left">
           <el-breadcrumb separator="/">
-            <el-breadcrumb-item :to="{ path: '/projects' }">Home</el-breadcrumb-item>
+            <el-breadcrumb-item :to="{ path: '/projects' }">首页</el-breadcrumb-item>
             <el-breadcrumb-item v-if="currentTitle">{{ currentTitle }}</el-breadcrumb-item>
           </el-breadcrumb>
         </div>
         <div class="header-right">
-          <el-tag type="success" size="small">Backend Connected</el-tag>
+          <el-tag type="success" size="small">后端已连接</el-tag>
         </div>
       </el-header>
       <el-main class="main-content">

+ 5 - 5
web/frontend/src/router/index.ts

@@ -12,31 +12,31 @@ const router = createRouter({
           path: 'projects',
           name: 'ProjectList',
           component: () => import('@/views/ProjectList.vue'),
-          meta: { title: 'Projects' }
+          meta: { title: '项目管理' }
         },
         {
           path: 'projects/:id',
           name: 'ProjectDetail',
           component: () => import('@/views/ProjectDetail.vue'),
-          meta: { title: 'Project Detail' }
+          meta: { title: '项目详情' }
         },
         {
           path: 'plans/:id',
           name: 'PlanDetail',
           component: () => import('@/views/PlanDetail.vue'),
-          meta: { title: 'Plan Detail' }
+          meta: { title: '方案详情' }
         },
         {
           path: 'experience',
           name: 'Experience',
           component: () => import('@/views/ExperienceList.vue'),
-          meta: { title: 'Experience Library' }
+          meta: { title: '经验库' }
         },
         {
           path: 'dashboard',
           name: 'Dashboard',
           component: () => import('@/views/Dashboard.vue'),
-          meta: { title: 'Results Dashboard' }
+          meta: { title: '结果分析' }
         }
       ]
     }

+ 40 - 29
web/frontend/src/views/Dashboard.vue

@@ -1,13 +1,13 @@
 <template>
   <div class="page-container" v-loading="loading">
     <div class="page-header">
-      <span class="page-title">Results Dashboard</span>
+      <span class="page-title">结果分析仪表盘</span>
       <div class="header-controls">
-        <el-select v-model="selectedProject" placeholder="Select Project" style="width: 220px" @change="onProjectChange" clearable>
+        <el-select v-model="selectedProject" placeholder="选择项目" style="width: 220px" @change="onProjectChange" clearable>
           <el-option v-for="p in projects" :key="p.id" :label="p.name" :value="p.id" />
         </el-select>
-        <el-select v-model="selectedPlan" placeholder="Select Plan" style="width: 220px" @change="loadAllCharts" :disabled="!selectedProject" clearable>
-          <el-option v-for="p in planList" :key="p.id" :label="`${p.name} (${p.result_count || 0} results)`" :value="p.id" />
+        <el-select v-model="selectedPlan" placeholder="选择方案" style="width: 220px" @change="loadAllCharts" :disabled="!selectedProject" clearable>
+          <el-option v-for="p in planList" :key="p.id" :label="`${p.name} (${p.result_count || 0} 结果)`" :value="p.id" />
         </el-select>
       </div>
     </div>
@@ -16,25 +16,25 @@
     <el-row :gutter="16" class="stats-row" v-if="overview">
       <el-col :span="6">
         <div class="stat-card">
-          <div class="stat-label">Total Plans</div>
+          <div class="stat-label">方案总数</div>
           <div class="stat-value">{{ overview.total_plans }}</div>
         </div>
       </el-col>
       <el-col :span="6">
         <div class="stat-card">
-          <div class="stat-label">Results (OK/Failed)</div>
+          <div class="stat-label">结果(成功/失败)</div>
           <div class="stat-value">{{ overview.ok_results }}<span class="stat-sub">/{{ overview.total_results }}</span></div>
         </div>
       </el-col>
       <el-col :span="6">
         <div class="stat-card stat-success">
-          <div class="stat-label">Best Efficiency</div>
+          <div class="stat-label">最高效率</div>
           <div class="stat-value">{{ overview.best_efficiency_pct }}<span class="stat-sub">%</span></div>
         </div>
       </el-col>
       <el-col :span="6">
         <div class="stat-card stat-primary">
-          <div class="stat-label">Best Torque</div>
+          <div class="stat-label">最大转矩</div>
           <div class="stat-value">{{ overview.best_torque_nm }}<span class="stat-sub">Nm</span></div>
         </div>
       </el-col>
@@ -45,7 +45,7 @@
       <el-col :span="12">
         <div class="card">
           <div class="card-header">
-            <h3>Parameter vs Metric Trend</h3>
+            <h3>参数-指标趋势图</h3>
             <div class="chart-selectors">
               <el-select v-model="trendParam" size="small" style="width: 130px" @change="loadTrendChart">
                 <el-option v-for="p in availableParams" :key="p" :label="p" :value="p" />
@@ -61,7 +61,7 @@
       <el-col :span="12">
         <div class="card">
           <div class="card-header">
-            <h3>Pareto Frontier</h3>
+            <h3>Pareto前沿</h3>
             <div class="chart-selectors">
               <el-select v-model="paretoX" size="small" style="width: 140px" @change="loadParetoChart">
                 <el-option v-for="m in metricOptions" :key="m.key" :label="m.label" :value="m.key" />
@@ -81,7 +81,7 @@
       <el-col :span="12">
         <div class="card">
           <div class="card-header">
-            <h3>Parameter Sensitivity</h3>
+            <h3>参数敏感性分析</h3>
             <el-select v-model="sensitivityMetric" size="small" style="width: 180px" @change="loadSensitivityChart">
               <el-option v-for="m in metricOptions" :key="m.key" :label="`${m.label} (${m.unit})`" :value="m.key" />
             </el-select>
@@ -92,24 +92,24 @@
       <el-col :span="12">
         <div class="card">
           <div class="card-header">
-            <h3>Results Table</h3>
-            <el-tag size="small" type="info">{{ filteredResults.length }} points</el-tag>
+            <h3>结果数据表</h3>
+            <el-tag size="small" type="info">{{ filteredResults.length }} 个数据点</el-tag>
           </div>
           <el-table :data="filteredResults" size="small" stripe max-height="320">
             <el-table-column prop="run_index" label="#" width="50" fixed />
             <el-table-column v-for="p in availableParams" :key="p" :label="p" :prop="`params.${p}`" width="90">
               <template #default="{ row }">{{ row.params?.[p] }}</template>
             </el-table-column>
-            <el-table-column prop="metrics.tavg_nm" label="Tavg" width="80">
+            <el-table-column prop="metrics.tavg_nm" label="平均转矩" width="90">
               <template #default="{ row }">{{ row.metrics?.tavg_nm?.toFixed(3) }}</template>
             </el-table-column>
-            <el-table-column prop="metrics.efficiency_pct" label="Eff%" width="70">
+            <el-table-column prop="metrics.efficiency_pct" label="效率%" width="75">
               <template #default="{ row }">{{ row.metrics?.efficiency_pct?.toFixed(1) }}</template>
             </el-table-column>
-            <el-table-column prop="metrics.ripple_pct" label="Rip%" width="70">
+            <el-table-column prop="metrics.ripple_pct" label="脉动%" width="75">
               <template #default="{ row }">{{ row.metrics?.ripple_pct?.toFixed(2) }}</template>
             </el-table-column>
-            <el-table-column prop="status" label="Status" width="70">
+            <el-table-column prop="status" label="状态" width="70">
               <template #default="{ row }">
                 <el-tag size="small" :type="row.status === 'OK' ? 'success' : 'danger'">{{ row.status }}</el-tag>
               </template>
@@ -119,7 +119,7 @@
       </el-col>
     </el-row>
 
-    <el-empty v-if="!selectedProject && !loading" description="Select a project to view analytics dashboard" :image-size="80" />
+    <el-empty v-if="!selectedProject && !loading" description="请选择项目以查看分析仪表盘" :image-size="80" />
   </div>
 </template>
 
@@ -170,7 +170,7 @@ async function loadProjects() {
     const res = await projectApi.list({ limit: 100 })
     projects.value = res.data.items
   } catch (e: any) {
-    ElMessage.error('Failed to load projects: ' + e.message)
+    ElMessage.error('加载项目失败:' + e.message)
   }
 }
 
@@ -198,7 +198,7 @@ async function onProjectChange(projectId: number | null) {
     overview.value = overviewRes.data
     planList.value = plansRes.data.items
   } catch (e: any) {
-    ElMessage.error('Failed to load project overview: ' + e.message)
+    ElMessage.error('加载项目概览失败:' + e.message)
   } finally {
     loading.value = false
   }
@@ -214,7 +214,7 @@ async function loadPlanResults() {
       trendParam.value = availableParams.value[0]
     }
   } catch (e: any) {
-    ElMessage.error('Failed to load plan results: ' + e.message)
+    ElMessage.error('加载方案结果失败:' + e.message)
   }
 }
 
@@ -263,7 +263,7 @@ async function loadTrendChart() {
         itemStyle: { color: '#3b82f6' },
         markLine: {
           silent: true,
-          data: [{ type: 'average', name: 'Avg' }],
+          data: [{ type: 'average', name: '平均值' }],
           lineStyle: { color: '#94a3b8', type: 'dashed' }
         }
       }]
@@ -282,13 +282,13 @@ async function loadParetoChart() {
     const paretoPts = data.pareto_points.map((p: any) => [p.x, p.y])
     paretoChart.setOption({
       tooltip: { trigger: 'item', formatter: (p: any) => `${data.x_label}: ${p.value[0]} ${data.x_unit}<br/>${data.y_label}: ${p.value[1]} ${data.y_unit}` },
-      legend: { data: ['All Points', 'Pareto Frontier'], top: 0 },
+      legend: { data: ['全部数据点', 'Pareto前沿'], top: 0 },
       grid: { left: 60, right: 20, top: 40, bottom: 40 },
       xAxis: { type: 'value', name: data.x_label, nameLocation: 'middle', nameGap: 25 },
       yAxis: { type: 'value', name: data.y_label, nameLocation: 'middle', nameGap: 50 },
       series: [
-        { name: 'All Points', type: 'scatter', data: allPts, symbolSize: 8, itemStyle: { color: '#cbd5e1' } },
-        { name: 'Pareto Frontier', type: 'line', data: paretoPts, symbolSize: 12, lineStyle: { color: '#ef4444', width: 2 }, itemStyle: { color: '#ef4444' } }
+        { name: '全部数据点', type: 'scatter', data: allPts, symbolSize: 8, itemStyle: { color: '#cbd5e1' } },
+        { name: 'Pareto前沿', type: 'line', data: paretoPts, symbolSize: 12, lineStyle: { color: '#ef4444', width: 2 }, itemStyle: { color: '#ef4444' } }
       ]
     })
   } catch (e: any) {
@@ -306,10 +306,10 @@ async function loadSensitivityChart() {
       tooltip: { trigger: 'axis', formatter: (params: any) => {
         const p = params[0]
         const item = items[p.dataIndex]
-        return `${item.param}<br/>Correlation: ${item.correlation}<br/>Direction: ${item.direction}`
+        return `${item.param}<br/>相关系数: ${item.correlation}<br/>方向: ${item.direction}`
       }},
       grid: { left: 100, right: 30, top: 30, bottom: 30 },
-      xAxis: { type: 'value', name: '|Correlation|', max: 1, nameLocation: 'middle', nameGap: 25 },
+      xAxis: { type: 'value', name: '|相关系数|', max: 1, nameLocation: 'middle', nameGap: 25 },
       yAxis: { type: 'category', data: items.map((i: any) => i.param), inverse: true },
       series: [{
         type: 'bar',
@@ -395,18 +395,25 @@ onUnmounted(() => {
 .chart-controls {
   margin-bottom: 16px;
 }
+.chart-controls :deep(.el-col) {
+  display: flex;
+}
 .card {
   background: #fff;
   border-radius: 8px;
   padding: 16px;
   border: 1px solid #e2e8f0;
-  height: 100%;
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+  min-height: 400px;
 }
 .card-header {
   display: flex;
   justify-content: space-between;
   align-items: center;
   margin-bottom: 12px;
+  flex-shrink: 0;
 }
 .card-header h3 {
   margin: 0;
@@ -421,6 +428,10 @@ onUnmounted(() => {
 }
 .chart-container {
   width: 100%;
-  height: 320px;
+  flex: 1;
+  min-height: 320px;
+}
+.chart-container :deep(canvas) {
+  width: 100% !important;
 }
 </style>

+ 75 - 75
web/frontend/src/views/ExperienceList.vue

@@ -1,16 +1,16 @@
 <template>
   <div class="page-container">
     <div class="page-header">
-      <span class="page-title">Experience Library</span>
+      <span class="page-title">经验库</span>
       <div class="header-controls">
         <el-button type="success" size="small" @click="showImportDialog = true">
-          <el-icon><Upload /></el-icon> Import from Plan
+          <el-icon><Upload /></el-icon> 从方案导入
         </el-button>
         <el-button type="primary" size="small" @click="showSimilarDialog = true">
-          <el-icon><Search /></el-icon> Find Similar
+          <el-icon><Search /></el-icon> 相似检索
         </el-button>
         <el-button size="small" @click="loadAll">
-          <el-icon><Refresh /></el-icon> Refresh
+          <el-icon><Refresh /></el-icon> 刷新
         </el-button>
       </div>
     </div>
@@ -19,7 +19,7 @@
     <el-row :gutter="16" class="stats-row" v-if="stats">
       <el-col :span="6">
         <div class="stat-card">
-          <div class="stat-label">Total Cases</div>
+          <div class="stat-label">案例总数</div>
           <div class="stat-value">{{ stats.total }}</div>
         </div>
       </el-col>
@@ -31,13 +31,13 @@
       </el-col>
       <el-col :span="6">
         <div class="stat-card stat-success">
-          <div class="stat-label">Avg Rating</div>
+          <div class="stat-label">平均评分</div>
           <div class="stat-value">{{ stats.avg_rating || 0 }}<span class="stat-sub">/5</span></div>
         </div>
       </el-col>
       <el-col :span="6">
         <div class="stat-card">
-          <div class="stat-label">Tracked Params</div>
+          <div class="stat-label">跟踪参数</div>
           <div class="stat-value">{{ Object.keys(stats.param_coverage || {}).length }}</div>
         </div>
       </el-col>
@@ -45,20 +45,20 @@
 
     <!-- Filters -->
     <div class="card filter-bar">
-      <el-input v-model="searchKeyword" placeholder="Search conclusion or tags..." style="width: 280px" clearable @input="loadExperience" :prefix-icon="Search" />
-      <el-select v-model="topologyFilter" placeholder="Topology" style="width: 130px" clearable @change="loadExperience">
+      <el-input v-model="searchKeyword" placeholder="搜索结论或标签..." style="width: 280px" clearable @input="loadExperience" :prefix-icon="Search" />
+      <el-select v-model="topologyFilter" placeholder="拓扑结构" style="width: 130px" clearable @change="loadExperience">
         <el-option label="SSSR" value="SSSR" />
         <el-option label="DRSS" value="DRSS" />
       </el-select>
-      <el-select v-model="tagFilter" placeholder="Tag" style="width: 150px" clearable @change="loadExperience">
+      <el-select v-model="tagFilter" placeholder="标签" style="width: 150px" clearable @change="loadExperience">
         <el-option v-for="t in allTags" :key="t" :label="t" :value="t" />
       </el-select>
-      <el-select v-model="minRating" placeholder="Min Rating" style="width: 130px" clearable @change="loadExperience">
-        <el-option label="1 star" :value="1" />
-        <el-option label="2 stars" :value="2" />
-        <el-option label="3 stars" :value="3" />
-        <el-option label="4 stars" :value="4" />
-        <el-option label="5 stars" :value="5" />
+      <el-select v-model="minRating" placeholder="最低评分" style="width: 130px" clearable @change="loadExperience">
+        <el-option label="1" :value="1" />
+        <el-option label="2" :value="2" />
+        <el-option label="3" :value="3" />
+        <el-option label="4" :value="4" />
+        <el-option label="5" :value="5" />
       </el-select>
     </div>
 
@@ -66,81 +66,81 @@
     <div class="card">
       <el-table :data="filteredCases" v-loading="loading" stripe @row-click="openDetail" highlight-current-row>
         <el-table-column prop="id" label="ID" width="60" />
-        <el-table-column prop="topology" label="Topology" width="90">
+        <el-table-column prop="topology" label="拓扑" width="90">
           <template #default="{ row }">
             <el-tag size="small" :type="row.topology === 'SSSR' ? 'primary' : 'success'">{{ row.topology }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="source_plan_id" label="Source Plan" width="160" show-overflow-tooltip />
-        <el-table-column label="Key Params" min-width="180">
+        <el-table-column prop="source_plan_id" label="来源方案" width="160" show-overflow-tooltip />
+        <el-table-column label="关键参数" min-width="180">
           <template #default="{ row }">
             <span v-for="(v, k) in row.params" :key="k" class="param-chip">
               {{ k }}={{ v }}
             </span>
           </template>
         </el-table-column>
-        <el-table-column label="Metrics" min-width="220">
+        <el-table-column label="指标" min-width="220">
           <template #default="{ row }">
             <div class="metrics-cell">
-              <span class="metric-item"><strong>T:</strong> {{ row.metrics?.tavg_nm?.toFixed(3) }}Nm</span>
-              <span class="metric-item"><strong>Eff:</strong> {{ row.metrics?.efficiency_pct?.toFixed(1) }}%</span>
-              <span class="metric-item"><strong>Rip:</strong> {{ row.metrics?.ripple_pct?.toFixed(2) }}%</span>
+              <span class="metric-item"><strong>转矩:</strong> {{ row.metrics?.tavg_nm?.toFixed(3) }}Nm</span>
+              <span class="metric-item"><strong>效率:</strong> {{ row.metrics?.efficiency_pct?.toFixed(1) }}%</span>
+              <span class="metric-item"><strong>脉动:</strong> {{ row.metrics?.ripple_pct?.toFixed(2) }}%</span>
             </div>
           </template>
         </el-table-column>
-        <el-table-column prop="conclusion" label="Conclusion" min-width="180" show-overflow-tooltip />
-        <el-table-column label="Tags" width="120">
+        <el-table-column prop="conclusion" label="结论" min-width="180" show-overflow-tooltip />
+        <el-table-column label="标签" width="120">
           <template #default="{ row }">
             <el-tag v-for="t in row.tags" :key="t" size="small" style="margin-right: 4px" type="info">{{ t }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="rating" label="Rating" width="90">
+        <el-table-column prop="rating" label="评分" width="90">
           <template #default="{ row }">
             <el-rate v-model="row.rating" disabled size="small" />
           </template>
         </el-table-column>
-        <el-table-column prop="created_at" label="Created" width="150">
+        <el-table-column prop="created_at" label="创建时间" width="150">
           <template #default="{ row }">{{ formatDate(row.created_at) }}</template>
         </el-table-column>
-        <el-table-column label="Actions" width="120" fixed="right">
+        <el-table-column label="操作" width="120" fixed="right">
           <template #default="{ row }">
-            <el-button size="small" text type="primary" @click.stop="openEdit(row)">Edit</el-button>
-            <el-button size="small" text type="danger" @click.stop="deleteCase(row)">Delete</el-button>
+            <el-button size="small" text type="primary" @click.stop="openEdit(row)">编辑</el-button>
+            <el-button size="small" text type="danger" @click.stop="deleteCase(row)">删除</el-button>
           </template>
         </el-table-column>
       </el-table>
-      <div class="pagination-info">Showing {{ filteredCases.length }} of {{ totalCases }} cases</div>
+      <div class="pagination-info">共 {{ totalCases }} 条,显示 {{ filteredCases.length }} 条</div>
     </div>
 
     <!-- Detail Dialog -->
-    <el-dialog v-model="detailVisible" :title="`Experience Case #${selectedCase?.id}`" width="700px">
+    <el-dialog v-model="detailVisible" :title="`经验案例 #${selectedCase?.id}`" width="700px">
       <div v-if="selectedCase" class="detail-content">
         <el-descriptions :column="2" border size="small">
-          <el-descriptions-item label="Topology">{{ selectedCase.topology }}</el-descriptions-item>
-          <el-descriptions-item label="Source Plan">{{ selectedCase.source_plan_id || '-' }}</el-descriptions-item>
-          <el-descriptions-item label="Model Path" :span="2">{{ selectedCase.model_path || '-' }}</el-descriptions-item>
-          <el-descriptions-item label="Rating">
+          <el-descriptions-item label="拓扑">{{ selectedCase.topology }}</el-descriptions-item>
+          <el-descriptions-item label="来源方案">{{ selectedCase.source_plan_id || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="模型路径" :span="2">{{ selectedCase.model_path || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="评分">
             <el-rate v-model="selectedCase.rating" disabled size="small" />
           </el-descriptions-item>
-          <el-descriptions-item label="Created">{{ formatDate(selectedCase.created_at) }}</el-descriptions-item>
+          <el-descriptions-item label="创建时间">{{ formatDate(selectedCase.created_at) }}</el-descriptions-item>
         </el-descriptions>
 
-        <h4 class="detail-section">Parameters</h4>
+        <h4 class="detail-section">参数</h4>
         <el-table :data="paramRows" size="small" border>
           <el-table-column prop="key" label="Parameter" width="180" />
           <el-table-column prop="value" label="Value" />
         </el-table>
 
-        <h4 class="detail-section">Metrics</h4>
+        <h4 class="detail-section">指标</h4>
         <el-table :data="metricRows" size="small" border>
           <el-table-column prop="key" label="Metric" width="180" />
           <el-table-column prop="value" label="Value" />
         </el-table>
 
-        <h4 class="detail-section">Conclusion</h4>
-        <p class="conclusion-text">{{ selectedCase.conclusion || 'No conclusion recorded.' }}</p>
+        <h4 class="detail-section">结论</h4>
+        <p class="conclusion-text">{{ selectedCase.conclusion || '暂无结论记录。' }}</p>
 
-        <h4 class="detail-section" v-if="selectedCase.tags?.length">Tags</h4>
+        <h4 class="detail-section" v-if="selectedCase.tags?.length">标签</h4>
         <el-tag v-for="t in selectedCase.tags" :key="t" style="margin-right: 6px">{{ t }}</el-tag>
       </div>
     </el-dialog>
@@ -173,70 +173,70 @@
     </el-dialog>
 
     <!-- Import Dialog -->
-    <el-dialog v-model="showImportDialog" title="Import from Simulation Plan" width="500px">
+    <el-dialog v-model="showImportDialog" title="从方案导入经验库" width="500px">
       <el-form label-width="120px" size="small">
-        <el-form-item label="Select Plan" required>
-          <el-select v-model="importPlanId" placeholder="Choose a plan" style="width: 100%" filterable>
+        <el-form-item label="选择方案" required>
+          <el-select v-model="importPlanId" placeholder="请选择方案" style="width: 100%" filterable>
             <el-option v-for="p in allPlans" :key="p.id" :label="`${p.name} (${p.plan_id})`" :value="p.id" />
           </el-select>
         </el-form-item>
-        <el-form-item label="Tags">
-          <el-select v-model="importTags" multiple filterable allow-create style="width: 100%" placeholder="Add tags...">
+        <el-form-item label="标签">
+          <el-select v-model="importTags" multiple filterable allow-create style="width: 100%" placeholder="添加标签...">
             <el-option label="auto-imported" value="auto-imported" />
             <el-option label="verified" value="verified" />
           </el-select>
         </el-form-item>
-        <el-form-item label="Rating">
+        <el-form-item label="评分">
           <el-rate v-model="importRating" />
         </el-form-item>
-        <el-form-item label="Auto Conclusion">
+        <el-form-item label="自动结论">
           <el-switch v-model="importAutoConclusion" />
-          <span style="margin-left: 8px; font-size: 12px; color: #909399">Generate conclusion from metrics</span>
+          <span style="margin-left: 8px; font-size: 12px; color: #909399">根据指标自动生成结论</span>
         </el-form-item>
       </el-form>
       <template #footer>
-        <el-button @click="showImportDialog = false">Cancel</el-button>
-        <el-button type="success" @click="doImport" :loading="importLoading" :disabled="!importPlanId">Import</el-button>
+        <el-button @click="showImportDialog = false">取消</el-button>
+        <el-button type="success" @click="doImport" :loading="importLoading" :disabled="!importPlanId">导入</el-button>
       </template>
     </el-dialog>
 
     <!-- Similar Case Search Dialog -->
-    <el-dialog v-model="showSimilarDialog" title="Find Similar Cases" width="600px">
+    <el-dialog v-model="showSimilarDialog" title="相似案例检索" width="600px">
       <div class="similar-form">
-        <p class="form-hint">Enter parameter values to find similar experience cases. Leave empty to ignore a parameter.</p>
+        <p class="form-hint">输入参数值以检索相似经验案例,留空则忽略该参数。</p>
         <el-form label-width="140px" size="small">
           <el-form-item v-for="p in similarParams" :key="p.key" :label="p.key">
             <el-input v-model.number="p.value" type="number" placeholder="value" clearable style="width: 200px" />
           </el-form-item>
-          <el-form-item label="Topology">
-            <el-select v-model="similarTopology" placeholder="All" clearable style="width: 200px">
+          <el-form-item label="拓扑">
+            <el-select v-model="similarTopology" placeholder="全部" clearable style="width: 200px">
               <el-option label="SSSR" value="SSSR" />
               <el-option label="DRSS" value="DRSS" />
             </el-select>
           </el-form-item>
-          <el-form-item label="Max Results">
+          <el-form-item label="结果数量">
             <el-input-number v-model="similarTopK" :min="1" :max="20" style="width: 200px" />
           </el-form-item>
         </el-form>
-        <el-button type="primary" @click="searchSimilar" :loading="similarLoading">Search</el-button>
+        <el-button type="primary" @click="searchSimilar" :loading="similarLoading">检索</el-button>
       </div>
 
       <div v-if="similarResults.length > 0" class="similar-results">
-        <el-divider>Results ({{ similarResults.length }})</el-divider>
-        <el-table :data="similarResults" size="small" stripe @row-click="(row) => { selectedCase = row; detailVisible = true }">
+        <el-divider>检索结果 ({{ similarResults.length }})</el-divider>
+        <el-table :data="similarResults" size="small" stripe @row-click="(row: any) => { selectedCase = row; detailVisible = true }">
           <el-table-column prop="id" label="ID" width="60" />
-          <el-table-column prop="topology" label="Topo" width="70" />
-          <el-table-column label="Similarity" width="100">
+          <el-table-column prop="topology" label="拓扑" width="70" />
+          <el-table-column label="相似度" width="100">
             <template #default="{ row }">
               <el-progress :percentage="Math.round(row.similarity_score * 100)" :stroke-width="8" :color="row.similarity_score > 0.7 ? '#67c23a' : row.similarity_score > 0.5 ? '#e6a23c' : '#f56c6c'" />
             </template>
           </el-table-column>
-          <el-table-column label="Shared Params" min-width="150">
+          <el-table-column label="共享参数" min-width="150">
             <template #default="{ row }">
               <el-tag v-for="p in row.shared_params" :key="p" size="small" style="margin-right: 4px" type="info">{{ p }}</el-tag>
             </template>
           </el-table-column>
-          <el-table-column prop="conclusion" label="Conclusion" show-overflow-tooltip />
+          <el-table-column prop="conclusion" label="结论" show-overflow-tooltip />
         </el-table>
       </div>
     </el-dialog>
@@ -245,7 +245,7 @@
 
 <script setup lang="ts">
 import { ref, computed, onMounted, reactive } from 'vue'
-import { ElMessage } from 'element-plus'
+import { ElMessage, ElMessageBox } from 'element-plus'
 import { Search, Refresh, Upload } from '@element-plus/icons-vue'
 import { experienceApi, analyticsApi, planApi } from '@/api'
 
@@ -344,7 +344,7 @@ async function loadExperience() {
     }
     allTags.value = Array.from(tags)
   } catch (e: any) {
-    ElMessage.error('Failed to load experience: ' + e.message)
+    ElMessage.error('加载经验库失败:' + e.message)
   } finally {
     loading.value = false
   }
@@ -387,11 +387,11 @@ async function saveEdit() {
       tags: editForm.tags,
       rating: editForm.rating
     })
-    ElMessage.success('Case updated successfully')
+    ElMessage.success('案例更新成功')
     editVisible.value = false
     await loadAll()
   } catch (e: any) {
-    ElMessage.error('Update failed: ' + e.message)
+    ElMessage.error('更新失败:' + e.message)
   } finally {
     editLoading.value = false
   }
@@ -399,17 +399,17 @@ async function saveEdit() {
 
 async function deleteCase(row: any) {
   try {
-    await ElMessage.confirm(`Delete experience case #${row.id}?`, 'Confirm Delete', {
+    await ElMessageBox.confirm(`确定删除经验案例 #${row.id}?`, '确认删除', {
       type: 'warning',
-      confirmButtonText: 'Delete',
-      cancelButtonText: 'Cancel'
+      confirmButtonText: '删除',
+      cancelButtonText: '取消'
     })
   } catch {
     return
   }
   try {
     await experienceApi.delete(row.id)
-    ElMessage.success('Case deleted')
+    ElMessage.success('案例已删除')
     await loadAll()
   } catch (e: any) {
     ElMessage.error('Delete failed: ' + e.message)
@@ -453,7 +453,7 @@ async function searchSimilar() {
     }
   }
   if (Object.keys(params).length === 0) {
-    ElMessage.warning('Please enter at least one parameter value')
+    ElMessage.warning('请至少输入一个参数值')
     return
   }
   similarLoading.value = true
@@ -461,10 +461,10 @@ async function searchSimilar() {
     const res = await analyticsApi.experienceSimilar(params, similarTopology.value || undefined, similarTopK.value)
     similarResults.value = res.data.items
     if (similarResults.value.length === 0) {
-      ElMessage.info('No similar cases found. Try adjusting tolerance or parameters.')
+      ElMessage.info('未找到相似案例,请调整参数或容差。')
     }
   } catch (e: any) {
-    ElMessage.error('Search failed: ' + e.message)
+    ElMessage.error('检索失败:' + e.message)
   } finally {
     similarLoading.value = false
   }

+ 30 - 30
web/frontend/src/views/PlanDetail.vue

@@ -3,7 +3,7 @@
     <div class="page-header">
       <div>
         <el-button text @click="goBack" style="padding: 0; margin-bottom: 8px">
-          <el-icon><ArrowLeft /></el-icon> Back
+          <el-icon><ArrowLeft /></el-icon> 返回
         </el-button>
         <span class="page-title">{{ plan?.name }}</span>
         <el-tag size="small" type="primary" style="margin-left: 12px">{{ plan?.plan_id }}</el-tag>
@@ -11,11 +11,11 @@
       </div>
       <div>
         <el-button @click="downloadPlan">
-          <el-icon><Download /></el-icon> Download JSON
+          <el-icon><Download /></el-icon> 下载JSON
         </el-button>
         <el-upload :show-file-list="false" :before-upload="uploadResults" accept=".csv">
           <el-button type="success">
-            <el-icon><Upload /></el-icon> Upload Results
+            <el-icon><Upload /></el-icon> 上传结果
           </el-button>
         </el-upload>
       </div>
@@ -24,25 +24,25 @@
     <el-row :gutter="16" v-if="plan">
       <el-col :span="6">
         <div class="stat-card">
-          <div class="stat-label">Estimated Points</div>
+          <div class="stat-label">预估数据点</div>
           <div class="stat-value">{{ plan.estimated_points }}</div>
         </div>
       </el-col>
       <el-col :span="6">
         <div class="stat-card">
-          <div class="stat-label">Est. Time</div>
-          <div class="stat-value">{{ plan.estimated_time_min }} min</div>
+          <div class="stat-label">预估时间</div>
+          <div class="stat-value">{{ plan.estimated_time_min }} 分钟</div>
         </div>
       </el-col>
       <el-col :span="6">
         <div class="stat-card">
-          <div class="stat-label">Results Uploaded</div>
+          <div class="stat-label">已上传结果</div>
           <div class="stat-value">{{ results.length }}</div>
         </div>
       </el-col>
       <el-col :span="6">
         <div class="stat-card">
-          <div class="stat-label">OK / Failed</div>
+          <div class="stat-label">成功 / 失败</div>
           <div class="stat-value">{{ okCount }} / {{ failedCount }}</div>
         </div>
       </el-col>
@@ -51,18 +51,18 @@
     <el-row :gutter="16" v-if="plan">
       <el-col :span="10">
         <div class="card">
-          <h3 style="margin-bottom: 12px">Scan Variables</h3>
+          <h3 style="margin-bottom: 12px">扫描变量</h3>
           <el-table :data="variablesList" size="small" border>
-            <el-table-column prop="name" label="Variable" width="120" />
-            <el-table-column prop="unit" label="Unit" width="70" />
-            <el-table-column prop="count" label="Count" width="70" />
-            <el-table-column prop="values" label="Values" />
+            <el-table-column prop="name" label="变量" width="120" />
+            <el-table-column prop="unit" label="单位" width="70" />
+            <el-table-column prop="count" label="数量" width="70" />
+            <el-table-column prop="values" label="取值" />
           </el-table>
         </div>
       </el-col>
       <el-col :span="14">
         <div class="card">
-          <h3 style="margin-bottom: 12px">Plan JSON</h3>
+          <h3 style="margin-bottom: 12px">方案JSON</h3>
           <pre style="background: #f8fafc; padding: 12px; border-radius: 4px; font-size: 11px; max-height: 300px; overflow: auto">{{ JSON.stringify(plan.plan_data, null, 2) }}</pre>
         </div>
       </el-col>
@@ -70,30 +70,30 @@
 
     <!-- Results Section -->
     <div class="card" v-if="results.length > 0">
-      <h3 style="margin-bottom: 12px">Simulation Results</h3>
+      <h3 style="margin-bottom: 12px">仿真结果</h3>
       <el-table :data="results" stripe border size="small" max-height="400">
         <el-table-column prop="run_index" label="#" width="50" fixed />
         <el-table-column v-for="param in paramKeys" :key="param" :label="param" :prop="`params.${param}`" width="100">
           <template #default="{ row }">{{ row.params[param] }}</template>
         </el-table-column>
-        <el-table-column prop="metrics.tavg_nm" label="Tavg (Nm)" width="100">
+        <el-table-column prop="metrics.tavg_nm" label="平均转矩(Nm)" width="110">
           <template #default="{ row }">{{ row.metrics.tavg_nm?.toFixed(4) }}</template>
         </el-table-column>
-        <el-table-column prop="metrics.ripple_pct" label="Ripple (%)" width="100">
+        <el-table-column prop="metrics.ripple_pct" label="转矩脉动(%)" width="110">
           <template #default="{ row }">{{ row.metrics.ripple_pct?.toFixed(3) }}</template>
         </el-table-column>
-        <el-table-column prop="metrics.efficiency_pct" label="Eff (%)" width="90">
+        <el-table-column prop="metrics.efficiency_pct" label="效率(%)" width="90">
           <template #default="{ row }">{{ row.metrics.efficiency_pct?.toFixed(2) }}</template>
         </el-table-column>
-        <el-table-column prop="metrics.total_losses_w" label="Loss (W)" width="90">
+        <el-table-column prop="metrics.total_losses_w" label="总损耗(W)" width="100">
           <template #default="{ row }">{{ row.metrics.total_losses_w?.toFixed(2) }}</template>
         </el-table-column>
-        <el-table-column prop="metrics.copper_loss_w" label="Cu (W)" width="80" />
-        <el-table-column prop="metrics.iron_loss_w" label="Fe (W)" width="80" />
-        <el-table-column prop="metrics.back_emf_v" label="BEMF (V)" width="90" />
-        <el-table-column prop="metrics.no_load_speed_rpm" label="NoLoad RPM" width="100" />
-        <el-table-column prop="solve_time_s" label="Time (s)" width="80" />
-        <el-table-column prop="status" label="Status" width="80">
+        <el-table-column prop="metrics.copper_loss_w" label="铜耗(W)" width="90" />
+        <el-table-column prop="metrics.iron_loss_w" label="铁耗(W)" width="90" />
+        <el-table-column prop="metrics.back_emf_v" label="反电动势(V)" width="110" />
+        <el-table-column prop="metrics.no_load_speed_rpm" label="空载转速(rpm)" width="120" />
+        <el-table-column prop="solve_time_s" label="求解时间(s)" width="100" />
+        <el-table-column prop="status" label="状态" width="80">
           <template #default="{ row }">
             <el-tag size="small" :type="row.status === 'OK' ? 'success' : 'danger'">{{ row.status }}</el-tag>
           </template>
@@ -101,7 +101,7 @@
       </el-table>
     </div>
 
-    <el-empty v-else description="No results uploaded yet. Upload scan_results.csv to see results." />
+    <el-empty v-else description="暂无结果。请上传 scan_results.csv 查看仿真结果。" />
   </div>
 </template>
 
@@ -145,7 +145,7 @@ async function loadPlan() {
     const resultsRes = await planApi.getResults(id)
     results.value = resultsRes.data.items
   } catch (e: any) {
-    ElMessage.error('Failed to load plan: ' + e.message)
+    ElMessage.error('加载方案失败:' + e.message)
   } finally {
     loading.value = false
   }
@@ -160,17 +160,17 @@ function downloadPlan() {
   a.download = `${plan.value.plan_id}.json`
   a.click()
   URL.revokeObjectURL(url)
-  ElMessage.success('Plan JSON downloaded')
+  ElMessage.success('方案JSON已下载')
 }
 
 async function uploadResults(file: File) {
   try {
     const id = Number(route.params.id)
     const res = await planApi.uploadResults(id, file)
-    ElMessage.success(`Uploaded ${res.data.count} results`)
+    ElMessage.success(`已上传 ${res.data.count} 条结果`)
     await loadPlan()
   } catch (e: any) {
-    ElMessage.error('Upload failed: ' + e.message)
+    ElMessage.error('上传失败:' + e.message)
   }
   return false
 }

+ 55 - 55
web/frontend/src/views/ProjectDetail.vue

@@ -3,108 +3,108 @@
     <div class="page-header">
       <div>
         <el-button text @click="$router.push('/projects')" style="padding: 0; margin-bottom: 8px">
-          <el-icon><ArrowLeft /></el-icon> Back to Projects
+          <el-icon><ArrowLeft /></el-icon> 返回项目列表
         </el-button>
         <span class="page-title">{{ project?.name }}</span>
         <el-tag size="small" style="margin-left: 12px">{{ project?.topology }}</el-tag>
         <el-tag size="small" type="success" style="margin-left: 8px">{{ project?.status }}</el-tag>
       </div>
       <el-button type="primary" @click="showCreatePlan = true">
-        <el-icon><Plus /></el-icon> New Plan
+        <el-icon><Plus /></el-icon> 新建方案
       </el-button>
     </div>
 
     <el-row :gutter="16" v-if="project">
       <el-col :span="8">
         <div class="card">
-          <h3 style="margin-bottom: 12px">Project Info</h3>
+          <h3 style="margin-bottom: 12px">项目信息</h3>
           <el-descriptions :column="1" border size="small">
             <el-descriptions-item label="ID">{{ project.id }}</el-descriptions-item>
-            <el-descriptions-item label="Topology">{{ project.topology }}</el-descriptions-item>
-            <el-descriptions-item label="Model">{{ project.model_path || '-' }}</el-descriptions-item>
-            <el-descriptions-item label="Status">{{ project.status }}</el-descriptions-item>
-            <el-descriptions-item label="Plans">{{ project.plan_count }}</el-descriptions-item>
-            <el-descriptions-item label="Created">{{ formatDate(project.created_at) }}</el-descriptions-item>
-            <el-descriptions-item label="Updated">{{ formatDate(project.updated_at) }}</el-descriptions-item>
+            <el-descriptions-item label="拓扑结构">{{ project.topology }}</el-descriptions-item>
+            <el-descriptions-item label="模型">{{ project.model_path || '-' }}</el-descriptions-item>
+            <el-descriptions-item label="状态">{{ project.status }}</el-descriptions-item>
+            <el-descriptions-item label="方案数">{{ project.plan_count }}</el-descriptions-item>
+            <el-descriptions-item label="创建时间">{{ formatDate(project.created_at) }}</el-descriptions-item>
+            <el-descriptions-item label="更新时间">{{ formatDate(project.updated_at) }}</el-descriptions-item>
           </el-descriptions>
           <div v-if="project.description" style="margin-top: 12px">
-            <div style="color: #6b7280; font-size: 12px; margin-bottom: 4px">Description</div>
+            <div style="color: #6b7280; font-size: 12px; margin-bottom: 4px">项目描述</div>
             <div style="font-size: 13px">{{ project.description }}</div>
           </div>
         </div>
       </el-col>
       <el-col :span="16">
         <div class="card">
-          <h3 style="margin-bottom: 12px">Boundary Conditions</h3>
+          <h3 style="margin-bottom: 12px">边界条件</h3>
           <pre v-if="Object.keys(project.boundary_conditions).length" style="background: #f8fafc; padding: 12px; border-radius: 4px; font-size: 12px">{{ JSON.stringify(project.boundary_conditions, null, 2) }}</pre>
-          <el-empty v-else description="No boundary conditions set" :image-size="60" />
+          <el-empty v-else description="未设置边界条件" :image-size="60" />
         </div>
       </el-col>
     </el-row>
 
     <div class="card">
-      <h3 style="margin-bottom: 12px">Simulation Plans</h3>
+      <h3 style="margin-bottom: 12px">仿真方案</h3>
       <el-table :data="plans" stripe @row-click="goToPlan">
         <el-table-column prop="id" label="ID" width="60" />
-        <el-table-column prop="name" label="Name" min-width="200">
+        <el-table-column prop="name" label="方案名称" min-width="200">
           <template #default="{ row }">
             <span style="color: #3b82f6; cursor: pointer">{{ row.name }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="plan_id" label="Plan UUID" width="180" />
-        <el-table-column prop="status" label="Status" width="100">
+        <el-table-column prop="plan_id" label="方案UUID" width="180" />
+        <el-table-column prop="status" label="状态" width="100">
           <template #default="{ row }">
             <el-tag size="small" :type="planStatusType(row.status)">{{ row.status }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="estimated_points" label="Points" width="80" />
-        <el-table-column prop="estimated_time_min" label="Est. (min)" width="90" />
-        <el-table-column prop="result_count" label="Results" width="80" />
-        <el-table-column prop="updated_at" label="Updated" width="160">
+        <el-table-column prop="estimated_points" label="数据点" width="80" />
+        <el-table-column prop="estimated_time_min" label="预估(分钟)" width="100" />
+        <el-table-column prop="result_count" label="结果数" width="80" />
+        <el-table-column prop="updated_at" label="更新时间" width="160">
           <template #default="{ row }">{{ formatDate(row.updated_at) }}</template>
         </el-table-column>
       </el-table>
     </div>
 
     <!-- Create Plan Dialog with Visual Variable Editor -->
-    <el-dialog v-model="showCreatePlan" title="Create New Simulation Plan" width="900px" top="5vh">
+    <el-dialog v-model="showCreatePlan" title="新建仿真方案" width="900px" top="5vh">
       <el-form :model="planForm" label-width="100px">
         <el-row :gutter="12">
           <el-col :span="14">
-            <el-form-item label="Plan Name">
-              <el-input v-model="planForm.name" placeholder="e.g. Airgap Scan 0.6-1.5mm" />
+            <el-form-item label="方案名称">
+              <el-input v-model="planForm.name" placeholder="例如:气隙扫描 0.6-1.5mm" />
             </el-form-item>
           </el-col>
           <el-col :span="10">
-            <el-form-item label="Model Path">
+            <el-form-item label="模型路径">
               <el-input v-model="planForm.model_path" :placeholder="project?.model_path || 'models/xxx.mot'" />
             </el-form-item>
           </el-col>
         </el-row>
-        <el-form-item label="Notes">
+        <el-form-item label="备注">
           <el-input v-model="planForm.notes" type="textarea" :rows="1" />
         </el-form-item>
       </el-form>
 
-      <el-divider content-position="left">Scan Variables</el-divider>
+      <el-divider content-position="left">扫描变量</el-divider>
 
       <div class="plan-editor-toolbar">
         <el-button type="primary" size="small" @click="addVariable">
-          <el-icon><Plus /></el-icon> Add Variable
+          <el-icon><Plus /></el-icon> 添加变量
         </el-button>
         <el-button type="success" size="small" @click="generateFromRules" :loading="generating">
-          <el-icon><MagicStick /></el-icon> Generate from Rules
+          <el-icon><MagicStick /></el-icon> 规则生成
         </el-button>
-        <el-button size="small" @click="clearVariables">Clear All</el-button>
+        <el-button size="small" @click="clearVariables">清空全部</el-button>
         <div class="plan-estimate">
-          <el-tag type="info">Total Points: <b>{{ estimatedPoints }}</b></el-tag>
-          <el-tag type="warning" style="margin-left: 8px">Est. Time: <b>{{ estimatedTimeMin }} min</b></el-tag>
+          <el-tag type="info">总数据点:<b>{{ estimatedPoints }}</b></el-tag>
+          <el-tag type="warning" style="margin-left: 8px">预估时间:<b>{{ estimatedTimeMin }} 分钟</b></el-tag>
         </div>
       </div>
 
       <el-table :data="planVariables" size="small" border class="variable-table" max-height="320">
         <el-table-column label="#" type="index" width="40" align="center" />
-        <el-table-column label="Parameter" width="200">
+        <el-table-column label="参数" width="200">
           <template #default="{ row }">
             <el-select v-model="row.name" size="small" filterable @change="onParamChange(row)" style="width: 100%">
               <el-option
@@ -119,50 +119,50 @@
             </el-select>
           </template>
         </el-table-column>
-        <el-table-column label="Mode" width="90" align="center">
+        <el-table-column label="模式" width="90" align="center">
           <template #default="{ row }">
             <el-radio-group v-model="row.mode" size="small" @change="onModeChange(row)">
-              <el-radio-button label="range">Range</el-radio-button>
-              <el-radio-button label="values">Values</el-radio-button>
+              <el-radio-button label="range">范围</el-radio-button>
+              <el-radio-button label="values">枚举</el-radio-button>
             </el-radio-group>
           </template>
         </el-table-column>
-        <el-table-column label="Start" width="90">
+        <el-table-column label="起始" width="90">
           <template #default="{ row }">
             <el-input-number v-if="row.mode === 'range'" v-model="row.start" :step="row.step || 0.1" :precision="4" controls-position="right" size="small" style="width: 100%" @change="updateValues(row)" />
             <span v-else style="color: #c0c4cc">-</span>
           </template>
         </el-table-column>
-        <el-table-column label="Stop" width="90">
+        <el-table-column label="终止" width="90">
           <template #default="{ row }">
             <el-input-number v-if="row.mode === 'range'" v-model="row.stop" :step="row.step || 0.1" :precision="4" controls-position="right" size="small" style="width: 100%" @change="updateValues(row)" />
             <span v-else style="color: #c0c4cc">-</span>
           </template>
         </el-table-column>
-        <el-table-column label="Step" width="80">
+        <el-table-column label="步长" width="80">
           <template #default="{ row }">
             <el-input-number v-if="row.mode === 'range'" v-model="row.step" :min="0.001" :precision="4" controls-position="right" size="small" style="width: 100%" @change="updateValues(row)" />
             <span v-else style="color: #c0c4cc">-</span>
           </template>
         </el-table-column>
-        <el-table-column label="Explicit Values">
+        <el-table-column label="枚举值">
           <template #default="{ row }">
             <el-input
               v-if="row.mode === 'values'"
               v-model="row.values_text"
               size="small"
-              placeholder="e.g. 0.6, 1.0, 1.5"
+              placeholder="例如:0.6, 1.0, 1.5"
               @change="parseValues(row)"
             />
             <span v-else style="font-size: 12px; color: #606266">{{ row.values?.join(', ') || '' }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="Count" width="60" align="center">
+        <el-table-column label="数量" width="60" align="center">
           <template #default="{ row }">
             <el-tag size="small" :type="row.values?.length > 0 ? 'success' : 'info'">{{ row.values?.length || 0 }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="Actions" width="60" align="center" fixed="right">
+        <el-table-column label="操作" width="60" align="center" fixed="right">
           <template #default="{ $index }">
             <el-button size="small" text type="danger" @click="removeVariable($index)">
               <el-icon><Delete /></el-icon>
@@ -172,11 +172,11 @@
       </el-table>
 
       <div v-if="planVariables.length === 0" style="text-align: center; padding: 24px; color: #909399">
-        No scan variables. Click "Add Variable" or "Generate from Rules" to start.
+        暂无扫描变量。点击"添加变量"或"规则生成"开始配置。
       </div>
 
       <template #footer>
-        <el-button @click="showCreatePlan = false">Cancel</el-button>
+        <el-button @click="showCreatePlan = false">取消</el-button>
         <el-button type="primary" @click="createPlan" :loading="creating" :disabled="planVariables.length === 0">
           Create Plan ({{ estimatedPoints }} points)
         </el-button>
@@ -227,7 +227,7 @@ async function loadScanParams() {
     const res = await generationApi.listScanParameters()
     scanParams.value = res.data.parameters
   } catch (e: any) {
-    ElMessage.error('Failed to load scan parameters: ' + e.message)
+    ElMessage.error('加载扫描参数失败:' + e.message)
   }
 }
 
@@ -240,12 +240,12 @@ function availableParams(currentName: string) {
 // Add a new variable row
 function addVariable() {
   if (scanParams.value.length === 0) {
-    ElMessage.warning('Scan parameters not loaded yet')
+    ElMessage.warning('扫描参数尚未加载')
     return
   }
   const available = availableParams('')
   if (available.length === 0) {
-    ElMessage.warning('All parameters already added')
+    ElMessage.warning('所有参数已添加')
     return
   }
   const p = available[0]
@@ -347,7 +347,7 @@ const estimatedTimeMin = computed(() => estimatedPoints.value * 3)
 // Generate plan from rules (backend rule engine)
 async function generateFromRules() {
   if (!project.value) {
-    ElMessage.warning('Project not loaded')
+    ElMessage.warning('项目未加载')
     return
   }
   generating.value = true
@@ -371,7 +371,7 @@ async function generateFromRules() {
       values: v.values || [],
       values_text: '',
     }))
-    ElMessage.success(`Generated plan with ${generated.estimated_points} points`)
+    ElMessage.success(`已生成方案,共 ${generated.estimated_points} 个数据点`)
   } catch (e: any) {
     ElMessage.error('Failed to generate plan: ' + e.message)
   } finally {
@@ -388,7 +388,7 @@ async function loadProject() {
     const plansRes = await planApi.list({ project_id: id, limit: 100 })
     plans.value = plansRes.data.items
   } catch (e: any) {
-    ElMessage.error('Failed to load project: ' + e.message)
+    ElMessage.error('加载项目失败:' + e.message)
   } finally {
     loading.value = false
   }
@@ -396,17 +396,17 @@ async function loadProject() {
 
 async function createPlan() {
   if (!planForm.value.name.trim()) {
-    ElMessage.warning('Please enter a plan name')
+    ElMessage.warning('请输入方案名称')
     return
   }
   if (planVariables.value.length === 0) {
-    ElMessage.warning('Please add at least one scan variable')
+    ElMessage.warning('请至少添加一个扫描变量')
     return
   }
   // Validate all variables have values
   for (const v of planVariables.value) {
     if (!v.values || v.values.length === 0) {
-      ElMessage.warning(`Variable "${v.name}" has no valid values`)
+      ElMessage.warning(`变量 "${v.name}" 没有有效值`)
       return
     }
   }
@@ -431,12 +431,12 @@ async function createPlan() {
       plan_data: planData,
       notes: planForm.value.notes,
     })
-    ElMessage.success(`Plan created with ${estimatedPoints.value} points`)
+    ElMessage.success(`方案已创建,共 ${estimatedPoints.value} 个数据点`)
     showCreatePlan.value = false
     resetPlanForm()
     await loadProject()
   } catch (e: any) {
-    ElMessage.error('Failed to create plan: ' + e.message)
+    ElMessage.error('创建方案失败:' + e.message)
   } finally {
     creating.value = false
   }

+ 42 - 42
web/frontend/src/views/ProjectList.vue

@@ -1,34 +1,34 @@
 <template>
   <div class="page-container">
     <div class="page-header">
-      <span class="page-title">Simulation Projects</span>
+      <span class="page-title">仿真项目</span>
       <el-button type="primary" @click="showCreateDialog = true">
-        <el-icon><Plus /></el-icon> New Project
+        <el-icon><Plus /></el-icon> 新建项目
       </el-button>
     </div>
 
     <el-row :gutter="16" class="stats-row">
       <el-col :span="6">
         <div class="stat-card">
-          <div class="stat-label">Total Projects</div>
+          <div class="stat-label">项目总数</div>
           <div class="stat-value">{{ total }}</div>
         </div>
       </el-col>
       <el-col :span="6">
         <div class="stat-card">
-          <div class="stat-label">SSSR Topology</div>
+          <div class="stat-label">SSSR拓扑</div>
           <div class="stat-value">{{ sssrCount }}</div>
         </div>
       </el-col>
       <el-col :span="6">
         <div class="stat-card">
-          <div class="stat-label">Active</div>
+          <div class="stat-label">进行中</div>
           <div class="stat-value">{{ activeCount }}</div>
         </div>
       </el-col>
       <el-col :span="6">
         <div class="stat-card">
-          <div class="stat-label">Completed</div>
+          <div class="stat-label">已完成</div>
           <div class="stat-value">{{ completedCount }}</div>
         </div>
       </el-col>
@@ -37,65 +37,65 @@
     <div class="card">
       <el-table :data="projects" v-loading="loading" stripe @row-click="goToProject">
         <el-table-column prop="id" label="ID" width="70" />
-        <el-table-column prop="name" label="Name" min-width="200">
+        <el-table-column prop="name" label="项目名称" min-width="200">
           <template #default="{ row }">
             <span style="color: #3b82f6; cursor: pointer; font-weight: 500">{{ row.name }}</span>
           </template>
         </el-table-column>
-        <el-table-column prop="topology" label="Topology" width="100">
+        <el-table-column prop="topology" label="拓扑结构" width="100">
           <template #default="{ row }">
             <el-tag size="small" :type="row.topology === 'SSSR' ? 'primary' : 'warning'">{{ row.topology }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="status" label="Status" width="110">
+        <el-table-column prop="status" label="状态" width="110">
           <template #default="{ row }">
             <el-tag size="small" :type="statusType(row.status)">{{ row.status }}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="plan_count" label="Plans" width="80" />
-        <el-table-column prop="description" label="Description" min-width="200" show-overflow-tooltip />
-        <el-table-column prop="updated_at" label="Updated" width="170">
+        <el-table-column prop="plan_count" label="方案数" width="80" />
+        <el-table-column prop="description" label="项目描述" min-width="200" show-overflow-tooltip />
+        <el-table-column prop="updated_at" label="更新时间" width="170">
           <template #default="{ row }">{{ formatDate(row.updated_at) }}</template>
         </el-table-column>
-        <el-table-column label="Actions" width="100" fixed="right">
+        <el-table-column label="操作" width="100" fixed="right">
           <template #default="{ row }">
-            <el-button size="small" text type="danger" @click.stop="deleteProject(row.id)">Delete</el-button>
+            <el-button size="small" text type="danger" @click.stop="deleteProject(row.id)">删除</el-button>
           </template>
         </el-table-column>
       </el-table>
     </div>
 
     <!-- Create Project Dialog -->
-    <el-dialog v-model="showCreateDialog" title="Create New Project" width="640px">
+    <el-dialog v-model="showCreateDialog" title="新建项目" width="640px">
       <el-form :model="newProject" label-width="130px">
-        <el-form-item label="Project Name">
-          <el-input v-model="newProject.name" placeholder="e.g. MARS Airgap Optimization" />
+        <el-form-item label="项目名称">
+          <el-input v-model="newProject.name" placeholder="例如:MARS气隙优化" />
         </el-form-item>
-        <el-form-item label="Topology">
+        <el-form-item label="拓扑结构">
           <el-select v-model="newProject.topology" style="width: 100%">
-            <el-option label="SSSR (Single Stator Single Rotor)" value="SSSR" />
-            <el-option label="DRSS (Double Rotor Single Stator)" value="DRSS" />
-            <el-option label="SDSR (Single Stator Double Rotor)" value="SDSR" />
+            <el-option label="SSSR(单定子单转子)" value="SSSR" />
+            <el-option label="DRSS(双转子单定子)" value="DRSS" />
+            <el-option label="SDSR(单定子双转子)" value="SDSR" />
           </el-select>
         </el-form-item>
-        <el-form-item label="Model Path">
+        <el-form-item label="模型路径">
           <el-input v-model="newProject.model_path" placeholder="models/xxx.mot" />
         </el-form-item>
-        <el-form-item label="Description">
+        <el-form-item label="项目描述">
           <el-input v-model="newProject.description" type="textarea" :rows="2" />
         </el-form-item>
 
-        <el-divider content-position="left">Boundary Conditions</el-divider>
+        <el-divider content-position="left">边界条件</el-divider>
 
         <el-row :gutter="12">
           <el-col :span="12">
-            <el-form-item label="Outer Diameter">
+            <el-form-item label="外径">
               <el-input-number v-model="bc.outer_diameter_mm" :min="10" :max="500" :step="1" :precision="1" controls-position="right" style="width: 100%" />
               <span class="unit-suffix">mm</span>
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="Inner Diameter">
+            <el-form-item label="内径">
               <el-input-number v-model="bc.inner_diameter_mm" :min="0" :max="400" :step="1" :precision="1" controls-position="right" style="width: 100%" />
               <span class="unit-suffix">mm</span>
             </el-form-item>
@@ -104,13 +104,13 @@
 
         <el-row :gutter="12">
           <el-col :span="12">
-            <el-form-item label="Shaft Speed">
+            <el-form-item label="转速">
               <el-input-number v-model="bc.speed_rpm" :min="100" :max="50000" :step="500" :precision="0" controls-position="right" style="width: 100%" />
               <span class="unit-suffix">rpm</span>
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="RMS Current">
+            <el-form-item label="有效值电流">
               <el-input-number v-model="bc.current_a" :min="0.5" :max="200" :step="1" :precision="1" controls-position="right" style="width: 100%" />
               <span class="unit-suffix">A</span>
             </el-form-item>
@@ -119,13 +119,13 @@
 
         <el-row :gutter="12">
           <el-col :span="12">
-            <el-form-item label="Magnet Temp">
+            <el-form-item label="磁钢温度">
               <el-input-number v-model="bc.magnet_temp_c" :min="-40" :max="200" :step="10" :precision="0" controls-position="right" style="width: 100%" />
-              <span class="unit-suffix">C</span>
+              <span class="unit-suffix">°C</span>
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="Target Torque">
+            <el-form-item label="目标转矩">
               <el-input-number v-model="bc.target_torque_nm" :min="0" :max="1000" :step="0.1" :precision="2" controls-position="right" style="width: 100%" />
               <span class="unit-suffix">Nm</span>
             </el-form-item>
@@ -134,13 +134,13 @@
 
         <el-row :gutter="12">
           <el-col :span="12">
-            <el-form-item label="Target Efficiency">
+            <el-form-item label="目标效率">
               <el-input-number v-model="bc.target_efficiency_pct" :min="0" :max="100" :step="1" :precision="1" controls-position="right" style="width: 100%" />
               <span class="unit-suffix">%</span>
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="Max Losses">
+            <el-form-item label="最大损耗">
               <el-input-number v-model="bc.max_losses_w" :min="0" :max="10000" :step="10" :precision="0" controls-position="right" style="width: 100%" />
               <span class="unit-suffix">W</span>
             </el-form-item>
@@ -148,8 +148,8 @@
         </el-row>
       </el-form>
       <template #footer>
-        <el-button @click="showCreateDialog = false">Cancel</el-button>
-        <el-button type="primary" @click="createProject" :loading="creating">Create</el-button>
+        <el-button @click="showCreateDialog = false">取消</el-button>
+        <el-button type="primary" @click="createProject" :loading="creating">创建</el-button>
       </template>
     </el-dialog>
   </div>
@@ -191,7 +191,7 @@ async function loadProjects() {
     projects.value = res.data.items
     total.value = res.data.total
   } catch (e: any) {
-    ElMessage.error('Failed to load projects: ' + e.message)
+    ElMessage.error('加载项目失败:' + e.message)
   } finally {
     loading.value = false
   }
@@ -199,7 +199,7 @@ async function loadProjects() {
 
 async function createProject() {
   if (!newProject.value.name.trim()) {
-    ElMessage.warning('Please enter a project name')
+    ElMessage.warning('请输入项目名称')
     return
   }
   creating.value = true
@@ -213,7 +213,7 @@ async function createProject() {
     }
     const payload = { ...newProject.value, boundary_conditions: boundaryConditions }
     await projectApi.create(payload)
-    ElMessage.success('Project created')
+    ElMessage.success('项目创建成功')
     showCreateDialog.value = false
     newProject.value = { name: '', topology: 'SSSR', model_path: '', description: '' }
     bc.value = {
@@ -223,7 +223,7 @@ async function createProject() {
     }
     await loadProjects()
   } catch (e: any) {
-    ElMessage.error('Failed to create project: ' + e.message)
+    ElMessage.error('创建项目失败:' + e.message)
   } finally {
     creating.value = false
   }
@@ -231,12 +231,12 @@ async function createProject() {
 
 async function deleteProject(id: number) {
   try {
-    await ElMessageBox.confirm('Delete this project and all its plans/results?', 'Confirm', { type: 'warning' })
+    await ElMessageBox.confirm('确定删除此项目及其所有方案和结果?', '确认删除', { type: 'warning' })
     await projectApi.delete(id)
-    ElMessage.success('Project deleted')
+    ElMessage.success('项目已删除')
     await loadProjects()
   } catch (e: any) {
-    if (e !== 'cancel') ElMessage.error('Failed to delete')
+    if (e !== 'cancel') ElMessage.error('删除失败')
   }
 }