|
@@ -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) {
|