Bootstrap fileinput组件封装及使用详解


Posted in Javascript onMarch 10, 2017

介绍

通过本文,你可以学习到如何封装或者开发一个前端组件,同时学习Bootstrap-fileinput组件的使用,封装后使用更加简单方便。

Bootstrap fileinput组件封装及使用详解

BaseFile是AdminEAP框架中基于Bootstrap-fileinput的附件上传组件,它支持 支持多文件、在线预览、拖拽上传等功能,封装后BaseFile主要包括以下功能:

  • 弹出窗口的附件上传
  • 当前界面的附件上传
  • 显示附件明细
  • 可编辑的附件明细(删除、预览、不可新增)

关于Bootstrap-fileinput的API文档可参考http://plugins.krajee.com/file-input

本文源码已在AdminEAP框架(一个基于AdminLTE的Java开发平台)中开源,可在Github下载相关代码:

Github:https://github.com/bill1012/AdminEAP

AdminEAP官网:http://www.admineap.com

使用说明

1、初始化

如果需要在当前界面使用附件上传功能(非弹窗方式)则需要在头部引入相关的css和js文件

css文件

<link rel="stylesheet" href="./resources/common/libs/fileinput/css/fileinput.min.css" rel="external nofollow" >

js文件

<script src="./resources/common/libs/fileinput/js/fileinput.js"></script>
<script src="./resources/common/libs/fileinput/js/locales/zh.js"></script>
<!--BaseFile组件-->
<script src="./resources/common/js/base-file.js"></script>

form表单上还需要配置enctype="multipart/form-data"属性

2、弹窗方式调用

BaseFile支持弹窗方式打开一个附件上传窗口,点击附件上传后,弹出窗口,上传附件关闭窗口后,上传的附件在type=file的控件回填。

在表单中点击弹窗上传附件:

Bootstrap fileinput组件封装及使用详解

Bootstrap fileinput组件封装及使用详解

上传完毕,关闭窗口,附件回填

Bootstrap fileinput组件封装及使用详解

再次打开上传附件上传窗口时,会把已有的附件回填到附件上传窗口。

配置如下:

html代码

<input type="hidden" name="fileIds" id="fileIds">
   <div class="form-group">
    <div class="btn btn-default btn-file" id="uploadFile">
     <i class="fa fa-paperclip"></i> 上传附件(Max. 10MB)
    </div>
   </div>
   <div class="form-group" id="file_container">
    <input type="file" name="file" id="attachment">
   </div>

js代码

$("#uploadFile").file({
   title: "请上传附件",
   fileinput: {
    maxFileSize: 10240,
    maxFileCount:3
   },
   fileIdContainer:"[name='fileIds']",
   showContainer:'#attachment',
   //显示文件类型 edit=可编辑 detail=明细 默认为明细
   showType:'edit',
   //弹出窗口 执行上传附件后的回调函数(window:false不调用此方法)
   window:true,
   callback:function(fileIds,oldfileIds){
    //更新fileIds
    this.showFiles({
     fileIds:fileIds
    });
   }
  });

3、本地界面调用

本地界面调用附件上传,如下图所示:

将上传附件嵌入到当前界面方式

Bootstrap fileinput组件封装及使用详解

上传后的附件可删除、可预览

Bootstrap fileinput组件封装及使用详解

(目前图片文件可预览,其他文件不可预览,后期将集成txt/xml/html/pdf的预览功能)

配置如下:

html代码

<div class="form-group" id="file_container">
  <input type="file" name="file" id="attachment">
</div>

js代码

$("#attachment").file({
   fileinput: {
    maxFileSize: 10240,
    maxFileCount:3
   },
   fileIdContainer:"[name='fileIds']",
   window:false
  });

4、控件参数说明

window 默认为true,弹窗方式打开

title window=true时配置,弹窗的标题,默认为“文件上传”

width window=true时配置,弹窗的宽度,默认900

winId window=true时配置,弹出的id,默认为fileWin

fileinput Bootstrap-fileinput的配置参数,会覆盖默认配置,比如允许上传哪种类型的附件allowedFileTypes,允许上传最大附件大小maxFileSize,允许上传附件的个数maxFileCount等,具体的配置参数可以查询Bootstrap-fileinput的API文档。

fileIdContainer 必须,上传后的附件id存储的位置,id以逗号分隔

showContainer window=true必须配置,文件上传后回填的区域,window=false时如不配置,则取base-file的初始对象

showType window=true配置,值为edit或者detail,edit表示回填后可对数据进行删除、预览,detail只能显示,不能删除

callback window=true配置,关闭附件上传的窗口后执行的回调函数(比如更新当前的文件列表),fileIds,oldfileIds两个参数分别是更新后文件ids和更新前的文件ids

BaseFile默认配置,BaseFile的更多实现,请查看BaseFile源码

BaseFile.prototype.default = {
  winId: "fileWin",
  width: 900,
  title: "文件上传",
  //通用文件上传界面
  url: basePath + "/file/uploader",
  //默认支持多文件上传
  multiple: true,
  //默认弹出附件上传窗口
  window:true,
  showType:"detail",
  fileinput: {
   language: 'zh',
   uploadUrl: basePath + "/file/uploadMultipleFile",
   deleteUrl:basePath+"/file/delete",
   uploadAsync:false,
   validateInitialCount:true,
   overwriteInitial: false,
   allowedPreviewTypes: ['image'],
   previewFileIcon:'<i class="fa fa-file-o"></i>',
   previewFileIconSettings: null,
   slugCallback: function (text) {
    var newtext=(!text||text=='') ? '' : String(text).replace(/[\-\[\]\/\{}:;#%=\(\)\*\+\?\\\^\$\|<>&"']/g, '_');
    //去除空格
    return newtext.replace(/(^\s+)|(\s+$)/g,"").replace(/\s/g,"");
   }
  }
 }

5、BaseFile控件源码

/**
 * 通用文件管理组件
 * @author billjiang qq:475572229
 */
(function ($, window, document, undefined) {
 'use strict';

 var pluginName = 'file';

 $.fn[pluginName] = function (options) {
  var self = $(this);
  if (this == null)
   return null;
  var data = this.data(pluginName);
  if (!data) {
   data = new BaseFile(this, $.extend(true, {}, options));
   self.data(pluginName, data);
  }
 };


 var BaseFile = function (element, options) {
  this.element = element;
  //extend优先级 后面的会覆盖前面的
  //alert(this.element.selector);
  //将容器ID传过去便于弹窗获取到BaseFile对象,如果页面布局不在使用jquery.load方法,则该方法会失效,因为不是一个页面了
  options.container = options.container || this.element.selector.replace("#", "");
  //初始化文件图标信息
  this.getFileIconSettings();
  this.options = $.extend(true, {}, this.default, options);
  //初始化图标信息
  this.initFileIds();

  if(this.options.window) {
   this.element.click(function () {
    $(this).data('file').openWin();
   });
  }else{
   //非弹窗形式
   if(this.options.multiple)
    this.element.attr("multiple","multiple");
  }

  //如果配置了附件编辑容器showContainer(附件列表,可单个删除),则进行初始化
  if(this.hasDisplayZone()){
   this.showFiles();
  }


 }

 BaseFile.prototype.default = {
  winId: "fileWin",
  width: 900,
  title: "文件上传",
  //通用文件上传界面
  url: basePath + "/file/uploader",
  //默认支持多文件上传
  multiple: true,
  //默认弹出附件上传窗口
  window:true,
  showType:"detail",
  fileinput: {
   language: 'zh',
   uploadUrl: basePath + "/file/uploadMultipleFile",
   deleteUrl:basePath+"/file/delete",
   uploadAsync:false,
   validateInitialCount:true,
   overwriteInitial: false,
   allowedPreviewTypes: ['image'],
   previewFileIcon:'<i class="fa fa-file-o"></i>',
   previewFileIconSettings: null,
   slugCallback: function (text) {
    var newtext=(!text||text=='') ? '' : String(text).replace(/[\-\[\]\/\{}:;#%=\(\)\*\+\?\\\^\$\|<>&"']/g, '_');
    //去除空格
    return newtext.replace(/(^\s+)|(\s+$)/g,"").replace(/\s/g,"");
   }
  }
 }

 BaseFile.prototype.getFileInputConfig=function () {
  return this.options.fileinput;
 }
 BaseFile.prototype.getFileIconSettings = function () {
  var self = this;
  ajaxPost(basePath + "/file/icons", null, function (icons) {
   self.previewFileIconSettings = icons;
   //console.log(self.previewFileIconSettings);
  })
 }


 BaseFile.prototype.openWin = function () {
  var that = this;
  var self = $.extend(true, {}, this.options);
  //深拷贝后删除属性,这样不会通过后台传送过去,防止被XSS过滤掉特殊字符
  //不需要通过参数config=传递到弹窗的参数可使用delete删除
  delete self.callback;
  delete self.fileIds;
  delete self.showContainer;
  delete self.fileIdContainer;
  delete self.fileinput;

  /*console.log(this.options);
   console.log("=============");
   console.log(self);*/
  modals.openWin({
   winId: that.options.winId,
   url: that.options.url + "?config=" + JSON.stringify(self),
   width: that.options.width + "px",
   title: that.options.title,
   backdrop: "static"
  });
 }

 BaseFile.prototype.callbackHandler = function (fileIds) {
  //更新fileIds并执行回调函数
  var oldfileIds = this.options.fileIds;
  this.options.fileIds = fileIds;
  this.updateFileIds();
  if (this.options.callback) {
   this.options.callback.call(this, fileIds, oldfileIds);
  }
 }

 //调用成功后执行显示附件
 BaseFile.prototype.showFiles=function(options){
  options=options||{};
  if(!this.hasDisplayZone()){
   modals.error("请配置showContainer属性,并在容器下配置type=file的input组件");
   return;
  }
  var fileIds=options.fileIds||this.options.fileIds;
  if(!fileIds&&this.options.window){
   $(this.options.showContainer).hide();
   return;
  }
  //显示
  $(this.options.showContainer).show();
  var fileComponet=$(this.options.showContainer);
  var fileResult=this.getFileResult(fileIds),preview=fileResult.initialPreview,previewConfig=fileResult.initialPreviewConfig,self=this;
  //配置三类参数 edit=附件列表(可删除) detail=附件列表(显示) 可上传
  var defaultConfig={
   initialPreview:preview,
   initialPreviewConfig:previewConfig
  };
  var config;
  if(this.options.window){
   if(this.options.showType=="edit"){
    //全局配置->本方法默认配置->edit属性下配置->外部参数
    config=$.extend({},self.options.fileinput,defaultConfig,{
     showRemove:false,
     showUpload:false,
     showClose:false,
     showBrowse:false,
     showCaption:false
    },options);
   }else if(this.options.showType=="detail"){
    config=$.extend({},self.options.fileinput,defaultConfig,{
     showRemove:false,
     showUpload:false,
     showClose:false,
     showBrowse:false,
     showCaption:false,
     initialPreviewShowDelete:false
    },options);
   }
  }else{
   config=$.extend({},self.options.fileinput,defaultConfig,{
    showClose:false
   },options);
  }

  if(!config){
   modals.error("未找到showFiles中的相关配置");
   return;
  }
  //console.log("config=========="+JSON.stringify(config));
  fileComponet.fileinput('destroy');
  fileComponet.fileinput(config).on("filedeleted",function (event,key) {
   var newfids=self.deleteFileIds(key,self.options.fileIds);
   self.options.fileIds=newfids;
   self.updateFileIds();
  }).on("fileuploaded",function(event,data,previewId,index){
   var newfids=self.addFileIds(data.response.fileIds,self.options.fileIds);
   self.options.fileIds=newfids;
   self.updateFileIds();
  }).on("filebatchuploadsuccess",function (event,data,previewId,index) {
   var newfids=self.addFileIds(data.response.fileIds,self.options.fileIds);
   self.options.fileIds=newfids;
   self.updateFileIds();
  }).on("filezoomhidden", function(event, params) {
   $(document.body).removeClass('modal-open');
   $(document.body).css("padding-right","0px");
  });
 }

 /**
  * 向targetIds里删除数据fileIds
  * @param fileIds
  * @param targetIds
  */
 BaseFile.prototype.deleteFileIds=function(fileIds,targetIds){
  if(!fileIds) return targetIds;
  //没有文件删除,其中必有蹊跷
  if(!targetIds){
   modals.error("没有要删除的文件,请检查是否数据没有初始化");
   return;
  }
  var fileIdArr=fileIds.split(",");
  var fresult=targetIds.split(",");
  $.each(fileIdArr,function (index,fileId){
   //存在则删除
   if($.inArray(fileId,fresult)>-1){
    fresult.splice($.inArray(fileId,fresult),1);
   }
  })
  return fresult.join();
 }

 /**
  * 向targetIds里加数据fileIds
  * @param fileIds
  * @param targetIds
  */
 BaseFile.prototype.addFileIds=function (fileIds,targetIds) {
  if(!fileIds)return targetIds;
  var fileIdArr=fileIds.split(",");
  var fresult=[];
  if(targetIds){
   fresult=targetIds.split(",");
  }
  $.each(fileIdArr,function (index,fileId){
   //不存在,新增
   if($.inArray(fileId,fresult)==-1){
    fresult.push(fileId);
   }
  })
  return fresult.join();
 }

 BaseFile.prototype.updateFileIds=function(){
  if(this.options.fileIdContainer)
   $(this.options.fileIdContainer).val(this.options.fileIds);
 }

 BaseFile.prototype.initFileIds=function(){
  //不弹出窗口的话一定要绑定fileIdContainer
  if(!this.options.window){
   if(!this.options.fileIdContainer||!$(this.options.fileIdContainer)){
    modals.info("请设置fileIdContainer属性");
    return;
   }
  }
  if(!this.options.fileIds){
   if(this.options.fileIdContainer){
    this.options.fileIds=$(this.options.fileIdContainer).val();
   }
  }
 }

 BaseFile.prototype.getFileResult=function(fileIds){
  var ret=null;
  ajaxPost(basePath+"/file/getFiles",{fileIds:fileIds},function(result){
   ret=result;
  });
  return ret;
 };

 /**
  * 是否有显示区域
  * @returns {boolean}
  */
 BaseFile.prototype.hasDisplayZone=function(){
  if(!this.options.showContainer){
   this.options.showContainer=this.element.selector;
  }
  if(!this.options.showContainer||!$(this.options.showContainer)){
   return false;
  }
  return true;
 }


})(jQuery, window, document);

6、后端源码

package com.cnpc.framework.base.controller;


import com.cnpc.framework.base.entity.SysFile;
import com.cnpc.framework.base.entity.User;
import com.cnpc.framework.base.pojo.AvatarResult;
import com.cnpc.framework.base.pojo.FileResult;
import com.cnpc.framework.base.pojo.MarkDownResult;
import com.cnpc.framework.base.pojo.Result;
import com.cnpc.framework.base.service.UploaderService;
import com.cnpc.framework.util.SecurityUtil;
import com.cnpc.framework.utils.DateUtil;
import com.cnpc.framework.utils.FileUtil;
import com.cnpc.framework.utils.PropertiesUtil;
import com.cnpc.framework.utils.StrUtil;
import org.apache.commons.fileupload.util.Streams;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.util.*;

@Controller
@RequestMapping("/file")
public class UploaderController {

 private static Logger logger= LoggerFactory.getLogger(UploaderController.class);

 //previewFileIconSettings
 public static Map fileIconMap=new HashMap();
 @Resource
 private UploaderService uploaderService;

 static {
  fileIconMap.put("doc" ,"<i class='fa fa-file-word-o text-primary'></i>");
  fileIconMap.put("docx","<i class='fa fa-file-word-o text-primary'></i>");
  fileIconMap.put("xls" ,"<i class='fa fa-file-excel-o text-success'></i>");
  fileIconMap.put("xlsx","<i class='fa fa-file-excel-o text-success'></i>");
  fileIconMap.put("ppt" ,"<i class='fa fa-file-powerpoint-o text-danger'></i>");
  fileIconMap.put("pptx","<i class='fa fa-file-powerpoint-o text-danger'></i>");
  fileIconMap.put("jpg" ,"<i class='fa fa-file-photo-o text-warning'></i>");
  fileIconMap.put("pdf" ,"<i class='fa fa-file-pdf-o text-danger'></i>");
  fileIconMap.put("zip" ,"<i class='fa fa-file-archive-o text-muted'></i>");
  fileIconMap.put("rar" ,"<i class='fa fa-file-archive-o text-muted'></i>");
  fileIconMap.put("default" ,"<i class='fa fa-file-o'></i>");
 }

 //从setting.properties文件中注入文件相对目录(相对目录为显示文件)
 //@Value("${uploaderPath}") 只有配置@Config才能注入
 private static final String uploaderPath=PropertiesUtil.getValue("uploaderPath");


 /**
  * 跳转到通用文件上传窗口
  * @return
  */
 @RequestMapping(value="/uploader",method = RequestMethod.GET)
 public String uploader(String config,HttpServletRequest request){
  request.setAttribute("config",config);
  return "base/file/file_uploader";
 }


 /**
  * 通用文件上传接口,存储到固定地址,以后存储到文件服务器地址
  */
 @RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
 @ResponseBody
 public SysFile uploadFile(@RequestParam(value = "file", required = false) MultipartFile file,
        HttpServletRequest request, HttpServletResponse response) {
  //TODO dosomething
  return new SysFile();
 }

 /**
  * 多文件上传,用于uploadAsync=false(同步多文件上传使用)
  * @param files
  * @param request
  * @param response
  * @return
  */
 @RequestMapping(value = "/uploadMultipleFile", method = RequestMethod.POST)
 @ResponseBody
 public FileResult uploadMultipleFile(@RequestParam(value = "file", required = false) MultipartFile[] files,
           HttpServletRequest request, HttpServletResponse response) throws IOException {
  System.out.println("the num of file:"+files.length);

  FileResult msg = new FileResult();

  ArrayList<Integer> arr = new ArrayList<>();
  //缓存当前的文件
  List<SysFile> fileList=new ArrayList<>();
  String dirPath = request.getRealPath("/");
  for (int i = 0; i < files.length; i++) {
   MultipartFile file = files[i];

   if (!file.isEmpty()) {
    InputStream in = null;
    OutputStream out = null;
    try {
     File dir = new File(dirPath+uploaderPath);
     if (!dir.exists())
      dir.mkdirs();
     //这样也可以上传同名文件了
     String filePrefixFormat="yyyyMMddHHmmssS";
     System.out.println(DateUtil.format(new Date(),filePrefixFormat));
     String savedName=DateUtil.format(new Date(),filePrefixFormat)+"_"+file.getOriginalFilename();
     String filePath=dir.getAbsolutePath() + File.separator + savedName;
     File serverFile = new File(filePath);
     //将文件写入到服务器
     //FileUtil.copyInputStreamToFile(file.getInputStream(),serverFile);
     file.transferTo(serverFile);
     SysFile sysFile=new SysFile();
     sysFile.setFileName(file.getOriginalFilename());
     sysFile.setSavedName(savedName);
     sysFile.setCreateDateTime(new Date());
     sysFile.setUpdateDateTime(new Date());
     sysFile.setCreateUserId(SecurityUtil.getUserId());
     sysFile.setDeleted(0);
     sysFile.setFileSize(file.getSize());
     sysFile.setFilePath(uploaderPath+File.separator+savedName);
     uploaderService.save(sysFile);
     fileList.add(sysFile);
     /*preview.add("<div class=\"file-preview-other\">\n" +
       "<span class=\"file-other-icon\"><i class=\"fa fa-file-o text-default\"></i></span>\n" +
       "</div>");*/

     logger.info("Server File Location=" + serverFile.getAbsolutePath());
    } catch (Exception e) {
     logger.error( file.getOriginalFilename()+"上传发生异常,异常原因:"+e.getMessage());
     arr.add(i);
    } finally {
     if (out != null) {
      out.close();
     }
     if (in != null) {
      in.close();
     }
    }
   } else {
    arr.add(i);
   }
  }

  if(arr.size() > 0) {
   msg.setError("文件上传失败!");
   msg.setErrorkeys(arr);
  }
  FileResult preview=getPreivewSettings(fileList,request);
  msg.setInitialPreview(preview.getInitialPreview());
  msg.setInitialPreviewConfig(preview.getInitialPreviewConfig());
  msg.setFileIds(preview.getFileIds());
  return msg;
 }

 //删除某一项文件
 @RequestMapping(value="/delete",method = RequestMethod.POST)
 @ResponseBody
 public Result delete(String id,HttpServletRequest request){
  SysFile sysFile=uploaderService.get(SysFile.class,id);
  String dirPath=request.getRealPath("/");
  FileUtil.delFile(dirPath+uploaderPath+File.separator+sysFile.getSavedName());
  uploaderService.delete(sysFile);
  return new Result();
 }

 /**
  * 获取字体图标map,base-file控件使用
  */
 @RequestMapping(value="/icons",method = RequestMethod.POST)
 @ResponseBody
 public Map getIcons(){
  return fileIconMap;
 }

 /**
  * 根据文件名获取icon
  * @param fileName 文件
  * @return
  */
 public String getFileIcon(String fileName){
  String ext= StrUtil.getExtName(fileName);
  return fileIconMap.get(ext)==null?fileIconMap.get("default").toString():fileIconMap.get(ext).toString();
 }

 /**
  * 根据附件IDS 获取文件
  * @param fileIds
  * @param request
  * @return
  */
 @RequestMapping(value="/getFiles",method = RequestMethod.POST)
 @ResponseBody
 public FileResult getFiles(String fileIds,HttpServletRequest request){
  String[] fileIdArr=fileIds.split(",");
  DetachedCriteria criteria=DetachedCriteria.forClass(SysFile.class);
  criteria.add(Restrictions.in("id",fileIdArr));
  criteria.addOrder(Order.asc("createDateTime"));
  List<SysFile> fileList=uploaderService.findByCriteria(criteria);
  return getPreivewSettings(fileList,request);
 }


 /**
  * 回填已有文件的缩略图
  * @param fileList 文件列表
  * @param request
  * @return initialPreiview initialPreviewConfig fileIds
  */
 public FileResult getPreivewSettings(List<SysFile> fileList,HttpServletRequest request){
  FileResult fileResult=new FileResult();
  List<String> previews=new ArrayList<>();
  List<FileResult.PreviewConfig> previewConfigs=new ArrayList<>();
  //缓存当前的文件
  String dirPath = request.getRealPath("/");
  String[] fileArr=new String[fileList.size()];
  int index=0;
  for (SysFile sysFile : fileList) {
   //上传后预览 TODO 该预览样式暂时不支持theme:explorer的样式,后续可以再次扩展
   //如果其他文件可预览txt、xml、html、pdf等 可在此配置
   if(FileUtil.isImage(dirPath+uploaderPath+File.separator+sysFile.getSavedName())) {
    previews.add("<img src='." + sysFile.getFilePath().replace(File.separator, "/") + "' class='file-preview-image kv-preview-data' " +
      "style='width:auto;height:160px' alt='" + sysFile.getFileName() + " title='" + sysFile.getFileName() + "''>");
   }else{
    previews.add("<div class='kv-preview-data file-preview-other-frame'><div class='file-preview-other'>" +
      "<span class='file-other-icon'>"+getFileIcon(sysFile.getFileName())+"</span></div></div>");
   }
   //上传后预览配置
   FileResult.PreviewConfig previewConfig=new FileResult.PreviewConfig();
   previewConfig.setWidth("120px");
   previewConfig.setCaption(sysFile.getFileName());
   previewConfig.setKey(sysFile.getId());
   // previewConfig.setUrl(request.getContextPath()+"/file/delete");
   previewConfig.setExtra(new FileResult.PreviewConfig.Extra(sysFile.getId()));
   previewConfig.setSize(sysFile.getFileSize());
   previewConfigs.add(previewConfig);
   fileArr[index++]=sysFile.getId();
  }
  fileResult.setInitialPreview(previews);
  fileResult.setInitialPreviewConfig(previewConfigs);
  fileResult.setFileIds(StrUtil.join(fileArr));
  return fileResult;
 }
}

总结

本文源码已在AdminEAP框架(一个基于AdminLTE的Java开发平台)中开源,可在Github下载相关代码:

Github:https://github.com/bill1012/AdminEAP

AdminEAP官网:http://www.admineap.com

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript 继承详解(四)
Jul 13 Javascript
ActiveX控件与Javascript之间的交互示例
Jun 04 Javascript
jquery.uploadify插件在chrome浏览器频繁崩溃解决方法
Mar 01 Javascript
jquery ajax双击div可直接修改div中的内容
Mar 04 Javascript
JS中setTimeout的巧妙用法前端函数节流
Mar 24 Javascript
详解AngularJS中$filter过滤器使用(自定义过滤器)
Feb 04 Javascript
vue2.0 自定义日期时间过滤器
Jun 07 Javascript
jquery实现限制textarea输入字数的方法
Sep 06 jQuery
Vue自定义指令封装节流函数的方法示例
Jul 09 Javascript
Vue的H5页面唤起支付宝支付功能
Apr 18 Javascript
layui 点击重置按钮, select 并没有被重置的解决方法
Sep 03 Javascript
vue中echarts图表大小适应窗口大小且不需要刷新案例
Jul 19 Javascript
Bootstrap栅格系统使用方法及页面调整变形的解决方法
Mar 10 #Javascript
Node.js中.pfx后缀文件的处理方法
Mar 10 #Javascript
Vue.js结合bootstrap实现分页控件
Mar 10 #Javascript
js获取ip和地区
Mar 10 #Javascript
Vue.js bootstrap前端实现分页和排序
Mar 10 #Javascript
JavaScript组件开发之输入框加候选框
Mar 10 #Javascript
原生javascript移动端滑动banner效果
Mar 10 #Javascript
You might like
PHP5 安装方法
2006/10/09 PHP
php下实现在指定目录搜索指定类型文件的函数
2008/10/03 PHP
php中static静态变量的使用方法详解
2010/06/04 PHP
PHP生成随机密码方法汇总
2015/08/27 PHP
PHP正则表达式处理函数(PCRE 函数)实例小结
2019/05/09 PHP
Laravel 使用查询构造器配合原生sql语句查询的例子
2019/10/12 PHP
javascript编程起步(第二课)
2007/02/27 Javascript
JavaScript Sort 表格排序
2009/10/31 Javascript
Textbox控件注册回车事件及触发按钮提交事件具体实现
2013/03/04 Javascript
js解决弹窗问题实现班级跳转DIV示例
2014/01/06 Javascript
简介AngularJS的视图功能应用
2015/06/17 Javascript
javascript实现网站加入收藏功能
2015/12/16 Javascript
全面解析JavaScript中“&amp;&amp;”和“||”操作符(总结篇)
2016/07/18 Javascript
Javascript中级语法快速入手
2016/07/30 Javascript
利用Angular.js限制textarea输入的字数
2016/10/20 Javascript
Angular路由简单学习
2016/12/26 Javascript
jQuery除指定区域外点击任何地方隐藏DIV功能
2017/11/13 jQuery
vue实现记事本功能
2019/06/26 Javascript
countUp.js实现数字动态变化效果
2019/10/17 Javascript
基于vue+element实现全局loading过程详解
2020/07/10 Javascript
python基础教程之缩进介绍
2014/08/29 Python
python实现简单的TCP代理服务器
2014/10/08 Python
ubuntu系统下 python链接mysql数据库的方法
2017/01/09 Python
Python随机生成手机号、数字的方法详解
2017/07/21 Python
Python模块WSGI使用详解
2018/02/02 Python
python使用__slots__让你的代码更加节省内存
2018/09/05 Python
idea创建springMVC框架和配置小文件的教程图解
2018/09/18 Python
python3实现点餐系统
2019/01/24 Python
Python 用matplotlib画以时间日期为x轴的图像
2019/08/06 Python
浅谈Python中threading join和setDaemon用法及区别说明
2020/05/02 Python
考试作弊检讨书
2014/10/21 职场文书
消夏晚会主持词
2015/06/30 职场文书
篮球赛闭幕式主持词
2015/07/03 职场文书
治庸问责工作总结
2015/08/11 职场文书
如何利用map实现Nginx允许多个域名跨域
2021/03/31 Servers
CSS浮动引起的高度塌陷问题
2022/08/05 HTML / CSS