实战踩坑
ui
1. element 表单验证, `ref`+`prop`+`rules`
返回目录1. element ui
- element 表单验证,
ref+prop+rules - 覆盖样式
>>> .class,/deep/.class - 页面最小宽度
overflow: auto;,min-width: 1000px;
2. element 大图预览循环
:preview-src-list="imgListObj.list.map(item=>{return item.img}).filter((item,val)=>{return val>=index}).concat(imgListObj.list.map(item=>{return item.img}).filter((item,val)=>{return val<index}))"3. elememt upload
<el-upload
ref="uploadcover"
class="avatar-uploader"
action
:show-file-list="false"
:before-upload="beforeUpload"
:http-request="uploadFile"
>
<el-image
v-if="infoValue.cover"
:src="infoValue.cover"
class="avatar"
fit="scale-down"
@click="uploadWhat('cover')"
/>
<i
v-else
class="el-icon-plus avatar-uploader-icon"
@click="uploadWhat('cover')"
/>
<div slot="tip" class="el-upload_tip">
只能上传一张jpg,png图片,且不超过1Mb
</div>
</el-upload>$upload: 100px;
/deep/.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: $upload;
height: $upload;
line-height: $upload;
text-align: center;
}
.avatar {
width: $upload;
height: $upload;
display: block;
} beforeUpload(file) {
console.log('beforeUpload', file)
const isImage = /^image\/(jpg|png)$/.test(file.type)
const isLt1M = file.size / 1024 / 1024 < 1
if (!isImage) {
this.$message.error('上传图片只能是 jpg/png 格式!')
} else if (!isLt1M) {
this.$message.error('上传图片大小不能超过 1MB!')
}
return isImage && isLt1M
},
uploadFile(param) {
const fileObj = param.file
var form = new FormData()
form.append('file', fileObj)
form.append('type', 1)
upload(form).then(res => {
console.log('上传文件', res)
if (!res.errcode) {
console.log('上传文件', res.data)
if (this.uploadWhatVal === 'cover') {
this.infoValue.cover = res.data
}
if (this.uploadWhatVal === 'avatar') {
this.infoValue.avatar = res.data
}
}
})
}4. vant 复选框全选
vant 用 dom 操作全选会造成卡顿
解决办法:点击全选时把渲染列表赋值到选中列表中即可
5. elemennt el-input 组件重复获取焦点
需求:input 框组件每次显示隐藏都自动获取焦点
问题:设置 autofocus 属性只会在第一次生效,后续再次打开 input 组件不会自动聚焦
解决方法
// 每次打开input框执行以下方法
// 可以使用watch侦听组件的显示/隐藏,显示时执行以下 foucs()
this.$nextTick(() => {
this.$refs.elinput.focus();
});6. elememt 表格+无限滚动解决办法
7. element 级联选择器
//国,省,市
<el-cascader v-model="region" :props="props" />
props: {
lazy: true,
lazyLoad(node, resolve) {
const { level, value } = node
const params = {
address: value
}
getAddress(params).then(res => {
const { errcode, data } = res
if (!errcode) {
const listObject = data
const listArray = []
Object.keys(listObject).forEach(key => {
listArray.push({
label: listObject[key],
value: key,
leaf: level >= 2
})
})
resolve(listArray)
}
})
}
}<el-cascader
v-model="searchForm.user"
style="width:250px"
placeholder="选择个人号"
:props="searchForm.props"
:show-all-levels="false"
/>
props: {
lazy: true,
lazyLoad(node, resolve) {
const { level, value } = node
const data = {
groupid: value
}
// console.log('有没有value', value)
const listArray = []
if (!value) {
getGroup().then(res => {
const { errcode, data } = res
if (!errcode) {
const listObject = data.list
const allitem = {
label: '全部个人号',
value: undefined,
leaf: level >= 0
}
listArray.push(allitem)
listObject.forEach(item => {
listArray.push({
label: `${item.group_name}(${item.cnt})`,
value: item.id,
leaf: level >= 2
})
})
resolve(listArray)
}
})
} else {
getPersonalList(data).then(res => {
const { errcode, data } = res
if (!errcode) {
const listObject = data.list
listObject.forEach(item => {
listArray.push({
label: item.nickname,
value: item.mainwxid,
leaf: level >= 1
})
})
resolve(listArray)
}
})
}
// .setTimeout(() => {
// const nodes = Array.from({ length: level + 1 })
// .map(item => ({
// value: ++id,
// label: `选项${id}`,
// leaf: level >= 2
// }))
// // 通过调用resolve将子节点数据返回,通知组件数据加载完成
// resolve(nodes)
// }, 1000)
}
},8. element 时间日期选择器配置项
<template>
<div>
<!-- 调用此组件时注意 size 为 默认值为small ,可以传参改变size -->
<el-date-picker
v-model="dateTime"
type="daterange"
:picker-options="pickerOptions"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:size="size"
@change="pickdatetime"
/>
</div>
</template>
<script>
export default {
name: "DataPicker",
props: {
size: {
type: String,
default: "small",
},
defaultVal: {
type: Array,
default() {
return [];
},
},
},
data() {
return {
dateTime: "",
pickerOptions: {
disabledDate(time) {
return time.getTime() > Date.now();
},
shortcuts: [
{
text: "最近一周",
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit("pick", [start, end]);
},
},
{
text: "最近一个月",
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit("pick", [start, end]);
},
},
{
text: "最近三个月",
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit("pick", [start, end]);
},
},
],
},
};
},
watch: {
defaultVal(newval, oldval) {
console.log(newval);
if (!newval.length) {
this.dateTime = "";
}
},
},
methods: {
pickdatetime() {
// console.log("选中的时间", this.dateTime);
this.$emit("pickdatetime", this.dateTime);
},
},
};
</script>9. element 二次封装 el-Popover 组件
<template>
<div>
<el-popover
ref="popover"
v-model="visible"
placement="top"
trigger="manual"
popper-class="right--click--popover"
>
<div ref="popover" class="right--click--popover__inner">
<el-button type="text" @click="handlerRecall">撤回</el-button>
</div>
<slot slot="reference" name="inner" />
</el-popover>
</div>
</template>
<script>
import { sendMsg } from "@/api/chat.js";
export default {
name: "Name",
components: {},
props: {
visible: {
type: Boolean,
default: false,
},
activeChat: {
type: Object,
default() {
return {};
},
},
msgId: {
type: String,
default: "",
},
},
data() {
return {};
},
watch: {
visible(newval, oldval) {
if (newval) {
// 显示添加监听
document.addEventListener("click", this.handlePopVisible);
} else {
// 隐藏移除监听
document.removeEventListener("click", this.handlePopVisible);
}
},
},
methods: {
handlePopVisible(e) {
this.$nextTick(() => {
const pop = document.querySelector(".right--click--popover");
// console.log('pop', e.target)
if (!pop.contains(e.target)) {
this.$emit("handlerRecall", false);
}
});
},
handlerRecall() {
this.$emit("handlerRecall", false);
},
},
};
</script>
<style lang="scss">
@import "../../../../styles/theme/index.css";
.right--click--popover {
min-width: 0px;
padding: 0px 10px;
}
</style>部分调用代码
<template>
<div>
<PopMenu :visible="message.popMenuVisible" :active-chat="activeChat" :msg-id="message.msg_id" @handlerRecall="handlerRecall">
<div slot="inner" @contextmenu.prevent="handleRightClick(message)">右击显示</div>
</PopMenu>
</div>
</template>
<script>
import PopMenu from '../../components/PopMenu/index'
export default {
name: 'Name',
components: {
PopMenu
},
methods: {
handleRightClick(val) {
console.log('右击', val)
console.log('seatName', this.fanId)
this.list = this.list.map(item => {
item.isFromAlt = item.sender_key === this.altKey
if (item.msg_id === val.msg_id && item.isFromAlt) {
item.popMenuVisible = true
} else {
item.popMenuVisible = false
}
return item
})
this.popMenuVisibleGlobal = true
},
handleScroll(e) {
console.log(e)
this.handlerRecall()
},
handlerRecall() {
this.list = this.list.map(item => {
item.popMenuVisible = false
return item
})
this.popMenuVisibleGlobal = false
}
}
<script>
10. element 隐藏 checkbox 的文本
<!-- 1. 加个空格 -->
<el-checkbox :label="item.id"> </el-checkbox>// 2. 隐藏label的区域
.el-checkbox {
/deep/.el-checkbox__label {
display: none;
}
}11. element checkbox-group 高级使用
// 根据id获取整个对象
checkListObj() {
this.getListObj.list.forEach(item => {
if (this.checkedList.includes(item.id)) {
if (!this.arr.map(item => item.id).includes(item.id)) {
this.arr.push(item)
}
}
// else {
// this.arr = this.arr.filter(item => {
// if (this.checkedList.includes(item.id)) {
// return item
// }
// })
// }
})
if (this.arr.length !== this.checkedList.length) {
this.arr = this.arr.filter(item => {
if (this.checkedList.includes(item.id)) {
return item
}
})
}
},
// 全选
handleCheckAllChange(val) {
// this.checkedList = val ? this.getListObj.list.map(item => {
// return item.id
// }) : []
// this.checkedList = val ? this.getListObj.list : []
// this.isIndeterminate = false
// 当前页全选
const arr = this.getListObj.list.map(item => item.id)
if (val) {
arr.forEach(item => {
if (!this.checkedList.includes(item)) {
this.checkedList.push(item)
}
})
} else {
this.checkedList = this.checkedList.filter(item => {
return !arr.includes(item)
})
}
this.isIndeterminate = false
},
// 选择单个
handleCheckedItemChange(value) {
// let checkedCount =value.length
// this.checkAll = checkedCount === this.getListObj.list.length
// this.isIndeterminate =
// checkedCount > 0 && checkedCount < this.getListObj.list.length
// console.log(checkedCount)
let checkedCount = 0
const arr = this.getListObj.list.map(item => item.id)
value.forEach(item => {
if (arr.includes(item)) {
checkedCount++
}
})
this.checkAll = checkedCount === arr.length
this.isIndeterminate = checkedCount > 0 && checkedCount < arr.length
},
12. element 阻止组件的 @change 事件冒泡
主角: @click.native.stop
<!-- checkbox的 @change 事件会触发折叠面板的 @change 事件 -->
<el-collapse
v-model="tabsAct"
class="collapse"
accordion
@change="collapseChange"
>
<el-collapse-item v-for="(i) in catalog" :key="i.status" :name="i.status">
<template slot="title">
<!-- <i :class="item.icon" /> -->
<el-checkbox
v-model="checkAll"
@change="handleCheckAllChange"
@click.native.stop
>{{ i.title }}</el-checkbox
>
</template>
</el-collapse-item>
</el-collapse>13. elememt 表格复选功能数据处理
// 需要的数据类型
obj: {
// 类型: [分组1, 分组2]
1: [1,2,3],
2: [3,4,5],
3: [],
4: [1],
}
/*
// data中的数据:
catalog: [
{ title: '文字', icon: 'el-icon-picture-outline', status: '5' },
{ title: '图片', icon: 'el-icon-picture-outline', status: '1' },
{ title: '语音', icon: 'el-icon-microphone', status: '2' },
{ title: '分享页', icon: 'el-icon-link', status: '3' },
{ title: '小视频', icon: 'el-icon-video-camera', status: '4' },
{ title: '二维码', icon: 'el-icon-video-camera', status: '6' }
],
checkedListObj: { '1': [], '2': [], '3': [], '4': [], '5': [], '6': [] }
*/
handleSelectionChange(val) {
this.catalog.forEach((item) => {
if (item.status === this.tabsAct) {
this.checkedListObj[this.tabsAct] = val
}
})
},
// 选择5个后禁用表格中的复选框
canSelect(row, index) {
if (this.checkedListObj[this.tabsAct].length >= 5) {
return this.checkedListObj[this.tabsAct].includes(row)
} else {
return true
}
}14. element 表格拖动排序
import Sortable from "sortablejs";
const el = this.$refs.dragtable.$el.querySelector("tbody");
Sortable.create(el, {
handle: "#allowDrag",
onUpdate: (evt) => {
// 监听拖动结束事件
// // console.log(this) // this是当前vue上下文
// console.log(evt.oldIndex) // 当前行的被拖拽前的顺序
// console.log(evt.newIndex) // 当前行的被拖拽后的顺序
// 这里就可以写我们需要传给后台的逻辑代码
if (evt.oldIndex > evt.newIndex) {
this.propsList.splice(evt.newIndex, 0, this.propsList[evt.oldIndex]);
this.propsList.splice(evt.oldIndex + 1, 1);
} else {
// 如果当前元素在拖动目标位置的上方(即把该元素向下拖),
// 同样,给数组newIndex+1的地方新增一个和当前元素值一样的元素,
// 这时,将之前的那个拖动的元素删除掉,索引不变,还是oldIndex
this.propsList.splice(evt.newIndex + 1, 0, this.propsList[evt.oldIndex]);
this.propsList.splice(evt.oldIndex, 1);
}
this.$emit("drag", this.propsList);
},
});15. element 的 button 被禁用时 Tooltip 无效
在 button 外面多套一层 span 就行了
<el-tooltip
class="item"
effect="dark"
:content="button被禁用也能正常使用"
placement="top-start"
>
<span>
<el-button
plain
type="success"
:disabled="true"
@click="onBtnClick(item.id)"
>{{ item.name }}</el-button
>
</span>
</el-tooltip>16. element 表单提交验证失败页面滚动到第一个失败位置
this.$nextTick(() => {
const isError = document.getElementsByClassName("is-error");
isError[0].scrollIntoView({
behavior: "smooth",
block: "center",
});
});// 完整 demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<!-- 引入开发vue -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 引入样式 -->
<link
rel="stylesheet"
href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"
/>
<!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
</head>
<style>
.container {
width: 500px;
}
</style>
<body>
<div id="app">
<template>
<div ref="container" class="container">
<el-form
:model="ruleForm"
:rules="rules"
ref="ruleForm"
label-width="100px"
class="demo-ruleForm"
>
<el-form-item label="活动名称" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="活动形式" prop="desc">
<el-input type="textarea" v-model="ruleForm.desc"></el-input>
</el-form-item>
<el-form-item label="活动区域" prop="region">
<el-select v-model="ruleForm.region" placeholder="请选择活动区域">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
<el-form-item label="活动时间" required>
<el-col :span="11">
<el-form-item prop="date1">
<el-date-picker
type="date"
placeholder="选择日期"
v-model="ruleForm.date1"
style="width: 100%"
></el-date-picker>
</el-form-item>
</el-col>
<el-col class="line" :span="2">-</el-col>
<el-col :span="11">
<el-form-item prop="date2">
<el-time-picker
placeholder="选择时间"
v-model="ruleForm.date2"
style="width: 100%"
></el-time-picker>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="即时配送" prop="delivery">
<el-switch v-model="ruleForm.delivery"></el-switch>
</el-form-item>
<el-form-item label="活动性质" prop="type">
<el-checkbox-group v-model="ruleForm.type">
<el-checkbox
label="美食/餐厅线上活动"
name="type"
></el-checkbox>
<el-checkbox label="地推活动" name="type"></el-checkbox>
<el-checkbox label="线下主题活动" name="type"></el-checkbox>
<el-checkbox label="单纯品牌曝光" name="type"></el-checkbox>
</el-checkbox-group>
</el-form-item>
<div class="" style="height: 1000px;"></div>
<el-form-item label="活动性质" prop="type">
<el-checkbox-group v-model="ruleForm.type">
<el-checkbox
label="美食/餐厅线上活动"
name="type"
></el-checkbox>
<el-checkbox label="地推活动" name="type"></el-checkbox>
<el-checkbox label="线下主题活动" name="type"></el-checkbox>
<el-checkbox label="单纯品牌曝光" name="type"></el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="特殊资源" prop="resource">
<el-radio-group v-model="ruleForm.resource">
<el-radio label="线上品牌商赞助"></el-radio>
<el-radio label="线下场地免费"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="活动形式" prop="desc">
<el-input type="textarea" v-model="ruleForm.desc"></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>
</div>
</template>
</div>
</body>
<script>
var app = new Vue({
el: "#app",
data: {
message: "Hello Vue!",
ruleForm: {
name: "",
region: "",
date1: "",
date2: "",
delivery: false,
type: [],
resource: "",
desc: "",
},
rules: {
name: [
{ required: true, message: "请输入活动名称", trigger: "blur" },
{
min: 3,
max: 5,
message: "长度在 3 到 5 个字符",
trigger: "blur",
},
],
region: [
{ required: true, message: "请选择活动区域", trigger: "change" },
],
date1: [
{
type: "date",
required: true,
message: "请选择日期",
trigger: "change",
},
],
date2: [
{
type: "date",
required: true,
message: "请选择时间",
trigger: "change",
},
],
type: [
{
type: "array",
required: true,
message: "请至少选择一个活动性质",
trigger: "change",
},
],
resource: [
{ required: true, message: "请选择活动资源", trigger: "change" },
],
desc: [
{ required: true, message: "请填写活动形式", trigger: "blur" },
],
},
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
alert("submit!");
} else {
// 找到验证不通过的第一个元素平滑滚动
this.$nextTick(() => {
const isError = document.getElementsByClassName("is-error");
isError[0].scrollIntoView({
behavior: "smooth",
block: "center",
});
});
console.log("error submit!!");
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
},
});
</script>
</html>17. element 的table表格控件表头与内容列不对齐问题
APP.vue
/* 解决element-ui的table表格控件表头与内容列不对齐问题 */
body .el-table th.gutter {
display: table-cell !important;
}
// element表格出现滚动条时导致表头和数据不对齐
.el-table {
th.gutter,
colgroup.gutter {
display: block !important;
width: 10px !important;
}
}