/** * Main_List 全文首次出现顺序(与 Edit Content / 稿面 / 底部参考文献列表共用)。 * 正文中 占位处需先插入对应表格段在 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_id(Edit 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 = /]*>[\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' && / { if (!order.includes(id)) order.push(id); }); } else { extractAutociteIdsFromHtmlString(raw).forEach((id) => { if (!order.includes(id)) order.push(id); }); } }); }); return order; }