├── .eslintignore ├── renovate.json ├── .npmignore ├── jsconfig.json ├── src ├── main.js ├── utils │ └── type.js └── components │ └── Table │ ├── ComponentsMapping.vue │ ├── ElementsMapping.vue │ └── index.vue ├── example ├── src │ ├── views │ │ └── ExampleTable.vue │ ├── components │ │ ├── data.js │ │ ├── Layout.vue │ │ ├── table-children-b.vue │ │ ├── table-children-a.vue │ │ ├── banner.vue │ │ └── table-list.vue │ ├── styles │ │ └── variables.scss │ └── directive │ │ └── clipboard.js ├── router.js ├── main.js ├── App.vue └── index.html ├── .gitignore ├── LICENSE ├── babel.config.js ├── .eslintrc.js ├── README.md └── package.json /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public/ 3 | build/ 4 | config/ 5 | dist/ 6 | lib/ 7 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "pin": false 6 | } 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git 2 | CVS 3 | .svn 4 | .hg 5 | .lock-wscript 6 | .wafpickle-N 7 | .*.swp 8 | .DS_Store 9 | ._* 10 | yarn.lock 11 | npm-debug.log 12 | .npmrc 13 | node_modules 14 | config.gypi 15 | *.orig 16 | package-lock.json 17 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "module": "commonjs", 5 | "paths": { 6 | "root/*": ["./*"], 7 | "@/*": ["./src/*"], 8 | "example/*": ["./example/src/*"] 9 | } 10 | }, 11 | "exclude": ["node_modules"] 12 | } 13 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import HocElTable from '@/components/Table' 2 | 3 | const install = function (app) { 4 | app.component('HocElTable', HocElTable) 5 | } 6 | 7 | if (typeof window !== 'undefined' && window.Vue) { 8 | install(window.Vue) 9 | } 10 | 11 | export default { 12 | version: '2.2.0', 13 | install, 14 | HocElTable 15 | } 16 | -------------------------------------------------------------------------------- /example/src/views/ExampleTable.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /example/router.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router' 2 | 3 | const importModule = (filePath) => { 4 | return () => import(`example/${filePath}`) 5 | } 6 | 7 | const routes = [{ 8 | path: '/', 9 | component: importModule('views/ExampleTable') 10 | }] 11 | 12 | export default createRouter({ 13 | routes, 14 | history: createWebHistory() 15 | }) 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /dist 13 | /dist-ssr 14 | /lib 15 | 16 | # misc 17 | .DS_Store 18 | *.local 19 | .vscode/ 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | .eslintcache 25 | -------------------------------------------------------------------------------- /example/src/components/data.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data: [ 3 | { id: 0, name: '王小虎1', isForbid: false }, 4 | { id: 1, name: '王小虎2', isForbid: false }, 5 | { id: 2, name: '王小虎3', isForbid: false } 6 | ], 7 | meta: { 8 | pagination: { 9 | total: 3, 10 | count: 10, 11 | perPage: 10, 12 | currentPage: 1, 13 | totalPages: 1 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import router from './router.js' 3 | import ElementPlus from 'element-plus' 4 | 5 | // import HocElTable from 'root/lib/hoc-el-table' // Switch to bundle lib 6 | import HocElTable from '@/main.js' 7 | import App from './App.vue' 8 | 9 | import 'example/styles/variables.scss' 10 | 11 | createApp(App) 12 | .use(router) 13 | .use(ElementPlus) 14 | .use(HocElTable) 15 | .mount('#app') 16 | -------------------------------------------------------------------------------- /example/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 14 | 26 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /example/src/components/Layout.vue: -------------------------------------------------------------------------------- 1 | 6 | 13 | 14 | 31 | -------------------------------------------------------------------------------- /example/src/components/table-children-b.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 31 | 32 | 37 | -------------------------------------------------------------------------------- /src/utils/type.js: -------------------------------------------------------------------------------- 1 | const toString = Object.prototype.toString 2 | 3 | export function isFunction (obj) { 4 | return typeof (obj) === 'function' 5 | } 6 | 7 | export function isObject (obj) { 8 | return obj === Object(obj) 9 | } 10 | 11 | export function isArray (obj) { 12 | return toString.call(obj) === '[object Array]' 13 | } 14 | 15 | export function isDate (obj) { 16 | return toString.call(obj) === '[object Date]' 17 | } 18 | 19 | export function isRegExp (obj) { 20 | return toString.call(obj) === '[object RegExp]' 21 | } 22 | export function isBoolean (obj) { 23 | return toString.call(obj) === '[object Boolean]' 24 | } 25 | 26 | export function isNumberical (obj) { 27 | return (typeof (obj) === 'number' || typeof (obj) === 'string') && !isNaN(obj - parseFloat(obj)) 28 | } 29 | -------------------------------------------------------------------------------- /example/src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | 2 | /* theme color */ 3 | $--color-primary: #b38358; 4 | $--color-success: #52C41A; 5 | $--color-warning: #FAAD14; 6 | $--color-danger: #FA5555; 7 | $--color-info: #909399; 8 | 9 | 10 | // 按需导入,重写覆盖自己所需样式 11 | @forward "element-plus/theme-chalk/src/common/var.scss" with ( 12 | $colors: ( 13 | "primary": ( 14 | "base": $--color-primary, 15 | ), 16 | "success": ( 17 | "base": $--color-success, 18 | ), 19 | "warning": ( 20 | "base": $--color-warning, 21 | ), 22 | "danger": ( 23 | "base": $--color-danger, 24 | ), 25 | "info": ( 26 | "base": $--color-info, 27 | ), 28 | 29 | ), 30 | $border-radius: ( 31 | 'base': 4px, 32 | 'small': 2px, 33 | 'round': 20px, 34 | 'circle': 100%, 35 | ), 36 | ); 37 | 38 | @import 'element-plus/theme-chalk/src/index.scss'; 39 | -------------------------------------------------------------------------------- /src/components/Table/ComponentsMapping.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020-present, Wisdom 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/src/components/table-children-a.vue: -------------------------------------------------------------------------------- 1 | 18 | 32 | 52 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const babelConfig = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | chrome: '58', 8 | ie: '11' 9 | } 10 | } 11 | ] 12 | ], 13 | plugins: [ 14 | [ 15 | '@babel/plugin-transform-runtime', 16 | { 17 | corejs: false, 18 | helpers: true, 19 | regenerator: true, 20 | useESModules: false 21 | } 22 | ], 23 | '@babel/plugin-syntax-dynamic-import', 24 | '@babel/plugin-syntax-import-meta', 25 | ['@babel/plugin-proposal-class-properties', { loose: false }], 26 | ['@babel/plugin-proposal-private-methods', { loose: false }], 27 | '@babel/plugin-proposal-json-strings', 28 | [ 29 | '@babel/plugin-proposal-decorators', 30 | { 31 | legacy: true 32 | } 33 | ], 34 | '@babel/plugin-proposal-function-sent', 35 | '@babel/plugin-proposal-export-default-from', 36 | '@babel/plugin-proposal-export-namespace-from', 37 | '@babel/plugin-proposal-numeric-separator', 38 | '@babel/plugin-proposal-throw-expressions', 39 | '@babel/plugin-transform-modules-commonjs' 40 | ] 41 | } 42 | 43 | module.exports = babelConfig 44 | -------------------------------------------------------------------------------- /example/src/directive/clipboard.js: -------------------------------------------------------------------------------- 1 | import Clipboard from 'clipboard' 2 | 3 | const clipboard = { 4 | beforeMount: function (el, binding, vnode) { 5 | if (binding.arg === 'success') { 6 | el._v_clipboard_success = binding.value 7 | } else if (binding.arg === 'error') { 8 | el._v_clipboard_error = binding.value 9 | } else { 10 | const clipboard = new Clipboard(el, { 11 | text: function () { return binding.value }, 12 | action: function () { return binding.arg === 'cut' ? 'cut' : 'copy' } 13 | }) 14 | clipboard.on('success', function (e) { 15 | const callback = el._v_clipboard_success 16 | callback && callback(e) 17 | }) 18 | clipboard.on('error', function (e) { 19 | const callback = el._v_clipboard_error 20 | callback && callback(e) 21 | }) 22 | el._v_clipboard = clipboard 23 | } 24 | }, 25 | updated: function (el, binding) { 26 | if (binding.arg === 'success') { 27 | el._v_clipboard_success = binding.value 28 | } else if (binding.arg === 'error') { 29 | el._v_clipboard_error = binding.value 30 | } else { 31 | el._v_clipboard.text = function () { return binding.value } 32 | el._v_clipboard.action = function () { return binding.arg === 'cut' ? 'cut' : 'copy' } 33 | } 34 | }, 35 | unmounted: function (el, binding) { 36 | if (binding.arg === 'success') { 37 | delete el._v_clipboard_success 38 | } else if (binding.arg === 'error') { 39 | delete el._v_clipboard_error 40 | } else { 41 | el._v_clipboard.destroy() 42 | delete el._v_clipboard 43 | } 44 | } 45 | } 46 | 47 | export { 48 | clipboard 49 | } 50 | -------------------------------------------------------------------------------- /example/src/components/banner.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 32 | 33 | 59 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | es6: true, 6 | browser: true, 7 | jest: true 8 | }, 9 | globals: { 10 | defineProps: 'readonly', 11 | defineEmits: 'readonly', 12 | defineExpose: 'readonly', 13 | withDefaults: 'readonly' 14 | }, 15 | extends: [ 16 | 'eslint:recommended', 17 | 'plugin:vue/base', 18 | 'plugin:vue/vue3-essential', 19 | 'plugin:vue/vue3-strongly-recommended', 20 | 'plugin:vue/vue3-recommended' 21 | ], 22 | parserOptions: { 23 | ecmaFeatures: { 24 | legacyDecorators: true 25 | }, 26 | parser: '@babel/eslint-parser' 27 | }, 28 | rules: { 29 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 30 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 31 | 32 | // vue 33 | 'vue/multi-word-component-names': 0, 34 | 'vue/no-unused-components': 1, 35 | 'vue/no-mutating-props': 0, 36 | 'vue/script-setup-uses-vars': 'error', 37 | 'vue/html-self-closing': ['error', { 38 | html: { 39 | void: 'never', 40 | normal: 'never', 41 | component: 'always' 42 | }, 43 | svg: 'always', 44 | math: 'always' 45 | }], 46 | 47 | // js 48 | 'no-unused-vars': 1, 49 | 'no-undef': 1, 50 | // https://eslint.org/docs/rules/no-var 51 | 'no-var': 'error', 52 | // https://cn.eslint.org/docs/rules/no-trailing-spaces 53 | 'no-trailing-spaces': 2, // 禁用行尾空白 54 | 'comma-style': ['error', 'last'], 55 | 'comma-dangle': ['error', 'never'], 56 | 'no-irregular-whitespace': 2, 57 | 'no-multi-spaces': 1, 58 | 'no-multiple-empty-lines': [ 59 | 2, 60 | { 61 | max: 1 62 | } 63 | ], 64 | // https://cn.eslint.org/docs/rules/eol-last 65 | 'eol-last': 2, 66 | quotes: [ 67 | 'error', 68 | 'single', 69 | { 70 | avoidEscape: true, 71 | allowTemplateLiterals: true 72 | } 73 | ], 74 | // https://eslint.org/docs/rules/prefer-const 75 | 'prefer-const': 2, 76 | camelcase: ['error', { properties: 'never' }], 77 | indent: ['error', 2, { SwitchCase: 1 }], 78 | semi: ['error', 'never'], 79 | 'space-before-function-paren': 'error' 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @hoc-element/table 2 | 3 | [![npm](https://img.shields.io/npm/v/@hoc-element/table)](https://www.npmjs.com/package/@hoc-element/table) 4 | [![license](https://img.shields.io/npm/l/@hoc-element/table)](https://github.com/pdsuwwz/hoc-element-table/blob/main/LICENSE) 5 | 6 | 📦 基于 Webpack 5 构建,重构成 Vue 3.x,二次封装了 element-plus 库中的 table,通过配置文件的方式即可生成 `table` 表格, 无需再写大量的诸如 `` 的模板,实现更高的自由度,旨在减少重复的操作,让开发变得更高效。 7 | 8 | > 该组件库可供学习、参考和用于二次开发 9 | 10 | **[Live demo](https://pdsuwwz.github.io/hoc-element-table)** 11 | 12 | ## Version 13 | 14 | * Vue 3.x 版本 | [Vue 2.x 版本](https://github.com/pdsuwwz/hoc-element-table/tree/vue2.0) 15 | 16 | ## Screenshot 17 | 18 | image 19 | 20 | ## Environment Support 21 | 22 | * Vue 3.2.x 23 | 24 | ## Install 25 | 26 | ```shell 27 | npm install @hoc-element/table 28 | 29 | # or 30 | 31 | pnpm add @hoc-element/table 32 | ``` 33 | 34 | ## Quick Start 35 | 36 | * 使用前请安装 [element-plus](https://www.npmjs.com/package/element-plus) 37 | 38 | ```js 39 | import { createApp } from 'vue' 40 | import ElementPlus from 'element-plus' 41 | import HocElTable from '@hoc-element/table' 42 | import App from './App.vue' 43 | 44 | createApp(App) 45 | .use(ElementPlus) 46 | .use(HocElTable) 47 | .mount('#app') 48 | 49 | ``` 50 | 51 | ## Changelog 52 | 53 | Detailed changes for each release are documented in the [release notes](https://github.com/pdsuwwz/hoc-element-table/releases) 54 | 55 | ## Feature 56 | 57 | - [x] 基于 Webpack 5 构建 58 | - [x] 全面支持 Vue 3 59 | - [x] 支持 `JSON` 序列化表格快速配置 60 | - [x] 已内置 `Pagination` 分页 61 | - [x] 支持自定义 `prop` 列名 62 | - [x] 支持单元格内容自定义渲染 【见 [单元格渲染配置说明](#单元格渲染配置说明)】 63 | - [x] 支持自定义单元格 `style` 样式 64 | - [x] 支持绑定自定义指令 `directives` 65 | - [x] 支持绑定 `element-plus` 原生 `Table` 的 `Events` 和 `Methods` 66 | - [x] 支持 `Header` 和 `Pagination` 的显隐控制 67 | 68 | ## 单元格渲染配置说明 69 | 70 | | 方法 | 说明 | 场景 | 71 | | -------- | -------- | -------- | 72 | | --- | 渲染单元格的 attrs.prop 对应的键值 | 适用于直接显示 prop 的值场景 | 73 | | render | 渲染字符串 | 适用于对默认 prop 的值做一些微处理的场景 | 74 | | renderHTML | 渲染指定的 DOM 元素 | 适用于展示 Action,一般用在最后一列(目前只支持 el-button 的渲染,详见 Example) | 75 | | renderComponent | 渲染组件 | 适用于单元格内需要展示复杂内容的场景,详见 Example | 76 | 77 | ## Example 78 | 79 | 这是比较全的例子,几乎囊括了 API 的所有用法,源码戳这: [Code](https://github.com/pdsuwwz/hoc-element-table/tree/main/example/src/components/table-list.vue) 80 | 81 | ## Deploy 82 | 83 | 见仓库 [📥 hoc-element-table-build](https://github.com/pdsuwwz/hoc-element-table-build) 84 | -------------------------------------------------------------------------------- /src/components/Table/ElementsMapping.vue: -------------------------------------------------------------------------------- 1 | 2 | 116 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hoc-element/table", 3 | "version": "2.2.0", 4 | "main": "lib/hoc-el-table.js", 5 | "description": "📦 A Vue3.x Table Component built on Webpack 5 that follows the Configuration", 6 | "author": "Wisdom ", 7 | "license": "MIT", 8 | "scripts": { 9 | "build": "cross-env NODE_ENV=production webpack --progress --config build/webpack.config.js", 10 | "dev:example": "cross-env NODE_ENV=development webpack serve --progress --config build/webpack.example.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/pdsuwwz/hoc-element-table.git" 15 | }, 16 | "publishConfig": { 17 | "access": "public" 18 | }, 19 | "peerDependencies": { 20 | "element-plus": ">=2.0.0", 21 | "vue": ">=3.2.0" 22 | }, 23 | "engines": { 24 | "node": ">=14.19.0" 25 | }, 26 | "devDependencies": { 27 | "@babel/core": "^7.17.12", 28 | "@babel/eslint-parser": "^7.17.0", 29 | "@babel/eslint-plugin": "^7.17.7", 30 | "@babel/plugin-proposal-class-properties": "^7.17.12", 31 | "@babel/plugin-proposal-decorators": "^7.17.12", 32 | "@babel/plugin-proposal-export-default-from": "^7.17.12", 33 | "@babel/plugin-proposal-export-namespace-from": "^7.17.12", 34 | "@babel/plugin-proposal-function-sent": "^7.17.12", 35 | "@babel/plugin-proposal-json-strings": "^7.17.12", 36 | "@babel/plugin-proposal-numeric-separator": "^7.16.7", 37 | "@babel/plugin-proposal-private-methods": "^7.17.12", 38 | "@babel/plugin-proposal-throw-expressions": "^7.16.7", 39 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 40 | "@babel/plugin-syntax-import-meta": "^7.10.4", 41 | "@babel/plugin-transform-modules-commonjs": "^7.17.12", 42 | "@babel/plugin-transform-runtime": "^7.17.12", 43 | "@babel/preset-env": "^7.17.12", 44 | "@babel/runtime": "^7.17.9", 45 | "@babel/runtime-corejs2": "^7.17.11", 46 | "@types/lodash-es": "^4.17.6", 47 | "@vue/compiler-sfc": "^3.2.33", 48 | "babel-loader": "^9.0.0", 49 | "clipboard": "^2.0.11", 50 | "core-js": "^3.22.5", 51 | "cross-env": "^7.0.3", 52 | "css-loader": "^6.7.1", 53 | "element-plus": "^2.2.0", 54 | "eslint": "^8.0.0", 55 | "eslint-friendly-formatter": "^4.0.1", 56 | "eslint-plugin-import": "^2.26.0", 57 | "eslint-plugin-jsx-a11y": "^6.5.1", 58 | "eslint-plugin-node": "^11.1.0", 59 | "eslint-plugin-promise": "^6.0.0", 60 | "eslint-plugin-vue": "9.9.0", 61 | "eslint-webpack-plugin": "^4.0.0", 62 | "file-loader": "^6.2.0", 63 | "html-webpack-plugin": "^5.5.0", 64 | "sass": "^1.51.0", 65 | "sass-loader": "13.2.0", 66 | "style-loader": "^3.3.1", 67 | "url-loader": "^4.1.1", 68 | "vue": "^3.2.33", 69 | "vue-loader": "^17.0.0", 70 | "vue-router": "^4.0.15", 71 | "vue-style-loader": "^4.1.3", 72 | "webpack": "^5.72.1", 73 | "webpack-cli": "^5.0.0", 74 | "webpack-dev-server": "^4.9.0" 75 | }, 76 | "browserslist": [ 77 | "> 1%", 78 | "last 2 versions", 79 | "not ie <= 8" 80 | ], 81 | "keywords": [ 82 | "vue", 83 | "element-plus", 84 | "table", 85 | "hoc", 86 | "高阶组件", 87 | "组件化", 88 | "表格" 89 | ] 90 | } 91 | -------------------------------------------------------------------------------- /src/components/Table/index.vue: -------------------------------------------------------------------------------- 1 | 82 | 83 | 298 | 299 | 331 | -------------------------------------------------------------------------------- /example/src/components/table-list.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 364 | 365 | 374 | --------------------------------------------------------------------------------