Add DICOM UPP registration workspace
This commit is contained in:
@@ -8,6 +8,7 @@ const app = {
|
||||
algorithmModel: "",
|
||||
dicomPart: "",
|
||||
viewerUrl: "http://127.0.0.1:8107",
|
||||
registrationUrl: "http://127.0.0.1:8109",
|
||||
searchTimer: null,
|
||||
offset: 0,
|
||||
limit: 20,
|
||||
@@ -171,6 +172,7 @@ function assetSummary(row) {
|
||||
|
||||
function setDbStatus(data) {
|
||||
app.viewerUrl = data.viewer_url || app.viewerUrl;
|
||||
app.registrationUrl = data.registration_url || app.registrationUrl;
|
||||
const pill = $("dbStatus");
|
||||
if (data.database?.ok) {
|
||||
pill.textContent = `${data.database.database} 已连接`;
|
||||
@@ -187,6 +189,7 @@ function renderMetrics(counts = {}) {
|
||||
["DICOM", counts.pacs_count],
|
||||
["仅 DICOM", counts.pacs_only_count],
|
||||
["仅 STL", counts.stl_only_count],
|
||||
["已配准", counts.registered_ct_count],
|
||||
];
|
||||
$("metrics").innerHTML = items
|
||||
.map(([label, value]) => `<div class="metric"><span>${escapeHtml(label)}</span><strong>${Number(value || 0)}</strong></div>`)
|
||||
@@ -203,6 +206,7 @@ function cardTitle(row) {
|
||||
`UPP患者:${row.upp_patient_name || "无"}`,
|
||||
`重建模型:${row.algorithm_model || "无"}`,
|
||||
`部位标注:${annotationLabels(row).join("、") || "无"}`,
|
||||
`配准:${Number(row.registered_series || 0)} 序列,${Number(row.locked_count || 0)} 锁定`,
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
@@ -237,6 +241,11 @@ function renderList() {
|
||||
</div>
|
||||
<div class="card-status">
|
||||
<i class="mini-badge ${escapeHtml(row.relation_status)}">${escapeHtml(statusLabel(row.relation_status))}</i>
|
||||
${
|
||||
Number(row.registration_count || 0)
|
||||
? `<small class="registration-mini" title="${Number(row.registration_count || 0)} 条配准记录,${Number(row.locked_count || 0)} 条锁定">${Number(row.registered_series || 0)} 已配准</small>`
|
||||
: `<small class="registration-mini pending">未配准</small>`
|
||||
}
|
||||
${
|
||||
Number(row.duplicate_model_asset_count || 0)
|
||||
? `<small class="name-check-warning" title="同一CT同一算法模型只能对应一个STL,已按规则保留一组">${Number(row.duplicate_model_asset_count)} 组重复</small>`
|
||||
@@ -290,16 +299,39 @@ function viewerBaseUrl() {
|
||||
}
|
||||
}
|
||||
|
||||
function registrationBaseUrl() {
|
||||
try {
|
||||
const url = new URL(app.registrationUrl || `${location.protocol}//${location.hostname}:8109`, location.href);
|
||||
if (["127.0.0.1", "localhost", "0.0.0.0"].includes(url.hostname) && !["127.0.0.1", "localhost"].includes(location.hostname)) {
|
||||
url.hostname = location.hostname;
|
||||
}
|
||||
url.protocol = location.protocol;
|
||||
return url.toString().replace(/\/$/, "");
|
||||
} catch (_) {
|
||||
return `${location.protocol}//${location.hostname}:8109`;
|
||||
}
|
||||
}
|
||||
|
||||
function dicomViewerUrl(row = app.active) {
|
||||
if (!row?.pacs_ct_number) return "";
|
||||
return `${viewerBaseUrl()}/?ct_number=${encodeURIComponent(row.pacs_ct_number)}`;
|
||||
}
|
||||
|
||||
function registrationViewerUrl(row = app.active) {
|
||||
const ct = row?.pacs_ct_number || row?.ct_key;
|
||||
if (!ct) return "";
|
||||
return `${registrationBaseUrl()}/?ct_number=${encodeURIComponent(ct)}`;
|
||||
}
|
||||
|
||||
function updateOpenDicomButton() {
|
||||
const button = $("openDicomBtn");
|
||||
const url = dicomViewerUrl();
|
||||
button.disabled = !url;
|
||||
button.title = url || "当前记录没有 DICOM 检查号";
|
||||
const registrationButton = $("openRegistrationBtn");
|
||||
const registration = registrationViewerUrl();
|
||||
registrationButton.disabled = !registration;
|
||||
registrationButton.title = registration || "当前记录没有可配准 CT 号";
|
||||
}
|
||||
|
||||
function clearDetail() {
|
||||
@@ -349,6 +381,7 @@ function renderDetail(row) {
|
||||
["标注", `${Number(row.annotated_series || 0)} 已标注`],
|
||||
["完成", row.completed ? "已完成" : "待处理"],
|
||||
["部位标注", labels.join("、")],
|
||||
["配准状态", row.registration_count ? `${Number(row.registered_series || 0)} 个序列已配准 / ${Number(row.locked_count || 0)} 条锁定` : "未配准"],
|
||||
]);
|
||||
|
||||
renderDl("stlDetails", [
|
||||
@@ -370,6 +403,7 @@ function renderDetail(row) {
|
||||
["目录", row.processed_stl_dir],
|
||||
["分割分类", uniq(asList(row.segment_categories)).join("、")],
|
||||
["更新时间", fmtDate(row.stl_updated_at || row.upp_updated_at)],
|
||||
["配准更新时间", fmtDate(row.registration_updated_at)],
|
||||
]);
|
||||
|
||||
updateOpenDicomButton();
|
||||
@@ -597,6 +631,15 @@ function openViewerHome() {
|
||||
window.open(viewerBaseUrl(), "_blank", "noopener");
|
||||
}
|
||||
|
||||
function openRegistrationViewer() {
|
||||
const url = registrationViewerUrl();
|
||||
if (url) window.open(url, "_blank", "noopener");
|
||||
}
|
||||
|
||||
function openRegistrationHome() {
|
||||
window.open(registrationBaseUrl(), "_blank", "noopener");
|
||||
}
|
||||
|
||||
function wire() {
|
||||
$("loginForm").addEventListener("submit", login);
|
||||
$("logoutBtn").addEventListener("click", logout);
|
||||
@@ -604,7 +647,9 @@ function wire() {
|
||||
$("backToViewer").addEventListener("click", showViewer);
|
||||
$("refreshBtn").addEventListener("click", refreshAll);
|
||||
$("viewerHomeBtn").addEventListener("click", openViewerHome);
|
||||
$("registrationHomeBtn").addEventListener("click", openRegistrationHome);
|
||||
$("openDicomBtn").addEventListener("click", openDicomViewer);
|
||||
$("openRegistrationBtn").addEventListener("click", openRegistrationViewer);
|
||||
$("relationList").addEventListener("scroll", () => {
|
||||
const list = $("relationList");
|
||||
if (list.scrollTop + list.clientHeight >= list.scrollHeight - 140) loadRelations(false);
|
||||
|
||||
Reference in New Issue
Block a user