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

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

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