前言:记录项目相遇功能难点,为后续项目做参考,避免翻找原来项目
1.实现对数组对象某个属性值去重
const pageList = [
{ fullPath: '/targetEstimate/ceTask' },
{ fullPath: '/targetEstimate/ceTask2' },
{ fullPath: '/dashboard/analysis' },
{ fullPath: '/dashboard/analysis' },
];
const uniquePages = Array.from(pageList.reduce((map, page) => {
map.set(page.fullPath, page);
return map;
}, new Map()).values());
console.log(uniquePages);
2.echarts树形图根据字节点多少决定容器宽度、高度,避免固定宽高元素重叠挤压
// 树图配置
const option = {
// 鼠标悬浮提示过长处理
tooltip: {
trigger: 'item',
triggerOn: 'mousemove',
extraCssText: 'max-width:600px; white-space:pre-wrap',
formatter: function (params) {
if (params.data.formula) {
return `<div>${params.data.formula}</div>`
} else {
return params.data.formula
}
},
},
series: [
{
type: 'tree',
symbolSize: ['150', 60],
left: 0,
right: 0,
top: '8%',
bottom: '13%',
symbol: `image://${zi}`,
edgeShape: 'polyline',
orient: 'TB',
lineStyle: { color: '#3193FF', width: 2 },
label: {
position: ['50%', '20%'],
align: 'center',
color: '#fff',
formatter: ({ data }) => {
return `{name|${data.score}(分)}\n${data.name || ''}`
},
rich: {
name: { fontWeight: 700, fontSize: 20, padding: [0, 0, 10, 0] },
},
},
data: [
{
symbolSize: [188, 60],
symbol: `image://${gen}`,
lineStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0.5, color: '#FE7000' },
{ offset: 0.9, color: '#31A2FF' },
]),
},
...this.targetInfo.targetList[0],
},
],
},
],
}
this.ec5 = echarts.init(this.$refs.ec5)
this.ec5.setOption(option)
// 根据子项目多少决定宽度度
let container = this.$refs.ec5
let allNode = 0
let nodes = this.ec5._chartsViews[0]._data._graphicEls
for (let i = 0, count = nodes.length; i < count; i++) {
let node = nodes[i]
if (node === undefined) continue
allNode++
}
// 根据子项目多少决定高度
// let height = window.innerHeight
// let currentHeight = 35 * allNode
// let newHeight = Math.max(currentHeight, height)
// container.style.height = newHeight + 'px'
let width = window.innerWidth
let currentWidth = 150 * allNode
let newWidth = Math.max(currentWidth, width)
// 判断字节点小于等于5容器就100%显示
if (nodes.length <= 5) {
container.style.width = '100%'
} else {
// 超过5个根据节点宽度*个数,设置容器宽度
container.style.width = newWidth + 'px'
}
3.echarts平均线(数值、阴影)
markLine: {
symbolSize: 6,
label: { position: 'insideEndTop', formatter: '{b}: {c}' },
data: [{ name: '平均分', type: 'average' }],
lineStyle: {
color: '#3193FF',
type: 'solid',
width: 2,
shadowBlur: 10, // 阴影模糊度
shadowColor: '#BFDCFF', // 阴影颜色
},
},
4.多行3列瀑布流
.box{ /* 瀑布流父容器 */
column-count: 3; /* 控制屏幕分为多少列 */
column-gap: 10px; /* 控制列与列之间的距离 */
}
5.实现两个数组长度一样的数,合并成一个固定格式的对象格式数组
const arr1 = ['项目1','项目2','项目3']
const arr2 = [15,33,6]
arr2.map((value, index) => ({ name: arr1[index], value }));
6.前后补0或者其它字符
padEnd()
方法会将当前字符串从末尾开始填充给定的字符串(如果需要会重复填充),直到达到给定的长度。填充是从当前字符串的末尾开始的。
padStart()
方法用另一个字符串填充当前字符串(如果需要会重复填充),直到达到给定的长度。填充是从当前字符串的开头开始的。
console.log('5'.padStart(2, '0'));
// 输出: "05"
console.log('Breaded Mushrooms'.padEnd(25, '.'));
// 输出: "Breaded Mushrooms........"
7.折叠面板(展开多个面板、手风琴效果)
// 1. 可同时展开多个面板,面板之间不影响
// closedIndexes:array 收起索引数组 (如果当前项已在关闭列表中)
if (this.closedIndexes.includes(uuid)) {
// 移除它,使其展开
this.closedIndexes = this.closedIndexes.filter(index => index !== uuid);
} else {
// 当前项不在关闭列表中,将其添加进去并收起
this.closedIndexes.push(uuid);
}
// 模板样式样式
closedIndexes.includes(a.uuid)?'展开样式':'收起样式'
// 2.手风琴效果,每次只能展开一个面板
// dropIndex:string|number 当前打开的索引(点击项等于dropIndex ,先置空再赋值)
if (this.dropIndex === i) {
return this.dropIndex = ''
}
this.dropIndex = i
// 模板样式样式
dropIndex===index ? 'arrow-up' : 'arrow-down'
8.uniapp点击滚动指定元素位置
//目标位置的节点:类class或者id
uni.createSelectorQuery().select("#app" + index).boundingClientRect(data => {
//最外层盒子的节点:类class或者id
uni.createSelectorQuery().select(".box1").boundingClientRect(res => {
uni.pageScrollTo({ duration: 100, scrollTop: data.top + 80 - res.top
})
}).exec()
}).exec();
9.vue实现页面缓存
// 路由对象需要被缓存的添加
meta: {
keepAlive: true
}
// router-view外层添加keep-alive
<div id="app">
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
</div>
10.滚动到页面顶部
// options 包含以下参数的对象:
// top:指定沿 Y 轴滚动窗口或元素的像素数。
// left:指定沿 X 轴滚动窗口或元素的像素数。
// behavior:
// smooth 表示平滑滚动并产生过渡效果
// instant 表示滚动会直接跳转到目标位置,没有过渡效果
// auto 或缺省值表示浏览器会自动选择滚动时的过渡效果
window.scrollTo({ top: 0, behavior: 'smooth' });
11.echarts图表自动滚动,鼠标移入停止滚动,移出滚动继续
// echartConfig 文件代码
export default {
/**
* echarts x轴滚动条
* @param {Number} dataLength series.data数组长度
* @param {Number} endValue 一次显示多少条
* @returns datazoom对象
*/
dataZoom: (dataLength = 0, endValue = 11) => {
return {
show: dataLength > endValue ? true : false,
type: 'slider',
startValue: 0,
endValue,
showDataShadow: false,
// 拖动显示文字
textStyle: false,
fillerColor: '#019069',
moveHandleSize: 0,
borderColor: 'rgba(1, 144, 105, 0.2)',
backgroundColor: 'rgba(1, 144, 105, 0.2)',
height: 6,
bottom: 15,
handleStyle: {
borderWidth: 0,
color: '#019069'
}
}
},
/**
* echarts图标自动滚动
* @param {object} documentEC echarts实例对象
* @param {object} option echarts图表配置
* @param {Number} dataLength series.data数组长度
* @param {Number} endValue 一次显示多少条
*/
autoMove: (documentEC, option, dataLength, endValue = 11) => {
option.timer = setInterval(() => {
if (Number(option?.dataZoom.endValue) === dataLength - 1) {
option.dataZoom.endValue = endValue;
option.dataZoom.startValue = 0;
} else {
option.dataZoom.endValue = option?.dataZoom.endValue + 1;
option.dataZoom.startValue = option?.dataZoom.startValue + 1;
}
documentEC.setOption(option);
}, 3000);
}
}
// 页面中使用
const option = {
dataZoom: ec.dataZoom(15),
}
const automaticEC = () => ec.autoMove(departmentEC, option,option?.dataset.source.length)
automaticEC()
departmentEC.on('mouseover', () => clearInterval(option.timer))
departmentEC.on('mouseout', automaticEC)
departmentTimer = option.timer
12.echarts多个系列设置不同颜色图例
用途:UI图表是渐变色,图例是纯色,可以这么处理
const marks = [
{ name: '复用额', color: '#FFCB17' },
{ name: '回收额', color: color }
]
legend: {
textStyle: { color: '#fff' },
data: marks.map(i => ({ name: i.name, itemStyle: { color: i.color } }))
}
13.vuex的使用
// store文件
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
export default new Vuex.Store({
state: {
select: {
year: '2024年',
month: '05月'
},
},
// 计算属性
getters: {
selects (state) {
return state.select
}
},
// 修改state属性值且必须是同步函数
mutations: {
updateSelect(state, data) {
state.select = data
}
},
// 做异步处理:发送ajax请求,通过 store.dispatch 方法触发
actions: {
activeSelect(context) {
setTimeout(() => {
commit('updateSelect',{year:'5099',month:'99'})
}, 1000)
}
},
// 分模块
modules: {a:moduleA}
})
// 页面中修改store.state中属性的值
this.$store.commit('updateSelect',newVal)
// 页面中读取store.state的值
this.$store.state.doneTodosCount
// 页面中读取store.getters的值
this.$store.getters.doneTodosCount
// 页面中异步处理store.active
this.$store.store.dispatch('activeSelect')
14.实现将数字按千位逗号分隔并保留两位小数
function formatNumber(number) {
// 将数字转换为字符串并根据小数点分割整数部分和小数部分
let parts = number.toString().split('.');
// 处理整数部分,添加千位分隔符
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
// 如果有小数部分,则保留两位小数;否则补充".00"
if (parts.length > 1) {
parts[1] = parts[1].substring(0, 2);
} else {
parts.push("00");
}
return parts.join(".");
}
// 测试示例
console.log(formatNumber(5000)); // 输出: "5,000.00"
console.log(formatNumber(8557.674)); // 输出: "8,557.67"
15.使用html2Canvas 点击按钮把某个元素 下载为图片
问题:在项目中使用html2Canvas进行截图时发现无法截取滚动条部分
解决思路:当我们截取的div容器的宽和高与内部的子容器div的宽和高不一样时,内部div就会出现滚动条,因为我们截取的div与内部div的宽和高不一致,所以截取时只能截取我们定位的div,无法截取到滚动条部分。
截取元素的宽高改成和滚动的宽和高一致,截取时就能包含滚动条部分,截取结束后宽高再恢复原来值。
<template>
<div class="about" ref="capture">
<div class="gantt_data_area" style="width: 3200px;background-color: red;">
<button @click="download" style="position: fixed;left: 0;">download</button>
<div style="height: 1500px; background-color: pink;"></div>
</div>
</div>
</template>
<script>
import html2canvas from 'html2canvas';
export default {
methods: {
download() {
let targetDom = this.$refs.capture; //原本需要截图的div
this.$nextTick(() => {
const swidth = document.querySelector('.gantt_data_area'); // 定位到内层出现滚动条div宽度
targetDom.style.width = `${swidth.offsetWidth + 300}px`;
html2canvas(targetDom, {
dpi: window.devicePixelRatio * 4,
scale: 4,
useCORS: true, //允许canvas画布内可以跨域请求外部链接图片, 允许跨域请求。
allowTaint: false,
height: targetDom.scrollHeight, //画布高度(包含未显示的滚动部分)
width: targetDom.scrollWidth, //画布高度(包含未显示的滚动部分)
windowWidth: targetDom.scrollWidth,
windowHeight: targetDom.scrollHeight,
}).then((canvas) => {
var imgUri = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream') // 获取生成的图片的url
var saveLink = document.createElement('a')
saveLink.href = imgUri
saveLink.download = 'downLoad.png'
saveLink.click()
targetDom.style.width = '100%'; // 截图结束后需要将修改的宽度高度恢复
targetDom.style.height = '100%';
});
});
}
}
}
</script>
16.获取cookie名未指定名称的值
document.cookie = "token=11111111111111111111";
document.cookie = "name=11111111111111111111";
// 获取cookie名未token的值
var token = document.cookie.replace(/(?:(?:^|.*;\s*)token\s*\=\s*([^;]*).*$)|^.*$/, "$1");
17.计算百分比
百分比 = (已使用量 / 总量) * 100%
将给定的数字代入公式中:
百分比 = (10 / 300) * 100
18.递归获取某个值
function getUncheckedIds(data) {
let uncheckedIds = [];
function traverse(item) {
if (item.hasChecked === 0) {
uncheckedIds.push(item.id);
}
if (item.children && item.children.length > 0) {
for (const child of item.children) {
traverse(child);
}
}
}
for (const item of data) {
traverse(item);
}
return uncheckedIds;
}
const data = [{id: 1, hasChecked: 0, children: [ {id: 1, hasChecked: 1, children: [ { id: 2,hasChecked: 0 } ] } ] }];
const uncheckedIds = getUncheckedIds(data);
console.log(uncheckedIds); // Output: [1, 2]
19.递归给每一项都加上 isSelected: false
function addIsSelectedProperty(data) {
function traverse(item) {
item.isSelected = false;
if (item.children && item.children.length > 0) {
for (const child of item.children) {
traverse(child);
}
}
}
for (const item of data) {
traverse(item);
}
return data;
}
const data = [
{
id: 1,
hasChecked: 0,
children: [ {id: 1, hasChecked: 1, children: [ {id: 2, hasChecked: 0 } ] } ]
}
];
const updatedData = addIsSelectedProperty(data);
console.log(updatedData);
20.实现复制,将指定内容写到剪切板
const copyItem = async (item) => {
await navigator.clipboard.writeText(`试卷名称:${item.name}`)
message.success('复制成功');
}
21.uniapp路由跳转携带对象字符报有关encodeURIComponent的错
uni.navigateTo({url: `./children/auditLog?item=${encodeURIComponent(JSON.stringify(item))}`})
22.JS实现JSON对象与URL参数的相互转换
const json = {
a:1,
b:2
}
// 1. json 转url参数
// 第一种:循环处理
const jsonToUrlParam = (json) => Object.keys(json).map(key => `${key}=${json[key]}`).join('&')
// 第二种:URLSearchParams
new URLSearchParams(json).toString();
console.log(jsonToUrlParam(json))
console.log(new URLSearchParams(json).toString())
// 输出:a=1&b=2
// URL参数转换为JSON对象
function urlParamToJson(urlParam) {
let json = {};
urlParam.trim()
.split('&')
.forEach(item => json[item.split('=')[0]] = item.split('=')[1]);
return json;
}
console.log(urlParamToJson(json))
// 输出 {a:1,b:2}
结语:后续会持续更新......