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

Vue2-封装一个含所有表单控件且支持动态增减行列的表格组件

效果

1. 无编辑权限:显示普通表格

2. 有编辑权限:根据配置显示编辑控件

3. 可以动态新增行,也可以动态新增列 

核心代码

无权限情况的核心代码

      <!-- 无编辑权限时显示普通表格 --><el-tablev-if="!hasEditPermission"ref="table":border="border":key="tablekey"fitv-bind="$attrs":element-loading-text="loadingText":data="tableData":header-cell-style="headerCellStyles":cell-style="cellStyles"v-on="$listeners"><!-- 复选框 --><el-table-columnv-if="showSelection"column-key="selection"v-bind="$attrs"type="selection"width="35"align="center"fixed/><!-- 序号 --><el-table-columnv-if="showIndex"column-key="index"label="序号"type="index"width="55"align="left"fixed/><!-- 所有列可扩展 --><el-table-columnv-if="showExpand"type="expand"width="25"align="center"fixed><slotslot-scope="scope"name="expand":elCol="scope.column":row="scope.row":rowIndex="scope.$index"/></el-table-column><!-- 表头数据 --><el-table-columnv-for="(column, cIndex) in tableColumns":key="cIndex":align="column.align || 'center'":prop="column.prop":label="column.label":width="column.width":sortable="column.sortable || false":type="column.type":fixed="column.fixed || (cIndex === 0 && firstColumnFixed)"v-bind="column.attrs"v-on="column.listeners"><!-- 表头的自定义插槽, 使用 slotHeaderName 和 slotHeaderIcon 定义--><templatev-if="column.slotHeaderName || column.slotHeaderIcon"#header><!-- 表头自定义插槽--><slotv-if="column.slotHeaderName":name="column.slotHeaderName":row="column"/><span v-if="column.slotHeaderIcon">{{ column.label }}</span><!-- 表头图标插槽:使用表头图标,默认添加弹出层,弹出层内容插槽名称为:表头插槽名称 + PopContent --><el-popoverv-if="column.slotHeaderIcon"class="header-slot-popup"placement="bottom":title="column.slotHeaderIconPopTitle || column.label":width="column.slotHeaderPopWidth"trigger="click"><div class="filter-content"><slotv-if="column.slotHeaderIcon"class="filter-slot":name="column.slotHeaderPopName + 'PopContent'"/></div><!-- 列对象中添加 slotHeaderIcon 属性,表示使用头部图标插槽--><svg-iconv-if="column.slotHeaderIcon"slot="reference"class="header-icon-slot":class="column.slotHeaderIconClass":icon-class="column.slotHeaderIcon"@click="headerIconClick(column)"/></el-popover></template><!-- 插入自定义模板到列中 --><slotv-if="column.textSlot"slot-scope="scope":name="column.textSlotName || 'slotTextCol'":colConf="column":elCol="scope.column":row="scope.row":rowIndex="scope.$index":colIndex="cIndex":rowspan="column.rowspan"/><!-- 扩展列插槽 --><template v-else-if="column.expandSlot"><slotv-if="column.expandSlot":name="column.expandSlot || 'slotExpend'":colConf="column":elCol="scope.column":row="scope.row":rowIndex="scope.$index":colIndex="cIndex":rowspan="column.rowspan"/></template><!--列内容插槽 --><template v-else slot-scope="scope"><div class="content"><el-checkboxv-if="column.checkbox":value="checked(scope.row, column)"@change="checkboxChange($event, scope.row, column)">{{ scope.row[column.prop] }}</el-checkbox><el-tooltipv-elseclass="tootip-container"effect="dark"placement="top":disabled="!column.tooltip"><template slot="content"><!-- 字符串长度大于 80 格式化 --><spanv-if="String(scope.row[column.prop]).length > 80":class="{ 'cursor-pointer hoveTitle': column.tooltip }"v-html="formateTooltip(String(scope.row[column.prop]))"/><span v-else>{{ String(scope.row[column.prop]) }}</span></template><p><span:class="{ 'cursor-pointer hoveTitle': column.tooltip }">{{column.formatter(scope.row,scope.column,scope.row[column.prop],scope.$index)}}</span></p></el-tooltip></div></template></el-table-column><!-- 固定列数据 --><el-table-columnv-if="showButtons"column-key="buttons":label="fixButConfig.label":align="fixButConfig.align":fixed="fixButConfig.fixed":width="fixButConfig.width"><slotslot="default"slot-scope="scope"name="buttons":$index="scope.$index":row="scope.row":column="scope.column"/></el-table-column><!-- 默认插槽 --><slot /></el-table>

 有权限情况的核心代码

      <!-- 有编辑权限时显示可编辑表格 --><el-form v-else :model="formModel" :rules="rules" :ref="formRef"><el-tableref="table":border="border":key="tablekey"fitv-bind="$attrs":element-loading-text="loadingText":data="tableData":header-cell-style="headerCellStyles":cell-style="cellStyles"v-on="$listeners"><!-- 复选框 --><el-table-columnv-if="showSelection"column-key="selection"v-bind="$attrs"type="selection"width="35"align="center"fixed/><!-- 序号 --><el-table-columnv-if="showIndex"column-key="index"label="序号"type="index"width="55"align="left"fixed/><!-- 删除行 --><el-table-columnv-if="hasEditPermission && showRemoveRow"label=""width="50"align="center"fixed><template slot-scope="scope"><el-buttonicon="el-icon-remove-outline"size="medium"type="text"circle:disabled="data.length <= 1"@click="removeRow(scope.$index)"></el-button></template></el-table-column><!-- 可编辑列 --><el-table-columnv-for="(column, cIndex) in tableColumns":key="cIndex":align="column.align || 'center'":prop="column.prop":label="column.label":width="column.width"v-bind="column.attrs"><!-- 表头的自定义插槽, 使用 slotHeaderName 和 slotHeaderIcon 定义--><templatev-if="column.slotHeaderName || column.slotHeaderIcon"#header><!-- 表头自定义插槽--><slotv-if="column.slotHeaderName":name="column.slotHeaderName":row="column"/><span v-if="column.slotHeaderIcon">{{ column.label }}</span><!-- 表头图标插槽:使用表头图标,默认添加弹出层,弹出层内容插槽名称为:表头插槽名称 + PopContent --><el-popoverv-if="column.slotHeaderIcon"class="header-slot-popup"placement="bottom":title="column.slotHeaderIconPopTitle || column.label":width="column.slotHeaderPopWidth"trigger="click"><div class="filter-content"><slotv-if="column.slotHeaderIcon"class="filter-slot":name="column.slotHeaderPopName + 'PopContent'"/></div><!-- 列对象中添加 slotHeaderIcon 属性,表示使用头部图标插槽--><svg-iconv-if="column.slotHeaderIcon"slot="reference"class="header-icon-slot":class="column.slotHeaderIconClass":icon-class="column.slotHeaderIcon"@click="headerIconClick(column)"/></el-popover></template><template #header v-else><span :class="{ 'required-label': isRequiredColumn(column) }">{{ column.label }}</span></template><template slot-scope="scope"><el-form-item:prop="`[${scope.$index}].${column.prop}`":rules="column.rules":style="column.style":class="column.className"><!-- 只读字段 --><template v-if="!column.type && !column.slot"><slotv-if="column.textSlot":name="column.textSlotName || 'slotTextCol'":colConf="column":elCol="scope.column":row="scope.row":rowIndex="scope.$index":colIndex="cIndex":rowspan="column.rowspan"/><el-tooltipv-elsev-bind="column.attrs"class="tootip-container"effect="dark"placement="top":disabled="!column.tooltip"><template slot="content"><!-- 字符串长度大于 80 格式化 --><spanv-if="String(scope.row[column.prop]).length > 80":class="{ 'cursor-pointer hoveTitle': column.tooltip }"v-html="formateTooltip(String(scope.row[column.prop]))"/><span v-else>{{ String(scope.row[column.prop]) }}</span></template><p><span:class="{ 'cursor-pointer hoveTitle': column.tooltip }">{{column.formatter(scope.row,scope.column,scope.row[column.prop],scope.$index)}}</span></p></el-tooltip></template><!-- 可编辑字段-根据列类型渲染不同表单控件 --><template v-else><el-inputv-if="column.type === $const.DialogCompType.input"v-model="data[scope.$index][column.prop]"class="item-inputs":placeholder="placeholderFormate(column)":style="{ width: column.width || '100%' }"v-bind="column.attrs"v-on="column.listeners"/><remote-search-selectorv-else-if="column.type === $const.DialogCompType.remoteSearchSelector":init-value="data[scope.$index][column.prop]"class="item-inputs":placeholder="placeholderFormate(column)"v-bind="column.attrs":options="column.optionList":show-value="column.showValue":remote-methods="column.remoteMethod":style="{ width: column.width || '100%' }"v-on="column.listeners"@valueChange="remoteSearchValueChage($event, data[scope.$index], column)"/><el-input-numberv-else-if="column.type === $const.DialogCompType.number"v-model="data[scope.$index][column.prop]"class="item-inputs":style="{ width: column.width || '100%' }":placeholder="placeholderFormate(column)"v-bind="column.attrs"v-on="column.listeners"/><el-inputv-else-if="column.type === $const.DialogCompType.textarea"v-model="data[scope.$index][column.prop]"class="item-inputs"type="textarea":style="{ width: column.width || '100%' }":placeholder="placeholderFormate(column)"v-bind="column.attrs":autosize="column.attrs && column.attrs.autosize? column.attrs.autosize: true"v-on="column.listeners"/><el-date-pickerv-else-if="column.type === $const.DialogCompType.datePicker"v-model="data[scope.$index][column.prop]"class="item-inputs":type="(column.attrs && column.attrs.type) || 'date'":placeholder="placeholderFormate(column)":style="{ width: column.width || '100%' }"v-bind="column.attrs"v-on="column.listeners"/><el-date-pickerv-else-if="column.type === $const.DialogCompType.dateTime"v-model="data[scope.$index][column.prop]"class="item-inputs"type="datetime":placeholder="placeholderFormate(column)":style="{ width: column.width || '100%' }"v-bind="column.attrs"v-on="column.listeners"/><date-picker-wrapv-else-if="column.type === $const.DialogCompType.datePickerColor":date="data[scope.$index][column.prop]"class="item-inputs":placeholder="placeholderFormate(column)":style="{ width: column.width || '100%' }"v-bind="column.attrs"v-on="column.listeners"/><el-date-pickerv-else-if="column.type === $const.FormCompType.daterange"v-model="data[scope.$index][column.prop]":type="(column.attrs && column.attrs.type) || 'daterange'":placeholder="placeholderFormate(column)":style="{ width: column.width || '100%' }":picker-options="{ disabledDate: daterangeDisabledDate }"v-bind="column.attrs"v-on="column.listeners"/><el-date-pickerv-else-if="column.type === $const.FormCompType.datetimerange"v-model="data[scope.$index][column.prop]"type="datetimerange":placeholder="placeholderFormate(column)":style="{ width: column.width || '100%' }"v-bind="column.attrs"v-on="column.listeners"/><el-switchv-else-if="column.type === $const.DialogCompType.switch"v-model="data[scope.$index][column.prop]"v-bind="column.attrs"v-on="column.listeners"/><el-dropdownv-else-if="column.type === $const.DialogCompType.dropdown"v-bind="column.attrs"v-on="column.listeners"><el-button v-bind="column.dropdownButtonsAttrs">{{ column.dropdownLabel }}<i class="el-icon-arrow-down el-icon--right" /></el-button><el-dropdown-menu slot="dropdown"><el-dropdown-itemv-for="dItem in column.dropdownItem":key="dItem.label"v-bind="dItem.attrs"v-on="dItem.listeners">{{ dItem.label }}</el-dropdown-item></el-dropdown-menu></el-dropdown><selector-wrapv-else-if="column.type === $const.DialogCompType.select":item="column":data="data[scope.$index]"v-bind="column.attrs"v-on="column.listeners"/><el-radio-groupv-else-if="column.type === $const.DialogCompType.radio"v-model="data[scope.$index][column.prop]"class="item-inputs":style="{ width: column.width || '100%' }"v-bind="column.attrs"v-on="column.listeners"><el-radiov-for="option in column.optionList":key="option.id || option.value":label="option.value"v-bind="option.attrs"v-on="option.listeners">{{ option.label }}</el-radio></el-radio-group><el-sliderv-else-if="column.type === $const.DialogCompType.slider"v-model="data[scope.$index][column.prop]"class="item-inputs"v-bind="column.attrs"v-on="column.listeners"/><el-alertv-else-if="column.type === $const.DialogCompType.alert"v-bind="column.attrs"/><el-dividerv-else-if="column.type === $const.DialogCompType.divider"v-bind="column.attrs"><span v-if="column.tips">{{ column.tips }}</span></el-divider><file-uploadv-else-if="column.type === $const.DialogCompType.files"class="item-inputs"v-bind="column.attrs"v-on="column.listeners"/><divv-else-if="column.type === $const.DialogCompType.buttons"class="dialog-buttons":style="{display: 'flex','justify-content':buttonsAlign[column.align] || 'flex-end',}"><el-buttonv-for="but in column.buttons":key="but.label":type="but.type"v-bind="but.attrs"size="small"@click="submitValidate(but.validate, but.reset, but.event)">{{ but.label }}</el-button></div><tinymcev-else-if="column.type === $const.DialogCompType.editor"v-model="data[scope.$index][column.prop]":height="column.height"/><slotv-else-if="column.slot":name="column.slotName || 'slotCol'":colConf="column":elCol="scope.column":row="scope.row":rowIndex="scope.$index":colIndex="cIndex":rowspan="column.rowspan"/></template></el-form-item></template></el-table-column><!-- 增加列 --><el-table-columnv-if="hasEditPermission && showAddColumn"label=""width="45"align="center"fixed='right'><template #header><el-buttonicon="el-icon-circle-plus-outline"size="medium"type="text"circle@click="addColumn"></el-button></template></el-table-column><!-- 操作列 --><el-table-columnv-if="showButtons"column-key="buttons":label="fixButConfig.label":align="fixButConfig.align":fixed="fixButConfig.fixed":width="fixButConfig.width"><slotslot="default"slot-scope="scope"name="buttons":$index="scope.$index":row="scope.row":column="scope.column"/></el-table-column></el-table></el-form><!-- 在表格下方添加操作按钮 --><div v-if="hasEditPermission" class="row-actions"><el-buttonv-if="showAddRow"type="text"size="medium "@click="addRow"icon="el-icon-circle-plus-outline">增加行</el-button><el-buttonv-if="showSave"type="text"size="medium "@click="saveData"icon="el-icon-circle-check">保存</el-button></div>

增减行列核心方法

    // 增加行addRow() {const newRow = {};this.columns.forEach((col) => {newRow[col.prop] = ""; // 初始化空值});this.tempData = [...this.tempData, newRow]; // 更新 tempDatathis.$emit("update:data", this.tempData);// 不再强制刷新整个表格// this.tablekey += 1;},// 验证表单validateForm() {return new Promise((resolve) => {this.$refs[this.formRef].validate((valid) => {if (valid) {resolve(true);} else {this.$message.error("请检查表单填写是否正确");resolve(false);}});});},// 删除行removeRow(index) {this.tempData.splice(index, 1);this.$emit("update:data", [...this.tempData]);// 不再强制刷新整个表格// this.tablekey += 1;},// 保存数据async saveData() {const isValid = await this.validateForm();this.$emit("update:data", [...this.tempData]);return isValid;},// 比较列配置是否相等isColumnsEqual(newColumns, oldColumns) {if (newColumns.length !== oldColumns.length) return false;return newColumns.every((newCol, index) => {const oldCol = oldColumns[index];return JSON.stringify(newCol) === JSON.stringify(oldCol);});},// 新增列async addColumn() {try {this.$emit('add-column', this.columns);this.$nextTick(() => {if (this.$refs.table) {this.$refs.table.doLayout();}});} catch (error) {this.$message.error('新增列操作出错,请稍后重试');}},
http://www.lryc.cn/news/602164.html

相关文章:

  • 行业案例:杰和科技为智慧教育构建数字化硬件底座
  • vue如何在data里使用this
  • 【保姆级喂饭教程】Python依赖管理工具大全:Virtualenv、venv、Pipenv、Poetry、pdm、Rye、UV、Conda、Pixi等
  • 热门JavaScript库“is“等软件包遭npm供应链攻击植入后门
  • 【SpringMVC】MVC中Controller的配置 、RestFul的使用、页面重定向和转发
  • 构建你的专属区块链:深入了解 Polkadot SDK
  • C语言-数组:数组(定义、初始化、元素的访问、遍历)内存和内存地址、数组的查找算法和排序算法;
  • 《 服务注册发现原理:从 Eureka 到 Nacos 的演进》
  • Docker搭建Hadoop集群
  • 【科普】STM32CubeMX是配置工具,STM32CubeIDE是集成开发环境,二者互补但定位不同,前者负责初始化配置,后者专注代码开发调试。
  • 魔术橡皮:一键抠图、一键去除图片多余物体软件、图片变清晰软件、图片转漫画软件、图片转素描软件
  • 【C++详解】深入解析继承 类模板继承、赋值兼容转换、派生类默认成员函数、多继承与菱形继承
  • sqli-labs通关笔记-第23关 GET字符型注入(单引号闭合-过滤注释符 手工注入+脚本注入两种方法)
  • 常见的接⼝测试⾯试题
  • 【深度学习优化算法】10:Adam算法
  • 力扣面试150题--颠倒二进制位
  • 医疗领域非结构化数据处理技术突破与未来演进
  • Java学习-----JVM的垃圾回收算法
  • 虚拟地址空间:从概念到内存管理的底层逻辑
  • Nuxt3 全栈作品【通用信息管理系统】修改密码
  • React中的合成事件解释和理解
  • 架构实战——互联网架构模板(“开发层”和“服务层”技术)
  • DevOps时代的知识治理革命:Wiki如何成为研发效能的新引擎
  • 并发安全之锁机制一
  • 小架构step系列28:自定义校验注解
  • “太赫兹”
  • KubeSphere理论及实战
  • Error reading config file (/home/ansible.cfg): ‘ACTION_WARNINGS(default) = True
  • 什么是3DVR?VR技术有哪些应用场景?
  • 关于sql面试积累