antd vue 可拖拽 table 标题

172 阅读2分钟
页面
div class="ant-alert ant-alert-info">
    <div class="float-right">
      <ColumnsByOrder
        @onColSettingsChange="onColSettingsChange"
        @draggableChange="draggableChange"
        :columnsByOrder.sync="columnsByOrder"
        :settingColumns.sync="settingColumns"
      />
    </div>
</div>
<p-table ref="table" rowKey="id" :loading="loading" :data-source="tableData" bordered :columns="columns" :pagination="pagination" @change="handleTableChange" :scroll="{ x: 1000 }" :components="components">
    <template slot="no" slot-scope="text, record, index">
      <div>{{ index - 0 + 1 }}</div>
    </template>
    <template slot="returnMessage" slot-scope="text, record, index">
      <div class="text-eclipse" @click="showDetail(text,'json')">{{ text }}</div>
    </template>
    <template slot="messageBody" slot-scope="text, record, index">
      <div class="text-eclipse" @click="showDetail(text,'json')">{{ text }}</div>
    </template>
 </p-table>
  
ColumnsByOrder组件

<template>
  <p-popover
    trigger="click"
    placement="bottomRight"
    :overlayStyle="{minWidth: '300px'}"
  >
    <template slot="title">
      <p-row class="f-modal-title-rank" type="flex">
        <p-col class="left">
          <slot name="title">自定义列</slot>
        </p-col>
        <p-col class="right">
          <p-dropdown>
            <p class="poros-dropdown-link">
              <p-icon type="unordered-list" />
            </p>
            <p-menu slot="overlay">
              <p-menu-item>
                <div @click="rankBtn">{{ titleRank }}</div>
              </p-menu-item>
              <p-menu-item>
                <div @click="checkAll">全选</div>
              </p-menu-item>
            </p-menu>
          </p-dropdown>
        </p-col>
      </p-row>
    </template>
    <template slot="content">
      <p-checkbox-group
        @change="onColSettingsChange"
        v-model="settingColumnsNew"
        :defaultValue="settingColumnsNew"
      >
        <p-row>
          <template v-for="(item, index) in columnsByOrderNew">
            <template
              v-if="item.fixed == 'left'"
            >
              <p-col :span="spanWidth" :key="item.dataIndex">
                <p-checkbox :value="item.dataIndex"
                  >{{ index }}:{{ item.title }}</p-checkbox
                >
              </p-col>
            </template>
          </template>
          <draggable
            ghost-class="chosenClass"
            v-model="columnsByOrderNew"
            @change="draggableChange"
            filter=".unmover"
          >
            <template v-for="(item, index) in columnsByOrderNew">
              <template
                v-if="item.key != 'rowIndex' && item.dataIndex != 'action'&& item.fixed != 'left'"
              >
                <p-col :span="spanWidth" :key="item.dataIndex" :class="{'unmover':item.fixed=='left'}">
                  <p-checkbox :value="item.dataIndex"
                    >{{ index }}:{{ item.title }}</p-checkbox
                  >
                </p-col>
              </template>
            </template>
          </draggable>
        </p-row>
      </p-checkbox-group>
    </template>
    <a> <p-icon type="setting" />自定义列</a>
  </p-popover>
</template>
<script>
import draggable from 'vuedraggable'
export default {
  components: { draggable },
  props: {
    getWidth: {
      type: Object,
      default: () => ({}),
    },
    settingColumns: {
      type: Array,
      default: () => [],
    },
    columnsByOrder: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      spanWidth: 12,
      titleRank: '一列',
    }
  },
  computed: {
    settingColumnsNew: {
      get() {
        return this.settingColumns
      },
      set(val) {
        this.$emit('update:settingColumns', val)
      },
    },
    columnsByOrderNew: {
      get() {
        return this.columnsByOrder
      },
      set(val) {
        this.$emit('update:columnsByOrder', val)
      },
    },
  },
  methods: {
    rankBtn() {
      if (this.spanWidth === 12) {
        this.spanWidth = 24
        this.titleRank = '两列'
      } else {
        this.spanWidth = 12
        this.titleRank = '一列'
      }
    },
    checkAll() {
      let arr = []
      this.$emit('onColSettingsChange', this.columnsByOrder.map(item=>item.dataIndex))
    },
    onColSettingsChange(val) {
      this.$emit('onColSettingsChange', val)
    },
    draggableChange(val) {
      this.$emit('draggableChange', val)
    },
  },
}
</script>
<style scoped lang="less">
.f-modal-title-rank {
  justify-content: space-between;
}
.f-modal-title-rank .right {
  cursor: pointer;
}
.chosenClass {
  box-shadow: 1px 1px 8px 1px rgba(0, 0, 0, 0.2);
}
.list-group-item {
  cursor: pointer;
}
</style>


mixinjs

import $ from 'jquery'
import Vue from 'vue'
import ColumnsByOrder from '@/components/ColumnsByOrder'
import VueDraggableResizable from 'vue-draggable-resizable'
Vue.component('vue-draggable-resizable', VueDraggableResizable);

//defColumns 为默认的列
//columns 为当前的列
export const PageListMixin = {
  components: { ColumnsByOrder },
  data() {
    this.components = {
      header: {
        cell: (h, props, children) => {
          const { key, ...restProps } = props
          // const col = this.columns.find((col) => {
          //   const k = col.dataIndex || col.key
          //   return k === key
          // })
          let col = null
          if (key === "selection-column") {
            // 当前column为全选的时候,要返回全选的 th ,否则无法出现全选
            return <th {...restProps} class={props.class} width="50">{children}</th>
          } else {
            col = this.columns.find((item) => {
              const k = item.dataIndex || item.key;
              return k === key;
            });
          }
          if (key === 'action'|| key === 'operation') {
            return h('th', { ...restProps }, [...children])
          }

          if (!col || !col.width ) {
            return <th {...restProps} class={props.class} width="180">{children}</th>;
          }

          const dragProps = {
            key: col.dataIndex || col.key,
            class: 'table-draggable-handle',
            attrs: {
              w: 10,
              x: col.width,
              z: 10,
              axis: 'x',
              draggable: true,
              resizable: false,
            },
            on: {
              dragging: (x, y) => {
                col.width = Math.max(x, 1) < 100 ? 100 : Math.max(x, 1)
                // if (
                //   this.columns
                //     .slice(0, this.columns.length - 1)
                //     .map((item) => item.width || 0)
                //     .reduce((x, y) => x + y) >=
                //   document.querySelector('.poros-table-scroll').offsetWidth -
                //     130
                // ) {
                //   this.columns[this.columns.length - 1].width = 130
                // } else {
                //   delete this.columns[this.columns.length - 1].width
                // }
                $('tbody td').css('user-select', 'none')
                this.$forceUpdate()
                this.$nextTick(()=>{
                  this.setFixedWidth()
                })
              },
              dragstop: (x, y) => {
                $('tbody td').css('user-select', 'auto')
              }
            },
          }
          const drag = h('vue-draggable-resizable', { ...dragProps })
          return h('th', { ...restProps, class: 'resize-table-th' }, [
            ...children,
            drag,
          ])
        },
      },
    }
    return {
      columnsByOrder: [],
      settingColumns: [],
    }
  },
  created () {
    if(this.columns) {
      this.defColumns = JSON.parse(JSON.stringify(this.columns))
    }
  },
  mounted() {
    this.initColumns()
    this.draggableInit()
    // this.setdefColumns()
    this.$nextTick(() => {
      // 为了解决拖拽时,点击事件失效的问题
      let eles = document.querySelectorAll('.table-draggable-handle')
      for (let i = 0; i < eles.length; i++) {
        eles[i].onclick = function(e) {
          var ev = e || window.event
          if (ev && ev.stopPropagation) {
            ev.stopPropagation()
          } else {
            ev.cancelBubble = true
          }
        }
      }
    })
  },
  computed: {
  },
  methods: {
    update() {
      this.$forceUpdate()
    },
    throttle(fn) {
      let canRun = true
      return function() {
        if (!canRun) {
          return
        }
        canRun = false
        setTimeout(() => {
          fn.call(this, arguments)
          canRun = true
        }, 100)
      }
    },
    // 计算table前后fiexed的宽度
    setFixedWidth() {
      if(this.$refs.table) {
        let arrLength = this.columns.length - 1
        if(this.columns[arrLength]['dataIndex'] == 'action'|| this.columns[arrLength]['dataIndex'] == 'operation') {
          let lastThWidth = this.$refs.table.$el.querySelector('.poros-table-row-cell-last')?.offsetWidth
          this.$refs.table.$el.querySelector('.poros-table-fixed-right .poros-table-fixed').style.width = lastThWidth + 'px'
        }
        if(this.columns[0]['fixed'] == 'left') {
          const firstThWidth = this.$refs.table.$el.querySelector('.poros-table-scroll .poros-table-thead .resize-table-th')?.offsetWidth
          this.$refs.table.$el.querySelector('.poros-table-fixed-left .poros-table-fixed').style.width = firstThWidth + 'px'
        }
        this.$forceUpdate()
      }
    },
    arrayIsEqual(objArr1, objArr2, key, isString = true) {
      let arrFlag = false
      let arr1 = objArr1.map((item) =>
        isString ? item[key].trim() : item[key]
      )
      let arr2 = objArr2.map((item) =>
        isString ? item[key].trim() : item[key]
      )
      if (arr1.length != arr2.length) {
        return arrFlag
      }
      arr1.forEach((element, index) => {
        if (arr1.indexOf(arr2[index]) != -1 && arr2.indexOf(element) != -1) {
          arrFlag = true
        }
      })
      return arrFlag
    },
    //初始化列设置
    initColumns(customerName) {
      if(!this.defColumns) return
      let allSettingColumns = []
      this.defColumns.forEach(function(item, i, array) {
        allSettingColumns.push(item.dataIndex)
      })
      this.settingColumns = allSettingColumns
      this.columns = this.defColumns
    },
    draggableInit(customerName) {
      if(!this.defColumns) return
      this.columnsByOrder = this.defColumns.filter(
        (item) => item.key != 'rowIndex'&& item.dataIndex != 'action'&& item.dataIndex != 'operation'
      )
    },
    draggableChange(rest, customerName) {
      if (rest.moved) {
        const { newIndex, oldIndex } = rest.moved
        this.onOrderChange(customerName)
      }
    },
    //列设置更改事件
    onColSettingsChange(checkedValues, customerName) {
      this.settingColumns = checkedValues
      this.completeColumns()
    },
    onOrderChange(customerName) {
      // 自定义列排序
      this.completeColumns()
    },
    completeColumns() {
      const columns = this.columnsByOrder.filter((item) => {
        if (this.settingColumns.includes(item.dataIndex)) {
          return true
        }
        return false
      })
      let indexColumn = this.defColumns.find((item) => item.key == 'rowIndex')
      let actionColumn = this.defColumns.find(
        (item) => item.dataIndex == 'action' || item.dataIndex == 'operation'
      )
      indexColumn && columns.unshift(indexColumn)
      actionColumn && columns.push(actionColumn)
      this.columns = columns
      this.$nextTick(() => {
        this.setFixedWidth()
      })
      // this.setdefColumns && this.setdefColumns()
    },
    setdefColumns() {
      if (
        this.columns.map((item) => item.width || 0).reduce((x, y) => x + y) >
        window.innerWidth - 220
      ) {
        let arrLength = this.columns.length - 1
        this.columns.forEach((item, index) => {
          item['ellipsis'] = true
          item['field'] = item['dataIndex']
          if (
            !['action', 'rowIndex'].includes(item['key']) &&
            item['title'] != '操作' &&
            item['title'] != '操作内容'
          ) {
            item['sorter'] = item['unSorter'] ? false : true
          }
          delete item['fixed']
          if (item['key'] === 'rowIndex') {
            item['width'] = 60
          } else {
            item['width'] = item['width'] || 140
          }
        })
        this.columns[0]['fixed'] = 'left'
        this.columns[1]['fixed'] = 'left'
        if (
          this.columns[arrLength]['key'] == 'action' ||
          this.columns[arrLength]['title'] == '操作' ||
          this.columns[arrLength]['title'] == '操作内容'
        ) {
          this.columns[arrLength]['fixed'] = 'right'
        }
      } else {
        this.columns.forEach((item, index) => {
          item['ellipsis'] = true
          item['field'] = item['dataIndex']
          delete item['fixed']
          if (
            !['action', 'rowIndex'].includes(item['key']) &&
            item['title'] != '操作' &&
            item['title'] != '操作内容'
          ) {
            item['sorter'] =  item['unSorter'] ? false : true
            item['width'] = item['width'] || 140
          } else {
            item['width'] = 60
          }
        })
        this.columns.map((item) => item.width || 0).reduce((x, y) => x + y) <
          window.innerWidth - 220 &&
          delete this.columns[this.columns.length - 1].width
      }
    },
  },
}

image.png