fix: 优化界面
This commit is contained in:
23
Dockerfile
23
Dockerfile
@@ -18,5 +18,28 @@ RUN --mount=type=cache,target=/root/.cache/pip \
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# 可选:构建时自动拉取远端仓库最新代码覆盖工作目录(默认关闭)。
|
||||||
|
# 放在 COPY 之后,避免影响依赖层缓存;仅在你显式开启时才会触发网络更新。
|
||||||
|
# 用法示例:
|
||||||
|
# docker compose build \
|
||||||
|
# --build-arg GIT_AUTO_UPDATE=1 \
|
||||||
|
# --build-arg GIT_REMOTE_URL=https://github.com/you/repo.git \
|
||||||
|
# --build-arg GIT_REF=main
|
||||||
|
ARG GIT_AUTO_UPDATE=0
|
||||||
|
ARG GIT_REMOTE_URL=
|
||||||
|
ARG GIT_REF=main
|
||||||
|
RUN if [ "$GIT_AUTO_UPDATE" = "1" ] && [ -n "$GIT_REMOTE_URL" ]; then \
|
||||||
|
set -eux; \
|
||||||
|
apt-get update; \
|
||||||
|
apt-get install -y --no-install-recommends git ca-certificates; \
|
||||||
|
rm -rf /var/lib/apt/lists/*; \
|
||||||
|
tmpdir="$(mktemp -d)"; \
|
||||||
|
git clone --depth=1 --branch "$GIT_REF" "$GIT_REMOTE_URL" "$tmpdir/repo"; \
|
||||||
|
cp -a "$tmpdir/repo/." /app/; \
|
||||||
|
rm -rf "$tmpdir"; \
|
||||||
|
else \
|
||||||
|
echo "Skip remote code update (GIT_AUTO_UPDATE=0 or GIT_REMOTE_URL empty)"; \
|
||||||
|
fi
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
|
|||||||
@@ -51,32 +51,6 @@ async function postJSON(url, body) {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTrace(trace, headerRid) {
|
|
||||||
const wrap = $("traceWrap");
|
|
||||||
const pre = $("traceJson");
|
|
||||||
const badge = $("traceBadge");
|
|
||||||
if (!pre || !wrap) return;
|
|
||||||
|
|
||||||
if (!trace || Object.keys(trace).length === 0) {
|
|
||||||
pre.textContent = headerRid
|
|
||||||
? JSON.stringify({ request_id: headerRid, note: "响应中无 trace 字段" }, null, 2)
|
|
||||||
: "(尚无数据)完成一次「AI 改写」后,这里会显示请求 ID、耗时、质检与降级原因。";
|
|
||||||
if (badge) badge.textContent = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const merged = { ...trace };
|
|
||||||
if (headerRid && !merged.request_id) merged.request_id = headerRid;
|
|
||||||
pre.textContent = JSON.stringify(merged, null, 2);
|
|
||||||
|
|
||||||
const mode = merged.mode || "";
|
|
||||||
if (badge) {
|
|
||||||
badge.textContent = mode === "ai" ? "AI" : mode === "fallback" ? "保底" : "";
|
|
||||||
badge.className = "trace-badge " + (mode === "ai" ? "is-ai" : mode === "fallback" ? "is-fallback" : "");
|
|
||||||
}
|
|
||||||
wrap.open = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$("rewriteBtn").addEventListener("click", async () => {
|
$("rewriteBtn").addEventListener("click", async () => {
|
||||||
const sourceText = $("sourceText").value.trim();
|
const sourceText = $("sourceText").value.trim();
|
||||||
if (sourceText.length < 20) {
|
if (sourceText.length < 20) {
|
||||||
@@ -99,7 +73,6 @@ $("rewriteBtn").addEventListener("click", async () => {
|
|||||||
$("summary").value = data.summary || "";
|
$("summary").value = data.summary || "";
|
||||||
$("body").value = data.body_markdown || "";
|
$("body").value = data.body_markdown || "";
|
||||||
updateCounters();
|
updateCounters();
|
||||||
renderTrace(data.trace, data._requestId);
|
|
||||||
const tr = data.trace || {};
|
const tr = data.trace || {};
|
||||||
const modelLine = tr.model ? `模型 ${tr.model}` : "";
|
const modelLine = tr.model ? `模型 ${tr.model}` : "";
|
||||||
if (data.mode === "fallback") {
|
if (data.mode === "fallback") {
|
||||||
@@ -193,32 +166,4 @@ $("imBtn").addEventListener("click", async () => {
|
|||||||
$(id).addEventListener("input", updateCounters);
|
$(id).addEventListener("input", updateCounters);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function loadBackendConfig() {
|
|
||||||
const el = $("backendConfig");
|
|
||||||
if (!el) return;
|
|
||||||
try {
|
|
||||||
const res = await fetch("/api/config");
|
|
||||||
const c = await res.json();
|
|
||||||
if (!c.openai_configured) {
|
|
||||||
el.textContent =
|
|
||||||
"后端未配置 OPENAI_API_KEY:改写将使用本地保底稿,千问不会参与。请在 .env 中配置并重启容器。";
|
|
||||||
el.style.color = "#b42318";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const name =
|
|
||||||
c.provider === "dashscope"
|
|
||||||
? "通义千问(DashScope 兼容接口)"
|
|
||||||
: "OpenAI 兼容接口";
|
|
||||||
const host = c.base_url_host ? ` · ${c.base_url_host}` : "";
|
|
||||||
const to = c.openai_timeout_sec != null ? ` · 单轮最长等待 ${c.openai_timeout_sec}s` : "";
|
|
||||||
el.textContent = `已接入:${c.openai_model} · ${name}${host}${to}`;
|
|
||||||
el.style.color = "";
|
|
||||||
} catch (e) {
|
|
||||||
el.textContent = "无法读取 /api/config(请确认服务已启动)";
|
|
||||||
el.style.color = "#b42318";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loadBackendConfig();
|
|
||||||
updateCounters();
|
updateCounters();
|
||||||
renderTrace(null, "");
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
:root {
|
:root {
|
||||||
--bg: #f3f7f5;
|
--bg: #ecfeff;
|
||||||
--panel: #ffffff;
|
--panel: #ffffff;
|
||||||
--line: #d7e3dd;
|
--line: #d9eef2;
|
||||||
--text: #1a3128;
|
--text: #164e63;
|
||||||
--muted: #5e7a6f;
|
--muted: #457386;
|
||||||
--accent: #18794e;
|
--accent: #0891b2;
|
||||||
--accent-2: #0f5f3d;
|
--accent-2: #0e7490;
|
||||||
|
--accent-soft: #f0fdff;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@@ -14,17 +15,18 @@
|
|||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
background: radial-gradient(circle at 10% 20%, #e6f4ec, transparent 35%),
|
background: var(--bg);
|
||||||
radial-gradient(circle at 90% 80%, #dff0ff, transparent 30%),
|
|
||||||
var(--bg);
|
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-family: "PingFang SC", "Noto Sans SC", "Microsoft YaHei", sans-serif;
|
font-family: Inter, "PingFang SC", "Noto Sans SC", "Microsoft YaHei", sans-serif;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topbar {
|
.topbar {
|
||||||
max-width: 1280px;
|
max-width: 1240px;
|
||||||
margin: 20px auto 0;
|
height: 72px;
|
||||||
padding: 0 16px;
|
margin: 0 auto;
|
||||||
|
padding: 0 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -32,42 +34,43 @@ body {
|
|||||||
|
|
||||||
.brand h1 {
|
.brand h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
font-size: 32px;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand .muted {
|
.brand .muted {
|
||||||
margin: 6px 0 0;
|
margin: 6px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.backend-config {
|
|
||||||
margin: 8px 0 0;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #0f5f3d;
|
color: #fafafa;
|
||||||
background: #eaf7f0;
|
background: var(--accent-2);
|
||||||
border: 1px solid #cde6d7;
|
border: 1px solid var(--accent-2);
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layout {
|
.layout {
|
||||||
max-width: 1280px;
|
max-width: 1240px;
|
||||||
margin: 14px auto 24px;
|
height: calc(100vh - 72px);
|
||||||
padding: 0 16px;
|
margin: 0 auto;
|
||||||
|
padding: 0 20px;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: minmax(320px, 42%) 1fr;
|
||||||
gap: 16px;
|
gap: 12px;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
border-radius: 14px;
|
border-radius: 12px;
|
||||||
padding: 18px;
|
padding: 14px;
|
||||||
box-shadow: 0 8px 24px rgba(32, 84, 55, 0.07);
|
box-shadow: 0 6px 24px rgba(8, 145, 178, 0.08);
|
||||||
|
min-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
@@ -75,16 +78,25 @@ h2 {
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.panel-head {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-head h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.muted {
|
.muted {
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
margin-top: -6px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 10px;
|
margin-top: 8px;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 4px;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,8 +117,8 @@ button {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
padding: 10px 12px;
|
padding: 8px 10px;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
|
transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,13 +130,13 @@ textarea {
|
|||||||
input:focus,
|
input:focus,
|
||||||
textarea:focus {
|
textarea:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: #8ec6aa;
|
border-color: #7dd3e7;
|
||||||
box-shadow: 0 0 0 3px rgba(24, 121, 78, 0.12);
|
box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-top: 12px;
|
margin-top: 8px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,6 +154,22 @@ button.primary:hover {
|
|||||||
background: var(--accent-2);
|
background: var(--accent-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.secondary {
|
||||||
|
background: #fff;
|
||||||
|
color: var(--text);
|
||||||
|
border-color: var(--line);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.secondary:hover {
|
||||||
|
background: #f8fdff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtle-btn {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #a5dae6;
|
||||||
|
color: var(--accent-2);
|
||||||
|
}
|
||||||
|
|
||||||
button:disabled {
|
button:disabled {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
opacity: 0.65;
|
opacity: 0.65;
|
||||||
@@ -173,67 +201,29 @@ button:disabled {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
min-height: 22px;
|
min-height: 18px;
|
||||||
margin-top: 8px;
|
margin-top: 6px;
|
||||||
color: var(--accent-2);
|
color: var(--accent-2);
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.small {
|
.small {
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
margin: 0 0 12px;
|
margin: 0 0 6px;
|
||||||
}
|
|
||||||
|
|
||||||
.flow-hint {
|
|
||||||
margin: 0 0 14px 18px;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trace-wrap {
|
|
||||||
margin-top: 12px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
border: 1px dashed var(--line);
|
|
||||||
border-radius: 10px;
|
|
||||||
background: #f9fbf9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trace-wrap summary {
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: 700;
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.trace-badge {
|
|
||||||
margin-left: 8px;
|
|
||||||
font-size: 11px;
|
|
||||||
padding: 2px 8px;
|
|
||||||
border-radius: 999px;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trace-badge.is-ai {
|
|
||||||
background: #eaf7f0;
|
|
||||||
color: #0f5f3d;
|
|
||||||
border: 1px solid #cde6d7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trace-badge.is-fallback {
|
|
||||||
background: #fff4e6;
|
|
||||||
color: #9a3412;
|
|
||||||
border: 1px solid #fed7aa;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.body-split {
|
.body-split {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 0.9fr;
|
||||||
gap: 12px;
|
gap: 10px;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.body-split textarea {
|
.body-split textarea {
|
||||||
min-height: 280px;
|
min-height: 170px;
|
||||||
|
max-height: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-panel {
|
.preview-panel {
|
||||||
@@ -244,10 +234,10 @@ button:disabled {
|
|||||||
|
|
||||||
.markdown-preview {
|
.markdown-preview {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 280px;
|
min-height: 170px;
|
||||||
max-height: 480px;
|
max-height: 240px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: 12px 14px;
|
padding: 10px 12px;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background: #fafcfb;
|
background: #fafcfb;
|
||||||
@@ -258,7 +248,7 @@ button:disabled {
|
|||||||
.markdown-preview h2 {
|
.markdown-preview h2 {
|
||||||
font-size: 1.15rem;
|
font-size: 1.15rem;
|
||||||
margin: 1em 0 0.5em;
|
margin: 1em 0 0.5em;
|
||||||
color: var(--accent-2);
|
color: #111827;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown-preview h3 {
|
.markdown-preview h3 {
|
||||||
@@ -280,23 +270,14 @@ button:disabled {
|
|||||||
margin: 0.25em 0;
|
margin: 0.25em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.trace-json {
|
|
||||||
margin: 10px 0 0;
|
|
||||||
padding: 10px;
|
|
||||||
max-height: 220px;
|
|
||||||
overflow: auto;
|
|
||||||
font-size: 11px;
|
|
||||||
line-height: 1.45;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid var(--line);
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 960px) {
|
@media (max-width: 960px) {
|
||||||
|
body {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.layout {
|
.layout {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.body-split {
|
.body-split {
|
||||||
|
|||||||
@@ -10,27 +10,22 @@
|
|||||||
<header class="topbar">
|
<header class="topbar">
|
||||||
<div class="brand">
|
<div class="brand">
|
||||||
<h1>{{ app_name }}</h1>
|
<h1>{{ app_name }}</h1>
|
||||||
<p class="muted">粘贴原文 → 洗成约 <strong>5 段、500 字内</strong> 的短文(无小标题)→ 右侧预览 → 满意后发布。</p>
|
<p class="muted">从原文到公众号草稿,一页完成改写、封面和发布。</p>
|
||||||
<p id="backendConfig" class="backend-config muted small" aria-live="polite"></p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="badge">Beta</div>
|
<div class="badge">Studio</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main class="layout">
|
<main class="layout">
|
||||||
<section class="panel input-panel">
|
<section class="panel input-panel">
|
||||||
<h2>输入与改写策略</h2>
|
<div class="panel-head">
|
||||||
<ol class="flow-hint muted">
|
<h2>内容输入</h2>
|
||||||
<li>粘贴原文并设置语气/读者</li>
|
</div>
|
||||||
<li>点击改写 → 右侧为短标题、摘要与<strong>五段正文</strong>(段落间空一行)</li>
|
|
||||||
<li>看「运行追踪」:<strong>模式为 AI</strong> 且模型名正确,即千问/接口已生效</li>
|
|
||||||
<li>人工改好后 →「发布到公众号草稿箱」(需配置 WECHAT_*)</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<div class="field-head">
|
<div class="field-head">
|
||||||
<label>原始内容</label>
|
<label>内容</label>
|
||||||
<span id="sourceCount" class="meta">0 字</span>
|
<span id="sourceCount" class="meta">0 字</span>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="sourceText" rows="14" placeholder="粘贴原文(长帖、线程、摘录均可),洗稿会围绕原文主题展开…"></textarea>
|
<textarea id="sourceText" rows="9" placeholder="粘贴原文(长帖、线程、摘录均可),洗稿会围绕原文主题展开…"></textarea>
|
||||||
|
|
||||||
<div class="grid2">
|
<div class="grid2">
|
||||||
<div>
|
<div>
|
||||||
@@ -62,8 +57,9 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="panel output-panel">
|
<section class="panel output-panel">
|
||||||
|
<div class="panel-head">
|
||||||
<h2>发布内容</h2>
|
<h2>发布内容</h2>
|
||||||
<p class="muted small">下方「运行追踪」会显示本次请求 ID、耗时、质检项与是否降级,便于与容器日志对照。</p>
|
</div>
|
||||||
|
|
||||||
<label>标题</label>
|
<label>标题</label>
|
||||||
<input id="title" type="text" />
|
<input id="title" type="text" />
|
||||||
@@ -72,12 +68,12 @@
|
|||||||
<label>摘要</label>
|
<label>摘要</label>
|
||||||
<span id="summaryCount" class="meta">0 字</span>
|
<span id="summaryCount" class="meta">0 字</span>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="summary" rows="3"></textarea>
|
<textarea id="summary" rows="2"></textarea>
|
||||||
|
|
||||||
<label>公众号封面(可选上传)</label>
|
<label>公众号封面(可选上传)</label>
|
||||||
<div class="cover-tools">
|
<div class="cover-tools">
|
||||||
<input id="coverFile" type="file" accept="image/png,image/jpeg,image/jpg,image/webp" />
|
<input id="coverFile" type="file" accept="image/png,image/jpeg,image/jpg,image/webp" />
|
||||||
<button id="coverUploadBtn" type="button">上传封面并绑定</button>
|
<button id="coverUploadBtn" class="subtle-btn" type="button">上传封面并绑定</button>
|
||||||
</div>
|
</div>
|
||||||
<input id="thumbMediaId" type="text" placeholder="thumb_media_id(上传后自动填充,也可手动粘贴)" />
|
<input id="thumbMediaId" type="text" placeholder="thumb_media_id(上传后自动填充,也可手动粘贴)" />
|
||||||
<p id="coverHint" class="muted small">未上传时将使用后端默认封面策略。</p>
|
<p id="coverHint" class="muted small">未上传时将使用后端默认封面策略。</p>
|
||||||
@@ -87,24 +83,19 @@
|
|||||||
<span id="bodyCount" class="meta">0 字</span>
|
<span id="bodyCount" class="meta">0 字</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="body-split">
|
<div class="body-split">
|
||||||
<textarea id="body" rows="10" placeholder="五段之间空一行;无需 # 标题"></textarea>
|
<textarea id="body" rows="7" placeholder="五段之间空一行;无需 # 标题"></textarea>
|
||||||
<div class="preview-panel">
|
<div class="preview-panel">
|
||||||
<div class="field-head">
|
<div class="field-head">
|
||||||
<label>排版预览</label>
|
<label>排版预览</label>
|
||||||
<span class="meta">与公众号 HTML 渲染接近</span>
|
<span class="meta">实时同步</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="bodyPreview" class="markdown-preview"></div>
|
<div id="bodyPreview" class="markdown-preview"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<details id="traceWrap" class="trace-wrap">
|
|
||||||
<summary>运行追踪 <span id="traceBadge" class="trace-badge"></span></summary>
|
|
||||||
<pre id="traceJson" class="trace-json"></pre>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button id="wechatBtn">发布到公众号草稿箱</button>
|
<button id="wechatBtn" class="primary">发布到公众号草稿箱</button>
|
||||||
<button id="imBtn">发送到 IM</button>
|
<button id="imBtn" class="secondary">发送到 IM</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
Reference in New Issue
Block a user