Echarts y轴name显示问题
帮同事解决一个很简单的一个问题 用到的 api地址直达 api地址直达
直接随便找个案例 在这里写
option = {
xAxis: {},
yAxis: {
name: 'y\n轴\n标\n题', //坐标名称 \n来换行
nameTextStyle: { //文字样式
color: 'red', //颜色
align: 'center', //径向水平对齐方式
verticalAlign: 'middle', //径向轴名称文字的垂直对齐方式
padding: [200, 70, -10, -10],//上右下左
fontSize: 20,//字体大小
},
nameLocation: 'center',//得到坐标轴名称的显示位置。
nameRotate: 0,//坐标轴名字旋转,是个角度值
},
series: [{
data: [[10, 40], [50, 100], [40, 20]],
type: 'line'
}]
};
ant design 有多个table组件复选框选中统计合并问题
- 项目需求,需要将数据分组以table形式展示可以选择最后提交所有组下选中人员的集合
onChange 事件无法满足此需求,查看api 【onSelect,onSelectAll】
//table 组件[子组件]
<a-table
:columns="columnstable"
:data-source="data"
:scroll="{ x: 1000, y: 500 }"
:pagination="false"
:row-selection="rowSelection"
></a-table>
data() {
return {
rowSelection: {
onSelect: this.onSelect,
onSelectAll: this.onSelectAllEvent
},
}
},
methods: {
onSelect(record, selected, selectedRows) {
//获取单个取消或选中的对象 来分别处理多个表格的选中状态
this.$emit('oneAction',{selected:selected,record:record})
},
onSelectAllEvent(selected, selectedRows, changeRows) {
//获取每个表格的全选或全不选数据 来分别处理多个表格的选中状态
this.$emit('allAction',{selected:selected,changeRows:changeRows})
},
},
//父组件
<selectTable
@oneAction="oneAction"
@allAction="allAction"
></selectTable>
//seachTable 组件单个复选框操作
oneAction(params){
params.selected?this.getRuslutData(params.record,'push'):this.getRuslutData(params.record,'onePop')
},
//seachTable 每个组件全选操作
allAction(paramsAry){
paramsAry.selected?this.getRuslutData(paramsAry.changeRows,'pushAll'):this.getRuslutData(paramsAry.changeRows,'onePopAll')
},
/*
*params
*data[Array || Object]
*type[String] 操作状态
*/
getRuslutData(data,type){
if(type == 'push'){
this.selectDataCach.push(data)
}else if(type == 'onePop'){
this.selectDataCach = this.selectDataCach.filter(k => data.vctid !== k.vctid)
}else if(type == 'pushAll'){
this.selectDataCach = [...this.selectDataCach,...data]
}else{
//取差集
this.selectDataCach = this.selectDataCach.concat(data).filter(m => !(data.map(z =>z.vctid)).includes(m.vctid))
}
this.formdata.selectdata = this.selectDataCach.map(i => i.vctid)
}
vue跳外链回退不触发方法问题
window.addEventListener('pageshow', ()=> {
//这里写要触发的代码
});
数组去重
//数组去重,今天项目中用到数组去重,总结下几总方便好用的方法
//方法一,不得不说reduce真的很强大【简单明了 推荐】
let _obj = {}
aryList.reduce((pro,cur) =>{
cur.id in _obj?null:(_obj[cur.id] = '',pro.push(cur))
return pro
},[])
//方法二 Map【set不可重复,这个更简单】
let aryMap = new Map()
aryList.map(k => aryMap.set(k.id,k))
console.log([...aryMap.values()])
方法... 其他方法万变不离其宗
//判断对象中的属性值是否有空值 有则返回false
Object.values(i).every(m => m !== '' && m !== null)
记一次封装axios 网略请求问题
//响应拦截err时 监听状态码监听不到
//因为是项目迭代 之前封装没注意过这块
//查了下error.response 就可以取到状态码了
...
error => {
if (error.status) ...
}
...
vue3+ts 遇到的问题都在这里
vue2.0 的时候父组件调用子组件的方法,很简单 直接this.$refs.xx.xxx() 就行
今天用vue3.0 + ts 来实现父组件调用子组件的方法
//子组件 父组件调用 openShar 来改变 showShare 没什么可说的
setup() {
const showShare = ref<boolean>(false)
const options = reactive([]);
const openShar = () =>{
showShare.value = true
}
return {
options,
showShare,
onSelect,
openShar
};
},
//父组件 Teleport 【瞬移】
<Teleport to="body">
<my-share ref="shareRef" />
</Teleport>
setup(){
const navbarInfo = reactive<navBarInfoType>({
title:'Vue3+Ts',
backText:'返回',
rightText:'分享'
})
//因为使用了ts 直接const shareRef = ref<boolean>(false) 这样定义
//shareRef.value 索引不到 而且编译器会报错
//要写成下面这样 ref<InstanceType<typeof myShare>> [mySahare 是子组件]
const shareRef = ref<InstanceType<typeof myShare>>()
//打开分享
const openShare = () =>{
//这样就可以被ts索引到了
shareRef.value?.openShar()
}
return {
...toRefs(navbarInfo),
openShare,
shareRef
}
}
今天遇到一个微信中打开h5网页,跳转路由后屏幕底部出现操作栏导致动态计算页面计算滚动区域的高度失效
//通过window.onresize监听可视区域的变化,然后赋值给响应数据,watch 监听数据重新赋值,直接上代码
<template>
<div id="appraisal">
<div ref="titRef" class="title">填写信息并评价</div>
<div class="content">
<div class="con-title">问题调查</div>
<div class="pro-con" :style="radioGroupStyle">
<div
class="pro-item"
v-for="(item, index) in starAry"
:key="`pro-${index}`"
>
<div class="pro-item-h">{{ i }}.查勘员是否准时到场</div>
<van-rate
class="rate"
v-model="item.strNum"
color="#ffd21e"
void-icon="star"
void-color="#eee"
/>
</div>
</div>
<div style="width: 100%; text-align: center">
<span ref="btnRef" class="submit">提交</span>
</div>
</div>
</div>
</template>
<script lang='ts'>
import { ref, onMounted, computed, reactive, toRefs, watch } from "vue";
interface radioGroupStyleType {
height: string;
"overflow-y": string;
"-webkit-overflow-scrolling": string;
}
interface starObjItemType {
id: string;
strNum: number;
}
interface reactiveType {
starAry: starObjItemType[];
radioGroupHeight: string;
documentHeight: number;
}
export default {
name: "",
setup() {
const value = ref(3);
const data = reactive<reactiveType>({
radioGroupHeight: "",
documentHeight: 0,
starAry: [
{ id: "1", strNum: 3 },
{ id: "2", strNum: 1 },
{ id: "3", strNum: 3 },
{ id: "4", strNum: 2 },
{ id: "5", strNum: 3 },
{ id: "6", strNum: 2 },
],
});
window.onresize = () => {
return (() => {
data.documentHeight = document.body.clientHeight;
})();
};
const titRef = ref<HTMLElement>();
const btnRef = ref<HTMLElement>();
const getRadioGroupHeight = (documentHeight: number) => {
documentHeight == 0? documentHeight = document.body.clientHeight:documentHeight
data.radioGroupHeight =
documentHeight -
Number(titRef.value?.clientHeight) -
Number(btnRef.value?.clientHeight) -
230 +
"px";
};
watch(data, (val) => {
getRadioGroupHeight(val.documentHeight);
});
const radioGroupStyle = computed((): radioGroupStyleType => {
return {
"overflow-y": "auto",
height: data.radioGroupHeight,
"-webkit-overflow-scrolling": "touch",
};
});
onMounted(() => {
getRadioGroupHeight(data.documentHeight);
});
return {
value,
titRef,
btnRef,
radioGroupStyle,
...toRefs(data),
activeIcon: "https://img.yzcdn.cn/vant/user-active.png",
inactiveIcon: "https://img.yzcdn.cn/vant/user-inactive.png",
};
},
};
</script>
<style lang="less" scoped>
#appraisal {
background: #008681;
width: 100vw;
height: 100vh;
padding: 20px;
box-sizing: border-box;
.title {
width: 100%;
text-align: center;
color: #fff;
font-size: 30px;
margin-bottom: 20px;
}
.content {
background: #fff;
border-radius: 8px;
padding: 0 20px 20px 20px;
box-sizing: border-box;
.con-title {
font-size: 28px;
width: 100%;
text-align: center;
padding-top: 50px;
box-sizing: border-box;
margin-bottom: 20px;
}
.pro-item {
font-size: 20px;
margin-bottom: 20px;
.pro-item-h {
margin-bottom: 10px;
}
.rate {
padding-left: 15px;
}
}
}
.submit {
font-size: 20px;
padding: 2px 30px;
color: #fff;
background: #005a57;
border: 1px solid #fff;
display: inline-block;
margin: 0 auto;
margin-top: 20px;
}
.img-icon {
height: 20px;
}
}
/deep/ .radio .van-radio__icon {
font-size: 28px;
}
/deep/ .radio span {
font-size: 28px;
color: #fff;
margin-left: 30px;
}
::-webkit-scrollbar {
/*隐藏滚轮*/
display: none;
}
</style>
vant 下拉刷新遇到的坑
1.今天遇到的问题是下拉刷新与滚动条冲突,因为之前遇到过 refresh组件的父节点如果设置滚动就会引起浏览器回滚时出发下拉刷新
,检查了下refresh 父节点并没有设置overflow-scorll,就很神奇,然后发现父节点有设置overflow-x:hidden 搞定....
2.vant 中用toast.loading 做全局加载loading,要配置参数 duration:0 否则 会在默认的时间清楚loading ,这个问题被坑了两次
ts中 给window添加属性
//方法一
(<any>window).MyNamespace
//方法二 https://github.com/Microsoft/TypeScript/issues/3180#issuecomment-102523512
// typings.d.ts
declare interface Window {
myNamespace?: MyNamespace & typeof MyNamespace
}
declare interface MyNamespace {
somemethod?()
}
declare namespace MyNamespace {
// ...
}
公众号h5返回公众号首页
declare var window: Window & { WeixinJSBridge: any };
window.WeixinJSBridge.call("closeWindow");
ant design vue 表格多选问题
问题阐述:
table 做了多选的总操作,同时每一行后面也可以操作每一条数据,从而导致做完每一行的操作后,数据更新导致已选中的数据错乱(选中的值向下移了一条)
问题分析:
通过回调函数看选中的数据是用索引记录的,所以操作完一条后,选中的索引没有相应的删除,导致之前没被选中的数据选中
初步解决方案
一。指定选中的值不为索引,为指定的唯一Id
二。操作每一条时记录当前的索引,找到对应选中数据的索引,删除对应选中的索引,但是依然还会有问题
api中 table 组件中 rowKey 表格行 key 的取值,可以是字符串或一个函数
这样你选中后的数据就在selectedRowKeys数组里了(即指定不为索引为唯一id)
<a-table
:rowKey="
(record, index) => {
return record.id;
}
"
/>
文件夹/文件上传二次封装
1.同一组件实现文件夹及文件上传
2.上传的文件树重写【业务需求只上传一级文件,代码递归可扩展多级】
上代码 不做过多阐述,代码有注释
有一点是文件上传取不到集合只能watch 监听任务队列改变数据格式【感觉不是和合理,有没有更加合理的解决方式】
<template>
<div class="fileBox" @click="folderListReset">
<a-upload-dragger
class="fileBox"
name="file"
directory
:multiple="true"
:customRequest="customUpload"
:fileList="defaultFileList"
>
<p class="ant-upload-drag-icon">
<a-icon type="cloud-upload" />
</p>
<div class="ant-upload-text">
<div style="margin-bottom:5px">将文件/文件夹拖到此处,或</div>
<span
@click="fileUpload"
style="color:#8f2e61;margin-right:10px;cursor: pointer;"
>点击上传文件</span
>/<span
@click="folderUpload"
style="color:#8f2e61;margin-left:10px;cursor: pointer;"
>点击上传文件夹</span
>
</div>
</a-upload-dragger>
<div class="fileList" v-for="(item, index) in viewFileData" :key="index">
<div v-if="item.folderList && Boolean(item.folderList.length)">
<div class="fileItem">
<div @click="folderAction(item.name)" style="width:90%">
<a-icon
class="icon"
:type="item.folderOpen ? 'folder-open' : 'folder'"
/>
<div class="eclips" style="width:85%;cursor: pointer;">{{ item.name }}</div>
</div>
<a-icon
type="delete"
@click="deleteFile(item.name, 'folderList')"
style="cursor: pointer;"
/>
</div>
<div class="fileItemChild" v-if="item.folderOpen">
<div
class="fileItem"
v-for="(ite, indx) in item.folderList"
:key="indx"
>
<div>
<a-icon class="icon" type="file" />
<div class="eclips">{{ ite.name }}</div>
</div>
<a-icon
type="delete"
@click="deleteFile(`${item.name}*#/${ite.name}`)"
style="cursor: pointer;"
/>
</div>
</div>
</div>
<div class="fileItem" v-else>
<div style="width:100%">
<a-icon class="icon" type="file" />
<div class="eclips" >{{ item.name }}</div>
</div>
<a-icon
type="delete"
@click="deleteFile(item.name)"
style="cursor: pointer;"
/>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
defaultFileList: [],
currFileList: [],
folderList: [],
viewFileData: []
};
},
watch: {
currFileList: function(p) {
let names = [];
this.viewFileData = p.filter(
k => !names.includes(k.name) && names.push(k.name)
);
setTimeout(() => {
this.viewFileData.forEach((k, i) => {
if (k.folderList) {
k.folderList = k.folderList.filter(c => c.folder == k.name);
k.folderList.forEach(m => {
m.name = m.name.split(".")[0];
});
}
});
}, 0);
}
},
methods: {
//文件上传
customUpload(e) {
//处理
this.changeFileData(e);
},
folderListReset() {
this.folderList = [];
this.flag = true;
},
//切换上传文件
fileUpload() {
document.querySelector(".ant-upload input").webkitdirectory = false;
},
//切换上传文件夹
folderUpload() {
document.querySelector(".ant-upload input").webkitdirectory = true;
},
//文件删除
deleteFile(fileName, folderList) {
//删除文件夹里的文件
if (fileName.includes("*#/")) {
let folderName = fileName.split("*#/")[0];
let fulterFileName = fileName.split("*#/")[1];
let curFolderList = this.currFileList
.find(i => i.name == folderName && i.folderList)
.folderList.filter(k => k.name != fulterFileName);
this.currFileList.forEach(z => {
if (z.name == folderName) {
z.folderList = curFolderList;
}
});
return;
}
//删除文件或文件夹
//文件有后缀 文件夹无后缀【无需考虑文件文件夹重名问题】
this.currFileList = this.currFileList.filter(i => i.name != fileName);
},
//文件夹操作
folderAction(name) {
this.currFileList.forEach(item => {
if (item.name == name && item.folderList) {
item.folderOpen = !item.folderOpen;
}
});
},
changeFileData(fileItem) {
let _fileObj = null;
//先过滤文件-多级文件夹情况
if (fileItem.file.webkitRelativePath.split("/").length > 2) {
return;
}
//如果上传的是文件夹
if (fileItem.file.webkitRelativePath.split("/").length == 2) {
_fileObj = {
name: fileItem.file.webkitRelativePath.split("/")[0],
folderList: this.folderList,
folderOpen: false
};
//如果上传相同的文件夹 删除之前文件夹[首次]
if (this.flag) {
//筛选出不是文件夹和不是相同文件夹
this.currFileList = this.currFileList.filter(
z => !z.folderList || z.name != _fileObj.name
);
}
this.flag = false;
//获取当前文件夹下的所有文件【文件夹名加文件名防止误过滤】
let _obj = {
folder: _fileObj.name,
name: fileItem.file.webkitRelativePath.split("/")[1] + _fileObj.name,
file: fileItem.file
};
//过滤重复上传【文件夹】
if (this.filterFile(this.folderList, _obj)) return;
this.folderList.push(_obj);
}
//如果上传的是文件
if (fileItem.file.webkitRelativePath.split("/").length == 1) {
_fileObj = {
name: fileItem.file.name,
file: fileItem.file
};
}
//过滤重复文件【非文件夹】
if (this.filterFile(this.currFileList, _fileObj)) return;
this.currFileList.push(_fileObj);
},
//过滤重复文件方法封装-【递归-支持n级文件夹】
filterFile(currFileList, curObj) {
return (
currFileList &&
currFileList.some(i =>
curObj.folderList
? this.filterFile(i.folderList, curObj)
: i.name == curObj.name
)
);
}
}
};
</script>
</script>
axios 请求被拦截(307) 重定向到其他地址
第一次遇到 谷歌了下verify 设为false即可解决
项目用的是axios 加 httpsAgent: new https.Agent({ rejectUnauthorized: false })
})即可
参考地址 https://github.com/axios/axios/issues/535
ant design vue [select支持拼音筛选]
用pinyin-match 第三方库
//template
<a-select
:filterOption="filterOption"
/>
//js
filterOption(inputValue,option){
return PinyinMatch.match(option.componentOptions.children[0].text,inputValue)?true:false
},
select数据量过大导致页面卡顿
虚拟列表只展示可视区域的数据
用anted 钩子函数popupScroll 改变可视区域数据
<a-select
@popupScroll="handleScroll($event,item.selectOptionList)"
/>
data(){
return{
start: 0,
isScrolling: false,
scrollLoc: 0,
keyword: '',
itemObj:[],
label:'',
}
}
computed: {
filterItemObj() {
return this.itemObj.filter(item => item[this.label].includes(this.keyword));
},
showLimit() {
return this.filterItemObj.length > 30 ? 30 : this.filterItemObj.length;
},
end() {
return (this.start + this.showLimit) > this.filterItemObj.length ? this.filterItemObj.length : (this.start + this.showLimit);
},
selectOption() {
return this.filterItemObj.slice(this.start, this.end);
},
},
async handleScroll(e,data) {
if(data.length<400) return
if (this.isScrolling) return;
this.isScrolling = true;
let scrollTop = e.target.scrollTop;
let scrollHeight = e.target.scrollHeight;
let clientHeight = e.target.clientHeight;
let scrollBottom = scrollHeight - clientHeight - scrollTop;
if (scrollTop > this.scrollLoc) {
if (scrollBottom <= 4 * 32) {
let oldStart = this.start;
this.start += 10;
this.start = this.start > this.filterItemObj.length - this.showLimit ? this.filterItemObj.length - this.showLimit : this.start;
let gap = this.start - oldStart;
e.target.scrollTop -= 32 * gap;
}
} else if (scrollTop < this.scrollLoc) {
if (scrollTop <= 4 * 32) {
let oldStart = this.start;
this.start -= 10;
this.start = this.start > 0 ? this.start : 0;
let gap = oldStart - this.start;
if(!this.start && !gap){
e.target.scrollTop = 0
}
if (scrollTop === 0) e.target.scrollTop += 32 * gap / 2;
}
}
await this.$nextTick();
this.scrollLoc = e.target.scrollTop;
this.isScrolling = false;
}
ant-design-vue-pro [pro-layout]菜单默认展开问题
公司项目,框架版本比较老,客户提需求左侧路由菜单要默认展开,听到这个需求感觉很好改,回去改的时候发现各种问题
项目依赖 "@ant-design-vue/pro-layout": "^1.0.1-0", 找到相关文档 地址 根本没有相关api
看下框架底层 可以传递 openKeys 打开菜单,但是看代码逻辑,如果打开就不能关闭,不符合预期。而且1.0.1-0 是不支持openKeys的(也可能是这个版本有bug),升级大版本到最高版本就可以了(1.0.13),下面就要解决展开不能收起的问题
看底层可以通过派发 openChange 来监听事件 改变openKeys 好了 大功告成 完美解决
handleOpenChange: function handleOpenChange(openKeys) {
this.setOpenKeys(openKeys);
this.$emit('openChange', openKeys);
this.$emit('update:openKeys', openKeys);
},
<pro-layout
:title="siteTitle"
:menus="menus"
:collapsed="collapsed"
:media-query="query"
:handle-media-query="handleMediaQuery"
:handle-collapse="handleCollapse"
:i18n-render="i18nRender"
:openKeys.sync="openKeys"
@openChange="setOpenKeys"
v-bind="{
theme,
layout,
isMobile,
contentWidth,
fixedHeader,
fixSiderbar: fixedSidebar
}"
>
setOpenKeys(d){
this.openKeys = d
},
git fatal: Authentication failed
解决方案:
控制面板-> 用户账户 -> 凭据管理器 -> windows 凭据
删除git:xxx 凭据即可
Vue3 路由跳转添加动画
//template
<router-view class="router-view" v-slot="{ Component }">
<transition :name="transitionName">
<component :is="Component" />
</transition>
</router-view>
//js
const transition = ["slide-left", "slide-right"];
let transitionName = ref(transition[0]);
const route = useRoute();
// 监控路由的变化
watch(
() => route.meta.index,
(newIndex, oldIndex) => {
if (newIndex > oldIndex) {
transitionName.value = transition[0];
} else {
transitionName.value = transition[1];
}
}
);
//css
.router-view {
width: 100%;
height: 100%;
position: absolute;
top: 10vh;
bottom: 0;
margin: 0 auto;
overflow-y: hidden;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
}
.slide-right-enter-active,
.slide-right-leave-active,
.slide-left-enter-active,
.slide-left-leave-active {
will-change: transform;
transition: all 500ms;
position: absolute;
}
.slide-right-enter-from {
opacity: 0;
transform: translate3d(-100%, 0, 0);
}
.slide-right-leave-active {
opacity: 0;
transform: translate3d(100%, 0, 0);
}
.slide-left-enter-from {
opacity: 0;
transform: translate3d(100%, 0, 0);
}
.slide-left-leave-active {
opacity: 0;
transform: translate3d(-100%, 0, 0);
}
数据可视化大屏方案 scale缩放
按设计稿尺寸去做,然后缩放
目前能满足需求,弊端:缩放后地图点击位置会有偏移
let lazyFun;
function init(el, width, height) {
var _el = document.getElementById(el);
var hScale = window.innerHeight / height;
var wScale = window.innerWidth / width;
_el.style.transform = 'scale(' + wScale + ',' + hScale + ')'
}
init('app', 1920, 1080);
window.onresize = function() {
clearTimeout(lazyFun);
lazyFun = setTimeout(function() {
init('app', 1920, 1080)
}, 600);
};
echarts 自定义tootip 超出父元素被截断
tooltip.confine:true 即可
页面缩放导致页面文字虚化及鼠标移入文字抖动问题
文字加上transform:perspective(1px) 样式或 transform:translate3d(0,0,0) 即可
详细请看 https://developer.mozilla.org/zh-CN/docs/Web/CSS/transform-function/perspective