关于前端:后台系统常用的table

51次阅读

共计 6563 个字符,预计需要花费 17 分钟才能阅读完成。

后盾零碎,离不开 crud,而 crud 中,经常会有表格的身影。
表格能够间接、清晰地列出数据

背景

每天总是一直地复制粘贴,修修改改,没完没了的 el-table>el-table-column…,不如整顿成可配置可服用的组件~

根本表格

只须要展现信息

<template>
  <el-table
    :key="tableKey"
    v-loading="tableConfig.isLoading"
    :data="data"
    v-bind="getTableAttrs(tableConfig)"
    @sort-change="(data) => $emit('sort-change',data)"
    @row-click="(...args) => handleAction('rowClick', args)"
  >
    <el-table-column
      v-if="tableConfig.selection"
      type="selection"
      width="55"
    />
    <template v-for="col in formateColumns">
      <el-table-column
        v-if="col.tableIsShow !== false && col.colProps && col.colProps.formatter"
        :key="col.prop"
        :label="col.label"
        v-bind="col.colProps"
      />
      <el-table-column
        v-else-if="col.tableIsShow !== false && col.colProps && col.colProps.component"
        :key="col.prop"
        :label="col.label"
        v-bind="col.colProps"
      >
        <template slot-scope="scope">
          <component
            :is="col.colProps.component"
            :row="scope.row"
            :index="scope.$index"
            :column="col"
            v-bind="col.colProps.props"
            @action="(methodName, ...args) => handleAction(methodName,args)"
          />
        </template>
      </el-table-column>
      <el-table-column
        v-else-if="col.tableIsShow !== false"
        :key="col.prop"
        :label="col.label"
        v-bind="col.colProps"
      >
        <template slot-scope="scope">
          <span v-if="col.colProps && col.colProps.constant">
            {{col.colProps.constant[scope.row[col.prop]] }}
          </span>
          <span v-else>{{scope.row[col.prop] }}</span>
        </template>
      </el-table-column>
    </template>
    <operation
      v-if="tableConfig.operations"
      :operations="getOperations(tableConfig)"
      @action="(methodName, ...args) => handleAction(methodName, args)"
    />
  </el-table>
</template>
<script>
import Operation from "./operation";
import ToolbarDropdown from "./toolbar-dropdown";
import {getPagination,getTableConfig} from "oa-ui/src/utils/fields-tools";
import {Table,TableColumn,Button} from "element-ui";

export default {
  name: "OaTable",
  components: {
    Operation,
    ToolbarDropdown,
    ELTable:Table,
    ElTableColumn:TableColumn,
    ElButton:Button
  },
  props: {
    size:{
      type:String,
      default: ''
    },
    tableConfig: {
      type: Object,
      default: function () {
        return {
          showHeader: true,
          totalRecords: 0, // 总记录数
          isLoading: false, // 是否 loading 状态
          selection: false, // 是否多选
          operations: {
            btn: [
              // 表格操作按钮
              {name: "编辑", method: "handleEdit"},
              {name: "删除", method: "handleDelete", needConfirm: true},
            ],
          },
        };
      },
    },
    columns: {
      type: Array,
      default: function () {return [];
      },
    },
    data: {
      // 数据
      type: Array,
      default: function () {return [];
      },
    },
  },
  data() {
    return {
      tableKey: 0,
      checkedColumnsName: null,
      selectedSelection: this.defaultSelectedSelection || []};
  },
  computed:{tableSize(){return this.size || this?.$ELEMENT?.size},
    formateColumns(){
      return this.columns?.map(col=>{
        return {
          label: col.title,
          prop: col.field,
          ...col
        }
      })
    }
  },
  watch: {checkedColumnsName(valArr) {
    // 更新表格列数据
      this.tableKey = this.tableKey + 1;
      this.columns.forEach((item) => {item.tableIsShow = valArr.indexOf(item.prop) >= 0;
      });
    },
  },
  created() {
    // 初始化选中的列
    const checkedColumns = [];
    this.columns.forEach((item) => {if (item.tableIsShow !== false) {checkedColumns.push(item.prop);
      }
    });
    this.checkedColumnsName = checkedColumns;
  },
  methods: {getTableAttrs(tableConfig){
      return {
        size:this.tableSize,
        border:true,
        stripe:true,
        fit:true,
        highlightCurrentRow:true,
        style:"width:100%",
        ...getTableConfig(tableConfig),
      }
    },
    getPaginationAttrs(tableConfig){
      return {
        size:this.tableSize,
        style:"text-align: right",
        total:tableConfig.totalRecords,
        ...getPagination(tableConfig),
      }
    },
    getOperations(tableConfig){
      return {
        size: this.tableSize,
        ...tableConfig.operations
      }
    },
    handleAction(methodName,args){this.$emit('action',methodName,...args)
    }
  },
};
</script>

开发的时候,关注在更多的业务上,而不是繁琐的写标签上

<oa-table
  ref="dataTable"
  :data="data"
  :table-config="tableConfig"
  :columns="columns"
  @action="$_handleAction"
>
</oa-table>

<script>
  export default {data() {
      return {
        queryParam: {
          // 搜寻条件
          _start: 0,
          _length: 20,
          _searching: {},
          _order_field: undefined, // 排序配置
          _order_type: undefined
        },
        data: [],
        tableConfig: {
          isLoading: false,
          totalRecords: 0,
          operations: false,
          filterable: true
        },
        columns: [
          {
            field: "id",
            title: "ID"
          },
          {
            field: "name",
            title: "名称",
            type: "el-input",
            config: {showInSearch: false},
            value: "123"
          },
          {
            field: "isValidText",
            title: "是否启用",
            config: {showInTable: { sortable: false}
            }
          },
          {
            field: "showIndex",
            title: "排序",
            config: {}},
          {
            field: "alias",
            title: "更新工夫",
            config: {showInTable: { sortable: false}
            }
          }
        ]
      };
    },
    mounted() {this.handleSearch(this.queryParam);
    },
    methods: {handleSearch(param) {
        const response = {
          code: 20000,
          data: {
            dataList: [
              {
                id: "1",
                groupId: "324",
                code: null,
                name: "测试 name1",
                alias: "2020-12-10 14:30:33",
                itemLevel: null,
                tag: null,
                parentCode: null,
                showIndex: 1,
                isValid: 1,
                isDisplay: null,
                english: null,
                isValidText: "是",
                firstname: null,
                secondname: null,
                fisrtGroupId: null,
                handlerName: null,
                eId: null,
                itemLevelText: "枚举值"
              }
            ],
            totalCount: 3,
            other: null
          },
          message: "操作胜利"
        };
        const {_searching, ...others} = param;
        this.tableConfig.isLoading = true;
        others.groupId = this.groupId;

        setTimeout(() => {
          this.data = response.data.dataList;
          this.tableConfig.totalRecords = response.data.totalCount;
          this.tableConfig.isLoading = false;
        }, 2000);
      },
      handleInValid(row) {alert("handleInValid");
      },
      $_handleAction(methodName, row) {this[methodName] && this[methodName](row);
      }
    }
  };
</script>

带搜寻的表格

个别在最左边有操作按钮,编辑、删除、详情,点击弹窗、跳转、二次确认框

单元格可 DIY

单元格外面的内容,须要解决,可能会显示几个信息

表格可编辑

表单嵌套表格,且表格可编辑

通过操作区域,触发单元格变为 form 组件

表格可编辑时,可能会波及到:

  1. 规定校验
    校验时,一个是保留按钮的校验,一个是底部提交按钮的校验
    下图这种,
  2. 数据联动

table 组件的代码

<template>
 <el-table
   :key="tableKey"
   :data="value"
   border
   size="small"
 >
   <template slot="empty">
     <el-button 
       size="small"
       @click="handleAdd"
     >
       新增
     </el-button>
   </template>
   <template v-for="col in columns">
     <el-table-column
       :key="col.key"
       :prop="col.prop"
       v-bind="col"
     >
       <template slot-scope="{row,$index}">
         <el-form-item

           :prop="`${keyName}.${$index}.${col.prop}`"
           :rules="col.rules"
         >
           <component
             :is="col.type"
             v-model="value[`${$index}`][`${col.prop}`]"
             :row="row"
             :index="$index"
             :column="col"
             v-bind="col.props"
           />
         </el-form-item>
       </template>
     </el-table-column>
   </template>
   <el-table-column
     label="操作"
     align="center"
   >
     <template slot-scope="scope">
       <el-button 
         v-if="scope.$index === value.length -1"
         size="small"
         type="text"
         @click="handleAdd"
       >
         新增
       </el-button>
       <el-button
         size="small"
         type="text"
         @click="handleDelete(scope)"
       >
         删除
       </el-button>
     </template>
   </el-table-column>
 </el-table>
</template>

<script>
export default {
 props:{
   keyName:{
     type:String,
     required:true
   },
   value:{
     type:Array,
     default(){return []
     }
   },
   columns:{
     type:Array,
     default(){
       return [{
         label:"姓名",
         prop:"name",
         width:100,
         type:'el-input',
         align:'center',
         rules:[{required:true,message:'姓名不能为空'}]
       },{
         label:"身高 /cm",
         prop:"height",
         width:200,
         type:'el-input-number',
         align:'center',
         props:{
           placeholder:"请输出身高",
           controlsPosition:'right'
         },
         rules:[{required:true,message:'身高不能为空'}]
       },{
         label:"喜好",
         prop:"hobby",
         width:200,
         type:'el-input',
         align:'center',
         rules:[{required:true,message:'喜好不能为空'}]
       }]
     }
   }
 },
 data(){
   return {tableKey:0}
 },
 methods:{handleDelete({$index}){
     const v = this.value;
     v.splice($index,1);
     this.$set(this,'value',v)
   },
   handleAdd(){
     this.value.push({edit:true});
   }
 }
}
</script>

上面这种表格的模式,须要做两局部的校验:

  1. 点击表格的保留按钮
  2. 点击底部的提交按钮

elemnt-ui 的表单校验对这种嵌套表单校验不是很敌对,所以,不太倡议这种模式,或者手动弹窗提醒

开发

资料:vue、element-ui、form-create

正文完
 0