工作台笔记之前是个纯 textarea,内容原样显示,所以 Markdown 一直不解析。现在在笔记面板右上角加了「编辑 / 预览」切换:

编辑:还是原来的 textarea,防抖自动保存不变,占位提示换成了 Markdown 语法提示(和待办/工单编辑器一致)。
预览:用项目里已有的 MarkdownView 渲染,支持标题、列表、任务勾选框、代码块、链接、图片(本地图和外链都会经后端转成 dataURL 显示),点链接走系统浏览器。
几个细节:切到预览前会先把防抖窗口内没落盘的改动存一次,避免丢内容;切换状态记在 localStorage,下次进工作台保持上次选的模式;空笔记在预览下显示「(暂无内容)」占位。按钮复用了 md 编辑器已有的 mdEdit/mdPreviewTab 文案,所以中英文都已经有了,不需要新增词条。
This commit is contained in:
李琦
2026-08-17 09:05:12 +08:00
parent c430a0f6da
commit 83e69317d4

View File

@@ -2,9 +2,10 @@
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { Star, Folder, Code2, GitCommitHorizontal, ListTodo, TicketCheck, StickyNote, ArrowRight, Circle, CircleDot, CircleCheck, Play, Check, CalendarDays, Plus, Bell, Save } from 'lucide-vue-next'
import { Star, Folder, Code2, GitCommitHorizontal, ListTodo, TicketCheck, StickyNote, ArrowRight, Circle, CircleDot, CircleCheck, Play, Check, CalendarDays, Plus, Bell, Save, Pencil, Eye } from 'lucide-vue-next'
import StatCard from '../components/StatCard.vue'
import AIDayPanel from '../components/AIDayPanel.vue'
import MarkdownView from '../components/MarkdownView.vue'
import { call } from '../api'
import { useAppStore } from '../store'
@@ -19,6 +20,8 @@ const noteText = ref('')
const noteSavedAt = ref('')
const noteDirty = ref(false)
const noteSaving = ref(false)
// 编辑/预览切换(预览走 Markdown 渲染),选择记忆在本地
const notePreview = ref(localStorage.getItem('cc-wb-note-preview') === '1')
const messages = ref([])
let noteTimer
@@ -76,6 +79,13 @@ function editNote() {
clearTimeout(noteTimer)
noteTimer = setTimeout(() => { saveNote(false) }, 800)
}
// 切到预览前先落盘,避免防抖窗口内的改动丢失
async function setNotePreview(on) {
if (notePreview.value === on) return
if (on && noteDirty.value) await saveNote(false)
notePreview.value = on
localStorage.setItem('cc-wb-note-preview', on ? '1' : '0')
}
async function saveNote(manual = true) {
if (noteSaving.value) return
noteSaving.value = true
@@ -201,6 +211,10 @@ onUnmounted(() => clearTimeout(noteTimer))
<div class="wb-note-acts">
<small v-if="noteDirty" class="wb-note-dirty">{{ t('noteUnsaved') }}</small>
<small v-else-if="noteSavedAt" class="wb-note-saved">{{ t('autoSaved') }} {{ noteSavedAt }}</small>
<div class="tabs compact wb-note-tabs">
<button type="button" :class="{ active: !notePreview }" @click="setNotePreview(false)"><Pencil />{{ t('mdEdit') }}</button>
<button type="button" :class="{ active: notePreview }" @click="setNotePreview(true)"><Eye />{{ t('mdPreviewTab') }}</button>
</div>
<button type="button" class="btn secondary" :title="t('noteNew')" @click="newNote"><Plus /></button>
<button type="button" class="btn primary" :disabled="noteSaving || !noteDirty" :title="t('save')" @click="saveNote(true)"><Save />{{ t('save') }}</button>
<button type="button" class="btn secondary" @click="router.push('/notes')">{{ t('viewAll') }}<ArrowRight /></button>
@@ -221,7 +235,8 @@ onUnmounted(() => clearTimeout(noteTimer))
</button>
<div v-if="!notes.length" class="wb-note-list-empty">{{ t('noteEmpty') }}</div>
</aside>
<textarea v-model="noteText" class="wb-note" :placeholder="t('notepadPlaceholder')" @input="editNote" />
<textarea v-show="!notePreview" v-model="noteText" class="wb-note" :placeholder="t('mdPlaceholder')" @input="editNote" />
<MarkdownView v-if="notePreview" class="wb-note-preview md-preview-box" :source="noteText || t('mdEmpty')" />
</div>
</section>