企业网站服务费怎么做记账凭证,昆山广告制作公司,做单不能用什么网站,申晨推荐的营销网站多种富文本
一、Tinymce富文本
踩坑#xff1a;disabled和readonly效果问题
解决方法#xff1a;
【Tinymce富文本】如何实现disabled和readonly效果_tinymce disabled-CSDN博客
二、vue-quill-editor富文本
官方链接#xff1a; vue-quill-editor Quill官方中文文档…多种富文本
一、Tinymce富文本
踩坑disabled和readonly效果问题
解决方法
【Tinymce富文本】如何实现disabled和readonly效果_tinymce disabled-CSDN博客
二、vue-quill-editor富文本
官方链接 vue-quill-editor · Quill官方中文文档 · 看云
vue-quill-editor 使用教程 最全 - 灰信网软件开发博客聚合
踩坑
上传图片时图片路径会被默认转为base64编码格式上传多个图片时路径过长数据库存储不下导致报错用富文本(vue-quill-editor)上传图片时图片路径会被默认转为base64编码格式上传多个图片时路径过长数据库存储不下导致报错 - 简书
解决方法
插入图片不采用base64而是从服务器传图片再显示返回url。
搜索关键词
vue使用vue-quill实现富文本编辑器上传图片
使用步骤
1.安装依赖
2.全局注册
3.封装组件注意这是antd工程
CourseRichText.vue
templatedivquill-editorclasseditorrefmyTextEditorv-modeleditorContent:optionseditorOptionchangeonEditorChange($event)readyready($event):disableddisabled/quill-editora-uploadclassant-my-uploaderstyledisplay:none:action${publicName}/proxy/v1/health/commFile/upload:before-uploadbeforeUploadchangehandleChangea-button a-icon typeupload /Upload /a-button/a-upload!-- div剩余可输入span :style{ color: 1000 - innerText.length 0 ? red : black }{{1000 - innerText.length}}/span字/div --/div
/templatescript
import { quillEditor } from vue-quill-editor;
import /styles/vue-quill-editor/quill.core.css
import /styles/vue-quill-editor/quill.snow.css
import /styles/vue-quill-editor/quill.bubble.css
import Quill from quill;
// 图片大小调整
// import ImageResize from quill-image-resize-module;
// Quill.register(modules/imageResize, ImageResize);
// 自定义文字大小
let fontSizeStyle Quill.import(attributors/style/size);
fontSizeStyle.whitelist [10px,11px,12px,13px,14px,15px,16px,17px,18px,19px,20px,21px,22px,23px,24px,25px,26px
];
Quill.register(fontSizeStyle, true);
// import { lineHeightStyle } from /utils/lineheight;
//工具菜单栏配置
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] }], // 字体大小[{ size: fontSizeStyle.whitelist }], // 字体大小[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色[{ font: [] }], // 字体种类[{ align: [] }], // 对齐方式// [{ lineheight: [initial, 1, 1.5, 1.75, 2, 3, 4, 5] }], // 行高[clean], // 清除文本格式[link, image] // 链接、图片// [video] // 视频
];
export default {components: {quillEditor},props: {content: String,disabled: Boolean},name:courseRichText,data() {return {publicName: window.location.pathname,innerText: ,editorContent: null,editorOption: {placeholder: 请在这里输入, //提示readyOnly: false, //是否只读theme: snow, //主题 snow/bubblesyntax: true, //语法检测modules: {// imageResize: {// //添加// displayStyles: {// //添加// backgroundColor: black,// border: none,// color: white// },// modules: [Resize, DisplaySize, Toolbar] //添加// },toolbar: {container: toolbarOptions,handlers: {image: function(value) {if (value) {console.log(value);// 触发input框选择图片文件document.querySelector(.ant-my-uploader input).click();} else {this.quill.format(image, false);}},lineheight: function(value) {if (value) {this.quill.format(lineHeight, value);} else {console.log(value);}}}}}},loading: false};},computed: {editor() {return this.$refs.myTextEditor.quillEditor;}},mounted() {// Quill.register({ formats/line-height: LineHeight }, true);},watch: {content: {handler: function(val) {this.editorContent val;// 更新字数},immediate: true}},methods: {ready() {// Quill.register({ formats/lineHeight: lineHeightStyle }, true);},onEditorChange(editor) {this.editorContent editor.html;// 不去除空格// this.innerText editor.text;// 去除空格this.innerText editor.text.replace(/[\r\n]$/g, );this.$emit(onChange, {content: editor.html,textLength: this.innerText.length});},// 上传图片uploadSuccess(val) {let quill this.$refs.myTextEditor.quill;// 获取光标所在位置let length quill.getSelection().index;// 插入图片 res.url为服务器返回的图片地址quill.insertEmbed(length, image, val.absolutePath);// 调整光标到最后quill.setSelection(length 1);},handleChange(info) {switch (info.file.status) {case uploading:this.loading true;break;case done:this.loading false;// eslint-disable-next-line no-case-declarationsconst { response } info.file; // 请求返回的数据if (response.code 200) {this.uploadSuccess(response.data);this.$message.success({content: 上传成功,key: uploadPic,duration: 2});} else {this.$message.error({content: response.message[0] || 上传发生错误 response.code,key: uploadPic,duration: 2});}break;case error:this.loading false;// 错误消息提示this.$message.error({content: 网络错误请稍后再试,key: uploadPic,duration: 2});break;default:break;}},beforeUpload(file) {return new Promise((resolve, reject) {this.$message.success({content: 上传中,key: uploadPic,duration: 2});const isJpgOrPng file.type image/jpeg ||file.type image/png ||file.type image/jpg;if (!isJpgOrPng) {this.$message.error(图片仅支持 jpeg 或 png 或 jpg 格式);return reject(false);}// const isLt300kb file.size / 1024 300;const isLt2M file.size / 1024 / 1024 2;if (!isLt2M) {this.$message.error(图片大于 2M);return reject(false);}return resolve(true);});}}
};
/scriptstyle
.editor {line-height: normal !important;/* height: 400px; */background-color: #ffffff;
}
.ql-snow .ql-tooltip[data-modelink]::before {content: 请输入链接地址:;
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {border-right: 0px;content: 保存;padding-right: 0px;
}
.ql-snow .ql-tooltip a.ql-action::after {content: 编辑;
}
.ql-snow .ql-tooltip a.ql-remove::before {content: 移除;
}
.ql-snow .ql-tooltip[data-modevideo]::before {content: 请输入视频地址:;
}
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {content: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-valuesmall]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-valuesmall]::before {content: 10px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-valuelarge]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-valuelarge]::before {content: 18px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-valuehuge]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-valuehuge]::before {content: 32px;
}.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before {content: 文本;
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value1]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value1]::before {content: 标题1;
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value2]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value2]::before {content: 标题2;
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value3]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value3]::before {content: 标题3;
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value4]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value4]::before {content: 标题4;
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value5]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value5]::before {content: 标题5;
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value6]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value6]::before {content: 标题6;
}
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {content: 标准字体;
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-valueserif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-valueserif]::before {content: 衬线字体;
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-valuemonospace]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-valuemonospace]::before {content: 等宽字体;
}
/* 编辑器内部出现滚动条 */
.ql-container {/* overflow-y: auto; */height: 200px !important;
}
/*滚动条整体样式*/
.ql-container ::-webkit-scrollbar {width: 10px; /*竖向滚动条的宽度*/height: 10px; /*横向滚动条的高度*/
}
.ql-container ::-webkit-scrollbar-thumb {/*滚动条里面的小方块*/background: #666666;border-radius: 5px;
}
.ql-container ::-webkit-scrollbar-track {/*滚动条轨道的样式*/background: #ccc;border-radius: 5px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value10px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value10px]::before {content: 10px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value11px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value11px]::before {content: 11px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value12px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value12px]::before {content: 12px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value13px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value13px]::before {content: 13px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value14px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value14px]::before {content: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value15px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value15px]::before {content: 15px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value16px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value16px]::before {content: 16px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value17px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value17px]::before {content: 17px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value18px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value18px]::before {content: 18px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value19px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value19px]::before {content: 19px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value20px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value20px]::before {content: 20px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value21px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value21px]::before {content: 21px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value22px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value22px]::before {content: 22px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value23px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value23px]::before {content: 23px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value24px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value24px]::before {content: 24px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value25px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value25px]::before {content: 25px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value26px]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value26px]::before {content: 26px;
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-label::before{content: 行高;
}
.ql-snow.ql-picker.ql-lineheight.ql-picker-item[data-valueinitial]::before {content: 默认;
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value1]::before {content: 1;
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value1.5]::before {content: 1.5;
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value1.75]::before {content: 1.75;
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value2]::before {content: 2;
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value3]::before {content: 3;
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value4]::before {content: 4;
}
.ql-snow .ql-picker.ql-lineheight .ql-picker-item[data-value5]::before {content: 5;
}
.ql-snow .ql-picker.ql-lineheight {width: 70px;
}
/style
4.页面使用组件
course-rich-text :disabled dialogType!add dialogType!edit onChangerichTextChange :contentmessageData.msgDetail /
三、Monaco Editor
封装组件MonacoEditor.vue
templatedivdiv idcode-editor stylewidth:100%;height:100%;min-height:200px;border:1px solid #DCDFE6/div/div
/templatescript
// import * as monaco from monaco-editor
import * as monaco from monaco-editor/esm/vs/editor/editor.api
import { checkJsonCode, getJsonPath } from /utils/monaco-editor.jsexport default {name: monacoEditor,model: {prop: content,event: change},props: {content: null,language: {default: javascript},readOnly: {default: false}},data: function () {return {editor: null,jsonPath: null}},watch: {content: function (newValue) {let value_ newValueif (this.editor) {if (newValue ! this.editor.getValue()) {if (this.language json) {value_ checkJsonCode(value_)}monaco.editor.setModelLanguage(this.editor.getModel(), this.language);this.editor.trigger(this.editor.getValue(), editor.action.formatDocument)this.editor.setValue(value_);}}}},mounted: function () {const copyToClipboard this.copyToClipboardlet value_ this.contentif (this.language json) {value_ checkJsonCode(this.content)}this.editor monaco.editor.create(this.$el.querySelector(#code-editor),{value: value_,language: this.language,theme: vs,readOnly: this.readOnly,automaticLayout: true});this.editor.addAction({id: json-path,label: Copy JsonPath,keybindings: [monaco.KeyMod.chord(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_J)],precondition: editorLangId json,keybindingContext: editorLangId json,contextMenuGroupId: 9_cutcopypaste,contextMenuOrder: 2,run: copyToClipboard});this.editor.onDidChangeModelContent(event {const value this.editor.getValue()if (this.value ! value) {this.$emit(change, value, event)}})this.editor.onDidChangeCursorPosition(event {const value this.editor.getValue()const offSet this.editor.getModel().getOffsetAt(event.position)const language this.language;if (this.value ! value language json) {this.$emit(on-cursor-change, { offSet: offSet })}if (language json offSet ! 0) {this.jsonPath getJsonPath(value, offSet)this.$emit(on-jsonpath-change, { jsonPath: this.jsonPath })}})},methods: {copyToClipboard() {const notification this.$Noticeif (this.jsonPath) {navigator.clipboard.writeText(this.jsonPath).then(function () { }, function () {notification.error({title: jsonpath copy failed.});});} else {notification.warning({title: There is no jsonpath that can be copied.});}}}
};
/scriptmonaco-editor.js
const colType { Object, Array }export function getJsonPath(text, offSet) {let pos 0let stack []let isInKey falsewhile (pos offSet) {const startPos posswitch (text[pos]) {case :const { text: s, pos: newPos } readString(text, pos)if (stack.length) {const frame stack[stack.length - 1]if (frame.colType colType.Object isInKey) {frame.key sisInKey false}}pos newPosbreakcase {:stack.push({ colType: colType.Object })isInKey truebreakcase [:stack.push({ colType: colType.Array, index: 0 })breakcase }:case ]:stack.pop()breakcase ,:if (stack.length) {const frame stack[stack.length - 1]if (frame.colType colType.Object) {isInKey true} else {frame.index}}break}if (pos startPos) {pos}}return pathToString(stack);
}function pathToString(path) {let s $try {for (const frame of path) {if (frame.colType colType.Object) {if (!frame.key.match(/^[a-zA-Z$_][a-zA-Z\d$_]*$/)) {const key frame.key.replace(, \\)s [${frame.key}]} else {if (s.length) {s .}s frame.key}} else {s [${frame.index}]}}return s;} catch (ex) {return ;}
}function isEven(n) {return n % 2 0;
}function readString(text, pos) {let i pos 1i findEndQuote(text, i)var textpos {text: text.substring(pos 1, i),pos: i 1}return textpos
}// Find the next end quote
function findEndQuote(text, i) {while (i text.length) {// console.log(findEndQuote: i : text[i])if (text[i] ) {var bt i// Handle backtracking to find if this quote is escaped (or, if the escape is escaping a slash)while (0 bt text[bt] \\) {bt--}if (isEven(i - bt)) {break;}}i}return i
}export function checkJsonCode(strJsonCode) {let res ;try {for (let i 0, j 0, k 0, ii, ele; i strJsonCode.length; i) {ele strJsonCode.charAt(i);if (j % 2 0 ele }) {// eslint-disable-next-line no-plusplusk--;for (ii 0; ii k; ii) ele ${ele};ele \n${ele};} else if (j % 2 0 ele {) {ele \n;// eslint-disable-next-line no-plusplusk;for (ii 0; ii k; ii) ele ;} else if (j % 2 0 ele ,) {ele \n;for (ii 0; ii k; ii) ele ;// eslint-disable-next-line no-plusplus} else if (ele ) j;res ele;}} catch (error) {res strJsonCode;}return res;}
使用
monacoEditor v-modelform.ruleContent /monacoEditor
四、mavonEditor