# 项目目录结构

## 3.请求处理

项目中使用mock-server开启本地服务API接口，使得前端与后端项目都可单独进行开发；模拟后端接口的配置文件统一放置在`@/mock/`文件夹下面

### 3.1使用mockjs模拟后端API

```bash
index.js  ##mock配置文件
mock-server.js  ##mock本地服务配置
table.js
user.js   ##用户的后端API响应数据
utils.js
```

### 3.2模拟后端接口

```javascript
module.exports = [
  // user login
  {
    url: "/vue-admin-template/user/login",
    type: "post",
    response: config => {
      const { username } = config.body;  //获取req.body.username
      const token = tokens[username];    //获取用户的tookens

      // mock error
      if (!token) {
        return {
          code: 60204,
          message: "Account and password are incorrect."
        };
      }

      return {
        code: 20000,
        data: token   //验证成功，返回token数据
      };
    }
  }]
```

### 3.3axios发起ajax请求

使用vuex进行状态管理，配置了请求拦截器

`getToken`在`auth.js`中配置好了，这个文件中还配置了一系列的对token的操作函数

```javascript
// request interceptor
service.interceptors.request.use(
  config => {
    // do something before request is sent

    if (store.getters.token) {
      // let each request carry token
      // ['X-Token'] is a custom headers key
      // please modify it according to the actual situation
      config.headers['X-Token'] = getToken() //设置header中的token
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)
```

配置响应拦截器

* 后端的错误状态码，可以自己定义
* 当后端正常响应，并且返回错误代码时，使用Promise返回一个reject，在路由处用catch进行捕获

```javascript
service.interceptors.response.use( response => {
    const res = response.data

    // if the custom code is not 20000, it is judged as an error.
    if (res.code !== 20000) {
      Message({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      })

      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
      if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
        // to re-login
        MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
          confirmButtonText: 'Re-Login',
          cancelButtonText: 'Cancel',
          type: 'warning'
        }).then(() => {
          store.dispatch('user/resetToken').then(() => {
            location.reload()
          })
        })
      }
      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return res
    }
  },
  error => {
    console.log('err' + error) // for debug
    Message({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

export default service
```

## 4.前端路由跳转

`@/src/layout/`目录下配置了页面的布局，因为vue-element-admin是一个后台管理系统，因此也仅仅只含有一个布局；

在`@/src/router/`目录下配置了整个前端路由；在路由跳转时，采用`transition`动画特效

```javascript
  <section class="app-main">
    <transition name="fade-transform" mode="out-in">
      <router-view :key="key" />
    </transition>
  </section>
```

## 5.组件使用封装好的API

在`@/src/api/table 目录下就封装了一个`getlsit()\`函数，用于直接操作request的请求

```javascript
export function getList(params) {
  return request({
    url: "/vue-admin-template/table/li1st1",
    method: "get",
    params
  });
}
```

错误的处理，在错误到达组件之前就已经被axios响应拦截器给处理掉了，但是在组件那会返回一个Promise.reject对象；不对它进行处理也没有关系，但是会在后台打印一个Promise错误

```javascript
createError.js?2d83:16 Uncaught (in promise) Error: Request failed with status code 404
    at createError (createError.js?2d83:16)
    at settle (settle.js?467f:18)
    at XMLHttpRequest.handleLoad (xhr.js?b50d:59)
```

### 6.Vuex用于组件间的通信

* 声明user组件的state数据

```javascript
const getDefaultState = () => {
  return {
    token: getToken(),
    name: '',
    avatar: ''
  }
}
const state = getDefaultState()
```

* 定义mutations方法

  这些数据都是从cookie中获得的

  * 重置state内部的数据
  * 设置cookie
  * 设置用户名
  * 设置用户头像

```javascript
const mutations = {
  RESET_STATE: (state) => {
    Object.assign(state, getDefaultState())
  },
  SET_TOKEN: (state, token) => {
    state.token = token
  },
  SET_NAME: (state, name) => {
    state.name = name
  },
  SET_AVATAR: (state, avatar) => {
    state.avatar = avatar
  }
}
```

* 定义异步操作

  使用action进行异步操作，例如登录操作；这个操作在发起Ajax请求得到数据后返回一个Promise对象

```javascript
const actions = {
  // user login
  login({ commit }, userInfo) {
    const { username, password } = userInfo
    return new Promise((resolve, reject) => {
      login({ username: username.trim(), password: password }).then(response => {
        const { data } = response
        commit('SET_TOKEN', data.token)
        setToken(data.token)
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  },
```

* 组合多个module
  * 在根store中，指定组合多个模块
  * 在getters中指定封装在根store的state

```javascript
const store = new Vuex.Store({
  modules: {
    app,
    settings,
    user
  },
  getters
})
```

## 参考

1. <https://panjiachen.gitee.io/vue-element-admin-site/zh/guide/>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zpliu.gitbook.io/booknote/vue-admin/01-mu-lu-jie-gou.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
