UnaidedSelector.vue

el-select 组件封装成以下形式并作为父组件,可以使得选框的选项和配置在组件内部进行处理,使用时只需要引入继承自此组件的子组件即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
<template>
<el-select
v-model="INNER_SELECTED"
:size="size"
:placeholder="placeholder"
:clearable="clearable"
:loading="loading"
>
<el-option
v-for="(option, index) in options"
:key="index"
:label="typeof option === 'string' ? option : option.label"
:value="typeof option === 'string' ? option : option.value"
:disabled="option.disabled"
>
</el-option>
</el-select>
</template>

<script lang="ts">
import { Vue, Component, Model } from 'vue-property-decorator';
import { ElementUiSize } from '@/config/enums';

interface Option {
label: string;
value: string | number;
disabled?: boolean;
}

@Component
export default class UnaidedSelector extends Vue {
protected options: string[] | Option[] = [];
protected size: ElementUiSize = ElementUiSize.Medium;
protected placeholder: string = '请选择';
protected clearable: boolean = false;
protected loading: boolean = false;

@Model('change', { default: '' }) private INNER_VALUE?: string | number;

get INNER_SELECTED() {
return this.INNER_VALUE;
}
set INNER_SELECTED(val) {
this.$emit('change', val);
}
}
</script>

继承独立选框组件的子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script lang="ts">
@Component
export default class SelectorDemo extends UnaidedSelector {
private clearable = true;

private created() {
this.getOptions();
}

private async getOptions(): Promise<void> {
const data = await API();
this.options = data.data.map((item: any) => ({ label: item.name, value: item.id }));
}
}
</script>

使用时:

1
<selector-demo v-model="selected"></selector-demo>

UnaidedDialog.ts

在 ElementUI 中,对话框 el-dialog 是非常常用的交互组件,但假如一个页面中存在多个对话框,就会使页面逻辑变得非常臃肿,难以维护。

最近做的一个项目有这样一种情况:页面中有一组数据,由表格管理,表格的每一行都有编辑和删除操作,并且还可以新增数据。其中编辑和新增的差距不大,可以使用同一套表单。那么假如不将弹窗组件拆开管理,这个页面将要同时维护一个表格组件,一个分页组件以及 3 个弹窗组件,并且加上表单验证、接口管理,这个页面的代码量将会相当可观。假如将对话框组件单独拆分,那么页面逻辑将会非常清晰:

  • 父页面管理查询接口,维护表格和分页
  • “增” 和 “改” 使用对话框实现,维护增和改接口,一套表单验证
  • “删” 使用对话框实现,维护删除接口,一套表单验证

下面提供了一个 Mixin 文件,在 el-dialog 组件中使用此混入文件,可以使对话框独立得处理逻辑,与父组件之间的数据交互只使用 props 和自定义事件,保证逻辑清晰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { Vue, Component, Model, Watch } from 'vue-property-decorator';

@Component
export default class UnaidedDialog extends Vue {
get UnaidedDialog_flag() {
return this.UNAIDED_DIALOG_INNER_VALUE;
}
set UnaidedDialog_flag(val) {
this.$emit('change', val);
}

@Model('change', { default: false }) protected UNAIDED_DIALOG_INNER_VALUE?: boolean;

/* tslint:disable:no-empty */
protected UnaidedDialog_whenOpen() {}

/* tslint:disable:no-empty */
protected UnaidedDialog_whenClose() {}

@Watch('UnaidedDialog_flag')
private onDialogStatusChange(val: boolean) {
val ? (this as any).UnaidedDialog_whenOpen() : (this as any).UnaidedDialog_whenClose();
}
}

使用此 Mixin 的对话框组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<template>
<el-dialog
:visible.sync="UnaidedDialog_flag"
:title="dialogType === 'create' ? '新增' : '编辑'"
>
<!-- ... -->

<div slot="footer">
<el-button>取消</el-button>
<el-button @click="submit">保存</el-button>
</div>
</el-dialog>
</template>

<script lang="ts">
import { Component, Prop, Mixins } from 'vue-property-decorator';
import UnaidedDialog from '@/mixins/UnaidedDialog';

@Component
export default class DialogDemo extends Mixins(UnaidedDialog) {
@Prop({
type: String,
validator: (val: string) => ['create', 'edit'].includes(val)
})
private dialogType!: string;

private async UnaidedDialog_whenOpen() {
const data = await API();
}
}
</script>

使用独立对话框:

1
2
<!-- flag 用于控制对话框显隐 -->
<dialog-demo v-model="dialog.flag" :dialog-type="dialog.type"></dialog-demo>

LazyDialog.ts

这个 Mixin 是对话框组件的补充。有些时候需要在弹窗首次打开的时候做一些操作,再次打开则不再触发,这种需求可以使用下方的 Mixin 实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { Vue, Component, Watch } from 'vue-property-decorator';

@Component
export default class LazyDialog extends Vue {
private LAZY_DIALOG_INIT_TIMES: number = 0;

@Watch('UnaidedDialog_flag')
protected onLazyDialogInitTimesChange(val: number) {
if (val && this.LAZY_DIALOG_INIT_TIMES < 1) {
this.LAZY_DIALOG_INIT_TIMES++;
(this as any).LazyDialog_created();
}
}
}

使用此 Mixin:

1
2
3
4
5
6
7
8
9
10
11
<script lang="ts">
import UnaidedDialog from '@/mixins/UnaidedDialog';
import LazyDialog from '@/mixins/LazyDialog';

@Component
export default class DialogWithLazyDialog extends Mixins(UnaidedDialog, LazyDialog) {
private LazyDialog_created() {
this.getInitData();
}
}
</script>

一些最佳实践

  • 可以在 UnaidedDialog_whenOpen 中获取数据并填充进表单,接着进行表单验证
  • UnaidedDialog_whenClose 中清空表单并重置表单验证
  • 如果使用对话框提交表单,那么应当将提交结果通过 $emit() 传递至父组件,由父组件处理后续事件