[Summary] background Vue project management project summary

The company did mostly back office management project, excluding the business logic of each project, in fact, can do with a common set of templates.

Login logic

Each system has its own logic to log out, and the front end we have to do is actually request the background, to get permission to log on, log on to bring rights, access to user information and menu information. In vueprojects in development, we usually do in this series to determine the global routing hook.

router.beforeEach(async(to, from, next) => {
  NProgress.start();
  await store.dispatch('SetConfigApi'); // 获取配置
  await store.dispatch('SetApi'); // 设置基本配置
  const token = await store.dispatch('getToken'); // 获取token
  if (token) {
    // 用户信息不存在
    if (!store.getters.userInfo) {
      await store.dispatch('GetUser'); // 获取用户信息
      const menuList = await store.dispatch('GetMenu', localRoute); // 获取菜单
      await store.dispatch('GenerateRoutes', localRoute);
      router.addRoutes(store.getters.addRoutes);
      ...
    } else {
      next();
    }
  } else {
    if (whiteList.includes(to.path)) {
      // 在免登录白名单,直接进入
      next();
    } else {
      window.location.href = store.getters.api.IPORTAL_LOCAL_API;
      NProgress.done();
    }
  }
});
复制代码

When the user enters the system, to get your system configuration information, the configuration information can be front-end jsonfile, or the background the interface; in this way can be flexible configuration change to a project, but do not always die packaged into the project, to direct the operation and maintenance child boots can modify the configuration information corresponding to it.

Permissions menu

Routing the previous menu is written directly to die at the front, but when we have direct access to this route, you can still enter into this function page; later changed to dynamically add direct routes router.addRoutes.

  1. First get a list of front-end menu
  2. Add User menu cycle route set based on the acquired list menu
  3. Dynamically add routes

Specific View

Request program

Item request is used axios, it can be added to our interceptor to handle the request can be processed by axios.CancelTokenrepeating the request, particularly to see the code:

// 设置请求统一信息
import axios from 'axios';
import store from '@/store/index.js';
import qs from 'qs';
import { messages } from './msg-box.js';

const service = axios.create({
  timeout: 300000, // 超时设置
  withCredentials: true // 跨域请求
});

let hasLogoutStatus = false; // 是否某个请求存在需要退出的状态

const queue = []; // 请求队列

const CancelToken = axios.CancelToken; // axios内置的中断方法

/**
 * 拼接请求的url和方法;
 * 同样的`url + method` 可以视为相同的请求
 * @param {Object} config 请求头对象
 */
const token = config => {
  return `${config.url}_${config.method}`;
};

/**
 * 中断重复的请求,并从队列中移除
 * @param {Object} config 请求头对象
 */
const removeQueue = config => {
  for (let i = 0, size = queue.length; i < size; i++) {
    const task = queue[i];
    if (!task) return;
    // 出现401,403状态码中断后续请求
    const isLogout = token(config).includes('logout');
    // 退出接口跳过中断逻辑
    if (!isLogout && hasLogoutStatus) {
      task.token();
      queue.splice(i, 1);
    } else {
      const cancelMethods = ['post', 'put', 'delete']; // 需要中断的请求方式
      const { method } = config;
      if (cancelMethods.includes(method)) {
        if (task.token === token(config)) {
          task.cancel();
          queue.splice(i, 1);
        }
      }
    }
  }
};

/**
 * 请求错误统一处理
 * @param {Object} response 错误对象
 */
const errorHandle = response => {
  // eslint-disable-next-line prettier/prettier
  const { status, data: { message = '' }} = response;
  let msg = message;
  if (!message) {
    switch (status) {
      case 401:
        msg = '您没有权限访问此操作!';
        break;
      case 403:
        msg = '您的登录状态已失效,请重新登录。';
        break;
      case 424:
        msg = response.data.error;
        break;
      default:
        msg = '服务请求异常,请刷新重试。';
    }
  }
  hasLogoutStatus = status === 401 || status === 403;
  if (hasLogoutStatus) {
    messages('error', msg, () => {
      store.dispatch('Logout');
    });
  }
  messages('error', msg);
};

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 中断之前的同名请求
    removeQueue(config);
    // 添加cancelToken
    config.cancelToken = new CancelToken(c => {
      queue.push({ token: token(config), cancel: c });
    });
    // 登录后添加token
    if (store.getters.token) {
      config.headers['Authorization'] =
        store.getters.token.token_type + ' ' + store.getters.token.access_token;
    }
    return config;
  },
  error => {
    return Promise.reject(error);
  }
);

// 响应拦截器
service.interceptors.response.use(
  response => {
    // 在请求完成后,自动移出队列
    removeQueue(response.config);
    // 关闭全局按钮Loading响应
    store.dispatch('CancalLoading');
    // 错误码处理
    if (response.status !== 200) {
      return Promise.reject(response);
    }
    return response;
  },
  error => {
    const { response } = error;
    if (response) {
      // 错误处理
      errorHandle(response);
      return Promise.reject(response);
    } else {
      // 请求超时
      if (error.message.includes('timeout')) {
        console.log('超时了');
        messages('error', '请求已超时,请刷新或检查互联网连接');
      } else {
        // 断网,可以展示断网组件
        console.log('断网了');
        messages('error', '请检查网络是否已连接');
      }
    }
  }
);

export default {
  get: (url, data = {}) => {
    return new Promise((resolve, reject) => {
      service
        .get(store.getters.api.API + url, { params: data })
        .then(response => {
          resolve(response.data);
        })
        .catch(error => {
          reject(error);
        });
    }).catch(error => {
      throw new Error(error);
    });
  },
  post: (url, data = {}) => {
    return new Promise((resolve, reject) => {
      service
        .post(store.getters.api.API + url, data, {
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
          },
          withCredentials: true,
          transformRequest: [
            data => {
              return qs.stringify(data);
            }
          ]
        })
        .then(response => {
          resolve(response.data);
        })
        .catch(error => {
          reject(error);
        });
    }).catch(error => {
      return Promise.reject(error);
    });
  },
  ...
  /**
   * blob下载
   * @param {String} url 请求地址
   * @param {String} method 请求方式 默认`get`
   * @param {Object} data 请求数据
   */
  exportFile({ url = '', data = {}, method = 'get' }) {
    return new Promise((resolve, reject) => {
      const isPost =
        method.toLocaleUpperCase() === 'POST'
          ? {
            headers: { 'Content-Type': 'application/json' },
            data
          }
          : {
            params: data
          };
      const downConfig = {
        withCredentials: true,
        responseType: 'blob',
        ...isPost
      };
      service
        // eslint-disable-next-line no-unexpected-multiline
        [method](store.getters.api.API + url, downConfig)
        .then(response => {
          resolve(response);
        })
        .catch(error => {
          reject(error);
        });
    }).catch(error => {
      return Promise.reject(error);
    });
  }
};

复制代码

When a request is required to be a reference file http.js, can also be linked to the vueprototype, the components used inthis.$http

// user.js
import http from '@/utils/http.js';

export function getUser() {
  return http.get('/user');
}

// main.js
Vue.prototype.$http = http;
复制代码

Button Loading process

Button loadingeffect may process a background scene in response to a little longer, as used herein, storeencapsulation of the treatment.

// loading.js
import Vue from 'vue';

const loading = {
  state: {},
  mutations: {
    SET_LOADING: (state, data) => {
      const isObject =
        Object.prototype.toString.call(data) === '[object Object]';
      if (!isObject) return;
      Object.keys(data).forEach(key => {
        Vue.set(state, key, data[key]);
      });
    },
    CANCAL_LOADING: state => {
      Object.keys(state).forEach(key => {
        Vue.delete(state, key);
      });
    }
  },
  actions: {
    SetLoading({ commit }, data) {
      commit('SET_LOADING', data);
    },
    CancalLoading({ commit }, data) {
      commit('CANCAL_LOADING', data);
    }
  }
};

export default loading;

// http.js
service.interceptors.response.use(
  response => {
    // 关闭全局按钮Loading响应
    store.dispatch('CancalLoading');
    ...
})    
复制代码

Defined within the component

<el-button :loading="btn.save" @click="handleClick">保存</el-button>

computed: {
    btn() {
        return this.$store.state.loading;
    }
}
methods: {
    handleClick() {
        this.$store.dispatch('SetLoading', { save: true });    
    }
}
复制代码

They can use more perfect loading, rather than each in the datadefinition of.

to sum up

Some treatments are more back-end systems can be used, the specific code view .

Other summary article:

Reproduced in: https: //juejin.im/post/5cedfea96fb9a07ef06f761e

Guess you like

Origin blog.csdn.net/weixin_33781606/article/details/93164188