Parcourir la source

refactor(editor): 新建报告改为对话框形式

- 移除新建报告的 popover 下拉菜单
- 新增完整的新建报告对话框
- 对话框包含创建方式选择(空白报告/上传文件)
- 支持输入报告名称
- 上传模式支持拖拽上传 PDF/Word 文件
- 创建完成后自动切换到新报告
何文松 il y a 3 semaines
Parent
commit
40a7e7bbea
1 fichiers modifiés avec 323 ajouts et 110 suppressions
  1. 323 110
      frontend/vue-demo/src/views/Editor.vue

+ 323 - 110
frontend/vue-demo/src/views/Editor.vue

@@ -40,39 +40,15 @@
 
         <!-- 我的报告面板 -->
         <div class="panel-body reports-panel" v-show="leftPanelTab === 'reports'">
-          <!-- 新建报告按钮(带下拉菜单) -->
-          <el-popover
-            placement="bottom-start"
-            :width="240"
-            trigger="click"
-            popper-class="new-report-popover"
+          <!-- 新建报告按钮 -->
+          <el-button 
+            class="new-report-btn" 
+            type="primary" 
+            :icon="Plus"
+            @click="showNewReportDialog = true"
           >
-            <template #reference>
-              <el-button 
-                class="new-report-btn" 
-                type="primary" 
-                :icon="Plus"
-              >
-                新建报告
-              </el-button>
-            </template>
-            <div class="new-report-menu">
-              <div class="menu-item" @click="handleCreateReport">
-                <div class="menu-icon">📄</div>
-                <div class="menu-content">
-                  <div class="menu-title">创建空白报告</div>
-                  <div class="menu-desc">从零开始创建新报告</div>
-                </div>
-              </div>
-              <div class="menu-item" @click="handleUploadFile">
-                <div class="menu-icon">📁</div>
-                <div class="menu-content">
-                  <div class="menu-title">上传文件创建</div>
-                  <div class="menu-desc">上传 PDF/Word 自动解析</div>
-                </div>
-              </div>
-            </div>
-          </el-popover>
+            新建报告
+          </el-button>
 
           <!-- 报告搜索 -->
           <el-input
@@ -420,6 +396,91 @@
       </div>
     </div>
 
+    <!-- 新建报告对话框 -->
+    <el-dialog 
+      v-model="showNewReportDialog" 
+      title="新建报告" 
+      width="460"
+      :close-on-click-modal="false"
+      class="new-report-dialog"
+    >
+      <div class="new-report-form">
+        <!-- 创建方式选择 -->
+        <div class="create-type-section">
+          <div class="section-label">创建方式</div>
+          <div class="create-type-options">
+            <div 
+              class="type-option" 
+              :class="{ active: newReportType === 'blank' }"
+              @click="newReportType = 'blank'"
+            >
+              <div class="option-icon">📄</div>
+              <div class="option-content">
+                <div class="option-title">空白报告</div>
+                <div class="option-desc">从零开始创建</div>
+              </div>
+              <div class="option-check" v-if="newReportType === 'blank'">✓</div>
+            </div>
+            <div 
+              class="type-option" 
+              :class="{ active: newReportType === 'upload' }"
+              @click="newReportType = 'upload'"
+            >
+              <div class="option-icon">📁</div>
+              <div class="option-content">
+                <div class="option-title">上传文件</div>
+                <div class="option-desc">PDF/Word 自动解析</div>
+              </div>
+              <div class="option-check" v-if="newReportType === 'upload'">✓</div>
+            </div>
+          </div>
+        </div>
+        
+        <!-- 报告名称输入 -->
+        <div class="name-input-section">
+          <div class="section-label">报告名称</div>
+          <el-input 
+            v-model="newReportName" 
+            placeholder="请输入报告名称"
+            maxlength="50"
+            show-word-limit
+          />
+        </div>
+        
+        <!-- 文件上传区域(仅上传方式显示) -->
+        <div class="upload-section" v-if="newReportType === 'upload'">
+          <div class="section-label">上传文件</div>
+          <el-upload
+            ref="newReportUploadRef"
+            class="report-upload-area"
+            drag
+            :auto-upload="false"
+            :limit="1"
+            :on-change="handleNewReportFileChange"
+            :on-exceed="handleNewReportFileExceed"
+            accept=".pdf,.doc,.docx"
+          >
+            <div class="upload-content">
+              <el-icon class="upload-icon"><Upload /></el-icon>
+              <div class="upload-text">拖拽文件到此处,或<em>点击上传</em></div>
+              <div class="upload-hint">支持 PDF、Word 文档</div>
+            </div>
+          </el-upload>
+        </div>
+      </div>
+      <template #footer>
+        <el-button @click="showNewReportDialog = false">取消</el-button>
+        <el-button 
+          type="primary" 
+          @click="handleConfirmCreateReport"
+          :disabled="!newReportName.trim()"
+          :loading="creatingReport"
+        >
+          创建
+        </el-button>
+      </template>
+    </el-dialog>
+
     <!-- 添加来源文件对话框 -->
     <el-dialog v-model="showAddSourceDialog" title="添加来源文件定义" width="400">
       <el-form :model="newSourceFile" label-width="80px">
@@ -577,7 +638,7 @@
 import { ref, reactive, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 import {
-  ArrowLeft, Clock, Share, Check, Plus, Delete, Connection, Refresh, Search, Loading
+  ArrowLeft, Clock, Share, Check, Plus, Delete, Connection, Refresh, Search, Loading, Upload
 } from '@element-plus/icons-vue'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { useTemplateStore } from '@/stores/template'
@@ -810,50 +871,74 @@ async function loadDocumentById(documentId) {
   }
 }
 
-// 新建报告
-async function handleCreateReport() {
+// 新建报告对话框 - 文件选择变化
+function handleNewReportFileChange(file) {
+  newReportFile.value = file.raw
+}
+
+// 新建报告对话框 - 文件超出限制
+function handleNewReportFileExceed() {
+  ElMessage.warning('只能上传一个文件')
+}
+
+// 新建报告对话框 - 重置
+function resetNewReportDialog() {
+  newReportType.value = 'blank'
+  newReportName.value = ''
+  newReportFile.value = null
+  if (newReportUploadRef.value) {
+    newReportUploadRef.value.clearFiles()
+  }
+}
+
+// 新建报告对话框 - 确认创建
+async function handleConfirmCreateReport() {
+  if (!newReportName.value.trim()) {
+    ElMessage.warning('请输入报告名称')
+    return
+  }
+  
+  creatingReport.value = true
   try {
-    const { value: reportName } = await ElMessageBox.prompt('请输入报告名称', '新建报告', {
-      confirmButtonText: '创建',
-      cancelButtonText: '取消',
-      inputPattern: /\S+/,
-      inputErrorMessage: '报告名称不能为空'
-    })
+    // 先创建报告
+    const newReport = await templateApi.create({ name: newReportName.value.trim() })
     
-    if (reportName) {
-      const newReport = await templateApi.create({ name: reportName })
+    // 如果是上传模式且有文件,上传文件
+    if (newReportType.value === 'upload' && newReportFile.value) {
+      try {
+        await sourceFileApi.upload(newReport.id, newReportFile.value)
+        ElMessage.success('报告创建成功,文件上传中...')
+      } catch (uploadError) {
+        console.error('文件上传失败:', uploadError)
+        ElMessage.warning('报告已创建,但文件上传失败')
+      }
+    } else {
       ElMessage.success('报告创建成功')
-      // 刷新报告列表
-      await loadMyReports()
-      // 如果新报告有文档,切换到该报告
-      if (newReport && newReport.baseDocumentId) {
-        await switchReport(newReport)
+    }
+    
+    // 关闭对话框并重置
+    showNewReportDialog.value = false
+    resetNewReportDialog()
+    
+    // 刷新报告列表
+    await loadMyReports()
+    
+    // 切换到新报告
+    if (newReport) {
+      await switchReport(newReport)
+      // 如果是上传模式,切换到资源 Tab
+      if (newReportType.value === 'upload') {
+        leftPanelTab.value = 'files'
       }
     }
   } catch (error) {
-    if (error !== 'cancel') {
-      console.error('创建报告失败:', error)
-      ElMessage.error('创建报告失败')
-    }
-  }
-}
-
-// 上传文件入口
-function handleUploadFile() {
-  // 先选择一个报告,然后切换到资源 Tab
-  if (myReports.value.length > 0) {
-    leftPanelTab.value = 'reports'
-    ElMessage.info('请先选择一个报告,然后在资源面板上传文件')
-  } else {
-    ElMessage.info('请先创建一个报告')
+    console.error('创建报告失败:', error)
+    ElMessage.error('创建报告失败')
+  } finally {
+    creatingReport.value = false
   }
 }
 
-// AI 辅助入口
-function handleAiAssist() {
-  ElMessage.info('AI 辅助功能开发中...')
-}
-
 // 格式化报告时间
 function formatReportTime(dateStr) {
   if (!dateStr) return ''
@@ -884,6 +969,14 @@ function getReportStatusText(status) {
   return statusMap[status] || '草稿'
 }
 
+// 新建报告对话框
+const showNewReportDialog = ref(false)
+const newReportType = ref('blank')  // 'blank' | 'upload'
+const newReportName = ref('')
+const newReportFile = ref(null)
+const newReportUploadRef = ref(null)
+const creatingReport = ref(false)
+
 // 来源文件(从 API 获取)
 const sourceFiles = ref([])
 const selectedFile = ref(null)
@@ -2762,48 +2855,6 @@ onUnmounted(() => {
     }
   }
   
-  // 新建报告下拉菜单样式
-  .new-report-menu {
-    padding: 4px 0;
-    
-    .menu-item {
-      display: flex;
-      align-items: flex-start;
-      gap: 12px;
-      padding: 12px 14px;
-      cursor: pointer;
-      border-radius: var(--radius-sm);
-      transition: all 0.2s;
-      
-      &:hover {
-        background: var(--bg);
-      }
-      
-      .menu-icon {
-        font-size: 24px;
-        flex-shrink: 0;
-        line-height: 1;
-      }
-      
-      .menu-content {
-        flex: 1;
-        min-width: 0;
-      }
-      
-      .menu-title {
-        font-size: 14px;
-        font-weight: 500;
-        color: var(--text-1);
-        margin-bottom: 4px;
-      }
-      
-      .menu-desc {
-        font-size: 12px;
-        color: var(--text-3);
-        line-height: 1.4;
-      }
-    }
-  }
   
   .report-search {
     :deep(.el-input__wrapper) {
@@ -4364,4 +4415,166 @@ onUnmounted(() => {
     box-shadow: none;
   }
 }
+
+// ==========================================
+// 新建报告对话框样式
+// ==========================================
+.new-report-dialog {
+  :deep(.el-dialog__header) {
+    padding: 16px 20px;
+    border-bottom: 1px solid var(--border);
+    margin-right: 0;
+  }
+  
+  :deep(.el-dialog__body) {
+    padding: 20px;
+  }
+  
+  :deep(.el-dialog__footer) {
+    padding: 12px 20px;
+    border-top: 1px solid var(--border);
+  }
+}
+
+.new-report-form {
+  .section-label {
+    font-size: 13px;
+    font-weight: 500;
+    color: var(--text-2);
+    margin-bottom: 10px;
+  }
+  
+  .create-type-section {
+    margin-bottom: 20px;
+  }
+  
+  .create-type-options {
+    display: flex;
+    gap: 12px;
+  }
+  
+  .type-option {
+    flex: 1;
+    display: flex;
+    align-items: flex-start;
+    gap: 12px;
+    padding: 14px;
+    background: var(--bg);
+    border: 2px solid var(--border);
+    border-radius: var(--radius-md);
+    cursor: pointer;
+    transition: all 0.2s;
+    position: relative;
+    
+    &:hover {
+      border-color: var(--primary-light);
+      background: var(--white);
+    }
+    
+    &.active {
+      border-color: var(--primary);
+      background: var(--primary-light);
+      
+      .option-title {
+        color: var(--primary);
+      }
+    }
+    
+    .option-icon {
+      font-size: 24px;
+      flex-shrink: 0;
+      line-height: 1;
+    }
+    
+    .option-content {
+      flex: 1;
+      min-width: 0;
+    }
+    
+    .option-title {
+      font-size: 14px;
+      font-weight: 600;
+      color: var(--text-1);
+      margin-bottom: 4px;
+    }
+    
+    .option-desc {
+      font-size: 12px;
+      color: var(--text-3);
+      line-height: 1.4;
+    }
+    
+    .option-check {
+      position: absolute;
+      top: 8px;
+      right: 8px;
+      width: 20px;
+      height: 20px;
+      background: var(--primary);
+      color: white;
+      border-radius: 50%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      font-size: 12px;
+      font-weight: bold;
+    }
+  }
+  
+  .name-input-section {
+    margin-bottom: 20px;
+    
+    :deep(.el-input__wrapper) {
+      border-radius: var(--radius-sm);
+    }
+  }
+  
+  .upload-section {
+    .report-upload-area {
+      :deep(.el-upload) {
+        width: 100%;
+      }
+      
+      :deep(.el-upload-dragger) {
+        width: 100%;
+        height: auto;
+        padding: 24px;
+        border-radius: var(--radius-md);
+        border: 2px dashed var(--border);
+        background: var(--bg);
+        
+        &:hover {
+          border-color: var(--primary);
+          background: var(--primary-light);
+        }
+      }
+      
+      .upload-content {
+        text-align: center;
+      }
+      
+      .upload-icon {
+        font-size: 32px;
+        color: var(--text-3);
+        margin-bottom: 8px;
+      }
+      
+      .upload-text {
+        font-size: 14px;
+        color: var(--text-2);
+        margin-bottom: 4px;
+        
+        em {
+          color: var(--primary);
+          font-style: normal;
+        }
+      }
+      
+      .upload-hint {
+        font-size: 12px;
+        color: var(--text-3);
+      }
+    }
+  }
+}
 </style>