Bootstrap fileinput文件上传预览插件使用详解


Posted in Javascript onMay 16, 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" >

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文件上传预览插件使用详解

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、后端源码

@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 原型模式实现OOP的再研究
Apr 09 Javascript
jquery中加载图片自适应大小主要实现代码
Aug 23 Javascript
JS匀速运动演示示例代码
Nov 26 Javascript
JavaScript中的 attribute 和 jQuery中的 attr 方法浅析
Jan 04 Javascript
简单实现js无缝滚动效果
Feb 05 Javascript
js中new一个对象的过程
Feb 20 Javascript
详谈js原型继承的一些问题
Sep 06 Javascript
JavaScript中的一些隐式转换和总结(推荐)
Dec 22 Javascript
微信小程序按钮去除边框线分享页面功能
Aug 27 Javascript
小程序如何构建骨架屏
May 29 Javascript
解决vue的过渡动画无法正常实现问题
Oct 31 Javascript
vue实现下载文件流完整前后端代码
Nov 17 Vue.js
js自定义瀑布流布局插件
May 16 #Javascript
简单实现js点击展开二级菜单功能
May 16 #Javascript
JavaScript禁止微信浏览器下拉回弹效果
May 16 #Javascript
jQuery实现div跟随鼠标移动
Aug 20 #jQuery
jquery+ajax实现省市区三级联动 (封装和不封装两种方式)
May 15 #jQuery
bootstrap响应式表格实例详解
May 15 #Javascript
VUE多层路由嵌套实现代码
May 15 #Javascript
You might like
完美解决PHP中的Cannot modify header information 问题
2013/08/12 PHP
swoole和websocket简单聊天室开发
2017/11/18 PHP
php语言注释,单行注释和多行注释
2018/01/21 PHP
CentOS7.0下安装PHP5.6.30服务的教程详解
2018/09/29 PHP
javaScript - 如何引入js代码
2021/03/09 Javascript
JavaScript中的Screen屏幕对象
2008/01/16 Javascript
nodejs 后缀名判断限制代码
2011/03/31 NodeJs
Javascript表格翻页效果实现思路及代码
2013/08/23 Javascript
jQuery实现动态表单验证时文本框抖动效果完整实例
2015/08/21 Javascript
jQuery的promise与deferred对象在异步回调中的作用
2016/05/03 Javascript
vue2.0 与 bootstrap datetimepicker的结合使用实例
2017/05/22 Javascript
基于JS代码实现简单易用的倒计时 x 天 x 时 x 分 x 秒效果
2017/07/13 Javascript
通过一个简单的例子学会vuex与模块化
2017/11/22 Javascript
vue中axios实现数据交互与跨域问题
2019/05/12 Javascript
微信小程序云函数使用mysql数据库过程详解
2019/08/07 Javascript
详解element-ui 表单校验 Rules 配置 常用黑科技
2020/07/11 Javascript
Vue组件跨层级获取组件操作
2020/07/27 Javascript
Nodejs 数组的队列以及forEach的应用详解
2021/02/25 NodeJs
windows下wxPython开发环境安装与配置方法
2014/06/28 Python
解决Python传递中文参数的问题
2015/08/04 Python
Python 实现 贪吃蛇大作战 代码分享
2016/09/07 Python
TensorFlow损失函数专题详解
2018/04/26 Python
啥是佩奇?使用Python自动绘画小猪佩奇的代码实例
2019/02/20 Python
Python socket模块ftp传输文件过程解析
2019/11/05 Python
python实现拼图小游戏
2020/02/22 Python
Python参数传递机制传值和传引用原理详解
2020/05/22 Python
Python中常见的数制转换有哪些
2020/05/27 Python
《猴子种树》教学反思
2014/02/14 职场文书
2014年元旦联欢会活动策划方案
2014/02/16 职场文书
幼儿园毕业典礼主持词
2014/03/21 职场文书
《陈涉世家》教学反思
2014/04/12 职场文书
亮剑精神演讲稿
2014/05/23 职场文书
卫生厅领导班子党的群众路线教育实践活动整改措施
2014/09/20 职场文书
2014年学校德育工作总结
2014/12/05 职场文书
Ajax实现异步加载数据
2021/11/17 Javascript
一起来看看Vue的核心原理剖析
2022/03/24 Vue.js