记录一些开发中遇到的问题

515 阅读5分钟

安装node js,输入命令,提示:'node' 不是内部或外部命令,也不是可运行的程序 或批处理文件的解决方法

大概率问题是环境变量未配置的问题

  • 控制面板>系统>高级系统设置>高级>环境变量
  • 新建用户环境变量NODE_PATH(变量名),值就是nodejs安装路径(C:\Program Files\nodejs)默认安装路径
  • 编辑用户变量Pathnodejs路径直接添加在后面即可

Vue报错Uncaught TypeError: Cannot read property 'match' of undefined

vue页面后退控制台报错
是因为a标签没有设置href属性,加上href属性就好了

vue-awesome-swiper 使用loop:true后点击事件不生效的问题

<template>
  <swiper class="swiper" :options="swiperOption">
    <swiper-slide @click="next">Slide 1</swiper-slide>
    <swiper-slide>Slide 2</swiper-slide>
    <swiper-slide>Slide 3</swiper-slide>
    <swiper-slide>Slide 4</swiper-slide>
    <swiper-slide>Slide 5</swiper-slide>
    <div class="swiper-pagination" slot="pagination"></div>
    <div class="swiper-button-prev" slot="button-prev"></div>
    <div class="swiper-button-next" slot="button-next"></div>
  </swiper>
</template>

<script>
  import { swiper, swiperSlide } from "vue-awesome-swiper";
  import "swiper/swiper-bundle.css";

  export default {
    name: 'swiper-example-loop',
    components: {
      swiper,
      swiperSlide
    },
    data() {
      return {
        swiperOption: {
          slidesPerView: 1,
          spaceBetween: 30,
          loop: true,
          pagination: {
            el: '.swiper-pagination',
            clickable: true
          },
          navigation: {
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev'
          }
        }
      }
    },
    methods:{
      next(){
        console.log(11)
      }
    }
  }
</script>

会发现loop出来的点击事件不生效

//解决
data() {
      let vm=this;
      return {
        swiperOption: {
          slidesPerView: 1,
          spaceBetween: 30,
          loop: true,
          pagination: {
            el: '.swiper-pagination',
            clickable: true
          },
          navigation: {
            nextEl: '.swiper-button-next',
            prevEl: '.swiper-button-prev'
          },
          on:function(e){
            vm.next(e)//获取所需要的参数
          }
        }
      }
    }

js动态往body添加dom

let elem = document.createElement("div");
elem.className='loading' ;
// 或者 elem.id='loading';
elem.innerHTML = `<div class="load-3">
        <div class="indexLine"></div>
        <div class="indexLine"></div>
        <div class="indexLine"></div>
      </div>`;
  document.body.appendChild(elem);

监听页面关闭或刷新执行

原理

window.onbeforeunload = function (e) {
      e = e || window.event;

      // 兼容IE8和Firefox 4之前的版本
      if (e) {
        //     e.returnValue = '关闭提示';
      }

      // Chrome, Safari, Firefox 4+, Opera 12+ , IE 9+
      //   return '关闭提示';
      localStorage.removeItem("ax");
    };

vue中使用

  created(){
    window.addEventListener('beforeunload', this.leaveConfirm)
  },
  methods: {
    leaveConfirm (e) {
      e = e || window.event
      if (e) {
        // e.returnValue = '关闭提示'
      }
      // return '关闭提示'
      // 执行一些操作
      localStorage.removeItem('xx')
    },
  },
  destroyed () {
    window.removeEventListener('beforeunload', this.leaveConfirm);
  },

Vue无痕刷新

//App.vue
<template>
  <div id="app">
    <router-view v-if="isRouterAlive" />
  </div>
</template>

<script>
export default {
  name: "App",
  provide() {
    return {
      reload: this.reload
    };
  },
  data() {
    return {
      isRouterAlive: true
    };
  },
  methods: {
    reload() {
      this.isRouterAlive = false;
      this.$nextTick(function() {
        this.isRouterAlive = true;
      });
    }
  }
};
</script>

<style>
</style>

//要用到刷新的组件 调用this.reload()
<script>
export default {
  inject: ['reload'],
  data() {
    return {};
  },
  methods: {
      refresh(){
          this.reload();
      }
      },
  };
</script>

还有两种方法(体验一般)

  1. 这两种都可以刷新当前页面的,缺点就是相当于按ctrl+F5 强制刷新那种,整个页面重新加载,会出现一个瞬间的空白页面,体验不好
location.reload()
this.$router.go(0)
  1. 先跳转一个空白页,再跳转回来,这种方法就是地址栏url会有瞬间变化
<template>
  <div></div>
</template>

<script>
export default {
  mounted() {
    let redirect = this.$route.query.redirect;
    this.$router.replace(redirect);
  },
};
</script>

<style lang="less" scoped>
</style>

ElementUI DatePicker选择月当前月份高亮样式修改

<template>
  <div class="home">
    <el-date-picker
      v-model="value2"
      type="month"
      popper-class="month"
      placeholder="选择月"
    >
    </el-date-picker>
  </div>
</template>

<script>

export default {
  name: "Home",
  data() {
    return {
      value2: new Date(2021, 8, 1),//设置默认选择2021九月
    };
  },
};
</script>
<style lang="less">
.month {
  .el-month-table {
    td.today {
      .cell {
        font-weight: normal !important;
        color: unset !important;
      }
    }
  }
}
</style>

Css实现滚动时隐藏滚动条

html {
    -ms-overflow-style:none; 
    overflow:-moz-scrollbars-none; 
}
html::-webkit-scrollbar{
    width:0px
}

Vue install 使用

const plugin = {
    install(Vue, opts = {}) {
        // 组件
        // 指令
        // 混入
        // 挂载vue原型
        //传入的参数
        console.log(opts);
        Vue.directive('copy', {
            bind(el, { value }) {
                el.$value = value
                el.handler = () => {
                    if (!el.$value) {
                        // 值为空的时候,给出提示。可根据项目UI仔细设计
                        console.log('无复制内容')
                        return
                    }
                    // 动态创建 textarea 标签
                    const textarea = document.createElement('textarea')
                    // 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
                    textarea.readOnly = 'readonly'
                    textarea.style.position = 'absolute'
                    textarea.style.left = '-9999px'
                    // 将要 copy 的值赋给 textarea 标签的 value 属性
                    textarea.value = `${el.$value} - ${opts.name}`
                    // 将 textarea 插入到 body 中
                    document.body.appendChild(textarea)
                    // 选中值并复制
                    textarea.select()
                    const result = document.execCommand('Copy')
                    if (result) {
                        console.log('复制成功') // 可根据项目UI仔细设计
                    }
                    document.body.removeChild(textarea)
                }
                // 绑定点击事件,就是所谓的一键 copy 啦
                el.addEventListener('click', el.handler)
            },
            // 当传进来的值更新的时候触发
            componentUpdated(el, { value }) {
                el.$value = value
            },
            // 指令与元素解绑的时候,移除事件绑定
            unbind(el) {
                el.removeEventListener('click', el.handler)
            }
        });
    }
}


export default plugin

在main.js中引入

import copy from './assets/js/plugin' //plugin.js根据项目中的位置自己定
Vue.use(copy,{name:'shadow'}) //使用与传参

jsonp判断url是否能打开

$.ajax({
       url:'http://ybj.zj.gov.cn/art/2019/8/7/art_1229225623_1181747.html',
        cache: false,
        dataType: "jsonp", //跨域采用jsonp方式 
        processData: false,
        timeout:10000, //超时时间,毫秒
        complete: function (data) {
          if(data.status==200){
            //可访问
          }else{
            //不可访问
          }
        }
     })

获取Url参数(兼容哈希)

let url='https://xxx.cn/phone/dist/index.html?dataOpenId=oppyn1BSQzOA4C1706jNWLUoaSnU&dataUserId=406f3640614511ebad49bd6e5d36a63a#/home'

getQueryString = (name, search) => {
  search = search ||  window.location.search.substr(1) || window.location.hash.split("?")[1];
  let reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
  let r = search.match(reg);
  if (r != null) return  unescape(r[2]); return null;
}

//getQueryString('dataOpenId')  //'oppyn1BSQzOA4C1706jNWLUoaSnU'
//getQueryString('dataUserId')  //'406f3640614511ebad49bd6e5d36a63a'
//getQueryString('dataUserId',url) //'406f3640614511ebad49bd6e5d36a63a#/home'

Elementel-cascader级联选择器动态加载及回显

误区:默认不需要自己去加载第一层数据,会自动请求第一层数据
回显只需要传入address数组即选中的id回显会自动去请求回显
addressList设置空数组即可

 <el-cascader
      ref="cascaderAddr"
      :props="addressProps"
      v-model="address"
      :options="addressList"
      @change="addressChange"
    ></el-cascader>
    <script>

export default {
  name: "Home",
  data() {
    return {
      addressList:[],
      address:[],
      addressProps:{
        label:'title',
        value:'key',
        lazy: true,
        lazyLoad(node, resolve){
          const { level } = node;
          const nodes = []; 
          homeRecommendApi.getTree({pId:node.value}).then(res=>{
            res.data.map(item=>{
              let obj = {
                  key: item.key,
                  title: item.title,
                  leaf: level >= 3, 
                };
                nodes.push(obj);
            })
            resolve(nodes);
          })
        }
      },
    };
  },
};
</script>

微信小程序使用Echarts控制台报错非法颜色none

ec-canvas文件夹下wx-canvas.js文件的_initStyle()方法中加入代码

使用force-use-old-canvas="true"属性引起(该属性解决图表不随屏幕滚动问题)

<ec-canvas id="progressChart" canvas-id="mychart-bar" ec="{{ ecProgress }}"  force-use-old-canvas="true">
</ec-canvas>
var styles = [
      "fillStyle",
      "strokeStyle",
      "globalAlpha",
      "textAlign",
      "textBaseAlign",
      "shadow",
      "lineWidth",
      "lineCap",
      "lineJoin",
      "lineDash",
      "miterLimit",
      "fontSize",
    ];
    styles.forEach((style) => {
      Object.defineProperty(ctx, style, {
        set: (value) => {
          if (
            (style !== "fillStyle" && style !== "strokeStyle") ||
            (value !== "none" && value !== null)
          ) {
            ctx["set" + style.charAt(0).toUpperCase() + style.slice(1)](value);
          }
        },
      });
    });

将字符串转换成数组("[上海, 天津, 河南]")

"[上海, 天津, 河南]".replace (/\[|]/g, '').split(',')

浙里办数据脱敏

const desensitization = (str, beginLen, endLen = -1) => {
    let len = str.length;
    if (beginLen == 0) {
        endLen = endLen * len + 1;
    }
    let firstStr = str.substr(0, beginLen);
    let lastStr = str.substr(endLen);
    let middleStr = str.substring(beginLen, len - Math.abs(endLen)).replace(/[\s\S]/ig, '*');
    return `${firstStr}${middleStr}${lastStr}`;
}
//姓名脱敏
desensitization(userName,0)
//手机号脱敏
desensitization(phone,3,-4)
//身份证号脱敏
desensitization(idCard,1,-1)

字符串首字脱敏展示

const maskChineseString=(s)=>s.replace(/^(.)/, "*")

maskChineseString('abc') //*bc
maskChineseString('张三丰') //*三丰

Element的el-time-picker选择了时间点确定后鼠标快速移入选择框选择框再次显示问题

  1. 通过修改css的方法(缺点:会移除显示隐藏的动画)
.el-time-panel {
  transition: none !important;
}
  1. 失去焦点事件内手动执行关闭显示框
setTimeout(() => {
  this.$refs.timePicker.pickerVisible = false;
}, 50);
  1. 最优解
  data () {
    return {
      externalVisible:false
    }
  },
  methods:{
    handleOnFocus (ref) {
      if(ref.pickerVisible && this.externalVisible) {
          ref.pickerVisible = false
          ref.blur()
      }
    },
    handleOnBlur (ref) {
      ref.picker.$on("dodestroy", () => {
          this.externalVisible = false
        })
        this.externalVisible = true
    },
  }

根据给定的对象替换数组中的key对应的值

let chartProperties={id:'name',name:'id'};
let arr=[
    {
      "id": "nav_1",
      "name": "首页"
    },
    {
      "id": "nav_2",
      "name": "新闻"
    },
    {
      "id": "nav_3",
      "name": "产品"
    }
  ];

  let newArr=arr.map(obj=>{
    for (const key in chartProperties) {
      const value=chartProperties[key]
      const temp=obj[key]
      obj[key]=obj[value]
      obj[value]=temp
      return obj
    }
  })
console.log(newArr);
// result
[
    {
        "id": "首页",
        "name": "nav_1"
    },
    {
        "id": "新闻",
        "name": "nav_2"
    },
    {
        "id": "产品",
        "name": "nav_3"
    }
]

方法二

const chartProperties={
    name: "id",
    id: "name"
}

const data=[
    {
      "id": "nav_1",
      "name": "首页"
    },
    {
      "id": "nav_2",
      "name": "新闻"
    },
    {
      "id": "nav_3",
      "name": "产品"
    }
  ];;
const {name, id} = chartProperties;

const newData = data.map((item) => ({
  name: item[name],
  id: item[id] 
}));

对象key属性未知数据转换

const chartProperties = {
    name: "id",
    id: "name",
    a: 'name'
}

const data = [
    {
        "id": "nav_1",
        "name": "首页"
    },
    {
        "id": "nav_2",
        "name": "新闻"
    },
    {
        "id": "nav_3",
        "name": "产品"
    }
];;
let keys = Object.keys(chartProperties)
let values = Object.values(chartProperties)
let arr = []
for (let i of data) {
    let obj = {}
    for (let j of keys) {
        obj[j] = i[chartProperties[j]]
    }
    arr.push(obj)
}
//result
[
    { name: 'nav_1', id: '首页', a: '首页' },
    { name: 'nav_2', id: '新闻', a: '新闻' },
    { name: 'nav_3', id: '产品', a: '产品' }
]

方法二

const result = data.map(item => {
    const newItem = {};

    Object.keys(chartProperties).forEach(key => {
        newItem[key] = item[chartProperties[key]];
    });

    return newItem;
});
console.log(result);

方法三

const result = data.reduce((arr, item) => {
    const newItem = {};

    Object.keys(chartProperties).forEach(key => {
        newItem[key] = item[chartProperties[key]];
    });

    arr.push(newItem);

    return arr;
}, []);

把vue组件添加到body下

使用场景:祖先元素使用了transform属性影响到了子元素的fixed定位(吸顶效果)

// mounted中添加代码
this.$nextTick(() => {
 const body = document.querySelector("body");
 if (body.append) {
   body.append(this.$el);
 } else {
   body.appendChild(this.$el);
 }
});

遍历多层嵌套数据获取key值等于指定值的对象

/**
 * 
 * @param data 传入的多层嵌套结构数据
 * @param modelValue 指定的key的value值
 * @param deepSearch(data, 'model', modelValue); 'model' 是指定的key 可自行修改
 * @returns 
 */
const findItemByModel = (data, modelValue) => {
    const deepSearch = (object, key, value) => {
        if (object.hasOwnProperty(key) && object[key] === value) return object;

        for (let i = 0; i < Object.keys(object).length; i++) {
            let child = object[Object.keys(object)[i]];
            if (typeof child === "object" && child != null) {
                let result = deepSearch(child, key, value);
                if (result != null) return result;
            }
        }
        return null;
    };

    return deepSearch(data, 'model', modelValue);
};

Vue slot使用v-model双向绑定

<template>
  <div>
    <slot v-bind:value="value" v-on:input="value = $event.target.value"></slot>
  </div>
</template>
<!-- Parent.vue -->
<template>
  <div>
    <Child v-model="value">
      <template #default="{ value, updateValue }">
        <input :value="value" @input="updateValue($event.target.value)">
      </template>
    </Child>
  </div>
</template>

<script>
import Child from './Child.vue'

export default {
  components: {
    Child
  },
  data() {
    return {
      value: ''
    }
  }
}
</script>
<!-- Child.vue -->
<template>
  <div>
    <slot :value="value" @update:value="updateValue">
      <!-- 插入内容 -->
    </slot>
  </div>
</template>

<script>
export default {
  props: {
    modelValue: String
  },
  computed: {
    value: {
      get() {
        return this.modelValue
      },
      set(newValue) {
        this.$emit('update:modelValue', newValue)
      }
    }
  },
  methods: {
    updateValue(newValue) {
      this.value = newValue
    }
  }
}
</script>

Jenkins构建Vue build一直报错问题

node -v
//全局安装yarn并设置镜像
npm install -g yarn -registry=https://registry.npmmirror.com
yarn -v
//在安装包时忽略engines字段的检查。engines字段通常用于指定项目运行所需的Node.js和npm版本,  
//如果不忽略这个检查,Yarn可能会因为当前环境不符合这些要求而拒绝安装包
yarn config set ignore-engines true
//安装项目依赖,但是不会生成或更新yarn.lock文件
yarn install --pure-lockfile 
yarn run build