路由部分

1.hash 路由

创建 vue 项目时不需要作任何配置

2.route 路由(history 模式)

需要后端专门设置 ngnix,vue 中需要单独配置 mode:'history'

导入组件时

路径的写法可写为相对路径或者绝对路径

1)相对路径

import home from "../views/home.vue"

2)绝对路径(@相对于 src 的目录)

import home from "@/views/home.vue"

动态路由配置时可带上参数

path:"/home:id"

this.$router.push('/home',query:{info:123})

相对路由参数的变化做出响应的话,可以用 watch 监听路由
watch:{
  "$route":function(to,from,next){
    // 对路由变化做出响应
  }
}
或者使用导航守卫beforeRouteUpdate:
beforeRouteUpdate(to,from,next){
  // 对路由变化做出响应
}
路由跳转打开新页面
let link = this.$router.resolve({
  path: '/standardProUnit', //要跳转的路由
  query: {
    id: 1,
  },
});
window.open(link.href, '_blank'); //打开新窗口
路由元信息
routes: [
  {
    path: '/foo',
    component: Foo,
    meta: {
      isLogin: true,
      requireAuth: true,
    },
  },
];
// 可通过this.$route访问元信息

父子组件传值

1.父传子
// 父组件
<com-today :todayData="navList"></com-today>
data(){ return { navList:[1,2,3] } } // 子组件 props:['todayData'] // 可用this.todayData来调用,template里面{{ todayData }}直接使用
2.子传父
// 子组件
<p @click="sendMsg"> 传值</p>

sendMsg(){ this.$emit('receiveMsg',this.msg) },
// 父组件
<child @receiveMsg="receive"></child>

receive(data){ console.log(data); },
3.路由传值
区别

params:不在路由地址中显示,相当于 ajax 请求的 post 方式

query:传值会在路由地址中展示出来

// params方式(用name跳转页面)
// 不配置参数名时,页面刷新后参数会消失
1.router/index.js中的配置
{
  path:'/home/:id', //多个参数时 path:'/home/:id&:name',
  name:'home',
  component:()=>import('@/views/home.vue')
},
 2.页面中路由的传参
this.$router.push({
  name:"home",
  params:{id:this.id}  // 多个参数时 params:{id:this.id,name:this.name}
})


// query方式(用path跳转页面)
this.$router.push({
  path:'/home',
  query:{id:this.id}
})

Vue 中使用 rem 布局方式(2 种)

  • 在 public 下的 index.html 页面中添加如下 js

    fnResize();
    window.onresize = function () {
      fnResize();
    };
    function fnResize() {
      var deviceWidth = document.documentElement.clientWidth || window.innerWidth;
      if (deviceWidth >= 750) {
        deviceWidth = 750;
      }
      if (deviceWidth <= 320) {
        deviceWidth = 320;
      }
      document.documentElement.style.fontSize = deviceWidth / 7.5 + 'px';
    }
    • 然后在写 css 就可以将 px 单位换成 rem
    • 设置的比例是 100px=1rem
  • 或者安装插件引入

    • 首先安装 amfe-flexible 插件,在 main.js 里引入

      // 1.安装插件
      npm i amfe-flexible
      // 2.在main.js中引入
      import 'amfe-flexible'
    • 再安装 postcss-px2rem 插件

      npm i postcss-px2rem
    • 在 package.json 中配置

      "postcss": {
         "plugins": {
           "autoprefixer": {},
           "postcss-px2rem": {
             "remUnit": 37.5
           }
         }
       }
    • 在.vue 文件里。样式直接写 px 单位就可以了。在浏览器查看时会自动转换为 rem 单位。如果字体还想用 px。那就这样将 px 大写。就不会编译为 rem 单位了。样式就可以实现 px。

eslint 的错误提示

// error  'clock_start' is assigned a value but never used
// 在.eslintrc.js中的rules里加入下面这行
rules: {
  "no-unused-vars": 'off'
}

滚动条不显示

/**.class为类名**/
.class::-webkit-scrollbar {
  display: none; //去掉滚动条,不能滚动了;
  width: 0; //可以滚动,且滚动条不显示;
}

vue 中路由跳转怎么新开页面

let wrapUrl = this.$router.resolve({ path: '/wrap' });
window.open(wrapUrl.href, '_blank');

文件下载接口的处理

this.$axios({
  method: 'post',
  url: '/api/hlcloud-wms-app/inboundOrderStat/export', // 接口地址
  responseType: 'blob', // 接收格式设置
  data: dataObj, // 参数
}).then((res) => {
  // console.log(res)
  // 如果后台传的请求头中有文件名
  // console.log(encodeURI(res.headers['content-disposition']));
  let excelName = res.headers['content-disposition'].split('=')[1];
  // console.log(excelName)
  const content = res.data;
  const blob = new Blob([content]);
  const fileName = decodeURIComponent(excelName); //文件名解码(需要后端先进行编码,以防中文乱码)
  if ('download' in document.createElement('a')) {
    // 非IE下载
    const elink = document.createElement('a');
    elink.download = fileName;
    elink.style.display = 'none';
    elink.href = URL.createObjectURL(blob);
    document.body.appendChild(elink);
    elink.click();
    URL.revokeObjectURL(elink.href); // 释放URL 对象
    document.body.removeChild(elink);
  } else {
    // IE10+下载
    navigator.msSaveBlob(blob, fileName);
  }
});

elementUI 中的使用注意事项

  • 使用 required=”true”给 table 中的 label 加*号
<el-form-item :required="true" label="活动类型">
  <el-input v-model="input"></el-input>
</el-form-item>
  • 获取 date 组件的日期格式
<el-date-picker v-model="getdate" type="daterange" range-separator="" value-format="yyyy-MM-dd HH:mm:ss" start-placeholder="开始日期" end-placeholder="结束日期"> </el-date-picker>
  • table 中列文字过多悬浮显示并隐藏

    :show-overflow-tooltip="true"
  • table 的 selection 表头多选框变为文字标题

    /deep/ .el-table__header-wrapper .el-checkbox__input::after {
      content: '全选';
      position: absolute;
      margin-left: 5px;
    }
  • 解决 table 中翻页序号连续递增的问题

    <!--page为当前页码,rows为每页条数-->
    <el-table-column type="index" :index="(index)=>{return (index+1) + (page-1)*rows}"></el-table-column>
  • el-input 输入框自动聚焦的方法

    this.$nextTick(() => {
      this.$refs.focusInput.focus();
    });
  • tree-node 中拼接每一级的数据

    handleNodeClick(data,node) {
      this.placeFullName = [];
      let num = node.level;  // 获取当前点击的层级
      for(let i=0;i<num;i++){
        if(i>0){
          node = node.parent; // 赋值节点
          this.placeFullName.unshift(node.data.placeName);
        }else{
          this.placeFullName.unshift(data.placeName);
        }
      }
      this.detailForm.AssetItem.placeName = this.placeFullName.join('');
  • calendar 组件只生效当前月份点击事件

    /* 日历中禁止非当前月点击事件 */
    ::v-deep .el-calendar-table:not(.is-range) td.next,
    ::v-deep .el-calendar-table:not(.is-range) td.prev {
      pointer-events: none;
    }
  • table 加了固定头,内容可滚动,当滚到 table 底边时,点击分页后还在底边

    // 先给table设置了ref="gridTable"
    // 点击分页的时候加上这句即可
    this.$refs.gridTable.bodyWrapper.scrollTop = 0;

-

// 定义设置cookie方法
Vue.prototype.setCookie = function (name, value, day) {
  if (day !== 0) {
    //当设置的时间等于0时,不设置expires属性,cookie在浏览器关闭后删除
    var curDate = new Date();
    var curTamp = curDate.getTime();
    var curWeeHours = new Date(curDate.toLocaleDateString()).getTime() - 1;
    var passedTamp = curTamp - curWeeHours;
    var leftTamp = 24 * 60 * 60 * 1000 - passedTamp;
    var leftTime = new Date();
    leftTime.setTime(leftTamp + curTamp);
    document.cookie = name + '=' + escape(value) + ';expires=' + leftTime.toGMTString();
  } else {
    document.cookie = name + '=' + escape(value);
  }
};
// 设置cookie
this.setCookie('AuthenticationToken', 'a9cd00f0b00b4371822c8f6069aba8d0', 0);

开发环境和生产环境的配置

  • /src 目录下新建.env.development文件

    NODE_ENV = 'development';
    VUE_APP_FLAG = 'dev';
  • /src 目录下新建.env.production文件

    NODE_ENV = 'production';
    VUE_APP_FLAG = 'pro';
  • main.js 中

    let url = window.location.href.split('/');
    let baseUrl = url[0] + '//' + url[2];
    // 判断环境
    if (process.env.NODE_ENV === 'production') {
      if (process.env.VUE_APP_FLAG === 'pro') {
        axios.defaults.baseURL = baseUrl;
      } else {
        axios.defaults.baseURL = baseUrl;
      }
    } else {
      axios.defaults.baseURL = '/api';
    }
  • router 文件中 index.js 的配置

    const router = new VueRouter({
      mode: 'history',
      base: '/wms/', //process.env.BASE_URL, 将默认的改为服务器下的文件路径
      routes,
    });

Vue 中 cli2 搭建的项目打包时图片/图标不显示

//只需要在build/utils.js文件中找到vue-style-loader配置,为它新增(publicPath: '../../')即可。
if (options.extract) {
  return ExtractTextPlugin.extract({
    use: loaders,
    fallback: 'vue-style-loader',
    publicPath: '../../', // 新增项
  });
} else {
  return ['vue-style-loader'].concat(loaders);
}

Vue 中解除双向绑定的方法

// 1.对对象进行深拷贝
// 嵌套for循环赋值的问题也能用此方法解决
let obj = JSON.parse(JSON.stringify(data));
// 2.Vue中自有的方法
Object.freeze(obj);

获取地址中的参数

// 获取地址栏中的参数  name为参数的名字 函数会返回参数名所对应的参数值
getQueryString(name){
  var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
  var r = window.location.search.substr(1).match(reg);
  if(r!=null)return  unescape(r[2]); return null;
}

封装 elementUI 的 treeSelect 下拉框

<!--
  author:KinXpeng
  date:2020-5-20 09:24:00
  **** 使用案例
  <tree-select
    :options="treeData"
    :defaultProps="defaultProps"
    :clearable="true"
    :accordion="true"
    @selectNode="selectNode"
  ></tree-select>
  *** 1.options 为绑定的树状数据
  *** 2.defaultProps 为配置项,label和children
  *** 3.clearable 为是否可清除选择
  *** 4.accordion 为是否开启手风琴模式,只会展开一项
  *** 5.selectNode 为选中节点的回调函数,有两个参数(data,node),data为节点的数据
  ***   有清除按钮时,点击清除会触发selectNode函数,data为空,可通过判断data来设置选中的value为空
-->
<template>
  <div class="treeSelect">
    <el-select v-model="selectValue" :clearable="clearable" @clear="clearHandle" :popper-class="popperClass" placeholder="请选择" ref="tree_select">
      <el-option :value="selectValue" :label="selectValue" style="height:auto">
        <el-tree class="selectTree" :data="options" :show-checkbox="false" ref="selectTree" highlight-current :props="defaultProps" :accordion="accordion" @node-click="handleNodeClick"> </el-tree>
      </el-option>
    </el-select>
  </div>
</template>

<script>
export default {
  name: 'treeSelect',
  props: {
    // 树结构数据
    options: {
      type: Array,
      default() {
        return [];
      },
    },
    // 配置项
    defaultProps: {
      type: Object,
      default() {
        return {};
      },
    },
    // 清除
    clearable: {
      type: Boolean,
      default() {
        return false;
      },
    },
    // 是否打开手风琴模式
    accordion: {
      type: Boolean,
      default() {
        return false;
      },
    },
  },
  data() {
    return {
      selectValue: '',
      popperClass: 'tree_select',
      isShowSelect: false,
    };
  },
  methods: {
    // 选中节点
    handleNodeClick(data, node) {
      this.selectValue = data[this.defaultProps.label];
      this.$emit('selectNode', data, node);
      this.$refs.tree_select.blur(); // 选中后隐藏下拉框
    },
    // 清除选择回调函数
    clearHandle() {
      this.$emit('selectNode');
    },
  },
  created() {},
};
</script>

<style lang="less">
.treeSelect {
  display: inline-block;
}
.tree_select {
  .el-select-dropdown__item {
    padding: 0;
    .el-tree-node__label {
      font-weight: normal;
    }
    .el-tree-node {
      padding: 5px 0;
    }
  }
  .el-select-dropdown__item.hover {
    background: #fff;
    padding: 0;
    &:hover {
      background: #fff;
    }
  }
}
.hide {
  display: none !important;
}
</style>

封装公用请求接口返回为 Promise 对的解决方案

#### bus.js文件
//  在公用的js中封装好统一的函数
// 数据字典通用接口
function dictionaryList(dictCode) {
  return new Promise((resolve,reject)=>{ // 返回Promise对象
    Vue.prototype.$http
      .post(
        "/hlcloud-ubp-app/dictDataQuery/listByExtend?dictCode="+dictCode+"&objectDataOnly=false&softProdCode=EAM"
      )
      .then(res => {
        if (res.data.code == "0") {
          return resolve(res.data.data); // 将请求的数据resolve出去
        }
      })
      .catch(err => {
        return reject(err)
      });
  })
};
// 异步处理返回的数据
async function getDictionaryList(dictCode){
  let listData = await dictionaryList(dictCode);
  return listData;
}
#### 调用的页面
// 使用.then() 将需要的数据从Promise对象拿到
// 资源类型
this.bus.getDictionaryList('ResourceType').then(data=>{
  this.resourcetypeList = data;
});

获取地址栏参数

// 获取地址栏中的参数(传入参数名称即可)
function getQueryString(name) {
  var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');
  var r = window.location.search.substr(1).match(reg);
  if (r != null) return unescape(r[2]);
  return null;
}

数组去重返回新数组

// a,b 为两个数组  其中a.length > b.length
arrSubtraction2(a, b) {
  if (a && b) {
    return a.filter(i => !b.includes(i))
  }
  throw new Error('error')
}

Avoided redundant navigation to current location: “/writeArticle”.

// this.$router.push() 时报错
// 在router中的index.js中
const originalPush = VueRouter.prototype.push;

VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch((err) => err);
};

vue 中使用 scss 设置夜间模式

  • 安装 scss 依赖

    npm install node-sass --save-dev    //安装node-sass
    npm install sass-loader --save-dev  //安装sass-loader
    npm install style-loader --save-dev //安装style-loader
  • 在 src/assets 新建目录 scss,以及新建 dark.scss 主题文件

    // scss样式
    $themes: (
      light: (
        background_color: #cccccc,
        //背景色
        text-color: rgba(0, 0, 0, 0.65),
        // 主文本色
      ),
      dark: (
        background_color: #181c27,
        //背景
        text-color: rgba(255, 255, 255, 0.65),
        // 主文本色
      ),
    );
    
    // 处理样式,遍历主题map
    @mixin themeify {
      @each $theme-name, $theme-map in $themes {
        //!global 把局部变量提升为全局变量
        $theme-map: $theme-map !global;
        //判断html的data-theme的属性值  #{}是sass的插值表达式
        //& sass嵌套里的父容器标识   @content是混合器插槽,像vue的slot
        [data-theme='#{$theme-name}'] & {
          @content;
        }
      }
    }
    //声明一个根据Key获取颜色的function
    @function themed($key) {
      @return map-get($theme-map, $key);
    }
    
    // 获取颜色
    @mixin background_color($color) {
      @include themeify {
        background: themed($color) !important;
      }
    }
    //获取字体颜色
    @mixin font_color($color) {
      @include themeify {
        color: themed($color) !important;
      }
    }
  • 在 main.js 中引入 dark.scss

    import './assets/scss/dark.scss';
  • 在 dark.vue 中使用

    <template>
      <div id="darkMode">
        <div>
          <a class="btn" @click="modelChange">模式切换</a>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: 'darkMode',
      data() {
        return {
          dark: false,
        };
      },
      methods: {
        // 使用方法
        modelChange() {
          this.dark = !this.dark;
          if (this.dark) {
            window.document.documentElement.setAttribute('data-theme', 'dark');
          } else {
            window.document.documentElement.setAttribute('data-theme', 'light');
          }
        },
      },
      mounted() {
        window.document.documentElement.setAttribute('data-theme', 'light');
      },
    };
    </script>
    
    <style scoped lang="scss">
    @import '@/assets/scss/common/common';
    #darkMode {
      //在此使用了背景颜色变量
      @include background_color('background_color');
      //再次使用了文字颜色变量
      @include font_color('text-color');
      width: 100vw;
      height: 100vh;
      .btn {
        width: 100px;
        height: 40px;
        margin: 0 auto;
      }
    }
    </style>

设置字体小于 12px

span{
  font-size: 12px;
  transform: scale(.8); // 将字体缩小80%,即9.6px;如需更小,修改scale中的数值即可。
  transform-origin: 0 0; // 设置后发现文字在原来的基础上偏移了,解决基点偏移问题。
}

Nuxt.js

  • Nuxt.js 的使用

  • 安装注意事项

    • 使用npx create-nuxt-app myapp安装

    • 配置项信息安装官网默认选择

    • 安装 scss 报错处理

      1.安装scss依赖
      npm install --save-dev node-sass sass-loader
      2.在 nuxt.congig.js 文件里进行如下配置
      modules: [
      	'@nuxtjs/style-resources', // 导入模块
      ],
      3.通常这样安装后会报错,提示node_modules错误等等,换个低版本的scss安装即可
      4.npm install --save-dev sass sass-loader@10 fibers  // 可以指定版本号
  • 安装配置问题

    • 跨域配置(在 nuxt.config.js 中配置)

      axios: {
        proxy: true, // 配置代理
        prefix:'/api', // 给请求的url前面加上'/api'
      },
      proxy: {
        '/api': {
          target: 'http://127.0.0.1:3000/',
          changeOrigin: true, // 是否跨域
          pathRewrite: {
            '^/api': '/'
          }
        }
      },
    • 跨域之后在 vue 文件中如何使用 axios 请求接口

      export default {
        asyncData(context) {
          // 使用context对象
          return context.$axios.post('/menuList').then((res) => {
            console.log(res.data.data);
          });
        },
      };
    • 修改项目启动端口,在 package.json 中配置

      "config": {
          "nuxt": {
            "host": "0.0.0.0",
            "port": "3333"  // 所修改的端口号
          }
        }
    • 1

  • 等等

  • Nuxt.js 中遇到的问题

vue 中切换路由的时候滚动条到达顶部
scrollBehavior (to, from, savedPosition) {
	return { x: 0, y: 0 }
},
数组等量切分
// 第一个参数为需要切分的数组,第二个参数为切分的大小
function splitData(list, size) {
  let res = [];
  for (let i = 0, len = list.length; i < len; i += size) {
    res.push(list.slice(i, i + size));
  }
  return res;
}
css 中的粘性布局
/*此属性会自动监听scroll属性,仅限在外层盒子的滚动中生效,外层有overflow:hidden时不生效*/
position: sticky;
position: -webkit-sticky;
top: 0;
监听页面滚动条滚动的方向
// data
data(){
  return {
    scrollAction:{
      x: 'undefined',
      y: 'undefined'
    },
    scrollDirection:null,
  }
}

// methods
scrollFunc() {
  if (typeof this.scrollAction.x == 'undefined') {
    this.scrollAction.x = window.pageXOffset;
    this.scrollAction.y = window.pageYOffset;
  }
  var diffX = this.scrollAction.x - window.pageXOffset;
  var diffY = this.scrollAction.y - window.pageYOffset;
  if (diffX < 0) {
    // Scroll right
    this.scrollDirection = 'right';
  } else if (diffX > 0) {
    // Scroll left
    this.scrollDirection = 'left';
  } else if (diffY < 0) {
    // Scroll down
    this.scrollDirection = 'down';
  } else if (diffY > 0) {
    // Scroll up
    this.scrollDirection = 'up';
  } else {
    // First scroll event
  }
  this.scrollAction.x = window.pageXOffset;
  this.scrollAction.y = window.pageYOffset;
},

// mounted
mounted(){
  window.addEventListener("scroll",this.handleScroll);
},
// destroyed
destroyed() {
  document.removeEventListener('scroll', this.handleScroll);
}
js 获取当前月份的起止日期
// 格式化日期
getDateStr(date) {
  let year = "";
  let month = "";
  let day = "";
  let now = date;
  year = ""+now.getFullYear();
  if((now.getMonth()+1)<10){
    month = "0"+(now.getMonth()+1);
  }else{
    month = ""+(now.getMonth()+1);
  }
  if((now.getDate())<10){
    day = "0"+(now.getDate());
  }else{
    day = ""+(now.getDate());
  }
  return year+"-"+month+"-"+day;
},
  // 获取当前月份起止日期
  // 参数0,当前月份,-1为上一个月,1为下个月
  getMonthStartAndEnd(AddMonthCount) {
    //起止日期数组
    let startStop = new Array();
    //获取当前时间
    let currentDate = new Date();
    let month=currentDate.getMonth()+AddMonthCount;
    if(month<0){
      let n = parseInt((-month)/12);
      month += n*12;
      currentDate.setFullYear(currentDate.getFullYear()-n);
    }
    currentDate = new Date(currentDate.setMonth(month));
    //获得当前月份0-11
    let currentMonth = currentDate.getMonth();
    //获得当前年份4位年
    let currentYear = currentDate.getFullYear();
    //获得上一个月的第一天
    let currentMonthFirstDay = new Date(currentYear, currentMonth,1);
    //获得上一月的最后一天
    let currentMonthLastDay = new Date(currentYear, currentMonth+1, 0);
    //添加至数组
    startStop.push(this.getDateStr(currentMonthFirstDay));
    startStop.push(this.getDateStr(currentMonthLastDay));
    //返回
    return startStop;
  },

vue.config.js 配置

  • 减少打包体积,去除.map 文件

    module.exports = {
      productionSourceMap: false, // 去掉打包后的map文件
    };

Element UI 中—Message 消息提示每次只弹出 1 个,不能同时出现 2 个

  • 新建 /utils/message.js

    //message.js
    /**重置message,防止重复点击重复弹出message消息提示 */
    import { Message } from 'element-ui';
    
    let messageInstance = null;
    
    const resetMessage = (options) => {
      if (messageInstance) {
        messageInstance.close();
      }
      Message.closeAll(); //手动关闭所有消息提示实例
      messageInstance = Message(options);
      // console.log(Message(options));
    };
    
    ['error', 'success', 'info', 'warning'].forEach((type) => {
      resetMessage[type] = (options) => {
        if (typeof options === 'string') {
          options = {
            message: options,
          };
        }
        options.type = type;
        return resetMessage(options);
      };
    });
    
    export const message = resetMessage;
  • main.js 中引入,挂载到全局中

    import { message } from '@/utils/message.js';
    
    Vue.use(ElementUI);
    Vue.prototype.$message = message; // 挂载时在use后面,以便覆盖原有的提示
  • 使用方法和之前一样

    this.$message({
      type: 'error',
      message: '提示信息',
    });