提交 ac781e25 编写于 作者: 李少辉-开发者's avatar 李少辉-开发者 🎧

Merge branch 'master' of codechina.csdn.net:codechina_dev/markdown-editor

Signed-off-by: 李少辉-开发者's avatarlish <lish@csdn.net>
node_modules
node_modules/*
dist
.vscode
*.css
package-lock.json
*.LICENSE.*
......@@ -12,6 +12,22 @@
- 单张图片/单个附件上传
- Markdown & Html 内容获取
### v0.2
2021-06-15 v0.2更新,添加功能:
- [内容回显](#options)
- [顶部工具栏配置](#toolsoptions)
### v0.3
2021-06-17 v0.3更新,添加功能:
- [focus事件](#onfocus)
- [blur事件](#onblur)
- [ctrl+enter/command+enter快捷键](#onsubmit)
- [夜间模式](#themeoptions)
# 使用
1. 通过script标签引入
......@@ -34,21 +50,69 @@ new MdEditor({
| 属性 | 说明 | 类型 | 默认值 |
| ------ | ------ | ------ | ------ |
| el | 编辑器渲染的容器 | String | "#app"
| value | 编辑器回显内容 | String \| Number | ""
| themeOptions | 主题颜色配置 | Object | [themeOptions](#themeoptions)
| toolsOptions | 顶部工具栏配置 | Object | [toolsOptions](#toolsoptions)
| canAttachFile | 是否可以上传图片 | Boolean | true
| canPreview | 是否开启预览 | Boolean | true
| placeholder | placeholder | String | "请输入内容"
| onChange | 获取编辑器markdown及html内容 | Function | function(res) {} [示例](#onchange)
| onUpload | 上传文件钩子函数 | Function | function(file, callback) {} [示例](#onupload)
| onsubmit | 快捷键函数 | Function | function(file, callback) {} [示例](#onsubmit)
# themeOptions
| 属性 | 说明 | 类型 | 默认值 |
| ------ | ------ | ------ | ------ |
| dark | 夜间模式 | Boolean | false
| borderColor | 编辑器边框默认颜色 | String | "#dbdbdb"
| borderColorActive | 编辑器边框激活颜色 | String | "#409eff"
| textColor | 编辑器文字默认颜色 | String | "#303030"
| textColorActive | 编辑器文字激活颜色 | String | "#000"
# toolsOptions
| 属性 | 说明 | 类型 | 默认值 |
| ------ | ------ | ------ | ------ |
| bold | 加粗 | Boolean | true
| italic | 斜体 | Boolean | true
| quote | 引用 | Boolean | true
| code | 代码 | Boolean | true
| link | 链接 | Boolean | true
| ul | 无序列表 | Boolean | true
| ol | 有序列表 | Boolean | true
| task | 任务列表 | Boolean | true
| table | 表格 | Boolean | true
| fullScreen | 全屏模式 | Boolean | true
# onFocus
编辑器获取焦点时触发
```js
new MdEditor({
...,
onFocus: function(res) {
console.log(res) // { text: "...", html: "..." }
}
})
```
# onBlur
编辑器失去焦点时触发
```js
new MdEditor({
...,
onBlur: function(res) {
console.log(res) // { text: "...", html: "..." }
}
})
```
# onChange
用于获取markdown内容及编译后的html内容
......@@ -62,6 +126,19 @@ new MdEditor({
})
```
# onSubmit
按下ctrl+enter/command+enter组合键时触发
```js
new MdEditor({
...,
onSubmit: function(res) {
console.log(res) // { text: "...", html: "..." }
}
})
```
# onUpload
上传或粘贴文件时会触发此函数
......@@ -87,12 +164,26 @@ new MdEditor({
<script>
new MdEditor({
el: "#app", // required
value: "回显的内容",
themeOptions: {
borderColor: "#dbdbdb",
borderColorActive: "#409eff",
textColor: "#303030",
textColorActive: "#000"
},
toolsOptions: {
bold: true,
italic: false,
quote: true,
code: true,
link: false,
ul: true,
ol: true,
task: true,
table: false,
fullScreen: false
},
canPreview: true,
canAttachFile: true,
placeholder: "请输入内容",
onChange: function(res) {
......
......@@ -5,7 +5,12 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style></style>
<style>
.md_container {
width: 600px !important;
margin: 40px auto;
}
</style>
</head>
<body>
<div id="app"></div>
......@@ -13,17 +18,41 @@
<script>
new MdEditor({
el: "#app", // required
value: "34567567567",
themeOptions: {
dark: true,
borderColor: "#dbdbdb",
borderColorActive: "#409eff",
textColor: "#303030",
textColorActive: "#000"
},
toolsOptions: {
bold: true,
italic: true,
quote: true,
code: true,
link: true,
ul: true,
ol: true,
task: true,
table: true,
fullScreen: true
},
canPreview: true,
canAttachFile: true,
placeholder: "请输入内容",
onFocus: function(res) {
console.log(res);
},
onBlur: function(res) {
console.log(res);
},
onChange: function(res) {
console.log(res);
},
onSubmit: function(res) {
console.log(res);
},
onUpload: function(file, callback) {
new Promise((res, rej) => {
setTimeout(() => {
......
此差异已折叠。
{
"name": "markdown-editor",
"description": " A open source markdown editor of csdn codechina team contributed",
"version": "0.1.1",
"version": "0.3.1",
"publisher": "guoweijia",
"scripts": {
"start": "webpack serve --mode=development",
......
......@@ -5,6 +5,8 @@
:selectionInfo.sync="selectionInfo"
:showPreview.sync="showPreview"
:isFocus.sync="isFocus"
:canPreview="canPreview"
:toolsOptions="toolsOptions"
:fullScreen.sync="fullScreen"
/>
<markdownPreview :text="text" :html.sync="html" v-show="showPreview" />
......@@ -15,6 +17,7 @@
:placeholder="placeholder"
:isFocus.sync="isFocus"
:fullScreen.sync="fullScreen"
@submit="submit"
v-show="!showPreview"
/>
<markdown-footer
......@@ -47,6 +50,18 @@ export default {
canAttachFile: {
type: Boolean,
default: true
},
value: {
type: [String, Number],
default: ""
},
canPreview: {
type: Boolean,
default: true
},
toolsOptions: {
type: Object,
default: () => {}
}
},
data() {
......@@ -56,45 +71,6 @@ export default {
showPreview: false,
fileList: [],
text: "",
// text: `
// # 标题一标题一标题一
// ## 标题二标题二
// 666\`行内代码\`666
// \`\`\`js
// // 是注释呀
// /**
// * @params x
// */
// function fn() {
// return null;
// }
// \`\`\`
// **粗体文字**
// _斜体文字_
// > 这段是引用的内容\n
// > 这段是引用的内容
// > 这段是引用的内容
// [链接](url)
// - 无序列表
// - 无序列表
// - 无序列表
// 1. 有序列表
// 2. 有序列表
// 3. 有序列表
// - [ ] 任务列表
// - [x] 任务列表
// - [ ] 任务列表
// | header | header |
// | ------ | ------ |
// | cell | cell |
// | cell | cell |`,
html: "",
selectionInfo: {
selectorId: "",
......@@ -103,7 +79,6 @@ export default {
}
};
},
watch: {
html: {
immediate: true,
......@@ -114,6 +89,25 @@ export default {
});
}
},
isFocus: {
handler: function(val) {
const value = {
text: this.text,
html: this.html
};
if (val) {
this.$emit("focus", value);
} else {
this.$emit("blur", value);
}
}
},
value: {
immediate: true,
handler: function(val) {
this.text = val;
}
},
fileList: {
immediate: false,
deep: true,
......@@ -137,12 +131,21 @@ export default {
this.fileList = [];
}
}
},
methods: {
submit() {
this.$emit("submit", {
text: this.text,
html: this.html
});
}
}
};
</script>
<style lang="less" scoped>
.md_container {
width: 100%;
background: var(--md-editor-frame-bg-color);
// margin: 200px auto;
border: 1px solid var(--md-editor-border-color);
border-radius: 4px;
......
......@@ -40,11 +40,45 @@ export function formatText(text, selectionInfo, startStr = "", endStr = "") {
// 初始化样式
export function initStyle({
dark,
borderColor,
borderColorActive,
textColor,
textColorActive
textColorActive,
frameBgColor,
contentBgColor,
codeBgColor
}) {
// 夜晚模式
if (dark) {
borderColor = "#b2b2b2";
borderColorActive = "#fff";
textColor = "#fff";
textColorActive = "#fff";
frameBgColor = "#343434";
codeBgColor = "#484848";
contentBgColor = "#484848";
}
if (frameBgColor) {
document.documentElement.style.setProperty(
"--md-editor-frame-bg-color",
frameBgColor
);
}
if (contentBgColor) {
document.documentElement.style.setProperty(
"--md-editor-content-bg-color",
contentBgColor
);
}
if (codeBgColor) {
document.documentElement.style.setProperty(
"--md-editor-code-bg-color",
codeBgColor
);
}
if (borderColor) {
document.documentElement.style.setProperty(
"--md-editor-border-color",
......@@ -75,3 +109,7 @@ export function initStyle({
export function isNotEmpty(val) {
return val !== null && val !== undefined;
}
export function isNotFalse(val) {
return val !== false;
}
......@@ -106,7 +106,7 @@
}
p {
font-size: 18px;
color: #4d4d4d;
color: var(--md-editor-text-color-active);
font-weight: 400;
line-height: 26px !important;
margin: 0 0 16px;
......@@ -244,14 +244,14 @@
}
}
&:not(.hljs) {
background-color: #f3f4f5;
background-color: var(--md-editor-code-bg-color);
}
}
}
code {
border-radius: 4px;
display: inline-block;
background-color: #f3f4f5;
background-color: var(--md-editor-code-bg-color);
padding: 2px 6px;
font-family: -apple-system, SF UI Text, Arial, PingFang SC, Hiragino Sans GB,
Microsoft YaHei, WenQuanYi Micro Hei, sans-serif, SimHei, SimSun;
......
......@@ -3,4 +3,7 @@
--md-editor-border-color-active: #409eff;
--md-editor-text-color: #303030;
--md-editor-text-color-active: #000;
--md-editor-frame-bg-color: #fff;
--md-editor-content-bg-color: #fff;
--md-editor-code-bg-color: #f3f4f5;
}
......@@ -12,7 +12,7 @@ export default {
},
props: {
text: {
type: String,
type: [String, Number],
default: ""
},
html: {
......@@ -28,8 +28,9 @@ export default {
return html;
}
});
if (!val.trim()) return;
const html = marked(val);
const str = val + "";
// if (!str.trim()) return;
const html = marked(str);
this.$emit("update:html", html);
}
},
......
<template>
<div :class="['md_textarea', { fullScreen }]">
<div :class="['md_textarea', { fullScreen, isFocus }]">
<textarea
:id="id"
@change="$emit('update:text', textContent)"
@focus="setFocus(true)"
@blur="setFocus(false)"
@paste="pasteFile"
@keydown.meta.enter.exact="submit"
@keydown.ctrl.enter.exact="submit"
v-model="textContent"
:placeholder="placeholder"
rows="10"
......@@ -36,15 +38,15 @@ export default {
},
fileList: {
type: Array,
default: ()=>[]
default: () => []
},
text: {
type: String,
default: ''
type: [String, Number],
default: ""
},
selectionInfo: {
type: Object,
default: ()=>{}
default: () => {}
}
},
data() {
......@@ -67,24 +69,26 @@ export default {
beforeDestroy() {
document.removeEventListener("mouseup", this.checkSelection);
},
methods: {
submit() {
this.$emit("submit");
},
setFocus(val) {
this.$emit('update:isFocus', val)
this.$emit("update:isFocus", val);
},
checkSelection() {
const info = getSelectionInfo(this.id);
if (!info) {
const cursorPoint = getPosition(this.id);
this.$emit('update:selectionInfo',{
this.$emit("update:selectionInfo", {
selectorId: this.id,
selectionStart: cursorPoint,
selectionEnd: cursorPoint
})
});
return;
}
this.$emit('update:selectionInfo',info)
this.$emit("update:selectionInfo", info);
},
pasteFile(event) {
let fileList = [];
......@@ -96,7 +100,7 @@ export default {
}
}
if (!fileList.length) return;
this.$emit('update:fileList', fileList)
this.$emit("update:fileList", fileList);
}
}
};
......@@ -105,7 +109,16 @@ export default {
.md_textarea {
position: relative;
padding: 10px 0;
background: #fff;
background: var(--md-editor-content-bg-color);
border-left: 1px solid var(--md-editor-border-color);
border-right: 1px solid var(--md-editor-border-color);
transition: border 0.3s;
padding: 14px;
box-sizing: border-box;
&.isFocus {
border-left: 1px solid var(--md-editor-border-color-active);
border-right: 1px solid var(--md-editor-border-color-active);
}
&.fullScreen {
position: fixed;
width: 100vw;
......@@ -125,6 +138,7 @@ export default {
width: 100%;
height: 100%;
box-sizing: border-box;
background: var(--md-editor-content-bg-color);
color: var(--md-editor-text-color);
resize: none;
font-family: "Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas",
......
<template>
<div :class="['md_footer', { active: isFocus }]">
<div class="doc"></div>
<upload-files @changeFileList="$emit('update:fileList', $event)" :fileList="fileList" v-if="canAttachFile" />
<upload-files
@changeFileList="$emit('update:fileList', $event)"
:fileList="fileList"
v-if="canAttachFile"
/>
</div>
</template>
<script>
......@@ -9,19 +13,19 @@ import uploadFiles from "./upload-files";
export default {
components: { uploadFiles },
props: {
isFocus: {
isFocus: {
type: Boolean,
default: false
},
canAttachFile: {
canAttachFile: {
type: Boolean,
default: false
},
fileList: {
type: Array,
default: ()=>[]
default: () => []
}
},
}
};
</script>
<style lang="less" scoped>
......
......@@ -2,12 +2,13 @@
<div :class="['md_header', { active: isFocus }]">
<div class="header_tabs">
<div
:class="['tab_item', { active: !showPreview }]"
:class="['tab_item', { active: canPreview && !showPreview }]"
@click="setShowPreview(false)"
>
编辑
</div>
<div
v-if="canPreview"
:class="['tab_item', { active: showPreview }]"
@click="setShowPreview(true)"
>
......@@ -20,7 +21,7 @@
:fullScreen="fullScreen"
@setFullScreen="$emit('update:fullScreen', true)"
@updateText="updateText"
v-for="(item, index) in toolButtonList"
v-for="(item, index) in toolsShow"
:key="index"
:text="text"
:selectionInfo="selectionInfo"
......@@ -29,6 +30,7 @@
</div>
</template>
<script>
import { isNotFalse } from "@/assets/js/utils";
import toolButton from "./tool-button";
export default {
components: { toolButton },
......@@ -45,8 +47,16 @@ export default {
type: Boolean,
default: false
},
canPreview: {
type: Boolean,
default: true
},
toolsOptions: {
type: Object,
default: () => {}
},
text: {
type: String,
type: [String, Number],
default: ""
},
selectionInfo: {
......@@ -54,6 +64,16 @@ export default {
default: () => {}
}
},
computed: {
toolsShow() {
const toolsList = this.toolButtonList;
const toolsOptions = this.toolsOptions;
if (!toolsOptions) return toolsList;
return toolsList.filter(item => {
return isNotFalse(toolsOptions[item.name]);
});
}
},
data() {
return {
toolButtonList: [
......
......@@ -20,22 +20,22 @@ export default {
default: false
},
text: {
type: String,
default: ''
type: [String, Number],
default: ""
},
selectionInfo: {
type: Object,
default: ()=>{}
default: () => {}
},
uploadPath: {
type: String,
default: ''
default: ""
}
},
data() {
return {
ulNum: 1
}
};
},
methods: {
handleTool(type, startStr, endStr) {
......@@ -53,21 +53,21 @@ export default {
case "ol":
let ulNum = this.ulNum;
this.updateText(`\n${ulNum}. `, "");
this.ulNum++
this.ulNum++;
break;
case "fullScreen":
this.$emit('setFullScreen', true)
this.$emit("setFullScreen", true);
break;
default:
break;
}
},
updateText(startStr, endStr) {
const originalText = this.text
const selectionInfo = this.selectionInfo
const newText = formatText(originalText, selectionInfo, startStr, endStr)
this.$emit('updateText',newText)
}
const originalText = this.text;
const selectionInfo = this.selectionInfo;
const newText = formatText(originalText, selectionInfo, startStr, endStr);
this.$emit("updateText", newText);
}
}
};
</script>
......
......@@ -12,9 +12,15 @@ function initMdEditor(obj) {
el,
onChange,
onUpload,
onFocus,
onBlur,
onSubmit,
placeholder,
value,
canPreview,
canAttachFile,
themeOptions
themeOptions,
toolsOptions
} = obj;
if (!el || !document.querySelector(el)) throw new Error("请指定容器");
if (isNotEmpty(themeOptions)) initStyle(themeOptions);
......@@ -26,6 +32,15 @@ function initMdEditor(obj) {
change(val) {
onChange(val);
},
focus(val) {
onFocus(val);
},
blur(val) {
onBlur(val);
},
submit(val) {
onSubmit(val);
},
upload({ val, callback }) {
onUpload(val, function(res) {
callback(res);
......@@ -34,6 +49,9 @@ function initMdEditor(obj) {
},
props: {
canAttachFile,
value,
canPreview,
toolsOptions,
placeholder
}
})
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册