🔥【白哥开源社区】第二期完结篇
【白哥开源社区】第二期已经完结,持续时间为2023.06.13-2023.06.29
。
1. 项目地址
github:github.com/xiumubai/li…
gitee:gitee.com/xiumubai/li…
目前开发是在gitee上推送代码,会同步到github中。github国内访问太慢了(科学上网无济于事)。
2. 接口文档
2.1. 接口
apifox:5z6ejvyqg4.apifox.cn
本次的接口全部使用apifox进行云端Mock。接口共计73个。
2.2. 功能
本次功能涉及到以下功能模块:
3. 数据格式
3.1. 最外层数据格式
{
"code": 200,
"message": "成功",
"data": {
}
"ok": true
}
3.2. 分页数据格式
{
"code": 200,
"message": "成功",
"data": {
"list": [{}],
"total": 7,
"pageSize": 10,
"pageNum": 1,
}
"ok": true
}
4. Mock
我们之所以使用apifox的原因是功能强大,方便协作和管理,后期便于跟后端对接。
4.1. 基础mock
我们先来创建一个基础的Mock接口。
比如登录接口。
直接点击添加接口
即可。然后输入接口的配置:
- 请求方式:
post
- 请求路径:
/admin/acl/login
- 请求参数:
Body
- 返回响应
在响应的数据这里,我们需要把每个返回数据的字段填写上,这样返回的数据字段就会根据我们设置的字段返回。
最后点击保存,我们就成功了Mock了一个接口了。写完了这个接口,如何在我们的项目中测试呢?就需要我们的云端Mock功能了。
4.2. 云端Mock
云端Mock可以为我们提供一个云端服务器,就跟后端一样,会给我们返回Mock的数据。
如果所示:apifox会给我们提供一个云端Mock的url地址,这个就是接口网管地址,加上我们写的接口请求路径,就可以完成的请求一个接口了。
云端Mock未开启的请点击开关按钮开启。
开放访问:任何人都能访问。
Token鉴权:需要在接口地址中携带一个Token才能访问这个接口。
有个这个Mock的url以后,回到前我们创建的登录接口,切换右上角未云端Mock
即可:
然后我们点击发送,可以成功请求到Mock结果了。
4.3. 期望Mock
情景:当我们登录的时候,会涉及到不同的登录判断情况,比如密码错误了,账户名错误了等等。
基于以上情景,我们需要使用高级Mock中期望Mock功能。
点击高级Mock中的新建期望
然后新增一个期望
这里我们可以根据参数来做判断,当不满足或满足某种情况,返回某些特定结果。
如果你的返回情况判断比较多,你可以写多种:
最后记得把你的按钮打开。关闭则不生效。
4.4. 脚本Mock
情景:当我们Mock一个分页列表的时候,需要根据分页参数来返回数据,期望Mock的方式已经不能再满足了。这时候就需要使用脚本Mock的方式了。
在高级Mock选择脚本,开启Mock
注意:脚本Mock的优先级比期望Mock低,需要先把期望Mock关闭。
然后在自定义脚本
中输入代码:
var MockJs = require('mockjs');
var pageNum = Number(fox.mockRequest.getParam('pageNum'));
var pageSize = Number(fox.mockRequest.getParam('pageSize'));
// 生成中国手机号断前三位
function generateChinaMobilePrefix() {
const prefixes = [
'134', '135', '136', '137', '138', '139', '150', '151', '152', '157',
'158', '159', '165', '1703', '1705', '1706', '178', '182', '183', '184',
'187', '188', '195', '197', '198'
];
const randomIndex = Math.floor(Math.random() * prefixes.length);
return prefixes[randomIndex];
}
// 扩展手机号
MockJs.Random.extend({
phone: function () {
return generateChinaMobilePrefix() + MockJs.mock(/\d{8}/)
}
});
// 生成分页列表数据
function generatePageList(pageNum, pageSize, total) {
const results = [];
for (let i = 0; i < pageSize; i++) {
const id = (pageNum - 1) * pageSize + i + 1;
results.push({
id,
"username": MockJs.mock('@first'),
"nickName": MockJs.mock('@cname'),
"roleName": '系统管理员 游客',
"createTime": MockJs.mock('@datetime'),
"updateTime": MockJs.mock('@datetime'),
"phone": MockJs.mock("@phone")
});
}
return {
code: 200,
message: "成功",
data: {
list: results,
pageNum,
pageSize,
total,
},
ok: true
};
}
fox.mockResponse.setBody(generatePageList(pageNum, pageSize, 100))
fox.mockRequest.getParam()
会获取包括 Path 参数、Body 参数、Query 参数
fox.mockResponse.setBody()
会把最终生成的数据返回。
其他的逻辑就是需要自己去判断和添加了。跟我们的js一样的。
4.5. Mock优先级
在apixfox的Mock中,默认Mock<脚本Mock<期望Mock。
4.6. Mock语法扩展
有时候我们想使用Mock中不存在的语法,比如生成手机号@phone
,我们可以扩展:
var MockJs = require('mockjs');
// 扩展手机号
MockJs.Random.extend({
phone: function () {
var phonePrefixs = ['132', '135', '189']
return this.pick(phonePrefixs) + MockJs.mock(/\d{8}/)
}
});
// 使用
"phone": MockJs.mock("@phone")
4.7. Mock奇门遁甲
4.7.1. mock头像
"avatar": MockJs.mock(`@image('100x100', @color, @cname)`),
4.7.2. Mock枚举值
...MockJs.mock({"status|1": [0, 1]})
// 或者
status: Mockjs.mock('@integer(0, 1)')
5. 权限
5.1. 菜单权限
5.1.1. 添加mock数据
菜单权限的数据放在用户信息接口当中:/admin/acl/info
,数据接口如下:
{
"code": 200,
"message": "成功",
"data": {
"routes": [
"User",
"Acl",
"Role",
"Permission",
"UserManage",
"UserNormal",
"UserCreator",
"UserAnchor",
"UserManager",
"UserActor",
"MarketManager",
"AdvertisementSetting",
"AdvertisementSpaceSetting",
"VIPManager",
"ProxyManager",
"FreeVideoPermissionManager",
"GiftManager"
],
"buttons": [
"btn.User.assgin",
"btn.User.add",
"btn.User.remove",
"btn.User.update",
"btn.User.remove",
"btn.Role.add",
"btn.Role.assgin",
"btn.Role.update",
"btn.Role.remove"
],
"roles": [
"系统管理员",
"游客"
],
"name": "admin",
"avatar": "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif"
},
"ok": true
}
其中
routes
表示路由权限buttons
表示按钮权限
那么如果新增一个菜单权限,就需要在apifox中去修改routes
的数据:
按照上面的步骤,在高级Mock的期望中,点击详情
:
然后在routes
下面添加你需要的菜单权限名称。
注意:首字母全部大写,一级菜单和子菜单都需要提供对应的权限。
5.1.2. 添加路由菜单
接下来,我们回到代码中,找到route/dynamicRoutes.ts
文件,这里面是所有的权限路由菜单。
export const dynamicRoutes: RouteRecordRaw[] = [
// 权限管理
{
name: 'Acl',
path: '/acl',
component: Layout,
redirect: '/acl/user',
meta: {
title: '权限管理',
icon: 'Setting',
},
children: [
{
name: 'User',
path: '/acl/user',
component: () => import('@/views/acl/user/index.vue'),
meta: {
title: '用户管理',
icon: 'UserFilled',
},
},
{
name: 'Role',
path: '/acl/role',
component: () => import('@/views/acl/role/index.vue'),
meta: {
title: '角色管理',
icon: 'Avatar',
},
},
{
name: 'Permission',
path: '/acl/permission',
component: () => import('@/views/acl/permission/index.vue'),
meta: {
title: '菜单管理',
icon: 'Menu',
},
},
],
},
]
上面的示例中,没个路由都包含一个name
字段,这个值会跟接口中的routes
里面的值做匹配,如果匹配上,就会出现在左侧的菜单栏中。
5.1.3. 添加页面
最后,component
中是页面路径,我们需要在views
目录下添加对应的页面。
5.2. 按钮权限
5.2.1. 添加mock数据
在菜单权限中,我们已经知道了按钮的数据格式,是添加在buttons
中的。现在我们需要给页面中增删改按钮添加对应的权限,同样的,还是在apifox中相同的位置:
在buttons下面添加对应的按钮权限值,btn开头,中间是菜单权限值,结尾是操作值。
5.2.2. 添加按钮逻辑
添加完成以后,现在我们去页面控制按钮。我把按钮分了下面两种情况,我更偏向于第二种。
一、按钮无权限,隐藏按钮
第一种情况就是按钮没有权限,直接在页面中隐藏掉,不让用户看见。
我在代码中添加了三种控制按钮的方法:
- 指令方式
<el-button
type="primary"
icon="Plus"
v-auth="['btn.User.add']"
@click="openDrawer('新增')"
>
通过v-auth
传递一个按钮权限值。注意这里可以添加多个权限值。满足其中一个即可。
指令的底层代码在目录:src/directives/modules/auth.ts
中,感兴趣的可以自己去看看逻辑。
- 组件方式
<Auth :value="['btn.Role.add']">
<el-button type="primary" icon="Plus" @click="openDialog('新增')">
添加
</el-button>
</Auth>
这里我封装了一个Auth组件,传递一个按钮权限值,同样的可以添加多个权限值。
组件的底层代码在目录:src/components/Auth/src/Auth.tsx
中,感兴趣的可以自己去看看逻辑。
- hooks方式
<template>
<el-button
type="primary"
link
v-if="BUTTONS['btn.Permission.update']"
icon="Edit"
@click="openDialog(2, scope.row)"
>
编辑
</el-button>
</template>
<script setup lang="ts">
import { useAuthButtons } from '@/hooks/useAuthButtons'
const { BUTTONS } = useAuthButtons()
</script>
这里我封装了一个useAuthButtons
的hooks函数,然后通过v-if
的方式去控制按钮。同样的可以添加多个权限值。
hooks的底层代码在目录:src/hooks/useAuthButtons.ts
中,感兴趣的可以自己去看看逻辑。
二、按钮无权限,按钮展示,禁用按钮
为什么说我更偏向于这样的方式来控制按钮呢?如果在页面有某个操作不让用户操作,应该禁用掉,而不是直接隐藏掉。还有一个问题就是当某个按钮隐藏掉以后会影响布局。比如表格中的操作栏:
当然这种方式写起来更繁琐和复杂。
假设一个业务场景:我需要对新增按钮进行权限的控制,无权限的时候我可以让用户看到这个按钮,但是设置一个disabled
属性不让用户点击,代码如下:
<template #tableHeader>
<el-button
type="primary"
icon="Plus"
:disabled="!BUTTONS['btn.goodsInfo.add']"
@click="openDialog('新增')"
>
添加
</el-button>
</template>
这里使用了useAuthButtons
这个hooks函数来控制。
仅仅这样做还是不够的,如果会打开浏览器调试工具的,直接把这个disabled
干掉了不就行了?所以我们还需要在新增逻辑中控制一下:
<template #tableHeader>
<el-button
type="primary"
icon="Plus"
:disabled="!BUTTONS['btn.goodsInfo.add']"
@click="openDialog('新增')"
>
添加
</el-button>
</template>
<script setup lang="ts">
import { useAuth, hasAuth } from '@/hooks/useAuth'
import { useAuthButtons } from '@/hooks/useAuthButtons'
const { BUTTONS } = useAuthButtons()
// 点击新增按钮
const openDialog = async (
title: string,
rowData: Partial<GoodsInfo.ResGoodsInfoItem> = {},
) => {
// 检查是否有操作权限
await useAuth(hasAuth('btn.goodsInfo.add'))
// 剩余逻辑
...
}
</script>
这里我使用了两个hooks,hasAuth
先判断是否有权限,然后使用useAuth
来提示用户无权限。逻辑可以看这里:
// src/hooks/useAuth.ts
import { ElMessage } from 'element-plus'
import { useAuthStore } from '@/store/modules/auth'
// 无权限的时候提示
export const useAuth = (hasAuth: boolean) => {
return new Promise((resolve) => {
if (!hasAuth) {
ElMessage({
message: '你没有权限!!!',
type: 'warning',
})
} else {
resolve('success')
}
})
}
// 判断是否有权限
export const hasAuth = (value: string) => {
const authStore = useAuthStore()
const authButtons = authStore.authButtonList || []
return authButtons.includes(value)
}
之所以使用这么多方式去实现按钮权限的控制,是想让大家在项目中灵活运用,掌握各种招式,条条大路通罗马,掌握其中一个,也不错。
6. Git规范
6.1. commit规范
前缀 | 解释 | 示例 |
---|---|---|
feat | 新功能 | feat: 添加新功能 |
fix | 修复 | fix: 修改bug |
docs | 文档变更 | docs: 更新文档 |
style | 代码样式变更 | style: 修改样式 |
refactor | 重构 | refactor: 重构代码 |
perf | 性能优化 | perf: 优化了性能 |
test | 增加测试 | test: 单元测试 |
revert | 回退 | revert: 回退代码 |
build | 打包 | build: 打包代码 |
chore | 构建过程或辅助工具的变动 | chore: 修改构建| |
示例:
feat: 添加数据子典模块
6.2. 代码分支规范
分支管理 | 命名规范 | 解释 |
---|---|---|
master | master | 稳定版本分支,上线完成回归后后,由项目技术负责人从 release 分支合并进来,并打 tag |
feature/xiumubai | feature/功能名称比如你领取的任务是数据字典,分支就可以命名为:feature/dict,具体可以看看路由name的名字 | 新功能开发使用分支,基于master建立 |
bug | bug/功能名称示例:bug/style | 紧急线上bug修复使用分支,基于master建立 |
注意,在代码提交之前一定先pull主分支(master)的代码,然后merge到你的自己的分支,确保代码没有任何冲突,在提交到PR。
示例:
7. 开源贡献列表
codebo: gitee.com/li-haibo-19…
wy: gitee.com/wy0531
cyhcnn:gitee.com/cyhcnn
8. 最后
如果你也想参加开源项目,可以添加我的wx:xiumubai01
,我邀请你进【白哥开源社区】群。
转载自:https://juejin.cn/post/7250442153431400504