Эх сурвалжийг харах

wip: Table组件重构中

kailong321200875 3 жил өмнө
parent
commit
f64842462e
28 өөрчлөгдсөн 2577 нэмэгдсэн , 174 устгасан
  1. 6 0
      components.d.ts
  2. 37 0
      src/components/Table/components/Slot.vue
  3. 64 0
      src/components/Table/components/TableColumn.vue
  4. 170 0
      src/components/Table/index.vue
  5. 2 0
      src/components/index.ts
  6. 181 173
      src/router/index.ts
  7. 1 1
      src/utils/index.ts
  8. 26 0
      src/views/components-demo/watermark/index.vue
  9. 61 0
      src/views/table-demo/basic-table/index.vue
  10. 61 0
      src/views/table-demo/border-table/index.vue
  11. 91 0
      src/views/table-demo/custom-header/index.vue
  12. 68 0
      src/views/table-demo/custom-index/index.vue
  13. 125 0
      src/views/table-demo/expand-row/index.vue
  14. 149 0
      src/views/table-demo/fixed-column-header/index.vue
  15. 110 0
      src/views/table-demo/fixed-column/index.vue
  16. 81 0
      src/views/table-demo/fixed-header/index.vue
  17. 147 0
      src/views/table-demo/fluid-height/index.vue
  18. 151 0
      src/views/table-demo/merge-table/index.vue
  19. 145 0
      src/views/table-demo/multi-header/index.vue
  20. 90 0
      src/views/table-demo/multiple-choice/index.vue
  21. 79 0
      src/views/table-demo/page-table/index.vue
  22. 124 0
      src/views/table-demo/screen-table/index.vue
  23. 82 0
      src/views/table-demo/single-choice/index.vue
  24. 69 0
      src/views/table-demo/sort-table/index.vue
  25. 85 0
      src/views/table-demo/state-table/index.vue
  26. 61 0
      src/views/table-demo/stripe-table/index.vue
  27. 148 0
      src/views/table-demo/total-table/index.vue
  28. 163 0
      src/views/table-demo/tree-and-load/index.vue

+ 6 - 0
components.d.ts

@@ -35,6 +35,7 @@ declare module 'vue' {
     ElMenu: typeof import('element-plus/es')['ElMenu']
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
     ElOption: typeof import('element-plus/es')['ElOption']
+    ElPagination: typeof import('element-plus/es')['ElPagination']
     ElRadio: typeof import('element-plus/es')['ElRadio']
     ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
     ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
@@ -43,6 +44,8 @@ declare module 'vue' {
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
+    ElTable: typeof import('element-plus/es')['ElTable']
+    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
     ElTabPane: typeof import('element-plus/es')['ElTabPane']
     ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']
@@ -56,7 +59,10 @@ declare module 'vue' {
     Qrcode: typeof import('./src/components/Qrcode/index.vue')['default']
     Redirect: typeof import('./src/components/Redirect/index.vue')['default']
     Search: typeof import('./src/components/Search/index.vue')['default']
+    Slot: typeof import('./src/components/Table/components/Slot.vue')['default']
     SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default']
+    Table: typeof import('./src/components/Table/index.vue')['default']
+    TableColumn: typeof import('./src/components/Table/components/TableColumn.vue')['default']
   }
 }
 

+ 37 - 0
src/components/Table/components/Slot.vue

@@ -0,0 +1,37 @@
+<script lang="ts">
+import { defineComponent, inject, h, PropType } from 'vue'
+export default defineComponent({
+  name: 'Slot',
+  props: {
+    row: {
+      type: Object as PropType<object>,
+      default: () => null
+    },
+    index: {
+      type: Number as PropType<number>,
+      default: null
+    },
+    column: {
+      type: Object as PropType<object>,
+      default: () => null
+    },
+    slotName: {
+      type: String as PropType<string>,
+      default: ''
+    }
+  },
+  render(props: any) {
+    const _this: any = inject('tableRoot')
+    return h(
+      'span',
+      _this.slots[props.slotName]({
+        row: props.row,
+        column: props.column,
+        $index: props.index
+      })
+    )
+  }
+})
+</script>
+
+<style></style>

+ 64 - 0
src/components/Table/components/TableColumn.vue

@@ -0,0 +1,64 @@
+<template>
+  <el-table-column v-bind="{ ...getItemBindValue(child) }" :prop="child.key">
+    <template v-for="item in child.children">
+      <!-- 树型数据 -->
+      <template v-if="item.children && item.children.length">
+        <table-column :key="item[item.field]" :child="item" />
+      </template>
+
+      <template v-else>
+        <el-table-column
+          :key="item[item.field]"
+          v-bind="{ ...getItemBindValue(item) }"
+          :prop="item.field"
+        >
+          <!-- 表头插槽 -->
+          <template v-if="item.slots && item.slots.header" #header="scope">
+            <table-slot
+              v-if="item.slots && item.slots.header"
+              :slot-name="item.slots.header"
+              :column="item"
+              :index="scope.$index"
+            />
+          </template>
+
+          <!-- 表格内容插槽自定义 -->
+          <template v-if="item.slots && item.slots.default" #default="scope">
+            <table-slot
+              :slot-name="item.slots.default"
+              :row="scope.row"
+              :column="item"
+              :index="scope.$index"
+            />
+          </template>
+        </el-table-column>
+      </template>
+    </template>
+  </el-table-column>
+</template>
+
+<script setup lang="ts" name="TableColumn">
+import { PropType } from 'vue'
+import TableSlot from './Slot.vue'
+import { deepClone } from '@/utils'
+
+defineProps({
+  child: {
+    type: Object as PropType<IObj>,
+    required: true
+  }
+})
+
+function getItemBindValue(item: any) {
+  const delArr: string[] = ['children']
+  const obj = deepClone(item)
+  for (const key in obj) {
+    if (delArr.indexOf(key) !== -1) {
+      delete obj[key]
+    }
+  }
+  return obj
+}
+</script>
+
+<style></style>

+ 170 - 0
src/components/Table/index.vue

@@ -0,0 +1,170 @@
+<template>
+  <el-table ref="elTable" :border="true" v-bind="getBindValue" @header-dragend="headerDragend">
+    <!-- 多选 -->
+    <el-table-column
+      v-if="selection"
+      type="selection"
+      :reserve-selection="reserveSelection"
+      width="40"
+    />
+    <template v-for="item in columns">
+      <!-- 自定义索引 -->
+      <template v-if="item.type === 'index'">
+        <el-table-column
+          :key="item[item.field]"
+          v-bind="{ ...getItemBindValue(item) }"
+          type="index"
+          :index="item.index"
+        />
+      </template>
+
+      <!-- 树型数据 -->
+      <template v-else-if="item.children && item.children.length">
+        <table-column :key="item[item.field]" :child="item" />
+      </template>
+
+      <template v-else>
+        <el-table-column
+          :key="item[item.field]"
+          v-bind="{ ...getItemBindValue(item) }"
+          :prop="item.field"
+        >
+          <!-- 表头插槽 -->
+          <template v-if="item.slots && item.slots.header" #header="scope">
+            <table-slot
+              v-if="item.slots && item.slots.header"
+              :slot-name="item.slots.header"
+              :column="item"
+              :index="scope.$index"
+            />
+          </template>
+
+          <!-- 表格内容插槽自定义 -->
+          <template v-if="item.slots && item.slots.default" #default="scope">
+            <table-slot
+              v-if="item.slots && item.slots.default"
+              :slot-name="item.slots.default"
+              :row="scope.row"
+              :column="item"
+              :index="scope.$index"
+            />
+          </template>
+        </el-table-column>
+      </template>
+    </template>
+  </el-table>
+
+  <div v-if="pagination" class="pagination__wrap">
+    <el-pagination
+      :style="paginationStyle"
+      :page-sizes="[10, 20, 30, 40, 50, 100]"
+      layout="total, sizes, prev, pager, next, jumper"
+      v-bind="getPaginationBindValue"
+      @size-change="sizeChange"
+      @current-change="currentChange"
+    />
+  </div>
+</template>
+
+<script setup lang="ts" name="ComTable">
+import { PropType, computed, ref, unref, useAttrs } from 'vue'
+import { deepClone } from '@/utils'
+import { isObject } from '@/utils/validate'
+import TableColumn from './components/TableColumn.vue'
+import TableSlot from './components/Slot.vue'
+
+const props = defineProps({
+  // 表头
+  columns: {
+    type: Array as PropType<IObj[]>,
+    default: () => []
+  },
+  // 是否多选
+  selection: {
+    type: Boolean as PropType<boolean>,
+    default: false
+  },
+  // 是否展示分页
+  pagination: {
+    type: [Boolean, Object] as PropType<boolean | IObj>,
+    default: false
+  },
+  // 仅对 type=selection 的列有效,类型为 Boolean,为 true 则会在数据更新之后保留之前选中的数据(需指定 row-key)
+  reserveSelection: {
+    type: Boolean as PropType<boolean>,
+    default: false
+  }
+})
+
+const attrs = useAttrs()
+
+const elTable = ref<HTMLElement | null>(null)
+function getTableRef() {
+  return unref(elTable as any)
+}
+
+// const _this = getCurrentInstance()
+// provide('tableRoot', _this)
+
+const getBindValue = computed((): IObj => {
+  const bindValue = { ...attrs, ...props } as IObj
+  delete bindValue.columns
+  return bindValue
+})
+
+function getItemBindValue(item: IObj) {
+  const delArr: string[] = []
+  const obj = deepClone(item)
+  for (const key in obj) {
+    if (delArr.indexOf(key) !== -1) {
+      delete obj[key]
+    }
+  }
+  return obj
+}
+
+const getPaginationBindValue = computed((): IObj => {
+  const PaginationBindValue =
+    props.pagination && isObject(props.pagination) ? { ...props.pagination } : {}
+  return PaginationBindValue
+})
+
+const paginationStyle = computed(() => {
+  return {
+    textAlign: (props.pagination && (props.pagination as IObj).position) || 'right'
+  }
+})
+
+function headerDragend(newWidth: number, _: number, column: IObj) {
+  // 不懂为啥无法自动计算宽度,只能手动去计算了。。失望ing,到时候看看能不能优化吧。
+  const htmlArr = document.getElementsByClassName(column.id)
+  for (const v of htmlArr as any) {
+    if (v.firstElementChild) {
+      ;(v.firstElementChild as any).style.width = newWidth + 'px'
+    }
+  }
+}
+
+function sizeChange(val: number) {
+  if (props.pagination && (props.pagination as IObj).onSizeChange) {
+    ;(props.pagination as IObj).onSizeChange(val)
+  }
+}
+
+function currentChange(val: number) {
+  if (props.pagination && (props.pagination as IObj).onCurrentChange) {
+    ;(props.pagination as IObj).onCurrentChange(val)
+  }
+}
+defineExpose({
+  getTableRef
+})
+</script>
+
+<style lang="less" scoped>
+.pagination__wrap {
+  padding: 10px;
+  margin-top: 15px;
+  background: #fff;
+}
+</style>

+ 2 - 0
src/components/index.ts

@@ -3,10 +3,12 @@ import SvgIcon from './SvgIcon/index.vue' // svg组件
 import ComSearch from './Search/index.vue' // search组件
 import ComDialog from './Dialog/index.vue' // dialog组件
 import ComDetail from './Detail/index.vue' // detail组件
+import ComTable from './Table/index.vue' // table组件
 
 export function setupGlobCom(app: App<Element>): void {
   app.component('SvgIcon', SvgIcon)
   app.component('ComSearch', ComSearch)
   app.component('ComDialog', ComDialog)
   app.component('ComDetail', ComDetail)
+  app.component('ComTable', ComTable)
 }

+ 181 - 173
src/router/index.ts

@@ -216,182 +216,190 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
         meta: {
           title: '文字高亮'
         }
+      },
+      {
+        path: 'watermark',
+        component: () => import('_v/components-demo/watermark/index.vue'),
+        name: 'WatermarkDemo',
+        meta: {
+          title: '水印'
+        }
       }
     ]
   },
-  // {
-  //   path: '/table-demo',
-  //   component: Layout,
-  //   redirect: '/table-demo/basic-table',
-  //   name: 'TableDemo',
-  //   meta: {
-  //     title: '表格',
-  //     icon: 'table',
-  //     alwaysShow: true
-  //   },
-  //   children: [
-  //     {
-  //       path: 'basic-table',
-  //       component: () => import('_v/table-demo/basic-table/index.vue'),
-  //       name: 'BasicTable',
-  //       meta: {
-  //         title: '基础表格'
-  //       }
-  //     },
-  //     {
-  //       path: 'page-table',
-  //       component: () => import('_v/table-demo/page-table/index.vue'),
-  //       name: 'PageTable',
-  //       meta: {
-  //         title: '分页表格'
-  //       }
-  //     },
-  //     {
-  //       path: 'stripe-table',
-  //       component: () => import('_v/table-demo/stripe-table/index.vue'),
-  //       name: 'StripeTable',
-  //       meta: {
-  //         title: '带斑马纹表格'
-  //       }
-  //     },
-  //     {
-  //       path: 'border-table',
-  //       component: () => import('_v/table-demo/border-table/index.vue'),
-  //       name: 'BorderTable',
-  //       meta: {
-  //         title: '带边框表格'
-  //       }
-  //     },
-  //     {
-  //       path: 'state-table',
-  //       component: () => import('_v/table-demo/state-table/index.vue'),
-  //       name: 'StateTable',
-  //       meta: {
-  //         title: '带状态表格'
-  //       }
-  //     },
-  //     {
-  //       path: 'fixed-header',
-  //       component: () => import('_v/table-demo/fixed-header/index.vue'),
-  //       name: 'FixedHeader',
-  //       meta: {
-  //         title: '固定表头'
-  //       }
-  //     },
-  //     {
-  //       path: 'fixed-column',
-  //       component: () => import('_v/table-demo/fixed-column/index.vue'),
-  //       name: 'FixedColumn',
-  //       meta: {
-  //         title: '固定列'
-  //       }
-  //     },
-  //     {
-  //       path: 'fixed-column-header',
-  //       component: () => import('_v/table-demo/fixed-column-header/index.vue'),
-  //       name: 'FixedColumnHeader',
-  //       meta: {
-  //         title: '固定列和表头'
-  //       }
-  //     },
-  //     {
-  //       path: 'fluid-height',
-  //       component: () => import('_v/table-demo/fluid-height/index.vue'),
-  //       name: 'FluidHeight',
-  //       meta: {
-  //         title: '流体高度'
-  //       }
-  //     },
-  //     {
-  //       path: 'multi-header',
-  //       component: () => import('_v/table-demo/multi-header/index.vue'),
-  //       name: 'MultiHeader',
-  //       meta: {
-  //         title: '多级表头'
-  //       }
-  //     },
-  //     {
-  //       path: 'single-choice',
-  //       component: () => import('_v/table-demo/single-choice/index.vue'),
-  //       name: 'SingleChoice',
-  //       meta: {
-  //         title: '单选'
-  //       }
-  //     },
-  //     {
-  //       path: 'multiple-choice',
-  //       component: () => import('_v/table-demo/multiple-choice/index.vue'),
-  //       name: 'MultipleChoice',
-  //       meta: {
-  //         title: '多选'
-  //       }
-  //     },
-  //     {
-  //       path: 'sort-table',
-  //       component: () => import('_v/table-demo/sort-table/index.vue'),
-  //       name: 'SortTable',
-  //       meta: {
-  //         title: '排序'
-  //       }
-  //     },
-  //     {
-  //       path: 'screen-table',
-  //       component: () => import('_v/table-demo/screen-table/index.vue'),
-  //       name: 'ScreenTable',
-  //       meta: {
-  //         title: '筛选'
-  //       }
-  //     },
-  //     {
-  //       path: 'expand-row',
-  //       component: () => import('_v/table-demo/expand-row/index.vue'),
-  //       name: 'ExpandRow',
-  //       meta: {
-  //         title: '展开行'
-  //       }
-  //     },
-  //     {
-  //       path: 'tree-and-load',
-  //       component: () => import('_v/table-demo/tree-and-load/index.vue'),
-  //       name: 'TreeAndLoad',
-  //       meta: {
-  //         title: '树形数据与懒加载'
-  //       }
-  //     },
-  //     {
-  //       path: 'custom-header',
-  //       component: () => import('_v/table-demo/custom-header/index.vue'),
-  //       name: 'CustomHeader',
-  //       meta: {
-  //         title: '自定义表头'
-  //       }
-  //     },
-  //     {
-  //       path: 'total-table',
-  //       component: () => import('_v/table-demo/total-table/index.vue'),
-  //       name: 'TotalTable',
-  //       meta: {
-  //         title: '表尾合计行'
-  //       }
-  //     },
-  //     {
-  //       path: 'merge-table',
-  //       component: () => import('_v/table-demo/merge-table/index.vue'),
-  //       name: 'MergeTable',
-  //       meta: {
-  //         title: '合并行或列'
-  //       }
-  //     },
-  //     {
-  //       path: 'custom-index',
-  //       component: () => import('_v/table-demo/custom-index/index.vue'),
-  //       name: 'CustomIndex',
-  //       meta: {
-  //         title: '自定义索引'
-  //       }
-  //     }
-  //   ]
-  // },
+  {
+    path: '/table-demo',
+    component: Layout,
+    redirect: '/table-demo/basic-table',
+    name: 'TableDemo',
+    meta: {
+      title: '表格',
+      icon: 'table',
+      alwaysShow: true
+    },
+    children: [
+      {
+        path: 'basic-table',
+        component: () => import('_v/table-demo/basic-table/index.vue'),
+        name: 'BasicTable',
+        meta: {
+          title: '基础表格'
+        }
+      }
+      // {
+      //   path: 'page-table',
+      //   component: () => import('_v/table-demo/page-table/index.vue'),
+      //   name: 'PageTable',
+      //   meta: {
+      //     title: '分页表格'
+      //   }
+      // },
+      // {
+      //   path: 'stripe-table',
+      //   component: () => import('_v/table-demo/stripe-table/index.vue'),
+      //   name: 'StripeTable',
+      //   meta: {
+      //     title: '带斑马纹表格'
+      //   }
+      // },
+      // {
+      //   path: 'border-table',
+      //   component: () => import('_v/table-demo/border-table/index.vue'),
+      //   name: 'BorderTable',
+      //   meta: {
+      //     title: '带边框表格'
+      //   }
+      // },
+      // {
+      //   path: 'state-table',
+      //   component: () => import('_v/table-demo/state-table/index.vue'),
+      //   name: 'StateTable',
+      //   meta: {
+      //     title: '带状态表格'
+      //   }
+      // },
+      // {
+      //   path: 'fixed-header',
+      //   component: () => import('_v/table-demo/fixed-header/index.vue'),
+      //   name: 'FixedHeader',
+      //   meta: {
+      //     title: '固定表头'
+      //   }
+      // },
+      // {
+      //   path: 'fixed-column',
+      //   component: () => import('_v/table-demo/fixed-column/index.vue'),
+      //   name: 'FixedColumn',
+      //   meta: {
+      //     title: '固定列'
+      //   }
+      // },
+      // {
+      //   path: 'fixed-column-header',
+      //   component: () => import('_v/table-demo/fixed-column-header/index.vue'),
+      //   name: 'FixedColumnHeader',
+      //   meta: {
+      //     title: '固定列和表头'
+      //   }
+      // },
+      // {
+      //   path: 'fluid-height',
+      //   component: () => import('_v/table-demo/fluid-height/index.vue'),
+      //   name: 'FluidHeight',
+      //   meta: {
+      //     title: '流体高度'
+      //   }
+      // },
+      // {
+      //   path: 'multi-header',
+      //   component: () => import('_v/table-demo/multi-header/index.vue'),
+      //   name: 'MultiHeader',
+      //   meta: {
+      //     title: '多级表头'
+      //   }
+      // },
+      // {
+      //   path: 'single-choice',
+      //   component: () => import('_v/table-demo/single-choice/index.vue'),
+      //   name: 'SingleChoice',
+      //   meta: {
+      //     title: '单选'
+      //   }
+      // },
+      // {
+      //   path: 'multiple-choice',
+      //   component: () => import('_v/table-demo/multiple-choice/index.vue'),
+      //   name: 'MultipleChoice',
+      //   meta: {
+      //     title: '多选'
+      //   }
+      // },
+      // {
+      //   path: 'sort-table',
+      //   component: () => import('_v/table-demo/sort-table/index.vue'),
+      //   name: 'SortTable',
+      //   meta: {
+      //     title: '排序'
+      //   }
+      // },
+      // {
+      //   path: 'screen-table',
+      //   component: () => import('_v/table-demo/screen-table/index.vue'),
+      //   name: 'ScreenTable',
+      //   meta: {
+      //     title: '筛选'
+      //   }
+      // },
+      // {
+      //   path: 'expand-row',
+      //   component: () => import('_v/table-demo/expand-row/index.vue'),
+      //   name: 'ExpandRow',
+      //   meta: {
+      //     title: '展开行'
+      //   }
+      // },
+      // {
+      //   path: 'tree-and-load',
+      //   component: () => import('_v/table-demo/tree-and-load/index.vue'),
+      //   name: 'TreeAndLoad',
+      //   meta: {
+      //     title: '树形数据与懒加载'
+      //   }
+      // },
+      // {
+      //   path: 'custom-header',
+      //   component: () => import('_v/table-demo/custom-header/index.vue'),
+      //   name: 'CustomHeader',
+      //   meta: {
+      //     title: '自定义表头'
+      //   }
+      // },
+      // {
+      //   path: 'total-table',
+      //   component: () => import('_v/table-demo/total-table/index.vue'),
+      //   name: 'TotalTable',
+      //   meta: {
+      //     title: '表尾合计行'
+      //   }
+      // },
+      // {
+      //   path: 'merge-table',
+      //   component: () => import('_v/table-demo/merge-table/index.vue'),
+      //   name: 'MergeTable',
+      //   meta: {
+      //     title: '合并行或列'
+      //   }
+      // },
+      // {
+      //   path: 'custom-index',
+      //   component: () => import('_v/table-demo/custom-index/index.vue'),
+      //   name: 'CustomIndex',
+      //   meta: {
+      //     title: '自定义索引'
+      //   }
+      // }
+    ]
+  },
   // {
   //   path: '/directives-demo',
   //   component: Layout,

+ 1 - 1
src/utils/index.ts

@@ -129,7 +129,7 @@ export function exportFile(response: AxiosResponse) {
       ? response.headers['content-disposition'].split(';')[1].split('=')[1]
       : 'test'
   )
-  const blob = new Blob([response.data], {
+  const blob = new Blob([response.data as Blob], {
     type: response.headers['content-type']
   })
   if (typeof (window.navigator as any).msSaveBlob !== 'undefined') {

+ 26 - 0
src/views/components-demo/watermark/index.vue

@@ -0,0 +1,26 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="useWatermark,为整个系统提供水印功能。"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <el-button type="primary" @click="setWatermark('vue-element-plus-admin')">创建水印</el-button>
+    <el-button type="danger" @click="clear">清除水印</el-button>
+    <el-button type="warning" @click="setWatermark('vue-element-plus-admin-new')">
+      重置水印
+    </el-button>
+  </div>
+</template>
+
+<script setup lang="ts" name="Watermark">
+import { onBeforeUnmount } from 'vue'
+import { useWatermark } from '@/hooks/web/useWatermark'
+const { setWatermark, clear } = useWatermark()
+
+onBeforeUnmount(() => {
+  clear()
+})
+</script>

+ 61 - 0
src/views/table-demo/basic-table/index.vue

@@ -0,0 +1,61 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 基础表格"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table v-loading="loading" :columns="columns" :data="tableData" />
+  </div>
+</template>
+
+<script setup lang="ts" name="BasicTable">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'address',
+    label: '地址'
+  }
+]
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄'
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄'
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+</script>
+
+<style></style>

+ 61 - 0
src/views/table-demo/border-table/index.vue

@@ -0,0 +1,61 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 带边框表格"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table v-loading="loading" :columns="columns" :data="tableData" border />
+  </div>
+</template>
+
+<script setup lang="ts" name="BorderTable">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'address',
+    label: '地址'
+  }
+]
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄'
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄'
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+</script>
+
+<style></style>

+ 91 - 0
src/views/table-demo/custom-header/index.vue

@@ -0,0 +1,91 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 自定义表头"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table
+      v-loading="loading"
+      :columns="columns"
+      :data="
+        tableData.filter(
+          (data) => !search || data.name.toLowerCase().includes(search.toLowerCase())
+        )
+      "
+    >
+      <template #actionHeader>
+        <el-input v-model="search" size="mini" placeholder="输入关键字搜索" />
+      </template>
+      <template #action="scope">
+        <el-button size="mini" @click="handleEdit(scope.$index, scope.row)">Edit</el-button>
+        <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)"
+          >Delete</el-button
+        >
+      </template>
+    </com-table>
+  </div>
+</template>
+
+<script setup lang="ts" name="CustomHeader">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'action',
+    slots: {
+      header: 'actionHeader',
+      default: 'action'
+    }
+  }
+]
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎1',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎2',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎3',
+    address: '上海市普陀区金沙江路 1519 弄'
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎4',
+    address: '上海市普陀区金沙江路 1516 弄'
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+const search = ref<string>('')
+
+function handleEdit(index: number, row: any) {
+  console.log(index, row)
+}
+function handleDelete(index: number, row: any) {
+  console.log(index, row)
+}
+</script>
+
+<style></style>

+ 68 - 0
src/views/table-demo/custom-index/index.vue

@@ -0,0 +1,68 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 自定义索引"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table v-loading="loading" :columns="columns" :data="tableData" />
+  </div>
+</template>
+
+<script setup lang="ts" name="CustomIndex">
+import { ref } from 'vue'
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄'
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄'
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+const columns = ref<any[]>([
+  {
+    field: 'index',
+    type: 'index',
+    index: (index: number) => {
+      return index * 2
+    }
+  },
+  {
+    field: 'date',
+    label: '日期'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'address',
+    label: '地址'
+  }
+])
+</script>
+
+<style></style>

+ 125 - 0
src/views/table-demo/expand-row/index.vue

@@ -0,0 +1,125 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 展开行"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table ref="multipleTable" v-loading="loading" :columns="columns" :data="tableData">
+      <template #id="scope">
+        <el-form label-position="left" inline class="demo-table-expand">
+          <el-form-item label="商品名称">
+            <span>{{ scope.row.name }}</span>
+          </el-form-item>
+          <el-form-item label="所属店铺">
+            <span>{{ scope.row.shop }}</span>
+          </el-form-item>
+          <el-form-item label="商品 ID">
+            <span>{{ scope.row.id }}</span>
+          </el-form-item>
+          <el-form-item label="店铺 ID">
+            <span>{{ scope.row.shopId }}</span>
+          </el-form-item>
+          <el-form-item label="商品分类">
+            <span>{{ scope.row.category }}</span>
+          </el-form-item>
+          <el-form-item label="店铺地址">
+            <span>{{ scope.row.address }}</span>
+          </el-form-item>
+          <el-form-item label="商品描述">
+            <span>{{ scope.row.desc }}</span>
+          </el-form-item>
+        </el-form>
+      </template>
+    </com-table>
+  </div>
+</template>
+
+<script setup lang="ts" name="ExpandRow">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'id',
+    type: 'expand',
+    slots: {
+      default: 'id'
+    }
+  },
+  {
+    field: 'id',
+    label: '商品ID'
+  },
+  {
+    field: 'name',
+    label: '商品名称'
+  },
+  {
+    field: 'desc',
+    label: '描述'
+  }
+]
+
+const tableData = [
+  {
+    id: '12987122',
+    name: '好滋好味鸡蛋仔',
+    category: '江浙小吃、小吃零食',
+    desc: '荷兰优质淡奶,奶香浓而不腻',
+    address: '上海市普陀区真北路',
+    shop: '王小虎夫妻店',
+    shopId: '10333'
+  },
+  {
+    id: '12987123',
+    name: '好滋好味鸡蛋仔',
+    category: '江浙小吃、小吃零食',
+    desc: '荷兰优质淡奶,奶香浓而不腻',
+    address: '上海市普陀区真北路',
+    shop: '王小虎夫妻店',
+    shopId: '10333'
+  },
+  {
+    id: '12987125',
+    name: '好滋好味鸡蛋仔',
+    category: '江浙小吃、小吃零食',
+    desc: '荷兰优质淡奶,奶香浓而不腻',
+    address: '上海市普陀区真北路',
+    shop: '王小虎夫妻店',
+    shopId: '10333'
+  },
+  {
+    id: '12987126',
+    name: '好滋好味鸡蛋仔',
+    category: '江浙小吃、小吃零食',
+    desc: '荷兰优质淡奶,奶香浓而不腻',
+    address: '上海市普陀区真北路',
+    shop: '王小虎夫妻店',
+    shopId: '10333'
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+</script>
+
+<style lang="less" scoped>
+:deep(.demo-table-expand) {
+  font-size: 0;
+
+  label {
+    width: 90px;
+    color: #99a9bf;
+  }
+
+  .el-form-item {
+    width: 50%;
+    margin-right: 0;
+    margin-bottom: 0;
+  }
+}
+</style>

+ 149 - 0
src/views/table-demo/fixed-column-header/index.vue

@@ -0,0 +1,149 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 固定列和表头"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table
+      v-loading="loading"
+      :columns="columns"
+      :data="tableData"
+      border
+      height="250"
+      style="width: 820px"
+    >
+      <template #action="scope">
+        <el-button type="text" size="small" @click="handleClick(scope.row)">查看</el-button>
+        <el-button type="text" size="small">编辑</el-button>
+      </template>
+    </com-table>
+  </div>
+</template>
+
+<script setup lang="ts" name="FixedColumnHeader">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期',
+    fixed: true,
+    width: '150'
+  },
+  {
+    field: 'name',
+    label: '姓名',
+    width: '120'
+  },
+  {
+    field: 'province',
+    label: '省份',
+    width: '120'
+  },
+  {
+    field: 'city',
+    label: '市区',
+    width: '120'
+  },
+  {
+    field: 'address',
+    label: '地址',
+    width: '300'
+  },
+  {
+    field: 'zip',
+    label: '邮编',
+    width: '120'
+  },
+  {
+    field: 'action',
+    label: '操作',
+    width: '100',
+    fixed: 'right',
+    slots: {
+      default: 'action'
+    }
+  }
+]
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1518 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1517 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1519 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1516 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1518 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1517 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1519 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1516 弄',
+    zip: 200333
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+function handleClick(row: any) {
+  console.log(row)
+}
+</script>
+
+<style></style>

+ 110 - 0
src/views/table-demo/fixed-column/index.vue

@@ -0,0 +1,110 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 固定列"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table v-loading="loading" :columns="columns" :data="tableData" border style="width: 820px">
+      <template #action="scope">
+        <el-button type="text" size="small" @click="handleClick(scope.row)">查看</el-button>
+        <el-button type="text" size="small">编辑</el-button>
+      </template>
+    </com-table>
+  </div>
+</template>
+
+<script setup lang="ts" name="FixedColumn">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期',
+    fixed: true,
+    width: '150'
+  },
+  {
+    field: 'name',
+    label: '姓名',
+    width: '120'
+  },
+  {
+    field: 'province',
+    label: '省份',
+    width: '120'
+  },
+  {
+    field: 'city',
+    label: '市区',
+    width: '120'
+  },
+  {
+    field: 'address',
+    label: '地址',
+    width: '300'
+  },
+  {
+    field: 'zip',
+    label: '邮编',
+    width: '120'
+  },
+  {
+    field: 'action',
+    label: '操作',
+    width: '100',
+    fixed: 'right',
+    slots: {
+      default: 'action'
+    }
+  }
+]
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1518 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1517 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1519 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1516 弄',
+    zip: 200333
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+function handleClick(row: any) {
+  console.log(row)
+}
+</script>
+
+<style></style>

+ 81 - 0
src/views/table-demo/fixed-header/index.vue

@@ -0,0 +1,81 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 固定表头"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table v-loading="loading" :columns="columns" :data="tableData" height="250" border />
+  </div>
+</template>
+
+<script setup lang="ts" name="FixedHeader">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'address',
+    label: '地址'
+  }
+]
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄'
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄'
+  },
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄'
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄'
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+</script>
+
+<style></style>

+ 147 - 0
src/views/table-demo/fluid-height/index.vue

@@ -0,0 +1,147 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 流体高度"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table
+      v-loading="loading"
+      :columns="columns"
+      :data="tableData"
+      border
+      max-height="250"
+      style="width: 820px"
+    >
+      <template #action="scope">
+        <el-button type="text" size="small" @click="deleteRow(scope.$index)">移除</el-button>
+      </template>
+    </com-table>
+  </div>
+</template>
+
+<script setup lang="ts" name="FluidHeight">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期',
+    fixed: true,
+    width: '150'
+  },
+  {
+    field: 'name',
+    label: '姓名',
+    width: '120'
+  },
+  {
+    field: 'province',
+    label: '省份',
+    width: '120'
+  },
+  {
+    field: 'city',
+    label: '市区',
+    width: '120'
+  },
+  {
+    field: 'address',
+    label: '地址',
+    width: '300'
+  },
+  {
+    field: 'zip',
+    label: '邮编',
+    width: '120'
+  },
+  {
+    field: 'action',
+    label: '操作',
+    width: '100',
+    fixed: 'right',
+    slots: {
+      default: 'action'
+    }
+  }
+]
+const tableData = ref<IObj[]>([
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1518 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1517 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1519 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1516 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1518 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1517 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1519 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1516 弄',
+    zip: 200333
+  }
+])
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+function deleteRow(index: number) {
+  tableData.value.splice(index, 1)
+}
+</script>
+
+<style></style>

+ 151 - 0
src/views/table-demo/merge-table/index.vue

@@ -0,0 +1,151 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 合并行或列"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table
+      v-loading="loading"
+      :columns="columns"
+      :data="tableData"
+      :span-method="arraySpanMethod"
+      border
+    />
+
+    <com-table
+      v-loading="loading"
+      :columns="columns1"
+      :data="tableData"
+      :span-method="objectSpanMethod"
+      border
+      style="margin-top: 20px"
+    />
+  </div>
+</template>
+
+<script setup lang="ts" name="MergeTable">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'id',
+    label: 'ID'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'amount1',
+    label: '数值1',
+    sortable: true
+  },
+  {
+    field: 'amount2',
+    label: '数值2',
+    sortable: true
+  },
+  {
+    field: 'amount3',
+    label: '数值4',
+    sortable: true
+  }
+]
+
+const columns1 = [
+  {
+    field: 'id',
+    label: 'ID'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'amount1',
+    label: '数值1(元)'
+  },
+  {
+    field: 'amount2',
+    label: '数值2(元)'
+  },
+  {
+    field: 'amount3',
+    label: '数值4(元)'
+  }
+]
+
+const tableData = [
+  {
+    id: '12987122',
+    name: '王小虎',
+    amount1: '234',
+    amount2: '3.2',
+    amount3: 10
+  },
+  {
+    id: '12987123',
+    name: '王小虎',
+    amount1: '165',
+    amount2: '4.43',
+    amount3: 12
+  },
+  {
+    id: '12987124',
+    name: '王小虎',
+    amount1: '324',
+    amount2: '1.9',
+    amount3: 9
+  },
+  {
+    id: '12987125',
+    name: '王小虎',
+    amount1: '621',
+    amount2: '2.2',
+    amount3: 17
+  },
+  {
+    id: '12987126',
+    name: '王小虎',
+    amount1: '539',
+    amount2: '4.1',
+    amount3: 15
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+function arraySpanMethod({ rowIndex, columnIndex }: any) {
+  if (rowIndex % 2 === 0) {
+    if (columnIndex === 0) {
+      return [1, 2]
+    } else if (columnIndex === 1) {
+      return [0, 0]
+    }
+  }
+}
+
+function objectSpanMethod({ rowIndex, columnIndex }: any) {
+  if (columnIndex === 0) {
+    if (rowIndex % 2 === 0) {
+      return {
+        rowspan: 2,
+        colspan: 1
+      }
+    } else {
+      return {
+        rowspan: 0,
+        colspan: 0
+      }
+    }
+  }
+}
+</script>
+
+<style></style>

+ 145 - 0
src/views/table-demo/multi-header/index.vue

@@ -0,0 +1,145 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 多级表头"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table v-loading="loading" :columns="columns" :data="tableData">
+      <template #address="scope"> 地址是: {{ scope.row.address }} </template>
+      <template #action="scope">
+        <el-button type="text" size="small" @click="deleteRow(scope.$index)">移除</el-button>
+      </template>
+    </com-table>
+  </div>
+</template>
+
+<script setup lang="ts" name="MultiHeader">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期',
+    fixed: true,
+    width: '150'
+  },
+  {
+    label: '配送信息',
+    children: [
+      {
+        field: 'name',
+        label: '姓名',
+        width: '120'
+      },
+      {
+        label: '地址',
+        children: [
+          {
+            field: 'province',
+            label: '省份',
+            width: '120'
+          },
+          {
+            field: 'city',
+            label: '市区',
+            width: '120'
+          },
+          {
+            field: 'address',
+            label: '地址',
+            slots: {
+              default: 'address'
+            }
+          },
+          {
+            field: 'zip',
+            label: '邮编',
+            width: '120'
+          }
+        ]
+      }
+    ]
+  },
+  {
+    field: 'action',
+    label: '操作',
+    width: '100',
+    slots: {
+      default: 'action'
+    }
+  }
+]
+
+const tableData = ref<any[]>([
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1518 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1518 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1518 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1518 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-08',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1518 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-06',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1518 弄',
+    zip: 200333
+  },
+  {
+    date: '2016-05-07',
+    name: '王小虎',
+    province: '上海',
+    city: '普陀区',
+    address: '上海市普陀区金沙江路 1518 弄',
+    zip: 200333
+  }
+])
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+function deleteRow(index: number) {
+  tableData.value.splice(index, 1)
+}
+</script>
+
+<style></style>

+ 90 - 0
src/views/table-demo/multiple-choice/index.vue

@@ -0,0 +1,90 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 多选"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table
+      ref="multipleTable"
+      v-loading="loading"
+      selection
+      :columns="columns"
+      :data="tableData"
+      @selection-change="handleSelectionChange"
+    />
+
+    <div style="margin-top: 20px">
+      <el-button @click="toggleSelection([tableData[1], tableData[2]])"
+        >切换第二、第三行的选中状态</el-button
+      >
+      <el-button @click="toggleSelection()">取消选择</el-button>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts" name="MultipleChoice">
+import { ref, unref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'address',
+    label: '地址'
+  }
+]
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄'
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄'
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+const multipleTable = ref<HTMLElement | null>(null)
+function toggleSelection(rows?: any[]) {
+  const multipleTableRef = unref(multipleTable as any).getTableRef()
+  if (rows) {
+    rows.forEach((row) => {
+      multipleTableRef.toggleRowSelection(row)
+    })
+  } else {
+    multipleTableRef.clearSelection()
+  }
+}
+function handleSelectionChange(val: any) {
+  console.log(val)
+}
+</script>
+
+<style></style>

+ 79 - 0
src/views/table-demo/page-table/index.vue

@@ -0,0 +1,79 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 分页表格"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table
+      v-loading="loading"
+      :columns="columns"
+      :data="tableData"
+      :pagination="{
+        currentPage: 1,
+        total: 400,
+        onSizeChange: handleSizeChange,
+        onCurrentChange: handleCurrentChange
+      }"
+    />
+  </div>
+</template>
+
+<script setup lang="ts" name="PageTable">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'address',
+    label: '地址'
+  }
+]
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄'
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄'
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+function handleSizeChange(val: number) {
+  console.log(val)
+}
+
+function handleCurrentChange(val: number) {
+  console.log(val)
+}
+</script>
+
+<style></style>

+ 124 - 0
src/views/table-demo/screen-table/index.vue

@@ -0,0 +1,124 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 筛选"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <el-button @click="resetDateFilter">清除日期过滤器</el-button>
+    <el-button @click="clearFilter">清除所有过滤器</el-button>
+    <com-table
+      ref="filterTable"
+      v-loading="loading"
+      row-key="date"
+      :columns="columns"
+      :data="tableData"
+      :default-sort="{ prop: 'date', order: 'descending' }"
+    >
+      <template #tag="scope">
+        <el-tag
+          :type="(scope.row.tag === '家' ? 'primary' : 'success') as any"
+          disable-transitions
+          >{{ scope.row.tag }}</el-tag
+        >
+      </template>
+    </com-table>
+  </div>
+</template>
+
+<script setup lang="ts" name="ScreenTable">
+import { ref, unref } from 'vue'
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄',
+    tag: '家'
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄',
+    tag: '公司'
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄',
+    tag: '家'
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄',
+    tag: '公司'
+  }
+]
+
+const filterTable = ref<HTMLElement | null>(null)
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+const columns = ref<any[]>([
+  {
+    field: 'date',
+    label: '日期',
+    sortable: true,
+    width: '180',
+    columnKey: 'date',
+    filters: [
+      { text: '2016-05-01', value: '2016-05-01' },
+      { text: '2016-05-02', value: '2016-05-02' },
+      { text: '2016-05-03', value: '2016-05-03' },
+      { text: '2016-05-04', value: '2016-05-04' }
+    ],
+    filterMethod: filterHandler
+  },
+  {
+    field: 'name',
+    label: '姓名',
+    sortable: true
+  },
+  {
+    field: 'address',
+    label: '地址'
+  },
+  {
+    field: 'tag',
+    label: '标签',
+    filters: [
+      { text: '家', value: '家' },
+      { text: '公司', value: '公司' }
+    ],
+    filterMethod: filterTag,
+    filterPlacement: 'bottom-end',
+    slots: {
+      default: 'tag'
+    }
+  }
+])
+
+function resetDateFilter() {
+  const filterTableRef = unref(filterTable as any).getTableRef()
+  filterTableRef.clearFilter('date')
+}
+function clearFilter() {
+  const filterTableRef = unref(filterTable as any).getTableRef()
+  filterTableRef.clearFilter()
+}
+function filterTag(value: string, row: any) {
+  return row.tag === value
+}
+function filterHandler(value: string, row: any, column: any) {
+  const property = column['property']
+  return row[property] === value
+}
+</script>
+
+<style></style>

+ 82 - 0
src/views/table-demo/single-choice/index.vue

@@ -0,0 +1,82 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 单选"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table
+      ref="singleTable"
+      v-loading="loading"
+      highlight-current-row
+      :columns="columns"
+      :data="tableData"
+      @current-change="handleCurrentChange"
+    />
+
+    <div style="margin-top: 20px">
+      <el-button @click="setCurrent(tableData[1])">选中第二行</el-button>
+      <el-button @click="setCurrent()">取消选择</el-button>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts" name="SingleChoice">
+import { ref, unref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'address',
+    label: '地址'
+  }
+]
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄'
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄'
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+const singleTable = ref<HTMLElement | null>(null)
+function setCurrent(row?: any) {
+  const singleTableRef = unref(singleTable as any).getTableRef()
+  singleTableRef.setCurrentRow(row)
+}
+function handleCurrentChange(val: any) {
+  console.log(val)
+}
+</script>
+
+<style></style>

+ 69 - 0
src/views/table-demo/sort-table/index.vue

@@ -0,0 +1,69 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 排序"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table
+      ref="multipleTable"
+      v-loading="loading"
+      :columns="columns"
+      :data="tableData"
+      :default-sort="{ prop: 'date', order: 'descending' }"
+    />
+  </div>
+</template>
+
+<script setup lang="ts" name="SortTable">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期',
+    sortable: true
+  },
+  {
+    field: 'name',
+    label: '姓名',
+    sortable: true
+  },
+  {
+    field: 'address',
+    label: '地址'
+  }
+]
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄'
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄'
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+</script>
+
+<style></style>

+ 85 - 0
src/views/table-demo/state-table/index.vue

@@ -0,0 +1,85 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 带状态表格"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table
+      v-loading="loading"
+      :columns="columns"
+      :data="tableData"
+      :row-class-name="tableRowClassName"
+    />
+  </div>
+</template>
+
+<script setup lang="ts" name="StateTable">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'address',
+    label: '地址'
+  }
+]
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄'
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄'
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+function tableRowClassName({ rowIndex }: any) {
+  if (rowIndex === 1) {
+    return 'warning-row'
+  } else if (rowIndex === 3) {
+    return 'success-row'
+  }
+  return ''
+}
+</script>
+
+<style lang="less" scoped>
+:deep(.el-table) {
+  .warning-row {
+    background: oldlace;
+  }
+
+  .success-row {
+    background: #f0f9eb;
+  }
+}
+</style>

+ 61 - 0
src/views/table-demo/stripe-table/index.vue

@@ -0,0 +1,61 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 带斑马纹表格"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table v-loading="loading" :columns="columns" :data="tableData" stripe />
+  </div>
+</template>
+
+<script setup lang="ts" name="StripeTable">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'address',
+    label: '地址'
+  }
+]
+
+const tableData = [
+  {
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄'
+  },
+  {
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄'
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+</script>
+
+<style></style>

+ 148 - 0
src/views/table-demo/total-table/index.vue

@@ -0,0 +1,148 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 表尾合计行"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table v-loading="loading" :columns="columns" :data="tableData" border show-summary />
+
+    <com-table
+      v-loading="loading"
+      :columns="columns1"
+      :data="tableData"
+      border
+      height="200"
+      :summary-method="getSummaries"
+      show-summary
+      style="margin-top: 20px"
+    />
+  </div>
+</template>
+
+<script setup lang="ts" name="TotalTable">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'id',
+    label: 'ID'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'amount1',
+    label: '数值1',
+    sortable: true
+  },
+  {
+    field: 'amount2',
+    label: '数值2',
+    sortable: true
+  },
+  {
+    field: 'amount3',
+    label: '数值4',
+    sortable: true
+  }
+]
+
+const columns1 = [
+  {
+    field: 'id',
+    label: 'ID'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'amount1',
+    label: '数值1(元)'
+  },
+  {
+    field: 'amount2',
+    label: '数值2(元)'
+  },
+  {
+    field: 'amount3',
+    label: '数值4(元)'
+  }
+]
+
+const tableData = [
+  {
+    id: '12987122',
+    name: '王小虎',
+    amount1: '234',
+    amount2: '3.2',
+    amount3: 10
+  },
+  {
+    id: '12987123',
+    name: '王小虎',
+    amount1: '165',
+    amount2: '4.43',
+    amount3: 12
+  },
+  {
+    id: '12987124',
+    name: '王小虎',
+    amount1: '324',
+    amount2: '1.9',
+    amount3: 9
+  },
+  {
+    id: '12987125',
+    name: '王小虎',
+    amount1: '621',
+    amount2: '2.2',
+    amount3: 17
+  },
+  {
+    id: '12987126',
+    name: '王小虎',
+    amount1: '539',
+    amount2: '4.1',
+    amount3: 15
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+function getSummaries(param: any) {
+  const { columns, data } = param
+  const sums: any[] = []
+  columns.forEach((column: any, index: number) => {
+    if (index === 0) {
+      sums[index] = '总价'
+      return
+    }
+    const values = data.map((item: any) => Number(item[column.property]))
+    if (!values.every((value: number) => isNaN(value))) {
+      sums[index] = values.reduce((prev: number, curr: number) => {
+        const value = Number(curr)
+        if (!isNaN(value)) {
+          return prev + curr
+        } else {
+          return prev
+        }
+      }, 0)
+      sums[index] += ' 元'
+    } else {
+      sums[index] = 'N/A'
+    }
+  })
+
+  return sums
+}
+</script>
+
+<style></style>

+ 163 - 0
src/views/table-demo/tree-and-load/index.vue

@@ -0,0 +1,163 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="基于 Element 的 Table 组件进行二次封装,实现数据驱动,支持所有 Table 参数 -- 树形数据与懒加载"
+      type="info"
+      style="margin-bottom: 20px"
+    />
+    <com-table
+      v-loading="loading"
+      :columns="columns"
+      :data="tableData"
+      row-key="id"
+      border
+      default-expand-all
+      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+    />
+
+    <com-table
+      v-loading="loading"
+      :columns="columns1"
+      :data="tableData1"
+      row-key="id"
+      border
+      lazy
+      :load="load"
+      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+      style="margin-top: 20px"
+    />
+  </div>
+</template>
+
+<script setup lang="ts" name="TreeAndLoad">
+import { ref } from 'vue'
+
+const columns = [
+  {
+    field: 'date',
+    label: '日期',
+    sortable: true
+  },
+  {
+    field: 'name',
+    label: '姓名',
+    sortable: true
+  },
+  {
+    field: 'address',
+    label: '地址'
+  }
+]
+
+const columns1 = [
+  {
+    field: 'date',
+    label: '日期'
+  },
+  {
+    field: 'name',
+    label: '姓名'
+  },
+  {
+    field: 'address',
+    label: '地址'
+  }
+]
+
+const tableData = [
+  {
+    id: 1,
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    id: 2,
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    id: 3,
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄',
+    children: [
+      {
+        id: 31,
+        date: '2016-05-01',
+        name: '王小虎',
+        address: '上海市普陀区金沙江路 1519 弄'
+      },
+      {
+        id: 32,
+        date: '2016-05-01',
+        name: '王小虎',
+        address: '上海市普陀区金沙江路 1519 弄'
+      }
+    ]
+  },
+  {
+    id: 4,
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄'
+  }
+]
+
+const tableData1 = [
+  {
+    id: 1,
+    date: '2016-05-02',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1518 弄'
+  },
+  {
+    id: 2,
+    date: '2016-05-04',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1517 弄'
+  },
+  {
+    id: 3,
+    date: '2016-05-01',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1519 弄',
+    hasChildren: true
+  },
+  {
+    id: 4,
+    date: '2016-05-03',
+    name: '王小虎',
+    address: '上海市普陀区金沙江路 1516 弄'
+  }
+]
+
+const loading = ref<boolean>(true)
+setTimeout(() => {
+  loading.value = false
+}, 1000)
+
+function load(_: any, __: any, resolve: Function) {
+  setTimeout(() => {
+    resolve([
+      {
+        id: 31,
+        date: '2016-05-01',
+        name: '王小虎',
+        address: '上海市普陀区金沙江路 1519 弄'
+      },
+      {
+        id: 32,
+        date: '2016-05-01',
+        name: '王小虎',
+        address: '上海市普陀区金沙江路 1519 弄'
+      }
+    ])
+  }, 1000)
+}
+</script>
+
+<style></style>