+-
后台系统常用的table
首页 专栏 前端 文章详情
0

后台系统常用的table

southnan0 发布于 3 月 9 日

后台系统,离不开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组件

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

规则校验
校验时,一个是保存按钮的校验,一个是底部提交按钮的校验
下图这种, 数据联动

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>

下面这种表格的形式,需要做两部分的校验:

点击表格的保存按钮 点击底部的提交按钮

elemnt-ui的表单校验对这种嵌套表单校验不是很友好,所以,不太建议这种形式,或者手动弹窗提示

开发

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

前端 vue.js
阅读 36 发布于 3 月 9 日
收藏
分享
本作品系原创, 采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议
记忆光盘
好记性 || 烂笔头
关注专栏
avatar
southnan0

欲买桂花同载酒,终不似,少年游

153 声望
4 粉丝
关注作者
0 条评论
得票 时间
提交评论
avatar
southnan0

欲买桂花同载酒,终不似,少年游

153 声望
4 粉丝
关注作者
宣传栏
目录

后台系统,离不开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组件

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

规则校验
校验时,一个是保存按钮的校验,一个是底部提交按钮的校验
下图这种, 数据联动

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>

下面这种表格的形式,需要做两部分的校验:

点击表格的保存按钮 点击底部的提交按钮

elemnt-ui的表单校验对这种嵌套表单校验不是很友好,所以,不太建议这种形式,或者手动弹窗提示

开发

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