重构编辑器,进度50%
This commit is contained in:
1335
package-lock.json
generated
1335
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,8 @@
|
|||||||
"url": "https://blog.liuyuyang.net"
|
"url": "https://blog.liuyuyang.net"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@bytemd/plugin-highlight": "^1.21.0",
|
||||||
|
"@bytemd/react": "^1.21.0",
|
||||||
"antd": "^5.19.3",
|
"antd": "^5.19.3",
|
||||||
"apexcharts": "^3.41.0",
|
"apexcharts": "^3.41.0",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
@@ -32,7 +34,6 @@
|
|||||||
"react-toastify": "^9.1.3",
|
"react-toastify": "^9.1.3",
|
||||||
"sass": "^1.77.8",
|
"sass": "^1.77.8",
|
||||||
"sort-by": "^0.0.2",
|
"sort-by": "^0.0.2",
|
||||||
"vditor": "^3.10.4",
|
|
||||||
"vite-plugin-sass-dts": "^1.3.25",
|
"vite-plugin-sass-dts": "^1.3.25",
|
||||||
"zustand": "^4.5.4"
|
"zustand": "^4.5.4"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,13 +1,25 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { Card } from "antd"
|
import { Card } from "antd"
|
||||||
import Breadcrumb from "../Breadcrumbs"
|
|
||||||
import { titleSty } from '@/styles/sty'
|
import { titleSty } from '@/styles/sty'
|
||||||
|
import { ReactNode } from "react"
|
||||||
|
|
||||||
export default ({ value, className }: { value: string, className?: string }) => {
|
interface Props {
|
||||||
|
value: string,
|
||||||
|
children?: ReactNode,
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ({ value, children, className }: Props) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Card title={<Breadcrumb pageName={value} />} className={`${titleSty} ${className}`} />
|
<Card className={`${titleSty} p-4 mb-2 ${className}`}>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<h2 className="font-semibold text-black dark:text-white text-xl">{value}</h2>
|
||||||
|
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
207
src/pages/Create/components/Editor/index.scss
Normal file
207
src/pages/Create/components/Editor/index.scss
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
.markdown-body {
|
||||||
|
color: #595959;
|
||||||
|
font-size: 15px;
|
||||||
|
font-family: -apple-system, system-ui, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
|
||||||
|
background-image: linear-gradient(90deg, rgba(60, 10, 30, 0.04) 3%, rgba(0, 0, 0, 0) 3%), linear-gradient(360deg, rgba(60, 10, 30, 0.04) 3%, rgba(0, 0, 0, 0) 3%);
|
||||||
|
background-size: 20px 20px;
|
||||||
|
background-position: center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 段落 */
|
||||||
|
.markdown-body p {
|
||||||
|
color: #595959;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 2;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 段落间距控制 */
|
||||||
|
.markdown-body p+p {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标题的通用设置 */
|
||||||
|
.markdown-body h1,
|
||||||
|
.markdown-body h2,
|
||||||
|
.markdown-body h3,
|
||||||
|
.markdown-body h4,
|
||||||
|
.markdown-body h5,
|
||||||
|
.markdown-body h6 {
|
||||||
|
padding: 30px 0;
|
||||||
|
margin: 0;
|
||||||
|
color: #135ce0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 一级标题 */
|
||||||
|
.markdown-body h1 {
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 22px;
|
||||||
|
margin: 50px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 一级标题前缀,用来放背景图,支持透明度控制 */
|
||||||
|
.markdown-body h1:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
top: -10px;
|
||||||
|
left: 50%;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background-size: 100% 100%;
|
||||||
|
opacity: .36;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABfVBMVEX///8Ad/8AgP8AgP8AgP8Aff8AgP8Af/8AgP8AVf8Af/8Af/8AgP8AgP8Af/8Afv8AAP8Afv8Afv8Aef8AgP8AdP8Afv8AgP8AgP8Acf8Ae/8AgP8Af/8AgP8Af/8Af/8AfP8Afv8AgP8Af/8Af/8Afv8Afv8AgP8Afv8AgP8Af/8Af/8AgP8AgP8Afv8AgP8Af/8AgP8AgP8AgP8Ae/8Afv8Af/8AgP8Af/8AgP8Af/8Af/8Aff8Af/8Abf8AgP8Af/8AgP8Af/8Af/8Afv8AgP8AgP8Afv8Afv8AgP8Af/8Aff8AgP8Afv8AgP8Aff8AgP8AfP8AgP8Ae/8AgP8Af/8AgP8AgP8AgP8Afv8AgP8AgP8AgP8Afv8AgP8AgP8AgP8AgP8AgP8Af/8AgP8Af/8Af/8Aev8Af/8AgP8Aff8Afv8AgP8AgP8AgP8Af/8AgP8Af/8Af/8AgP8Afv8AgP8AgP8AgP8AgP8Af/8AeP8Af/8Af/8Af//////rzEHnAAAAfXRSTlMAD7CCAivatxIDx5EMrP19AXdLEwgLR+6iCR/M0yLRzyFF7JupSXn8cw6v60Q0QeqzKtgeG237HMne850/6Qeq7QaZ+WdydHtj+OM3qENCMRYl1B3K2U7wnlWE/mhlirjkODa9FN/BF7/iNV/2kASNZpX1Wlf03C4stRGxgUPclqoAAAABYktHRACIBR1IAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gEaBzgZ4yeM3AAAAT9JREFUOMvNUldbwkAQvCAqsSBoABE7asSOBRUVVBQNNuy9996789+9cMFAMHnVebmdm+/bmdtbQv4dOFOW2UjPzgFyLfo6nweKfIMOBYWwFtmMPGz2Yj2pJI0JDq3udJW6VVbmKa9I192VQFV1ktXUAl5NB0cd4KpnORqsEO2ZIRpF9gJfE9Dckqq0KuZt7UAH5+8EPF3spjsRpCeQNO/tA/qDwIDA+OCQbBoKA8NOdjMySgcZGVM6jwcgRuUiSs0nlPFNSrEpJfU0jTLD6llqbvKxei7OzvkFNQohi0vAsj81+MoqsCaoPOQFgus/1LyxichW+hS2JWCHZ7VlF9jb187pIAYcHiViHAMnp5mTjJ8B5xeEXF4B1ze/fTh/C0h398DDI9HB07O8ci+vRBdvdGnfP4gBuM8vw7X/G3wDmFhFZEdxzjMAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTgtMDEtMjZUMDc6NTY6MjUrMDE6MDA67pVWAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE4LTAxLTI2VDA3OjU2OjI1KzAxOjAwS7Mt6gAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAAWdEVYdFRpdGxlAGp1ZWppbl9sb2dvIGNvcHlxapmKAAAAV3pUWHRSYXcgcHJvZmlsZSB0eXBlIGlwdGMAAHic4/IMCHFWKCjKT8vMSeVSAAMjCy5jCxMjE0uTFAMTIESANMNkAyOzVCDL2NTIxMzEHMQHy4BIoEouAOoXEXTyQjWVAAAAAElFTkSuQmCC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 二级标题 */
|
||||||
|
.markdown-body h2 {
|
||||||
|
position: relative;
|
||||||
|
font-size: 20px;
|
||||||
|
border-left: 4px solid;
|
||||||
|
padding: 0 0 0 10px;
|
||||||
|
margin: 30px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 三级标题 */
|
||||||
|
.markdown-body h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无序列表 */
|
||||||
|
.markdown-body ul {
|
||||||
|
list-style: disc outside;
|
||||||
|
margin-left: 2em;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无序列表内容 */
|
||||||
|
.markdown-body li {
|
||||||
|
line-height: 2;
|
||||||
|
color: #595959;
|
||||||
|
margin-bottom: 0;
|
||||||
|
list-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 已加载图片 */
|
||||||
|
.markdown-body img.loaded {
|
||||||
|
margin: 0 auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 引用 */
|
||||||
|
.markdown-body blockquote {
|
||||||
|
background: #fff9f9;
|
||||||
|
margin: 2em 0;
|
||||||
|
padding: 2px 20px;
|
||||||
|
border-left: 4px solid #b2aec5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 引用文字 */
|
||||||
|
.markdown-body blockquote p {
|
||||||
|
color: #666;
|
||||||
|
line-height: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 链接 */
|
||||||
|
.markdown-body a {
|
||||||
|
color: #036aca;
|
||||||
|
border-bottom: 1px solid rgba(3, 106, 202, .8);
|
||||||
|
font-weight: 400;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 加粗 */
|
||||||
|
.markdown-body strong {
|
||||||
|
color: #036aca;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 加粗斜体 */
|
||||||
|
.markdown-body em strong {
|
||||||
|
color: #036aca;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分隔线 */
|
||||||
|
.markdown-body hr {
|
||||||
|
border-top: 1px solid #135ce0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 代码 */
|
||||||
|
.markdown-body pre {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body pre,
|
||||||
|
.markdown-body code {
|
||||||
|
overflow: auto;
|
||||||
|
position: relative;
|
||||||
|
line-height: 1.75;
|
||||||
|
font-family: Menlo, Monaco, Consolas, Courier New, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body pre>code {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 15px 12px;
|
||||||
|
margin: 0;
|
||||||
|
word-break: normal;
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
color: #DCDCDC;
|
||||||
|
background: #1E1E1E;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body code {
|
||||||
|
word-break: break-word;
|
||||||
|
border-radius: 2px;
|
||||||
|
overflow-x: auto;
|
||||||
|
background-color: #fff5f5;
|
||||||
|
color: #ff502c;
|
||||||
|
font-size: .87em;
|
||||||
|
padding: .065em .4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格 */
|
||||||
|
.markdown-body table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 1rem 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body table th,
|
||||||
|
.markdown-body table td {
|
||||||
|
border: 1px solid #dfe2e5;
|
||||||
|
padding: .6em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body table tr {
|
||||||
|
border-top: 1px solid #dfe2e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body table tr:nth-child(2n) {
|
||||||
|
background-color: #f6f8fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
From: icewolf-sec (御坂19008号)
|
||||||
|
Description: 新增HTML5 键盘文字标签样式
|
||||||
|
*/
|
||||||
|
.markdown-body kbd {
|
||||||
|
color: white;
|
||||||
|
background-color: #135ce0;
|
||||||
|
border: 1px solid #135ce0;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 2px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: auto 5px auto;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bytemd {
|
||||||
|
height: 600px;
|
||||||
|
}
|
||||||
47
src/pages/Create/components/Editor/index.tsx
Normal file
47
src/pages/Create/components/Editor/index.tsx
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { Editor, Viewer } from '@bytemd/react'
|
||||||
|
import 'bytemd/dist/index.css'
|
||||||
|
import highlight from '@bytemd/plugin-highlight'
|
||||||
|
import 'highlight.js/styles/vs2015.css';
|
||||||
|
import "./index.scss"
|
||||||
|
|
||||||
|
const plugins = [
|
||||||
|
highlight()
|
||||||
|
]
|
||||||
|
|
||||||
|
// import FileUpload from "@/components/FileUpload";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
value?: string,
|
||||||
|
getValue: (value: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditorMD = ({ value, getValue }: Props) => {
|
||||||
|
const [content, setContent] = useState('')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Editor
|
||||||
|
value={content}
|
||||||
|
plugins={plugins}
|
||||||
|
onChange={(v) => {
|
||||||
|
setContent(v)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 文件上传 */}
|
||||||
|
{/* <FileUpload
|
||||||
|
dir="article"
|
||||||
|
open={openUploadModalOpen}
|
||||||
|
onSuccess={(urls: string[]) => {
|
||||||
|
urls.forEach((path: string) => {
|
||||||
|
vd?.insertValue(``);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onCancel={() => setOpenUploadModalOpen(false)}
|
||||||
|
/> */}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditorMD;
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#vditor {
|
|
||||||
border: none;
|
|
||||||
|
|
||||||
.vditor-toolbar {
|
|
||||||
padding-left: 0 !important;
|
|
||||||
background-color: transparent;
|
|
||||||
border-bottom: none;
|
|
||||||
|
|
||||||
.vditor-tooltipped__ai {
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 25px;
|
|
||||||
height: 25px;
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.vditor-reset {
|
|
||||||
background-color: transparent !important;
|
|
||||||
|
|
||||||
.vditor-ir__marker--heading {
|
|
||||||
color: #727cf5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 针对表单元素的光标样式
|
|
||||||
pre {
|
|
||||||
// 设置光标样式
|
|
||||||
caret-color: #727cf5; // 光标颜色
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,278 +0,0 @@
|
|||||||
import { useEffect, useState } from "react";
|
|
||||||
import Vditor from "vditor";
|
|
||||||
import "vditor/dist/index.css";
|
|
||||||
import "./index.scss"
|
|
||||||
import FileUpload from "@/components/FileUpload";
|
|
||||||
|
|
||||||
const toolbar = [
|
|
||||||
{
|
|
||||||
hotkey: "⌘H",
|
|
||||||
icon:
|
|
||||||
'<svg><use xlink:href="#vditor-icon-headings"></use></svg>',
|
|
||||||
name: "headings",
|
|
||||||
tipPosition: "ne",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⌘B",
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-bold"></use></svg>',
|
|
||||||
name: "bold",
|
|
||||||
prefix: "**",
|
|
||||||
suffix: "**",
|
|
||||||
tipPosition: "ne",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⌘I",
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-italic"></use></svg>',
|
|
||||||
name: "italic",
|
|
||||||
prefix: "*",
|
|
||||||
suffix: "*",
|
|
||||||
tipPosition: "ne",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⌘D",
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-strike"></use></svg>',
|
|
||||||
name: "strike",
|
|
||||||
prefix: "~~",
|
|
||||||
suffix: "~~",
|
|
||||||
tipPosition: "ne",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⌘K",
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-link"></use></svg>',
|
|
||||||
name: "link",
|
|
||||||
prefix: "[",
|
|
||||||
suffix: "](https://)",
|
|
||||||
tipPosition: "n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "|",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⌘L",
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-list"></use></svg>',
|
|
||||||
name: "list",
|
|
||||||
prefix: "* ",
|
|
||||||
tipPosition: "n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⌘O",
|
|
||||||
icon:
|
|
||||||
'<svg><use xlink:href="#vditor-icon-ordered-list"></use></svg>',
|
|
||||||
name: "ordered-list",
|
|
||||||
prefix: "1. ",
|
|
||||||
tipPosition: "n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⌘J",
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-check"></use></svg>',
|
|
||||||
name: "check",
|
|
||||||
prefix: "* [ ] ",
|
|
||||||
tipPosition: "n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⇧⌘I",
|
|
||||||
icon:
|
|
||||||
'<svg><use xlink:href="#vditor-icon-outdent"></use></svg>',
|
|
||||||
name: "outdent",
|
|
||||||
tipPosition: "n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⇧⌘O",
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-indent"></use></svg>',
|
|
||||||
name: "indent",
|
|
||||||
tipPosition: "n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "|",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⌘;",
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-quote"></use></svg>',
|
|
||||||
name: "quote",
|
|
||||||
prefix: "> ",
|
|
||||||
tipPosition: "n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⇧⌘H",
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-line"></use></svg>',
|
|
||||||
name: "line",
|
|
||||||
prefix: "---",
|
|
||||||
tipPosition: "n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⌘U",
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-code"></use></svg>',
|
|
||||||
name: "code",
|
|
||||||
prefix: "```",
|
|
||||||
suffix: "\n```",
|
|
||||||
tipPosition: "n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⌘G",
|
|
||||||
icon:
|
|
||||||
'<svg><use xlink:href="#vditor-icon-inline-code"></use></svg>',
|
|
||||||
name: "inline-code",
|
|
||||||
prefix: "`",
|
|
||||||
suffix: "`",
|
|
||||||
tipPosition: "n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "|",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-upload"></use></svg>',
|
|
||||||
name: "upload",
|
|
||||||
tipPosition: "n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⌘M",
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-table"></use></svg>',
|
|
||||||
name: "table",
|
|
||||||
prefix: "| col1",
|
|
||||||
suffix:
|
|
||||||
" | col2 | col3 |\n| --- | --- | --- |\n| | | |\n| | | |",
|
|
||||||
tipPosition: "n",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "|",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⌘Z",
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-undo"></use></svg>',
|
|
||||||
name: "undo",
|
|
||||||
tipPosition: "nw",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
hotkey: "⌘Y",
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-redo"></use></svg>',
|
|
||||||
name: "redo",
|
|
||||||
tipPosition: "nw",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "|",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon:
|
|
||||||
'<svg><use xlink:href="#vditor-icon-align-center"></use></svg>',
|
|
||||||
name: "outline",
|
|
||||||
tipPosition: "nw",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-theme"></use></svg>',
|
|
||||||
name: "content-theme",
|
|
||||||
tipPosition: "nw",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: '<svg><use xlink:href="#vditor-icon-code-theme"></use></svg>',
|
|
||||||
name: "code-theme",
|
|
||||||
tipPosition: "nw",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "br",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
interface VditorProps {
|
|
||||||
value?: string,
|
|
||||||
getValue: (value: string) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const VditorEditor = ({ value, getValue }: VditorProps) => {
|
|
||||||
const [openUploadModalOpen, setOpenUploadModalOpen] = useState(false);
|
|
||||||
const [vd, setVd] = useState<Vditor>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const vditor = new Vditor("vditor", {
|
|
||||||
minHeight: 550,
|
|
||||||
// 禁止缓存数据
|
|
||||||
cache: {
|
|
||||||
enable: false
|
|
||||||
},
|
|
||||||
preview: {
|
|
||||||
// 限制防抖时间
|
|
||||||
delay: 500
|
|
||||||
},
|
|
||||||
toolbar,
|
|
||||||
// upload: {
|
|
||||||
// handler: async (files) => {
|
|
||||||
// console.log(files, 333);
|
|
||||||
|
|
||||||
// const formData = new FormData();
|
|
||||||
// files.forEach(file => {
|
|
||||||
// formData.append('files', file);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // 添加额外参数
|
|
||||||
// formData.append('dir', 'article');
|
|
||||||
|
|
||||||
// const res = await fetch(`${baseURL}/file`, {
|
|
||||||
// method: "POST",
|
|
||||||
// body: formData,
|
|
||||||
// headers: {
|
|
||||||
// "Authorization": `Bearer ${store.token}`
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const { code, message, data } = await res.json();
|
|
||||||
// if (code !== 200) return message.error("文件上传失败:" + message);
|
|
||||||
|
|
||||||
// // 插入到编辑器中
|
|
||||||
// data.forEach((path: string) => {
|
|
||||||
// vditor.insertValue(``);
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
input: (value) => {
|
|
||||||
// 把数据传给父组件
|
|
||||||
getValue(value)
|
|
||||||
},
|
|
||||||
after: () => {
|
|
||||||
// 获取文件上传按钮
|
|
||||||
const uploadButton = document.querySelector('.vditor-toolbar [data-type="upload"]')!;
|
|
||||||
|
|
||||||
// 添加点击事件监听器
|
|
||||||
uploadButton.addEventListener('click', (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
console.log('文件上传图标被点击');
|
|
||||||
// 在这里添加你的自定义逻辑
|
|
||||||
|
|
||||||
setOpenUploadModalOpen(true)
|
|
||||||
});
|
|
||||||
|
|
||||||
setVd(vditor);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
vd?.destroy();
|
|
||||||
setVd(undefined);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// 监听 value 变化并更新编辑器内容
|
|
||||||
useEffect(() => {
|
|
||||||
if (vd && value !== undefined && value !== vd.getValue()) {
|
|
||||||
vd.setValue(value);
|
|
||||||
}
|
|
||||||
}, [value, vd]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div id="vditor" className="vditor" />
|
|
||||||
|
|
||||||
{/* 文件上传 */}
|
|
||||||
<FileUpload
|
|
||||||
dir="article"
|
|
||||||
open={openUploadModalOpen}
|
|
||||||
onSuccess={(urls: string[]) => {
|
|
||||||
urls.forEach((path: string) => {
|
|
||||||
vd?.insertValue(``);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onCancel={() => setOpenUploadModalOpen(false)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default VditorEditor;
|
|
||||||
@@ -3,7 +3,7 @@ import { useSearchParams } from 'react-router-dom';
|
|||||||
import { Button, Card, Drawer, Dropdown, MenuProps, message } from 'antd';
|
import { Button, Card, Drawer, Dropdown, MenuProps, message } from 'antd';
|
||||||
|
|
||||||
import Title from '@/components/Title';
|
import Title from '@/components/Title';
|
||||||
import VditorEditor from './components/VditorMD';
|
import Editor from './components/Editor';
|
||||||
import PublishForm from './components/PublishForm';
|
import PublishForm from './components/PublishForm';
|
||||||
|
|
||||||
import { Article } from '@/types/app/article';
|
import { Article } from '@/types/app/article';
|
||||||
@@ -151,11 +151,8 @@ const CreatePage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title value="创作" />
|
<Title value="创作">
|
||||||
|
<div className='flex space-x-4'>
|
||||||
<Card className='relative mt-2'>
|
|
||||||
<div className='flex justify-end w-full'>
|
|
||||||
<div className='relative z-50 flex w-[24%] space-x-4'>
|
|
||||||
<Dropdown.Button menu={{ items }}>创作神器</Dropdown.Button>
|
<Dropdown.Button menu={{ items }}>创作神器</Dropdown.Button>
|
||||||
|
|
||||||
<Button className='w-full flex justify-between' onClick={saveBtn} >
|
<Button className='w-full flex justify-between' onClick={saveBtn} >
|
||||||
@@ -166,11 +163,10 @@ const CreatePage = () => {
|
|||||||
<GrFormNext className='text-2xl' /> 下一步
|
<GrFormNext className='text-2xl' /> 下一步
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Title>
|
||||||
|
|
||||||
<div className='relative -top-[40px]'>
|
<Card className='[&>.ant-card-body]:!p-0 overflow-hidden rounded-xl'>
|
||||||
<VditorEditor value={content} getValue={getVditorData} />
|
<Editor value={content} getValue={getVditorData} />
|
||||||
</div>
|
|
||||||
|
|
||||||
<Drawer
|
<Drawer
|
||||||
title={id ? "编辑文章" : "发布文章"}
|
title={id ? "编辑文章" : "发布文章"}
|
||||||
|
|||||||
Reference in New Issue
Block a user