feat: menu device

This commit is contained in:
louis 2024-04-10 17:39:17 +08:00
parent 2df310b1c0
commit 563c08c233
11 changed files with 83 additions and 18 deletions

View File

@ -40,3 +40,9 @@ export enum HasInventoryStatusEnum {
Yes = 1, // 有库存 Yes = 1, // 有库存
No = 2 // 无库存 No = 2 // 无库存
} }
// 权限资源设备类型
export enum ResourceDeviceEnum {
APP = 0,
PC = 1
}

View File

@ -132,8 +132,8 @@ export class AuthService {
/** /**
* *
*/ */
async getMenus(uid: number): Promise<string[]> { async getMenus(uid: number, isApp: number): Promise<string[]> {
return this.menuService.getMenus(uid); return this.menuService.getMenus(uid,isApp);
} }
/** /**

View File

@ -14,6 +14,8 @@ import { UserService } from '../../user/user.service';
import { AuthService } from '../auth.service'; import { AuthService } from '../auth.service';
import { AccountMenus, AccountUpdateDto } from '../dto/account.dto'; import { AccountMenus, AccountUpdateDto } from '../dto/account.dto';
import { JwtAuthGuard } from '../guards/jwt-auth.guard'; import { JwtAuthGuard } from '../guards/jwt-auth.guard';
import { IsMobile } from '~/common/decorators/http.decorator';
import { ResourceDeviceEnum } from '~/constants/enum';
@ApiTags('Account - 账户模块') @ApiTags('Account - 账户模块')
@ApiSecurityAuth() @ApiSecurityAuth()
@ -45,8 +47,11 @@ export class AccountController {
@ApiOperation({ summary: '获取菜单列表' }) @ApiOperation({ summary: '获取菜单列表' })
@ApiResult({ type: [AccountMenus] }) @ApiResult({ type: [AccountMenus] })
@AllowAnon() @AllowAnon()
async menu(@AuthUser() user: IAuthUser): Promise<string[]> { async menu(@AuthUser() user: IAuthUser, @IsMobile() isApp: boolean): Promise<string[]> {
return this.authService.getMenus(user.uid); return this.authService.getMenus(
user.uid,
isApp ? ResourceDeviceEnum.APP : ResourceDeviceEnum.PC
);
} }
@Get('permissions') @Get('permissions')

View File

@ -53,6 +53,9 @@ export class MaterialsInOutDto {
inOrOut: MaterialsInOrOutEnum; inOrOut: MaterialsInOrOutEnum;
@ApiProperty({ description: '时间' }) @ApiProperty({ description: '时间' })
@Transform(params => {
return params.value ? new Date(params.value) : null;
})
@IsOptional() @IsOptional()
time: Date; time: Date;

View File

@ -23,6 +23,8 @@ import {
import { MenuDto, MenuQueryDto, MenuUpdateDto } from './menu.dto'; import { MenuDto, MenuQueryDto, MenuUpdateDto } from './menu.dto';
import { MenuItemInfo } from './menu.model'; import { MenuItemInfo } from './menu.model';
import { MenuService } from './menu.service'; import { MenuService } from './menu.service';
import { IsMobile } from '~/common/decorators/http.decorator';
import { ResourceDeviceEnum } from '~/constants/enum';
export const permissions = definePermission('system:menu', { export const permissions = definePermission('system:menu', {
LIST: 'list', LIST: 'list',
@ -43,7 +45,9 @@ export class MenuController {
@ApiResult({ type: [MenuItemInfo] }) @ApiResult({ type: [MenuItemInfo] })
@Perm(permissions.LIST) @Perm(permissions.LIST)
async list(@Query() dto: MenuQueryDto) { async list(@Query() dto: MenuQueryDto) {
return this.menuService.list(dto); return this.menuService.list({
...dto
});
} }
@Get(':id') @Get(':id')

View File

@ -3,6 +3,7 @@ import {
IsBoolean, IsBoolean,
IsIn, IsIn,
IsInt, IsInt,
IsNumber,
IsOptional, IsOptional,
IsString, IsString,
Min, Min,
@ -15,6 +16,11 @@ export class MenuDto {
@IsIn([0, 1, 2]) @IsIn([0, 1, 2])
type: number; type: number;
@ApiProperty({ description: '客户端设备类型' })
@IsOptional()
@IsIn([0, 1])
device: number;
@ApiProperty({ description: '父级菜单' }) @ApiProperty({ description: '父级菜单' })
@IsOptional() @IsOptional()
parentId: number; parentId: number;
@ -85,4 +91,11 @@ export class MenuDto {
export class MenuUpdateDto extends PartialType(MenuDto) {} export class MenuUpdateDto extends PartialType(MenuDto) {}
export class MenuQueryDto extends PartialType(MenuDto) {} export class MenuQueryDto extends PartialType(MenuDto) {
@ApiProperty({ description: 'App端的菜单权限' })
@IsNumber()
@IsOptional()
isApp?: number;
}

View File

@ -6,28 +6,28 @@ import { RoleEntity } from '../role/role.entity';
@Entity({ name: 'sys_menu' }) @Entity({ name: 'sys_menu' })
export class MenuEntity extends CommonEntity { export class MenuEntity extends CommonEntity {
@Column({ name: 'parent_id', nullable: true }) @Column({ name: 'parent_id', nullable: true, comment: '父级ID' })
parentId: number; parentId: number;
@Column() @Column()
name: string; name: string;
@Column({ nullable: true }) @Column({ nullable: true, comment: '前端路径' })
path: string; path: string;
@Column({ nullable: true }) @Column({ nullable: true, comment: '权限' })
permission: string; permission: string;
@Column({ type: 'tinyint', default: 0 }) @Column({ type: 'tinyint', default: 0, comment: '类型0-目录 1-菜单 2-权限' })
type: number; type: number;
@Column({ nullable: true, default: '' }) @Column({ nullable: true, default: '', comment: '图标' })
icon: string; icon: string;
@Column({ name: 'order_no', type: 'int', nullable: true, default: 0 }) @Column({ name: 'order_no', type: 'int', nullable: true, default: 0 })
orderNo: number; orderNo: number;
@Column({ name: 'component', nullable: true }) @Column({ name: 'component', nullable: true, comment: '前端组件文件地址' })
component: string; component: string;
@Column({ name: 'is_ext', type: 'boolean', default: false }) @Column({ name: 'is_ext', type: 'boolean', default: false })
@ -48,6 +48,9 @@ export class MenuEntity extends CommonEntity {
@Column({ type: 'tinyint', default: 1 }) @Column({ type: 'tinyint', default: 1 })
status: number; status: number;
@Column({ type: 'tinyint', default: 1,comment: '用户端类型0-APP 1-PC' })
device: number;
@ManyToMany(() => RoleEntity, role => role.menus, { @ManyToMany(() => RoleEntity, role => role.menus, {
onDelete: 'CASCADE' onDelete: 'CASCADE'
}) })

View File

@ -18,6 +18,7 @@ import { deleteEmptyChildren, generatorMenu, generatorRouters } from '~/utils';
import { RoleService } from '../role/role.service'; import { RoleService } from '../role/role.service';
import { MenuDto, MenuQueryDto, MenuUpdateDto } from './menu.dto'; import { MenuDto, MenuQueryDto, MenuUpdateDto } from './menu.dto';
import { ResourceDeviceEnum } from '~/constants/enum';
@Injectable() @Injectable()
export class MenuService { export class MenuService {
@ -32,7 +33,14 @@ export class MenuService {
/** /**
* *
*/ */
async list({ name, path, permission, component, status }: MenuQueryDto): Promise<MenuEntity[]> { async list({
name,
path,
permission,
component,
status,
isApp
}: MenuQueryDto): Promise<MenuEntity[]> {
const menus = await this.menuRepository.find({ const menus = await this.menuRepository.find({
where: { where: {
...(name && { name: Like(`%${name}%`) }), ...(name && { name: Like(`%${name}%`) }),
@ -66,20 +74,29 @@ export class MenuService {
/** /**
* *
*/ */
async getMenus(uid: number): Promise<string[]> { async getMenus(uid: number, deviceType: number): Promise<string[]> {
const roleIds = await this.roleService.getRoleIdsByUser(uid); const roleIds = await this.roleService.getRoleIdsByUser(uid);
let menus: MenuEntity[] = []; let menus: MenuEntity[] = [];
if (isEmpty(roleIds)) return generatorRouters([]); if (isEmpty(roleIds)) return generatorRouters([]);
if (this.roleService.hasAdminRole(roleIds)) { if (this.roleService.hasAdminRole(roleIds)) {
menus = await this.menuRepository.find({ order: { orderNo: 'ASC' } }); menus = await this.menuRepository.find({
order: { orderNo: 'ASC' },
where: {
...(isNumber(deviceType) ? { device: deviceType } : null)
}
});
} else { } else {
menus = await this.menuRepository menus = await this.menuRepository
.createQueryBuilder('menu') .createQueryBuilder('menu')
.innerJoinAndSelect('menu.roles', 'role') .innerJoinAndSelect('menu.roles', 'role')
.where({
...(isNumber(deviceType) ? { device: deviceType } : null)
})
.andWhere('role.id IN (:...roleIds)', { roleIds }) .andWhere('role.id IN (:...roleIds)', { roleIds })
.orderBy('menu.order_no', 'ASC') .orderBy('menu.order_no', 'ASC')
.getMany(); .getMany();
} }

View File

@ -35,4 +35,9 @@ export class RoleQueryDto extends IntersectionType(PagerDto<RoleDto>, PartialTyp
@IsInt() @IsInt()
@IsOptional() @IsOptional()
status?: number; status?: number;
@ApiProperty({ description: '用于下拉框选择', required: false })
@IsInt()
@IsOptional()
useForSelect: number;
} }

View File

@ -32,14 +32,17 @@ export class RoleService {
pageSize, pageSize,
name, name,
value, value,
status status,
useForSelect
}: RoleQueryDto): Promise<Pagination<RoleEntity>> { }: RoleQueryDto): Promise<Pagination<RoleEntity>> {
const queryBuilder = this.roleRepository.createQueryBuilder('role').where({ const queryBuilder = this.roleRepository.createQueryBuilder('role').where({
...(name ? { name: Like(`%${name}%`) } : null), ...(name ? { name: Like(`%${name}%`) } : null),
...(value ? { value: Like(`%${value}%`) } : null), ...(value ? { value: Like(`%${value}%`) } : null),
...(isNumber(status) ? { status } : null) ...(isNumber(status) ? { status } : null)
}); });
if(useForSelect){
queryBuilder.andWhere('role.id != :id', { id: ROOT_ROLE_ID })
}
return paginate<RoleEntity>(queryBuilder, { return paginate<RoleEntity>(queryBuilder, {
page, page,
pageSize pageSize

View File

@ -268,7 +268,13 @@ export class UserService {
if (keyword) { if (keyword) {
//关键字模糊查询product的name,productNumber,productSpecification //关键字模糊查询product的name,productNumber,productSpecification
queryBuilder.andWhere( queryBuilder.andWhere(
'(user.nickname like :keyword or user.namePinyin like :keyword or dept.name like :keyword)', `(user.nickname like :keyword or user.namePinyin like :keyword
or dept.name like :keyword
or user.phone like :keyword
or user.email like :keyword
or user.qq like :keyword
or user.remark like :keyword
)`,
{ {
keyword: `%${keyword}%` keyword: `%${keyword}%`
} }