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 | 365 | |
| 366 | 366 | ::: |
| 367 | 367 | |
| 368 | -## 弹窗类型 | |
| 368 | +## 自定义弹窗 | |
| 369 | 369 | |
| 370 | 370 | 除了本组件内置的`new`、`edit`、`detail`三种弹出框模式之外,还可以通过任意插槽打开任意自定义弹出框。也支持重新定义原有的三种弹框,同时也需要重新自定义表单校验和提交等逻辑。 |
| 371 | 371 | |
| ... | ... | @@ -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 | 599 | 本组件不包含自定义业务逻辑,因此配置项不包含权限判断,如果需要按钮的权限判断,可以通过`action`插槽和`operation`插槽将渲染逻辑暴露在视图模板中,然后进行自定义判断。 | ... | ... |
packages/schema-page/index.scss
| ... | ... | @@ -96,4 +96,30 @@ |
| 96 | 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 | 126 | \ No newline at end of file | ... | ... |
packages/schema-page/index.vue
| ... | ... | @@ -75,19 +75,32 @@ |
| 75 | 75 | </slot> |
| 76 | 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 | 92 | <template v-for="item in getSlotKeys('dialog-')" #[item.name]="slotScope"> |
| 80 | 93 | <slot :name="item.slot" v-bind="{ ..._slotScope, ...slotScope }"></slot> |
| 81 | 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 | 102 | <z-schema-form |
| 90 | - :key="`form-${elDialogType}`" | |
| 103 | + :key="`form-${modalType}`" | |
| 91 | 104 | ref="form" |
| 92 | 105 | :value="valueForm" |
| 93 | 106 | @input="e => $emit('update:value-form', e)" |
| ... | ... | @@ -107,15 +120,15 @@ |
| 107 | 120 | </z-schema-form> |
| 108 | 121 | </template> |
| 109 | 122 | <!-- 详情弹出框内容 --> |
| 110 | - <template v-else-if="elDialogType === 'detail'"> | |
| 123 | + <template v-else-if="modalType === 'detail'"> | |
| 111 | 124 | <z-schema-form key="form-detail" ref="form" v-model="detail" :schema="schema.detail || detailSchema"> |
| 112 | 125 | <template v-for="item in getSlotKeys('detail-')" #[item.name]="slotScope"> |
| 113 | 126 | <slot :name="item.slot" v-bind="{ ..._slotScope, ...slotScope }"></slot> |
| 114 | 127 | </template> |
| 115 | 128 | </z-schema-form> |
| 116 | 129 | </template> |
| 117 | - </div> | |
| 118 | - </template> | |
| 130 | + </template> | |
| 131 | + </div> | |
| 119 | 132 | </el-dialog> |
| 120 | 133 | </div> |
| 121 | 134 | </template> |
| ... | ... | @@ -169,10 +182,10 @@ export default { |
| 169 | 182 | layout: get(this.schema, 'pagination.layout') || 'total, sizes, prev, pager, next, jumper', |
| 170 | 183 | total: get(this.schema, 'pagination.total') || 0, |
| 171 | 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 | 189 | detailSchema: filterout(cloneDeep(this.schema.form), ['is', 'rules']), |
| 177 | 190 | detail: this.valueDetail || {}, |
| 178 | 191 | tableData: this.valueTable || [], |
| ... | ... | @@ -201,15 +214,15 @@ export default { |
| 201 | 214 | this.$emit('update:dialog-visible', val); |
| 202 | 215 | }, |
| 203 | 216 | dialogType(val) { |
| 204 | - this.elDialogType = val; | |
| 217 | + this.modalType = val; | |
| 205 | 218 | }, |
| 206 | - elDialogType(val) { | |
| 219 | + modalType(val) { | |
| 207 | 220 | this.$emit('update:dialog-type', val); |
| 208 | 221 | }, |
| 209 | 222 | dialogTitle(val) { |
| 210 | - this.elDialogTitle = val; | |
| 223 | + this.modalTitle = val; | |
| 211 | 224 | }, |
| 212 | - elDialogTitle(val) { | |
| 225 | + modalTitle(val) { | |
| 213 | 226 | this.$emit('update:dialog-title', val); |
| 214 | 227 | }, |
| 215 | 228 | valueTable(val) { |
| ... | ... | @@ -238,13 +251,41 @@ export default { |
| 238 | 251 | }, defaultScope); |
| 239 | 252 | }, |
| 240 | 253 | _dialogProps() { |
| 254 | + const { is, ...other } = this.modalProps || {}; | |
| 241 | 255 | return { |
| 242 | - 'destroy-on-close': true, | |
| 243 | - 'append-to-body': true, | |
| 256 | + title: this.modalTitle, | |
| 244 | 257 | 'lock-scroll': false, |
| 258 | + 'append-to-body': true, | |
| 259 | + 'destroy-on-close': true, | |
| 245 | 260 | 'close-on-click-modal': false, |
| 246 | 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 | 341 | } else { |
| 301 | 342 | this.submitting = true; |
| 302 | 343 | let submitAPI = this.apiSubmit || this.emptyPromise; |
| 303 | - if (this.elDialogType === 'new') { | |
| 344 | + if (this.modalType === 'new') { | |
| 304 | 345 | submitAPI = this.apiNew || this.apiSubmit || this.emptyPromise; |
| 305 | - } else if (this.elDialogType === 'edit') { | |
| 346 | + } else if (this.modalType === 'edit') { | |
| 306 | 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 | 350 | .then(() => { |
| 310 | 351 | if (this.$listeners['submit-success']) { |
| 311 | 352 | this.$emit('submit-success'); |
| ... | ... | @@ -365,10 +406,10 @@ export default { |
| 365 | 406 | }, |
| 366 | 407 | // 打开弹出框 |
| 367 | 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 | 413 | this.visible = true; |
| 373 | 414 | this.$emit('dialog-change', type); |
| 374 | 415 | }, |
| ... | ... | @@ -382,7 +423,7 @@ export default { |
| 382 | 423 | }, |
| 383 | 424 | // 弹出框关闭 |
| 384 | 425 | onDialogClose() { |
| 385 | - this.elDialogType = 'none'; | |
| 426 | + this.modalType = 'none'; | |
| 386 | 427 | this.$emit('dialog-change', 'none'); |
| 387 | 428 | }, |
| 388 | 429 | // 弹出框关闭动画结束 |
| ... | ... | @@ -390,8 +431,8 @@ export default { |
| 390 | 431 | if (this.$refs.form) { |
| 391 | 432 | this.$refs.form.resetFields(); |
| 392 | 433 | } |
| 393 | - this.elDialogRender = false; | |
| 394 | - this.elDialogProps = {}; | |
| 434 | + this.modalRender = false; | |
| 435 | + this.modalProps = {}; | |
| 395 | 436 | this.$emit('update:value-form', this.cloneDeep(this.originProps).valueForm); |
| 396 | 437 | this.$emit('update:value-detail', this.cloneDeep(this.originProps).detailValue); |
| 397 | 438 | }, | ... | ... |