Skip to content

Commit bc68710

Browse files
authored
feat: add markdown help dialog (#1485)
1 parent 71ee658 commit bc68710

4 files changed

Lines changed: 291 additions & 9 deletions

File tree

apps/web/src/assets/example/markdown.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ Markdown 是一种简单、强大且易于掌握的标记语言,通过学习
301301

302302
现在,拿起 Markdown 编辑器,开始创作吧!探索 Markdown 的世界,你会发现它远比想象中更精彩!
303303

304-
#### 推荐阅读
304+
### 推荐阅读
305305

306306
- [阿里又一个 20k+ stars 开源项目诞生,恭喜 fastjson!](https://mp.weixin.qq.com/s/RNKDCK2KoyeuMeEs6GUrow)
307307
- [刷掉 90% 候选人的互联网大厂海量数据面试题(附题解 + 方法总结)](https://mp.weixin.qq.com/s/rjGqxUvrEqJNlo09GrT1Dw)
@@ -311,5 +311,5 @@ Markdown 是一种简单、强大且易于掌握的标记语言,通过学习
311311
---
312312

313313
<center>
314-
<img src="https://cdn-doocs.oss-cn-shenzhen.aliyuncs.com/gh/doocs/md/images/1648303220922-7e14aefa-816e-44c1-8604-ade709ca1c69.png" style="width: 100px;">
314+
<img src="https://cdn-doocs.oss-cn-shenzhen.aliyuncs.com/gh/doocs/md/images/1648303220922-7e14aefa-816e-44c1-8604-ade709ca1c69.png" alt="qr code" style="width: 100px;">
315315
</center>

apps/web/src/components/editor/editor-header/HelpDropdown.vue

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
11
<script setup lang="ts">
2-
import { Heart, HelpCircle, MessageSquare, Tag } from 'lucide-vue-next'
2+
import { BookText, Heart, HelpCircle, MessageSquare, Tag } from 'lucide-vue-next'
33
44
const props = withDefaults(defineProps<{
55
asSub?: boolean
66
}>(), {
77
asSub: false,
88
})
99
10-
const emit = defineEmits([`openAbout`, `openFund`])
10+
const emit = defineEmits(['openAbout', 'openFund', 'openMarkdownHelp'])
1111
1212
const { asSub } = toRefs(props)
1313
1414
function openAboutDialog() {
15-
emit(`openAbout`)
15+
emit('openAbout')
1616
}
1717
1818
function openFundDialog() {
19-
emit(`openFund`)
19+
emit('openFund')
20+
}
21+
22+
function openMarkdownHelp() {
23+
emit('openMarkdownHelp')
2024
}
2125
2226
function openFeedback() {
@@ -35,6 +39,10 @@ function openReleases() {
3539
帮助
3640
</MenubarSubTrigger>
3741
<MenubarSubContent>
42+
<MenubarItem @click="openMarkdownHelp()">
43+
<BookText class="mr-2 h-4 w-4" />
44+
语法帮助
45+
</MenubarItem>
3846
<MenubarItem @click="openFeedback()">
3947
<MessageSquare class="mr-2 h-4 w-4" />
4048
反馈
@@ -58,6 +66,10 @@ function openReleases() {
5866
<MenubarMenu v-else>
5967
<MenubarTrigger>帮助</MenubarTrigger>
6068
<MenubarContent align="start">
69+
<MenubarItem @click="openMarkdownHelp()">
70+
<BookText class="mr-2 h-4 w-4" />
71+
语法帮助
72+
</MenubarItem>
6173
<MenubarItem @click="openFeedback()">
6274
<MessageSquare class="mr-2 h-4 w-4" />
6375
反馈
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
<script setup lang="ts">
2+
import { BookOpen, Code2, FileText, FunctionSquare, PieChart } from 'lucide-vue-next'
3+
4+
const props = defineProps({
5+
visible: {
6+
type: Boolean,
7+
default: false,
8+
},
9+
})
10+
11+
const emit = defineEmits([`close`])
12+
13+
const activeTab = ref(`basic`)
14+
15+
function onUpdate(val: boolean) {
16+
if (!val) {
17+
emit(`close`)
18+
}
19+
}
20+
21+
interface SyntaxItem {
22+
name: string
23+
syntax: string
24+
example?: string
25+
tip?: string
26+
}
27+
28+
interface SyntaxCategory {
29+
id: string
30+
label: string
31+
icon: Component
32+
items: SyntaxItem[]
33+
}
34+
35+
const syntaxCategories: SyntaxCategory[] = [
36+
{
37+
id: `basic`,
38+
label: `基础语法`,
39+
icon: FileText,
40+
items: [
41+
{
42+
name: `标题`,
43+
syntax: `# 一级标题\n## 二级标题\n### 三级标题`,
44+
tip: `# 数量表示标题级别,最多支持六级`,
45+
},
46+
{
47+
name: `粗体`,
48+
syntax: `**粗体文本**`,
49+
example: `粗体文本`,
50+
},
51+
{
52+
name: `斜体`,
53+
syntax: `*斜体文本*`,
54+
example: `斜体文本`,
55+
},
56+
{
57+
name: `删除线`,
58+
syntax: `~~删除线~~`,
59+
example: `删除线`,
60+
},
61+
{
62+
name: `高亮`,
63+
syntax: `==高亮文本==`,
64+
example: `高亮文本`,
65+
},
66+
{
67+
name: `下划线`,
68+
syntax: `++下划线++`,
69+
example: `下划线`,
70+
},
71+
{
72+
name: `行内代码`,
73+
syntax: `\`代码\``,
74+
example: `代码`,
75+
},
76+
{
77+
name: `无序列表`,
78+
syntax: `- 项目 1\n- 项目 2\n - 嵌套项目`,
79+
tip: `使用 -、* 或 + 加空格`,
80+
},
81+
{
82+
name: `有序列表`,
83+
syntax: `1. 项目 1\n2. 项目 2`,
84+
tip: `数字加点号`,
85+
},
86+
{
87+
name: `链接`,
88+
syntax: `[显示文本](链接地址)`,
89+
example: `Doocs`,
90+
tip: `微信公众号不支持外链跳转`,
91+
},
92+
{
93+
name: `图片`,
94+
syntax: `![描述](图片地址)`,
95+
tip: `支持网络图片地址`,
96+
},
97+
{
98+
name: `引用`,
99+
syntax: `> 引用内容\n>> 嵌套引用`,
100+
},
101+
{
102+
name: `分割线`,
103+
syntax: `---`,
104+
tip: `三个或更多 -、* 或 _`,
105+
},
106+
{
107+
name: `表格`,
108+
syntax: `| 列1 | 列2 |\n| --- | --- |\n| 内容1 | 内容2 |`,
109+
tip: `点击「编辑 > 插入表格」快速创建`,
110+
},
111+
],
112+
},
113+
{
114+
id: `code`,
115+
label: `代码块`,
116+
icon: Code2,
117+
items: [
118+
{
119+
name: `基本代码块`,
120+
syntax: `\`\`\`\n代码内容\n\`\`\``,
121+
},
122+
{
123+
name: `指定语言`,
124+
syntax: `\`\`\`js\nconsole.log("Hello")\n\`\`\``,
125+
tip: `支持 js、ts、python、java、go 等多种语言`,
126+
},
127+
],
128+
},
129+
{
130+
id: `math`,
131+
label: `数学公式`,
132+
icon: FunctionSquare,
133+
items: [
134+
{
135+
name: `行内公式`,
136+
syntax: `$E = mc^2$`,
137+
example: `E = mc²`,
138+
},
139+
{
140+
name: `块级公式`,
141+
syntax: `$$\n\\int_0^1 x^2 dx\n$$`,
142+
},
143+
{
144+
name: `LaTeX 格式(行内)`,
145+
syntax: `\\(x^2 + y^2\\)`,
146+
tip: `LaTeX 标准格式`,
147+
},
148+
{
149+
name: `LaTeX 格式(块级)`,
150+
syntax: `\\[\n\\sum_{i=1}^n x_i\n\\]`,
151+
},
152+
],
153+
},
154+
{
155+
id: `diagram`,
156+
label: `图表绘制`,
157+
icon: PieChart,
158+
items: [
159+
{
160+
name: `Mermaid 流程图`,
161+
syntax: `\`\`\`mermaid\ngraph LR\n A --> B\n\`\`\``,
162+
tip: `支持流程图、时序图、饼图等`,
163+
},
164+
{
165+
name: `PlantUML`,
166+
syntax: `\`\`\`plantuml\n@startuml\nA -> B\n@enduml\n\`\`\``,
167+
tip: `详见 plantuml.com`,
168+
},
169+
{
170+
name: `信息图`,
171+
syntax: `\`\`\`infographic\ninfographic list-row\n...\n\`\`\``,
172+
tip: `AntV 信息图引擎`,
173+
},
174+
],
175+
},
176+
{
177+
id: `other`,
178+
label: `其他语法`,
179+
icon: BookOpen,
180+
items: [
181+
{
182+
name: `注音标注`,
183+
syntax: `[文字]{注音}\n[文字]^(注音)`,
184+
example: `你好`,
185+
tip: `支持日语假名、拼音等`,
186+
},
187+
{
188+
name: `幻灯片`,
189+
syntax: `<![alt](url1),![alt](url2)>`,
190+
tip: `横屏滑动图片,仅支持微信公众号`,
191+
},
192+
{
193+
name: `HTML 标签`,
194+
syntax: `<center>居中内容</center>`,
195+
tip: `部分 HTML 标签可用`,
196+
},
197+
],
198+
},
199+
]
200+
201+
function copySyntax(syntax: string) {
202+
navigator.clipboard.writeText(syntax)
203+
toast.success(`已复制到剪贴板`)
204+
}
205+
</script>
206+
207+
<template>
208+
<Dialog :open="props.visible" @update:open="onUpdate">
209+
<DialogContent class="sm:max-w-2xl max-h-[80vh]">
210+
<DialogHeader>
211+
<DialogTitle>Markdown 语法帮助</DialogTitle>
212+
<DialogDescription>
213+
查看支持的 Markdown 语法,点击语法可直接复制
214+
</DialogDescription>
215+
</DialogHeader>
216+
217+
<Tabs v-model="activeTab" class="w-full">
218+
<TabsList class="grid w-full grid-cols-5">
219+
<TabsTrigger
220+
v-for="cat in syntaxCategories"
221+
:key="cat.id"
222+
:value="cat.id"
223+
class="!flex-col !items-center !justify-center gap-1 py-2 px-2 [&>span]:flex [&>span]:flex-col [&>span]:items-center [&>span]:justify-center"
224+
>
225+
<component :is="cat.icon" class="size-4" />
226+
<span class="text-xs whitespace-normal">{{ cat.label }}</span>
227+
</TabsTrigger>
228+
</TabsList>
229+
230+
<TabsContent
231+
v-for="cat in syntaxCategories"
232+
:key="cat.id"
233+
:value="cat.id"
234+
class="mt-4 overflow-y-auto max-h-[50vh] space-y-3"
235+
>
236+
<div
237+
v-for="item in cat.items"
238+
:key="item.name"
239+
class="rounded-lg border p-3 hover:bg-accent/50 transition-colors cursor-pointer"
240+
@click="copySyntax(item.syntax)"
241+
>
242+
<div class="flex items-center justify-between mb-2">
243+
<span class="font-medium text-sm">{{ item.name }}</span>
244+
<span class="text-xs text-muted-foreground">点击复制</span>
245+
</div>
246+
<pre class="text-xs bg-muted/50 p-2 rounded overflow-x-auto font-mono">{{ item.syntax }}</pre>
247+
<div v-if="item.tip" class="mt-2 text-xs text-muted-foreground">
248+
💡 {{ item.tip }}
249+
</div>
250+
</div>
251+
</TabsContent>
252+
</Tabs>
253+
254+
<DialogFooter class="sm:justify-between">
255+
<span class="text-xs text-muted-foreground hidden sm:inline">
256+
基于 CommonMark 规范,支持多种扩展语法
257+
</span>
258+
<Button variant="outline" @click="emit(`close`)">
259+
关闭
260+
</Button>
261+
</DialogFooter>
262+
</DialogContent>
263+
</Dialog>
264+
</template>

apps/web/src/components/editor/editor-header/index.vue

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import FileDropdown from './FileDropdown.vue'
1212
import FormatDropdown from './FormatDropdown.vue'
1313
import HelpDropdown from './HelpDropdown.vue'
1414
import InsertDropdown from './InsertDropdown.vue'
15+
import MarkdownHelpDialog from './MarkdownHelpDialog.vue'
1516
import StyleDropdown from './StyleDropdown.vue'
1617
1718
const emit = defineEmits([`startCopy`, `endCopy`])
@@ -39,8 +40,8 @@ function editorRefresh() {
3940
const aboutDialogVisible = ref(false)
4041
const fundDialogVisible = ref(false)
4142
const editorStateDialogVisible = ref(false)
43+
const markdownHelpDialogVisible = ref(false)
4244
43-
// 处理帮助菜单事件
4445
function handleOpenAbout() {
4546
aboutDialogVisible.value = true
4647
}
@@ -53,6 +54,10 @@ function handleOpenEditorState() {
5354
editorStateDialogVisible.value = true
5455
}
5556
57+
function handleOpenMarkdownHelp() {
58+
markdownHelpDialogVisible.value = true
59+
}
60+
5661
const copyMode = store.reactive(addPrefix(`copyMode`), `txt`)
5762
5863
const { copy: copyContent } = useClipboard({
@@ -242,7 +247,7 @@ function copyToWeChat() {
242247
<FormatDropdown />
243248
<InsertDropdown />
244249
<StyleDropdown />
245-
<HelpDropdown @open-about="handleOpenAbout" @open-fund="handleOpenFund" />
250+
<HelpDropdown @open-about="handleOpenAbout" @open-fund="handleOpenFund" @open-markdown-help="handleOpenMarkdownHelp" />
246251
</Menubar>
247252
</div>
248253

@@ -261,7 +266,7 @@ function copyToWeChat() {
261266
<FormatDropdown :as-sub="true" />
262267
<InsertDropdown :as-sub="true" />
263268
<StyleDropdown :as-sub="true" />
264-
<HelpDropdown :as-sub="true" @open-about="handleOpenAbout" @open-fund="handleOpenFund" />
269+
<HelpDropdown :as-sub="true" @open-about="handleOpenAbout" @open-fund="handleOpenFund" @open-markdown-help="handleOpenMarkdownHelp" />
265270
</MenubarContent>
266271
</MenubarMenu>
267272
</Menubar>
@@ -299,6 +304,7 @@ function copyToWeChat() {
299304
<AboutDialog :visible="aboutDialogVisible" @close="aboutDialogVisible = false" />
300305
<FundDialog :visible="fundDialogVisible" @close="fundDialogVisible = false" />
301306
<EditorStateDialog :visible="editorStateDialogVisible" @close="editorStateDialogVisible = false" />
307+
<MarkdownHelpDialog :visible="markdownHelpDialogVisible" @close="markdownHelpDialogVisible = false" />
302308
<AIImageGeneratorPanel v-model:open="uiStore.aiImageDialogVisible" />
303309
</template>
304310

0 commit comments

Comments
 (0)