zoukankan      html  css  js  c++  java
  • Vue实战狗尾草博客管理系统第一章

     源码地址:https://github.com/18291907191/gwc_manage

    Vue实战狗尾草博客后台管理系统第一章

    这里准备采用的技术栈为:vue全家桶+element-ui

    这里因为是后台管理系统,没有做SSR的必要。所以这里就采用前后端分离来昨晚这个项目~

    项目搭建

    vue init webpack gwc_manage
     初始化过程中,会让我们进行一些依赖包,项目配置的基本选择。项目名称,作者信息,描述,是否安装路由,eslint,测试单元,npm/yarn。

    这里不选择安装eslint和测试单元。用过的人应该知道非常酸爽。

    项目初始化成功后,

    npm run dev

    可以看到项目成功运行在8080端口。我么就可以打开8080端口查看我们搭建好的项目

    上图所述,就是vue初始化后的页面。

    安装less并配置全局的变量

    npm install less less-loader -s

    注:后面的-s表示本地安装-g表示全局安装,不些则表示默认本地安装

    因为使用了less后,个人一般喜欢将整个项目的配色进行一个管理。所以需要进行全局的配置。

    这里安装完成后,继续安装

    npm install sass-resources-loader --save-dev

    接下来,我们找到build文件夹下的utils文件

    exports.cssLoaders = function (options) {}中加上一下代码:

    function lessResourceLoader() {
      var loaders = [
        cssLoader,
        'less-loader',
        {
          loader: 'sass-resources-loader',
          options: {
            resources: [
              path.resolve(__dirname, '../src/assets/styles/common.less'),
            ]
          }
        }
      ];
      if (options.extract) {
        return ExtractTextPlugin.extract({
          use: loaders,
          fallback: 'vue-style-loader'
        })
      } else {
        return ['vue-style-loader'].concat(loaders)
      }
    }

    注意位置一定要加对,且记得把path.resolve(__dirname,'../src/assets/styles/common.less')路径改成自己对应的文件。然后后面将 return{} 块中的 less: generateLoaders('less') 替换成上面自定义的函数 less: lessResourceLoader();

    修改完配置文件记得重启服务器:npm run dev

    给大家上图看一下主要配置:

    重启服务后,基本就配置完成。不过我们还是的检测一下。

    在common.less中写入:

    @bg-blue: #3576e0;

    在App.vue中修改

    <style lang="less" scoped>
    #app {
      font-family: 'Avenir', Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: center;
      color: #2c3e50;
      margin-top: 60px;
      background-color: @bg-blue;
    }
    </style>

    然后保存,打开浏览器。就可以看到我们配置好的全局变量被成功引用

    背景色变成蓝色。说明在common.less中定义的变量被正常调用。这里全局的less配置也就成功了。

    引入全局的基本样式

    项目搭建好了,但是可以看到一些基本的样式需要我们进行更改

    1. assets文件夹下styles文件中新建base.less文件。百度搜索reset.css然后复制文中的基本样式,并添加html,body宽高为100%即可。

    2. 新建function.less文件css样式工具封装在文中找到适合自己项目的文件复制并粘贴金function.less文件。

    3. 新建index.css文件作为出口文件。文件内容如下:

    @import './base.less';
    @import './function.less';
    1. 在main.js文件中引入index.css文件

    至此引入的基本的样式文件就算被全局引用了。

    至于封装的function样式类,我们只需要在需要的时候,选择性的 添加类名即可。

    安装element-ui

    npm install element-ui -s

    安装完成后,在main.js中引入

    import ElementUI from 'element-ui'
    import 'element-ui/lib/theme-chalk/index.css'
    Vue.use(ElementUI);

    安装完成后,修改App.vue:

    <template>
      <div id="app">
        <img src="./assets/logo.png">
        <el-button type="danger">测试Element</el-button>
        <router-view/>
      </div>
    </template>

    打开浏览器,我们可以看到button按钮是已经生效了。

    这里,element-ui也已经安装成功。

    请求方法,拦截器,全局加载框的配置

    在src文件夹下新建utils文件夹新建http.js为我们的请求方法的文件

    封装前,我们需要借助axios来作为请求的底层,并且需要缓存机制去缓存登录状态

    这里借助两个依赖包 axios和vue-cookies

    npm install axios vue-cookies -s
    1. 请求方法封装。这里封装三个类型的。get请求一个post,json格式的一个post,body格式的一个

    import axios from 'axios'
    import Vue from 'vue'
    
    // 请求方式的配置
    export const postJsonRequest = (url, params) => {
      return axios({
        method: 'post',
        url: url,
        data: params,
        headers: {
          'Content-Type': 'application/json',
        },
      });
    }
    export const postRequest = (url, params) => {
      return axios({
        method: 'post',
        url: url,
        data: params,
        transformRequest: [function (data) {
          let ret = ''
          for (let it in data) {
            ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
          }
          return ret
        }],
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        }
      });
    }
    
    export const getRequest = (url, data = {}) => {
      return axios({
        method: 'get',
        params: data,
        url: url,
      });
    }

    这个就是请求方法的配置

    随后我们在utils目录下新建index.js文件作为工具文件夹的出口文件

    utils>index.js:

    引入封装的http请求并挂在到Vue原型链上供全局使用

    import * as http from './http';
    
    const install = (Vue, opts = {}) => {
      if (install.installed) return;
      Vue.prototype.$http = http;
    }
    
    export default install

    main.js

    import Utils from './utils';
    Vue.use(Utils);

    这里方法请求的封装就算是完成了。

    1. 拦截器的配置

    拦截器:登录状态的管理,以及每次请求时,我们做的一些处理。对不同code状态做的不同的处理

    拦截器分为请求拦截器和响应拦截器。字面意思可理解为:请求时的拦截和响应时的拦截

    import axios from 'axios'
    import { Message } from 'element-ui'
    import router from 'vue-router'
    import Vue from 'vue'
    import VueCookies from 'vue-cookies'
    // 请求拦截
    axios.interceptors.request.use(config => {
      if (VueCookies.isKey('isLogin')) {
        const token = getToken(config);
        config.headers['token'] = token;
      }
      return config;
    }, err => {
      Message.error({
        message: '请求超时!'
      });
      return Promise.resolve(err);
    })
    
    // 响应拦截
    axios.interceptors.response.use(res => {
      switch (res.data.code) {
        case 200:
          return res.data.result;
        case 401:
          Message.error({
            message: res.data.message
          });
          router.push('/login');
          VueCookies.remove('userinfo');
          return Promise.reject(res);
        case 201:
          Message.error({
            message: res.data.message
          });
        case 403:
          Message.warning({
            message: res.data.message
          });
          return Promise.reject(res);
        default:
          return Promise.reject(res);
      }
    
    }, err => {
      if (!err.response) {
        return false;
      }
      switch (err.response.status) {
        case 500:
          Message.error({
            message: '服务器出小差了⊙﹏⊙∥'
          });
          break;
        case 504:
          Message.error({
            message: '服务器被吃了⊙﹏⊙∥'
          });
          break;
        case 404:
          Message.error({
            message: '服务器被吃了⊙﹏⊙∥'
          });
          break;
        case 403:
          Message.error({
            message: '权限不足,请联系管理员!'
          });
          break;
        default:
          Message.error({
            message: '网络超时'
          });
      }
      return Promise.reject(err);
    })

    Vue拦截器的基本配置也就是以上这些了。下来个人想要实现一个loading的局部加载。就要借助计数器了。

    为什么说要做一个计数器?

    因为在同一个view中可能会有很多个请求,我们总不可能在一个请求完成后就关闭loading的加载吧?这样显然不合适。所以我们需要计时器去将所有的请求记录起来。然后在所有的请求完成后关闭loading。

    //请求时loading配置
    var loading;
    
    function startLoading() {
      loading = Vue.prototype.$loading({
        lock: true,
        text: "Loading...",
        background: 'rgba(0, 0, 0, 0.5)',
        target: document.querySelector('.loading-area') //设置加载动画区域
      });
    }
    
    function endLoading() {
      loading.close();
    }
    var needLoadingRequestCount = 0;
    //开启loading并计数
    function showFullScreenLoading() {
      if (needLoadingRequestCount === 0) {
        startLoading();
      }
      needLoadingRequestCount++;
    };
    //计数器==0关闭loading
    function tryHideFullScreenLoading() {
      if (needLoadingRequestCount <= 0) return;
      needLoadingRequestCount--;
      if (needLoadingRequestCount === 0) {
        endLoading();
      }
    };

    loading的依赖在element安装后是直接挂在到了vue的原型链上。我们不再需要做过多处理。这里计数器完成。下来我们只需要在请求的时候调用,在响应的时候或者请求报错的时候调用关闭的方法即可。

    需要注意的是要实现局部的加载,我们就需要在响应实现loading的地方添加类型loading-area才可以!!!

    http.js所有代码如下:

    /**
     * @description 配置网络请求
     * @author luokaibin chaizhiyang
     */
    import axios from 'axios'
    import { Message } from 'element-ui'
    import router from 'vue-router'
    import Vue from 'vue'
    import VueCookies from 'vue-cookies'
    
    axios.defaults.timeout = 300000; // 请求超时5fen
    
    //请求时loading配置
    var loading;
    
    function startLoading() {
      loading = Vue.prototype.$loading({
        lock: true,
        text: "Loading...",
        background: 'rgba(0, 0, 0, 0.5)',
        target: document.querySelector('.loading-area') //设置加载动画区域
      });
    }
    
    function endLoading() {
      loading.close();
    }
    var needLoadingRequestCount = 0;
    
    function showFullScreenLoading() {
      if (needLoadingRequestCount === 0) {
        startLoading();
      }
      needLoadingRequestCount++;
    };
    
    function tryHideFullScreenLoading() {
      if (needLoadingRequestCount <= 0) return;
      needLoadingRequestCount--;
      if (needLoadingRequestCount === 0) {
        endLoading();
      }
    };
    
    // 请求拦截
    axios.interceptors.request.use(config => {
      showFullScreenLoading();
      if (VueCookies.isKey('userinfo')) {
        const token = getToken(config);
        config.headers['token'] = token;
      }
      return config;
    }, err => {
      tryHideFullScreenLoading();
      Message.error({
        message: '请求超时!'
      });
      return Promise.resolve(err);
    })
    
    // 响应拦截
    axios.interceptors.response.use(res => {
      tryHideFullScreenLoading();
      switch (res.data.code) {
        case 200:
          return res.data.result;
        case 401:
          Message.error({
            message: res.data.message
          });
          router.push('/login');
          // VueCookies.remove('userinfo');
          return Promise.reject(res);
        case 201:
          Message.error({
            message: res.data.message
          });
        case 403:
          Message.warning({
            message: res.data.message
          });
          return Promise.reject(res);
        default:
          return Promise.reject(res);
      }
    
    }, err => {
      tryHideFullScreenLoading();
      if (!err.response) {
        return false;
      }
      switch (err.response.status) {
        case 500:
          Message.error({
            message: '服务器出小差了⊙﹏⊙∥'
          });
          break;
        case 504:
          Message.error({
            message: '服务器被吃了⊙﹏⊙∥'
          });
          break;
        case 404:
          Message.error({
            message: '服务器被吃了⊙﹏⊙∥'
          });
          break;
        case 403:
          Message.error({
            message: '权限不足,请联系管理员!'
          });
          break;
        default:
          Message.error({
            message: '网络超时'
          });
      }
      return Promise.reject(err);
    })
    
    // 请求方式的配置
    export const postJsonRequest = (url, params) => {
      return axios({
        method: 'post',
        url: url,
        data: params,
        headers: {
          'Content-Type': 'application/json',
        },
      });
    }
    export const postRequest = (url, params) => {
      return axios({
        method: 'post',
        url: url,
        data: params,
        transformRequest: [function (data) {
          let ret = ''
          for (let it in data) {
            ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
          }
          return ret
        }],
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        }
      });
    }
    
    export const getRequest = (url, data = {}) => {
      return axios({
        method: 'get',
        params: data,
        url: url,
      });
    }

    至此整个的全局loading,拦截器,fetch方法的封装就算完成了。

    总结

    1. element-ui的使用,我们要多借助官方文档去灵活使用。而不是死记硬背。太多的ui框架,再强的大脑也背不过来,何况,为了自己的头发着想一下啦~

    2. 这里并未设置跨域访问。大家可以根据vue的代理。这样也是可以进行跨域访问的。详见我的这篇博文Vue设置代理进行跨域访问,不过这篇文章是针对vue3.0设置,2.0的设置文件,为config下的index.js文件。当然前提是后端接口也进行跨域的设置,否则单纯的前端设置是没有作用的。

    3. 局部loading的使用,要求有一个好的布局基础。否则,在后期的使用过程中,会有喝多样式错乱的问题。

    4. 请求方式有很多种,还有下载文件的请求配置,delete,post,put等等大家可根据自己需求灵活配置。最终的使用this.$http. get/post/put/...等等就可以进行调用

    下一章节:

    底层布局(Layout),路由(Router),状态库(vuex),Github仓库关联。

    敬请期待~

  • 相关阅读:
    解析包含空格的字符串
    机器学习实战之Logistic回归
    复杂的数据类型
    k-近邻算法
    C&C++
    位运算
    文件操作
    结构体与共用体
    预处理-04-#if defined和#if !defined
    预处理-03-文件包含、条件编译、小结
  • 原文地址:https://www.cnblogs.com/bgwhite/p/10869953.html
Copyright © 2011-2022 走看看