feat(web): 移除分屏预览,编辑页改用源码模式开关

- AdminEdit:删除 showPreview 分屏块与 MarkdownViewer 引入;顶栏按钮语义改为
  「源码模式 / 退出源码」,作为即时渲染的逃生舱(方案 §4.7),工具栏提示随模式切换
- index.css:移除随分屏预览一并废弃的 .pn-ae-preview 样式

编辑态与阅读态已同构(同一套 --md-* 契约),分屏预览失去存在意义;保留一键源码模式,
装饰层万一出问题用户仍可退回可用的纯源码编辑。
This commit is contained in:
2026-09-11 01:03:08 +08:00
parent 5c5ab3dfc3
commit 9fcd8cd7cd
2 changed files with 8 additions and 21 deletions
-8
View File
@@ -1749,14 +1749,6 @@ button.pn-ae-tool svg {
min-height: 420px; min-height: 420px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
} }
.pn-ae-preview {
margin-top: 20px;
padding: 4px 2px 0;
border-top: 1px solid var(--border);
}
.pn-ae-preview .pn-note-body {
margin-top: 20px;
}
.pn-ae-upload-alert { .pn-ae-upload-alert {
margin-top: 10px; margin-top: 10px;
} }
+8 -13
View File
@@ -3,7 +3,7 @@ import { useNavigate, useParams } from 'react-router'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { import {
Bold, Code, Eye, EyeOff, Italic, Link2, List, Quote, Bold, Code, Eye, FileCode2, Italic, Link2, List, Quote,
Save, Table, Trash2, Save, Table, Trash2,
} from 'lucide-react' } from 'lucide-react'
import { Alert, Badge, Button, Card, DatePicker, Divider, Input, Select, Spin, Switch } from 'antd' import { Alert, Badge, Button, Card, DatePicker, Divider, Input, Select, Spin, Switch } from 'antd'
@@ -11,7 +11,6 @@ import { api, ApiError } from '../lib/api'
import type { NoteItem } from '../lib/api' import type { NoteItem } from '../lib/api'
import { invalidatePublic } from '../lib/invalidate' import { invalidatePublic } from '../lib/invalidate'
import { useDocumentTitle } from '../features/site' import { useDocumentTitle } from '../features/site'
import MarkdownViewer from '../components/MarkdownViewer'
import { AdminTopbar } from '../components/Layout' import { AdminTopbar } from '../components/Layout'
import { useToast } from '@/hooks/use-toast' import { useToast } from '@/hooks/use-toast'
import { slugPreview } from '../lib/utils' import { slugPreview } from '../lib/utils'
@@ -71,7 +70,7 @@ export default function AdminEdit() {
const [form, setForm] = useState<Form>(emptyForm) const [form, setForm] = useState<Form>(emptyForm)
const [errorMsg, setErrorMsg] = useState<string | null>(null) const [errorMsg, setErrorMsg] = useState<string | null>(null)
const [slugFieldError, setSlugFieldError] = useState<string | null>(null) const [slugFieldError, setSlugFieldError] = useState<string | null>(null)
const [showPreview, setShowPreview] = useState(false) const [sourceMode, setSourceMode] = useState(false)
const [uploadMsg, setUploadMsg] = useState<{ kind: 'busy' | 'error'; text: string } | null>(null) const [uploadMsg, setUploadMsg] = useState<{ kind: 'busy' | 'error'; text: string } | null>(null)
const dirtyRef = useRef(false) const dirtyRef = useRef(false)
const slugTouchedRef = useRef(false) const slugTouchedRef = useRef(false)
@@ -202,10 +201,11 @@ export default function AdminEdit() {
<AdminTopbar title={isNew ? '新建笔记' : '编辑笔记'}> <AdminTopbar title={isNew ? '新建笔记' : '编辑笔记'}>
<Button <Button
type="text" type="text"
icon={showPreview ? <EyeOff size={15} /> : <Eye size={15} />} icon={sourceMode ? <Eye size={15} /> : <FileCode2 size={15} />}
onClick={() => setShowPreview((v) => !v)} onClick={() => setSourceMode((v) => !v)}
title={sourceMode ? '退出源码模式,回到即时渲染' : '查看/编辑 Markdown 源码(逃生舱)'}
> >
{showPreview ? '隐藏预览' : '预览'} {sourceMode ? '退出源码' : '源码模式'}
</Button> </Button>
<Button <Button
type="primary" type="primary"
@@ -257,7 +257,7 @@ export default function AdminEdit() {
<Button type="text" className="pn-ae-tool" aria-label="无序列表" title="无序列表" icon={<List size={15} />} onClick={() => insert('\n- 列表项\n')} /> <Button type="text" className="pn-ae-tool" aria-label="无序列表" title="无序列表" icon={<List size={15} />} onClick={() => insert('\n- 列表项\n')} />
<Button type="text" className="pn-ae-tool" aria-label="引用" title="引用" icon={<Quote size={15} />} onClick={() => insert('\n> 引用\n')} /> <Button type="text" className="pn-ae-tool" aria-label="引用" title="引用" icon={<Quote size={15} />} onClick={() => insert('\n> 引用\n')} />
<Button type="text" className="pn-ae-tool" aria-label="插入表格" title="插入表格" icon={<Table size={15} />} onClick={() => insert('\n| 列1 | 列2 |\n| --- | --- |\n| a | b |\n')} /> <Button type="text" className="pn-ae-tool" aria-label="插入表格" title="插入表格" icon={<Table size={15} />} onClick={() => insert('\n| 列1 | 列2 |\n| --- | --- |\n| a | b |\n')} />
<span className="pn-ae-tool-hint">纯文本 · Markdown</span> <span className="pn-ae-tool-hint">{sourceMode ? '源码模式 · Markdown' : '即时渲染 · Markdown'}</span>
</div> </div>
<div className="pn-ae-body-wrap"> <div className="pn-ae-body-wrap">
@@ -265,6 +265,7 @@ export default function AdminEdit() {
<Editor <Editor
value={form.content} value={form.content}
onChange={(v) => set('content', v)} onChange={(v) => set('content', v)}
sourceMode={sourceMode}
onUploadStart={(name) => setUploadMsg({ kind: 'busy', text: `上传中:${name}…` })} onUploadStart={(name) => setUploadMsg({ kind: 'busy', text: `上传中:${name}…` })}
onUploadEnd={(err) => setUploadMsg(err ? { kind: 'error', text: err } : null)} onUploadEnd={(err) => setUploadMsg(err ? { kind: 'error', text: err } : null)}
/> />
@@ -278,12 +279,6 @@ export default function AdminEdit() {
message={uploadMsg.text} message={uploadMsg.text}
/> />
)} )}
{showPreview && (
<div className="pn-ae-preview">
<MarkdownViewer source={form.content} />
</div>
)}
</Card> </Card>
<aside className="pn-ae-side"> <aside className="pn-ae-side">