Commit 8a0c5cbbbb7f57aef07082cb34388ecdee7360ee
1 parent
4ae177c1
Exists in
master
and in
2 other branches
feat: 修改SchemaPage组件支持抽屉
Showing
3 changed files
with
207 additions
and
31 deletions
Show diff stats
examples/views/docs/component/schema-page.md
| @@ -365,7 +365,7 @@ export default { | @@ -365,7 +365,7 @@ export default { | ||
| 365 | 365 | ||
| 366 | ::: | 366 | ::: |
| 367 | 367 | ||
| 368 | -## 弹窗类型 | 368 | +## 自定义弹窗 |
| 369 | 369 | ||
| 370 | 除了本组件内置的`new`、`edit`、`detail`三种弹出框模式之外,还可以通过任意插槽打开任意自定义弹出框。也支持重新定义原有的三种弹框,同时也需要重新自定义表单校验和提交等逻辑。 | 370 | 除了本组件内置的`new`、`edit`、`detail`三种弹出框模式之外,还可以通过任意插槽打开任意自定义弹出框。也支持重新定义原有的三种弹框,同时也需要重新自定义表单校验和提交等逻辑。 |
| 371 | 371 | ||
| @@ -485,6 +485,115 @@ export default { | @@ -485,6 +485,115 @@ export default { | ||
| 485 | 485 | ||
| 486 | ::: | 486 | ::: |
| 487 | 487 | ||
| 488 | +## 弹窗类型 | ||
| 489 | + | ||
| 490 | +弹出框组件默认为`el-dialog`,使用自定义弹窗时,可以设置为`el-drawer`。 | ||
| 491 | + | ||
| 492 | +::: snippet 通过`openDialog`的config参数配置`is: 'el-drawer'`即可改为抽屉式弹窗,`dialog-xxx-footer`可以设置抽屉固定的footer | ||
| 493 | + | ||
| 494 | +```html | ||
| 495 | +<template> | ||
| 496 | + <z-schema-page :schema="schema" :value-table.sync="tableModel"> | ||
| 497 | + <template #action-button="{ size, openDialog }"> | ||
| 498 | + <el-button :size="size" @click="openDialog('bill', '账单', { is: 'el-drawer', width: '70%' })">账单</el-button> | ||
| 499 | + </template> | ||
| 500 | + <template #operation-button="{ openDialog, row }"> | ||
| 501 | + <el-button type="text" @click="openDialog('load', `配置-${row.name}`, { is: 'el-drawer', width: '400px' })">配载</el-button> | ||
| 502 | + </template> | ||
| 503 | + <template #table-cell-name="{ value, row, openDetail }"> | ||
| 504 | + <el-link type="primary" @click="openDetail(row)">{{ value }}</el-link> | ||
| 505 | + </template> | ||
| 506 | + <template #dialog-bill="{ closeDialog }"> | ||
| 507 | + <div style="background: #f5f5f5; height: 90vh; display: flex; align-items: center; justify-content: center">一段很长的内容</div> | ||
| 508 | + <z-schema-table v-model="billData" :schema="billSchema"></z-schema-table> | ||
| 509 | + </template> | ||
| 510 | + <template #dialog-bill-footer="{ closeDialog }"> | ||
| 511 | + <el-button plain @click="closeDialog">关闭</el-button> | ||
| 512 | + </template> | ||
| 513 | + <template #dialog-load="{ closeDialog }"> | ||
| 514 | + <el-alert title="这里是一段自定义信息" type="error" show-icon style="margin-bottom: 20px"></el-alert> | ||
| 515 | + <z-schema-form v-model="loadModel" :schema="loadSchema"></z-schema-form> | ||
| 516 | + <el-button type="primary" @click="closeDialog">关闭弹出框</el-button> | ||
| 517 | + </template> | ||
| 518 | + </z-schema-page> | ||
| 519 | +</template> | ||
| 520 | + | ||
| 521 | +<script> | ||
| 522 | +export default { | ||
| 523 | + data() { | ||
| 524 | + return { | ||
| 525 | + schema: { | ||
| 526 | + filter: { | ||
| 527 | + items: [ | ||
| 528 | + { is: 'el-input', prop: 'name', label: '姓名' }, | ||
| 529 | + ] | ||
| 530 | + }, | ||
| 531 | + table: { | ||
| 532 | + props: { size: 'mini', border: true } , | ||
| 533 | + items: [ | ||
| 534 | + { prop: 'name', label: '姓名' }, | ||
| 535 | + { prop: 'age', label: '年龄' }, | ||
| 536 | + { prop: 'address', label: '地址' }, | ||
| 537 | + { prop: 'status', label: '状态', render: (value, row, h) => h('el-tag', { props: { size: 'mini', type: 'info' } }, value) }, | ||
| 538 | + ] | ||
| 539 | + }, | ||
| 540 | + form: { | ||
| 541 | + props: { labelWidth: '70px', size: 'small', span: 12 }, | ||
| 542 | + items: [ | ||
| 543 | + { is: 'el-input', prop: 'name', label: '姓名', rules: [{ required: true, message: '请输入姓名' }] }, | ||
| 544 | + { is: 'el-input-number', prop: 'age', label: '年龄', rules: [{ required: true, message: '请输入有效年龄' }] }, | ||
| 545 | + { is: 'el-input', props: { type: 'textarea' }, prop: 'address', label: '住址', span: 24 }, | ||
| 546 | + ] | ||
| 547 | + }, | ||
| 548 | + operation: { width: 120 } | ||
| 549 | + }, | ||
| 550 | + tableModel: [ | ||
| 551 | + { id: '0', name: '李饼', age: 32, address: '地址0', status: '正常' }, | ||
| 552 | + { id: '1', name: '陈拾', age: 20, address: '地址1', status: '正常' }, | ||
| 553 | + { id: '3', name: '王七', age: 26, address: '地址3', status: '正常' }, | ||
| 554 | + { id: '4', name: '崔倍', age: 27, address: '地址4', status: '正常' }, | ||
| 555 | + { id: '5', name: '孙豹', age: 38, address: '地址5', status: '正常' }, | ||
| 556 | + ], | ||
| 557 | + loadModel: { | ||
| 558 | + name: '', | ||
| 559 | + count: 1, | ||
| 560 | + packages: [] | ||
| 561 | + }, | ||
| 562 | + loadSchema: { | ||
| 563 | + props: { 'label-width': '70px', size: 'small' }, | ||
| 564 | + items: [ | ||
| 565 | + { is: 'el-input', prop: 'name', label: '名称' }, | ||
| 566 | + { is: 'el-input-number', prop: 'count', label: '数量' }, | ||
| 567 | + { | ||
| 568 | + is: 'el-checkbox-group', | ||
| 569 | + prop: 'packages', | ||
| 570 | + label: '包裹', | ||
| 571 | + children: [ | ||
| 572 | + { is: 'el-checkbox', props: { label: '零食' } }, | ||
| 573 | + { is: 'el-checkbox', props: { label: '手机' } }, | ||
| 574 | + { is: 'el-checkbox', props: { label: '电脑' } }, | ||
| 575 | + ], | ||
| 576 | + }, | ||
| 577 | + ] | ||
| 578 | + }, | ||
| 579 | + billData: [ | ||
| 580 | + { billno: 'B20210401000001', amount: '18750' }, | ||
| 581 | + { billno: 'B20210401000002', amount: '637' }, | ||
| 582 | + ], | ||
| 583 | + billSchema: { | ||
| 584 | + items: [ | ||
| 585 | + { label: '单号', prop: 'billno' }, | ||
| 586 | + { label: '金额', prop: 'amount' }, | ||
| 587 | + ] | ||
| 588 | + } | ||
| 589 | + } | ||
| 590 | + }, | ||
| 591 | +} | ||
| 592 | +</script> | ||
| 593 | +``` | ||
| 594 | + | ||
| 595 | +::: | ||
| 596 | + | ||
| 488 | ## 按钮权限 | 597 | ## 按钮权限 |
| 489 | 598 | ||
| 490 | 本组件不包含自定义业务逻辑,因此配置项不包含权限判断,如果需要按钮的权限判断,可以通过`action`插槽和`operation`插槽将渲染逻辑暴露在视图模板中,然后进行自定义判断。 | 599 | 本组件不包含自定义业务逻辑,因此配置项不包含权限判断,如果需要按钮的权限判断,可以通过`action`插槽和`operation`插槽将渲染逻辑暴露在视图模板中,然后进行自定义判断。 |
packages/schema-page/index.scss
| @@ -96,4 +96,30 @@ | @@ -96,4 +96,30 @@ | ||
| 96 | margin-top: 10px; | 96 | margin-top: 10px; |
| 97 | } | 97 | } |
| 98 | } | 98 | } |
| 99 | +} | ||
| 100 | + | ||
| 101 | +.z-schema-page__drawer { | ||
| 102 | + .el-drawer__header { | ||
| 103 | + padding-bottom: 10px; | ||
| 104 | + margin-bottom: 0; | ||
| 105 | + border-bottom: 1px solid #DCDFE6; | ||
| 106 | + } | ||
| 107 | + .el-drawer__body { | ||
| 108 | + box-sizing: border-box; | ||
| 109 | + height: calc(100vh - 55px); | ||
| 110 | + } | ||
| 111 | + &-content { | ||
| 112 | + padding: 20px; | ||
| 113 | + overflow-y: auto; | ||
| 114 | + } | ||
| 115 | + &-footer { | ||
| 116 | + height: 60px; | ||
| 117 | + display: flex; | ||
| 118 | + align-items: center; | ||
| 119 | + justify-content: center; | ||
| 120 | + box-sizing: border-box; | ||
| 121 | + padding: 10px; | ||
| 122 | + background: #fff; | ||
| 123 | + border-top: 1px solid #DCDFE6; | ||
| 124 | + } | ||
| 99 | } | 125 | } |
| 100 | \ No newline at end of file | 126 | \ No newline at end of file |
packages/schema-page/index.vue
| @@ -75,19 +75,32 @@ | @@ -75,19 +75,32 @@ | ||
| 75 | </slot> | 75 | </slot> |
| 76 | </div> | 76 | </div> |
| 77 | <!-- 弹出框 --> | 77 | <!-- 弹出框 --> |
| 78 | - <el-dialog :title="elDialogTitle" :visible.sync="visible" v-bind="_dialogProps" @update:visible="onVisibleUpdate" @close="onDialogClose" @closed="onDialogClosed"> | 78 | + <el-drawer :visible="_modalComponent === 'el-drawer' && visible" v-bind="_drawerProps" v-on="_modalListeners"> |
| 79 | + <template v-for="item in getSlotKeys('dialog-')" #[item.name]="slotScope"> | ||
| 80 | + <slot :name="item.slot" v-bind="{ ..._slotScope, ...slotScope }"></slot> | ||
| 81 | + </template> | ||
| 82 | + <template v-if="_modalComponent === 'el-drawer' && modalRender"> | ||
| 83 | + <div v-loading="dialogLoading" class="z-schema-page__drawer-content" :style="`height: calc(100vh - ${getSlot(`dialog-${modalType}-footer`) ? 115 : 55}px);`"> | ||
| 84 | + <slot :name="`dialog-${modalType}`" v-bind="_slotScope"></slot> | ||
| 85 | + </div> | ||
| 86 | + <div v-if="getSlot(`dialog-${modalType}-footer`)" class="z-schema-page__drawer-footer"> | ||
| 87 | + <slot :name="`dialog-${modalType}-footer`" v-bind="_slotScope"></slot> | ||
| 88 | + </div> | ||
| 89 | + </template> | ||
| 90 | + </el-drawer> | ||
| 91 | + <el-dialog :visible="_modalComponent === 'el-dialog' && visible" v-bind="_dialogProps" v-on="_modalListeners"> | ||
| 79 | <template v-for="item in getSlotKeys('dialog-')" #[item.name]="slotScope"> | 92 | <template v-for="item in getSlotKeys('dialog-')" #[item.name]="slotScope"> |
| 80 | <slot :name="item.slot" v-bind="{ ..._slotScope, ...slotScope }"></slot> | 93 | <slot :name="item.slot" v-bind="{ ..._slotScope, ...slotScope }"></slot> |
| 81 | </template> | 94 | </template> |
| 82 | <!-- 弹出框内容渲染状态,用于关闭时销毁已渲染的内容 --> | 95 | <!-- 弹出框内容渲染状态,用于关闭时销毁已渲染的内容 --> |
| 83 | - <template v-if="elDialogRender"> | 96 | + <div v-if="_modalComponent === 'el-dialog' && modalRender" v-loading="dialogLoading"> |
| 84 | <!-- 自定义弹出框内容 --> | 97 | <!-- 自定义弹出框内容 --> |
| 85 | - <slot v-if="getSlot(`dialog-${elDialogType}`)" :name="`dialog-${elDialogType}`" v-bind="_slotScope"></slot> | ||
| 86 | - <div v-else v-loading="dialogLoading"> | 98 | + <slot v-if="getSlot(`dialog-${modalType}`)" :name="`dialog-${modalType}`" v-bind="_slotScope"></slot> |
| 99 | + <template v-else> | ||
| 87 | <!-- 新增/修改弹出框内容 --> | 100 | <!-- 新增/修改弹出框内容 --> |
| 88 | - <template v-if="['new', 'edit'].includes(elDialogType)"> | 101 | + <template v-if="['new', 'edit'].includes(modalType)"> |
| 89 | <z-schema-form | 102 | <z-schema-form |
| 90 | - :key="`form-${elDialogType}`" | 103 | + :key="`form-${modalType}`" |
| 91 | ref="form" | 104 | ref="form" |
| 92 | :value="valueForm" | 105 | :value="valueForm" |
| 93 | @input="e => $emit('update:value-form', e)" | 106 | @input="e => $emit('update:value-form', e)" |
| @@ -107,15 +120,15 @@ | @@ -107,15 +120,15 @@ | ||
| 107 | </z-schema-form> | 120 | </z-schema-form> |
| 108 | </template> | 121 | </template> |
| 109 | <!-- 详情弹出框内容 --> | 122 | <!-- 详情弹出框内容 --> |
| 110 | - <template v-else-if="elDialogType === 'detail'"> | 123 | + <template v-else-if="modalType === 'detail'"> |
| 111 | <z-schema-form key="form-detail" ref="form" v-model="detail" :schema="schema.detail || detailSchema"> | 124 | <z-schema-form key="form-detail" ref="form" v-model="detail" :schema="schema.detail || detailSchema"> |
| 112 | <template v-for="item in getSlotKeys('detail-')" #[item.name]="slotScope"> | 125 | <template v-for="item in getSlotKeys('detail-')" #[item.name]="slotScope"> |
| 113 | <slot :name="item.slot" v-bind="{ ..._slotScope, ...slotScope }"></slot> | 126 | <slot :name="item.slot" v-bind="{ ..._slotScope, ...slotScope }"></slot> |
| 114 | </template> | 127 | </template> |
| 115 | </z-schema-form> | 128 | </z-schema-form> |
| 116 | </template> | 129 | </template> |
| 117 | - </div> | ||
| 118 | - </template> | 130 | + </template> |
| 131 | + </div> | ||
| 119 | </el-dialog> | 132 | </el-dialog> |
| 120 | </div> | 133 | </div> |
| 121 | </template> | 134 | </template> |
| @@ -169,10 +182,10 @@ export default { | @@ -169,10 +182,10 @@ export default { | ||
| 169 | layout: get(this.schema, 'pagination.layout') || 'total, sizes, prev, pager, next, jumper', | 182 | layout: get(this.schema, 'pagination.layout') || 'total, sizes, prev, pager, next, jumper', |
| 170 | total: get(this.schema, 'pagination.total') || 0, | 183 | total: get(this.schema, 'pagination.total') || 0, |
| 171 | visible: this.dialogVisible, | 184 | visible: this.dialogVisible, |
| 172 | - elDialogRender: true, | ||
| 173 | - elDialogType: this.dialogType || 'none', | ||
| 174 | - elDialogTitle: this.dialogTitle || '', | ||
| 175 | - elDialogProps: {}, | 185 | + modalRender: true, |
| 186 | + modalType: this.dialogType || 'none', | ||
| 187 | + modalTitle: this.dialogTitle || '', | ||
| 188 | + modalProps: {}, | ||
| 176 | detailSchema: filterout(cloneDeep(this.schema.form), ['is', 'rules']), | 189 | detailSchema: filterout(cloneDeep(this.schema.form), ['is', 'rules']), |
| 177 | detail: this.valueDetail || {}, | 190 | detail: this.valueDetail || {}, |
| 178 | tableData: this.valueTable || [], | 191 | tableData: this.valueTable || [], |
| @@ -201,15 +214,15 @@ export default { | @@ -201,15 +214,15 @@ export default { | ||
| 201 | this.$emit('update:dialog-visible', val); | 214 | this.$emit('update:dialog-visible', val); |
| 202 | }, | 215 | }, |
| 203 | dialogType(val) { | 216 | dialogType(val) { |
| 204 | - this.elDialogType = val; | 217 | + this.modalType = val; |
| 205 | }, | 218 | }, |
| 206 | - elDialogType(val) { | 219 | + modalType(val) { |
| 207 | this.$emit('update:dialog-type', val); | 220 | this.$emit('update:dialog-type', val); |
| 208 | }, | 221 | }, |
| 209 | dialogTitle(val) { | 222 | dialogTitle(val) { |
| 210 | - this.elDialogTitle = val; | 223 | + this.modalTitle = val; |
| 211 | }, | 224 | }, |
| 212 | - elDialogTitle(val) { | 225 | + modalTitle(val) { |
| 213 | this.$emit('update:dialog-title', val); | 226 | this.$emit('update:dialog-title', val); |
| 214 | }, | 227 | }, |
| 215 | valueTable(val) { | 228 | valueTable(val) { |
| @@ -238,13 +251,41 @@ export default { | @@ -238,13 +251,41 @@ export default { | ||
| 238 | }, defaultScope); | 251 | }, defaultScope); |
| 239 | }, | 252 | }, |
| 240 | _dialogProps() { | 253 | _dialogProps() { |
| 254 | + const { is, ...other } = this.modalProps || {}; | ||
| 241 | return { | 255 | return { |
| 242 | - 'destroy-on-close': true, | ||
| 243 | - 'append-to-body': true, | 256 | + title: this.modalTitle, |
| 244 | 'lock-scroll': false, | 257 | 'lock-scroll': false, |
| 258 | + 'append-to-body': true, | ||
| 259 | + 'destroy-on-close': true, | ||
| 245 | 'close-on-click-modal': false, | 260 | 'close-on-click-modal': false, |
| 246 | ...(this.schema.dialog || {}), | 261 | ...(this.schema.dialog || {}), |
| 247 | - ...this.elDialogProps, | 262 | + ...other, |
| 263 | + }; | ||
| 264 | + }, | ||
| 265 | + _drawerProps() { | ||
| 266 | + const { is, ...other } = this.modalProps || {}; | ||
| 267 | + return { | ||
| 268 | + title: this.modalTitle, | ||
| 269 | + size: '50%', | ||
| 270 | + 'append-to-body': true, | ||
| 271 | + 'destroy-on-close': true, | ||
| 272 | + 'close-on-click-modal': false, | ||
| 273 | + 'custom-class': 'z-schema-page__drawer', | ||
| 274 | + ...(this.schema.drawer || {}), | ||
| 275 | + ...other, | ||
| 276 | + }; | ||
| 277 | + }, | ||
| 278 | + _modalComponent() { | ||
| 279 | + if (this.modalProps.is === 'el-drawer') { | ||
| 280 | + return 'el-drawer'; | ||
| 281 | + } | ||
| 282 | + return 'el-dialog'; | ||
| 283 | + }, | ||
| 284 | + _modalListeners() { | ||
| 285 | + return { | ||
| 286 | + 'update:visible': this.onVisibleUpdate, | ||
| 287 | + close: this.onDialogClose, | ||
| 288 | + closed: this.onDialogClosed, | ||
| 248 | }; | 289 | }; |
| 249 | }, | 290 | }, |
| 250 | }, | 291 | }, |
| @@ -300,12 +341,12 @@ export default { | @@ -300,12 +341,12 @@ export default { | ||
| 300 | } else { | 341 | } else { |
| 301 | this.submitting = true; | 342 | this.submitting = true; |
| 302 | let submitAPI = this.apiSubmit || this.emptyPromise; | 343 | let submitAPI = this.apiSubmit || this.emptyPromise; |
| 303 | - if (this.elDialogType === 'new') { | 344 | + if (this.modalType === 'new') { |
| 304 | submitAPI = this.apiNew || this.apiSubmit || this.emptyPromise; | 345 | submitAPI = this.apiNew || this.apiSubmit || this.emptyPromise; |
| 305 | - } else if (this.elDialogType === 'edit') { | 346 | + } else if (this.modalType === 'edit') { |
| 306 | submitAPI = this.apiEdit || this.apiSubmit || this.emptyPromise; | 347 | submitAPI = this.apiEdit || this.apiSubmit || this.emptyPromise; |
| 307 | } | 348 | } |
| 308 | - submitAPI(this.valueForm, { type: this.elDialogType }) | 349 | + submitAPI(this.valueForm, { type: this.modalType }) |
| 309 | .then(() => { | 350 | .then(() => { |
| 310 | if (this.$listeners['submit-success']) { | 351 | if (this.$listeners['submit-success']) { |
| 311 | this.$emit('submit-success'); | 352 | this.$emit('submit-success'); |
| @@ -365,10 +406,10 @@ export default { | @@ -365,10 +406,10 @@ export default { | ||
| 365 | }, | 406 | }, |
| 366 | // 打开弹出框 | 407 | // 打开弹出框 |
| 367 | openDialog(type, title, config) { | 408 | openDialog(type, title, config) { |
| 368 | - this.elDialogRender = true; | ||
| 369 | - this.elDialogType = type; | ||
| 370 | - this.elDialogTitle = title; | ||
| 371 | - this.elDialogProps = config || {}; | 409 | + this.modalRender = true; |
| 410 | + this.modalType = type; | ||
| 411 | + this.modalTitle = title; | ||
| 412 | + this.modalProps = config || {}; | ||
| 372 | this.visible = true; | 413 | this.visible = true; |
| 373 | this.$emit('dialog-change', type); | 414 | this.$emit('dialog-change', type); |
| 374 | }, | 415 | }, |
| @@ -382,7 +423,7 @@ export default { | @@ -382,7 +423,7 @@ export default { | ||
| 382 | }, | 423 | }, |
| 383 | // 弹出框关闭 | 424 | // 弹出框关闭 |
| 384 | onDialogClose() { | 425 | onDialogClose() { |
| 385 | - this.elDialogType = 'none'; | 426 | + this.modalType = 'none'; |
| 386 | this.$emit('dialog-change', 'none'); | 427 | this.$emit('dialog-change', 'none'); |
| 387 | }, | 428 | }, |
| 388 | // 弹出框关闭动画结束 | 429 | // 弹出框关闭动画结束 |
| @@ -390,8 +431,8 @@ export default { | @@ -390,8 +431,8 @@ export default { | ||
| 390 | if (this.$refs.form) { | 431 | if (this.$refs.form) { |
| 391 | this.$refs.form.resetFields(); | 432 | this.$refs.form.resetFields(); |
| 392 | } | 433 | } |
| 393 | - this.elDialogRender = false; | ||
| 394 | - this.elDialogProps = {}; | 434 | + this.modalRender = false; |
| 435 | + this.modalProps = {}; | ||
| 395 | this.$emit('update:value-form', this.cloneDeep(this.originProps).valueForm); | 436 | this.$emit('update:value-form', this.cloneDeep(this.originProps).valueForm); |
| 396 | this.$emit('update:value-detail', this.cloneDeep(this.originProps).detailValue); | 437 | this.$emit('update:value-detail', this.cloneDeep(this.originProps).detailValue); |
| 397 | }, | 438 | }, |