|
@@ -1,57 +1,73 @@
|
|
|
<template>
|
|
|
- <div class="czr-markdown-main">
|
|
|
- <div class="czr-markdown-main-menus">
|
|
|
- <template v-for="item in Menus">
|
|
|
- <template v-if="item.options?.length > 0">
|
|
|
- <el-dropdown>
|
|
|
+ <template v-if="!view">
|
|
|
+ <div class="czr-markdown-main flex size-full flex-col shadow">
|
|
|
+ <div class="flex items-center gap-2.5 p-2.5 shadow">
|
|
|
+ <template v-for="item in Menus">
|
|
|
+ <template v-if="item.options?.length > 0">
|
|
|
+ <el-dropdown>
|
|
|
+ <div
|
|
|
+ class="__hover button flex items-center gap-1"
|
|
|
+ :title="item.title"
|
|
|
+ >
|
|
|
+ <SvgIcon :name="item.icon" color="#000000" />
|
|
|
+ <SvgIcon
|
|
|
+ name="czr_arrow"
|
|
|
+ :rotate="90"
|
|
|
+ size="12"
|
|
|
+ color="#000000"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <template #dropdown>
|
|
|
+ <el-dropdown-menu>
|
|
|
+ <template v-for="op in item.options">
|
|
|
+ <el-dropdown-item @click="insertText(op.start, op.end)">{{
|
|
|
+ op.title
|
|
|
+ }}</el-dropdown-item>
|
|
|
+ </template>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </template>
|
|
|
+ </el-dropdown>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
<div
|
|
|
class="__hover button flex items-center gap-1"
|
|
|
+ @click="insertText(item.start, item.end)"
|
|
|
:title="item.title"
|
|
|
>
|
|
|
<SvgIcon :name="item.icon" color="#000000" />
|
|
|
- <SvgIcon
|
|
|
- name="czr_arrow"
|
|
|
- :rotate="90"
|
|
|
- size="12"
|
|
|
- color="#000000"
|
|
|
- />
|
|
|
</div>
|
|
|
- <template #dropdown>
|
|
|
- <el-dropdown-menu>
|
|
|
- <template v-for="op in item.options">
|
|
|
- <el-dropdown-item @click="insertText(op.start, op.end)">{{
|
|
|
- op.title
|
|
|
- }}</el-dropdown-item>
|
|
|
- </template>
|
|
|
- </el-dropdown-menu>
|
|
|
- </template>
|
|
|
- </el-dropdown>
|
|
|
+ </template>
|
|
|
+ <template v-if="item.split">
|
|
|
+ <div class="h-4 w-[1px] bg-[#000000]/30" />
|
|
|
+ </template>
|
|
|
</template>
|
|
|
- <template v-else>
|
|
|
- <div
|
|
|
- class="__hover button flex items-center gap-1"
|
|
|
- @click="insertText(item.start, item.end)"
|
|
|
- :title="item.title"
|
|
|
- >
|
|
|
- <SvgIcon :name="item.icon" color="#000000" />
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <template v-if="item.split">
|
|
|
- <div class="split" />
|
|
|
- </template>
|
|
|
- </template>
|
|
|
- </div>
|
|
|
- <div class="czr-markdown-main-content" :class="`layout-${layout}`">
|
|
|
- <div class="editor">
|
|
|
- <textarea
|
|
|
- ref="ref_textarea"
|
|
|
- v-model="markdownValue"
|
|
|
- @input="updateMarkdown"
|
|
|
- ></textarea>
|
|
|
</div>
|
|
|
+ <div class="flex flex-1 overflow-hidden" :class="`layout-${layout}`">
|
|
|
+ <div
|
|
|
+ class="flex-1 p-4"
|
|
|
+ :style="
|
|
|
+ layout === 'x'
|
|
|
+ ? 'border-right: 1px solid rgba(0, 0, 0, 0.15)'
|
|
|
+ : 'border-bottom: 1px solid rgba(0, 0, 0, 0.15)'
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <textarea
|
|
|
+ ref="ref_textarea"
|
|
|
+ class="size-full"
|
|
|
+ style="resize: none"
|
|
|
+ v-model="markdownValue"
|
|
|
+ @input="updateMarkdown"
|
|
|
+ ></textarea>
|
|
|
+ </div>
|
|
|
+ <div class="preview flex-1 p-4" v-html="markdownHtmlCpt"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <div class="czr-markdown-main">
|
|
|
<div class="preview" v-html="markdownHtmlCpt"></div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </template>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
@@ -67,7 +83,6 @@ renderer.link = ({ href, text }) => {
|
|
|
return `<a href="${href}">${text}</a>`
|
|
|
}
|
|
|
renderer.text = ({ text }) => {
|
|
|
- console.log(props)
|
|
|
if (props.rendererFunc) {
|
|
|
return props.rendererFunc({ text })
|
|
|
}
|
|
@@ -90,6 +105,7 @@ const props = defineProps({
|
|
|
default: 'x',
|
|
|
},
|
|
|
rendererFunc: <any>{},
|
|
|
+ view: { default: false },
|
|
|
})
|
|
|
const Menus: any = [
|
|
|
{
|
|
@@ -178,151 +194,107 @@ onMounted(() => {
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
.czr-markdown-main {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
|
|
- .czr-markdown-main-menus {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 10px;
|
|
|
- box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
|
|
- padding: 10px;
|
|
|
- .split {
|
|
|
- width: 1px;
|
|
|
- height: 16px;
|
|
|
- background-color: rgba(0, 0, 0, 0.3);
|
|
|
+ :deep(.preview) {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ overflow: auto;
|
|
|
+ /* 通用标题样式 */
|
|
|
+ h1,
|
|
|
+ h2,
|
|
|
+ h3,
|
|
|
+ h4,
|
|
|
+ h5,
|
|
|
+ h6 {
|
|
|
+ font-family:
|
|
|
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial,
|
|
|
+ sans-serif;
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 1.25;
|
|
|
+ margin-top: 1.5em;
|
|
|
+ margin-bottom: 0.5em;
|
|
|
+ color: #222;
|
|
|
}
|
|
|
- }
|
|
|
- .czr-markdown-main-content {
|
|
|
- flex: 1;
|
|
|
- display: flex;
|
|
|
- overflow: hidden;
|
|
|
- &.layout-y {
|
|
|
- flex-direction: column;
|
|
|
- .editor {
|
|
|
- border-bottom: 1px solid rgba(0, 0, 0, 0.15);
|
|
|
- }
|
|
|
- }
|
|
|
- &.layout-x {
|
|
|
- .editor {
|
|
|
- border-right: 1px solid rgba(0, 0, 0, 0.15);
|
|
|
- }
|
|
|
- }
|
|
|
- .editor,
|
|
|
- .preview {
|
|
|
- flex: 1;
|
|
|
- padding: 14px;
|
|
|
- }
|
|
|
- .editor {
|
|
|
- textarea {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- resize: none;
|
|
|
- }
|
|
|
+
|
|
|
+ /* H1 样式 */
|
|
|
+ h1 {
|
|
|
+ font-size: 2em;
|
|
|
+ border-bottom: 1px solid #eaecef;
|
|
|
+ padding-bottom: 0.3em;
|
|
|
+ margin-top: 0;
|
|
|
}
|
|
|
- :deep(.preview) {
|
|
|
- overflow: auto;
|
|
|
- /* 通用标题样式 */
|
|
|
- h1,
|
|
|
- h2,
|
|
|
- h3,
|
|
|
- h4,
|
|
|
- h5,
|
|
|
- h6 {
|
|
|
- font-family:
|
|
|
- -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,
|
|
|
- Arial, sans-serif;
|
|
|
- font-weight: 600;
|
|
|
- line-height: 1.25;
|
|
|
- margin-top: 1.5em;
|
|
|
- margin-bottom: 0.5em;
|
|
|
- color: #222;
|
|
|
- }
|
|
|
|
|
|
- /* H1 样式 */
|
|
|
- h1 {
|
|
|
- font-size: 2em;
|
|
|
- border-bottom: 1px solid #eaecef;
|
|
|
- padding-bottom: 0.3em;
|
|
|
- margin-top: 0;
|
|
|
- }
|
|
|
+ /* H2 样式 */
|
|
|
+ h2 {
|
|
|
+ font-size: 1.5em;
|
|
|
+ border-bottom: 1px solid #eaecef;
|
|
|
+ padding-bottom: 0.3em;
|
|
|
+ }
|
|
|
|
|
|
- /* H2 样式 */
|
|
|
- h2 {
|
|
|
- font-size: 1.5em;
|
|
|
- border-bottom: 1px solid #eaecef;
|
|
|
- padding-bottom: 0.3em;
|
|
|
- }
|
|
|
+ /* H3 样式 */
|
|
|
+ h3 {
|
|
|
+ font-size: 1.25em;
|
|
|
+ }
|
|
|
|
|
|
- /* H3 样式 */
|
|
|
- h3 {
|
|
|
- font-size: 1.25em;
|
|
|
- }
|
|
|
+ /* H4 样式 */
|
|
|
+ h4 {
|
|
|
+ font-size: 1em;
|
|
|
+ }
|
|
|
|
|
|
- /* H4 样式 */
|
|
|
- h4 {
|
|
|
- font-size: 1em;
|
|
|
- }
|
|
|
+ /* H5 样式 */
|
|
|
+ h5 {
|
|
|
+ font-size: 0.875em;
|
|
|
+ }
|
|
|
|
|
|
- /* H5 样式 */
|
|
|
- h5 {
|
|
|
- font-size: 0.875em;
|
|
|
+ /* H6 样式 */
|
|
|
+ h6 {
|
|
|
+ font-size: 0.85em;
|
|
|
+ color: #6a737d;
|
|
|
+ }
|
|
|
+ /* 默认链接样式 */
|
|
|
+ a {
|
|
|
+ color: #0366d6; /* 链接颜色 */
|
|
|
+ text-decoration: none; /* 去掉下划线 */
|
|
|
+ transition: color 0.2s; /* 颜色过渡效果 */
|
|
|
+ /* 鼠标悬停样式 */
|
|
|
+ &:hover {
|
|
|
+ color: #0550a8; /* 悬停颜色 */
|
|
|
+ text-decoration: underline; /* 显示下划线 */
|
|
|
}
|
|
|
-
|
|
|
- /* H6 样式 */
|
|
|
- h6 {
|
|
|
- font-size: 0.85em;
|
|
|
- color: #6a737d;
|
|
|
+ /* 已访问链接 */
|
|
|
+ &:visited {
|
|
|
+ color: #5a32a3; /* 紫色表示已访问 */
|
|
|
}
|
|
|
- /* 默认链接样式 */
|
|
|
- a {
|
|
|
- color: #0366d6; /* 链接颜色 */
|
|
|
- text-decoration: none; /* 去掉下划线 */
|
|
|
- transition: color 0.2s; /* 颜色过渡效果 */
|
|
|
- /* 鼠标悬停样式 */
|
|
|
- &:hover {
|
|
|
- color: #0550a8; /* 悬停颜色 */
|
|
|
- text-decoration: underline; /* 显示下划线 */
|
|
|
- }
|
|
|
- /* 已访问链接 */
|
|
|
- &:visited {
|
|
|
- color: #5a32a3; /* 紫色表示已访问 */
|
|
|
- }
|
|
|
|
|
|
- /* 活动链接 (点击时) */
|
|
|
- &:active {
|
|
|
- color: #d63384;
|
|
|
- }
|
|
|
+ /* 活动链接 (点击时) */
|
|
|
+ &:active {
|
|
|
+ color: #d63384;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- /*
|
|
|
- * 暗色系代码块样式
|
|
|
- * 适用于夜间/暗色模式的代码展示
|
|
|
- */
|
|
|
+ /*
|
|
|
+* 暗色系代码块样式
|
|
|
+* 适用于夜间/暗色模式的代码展示
|
|
|
+*/
|
|
|
|
|
|
- // 基础代码块样式
|
|
|
- pre {
|
|
|
- background-color: #1e1e1e; // 深灰背景
|
|
|
- border-radius: 6px; // 圆角
|
|
|
- padding: 1.25rem; // 内边距
|
|
|
- margin: 1.5rem 0; // 外边距
|
|
|
- overflow: auto; // 溢出滚动
|
|
|
- border: 1px solid #333; // 边框
|
|
|
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); // 阴影效果
|
|
|
+ // 基础代码块样式
|
|
|
+ pre {
|
|
|
+ background-color: #1e1e1e; // 深灰背景
|
|
|
+ border-radius: 6px; // 圆角
|
|
|
+ padding: 1.25rem; // 内边距
|
|
|
+ margin: 1.5rem 0; // 外边距
|
|
|
+ overflow: auto; // 溢出滚动
|
|
|
+ border: 1px solid #333; // 边框
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); // 阴影效果
|
|
|
|
|
|
- // 代码内容样式
|
|
|
- code {
|
|
|
- display: block;
|
|
|
- color: #d4d4d4; // 主文本颜色
|
|
|
- font-family:
|
|
|
- 'Fira Code', 'Consolas', 'Monaco', monospace; // 等宽字体栈
|
|
|
- font-size: 0.95em;
|
|
|
- line-height: 1.5;
|
|
|
- text-shadow: none;
|
|
|
- white-space: pre;
|
|
|
- }
|
|
|
+ // 代码内容样式
|
|
|
+ code {
|
|
|
+ display: block;
|
|
|
+ color: #d4d4d4; // 主文本颜色
|
|
|
+ font-family: 'Fira Code', 'Consolas', 'Monaco', monospace; // 等宽字体栈
|
|
|
+ font-size: 0.95em;
|
|
|
+ line-height: 1.5;
|
|
|
+ text-shadow: none;
|
|
|
+ white-space: pre;
|
|
|
}
|
|
|
}
|
|
|
}
|