参考文献的暂存
This commit is contained in:
@@ -412,6 +412,7 @@
|
||||
@openRefSelector="handleOpenRefSelector"
|
||||
:chanFerForm="chanFerForm"
|
||||
:body-cite-id-order="editModalBodyCiteOrder"
|
||||
:table-link-cite-max-map="tableLinkCiteMaxMap"
|
||||
@editorInput="onEditModalEditorInput"
|
||||
v-if="editVisible"
|
||||
ref="commonContent"
|
||||
@@ -1032,6 +1033,49 @@ export default {
|
||||
const order = this.extractAutociteOrderFromMainList(this.Main_List, this.currentContent.am_id, draft);
|
||||
return order.length > 0 ? order : this.articleCiteIdOrder;
|
||||
},
|
||||
/**
|
||||
* mytable(data-id) -> 该表格中已出现引用的最大全局序号
|
||||
* 用于正文链接表格后,后续 [2,3] 按“表内最大序号之后”继续映射(如 33,34)
|
||||
*/
|
||||
tableLinkCiteMaxMap() {
|
||||
const out = {};
|
||||
const list = Array.isArray(this.Main_List) ? this.Main_List : [];
|
||||
if (!list.length) return out;
|
||||
const order = Array.isArray(this.editModalBodyCiteOrder) ? this.editModalBodyCiteOrder : this.articleCiteIdOrder;
|
||||
const citeMap = this.buildDisplayCiteMap(order || []);
|
||||
list.forEach((p) => {
|
||||
if (!p || Number(p.type) !== 2) return;
|
||||
const candidates = this.collectCiteHtmlSourcesForMainListItem(p);
|
||||
const ids = [];
|
||||
candidates.forEach((raw) => {
|
||||
extractAutociteIdsFromHtmlString(raw).forEach((id) => {
|
||||
if (!ids.includes(id)) ids.push(id);
|
||||
});
|
||||
});
|
||||
const numsFromIds = ids
|
||||
.map((id) => Number(citeMap[String(id)]))
|
||||
.filter((n) => !Number.isNaN(n) && n > 0);
|
||||
// 兜底:有些表格仍是纯文本 [34](未转 mycite),这里直接取括号数字最大值参与 max
|
||||
const numsFromPlain = [];
|
||||
candidates.forEach((raw) => {
|
||||
this.extractBracketCiteNumbersFromText(raw).forEach((n) => numsFromPlain.push(n));
|
||||
});
|
||||
const nums = [...numsFromIds, ...numsFromPlain];
|
||||
const max = nums.length ? Math.max(...nums) : 0;
|
||||
if (max <= 0) return;
|
||||
if (p.am_id != null) out[String(p.am_id)] = max;
|
||||
if (p.amt_id != null) out[String(p.amt_id)] = max;
|
||||
if (p.p_main_table_id != null) out[String(p.p_main_table_id)] = max;
|
||||
if (p.table) {
|
||||
if (p.table.amt_id != null) out[String(p.table.amt_id)] = max;
|
||||
if (p.table.am_id != null) out[String(p.table.am_id)] = max;
|
||||
if (p.table.p_main_table_id != null) out[String(p.table.p_main_table_id)] = max;
|
||||
if (p.table.table_id != null) out[String(p.table.table_id)] = max;
|
||||
}
|
||||
});
|
||||
console.log('[tableLinkCiteMaxMap]', out);
|
||||
return out;
|
||||
},
|
||||
/** 表格抽屉内角标:与 Edit Content 同源,按 Main_List + Title/表/Note 合并稿做全文首次出现排序,避免仍用 articleCiteIdOrder 列表下标 [38] */
|
||||
tableModalBodyCiteOrder() {
|
||||
if (!this.threeVisible || !this.lineStyle || this.lineStyle.am_id == null) {
|
||||
@@ -1728,6 +1772,8 @@ export default {
|
||||
pushRows(t.tableContent);
|
||||
}
|
||||
if (typeof t.note === 'string' && t.note.trim()) out.push(t.note);
|
||||
// 兜底:不同接口版本的表体字段名不一致,深度扫描 table 对象里所有可能含引用的字符串字段。
|
||||
this.collectCiteStringsDeep(t, out);
|
||||
return out;
|
||||
}
|
||||
if (typ === 1 && p.image) {
|
||||
@@ -1744,6 +1790,58 @@ export default {
|
||||
if (p && typeof p.content === 'string' && p.content.trim()) out.push(p.content);
|
||||
return out;
|
||||
},
|
||||
/**
|
||||
* 递归收集对象中的字符串字段,覆盖 html/text/content 等异构字段名。
|
||||
* 仅保留看起来可能包含引用信息的片段,避免噪音过大。
|
||||
*/
|
||||
collectCiteStringsDeep(source, out, depth = 0) {
|
||||
if (!source || depth > 6) return;
|
||||
if (typeof source === 'string') {
|
||||
const s = source.trim();
|
||||
if (!s) return;
|
||||
const maybeCite = /<(?:mycite|autocite)\b/i.test(s) || /\[[\d\s,,\-–—]+\]/.test(s);
|
||||
if (maybeCite) out.push(s);
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(source)) {
|
||||
source.forEach((item) => this.collectCiteStringsDeep(item, out, depth + 1));
|
||||
return;
|
||||
}
|
||||
if (typeof source === 'object') {
|
||||
Object.keys(source).forEach((k) => {
|
||||
const v = source[k];
|
||||
this.collectCiteStringsDeep(v, out, depth + 1);
|
||||
});
|
||||
}
|
||||
},
|
||||
extractBracketCiteNumbersFromText(raw) {
|
||||
const out = [];
|
||||
if (!raw || typeof raw !== 'string') return out;
|
||||
const text = raw.replace(/<[^>]+>/g, ' ');
|
||||
const re = /\[([\d\s,,\-–—]+)\]/g;
|
||||
let m;
|
||||
while ((m = re.exec(text)) !== null) {
|
||||
const inner = String(m[1] || '').trim().replace(/,/g, ',');
|
||||
if (!inner) continue;
|
||||
const range = inner.match(/^(\d+)\s*[-–—]\s*(\d+)$/);
|
||||
if (range) {
|
||||
const a = Number(range[1]);
|
||||
const b = Number(range[2]);
|
||||
if (!Number.isNaN(a) && !Number.isNaN(b)) {
|
||||
const lo = Math.min(a, b);
|
||||
const hi = Math.max(a, b);
|
||||
for (let i = lo; i <= hi; i++) out.push(i);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
inner
|
||||
.split(',')
|
||||
.map((x) => parseInt(String(x).trim(), 10))
|
||||
.filter((n) => !Number.isNaN(n) && n > 0)
|
||||
.forEach((n) => out.push(n));
|
||||
}
|
||||
return out;
|
||||
},
|
||||
/** 与 word.vue 一致:从各段 HTML 中按出现顺序收集 autocite(含表格单元格、表题、表注、图题图注) */
|
||||
extractAutociteOrderFromMainList(mainList, draftAmId, draftHtml) {
|
||||
const list = Array.isArray(mainList) ? mainList : [];
|
||||
@@ -2172,12 +2270,21 @@ export default {
|
||||
this.saveContent(newContent, mainId);
|
||||
},
|
||||
handleConfirmLink(selectedMedia) {
|
||||
const targetId = selectedMedia.select.amt_id || selectedMedia.select.ami_id;
|
||||
const select = (selectedMedia && selectedMedia.select) || {};
|
||||
// 不同列表来源字段不一致:表可能是 amt_id / am_id,图可能是 ami_id / am_id
|
||||
const targetId =
|
||||
select.amt_id ||
|
||||
select.ami_id ||
|
||||
select.am_id ||
|
||||
select.p_main_table_id ||
|
||||
select.table_id ||
|
||||
(select.table &&
|
||||
(select.table.amt_id || select.table.am_id || select.table.p_main_table_id || select.table.table_id));
|
||||
const type = selectedMedia.type;
|
||||
const tagName = `my${type}`;
|
||||
let originalContent = selectedMedia.linkData.content;
|
||||
const label = selectedMedia.linkData.label;
|
||||
if (!label || !originalContent) return;
|
||||
if (!label || !originalContent || targetId == null || targetId === '') return;
|
||||
const isAlreadyTagged = label.startsWith('<my') && label.endsWith('>');
|
||||
let newContent = '';
|
||||
if (isAlreadyTagged) {
|
||||
|
||||
@@ -84,6 +84,11 @@ export default {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
/** mytable(data-id) -> 该表内已出现引用的最大全局序号(由父组件计算) */
|
||||
tableLinkCiteMaxMap: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
/** 为 false 时不显示 Ref 工具栏按钮,也不自动在 LateX 后注入 insertRef(用于图表标题等) */
|
||||
showRefButton: {
|
||||
type: Boolean,
|
||||
@@ -357,7 +362,7 @@ export default {
|
||||
if (!body) return { replaced: 0 };
|
||||
let replaced = 0;
|
||||
ed.undoManager.transact(() => {
|
||||
replaced = this._replaceBracketCitesInNode(body, doc, numToId);
|
||||
replaced = this._replaceBracketCitesInDocOrder(body, doc, numToId, { tableOffset: 0 });
|
||||
});
|
||||
this.renderAutociteInEditor(ed);
|
||||
ed.fire('change');
|
||||
@@ -409,23 +414,41 @@ export default {
|
||||
/* ignore */
|
||||
}
|
||||
},
|
||||
_replaceBracketCitesInNode(node, doc, numToId) {
|
||||
/**
|
||||
* 按文档顺序遍历,使「表格链接」后任意后续段落里的 [n] 都能用上该表的最大全局序号偏移。
|
||||
* 旧实现只在同一父节点下、MYTABLE 与后续文本为兄弟时才生效,MYTABLE 在上一段、角标在下一段时会失效。
|
||||
*/
|
||||
_replaceBracketCitesInDocOrder(node, doc, numToId, state) {
|
||||
if (node.nodeType === 3) {
|
||||
return this._processTextNodeForBracketCites(node, doc, numToId);
|
||||
return this._processTextNodeForBracketCites(node, doc, numToId, state.tableOffset);
|
||||
}
|
||||
if (node.nodeType !== 1) return 0;
|
||||
const name = node.nodeName;
|
||||
if (name === 'AUTOCITE' || name === 'WMATH' || name === 'SCRIPT' || name === 'STYLE') {
|
||||
if (name === 'AUTOCITE' || name === 'MYCITE' || name === 'WMATH' || name === 'SCRIPT' || name === 'STYLE') {
|
||||
return 0;
|
||||
}
|
||||
let total = 0;
|
||||
const children = Array.from(node.childNodes);
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
total += this._replaceBracketCitesInNode(children[i], doc, numToId);
|
||||
const isMytable = name === 'MYTABLE' || (node.tagName && String(node.tagName).toLowerCase() === 'mytable');
|
||||
if (isMytable) {
|
||||
let total = 0;
|
||||
Array.from(node.childNodes).forEach((c) => {
|
||||
total += this._replaceBracketCitesInDocOrder(c, doc, numToId, state);
|
||||
});
|
||||
const tid = (node.getAttribute && node.getAttribute('data-id')) || '';
|
||||
const map = this.tableLinkCiteMaxMap || {};
|
||||
const tableMax = Number(map[String(tid)]);
|
||||
console.log('[autolink/mytable]', { tid, tableMax, map });
|
||||
if (!Number.isNaN(tableMax) && tableMax > 0) {
|
||||
state.tableOffset = tableMax;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
let total = 0;
|
||||
Array.from(node.childNodes).forEach((c) => {
|
||||
total += this._replaceBracketCitesInDocOrder(c, doc, numToId, state);
|
||||
});
|
||||
return total;
|
||||
},
|
||||
_processTextNodeForBracketCites(textNode, doc, numToId) {
|
||||
_processTextNodeForBracketCites(textNode, doc, numToId, tableOffset = 0) {
|
||||
const text = textNode.textContent;
|
||||
const re = /\[([\d\s,,\-–—]+)\]/g;
|
||||
let m;
|
||||
@@ -446,7 +469,16 @@ export default {
|
||||
const ids = [];
|
||||
const seen = new Set();
|
||||
nums.forEach((n) => {
|
||||
const id = numToId[n];
|
||||
// 表格链接后的局部序号从 tableMax+1 开始接续:1->max+1, 2->max+2 ...
|
||||
const mappedNo = n > 0 && tableOffset > 0 ? n + tableOffset - 1 : n;
|
||||
const id = numToId[mappedNo];
|
||||
console.log('[autolink/map]', {
|
||||
rawBracket: m[0],
|
||||
n,
|
||||
tableOffset,
|
||||
mappedNo,
|
||||
id
|
||||
});
|
||||
if (id && !seen.has(id)) {
|
||||
seen.add(id);
|
||||
ids.push(id);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
:value="value"
|
||||
:chanFerForm="chanFerForm"
|
||||
:bodyCiteIdOrder="bodyCiteIdOrder"
|
||||
:tableLinkCiteMaxMap="tableLinkCiteMaxMap"
|
||||
:typesettingType="typesettingType"
|
||||
class="paste-area text-container"
|
||||
:toolbar="toolbarConfig"
|
||||
@@ -52,6 +53,11 @@ export default {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
/** mytable(data-id) 对应表格内已出现引用的最大全局序号,用于正文中 “See Table X. And see [2,3]” 的偏移映射 */
|
||||
tableLinkCiteMaxMap: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
/** false:标题等场景不显示 Ref */
|
||||
showRefButton: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
Special note: If the number of authors is 6 or fewer, all authors should be listed.
|
||||
</div>
|
||||
<el-button
|
||||
v-if="canDelete"
|
||||
@click="StepBNext(1)"
|
||||
type="primary"
|
||||
plain
|
||||
@@ -110,7 +111,7 @@
|
||||
|
||||
<span @click="handleContainerClick" style="margin-left: 5px" v-html="getRepeatRefHtml()"> </span>
|
||||
</div>
|
||||
<div class="topBtnBox btns" v-if="chanFerForm.length > 0 && role == 'editor'">
|
||||
<div class="topBtnBox btns" v-if="chanFerForm.length > 0 && role == 'editor' && canDelete">
|
||||
<el-button type="primary" plain @click="selectAllRef">Select all</el-button>
|
||||
<el-button type="success" plain @click="toggleSelection">Select none</el-button>
|
||||
<el-button type="danger" plain @click="deleteSomeRefs" :disabled="multipleSelection.length > 0 ? false : true"
|
||||
@@ -154,11 +155,10 @@
|
||||
class="status ok"
|
||||
:class="scope.row.refer_type == 'journal' ? getJournalDateno(scope.row.dateno, 'status') : ''"
|
||||
v-if="
|
||||
(
|
||||
(scope.row.refer_type == 'journal' && scope.row.doilink != '' && scope.row.cs == 1) ||
|
||||
(scope.row.refer_type == 'book' && scope.row.isbn != '' && scope.row.cs == 1)
|
||||
) && scope.row.retract == 0
|
||||
"
|
||||
((scope.row.refer_type == 'journal' && scope.row.doilink != '' && scope.row.cs == 1) ||
|
||||
(scope.row.refer_type == 'book' && scope.row.isbn != '' && scope.row.cs == 1)) &&
|
||||
scope.row.retract == 0
|
||||
"
|
||||
>
|
||||
<i class="el-icon-circle-check"></i>
|
||||
</span>
|
||||
@@ -173,7 +173,9 @@
|
||||
<!-- journal 形式 -->
|
||||
<div style="text-align: left" v-if="scope.row.refer_type == 'journal'" class="reference-item">
|
||||
<p>
|
||||
{{ scope.row.author }} <span v-html="formatTitle(scope.row.title)"></span>. <em>{{ scope.row.joura }}</em
|
||||
{{ scope.row.author }} <span v-html="formatTitle(scope.row.title)"></span>. <em>{{
|
||||
scope.row.joura
|
||||
}}</em
|
||||
>. <span :class="getJournalDateno(scope.row.dateno, 'title')">{{ scope.row.dateno }}</span
|
||||
>.<br />
|
||||
</p>
|
||||
@@ -181,13 +183,16 @@
|
||||
</div>
|
||||
<!-- book 形式 -->
|
||||
<div style="text-align: left" v-if="scope.row.refer_type == 'book'" class="reference-item">
|
||||
<p>{{ scope.row.author }} <span v-html="formatTitle(scope.row.title)"></span>. {{ scope.row.dateno }}. <br /></p>
|
||||
<p>
|
||||
{{ scope.row.author }} <span v-html="formatTitle(scope.row.title)"></span>. {{
|
||||
scope.row.dateno
|
||||
}}. <br />
|
||||
</p>
|
||||
<a class="doiLink" :href="scope.row.isbn" target="_blank">{{ scope.row.isbn }}</a>
|
||||
</div>
|
||||
<!-- other 形式 -->
|
||||
<p class="wrongLine reference-item" style="text-align: left" v-if="scope.row.refer_type == 'other'">
|
||||
<span v-html="formatTitle(scope.row.refer_frag)"></span>
|
||||
|
||||
</p>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -242,7 +247,7 @@
|
||||
</div>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="bottomBtnBox btns" v-if="chanFerForm.length > 0 && role == 'editor'">
|
||||
<div class="bottomBtnBox btns" v-if="chanFerForm.length > 0 && role == 'editor'&& canDelete">
|
||||
<el-button type="primary" plain @click="selectAllRef">Select all</el-button>
|
||||
<el-button type="success" plain @click="toggleSelection">Select none</el-button>
|
||||
<el-button type="danger" plain @click="deleteSomeRefs" :disabled="multipleSelection.length > 0 ? false : true"
|
||||
@@ -546,6 +551,7 @@ export default {
|
||||
},
|
||||
addLoading: false,
|
||||
editboxVisible: false,
|
||||
canDelete: true,
|
||||
multipleSelection: [] // 多选
|
||||
};
|
||||
},
|
||||
@@ -572,18 +578,19 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
formatTitle(title) {
|
||||
if (!title) return '';
|
||||
// 使用正则匹配,'gi' 表示全局匹配且不区分大小写
|
||||
// \b 确保是完整单词匹配,防止误伤含有这些字母的其他单词
|
||||
const reg = /\b(Retracted|Retraction)\b/gi;
|
||||
|
||||
return title.replace(reg, (match) => {
|
||||
return `<span style="color: red; font-weight: bold;">${match}</span>`;
|
||||
});
|
||||
}
|
||||
,
|
||||
getCurrentRoute() {
|
||||
this.canDelete = !['/articleListEditor_B1'].includes(this.$route.path);
|
||||
},
|
||||
formatTitle(title) {
|
||||
if (!title) return '';
|
||||
// 使用正则匹配,'gi' 表示全局匹配且不区分大小写
|
||||
// \b 确保是完整单词匹配,防止误伤含有这些字母的其他单词
|
||||
const reg = /\b(Retracted|Retraction)\b/gi;
|
||||
|
||||
return title.replace(reg, (match) => {
|
||||
return `<span style="color: red; font-weight: bold;">${match}</span>`;
|
||||
});
|
||||
},
|
||||
getJournalDateno(dateno, type) {
|
||||
if (dateno && typeof dateno === 'string') {
|
||||
const hasInvalidColon = !dateno.includes(':') || (dateno.includes(':') && dateno.split(':').pop().trim() === '');
|
||||
@@ -682,7 +689,7 @@ export default {
|
||||
.then((res) => {
|
||||
if (res.status == 1) {
|
||||
return res.data;
|
||||
}
|
||||
}
|
||||
throw res.msg;
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -697,6 +704,7 @@ export default {
|
||||
return {};
|
||||
},
|
||||
init(e) {
|
||||
this.getCurrentRoute();
|
||||
this.chanFerForm = e;
|
||||
this.bijiao();
|
||||
////console.log('更新更新')
|
||||
|
||||
Reference in New Issue
Block a user