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> = { lifecycle: 'project:product:status', danger: 'project:product:delete' }; const productBaseInfoReadonlyMessageMap: Partial> = { paused: '当前产品已暂停,基础信息仅支持查看,不可编辑。', archived: '当前产品已归档,基础信息仅支持查看,不可编辑。', abandoned: '当前产品已废弃,基础信息仅支持查看,不可编辑。' }; const productLifecycleStatusSummaryMap: Record = { active: { tone: 'emerald', caption: '产品正常服务中', description: '当前可以执行暂停、归档或废弃。' }, paused: { tone: 'amber', caption: '产品已暂停推进', description: '条件恢复后可重新启用,也可继续归档或废弃。' }, archived: { tone: 'slate', caption: '产品已收口归档', description: '保留历史信息,当前不再开放新的生命周期动作。' }, abandoned: { tone: 'rose', caption: '产品已停止建设', description: '产品已结束推进,当前不再开放新的生命周期动作。' } }; const productLifecycleActionCardMetaMap: Record = { 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; }