提交 453e0bad 编写于 作者: 璃白.'s avatar 璃白. 🌻

feat:添加高度自适应和内容长度限制

上级 8df9a059
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
width: 600px !important; width: 600px !important;
margin: 40px auto; margin: 40px auto;
} }
body {
/* background-color: #222226; */
}
</style> </style>
</head> </head>
<body> <body>
...@@ -20,7 +23,7 @@ ...@@ -20,7 +23,7 @@
el: "#app", // required el: "#app", // required
value: "34567567567", value: "34567567567",
themeOptions: { themeOptions: {
dark: true, // dark: true,
borderColor: "#dbdbdb", borderColor: "#dbdbdb",
borderColorActive: "#409eff", borderColorActive: "#409eff",
textColor: "#303030", textColor: "#303030",
...@@ -36,19 +39,29 @@ ...@@ -36,19 +39,29 @@
ol: true, ol: true,
task: true, task: true,
table: true, table: true,
file: true,
fullScreen: true fullScreen: true
}, },
zIndex: 7000,
maxLength: 600,
showWordLimit: true,
rows: "auto",
rule: /http:\/\/www\.baidu\.com/,
canPreview: true, canPreview: true,
canAttachFile: true, canAttachFile: true,
placeholder: "请输入内容", placeholder: "请输入内容",
onFocus: function(res) { throttle: 1000,
console.log(res); // onFocus: function(res) {
}, // console.log(res);
// },
onBlur: function(res) { onBlur: function(res) {
console.log(res); console.log(res);
}, },
onInput: function(res) {
console.log("input");
},
onChange: function(res) { onChange: function(res) {
console.log(res); console.log("change");
}, },
onSubmit: function(res) { onSubmit: function(res) {
console.log(res); console.log(res);
......
此差异已折叠。
{ {
"name": "markdown-editor", "name": "markdown-editor",
"description": " A open source markdown editor of csdn codechina team contributed", "description": " A open source markdown editor of csdn codechina team contributed",
"version": "0.3.1", "version": "0.4.1",
"publisher": "guoweijia", "publisher": "guoweijia",
"scripts": { "scripts": {
"start": "webpack serve --mode=development", "start": "webpack serve --mode=development",
......
...@@ -8,6 +8,16 @@ ...@@ -8,6 +8,16 @@
:canPreview="canPreview" :canPreview="canPreview"
:toolsOptions="toolsOptions" :toolsOptions="toolsOptions"
:fullScreen.sync="fullScreen" :fullScreen.sync="fullScreen"
@upload="$refs.mdUploadFile.click()"
/>
<input
ref="mdUploadFile"
class="md_upload"
type="file"
hidden
name="md-upload-file"
id="md-upload-file"
@change="upload"
/> />
<markdownPreview :text="text" :html.sync="html" v-show="showPreview" /> <markdownPreview :text="text" :html.sync="html" v-show="showPreview" />
<markdown-editor <markdown-editor
...@@ -16,17 +26,25 @@ ...@@ -16,17 +26,25 @@
:fileList.sync="fileList" :fileList.sync="fileList"
:placeholder="placeholder" :placeholder="placeholder"
:isFocus.sync="isFocus" :isFocus.sync="isFocus"
:throttleTime="throttle"
:fullScreen.sync="fullScreen" :fullScreen.sync="fullScreen"
:maxLength="maxLength"
:textLength.sync="textLength"
:rows="rows"
@submit="submit" @submit="submit"
v-show="!showPreview" v-show="!showPreview"
/> />
<markdown-footer <div v-if="maxLength && showWordLimit" class="word_limit">
<span>{{ textLength }}</span
>/<span>{{ maxLength }}</span>
</div>
<!-- <markdown-footer
:fileList.sync="fileList" :fileList.sync="fileList"
:canAttachFile="canAttachFile" :canAttachFile="canAttachFile"
:isFocus.sync="isFocus" :isFocus.sync="isFocus"
:can-attach-file="canAttachFile" :can-attach-file="canAttachFile"
v-if="!showPreview && canAttachFile" v-if="!showPreview && canAttachFile"
/> /> -->
</div> </div>
</template> </template>
<script> <script>
...@@ -55,6 +73,10 @@ export default { ...@@ -55,6 +73,10 @@ export default {
type: [String, Number], type: [String, Number],
default: "" default: ""
}, },
throttle: {
type: Number,
default: 1000
},
canPreview: { canPreview: {
type: Boolean, type: Boolean,
default: true default: true
...@@ -62,6 +84,22 @@ export default { ...@@ -62,6 +84,22 @@ export default {
toolsOptions: { toolsOptions: {
type: Object, type: Object,
default: () => {} default: () => {}
},
rows: {
type: [Number, String],
default: ""
},
maxLength: {
type: [Number, String],
default: ""
},
showWordLimit: {
type: Boolean,
default: false
},
rule: {
type: RegExp,
default: /./
} }
}, },
data() { data() {
...@@ -72,6 +110,7 @@ export default { ...@@ -72,6 +110,7 @@ export default {
fileList: [], fileList: [],
text: "", text: "",
html: "", html: "",
textLength: "",
selectionInfo: { selectionInfo: {
selectorId: "", selectorId: "",
selectionStart: "", selectionStart: "",
...@@ -79,14 +118,20 @@ export default { ...@@ -79,14 +118,20 @@ export default {
} }
}; };
}, },
watch: { watch: {
html: { html: {
immediate: true, immediate: true,
handler: function(val) { handler: function(val) {
this.textLength = this.text.length;
this.$emit("change", { this.$emit("change", {
text: this.text, text: this.text,
html: this.html html: this.html
}); });
this.$emit("input", {
text: this.text,
html: this.html
});
} }
}, },
isFocus: { isFocus: {
...@@ -133,6 +178,9 @@ export default { ...@@ -133,6 +178,9 @@ export default {
} }
}, },
methods: { methods: {
upload(e) {
this.fileList = Array.from(e.target.files);
},
submit() { submit() {
this.$emit("submit", { this.$emit("submit", {
text: this.text, text: this.text,
...@@ -152,8 +200,16 @@ export default { ...@@ -152,8 +200,16 @@ export default {
padding: 10px 16px; padding: 10px 16px;
box-sizing: border-box; box-sizing: border-box;
transition: border 0.3s; transition: border 0.3s;
position: relative;
&.active { &.active {
border: 1px solid var(--md-editor-border-color-active); border: 1px solid var(--md-editor-border-color-active);
} }
.word_limit {
position: absolute;
right: 10px;
bottom: 8px;
font-size: 12px;
color: var(--md-editor-text-color);
}
} }
</style> </style>
...@@ -12,8 +12,24 @@ export function getSelectionInfo(selectorId) { ...@@ -12,8 +12,24 @@ export function getSelectionInfo(selectorId) {
}; };
} }
// 节流
export const throttle = function(fn, wait) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
fn.apply(context, args);
timer = null;
}, wait);
}
};
};
export const getPosition = function(selectorId) { export const getPosition = function(selectorId) {
const element = document.getElementById(selectorId); const element = document.getElementById(selectorId);
if (!element) return 0;
let cursorPos = 0; let cursorPos = 0;
if (document.selection) { if (document.selection) {
//IE //IE
...@@ -38,6 +54,13 @@ export function formatText(text, selectionInfo, startStr = "", endStr = "") { ...@@ -38,6 +54,13 @@ export function formatText(text, selectionInfo, startStr = "", endStr = "") {
return newText; return newText;
} }
export function setzIndex(index) {
document.documentElement.style.setProperty(
"--md-editor-fullScrren-zIndex",
index
);
}
// 初始化样式 // 初始化样式
export function initStyle({ export function initStyle({
dark, dark,
...@@ -51,13 +74,13 @@ export function initStyle({ ...@@ -51,13 +74,13 @@ export function initStyle({
}) { }) {
// 夜晚模式 // 夜晚模式
if (dark) { if (dark) {
borderColor = "#b2b2b2"; borderColor = "#44444F";
borderColorActive = "#fff"; borderColorActive = "#2998F2";
textColor = "#fff"; textColor = "#777888";
textColorActive = "#fff"; textColorActive = "#CCCCD8";
frameBgColor = "#343434"; frameBgColor = "#222226";
codeBgColor = "#484848"; codeBgColor = "#777888";
contentBgColor = "#484848"; contentBgColor = "#222226";
} }
if (frameBgColor) { if (frameBgColor) {
document.documentElement.style.setProperty( document.documentElement.style.setProperty(
...@@ -69,7 +92,7 @@ export function initStyle({ ...@@ -69,7 +92,7 @@ export function initStyle({
document.documentElement.style.setProperty( document.documentElement.style.setProperty(
"--md-editor-content-bg-color", "--md-editor-content-bg-color",
contentBgColor contentBgColor
); );
} }
if (codeBgColor) { if (codeBgColor) {
......
...@@ -6,4 +6,5 @@ ...@@ -6,4 +6,5 @@
--md-editor-frame-bg-color: #fff; --md-editor-frame-bg-color: #fff;
--md-editor-content-bg-color: #fff; --md-editor-content-bg-color: #fff;
--md-editor-code-bg-color: #f3f4f5; --md-editor-code-bg-color: #f3f4f5;
--md-editor-fullScrren-zIndex: 2000;
} }
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
<textarea <textarea
:id="id" :id="id"
@change="$emit('update:text', textContent)" @change="$emit('update:text', textContent)"
@input="input"
@focus="setFocus(true)" @focus="setFocus(true)"
@blur="setFocus(false)" @blur="setFocus(false)"
@paste="pasteFile" @paste="pasteFile"
...@@ -10,7 +11,9 @@ ...@@ -10,7 +11,9 @@
@keydown.ctrl.enter.exact="submit" @keydown.ctrl.enter.exact="submit"
v-model="textContent" v-model="textContent"
:placeholder="placeholder" :placeholder="placeholder"
rows="10" :maxlength="maxLength"
:rows="rows"
:style="{ height: editorHeight, overflow: autoSize ? 'hidden' : 'auto' }"
> >
</textarea> </textarea>
<span <span
...@@ -21,13 +24,21 @@ ...@@ -21,13 +24,21 @@
</div> </div>
</template> </template>
<script> <script>
import { getSelectionInfo, getPosition } from "@/assets/js/utils"; import {
getSelectionInfo,
getPosition,
throttle as throttleFn
} from "@/assets/js/utils";
export default { export default {
props: { props: {
fullScreen: { fullScreen: {
type: Boolean, type: Boolean,
default: false default: false
}, },
throttleTime: {
type: Number,
default: 1000
},
isFocus: { isFocus: {
type: Boolean, type: Boolean,
default: false default: false
...@@ -44,15 +55,25 @@ export default { ...@@ -44,15 +55,25 @@ export default {
type: [String, Number], type: [String, Number],
default: "" default: ""
}, },
maxLength: {
type: [String, Number],
default: ""
},
rows: {
type: [String, Number],
default: ""
},
selectionInfo: { selectionInfo: {
type: Object, type: Object,
default: () => {} default: () => {}
} }
}, },
data() { data() {
return { return {
id: new Date().getTime(), id: new Date().getTime(),
textContent: "" textContent: "",
editorHeight: "auto"
}; };
}, },
created() { created() {
...@@ -64,13 +85,62 @@ export default { ...@@ -64,13 +85,62 @@ export default {
handler: function(val) { handler: function(val) {
this.textContent = val; this.textContent = val;
} }
},
textContent: {
immediate: true,
handler: function() {
setTimeout(() => {
if (!this.autoSize) return;
this.reSizeHeight();
}, 0);
}
} }
}, },
beforeDestroy() { beforeDestroy() {
document.removeEventListener("mouseup", this.checkSelection); document.removeEventListener("mouseup", this.checkSelection);
}, },
computed: {
emitText() {
return throttleFn(() => {
this.$emit("update:text", this.textContent);
}, this.throttleTime);
},
autoSize() {
return this.rows === "auto";
}
},
methods: { methods: {
input() {
this.$emit("update:textLength", this.textContent.length);
this.emitText();
},
reSizeHeight() {
const textEl = document.getElementById(this.id);
if (!textEl) return;
const fontSize = getComputedStyle(textEl).getPropertyValue("font-size");
const lineHeight = getComputedStyle(textEl).getPropertyValue(
"line-height"
);
const fontFamily = getComputedStyle(textEl).getPropertyValue(
"font-family"
);
const hideElId = "hdieEl" + this.id;
let hideEl = document.getElementById(hideElId);
if (!hideEl) {
hideEl = document.createElement("div");
textEl.parentNode.appendChild(hideEl);
}
hideEl.id = hideElId;
hideEl.style.fontSize = fontSize;
hideEl.style.lineHeight = lineHeight;
hideEl.style.fontFamily = fontFamily;
hideEl.innerText = this.textContent;
const contentHeight = hideEl.offsetHeight;
this.editorHeight = `${contentHeight + parseFloat(fontSize) * 1.2}px`;
textEl.parentNode.removeChild(hideEl);
},
submit() { submit() {
this.$emit("submit"); this.$emit("submit");
}, },
...@@ -108,17 +178,17 @@ export default { ...@@ -108,17 +178,17 @@ export default {
<style lang="less" scoped> <style lang="less" scoped>
.md_textarea { .md_textarea {
position: relative; position: relative;
padding: 10px 0; padding: 14px 0;
background: var(--md-editor-content-bg-color); background: var(--md-editor-content-bg-color);
border-left: 1px solid var(--md-editor-border-color); // border-left: 1px solid var(--md-editor-border-color);
border-right: 1px solid var(--md-editor-border-color); // border-right: 1px solid var(--md-editor-border-color);
transition: border 0.3s; transition: border 0.3s;
padding: 14px; // padding: 14px;
box-sizing: border-box; box-sizing: border-box;
&.isFocus { // &.isFocus {
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; position: fixed;
width: 100vw; width: 100vw;
...@@ -126,23 +196,29 @@ export default { ...@@ -126,23 +196,29 @@ export default {
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
z-index: 99; z-index: var(--md-editor-fullScrren-zIndex);
padding: 40px 60px; padding: 40px 60px;
box-sizing: border-box; box-sizing: border-box;
textarea { textarea {
font-size: 20px; font-size: 20px;
} }
} }
textarea { textarea {
display: block; display: block;
width: 100%; width: 100%;
height: 100%; height: 100%;
box-sizing: border-box; box-sizing: border-box;
background: var(--md-editor-content-bg-color); background: var(--md-editor-content-bg-color);
color: var(--md-editor-text-color); color: var(--md-editor-text-color-active);
height: var(--md-editor-height);
resize: none; resize: none;
font-family: "Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas", font-family: "Menlo", "DejaVu Sans Mono", "Liberation Mono", "Consolas",
"Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace; "Ubuntu Mono", "Courier New", "andale mono", "lucida console", monospace;
&::placeholder {
color: var(--md-editor-text-color);
}
} }
.icon { .icon {
position: absolute; position: absolute;
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
:fullScreen="fullScreen" :fullScreen="fullScreen"
@setFullScreen="$emit('update:fullScreen', true)" @setFullScreen="$emit('update:fullScreen', true)"
@updateText="updateText" @updateText="updateText"
@upload="$emit('upload')"
v-for="(item, index) in toolsShow" v-for="(item, index) in toolsShow"
:key="index" :key="index"
:text="text" :text="text"
...@@ -55,6 +56,7 @@ export default { ...@@ -55,6 +56,7 @@ export default {
type: Object, type: Object,
default: () => {} default: () => {}
}, },
text: { text: {
type: [String, Number], type: [String, Number],
default: "" default: ""
...@@ -101,9 +103,9 @@ export default { ...@@ -101,9 +103,9 @@ export default {
{ {
name: "code", name: "code",
icon: "code", icon: "code",
tip: "插入代码", tip: "插入代码",
startStr: "`", startStr: "\n```\n",
endStr: "`" endStr: "\n```"
}, },
{ {
name: "link", name: "link",
...@@ -126,6 +128,13 @@ export default { ...@@ -126,6 +128,13 @@ export default {
startStr: "", startStr: "",
endStr: "" endStr: ""
}, },
{
name: "file",
icon: "tupian",
tip: "上传文件",
startStr: "",
endStr: ""
},
{ {
name: "task", name: "task",
icon: "renwu", icon: "renwu",
......
...@@ -26,10 +26,6 @@ export default { ...@@ -26,10 +26,6 @@ export default {
selectionInfo: { selectionInfo: {
type: Object, type: Object,
default: () => {} default: () => {}
},
uploadPath: {
type: String,
default: ""
} }
}, },
data() { data() {
...@@ -55,6 +51,9 @@ export default { ...@@ -55,6 +51,9 @@ export default {
this.updateText(`\n${ulNum}. `, ""); this.updateText(`\n${ulNum}. `, "");
this.ulNum++; this.ulNum++;
break; break;
case "file":
this.$emit("upload");
break;
case "fullScreen": case "fullScreen":
this.$emit("setFullScreen", true); this.$emit("setFullScreen", true);
break; break;
...@@ -80,6 +79,9 @@ export default { ...@@ -80,6 +79,9 @@ 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>
...@@ -2,7 +2,7 @@ import Vue from "vue"; ...@@ -2,7 +2,7 @@ import Vue from "vue";
import App from "./App"; import App from "./App";
import Vtip from "vtip"; import Vtip from "vtip";
import "vtip/lib/index.min.css"; import "vtip/lib/index.min.css";
import { initStyle, isNotEmpty } from "@/assets/js/utils"; import { initStyle, setzIndex, isNotEmpty } from "@/assets/js/utils";
import "@/assets/style/global.less"; import "@/assets/style/global.less";
Vue.use(Vtip.directive); Vue.use(Vtip.directive);
...@@ -10,13 +10,20 @@ Vue.use(Vtip.directive); ...@@ -10,13 +10,20 @@ Vue.use(Vtip.directive);
function initMdEditor(obj) { function initMdEditor(obj) {
const { const {
el, el,
onChange, onChange = () => {},
onUpload, onUpload = () => {},
onFocus, onFocus = () => {},
onBlur, onBlur = () => {},
onSubmit, onInput = () => {},
onSubmit = () => {},
placeholder, placeholder,
value, value,
zIndex = 2000,
rule,
rows = 10,
maxLength,
showWordLimit,
throttle,
canPreview, canPreview,
canAttachFile, canAttachFile,
themeOptions, themeOptions,
...@@ -24,7 +31,7 @@ function initMdEditor(obj) { ...@@ -24,7 +31,7 @@ function initMdEditor(obj) {
} = obj; } = 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);
new Vue({ new Vue({
render: h => render: h =>
h(App, { h(App, {
...@@ -32,6 +39,9 @@ function initMdEditor(obj) { ...@@ -32,6 +39,9 @@ function initMdEditor(obj) {
change(val) { change(val) {
onChange(val); onChange(val);
}, },
input(val) {
onInput(val);
},
focus(val) { focus(val) {
onFocus(val); onFocus(val);
}, },
...@@ -50,9 +60,14 @@ function initMdEditor(obj) { ...@@ -50,9 +60,14 @@ function initMdEditor(obj) {
props: { props: {
canAttachFile, canAttachFile,
value, value,
rule,
rows,
throttle,
canPreview, canPreview,
toolsOptions, toolsOptions,
placeholder placeholder,
maxLength,
showWordLimit
} }
}) })
}).$mount(el); }).$mount(el);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册