∞拾年

撸码日常
Echarts y轴name显示问题帮同事解决一个很简单的一个问题 用到的 api地址直达 api地址直达直接随便...
扫描右侧二维码阅读全文
21
2021/06

撸码日常

Echarts y轴name显示问题

帮同事解决一个很简单的一个问题 用到的 api地址直达 api地址直达

直接随便找个案例 在这里写
截屏2021-06-21 下午8.52.33.png

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)
屏幕截图 2022-03-01 092340.png

<a-table
:rowKey="
          (record, index) => {
            return record.id;
          }
        "
/>

文件夹/文件上传二次封装

1.同一组件实现文件夹及文件上传
2.上传的文件树重写【业务需求只上传一级文件,代码递归可扩展多级】
上代码 不做过多阐述,代码有注释
有一点是文件上传取不到集合只能watch 监听任务队列改变数据格式【感觉不是和合理,有没有更加合理的解决方式】
屏幕截图 2022-07-22 163608.png

<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;
    }
Last modification:September 21st, 2022 at 04:55 pm
If you think my article is useful to you, please feel free to appreciate

Leave a Comment