添加期刊引用分析

This commit is contained in:
2026-06-17 09:35:15 +08:00
parent ec4a59eedb
commit ea5695f913
36 changed files with 6129 additions and 103 deletions

View File

@@ -0,0 +1,236 @@
import { htmlToPlainText } from '@/utils/exportTableWord';
function escapeHtml(text) {
return String(text || '')
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
function hasItalicHtmlTag(html) {
return /<i\b|<em\b/i.test(String(html || ''));
}
function stripAvailableAtSuffix(html) {
return String(html || '')
.replace(/\s*Available at:\s*[\s\S]*$/i, '')
.trim();
}
function normalizeReferenceLink(url) {
const raw = String(url || '').trim();
if (!raw) {
return '';
}
if (/^https?:\/\//i.test(raw)) {
return raw;
}
if (/^10\./.test(raw)) {
return 'https://doi.org/' + raw;
}
return raw;
}
function getReferenceIsbnValue(ref) {
if (!ref) {
return '';
}
return String(ref.isbn || ref.doilink || '').trim();
}
function appendHtmlField(parts, html, suffix, options) {
const opts = options || {};
const raw = String(html || '').trim();
if (!raw) {
return;
}
if (opts.italics && !hasItalicHtmlTag(raw)) {
parts.push('<i>' + raw + '</i>');
} else {
parts.push(raw);
}
if (suffix) {
parts.push(escapeHtml(suffix));
}
}
/** 将接口参考文献转为可编辑 HTML支持后续粘贴带 <i> 的内容) */
export function referenceRecordToEditableHtml(ref) {
if (!ref) {
return '';
}
const referType = String(ref.refer_type || 'journal').toLowerCase();
if (!ref.author && ref.refer_frag) {
let html = stripAvailableAtSuffix(ref.refer_frag);
const link = normalizeReferenceLink(ref.doilink || getReferenceIsbnValue(ref));
if (link) {
html += '<br>Available at:<br>' + escapeHtml(ref.doilink || ref.isbn || link);
}
return html;
}
const parts = [];
if (referType === 'book') {
if (ref.author) {
appendHtmlField(parts, ref.author, ' ');
}
if (ref.title) {
appendHtmlField(parts, ref.title, '. ', { italics: true });
}
if (ref.dateno) {
appendHtmlField(parts, ref.dateno, '. ');
}
const isbnValue = getReferenceIsbnValue(ref);
if (isbnValue) {
parts.push('Available at:<br>ISBN: ' + escapeHtml(isbnValue));
}
return parts.join('');
}
if (referType === 'other') {
let html = stripAvailableAtSuffix(ref.refer_frag);
const link = normalizeReferenceLink(ref.doilink);
if (link) {
html += '<br>Available at:<br>' + escapeHtml(ref.doilink || link);
}
return html;
}
if (ref.author) {
appendHtmlField(parts, ref.author, ' ');
}
if (ref.title) {
appendHtmlField(parts, ref.title, '. ');
}
if (ref.joura) {
appendHtmlField(parts, ref.joura, '. ', { italics: !hasItalicHtmlTag(ref.joura) });
}
if (ref.dateno) {
appendHtmlField(parts, ref.dateno, '. ');
}
const doiLink = normalizeReferenceLink(ref.doilink);
if (doiLink) {
parts.push('Available at:<br>' + escapeHtml(ref.doilink || doiLink));
}
return parts.join('');
}
export function buildReferencesEditableHtml(references, labels) {
const list = (references || []).filter(Boolean);
const L = labels || {};
const pageTitle = L.pageTitle || 'References Editor';
const intro = L.intro || '';
const saveTip = L.saveTip || '';
const downloadBtn = L.downloadEdited || 'Download edited HTML';
const refTitle = L.referencesTitle || 'References';
let listHtml = '';
list.forEach(function (ref, index) {
const referType = String(ref.refer_type || 'journal').toLowerCase();
const content = referenceRecordToEditableHtml(ref);
listHtml +=
'<li class="ref-item" contenteditable="true" spellcheck="false"' +
' data-ref-index="' + index + '"' +
' data-ref-type="' + escapeHtml(referType) + '">' +
content +
'</li>';
});
return (
'<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8">' +
'<meta name="viewport" content="width=device-width,initial-scale=1">' +
'<title>' + escapeHtml(pageTitle) + '</title>' +
'<style>' +
'*{box-sizing:border-box}body{margin:0;font-family:Charis SIL,Georgia,"Times New Roman",serif;background:#f5f7fa;color:#303133;line-height:1.5}' +
'.page{max-width:920px;margin:0 auto;padding:20px}' +
'.header{background:#fff;border-radius:8px;padding:16px 20px;margin-bottom:12px;box-shadow:0 1px 4px rgba(0,0,0,.08)}' +
'.header h1{margin:0 0 8px;font-size:20px;text-align:center;font-style:italic;font-weight:bold;color:#008080}' +
'.intro{color:#606266;font-size:14px;margin-bottom:12px;white-space:pre-wrap}' +
'.actions{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}' +
'.btn{border:1px solid #409eff;background:#409eff;color:#fff;border-radius:4px;padding:8px 16px;font-size:13px;cursor:pointer}' +
'.btn.plain{background:#ecf5ff;color:#409eff}' +
'.ref-panel{background:#fff;border-radius:8px;padding:20px 24px;box-shadow:0 1px 4px rgba(0,0,0,.08);font-size:12px}' +
'.ref-list{margin:0;padding-left:1.4em;list-style:decimal}' +
'.ref-item{margin-bottom:10px;padding:4px 0;outline:none;min-height:1.4em}' +
'.ref-item:focus{background:#fafcff;box-shadow:inset 0 0 0 1px #c6e2ff;border-radius:2px}' +
'.ref-item i,.ref-item em{font-style:italic}' +
'</style></head><body><div class="page">' +
'<div class="header"><h1>' + escapeHtml(refTitle) + '</h1>' +
'<div class="intro">' + escapeHtml(intro) + '</div>' +
'<div class="actions">' +
'<button type="button" class="btn" id="download-edited-btn">' + escapeHtml(downloadBtn) + '</button>' +
'</div>' +
'<div class="intro" style="font-size:12px;color:#909399">' + escapeHtml(saveTip) + '</div>' +
'</div>' +
'<div class="ref-panel"><ol class="ref-list" id="ref-list">' +
listHtml +
'</ol></div></div>' +
'<script>(function(){' +
'function serializePage(){var clone=document.documentElement.cloneNode(true);' +
'var btn=clone.querySelector("#download-edited-btn");if(btn){btn.remove();}' +
'return "<!DOCTYPE html>"+clone.outerHTML;}' +
'document.getElementById("download-edited-btn").addEventListener("click",function(){' +
'var html=serializePage();var blob=new Blob([html],{type:"text/html;charset=utf-8"});' +
'var url=URL.createObjectURL(blob);var a=document.createElement("a");' +
'a.href=url;a.download=' + JSON.stringify(L.downloadFileName || 'references-edited.html') + ';' +
'document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(url);});' +
'})();<' +
'/script></body></html>'
);
}
export function parseReferencesEditableHtml(htmlText) {
const raw = String(htmlText || '');
if (!raw.trim()) {
return [];
}
const doc = new DOMParser().parseFromString(raw, 'text/html');
const nodes = doc.querySelectorAll('#ref-list .ref-item, #ref-list li[data-ref-index], ol.ref-list li');
const items = [];
nodes.forEach(function (node) {
const html = (node.innerHTML || '').trim();
if (!html) {
return;
}
items.push({
refer_type: String(node.getAttribute('data-ref-type') || 'journal').toLowerCase(),
html: html
});
});
return items;
}
export function downloadReferencesEditableHtml(references, labels, fileName) {
const html = buildReferencesEditableHtml(references, labels);
const blob = new Blob([html], { type: 'text/html;charset=utf-8' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = fileName || 'references-editor.html';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
export function buildReferencesHtmlLabels(translate) {
const t = translate || function (key) {
return key;
};
return {
pageTitle: t('commonTable.refHtmlPageTitle'),
referencesTitle: t('commonTable.refHtmlReferencesTitle'),
intro: t('commonTable.refHtmlIntro'),
saveTip: t('commonTable.refHtmlSaveTip'),
downloadEdited: t('commonTable.refHtmlDownloadEdited'),
downloadFileName: t('commonTable.refHtmlDownloadFileName')
};
}