Merge branch 'release/v1.2.1'

Former-commit-id: 70e0b604b52be0b67f3e4410267efb0aab825033 [formerly 3213c30e40aae71b520834283486336dafefe761] [formerly 70e0b604b52be0b67f3e4410267efb0aab825033 [formerly 3213c30e40aae71b520834283486336dafefe761] [formerly 70e0b604b52be0b67f3e4410267efb0aab825033 [formerly 3213c30e40aae71b520834283486336dafefe761] [formerly 3213c30e40aae71b520834283486336dafefe761 [formerly ff487431f8a572b715a8de2b1e43ef901f55349c [formerly ee171eb97c043ba78dc9278cdecc60ea416756ed]]]]]
Former-commit-id: 2876fa4c68ec4395b328c511eb8aa96861cf8104
Former-commit-id: 38a2c96e8acbb1932ca8369ddcc5eb5f1df400f4
Former-commit-id: 2c86cd431f88a1cff3318d742284e0b7e3c379c9 [formerly 217006d35b7c457105057a9bf405889306479c16]
Former-commit-id: f6d0e7431ea3dcc1f837f52fe82989e3027fd386
Former-commit-id: 7ece94c4171b4e69fe6bff52a7b68ec2fc16a099
Former-commit-id: 6c0b322014af909374b0914515b68a6160db7b82
Former-commit-id: ef073f1695a05bab6be7b26257d27b053026dca5
Former-commit-id: 2dac74f74ccb8ac818e2aab54bda8c8ee30be370
This commit is contained in:
liyang
2018-08-27 13:49:57 +08:00
23 changed files with 431 additions and 54 deletions

5
.env
View File

@@ -1,4 +1,7 @@
# 所有环境
# 页面 title 前缀
VUE_APP_TITLE=D2Admin
VUE_APP_TITLE=D2Admin
# 网络请求公用地址
VUE_APP_API=/api/

View File

@@ -51,8 +51,9 @@ module.exports = {
{ text: '组件', link: '/zh/sys-components/' },
{ text: '插件', link: '/zh/sys-plugins/' },
{ text: 'vuex', link: '/zh/sys-vuex/' },
{ text: '菜单', link: '/zh/sys-menu/' },
{ text: '路由', link: '/zh/sys-route/' },
{ text: '菜单', link: '/zh/sys-menu/' },
{ text: '异步请求', link: '/zh/sys-ajax/' },
{ text: '多页面', link: '/zh/sys-multi-page/' },
{ text: '数据持久化', link: '/zh/sys-db/' },
{ text: 'CSS 实用类', link: '/zh/sys-css/' },

View File

@@ -10,4 +10,5 @@ sidebar: auto
| <img src="https://avatars1.githubusercontent.com/u/24645480?s=460&v=4" style="width: 30px;"/> | sunhaoxiang | [https://github.com/sunhaoxiang](https://github.com/sunhaoxiang) |
| <img src="https://avatars2.githubusercontent.com/u/11420885?s=460&v=4" style="width: 30px;"/> | namklaw | [https://github.com/namklaw](https://github.com/namklaw) |
| <img src="https://avatars2.githubusercontent.com/u/6757507?s=460&v=4" style="width: 30px;"/> | mokeyjay | [https://github.com/mokeyjay](https://github.com/mokeyjay) |
| <img src="https://avatars2.githubusercontent.com/u/10137653?s=460&v=4" style="width: 30px;"/> | Aysnine | [https://github.com/Aysnine](https://github.com/Aysnine) |
| <img src="https://avatars2.githubusercontent.com/u/10137653?s=460&v=4" style="width: 30px;"/> | Aysnine | [https://github.com/Aysnine](https://github.com/Aysnine) |
| <img src="https://avatars2.githubusercontent.com/u/12825624?s=460&v=4" style="width: 30px;"/> | rongxingsun | [https://github.com/rongxingsun](https://github.com/rongxingsun) |

195
docs/zh/sys-ajax/README.md Normal file
View File

@@ -0,0 +1,195 @@
---
sidebar: auto
---
# 异步请求
D2Admin 使用 [axios](https://github.com/axios/axios) 作为异步请求工具,并做了一些封装。
| axios | 地址 |
| --- | --- |
| Github | [https://github.com/axios/axios](https://github.com/axios/axios) |
| npm | [https://www.npmjs.com/package/axios](https://www.npmjs.com/package/axios) |
| 中文文档 | [https://www.kancloud.cn/yunye/axios/234845](https://www.kancloud.cn/yunye/axios/234845) |
## 介绍
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
* 支持浏览器和node.js
* 支持promise
* 能拦截请求和响应
* 能转换请求和响应数据
* 能取消请求
* 自动转换JSON数据
* 浏览器端支持防止CSRF(跨站请求伪造)
## 浏览器支持
![Chrome](https://raw.github.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/src/safari/safari_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/src/opera/opera_48x48.png) | ![Edge](https://raw.github.com/alrra/browser-logos/master/src/edge/edge_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png) |
--- | --- | --- | --- | --- | --- |
Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | 11 ✔ |
[![Browser Matrix](https://saucelabs.com/open_sauce/build_matrix/axios.svg)](https://saucelabs.com/u/axios)
## 使用方式
axios 默认的使用方式在这里不做介绍D2Admin 推荐在您的项目中使用下面的方式获取数据:
### 设置接口地址
默认的请求地址在 `d2-admin/.env`
```
VUE_APP_API=/api/
```
上述设置将在您访问 `/demo/a` 时实际去访问 `/api/demo/a`
### 区分不同环境设置接口地址
如果您希望不同的环境使用不同的请求地址,可以在 `d2-admin/.env.development` 中添加设置(示例):
```
VUE_APP_API=/api-dev/
```
这样您在开发环境和正式环境就有了不同的公共请求地址,在开发环境访问 `/demo/a` 时实际去访问 `/api-dev/demo/a`
### 通用配置
您在开始使用 D2Admin 开发您的项目之前,应该首先修改 `d2-admin/src/plugin/axios/index.js` 下的设置。
默认的设置需要遵循下面的数据返回格式约定:
``` js
{
// 和后台约定的状态码
code: 0,
// 后台返回请求状态信息
msg: '返回信息',
// data 内才是真正的返回数据
data: {
list: [
...
]
}
}
```
在响应拦截器中处理完数据后将会返回:
``` js
{
list: [
...
]
}
```
### 业务错误
当发生错误时返回的数据示例:
``` js
{
// 和后台约定的状态码
code: 'unlogin',
// 后台返回请求状态信息
msg: '用户没有登录'
}
```
如果针对某个错误指定处理方法,应该在响应拦截器中加入对应的代码:
``` js
service.interceptors.response.use(
response => {
// 成功返回数据,在这里判断和后台约定的状态标识
}
)
```
### http 错误
如果需要针对某个 http 错误指定处理方法,应该在响应拦截器中第二个参数中添加对应的代码。
``` js
service.interceptors.response.use(
response => {},
error => {
// 发生 http 错误,在这里判断状态码
}
)
```
### 不返回 code
在默认的设置中,如果您的接口没有返回 code 字段,将不会进行状态(非 http 状态,而是和后台约定好的状态类型)判断,直接返回 axios 请求返回的数据。
例如接口返回如下数据:
``` js
{
list: [
...
]
}
```
在响应拦截器中判断该接口没有返回 code 字段,将会直接将返回:
``` js
{
list: [
...
]
}
```
### 设计 API
假设您有一个返回数据的 API 接口,想访问它,您首先应该在 `d2-admin/src/api` 文件夹内创建合适的文件目录,例如:`d2-admin/src/api/demo/business/table/1/index.js`,这个文件中应该导出一个或者多个请求:
``` js
import request from '@/plugin/axios'
export function BusinessTable1List (data) {
return request({
url: '/demo/business/table/1',
method: 'post',
data
})
}
```
### 使用 API 获取数据
在上面的步骤中创建了 API 文件,您应该在页面中这样使用:
``` vue
<script>
import { BusinessTable1List } from '@/api/demo/business/table/1'
export default {
methods: {
handleSubmit (form) {
BusinessTable1List({
name: ''
})
.then(res => {
// 返回数据
})
.catch(err => {
// 异常情况
})
}
}
}
</script>
```
而不是在页面中直接调用 axios。
::: tip
虽然没有强制规定,请注意您的 API 文件夹结构规律性
:::

View File

@@ -1 +1 @@
208665c165b32daf4021f61c3ad430a972cb9735
937f5369a5d9b43eeb3c7fa2991f76724481721c

View File

@@ -0,0 +1,9 @@
import request from '@/plugin/axios'
export function ComponentsMarkdownBase (url) {
return request({
baseURL: process.env.BASE_URL,
url,
method: 'get'
})
}

View File

@@ -0,0 +1,9 @@
import request from '@/plugin/axios'
export function BusinessTable1List (data) {
return request({
url: '/demo/business/table/1',
method: 'post',
data
})
}

View File

@@ -0,0 +1,8 @@
import request from '@/plugin/axios'
export function PluginMocksAjax () {
return request({
url: '/demo/plugins/mock/ajax',
method: 'get'
})
}

17
src/api/sys/http/index.js Normal file
View File

@@ -0,0 +1,17 @@
import request from '@/plugin/axios'
export function httpGet (url, params = {}) {
return request({
url,
method: 'get',
params
})
}
export function httpPost (url, data = {}) {
return request({
url,
method: 'post',
data
})
}

View File

@@ -0,0 +1,9 @@
import request from '@/plugin/axios'
export function AccountLogin (data) {
return request({
url: '/login',
method: 'post',
data
})
}

View File

@@ -1 +1 @@
87fff7214e7b64123842f1f52b649a8af614eb46
bfee5e91a3b150e04d9f24695061f6f3e7c59342

View File

@@ -12,6 +12,7 @@ import marked from 'marked'
import highlight from 'highlight.js'
import bandupan from './plugin/baidupan'
import 'github-markdown-css'
import { ComponentsMarkdownBase } from '@/api/components/markdown'
export default {
name: 'd2-markdown',
props: {
@@ -63,7 +64,7 @@ export default {
},
// 从 url 加载原始数据
async getReadme (url) {
const data = await this.$axios.get(url)
const data = await ComponentsMarkdownBase(url)
return this.marked(data)
},
marked (data) {

View File

@@ -1,5 +1,5 @@
<template>
<el-menu mode="horizontal" @select="handleMenuSelect">
<el-menu mode="horizontal" :default-active="active" @select="handleMenuSelect">
<template v-for="(menu, menuIndex) in header">
<d2-layout-header-aside-menu-item v-if="menu.children === undefined" :menu="menu" :key="menuIndex"/>
<d2-layout-header-aside-menu-sub v-else :menu="menu" :key="menuIndex"/>
@@ -25,6 +25,19 @@ export default {
...mapState('d2admin/menu', [
'header'
])
},
data () {
return {
active: ''
}
},
watch: {
'$route.matched': {
handler (val) {
this.active = val[val.length - 1].path
},
immediate: true
}
}
}
</script>

View File

@@ -7,17 +7,23 @@ Mock.mock('/api/demo/business/table/1', ({ body }) => {
page.total = 1000
return Mock.mock(
{
page,
'list|20': [{
'key': '@guid',
'value|1': [10, 100, 200, 500],
'type': '@boolean',
'admin': '@cname',
'adminNote': '@cparagraph(0.5)',
'dateTimeCreat': '@datetime',
'used': '@boolean',
'dateTimeUse': '@datetime'
}]
code: 0,
msg: '获取数据成功',
data: {
page,
'list|20': [
{
'key': '@guid',
'value|1': [10, 100, 200, 500],
'type': '@boolean',
'admin': '@cname',
'adminNote': '@cparagraph(0.5)',
'dateTimeCreat': '@datetime',
'used': '@boolean',
'dateTimeUse': '@datetime'
}
]
}
}
)
})

View File

@@ -1,13 +1,19 @@
import Mock from 'mockjs'
Mock.mock('/api/demo/plugins/mock/ajax', {
'list|4-10': [{
'id|+1': 1,
'name': '@CNAME',
'star|1-5': '★',
'delFlag|1': [0, 1],
'creatDate': '@DATE',
'address': '@CITY',
'zip': '@ZIP'
}]
code: 0,
msg: '获取数据成功',
data: {
'list|4-10': [
{
'id|+1': 1,
'name': '@CNAME',
'star|1-5': '★',
'delFlag|1': [0, 1],
'creatDate': '@DATE',
'address': '@CITY',
'zip': '@ZIP'
}
]
}
})

View File

@@ -21,7 +21,7 @@ const userDB = [
}
]
Mock.mock('/login', 'post', ({url, type, body}) => {
Mock.mock('/api/login', 'post', ({url, type, body}) => {
const bodyObj = JSON.parse(body)
const user = userDB.find(e => e.username === bodyObj.username && e.password === bodyObj.password)
if (user) {
@@ -36,7 +36,8 @@ Mock.mock('/login', 'post', ({url, type, body}) => {
} else {
return {
code: 401,
msg: '用户名或密码错误'
msg: '用户名或密码错误',
data: {}
}
}
})

View File

@@ -17,6 +17,7 @@
</template>
<script>
import { BusinessTable1List } from '@/api/demo/business/table/1'
export default {
// name 值和本页的 $route.name 一致才可以缓存页面
name: 'demo-business-table-1',
@@ -53,7 +54,7 @@ export default {
this.$notify({
title: '开始请求模拟表格数据'
})
this.$axios.post('/api/demo/business/table/1', {
BusinessTable1List({
...form,
page: this.page
})

View File

@@ -1,6 +1,6 @@
<template>
<d2-container>
<template slot="header">异步加载文件</template>
<d2-markdown :url="`${$baseUrl}markdown/demo.md`"/>
<d2-markdown url="markdown/demo.md"/>
</d2-container>
</template>

View File

@@ -23,7 +23,9 @@
</template>
<script>
import { PluginMocksAjax } from '@/api/demo/plugins/mocks/ajax'
export default {
name: 'demo-plugins-mock-ajax',
data () {
return {
table: {
@@ -37,7 +39,7 @@ export default {
},
methods: {
ajax () {
this.$axios.get('/api/demo/plugins/mock/ajax')
PluginMocksAjax()
.then(res => {
this.table.columns = Object.keys(res.list[0]).map(e => ({
label: e,
@@ -45,6 +47,9 @@ export default {
}))
this.table.data = res.list
})
.catch(() => {
// 错误情况
})
}
}
}

View File

@@ -1,13 +1,110 @@
import store from '@/store'
import axios from 'axios'
import { Message } from 'element-ui'
import util from '@/libs/util'
axios.interceptors.response.use(res => {
return res.data
}, err => {
return Promise.reject(err)
// 创建一个错误
function errorCreat (msg) {
const err = new Error(msg)
errorLog(err)
throw err
}
// 记录和显示错误
function errorLog (err) {
// 添加到日志
store.dispatch('d2admin/log/add', {
type: 'error',
err,
info: '数据请求异常'
})
// 打印到控制台
if (process.env.NODE_ENV === 'development') {
util.log.danger('>>>>>> Error >>>>>>')
console.log(err)
}
// 显示提示
Message({
message: err.message,
type: 'error',
duration: 5 * 1000
})
}
// 创建一个 axios 实例
const service = axios.create({
baseURL: process.env.VUE_APP_API,
timeout: 5000 // 请求超时时间
})
export default {
install (Vue, options) {
Vue.prototype.$axios = axios
// 请求拦截器
service.interceptors.request.use(
config => {
// 在请求发送之前做一些处理
if (!(/^https:\/\/|http:\/\//.test(config.url))) {
const token = util.cookies.get('token')
if (token && token !== 'undefined') {
// 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
config.headers['X-Token'] = token
}
}
return config
},
error => {
// 发送失败
console.log(error)
Promise.reject(error)
}
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
// dataAxios 是 axios 返回数据中的 data
const dataAxios = response.data
// 这个状态码是和后端约定的
const { code } = dataAxios
// 根据 code 进行判断
if (code === undefined) {
// 如果没有 code 代表这不是项目后端开发的接口 比如可能是 D2Admin 请求最新版本
return dataAxios
} else {
// 有 code 代表这是一个后端接口 可以进行进一步的判断
switch (code) {
case 0:
// [ 示例 ] code === 0 代表没有错误
return dataAxios.data
case 'xxx':
// [ 示例 ] 其它和后台约定的 code
errorCreat(`[ code: xxx ] ${dataAxios.msg}: ${response.config.url}`)
break
default:
// 不是正确的 code
errorCreat(`${dataAxios.msg}: ${response.config.url}`)
break
}
}
},
error => {
if (error && error.response) {
switch (error.response.status) {
case 400: error.message = '请求错误'; break
case 401: error.message = '未授权,请登录'; break
case 403: error.message = '拒绝访问'; break
case 404: error.message = `请求地址出错: ${error.response.config.url}`; break
case 408: error.message = '请求超时'; break
case 500: error.message = '服务器内部错误'; break
case 501: error.message = '服务未实现'; break
case 502: error.message = '网关错误'; break
case 503: error.message = '服务不可用'; break
case 504: error.message = '网关超时'; break
case 505: error.message = 'HTTP版本不受支持'; break
default: break
}
}
errorLog(error)
return Promise.reject(error)
}
)
export default service

View File

@@ -8,7 +8,6 @@ import '@/components'
// svg 图标
import '@/assets/svg-icons'
// 功能插件
import pluginAxios from '@/plugin/axios'
import pluginError from '@/plugin/error'
import pluginExport from '@/plugin/export'
import pluginImport from '@/plugin/import'
@@ -20,7 +19,6 @@ export default {
// Element
Vue.use(ElementUI)
// 插件
Vue.use(pluginAxios)
Vue.use(pluginError)
Vue.use(pluginExport)
Vue.use(pluginImport)

View File

@@ -1,4 +1,5 @@
import util from '@/libs/util.js'
import { AccountLogin } from '@/api/sys/login'
export default {
namespaced: true,
@@ -12,13 +13,9 @@ export default {
*/
login ({ commit }, { vm, username, password }) {
// 开始请求登录接口
vm.$axios({
method: 'post',
url: '/login',
data: {
username,
password
}
AccountLogin({
username,
password
})
.then(res => {
// 设置 cookie 一定要存 uuid 和 token 两个 cookie
@@ -26,11 +23,11 @@ export default {
// uuid 是用户身份唯一标识 用户注册的时候确定 并且不可改变 不可重复
// token 代表用户当前登录状态 建议在网络请求中携带 token
// 如有必要 token 需要定时更新,默认保存一天
util.cookies.set('uuid', res.data.uuid)
util.cookies.set('token', res.data.token)
util.cookies.set('uuid', res.uuid)
util.cookies.set('token', res.token)
// 设置 vuex 用户信息
commit('d2admin/user/set', {
name: res.data.name
name: res.name
}, { root: true })
// 用户登陆后从持久化数据加载一系列的设置
commit('load')

View File

@@ -1,4 +1,4 @@
import axios from 'axios'
import { httpGet } from '@/api/sys/http'
import semver from 'semver'
import util from '@/libs/util.js'
import setting from '@/setting.js'
@@ -19,7 +19,7 @@ export default {
* @param {Object} param context
*/
checkUpdate ({ state, commit }) {
axios.get(setting.releases.api)
httpGet(setting.releases.api)
.then(res => {
let versionGet = res.tag_name
const update = semver.lt(state.version, versionGet)