Quellcode durchsuchen

增加pdf上传功能

xueyiming vor 1 Monat
Ursprung
Commit
e1d910c29b
1 geänderte Dateien mit 216 neuen und 117 gelöschten Zeilen
  1. 216 117
      src/views/KnowledgeContent.vue

+ 216 - 117
src/views/KnowledgeContent.vue

@@ -143,8 +143,17 @@
         @close="resetForm"
     >
       <div class="dialog-content">
-        <el-form :model="formData" ref="formRef" label-width="100px">
-          <el-form-item label="📝 文档标题" :rules="[{ required: true, message: '请输入标题', trigger: 'blur' }]">
+        <!-- 输入方式切换 -->
+        <div class="input-mode-switch">
+          <el-radio-group v-model="inputMode" size="large" @change="handleModeChange">
+            <el-radio-button label="text">📝 文本输入</el-radio-button>
+            <el-radio-button label="pdf">📄 PDF上传</el-radio-button>
+          </el-radio-group>
+        </div>
+
+        <el-form :model="formData" ref="formRef" label-width="140px">
+          <el-form-item v-if="inputMode === 'text'" label="📝 文档标题"
+                        :rules="[{ required: inputMode === 'text', message: '请输入标题', trigger: 'blur' }]">
             <el-input
                 v-model="formData.title"
                 placeholder="请输入文档标题"
@@ -152,7 +161,12 @@
             ></el-input>
           </el-form-item>
 
-          <el-form-item label="📄 文档内容" :rules="[{ required: true, message: '请输入文本内容', trigger: 'blur' }]">
+          <!-- 文本输入模式 -->
+          <el-form-item
+              v-if="inputMode === 'text'"
+              label="📄 文档内容"
+              :rules="[{ required: inputMode === 'text', message: '请输入文本内容', trigger: 'blur' }]"
+          >
             <el-input
                 type="textarea"
                 v-model="formData.text"
@@ -162,7 +176,43 @@
             ></el-input>
           </el-form-item>
 
-          <el-form-item label="🔗 是否分块">
+          <!-- PDF上传模式 -->
+          <el-form-item
+              v-else
+              label="📄 PDF文件"
+              :rules="[{ required: inputMode === 'pdf', message: '请选择PDF文件', trigger: 'change' }]"
+          >
+            <div class="upload-area">
+              <el-upload
+                  ref="uploadRef"
+                  class="pdf-upload"
+                  :action="uploadAction"
+                  :file-list="fileList"
+                  :before-upload="beforeUpload"
+                  :on-success="handleUploadSuccess"
+                  :limit="100"
+                  accept=".pdf"
+                  :auto-upload="true"
+              >
+                <template #trigger>
+                  <div class="upload-trigger">
+                    <div class="upload-icon">📎</div>
+                    <div class="upload-text">
+                      <p class="upload-title">点击选择PDF文件</p>
+                      <!--                      <p class="upload-subtitle">或拖拽文件到此处</p>-->
+                    </div>
+                  </div>
+                </template>
+                <template #tip>
+                  <div class="upload-tip">
+                    支持上传单个PDF文件,大小不超过100MB
+                  </div>
+                </template>
+              </el-upload>
+            </div>
+          </el-form-item>
+
+          <el-form-item v-if="inputMode === 'text'" label="🔗 是否分块">
             <el-switch
                 v-model="formData.isChunk"
                 active-text="启用分块"
@@ -173,7 +223,7 @@
       </div>
 
       <template #footer>
-        <div class="dialog-footer">
+        <div v-if="inputMode === 'text'" class="dialog-footer">
           <el-button
               class="action-btn secondary"
               @click="dialogVisible = false"
@@ -183,9 +233,10 @@
           <el-button
               type="primary"
               class="action-btn primary"
+              :loading="submitting"
               @click="submitData"
           >
-            💾 保存文档
+            💾 {{ submitting ? '提交中...' : '保存文档' }}
           </el-button>
         </div>
       </template>
@@ -304,6 +355,7 @@ import {defineComponent, onMounted, ref, computed} from 'vue';
 import {useRouter} from 'vue-router';
 import axios from 'axios';
 import {ElMessage, ElMessageBox} from 'element-plus';
+import type {UploadProps, UploadUserFile, UploadInstance} from 'element-plus';
 
 interface Item {
   doc_id: string;
@@ -330,13 +382,23 @@ export default defineComponent({
 
     const allItems = ref<Item[]>([]);
 
+    // 输入模式:text 或 pdf
+    const inputMode = ref('text');
+    const uploadRef = ref<UploadInstance>();
+    const fileList = ref<UploadUserFile[]>([]);
+
     // 新知识表单数据
     const formData = ref({
       title: '',
       text: '',
-      isChunk: true
+      isChunk: true,
+      fileId: '' // 用于存储上传后的文件ID
     });
 
+    // 上传相关配置
+    const uploadAction = `${API_BASE_URL}/upload/file`;
+
+
     const dialogVisible = ref(false);
     const formRef = ref();
 
@@ -425,6 +487,29 @@ export default defineComponent({
       formData.value.text = '';
     };
 
+    // 文件上传前的验证
+    const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
+      if (rawFile.type !== 'application/pdf') {
+        ElMessage.error('只能上传PDF文件!');
+        return false;
+      }
+      if (rawFile.size / 1024 / 1024 > 100) {
+        ElMessage.error('文件大小不能超过100MB!');
+        return false;
+      }
+      return true;
+    };
+
+    // 文件上传成功处理
+    const handleUploadSuccess: UploadProps['onSuccess'] = (response, uploadFile) => {
+      // 根据实际返回数据结构判断
+      if (response.status_code === 200) {
+        ElMessage.success('文件上传成功!');
+      } else {
+        ElMessage.error(response.detail || '文件上传失败');
+      }
+    };
+
     // 提交表单数据
     const submitData = async () => {
       if (!formData.value.title || !formData.value.text) {
@@ -560,21 +645,27 @@ export default defineComponent({
       statusDesc,
       formRef,
       goBack,
-      handleSizeChange
+      handleSizeChange,
+      // 新增的上传相关
+      inputMode,
+      uploadRef,
+      fileList,
+      uploadAction,
+      beforeUpload,
+      handleUploadSuccess,
     };
   },
 });
 </script>
 
 <style scoped>
-/* 基础样式 */
+/* 基础样式 - 保持不变 */
 .app-container {
   min-height: 100vh;
   background: linear-gradient(135deg, rgba(102, 126, 234, 0.6) 0%, rgba(118, 75, 162, 0.4) 100%);
   font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
 }
 
-/* 顶部导航样式 */
 .app-header {
   background: rgba(255, 255, 255, 0.1);
   backdrop-filter: blur(20px);
@@ -620,12 +711,6 @@ export default defineComponent({
   box-shadow: 0 8px 25px rgba(255, 255, 255, 0.2);
 }
 
-.back-icon {
-  color: white;
-  margin-right: 6px;
-  font-weight: bold;
-}
-
 .header-titles {
   flex: 1;
 }
@@ -795,25 +880,6 @@ export default defineComponent({
   white-space: nowrap;
 }
 
-.document-status {
-  font-size: 0.75rem;
-  font-weight: 500;
-  padding: 2px 8px;
-  border-radius: 8px;
-}
-
-.document-status.available {
-  background: #f0fff4;
-  color: #2f855a;
-  border: 1px solid #c6f6d5;
-}
-
-.document-status.unavailable {
-  background: #fed7d7;
-  color: #c53030;
-  border: 1px solid #feb2b2;
-}
-
 .document-actions {
   display: flex;
   align-items: center;
@@ -853,10 +919,6 @@ export default defineComponent({
   margin: 0;
 }
 
-.custom-pagination :deep(.el-pagination) {
-  justify-content: flex-end;
-}
-
 /* 主内容区样式 */
 .content-area {
   flex: 1;
@@ -984,9 +1046,8 @@ export default defineComponent({
 }
 
 .content-dialog :deep(.el-dialog__header) {
-  padding: 0;
+  padding: 24px 24px 0;
   margin: 0;
-  border-bottom: 1px solid #e2e8f0;
 }
 
 .content-dialog :deep(.el-dialog__headerbtn) {
@@ -1009,7 +1070,7 @@ export default defineComponent({
 }
 
 .dialog-content {
-  padding: 0;
+  padding: 0 24px;
 }
 
 .content-dialog :deep(.el-form-item__label) {
@@ -1037,7 +1098,7 @@ export default defineComponent({
 }
 
 .dialog-footer {
-  padding: 0;
+  padding: 0 24px 24px;
   margin-top: 24px;
   display: flex;
   justify-content: flex-end;
@@ -1075,7 +1136,105 @@ export default defineComponent({
   box-shadow: 0 4px 12px rgba(66, 153, 225, 0.3);
 }
 
-/* 分段弹窗样式 */
+/* 新增的上传相关样式 */
+.input-mode-switch {
+  margin-bottom: 24px;
+  text-align: center;
+}
+
+.input-mode-switch :deep(.el-radio-group) {
+  width: 100%;
+}
+
+.input-mode-switch :deep(.el-radio-button) {
+  width: 50%;
+}
+
+.input-mode-switch :deep(.el-radio-button__inner) {
+  width: 100%;
+  padding: 12px 20px;
+  font-size: 14px;
+  font-weight: 500;
+  border: none;
+}
+
+.upload-area {
+  width: 100%;
+}
+
+.pdf-upload {
+  width: 100%;
+}
+
+.pdf-upload :deep(.el-upload) {
+  width: 100%;
+}
+
+.pdf-upload :deep(.el-upload-dragger) {
+  width: 100%;
+  height: 160px;
+  border: 2px dashed #e2e8f0;
+  border-radius: 12px;
+  background: #fafbfc;
+  transition: all 0.3s ease;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+}
+
+.pdf-upload :deep(.el-upload-dragger:hover) {
+  border-color: #4299e1;
+  background: #f0f9ff;
+}
+
+.upload-trigger {
+  text-align: center;
+  padding: 20px;
+}
+
+.upload-icon {
+  font-size: 2.5rem;
+  margin-bottom: 12px;
+  opacity: 0.7;
+}
+
+.upload-title {
+  font-size: 1.1rem;
+  font-weight: 600;
+  color: #2d3748;
+  margin: 0 0 6px 0;
+}
+
+.upload-subtitle {
+  font-size: 0.9rem;
+  color: #718096;
+  margin: 0;
+}
+
+.upload-tip {
+  margin-top: 12px;
+  text-align: center;
+  color: #a0aec0;
+  font-size: 0.85rem;
+}
+
+/* 文件列表样式 */
+.pdf-upload :deep(.el-upload-list) {
+  margin-top: 16px;
+}
+
+.pdf-upload :deep(.el-upload-list__item) {
+  border-radius: 8px;
+  margin-bottom: 8px;
+  transition: all 0.3s ease;
+}
+
+.pdf-upload :deep(.el-upload-list__item:hover) {
+  background-color: #f7fafc;
+}
+
+/* 分段弹窗样式 - 保持不变 */
 .chunk-content {
   padding: 0;
 }
@@ -1209,6 +1368,19 @@ export default defineComponent({
   .add-btn {
     width: 100%;
   }
+
+  .input-mode-switch :deep(.el-radio-button__inner) {
+    padding: 10px 16px;
+    font-size: 13px;
+  }
+
+  .upload-trigger {
+    padding: 16px;
+  }
+
+  .upload-icon {
+    font-size: 2rem;
+  }
 }
 
 @media (max-width: 480px) {
@@ -1331,12 +1503,6 @@ export default defineComponent({
   gap: 20px;
 }
 
-.stat-item {
-  color: #64748b;
-  font-size: 0.9rem;
-  font-weight: 500;
-}
-
 .chunk-content {
   flex: 1;
   overflow: auto;
@@ -1397,25 +1563,6 @@ export default defineComponent({
   padding-right: 8px;
 }
 
-/* 自定义滚动条 */
-.full-content::-webkit-scrollbar {
-  width: 4px;
-}
-
-.full-content::-webkit-scrollbar-track {
-  background: #f1f5f9;
-  border-radius: 2px;
-}
-
-.full-content::-webkit-scrollbar-thumb {
-  background: #cbd5e1;
-  border-radius: 2px;
-}
-
-.full-content::-webkit-scrollbar-thumb:hover {
-  background: #94a3b8;
-}
-
 .status-tag {
   border: none;
   font-weight: 600;
@@ -1423,33 +1570,6 @@ export default defineComponent({
   border-radius: 20px;
 }
 
-.empty-chunks {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  height: 300px;
-  color: #64748b;
-}
-
-.empty-icon {
-  font-size: 4rem;
-  margin-bottom: 16px;
-  opacity: 0.5;
-}
-
-.empty-chunks h3 {
-  margin: 0 0 8px 0;
-  color: #475569;
-  font-size: 1.25rem;
-  font-weight: 600;
-}
-
-.empty-chunks p {
-  margin: 0;
-  font-size: 0.9rem;
-}
-
 .pagination-section {
   padding: 20px 24px;
   background: #f8fafc;
@@ -1461,25 +1581,4 @@ export default defineComponent({
 .pagination-section :deep(.el-pagination) {
   justify-content: center;
 }
-
-.pagination-section :deep(.el-pagination .btn-prev),
-.pagination-section :deep(.el-pagination .btn-next) {
-  border-radius: 8px;
-  border: 1px solid #d1d5db;
-}
-
-.pagination-section :deep(.el-pagination .number) {
-  border-radius: 8px;
-  border: 1px solid #d1d5db;
-}
-
-.pagination-section :deep(.el-pagination .number.active) {
-  background: #3b82f6;
-  border-color: #3b82f6;
-  color: white;
-}
-
-.pagination-section :deep(.el-pagination .el-select .el-input) {
-  border-radius: 8px;
-}
 </style>