Commit 8657d1e91433c9104dd0fa5377c4c690c4a36978
1 parent
cda4e04e
Exists in
master
and in
2 other branches
feat: 新增Table组件
Showing
9 changed files
with
414 additions
and
6 deletions
Show diff stats
examples/router/routes.js
| ... | ... | @@ -57,6 +57,17 @@ const _components = [ |
| 57 | 57 | ], |
| 58 | 58 | }, |
| 59 | 59 | { |
| 60 | + group: '展示组件', | |
| 61 | + children: [ | |
| 62 | + { | |
| 63 | + path: 'table', | |
| 64 | + name: 'table', | |
| 65 | + meta: { title: 'Table 表格' }, | |
| 66 | + component: () => import('@/views/docs/component/table.md'), | |
| 67 | + }, | |
| 68 | + ], | |
| 69 | + }, | |
| 70 | + { | |
| 60 | 71 | group: '方案组件', |
| 61 | 72 | children: [ |
| 62 | 73 | { | ... | ... |
examples/views/docs/component/form.md
| ... | ... | @@ -115,9 +115,9 @@ export default { |
| 115 | 115 | ```html |
| 116 | 116 | <template> |
| 117 | 117 | <z-form label-width="100px" size="mini" span="8"> |
| 118 | - <z-form-item label="户主姓名" v-model="model.name" required></z-form-item> | |
| 119 | - <z-form-item label="年龄" v-model="model.age"></z-form-item> | |
| 120 | - <z-form-item label="性别" v-model="model.gender"></z-form-item> | |
| 118 | + <z-form-item label="户主姓名" :value="model.name" required></z-form-item> | |
| 119 | + <z-form-item label="年龄" :value="model.age"></z-form-item> | |
| 120 | + <z-form-item label="性别" :value="model.gender"></z-form-item> | |
| 121 | 121 | <z-form-item label="详细地址" span="24"> |
| 122 | 122 | <el-tag size="mini" type="info">{{ model.address }}</el-tag> |
| 123 | 123 | <template #label> | ... | ... |
examples/views/docs/component/select.md
| ... | ... | @@ -144,6 +144,37 @@ export default { |
| 144 | 144 | |
| 145 | 145 | </div> |
| 146 | 146 | |
| 147 | +## 绑定值参数名 | |
| 148 | + | |
| 149 | +可以根据不同业务数据的需要,自定义下拉框的绑定值参数名。 | |
| 150 | + | |
| 151 | +::: snippet `labelKey`设置下拉框显示的文本,默认**label**;`valueKey`设置下拉框选项对应的值,默认**value**。 | |
| 152 | + | |
| 153 | +```html | |
| 154 | +<template> | |
| 155 | + <div> | |
| 156 | + <z-select v-model="model" :options="options" label-key="name" value-key="code"></z-select> | |
| 157 | + <span>{{ model }}</span> | |
| 158 | + </div> | |
| 159 | +</template> | |
| 160 | + | |
| 161 | +<script> | |
| 162 | +export default { | |
| 163 | + data() { | |
| 164 | + return { | |
| 165 | + model: '', | |
| 166 | + options: [ | |
| 167 | + { name: '单车', code: 'VAN' }, | |
| 168 | + { name: '牵引车', code: 'TRACTOR' } | |
| 169 | + ], | |
| 170 | + } | |
| 171 | + } | |
| 172 | +} | |
| 173 | +</script> | |
| 174 | +``` | |
| 175 | + | |
| 176 | +::: | |
| 177 | + | |
| 147 | 178 | ## 设置多选 |
| 148 | 179 | |
| 149 | 180 | 多选时支持字符串或数组类型的值 |
| ... | ... | @@ -242,7 +273,7 @@ export default { |
| 242 | 273 | |
| 243 | 274 | ## 远程加载 |
| 244 | 275 | |
| 245 | -可以通过设置`queryApi`自定义查询接口,也可以设置`url`及`http`进行内置格式的查询,其中`http`可在组件库全局配置 | |
| 276 | +可以通过设置`queryApi`自定义查询接口,必须是返回`Promise`的`Function`类型,Promise的返回值为`Array`类型,格式需要与`lebalKey`、`valueKey`项对应。 | |
| 246 | 277 | |
| 247 | 278 | <div class="code-snippet-box"> |
| 248 | 279 | |
| ... | ... | @@ -269,6 +300,9 @@ export default { |
| 269 | 300 | }, |
| 270 | 301 | methods: { |
| 271 | 302 | queryAPI(val) { |
| 303 | + // 正式项目使用时,可以返回axios的实例,如: | |
| 304 | + // return this.$axios({ url: 'xxx', params: { foo: 'bar' } }).then(response => ({ result: response.result })); | |
| 305 | + // 此处的Promise是模拟接口 | |
| 272 | 306 | return new Promise(resolve => { |
| 273 | 307 | setTimeout(() => { |
| 274 | 308 | resolve({ |
| ... | ... | @@ -375,6 +409,52 @@ export default { |
| 375 | 409 | |
| 376 | 410 | </div> |
| 377 | 411 | |
| 412 | +## 搜索前校验 | |
| 413 | + | |
| 414 | +可以通过设置`before-query`设置搜索之前的钩子,若为返回值为**false**则不执行搜索。 | |
| 415 | + | |
| 416 | +::: snippet 一般用于搜索前的业务逻辑校验 | |
| 417 | + | |
| 418 | +```html | |
| 419 | +<template> | |
| 420 | + <z-select v-model="model" :options="options" :queryApi="queryAPI" :before-query="beforeQuery"></z-select> | |
| 421 | +</template> | |
| 422 | + | |
| 423 | +<script> | |
| 424 | +export default { | |
| 425 | + data() { | |
| 426 | + return { | |
| 427 | + model: '', | |
| 428 | + options: [ | |
| 429 | + { label: '王七', value: 'wq' }, | |
| 430 | + { label: '陈拾', value: 'cs' }, | |
| 431 | + ] | |
| 432 | + } | |
| 433 | + }, | |
| 434 | + methods: { | |
| 435 | + beforeQuery(query) { | |
| 436 | + console.log(query.length); | |
| 437 | + return query.length > 2; | |
| 438 | + }, | |
| 439 | + queryAPI(val) { | |
| 440 | + return new Promise(resolve => { | |
| 441 | + setTimeout(() => { | |
| 442 | + resolve({ | |
| 443 | + result: [ | |
| 444 | + { label: '王五', value: 'ww' }, | |
| 445 | + { label: '赵六', value: 'zl' }, | |
| 446 | + ] | |
| 447 | + }); | |
| 448 | + }, 1000); | |
| 449 | + }); | |
| 450 | + }, | |
| 451 | + } | |
| 452 | +} | |
| 453 | +</script> | |
| 454 | +``` | |
| 455 | + | |
| 456 | +::: | |
| 457 | + | |
| 378 | 458 | ## API |
| 379 | 459 | |
| 380 | 460 | ## Attribute 属性 |
| ... | ... | @@ -384,8 +464,8 @@ export default { |
| 384 | 464 | value | 值 | String, Number, Boolean, Object, Array | - | - |
| 385 | 465 | placeholder | 占位符 | String | - | 请选择 |
| 386 | 466 | options | 选项列表 | Array | - | [] |
| 387 | -labelKey | 标签字段名 | String | - | name | |
| 388 | -valueKey | 值字段名 | String | - | code | |
| 467 | +labelKey | 标签字段名 | String | - | label | |
| 468 | +valueKey | 值字段名 | String | - | value | |
| 389 | 469 | searchKey | 搜索字段名 | String | - | query |
| 390 | 470 | size | 大小 | String | mini、small、large | mini |
| 391 | 471 | multiple | 多选 | Boolean | - | false | ... | ... |
| ... | ... | @@ -0,0 +1,79 @@ |
| 1 | +# Table 表格 | |
| 2 | + | |
| 3 | +拓展ElementUI的`el-table` | |
| 4 | + | |
| 5 | +## 基础用法 | |
| 6 | + | |
| 7 | +基本参数与ElementUI的`el-table`相同。 | |
| 8 | + | |
| 9 | +::: snippet 支持`size`继承 | |
| 10 | + | |
| 11 | +```html | |
| 12 | +<template> | |
| 13 | + <el-form size="mini"> | |
| 14 | + <z-table :data="tableData" border> | |
| 15 | + <el-table-column prop="name" label="姓名"></el-table-column> | |
| 16 | + <el-table-column prop="age" label="年龄"></el-table-column> | |
| 17 | + <el-table-column prop="gender" label="性别"></el-table-column> | |
| 18 | + </z-table> | |
| 19 | + </el-form> | |
| 20 | +</template> | |
| 21 | + | |
| 22 | +<script> | |
| 23 | +export default { | |
| 24 | + data() { | |
| 25 | + return { | |
| 26 | + tableData: [ | |
| 27 | + { name: '张三', age: '31', gender: '男' }, | |
| 28 | + { name: '李四', age: '27', gender: '女' }, | |
| 29 | + { name: '王五', age: '16', gender: '男' }, | |
| 30 | + ], | |
| 31 | + }; | |
| 32 | + }, | |
| 33 | +} | |
| 34 | +</script> | |
| 35 | +``` | |
| 36 | + | |
| 37 | +::: | |
| 38 | + | |
| 39 | +## 可编辑表格 | |
| 40 | + | |
| 41 | +一般用于表格的静态数据编辑 | |
| 42 | + | |
| 43 | +::: snippet 设置type为`editable`开启可编辑模式 | |
| 44 | + | |
| 45 | +```html | |
| 46 | +<template> | |
| 47 | + <el-form size="mini"> | |
| 48 | + <el-row> | |
| 49 | + <el-col :span="18"> | |
| 50 | + <z-table :value="tableData" type="editable" border :columns="columns"></z-table> | |
| 51 | + </el-col> | |
| 52 | + <el-col :span="6" style="padding-left: 10px"> | |
| 53 | + <pre class="demo-model">{{ tableData }}</pre> | |
| 54 | + </el-col> | |
| 55 | + </el-row> | |
| 56 | + </el-form> | |
| 57 | +</template> | |
| 58 | + | |
| 59 | +<script> | |
| 60 | +export default { | |
| 61 | + data() { | |
| 62 | + return { | |
| 63 | + tableData: [ | |
| 64 | + { name: '张三', age: '31', gender: '男' }, | |
| 65 | + { name: '李四', age: '27', gender: '女' }, | |
| 66 | + { name: '王五', age: '16', gender: '男' }, | |
| 67 | + ], | |
| 68 | + columns: [ | |
| 69 | + { prop: 'name', label: '姓名' }, | |
| 70 | + { type: 'el-input-number', prop: 'age', label: '年龄' }, | |
| 71 | + { prop: 'gender', label: '性别' }, | |
| 72 | + ] | |
| 73 | + }; | |
| 74 | + }, | |
| 75 | +} | |
| 76 | +</script> | |
| 77 | +``` | |
| 78 | + | |
| 79 | +::: | |
| 0 | 80 | \ No newline at end of file | ... | ... |
packages/index.js
| 1 | 1 | import Vue from 'vue'; |
| 2 | +import ZTable from './table/index'; | |
| 3 | +import ZTableNormal from './table/normal'; | |
| 4 | +import ZTableEditable from './table/editable'; | |
| 2 | 5 | import ElImageViewer from './upload/image-viewer'; |
| 3 | 6 | |
| 4 | 7 | let components = {}; |
| ... | ... | @@ -11,6 +14,9 @@ componentsFiles.keys().forEach(path => { |
| 11 | 14 | |
| 12 | 15 | // 给组件库配置install方法 |
| 13 | 16 | const install = function(Vue, opts = {}) { |
| 17 | + components[ZTable.name] = ZTable; | |
| 18 | + components[ZTableNormal.name] = ZTableNormal; | |
| 19 | + components[ZTableEditable.name] = ZTableEditable; | |
| 14 | 20 | Object.values(components).forEach(component => { |
| 15 | 21 | // 组件前缀 |
| 16 | 22 | const prefix = opts.name || 'z'; | ... | ... |
| ... | ... | @@ -0,0 +1,106 @@ |
| 1 | +<template> | |
| 2 | + <el-table :data="tableData | tableDataFilter" :size="tableSize" v-bind="_props" @header-click="onHeaderClick" @cell-click="onCellClick" @cell-dblclick="onCellDblclick"> | |
| 3 | + <template v-for="(item, index) in columns"> | |
| 4 | + <el-table-column v-bind="item" :key="index"> | |
| 5 | + <template #default="{ row, column }"> | |
| 6 | + <table-column-cell | |
| 7 | + :editable="row.$editable || (tableEditCell.index === row.$index && tableEditCell.prop === item.prop)" | |
| 8 | + :type="item.type" | |
| 9 | + :value="row[column.property]" | |
| 10 | + @input="onCellInput" | |
| 11 | + ></table-column-cell> | |
| 12 | + </template> | |
| 13 | + </el-table-column> | |
| 14 | + </template> | |
| 15 | + </el-table> | |
| 16 | +</template> | |
| 17 | + | |
| 18 | +<script> | |
| 19 | +import TableNormal from './normal'; | |
| 20 | +import tableProps from './props'; | |
| 21 | +import { cloneDeep, get, set } from '../utils'; | |
| 22 | + | |
| 23 | +export default { | |
| 24 | + name: 'TableEditable', | |
| 25 | + extends: TableNormal, | |
| 26 | + components: { | |
| 27 | + tableColumnCell: { | |
| 28 | + props: { | |
| 29 | + value: [String, Number, Array, Object], | |
| 30 | + type: { type: String, default: 'el-input' }, | |
| 31 | + editable: Boolean, | |
| 32 | + }, | |
| 33 | + watch: { | |
| 34 | + editable(val) { | |
| 35 | + if (val && this.type === 'el-input') { | |
| 36 | + this.$nextTick(() => { | |
| 37 | + this.$children[0].focus(); | |
| 38 | + }); | |
| 39 | + } | |
| 40 | + }, | |
| 41 | + }, | |
| 42 | + render(h) { | |
| 43 | + if (this.editable) { | |
| 44 | + return h(this.type, { | |
| 45 | + props: { value: this.value, size: 'mini' }, | |
| 46 | + on: { | |
| 47 | + input: value => { | |
| 48 | + this.$emit('input', value); | |
| 49 | + }, | |
| 50 | + }, | |
| 51 | + }); | |
| 52 | + } | |
| 53 | + return h('span', this.value); | |
| 54 | + }, | |
| 55 | + }, | |
| 56 | + }, | |
| 57 | + props: { | |
| 58 | + value: { | |
| 59 | + type: Array, | |
| 60 | + default() { | |
| 61 | + return []; | |
| 62 | + }, | |
| 63 | + }, | |
| 64 | + columns: { | |
| 65 | + type: Array, | |
| 66 | + default() { | |
| 67 | + return []; | |
| 68 | + }, | |
| 69 | + }, | |
| 70 | + ...tableProps, | |
| 71 | + }, | |
| 72 | + data() { | |
| 73 | + return { | |
| 74 | + tableData: this.value, | |
| 75 | + tableRowTemplate: { $editable: true }, // 行数据模板 | |
| 76 | + tableEditCell: {}, // 正在编辑的单元格 | |
| 77 | + tableSelection: [], // 表格已选中 | |
| 78 | + }; | |
| 79 | + }, | |
| 80 | + filters: { | |
| 81 | + tableDataFilter(value) { | |
| 82 | + return value.map((item, index) => ({ ...item, $index: index })); | |
| 83 | + }, | |
| 84 | + }, | |
| 85 | + methods: { | |
| 86 | + onHeaderClick() { | |
| 87 | + this.tableEditCell = {}; | |
| 88 | + }, | |
| 89 | + onCellClick(row, column, cell, event) { | |
| 90 | + if (row.$index !== this.tableEditCell.index || column.property !== this.tableEditCell.prop) { | |
| 91 | + this.tableEditCell = {}; | |
| 92 | + } | |
| 93 | + }, | |
| 94 | + onCellDblclick(row, column, cell, event) { | |
| 95 | + this.tableEditCell = { index: row.$index, prop: column.property }; | |
| 96 | + }, | |
| 97 | + onCellInput(value) { | |
| 98 | + const tableData = cloneDeep(this.tableData); | |
| 99 | + const tableRow = tableData[this.tableEditCell.index]; | |
| 100 | + set(tableRow, this.tableEditCell.prop, value); | |
| 101 | + tableData[this.tableEditCell.index] = tableRow; | |
| 102 | + this.$set(this.tableData, this.tableEditCell.index, tableRow); | |
| 103 | + }, | |
| 104 | + }, | |
| 105 | +}; | |
| 106 | +</script> | ... | ... |
| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | +import tableProps from './props'; | |
| 2 | + | |
| 3 | +export default { | |
| 4 | + name: 'Table', | |
| 5 | + props: { | |
| 6 | + type: { | |
| 7 | + type: String, | |
| 8 | + default: 'normal', | |
| 9 | + }, | |
| 10 | + value: { | |
| 11 | + type: Array, | |
| 12 | + default() { | |
| 13 | + return []; | |
| 14 | + }, | |
| 15 | + }, | |
| 16 | + columns: { | |
| 17 | + type: Array, | |
| 18 | + default() { | |
| 19 | + return []; | |
| 20 | + }, | |
| 21 | + }, | |
| 22 | + ...tableProps, | |
| 23 | + }, | |
| 24 | + render(h) { | |
| 25 | + const scopedSlots = this.$scopedSlots; | |
| 26 | + return h(`z-table-${this.type}`, { props: { ...this._props }, scopedSlots }); | |
| 27 | + }, | |
| 28 | +}; | ... | ... |
| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | +<template> | |
| 2 | + <el-table :size="tableSize" v-bind="_props"> | |
| 3 | + <slot></slot> | |
| 4 | + <slot name="append"></slot> | |
| 5 | + </el-table> | |
| 6 | +</template> | |
| 7 | + | |
| 8 | +<script> | |
| 9 | +import tableProps from './props'; | |
| 10 | + | |
| 11 | +export default { | |
| 12 | + name: 'TableNormal', | |
| 13 | + inject: { | |
| 14 | + elForm: { | |
| 15 | + default: '', | |
| 16 | + }, | |
| 17 | + elFormItem: { | |
| 18 | + default: '', | |
| 19 | + }, | |
| 20 | + }, | |
| 21 | + props: { | |
| 22 | + ...tableProps, | |
| 23 | + }, | |
| 24 | + computed: { | |
| 25 | + _elFormItemSize() { | |
| 26 | + return (this.elFormItem || {}).elFormItemSize; | |
| 27 | + }, | |
| 28 | + tableSize() { | |
| 29 | + return this.size || this._elFormItemSize || (this.elForm || {}).size || (this.$ELEMENT || {}).size; | |
| 30 | + }, | |
| 31 | + }, | |
| 32 | + mounted() { | |
| 33 | + // console.log(this); | |
| 34 | + }, | |
| 35 | +}; | |
| 36 | +</script> | ... | ... |
| ... | ... | @@ -0,0 +1,62 @@ |
| 1 | +export default { | |
| 2 | + data: { | |
| 3 | + type: Array, | |
| 4 | + default: function() { | |
| 5 | + return []; | |
| 6 | + }, | |
| 7 | + }, | |
| 8 | + size: String, | |
| 9 | + width: [String, Number], | |
| 10 | + height: [String, Number], | |
| 11 | + maxHeight: [String, Number], | |
| 12 | + fit: { | |
| 13 | + type: Boolean, | |
| 14 | + default: true, | |
| 15 | + }, | |
| 16 | + stripe: Boolean, | |
| 17 | + border: Boolean, | |
| 18 | + rowKey: [String, Function], | |
| 19 | + context: {}, | |
| 20 | + showHeader: { | |
| 21 | + type: Boolean, | |
| 22 | + default: true, | |
| 23 | + }, | |
| 24 | + showSummary: Boolean, | |
| 25 | + sumText: String, | |
| 26 | + summaryMethod: Function, | |
| 27 | + rowClassName: [String, Function], | |
| 28 | + rowStyle: [Object, Function], | |
| 29 | + cellClassName: [String, Function], | |
| 30 | + cellStyle: [Object, Function], | |
| 31 | + headerRowClassName: [String, Function], | |
| 32 | + headerRowStyle: [Object, Function], | |
| 33 | + headerCellClassName: [String, Function], | |
| 34 | + headerCellStyle: [Object, Function], | |
| 35 | + highlightCurrentRow: Boolean, | |
| 36 | + currentRowKey: [String, Number], | |
| 37 | + emptyText: String, | |
| 38 | + expandRowKeys: Array, | |
| 39 | + defaultExpandAll: Boolean, | |
| 40 | + defaultSort: Object, | |
| 41 | + tooltipEffect: String, | |
| 42 | + spanMethod: Function, | |
| 43 | + selectOnIndeterminate: { | |
| 44 | + type: Boolean, | |
| 45 | + default: true, | |
| 46 | + }, | |
| 47 | + indent: { | |
| 48 | + type: Number, | |
| 49 | + default: 16, | |
| 50 | + }, | |
| 51 | + treeProps: { | |
| 52 | + type: Object, | |
| 53 | + default() { | |
| 54 | + return { | |
| 55 | + hasChildren: 'hasChildren', | |
| 56 | + children: 'children', | |
| 57 | + }; | |
| 58 | + }, | |
| 59 | + }, | |
| 60 | + lazy: Boolean, | |
| 61 | + load: Function, | |
| 62 | +}; | ... | ... |