目录

开发规范

# 1. view 页面

@views (opens new window) 目录下,每个模块对应一个目录,它的所有功能的 .vue 都放在该目录里。

 目录

一般来说,一个路由对应一个 .vue 文件。

# 2. api 请求

@/api (opens new window) 目录下,每个模块对应一个 .api 文件。

 目录

每个 API 方法,会调用 request 方法,发起对后端 RESTful API 的调用。

# 2.1 请求封装

@/utils/request (opens new window) 基于 axios (opens new window) 封装,统一处理 GET、POST 方法的请求参数、请求头,以及错误提示信息等。

# 2.1.1 创建 axios 实例

  • baseURL 基础路径
  • timeout 超时时间
实现代码
1
2
3
4
5
6
7
8
9
import axios from 'axios'

// 创建 axios 实例
const service = axios.create({
// axios 中请求配置有 baseURL 选项,表示请求 URL 公共部分
baseURL: process.env.VUE_APP_BASE_API + '/admin-api/', // 此处的 /admin-api/ 地址,原因是后端的基础路径为 /admin-api/
// 超时
timeout: 10000
})

# 2.1.2 Request 拦截器

  • Authorizationtenant-id 请求头
  • GET 请求参数的拼接
实现代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { getToken } from '@/utils/auth'
import { getTenantEnable } from "@/utils/ruoyi";
import Cookies from "js-cookie";

service.interceptors.request.use(config => {
// 是否需要设置 token
const isToken = (config.headers || {}).isToken === false
if (getToken() && !isToken) {
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
}
// 设置租户
if (getTenantEnable()) {
const tenantId = Cookies.get('tenantId');
if (tenantId) {
config.headers['tenant-id'] = tenantId;
}
}
// get 请求映射 params 参数
if (config.method === 'get' && config.params) {
let url = config.url + '?';
for (const propName of Object.keys(config.params)) {
const value = config.params[propName];
var part = encodeURIComponent(propName) + "=";
if (value !== null && typeof(value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
let params = propName + '[' + key + ']';
var subPart = encodeURIComponent(params) + "=";
url += subPart + encodeURIComponent(value[key]) + "&";
}
} else {
url += part + encodeURIComponent(value) + "&";
}
}
}
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})

# 2.1.3 Response 拦截器

  • Token 失效、登录过期时,跳回首页
  • 请求失败,Message 错误提示
实现代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import { Notification, MessageBox, Message } from 'element-ui'
import store from '@/store'
import errorCode from '@/utils/errorCode'
import Cookies from "js-cookie";

export let isRelogin = { show: false };

service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
if (code === 401) {
if (!isRelogin.show) {
isRelogin.show = true;
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}
).then(() => {
isRelogin.show = false;
store.dispatch('LogOut').then(() => {
location.href = '/index';
})
}).catch(() => {
isRelogin.show = false;
});
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
Message({
message: msg,
type: 'error'
})
return Promise.reject(new Error(msg))
} else if (code !== 200) {
Notification.error({
title: msg
})
return Promise.reject('error')
} else { // 请求成功!
return res.data
}
},
error => {
console.log('err' + error)
let { message } = error;
if (message === "Network Error") {
message = "后端接口连接异常";
}
else if (message.includes("timeout")) {
message = "系统接口请求超时";
}
else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
Message({
message: message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)

# 2.2 交互流程

一个完整的前端 UI 交互到服务端处理流程,如下图所示:

交互流程

以 [系统管理 -> 用户管理] 菜单为例,查看它是如何读取用户列表的。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// ① api/system/user.js
import request from '@/utils/request'

// 查询用户列表
export function listUser(query) {
return request({
url: '/system/user/page',
method: 'get',
params: query
})
}

// ② views/system/user/index.vue
import { listUser } from "@/api/system/user";

export default {
data() {
userList: null,
loading: true
},
methods: {
getList() {
this.loading = true
listUser().then(response => {
this.userList = response.rows
this.loading = false
})
}
}
}

# 2.3 自定义 baseURL 基础路径

如果想要自定义的 baseURL 基础路径,可以通过 baseURL 进行直接覆盖。示例如下:

1
2
3
4
5
6
7
8
export function listUser(query) {
return request({
url: '/system/user/page',
method: 'get',
params: query,
baseURL: 'https://www.iocoder.cn' // 自定义
})
}

# 3. component 组件

① 在 @/components ( opens new window) 目录下,实现全局 组件,被所有模块所公用。例如说,富文本编辑器、各种各搜索组件、封装的分页组件等等。

全局组件

② 每个模块的业务组件,可实现在 views 目录下,自己模块的目录的 components 目录下,避免单个 .vue 文件过大,降低维护成功。例如说, @/views/pay/app/components/xxx.vue

业务组件

# 4. style 样式

① 在 @/styles ( opens new window) 目录下,实现全局 样式,被所有页面所公用。

全局样式

② 每个 .vue 页面,可在 <style /> 标签中添加样式,注意需要添加 scoped 表示只作用在当前页面里,避免造成全局的样式污染。

业务样式

上次更新: 2023/03/01, 00:29:43

本站由 钟意 使用 Stellar 1.28.1 主题创建。
又拍云 提供CDN加速/云存储服务
vercel 提供托管服务
湘ICP备2023019799号-1
总访问 次 | 本页访问