环境搭建
https://element.eleme.cn/#/zh-CN/component/installation
1、根据官网提示,npm i element-ui
小坑:
终端在运行,无法安装 |
解决办法:ctrl + C停止终端 |
安装完成后,可在package.json查看到element UI的版本
"dependencies": {
"element-ui": "^2.15.3",
"vue": "^2.5.2",
"vue-router": "^3.0.1"
},
2、根据提示需要输入npm audit fix :检测项目依赖中的漏洞并自动安装需要更新的有漏洞的依赖,而不必再自己进行跟踪和修复。
https://blog.csdn.net/weixin_40817115/article/details/81007774
3、在 main.js 中导入element-ui:(这些官网都有)
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
4、随便找一个组件模板,复制template里的代码,运行测试。
好用的插件
【pinyin-match】拼音匹配
https://blog.csdn.net/Jie_1997/article/details/125557403
评价:这个插件其实不好用,需要切换成英文输入法才识别拼音。
1、安装:npm install pinyin-match --save
2、在组件中引入:import PinYinMatch from "pinyin-match";
3、data中定义两个数组:
listCharge: [], //初始的数据
listCharge2: [], //拼音匹配后的结果
4、el-select标签中定义属性:
注1:@visible-change表示下拉框隐藏的时候触发的事件。用来重置下拉选项。因为当你输入过拼音后,搜索结果变少了。所以需要借助这个事件来重置下拉选项。
注2:filterable表示下拉框可以有输入功能。
注3:filter-method表示数据过滤使用的方法。这个方法默认传入一个参数value,表示当前输入的值。如果想要传入自定义的参数,可以这样写:(value) => xxx(value, 自定义参数)
<el-select v-model="xxx"
@visible-change="handleEventrProject"
filterable
:filter-method="matchPinYin2">
5、@visible-change绑定的方法
//当下拉框消失,要让下拉选项变回到原来的,含有所有选项
handleEventrProject() {
this.projectList2 = this.projectList;
this.listCharge2 = this.listCharge;
},
6、filter-method绑定的方法
matchPinYin2(val) { //这个val是默认传入的
if (val) {
let result = [];
this.listCharge.forEach((item) => {
//匹配不上,则返回false。
//匹配上,则返回包含两个元素的数组,第一个元素表示从第几位开始匹配,第二个元素表示匹配上了几个。
let matchRes = PinYinMatch.match(item.xxx, val); //这里xxx是label对应的属性
if (matchRes) {
result.push(item);
}
});
this.listCharge2 = result;
} else {
this.listCharge2 = this.listCharge;
}
},
【element-china-area-data】省市区选择器
博文:https://blog.csdn.net/bamboozjy/article/details/82220894
官网:https://blog.csdn.net/bamboozjy/article/details/82220894
安装插件:npm install element-china-area-data -S
在需要使用的页面,进行引入:
import {
provinceAndCityData, //省市二级联动数据(不带“全部”选项)
regionData, //省市区三级联动数据(不带“全部”选项)
provinceAndCityDataPlus, //省市区三级联动数据(带“全部”选项)
regionDataPlus,省市区三级联动数据(带“全部”选项)
CodeToText, //是个大对象,属性是区域码,属性值是汉字 用法例如:CodeToText['110000']输出北京市
TextToCode //是个大对象,属性是汉字,属性值是区域码 用法例如:TextToCode['北京市'].code输出110000,TextToCode['北京市']['市辖区'].code输出110100,TextToCode['北京市']['市辖区']['朝阳区'].code输出110105
} from 'element-china-area-data'
示例:
<template>
<div style="margin: 50px;">
<el-cascader size="large"
:options="options"
v-model="selectedOptions"
@change="addressChange">
</el-cascader>
</div>
</template>
<script>
import { regionData, CodeToText } from "element-china-area-data";
export default {
data() {
return {
options: regionData,
selectedOptions: [],
};
},
methods: {
addressChange(arr) {
console.log(arr);
console.log(CodeToText[arr[0]], CodeToText[arr[1]], CodeToText[arr[2]]);
},
},
};
</script>
【el-table-infinite-scroll】无限加载数据
https://blog.csdn.net/cxwtsh123/article/details/127806045
安装:npm install --save el-table-infinite-scroll
在main.js中引入:import ElTableInfiniteScroll from 'el-table-infinite-scroll';
在main.js中使用:Vue.use(ElTableInfiniteScroll)
坑:启动以后报错!!!说vue缺少一个东西。我推测我安装了最新版的el-table-infinite-scroll,它需要用到vue3中的依赖。但是我用的是vue2,所以我指定了版本1.0.10
临时记一下:
1、2、6
v-el-table-infinite-scroll="loadMore"
ref="tableRef"
height="670"
stripe
3、5
loadMore() {
if (this.queryParams.pageNum * this.queryParams.pageSize > this.total) {
console.log("已经到底啦!");
return;
}
this.queryParams.pageNum++;
this.loading = true;
listUser(this.queryParams).then(
(response) => {
const newData = response.rows;
this.userList = this.userList.concat(newData);
this.loading = false;
}
);
},
4、handleQuery
if (this.queryParams.pageNum != 1) {
this.$refs.tableRef.bodyWrapper.scrollTop = 0;
}
7、分页改成15,把getList替换成handleQuery
关键步骤其实就这几步:
1)设置表格高度
2)给表格添加属性v-el-table-infinite-scroll,绑定触底事件
3)触底事件给分页+1,然后数组拼接。
const newData = response.rows; //新数据
this.xxxData= this.xxxData.concat(newData); //用concat拼接
4)让表格回到顶部:先让table绑定ref,再执行:
if (this.queryParams.pageNum != 1) {
this.$refs.xxxRef.bodyWrapper.scrollTop = 0;
}
5)触底
if (this.queryParams.pageNum * this.queryParams.pageSize > this.total) {
console.log("已经到底啦!");
return;
}
子组件:
1、标签
<el-table :data="tableData"
v-el-table-infinite-scroll="load"
:infinite-scroll-disabled="disabled"
:height="500"
style="width: 100%; margin-top: 20px;"
:stripe="true"
:border="true"
:highlight-current-row="true">
2、参数
props: {
tableData: { type: Array },
tableColumn: { type: Array },
disabled: { type: Boolean, default: true },
scrollLoading: { type: Boolean },
},
3、在js中定义方法
load() {
console.log("这里是子组件,下拉滚动加载触发了……");
//子组件调用父组件中的方法,用$emit
this.$emit("update:scrollLoading", true);
this.$emit("loadMore"); //自定义事件loadMore
},
4、监听
watch: {
scrollLoading(val) {
this.scrollLoading = val;
},
},
父组件
1、标签
<HhmTable :tableData="tableData"
:tableColumn="tableColumn"
@loadMore="loadMoreToDo"
:scrollLoading.sync="scrollLoading"
:disabled="disabled"
ref="hhm_table_ref"></HhmTable>
2、data
scrollLoading: false, //是否加载
disabled: false, //是否不能加载
3、自定义事件
//滚动加载更多数据
loadMoreToDo() {
console.log("这里是父组件");
if (this.disabled) return;
this.searchModel.pageNum++; //加载下一页内容
this.$http
.get("/processHis/page", {
params: this.searchModel,
})
.then((e) => {
const newData = e.data.data.records;
console.log("新的数据", newData);
const newTableData = this.tableData.concat(newData); //合并数组
console.log("表格数据:", newTableData);
this.tableData = newTableData;
});
},
【v-infinite-scroll】无限加载数据
这个可以用在div身上。
v-infinite-scroll="loadMore"
【sortablej】表格的行列可拖拽
快速上手示例:https://blog.csdn.net/weixin_45232247/article/details/125411977
sortablej官网:http://www.sortablejs.com/
安装插件:
npm install sortablejs --save
引入插件:
import Sortable from "sortablejs";
data中分别定义初始的prop列和变化后的prop列:
表格的动态列 dropCol: [{ 序号", prop: "index", }, { 贡献值", prop: "contributionValue", } ], |
生命周期:
mounted() {
// 阻止默认行为
document.body.ondrop = function(event) {
event.preventDefault();
event.stopPropagation();
};
this.$nextTick(() => {
this.columnDrop(); //列拖拽
})
},
方法实现:
//列拖拽
columnDrop() {
const wrapperTr = document.querySelector(".el-table__header-wrapper tr");
this.sortable = Sortable.create(wrapperTr, {
animation: 180,
delay: 0,
onEnd: (evt) => {
const oldItem = this.dropCol[evt.oldIndex];
this.dropCol.splice(evt.oldIndex, 1);
this.dropCol.splice(evt.newIndex, 0, oldItem);
},
});
},
难点1:有多行tr时,选择问题。
document.querySelector(".el-table__header-wrapper tr");
querySelector是查询所有???
后代选择器,所有儿子、孙子tr
改用:
const wrapperTr = document.querySelectorAll(".el-table__header-wrapper tr");
this.sortable = Sortable.create(wrapperTr[1], {这里看情况选择第几行
小胖还用了mounted中的setTimeout来解决,更好的是this.$nextTick(()=>{...})
难点2:含有插槽的复杂场景:
<!-- 循环渲染字段,实现列可拖拽,需要让prop绑定dropCol -->
<el-table-column v-for="(item, index) in colList"
:key="`col_${index}`"
:prop="dropCol[index].prop"
:label="item.label">
<!-- 处理特殊数据,用具名插槽 -->
<template v-slot="scope">
<span v-if="dropCol[index].prop === 'index'">
{{taskList.indexOf(scope.row) + 1}}
</span>
<span v-else-if="dropCol[index].prop === 'startAndEndTime'">
{{parseTime(scope.row.startTime, "{h}:{i}") + " - " + parseTime(scope.row.endTime, "{h}:{i}")}}
</span>
<span v-else>
{{ scope.row[dropCol[index].prop] }}
</span>
</template>
</el-table-column>
【v-dialogDrag】弹框可拖拽
https://blog.csdn.net/Gx0525_/article/details/122713157?spm=1001.2014.3001.5506
这不是插件。
不知道是不是只能用在el-dialog身上。
【vue-count-to】动画:让数字滚动加载
https://blog.csdn.net/m0_51431448/article/details/125794123
安装:npm install vue-count-to
引入组件:import CountTo from 'vue-count-to'
注册组件:
components: { CountTo }, |
模板中使用:
<CountTo :startVal='startVal' :endVal='endVal' :duratinotallow='duration' /> |
属性说明:
duration是动画的时间,单位为毫秒。
注意:用了这个插件以后,会自动把数字转换成国外的表达方式,即每三位用逗号隔开。
示例:
<div slot="header">{{item.title}}</div>
<div>
<CountTo :startVal="0"
:endVal="item.value"
:duration="3000" />
</div>
【vue-print-nb】打印
快速上手
https://blog.csdn.net/weixin_44867717/article/details/128168044
1、在控制台安装插件:npm i vue-print-nb
2、在main.js中引入插件:import Print from 'vue-print-nb'
3、在main.js中全局引用该插件:Vue.use(Print)
4、在组件中使用插件:
<div id="single-info">这是要打印的内容</div> <el-button id="info-btn" v-print="'#single-info'" @click="prints" >打印</el-button > |
注:在按钮上使用v-print,绑定id,这个id就是你想要打印的内容。
批量打印
我的思路:把所有要打印的内容放在一个div中,然后用css让它隐藏。
坑:表格显示不全
https://blog.csdn.net/liangyiyiliang/article/details/124451553
这是网页:
这是打印预览:
原因:原来是el-table 会计算100%宽度为具体多少px 然后再计算出每一列的宽度,通过设置table>colgroup>col 的width属性来设置每一列的宽度。问题是浏览器的打印区域的100%的具体px(如这里的1388px)肯定与打印纸的宽度不一致,所以导致部分区域打印不出来!
------------------【basic】-----------------------
【button】按钮
属性
type | 按钮的颜色 注:用vsCode编写这个属性,没有提示,不要害怕,这是正常的。 | primary:蓝色 success:绿色 info:灰色 warning:橙色 danger:红色 |
示例:
<el-button type="primary" @click="xxx()">发送</el-button>
------------------【form】-----------------------
【radio】单选框
https://element.eleme.cn/#/zh-CN/component/radio
标签【radio-group】单选框组
要使用 Radio 组件,只需要设置v-model绑定变量,选中意味着变量的值为相应 Radio label属性的值,label可以是String、Number或Boolean。
角色类型"> 表单名.字段名"> 值1">管理员</el-radio> 值2">学生</el-radio> </el-radio-group> </el-form-item> |
注:
- 本来以为label是标签,结果这里label就是值!而展示的内容直接写在标签体里面。
- radio-group绑定的是一个普通对象。
属性【label】标签的值
本来以为label是标签,结果这里label就是值!而展示的内容直接写在标签体里面。
【checkbox】多选框
标签【checkbox-group】多选框组
checkbox-group元素能把多个 checkbox 管理为一组,只需要在 Group 中使用v-model绑定Array类型的变量即可。 el-checkbox 的 label属性是该 checkbox 对应的值,若该标签中无内容,则该属性也充当 checkbox 按钮后的介绍。label与数组中的元素值相对应,如果存在指定的值则为选中状态,否则为不选中。
<el-checkbox-group v-model="checkList">
<el-checkbox label="复选框 A"></el-checkbox>
<el-checkbox label="复选框 B"></el-checkbox>
<el-checkbox label="复选框 C"></el-checkbox>
</el-checkbox-group>
data() {
return {
checkList: ["复选框 A"],
};
},
注1:checkbox-group绑定的是一个数组!!!
注2:label是值,不是标签
注3:checkbox-group设置了样式font-size=0,导致文本、<i>都看不见了!!!可以重新定义样式来解决。
属性【label】标签的值
本来以为label是标签,结果这里label就是值!
展示的内容:直接写在标签体里面。
注:如果标签体里面没有内容,则label也会作为内容展现出来。
也许可以用true-label和false-label来实现无展示的内容的效果。
需求:如何让方框在内容右侧?
【input】输入框
需求:修改样式
::v-deep .el-input__inner {
border-radius: 0px;
border-top-width: 0px;
border-left-width: 0px;
border-right-width: 0px;
border-bottom-width: 1px;
/*outline: medium;*/
}
需求:只能输入整数(正则表达式)
由于type=number不能限制长度,而且可以输入e、小数点、Π等数学相关的符号,不好用!
所以改用type=text,配合正则表达式。
type="text"
maxlength="3"
oninput="value=value.replace(/[^\d]/g,'')"
用notallow="value=value.replace(/[^\d]/g,'')"的正则表达式来把除了数字以外的字符都变为空。
【select】选择器
基本结构
<template> <el-select v-model="xxx" placeholder="请选择"> <el-option v-for = "iteminxxx" :key = "item.xxx" :label = "item.xxx" :value = "item.xxx"> </el-option> </el-select> </template> |
说明:这个属性key,我感觉没用,但是不加的话会有warning,所以还是加上吧:
坑:el-select只显示值,不显示标签。el-option的标签可以正常显示。
原因:select和option的value的类型匹配不上!经常都是string和int搞错了。
属性【filterable】可搜索
是否可搜索,就是变成输入框,搜出label
【cascader】级联选择器
<el-cascader v-model = "xxxValue" :options = "xxxOptions" :props = "{ expandTrigger: 'hover' }" @change = "触发的方法" > </el-cascader> |
【v-model】选中的值
前端填入这个级联选择器的值,数组的形式,从0开始分别表示一级二级三级。
属性【options】选项值
可选择的项,这个数组必须是如下结构(也可以通过Props属性自定义键名):
{value: label: children: [ { value: label: } ]} |
需要通过一个接口去后端捞出所有选项,再赋值给options。(通常我们在created生命周期中调用方法,一开始就捞到这些数据)
dictGroup({}).then((resp) => { 为什么要先清空? resp.data.forEach((item) => { const v = item.id; let row = { label: item.groupName, value: item.id, children: [] };//options固定的结构 item.groupList.forEach((y) => { row.children.push({ label: y.staffName, value: y.staffId }); }); this.staffTreeOption.push(row); }); this.getList(); }); |
属性【props】自定义键名
用法:先在标签中定义props,然后在data中指定value、label、children
示例:
在标签中定义props
<el-cascader v-model="address"
:options="cityMap"
:props="props111">
</el-cascader>
在data中指定
props111: {
label: "province",
value: "code",
children: "cities",
},
事件【@change】
当选中节点变化时触发。即:级联选择器选好后会触发。比如我这里绑定一个自定义的方法handleChange,默认会传入event(就是这里的value)
handleChange(value) { if (this.deptValue != null){ this.listQuery.xxx= this.xxxValue[1]; //可以用props中的一个配置,关掉这个复杂的父子结构,从而只显示子节点的数据,就不用这样麻烦了,而且父子结构会影响到v-model的值,从而让后端取不到。 } } |
emitPath:建议设为false 在选中节点改变时,是否返回由该节点所在的各级菜单的值所组成的数组,若设置 false,则只返回该节点的值 |
【DatePicker】日期选择器
示例:
<el-date-picker v-model="changeTimeParam"
type="daterange"
value-format="yyyy-MM-dd"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
size="mini"
style="width: 250px">
</el-date-picker>
属性【value】绑定数据
或者自定义一个v-model来绑定数据。
当我们把type设为daterange时,打印一下value绑定的数据对象的格式,发现是数组:
注:这个数组对象不能为空,否则报错。请留意后期查询赋值时如果为空,给它设为两个元素都是空字符串的数组。
坑:Element提供的清空按钮,会把这个数组变成null,导致报错!!!
属性【type】选择器的类型
值1:daterange,日期范围。此时value是一个数组,有开始日期和结束日期。
值2:date,日期
属性【value-format】日期格式(和后端联调)
值1:yyyy-MM-dd,年月日
值2:value-format="yyyy-MM-dd HH:mm:ss"
属性【format】日期格式(前端展现)
可以随心所欲地搭配yyyyMMdd这些。
示例:
format="yyyy年MM月dd日"
属性【prefix-icon】设置头部的图标
默认:el-icon-date
效果:
我试了一下,可以设置它的值为none,就能去掉图标。
prefix-icon="none"
设置宽度
只能自定义一个行内样式来修改宽度
style="width: 250px"
事件【change】
触发:用户确认选定的值时
组件绑定值。格式与绑定值一致,可受 value-format 控制
【Form】表单
样式:简单粗暴去掉所有选择器的外框
定义一个样式xxxclass,想让哪个选择器的外框隐藏,就给他加这个class
.xxxclass /deep/ * {
border: none;
}
属性【inline】变为行内元素
“设置 inline 属性可以让表单域变为行内的表单域”
默认会换行的,不实用!
格式:
:inline="true"
标签【el-form-item】
表单验证(常规)
https://www.cnblogs.com/wozho/p/10955525.html
环境搭建:安装async-validator(Element自带了,我还搞了半天,被我自己蠢死了。)
1、在<el-form>中设置参数:
:model="要检查的表单对象" :rules="rules" ref="表单标识名" |
2、在<el-form-item>中设置参数:
prop的属性来自第1步中model表单对象中的属性。
“Form 组件提供了表单验证的功能,只需要通过 rules 属性传入约定的验证规则,并将 Form-Item 的 prop 属性设置为需校验的字段名即可。”
prop="属性名" |
3、定义表单验证的规则rules
rules: {
xxx: [
{ required: true, message: "xxx不能为空", trigger: "blur" },
],
changeTime: [
{ required: true, message: "出院时间不能为空", trigger: "blur" },
]
},
4、按钮的方法传入表单:@click="saveUser('表单名')"
5、方法里怎么写:
saveUser(formName) { this.$refs[formName].validate((valid) => { if (valid) { 合法的情况 …… } else { //不合法的情况 …… return false; } }); }, |
表单校验(自定义校验规则)
需求:手机号格式校验、身份证格式校验
1、rules里面:
rules: {
idCard: [
{ required: false, validator: checkIdCard, trigger: "change" },
],
membersPhone: [
{ required: false, validator: checkPhone, trigger: "blur" },
],
2、data中定义属性(注意,这不是在return中定义的,是在外层)
data() {
// 身份证验证
var checkIdCard = (rule, value, callback) => {
const idcardReg =
/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
if (!value) {
//暂时可以为空
// return callback(new Error("身份证号不能为空"));
callback(); //注:callback方法必须被调用
}
//有值,才进行格式校验
if (value) {
setTimeout(() => {
if (idcardReg.test(value)) {
this.selectBirAndSex(value);
var now = new Date();
var birthday = new Date(this.xxx.birthday);
if (birthday.getTime() > now.getTime()) {
callback(new Error("身份证号异常,出生日期异常"));
}
callback();
} else {
callback(new Error("身份证格式不正确"));
}
}, 100);
}
};
//手机号校验
var checkPhone = (rule, value, callback) => {
let regPone = null;
let mobile = /^((13|14|15|17|18)[0-9]{1}\d{8})$/; // 最新16手机正则
let tel = /^((0\d{2,3}\d{7,8})|(1[3584]\d{9}))$/; // 座机
if (!value) {
//暂时可以为空
// return callback(new Error("身份证号不能为空"));
callback(); //注:callback方法必须被调用
}
//有值,才进行格式校验
if (value) {
setTimeout(() => {
// Number.isInteger是es6验证数字是否为整数的方法,但是我实际用的时候输入的数字总是识别成字符串
// 所以我就在前面加了一个+实现隐式转换
if (!Number.isInteger(+value)) {
callback(new Error("请输入数字值"));
} else {
if (value !== null && value.charAt(0) === "0") {
// charAt查找第一个字符方法,用来判断输入的是座机还是手机号
regPone = tel;
} else if (value !== null && value.charAt(0) !== "0") {
regPone = mobile;
}
if (regPone.test(value)) {
callback();
} else {
callback(
new Error("请输入正确的电话格式,其中座机(区号+座机号码)")
);
}
}
}, 100);
}
};
3、方法中定义
/** 身份证查询出生日期,性别 */
selectBirAndSex(val) {
let iden = this.form.idCard;
let sex = null;
let birth = null;
let myDate = new Date();
let month = myDate.getMonth() + 1;
let day = myDate.getDate();
let age = 0;
if (val === 18) {
age = myDate.getFullYear() - iden.substring(6, 10) - 1;
sex = iden.substring(16, 17);
birth = iden.substring(6, 10) + "-" + iden.substring(10, 12) + "-" + iden.substring(12, 14);
if (iden.substring(10, 12) < month || iden.substring(10, 12) == month && iden.substring(12, 14) <= day)
age++;
}
if (val === 15) {
age = myDate.getFullYear() - iden.substring(6, 8) - 1901;
sex = iden.substring(13, 14);
birth = "19" + iden.substring(6, 8) + "-" + iden.substring(8, 10) + "-" + iden.substring(10, 12);
if (iden.substring(8, 10) < month || iden.substring(8, 10) == month && iden.substring(10, 12) <= day)
age++;
}
if (sex % 2 === 0)
sex = '1';
else
sex = '0';
this.form.patientSex = sex;
this.form.patientAge = age;
}
4、标签中的使用
<el-form-item label="联系电话"
prop="membersPhone">
<el-form-item label="身份证号码"
prop="idCard">
方法:表单验证-重置
定义一个方法:
resetForm(formName) { this.$refs[formName].resetFields(); } |
在表单标签中定义好表单的名称,用ref来定义。
然后调用的时候,传入表单的名称。
坑:公司场景遇到一个问题,想要重置表单,却获取不到vueComponent实例对象。从而无法使用resetFields方法。
解决方案:原来是要传入表单的名称,而不是表单对象!
------------------【data】-----------------------
【Table】表格
功能:斑马纹【stripe】
stripe属性可以创建带斑马纹的表格
功能:排序【sortable】
在想要排序的列上面,加属性sortable即可。
注意:如果你使用了<template slot-scope=”scope”>的写法,则还需要在列标签上,加上prop属性,绑定那个字段!!!
如果你想让某一列初次加载的时候就有一个默认排序,而不是点击后才排序,可以这么做:
在<table>中添加属性default-sort
指定要排序的字段prop
指定排序的方式:order、ascending、descending
:default-sort = "{prop: 'xxx', order: 'descending'}"
功能:表尾合计行【show-summary】
首先必须要在<el-table>中使用属性show-summary来开启这个表尾合计行的功能。
方法1:无其他配置。只要是数值,就合并。
这种方法的可用性不太好,如果字段里有那种不需要合计的编号,也会错误地相加。
方法2:在<el-table>中使用属性summary-method触发一个自定义方法,注意上面那个属性show-summary还是要写的!
这个自定义方法默认携带一个参数,我打印出来,结果发现打印了两次???
属性columns:第一次的columns是0条数据,第二次是5条数据,对应5列字段。
属性data:就是row的集合,就是每条记录,就是每行记录,和后端查出来的结果一致
其中,集合columns的每一项对象的属性中,这个property我经常用到,判断和prop是否相等。
示例:
getSummaries(param) {
console.log("看看合计方法携带的参数:", param);
const { columns, data } = param; //解构表达式,对象解构
const sums = [];
columns.forEach((column, index) => {
if (index === 0) {
//如果是第1列
sums[index] = "总价"; //直接设为文字“总价”
return;
}
//我直接根据字段名称,粗暴判断!!!
if(column.property != 'amount2') return;
const values = data.map((item) => Number(item[column.property])); //把字段的属性值转为数字
//1、如果是数字
if (!values.every((value) => isNaN(value))) {
//这是什么语法???看不懂
//求和的算法
sums[index] = values.reduce((prev, curr) => {
const value = Number(curr);
//1.1、如果当前值是正常的数字,就进行相加
if (!isNaN(value)) {
return prev + curr;
}
//1.2、如果当前值不正常,就跳过当前值
else {
return prev; //(由于必须返回,所以这里返回上一次的值)
}
}, 0); //这整个箭头函数都是回调函数,0是初始值
sums[index] += " 元";
}
//2、如果不是数字
else {
sums[index] = "N/A";
}
});
return sums;
},
事件:单选
“Table 组件提供了单选的支持,只需要配置highlight-current-row属性即可实现单选。之后由current-change事件来管理选中时触发的事件,它会传入currentRow,oldCurrentRow。如果需要显示索引,可以增加一列el-table-column,设置type属性为index即可显示从 1 开始的索引号。”
示例:
标签中: @current-change="handleCurrentChange" js中: handleCurrentChange(val) { this.currentRow = val; } |
注:这个事件其实有两个参数currentRow、oldCurrentRow,第二次点击同一行,currentRow是空的,直接赋值会报错!要进行判空。
事件:右键
1、在指定区域绑定 右键事件:
@contextmenu.prevent="onContextmenu"
或者在element的table中,有这个
@row-contextmenu="rightClick"
2、绘制新菜单:
<div id="menu"
class="menuDiv">
<div class="menuUl">
<el-button class="li"
v-for="(item, index) in menus"
:key="index"
@click.stop="menuClick(item)"
v-hasPermi="[item.hasPermi]"
style="width: 100%; text-align: left">
<i :class="item.icon"></i> {{ item.name }}
</el-button>
</div>
</div>
3、右键事件:
onContextmenu(event) {
let contextmenu = [];
contextmenu.push({
label: "病人信息",
icon: "el-icon-caret-right",
onClick: () => {
this.chooseItem.status = 1;
this.turnTODetail(this.chooseItem, "archivesUpdate", "one");
},
});
this.$contextmenu({
items: contextmenu,
event, // 鼠标事件信息
customClass: "custom-class", // 自定义菜单 class
zIndex: 3, // 菜单样式 z-index
minWidth: 111, // 主菜单最小宽度,
});
return false;
},
或
// 表格右击的功能
rightClick(row, column, event) {
this.activeRow = row;
this.activeRow.status = 1;
event.preventDefault();
let menu = document.querySelector("#menu");
// 根据事件对象中鼠标点击的位置,进行定位
menu.style.left = event.clientX - 155 + "px";
menu.style.top = event.clientY - 75 + "px";
// 改变自定义菜单的隐藏与显示
menu.style.display = "block";
menu.style.zIndex = 1000;
},
强制修改样式
一、在el-table 外加个div 自定义class
<div class="hhm_table" style="margin-top:20px;"> <el-table :data="skills" :header-cell-style="getRowClass"> <el-table-column label="#" type="index"></el-table-column> <el-table-column label="专业技能" prop="data"></el-table-column> </el-table> </div> |
二、在style标签中加上scpoed
三、重写样式
.hhm_table{ width: 50%; margin: auto; } .hhm_table /deep/ .el-table--fit{ padding: 20px; } .hhm_table /deep/ .el-table, .el-table__expanded-cell { background-color: transparent; } .hhm_table /deep/ .el-table tr { background-color: transparent!important; } .hhm_table /deep/ .el-table--enable-row-transition .el-table__body td, .el-table .cell{ background-color: transparent; } .el-table::before {//去除底部白线 left: 0; bottom: 0; width: 100%; height: 0px; } |
具名插槽【v-slot】
绑定字段的值:scope.row[xxx.prop]
示例:
<template v-slot="scope"> {{ scope.row[dropCol[index].prop] }} </template> |
作用域插槽【slot-scope】
绑定字段的值:scope.row.xxx
Element UI中,table就用到了这个作用域插槽,它维护了一个slot,提供了名为scope的对象,可以通过scope对象获取到一整行的信息:
<el-table-column label="操作">
<template slot-scope="scope">
<el-button @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button @click="handleDelete(scope)">删除</el-button>
</template>
</el-table-column>
scope对象的结构:
引入插槽,下面蓝色部分代码:
<el-table :data="tableData"> 字段名"> <template slot-scope="scope"> <span>{{scope.row.room}}</span> </template> </el-table-column> </el-table> |
js中把后台返回的数据response,赋值给tableData: this.tableData = response; |
说明:原版是<el-table>里面的:data表示对象(每一条记录),然后<el-table-column>里的:prop表示对象的属性。升级版用了插槽slot,直接用scope.row.属性,代替原来的:prop
scope的结构:
索引
这是Element比较牛逼的地方。
创建索引:
官方:“如果需要显示索引,可以增加一列el-table-column,设置type属性为index即可显示从 1 开始的索引号。”
<el-table-column type="index" label="序号" width="50"></el-table-column> |
获取索引:
<template slot-scope="scope"> <el-button @click="xxx(scope)"></el-button> </template> scope的结构: js中: let index = xxx.$index; |
自定义样式
亲测有效:https://blog.csdn.net/qq_44758515/article/details/106944281
要用<style scoped>
一般环境:https://blog.csdn.net/shykevin/article/details/113984039
less环境:https://blog.csdn.net/weixin_43524214/article/details/125733507
合并单元格(李柏林的小妙招)
使用map来记录要合并的行数,巧妙地使用map来记录rowspan,用vue的this.$set设值。
<tbody>
<tr v-for="(item, index) in tableData"
:key="index">
<td v-if="item.isRowSpan"
:rowspan="map.get(item.group)">{{item.group}}</td>
<td>{{item.name}}</td>
</tr>
</tbody>
this.map = new Map();
this.tableData.forEach((item) => {
var rowspan = this.map.get(item.group);
//如果不为空,说明已经存在同组了,就更新这组的rowspan,让它+1
if (rowspan != null) {
this.$set(item, "isRowSpan", false); //this.$set是vue中深拷贝的写法,可以让页面回显
rowspan += 1;
this.map.set(item.group, rowspan);
}
//如果为空,说明这是第一次出现这一组,就让rowspan设为1
else {
this.$set(item, "isRowSpan", true);
item.isRowSpan = true;
this.map.set(item.group, 1);
}
});
合并单元格(结合element UI)
https://blog.csdn.net/qq_29468573/article/details/80742646
这篇文章有个小bug,即第二次调用生成数组的方法时,上一次的数据没清空,导致出现混乱。
生成合并单元格需要的数组 getSpanArr(data) { 初始化一下,防止第二次点击搜索,上一次的结果还在 初始化一下,防止第二次点击搜索,上一次的结果还在 for (var i = 0; i < data.length; i++) { if (i === 0) { this.spanArr.push(1); this.pos = 0; } else { // 判断当前元素与上一个元素是否相同 if (data[i].xxx=== data[i - 1].xxx) { this.spanArr[this.pos] += 1; this.spanArr.push(0); } else { this.spanArr.push(1); this.pos = i; } } } }, |
合并单元格 cellMerge({ row, column, rowIndex, columnIndex }) { if (columnIndex === 0 || columnIndex === 1 || columnIndex === 2) { const _row = this.spanArr[rowIndex]; const _col = _row > 0 ? 1 : 0; return { rowspan: _row, colspan: _col } } }, |
data中定义: 合并单元格的集合 数组下标 |
搜索中调用: this.getSpanArr(this.tableData); |
【tree】树形控件
默认展开
在标签里添加属性:default-expanded-keys
该属性的值为数组,要展开的所有节点的数组。
<el-tree :data="data" show-checkbox node-key="id" :default-expanded-keys="[2, 3]" :props="defaultProps"> </el-tree> |
【pagination】分页
属性【layout】整体布局
设置layout,表示整体布局上内容的排版,用逗号分隔,布局元素会依次显示。
sizes:选择分页大小 total:总页数(这个样式是固定的,就是“共xxx条”,改不了) prev:上一页 next:下一页 pager:页码列表 jumper:跳页元素 -> :它后的元素会靠右显示 |
属性【total】总条数
属性【page-size】每页条数(当前)
当前的每页显示记录的个数。
属性【page-sizes】分页选项
总共有哪些分页选项。
注意:这个属性比上一个属性多了s,表示集合。
事件【@size-change】
@size-change="handleSizeChange"
js方法中可以直接获取到当前选择的值: handleSizeChange(val) { console.log(`每页${val}条`); }, |
事件【@current-change】
@current-change="handleCurrentChange"
handleCurrentChange(val) { console.log(`当前页: ${val}`); } |
示例【小创云】
<div v-show="!listLoading" class="pagination-container"> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page.sync="listQuery.page" :page-sizes="[10,20,30, 50]" :page-size="listQuery.limit" layout="total, sizes, prev, pager, next, jumper" :total="total"> </el-pagination> </div> |
/** 改变分页的大小(limit表示一页显示几条数据) */ handleSizeChange(val) { this.listQuery.limit = val; this.getList(); }, |
/** 跳转到某一页(page表示当前页) */ handleCurrentChange(val) { this.listQuery.page = val; this.getList(); }, |
/** 查询列表信息 */ getList() { this.listLoading = true; page(this.listQuery).then(response => { 列表的结构", response); this.tableData = response.data.rows; this.total = response.data.total;//显示所有页码 this.listLoading = false; }) }, |
后端: 方法一,用封装好的类TableResultResponse: @ResponseBody 方法二,手写 @RequestMapping(value = {"/hos/getAllScoresOfHos"}, method = RequestMethod.GET)
|
------------------【notice】---------------------
【Loading】加载
Element 提供了两种调用 Loading 的方法:指令、服务。
对于自定义指令v-loading,只需要绑定Boolean即可。默认状况下,Loading 遮罩会插入到绑定元素的子节点,通过添加body修饰符,可以使遮罩插入至 DOM 中的 body 上。
------------------【navigation】---------------------
【NavMenu】导航菜单
总体结构
格式:
<el-menu> <el-submenu index="1"> <template slot="title">选项1</template> <el-menu-item index="1-1">选项1-1</el-menu-item> </el-submenu> <el-submenu index="2"> <template slot="title">选项2</template> <el-menu-item index="2-1">选项2-1</el-menu-item> </el-submenu> </el-menu> |
【Menu】最外层
介绍:<el-menu>是最外层,必须先写这个!
参数 | 说明 | 可选值 | 默认值 |
mode | 模式。默认是垂直排列,可以改成水平排列。 | horizontal / vertical | vertical |
collapse | 是否水平折叠收起菜单(仅在 mode 为 vertical 垂直排列时可用) | — | false |
background-color | 菜单的背景色(仅支持 hex 格式) | — | #ffffff |
text-color | 菜单的文字颜色(仅支持 hex 格式) | — | #303133 |
active-text-color | 当前激活菜单的文字颜色(仅支持 hex 格式) | — | #409EFF |
default-active | 当前激活菜单的 index | — | — |
default-openeds | 当前打开的 sub-menu 的 index 的数组 | — | — |
unique-opened | 是否只保持一个子菜单的展开 | — | false |
menu-trigger | 子菜单打开的触发方式(只在 mode 为 horizontal 水平排列时有效) | hover / click | hover |
router | 是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转 | — | false |
collapse-transition | 是否开启折叠动画 | — | true |
怎么没有“当前激活的菜单的背景颜色”这个参数呢?
【SubMenu】子菜单
介绍:<el-submenu>是<el-menu>下的直接子菜单项
问:怎么若依框架不加<el-menu>?
答:原来是外层父组件已经写了!
官方说明
参数 | 说明 | 默认值 |
index | 唯一标志,类似id | null |
popper-class | 弹出菜单的自定义类名 | — |
show-timeout | 展开 sub-menu 的延时 | 300 |
hide-timeout | 收起 sub-menu 的延时 | 300 |
disabled | 是否禁用 | false |
popper-append-to-body | 是否将弹出菜单插入至 body 元素。在菜单的定位出现问题时,可尝试修改该属性 | 一级子菜单:true 非一级子菜单:false |
自定义样式
https://blog.csdn.net/m0_56409834/article/details/126543172
要用/deep/,则需要先安装less
npm i less-loader@6(这个less-loader的版本要匹配你webpack的版本)
npm i less(可能还需要安装这个less)
/deep/.el-submenu .el-menu-item { background-image: linear-gradient(#0099cc, #003366); } |
【Menu-Item】叶子节点
介绍:<el-menu-item>是最小的叶子节点
【Tabs】标签页
基础用法
<el-tabs v-model="activeName"
@tab-click="handleClick">
<el-tab-pane label="用户管理"
name="first">用户管理</el-tab-pane>
<el-tab-pane label="配置管理"
name="second">配置管理</el-tab-pane>
<el-tab-pane label="角色管理"
name="third">角色管理</el-tab-pane>
<el-tab-pane label="定时任务补偿"
name="fourth">定时任务补偿</el-tab-pane>
</el-tabs>
data() {
return {
activeName: "fourth",
};
},
methods: {
handleClick(tab, event) {
console.log(tab, event);
},
},
属性【value/v-model】指定当前标签页
<el-tabs>中指定value,就会去找<el-tab-pane>对应的name
------------------【others】---------------------
【Dialog】对话框
属性【width】宽度
默认50%
值是百分比,不是数字!
但是可以用像素来指定,这样的话一定要加上px
属性【close-on-click-modal】点击阴影,关闭弹框
功能:是否可以通过点击 modal 关闭 Dialog
我的评价:这个功能很烦,光标选择文本时会误触从而关闭弹框,烦。
示例:通常我们把它关掉,来提升用户体验。
<el-dialog :visible.sync="dialogVisible" width="30%" :close-on-click-modal="false"> |
功能:可拖拽
https://blog.csdn.net/Gx0525_/article/details/122713157?spm=1001.2014.3001.5506
官方不提供拖拽的功能,需要外部引入js文件!!!
1、新建一个directive.js文件
import Vue from 'vue' // v-dialogDrag: 弹窗拖拽 Vue.directive('dialogDrag', { bind(el, binding, vnode, oldVnode) { const dialogHeaderEl = el.querySelector('.el-dialog__header') const dragDom = el.querySelector('.el-dialog') dialogHeaderEl.style.cursor = 'move' 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null); const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null) dialogHeaderEl.onmousedown = (e) => { 鼠标按下,计算当前元素距离可视区的距离 const disX = e.clientX - dialogHeaderEl.offsetLeft const disY = e.clientY - dialogHeaderEl.offsetTop 获取到的值带px 正则匹配替换 let styL, styT 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px if (sty.left.includes('%')) { styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100) styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100) } else { styL = +sty.left.replace(/\px/g, '') styT = +sty.top.replace(/\px/g, '') } document.onmousemove = function(e) { 通过事件委托,计算移动的距离 const l = e.clientX - disX const t = e.clientY - disY 移动当前元素 dragDom.style.left = `${l + styL}px` dragDom.style.top = `${t + styT}px` 将此时的位置传出去 // binding.value({x:e.pageX,y:e.pageY}) } document.onmouseup = function(e) { document.onmousemove = null document.onmouseup = null } } } }) // v-dialogDragWidth: 弹窗宽度拖大 拖小 Vue.directive('dialogDragWidth', { bind(el, binding, vnode, oldVnode) { const dragDom = binding.value.$el.querySelector('.el-dialog') el.onmousedown = (e) => { 鼠标按下,计算当前元素距离可视区的距离 const disX = e.clientX - el.offsetLeft document.onmousemove = function(e) { 移动时禁用默认事件 通过事件委托,计算移动的距离 const l = e.clientX - disX dragDom.style.width = `${l}px` } document.onmouseup = function(e) { document.onmousemove = null document.onmouseup = null } } } }) |
2、在main.js中引入这个文件
import '@/utils/directive' // 拖拽弹窗,在需要用到拖拽功能的弹窗标签上加v-dialogDrag
3、在组件中使用:
<el-dialog v-dialogDrag
【Divider】分割线
https://element.eleme.cn/#/zh-CN/component/divider
比hr更淡的分割线,更加符合element风格。
也可以垂直分割。
【InfiniteScroll】无限滚动
【Popover】弹出框
属性【popper-class】为 popper 添加类名
如果对样式不满意,可以用这个属性带上自定义的类。
注意:因为它实际上是在根标签body下的,所以不能用<style scope>,否则不生效!
<el-popover title="医嘱数量"
popper-class="hhm_el_popper2"
placement="top-start"
@show="getStatusSum(item)"
width="200"
trigger="hover">
<div>
<span>进行中:{{notStopSum}}</span>
<span style="margin-left: 20px;">已停止:{{stopSum}}</span>
</div>
<i class="el-icon-info"
style="color: #5cb6ff;"
slot="reference"></i>
</el-popover>