Markdown 编辑器
带工具栏、分栏预览与表情面板的编辑器。
渲染部分直接复用 SrMarkdown,不是另写一套。 因此编辑时看到的预览与最终渲染结果完全一致——不会出现 「编辑器里好好的,上线后样式变了」。
基础用法
v-model 绑定 Markdown 源文本:
编辑器验证
左写右看,改动实时同步。
支持 粗体、斜体、行内代码,以及列表:
- 第一项
- 第二项
引用块也可以。
<script setup>
import { ref } from 'vue'
const draft = ref('# 标题\n\n正文内容。')
</script>
<template>
<SrMarkdownEditor v-model="draft" />
</template>三种视图
view 控制初始视图,默认 split(左编辑、右预览)。
view="edit":只显示编辑区
编辑器验证
左写右看,改动实时同步。
支持 粗体、斜体、行内代码,以及列表:
- 第一项
- 第二项
引用块也可以。
view="preview":只显示预览
<SrMarkdownEditor v-model="draft" view="edit" />
<SrMarkdownEditor v-model="draft" view="split" />
<SrMarkdownEditor v-model="draft" view="preview" />工具栏上也有视图切换按钮,用户可自行切换。
工具栏与字数统计
两者默认都开,可以单独关掉:
编辑器验证
左写右看,改动实时同步。
支持 粗体、斜体、行内代码,以及列表:
- 第一项
- 第二项
引用块也可以。
toolbar="false":只留一个纯文本框
编辑器验证
左写右看,改动实时同步。
支持 粗体、斜体、行内代码,以及列表:
- 第一项
- 第二项
引用块也可以。
show-count="false":不显示右下角字数
<SrMarkdownEditor v-model="draft" :toolbar="false" />
<SrMarkdownEditor v-model="draft" :show-count="false" />字数是怎么数的
字数由 countText 统计,它会先剔除语法符号再计数: #、**、列表符号、代码块都不会算进去。
图片整段不计(它是资源不是文字),链接只算可读的那部分文字。
不这么做的话,用户看到的数字会比自己数出来的大——而人对字数的直觉判断 是「我能读到的字」,不是「源文本的字符数」。
表情
不传 emoji-packages 时用内置的字符表情包。传了就用你自己的:
<SrMarkdownEditor v-model="draft" :emoji-packages="myPackages" />面板与预览必须用同一份数据
组件会把 emojiPackages 同时交给表情面板与预览渲染器。
这一条不能拆开传:面板里插入了某个表情、而渲染用的表里没有它, 预览区就会显示成一串 [名称] 纯文本——用户看到自己刚点的表情变成了乱码。
所以组件内部只接收一份数据、分发给两处,从源头避免这个偏差。
插入的是标记文本,不是表情本身
点击表情插入的是 [微笑] 这样的标记,而不是直接插入 🙂。
好处是表情包可以整体替换:字符换成图片、换一整套画风, 历史内容里存的标记一个字都不用改,重新渲染时按新表查出来即可。
代价是渲染端必须认识这套标记——这正是 emojiPackages 要传两处的原因。
行数、上限与禁用
预览区——左边写,这里实时看效果
rows="4" + maxlength="100":超过 100 字时计数变红
这段内容不可编辑。
disabled:整体禁用
<SrMarkdownEditor v-model="draft" :rows="4" :maxlength="100" />
<SrMarkdownEditor v-model="draft" disabled />maxlength 传 0(默认)表示不限。
maxlength 只提示,不阻止输入
超限时计数变红,但不会截断用户的输入。这是有意的:
写到一半被打断、或者粘贴一段稍长的内容被砍掉尾巴,比「看到红色计数」糟糕得多。 是否放行由业务判断——提交时校验、或只做提醒,都能基于这个状态自行决定。
若确实需要硬截断,在 v-model 的 update:modelValue 里自己裁。
Props
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
modelValue | string | '' | Markdown 源文本,配合 v-model |
view | 'edit' | 'split' | 'preview' | 'split' | 初始视图 |
rows | number | 12 | 编辑区行数 |
maxlength | number | 0 | 最大字数,0 为不限 |
placeholder | string | '写点什么…支持 Markdown 语法' | 占位提示 |
disabled | boolean | false | 是否禁用 |
toolbar | boolean | true | 是否显示工具栏 |
showCount | boolean | true | 是否显示字数统计 |
size | 'sm' | 'md' | 'lg' | 'md' | 预览区字号 |
emojiPackages | SrEmojiPackage[] | [] | 表情包数据,缺省用内置的 |
事件
| 名称 | 参数 | 说明 |
|---|---|---|
update:modelValue | (value: string) | 内容变化 |
无障碍
已经做到的:
- 工具栏按钮都是原生
<button>且带aria-label——加粗、插入链接等取action.title,表情面板与视图切换分别是「表情」「视图切换」。 读屏软件念出的是用途,不是图标名 - 编辑区是原生
<textarea>,输入法与读屏软件的交互由浏览器保证 placeholder提供输入提示- 主动设了
spellcheck="false"——Markdown 里全是语法符号, 开着拼写检查会满屏红波浪线
尚未做的(需要时请自行补):
| 缺口 | 影响 |
|---|---|
字数统计没有 aria-live | 输入时字数变化不会被朗读,读屏用户感知不到 |
编辑区没有 aria-label、也没有关联的 <label> | 用途只靠 placeholder 传达,而 placeholder 在输入后即消失 |
放进正式表单时,建议在组件外补一个可见的 <label>: 光靠 placeholder 传达字段名,对方填到一半就看不到它了。
相关
- 渲染器与支持的语法:SrMarkdown
- 代码块高亮:SrCodeBlock
- 表情面板:SrEmojiPicker