Files
tougao_web/src/utils/autociteMainListOrder.js
2026-04-09 11:52:33 +08:00

162 lines
6.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Main_List 全文首次出现顺序(与 Edit Content / 稿面 / 底部参考文献列表共用)。
* 正文中 <mytable> 占位处需先插入对应表格段在 Main_List 中的表题/表体/表注引用,再计其后的 mycite。
*/
import { extractAutociteIdsFromHtmlString } from '@/utils/autociteHtml.js';
export function 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) => collectCiteStringsDeep(item, out, depth + 1));
return;
}
if (typeof source === 'object') {
Object.keys(source).forEach((k) => {
const v = source[k];
collectCiteStringsDeep(v, out, depth + 1);
});
}
}
export function collectCiteHtmlSourcesForMainListItem(p, draftAmId, draftHtml) {
if (draftAmId != null && p && p.am_id == draftAmId && draftHtml != null) {
return [draftHtml];
}
const out = [];
const typ = p != null && p.type != null ? Number(p.type) : NaN;
if (typ === 2 && p.table) {
const t = p.table;
if (typeof t.title === 'string' && t.title.trim()) out.push(t.title);
if (typeof t.html_data === 'string' && t.html_data.trim()) {
out.push(t.html_data);
} else {
const pushRows = (rows) => {
if (!Array.isArray(rows)) return;
rows.forEach((row) => {
if (!Array.isArray(row)) return;
row.forEach((cell) => {
if (cell && typeof cell.text === 'string' && cell.text) out.push(cell.text);
});
});
};
pushRows(t.tableHeader);
pushRows(t.tableContent);
}
if (typeof t.note === 'string' && t.note.trim()) out.push(t.note);
collectCiteStringsDeep(t, out);
return out;
}
if (typ === 1 && p.image) {
const img = p.image;
if (typeof img.title === 'string' && img.title.trim()) out.push(img.title);
if (typeof img.note === 'string' && img.note.trim()) out.push(img.note);
if (out.length === 0) {
if (p && typeof p.content === 'string' && p.content.trim()) out.push(p.content);
if (p && typeof p.text === 'string' && p.text.trim()) out.push(p.text);
}
return out;
}
if (p && typeof p.text === 'string' && p.text.trim()) out.push(p.text);
if (p && typeof p.content === 'string' && p.content.trim()) out.push(p.content);
return out;
}
export function findMainListTableByLinkId(mainList, rawId) {
if (rawId == null || String(rawId).trim() === '') return null;
const sid = String(rawId).trim();
const list = Array.isArray(mainList) ? mainList : [];
return (
list.find((p) => {
if (!p || Number(p.type) !== 2) return false;
if (String(p.amt_id) === sid) return true;
if (String(p.am_id) === sid) return true;
if (p.p_main_table_id != null && String(p.p_main_table_id) === sid) return true;
if (p.table && p.table.amt_id != null && String(p.table.amt_id) === sid) return true;
if (p.table && p.table.am_id != null && String(p.table.am_id) === sid) return true;
if (p.table && p.table.p_main_table_id != null && String(p.table.p_main_table_id) === sid)
return true;
if (p.table && p.table.table_id != null && String(p.table.table_id) === sid) return true;
return false;
}) || null
);
}
/**
* @param {object} [options]
* @param {number|string|null} [options.tableDraftAmId] 正在编辑的表格段 am_idEdit Table 抽屉)
* @param {string|null} [options.tableDraftHtml] 与该表对应的 Title+表体+Note 合并稿;正文里 mytable 展开到该表时用此稿替代 Main_List 已存内容,使「全文 mytable 之前」的序号与抽屉内一致
*/
export function extractAutociteOrderFromDraftHtmlWithInlineTables(html, mainList, options = {}) {
const order = [];
const pushUnique = (id) => {
if (id && !order.includes(id)) order.push(id);
};
const pushFromRaw = (raw) => {
extractAutociteIdsFromHtmlString(raw).forEach((id) => pushUnique(id));
};
if (!html || typeof html !== 'string') return order;
const re = /<mytable\b[^>]*>[\s\S]*?<\/mytable>/gi;
let last = 0;
let m;
while ((m = re.exec(html)) !== null) {
const before = html.slice(last, m.index);
if (before) pushFromRaw(before);
const openEnd = html.indexOf('>', m.index) + 1;
const openTag = html.slice(m.index, openEnd);
const dm = openTag.match(/\bdata-id\s*=\s*["']([^"']*)["']/i);
const tid = dm ? String(dm[1]).trim() : '';
const tableP = findMainListTableByLinkId(mainList, tid);
if (tableP) {
const useDraft =
options.tableDraftAmId != null &&
options.tableDraftHtml != null &&
String(tableP.am_id) === String(options.tableDraftAmId);
const sources = useDraft
? [options.tableDraftHtml]
: collectCiteHtmlSourcesForMainListItem(tableP, null, null);
sources.forEach((raw) => pushFromRaw(raw));
}
last = m.index + m[0].length;
}
const after = html.slice(last);
if (after) pushFromRaw(after);
return order;
}
export function extractAutociteOrderFromMainList(mainList, draftAmId, draftHtml, options = {}) {
const list = Array.isArray(mainList) ? mainList : [];
const order = [];
list.forEach((p) => {
if (draftAmId != null && p && p.am_id == draftAmId && draftHtml != null) {
extractAutociteOrderFromDraftHtmlWithInlineTables(draftHtml, list, options).forEach((id) => {
if (!order.includes(id)) order.push(id);
});
return;
}
const candidates = collectCiteHtmlSourcesForMainListItem(p, draftAmId, draftHtml);
candidates.forEach((raw) => {
if (typeof raw === 'string' && /<mytable\b/i.test(raw)) {
extractAutociteOrderFromDraftHtmlWithInlineTables(raw, list, options).forEach((id) => {
if (!order.includes(id)) order.push(id);
});
} else {
extractAutociteIdsFromHtmlString(raw).forEach((id) => {
if (!order.includes(id)) order.push(id);
});
}
});
});
return order;
}