feat(product): 新增产品管理模块与字典组件功能

- 新增产品管理相关路由和页面(dashboard、list、requirement、setting)
- 实现产品基础信息编辑弹窗组件(base-info-dialog.vue)
- 添加运行时字典功能(dict-select、dict-text、dict-tag组件)
- 集成字典管理store和API调用
- 规范ID类型定义为string避免精度丢失问题
- 完善国际化资源文件支持中英文对照
- 新增对象上下文业务域入口页导航实现说明
- 添加Vue DevTools浮动入口注释说明
- 统一权限控制支持全局和对象作用域区分
- 规范分页查询参数类型定义与使用方式
This commit is contained in:
2026-04-23 09:05:55 +08:00
parent c5911ea34b
commit 4122dfa50d
95 changed files with 9581 additions and 801 deletions

View File

@@ -68,6 +68,14 @@ export function fetchGetDictDataPage(params: Api.Dict.DictDataSearchParams) {
});
}
/** 获取前端运行时字典缓存 */
export function fetchGetFrontendDictCache() {
return request<Api.Dict.FrontendDictCache>({
url: `${DICT_DATA_PREFIX}/frontend-cache`,
method: 'get'
});
}
/** 创建字典数据 */
export function fetchCreateDictData(data: Api.Dict.SaveDictDataParams) {
return request<number>({

View File

@@ -1,4 +1,6 @@
export * from './auth';
export * from './dict';
export * from './object-context';
export * from './product';
export * from './route';
export * from './system-manage';

View File

@@ -0,0 +1,202 @@
import type { LocationQueryValue } from 'vue-router';
import { request } from '../request';
import {
type ServiceRequestResult,
normalizeNullableStringId,
normalizeStringId,
safeJsonRequestConfig
} from './shared';
interface BackendObjectContextMenuDTO {
key?: string | null;
label?: string | null;
routeKey?: string | null;
routePath?: string | null;
id?: string | number | null;
name?: string | null;
path?: string | null;
children?: BackendObjectContextMenuDTO[] | null;
}
interface BackendProductContextProductDTO {
id?: string | number | null;
code?: string | null;
directionCode?: string | null;
name?: string | null;
managerUserId?: string | number | null;
statusCode?: string | null;
}
interface BackendProductContextRoleDTO {
roleId?: string | number | null;
roleCode?: string | null;
roleName?: string | null;
}
interface BackendObjectContextDTO {
domainKey?: string | null;
objectType?: string | null;
objectId?: string | number | null;
objectName?: string | null;
objectSummary?: Record<string, unknown> | null;
menus?: BackendObjectContextMenuDTO[] | null;
contextScopedMenus?: BackendObjectContextMenuDTO[] | null;
buttonCodes?: string[] | null;
currentProduct?: BackendProductContextProductDTO | null;
currentRole?: BackendProductContextRoleDTO | null;
navs?: BackendObjectContextMenuDTO[] | null;
buttons?: string[] | null;
defaultRouteKey?: string | null;
defaultRoutePath?: string | null;
}
function normalizeString(value: string | number | null | undefined) {
if (value === null || value === undefined) {
return '';
}
return String(value);
}
function normalizeRoutePath(path: string | null | undefined) {
const normalizedPath = normalizeString(path).trim();
if (!normalizedPath) {
return '';
}
if (normalizedPath.startsWith('/')) {
return normalizedPath;
}
return `/${normalizedPath}`;
}
function normalizeCurrentProduct(
product: BackendProductContextProductDTO
): Record<'id' | 'code' | 'directionCode' | 'name' | 'managerUserId' | 'statusCode', string> {
return {
id: normalizeStringId(product.id || ''),
code: normalizeString(product.code),
directionCode: normalizeString(product.directionCode),
name: normalizeString(product.name),
managerUserId: normalizeNullableStringId(product.managerUserId) ?? '',
statusCode: normalizeString(product.statusCode)
};
}
function normalizeCurrentRole(role: BackendProductContextRoleDTO) {
return {
roleId: normalizeStringId(role.roleId || ''),
roleCode: normalizeString(role.roleCode),
roleName: normalizeString(role.roleName)
};
}
function normalizeMenu(menu: BackendObjectContextMenuDTO): App.ObjectContext.Menu {
const routeKey = normalizeString(menu.routeKey);
const routePath = normalizeRoutePath(menu.routePath || menu.path);
const key = normalizeString(menu.key || routeKey || routePath || menu.id);
return {
key,
label: normalizeString(menu.label || menu.name),
routeKey: routeKey || null,
routePath: routePath || null,
children: menu.children?.map(child => normalizeMenu(child)) || []
};
}
function getFirstRoutableMenu(menus: App.ObjectContext.Menu[]): App.ObjectContext.Menu | null {
for (const menu of menus) {
if (menu.routeKey || menu.routePath) {
return menu;
}
const firstChildMenu = menu.children?.length ? getFirstRoutableMenu(menu.children) : null;
if (firstChildMenu) {
return firstChildMenu;
}
}
return null;
}
function normalizeObjectSummary(data: BackendObjectContextDTO): App.ObjectContext.Summary | null {
if (data.objectSummary) {
return data.objectSummary;
}
const summary: App.ObjectContext.Summary = {};
if (data.currentProduct) {
summary.currentProduct = normalizeCurrentProduct(data.currentProduct);
}
if (data.currentRole !== undefined) {
summary.currentRole = data.currentRole ? normalizeCurrentRole(data.currentRole) : null;
}
return Object.keys(summary).length ? summary : null;
}
function createContextApiUrl(config: App.ObjectContext.DomainConfig, objectId: string) {
if (config.contextApiObjectIdPlacement !== 'path') {
return config.contextApiPath;
}
const placeholder = `{${config.contextApiObjectIdParamKey}}`;
return config.contextApiPath.replace(placeholder, encodeURIComponent(objectId));
}
function normalizeObjectContext(
config: App.ObjectContext.DomainConfig,
objectId: string,
data: BackendObjectContextDTO
): Api.ObjectContext.ContextInfo {
const rawMenus = data.contextScopedMenus ?? data.menus ?? data.navs ?? [];
const contextScopedMenus = rawMenus.map(menu => normalizeMenu(menu));
const firstRoutableMenu = getFirstRoutableMenu(contextScopedMenus);
const currentProduct = data.currentProduct ? normalizeCurrentProduct(data.currentProduct) : null;
return {
domainKey: (data.domainKey || config.domainKey) as App.ObjectContext.DomainKey,
objectType: (data.objectType || config.objectType) as App.ObjectContext.ObjectType,
objectId: normalizeString(data.objectId) || currentProduct?.id || objectId,
objectName: normalizeString(data.objectName || currentProduct?.name),
objectSummary: normalizeObjectSummary(data),
contextScopedMenus,
buttonCodes: data.buttonCodes ?? data.buttons ?? [],
defaultRouteKey: data.defaultRouteKey || firstRoutableMenu?.routeKey || '',
defaultRoutePath:
normalizeRoutePath(data.defaultRoutePath) || firstRoutableMenu?.routePath || config.fallbackDefaultRoutePath
};
}
export async function fetchGetObjectContext(
config: App.ObjectContext.DomainConfig,
objectId: string
): Promise<ServiceRequestResult<Api.ObjectContext.ContextInfo>> {
const result = await request<BackendObjectContextDTO>({
...safeJsonRequestConfig,
url: createContextApiUrl(config, objectId),
method: 'get',
params:
config.contextApiObjectIdPlacement === 'path'
? undefined
: ({
[config.contextApiObjectIdParamKey]: objectId
} satisfies Record<string, LocationQueryValue | LocationQueryValue[]>)
});
if (result.error || !result.data) {
return result as ServiceRequestResult<Api.ObjectContext.ContextInfo>;
}
return {
...result,
data: normalizeObjectContext(config, objectId, result.data)
};
}

View File

@@ -0,0 +1,83 @@
import { normalizeNullableStringId, normalizeStringId } from './shared';
type ProductStatusCode = Api.Product.ProductStatusCode;
type ProductStatusActionCode = Api.Product.ProductStatusActionCode;
interface ProductSettingsResponse {
baseInfo: {
id: string | number;
code: string;
directionCode: string;
name: string;
managerUserId?: string | number | null;
managerUserNickname?: string | null;
description?: string | null;
statusCode: ProductStatusCode;
lastStatusReason?: string | null;
};
lifecycle: {
statusCode: ProductStatusCode;
lastStatusReason?: string | null;
availableActions?: Array<{
actionCode: ProductStatusActionCode;
actionName: string;
needReason: boolean;
}> | null;
};
}
interface ProductMemberResponse {
id: string | number;
userId: string | number;
userNickname: string;
roleId: string | number;
roleName: string;
roleCode: string;
managerFlag: boolean;
status: 0 | 1;
joinedTime: string;
leftTime?: string | null;
remark?: string | null;
}
export function normalizeProductSettings(response: ProductSettingsResponse): Api.Product.ProductSettings {
return {
baseInfo: {
id: normalizeStringId(response.baseInfo.id),
code: response.baseInfo.code || '',
directionCode: response.baseInfo.directionCode || '',
name: response.baseInfo.name || '',
managerUserId: normalizeNullableStringId(response.baseInfo.managerUserId) ?? '',
managerUserNickname: response.baseInfo.managerUserNickname || '',
description: response.baseInfo.description ?? null,
statusCode: response.baseInfo.statusCode,
lastStatusReason: response.baseInfo.lastStatusReason ?? null
},
lifecycle: {
statusCode: response.lifecycle.statusCode,
lastStatusReason: response.lifecycle.lastStatusReason ?? null,
availableActions:
response.lifecycle.availableActions?.map(item => ({
actionCode: item.actionCode,
actionName: item.actionName,
needReason: item.needReason
})) ?? []
}
};
}
export function normalizeProductMember(response: ProductMemberResponse): Api.Product.ProductMember {
return {
id: normalizeStringId(response.id),
userId: normalizeStringId(response.userId),
userNickname: response.userNickname || '',
roleId: normalizeStringId(response.roleId),
roleName: response.roleName || '',
roleCode: response.roleCode || '',
managerFlag: Boolean(response.managerFlag),
status: response.status,
joinedTime: response.joinedTime,
leftTime: response.leftTime ?? null,
remark: response.remark ?? null
};
}

157
src/service/api/product.ts Normal file
View File

@@ -0,0 +1,157 @@
import { WEB_SERVICE_PREFIX } from '@/constants/service';
import { request } from '../request';
import {
type ServiceRequestResult,
mapServiceResult,
normalizeNullableStringId,
normalizeStringId,
safeJsonRequestConfig
} from './shared';
import { normalizeProductMember, normalizeProductSettings } from './product-shared';
const PRODUCT_PREFIX = `${WEB_SERVICE_PREFIX}/project/product`;
type ProductResponse = Omit<Api.Product.Product, 'id' | 'managerUserId'> & {
id: string | number;
managerUserId?: string | number | null;
};
type ProductPageResponse = Api.Product.PageResult<ProductResponse>;
function normalizeProduct(product: ProductResponse): Api.Product.Product {
return {
...product,
id: normalizeStringId(product.id),
managerUserId: normalizeNullableStringId(product.managerUserId) ?? ''
};
}
/** 鑾峰彇浜у搧鍒嗛〉 */
export async function fetchGetProductPage(params?: Api.Product.ProductSearchParams) {
const result = await request<ProductPageResponse>({
...safeJsonRequestConfig,
url: `${PRODUCT_PREFIX}/page`,
method: 'get',
params
});
return mapServiceResult(result as ServiceRequestResult<ProductPageResponse>, data => ({
...data,
list: data.list.map(normalizeProduct)
}));
}
/** 鑾峰彇浜у搧璇︽儏 */
export async function fetchGetProduct(id: string) {
const result = await request<ProductResponse>({
...safeJsonRequestConfig,
url: `${PRODUCT_PREFIX}/get`,
method: 'get',
params: { id }
});
return mapServiceResult(result as ServiceRequestResult<ProductResponse>, normalizeProduct);
}
/** 鍒涘缓浜у搧 */
export async function fetchCreateProduct(data: Api.Product.SaveProductParams) {
const result = await request<string | number>({
...safeJsonRequestConfig,
url: `${PRODUCT_PREFIX}/create`,
method: 'post',
data
});
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
}
/** 鏇存柊浜у搧 */
export function fetchUpdateProduct(data: Api.Product.UpdateProductParams) {
return request<boolean>({
url: `${PRODUCT_PREFIX}/update`,
method: 'put',
data
});
}
/** 鍙樻洿浜у搧鐘舵€? */
export function fetchChangeProductStatus(data: Api.Product.ChangeProductStatusParams) {
return request<boolean>({
url: `${PRODUCT_PREFIX}/change-status`,
method: 'post',
data
});
}
/** 鍒犻櫎浜у搧 */
export function fetchDeleteProduct(data: Api.Product.DeleteProductParams) {
return request<boolean>({
url: `${PRODUCT_PREFIX}/delete`,
method: 'post',
data
});
}
export async function fetchGetProductSettings(id: string) {
const result = await request<Api.Product.ProductSettings>({
...safeJsonRequestConfig,
url: `${PRODUCT_PREFIX}/${id}/settings`,
method: 'get'
});
return mapServiceResult(result as ServiceRequestResult<Api.Product.ProductSettings>, normalizeProductSettings);
}
export function fetchUpdateProductSettingBaseInfo(id: string, data: Api.Product.UpdateProductSettingBaseInfoParams) {
return request<boolean>({
...safeJsonRequestConfig,
url: `${PRODUCT_PREFIX}/${id}/settings/base-info`,
method: 'put',
data
});
}
export async function fetchGetProductMembers(id: string) {
const result = await request<Api.Product.ProductMember[]>({
...safeJsonRequestConfig,
url: `${PRODUCT_PREFIX}/${id}/members`,
method: 'get'
});
return mapServiceResult(result as ServiceRequestResult<Api.Product.ProductMember[]>, data =>
data.map(normalizeProductMember)
);
}
export async function fetchCreateProductMember(id: string, data: Api.Product.CreateProductMemberParams) {
const result = await request<string | number>({
...safeJsonRequestConfig,
url: `${PRODUCT_PREFIX}/${id}/members`,
method: 'post',
data
});
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
}
export function fetchUpdateProductMember(id: string, memberId: string, data: Api.Product.UpdateProductMemberParams) {
return request<boolean>({
...safeJsonRequestConfig,
url: `${PRODUCT_PREFIX}/${id}/members/${memberId}`,
method: 'put',
data
});
}
export function fetchInactiveProductMember(
id: string,
memberId: string,
data: Api.Product.InactiveProductMemberParams
) {
return request<boolean>({
...safeJsonRequestConfig,
url: `${PRODUCT_PREFIX}/${id}/members/${memberId}/inactive`,
method: 'post',
data
});
}

View File

@@ -1,7 +1,10 @@
import type { LastLevelRouteKey } from '@elegant-router/types';
import type { RouteMeta } from 'vue-router';
import type { ElegantConstRoute, LastLevelRouteKey } from '@elegant-router/types';
import { objectContextDomainConfigs } from '@/constants/object-context';
import { SYSTEM_SERVICE_PREFIX } from '@/constants/service';
import { createStaticRoutes } from '@/router/routes';
import { request } from '../request';
import type { ServiceRequestResult } from './shared';
import { type ServiceRequestResult, safeJsonRequestConfig } from './shared';
type BackendMenuRoute = Omit<Api.Route.MenuRoute, 'id' | 'children'> & {
id: string | number;
@@ -15,6 +18,12 @@ interface BackendUserRouteDTO {
let userRoutePromise: Promise<ServiceRequestResult<BackendUserRouteDTO>> | null = null;
const staticObjectContextRouteMap = new Map<App.ObjectContext.DomainKey, ElegantConstRoute>(
createStaticRoutes()
.authRoutes.filter(route => objectContextDomainConfigs.some(config => config.domainKey === route.name))
.map(route => [route.name as App.ObjectContext.DomainKey, route])
);
export function clearUserRouteCache() {
userRoutePromise = null;
}
@@ -27,22 +36,117 @@ function normalizeMenuRoute(route: BackendMenuRoute): Api.Route.MenuRoute {
};
}
function normalizePath(path?: string | null) {
if (!path) {
return '/';
}
return path.endsWith('/') && path !== '/' ? path.slice(0, -1) : path;
}
function isPathMatchedByPrefix(path: string, prefix: string) {
const normalizedPath = normalizePath(path);
const normalizedPrefix = normalizePath(prefix);
return normalizedPath === normalizedPrefix || normalizedPath.startsWith(`${normalizedPrefix}/`);
}
function isTopLevelObjectContextEntryRoute(route: Api.Route.MenuRoute, config: App.ObjectContext.DomainConfig) {
const routePath = normalizePath(route.path);
return (
route.component?.startsWith('view.') &&
!route.children?.length &&
(route.name === config.entryRouteKey || routePath === normalizePath(config.entryRoutePath))
);
}
function cloneStaticRouteAsMenuRoute(route: ElegantConstRoute, idPrefix: string): Api.Route.MenuRoute {
return {
...route,
id: `${idPrefix}:${String(route.name || route.path)}`,
children: route.children?.map(child => cloneStaticRouteAsMenuRoute(child, idPrefix))
};
}
function replaceWithStaticObjectContextDomainRoute(routes: Api.Route.MenuRoute[]) {
let normalizedRoutes = [...routes];
objectContextDomainConfigs.forEach(config => {
const hasDomainRootRoute = normalizedRoutes.some(route => route.name === config.domainKey);
if (hasDomainRootRoute) {
return;
}
const domainTopLevelRoutes = normalizedRoutes.filter(route =>
config.routePathPrefixes.some(prefix => isPathMatchedByPrefix(route.path, prefix))
);
const entryRoute = domainTopLevelRoutes.find(route => isTopLevelObjectContextEntryRoute(route, config));
if (!entryRoute) {
return;
}
const staticDomainRoute = staticObjectContextRouteMap.get(config.domainKey);
if (!staticDomainRoute) {
return;
}
const wrappedDomainRoute = cloneStaticRouteAsMenuRoute(staticDomainRoute, `object-context:${config.domainKey}`);
const entryRouteIndex = normalizedRoutes.findIndex(route => route.id === entryRoute.id);
const domainRouteIds = new Set(domainTopLevelRoutes.map(route => route.id));
if (entryRoute.meta) {
const nextMeta: RouteMeta = {
title: wrappedDomainRoute.meta?.title || config.domainKey,
...(wrappedDomainRoute.meta || {})
};
if (entryRoute.meta.icon) {
nextMeta.icon = entryRoute.meta.icon;
}
if (entryRoute.meta.localIcon) {
nextMeta.localIcon = entryRoute.meta.localIcon;
}
if (entryRoute.meta.order !== undefined) {
nextMeta.order = entryRoute.meta.order;
}
wrappedDomainRoute.meta = nextMeta;
}
normalizedRoutes = normalizedRoutes.filter(route => !domainRouteIds.has(route.id));
normalizedRoutes.splice(entryRouteIndex < 0 ? normalizedRoutes.length : entryRouteIndex, 0, wrappedDomainRoute);
});
return normalizedRoutes;
}
function normalizeUserRoute(data: BackendUserRouteDTO): Api.Route.UserRoute {
return {
routes: (data.routes ?? []).map(route => normalizeMenuRoute(route)),
routes: replaceWithStaticObjectContextDomainRoute((data.routes ?? []).map(route => normalizeMenuRoute(route))),
home: (data.home || 'system_user') as LastLevelRouteKey
};
}
/** 获取常量路由 */
export function fetchGetConstantRoutes() {
return request<Api.Route.MenuRoute[]>({ url: '/route/getConstantRoutes' });
return request<Api.Route.MenuRoute[]>({
...safeJsonRequestConfig,
url: '/route/getConstantRoutes'
});
}
/** 获取用户路由 */
export async function fetchGetUserRoutes(force = false): Promise<ServiceRequestResult<Api.Route.UserRoute>> {
if (!userRoutePromise || force) {
userRoutePromise = request<BackendUserRouteDTO>({
...safeJsonRequestConfig,
url: `${SYSTEM_SERVICE_PREFIX}/auth/get-user-routes`
}).then(result => result as ServiceRequestResult<BackendUserRouteDTO>);
}

View File

@@ -1,4 +1,5 @@
import type { AxiosError, AxiosResponse } from 'axios';
import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
import { safeJsonTransformResponse } from '../request/json';
export type ServiceRequestResult<T> =
| {
@@ -11,3 +12,33 @@ export type ServiceRequestResult<T> =
error: AxiosError<App.Service.Response<unknown>>;
response: AxiosResponse<App.Service.Response<unknown>> | undefined;
};
export const safeJsonRequestConfig: Pick<AxiosRequestConfig, 'transformResponse'> = {
transformResponse: [safeJsonTransformResponse]
};
export function normalizeStringId(id: string | number) {
return String(id);
}
export function normalizeNullableStringId(id: string | number | null | undefined) {
if (id === null || id === undefined || id === '') {
return null;
}
return String(id);
}
export function mapServiceResult<TInput, TOutput>(
result: ServiceRequestResult<TInput>,
mapper: (data: TInput) => TOutput
): ServiceRequestResult<TOutput> {
if (result.error || result.data === null) {
return result as ServiceRequestResult<TOutput>;
}
return {
...result,
data: mapper(result.data)
};
}

View File

@@ -1,6 +1,12 @@
import { SYSTEM_SERVICE_PREFIX } from '@/constants/service';
import { request } from '../request';
import type { ServiceRequestResult } from './shared';
import {
type ServiceRequestResult,
mapServiceResult,
normalizeNullableStringId,
normalizeStringId,
safeJsonRequestConfig
} from './shared';
import UserManagementRelationQueryReqVO = Api.SystemManage.UserManagementRelationQueryReqVO;
const ROLE_PREFIX = `${SYSTEM_SERVICE_PREFIX}/role`;
@@ -45,10 +51,18 @@ function createRolePageQuery(params?: Api.SystemManage.RoleSearchParams) {
}
});
if (params.scopeType) {
query.append('scopeType', params.scopeType);
}
if (params.objectType) {
query.append('objectType', params.objectType);
}
return query.toString();
}
function createBatchDeleteQuery(ids: number[]) {
function createBatchDeleteQuery(ids: Array<string | number>) {
const query = new URLSearchParams();
ids.forEach(id => {
@@ -58,39 +72,160 @@ function createBatchDeleteQuery(ids: number[]) {
return query.toString();
}
type UserSimpleResponse = Omit<Api.SystemManage.UserSimple, 'id'> & {
id: string | number;
};
type RoleResponse = Omit<Api.SystemManage.Role, 'id'> & {
id: string | number;
};
type RolePageResponse = Api.SystemManage.PageResult<RoleResponse>;
type RoleSimpleResponse = Omit<Api.SystemManage.RoleSimple, 'id'> & {
id: string | number;
};
type MenuResponse = Omit<Api.SystemManage.Menu, 'id' | 'parentId' | 'children'> & {
id: string | number;
parentId: string | number;
children?: MenuResponse[] | null;
};
type MenuSimpleResponse = Omit<Api.SystemManage.MenuSimple, 'id' | 'parentId' | 'children'> & {
id: string | number;
parentId: string | number;
children?: MenuSimpleResponse[] | null;
};
type UserManagementRelationResponse = Omit<
Api.SystemManage.UserManagementRelation,
'id' | 'managerUserId' | 'subordinateUserId'
> & {
id: string | number | null;
managerUserId: string | number | null;
subordinateUserId: string | number | null;
};
type UserManagementRelationTreeResponse = Omit<
Api.SystemManage.UserManagementRelationTreeRespVO,
'id' | 'userId' | 'managerUserId' | 'children'
> & {
id: string | number | null;
userId: string | number;
managerUserId: string | number | null;
children?: UserManagementRelationTreeResponse[] | null;
};
function normalizeUserSimple(user: UserSimpleResponse): Api.SystemManage.UserSimple {
return {
...user,
id: normalizeStringId(user.id)
};
}
function normalizeRole(role: RoleResponse): Api.SystemManage.Role {
return {
...role,
id: normalizeStringId(role.id)
};
}
function normalizeRoleSimple(role: RoleSimpleResponse): Api.SystemManage.RoleSimple {
return {
...role,
id: normalizeStringId(role.id)
};
}
function normalizeMenu(menu: MenuResponse): Api.SystemManage.Menu {
return {
...menu,
id: normalizeStringId(menu.id),
parentId: normalizeStringId(menu.parentId),
children: menu.children?.map(normalizeMenu) ?? null
};
}
function normalizeMenuSimple(menu: MenuSimpleResponse): Api.SystemManage.MenuSimple {
return {
...menu,
id: normalizeStringId(menu.id),
parentId: normalizeStringId(menu.parentId),
children: menu.children?.map(normalizeMenuSimple) ?? null
};
}
function normalizeUserManagementRelation(
relation: UserManagementRelationResponse
): Api.SystemManage.UserManagementRelation {
return {
...relation,
id: normalizeNullableStringId(relation.id),
managerUserId: normalizeNullableStringId(relation.managerUserId),
subordinateUserId: normalizeNullableStringId(relation.subordinateUserId)
};
}
function normalizeUserManagementRelationTree(
relation: UserManagementRelationTreeResponse
): Api.SystemManage.UserManagementRelationTreeRespVO {
return {
...relation,
id: normalizeNullableStringId(relation.id),
userId: normalizeStringId(relation.userId),
managerUserId: normalizeNullableStringId(relation.managerUserId),
children: relation.children?.map(normalizeUserManagementRelationTree) ?? null
};
}
/** 获取角色分页 */
export function fetchGetRolePage(params?: Api.SystemManage.RoleSearchParams) {
export async function fetchGetRolePage(params?: Api.SystemManage.RoleSearchParams) {
const query = createRolePageQuery(params);
return request<Api.SystemManage.RoleList>({
const result = await request<RolePageResponse>({
...safeJsonRequestConfig,
url: query ? `${ROLE_PREFIX}/page?${query}` : `${ROLE_PREFIX}/page`,
method: 'get'
});
return mapServiceResult(result as ServiceRequestResult<RolePageResponse>, data => ({
...data,
list: data.list.map(normalizeRole)
}));
}
/** 为兼容旧代码保留原函数名 */
export const fetchGetRoleList = fetchGetRolePage;
/** 获取角色详情 */
export function fetchGetRole(id: number) {
return request<Api.SystemManage.Role>({
export async function fetchGetRole(id: string) {
const result = await request<RoleResponse>({
...safeJsonRequestConfig,
url: `${ROLE_PREFIX}/get`,
method: 'get',
params: { id }
});
return mapServiceResult(result as ServiceRequestResult<RoleResponse>, normalizeRole);
}
/** 创建角色 */
export function fetchCreateRole(data: Api.SystemManage.SaveRoleParams) {
return request<number>({
export async function fetchCreateRole(data: Api.SystemManage.SaveRoleParams) {
const result = await request<string | number>({
...safeJsonRequestConfig,
url: `${ROLE_PREFIX}/create`,
method: 'post',
data
});
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
}
/** 更新角色 */
export function fetchUpdateRole(data: { id: number } & Api.SystemManage.SaveRoleParams) {
export function fetchUpdateRole(
data: { id: string } & Omit<Api.SystemManage.SaveRoleParams, 'scopeType' | 'objectType'>
) {
return request<boolean>({
url: `${ROLE_PREFIX}/update`,
method: 'put',
@@ -99,7 +234,7 @@ export function fetchUpdateRole(data: { id: number } & Api.SystemManage.SaveRole
}
/** 删除角色 */
export function fetchDeleteRole(id: number) {
export function fetchDeleteRole(id: string) {
return request<boolean>({
url: `${ROLE_PREFIX}/delete`,
method: 'delete',
@@ -108,7 +243,7 @@ export function fetchDeleteRole(id: number) {
}
/** 批量删除角色 */
export function fetchBatchDeleteRole(ids: number[]) {
export function fetchBatchDeleteRole(ids: string[]) {
return request<boolean>({
url: `${ROLE_PREFIX}/delete-list?${createBatchDeleteQuery(ids)}`,
method: 'delete'
@@ -121,7 +256,8 @@ export function fetchBatchDeleteRole(ids: number[]) {
* 为当前用户页面保留 `roleName / roleCode` 字段,直到该页面完成重构
*/
export async function fetchGetAllRoles(): Promise<ServiceRequestResult<Api.SystemManage.AllRole[]>> {
const result = await request<Api.SystemManage.RoleSimpleList>({
const result = await request<RoleSimpleResponse[]>({
...safeJsonRequestConfig,
url: `${ROLE_PREFIX}/simple-list`,
method: 'get'
});
@@ -132,20 +268,28 @@ export async function fetchGetAllRoles(): Promise<ServiceRequestResult<Api.Syste
return {
...result,
data: result.data.map(item => ({
...item,
roleName: item.name,
roleCode: item.code
}))
data: result.data.map(item => {
const role = normalizeRoleSimple(item);
return {
...role,
roleName: role.name,
roleCode: role.code
};
})
};
}
/** 获取启用状态的角色简表 */
export function fetchGetRoleSimpleList() {
return request<Api.SystemManage.RoleSimpleList>({
export async function fetchGetRoleSimpleList(params?: Api.SystemManage.ScopeQueryParams) {
const result = await request<RoleSimpleResponse[]>({
...safeJsonRequestConfig,
url: `${ROLE_PREFIX}/simple-list`,
method: 'get'
method: 'get',
params
});
return mapServiceResult(result as ServiceRequestResult<RoleSimpleResponse[]>, data => data.map(normalizeRoleSimple));
}
/** 获取部门列表 */
@@ -300,10 +444,13 @@ export function fetchBatchDeletePost(ids: number[]) {
/** 获取用户简单列表(用于用户选择下拉框) */
export function fetchGetUserSimpleList() {
return request<Api.SystemManage.UserSimple[]>({
return request<UserSimpleResponse[]>({
...safeJsonRequestConfig,
url: `${USER_PREFIX}/simple-list`,
method: 'get'
});
}).then(result =>
mapServiceResult(result as ServiceRequestResult<UserSimpleResponse[]>, data => data.map(normalizeUserSimple))
);
}
/** 获取用户分页 */
@@ -320,11 +467,14 @@ export const fetchGetUserList = fetchGetUserPage;
/** 通过部门id获取用户详情 */
export function fetchGetUserListByDeptId(deptId: any) {
return request<Api.SystemManage.UserSimple[]>({
return request<UserSimpleResponse[]>({
...safeJsonRequestConfig,
url: `${USER_PREFIX}/list-by-dept-id`,
method: 'get',
params: { deptId }
});
}).then(result =>
mapServiceResult(result as ServiceRequestResult<UserSimpleResponse[]>, data => data.map(normalizeUserSimple))
);
}
/** 获取用户详情 */
@@ -390,34 +540,45 @@ export function fetchBatchDeleteUser(ids: number[]) {
}
/** 获取菜单列表 */
export function fetchGetMenuList(params?: Api.SystemManage.MenuSearchParams) {
return request<Api.SystemManage.MenuList>({
export async function fetchGetMenuList(params?: Api.SystemManage.MenuSearchParams) {
const result = await request<MenuResponse[]>({
...safeJsonRequestConfig,
url: `${MENU_PREFIX}/list`,
method: 'get',
params
});
return mapServiceResult(result as ServiceRequestResult<MenuResponse[]>, data => data.map(normalizeMenu));
}
/** 获取菜单详情 */
export function fetchGetMenu(id: number) {
return request<Api.SystemManage.Menu>({
export async function fetchGetMenu(id: string) {
const result = await request<MenuResponse>({
...safeJsonRequestConfig,
url: `${MENU_PREFIX}/get`,
method: 'get',
params: { id }
});
return mapServiceResult(result as ServiceRequestResult<MenuResponse>, normalizeMenu);
}
/** 创建菜单 */
export function fetchCreateMenu(data: Api.SystemManage.SaveMenuParams) {
return request<number>({
export async function fetchCreateMenu(data: Api.SystemManage.SaveMenuParams) {
const result = await request<string | number>({
...safeJsonRequestConfig,
url: `${MENU_PREFIX}/create`,
method: 'post',
data
});
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
}
/** 更新菜单 */
export function fetchUpdateMenu(data: { id: number } & Api.SystemManage.SaveMenuParams) {
export function fetchUpdateMenu(
data: { id: string } & Omit<Api.SystemManage.SaveMenuParams, 'scopeType' | 'objectType'>
) {
return request<boolean>({
url: `${MENU_PREFIX}/update`,
method: 'put',
@@ -426,7 +587,7 @@ export function fetchUpdateMenu(data: { id: number } & Api.SystemManage.SaveMenu
}
/** 删除菜单 */
export function fetchDeleteMenu(id: number) {
export function fetchDeleteMenu(id: string) {
return request<boolean>({
url: `${MENU_PREFIX}/delete`,
method: 'delete',
@@ -435,7 +596,7 @@ export function fetchDeleteMenu(id: number) {
}
/** 批量删除菜单 */
export function fetchBatchDeleteMenu(ids: number[]) {
export function fetchBatchDeleteMenu(ids: string[]) {
return request<boolean>({
url: `${MENU_PREFIX}/delete-list?${createBatchDeleteQuery(ids)}`,
method: 'delete'
@@ -443,20 +604,27 @@ export function fetchBatchDeleteMenu(ids: number[]) {
}
/** 获取启用状态的菜单简表 */
export function fetchGetMenuSimpleList() {
return request<Api.SystemManage.MenuSimpleList>({
export async function fetchGetMenuSimpleList(params?: Api.SystemManage.ScopeQueryParams) {
const result = await request<MenuSimpleResponse[]>({
...safeJsonRequestConfig,
url: `${MENU_PREFIX}/simple-list`,
method: 'get'
method: 'get',
params
});
return mapServiceResult(result as ServiceRequestResult<MenuSimpleResponse[]>, data => data.map(normalizeMenuSimple));
}
/** 获取角色关联的菜单 ID 列表 */
export function fetchGetRoleMenuIds(roleId: number) {
return request<number[]>({
export async function fetchGetRoleMenuIds(roleId: string) {
const result = await request<Array<string | number>>({
...safeJsonRequestConfig,
url: `${PERMISSION_PREFIX}/list-role-menus`,
method: 'get',
params: { roleId }
});
return mapServiceResult(result as ServiceRequestResult<Array<string | number>>, data => data.map(normalizeStringId));
}
/** 分配角色菜单 */
@@ -469,12 +637,15 @@ export function fetchAssignRoleMenus(data: Api.SystemManage.AssignRoleMenuParams
}
/** 获取用户关联的角色 ID 列表 */
export function fetchGetUserRoleIds(userId: number) {
return request<number[]>({
export async function fetchGetUserRoleIds(userId: number) {
const result = await request<Array<string | number>>({
...safeJsonRequestConfig,
url: `${PERMISSION_PREFIX}/list-user-roles`,
method: 'get',
params: { userId }
});
return mapServiceResult(result as ServiceRequestResult<Array<string | number>>, data => data.map(normalizeStringId));
}
/** 分配用户角色 */
@@ -497,11 +668,16 @@ export function fetchAssignUserRoles(data: Api.SystemManage.AssignUserRoleParams
* - 叶子节点:基层员工,没有下级
*/
export function fetchGetUserManagementRelationTree(query: UserManagementRelationQueryReqVO) {
return request<Api.SystemManage.UserManagementRelationTreeRespVO[]>({
return request<UserManagementRelationTreeResponse[]>({
...safeJsonRequestConfig,
url: `${USER_MANAGEMENT_RELATION_PREFIX}/tree`,
method: 'get',
params: query
});
}).then(result =>
mapServiceResult(result as ServiceRequestResult<UserManagementRelationTreeResponse[]>, data =>
data.map(normalizeUserManagementRelationTree)
)
);
}
/**
@@ -509,11 +685,16 @@ export function fetchGetUserManagementRelationTree(query: UserManagementRelation
* 用于树形控件展示,包含用户的上下级层级关系
*/
export function fetchGetUserManagementRelationQuery(query: UserManagementRelationQueryReqVO) {
return request<Api.SystemManage.UserManagementRelationTreeRespVO[]>({
return request<UserManagementRelationTreeResponse[]>({
...safeJsonRequestConfig,
url: `${USER_MANAGEMENT_RELATION_PREFIX}/query`,
method: 'get',
params: query
});
}).then(result =>
mapServiceResult(result as ServiceRequestResult<UserManagementRelationTreeResponse[]>, data =>
data.map(normalizeUserManagementRelationTree)
)
);
}
/**
@@ -523,12 +704,15 @@ export function fetchGetUserManagementRelationQuery(query: UserManagementRelatio
*
* @param id 关系记录主键 ID
*/
export function fetchGetUserManagementRelation(id: number) {
return request<Api.SystemManage.UserManagementRelation>({
export function fetchGetUserManagementRelation(id: string) {
return request<UserManagementRelationResponse>({
...safeJsonRequestConfig,
url: `${USER_MANAGEMENT_RELATION_PREFIX}/get`,
method: 'get',
params: { id }
});
}).then(result =>
mapServiceResult(result as ServiceRequestResult<UserManagementRelationResponse>, normalizeUserManagementRelation)
);
}
/**
@@ -539,11 +723,12 @@ export function fetchGetUserManagementRelation(id: number) {
* @param data 创建请求参数
*/
export function fetchCreateUserManagementRelation(data: Api.SystemManage.UserManagementRelationSaveReqVO) {
return request<number>({
return request<string | number>({
...safeJsonRequestConfig,
url: `${USER_MANAGEMENT_RELATION_PREFIX}/create`,
method: 'post',
data
});
}).then(result => mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId));
}
/**
@@ -554,7 +739,7 @@ export function fetchCreateUserManagementRelation(data: Api.SystemManage.UserMan
* @param data 更新请求参数(包含 id
*/
export function fetchUpdateUserManagementRelation(
data: { id: number } & Api.SystemManage.UserManagementRelationSaveReqVO
data: { id: string } & Api.SystemManage.UserManagementRelationSaveReqVO
) {
return request<boolean>({
url: `${USER_MANAGEMENT_RELATION_PREFIX}/update`,
@@ -570,7 +755,7 @@ export function fetchUpdateUserManagementRelation(
*
* @param id 关系记录主键 ID
*/
export function fetchDeleteUserManagementRelation(id: number | null) {
export function fetchDeleteUserManagementRelation(id: string | null) {
return request<boolean>({
url: `${USER_MANAGEMENT_RELATION_PREFIX}/delete`,
method: 'delete',
@@ -585,7 +770,7 @@ export function fetchDeleteUserManagementRelation(id: number | null) {
*
* @param ids 关系记录主键 ID 列表
*/
export function fetchBatchDeleteUserManagementRelation(ids: number[]) {
export function fetchBatchDeleteUserManagementRelation(ids: string[]) {
return request<boolean>({
url: `${USER_MANAGEMENT_RELATION_PREFIX}/delete-list?${createBatchDeleteQuery(ids)}`,
method: 'delete'