項(xiàng)目需求:富文本編輯器可以一次性上傳多張圖片
1:npm 安裝kindeditor依賴
2:創(chuàng)建kindeditor組件 src/components/Kindeditor
kindeditor.png
kindeditor.vue
<template>
<div class="margin-top-20">
<textarea :id="id" name="content" v-model="outContent"></textarea>
<input
@change="selectedFile"
style="visibility: hidden;height:0;"
type="file"
name
id="inputFile"
/>
</div>
</template>
<script>
import "kindeditor/themes/default/default.css";
import "kindeditor/kindeditor-all-min.js";
import "kindeditor/lang/zh-CN.js";
// 以下四個(gè)配置文件見下文
import items from "./config/items";
import htmlTags from "./config/htmlTags";
import fontSizeTable from "./config/fontSizeTable";
import otherConfig from "./config/otherConfig";
export default {
name: "kindeditor-component",
props: {
// 編輯器內(nèi)容 url
html: {
type: String,
default: ""
},
// 編輯器內(nèi)容
content: {
type: String,
default: ""
},
// 編輯器id
id: {
type: String,
// required: true,
default: "kindeditor-id"
},
// 寬
width: {
type: String,
default: `100%`
},
// 高
height: {
type: String,
default: "400"
},
// 最小寬
minWidth: {
type: Number,
default: 650
},
// 最小高
minHeight: {
type: Number,
default: 400
},
// toolbar 工具欄配置
items: {
type: Array,
default: function() {
return [...items];
}
},
// 標(biāo)簽配置
htmlTags: {
type: Object,
default: function() {
return { ...htmlTags };
}
},
//字號配置
fontSizeTable: {
type: Array,
default: function() {
return [...fontSizeTable];
}
},
// 語言配置
langType: {
type: String,
default: "zh-CN"
},
// 主題配置
themeType: {
type: String,
default: "default"
},
// body 的樣式
bodyClass: {
type: String,
default: "ke-content"
},
// 其他配置項(xiàng)
...otherConfig
},
data() {
return {
editor: null,
outContent: this.content
};
},
watch: {
content(val) {
this.editor && val !== this.outContent && this.editor.html(val);
},
// 分發(fā)編輯器內(nèi)容改變事件
outContent(val) {
this.$emit("update:content", val);
this.$emit("on-content-change", val);
this.$emit("input", val);
},
// 初始化編輯器內(nèi)容
html(val) {
if (
this.html &&
(this.html.startsWith("https://") || this.html.startsWith("http://"))
) {
this.loadUrl(val);
} else {
this.outContent = "";
this.outContent ? this.editor.appendHtml(this.outContent) : "";
}
}
},
created() {
if (
this.html &&
(this.html.startsWith("https://") || this.html.startsWith("http://"))
) {
this.loadUrl(this.html);
} else {
this.outContent = "";
setTimeout(() => {
this.outContent ? this.editor.appendHtml(this.outContent) : "";
}, 1000);
}
},
mounted() {
// 初始訪問時(shí)創(chuàng)建
this.initEditor();
// 添加焦點(diǎn)
// this.editor.focus();
// 添加點(diǎn)擊圖片回調(diào)函數(shù)
this.editor.clickToolbar("image", () => {
// 禁用自帶的圖片彈窗
this.editor.hideDialog();
// 打開文件
this.handleOpenFile();
});
},
activated() {
// keep-alive 進(jìn)入時(shí)創(chuàng)建
this.initEditor();
},
deactivated() {
// keep-alive 離開時(shí)移除
this.removeEditor();
},
beforeDestroy() {
// 實(shí)例銷毀之前移除
this.removeEditor();
},
methods: {
// 打開文件
handleOpenFile() {
let input = document.getElementById("inputFile");
// 解決同一個(gè)文件不能監(jiān)聽的問題
input.addEventListener(
"click",
function() {
this.value = "";
},
false
);
// 點(diǎn)擊input
input.click();
},
// 不上傳的情況下預(yù)覽圖片
getObjectURL (file) {
var url = null
// 下面函數(shù)執(zhí)行的效果是一樣的,只是需要針對不同的瀏覽器執(zhí)行不同的 js 函數(shù)而已
if (window.createObjectURL !== undefined) { // basic
url = window.createObjectURL(file)
} else if (window.URL !== undefined) { // mozilla(firefox)
url = window.URL.createObjectURL(file)
} else if (window.webkitURL !== undefined) { // webkit or chrome
url = window.webkitURL.createObjectURL(file)
}
return url
},
// 選擇好文件
async selectedFile($event) {
const file = $event.target.files[0];
// 把圖片上傳到后端服務(wù)器 拿到url uploadImage 是自己后端上傳圖片的接口
// 調(diào)用appendHtml方法把圖片追加到富文本
// const url= await uploadImage (file)
// this.editor.appendHtml(
// `<img style="max-width:100%;" src="https://${放你的圖片路徑}">`
// );
},
// 編輯器內(nèi)容上傳到cos放钦,調(diào)用返回url
async content2Url() {
// 把html片段上傳到后端服務(wù)器 拿到url uploadHtml 是自己后端上傳的接口
// try {
// const res = await uploadHtml(this.outContent)
// return res
// } catch (error) {
// this.$message({
// message: error.data.message,
// type: 'error'
// })
// }
}
// 加載html填充編輯器內(nèi)容
loadUrl(url) {
if (url && url.length > 0) {
axios.get(url)
.then(response => {
// 處理HTML顯示
this.outContent = response.data;
this.editor.appendHtml(this.outContent);
this.$emit("subLoadUrlToHtml", response.data);
})
.catch(() => {
this.outContent = "服務(wù)器數(shù)據(jù)加載失敗色徘,請重試!";
this.editor.appendHtml(this.outContent);
});
}
},
// 移除編輯器實(shí)例
removeEditor() {
window.KindEditor.remove(`#${this.id}`);
},
// 初始化編輯器
initEditor() {
this.removeEditor();
this.editor = window.KindEditor.create("#" + this.id, {
width: this.width,
height: this.height,
minWidth: this.minWidth,
minHeight: this.minHeight,
items: this.items,
noDisableItems: this.noDisableItems,
filterMode: this.filterMode,
htmlTags: this.htmlTags,
wellFormatMode: this.wellFormatMode,
resizeType: this.resizeType,
themeType: this.themeType,
langType: this.langType,
designMode: this.designMode,
fullscreenMode: this.fullscreenMode,
basePath: this.basePath,
themesPath: this.themesPath,
pluginsPath: this.pluginsPath,
langPath: this.langPath,
minChangeSize: this.minChangeSize,
loadStyleMode: this.loadStyleMode,
urlType: this.urlType,
newlineTag: this.newlineTag,
pasteType: this.pasteType,
dialogAlignType: this.dialogAlignType,
shadowMode: this.shadowMode,
zIndex: this.zIndex,
useContextmenu: this.useContextmenu,
syncType: this.syncType,
indentChar: this.indentChar,
cssPath: this.cssPath,
cssData: this.cssData,
bodyClass: this.bodyClass,
colorTable: this.colorTable,
afterCreate: this.afterCreate,
// 編輯器內(nèi)容改變回調(diào)
afterChange: () => {
this.editor ? (this.outContent = this.editor.html()) : "";
},
afterTab: this.afterTab,
afterFocus: this.afterFocus,
afterBlur: this.afterBlur,
afterUpload: this.afterUpload,
uploadJson: this.uploadJson,
fileManagerJson: this.fileManagerJson,
allowPreviewEmoticons: this.allowPreviewEmoticons,
allowImageUpload: this.allowImageUpload,
allowFlashUpload: this.allowFlashUpload,
allowMediaUpload: this.allowMediaUpload,
allowFileUpload: this.allowFileUpload,
allowFileManager: this.allowFileManager,
fontSizeTable: this.fontSizeTable,
imageTabIndex: this.imageTabIndex,
formatUploadUrl: this.formatUploadUrl,
fullscreenShortcut: this.fullscreenShortcut,
extraFileUploadParams: this.extraFileUploadParams,
filePostName: this.filePostName,
fillDescAfterUploadImage: this.fillDescAfterUploadImage,
afterSelectFile: this.afterSelectFile,
pagebreakHtml: this.pagebreakHtml,
allowImageRemote: this.allowImageRemote,
autoHeightMode: this.autoHeightMode,
fixToolBar: this.fixToolBar,
tabIndex: this.tabIndex
});
}
}
};
</script>
<style>
</style>
src/components/Kindeditor/config/items.js 配置富文本編輯器需要的插件
// toolbar配置
const items = [
// "source",
// "|",
"undo",
"redo",
"|",
"preview",
// "print",
// "template",
// "code",
"cut",
"copy",
"paste",
"plainpaste",
"wordpaste",
"|",
"justifyleft",
"justifycenter",
"justifyright",
"justifyfull",
"insertorderedlist",
"insertunorderedlist",
"indent",
"outdent",
"subscript",
"superscript",
"clearhtml",
"quickformat",
// "selectall",
"|",
"fullscreen",
"/",
"formatblock",
// "fontname",
"fontsize",
"|",
"forecolor",
"hilitecolor",
"bold",
"italic",
"underline",
"strikethrough",
"lineheight",
"removeformat",
"|",
"image",
// "multiimage",
// "flash",
// "media",
// "insertfile",
// "table",
"hr",
// "emoticons",
// "baidumap",
"pagebreak",
"anchor",
"link",
"unlink",
"|",
"about"
];
export default items;
src/components/Kindeditor/config/htmlTags.js
const htmlTags = {
font: ["color", "size", "face", ".background-color"],
span: ["style"],
div: ["class", "align", "style"],
table: [
"class",
"border",
"cellspacing",
"cellpadding",
"width",
"height",
"align",
"style"
],
"td,th": [
"class",
"align",
"valign",
"width",
"height",
"colspan",
"rowspan",
"bgcolor",
"style"
],
a: ["class", "href", "target", "name", "style"],
embed: [
"src",
"width",
"height",
"type",
"loop",
"autostart",
"quality",
"style",
"align",
"allowscriptaccess",
"/"
],
img: [
"src",
"width",
"height",
"border",
"alt",
"title",
"align",
"style",
"/"
],
hr: ["class", "/"],
br: ["/"],
"p,ol,ul,li,blockquote,h1,h2,h3,h4,h5,h6": ["align", "style"],
"tbody,tr,strong,b,sub,sup,em,i,u,strike": []
};
export default htmlTags;
src/components/Kindeditor/config/fontSizeTable.js // 字體配置
const fontSizeTable = [
"9px",
"10px",
"12px",
"14px",
"16px",
"18px",
"24px",
"32px"
];
export default fontSizeTable;
src/components/Kindeditor/config/otherConfig.js
// 其他非主要配置項(xiàng)
const otherConfig = {
noDisableItems: {
type: Array,
default: function() {
return ["source", "fullscreen"];
}
},
filterMode: {
type: Boolean,
default: true
},
wellFormatMode: {
type: Boolean,
default: true
},
resizeType: {
type: Number,
default: 2
},
designMode: {
type: Boolean,
default: true
},
fullscreenMode: {
type: Boolean,
default: false
},
basePath: {
type: String
},
themesPath: {
type: String
},
pluginsPath: {
type: String,
default: ""
},
langPath: {
type: String
},
minChangeSize: {
type: Number,
default: 5
},
loadStyleMode: {
type: Boolean,
default: true
},
urlType: {
type: String,
default: ""
},
newlineTag: {
type: String,
default: "p"
},
pasteType: {
type: Number,
default: 2
},
dialogAlignType: {
type: String,
default: "page"
},
shadowMode: {
type: Boolean,
default: true
},
zIndex: {
type: Number,
default: 811213
},
useContextmenu: {
type: Boolean,
default: true
},
syncType: {
type: String,
default: "form"
},
indentChar: {
type: String,
default: "\t"
},
cssPath: {
type: [String, Array]
},
cssData: {
type: String
},
colorTable: {
type: Array
},
afterCreate: {
type: Function
},
afterTab: {
type: Function
},
afterFocus: {
type: Function
},
afterBlur: {
type: Function
},
afterUpload: {
type: Function
},
uploadJson: {
type: String
},
fileManagerJson: {
type: String
},
allowPreviewEmoticons: {
type: Boolean,
default: true
},
allowImageUpload: {
type: Boolean,
default: true
},
allowFlashUpload: {
type: Boolean,
default: true
},
allowMediaUpload: {
type: Boolean,
default: true
},
allowFileUpload: {
type: Boolean,
default: true
},
allowFileManager: {
type: Boolean,
default: false
},
imageTabIndex: {
type: Number,
default: 0
},
formatUploadUrl: {
type: Boolean,
default: true
},
fullscreenShortcut: {
type: Boolean,
default: false
},
extraFileUploadParams: {
type: Object,
default: function() {
return {};
}
},
filePostName: {
type: String,
default: "imgFile"
},
fillDescAfterUploadImage: {
type: Boolean,
default: false
},
afterSelectFile: {
type: Function
},
pagebreakHtml: {
type: String,
default: '<hr style="page-break-after: always;" class="ke-pagebreak" />'
},
allowImageRemote: {
type: Boolean,
default: true
},
autoHeightMode: {
type: Boolean,
default: false
},
fixToolBar: {
type: Boolean,
default: false
},
tabIndex: {
type: Number
}
};
export default otherConfig;
使用
<template>
<div>
<Kind-editor ref="kindeditor" :html="html" @input="getContent"></Kind-editor>
</div>
</template>
<script>
import KindEditor from "@/components/Kindeditor";
export default {
name: 'GoodsForm',
components: {
KindEditor
},
data() {
return {
html: 'https://ebusiness-1255313385.cosbj.myqcloud.com/image/20190823/center2019082304054532.html',
content:''
}
},
methods: {
// 獲取編輯器內(nèi)容
getContent(content) {
this.content = content
},
// 編輯器內(nèi)容轉(zhuǎn)換成在線url
async getcontent2Url() {
try {
const htmlUrl = await this.$refs.kindeditor.content2Url()
return htmlUrl
} catch (error) {
console.log(error)
}
}
}
}
</script>
效果圖
效果圖
這是參考https://blog.csdn.net/qq_39953537/article/details/100043354
單次僅能上傳單張圖片]
多圖上傳功能(圖片上傳更改為多圖上傳)
kindeditor.vue
<template>
<div class="margin-top-20">
<textarea :id="id" name="content" v-model="outContent"></textarea>
<!-- <input
@change="selectedFile"
style="visibility: hidden;height:0;"
type="file"
name
id="inputFile"
/> -->
<input
style="visibility: hidden;height:0;"
ref="fileElem"
accept="image/*"
class="img-input"
type="file"
multiple="multiple"
@change="onchange"
/>
</div>
</template>
<script>
import '../../../node_modules/kindeditor/kindeditor-all.js'
import '../../../node_modules/kindeditor/lang/zh-CN.js'
import '../../../node_modules/kindeditor/themes/default/default.css'
import items from "./config/items"
import htmlTags from "./config/htmlTags"
import fontSizeTable from "./config/fontSizeTable";
import otherConfig from "./config/otherConfig";
export default {
name: 'kindeditor-component',
data () {
return {
editor: null,
outContent: this.content,
// 多圖
dialogVisible: false,
loading: false,
imgSrcList: []
}
},
props: {
// 最大上傳文件的大小
maxFileSize: {
type: Number,
default: 5
},
// 編輯器內(nèi)容 url
html: {
type: String,
default: ""
},
// 編輯器內(nèi)容
content: {
type: String,
default: ""
},
// 編輯器id
id: {
type: String,
// required: true,
default: "kindeditor-id"
},
// 寬
width: {
type: String,
default: `100%`
},
// 高
height: {
type: String,
default: "400"
},
// 最小寬
minWidth: {
type: Number,
default: 650
},
// 最小高
minHeight: {
type: Number,
default: 400
},
// toolbar 工具欄配置
items: {
type: Array,
default: function() {
return [...items];
}
},
// 標(biāo)簽配置
htmlTags: {
type: Object,
default: function() {
return { ...htmlTags };
}
},
//字號配置
fontSizeTable: {
type: Array,
default: function() {
return [...fontSizeTable];
}
},
// 語言配置
langType: {
type: String,
default: "zh-CN"
},
// 主題配置
themeType: {
type: String,
default: "default"
},
// body 的樣式
bodyClass: {
type: String,
default: "ke-content"
},
// 其他配置項(xiàng)
...otherConfig
},
watch: {
content (val) {
this.editor && val !== this.outContent && this.editor.html(val)
},
// 分發(fā)編輯器內(nèi)容改變事件
outContent(val) {
this.$emit("update:content", val);
this.$emit("on-content-change", val);
this.$emit("input", val);
},
// 初始化編輯器內(nèi)容
html(val) {
if (
this.html &&
(this.html.startsWith("https://") || this.html.startsWith("http://"))
) {
this.loadUrl(val);
} else {
this.outContent = "";
this.outContent ? this.editor.appendHtml(this.outContent) : "";
}
}
},
created(){
if (
this.html &&
(this.html.startsWith("https://") || this.html.startsWith("http://"))
) {
this.loadUrl(this.html);
} else {
this.outContent = "";
setTimeout(() => {
this.outContent ? this.editor.appendHtml(this.outContent) : "";
}, 1000);
}
},
mounted () {
// 初始訪問時(shí)創(chuàng)建
this.initEditor();
// 添加焦點(diǎn)
// this.editor.focus();
},
activated() {
// keep-alive 進(jìn)入時(shí)創(chuàng)建
this.initEditor();
},
deactivated() {
// keep-alive 離開時(shí)移除
this.removeEditor();
},
beforeDestroy() {
// 實(shí)例銷毀之前移除
this.removeEditor();
},
methods: {
// 打開文件
handleOpenFile() {
console.log('打開上傳圖片的文件 ');
let input =this.$refs.fileElem
console.log(input);
// 解決同一個(gè)文件不能監(jiān)聽的問題
input.addEventListener(
"click",
function() {
this.value = "";
},
false
);
// 點(diǎn)擊input
input.click();
},
// 圖片上傳
async onchange() {
try {
// 文件列表
let files = this.$refs.fileElem.files
console.log(files);
// 文件所有尺寸
const sizes = []
// 所有文件的base64位地址
const allReadFile = []
for (let index = 0; index < files.length; index++) {
const item = files[index]
sizes.push(item.size)
// allReadFile.push(readFile(item))
}
// 獲取最大尺寸檢驗(yàn)
const maxSize = Math.max.apply(null, sizes)
if (maxSize > 1024 * 1024 * this.maxFileSize) {
this.$message({
message: `圖片不得大于${this.maxFileSize}M`,
type: 'warning',
duration: 2000
})
return
}
// 讀取所有文件為base64數(shù)據(jù)
// const base64List = await Promise.all(allReadFile)
// this.imgSrcList = [...this.imgSrcList, ...base64List]
} catch (error) {
console.log(error)
}
// this.handleSubmit() 確定上傳
},
// 確定上傳
async handleSubmit() {
if (!this.imgSrcList.length) {
this.$message({
message: '請上傳圖片!',
type: 'error'
})
return
}
try {
// 所有blob文件
const blobFiles = []
// 所有上傳圖片請求
const allRequest = []
// Base64 數(shù)據(jù)轉(zhuǎn)成blob數(shù)據(jù)
this.imgSrcList.forEach(item => {
const blobFile = Base64ToBlob(item)
blobFiles.push(blobFile)
})
// 添加請求
blobFiles.forEach(item => {
allRequest.push(uploadImage(item))
})
// 執(zhí)行請求拿到結(jié)果
const urlList = await Promise.all(allRequest)
// 分發(fā)事件
this.$emit('success', urlList)
} catch (error) {
console.log(error, error)
}
},
// 刪除圖片
deleteImage(index) {
this.imgSrcList.splice(index, 1)
},
// 編輯器內(nèi)容上傳到cos最筒,調(diào)用返回url
async content2Url() {
// 把html片段上傳到后端服務(wù)器 拿到url uploadHtml 是自己后端上傳的接口
// try {
// const res = await uploadHtml(this.outContent)
// return res
// } catch (error) {
// this.$message({
// message: error.data.message,
// type: 'error'
// })
// }
},
// 加載html填充編輯器內(nèi)容
loadUrl(url) {
if (url && url.length > 0) {
this.$http.get(url)
.then(response => {
// 處理HTML顯示
this.outContent = response.data;
this.editor.appendHtml(this.outContent);
this.$emit("subLoadUrlToHtml", response.data);
})
.catch(() => {
this.outContent = "服務(wù)器數(shù)據(jù)加載失敗贺氓,請重試!";
this.editor.appendHtml(this.outContent);
});
}
},
// 移除編輯器實(shí)例
removeEditor() {
window.KindEditor.remove(`#${this.id}`);
},
// 初始化編輯器
initEditor() {
this.removeEditor();
this.editor = window.KindEditor.create("#" + this.id, {
width: this.width,
height: this.height,
minWidth: this.minWidth,
minHeight: this.minHeight,
items: this.items,
noDisableItems: this.noDisableItems,
filterMode: this.filterMode,
htmlTags: this.htmlTags,
wellFormatMode: this.wellFormatMode,
resizeType: this.resizeType,
themeType: this.themeType,
langType: this.langType,
designMode: this.designMode,
fullscreenMode: this.fullscreenMode,
basePath: this.basePath,
themesPath: this.themesPath,
pluginsPath: this.pluginsPath,
langPath: this.langPath,
minChangeSize: this.minChangeSize,
loadStyleMode: this.loadStyleMode,
urlType: this.urlType,
newlineTag: this.newlineTag,
pasteType: this.pasteType,
dialogAlignType: this.dialogAlignType,
shadowMode: this.shadowMode,
zIndex: this.zIndex,
useContextmenu: this.useContextmenu,
syncType: this.syncType,
indentChar: this.indentChar,
cssPath: this.cssPath,
cssData: this.cssData,
bodyClass: this.bodyClass,
colorTable: this.colorTable,
afterCreate: this.afterCreate,
// 編輯器內(nèi)容改變回調(diào)
afterChange: () => {
this.editor ? (this.outContent = this.editor.html()) : "";
},
afterTab: this.afterTab,
afterFocus: this.afterFocus,
afterBlur: this.afterBlur,
afterUpload: this.afterUpload,
uploadJson: this.uploadJson,
fileManagerJson: this.fileManagerJson,
allowPreviewEmoticons: this.allowPreviewEmoticons,
allowImageUpload: this.allowImageUpload,
allowFlashUpload: this.allowFlashUpload,
allowMediaUpload: this.allowMediaUpload,
allowFileUpload: this.allowFileUpload,
allowFileManager: this.allowFileManager,
fontSizeTable: this.fontSizeTable,
imageTabIndex: this.imageTabIndex,
formatUploadUrl: this.formatUploadUrl,
fullscreenShortcut: this.fullscreenShortcut,
extraFileUploadParams: this.extraFileUploadParams,
filePostName: this.filePostName,
fillDescAfterUploadImage: this.fillDescAfterUploadImage,
afterSelectFile: this.afterSelectFile,
pagebreakHtml: this.pagebreakHtml,
allowImageRemote: this.allowImageRemote,
autoHeightMode: this.autoHeightMode,
fixToolBar: this.fixToolBar,
tabIndex: this.tabIndex
});
// 添加點(diǎn)擊圖片回調(diào)函數(shù)
this.editor.clickToolbar("image", () => {
console.log('shangchuantupian');
// 禁用自帶的圖片彈窗
this.editor.hideDialog();
// 打開文件
this.handleOpenFile();
});
}
}
}
</script>
<style>
.margin-top-20{
margin-bottom:-50px;
}
</style>