草庐IT

element-ui表格自定义动态列

终生耕田 2023-11-02 原文

element-ui表格自定义动态列

实现效果


具体功能

  1. 拖拽表头改变宽度
    1. 限制最小宽度, 实时保存设置。
  2. 隐藏列
    1. 选中列隐藏, 不显示在表格中。
    2. “勾选” 列和"操作" 列不可隐藏, 并且不包含在列控制组件中。
    3. 隐藏后, 无论是否冻结均不显示。有特殊标识则要另外做判断。
  3. 列冻结
    1. 开启时, 表示选中列靠左冻结。
    2. 只有"操作" 列允许靠右冻结且禁止更改。
    3. 列按照冻结>不冻结的顺序动态排序。
  4. 列拖动排序
    1. 拖动结束按照冻结>不冻结的顺序自动排序。
  5. 恢复默认配置
    1. 恢复表格的默认配置, 清除用户的自定义设置。
  6. 保存
    1. 只有保存后用户的相关设置才会生效(包含隐藏列, 列冻结和列拖动排序)。
  7. 取消
    1. 不保存用户的相关设置(包含隐藏列, 列冻结和列拖动排序)。

实现原理

相关数据格式

// 利用当前页面路由name值当作唯一标识, 存储到数据库中
pathSign: 'DataGnGoodsReport'

// 表头数据格式
[
	{
		"isFixed": "false",
		"isSortable": false,
		"label": "所属部门",
		"minWidth": 108,
		"show": true,
		"specialMask": "isStoreSp",	// 特殊标识, 可用于该特殊标识和show 双层判断隐藏该列, 需要做字符串转变量的处理
		"value": "departId",	// 值标识
		"width": 108
	}
]

页面配置

// 页面Table
<el-table
  border	// 边框
  row-key="id"
  :data="tableDataFormat"
  :row-style="{'height': '100%'}"
  @sort-change="sortChange"	// 排序方法
  @header-dragend="headerDragend"	// 表头拖拽方法
>
	// 根据表头数组动态渲染数据
	<template v-for="(item, index) in renderArr">
      <el-table-column
        v-if="item.specialMask ? item.show && strToVariable(item.specialMask) : item.show"	// 是否显示
        :key="index"
        resizable
        :prop="item.value"	// 列的值
        :label="item.label"	// 表头文案
        :min-width="item.minWidth"	// 最小宽度
        :width="item.width"	// 宽度
        :fixed="item.isFixed === 'false' ? false : item.isFixed"	// 是否冻结
        :align="item.align || 'center'"	// 文本对齐方式
        :sortable="item.isSortable && 'custom'"	// 当前列是否可排序(升降序)
      >   
      	<template #default="{row}">

					/* 根据显示类型不同, 做不同处理 */
        	<span v-if="item.value == name">{{ row[item.value] }}</span>
        	......

      	</template>
    	</el-table-column>
	</template>
</el-table>

// 引入列控制组件
<column-control ref="columnControl" :visible.sync="columnControlDialogVisible" :render-arr.sync="renderArr" @save="saveRenderArr" />

拖拽表头改变宽度

鼠标悬浮在表头上方才显示右边框

.table-container >>> .el-table--border th{
  border-right: none;
}
.table-container >>> .el-table__header th:hover {
  border-right: 1px solid #939599;
}
.table-container >>> .el-table--border td{
  border: 0!important;
}

表头拖拽

// 表头拖拽, 使用表格header-dragend钩子完成
headerDragend(newWidth, oldWidth, column, event) {
  const item = this.renderArr.find(item => item.label === column.label)
  // 取最小宽度
  if (newWidth <= item.minWidth) {
    column.width = item.minWidth
  } else {
    item.width = parseInt(column.width)
  }
  // 保存配置
  this.saveRenderArr(this.renderArr)
},

列控制组件封装

HTML

<template>
  <el-dialog
    title="列控制"
    :visible.sync="visible"
    width="60%"
    top="4vh"
    :before-close="handleColumnControl"
  >
    <div class="table-column">
      <el-table
        ref="columnTable"
        row-key="label"
        :data="renderArrCopy"
        height="70vh"
        :row-style="{'height': '100%'}"
      >
        <el-table-column
          label="#"
          type="index"
          width="50"
        />
        <el-table-column
          prop="label"
          label="列名"
        />
        <el-table-column
          prop="show"
          label="隐藏列"
        >
          <template slot-scope="{row}">
            // 隐藏值取反判断
            <el-switch v-model="row.show" :active-value="false" :inactive-value="true" />
          </template>
        </el-table-column>
        <el-table-column
          prop="show"
          label="列冻结"
        >
          <template slot-scope="{row}">
            <el-select v-model="row.isFixed" placeholder="请选择" :disabled="row.value === 'action'" @change="sortRenderArr">
              <el-option
                v-for="item in options"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </template>
        </el-table-column>
        <el-table-column
          label="拖动排序"
          align="center"
        >
          <svg-icon icon-class="drag2" class="drag-icon move" />
        </el-table-column>
      </el-table>
    </div>
    <div class="submit-box">
      <el-button @click="handleColumnControl">取消</el-button>
      <el-button type="primary" @click="save">保存</el-button>
    </div>
    <div class="default-box" @click="restoreDefault">恢复出厂设置</div>
    <div class="tips">表格列根据 冻结>不冻结 的顺序排序</div>
  </el-dialog>
</template>

JS

<script>
import Sortable from 'sortablejs'
import { deepClone } from '@/utils/index'

export default {
  name: 'ColumnControl',
  props: {
    visible: {
      type: Boolean,
      default: false
    },
    renderArr: {
      type: Array,
      default: function() {
        return []
      }
    }
  },
  data() {
    return {
      options: [
        {
          value: 'left',
          label: '冻结'
        },
        {
          value: 'false',
          label: '不冻结'
        }
      ],
      renderArrCopy: []	// 拷贝的表头数据
    }
  },
  computed: {
  },
  watch: {
    visible: function(newVal, oldVal) {
      newVal && this.rowDrop()
      this.deepClone()
    }
  },

  methods: {
    // 深拷贝
    deepClone() {
      // 组件内部表头数据和页面表头数据分离
      this.renderArrCopy = deepClone(this.renderArr)
    },
    // 列拖动排序
    rowDrop() {
      this.$nextTick(() => {
        // 要侦听拖拽响应的DOM对象
        const tbody = document.querySelector('.table-column .el-table__body-wrapper .el-table__body tbody')
        const _this = this
        Sortable.create(tbody, {
          animation: 20,
          // 结束拖拽后的回调函数
          onEnd({ newIndex, oldIndex }) {
            const currRow = _this.renderArrCopy.splice(oldIndex, 1)[0]
            _this.renderArrCopy.splice(newIndex, 0, currRow)
            const timer = setTimeout(() => {
              _this.sortRenderArr()
              clearTimeout(timer)
            }, 0)
          }
        })

        // 删除最后一项操作('操作' 列项不显示在该组件中。因数据上需要保留该项, 所以不可采用删除数据的方法来达到该效果, 只能从页面渲染做隐藏处理)
        const lastIndex = this.renderArrCopy.length - 1
        if (this.renderArrCopy[lastIndex].label === '操作') {
          // 获取'操作'项的那一行并删除
          const rows = document.querySelectorAll('.table-column .el-table__row')
          // 类数组转为数组
          Array.from(rows).forEach((item, index) => {
            index === lastIndex && item.remove()
          })
        }
      })
    },
    // 按冻结 > 不冻结排序
    sortRenderArr() {
      this.renderArrCopy.sort((star, next) => {
        const arr = ['left', 'false']
        return arr.indexOf(star.isFixed) - arr.indexOf(next.isFixed)
      })
    },
    // 取消
    handleColumnControl() {
      this.$emit('update:visible', false)
    },
    // 保存
    async save() {
      this.$emit('update:renderArr', this.renderArrCopy)
      this.handleColumnControl()
      this.$emit('save', this.renderArrCopy)
    },
    // 恢复默认配置
    restoreDefault() {
      this.$emit('save', [])
    }
  }
}
  </script>

全局方法

import { getRenderArr, saveRenderArr } from '@/api/common'
export const columnControlMixin = {
  data() {
    return {
      renderArr: [], // 动态列表头数组
      columnControlDialogVisible: false, // 动态列组件显示设置
    }
  },
  computed: {
    // 字符串转变量
    strToVariable() {
      return function(text) {
        return this[text]
      }
    }
  },
  created() {
    // 获取表头数据
    this.getRenderArr()
  },
  methods: {
    // 表头拖拽
    headerDragend(newWidth, oldWidth, column, event) {
      const item = this.renderArr.find(item => item.label === column.label)
      if (newWidth <= item.minWidth) {
        column.width = item.minWidth
      } else {
        item.width = parseInt(column.width)
      }
      this.saveRenderArr(this.renderArr)
    },
    // 根据name值获取表头数据
    async getRenderArr() {
      try {
        const res = await getRenderArr({
          pathSign: this.$route.name
        })
        if (res.code === 200) {
          this.renderArr = res?.data
        }
      } catch (error) {
        console.log(error)
      }
    },
    // 保存页面表头数据和重置
    async saveRenderArr(arr) {
      try {
        await saveRenderArr({
          pathSign: this.$route.name,
          content: arr || []
        })
        if (!arr.length) {
          await this.getRenderArr()
          // 列控制组件重新拷贝数据
          await this.$refs.columnControl.deepClone()
          this.$message({
            message: '恢复出厂设置成功',
            type: 'success'
          })
        }
      } catch (error) {
        console.log(error)
      }
    }
  }

}

有关element-ui表格自定义动态列的更多相关文章

  1. ruby - i18n Assets 管理/翻译 UI - 2

    我正在使用i18n从头开始​​构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在ruby​​onrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi

  2. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  3. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  4. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  5. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  6. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  7. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  8. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

  9. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  10. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

随机推荐