作者堆叠 专刊
This commit is contained in:
674
application/api/view/references/index.html
Normal file
674
application/api/view/references/index.html
Normal file
@@ -0,0 +1,674 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>参考文献堆叠统计</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&family=Fraunces:opsz,wght@9..144,500;9..144,650&display=swap" rel="stylesheet" />
|
||||
<style>
|
||||
:root {
|
||||
--ink: #14212b;
|
||||
--muted: #5c6b76;
|
||||
--line: #d7e0e6;
|
||||
--accent: #0f766e;
|
||||
--accent-soft: #d9f3ef;
|
||||
--warn: #b45309;
|
||||
--danger: #b91c1c;
|
||||
--ok: #047857;
|
||||
--font: "DM Sans", system-ui, sans-serif;
|
||||
--display: "Fraunces", Georgia, serif;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
html, body { margin: 0; height: 100%; }
|
||||
body {
|
||||
font-family: var(--font);
|
||||
color: var(--ink);
|
||||
line-height: 1.45;
|
||||
background:
|
||||
linear-gradient(160deg, rgba(15, 118, 110, 0.18), transparent 42%),
|
||||
linear-gradient(340deg, rgba(20, 33, 43, 0.08), transparent 40%),
|
||||
#cfd8df;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.window {
|
||||
width: min(520px, 100%);
|
||||
height: min(680px, calc(100vh - 32px));
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(20, 33, 43, 0.12);
|
||||
box-shadow:
|
||||
0 24px 48px rgba(20, 33, 43, 0.18),
|
||||
0 2px 0 rgba(255, 255, 255, 0.5) inset;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.titlebar {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 10px 12px 10px 14px;
|
||||
background: linear-gradient(180deg, #f7fafb, #eef3f6);
|
||||
border-bottom: 1px solid var(--line);
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
}
|
||||
.titlebar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
.traffic {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
.traffic i {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
}
|
||||
.traffic .r { background: #ff5f57; }
|
||||
.traffic .y { background: #febc2e; }
|
||||
.traffic .g { background: #28c840; }
|
||||
.titlebar h1 {
|
||||
margin: 0;
|
||||
font-family: var(--display);
|
||||
font-size: 0.98rem;
|
||||
font-weight: 650;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.titlebar .sub {
|
||||
font-size: 0.72rem;
|
||||
color: var(--muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
flex: 0 0 auto;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: #fafcfd;
|
||||
}
|
||||
.start-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 8px;
|
||||
align-items: end;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
color: var(--muted);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
input[type="number"] {
|
||||
width: 100%;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 8px 10px;
|
||||
font: inherit;
|
||||
font-size: 0.9rem;
|
||||
color: var(--ink);
|
||||
background: #fff;
|
||||
outline: none;
|
||||
}
|
||||
input:focus {
|
||||
border-color: #7bbbb4;
|
||||
box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.12);
|
||||
}
|
||||
.btn {
|
||||
appearance: none;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
padding: 8px 14px;
|
||||
font: inherit;
|
||||
font-size: 0.88rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
height: 36px;
|
||||
}
|
||||
.btn:disabled { opacity: .55; cursor: not-allowed; }
|
||||
.btn-primary { background: var(--accent); color: #fff; }
|
||||
|
||||
.error {
|
||||
display: none;
|
||||
margin-top: 8px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 8px;
|
||||
background: #fef2f2;
|
||||
color: var(--danger);
|
||||
border: 1px solid #fecaca;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.error.show { display: block; }
|
||||
|
||||
.body {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
}
|
||||
.results { display: none; }
|
||||
.results.show { display: block; }
|
||||
.placeholder {
|
||||
color: var(--muted);
|
||||
font-size: 0.86rem;
|
||||
text-align: center;
|
||||
padding: 36px 12px;
|
||||
}
|
||||
|
||||
.summary {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.stat {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 8px 10px;
|
||||
background: #fafcfd;
|
||||
}
|
||||
.stat .k { font-size: 0.7rem; color: var(--muted); }
|
||||
.stat .v {
|
||||
font-family: var(--display);
|
||||
font-size: 1.15rem;
|
||||
font-weight: 650;
|
||||
margin-top: 2px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.stat .sub { font-size: 0.72rem; color: var(--muted); margin-top: 2px; }
|
||||
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 1px 7px;
|
||||
border-radius: 999px;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 650;
|
||||
}
|
||||
.pill.ok { background: #dcfce7; color: var(--ok); }
|
||||
.pill.bad { background: #fee2e2; color: var(--danger); }
|
||||
.pill.warn { background: #ffedd5; color: var(--warn); }
|
||||
|
||||
.section-title {
|
||||
font-size: 0.88rem;
|
||||
margin: 14px 0 8px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.section-title .hint {
|
||||
font-size: 0.72rem;
|
||||
font-weight: 500;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.card-list { display: grid; gap: 8px; }
|
||||
.item-card {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
}
|
||||
.item-card h3 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 0.88rem;
|
||||
font-weight: 650;
|
||||
}
|
||||
.meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 10px;
|
||||
color: var(--muted);
|
||||
font-size: 0.76rem;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.meta strong { color: var(--ink); font-weight: 600; }
|
||||
.ratio-bar {
|
||||
height: 5px;
|
||||
border-radius: 999px;
|
||||
background: #e7eef2;
|
||||
overflow: hidden;
|
||||
margin: 6px 0 8px;
|
||||
}
|
||||
.ratio-bar > i {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #0f766e, #14968c);
|
||||
border-radius: inherit;
|
||||
}
|
||||
.ratio-bar.bad > i { background: linear-gradient(90deg, #b91c1c, #dc2626); }
|
||||
|
||||
.ref-chips { display: flex; flex-wrap: wrap; gap: 4px; }
|
||||
.chip {
|
||||
display: inline-flex;
|
||||
padding: 1px 6px;
|
||||
border-radius: 5px;
|
||||
background: var(--accent-soft);
|
||||
color: var(--accent);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.chip.warn { background: #ffedd5; color: var(--warn); }
|
||||
.chip.bad { background: #fee2e2; color: var(--danger); }
|
||||
|
||||
.ref-list {
|
||||
margin: 8px 0 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
}
|
||||
.ref-list li {
|
||||
font-size: 0.74rem;
|
||||
color: var(--muted);
|
||||
padding: 6px 8px;
|
||||
border-radius: 6px;
|
||||
background: #f7fafb;
|
||||
border: 1px solid #e8eef2;
|
||||
max-height: 3.2em;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ref-list .no {
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.ref-list a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ref-list a:hover {
|
||||
color: var(--accent);
|
||||
text-decoration: underline;
|
||||
}
|
||||
.chip a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
.chip a:hover { text-decoration: underline; }
|
||||
.chip.linkable { cursor: pointer; }
|
||||
|
||||
.empty {
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
color: var(--muted);
|
||||
border: 1px dashed var(--line);
|
||||
border-radius: 8px;
|
||||
background: #fafcfd;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.note {
|
||||
margin-top: 6px;
|
||||
font-size: 0.74rem;
|
||||
color: var(--muted);
|
||||
line-height: 1.45;
|
||||
}
|
||||
.foot-meta {
|
||||
margin-top: 12px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid var(--line);
|
||||
font-size: 0.72rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.statusbar {
|
||||
flex: 0 0 auto;
|
||||
padding: 6px 12px;
|
||||
border-top: 1px solid var(--line);
|
||||
background: #f7fafb;
|
||||
font-size: 0.7rem;
|
||||
color: var(--muted);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="window" role="dialog" aria-label="参考文献堆叠统计">
|
||||
<div class="titlebar">
|
||||
<div class="titlebar-left">
|
||||
<div class="traffic" aria-hidden="true"><i class="r"></i><i class="y"></i><i class="g"></i></div>
|
||||
<div>
|
||||
<h1>参考文献堆叠统计</h1>
|
||||
<div class="sub">同作者 >15% · 同刊 >20% · 自引 >10%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toolbar">
|
||||
<div class="start-grid">
|
||||
<div>
|
||||
<label for="pArticleId">p_article_id</label>
|
||||
<input id="pArticleId" type="number" min="1" placeholder="例如 3649" value="{$init_p_article_id|default=0}" />
|
||||
</div>
|
||||
<button class="btn btn-primary" id="btnLoad" type="button">查询</button>
|
||||
</div>
|
||||
<div class="error" id="errorBox"></div>
|
||||
</div>
|
||||
|
||||
<div class="body">
|
||||
<div class="placeholder" id="placeholder">输入 p_article_id 后点击查询</div>
|
||||
<div class="results" id="results">
|
||||
<div class="summary" id="summary"></div>
|
||||
|
||||
<h2 class="section-title">
|
||||
同作者堆叠
|
||||
<span class="hint" id="authorThreshold"></span>
|
||||
<span class="pill" id="authorPill"></span>
|
||||
</h2>
|
||||
<div id="authorSection"></div>
|
||||
|
||||
<h2 class="section-title">
|
||||
同刊堆叠
|
||||
<span class="hint" id="journalThreshold"></span>
|
||||
<span class="pill" id="journalPill"></span>
|
||||
</h2>
|
||||
<div id="journalSection"></div>
|
||||
|
||||
<h2 class="section-title">
|
||||
自引
|
||||
<span class="hint" id="selfThreshold"></span>
|
||||
<span class="pill" id="selfPill"></span>
|
||||
</h2>
|
||||
<div id="selfSection"></div>
|
||||
|
||||
<h2 class="section-title">
|
||||
作者数据问题
|
||||
<span class="hint">缺失 / 不全</span>
|
||||
</h2>
|
||||
<div id="issueSection"></div>
|
||||
|
||||
<p class="foot-meta" id="footMeta"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="statusbar">
|
||||
<span>Stacking Stats</span>
|
||||
<span id="statusText">就绪</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var API_STATS = {$api_stats_js};
|
||||
var input = document.getElementById('pArticleId');
|
||||
var btn = document.getElementById('btnLoad');
|
||||
var errorBox = document.getElementById('errorBox');
|
||||
var results = document.getElementById('results');
|
||||
var placeholder = document.getElementById('placeholder');
|
||||
var statusText = document.getElementById('statusText');
|
||||
|
||||
if (Number(input.value) === 0) input.value = '';
|
||||
|
||||
function showError(msg) {
|
||||
errorBox.textContent = msg || '请求失败';
|
||||
errorBox.classList.add('show');
|
||||
statusText.textContent = '出错';
|
||||
}
|
||||
function clearError() {
|
||||
errorBox.textContent = '';
|
||||
errorBox.classList.remove('show');
|
||||
}
|
||||
function pct(ratio) {
|
||||
return (Number(ratio || 0) * 100).toFixed(1) + '%';
|
||||
}
|
||||
function escapeHtml(s) {
|
||||
return String(s == null ? '' : s)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
function setPill(el, exceeded) {
|
||||
el.className = 'pill ' + (exceeded ? 'bad' : 'ok');
|
||||
el.textContent = exceeded ? '已超阈值' : '未超阈值';
|
||||
}
|
||||
function renderRefs(list) {
|
||||
if (!list || !list.length) return '';
|
||||
var html = '<ul class="ref-list">';
|
||||
list.forEach(function (r) {
|
||||
var text = escapeHtml(r.refer_text || ('p_refer_id=' + r.p_refer_id));
|
||||
var no = '[' + escapeHtml(r.reference_no) + ']';
|
||||
var url = String(r.url || '').trim();
|
||||
if (url) {
|
||||
html += '<li><a href="' + escapeHtml(url) + '" target="_blank" rel="noopener noreferrer" title="打开文献">'
|
||||
+ '<span class="no">' + no + '</span>' + text + '</a></li>';
|
||||
} else {
|
||||
html += '<li><span class="no">' + no + '</span>' + text + '</li>';
|
||||
}
|
||||
});
|
||||
html += '</ul>';
|
||||
return html;
|
||||
}
|
||||
function renderChips(nos, cls, refs) {
|
||||
if (!nos || !nos.length) return '<span class="chip">无</span>';
|
||||
var urlMap = {};
|
||||
(refs || []).forEach(function (r) {
|
||||
if (r && r.reference_no != null && r.url) {
|
||||
urlMap[String(r.reference_no)] = String(r.url);
|
||||
}
|
||||
});
|
||||
return nos.map(function (n) {
|
||||
var url = urlMap[String(n)] || '';
|
||||
var label = '[' + escapeHtml(n) + ']';
|
||||
if (url) {
|
||||
return '<span class="chip linkable ' + (cls || '') + '"><a href="'
|
||||
+ escapeHtml(url) + '" target="_blank" rel="noopener noreferrer" title="打开文献">'
|
||||
+ label + '</a></span>';
|
||||
}
|
||||
return '<span class="chip ' + (cls || '') + '">' + label + '</span>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function renderAuthor(data) {
|
||||
var block = data.same_author_stacking || {};
|
||||
document.getElementById('authorThreshold').textContent = '阈值 > ' + pct(block.threshold);
|
||||
setPill(document.getElementById('authorPill'), !!block.exceeded);
|
||||
var items = block.items || [];
|
||||
var box = document.getElementById('authorSection');
|
||||
if (!items.length) {
|
||||
box.innerHTML = '<div class="empty">未发现超过阈值的同作者堆叠</div>';
|
||||
return;
|
||||
}
|
||||
box.innerHTML = '<div class="card-list">' + items.map(function (it) {
|
||||
return '<div class="item-card">'
|
||||
+ '<h3>' + escapeHtml(it.author_name || '未知作者') + '</h3>'
|
||||
+ '<div class="meta">'
|
||||
+ '<span>引用 <strong>' + escapeHtml(it.cite_count) + '</strong> 条</span>'
|
||||
+ '<span>占比 <strong>' + pct(it.cite_ratio) + '</strong></span>'
|
||||
+ (it.orcid ? '<span>ORCID <strong>' + escapeHtml(it.orcid) + '</strong></span>' : '')
|
||||
+ '</div>'
|
||||
+ '<div class="ratio-bar bad"><i style="width:' + Math.min(100, Number(it.cite_ratio || 0) * 100) + '%"></i></div>'
|
||||
+ '<div class="ref-chips">' + renderChips(it.reference_nos, '', it.references) + '</div>'
|
||||
+ renderRefs(it.references)
|
||||
+ '</div>';
|
||||
}).join('') + '</div>';
|
||||
}
|
||||
|
||||
function renderJournal(data) {
|
||||
var block = data.same_journal_stacking || {};
|
||||
document.getElementById('journalThreshold').textContent = '阈值 > ' + pct(block.threshold);
|
||||
setPill(document.getElementById('journalPill'), !!block.exceeded);
|
||||
var items = block.items || [];
|
||||
var box = document.getElementById('journalSection');
|
||||
if (!items.length) {
|
||||
box.innerHTML = '<div class="empty">未发现超过阈值的同刊堆叠</div>';
|
||||
return;
|
||||
}
|
||||
box.innerHTML = '<div class="card-list">' + items.map(function (it) {
|
||||
return '<div class="item-card">'
|
||||
+ '<h3>' + escapeHtml(it.journal_name || '未知期刊') + '</h3>'
|
||||
+ '<div class="meta">'
|
||||
+ '<span>引用 <strong>' + escapeHtml(it.cite_count) + '</strong> 条</span>'
|
||||
+ '<span>占比 <strong>' + pct(it.cite_ratio) + '</strong></span>'
|
||||
+ '</div>'
|
||||
+ '<div class="ratio-bar bad"><i style="width:' + Math.min(100, Number(it.cite_ratio || 0) * 100) + '%"></i></div>'
|
||||
+ '<div class="ref-chips">' + renderChips(it.reference_nos, '', it.references) + '</div>'
|
||||
+ renderRefs(it.references)
|
||||
+ '</div>';
|
||||
}).join('') + '</div>';
|
||||
}
|
||||
|
||||
function renderSelf(data) {
|
||||
var block = data.self_citation || {};
|
||||
document.getElementById('selfThreshold').textContent = '阈值 > ' + pct(block.threshold);
|
||||
setPill(document.getElementById('selfPill'), !!block.exceeded);
|
||||
var box = document.getElementById('selfSection');
|
||||
var items = block.items || [];
|
||||
var head = '<div class="item-card">'
|
||||
+ '<div class="meta">'
|
||||
+ '<span>自引条数 <strong>' + escapeHtml(block.cite_count || 0) + '</strong></span>'
|
||||
+ '<span>占比 <strong>' + pct(block.cite_ratio) + '</strong></span>'
|
||||
+ '</div>'
|
||||
+ '<div class="ratio-bar ' + (block.exceeded ? 'bad' : '') + '"><i style="width:' + Math.min(100, Number(block.cite_ratio || 0) * 100) + '%"></i></div>'
|
||||
+ '<div class="ref-chips">' + renderChips(block.reference_nos, block.exceeded ? 'bad' : '', items.map(function (it) { return it.reference; }).filter(Boolean)) + '</div>'
|
||||
+ (block.note ? '<p class="note">' + escapeHtml(block.note) + '</p>' : '')
|
||||
+ '</div>';
|
||||
|
||||
if (!items.length) {
|
||||
box.innerHTML = head + '<div class="empty" style="margin-top:8px">无自引命中</div>';
|
||||
return;
|
||||
}
|
||||
box.innerHTML = head + '<div class="card-list" style="margin-top:8px">' + items.map(function (it) {
|
||||
return '<div class="item-card">'
|
||||
+ '<h3>参考文献 [' + escapeHtml(it.reference_no) + ']</h3>'
|
||||
+ '<div class="meta">'
|
||||
+ '<span>本文作者 <strong>' + escapeHtml(it.manuscript_author || '-') + '</strong></span>'
|
||||
+ '<span>匹配文献作者 <strong>' + escapeHtml(it.matched_refer_author || '-') + '</strong></span>'
|
||||
+ (it.manuscript_orcid ? '<span>ORCID <strong>' + escapeHtml(it.manuscript_orcid) + '</strong></span>' : '')
|
||||
+ '</div>'
|
||||
+ (it.reference ? renderRefs([it.reference]) : '')
|
||||
+ '</div>';
|
||||
}).join('') + '</div>';
|
||||
}
|
||||
|
||||
function renderIssues(data) {
|
||||
var issues = data.author_data_issues || {};
|
||||
var box = document.getElementById('issueSection');
|
||||
var items = issues.items || [];
|
||||
var refsForChips = items.map(function (it) { return it.reference; }).filter(Boolean);
|
||||
var head = '<div class="item-card"><div class="meta">'
|
||||
+ '<span>缺失 <strong>' + escapeHtml(issues.missing_count || 0) + '</strong></span>'
|
||||
+ '<span>不全 <strong>' + escapeHtml(issues.incomplete_count || 0) + '</strong></span>'
|
||||
+ '</div>'
|
||||
+ '<div class="ref-chips" style="margin-top:6px">'
|
||||
+ '<span style="font-size:0.7rem;color:var(--muted);margin-right:4px">缺失:</span>'
|
||||
+ renderChips(issues.missing_reference_nos, 'bad', refsForChips)
|
||||
+ '<span style="font-size:0.7rem;color:var(--muted);margin:0 4px 0 8px">不全:</span>'
|
||||
+ renderChips(issues.incomplete_reference_nos, 'warn', refsForChips)
|
||||
+ '</div></div>';
|
||||
|
||||
if (!items.length) {
|
||||
box.innerHTML = head + '<div class="empty" style="margin-top:8px">作者数据无明显问题</div>';
|
||||
return;
|
||||
}
|
||||
box.innerHTML = head + '<div class="card-list" style="margin-top:8px">' + items.map(function (it) {
|
||||
var cls = it.issue_type === 'missing' ? 'bad' : 'warn';
|
||||
return '<div class="item-card">'
|
||||
+ '<h3>[' + escapeHtml(it.reference_no) + '] '
|
||||
+ '<span class="pill ' + cls + '">' + escapeHtml(it.issue_type) + '</span></h3>'
|
||||
+ '<div class="meta">'
|
||||
+ '<span>原因 <strong>' + escapeHtml(it.reason || '-') + '</strong></span>'
|
||||
+ '<span>来源 <strong>' + escapeHtml(it.author_source || '-') + '</strong></span>'
|
||||
+ '<span>作者数 <strong>' + escapeHtml(it.author_count || 0) + '</strong></span>'
|
||||
+ '</div>'
|
||||
+ (it.raw_author ? '<p class="note">raw_author: ' + escapeHtml(it.raw_author) + '</p>' : '')
|
||||
+ (it.reference ? renderRefs([it.reference]) : '')
|
||||
+ '</div>';
|
||||
}).join('') + '</div>';
|
||||
}
|
||||
|
||||
function renderAll(data) {
|
||||
var authorExceeded = !!(data.same_author_stacking && data.same_author_stacking.exceeded);
|
||||
var journalExceeded = !!(data.same_journal_stacking && data.same_journal_stacking.exceeded);
|
||||
var selfExceeded = !!(data.self_citation && data.self_citation.exceeded);
|
||||
var issues = data.author_data_issues || {};
|
||||
|
||||
document.getElementById('summary').innerHTML =
|
||||
'<div class="stat"><div class="k">参考文献总数</div><div class="v">' + escapeHtml(data.total_references || 0) + '</div></div>'
|
||||
+ '<div class="stat"><div class="k">同作者堆叠</div><div class="v">' + ((data.same_author_stacking && data.same_author_stacking.items) || []).length + '</div><div class="sub">' + (authorExceeded ? '已超阈值' : '未超阈值') + '</div></div>'
|
||||
+ '<div class="stat"><div class="k">同刊堆叠</div><div class="v">' + ((data.same_journal_stacking && data.same_journal_stacking.items) || []).length + '</div><div class="sub">' + (journalExceeded ? '已超阈值' : '未超阈值') + '</div></div>'
|
||||
+ '<div class="stat"><div class="k">自引 / 作者问题</div><div class="v">' + escapeHtml((data.self_citation && data.self_citation.cite_count) || 0) + ' / ' + escapeHtml((issues.missing_count || 0) + (issues.incomplete_count || 0)) + '</div><div class="sub">' + (selfExceeded ? '自引已超阈值' : '自引未超阈值') + '</div></div>';
|
||||
|
||||
renderAuthor(data);
|
||||
renderJournal(data);
|
||||
renderSelf(data);
|
||||
renderIssues(data);
|
||||
|
||||
document.getElementById('footMeta').textContent =
|
||||
'p_article_id=' + (data.p_article_id || '-')
|
||||
+ ' · article_id=' + (data.article_id || '-')
|
||||
+ ' · ' + (data.computed_at || '-');
|
||||
|
||||
placeholder.style.display = 'none';
|
||||
results.classList.add('show');
|
||||
statusText.textContent = '共 ' + (data.total_references || 0) + ' 条 · ' + (data.computed_at || '');
|
||||
}
|
||||
|
||||
function load() {
|
||||
clearError();
|
||||
var id = parseInt(input.value, 10) || 0;
|
||||
if (id <= 0) {
|
||||
showError('请填写有效的 p_article_id');
|
||||
return;
|
||||
}
|
||||
btn.disabled = true;
|
||||
btn.textContent = '…';
|
||||
statusText.textContent = '查询中';
|
||||
|
||||
var url = API_STATS + (API_STATS.indexOf('?') >= 0 ? '&' : '?') + 'p_article_id=' + encodeURIComponent(id);
|
||||
fetch(url, {
|
||||
method: 'GET',
|
||||
headers: { 'Accept': 'application/json' },
|
||||
credentials: 'same-origin'
|
||||
}).then(function (res) {
|
||||
return res.json();
|
||||
}).then(function (json) {
|
||||
if (!json || Number(json.code) !== 0) {
|
||||
throw new Error((json && json.msg) || '接口返回失败');
|
||||
}
|
||||
renderAll(json.data || {});
|
||||
try {
|
||||
var u = new URL(window.location.href);
|
||||
u.searchParams.set('p_article_id', String(id));
|
||||
window.history.replaceState({}, '', u.toString());
|
||||
} catch (e) {}
|
||||
}).catch(function (err) {
|
||||
results.classList.remove('show');
|
||||
placeholder.style.display = '';
|
||||
showError(err.message || String(err));
|
||||
}).finally(function () {
|
||||
btn.disabled = false;
|
||||
btn.textContent = '查询';
|
||||
});
|
||||
}
|
||||
|
||||
btn.addEventListener('click', load);
|
||||
input.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter') load();
|
||||
});
|
||||
|
||||
if (parseInt(input.value, 10) > 0) load();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user