Bootstrap

vue表格设置的拖拽排序并且控制显示与隐藏的组件

前提概要:在这里插入图片描述
需求根据ant-design-pro react版本的样式和功能看上了一个高级表格的排序拖拽和显示隐藏并要求根据作缓存 客户下次打开的时候表格和原来设置的一样
可是查询了一下vue版本的并没有这种高级表格的组件 所以只好自己写一个了。
demo效果如下
在这里插入图片描述
来看看具体如何实现
首先控制表格列表数据

const columns = [
  { title: 'Full Name', width: 100, dataIndex: 'name',checkedSwitch: true, key: 'name', fixed: 'left' },
  { title: 'Age', width: 100, dataIndex: 'age', key: 'age',checkedSwitch: true, fixed: 'left' },
  { title: 'Column 1', width: 100, dataIndex: 'address',checkedSwitch: true, key: '1' },
  { title: 'Column 2', width: 100, dataIndex: 'address',checkedSwitch: true, key: '2' },
  { title: 'Column 3', width: 100, dataIndex: 'address',checkedSwitch: true, key: '3' },
  { title: 'Column 4', width: 100, dataIndex: 'address',checkedSwitch: true, key: '4' },
  { title: 'Column 5', width: 100, dataIndex: 'address',checkedSwitch: true, key: '5' },
  { title: 'Column 6', width: 100, dataIndex: 'address',checkedSwitch: true, key: '6' },
  { title: 'Column 7', width: 100, dataIndex: 'address',checkedSwitch: true, key: '7' },
  { title: 'Column 8', width: 100, dataIndex: 'address',checkedSwitch: true, key: '8' },
  { title: 'Column 9', width: 100, dataIndex: 'address',checkedSwitch: true, key: '9' },
  { title: 'Column 10', width: 150, dataIndex: 'address',checkedSwitch: true, key: '10' },
  { title: 'Column 11', width: 150, dataIndex: 'address',checkedSwitch: true, key: '11' },
  { title: 'Column 12', width: 150, dataIndex: 'address',checkedSwitch: true, key: '12' },
  { title: 'Column 13', width: 150, dataIndex: 'address',checkedSwitch: true, key: '13' },
  {
    title: 'Action',
    key: 'operation',
    checkedSwitch: true,
    fixed: 'right',
    width: 100,
    scopedSlots: { customRender: 'action' },
  },
];

通过checkedSwitch来控制表格列表的显示和隐藏,最后在传给table的数据只要做一遍过滤就行了。现在到三个固定在左侧、中间、右侧 用过ant的都知道fixed控制固定的,只要将数据改变一下就行了,拖拽插件使用的是vuedraggable,这里也遇到一个问题就是最后左右俩侧的数据如果改变了位置,必须要重新做一下排序,将fixed为左侧放在数据最前。以下是完全代码,这里我用的jsx写的vue,老实说都差不了多少,如果版本高的也可以直接复制粘贴去使用,版本低的,也可以自己试着改一下。
首先是 设置组件

import draggable from "vuedraggable";
let vm;


const Module = ({ props: {
  leftList,
  title,
  listName,
  isShow = true,
  type
}, data }) => {
  const switchChange = (v, index) => {
        leftList.map((item, key) => {
          if (index === key) {
            item.checkedSwitch = v.target.checked;
          }
        });
  }
  const getTop = (list) => {
    leftList.map((item) => {
      if (list.title === item.title) {
        delete item.fixed
        item.fixed = "left"
      }
    });
  }
  const getConted = (list) => {
    leftList.map((item) => {
      if (list.title === item.title) {
        delete item.fixed
      }
    });
  }
  const getBottom = (list) => {
    leftList.map((item) => {
      if (list.title === item.title) {
        delete item.fixed
        item.fixed = "right"
      }
    });
  }
  return <div class="module-class">
  <div style="margin-top: 6px;margin-bottom: 6px;color: rgba(0,0,0,.45);font-size: 12px;">{title}</div>
   <draggable
      list={leftList}
      onChange={() => data.on.click()}
    >
    {leftList.map((item,index) => {
    return  <div
        class="checkbox"
        style={"display: flex;align-items: center;justify-content: flex-start"}
      >
        <a-checkbox
          checked={item.checkedSwitch}
          onChange={$event => {switchChange($event, index); data.on.change(listName, leftList) }}>
        </a-checkbox>
        <div class="isVerticalAlignDiv" style="margin-left: 10px; display: flex;justify-content: space-between;width: 100%;">
          <span>{ item.title }</span>
          {isShow && <span class="isVerticalAlign" style={'display: flex;width: 35px;justify-content: space-around;'}>
          {/* 这里使用的同一个组件表格和列表是同一个  控制显示和隐藏*/}
          {listName !== 'contList' && <a-icon onClick={() => {getConted(item,listName, leftList);data.on.change(listName, leftList)}}  type="vertical-align-middle" style={{color: '#1890ff'}} /> }
          {listName !== 'leftList' && !item.children && type !=1 && <a-icon onClick={() => {getTop(item); data.on.change(listName, leftList)}} type="vertical-align-top" style={{color: '#1890ff'}}/> }
          {listName !== 'rightList' && !item.children && type !=1 && <a-icon onClick={() => {getBottom(item); data.on.change(listName, leftList)}} type="vertical-align-bottom" style={{color: '#1890ff'}}/> }
          </span> }
        </div>
      </div>
      })}
    </draggable>
  </div>

};

export default {
  name: 'tableSetting',
  components: {
    draggable,
  },
  props: {
    paramsColumns: {
      // 当前操作过后列,初始默认为所有
      type: Array,
      default: () => [],
    },
    columnsBase: {
      // 默认初始数据
      type: Array,
      default: () => [],
    },
    type: {
      type: Number
    }
  },
  data() {
    return {
      visible: false,
      currentColumns: [],
      filteredColumns: this.paramsColumns,
    };
  },
  watch: {
    currentColumns: {
      handler(val) {
        if (val) {
          console.log('%c 🍒 val: ', 'font-size:20px;background-color: #465975;color:#fff;', val);
        }
      },
      deep:true
    }
  },
  created() {
    vm = this;
    vm.currentColumns = JSON.parse(JSON.stringify(vm.columnsBase));
    // vm.currentColumns = vm.columnsBase;
  },
  mounted() {
    this.filteColumns();
    // 初始化数据源
    // vm.$emit("filterColumns", this.columnsBase);
  },
  computed: {
    leftList () {
      let arr =[]
      this.currentColumns.map(item => {
        if (item.fixed == 'left') {
          arr.push(item)
        }
      })
      return arr
    },
    contList () {
      let arr =[]
      this.currentColumns.map(item => {
        if (item.fixed !== 'left' && item.fixed !== 'right') {
          arr.push(item)
        }
      })
      return arr
    },
    scorllSize () {
      let size = 0
      this.currentColumns.map(item => {
        if (item.fixed !== 'left' && item.fixed !== 'right' && item.checkedSwitch === true) {
          size += item.width
        }
      })
      return size
    },
    rightList () {
      let arr =[]
      this.currentColumns.map(item => {
        if (item.fixed == 'right') {
          arr.push(item)
        }
      })
      return arr
    },
  },
  methods: {
    // 顺序排序
    bubbleSort(arr){
      let newArr
      newArr = arr.filter(res => res.fixed === 'left').concat(arr.filter(res => res.fixed !== 'left' && res.fixed !== 'right')).concat(arr.filter(res => res.fixed === 'right'))
      return newArr
    },
    // 恢复默认
    setRecovery() {
      this.currentColumns = this.clone(this.filteredColumns);
      vm.$emit("filterColumns", this.type, this.currentColumns);
    },
    changeList(name, event) {
      if(name === 'contList') {
        this.currentColumns = this.bubbleSort([...this.leftList, ...event, ...this.rightList])
      }else if(name === 'leftList') {
        this.currentColumns = this.bubbleSort([...event, ...this.contList, ...this.rightList])
      }else if(name === 'rightList') {
        this.currentColumns = this.bubbleSort([...this.leftList, ...this.contList, ...event])
      }
      this.$emit("filterColumns", this.type, this.currentColumns);
      this.$emit("scorllSize", this.scorllSize);
    },
    // 格式化控件里实时的字段的switch
    filteColumns() {
      this.currentColumns.map((item) => {
        if (typeof item.checkedSwitch === "undefined") {
          item.checkedSwitch = false;
        }
      });
    },
    clone(arr) {
      return JSON.parse(JSON.stringify(arr));
    },
    change() {
      this.currentColumns = [...this.leftList, ...this.contList, ...this.rightList]
      vm.$emit("filterColumns", this.type, this.currentColumns);
    },
    deepClone(obj){
      const isObject = args => (typeof args === 'object' || typeof args === 'function')
      if (!isObject) throw new Error('Not Reference Types')
      const newObj = Array.isArray(obj) ? [ ...obj ] : { ...obj }
      Reflect.ownKeys(newObj).map(key => {
        newObj[key] = isObject(obj[key]) ? this.deepClone(obj[key]) : obj[key]
      })
      return newObj
      }
  },
  render() {
    return  <div>
      <a-popover
      v-model={this.visible}
      trigger="click"
      placement="bottomRight"
      overlayClassName="poptip-class"
    >
      {this.$slots.default}
      <template slot="title" >
        <div class="header" style={"display: flex;align-items: center;justify-content: space-between;cursor: pointer;"}>
          <div class="title">列展示</div>
          <span class="setRecovery" style="color: #1890ff" onClick={() =>this.setRecovery()}>重置</span>
        </div>
      </template>
      <div class="popoverModule" slot="content" style={{width: '200px',cursor: 'pointer', height: '280px', overflow: 'auto', paddingBottom: '20px'}}>
       { this.leftList.length !==0 && <Module title={'固定在左侧'} leftList={this.leftList} onClick={this.change} listName={'leftList'} onChange={this.changeList}></Module> }
       { this.contList.length !==0 && <Module title={'不固定'} leftList={this.contList} type={this.type} onClick={this.change} listName={'contList'} isShow={(this.leftList.length+this.rightList.length) < 6} onChange={this.changeList}></Module> }
       { this.rightList.length !==0 && <Module title={'固定在右侧'} leftList={this.rightList} onClick={this.change} listName={'rightList'}  onChange={this.changeList}></Module> }
      </div>
    </a-popover>
    </div>
  }
}

然后是表格 使用方法

const columns = [
  { title: 'Full Name', width: 100, dataIndex: 'name',checkedSwitch: true, key: 'name', fixed: 'left' },
  { title: 'Age', width: 100, dataIndex: 'age', key: 'age',checkedSwitch: true, fixed: 'left' },
  { title: 'Column 1', width: 100, dataIndex: 'address',checkedSwitch: true, key: '1' },
  { title: 'Column 2', width: 100, dataIndex: 'address',checkedSwitch: true, key: '2' },
  { title: 'Column 3', width: 100, dataIndex: 'address',checkedSwitch: true, key: '3' },
  { title: 'Column 4', width: 100, dataIndex: 'address',checkedSwitch: true, key: '4' },
  { title: 'Column 5', width: 100, dataIndex: 'address',checkedSwitch: true, key: '5' },
  { title: 'Column 6', width: 100, dataIndex: 'address',checkedSwitch: true, key: '6' },
  { title: 'Column 7', width: 100, dataIndex: 'address',checkedSwitch: true, key: '7' },
  { title: 'Column 8', width: 100, dataIndex: 'address',checkedSwitch: true, key: '8' },
  { title: 'Column 9', width: 100, dataIndex: 'address',checkedSwitch: true, key: '9' },
  { title: 'Column 10', width: 150, dataIndex: 'address',checkedSwitch: true, key: '10' },
  { title: 'Column 11', width: 150, dataIndex: 'address',checkedSwitch: true, key: '11' },
  { title: 'Column 12', width: 150, dataIndex: 'address',checkedSwitch: true, key: '12' },
  { title: 'Column 13', width: 150, dataIndex: 'address',checkedSwitch: true, key: '13' },
  {
    title: 'Action',
    key: 'operation',
    checkedSwitch: true,
    fixed: 'right',
    width: 100,
    scopedSlots: { customRender: 'action' },
  },
];

const dataList = [
  {
    key: '1',
    name: 'John Brown',
    age: 32,
    address: 'New York Park',
  },
  {
    key: '2',
    name: 'Jim Green',
    age: 40,
    address: 'London Park',
  },
];
import setting from '@/components/tableSetting/index'
export default {
  data() {
    return {
      columns,
      dataList,
      totalScorllSize: 1200,
      columnsList: []
    }
  },
  components: {
    'table-filter': setting
  },
  created() {
    this.columnsList = this.columns
  },
  methods: {
    filterColumns(type, e) {
      console.log('%c 🍖 e: ', 'font-size:20px;background-color: #E41A6A;color:#fff;', e);
      console.log('%c 🍜 type: ', 'font-size:20px;background-color: #ED9EC7;color:#fff;', type);
      this.columnsList = e
      // this.$emit('setFilterList', type, JSON.parse(JSON.stringify(e)))
    },
    scorllSize(size) {
      console.log('%c 🍝 size: ', 'font-size:20px;background-color: #33A5FF;color:#fff;', size);
      this.totalScorllSize = size
    },
  },
  render() {
    return <div style={{padding: '16px 18px'}}>
      <div style={{display: 'flex',justifyContent: 'end'}}><table-filter
              style={"cursor: pointer;display: inline-block;font-size: 16px;"}
              columnsBase={this.columnsList}
              type={2}
              class="table-filter-setting"
              params-columns={this.columnsList}
              onFilterColumns={this.filterColumns}
              onScorllSize={this.scorllSize}
            >
              <a-icon class="table-filter-setting-icon" type="setting" style="padding-right: 10px" />
            </table-filter></div>
      <a-table columns={this.columnsList.filter(item => item.checkedSwitch === true)} dataSource={this.dataList} scroll={{ x: this.totalScorllSize }}>
        <a slot="action" slot-scope="text" href="javascript:;">action</a>
      </a-table>
    </div>
  }
}

好了 就这样吧

;