目标:
1.vue中支持kindeditor
2.kindeditor上传及文件管理接口要使用.net core api,并需要支持token
一、下载kindeditor
二、将kindeditor整个文件夹复制到vue项目的public/static文件夹下(可以只保留其中的css和img文件),将kindeditor的关联主题样式拷贝到assets下
三、新建vue-kind-editor组件,将kindeditor中的lang、themes、kindeditor-all.js、kindeditor-all.min.js 拷贝到组件文件夹下,新建index.vue
index.vue代码,注意:token(新增)、pluginsPath(需指向static文件夹)、uploadJson、fileManagerJson属性
<template>
<textarea :id="id" name="content" v-model="outContent"></textarea>
</template>
<script>
import "@/assets/kindeditor/themes/default/default.css";
import "./kindeditor-all.min.js";
import "./lang/zh-CN.min.js";
import store from "@/store";
export default {
name: "vue-kind-editor",
data() {
return {
editor: null,
outContent: this.content,
};
},
props: {
header: {
type: Object,
default: () => ({}),
},
token: {
type: String,
default: store.getters.token,
},
content: {
type: String,
default: "",
},
id: {
type: String,
required: true,
default: "vue-kind-editor-" + new Date().valueOf(),
},
width: {
type: String,
default: "100%",
},
height: {
type: String,
default: "500px",
},
minWidth: {
type: Number,
default: 650,
},
minHeight: {
type: Number,
default: 100,
},
items: {
type: Array,
default: function () {
return [
"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",
];
},
},
noDisableItems: {
type: Array,
default: function () {
return ["source", "fullscreen"];
},
},
filterMode: {
type: Boolean,
default: true,
},
htmlTags: {
type: Object,
default: function () {
return {
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": [],
};
},
},
wellFormatMode: {
type: Boolean,
default: true,
},
resizeType: {
type: Number,
default: 2,
},
themeType: {
type: String,
default: "default",
},
langType: {
type: String,
default: "zh-CN",
},
designMode: {
type: Boolean,
default: true,
},
fullscreenMode: {
type: Boolean,
default: false,
},
basePath: {
type: String,
},
themesPath: {
type: String,
},
pluginsPath: {
type: String,
default: "/static/kindeditor/plugins/",
},
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,
},
bodyClass: {
type: String,
default: "ke-content",
},
colorTable: {
type: Array,
},
afterCreate: {
type: Function,
},
afterChange: {
type: Function,
},
afterTab: {
type: Function,
},
afterFocus: {
type: Function,
},
afterBlur: {
type: Function,
},
afterUpload: {
type: Function,
},
uploadJson: {
type: String,
default: "/api/platform/resource/uploaddocument",
},
fileManagerJson: {
type: String,
default: "/api/platform/resource/filemanager",
},
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: true,
},
fontSizeTable: {
type: Array,
default: function () {
return ["9px", "10px", "12px", "14px", "16px", "18px", "24px", "32px"];
},
},
imageTabIndex: {
type: Number,
default: 0,
},
formatUploadUrl: {
type: Boolean,
default: true,
},
fullscreenShortcut: {
type: Boolean,
default: false,
},
extraFileUploadParams: {
type: Object,
default: function () {
//注意:仅支持post
return {
//"key":"value"
};
},
},
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,
},
},
watch: {
content(val) {
this.editor && val !== this.outContent && this.editor.html(val);
},
outContent(val) {
this.$emit("update:content", val);
this.$emit("on-content-change", val);
},
},
computed: {
},
mounted() {
// 初始访问时创建
this.initEditor();
},
/**
* keep-alive 会用到进入时调用activated 离开时调用deactivated
* 初始访问 created、mounted 切换时deactivated 再次进入时 activated
*/
activated() {
// keep-alive 进入时创建
this.initEditor();
},
deactivated() {
// keep-alive 离开时移除
this.removeEditor();
},
methods: {
removeEditor() {
window.KindEditor.remove("#" + this.id);
},
initEditor() {
var _this = this;
_this.removeEditor();
_this.editor = window.KindEditor.create("#" + this.id, {
header: _this.header,
token:_this.token,
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,
afterChange: function () {
_this.outContent = this.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>
四、修改kindeditor.all.js,让其支持.net core的上传文件,修改完后可以进行压缩成kindeditor-all.min.js
1.修改kindeditor.all.js中的_ajax方法,来支持上传文件功能,代码如下
function _ajax(url, fn, method, formData, dataType,token) {
method = method || 'GET';
method = method.toUpperCase();
dataType = dataType || 'json';
var xhr = window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
xhr.open(method, url, true);
//请求头必须在open之后设置
if(token) xhr.setRequestHeader("Authorization","Bearer "+token);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
if (fn) {
var data = _trim(xhr.responseText);
if (dataType == 'json') {
data = _json(data);
}
fn(data);
}
}
};
if (method == 'POST') {
xhr.send(formData);
} else {
xhr.send(null);
}
}
2.修改image、flash、media、file插件,修改dialog组件的yesBtn的click方法,将其中的上传文件代码即uploadbutton.submit();改为调用1中的_ajax方法。注意:每个插件原理相同,代码略有不同。
//提交上传表单
//uploadbutton.submit();
var formData = new FormData();
var file = document.getElementsByClassName("ke-upload-file");
formData.append(filePostName, file[0].files[0]);
for(var k in extraParams){
formData.append(K,extraParams[k]);
}
K.ajax(K.addParam(uploadJson, 'dir=image&' + new Date().getTime()), function (data) {
dialog.hideLoading();
if (data.error === 0) {
var url = data.url;
if (formatUploadUrl) {
url = K.formatUrl(url, 'absolute');
}
if (self.afterUpload) {
self.afterUpload.call(self, url, data, name);
}
if (!fillDescAfterUploadImage) {
clickFn.call(self, url, data.title, data.width, data.height, data.border, data.align);
} else {
K(".ke-dialog-row #remoteUrl", div).val(url);
K(".ke-tabs-li", div)[0].click();
K(".ke-refresh-btn", div).click();
}
} else {
alert(data.message);
}
}, "post", formData,null, self.token);
3.修改filemanager插件,修改reloadPage方法,调用_ajax方法的参数进行调整,代码如下
K.ajax(K.addParam(fileManagerJson, param + '&' + new Date().getTime()), function (data) {
dialog.hideLoading();
func(data);
}, null, null, null, self.token);
五、.net core 接口,接口代码请根据自己系统进行修改,只需要注意返回的json格式要跟kindeditor处理方法对应。
/// <summary>
/// 上传文件(kindeditor)
/// </summary>
/// <param name="dir"></param>
/// <param name="imgFile"></param>
/// <returns></returns>
[HttpPost]
//[AllowAnonymous]
public async Task<IActionResult> UploadDocument([FromQuery(Name = "dir")] string dir, [FromForm] IFormFile imgFile)
{
var req = Request;
if (String.IsNullOrEmpty(dir))
{
return new JsonResult(new { error = 1, message = "目录名不正确。" });
}
var _config = _uploadConfig[dir];
if (_config == null)
{
return new JsonResult(new { error = 1, message = "目录名不正确。" });
}
var date = DateTime.Today.ToString("yyyyMMdd");
if (imgFile == null || imgFile.Length < 1)
{
return new JsonResult(new { error = 1, message = "请上传文件!" });
}
//格式限制
//var extentions = ((string)extTable[dir]).Split(',').ToList();
//String fileExt = Path.GetExtension(imgFile.FileName).ToLower();
//if (fileExt.Length > 0)
// fileExt = fileExt.Substring(fileExt.IndexOf('.') + 1);
if (!_config.ContentType.Contains(imgFile.ContentType))
{
return new JsonResult(new { error = 1, message = "文件格式错误!" });
}
//大小限制
if (!(imgFile.Length <= _config.MaxSize))
{
return new JsonResult(new { error = 1, message = "文件过大!" });
}
var fileInfo = new ZzwIot.Core.Common.Files.FileInfo(imgFile.FileName, imgFile.Length)
{
UploadPath = _config.UploadPath + "/",
RequestPath = _config.RequestPath + "/"
};
var dateTimeFormat = _config.DateTimeFormat.NotNull() ? DateTime.Now.ToString(_config.DateTimeFormat) : "";
var format = _config.Format.NotNull() ? StringHelper.Format(_config.Format, new { Id = date }) : "";
fileInfo.RelativePath = Path.Combine(dateTimeFormat, format).ToPath();
if (!Directory.Exists(fileInfo.FileDirectory))
{
Directory.CreateDirectory(fileInfo.FileDirectory);
}
fileInfo.SaveName = $"{IdWorkerHelper.GenId64()}.{fileInfo.Extension}";
await _uploadHelper.SaveAsync(imgFile, fileInfo.FilePath);
Hashtable hash = new Hashtable();
hash["error"] = 0;
hash["url"] = fileInfo.RequestPath + fileInfo.FileRelativePath;
return new JsonResult(hash);
}
/// <summary>
/// 文件管理(kindeditor)
/// </summary>
/// <returns></returns>
[HttpGet]
//[AllowAnonymous]
public IActionResult FileManager([FromQuery(Name = "dir")] string dir,
[FromQuery(Name = "path")] string path,
[FromQuery(Name = "order")] string order
)
{
var config = _uploadConfig[dir];
if (config == null)
{
return Content("目录错误");
}
String rootUrl = config.RequestPath + "/";
//图片扩展名
//String photoTypes = "gif,jpg,jpeg,png,bmp";
String currentPath = "";
String currentUrl = "";
String currentDirPath = "";
String moveupDirPath = "";
String dirPath = config.UploadPath + "/";
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
//根据path参数,设置各路径和URL
path = String.IsNullOrEmpty(path) ? "" : path;
if (path == "")
{
currentPath = dirPath;
currentUrl = rootUrl;
currentDirPath = "";
moveupDirPath = "";
}
else
{
currentPath = dirPath + path;
currentUrl = rootUrl + path;
currentDirPath = path;
moveupDirPath = Regex.Replace(currentDirPath, @"(.*?)[^\/]+\/$", "$1");
}
//排序形式,name or size or type
order = String.IsNullOrEmpty(order) ? "" : order.ToLower();
//不允许使用..移动到上一级目录
if (Regex.IsMatch(path, @"\.\."))
{
return Content("拒绝访问。");
}
//最后一个字符不是/
if (path != "" && !path.EndsWith("/"))
{
return Content("Path无效。");
}
//目录不存在或不是目录
if (!Directory.Exists(currentPath))
{
return Content("目录不存在。");
}
//遍历目录取得文件信息
string[] dirList = Directory.GetDirectories(currentPath);
string[] fileList = Directory.GetFiles(currentPath);
switch (order)
{
case "size":
Array.Sort(dirList, new NameSorter());
Array.Sort(fileList, new SizeSorter());
break;
case "type":
Array.Sort(dirList, new NameSorter());
Array.Sort(fileList, new TypeSorter());
break;
case "name":
default:
Array.Sort(dirList, new NameSorter());
Array.Sort(fileList, new NameSorter());
break;
}
Hashtable result = new Hashtable();
result["moveup_dir_path"] = moveupDirPath;
result["current_dir_path"] = currentDirPath;
result["current_url"] = currentUrl;
result["total_count"] = dirList.Length + fileList.Length;
List<Hashtable> dirFileList = new List<Hashtable>();
result["file_list"] = dirFileList;
for (int i = 0; i < dirList.Length; i++)
{
DirectoryInfo directory = new DirectoryInfo(dirList[i]);
Hashtable hash = new Hashtable();
hash["is_dir"] = true;
hash["has_file"] = (directory.GetFileSystemInfos().Length > 0);
hash["filesize"] = 0;
hash["is_photo"] = false;
hash["filetype"] = "";
hash["filename"] = directory.Name;
hash["datetime"] = directory.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss");
dirFileList.Add(hash);
}
var photoTypes = "png,gif,jpg,jpeg,bmp";
for (int i = 0; i < fileList.Length; i++)
{
FileInfo file = new FileInfo(fileList[i]);
Hashtable hash = new Hashtable();
hash["is_dir"] = false;
hash["has_file"] = false;
hash["filesize"] = file.Length;
hash["is_photo"] = (Array.IndexOf(photoTypes.Split(','), file.Extension.Substring(1).ToLower()) >= 0);
hash["filetype"] = file.Extension.Substring(1);
hash["filename"] = file.Name;
hash["datetime"] = file.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss");
dirFileList.Add(hash);
}
return new JsonResult(result);
}
六、vue-kind-editor调用
import KindEditor from "@/components/vue-kind-editor";
<kind-editor :content.sync="addForm.content" id="kind-editor" />