1. echarts 树形结构配置
配置效果如下:
完整配置代码如下:(可运行),慢慢看,你终将会实现你想要的效果。
<template>
<div class="ent-structure" ref="ent-structure">
<div class="ent-tree" ref="ent-tree" :style="{ width: '800px', height: '800px' }"></div>
</div>
</template>
<script>
import * as echarts from "echarts";
export default {
data() {
return {
treeData: {
id: "1362",
name: "总企业",
relationType: 0,
childList: [
{
id: "2095",
name: "测试企业1",
relationType: 1,
childList: [],
},
{
id: "2120",
name: "测试企业2",
relationType: 2,
childList: [
{
id: "2121",
name: "测试企业21",
relationType: 1,
childList: [],
},
{
id: "2122",
name: "测试企业22",
relationType: 1,
childList: [],
},
],
},
{
id: "1448",
name: "测试企业3",
relationType: 2,
childList: [
{
id: "2048",
name: "测试企业31",
relationType: 1,
childList: [],
},
{
id: "2177",
name: "90794",
relationType: 1,
childList: [],
},
{
id: "2179",
name: "51425",
relationType: 2,
childList: [
{
id: "2180",
name: "67685",
relationType: 2,
childList: [],
},
{
id: "2181",
name: "666",
relationType: 1,
childList: [],
},
],
},
{
id: "2188",
name: "28701",
relationType: 2,
childList: [],
},
],
},
],
},
Echarts: null,
childLength: [],
};
},
mounted() {
this.formatData(this.treeData);
window.onresize = () => {
this.$nextTick(() => {
this.Echarts.resize();
});
};
},
methods: {
formatData(data) {
this.$nextTick(() => {
this.recursive(data);
this.setCanvasWidth();
this.renderTree(data);
});
},
// 递归-给每个子节点中添加 children
recursive(data) {
if (data.childList) {
this.childLength.push(data.childList.length);
data.children = data.childList;
data.childList.forEach((item) => {
this.recursive(item);
});
} else {
return;
}
},
// 根据树的childList设置容器宽度
setCanvasWidth() {
let container = document.getElementsByClassName("ent-structure")[0];
let maxLen = Math.max(...this.childLength);
if (maxLen <= 5) {
container.style.width = "1000px";
container.style.height = "700px";
} else {
container.style.width = `${maxLen * 180}px`;
}
},
// 绘制企业结构的echarts
renderTree(data) {
this.childLength = [];
this.Echarts = echarts.init(this.$refs["ent-tree"]);
const options = {
tooltip: {
// 提示框浮层设置
trigger: "item",
triggerOn: "mousemove", // 提示框触发条件
},
series: [
{
type: "tree",
data: [this.treeData],
nodePadding: 30, //结点间距 (发现没用)
layerPadding: 10, //连接线长度 (发现没用)
name: "树图",
top: "1%", // 组件离容器上侧的距离,像素值20,或相对容器的百分比20%
left: "1%", // 组件离容器左侧的距离
bottom: "1%", // 组件离容器下侧的距离
right: "1%", // 组件离容器右侧的距离
layout: "orthogonal", // 树图的布局,正交orthogonal和径向radial两种
orient: "TB", // 树图中正交布局的方向,'LR','RL','TB','BT',只有布局是正交时才生效
// edgeShape: 'polyline', // 树图边的形状,有曲线curve和折线polyline两种,只有正交布局下生效
zoom: 0.8, //当前视角的缩放比例
roam: true, //是否开启平游或缩放 // 是否开启鼠标缩放或平移,默认false
scaleLimit: {
//滚轮缩放的极限控制
min: 0.2,
max: 1,
},
initialTreeDepth: undefined, // 树图初始的展开层级(深度),根节点是0,不设置时全部展开
symbol: "circle", // 标记的图形,默认是emptyCircle;circle,rect,roundRect,triangle,diamond,pin,arrow,none
// symbolRotate: 270, // 配合arrow图形使用效果较好
symbolSize: 6, // 大于0时是圆圈,等于0时不展示,标记的大小
itemStyle: {
// 树图中每个节点的样式
color: "#0780ED", // 节点未展开时的填充色
// borderWidth: 1, // 描边线宽,为0时无描边
// borderType: 'dotted', // 描边类型
borderCap: "round", // 指定线段末端的绘制方式butt方形结束,round圆形结束,square
// shadowColor: 'rgba(0,121,221,0.3)', // 阴影颜色
// shadowBlur: 16, // 图形阴影的模糊大小
// opacity: 1 // 图形透明度
},
label: {
// 每个节点对应的文本标签样式
show: true, // 是否显示标签
distance: 8, // 文本距离图形元素的距离
position: ["50%", "50%"], // 标签位置
verticalAlign: "middle", // 文字垂直对齐方式,默认自动,top,middle,bottom
align: "center", // // 设置非叶子节点标签居中对齐,文字水平对齐方式,默认自动,left,right,center
fontSize: 16, // 字体大小
color: "#333", // 字体颜色
// color: 'red', // 字体颜色
backgroundColor: "#F8F9FA", // 文字块的背景颜色
borderColor: "#CED4DA", // 文字块边框颜色
borderWidth: 1, // 文字块边框宽度
borderType: "solid", // 文字块边框描边类型 solid dashed dotted
borderRadius: 2, // 文字块的圆角
padding: [6, 12], // 文字块内边距
shadowColor: "rgba(0,121,221,0.3)", // 文字块的背景阴影颜色
shadowBlur: 6, // 文字块的背景阴影长度
// height: 130,
// width: 12,
// rotate: -90,
// 文字超出宽度是否截断或者换行;只有配置width时有效
// overflow: 'truncate', // truncate截断,并在末尾显示ellipsis配置的文本,默认为...;break换行;breakAll换行,并强制单词内换行
// ellipsis: '...',
formatter: (params) => {
let newName = "";
let len = params.data.name.length;
let strLen = params.data.relationType === 0 ? 100 : 1; //一行显示几个字
let rowNum = Math.ceil(len / strLen);
if (len > strLen) {
for (let p = 0; p < rowNum; p++) {
let tempStr = "";
let start = p * strLen;
let end = start + strLen;
if (p == rowNum - 1) {
tempStr = params.data.name.substring(start, len);
} else {
tempStr = params.data.name.substring(start, end) + "\n";
}
newName += tempStr;
}
} else {
newName = params.data.name;
}
let n = newName;
if (n.length > 16) {
n = n.slice(0, 16) + "...";
}
let str = `{name|${n}}`;
let isMain = params.data.relationType === 0;
str = isMain ? `{name|${n}} {current|本企业} {main|总部}` : str;
return str;
},
rich: {
name: {
fontSize: 14,
},
current: {
fontSize: 12,
padding: 0,
color: "#07CA42",
fontWeight: 600,
backgroundColor: "#E5F7EA",
borderRadius: 2,
},
main: {
color: "#0780ED",
fontSize: 12,
padding: 0,
fontWeight: 600,
backgroundColor: "#E6F2FD",
borderRadius: 2,
},
},
},
//设置叶子节点对齐
labelLayout: {
dy: -20,
verticalAlign: "top",//叶子节点顶部对齐
},
lineStyle: {
// 树图边的样式
// color: 'rgba(0,0,0,.35)', // 树图边的颜色
width: 1, // 树图边的宽度
// curveness: 1, // 树图边的曲度
shadowColor: "rgba(0, 0, 0, 0.5)", // 阴影颜色
// shadowBlur: 10 // 图形阴影的模糊大小
},
blur: {
// 淡出状态的相关配置,开启emphasis.focus后有效
itemStyle: {}, // 节点的样式
lineStyle: {}, // 树图边的样式
label: {}, // 淡出标签的文本样式
},
leaves: {
// 叶子节点的特殊配置
label: {
// 叶子节点的文本标签样式
distance: 0,
position: "bottom",
verticalAlign: "middle",
},
itemStyle: {}, // 叶子节点的样式
emphasis: {}, // 叶子节点高亮状态的配置
blur: {}, // 叶子节点淡出状态的配置
select: {}, // 叶子节点选中状态的配置
},
animation: true, // 是否开启动画
expandAndCollapse: true, // 子树折叠和展开的交互,默认打开
animationDuration: 550, // 初始动画的时长
animationEasing: "linear", // 初始动画的缓动效果
animationDelay: 0, // 初始动画的延迟
animationDurationUpdate: 750, // 数据更新动画的时长
animationEasingUpdate: "cubicInOut", // 数据更新动画的缓动效果
animationDelayUpdate: 0, // 数据更新动画的延迟
},
],
};
/**
* 遍历数节点,修连接线的颜色
*/
function nodesStutes(nodes) {
if (nodes.childList && nodes.childList.length) {
for (let i = 0; i < nodes.childList.length; i++) {
if (nodes.childList[i].relationType === 2) {
// 修改连线颜色
nodes.childList[i].lineStyle = {
color: "#6DD400",
};
} else if (nodes.childList[i].relationType === 1) {
nodes.childList[i].lineStyle = {
color: "#0780ED",
};
}
nodesStutes(nodes.childList[i]);
}
}
}
//调用函数
nodesStutes(data);
this.Echarts.setOption(options, true);
},
},
};
</script>
<style scoped>
.ent-structure {
width: 100%;
height: 100%;
background: #fff;
}
.ent-tree {
width: 100%;
height: 100%;
overflow: auto;
}
</style>
2. Vue2/Vue3中在css中使用js的变量(less、scss也适用)
2.1 Vue2 用法
在以前的 Vue2 中,我们通常使用计算属性反映 data 中的 变量到 css 中:
<template>
<div :style="cssVars">
<p class="text">测试文本</p>
</div>
</template>
<script>
export default {
data() {
return {
color: "red"
};
},
computed: {
cssVars() {
return {
"--color": this.color
};
}
}
};
</script>
<style lang="scss" scoped>
.text {
color: var(--color);
}
</style>
从而在 data 中的数据改变时,计算属性也会被同步改变并反映到挂载的节点 style
中。
2.2 Vue3 用法
在 Vue3 中,提供了一种新的快捷使用方法:v-bind() 。至于对于less和scss的支持都很好。如果是使用less的话,只需在<style>
标签里添加lang="less"
(即<style lang="less">
)即可,scss同理。
单文件组件的 <style>
标签支持使用 v-bind
CSS 函数将 CSS 的值链接到动态的组件状态:
<template>
<div class="text">hello</div>
</template>
<script>
export default {
data() {
return {
color: 'red'
}
}
}
</script>
<style>
.text {
color: v-bind(color);
}
</style>
这个语法同样也适用于 <script setup>
,且支持 JavaScript 表达式 (需要用引号包裹起来):
<script setup>
const theme = {
color: 'red'
}
</script>
<template>
<p>hello</p>
</template>
<style scoped>
p {
color: v-bind('theme.color');
}
</style>
并且最新版本的vue也支持模版语法了:
<script setup>
const theme = {
borderSize: 5
}
</script>
<template>
<p>hello</p>
</template>
<style scoped>
p {
border: v-bind(`${theme.borderSize}px`);
}
</style>
无论在 Vue2 还是 3 中,css 变量都是响应式的,无需担心不会更新的问题。
3. v-show设置动画
把需要有动画效果的含有v-show的标签用 <transition name="fade"></transition>
包裹起来
比如:
<transition name="fade">
<span v-show="isShow">动画效果</span>
</transition>
然后在css里写上
.fade-enter-active, .fade-leave-active {
transition: opacity .25s
}
.fade-enter, .fade-leave-to /* .fade-leave-active in <2.1.8 */ {
opacity: 0
}
4.Vue页面无法竖向滚动导致显示不全
在当前页面的最外层div标签中设置样式
.fragment-container{
height: 100vh;
overflow-y: scroll;
max-width: 100vw;
margin: 0 auto;
}
5.记住密码功能的实现
- 安装
js-cookie
和crypto-js
密码加密
npm i -S js-cookie
npm i -S crypto-js
- 引入
import Cookies from 'js-cookie'
Vue.prototype.$cookie = Cookies;
3.效果
4. 完整可实现代码如下:
<template>
<div>
<el-form :label-position="labelPosition" label-width="80px" :model="loginForm">
<el-form-item label="名称">
<el-input v-model="loginForm.username"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input v-model="loginForm.password" placeholder="请输入密码" show-password></el-input>
</el-form-item>
</el-form>
<el-checkbox v-model="rememberPassword">记住密码</el-checkbox>
<br>
<el-button @click="handleLogin">立即登陆</el-button>
</div>
</template>
<script>
import CryptoJS from "crypto-js";
export default {
name: "Login",
data() {
return {
labelPosition: "right",
rememberPassword: true,
loginForm: {
username: "",
password: "",
},
};
},
mounted() {
this.getCookie();
},
methods: {
getCookie() {
this.loginForm.username = this.$cookie.get("username");
if (this.$cookie.get("password")) {
//解密
this.loginForm.password = CryptoJS.AES.decrypt(
this.$cookie.get("password"),
"246521"
).toString(CryptoJS.enc.Utf8);
} else {
this.loginForm.username = "";
}
},
handleLogin() {
let code = 200;
if (code == 200) {
if (this.rememberPassword) {
this.$cookie.set("username", this.loginForm.username, {
expires: 3,
}); //创建有效期为3天的cookie
// 第二个参数为密钥,这里我设置为123456
this.$cookie.set(
"password",
CryptoJS.AES.encrypt(this.loginForm.password, "246521"),
{ expires: 3 }
);
} else {
this.$cookie.remove("username");
this.$cookie.remove("password");
}
this.$router.push("/test");
}
},
},
};
</script>
6. 前端实现二维码展示
前端插件QRCode.js生成二维码
- 安装
npm install qrcodejs2 --save
- 完整代码,,可实现代码
<template>
<div class="test">
<div class="qrcode">
<!-- 二维码 -->
<div id="qrcode" ref="qrcode"></div>
</div>
<el-button @click="clearClick" size="small">清理</el-button>
<el-button @click="reProducet" size="small">重新生成</el-button>
</div>
</template>
<script>
// 引用
import QRCode from "qrcodejs2";
export default {
data() {
return {
qrcode: {},
};
},
methods: {
reProducet() {
this.qrcode.makeCode("hhhhh");
},
clearClick() {
// console.log("清理", this.qrcode.clear);
this.qrcode.clear();
},
creatQrCode() {
let text = "https://juejin.cn/user/2305013721343703/posts";
this.qrcode = new QRCode(this.$refs.qrcode, {
text: text, //二维码内容字符串
width: 128, //图像宽度
height: 128, //图像高度
colorDark: "#000000", //二维码前景色
colorLight: "#ffffff", //二维码背景色
correctLevel: QRCode.CorrectLevel.H, //容错级别
});
},
},
mounted() {
this.creatQrCode();
},
};
</script>
<style scoped>
.qrcode {
display: flex;
justify-content: center;
align-items: center;
}
</style>
- 方法
// 清理二维码
qrcode.clear();
// 生成新的二维码
qrcode.makeCode("https://juejin.cn/user/2305013721343703/posts");
- options 选项详细介绍
选项 | 作用 | 默认值 | 值类型 | 说明 |
---|---|---|---|---|
text | 二维码包含的信息 | "" | string | |
width | 二维码的宽度 | 256 | Number | |
height | 二维码的高度 | 256 | Number | |
colorDark | 二维码颜色 | #000000 | String | 与 canvas 支持的颜色格式一致 |
colorLight | 二维码背景色 | #ffffff | String | 与 canvas 支持的颜色格式一致 |
correctLevel | 模糊程度 | QRCode.CorrectLevel.H | Number | 由低到高:QRCode.CorrectLevel.M、QRCode.CorrectLevel.L、QRCode.CorrectLevel.H、QRCode.CorrectLevel.Q,级别越高二维码的容量就会越低,建议使用低级别 |