Files
cn-rdms-web/src/views/product/setting/shared.ts

216 lines
6.5 KiB
TypeScript

import dayjs from 'dayjs';
export interface ProductManagerMemberLike {
roleId: string;
}
interface ProductTeamManageContext {
buttonCodes: readonly string[];
loginUserId: string | null | undefined;
currentManagerUserId: string | null | undefined;
}
interface ProductLifecycleStatusSummary {
tone: 'emerald' | 'amber' | 'slate' | 'rose';
caption: string;
description: string;
}
interface ProductLifecycleActionCardMeta {
tone: 'emerald' | 'amber' | 'slate' | 'rose';
description: string;
}
export const productSettingSectionKeys = ['base-info', 'team', 'lifecycle', 'danger'] as const;
export type ProductSettingSectionKey = (typeof productSettingSectionKeys)[number];
const productSettingSectionAuthCodeMap: Partial<Record<ProductSettingSectionKey, string>> = {
lifecycle: 'project:product:status',
danger: 'project:product:delete'
};
const productBaseInfoReadonlyMessageMap: Partial<Record<Api.Product.ProductStatusCode, string>> = {
paused: '当前产品已暂停,基础信息仅支持查看,不可编辑。',
archived: '当前产品已归档,基础信息仅支持查看,不可编辑。',
abandoned: '当前产品已废弃,基础信息仅支持查看,不可编辑。'
};
const productLifecycleStatusSummaryMap: Record<Api.Product.ProductStatusCode, ProductLifecycleStatusSummary> = {
active: {
tone: 'emerald',
caption: '产品正常服务中',
description: '当前可以执行暂停、归档或废弃。'
},
paused: {
tone: 'amber',
caption: '产品已暂停推进',
description: '条件恢复后可重新启用,也可继续归档或废弃。'
},
archived: {
tone: 'slate',
caption: '产品已收口归档',
description: '保留历史信息,当前不再开放新的生命周期动作。'
},
abandoned: {
tone: 'rose',
caption: '产品已停止建设',
description: '产品已结束推进,当前不再开放新的生命周期动作。'
}
};
const productLifecycleActionCardMetaMap: Record<Api.Product.ProductStatusActionCode, ProductLifecycleActionCardMeta> = {
pause: {
tone: 'amber',
description: '暂停当前产品,后续仍可恢复或归档。'
},
resume: {
tone: 'emerald',
description: '恢复启用后,继续推进产品协作。'
},
archive: {
tone: 'slate',
description: '收口当前产品,保留历史记录并结束维护。'
},
abandon: {
tone: 'rose',
description: '终止当前产品建设,请谨慎确认。'
}
};
const productTeamTableHeaderHeight = 40;
const productTeamTableRowHeight = 40;
export function shouldRequireManagerHandover(
targetRoleId: string,
currentManager: ProductManagerMemberLike | null | undefined
) {
if (!currentManager?.roleId) {
return false;
}
return targetRoleId === currentManager.roleId;
}
export function getPreviousManagerRoleOptions(roleOptions: Api.SystemManage.RoleSimple[], managerRoleId: string) {
return roleOptions.filter(role => role.id !== managerRoleId);
}
export function getProductSettingSectionKeys() {
return [...productSettingSectionKeys];
}
export function isProductBaseInfoEditable(status: Api.Product.ProductStatusCode | null | undefined) {
return status === 'active';
}
export function resolveVisibleProductSettingSections(
sectionKeys: readonly ProductSettingSectionKey[],
buttonCodes: readonly string[]
) {
return sectionKeys.filter(sectionKey => {
const authCode = productSettingSectionAuthCodeMap[sectionKey];
if (!authCode) {
return true;
}
return buttonCodes.includes(authCode);
});
}
export function resolveVisibleProductSettingSectionKey(
currentKey: ProductSettingSectionKey | string | null | undefined,
visibleSectionKeys: readonly ProductSettingSectionKey[]
) {
if (!visibleSectionKeys.length) {
return 'base-info' satisfies ProductSettingSectionKey;
}
if (currentKey && visibleSectionKeys.includes(currentKey as ProductSettingSectionKey)) {
return currentKey as ProductSettingSectionKey;
}
return visibleSectionKeys[0];
}
export function getProductBaseInfoReadonlyMessage(status: Api.Product.ProductStatusCode | null | undefined) {
if (!status || isProductBaseInfoEditable(status)) {
return '';
}
return productBaseInfoReadonlyMessageMap[status] || '当前产品状态不允许编辑基础信息。';
}
export function getProductLifecycleStatusSummary(status: Api.Product.ProductStatusCode) {
return productLifecycleStatusSummaryMap[status];
}
export function formatProductMemberDate(value: string | number | null | undefined) {
if (value === null || value === undefined || value === '') {
return '--';
}
const normalizedValue = typeof value === 'string' && /^\d+$/.test(value) ? Number(value) : value;
const parsedDate = dayjs(normalizedValue);
if (!parsedDate.isValid()) {
return String(value);
}
return parsedDate.format('YYYY-MM-DD');
}
export function filterProductMembersByKeyword(
members: readonly Api.Product.ProductMember[],
keyword: string | null | undefined
) {
return filterProductMembers(members, { keyword });
}
export function filterProductMembers(
members: readonly Api.Product.ProductMember[],
filters: {
keyword?: string | null | undefined;
roleId?: string | null | undefined;
}
) {
const normalizedKeyword = String(filters.keyword || '')
.trim()
.toLocaleLowerCase();
const normalizedRoleId = String(filters.roleId || '').trim();
if (!normalizedKeyword && !normalizedRoleId) {
return [...members];
}
return members.filter(member => {
const matchesKeyword = !normalizedKeyword || member.userNickname.toLocaleLowerCase().includes(normalizedKeyword);
const matchesRole = !normalizedRoleId || member.roleId === normalizedRoleId;
return matchesKeyword && matchesRole;
});
}
export function getProductTeamTableHeight(visibleRows: number) {
const normalizedRows = Math.max(0, visibleRows);
return productTeamTableHeaderHeight + normalizedRows * productTeamTableRowHeight;
}
export function getProductLifecycleActionCardMeta(actionCode: Api.Product.ProductStatusActionCode) {
return productLifecycleActionCardMetaMap[actionCode];
}
export function canManageProductTeam(context: ProductTeamManageContext) {
const hasUpdateAuth = context.buttonCodes.includes('project:product:update');
const loginUserId = String(context.loginUserId || '');
const currentManagerUserId = String(context.currentManagerUserId || '');
if (!hasUpdateAuth || !loginUserId || !currentManagerUserId) {
return false;
}
return loginUserId === currentManagerUserId;
}