fix(frontend): 调整审核页布局与报告渲染
This commit is contained in:
@@ -125,10 +125,20 @@ input:focus {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app-shell {
|
||||
display: grid;
|
||||
grid-template-rows: 60px minmax(0, 1fr);
|
||||
height: 100vh;
|
||||
min-height: 0;
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
.workspace {
|
||||
display: grid;
|
||||
grid-template-columns: 296px minmax(0, 1fr) 340px;
|
||||
min-height: 100vh;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
@@ -136,6 +146,8 @@ input:focus {
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
padding: 18px;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
background: linear-gradient(180deg, var(--sidebar) 0%, var(--sidebar-strong) 100%);
|
||||
border-right: 1px solid var(--line);
|
||||
transition: width 180ms ease, padding 180ms ease, transform 180ms ease;
|
||||
@@ -146,6 +158,12 @@ input:focus {
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -310,8 +328,9 @@ input:focus {
|
||||
|
||||
.chat-shell {
|
||||
display: grid;
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@@ -322,6 +341,8 @@ input:focus {
|
||||
gap: 16px;
|
||||
padding: 0 24px;
|
||||
min-height: 60px;
|
||||
position: relative;
|
||||
z-index: 30;
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: #ffffff;
|
||||
}
|
||||
@@ -470,7 +491,7 @@ input:focus {
|
||||
display: grid;
|
||||
grid-template-rows: minmax(0, 1fr) auto;
|
||||
min-height: 0;
|
||||
height: calc(100vh - 60px);
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -562,8 +583,26 @@ input:focus {
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.message-bubble p {
|
||||
.message-bubble p,
|
||||
.message-content p {
|
||||
margin: 0;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.message-content a {
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.message-content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.message-bubble.streaming {
|
||||
@@ -737,7 +776,7 @@ input:focus {
|
||||
}
|
||||
|
||||
.workspace[data-sidebar-state="collapsed"] {
|
||||
grid-template-columns: 88px minmax(0, 1fr);
|
||||
grid-template-columns: 88px minmax(0, 1fr) 340px;
|
||||
}
|
||||
|
||||
.workspace[data-sidebar-state="collapsed"] .brand-text,
|
||||
@@ -760,12 +799,20 @@ input:focus {
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.workspace[data-sidebar-state="collapsed"] .sidebar-header {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.workspace[data-sidebar-state="collapsed"] .brand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.summary-panel {
|
||||
display: grid;
|
||||
grid-template-rows: auto auto minmax(0, 1fr);
|
||||
gap: 14px;
|
||||
min-width: 0;
|
||||
max-height: 100vh;
|
||||
max-height: 100%;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
border-left: 1px solid var(--line);
|
||||
@@ -915,6 +962,7 @@ input:focus {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.message-bubble th,
|
||||
@@ -926,15 +974,26 @@ input:focus {
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.app-body {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.app-shell {
|
||||
grid-template-rows: 60px auto;
|
||||
height: auto;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.workspace {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
min-height: 100vh;
|
||||
overflow: auto;
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
inset: 0 auto 0 0;
|
||||
inset: 60px auto 0 0;
|
||||
width: 280px;
|
||||
z-index: 20;
|
||||
box-shadow: var(--shadow);
|
||||
@@ -953,10 +1012,6 @@ input:focus {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.sidebar-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.topbar,
|
||||
.chat-scroll,
|
||||
.composer-wrap {
|
||||
@@ -965,16 +1020,22 @@ input:focus {
|
||||
}
|
||||
|
||||
.topbar {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
min-height: auto;
|
||||
padding-top: 12px;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
min-height: 60px;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.topbar-left {
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.topbar-right {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
flex: 0 0 auto;
|
||||
width: auto;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.conversation-header {
|
||||
@@ -982,7 +1043,7 @@ input:focus {
|
||||
}
|
||||
|
||||
.chat-stage {
|
||||
min-height: calc(100vh - 88px);
|
||||
min-height: calc(100vh - 60px);
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@@ -1050,7 +1111,8 @@ input:focus {
|
||||
}
|
||||
|
||||
.chat-stage {
|
||||
height: calc(100vh - 126px);
|
||||
min-height: calc(100vh - 60px);
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.chat-scroll {
|
||||
|
||||
102
static/js/app.js
102
static/js/app.js
@@ -153,11 +153,105 @@
|
||||
return escapeHtml(text).replace(/\n/g, "<br>");
|
||||
}
|
||||
|
||||
function renderInlineMarkdown(text) {
|
||||
return escapeHtml(text || "").replace(/\[([^\]]+)\]\(([^)]+)\)/g, function (_match, label, href) {
|
||||
var safeHref = escapeHtml(href);
|
||||
var safeLabel = escapeHtml(label);
|
||||
if (!/^\/[^/\\]/.test(href) && !/^https?:\/\//.test(href)) {
|
||||
return safeLabel;
|
||||
}
|
||||
return '<a href="' + safeHref + '" target="_blank" rel="noopener">' + safeLabel + "</a>";
|
||||
});
|
||||
}
|
||||
|
||||
function renderMarkdownTable(lines, startIndex) {
|
||||
var header = lines[startIndex].trim();
|
||||
var separator = lines[startIndex + 1] ? lines[startIndex + 1].trim() : "";
|
||||
if (header.charAt(0) !== "|" || separator.indexOf("---") === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function cells(line) {
|
||||
return line
|
||||
.trim()
|
||||
.replace(/^\|/, "")
|
||||
.replace(/\|$/, "")
|
||||
.split("|")
|
||||
.map(function (cell) {
|
||||
return cell.trim();
|
||||
});
|
||||
}
|
||||
|
||||
var html = "<table><thead><tr>";
|
||||
cells(header).forEach(function (cell) {
|
||||
html += "<th>" + renderInlineMarkdown(cell) + "</th>";
|
||||
});
|
||||
html += "</tr></thead><tbody>";
|
||||
|
||||
var index = startIndex + 2;
|
||||
while (index < lines.length && lines[index].trim().charAt(0) === "|") {
|
||||
html += "<tr>";
|
||||
cells(lines[index]).forEach(function (cell) {
|
||||
html += "<td>" + renderInlineMarkdown(cell || "-") + "</td>";
|
||||
});
|
||||
html += "</tr>";
|
||||
index += 1;
|
||||
}
|
||||
html += "</tbody></table>";
|
||||
return { html: html, nextIndex: index };
|
||||
}
|
||||
|
||||
function renderBasicMarkdown(text) {
|
||||
var lines = (text || "").split(/\r?\n/);
|
||||
var html = "";
|
||||
var paragraph = [];
|
||||
var index = 0;
|
||||
|
||||
function flushParagraph() {
|
||||
if (!paragraph.length) {
|
||||
return;
|
||||
}
|
||||
html += "<p>" + renderInlineMarkdown(paragraph.join("\n")).replace(/\n/g, "<br>") + "</p>";
|
||||
paragraph = [];
|
||||
}
|
||||
|
||||
while (index < lines.length) {
|
||||
var line = lines[index];
|
||||
var table = renderMarkdownTable(lines, index);
|
||||
if (table) {
|
||||
flushParagraph();
|
||||
html += table.html;
|
||||
index = table.nextIndex;
|
||||
continue;
|
||||
}
|
||||
if (!line.trim()) {
|
||||
flushParagraph();
|
||||
} else {
|
||||
paragraph.push(line);
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
flushParagraph();
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderAssistantContent(text) {
|
||||
if (window.marked && window.DOMPurify) {
|
||||
return window.DOMPurify.sanitize(window.marked.parse(text || ""));
|
||||
}
|
||||
return nl2br(text || "");
|
||||
return renderBasicMarkdown(text || "");
|
||||
}
|
||||
|
||||
function renderExistingAssistantMessages() {
|
||||
document.querySelectorAll(".message.assistant .message-bubble").forEach(function (bubble) {
|
||||
var target = bubble.querySelector(".markdown-content");
|
||||
var raw = bubble.querySelector(".message-raw");
|
||||
if (!target || !raw || target.dataset.rendered === "true") {
|
||||
return;
|
||||
}
|
||||
target.innerHTML = renderAssistantContent(raw.content ? raw.content.textContent : raw.textContent);
|
||||
target.dataset.rendered = "true";
|
||||
});
|
||||
}
|
||||
|
||||
function scrollChatToBottom() {
|
||||
@@ -181,7 +275,10 @@
|
||||
var bubble = document.createElement("div");
|
||||
bubble.className = "message-bubble";
|
||||
|
||||
var text = document.createElement("p");
|
||||
var text = document.createElement(role === "assistant" ? "div" : "p");
|
||||
if (role === "assistant") {
|
||||
text.className = "message-content markdown-content";
|
||||
}
|
||||
text.innerHTML = role === "assistant" ? renderAssistantContent(content) : nl2br(content);
|
||||
bubble.appendChild(text);
|
||||
|
||||
@@ -549,6 +646,7 @@
|
||||
|
||||
syncNodeRailVisibility();
|
||||
bindNodeAnchorClicks();
|
||||
renderExistingAssistantMessages();
|
||||
|
||||
if (chatScroll) {
|
||||
chatScroll.addEventListener("scroll", setActiveNode, { passive: true });
|
||||
|
||||
Reference in New Issue
Block a user