瀏覽代碼

fix(frontend): 移除所有页面的 mock 数据,改为调用真实 API

- Generations.vue: 从 API 获取生成任务列表和模板列表
- GenerationDetail.vue: 从 API 获取生成任务详情
- TemplateDetail.vue: 从 API 获取模板详情、来源文件、变量
- Editor.vue: 从 API 获取模板数据,CRUD 操作调用 API
- vite.config.js: API 代理改为 192.168.2.3:18520
何文松 1 月之前
父節點
當前提交
7ade2c7341

+ 78 - 45
frontend/vue-demo/src/views/Editor.vue

@@ -312,17 +312,14 @@ const route = useRoute()
 const templateStore = useTemplateStore()
 
 const templateId = route.params.templateId
-const reportTitle = ref('智慧园区建设项目可行性研究报告')
+const reportTitle = ref('')
 const viewMode = ref('edit')
 const saved = ref(true)
 const editorRef = ref(null)
+const loading = ref(false)
 
-// 来源文件
-const sourceFiles = ref([
-  { id: '1', alias: '可研批复', required: true },
-  { id: '2', alias: '项目建议书', required: true },
-  { id: '3', alias: '技术方案', required: false }
-])
+// 来源文件(从 API 获取)
+const sourceFiles = ref([])
 const selectedFile = ref(null)
 const showAddSourceDialog = ref(false)
 const newSourceFile = reactive({
@@ -331,13 +328,34 @@ const newSourceFile = reactive({
   required: true
 })
 
-// 变量
-const variables = ref([
-  { id: '1', name: 'project_name', displayName: '项目名称', category: 'entity', exampleValue: '智慧园区建设项目', sourceType: 'document' },
-  { id: '2', name: 'total_investment', displayName: '总投资额', category: 'data', exampleValue: '5000万元', sourceType: 'document' },
-  { id: '3', name: 'project_location', displayName: '项目地点', category: 'location', exampleValue: '华南科技园', sourceType: 'document' },
-  { id: '4', name: 'tech_solution', displayName: '技术方案', category: 'concept', exampleValue: '智能化管理平台', sourceType: 'document' }
-])
+// 变量(从 API 获取)
+const variables = ref([])
+
+// 加载模板数据
+onMounted(async () => {
+  await fetchTemplateData()
+})
+
+async function fetchTemplateData() {
+  loading.value = true
+  try {
+    await templateStore.fetchTemplateDetail(templateId)
+    
+    // 设置模板标题
+    reportTitle.value = templateStore.currentTemplate?.name || '未命名模板'
+    
+    // 设置来源文件
+    sourceFiles.value = templateStore.sourceFiles || []
+    
+    // 设置变量
+    variables.value = templateStore.variables || []
+  } catch (error) {
+    console.error('加载模板失败:', error)
+    ElMessage.error('加载模板失败')
+  } finally {
+    loading.value = false
+  }
+}
 const showVariableDialog = ref(false)
 const showAddVariableDialog = ref(false)
 const editingVariable = ref(null)
@@ -412,23 +430,30 @@ function selectFile(file) {
   selectedFile.value = file
 }
 
-function removeSourceFile(file) {
-  sourceFiles.value = sourceFiles.value.filter(f => f.id !== file.id)
-  ElMessage.success('删除成功')
+async function removeSourceFile(file) {
+  try {
+    await templateStore.deleteSourceFile(file.id)
+    sourceFiles.value = sourceFiles.value.filter(f => f.id !== file.id)
+    ElMessage.success('删除成功')
+  } catch (error) {
+    ElMessage.error('删除失败: ' + error.message)
+  }
 }
 
-function addSourceFile() {
+async function addSourceFile() {
   if (!newSourceFile.alias) {
     ElMessage.warning('请输入文件别名')
     return
   }
-  sourceFiles.value.push({
-    id: Date.now().toString(),
-    ...newSourceFile
-  })
-  showAddSourceDialog.value = false
-  Object.assign(newSourceFile, { alias: '', description: '', required: true })
-  ElMessage.success('添加成功')
+  try {
+    const sf = await templateStore.addSourceFile(templateId, newSourceFile)
+    sourceFiles.value.push(sf)
+    showAddSourceDialog.value = false
+    Object.assign(newSourceFile, { alias: '', description: '', required: true })
+    ElMessage.success('添加成功')
+  } catch (error) {
+    ElMessage.error('添加失败: ' + error.message)
+  }
 }
 
 function getCategoryIcon(category) {
@@ -470,35 +495,43 @@ function editVariable(variable) {
   showVariableDialog.value = true
 }
 
-function saveVariable() {
+async function saveVariable() {
   if (!variableForm.name || !variableForm.displayName) {
     ElMessage.warning('请填写必要字段')
     return
   }
 
-  if (editingVariable.value) {
-    // 更新
-    Object.assign(editingVariable.value, variableForm)
-    ElMessage.success('更新成功')
-  } else {
-    // 新增
-    variables.value.push({
-      id: Date.now().toString(),
-      ...variableForm
-    })
-    ElMessage.success('添加成功')
-  }
+  try {
+    if (editingVariable.value) {
+      // 更新
+      const updated = await templateStore.updateVariable(editingVariable.value.id, variableForm)
+      Object.assign(editingVariable.value, updated)
+      ElMessage.success('更新成功')
+    } else {
+      // 新增
+      const newVar = await templateStore.addVariable(templateId, variableForm)
+      variables.value.push(newVar)
+      ElMessage.success('添加成功')
+    }
 
-  showVariableDialog.value = false
-  resetVariableForm()
+    showVariableDialog.value = false
+    resetVariableForm()
+  } catch (error) {
+    ElMessage.error('保存失败: ' + error.message)
+  }
 }
 
-function deleteVariable() {
+async function deleteVariable() {
   if (editingVariable.value) {
-    variables.value = variables.value.filter(v => v.id !== editingVariable.value.id)
-    showVariableDialog.value = false
-    resetVariableForm()
-    ElMessage.success('删除成功')
+    try {
+      await templateStore.deleteVariable(editingVariable.value.id)
+      variables.value = variables.value.filter(v => v.id !== editingVariable.value.id)
+      showVariableDialog.value = false
+      resetVariableForm()
+      ElMessage.success('删除成功')
+    } catch (error) {
+      ElMessage.error('删除失败: ' + error.message)
+    }
   }
 }
 

+ 75 - 36
frontend/vue-demo/src/views/GenerationDetail.vue

@@ -123,34 +123,52 @@ import { ref, onMounted } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 import { ArrowLeft, View } from '@element-plus/icons-vue'
 import { ElMessage } from 'element-plus'
+import { generationApi } from '@/api'
 
 const router = useRouter()
 const route = useRoute()
 const generationId = route.params.id
 
-// Mock 数据
-const generation = ref({
-  id: generationId,
-  name: '智慧园区建设项目可行性研究报告',
-  templateName: '可行性研究报告',
-  status: 'review',
-  progress: 100,
-  createTime: '2026-01-20 10:30'
-})
+const loading = ref(false)
+
+// 从 API 获取数据
+const generation = ref(null)
+const sourceFileMap = ref({})
+const variableValues = ref([])
 
-const sourceFileMap = ref({
-  '可研批复': 'doc_123',
-  '项目建议书': 'doc_456',
-  '技术方案': 'doc_789'
+onMounted(async () => {
+  await fetchGenerationDetail()
 })
 
-const variableValues = ref([
-  { name: 'project_name', displayName: '项目名称', value: '智慧园区建设项目', confidence: 0.95, status: 'extracted' },
-  { name: 'total_investment', displayName: '总投资额', value: '5000万元', confidence: 0.88, status: 'extracted' },
-  { name: 'project_location', displayName: '项目地点', value: '华南科技园', confidence: 0.92, status: 'extracted' },
-  { name: 'tech_solution', displayName: '技术方案', value: '智能化管理平台', confidence: 0.78, status: 'low_confidence' },
-  { name: 'completion_date', displayName: '完成日期', value: '', confidence: 0, status: 'pending' }
-])
+async function fetchGenerationDetail() {
+  loading.value = true
+  try {
+    const data = await generationApi.getById(generationId)
+    generation.value = {
+      ...data,
+      templateName: data.templateName || '未知模板',
+      progress: data.status === 'completed' ? 100 : (data.status === 'extracting' ? 50 : 0)
+    }
+    
+    // 解析来源文件映射
+    sourceFileMap.value = data.sourceFileMap || {}
+    
+    // 解析提取结果
+    const extractedValues = data.extractedValues || {}
+    variableValues.value = Object.entries(extractedValues).map(([name, val]) => ({
+      name,
+      displayName: val.displayName || name,
+      value: val.value || '',
+      confidence: val.confidence || 0,
+      status: val.value ? 'extracted' : 'pending'
+    }))
+  } catch (error) {
+    console.error('获取生成任务详情失败:', error)
+    ElMessage.error('获取生成任务详情失败')
+  } finally {
+    loading.value = false
+  }
+}
 
 function getStatusType(status) {
   const map = {
@@ -206,27 +224,48 @@ function markAsModified(row) {
   row.status = 'modified'
 }
 
-function executeExtraction() {
-  generation.value.status = 'extracting'
-  generation.value.progress = 0
-
-  const interval = setInterval(() => {
-    generation.value.progress += 10
-    if (generation.value.progress >= 100) {
-      clearInterval(interval)
-      generation.value.status = 'review'
-      ElMessage.success('变量提取完成')
-    }
-  }, 500)
+async function executeExtraction() {
+  try {
+    generation.value.status = 'extracting'
+    generation.value.progress = 0
+    
+    await generationApi.execute(generationId)
+    
+    // 轮询进度
+    const interval = setInterval(async () => {
+      try {
+        const progress = await generationApi.getProgress(generationId)
+        generation.value.progress = progress.progress || 0
+        generation.value.status = progress.status
+        
+        if (progress.status === 'review' || progress.status === 'completed' || progress.status === 'error') {
+          clearInterval(interval)
+          await fetchGenerationDetail() // 重新获取完整数据
+          ElMessage.success('变量提取完成')
+        }
+      } catch {
+        clearInterval(interval)
+      }
+    }, 2000)
+  } catch (error) {
+    ElMessage.error('执行失败: ' + error.message)
+    generation.value.status = 'error'
+  }
 }
 
-function confirmGeneration() {
-  generation.value.status = 'completed'
-  ElMessage.success('文档生成成功')
+async function confirmGeneration() {
+  try {
+    await generationApi.confirm(generationId)
+    generation.value.status = 'completed'
+    ElMessage.success('文档生成成功')
+  } catch (error) {
+    ElMessage.error('生成失败: ' + error.message)
+  }
 }
 
 function downloadDocument() {
-  ElMessage.info('下载功能开发中...')
+  const url = generationApi.getDownloadUrl(generationId)
+  window.open(url, '_blank')
 }
 </script>
 

+ 77 - 76
frontend/vue-demo/src/views/Generations.vue

@@ -141,7 +141,7 @@ import { ref, reactive, computed, onMounted } from 'vue'
 import { useRouter } from 'vue-router'
 import { Plus, Upload } from '@element-plus/icons-vue'
 import { ElMessage } from 'element-plus'
-import { generationApi } from '@/api'
+import { generationApi, templateApi, sourceFileApi } from '@/api'
 
 const router = useRouter()
 
@@ -152,65 +152,50 @@ const searchKeyword = ref('')
 const timeFilter = ref('all')
 const showCreateDialog = ref(false)
 
-// Mock 数据
-const generations = ref([
-  {
-    id: '1',
-    name: '智慧园区建设项目可行性研究报告',
-    templateName: '可行性研究报告',
-    status: 'completed',
-    progress: 100,
-    variableCount: 12,
-    createTime: '2026-01-20 10:30'
-  },
-  {
-    id: '2',
-    name: 'Q4市场分析报告',
-    templateName: '市场分析报告',
-    status: 'review',
-    progress: 100,
-    variableCount: 8,
-    createTime: '2026-01-19 14:20'
-  },
-  {
-    id: '3',
-    name: '新能源项目可研报告',
-    templateName: '可行性研究报告',
-    status: 'extracting',
-    progress: 65,
-    variableCount: 15,
-    createTime: '2026-01-20 16:45'
-  },
-  {
-    id: '4',
-    name: '2026年度预算报告',
-    templateName: '预算报告',
-    status: 'pending',
-    progress: 0,
-    variableCount: 20,
-    createTime: '2026-01-20 09:00'
+// 从 API 获取数据
+const generations = ref([])
+const templates = ref([])
+
+// 加载数据
+onMounted(async () => {
+  await Promise.all([fetchGenerations(), fetchTemplates()])
+})
+
+async function fetchGenerations() {
+  loading.value = true
+  try {
+    const data = await generationApi.list()
+    generations.value = (data.records || data || []).map(g => ({
+      ...g,
+      templateName: g.templateName || '未知模板',
+      variableCount: g.extractedValues ? Object.keys(g.extractedValues).length : 0,
+      progress: g.status === 'completed' ? 100 : (g.status === 'extracting' ? 50 : 0)
+    }))
+  } catch (error) {
+    console.error('获取生成任务失败:', error)
+    ElMessage.error('获取生成任务列表失败')
+  } finally {
+    loading.value = false
   }
-])
-
-const templates = ref([
-  {
-    id: '1',
-    name: '可行性研究报告',
-    sourceFiles: [
-      { id: '1', alias: '可研批复', required: true },
-      { id: '2', alias: '项目建议书', required: true },
-      { id: '3', alias: '技术方案', required: false }
-    ]
-  },
-  {
-    id: '2',
-    name: '市场分析报告',
-    sourceFiles: [
-      { id: '1', alias: '市场数据', required: true },
-      { id: '2', alias: '竞品分析', required: false }
-    ]
+}
+
+async function fetchTemplates() {
+  try {
+    const data = await templateApi.list()
+    const templateList = data.records || data || []
+    // 获取每个模板的来源文件
+    templates.value = await Promise.all(templateList.map(async (t) => {
+      try {
+        const sourceFiles = await sourceFileApi.list(t.id)
+        return { ...t, sourceFiles: sourceFiles || [] }
+      } catch {
+        return { ...t, sourceFiles: [] }
+      }
+    }))
+  } catch (error) {
+    console.error('获取模板列表失败:', error)
   }
-])
+}
 
 const newGeneration = reactive({
   name: '',
@@ -269,32 +254,42 @@ async function executeGeneration(gen) {
     gen.status = 'extracting'
     gen.progress = 0
     
-    // 模拟进度
-    const interval = setInterval(() => {
-      gen.progress += 10
-      if (gen.progress >= 100) {
+    await generationApi.execute(gen.id)
+    
+    // 轮询进度
+    const interval = setInterval(async () => {
+      try {
+        const progress = await generationApi.getProgress(gen.id)
+        gen.progress = progress.progress || 0
+        gen.status = progress.status
+        if (progress.status === 'review' || progress.status === 'completed' || progress.status === 'error') {
+          clearInterval(interval)
+        }
+      } catch {
         clearInterval(interval)
-        gen.status = 'review'
       }
-    }, 500)
+    }, 2000)
     
     ElMessage.success('开始提取变量')
   } catch (error) {
-    ElMessage.error('执行失败')
+    ElMessage.error('执行失败: ' + error.message)
+    gen.status = 'error'
   }
 }
 
 async function confirmGeneration(gen) {
   try {
+    await generationApi.confirm(gen.id)
     gen.status = 'completed'
     ElMessage.success('文档生成成功')
   } catch (error) {
-    ElMessage.error('生成失败')
+    ElMessage.error('生成失败: ' + error.message)
   }
 }
 
 function downloadGeneration(gen) {
-  ElMessage.info('下载功能开发中...')
+  const url = generationApi.getDownloadUrl(gen.id)
+  window.open(url, '_blank')
 }
 
 function handleSourceFileUpload(alias, response) {
@@ -312,7 +307,7 @@ async function createGeneration() {
 
   // 检查必需文件
   const template = selectedTemplate.value
-  const missingFiles = template.sourceFiles
+  const missingFiles = (template.sourceFiles || [])
     .filter(sf => sf.required && !newGeneration.sourceFileMap[sf.alias])
     .map(sf => sf.alias)
 
@@ -323,16 +318,22 @@ async function createGeneration() {
 
   creating.value = true
   try {
-    const gen = {
-      id: Date.now().toString(),
+    const data = {
+      templateId: newGeneration.templateId,
       name: newGeneration.name || template.name,
-      templateName: template.name,
-      status: 'pending',
-      progress: 0,
-      variableCount: 10,
-      createTime: new Date().toLocaleString()
+      sourceFileMap: newGeneration.sourceFileMap
     }
-    generations.value.unshift(gen)
+    
+    const gen = await generationApi.create(data)
+    
+    // 添加到列表
+    generations.value.unshift({
+      ...gen,
+      templateName: template.name,
+      variableCount: 0,
+      progress: 0
+    })
+    
     showCreateDialog.value = false
     ElMessage.success('生成任务创建成功')
     

+ 30 - 21
frontend/vue-demo/src/views/TemplateDetail.vue

@@ -98,34 +98,43 @@ import { ref, onMounted } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 import { ArrowLeft } from '@element-plus/icons-vue'
 import { ElMessage } from 'element-plus'
+import { templateApi, sourceFileApi, variableApi } from '@/api'
 
 const router = useRouter()
 const route = useRoute()
 const templateId = route.params.id
 
-// Mock 数据
-const template = ref({
-  id: templateId,
-  name: '可行性研究报告',
-  status: 'published',
-  useCount: 96,
-  isPublic: true,
-  createTime: '2026-01-15 10:00',
-  description: '适用于各类项目可行性研究报告的生成,包含项目概述、技术方案、投资估算等章节。'
+const loading = ref(false)
+
+// 从 API 获取数据
+const template = ref(null)
+const sourceFiles = ref([])
+const variables = ref([])
+
+onMounted(async () => {
+  await fetchTemplateDetail()
 })
 
-const sourceFiles = ref([
-  { id: '1', alias: '可研批复', description: '项目可研批复文件', required: true },
-  { id: '2', alias: '项目建议书', description: '项目建议书', required: true },
-  { id: '3', alias: '技术方案', description: '技术方案说明', required: false }
-])
-
-const variables = ref([
-  { id: '1', name: 'project_name', displayName: '项目名称', category: 'entity', sourceType: 'document', exampleValue: '智慧园区建设项目' },
-  { id: '2', name: 'total_investment', displayName: '总投资额', category: 'data', sourceType: 'document', exampleValue: '5000万元' },
-  { id: '3', name: 'project_location', displayName: '项目地点', category: 'location', sourceType: 'document', exampleValue: '华南科技园' },
-  { id: '4', name: 'tech_solution', displayName: '技术方案', category: 'concept', sourceType: 'document', exampleValue: '智能化管理平台' }
-])
+async function fetchTemplateDetail() {
+  loading.value = true
+  try {
+    // 并行获取模板详情、来源文件和变量
+    const [templateData, sourceFilesData, variablesData] = await Promise.all([
+      templateApi.getById(templateId),
+      sourceFileApi.list(templateId),
+      variableApi.list(templateId)
+    ])
+    
+    template.value = templateData
+    sourceFiles.value = sourceFilesData || []
+    variables.value = variablesData || []
+  } catch (error) {
+    console.error('获取模板详情失败:', error)
+    ElMessage.error('获取模板详情失败')
+  } finally {
+    loading.value = false
+  }
+}
 
 function getStatusType(status) {
   const map = { draft: 'info', published: 'success', archived: 'warning' }

+ 1 - 1
frontend/vue-demo/vite.config.js

@@ -7,7 +7,7 @@ import vue from '@vitejs/plugin-vue'
 // 
 // 修改下方 API_SERVER 变量即可切换
 
-const API_SERVER = process.env.API_SERVER || 'http://47.101.133.94:18520'
+const API_SERVER = process.env.API_SERVER || 'http://192.168.2.3:18520'
 
 export default defineConfig({
   plugins: [vue()],