资料
element-ui 中文官网
element-ui 官网组件使用指南
1. 项目中安装并引用 element-ui
- 安装
npm i element-ui -S
- 项目中
main.js
文件中引用
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
/* eslint-disable no-new */
new Vue({
el: '#app',
render: h => h(App)
})
2. 解决一个页面多个 Upload
组件,出现 "上传成功之后无法判断是哪个组件" 的问题
- 源码和相应部分页面截图,当前页面有三个上传组件
- 解决思路:
给【选取文件】这个按钮绑了个点击事件 ,多用了个变量
uploadSign
做标记 成功的时候判断标记uploadSign
相应的值就好了
- 实现的源码
<template>
<div>
<el-form>
<el-form-item label="身份证正面">
<el-row :gutter="30">
<el-col :span="8">
<div class="grid-content">
<el-upload
class="upload-demo"
ref="frontPic"
accept="image/png, image/jpeg"
name="imgPath"
:action="imgUploadAPI"
:before-upload="beforeAvatarUpload"
:on-success="handleSuccess"
:limit="1"
:on-exceed="handleExceed"
list-type="picture">
<el-button size="small" type="primary" @click="uploadSign=1">选取文件</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件</div>
</el-upload>
</div>
</el-col>
<el-col :span="8">
<div class="grid-content">
<img class="show-img" src="@/assets/img/frontPic.png" alt="样图" />
</div>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="身份证背面">
<el-row :gutter="30">
<el-col :span="8">
<div class="grid-content">
<el-upload
class="upload-demo"
ref="reversePic"
accept="image/png, image/jpeg"
name="imgPath"
:action="imgUploadAPI"
:before-upload="beforeAvatarUpload"
:on-success="handleSuccess"
:limit="1"
:on-exceed="handleExceed"
list-type="picture">
<el-button size="small" type="primary" @click="uploadSign=2">选取文件</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件</div>
</el-upload>
</div>
</el-col>
<el-col :span="8">
<div class="grid-content">
<img class="show-img" src="@/assets/img/reversePic.png" alt="样图" />
</div>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="身份证SIM卡手持">
<el-row :gutter="30">
<el-col :span="8">
<div class="grid-content">
<el-upload
class="upload-demo"
ref="handPic"
accept="image/png, image/jpeg"
name="imgPath"
:action="imgUploadAPI"
:before-upload="beforeAvatarUpload"
:on-success="handleSuccess"
:limit="1"
:on-exceed="handleExceed"
list-type="picture">
<el-button size="small" type="primary" @click="uploadSign=3">选取文件</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件</div>
</el-upload>
</div>
</el-col>
<el-col :span="8">
<div class="grid-content">
<img class="show-img" src="@/assets/img/handPic.png" alt="样图" />
</div>
</el-col>
</el-row>
</el-form-item>
</el-form>
</div>
</template>
<script>
import qs from 'qs' // 解决 axios 数据提交格式与后台不一致的问题 -> name=hehe&age=10
import { Loading } from 'element-ui'
export default {
name: 'App',
data() {
return {
// 多个上传组件标记 1 身份证正面;2 身份证背面;3 省份证SIM卡手持
uploadSign: null,
// 图片上传接口
imgUploadAPI: '/jjhServerApi/ab/imgPath',
// 信息提交接口
postAPI: '/jjhServerApi/ab/insertposn',
// 信息提交数据
postData: {
/**
* name 姓名
* department 部门
* phoneNumber 手机号码
* position 职位
* certCode 证件号 身份证号
* companyId 公司Id
* frontPic 身份证正面
* reversePic 身份证反面
* handPic 手持身份证
* lastOperation 该记录最后操作 A:添加操作,D:设置为离职操作
*/
name: '',
phoneNumber: '',
department: '',
position: '',
certCode: '',
companyId: '1', // 先写一个假数据
frontPic: '',
reversePic: '',
handPic: '',
lastOperation: 'A'
}
},
methods: {
// 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。
// 判断上传文件类型
beforeAvatarUpload: function(file) {
const me = this
const isJPG = file.type === 'image/jpeg'
const isPNG = file.type === 'image/png'
if (!(isJPG || isPNG)) {
me.$message.error('上传的文件只能是 JPG 或者是 PNG 格式的')
}
},
// 文件超出个数限制时的钩子
handleExceed(file, fileList) {
const me = this
me.$message.warning('只能上传一个文件')
},
// 文件上传成功时的钩子
handleSuccess(res, file) {
const me = this
const sign = me.uploadSign
// 判断 code 是否为 0 0 代表成功
if (res.code === 0) {
// 通过 sign 判断给相应的字段赋值
if (sign === 1) {
// 正面
me.postData.frontPic = JSON.parse(res.result).url
} else if (sign === 2) {
// 背面
me.postData.reversePic = JSON.parse(res.result).url
} else if (sign === 3) {
// 手持
me.postData.handPic = JSON.parse(res.result).url
}
} else {
// 失败回调
me.$message.error(`${res.info}`)
// 通过 sign 判断清空相应的列表 因为默认会显示出图片其实是失败了,容易造成用户误解
if (sign === 1) {
// 正面
me.$refs.frontPic.clearFiles()
} else if (sign === 2) {
// 背面
me.$refs.reversePic.clearFiles()
} else if (sign === 3) {
// 手持
me.$refs.handPic.clearFiles()
}
}
// 清空标记
me.uploadSign = null
}
}
}
</script>
- 效果 gif 图
3. form 表单验证规则
- 效果 gif 图
- 直接上源码了,除了正则表达式大多都是官网示例的东西,还有就是
upload
上传插件是我手写的规则验证的;
因为upload
上传成功后的删除图标是后面生成的,且:on-remove
给的方法如果自定义参数的话,就不能正常使用了,所以使用了三个不同的方法来定义删除成功后的钩子
<template>
<div class="container">
<el-form ref="dataForm" :model="postData" :rules="rules" label-width="180px">
<el-form-item label="员工姓名" prop="name">
<el-input v-model="postData.name" type="text" placeholder="请输入员工姓名" autofocus=""></el-input>
</el-form-item>
<el-form-item label="手机号码" prop="phoneNumber">
<el-input v-model="postData.phoneNumber" type="text" placeholder="请输入手机号码"></el-input>
</el-form-item>
<el-form-item label="部门" prop="department">
<el-select v-model="postData.department" placeholder="请选择部门">
<el-option label="管理层" value="管理层"></el-option>
<el-option label="研发部" value="研发部"></el-option>
<el-option label="市场部" value="市场部"></el-option>
<el-option label="行政部" value="行政部"></el-option>
</el-select>
</el-form-item>
<el-form-item label="职位" prop="position">
<el-input v-model="postData.position" type="text" placeholder="请输入职位"></el-input>
</el-form-item>
<el-form-item label="身份证号" prop="certCode">
<el-input v-model="postData.certCode" type="text" placeholder="请输入身份证号"></el-input>
</el-form-item>
<el-form-item label="身份证正面">
<el-row :gutter="30">
<el-col :span="11">
<div class="grid-content">
<el-upload
class="upload-demo"
ref="frontPic"
accept="image/png, image/jpeg"
name="imgPath"
:action="imgUploadAPI"
:before-upload="beforeAvatarUpload"
:on-success="handleSuccess"
:before-remove="beforeRemove"
:on-remove="handleRemoveFront"
:limit="1"
:on-exceed="handleExceed"
list-type="picture">
<el-button size="small" type="primary" @click="uploadSign=1">选取文件</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件</div>
</el-upload>
</div>
</el-col>
<el-col :span="12">
<div class="grid-content">
<img class="show-img" src="@/assets/img/frontPic.png" alt="样图" />
</div>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="身份证背面">
<el-row :gutter="30">
<el-col :span="11">
<div class="grid-content">
<el-upload
class="upload-demo"
ref="reversePic"
accept="image/png, image/jpeg"
name="imgPath"
:action="imgUploadAPI"
:before-upload="beforeAvatarUpload"
:on-success="handleSuccess"
:before-remove="beforeRemove"
:on-remove="handleRemoveReverse"
:limit="1"
:on-exceed="handleExceed"
list-type="picture">
<el-button size="small" type="primary" @click="uploadSign=2">选取文件</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件</div>
</el-upload>
</div>
</el-col>
<el-col :span="12">
<div class="grid-content">
<img class="show-img" src="@/assets/img/reversePic.png" alt="样图" />
</div>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="身份证SIM卡手持">
<el-row :gutter="30">
<el-col :span="11">
<div class="grid-content">
<el-upload
class="upload-demo"
ref="handPic"
accept="image/png, image/jpeg"
name="imgPath"
:action="imgUploadAPI"
:before-upload="beforeAvatarUpload"
:on-success="handleSuccess"
:before-remove="beforeRemove"
:on-remove="handleRemoveHand"
:limit="1"
:on-exceed="handleExceed"
list-type="picture">
<el-button size="small" type="primary" @click="uploadSign=3">选取文件</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件</div>
</el-upload>
</div>
</el-col>
<el-col :span="12">
<div class="grid-content">
<img class="show-img" src="@/assets/img/handPic.png" alt="样图" />
</div>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('dataForm')">立即提交</el-button>
<el-button @click="resetForm('dataForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import qs from 'qs' // 解决 axios 数据提交格式与后台不一致的问题 -> name=hehe&age=10
import { Loading } from 'element-ui'
export default {
name: 'App',
data() {
return {
// 多个上传组件标记 1 身份证正面;2 身份证背面;3 省份证SIM卡手持
uploadSign: null,
// 图片上传接口
imgUploadAPI: '/jjhServerApi/ab/imgPath',
// 信息提交接口
postAPI: '/jjhServerApi/ab/insertposn',
// 信息提交数据
postData: {
/**
* name 姓名
* department 部门
* phoneNumber 手机号码
* position 职位
* certCode 证件号 身份证号
* companyId 公司Id
* frontPic 身份证正面
* reversePic 身份证反面
* handPic 手持身份证
* lastOperation 该记录最后操作 A:添加操作,D:设置为离职操作
*/
name: '',
phoneNumber: '',
department: '',
position: '',
certCode: '',
companyId: '1', // 先写一个假数据
frontPic: '',
reversePic: '',
handPic: '',
lastOperation: 'A'
},
// 表单定义验证规则 trigger 为触发条件 http://element-cn.eleme.io/#/zh-CN/component/form#biao-dan-yan-zheng 官网地址
rules: {
// 姓名
name: [{ required: true, message: '请输入员工姓名', trigger: 'blur' }],
// 手机号码
phoneNumber: [
{ required: true, message: '请输入手机号码', trigger: 'blur' },
{
validator: function(rule, value, callback) {
var MobileRegex = /^(13[0-9]|147|15[0-9]|17[0-9]|18[0-9])\d{8}$/
if (!MobileRegex.test(value)) {
callback(new Error('手机号码格式不正确!'))
} else {
callback()
}
},
trigger: 'blur'
}
],
// 选择部门
department: [
{
required: true,
message: '请选择您的部门',
trigger: 'change'
}
],
// 职位
position: [{ required: true, message: '请输入职位', trigger: 'blur' }],
// 身份证号
certCode: [
{ required: true, message: '请输入身份证号', trigger: 'blur' },
{
validator: function(rule, value, callback) {
var MobileRegex = /(^\d{18}$)|(^\d{17}(\d|X|x)$)/
if (!MobileRegex.test(value)) {
callback(new Error('身份证号格式不正确!'))
} else {
callback()
}
},
trigger: 'blur'
}
]
}
}
},
methods: {
// 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。
beforeAvatarUpload: function(file) {
// 判断上传文件类型
const me = this
const isJPG = file.type === 'image/jpeg'
const isPNG = file.type === 'image/png'
if (!(isJPG || isPNG)) {
me.$message.error('上传的文件只能是 JPG 或者是 PNG 格式的')
}
},
// 文件超出个数限制时的钩子
handleExceed(file, fileList) {
const me = this
me.$message.warning('只能上传一个文件')
},
// 文件上传成功时的钩子
handleSuccess(res, file) {
const me = this
const sign = me.uploadSign
// 判断接口返回的 code 是否为 0 0 代表成功
if (res.code === 0) {
// 通过 sign 判断给相应的字段赋值
if (sign === 1) {
// 正面
me.postData.frontPic = JSON.parse(res.result).url
} else if (sign === 2) {
// 背面
me.postData.reversePic = JSON.parse(res.result).url
} else if (sign === 3) {
// 手持
me.postData.handPic = JSON.parse(res.result).url
}
} else {
// 失败回调
me.$message.error(`${res.info}`)
// 通过 sign 判断清空相应的列表 因为默认会显示出图片其实是失败了,容易造成用户误解
if (sign === 1) {
// 正面
me.$refs.frontPic.clearFiles()
} else if (sign === 2) {
// 背面
me.$refs.reversePic.clearFiles()
} else if (sign === 3) {
// 手持
me.$refs.handPic.clearFiles()
}
}
// 清空标记
me.uploadSign = null
},
// 删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止上传。
beforeRemove(file, fileList) {
// 增加一个询问框
return this.$confirm(`确定移除 ${file.name}?`)
},
// 文件列表移除文件时的钩子 因为是三个组件所以便分开写了三个方法,写成一个带参数的试过了不行
handleRemoveFront(file, fileList) {
const me = this
me.postData.frontPic = ''
},
handleRemoveReverse(file, fileList) {
const me = this
me.postData.reversePic = ''
},
handleRemoveHand(file, fileList) {
const me = this
me.postData.handPic = ''
},
// 提交数据
submitForm(formName) {
const me = this
this.$refs[formName].validate(valid => {
// 通过插件自定义全部规则已验证通过
if (valid) {
if (!me.postData.frontPic) {
me.$message.error('请上传您的身份证正面图片')
return false
}
if (!me.postData.reversePic) {
me.$message.error('请上传您的身份证反面图片')
return false
}
if (!me.postData.handPic) {
me.$message.error('请上传您的身份证SIM卡手持图片')
return false
}
let loading = Loading.service({
fullscreen: true,
text: '正在请求服务器'
})
me.axios
.post(me.postAPI, qs.stringify(me.postData))
.then(response => {
loading.close()
const getData = response.data
// code 为 0 表示成功
if (getData.code === 0) {
me.$message.success(`${getData.info},即将自动跳转至列表页面`)
// 新增成功跳转至 【通讯录管理】页面
setTimeout(function() {
location.href = 'addresslist.html'
}, 2000)
} else {
me.$message.error(getData.info)
}
})
.catch(error => {
console.log(error)
me.$message.error('请求服务器失败了,请稍后重试!')
})
} else {
console.log('error submit!!')
return false
}
})
},
// 重置表单
resetForm(formName) {
const me = this
this.$refs[formName].resetFields()
// 清空上传的文件
// 正面
me.$refs.frontPic.clearFiles()
// 背面
me.$refs.reversePic.clearFiles()
// 手持
me.$refs.handPic.clearFiles()
me.postData.frontPic = ''
me.postData.reversePic = ''
me.postData.handPic = ''
}
}
}
</script>
<style lang="less">
body {
.container {
form {
width: 700px;
margin: 0 auto;
.show-img {
width: 300px;
max-width: 100%;
}
}
}
}
</style>
- 主要演示下删除上传成功后的效果 gif
- 优化将
frontPic
reversePic
handPic
这三个字段用一个数组包着,sign
就不用在搞那么多的判断了,这样的话就是0
1
2
,下面是三个代码片段
// 三个上传字段写个数组方便下面使用,就不用一个一个的判断 uploadSign 的值了,直接 me.postData[me.uploadSignString[me.uploadSign]] 即可
uploadSignString: ['frontPic', 'reversePic', 'handPic'],
// 通过 [me.uploadSignString[me.uploadSign]] 判断给相应的字段赋值
me.postData[me.uploadSignString[me.uploadSign]] = JSON.parse(
res.result
).url
// 通过 sign 判断清空相应的列表 因为默认会显示出图片其实是失败了,容易造成用户误解
me.$refs[me.uploadSignString[me.uploadSign]].clearFiles()
3. 使用 vue-cli3.x
时,引用 element-ui
的 css
文件报错
- 报错截图
These dependencies were not found:
* fonts/element-icons.ttf in ./node_modules/css-loader??ref--6-oneOf-3-1!./node_modules/postcss-loader/src??ref--6-oneOf-3-2!./node_modules/element-ui/lib/theme-chalk/index.css
* fonts/element-icons.woff in ./node_modules/css-loader??ref--6-oneOf-3-1!./node_modules/postcss-loader/src??ref--6-oneOf-3-2!./node_modules/element-ui/lib/theme-chalk/index.css
To install them, you can run: npm install --save fonts/element-icons.ttf fonts/element-icons.woff
- 解决错误
问题 github 参考链接
出现错误的主要原因是在vue.config.js
配置文件中设置了css.modules = true
,这个默认的值为false
,只要把这个设置去掉问题即可得到解决
4. 使用分页 el-pagination
时,如果没加 :total
属性的话,即使用了 pager
这个 list
也显示不出来
- 出现问题的代码(我的 html 代码使用的是
pug
模板)
el-pagination(v-if="cusCrmList.total > 11" @current-change="clientPaginationHandle" :page-size="cusCrmList.size" layout="prev, pager, next, slot, jumper" :current-page.sync="cusCrmList.current" background small prev-text="上一页" next-text="下一页" align="right")
span.el-pagination__total 共 {{ cusCrmList.pages }} 页
- 因为我不需要展示总页数,所以便没加
:total
属性,但是这样的话,分页的pager
部分就展示不出来了,后来试了试,发现是没加这个的原因,下面是可以正常显示的代码
el-pagination(v-if="cusCrmList.total > 11" @current-change="clientPaginationHandle" :page-size="cusCrmList.size" layout="prev, pager, next, slot, jumper" :current-page.sync="cusCrmList.current" :total="cusCrmList.total" background small prev-text="上一页" next-text="下一页" align="right")
span.el-pagination__total 共 {{ cusCrmList.pages }} 页
5. 使用 v-loading
时,需要给其父级增加相对定位 position: relative
- 偶然性
Bug
出现在IE
上面(本地开发会出现,线上暂未见到),大致就是在某一次强制刷新页面的时候,由于loading
的原因,遮住了部分的页面,页面会出现多个错位的loading
导致布局看起来有些错乱,刷新也没有用,只能重新打开当前页面,后来发现使用v-loading
自动生成的类名是el-loading-mask
的标签样式如下
.el-loading-mask {
position: absolute;
z-index: 2000;
background-color: rgba(255, 255, 255, 0.9);
margin: 0;
top: 0;
right: 0;
bottom: 0;
left: 0;
-webkit-transition: opacity 0.3s;
transition: opacity 0.3s;
}
虽然正常情况是标签
display: none
了,但是这里的Bug
就是在异常情况下(没有隐藏掉),样式如上所示为绝对定位
,但是由于父级没给相对定位
,所以这里是相对于body
去决定定位的,所以这个el-loading-mask
的位置是总在页面最顶部,给加v-loading
标签的父级加上相对定位 -> position: relative
,这样的话及时出现loading
未隐藏的Bug
也不会影响页面的正常布局
6. 使用 dropdown、dropdown-menu、dropdown-item
实现点击下拉子菜单更改数据的需求
- 效果 Gif 图
- 官方实例参考地址
- 官网示例使用的
command="a"
属性的值是string
,从下面的文档中可以看到这个指令支持 类型:string/number/object
,我这里需要使用object
类型的值 -
object
类型使用示例的主要源码:
<template lang="pug">
el-dropdown(:show-timeout="0" @command="filtersStateHandle")
span(class="filter-state-title") 状态
i(class="el-icon-arrow-down el-icon--right")
el-dropdown-menu(slot="dropdown")
el-dropdown-item(v-for="(item,index) in filtersStateData" :key="item.value" :command="{ value: item.value }" :class="{ 'filter-state-active': item.value === signFilterData.getServState }") {{ item.text }}
template(slot-scope="scope")
span {{ scope.row.GET_SERV_STATE }}
</template>
<script>
export default {
data() {
return {
// 【状态】字段下来使用的数据
filtersStateData: [
{ text: "全部", value: "ALL" },
{ text: "正常", value: "F0A" },
{ text: "主动停机", value: "F0I" },
{ text: "欠费双向停机", value: "F0K" },
{ text: "拆机", value: "F1X" }
],
}
},
methods:{
/**
* 右侧 header 【状态】 下拉筛选子列表点击回调
* @param {object} command 当前点击的元素 key 为 command 的对象值
*/
filtersStateHandle(command) {
// console.log(command);
const me = this;
// 数据变化
me.signFilterData.getServState = command.value;
me.getProductDyList();
},
}
}
<script>
<style lang="stylus" scoped>
.el-dropdown-menu
.filter-state-active
background-color #ecf3ff
color #659bfe
</style>
7. 当使用 v-if v-else
进行两个 el-table
数据判断,数据切换时会报错
- 报错信息如下
Error in render: "TypeError: _self.$scopedSlots.default is not a function"
- 原因
是由于两个
el-table
组件使用了相同的数据导致
- 解决方法
给两个
el-table
组价分别增加一个不同的key
属性值
8. 当使用 pug
模板进行布局时,table
的排序 sortable
功能无法使用
- 这个问题真的是很怪异,翻阅了 github 的 Issues 列表幸好查到了相关的问题文章
issues 链接:[Bug Report] el-table can not sort with Pug template language used. #11531
上述人员提供的成功在线 demo
- 主要就是写法和官网的有些差异
sortable 写成 :sortable='true' 即可
9. 引用 element-ui
中的 Tree
组件时,在 IE
浏览器中会报错
- 错误重现
- 浏览器版本 IE11
- 报错截图
- 示例源码
<el-tree ref="treeDOM" :data="treeData" show-checkbox node-key="id" default-expand-all :expand-on-click-node="false"> <span class="custom-tree-node" slot-scope="{ node, data }">{{ data.name }} ({{ data.roleName }}) </span> </el-tree>
- 百度之后找到可参考的文章
vue在IE浏览器下报错Failed to generate render function:SyntaxError: 缺少标识符 in
- 解决思路
看到这个错误提示的时候,其实我没想到会是
Tree
组件的问题,主要是参考上面的文章,测试之后发现确实是这个组件的写法导致的错误;
主要的原因是slot-scope="{ node, data }"
这个是es6
的对象解构赋值,很显然IE
是不支持的,具体的纠错方法是将slot-scope
属性的值写成scope
这个对象,然后后面使用scope.node
或者scope.data
的写法去调用子属性即可。
- 示例源码
<el-tree ref="treeDOM" :data="treeData" show-checkbox node-key="id" default-expand-all :expand-on-click-node="false"> <span class="custom-tree-node" slot-scope="scope">{{ scope.data.name }} ({{ scope.data.roleName }}) </span> </el-tree>
10. el-table
-> el-table-column
的 render-header
属性使用示例
- 需求:表头需要定制为换行的风格
- 实现:使用
render-header
属性,下面是相应的html
和javascript
代码片段
el-table-column(prop="" label="第一行的文本,第二行的文本" show-overflow-tooltip :render-header="renderHeaderHtml")
renderHeaderHtml(h, { column, $index }){
const stringArray = column.label.split(',')
return h('p', [
h('span', stringArray[0]),
h('br'),
h('span', stringArray[1])
])
}
11. el-cascader
,封装一个公用的组件
- 组件的完整源码,这个组件是
<template lang="pug">
//- 基于 el-cascader 组件,自定义一个角色结构树组件
.identity-tree
//- 是否保留清空方法待定 clearable
el-cascader(ref="identityTree" v-model="selectedOptions" :props="{ checkStrictly: true,lazy: true,lazyLoad: loadHandleNode,leaf: 'isLeaf', label: 'orgnName', value: 'orgnId'}" :show-all-levels="false" @change="cascaderHandleChange" filterable placeholder="请选择" size="mini")
</template>
<script>
// api
import { getSysOrgnInfo } from "@/api/common";
export default {
name: "IdentityTree",
data() {
return {
selectedOptions: [],
// 储存转换后的当前用户信息
currentUserInfo: {}
};
},
methods: {
/**
* 查询部门下拉树结构
* id 如果有传 id 值,表示这里请求的是子级的数据,返回一个 peomise,否则表示是一级数据直接返回
*/
getSysOrgnInfo(orgnId) {
// 判断如果传的是 "" 值,表示这里是需要初始化最初的用户身份,否则正常请求接口
if (orgnId) {
// 获取下级的列表数据
return new Promise(resolve => {
getSysOrgnInfo({
orgnSumId: orgnId
}).then(res => {
let saveData = res.data.data;
console.log(saveData);
// 手动将 isLeaf 字段转换成 Boolean 值
saveData.forEach(element => {
element.isLeaf = !!element.isLeaf;
});
resolve(saveData);
});
});
} else {
// 获取当前用户身份的数据
return new Promise(resolve => {
let { currentManagerTm } = JSON.parse(
JSON.stringify(this.$store.getters.getUserInfo)
);
currentManagerTm.orgnName = currentManagerTm.areaName;
currentManagerTm.orgnId = currentManagerTm.areaId;
// resolve([currentManagerTm]);
// 直接传递当前用户信息
// this.$emit("update-component-data", this.currentUserInfo);
getSysOrgnInfo({
sessionOrgnSumId: currentManagerTm.orgnId
}).then(res => {
let saveData = res.data.data;
// 手动将 isLeaf 字段转换成 Boolean 值
saveData.forEach(element => {
element.isLeaf = !!element.isLeaf;
});
resolve(saveData);
this.currentUserInfo = saveData[0];
// 默认选中当前用户身份,并触发页面加载数据
this.selectedOptions = [currentManagerTm.orgnId];
this.$emit("update-component-data", saveData[0]);
});
});
/*
return new Promise(resolve => {
let { currentManagerTm } = JSON.parse(
JSON.stringify(this.$store.getters.getUserInfo)
);
getSysOrgnInfo({
sessionOrgnSumId: currentManagerTm.areaId
}).then(res => {
let saveData = res.data.data;
console.log(saveData);
// 手动将 isLeaf 字段转换成 Boolean 值
saveData.forEach(element => {
element.isLeaf = !!element.isLeaf;
});
resolve(saveData);
this.$emit("update-component-data", saveData);
});
});
*/
}
},
/**
* 部门节点加载函数
*/
async loadHandleNode(node, resolve) {
console.log(node.level);
const data = await this.getSysOrgnInfo(
node.level === 0 ? "" : node.data.orgnId
);
resolve(data);
},
/**
* 选中弹窗内部的部门节点加载函数
*/
cascaderHandleChange(value) {
let $identityTree = this.$refs.identityTree;
// 由于组件没有单选关闭悬浮窗的回调,所以需要手动关闭
$identityTree.toggleDropDownVisible(false);
if (value) {
// 调用父组件方法,传递当前单选选中的身份对象
const checkedNodes = $identityTree.getCheckedNodes();
// 判断如果单选无选中值时,传递当前用户信息
this.$emit(
"update-component-data",
checkedNodes.length ? checkedNodes[0].data : this.currentUserInfo
);
}
}
}
};
</script>
12. Uncaught ReferenceError: _MessageBox is not defined
浏览器控制台报错,错误导致应用无法正常显示
- 这个错误之前从未遇到过,我什么都没改过,也不知道抽什么风 0.0
- 参考文章:
https://github.com/ElementUI/babel-plugin-component/issues/31
- 解决方法:
https://github.com/ElementUI/babel-plugin-component/issues/31#issuecomment-530874578
const msgbox = MessageBox
const { alert, confirm, prompt } = msgbox
Vue.prototype.$msgbox = msgbox
Vue.prototype.$alert = alert
Vue.prototype.$confirm = confirm
Vue.prototype.$prompt = prompt
13. el-form-item
自动验证的 Bug
- 问题描述
首先页面是两个大的模块,由
if else
驱动其显示隐藏,,第二个模块中的一个表单元素在每次判断其显示时,会自动验证一下,而且提示还不是我自定义的文本
- 参考资料
在dialog弹框里放一个form,form里面的一组checkbox加了change验证后,在第二次打开弹框及以后,每次打开dialog弹框,什么都没做,它就自动验证了 #3240
文章给出的解决方法是this.$refs.baseForm.resetFields();
,但是场景和我的有些差异。
- 解决方法
将这个表单元素自身判断使用的
v-if
,放到作为其自动响应式布局的父级el-row
即可。
14. el-select
绑定值为对象时使用 value-key
的示例
<template>
<el-select v-model="value" value-key="code" placeholder="请选择">
<el-option-group
v-for="group in options"
:key="group.label"
:label="group.label">
<el-option
v-for="item in group.child"
:key="item.code"
:label="item.label"
:value="item">
</el-option>
</el-option-group>
</el-select>
</template>
export default {
data() {
return {
options:[
{
"code": "01",
"label": "用户属性",
"child": [
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001001",
"label": "用户邮箱",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001002",
"label": "用户名称",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001003",
"label": "用户ID",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001004",
"label": "用户归属省份",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001005",
"label": "用户归属地市",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001006",
"label": "用户归属区县",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001007",
"label": "用户归属渠道",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001008",
"label": "客户手机号",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001009",
"label": "客户ID",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001010",
"label": "客户名称",
"firstLevelCode": "01"
},
{
"calculateFlag": "1",
"inputFlag": "1",
"code": "A01001011",
"label": "注册时间",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001012",
"label": "注册渠道",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001013",
"label": "个人/企业",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001015",
"label": "所属行业",
"firstLevelCode": "01"
},
{
"calculateFlag": "3",
"inputFlag": "0",
"code": "A01001016",
"label": "是否实名",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001017",
"label": "实名认证类型",
"firstLevelCode": "01"
},
{
"calculateFlag": "1",
"inputFlag": "0",
"code": "A01001018",
"label": "实名认证时间",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001019",
"label": "性别",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001020",
"label": "年龄",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001021",
"label": "生日",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001022",
"label": "客户经理",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001023",
"label": "一人一码客户经理",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001024",
"label": "一人一码客户经理手机号",
"firstLevelCode": "01"
},
{
"calculateFlag": "3",
"inputFlag": "0",
"code": "A01001026",
"label": "是否一人一码注册用户(云大使注册)",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001027",
"label": "一人一码(云大使)二维码ID",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001028",
"label": "一人一码归属省",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001029",
"label": "一人一码归属地市",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001030",
"label": "一人一码归属区县",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01001031",
"label": "运营商",
"firstLevelCode": "01"
},
{
"calculateFlag": "3",
"inputFlag": "0",
"code": "A01002002",
"label": "是否资源在用用户",
"firstLevelCode": "01"
},
{
"calculateFlag": "3",
"inputFlag": "0",
"code": "A01002003",
"label": "是否测试用户",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01002005",
"label": "到期产品",
"firstLevelCode": "01"
},
{
"calculateFlag": "1",
"inputFlag": "0",
"code": "A01002006",
"label": "到期时间",
"firstLevelCode": "01"
},
{
"calculateFlag": "3",
"inputFlag": "0",
"code": "A01002008",
"label": "是否首次到期",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01002010",
"label": "在网时长",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01003001",
"label": "账户余额",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01003002",
"label": "欠费金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "2",
"code": "A01003003",
"label": "欠费次数",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01003004",
"label": "总出账金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01003005",
"label": "近3月出账金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01003006",
"label": "分产品出账金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01003010",
"label": "充值金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01003013",
"label": "充值方式",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01003015",
"label": "累计充值次数",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01003016",
"label": "累计充值金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01003017",
"label": "累计欠费次数",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01003018",
"label": "累计提现次数",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01003019",
"label": "日均按需费用",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01004016",
"label": "近30天充值次数",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "2",
"code": "A01004017",
"label": "近30天充值金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01004018",
"label": "累计订单标准价",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01004019",
"label": "累计订单降配实际价格",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01004020",
"label": "累计订单实际价格",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01004021",
"label": "累计订单现金金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01004022",
"label": "累计降配次数",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01004023",
"label": "累计降配订单标准价",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01004024",
"label": "累计降配订单现金",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01004025",
"label": "累计退订次数",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01004026",
"label": "累计退订金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01004027",
"label": "累计优惠券/代金券使用金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01005001",
"label": "产品名称",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01005002",
"label": "产品类型",
"firstLevelCode": "01"
},
{
"calculateFlag": "1",
"inputFlag": "0",
"code": "A01005011",
"label": "订单失效时间",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01005012",
"label": "订单按需或包周期",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01005013",
"label": "付费类型",
"firstLevelCode": "01"
},
{
"calculateFlag": "3",
"inputFlag": "0",
"code": "A01005021",
"label": "是否一人一码订单",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01006010",
"label": "资源个数",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01006011",
"label": "资源类型",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01006012",
"label": "资源使用时长",
"firstLevelCode": "01"
},
{
"calculateFlag": "1",
"inputFlag": "0",
"code": "A01006013",
"label": "资源最晚到期时间",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01008001",
"label": "优惠券/满折优惠券活动名称",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01008002",
"label": "优惠券号",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01008003",
"label": "优惠券名称",
"firstLevelCode": "01"
},
{
"calculateFlag": "1",
"inputFlag": "0",
"code": "A01008004",
"label": "优惠券领取时间",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01008005",
"label": "优惠券金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "1",
"inputFlag": "0",
"code": "A01008007",
"label": "优惠券到期时间",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01008009",
"label": "优惠券使用金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01008013",
"label": "优惠券使用条件",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01008014",
"label": "优惠券发放目的",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01008016",
"label": "满减折扣类型",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01008017",
"label": "代金券号码",
"firstLevelCode": "01"
},
{
"calculateFlag": "1",
"inputFlag": "0",
"code": "A01008019",
"label": "代金券领取时间",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01008020",
"label": "代金券金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "1",
"inputFlag": "0",
"code": "A01008022",
"label": "代金券到期时间",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01008031",
"label": "满折优惠券名称",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01008032",
"label": "满折优惠券券号",
"firstLevelCode": "01"
},
{
"calculateFlag": "1",
"inputFlag": "0",
"code": "A01008033",
"label": "满折优惠券领取时间",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01008034",
"label": "满折优惠券金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "1",
"inputFlag": "0",
"code": "A01008036",
"label": "满折优惠券到期时间",
"firstLevelCode": "01"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A01008038",
"label": "满折优惠券使用金额",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01008042",
"label": "满折优惠券使用条件",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01008054",
"label": "折扣券发放目的",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009001",
"label": "track",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009002",
"label": "推广单元",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009003",
"label": "推广关键词",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009004",
"label": "推广渠道",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009005",
"label": "引导登录页面名称",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009006",
"label": "引导登录页面模块",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009007",
"label": "引导登录注册模块",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009008",
"label": "引导页面登录URL",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009009",
"label": "引导页面注册URL",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009010",
"label": "引导注册页面名称",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009011",
"label": "注册访问来源(referer)",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009012",
"label": "注册track",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009013",
"label": "注册推广单元",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009014",
"label": "注册推广计划",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009015",
"label": "注册推广关键词",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009016",
"label": "注册推广渠道",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009017",
"label": "实名渠道",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009018",
"label": "实名track",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009019",
"label": "实名推广单元",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009020",
"label": "实名推广计划",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009021",
"label": "实名推广关键词",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009022",
"label": "订单渠道",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009023",
"label": "订单track",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009024",
"label": "订单推广单元",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009025",
"label": "订单推广计划",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01009026",
"label": "订单推广关键词",
"firstLevelCode": "01"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A01010002",
"label": "实名认证状态",
"firstLevelCode": "01"
}
]
},
{
"code": "02",
"label": "用户行为",
"child": [
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A02001016",
"label": "访问来源(referer)",
"firstLevelCode": "02"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A02001017",
"label": "访问终端类型",
"firstLevelCode": "02"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A02002001",
"label": "近30天浏览页面名称",
"firstLevelCode": "02"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A02003005",
"label": "CPU使用率",
"firstLevelCode": "02"
},
{
"calculateFlag": "2",
"inputFlag": "1",
"code": "A02003006",
"label": "内存使用率",
"firstLevelCode": "02"
},
{
"calculateFlag": "1",
"inputFlag": "0",
"code": "A02008001",
"label": "最近登录时间",
"firstLevelCode": "02"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A02009001",
"label": "故障产品名称",
"firstLevelCode": "02"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A02009002",
"label": "故障解决满意度",
"firstLevelCode": "02"
},
{
"calculateFlag": "0",
"inputFlag": "2",
"code": "A02009004",
"label": "故障原因",
"firstLevelCode": "02"
}
]
}
],
value: ''
}
}
}
15. el-cascader
在内部的 tags
单个标签删除时,getCheckedNodes
还是可以正常获取到删除之前的所有节点,导致处理数据时出现 bug
- 解决方式
这里我将
watch
中的同步代码,改为异步,之后就能正常读取到,删除之后的所有标签了
- 源码
// template => html
<el-cascader
v-else-if="item.type === 'cascader'"
v-model="basicInfo[item.name]"
:ref="`${item.name}Cascader`"
placeholder="全部"
:options="commonData[item.selectKey]"
:props="{
multiple: true,
checkStrictly: true,
label: 'activityName',
children: 'child',
value: 'activityName'
}"
filterable
collapse-tags
clearable
></el-cascader>
// js => watch
/**
* 监测活动字段,手动赋值
*/
'basicInfo.activeName': {
handler(val) {
// 同步会有 bug(tags 删除时,getCheckedNodes 还是可以正常获取到),所以做成异步的
this.$nextTick(() => {
if (
this.$refs['activeNameCascader'] &&
this.$refs['activeNameCascader'][0]
) {
// 因为组件是在一个 `v-for`循环中生成的,所以需要使用 [0]
const activeCascader = this.$refs['activeNameCascader'][0]
const selectedData = this.$refs[
'activeNameCascader'
][0].getCheckedNodes()
// 初始化值时有两种情况:
// 1 表头需要动态展示的 也就是 activityName 和 eventTypeName 设置为 null
// 2 表头需要固定展示的
let activityType = '',
activityName = null,
eventTypeName = null
const { dataDetailShow, pageNavValue } = this
if (
dataDetailShow ||
['RelevanceBuyStatistics'].includes(pageNavValue)
) {
activityName = eventTypeName = ''
}
// 有选中值才进行操作
if (selectedData.length) {
const levelOne = selectedData.filter((o) => {
const { level, checked } = o
return level === 1 && checked
})
if (levelOne.length) {
activityType = levelOne
.map((element) => {
return element.value
})
.join(',')
}
const levelTwo = selectedData.filter((o) => {
const { level, checked } = o
return level === 2 && checked
})
if (levelTwo.length) {
activityName = levelTwo
.map((element) => {
return element.value
})
.join(',')
}
const levelThree = selectedData.filter((o) => {
const { level, checked } = o
return level === 3 && checked
})
// levelThree 需要截取下划线之后的名称
if (levelThree.length) {
let resultFirstName = []
levelThree.forEach((element) => {
const { value } = element
const index = value.indexOf('_')
if (index !== -1) {
resultFirstName.push(value.substring(index + 1))
}
})
eventTypeName = resultFirstName.join(',')
}
}
// 赋值
this.$set(this.basicInfo, 'activityType', activityType)
this.$set(this.basicInfo, 'activityName', activityName)
// 活动用户关联购买统计 字段名不同
if (!['RelevanceBuyStatistics'].includes(pageNavValue)) {
this.$set(this.basicInfo, 'eventTypeName', eventTypeName)
} else {
this.$set(this.basicInfo, 'activityFirstName', eventTypeName)
}
}
})
}
}
16. el-table
自定义表头列表中某个单元格的样式
- 源码:
<template>
<el-table
:data="tableData"
style="width: 100%"
:header-cell-style="headerCellStyle">
<el-table-column
prop="date"
label="各省"
width="150">
</el-table-column>
<el-table-column label="宽带">
<el-table-column
prop="name"
label="姓名"
width="120">
</el-table-column>
<el-table-column label="移动">
<el-table-column
prop="province"
label="省份"
width="120">
</el-table-column>
<el-table-column
prop="city"
label="市区"
width="120">
</el-table-column>
<el-table-column
prop="address"
label="地址"
width="300">
</el-table-column>
<el-table-column
prop="zip"
label="邮编"
width="120">
</el-table-column>
</el-table-column>
</el-table-column>
</el-table>
</template>
<script>
export default {
data() {
return {
tableData: [
{
date: "2016-05-03",
name: "王小虎",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333
},
{
date: "2016-05-02",
name: "王小虎",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333
},
{
date: "2016-05-04",
name: "王小虎",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333
},
{
date: "2016-05-01",
name: "王小虎",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333
},
{
date: "2016-05-08",
name: "王小虎",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333
},
{
date: "2016-05-06",
name: "王小虎",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333
},
{
date: "2016-05-07",
name: "王小虎",
province: "上海",
city: "普陀区",
address: "上海市普陀区金沙江路 1518 弄",
zip: 200333
}
]
};
},
methods: {
/**
* 自定义表头列表中某个单元格的样式
*/
headerCellStyle({ column }) {
const whitelist = ['各省', '宽带', '移动', '5G']
if (!whitelist.includes(column.label)) {
return {
backgroundColor: 'red',
color: '#333'
}
} else {
return {}
}
}
}
}
</script>
17. el-table
多列合并单元格,可以指定某列
- 源码:
<template>
<div>
<!-- 指定 count 取默认属性 -->
<el-table
:data="tableData"
:span-method="arraySpanMethod"
border
style="width: 100%">
<el-table-column
prop="id"
label="ID"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名">
</el-table-column>
<el-table-column
prop="amount1"
sortable
label="数值 1">
</el-table-column>
<el-table-column
prop="amount2"
sortable
label="数值 2">
</el-table-column>
<el-table-column
prop="amount3"
sortable
label="数值 3">
</el-table-column>
</el-table>
<hr />
<!-- 指定 props -->
<el-table
:data="tableData"
:span-method="arraySpanMethod2"
border
style="width: 100%">
<el-table-column
prop="id"
label="ID"
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名">
</el-table-column>
<el-table-column
prop="amount1"
sortable
label="数值 1">
</el-table-column>
<el-table-column
prop="amount2"
sortable
label="数值 2">
</el-table-column>
<el-table-column
prop="amount3"
sortable
label="数值 3">
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
tableData: [
{
id: "12987122",
name: "王小1虎",
amount1: "234",
amount2: "3.2",
amount3: 10
},
{
id: "12987121",
name: "王小虎",
amount1: "324",
amount2: "4.43",
amount3: 12
},
{
id: "12987125",
name: "王小虎",
amount1: "621",
amount2: "1.9",
amount3: 9
},
{
id: "12987125",
name: "王小21虎",
amount1: "621",
amount2: "2.2",
amount3: 17
},
{
id: "12987126",
name: "王小2虎",
amount1: "539",
amount2: "2.2",
amount3: 15
}
],
mergeResult: [],
mergeResult2: [],
props: ["name", "amount2"]
};
},
created() {
// 指定 count 取默认属性
this.mergeResult = this.mergeColumnCells(this.tableData, 2);
// 指定 props
this.mergeResult2 = this.mergeColumnCells(this.tableData, 0, this.props);
console.log(this.mergeResult);
console.log(this.mergeResult2);
},
methods: {
/**
* 多列合并单元格,可以指定某列,下面的 count 和 props 参数,虽然都是非必须的,但是至少得保证有一个
* @param {Array} data 需要处理的数据
* @param {Number} count 非必须参数:需要合并单元格的列的总数量(满足从初始第一列至连续的 count 列的条件)
* @param {Array} props 非必须参数:需要合并单元格的数据列对应的属性列表,示例:['id','name']
* @returns {Array} 储存嵌套的每一列需要合并单元格的合并的格数,示例:[[1,1,2,0,1],[1,2,0,1,1]]
* 两种不同方式的调用示例:
* 指定 count 取默认属性:this.mergeResult = this.mergeColumnCells(this.tableData, 2);
* 指定 props:this.mergeResult = this.mergeColumnCells(this.tableData, 0, this.props);
* 完整的在线示例地址 [element table 多列合并单元格,可以指定某列](https://codepen.io/sunxiaochuan/pen/wvoOXzG)
*/
mergeColumnCells(data, count, props) {
if (data && data.length && (count || props)) {
// 先获取到需要合并的属性列表
props = props || [];
// 不存在的话就依照 count 字段开始取默认名称
if (!props.length) {
for (const key in data[0]) {
if (data[0].hasOwnProperty(key)) {
props.push(key);
}
if (props.length === count) {
break;
}
}
}
let saveData = [];
for (let index = 0; index < props.length; index++) {
const prop = props[index];
// 设置初始化值
saveData[index] = [];
let dataList = saveData[index];
// 标记属性值重复的位置
let position;
// 数据计算
for (let i = 0; i < data.length; i++) {
const element = data[i];
// 逻辑:默认的第一条 i === 0 数据将初始化单元格数量为 1,并将标记位置初始为 0;从第二条 i === 1 数据开始与上一条数据做属性比较:一致的话被标记位置的单元格 += 1,相应的当前单元格需要置为 0;否则当前单元格为 1,并重新将当前的索引设置为初始标记位置
if (i === 0) {
dataList.push(1);
position = 0;
} else {
// 判断与上一条数据对应的属性值是否一致,一致的话被标记位置的单元格 += 1,并设置当前的单元格为 0;否则当前单元格为 1,并重新将当前的索引设置为初始标记位置
const prev = data[i - 1];
if (element[prop] === prev[prop]) {
dataList[position] += 1;
dataList.push(0);
} else {
dataList.push(1);
position = i;
}
}
}
}
return saveData;
} else {
return [];
}
},
arraySpanMethod({ row, column, rowIndex, columnIndex }) {
if (columnIndex < 2) {
const _row = this.mergeResult[columnIndex][rowIndex];
return [_row, 1];
}
},
// 指定 props
arraySpanMethod2({ row, column, rowIndex, columnIndex }) {
const index = this.props.indexOf(column.property);
if (index != -1) {
const _row = this.mergeResult2[index][rowIndex];
return [_row, 1];
}
}
}
}
</script>
18. el-table
多列排序
- 源码:
<template>
<el-table :data="tableData" style="width: 100%" :header-cell-class-name='handleHeadAddClass' @sort-change='tableSortChange'>
<el-table-column prop="date" label="日期" sortable width="180">
</el-table-column>
<el-table-column prop="name" label="姓名" sortable width="180">
</el-table-column>
<el-table-column prop="address" label="地址">
</el-table-column>
</el-table>
</template>
<script>
export default {
data() {
return {
tableData: [
{
date: "2016-05-02",
name: "李小虎",
address: "上海市普陀区金沙江路 1518 弄"
},
{
date: "2016-05-04",
name: "王小虎",
address: "上海市普陀区金沙江路 1517 弄"
},
{
date: "2016-05-01",
name: "安小虎",
address: "上海市普陀区金沙江路 1519 弄"
},
{
date: "2016-05-03",
name: "王小虎",
address: "上海市普陀区金沙江路 1516 弄"
}
],
sortPropList: {}
};
},
methods: {
/**
* 报表所有表头都可以进行排序,1 升;2 降
*/
tableSortChange({ column, prop, order }) {
let sortType = undefined;
switch (order) {
case "ascending":
sortType = "1";
break;
case "descending":
sortType = "2";
break;
default:
break;
}
this.sortPropList[prop] = order;
// 获取数据
// this.$set(sortProp, 'sortType', sortType)
// this.submitFilterForm();
},
/**
* 增加多列排序
*/
handleHeadAddClass({ column }) {
if (this.sortPropList[column.property]) {
column.order = this.sortPropList[column.property];
}
}
}
}
</script>
19. 自定义 el-pagination
组件中的的文本内容的方法
- 新建一个
@/assets/locale/cn.js
文件,内容如下,具体参照 官方源码地址
export default {
el: {
colorpicker: {
confirm: '确定',
clear: '清空'
},
datepicker: {
now: '此刻',
today: '今天',
cancel: '取消',
clear: '清空',
confirm: '确定',
selectDate: '选择日期',
selectTime: '选择时间',
startDate: '开始日期',
startTime: '开始时间',
endDate: '结束日期',
endTime: '结束时间',
prevYear: '前一年',
nextYear: '后一年',
prevMonth: '上个月',
nextMonth: '下个月',
year: '年',
month1: '1 月',
month2: '2 月',
month3: '3 月',
month4: '4 月',
month5: '5 月',
month6: '6 月',
month7: '7 月',
month8: '8 月',
month9: '9 月',
month10: '10 月',
month11: '11 月',
month12: '12 月',
// week: '周次',
weeks: {
sun: '日',
mon: '一',
tue: '二',
wed: '三',
thu: '四',
fri: '五',
sat: '六'
},
months: {
jan: '一月',
feb: '二月',
mar: '三月',
apr: '四月',
may: '五月',
jun: '六月',
jul: '七月',
aug: '八月',
sep: '九月',
oct: '十月',
nov: '十一月',
dec: '十二月'
}
},
select: {
loading: '加载中',
noMatch: '无匹配数据',
noData: '无数据',
placeholder: '请选择'
},
cascader: {
noMatch: '无匹配数据',
loading: '加载中',
placeholder: '请选择',
noData: '暂无数据'
},
pagination: {
goto: '前往',
pagesize: '个用户/页',
total: '共 {total} 个用户',
pageClassifier: '页'
},
messagebox: {
title: '提示',
confirm: '确定',
cancel: '取消',
error: '输入的数据不合法!'
},
upload: {
deleteTip: '按 delete 键可删除',
delete: '删除',
preview: '查看图片',
continue: '继续上传'
},
table: {
emptyText: '暂无数据',
confirmFilter: '筛选',
resetFilter: '重置',
clearFilter: '全部',
sumText: '合计'
},
tree: {
emptyText: '暂无数据'
},
transfer: {
noMatch: '无匹配数据',
noData: '无数据',
titles: ['列表 1', '列表 2'],
filterPlaceholder: '请输入搜索内容',
noCheckedFormat: '共 {total} 项',
hasCheckedFormat: '已选 {checked}/{total} 项'
},
image: {
error: '加载失败'
},
pageHeader: {
title: '返回'
},
popconfirm: {
confirmButtonText: '确定',
cancelButtonText: '取消'
}
}
};
- 在
main.js
中引用即可,参考 官方文档地址
import locale from '@/assets/locale/cn.js'
Vue.use(ElementUI, { locale })
- 我这里主要修改的是分页的文本,显示结果如下图所示
后续
- 上面是修改全局统一配置的方法,但是我这个需求并不适用,还是需要局部自定义来实现的。下面是主要的代码部分
el-pagination(
v-show='total > 0',
@size-change='handleSizeChange',
@current-change='handleCurrentChange',
:current-page='tableParams.pageNum',
:page-sizes='[1, 2, 3, 4, 5, 10, 20, 50, 100]',
:page-size='tableParams.pageSize',
layout='slot, prev, pager, next, jumper',
:total='total'
)
span.el-pagination__total 共 {{ total }} 个用户
el-select(
v-model='tableParams.pageSize',
@change='handleSizeChange(tableParams.pageSize)',
size='mini',
placeholder='请选择'
)
el-option(
v-for='item in pageSizeList',
:key='item.value',
:label='item.name',
:value='item.value'
)
pageSizeList: [
{
name: '1个用户/页',
value: 1
},
{
name: '2个用户/页',
value: 2
},
{
name: '3个用户/页',
value: 3
},
{
name: '4个用户/页',
value: 4
},
{
name: '5个用户/页',
value: 5
},
{
name: '10个用户/页',
value: 10
},
{
name: '20个用户/页',
value: 20
},
{
name: '50个用户/页',
value: 50
},
{
name: '100个用户/页',
value: 100
}
]
20. el-form
组件自定义规则时,需要注意的地方
一定要在最后执行
callback()
,否则在this.$refs.ruleForm.validate((valid) => {})
检验的时候,无法执行
{}
中的内容。
ruleFormRules: {
userGroupName: [
{
validator: (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入人群名称'))
} else if (value.includes('_')) {
callback(new Error('不能包含_'))
} else {
callback()
}
},
trigger: 'blur'
}
],
updateType: [
{ required: true, message: '请选择更新方式', trigger: 'change' }
]
},
21. el-date-picker
组件动态更新 type
属性时,时间选择弹窗会出现错位的问题
- 解决方法:增加一个
key
属性,我是与type
值保持一致了
<el-date-picker
v-model="item.fieldValue"
:type="
item.type === '1'
? dateDictionary.type[item.dropDatas.code]
: dateDictionary.singleType[item.dropDatas.code]
"
:value-format="dateDictionary.value[item.dropDatas.code]"
:picker-options="pickerOptions"
:key="
item.type === '1'
? dateDictionary.type[item.dropDatas.code]
: dateDictionary.singleType[item.dropDatas.code]
"
/>