重构角色和权限相关DTO,替换RoleDto为RoleRespDto,并更新相关服务和控制器逻辑

This commit is contained in:
Chuck1sn
2025-06-16 10:57:35 +08:00
parent 7b8ef54e7b
commit ea10b156e3
34 changed files with 543 additions and 405 deletions

View File

@@ -15,7 +15,6 @@ import com.zl.mjga.repository.UserRepository;
import com.zl.mjga.service.IdentityAccessService;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.errors.*;
import jakarta.validation.Valid;
import java.awt.image.BufferedImage;
import java.security.Principal;
@@ -118,7 +117,7 @@ public class IdentityAccessController {
@PreAuthorize("hasAuthority(T(com.zl.mjga.model.urp.EPermission).WRITE_USER_ROLE_PERMISSION)")
@GetMapping("/role")
RoleDto queryRoleWithPermission(@RequestParam Long roleId) {
RoleRespDto queryRoleWithPermission(@RequestParam Long roleId) {
return identityAccessService.queryUniqueRoleWithPermission(roleId);
}
@@ -145,7 +144,7 @@ public class IdentityAccessController {
@PreAuthorize("hasAuthority(T(com.zl.mjga.model.urp.EPermission).READ_USER_ROLE_PERMISSION)")
@GetMapping("/roles")
@ResponseStatus(HttpStatus.OK)
PageResponseDto<List<RoleDto>> queryRoles(
PageResponseDto<List<RoleRespDto>> queryRoles(
@ModelAttribute PageRequestDto pageRequestDto, @ModelAttribute RoleQueryDto roleQueryDto) {
return identityAccessService.pageQueryRole(pageRequestDto, roleQueryDto);
}

View File

@@ -1,5 +1,6 @@
package com.zl.mjga.dto.urp;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
@AllArgsConstructor
@@ -7,8 +8,16 @@ import lombok.*;
@Data
@Builder
public class PermissionRespDto {
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private Long id;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private String code;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private String name;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean isBound;
}

View File

@@ -1,5 +1,6 @@
package com.zl.mjga.dto.urp;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.LinkedList;
import java.util.List;
import lombok.*;
@@ -8,10 +9,18 @@ import lombok.*;
@NoArgsConstructor
@Data
@Builder
public class RoleDto {
public class RoleRespDto {
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private Long id;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private String code;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private String name;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean isBound;
@Builder.Default List<PermissionRespDto> permissions = new LinkedList<>();
}

View File

@@ -1,6 +1,7 @@
package com.zl.mjga.dto.urp;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.OffsetDateTime;
import java.util.LinkedList;
import java.util.List;
@@ -13,7 +14,11 @@ import lombok.*;
@Data
@Builder
public class UserRolePermissionDto {
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private Long id;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private String username;
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@@ -21,9 +26,12 @@ public class UserRolePermissionDto {
private String avatar;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean enable;
@Builder.Default private List<RoleDto> roles = new LinkedList<>();
@Builder.Default private List<RoleRespDto> roles = new LinkedList<>();
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private OffsetDateTime createTime;
public Set<PermissionRespDto> getPermissions() {

View File

@@ -5,7 +5,7 @@ import static org.jooq.impl.DSL.*;
import com.zl.mjga.dto.PageRequestDto;
import com.zl.mjga.dto.urp.PermissionRespDto;
import com.zl.mjga.dto.urp.RoleDto;
import com.zl.mjga.dto.urp.RoleRespDto;
import com.zl.mjga.dto.urp.UserQueryDto;
import com.zl.mjga.dto.urp.UserRolePermissionDto;
import java.util.List;
@@ -102,7 +102,7 @@ public class UserRepository extends UserDao {
r -> r.map((record) -> record.into(PermissionRespDto.class)))
.as("permissions"))
.from(USER.role()))
.convertFrom(r -> r.map((record) -> record.into(RoleDto.class)))
.convertFrom(r -> r.map((record) -> record.into(RoleRespDto.class)))
.as("roles"))
.from(USER)
.where(USER.ID.eq(userId))

View File

@@ -80,17 +80,17 @@ public class IdentityAccessService {
return userRepository.fetchUniqueUserDtoWithNestedRolePermissionBy(userId);
}
public PageResponseDto<List<RoleDto>> pageQueryRole(
public PageResponseDto<List<RoleRespDto>> pageQueryRole(
PageRequestDto pageRequestDto, RoleQueryDto roleQueryDto) {
Result<Record> roleRecords = roleRepository.pageFetchBy(pageRequestDto, roleQueryDto);
if (roleRecords.isEmpty()) {
return PageResponseDto.empty();
}
List<RoleDto> roleDtoList =
List<RoleRespDto> roleRespDtoList =
roleRecords.stream()
.map(
record -> {
return RoleDto.builder()
return RoleRespDto.builder()
.id(record.getValue("id", Long.class))
.code(record.getValue("code", String.class))
.name(record.getValue("name", String.class))
@@ -103,17 +103,17 @@ public class IdentityAccessService {
})
.toList();
return new PageResponseDto<>(
roleRecords.get(0).getValue("total_role", Integer.class), roleDtoList);
roleRecords.get(0).getValue("total_role", Integer.class), roleRespDtoList);
}
public @Nullable RoleDto queryUniqueRoleWithPermission(Long roleId) {
public @Nullable RoleRespDto queryUniqueRoleWithPermission(Long roleId) {
Result<Record> roleWithPermissionRecords = roleRepository.fetchUniqueRoleWithPermission(roleId);
if (roleWithPermissionRecords.isEmpty()) {
return null;
}
RoleDto roleDto = createRbacDtoRolePart(roleWithPermissionRecords);
setCurrentRolePermission(roleDto, roleWithPermissionRecords);
return roleDto;
RoleRespDto roleRespDto = createRbacDtoRolePart(roleWithPermissionRecords);
setCurrentRolePermission(roleRespDto, roleWithPermissionRecords);
return roleRespDto;
}
public PageResponseDto<List<PermissionRespDto>> pageQueryPermission(
@@ -199,12 +199,12 @@ public class IdentityAccessService {
.toList());
}
private void setCurrentRolePermission(RoleDto roleDto, List<Record> roleResult) {
private void setCurrentRolePermission(RoleRespDto roleRespDto, List<Record> roleResult) {
if (roleResult.get(0).getValue(PERMISSION.ID) != null) {
roleResult.forEach(
(record) -> {
PermissionRespDto permissionRespDto = createRbacDtoPermissionPart(record);
roleDto.getPermissions().add(permissionRespDto);
roleRespDto.getPermissions().add(permissionRespDto);
});
}
}
@@ -217,12 +217,12 @@ public class IdentityAccessService {
return permissionRespDto;
}
private RoleDto createRbacDtoRolePart(List<Record> roleResult) {
RoleDto roleDto = new RoleDto();
roleDto.setId(roleResult.get(0).getValue(ROLE.ID));
roleDto.setCode(roleResult.get(0).getValue(ROLE.CODE));
roleDto.setName(roleResult.get(0).getValue(ROLE.NAME));
return roleDto;
private RoleRespDto createRbacDtoRolePart(List<Record> roleResult) {
RoleRespDto roleRespDto = new RoleRespDto();
roleRespDto.setId(roleResult.get(0).getValue(ROLE.ID));
roleRespDto.setCode(roleResult.get(0).getValue(ROLE.CODE));
roleRespDto.setName(roleResult.get(0).getValue(ROLE.NAME));
return roleRespDto;
}
public boolean isRoleDuplicate(String roleCode, String name) {

View File

@@ -178,14 +178,14 @@ class UserRolePermissionMvcTest {
stubRoleQueryDto.setRoleId(stubRoleId);
stubRoleQueryDto.setRoleCode(stubRoleCode);
stubRoleQueryDto.setRoleName(stubRoleName);
RoleDto stubRoleDto = new RoleDto();
stubRoleDto.setId(1L);
stubRoleDto.setName(stubRoleName);
stubRoleDto.setCode(stubRoleCode);
stubRoleDto.setPermissions(
RoleRespDto stubRoleRespDto = new RoleRespDto();
stubRoleRespDto.setId(1L);
stubRoleRespDto.setName(stubRoleName);
stubRoleRespDto.setCode(stubRoleCode);
stubRoleRespDto.setPermissions(
List.of(new PermissionRespDto(1L, "9VWU1nmU89zEVH", "9VWU1nmU89zEVH", false)));
when(identityAccessService.pageQueryRole(PageRequestDto.of(1, 5), stubRoleQueryDto))
.thenReturn(new PageResponseDto<>(1, List.of(stubRoleDto)));
.thenReturn(new PageResponseDto<>(1, List.of(stubRoleRespDto)));
mockMvc
.perform(

View File

@@ -112,19 +112,19 @@ class UserRolePermissionUnitTest {
DSL.field("total_user", Integer.class))
.values(stubUserId2, stubUserName2, stubUserPassword2, true, null, 2));
UserRolePermissionDto mockUserRolePermissionDto1 = new UserRolePermissionDto();
RoleDto mockRoleDto = new RoleDto();
mockRoleDto.setId(stubRoleId);
mockRoleDto.setCode(stubRoleCode);
mockRoleDto.setName(stubRoleName);
RoleRespDto mockRoleRespDto = new RoleRespDto();
mockRoleRespDto.setId(stubRoleId);
mockRoleRespDto.setCode(stubRoleCode);
mockRoleRespDto.setName(stubRoleName);
PermissionRespDto permissionRespDto = new PermissionRespDto();
permissionRespDto.setId(stubPermissionId);
permissionRespDto.setCode(stubPermissionCode);
permissionRespDto.setName(stubPermissionName);
mockRoleDto.getPermissions().add(permissionRespDto);
mockRoleRespDto.getPermissions().add(permissionRespDto);
mockUserRolePermissionDto1.setId(stubUserId1);
mockUserRolePermissionDto1.setUsername(stubUserName1);
mockUserRolePermissionDto1.setPassword(stubUserPassword1);
mockUserRolePermissionDto1.getRoles().add(mockRoleDto);
mockUserRolePermissionDto1.getRoles().add(mockRoleRespDto);
UserRolePermissionDto mockUserRolePermissionDto2 = new UserRolePermissionDto();
mockUserRolePermissionDto2.setId(stubUserId2);
@@ -202,7 +202,7 @@ class UserRolePermissionUnitTest {
mockResult.setId(stubRoleId);
mockResult.setRoles(
List.of(
new RoleDto(
new RoleRespDto(
stubRoleId,
stubRoleName,
stubRoleCode,
@@ -245,12 +245,12 @@ class UserRolePermissionUnitTest {
when(roleRepository.pageFetchBy(any(PageRequestDto.class), any(RoleQueryDto.class)))
.thenReturn(mockRoleResult);
RoleQueryDto roleQueryDto = new RoleQueryDto();
PageResponseDto<List<RoleDto>> pageResult =
PageResponseDto<List<RoleRespDto>> pageResult =
identityAccessService.pageQueryRole(PageRequestDto.of(1, 5), roleQueryDto);
assertThat(pageResult.getTotal()).isEqualTo(0L);
roleQueryDto.setUserId(1L);
PageResponseDto<List<RoleDto>> pageResult2 =
PageResponseDto<List<RoleRespDto>> pageResult2 =
identityAccessService.pageQueryRole(PageRequestDto.of(1, 5), roleQueryDto);
assertThat(pageResult2.getTotal()).isEqualTo(0L);
}

View File

@@ -289,7 +289,7 @@
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/RoleDto"
"$ref": "#/components/schemas/RoleRespDto"
}
}
}
@@ -1031,7 +1031,7 @@
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/PageResponseDtoListRoleDto"
"$ref": "#/components/schemas/PageResponseDtoListRoleRespDto"
}
}
}
@@ -1718,6 +1718,12 @@
}
},
"PermissionRespDto": {
"required": [
"code",
"id",
"isBound",
"name"
],
"type": "object",
"properties": {
"id": {
@@ -1735,7 +1741,13 @@
}
}
},
"RoleDto": {
"RoleRespDto": {
"required": [
"code",
"id",
"isBound",
"name"
],
"type": "object",
"properties": {
"id": {
@@ -1760,6 +1772,12 @@
}
},
"UserRolePermissionDto": {
"required": [
"createTime",
"enable",
"id",
"username"
],
"type": "object",
"properties": {
"id": {
@@ -1782,7 +1800,7 @@
"roles": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RoleDto"
"$ref": "#/components/schemas/RoleRespDto"
}
},
"createTime": {
@@ -1832,7 +1850,7 @@
}
}
},
"PageResponseDtoListRoleDto": {
"PageResponseDtoListRoleRespDto": {
"type": "object",
"properties": {
"total": {
@@ -1842,7 +1860,7 @@
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RoleDto"
"$ref": "#/components/schemas/RoleRespDto"
}
}
}

View File

@@ -779,29 +779,29 @@ export interface components {
};
PermissionRespDto: {
/** Format: int64 */
id?: number;
code?: string;
name?: string;
isBound?: boolean;
id: number;
code: string;
name: string;
isBound: boolean;
};
RoleDto: {
RoleRespDto: {
/** Format: int64 */
id?: number;
code?: string;
name?: string;
isBound?: boolean;
id: number;
code: string;
name: string;
isBound: boolean;
permissions?: components["schemas"]["PermissionRespDto"][];
};
UserRolePermissionDto: {
/** Format: int64 */
id?: number;
username?: string;
id: number;
username: string;
password?: string;
avatar?: string;
enable?: boolean;
roles?: components["schemas"]["RoleDto"][];
enable: boolean;
roles?: components["schemas"]["RoleRespDto"][];
/** Format: date-time */
createTime?: string;
createTime: string;
permissions?: components["schemas"]["PermissionRespDto"][];
};
RoleQueryDto: {
@@ -815,10 +815,10 @@ export interface components {
/** @enum {string} */
bindState?: "BIND" | "UNBIND" | "ALL";
};
PageResponseDtoListRoleDto: {
PageResponseDtoListRoleRespDto: {
/** Format: int64 */
total?: number;
data?: components["schemas"]["RoleDto"][];
data?: components["schemas"]["RoleRespDto"][];
};
PermissionQueryDto: {
/** Format: int64 */
@@ -1113,7 +1113,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": components["schemas"]["RoleDto"];
"*/*": components["schemas"]["RoleRespDto"];
};
};
};
@@ -1750,7 +1750,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
"*/*": components["schemas"]["PageResponseDtoListRoleDto"];
"*/*": components["schemas"]["PageResponseDtoListRoleRespDto"];
};
};
};

View File

@@ -10,10 +10,10 @@
import { getUserAvatarUrl } from "@/utils/avatarUtil";
import { computed } from "vue";
const {
src = "",
alt = "用户头像",
size = "md"
const {
src = "",
alt = "用户头像",
size = "md",
} = defineProps<{
src?: string;
alt?: string;
@@ -37,7 +37,7 @@ const processedSrc = computed(() => {
}
if (src === "/trump.jpg") {
return src;
}
}
return getUserAvatarUrl(src);
});
</script>

View File

@@ -2,7 +2,7 @@
<nav class="flex mb-4" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 sm:space-x-2 text-sm">
<li class="inline-flex items-center">
<RouterLink :to="{ name: RouteName.HOME }"
<RouterLink :to="Routes.HOME.fullPath()"
class="inline-flex items-center font-medium text-gray-500 hover:text-blue-600">
<svg class="w-3.5 h-3.5 mr-1.5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor"
viewBox="0 0 20 20">
@@ -30,7 +30,7 @@
</template>
<script setup lang="ts">
import { RouteName } from "@/router/constants";
import { Routes } from "@/router/constants";
import { computed } from "vue";
import type { RouteLocationRaw } from "vue-router";

View File

@@ -53,14 +53,14 @@
<li>
<button @click="() => {
userDropDownMenu?.toggle()
router.push(`${RoutePath.DASHBOARD}/${RoutePath.SETTINGS}`)
router.push(Routes.SETTINGS.fullPath())
}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 "
role="menuitem">Settings</button>
</li>
<li>
<button @click="() => {
userDropDownMenu?.toggle()
router.push(RouteName.USERVIEW)
router.push(Routes.USERVIEW.withParams({}))
}" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 "
role="menuitem">Dashboard</button>
</li>
@@ -93,7 +93,7 @@ import { Dropdown, type DropdownInterface, initFlowbite } from "flowbite";
import { onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import useUserAuth from "../composables/auth/useUserAuth";
import { RouteName, RoutePath } from "../router/constants";
import { Routes } from "../router/constants";
import Avatar from "./Avatar.vue";
import AiChatIcon from "./icons/AiChatIcon.vue";
@@ -113,7 +113,7 @@ const { signOut } = useUserAuth();
const router = useRouter();
const handleLogoutClick = () => {
signOut();
router.push(RoutePath.LOGIN);
router.push(Routes.LOGIN.path);
};
onMounted(() => {

View File

@@ -52,7 +52,7 @@ import type { components } from "../api/types/schema";
const alertStore = useAlertStore();
const { role, onSubmit } = defineProps<{
role?: components["schemas"]["RoleDto"];
role?: components["schemas"]["RoleRespDto"];
closeModal: () => void;
onSubmit: (data: RoleUpsertModel) => Promise<void>;
}>();

View File

@@ -28,7 +28,7 @@
</template>
<script setup lang="ts">
import { RoutePath } from "@/router/constants";
import { Routes } from "@/router/constants";
import { initFlowbite } from "flowbite";
import { onMounted, ref } from "vue";
import { RouterLink, useRoute } from "vue-router";
@@ -73,42 +73,42 @@ defineExpose({
const menuItems = [
{
title: "用户管理",
path: `${RoutePath.DASHBOARD}/${RoutePath.USERVIEW}`,
path: Routes.USERVIEW.fullPath(),
icon: UsersIcon,
},
{
title: "角色管理",
path: `${RoutePath.DASHBOARD}/${RoutePath.ROLEVIEW}`,
path: Routes.ROLEVIEW.fullPath(),
icon: RoleIcon,
},
{
title: "权限管理",
path: `${RoutePath.DASHBOARD}/${RoutePath.PERMISSIONVIEW}`,
path: Routes.PERMISSIONVIEW.fullPath(),
icon: PermissionIcon,
},
{
title: "部门管理",
path: `${RoutePath.DASHBOARD}/${RoutePath.DEPARTMENTVIEW}`,
path: Routes.DEPARTMENTVIEW.fullPath(),
icon: DepartmentIcon,
},
{
title: "岗位管理",
path: `${RoutePath.DASHBOARD}/${RoutePath.POSITIONVIEW}`,
path: Routes.POSITIONVIEW.fullPath(),
icon: PositionIcon,
},
{
title: "个人中心",
path: `${RoutePath.DASHBOARD}/${RoutePath.SETTINGS}`,
path: Routes.SETTINGS.fullPath(),
icon: SettingsIcon,
},
{
title: "定时任务",
path: `${RoutePath.DASHBOARD}/${RoutePath.SCHEDULERVIEW}`,
path: Routes.SCHEDULERVIEW.fullPath(),
icon: SchedulerIcon,
},
{
title: "大模型管理",
path: `${RoutePath.DASHBOARD}/${RoutePath.LLMCONFIGVIEW}`,
path: Routes.LLMCONFIGVIEW.fullPath(),
icon: LlmConfigIcon,
},
];

View File

@@ -4,8 +4,8 @@ import type { components } from "../../api/types/schema";
export const useRolesQuery = () => {
const total = ref<number>(0);
const roles = ref<components["schemas"]["RoleDto"][]>();
const roleWithDetail = ref<components["schemas"]["RoleDto"]>();
const roles = ref<components["schemas"]["RoleRespDto"][]>();
const roleWithDetail = ref<components["schemas"]["RoleRespDto"]>();
const getRoleWithDetail = async (roleId: number) => {
const { data } = await client.GET("/iam/role", {

View File

@@ -1,56 +1,156 @@
export enum RoutePath {
HOME = "/",
LOGIN = "/login",
DASHBOARD = "/dashboard",
GLOBAL_NOTFOUND = "/:pathMatch(.*)*",
NOTFOUND = ":pathMatch(.*)*",
OVERVIEW = "overview",
USERVIEW = "users",
ROLEVIEW = "roles",
BINDROLEVIEW = "bind-roles/:userId",
BINDPERMISSIONVIEW = "bind-permissions/:roleId",
BINDDEPARTMENTVIEW = "bind-departments/:userId",
BINDPOSITIONVIEW = "bind-positions/:userId",
PERMISSIONVIEW = "permissions",
DEPARTMENTVIEW = "departments",
POSITIONVIEW = "positions",
CREATEUSERVIEW = "create-user",
LLMCONFIGVIEW = "llm/config",
SCHEDULERVIEW = "scheduler",
UPSERTUSERVIEW = "upsert-user",
UPSERTROLEVIEW = "upsert-role",
UPSERTPERMISSIONVIEW = "upsert-permission",
UPSERTDEPARTMENTVIEW = "upsert-department",
UPSERTPOSITIONVIEW = "upsert-position",
SETTINGS = "settings",
}
import type { RouteLocationRaw } from "vue-router";
export enum RouteName {
HOME = "home",
LOGIN = "login",
DASHBOARD = "dashboard",
OVERVIEW = "overview",
USERVIEW = "users",
ROLEVIEW = "roles",
BINDROLEVIEW = "bind-roles",
BINDPERMISSIONVIEW = "bind-permissions",
BINDDEPARTMENTVIEW = "bind-departments",
BINDPOSITIONVIEW = "bind-positions",
PERMISSIONVIEW = "permissions",
DEPARTMENTVIEW = "departments",
POSITIONVIEW = "positions",
CREATEUSERVIEW = "create-user",
LLMCONFIGVIEW = "llm/config",
SCHEDULERVIEW = "scheduler",
UPSERTUSERVIEW = "upsert-user",
UPSERTROLEVIEW = "upsert-role",
UPSERTPERMISSIONVIEW = "upsert-permission",
UPSERTDEPARTMENTVIEW = "upsert-department",
UPSERTPOSITIONVIEW = "upsert-position",
SETTINGS = "settings",
NOTFOUND = "notfound",
GLOBAL_NOTFOUND = "global-notfound",
}
export type RouteConfig = {
path: string;
name: string;
fullPath: () => string;
withParams: <T extends Record<string, string | number>>(
params: T,
) => RouteLocationRaw;
};
// 基础路由
export const BaseRoutes = {
HOME: {
path: "/",
name: "home",
fullPath: () => "/",
withParams: () => ({ name: "home" }),
},
LOGIN: {
path: "/login",
name: "login",
fullPath: () => "/login",
withParams: () => ({ name: "login" }),
},
DASHBOARD: {
path: "/dashboard",
name: "dashboard",
fullPath: () => "/dashboard",
withParams: () => ({ name: "dashboard" }),
},
NOTFOUND: {
path: ":pathMatch(.*)*",
name: "notfound",
fullPath: () => "/dashboard/:pathMatch(.*)*",
withParams: () => ({ name: "notfound" }),
},
GLOBAL_NOTFOUND: {
path: "/:pathMatch(.*)*",
name: "global-notfound",
fullPath: () => "/:pathMatch(.*)*",
withParams: () => ({ name: "global-notfound" }),
},
} as const;
// 仪表盘子路由
export const DashboardRoutes = {
OVERVIEW: {
path: "overview",
name: "overview",
fullPath: () => `${BaseRoutes.DASHBOARD.path}/overview`,
withParams: () => ({ name: "overview" }),
},
SETTINGS: {
path: "settings",
name: "settings",
fullPath: () => `${BaseRoutes.DASHBOARD.path}/settings`,
withParams: () => ({ name: "settings" }),
},
} as const;
// 用户管理相关路由
export const UserRoutes = {
USERVIEW: {
path: "users",
name: "users",
fullPath: () => `${BaseRoutes.DASHBOARD.path}/users`,
withParams: () => ({ name: "users" }),
},
ROLEVIEW: {
path: "roles",
name: "roles",
fullPath: () => `${BaseRoutes.DASHBOARD.path}/roles`,
withParams: () => ({ name: "roles" }),
},
BINDROLEVIEW: {
path: "bind-roles/:userId",
name: "bind-roles",
fullPath: () => `${BaseRoutes.DASHBOARD.path}/bind-roles/:userId`,
withParams: <T extends { userId: string | number }>(params: T) => ({
name: "bind-roles",
params: { userId: params.userId.toString() },
}),
},
BINDPERMISSIONVIEW: {
path: "bind-permissions/:roleId",
name: "bind-permissions",
fullPath: () => `${BaseRoutes.DASHBOARD.path}/bind-permissions/:roleId`,
withParams: <T extends { roleId: string | number }>(params: T) => ({
name: "bind-permissions",
params: { roleId: params.roleId.toString() },
}),
},
BINDDEPARTMENTVIEW: {
path: "bind-departments/:userId",
name: "bind-departments",
fullPath: () => `${BaseRoutes.DASHBOARD.path}/bind-departments/:userId`,
withParams: <T extends { userId: string | number }>(params: T) => ({
name: "bind-departments",
params: { userId: params.userId.toString() },
}),
},
BINDPOSITIONVIEW: {
path: "bind-positions/:userId",
name: "bind-positions",
fullPath: () => `${BaseRoutes.DASHBOARD.path}/bind-positions/:userId`,
withParams: <T extends { userId: string | number }>(params: T) => ({
name: "bind-positions",
params: { userId: params.userId.toString() },
}),
},
PERMISSIONVIEW: {
path: "permissions",
name: "permissions",
fullPath: () => `${BaseRoutes.DASHBOARD.path}/permissions`,
withParams: () => ({ name: "permissions" }),
},
DEPARTMENTVIEW: {
path: "departments",
name: "departments",
fullPath: () => `${BaseRoutes.DASHBOARD.path}/departments`,
withParams: () => ({ name: "departments" }),
},
POSITIONVIEW: {
path: "positions",
name: "positions",
fullPath: () => `${BaseRoutes.DASHBOARD.path}/positions`,
withParams: () => ({ name: "positions" }),
},
} as const;
// AI相关路由
export const AiRoutes = {
LLMCONFIGVIEW: {
path: "llm/config",
name: "llm/config",
fullPath: () => `${BaseRoutes.DASHBOARD.path}/llm/config`,
withParams: () => ({ name: "llm/config" }),
},
SCHEDULERVIEW: {
path: "scheduler",
name: "scheduler",
fullPath: () => `${BaseRoutes.DASHBOARD.path}/scheduler`,
withParams: () => ({ name: "scheduler" }),
},
} as const;
export const Routes = {
...BaseRoutes,
...DashboardRoutes,
...UserRoutes,
...AiRoutes,
} as const;
export enum ERole {
ADMIN = "ADMIN",

View File

@@ -1,19 +1,19 @@
import useAlertStore from "@/composables/store/useAlertStore";
import useUserStore from "@/composables/store/useUserStore";
import type { NavigationGuard, Router } from "vue-router";
import type { RouteMeta } from "../types/router";
import { RoutePath } from "./constants";
import useAlertStore from "@/composables/store/useAlertStore";
import { Routes } from "./constants";
export const authGuard: NavigationGuard = (to) => {
const userStore = useUserStore();
if (to.meta.requiresAuth && !userStore.user) {
return {
path: RoutePath.LOGIN,
path: Routes.LOGIN.path,
query: { redirect: to.fullPath },
};
}
if (to.path === RoutePath.LOGIN && userStore.user) {
return { path: `${RoutePath.DASHBOARD}/${RoutePath.USERVIEW}` };
if (to.path === Routes.LOGIN.path && userStore.user) {
return { path: `${Routes.DASHBOARD.path}/${Routes.USERVIEW.path}` };
}
};

View File

@@ -2,20 +2,20 @@ import { createRouter, createWebHistory } from "vue-router";
import type { RouteRecordRaw } from "vue-router";
import { setupGuards } from "./guards";
import { Routes } from "./constants";
import authRoutes from "./modules/auth";
import dashboardRoutes from "./modules/dashboard";
import errorRoutes from "./modules/error";
import { RouteName, RoutePath } from "./constants";
const routes: RouteRecordRaw[] = [
dashboardRoutes,
...authRoutes,
...errorRoutes,
{
path: RoutePath.HOME,
name: RouteName.HOME,
path: Routes.HOME.path,
name: Routes.HOME.name,
redirect: {
path: `${RoutePath.DASHBOARD}/${RoutePath.USERVIEW}`,
path: `${Routes.DASHBOARD.path}/${Routes.USERVIEW.path}`,
},
},
];
@@ -27,7 +27,7 @@ const router = createRouter({
router.onError((err) => {
console.error("router err:", err);
router.push(RouteName.USERVIEW);
router.push(Routes.USERVIEW.name);
return false;
});

View File

@@ -1,10 +1,10 @@
import type { RouteRecordRaw } from "vue-router";
import { EPermission, RouteName, RoutePath } from "../constants";
import { EPermission, Routes } from "../constants";
const aiRoutes: RouteRecordRaw[] = [
{
path: RoutePath.LLMCONFIGVIEW,
name: RouteName.LLMCONFIGVIEW,
path: Routes.LLMCONFIGVIEW.path,
name: Routes.LLMCONFIGVIEW.name,
component: () => import("@/views/LlmConfigView.vue"),
meta: {
requiresAuth: true,

View File

@@ -1,17 +1,17 @@
import type { RouteRecordRaw } from "vue-router";
import { RouteName, RoutePath } from "../constants";
import { Routes } from "../constants";
const authRoutes: RouteRecordRaw[] = [
{
path: RoutePath.HOME,
name: RouteName.HOME,
path: Routes.HOME.path,
name: Routes.HOME.name,
redirect: {
name: RouteName.LOGIN,
name: Routes.LOGIN.name,
},
},
{
path: RoutePath.LOGIN,
name: RouteName.LOGIN,
path: Routes.LOGIN.path,
name: Routes.LOGIN.name,
component: () => import("../../views/LoginView.vue"),
},
];

View File

@@ -1,28 +1,28 @@
import type { RouteRecordRaw } from "vue-router";
import Dashboard from "../../components/Dashboard.vue";
import { EPermission, ERole, RouteName, RoutePath } from "../constants";
import userManagementRoutes from "./user";
import { EPermission, ERole, Routes } from "../constants";
import aiRoutes from "./ai";
import userManagementRoutes from "./user";
const dashboardRoutes: RouteRecordRaw = {
path: RoutePath.DASHBOARD,
name: RouteName.DASHBOARD,
path: Routes.DASHBOARD.path,
name: Routes.DASHBOARD.name,
component: Dashboard,
meta: {
requiresAuth: true,
},
children: [
{
path: RoutePath.OVERVIEW,
name: RouteName.OVERVIEW,
path: Routes.OVERVIEW.path,
name: Routes.OVERVIEW.name,
component: () => import("@/views/OverView.vue"),
meta: {
requiresAuth: true,
},
},
{
path: RoutePath.SETTINGS,
name: RouteName.SETTINGS,
path: Routes.SETTINGS.path,
name: Routes.SETTINGS.name,
component: () => import("@/views/SettingsView.vue"),
meta: {
requiresAuth: true,
@@ -31,13 +31,13 @@ const dashboardRoutes: RouteRecordRaw = {
...userManagementRoutes,
...aiRoutes,
{
path: RoutePath.NOTFOUND,
name: RouteName.NOTFOUND,
path: Routes.NOTFOUND.path,
name: Routes.NOTFOUND.name,
component: () => import("@/views/NotFound.vue"),
},
{
path: RoutePath.SCHEDULERVIEW,
name: RouteName.SCHEDULERVIEW,
path: Routes.SCHEDULERVIEW.path,
name: Routes.SCHEDULERVIEW.name,
component: () => import("@/views/SchedulerView.vue"),
meta: {
requiresAuth: true,
@@ -45,8 +45,8 @@ const dashboardRoutes: RouteRecordRaw = {
},
},
{
path: RoutePath.DEPARTMENTVIEW,
name: RouteName.DEPARTMENTVIEW,
path: Routes.DEPARTMENTVIEW.path,
name: Routes.DEPARTMENTVIEW.name,
component: () => import("@/views/DepartmentView.vue"),
meta: {
requiresAuth: true,
@@ -54,8 +54,8 @@ const dashboardRoutes: RouteRecordRaw = {
},
},
{
path: RoutePath.POSITIONVIEW,
name: RouteName.POSITIONVIEW,
path: Routes.POSITIONVIEW.path,
name: Routes.POSITIONVIEW.name,
component: () => import("@/views/PositionView.vue"),
meta: {
requiresAuth: true,

View File

@@ -1,10 +1,10 @@
import type { RouteRecordRaw } from "vue-router";
import { RouteName, RoutePath } from "../constants";
import { Routes } from "../constants";
const errorRoutes: RouteRecordRaw[] = [
{
path: RoutePath.GLOBAL_NOTFOUND,
name: RouteName.GLOBAL_NOTFOUND,
path: Routes.GLOBAL_NOTFOUND.path,
name: Routes.GLOBAL_NOTFOUND.name,
component: () => import("../../views/NotFound.vue"),
},
];

View File

@@ -1,10 +1,10 @@
import type { RouteRecordRaw } from "vue-router";
import { EPermission, RouteName, RoutePath } from "../constants";
import { EPermission, Routes } from "../constants";
const userManagementRoutes: RouteRecordRaw[] = [
{
path: RoutePath.USERVIEW,
name: RouteName.USERVIEW,
path: Routes.USERVIEW.path,
name: Routes.USERVIEW.name,
component: () => import("@/views/UserView.vue"),
meta: {
requiresAuth: true,
@@ -12,8 +12,8 @@ const userManagementRoutes: RouteRecordRaw[] = [
},
},
{
path: RoutePath.ROLEVIEW,
name: RouteName.ROLEVIEW,
path: Routes.ROLEVIEW.path,
name: Routes.ROLEVIEW.name,
component: () => import("@/views/RoleView.vue"),
meta: {
requiresAuth: true,
@@ -21,8 +21,8 @@ const userManagementRoutes: RouteRecordRaw[] = [
},
},
{
path: RoutePath.BINDROLEVIEW,
name: RouteName.BINDROLEVIEW,
path: Routes.BINDROLEVIEW.path,
name: Routes.BINDROLEVIEW.name,
component: () => import("@/views/BindRoleView.vue"),
meta: {
requiresAuth: true,
@@ -30,8 +30,8 @@ const userManagementRoutes: RouteRecordRaw[] = [
},
},
{
path: RoutePath.BINDDEPARTMENTVIEW,
name: RouteName.BINDDEPARTMENTVIEW,
path: Routes.BINDDEPARTMENTVIEW.path,
name: Routes.BINDDEPARTMENTVIEW.name,
component: () => import("@/views/BindDepartmentView.vue"),
meta: {
requiresAuth: true,
@@ -39,8 +39,8 @@ const userManagementRoutes: RouteRecordRaw[] = [
},
},
{
path: RoutePath.BINDPERMISSIONVIEW,
name: RouteName.BINDPERMISSIONVIEW,
path: Routes.BINDPERMISSIONVIEW.path,
name: Routes.BINDPERMISSIONVIEW.name,
component: () => import("@/views/BindPermissionView.vue"),
meta: {
requiresAuth: true,
@@ -48,8 +48,8 @@ const userManagementRoutes: RouteRecordRaw[] = [
},
},
{
path: RoutePath.PERMISSIONVIEW,
name: RouteName.PERMISSIONVIEW,
path: Routes.PERMISSIONVIEW.path,
name: Routes.PERMISSIONVIEW.name,
component: () => import("@/views/PermissionView.vue"),
meta: {
requiresAuth: true,
@@ -57,8 +57,8 @@ const userManagementRoutes: RouteRecordRaw[] = [
},
},
{
path: RoutePath.BINDPOSITIONVIEW,
name: RouteName.BINDPOSITIONVIEW,
path: Routes.BINDPOSITIONVIEW.path,
name: Routes.BINDPOSITIONVIEW.name,
component: () => import("@/views/BindPositionView.vue"),
meta: {
requiresAuth: true,

View File

@@ -1,6 +1,5 @@
import type { ComponentPublicInstance } from "vue";
import type { Router } from "vue-router";
import { RoutePath } from "../router/constants";
import {
ForbiddenError,
InternalServerError,
@@ -9,6 +8,7 @@ import {
UnAuthError,
} from "../types/error";
import { z } from "zod";
import { Routes } from "../router/constants";
const makeErrorHandler =
(
@@ -31,7 +31,7 @@ const makeErrorHandler =
});
} else if (err instanceof UnAuthError) {
signOut();
router.push(RoutePath.LOGIN);
router.push(Routes.LOGIN.path);
showAlert({
level: "error",
content: err.message,

View File

@@ -1,15 +1,15 @@
<template>
<div class="px-2 sm:px-4 pt-6 sm:rounded-lg">
<div class="mb-4 sm:mb-6 col-span-full">
<Breadcrumbs :names="['用户管理', '绑定部门']" :routes="[{ name: RouteName.USERVIEW }]" />
<h1 class="text-xl sm:text-2xl mb-4 sm:mb-6 font-semibold text-gray-900">绑定部门</h1>
</div>
<div class="px-2 sm:px-4 pt-6 sm:rounded-lg">
<div class="mb-4 sm:mb-6 col-span-full">
<Breadcrumbs :names="['用户管理', '绑定部门']" :routes="[Routes.USERVIEW.fullPath()]" />
<h1 class="text-xl sm:text-2xl mb-4 sm:mb-6 font-semibold text-gray-900">绑定部门</h1>
</div>
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<div class="flex gap-x-2">
<TableButton variant="primary" @click="() => {
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<div class="flex gap-x-2">
<TableButton variant="primary" @click="() => {
if (checkedDepartmentIds.length === 0) {
alertStore.showAlert({
content: '没有选择部门',
@@ -19,9 +19,9 @@
departmentBindModal?.show();
}
}">
绑定
</TableButton>
<TableButton variant="danger" @click="() => {
绑定
</TableButton>
<TableButton variant="danger" @click="() => {
if (checkedDepartmentIds.length === 0) {
alertStore.showAlert({
content: '没有选择部门',
@@ -31,59 +31,59 @@
departmentUnbindModal?.show();
}
}">
解绑
</TableButton>
</div>
</template>
</TableFilterForm>
解绑
</TableButton>
</div>
</template>
</TableFilterForm>
<!-- 移动端卡片布局 -->
<div class="md:hidden space-y-4">
<MobileCardListWithCheckbox :items="departments || []" v-model="checkedDepartmentIds">
<template #title="{ item }">
{{ item.name }}
</template>
<template #status="{ item }">
<div class="flex items-center">
<div class="h-2.5 w-2.5 rounded-full me-2" :class="item.isBound ? 'bg-green-500' : 'bg-red-500'"></div>
<span class="text-sm">{{ item.isBound === true ? "已绑定" : "未绑定" }}</span>
</div>
</template>
<template #content="{ item }">
<div>
<p class="text-xs font-medium text-gray-600">上级部门</p>
<p class="text-sm text-gray-900 mt-0.5">{{ !item.parentName ? '无' : item.parentName }}</p>
</div>
</template>
</MobileCardListWithCheckbox>
</div>
<!-- 移动端卡片布局 -->
<div class="md:hidden space-y-4">
<MobileCardListWithCheckbox :items="departments || []" v-model="checkedDepartmentIds">
<template #title="{ item }">
{{ item.name }}
</template>
<template #status="{ item }">
<div class="flex items-center">
<div class="h-2.5 w-2.5 rounded-full me-2" :class="item.isBound ? 'bg-green-500' : 'bg-red-500'"></div>
<span class="text-sm">{{ item.isBound === true ? "已绑定" : "未绑定" }}</span>
</div>
</template>
<template #content="{ item }">
<div>
<p class="text-xs font-medium text-gray-600">上级部门</p>
<p class="text-sm text-gray-900 mt-0.5">{{ !item.parentName ? '无' : item.parentName }}</p>
</div>
</template>
</MobileCardListWithCheckbox>
</div>
<!-- PC端表格布局 -->
<div class="hidden md:block">
<TableFormLayout :items="departments || []" :columns="columns" :hasCheckbox="true" v-model="checkedDepartmentIds"
@all-checked-change="allChecked = $event">
<template #parentName="{ item }">
{{ !item.parentName ? '无' : item.parentName }}
</template>
<template #name="{ item }">
{{ item.name }}
</template>
<template #bindState="{ item }">
<div class="flex items-center">
<div class="h-2.5 w-2.5 rounded-full me-2" :class="item.isBound ? 'bg-green-500' : 'bg-red-500'"></div>
{{ item.isBound === true ? "已绑定" : "未绑定" }}
</div>
</template>
</TableFormLayout>
</div>
<TablePagination :pageChange="handlePageChange" :total="total" />
<BindModal :id="'department-bind-modal'" :closeModal="() => {
<!-- PC端表格布局 -->
<div class="hidden md:block">
<TableFormLayout :items="departments || []" :columns="columns" :hasCheckbox="true" v-model="checkedDepartmentIds"
@all-checked-change="allChecked = $event">
<template #parentName="{ item }">
{{ !item.parentName ? '无' : item.parentName }}
</template>
<template #name="{ item }">
{{ item.name }}
</template>
<template #bindState="{ item }">
<div class="flex items-center">
<div class="h-2.5 w-2.5 rounded-full me-2" :class="item.isBound ? 'bg-green-500' : 'bg-red-500'"></div>
{{ item.isBound === true ? "已绑定" : "未绑定" }}
</div>
</template>
</TableFormLayout>
</div>
<TablePagination :pageChange="handlePageChange" :total="total" />
<BindModal :id="'department-bind-modal'" :closeModal="() => {
departmentBindModal!.hide();
}" :onSubmit="handleBindDepartmentSubmit" title="绑定选中的部门吗"></BindModal>
<UnModal :id="'department-unbind-modal'" :closeModal="() => {
<UnModal :id="'department-unbind-modal'" :closeModal="() => {
departmentUnbindModal!.hide();
}" :onSubmit="handleUnbindDepartmentSubmit" title="解绑选中的部门吗"></UnModal>
</div>
</div>
</template>
@@ -99,7 +99,7 @@ import TableFormLayout from "@/components/TableFormLayout.vue";
import TablePagination from "@/components/TablePagination.vue";
import { useDepartmentQuery } from "@/composables/department/useDepartmentQuery";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
import { RouteName } from "@/router/constants";
import { Routes } from "@/router/constants";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { onMounted, reactive, ref, watch } from "vue";
import { useRoute } from "vue-router";

View File

@@ -1,15 +1,15 @@
<template>
<div class="px-2 sm:px-4 pt-6 sm:rounded-lg">
<div class="mb-4 sm:mb-6 col-span-full">
<Breadcrumbs :names="['角色管理', '绑定权限']" :routes="[{ name: RouteName.ROLEVIEW }]" />
<h1 class="text-xl sm:text-2xl mb-4 sm:mb-6 font-semibold text-gray-900">绑定权限</h1>
</div>
<div class="px-2 sm:px-4 pt-6 sm:rounded-lg">
<div class="mb-4 sm:mb-6 col-span-full">
<Breadcrumbs :names="['角色管理', '绑定权限']" :routes="[Routes.ROLEVIEW.fullPath()]" />
<h1 class="text-xl sm:text-2xl mb-4 sm:mb-6 font-semibold text-gray-900">绑定权限</h1>
</div>
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<div class="flex gap-x-2">
<TableButton variant="primary" @click="() => {
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<div class="flex gap-x-2">
<TableButton variant="primary" @click="() => {
if (checkedPermissionIds.length === 0) {
alertStore.showAlert({
content: '没有选择权限',
@@ -19,9 +19,9 @@
permissionBindModal?.show();
}
}">
绑定
</TableButton>
<TableButton variant="danger" @click="() => {
绑定
</TableButton>
<TableButton variant="danger" @click="() => {
if (checkedPermissionIds.length === 0) {
alertStore.showAlert({
content: '没有选择权限',
@@ -31,59 +31,59 @@
permissionUnbindModal?.show();
}
}">
解绑
</TableButton>
</div>
</template>
</TableFilterForm>
解绑
</TableButton>
</div>
</template>
</TableFilterForm>
<!-- 移动端卡片布局 -->
<div class="md:hidden space-y-4">
<MobileCardListWithCheckbox :items="permissions || []" v-model="checkedPermissionIds">
<template #title="{ item }">
{{ item.name }}
</template>
<template #status="{ item }">
<div class="flex items-center">
<div class="h-2.5 w-2.5 rounded-full me-2" :class="item.isBound ? 'bg-green-500' : 'bg-red-500'"></div>
<span class="text-sm">{{ item.isBound === true ? "已绑定" : "未绑定" }}</span>
</div>
</template>
<template #content="{ item }">
<div>
<p class="text-xs font-medium text-gray-600">权限编码</p>
<p class="text-sm text-gray-900 mt-0.5">{{ item.code }}</p>
</div>
</template>
</MobileCardListWithCheckbox>
</div>
<!-- 移动端卡片布局 -->
<div class="md:hidden space-y-4">
<MobileCardListWithCheckbox :items="permissions || []" v-model="checkedPermissionIds">
<template #title="{ item }">
{{ item.name }}
</template>
<template #status="{ item }">
<div class="flex items-center">
<div class="h-2.5 w-2.5 rounded-full me-2" :class="item.isBound ? 'bg-green-500' : 'bg-red-500'"></div>
<span class="text-sm">{{ item.isBound === true ? "已绑定" : "未绑定" }}</span>
</div>
</template>
<template #content="{ item }">
<div>
<p class="text-xs font-medium text-gray-600">权限编码</p>
<p class="text-sm text-gray-900 mt-0.5">{{ item.code }}</p>
</div>
</template>
</MobileCardListWithCheckbox>
</div>
<!-- PC端表格布局 -->
<div class="hidden md:block">
<TableFormLayout :items="permissions || []" :columns="columns" :hasCheckbox="true" v-model="checkedPermissionIds"
@all-checked-change="allChecked = $event">
<template #code="{ item }">
{{ item.code }}
</template>
<template #name="{ item }">
{{ item.name }}
</template>
<template #bindState="{ item }">
<div class="flex items-center">
<div class="h-2.5 w-2.5 rounded-full me-2" :class="item.isBound ? 'bg-green-500' : 'bg-red-500'"></div>
{{ item.isBound === true ? "已绑定" : "未绑定" }}
</div>
</template>
</TableFormLayout>
</div>
<!-- PC端表格布局 -->
<div class="hidden md:block">
<TableFormLayout :items="permissions || []" :columns="columns" :hasCheckbox="true" v-model="checkedPermissionIds"
@all-checked-change="allChecked = $event">
<template #code="{ item }">
{{ item.code }}
</template>
<template #name="{ item }">
{{ item.name }}
</template>
<template #bindState="{ item }">
<div class="flex items-center">
<div class="h-2.5 w-2.5 rounded-full me-2" :class="item.isBound ? 'bg-green-500' : 'bg-red-500'"></div>
{{ item.isBound === true ? "已绑定" : "未绑定" }}
</div>
</template>
</TableFormLayout>
</div>
<TablePagination :pageChange="handlePageChange" :total="total" />
</div>
<TablePagination :pageChange="handlePageChange" :total="total" />
</div>
<BindModal :id="'permission-bind-modal'" :closeModal="() => {
<BindModal :id="'permission-bind-modal'" :closeModal="() => {
permissionBindModal!.hide();
}" :onSubmit="handleBindPermissionSubmit" title="确定绑定选中的权限吗"></BindModal>
<UnModal :id="'permission-unbind-modal'" :closeModal="() => {
<UnModal :id="'permission-unbind-modal'" :closeModal="() => {
permissionUnbindModal!.hide();
}" :onSubmit="handleUnbindPermissionSubmit" title="确定解绑选中的权限吗"></UnModal>
</template>
@@ -99,7 +99,7 @@ import type { FilterItem } from "@/components/TableFilterForm.vue";
import TableFormLayout from "@/components/TableFormLayout.vue";
import TablePagination from "@/components/TablePagination.vue";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
import { RouteName } from "@/router/constants";
import { Routes } from "@/router/constants";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { onMounted, reactive, ref, watch } from "vue";
import { useRoute } from "vue-router";

View File

@@ -1,15 +1,15 @@
<template>
<div class="px-2 sm:px-4 pt-6 sm:rounded-lg">
<div class="mb-4 sm:mb-6 col-span-full">
<Breadcrumbs :names="['用户管理', '绑定岗位']" :routes="[{ name: RouteName.USERVIEW }]" />
<h1 class="text-xl sm:text-2xl mb-4 sm:mb-6 font-semibold text-gray-900">绑定岗位</h1>
</div>
<div class="px-2 sm:px-4 pt-6 sm:rounded-lg">
<div class="mb-4 sm:mb-6 col-span-full">
<Breadcrumbs :names="['用户管理', '绑定岗位']" :routes="[Routes.USERVIEW.fullPath()]" />
<h1 class="text-xl sm:text-2xl mb-4 sm:mb-6 font-semibold text-gray-900">绑定岗位</h1>
</div>
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<div class="flex gap-x-2">
<TableButton variant="primary" @click="() => {
<TableFilterForm :filters="filterConfig" :initialValues="filterValues" @search="handleSearch"
@update:values="updateFilterValues">
<template #actions>
<div class="flex gap-x-2">
<TableButton variant="primary" @click="() => {
if (checkedPositionIds.length === 0) {
alertStore.showAlert({
content: '没有选择岗位',
@@ -19,9 +19,9 @@
positionBindModal?.show();
}
}">
绑定
</TableButton>
<TableButton variant="danger" @click="() => {
绑定
</TableButton>
<TableButton variant="danger" @click="() => {
if (checkedPositionIds.length === 0) {
alertStore.showAlert({
content: '没有选择岗位',
@@ -31,50 +31,50 @@
positionUnbindModal?.show();
}
}">
解绑
</TableButton>
</div>
</template>
</TableFilterForm>
解绑
</TableButton>
</div>
</template>
</TableFilterForm>
<!-- 移动端卡片布局 -->
<div class="md:hidden space-y-4">
<MobileCardListWithCheckbox :items="positions || []" v-model="checkedPositionIds">
<template #title="{ item }">
{{ item.name }}
</template>
<template #status="{ item }">
<div class="flex items-center">
<div class="h-2.5 w-2.5 rounded-full me-2" :class="item.isBound ? 'bg-green-500' : 'bg-red-500'"></div>
<span class="text-sm">{{ item.isBound === true ? "已绑定" : "未绑定" }}</span>
</div>
</template>
</MobileCardListWithCheckbox>
</div>
<!-- 移动端卡片布局 -->
<div class="md:hidden space-y-4">
<MobileCardListWithCheckbox :items="positions || []" v-model="checkedPositionIds">
<template #title="{ item }">
{{ item.name }}
</template>
<template #status="{ item }">
<div class="flex items-center">
<div class="h-2.5 w-2.5 rounded-full me-2" :class="item.isBound ? 'bg-green-500' : 'bg-red-500'"></div>
<span class="text-sm">{{ item.isBound === true ? "已绑定" : "未绑定" }}</span>
</div>
</template>
</MobileCardListWithCheckbox>
</div>
<!-- PC端表格布局 -->
<div class="hidden md:block">
<TableFormLayout :items="positions || []" :columns="columns" :hasCheckbox="true" v-model="checkedPositionIds"
@all-checked-change="allChecked = $event">
<template #name="{ item }">
{{ item.name }}
</template>
<template #bindState="{ item }">
<div class="flex items-center">
<div class="h-2.5 w-2.5 rounded-full me-2" :class="item.isBound ? 'bg-green-500' : 'bg-red-500'"></div>
{{ item.isBound === true ? "已绑定" : "未绑定" }}
</div>
</template>
</TableFormLayout>
</div>
<!-- PC端表格布局 -->
<div class="hidden md:block">
<TableFormLayout :items="positions || []" :columns="columns" :hasCheckbox="true" v-model="checkedPositionIds"
@all-checked-change="allChecked = $event">
<template #name="{ item }">
{{ item.name }}
</template>
<template #bindState="{ item }">
<div class="flex items-center">
<div class="h-2.5 w-2.5 rounded-full me-2" :class="item.isBound ? 'bg-green-500' : 'bg-red-500'"></div>
{{ item.isBound === true ? "已绑定" : "未绑定" }}
</div>
</template>
</TableFormLayout>
</div>
<TablePagination :pageChange="handlePageChange" :total="total" />
</div>
<TablePagination :pageChange="handlePageChange" :total="total" />
</div>
<BindModal :id="'position-bind-modal'" :closeModal="() => {
<BindModal :id="'position-bind-modal'" :closeModal="() => {
positionBindModal!.hide();
}" :onSubmit="handleBindPositionSubmit" title="绑定选中的岗位吗"></BindModal>
<UnModal :id="'position-unbind-modal'" :closeModal="() => {
<UnModal :id="'position-unbind-modal'" :closeModal="() => {
positionUnbindModal!.hide();
}" :onSubmit="handleUnbindPositionSubmit" title="解绑选中的岗位吗"></UnModal>
</template>
@@ -93,7 +93,7 @@ import { usePositionBind } from "@/composables/position/usePositionBind";
import { usePositionQuery } from "@/composables/position/usePositionQuery";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
import { useMobileStyles } from "@/composables/useMobileStyles";
import { RouteName } from "@/router/constants";
import { Routes } from "@/router/constants";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { onMounted, reactive, ref, watch } from "vue";
import { useRoute } from "vue-router";

View File

@@ -1,7 +1,7 @@
<template>
<div class="px-2 sm:px-4 pt-6 sm:rounded-lg">
<div class="mb-4 sm:mb-6 col-span-full">
<Breadcrumbs :names="['用户管理', '角色分配']" :routes="[{ name: RouteName.USERVIEW }]" />
<Breadcrumbs :names="['用户管理', '角色分配']" :routes="[Routes.USERVIEW.fullPath()]" />
<h1 class="text-xl sm:text-2xl mb-4 sm:mb-6 font-semibold text-gray-900">角色分配</h1>
</div>
@@ -101,7 +101,7 @@ import TableFormLayout from "@/components/TableFormLayout.vue";
import TablePagination from "@/components/TablePagination.vue";
import { useRolesQuery } from "@/composables/role/useRolesQuery";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
import { RouteName } from "@/router/constants";
import { Routes } from "@/router/constants";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { onMounted, reactive, ref } from "vue";
import { useRoute } from "vue-router";

View File

@@ -33,7 +33,7 @@ import { useRoute, useRouter } from "vue-router";
import { z } from "zod";
import useUserAuth from "../composables/auth/useUserAuth";
import useAlertStore from "../composables/store/useAlertStore";
import { RoutePath } from "../router/constants";
import { Routes } from "../router/constants";
const username = ref("admin");
const password = ref("admin");
@@ -58,8 +58,7 @@ const handleLogin = async () => {
content: "登录成功",
});
const redirectPath =
(route.query.redirect as string) ||
`${RoutePath.DASHBOARD}/${RoutePath.USERVIEW}`;
(route.query.redirect as string) || Routes.USERVIEW.fullPath();
router.push(redirectPath);
};

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { RoutePath } from "../router/constants";
import { Routes } from "../router/constants";
</script>
<template>
@@ -10,7 +10,7 @@ import { RoutePath } from "../router/constants";
found</h1>
<p class="mt-6 text-base sm:text-lg font-medium text-pretty text-gray-500">您访问的资源未找到请点击浏览器后退按钮返回</p>
<div class="mt-10 flex items-center justify-center gap-x-6">
<RouterLink :to="`${RoutePath.DASHBOARD}/${RoutePath.OVERVIEW}`"
<RouterLink :to="Routes.OVERVIEW.fullPath()"
class="rounded-md px-3.5 py-2.5 text-sm font-semibold bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 text-white shadow-xs focus-visible:outline-2 focus-visible:outline-offset-2">
回到主页</RouterLink>
<a href="#" class="text-sm font-semibold text-gray-900">联系我们<span aria-hidden="true">&rarr;</span></a>

View File

@@ -135,7 +135,7 @@ import PlusIcon from "@/components/icons/PlusIcon.vue";
import useRoleDelete from "@/composables/role/useRoleDelete";
import { useRolesQuery } from "@/composables/role/useRolesQuery";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
import { RouteName } from "@/router/constants";
import { Routes } from "@/router/constants";
import type { RoleUpsertModel } from "@/types/role";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
import { nextTick, onMounted, reactive, ref } from "vue";
@@ -169,7 +169,7 @@ const updateFilterValues = (
}
};
const selectedRole = ref<components["schemas"]["RoleDto"]>();
const selectedRole = ref<components["schemas"]["RoleRespDto"]>();
const roleUpsertModal = ref<ModalInterface>();
const roleDeleteModal = ref<ModalInterface>();
const actionExcStore = useActionExcStore();
@@ -223,7 +223,7 @@ const handleUpsertModalSubmit = async (data: RoleUpsertModel) => {
};
const handleUpsertRoleClick = async (
role?: components["schemas"]["RoleDto"],
role?: components["schemas"]["RoleRespDto"],
) => {
selectedRole.value = role;
await nextTick(() => {
@@ -232,14 +232,13 @@ const handleUpsertRoleClick = async (
};
const handleBindPermissionClick = async (
role: components["schemas"]["RoleDto"],
role: components["schemas"]["RoleRespDto"],
) => {
router.push({
name: RouteName.BINDPERMISSIONVIEW,
params: {
roleId: role.id,
},
});
router.push(
Routes.BINDPERMISSIONVIEW.withParams({
roleId: role.id!,
}),
);
};
const handleDeletedModalSubmit = async () => {
@@ -256,7 +255,7 @@ const handleDeletedModalSubmit = async () => {
};
const handleDeleteRoleClick = async (
role: components["schemas"]["RoleDto"],
role: components["schemas"]["RoleRespDto"],
) => {
selectedRole.value = role;
await nextTick(() => {

View File

@@ -52,14 +52,14 @@
</template>
<script setup lang="ts">
import Breadcrumbs from "@/components/Breadcrumbs.vue";
import useUserAuth from "@/composables/auth/useUserAuth";
import useUserStore from "@/composables/store/useUserStore";
import { initFlowbite } from "flowbite";
import { onMounted, ref } from "vue";
import { z } from "zod";
import useAlertStore from "../composables/store/useAlertStore";
import { RouteName } from "../router/constants";
import Breadcrumbs from "@/components/Breadcrumbs.vue";
import { Routes } from "../router/constants";
const { user } = useUserStore();
const { upsertCurrentUser } = useUserAuth();

View File

@@ -174,7 +174,7 @@ import { useSort } from "@/composables/sort";
import { useActionExcStore } from "@/composables/store/useActionExcStore";
import useUserDelete from "@/composables/user/useUserDelete";
import { useUserQuery } from "@/composables/user/useUserQuery";
import { RouteName } from "@/router/constants";
import { Routes } from "@/router/constants";
import type { UserUpsertSubmitModel } from "@/types/user";
import { dayjs, formatDate } from "@/utils/dateUtil";
import { Modal, type ModalInterface, initFlowbite } from "flowbite";
@@ -289,34 +289,31 @@ const handleUpsertUserClick = async (
const handleBindRoleClick = async (
user: components["schemas"]["UserRolePermissionDto"],
) => {
router.push({
name: RouteName.BINDROLEVIEW,
params: {
userId: user.id,
},
});
router.push(
Routes.BINDROLEVIEW.withParams({
userId: user.id!,
}),
);
};
const handleBindDepartmentClick = async (
user: components["schemas"]["UserRolePermissionDto"],
) => {
router.push({
name: RouteName.BINDDEPARTMENTVIEW,
params: {
userId: user.id,
},
});
router.push(
Routes.BINDDEPARTMENTVIEW.withParams({
userId: user.id!,
}),
);
};
const handleBindPositionClick = async (
user: components["schemas"]["UserRolePermissionDto"],
) => {
router.push({
name: RouteName.BINDPOSITIONVIEW,
params: {
userId: user.id,
},
});
router.push(
Routes.BINDPOSITIONVIEW.withParams({
userId: user.id!,
}),
);
};
const handleSortClick = async (field: string) => {