提交 5e516f8d 编写于 作者: 璃白.'s avatar 璃白. 🌻

feat:添加getValue/setValue/foucs/blur事件等

上级 453e0bad
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<title>Document</title> <title>Document</title>
<style> <style>
.md_container { .md_container {
width: 600px !important; /* width: 600px !important; */
margin: 40px auto; margin: 40px auto;
} }
body { body {
...@@ -17,13 +17,15 @@ ...@@ -17,13 +17,15 @@
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<button id="a">111111</button>
<button id="b">2222222</button>
<script src="./markdown-editor.js"></script> <script src="./markdown-editor.js"></script>
<script> <script>
new MdEditor({ const ee = new MdEditor({
el: "#app", // required el: "#app", // required
value: "34567567567", value: "![pic](https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3681880960,455182084&fm=26&gp=0.jpg)\n\n<a href='https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3681880960,455182084&fm=26&gp=0.jpg'>图片</a>\n\n<img src='https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3681880960,455182084&fm=26&gp=0.jpg' />",
themeOptions: { themeOptions: {
// dark: true, dark: false,
borderColor: "#dbdbdb", borderColor: "#dbdbdb",
borderColorActive: "#409eff", borderColorActive: "#409eff",
textColor: "#303030", textColor: "#303030",
...@@ -32,36 +34,40 @@ ...@@ -32,36 +34,40 @@
toolsOptions: { toolsOptions: {
bold: true, bold: true,
italic: true, italic: true,
quote: true, quote: false,
code: true, code: true,
link: true, link: false,
ul: true, ul: true,
ol: true, ol: true,
task: true, task: true,
table: true, table: true,
file: true, file: true,
fullScreen: true fullScreen: false
}, },
zIndex: 7000, zIndex: 7000,
maxLength: 600, maxLength: 1000,
showWordLimit: true, showWordLimit: true,
rows: "auto", rows: "8",
rule: /http:\/\/www\.baidu\.com/, filePathRule: /^https:\/\/ss2\.bdstatic\.com/,
canPreview: true, canPreview: true,
canAttachFile: true, // canAttachFile: true,
placeholder: "请输入内容", placeholder: "请输入内容",
throttle: 1000, throttle: 1000,
// onFocus: function(res) { // onFocus: function(res) {
// console.log(res); // console.log(res);
// }, // },
onLoad: function(res) {
console.log('load',res);
},
onBlur: function(res) { onBlur: function(res) {
console.log(res); console.log(res);
}, },
onInput: function(res) { onInput: function(res) {
console.log("input"); console.log("input",res);
}, },
onChange: function(res) { onChange: function(res) {
console.log("change"); console.log("change",res);
}, },
onSubmit: function(res) { onSubmit: function(res) {
console.log(res); console.log(res);
...@@ -80,6 +86,17 @@ ...@@ -80,6 +86,17 @@
}); });
} }
}); });
document.querySelector('#a').onclick = function() {
ee.setValue(234523453245345)
}
document.querySelector('#b').onclick = function() {
ee.setValue(222222222222)
}
</script> </script>
</body> </body>
</html> </html>
此差异已折叠。
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
"publisher": "guoweijia", "publisher": "guoweijia",
"scripts": { "scripts": {
"start": "webpack serve --mode=development", "start": "webpack serve --mode=development",
"build": "webpack --mode=production" "build": "webpack --mode=production",
"p": "npm run build && ssh -t root@45.76.193.214 rm -rf /test/markdown-editor.js exit && scp -r dist/markdown-editor.js root@45.76.193.214:/test/"
}, },
"devDependencies": { "devDependencies": {
"chalk": "^4.1.1", "chalk": "^4.1.1",
......
<template> <template>
<div :class="['md_container', { active: isFocus }]"> <div
:class="['md_container', { active: isFocus, fullScreen }]"
:id="'md_' + id"
>
<markdown-header <markdown-header
:text.sync="text" :text.sync="text"
:selectionInfo.sync="selectionInfo" :selectionInfo.sync="selectionInfo"
...@@ -7,7 +10,9 @@ ...@@ -7,7 +10,9 @@
:isFocus.sync="isFocus" :isFocus.sync="isFocus"
:canPreview="canPreview" :canPreview="canPreview"
:toolsOptions="toolsOptions" :toolsOptions="toolsOptions"
:zIndex="zIndex"
:fullScreen.sync="fullScreen" :fullScreen.sync="fullScreen"
:themeOptions="themeOptions"
@upload="$refs.mdUploadFile.click()" @upload="$refs.mdUploadFile.click()"
/> />
<input <input
...@@ -31,6 +36,7 @@ ...@@ -31,6 +36,7 @@
:maxLength="maxLength" :maxLength="maxLength"
:textLength.sync="textLength" :textLength.sync="textLength"
:rows="rows" :rows="rows"
:id="textareaId"
@submit="submit" @submit="submit"
v-show="!showPreview" v-show="!showPreview"
/> />
...@@ -52,7 +58,7 @@ import markdownHeader from "./components/header/md-header"; ...@@ -52,7 +58,7 @@ import markdownHeader from "./components/header/md-header";
import markdownFooter from "./components/footer/md-footer"; import markdownFooter from "./components/footer/md-footer";
import markdownEditor from "./components/content/md-textarea"; import markdownEditor from "./components/content/md-textarea";
import markdownPreview from "./components/content/md-preview"; import markdownPreview from "./components/content/md-preview";
import { formatText } from "@/assets/js/utils"; import { formatText, checktUrl } from "@/assets/js/utils";
export default { export default {
components: { components: {
markdownHeader, markdownHeader,
...@@ -65,41 +71,72 @@ export default { ...@@ -65,41 +71,72 @@ export default {
type: String, type: String,
default: "请输入内容" default: "请输入内容"
}, },
canAttachFile: { id: {
type: Boolean, type: String,
default: true default: ""
}, },
// canAttachFile: {
// type: Boolean,
// default: true
// },
// 初始化时赋值
value: { value: {
type: [String, Number], type: [String, Number],
default: "" default: ""
}, },
// 全屏时的z-index
zIndex: {
type: [String, Number],
default: ""
},
// input时间节流
throttle: { throttle: {
type: Number, type: Number,
default: 1000 default: 0
}, },
// 是否可以预览
canPreview: { canPreview: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// 主题
themeOptions: {
type: Object,
default: () => {}
},
focus: {
type: Boolean,
default: false
},
// 工具栏
toolsOptions: { toolsOptions: {
type: Object, type: Object,
default: () => {} default: () => {}
}, },
// 行高度
rows: { rows: {
type: [Number, String], type: [Number, String],
default: "" default: ""
}, },
// 最大长度
maxLength: { maxLength: {
type: [Number, String], type: [Number, String],
default: "" default: ""
}, },
// 显示字数限制
showWordLimit: { showWordLimit: {
type: Boolean, type: Boolean,
default: false default: false
}, },
rule: { // 图片路径规则
filePathRule: {
type: RegExp, type: RegExp,
default: /./ default: () => {}
}
},
computed: {
textareaId() {
return "textarea_" + this.id;
} }
}, },
data() { data() {
...@@ -118,20 +155,44 @@ export default { ...@@ -118,20 +155,44 @@ export default {
} }
}; };
}, },
created() {
setTimeout(() => {
this.$emit("load", {
text: this.text,
html: this.html
});
}, 0);
},
watch: { watch: {
focus: {
handler: function(val) {
const textEl = document.getElementById(this.textareaId);
if (val) {
textEl.focus();
} else {
textEl.blur();
}
}
},
text: {
immediate: true,
handler: function(val) {
this.textLength = val.length;
}
},
html: { html: {
immediate: true, immediate: true,
handler: function(val) { handler: function(val) {
this.textLength = this.text.length; const emitContent = {
this.$emit("change", {
text: this.text,
html: this.html
});
this.$emit("input", {
text: this.text, text: this.text,
html: this.html html: this.html
}); };
if (this.filePathRule) {
const checkResult = checktUrl(val, this.filePathRule);
emitContent.invalidList = checkResult;
}
this.$emit("change", emitContent);
this.$emit("input", emitContent);
} }
}, },
isFocus: { isFocus: {
...@@ -201,6 +262,16 @@ export default { ...@@ -201,6 +262,16 @@ export default {
box-sizing: border-box; box-sizing: border-box;
transition: border 0.3s; transition: border 0.3s;
position: relative; position: relative;
&.fullScreen {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
margin: 0;
border: none !important;
z-index: var(--md-editor-fullScrren-zIndex);
}
&.active { &.active {
border: 1px solid var(--md-editor-border-color-active); border: 1px solid var(--md-editor-border-color-active);
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
export function getSelectionInfo(selectorId) { export function getSelectionInfo(selectorId) {
const selector = document.getElementById(selectorId); const selector = document.getElementById(selectorId);
if (!selector) return;
// const selection = window.getSelection(); // const selection = window.getSelection();
const { selectionStart = 0, selectionEnd = 0 } = selector; const { selectionStart = 0, selectionEnd = 0 } = selector;
if (selectionStart === selectionEnd) return ""; if (selectionStart === selectionEnd) return "";
...@@ -136,3 +137,19 @@ export function isNotEmpty(val) { ...@@ -136,3 +137,19 @@ export function isNotEmpty(val) {
export function isNotFalse(val) { export function isNotFalse(val) {
return val !== false; return val !== false;
} }
export function checktUrl(val, rule) {
if (!val || !rule) return;
const hideEl = document.createElement("div");
hideEl.style.display = "none";
hideEl.innerHTML = val;
const imgList = Array.from(hideEl.getElementsByTagName("img"));
return imgList.filter(item => !rule.test(item.src)).map(item => item.src);
}
export function checkBoswer() {
const agent = navigator.userAgent.match(
/(iPhone|iPod|Android|ios|iOS|iPad|Backerry|WebOS|Symbian|Windows Phone|Phone)/i
);
return agent !== null;
}
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* { * {
padding: 0; padding: 0;
margin: 0; margin: 0;
-webkit-tap-highlight-color: rgba(0,0,0,0);;
} }
html, html,
......
@font-face { @font-face {
font-family: "iconfont"; /* Project id */ font-family: "iconfont"; /* Project id */
src: url('./font/iconfont.ttf?t=1622632202276') format('truetype'); src: url('font/iconfont.ttf?t=1624355824015') format('truetype');
} }
.iconfont { .iconfont {
...@@ -51,11 +51,11 @@ ...@@ -51,11 +51,11 @@
content: "\eaef"; content: "\eaef";
} }
.icon-quxiaoquanping_o:before { .icon-tupian:before {
content: "\eb98"; content: "\e7ed";
} }
.icon-tupian:before { .icon-quxiaoquanping_o:before {
content: "\e607"; content: "\eb98";
} }
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
:placeholder="placeholder" :placeholder="placeholder"
:maxlength="maxLength" :maxlength="maxLength"
:rows="rows" :rows="rows"
:style="{ height: editorHeight, overflow: autoSize ? 'hidden' : 'auto' }" :style="{ height: editorHeight, overflow: editorOverFlow }"
> >
</textarea> </textarea>
<span <span
...@@ -31,6 +31,10 @@ import { ...@@ -31,6 +31,10 @@ import {
} from "@/assets/js/utils"; } from "@/assets/js/utils";
export default { export default {
props: { props: {
id: {
type: String,
default: ""
},
fullScreen: { fullScreen: {
type: Boolean, type: Boolean,
default: false default: false
...@@ -71,9 +75,9 @@ export default { ...@@ -71,9 +75,9 @@ export default {
data() { data() {
return { return {
id: new Date().getTime(),
textContent: "", textContent: "",
editorHeight: "auto" editorHeight: "auto",
editorOverFlow: "auto"
}; };
}, },
created() { created() {
...@@ -86,6 +90,15 @@ export default { ...@@ -86,6 +90,15 @@ export default {
this.textContent = val; this.textContent = val;
} }
}, },
fullScreen: {
immediate: true,
handler: function() {
setTimeout(() => {
// if (!this.autoSize) return;
this.reSizeHeight();
}, 0);
}
},
textContent: { textContent: {
immediate: true, immediate: true,
handler: function() { handler: function() {
...@@ -138,7 +151,13 @@ export default { ...@@ -138,7 +151,13 @@ export default {
hideEl.style.fontFamily = fontFamily; hideEl.style.fontFamily = fontFamily;
hideEl.innerText = this.textContent; hideEl.innerText = this.textContent;
const contentHeight = hideEl.offsetHeight; const contentHeight = hideEl.offsetHeight;
this.editorHeight = `${contentHeight + parseFloat(fontSize) * 1.2}px`; this.editorHeight = this.fullScreen
? "calc(100% - 42px)"
: this.autoSize
? `${contentHeight + parseFloat(fontSize) * 1.2}px`
: "auto";
this.editorOverFlow =
this.autoSize && !this.fullScreen ? "hidden" : "auto";
textEl.parentNode.removeChild(hideEl); textEl.parentNode.removeChild(hideEl);
}, },
submit() { submit() {
...@@ -189,18 +208,13 @@ export default { ...@@ -189,18 +208,13 @@ export default {
// border-left: 1px solid var(--md-editor-border-color-active); // border-left: 1px solid var(--md-editor-border-color-active);
// border-right: 1px solid var(--md-editor-border-color-active); // border-right: 1px solid var(--md-editor-border-color-active);
// } // }
&.fullScreen { &.fullScreen {
position: fixed; height: 100%;
width: 100vw;
height: 100vh;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: var(--md-editor-fullScrren-zIndex);
padding: 40px 60px;
box-sizing: border-box;
textarea { textarea {
font-size: 20px; font-size: 20px;
max-height: 100%;
overflow-y: auto;
} }
} }
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
v-for="(item, index) in toolsShow" v-for="(item, index) in toolsShow"
:key="index" :key="index"
:text="text" :text="text"
:zIndex="zIndex"
:themeOptions="themeOptions"
:selectionInfo="selectionInfo" :selectionInfo="selectionInfo"
/> />
</div> </div>
...@@ -52,11 +54,18 @@ export default { ...@@ -52,11 +54,18 @@ export default {
type: Boolean, type: Boolean,
default: true default: true
}, },
themeOptions: {
type: Object,
default: () => {}
},
toolsOptions: { toolsOptions: {
type: Object, type: Object,
default: () => {} default: () => {}
}, },
zIndex: {
type: [String,Number],
default: ''
},
text: { text: {
type: [String, Number], type: [String, Number],
default: "" default: ""
......
<template> <template>
<div <div
v-tip.top="info.tip" v-if="isMobile"
@click="handleTool(info.name, info.startStr, info.endStr)"
class="tool_button"
>
<span :class="['icon iconfont', `icon-${info.icon}`]"></span>
</div>
<div
v-else
v-tip.top="options"
@click="handleTool(info.name, info.startStr, info.endStr)" @click="handleTool(info.name, info.startStr, info.endStr)"
class="tool_button" class="tool_button"
> >
...@@ -8,7 +16,7 @@ ...@@ -8,7 +16,7 @@
</div> </div>
</template> </template>
<script> <script>
import { formatText } from "@/assets/js/utils"; import { formatText, checkBoswer } from "@/assets/js/utils";
export default { export default {
props: { props: {
info: { info: {
...@@ -23,6 +31,14 @@ export default { ...@@ -23,6 +31,14 @@ export default {
type: [String, Number], type: [String, Number],
default: "" default: ""
}, },
zIndex: {
type: [String, Number],
default: ""
},
themeOptions: {
type: Object,
default: () => {}
},
selectionInfo: { selectionInfo: {
type: Object, type: Object,
default: () => {} default: () => {}
...@@ -33,6 +49,22 @@ export default { ...@@ -33,6 +49,22 @@ export default {
ulNum: 1 ulNum: 1
}; };
}, },
computed: {
darkMode() {
return this.themeOptions && this.themeOptions.dark;
},
isMobile() {
const isMobile = checkBoswer();
return isMobile;
},
options() {
return {
content: this.info.tip,
zIndex: parseInt(this.zIndex) + 1,
theme: this.darkMode ? "dark" : "light"
};
}
},
methods: { methods: {
handleTool(type, startStr, endStr) { handleTool(type, startStr, endStr) {
switch (type) { switch (type) {
...@@ -79,9 +111,6 @@ export default { ...@@ -79,9 +111,6 @@ export default {
&:hover { &:hover {
color: var(--md-editor-text-color-active); color: var(--md-editor-text-color-active);
} }
&.icon-tupian {
font-size: 24px;
}
} }
} }
</style> </style>
...@@ -8,8 +8,15 @@ import "@/assets/style/global.less"; ...@@ -8,8 +8,15 @@ import "@/assets/style/global.less";
Vue.use(Vtip.directive); Vue.use(Vtip.directive);
function initMdEditor(obj) { function initMdEditor(obj) {
const { const _this = this;
this.value = {
text: "",
html: ""
};
this.vEl = "";
let {
el, el,
onLoad = () => {},
onChange = () => {}, onChange = () => {},
onUpload = () => {}, onUpload = () => {},
onFocus = () => {}, onFocus = () => {},
...@@ -19,11 +26,11 @@ function initMdEditor(obj) { ...@@ -19,11 +26,11 @@ function initMdEditor(obj) {
placeholder, placeholder,
value, value,
zIndex = 2000, zIndex = 2000,
rule, filePathRule,
rows = 10, rows = 6,
maxLength, maxLength,
showWordLimit, showWordLimit,
throttle, throttle = 1000,
canPreview, canPreview,
canAttachFile, canAttachFile,
themeOptions, themeOptions,
...@@ -32,45 +39,84 @@ function initMdEditor(obj) { ...@@ -32,45 +39,84 @@ function initMdEditor(obj) {
if (!el || !document.querySelector(el)) throw new Error("请指定容器"); if (!el || !document.querySelector(el)) throw new Error("请指定容器");
if (isNotEmpty(themeOptions)) initStyle(themeOptions); if (isNotEmpty(themeOptions)) initStyle(themeOptions);
if (isNotEmpty(zIndex)) setzIndex(zIndex); if (isNotEmpty(zIndex)) setzIndex(zIndex);
new Vue({ const id = new Date().getTime() + "";
const props = {
canAttachFile,
value,
themeOptions,
filePathRule,
rows,
id,
throttle,
canPreview,
toolsOptions,
placeholder,
maxLength,
zIndex,
focus: false,
showWordLimit
};
const listeners = {
change(val) {
onChange(val);
_this.value = val;
},
input(val) {
onInput(val);
_this.value = val;
},
load(val) {
onLoad(val);
_this.value = val;
},
focus(val) {
onFocus(val);
_this.value = val;
},
blur(val) {
onBlur(val);
_this.value = val;
},
submit(val) {
onSubmit(val);
_this.value = val;
},
upload({ val, callback }) {
onUpload(val, function(res) {
callback(res);
});
}
};
this.vEl = new Vue({
render: h => render: h =>
h(App, { h(App, {
on: { on: listeners,
change(val) { props: props
onChange(val);
},
input(val) {
onInput(val);
},
focus(val) {
onFocus(val);
},
blur(val) {
onBlur(val);
},
submit(val) {
onSubmit(val);
},
upload({ val, callback }) {
onUpload(val, function(res) {
callback(res);
});
}
},
props: {
canAttachFile,
value,
rule,
rows,
throttle,
canPreview,
toolsOptions,
placeholder,
maxLength,
showWordLimit
}
}) })
}).$mount(el); }).$mount(el);
this.getValue = function(callback) {
if (callback) {
callback(this.value);
}
return this.value;
};
this.setValue = function(val) {
if (!val) return;
props.value = val + "";
this.vEl.$forceUpdate();
};
this.focus = function() {
props.focus = true;
this.vEl.$forceUpdate();
};
this.blur = function() {
props.focus = false;
this.vEl.$forceUpdate();
};
} }
window.MdEditor = initMdEditor; window.MdEditor = initMdEditor;
...@@ -8,6 +8,7 @@ module.exports = { ...@@ -8,6 +8,7 @@ module.exports = {
}, },
devServer: { devServer: {
contentBase: path.resolve(__dirname, "dist"), contentBase: path.resolve(__dirname, "dist"),
host: '0.0.0.0',
open: true open: true
}, },
resolve: { resolve: {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册