Vue 表格自定义树形组件,行,列表可传入等

依然范特西╮ 提交于 2020-08-09 11:12:13

1.组件,BaseTable 下有两个文件,index.vue ,extend.js

index.vue代码

<template>
  <div class="crud">
    <!--crud头部,包含列表标题和可操作按钮-->
    <el-row class="crud-header">
      <!--<el-col :span="8">{{formTitle}}列表</el-col>-->
      <el-col :span="24">
        <el-button type="success" size="mini" v-if="gridBtnConfig.create" @click="createOrUpdate(null)">
          <i class="el-icon-plus"></i> 新增
        </el-button>

        <!-- 标记为single的按钮不需要禁用,不依赖选择数据-->
        <el-button :disabled="!item.single && multipleSelection.length === 0" size="mini"
                   @click="handleEmit(item.emitName,'outer')"
                   v-for="(item,index) in outerButton" :key="index" :type="item.type?item.type:'primary'">
          <svg-icon v-if="item.icon" :icon-class="item.icon"></svg-icon>
          {{item.name}}
        </el-button>
        <el-button :disabled="multipleSelection.length === 0" size="mini" v-if="gridBtnConfig.delete" type="danger"
                   @click="remove(2)">
          <svg-icon icon-class="delete"></svg-icon>
          删除
        </el-button>
      </el-col>
    </el-row>

    <!--crud主体内容区,展示表格内容-->
    <el-table
      ref="multipleTable"
      v-loading="listLoading" element-loading-text="请等待"
      :data="showGridData"
      @selection-change="handleSelectionChange"
      border
      :stripe="true"
      :header-cell-style="{'textAlign':'center'}"
      style="width: 100%">
      <el-table-column
        v-if="!hideMultiple"
        fixed="left"
        type="selection"
        width="55">
      </el-table-column>
      <el-table-column
        v-if="hideMultiple"
        fixed="left"
        label="序号"
        type="index"
        width="55">
      </el-table-column>
      <el-table-column
        v-for="(item,index) in gridConfig"
        :key="index"
        :prop="item.prop"
        :label="item.label"
        :align="nameLeft(item.label)"
        show-overflow-tooltip
        :width="item.width?item.width:''">
        <template slot-scope="scope">
          <span v-if="item.filter" ref="yifutian">{{walkProperty(item.prop,scope.row)||walkProperty(item.prop,scope.row)==0? item.filter(walkProperty(item.prop,scope.row)) : '--'}}</span>
          <Cell
            v-if="item.render"
            :row="scope.row"
            :column="item"
            :index="scope.$index"
            :render="item.render"></Cell>
          <span v-if="!item.render&&!item.filter">
            <span v-if="!item.template" ref="yifutian">{{walkProperty(item.prop,scope.row)||walkProperty(item.prop,scope.row)==0 ? walkProperty(item.prop,scope.row) : '--'}}</span>
            <a style="color: #1582cf;cursor: pointer;" v-else @click="handleEmit(item.emitName,'inner',scope.row)">{{walkProperty(item.prop,scope.row)||walkProperty(item.prop,scope.row)==0 ? walkProperty(item.prop,scope.row) : '--'}}</a>
          </span>
        </template>
      </el-table-column>
      <el-table-column v-if="!hideEditArea" label="操作" :width="gridEditWidth?gridEditWidth:200" fixed="right">
        <template slot-scope="scope">
          <el-tooltip v-if="gridBtnConfig.update" effect="dark" content="修改" placement="top">
            <span style="color: #409EFF" class="edit-icon" @click="createOrUpdate(scope.row)"><svg-icon
              icon-class="xg"></svg-icon></span>
          </el-tooltip>
          <el-tooltip v-if="gridBtnConfig.view" effect="dark" content="查看" placement="top">
            <span style="color: #409EFF" class="edit-icon" @click="viewDetail(scope.row)"><svg-icon
              icon-class="view"></svg-icon></span>
          </el-tooltip>
          <!--扩展按钮,如果扩展按钮设置了动态显示隐藏的条件,则需要根据条件显示隐藏-->
          <el-tooltip v-for="(item,index) in innerButton" :key="index" :type="item.type?item.type:'primary'"
                      v-if="(!item.hideJudge)||(item.hideJudge &&  walkProperty(item.hideJudge.key,scope.row) === item.hideJudge.val && (item.hideJudge.key2 ? walkProperty(item.hideJudge.key2,scope.row) === item.hideJudge.val2 : true))"
                      effect="dark" :content="item.name" placement="top">
            <span :style="{color:item.iconColor?item.iconColor:'#409EFF'}" class="edit-icon"
                  @click="handleEmit(item.emitName,'inner',scope.row)"><svg-icon
              :icon-class="item.icon?item.icon:'xg'"></svg-icon></span>
          </el-tooltip>
          <el-tooltip v-if="gridBtnConfig.singleDelete" effect="dark" content="删除" placement="top">
            <span style="color: #F56C6C" class="edit-icon" @click="remove(1,scope.row)"><svg-icon
              icon-class="delete"></svg-icon></span>
          </el-tooltip>
        </template>
      </el-table-column>

    </el-table>

    <!--crud的分页组件-->
    <div class="crud-pagination">
      <!--如果不是异步请求展示数据,需要隐藏分页-->
      <el-pagination
        v-if="isAsync"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="currentPage"
        :page-sizes="pageSizes?pageSizes:[10, 20, 30, 40]"
        :page-size="currentPageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="dataTotal">
      </el-pagination>
    </div>

    <!--crud按钮触发的表单弹窗-->
    <BaseDialogForm :width="dialogWidth" :label-width="dialogLabelWidth" :title="dialogTitle" ref="dialogForm"
                    :config="formConfig"
                    :form-data="formModel"
                    @submit="dialogSubmit"></BaseDialogForm>

    <!--查看详情模态框-->
    <gx-dialog
      title="查看详情"
      :visible.sync="detailDialogVisible"
      :width="dialogWidth ? dialogWidth : '1300px'">
      <ViewDetail :config="infoConfig?infoConfig:formConfig" :data="detailData"></ViewDetail>
    </gx-dialog>

    <el-dialog
      title="提示"
      :close-on-click-modal="false"
      :visible.sync="deletess"
      width="400px">
      <i class="el-icon-info" style="color:#E6A23C;"></i>
      <span>此操作将永久删除数据, 是否继续?</span>
      <span slot="footer" class="dialog-footer">
         <el-button type="primary" @click="deletes" size="mini">确 定</el-button>
         <el-button @click="qxss" size="mini">取 消</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
  import BaseDialogForm from '@/components/BaseDialogForm/index.vue'
  import ViewDetail from '@/components/ViewDetail/index.vue'
  import GxDialog from "../GxDialog/index"
  /*用render渲染自定义template*/
  import Cell from './expand';

  export default {
    name: "base-crud",
    components: {
      BaseDialogForm,
      ViewDetail,
      GxDialog,
      Cell
    },
    props: [
      //
      'pageSizes',
      // 表单标题,例如用户、角色
      'formTitle',
      // 表单配置
      'formConfig',
      //详情的字段配置
      'infoConfig',
      // 表单的model数据
      'formData',
      // 表格配置
      'gridConfig',
      // 表格按钮配置
      'gridBtnConfig',
      // 表格死数据
      'gridData',
      // 数据接口
      'apiService',
      // 判断是否是异步数据
      'isAsync',
      //  表格编辑区域宽度
      'gridEditWidth',
      //  是否隐藏表格操作
      'hideEditArea',
      // 自定义新增和修改操作,覆盖组件内的新增和删除逻辑
      'selfCreateAndUpdate',
      //是否显示多选,若不显示多选,则显示列表序号
      'hideMultiple',
      //数据的id标识字符串
      'idKey',
      //表格获取数据默认传入的参数
      'gridParams',
      //
      'Update',
      //  禁止删除的条件配置
      'forbiddenData',
      //  操作的模态框宽度
      'dialogWidth',
      // 操作模态框中的表单label宽度
      'dialogLabelWidth',
      //  状态颜色区别配置数组:[[未处理],[已处理],[已过期],[其它状态]]
      'statusobj'
    ],
    data() {
      return {
        // 新增修改模态框title
        dialogTitle: '',
        // 展示的表格数据,数据来源可能是父组件传递的固定数据,可能是接口请求数据
        showGridData: [],
        // 当前页码
        currentPage: 1,
        // 每页显示数量
        currentPageSize: 10,
        // 列表数据总数
        dataTotal: 0,
        // 选中行数据
        selectedRow: '',
        // 表单数据
        formModel: {},
        //  保留多选的表格数据
        multipleSelection: [],
        //  扩展按钮,包括表格内按钮和表格外的批量操作按钮
        innerButton: [],
        outerButton: [],
        //  详情数据
        detailData: {},
        detailDialogVisible: false,
        //  是否是新增
        isCreate: true,
        // 数据加载
        listLoading: false,
        //  查询参数
        searchParams: {},
        //删除的数据
        ids: [],
        //删除的弹框
        deletess: false,
        color: true,
        statusObj: ''
      }
    },
    mounted() {
      //初始化扩展按钮
      if (this.gridBtnConfig.expands && this.gridBtnConfig.expands.length > 0) {
        //内部按钮,只操作单行数据
        this.innerButton = this.gridBtnConfig.expands.filter(item => item.editType === 'inner');
        //外部按钮,操作批量数据
        this.outerButton = this.gridBtnConfig.expands.filter(item => item.editType === 'outer');
      }
      this.getData();
      this.statusObj = this.statusobj ? this.statusobj : [['未提交'], ['已提交', '已批复', '审批通过', '待上报', '已批复', '已撤回'], ['审核中', '返回修改'], ['已上报', '待批复'], ['审批不通过']];
    },
    methods: {
      nameLeft(name) {
        let a = name.substr(name.length - 2, 2);
        // console.log(name.indexOf('名称'),a);
        if (a == '名称') {
          return 'left'
        } else {
          return 'center'
        }
      },
      // 获取列表数据
      getData() {
        this.listLoading = true;
        //没有定义api,直接返回,不进行请求
        if (!this.apiService) {
          this.listLoading = false;
          this.showGridData = this.gridData;
          return;
        }
        let params = {
          current: this.currentPage,
          size: this.currentPageSize
        };

        params = Object.assign({}, params, this.gridParams, this.searchParams);

        this.apiService.list(params).then(res => {
          this.showGridData = res.page.list;
          this.dataTotal = res.page.totalCount;
          this.listLoading = false;
          //请求数据完成
          this.$emit('getDataOver', res.page.list);
        });
      },
      //查询
      search(params) {
        this.currentPage = 1
        this.currentPageSize = 10
        this.searchParams = Object.assign({}, params);
        this.getData();
      },
      //清空查询
      clearSearch() {
        this.searchParams = {};
        this.getData();
      },
      // 查看详情
      viewDetail(row) {
        this.detailData = Object.assign({}, row);
        this.detailDialogVisible = true;
      },
      createOrUpdate(item) {
        // 如果配置了覆盖组件内新增和修改按钮时执行
        if (this.selfCreateAndUpdate) {
          this.$emit('createOrUpdate', item);
          return;
        }

        this.$refs.dialogForm.resetForm();

        this.formModel = item ? Object.assign({}, item) : Object.assign({}, this.formData);
        this.dialogTitle = (item ? '修改' : '新增') + this.formTitle;
        this.isCreate = !item;

        this.$refs.dialogForm.showDialog();
      },
      // 处理相应父组件的事件方法
      handleEmit(emitName, type, row) {
        // 如果是表格内按钮,只需要回传单行数据,表格外按钮,则需要回传批量数据
        if (type === 'inner') {
          this.$emit(emitName, row);
        }
        else {
          this.$emit(emitName, this.multipleSelection);
        }
      },
      handleCurrentChange(page) {
        this.currentPage = page;
        this.getData();
      },
      handleSizeChange(size) {
        this.currentPageSize = size;
        this.getData();
      },
      // 处理点击行
      handleRowClick(row, event, column) {
        this.selectedRow = Object.assign({}, row);
      },
      // 模态框数据提交
      dialogSubmit(data) {
        if (this.Update) {
          this.$emit('Update', data);
          return;
        }
        this.apiService[this.isCreate ? 'create' : 'update'](data).then(res => {
          this.$message.success(this.dialogTitle + '成功!');
          this.getData();
          this.$refs.dialogForm.hideDialog();
        })
      },
      hideDialog() {
        this.$refs.dialogForm.hideDialog();
      },
      //处理多选改变时
      handleSelectionChange(val) {
        this.multipleSelection = val;
      },
      // 切换当前行的选中状态
      toggleSelection(rows) {
        if (rows) {
          this.$nextTick(()=>{
            rows.forEach(row => {
              this.$refs.multipleTable.toggleRowSelection(row);
            });
          })
        } else {
          this.$refs.multipleTable.clearSelection();
        }
      },
      //删除方法
      remove(type, data) {
        this.ids = [];

        //type为1代表单个删除,2代表批量删除
        if (type === 1) {
          this.ids = [data[this.idKey ? this.idKey : 'id']];
        } else {
          //如果上传了禁止删除的条件,进入下面条件处理
          if (this.forbiddenData) {
            let key = this.forbiddenData.key;
            let arr = this.forbiddenData.data;

            let boolean = false;
            this.multipleSelection.forEach(item => {
              arr.forEach(val => {
                if (val === item[key]) {
                  return (boolean = true);
                }
              });
              //如果已经有禁止删除项,退出遍历,优化性能
              if (boolean) {
                return false;
              }
            });

            if (boolean) {
              return this.$notify({
                title: '警告',
                message: '您选择的数据包含禁止删除项,请重新选择数据进行删除!!!',
                type: 'warning',
                position: 'top-left',
                duration: 2000
              });
            }
          }

          this.ids = this.multipleSelection.map(item => item[this.idKey ? this.idKey : 'id']);
        }

        // this.$confirm('此操作将永久删除数据, 是否继续?', '提示', {
        //   showCancelButton: true,
        //   confirmButtonText: '确定',
        //   cancelButtonText: '取消',
        //   confirmButtonClass:'confirmButtonClass',
        //   type: 'warning'
        // }).then(() => {
        //   this.apiService.delete(ids).then(res => {
        //     this.$message({
        //       type: 'success',
        //       message: '删除成功!'
        //     });
        //
        //     this.getData();
        //   })
        // }).catch(() => {
        //   this.$message({
        //     type: 'info',
        //     message: '已取消删除'
        //   });
        //
        //
        // });
        this.deletess = true
      },
      deletes() {
        this.apiService.delete(this.ids).then(res => {
          this.$message({
            type: 'success',
            message: '删除成功!'
          });

          this.getData();
          this.deletess = false
        })

      },
      qxss() {
        this.deletess = false
        this.$message({
          type: 'info',
          message: '已取消删除'
        });

      },

      walkProperty(prop, data) {
        //a.b
        if (prop && prop.indexOf('.') !== -1) {
          let path = prop.split('.'), i, v = data;//[a, b]
          while (i = path.shift()) {
            if (v && v[i]) {
              v = v[i];
            }
            else {
              v = '';
            }
          }
          return v;
        }
        else {
          return data[prop];
        }
      },
      //清空多选
      clearSelection() {
        //清空多选的选中
        this.$refs.multipleTable && this.$refs.multipleTable.clearSelection();
      },
      shangse() {
        let i = 1;
        let elementArrays = this.$refs.yifutian;
        if (!elementArrays) {
          return;
        }
        this.statusObj.forEach(obj => {
          obj.forEach(obj2 => {
            elementArrays.forEach(item => {
              let str = item.innerText;
              if (str === obj2) {
                item.setAttribute('class', 'class' + i);
              }
            })
          });
          i++;
        });
      },
      setGridParams(data) {
        this.search(data);
      }
    },
    watch: {
      // 防止表格预置数据不成功,涉及生命周期问题
      gridData() {
        this.showGridData = this.gridData;
        this.dataTotal = this.showGridData.length;
      },
      showGridData: function () {
        this.$nextTick(function () {
          this.shangse();
        })
      }
    },
  }

</script>
<style rel="stylesheet/scss" lang="scss" scoped>
  .crud {
    .crud-header {
      margin-bottom: 10px;
      line-height: 40px;
    }

    .crud-pagination {
      text-align: right;
      margin-top: 10px;
    }

    /*表格内编辑图标*/
    .edit-icon {
      font-size: 20px;
      margin-right: 10px;
      cursor: pointer;
    }
    /*未处理*/
    .class1 {
      color: #999999;
      padding: 5px;
    }
    /*已处理*/
    .class2 {
      color: #06AC4D;
      padding: 5px;
    }
    /*已过期*/
    .class3 {
      color: #FBBE42;
      padding: 5px;
    }
    /*其它状态*/
    .class4 {
      color: #3DB5FC;
      padding: 5px;
    }

    .class5 {
      color: #FC5454;
      padding: 5px;
    }
  }
</style>
<style>
  .confirmButtonClass {
    color: #fff;
    background-color: #0597D2 !important;
    border-color: #0597D2 !important;
  }

</style>

--extend.js代码

export default {
    name: 'TableExpand',
    functional: true,
    props: {
        row: Object,
        render: Function,
        index: Number,
        column: {
            type: Object,
            default: null
        }
    },
    render: (h, ctx) => {
        const params = {
            row: ctx.props.row,
            index: ctx.props.index
        };
        if (ctx.props.column) params.column = ctx.props.column;
        return ctx.props.render(h, params);
    }
};

2.以上是两个组件的代码,下面将介绍调用及传入数据的格式

--调用组件

引入组件,且注册组件

import BaseTable from '@/components/BaseTable/index.vue'
注册加入components
components: {
      BaseTable
    },

调用组件

 <BaseTable :api-service="informationApi" :grid-config="INFO_CONFIG.gridConfig"
                  :grid-btn-config="INFO_CONFIG.gridBtnConfig"
                  ref="crud" :grid-edit-width="80"
                  :is-async="true" :hide-edit-area="true">
        </BaseTable>

传入数据的Js数据格式:

const INFO_CONFIG = {
  gridConfig: [
    {label: '设备代码', prop: 'sbdm', width: '150'},
    {label: '设备名称', prop: 'sbmc'},
    {label: '型号规格', prop: 'xh'},
    {label: '计量单位', prop: 'jldw', filter: equipmentUnitFilter},
    {label: '类型', prop: 'lx'},
    {label: '厂家', prop: 'cj'},
    {label: '是否是计量设备', prop: 'sfjlsb', filter: filterJLSB},
    {label: '筹供单位', prop: 'cgdw', filter: filterGCDW},
    {label: '使用年限(月)', prop: 'synx'},
  ],
  //操作按钮
  gridBtnConfig: {
    create: false, update: false, delete: false, view: false
  }
}
注意informationApi:是API 调用地址,如果需要直接传入数据,需要更组件中的逻辑,apiService传入true,不需要请求数据,出入gridData=[],即表格显示的数据,组件中传入的参数都写了注释,根据需做出更改吧
关注博客,后期更新Vue表单组件。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!