关于table:什么都2023年了你居然还不会table表头单元格合并附eltable单行多级表头多个案例

问题形容本文记录el-table表头合并的多种状况,并提出对应解决方案,预计能帮到局部道友 原生table知识点温习咱们晓得:一个简略的table表格个别由一个或多个tr、th或td标签组成(嵌套)tr标签定义表格行(table-row即为tr)th标签定义表头(table-header即为th)td标签定义表格单元格再简单的表格还包含 caption、col、colgroup、thead、tfoot、tbody等标签,这里暂不延长而合并单元格次要应用的是colspan和rouspan属性,即为可设置横跨列和横跨行的值 而合并单元格次要应用的是colspan和rouspan属性,即为可设置横跨列和横跨行的值 而合并单元格次要应用的是colspan和rouspan属性,即为可设置横跨列和横跨行的值 晓得这两个属性当前,咱们联合一个具体demo来看,就很好的了解了 原生表格demo假如咱们须要做一个周一到周末的表格,记录一下工作内容,如下效果图: 对应代码是这样的: <table border="1"> <tr> <th>工作日</th> <th>工作日</th> <th>工作日</th> <th>工作日</th> <th>工作日</th> <th>周末</th> <th>周末</th> </tr> <tr> <th>周一</th> <th>周二</th> <th>周三</th> <th>周四</th> <th>周五</th> <th>周六</th> <th>周日</th> </tr> <tr> <td>下班</td> <td>下班</td> <td>下班</td> <td>下班</td> <td>下班</td> <td>加班</td> <td>加班</td> </tr></table>然而,咱们想把五个工作日和两个周末进行合并一下,这样看起来更加优雅一些,如下需要 由图示,咱们晓得第一行的工作日一共有5个,咱们先把后4个工作日隐去使其隐没,再让第一个工作日横跨5个单元格宽度,即横跨5列(本来默认都是横跨1列)先隐去后四个工作日单元格 <tr> <th>工作日</th> <th style="display: none;">工作日</th> <th style="display: none;">工作日</th> <th style="display: none;">工作日</th> <th style="display: none;">工作日</th> <th>周末</th> <th>周末</th></tr>变成这样的成果了 这样的话,咱们只须要,再让第一个工作日单元格宽度变宽一些,占据五个单元格,这样的话,宽度有了,就把两个周末挤回到原来失常的地位了那,如何让第一个工作日单元格,占据五个单元格的宽度呢?或者说,如何让一个单元格横跨5列?很简略:<th colspan="5">工作日</th>即可rowspan同理,不赘述需要实现效果图: 对应需要实现代码: <table border="1"> <tr> <th colspan="5">工作日</th> <th style="display: none;">工作日</th> <th style="display: none;">工作日</th> <th style="display: none;">工作日</th> <th style="display: none;">工作日</th> <th colspan="2">周末</th> <th style="display: none;">周末</th> </tr> <tr> <th>周一</th> <th>周二</th> <th>周三</th> <th>周四</th> <th>周五</th> <th>周六</th> <th>周日</th> </tr> <tr> <td>下班</td> <td>下班</td> <td>下班</td> <td>下班</td> <td>下班</td> <td>加班</td> <td>加班</td> </tr></table>所以,通过下面的demo,咱们能够得出一个论断: ...

April 17, 2023 · 5 min · jiezi

关于table:eltable-分页全选功能讲解

明天和大家一起学习一个 el-table 分页全选的性能 咱们在用 el-table 组件的时候,必定会用到分页性能,不论是长列表下拉分页还是用 element-UI 的 el-pagination 分页。 然而咱们在具备抉择性能的 el-table 的时候,会遇到一个问题,在点击进行分页之后,之前抉择的数据没了,这个问题真的是困扰我好久。 在前面的我又开始面向百度开发,找到一个没有 bug 的办法这个办法是: 在 el-table 中,通过 @selection-change=“handleRowSelection” 和 :row-key=“getRowKeys”, 在第一行,也就是多选框的那一列上,加上 :reserve-selection="true", 间接上代码,仅供大家参考。 <template> <div> <el-table ref="table" :data="tableData" size="small" height="100%"row-key=“id” @selection-change="handleSelectChange" @select="handleSelect" > <el-table-column width="50" type="selection" /> <el-table-column type="index" label="序号" width="50"> <template scope="scope"> <span>{{ (pageInfo.pageNo - 1) * pageInfo.pageSize + scope.$index + 1 }}</span> </template> </el-table-column> <el-table-column label="名称1" /> <el-table-column label="名称2" /> <el-table-column label="名称3" /> <el-table-column label="名称4" /> <el-table-column label="名称5" /> </el-table> </div></template><script>export default { data() { return { tableData: [], selectedObj: {}, selectedData: [] } }, methods: { getList() { // 查数据的中央,解决分页选中状态 this.handleRowSelection(this.tableData) }, handleSelectChange(selection) { // 全选勾销,删除当前页所有数据 if (selection.length === 0) { this.tableData.forEach(item => { delete this.selectedObj[item.id] }) } // 勾选数据 增加 selection.forEach(item => { this.selectedObj[item.id] = item }) // 获取所有分页勾选的数据 this.selectedData = [] for (const key in this.selectedObj) { this.selectedData.push(this.selectedObj[key]) } }, handleSelect(selection, row) { // 勾销单个勾选时,删除对应属性 if (!selection.some(item => item.id === row.id)) { delete this.selectedObj[row.id] } }, // 解决以后列表选中状态 handleRowSelection(data) { data.forEach(item => { if (this.selectedObj[item.id]) { this.$nextTick(() => { this.$refs.table.toggleRowSelection(item) }) } }) } }}</script>源码附件曾经打包好上传到百度云了,大家自行下载即可~ ...

June 20, 2022 · 1 min · jiezi

关于table:vxetable-实现横向树列表

开发过程中,很多产品经理都喜爱做些花里胡哨的表格展现,以下是一种常见的表格展现形式,看上去如同很简单,实现起来很简略,上面看看代码怎么实现 组件实现次要围绕vxe-talble-v3,这款基于vue的表格框架能节俭很大的功夫 template <template> <vxe-table border height="600" :scroll-y="{ enabled: false }" :span-method="rowspanMethod" :data="tableData" align="center" > <vxe-column field="sort" width="150" title="序号"></vxe-column> <vxe-column field="type" width="250" title="类型" :formatter="formatterType"> </vxe-column> <vxe-colgroup title="验收我的项目" v-if="deepLevel>0"> <vxe-column v-for="(item,idx) in deepLevel" :key="idx" :field="`name${item}`" header-class-name="header"></vxe-column> </vxe-colgroup> <vxe-column field="content" type="html" width="350" title="设计规范要求及标准规定"> <template #default="{ row }"> <div class="cellContent" v-if="XEUtils.isArray(row.content)"> <p @click="cellLink(item)" v-for="(item,idx) in row.content" :key="idx"><i class="el-icon-paperclip icon"></i><span class="link">{{item.provisions_info}}</span></p> </div> <span v-else>{{row.content}}</span> </template> </vxe-column> </vxe-table></template>js import XEUtils from "xe-utils";export default { data() { return { tableData: [], acceptance_item: [], deepLevel:0, XEUtils:XEUtils }; }, created() { setTimeout(() => { // 获取api数据 this.acceptance_item = this.getData(); this.acceptance_item.forEach((item,idx)=>{ item.sort = idx+1+'' }) let that = this; // 遍历api树形构造层数,标记最深层 function arrLevel(arr,lv){ if(!arr || !arr.length) return; arr.forEach(item=>{ that.deepLevel = lv>that.deepLevel?lv:that.deepLevel; if(item.children && item.children.length){ arrLevel(item.children,lv+1) } }) } // 标记最深层赋值,vxetable在渲染进来 arrLevel(this.acceptance_item,1) console.log(this.deepLevel) this.toColTreeData(this.acceptance_item) }, 2000); }, methods: { // 将一般树结构转换为横向树列表 toColTreeData(treeData) { const options = { children: "children" }; const list = []; const keyMap = {}; XEUtils.eachTree( treeData, (item, index, result, paths, parent) => { item, index, result, paths, parent; keyMap[item.uid] = item; item.keys = parent ? parent.keys.concat([item.uid]) : [item.uid]; if (!item.children || !item.children.length) { const row = {}; item.keys.forEach((key, index) => { const level = index + 1; const obj = keyMap[key]; row[`id${level}`] = obj.uid; // 第1层赋值序号和类型 if(level==1){ row.sort = obj.sort row.type = obj.type } // content外面层会笼罩里面层 let content = obj.content?obj.content:obj.provisions row.content = content; // name遍历整个构造的所有name row[`name${level}`] = obj.name }); list.push(row); } }, options ); this.keyMap = keyMap; console.log(list) this.tableData = list }, formatterType ({ cellValue }) { return cellValue==1?"主控我的项目": cellValue == 2? "个别我的项目":"" }, cellLink(e){ debugger }, // 通用行合并函数(将雷同多列数据合并为一行) rowspanMethod({ row, _rowIndex, column,_columnIndex, visibleData }) { let that = this; let arr = [] for(let i=0;i<this.deepLevel;i++){ arr.push(`name${i+1}`) } const fields =[...arr,...["type","sort"]]; const cellValue = row[column.property]; if (cellValue && fields.includes(column.property)) { const prevRow = visibleData[_rowIndex - 1]; let nextRow = visibleData[_rowIndex + 1]; if (prevRow && prevRow[column.property] === cellValue) { return { rowspan: 0, colspan: 0 }; } else { let countRowspan = 1; while (nextRow && nextRow[column.property] === cellValue) { nextRow = visibleData[++countRowspan + _rowIndex]; } if (countRowspan > 1) { return { rowspan: countRowspan, colspan: 1 }; } } } // 用point标记name+'n' let point = '' Object.keys(row).forEach(item=>{ if(item.indexOf('name')>-1 && item != `name${that.deepLevel}`){ point = item; } }) // point 与 column.property 相等时,操作以后单元格横移拉伸 if(point == column.property && !row[`name${that.deepLevel}`]){ return { rowspan: 1, colspan: this.deepLevel - ( parseInt(point.split('name')[1]) - 1 ) } } // 将undefined的单元格暗藏 if(cellValue==undefined){ return { rowspan: 0, colspan: 0 } } }, getData(){ const data = [{ id: 10, uid: "317218091849220096", puid: "0", type: 1, name: "1", content: "钢筋", provisions: [ { provisions_id: "", provisions_info: "", specification_id: "", }, ], children: [ { id: 9, uid: "317218091849220097", puid: "317218091849220096", type: 0, name: "1.1", content: "", provisions: [ { provisions_id: "1", provisions_info: "钢筋", specification_id: "1", }, { provisions_id: "2", provisions_info: "混凝土", specification_id: "1", }, ], children: [], }, { id: 9, uid: "317218091849220200", puid: "317218091849220096", type: 0, name: "1.2", content: "", provisions: [ { provisions_id: "3", provisions_info: "钢筋1", specification_id: "1", }, { provisions_id: "4", provisions_info: "混凝土1", specification_id: "1", }, ], children: [], }, { id: 10, uid: "317218091849220201", puid: "317218091849220096", type: 0, name: "1.3", content: "", provisions: [ { provisions_id: "5", provisions_info: "钢筋2", specification_id: "1", }, { provisions_id: "6", provisions_info: "混凝土2", specification_id: "1", }, ], children: [ // { // id: 11, // uid: "317218091849220202", // puid: "317218091849220201", // type: 0, // name: "1.3.1", // content: "个别323", // provisions: [ // { // provisions_id: "", // provisions_info: "", // specification_id: "", // }, // ], // children: [], // }, ], }, ], }, { id: 13, uid: "317218091849220098", puid: "0", type: 2, name: "2", content: "", provisions: [ { provisions_id: "", provisions_info: "", specification_id: "", }, ], children: [ { id: 12, uid: "317218091849220099", puid: "317218091849220098", type: 0, name: "2.1", content: "", provisions: [ { provisions_id: "", provisions_info: "", specification_id: "", }, ], children: [ { id: 11, uid: "317218091849220100", puid: "317218091849220099", type: 0, name: "2.1.1", content: "个别", provisions: [ { provisions_id: "", provisions_info: "", specification_id: "", }, ], children: [], }, { id: 11, uid: "317218091849220101", puid: "317218091849220099", type: 0, name: "2.1.2", content: "个别", provisions: [ { provisions_id: "", provisions_info: "", specification_id: "", }, ], children: [], }, ], }, ], }] return data } },};scss ...

January 3, 2022 · 4 min · jiezi

关于table:table的宽高属性不支持小数渲染-奇葩

概述事件产生在半个月前,过后正在开发一个Table组件。Table的body应用div作为单元格渲染进去的,header局部应用原生table标签渲染的,后果宽度适应的时候,header 和body 总是无奈同步对齐,有几个像素的误差。最初发现了是浏览器渲染table相干元素的时候,不反对小数渲染。 剖析起因先写个demo <div class="stage"> <table class="table"> <tr> <td>Content</td> </tr> </table></div><style>* { padding: 0; margin: 0;}.stage { width: 100.5px; height: 100px; background-color: red;}.table { width: 100.5px; background-color: blue;}</style>chrome浏览器成果如下: 其实只有是浮点数,间接都会被截断,所以设置 100px的成果和设置100.1px、100.5px、100.9px都是一样的。 这就真的不能忍了,间接求助网络,发现相干材料非常少,官网也没有相干阐明,查到不少起因,比方: 浏览器最小渲染单位。这个显著很没有说服力,尽管浏览器确认存在最小渲染单位,然而无法解释 div 能渲染出浮点数,而table不行。stackoverflow有议论这个根本原因到底是什么,我也没彻底搞懂。不过,无论什么起因,必须先得拿出一套解决方案,工期不能误呀! 解决方案本人推敲了一个兼容计划,过后也是眉头一皱;计上心来,想到了一个解决形式。 想法非常简单,就是单元格宽度先向下取整,平均分配,这样理论累计的宽度 必定比 现实的小一点,而后把差进去的再依照索引进行调配,这样尽管有的单元格宽了1px,然而不影响视觉效果。 想到这个计划之后,发现这个解决形式和之前解决的如何保障 一组数据百分比之和为100%是一样的。 /** * @param {*} baseWidth 冀望宽度 * @param {*} cols 列数 * @returns 每列宽度的汇合 */function adjustWidth(baseWidth, cols) { const cellWidth = Math.floor(baseWidth / cols); // 计算基准单元格width const offset = baseWidth - cellWidth * cols; // 偏差值 const results = new Array(cols).fill(cellWidth); // 如果有冗余,再次调配 for (let i = 0; i < offset; i++) { results[i] = cellWidth + 1; } return results;}adjustWidth(1000, 3); // [ 334, 333, 333 ]

May 21, 2021 · 1 min · jiezi

关于table:antdvue-列表tablelist自定义缺省ui

一、antd-vue官网给出的空数据的参数也给出了例子,emptyText: '暂无数据'。如果你只须要改文案,依照这个形式即可。 但在理论场景中,咱们不仅须要自定义文案,更心愿有其余更多的自定义内容,比方缺省图二、更多个性化设置官网文档给的太过简洁,其实 emptyText 不仅能够传文字,也能够传组件!也就是说你能够自定义一个缺省的组件,依然通过 emptyText 传进去就行 看上面的例子:1、失常封装一个Empty组件(款式本人依据须要调整) 2、table 组件 的 locale 参数传入 组件引入: 值得说的是,Empty在引入之后,是不须要在components中注册的!来看一下比照成果,默认的: 自定义的: list 组件也是同样的办法。有了这个办法当前,怎么自定义都能够啦~

March 26, 2021 · 1 min · jiezi

关于table:elementui表格组件table实现列的动态显示与隐藏

前言在开发后盾管理系统中,表格是常常用到的数据展现形式。然而,有时候表格展现的列过多,会呈现一屏展现不下,须要手动滚动滚动条查看的状况。 其实,在零碎理论应用的过程中,不同用户关注的列不同,并不是肯定要展现所有的列。因而,能够开发一个可能配置表格须要展现的列的性能,这样只展现关注的列,不关注的列就无需在页面上展现,进步用户的应用体验。 需要要求能够动静配置表格中须要展现的列,默认展现所有列。 思路将列数据定义在data中,而后动静绑定列数据到模板上提供一组用于设置列展现的复选框,默认选中所有列,即默认展现所有列选中或勾销选中某个列时,更新模板中动静绑定的列数据,实现页面只显示须要展现的列开发状态首先依据思路,能够确定几个状态: tableData 表格数据tableColumns 表格所有列数据,列数据的惟一起源bindTableColumns 绑定到模板上的列数据,即须要展现的列数据checkedTableColumns 复选框中选中的列数据实现而后开始实现: 首先,写一个根底的表格。(参考element-ui根底表格) <template> <el-table :data="tableData"> <el-table-column prop="date" label="日期"> </el-table-column> <el-table-column prop="name" label="姓名"> </el-table-column> <el-table-column prop="address" label="地址"> </el-table-column> </el-table></template><script>export default { data() { return { tableData: [ { date: "2016-05-02", name: "王小虎", address: "上海市普陀区金沙江路 1518 弄", }, { date: "2016-05-04", name: "王小虎", address: "上海市普陀区金沙江路 1517 弄", }, { date: "2016-05-01", name: "王小虎", address: "上海市普陀区金沙江路 1519 弄", } ], }; },};</script>而后,优化模板。 将表格的列数据tableColumns定义在data中,在模板中通过v-for指令遍历列数据。 <template> <el-table :data="tableData"> <el-table-column v-for="column in tableColumns" :key="column.prop" :prop="column.prop" :label="column.label" > </el-table-column> </el-table></template><script>export default { data() { return { tableColumns: [ { prop: "date", label: "日期", }, { prop: "name", label: "姓名", }, { prop: "address", label: "地址", }, ], }; },};</script>其次,优化列数据tableColumns,给每一个column增加show属性,用于示意列的显示与暗藏。默认为true,即默认展现所有列。 ...

January 24, 2021 · 2 min · jiezi

elementUI-动态生成几行几列-table2

elementUI 动态生成几行几列 table现在碰到一个需求:就是根据用户选择的行列,来自动生成相应大小的 table,如下这个实现还不完善,因为数据不对,只是实现了动态的效果,仅是提供一种实现思路吧,后续我会再想想看怎么实现为好,先记录一下吧直接看代码吧<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>elementUI table 动态生成列</title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script src="https://unpkg.com/element-ui/lib/index.js"></script> <style type="text/css"> @import url("https://unpkg.com/element-ui/lib/theme-chalk/index.css"); </style></head><body><div id="app"> <el-form inline> <!--先选择 排数--> <el-form-item label="请选择排" style="margin-left: 50px;"> <el-select style="width: 100% ;" v-model="row1" placeholder="请选择排" @change="row1Change"> <el-option v-for="item in floorNumList" :key="item.floorId" :value="item.floorId"></el-option> </el-select> </el-form-item> <!--再选择 列数--> <el-form-item label="请选择列"> <el-select style="width: 100% ;" v-model="col1" placeholder="请选择列" @change="col1Change"> <el-option v-for="item in floorNumList" :key="item.floorId" :value="item.floorId"></el-option> </el-select> </el-form-item> <el-table ref="multipleTable" :data="rowDataList1" style="width:80%; border: 2px solid red; max-height: 500px; margin-left: 30px;" highlight-current-row :cell-style="cellStyle"> <el-table-column fixed type="selection" align="center" width="50" label="列"></el-table-column><!-- <el-table-column type="index" align="center" width="50" label="索引"></el-table-column>--> <el-table-column v-for="col in colDataList1" :prop="col.id" :label="col.id" align="center" > <el-table-column prop="id" align="center" > <template slot-scope="scope"> <el-button @click="handleClick(scope.row, col.id, scope.$index)" class="el-icon-cherry" v-bind:style="{ color: activeColor}">></el-button> </template> </el-table-column> </el-table-column> </el-table> </el-form> </div></div><script> let vm = new Vue({ el: '#app', data(){ return{ floorNumList: [ {floorId: 1}, {floorId: 2}, {floorId: 3}, {floorId: 4}, {floorId: 5}, {floorId: 6}, {floorId: 7}, {floorId: 8}, {floorId: 9}, {floorId: 10} ], floorNum: '', // 第1层 默认选择的排数 和 列数 row1: 1, col1: 1, // 第2层 默认选择的排数 和 列数 row2: 1, col2: 1, // 第3层 默认选择的排数 和 列数 row3: 1, col3: 1, // 第4层 默认选择的排数 和 列数 row4: 1, col4: 1, // 第5层 默认选择的排数 和 列数 row5: 1, col5: 1, activeColor: 'green', colPos: '', rowPos: '', rowDataList1: [{ // 默认给一个对象,即默认状态是 1行数据 id: Math.ceil(Math.random()*100) }], colDataList1: [ {id: '1'} ], } }, methods:{ col1Change(){ // 每触发一次,清空数据 this.colDataList1 = [{id: '1'}]; // 取得 选中的第一层的第一排的数值 let len = this.col1; if(len > 1){ for (let i = 2; i <= len; i++){ this.colDataList1.push({id: i + ''}); } return this.colDataList1; }else{ return this.colDataList1; } }, row1Change(){ // 每触发一次,清空数据 this.rowDataList1 = [{ id: Math.ceil(Math.random()*100)}]; let len = this.row1; if (len > 1){ for (let i = 2; i <= len ; i++){ this.rowDataList1.push({id: Math.ceil(Math.random()*100) + i}); } return this.rowDataList1; }else { return this.rowDataList1; } }, handleClick(row, col, index) { // console.log(JSON.stringify(row)); // console.log(JSON.stringify(col)); // console.log("点击的cell 行数: " + JSON.stringify(index)); // index 是 行数,0 表示第一行,从 0 开始 // 一点击获取 行纵坐标 this.colPos = col; this.rowPos = index; }, cellStyle({row, column, rowIndex, columnIndex}){ // console.log(JSON.stringify(row)) // console.log(JSON.stringify(column)) // console.log("要渲染的行数: " + JSON.stringify(rowIndex)) // console.log(JSON.stringify(columnIndex)) if(rowIndex == 0 && columnIndex == 0){ return ''; }else { if(rowIndex == this.rowPos && columnIndex == this.colPos){ //指定坐标 return 'background: pink'; }else{ return ''; } } }, } });</script></body></html>为了方便大家直接使用理解,我这里使用的脚本等都是在线链接,确保大家直接 down 下来就能运行处效果的。效果图 ...

July 10, 2019 · 2 min · jiezi

elementUI-table-表格-column-样式

前言好长时间没更新了,最近这段时间状态不佳,感觉整个人的精神状态不太好,总是回想起以前的某个人。。。 好了,废话不多说了;今天我遇到一个有趣的 如题 table 表格的样式显示问题,然后我 google 了半天,发现别人写的都是 table 表头等这些不着边际的问题,与我的目的相差甚远,然后只好自己思考着怎么写了,下面看具体要求吧 功能截图如下: 要达到的要求:根据状态那一列的数据(未到账,已取消,已到账)显示相应的颜色标识,并且只有在 状态那一列是 未到账 状态时,操作才会显示 到账,最终显示呢,如上图所示。 实现其实,实现相对来说很简单,亮点在于 elementui 对于 table 每一行数据的处理上实现代码如下: <!-- table 数据 start --> <el-table ref="multipleTable" :data="orderList" v-loading="listLoading" element-loading-text="拼命加载中" border style="width:100%;" height="536" highlight-current-row @selection-change="chooseSelectionChange" :default-sort = "{prop: 'order_time', order: 'descending'}"> <el-table-column fixed type="selection" align="center" width="50"></el-table-column> <el-table-column prop="order_time" align="center" label="下单时间" show-overflow-tooltip min-width="140" sortable></el-table-column> <el-table-column prop="type_name" align="center" label="状态" show-overflow-tooltip min-width="140"> <template slot-scope="scope"> <span v-if="scope.row.type_name == '未到账'" style="color:black;">未到账</span> <span v-if="scope.row.type_name == '已到账'" style="color:rgb(84, 195, 29);">已到账</span> <span v-if="scope.row.type_name == '已取消'" style="color:red;">已取消</span> </template> </el-table-column> <el-table-column fixed="right" align="center" label="操作" show-overflow-tooltip min-width="140"> <template slot-scope="scope"> <span class="option-item option-edit" @click.stop="tableOption('到账', scope.$index, scope.row)" style="color: #54c31d;" v-show="getButtonPermit('sys:user:operate')" v-if="scope.row.type_name == '未到账'">到账</span> </template> </el-table-column> </el-table> <!-- table 数据 end -->解释: ...

July 2, 2019 · 1 min · jiezi

iview中-formtable-渲染的问题

描述需求在表格里面渲染form表单,数字保留2位小数,不足的自动补齐。 选用选用的是 iview 的组件 Form 、Input、Table。 Form表单的数据可以双向绑定,这样在对输入的数据做补零操作后,更新Form表单的model绑定的数据,即可更新。 没有使用 InputNumber 是因为它的交互用户体验不是很友好,而选用了常用的Input。但问题不是出在这里,请继续往下看。 form+tabletest.vue<template> <div> <Form ref="formTable" :model="formTable" :rules="rulesTable" inline > <Table border :columns="columns" :data="data"></Table> </Form> </div></template><script> let Trim=function(str,isGlobal) { let result; result = str.replace(/(^\s+)|(\s+$)/g,""); if(isGlobal === true) result = result.replace(/\s/g,""); return result; }; import FormItemInput from './form-item-input.vue'; export default { name:'test', components: {FormItemInput}, data() { return { //+++ form 表单 start formTable:{}, rulesTable:{}, //+++ form 表单 end // +++++++ table start data:[ { "id":1, "name":"张三", "score":"" }, { "id":2, "name":"李四", "score":"" } ], columns:[ { title: '序号', type: 'index', width: 60, align: 'center' }, { title: '姓名', key: 'name', width: 200, ellipsis:true }, { title: '成绩', key: 'score', width: 150, className:'custom-table-form', ellipsis:true, renderHeader:(h, params) => { // 2019年1月4日11:01:36 // 必填 需要展示 * return h('div', [ h('i', { style: { color: 'red', marginRight: '5px' } }, '*'), h('span', {}, params.column.title) ]); }, render:(h, params)=>{ console.log('行数据',params.index, params); /** * 2019年5月14日17:08:32 添加校验 */ let _formKey=params.column.key+'_'+params.row.id; this.rulesTable[_formKey]=[]; // 2019年6月6日11:09:32 这步会导致,数据渲染两遍 this.$set(this.$data.formTable, _formKey, params.row[params.column.key]===undefined || params.row[params.column.key]===null ? '' : params.row[params.column.key]); this.rulesTable[_formKey].push( { /** * 清掉input输入的空格,如果为空,不能通过 */ validator(rule, value, callback, source, options) { let errors = []; if (!Trim(value) ){ errors.push( new Error( "该项是必填项")); } callback(errors); }, trigger: 'blur' }, { validator(rule, value, callback, source, options) { let errors = []; // 正则控制范围,比较大小,同时存在才为true let _tmpValue= value && Number(value); // 可以出现一项:100分,或者n项 几分~几十分 let reg= /^([1-9]?[0-9]{1,2})?\0?(\.\d{1,2})?$/; if(!(reg.test(_tmpValue)&&0<=_tmpValue&&_tmpValue<=100)) { errors.push( new Error("请输入0-100之间的数字,小数点后最多允许保留2位小数") ); } callback(errors); },type:'string', trigger: 'blur' } ); return h(FormItemInput, { props:{ formTable:this.formTable, formKey:_formKey, }, on:{ 'on-form-blur':(value)=>{ // 更新提交数据 this.$refs.formTable.validateField(_formKey, (message)=>{ //2019年5月16日10:12:08 校验通过以后,保留2位小数 if(message.length==0) { this.formTable[_formKey]=Number(value).toFixed(2); console.log('formTable',this.formTable); } }); } } }); } }, ], // +++++++ table end }; }, mounted() { }, methods: {} }</script>form-item-input.vue<template> <Form-item :prop="formKey" @on-form-blur="onFormBlur" :required="required" :label="label" :setLengthNumber="setLengthNumber" :lastFormItem="lastFormItem"> <Input v-model="formTable[formKey]" ></Input> </Form-item></template><script> export default { props:{ required:{ type:Boolean, default:false }, label:{ type:String, default:'' }, formTable:Object, formKey:String, }, methods: { onFormBlur(value){ this.$emit('on-form-blur', value); } } }</script>图示 ...

June 6, 2019 · 2 min · jiezi

vxetable-vue-table-一个非常强大表格组件

vxe-table vue table 一个非常强大表格组件一个功能更加强大的 Vue 表格组件查看 vxe-table 功能点基础尺寸斑马线条纹带边框单元格样式列宽拖动流体高度固定表头固定列固定表头和列表头分组序号单选多选排序筛选合并行或列表尾合计导出 CSV显示/隐藏列加载中格式化内容自定义模板快捷菜单滚动渲染展开行树形表格可编辑表格数据校验全键盘操作Excel 表格例子<template> <div> <vxe-table ref="xTable" :data.sync="tableData"> <vxe-table-column type="index" width="60"></vxe-table-column> <vxe-table-column prop="name" label="Name"></vxe-table-column> <vxe-table-column prop="date" label="Date"></vxe-table-column> <vxe-table-column prop="address" label="Address"></vxe-table-column> </vxe-table> </div></template><script>export default { data () { return { tableData: [ { id: 10001, name: 'test1', sex: 'Man', date: '2019-05-01', address: 'address abc123' } ] } }}</script>

May 24, 2019 · 1 min · jiezi

CSS三栏布局多种方法详解比较兼容性

<!-- 题目:假设高度已知,请写出三栏布局,其中左栏、右栏宽度各为300px,中间自适应 -->这是一道经典的面试题,下面记录了css布局的5种方法。 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>三栏布局</title> <style> * { margin: 0; padding: 0; } </style></head><body><!--5种解决方案--></body>1. 浮动解决方案: <!-- 1. 浮动解决方案 --> <scetion class="layout float"> <!-- 样式 --> <style media="screen"> .layout.float article div { min-height: 80px; } .layout.float .left { width: 300px; background-color: red; float: left; } .layout.float .center { background-color: yellow; } .layout.float .right { width: 300px; background-color: blue; float: right; } </style> <!-- 结构 --> <!-- 注意:结构中 右浮动的div一定要写在 center 的前面,否则无效. --> <h2>三栏布局</h2> <article class="left-ritgh-center"> <div class="left"></div> <div class="right"></div> <div class="center"> <h2>浮动解决方案</h2> 1.这是三栏布局的浮动解决方案; 2.这是三栏布局的浮动解决方案; 3.这是三栏布局的浮动解决方案; 4.这是三栏布局的浮动解决方案; 5.这是三栏布局的浮动解决方案; 6.这是三栏布局的浮动解决方案; </div> </article> </scetion>2. 绝对定位解决方案: <!-- 2. 绝对定位解决方案 --> <section class="layout absolute"> <!-- 样式 --> <style> .layout.absolute article div { min-height: 80px; position: absolute; } .layout.absolute .left { width: 300px; background-color: red; left: 0; } .layout.absolute .center { background-color: yellow; left: 300px; right: 300px; } .layout.absolute .right { width: 300px; background-color: blue; right: 0; } </style> <!-- 结构 --> <h1>三栏布局</h1> <article class="left-center-right"> <div class="left"></div> <div class="center"> <h2>绝对定位解决方案</h2> 1.这是三栏布局的绝对定位解决方案; 2.这是三栏布局的绝对定位解决方案; 3.这是三栏布局的绝对定位解决方案; 4.这是三栏布局的绝对定位解决方案; 5.这是三栏布局的绝对定位解决方案; 6.这是三栏布局的绝对定位解决方案; </div> <div class="right"></div> </article> </section>3. flexbox 解决方案: <!-- 3. flexbox解决方案 --> <section class="layout flexbox"> <!-- 样式 --> <style> /* flexbox解决方案在浏览器中显示时被上面的绝对定位解决方案挡住了,设置一个margin-top */ .layout.flexbox { margin-top: 110px; } /* 设置最低高度 */ .layout.flexbox article div { min-height: 80px; } .layout.flexbox article { display: flex; } .layout.flexbox .left { width: 300px; background-color: red; } .layout.flexbox .center { /*center部分增长 使整行填充*/ flex: 1; background-color: yellow; } .layout.flexbox .right { width: 300px; background-color: blue; } </style> <!-- 结构 --> <h1>三栏布局</h1> <article class="left-center-right"> <div class="left"></div> <div class="center"> <h2>flexbox解决方案</h2> 1.这是三栏布局的flexbox解决方案; 2.这是三栏布局的flexbox解决方案; 3.这是三栏布局的flexbox解决方案; 4.这是三栏布局的flexbox解决方案; 5.这是三栏布局的flexbox解决方案; 6.这是三栏布局的flexbox解决方案; </div> <div class="right"></div> </article> </section>4. 表格布局解决方案 <!-- 4. 表格布局解决方案 --> <section class="layout table"> <!-- 样式 --> <style> .layout.table .left-center-right { width: 100%; min-height: 80px; display: table; } .layout.table .left-center-right>div { display: table-cell; } .layout.table .left { width: 300px; background-color: red; } .layout.table .center { background-color: yellow; } .layout.table .right { width: 300px; background-color: blue; } </style> <!-- 结构 --> <h1>三栏布局</h1> <article class="left-center-right"> <div class="left"></div> <div class="center"> <h2>表格布局解决方案</h2> 1.这是三栏布局的表格布局解决方案; 2.这是三栏布局的表格布局解决方案; 3.这是三栏布局的表格布局解决方案; 4.这是三栏布局的表格布局解决方案; 5.这是三栏布局的表格布局解决方案; 6.这是三栏布局的表格布局解决方案; </div> <div class="right"></div> </article> </section>5. 网格布局解决方案: <!-- 5. 网格布局解决方案 --> <section class="layout grid"> <!-- 样式 --> <style> .layout.grid .left-center-right { width: 100%; display: grid; /* 网格行高 */ grid-template-rows: 100px; /* 网格列宽 */ grid-template-columns: 300px auto 300px; } .layout.grid .left { background-color: red; } .layout.grid .center { background-color: yellow; } .layout.grid .right { background-color: blue; } </style> <!-- 结构 --> <h1>三栏布局</h1> <article class="left-center-right"> <div class="left"></div> <div class="center"> <h2>网格布局解决方案</h2> 1.这是三栏布局的网格布局解决方案; 2.这是三栏布局的网格布局解决方案; 3.这是三栏布局的网格布局解决方案; 4.这是三栏布局的网格布局解决方案; 5.这是三栏布局的网格布局解决方案; 6.这是三栏布局的网格布局解决方案; </div> <div class="right"></div> </article> </section>效果: ...

May 23, 2019 · 2 min · jiezi

【mone-query】基于 element-ui 的通用查询组件

mone-queryGithub: https://github.com/jczzq/mone...Demo: https://blog.jczzq.com/mone-q...mone-query是基于element-ui封装的通用查询组件,它通过丰富的配置让你尽可能少的前端编码就可以完成大部分报表需求。必要依赖vue >= 2.5.2 element-ui >= 2.4.0 axios >= 0.16.0安装CommonJS方式npm install mone-query -Dumd方式<link href=“mone-query/lib/style.css” rel=“stylesheet”><script src="../mone-query/lib/index.js"></script>快速上手import Vue from “vue”;import ‘mone-query/style.css’;import MoneQuery from “mone-query”;Vue.use(MoneQuery, { // options …})<mone-query border :config=“config” :data=“data” />…import Config from “@/Config.json”;import Data from “@/Data.json”;data() { return { // 传入请求路径 config: “/api/config”, data: “/api/data” // or // 传入对象 config: Config.resultData, data: Data.resultData };}运行机制校验入参mone-query有两个必要参数config和data, config控制表格结构和自定义功能,data渲染数据行,这两个参数可以传入ajax路径字符串,也可以传入json对象,其他类型的参数传入时会抛出异常。注意组件还有其它一些参数可以传入,组件传入的参数与config有一些参数是重复的,甚至你还可以在Vue.use时给所有的组件实例传入默认参数,这是为了增加组件配置的灵活性,不过通过组件直接传入的参数优先级最高,config属性其次,最后是默认配置根据config参数获取配置如果config传入字符串,那么组件首先会发起Get请求查询配置json,这段期间表格会一直loading到请求结束,如果请求失败,表格将会触发on-error事件,使用者可以监听事件做后续处理,或者让页面保持错误面板的状态;请求成功的结果将直接作为配置对象使用。如果config传入json,将直接作为配置对象使用(具体结构示例参照Config.json)。根据配置对象渲染表结构受配置影响的部分包括:colbox: 工具栏的字段面板cols: 表头……根据data参数获取结果集如果data传入字符串,那么组件会将之作为ajax路径查询结果集。如果data传入json,将直接作为结果集使用(具体结构示例参照Data.json)。有时候在结果集渲染之前需要对某些字段进行处理,比如映射新值、时间转换等等,这时提供formatters对象,这是一个属性名为col.prop、属性值为该列过滤函数的对象,过滤函数有四个形参:row, column, cellValue, indexMoneQuery Attributes属性名类型含义可选值默认值*configObject , String表头列ajax路径配置对象{}*dataArray , String数据行ajax路径数据集合[]heightString/NumberTable高度auto8080pxautomax-heightString/NumberTable的最大高度–borderBoolean是否带有纵向边框-falseprimary-keyString主键(数据行多选时必要)-idpage-nameString分页参数中的pageSize键名-pageIndexsize-nameString分页参数中的pageSize键名-pageSizerows-nameStringajax请求结果集键名-resultData.rowstotal-nameStringajax请求结果总数键名-resultData.totalcolboxObject字段显示框方位-{ placement: “top”, width: “540px”, trigger: “click” }visible-fieldsBoolean,Array默认显示字段true: 全部显示; false: 全部隐藏; [“propA”, “propB”, …]truevisible-fields-configArray<FieldGroup>字段在工具栏的显示配置–formattersObject包含各个列过滤函数的对象-{}show-actionBoolean是否显示右侧操作栏-falseshow-deleteBoolean是否显示删除按钮-falseshow-headerBoolean是否显示表头(包含筛选条件)-trueshow-selectionBoolean是否显示多选框-trueshow-indexBoolean是否自定义序列-falseMoneQuery Events事件名说明参数delete点击删除按钮触发selectionsearch点击查询按钮触发parametersconfig-success查询配置成功resconfig-error查询配置失败errorconfig-complete查询配置完成(成功或失败都会触发)-data-success查询结果集成功resdata-error查询结果集失败errordata-complete查询结果集完成(成功或失败都会触发)-config Attributes属性名类型含义可选值默认值baseUrlStringajax根路径–primary-keyString主键(数据行多选时必要)-idcolsArray<Col>列-idpage-nameString分页参数中的pageSize键名-pageIndexsize-nameString分页参数中的pageSize键名-pageSizerows-nameStringajax请求结果集键名-resultData.rowstotal-nameStringajax请求结果总数键名-resultData.totalcolboxObject字段显示框方位-{ placement: “top”, width: “540px”, trigger: “click” }visible-fieldsBoolean,Array默认显示字段true: 全部显示; false: 全部隐藏; [“propA”, “propB”, …]truevisible-fields-configArray<FieldGroup>字段在工具栏的显示配置–show-actionBoolean是否显示右侧操作栏-falseshow-deleteBoolean是否显示删除按钮-falseshow-headerBoolean是否显示表头(包含筛选条件)-trueshow-selectionBoolean是否显示多选框-trueshow-indexBoolean是否自定义序列-falsecolbox Attributes属性名类型含义可选值默认值placementString展示方位-topwidthString面板宽度-540pxtriggerString触发事件类型-clickCol class属性名类型含义可选值默认值labelString列标题–propString列字段名–typeString列字段类型varchar、option、date、datetime-widthString列宽度widthwidthorderNumber排列顺序–actionString查询类型-varchar:lk,option:in; data:lt& gt; datetime:le& geplaceholderString输入提示–fixedBoolean是否固定列–optionsArray多选组件的选项列表,eg: [{label, value}]–Col.type enum枚举值含义varchar文本option多选date日期datetime日期时间FieldGroup class属性名类型含义可选值默认值titleString组标题–orderString排列顺序–selectionArray选中的–checkAllBoolean是否全选-trueisIndeterminateBoolean是否半选-falsecolPropsArray组成员–查询配置标准结构(application/json)resultCode 响应码resultData 响应结果(注意resultData必须是JSON对象)primaryKey String 主键prop(默认:id)rowsName String 结果集属性名,将根据rowsName指定的属性名获取目标结果集(默认: resultData.rows)totalName String 结果总数属性名,将根据totalName指定的属性名获取目标结果总数(默认: resultData.total)pageName String 当前页属性名,将根据pageName指定的属性名设置分页参数(默认: pageIndex)sizeName String 每页条数属性名,将根据sizeName指定的属性名设置分页参数(默认: pageSize)pageSize Number 默认每页查询的数据数量(默认: 20)cols Array 列头(字段)对象集合label String 标题prop String 属性名type String 属性类型varchar 文本option 多选date 日期datetime 日期时间bit 单选(暂不支持)int 数字(暂不支持)time 时间(暂不支持)width String 列宽order Number 排列顺序colbox Object 可选字段配置placement String 展示方位(默认:top)width String 面板宽度(默认:540px)trigger String 触发事件类型(默认:click)value Array (默认:所有字段)查询参数标准结构(application/json)colProps Array 要查询的字段列params Array 查询参数字段field Stringaction Stringeq 等于ne 不等于in 包含在ni 不包含在sw start withew end withlk likegt 大于lt 小于ge 大于等于le 小于等于filedType Stringvarchar 字符串option 多选date 日期范围datetime 日期时间范围bit 单选(暂不支持)int 数字(暂不支持)time 时间(暂不支持)value anysort Array 排序字段prop String 字段名type String 排序方式asc 正序desc 降序page ObjectpageIndex Number 当前页(默认:1)pageSize Number 每页多少条(默认:20)# 查询参数示例cols: [“name”, “age”, “grade”, “isMarried”, “birthday”, “schoolTime”],params: [ { field: “name”, action: “lk”, filedType: “varchar”, value: “李”, }, { field: “birthday”, action: “lt”, filedType: “datetime”, value: “2019-04-13”, }, { field: “birthday”, action: “ge”, filedType: “datetime”, value: “2019-03-13”, }, { field: “grade”, action: “in”, filedType: “option”, value: [1,2,5], }]sort: [ { prop: “age’, type: “asc” }],page: { pageIndex: 1, pageSize: 20}查询结果集标准结构(application/json)[resultCode] 响应码[resultData] 响应结果[rows] 结果集[total] 结果总数轻量版mone-query默认依赖element-ui部分组件和axios请求库,这里有两个构建版供你的应用选择:全部引入(js≈360kb, style≈103kb):包括element相关组件(可以在应用中使用这些组件,因为它们已经全局注册过了);另外是axios。只引入核心代码(≈24kb, style≈1kb),如果你的应用已经引入了element和axios,那建议只引入核心代码即可。 import “mone-query/lib/lite/style.css”; import moneQuery from “mone-query/lib/lite”; …PS: 这里说来说去最终不影响物质守恒,如果你不是太关心你应用的构建体积可以忽略 ...

March 27, 2019 · 2 min · jiezi

lb-element-table基于 Element UI Tabel 封装的表格组件

基于 Element UI Tabel 封装的表格组件,使用更简单,无需大量重复的代码量,只需简单配置column和data即可,简单明了。完美支持Element UI Tabel的属性及事件,熟悉的参数、熟悉的味道,熟悉的写法。Github点击前往更多示例及参考点击前往简单示例参考<template> <lb-table :column=“tableData.column” :data=“tableData.data”> </lb-table></template><script>export default { data () { return { tableData: { column: [ { prop: ‘date’, label: ‘日期’ }, { prop: ’name’, label: ‘姓名’ }, { prop: ‘address’, label: ‘地址’ } ], data: [ { date: ‘2016-05-02’, name: ‘王小虎1’, address: ‘上海市普陀区金沙江路 1518 弄’ }, { date: ‘2016-05-02’, name: ‘王小虎2’, address: ‘上海市普陀区金沙江路 1518 弄’ }, { date: ‘2016-05-02’, name: ‘王小虎3’, address: ‘上海市普陀区金沙江路 1518 弄’ } ] } } }}</script> ...

March 25, 2019 · 1 min · jiezi

element-ui组件table实现自定义筛选功能

element-ui默认的table组件支持的表头筛选(过滤)是比较简单的,只支持数组的方式,单选或多选的形式,但有时候我们喜欢支持输入框形式(其实感觉有点扯淡,一般列表页上面都有搜索条件)。注意:里面用到的jsx语法,可能需要安装一些插件。准备工作:1.安装babel-plugin-jsx-v-model插件npm i babel-plugin-jsx-v-model -D或者yarn add babel-plugin-jsx-v-model -D2..babelrc:{ “presets”: [“es2015”], “plugins”: [“jsx-v-model”, “transform-vue-jsx”]}3.重启本地环境实现效果如下:代码如下:<template> <div> <el-table :data=“tableData”> <el-table-column label=“这是文字” :render-header=“renderHeader” prop=“name”></el-table-column> <el-table-column label=“地址” prop=“address”></el-table-column> </el-table> </div></template><script>export default { data() { return { search: ‘’, visible: false, tableData: [{ date: ‘2016-05-02’, name: ‘王小虎’, address: ‘上海市普陀区金沙江路 1518 弄’ }, { date: ‘2016-05-04’, name: ‘王小虎’, address: ‘上海市普陀区金沙江路 1517 弄’ }, { date: ‘2016-05-01’, name: ‘王小虎’, address: ‘上海市普陀区金沙江路 1519 弄’ }, { date: ‘2016-05-03’, name: ‘王小虎’, address: ‘上海市普陀区金沙江路 1516 弄’ }] } }, methods: { renderHeader(h, {column, $index}, index) { return ( <span> 问题分类 <el-popover placement=‘bottom’ width=‘200’ height=‘200’ trigger=“click” v-model={this.visible}> <span slot=“reference”> <i class=“el-icon-search” style={this.search ? {‘color’ : ‘red’} : {‘color’: ‘blue’}}></i> </span> <el-input size=‘small’ v-model={this.search} placeholder=‘请输入内容’></el-input> <div class=‘el-table-filter__bottom’> <button class={this.search ? ’’ : ‘is-disabled’}>筛选</button> <button on-click={this.clearSearch}>重置</button> </div> </el-popover> </span> ); }, clearSearch() { this.search = ‘’; } }}</script>参考: https://www.jianshu.com/p/f55… ...

March 15, 2019 · 1 min · jiezi

vue+elementui 实现table的row 在hover/click实现展开行效果

前言:产品的需求是,在table表格 click 一行时,展开一行,显示对于此行的增删改查等操作按钮,click当前行会收起操作按钮消息,click 别的行,会收起之前的展开且展开当前行.hover的实现一样,我就以click来说明了.吐槽下elementUI的api~~~ 确实不那么友好(不然也没必要写这个文章了是不~~~)哈哈需求明确了,现在来实现此图片是api的例子,type=“expand” 这样会出现一列’>’,然而并不能实现,继续看,有个这个方法,那我们来实现一些,代码如下:<el-table :data=“tableData” ref=“refTable” row-key=“id” :expand-row-keys=“expands” @expand-change=“expandChange” @row-click=“rowClick”>rowClick(row,column,event){ this.$refs.refTable.toggleRowExpansion(row); },expandChange(row,expandedRows){ if(expandedRows.length>1){ expandedRows.shift() } }, 可见, table标签里,有row-key 需要是你tableData的唯一标识,<el-table-column type=“expand” width=“0” fixed=“right” label=“more”> <template slot-scope=“scope”> 这里写你需要展开的内容 </template></el-table-column可以看到,我的width值设置为了0,是为了不让那个箭头列显示,也没有找到好的方法,项目也比较急,所以就先这样啦~有好方法欢迎留言给我哦如果对你有帮助,请点个赞,可以更加勤快的分享文章 哈哈

March 8, 2019 · 1 min · jiezi

javascript动态合并纵向单元格

1.需求合并相邻行内容相同的单元格。2.概念rowspan指定单元格纵向跨越的行数。如rowspan被设为3,这表示该单元格必须跨越三行(本身一行,加上另外两行)3.公共方法 /** * 单元格合并方法,增加rowspan属性 * @param data 要处理的数据 * @param nameList 合并的字段list */function rowspanFun(data, nameList) { for (var i = 0; i < nameList.length; i++) { var name = nameList[i]; var startRow = 0; var endRow = data.length; var mergeNum = 1; if (endRow != 1) { for (var j = startRow; j < endRow; j++) { if (j == endRow - 1) { //判断是否是最后一个元素 if (startRow == endRow - 1) { data[j][name + ‘Rowspan’] = 1; } } else { if (data[startRow][name] == data[j + 1][name]) { data[j + 1][name + ‘Rowspan’] = 0; mergeNum = mergeNum + 1; data[startRow][name + ‘Rowspan’] =mergeNum; } else { startRow = j + 1; if (mergeNum > 1) { data[startRow][name + ‘Rowspan’] = 1; } else { data[j][name + ‘Rowspan’] = 1; } mergeNum = 1; } } } } else { data[0][name + ‘Rowspan’] = 1; } } return data; }4.js中调用公共方法var data = [ {name: ‘dwj’, sex: ‘女’, age: 20}, {name: ‘dwj’, sex: ‘男’, age: 20}, {name: ‘dwq’, sex: ‘女’, age: 20}, {name: ‘other’, sex: ‘女’, age: 20} ];rowspanFun(data, [’name’, ‘sex’]);调用方法后的数据处理结果5.html中使用<table> <tr *ngFor=“let item of data”> <td *ngIf=“item.nameRowspan != 0” [attr.rowspan]=‘item.nameRowspan’>{{item.name}}</td> <td *ngIf=“item.sexRowspan != 0” [attr.rowspan]=‘item.sexRowspan’>{{item.sex}}</td> <td>{{item.age}}</td> </tr> </table>注意:此html代码使用的是ng语法,请根据自已使用的js框架自行调整。6.结果 ...

February 12, 2019 · 1 min · jiezi

开源 UI 库中,唯一同时实现了大表格虚拟化和树表格的 Table 组件

背景有这样一个需求,一位 React Suite(以下简称 rsuite)的用户,他需要一个 Table 组件能够像 Jira Portfolio 一样,支持树形数据,同时需要支持大数据渲染。 截止到目前(2019年1月18日)为止,开源 UI 库中没有找到可以支持的组件,所以 rsuite 在最新的版本中支持这一特性。接下来,我们看一下 rsuite 中是怎么支持这两个功能?大表格虚拟化首先,我们看一下支持大数据渲染,在页面中渲染过多的 DOM 元素会带来性能问题,必须得有一种解决方案去优化它,我们暂且叫做大表格虚拟化。所谓的大表格虚拟化,其实就是为表格设置一个较大的数据(比如 10000 条数据),然后虚拟一个表格隐藏掉不需要显示的数据。 为了解决让浏览器渲染的大量 DOM 时候出现的性能问题,我们不能把 10000 条数据都渲染到页面,采用一种方式,只渲染可视范围内数据。 同时为表格设置一个滚动条,只有在滚动到需要显示的区域时候才渲染该区域的数据,减少的 DOM 数量。预览地址以上这是一个 10000 条数据的 Table,渲染后的 HTML 结构是:我们可以看到在 Table 中只渲染了 14 个 rs-table-row ,其中第一个和最后一个是没有 children, 只是一个拥有高度的占位符。 每一个 rs-table-row 都是绝对定位,所以即使 Table 中删除一个 Row, 或者新增一个 Row ,也不会改变其他 Row 的位置。 在这样的基础上,通过获取滚动条的滚动的位置,就很容易判断当前 Row 的 top 值是否在 Table 的可视范围内,同时更新所有的 Row。很多优秀的库都实现了这样的功能,原理基本一致,比如 react-virtualized 就提供 Table 组件,但是他不支持 Tree。树形表格在表格中展示树形数据的需求,我们见得比较多就像甘特图表格展示那样。它有子父层级关系,可以展开子节点。这样一个表格,很多 Table 组件都支持,但是如果同时需要支持虚拟化就相对比较麻烦,因为在展开关闭节点的时候需要重新计算显示的 DOM 以及设置滚动条的位置。在 rsuite Table 组件之前的版本中,渲染的树形表格的 DOM 结构是一棵 Tree。 所以首先需要把 Tree 拍平,转换一个一维数组,为每一个节点设置父节点,通过父节点的深度渲染 Tree 节点的相对位置。 然后就比较好处理,只需要在点击展开关闭节点按钮的时候,处理好数据的过滤。安装与使用rsuite 的 Table 组件的设计,对开发还是非常方便,通过 <Table>、<Column>、<Cell>、<HeaderCell> 组件定义结构,通过赋值data 属性渲染表格数据。安装npm install rsuite –save如果你在项目只希望用到 Table, 不想安装整个 rsuite 库,你可以单独安装 rsuite-table示例代码:import { Table } from ‘rsuite’;const { Column, HeaderCell, Cell } = Table;const data = [{ id: 1, name: ‘foobar’, email: ‘foobar@xxx.com’ }];ReactDOM.render( <Table height={400} data={data}> <Column width={70} align=“center” fixed> <HeaderCell>编号</HeaderCell> <Cell dataKey=“id” /> </Column> <Column width={200} fixed> <HeaderCell>姓名</HeaderCell> <Cell dataKey=“name” /> </Column> <Column width={200}> <HeaderCell>邮箱</HeaderCell> <Cell dataKey=“email” /> </Column> </Table>);最后最后,对于一个成熟的 Table 组件怎么能只有这点功能,所以它还支持:自定义调整列宽锁定列自动换行排序分页编辑合并单元格自定义单元格自动列宽可展开行剩下唯一的问题,就是您是否在项目中尝试它。 ...

January 21, 2019 · 1 min · jiezi

火狐浏览器table表格th、td填充背景描边后设置position: relative边框失效问题

转载火狐浏览器table表格th、td填充背景描边后设置position: relative边框失效问题一般在开发管理系统的时候经常会用到表格,在使用表格式时通常会给thead部分的th或td加上背景色然后还有边框颜色,在这种情况下浏览器基本都显示正常的,但是当给th或td加上position: relative时火狐浏览器就会显示th或td的边框被遮盖,只显示一片背景色没有边框,如下请在火狐浏览器查看,demo:火狐浏览器table边框失效问题我为什么要在th或td加上position: relative呢,我在开发系统的时候使用bootstrap结合jquery.dataTables,不想使用dataTables自带的图标主题,使用的bootstrap.dataTable 的主题,主题的图标是通过伪类:before和:after实现的,所以加上的position: relative,其实这也是主题的一个小bug,然后导致火狐浏览器只会显示背景色边框完全被遮盖。解决方法1:去掉th的背景色,把背景色填充在thend 的 tr上,这样就能完全解决这个问题,如下.table{ thead{ tr{ background-color: #4587E7; } th{ position: relative; color: #fff; } }}demo2解决方法2:给th设置一个z-index-1属性,这样也能完全解决这个问题,如下.table{ thead{ th{ position: relative; z-index: -1; color: #fff; background-color: #4587e7; } }}demo3转载火狐浏览器table表格th、td填充背景描边后设置position: relative边框失效问题扩展阅读:http://www.w3help.org/zh-cn/c…

December 26, 2018 · 1 min · jiezi

优雅的elementUI table 单元格可编辑实现方法

最近在做可编辑特定列的单元格的elementUI table,看了N多的开源、文章,找到一个很优雅的实现方式,分享给大家。PS:单元格可编辑的table,用英文搜索:Inline editable table with ElementUI 会得到高质量结果。先上效果:APP.vue:<template> <div id=“app”> <div style=“margin-bottom: 30px”> <el-switch style=“display: block” v-model=“editModeEnabled” active-color="#13ce66" inactive-color="#ff4949" active-text=“Edit enabled” inactive-text=“Edit disabled”> </el-switch> </div> <el-table :data=“gridData” style=“width: 100%"> <el-table-column label=“Name” min-width=“180”> <editable-cell slot-scope="{row}” :can-edit=“editModeEnabled” v-model=“row.name”> <span slot=“content”>{{row.name}}</span> </editable-cell> </el-table-column> <el-table-column min-wwidth=“150” label=“Gender”> <editable-cell slot-scope="{row}" editable-component=“el-select” :can-edit=“editModeEnabled” close-event=“change” v-model=“row.gender”> <el-tag size=“medium” :type=“row.gender === ‘M’ ? ‘primary’ : ‘danger’” slot=“content”> {{row.gender === ‘M’ ? ‘Male’: ‘Female’}} </el-tag> <template slot=“edit-component-slot”> <el-option value=“M” label=“Male”></el-option> <el-option value=“F” label=“Female”></el-option> </template> </editable-cell> </el-table-column> <el-table-column label=“Birth Date” min-width=“250”> <editable-cell slot-scope="{row}" :can-edit=“editModeEnabled” editable-component=“el-date-picker” format=“yyyy-MM-dd” value-format=“yyyy-MM-dd” v-model=“row.date”> <span slot=“content”>{{row.date}}</span> </editable-cell> </el-table-column> </el-table> </div></template><script>import EditableCell from “./components/EditableCell.vue”;export default { name: “App”, components: { EditableCell }, data() { return { editModeEnabled: false, gridData: [ { date: “2016-05-03”, name: “Tom”, gender: “M” }, { date: “2016-05-02”, name: “Lisa”, gender: “F” }, { date: “2016-05-04”, name: “Jon”, gender: “M” }, { date: “2016-05-01”, name: “Mary”, gender: “F” } ] }; }};</script><style>.edit-cell { min-height: 35px; cursor: pointer;}</style>EditeableCell.vue:<template> <div @click=“onFieldClick” class=“edit-cell”> <el-tooltip v-if="!editMode && !showInput" :placement=“toolTipPlacement” :open-delay=“toolTipDelay” :content=“toolTipContent”> <div tabindex=“0” class=“cell-content” :class="{’edit-enabled-cell’: canEdit}" @keyup.enter=“onFieldClick”> <slot name=“content”></slot> </div> </el-tooltip> <component :is=“editableComponent” v-if=“editMode || showInput” ref=“input” @focus=“onFieldClick” @keyup.enter.native=“onInputExit” v-on=“listeners” v-bind="$attrs" v-model=“model”> <slot name=“edit-component-slot”></slot> </component> </div></template><script>export default { name: “editable-cell”, inheritAttrs: false, props: { value: { type: String, default: "" }, toolTipContent: { type: String, default: “Click to edit” }, toolTipDelay: { type: Number, default: 500 }, toolTipPlacement: { type: String, default: “top-start” }, showInput: { type: Boolean, default: false }, editableComponent: { type: String, default: “el-input” }, closeEvent: { type: String, default: “blur” }, canEdit: { type: Boolean, default: false } }, data() { return { editMode: false }; }, computed: { model: { get() { return this.value; }, set(val) { this.$emit(“input”, val); } }, listeners() { return { [this.closeEvent]: this.onInputExit, …this.$listeners }; } }, methods: { onFieldClick() { if (this.canEdit) { this.editMode = true; this.$nextTick(() => { let inputRef = this.$refs.input; if (inputRef && inputRef.focus) { inputRef.focus(); } }); } }, onInputExit() { this.editMode = false; }, onInputChange(val) { this.$emit(“input”, val); } }};</script><style>.cell-content { min-height: 40px; padding-left: 5px; padding-top: 5px; border: 1px solid transparent;}.edit-enabled-cell { border: 1px dashed #409eff;}</style>在线查看(可能需要科学上网):https://codesandbox.io/s/2pv1…github:https://github.com/heianxing/…另外一个单元格编辑的例子:App.vue:<template> <div id=“app”> <el-tooltip content=“Click on any of the cells or on the edit button to edit content”> <i class=“el-icon-info”></i> </el-tooltip> <el-table :data=“gridData” style=“width: 100%"> <el-table-column label=“Operations” min-width=“180”> <template slot-scope="{row, index}"> <el-button icon=“el-icon-edit” @click=“setEditMode(row, index)"> </el-button> <el-button type=“success” icon=“el-icon-check” @click=“saveRow(row, index)"> </el-button> </template> </el-table-column> <el-table-column label=“Name” min-width=“180”> <editable-cell :show-input=“row.editMode” slot-scope="{row}” v-model=“row.name”> <span slot=“content”>{{row.name}}</span> </editable-cell> </el-table-column> <el-table-column min-wwidth=“150” label=“Gender”> <editable-cell :show-input=“row.editMode” slot-scope="{row}” editable-component=“el-select” close-event=“change” v-model=“row.gender”> <el-tag size=“medium” :type=“row.gender === ‘M’ ? ‘primary’ : ‘danger’” slot=“content”> {{row.gender === ‘M’ ? ‘Male’: ‘Female’}} </el-tag> <template slot=“edit-component-slot”> <el-option value=“M” label=“Male”></el-option> <el-option value=“F” label=“Female”></el-option> </template> </editable-cell> </el-table-column> <el-table-column label=“Birth Date” min-width=“250”> <editable-cell :show-input=“row.editMode” slot-scope="{row}” editable-component=“el-date-picker” format=“yyyy-MM-dd” value-format=“yyyy-MM-dd” v-model=“row.date”> <span slot=“content”>{{row.date}}</span> </editable-cell> </el-table-column> </el-table> </div></template><script>import EditableCell from “./components/EditableCell.vue”;export default { name: “App”, components: { EditableCell }, data() { return { gridData: [ { date: “2016-05-03”, name: “Tom”, gender: “M” }, { date: “2016-05-02”, name: “Lisa”, gender: “F” }, { date: “2016-05-04”, name: “Jon”, gender: “M” }, { date: “2016-05-01”, name: “Mary”, gender: “F” } ] }; }, methods: { setEditMode(row, index) { row.editMode = true; }, saveRow(row, index) { row.editMode = false; } }, mounted() { this.gridData = this.gridData.map(row => { return { …row, editMode: false }; }); }};</script><style>.edit-cell { min-height: 35px; cursor: pointer;}</style>EditeableCell.vue:<template> <div @click=“onFieldClick” class=“edit-cell”> <el-tooltip v-if="!editMode && !showInput" :placement=“toolTipPlacement” :open-delay=“toolTipDelay” :content=“toolTipContent”> <div tabindex=“0” @keyup.enter=“onFieldClick”> <slot name=“content”></slot> </div> </el-tooltip> <component :is=“editableComponent” v-if=“editMode || showInput” ref=“input” @focus=“onFieldClick” @keyup.enter.native=“onInputExit” v-on=“listeners” v-bind="$attrs" v-model=“model”> <slot name=“edit-component-slot”></slot> </component> </div></template><script>export default { name: “editable-cell”, inheritAttrs: false, props: { value: { type: String, default: "" }, toolTipContent: { type: String, default: “Click to edit” }, toolTipDelay: { type: Number, default: 500 }, toolTipPlacement: { type: String, default: “top-start” }, showInput: { type: Boolean, default: false }, editableComponent: { type: String, default: “el-input” }, closeEvent: { type: String, default: “blur” } }, data() { return { editMode: false }; }, computed: { model: { get() { return this.value; }, set(val) { this.$emit(“input”, val); } }, listeners() { return { [this.closeEvent]: this.onInputExit, …this.$listeners }; } }, methods: { onFieldClick() { this.editMode = true; this.$nextTick(() => { let inputRef = this.$refs.input; if (inputRef) { inputRef.focus(); } }); }, onInputExit() { this.editMode = false; }, onInputChange(val) { this.$emit(“input”, val); } }};</script><style></style>在线查看(可能需要科学上网):https://codesandbox.io/s/mrqq…github:https://github.com/heianxing/…英文来源:https://www.reddit.com/r/vuej… ...

December 22, 2018 · 4 min · jiezi

vue中 表头th 合并单元格,且表格列数不定的动态渲染方法

吐槽今天,在vue中遇到 复杂表格的渲染 ,需要合并表头的单元格,且合并单元格的那列还是动态数据,也就是说你不知道会有多少组要合并起来,哎,我也有点说不清楚,废话不多说了,看代码把:代码示例data数据是后端接口返回的,其中的数据格式是这样的:[ { “studentId”: “ff808081651e03d4016530b937f50a33”, “studentName”: “傅xx”, “studentCode”: “91scdsc109”, “sex”: { “value”: “MALE”, “name”: “男” }, “termName”: “2018秋”, “examDates”: [ “10月”, “9月28日” ], “map”: { “9月28日”: [ { “courseName”: “听力”, “scorel”: 6.0 }, { “courseName”: “阅读”, “score”: 7.0 }, { “courseName”: “写作”, “scorel”: 5.5 } ] }, “courseNames”: [ “听力”, “阅读”, “写作”, “口语”, “总分” ] }, { “studentId”: “ff8080816483a43d01648723c52801bc”, “studentName”: “陈xx”, “studentCode”: “91edfedf3”, “sex”: { “value”: “FEMALE”, “name”: “女” }, “termName”: “2018秋”, “examDates”: [ “10月”, “9月28日” ], “map”: { “9月28日”: [ { “courseName”: “听力”, “score”: 5.5, “order”: 0 }, { “courseName”: “阅读”, “score”: 6.0, “order”: 1 }, { “courseName”: “写作”, “score”: 5.5, “order”: 2 }, { “courseName”: “口语”, “scoreFinal”: 5.5, “order”: 3 } ] }, “courseNames”: [ “听力”, “阅读”, “写作”, “口语”, “总分” ] }]获取到以上数据后,对数据稍微处理下:<table class=“table”> <thead> <tr> <th rowspan=“2”>序号</th> <th rowspan=“2”>姓名</th> <th rowspan=“2”>学号</th> <th rowspan=“2”>性别</th> <th rowspan=“2”>学期</th> <th colspan=“5” v-for="(it,i) in examDates" :key=“i”>{{it}}</th> </tr> <tr> <template v-for=“itd in examDates”> <th v-for="(itc,j) in courseNames" :key=“itd+j”>{{itc}}</th> </template> </tr> </thead> <tbody> <tr v-for="(item,index) in studentDataList" :key=“index”> <td>{{index+1}}</td> <td>{{item.studentName}}</td> <td>{{item.studentCode}}</td> <td>{{item.sex.name}}</td> <td>{{item.termName}}</td> <template v-for=“examDate in examDates”> <template v-for="(course,j) in courseNames"> <td :key=“examDate+j”> {{initScoreFinal(examDate,course,item.map)}} </td> </template> </template> </tr> </tbody> </table>—————- 分割线 ————————————— data () { return { studentDataList: [], examDates: [], courseNames: [] }, created () { this.getData () }, methods: { // getData () { this.$get( //该方法是封装过的axios ‘/list.json’, { ….//此处是参数,略 }, response => { this.examDates = response.data[0].examDates courseNames = response.data[0].courseNames this.studentDataList = response.data } ) }, initScoreFinal (examDate, course, map) { let scoreFinal = 0 console.log(‘map:’ + map) for (var it in map) { map[it].forEach((item, index, array) => { if (it === examDate && item.courseName === course) { scoreFinal = item.scoreFinal } }) } return scoreFinal }} 效果如图:再吐个槽在网上搜了很多合并单元格的都是简单的数据合并,也就是td合并,我们这边的项目需要的这个表格比较变态,按日期分组的去五门课程,且日期数目不定,又可能多个日期,这就很头疼了,总之长知识了,记录下来。 ...

November 3, 2018 · 2 min · jiezi