Merge branch 'master' of https://git.nuttyreading.com/wangjinlei/tougao_web into Editorial-Board

This commit is contained in:
2025-12-24 16:22:22 +08:00
7 changed files with 610 additions and 786 deletions

View File

@@ -1149,9 +1149,9 @@ a {
mso-border-top-alt: none !important; mso-border-top-alt: none !important;
border-bottom: none !important; border-bottom: none !important;
mso-border-bottom-alt: none !important; mso-border-bottom-alt: none !important;
border: 1px dashed #dcdfe6 !important; /* border: 1px dashed #dcdfe6 !important;
border-left: 1px dashed #dcdfe6 !important; border-left: 1px dashed #dcdfe6 !important;
border-right: 1px dashed #dcdfe6 !important; border-right: 1px dashed #dcdfe6 !important; */
word-break: keep-all !important; word-break: keep-all !important;
/* text-align: justify !important; */ /* text-align: justify !important; */
} }
@@ -1283,7 +1283,7 @@ a {
} }
.word-container table tbody tr td { .word-container table tbody tr td {
text-align: left !important; text-align: center !important;
border-left: none !important; border-left: none !important;
mso-border-left-alt: none !important; mso-border-left-alt: none !important;
border-right: none !important; border-right: none !important;
@@ -1292,9 +1292,9 @@ a {
mso-border-top-alt: none !important; mso-border-top-alt: none !important;
border-bottom: none !important; border-bottom: none !important;
mso-border-bottom-alt: none !important; mso-border-bottom-alt: none !important;
border: 1px dashed #dcdfe6 !important; /* border: 1px dashed #dcdfe6 !important;
border-left: 1px dashed #dcdfe6 !important; border-left: 1px dashed #dcdfe6 !important;
border-right: 1px dashed #dcdfe6 !important; border-right: 1px dashed #dcdfe6 !important; */
word-break: keep-all !important; word-break: keep-all !important;
white-space: pre-wrap !important; white-space: pre-wrap !important;
/* text-align: justify !important; */ /* text-align: justify !important; */

102
src/common/js/TableUtils.js Normal file
View File

@@ -0,0 +1,102 @@
/**
* 表格数据处理工具
*/
export const TableUtils = {
/**
* 判断是否为表头行
* @param {number} rowIndex
* @param {Array} table
*/
isHeaderRow(rowIndex, table) {
if (!table || table.length === 0) return false;
const head = table[0];
// 健壮性检查确保第一行第一个单元格存在且有rowspan
const headerSpan = (head && head[0] && head[0].rowspan) ? head[0].rowspan : 1;
return rowIndex < headerSpan;
},
/**
* 拆分表头和表体
*/
splitTable(tableList) {
if (!Array.isArray(tableList) || tableList.length === 0) {
return { header: [], content: [] };
}
const header = [];
const content = [];
let cellIdCounter = 0;
tableList.forEach((row, rowIndex) => {
if (Array.isArray(row)) {
row.forEach((cell) => {
if (cell && typeof cell === 'object') {
cell.cellId = `cell-${cellIdCounter++}`;
}
});
}
if (this.isHeaderRow(rowIndex, tableList)) {
header.push(row);
} else {
content.push(row);
}
});
return { header, content };
},
/**
* 处理合并单元格后的逻辑行 ID用于斑马纹等
*/
addRowIdToData(content) {
if (!content || content.length === 0) return { rowData: [], rowIds: [] };
const data = JSON.parse(JSON.stringify(content));
const rowIdMap = {};
const usedRows = new Set();
let idCounter = 0;
// 1. 建立逻辑行映射
for (let i = 0; i < data.length; i++) {
if (usedRows.has(i)) continue;
const rowId = `row-${idCounter++}`;
rowIdMap[i] = rowId;
usedRows.add(i);
const row = data[i];
for (let j = 0; j < row.length; j++) {
const cell = row[j];
if (cell && cell.rowspan > 1) {
for (let k = 1; k < cell.rowspan; k++) {
const nextRowIndex = i + k;
if (nextRowIndex < data.length && !rowIdMap[nextRowIndex]) {
rowIdMap[nextRowIndex] = rowId;
usedRows.add(nextRowIndex);
}
}
}
}
}
// 2. 注入 rowId 并提取唯一 ID 列表
const seenIds = [];
data.forEach((row, i) => {
const rowId = rowIdMap[i];
row.rowId = rowId; // 直接赋值给行对象
row.forEach(cell => {
if (cell) cell.rowId = rowId;
});
if (rowId && !seenIds.includes(rowId)) {
seenIds.push(rowId);
}
});
// 取奇数或偶数 ID 用于斑马纹(根据你的需求 index % 2 === 0
const rowIds = seenIds.filter((_, index) => index % 2 === 0);
return { rowData: data, rowIds };
}
};

View File

@@ -1307,16 +1307,19 @@ export default {
}, },
handleImageAdd(type) { handleImageAdd(type) {
this.picStyle = { note: '', picUrl: '', title: '' }; this.picStyle = { note: '', picUrl: '', title: '' };
this.picStyle1 = { note: '', picUrl: '', title: '' };
this.picStyle.visiTitle = 'Add Figure'; this.picStyle.visiTitle = 'Add Figure';
this.pictVisible = true; this.pictVisible = true;
}, },
handleTableAdd(type) { handleTableAdd(type) {
this.lineStyle = { note: '', table_data: '', html_data: '' }; this.lineStyle = { note: '', table_data: '', html_data: '' };
this.lineStyle1 = { note: '', table_data: '', html_data: '' };
this.lineStyle.visiTitle = 'Add Table'; this.lineStyle.visiTitle = 'Add Table';
this.threeVisible = true; this.threeVisible = true;
}, },
addUploadWordTable(data) { addUploadWordTable(data) {
this.lineStyle = { note: '', table: data.table_data, html_data: data.html_data }; this.lineStyle = { note: '', table: data.table_data, html_data: data.html_data };
this.lineStyle1 = { note: '', table: data.table_data, html_data: data.html_data };
this.lineStyle.visiTitle = 'Add Table'; this.lineStyle.visiTitle = 'Add Table';
this.threeVisible = true; this.threeVisible = true;
@@ -1330,13 +1333,18 @@ export default {
this.pictVisible = true; this.pictVisible = true;
} else if (type == 'table') { } else if (type == 'table') {
this.lineStyle = {}; this.lineStyle = {};
this.lineStyle = { this.lineStyle1 = {};
// 1. 提取处理逻辑
const formattedData = {
...data, ...data,
table: JSON.parse(data.table_data), table: JSON.parse(data.table_data),
html_data: data.html_data, // 如果 data 中已经包含了 html_data, note, title且不需要特殊处理
note: data.note, // 解构赋值 (...data) 其实已经把它们带进来了。
title: data.title };
};
// 2. 统一赋值
this.lineStyle = formattedData;
this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指向不同引用(如果需要独立修改)
this.lineStyle.visiTitle = 'Edit Table'; this.lineStyle.visiTitle = 'Edit Table';
this.threeVisible = true; this.threeVisible = true;
} }

View File

@@ -135,6 +135,9 @@
<template slot="comment"> <template slot="comment">
<div style="" class="commentList annotations"></div> <div style="" class="commentList annotations"></div>
</template> </template>
<template slot="refrences">
<div style="" class="" main-id="References">222</div>
</template>
</common-word> </common-word>
</div> </div>
</div> </div>
@@ -603,6 +606,7 @@ export default {
}, },
methods: { methods: {
async copyArray(data) { async copyArray(data) {
try { try {
// 将数组内容转换为字符串,使用换行符分隔 // 将数组内容转换为字符串,使用换行符分隔
@@ -1524,7 +1528,7 @@ export default {
for (let i = 0; i < this.Main_List.length; i++) { for (let i = 0; i < this.Main_List.length; i++) {
this.Main_List[i].text = this.Main_List[i].content; this.Main_List[i].text = this.Main_List[i].content;
this.Main_List[i].getnum = 0; this.Main_List[i].getnum = 0;
// this.Main_List[i].checked = false;
} }
// setTimeout(async () => { // setTimeout(async () => {

View File

@@ -1,26 +1,20 @@
<template> <template>
<div class="ManuscirptList">
<div class="arrlist">
<ul class="catalogue-ul">
<li class="catalogue-li">
<div <div
style="" @click="goToListComment(item.am_id,'content')"
class="ManuscirptList"
>
<div
style=""
class="arrlist"
>
<ul style="width: 100%; height: auto">
<li >
<div style="">
<div @click="goToListComment(item.am_id,'content')"
v-for="(item, index) in catalogueList" v-for="(item, index) in catalogueList"
:key="index"
:class="['catalogue-item', 'level-' + item.level]" :class="['catalogue-item', 'level-' + item.level]"
style=""
> >
<div class="title-content-wrapper">
<div class="title-content" v-html="item.content"></div> <div class="title-content" v-html="item.content"></div>
</div> </div>
</div> </div>
</li> </li>
</ul> </ul>
@@ -86,7 +80,7 @@ export default {
this.catalogueList = this.content.filter(item => { this.catalogueList = this.content.filter(item => {
return item.is_h1 == 1 || item.is_h2 == 1 || item.is_h3 == 1; return item.is_h1 == 1 || item.is_h2 == 1 ;
}).map(item => { }).map(item => {
// 增加一个 level 字段进行标记 // 增加一个 level 字段进行标记
let level = 1; let level = 1;
@@ -98,8 +92,8 @@ this.catalogueList = this.content.filter(item => {
}; };
}) })
this.catalogueList=[...this.catalogueList,{ this.catalogueList=[...this.catalogueList,{
am_id:'reference', am_id:'References',
content:'<b><i>References</i></b>', content:'References',
level:1, level:1,
}] }]
@@ -477,58 +471,147 @@ console.log(this.catalogueList,'catalogueList')
</script> </script>
<style scoped> <style scoped>
.ManuscirptList{ .ManuscirptList {
padding-top: 20px; height: 100%;
padding: 0;
box-sizing: border-box; box-sizing: border-box;
background: #f5f5f5 !important; background-color: #f5f5f5 !important; /* Word 导航栏通常是白色的 */
border-right: 1px solid #e1e1e1;
display: flex;
flex-direction: column;
} }
.catalogue-item{
width: 90%; /* 模拟 Word 导航标题 */
white-space: nowrap; /* 强制不换行 */ .word-navigation-header {
overflow: hidden; /* 隐藏超出部分 */ padding: 12px 16px 0 16px;
text-overflow: ellipsis; /* 超出部分显示... */
box-sizing: border-box;
font-size: 14px; font-size: 14px;
color: rgb(51, 51, 51); color: #333;
font-family: "Segoe UI", "Microsoft YaHei", sans-serif;
}
.word-tabs {
display: flex;
margin-top: 10px;
border-bottom: 1px solid #e1e1e1;
}
.word-tabs span {
padding: 4px 12px;
font-size: 12px;
color: #666;
cursor: pointer; cursor: pointer;
padding: 4px 0px; position: relative;
margin-left: 16px; }
font-family: "Charis SIL";
word-wrap: break-word; .word-tabs span.active {
color: #2b579a; /* Word 经典蓝色 */
font-weight: bold;
}
.word-tabs span.active::after {
content: "";
position: absolute;
bottom: -1px;
left: 0;
right: 0;
height: 2px;
background-color: #2b579a;
}
/* 列表容器 */
.arrlist {
flex: 1;
overflow-y: auto;
padding-top: 8px;
}
.catalogue-ul {
width: 100%;
list-style: none;
padding: 0;
margin: 0;
}
/* 基础条目样式 - 模拟 Word 悬浮效果 */
.catalogue-item {
position: relative;
width: 100%;
padding: 6px 16px;
cursor: pointer;
display: flex;
align-items: flex-start;
box-sizing: border-box;
transition: background-color 0.1s;
}
.catalogue-item:hover {
background-color: #eff3f9; /* Word 淡淡的选中蓝 */
}
/* Word 样式的左侧小箭头 */
.word-icon-arrow {
width: 0;
height: 0;
border-left: 5px solid #666;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
margin-right: 8px;
margin-top: 6px;
}
.title-content-wrapper {
flex: 1;
overflow: hidden; overflow: hidden;
} }
.title-content,
.title-content span, /* 重点Word 常用排版字体 */
.title-content b { .title-content {
display: inline; /* 强制内部标签不换行 */ font-weight: bold !important;
font-weight: inherit; /* 继承父级的粗细设置,或者根据需要自定 */ font-style: normal !important;
font-size: inherit; font-size: 15px;
} line-height: 1.3;
.catalogue-item:hover { white-space: nowrap;
background-color: #e6f7ff; overflow: hidden;
color: #1890ff; text-overflow: ellipsis;
/* Times New Roman 增加学术感 */
font-family: "Times New Roman", "Charis SIL", "serif";
color: #333;
} }
/* 一级标题:加粗,无缩进 */ /* 保持 v-html 内部样式 */
::v-deep .title-content b { font-weight: bold; }
::v-deep .title-content i { font-style: italic; }
/* --- Word 风格的层级缩进 --- */
/* 1级标题 */
.level-1 { .level-1 {
font-weight: bold; padding-left: 16px;
/* font-style: italic; */
padding-left: 0;
/* margin-top: 8px; */
} }
/* 级标题:标准字体,缩进 20px */ /* 2级标题 */
.level-2 { .level-2 {
padding-left: 36px;
padding-left: 20px; }
color: #555; .level-2 .title-content {
font-size: 14px;
} }
/* 级标题:稍细字体,缩进 40px */ /* 3级标题 */
.level-3 { .level-3 {
padding-left: 56px;
}
.level-3 .title-content {
font-size: 14px;
color: #666;
}
padding-left: 40px; /* 隐藏滚动条 */
color: #888; .arrlist::-webkit-scrollbar {
font-style: italic; /* 模仿 Word 的一些三级样式 */ width: 5px;
}
.arrlist::-webkit-scrollbar-thumb {
background: #cdcdcd;
} }
</style> </style>

View File

@@ -9,7 +9,10 @@ import htmlDocx from 'html-docx-js/dist/html-docx.js';
import { Document, Packer, PageOrientation, Paragraph, TextRun } from 'docx'; // 引入 docx.js import { Document, Packer, PageOrientation, Paragraph, TextRun } from 'docx'; // 引入 docx.js
import html2canvas from 'html2canvas'; import html2canvas from 'html2canvas';
const tableStyle = ` b span{ const tableStyle = `*{
font-family: 'Charis SIL';
}
b span{
font-weight: bold !important; font-weight: bold !important;
} }
i span{ i span{

File diff suppressed because it is too large Load Diff