Tinymce 编辑器添加自定义图片管理插件
在使用Tinymce的过程中需要用到图片上传功能,而提供的上传插件在上传文件后是给了一个连接地址,就想用户需要什么图片,不能用最直观的方式表现出来么!
虽然官网上也有一个文件管理的插件moxiemanager,可奈何他是收费的!https://www.tiny.cloud/docs/plugins/moxiemanager/
然后就打算自己弄一个,其实实现效果起来很简单,就只是做了一个类型相册管理的功能,然后在点击图片的时候,将图片的地址信息插入到编辑器里就行了,由于后台用的是layui
的框架,所以界面也就用了layui来实现,这里我只弄了上传,删除功能,也可自己添加检索等功能,实现效果如下
1 、添加插件
我们需要在tinymce的 Plugins 目录下新建一个filemanager文件夹,并添加一个名为plugin.min.js ,其中editor传参后再图片管理页面通过
var editor = top.tinymce.activeEditor.windowManager.getParams().editor; 获取编辑器对象,进行图片插入操作
tinymce.PluginManager.add("filemanager", function (editor, url) { editor.addButton("filemanager", { title: "图片管理", icon: 'image', onclick: function () { editor.windowManager.open({ title: "图片管理", url: "/Administrator/Filemanager/Editor", width: window.innerWidth * 0.9, height: window.innerHeight * 0.8 }, { editor: editor // pointer to access editor from cshtml }) } }) });
2. 相册功能实现
文件夹管理实体类
public class FileManagerDirectoryEntity : BaseEntity { /// <summary> /// 父级Id /// </summary> public int ParentId { get; set; } /// <summary> /// 文件夹名称 /// </summary> public string Name { get; set; } /// <summary> /// 路径 /// </summary> public string FullPath { get; set; } /// <summary> /// 子文件数量 /// </summary> public int ChildrenCount { get; set; } }
文件管理实体类
public class FileManagerDirectoryEntity : BaseEntity { /// <summary> /// 父级Id /// </summary> public int ParentId { get; set; } /// <summary> /// 文件夹名称 /// </summary> public string Name { get; set; } /// <summary> /// 路径 /// </summary> public string FullPath { get; set; } /// <summary> /// 子文件数量 /// </summary> public int ChildrenCount { get; set; } }
相册功能具体实现controller
public class FileManagerController : Controller { #region Core private readonly IRepository<FileManagerDirectoryEntity> _fileManagerDirectoryRepository; private readonly IRepository<FileManagerFilesEntity> _fileManagerFilesRepository; private const string smallImage = "_small"; public FileManagerController( IRepository<FileManagerDirectoryEntity> fileManagerDirectoryRepository, IRepository<FileManagerFilesEntity> fileManagerFilesRepository ) { this._fileManagerDirectoryRepository = fileManagerDirectoryRepository; this._fileManagerFilesRepository = fileManagerFilesRepository; } #endregion /// <summary> /// 编辑器插件 /// 获取文件数据 /// </summary> /// <param name="dirId"></param> /// <returns></returns> public ActionResult Editor(int dirId = 0) { List<FileManagerFileModel> modelList = new List<FileManagerFileModel>(); //加载该文件夹下的文件夹 var dirList = _fileManagerDirectoryRepository.Table .Where(x=>x.ParentId==dirId) .OrderByDescending(x=>x.CreateTime) .ToList(); foreach(var item in dirList) { FileManagerFileModel model = new FileManagerFileModel(); model.Id = item.Id; model.Name = item.Name; model.FullPath = item.FullPath; model.FileType = 2; model.ChildrenCount = item.ChildrenCount; modelList.Add(model); } //加载该文件夹下的图片文件 var fileList = _fileManagerFilesRepository.Table .Where(x => x.DirectoryId == dirId) .OrderByDescending(x => x.CreateTime) .ToList(); foreach (var item in fileList) { FileManagerFileModel model = new FileManagerFileModel(); model.Id = item.Id; model.Name = item.Name; model.FullPath = item.FullPath; model.SmallFullPath = item.FullPath + smallImage + item.FileExt; model.FileType = 1; modelList.Add(model); } return View(modelList); } /// <summary> /// 创建文件夹 /// </summary> /// <param name="dirId"></param> /// <returns></returns> public ActionResult _AddDirectory(int dirId) { return View(); } /// <summary> /// 文件夹信息保存 /// </summary> /// <param name="dirId"></param> /// <param name="dirName"></param> /// <returns></returns> public ActionResult _AddDirectorySave(int dirId , string dirName) { var parentDirEntity = _fileManagerDirectoryRepository.GetById(dirId); if (!string.IsNullOrEmpty(dirName)) { var parentDirPath = parentDirEntity == null ? "/Content/FileManager/" : parentDirEntity.FullPath; if(parentDirEntity != null) { parentDirEntity.ChildrenCount++; } FileManagerDirectoryEntity entity = new FileManagerDirectoryEntity(); entity.ParentId = dirId; entity.Name = dirName; entity.FullPath = string.Format("{0}/{1}/", parentDirPath, Guid.NewGuid()); if (!Directory.Exists(Server.MapPath(entity.FullPath))) { Directory.CreateDirectory(Server.MapPath(entity.FullPath)); } _fileManagerDirectoryRepository.Insert(entity); _fileManagerDirectoryRepository.SaveChanges(); } return RedirectToAction("Editor",new { dirId = dirId}); } /// <summary> /// 上传图片 /// </summary> /// <param name="dirId"></param> /// <returns></returns> public JsonResult UploadImage(int dirId) { //路径地址 string fileUrl = ""; var parentDirEntity = _fileManagerDirectoryRepository.GetById(dirId); var parentDirPath = parentDirEntity == null ? "/Content/FileManager/" : parentDirEntity.FullPath; HttpFileCollectionBase postfile = HttpContext.Request.Files; if (postfile == null) { return Json(new { code = 1, msg = "文件不能为空" }); } var file = postfile[0]; string extName = Path.GetExtension(file.FileName); using (System.Drawing.Image image = System.Drawing.Image.FromStream(file.InputStream)) { string fileName = Guid.NewGuid().ToString() + extName; string smallImgName = string.Format("{0}{1}{2}", fileName, smallImage, extName); string route = Server.MapPath(parentDirPath); fileUrl = Path.Combine(parentDirPath, fileName); string savePath = Path.Combine(route, fileName); //缩略图路径 string smallImgPath = Path.Combine(route, smallImgName); //生成缩略图 try { ImageResizer.Fit(image, 160, 160, ImageResizeMode.Crop, ImageResizeScale.Down).Save(smallImgPath); } catch (Exception) { return Json(new { flag = false, msg = "生成缩略图出错!" }); } file.SaveAs(savePath); } #region 添加数据到素材表 FileManagerFilesEntity entity = new FileManagerFilesEntity(); entity.FileExt = extName; entity.FullPath = fileUrl; entity.Name = Path.GetFileNameWithoutExtension(file.FileName); entity.DirectoryId = dirId; entity.Size = file.ContentLength; _fileManagerFilesRepository.Insert(entity); _fileManagerFilesRepository.SaveChanges(); #endregion if (parentDirEntity != null) { parentDirEntity.ChildrenCount++; } _fileManagerDirectoryRepository.SaveChanges(); return Json(new { code = 0 }); } public class DeleteFilesParams { public int Id { get; set; } public int Type { get; set; } } /// <summary> /// 删除选中文件夹及图片 /// </summary> /// <returns></returns> public JsonResult CheckedFilesDelete(List<DeleteFilesParams> checkeds) { var directoryList = _fileManagerDirectoryRepository.Table.ToList(); var fileList = _fileManagerFilesRepository.Table.ToList(); foreach(var item in checkeds) { //删除图片 if (item.Type == 1) { var fileEntity = fileList.FirstOrDefault(x => x.Id == item.Id); string path = Server.MapPath(fileEntity.FullPath); if (System.IO.File.Exists(path)) { System.IO.File.Delete(path); } var parentDir = directoryList.Find(x => x.Id == fileEntity.DirectoryId); if (parentDir != null) { parentDir.ChildrenCount--; } _fileManagerFilesRepository.Delete(fileEntity); } else { var dirEntity = directoryList.FirstOrDefault(x => x.Id == item.Id); DeleteChildDirFiles(dirEntity.Id, directoryList, fileList); string path = Server.MapPath(dirEntity.FullPath); if (Directory.Exists(path)) { Directory.Delete(path, true); } var parentDir = directoryList.Find(x => x.Id == dirEntity.ParentId); if (parentDir != null) { parentDir.ChildrenCount--; } _fileManagerDirectoryRepository.Delete(dirEntity); } } _fileManagerFilesRepository.SaveChanges(); _fileManagerDirectoryRepository.SaveChanges(); return Json(new { code = 0 }); } public void DeleteChildDirFiles(int pid,List<FileManagerDirectoryEntity> dirList, List<FileManagerFilesEntity> files) { var dirEntityList = dirList.Where(x => x.ParentId == pid); var fileEntityList = files.Where(x => x.DirectoryId == pid); foreach (var item in dirEntityList) { DeleteChildDirFiles(item.Id, dirList, files); _fileManagerDirectoryRepository.Delete(item); } foreach (var item in fileEntityList) { _fileManagerFilesRepository.Delete(item); } _fileManagerDirectoryRepository.SaveChanges(); _fileManagerFilesRepository.SaveChanges(); } }
文件管理页面 Editor.chtml
@using Web.Areas.Administrator.Models @model List<FileManagerFileModel> @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>文件管理</title> <link href="/Assets/iconfont/iconfont.css" rel="stylesheet" /> <link href="/Scripts/layui/css/layui.css" rel="stylesheet" /> <style> body { background: #f6f6f6; } .toolbar { padding: 10px; background: #fff; } .toolbar i.iconfont{margin-right:10px;} .file-list{margin:20px;} .file-list li { float: left; background:#fff; margin-bottom:20px; margin-right:20px; } .file-list li .img-wapper { width:160px; } .file-list li .img-wapper img{width:100%; height:160px;} .file-list li .file-name { padding: 0 10px; width: 100%; overflow:hidden; line-height: 30px; height:30px; color: #666; font-size: 12px; background: #fafafa; } .file-list li .file-name .layui-form-checkbox{width:140px !important; overflow:hidden !important; } .file-list li:hover { box-shadow: 0 0 10px rgba(0,0,0,.1); } .file-list li:hover .file-name { background: #f5f5f5;} </style> </head> <body> <div class="toolbar"> <a class="layui-btn layui-btn-small" href="javascript:;" data-toggle="modal" data-title="新建文件夹" data-url="@Url.Action("_AddDirectory",new { dirId = Request["dirId"] == null ? 0 : int.Parse(Request["dirId"])})"> <i class="iconfont icon-directory"></i> 新建文件夹 </a> <button id="upload-img-btn" type="button" class="layui-btn"><i class="iconfont icon-photo"></i>上传图片</button> <button id="delete-img-btn" type="button" class="layui-btn layui-btn-danger"><i class="iconfont icon-photo"></i>删除图片</button> </div> <div class="layui-form"> <ul class="file-list"> @if (!string.IsNullOrWhiteSpace(Request["dirId"])) { <li class="file-item"> <div class="img-wapper"> <a href="javascript:history.back(-1);"> <img src="/Assets/images/default/admin_directory_back.png" alt="返回上级" title="返回上级" /> </a> </div> <div class="file-name"> ... </div> </li> } @if (Model.Any()) { foreach (var item in Model) { <li class="file-item"> @if (item.FileType == 1) { <div class="img-wapper"> <a href="javascript:;" class="file-img" data-url="@item.FullPath" data-title="@item.Name"> <img src="@item.SmallFullPath" title="@item.Name" /> </a> </div> <div class="file-name"> <input type="checkbox" name="file-id" lay-skin="primary" title="@item.Name" data-id="@item.Id" data-type="1"> </div> } else { <div class="img-wapper"> <a href="@Url.Action("Editor",new { dirId=item.Id})"> @if (item.ChildrenCount > 0) { <img src="/Assets/images/default/admin_directory_files.png" title="@item.Name" /> } else { <img src="/Assets/images/default/admin_directory.png" title="@item.Name" /> } </a> </div> <div class="file-name"> <input type="checkbox" name="file-id" lay-skin="primary" title="@item.Name" data-id="@item.Id" data-type="2"> </div> } </li> } } </ul> </div> <input id="dirId" value="@Request["dirId"]" hidden> <script src="~/Scripts/jquery-3.2.1.min.js"></script> <script src="/Scripts/layui/layui.js"></script> <script> //获取tinymce编辑器 var editor = top.tinymce.activeEditor.windowManager.getParams().editor; layui.use(['upload'], function () { var upload = layui.upload; var dirId = $("#dirId").val() == "" ? 0 : $("#dirId").val(); upload.render({ //允许上传的文件后缀 elem: '#upload-img-btn' , url: 'UploadImage?dirId=' + dirId , accept: 'file' //普通文件 , multiple: true , size: 1024 * 2 //限制文件大小,单位 KB , exts: 'jpg|jpeg|png|gif' //只允许上传压缩文件 , done: function (res) { if (res.code == 0) { window.location.reload(); } } }); //删除图片 $("#delete-img-btn").click(function () { var checkeds = []; $("input[name='file-id']:checkbox").each(function () { if (true == $(this).is(':checked')) { checkeds.push({ id: $(this).data('id'), type: $(this).data('type') }); } }); if (checkeds.length == 0) { layer.alert('请先选择需要删除的文件!'); } else { layer.confirm('删除后将无法恢复,请确认是否要删除所选文件?', { btn: ['确定删除', '我在想想'] //按钮 }, function () { $.ajax({ type: 'post', url: 'CheckedFilesDelete', data: { checkeds : checkeds }, success: function (result) { if (result.code == 0) { window.location.reload(); } else { showMsg(result.msg); } } }) }, function () { }); } }) }) //添加图片至编辑器 $(".file-img").click(function () { var url = $(this).data("url"), title = $(this).data("title"); //添加确认 layer.confirm('是否需要添加此图片?', { btn: ['确认添加', '我在想想'] //按钮 }, function () { editor.execCommand('mceInsertContent', false, '<img alt="' + title + '" src="' + url + '"/>'); editor.windowManager.close(); }, function () {}); }) </script> <script> //layui基本代码 $(function () { layui.use(['element', 'form', "layer"], function () { var element = layui.element; //表单渲染 var form = layui.form; form.on('submit(formDemo)', function (data) { layer.msg(JSON.stringify(data.field)); return false; }); form.render(); //异步加载modal $(document).on("click", '[data-toggle="modal"]', function (e) { var $this = $(this), url = $(this).data('url'), title = $(this).data("title") if (url) { $.ajax({ url: url, data: { rnd: Math.random() }, //dataType: 'html', success: function (data) { //示范一个公告层 layer.open({ type: 1 , title: title //不显示标题栏 , shade: 0.8 , shadeClose: true , fixed: false , area: ["900px"] , offset: '40px' , id: 'ajax-modal-wapper' //设定一个id,防止重复弹出 , move: false //禁止拖拽 , content: data }); }, error: function (XMLHttpRequest, textStatus, errorThrown) { alert('加载出错。' + textStatus + '. ' + XMLHttpRequest.status); }, complete: function () { } }); } }); }); }) </script> </body> </html>
新增文件夹页面 _AddDirectory.chtml
@{ Layout = null; } <div class="modal-content" style="padding-top:20px;"> <form class="layui-form" action="_AddDirectorySave" method="post" enctype="multipart/form-data"> <input name="dirId" value="@Request["dirId"]" hidden> <div class="layui-form-item"> <label class="layui-form-label" for="dirName">文件夹名称</label> <div class="layui-input-block"> <input class="layui-input" id="dirName" lay-verify="required" name="dirName" placeholder="请输入文件夹名称" type="text" value=""> </div> </div> <div class="layui-form-item"> <div class="layui-input-block"> <button class="layui-btn" lay-submit lay-filter="account-form" type="submit">保存信息</button> </div> </div> </form> </div>
3. 将选中图片插入编辑器
在图片列表的页面中,我们只需要在点击图片的事件中调用Tinymce编辑器的插入方法即可,以下为插入图片的代码
<script> //获取tinymce编辑器 var editor = top.tinymce.activeEditor.windowManager.getParams().editor; layui.use(['upload'], function () { var upload = layui.upload; var dirId = $("#dirId").val() == "" ? 0 : $("#dirId").val(); upload.render({ //允许上传的文件后缀 elem: '#upload-img-btn' , url: 'UploadImage?dirId=' + dirId , accept: 'file' //普通文件 , multiple: true , size: 1024 * 2 //限制文件大小,单位 KB , exts: 'jpg|jpeg|png|gif' //只允许上传压缩文件 , done: function (res) { if (res.code == 0) { window.location.reload(); } } }); //删除图片 $("#delete-img-btn").click(function () { var checkeds = []; $("input[name='file-id']:checkbox").each(function () { if (true == $(this).is(':checked')) { checkeds.push({ id: $(this).data('id'), type: $(this).data('type') }); } }); if (checkeds.length == 0) { layer.alert('请先选择需要删除的文件!'); } else { layer.confirm('删除后将无法恢复,请确认是否要删除所选文件?', { btn: ['确定删除', '我在想想'] //按钮 }, function () { $.ajax({ type: 'post', url: 'CheckedFilesDelete', data: { checkeds : checkeds }, success: function (result) { if (result.code == 0) { window.location.reload(); } else { showMsg(result.msg); } } }) }, function () { }); } }) }) //添加图片至编辑器 $(".file-img").click(function () { var url = $(this).data("url"), title = $(this).data("title"); //添加确认 layer.confirm('是否需要添加此图片?', { btn: ['确认添加', '我在想想'] //按钮 }, function () { editor.execCommand('mceInsertContent', false, '<img alt="' + title + '" src="' + url + '"/>'); editor.windowManager.close(); }, function () {}); }) </script>