Browse Source

批量处理模版替换

yangg 7 tháng trước cách đây
mục cha
commit
017405680f

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/index.html


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/static/css/app.5452a1fa.css


+ 1 - 0
dist/static/css/chunk-079de15b.a39facf0.css

@@ -0,0 +1 @@
+.header[data-v-7e826c04]{margin-left:20px}.header h2[data-v-7e826c04]{margin-bottom:0}.center[data-v-7e826c04]{margin:10px}.center[data-v-7e826c04] .el-form--inline .el-form-item{display:block}.center[data-v-7e826c04] .el-input-number--medium{width:150px}.footer[data-v-7e826c04]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;margin:0 100px 20px}[data-v-7e826c04] .avatar-uploader .el-upload{border:1px dashed #d9d9d9;border-radius:6px;cursor:pointer;position:relative;overflow:hidden}.avatar-uploader .el-upload[data-v-7e826c04]:hover{border-color:#409eff}.avatar-uploader-icon[data-v-7e826c04]{font-size:28px;color:#8c939d;width:100px;height:100px;line-height:100px;text-align:center}.avatar[data-v-7e826c04]{width:100px;height:100px;display:block}.hint[data-v-7e826c04]{font-size:12px;color:#c0c4cc}

+ 0 - 1
dist/static/css/chunk-386d37cb.86b22fbc.css

@@ -1 +0,0 @@
-.project-search[data-v-568e32be]{display:-webkit-box;display:-ms-flexbox;display:flex;padding:10px;font-size:12px}.listClass[data-v-568e32be]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.excel-table[data-v-568e32be]{width:100%;overflow-y:auto}.excel-table table[data-v-568e32be]{border-collapse:collapse;width:100%;margin-bottom:20px}.excel-table td[data-v-568e32be],.excel-table th[data-v-568e32be]{border:1px solid #ddd;padding:8px;text-align:left;white-space:nowrap}.excel-table th[data-v-568e32be]{background-color:#f2f2f2;font-weight:700;position:sticky;top:0;z-index:1}.excel-table tr[data-v-568e32be]:nth-child(2n){background-color:#f9f9f9}.no-data[data-v-568e32be]{font-size:18px;color:#909399;text-align:center;margin-top:50px}.pagination[data-v-568e32be]{position:fixed;bottom:0;right:51%}.movable-dialog[data-v-568e32be]{position:absolute!important}[data-v-568e32be] .el-dialog__header{cursor:move}

+ 1 - 0
dist/static/css/chunk-7f485efb.0d2269f4.css

@@ -0,0 +1 @@
+.project-search[data-v-05172fcf]{display:-webkit-box;display:-ms-flexbox;display:flex;padding:10px;font-size:12px}.listClass[data-v-05172fcf]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.excel-table[data-v-05172fcf]{width:100%;overflow-y:auto}.excel-table table[data-v-05172fcf]{border-collapse:collapse;width:100%;margin-bottom:20px}.excel-table td[data-v-05172fcf],.excel-table th[data-v-05172fcf]{border:1px solid #ddd;padding:8px;text-align:left;white-space:nowrap}.excel-table th[data-v-05172fcf]{background-color:#f2f2f2;font-weight:700;position:sticky;top:0;z-index:1}.excel-table tr[data-v-05172fcf]:nth-child(2n){background-color:#f9f9f9}.no-data[data-v-05172fcf]{font-size:18px;color:#909399;text-align:center;margin-top:50px}.pagination[data-v-05172fcf]{position:fixed;bottom:0;right:51%}.movable-dialog[data-v-05172fcf]{position:absolute!important}[data-v-05172fcf] .el-dialog__header{cursor:move}

+ 0 - 1
dist/static/css/chunk-7f7cce74.65cd4a82.css

@@ -1 +0,0 @@
-.header[data-v-37483169]{margin-left:20px}.header h2[data-v-37483169]{margin-bottom:0}.center[data-v-37483169]{margin:10px}.center[data-v-37483169] .el-form--inline .el-form-item{display:block}.center[data-v-37483169] .el-input-number--medium{width:150px}.footer[data-v-37483169]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;margin:0 100px 20px}[data-v-37483169] .avatar-uploader .el-upload{border:1px dashed #d9d9d9;border-radius:6px;cursor:pointer;position:relative;overflow:hidden}.avatar-uploader .el-upload[data-v-37483169]:hover{border-color:#409eff}.avatar-uploader-icon[data-v-37483169]{font-size:28px;color:#8c939d;width:100px;height:100px;line-height:100px;text-align:center}.avatar[data-v-37483169]{width:100px;height:100px;display:block}.hint[data-v-37483169]{font-size:12px;color:#c0c4cc}

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/static/js/app.5dc2bde5.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/static/js/app.f808da03.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/static/js/chunk-079de15b.e6b30f72.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/static/js/chunk-386d37cb.ed0cbb14.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/static/js/chunk-7f485efb.b2f872ec.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
dist/static/js/chunk-7f7cce74.ad93f6ec.js


+ 24 - 20
src/components/FilePreview/index.vue

@@ -100,9 +100,9 @@ export default {
       immediate: true,
       handler(newValue) {
         /* this.internalFileUrl = newValue; */
-        if (this.isWord) {
+        /* if (this.isWord) {
           this.loadWordContent();
-        }
+        } */
       },
     },
   },
@@ -120,15 +120,16 @@ export default {
       return ["xls", "xlsx"].includes(this.fileType);
     },
     isOffice() {
-      return [ "ppt", "pptx"].includes(this.fileType);
+      return ["ppt", "pptx"].includes(this.fileType);
     },
   },
   mounted() {
+    console.log(this.isExcel);
     if (this.fileType === "txt") {
       this.loadTextContent();
     } else if (this.isWord) {
       this.loadWordContent();
-    } else if (this.isExcel) {
+    } else if (this.$route.query.type=='xlsx') {
       this.loadExcelContent();
     }
   },
@@ -138,16 +139,16 @@ export default {
       this.wordLoadingError = false;
       try {
         // 获取文档 URL
-        if (this.type !== "ai") {
+        /* if (this.type !== "ai") { */
           const res = await searchTaskInfo({
             page: 1,
             page_size: 10,
             document_id: this.$route.query.id,
           });
           this.internalFileUrl = res.data.documentUrl;
-        } else {
+      /*   } else {
           this.internalFileUrl = localStorage.getItem("href");
-        }
+        } */
 
         // 使用 fetch 获取文件内容,指定 responseType 为 'arraybuffer'
         const response = await fetch(this.internalFileUrl, {
@@ -186,16 +187,16 @@ export default {
     async loadTextContent() {
       try {
         // 获取文档 URL
-        if (this.type !== "ai") {
+     /*    if (this.type !== "ai") { */
           const res = await searchTaskInfo({
             page: 1,
             page_size: 10,
             document_id: this.$route.query.id,
           });
           this.internalFileUrl = res.data.documentUrl;
-        } else {
+        /* } else {
           this.internalFileUrl = localStorage.getItem("href");
-        }
+        } */
 
         // 获取文本内容
         const response = await fetch(this.internalFileUrl);
@@ -212,19 +213,22 @@ export default {
     },
     async loadExcelContent() {
       try {
+        let url;
         // 获取文档 URL
-        if (this.type !== "ai") {
-          const res = await searchTaskInfo({
-            page: 1,
-            page_size: 10,
-            document_id: this.$route.query.id,
-          });
-          this.internalFileUrl = res.data.documentUrl;
-        } else {
+        /*  if (this.type !== "ai") { */
+        const res = await searchTaskInfo({
+          page: 1,
+          page_size: 10,
+          document_id: this.$route.query.id,
+        });
+        this.internalFileUrl = res.data.documentUrl;
+        url = res.data.documentUrl;
+        /*  } else {
           this.internalFileUrl = localStorage.getItem("href");
-        }
+          url=localStorage.getItem("href")
+        } */
 
-        const response = await fetch(this.internalFileUrl);
+        const response = await fetch(url);
         const blob = await response.blob();
 
         // 使用 LuckyExcel 加载文件

+ 58 - 58
src/views/appConfig/index.vue

@@ -131,7 +131,7 @@
               >
                 <el-option label="全部" value="all"></el-option>
                 <el-option
-                  v-for="(doc) in documentList"
+                  v-for="doc in documentList"
                   :label="doc.name"
                   :value="doc.id"
                   :key="`doc-${doc.id}`"
@@ -582,7 +582,7 @@
               >
                 <el-option label="全部" value="all"></el-option>
                 <el-option
-                  v-for="(doc) in editDocumentList"
+                  v-for="doc in editDocumentList"
                   :label="doc.name"
                   :value="doc.id"
                   :key="`edit-doc-${doc.id}`"
@@ -1840,70 +1840,35 @@ export default {
             );
             if (!parsedVariables) return;
             convertedForm.custom_variables = parsedVariables;
-
-            // 根据 radio 值处理文档和目录数据
+            // 根据radio值处理文档和目录
             if (convertedForm.radio === "1") {
-              // 目录模式:只提交目录数据,清空文档数据
-              convertedForm.documents = [];
-              // 如果选择了目录,则使用新选择的目录
-              if (this.editForm.document_directories) {
+              // 目录模式处理保持不变
+              if (
+                !this.editForm.document_directories ||
+                this.editForm.document_directories === "001"
+              ) {
+                convertedForm.document_directories = [];
+              } else {
                 convertedForm.document_directories = this.safeGetNamesByIds(
                   [this.editForm.document_directories],
                   this.editDirectoryList
                 );
-              } else {
-                // 如果没有选择新目录,保留原有目录
-                convertedForm.document_directories =
-                  this.editForm.tempDirectories;
               }
+              // 目录模式下清空文档选择
+              convertedForm.documents = [];
             } else {
-              // 文档模式:只提交文档数据,清空目录数据
+              // 文档模式
+              // 清空目录选择
               convertedForm.document_directories = [];
 
-              // 处理文档选择
-              if (convertedForm.documents.includes("all")) {
-                convertedForm.documents = ["全部"]; // 全选时传 "全部"
+              // 处理文档选择 - 如果为空则直接传空数组
+              if (!this.editForm.documents.length) {
+                convertedForm.documents = [];
               } else {
-                // 获取当前选中的文档ID列表
-                const currentSelectedIds = new Set(convertedForm.documents);
-
-                // 获取原有未删除的文档
-                const originalDocs = this.editForm.tempDocuments.filter(
-                  (doc) =>
-                    !doc.startsWith("temp_") && !currentSelectedIds.has(doc)
-                );
-
-                // 合并原有文档和新选择的文档
-                const allSelectedDocs = new Set([
-                  ...originalDocs,
-                  ...convertedForm.documents,
-                ]);
-
                 // 转换文档 ID 为名称
-                convertedForm.documents = Array.from(allSelectedDocs)
-                  .map((docId) => {
-                    // 如果是原有文档名称,直接使用
-                    if (typeof docId === "string" && !docId.match(/^[0-9]+$/)) {
-                      return docId;
-                    }
-                    // 否则查找文档名称
-                    const doc = this.editDocumentList.find(
-                      (d) => d.id === docId
-                    );
-                    return doc ? doc.name : null;
-                  })
-                  .filter((name) => name !== null); // 过滤掉未找到的文档
-
-                // 如果文档数量超过限制,保留前10个
-                if (convertedForm.documents.length > 10) {
-                  convertedForm.documents = convertedForm.documents.slice(
-                    0,
-                    10
-                  );
-                  this.$message.warning(
-                    "已超出最大文档数量限制,仅保留前10个文档"
-                  );
-                }
+                convertedForm.documents = this.editDocumentList
+                  .filter((doc) => this.editForm.documents.includes(doc.id))
+                  .map((doc) => doc.name);
               }
             }
 
@@ -2111,7 +2076,7 @@ export default {
     handleDocumentChange(selectedValues) {
       if (selectedValues.includes("all")) {
         // 如果选择了"全部",则清空其他选项
-        this.AIform.documents = ["all"];
+        this.AIform.documents = [];
       } else {
         // 如果没有选择"全部",则限制最多选择10个文档
         if (selectedValues.length > 10) {
@@ -2162,7 +2127,7 @@ export default {
               this.documentList
             ); */
             // 处理文档选择
-            if (
+            /*  if (
               convertedForm.documents.includes("all") ||
               convertedForm.documents.length === 0
             ) {
@@ -2178,7 +2143,42 @@ export default {
             convertedForm.document_directories = this.safeGetNamesByIds(
               this.AIform.document_directories,
               this.directoryList
-            );
+            ); */
+            // 根据radio值处理文档和目录
+            if (this.radio === "1") {
+              // 目录模式
+              // 如果选择了"全部"或未选择,则传空数组
+              if (
+                !this.AIform.document_directories ||
+                this.AIform.document_directories === "001"
+              ) {
+                convertedForm.document_directories = [];
+              } else {
+                convertedForm.document_directories = this.safeGetNamesByIds(
+                  [this.AIform.document_directories],
+                  this.directoryList
+                );
+              }
+              // 目录模式下清空文档选择
+              convertedForm.documents = [];
+            } else {
+              // 文档模式
+              // 清空目录选择
+              convertedForm.document_directories = [];
+
+              // 处理文档选择
+              if (
+                !this.AIform.documents.length ||
+                this.AIform.documents.includes("all")
+              ) {
+                convertedForm.documents = [];
+              } else {
+                convertedForm.documents = this.safeGetNamesByIds(
+                  this.AIform.documents,
+                  this.documentList
+                );
+              }
+            }
             /* if (convertedForm.document_directories.length === 0) {
               convertedForm.document_directories = ["全部"];
             } */

+ 1 - 1
src/views/knowledgeMenu/category/infoList.vue

@@ -58,7 +58,7 @@
     </div> -->
     <div style="width: 50%">
       <!-- 文件预览组件 -->
-      <file-preview :file-url="documentUrl" :file-type="type" />
+      <file-preview :file-url="documentUrl" :file-type="type" :type="type"/>
     </div>
   </div>
   <div v-else>

+ 939 - 614
src/views/project/ProjectInput.vue

@@ -1,680 +1,1005 @@
 <!-- 项目输入页面 -->
 <template>
-    <div class="project-input-container">
-      <!-- 从原代码中移动第一个 tab-pane 的内容 -->
-      <div class="project-selector">
-        <el-form :inline="true">
-          <el-form-item label="当前项目">
-            <el-select
-              disabled
-              v-model="selectedProjectId"
-              placeholder="请选择项目"
+  <div class="project-input-container">
+    <!-- 从原代码中移动第一个 tab-pane 的内容 -->
+    <div class="project-selector">
+      <el-form :inline="true">
+        <el-form-item label="当前项目">
+          <el-select
+            disabled
+            v-model="selectedProjectId"
+            placeholder="请选择项目"
+            style="width: 300px"
+            @change="handleProjectConfirm"
+          >
+            <el-option
+              v-for="project in projectOptions"
+              :key="project.id"
+              :label="project.name"
+              :value="project.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button
+            type="primary"
+            @click="handleAddGspr"
+            v-if="!hasExistingData"
+            >创建输入项目</el-button
+          >
+          <el-button type="primary" @click="handleUpdateGspr" v-else
+            >更新输入项目</el-button
+          >
+          <el-button type="danger" @click="handleDeleteItems">删除</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <!-- 使用 v-if 控制其余内容的显示 -->
+    <template v-if="selectedProjectId">
+      <div class="operation-bar">
+        <el-form :inline="true" :model="gsprSearchForm" class="search-form">
+          <el-form-item label="内容">
+            <el-input
+              v-model="gsprSearchForm.keyword"
+              placeholder="请输入项目名称/技术报告位置"
               style="width: 300px"
-              @change="handleProjectConfirm"
+              clearable
+            />
+          </el-form-item>
+          <el-form-item label="部门">
+            <el-select
+              v-model="gsprSearchForm.department"
+              placeholder="请选择部门"
+              clearable
             >
               <el-option
-                v-for="project in projectOptions"
-                :key="project.id"
-                :label="project.name"
-                :value="project.id"
+                v-for="(item, index) in departList"
+                :key="index"
+                :label="item.label"
+                :value="item.label"
               />
             </el-select>
           </el-form-item>
-          <el-form-item>
-            <el-button
-              type="primary"
-              @click="handleAddGspr"
-              v-if="!hasExistingData"
-              >创建输入项目</el-button
-            >
-            <el-button type="primary" @click="handleUpdateGspr" v-else
-              >更新输入项目</el-button
+          <!-- 添加时间范围选择器 -->
+          <el-form-item label="创建时间">
+            <el-date-picker
+              v-model="gsprSearchForm.dateRange"
+              type="daterange"
+              range-separator="至"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              value-format="yyyy-MM-dd"
+              :picker-options="pickerOptions"
+            />
+          </el-form-item>
+          <!-- 添加内容状态筛选 -->
+          <el-form-item label="内容状态">
+            <el-select
+              v-model="gsprSearchForm.hasContent"
+              placeholder="请选择"
+              clearable
             >
-            <el-button type="danger" @click="handleDeleteItems">删除</el-button>
+              <el-option label="有内容" :value="true" />
+              <el-option label="无内容" :value="false" />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="handleGsprSearch">搜索</el-button>
+            <el-button @click="handleGsprReset">重置</el-button>
           </el-form-item>
         </el-form>
       </div>
-      <!-- 使用 v-if 控制其余内容的显示 -->
-      <template v-if="selectedProjectId">
-        <div class="operation-bar">
-          <el-form :inline="true" :model="gsprSearchForm" class="search-form">
-            <el-form-item label="内容">
-              <el-input
-                v-model="gsprSearchForm.keyword"
-                placeholder="请输入项目名称/技术报告位置"
-                style="width: 300px"
-                clearable
-              />
-            </el-form-item>
-            <el-form-item label="部门">
-              <el-select
-                v-model="gsprSearchForm.department"
-                placeholder="请选择部门"
-                clearable
-              >
-                <el-option
-                  v-for="(item, index) in departList"
-                  :key="index"
-                  :label="item.label"
-                  :value="item.label"
-                />
-              </el-select>
-            </el-form-item>
-            <el-form-item>
-              <el-button type="primary" @click="handleGsprSearch">搜索</el-button>
-              <el-button @click="handleGsprReset">重置</el-button>
-            </el-form-item>
-          </el-form>
-        </div>
-  
-        <el-table
-          :data="gsprTableData"
-          style="width: 100%"
-          :default-sort="{ prop: sort_field, order: sort_order }"
-          @sort-change="handleSortChange"
-        >
-          <el-table-column prop="name" label="项目名称" sortable="custom" />
-          <el-table-column prop="tech_report_location" label="技术报告位置" />
-          <el-table-column prop="department" label="部门" sortable="custom" />
-          <el-table-column prop="content" label="内容">
-            <template slot-scope="scope">
-              <div style="display: flex; align-items: center">
-                <span style="margin-right: 10px">{{ scope.row.content }}</span>
-                <i
-                  class="el-icon-edit"
-                  @click="handleEditContent(scope.row)"
-                  style="cursor: pointer; margin-left: 5px"
-                ></i>
-              </div>
-            </template>
-          </el-table-column>
-          <el-table-column label="操作" width="200">
-            <template slot-scope="scope">
-              <el-button type="primary" @click="handleEditGspr(scope.row)"
-                >编辑</el-button
-              >
-              <el-button type="danger" @click="handleDeleteGspr(scope.row)"
-                >删除</el-button
-              >
-            </template>
-          </el-table-column>
-        </el-table>
-  
-        <div class="pagination-container">
-          <el-pagination
-            @size-change="handleGsprSizeChange"
-            @current-change="handleGsprCurrentChange"
-            :current-page="gsprPageForm.page"
-            :page-sizes="[10, 20, 30, 50]"
-            :page-size="gsprPageForm.page_size"
-            layout="total, sizes, prev, pager, next, jumper"
-            :total="gsprTotal"
-          />
-        </div>
-  
-        <!-- GSPR表单弹窗 -->
-        <el-dialog
-          :title="gsprDialogTitle"
-          :visible.sync="gsprDialogVisible"
-          width="700px"
-        >
-          <el-form
-            :model="gsprForm"
-            ref="gsprForm"
-            :rules="gsprRules"
-            label-width="120px"
+      <div>
+        <el-button
+            type="primary"
+            >导出输入项目</el-button
           >
-            <el-form-item label="名称" prop="name">
-              <el-input v-model="gsprForm.name" />
-            </el-form-item>
-            <el-form-item label="技术报告位置" prop="tech_report_location">
-              <el-input v-model="gsprForm.tech_report_location" />
-            </el-form-item>
-  
-            <el-form-item label="部门" prop="department">
-              <el-select v-model="gsprForm.department" placeholder="请选择状态">
-                <el-option
-                  v-for="(item, index) in departList"
-                  :key="index"
-                  :label="item.label"
-                  :value="item.label"
-                />
-              </el-select>
-            </el-form-item>
-            <el-form-item label="内容" prop="content">
-              <el-input
-                type="textarea"
-                v-model="gsprForm.content"
-                :rows="4"
-                placeholder="请输入内容"
-              />
-            </el-form-item>
-          </el-form>
-          <div slot="footer">
-            <el-button @click="gsprDialogVisible = false">取消</el-button>
-            <el-button type="primary" @click="handleGsprSubmit">确定</el-button>
-          </div>
-        </el-dialog>
-        <!-- 添加内容编辑弹窗 -->
-        <el-dialog
-          title="编辑内容"
-          :visible.sync="contentDialogVisible"
-          width="600px"
-        >
-          <el-form :model="contentForm" ref="contentForm">
-            <el-form-item label="内容" prop="content">
-              <el-input
-                type="textarea"
-                v-model="contentForm.content"
-                :rows="4"
-                placeholder="请输入内容"
-              />
-            </el-form-item>
-          </el-form>
-          <div slot="footer">
-            <el-button @click="contentDialogVisible = false">取消</el-button>
-            <el-button type="primary" @click="handleContentSubmit"
-              >确定</el-button
+      </div>
+      <el-table
+        :data="gsprTableData"
+        style="width: 100%"
+        :default-sort="{ prop: sort_field, order: sort_order }"
+        @sort-change="handleSortChange"
+      >
+        <el-table-column prop="sort" label="排序" sortable="custom" width="80" />
+        <el-table-column prop="name" label="项目名称" sortable="custom" />
+        <el-table-column prop="tech_report_location" label="技术报告位置" />
+        <el-table-column prop="department" label="部门" sortable="custom" />
+        <el-table-column prop="content" label="内容">
+          <template slot-scope="scope">
+            <div style="display: flex; align-items: center">
+              <span style="margin-right: 10px">{{ scope.row.content }}</span>
+              <i
+                class="el-icon-edit"
+                @click="handleEditContent(scope.row)"
+                style="cursor: pointer; margin-left: 5px"
+              ></i>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="" label="难度" sortable="custom" />
+        <el-table-column label="操作" width="200">
+          <template slot-scope="scope">
+            <el-button type="primary" @click="handleEditGspr(scope.row)"
+              >编辑</el-button
             >
-          </div>
-        </el-dialog>
-      </template>
-  
-      <!-- 未选择项目时显示提示 -->
-      <el-empty
-        v-else
-        description="请先选择一个项目"
-        :image-size="200"
-      ></el-empty>
-  
-      <!-- 添加删除确认对话框 -->
+            <el-button type="danger" @click="handleDeleteGspr(scope.row)"
+              >删除</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div class="pagination-container">
+        <el-pagination
+          @size-change="handleGsprSizeChange"
+          @current-change="handleGsprCurrentChange"
+          :current-page="gsprPageForm.page"
+          :page-sizes="[10, 20, 30, 50]"
+          :page-size="gsprPageForm.page_size"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="gsprTotal"
+        />
+      </div>
+
+      <!-- GSPR表单弹窗 -->
       <el-dialog
-        title="删除确认"
-        :visible.sync="deleteDialogVisible"
-        width="400px"
+        :title="gsprDialogTitle"
+        :visible.sync="gsprDialogVisible"
+        width="700px"
       >
-        <!-- 添加提示信息区域 -->
-        <div class="delete-warning" style="margin-bottom: 20px">
-          <i
-            class="el-icon-warning"
-            style="color: #e6a23c; margin-right: 8px"
-          ></i>
-          <span
-            >此操作将永久删除该项目下的所有输入项目,请输入'DELETE'确认删除</span
-          >
-        </div>
-  
         <el-form
-          :model="deleteForm"
-          :rules="deleteRules"
-          ref="deleteForm"
-          label-width="100px"
+          :model="gsprForm"
+          ref="gsprForm"
+          :rules="gsprRules"
+          label-width="120px"
         >
-          <el-form-item label="确认文本" prop="confirmText">
-            <el-input
-              v-model="deleteForm.confirmText"
-              placeholder="请输入'DELETE'"
-            ></el-input>
+          <el-form-item label="名称" prop="name">
+            <el-input v-model="gsprForm.name" />
           </el-form-item>
+          <el-form-item label="技术报告位置" prop="tech_report_location">
+            <el-input v-model="gsprForm.tech_report_location" />
+          </el-form-item>
+
+          <el-form-item label="部门" prop="department">
+            <el-select v-model="gsprForm.department" placeholder="请选择状态">
+              <el-option
+                v-for="(item, index) in departList"
+                :key="index"
+                :label="item.label"
+                :value="item.label"
+              />
+            </el-select>
+          </el-form-item>
+          <!--  <el-form-item label="内容" prop="content">
+            <div class="editor-area sticky-editor">
+              <ckeditor
+                ref="editor"
+                v-model="gsprForm.content"
+                :config="editorConfig"
+                :editorUrl="editorUrl"
+              ></ckeditor>
+            </div>
+          </el-form-item> -->
         </el-form>
-        <span slot="footer" class="dialog-footer">
-          <el-button @click="deleteDialogVisible = false">取消</el-button>
-          <el-button type="danger" @click="confirmDelete">确认删除</el-button>
-        </span>
+        <div slot="footer">
+          <el-button @click="gsprDialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleGsprSubmit">确定</el-button>
+        </div>
       </el-dialog>
-      <!-- 其余第一个tab的内容... -->
-    </div>
-  </template>
-    
-  <script>
-  import {
-    projectList,
-    projectAdd,
-    departments,
-    projectUpdate,
-    projectDelete,
-    create_items,
-    delete_items,
-    update_items,
-  } from "@/api/knowledge";
-  import { searchProject } from "@/api/project";
-  import { mapState } from "vuex";
-  
-  export default {
-    data() {
-      // 自定义验证规则
-      const validateDeleteConfirm = (rule, value, callback) => {
-        if (value !== "DELETE") {
-          callback(new Error("请输入正确的确认文本"));
-        } else {
-          callback();
+      <!-- 添加内容编辑弹窗 -->
+      <el-dialog
+        title="编辑内容"
+        :visible.sync="contentDialogVisible"
+        width="80%"
+      >
+        <!--  <el-form :model="contentForm" ref="contentForm">
+          <el-form-item label="内容" prop="content"> -->
+        <!-- <el-inputwidth="600px"
+              type="textarea"
+              v-model="contentForm.content"
+              :rows="4"
+              placeholder="请输入内容"
+            /> -->
+        <ckeditor
+          ref="editor"
+          v-model="contentForm.content"
+          :config="editorConfig"
+          :editorUrl="editorUrl"
+        ></ckeditor>
+        <!--   </el-form-item>
+        </el-form> -->
+        <div slot="footer">
+          <el-button @click="contentDialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleContentSubmit"
+            >确定</el-button
+          >
+        </div>
+      </el-dialog>
+    </template>
+
+    <!-- 未选择项目时显示提示 -->
+    <el-empty
+      v-else
+      description="请先选择一个项目"
+      :image-size="200"
+    ></el-empty>
+
+    <!-- 添加删除确认对话框 -->
+    <el-dialog
+      title="删除确认"
+      :visible.sync="deleteDialogVisible"
+      width="400px"
+    >
+      <!-- 添加提示信息区域 -->
+      <div class="delete-warning" style="margin-bottom: 20px">
+        <i
+          class="el-icon-warning"
+          style="color: #e6a23c; margin-right: 8px"
+        ></i>
+        <span
+          >此操作将永久删除该项目下的所有输入项目,请输入'DELETE'确认删除</span
+        >
+      </div>
+
+      <el-form
+        :model="deleteForm"
+        :rules="deleteRules"
+        ref="deleteForm"
+        label-width="100px"
+      >
+        <el-form-item label="确认文本" prop="confirmText">
+          <el-input
+            v-model="deleteForm.confirmText"
+            placeholder="请输入'DELETE'"
+          ></el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="deleteDialogVisible = false">取消</el-button>
+        <el-button type="danger" @click="confirmDelete">确认删除</el-button>
+      </span>
+    </el-dialog>
+    <!-- 其余第一个tab的内容... -->
+  </div>
+</template>
+  
+<script>
+import {
+  projectList,
+  projectAdd,
+  departments,
+  projectUpdate,
+  projectDelete,
+  create_items,
+  delete_items,
+  update_items,
+} from "@/api/knowledge";
+import { searchProject } from "@/api/project";
+import { mapState } from "vuex";
+import CKEditor from "ckeditor4-vue";
+export default {
+  compnents: {
+    ckeditor: CKEditor.component,
+  },
+  data() {
+    // 自定义验证规则
+    const validateDeleteConfirm = (rule, value, callback) => {
+      if (value !== "DELETE") {
+        callback(new Error("请输入正确的确认文本"));
+      } else {
+        callback();
+      }
+    };
+    return {
+      pickerOptions: {
+        shortcuts: [
+          {
+            text: "最近一周",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+          {
+            text: "最近一个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setMonth(start.getMonth() - 1);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+        ],
+      },
+      selectedProjectId: null,
+      projectOptions: [],
+      gsprTableData: [],
+      gsprSearchForm: {
+        keyword: "",
+        department: "",
+        project_id: null,
+        dateRange: [], // 添加时间范围
+        hasContent: null, // 添加内容状态
+      },
+      gsprPageForm: {
+        page: 1,
+        page_size: 30,
+      },
+      gsprTotal: 0,
+      departList: [],
+      sort_field: "name",
+      sort_order: "desc",
+      hasExistingData: false,
+      gsprDialogVisible: false,
+      gsprDialogTitle: "",
+      gsprForm: {
+        id: "",
+        name: "",
+        tech_report_location: "",
+        department: "",
+        content: "",
+        status: 5,
+      },
+      gsprPageForm: {
+        page: 1,
+        page_size: 30,
+      },
+      gsprTotal: 0,
+      gsprRules: {
+        name: [{ required: true, message: "请输入名称", trigger: "blur" }],
+        tech_report_location: [
+          { required: true, message: "请输入技术报告位置", trigger: "blur" },
+        ],
+        department: [
+          { required: true, message: "请输入部门", trigger: "blur" },
+        ],
+        status: [{ required: true, message: "请选择状态", trigger: "change" }],
+      },
+
+      contentDialogVisible: false,
+      contentForm: {
+        id: "",
+        content: "",
+        name: "",
+        department: "",
+      },
+      /* 删除逻辑 */
+      deleteDialogVisible: false,
+      deleteForm: {
+        confirmText: "",
+      },
+      deleteRules: {
+        confirmText: [
+          { required: true, message: "请输入确认文本", trigger: "blur" },
+          { validator: validateDeleteConfirm, trigger: "blur" },
+        ],
+      },
+      editorUrl: "/ckeditor/ckeditor.js",
+      editorConfig: {
+        language: "zh-cn",
+        height: "300px",
+      },
+    };
+  },
+  mounted() {
+    this.fetchProjects().then(() => {
+      // Get project ID from URL query parameter
+      const urlProjectId = this.$route.query.id;
+      if (urlProjectId) {
+        this.selectedProjectId = parseInt(urlProjectId);
+        this.handleProjectConfirm();
+      }
+    });
+  },
+
+  methods: {
+    /* 删除 */
+    async handleDeleteGspr(row) {
+      try {
+        await this.$confirm("确认删除该记录?", "提示", {
+          type: "warning",
+        });
+        // 替换为实际的删除API调用
+        await projectDelete({ id: row.id });
+        this.$message.success("删除成功");
+        this.fetchGsprData();
+      } catch (error) {
+        if (error !== "cancel") {
+          this.$message.error("删除失败");
         }
+      }
+    },
+    handleEditGspr(row) {
+      this.gsprDialogTitle = "编辑文献项目";
+      this.gsprForm = { ...row };
+      console.log(this.gsprForm);
+      this.gsprDialogVisible = true;
+    },
+    // 修改编辑内容显示方法
+    handleEditContent(row) {
+      const selectedProject = this.projectOptions.find(
+        (p) => p.id === this.selectedProjectId
+      );
+
+      // 处理content内容,将文本格式转换为HTML并清理空样式
+      const processedContent = this.cleanEmptyStyles(
+        this.convertTextToHtml(row.content)
+      );
+
+      this.contentForm = {
+        project: selectedProject.id,
+        project_name: selectedProject.name,
+        id: row.id,
+        tech_report_location: row.tech_report_location,
+        content: processedContent,
+        name: row.name,
+        department: row.department,
       };
-      return {
-        selectedProjectId: null,
-        projectOptions: [],
-        gsprTableData: [],
-        gsprSearchForm: {
-          keyword: "",
-          department: "",
-          project_id: null,
-        },
-        gsprPageForm: {
-          page: 1,
-          page_size: 30,
-        },
-        gsprTotal: 0,
-        departList: [],
-        sort_field: "name",
-        sort_order: "desc",
-        hasExistingData: false,
-        gsprDialogVisible: false,
-        gsprDialogTitle: "",
-        gsprForm: {
-          id: "",
-          name: "",
-          tech_report_location: "",
-          department: "",
-          content: "",
-          status: 5,
-        },
-        gsprPageForm: {
-          page: 1,
-          page_size: 30,
-        },
-        gsprTotal: 0,
-        gsprRules: {
-          name: [{ required: true, message: "请输入名称", trigger: "blur" }],
-          tech_report_location: [
-            { required: true, message: "请输入技术报告位置", trigger: "blur" },
-          ],
-          department: [
-            { required: true, message: "请输入部门", trigger: "blur" },
-          ],
-          status: [{ required: true, message: "请选择状态", trigger: "change" }],
-        },
-  
-        contentDialogVisible: false,
-        contentForm: {
-          id: "",
-          content: "",
-          name: "",
-          department: "",
-        },
-        /* 删除逻辑 */
-        deleteDialogVisible: false,
-        deleteForm: {
-          confirmText: "",
-        },
-        deleteRules: {
-          confirmText: [
-            { required: true, message: "请输入确认文本", trigger: "blur" },
-            { validator: validateDeleteConfirm, trigger: "blur" },
-          ],
-        },
-      };
+      this.contentDialogVisible = true;
     },
-    mounted() {
-      this.fetchProjects().then(() => {
-        // Get project ID from URL query parameter
-        const urlProjectId = this.$route.query.id;
-        if (urlProjectId) {
-          this.selectedProjectId = parseInt(urlProjectId);
-          this.handleProjectConfirm();
+
+    // 修改回显方法,解析样式
+    convertTextToHtml(text) {
+      if (!text) return "";
+
+      let html = text;
+
+      // 处理表格标记(包含边框样式)
+      html = html.replace(
+        /\[表格\|style=(.*?)\|class=(.*?)\]([\s\S]*?)(?=\[|$)/g,
+        (match, tableStyle, tableClass, tableContent) => {
+          const rows = tableContent.trim().split("\n");
+          let tableHtml = `<table style="${tableStyle}" class="${tableClass}">`;
+
+          rows.forEach((row) => {
+            tableHtml += "<tr>";
+            const cells = row.split("|").map((cell) => {
+              const cellMatch = cell.match(
+                /(.*?)(?:\[style=(.*?)\|class=(.*?)\])?$/
+              );
+              if (cellMatch) {
+                const [_, content, style = "", className = ""] = cellMatch;
+                return `<td style="${style}" class="${className}">${content.trim()}</td>`;
+              }
+              return `<td>${cell.trim()}</td>`;
+            });
+            tableHtml += cells.join("") + "</tr>";
+          });
+
+          tableHtml += "</table>";
+          return tableHtml;
         }
-      });
+      );
+
+      // 2. 处理图片标记(保持不变)
+      html = html.replace(
+        /\[图片:(.*?)\|style=(.*?)\|class=(.*?)\]/g,
+        (match, src, style, className) => {
+          return `<img src="${src}" alt="图片" style="${style}" class="${className}">`;
+        }
+      );
+
+      // 3. 处理文本样式(保持不变)
+      html = html.replace(
+        /\[文本:(.*?)\|style=(.*?)\|class=(.*?)\]/g,
+        (match, content, style, className) => {
+          return `<span style="${style}" class="${className}">${content}</span>`;
+        }
+      );
+
+      // 4. 处理换行
+      html = html.replace(/\n/g, "<br>");
+
+      return html;
     },
-  
-    methods: {
-      /* 删除 */
-      async handleDeleteGspr(row) {
-        try {
-          await this.$confirm("确认删除该记录?", "提示", {
-            type: "warning",
+
+    // 添加辅助方法:清理空样式属性
+    cleanEmptyStyles(html) {
+      return html
+        .replace(/style=""/g, "")
+        .replace(/class=""/g, "")
+        .replace(/\s+>/g, ">");
+    },
+    /*  */
+    async handleContentSubmit() {
+      try {
+        // 创建一个函数来处理富文本内容,保留样式
+        const processContent = (htmlContent) => {
+          const tempDiv = document.createElement("div");
+          tempDiv.innerHTML = htmlContent;
+
+          // 处理图片:保留样式属性
+          const images = tempDiv.getElementsByTagName("img");
+          Array.from(images).forEach((img) => {
+            const style = img.getAttribute("style") || "";
+            const className = img.getAttribute("class") || "";
+            const imgPlaceholder = `[图片:${img.src}|style=${style}|class=${className}]`;
+            img.replaceWith(document.createTextNode(imgPlaceholder));
           });
-          // 替换为实际的删除API调用
-          await projectDelete({ id: row.id });
-          this.$message.success("删除成功");
-          this.fetchGsprData();
-        } catch (error) {
-          if (error !== "cancel") {
-            this.$message.error("删除失败");
-          }
-        }
-      },
-      handleEditGspr(row) {
-        this.gsprDialogTitle = "编辑文献项目";
-        this.gsprForm = { ...row };
-        console.log(this.gsprForm);
-        this.gsprDialogVisible = true;
-      },
-      /* 编辑内容 */
-      handleEditContent(row) {
-        const selectedProject = this.projectOptions.find(
-          (p) => p.id === this.selectedProjectId
-        );
-        this.contentForm = {
-          project: selectedProject.id,
-          project_name: selectedProject.name,
-          id: row.id,
-          tech_report_location: row.tech_report_location,
-          content: row.content,
-          name: row.name,
-          department: row.department,
+
+          // 处理表格:保留完整的样式属性,包括边框
+          const tables = tempDiv.getElementsByTagName("table");
+          Array.from(tables).forEach((table) => {
+            const tableStyle = table.getAttribute("style") || "";
+            const tableClass = table.getAttribute("class") || "";
+            const tableBorder = table.getAttribute("border") || "";
+
+            const rows = Array.from(table.rows).map((row) => {
+              return Array.from(row.cells)
+                .map((cell) => {
+                  const cellStyle = cell.getAttribute("style") || "";
+                  const cellClass = cell.getAttribute("class") || "";
+                  const cellBorder = cell.getAttribute("border") || "";
+                  const content = cell.textContent.trim();
+
+                  // 合并所有样式属性
+                  const cellStyles = [
+                    cellStyle,
+                    cellBorder ? `border: ${cellBorder}px solid #000;` : "",
+                  ]
+                    .filter(Boolean)
+                    .join(" ");
+
+                  return `${content}${
+                    cellStyles || cellClass
+                      ? `[style=${cellStyles}|class=${cellClass}]`
+                      : ""
+                  }`;
+                })
+                .join("|");
+            });
+
+            // 合并表格的样式属性
+            const tableStyles = [
+              tableStyle,
+              tableBorder ? `border: ${tableBorder}px solid #000;` : "",
+            ]
+              .filter(Boolean)
+              .join(" ");
+
+            const tablePlaceholder = `[表格|style=${tableStyles}|class=${tableClass}]\n${rows.join(
+              "\n"
+            )}`;
+            table.replaceWith(document.createTextNode(tablePlaceholder));
+          });
+
+          // 处理文本样式
+          const styledSpans = tempDiv.getElementsByTagName("span");
+          Array.from(styledSpans).forEach((span) => {
+            const style = span.getAttribute("style") || "";
+            const className = span.getAttribute("class") || "";
+            if (style || className) {
+              const content = span.textContent;
+              span.replaceWith(
+                document.createTextNode(
+                  `[文本:${content}|style=${style}|class=${className}]`
+                )
+              );
+            }
+          });
+
+          return tempDiv.textContent || tempDiv.innerText;
         };
-        this.contentDialogVisible = true;
-      },
-      /*  */
-      async handleContentSubmit() {
-        try {
-          await projectUpdate(this.contentForm);
-          this.$message.success("内容更新成功");
-          this.contentDialogVisible = false;
-          this.fetchGsprData();
-        } catch (error) {
-          this.$message.error("内容更新失败");
+
+        const submitData = {
+          ...this.contentForm,
+          content: processContent(this.contentForm.content),
+        };
+
+        await projectUpdate(submitData);
+        this.$message.success("内容更新成功");
+        this.contentDialogVisible = false;
+        this.fetchGsprData();
+      } catch (error) {
+        this.$message.error("内容更新失败");
+      }
+    },
+
+    // 同样修改handleGsprSubmit方法
+    async handleGsprSubmit() {
+      try {
+        await this.$refs.gsprForm.validate();
+
+        // 处理富文本内容
+        const processContent = (htmlContent) => {
+          const tempDiv = document.createElement("div");
+          tempDiv.innerHTML = htmlContent;
+
+          // 处理图片
+          const images = tempDiv.getElementsByTagName("img");
+          Array.from(images).forEach((img) => {
+            const imgPlaceholder = `[图片:${img.src}]`;
+            img.replaceWith(document.createTextNode(imgPlaceholder));
+          });
+
+          // 处理表格
+          const tables = tempDiv.getElementsByTagName("table");
+          Array.from(tables).forEach((table) => {
+            const rows = Array.from(table.rows).map((row) =>
+              Array.from(row.cells)
+                .map((cell) => cell.textContent.trim())
+                .join(" | ")
+            );
+            const tablePlaceholder = "[表格]\n" + rows.join("\n");
+            table.replaceWith(document.createTextNode(tablePlaceholder));
+          });
+
+          return tempDiv.textContent || tempDiv.innerText;
+        };
+
+        // 创建新对象避免修改原始数据
+        const submitData = {
+          ...this.gsprForm,
+          content: processContent(this.gsprForm.content),
+        };
+
+        if (this.gsprForm.id) {
+          await projectUpdate(submitData);
+        } else {
+          await projectAdd(submitData);
         }
-      },
-      /*  */
-      async handleGsprSubmit() {
-        try {
-          await this.$refs.gsprForm.validate();
-          // 替换为实际的保存API调用
-          if (this.gsprForm.id) {
-            await projectUpdate(this.gsprForm);
-          } else {
-            await projectAdd(this.gsprForm);
-          }
-          this.$message.success(this.gsprForm.id ? "更新成功" : "创建成功");
-          this.gsprDialogVisible = false;
-          this.fetchGsprData();
-        } catch (error) {
-          if (error !== false) {
-            this.$message.error("保存失败");
-          }
+
+        this.$message.success(this.gsprForm.id ? "更新成功" : "创建成功");
+        this.gsprDialogVisible = false;
+        this.fetchGsprData();
+      } catch (error) {
+        if (error !== false) {
+          this.$message.error("保存失败");
         }
-      },
-      /* 初始化项目列表 */
-      async fetchProjects() {
-        try {
-          const response = await searchProject({ page: 1, pageSize: 999 });
-          if (response.status === 200) {
-            this.projectOptions = response.data.dataList;
-  
-            // 如果有当前项目ID但没有选中项目,自动选中
-            // 如果有当前项目ID但没有选中项目,自动选中
-            if (!this.selectedProjectId && this.$route.query.id) {
-              this.selectedProjectId = parseInt(this.$route.query.id);
-              this.handleProjectConfirm();
-            }
-          }
-        } catch (error) {
-          this.$message.error("获取项目列表失败");
+      }
+    },
+    /*  */
+    async handleGsprSubmit() {
+      try {
+        await this.$refs.gsprForm.validate();
+        // 替换为实际的保存API调用
+        if (this.gsprForm.id) {
+          await projectUpdate(this.gsprForm);
+        } else {
+          await projectAdd(this.gsprForm);
         }
-      },
-      async handleProjectConfirm() {
-        if (!this.selectedProjectId) {
-          this.$message.warning("请先选择一个项目");
-          return;
+        this.$message.success(this.gsprForm.id ? "更新成功" : "创建成功");
+        this.gsprDialogVisible = false;
+        this.fetchGsprData();
+      } catch (error) {
+        if (error !== false) {
+          this.$message.error("保存失败");
         }
-        console.log(this.selectedProjectId);
-        const selectedProject = this.projectOptions.find(
-          (p) => p.id === this.selectedProjectId
-        );
-        if (!selectedProject) {
-          this.$message.error("未找到选中的项目");
-          return;
+      }
+    },
+    /* 初始化项目列表 */
+    async fetchProjects() {
+      try {
+        const response = await searchProject({ page: 1, pageSize: 999 });
+        if (response.status === 200) {
+          this.projectOptions = response.data.dataList;
+
+          // 如果有当前项目ID但没有选中项目,自动选中
+          // 如果有当前项目ID但没有选中项目,自动选中
+          if (!this.selectedProjectId && this.$route.query.id) {
+            this.selectedProjectId = parseInt(this.$route.query.id);
+            this.handleProjectConfirm();
+          }
         }
-  
-        this.$store.dispatch("project/setCurrentProject", {
+      } catch (error) {
+        this.$message.error("获取项目列表失败");
+      }
+    },
+    async handleProjectConfirm() {
+      if (!this.selectedProjectId) {
+        this.$message.warning("请先选择一个项目");
+        return;
+      }
+      console.log(this.selectedProjectId);
+      const selectedProject = this.projectOptions.find(
+        (p) => p.id === this.selectedProjectId
+      );
+      if (!selectedProject) {
+        this.$message.error("未找到选中的项目");
+        return;
+      }
+
+      this.$store.dispatch("project/setCurrentProject", {
+        id: selectedProject.id,
+        name: selectedProject.name,
+      });
+
+      localStorage.setItem(
+        "currentProject",
+        JSON.stringify({
           id: selectedProject.id,
           name: selectedProject.name,
+        })
+      );
+
+      this.gsprSearchForm.project_id = this.selectedProjectId;
+      this.fetchGsprData();
+    },
+    // 创建项目
+    async handleAddGspr() {
+      if (!this.selectedProjectId) {
+        this.$message.warning("请先选择一个项目");
+        return;
+      }
+
+      const loading = this.$loading({
+        lock: true,
+        text: "正在创建项目...",
+        spinner: "el-icon-loading",
+        background: "rgba(0, 0, 0, 0.7)",
+      });
+
+      try {
+        const res = await create_items({
+          project_id: this.selectedProjectId,
+          project_name: this.projectOptions.find(
+            (p) => p.id === this.selectedProjectId
+          ).name,
         });
-  
-        localStorage.setItem(
-          "currentProject",
-          JSON.stringify({
-            id: selectedProject.id,
-            name: selectedProject.name,
-          })
-        );
-  
-        this.gsprSearchForm.project_id = this.selectedProjectId;
-        this.fetchGsprData();
-      },
-      // 创建项目
-      async handleAddGspr() {
-        if (!this.selectedProjectId) {
-          this.$message.warning("请先选择一个项目");
-          return;
+
+        if (res.status === 200) {
+          this.$message.success(
+            `成功创建 ${res.data.data.created_count} 个输入项目!`
+          );
+          await this.fetchGsprData();
         }
-  
+      } catch (error) {
+        this.$message.error("创建失败: " + (error.message || "未知错误"));
+      } finally {
+        loading.close();
+      }
+    },
+
+    // 更新项目
+    async handleUpdateGspr() {
+      if (!this.selectedProjectId) {
+        this.$message.warning("请先选择一个项目");
+        return;
+      }
+      const selectedProject = this.projectOptions.find(
+        (p) => p.id === this.selectedProjectId
+      );
+
+      try {
+        await this.$confirm(
+          "此操作将更新当前项目的所有输入项目,是否继续?",
+          "提示",
+          {
+            confirmButtonText: "确定",
+            cancelButtonText: "取消",
+            type: "warning",
+          }
+        );
+
         const loading = this.$loading({
           lock: true,
-          text: "正在创建项目...",
+          text: "正在更新项目...",
           spinner: "el-icon-loading",
           background: "rgba(0, 0, 0, 0.7)",
         });
-  
+
         try {
-          const res = await create_items({
-            project_id: this.selectedProjectId,
-            project_name: this.projectOptions.find(
-              (p) => p.id === this.selectedProjectId
-            ).name,
+          const res = await update_items({
+            // 假设有一个update_items的API
+            project_id: selectedProject.id,
+            project_name: selectedProject.name,
           });
-  
-          if (res.status === 200) {
-            this.$message.success(
-              `成功创建 ${res.data.data.created_count} 个输入项目!`
-            );
-            await this.fetchGsprData();
+
+          if (res.status !== 200) {
+            throw new Error("更新失败");
           }
-        } catch (error) {
-          this.$message.error("创建失败: " + (error.message || "未知错误"));
+
+          this.$message.success(
+            `成功更新 ${res.data.data.updated_count} 个输入项目!`
+          );
+          await this.fetchGsprData();
         } finally {
           loading.close();
         }
-      },
-  
-      // 更新项目
-      async handleUpdateGspr() {
-        if (!this.selectedProjectId) {
-          this.$message.warning("请先选择一个项目");
-          return;
+      } catch (error) {
+        if (error !== "cancel") {
+          this.$message.error("更新失败: " + (error.message || "未知错误"));
         }
-        const selectedProject = this.projectOptions.find(
-          (p) => p.id === this.selectedProjectId
-        );
-  
+      }
+    },
+
+    // 删除项目
+    async handleDeleteItems() {
+      if (!this.selectedProjectId) {
+        this.$message.warning("请先选择一个项目");
+        return;
+      }
+
+      const selectedProject = this.projectOptions.find(
+        (p) => p.id === this.selectedProjectId
+      );
+
+      this.$confirm(
+        `您确定要删除项目 "${selectedProject.name}" 的所有输入项目吗?此操作不可恢复。`,
+        "警告",
+        {
+          confirmButtonText: "继续删除",
+          cancelButtonText: "取消",
+          type: "warning",
+        }
+      )
+        .then(() => {
+          this.deleteForm.confirmText = "";
+          this.deleteDialogVisible = true;
+        })
+        .catch(() => {
+          this.$message({
+            type: "info",
+            message: "已取消删除",
+          });
+        });
+    },
+    // 确认删除
+    confirmDelete() {
+      this.$refs.deleteForm.validate(async (valid) => {
+        if (!valid) {
+          return false;
+        }
+
         try {
-          await this.$confirm(
-            "此操作将更新当前项目的所有输入项目,是否继续?",
-            "提示",
-            {
-              confirmButtonText: "确定",
-              cancelButtonText: "取消",
-              type: "warning",
-            }
-          );
-  
           const loading = this.$loading({
             lock: true,
-            text: "正在更新项目...",
+            text: "正在删除...",
             spinner: "el-icon-loading",
             background: "rgba(0, 0, 0, 0.7)",
           });
-  
-          try {
-            const res = await update_items({
-              // 假设有一个update_items的API
-              project_id: selectedProject.id,
-              project_name: selectedProject.name,
-            });
-  
-            if (res.status !== 200) {
-              throw new Error("更新失败");
-            }
-  
-            this.$message.success(
-              `成功更新 ${res.data.data.updated_count} 个输入项目!`
-            );
-            await this.fetchGsprData();
-          } finally {
-            loading.close();
-          }
-        } catch (error) {
-          if (error !== "cancel") {
-            this.$message.error("更新失败: " + (error.message || "未知错误"));
-          }
-        }
-      },
-  
-      // 删除项目
-      async handleDeleteItems() {
-        if (!this.selectedProjectId) {
-          this.$message.warning("请先选择一个项目");
-          return;
-        }
-  
-        const selectedProject = this.projectOptions.find(
-          (p) => p.id === this.selectedProjectId
-        );
-  
-        this.$confirm(
-          `您确定要删除项目 "${selectedProject.name}" 的所有输入项目吗?此操作不可恢复。`,
-          "警告",
-          {
-            confirmButtonText: "继续删除",
-            cancelButtonText: "取消",
-            type: "warning",
-          }
-        )
-          .then(() => {
-            this.deleteForm.confirmText = "";
-            this.deleteDialogVisible = true;
-          })
-          .catch(() => {
-            this.$message({
-              type: "info",
-              message: "已取消删除",
-            });
-          });
-      },
-        // 确认删除
-        confirmDelete() {
-        this.$refs.deleteForm.validate(async (valid) => {
-          if (!valid) {
-            return false;
-          }
-  
-          try {
-            const loading = this.$loading({
-              lock: true,
-              text: "正在删除...",
-              spinner: "el-icon-loading",
-              background: "rgba(0, 0, 0, 0.7)",
-            });
-  
-            const response = await delete_items({
-              project_id: this.selectedProjectId,
-            });
-  
-            loading.close();
-  
-            if (response.status === 200) {
-              this.$message.success("删除成功");
-              this.deleteDialogVisible = false;
-              // 重新加载数据
-              this.fetchGsprData();
-            } else {
-              this.$message.error(response.message || "删除失败");
-            }
-          } catch (error) {
-            this.$message.error("删除失败:" + (error.message || "未知错误"));
-          }
-        });
-      },
-  
-      // 数据获取与分页
-      async fetchGsprData() {
-        if (!this.selectedProjectId) {
-          return; // 如果没有选择项目,直接返回
-        }
-  
-        const loading = this.$loading({
-          lock: true,
-          text: "加载中...",
-          spinner: "el-icon-loading",
-          background: "rgba(0, 0, 0, 0.7)",
-        });
-  
-        try {
-          const params = {
-            sort_field: this.sort_field,
-            sort_order: this.sort_order,
+
+          const response = await delete_items({
             project_id: this.selectedProjectId,
-            ...this.gsprPageForm,
-            ...this.gsprSearchForm,
-          };
-          const response = await projectList(params);
+          });
+
+          loading.close();
+
           if (response.status === 200) {
-            this.gsprTableData = response.data.list;
-            this.gsprTotal = response.data.total;
-            this.hasExistingData = this.gsprTotal > 0;
+            this.$message.success("删除成功");
+            this.deleteDialogVisible = false;
+            // 重新加载数据
+            this.fetchGsprData();
+          } else {
+            this.$message.error(response.message || "删除失败");
           }
         } catch (error) {
-          console.error("获取数据失败:", error);
-          this.$message.error("获取数据失败");
-        } finally {
-          loading.close();
+          this.$message.error("删除失败:" + (error.message || "未知错误"));
         }
-      },
-  
-      handleGsprSizeChange(val) {
-        this.gsprPageForm.page_size = val;
-        this.fetchGsprData();
-      },
-  
-      handleGsprCurrentChange(val) {
-        this.gsprPageForm.page = val;
-        this.fetchGsprData();
-      },
-  
-      // 搜索相关
-      handleGsprSearch() {
-        this.gsprPageForm.page = 1;
-        this.fetchGsprData();
-      },
-  
-      handleGsprReset() {
-        this.gsprSearchForm = {
-          keyword: "",
-          department: "",
+      });
+    },
+
+    // 数据获取与分页
+    async fetchGsprData() {
+      if (!this.selectedProjectId) {
+        return; // 如果没有选择项目,直接返回
+      }
+
+      const loading = this.$loading({
+        lock: true,
+        text: "加载中...",
+        spinner: "el-icon-loading",
+        background: "rgba(0, 0, 0, 0.7)",
+      });
+
+      try {
+        const params = {
+          sort_field: this.sort_field,
+          sort_order: this.sort_order,
           project_id: this.selectedProjectId,
+          ...this.gsprPageForm,
+          ...this.gsprSearchForm,
+          // 处理日期范围
+          start_date: this.gsprSearchForm.dateRange?.[0] || null,
+          end_date: this.gsprSearchForm.dateRange?.[1] || null,
         };
-        this.handleGsprSearch();
-      },
-  
-      // 排序处理
-      handleSortChange({ prop, order }) {
-        this.sort_field = prop;
-        this.sort_order = order === "ascending" ? "asc" : "desc";
-        if (!order) {
-          this.sort_field = "name";
-          this.sort_order = "desc";
+        // 删除原始dateRange参数
+        delete params.dateRange;
+        const response = await projectList(params);
+        if (response.status === 200) {
+          this.gsprTableData = response.data.list;
+          this.gsprTotal = response.data.total;
+          this.hasExistingData = this.gsprTotal > 0;
         }
-        this.fetchGsprData();
-      },
+      } catch (error) {
+        console.error("获取数据失败:", error);
+        this.$message.error("获取数据失败");
+      } finally {
+        loading.close();
+      }
+    },
+
+    handleGsprSizeChange(val) {
+      this.gsprPageForm.page_size = val;
+      this.fetchGsprData();
+    },
+
+    handleGsprCurrentChange(val) {
+      this.gsprPageForm.page = val;
+      this.fetchGsprData();
+    },
+
+    // 搜索相关
+    handleGsprSearch() {
+      this.gsprPageForm.page = 1;
+      this.fetchGsprData();
+    },
+
+    handleGsprReset() {
+      this.gsprSearchForm = {
+        keyword: "",
+        department: "",
+        project_id: this.selectedProjectId,
+        dateRange: [], // 重置时间范围
+        hasContent: null, // 重置内容状态
+      };
+      this.handleGsprSearch();
+    },
+
+    // 排序处理
+    handleSortChange({ prop, order }) {
+      this.sort_field = prop;
+      this.sort_order = order === "ascending" ? "asc" : "desc";
+      if (!order) {
+        this.sort_field = "name";
+        this.sort_order = "desc";
+      }
+      this.fetchGsprData();
     },
-  };
-  </script>
-  <style >
-  .project-input-container {
-    padding: 20px;
+  },
+};
+</script>
+<style lang="scss" >
+.project-input-container {
+  padding: 20px;
+}
+.pagination-container {
+  display: flex;
+  justify-content: flex-end;
+}
+.editor-area {
+  ::v-deep .cke_top {
+    display: block; // Hide the original toolbar
   }
-  .pagination-container {
-    display: flex;
-    justify-content: flex-end;
+}
+.project-input-container {
+  padding: 20px;
+}
+
+.pagination-container {
+  display: flex;
+  justify-content: flex-end;
+}
+
+/* 添加编辑器工具栏样式 */
+.editor-area {
+  .cke_top {
+    display: flex !important;
+    justify-content: space-between;
+    align-items: center;
+    padding: 6px 8px !important;
+
+    /* 工具栏组样式 */
+    .cke_toolgroup {
+      margin: 0 2px;
+      display: flex;
+      align-items: center;
+    }
+
+    /* 工具栏按钮样式 */
+    .cke_toolbar {
+      float: none !important;
+      display: inline-flex;
+      align-items: center;
+    }
   }
-  </style>
+}
+
+/* 如果需要调整编辑器容器宽度 */
+.cke_chrome {
+  width: 100% !important;
+}
+</style>

+ 670 - 182
src/views/project/components/dataList.vue

@@ -12,9 +12,11 @@
           <span>{{ getCategoryName(scope.row.categoryId) }}</span>
         </template>
       </el-table-column>
-      <!-- <el-table-column prop="customer.name" label="所属客户" align="left" min-width="180"/> -->
+      <!--  -->
       <el-table-column prop="name" label="项目名称" align="left" />
       <el-table-column prop="createTime" label="创建时间" align="center" />
+      <el-table-column prop="updateTime" label="更新时间" align="center" />
+      <el-table-column prop="" label="完成度" align="center" />
       <el-table-column
         prop="statusName"
         label="项目状态"
@@ -22,8 +24,12 @@
         width="180"
       >
         <template #default="scope">
-          <div v-if="scope.row.status == 5">启用</div>
-          <div v-if="scope.row.status == 6">停用</div>
+          <el-tag
+            :type="scope.row.status === 5 ? 'success' : 'danger'"
+            effect="plain"
+          >
+            {{ scope.row.status === 5 ? "启用" : "停用" }}
+          </el-tag>
         </template>
       </el-table-column>
       <el-table-column label="操作" align="center" fixed="right">
@@ -149,7 +155,13 @@
       width="50%"
       append-to-body
     >
-      <el-table :data="boundTemplateList" style="width: 100%">
+      <el-table
+        :data="boundTemplateList"
+        style="width: 100%"
+        @selection-change="handleBoundTemplateSelect"
+        ref="boundTemplateTable"
+      >
+        <el-table-column type="selection" width="55"></el-table-column>
         <el-table-column prop="id" label="ID" width="80" align="center" />
         <el-table-column prop="title" label="模版名称" align="left" />
         <el-table-column prop="create_time" label="绑定时间" align="center" />
@@ -164,6 +176,16 @@
           </template>
         </el-table-column>
       </el-table>
+      <div class="dialog-footer" style="margin-top: 20px; text-align: right">
+        <el-button
+          type="primary"
+          @click="batchReplace"
+          :loading="batchReplaceLoading"
+          :disabled="selectedBoundTemplates.length === 0"
+        >
+          批量替换
+        </el-button>
+      </div>
     </el-dialog>
     <el-dialog
       title=""
@@ -265,6 +287,8 @@ export default {
       docAttr: {},
       templateLoading: false, // 添加模版加载状态
       boundTemplateLoading: false, // 添加已绑定模版加载状态
+      selectedBoundTemplates: [], // 新增:已选择的绑定模版
+      batchReplaceLoading: false, // 新增:批量替换loading状态
     };
   },
   mounted() {
@@ -337,50 +361,84 @@ export default {
         this.selTemplate(this.currentData); */
       /* }); */
     },
+    // 新增:处理已绑定模版的选择
+    handleBoundTemplateSelect(selection) {
+      this.selectedBoundTemplates = selection;
+    },
+
+    // 新增:批量替换方法
+    async batchReplace() {
+      if (this.selectedBoundTemplates.length === 0) {
+        this.$message.warning("请选择需要替换的模版");
+        return;
+      }
+
+      this.batchReplaceLoading = true;
+      try {
+        // 依次处理每个选中的模版
+        for (const template of this.selectedBoundTemplates) {
+          this.docAttr = template;
+          const TemList = JSON.parse(template.data);
+          await this.searchEx(TemList);
+
+          if (this.matchResults.length > 0) {
+            await this.replaceAll();
+          }
+        }
+
+        this.$message.success("批量替换完成");
+        this.boundTemplatesDialogVisible = false;
+      } catch (error) {
+        console.error("批量替换失败:", error);
+        this.$message.error("批量替换过程中发生错误");
+      } finally {
+        this.batchReplaceLoading = false;
+      }
+    },
 
     searchEx(val) {
-      let allMatches = [];
-      this.TemList = val;
-      this.TemList.forEach((el) => {
-        // 创建临时 div 来解析 HTML 内容
-        const tempDiv = document.createElement("div");
-        tempDiv.innerHTML = el.content;
-        const plainText = tempDiv.textContent || tempDiv.innerText;
-
-        // 分别匹配中文括号和英文括号
-        const patterns = [
-          /【([A-Z]{1,4}[0-9]{0,2})-([A-Z]{1,4}[0-9]{0,2})-([0-9]{2})-([0-9]{2,3})】/g,
-          /\[([A-Z]{1,4}[0-9]{0,2})-([A-Z]{1,4}[0-9]{0,2})-([0-9]{2})-([0-9]{2,3})\]/g,
-        ];
-
-        patterns.forEach((pattern) => {
-          let match;
-          while ((match = pattern.exec(plainText)) !== null) {
-            const fullMatch = match[0];
-            const segments = [match[1], match[2], match[3], match[4]];
-
-            // 验证每个段落的格式
-            const isValid = segments.every((segment) => segment.length > 0);
-
-            if (isValid) {
-              allMatches.push(fullMatch);
+      return new Promise((resolve) => {
+        let allMatches = [];
+        this.TemList = val;
+        this.TemList.forEach((el) => {
+          // 创建临时 div 来解析 HTML 内容
+          const tempDiv = document.createElement("div");
+          tempDiv.innerHTML = el.content;
+          const plainText = tempDiv.textContent || tempDiv.innerText;
+
+          // 分别匹配中文括号和英文括号
+          const patterns = [
+            /【([A-Z]{1,4}[0-9]{0,2})-([A-Z]{1,4}[0-9]{0,2})-([0-9]{2})-([0-9]{2,3})】/g,
+            /\[([A-Z]{1,4}[0-9]{0,2})-([A-Z]{1,4}[0-9]{0,2})-([0-9]{2})-([0-9]{2,3})\]/g,
+          ];
+
+          patterns.forEach((pattern) => {
+            let match;
+            while ((match = pattern.exec(plainText)) !== null) {
+              const fullMatch = match[0];
+              const segments = [match[1], match[2], match[3], match[4]];
+
+              // 验证每个段落的格式
+              const isValid = segments.every((segment) => segment.length > 0);
+
+              if (isValid) {
+                allMatches.push(fullMatch);
+              }
             }
-          }
+          });
         });
-      });
 
-      this.matchResults = [...new Set(allMatches)];
-      console.log("匹配结果:", this.matchResults);
-
-      // 调试用:打印每个匹配的详细信息
-      this.matchResults.forEach((match) => {
-        console.log("找到匹配:", match);
-        // 根据括号类型选择不同的切片方式
-        const content = match.startsWith("【")
-          ? match.slice(1, -1)
-          : match.slice(1, -1);
-        const parts = content.split("-");
-        console.log("分段:", parts);
+        this.matchResults = [...new Set(allMatches)];
+
+        // 调试用:打印每个匹配的详细信息
+        this.matchResults.forEach((match) => {
+          // 根据括号类型选择不同的切片方式
+          const content = match.startsWith("【")
+            ? match.slice(1, -1)
+            : match.slice(1, -1);
+          const parts = content.split("-");
+        });
+        resolve();
       });
     },
 
@@ -422,76 +480,142 @@ export default {
       return params;
     },
 
-    // 单个替换
-    replaceItem(item) {
-      // 去掉方括号
-      const cleanItem = item.replace(/[\[\]]/g, "");
-      this.$confirm(`确认替换 ${item}?`, "提示", {
-        confirmButtonText: "确定",
-        cancelButtonText: "取消",
-        type: "warning",
-      })
-        .then(async () => {
-          try {
-            const params = this.formatRequestParams(cleanItem);
-            const response = await axios.post(
-              `${process.env.VUE_APP_BASE_API}/project-raw-data/batch-content`,
-              params,
-              {
-                headers: {
-                  "Content-Type": "application/json",
-                },
-              }
-            );
+    // 添加表格内容处理方法
+    /*  processTableContent(content, searchItem, replacement) {
+      // 创建临时div解析HTML内容
+      const tempDiv = document.createElement("div");
+      tempDiv.innerHTML = content;
 
-            if (response.status === 200 && response.data) {
-              console.log(response.data.data);
-              // 修改获取替换值的逻辑
-              const replacementValue =
-                response.data.data.input_item_contents[0]?.content;
-
-              if (replacementValue) {
-                this.TemList.forEach((el, index) => {
-                  this.$set(
-                    this.TemList[index],
-                    "content",
-                    el.content.replace(
-                      new RegExp(this.escapeRegExp(item), "g"),
-                      replacementValue
-                    )
-                  );
-                });
-
-                this.matchResults = this.matchResults.filter(
-                  (match) => match !== item
-                );
-                this.onUpload();
-                this.$message.success("替换成功");
-                //this.$emit("close"); // 成功后触发关闭事件
-              } else {
-                this.$message.warning("未获取到替换内容");
-              }
+      // 处理表格内容
+      const tables = tempDiv.getElementsByTagName("table");
+      Array.from(tables).forEach((table) => {
+        const cells = table.getElementsByTagName("td");
+        Array.from(cells).forEach((cell) => {
+          if (cell.textContent.includes(searchItem)) {
+            // 保存原有的样式和类名
+            const cellStyle = cell.getAttribute("style") || "";
+            const cellClass = cell.getAttribute("class") || "";
+
+            // 处理替换内容
+            const processedReplacement = this.parseContent(replacement);
+
+            // 如果替换内容包含表格或图片,需要特殊处理
+            if (this.containsTableOrImage(processedReplacement)) {
+              cell.innerHTML = processedReplacement;
             } else {
-              this.$message.error("获取替换内容失败");
+              // 普通文本替换,保持原有样式
+              cell.innerHTML = cell.innerHTML.replace(
+                new RegExp(this.escapeRegExp(searchItem), "g"),
+                processedReplacement
+              );
             }
-          } catch (error) {
-            console.error("替换失败:", error);
-            this.$message.error(error.response?.data?.message || "替换失败");
+
+            // 恢复样式和类名
+            if (cellStyle) cell.setAttribute("style", cellStyle);
+            if (cellClass) cell.setAttribute("class", cellClass);
           }
-        })
-        .catch(() => {
-          // 取消操作
         });
+      });
+
+      // 处理普通文本内容
+      const textNodes = this.getTextNodes(tempDiv);
+      textNodes.forEach((node) => {
+        if (node.textContent.includes(searchItem)) {
+          const processedReplacement = this.parseContent(replacement);
+          const span = document.createElement("span");
+          span.innerHTML = node.textContent.replace(
+            new RegExp(this.escapeRegExp(searchItem), "g"),
+            processedReplacement
+          );
+          node.parentNode.replaceChild(span, node);
+        }
+      });
+
+      return tempDiv.innerHTML;
+    }, */
+
+    // 解析内容,处理特殊标记
+    parseContent(content) {
+      if (!content) return "";
+
+      let parsed = content;
+
+      // 处理图片标记
+      parsed = parsed.replace(
+        /\[图片:(.*?)\|style=(.*?)\|class=(.*?)\]/g,
+        (match, src, style, className) => {
+          return `<img src="${src}" alt="图片" style="${style}" class="${className}">`;
+        }
+      );
+
+      // 处理表格标记
+      parsed = parsed.replace(
+        /\[表格\|style=(.*?)\|class=(.*?)\]\n([\s\S]*?)(?=\[|$)/g,
+        (match, tableStyle, tableClass, tableContent) => {
+          const rows = tableContent.trim().split("\n");
+          let tableHtml = `<table style="${tableStyle}" class="${tableClass}">`;
+
+          rows.forEach((row) => {
+            tableHtml += "<tr>";
+            const cells = row.split(" | ").map((cell) => {
+              const [content, ...styleInfo] = cell.split("[style=");
+              if (styleInfo.length) {
+                const [style, className] = styleInfo[0]
+                  .slice(0, -1)
+                  .split("|class=");
+                return `<td style="${style}" class="${className}">${content}</td>`;
+              }
+              return `<td>${content}</td>`;
+            });
+            tableHtml += cells.join("") + "</tr>";
+          });
+
+          tableHtml += "</table>";
+          return tableHtml;
+        }
+      );
+
+      // 处理文本样式
+      parsed = parsed.replace(
+        /\[文本:(.*?)\|style=(.*?)\|class=(.*?)\]/g,
+        (match, content, style, className) => {
+          return `<span style="${style}" class="${className}">${content}</span>`;
+        }
+      );
+
+      return parsed;
+    },
+
+    // 检查内容是否包含表格或图片
+    containsTableOrImage(content) {
+      return content.includes("<table") || content.includes("<img");
+    },
+
+    // 获取所有文本节点
+    getTextNodes(node) {
+      const textNodes = [];
+      const walk = document.createTreeWalker(
+        node,
+        NodeFilter.SHOW_TEXT,
+        null,
+        false
+      );
+      let currentNode;
+      while ((currentNode = walk.nextNode())) {
+        textNodes.push(currentNode);
+      }
+      return textNodes;
     },
+
     /*更新  */
     onUpload() {
       let _this = this;
       if (_this.TemList.length <= 0) {
-        _this.$alert("增加组件");
+        _this.$message.error("增加组件");
         return;
       }
       if (_this.docAttr.title == "") {
-        _this.$alert("请填写模版标题");
+        _this.$message.error("请填写模版标题");
         return;
       }
       _this.docAttr.links = JSON.stringify(_this.docAttr.linkProduct);
@@ -504,100 +628,464 @@ export default {
       updateDocument(_this.docAttr).then((res) => {
         if (res.status != 200) return; //更新文档
         _this.docAttr.id = res.data;
-        _this.$alert("模版更新成功");
-        _this.searchArticle();
+        _this.$message.success("模版更新成功");
       });
     },
-    /*批量替换 */
-    replaceAll() {
-      this.loadingAll = true;
+    // 修改 convertTextToHtml 方法
+    /* convertTextToHtml(text) {
+      if (!text) return "";
 
-      this.$confirm(
-        `确认批量替换 ${this.matchResults.length} 项内容?`,
-        "提示",
-        {
-          confirmButtonText: "确定",
-          cancelButtonText: "取消",
-          type: "warning",
-        }
-      )
-        .then(async () => {
-          try {
-            // 清理所有项的方括号
-            const cleanItems = this.matchResults.map((item) =>
-              item.replace(/[\[\]]/g, "")
-            );
-            const params = this.formatRequestParams(cleanItems);
-            const response = await axios.post(
-              `${process.env.VUE_APP_BASE_API}/project-raw-data/batch-content`,
-              params,
-              {
-                headers: {
-                  "Content-Type": "application/json",
-                },
-              }
-            );
+      let html = text;
 
-            if (response.status === 200 && response.data) {
-              // 修改这里,使用与单个替换相同的数据结构
-              const replacementValues =
-                response.data.data.input_item_contents?.map(
-                  (item) => item.content
-                );
-              // 添加验证:检查是否所有值都为 null
-              if (replacementValues.every((value) => value === null)) {
-                this.$message.warning("所有替换内容均无效,请检查数据");
-                return;
-              }
+      // 修改表格处理逻辑
+      html = html.replace(
+        /\[表格\|style=(.*?)\|class=(.*?)\]([\s\S]*?)(?=\[|$)/g,
+        (match, tableStyle, tableClass, tableContent) => {
+          // 移除多余的空行
+          const rows = tableContent
+            .trim()
+            .split("\n")
+            .filter((row) => row.trim());
+          let tableHtml = `<table style="${tableStyle}" class="${tableClass}">`;
 
-              // 添加验证:检查是否存在 null 值
-              const nullCount = replacementValues.filter(
-                (value) => value === null
-              ).length;
-              if (nullCount > 0) {
-                this.$message.warning(
-                  `${nullCount} 项内容无法替换,请检查数据`
-                );
-                return;
-              }
-              if (replacementValues.length > 0) {
-                this.TemList.forEach((el, index) => {
-                  let updatedContent = el.content;
-                  this.matchResults.forEach((item, idx) => {
-                    const replacementValue = replacementValues[idx];
-                    if (replacementValue) {
-                      updatedContent = updatedContent.replace(
-                        new RegExp(this.escapeRegExp(item), "g"),
-                        replacementValue
-                      );
-                    }
-                  });
-                  this.$set(this.TemList[index], "content", updatedContent);
-                });
-                this.onUpload();
-                this.matchResults = [];
-                this.$message.success("批量替换成功");
-                // this.$emit("close"); // 成功后触发关闭事件
+          // 处理表头行
+          if (rows.length > 0) {
+            const headerCells = rows[0].split("|").map((cell) => cell.trim());
+            tableHtml += "<tr>";
+            headerCells.forEach((cell) => {
+              tableHtml += `<th>${cell}</th>`;
+            });
+            tableHtml += "</tr>";
+          }
+
+          // 处理数据行
+          for (let i = 1; i < rows.length; i++) {
+            const cells = rows[i].split("|").map((cell) => cell.trim());
+            tableHtml += "<tr>";
+            cells.forEach((cell) => {
+              // 检查单元格是否包含空格分隔的值
+              const [content, value] = cell.split(/\s+/);
+              if (value) {
+                // 如果有值,创建带有两列的单元格
+                tableHtml += `
+                <td>
+                  <div style="display: flex; justify-content: space-between;">
+                    <span>${content}</span>
+                    <span style="color: #666;">${value}</span>
+                  </div>
+                </td>`;
               } else {
-                this.$message.warning("未获取到替换内容");
+                // 如果只有内容,创建普通单元格
+                tableHtml += `<td>${content}</td>`;
               }
+            });
+            tableHtml += "</tr>";
+          }
+
+          tableHtml += "</table>";
+          return tableHtml;
+        }
+      );
+
+      // 保持原有的图片和文本处理逻辑不变
+      html = html.replace(
+        /\[图片:(.*?)\|style=(.*?)\|class=(.*?)\]/g,
+        (match, src, style, className) => {
+          return `<img src="${src}" alt="图片" style="${style}" class="${className}">`;
+        }
+      );
+
+      html = html.replace(
+        /\[文本:(.*?)\|style=(.*?)\|class=(.*?)\]/g,
+        (match, content, style, className) => {
+          return `<span style="${style}" class="${className}">${content}</span>`;
+        }
+      );
+
+      return html;
+    }, */
+
+    // 添加统一的内容处理方法
+    async processAndReplaceContent(content, originalMatch) {
+      try {
+        const params = this.formatRequestParams(originalMatch);
+        const response = await axios.post(
+          `${process.env.VUE_APP_BASE_API}/project-raw-data/batch-content`,
+          params,
+          {
+            headers: {
+              "Content-Type": "application/json",
+            },
+          }
+        );
+
+        if (response.status === 200 && response.data) {
+          const replacementContent =
+            response.data.data.matched_data[0]?.content;
+          if (replacementContent) {
+            // 处理富文本内容
+            this.TemList.forEach((el, index) => {
+              const processedContent = this.processTableContent(
+                el.content,
+                originalMatch,
+                replacementContent
+              );
+              this.$set(this.TemList[index], "content", processedContent);
+            });
+            return true;
+          }
+        }
+        return false;
+      } catch (error) {
+        console.error("处理内容失败:", error);
+        return false;
+      }
+    },
+
+    // 修改单个替换方法
+    async replaceItem(item) {
+      try {
+        const success = await this.processAndReplaceContent(this.TemList, item);
+        if (success) {
+          this.matchResults = this.matchResults.filter(
+            (match) => match !== item
+          );
+          this.onUpload();
+          this.$message.success("替换成功");
+        } else {
+          this.$message.warning("未获取到替换内容");
+        }
+      } catch (error) {
+        console.error("替换失败:", error);
+        this.$message.error(error.response?.data?.message || "替换失败");
+      }
+    },
+
+    // 修改批量替换方法
+    async replaceAll() {
+      this.batchReplaceLoading = true;
+      try {
+        // 收集所有匹配项
+        const allLocations = this.matchResults.map((item) => {
+          // 清理括号
+          return item.replace(/[\[\]【】]/g, "");
+        });
+
+        // 格式化所有位置的请求参数
+        const params = {
+          template_location: [],
+          middle_layer_location: [],
+          tech_report_location: [],
+          other_location: [],
+        };
+
+        // 遍历所有位置并分类
+        allLocations.forEach((location) => {
+          // 为每种类型创建对应长度的数组
+          const type = this.getDocumentType(location);
+          Object.keys(params).forEach((key) => {
+            if (type === key) {
+              params[key].push(location);
             } else {
-              this.$message.error("获取替换内容失败");
+              params[key].push("");
             }
-          } catch (error) {
-            console.error("批量替换失败:", error);
-            this.$message.error(
-              error.response?.data?.message || "批量替换失败"
-            );
+          });
+        });
+
+        // 发送单个批量请求
+        const response = await axios.post(
+          `${process.env.VUE_APP_BASE_API}/project-raw-data/batch-content`,
+          params,
+          {
+            headers: {
+              "Content-Type": "application/json",
+            },
           }
-        })
-        .catch(() => {
-          // 取消操作
-        })
-        .finally(() => {
-          this.loadingAll = false;
+        );
+
+        if (response.status === 200 && response.data) {
+          const replacementContents = response.data.data.matched_data;
+
+          if (replacementContents && replacementContents.length > 0) {
+            // 更新所有内容
+            this.TemList.forEach((el, index) => {
+              let updatedContent = el.content;
+
+              // 遍历所有替换内容和对应的匹配项
+              this.matchResults.forEach((item, idx) => {
+                const replacement = replacementContents[idx]?.content;
+                if (replacement) {
+                  // 处理表格和其他内容
+                  updatedContent = this.processTableContent(
+                    updatedContent,
+                    item,
+                    replacement
+                  );
+                }
+              });
+
+              // 更新内容
+              this.$set(this.TemList[index], "content", updatedContent);
+            });
+
+            // 更新文档
+            await this.onUpload();
+            this.matchResults = [];
+            this.$message.success("批量替换成功");
+            this.replaceVisible = false;
+          } else {
+            this.$message.warning("未获取到替换内容");
+          }
+        }
+      } catch (error) {
+        console.error("批量替换失败:", error);
+        this.$message.error(error.response?.data?.message || "批量替换失败");
+      } finally {
+        this.batchReplaceLoading = false;
+      }
+    },
+
+    // 辅助方法:获取文档类型
+    getDocumentType(code) {
+      if (code.includes("-RP-")) return "template_location";
+      if (code.includes("-IR-")) return "middle_layer_location";
+      if (code.includes("-WE-")) return "tech_report_location";
+      return "other_location";
+    },
+
+    // 修改处理表格内容的方法,添加对多个替换的支持
+    processTableContent(content, searchItem, replacement) {
+      const tempDiv = document.createElement("div");
+      tempDiv.innerHTML = content;
+
+      // 处理表格内容
+      const tables = tempDiv.getElementsByTagName("table");
+      Array.from(tables).forEach((table) => {
+        // 添加默认表格样式
+        const defaultTableStyle =
+          "border-collapse: collapse; width: 100%; margin: 10px 0;";
+        const defaultCellStyle =
+          "border: 1px solid #dcdfe6; padding: 8px; text-align: left;";
+
+        // 合并现有样式和默认样式
+        const existingStyle = table.getAttribute("style") || "";
+        table.setAttribute("style", `${defaultTableStyle} ${existingStyle}`);
+
+        // 处理所有单元格
+        const cells = table.getElementsByTagName("td");
+        Array.from(cells).forEach((cell) => {
+          const existingCellStyle = cell.getAttribute("style") || "";
+          cell.setAttribute(
+            "style",
+            `${defaultCellStyle} ${existingCellStyle}`
+          );
+
+          if (cell.textContent.includes(searchItem)) {
+            // 保存单元格的类名
+            const cellClass = cell.getAttribute("class") || "";
+
+            // 处理替换内容
+            const processedReplacement = this.parseContent(replacement);
+
+            if (this.containsTableOrImage(processedReplacement)) {
+              cell.innerHTML = processedReplacement;
+            } else {
+              cell.innerHTML = cell.innerHTML.replace(
+                new RegExp(this.escapeRegExp(searchItem), "g"),
+                processedReplacement
+              );
+            }
+
+            // 恢复类名
+            if (cellClass) cell.setAttribute("class", cellClass);
+          }
+        });
+
+        // 处理表头单元格
+        const headerCells = table.getElementsByTagName("th");
+        Array.from(headerCells).forEach((cell) => {
+          const existingHeaderStyle = cell.getAttribute("style") || "";
+          cell.setAttribute(
+            "style",
+            `${defaultCellStyle} background-color: #f5f7fa; font-weight: bold; ${existingHeaderStyle}`
+          );
         });
+      });
+
+      // 处理普通文本内容
+      const textNodes = this.getTextNodes(tempDiv);
+      textNodes.forEach((node) => {
+        if (node.textContent.includes(searchItem)) {
+          const processedReplacement = this.parseContent(replacement);
+          const span = document.createElement("span");
+          span.innerHTML = node.textContent.replace(
+            new RegExp(this.escapeRegExp(searchItem), "g"),
+            processedReplacement
+          );
+          node.parentNode.replaceChild(span, node);
+        }
+      });
+
+      return tempDiv.innerHTML;
+    },
+
+    // 添加样式到 convertTextToHtml 方法
+    convertTextToHtml(text) {
+      if (!text) return "";
+
+      let html = text;
+
+      // 处理特定格式的表格
+      html = html.replace(
+        /\[表格\|style=(.*?)\|class=(.*?)\]([\s\S]*?)(?=\[|$)/g,
+        (match, tableStyle, tableClass, tableContent) => {
+          const rows = tableContent.trim().split("\n");
+          let tableHtml = `<table style="${tableStyle}; border-collapse: collapse;" class="${tableClass}">`;
+
+          rows.forEach((row, index) => {
+            const [label, value] = row.split("|").map((item) => item.trim());
+            if (label) {
+              tableHtml += "<tr>";
+              if (value) {
+                // 如果有值,创建两列的行
+                tableHtml += `
+              <td style="border: 1px solid #000; padding: 8px;">${label}</td>
+              <td style="border: 1px solid #000; padding: 8px;">${value}</td>
+            `;
+              } else {
+                // 如果只有标签,创建单列的行(可能是表头)
+                tableHtml += `
+              <td style="border: 1px solid #000; padding: 8px; font-weight: ${
+                index === 0 ? "bold" : "normal"
+              }">${label}</td>
+            `;
+              }
+              tableHtml += "</tr>";
+            }
+          });
+
+          tableHtml += "</table>";
+          return tableHtml;
+        }
+      );
+
+      // 保持原有的图片和文本处理逻辑不变
+      html = html.replace(
+        /\[图片:(.*?)\|style=(.*?)\|class=(.*?)\]/g,
+        (match, src, style, className) => {
+          return `<img src="${src}" alt="图片" style="${style}" class="${className}">`;
+        }
+      );
+
+      html = html.replace(
+        /\[文本:(.*?)\|style=(.*?)\|class=(.*?)\]/g,
+        (match, content, style, className) => {
+          return `<span style="${style}" class="${className}">${content}</span>`;
+        }
+      );
+
+      return html;
+    },
+
+    /* // 修改替换内容的处理方法
+    async replaceItem(item) {
+      const cleanItem = item.replace(/[\[\]【】]/g, "");
+      try {
+        const params = this.formatRequestParams(cleanItem);
+        const response = await axios.post(
+          `${process.env.VUE_APP_BASE_API}/project-raw-data/batch-content`,
+          params,
+          {
+            headers: {
+              "Content-Type": "application/json",
+            },
+          }
+        );
+
+        if (response.status === 200 && response.data) {
+          const replacementContent =
+            response.data.data.input_item_contents[0]?.content;
+
+          if (replacementContent) {
+            // 处理富文本内容
+            this.TemList.forEach((el, index) => {
+              // 解析表格内容
+              const processedContent = this.processTableContent(
+                el.content,
+                item,
+                replacementContent
+              );
+              this.$set(this.TemList[index], "content", processedContent);
+            });
+
+            this.matchResults = this.matchResults.filter(
+              (match) => match !== item
+            );
+            this.onUpload();
+            this.$message.success("替换成功");
+          } else {
+            this.$message.warning("未获取到替换内容");
+          }
+        }
+      } catch (error) {
+        console.error("替换失败:", error);
+        this.$message.error(error.response?.data?.message || "替换失败");
+      }
     },
+    //批量替换
+    async replaceAll() {
+      this.loadingAll = true;
+      try {
+        const cleanItems = this.matchResults.map((item) =>
+          item.replace(/[\[\]【】]/g, "")
+        );
+        const params = this.formatRequestParams(cleanItems);
+        const response = await axios.post(
+          `${process.env.VUE_APP_BASE_API}/project-raw-data/batch-content`,
+          params,
+          {
+            headers: {
+              "Content-Type": "application/json",
+            },
+          }
+        );
+
+        if (response.status === 200 && response.data) {
+          const replacementContents =
+            response.data.data.input_item_contents?.map((item) => item.content);
+
+          if (replacementContents.length > 0) {
+            this.TemList.forEach((el, index) => {
+              let updatedContent = el.content;
+              this.matchResults.forEach((item, idx) => {
+                console.log(item,idx);
+                const replacementContent = replacementContents[idx];
+                if (replacementContent) {
+                  const regex = new RegExp(this.escapeRegExp(item), "g");
+                  // 使用新添加的 convertTextToHtml 方法
+                  updatedContent = updatedContent.replace(
+                    regex,
+                    this.convertTextToHtml(replacementContent)
+                  );
+                }
+              });
+              this.$set(this.TemList[index], "content", updatedContent);
+            });
+
+            this.onUpload();
+            this.matchResults = [];
+            this.$message.success("批量替换成功");
+          }
+        }
+        return Promise.resolve(); // 确保返回 Promise
+      } catch (error) {
+        console.error("批量替换失败:", error);
+        this.$message.error(error.response?.data?.message || "批量替换失败");
+        return Promise.reject(error);
+      } finally {
+        this.loadingAll = false;
+      }
+    }, */
 
     // 更新 escapeRegExp 方法以支持两种括号类型
     escapeRegExp(string) {

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác