Appearance
代码重写
代码生成器生成的代码,只是通过提前预制的模板,参数一定的配置参数生成的。生成的代码只具备简单的=单表增删改查=能力。若想要实现复杂的业务逻辑,还需要开发人员对生成的代码进行二次开发。
下面介绍如何重写增删改查方法。
后端
由于后端的Controller类继承了父类或实现接口,所以生成出来的Controller类虽然没有任何方法,但也具备了简单的增删改查接口。 但父类的实现很简单往往不能满足实际的业务,可以通过重写父类方法或新写一个接口等方式实现更复杂的业务逻辑。
新增
Controller
实现了SaveController,可以重写
handlerSave
或save
方法。
- 不建议同时重写这2个方法。
- 若重写了
save
方法没有手动调用handlerSave
方法,handlerSave
将不会被调用
java
@Override
public R<TestUser> handlerSave(TestUserSaveVO model) {
// 可以编写在调用service.save前的代码,如验证参数
// 若需要父类帮你调用service.save,请返回 R.successDef();
return R.successDef();
}
@Override
public R<TestUser> save(TestUserSaveVO testUserSaveVO) {
// 自己验证参数
// 自己调用save方法
return R.success(superService.save(testUserSaveVO));
}
@Override
public R<TestUser> handlerSave(TestUserSaveVO model) {
// 可以编写在调用service.save前的代码,如验证参数
// 若需要父类帮你调用service.save,请返回 R.successDef();
return R.successDef();
}
@Override
public R<TestUser> save(TestUserSaveVO testUserSaveVO) {
// 自己验证参数
// 自己调用save方法
return R.success(superService.save(testUserSaveVO));
}
Service
继承了SuperServiceImpl,可以重写
save
、saveBefore
和saveAfter
方法。
- 若重写了
save
方法,就不建议同时重写saveBefore
和saveAfter
方法 - 若重写了
saveBefore
或saveAfter
方法,就不建议同时重写save
方法
java
@Override
@Transactional(rollbackFor = Exception.class)
public TestUser save(TestUserSaveVO testUserSaveVO) {
// 重写save方法后,父类的 save 方法将不会在执行。若你重写的方法中没有主动调用saveBefore、saveAfter方法,saveBefore、saveAfter方法也不会执行,所以不建议同时重写这3个方法。
// 当然若你重写的save方法中手动调用了saveBefore、saveAfter方法,则可以同时重写!
TestUser bean = BeanUtil.toBean(testUserSaveVO, TestUser.class);
superManager.save(bean);
return bean;
}
@Override
protected TestUser saveBefore(TestUserSaveVO testUserSaveVO) {
// 参数校验、参数初始化等逻辑
return BeanUtil.toBean(saveVO, getEntityClass());
}
@Override
protected void saveAfter(TestUserSaveVO testUserSaveVO, TestUser entity) {
// 保存数据后需要执行的一些操作,如清理缓存、报错从表数据等
}
@Override
@Transactional(rollbackFor = Exception.class)
public TestUser save(TestUserSaveVO testUserSaveVO) {
// 重写save方法后,父类的 save 方法将不会在执行。若你重写的方法中没有主动调用saveBefore、saveAfter方法,saveBefore、saveAfter方法也不会执行,所以不建议同时重写这3个方法。
// 当然若你重写的save方法中手动调用了saveBefore、saveAfter方法,则可以同时重写!
TestUser bean = BeanUtil.toBean(testUserSaveVO, TestUser.class);
superManager.save(bean);
return bean;
}
@Override
protected TestUser saveBefore(TestUserSaveVO testUserSaveVO) {
// 参数校验、参数初始化等逻辑
return BeanUtil.toBean(saveVO, getEntityClass());
}
@Override
protected void saveAfter(TestUserSaveVO testUserSaveVO, TestUser entity) {
// 保存数据后需要执行的一些操作,如清理缓存、报错从表数据等
}
Manager
继承了SuperManagerImpl,可以重写
save
方法
java
@Override
public boolean save(TestUser entity) {
// 在manager层重写save方法,除了入库操作,还可以考虑操作缓存、调用第三方接口等。
return baseMapper.insert(entity) > 0;
}
@Override
public boolean save(TestUser entity) {
// 在manager层重写save方法,除了入库操作,还可以考虑操作缓存、调用第三方接口等。
return baseMapper.insert(entity) > 0;
}
Mapper
mapper层的
insert
方法由mybaits-plus动态实现,若不能满足你的需求,建议自己写一个新方法。
修改
Controller
实现了UpdateController,可以重写
handlerSave
或save
方法。
- 不建议同时重写这2个方法。
- 若重写了
update
方法没有手动调用handlerUpdate
方法,handlerUpdate
将不会被调用
java
@Override
public R<TestUser> update(TestUserUpdateVO testUserUpdateVO) {
// 自己验证参数
// 自己调用updateById方法
return R.success(superService.updateById(testUserUpdateVO));
}
@Override
public R<TestUser> handlerUpdate(TestUserUpdateVO testUserUpdateVO) {
// 可以编写在调用service.update前的代码,如验证参数
// 若需要父类帮你调用service.update,请返回 R.successDef();
return R.successDef();
}
@Override
public R<TestUser> update(TestUserUpdateVO testUserUpdateVO) {
// 自己验证参数
// 自己调用updateById方法
return R.success(superService.updateById(testUserUpdateVO));
}
@Override
public R<TestUser> handlerUpdate(TestUserUpdateVO testUserUpdateVO) {
// 可以编写在调用service.update前的代码,如验证参数
// 若需要父类帮你调用service.update,请返回 R.successDef();
return R.successDef();
}
Service
继承了SuperServiceImpl,可以重写
updateById
、saveBefore
和saveAfter
方法。
- 若重写了
updateById
方法,就不建议同时重写saveBefore
和saveAfter
方法 - 若重写了
saveBefore
或saveAfter
方法,就不建议同时重写updateById
方法
java
@Override
@Transactional(rollbackFor = Exception.class)
public TestUser updateById(TestUserUpdateVO testUserUpdateVO) {
/* 重写 updateById 方法后,父类的 updateById 方法将不会在执行。若你重写的方法中没有主动调用updateBefore、updateAfter方法,updateBefore、updateAfter方法不会执行,所以不建议同时重写这3个方法。
当然若你重写的 updateById 方法中手动调用了 updateBefore、updateAfter 方法,则可以同时重写!*/
TestUser bean = BeanUtil.toBean(testUserUpdateVO, TestUser.class);
superManager.updateById(bean);
return bean;
}
@Override
protected TestUser updateBefore(TestUserUpdateVO testUserUpdateVO) {
// 参数校验、参数初始化等逻辑
return BeanUtil.toBean(testUserUpdateVO, getEntityClass());
}
@Override
protected void updateAfter(TestUserUpdateVO testUserUpdateVO, TestUser entity) {
// 保存数据后需要执行的一些操作,如清理缓存、报错从表数据等
}
@Override
@Transactional(rollbackFor = Exception.class)
public TestUser updateById(TestUserUpdateVO testUserUpdateVO) {
/* 重写 updateById 方法后,父类的 updateById 方法将不会在执行。若你重写的方法中没有主动调用updateBefore、updateAfter方法,updateBefore、updateAfter方法不会执行,所以不建议同时重写这3个方法。
当然若你重写的 updateById 方法中手动调用了 updateBefore、updateAfter 方法,则可以同时重写!*/
TestUser bean = BeanUtil.toBean(testUserUpdateVO, TestUser.class);
superManager.updateById(bean);
return bean;
}
@Override
protected TestUser updateBefore(TestUserUpdateVO testUserUpdateVO) {
// 参数校验、参数初始化等逻辑
return BeanUtil.toBean(testUserUpdateVO, getEntityClass());
}
@Override
protected void updateAfter(TestUserUpdateVO testUserUpdateVO, TestUser entity) {
// 保存数据后需要执行的一些操作,如清理缓存、报错从表数据等
}
Manager
继承了SuperManagerImpl、SuperCacheManagerImpl,可以重写
updateById
方法。 继承了SuperCacheManagerImpl,记得自行清理缓存。
java
@Override
public boolean updateById(TestUser entity) {
// 在manager层重写save方法,除了入库操作,还可以考虑操作缓存、调用第三方接口等。
return SqlHelper.retBool(this.getBaseMapper().updateById(entity));
}
@Override
public boolean updateById(TestUser entity) {
// 在manager层重写save方法,除了入库操作,还可以考虑操作缓存、调用第三方接口等。
return SqlHelper.retBool(this.getBaseMapper().updateById(entity));
}
Mapper
mapper层的
updateById
方法由mybaits-plus动态实现,若不能满足你的需求,建议自己写一个新方法。
复制
Controller
实现了 SaveController
,可以重写 copy
方法。
java
@Override
public R<TestUser> copy(Long id) {
return R.success(superService.copy(id));
}
@Override
public R<TestUser> copy(Long id) {
return R.success(superService.copy(id));
}
Service
java
@Override
@Transactional(rollbackFor = Exception.class)
public TestUser copy(Long id) {
TestUser old = getById(id);
ArgumentAssert.notNull(old, "您要复制的数据不存在或已被删除,请刷新重试");
TestUser entity = BeanPlusUtil.toBean(old, TestUser.class);
entity.setId(null);
superManager.save(entity);
return entity;
}
@Override
@Transactional(rollbackFor = Exception.class)
public TestUser copy(Long id) {
TestUser old = getById(id);
ArgumentAssert.notNull(old, "您要复制的数据不存在或已被删除,请刷新重试");
TestUser entity = BeanPlusUtil.toBean(old, TestUser.class);
entity.setId(null);
superManager.save(entity);
return entity;
}
删除
Controller
实现了
DeleteController
,可以重写handlerDelete
或delete
方法。
- 不建议同时重写这2个方法。
- 若重写了
delete
方法没有手动调用handlerDelete
方法,handlerDelete
将不会被调用
java
@Override
public R<Boolean> delete(List<Long> ids) {
return R.success(getSuperService().removeByIds(ids));
}
@Override
public R<Boolean> handlerDelete(List<Long> ids) {
// 删除前的操作,如验证参数
return R.successDef();
}
@Override
public R<Boolean> delete(List<Long> ids) {
return R.success(getSuperService().removeByIds(ids));
}
@Override
public R<Boolean> handlerDelete(List<Long> ids) {
// 删除前的操作,如验证参数
return R.successDef();
}
Service
java
@Override
@Transactional(rollbackFor = Exception.class)
public boolean removeByIds(Collection<Long> idList) {
// 可以执行其他操作,如删除从表等
return superManager.removeByIds(idList);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean removeByIds(Collection<Long> idList) {
// 可以执行其他操作,如删除从表等
return superManager.removeByIds(idList);
}
Manager
java
@Override
public boolean removeByIds(Collection<?> list) {
return baseMapper.deleteBatchIds(list) > 0;
}
@Override
public boolean removeByIds(Collection<?> list) {
return baseMapper.deleteBatchIds(list) > 0;
}
查询
Controller
- get:单体查询 父类SuperCacheController的
get
方法是查询缓存 - getDetail:单体查询
getDetail
和get
的区别在于getDetail
会执行回显操作 - query:列表查询
java
@Override
public R<TestUserResultVO> get(Long id) {
TestUser entity = getSuperService().getById(id);
return success(BeanPlusUtil.toBean(entity, TestUserResultVO.class));
}
@Override
public R<TestUserResultVO> getDetail(Long id) {
TestUser entity = getSuperService().getById(id);
TestUserResultVO resultVO = BeanPlusUtil.toBean(entity, TestUserResultVO.class);
EchoService echoService = getEchoService();
if (echoService != null) {
echoService.action(resultVO);
}
return success(resultVO);
}
@Override
public R<List<TestUserResultVO>> query(TestUserPageQuery data) {
TestUser entity = BeanPlusUtil.toBean(data, TestUser.class);
QueryWrap<TestUser> wrapper = Wraps.q(entity);
List<TestUser> list = getSuperService().list(wrapper);
return success(BeanPlusUtil.toBeanList(list, getResultVOClass()));
}
@Override
public R<TestUserResultVO> get(Long id) {
TestUser entity = getSuperService().getById(id);
return success(BeanPlusUtil.toBean(entity, TestUserResultVO.class));
}
@Override
public R<TestUserResultVO> getDetail(Long id) {
TestUser entity = getSuperService().getById(id);
TestUserResultVO resultVO = BeanPlusUtil.toBean(entity, TestUserResultVO.class);
EchoService echoService = getEchoService();
if (echoService != null) {
echoService.action(resultVO);
}
return success(resultVO);
}
@Override
public R<List<TestUserResultVO>> query(TestUserPageQuery data) {
TestUser entity = BeanPlusUtil.toBean(data, TestUser.class);
QueryWrap<TestUser> wrapper = Wraps.q(entity);
List<TestUser> list = getSuperService().list(wrapper);
return success(BeanPlusUtil.toBeanList(list, getResultVOClass()));
}
分页查询
controller
若实现了PageController接口,可以重写
page
、query
、handlerQueryParams
、handlerWrapper
、handlerResult
- 若重写了
page
,就不建议重写其他方法 - 若重写了
query
,就不建议重写handlerQueryParams
、handlerWrapper
前端
默认情况下,会将表中除了创建人、修改人等字段外的所有字段均展示在前端页面,但复杂的业务,往往不需要讲所有字段都展示在页面上。这时就需要开发人员手动调整生成的代码,将多余的字段删除或隐藏。
字典选择
想要在表单的某个字段选择字典数据,可以在component属性指定配置
ApiSelect
和ApiRadioGroup
组件,这2个组件仅在UI表现形式上不同。同时需要在componentProps属性指定dictComponentProps
方法。
提示
- 字典类型 如:性别、民族
- 字典条目 如:男、女、汉族、苗族
- 扩展条目 如:后台的数据字典=性别=只配置了 男、女 2个条目,但在某些业务下,需要选择=全部、男、女=等选项,可以通过
dictComponentProps
方法的extend
和exetndFirst
参数配置。
tsx
export const editFormSchema = (_type: Ref<ActionEnum>): FormSchema[] => {
return [
{
label: t('ddd.user.testUser.education'),
field: 'education',
component: 'ApiRadioGroup',
componentProps: {
// 建议将魔法数参数移动到 DictEnum 中,并添加为: EchoDictType_Global_EDUCATION = 'EDUCATION';
// 'EDUCATION' 需要与 后端DictType类中的参数 以及 def_dict表中的key字段 保持一致,否则无法回显!
// ...dictComponentProps(DictEnum.EchoDictType_Global_EDUCATION),
...dictComponentProps('EDUCATION'),
},
},
]
}
export const editFormSchema = (_type: Ref<ActionEnum>): FormSchema[] => {
return [
{
label: t('ddd.user.testUser.education'),
field: 'education',
component: 'ApiRadioGroup',
componentProps: {
// 建议将魔法数参数移动到 DictEnum 中,并添加为: EchoDictType_Global_EDUCATION = 'EDUCATION';
// 'EDUCATION' 需要与 后端DictType类中的参数 以及 def_dict表中的key字段 保持一致,否则无法回显!
// ...dictComponentProps(DictEnum.EchoDictType_Global_EDUCATION),
...dictComponentProps('EDUCATION'),
},
},
]
}
tsx
/**
* @param type 字典类型
* @param extendFirst 扩展条目放在第一位还是最后一位
* @param extend 扩展条目
* @param excludes 需要排除的字典条目
*/
export const dictComponentProps = (
type: DictEnum | string,
extendFirst = true,
extend?: any,
excludes?: string | string[],
) => {
if (excludes && isString(excludes)) {
excludes = [excludes];
}
return {
api: asyncFindDictList,
params: { type, extend, extendFirst, excludes },
resultField: 'data',
showSearch: true,
filterOption: (input: string, option: any) => {
return option.label.toUpperCase().indexOf(input.toUpperCase()) >= 0;
},
};
};
/**
* @param type 字典类型
* @param extendFirst 扩展条目放在第一位还是最后一位
* @param extend 扩展条目
* @param excludes 需要排除的字典条目
*/
export const dictComponentProps = (
type: DictEnum | string,
extendFirst = true,
extend?: any,
excludes?: string | string[],
) => {
if (excludes && isString(excludes)) {
excludes = [excludes];
}
return {
api: asyncFindDictList,
params: { type, extend, extendFirst, excludes },
resultField: 'data',
showSearch: true,
filterOption: (input: string, option: any) => {
return option.label.toUpperCase().indexOf(input.toUpperCase()) >= 0;
},
};
};
枚举选择
想要在表单的某个字段选择后台定义的枚举类型数据,可以在component属性指定配置
ApiSelect
和ApiRadioGroup
组件,这2个组件仅在UI表现形式上不同。同时需要在componentProps属性指定enumComponentProps
方法。
注意
假如你想查询到 order服务 的枚举类 OrderStatusEnum,OrderStatusEnum类存放在 acuity-order-entity
模块,你必须在 acuity-oauth-controler/pom.xml
文件中加入 acuity-order-entity
的依赖。
xml
<dependency>
<groupId>top.acuity.box</groupId>
<artifactId>acuity-order-entity</artifactId>
<version>${acuity-project.version}</version>
</dependency>
<dependency>
<groupId>top.acuity.box</groupId>
<artifactId>acuity-order-entity</artifactId>
<version>${acuity-project.version}</version>
</dependency>
tsx
export const editFormSchema = (_type: Ref<ActionEnum>): FormSchema[] => {
return [
{
label: t('basic.system.baseFile.storageType'),
field: 'storageType',
component: 'ApiSelect',
componentProps: {
...enumComponentProps('FileStorageType'),
},
},
]
}
export const editFormSchema = (_type: Ref<ActionEnum>): FormSchema[] => {
return [
{
label: t('basic.system.baseFile.storageType'),
field: 'storageType',
component: 'ApiSelect',
componentProps: {
...enumComponentProps('FileStorageType'),
},
},
]
}
tsx
/**
* @param type 字典类型
* @param extendFirst 扩展条目放在第一位还是最后一位
* @param extend 扩展条目
* @param excludes 需要排除的字典条目
*/
export const enumComponentProps = (
type: EnumEnum | string,
extendFirst = true,
extend?: any,
excludes?: string | string[],
) => {
if (excludes && isString(excludes)) {
excludes = [excludes];
}
return {
api: asyncFindEnumList,
params: { type, extendFirst, extend, excludes },
resultField: 'data',
showSearch: true,
filterOption: (input: string, option: any) => {
return option.label.toUpperCase().indexOf(input.toUpperCase()) >= 0;
},
};
};
/**
* @param type 字典类型
* @param extendFirst 扩展条目放在第一位还是最后一位
* @param extend 扩展条目
* @param excludes 需要排除的字典条目
*/
export const enumComponentProps = (
type: EnumEnum | string,
extendFirst = true,
extend?: any,
excludes?: string | string[],
) => {
if (excludes && isString(excludes)) {
excludes = [excludes];
}
return {
api: asyncFindEnumList,
params: { type, extendFirst, extend, excludes },
resultField: 'data',
showSearch: true,
filterOption: (input: string, option: any) => {
return option.label.toUpperCase().indexOf(input.toUpperCase()) >= 0;
},
};
};
表格回显
数据想要回显,后端接口一定得调用
echoService.action(page);
方法。
tsx
export const columns = (): BasicColumn[] => {
return [
{
title: t('ddd.user.testUser.sex'),
dataIndex: ['echoMap', 'sex'],
key: 'sex',
},
]
}
export const columns = (): BasicColumn[] => {
return [
{
title: t('ddd.user.testUser.sex'),
dataIndex: ['echoMap', 'sex'],
key: 'sex',
},
]
}
java
@Override
@WebLog(value = "'分页列表查询:第' + #params?.current + '页, 显示' + #params?.size + '行'", response = false)
public R<IPage<TestUserResultVO>> page(@RequestBody @Validated PageParams<TestUserPageQuery> params) {
IPage<TestUserResultVO> page = superService.findPageResultVO(params);
echoService.action(page);
return R.success(page);
}
@Override
@WebLog(value = "'分页列表查询:第' + #params?.current + '页, 显示' + #params?.size + '行'", response = false)
public R<IPage<TestUserResultVO>> page(@RequestBody @Validated PageParams<TestUserPageQuery> params) {
IPage<TestUserResultVO> page = superService.findPageResultVO(params);
echoService.action(page);
return R.success(page);
}
按钮权限
按钮详情
下图展现了系统页面常用的几种按钮,都可以通过权限控制来使不同用户看不同的按钮。
按钮级权限的控制方式有几种:
权限组件
自定义指令
函数方式过滤
属性
权限组件
vue
<template>
<div>
<Authority :value="RoleEnum.SUPER">
<a-button class="mx-4" type="primary"> 拥有super权限可见 </a-button>
</Authority>
<Authority :value="[RoleEnum.TEST, RoleEnum.SUPER]">
<a-button class="mx-4" color="error"> 同时拥有[test,super]权限可见 </a-button>
</Authority>
<Authority :value="[RoleEnum.TEST, RoleEnum.SUPER]" :mode="PermModeEnum.HasAny">
<a-button class="mx-4" color="success"> 拥有任意一个[test,super]权限可见 </a-button>
</Authority>
<Authority :value="[RoleEnum.TEST, RoleEnum.SUPER]" :mode="PermModeEnum.Without">
<a-button class="mx-4" color="success"> 没有所有[test,super]权限可见 </a-button>
</Authority>
<Authority :value="[RoleEnum.TEST, RoleEnum.SUPER]" :mode="PermModeEnum.WithoutAny">
<a-button class="mx-4" color="success"> 没有任意一个[test,super]权限可见 </a-button>
</Authority>
</div>
</template>
<script>
import { Authority } from '/@/components/Authority';
import { defineComponent } from 'vue';
export default defineComponent({
components: { Authority },
});
</script>
<template>
<div>
<Authority :value="RoleEnum.SUPER">
<a-button class="mx-4" type="primary"> 拥有super权限可见 </a-button>
</Authority>
<Authority :value="[RoleEnum.TEST, RoleEnum.SUPER]">
<a-button class="mx-4" color="error"> 同时拥有[test,super]权限可见 </a-button>
</Authority>
<Authority :value="[RoleEnum.TEST, RoleEnum.SUPER]" :mode="PermModeEnum.HasAny">
<a-button class="mx-4" color="success"> 拥有任意一个[test,super]权限可见 </a-button>
</Authority>
<Authority :value="[RoleEnum.TEST, RoleEnum.SUPER]" :mode="PermModeEnum.Without">
<a-button class="mx-4" color="success"> 没有所有[test,super]权限可见 </a-button>
</Authority>
<Authority :value="[RoleEnum.TEST, RoleEnum.SUPER]" :mode="PermModeEnum.WithoutAny">
<a-button class="mx-4" color="success"> 没有任意一个[test,super]权限可见 </a-button>
</Authority>
</div>
</template>
<script>
import { Authority } from '/@/components/Authority';
import { defineComponent } from 'vue';
export default defineComponent({
components: { Authority },
});
</script>
- 自定义指令
指令集
- withoutPermission没有所有权限时渲染该组件
- withoutAnyPermission没有任意一个权限时渲染该组件
- hasPermission拥有所有权限时渲染该组件
- hasAnyPermission拥有任意一个权限时渲染该组件
vue
<template>
<div>
<a-button v-hasPermission="[RoleEnum.TEST, RoleEnum.SUPER]" class="mx-4" type="primary">
同时拥有[test,super]权限可见
</a-button>
<a-button v-hasAnyPermission="[RoleEnum.TEST, RoleEnum.SUPER]" class="mx-4" color="error">
拥有任意一个[test,super]权限可见
</a-button>
<a-button v-withoutPermission="[RoleEnum.TEST, RoleEnum.SUPER]" class="mx-4" color="success">
没有所有[test,super]权限可见
</a-button>
<a-button v-withoutAnyPermission="[RoleEnum.TEST, RoleEnum.SUPER]" class="mx-4" color="error">
没有任意一个[test,super]权限可见
</a-button>
</div>
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({
components: { },
});
</script>
<template>
<div>
<a-button v-hasPermission="[RoleEnum.TEST, RoleEnum.SUPER]" class="mx-4" type="primary">
同时拥有[test,super]权限可见
</a-button>
<a-button v-hasAnyPermission="[RoleEnum.TEST, RoleEnum.SUPER]" class="mx-4" color="error">
拥有任意一个[test,super]权限可见
</a-button>
<a-button v-withoutPermission="[RoleEnum.TEST, RoleEnum.SUPER]" class="mx-4" color="success">
没有所有[test,super]权限可见
</a-button>
<a-button v-withoutAnyPermission="[RoleEnum.TEST, RoleEnum.SUPER]" class="mx-4" color="error">
没有任意一个[test,super]权限可见
</a-button>
</div>
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({
components: { },
});
</script>
- 函数方式过滤
在能调用isPermission函数的地方,直接调用isPermission函数控制按钮权限
vue
<template>
<div>
<a-button v-if="isPermission([RoleEnum.TEST, RoleEnum.SUPER], PermModeEnum.Has)" class="mx-4" type="primary">
同时拥有[test,super]权限可见
</a-button>
<a-button v-if="isPermission([RoleEnum.TEST, RoleEnum.SUPER], PermModeEnum.HasAny)" class="mx-4" color="error">
拥有任意一个[test,super]权限可见
</a-button>
<a-button v-if="isPermission([RoleEnum.TEST, RoleEnum.SUPER], PermModeEnum.Without)" class="mx-4" color="success">
没有所有[test,super]权限可见
</a-button>
<a-button v-if="isPermission([RoleEnum.TEST, RoleEnum.SUPER], PermModeEnum.WithoutAny)" class="mx-4" color="error">
没有任意一个[test,super]权限可见
</a-button>
</div>
</template>
<script>
import { defineComponent } from 'vue';
import { usePermission } from '/@/hooks/web/usePermission';
import { RoleEnum, PermModeEnum } from '/@/enums/roleEnum';
export default defineComponent({
setup() {
const { isPermission } = usePermission();
return { isPermission, RoleEnum, PermModeEnum };
}
});
</script>
<template>
<div>
<a-button v-if="isPermission([RoleEnum.TEST, RoleEnum.SUPER], PermModeEnum.Has)" class="mx-4" type="primary">
同时拥有[test,super]权限可见
</a-button>
<a-button v-if="isPermission([RoleEnum.TEST, RoleEnum.SUPER], PermModeEnum.HasAny)" class="mx-4" color="error">
拥有任意一个[test,super]权限可见
</a-button>
<a-button v-if="isPermission([RoleEnum.TEST, RoleEnum.SUPER], PermModeEnum.Without)" class="mx-4" color="success">
没有所有[test,super]权限可见
</a-button>
<a-button v-if="isPermission([RoleEnum.TEST, RoleEnum.SUPER], PermModeEnum.WithoutAny)" class="mx-4" color="error">
没有任意一个[test,super]权限可见
</a-button>
</div>
</template>
<script>
import { defineComponent } from 'vue';
import { usePermission } from '/@/hooks/web/usePermission';
import { RoleEnum, PermModeEnum } from '/@/enums/roleEnum';
export default defineComponent({
setup() {
const { isPermission } = usePermission();
return { isPermission, RoleEnum, PermModeEnum };
}
});
</script>
- 属性 BasieTable操作栏、BasicTree操作栏、BasicTree右键菜单等可以通过auth属性来控制。
vue
<template>
<div class="p-4">
<BasicTable @register="registerTable">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: [RoleEnum.TEST, RoleEnum.SUPER], //同时拥有[test,super]权限可见
mode: PermModeEnum.Has
},
{
label: '删除',
icon: 'ic:outline-delete-outline',
onClick: handleDelete.bind(null, record),
auth: [RoleEnum.TEST, RoleEnum.SUPER], // 拥有任意一个[test,super]权限可见
mode: PermModeEnum.HasAny
},
]"
:dropDownActions="[
{
label: '禁用',
popConfirm: {
title: '是否禁用?',
confirm: handleOpen.bind(null, record),
},
auth: [RoleEnum.TEST, RoleEnum.SUPER], // 没有所有[test,super]权限可见
mode: PermModeEnum.Without
},
{
label: '同时控制',
popConfirm: {
title: '是否动态显示?',
confirm: handleOpen.bind(null, record),
},
auth: [RoleEnum.TEST, RoleEnum.SUPER], // 没有任意一个[test,super]权限可见
mode: PermModeEnum.WithoutAny
},
]"
/>
</template>
</template>
</BasicTable>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable, BasicColumn, TableAction } from '/@/components/Table';
import { RoleEnum, PermModeEnum } from '/@/enums/roleEnum';
import { demoListApi } from '/@/api/demo/table';
const columns: BasicColumn[] = [
{
title: '编号',
dataIndex: 'no',
width: 100,
},
{
title: '姓名',
dataIndex: 'name',
width: 200,
},
];
export default defineComponent({
components: { BasicTable, TableAction },
setup() {
const [registerTable] = useTable({
title: 'TableAction组件及固定列示例',
api: demoListApi,
columns: columns,
bordered: true,
rowKey: 'id',
rowSelection: {
type: 'checkbox',
},
actionColumn: {
width: 250,
title: 'Action',
dataIndex: 'action',
},
});
function handleEdit(record: Recordable) {
console.log('点击了编辑', record);
}
function handleDelete(record: Recordable) {
console.log('点击了删除', record);
}
function handleOpen(record: Recordable) {
console.log('点击了启用', record);
}
return {
registerTable,
handleEdit,
handleDelete,
handleOpen,
RoleEnum,
PermModeEnum,
};
},
});
</script>
<template>
<div class="p-4">
<BasicTable @register="registerTable">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<TableAction
:actions="[
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: [RoleEnum.TEST, RoleEnum.SUPER], //同时拥有[test,super]权限可见
mode: PermModeEnum.Has
},
{
label: '删除',
icon: 'ic:outline-delete-outline',
onClick: handleDelete.bind(null, record),
auth: [RoleEnum.TEST, RoleEnum.SUPER], // 拥有任意一个[test,super]权限可见
mode: PermModeEnum.HasAny
},
]"
:dropDownActions="[
{
label: '禁用',
popConfirm: {
title: '是否禁用?',
confirm: handleOpen.bind(null, record),
},
auth: [RoleEnum.TEST, RoleEnum.SUPER], // 没有所有[test,super]权限可见
mode: PermModeEnum.Without
},
{
label: '同时控制',
popConfirm: {
title: '是否动态显示?',
confirm: handleOpen.bind(null, record),
},
auth: [RoleEnum.TEST, RoleEnum.SUPER], // 没有任意一个[test,super]权限可见
mode: PermModeEnum.WithoutAny
},
]"
/>
</template>
</template>
</BasicTable>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable, BasicColumn, TableAction } from '/@/components/Table';
import { RoleEnum, PermModeEnum } from '/@/enums/roleEnum';
import { demoListApi } from '/@/api/demo/table';
const columns: BasicColumn[] = [
{
title: '编号',
dataIndex: 'no',
width: 100,
},
{
title: '姓名',
dataIndex: 'name',
width: 200,
},
];
export default defineComponent({
components: { BasicTable, TableAction },
setup() {
const [registerTable] = useTable({
title: 'TableAction组件及固定列示例',
api: demoListApi,
columns: columns,
bordered: true,
rowKey: 'id',
rowSelection: {
type: 'checkbox',
},
actionColumn: {
width: 250,
title: 'Action',
dataIndex: 'action',
},
});
function handleEdit(record: Recordable) {
console.log('点击了编辑', record);
}
function handleDelete(record: Recordable) {
console.log('点击了删除', record);
}
function handleOpen(record: Recordable) {
console.log('点击了启用', record);
}
return {
registerTable,
handleEdit,
handleDelete,
handleOpen,
RoleEnum,
PermModeEnum,
};
},
});
</script>
vue
<template>
<BasicTree
ref="treeRef"
:actionList="actionList"
:beforeRightClick="getRightMenuList"
:title="t('basic.user.baseOrg.table.title')"
:treeData="treeData"
>
</BasicTree>
</template>
<script lang="ts">
import { defineComponent, h, ref, unref } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
import {
BasicTree,
ContextMenuItem,
TreeActionItem,
TreeActionType,
TreeItem,
} from '/@/components/Tree';
import { eachTree, findChildrenByParentId, findNodeByKey } from '/@/utils/helper/treeHelper';
import { RoleEnum } from '/@/enums/roleEnum';
import { OrgTypeEnum } from '/@/enums/biz/base';
import { tree } from '/@/api/basic/user/baseOrg';
export default defineComponent({
name: 'BaseOrgManagement',
components: { BasicTree },
emits: ['select', 'add', 'edit', 'change', 'reset'],
setup(props, { emit }) {
const { t } = useI18n();
const { createMessage, createConfirm } = useMessage();
const treeRef = ref<Nullable<TreeActionType>>(null);
const treeData = ref<TreeItem[]>([]);
// 绑定角色
const [registerModal, { openModal }] = useModal();
function getTree() {
const tree = unref(treeRef);
if (!tree) {
throw new Error('树结构加载失败,请刷新页面');
}
return tree;
}
// 加载数据
async function fetch() {
treeData.value = (await tree()) as unknown as TreeItem[];
eachTree(treeData.value, (item) => {
item.key = item.id;
item.title = item.name;
item.slots = { titleBefore: 'titleBefore' };
return item;
});
setTimeout(() => {
getTree().filterByLevel(2);
}, 0);
}
let actionList: TreeActionItem[] = [];
let getRightMenuList = (_: any): ContextMenuItem[] => {
return [];
};
// 悬停图标
actionList = [
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.Has,
render: (node) => {
return h(
'a', {}, t('common.title.add'),
);
},
},
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.HasAny,
render: (node) => {
return h(
'a',{}, t('common.title.edit'),
);
},
},
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.Without,
render: (node) => {
return h(
'a', {}, t('common.title.delete'),
);
},
},
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.WithoutAny,
render: (node) => {
return h(
'a', {}, t('common.title.delete'),
);
},
},
];
// 右键菜单
getRightMenuList = (node: any): ContextMenuItem[] => {
return [
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.Has,
label: t('common.title.addChildren'),
icon: 'ant-design:plus-outlined',
},
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.HasAny,
label: t('common.title.edit'),
icon: 'ant-design:edit-outlined',
},
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.Without,
label: t('common.title.delete'),
icon: 'ant-design:delete-outlined',
},
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.WithoutAny,
label: t('common.title.delete'),
icon: 'ant-design:delete-outlined',
},
];
};
return {
t,
treeRef,
treeData,
getRightMenuList,
actionList,
};
},
});
</script>
<template>
<BasicTree
ref="treeRef"
:actionList="actionList"
:beforeRightClick="getRightMenuList"
:title="t('basic.user.baseOrg.table.title')"
:treeData="treeData"
>
</BasicTree>
</template>
<script lang="ts">
import { defineComponent, h, ref, unref } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
import {
BasicTree,
ContextMenuItem,
TreeActionItem,
TreeActionType,
TreeItem,
} from '/@/components/Tree';
import { eachTree, findChildrenByParentId, findNodeByKey } from '/@/utils/helper/treeHelper';
import { RoleEnum } from '/@/enums/roleEnum';
import { OrgTypeEnum } from '/@/enums/biz/base';
import { tree } from '/@/api/basic/user/baseOrg';
export default defineComponent({
name: 'BaseOrgManagement',
components: { BasicTree },
emits: ['select', 'add', 'edit', 'change', 'reset'],
setup(props, { emit }) {
const { t } = useI18n();
const { createMessage, createConfirm } = useMessage();
const treeRef = ref<Nullable<TreeActionType>>(null);
const treeData = ref<TreeItem[]>([]);
// 绑定角色
const [registerModal, { openModal }] = useModal();
function getTree() {
const tree = unref(treeRef);
if (!tree) {
throw new Error('树结构加载失败,请刷新页面');
}
return tree;
}
// 加载数据
async function fetch() {
treeData.value = (await tree()) as unknown as TreeItem[];
eachTree(treeData.value, (item) => {
item.key = item.id;
item.title = item.name;
item.slots = { titleBefore: 'titleBefore' };
return item;
});
setTimeout(() => {
getTree().filterByLevel(2);
}, 0);
}
let actionList: TreeActionItem[] = [];
let getRightMenuList = (_: any): ContextMenuItem[] => {
return [];
};
// 悬停图标
actionList = [
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.Has,
render: (node) => {
return h(
'a', {}, t('common.title.add'),
);
},
},
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.HasAny,
render: (node) => {
return h(
'a',{}, t('common.title.edit'),
);
},
},
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.Without,
render: (node) => {
return h(
'a', {}, t('common.title.delete'),
);
},
},
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.WithoutAny,
render: (node) => {
return h(
'a', {}, t('common.title.delete'),
);
},
},
];
// 右键菜单
getRightMenuList = (node: any): ContextMenuItem[] => {
return [
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.Has,
label: t('common.title.addChildren'),
icon: 'ant-design:plus-outlined',
},
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.HasAny,
label: t('common.title.edit'),
icon: 'ant-design:edit-outlined',
},
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.Without,
label: t('common.title.delete'),
icon: 'ant-design:delete-outlined',
},
{
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.WithoutAny,
label: t('common.title.delete'),
icon: 'ant-design:delete-outlined',
},
];
};
return {
t,
treeRef,
treeData,
getRightMenuList,
actionList,
};
},
});
</script>
- 属性
vue
export interface ActionItem extends ButtonProps {
// 权限编码
auth?: RoleEnum | RoleEnum[] | string | string[];
// 权限模式。Has-拥有所有,HasAny-拥有任意一个,Without-没有所有,WithoutAny-没有任意一个
authMode?: PermModeEnum;
}
export interface ActionItem extends ButtonProps {
// 权限编码
auth?: RoleEnum | RoleEnum[] | string | string[];
// 权限模式。Has-拥有所有,HasAny-拥有任意一个,Without-没有所有,WithoutAny-没有任意一个
authMode?: PermModeEnum;
}
字段权限
[字段详情]
vue
<template>
<div class="p-4">
<BasicTable @register="registerTable">
</BasicTable>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
import { demoListApi } from '/@/api/demo/table';
const columns: BasicColumn[] = [
{
title: '编号',
dataIndex: 'no',
width: 100,
},
{
title: '姓名1',
dataIndex: 'name1',
width: 200,
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.Has,
},
{
title: '姓名2',
dataIndex: 'name2',
width: 200,
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.HasAny,
},
{
title: '姓名',
dataIndex: 'name',
width: 200,
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.Without,
},
{
title: '地址',
dataIndex: 'address',
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.WithoutAny,
},
];
export default defineComponent({
components: { BasicTable },
setup() {
const [registerTable] = useTable({
title: '权限列实例',
api: demoListApi,
columns: columns,
bordered: true,
rowKey: 'id',
rowSelection: {
type: 'checkbox',
},
actionColumn: {
width: 250,
title: 'Action',
dataIndex: 'action',
// slots: { customRender: 'action' },
},
});
return {
registerTable,
};
},
});
</script>
<template>
<div class="p-4">
<BasicTable @register="registerTable">
</BasicTable>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable, BasicColumn } from '/@/components/Table';
import { demoListApi } from '/@/api/demo/table';
const columns: BasicColumn[] = [
{
title: '编号',
dataIndex: 'no',
width: 100,
},
{
title: '姓名1',
dataIndex: 'name1',
width: 200,
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.Has,
},
{
title: '姓名2',
dataIndex: 'name2',
width: 200,
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.HasAny,
},
{
title: '姓名',
dataIndex: 'name',
width: 200,
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.Without,
},
{
title: '地址',
dataIndex: 'address',
auth: [RoleEnum.TEST, RoleEnum.SUPER],
authMode: PermModeEnum.WithoutAny,
},
];
export default defineComponent({
components: { BasicTable },
setup() {
const [registerTable] = useTable({
title: '权限列实例',
api: demoListApi,
columns: columns,
bordered: true,
rowKey: 'id',
rowSelection: {
type: 'checkbox',
},
actionColumn: {
width: 250,
title: 'Action',
dataIndex: 'action',
// slots: { customRender: 'action' },
},
});
return {
registerTable,
};
},
});
</script>
vue
export interface ActionItem extends ButtonProps {
// 权限编码
auth?: RoleEnum | RoleEnum[] | string | string[];
// 权限模式。Has-拥有所有,HasAny-拥有任意一个,Without-没有所有,WithoutAny-没有任意一个
authMode?: PermModeEnum;
}
export interface ActionItem extends ButtonProps {
// 权限编码
auth?: RoleEnum | RoleEnum[] | string | string[];
// 权限模式。Has-拥有所有,HasAny-拥有任意一个,Without-没有所有,WithoutAny-没有任意一个
authMode?: PermModeEnum;
}
自定义表单验证
生成的前端
Edit.vue
页面已经自动封装了前后端统一校验功能,但后端返回的校验规则很有可能无法满足前端的业务,此时就需要前端进行自定义校验规则。
typescript
const [registerModel, { setModalProps: setProps, closeModal: close }] = useModalInner(async (data) => {
setProps({ confirmLoading: false });
await resetSchema(editFormSchema(type));
await resetFields();
type.value = data?.type || ActionEnum.ADD;
if (unref(type) !== ActionEnum.ADD) {
// 赋值
const record = { ...data?.record };
await setFieldsValue(record);
}
if (unref(type) !== ActionEnum.VIEW) {
let validateApi = Api[VALIDATE_API[unref(type)]];
// 先去 validateApi 接口查询后端的校验规则,在和前端自定义的校验规则 customFormSchemaRules 进行合并
await getValidateRules(validateApi, customFormSchemaRules(type)).then(async (rules) => {
rules && rules.length > 0 && (await updateSchema(rules));
});
}
});
const [registerModel, { setModalProps: setProps, closeModal: close }] = useModalInner(async (data) => {
setProps({ confirmLoading: false });
await resetSchema(editFormSchema(type));
await resetFields();
type.value = data?.type || ActionEnum.ADD;
if (unref(type) !== ActionEnum.ADD) {
// 赋值
const record = { ...data?.record };
await setFieldsValue(record);
}
if (unref(type) !== ActionEnum.VIEW) {
let validateApi = Api[VALIDATE_API[unref(type)]];
// 先去 validateApi 接口查询后端的校验规则,在和前端自定义的校验规则 customFormSchemaRules 进行合并
await getValidateRules(validateApi, customFormSchemaRules(type)).then(async (rules) => {
rules && rules.length > 0 && (await updateSchema(rules));
});
}
});
tsx
export const customFormSchemaRules = (
type: Ref<ActionEnum>,
getFieldsValue: () => Recordable,
): Partial<FormSchemaExt>[] => {
return [
{
field: 'key',
type: RuleType.append, // 追加规则、覆盖规则
rules: [
{
trigger: ['change', 'blur'],
async validator(_, value) {
if (type.value === ActionEnum.EDIT) {
return Promise.resolve();
}
if (value && (await check(value, getFieldsValue()?.parentId, getFieldsValue()?.id))) {
return Promise.reject(t('basic.base.baseDict.key') + '已经存在');
}
return Promise.resolve();
},
},
],
},
];
};
export const customFormSchemaRules = (
type: Ref<ActionEnum>,
getFieldsValue: () => Recordable,
): Partial<FormSchemaExt>[] => {
return [
{
field: 'key',
type: RuleType.append, // 追加规则、覆盖规则
rules: [
{
trigger: ['change', 'blur'],
async validator(_, value) {
if (type.value === ActionEnum.EDIT) {
return Promise.resolve();
}
if (value && (await check(value, getFieldsValue()?.parentId, getFieldsValue()?.id))) {
return Promise.reject(t('basic.base.baseDict.key') + '已经存在');
}
return Promise.resolve();
},
},
],
},
];
};