zoukankan      html  css  js  c++  java
  • vue制作表格table组件


    以前做的组件,留下来欣赏,这里用了scss和阿里矢量图标,可以自己用其他方式替代

    <template>
      <div class="FitTable" :style="tableStyle">
        <div class="progress" v-if="loading">
          <ScaleLoader class="progress-spinner" :loading="loading" :color="loadingColor"></ScaleLoader>
        </div>
        <div class="FitTable-header" ref="header" :style="headerStyle">
          <div class="FitTable-header__content">
            <div v-if="ifSelect" class="cell-select" :style="{height: headerHeight + 'px'}">
              <div class="cell-select-icon"></div>
            </div>
            <div v-if="selectData" class="FitTable-cell select-cell" :style="{ headerHeight + 'px'}">
              <div @click.stop="changeHeaderSelectStatus(selectHeaderStatus)" class="select-icon" :class="selectHeaderStatus ? ['icon-aliduihao1', 'selected']:['no-select']"></div>
            </div>
            <div v-for="(item, index) in columns" :key="item.unique || index" class="FitTable-cell"
                 :style="{minWidth: typeof item.minWidth === 'number'?item.minWidth + 'px':item.minWidth,
                  item.width,
                 textAlign: item.textAlign || 'center',
                 height: headerHeight + 'px'}"
                 :title="item.title">
              <slot :name="item.slotHeader" :data="{...item,index}">
                <div class="header-title">{{item.title}}
                  <div v-show="item.sort !== void(0)" class="table-sort">
                    <div class="table-sort-asc" :style="{opacity:(item.sort!=='desc')?1:0.3}" @click.stop="changeSort(item,'asc')"></div>
                    <div class="table-sort-desc" :style="{opacity:(item.sort!=='asc')?1:0.3}" @click.stop="changeSort(item,'desc')"></div>
                  </div>
                </div>
              </slot>
            </div>
          </div>
        </div>
        <div :style="tbodyHeight" ref="tableTbody">
          <FitScroll>
            <div class="FitTable-body" :style="{tableLayout:tableLayout}">
              <NoData :message="message" v-if="!loading&&!data.length"></NoData>
              <div class="FitTable-body-row">
                <template v-for="(item,index) in data">
                  <slot :name="`index-header${index}`">
                  </slot>
                  <div class="FitTable-body-row_line" :key="(dataKey && item[dataKey]) || index" :style="{height: lineHeight}">
                      <div class="cell-select" v-if="ifSelect">
                        <div class="cell-select-icon"></div>
                      </div>
                      <div v-if="selectData" class="FitTable-cell select-cell" :style="{ headerHeight + 'px'}">
                        <div @click.stop="changeSelectStatus(item[selectKey], selectDataSet.has(item[selectKey]))" class="select-icon" :class="selectDataSet.has(item[selectKey]) ? ['icon-aliduihao1', 'selected']:['no-select']"></div>
                      </div>
                      <div v-for="(_item, _index) in columns" :key="_item.unique || _index" class="FitTable-cell"
                           :style="{minWidth: typeof _item.minWidth === 'number'?_item.minWidth + 'px':_item.minWidth,
                            _item.width,
                           textAlign: _item.textAlign || 'center'}"
                            @click.stop="showExpand(index, _item.expand)">
                        <div class="FitTable-cell-content">
                          <div class="FitTable-cell-content-data">
                            <slot :name="_item.slot" :data="{...item,index, data: item}">
                              <span :title="item[_item.key] || _item.defaultVal" style="color: #DBE5E8;" v-if="!_item.expand">
                                {{item[_item.key] || _item.defaultVal}}
                              </span>
                              <span class="expand-icon" :class="expands.includes(index)?'icon-alileft':'icon-alibottom-triangle'" v-else></span>
                            </slot>
                          </div>
                        </div>
                      </div>
                  </div>
                  <div class="expand" :key="`expand-${(dataKey && item[dataKey]) || index}`" :style="expandStyle" v-show="expands.includes(index)">
                    <slot name="expand" :data="{...item,index, data: item}">
                    </slot>
                  </div>
                  <slot :name="`index-footer${index}`"></slot>
                </template>
              </div>
            </div>
          </FitScroll>
        </div>
      </div>
    </template>
    
    <script>
     /* eslint-disable */
    import ScaleLoader from 'vue-spinner/src/ScaleLoader.vue'
    import NoData from '../NoData'
     import resize from '@/mixins/eventResize';
    export default {
      name: "FitTable",
      components: {
        ScaleLoader,
        NoData
      },
      props: {
        maxHeight: {
          type: String,
          default: null
        },
        // 表格宽度,默认100%
         {
          type: String,
          default: '100%'
        },
        height: {
          type: Number | String,
          default: '100%'
        },
        ifSelect: {
          type: Boolean,
          default: false
        },
        // 起始索引
        // startIndex:{
        //   type: Number,
        //   default: 0
        // },
        /* TODO columns接受值: 对象数组
            title: 表头名称
            slotHeader: 表头插槽名称
            slot: 表体插槽
            defaultVal: 数据不存在,判断为false时的值,默认值
            unique: 默认索引值,必须唯一,便于vue优化
            key: 对应prop:{data}中数据的键值
            列固定宽度
            minWidth: 对应列最小宽度
            textAlign:文本对齐方式
            expand:展开expand插槽的信息
            onlySort: 设置为true,该字段排序时,其他字段排序重置
            sort: 默认排序方式 空字符为不排序,asc为升序, desc为降序
        */
        columns: {
          type: Array,
          default: () => []
        },
        data: {
          type: Array,
          default: () => []
        },
        // 唯一值,防止不必要的错误发生
        selectKey: {
          type: String,
          default: null
        },
        // 选择框
        selectData: {
          type: Array,
          default: null
        },
        message:{
          type: String,
          default: '查询不到数据'
        },
        headerHeight: {
          type: Number,
          default: 35
        },
        backgroundColor: {
          type: String,
          // default: '#3c6180',
          default: 'rgba(60,97,128,0.3)'
        },
        lineHeight: {
          type: String,
          default: ''
        },
        // prop:{data}中唯一的值,不传则通过索引优化表格
        dataKey: {
          type: String,
          default: null
        },
        loading: {
          type: Boolean,
          default: false
        },
        loadingColor: {
          type: String,
          default: '#45d1ea'
        },
        tableLayout: {
          type: String,
          default: 'fixed'
        }
      },
      mixins: [resize],
      computed: {
        tableStyle: {
          get () {
            return { this.width,maxHeight: this.maxHeight,height:typeof this.height === 'number' ?this.height + 'px':this.height};
          }
        },
        headerStyle () {
          return {this.tbodyWidth + 'px', backgroundColor: this.backgroundColor,tableLayout: this.tableLayout};
        },
        tbodyHeight () {
          const height = this.tableHeight > (this.maxHeight || Infinity)?this.maxHeight:this.tableHeight;
          return { height: height -this.headerHeight + 'px',overflowY: 'auto' };
        },
        expandStyle () {
          return {
             this.tbodyWidth + 'px'
          }
        },
        selectDataSet () {
          return this.selectData && new Set(this.selectData);
        },
        selectHeaderStatus () {
          if (this.selectDataSet && this.selectKey) {
            return this.selectDataSet.size === this.data.length;
          } else {
            return false;
          }
        }
      },
      watch: {
        height () {
          if (String(this.height).indexOf('%') !== -1) {
            this.tableHeight = this.$el.offsetHeight * parseInt(this.height) / 100
          } else {
            this.tableHeight = this.height;
          }
        }
      },
      data: () => ({
        rowWidth: null,
        // 将自定义的列width值收集起来的和
        customWidth: { count: 0, value: 0 },
        tbodyWidth: 0,
        tableHeight: 0,
        theaderHeight: 0,
        expands: []
      }),
      mounted() {
          // const width = this.$el.offsetWidth;
          // this.theaderHeight = this.$refs['header'].offsetHeight;
        this.$nextTick(() => {
          if (String(this.height).indexOf('%') !== -1) {
            this.tableHeight = this.$el.offsetHeight * parseInt(this.height) / 100;
          } else {
            this.tableHeight = this.height;
          }
          const getTbodyWidth = () => {
            requestAnimationFrame(() => {
              if (!this.$refs['tableTbody']) {
                return getTbodyWidth();
              }
              this.tbodyWidth = this.$refs['tableTbody'].clientWidth;
              if (!this.tbodyWidth) {
                getTbodyWidth();
              }
            })
          };
          getTbodyWidth();
        });
          // this.rowWidth = (width - this.customWidth.value) / (this.columns.length - this.customWidth.count) + 'px';
          // // eslint-disable-next-line no-console
          window.addEventListener('resize', this.resize);
          this.$once('hook:beforeDestroy', () => window.removeEventListener('resize', this.resize))
      },
      methods: {
        changeSort (item, type) {
          if (item.sort === 'asc' || item.sort === 'desc') {
            item.sort = ''
          } else {
            if (type === 'asc') {
              item.sort = 'desc'
            } else {
              item.sort = 'asc'
            }
          }
          if (item.onlySort) {
            this.columns.forEach(e => {
              if (e !== item && e.sort !== void (0)) {
                e.sort = ''
              }
            })
          }
          this.$emit('changeSort', item)
        },
        resize(el) {
          // if (this.width.indexOf('%') !== -1) {
          //   const width = this.$el.offsetWidth;
          //   this.rowWidth = (width - this.customWidth.value) / (this.columns.length - this.customWidth.count) + 'px'
          // }
          setTimeout(() => {
            this.theaderHeight = this.$refs['header'].offsetHeight;
            if (String(this.height).indexOf('%') !== -1) {
              // console.log(this.$el.offsetHeight,this,document.getElementsByClassName('FitTable')[0].offsetHeight, 8899)
              this.tableHeight = this.$el.offsetHeight * parseInt(this.height) / 100
            }
            this.tbodyWidth = this.$refs['tableTbody'].clientWidth;
          });
        },
        changeHeaderSelectStatus (status) {
          if (status) {
            this.selectData.splice(0, this.selectData.length);
          } else {
            this.data.forEach(e => {
              if (!this.selectDataSet.has(e[this.selectKey])) {
                this.selectData.push(e[this.selectKey]);
              }
            });
          }
        },
        changeSelectStatus (unique, status) {
          if (status) {
            this.selectData.reverse().forEach((e, index) => {
              if (e === unique) {
                this.selectData.splice(index, 1);
              }
            })
          } else {
            this.selectData.push(unique);
          }
        },
        showExpand (index, expand) {
          if (!expand) {
            return;
          }
          const findIndex = this.expands.findIndex(e => e === index);
          let ifExpand = true;
          if (~findIndex) {
            this.expands.splice(findIndex, 1);
            ifExpand = false;
          } else {
            this.expands.push(index);
          }
          this.$nextTick(() => {
            this.$emit('expand', {index, ifExpand});
          });
        }
      }
    }
    </script>
    
    <style lang="scss" scoped>
      @import "../../styles/_scroll";
      $sortWidth: 0.5vw;
      $sortColor: #ccc;
      $tableCell: table-cell;
      .FitTable {
        position: relative;
        .select-icon {
          margin: 0 auto;
           15px;
          height: 15px;
          border-radius: 2px;
        }
        .selected {
          display: flex;
          justify-content: center;
          align-items: center;
          font-size: 1rem;
          color: white;
          background-color: #3A97E2;
        }
        .no-select {
          border: 1px solid white;
        }
        &-header {
          display: table;
          box-sizing: border-box;
          // border-bottom: 1px solid rgba(255,255,255,0.32);
          &__content {
            display: table-header-group;
            border-bottom: 1px solid rgba(255,255,255,0.3);
            .cell-select {
               35px;
              display: $tableCell;
              position: relative;
              &-icon {
                position: absolute;
                left: 0;
                top: 0;
                right: 0;
                bottom: 0;
                margin: auto;
                height: 15px;
                 15px;
                border: 1px solid white;
              }
            }
            .FitTable-cell {
              display: $tableCell;
              vertical-align: middle;
              > div {
                //position: relative;
                //top: 50%;
                //transform: translateY(-50%);
                overflow: hidden;
                white-space: nowrap;
                text-overflow: ellipsis;
              }
              .header-title {
                margin: 1px 5px;
                color: #DBE5E8;
                display: inline-flex;
                align-items: center;
                .table-sort {
                  margin-left: 0.2vw;
                  display: inline-block;
                  &-asc {
                    cursor: pointer;
                    border-top: $sortWidth solid transparent;
                    border-bottom: $sortWidth solid $sortColor;
                    border-left: calc(#{$sortWidth} * 2 / 3) solid transparent;
                    border-right: calc(#{$sortWidth} * 2 / 3) solid transparent;
                    margin-bottom: 0.15vh;
                  }
                  &-desc {
                    cursor: pointer;
                    border-top: $sortWidth solid $sortColor;
                    border-bottom: $sortWidth solid transparent;
                    border-left: calc(#{$sortWidth} * 2 / 3) solid transparent;
                    border-right: calc(#{$sortWidth} * 2 / 3) solid transparent;
                    margin-top: 0.15vh;
                  }
                }
              }
            }
          }
        }
        &-body {
           100%;
          display: table;
          box-sizing: border-box;
          &-row {
            display: table-row-group;
            &_line {
              display: table-row;
              .cell-select {
                 35px;
                display: $tableCell;
                position: relative;
                &-icon {
                  position: absolute;
                  left: 0;
                  top: 0;
                  right: 0;
                  bottom: 0;
                  margin: auto;
                  height: 15px;
                   15px;
                  border: 1px solid white;
                }
              }
              &:nth-child(odd) {
                background-color: transparent;
              }
              &:nth-child(even) {
                background-color: rgba(158,158,158,0.1);
              }
              &:hover {
                background-color: rgba(158, 158, 158, 0.1);
              }
              .FitTable-cell {
                display: $tableCell;
                vertical-align: middle;
                &-content {
                  height: 30px;
                  padding: 1px 5px;
                  &-data {
                    position:relative;
                    top: 15px;
                    transform: translateY(-50%);
                    overflow: hidden;
                    white-space: nowrap;
                    text-overflow: ellipsis;
                    .expand-icon {
                      color: white;
                    }
                  }
                }
              }
            }
          }
        }
      }
      .progress {
        position: absolute;
        text-align: center;
         100%;
        height: 100%;
        // z-index: -1;
        &-spinner{
          position: absolute;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
        }
      }
    </style>
    
    
    
    
    
  • 相关阅读:
    exchart点击事件,空白无值也能点击触发
    两个界面来回切换展示(左下角 有小demo)
    警告滚动展示
    Oracle高级查询之OVER (PARTITION BY ..)
    解决安装office2007后,原来的*.doc文件的图标变成缩略图
    几个常用的文本处理shell 命令:find、grep、sort、uniq、sed、awk
    CentOS 7.3 安装配置 Nginx
    MySQL5.7.17源码编译安装与配置
    CentOS 7 源码安装 MySQL5.7
    Java命令学习系列(一)——Jps
  • 原文地址:https://www.cnblogs.com/smallZoro/p/12722344.html
Copyright © 2011-2022 走看看