vue+iview 实现可编辑表格的示例代码


Posted in Javascript onOctober 31, 2018

先简单说明一下,这个Demo引入的vue,iview的方式是标签引入的,没有用到webpack之类的构建工具...

毕竟公司还在用angularjs+jq.

这也是我第一次写文章,大家看看思路就行了,要是有大佬指点指点就更好了

话不多说,先来个效果图

vue+iview 实现可编辑表格的示例代码

我们再看下极为简单的目录结构

vue+iview 实现可编辑表格的示例代码

IViewEditTable    ## vue+iview 实现的可编辑表格
└── index.html    ## 首页
└── js
 └── editTable.js   ## 首页JS
└── ivew      ## iview相关
└── vue
 ├── axios.min.js   ## axios (ajax)
 ├── util.js    ## 与业务无关的纯工具函数包
 └── vue.min.js   ## vue (2.x)

首页html:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <title>可编辑表格</title>
 <link href="iview/iview.css" rel="external nofollow" rel="stylesheet" />
</head>
<body style="background-color: #f0f3f4;">
 <div id="editTableCtrl">
 <i-table :loading="loading" border :data="dataList" :columns="columnsList" stripe size="small"></i-table>
 </div>
 <script src="vue/axios.min.js"></script>
 <script src="vue/vue.min.js"></script>
 <script src="iview/iview.min.js"></script>
 <script src="vue/util.js"></script>
 <script src="js/editTable.js"></script>
</body>
</html>

首页没什么说的,都是基本的架子. 这是需要渲染的数据及其说明:

{
 "Status": 1,
 "Total": 233,
 "Items": [{
 "ID": 1,
 "PID": 3,
 "PRJCODE": "2018-001",           //项目编号 不可编辑
 "PRJNAME": "淡化海水配套泵站",         //项目名称 文本输入框
 "PRJTYPE": "基础设施",           //项目类型 下拉选项
 "JSUNIT": "投资公司",           //建设单位 文本输入框
 "FLOW_TYPE_CODE":"A02",           //流程分类 下拉选项,与数据库以code形式交互
 "DATE_START": "2018-12-1",          //开工时间 日期选择
 "DATE_END": "2019-12-1",          //竣工时间 日期选择
 "CONTENT": "建设淡化海水配套泵站一座,占地面积约8500平方米", //建设内容 多行输入框
 "INVEST_ALL": "1000"           //总投资 数字输入框
 }]
}

还有editTable.js的基本架子,$http是我为了方便在utils最后一行加入的 (angularjs用多了,习惯用\$http)

Vue.prototype.utils = utils
window.$http = axios

editTable.js :

var vm = new Vue({
 el: '#editTableCtrl',
 data: function() {
 return {
  loading: true,
  //表格的数据源
  dataList: [],
  // 列
  columnsList: [],
  // 增加编辑状态, 保存状态, 用于操作数据 避免干扰原数据渲染
  cloneDataList: []
 }
 },
 methods: {
 getData: function() {
  var self = this;
  self.loading = true;
  $http.get('json/editTable.txt').then(function(res) {
  self.dataList = res.data.Items;
  self.loading = false;
  });
 },
 },
 created: function() {
 this.getData();
 }
});

我们再来按照iview的规则编写渲染的列:

//...

   /**
    * @name columnsList (浏览器 渲染的列) 
    * @author catkin
    * @see https://www.iviewui.com/components/table
    * @param 
    * { 
    * titleHtml : 渲染带有html的表头 列: '资金<em class="blue" style="color:red">来源</em>'
    * editable : true,可编辑的列 必须有字段 
    * option  : 渲染的下拉框列表,如果需要与数据库交互的值与显示的值不同,须使用[{value:'value',label:'label'}]的形式,下面有例子
    * date   : 渲染成data类型 ,可选参数: 
    *       date | daterange: yyyy-MM-dd (默认)
    *       datetime | datetimerange: yyyy-MM-dd HH:mm:ss
    *       year: yyyy
    *       month: yyyy-MM
    * input   : 渲染input类型 ,可选参数为html5所有类型 (额外增加 textarea 属性), 默认text
    * handle  : 数组类型, 渲染操作方式,目前只支持 'edit', 'delete'
    * }
    * @version 0.0.1
    */

columnsList: [{
  width: 80,
  type: 'index',
  title: '序号',
  align: 'center'
}, {
  align: 'center',
  title: '项目编号',
  key: 'PRJCODE'
}, {
  align: 'center',
  title: '项目名称',
  titleHtml: '项目名称 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'PRJNAME',
  editable: true
}, {
  align: 'center',
  title: '项目分类',
  titleHtml: '项目分类 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'PRJTYPE',
  option: ['产业项目', '基础设施', '民生项目', '住宅项目'],
  editable: true
}, {
  align: 'center',
  title: '建设单位',
  titleHtml: '建设单位 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'JSUNIT',
  editable: true
}, {
  align: 'center',
  title: '流程分类',
  titleHtml: '流程分类 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'FLOW_TYPE_CODE',
  option: [{
   value: 'A01',
   label: '建筑-出让'
  }, {
   value: 'A02',
   label: '建筑-划拨'
  }, {
   value: 'B01',
   label: '市政-绿化'
  }, {
   value: 'B02',
   label: '市政-管线'
  }],
  editable: true
}, {
  align: 'center',
  title: '开工时间',
  titleHtml: '开工时间 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'DATE_START',
  //这里在后面处理的时候会分割成['month','yyyy-MM']的数组,分别代表iview的DatePicker组件选择日期的格式与数据库传过来时页面显示的格式
  date: 'month_yyyy-MM',  
  editable: true
}, {
  align: 'center',
  title: '竣工时间',
  titleHtml: '竣工时间 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'DATE_END',
  date: 'month_yyyy-MM',
  editable: true
}, {
  align: 'center',
  title: '建设内容',
  titleHtml: '建设内容 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'CONTENT',
  input: 'textarea',
  editable: true
}, {
  align: 'center',
  title: '总投资(万元)',
  titleHtml: '总投资<br />(万元) <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'INVEST_ALL',
  input: 'number',
  editable: true
}, {
  title: '操作',
  align: 'center',
  width: 150,
  key: 'handle',
  handle: ['edit', 'delete']
}]

//...

此时页面应该已经可以渲染出表格了

既然要编辑数据,并且我的需求是整行整行的编辑,而编辑的同时就会同步更新数据,那么问题来了
vue中, 数据更新,视图会随之更新. 想象一下,我在输入框中属于一个值,触发了数据更新,接着又触发了视图更新,那么我每次输入时,这个input都会失焦,毫无用户体验. 所以我们把可编辑的动态内容用cloneDataList渲染,静态显示的用dataList渲染
//...
self.dataList = res.data.Items;
// 简单的深拷贝,虽然map会返回新数组,但是数组元素也是引用类型,不能直接改,所以先深拷贝一份
self.cloneDataList = JSON.parse(JSON.stringify(self.dataList)).map(function(item) {
 // 给每行添加一个编辑状态 与 保存状态, 默认都是false
 item.editting = false;
 item.saving = false;
 return item;
});
//...

接下来,我们要根据columnsList做一次循环判断,根据相应的key写出不同的render函数

//全局添加
//根据value值找出数组中的对象元素
function findObjectInOption(value) {
 return function(item) {
 return item.value === value;
 }
}
//动态添加编辑按钮
var editButton = function(vm, h, currentRow, index) {
 return h('Button', {
 props: {
  size: 'small',
  type: currentRow.editting ? 'success' : 'primary',
  loading: currentRow.saving
 },
 style: {
  margin: '0 5px'
 },
 on: {
  click: function() {
  // 点击按钮时改变当前行的编辑状态, 当数据被更新时,render函数会再次执行,详情参考https://cn.vuejs.org/v2/api/#render
  // handleBackdata是用来删除当前行的editting属性与saving属性
  var tempData = vm.handleBackdata(currentRow)
  if (!currentRow.editting) {
   currentRow.editting = true;
  } else {
   // 这里也是简单的点击编辑后的数据与原始数据做对比,一致则不做操作,其实更好的应该遍历所有属性并判断
   if (JSON.stringify(tempData) == JSON.stringify(vm.dataList[index])) {
   console.log('未更改');
   return currentRow.editting = false;
   }
   vm.saveData(currentRow, index)
   currentRow.saving = true;
  }
  }
 }
 }, currentRow.editting ? '保存' : '编辑');
};
//动态添加 删除 按钮
var deleteButton = function(vm, h, currentRow, index) {
 return h('Poptip', {
  props: {
  confirm: true,
  title: currentRow.WRAPDATASTATUS != '删除' ? '您确定要删除这条数据吗?' : '您确定要对条数据撤销删除吗?',
  transfer: true,
  placement: 'left'
  },
  on: {
  'on-ok': function() {
   vm.deleteData(currentRow, index)
  }
  }
 },
 [
  h('Button', {
  style: {
   color: '#ed3f14',
   fontSize: '18px',
   padding: '2px 7px 0',
   border: 'none',
   outline: 'none',
   focus: {
   '-webkit-box-shadow': 'none',
   'box-shadow': 'none'
   }
  },
  domProps: {
   title: '删除'
  },
  props: {
   size: 'small',
   type: 'ghost',
   icon: 'android-delete',
   placement: 'left'
  }
  })
 ]);
};


//methods中添加
init: function() {
 console.log('init');
 var self = this;
 self.columnsList.forEach(function(item) {
 // 使用$set 可以触发视图更新
 // 如果含有titleHtml属性 将其值填入表头
 if (item.titleHtml) {
  self.$set(item, 'renderHeader', function(h, params) {
  return h('span', {
   domProps: {
   innerHTML: params.column.titleHtml
   }
  });
  });
 }
 // 如果含有操作属性 添加相应按钮
 if (item.handle) {
  item.render = function(h, param) {
  var currentRow = self.cloneDataList[param.index];
  var children = [];
  item.handle.forEach(function(item) {
   if (item === 'edit') {
   children.push(editButton(self, h, currentRow, param.index));
   } else if (item === 'delete') {
   children.push(deleteButton(self, h, currentRow, param.index));
   }
  });
  return h('div', children);
  };
 }
 //如果含有editable属性并且为true
 if (item.editable) {
  item.render = function(h, params) {
  var currentRow = self.cloneDataList[params.index];
  // 非编辑状态
  if (!currentRow.editting) {
   // 日期类型单独 渲染(利用工具暴力的formatDate格式化日期)
   if (item.date) {
   return h('span', self.utils.formatDate(currentRow[item.key], item.date.split('_')[1]))
   }
   // 下拉类型中value与label不一致时单独渲染
   if (item.option && self.utils.isArray(item.option)) {
   // 我这里为了简单的判断了第一个元素为object的情况,其实最好用every来判断所有元素
   if (typeof item.option[0] === 'object') {
    return h('span', item.option.find(findObjectInOption(currentRow[item.key])).label);
   }
   }
   return h('span', currentRow[item.key]);
  } else {
  // 编辑状态
   //如果含有option属性
   if (item.option && self.utils.isArray(item.option)) {
   return h('Select', {
    props: {
    // ***重点***: 这里要写currentRow[params.column.key],绑定的是cloneDataList里的数据
    value: currentRow[params.column.key]
    },
    on: {
    'on-change': function(value) {
     self.$set(currentRow, params.column.key, value)
    }
    }
   }, item.option.map(function(item) {
    return h('Option', {
    props: {
     value: item.value || item,
     label: item.label || item
    }
    }, item.label || item);
   }));
   } else if (item.date) {
   //如果含有date属性
   return h('DatePicker', {
    props: {
    type: item.date.split('_')[0] || 'date',
    clearable: false,
    value: currentRow[params.column.key]
    },
    on: {
    'on-change': function(value) {
     self.$set(currentRow, params.column.key, value)
    }
    }
   });
   } else {
   // 默认input
   return h('Input', {
    props: {
    // type类型也是自定的属性
    type: item.input || 'text',
    // rows只有在input 为textarea时才会起作用
    rows: 3,
    value: currentRow[params.column.key]
    },
    on: {
    'on-change'(event) {
     self.$set(currentRow, params.column.key, event.target.value)
    }
    }
   });
   }
  }
  };
 }
 });
},
// 还原数据,用来与原始数据作对比的
handleBackdata: function(object) {
 var clonedData = JSON.parse(JSON.stringify(object));
 delete clonedData.editting;
 delete clonedData.saving;
 return clonedData;
}

到这里完成已经差不多了,补上保存数据与删除数据的函数

// 保存数据
saveData: function(currentRow, index) {
 var self = this;
 // 修改当前的原始数据, 就不需要再从服务端获取了
 this.$set(this.dataList, index, this.handleBackdata(currentRow))
 // 需要保存的数据
 // 模拟ajax
 setTimeout(function() {
 充值编辑与保存状态
 currentRow.saving = false;
 currentRow.editting = false;
 self.$Message.success('保存完成');
 console.log(self.dataList);
 }, 1000)
},
// 删除数据
deleteData: function(currentRow, index) {
 var self = this;
 console.log(currentRow.ID);
 setTimeout(function() {
 self.$delete(self.dataList, index)
 self.$delete(self.cloneDataList, index)
 vm.$Message.success('删除成功');
 }, 1000)
},

完整的editTable.js代码

// 根据数据中下拉的值找到对应的对象
function findObjectInOption(name) {
 return function(item) {
 return item.value === name;
 }
}
var editButton = function(vm, h, currentRow, index) {
 return h('Button', {
 props: {
  size: 'small',
  type: currentRow.editting ? 'success' : 'primary',
  loading: currentRow.saving
 },
 style: {
  margin: '0 5px'
 },
 on: {
  click: function() {
   // 点击按钮时改变当前行的编辑状态,当数据被更新时,render函数会再次执行,详情参考https://cn.vuejs.org/v2/api/#render
   // handleBackdata是用来删除当前行的editting属性与saving属性
   var tempData = vm.handleBackdata(currentRow)
   if (!currentRow.editting) {
   currentRow.editting = true;
   } else {
   // 这里也是简单的点击编辑后的数据与原始数据做对比,一致则不做操作,其实更好的应该遍历所有属性并判断
   if (JSON.stringify(tempData) == JSON.stringify(vm.dataList[index])) {
    console.log('未更改');
    return currentRow.editting = false;
   }
   vm.saveData(currentRow, index)
   currentRow.saving = true;
   }
  }
 }
 }, currentRow.editting ? '保存' : '编辑');
};
//动态添加 删除 按钮
var deleteButton = function(vm, h, currentRow, index) {
 return h('Poptip', {
  props: {
  confirm: true,
  title: currentRow.WRAPDATASTATUS != '删除' ? '您确定要删除这条数据吗?' : '您确定要对条数据撤销删除吗?',
  transfer: true,
  placement: 'left'
  },
  on: {
  'on-ok': function() {
   vm.deleteData(currentRow, index)
  }
  }
 },
 [
  h('Button', {
  style: {
   color: '#ed3f14',
   fontSize: '18px',
   padding: '2px 7px 0',
   border: 'none',
   outline: 'none',
   focus: {
   '-webkit-box-shadow': 'none',
   'box-shadow': 'none'
   }
  },
  domProps: {
   title: '删除'
  },
  props: {
   size: 'small',
   type: 'ghost',
   icon: 'android-delete',
   placement: 'left'
  }
  })
 ]);
};
var vm = new Vue({
 el: '#editTableCtrl',
 data: function() {
 return {
  loading: true,
  //表格的数据源
  dataList: [],
  /**
  * @name columnsList (浏览器 渲染的列) 
  * @author ch
  * @see https://www.iviewui.com/components/table
  * @param 
  * { 
  * titleHtml : 渲染带有html的表头 列: '资金<em class="blue" style="color:red">来源</em>'
  * editable : true,可编辑的列 必须有字段 
  * option : 渲染的下拉框列表
  * date  : 渲染成data类型 ,可选参数: 
  *    date | daterange: yyyy-MM-dd (默认)
  *    datetime | datetimerange: yyyy-MM-dd HH:mm:ss
  *    year: yyyy
  *    month: yyyy-MM
  * input  : 渲染input类型 ,可选参数为html5所有类型 (额外增加 textarea 属性), 默认text
  * handle : 数组类型, 渲染操作方式,目前只支持 'edit', 'delete'
  * }
  * @version 0.0.1
  */
  columnsList: [{
  width: 80,
  type: 'index',
  title: '序号',
  align: 'center'
  }, {
  align: 'center',
  title: '项目编号',
  key: 'PRJCODE'
  }, {
  align: 'center',
  title: '项目名称',
  titleHtml: '项目名称 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'PRJNAME',
  editable: true
  }, {
  align: 'center',
  title: '项目分类',
  titleHtml: '项目分类 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'PRJTYPE',
  option: ['产业项目', '基础设施', '民生项目', '住宅项目'],
  editable: true
  }, {
  align: 'center',
  title: '建设单位',
  titleHtml: '建设单位 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'JSUNIT',
  editable: true
  }, {
  align: 'center',
  title: '流程分类',
  titleHtml: '流程分类 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'FLOW_TYPE_CODE',
  option: [{
   value: 'A01',
   label: '建筑-出让'
  }, {
   value: 'A02',
   label: '建筑-划拨'
  }, {
   value: 'B01',
   label: '市政-绿化'
  }, {
   value: 'B02',
   label: '市政-管线'
  }],
  editable: true
  }, {
  align: 'center',
  title: '开工时间',
  titleHtml: '开工时间 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'DATE_START',
  //这里在后面处理的时候会分割成['month','yyyy-MM']的数组,分别代表iview的DatePicker组件选择日期的格式与数据库传过来时页面显示的格式
  date: 'month_yyyy-MM',
  editable: true
  }, {
  align: 'center',
  title: '竣工时间',
  titleHtml: '竣工时间 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'DATE_END',
  date: 'month_yyyy-MM',
  editable: true
  }, {
  align: 'center',
  title: '建设内容',
  titleHtml: '建设内容 <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'CONTENT',
  input: 'textarea',
  editable: true
  }, {
  align: 'center',
  title: '总投资(万元)',
  titleHtml: '总投资<br />(万元) <i class="ivu-icon ivu-icon-edit"></i>',
  key: 'INVEST_ALL',
  input: 'number',
  editable: true
  }, {
  title: '操作',
  align: 'center',
  width: 150,
  key: 'handle',
  handle: ['edit', 'delete']
  }],
  // 增加编辑状态, 保存状态, 用于操作数据 避免干扰原数据渲染
  cloneDataList: []
 }
 },
 methods: {
 getData: function() {
  var self = this;
  self.loading = true;
  $http.get('json/editTable.txt').then(function(res) {
  // 给每行添加一个编辑状态 与 保存状态
  self.dataList = res.data.Items;
  self.cloneDataList = JSON.parse(JSON.stringify(self.dataList)).map(function(item) {
   item.editting = false;
   item.saving = false;
   return item;
  });
  self.loading = false;
  });
 },
 //初始化数据
 //methods中添加
 init: function() {
  console.log('init');
  var self = this;
  self.columnsList.forEach(function(item) {
  // 使用$set 可以触发视图更新
  // 如果含有titleHtml属性 将其值填入表头
  if (item.titleHtml) {
   self.$set(item, 'renderHeader', function(h, params) {
   return h('span', {
    domProps: {
    innerHTML: params.column.titleHtml
    }
   });
   });
  }
  // 如果含有操作属性 添加相应按钮
  if (item.handle) {
   item.render = function(h, param) {
   var currentRow = self.cloneDataList[param.index];
   var children = [];
   item.handle.forEach(function(item) {
    if (item === 'edit') {
    children.push(editButton(self, h, currentRow, param.index));
    } else if (item === 'delete') {
    children.push(deleteButton(self, h, currentRow, param.index));
    }
   });
   return h('div', children);
   };
  }
  //如果含有editable属性并且为true
  if (item.editable) {
   item.render = function(h, params) {
   var currentRow = self.cloneDataList[params.index];
   // 非编辑状态
   if (!currentRow.editting) {
    // 日期类型单独 渲染(利用工具暴力的formatDate格式化日期)
    if (item.date) {
    return h('span', self.utils.formatDate(currentRow[item.key], item.date.split('_')[1]))
    }
    // 下拉类型中value与label不一致时单独渲染
    if (item.option && self.utils.isArray(item.option)) {
    // 我这里为了简单的判断了第一个元素为object的情况,其实最好用every来判断所有元素
    if (typeof item.option[0] === 'object') {
     return h('span', item.option.find(findObjectInOption(currentRow[item.key])).label);
    }
    }
    return h('span', currentRow[item.key]);
   } else {
    // 编辑状态
    //如果含有option属性
    if (item.option && self.utils.isArray(item.option)) {
    return h('Select', {
     props: {
     // ***重点***: 这里要写currentRow[params.column.key],绑定的是cloneDataList里的数据
     value: currentRow[params.column.key]
     },
     on: {
     'on-change': function(value) {
      self.$set(currentRow, params.column.key, value)
     }
     }
    }, item.option.map(function(item) {
     return h('Option', {
     props: {
      value: item.value || item,
      label: item.label || item
     }
     }, item.label || item);
    }));
    } else if (item.date) {
    //如果含有date属性
    return h('DatePicker', {
     props: {
     type: item.date.split('_')[0] || 'date',
     clearable: false,
     value: currentRow[params.column.key]
     },
     on: {
     'on-change': function(value) {
      self.$set(currentRow, params.column.key, value)
     }
     }
    });
    } else {
    // 默认input
    return h('Input', {
     props: {
     // type类型也是自定的属性
     type: item.input || 'text',
     // rows只有在input 为textarea时才会起作用
     rows: 3,
     value: currentRow[params.column.key]
     },
     on: {
     'on-change'(event) {
      self.$set(currentRow, params.column.key, event.target.value)
     }
     }
    });
    }
   }
   };
  }
  });
 },
 saveData: function(currentRow, index) {
  var self = this;
  // 修改当前的原始数据, 就不需要再从服务端获取了
  this.$set(this.dataList, index, this.handleBackdata(currentRow))
  // 需要保存的数据
  // 模拟ajax
  setTimeout(function() {
  // 重置编辑与保存状态
  currentRow.saving = false;
  currentRow.editting = false;
  self.$Message.success('保存完成');
  console.log(self.dataList);
  }, 1000)
 },
 // 删除数据
 deleteData: function(currentRow, index) {
  var self = this;
  console.log(currentRow.ID);
  setTimeout(function() {
  self.$delete(self.dataList, index)
  self.$delete(self.cloneDataList, index)
  vm.$Message.success('删除成功');
  }, 1000)
 },
 // 还原数据,用来与原始数据作对比的
 handleBackdata: function(object) {
  var clonedData = JSON.parse(JSON.stringify(object));
  delete clonedData.editting;
  delete clonedData.saving;
  return clonedData;
 }
 },
 created: function() {
 this.getData();
 this.init();
 }
});

总结

两三天的时间搞的这些,刚开始也是各种懵逼.期间也试过用插槽来实现,但还是没有这个方法来的清晰(队友都能看懂), 总的来说就是在columnsList自定义一些属性,后面根据这些属性,在render函数里return不同的值,思路还是很简单的.

第一次写文章,并且我也是vue初学者,写的凑合看吧 ^_^ ,欢迎留言指正

本demo 连同之前学习vue时写的demo一起放在的我的github上,欢迎star...

github: https://github.com/catkinmu/vue.js-practice/tree/master/IViewEditTable

参考

vue: render文档
iview
iview admin1.x 版本

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

Javascript 相关文章推荐
JS面向对象编程之对象使用分析
Aug 19 Javascript
javascript hashtable 修正版 下载
Dec 30 Javascript
Jquery Ajax xmlhttp请求成功问题
Feb 04 Javascript
基于jquery实现人物头像跟随鼠标转动
Aug 23 Javascript
jQuery插件zTree实现更新根节点中第i个节点名称的方法示例
Mar 08 Javascript
微信小程序 监听手势滑动切换页面实例详解
Jun 15 Javascript
echart简介_动力节点Java学院整理
Aug 11 Javascript
微信小程序scroll-x失效的完美解决方法
Jul 18 Javascript
JavaScript Canvas实现验证码
Aug 02 Javascript
elementUI select组件value值注意事项详解
May 29 Javascript
vue视频播放暂停代码
Nov 08 Javascript
解决Vue-cli无法编译es6的问题
Oct 30 Javascript
详解vue 项目白屏解决方案
Oct 31 #Javascript
微信小程序ibeacon三点定位详解
Oct 31 #Javascript
小程序获取周围IBeacon设备的方法
Oct 31 #Javascript
详解js删除数组中的指定元素
Oct 31 #Javascript
Vue中使用方法、计算属性或观察者的方法实例详解
Oct 31 #Javascript
Vue项目History模式404问题解决方法
Oct 31 #Javascript
Vue.js中对css的操作(修改)具体方式详解
Oct 30 #Javascript
You might like
php获取网页上所有链接的方法
2015/04/03 PHP
php限制文件下载速度的代码
2015/10/20 PHP
php实现的操作excel类详解
2016/01/15 PHP
PHP5.0~5.6 各版本兼容性cURL文件上传功能实例分析
2018/05/11 PHP
Laravel框架实现修改登录和注册接口数据返回格式的方法
2018/08/17 PHP
PHP封装的分页类与简单用法示例
2019/02/25 PHP
JQuery 引发两次$(document.ready)事件
2010/01/15 Javascript
jQuery 文本框得失焦点的简单实例
2014/02/19 Javascript
jQuery.holdReady()使用方法
2014/05/20 Javascript
jQuery中:first-child选择器用法实例
2014/12/31 Javascript
js制作简易年历完整实例
2015/01/28 Javascript
jquery.gridrotator实现响应式图片展示画廊效果
2015/06/23 Javascript
jQuery实现的仿select功能代码
2015/08/19 Javascript
Bootstrap基本插件学习笔记之轮播幻灯片(23)
2016/12/08 Javascript
vue.js中指令Directives详解
2017/03/20 Javascript
再谈Angular4 脏值检测(性能优化)
2018/04/23 Javascript
微信小程序异步API为Promise简化异步编程的操作方法
2018/08/14 Javascript
浅谈express.js框架中间件(middleware)
2019/04/07 Javascript
vue跳转同一个组件,参数不同,页面接收值只接收一次的解决方法
2019/11/05 Javascript
[01:20:30]OG vs LGD 2018国际邀请赛淘汰赛BO3 第四场 8.26
2018/08/30 DOTA
[01:21]DOTA2 新英雄 森海飞霞
2020/12/18 DOTA
Python中的with...as用法介绍
2015/05/28 Python
Python爬取国外天气预报网站的方法
2015/07/10 Python
Python获取SQLite查询结果表列名的方法
2017/06/21 Python
对python中raw_input()和input()的用法详解
2018/04/22 Python
PyQt5内嵌浏览器注入JavaScript脚本实现自动化操作的代码实例
2019/02/13 Python
Python实现字典按key或者value进行排序操作示例【sorted】
2019/05/03 Python
Django网络框架之创建虚拟开发环境操作示例
2019/06/06 Python
详解python中自定义超时异常的几种方法
2019/07/29 Python
基于Python测试程序是否有错误
2020/05/16 Python
Django实现后台上传并显示图片功能
2020/05/29 Python
80年代复古T恤:TruffleShuffle
2018/07/02 全球购物
会计求职信范文
2014/05/24 职场文书
公司仓管员岗位职责
2015/04/01 职场文书
2015年社区矫正工作总结
2015/04/21 职场文书
PHP对接阿里云虚拟号的实现(号码隐私保护)
2021/04/06 PHP