- 记录 ElementUI 使用时遇到的问题
- Vue2.6 要用 TypeScript 需要使用
vue-class-component
/vue-property-decorator
,使用 Vuex 的话要用vuex-module-decorators
# 使用
# 问题
- 基本是对组件的使用中遇到的坑
# Form 表单
:label-position="labelPosition"
label 标签对齐方式:model="formLabelProps"
form 全部 form-item 的数据可以通过一个 data 绑定到 form 上,form-item 绑定对应的 prop,其中的输入框上绑定对应的v-model='formLabelProps.xx'
即可:rules
校验规则:同 antd,都用了这个 (opens new window),区别在于 antd 中的 callback 要求返回一个 Promise,重点在于检验后对于不符合规则的条目进行自定义提示,例:复制自官网:
<el-form
:model="ruleForm"
status-icon
:rules="rules"
ref="ruleForm"
label-width="100px"
class="demo-ruleForm"
>
<el-form-item label="密码" prop="pass">
<el-input
type="password"
v-model="ruleForm.pass"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="checkPass">
<el-input
type="password"
v-model="ruleForm.checkPass"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item label="年龄" prop="age">
<el-input v-model.number="ruleForm.age"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">提交</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
<script>
export default {
data() {
var checkAge = (rule, value, callback) => {
if (!value) {
return callback(new Error("年龄不能为空"));
}
setTimeout(() => {
if (!Number.isInteger(value)) {
callback(new Error("请输入数字值"));
} else {
if (value < 18) {
callback(new Error("必须年满18岁"));
} else {
callback();
}
}
}, 1000);
};
var validatePass = (rule, value, callback) => {
if (value === "") {
callback(new Error("请输入密码"));
} else {
if (this.ruleForm.checkPass !== "") {
this.$refs.ruleForm.validateField("checkPass");
}
callback();
}
};
var validatePass2 = (rule, value, callback) => {
if (value === "") {
callback(new Error("请再次输入密码"));
} else if (value !== this.ruleForm.pass) {
callback(new Error("两次输入密码不一致!"));
} else {
callback();
}
};
return {
ruleForm: {
pass: "",
checkPass: "",
age: "",
},
rules: {
pass: [{ validator: validatePass, trigger: "blur" }],
checkPass: [{ validator: validatePass2, trigger: "blur" }],
age: [{ validator: checkAge, trigger: "blur" }],
},
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
alert("submit!");
} else {
console.log("error submit!!");
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
},
};
</script>
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
:label-suffix
表单域标签的后缀:hide-required-asterisk
是否显示必填字段的标签旁边的红色星号:show-message
是否显示校验错误信息resetFields
对整个表单进行重置,将所有字段值重置为初始值并移除校验结果校验失败,自动滚动到失败的那一项:
// submit表单时,进行校验
submitForm() {
// 通过ref拿到el-form的DOM
this.formRef.validate(async (valid: boolean, object: ValidObject) => {
if (valid) {// do sth ...}
else { scrollView(object); }
}
}
// 滚动方法
scrollView(object: ValidObject) {
for (let i = 0; i < Object.keys(object).length; i++) {
const key = Object.keys(object)[i];
const dom: App = this.$refs[key] as App; // Vue+TS 用法很怪...有些地方感觉用decorator并不方便
// 滚动到指定节点
dom.$el.scrollIntoView({
// 值有start,center,end,nearest,当前显示在视图区域中间
block: 'start',
// 值有auto,instant,smooth,缓动动画(当前是慢速的)
behavior: 'smooth',
});
// 只需要检测一项跳出循环
break;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Upload
<el-upload
ref="upload"
:file-list="fileList"
:on-success="uploadFileSuccess"
:before-upload="beforeUploadFile"
:on-remove="handleRemove"
:auto-upload="true"
:on-exceed="handleExceed"
:on-preview="handlePreview"
:limit="10"
multiple
action
:http-request="handleUploadFile"
>
<el-button
size="small"
slot="trigger"
:disabled="uploadable"
v-if="!uploadable"
type="primary"
icon="el-icon-upload"
>上传附件</el-button
>
<el-button
size="small"
slot="tip"
disabled
v-if="uploadable"
type="primary"
icon="el-icon-upload"
>上传附件</el-button
>
<div slot="tip" class="el-upload__tip">附件单个大小不可超过20MB</div>
</el-upload>
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
action 必选参数,上传的地址
multiple 是否支持多选文件
drag 是否启用拖拽上传
show-file-list 是否显示已上传文件列表
on-preview 点击文件列表中已上传的文件时的钩子,可以在此处进行文件上传后的预览或下载等动作
on-remove 文件列表移除文件时的钩子
on-success 文件上传成功时的钩子
before-upload 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。
before-remove 删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除。
file-list 上传的文件列表, 例如:
[{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}]
auto-upload 是否在选取文件后立即进行上传
list-type 文件列表的类型 text/picture/picture-card
http-request 覆盖默认的上传行为,可以自定义上传的实现
on-exceed 文件超出个数限制时的钩子
limit 最大允许上传个数
tips: 上传按钮只能用 disable 修改样式,但是依然可以点击,所以设置两个按钮,配合 slot="tip"实现禁用上传功能
在 before-upload 中校验文件格式、大小等,on-success 之中可以得到上传后的所有文件列表,on-remove 之中得到移除某个文件之后的剩下的全部文件
# Dialog 对话框
- 坑:
destroy-on-close
关闭时销毁 Dialog 中的元素,不可用!!!也就是说 dialog 关闭之后不会销毁,而会保留上一次的内容。 modal-append-to-body
遮罩层是否插入至 body 元素上,若为 false,则遮罩层会插入至 Dialog 的父元素上- modal 是否需要遮罩层
- width Dialog 的宽度,默认 50%,
width="800px"
,注意要加上单位!!! visible
是否显示 Dialog,支持.sync
修饰符lock-scroll
是否在 Dialog 出现时将 body 滚动锁定before-close
关闭前的回调,会暂停 Dialog 的关闭 function(done),done 用于关闭 Dialog 可能可以用于解决上面的不销毁组件的坑……
# Pagination 分页
- 自定义分页样式
<template>
<div class="my-pagination">
<el-pagination
:current-page.sync="currentPageSync"
:layout="layout"
:total="total"
:page-size="pageSize"
@current-change="handleCurrentChange"
>
<el-dropdown placement="bottom" @command="handleSizeChange">
<div>
<span class="pagination-text left">每页</span>
<el-button class="dropdowm-page__button">
<span
>{{ pageSize }}<i class="el-icon-arrow-down el-icon--right"></i
></span>
</el-button>
<span class="pagination-text right">条</span>
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="p in pageSizes" :key="p" :command="p"
>{{ p }}</el-dropdown-item
>
</el-dropdown-menu>
</el-dropdown>
</el-pagination>
</div>
</template>
<script lang="ts">
/**
* 自定义分页组件
* 默认参数
* layout: slot, prev, pager, next
* total: 0
* pageSize: 10
* currentPage: 1
* */
import { Component, Prop, PropSync, Emit, Vue } from "vue-property-decorator";
@Component
export default class App extends Vue {
@Prop({ default: 0 }) total!: number;
@Prop({ default: 10 }) pageSize!: number;
@Prop({
default: (layout: string) => `slot, ${layout || "prev, pager, next"}`,
})
layout!: string;
@Prop({ default: () => [10, 20, 50, 100, 200] }) pageSizes!: Array<number>;
// PropSync,接收prop:currentPage,生成一个新的计算属性:currentPageSync,之后再同步给自身
@PropSync("currentPage", { default: 1, type: Number })
currentPageSync!: number;
// 下面两个空函数只是用于emit到父组件,触发对应的事件,不需要传递值
@Emit("size-change")
handleSizeChange(pageSize: number) {}
@Emit("current-change")
handleCurrentChange(currentPage: number) {}
}
</script>
<style lang="less" scoped>
.my-pagination ::v-deep .el-pagination {
.pagination-text {
width: 30px;
height: 30px;
font-size: 14px;
color: #00ee14;
letter-spacing: 0;
line-height: 30px;
font-weight: 400;
&.right {
text-align: left;
padding-left: 8px;
}
&.left {
text-align: right;
padding-right: 8px;
}
}
.dropdowm-page__button {
width: 60px;
height: 28px;
position: relative;
text-align: left;
line-height: 24px;
padding: 0 10px 0 12px;
background: #fff;
border: 1px solid grey;
color: balck;
}
.el-icon-arrow-down.el-icon--right {
position: absolute;
right: 6px;
top: 6px;
}
}
</style>
// 使用
<am-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="current"
:page-sizes="[10, 20, 50]"
:page-size="pageSize"
layout="slot, prev, pager, next"
:total="total"
></am-pagination>
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
- @PropSync 装饰器与@prop 用法类似,二者的区别在于:
- @PropSync 装饰器接收两个参数:
- propName: string 表示父组件传递过来的属性名;
- options: Constructor | Constructor[] | PropOptions 与@Prop 的第一个参数一致;
- @PropSync 会生成一个新的计算属性