Merge branch 'main' of http://192.168.1.22:3000/Web/cn-rdms-web
# Conflicts: # src/views/product/requirement/index.vue # src/views/system/user-management-relation/index.vue
This commit is contained in:
19
src/service/api/file.ts
Normal file
19
src/service/api/file.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { SYSTEM_SERVICE_PREFIX } from '@/constants/service';
|
||||
import { request } from '../request';
|
||||
|
||||
const FILE_PREFIX = `${SYSTEM_SERVICE_PREFIX}/file`;
|
||||
|
||||
/** 上传文件(模式一:后端中转) */
|
||||
export function uploadFile(file: File, directory?: string) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
if (directory) {
|
||||
formData.append('directory', directory);
|
||||
}
|
||||
|
||||
return request<string>({
|
||||
url: `${FILE_PREFIX}/upload`,
|
||||
method: 'post',
|
||||
data: formData
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
export * from './auth';
|
||||
export * from './dict';
|
||||
export * from './file';
|
||||
export * from './object-context';
|
||||
export * from './product';
|
||||
export * from './project';
|
||||
export * from './project-shared';
|
||||
export * from './route';
|
||||
export * from './system-manage';
|
||||
|
||||
199
src/service/api/object-context-normalize.ts
Normal file
199
src/service/api/object-context-normalize.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
import { normalizeNullableStringId, normalizeStringId } from './shared';
|
||||
|
||||
export interface BackendObjectContextMenuDTO {
|
||||
key?: string | null;
|
||||
label?: string | null;
|
||||
routeKey?: string | null;
|
||||
routePath?: string | null;
|
||||
id?: string | number | null;
|
||||
name?: string | null;
|
||||
path?: string | null;
|
||||
icon?: string | null;
|
||||
sort?: number | 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 BackendProjectContextProjectDTO {
|
||||
id?: string | number | null;
|
||||
projectCode?: string | null;
|
||||
projectName?: string | null;
|
||||
projectType?: string | null;
|
||||
productId?: string | number | null;
|
||||
managerUserId?: string | number | null;
|
||||
statusCode?: string | null;
|
||||
}
|
||||
|
||||
interface BackendObjectContextRoleDTO {
|
||||
roleId?: string | number | null;
|
||||
roleCode?: string | null;
|
||||
roleName?: string | null;
|
||||
guestFlag?: boolean | null;
|
||||
}
|
||||
|
||||
export 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;
|
||||
currentProject?: BackendProjectContextProjectDTO | null;
|
||||
currentRole?: BackendObjectContextRoleDTO | 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 normalizeCurrentProject(project: BackendProjectContextProjectDTO) {
|
||||
return {
|
||||
id: normalizeStringId(project.id || ''),
|
||||
projectCode: normalizeString(project.projectCode),
|
||||
projectName: normalizeString(project.projectName),
|
||||
projectType: normalizeString(project.projectType),
|
||||
productId: normalizeNullableStringId(project.productId),
|
||||
managerUserId: normalizeNullableStringId(project.managerUserId) ?? '',
|
||||
statusCode: normalizeString(project.statusCode)
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeCurrentRole(role: BackendObjectContextRoleDTO) {
|
||||
return {
|
||||
roleId: normalizeStringId(role.roleId || ''),
|
||||
roleCode: normalizeString(role.roleCode),
|
||||
roleName: normalizeString(role.roleName),
|
||||
guestFlag: Boolean(role.guestFlag)
|
||||
};
|
||||
}
|
||||
|
||||
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 getFirstNonEmptyMenuSource(data: BackendObjectContextDTO) {
|
||||
const menuSources = [data.contextScopedMenus, data.menus, data.navs];
|
||||
|
||||
return menuSources.find(source => Array.isArray(source) && source.length > 0) || [];
|
||||
}
|
||||
|
||||
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.currentProject) {
|
||||
summary.currentProject = normalizeCurrentProject(data.currentProject);
|
||||
}
|
||||
|
||||
if (data.currentRole !== undefined) {
|
||||
summary.currentRole = data.currentRole ? normalizeCurrentRole(data.currentRole) : null;
|
||||
}
|
||||
|
||||
return Object.keys(summary).length ? summary : null;
|
||||
}
|
||||
|
||||
// 待重构:拆 helper 以降低复杂度,暂以 disable 注释临时放行
|
||||
// eslint-disable-next-line complexity
|
||||
export function normalizeObjectContext(
|
||||
config: App.ObjectContext.DomainConfig,
|
||||
objectId: string,
|
||||
data: BackendObjectContextDTO
|
||||
): Api.ObjectContext.ContextInfo {
|
||||
const rawMenus = getFirstNonEmptyMenuSource(data);
|
||||
const contextScopedMenus = rawMenus.map(menu => normalizeMenu(menu));
|
||||
const firstRoutableMenu = getFirstRoutableMenu(contextScopedMenus);
|
||||
const currentProduct = data.currentProduct ? normalizeCurrentProduct(data.currentProduct) : null;
|
||||
const currentProject = data.currentProject ? normalizeCurrentProject(data.currentProject) : 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 || currentProject?.id || objectId,
|
||||
objectName: normalizeString(data.objectName || currentProduct?.name || currentProject?.projectName),
|
||||
objectSummary: normalizeObjectSummary(data),
|
||||
contextScopedMenus,
|
||||
buttonCodes: data.buttonCodes ?? data.buttons ?? [],
|
||||
defaultRouteKey: data.defaultRouteKey || firstRoutableMenu?.routeKey || '',
|
||||
defaultRoutePath:
|
||||
normalizeRoutePath(data.defaultRoutePath) || firstRoutableMenu?.routePath || config.fallbackDefaultRoutePath
|
||||
};
|
||||
}
|
||||
@@ -1,145 +1,7 @@
|
||||
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;
|
||||
}
|
||||
import { type ServiceRequestResult, safeJsonRequestConfig } from './shared';
|
||||
import { type BackendObjectContextDTO, normalizeObjectContext } from './object-context-normalize';
|
||||
|
||||
function createContextApiUrl(config: App.ObjectContext.DomainConfig, objectId: string) {
|
||||
if (config.contextApiObjectIdPlacement !== 'path') {
|
||||
@@ -151,30 +13,6 @@ function createContextApiUrl(config: App.ObjectContext.DomainConfig, objectId: s
|
||||
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
|
||||
|
||||
@@ -106,6 +106,15 @@ export async function fetchGetProductPage(params?: Api.Product.ProductSearchPara
|
||||
}));
|
||||
}
|
||||
|
||||
/** 获取产品入口页概览统计 */
|
||||
export function fetchGetProductOverviewSummary() {
|
||||
return request<Api.Product.ProductOverviewSummary>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${PRODUCT_PREFIX}/overview-summary`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/** 鑾峰彇浜у搧璇︽儏 */
|
||||
export async function fetchGetProduct(id: string) {
|
||||
const result = await request<ProductResponse>({
|
||||
|
||||
244
src/service/api/project-shared.ts
Normal file
244
src/service/api/project-shared.ts
Normal file
@@ -0,0 +1,244 @@
|
||||
import { normalizeNullableStringId, normalizeStringId } from './shared';
|
||||
|
||||
type ProjectStatusCode = Api.Project.ProjectStatusCode;
|
||||
type ProjectStatusActionCode = Exclude<Api.Project.ProjectStatusActionCode, 'auto_start'>;
|
||||
|
||||
type StringIdResponse = string | number;
|
||||
|
||||
export type ProjectLocalDateValue = string | number[] | null;
|
||||
|
||||
export type LifecycleActionResponse<ActionCode extends string> = Partial<Api.Project.LifecycleAction<ActionCode>> & {
|
||||
actionCode: ActionCode;
|
||||
};
|
||||
|
||||
export type ProjectExecutionResponse = Omit<
|
||||
Api.Project.ProjectExecution,
|
||||
| 'id'
|
||||
| 'projectId'
|
||||
| 'projectRequirementId'
|
||||
| 'ownerId'
|
||||
| 'availableActions'
|
||||
| 'plannedStartDate'
|
||||
| 'plannedEndDate'
|
||||
| 'actualStartDate'
|
||||
| 'actualEndDate'
|
||||
| 'progressRate'
|
||||
> & {
|
||||
id: StringIdResponse;
|
||||
projectId: StringIdResponse;
|
||||
projectRequirementId?: StringIdResponse | null;
|
||||
ownerId: StringIdResponse;
|
||||
availableActions?: LifecycleActionResponse<Api.Project.ProjectExecutionActionCode>[] | null;
|
||||
plannedStartDate?: ProjectLocalDateValue;
|
||||
plannedEndDate?: ProjectLocalDateValue;
|
||||
actualStartDate?: ProjectLocalDateValue;
|
||||
actualEndDate?: ProjectLocalDateValue;
|
||||
progressRate?: number | null;
|
||||
};
|
||||
|
||||
export type ExecutionMemberResponse = Omit<Api.Project.ExecutionMember, 'id' | 'executionId' | 'userId'> & {
|
||||
id: StringIdResponse;
|
||||
executionId: StringIdResponse;
|
||||
userId: StringIdResponse;
|
||||
};
|
||||
|
||||
export type ExecutionMemberLogResponse = Omit<
|
||||
Api.Project.ExecutionMemberLog,
|
||||
'id' | 'executionId' | 'userId' | 'operatorUserId'
|
||||
> & {
|
||||
id: StringIdResponse;
|
||||
executionId: StringIdResponse;
|
||||
userId: StringIdResponse;
|
||||
operatorUserId: StringIdResponse;
|
||||
};
|
||||
|
||||
export type ProjectTaskResponse = Omit<
|
||||
Api.Project.ProjectTask,
|
||||
| 'id'
|
||||
| 'projectId'
|
||||
| 'executionId'
|
||||
| 'parentTaskId'
|
||||
| 'ownerId'
|
||||
| 'availableActions'
|
||||
| 'plannedStartDate'
|
||||
| 'plannedEndDate'
|
||||
| 'actualStartDate'
|
||||
| 'actualEndDate'
|
||||
| 'progressRate'
|
||||
> & {
|
||||
id: StringIdResponse;
|
||||
projectId: StringIdResponse;
|
||||
executionId: StringIdResponse;
|
||||
parentTaskId?: StringIdResponse | null;
|
||||
ownerId: StringIdResponse;
|
||||
availableActions?: LifecycleActionResponse<Api.Project.ProjectTaskActionCode>[] | null;
|
||||
plannedStartDate?: ProjectLocalDateValue;
|
||||
plannedEndDate?: ProjectLocalDateValue;
|
||||
actualStartDate?: ProjectLocalDateValue;
|
||||
actualEndDate?: ProjectLocalDateValue;
|
||||
progressRate?: number | null;
|
||||
};
|
||||
|
||||
export interface ProjectMemberResponse {
|
||||
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;
|
||||
}
|
||||
|
||||
const projectLifecycleActionNameMap: Record<ProjectStatusActionCode, string> = {
|
||||
pause: '暂停项目',
|
||||
resume: '恢复项目',
|
||||
complete: '完成项目',
|
||||
cancel: '取消项目',
|
||||
reopen: '重新开启',
|
||||
archive: '归档项目'
|
||||
};
|
||||
|
||||
const projectLifecycleActionReasonRequiredMap: Record<ProjectStatusActionCode, boolean> = {
|
||||
pause: true,
|
||||
resume: false,
|
||||
complete: true,
|
||||
cancel: true,
|
||||
reopen: true,
|
||||
archive: false
|
||||
};
|
||||
|
||||
const projectLifecycleActionMap: Record<ProjectStatusCode, ProjectStatusActionCode[]> = {
|
||||
pending: ['cancel'],
|
||||
active: ['pause', 'complete', 'cancel'],
|
||||
paused: ['resume', 'cancel'],
|
||||
completed: ['reopen', 'archive'],
|
||||
cancelled: [],
|
||||
archived: []
|
||||
};
|
||||
|
||||
export function getProjectLifecycleActions(statusCode: ProjectStatusCode): Api.Project.ProjectLifecycleAction[] {
|
||||
return projectLifecycleActionMap[statusCode].map(actionCode => ({
|
||||
actionCode,
|
||||
actionName: projectLifecycleActionNameMap[actionCode],
|
||||
needReason: projectLifecycleActionReasonRequiredMap[actionCode]
|
||||
}));
|
||||
}
|
||||
|
||||
export function normalizeProjectLocalDate(value: ProjectLocalDateValue | undefined) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
const [year, month, day] = value;
|
||||
|
||||
if (!year || !month || !day) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [year, month, day].map(item => String(item).padStart(2, '0')).join('-');
|
||||
}
|
||||
|
||||
return String(value);
|
||||
}
|
||||
|
||||
export function normalizeLifecycleActions<ActionCode extends string>(
|
||||
actions: LifecycleActionResponse<ActionCode>[] | null | undefined
|
||||
): Api.Project.LifecycleAction<ActionCode>[] {
|
||||
return (actions ?? []).map(action => ({
|
||||
actionCode: action.actionCode,
|
||||
actionName: action.actionName ?? '',
|
||||
needReason: Boolean(action.needReason)
|
||||
}));
|
||||
}
|
||||
|
||||
export function normalizeProjectMember(response: ProjectMemberResponse): Api.Project.ProjectMember {
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeProjectExecution(response: ProjectExecutionResponse): Api.Project.ProjectExecution {
|
||||
return {
|
||||
...response,
|
||||
id: normalizeStringId(response.id),
|
||||
projectId: normalizeStringId(response.projectId),
|
||||
projectRequirementId: normalizeNullableStringId(response.projectRequirementId),
|
||||
ownerId: normalizeStringId(response.ownerId),
|
||||
ownerNickname: response.ownerNickname ?? null,
|
||||
statusName: response.statusName ?? null,
|
||||
terminal: Boolean(response.terminal),
|
||||
allowEdit: Boolean(response.allowEdit),
|
||||
availableActions: normalizeLifecycleActions(response.availableActions),
|
||||
plannedStartDate: normalizeProjectLocalDate(response.plannedStartDate),
|
||||
plannedEndDate: normalizeProjectLocalDate(response.plannedEndDate),
|
||||
actualStartDate: normalizeProjectLocalDate(response.actualStartDate),
|
||||
actualEndDate: normalizeProjectLocalDate(response.actualEndDate),
|
||||
progressRate: typeof response.progressRate === 'number' ? response.progressRate : 0,
|
||||
executionDesc: response.executionDesc ?? null,
|
||||
lastStatusReason: response.lastStatusReason ?? null
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeExecutionMember(response: ExecutionMemberResponse): Api.Project.ExecutionMember {
|
||||
return {
|
||||
...response,
|
||||
id: normalizeStringId(response.id),
|
||||
executionId: normalizeStringId(response.executionId),
|
||||
userId: normalizeStringId(response.userId),
|
||||
userNickname: response.userNickname ?? null,
|
||||
joinedAt: response.joinedAt ?? null,
|
||||
removedAt: response.removedAt ?? null,
|
||||
removedReason: response.removedReason ?? null
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeExecutionMemberLog(response: ExecutionMemberLogResponse): Api.Project.ExecutionMemberLog {
|
||||
return {
|
||||
...response,
|
||||
id: normalizeStringId(response.id),
|
||||
executionId: normalizeStringId(response.executionId),
|
||||
userId: normalizeStringId(response.userId),
|
||||
operatorUserId: normalizeStringId(response.operatorUserId),
|
||||
userNicknameSnapshot: response.userNicknameSnapshot ?? null,
|
||||
operatorNicknameSnapshot: response.operatorNicknameSnapshot ?? null,
|
||||
reason: response.reason ?? null
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeProjectTask(response: ProjectTaskResponse): Api.Project.ProjectTask {
|
||||
return {
|
||||
...response,
|
||||
id: normalizeStringId(response.id),
|
||||
projectId: normalizeStringId(response.projectId),
|
||||
executionId: normalizeStringId(response.executionId),
|
||||
parentTaskId: normalizeNullableStringId(response.parentTaskId),
|
||||
ownerId: normalizeStringId(response.ownerId),
|
||||
ownerNickname: response.ownerNickname ?? null,
|
||||
statusName: response.statusName ?? null,
|
||||
terminal: Boolean(response.terminal),
|
||||
allowEdit: Boolean(response.allowEdit),
|
||||
availableActions: normalizeLifecycleActions(response.availableActions),
|
||||
progressRate: typeof response.progressRate === 'number' ? response.progressRate : 0,
|
||||
plannedStartDate: normalizeProjectLocalDate(response.plannedStartDate),
|
||||
plannedEndDate: normalizeProjectLocalDate(response.plannedEndDate),
|
||||
actualStartDate: normalizeProjectLocalDate(response.actualStartDate),
|
||||
actualEndDate: normalizeProjectLocalDate(response.actualEndDate),
|
||||
taskDesc: response.taskDesc ?? null,
|
||||
lastStatusReason: response.lastStatusReason ?? null
|
||||
};
|
||||
}
|
||||
532
src/service/api/project.ts
Normal file
532
src/service/api/project.ts
Normal file
@@ -0,0 +1,532 @@
|
||||
import { WEB_SERVICE_PREFIX } from '@/constants/service';
|
||||
import { request } from '../request';
|
||||
import {
|
||||
type ServiceRequestResult,
|
||||
mapServiceResult,
|
||||
normalizeNullableStringId,
|
||||
normalizeStringId,
|
||||
safeJsonRequestConfig
|
||||
} from './shared';
|
||||
import {
|
||||
type ExecutionMemberLogResponse,
|
||||
type ExecutionMemberResponse,
|
||||
type ProjectExecutionResponse,
|
||||
type ProjectLocalDateValue,
|
||||
type ProjectMemberResponse,
|
||||
type ProjectTaskResponse,
|
||||
getProjectLifecycleActions,
|
||||
normalizeExecutionMember,
|
||||
normalizeExecutionMemberLog,
|
||||
normalizeProjectExecution,
|
||||
normalizeProjectLocalDate,
|
||||
normalizeProjectMember,
|
||||
normalizeProjectTask
|
||||
} from './project-shared';
|
||||
|
||||
const PROJECT_PREFIX = `${WEB_SERVICE_PREFIX}/project/project`;
|
||||
|
||||
type ProjectResponse = Omit<
|
||||
Api.Project.Project,
|
||||
'id' | 'managerUserId' | 'productId' | 'plannedStartDate' | 'plannedEndDate' | 'actualStartDate' | 'actualEndDate'
|
||||
> & {
|
||||
id: string | number;
|
||||
managerUserId?: string | number | null;
|
||||
productId?: string | number | null;
|
||||
plannedStartDate?: ProjectLocalDateValue;
|
||||
plannedEndDate?: ProjectLocalDateValue;
|
||||
actualStartDate?: ProjectLocalDateValue;
|
||||
actualEndDate?: ProjectLocalDateValue;
|
||||
};
|
||||
|
||||
type ProjectPageResponse = Api.Project.PageResult<ProjectResponse>;
|
||||
type ProjectExecutionPageResponse = Api.Project.PageResult<ProjectExecutionResponse>;
|
||||
type ProjectTaskPageResponse = Api.Project.PageResult<ProjectTaskResponse>;
|
||||
type StatusBoardResponse = Api.Project.StatusBoard;
|
||||
|
||||
type ProjectContextResponse = Omit<Api.Project.ProjectContext, 'currentProject' | 'navs'> & {
|
||||
currentProject: Omit<Api.Project.ProjectContext['currentProject'], 'id'> & { id: string | number };
|
||||
navs: Array<Omit<Api.Project.ProjectContext['navs'][number], 'id'> & { id: string | number }>;
|
||||
};
|
||||
|
||||
function getExecutionPrefix(projectId: string) {
|
||||
return `${PROJECT_PREFIX}/${projectId}/executions`;
|
||||
}
|
||||
|
||||
function getTaskPrefix(projectId: string, executionId: string) {
|
||||
return `${getExecutionPrefix(projectId)}/${executionId}/tasks`;
|
||||
}
|
||||
|
||||
/** 归一化项目数据 */
|
||||
function normalizeProject(project: ProjectResponse): Api.Project.Project {
|
||||
return {
|
||||
...project,
|
||||
id: normalizeStringId(project.id),
|
||||
managerUserId: normalizeNullableStringId(project.managerUserId) ?? '',
|
||||
productId: normalizeNullableStringId(project.productId),
|
||||
plannedStartDate: normalizeProjectLocalDate(project.plannedStartDate),
|
||||
plannedEndDate: normalizeProjectLocalDate(project.plannedEndDate),
|
||||
actualStartDate: normalizeProjectLocalDate(project.actualStartDate),
|
||||
actualEndDate: normalizeProjectLocalDate(project.actualEndDate)
|
||||
};
|
||||
}
|
||||
|
||||
/** 将项目详情组装为设置页数据 */
|
||||
function createProjectSettings(project: Api.Project.Project): Api.Project.ProjectSettings {
|
||||
return {
|
||||
baseInfo: {
|
||||
id: project.id,
|
||||
projectCode: project.projectCode,
|
||||
projectName: project.projectName,
|
||||
directionCode: project.directionCode,
|
||||
projectType: project.projectType,
|
||||
productId: project.productId,
|
||||
productName: project.productName ?? null,
|
||||
managerUserId: project.managerUserId,
|
||||
managerUserNickname: project.managerUserNickname ?? null,
|
||||
statusCode: project.statusCode,
|
||||
plannedStartDate: project.plannedStartDate,
|
||||
plannedEndDate: project.plannedEndDate,
|
||||
actualStartDate: project.actualStartDate,
|
||||
actualEndDate: project.actualEndDate,
|
||||
projectDesc: project.projectDesc,
|
||||
lastStatusReason: project.lastStatusReason
|
||||
},
|
||||
lifecycle: {
|
||||
statusCode: project.statusCode,
|
||||
lastStatusReason: project.lastStatusReason,
|
||||
availableActions: getProjectLifecycleActions(project.statusCode)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** 获取项目分页 */
|
||||
export async function fetchGetProjectPage(params?: Api.Project.ProjectSearchParams) {
|
||||
const result = await request<ProjectPageResponse>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${PROJECT_PREFIX}/page`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<ProjectPageResponse>, data => ({
|
||||
...data,
|
||||
list: data.list.map(normalizeProject)
|
||||
}));
|
||||
}
|
||||
|
||||
/** 获取项目入口页概览统计 */
|
||||
export function fetchGetProjectOverviewSummary() {
|
||||
return request<Api.Project.ProjectOverviewSummary>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${PROJECT_PREFIX}/overview-summary`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取项目详情 */
|
||||
export async function fetchGetProject(id: string) {
|
||||
const result = await request<ProjectResponse>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${PROJECT_PREFIX}/get`,
|
||||
method: 'get',
|
||||
params: { id }
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<ProjectResponse>, normalizeProject);
|
||||
}
|
||||
|
||||
/** 创建项目 */
|
||||
export async function fetchCreateProject(data: Api.Project.SaveProjectParams) {
|
||||
const result = await request<string | number>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${PROJECT_PREFIX}/create`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
|
||||
}
|
||||
|
||||
/** 更新项目 */
|
||||
export function fetchUpdateProject(data: Api.Project.UpdateProjectParams) {
|
||||
return request<boolean>({
|
||||
url: `${PROJECT_PREFIX}/update`,
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 变更项目状态 */
|
||||
export function fetchChangeProjectStatus(data: Api.Project.ChangeProjectStatusParams) {
|
||||
return request<boolean>({
|
||||
url: `${PROJECT_PREFIX}/change-status`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 删除项目 */
|
||||
export function fetchDeleteProject(data: Api.Project.DeleteProjectParams) {
|
||||
return request<boolean>({
|
||||
url: `${PROJECT_PREFIX}/delete`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取项目上下文 */
|
||||
export async function fetchGetProjectContext(id: string) {
|
||||
const result = await request<ProjectContextResponse>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${PROJECT_PREFIX}/${id}/context`,
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<ProjectContextResponse>, data => ({
|
||||
...data,
|
||||
currentProject: {
|
||||
...data.currentProject,
|
||||
id: normalizeStringId(data.currentProject.id)
|
||||
},
|
||||
navs: data.navs.map(nav => ({
|
||||
...nav,
|
||||
id: normalizeStringId(nav.id)
|
||||
}))
|
||||
}));
|
||||
}
|
||||
|
||||
/** 获取项目成员列表 */
|
||||
export async function fetchGetProjectMembers(id: string) {
|
||||
const result = await request<ProjectMemberResponse[]>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${PROJECT_PREFIX}/${id}/members`,
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<ProjectMemberResponse[]>, data =>
|
||||
data.map(normalizeProjectMember)
|
||||
);
|
||||
}
|
||||
|
||||
/** 创建项目成员 */
|
||||
export async function fetchCreateProjectMember(id: string, data: Api.Project.CreateProjectMemberParams) {
|
||||
const result = await request<string | number>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${PROJECT_PREFIX}/${id}/members`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
|
||||
}
|
||||
|
||||
/** 更新项目成员 */
|
||||
export function fetchUpdateProjectMember(id: string, memberId: string, data: Api.Project.UpdateProjectMemberParams) {
|
||||
return request<boolean>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${PROJECT_PREFIX}/${id}/members/${memberId}`,
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 移出项目成员 */
|
||||
export function fetchInactiveProjectMember(
|
||||
id: string,
|
||||
memberId: string,
|
||||
data: Api.Project.InactiveProjectMemberParams
|
||||
) {
|
||||
return request<boolean>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${PROJECT_PREFIX}/${id}/members/${memberId}/inactive`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取项目设置 */
|
||||
export async function fetchGetProjectSettings(id: string) {
|
||||
const result = await fetchGetProject(id);
|
||||
|
||||
if (result.error || !result.data) {
|
||||
return result as ServiceRequestResult<Api.Project.ProjectSettings>;
|
||||
}
|
||||
|
||||
return {
|
||||
...result,
|
||||
data: createProjectSettings(result.data)
|
||||
};
|
||||
}
|
||||
|
||||
/** 更新项目设置基础信息 */
|
||||
export async function fetchUpdateProjectSettingBaseInfo(
|
||||
id: string,
|
||||
data: Api.Project.UpdateProjectSettingBaseInfoParams
|
||||
) {
|
||||
const detailResult = await fetchGetProject(id);
|
||||
|
||||
if (detailResult.error || !detailResult.data) {
|
||||
return detailResult as ServiceRequestResult<boolean>;
|
||||
}
|
||||
|
||||
return fetchUpdateProject({
|
||||
id,
|
||||
projectCode: detailResult.data.projectCode,
|
||||
projectName: data.projectName,
|
||||
directionCode: data.directionCode,
|
||||
projectType: data.projectType,
|
||||
productId: detailResult.data.productId,
|
||||
managerUserId: detailResult.data.managerUserId,
|
||||
plannedStartDate: data.plannedStartDate,
|
||||
plannedEndDate: data.plannedEndDate,
|
||||
actualStartDate: detailResult.data.actualStartDate,
|
||||
actualEndDate: detailResult.data.actualEndDate,
|
||||
projectDesc: data.projectDesc
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取项目执行分页 */
|
||||
export async function fetchGetProjectExecutionPage(
|
||||
projectId: string,
|
||||
params?: Api.Project.ProjectExecutionSearchParams
|
||||
) {
|
||||
const result = await request<ProjectExecutionPageResponse>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getExecutionPrefix(projectId)}/page`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<ProjectExecutionPageResponse>, data => ({
|
||||
...data,
|
||||
list: data.list.map(normalizeProjectExecution)
|
||||
}));
|
||||
}
|
||||
|
||||
/** 获取项目执行状态看板 */
|
||||
export function fetchGetProjectExecutionStatusBoard(
|
||||
projectId: string,
|
||||
params?: Api.Project.ProjectExecutionStatusBoardParams
|
||||
) {
|
||||
return request<StatusBoardResponse>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getExecutionPrefix(projectId)}/status-board`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取项目执行详情 */
|
||||
export async function fetchGetProjectExecution(projectId: string, executionId: string) {
|
||||
const result = await request<ProjectExecutionResponse>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getExecutionPrefix(projectId)}/${executionId}`,
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<ProjectExecutionResponse>, normalizeProjectExecution);
|
||||
}
|
||||
|
||||
/** 创建项目执行 */
|
||||
export async function fetchCreateProjectExecution(projectId: string, data: Api.Project.SaveProjectExecutionParams) {
|
||||
const result = await request<string | number>({
|
||||
...safeJsonRequestConfig,
|
||||
url: getExecutionPrefix(projectId),
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
|
||||
}
|
||||
|
||||
/** 更新项目执行 */
|
||||
export function fetchUpdateProjectExecution(
|
||||
projectId: string,
|
||||
executionId: string,
|
||||
data: Api.Project.SaveProjectExecutionParams
|
||||
) {
|
||||
return request<boolean>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getExecutionPrefix(projectId)}/${executionId}`,
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 变更项目执行负责人 */
|
||||
export function fetchChangeProjectExecutionOwner(
|
||||
projectId: string,
|
||||
executionId: string,
|
||||
data: Api.Project.ChangeExecutionOwnerParams
|
||||
) {
|
||||
return request<boolean>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getExecutionPrefix(projectId)}/${executionId}/change-owner`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 变更项目执行状态 */
|
||||
export function fetchChangeProjectExecutionStatus(
|
||||
projectId: string,
|
||||
executionId: string,
|
||||
data: Api.Project.ChangeExecutionStatusParams
|
||||
) {
|
||||
return request<boolean>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getExecutionPrefix(projectId)}/${executionId}/change-status`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取项目执行成员 */
|
||||
export async function fetchGetProjectExecutionMembers(projectId: string, executionId: string) {
|
||||
const result = await request<ExecutionMemberResponse[]>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getExecutionPrefix(projectId)}/${executionId}/members`,
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<ExecutionMemberResponse[]>, data =>
|
||||
data.map(normalizeExecutionMember)
|
||||
);
|
||||
}
|
||||
|
||||
/** 创建项目执行成员 */
|
||||
export async function fetchCreateProjectExecutionMember(
|
||||
projectId: string,
|
||||
executionId: string,
|
||||
data: Api.Project.CreateExecutionMemberParams
|
||||
) {
|
||||
const result = await request<string | number>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getExecutionPrefix(projectId)}/${executionId}/members`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
|
||||
}
|
||||
|
||||
/** 移除项目执行成员 */
|
||||
export function fetchInactiveProjectExecutionMember(
|
||||
projectId: string,
|
||||
executionId: string,
|
||||
payload: { memberId: string; data: Api.Project.InactiveExecutionMemberParams }
|
||||
) {
|
||||
return request<boolean>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getExecutionPrefix(projectId)}/${executionId}/members/${payload.memberId}/inactive`,
|
||||
method: 'post',
|
||||
data: payload.data
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取项目执行成员变更历史分页 */
|
||||
export async function fetchGetProjectExecutionMemberLogPage(
|
||||
projectId: string,
|
||||
executionId: string,
|
||||
params?: Api.Project.ExecutionMemberLogSearchParams
|
||||
) {
|
||||
const result = await request<Api.Project.PageResult<ExecutionMemberLogResponse>>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getExecutionPrefix(projectId)}/${executionId}/member-logs`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<Api.Project.PageResult<ExecutionMemberLogResponse>>, data => ({
|
||||
...data,
|
||||
list: data.list.map(normalizeExecutionMemberLog)
|
||||
}));
|
||||
}
|
||||
|
||||
/** 获取项目任务分页 */
|
||||
export async function fetchGetProjectTaskPage(
|
||||
projectId: string,
|
||||
executionId: string,
|
||||
params?: Api.Project.ProjectTaskSearchParams
|
||||
) {
|
||||
const result = await request<ProjectTaskPageResponse>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getTaskPrefix(projectId, executionId)}/page`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<ProjectTaskPageResponse>, data => ({
|
||||
...data,
|
||||
list: data.list.map(normalizeProjectTask)
|
||||
}));
|
||||
}
|
||||
|
||||
/** 获取项目任务状态看板 */
|
||||
export function fetchGetProjectTaskStatusBoard(
|
||||
projectId: string,
|
||||
executionId: string,
|
||||
params?: Api.Project.ProjectTaskStatusBoardParams
|
||||
) {
|
||||
return request<StatusBoardResponse>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getTaskPrefix(projectId, executionId)}/status-board`,
|
||||
method: 'get',
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取项目任务详情 */
|
||||
export async function fetchGetProjectTask(projectId: string, executionId: string, taskId: string) {
|
||||
const result = await request<ProjectTaskResponse>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getTaskPrefix(projectId, executionId)}/${taskId}`,
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<ProjectTaskResponse>, normalizeProjectTask);
|
||||
}
|
||||
|
||||
/** 创建项目任务 */
|
||||
export async function fetchCreateProjectTask(
|
||||
projectId: string,
|
||||
executionId: string,
|
||||
data: Api.Project.SaveProjectTaskParams
|
||||
) {
|
||||
const result = await request<string | number>({
|
||||
...safeJsonRequestConfig,
|
||||
url: getTaskPrefix(projectId, executionId),
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<string | number>, normalizeStringId);
|
||||
}
|
||||
|
||||
/** 更新项目任务 */
|
||||
export function fetchUpdateProjectTask(
|
||||
projectId: string,
|
||||
executionId: string,
|
||||
payload: { taskId: string; data: Api.Project.SaveProjectTaskParams }
|
||||
) {
|
||||
return request<boolean>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getTaskPrefix(projectId, executionId)}/${payload.taskId}`,
|
||||
method: 'put',
|
||||
data: payload.data
|
||||
});
|
||||
}
|
||||
|
||||
/** 变更项目任务状态 */
|
||||
export function fetchChangeProjectTaskStatus(
|
||||
projectId: string,
|
||||
executionId: string,
|
||||
payload: { taskId: string; data: Api.Project.ChangeTaskStatusParams }
|
||||
) {
|
||||
return request<boolean>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${getTaskPrefix(projectId, executionId)}/${payload.taskId}/change-status`,
|
||||
method: 'post',
|
||||
data: payload.data
|
||||
});
|
||||
}
|
||||
@@ -95,31 +95,78 @@ function replaceWithStaticObjectContextDomainRoute(routes: Api.Route.MenuRoute[]
|
||||
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));
|
||||
// Create a map of backend routes by name for quick lookup
|
||||
const backendRouteMap = new Map<string, Api.Route.MenuRoute>();
|
||||
domainTopLevelRoutes.forEach(route => {
|
||||
if (route.name) {
|
||||
backendRouteMap.set(String(route.name), route);
|
||||
}
|
||||
});
|
||||
|
||||
if (entryRoute.meta) {
|
||||
const nextMeta: RouteMeta = {
|
||||
title: wrappedDomainRoute.meta?.title || config.domainKey,
|
||||
...(wrappedDomainRoute.meta || {})
|
||||
// Clone static route but preserve backend route's meta for children
|
||||
// 待重构:拆 helper 以降低复杂度,暂以 disable 注释临时放行
|
||||
// eslint-disable-next-line complexity
|
||||
function cloneStaticRoutePreservingBackendMeta(route: ElegantConstRoute, idPrefix: string): Api.Route.MenuRoute {
|
||||
const backendRoute = route.name ? backendRouteMap.get(String(route.name)) : undefined;
|
||||
const { children: _children, ...routeWithoutChildren } = route;
|
||||
const baseRoute: Api.Route.MenuRoute = {
|
||||
...routeWithoutChildren,
|
||||
id: `${idPrefix}:${String(route.name || route.path)}`
|
||||
};
|
||||
|
||||
if (entryRoute.meta.icon) {
|
||||
nextMeta.icon = entryRoute.meta.icon;
|
||||
// If there's a backend route, preserve its meta
|
||||
if (backendRoute?.meta) {
|
||||
baseRoute.meta = {
|
||||
...baseRoute.meta,
|
||||
title: backendRoute.meta.title || baseRoute.meta?.title || String(route.name || route.path),
|
||||
icon: backendRoute.meta.icon || baseRoute.meta?.icon,
|
||||
localIcon: backendRoute.meta.localIcon || baseRoute.meta?.localIcon,
|
||||
order:
|
||||
backendRoute.meta.order !== undefined && backendRoute.meta.order !== null
|
||||
? backendRoute.meta.order
|
||||
: baseRoute.meta?.order,
|
||||
keepAlive:
|
||||
backendRoute.meta.keepAlive !== undefined && backendRoute.meta.keepAlive !== null
|
||||
? backendRoute.meta.keepAlive
|
||||
: baseRoute.meta?.keepAlive,
|
||||
i18nKey: backendRoute.meta.i18nKey || baseRoute.meta?.i18nKey
|
||||
};
|
||||
}
|
||||
|
||||
if (entryRoute.meta.localIcon) {
|
||||
nextMeta.localIcon = entryRoute.meta.localIcon;
|
||||
// Recursively process children
|
||||
if (route.children?.length) {
|
||||
baseRoute.children = route.children.map(child => cloneStaticRoutePreservingBackendMeta(child, idPrefix));
|
||||
}
|
||||
|
||||
if (entryRoute.meta.order !== undefined) {
|
||||
nextMeta.order = entryRoute.meta.order;
|
||||
}
|
||||
|
||||
wrappedDomainRoute.meta = nextMeta;
|
||||
return baseRoute;
|
||||
}
|
||||
|
||||
const wrappedDomainRoute = cloneStaticRoutePreservingBackendMeta(
|
||||
staticDomainRoute,
|
||||
`object-context:${config.domainKey}`
|
||||
);
|
||||
|
||||
// Merge entry route's meta to domain route
|
||||
if (entryRoute.meta) {
|
||||
wrappedDomainRoute.meta = {
|
||||
...wrappedDomainRoute.meta,
|
||||
title: entryRoute.meta.title || wrappedDomainRoute.meta?.title || config.domainKey,
|
||||
icon: entryRoute.meta.icon || wrappedDomainRoute.meta?.icon,
|
||||
localIcon: entryRoute.meta.localIcon || wrappedDomainRoute.meta?.localIcon,
|
||||
order:
|
||||
entryRoute.meta.order !== undefined && entryRoute.meta.order !== null
|
||||
? entryRoute.meta.order
|
||||
: wrappedDomainRoute.meta?.order,
|
||||
keepAlive:
|
||||
entryRoute.meta.keepAlive !== undefined && entryRoute.meta.keepAlive !== null
|
||||
? entryRoute.meta.keepAlive
|
||||
: wrappedDomainRoute.meta?.keepAlive
|
||||
};
|
||||
}
|
||||
|
||||
const entryRouteIndex = normalizedRoutes.findIndex(route => route.id === entryRoute.id);
|
||||
const domainRouteIds = new Set(domainTopLevelRoutes.map(route => route.id));
|
||||
|
||||
normalizedRoutes = normalizedRoutes.filter(route => !domainRouteIds.has(route.id));
|
||||
normalizedRoutes.splice(entryRouteIndex < 0 ? normalizedRoutes.length : entryRouteIndex, 0, wrappedDomainRoute);
|
||||
});
|
||||
|
||||
@@ -74,6 +74,7 @@ function createBatchDeleteQuery(ids: Array<string | number>) {
|
||||
|
||||
type UserSimpleResponse = Omit<Api.SystemManage.UserSimple, 'id'> & {
|
||||
id: string | number;
|
||||
deptId?: string | number | null;
|
||||
};
|
||||
|
||||
type RoleResponse = Omit<Api.SystemManage.Role, 'id'> & {
|
||||
@@ -120,7 +121,8 @@ type UserManagementRelationTreeResponse = Omit<
|
||||
function normalizeUserSimple(user: UserSimpleResponse): Api.SystemManage.UserSimple {
|
||||
return {
|
||||
...user,
|
||||
id: normalizeStringId(user.id)
|
||||
id: normalizeStringId(user.id),
|
||||
deptId: normalizeNullableStringId(user.deptId)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user