├── index.js ├── doc └── image │ └── banner.png ├── .babelrc ├── .gitignore ├── .editorconfig ├── src ├── index.js ├── components │ ├── renderComponent.vue │ ├── renderCustomComponent.vue │ └── d2-column.vue ├── mixin │ ├── add.js │ ├── handleRow.js │ ├── pagination.js │ ├── utils.js │ ├── remove.js │ ├── edit.js │ ├── exposeMethods.js │ ├── data.js │ ├── base.js │ └── dialog.js └── d2-crud.vue ├── cdn.sh ├── LICENSE ├── package.json ├── README.md ├── webpack.config.js └── dist └── d2-crud.js /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src') 2 | -------------------------------------------------------------------------------- /doc/image/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/d2-projects/d2-crud/HEAD/doc/image/banner.png -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { "modules": false }], 4 | "stage-3" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log 4 | yarn-error.log 5 | 6 | # Editor directories and files 7 | .idea 8 | *.suo 9 | *.ntvs* 10 | *.njsproj 11 | *.sln 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import d2Crud from './d2-crud.vue' 2 | 3 | const install = (Vue, options) => { 4 | if (options) { 5 | Vue.prototype.$d2CrudSize = options.size ? options.size : null 6 | } else { 7 | Vue.prototype.$d2CrudSize = null 8 | } 9 | Vue.component('d2Crud', d2Crud) 10 | } 11 | if (typeof window !== 'undefined' && window.Vue) { 12 | install(window.Vue) 13 | } 14 | 15 | export default Object.assign(d2Crud, { install }) 16 | -------------------------------------------------------------------------------- /cdn.sh: -------------------------------------------------------------------------------- 1 | # chmod +x ./cdn.sh 2 | # ./cdn.sh 3 | 4 | VERSION=2.0.5 5 | FOLDER=d2-crud 6 | NAME=@d2-projects/d2-crud 7 | 8 | qshell account 9 | qshell qupload2 \ 10 | --src-dir=/Users/liyang/Documents/code/$FOLDER/dist \ 11 | --bucket=d2-cdn \ 12 | --key-prefix=packages/$NAME@$VERSION/ \ 13 | --overwrite=true \ 14 | --check-exists=true \ 15 | --check-hash=true \ 16 | --check-size=true \ 17 | --rescan-local=true \ 18 | --thread-count=32 19 | 20 | echo done 上传完成 -------------------------------------------------------------------------------- /src/components/renderComponent.vue: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /src/mixin/add.js: -------------------------------------------------------------------------------- 1 | import _forEach from 'lodash.foreach' 2 | import _clonedeep from 'lodash.clonedeep' 3 | 4 | export default { 5 | methods: { 6 | /** 7 | * @description 新增行数据 8 | */ 9 | handleAdd (templage = null) { 10 | this.formMode = 'add' 11 | this.$emit('dialog-open', { 12 | mode: 'add' 13 | }) 14 | this.isDialogShow = true 15 | if (templage) { 16 | this.formData = _clonedeep(templage) 17 | this.addTemplateStorage = _clonedeep(templage) 18 | } else { 19 | this.formData = this.addTemplate ? _clonedeep(this.addTemplate) : {} 20 | this.addTemplateStorage = this.addTemplate ? _clonedeep(this.addTemplate) : {} 21 | } 22 | _forEach(this.formData, (value, key) => { 23 | this.formData[key] = this.addTemplateStorage[key].value 24 | }) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/mixin/handleRow.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | /** 4 | * @description 默认排序 5 | */ 6 | rowHandle: { 7 | type: Object, 8 | default: null 9 | } 10 | }, 11 | methods: { 12 | /** 13 | * @description 控制操作列 show 的方法 14 | */ 15 | handleRowHandleButtonShow (show = true, index, row) { 16 | if (typeof show === 'boolean') { 17 | return show 18 | } else if (typeof show === 'function') { 19 | return show(index, row) 20 | } 21 | return Boolean(show) 22 | }, 23 | /** 24 | * @description 控制操作列 disabled 的方法 25 | */ 26 | handleRowHandleButtonDisabled (disabled = false, index, row) { 27 | if (typeof disabled === 'boolean') { 28 | return disabled 29 | } else if (typeof disabled === 'function') { 30 | return disabled(index, row) 31 | } 32 | return Boolean(disabled) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/mixin/pagination.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | /** 4 | * @description 表格数据 5 | */ 6 | pagination: { 7 | type: Object, 8 | default: null 9 | } 10 | }, 11 | methods: { 12 | /** 13 | * @description 每页条数改变 14 | */ 15 | handlePaginationSizeChange (pageSize) { 16 | this.$emit('pagination-size-change', pageSize) 17 | }, 18 | /** 19 | * @description 当前页码改变 20 | */ 21 | handlePaginationCurrentChange (currentPage) { 22 | this.$emit('pagination-current-change', currentPage) 23 | }, 24 | /** 25 | * @description 上一页 26 | */ 27 | handlePaginationPrevClick (currentPage) { 28 | this.$emit('pagination-prev-click', currentPage) 29 | }, 30 | /** 31 | * @description 下一页 32 | */ 33 | handlePaginationNextClick (currentPage) { 34 | this.$emit('pagination-next-click', currentPage) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/mixin/utils.js: -------------------------------------------------------------------------------- 1 | export default { 2 | methods: { 3 | /** 4 | * @description 组件属性默认值 5 | */ 6 | handleAttribute (attribute, defaultValue) { 7 | if (attribute === false || attribute === 0 || attribute === '') { 8 | return attribute 9 | } 10 | return attribute || defaultValue 11 | }, 12 | /** 13 | * @description 根据dialog模式渲染不同表单 14 | */ 15 | handleFormTemplateMode (key) { 16 | if (this.formMode === 'edit') { 17 | return this.editTemplateStorage[key] 18 | } else if (this.formMode === 'add') { 19 | return this.addTemplateStorage[key] 20 | } 21 | }, 22 | /** 23 | * @description 根据dialog模式渲染不同表单校验规则 24 | */ 25 | handleFormRulesMode () { 26 | if (this.formMode === 'edit') { 27 | return this.editRules 28 | } else if (this.formMode === 'add') { 29 | return this.addRules 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/components/renderCustomComponent.vue: -------------------------------------------------------------------------------- 1 | 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 sunhaoxiang 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 | -------------------------------------------------------------------------------- /src/mixin/remove.js: -------------------------------------------------------------------------------- 1 | export default { 2 | methods: { 3 | /** 4 | * @description 删除行 5 | */ 6 | handleRemove (index, indexrow) { 7 | if ((!('confirm' in this.rowHandle.remove)) || this.rowHandle.remove.confirm === true) { 8 | this.$confirm(this.handleAttribute(this.rowHandle.remove.confirmText, '确定删除吗?'), this.handleAttribute(this.rowHandle.remove.confirmTitle, '删除'), { 9 | confirmButtonText: this.handleAttribute(this.rowHandle.remove.confirmButtonText, '确定'), 10 | cancelButtonText: this.handleAttribute(this.rowHandle.remove.cancelButtonText, '取消'), 11 | type: this.handleAttribute(this.rowHandle.remove.type, 'error') 12 | }).then(() => { 13 | this.$emit('row-remove', {index, row: indexrow}, () => { 14 | this.handleRemoveDone(index) 15 | }) 16 | }).catch(() => {}) 17 | } else { 18 | this.$emit('row-remove', {index, row: indexrow}, () => { 19 | this.handleRemoveDone(index) 20 | }) 21 | } 22 | }, 23 | /** 24 | * @description 删除完成 25 | */ 26 | handleRemoveDone (index) { 27 | this.handleRemoveRow(index) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/mixin/edit.js: -------------------------------------------------------------------------------- 1 | import _forEach from 'lodash.foreach' 2 | import _clonedeep from 'lodash.clonedeep' 3 | 4 | export default { 5 | data () { 6 | return { 7 | /** 8 | * @description 被编辑行的索引 9 | */ 10 | editIndex: 0 11 | } 12 | }, 13 | methods: { 14 | /** 15 | * @description 编辑行数据 16 | * @param {Number} index 行所在索引 17 | * @param {Object} row 行数据 18 | */ 19 | handleEdit (index, row, templage = null) { 20 | this.formMode = 'edit' 21 | this.editDataStorage = _clonedeep(row) 22 | this.isDialogShow = true 23 | this.$emit('dialog-open', { 24 | mode: 'edit', 25 | row 26 | }) 27 | this.editIndex = index 28 | if (templage) { 29 | this.formData = _clonedeep(templage) 30 | this.editTemplateStorage = _clonedeep(templage) 31 | } else { 32 | this.formData = this.editTemplate ? _clonedeep(this.editTemplate) : {} 33 | this.editTemplateStorage = this.editTemplate ? _clonedeep(this.editTemplate) : {} 34 | } 35 | _forEach(this.formData, (value, key) => { 36 | this.formData[key] = row.hasOwnProperty(key) ? row[key] : (this.formData[key] || '') 37 | }) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/mixin/exposeMethods.js: -------------------------------------------------------------------------------- 1 | export default { 2 | methods: { 3 | /** 4 | * @description 外部暴露的更新单元格数据方法 5 | */ 6 | updateCell (rowIndex, key, value) { 7 | this.$set(this.d2CrudData, rowIndex, { 8 | ...this.d2CrudData[rowIndex], 9 | [key]: value 10 | }) 11 | }, 12 | /** 13 | * @description 外部暴露的新增行方法 14 | */ 15 | addRow (row) { 16 | this.handleAddRow(row) 17 | }, 18 | /** 19 | * @description 外部暴露的编辑行方法 20 | */ 21 | updateRow (index, row) { 22 | this.handleUpdateRow(index, row) 23 | }, 24 | /** 25 | * @description 外部暴露的删除行方法 26 | */ 27 | removeRow (index) { 28 | this.handleRemoveRow(index) 29 | }, 30 | /** 31 | * @description 外部暴露的打开模态框方法 32 | */ 33 | showDialog ({ 34 | mode, 35 | rowIndex = 0, 36 | template = null 37 | }) { 38 | if (mode === 'edit') { 39 | this.handleEdit(rowIndex, this.d2CrudData[rowIndex], template) 40 | } else if (mode === 'add') { 41 | this.handleAdd(template) 42 | } 43 | }, 44 | /** 45 | * @description 外部暴露的关闭模态框方法 46 | */ 47 | closeDialog () { 48 | this.handleCloseDialog() 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@d2-projects/d2-crud", 3 | "description": "A d2-projects project", 4 | "version": "2.0.5", 5 | "author": "孙昊翔 <673686754@qq.com>", 6 | "license": "MIT", 7 | "private": false, 8 | "main": "dist/d2-crud.js", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/d2-projects/d2-crud" 12 | }, 13 | "keywords": [ 14 | "d2-crud", 15 | "table", 16 | "d2-projects", 17 | "vue", 18 | "element" 19 | ], 20 | "scripts": { 21 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" 22 | }, 23 | "dependencies": { 24 | "lodash.clonedeep": "^4.5.0", 25 | "lodash.foreach": "^4.5.0", 26 | "lodash.get": "^4.4.2", 27 | "lodash.set": "^4.3.2", 28 | "vue": "^2.5.19" 29 | }, 30 | "browserslist": [ 31 | "> 1%", 32 | "last 2 versions", 33 | "not ie <= 8" 34 | ], 35 | "devDependencies": { 36 | "babel-core": "^6.26.0", 37 | "babel-loader": "^7.1.2", 38 | "babel-preset-env": "^1.6.0", 39 | "babel-preset-stage-3": "^6.24.1", 40 | "cross-env": "^5.0.5", 41 | "css-loader": "^0.28.7", 42 | "file-loader": "^1.1.4", 43 | "node-sass": "^4.5.3", 44 | "sass-loader": "^6.0.6", 45 | "vue-loader": "^13.0.5", 46 | "vue-template-compiler": "^2.5.19", 47 | "webpack": "^3.6.0", 48 | "webpack-dev-server": "^2.9.1" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://raw.githubusercontent.com/d2-projects/d2-crud/master/doc/image/banner.png) 2 | 3 | ![npm](https://img.shields.io/npm/v/@d2-projects/d2-crud.svg) 4 | ![npm](https://img.shields.io/npm/dt/@d2-projects/d2-crud.svg) 5 | ![GitHub license](https://img.shields.io/github/license/d2-projects/d2-crud.svg) 6 | 7 | 8 | ## 介绍 9 | 10 | [D2-Crud](https://github.com/d2-projects/d2-crud)是一套基于[Vue.js 2.2.0+](https://cn.vuejs.org/)和[Element UI 2.0.0+](http://element-cn.eleme.io/#/zh-CN)的表格组件。`D2-Crud` 将 `Element` 的功能进行了封装,并增加了表格的增删改查、数据校验、表格内编辑等常用的功能。大部分功能可由配置 `json` 实现,在实现并扩展了 `Element` 表格组件功能的同时,降低了开发难度,减少了代码量,大大简化了开发流程。 11 | 12 | 13 | ## 文档和示例 14 | 15 | 文档: 16 | 示例: 17 | 18 | ## 功能 19 | 20 | - 继承了 Element 中表格所有功能 21 | - 新增表格数据 22 | - 修改表格数据 23 | - 删除表格数据 24 | - 使用 Element 中的组件渲染表格内容和表单内容 25 | - 表单校验 26 | - 表格内编辑 27 | - 渲染自定义组件 28 | 29 | ## 安装 30 | 31 | 使用npm 32 | ``` bash 33 | npm i element-ui @d2-projects/d2-crud -S 34 | ``` 35 | 36 | 使用yarn 37 | ``` bash 38 | yarn add element-ui @d2-projects/d2-crud 39 | ``` 40 | 41 | ## 在项目中使用 42 | 43 | 在`main.js`中写入以下内容: 44 | 45 | ``` js 46 | import Vue from 'vue' 47 | import ElementUI from 'element-ui' 48 | import 'element-ui/lib/theme-chalk/index.css' 49 | import D2Crud from '@d2-projects/d2-crud' 50 | 51 | Vue.use(ElementUI) 52 | Vue.use(D2Crud) 53 | 54 | new Vue({ 55 | el: '#app', 56 | render: h => h(App) 57 | }) 58 | ``` 59 | 60 | 之后就可以在项目中使用 `D2-Crud` 了。 61 | 62 | ## CDN 63 | 64 | externals: D2Crud 65 | 66 | https://cdn.d2.pub/packages/@d2-projects/d2-crud@2.0.5/d2-crud.js -------------------------------------------------------------------------------- /src/mixin/data.js: -------------------------------------------------------------------------------- 1 | import _clonedeep from 'lodash.clonedeep' 2 | import _get from 'lodash.get' 3 | import _set from 'lodash.set' 4 | 5 | export default { 6 | props: { 7 | /** 8 | * @description 表格数据 9 | */ 10 | data: { 11 | type: Array, 12 | required: true 13 | } 14 | }, 15 | data () { 16 | return { 17 | /** 18 | * @description 表格内部数据 19 | */ 20 | d2CrudData: [] 21 | } 22 | }, 23 | computed: { 24 | d2CrudDataLength () { 25 | return this.d2CrudData.length 26 | } 27 | }, 28 | watch: { 29 | data () { 30 | this.handleDataChange() 31 | } 32 | }, 33 | mounted () { 34 | this.handleDataChange() 35 | }, 36 | methods: { 37 | /** 38 | * @description lodash.get 39 | */ 40 | _get, 41 | /** 42 | * @description lodash.set 43 | */ 44 | _set, 45 | /** 46 | * @description 同步外部表格数据到d2CrudData内部 47 | */ 48 | handleDataChange () { 49 | if (this.d2CrudData !== this.data) { 50 | this.d2CrudData = _clonedeep(this.data) 51 | } 52 | }, 53 | /** 54 | * @description 排序时数据变化 55 | */ 56 | handleSortDataChange () { 57 | this.$nextTick(() => { 58 | this.d2CrudData = this.$refs.elTable.store.states.data 59 | }) 60 | }, 61 | /** 62 | * @description 排序状态 63 | */ 64 | handleSortChange ({ column, prop, order }) { 65 | this.handleSortDataChange() 66 | this.$emit('sort-change', { column, prop, order }) 67 | }, 68 | /** 69 | * @description 更新行数据 70 | * @param {Number} index 表格数据索引 71 | * @param {Object} row 更新的表格行数据 72 | */ 73 | handleUpdateRow (index, row) { 74 | this.$set(this.d2CrudData, index, row) 75 | if (this.defaultSort) { 76 | this.handleSortDataChange() 77 | } 78 | }, 79 | /** 80 | * @description 新增行数据 81 | * @param {Object} row 新增的表格行数据 82 | */ 83 | handleAddRow (row) { 84 | this.$set(this.d2CrudData, this.d2CrudData.length, row) 85 | if (this.defaultSort) { 86 | this.handleSortDataChange() 87 | } 88 | }, 89 | /** 90 | * @description 删除行 91 | * @param {Object} index 被删除行索引 92 | */ 93 | handleRemoveRow (index) { 94 | this.$delete(this.d2CrudData, index) 95 | if (this.defaultSort) { 96 | this.handleSortDataChange() 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var webpack = require('webpack') 3 | 4 | module.exports = { 5 | entry: './src/index.js', 6 | output: { 7 | path: path.resolve(__dirname, './dist'), 8 | publicPath: '/dist/', 9 | filename: 'd2-crud.js', 10 | library: 'D2Crud', 11 | libraryTarget: 'umd', 12 | umdNamedDefine: true 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.css$/, 18 | use: [ 19 | 'vue-style-loader', 20 | 'css-loader' 21 | ], 22 | }, 23 | { 24 | test: /\.scss$/, 25 | use: [ 26 | 'vue-style-loader', 27 | 'css-loader', 28 | 'sass-loader' 29 | ], 30 | }, 31 | { 32 | test: /\.sass$/, 33 | use: [ 34 | 'vue-style-loader', 35 | 'css-loader', 36 | 'sass-loader?indentedSyntax' 37 | ], 38 | }, 39 | { 40 | test: /\.vue$/, 41 | loader: 'vue-loader', 42 | options: { 43 | loaders: { 44 | // Since sass-loader (weirdly) has SCSS as its default parse mode, we map 45 | // the "scss" and "sass" values for the lang attribute to the right configs here. 46 | // other preprocessors should work out of the box, no loader config like this necessary. 47 | 'scss': [ 48 | 'vue-style-loader', 49 | 'css-loader', 50 | 'sass-loader' 51 | ], 52 | 'sass': [ 53 | 'vue-style-loader', 54 | 'css-loader', 55 | 'sass-loader?indentedSyntax' 56 | ] 57 | } 58 | // other vue-loader options go here 59 | } 60 | }, 61 | { 62 | test: /\.js$/, 63 | loader: 'babel-loader', 64 | exclude: /node_modules/ 65 | }, 66 | { 67 | test: /\.(png|jpg|gif|svg)$/, 68 | loader: 'file-loader', 69 | options: { 70 | name: '[name].[ext]?[hash]' 71 | } 72 | } 73 | ] 74 | }, 75 | resolve: { 76 | alias: { 77 | 'vue$': 'vue/dist/vue.esm.js' 78 | }, 79 | extensions: ['*', '.js', '.vue', '.json'] 80 | }, 81 | devServer: { 82 | historyApiFallback: true, 83 | noInfo: true, 84 | overlay: true 85 | }, 86 | performance: { 87 | hints: false 88 | }, 89 | devtool: '#eval-source-map' 90 | } 91 | 92 | if (process.env.NODE_ENV === 'production') { 93 | module.exports.devtool = '#source-map' 94 | // http://vue-loader.vuejs.org/en/workflow/production.html 95 | module.exports.plugins = (module.exports.plugins || []).concat([ 96 | new webpack.DefinePlugin({ 97 | 'process.env': { 98 | NODE_ENV: '"production"' 99 | } 100 | }), 101 | new webpack.optimize.UglifyJsPlugin({ 102 | sourceMap: true, 103 | compress: { 104 | warnings: false 105 | } 106 | }), 107 | new webpack.LoaderOptionsPlugin({ 108 | minimize: true 109 | }) 110 | ]) 111 | } 112 | -------------------------------------------------------------------------------- /src/mixin/base.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | /** 4 | * @description 表头数据 5 | */ 6 | columns: { 7 | type: Array, 8 | required: true 9 | }, 10 | /** 11 | * @description 表格加载 12 | */ 13 | loading: { 14 | type: Boolean, 15 | default: false 16 | }, 17 | /** 18 | * @description 表格加载配置 19 | */ 20 | loadingOptions: { 21 | type: Object, 22 | default: null 23 | }, 24 | /** 25 | * @description 表格配置 26 | */ 27 | options: { 28 | type: Object, 29 | default: null 30 | }, 31 | /** 32 | * @description 索引 33 | */ 34 | indexRow: { 35 | default: null 36 | }, 37 | /** 38 | * @description 多选 39 | */ 40 | selectionRow: { 41 | default: null 42 | } 43 | }, 44 | methods: { 45 | /** 46 | * @description 行选中状态 47 | */ 48 | handleCurrentChange (currentRow, oldCurrentRow) { 49 | this.$emit('current-change', currentRow, oldCurrentRow) 50 | }, 51 | /** 52 | * @description 勾选数据时触发的事件 53 | */ 54 | handleSelect (selection, row) { 55 | this.$emit('select', selection, row) 56 | }, 57 | /** 58 | * @description 勾选全选时触发的事件 59 | */ 60 | handleSelectAll (selection) { 61 | this.$emit('select-all', selection) 62 | }, 63 | /** 64 | * @description 复选框选择项发生变化时触发的事件 65 | */ 66 | handleSelectionChange (selection) { 67 | this.$emit('selection-change', selection) 68 | }, 69 | /** 70 | * @description 单元格 hover 进入时触发的事件 71 | */ 72 | handleCellMouseEnter (row, column, cell, event) { 73 | this.$emit('cell-mouse-enter', row, column, cell, event) 74 | }, 75 | /** 76 | * @description 单元格 hover 退出时触发的事件 77 | */ 78 | handleCellMouseLeave (row, column, cell, event) { 79 | this.$emit('cell-mouse-leave', row, column, cell, event) 80 | }, 81 | /** 82 | * @description 单元格点击时触发的事件 83 | */ 84 | handleCellClick (row, column, cell, event) { 85 | this.$emit('cell-click', row, column, cell, event) 86 | }, 87 | /** 88 | * @description 单元格双击时触发的事件 89 | */ 90 | handleCellDblclick (row, column, cell, event) { 91 | this.$emit('cell-dblclick', row, column, cell, event) 92 | }, 93 | /** 94 | * @description 行点击时触发的事件 95 | */ 96 | handleRowClick (row, event, column) { 97 | this.$emit('row-click', row, event, column) 98 | }, 99 | /** 100 | * @description 行右键点击时触发的事件 101 | */ 102 | handleRowContextmenu (row, event) { 103 | this.$emit('row-contextmenu', row, event) 104 | }, 105 | /** 106 | * @description 行双击时触发的事件 107 | */ 108 | handleRowDblclick (row, event) { 109 | this.$emit('row-dblclick', row, event) 110 | }, 111 | /** 112 | * @description 表头点击时触发的事件 113 | */ 114 | handleHeaderClick (column, event) { 115 | this.$emit('header-click', column, event) 116 | }, 117 | /** 118 | * @description 表头右键点击时触发的事件 119 | */ 120 | handleHeaderContextmenu (column, event) { 121 | this.$emit('header-contextmenu', column, event) 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/mixin/dialog.js: -------------------------------------------------------------------------------- 1 | import _forEach from 'lodash.foreach' 2 | import _clonedeep from 'lodash.clonedeep' 3 | 4 | export default { 5 | props: { 6 | /** 7 | * @description dialog配置 8 | */ 9 | formOptions: { 10 | type: Object, 11 | default: null 12 | }, 13 | /** 14 | * @description dialog新增标题 15 | */ 16 | addTitle: { 17 | type: String, 18 | default: '添加' 19 | }, 20 | /** 21 | * @description dialog修改标题 22 | */ 23 | editTitle: { 24 | type: String, 25 | default: '编辑' 26 | }, 27 | /** 28 | * @description 新增表单模板 29 | */ 30 | addTemplate: { 31 | type: Object, 32 | default: null 33 | }, 34 | /** 35 | * @description 修改表单模板 36 | */ 37 | editTemplate: { 38 | type: Object, 39 | default: null 40 | }, 41 | /** 42 | * @description 新增表单校验规则 43 | */ 44 | addRules: { 45 | type: Object, 46 | default: null 47 | }, 48 | /** 49 | * @description 编辑表单校验规则 50 | */ 51 | editRules: { 52 | type: Object, 53 | default: null 54 | } 55 | }, 56 | data () { 57 | return { 58 | /** 59 | * @description dialog显示与隐藏 60 | */ 61 | isDialogShow: false, 62 | /** 63 | * @description 表单数据 64 | */ 65 | formData: {}, 66 | /** 67 | * @description 表单模式 68 | */ 69 | formMode: 'edit', 70 | /** 71 | * @description 编辑暂存数据,用于储存不在editTemplate中的数据 72 | */ 73 | editDataStorage: {}, 74 | /** 75 | * @description 新增表单模板暂存 76 | */ 77 | addTemplateStorage: {}, 78 | /** 79 | * @description 修改表单模板暂存 80 | */ 81 | editTemplateStorage: {} 82 | } 83 | }, 84 | methods: { 85 | /** 86 | * @description 保存行数据 87 | */ 88 | handleDialogSave () { 89 | this.$refs.form.validate((valid) => { 90 | if (!valid) { 91 | return false 92 | } 93 | let rowData = {} 94 | if (this.formMode === 'edit') { 95 | rowData = _clonedeep(this.editDataStorage) 96 | _forEach(this.formData, (value, key) => { 97 | this._set(rowData, key, value) 98 | }) 99 | this.$emit('row-edit', { 100 | index: this.editIndex, 101 | row: rowData 102 | }, (param = null) => { 103 | if (param === false) { 104 | this.handleCloseDialog() 105 | return 106 | } 107 | this.handleDialogSaveDone({ 108 | ...rowData, 109 | ...param 110 | }) 111 | }) 112 | } else if (this.formMode === 'add') { 113 | _forEach(this.formData, (value, key) => { 114 | this._set(rowData, key, value) 115 | }) 116 | this.$emit('row-add', rowData, (param = null) => { 117 | if (param === false) { 118 | this.handleCloseDialog() 119 | return 120 | } 121 | this.handleDialogSaveDone({ 122 | ...rowData, 123 | ...param 124 | }) 125 | }) 126 | } 127 | }) 128 | }, 129 | /** 130 | * @description 取消保存行数据 131 | */ 132 | handleDialogCancel (done) { 133 | this.$emit('dialog-cancel', done) 134 | }, 135 | /** 136 | * @description 保存完成 137 | */ 138 | handleDialogSaveDone (rowData) { 139 | if (this.formMode === 'edit') { 140 | this.handleUpdateRow(this.editIndex, rowData) 141 | this.editDataStorage = {} 142 | } else if (this.formMode === 'add') { 143 | this.handleAddRow(rowData) 144 | } 145 | this.handleCloseDialog() 146 | }, 147 | /** 148 | * @description 关闭模态框 149 | */ 150 | handleCloseDialog () { 151 | this.isDialogShow = false 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/components/d2-column.vue: -------------------------------------------------------------------------------- 1 | 149 | 150 | 173 | -------------------------------------------------------------------------------- /src/d2-crud.vue: -------------------------------------------------------------------------------- 1 | 764 | 765 | 801 | 802 | 816 | -------------------------------------------------------------------------------- /dist/d2-crud.js: -------------------------------------------------------------------------------- 1 | !function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define("D2Crud",[],n):"object"==typeof exports?exports.D2Crud=n():e.D2Crud=n()}("undefined"!=typeof self?self:this,function(){return function(e){function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}var t={};return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:o})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="/dist/",n(n.s=7)}([function(e,n,t){(function(e,t){function o(e,n){return e.set(n[0],n[1]),e}function r(e,n){return e.add(n),e}function a(e,n){for(var t=-1,o=e?e.length:0;++t-1}function C(e,n){var t=this.__data__,o=B(t,e);return o<0?t.push([e,n]):t[o][1]=n,this}function S(e){var n=-1,t=e?e.length:0;for(this.clear();++n-1&&e%1==0&&e-1&&e%1==0&&e<=De}function $e(e){var n=typeof e;return!!e&&("object"==n||"function"==n)}function xe(e){return!!e&&"object"==typeof e}function Ce(e){return we(e)?P(e):W(e)}function Se(){return[]}function je(){return!1}var Oe=200,ze="__lodash_hash_undefined__",De=9007199254740991,Te="[object Arguments]",Me="[object Boolean]",Fe="[object Date]",Ae="[object Function]",Re="[object GeneratorFunction]",Ie="[object Map]",He="[object Number]",Pe="[object Object]",Ee="[object RegExp]",Be="[object Set]",Ue="[object String]",Ne="[object Symbol]",Le="[object ArrayBuffer]",qe="[object DataView]",Ge="[object Float32Array]",Ve="[object Float64Array]",We="[object Int8Array]",Je="[object Int16Array]",Xe="[object Int32Array]",Ke="[object Uint8Array]",Qe="[object Uint8ClampedArray]",Ye="[object Uint16Array]",Ze="[object Uint32Array]",en=/[\\^$.*+?()[\]{}|]/g,nn=/\w*$/,tn=/^\[object .+?Constructor\]$/,on=/^(?:0|[1-9]\d*)$/,rn={};rn[Te]=rn["[object Array]"]=rn[Le]=rn[qe]=rn[Me]=rn[Fe]=rn[Ge]=rn[Ve]=rn[We]=rn[Je]=rn[Xe]=rn[Ie]=rn[He]=rn[Pe]=rn[Ee]=rn[Be]=rn[Ue]=rn[Ne]=rn[Ke]=rn[Qe]=rn[Ye]=rn[Ze]=!0,rn["[object Error]"]=rn[Ae]=rn["[object WeakMap]"]=!1;var an="object"==typeof e&&e&&e.Object===Object&&e,cn="object"==typeof self&&self&&self.Object===Object&&self,ln=an||cn||Function("return this")(),un="object"==typeof n&&n&&!n.nodeType&&n,dn=un&&"object"==typeof t&&t&&!t.nodeType&&t,sn=dn&&dn.exports===un,pn=Array.prototype,mn=Function.prototype,fn=Object.prototype,hn=ln["__core-js_shared__"],yn=function(){var e=/[^.]+$/.exec(hn&&hn.keys&&hn.keys.IE_PROTO||"");return e?"Symbol(src)_1."+e:""}(),bn=mn.toString,vn=fn.hasOwnProperty,wn=fn.toString,kn=RegExp("^"+bn.call(vn).replace(en,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),gn=sn?ln.Buffer:void 0,_n=ln.Symbol,$n=ln.Uint8Array,xn=p(Object.getPrototypeOf,Object),Cn=Object.create,Sn=fn.propertyIsEnumerable,jn=pn.splice,On=Object.getOwnPropertySymbols,zn=gn?gn.isBuffer:void 0,Dn=p(Object.keys,Object),Tn=ce(ln,"DataView"),Mn=ce(ln,"Map"),Fn=ce(ln,"Promise"),An=ce(ln,"Set"),Rn=ce(ln,"WeakMap"),In=ce(Object,"create"),Hn=he(Tn),Pn=he(Mn),En=he(Fn),Bn=he(An),Un=he(Rn),Nn=_n?_n.prototype:void 0,Ln=Nn?Nn.valueOf:void 0;f.prototype.clear=h,f.prototype.delete=y,f.prototype.get=b,f.prototype.has=v,f.prototype.set=w,k.prototype.clear=g,k.prototype.delete=_,k.prototype.get=$,k.prototype.has=x,k.prototype.set=C,S.prototype.clear=j,S.prototype.delete=O,S.prototype.get=z,S.prototype.has=D,S.prototype.set=T,M.prototype.clear=F,M.prototype.delete=A,M.prototype.get=R,M.prototype.has=I,M.prototype.set=H;var qn=On?p(On,Object):Se,Gn=G;(Tn&&Gn(new Tn(new ArrayBuffer(1)))!=qe||Mn&&Gn(new Mn)!=Ie||Fn&&"[object Promise]"!=Gn(Fn.resolve())||An&&Gn(new An)!=Be||Rn&&"[object WeakMap]"!=Gn(new Rn))&&(Gn=function(e){var n=wn.call(e),t=n==Pe?e.constructor:void 0,o=t?he(t):void 0;if(o)switch(o){case Hn:return qe;case Pn:return Ie;case En:return"[object Promise]";case Bn:return Be;case Un:return"[object WeakMap]"}return n});var Vn=Array.isArray,Wn=zn||je;t.exports=ye}).call(n,t(2),t(17)(e))},function(e,n){e.exports=function(e,n,t,o,r,a){var i,c=e=e||{},l=typeof e.default;"object"!==l&&"function"!==l||(i=e,c=e.default);var u="function"==typeof c?c.options:c;n&&(u.render=n.render,u.staticRenderFns=n.staticRenderFns,u._compiled=!0),t&&(u.functional=!0),r&&(u._scopeId=r);var d;if(a?(d=function(e){e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,e||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),o&&o.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(a)},u._ssrRegister=d):o&&(d=o),d){var s=u.functional,p=s?u.render:u.beforeCreate;s?(u._injectStyles=d,u.render=function(e,n){return d.call(n),p(e,n)}):u.beforeCreate=p?[].concat(p,d):[d]}return{esModule:i,exports:c,options:u}}},function(e,n){var t;t=function(){return this}();try{t=t||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(t=window)}e.exports=t},function(e,n){function t(e,n){for(var t=-1,o=e?e.length:0;++t-1&&e%1==0&&e-1&&e%1==0&&e<=w}function h(e){var n=typeof e;return!!e&&("object"==n||"function"==n)}function y(e){return!!e&&"object"==typeof e}function b(e){return s(e)?r(e):i(e)}function v(e){return e}var w=9007199254740991,k="[object Arguments]",g="[object Function]",_="[object GeneratorFunction]",$=/^(?:0|[1-9]\d*)$/,x=Object.prototype,C=x.hasOwnProperty,S=x.toString,j=x.propertyIsEnumerable,O=function(e,n){return function(t){return e(n(t))}}(Object.keys,Object),z=function(e,n){return function(t,o){if(null==t)return t;if(!s(t))return e(t,o);for(var r=t.length,a=n?r:-1,i=Object(t);(n?a--:++at.parts.length&&(o.parts.length=t.parts.length)}else{for(var i=[],r=0;r0&&void 0!==arguments[0])||arguments[0],n=arguments[1],t=arguments[2];return"boolean"==typeof e?e:"function"==typeof e?e(n,t):Boolean(e)},handleRowHandleButtonDisabled:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],n=arguments[1],t=arguments[2];return"boolean"==typeof e?e:"function"==typeof e?e(n,t):Boolean(e)}}}},function(e,n,t){"use strict";var o=t(0),r=t.n(o),a=t(18),i=t.n(a),c=t(19),l=t.n(c);n.a={props:{data:{type:Array,required:!0}},data:function(){return{d2CrudData:[]}},computed:{d2CrudDataLength:function(){return this.d2CrudData.length}},watch:{data:function(){this.handleDataChange()}},mounted:function(){this.handleDataChange()},methods:{_get:i.a,_set:l.a,handleDataChange:function(){this.d2CrudData!==this.data&&(this.d2CrudData=r()(this.data))},handleSortDataChange:function(){var e=this;this.$nextTick(function(){e.d2CrudData=e.$refs.elTable.store.states.data})},handleSortChange:function(e){var n=e.column,t=e.prop,o=e.order;this.handleSortDataChange(),this.$emit("sort-change",{column:n,prop:t,order:o})},handleUpdateRow:function(e,n){this.$set(this.d2CrudData,e,n),this.defaultSort&&this.handleSortDataChange()},handleAddRow:function(e){this.$set(this.d2CrudData,this.d2CrudData.length,e),this.defaultSort&&this.handleSortDataChange()},handleRemoveRow:function(e){this.$delete(this.d2CrudData,e),this.defaultSort&&this.handleSortDataChange()}}}},function(e,n){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children||(e.children=[]),Object.defineProperty(e,"loaded",{enumerable:!0,get:function(){return e.l}}),Object.defineProperty(e,"id",{enumerable:!0,get:function(){return e.i}}),e.webpackPolyfill=1),e}},function(e,n,t){(function(n){function t(e,n){return null==e?void 0:e[n]}function o(e){var n=!1;if(null!=e&&"function"!=typeof e.toString)try{n=!!(e+"")}catch(e){}return n}function r(e){var n=-1,t=e?e.length:0;for(this.clear();++n-1}function h(e,n){var t=this.__data__,o=_(t,e);return o<0?t.push([e,n]):t[o][1]=n,this}function y(e){var n=-1,t=e?e.length:0;for(this.clear();++n-1}function h(e,n){var t=this.__data__,o=$(t,e);return o<0?t.push([e,n]):t[o][1]=n,this}function y(e){var n=-1,t=e?e.length:0;for(this.clear();++n-1&&e%1==0&&e2&&void 0!==arguments[2]?arguments[2]:null;this.formMode="edit",this.editDataStorage=i()(n),this.isDialogShow=!0,this.$emit("dialog-open",{mode:"edit",row:n}),this.editIndex=e,o?(this.formData=i()(o),this.editTemplateStorage=i()(o)):(this.formData=this.editTemplate?i()(this.editTemplate):{},this.editTemplateStorage=this.editTemplate?i()(this.editTemplate):{}),r()(this.formData,function(e,o){t.formData[o]=n.hasOwnProperty(o)?n[o]:t.formData[o]||""})}}}},function(e,n,t){"use strict";var o=t(3),r=t.n(o),a=t(0),i=t.n(a);n.a={methods:{handleAdd:function(){var e=this,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;this.formMode="add",this.$emit("dialog-open",{mode:"add"}),this.isDialogShow=!0,n?(this.formData=i()(n),this.addTemplateStorage=i()(n)):(this.formData=this.addTemplate?i()(this.addTemplate):{},this.addTemplateStorage=this.addTemplate?i()(this.addTemplate):{}),r()(this.formData,function(n,t){e.formData[t]=e.addTemplateStorage[t].value})}}}},function(e,n,t){"use strict";n.a={methods:{handleRemove:function(e,n){var t=this;"confirm"in this.rowHandle.remove&&!0!==this.rowHandle.remove.confirm?this.$emit("row-remove",{index:e,row:n},function(){t.handleRemoveDone(e)}):this.$confirm(this.handleAttribute(this.rowHandle.remove.confirmText,"确定删除吗?"),this.handleAttribute(this.rowHandle.remove.confirmTitle,"删除"),{confirmButtonText:this.handleAttribute(this.rowHandle.remove.confirmButtonText,"确定"),cancelButtonText:this.handleAttribute(this.rowHandle.remove.cancelButtonText,"取消"),type:this.handleAttribute(this.rowHandle.remove.type,"error")}).then(function(){t.$emit("row-remove",{index:e,row:n},function(){t.handleRemoveDone(e)})}).catch(function(){})},handleRemoveDone:function(e){this.handleRemoveRow(e)}}}},function(e,n,t){"use strict";var o=t(3),r=t.n(o),a=t(0),i=t.n(a),c=Object.assign||function(e){for(var n=1;n0&&void 0!==arguments[0]?arguments[0]:null;if(!1===n)return void e.handleCloseDialog();e.handleDialogSaveDone(c({},t,n))})):"add"===e.formMode&&(r()(e.formData,function(n,o){e._set(t,o,n)}),e.$emit("row-add",t,function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;if(!1===n)return void e.handleCloseDialog();e.handleDialogSaveDone(c({},t,n))}))})},handleDialogCancel:function(e){this.$emit("dialog-cancel",e)},handleDialogSaveDone:function(e){"edit"===this.formMode?(this.handleUpdateRow(this.editIndex,e),this.editDataStorage={}):"add"===this.formMode&&this.handleAddRow(e),this.handleCloseDialog()},handleCloseDialog:function(){this.isDialogShow=!1}}}},function(e,n,t){"use strict";n.a={props:{pagination:{type:Object,default:null}},methods:{handlePaginationSizeChange:function(e){this.$emit("pagination-size-change",e)},handlePaginationCurrentChange:function(e){this.$emit("pagination-current-change",e)},handlePaginationPrevClick:function(e){this.$emit("pagination-prev-click",e)},handlePaginationNextClick:function(e){this.$emit("pagination-next-click",e)}}}},function(e,n,t){"use strict";function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}var r=Object.assign||function(e){for(var n=1;n