Browse Source

feat: 🎸 Table组件重构完成并给出相应示例

chenkl 4 years ago
parent
commit
35879f8ecc
50 changed files with 1059 additions and 452 deletions
  1. 91 0
      mock/example/index.ts
  2. 63 0
      mock/index.ts
  3. 68 0
      mock/mock-server.ts
  4. 1 1
      package.json
  5. 0 73
      src/components/Button/index.vue
  6. 6 6
      src/components/Editor/props.ts
  7. 23 0
      src/components/Message/index.ts
  8. 10 10
      src/components/Search/index.vue
  9. 1 1
      src/components/Table/components/Slot.vue
  10. 11 11
      src/components/Table/components/TableColumn.vue
  11. 105 50
      src/components/Table/index.vue
  12. 3 3
      src/directives/clipboard/index.ts
  13. 36 2
      src/hooks/useExample.ts
  14. 2 2
      src/libs/element.ts
  15. 2 2
      src/pages/index/api/modules/example.ts
  16. 4 3
      src/pages/index/axios-config/request.ts
  17. 2 2
      src/pages/index/main.ts
  18. 60 20
      src/pages/index/router/index.ts
  19. 1 1
      src/pages/index/store/modules/app.ts
  20. 39 45
      src/pages/index/views/components-demo/button/index.vue
  21. 34 0
      src/pages/index/views/components-demo/message/index.vue
  22. 3 3
      src/pages/index/views/components-demo/preview/index.vue
  23. 3 3
      src/pages/index/views/directives-demo/clipboard/index.vue
  24. 90 1
      src/pages/index/views/example-demo/example/index.vue
  25. 2 2
      src/pages/index/views/hooks-demo/useWatermark/index.vue
  26. 1 1
      src/pages/index/views/table-demo/basic-table/index.vue
  27. 1 1
      src/pages/index/views/table-demo/border-table/index.vue
  28. 1 1
      src/pages/index/views/table-demo/custom-header/index.vue
  29. 85 0
      src/pages/index/views/table-demo/custom-index/index.vue
  30. 1 1
      src/pages/index/views/table-demo/expand-row/index.vue
  31. 1 1
      src/pages/index/views/table-demo/fixed-column-header/index.vue
  32. 1 1
      src/pages/index/views/table-demo/fixed-column/index.vue
  33. 1 1
      src/pages/index/views/table-demo/fixed-header/index.vue
  34. 1 1
      src/pages/index/views/table-demo/fluid-height/index.vue
  35. 164 0
      src/pages/index/views/table-demo/merge-table/index.vue
  36. 1 1
      src/pages/index/views/table-demo/multi-header/index.vue
  37. 1 1
      src/pages/index/views/table-demo/multiple-choice/index.vue
  38. 93 0
      src/pages/index/views/table-demo/page-table/index.vue
  39. 1 1
      src/pages/index/views/table-demo/screen-table/index.vue
  40. 1 1
      src/pages/index/views/table-demo/single-choice/index.vue
  41. 1 1
      src/pages/index/views/table-demo/sort-table/index.vue
  42. 1 1
      src/pages/index/views/table-demo/state-table/index.vue
  43. 1 1
      src/pages/index/views/table-demo/stripe-table/index.vue
  44. 1 1
      src/pages/index/views/table-demo/total-table/index.vue
  45. 1 1
      src/pages/index/views/table-demo/tree-and-load/index.vue
  46. 35 122
      src/styles/button.less
  47. 1 3
      src/styles/index.less
  48. 0 35
      src/utils/event/resize-event.ts
  49. 0 30
      src/utils/scrollbar-width.ts
  50. 4 4
      yarn.lock

+ 91 - 0
mock/example/index.ts

@@ -0,0 +1,91 @@
+import Mock from 'mockjs'
+import { toAnyString } from '@/utils'
+
+const List: any[] = []
+const count = 100
+
+const baseContent = '<p>I am testing data, I am testing data.</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>'
+const image_uri = 'https://wpimg.wallstcn.com/e4558086-631c-425c-9430-56ffb46e70b3'
+
+for (let i = 0; i < count; i++) {
+  List.push(Mock.mock({
+    id: toAnyString(),
+    timestamp: +Mock.Random.date('T'),
+    author: '@first',
+    reviewer: '@first',
+    title: '@title(5, 10)',
+    content_short: 'mock data',
+    content: baseContent,
+    forecast: '@float(0, 100, 2, 2)',
+    importance: '@integer(1, 3)',
+    'type|1': ['CN', 'US', 'JP', 'EU'],
+    'status|1': ['published', 'draft', 'deleted'],
+    display_time: '@datetime',
+    comment_disabled: true,
+    pageviews: '@integer(300, 5000)',
+    image_uri,
+    platforms: ['a-platform']
+  }))
+}
+
+export default [
+  {
+    url: 'http://mockjs.test.cn/example/list',
+    type: 'get',
+    response: (config: any) => {
+      const { title, pageIndex, pageSize } = config.query
+
+      const mockList = List.filter(item => {
+        if (title && item.title.indexOf(title) < 0) return false
+        return true
+      })
+      const pageList = mockList.filter((item, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1))
+
+      return {
+        code: '0000',
+        data: {
+          total: mockList.length,
+          list: pageList
+        }
+      }
+    }
+  },
+
+  {
+    url: 'http://mockjs.test.cn/example/delete',
+    type: 'post',
+    response: (config: any) => {
+      return {
+        code: '0000',
+        data: '删除成功'
+      }
+    }
+  },
+
+  {
+    url: 'http://mockjs.test.cn/example/detail',
+    type: 'get',
+    response: (config: any) => {
+      const { id } = config.query
+      for (const example of List) {
+        if (example.id === id) {
+          return {
+            code: '0000',
+            data: example
+          }
+        }
+      }
+    }
+  },
+
+  {
+    url: 'http://mockjs.test.cn/example/save',
+    type: 'post',
+    response: (config: any) => {
+      return {
+        code: '0000',
+        data: 'success'
+      }
+    }
+  }
+]

+ 63 - 0
mock/index.ts

@@ -0,0 +1,63 @@
+import Mock from 'mockjs'
+import { param2Obj } from '@/utils'
+
+import example from './example'
+
+const mocks: any[] = [
+  ...example
+]
+
+// for front mock
+// please use it cautiously, it will redefine XMLHttpRequest,
+// which will cause many of your third-party libraries to be invalidated(like progress event).
+export function mockXHR() {
+  const send: any = (Mock as any).XHR.prototype.send;
+  ((Mock as any).XHR.prototype as any).proxy_send = send;
+  (Mock as any).XHR.prototype.send = function() {
+    if (this.custom.xhr) {
+      this.custom.xhr.withCredentials = this.withCredentials || false
+
+      if (this.responseType) {
+        this.custom.xhr.responseType = this.responseType
+      }
+    }
+    this.proxy_send(...arguments)
+  }
+
+  function XHR2ExpressReqWrap(respond: any) {
+    return function(options: any) {
+      let result = null
+      if (respond instanceof Function) {
+        const { body, type, url } = options
+        // https://expressjs.com/en/4x/api.html#req
+        result = respond({
+          method: type,
+          body: JSON.parse(body),
+          query: param2Obj(url)
+        })
+      } else {
+        result = respond
+      }
+      return Mock.mock(result)
+    }
+  }
+
+  for (const i of mocks) {
+    Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response))
+  }
+}
+
+// for mock server
+const responseFake = (url: string, type: string, respond: any) => {
+  return {
+    url: new RegExp(`${url}`),
+    type: type || 'get',
+    response(req: any, res: any) {
+      res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
+    }
+  }
+}
+
+export default mocks.map(route => {
+  return responseFake(route.url, route.type, route.response)
+})

+ 68 - 0
mock/mock-server.ts

@@ -0,0 +1,68 @@
+const chokidar = require('chokidar')
+const bodyParser = require('body-parser')
+const chalk = require('chalk')
+const path = require('path')
+
+const mockDir = path.join(process.cwd(), 'mock')
+
+function registerRoutes(app) {
+  let mockLastIndex
+  const { default: mocks } = require('./index.ts')
+  for (const mock of mocks) {
+    app[mock.type](mock.url, mock.response)
+    mockLastIndex = app._router.stack.length
+  }
+  const mockRoutesLength = Object.keys(mocks).length
+  return {
+    mockRoutesLength: mockRoutesLength,
+    mockStartIndex: mockLastIndex - mockRoutesLength
+  }
+}
+
+function unregisterRoutes() {
+  Object.keys(require.cache).forEach(i => {
+    if (i.includes(mockDir)) {
+      delete require.cache[require.resolve(i)]
+    }
+  })
+}
+
+module.exports = app => {
+  // es6 polyfill
+  require('@babel/register')
+
+  // parse app.body
+  // https://expressjs.com/en/4x/api.html#req.body
+  app.use(bodyParser.json())
+  app.use(bodyParser.urlencoded({
+    extended: true
+  }))
+
+  const mockRoutes = registerRoutes(app)
+  var mockRoutesLength = mockRoutes.mockRoutesLength
+  var mockStartIndex = mockRoutes.mockStartIndex
+
+  // watch files, hot reload mock server
+  chokidar.watch(mockDir, {
+    ignored: /mock-server/,
+    ignoreInitial: true
+  }).on('all', (event, path) => {
+    if (event === 'change' || event === 'add') {
+      try {
+        // remove mock routes stack
+        app._router.stack.splice(mockStartIndex, mockRoutesLength)
+
+        // clear routes cache
+        unregisterRoutes()
+
+        const mockRoutes = registerRoutes(app)
+        mockRoutesLength = mockRoutes.mockRoutesLength
+        mockStartIndex = mockRoutes.mockStartIndex
+
+        console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed  ${path}`))
+      } catch (error) {
+        console.log(chalk.redBright(error))
+      }
+    }
+  })
+}

+ 1 - 1
package.json

@@ -19,7 +19,7 @@
     "clipboard": "^2.0.6",
     "core-js": "^3.6.5",
     "echarts": "^4.9.0",
-    "element-plus": "1.0.1-beta.10",
+    "element-plus": "1.0.1-beta.11",
     "highlight.js": "^10.4.0",
     "lodash-es": "^4.17.15",
     "mockjs": "^1.1.0",

+ 0 - 73
src/components/Button/index.vue

@@ -1,73 +0,0 @@
-<template>
-  <Button v-bind="getBindValue" :class="[getColor, $attrs.class]">
-    <template #default="data">
-      <slot name="icon" />
-      <slot v-bind="data" />
-    </template>
-  </Button>
-</template>
-<script lang="ts">
-import { PropType } from 'vue'
-import { defineComponent, computed, VNodeChild } from 'vue'
-import { Button } from 'ant-design-vue'
-export default defineComponent({
-  name: 'AButton',
-  components: { Button },
-  inheritAttrs: false,
-  props: {
-    // 按钮类型
-    type: {
-      type: String as PropType<'primary' | 'default' | 'danger' | 'dashed' | 'link' | 'warning' | 'success' | 'info'>,
-      default: 'default'
-    },
-    disabled: {
-      type: Boolean as PropType<boolean>,
-      default: false
-    },
-    htmlType: {
-      type: String as PropType<'button' | 'submit' | 'reset' | 'menu'>,
-      default: 'button'
-    },
-    icon: {
-      type: Object as PropType<VNodeChild | JSX.Element>,
-      default: () => null
-    },
-    size: {
-      type: String as PropType<'small' | 'large' | 'default'>,
-      default: 'default'
-    },
-    loading: {
-      type: Boolean as PropType<boolean>,
-      default: false
-    },
-    ghost: {
-      type: Boolean as PropType<boolean>,
-      default: false
-    },
-    block: {
-      type: Boolean as PropType<boolean>,
-      default: false
-    }
-  },
-  setup(props, { attrs }) {
-    const getColor = computed(() => {
-      const res: string[] = []
-      const { type, disabled } = props
-      type && res.push(`ant-btn-${type}`)
-      disabled && res.push('is-disabled')
-      return res
-    })
-
-    const getBindValue = computed((): any => {
-      const otherTypes = ['warning', 'success', 'info']
-      const bindValue = { ...attrs, ...props }
-      if (otherTypes.indexOf(props.type) !== -1) {
-        bindValue.type = 'default'
-      }
-      return bindValue
-    })
-
-    return { getBindValue, getColor }
-  }
-})
-</script>

+ 6 - 6
src/components/Editor/props.ts

@@ -1,5 +1,5 @@
 import { PropType } from 'vue'
-import { ElMessage } from 'element-plus'
+import { Message } from '_c/Message'
 import { oneOf } from '@/utils'
 
 import { Config } from './types'
@@ -18,19 +18,19 @@ export const editorProps = {
         customAlert: (s: string, t: string) => {
           switch (t) {
           case 'success':
-            ElMessage.success(s)
+            Message.success(s)
             break
           case 'info':
-            ElMessage.info(s)
+            Message.info(s)
             break
           case 'warning':
-            ElMessage.warning(s)
+            Message.warning(s)
             break
           case 'error':
-            ElMessage.error(s)
+            Message.error(s)
             break
           default:
-            ElMessage.info(s)
+            Message.info(s)
             break
           }
         },

+ 23 - 0
src/components/Message/index.ts

@@ -0,0 +1,23 @@
+import { ElMessage } from 'element-plus'
+
+let messageInstance: any | null = null
+
+const resetMessage = (options: any) => {
+  if (messageInstance) {
+    messageInstance.close()
+  }
+  messageInstance = ElMessage(options)
+}
+['error', 'success', 'info', 'warning'].forEach((type: string) => {
+  resetMessage[type] = (options: any) => {
+    if (typeof options === 'string') {
+      options = {
+        message: options
+      }
+    }
+    options.type = type
+    return resetMessage(options)
+  }
+})
+
+export const Message = resetMessage as any

+ 10 - 10
src/components/Search/index.vue

@@ -22,7 +22,7 @@
             <template v-if="item.itemType === 'switch'">
               <el-switch
                 v-model="formInline[item.field]"
-                v-bind="{...bindValue(item)}"
+                v-bind="{...getItemBindValue(item)}"
                 @change="((val) => {changeVal(val, item)})"
               />
             </template>
@@ -30,7 +30,7 @@
             <template v-if="item.itemType === 'input'">
               <el-input
                 v-model="formInline[item.field]"
-                v-bind="{...bindValue(item)}"
+                v-bind="{...getItemBindValue(item)}"
                 @change="((val) => {changeVal(val, item)})"
               />
             </template>
@@ -38,7 +38,7 @@
             <template v-if="item.itemType === 'select'">
               <el-select
                 v-model="formInline[item.field]"
-                v-bind="{...bindValue(item)}"
+                v-bind="{...getItemBindValue(item)}"
                 @change="((val) => {changeVal(val, item)})"
               >
                 <el-option
@@ -59,7 +59,7 @@
                   <el-radio
                     v-for="v in item.options"
                     :key="item.optionValue ? v[item.optionValue] : v.value"
-                    v-bind="{...bindValue(item)}"
+                    v-bind="{...getItemBindValue(item)}"
                     :label="item.optionValue ? v[item.optionValue] : v.value"
                   >
                     {{ item.optionLabel ? v[item.optionLabel] : v.label }}
@@ -69,7 +69,7 @@
                   <el-radio-button
                     v-for="v in item.options"
                     :key="item.optionValue ? v[item.optionValue] : v.value"
-                    v-bind="{...bindValue(item)}"
+                    v-bind="{...getItemBindValue(item)}"
                     :label="item.optionValue ? v[item.optionValue] : v.value"
                   >
                     {{ item.optionLabel ? v[item.optionLabel] : v.label }}
@@ -102,7 +102,7 @@
             <template v-if="item.itemType === 'timePicker'">
               <el-time-picker
                 v-model="formInline[item.field]"
-                v-bind="{...bindValue(item)}"
+                v-bind="{...getItemBindValue(item)}"
                 @change="((val) => {changeVal(val, item)})"
               />
             </template>
@@ -110,7 +110,7 @@
             <template v-if="item.itemType === 'timeSelect'">
               <el-time-select
                 v-model="formInline[item.field]"
-                v-bind="{...bindValue(item)}"
+                v-bind="{...getItemBindValue(item)}"
                 @change="((val) => {changeVal(val, item)})"
               />
             </template>
@@ -118,7 +118,7 @@
             <template v-if="item.itemType === 'datePicker' || item.itemType === 'dateTimePicker'">
               <el-date-picker
                 v-model="formInline[item.field]"
-                v-bind="{...bindValue(item)}"
+                v-bind="{...getItemBindValue(item)}"
                 @change="((val) => {changeVal(val, item)})"
               />
             </template>
@@ -236,7 +236,7 @@ export default defineComponent({
       }
     )
 
-    function bindValue(item: any) {
+    function getItemBindValue(item: any) {
       const delArr: string[] = ['label', 'itemType', 'value', 'field']
       const obj = deepClone(item)
       for (const key in obj) {
@@ -288,7 +288,7 @@ export default defineComponent({
     }
 
     return {
-      bindValue,
+      getItemBindValue,
       ruleForm,
       formInline,
       submitForm,

+ 1 - 1
src/components/Table/components/Slot.vue

@@ -22,7 +22,7 @@ export default defineComponent({
   },
   render(props: any) {
     const _this: any = inject('tableRoot')
-    return h('div', _this.slots[props.slotName]({
+    return h('span', _this.slots[props.slotName]({
       row: props.row,
       column: props.column,
       index: props.index

+ 11 - 11
src/components/Table/components/TableColumn.vue

@@ -1,5 +1,5 @@
 <template>
-  <el-table-column v-bind="{...bindValue(child)}" :prop="child.key">
+  <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">
@@ -12,7 +12,7 @@
       <template v-else>
         <el-table-column
           :key="item[item.key]"
-          v-bind="{...bindValue(item)}"
+          v-bind="{...getItemBindValue(item)}"
           :prop="item.key"
         >
           <!-- 表头插槽 -->
@@ -20,25 +20,25 @@
             <table-slot
               v-if="item.slots && item.slots.header"
               :slot-name="item.slots.header"
-              :row="scope.row"
               :column="item"
               :index="scope.$index"
             />
           </template>
+
           <!-- 表格内容插槽自定义 -->
-          <template #default="scope">
+          <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"
             />
-            <!-- 不需要插槽 -->
-            <div v-else style="display: inline-block;">
-              {{ scope.row[item.key] }}
-            </div>
           </template>
+
+          <!-- 不需要插槽 -->
+          <!-- <span v-if="!item.slots || !item.slots.default">
+            {{ scope.row[item.key] }}
+          </span> -->
         </el-table-column>
       </template>
     </template>
@@ -62,7 +62,7 @@ export default defineComponent({
     }
   },
   setup() {
-    function bindValue(item: any) {
+    function getItemBindValue(item: any) {
       const delArr: string[] = ['children']
       const obj = deepClone(item)
       for (const key in obj) {
@@ -74,7 +74,7 @@ export default defineComponent({
     }
 
     return {
-      bindValue
+      getItemBindValue
     }
   }
 })

+ 105 - 50
src/components/Table/index.vue

@@ -1,60 +1,81 @@
 <template>
-  <el-table ref="elTable" v-bind="getBindValue">
-    <!-- 多选 -->
-    <el-table-column
-      v-if="selection"
-      type="selection"
-      width="55"
-    />
-    <template v-for="item in columns">
-      <!-- 树型数据 -->
-      <template v-if="item.children && item.children.length">
-        <table-column
-          :key="item[item.key]"
-          :child="item"
-        />
-      </template>
+  <div>
+    <el-table ref="elTable" :border="true" v-bind="getBindValue" @header-dragend="headerDragend">
+      <!-- 多选 -->
+      <el-table-column
+        v-if="selection"
+        type="selection"
+        width="55"
+      />
+      <template v-for="item in columns">
+        <!-- 自定义索引 -->
+        <template v-if="item.type === 'index'">
+          <el-table-column
+            :key="item[item.key]"
+            v-bind="{...getItemBindValue(item)}"
+            type="index"
+            :index="item.index"
+          />
+        </template>
+
+        <!-- 树型数据 -->
+        <template v-else-if="item.children && item.children.length">
+          <table-column
+            :key="item[item.key]"
+            :child="item"
+          />
+        </template>
 
-      <template v-else>
-        <el-table-column
-          :key="item[item.key]"
-          v-bind="{...bindValue(item)}"
-          :prop="item.key"
-        >
-          <!-- 表头插槽 -->
-          <template v-if="item.slots && item.slots.header" #header="scope">
-            <table-slot
-              v-if="item.slots && item.slots.header"
-              :slot-name="item.slots.header"
-              :row="scope.row"
-              :column="item"
-              :index="scope.$index"
-            />
-          </template>
+        <template v-else>
+          <el-table-column
+            :key="item[item.key]"
+            v-bind="{...getItemBindValue(item)}"
+            :prop="item.key"
+          >
+            <!-- 表头插槽 -->
+            <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 #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 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>
             <!-- 不需要插槽 -->
-            <div v-else style="display: inline-block;">
+            <!-- <span v-if="!item.slots || !item.slots.default">
               {{ scope.row[item.key] }}
-            </div>
-          </template>
-        </el-table-column>
+            </span> -->
+          </el-table-column>
+        </template>
       </template>
-    </template>
-  </el-table>
+    </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"
+      />
+    </div>
+  </div>
 </template>
 
 <script lang="ts">
 import { defineComponent, PropType, computed, provide, getCurrentInstance, ref, unref } from 'vue'
 import { deepClone } from '@/utils'
+import { isObject } from '@/utils/is'
 import TableColumn from './components/TableColumn.vue'
 import TableSlot from './components/Slot.vue'
 export default defineComponent({
@@ -71,6 +92,10 @@ export default defineComponent({
     selection: {
       type: Boolean as PropType<boolean>,
       default: false
+    },
+    pagination: {
+      type: [Boolean, Object] as PropType<boolean | object>,
+      default: false
     }
   },
   setup(props, { attrs, slots }) {
@@ -88,7 +113,7 @@ export default defineComponent({
       return bindValue
     })
 
-    function bindValue(item: any) {
+    function getItemBindValue(item: any) {
       const delArr: string[] = []
       const obj = deepClone(item)
       for (const key in obj) {
@@ -99,15 +124,45 @@ export default defineComponent({
       return obj
     }
 
+    const getPaginationBindValue = computed((): any => {
+      const PaginationBindValue = props.pagination && isObject(props.pagination)
+        ? { ...(props.pagination as any) }
+        : {}
+      return PaginationBindValue
+    })
+
+    const paginationStyle = computed(() => {
+      return {
+        textAlign: props.pagination && (props.pagination as any).position || 'right'
+      }
+    })
+
+    function headerDragend(newWidth: number, oldWidth: number, column: any, event: any) {
+      // 不懂为啥无法自动计算宽度,只能手动去计算了。。失望ing,到时候看看能不能优化吧。
+      const htmlArr = document.getElementsByClassName(column.id)
+      for (const v of htmlArr) {
+        if (v.firstElementChild) {
+          (v.firstElementChild as any).style.width = newWidth + 'px'
+        }
+      }
+    }
+
     return {
       elTable,
-      getBindValue, bindValue,
+      getBindValue, getItemBindValue,
       slots,
-      getTableRef
+      getTableRef,
+      getPaginationBindValue, paginationStyle,
+      headerDragend
     }
   }
 })
 </script>
 
-<style>
+<style lang="less" scoped>
+.pagination__wrap {
+  margin-top: 15px;
+  background: #fff;
+  padding: 10px;
+}
 </style>

+ 3 - 3
src/directives/clipboard/index.ts

@@ -1,6 +1,6 @@
 import Clipboard from 'clipboard'
 import { Directive, DirectiveBinding } from 'vue'
-import { ElMessage } from 'element-plus'
+import { Message } from '_c/Message'
 
 if (!Clipboard) {
   throw new Error('you should npm install `clipboard` --save at first ')
@@ -47,7 +47,7 @@ function createdClipboard(el: HTMLElement | any, arg: string | undefined, value:
       if (callback) {
         callback(e)
       } else {
-        ElMessage.success('复制成功')
+        Message.success('复制成功')
       }
     })
     clipboard.on('error', e => {
@@ -55,7 +55,7 @@ function createdClipboard(el: HTMLElement | any, arg: string | undefined, value:
       if (callback) {
         callback(e)
       } else {
-        ElMessage.success('复制失败')
+        Message.success('复制失败')
       }
     })
     el._v_clipboard = clipboard

+ 36 - 2
src/hooks/useExample.ts

@@ -1,6 +1,40 @@
 // 常用的增删改查 hook
-import { reactive } from 'vue'
+import { reactive, ref } from 'vue'
 
-export function() {
+interface DefalutParams {
+  pageIndex: number
+  pageSize: number
+}
+
+export function useExample() {
+  const defalutParams = reactive<DefalutParams>({
+    pageIndex: 1,
+    pageSize: 10
+  })
   
+  const tableData = ref<any[]>([])
+  
+  const loading = ref<boolean>(true)
+
+  const total = ref<number>(0)
+  
+  function sizeChange(val: number) {
+    loading.value = true
+    defalutParams.pageIndex = 1
+    defalutParams.pageSize = val
+  }
+  
+  function currentChange(val: number) {
+    loading.value = true
+    defalutParams.pageIndex = val
+  }
+
+  return {
+    defalutParams,
+    tableData,
+    loading,
+    total,
+    sizeChange,
+    currentChange
+  }
 }

+ 2 - 2
src/libs/element.ts

@@ -87,7 +87,7 @@ import {
   ElUpload,
   ElInfiniteScroll,
   ElLoading,
-  ElMessage,
+  // ElMessage,
   ElMessageBox,
   ElNotification
 } from 'element-plus'
@@ -182,7 +182,7 @@ const components = [
 const plugins = [
   ElInfiniteScroll,
   ElLoading,
-  ElMessage,
+  // ElMessage,
   ElMessageBox,
   ElNotification
 ]

+ 2 - 2
src/pages/index/api/modules/common.ts → src/pages/index/api/modules/example.ts

@@ -10,8 +10,8 @@ interface PropsData {
 }
 
 const methods: EmptyObjFun = {
-  getList: function({ params }: PropsData): AxiosPromise {
-    return fetch({ url: '/getJoke', method: 'get', params })
+  getExampleList: function({ params }: PropsData): AxiosPromise {
+    return fetch({ url: '/example/list', method: 'get', params })
   }
 }
 

+ 4 - 3
src/pages/index/axios-config/request.ts

@@ -1,6 +1,6 @@
 import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
 
-import { ElMessage } from 'element-plus'
+import { Message } from '_c/Message'
 
 import qs from 'qs'
 
@@ -19,6 +19,7 @@ const service: AxiosInstance = axios.create({
 // request拦截器
 service.interceptors.request.use(
   (config: AxiosRequestConfig) => {
+    console.log(config)
     if (config.method === 'post' && config.headers['Content-Type'] === 'application/x-www-form-urlencoded') {
       config.data = qs.stringify(config.data)
     }
@@ -37,12 +38,12 @@ service.interceptors.response.use(
     if (response.data.code === result_code) {
       return response.data
     } else {
-      ElMessage.error(response.data.message)
+      Message.error(response.data.message)
     }
   },
   (error: AxiosError) => {
     console.log('err' + error) // for debug
-    ElMessage.error(error.message)
+    Message.error(error.message)
     return Promise.reject(error)
   }
 )

+ 2 - 2
src/pages/index/main.ts

@@ -29,7 +29,7 @@ import { setupGlobCom } from '@/components' // 全局公用组件
 
 import { setupDirectives } from '@/directives' // 自定义指令
 
-// import { mockXHR } from '../../../mock'
+import { mockXHR } from '../../../mock'
 
 import '@/styles/reset.css' // 重置不同浏览器之间的标签默认样式
 
@@ -49,7 +49,7 @@ setupGlobCom(app) // 注册全局公用组件
 
 setupDirectives(app) // 注册全局自定义指令
 
-// mockXHR() // mock注册
+mockXHR() // mock注册
 
 router.isReady().then(() => {
   app.mount('#app')

+ 60 - 20
src/pages/index/router/index.ts

@@ -123,6 +123,22 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
           title: '图片预览'
         }
       },
+      {
+        path: 'button',
+        component: () => import('_p/index/views/components-demo/button/index.vue'),
+        name: 'ButtonDemo',
+        meta: {
+          title: '按钮'
+        }
+      },
+      {
+        path: 'message',
+        component: () => import('_p/index/views/components-demo/message/index.vue'),
+        name: 'MessageDemo',
+        meta: {
+          title: '消息提示'
+        }
+      },
       {
         path: 'count-to',
         component: () => import('_p/index/views/components-demo/count-to/index.vue'),
@@ -184,6 +200,14 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
           title: '基础表格'
         }
       },
+      {
+        path: 'page-table',
+        component: () => import('_p/index/views/table-demo/page-table/index.vue'),
+        name: 'PageTable',
+        meta: {
+          title: '分页表格'
+        }
+      },
       {
         path: 'stripe-table',
         component: () => import('_p/index/views/table-demo/stripe-table/index.vue'),
@@ -312,6 +336,22 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
           title: '表尾合计行'
         }
       },
+      {
+        path: 'merge-table',
+        component: () => import('_p/index/views/table-demo/merge-table/index.vue'),
+        name: 'MergeTable',
+        meta: {
+          title: '合并行或列'
+        }
+      },
+      {
+        path: 'custom-index',
+        component: () => import('_p/index/views/table-demo/custom-index/index.vue'),
+        name: 'CustomIndex',
+        meta: {
+          title: '自定义索引'
+        }
+      }
     ]
   },
   {
@@ -440,26 +480,26 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
       }
     ]
   },
-  // {
-  //   path: '/example-demo',
-  //   component: Layout,
-  //   name: 'ExampleDemo',
-  //   meta: {
-  //     alwaysShow: true,
-  //     icon: 'example',
-  //     title: '综合实例'
-  //   },
-  //   children: [
-  //     {
-  //       path: 'example',
-  //       component: () => import('_p/index/views/example-demo/example/index.vue'),
-  //       name: 'Example',
-  //       meta: {
-  //         title: '列表综合实例'
-  //       }
-  //     }
-  //   ]
-  // }
+  {
+    path: '/example-demo',
+    component: Layout,
+    name: 'ExampleDemo',
+    meta: {
+      alwaysShow: true,
+      icon: 'example',
+      title: '综合实例'
+    },
+    children: [
+      {
+        path: 'example',
+        component: () => import('_p/index/views/example-demo/example/index.vue'),
+        name: 'Example',
+        meta: {
+          title: '列表综合实例'
+        }
+      }
+    ]
+  }
 ]
 
 const router = createRouter({

+ 1 - 1
src/pages/index/store/modules/app.ts

@@ -28,7 +28,7 @@ class App extends VuexModule implements AppState {
   // public fixedTags = true // 是否固定标签栏
   // public fixedNavbar = true // 是否固定navbar
   public fixedHeader = true // 是否固定header
-  public layout = 'Classic' // layout布局
+  public layout = 'Top' // layout布局
   public showBreadcrumb = true // 是否显示面包屑
   public showHamburger = true // 是否显示侧边栏缩收按钮
   public showScreenfull = true // 是否全屏按钮

+ 39 - 45
src/pages/index/views/components-demo/button/index.vue

@@ -1,48 +1,48 @@
 <template>
   <div>
-    <el-alert message="扩展 antdv 的 Button 组件,实现 primary、success、warning、info、default 等主题样式。" style="margin-bottom: 20px;" />
-    <div class="btn__wrap">default</div>
-    <div class="btn__item">
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="对 Element 的 Button 组件进行美化,实现水波纹样式,如不需要,可在 src/styles/button.less 进行更改注释。"
+      type="info"
+      style="margin-bottom: 20px;"
+    />
+
+    <el-row style="margin-bottom: 20px;">
       <el-button>默认按钮</el-button>
-      <el-button shape="round">默认按钮</el-button>
-      <el-button loading>默认按钮</el-button>
-      <el-button disabled>默认按钮</el-button>
-    </div>
-    <div class="btn__wrap">primary</div>
-    <div class="btn__item">
       <el-button type="primary">主要按钮</el-button>
-      <el-button type="primary" shape="round">主要按钮</el-button>
-      <el-button type="primary" loading>主要按钮</el-button>
-      <el-button type="primary" disabled>主要按钮</el-button>
-    </div>
-    <div class="btn__wrap">success</div>
-    <div class="btn__item">
       <el-button type="success">成功按钮</el-button>
-      <el-button type="success" shape="round">成功按钮</el-button>
-      <el-button type="success" loading>成功按钮</el-button>
-      <el-button type="success" disabled>成功按钮</el-button>
-    </div>
-    <div class="btn__wrap">warning</div>
-    <div class="btn__item">
+      <el-button type="info">信息按钮</el-button>
       <el-button type="warning">警告按钮</el-button>
-      <el-button type="warning" shape="round">警告按钮</el-button>
-      <el-button type="warning" loading>警告按钮</el-button>
-      <el-button type="warning" disabled>警告按钮</el-button>
-    </div>
-    <div class="btn__wrap">danger</div>
-    <div class="btn__item">
       <el-button type="danger">危险按钮</el-button>
-      <el-button type="danger" shape="round">危险按钮</el-button>
-      <el-button type="danger" loading>危险按钮</el-button>
-      <el-button type="danger" disabled>危险按钮</el-button>
-    </div>
-    <div class="btn__wrap">info</div>
-    <div class="btn__item">
-      <el-button type="info">信息按钮</el-button>
-      <el-button type="info" shape="round">信息按钮</el-button>
-      <el-button type="info" loading>信息按钮</el-button>
-      <el-button type="info" disabled>信息按钮</el-button>
-    </div>
+    </el-row>
+
+    <el-row style="margin-bottom: 20px;">
+      <el-button plain>朴素按钮</el-button>
+      <el-button type="primary" plain>主要按钮</el-button>
+      <el-button type="success" plain>成功按钮</el-button>
+      <el-button type="info" plain>信息按钮</el-button>
+      <el-button type="warning" plain>警告按钮</el-button>
+      <el-button type="danger" plain>危险按钮</el-button>
+    </el-row>
+
+    <el-row style="margin-bottom: 20px;">
+      <el-button round>圆角按钮</el-button>
+      <el-button type="primary" round>主要按钮</el-button>
+      <el-button type="success" round>成功按钮</el-button>
+      <el-button type="info" round>信息按钮</el-button>
+      <el-button type="warning" round>警告按钮</el-button>
+      <el-button type="danger" round>危险按钮</el-button>
+    </el-row>
+
+    <el-row style="margin-bottom: 20px;">
+      <el-button icon="el-icon-search" circle />
+      <el-button type="primary" icon="el-icon-edit" circle />
+      <el-button type="success" icon="el-icon-check" circle />
+      <el-button type="info" icon="el-icon-message" circle />
+      <el-button type="warning" icon="el-icon-star-off" circle />
+      <el-button type="danger" icon="el-icon-delete" circle />
+    </el-row>
   </div>
 </template>
 
@@ -53,11 +53,5 @@ export default defineComponent({
 })
 </script>
 
-<style lang="less" scoped>
-.btn__wrap {
-  margin-bottom: 10px;
-}
-.btn__item {
-  margin-bottom: 20px;
-}
+<style>
 </style>

+ 34 - 0
src/pages/index/views/components-demo/message/index.vue

@@ -0,0 +1,34 @@
+<template>
+  <div>
+    <el-alert
+      effect="dark"
+      :closable="false"
+      title="二次封装 Element 的 Message 组件,每次只显示最新一条消息,避免出现太多消息提示导致不美观。"
+      type="info"
+      style="margin-bottom: 20px;"
+    />
+    <el-button @click="show">显示</el-button>
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from 'vue'
+import { Message } from '_c/Message'
+export default defineComponent({
+  name: 'MessageDemo',
+  setup() {
+    const count = ref<number>(0)
+    function show() {
+      count.value = count.value + 1
+      Message.success('这是成功消息' + count.value)
+    }
+    return {
+      count,
+      show
+    }
+  }
+})
+</script>
+
+<style>
+</style>

+ 3 - 3
src/pages/index/views/components-demo/preview/index.vue

@@ -49,7 +49,7 @@
 <script lang="ts">
 import { defineComponent, ref } from 'vue'
 import { createImgPreview } from '_c/Preview/functional'
-import { ElMessage } from 'element-plus'
+import { Message } from '_c/Message'
 export default defineComponent({
   // name: 'PreviewDemo',
   setup() {
@@ -83,10 +83,10 @@ export default defineComponent({
         show: true,
         index: 0,
         onSelect: (i: number) => {
-          ElMessage.info('当前点击的图片索引:' + i)
+          Message.info('当前点击的图片索引:' + i)
         },
         onClose: (i: number) => {
-          ElMessage.info('关闭之后的图片索引:' + i)
+          Message.info('关闭之后的图片索引:' + i)
         }
       })
     }

+ 3 - 3
src/pages/index/views/directives-demo/clipboard/index.vue

@@ -40,17 +40,17 @@
 
 <script lang="ts">
 import { defineComponent, ref } from 'vue'
-import { ElMessage } from 'element-plus'
+import { Message } from '_c/Message'
 export default defineComponent({
   // name: 'Clipboard'
   setup() {
     const inputVal1 = ref<string>('')
     const inputVal2 = ref<string>('')
     function clipboardSuccess(val: any) {
-      ElMessage.success('我是自定义成功回调:' + val.text)
+      Message.success('我是自定义成功回调:' + val.text)
     }
     function clipboardError() {
-      ElMessage.error('我是自定义失败回调')
+      Message.error('我是自定义失败回调')
     }
     return {
       inputVal1, inputVal2,

+ 90 - 1
src/pages/index/views/example-demo/example/index.vue

@@ -1,9 +1,98 @@
-<template />
+<template>
+  <div>
+    <com-table
+      v-loading="loading"
+      :columns="columns"
+      :data="tableData"
+      :pagination="{
+        currentPage: defalutParams.pageIndex,
+        total: total,
+        onSizeChange: handleSizeChange,
+        onCurrentChange: handleCurrentChange
+      }"
+    />
+  </div>
+</template>
 
 <script lang="ts">
 import { defineComponent } from 'vue'
+import ComTable from '_c/Table/index.vue'
+import Search from '_c/Search/index.vue'
+import { useExample } from '@/hooks/useExample'
+
+import api from '_p/index/api'
+
+const columns = [
+  {
+    key: 'title',
+    label: '标题',
+    showOverflowTooltip: true
+  },
+  {
+    key: 'author',
+    label: '作者'
+  },
+  {
+    key: 'display_time',
+    label: '创建时间'
+  },
+  {
+    key: 'importance',
+    label: '重要性'
+  },
+  {
+    key: 'pageviews',
+    label: '阅读数'
+  }
+]
+
 export default defineComponent({
   // name: 'Example',
+  components: {
+    ComTable,
+    Search
+  },
+  setup() {
+    const { defalutParams, tableData, loading, total, currentChange, sizeChange } = useExample()
+
+    async function getExampleList() {
+      try {
+        const res = await api.example.getExampleList({
+          params: defalutParams
+        })
+        if (res) {
+          total.value = res.data.total
+          tableData.value = res.data.list
+        }
+      } finally {
+        loading.value = false
+      }
+    }
+
+    function handleSizeChange(val: number) {
+      // 该方法重置了一些默认参数
+      sizeChange(val)
+      getExampleList()
+    }
+
+    function handleCurrentChange(val: number) {
+      // 该方法重置了一些默认参数
+      currentChange(val)
+      getExampleList()
+    }
+
+    getExampleList()
+
+    return {
+      columns,
+      defalutParams,
+      loading,
+      tableData,
+      total,
+      handleSizeChange,
+      handleCurrentChange
+    }
+  }
 })
 </script>
 

+ 2 - 2
src/pages/index/views/hooks-demo/useWatermark/index.vue

@@ -7,9 +7,9 @@
       type="info"
       style="margin-bottom: 20px;"
     />
-    <el-button type="primary" @click="setWatermark('vue-antdv-admin')">创建水印</el-button>
+    <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-antdv-admin-new')">重置水印</el-button>
+    <el-button type="warning" @click="setWatermark('vue-element-plus-admin-new')">重置水印</el-button>
   </div>
 </template>
 

+ 1 - 1
src/pages/index/views/table-demo/basic-table/index.vue

@@ -59,7 +59,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
     return {
       columns,
       tableData,

+ 1 - 1
src/pages/index/views/table-demo/border-table/index.vue

@@ -64,7 +64,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
     return {
       columns,
       tableData,

+ 1 - 1
src/pages/index/views/table-demo/custom-header/index.vue

@@ -85,7 +85,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     const search = ref<string>('')
 

+ 85 - 0
src/pages/index/views/table-demo/custom-index/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"
+    />
+  </div>
+</template>
+
+<script lang="ts">
+import { defineComponent, ref } from 'vue'
+import ComTable from '_c/Table/index.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 弄'
+  }
+]
+
+export default defineComponent({
+  // name: 'CustomIndex',
+  components: {
+    ComTable
+  },
+  setup() {
+    const loading = ref<boolean>(true)
+    setTimeout(() => {
+      loading.value = false
+    }, 1000)
+
+    const columns = ref<any[]>([
+      {
+        key: 'index',
+        type: 'index',
+        index: (index: number) => {
+          return index * 2
+        }
+      },
+      {
+        key: 'date',
+        label: '日期'
+      },
+      {
+        key: 'name',
+        label: '姓名'
+      },
+      {
+        key: 'address',
+        label: '地址'
+      }
+    ])
+
+    return {
+      columns,
+      tableData,
+      loading
+    }
+  }
+})
+</script>
+
+<style>
+</style>

+ 1 - 1
src/pages/index/views/table-demo/expand-row/index.vue

@@ -113,7 +113,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     return {
       columns,

+ 1 - 1
src/pages/index/views/table-demo/fixed-column-header/index.vue

@@ -146,7 +146,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     function handleClick(row: any) {
       console.log(row)

+ 1 - 1
src/pages/index/views/table-demo/fixed-column/index.vue

@@ -113,7 +113,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     function handleClick(row: any) {
       console.log(row)

+ 1 - 1
src/pages/index/views/table-demo/fixed-header/index.vue

@@ -83,7 +83,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     return {
       columns,

+ 1 - 1
src/pages/index/views/table-demo/fluid-height/index.vue

@@ -144,7 +144,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     function deleteRow(index: number) {
       tableData.value.splice(index, 1)

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

@@ -0,0 +1,164 @@
+<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 lang="ts">
+import { defineComponent, ref } from 'vue'
+import ComTable from '_c/Table/index.vue'
+
+const columns = [
+  {
+    key: 'id',
+    label: 'ID'
+  },
+  {
+    key: 'name',
+    label: '姓名'
+  },
+  {
+    key: 'amount1',
+    label: '数值1',
+    sortable: true
+  },
+  {
+    key: 'amount2',
+    label: '数值2',
+    sortable: true
+  },
+  {
+    key: 'amount3',
+    label: '数值4',
+    sortable: true
+  }
+]
+
+const columns1 = [
+  {
+    key: 'id',
+    label: 'ID'
+  },
+  {
+    key: 'name',
+    label: '姓名'
+  },
+  {
+    key: 'amount1',
+    label: '数值1(元)'
+  },
+  {
+    key: 'amount2',
+    label: '数值2(元)'
+  },
+  {
+    key: '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
+  }
+]
+
+export default defineComponent({
+  // name: 'MergeTable',
+  components: {
+    ComTable
+  },
+  setup() {
+    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
+          }
+        }
+      }
+    }
+
+    return {
+      columns, columns1,
+      tableData,
+      loading,
+      arraySpanMethod, objectSpanMethod
+    }
+  }
+})
+</script>
+
+<style>
+</style>

+ 1 - 1
src/pages/index/views/table-demo/multi-header/index.vue

@@ -142,7 +142,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     function deleteRow(index: number) {
       tableData.value.splice(index, 1)

+ 1 - 1
src/pages/index/views/table-demo/multiple-choice/index.vue

@@ -71,7 +71,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     const multipleTable = ref<HTMLElement | null>(null)
     function toggleSelection(rows: any[]) {

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

@@ -0,0 +1,93 @@
+<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 lang="ts">
+import { defineComponent, ref } from 'vue'
+import ComTable from '_c/Table/index.vue'
+
+const columns = [
+  {
+    key: 'date',
+    label: '日期'
+  },
+  {
+    key: 'name',
+    label: '姓名'
+  },
+  {
+    key: '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 弄'
+  }
+]
+
+export default defineComponent({
+  // name: 'PageTable',
+  components: {
+    ComTable
+  },
+  setup() {
+    const loading = ref<boolean>(true)
+    setTimeout(() => {
+      loading.value = false
+    }, 1000)
+
+    function handleSizeChange(val: number) {
+      console.log(val)
+    }
+
+    function handleCurrentChange(val: number) {
+      console.log(val)
+    }
+
+    return {
+      columns,
+      tableData,
+      loading,
+      handleSizeChange, handleCurrentChange
+    }
+  }
+})
+</script>
+
+<style>
+</style>

+ 1 - 1
src/pages/index/views/table-demo/screen-table/index.vue

@@ -66,7 +66,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     const columns = ref<any[]>([
       {

+ 1 - 1
src/pages/index/views/table-demo/single-choice/index.vue

@@ -71,7 +71,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     const singleTable = ref<HTMLElement | null>(null)
     function setCurrent(row: any) {

+ 1 - 1
src/pages/index/views/table-demo/sort-table/index.vue

@@ -67,7 +67,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     return {
       columns,

+ 1 - 1
src/pages/index/views/table-demo/state-table/index.vue

@@ -64,7 +64,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     function tableRowClassName({ rowIndex }: any) {
       if (rowIndex === 1) {

+ 1 - 1
src/pages/index/views/table-demo/stripe-table/index.vue

@@ -64,7 +64,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
     return {
       columns,
       tableData,

+ 1 - 1
src/pages/index/views/table-demo/total-table/index.vue

@@ -124,7 +124,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     function getSummaries(param: any) {
       const { columns, data } = param

+ 1 - 1
src/pages/index/views/table-demo/tree-and-load/index.vue

@@ -145,7 +145,7 @@ export default defineComponent({
     const loading = ref<boolean>(true)
     setTimeout(() => {
       loading.value = false
-    }, 2000)
+    }, 1000)
 
     function load(tree: any, treeNode: any, resolve: Function) {
       setTimeout(() => {

+ 35 - 122
src/styles/button.less

@@ -1,126 +1,39 @@
-@import "~ant-design-vue/lib/style/themes/default";
-@import "~ant-design-vue/es/button/style/mixin";
-
-// warning button style
-@btn-warning-color: #fff;
-@btn-warning-bg: @warning-color;
-@btn-warning-disabled-border-color: #f3d19e;
-@btn-warning-disabled-bg-color: #f3d19e;
-
-// success button style
-@btn-success-color: #fff;
-@btn-success-bg: @success-color;
-@btn-success-disabled-border-color: #b3e19d;
-@btn-success-disabled-bg-color: #b3e19d;
-
-// info button style
-@btn-info-color: #fff;
-@btn-info-bg: #909399;
-@btn-info-disabled-border-color: #c8c9cc;
-@btn-info-disabled-bg-color: #c8c9cc;
-
-// primary button style
-@btn-primary-disabled-border-color: #a0cfff;
-@btn-primary-disabled-bg-color: #a0cfff;
-
-// danger button style
-@btn-danger-disabled-border-color: #fab6b6;
-@btn-danger-disabled-bg-color: #fab6b6;
-
-// set button margin
-.ant-btn+.ant-btn {
-  margin-left: 10px;
+.el-button + .el-button {
+  position: relative;
 }
-
-.button-variant(@color; @background) {
-  .button-color(@color; @background; @background);
-
-  text-shadow: @btn-text-shadow;
-  box-shadow: @btn-primary-shadow;
-
-  &:hover,
+.el-button:not(.is-disabled) {
+  position: relative;
   &:focus {
-    .button-color(@color; ~`colorPalette('@{background}', 5) `; ~`colorPalette('@{background}', 5) `);
-  }
-
-  &:active,
-  &.active {
-    .button-color(@color; ~`colorPalette('@{background}', 7) `; ~`colorPalette('@{background}', 7) `);
-  }
-
-  .button-disabled();
-}
-
-.ant-btn-warning,
-.ant-btn-success,
-.ant-btn-info {
-
-  .ant-btn-group {
-    &:not(:first-child):not(:last-child) {
-      border-right-color: @gold-5;
-      border-left-color: @gold-5;
-
-      &:disabled {
-        border-color: @btn-default-border;
-      }
-    }
-
-    &:first-child {
-      &:not(:last-child) {
-        border-right-color: @gold-5;
-
-        &[disabled] {
-          border-right-color: @btn-default-border;
-        }
-      }
-    }
-  }
-
-  .ant-btn-group &:last-child:not(:first-child),
-  .ant-btn-group & + & {
-    border-left-color: @gold-5;
-
-    &[disabled] {
-      border-left-color: @btn-default-border;
-    }
-  }
-}
-
-.ant-btn-success {
-  .button-variant(@btn-success-color; @btn-success-bg);
-  &:not(.ant-btn-link)[disabled] {
-    color: #fff;
-    background-color: @btn-success-disabled-bg-color;
-    border-color: @btn-success-disabled-border-color;
-  }
-}
-.ant-btn-info {
-  .button-variant(@btn-info-color; @btn-info-bg);
-  &:not(.ant-btn-link)[disabled] {
-    color: #fff;
-    background-color: @btn-info-disabled-bg-color;
-    border-color: @btn-info-disabled-border-color;
-  }
-}
-.ant-btn-warning {
-  .button-variant(@btn-warning-color; @btn-warning-bg);
-  &:not(.ant-btn-link)[disabled] {
-    color: #fff;
-    background-color: @btn-warning-disabled-bg-color;
-    border-color: @btn-warning-disabled-border-color;
-  }
-}
-.ant-btn-primary {
-  &:not(.ant-btn-link)[disabled] {
-    color: #fff;
-    background-color: @btn-primary-disabled-bg-color;
-    border-color: @btn-primary-disabled-border-color;
-  }
-}
-.ant-btn-danger {
-  &:not(.ant-btn-link)[disabled] {
-    color: #fff;
-    background-color: @btn-danger-disabled-bg-color;
-    border-color: @btn-danger-disabled-border-color;
+    outline: none;
+  }
+  &:after {
+    content: "";
+    display: block;
+    position: absolute;
+    top: -6px;
+    left: -6px;
+    right: -6px;
+    bottom: -6px;
+    pointer-events: none;
+    background-color: inherit;
+    background-repeat: no-repeat;
+    background-position: 50%;
+    opacity: 0;
+    transition: all .3s;
+    border-radius: inherit;
+  }
+  &:active:after {
+    opacity: .7;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    transition: 0s;
+  }
+}
+
+.el-button--default {
+  &:after {
+    background-color: lightgrey !important;
   }
 }

+ 1 - 3
src/styles/index.less

@@ -1,7 +1,5 @@
 @import '~element-plus/lib/theme-chalk/index.css';
-// @import './sidebar.less';
 @import './transition.less';
 @import './sider.less';
 @import './glob.less';
-// @import  './button.less';
-// @import  './antdv.less';
+@import  './button.less';

+ 0 - 35
src/utils/event/resize-event.ts

@@ -1,35 +0,0 @@
-import ResizeObserver from 'resize-observer-polyfill'
-
-const isServer = typeof window === 'undefined'
-
-/* istanbul ignore next */
-const resizeHandler = function(entries: any[]) {
-  for (const entry of entries) {
-    const listeners = entry.target.__resizeListeners__ || []
-    if (listeners.length) {
-      listeners.forEach((fn: () => any) => {
-        fn()
-      })
-    }
-  }
-}
-
-/* istanbul ignore next */
-export const addResizeListener = function(element: any, fn: () => any) {
-  if (isServer) return
-  if (!element.__resizeListeners__) {
-    element.__resizeListeners__ = []
-    element.__ro__ = new ResizeObserver(resizeHandler)
-    element.__ro__.observe(element)
-  }
-  element.__resizeListeners__.push(fn)
-}
-
-/* istanbul ignore next */
-export const removeResizeListener = function(element: any, fn: () => any) {
-  if (!element || !element.__resizeListeners__) return
-  element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1)
-  if (!element.__resizeListeners__.length) {
-    element.__ro__.disconnect()
-  }
-}

+ 0 - 30
src/utils/scrollbar-width.ts

@@ -1,30 +0,0 @@
-import { isWindow } from '@/utils/is'
-
-let scrollBarWidth: number
-
-export default function(): number {
-  if (!isWindow) return 0
-  if (scrollBarWidth !== undefined) return scrollBarWidth
-
-  const outer = document.createElement('div')
-  outer.className = 'scrollbar__wrap'
-  outer.style.visibility = 'hidden'
-  outer.style.width = '100px'
-  outer.style.position = 'absolute'
-  outer.style.top = '-9999px'
-  document.body.appendChild(outer)
-
-  const widthNoScroll = outer.offsetWidth
-  outer.style.overflow = 'scroll'
-
-  const inner = document.createElement('div')
-  inner.style.width = '100%'
-  outer.appendChild(inner)
-
-  const widthWithScroll = inner.offsetWidth
-  const parentNode = outer.parentNode
-  parentNode && parentNode.removeChild(outer)
-  scrollBarWidth = widthNoScroll - widthWithScroll
-
-  return scrollBarWidth
-}

+ 4 - 4
yarn.lock

@@ -3990,10 +3990,10 @@ electron-to-chromium@^1.3.621:
   resolved "https://registry.npm.taobao.org/electron-to-chromium/download/electron-to-chromium-1.3.622.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felectron-to-chromium%2Fdownload%2Felectron-to-chromium-1.3.622.tgz#9726bd2e67a5462154750ce9701ca6af07d07877"
   integrity sha1-lya9LmelRiFUdQzpcBymrwfQeHc=
 
-element-plus@1.0.1-beta.10:
-  version "1.0.1-beta.10"
-  resolved "https://registry.yarnpkg.com/element-plus/-/element-plus-1.0.1-beta.10.tgz#3fdc914c4a888b899d701642e13d1904ccae01f0"
-  integrity sha512-yjiU9Nnt8VyKJR6L2JoSa59W9pMX4A/Q6csXbd3OJ2ZcakalCP1iDGhAWmp6WQOkgmJFhhhyLENDd5HH1d5Zdw==
+element-plus@1.0.1-beta.11:
+  version "1.0.1-beta.11"
+  resolved "https://registry.yarnpkg.com/element-plus/-/element-plus-1.0.1-beta.11.tgz#bcced187b44748b8d597c2681923c5097ee95fbe"
+  integrity sha512-qmael6DKlhJsOaEi/n7Zpw10qOCRO1irCXJ1LO8eqtl/7UCPZQNiOIIu6Gd1SgsJWbiW55NrFbHiRpfx37ZLIg==
   dependencies:
     "@popperjs/core" "^2.4.4"
     async-validator "^3.4.0"