├── .babelrc ├── .gitignore ├── README.md ├── README.zh-CN.md ├── build ├── webpack.base.conf.js ├── webpack.dev.conf.js ├── webpack.doc.conf.js ├── webpack.prod.conf.js └── webpack.server.conf.js ├── demo.png ├── dev ├── index.html ├── server │ ├── dist │ │ ├── build.js │ │ └── build.js.map │ ├── favicon.png │ ├── index.html │ └── server.js ├── src │ ├── App.vue │ ├── assets │ │ ├── css │ │ │ └── flex.css │ │ └── image │ │ │ └── logo.png │ ├── index.js │ └── views │ │ ├── CustomCell.vue │ │ ├── CustomStyle.vue │ │ ├── DefaultStyle.vue │ │ ├── Home.vue │ │ ├── NavMenu.vue │ │ ├── VueButton.vue │ │ └── VueSearch.vue └── static │ └── favicon.png ├── dist └── index.min.js ├── docs ├── build.js ├── build.js.map ├── favicon.png ├── images │ ├── basic.png │ ├── border.png │ ├── checkbox.png │ ├── edit.png │ ├── filter.png │ ├── fixed.png │ ├── header.png │ ├── height.png │ ├── heightAndFixed.png │ ├── highlight.png │ ├── pagination.png │ ├── search.png │ ├── sort.png │ ├── stripe.png │ └── width.png └── index.html ├── package.json └── src ├── assets ├── css │ └── flex.css └── iconfont │ ├── iconfont.css │ ├── iconfont.eot │ ├── iconfont.js │ ├── iconfont.json │ ├── iconfont.svg │ ├── iconfont.ttf │ ├── iconfont.woff │ └── iconfont.woff2 ├── components ├── FilterPanel.vue ├── ResizeDetector.vue ├── VueInput.vue ├── VuePagination.vue ├── VueTableDynamic.vue └── scrollbar │ └── HorizontalScrollbar.vue ├── index.js └── utils ├── array.js ├── clickoutside.js ├── display.js ├── dom.js ├── unique.js └── util.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { "modules": false }], 4 | "stage-0" 5 | ], 6 | "plugins": [ 7 | [ 8 | "transform-runtime", 9 | { 10 | "regenerator": true 11 | } 12 | ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | package-lock.json 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-table-dynamic 2 | 3 | English | [简体中文](./README.zh-CN.md) 4 | 5 | [vue-table-dynamic](https://github.com/TheoXiong/vue-table-dynamic) is a vue component of dynamic table. It's designed to respond to data changes in real time, and oriented to the runtime. 6 | 7 | ## Features 8 | - Multiple Select 9 | - Search 10 | - Sort 11 | - Filter 12 | - Pagination 13 | - Edit 14 | - Border 15 | - Stripe 16 | - Highlight 17 | - Column Width 18 | - Configure Header 19 | - Fixed Header 20 | - Fixed Columns 21 | - Slot 22 | 23 | ## Demo 24 | [https://theoxiong.github.io/vue-table-dynamic/](https://theoxiong.github.io/vue-table-dynamic/) 25 | 26 | ![Demo](./demo.png) 27 | 28 | ## Install 29 | ``` 30 | $ npm install vue-table-dynamic --save 31 | ``` 32 | 33 | ## Usage 34 | 35 | ### Import 36 | ``` 37 | import VueTableDynamic from 'vue-table-dynamic' 38 | ``` 39 | 40 | ### Registration 41 | #### Global registration 42 | ``` 43 | Vue.use(VueTableDynamic) 44 | ``` 45 | #### Local registration 46 | ``` 47 | 52 | ``` 53 | 54 | ### Basic Table 55 | 56 | Basic table usage 57 | 58 | ![basic](./docs/images/basic.png) 59 | 60 | ``` 61 | 66 | 67 | 87 | ``` 88 | 89 | ### Border 90 | 91 | Bordered table usage 92 | 93 | - `border:`*`true`* with border 94 | - `border:`*`false`* without border 95 | 96 | ![border](./docs/images/border.png) 97 | 98 | ``` 99 | 104 | 105 | 126 | ``` 127 | 128 | ### Stripe 129 | 130 | Striped rows 131 | 132 | - `stripe:`*`true`* striped 133 | - `stripe:`*`false`* unstriped 134 | 135 | ![stripe](./docs/images/stripe.png) 136 | 137 | ``` 138 | 143 | 144 | 167 | ``` 168 | 169 | ### Highlight 170 | 171 | Highlighted rows/columns/cells 172 | 173 | - `highlight:`*`{row?:Array; column?:Array; cell?:Array<[number,number]>;}`* configure highlighted rows, columns, cells. such as: *`{row: [1], column: [1], cell: [[-1, -1]]}`* if negative, the position from the end of the array. 174 | - `highlightedColor:`*`string`* configure highlighted colors 175 | 176 | ![highlight](./docs/images/highlight.png) 177 | 178 | ``` 179 | 184 | 185 | 210 | ``` 211 | 212 | ### Multiple Select 213 | 214 | Select multiple rows 215 | 216 | - `showCheck:`*`boolean`* show checkbox of rows 217 | - `getCheckedRowDatas:`*`function`* get data for all currently selected rows 218 | - `setAllRowChecked:`*`function(selected:boolean)`* toggle all selection 219 | - `select:`*`event`* currently selected/unselected rows 220 | 221 | ![checkbox](./docs/images/checkbox.png) 222 | 223 | ``` 224 | 235 | 236 | 266 | ``` 267 | 268 | ### Search 269 | 270 | Filter rows by keyword 271 | 272 | - `enableSearch:`*`boolean`* enable/disable searching 273 | - `search:`*`function(value:string, included:array, excluded:array)`* manual row filtering 274 | 275 | ![search](./docs/images/search.png) 276 | 277 | ``` 278 | 287 | 288 | 311 | ``` 312 | 313 | ### Sort 314 | 315 | Sort rows based on specified column data 316 | 317 | - `sort:`*`Array`* array members are sortable column indexes. such as: *`[0, 1]`* 318 | 319 | ![sort](./docs/images/sort.png) 320 | 321 | ``` 322 | 331 | 332 | 355 | ``` 356 | 357 | ### Filter 358 | 359 | Filter rows based on specified column data and rule 360 | 361 | - `filter:`*`Array<{column:number; content:Array<{text:string; value:string|number;}>; method:function;}>`* specify filterable columns and rules. such as: *`[{column: 0, content: [{text: '> 2', value: 2}], method: (value, cell) => { return cell.data > value }}]`* 362 | - `filter[].column:` column index 363 | - `filter[].content:` filter items 364 | - `filter[].method:` filter rule. 365 | 366 | ![filter](./docs/images/filter.png) 367 | 368 | ``` 369 | 378 | 379 | 412 | ``` 413 | 414 | ### Pagination 415 | 416 | Table with pagination 417 | 418 | - `pagination:`*`boolean`* enable/disable pagination 419 | - `pageSize?:`*`number`* row count of each page. default: *`10`* 420 | - `pageSizes?:`*`Array`* options of row count per page. default: *`[10, 20, 50, 100]`* 421 | 422 | ![pagination](./docs/images/pagination.png) 423 | 424 | ``` 425 | 434 | 435 | 467 | ``` 468 | 469 | ### Edit 470 | 471 | Editable table 472 | Support specifying rows/columns/cells for editing 473 | 474 | - `edit:`*`{row?:Array; column?:Array; cell?:Array<[number,number]>;}`* configure editable rows, columns, cells. such as: *`{row: [1], column: [1], cell: [[-1, -1]]}`*. if negative, the position from the end of the array. 475 | - `getData:`*`function()`* table data changed after editing, get the latest data by this method. 476 | - `cell-change:`*`event`* cell data changed event 477 | - `edit:`*`{row: 'all'}`* all cells can be edited 478 | - if `header` is *`'row'`*, the first row is not editable 479 | - if the type of source data item is `number`, only acceptable when entering numbers 480 | 481 | ![edit](./docs/images/edit.png) 482 | 483 | ``` 484 | 494 | 495 | 526 | ``` 527 | 528 | ### Column width 529 | 530 | Configure column width 531 | 532 | - `columnWidth:`*`Array<{column:number; width:number|string;}>`* such as: *`[{column: 0, width: 60}, {column: 3, width: '15%'}]`* 533 | - `columnWidth[].column` index of column 534 | - `columnWidth[].width` width of column. number for pixel value, string for percentage 535 | 536 | ![width](./docs/images/width.png) 537 | 538 | ``` 539 | 544 | 545 | 567 | ``` 568 | 569 | ### Header Configure 570 | 571 | - `header:`*`row`* the first row is header 572 | - `header:`*`column`* the first column is header 573 | - `header:`*`''`* no header 574 | 575 | ![Header](./docs/images/header.png) 576 | 577 | ``` 578 | 583 | 584 | 606 | ``` 607 | 608 | ### Fixed Header 609 | 610 | Fixed header by configure the height of table 611 | 612 | - `height:`*`number`* table height 613 | - when the value of `header` is not *`'row'`*, the first row is a normal row, will not fixed 614 | 615 | ![height](./docs/images/height.png) 616 | 617 | ``` 618 | 627 | 628 | 658 | ``` 659 | 660 | ### Fixed Columns 661 | 662 | Fixed columns by configure `fixed` 663 | 664 | - `fixed:`*`number`* the fixed columns 665 | - columns with index less than or equal to `fixed` will be configured as fixed. such as `fixed: 1`, the first column and the second column will be fixed 666 | - for fixed columns, need to specify the column width (pixel value) by `columnwidth` 667 | - multi check box is not in the range of fixed column, it's always in front of the first column 668 | - if need to fix a column in the middle, adjust the column data in params.data to column 0 669 | 670 | ![fixed](./docs/images/fixed.png) 671 | 672 | ``` 673 | 678 | 679 | 710 | ``` 711 | 712 | ### Fixed Header and Columns 713 | 714 | Fixed header by `height`. Fixed columns by `fixed` 715 | 716 | ![heightAndFixed](./docs/images/heightAndFixed.png) 717 | 718 | ``` 719 | 724 | 725 | 757 | ``` 758 | 759 | 760 | ### Slot 761 | 762 | Customize cell content by slot 763 | The slot name should be `column-n`, `n` is the index of column 764 | 765 | ``` 766 | 775 | 776 | 796 | ``` 797 | 798 | ## API 799 | 800 | ### Attributes 801 | 802 | - `params` is the object that need to be passed to the component `props` 803 | - the following items are all child properties of the `params` object 804 | - `data` is required attribute, others are optional 805 | 806 | | name | description | type | optional value | default value | 807 | | -----| ----------- | ---- | -------------- | ------------- | 808 | | `data` | source data | `Array<[number, ..., number]>` | - | `[]` | 809 | | `header` | configure header | `string` | `row`: the first row is header; `column`: the first column is header; `''`: no header | `''` | 810 | | `border` | table with border | `boolean` | `true`/`false` | `false` | 811 | | `stripe` | striped table | `boolean` | `true`/`false` | `false` | 812 | | `highlight` | configure highlighted rows, columns, cells. such as: {row: `[1]`, column: `[1]`, cell: `[[-1, -1]]`}. if negative, the position from the end of the array. | {row?:Array<>; column?:Array<>; cell?:Array<>;} | - | `{}` | 813 | | `highlightedColor` | highlighted colors | `string` | - | `#EBEBEF` | 814 | | `wordWrap` | word-wrap style of table cell | `string` | `normal/break-word` | `normal` | 815 | | `whiteSpace` | white-space style of table cell | `string` | `nowrap/normal/pre/pre-wrap/pre-line` | `nowrap` | 816 | | `textOverflow` | text-overflow style of table cell | `string` | `clip/ellipsis` | `clip` | 817 | | `showCheck` | show checkbox of rows. Only when the `header` is `'row'`, the first cell of the first row is the checkbox of all rows. Otherwise, the first cell is the checkbox of the first row | `boolean` | `true`/`false` | `false` | 818 | | `enableSearch` | enable/disable searching, filter rows by keyword | `boolean` | `true`/`false` | `false` | 819 | | `minWidth` | min width of table | `number` | - | `100` | 820 | | `maxWidth` | max width of table | `number` | - | `10000` | 821 | | `height` | table height. fixed header by configure the height of table | `number` | - | - | 822 | | `fixed` | fixed columns | `number` | `>= 0` | | 823 | | `headerHeight` | header height | `number` | `>= 24` | `30` | 824 | | `rowHeight` | row height | `number` | `>= 24` | `30` | 825 | | `columnWidth` | Configure column width | Array<{column:number; width:number/string;}> | - | - | 826 | | `sort` | sort rows based on specified column data | `Array` | - | - | 827 | | `filter` | filter rows based on specified column data and rule. `column`: index; `content`: filter items; `method` filter rule. | Array<{column, content, method}> | - | - | 828 | | `edit` | specifying rows/columns/cells for editing. table data changed after editing, get the latest data by `getData` method | {row?:Array<>; column?:Array<>; cell?:Array<>;} | - | - | 829 | | `pagination` | table with pagination | `boolean` | `true`/`false` | `false` | 830 | | `pageSize` | row count of each page | `number` | - | `10` | 831 | | `pageSizes` | options of row count per page | `Array` | - | `[10, 20, 50, 100]`| 832 | | `showTotal` | show total count of pagination | `boolean` | `true`/`false` | `false` | 833 | | `scrollbar` | display of scroll bar | `string` | `show/hover/hidden` | `show` | 834 | 835 | ### Methods 836 | 837 | | method name | description | parameters | return | 838 | | ------- | -------- | --------- | ---------- | 839 | | `getData` | table data changed after editing, get the latest data by this method | - | `Array<[number, ..., number]>` | 840 | | `getCheckedRowDatas` | get data for all currently selected rows | `includeWhenHeaderInfirstRow: boolean` include header row when the first row is header,default is `false` | `Array<[number, ..., number]>` | 841 | | `getRowData` | get row data by index | `rowIndex:number` index;`isCurrent: boolean` is the index sorted,default is `false` | `Array` | 842 | | `search` | manual row filtering | `searchValue:string` keyword; `included:array` match in specified column; `excluded:array` not match in specified column, priority over `included` | - | 843 | | `clearSearch` | clear searching, show all rows | - | - | 844 | | `toPage` | switch to the target page, when pagination is enable | `tagetPage:number` page to switch | - | 845 | 846 | ### Events 847 | 848 | | event name | description | parameters | 849 | | ------------- | ------------- | ---------- | 850 | | `select` | event when selecting a row | `checked: boolean`; ` index: number`; `data: Array` | 851 | | `select-all` | event when clicking the checkbox in table header | `isCheckedAll: boolean` | 852 | | `row-click` | event when clicking a row | ` index:number`; `data:Array` | 853 | | `cell-click` | event when clicking a cell | `rowIndex:number`; `columnIndex:number`; ` data:string\number` | 854 | | `cell-contextmenu` | event when opening a cell's context menu | `rowIndex:number`; `columnIndex:number`; ` data:string\number` | 855 | | `cell-change` | event when edting a cell | `rowIndex:number`; `columnIndex:number`; `data:string\number` | 856 | | `sort-change` | event when sorting | `index: number`; `value: string` | 857 | 858 | 859 | 860 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # vue-table-dynamic 2 | 3 | 简体中文 | [English](./README.md) 4 | 5 | [vue-table-dynamic](https://github.com/TheoXiong/vue-table-dynamic) 是一个动态表格组件。面向运行时,实时响应源数据变化,动态更新表格内容与形态。具有丰富的特性,所有特性均可按需引入,满足多样化需求。 6 | 7 | ## 特性 8 | - [多选](#多选) 9 | - [搜索](#搜索) 10 | - [排序](#排序) 11 | - [筛选](#筛选) 12 | - [分页](#分页) 13 | - [编辑](#编辑]) 14 | - [边框](#边框) 15 | - [条纹](#条纹) 16 | - [高亮](#高亮) 17 | - [配置列宽](#配置列宽) 18 | - [配置表头](#配置表头) 19 | - [固定表头](#固定表头) 20 | - [固定列](#固定列) 21 | - [作用域插槽](#作用域插槽) 22 | 23 | ## Demo 24 | [https://theoxiong.github.io/vue-table-dynamic/](https://theoxiong.github.io/vue-table-dynamic/) 25 | 26 | ![Demo](./demo.png) 27 | 28 | ## 安装 29 | ``` 30 | $ npm install vue-table-dynamic --save 31 | ``` 32 | 33 | ## 使用 34 | 35 | ### 引入模块 36 | ``` 37 | import VueTableDynamic from 'vue-table-dynamic' 38 | ``` 39 | 40 | ### 注册 41 | #### 全局注册 42 | ``` 43 | Vue.use(VueTableDynamic) 44 | ``` 45 | #### 组件内注册 46 | ``` 47 | 52 | ``` 53 | 54 | ### 基础表格 55 | 56 | 基础表格用法 57 | 58 | ![basic](./docs/images/basic.png) 59 | 60 | ``` 61 | 66 | 67 | 87 | ``` 88 | 89 | ### 边框 90 | 91 | 配置`border`属性,使用带边框表格 92 | 93 | ![border](./docs/images/border.png) 94 | 95 | ``` 96 | 101 | 102 | 123 | ``` 124 | 125 | ### 条纹 126 | 127 | 配置`stripe`属性,显示行条纹 128 | 129 | ![stripe](./docs/images/stripe.png) 130 | 131 | ``` 132 | 137 | 138 | 161 | ``` 162 | 163 | ### 高亮 164 | 165 | 配置`highlight`属性,指定行/列/单元高亮 166 | 167 | - `highlight`类型为`{row?:Array; column?:Array; cell?:Array<[number,number]>;}`。如:`{row: [1], column: [1], cell: [[-1, -1]]}`,负数表示倒序索引 168 | - `highlightedColor:`*`string`* 设置高亮背景色 169 | 170 | ![highlight](./docs/images/highlight.png) 171 | 172 | ``` 173 | 178 | 179 | 204 | ``` 205 | 206 | ### 多选 207 | 208 | 配置`showCheck`属性,启用多选功能 209 | 210 | - 通过`select`事件和`selection-change`事件,监听用户勾选操作 211 | - 通过`getCheckedRowDatas`方法获取当前所有选中的行数据 212 | - 通过`setAllRowChecked`方法将选中状态切换为全选或清空选择 213 | 214 | ![checkbox](./docs/images/checkbox.png) 215 | 216 | ``` 217 | 228 | 229 | 259 | ``` 260 | 261 | ### 搜索 262 | 263 | 配置`enableSearch`属性,启用全局搜索功能,仅显示匹配到搜索关键字的行 264 | 265 | - 通过 `search:`*`function(value:string, included:array, excluded:array)`* 方法可以手动对行搜索过滤,适用于自定义搜索框(配置`enableSearch`为`false`) 266 | 267 | ![search](./docs/images/search.png) 268 | 269 | ``` 270 | 279 | 280 | 303 | ``` 304 | 305 | ### 排序 306 | 307 | 配置`sort`属性,启用排序功能,基于列数据对行进行排序 308 | 309 | - `sort`类型为`Array`,数组成员为启用排序的列索引。如:`sort: [0, 1]`,基于第0列和第1列排序 310 | 311 | ![sort](./docs/images/sort.png) 312 | 313 | ``` 314 | 323 | 324 | 347 | ``` 348 | 349 | ### 筛选 350 | 351 | 通过`filter`配置具有筛选功能的列以及筛选条件 352 | 353 | - `filter`类型为`Array<{column:number; content:Array<{text:string; value:string|number;}>; method:function;}>`。 如:`[{column: 0, content: [{text: '> 2', value: 2}], method: (value, cell) => { return cell.data > value }}]` 354 | - `filter[].column` 列索引 355 | - `filter[].content`筛选项 356 | - `filter[].method` 筛选的方法,如果是多选的筛选项,对每一条数据会执行多次,任意一次返回 true 就会显示。 357 | 358 | ![filter](./docs/images/filter.png) 359 | 360 | ``` 361 | 370 | 371 | 404 | ``` 405 | 406 | ### 分页 407 | 408 | 配置`pagination`属性,启用分页功能,支持配置每页显示条数和条数可选项 409 | 410 | - `pagination:boolean`为`true`时启用分页功能 411 | - `pageSize?:number` 每页显示行数,默认为`10` 412 | - `pageSizes?:Array` 每页行数可选项,默认为`[10, 20, 50, 100]` 413 | 414 | ![pagination](./docs/images/pagination.png) 415 | 416 | ``` 417 | 426 | 427 | 459 | ``` 460 | 461 | ### 编辑 462 | 463 | 配置`edit`属性,启用编辑功能,支持配置某行/某列/某个表格单元可编辑 464 | 465 | - `edit`类型为`{row?:Array; column?:Array; cell?:Array<[number,number]>;}`。如:`{row: [1], column: [1], cell: [[-1, -1]]}`,负数表示倒序索引 466 | - 配置为`{row: 'all'}`时,所有单元均可编辑 467 | - 通过`cell-change`事件,监听编辑操作 468 | - 编辑会改变当前数据,不会改变传入的源数据 469 | - 通过`getData`方法,可获取编辑后的最新数据 470 | - 如果配置了 `header: 'row'`,则第一行表头不可编辑 471 | - 如果源数据为`number`类型, 则仅在输入数字时有效 472 | 473 | ![edit](./docs/images/edit.png) 474 | 475 | ``` 476 | 486 | 487 | 518 | ``` 519 | 520 | ### 配置列宽 521 | 522 | 通过`columnWidth`属性配置表格列宽度 523 | 524 | - `columnWidth`类型为`Array<{column:number; width:number|string;}>`。如:`[{column: 0, width: 60}, {column: 3, width: '15%'}]` 525 | - `columnWidth[].column` 列索引 526 | - `columnWidth[].width` 列宽度,支持像素值(number)和百分比(string) 527 | 528 | ![width](./docs/images/width.png) 529 | 530 | ``` 531 | 536 | 537 | 559 | ``` 560 | 561 | ### 配置表头 562 | 563 | 通过`header`属性配置表头 564 | 565 | - `header: row` 首行为表头 566 | - `header: column` 首列为表头 567 | - `header: ''` 无表头 568 | 569 | ![header](./docs/images/header.png) 570 | 571 | ``` 572 | 577 | 578 | 600 | ``` 601 | 602 | ### 固定表头 603 | 604 | 通过`height`配置表格高度即可固定表头。当表格超出配置高度,垂直滚动时首行表头会固定不动 605 | 606 | - `height: number` 表格可视高度 607 | - 当`header`不为`row`时,首行被视为普通表格行,会跟随滚动 608 | 609 | ![height](./docs/images/height.png) 610 | 611 | ``` 612 | 621 | 622 | 652 | ``` 653 | 654 | ### 固定列 655 | 656 | 通过`fixed`配置需要固定的列。当表格内容宽度超出容器宽度时,出现水平滚动条。水平滚动时,固定的列不会移动 657 | 658 | - `fixed: number` 需要固定的列。 659 | - 索引小于等于`fixed`的列都会配置为固定。如`fixed`为1时,第0列和第1列被配置为固定 660 | - 对于配置为固定的列,需要通过`columnWidth`指定列的宽度(像素值) 661 | - 多选框不在固定列索引范围内。即:有多选框时,第0列为内容列,多选框始终在第0列前面 662 | - 如果需要将中间某列内容固定,可以将params.data中该列数据调整到第0列 663 | 664 | ![fixed](./docs/images/fixed.png) 665 | 666 | ``` 667 | 672 | 673 | 704 | ``` 705 | 706 | ### 固定表头和列 707 | 708 | 通过`height`固定表头, 通过`fixed`配置需要固定的列 709 | 710 | ![heightAndFixed](./docs/images/heightAndFixed.png) 711 | 712 | ``` 713 | 718 | 719 | 751 | ``` 752 | 753 | ### 作用域插槽 754 | 755 | 通过作用域插槽,可自定义cell内容 756 | 具名插槽的名称为 `column-n`, n为列索引 757 | 758 | ``` 759 | 768 | 769 | 789 | ``` 790 | 791 | ## API 792 | 793 | ### 属性 794 | 795 | - 传入到组件的`props`只有`params`对象,下表的属性均为`params`对象的一级属性 796 | - `data`为必需属性,其他均为可选项 797 | 798 | | 属性名 | 说明 | 类型 | 可选值 | 默认值 | 799 | | ----| --------- | ------- | ------------- | --------- | 800 | | `data` | 表格源数据 | `Array<[number, ..., number]>` | - | `[]` | 801 | | `header` | 表头配置 | `string` | `row`: 首行表头; `column`: 首列表头; 其他: 无 | 无表头 | 802 | | `border` | 是否带边框 | `boolean` | `true`/`false` | `false` | 803 | | `stripe` | 是否带条纹 | `boolean` | `true`/`false` | `false` | 804 | | `highlight` | 高亮的行/列/表单元。值为负数时,表示倒序索引 | {row?:Array<>; column?:Array<>; cell?:Array<>;} | - | `{}` | 805 | | `highlightedColor` | 高亮的颜色 | `string` | - | `#EBEBEF` | 806 | | `wordWrap` | 表格单元文本的word-wrap样式 | `string` | `normal/break-word` | `normal` | 807 | | `whiteSpace` | 表格单元文本的white-space样式 | `string` | `nowrap/normal/pre/pre-wrap/pre-line` | `nowrap` | 808 | | `textOverflow` | 表格单元文本的text-overflow样式 | `string` | `clip/ellipsis` | `clip` | 809 | | `showCheck` | 显示多选框。仅当`header`为`row`时,第一行第一列为全选框,否则第一列均为当前行的勾选框 | `boolean` | `true`/`false` | `false` | 810 | | `enableSearch` | 使用全局搜索功能 | `boolean` | `true`/`false` | `false` | 811 | | `minWidth` | 表最小宽度 | `number` | - | `100` | 812 | | `maxWidth` | 表最大宽度 | `number` | - | `10000` | 813 | | `height` | 表可视高度。通过配置表格高度,(当`header`为`row`时)可固定表头。当表格超出配置高度,垂直滚动时首行表头会固定不动 | `number` | - | - | 814 | | `fixed` | 固定列,需要与columnWidth配合 | `number` | `>= 0` | | 815 | | `headerHeight` | 表头高度 | `number` | `>= 24` | `30` | 816 | | `rowHeight` | 行高 | `number` | `>= 24` | `30` | 817 | | `columnWidth` | 指定某一列或某几列的宽度,剩余列宽度均分。`width`值可为绝对值或相对百分比 | Array<{column: number; width: number/string;}> | - | - | 818 | | `sort` | 指定可排序的列 | `Array` | - | - | 819 | | `filter` | 指定可筛选的列。`column`为列索引; `content`为筛选项; `method` 为筛选的方法,同时选中多个筛选项时任意一次返回true就会显示 | Array<{column, content, method}> | - | - | 820 | | `edit` | 可编辑的 行、列、表单元。负数表示倒序(如`-1`为最后`1`行、列)配置`row`为` 'all'`时,所有行可编辑。编辑会改变当前数据,不会改变传入的源数据| {row?: Array<>; column?: Array<>; cell?: Array<>;} | - | - | 821 | | `pagination` | 使用分页 | `boolean` | `true`/`false` | `false` | 822 | | `pageSize` | 每页行数 | `number` | - | `10` | 823 | | `pageSizes` | 每页行数的可选项 | `Array` | - | | 824 | | `showTotal` | 显示分页总数 | `boolean` | `true`/`false` | `false` | 825 | | `scrollbar` | 滚动条的显示时机 | `string` | `show/hover/hidden` | `show` | 826 | 827 | ### 方法 828 | 829 | | 方法名 | 说明 | 参数 | 返回 | 830 | | ------- | -------- | --------- | ---------- | 831 | | `getData` | 获取表格数据。当使用编辑功能时,可使用此方法获取编辑后的最新数据 | 无 | `Array<[number, ..., number]>` | 832 | | `getCheckedRowDatas` | 使用多选功能时,获取选中行的数据 | `includeWhenHeaderInfirstRow: boolean` 当首行为表头时,是否包含表头行,默认`false` | `Array<[number, ..., number]>` | 833 | | `getRowData` | 根据行索引获取指定行的数据 | `rowIndex: number` 行索引;`isCurrent: boolean`索引是否为排序后的索引,默认false,即原始索引 | `Array` | 834 | | `search` | 手动对行搜索过滤,适用于自定义搜索框(配置`enableSearch`为`false`) | `searchValue: string`搜索的关键字; `included:array` 在指定的列里进行匹配; `excluded:array` 不在指定的列里匹配, 优先级高于`included` | 无 | 835 | | `clearSearch` | 取消搜索过滤,显示所有行 | 无 | 无 | 836 | | `toPage` | (分页时)跳转到目标页 | `tagetPage:number` 目标页索引 | - | 837 | 838 | ### 事件 839 | 840 | | 事件名 | 说明 | 回调参数 | 841 | | ------------- | ------------------------ | -----------------------------------------------------------| 842 | | `select` | 行的选中/取消选中 | `checked: boolean`; ` index: number`; `data: Array` | 843 | | `select-all` | 全选/取消全选 | `isCheckedAll: boolean` | 844 | | `row-click` | 点击行 | ` index: number`; ` data: Array` | 845 | | `cell-click` | 点击表格单元 | `rowIndex: number`; `columnIndex: number`; ` data: string\number` | 846 | | `cell-contextmenu` | 右键表格单元 | `rowIndex: number`; `columnIndex: number`; ` data: string\number` | 847 | | `cell-change` | 表格单元数据(编辑)改变 | `rowIndex: number`; `columnIndex: number`; ` data: string\number` | 848 | | `sort-change` | 排序 | `index: number`; `value: string` | 849 | 850 | 851 | 852 | -------------------------------------------------------------------------------- /build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const { VueLoaderPlugin } = require('vue-loader') 3 | 4 | module.exports = { 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.scss$/, 9 | use: ['vue-style-loader', 'css-loader', 'sass-loader'] 10 | }, 11 | { 12 | test: /\.sass$/, 13 | use: ['vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax'] 14 | }, 15 | { 16 | test: /\.css$/, 17 | use: [ 18 | 'vue-style-loader', 19 | 'css-loader' 20 | ] 21 | }, 22 | { 23 | test: /\.vue$/, 24 | loader: 'vue-loader', 25 | options: { 26 | extractCSS: process.env.NODE_ENV === 'production', 27 | loaders: { 28 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', 29 | scss: 'vue-style-loader!css-loader!sass-loader', 30 | less: 'vue-style-loader!css-loader!less-loader' 31 | } 32 | } 33 | }, 34 | { 35 | test: /\.html$/, 36 | use: 'vue-html-loader' 37 | }, 38 | { 39 | test: /\.js$/, 40 | loader: 'babel-loader', 41 | exclude: [ 42 | path.resolve(__dirname, '../node_modules'), 43 | path.resolve(__dirname, '../dist') 44 | ] 45 | }, 46 | { 47 | test: /\.(png|jpg|gif|svg)$/, 48 | loader: 'url-loader?limit=5000000&name=assets/[name].[ext]' 49 | }, 50 | { 51 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 52 | use: { 53 | loader: 'url-loader', 54 | query: { 55 | limit: 500000, 56 | name: 'fonts/[name]--[folder].[ext]' 57 | } 58 | } 59 | } 60 | ] 61 | }, 62 | performance: { 63 | hints: false 64 | }, 65 | devtool: 'source-map', 66 | resolve: { 67 | extensions: ['*', '.js', '.vue', '.json', '.ts'] 68 | }, 69 | plugins: [new VueLoaderPlugin()] 70 | } 71 | -------------------------------------------------------------------------------- /build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const merge = require("webpack-merge"); 3 | const baseWebpackConfig = require("./webpack.base.conf"); 4 | 5 | module.exports = merge(baseWebpackConfig, { 6 | entry: path.resolve(__dirname, "../dev/src/index.js"), 7 | output: { 8 | path: path.resolve(__dirname, "../dev/dist"), 9 | publicPath: "dist/", 10 | filename: "build.js" 11 | }, 12 | devServer: { 13 | contentBase: path.resolve(__dirname, "../dev"), 14 | compress: true, 15 | port: 3000, 16 | stats: { 17 | assets: true, 18 | children: false, 19 | chunks: false, 20 | hash: true, 21 | modules: false, 22 | publicPath: true, 23 | timings: false, 24 | version: true, 25 | warnings: true, 26 | colors: { 27 | green: "\u001b[32m" 28 | } 29 | } 30 | }, 31 | resolve: { 32 | alias: { 33 | '@': path.join(__dirname, '../dev/src'), 34 | 'vue$': 'vue/dist/vue.esm.js' 35 | }, 36 | extensions: ['*', '.js', '.vue', '.json', ".ts"] 37 | }, 38 | }) 39 | -------------------------------------------------------------------------------- /build/webpack.doc.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const merge = require('webpack-merge') 3 | const baseWebpackConfig = require('./webpack.base.conf') 4 | 5 | module.exports = merge(baseWebpackConfig, { 6 | entry: path.resolve(__dirname, '../dev/src/index.js'), 7 | output: { 8 | path: path.resolve(__dirname, '../docs'), 9 | publicPath: '', 10 | filename: 'build.js' 11 | }, 12 | resolve: { 13 | alias: { 14 | '@': path.join(__dirname, '../dev/src'), 15 | 'vue$': 'vue/dist/vue.esm.js' 16 | }, 17 | extensions: ['*', '.js', '.vue', '.json', '.ts'] 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | const path = require("path") 2 | const merge = require("webpack-merge") 3 | const baseWebpackConfig = require("./webpack.base.conf") 4 | 5 | module.exports = merge(baseWebpackConfig, { 6 | entry: path.resolve(__dirname, '../src/index.js'), 7 | output: { 8 | path: path.resolve(__dirname, "../dist"), 9 | publicPath: "dist/", 10 | libraryTarget: 'umd', 11 | umdNamedDefine: true, 12 | filename: 'index.min.js' 13 | }, 14 | devtool: '' 15 | }) 16 | -------------------------------------------------------------------------------- /build/webpack.server.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const merge = require('webpack-merge') 3 | const baseWebpackConfig = require('./webpack.base.conf') 4 | 5 | module.exports = merge(baseWebpackConfig, { 6 | entry: path.resolve(__dirname, '../dev/src/index.js'), 7 | output: { 8 | path: path.resolve(__dirname, '../dev/server/dist'), 9 | publicPath: 'dist/', 10 | filename: 'build.js' 11 | }, 12 | devServer: { 13 | contentBase: path.resolve(__dirname, '../dev'), 14 | compress: true, 15 | port: 3000, 16 | stats: { 17 | assets: true, 18 | children: false, 19 | chunks: false, 20 | hash: true, 21 | modules: false, 22 | publicPath: true, 23 | timings: false, 24 | version: true, 25 | warnings: true, 26 | colors: { 27 | green: '\u001b[32m' 28 | } 29 | } 30 | }, 31 | resolve: { 32 | alias: { 33 | '@': path.join(__dirname, '../dev/src'), 34 | 'vue$': 'vue/dist/vue.esm.js' 35 | }, 36 | extensions: ['*', '.js', '.vue', '.json', '.ts'] 37 | } 38 | }) 39 | -------------------------------------------------------------------------------- /demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/demo.png -------------------------------------------------------------------------------- /dev/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vue Table Dynamic 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /dev/server/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/dev/server/favicon.png -------------------------------------------------------------------------------- /dev/server/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vue Table Dynamic 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /dev/server/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const app = express() 3 | const path = require('path') 4 | 5 | app.use(express.static(path.resolve(__dirname, './'))) 6 | 7 | const server = app.listen('9088', () => { 8 | const host = server.address().address 9 | const port = server.address().port 10 | console.log('访问地址为 http://%s:%s', host, port) 11 | }) 12 | -------------------------------------------------------------------------------- /dev/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /dev/src/assets/css/flex.css: -------------------------------------------------------------------------------- 1 | .dev-flex-box{ 2 | display:-webkit-flex; 3 | display: -moz-flex; 4 | display:-ms-flexbox; /* IE 10 */ 5 | display:-moz-box; 6 | display: flex; 7 | } 8 | .dev-flex-c{ 9 | display:-webkit-flex; 10 | display: -moz-flex; 11 | display:-ms-flexbox; /* IE 10 */ 12 | display:-moz-box; 13 | display: flex; 14 | align-items: center; 15 | -webkit-align-items:center; 16 | -moz-align-items:center; 17 | -ms-align-items:center; 18 | -o-align-items:center; 19 | } 20 | .dev-flex-c-c{ 21 | display: flex; 22 | display:-webkit-flex; 23 | display: -moz-flex; 24 | display:-ms-flexbox; /* IE 10 */ 25 | display:-moz-box; 26 | align-items: center; 27 | -webkit-align-items:center; 28 | -moz-align-items:center; 29 | -ms-align-items:center; 30 | -o-align-items:center; 31 | justify-content: center; 32 | -webkit-justify-content: center; 33 | -moz-justify-content: center; 34 | -ms-justify-content: center; 35 | -o-justify-content: center; 36 | } 37 | .dev-flex-c-b{ 38 | display: flex; 39 | display:-webkit-flex; 40 | display: -moz-flex; 41 | display:-ms-flexbox; /* IE 10 */ 42 | display:-moz-box; 43 | align-items: center; 44 | -webkit-align-items:center; 45 | -moz-align-items:center; 46 | -ms-align-items:center; 47 | -o-align-items:center; 48 | justify-content: space-between; 49 | -webkit-justify-content: space-between; 50 | -moz-justify-content: space-between; 51 | -ms-justify-content: space-between; 52 | -o-justify-content: space-between; 53 | } 54 | .dev-flex-c-s{ 55 | display: flex; 56 | display:-webkit-flex; 57 | display: -moz-flex; 58 | display:-ms-flexbox; /* IE 10 */ 59 | display:-moz-box; 60 | align-items: center; 61 | -webkit-align-items:center; 62 | -moz-align-items:center; 63 | -ms-align-items:center; 64 | -o-align-items:center; 65 | justify-content: flex-start; 66 | -webkit-justify-content: flex-start; 67 | -moz-justify-content: flex-start; 68 | -ms-justify-content: flex-start; 69 | -o-justify-content: flex-start; 70 | } 71 | .dev-flex-c-e{ 72 | display: flex; 73 | display:-webkit-flex; 74 | display: -moz-flex; 75 | display:-ms-flexbox; /* IE 10 */ 76 | display:-moz-box; 77 | align-items: center; 78 | -webkit-align-items:center; 79 | -moz-align-items:center; 80 | -ms-align-items:center; 81 | -o-align-items:center; 82 | justify-content: flex-end; 83 | -webkit-justify-content: flex-end; 84 | -moz-justify-content: flex-end; 85 | -ms-justify-content: flex-end; 86 | -o-justify-content: flex-end; 87 | } 88 | .dev-flex-s{ 89 | display: flex; 90 | display:-webkit-flex; 91 | display: -moz-flex; 92 | display:-ms-flexbox; /* IE 10 */ 93 | display:-moz-box; 94 | align-items: flex-start; 95 | -webkit-align-items:flex-start; 96 | -moz-align-items:flex-start; 97 | -ms-align-items:flex-start; 98 | -o-align-items:flex-start; 99 | } 100 | .dev-flex-s-s{ 101 | display: flex; 102 | display:-webkit-flex; 103 | display: -moz-flex; 104 | display:-ms-flexbox; /* IE 10 */ 105 | display:-moz-box; 106 | align-items: flex-start; 107 | -webkit-align-items:flex-start; 108 | -moz-align-items:flex-start; 109 | -ms-align-items:flex-start; 110 | -o-align-items:flex-start; 111 | justify-content: flex-start; 112 | -webkit-justify-content: flex-start; 113 | -moz-justify-content: flex-start; 114 | -ms-justify-content: flex-start; 115 | -o-justify-content: flex-start; 116 | } 117 | .dev-flex-s-c{ 118 | display: flex; 119 | display:-webkit-flex; 120 | display: -moz-flex; 121 | display:-ms-flexbox; /* IE 10 */ 122 | display:-moz-box; 123 | align-items: flex-start; 124 | -webkit-align-items:flex-start; 125 | -moz-align-items:flex-start; 126 | -ms-align-items:flex-start; 127 | -o-align-items:flex-start; 128 | justify-content: center; 129 | -webkit-justify-content: center; 130 | -moz-justify-content: center; 131 | -ms-justify-content: center; 132 | -o-justify-content: center; 133 | } 134 | .dev-flex-auto{ 135 | -webkit-flex: auto; 136 | -webkit-box-flex: 1; 137 | -ms-flex: auto; 138 | flex: auto; 139 | } 140 | .dev-flex-1{ 141 | -webkit-flex: 1; 142 | -ms-flex: 1; 143 | flex: 1; 144 | } 145 | .dev-flex-dir-column{ 146 | -webkit-flex-direction: column; 147 | -ms-flex-direction: column; 148 | -webkit-box-orient: vertical; 149 | flex-direction: column; 150 | } -------------------------------------------------------------------------------- /dev/src/assets/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/dev/src/assets/image/logo.png -------------------------------------------------------------------------------- /dev/src/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import './assets/css/flex.css' 4 | 5 | // dev 6 | import VueTableDynamic from '../../src/index.js' 7 | // prod 8 | // import VueTableDynamic from '../../' 9 | 10 | Vue.use(VueTableDynamic) 11 | 12 | Vue.config.productionTip = false 13 | 14 | // eslint-disable-next-line no-new 15 | new Vue({ 16 | el: '#app', 17 | render: h => h(App) 18 | }) 19 | -------------------------------------------------------------------------------- /dev/src/views/CustomCell.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 162 | 163 | 265 | 273 | -------------------------------------------------------------------------------- /dev/src/views/CustomStyle.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 373 | 374 | 461 | 462 | -------------------------------------------------------------------------------- /dev/src/views/DefaultStyle.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 372 | 373 | 461 | -------------------------------------------------------------------------------- /dev/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 37 | 38 | -------------------------------------------------------------------------------- /dev/src/views/NavMenu.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 58 | 59 | -------------------------------------------------------------------------------- /dev/src/views/VueButton.vue: -------------------------------------------------------------------------------- 1 | 23 | 47 | 48 | 193 | -------------------------------------------------------------------------------- /dev/src/views/VueSearch.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 138 | 139 | 245 | -------------------------------------------------------------------------------- /dev/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/dev/static/favicon.png -------------------------------------------------------------------------------- /docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/favicon.png -------------------------------------------------------------------------------- /docs/images/basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/basic.png -------------------------------------------------------------------------------- /docs/images/border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/border.png -------------------------------------------------------------------------------- /docs/images/checkbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/checkbox.png -------------------------------------------------------------------------------- /docs/images/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/edit.png -------------------------------------------------------------------------------- /docs/images/filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/filter.png -------------------------------------------------------------------------------- /docs/images/fixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/fixed.png -------------------------------------------------------------------------------- /docs/images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/header.png -------------------------------------------------------------------------------- /docs/images/height.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/height.png -------------------------------------------------------------------------------- /docs/images/heightAndFixed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/heightAndFixed.png -------------------------------------------------------------------------------- /docs/images/highlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/highlight.png -------------------------------------------------------------------------------- /docs/images/pagination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/pagination.png -------------------------------------------------------------------------------- /docs/images/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/search.png -------------------------------------------------------------------------------- /docs/images/sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/sort.png -------------------------------------------------------------------------------- /docs/images/stripe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/stripe.png -------------------------------------------------------------------------------- /docs/images/width.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/docs/images/width.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vue Table Dynamic 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-table-dynamic", 3 | "version": "0.4.10", 4 | "description": "A vue component of dynamic table", 5 | "main": "dist/index.min.js", 6 | "scripts": { 7 | "dev": "node_modules/.bin/webpack-dev-server --config ./build/webpack.dev.conf.js --open --hot", 8 | "build": "webpack -p --hide-modules --config ./build/webpack.prod.conf.js", 9 | "build:doc": "webpack -p --hide-modules --config ./build/webpack.doc.conf.js", 10 | "build:server": "webpack -p --hide-modules --config ./build/webpack.server.conf.js", 11 | "server": "node dev/server/server.js" 12 | }, 13 | "author": "TheoXiong", 14 | "license": "MIT", 15 | "dependencies": { 16 | "lodash.throttle": "^4.1.1", 17 | "lodash.trim": "^4.5.1", 18 | "vue-scrollbar-simple": "0.0.10" 19 | }, 20 | "devDependencies": { 21 | "babel-core": "^6.26.3", 22 | "babel-loader": "^7.1.4", 23 | "babel-plugin-component": "^1.1.1", 24 | "babel-plugin-transform-runtime": "^6.23.0", 25 | "babel-preset-env": "^1.7.0", 26 | "babel-preset-es2015": "^6.24.1", 27 | "babel-preset-stage-0": "^6.24.1", 28 | "babel-register": "^6.26.0", 29 | "babili-webpack-plugin": "^0.1.2", 30 | "cfonts": "^2.1.2", 31 | "chalk": "^2.4.1", 32 | "copy-webpack-plugin": "^4.5.1", 33 | "cross-env": "^5.1.6", 34 | "crypto-random-string": "^3.0.0", 35 | "css-loader": "^0.28.11", 36 | "element-ui": "^2.8.2", 37 | "express": "^4.17.1", 38 | "file-loader": "^1.1.11", 39 | "file-saver": "^2.0.2", 40 | "html-webpack-plugin": "^3.2.0", 41 | "lodash.clonedeep": "^4.5.0", 42 | "mini-css-extract-plugin": "0.4.0", 43 | "multispinner": "^0.2.1", 44 | "node-loader": "^0.6.0", 45 | "node-sass": "^4.13.0", 46 | "raw-loader": "^1.0.0", 47 | "sass": "^1.3.2", 48 | "sass-loader": "^8.0.0", 49 | "style-loader": "^0.21.0", 50 | "table": "^5.4.6", 51 | "url-loader": "^1.0.1", 52 | "vue": "^2.6.0", 53 | "vue-html-loader": "^1.2.4", 54 | "vue-loader": "^15.6.2", 55 | "vue-msgs": "^1.0.6", 56 | "vue-style-loader": "^4.1.0", 57 | "vue-template-compiler": "^2.5.16", 58 | "webpack": "^4.29.4", 59 | "webpack-cli": "^3.0.8", 60 | "webpack-dev-server": "^3.1.4", 61 | "webpack-hot-middleware": "^2.22.2", 62 | "webpack-merge": "^4.1.3" 63 | }, 64 | "keywords": [ 65 | "table", 66 | "dynamic", 67 | "data", 68 | "vue" 69 | ], 70 | "repository": { 71 | "type": "git", 72 | "url": "git+https://github.com/TheoXiong/vue-table-dynamic.git" 73 | }, 74 | "bugs": { 75 | "url": "https://github.com/TheoXiong/vue-table-dynamic/issues" 76 | }, 77 | "homepage": "https://github.com/TheoXiong/vue-table-dynamic#readme" 78 | } 79 | -------------------------------------------------------------------------------- /src/assets/css/flex.css: -------------------------------------------------------------------------------- 1 | .flex-box{ 2 | display:-webkit-flex; 3 | display: -moz-flex; 4 | display:-ms-flexbox; /* IE 10 */ 5 | display:-moz-box; 6 | display: flex; 7 | } 8 | .flex-c{ 9 | display:-webkit-flex; 10 | display: -moz-flex; 11 | display:-ms-flexbox; /* IE 10 */ 12 | display:-moz-box; 13 | display: flex; 14 | align-items: center; 15 | -webkit-align-items:center; 16 | -moz-align-items:center; 17 | -ms-align-items:center; 18 | -o-align-items:center; 19 | } 20 | .flex-c-c{ 21 | display: flex; 22 | display:-webkit-flex; 23 | display: -moz-flex; 24 | display:-ms-flexbox; /* IE 10 */ 25 | display:-moz-box; 26 | align-items: center; 27 | -webkit-align-items:center; 28 | -moz-align-items:center; 29 | -ms-align-items:center; 30 | -o-align-items:center; 31 | justify-content: center; 32 | -webkit-justify-content: center; 33 | -moz-justify-content: center; 34 | -ms-justify-content: center; 35 | -o-justify-content: center; 36 | } 37 | .flex-c-b{ 38 | display: flex; 39 | display:-webkit-flex; 40 | display: -moz-flex; 41 | display:-ms-flexbox; /* IE 10 */ 42 | display:-moz-box; 43 | align-items: center; 44 | -webkit-align-items:center; 45 | -moz-align-items:center; 46 | -ms-align-items:center; 47 | -o-align-items:center; 48 | justify-content: space-between; 49 | -webkit-justify-content: space-between; 50 | -moz-justify-content: space-between; 51 | -ms-justify-content: space-between; 52 | -o-justify-content: space-between; 53 | } 54 | .flex-c-s{ 55 | display: flex; 56 | display:-webkit-flex; 57 | display: -moz-flex; 58 | display:-ms-flexbox; /* IE 10 */ 59 | display:-moz-box; 60 | align-items: center; 61 | -webkit-align-items:center; 62 | -moz-align-items:center; 63 | -ms-align-items:center; 64 | -o-align-items:center; 65 | justify-content: flex-start; 66 | -webkit-justify-content: flex-start; 67 | -moz-justify-content: flex-start; 68 | -ms-justify-content: flex-start; 69 | -o-justify-content: flex-start; 70 | } 71 | .flex-c-e{ 72 | display: flex; 73 | display:-webkit-flex; 74 | display: -moz-flex; 75 | display:-ms-flexbox; /* IE 10 */ 76 | display:-moz-box; 77 | align-items: center; 78 | -webkit-align-items:center; 79 | -moz-align-items:center; 80 | -ms-align-items:center; 81 | -o-align-items:center; 82 | justify-content: flex-end; 83 | -webkit-justify-content: flex-end; 84 | -moz-justify-content: flex-end; 85 | -ms-justify-content: flex-end; 86 | -o-justify-content: flex-end; 87 | } 88 | .flex-s{ 89 | display: flex; 90 | display:-webkit-flex; 91 | display: -moz-flex; 92 | display:-ms-flexbox; /* IE 10 */ 93 | display:-moz-box; 94 | align-items: flex-start; 95 | -webkit-align-items:flex-start; 96 | -moz-align-items:flex-start; 97 | -ms-align-items:flex-start; 98 | -o-align-items:flex-start; 99 | } 100 | .flex-s-s{ 101 | display: flex; 102 | display:-webkit-flex; 103 | display: -moz-flex; 104 | display:-ms-flexbox; /* IE 10 */ 105 | display:-moz-box; 106 | align-items: flex-start; 107 | -webkit-align-items:flex-start; 108 | -moz-align-items:flex-start; 109 | -ms-align-items:flex-start; 110 | -o-align-items:flex-start; 111 | justify-content: flex-start; 112 | -webkit-justify-content: flex-start; 113 | -moz-justify-content: flex-start; 114 | -ms-justify-content: flex-start; 115 | -o-justify-content: flex-start; 116 | } 117 | .flex-s-c{ 118 | display: flex; 119 | display:-webkit-flex; 120 | display: -moz-flex; 121 | display:-ms-flexbox; /* IE 10 */ 122 | display:-moz-box; 123 | align-items: flex-start; 124 | -webkit-align-items:flex-start; 125 | -moz-align-items:flex-start; 126 | -ms-align-items:flex-start; 127 | -o-align-items:flex-start; 128 | justify-content: center; 129 | -webkit-justify-content: center; 130 | -moz-justify-content: center; 131 | -ms-justify-content: center; 132 | -o-justify-content: center; 133 | } 134 | .flex-auto{ 135 | -webkit-flex: auto; 136 | -webkit-box-flex: 1; 137 | -ms-flex: auto; 138 | flex: auto; 139 | } 140 | .flex-1{ 141 | -webkit-flex: 1; 142 | -ms-flex: 1; 143 | flex: 1; 144 | } 145 | .flex-dir-column{ 146 | -webkit-flex-direction: column; 147 | -ms-flex-direction: column; 148 | -webkit-box-orient: vertical; 149 | flex-direction: column; 150 | } -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "iconfont"; 2 | src: url('iconfont.eot?t=1588160269425'); /* IE9 */ 3 | src: url('iconfont.eot?t=1588160269425#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAQUAAsAAAAACOwAAAPIAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDZAqENINjATYCJAMgCxIABCAFhG0HWxvAB8gekiQVAgl+cMABFYK94uG//Wj3zcz/f9a07SZzIIpm0UTSRDWNoslaoRQ80UQTkbP/q0u/7fgCQErbKUDgKwDbkvHgySoATUC85nWjLQRTuwV4DlcI1NWhaUoDDNZNPUsEgthqsd4BYwHLBKx9r9TI/kFe0H8Yu7zIxwn0mpaJOyit3gx+Cv24QFzU1QPgl7ArFZWhCm3BpUU8g0ZN9/kWAE+934+fkB9+JE2mn3rkQpMCtV/HpiaEs4t3nXYwoBjOC3YVGStAIe4W+q8TgvwKQa/8tuIQ0K9Kytfxocmh+eGpqYnFRb6OJUhzaj0b+g0a2OE8KNJW/uF1GploofJ+cA4WOr6OWYjC13FE4uskIvg6h2j5Oo/o+DaFaPg7cYUMKhgOii+At4i40xiGLiHtmH6PxPDIoBPk3vUo5c7dwMCHsSp2d923yb074G40VkD93Vj1/rkj3Ll7DaRZ7rv3Oe5eDLGeCOjiIfcCsPZOgfZGg5p7J8j3fu5cn0653kM52/uewBv9FfhubM39GPKh53pfV9+5/u7+G72dvWd72oppW7FmXch1cDcw9OHgtyj5cWv/wJeUL/sGlpWIq/ykxMvGLSS/VWKJ+Qe/9uVfENf4bVbjwPGb/daIF/J/7WtOge634Xxz5QqU7D/b2TWzr9dbQOmXdIH/kLqroCrT3LLFzDxuSlxJaDgu3KKqYFfqKV44vulnUeGmpMV2YuqiO/vkM6As/ZJCHz/2/zv9OFTq+/TwnY+TPw8ZS0sh//nzjQ7HNiR3AnCHnsu3+bPgifam/5DeKoou2JrIt4UwQH7N/wLBjXjxZ/yNMye2VO0LrfxXAwv43H41GMW3Jgp0C1bpvA3+A0XDmaKodEct0mhWm0C1MpjMWdOrF39AeNT/IY0TjcsJ1UQESWcKsmpGXwZVQGj02QGtag/0Wua8us8YShClB0suAQjDvkMy6A1kw1qxhfgPGpMmoDUcAvQ6FZG37DMXekADxQcYVtDBY0iXPW5iuliAX7kRq4esAzSu8mhbMbX1FMrLzk2XnNiN6RBn2IfVfMYIItTjQhJ4GrYsD/JSj4Fllq0x5i3PySFF35Qte1wQ4AAKO4DBFMhBxyA6mYcbcZYzAZmf3whTHWI5gFYMlIW3wiibvjmSJ1tuA6xT7240cCnbbYep8jEMwUcRlIcLIkEvZhl5D8RbvM+AyZhsWoesV7kctIg01WdPb3TdQBkbQf/YGilylGiijS5q9EiXvCLVVY35WJgwH8VzxO2w8QEqa6K5C8um6NLdh2wA') format('woff2'), 5 | url('iconfont.woff?t=1588160269425') format('woff'), 6 | url('iconfont.ttf?t=1588160269425') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1588160269425#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family: "iconfont" !important; 12 | font-size: 16px; 13 | font-style: normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .iconup:before { 19 | content: "\e7ee"; 20 | } 21 | 22 | .iconright:before { 23 | content: "\e7ec"; 24 | } 25 | 26 | .iconleft:before { 27 | content: "\efed"; 28 | } 29 | 30 | .icondown:before { 31 | content: "\e7eb"; 32 | } 33 | 34 | .iconsearch:before { 35 | content: "\e8ef"; 36 | } 37 | 38 | .iconcheck:before { 39 | content: "\e7fc"; 40 | } 41 | 42 | .iconminus:before { 43 | content: "\e7fd"; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/src/assets/iconfont/iconfont.eot -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.js: -------------------------------------------------------------------------------- 1 | !function(s){var e,l='',t=(e=document.getElementsByTagName("script"))[e.length-1].getAttribute("data-injectcss");if(t&&!s.__iconfont__svg__cssinject__){s.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(e){console&&console.log(e)}}!function(e){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(e,0);else{var t=function(){document.removeEventListener("DOMContentLoaded",t,!1),e()};document.addEventListener("DOMContentLoaded",t,!1)}else document.attachEvent&&(o=e,n=s.document,i=!1,(l=function(){try{n.documentElement.doScroll("left")}catch(e){return void setTimeout(l,50)}c()})(),n.onreadystatechange=function(){"complete"==n.readyState&&(n.onreadystatechange=null,c())});function c(){i||(i=!0,o())}var o,n,i,l}(function(){var e,t,c,o,n,i;(e=document.createElement("div")).innerHTML=l,l=null,(t=e.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",c=t,(o=document.body).firstChild?(n=c,(i=o.firstChild).parentNode.insertBefore(n,i)):o.appendChild(c))})}(window); -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1550311", 3 | "name": "DataTable", 4 | "font_family": "iconfont", 5 | "css_prefix_text": "icon", 6 | "description": "", 7 | "glyphs": [ 8 | { 9 | "icon_id": "4767013", 10 | "name": "up", 11 | "font_class": "up", 12 | "unicode": "e7ee", 13 | "unicode_decimal": 59374 14 | }, 15 | { 16 | "icon_id": "4767011", 17 | "name": "right", 18 | "font_class": "right", 19 | "unicode": "e7ec", 20 | "unicode_decimal": 59372 21 | }, 22 | { 23 | "icon_id": "4767012", 24 | "name": "left", 25 | "font_class": "left", 26 | "unicode": "efed", 27 | "unicode_decimal": 61421 28 | }, 29 | { 30 | "icon_id": "4767014", 31 | "name": "down", 32 | "font_class": "down", 33 | "unicode": "e7eb", 34 | "unicode_decimal": 59371 35 | }, 36 | { 37 | "icon_id": "6598347", 38 | "name": "search", 39 | "font_class": "search", 40 | "unicode": "e8ef", 41 | "unicode_decimal": 59631 42 | }, 43 | { 44 | "icon_id": "4767093", 45 | "name": "check", 46 | "font_class": "check", 47 | "unicode": "e7fc", 48 | "unicode_decimal": 59388 49 | }, 50 | { 51 | "icon_id": "4767099", 52 | "name": "minus", 53 | "font_class": "minus", 54 | "unicode": "e7fd", 55 | "unicode_decimal": 59389 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/src/assets/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/src/assets/iconfont/iconfont.woff -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheoXiong/vue-table-dynamic/ba4c998bf0fbc27a947ec8e84a1ca2f20782b7fa/src/assets/iconfont/iconfont.woff2 -------------------------------------------------------------------------------- /src/components/FilterPanel.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 163 | 164 | -------------------------------------------------------------------------------- /src/components/ResizeDetector.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 81 | 82 | -------------------------------------------------------------------------------- /src/components/VueInput.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 136 | 137 | 240 | -------------------------------------------------------------------------------- /src/components/VuePagination.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 235 | 236 | -------------------------------------------------------------------------------- /src/components/scrollbar/HorizontalScrollbar.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 179 | 180 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import VueTableDynamic from './components/VueTableDynamic.vue' 2 | 3 | VueTableDynamic.install = function (Vue, options) { 4 | Vue.component('VueTableDynamic', VueTableDynamic) 5 | } 6 | 7 | export default VueTableDynamic 8 | -------------------------------------------------------------------------------- /src/utils/array.js: -------------------------------------------------------------------------------- 1 | // 非空数组 2 | export const unemptyArray = arr => { 3 | return Array.isArray(arr) && arr.length > 0 4 | } 5 | 6 | // 二维矩阵 7 | export const is2DMatrix = matrix => { 8 | if (unemptyArray(matrix) && unemptyArray(matrix[0])) { 9 | let column = matrix[0].length 10 | return matrix.every(item => { 11 | return unemptyArray(item) && item.length === column 12 | }) 13 | } 14 | return false 15 | } -------------------------------------------------------------------------------- /src/utils/clickoutside.js: -------------------------------------------------------------------------------- 1 | const on = (function () { 2 | if (document.addEventListener) { 3 | return function (element, event, handler) { 4 | if (element && event && handler) { 5 | element.addEventListener(event, handler, false) 6 | } 7 | } 8 | } else { 9 | return function (element, event, handler) { 10 | if (element && event && handler) { 11 | element.attachEvent('on' + event, handler) 12 | } 13 | } 14 | } 15 | })() 16 | 17 | const off = (function () { 18 | if (document.removeEventListener) { 19 | return function (element, event, handler) { 20 | if (element && event) { 21 | element.removeEventListener(event, handler, false) 22 | } 23 | } 24 | } else { 25 | return function (element, event, handler) { 26 | if (element && event) { 27 | element.detachEvent('on' + event, handler) 28 | } 29 | } 30 | } 31 | })() 32 | 33 | const isArray = obj => { 34 | return Object.prototype.toString.call(obj) === '[object Array]' 35 | } 36 | 37 | const _inner = Symbol('_inner') 38 | const _outer = Symbol('_outer') 39 | const _handler = Symbol('_handler') 40 | const _callback = Symbol('_callback') 41 | 42 | /** 43 | * Call the callback function on click out of element 44 | */ 45 | class ClickOutside { 46 | constructor (inner, outer, callback) { 47 | this[_inner] = inner || [] 48 | this[_outer] = outer || window.document 49 | this[_callback] = typeof callback === 'function' ? callback : () => {} 50 | this[_handler] = function (e) { 51 | try { 52 | if (!isArray(this[_inner]) || !e.target) return 53 | for (let i = 0; i < this[_inner].length; i++) { 54 | if (!this[_inner][i] || !this[_inner][i].contains || this[_inner][i].contains(e.target)) return 55 | } 56 | this[_callback](e.target) 57 | } catch (error) { 58 | throw error 59 | } 60 | }.bind(this) 61 | } 62 | bind () { 63 | on(this[_outer], 'mouseup', this[_handler]) 64 | } 65 | unbind () { 66 | off(this[_outer], 'mouseup', this[_handler]) 67 | } 68 | update (callback) { 69 | typeof callback === 'function' ? this[_callback] = callback : '' 70 | } 71 | } 72 | 73 | export default ClickOutside 74 | -------------------------------------------------------------------------------- /src/utils/display.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Ttile: Toggle display with diffrent mode 3 | * Author: TheoXiong 4 | */ 5 | import { on, off, stop } from './dom.js' 6 | 7 | /** 8 | * Display 9 | * mode: four options, as follow 10 | * mode 1: click-click, show by click and hide by click 11 | * mode 2: click-hover, show by click and hide by hover 12 | * mode 3: hover-click, show by hover and hide by click 13 | * mode 4: hover-hover, show by hover and hide by hover 14 | * mode 5: context-click, show by contextmenu and hide by click(or contextmenu) other 15 | */ 16 | class Display { 17 | /** 18 | * constructor function 19 | * @param {String} mode : four options, see the details above 20 | * @param {Object} isShow : control display by this flag 21 | * @param {Dom Object} activeEle : the target element 22 | * @param {Dom Object} emitEle : the emit element 23 | * @param {Dom Object} document : window.document 24 | */ 25 | constructor (mode, isShow, activeEle, emitEle, document) { 26 | this.mode = mode || 'click-click' 27 | this.activeEle = activeEle || null 28 | this.emitEle = emitEle || null 29 | this.document = document || window.document || null 30 | this.isShow = isShow || { value: false } 31 | 32 | // The event coordinate relative to client 33 | this.activeX = 0 34 | this.activeY = 0 35 | this.deactiveX = 0 36 | this.deactiveY = 0 37 | 38 | this.doOpenBinding = this.doOpen.bind(this) 39 | this.doCloseBinding = this.doClose.bind(this) 40 | this.doToggleBinding = this.doToggle.bind(this) 41 | this.doClickBinding = this.doClick.bind(this) 42 | } 43 | 44 | /** 45 | * bind event handler to element 46 | */ 47 | doBind () { 48 | let activeEle = this.activeEle 49 | let emitEle = this.emitEle 50 | let document = this.document 51 | 52 | if (this.mode === 'click-click') { 53 | on(activeEle, 'click', stop) 54 | on(emitEle, 'click', this.doToggleBinding) 55 | on(document, 'click', this.doClickBinding) 56 | } else if (this.mode === 'click-hover') { 57 | on(activeEle, 'click', stop) 58 | on(emitEle, 'click', this.doOpenBinding) 59 | on(emitEle, 'mouseleave', this.doCloseBinding) 60 | } else if (this.mode === 'hover-click') { 61 | on(emitEle, 'mouseenter', this.doOpenBinding) 62 | on(activeEle, 'click', stop) 63 | on(emitEle, 'click', this.doCloseBinding) 64 | on(document, 'click', this.doClickBinding) 65 | } else if (this.mode === 'hover-hover') { 66 | on(emitEle, 'mouseenter', this.doOpenBinding) 67 | on(emitEle, 'mouseleave', this.doCloseBinding) 68 | } else if (this.mode === 'context-click') { 69 | on(emitEle, 'contextmenu', this.doOpenBinding) 70 | on(document, 'click', this.doClickBinding) 71 | on(document, 'contextmenu', this.doClickBinding) 72 | } 73 | } 74 | 75 | /** 76 | * unbind event handler from element 77 | */ 78 | unBind () { 79 | let activeEle = this.activeEle 80 | let emitEle = this.emitEle 81 | let document = this.document 82 | 83 | if (this.mode === 'click-click') { 84 | off(activeEle, 'click', stop) 85 | off(emitEle, 'click', this.doToggleBinding) 86 | off(document, 'click', this.doClickBinding) 87 | } else if (this.mode === 'click-hover') { 88 | off(activeEle, 'click', stop) 89 | off(emitEle, 'click', this.doOpenBinding) 90 | off(emitEle, 'mouseleave', this.doCloseBinding) 91 | } else if (this.mode === 'hover-click') { 92 | off(emitEle, 'mouseenter', this.doOpenBinding) 93 | off(activeEle, 'click', stop) 94 | off(emitEle, 'click', this.doCloseBinding) 95 | off(document, 'click', this.doClickBinding) 96 | } else if (this.mode === 'hover-hover') { 97 | off(emitEle, 'mouseenter', this.doOpenBinding) 98 | off(emitEle, 'mouseleave', this.doCloseBinding) 99 | } else if (this.mode === 'context-click') { 100 | off(emitEle, 'contextmenu', this.doOpenBinding) 101 | off(document, 'click', this.doClickBinding) 102 | off(document, 'contextmenu', this.doClickBinding) 103 | } 104 | } 105 | doOpen (e) { 106 | if (e) { 107 | this.activeX = e.clientX || 0 108 | this.activeY = e.clientY || 0 109 | } 110 | this.isShow.value = true 111 | } 112 | doClose (e) { 113 | if (e) { 114 | this.deactiveX = e.clientX || 0 115 | this.deactiveY = e.clientY || 0 116 | } 117 | this.isShow.value = false 118 | } 119 | doToggle (e) { 120 | if (!this.isShow.value && e) { 121 | this.activeX = e.clientX || 0 122 | this.activeY = e.clientY || 0 123 | } else if (this.isShow.value && e) { 124 | this.deactiveX = e.clientX || 0 125 | this.deactiveY = e.clientY || 0 126 | } 127 | this.isShow.value = !(this.isShow.value) 128 | } 129 | doClick (e) { 130 | try { 131 | if (!this.activeEle || 132 | !this.emitEle || 133 | !this.activeEle.contains || 134 | !this.emitEle.contains || 135 | this.activeEle.contains(e.target) || 136 | this.emitEle.contains(e.target)) { 137 | return 138 | } 139 | 140 | if (this.isShow.value) { 141 | if (e) { 142 | this.deactiveX = e.clientX || 0 143 | this.deactiveY = e.clientY || 0 144 | } 145 | this.isShow.value = false 146 | } 147 | } catch (error) { 148 | throw error 149 | } 150 | } 151 | 152 | clear () { 153 | this.mode = '' 154 | this.activeEle = null 155 | this.emitEle = null 156 | this.document = null 157 | this.isShow = null 158 | this.doOpenBinding = null 159 | this.doCloseBinding = null 160 | this.doToggleBinding = null 161 | this.doClickBinding = null 162 | } 163 | } 164 | 165 | export default Display 166 | -------------------------------------------------------------------------------- /src/utils/dom.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Ttile: Simplify dom api 3 | * Author: TheoXiong 4 | */ 5 | 6 | export function on (element, event, handler) { 7 | if (element && event && handler) { 8 | if (typeof element.addEventListener === 'function' && 9 | typeof handler === 'function' && 10 | typeof event === 'string') { 11 | element.addEventListener(event, handler, false) 12 | } 13 | } 14 | } 15 | 16 | export function off (element, event, handler) { 17 | if (element && event && handler) { 18 | if (typeof element.removeEventListener === 'function' && 19 | typeof handler === 'function' && 20 | typeof event === 'string') { 21 | element.removeEventListener(event, handler, false) 22 | } 23 | } 24 | } 25 | 26 | export function stop (e) { 27 | if (e && typeof e.stopPropagation === 'function') { 28 | e.stopPropagation() 29 | } 30 | } 31 | 32 | export function click (element) { 33 | if (element && typeof element.click === 'function') { 34 | element.click() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/utils/unique.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Unique ID Generator 3 | * TheoXiong 4 | */ 5 | 6 | const now = () => { 7 | let current = Date.now() 8 | let last = now.last || current 9 | now.last = current > last ? current : last + 1 10 | return now.last 11 | } 12 | 13 | const unique = (prefix = '', suffix = '') => { 14 | return prefix + now().toString(16) + suffix 15 | } 16 | 17 | export { 18 | unique 19 | } -------------------------------------------------------------------------------- /src/utils/util.js: -------------------------------------------------------------------------------- 1 | export const toFixed = (num, s) => { 2 | let times = Math.pow(10, s) 3 | let des = num * times + (num >= 0 || -1) * 0.5 4 | return parseInt(des, 10) / times 5 | } 6 | 7 | export const isPercentage = (p) => { 8 | return (typeof p === 'string') && /^\d+(\.\d+)?%$/.test(p) 9 | } --------------------------------------------------------------------------------