|
@@ -1,40 +1,15 @@
|
|
|
<template>
|
|
|
- <div class="boxs">
|
|
|
- <!-- <el-upload
|
|
|
- class="avatar-uploader"
|
|
|
- name="file"
|
|
|
- action=""
|
|
|
- :show-file-list="false"
|
|
|
- :data="uploadData"
|
|
|
- :http-request="uploadHttpRequest"
|
|
|
- >
|
|
|
- </el-upload> -->
|
|
|
- <quill-editor v-model="content" ref="myQuillEditor" :options="editorOption" @blur="saveHtml" @change="saveHtml1" @focus="getFocus"></quill-editor>
|
|
|
- <!-- 富文本内容 -->
|
|
|
+ <div>
|
|
|
+ <div class="edit-box" contenteditable="true" :placeholder="placeholder" v-html="content" :key="keys" :id="keys"
|
|
|
+ @blur="saveValue($event)" @click="getFocus($event)" v-on:paste="handlePaste($event)">
|
|
|
</div>
|
|
|
-</template>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
<script>
|
|
|
- import { quillEditor } from "vue-quill-editor";
|
|
|
- import ImageBlot from './reviewClass'
|
|
|
- // 工具栏配置
|
|
|
-// const toolbarOptions = [
|
|
|
-// ["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
|
|
|
-// ["blockquote", "code-block"], // 引用 代码块
|
|
|
-// [{ header: 1 }, { header: 2 }], // 1、2 级标题
|
|
|
-// [{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
|
|
|
-// [{ script: "sub" }, { script: "super" }], // 上标/下标
|
|
|
-// [{ indent: "-1" }, { indent: "+1" }], // 缩进
|
|
|
-// // [{'direction': 'rtl'}], // 文本方向
|
|
|
-// [{ size: ["small", false, "large", "huge"] }], // 字体大小
|
|
|
-// [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
|
|
|
-// [{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
|
|
|
-// [{ font: [] }], // 字体种类
|
|
|
-// [{ align: [] }], // 对齐方式
|
|
|
-// ["clean"], // 清除文本格式
|
|
|
-// ["link", "image", "video"], // 链接、图片、视频
|
|
|
-// ];
|
|
|
- export default {
|
|
|
- props:{
|
|
|
+export default {
|
|
|
+ components: {},
|
|
|
+ props:{
|
|
|
value:{
|
|
|
type:String,
|
|
|
default:(value)=>{
|
|
@@ -47,12 +22,6 @@
|
|
|
return "请输入"
|
|
|
}
|
|
|
},
|
|
|
- filed:{
|
|
|
- type:String,
|
|
|
- default:()=>{
|
|
|
- return ''
|
|
|
- }
|
|
|
- },
|
|
|
keys:{
|
|
|
type:String,
|
|
|
default:()=>{
|
|
@@ -67,153 +36,181 @@
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
- data() {
|
|
|
- return {
|
|
|
- uploadData: {}, // 图片文件
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
content: this.value||"", // 内容
|
|
|
- // content: null,
|
|
|
- editorOption: {
|
|
|
- placeholder: this.placeholder,
|
|
|
- key:this.keys,
|
|
|
- // modules: {
|
|
|
- // toolbar: {
|
|
|
- // container: toolbarOptions,
|
|
|
- // handlers: {
|
|
|
- // image: function (value) {
|
|
|
- // if (value) {
|
|
|
- // // 触发input框选择图片文件
|
|
|
- // document.querySelector(".avatar-uploader input").click();
|
|
|
- // } else {
|
|
|
- // this.quill.format("image", false);
|
|
|
- // }
|
|
|
- // },
|
|
|
- // },
|
|
|
- // },
|
|
|
- // },
|
|
|
- },
|
|
|
- };
|
|
|
+ };
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ value(val){
|
|
|
+ this.content = val
|
|
|
},
|
|
|
- watch:{
|
|
|
- value(val){
|
|
|
- // var a = this.content.replace(/<p>|<\/p>/g,'')
|
|
|
- if(this.content!=val){
|
|
|
- this.content = val
|
|
|
- }
|
|
|
- },
|
|
|
+ },
|
|
|
+ computed: {},
|
|
|
+ created() {},
|
|
|
+ mounted() {},
|
|
|
+ methods: {
|
|
|
+ async handlePaste(event){
|
|
|
+
|
|
|
+ const items = (event.clipboardData || window.clipboardData).items;
|
|
|
+ let file = null;
|
|
|
+ if (!items || items.length === 0) {
|
|
|
+ this.$message.error("当前浏览器不支持本地");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ for (let i = 0; i < items.length; i++) {
|
|
|
+ // if (items[i].type.indexOf("text") !== -1) {
|
|
|
+ // var text = (e.originalEvent || e).clipboardData.getData('text/plain') || prompt('在这里输入文本');
|
|
|
+ // //清除回车
|
|
|
+ // text = text.replace(/\[\d+\]|\n|\r/ig,"")
|
|
|
+ // var content = event.target.innerHTML
|
|
|
+ // var str2 = content.slice(0, index.endOffset) + text + content.slice(index.endOffset);
|
|
|
+ // event.target.innerHTML = str2
|
|
|
+ // this.saveValue(event)
|
|
|
+ // // this.$forceUpdate()
|
|
|
+ // // if(this.tableHeight){
|
|
|
+ // // this.setHeight()
|
|
|
+ // // }
|
|
|
+ // break;
|
|
|
+ // }
|
|
|
+ if (items[i].type.indexOf("image") !== -1) {
|
|
|
+ var e = event || window.event
|
|
|
+ e.preventDefault();
|
|
|
+ file = items[i].getAsFile();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!file) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ var content = event.target.innerHTML
|
|
|
+ var str = await this.fileToBase64(file)
|
|
|
+ var randNum=Math.floor(Math.random()*(9999-1000))+1000;;
|
|
|
+ var new_img = '<img key="'+ randNum +'" src="' + str + '" style="width:80px;height: 80px;border: 1px solid #f9f6f675;vertical-align:middle">';
|
|
|
+
|
|
|
+ // 创建新的光标对象
|
|
|
+// var range = document.createRange();
|
|
|
+// // 将光标对象的范围界定为新建的表情节点
|
|
|
+// range.selectNodeContents(new_img);
|
|
|
+// // 定位光标位置在表情节点的最大长度位置
|
|
|
+// range.setStart(new_img, new_img.length);
|
|
|
+
|
|
|
+// // 将选区折叠为一个光标
|
|
|
+// range.collapse(true);
|
|
|
+// // 清除所有光标对象
|
|
|
+// selection.removeAllRanges();
|
|
|
+// // 添加新的光标对象
|
|
|
+// selection.addRange(range);
|
|
|
+// console.log(event.target.innerHTML)
|
|
|
+ var index = window.getSelection().getRangeAt(0)
|
|
|
+ var indexText = this.getColumn(event.target,window.getSelection())
|
|
|
+ var index2 = 0
|
|
|
+ var str2 = content.slice(0, indexText) + new_img + content.slice(indexText);
|
|
|
+ event.target.innerHTML = str2
|
|
|
+ this.$nextTick(()=>{
|
|
|
+ var selectedText = window.getSelection();
|
|
|
+ var selectedRange = document.createRange();
|
|
|
+ try{
|
|
|
+ for(var i=0;i<event.target.childNodes.length;i++){
|
|
|
+ if(event.target.childNodes[i].outerHTML == new_img){
|
|
|
+ index2 = i
|
|
|
+ throw new Error()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }catch{
|
|
|
+
|
|
|
+ }
|
|
|
+ selectedRange.setStart(event.target,index2+1);
|
|
|
+ selectedRange.collapse(true);
|
|
|
+ selectedText.removeAllRanges();
|
|
|
+ selectedText.addRange(selectedRange);
|
|
|
+ event.target.parentNode.focus();
|
|
|
+ })
|
|
|
+
|
|
|
+ // this.saveValue(event)
|
|
|
+ // var selection = window.getSelection()
|
|
|
+ // window.getSelection().addRange(this.index)
|
|
|
+ // console.log(window.getSelection().getRangeAt(0))
|
|
|
+
|
|
|
+ },
|
|
|
+ getColumn(node, selectObj) {
|
|
|
+ var baseNode = node;
|
|
|
+ var anchorNodePosition = this.getPosition(baseNode, selectObj.anchorNode, selectObj.anchorOffset);
|
|
|
+ var focusNodePosition = this.getPosition(baseNode, selectObj.focusNode, selectObj.focusOffset);
|
|
|
+ var num = Math.min(anchorNodePosition, focusNodePosition)
|
|
|
+ return num;
|
|
|
},
|
|
|
- computed:{
|
|
|
- quill(){
|
|
|
- return this.$refs.myQuillEditor.quill
|
|
|
+ getNodes(baseNode, path) {
|
|
|
+ // 拿到所有类型的节点
|
|
|
+ var temPath = path;
|
|
|
+ if (baseNode != null) {
|
|
|
+ temPath.push(baseNode);
|
|
|
+ if (baseNode.childNodes.length > 0) {
|
|
|
+ for (let i = 0; i < baseNode.childNodes.length; i++) {
|
|
|
+ this.getNodes(baseNode.childNodes[i], temPath);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
},
|
|
|
- mounted() {
|
|
|
- // let quill = this.$refs.myQuillEditor.quill
|
|
|
- // this.quill.enable(false)
|
|
|
- // this.$nextTick(()=>{
|
|
|
- this.quill.enable(true)
|
|
|
- // })
|
|
|
- this.$forceUpdate()
|
|
|
- this.quill.root.addEventListener('paste', evt => {
|
|
|
- if (evt.clipboardData && evt.clipboardData.files && evt.clipboardData.files.length) {
|
|
|
- evt.preventDefault();
|
|
|
- [].forEach.call(evt.clipboardData.files,async file => {
|
|
|
- if (!file.type.match(/^image\/(gif|jpe?g|a?png|bmp)/i)) {
|
|
|
- return
|
|
|
- }
|
|
|
- // 获取光标所在位置
|
|
|
- let length = this.quill.selection.savedRange.index
|
|
|
- var str = await this.fileToBase64(file)
|
|
|
- // this.quill.insertEmbed(length, 'image',{
|
|
|
- // url:str,
|
|
|
- // width:'',
|
|
|
- // height:'',
|
|
|
- // style:'width:80px;height: 80px;border: 1px solid #f9f6f675;vertical-align:middle'
|
|
|
- // })
|
|
|
- this.quill.insertEmbed(length, 'image',str)
|
|
|
- var html = `<img style="width:80px;height: 80px;border: 1px solid #f9f6f675;vertical-align:middle" src="${str}">`
|
|
|
- // this.$nextTick(()=>{
|
|
|
- this.content = this.content.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/g,html)
|
|
|
- this.quill.container.children[0].innerHTML = this.content
|
|
|
- this.$nextTick(()=>{
|
|
|
- this.quill.setSelection(length + 1)
|
|
|
- })
|
|
|
-
|
|
|
- })
|
|
|
+ getPosition(baseNode, node, offset) {
|
|
|
+ //根据节点获取给定node中offset位置在栏位中的实际位置
|
|
|
+ let path = [];
|
|
|
+ this.getNodes(baseNode, path);
|
|
|
+ var retIdx = 0;
|
|
|
+ for (let i = 0; i < path.length; i++) {
|
|
|
+ if (path[i] == node) {
|
|
|
+ retIdx += offset;
|
|
|
+ return retIdx;
|
|
|
+ } else {
|
|
|
+ if (path[i].nodeType == 3) {
|
|
|
+ retIdx += path[i].nodeValue.length;
|
|
|
+ }else if(i!=0 && path[i].nodeType == 1){
|
|
|
+ retIdx += path[i].outerHTML.length;
|
|
|
+ }
|
|
|
}
|
|
|
- }, false)
|
|
|
+ }
|
|
|
},
|
|
|
- methods: {
|
|
|
- // 将file文件上传转化为base64进行显示
|
|
|
- fileToBase64(file) {
|
|
|
- return new Promise((resolve, reject) => {
|
|
|
- ///FileReader类就是专门用来读文件的
|
|
|
- const reader = new FileReader()
|
|
|
- //开始读文件
|
|
|
- reader.readAsDataURL(file)
|
|
|
- reader.onload = () => resolve(reader.result)
|
|
|
- // 失败返回失败的信息
|
|
|
- reader.onerror = error => reject(error)
|
|
|
- })
|
|
|
- },
|
|
|
- //失去焦点
|
|
|
- saveHtml(event) {
|
|
|
- // this.quill.enable(false)
|
|
|
- // console.log("this.content", this.content);
|
|
|
- // this.content = this.content.replace(/<p>|<\/p>/g,'')
|
|
|
- // this.$emit('input',this.content)
|
|
|
- // this.$nextTick(()=>{
|
|
|
- // this.quill.enable(true)
|
|
|
- // })
|
|
|
-
|
|
|
- // this.$emit('value',{
|
|
|
- // filed:this.filed,
|
|
|
- // content:this.content
|
|
|
- // })
|
|
|
- },
|
|
|
- //值变化
|
|
|
- saveHtml1(event) {
|
|
|
- // var a = this.content.replace(/<p>|<\/p>/g,'')
|
|
|
- var a = this.content
|
|
|
- this.$emit('input',a)
|
|
|
- },
|
|
|
- getFocus(){
|
|
|
- this.quill.enable(true)
|
|
|
- },
|
|
|
+ // 将file文件上传转化为base64进行显示
|
|
|
+ fileToBase64(file) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const reader = new FileReader()
|
|
|
+ //开始读文件
|
|
|
+ reader.readAsDataURL(file)
|
|
|
+ reader.onload = () => resolve(reader.result)
|
|
|
+ // 失败返回失败的信息
|
|
|
+ reader.onerror = error => reject(error)
|
|
|
+ })
|
|
|
+ },
|
|
|
+ getFocus(event){
|
|
|
+ event.target.parentNode.focus();
|
|
|
},
|
|
|
- };
|
|
|
-</script>
|
|
|
-<style lang="scss">
|
|
|
- .boxs {
|
|
|
- /* line-height: normal !important;
|
|
|
- height: 500px;
|
|
|
- width: 700px;
|
|
|
- margin: 20px auto; */
|
|
|
- height: 100%;
|
|
|
- .ql-toolbar{
|
|
|
- display: none;
|
|
|
+ saveValue(event){
|
|
|
+ var a = event.target.innerHTML
|
|
|
+ this.$emit('input',a)
|
|
|
}
|
|
|
- .quill-editor{
|
|
|
- border:1px solid #dcdfe6;
|
|
|
- border-radius: 5px;
|
|
|
- .ql-container
|
|
|
- {
|
|
|
- border:0;
|
|
|
- .ql-editor{
|
|
|
- // display: flex;
|
|
|
- // flex-wrap: nowrap;
|
|
|
- align-items: end;
|
|
|
- }
|
|
|
- }
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+<style lang="scss" scoped>
|
|
|
+.edit-box{
|
|
|
+ padding: 0 15px;
|
|
|
+ min-height:40px;
|
|
|
+ line-height: 40px;
|
|
|
+ outline: #dcdfe6;
|
|
|
+ border:1px solid #DCDFE6;
|
|
|
+ border-radius:5px;
|
|
|
+ div{
|
|
|
+ height:30px !important;
|
|
|
+ line-height: 30px !important;
|
|
|
}
|
|
|
- .quill-editor:focus {
|
|
|
- border:1px solid #409eff;
|
|
|
- }
|
|
|
- .ql-editor.ql-blank::before {
|
|
|
- font-style: normal;
|
|
|
- color: #cacdd4;
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-</style>
|
|
|
-
|
|
|
-
|
|
|
+}
|
|
|
+.edit-box:empty::before {
|
|
|
+ content: attr(placeholder);
|
|
|
+ // margin-left:15px;
|
|
|
+ font-style: normal;
|
|
|
+ color: #cacdd4;
|
|
|
+}
|
|
|
+// .edit-box:focus::before {
|
|
|
+// content:none;
|
|
|
+// }
|
|
|
+</style>
|