1. el-table分页数据+回显+勾选状态+记录数据(map实战)

原以为js中即便是学到了map可实际上也不会用到map,可我今天就遇到一个el-table分页查询,然后需要勾选表格内容,切换页码后回显勾选项的需求。

一开始想的是,把所有已勾选的数据用list来维护,可稍微一思索就发现这样实际执行起来还是有缺陷的,比如用于来回切换页码等场景稍微一复杂就容易乱套。

还是应该记录每一条数据的信息,用户点击勾选的时候,用当前的勾选情况去跟已勾选的数据做一个比对。如果已记录的数据里存在当前列表中的勾选项,同时本次属于勾选状态,则不做处理。

如果处于未勾选状态,则从用于记录的总数据中删除该选项。如果总得记录结果没有改数据,同时该数据本次是勾选状态,则把新数据添加进去。

用map是最好的了,于是就实战一下,试试手~

1
2
3
4
5
6
7
8
9
10
11
12
<el-button @click="query"> 获取新数据</el-button>
<el-table
ref="myTable"
:data="tableData"
style="width: 100%"
@selection-change="handleSelectionChange"
>
<el-table-column prop="date" label="日期" width="180"> </el-table-column>
<el-table-column prop="name" label="姓名" width="180"> </el-table-column>
<el-table-column prop="address" label="地址"> </el-table-column>
<el-table-column type="selection" width="55"> </el-table-column>
</el-table>

选择selection-change ,它会把当前页码的勾选项列出来。
接下来就是它对应的handle函数了,我把处理逻辑用备注的形式写出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const selectedMap = new Map();
handleSelectionChange(val) {
// val 表示当前勾选项,属于array类型
// 我们首选需要一个list用来记录当前tableData都有哪些备选哪些没有被选上,我这里先用一个map记录当前表格中的备选项
// 然后遍历tableData 把已选项维护到一个list中
const currSelectMap = new Map();
for (let i = 0; i < val.length; i++) {
currSelectMap.set(val[i].name, true);
}

const selectList = [];
this.tableData.forEach((item) => {
const name = item.name;
selectList.push({
name,
selected: currSelectMap.has(name) ? true : false,
});
});
// 遍历selectList,它记录了当前表格中每一项的一个唯一ID(我这里是name是唯一的),以及是否选中两个参数
// 遍历过程中与记录总的被选中的数据做一个比较,代码由本文前面的处理逻辑形成
selectList.forEach((item) => {
const name = item.name;
const selected = item.selected;
if (selectedMap.has(name) && !selected) {
selectedMap.delete(name);
}
if (!selectedMap.has(name) && item.selected) {
selectedMap.set(name, item);
}
});
console.log("selectedMap", selectedMap);
},

最后的最后,el-table切换页码时候还需要额外处理一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
getRowKey(row) {
return row.name;
},
query() {
// 查询
this.tableData = [
{
date: "2016-05-02",
name: "王小虎4",
address: "上海市普陀区金沙江路 1518 弄",
},
{
date: "2016-05-04",
name: "王小虎5",
address: "上海市普陀区金沙江路 1517 弄",
},
{
date: "2016-05-01",
name: "王小虎6",
address: "上海市普陀区金沙江路 1519 弄",
},
{
date: "2016-05-03",
name: "王小虎7",
address: "上海市普陀区金沙江路 1516 弄",
},
];

for (let i = 0; i < this.tableData.length; i++) {
//如果有的话,添加进去,把参数设置为true
const name = this.tableData[i].name;
if (selectedMap.has(name)) {
this.$nextTick(() => {
this.$refs.myTable.toggleRowSelection(this.tableData[i], true);
});
}
}
},

就是切换到新数据时,我们要打勾,但是通过代码打钩的过程会触发handleSelectionChange函数这样就导致,可能就跟我们期待的差很多了,具体差到哪里,读者朋友可以把 this.$nextTick(DOM 更新结束后执行其指定的回调)去掉试试。

2. 修改ElementUI默认样式最直接有效的方法

做项目的过程在难免会遇到要修改ElementUI的默认样式

解决方法:

方式一:全局修改 写一个单独的CSS文件,里面直接用element自带得类名进行样式修改(部分需要添加 !important)。这种方法会将整个项目用到的该组件样式全部修改了。一般用来放公共样式,如表格、弹框、表单会用到。

修改el-dialog

效果

修改el-message-box

效果

css文件单独在main.js中引入就可以了

1
import "./style/reset.css";

方式二:组件内修改 单独在要修改的组件内再写一对标签,不要加scoped。 然后在里面直接修改样式就行,为了防止可能影响到其他组件的样式,建议加一层父盒子的类名。例如:我想修改类名为login-form的盒子内的 input 样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<div class="login-form">
<el-form ref="form" :model="form">
<el-form-item>
<el-input
placeholder="身份证号码"
prefix-icon="el-icon-user"
v-model.trim="form.name"
></el-input>
</el-form-item>
<el-form-item>
<el-input
placeholder="请输入密码"
prefix-icon="el-icon-lock"
v-model.trim="form.password"
autocomplete="off"
type="password"
@keypress.native.13="login"
/>
</el-form-item>
<el-form-item>
<div :underline="false" style="float: right;cursor: pointer;">
忘记密码?
</div>
</el-form-item>
<el-form-item>
<div class="clickLogin" @click="login">登录</div>
</el-form-item>
</el-form>
</div>
1
2
3
4
5
.login-form {
.el-input__inner {
color: #999;
}
}

如果不生效,也请加上 !important

/deep/跟>>>快被淘汰了,不建议使用

3. flex布局 三个div 两个左对齐 一个右对齐

一、问题

有一个盒子flex布局,子元素有 三个div,在不改变dom结构的情况下,实现 前两个左对齐 第三个右对齐。

二、实现方案

在flex布局中如果某个元素的margin为auto那么它的margin将会自动填充为 剩下的空间

三、代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>flex布局</title>
<style>
*{
margin: 0;
padding: 0;
}
.box {
display: flex;
justify-content: flex-start;
}

.block {
width: 100px;
height: 100px;
border:1px solid black;

}
</style>
</head>
<body>
<div class="box">
<div class="block">块1</div>
<div class="block">块2</div>
// 此地margin-left:auto;自动填充了剩余空间
<div class="block" style="margin-left: auto;">块3</div>
<div class="block">块4</div>
</div>
</body>
</html>

效果如下:

在这里插入图片描述

4. echart 柱状图y轴要显示成百分比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<template>
<div id="main" style="width: 600px; height: 400px"></div>
</template>
<script>
import * as echarts from 'echarts'
export default {
data() {
return {
option: {
xAxis: { //x轴设置
type: 'category',
boundaryGap: false,
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
},
yAxis: { //y轴设置
type: 'value',
axisLabel: { //y轴设置为%
show: true,
interval: 'auto',
formatter: '{value} %',
},
max: 100, //最大值
min: 0, //最小值
},
series: [
{
data: [100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
type: 'line',
areaStyle: {},
},
],
},
}
},
mounted() {
let myChart = echarts.init(document.getElementById('main'))
// 指定图表的配置项和数据
let option = this.option
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option)
},
}

5. ECharts在柱状图的柱子上方显示数量的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<template>
<v-chart :options="getOptions()" />
</template>

<script>
export default {
methods: {
getOptions() {
return {
title: {
text: '企业诊断区域分布'
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
},
legend: {
right: '0'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: ['婺城区', '金东区', '武义县', '浦江县', '磐安县', '兰溪市', '义务市', '东阳时', '永康市'],
axisTick: {
alignWithLabel: true
}
}
],
yAxis: [
{
type: 'value',
}
],
series: [
{
name: '诊断数',
type: 'bar',
barWidth: '30%',
data: [1361, 4036, 1776, 1225, 2565, 1231, 1523, 1189, 1956],
itemStyle: { // 上方显示数值
normal: {
label: {
show: true, // 开启显示
position: 'top', // 在上方显示
textStyle: { // 数值样式
color: '#999',
fontSize: 12
}
}
}
}
},
{
name: '改造数',
type: 'bar',
barWidth: '30%',
data: [582, 1918, 1015, 669, 1124, 640, 710, 612, 791],
itemStyle: { // 上方显示数值
normal: {
label: {
show: true, // 开启显示
position: 'top', // 在上方显示
textStyle: { //数值样式
color: '#999',
fontSize: 12
}
}
}
}
}
]
}
},
},
};
</script>

<style>
</style>

6. 滚动条样式修改

前言

浏览器中的滚动条样式大家一定都不陌生,其样式并不好康。可能很多小伙伴还不知道,这个东东的样式也可以修改(仅支持部分现代浏览器),本次就来带大家用 CSS 修改一下它的样式。

一、认识滚动条

首先我们先来简单看一下滚动条是由哪几部分组成的:

滚动条的组成.png

当横向和纵向都有滚动条时,还会有一个交汇的部分(见下图),但是因为一般网页开发中都不会让横向出现滚动条(因为影响美观),所以这个小方块出现的频率不算太高。下图为同时有垂直滚动条和水平滚动条时交汇的部分:

当同时有垂直滚动条和水平滚动条时交汇的部分.png

二、解决方案

CSS伪类

目前我们可以通过 CSS伪类 来实现滚动条的样式修改,以下为修改滚动条样式用到的CSS伪类:

  • ::-webkit-scrollbar — 整个滚动条
  • ::-webkit-scrollbar-button — 滚动条上的按钮 (上下箭头)
  • ::-webkit-scrollbar-thumb — 滚动条上的滚动滑块
  • ::-webkit-scrollbar-track — 滚动条轨道
  • ::-webkit-scrollbar-track-piece — 滚动条没有滑块的轨道部分
  • ::-webkit-scrollbar-corner — 当同时有垂直滚动条和水平滚动条时交汇的部分
  • ::-webkit-resizer — 某些元素的corner部分的部分样式(例:textarea的可拖动按钮)

此处附上MDN文档传送门:https://developer.mozilla.org/zh-CN/docs/Web/CSS/::-webkit-scrollbar

兼容性问题

当然这种解决方案还存在一定的兼容性问题,仅仅在支持WebKit的浏览器 (例如, 谷歌Chrome, 苹果Safari)可以使用。其实一看到 -webkit- 前缀就能明白它是 CSS3 中的 私有属性前缀 ,特定前缀是为了适配特定浏览器内核的。我们前往Can I use对其兼容性一探究竟:
“::-webkit-scrollbar”属性兼容性.png

由上图我们可以看到,兼容性并不算太好,不过我们也不用过于在意,毕竟我们只是规则的使用者而非制定者。

三、进行测试

1. 整个滚动条

我们一条属性一条属性来进行测试,首先使用 ::-webkit-scrollbar 。先改变一下它的宽度,测试一下效果:

1
2
3
4
5
/* 整个滚动条 */
/* 宽高分别对应纵向滚动条和横向滚动条的宽度 */
::-webkit-scrollbar {
width: 50px;
}

滚动条样式修改测试-1.gif

我们可以看到,滚动条似乎“消失”了,但是仍然能靠鼠标拖动来滚动页面。我们再给它加一个背景色康康效果:

1
2
3
4
5
/* 整个滚动条 */
::-webkit-scrollbar {
width: 50px;
background-color: skyblue;
}

滚动条样式修改测试-2.png

增加背景颜色后,滚动条又“出现”了。结合刚才的代码我们不难看出:设置 ::-webkit-scrollbar 属性会使滚动条默认样式失效。既然如此,我们就必须结合其他属性一起使用。

2. 滚动条上的箭头按钮

我们来使用一下 ::-webkit-scrollbar-button 属性,发现当此属性单独使用时无任何效果:

1
2
3
4
/* 滚动条上的箭头按钮 */
::-webkit-scrollbar-button {
background-color: slateblue;
}

滚动条样式修改测试-3.png

于是乎我们加上之前的代码再试试:

滚动条样式修改测试-4.png

我们可以看出,两个箭头的按钮位置的背景颜色发生了变化。看来,滚动条的其他伪类属性需要配合第一步中的 ::-webkit-scrollbar 才能生效。

3. 滚动条上的滚动滑块

我们用 ::-webkit-scrollbar-thumb 来改变滚动条中滑块的样式:

1
2
3
4
5
6
7
8
9
10
/* 整个滚动条 */
::-webkit-scrollbar {
width: 50px;
background-color: skyblue;
}

/* 滚动条上的滚动滑块 */
::-webkit-scrollbar-thumb {
background-color: orange;
}

滚动条样式修改测试-5.gif

4. 滚动条轨道

::-webkit-scrollbar-track 属性修改滚动条轨道样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 整个滚动条 */
::-webkit-scrollbar {
width: 50px;
background-color: skyblue;
}

/* 滚动条上的滚动滑块 */
::-webkit-scrollbar-thumb {
background-color: orange;
}

/* 滚动条轨道 */
::-webkit-scrollbar-track {
background-color: hotpink;
}

滚动条样式修改测试-6.png

通过效果图我们可以发现,设置的滚动条轨道背景色遮住了设置的整个滚动条的背景色(天蓝)。那是否可以实现两种背景色里外嵌套的效果呢,目前做出了几种尝试都没有效果,只能暂时放弃,以下为经测试未实现嵌套背景色效果代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 未实现背景色嵌套效果代码 */
::-webkit-scrollbar {
/* 无法通过 padding 实现 */
padding: 4px;
width: 50px;
background-color: skyblue;
box-sizing: border-box;
}

::-webkit-scrollbar-track {
/* 无法通过调整宽度实现 */
width: 80%;
background-color: hotpink;
}

既然如此,我们如果需要调整滚动条的背景颜色,只需要在 ::-webkit-scrollbar::-webkit-scrollbar-track 中任选其一即可。

5. 滚动条没有滑块的轨道部分

这次我们同时设置 ::-webkit-scrollbar-track::-webkit-scrollbar-track-piece 来看效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 整个滚动条 */
::-webkit-scrollbar {
width: 50px;
}

/* 滚动条上的滚动滑块 */
::-webkit-scrollbar-thumb {
background-color: orange;
}

/* 滚动条轨道 */
::-webkit-scrollbar-track {
background-color: hotpink;
}

/* 滚动条没有滑块的轨道部分 */
::-webkit-scrollbar-track-piece {
background-color: purple;
}

滚动条样式修改测试-7.png

上述代码符合预期效果,但是我给滑块设置透明的背景色(transparent)则会全是 purple 颜色,也不会出现滑块底部呈现 pink 颜色。所以,如果要改背景色还是选择轨道来修改吧。

6. 测试总结

  1. 设置 ::-webkit-scrollbar 属性会使滚动条默认样式失效
  2. 其他修改滚动条样式的私有属性需要配合 ::-webkit-scrollbar 属性使用
  3. 如果要设置滚动条背景色, ::-webkit-scrollbar 、 ::-webkit-scrollbar-track 、 ::-webkit-scrollbar-track-piece 三个属性设置一个即可。

四、开始换装

1. 纯色系滚动条

在研究过滚动条修改的 CSS 属性后我们终于可以开始动工了,先来仿照Element中的滚动条样式,修改一个纯色系滚动条:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 整个滚动条 */
::-webkit-scrollbar {
/* 对应纵向滚动条的宽度 */
width: 10px;
/* 对应横向滚动条的宽度 */
height: 10px;
}

/* 滚动条上的滚动滑块 */
::-webkit-scrollbar-thumb {
background-color: #49b1f5;
border-radius: 32px;
}

/* 滚动条轨道 */
::-webkit-scrollbar-track {
background-color: #dbeffd;
border-radius: 32px;
}

滚动条样式修改测试-8.gif

效果还不错,比默认的样式要好上不少。此处使用蓝色,实际开发中可以使用项目的主题色作为滚动条的配色参考。

2. 花纹系滚动条

我们可以利用 background-image 这一属性来实现滚动条的花纹效果(此处效果非本人原创),直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* 整个滚动条 */
::-webkit-scrollbar {
width: 10px;
height: 10px;
}

/* 滚动条上的滚动滑块 */
::-webkit-scrollbar-thumb {
background-color: #49b1f5;
/* 关键代码 */
background-image: -webkit-linear-gradient(45deg,
rgba(255, 255, 255, 0.4) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, 0.4) 50%,
rgba(255, 255, 255, 0.4) 75%,
transparent 75%,
transparent);
border-radius: 32px;
}

/* 滚动条轨道 */
::-webkit-scrollbar-track {
background-color: #dbeffd;
border-radius: 32px;
}

滚动条样式修改测试-9.gif

7. 用CSS画图形

三角形

首先第一步,搭出盒子模型,给它加上边框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.triangle {
width: 50px;
height: 50px;
border: 2px solid;
border-color: #3fed9c #d8b423 #06433c #0ca9f1;
}
</style>
</head>

<body>
<div class="triangle"></div>
</body>

</html>

如下图所示:

Snipaste_2022-05-12_20-21-30.png
步骤二、
将盒子的border设置为50px 变更代码如下:

1
2
3
4
5
6
.triangle {
width: 50px;
height: 50px;
border: 50px solid;
border-color: #3fed9c #d8b423 #06433c #0ca9f1;
}

如下图所示:

Snipaste_2022-05-12_20-23-54.png
步骤三、 此时中间白色的区域就是宽高(height,width)只要将他们设置为0就可以得到四个三角形 变更代码如下:

1
2
3
4
5
6
.triangle {
width: 0;
height: 0;
border: 50px solid;
border-color: #3fed9c #d8b423 #06433c #0ca9f1;
}

如下图所示:

Snipaste_2022-05-12_20-30-48.png
步骤四、 虽然完成了以上方式可以达到视觉上实现了三角形,但实际上,隐藏的部分任然占据部分高度,需要将上方的宽度去掉,然后再将两侧的颜色设置透明。变更代码如下:

1
2
3
4
5
6
7
.triangle {
width: 0;
height: 0;
border-bottom: 50px solid #06433c;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
}

如下图所示:

Snipaste_2022-05-13_15-54-28.png

想要什么方向的三角形就将对立面不设置宽度,两边设置宽度并为透明

空心三角

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<style>
.triangle {
width: 0;
height: 0;
border-bottom: 50px solid #06433c;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
position: relative;
}

.triangle::after {
content: '';
border-bottom: 40px solid #04be9c;
border-left: 40px solid transparent;
border-right: 40px solid transparent;
position: absolute;
top: 0;
left: 0;
}
</style>

如果想要实现一个只有边框是空心的三角形,由于这里不能再使用border属性,所以最直接的方法是利用伪类新建一个小一点的三角形定位上去。效果图如下:

Snipaste_2022-05-12_21-08-49.png
伪类元素定位参照对象的内容区域宽高都为0,则内容区域即可以理解成中心一点,所以伪元素相对中心这点定位
将元素定位进行微调以及改变颜色,就能够完成下方效果图:

Snipaste_2022-05-12_21-11-35.png
最终全局效果代码(空心三角形)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.triangle {
width: 0;
height: 0;
border-bottom: 50px solid #06433c;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
position: relative;
}

.triangle::after {
content: '';
border-bottom: 40px solid #fff;
border-left: 40px solid transparent;
border-right: 40px solid transparent;
position: absolute;
top: 6px;
left: -40px;
}
</style>
</head>

<body>
<div class="triangle"></div>
</body>

</html>

原理

可以看到,边框是实现三角形的部分,边框实际上并不是一个直线,如果我们将四条边设置不同的颜色,将边框逐渐放大,可以得到每条边框都是一个梯形

img

当分别取消边框的时候,发现下面几种情况:

  • 取消一条边的时候,与这条边相邻的两条边的接触部分会变成直的
  • 当仅有邻边时, 两个边会变成对分的三角
  • 当保留边没有其他接触时,极限情况所有东西都会消失

img

通过上图的变化规则,利用旋转、隐藏,以及设置内容宽高等属性,就能够实现其他类型的三角形

8. 自定义表格,带滚动动画

html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div class="statusList">
<ul class="seTable">
<li>#</li>
<li>区县市</li>
<li>数量</li>
<li>用电(wkW/h)</li>
</ul>
<!-- <div>aaaaaaa</div> -->
<div id="orderItems" class="outlineBorder" @mouseenter="mouseenter()" @mouseleave="mouseleave()" ref="table">
<ul v-show="radio1 == '1'" class="rolling" v-for="(item, index) of tableData" :key="index">
<li>{{ index + 1 }}</li>
<li>{{ item.area }}</li>
<li>{{ item.num }}</li>
<li>{{ item.consumption }}</li>
</ul>
<ul v-show="radio1 == '2'" class="rolling" v-for="(item, index) of tableData" :key="index">
<li>{{ index + 1 }}</li>
<li>{{ item.industry }}</li>
<li>{{ item.num }}</li>
<li>{{ item.consumption }}</li>
</ul>
</div>
</div>

Css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
.statusList {
width: 94%;
margin: 0 auto;
margin-top: 5px;

.seTable {
width: 100%;
padding-bottom: 0px;
overflow: hidden;
display: flex;

li {
float: left;
font-size: 16px;
color: #a2a2a2;
background: linear-gradient(to right, #eee, #f4f4f4);
padding: 7px 0;
text-align: center;
}

li:nth-child(1) {
width: 15%;
}

li:nth-child(2) {
width: 25%;
}

li:nth-child(3) {
width: 25%;
}

li:nth-child(4) {
width: 35%;
}

}

.outlineBorder {
font-size: 15px;
position: relative;
width: 100%;
overflow: hidden;
// 可视高度
height: 240px;

ul {
width: 100%;
height: 40px;
display: flex;
justify-content: flex-start;
}

ul li {
font-size: 16px;
color: #7c7c7c;
height: 40px;
line-height: 40px;
padding: 7px 0;
text-align: center;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

ul li:nth-child(1) {
width: 15%;
}

ul li:nth-child(2) {
width: 25%;
// text-align: left;
}

ul li:nth-child(3) {
width: 25%;
}

ul li:nth-child(4) {
width: 35%;
}
}
}

js

js部分写js动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
export default {
data() {
return {
tableData: [],
// 定时器
timer: null,
// 鼠标是否进入
enter: false,
// 表单是否能滚动
isScroll: true,
};
},
mounted() {
this.getCityData();
this.tableScroll();
},

methods: {
changeData() {
console.log(this.radio1);
if (this.radio1 == '1') {
this.getCityData();
this.tableScroll();
} else if (this.radio1 == '2') {
this.getIndustryData();
this.tableScroll();
} else if(this.radio1 == '3') {
this.getChainData();
this.tableScroll();
}
},
// 鼠标进入表格触发
mouseenter() {
this.enter = true;
if (!this.isScroll) return;
clearInterval(this.timer);
},
// 鼠标出表格触发
mouseleave() {
this.enter = false;
if (!this.isScroll) return;
clearInterval(this.timer);
this.tableScroll();
},
// 表格滚动事件
tableScroll() {
// 先把以前的定时器清除
clearInterval(this.timer);
// 拿到表格挂载后的真实DOM
const divData = this.$refs.table;
// 拿到元素后,对元素进行定时增加距离顶部距离,实现滚动效果(此配置为每100毫秒移动1像素)
this.timer = setInterval(() => {
// 元素自增距离顶部1像素,,,,没有滚动高度就一直是0
divData.scrollTop += 1;
// 可视高度 = 可滚动内容的高度
// console.log('可视高度', divData.clientHeight);
// console.log('可滚动高度', divData.scrollHeight);
if (divData.clientHeight == divData.scrollHeight) {
// 如果相等了,,,则鼠标移入移出就让它都没有作用
this.isScorll = false;
return clearInterval(this.timer);
}
// 判断元素是否滚动到底部(可视高度+距离顶部=整个高度)
if (
divData.clientHeight + divData.scrollTop + 1 >
divData.scrollHeight
) {
clearInterval(this.timer);
setTimeout(() => {
// 如果鼠标进入了,,则不执行后面的,不再调用滚动事件新增新的定时器
if (this.enter) return;
divData.scrollTop = 0;
this.tableScroll();
}, 1000);
}
}, 40)
}
},
};

10. highcharts 地图显示

获取地图JSON 数据

首页 | GeoJSON.cn

将json 数据拷贝到本地。

项目如果是vue-cli2创建的就放置在 static 文件夹。

如果是vue-cli3创建就没有static文件夹,那就放置在 public 文件夹下。

渲染数据

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<template>
<div class="com-container">
<div id="map-container">

</div>
<div class="coverMap"></div>
</div>
</template>

<script>
import HighCharts from 'highcharts/highmaps'
export default {

data() {
return {
mapData: [
['兰溪', 728],
['婺城', 710],
['金东', 963],
['浦江', 541],
['义乌', 622],
['武义', 866],
['永康', 398],
['东阳', 785],
['磐安', 223],
]
};
},

mounted() {
this.getData();
},

methods: {
getData() {
let data = this.mapData;
$.getJSON('../../../../static/jinhua.json', function (geojson) {
console.log(geojson);
// Initiate the chart
HighCharts.mapChart('map-container', {
title: {
text: ''
},
mapNavigation: {
enabled: true,
buttonOptions: {
verticalAlign: 'bottom'
}
},
colorAxis: {
tickPixelInterval: 150,
labels: {
}
},
series: [{
data: data,
mapData: geojson,
joinBy: 'name',
keys: ['name', 'value'],
name: '不知道啥数据',
states: {
hover: {
color: '#a4edba'
}
},
dataLabels: {
enabled: true,
format: `{point.name}
{point.value}`,
style: {
width: '80px' // force line-wrap
}
}
}]
});
});
},
},
};
</script>

<style lang="less" scoped>
.com-container {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;

#map-container {
width: 100%;
height: 100%;
}

.coverMap {
position: absolute;
bottom: 0px;
right: 0;
width: 150px;
height: 30px;
background-color: #fff;
z-index: 99;
}
}
</style>

11. echarts 图表 百分比精度问题解决

在数据那加 toFixed() 即可解决

1
valueFormatter: (value) => (value * 100).toFixed(0) + '%'

12. 如何实现上拉加载,下拉刷新?

一、前言

下拉刷新和上拉加载这两种交互方式通常出现在移动端中

本质上等同于PC网页中的分页,只是交互形式不同

开源社区也有很多优秀的解决方案,如iscrollbetter-scrollpulltorefresh.js库等等

这些第三方库使用起来非常便捷

我们通过原生的方式实现一次上拉加载,下拉刷新,有助于对第三方库有更好的理解与使用

二、实现原理

上拉加载及下拉刷新都依赖于用户交互

最重要的是要理解在什么场景,什么时机下触发交互动作

上拉加载

首先可以看一张图

img

上拉加载的本质是页面触底,或者快要触底时的动作

判断页面触底我们需要先了解一下下面几个属性

  • scrollTop:滚动视窗的高度距离window顶部的距离,它会随着往上滚动而不断增加,初始值是0,它是一个变化的值
  • clientHeight:它是一个定值,表示屏幕可视区域的高度;
  • scrollHeight:页面不能滚动时也是存在的,此时scrollHeight等于clientHeight。scrollHeight表示body所有元素的总长度(包括body元素自身的padding)

综上我们得出一个触底公式:

1
scrollTop + clientHeight >= scrollHeight

简单实现

1
2
3
4
5
6
7
8
9
let clientHeight  = document.documentElement.clientHeight; //浏览器高度
let scrollHeight = document.body.scrollHeight;
let scrollTop = document.documentElement.scrollTop;

let distance = 50; //距离视窗还用50的时候,开始触发;

if ((scrollTop + clientHeight) >= (scrollHeight - distance)) {
console.log("开始加载数据");
}

下拉刷新

下拉刷新的本质是页面本身置于顶部时,用户下拉时需要触发的动作

关于下拉刷新的原生实现,主要分成三步:

  • 监听原生touchstart事件,记录其初始位置的值,e.touches[0].pageY
  • 监听原生touchmove事件,记录并计算当前滑动的位置值与初始位置值的差值,大于0表示向下拉动,并借助CSS3的translateY属性使元素跟随手势向下滑动对应的差值,同时也应设置一个允许滑动的最大值;
  • 监听原生touchend事件,若此时元素滑动达到最大值,则触发callback,同时将translateY重设为0,元素回到初始位置

举个例子:

Html结构如下:

1
2
3
4
5
6
7
8
9
10
11
<main>
<p class="refreshText"></p >
<ul id="refreshContainer">
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
<li>555</li>
...
</ul>
</main>

监听touchstart事件,记录初始的值

1
2
3
4
5
6
7
8
9
10
var _element = document.getElementById('refreshContainer'),
_refreshText = document.querySelector('.refreshText'),
_startPos = 0, // 初始的值
_transitionHeight = 0; // 移动的距离

_element.addEventListener('touchstart', function(e) {
_startPos = e.touches[0].pageY; // 记录初始位置
_element.style.position = 'relative';
_element.style.transition = 'transform 0s';
}, false);

监听touchmove移动事件,记录滑动差值

1
2
3
4
5
6
7
8
9
10
11
12
13
_element.addEventListener('touchmove', function(e) {
// e.touches[0].pageY 当前位置
_transitionHeight = e.touches[0].pageY - _startPos; // 记录差值

if (_transitionHeight > 0 && _transitionHeight < 60) {
_refreshText.innerText = '下拉刷新';
_element.style.transform = 'translateY('+_transitionHeight+'px)';

if (_transitionHeight > 55) {
_refreshText.innerText = '释放更新';
}
}
}, false);

最后,就是监听touchend离开的事件

1
2
3
4
5
6
7
_element.addEventListener('touchend', function(e) {
_element.style.transition = 'transform 0.5s ease 1s';
_element.style.transform = 'translateY(0px)';
_refreshText.innerText = '更新中...';
// todo...

}, false);

从上面可以看到,在下拉到松手的过程中,经历了三个阶段:

  • 当前手势滑动位置与初始位置差值大于零时,提示正在进行下拉刷新操作
  • 下拉到一定值时,显示松手释放后的操作提示
  • 下拉到达设定最大值松手时,执行回调,提示正在进行更新操作

三、案例

在实际开发中,我们更多的是使用第三方库,下面以better-scroll进行举例:

HTML结构

1
2
3
4
5
6
7
8
9
<div id="position-wrapper">
<div>
<p class="refresh">下拉刷新</p >
<div class="position-list">
<!--列表内容-->
</div>
<p class="more">查看更多</p >
</div>
</div>

实例化上拉下拉插件,通过use来注册插件

1
2
3
4
5
import BScroll from "@better-scroll/core";
import PullDown from "@better-scroll/pull-down";
import PullUp from '@better-scroll/pull-up';
BScroll.use(PullDown);
BScroll.use(PullUp);

实例化BetterScroll,并传入相关的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
let pageNo = 1,pageSize = 10,dataList = [],isMore = true;  
var scroll= new BScroll("#position-wrapper",{
scrollY:true,//垂直方向滚动
click:true,//默认会阻止浏览器的原生click事件,如果需要点击,这里要设为true
pullUpLoad:true,//上拉加载更多
pullDownRefresh:{
threshold:50,//触发pullingDown事件的位置
stop:0//下拉回弹后停留的位置
}
});
//监听下拉刷新
scroll.on("pullingDown",pullingDownHandler);
//监测实时滚动
scroll.on("scroll",scrollHandler);
//上拉加载更多
scroll.on("pullingUp",pullingUpHandler);

async function pullingDownHandler(){
dataList=[];
pageNo=1;
isMore=true;
$(".more").text("查看更多");
await getlist();//请求数据
scroll.finishPullDown();//每次下拉结束后,需要执行这个操作
scroll.refresh();//当滚动区域的dom结构有变化时,需要执行这个操作
}
async function pullingUpHandler(){
if(!isMore){
$(".more").text("没有更多数据了");
scroll.finishPullUp();//每次上拉结束后,需要执行这个操作
return;
}
pageNo++;
await this.getlist();//请求数据
scroll.finishPullUp();//每次上拉结束后,需要执行这个操作
scroll.refresh();//当滚动区域的dom结构有变化时,需要执行这个操作
}
function scrollHandler(){
if(this.y>50) $('.refresh').text("松手开始加载");
else $('.refresh').text("下拉刷新");
}
function getlist(){
//返回的数据
let result=....;
dataList=dataList.concat(result);
//判断是否已加载完
if(result.length<pageSize) isMore=false;
//将dataList渲染到html内容中
}

注意点:

使用better-scroll实现下拉刷新、上拉加载时要注意以下几点:

  • wrapper里必须只有一个子元素
  • 子元素的高度要比wrapper要高
  • 使用的时候,要确定DOM元素是否已经生成,必须要等到DOM渲染完成后,再new BScroll()
  • 滚动区域的DOM元素结构有变化后,需要执行刷新 refresh()
  • 上拉或者下拉,结束后,需要执行finishPullUp()或者finishPullDown(),否则将不会执行下次操作
  • better-scroll,默认会阻止浏览器的原生click事件,如果滚动内容区要添加点击事件,需要在实例化属性里设置click:true

小结

下拉刷新、上拉加载原理本身都很简单,真正复杂的是封装过程中,要考虑的兼容性、易用性、性能等诸多细节

13. 没登录的功能做路由拦截

这里之前做的有个bug,向下面这么写,直接拿sessionStorage里的数据,如果sessionStorage为空,则会直接返回白屏,什么都没有的bug。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 路由守卫
router.beforeEach(async (to, from, next) => {
let routerList = JSON.parse(sessionStorage.getItem('routerList'));
routerList = routerList.filter(item => {
return item.state === 0;
})
let router = routerList.map(item => {
return item.router;
})
let username = sessionStorage.getItem('username');
console.log('vuex', routerList);
console.log('router', router);
if (username) { //判断当前路由是否需要进行权限控制
console.log('toPath', to.path);
// 包含不让访问的路径
if(router.includes(to.path)) {
// 您没有访问的权限
alert('您没有访问的权限');
next('/')
} else {
next();
}
} else {
let toPath = to.path;
if(toPath.includes('/question') || toPath.includes('/home/greenPlantsPage')) {
//把未登录的时候向去而没有去成的信息,存储于地址栏中【路由】
next('/login?redirect=' + toPath)
} else {
//去的不是上面这些路由(question/green)---放行
next();
}
}
})

完美无错的写法应该看 garbage 写的,要结合 vuex。

Login.vue

要保存各个数据

TopBanner.vue

退出登录后要清除的数据

vuex 下的 user

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { getToken, getRouterList } from '../../utils/token'
const state = {
token: getToken(),
routerList: JSON.parse(getRouterList())
}

const mutations = {
}

const actions = {}

const getters = {}

export default {
state,
mutations,
actions,
getters
}

utils 下的 token 工具包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//存储token
export const setToken = (token) => {
sessionStorage.setItem("token", token);
};
//获取token
export const getToken = () => {
return sessionStorage.getItem("token");
};

//清除本地存储的token
export const removeToken = () => {
sessionStorage.removeItem("token");
}

// 存储routerList
export const setRouterList = (routerList) => {
sessionStorage.setItem('routerList', routerList);
}

// 获取 routerList
export const getRouterList = () => {
return sessionStorage.getItem('routerList');
}

// 清除routerList
export const removeRouterList = () => sessionStorage.removeItem('routerList');

最后要写的路由守卫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 路由守卫
router.beforeEach(async (to, from, next) => {
// let router = null;
console.log('store', store.state.user.routerList);
if (store.state.user.routerList) {
let routerList = store.state.user.routerList;
routerList = routerList.filter(item => {
return item.state === 0;
})
router = routerList.map(item => {
return item.router;
})
}
// let username = sessionStorage.getItem('username');
// console.log('vuex', routerList);
// console.log('router', router);
let token = store.state.user.token;
console.log('token', token);
if (token) { //判断当前路由是否需要进行权限控制
console.log('toPath', to.path);
// 包含不让访问的路径
if (router.includes(to.path)) {
// 您没有访问的权限
Message({
message: "您没有访问的权限",
type: 'waring'
})
next('/')
} else {
next();
}
} else {
let toPath = to.path;
if (toPath.includes('/question') || toPath.includes('/home/greenPlantsPage')) {
//把未登录的时候向去而没有去成的信息,存储于地址栏中【路由】
next('/login?redirect=' + toPath)
} else {
//去的不是上面这些路由(question/green)---放行
next();
}
}
})

14. vue 路由守卫及elementUI 的Message在路由守卫里的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { Message } from 'element-ui'


router.beforeEach(async (to, from, next) => {
// let router = null;
console.log('store', store.state.user.routerList);
if (store.state.user.routerList) {
let routerList = store.state.user.routerList;
routerList = routerList.filter(item => {
return item.state === 0;
})
router = routerList.map(item => {
return item.router;
})
}
// let username = sessionStorage.getItem('username');
// console.log('vuex', routerList);
// console.log('router', router);
let token = store.state.user.token;
console.log('token', token);
if (token) { //判断当前路由是否需要进行权限控制
console.log('toPath', to.path);
// 包含不让访问的路径
if (router.includes(to.path)) {
// 您没有访问的权限
Message({
message: "您没有访问的权限",
type: 'waring'
})
next('/')
} else {
next();
}
} else {
let toPath = to.path;
if (toPath.includes('/question') || toPath.includes('/home/greenPlantsPage')) {
//把未登录的时候向去而没有去成的信息,存储于地址栏中【路由】
next('/login?redirect=' + toPath)
} else {
//去的不是上面这些路由(question/green)---放行
next();
}
}
})