表格参考文献的预览
This commit is contained in:
169
src/common/js/citeTablePreview.js
Normal file
169
src/common/js/citeTablePreview.js
Normal file
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* 表格弹窗预览:将单元格内空 mycite 转为可见 [n],与 word.vue renderCiteLabels 一致(纯函数,无 Vue 依赖)
|
||||
*/
|
||||
|
||||
export function formatCiteNumbers(nums) {
|
||||
if (!nums || !nums.length) return '';
|
||||
const sorted = [...new Set(nums)].sort((a, b) => a - b);
|
||||
const result = [];
|
||||
let i = 0;
|
||||
while (i < sorted.length) {
|
||||
let j = i;
|
||||
while (j < sorted.length - 1 && sorted[j + 1] === sorted[j] + 1) j++;
|
||||
if (j - i >= 2) {
|
||||
result.push(`${sorted[i]}–${sorted[j]}`);
|
||||
} else {
|
||||
for (let k = i; k <= j; k++) result.push(sorted[k]);
|
||||
}
|
||||
i = j + 1;
|
||||
}
|
||||
return result.join(', ');
|
||||
}
|
||||
|
||||
export function sortAutociteIdsByCiteNumber(ids, citeMap) {
|
||||
const map = citeMap || {};
|
||||
const uniq = [...new Set(ids.map(String))];
|
||||
return uniq.sort((a, b) => {
|
||||
const na = map[a];
|
||||
const nb = map[b];
|
||||
const ha = na != null && na !== '';
|
||||
const hb = nb != null && nb !== '';
|
||||
if (ha && hb) return Number(na) - Number(nb);
|
||||
if (ha) return -1;
|
||||
if (hb) return 1;
|
||||
return String(a).localeCompare(String(b));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} refs - chanFerForm,项含 p_refer_id
|
||||
* @param {Array<string|number>} bodyCiteIdOrder - 正文首次出现顺序;空则按列表行序 1..n
|
||||
*/
|
||||
export function buildCiteMapFromRefs(refs, bodyCiteIdOrder) {
|
||||
const refList = Array.isArray(refs) ? refs : [];
|
||||
const refIdSet = new Set(
|
||||
refList.map((r) => (r && r.p_refer_id != null ? String(r.p_refer_id) : '')).filter(Boolean)
|
||||
);
|
||||
|
||||
let orderedIds = [];
|
||||
if (Array.isArray(bodyCiteIdOrder) && bodyCiteIdOrder.length > 0) {
|
||||
orderedIds = bodyCiteIdOrder.map(String);
|
||||
}
|
||||
|
||||
if (orderedIds.length === 0) {
|
||||
const map = {};
|
||||
refList.forEach((row, idx) => {
|
||||
const key = row && row.p_refer_id != null ? String(row.p_refer_id) : '';
|
||||
if (key) map[key] = idx + 1;
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
const filtered = [];
|
||||
orderedIds.forEach((id) => {
|
||||
if (refIdSet.has(id) && !filtered.includes(id)) filtered.push(id);
|
||||
});
|
||||
const map = {};
|
||||
filtered.forEach((id, idx) => {
|
||||
map[id] = idx + 1;
|
||||
});
|
||||
let next = filtered.length + 1;
|
||||
refList.forEach((r) => {
|
||||
const key = r && r.p_refer_id != null ? String(r.p_refer_id) : '';
|
||||
if (!key || map[key] != null) return;
|
||||
map[key] = next++;
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
function escAttr(s) {
|
||||
return String(s || '')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} html
|
||||
* @param {Array} refs - chanFerForm
|
||||
* @param {Object} citeMap - p_refer_id -> 显示序号
|
||||
*/
|
||||
export function renderCiteLabelsInHtml(html, refs, citeMap) {
|
||||
if (!html || typeof html !== 'string') return html || '';
|
||||
if (!Array.isArray(refs) || refs.length === 0) return html;
|
||||
|
||||
/** 与稿面一致:历史/表格 JSON 中可能为 <autocite> */
|
||||
let h = html.replace(/<\/autocite>/gi, '</mycite>').replace(/<autocite\b/gi, '<mycite');
|
||||
|
||||
const refList = refs;
|
||||
const refMap = refList.reduce((acc, item) => {
|
||||
const key = item && item.p_refer_id != null ? String(item.p_refer_id) : '';
|
||||
if (key) acc[key] = item;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
let normalized = h.replace(/<mycite\b[^>]*\/?>[\s\S]*?(?:<\/mycite>)?/gi, (fullTag) => {
|
||||
const attrMatch = fullTag.match(/\bdata-id\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i);
|
||||
const dataId = (attrMatch && (attrMatch[1] || attrMatch[2] || attrMatch[3])) || '';
|
||||
if (!dataId) return '';
|
||||
return `<mycite data-id="${escAttr(dataId)}"></mycite>`;
|
||||
});
|
||||
const citeGroupRe = /(?:<mycite\s+data-id="([^"]*)"\s*><\/mycite>\s*)+/gi;
|
||||
normalized = normalized.replace(citeGroupRe, (groupMatch) => {
|
||||
const ids = [];
|
||||
const innerRe = /<mycite\s+data-id="([^"]*)"\s*><\/mycite>/gi;
|
||||
let m;
|
||||
while ((m = innerRe.exec(groupMatch)) !== null) {
|
||||
m[1].split(',').forEach((part) => {
|
||||
const id = part.trim();
|
||||
if (id) ids.push(id);
|
||||
});
|
||||
}
|
||||
const sortedAll = sortAutociteIdsByCiteNumber(ids, citeMap);
|
||||
const validIds = sortedAll.filter((id) => refMap[id]);
|
||||
const sortedIds = validIds.length ? sortAutociteIdsByCiteNumber(validIds, citeMap) : [];
|
||||
const map = citeMap || {};
|
||||
|
||||
const parts = sortedIds.map((id) => {
|
||||
const ref = refMap[id];
|
||||
const no = ref ? map[id] : null;
|
||||
const num = no != null && no !== '' ? String(no) : null;
|
||||
return { id, ref, num };
|
||||
});
|
||||
|
||||
const numsForLabel = parts
|
||||
.map((p) => p.num)
|
||||
.filter((n) => n != null && n !== '')
|
||||
.map(Number);
|
||||
const label = numsForLabel.length > 0 ? formatCiteNumbers(numsForLabel) : '';
|
||||
if (!label) {
|
||||
return '';
|
||||
}
|
||||
const dataIdAttr = escAttr(sortedIds.join(','));
|
||||
return `<mycite data-id="${dataIdAttr}">[${label}]</mycite>`;
|
||||
});
|
||||
return normalized.replace(/<mycite[^>]*data-id=""[^>]*>[\s\S]*?<\/mycite>/gi, '');
|
||||
}
|
||||
|
||||
export function applyCiteLabelsToTableRows(rows, refs, citeMap) {
|
||||
if (!Array.isArray(rows) || !rows.length) return rows;
|
||||
if (!Array.isArray(refs) || refs.length === 0) return rows;
|
||||
|
||||
return rows.map((row) => {
|
||||
if (!Array.isArray(row)) return row;
|
||||
const nextRow = row.map((cell) => {
|
||||
if (!cell || typeof cell !== 'object') return cell;
|
||||
const t = cell.text;
|
||||
const lower = typeof t === 'string' ? t.toLowerCase() : '';
|
||||
if (!lower || (!lower.includes('mycite') && !lower.includes('autocite'))) return cell;
|
||||
const next = renderCiteLabelsInHtml(t, refs, citeMap);
|
||||
if (next === t) return cell;
|
||||
return { ...cell, text: next };
|
||||
});
|
||||
// TableUtils.addRowIdToData 会给数组行对象挂 rowId,渲染 oddColor 依赖它,不能在 map 后丢失
|
||||
if (row.rowId != null) {
|
||||
nextRow.rowId = row.rowId;
|
||||
}
|
||||
return nextRow;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user