当前位置: 首页 > news >正文

Vue2虚拟列表,umy-ui封装

一、起因

1、需求: 由于业务需求在页面一次性展示较多数据,不低于上千,但是每条数据涉及样式较多,数据渲染过多就会导致页面卡顿
2、满足: 大量数据加载;表格功能:列显隐、列顺序调整、固定、筛选、排序;表格调整存储本地
3、技术框架: 若依、Element UI、vue2

二、umy-ui

1、umy-ui库中的table表格组件,它不造轮子。它改造了element-ui等等库的表格组件。只为了免费解决前端小伙伴的问题。

2、用前须知(这是关于表格的须知,你应该认真读完下面的内容)

 1. 表格解决卡顿问题,那么虚拟表格原理呢大概就是: 减少对DOM节点的渲染,通过滚动函数节流实现滚动后事件来动态渲染数据2. 基础表格其实就是element的表格的升级版,修改了ele的表格bug(如果你想使用个普通表格你无需安装其他库,就使用这个表格即可),你可以发现基础表格里面的示例没有配置:use-virtual 这个属性。3 基础表格没有使用use-virtual属性,代表表格数据不多,只想要一个普通的表格。如果你表格卡。请你关注下虚拟表格部分。4. 使用u-table 开启use-virtual虚拟可以支持微小的合并行|列 如2列 2行,支持多级头, 超过2行2列可能布局错乱,因为虚拟滚动的原理导致某些节点并未渲染。4.5 使用u-table 开启use-virtual不支持开展行,如果需要展开行,你是要虚拟表格部分的ux展开行!5. u-table不支持展开行,需要展开行使用ux-grid6. ux-grid解决列多 行多导致卡的情况, u-table解决行多的情况,不解决列多的情况(如你的列超过70+,你可能就需要使用ux-grid了,因为此时你需要把列也虚拟)7. 重点:虚拟表格集成了基础表格的东西(如属性/方法/事件)!8. 虚拟表格在本文档中呢, 意思就是解决了数据量多导致卡顿的情况! 基础表格在文档中呢,意思就是升级版的el-table(但是没解决数据多卡的情况)!9. 编辑型表格呢,是解决那种表格单元带有输入框或者选择时间等等的情况,而导致卡顿的场景!意思就是表格单元格具有一定的操作,单元格有自定义组件或者UI库组件等等10. 有了表格,怎么导出表格数据为excel并且带样式呢?,[请点击](https://github.com/livelyPeng/pl-export-excel)

三、安装引入

1.安装
推荐使用 npm 的方式安装,它能更好地和 webpack 打包工具配合使用

 npm install umy-ui

2.引入
main.js

// 引入umy-ui
import UmyUi from 'umy-ui'Vue.use(UmyUi);

三、封装

以下代码是基于若依框架封装的主代码,其余见附带资源中,对应表格中输入或展示形式可自行封装:

<script>
export default {name: "SuperUxTable",props: {// 数据value: {type: [Array],require: true,},// 字典dict: {type: [Object],require: true,},// 分页page: {type: [Object],require: false,},// 模板columns: {type: [Array],require: true,},// 是否显示序号index: {type: Boolean,default: false,},// 是否显示单选radio: {type: Boolean,default: false,},// 是否显示多选checkbox: {type: Boolean,default: false,},// 是否显示分页pagination: {type: Boolean,default: false,},// 是否列操作convenitentOperation: {type: Boolean,default: false,},// 是否禁止选择selectable: {type: Function,default: () => {},},//storageKey: {type: String,},showSummary: {type: Boolean,default: false,},height: {type: [String, Number],require: false,},firstSummary: {type: Boolean,default: false,},},components: {ElDictTag: () => import("@/components/DictTag/index.vue"),ElDraggable: () => import("@/components/draggable/index.vue"),ElFilePreview: () => import("@/components/file-preview/index.vue"),ElComputedInput: () => import("@/components/computed-input/index.vue"),ElPopoverSelectV2: () => import("@/components/popover-select-v2/index.vue"),ElPopoverMultipleSelectV2: () =>import("@/components/popover-select-v2/multiple.vue"),ElComputedInputV2: () => import("@/components/computed-input-v2/index.vue"),ElPopoverTreeSelect: () =>import("@/components/popover-tree-select/index.vue"),ButtonHide: () => import("./hide.vue"),ButtonFreeze: () => import("./freeze.vue"),IconHide: () => import("./once/hide.vue"),IconSort: () => import("./once/sort.vue"),IconFreeze: () => import("./once/freeze.vue"),IconFilter: () => import("./once/filters.vue"),},data() {const { columns, storageKey } = this.$props;const localColumns = localStorage.getItem(storageKey);const innerColumns =storageKey && localColumns? JSON.parse(localColumns): columns.map(({ item, attr }) => ({attr,item: { hidden: true, ...item },}));return {innerColumns: innerColumns,rowKey: "id",// 选择selectData: [],selectState: false,// 过滤filterData: [],filterState: false,count: 0,scrollTop: 0,resizeHeight: 0,};},computed: {innerValue: {get() {if (this.filterState) {return this.filterData;} else if (this.selectState) {return this.selectData;} else {return this.$props.value;}},set(value) {this.$emit("input", value);},},showColumns: {get() {return this.innerColumns.filter(({ item }) => item.hidden);},set() {},},filterRules: {get() {return Object.fromEntries(this.innerColumns.filter(({ item }) => item.filter && !!item.filter.length).map(({ item }) => [item.key, item.filter]));},set() {},},tableHeight: {get() {let { height } = this.$props;return height ? height : this.resizeHeight;},set() {},},},watch: {filterRules: {handler: function (newValue) {function multiFilter(array, filters) {const filterKeys = Object.keys(filters);// filters all elements passing the criteriareturn array.filter((item) => {// dynamically validate all filter criteriareturn filterKeys.every((key) => {//ignore when the filter is empty Anneif (!filters[key].length) return true;return !!~filters[key].indexOf(item[key]);});});}this.filterState = JSON.stringify(newValue) !== "{}";this.filterData = multiFilter(this.$props.value, newValue);},},value: {handler: function (newValue) {if (this.value.length > 0) {this.$refs.superUxTable && this.$refs.superUxTable.clearSelection();}},immediate: true,deep: true,},},directives: {// 使用局部注册指令的方式resize: {// 指令的名称bind(el, binding) {// el为绑定的元素,binding为绑定给指令的对象let width = "",height = "";function isReize() {const style = document.defaultView.getComputedStyle(el);if (width !== style.width || height !== style.height) {binding.value(); // 关键}width = style.width;height = style.height;}el.__vueSetInterval__ = setInterval(isReize, 300);},unbind(el) {clearInterval(el.__vueSetInterval__);},},},methods: {resize() {this.resizeHeight =document.getElementsByClassName("el-super-ux-table")[0].offsetHeight -55;},//onSelectionChange(value) {this.selectData = value;this.$emit("row-select", this.selectData);},//onRowClick(row, column, event) {const { radio, checkbox } = this.$props;// 单选if (radio) {this.$emit("row-select", [row]);}// 多选if (checkbox) {this.$refs.superUxTable.toggleRowSelection([this.innerValue.find((item) => item.id === row.id),]);}},// 宽度onWidth({ column }) {this.innerColumns = this.innerColumns.map(({ item, attr }) => ({attr,item: {...item,width: item.key === column.property ? column.resizeWidth : item.width,},}));if (this.$props.storageKey) {localStorage.setItem(this.$props.storageKey,JSON.stringify(this.innerColumns));}},// 隐藏onHide(prop) {this.$nextTick(() => {this.$refs.superUxTable.doLayout();if (this.$props.storageKey) {localStorage.setItem(this.$props.storageKey,JSON.stringify(this.innerColumns));}});},// 排序onSort(prop) {const { key, sort } = prop;console.log(key, "key", sort, "sort");this.$nextTick(() => {this.$refs.superUxTable.sort(key, sort);this.$refs.superUxTable.doLayout();if (this.$props.storageKey) {localStorage.setItem(this.$props.storageKey,JSON.stringify(this.innerColumns));}});},// 冻结onFreeze() {this.$nextTick(() => {this.$refs.superUxTable.doLayout();if (this.$props.storageKey) {localStorage.setItem(this.$props.storageKey,JSON.stringify(this.innerColumns));}this.count++;});},// 过滤onFilter() {this.$nextTick(() => {this.$refs.superUxTable.doLayout();if (this.$props.storageKey) {localStorage.setItem(this.$props.storageKey,JSON.stringify(this.innerColumns));}});},onFilters(value) {const {item: { key },attr: { dictName },} = value;let dataList = [];const dict = this.dict.type[dictName];dataList = Array.from(new Set(this.innerValue.map((item) => item[key]).filter((item) => item))).map((item) => ({text: dictName? (dict.find((dictItem) => dictItem.value == item) || {}).label: item,value: item,}));return dataList;},// 继承el-table的MethodextendMethod() {const refMethod = Object.entries(this.$refs["superUxTable"]);for (const [key, value] of refMethod) {if (!(key.includes("$") || key.includes("_"))) {this[key] = value;}}},getSummaries({ columns, data }) {const means = []; // 合计let { firstSummary } = this.$props;columns.forEach((column, columnIndex) => {if (!firstSummary && columnIndex === 0) {means.push("合计");} else {const values = data.map((item) => Number(item[column.property]));let sumColumn = this.showColumns.filter(({ item, attr }) => attr.isSummary && item.key === column.property);// 合计// if (!values.every(value => isNaN(value))) {if (sumColumn.length) {means[columnIndex] = values.reduce((prev, curr) => {const value = Number(curr);if (!isNaN(value)) {return prev + curr;} else {return prev;}}, 0);means[columnIndex] = means[columnIndex].toFixed(2);} else {means[columnIndex] = "";}}});// sums[index] = sums[index] && sums[index].toFixed(2); // 保留2位小数,解决小数合计列return [means];},},created() {},mounted() {this.extendMethod();},updated() {this.$nextTick(() => {this.$refs.superUxTable.doLayout();});},destroyed() {},
};
</script><template><div class="el-super-ux-table" :key="count" v-resize="resize"><ux-gridborderrow-keyuse-virtualkeep-sourceshow-overflowbeautify-tableref="superUxTable"v-bind="$attrs":height="tableHeight"v-on="$listeners":data="innerValue":show-summary="showSummary":summary-method="getSummaries"@row-click="onRowClick"@header-dragend="onWidth"@selection-change="onSelectionChange":header-row-style="{color: '#515a6e',}"style="flex: 1"><!-- 多选 --><ux-table-columnv-if="checkbox"fixed="left"width="50"align="center"type="checkbox"resizablereserve-selection:column-key="rowKey"></ux-table-column><!-- 序号 --><ux-table-columnv-if="index"fixed="left"width="50"title="序号"type="index"align="center"class="is-index"resizable></ux-table-column><ux-table-columnv-for="({ item, attr }, index) in showColumns":key="item.key + index":field="item.key":title="item.title":fixed="item.fixed ? 'left' : undefined":width="item.width || 180":sortable="item.sortabled"resizableshow-overflow><template slot="header" slot-scope="scope"><template><span v-if="item.require" style="color: #ff4949">*</span><span:style="{color:item.sort ||item.fixed ||(item.filter && !!item.filter.length)? '#1890ff': '',}">{{ item.title }}</span><template><!-- <icon-sortv-if="item.sortabled"v-model="item.sort"@sort="onSort(item)"></icon-sort> --><icon-freezev-if="item.fixedabled"v-model="item.fixed"@freeze="onFreeze"></icon-freeze><icon-filterv-if="item.filterabled"v-model="item.filter":filters="onFilters({ item, attr })"@filter="onFilter"></icon-filter><icon-hidev-if="item.hiddenabled"v-model="item.hidden"@hide="onHide"></icon-hide></template></template></template><template slot-scope="scope"><slot :name="item.key" v-bind="scope" :item="item" :attr="attr"><template v-if="attr.is"><componentv-if="attr.is === 'el-dict-tag'"v-bind="attr":size="$attrs.size":value="scope.row[item.key]":options="dict.type[attr.dictName]"></component><componentv-else-if="attr.is === 'el-popover-select-v2'"v-bind="attr"v-model="scope.row[item.key]":title="item.title":size="$attrs.size":source.sync="scope.row"></component><componentv-else-if="attr.is === 'el-popover-multiple-select-v2'"v-bind="attr"v-model="scope.row[item.key]":title="item.title":size="$attrs.size":source.sync="scope.row"></component><componentv-else-if="attr.is === 'el-select'"v-bind="attr"v-model="scope.row[item.key]":size="$attrs.size"><template><el-optionv-for="item in dict.type[attr.dictName]":key="item.value":label="item.label":value="item.value"></el-option></template></component><componentv-elsev-bind="attr"v-model="scope.row[item.key]":size="$attrs.size"style="width: 100%"></component></template><template v-else><component v-if="attr.formatter" is="span">{{attr.formatter(scope.row)}}</component><component v-else is="span">{{scope.row[item.key] || "--"}}</component></template></slot></template></ux-table-column><slot></slot><!-- </el-table> --></ux-grid><divstyle="height: 50px;display: flex;justify-content: space-between;align-items: center;":style="{height: checkbox || pagination ? '50px' : '0px',}"><div class="mr-4"><template v-if="convenitentOperation"><button-hide v-model="innerColumns" @change="onHide"></button-hide></template></div><paginationv-if="pagination"v-show="!selectState":total="page.total":page.sync="page.pageNum":limit.sync="page.pageSize"@pagination="$emit('pagination', { ...$event })"style="height: 32px; padding: 0 !important; flex: 1; overflow-x: auto"/></div></div>
</template><style lang="scss" scoped>
.el-super-ux-table {position: relative;display: flex;flex: 1;flex-direction: column;overflow: auto;
}
::v-deep.el-super-ux-table .elx-cell {word-break: keep-all;white-space: nowrap;.icon-sort {display: none;}&:hover .icon-sort {display: inline-block;}.icon-freeze {display: none;}&:hover .icon-freeze {display: inline-block;}.icon-filter {display: none;}&:hover .icon-filter {display: inline-block;}.icon-hide {display: none;}&:hover .icon-hide {display: inline-block;}.elx-cell--sort {display: none;}&:hover .elx-cell--sort {display: inline-block;}
}::v-deep.uxbeautifyTableClass.elx-header--column.elx-resizable.is--line:before {height: 100%;background-color: #dfe6ec;
}
</style>

四、实例

<el-super-ux-tableindexv-model="materialInfo[item.key]":dict="dict":ref="tabName":columns="columns":size="$attrs.size":height="420"><!-- 判断是否禁用 --><template slot="drug" slot-scope="scope"><componentv-bind="scope.attr"v-model="scope.row[scope.item.key]":size="$attrs.size":source.sync="scope.row":disabled="!(scope.row.medicineMaterial === '0')"><el-optionv-for="item in dict.type[scope.attr.dictName]":key="item.value":label="item.label":value="item.value"></el-option></component></template><template slot="registrationNo" slot-scope="scope"><componentv-bind="scope.attr"v-model="scope.row[scope.item.key]":size="$attrs.size":source.sync="scope.row":disabled="!(scope.row.medicineMaterial === '0')"></component></template><ux-table-columnfixed="right"title="操作"width="120"align="center"><template slot="header" slot-scope="scope"><el-buttontype="text":size="$attrs.size"@click="useRowAdd(tabName)">增行</el-button></template><template slot-scope="scope"><el-buttontype="text":size="$attrs.size"@click.native.prevent="useRowRemove(tabName, scope)">删除</el-button><AmendantRecordv-if="tabName === 'materialBasic' &&addType === 'edit' &&scope.row.id"v-model="scope.row"></AmendantRecord></template></ux-table-column></el-super-ux-table>

相关文章:

  • 计算机网络之IP篇
  • 生产实践:Redis与Mysql的数据强一致性方案
  • springboot 整合 Spring Security 上篇
  • Dockerfile脚本编写流程及示例
  • 零信任组件和实施
  • RK3288升级WebView版本,替换webview app
  • ODN光纤链路全程衰减如何计算
  • 《python每天一小段》-- (10)爬取小说:斗罗大陆
  • 【C语言】深入理解指针(1)
  • 软件平台架构设计与技术管理之道笔记
  • [HTML]Web前端开发技术6(HTML5、CSS3、JavaScript )DIV与SPAN,盒模型,Overflow——喵喵画网页
  • 时间复杂度为 O(n^2) 的排序算法 | 京东物流技术团队
  • springboot 集成Dubbo2.7.8 ,连接zookeeper 提示错误 zookeeper not connected
  • 【电路笔记】-电阻器额定功率
  • hikvision SDK使用学习/实践
  • 10个最佳ES6特性 ES7与ES8的特性
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • CSS相对定位
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • Java比较器对数组,集合排序
  • Java超时控制的实现
  • Java程序员幽默爆笑锦集
  • js正则,这点儿就够用了
  • Objective-C 中关联引用的概念
  • Quartz初级教程
  • React 快速上手 - 07 前端路由 react-router
  • Redis 懒删除(lazy free)简史
  • Spring声明式事务管理之一:五大属性分析
  • WebSocket使用
  • 笨办法学C 练习34:动态数组
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 大快搜索数据爬虫技术实例安装教学篇
  • 对JS继承的一点思考
  • 高性能JavaScript阅读简记(三)
  • 计算机在识别图像时“看到”了什么?
  • 京东美团研发面经
  • 使用 Node.js 的 nodemailer 模块发送邮件(支持 QQ、163 等、支持附件)
  • 听说你叫Java(二)–Servlet请求
  • 通过几道题目学习二叉搜索树
  • 小程序 setData 学问多
  • Semaphore
  • 阿里云移动端播放器高级功能介绍
  • # 安徽锐锋科技IDMS系统简介
  • #pragma pack(1)
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (附源码)springboot学生选课系统 毕业设计 612555
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (六)软件测试分工
  • (七)Java对象在Hibernate持久化层的状态
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • (最完美)小米手机6X的Usb调试模式在哪里打开的流程
  • ./和../以及/和~之间的区别
  • .jks文件(JAVA KeyStore)