From 67ef8af3fa37cfeeef356cc3707d47c90732b8f7 Mon Sep 17 00:00:00 2001 From: dk <1260500659@qq.com> Date: Wed, 6 May 2026 17:50:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E4=BA=A7=E5=93=81=E9=9C=80=E6=B1=82):=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BA=A7=E5=93=81=E9=9C=80=E6=B1=82=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E4=BB=A3=E7=A0=81=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/custom/readonly-field.vue | 44 ++ src/constants/dict.ts | 42 +- src/service/api/product.ts | 227 ++++++ src/typings/api/product.d.ts | 209 +++++ src/typings/components.d.ts | 8 + src/views/product/requirement/index.vue | 714 +++++++++++++++++- .../modules/member-select-option.vue | 39 + .../requirement/modules/module-tree-node.vue | 313 ++++++++ .../modules/requirement-action-dialog.vue | 154 ++++ .../modules/requirement-create-dialog.vue | 294 ++++++++ .../modules/requirement-detail-dialog.vue | 433 +++++++++++ .../modules/requirement-module-tree.vue | 451 +++++++++++ .../modules/requirement-search.vue | 115 +++ .../modules/requirement-split-dialog.vue | 248 ++++++ .../shared/requirement-master-data.ts | 98 +++ 15 files changed, 3386 insertions(+), 3 deletions(-) create mode 100644 src/components/custom/readonly-field.vue create mode 100644 src/views/product/requirement/modules/member-select-option.vue create mode 100644 src/views/product/requirement/modules/module-tree-node.vue create mode 100644 src/views/product/requirement/modules/requirement-action-dialog.vue create mode 100644 src/views/product/requirement/modules/requirement-create-dialog.vue create mode 100644 src/views/product/requirement/modules/requirement-detail-dialog.vue create mode 100644 src/views/product/requirement/modules/requirement-module-tree.vue create mode 100644 src/views/product/requirement/modules/requirement-search.vue create mode 100644 src/views/product/requirement/modules/requirement-split-dialog.vue create mode 100644 src/views/product/requirement/shared/requirement-master-data.ts diff --git a/src/components/custom/readonly-field.vue b/src/components/custom/readonly-field.vue new file mode 100644 index 0000000..7c5d0e0 --- /dev/null +++ b/src/components/custom/readonly-field.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/src/constants/dict.ts b/src/constants/dict.ts index ab60a20..b146136 100644 --- a/src/constants/dict.ts +++ b/src/constants/dict.ts @@ -32,6 +32,46 @@ export const RDMS_OBJECT_DIRECTION_LEGACY_DICT_CODE = 'rdms_product_direction'; * 用户所属公司字典编码 * * 对应业务字段:用户相关接口和页面中的 company - * 来源口径:当前系统“用户管理”页面按系统字典 system_user_company 做下拉和文案回显 + * 来源口径:当前系统"用户管理"页面按系统字典 system_user_company 做下拉和文案回显 */ export const SYSTEM_USER_COMPANY_DICT_CODE = 'system_user_company'; + +/** + * 需求终态状态字典编码 + * + * 对应业务字段:需求相关接口和页面中的 terminal status + * 来源口径:产品需求权限文档中定义,标签包括已关闭、已取消、已拒绝 + */ +export const RDMS_REQ_TERMINAL_STATUS_DICT_CODE = 'rdms_req_terminal_status'; + +/** + * 需求来源类型字典编码 + * + * 对应业务字段:需求相关接口和页面中的 sourceType + * 来源口径:产品需求文档中定义,标签包括工单流转、手动新增 + */ +export const RDMS_REQ_SOURCE_TYPE_DICT_CODE = 'rdms_req_source_type'; + +/** + * 需求优先级字典编码 + * + * 对应业务字段:需求相关接口和页面中的 priority + * 来源口径:产品需求文档中定义,标签包括紧急、高、中、低 + */ +export const RDMS_REQ_PRIORITY_DICT_CODE = 'rdms_req_priority'; + +/** + * 需求分类字典编码 + * + * 对应业务字段:需求相关接口和页面中的 category + * 来源口径:产品需求文档中定义,标签包括工程需求、用户需求、安全需求、体验优化、功能需求 + */ +export const RDMS_REQ_CATEGORY_DICT_CODE = 'rdms_req_category'; + +/** + * 需求状态字典编码 + * + * 对应业务字段:需求相关接口和页面中的 statusCode + * 来源口径:产品需求文档中定义 + */ +export const RDMS_REQ_STATUS_DICT_CODE = 'rdms_req_status'; diff --git a/src/service/api/product.ts b/src/service/api/product.ts index 9376c23..6fc10d4 100644 --- a/src/service/api/product.ts +++ b/src/service/api/product.ts @@ -157,6 +157,233 @@ export function fetchDeleteProduct(data: Api.Product.DeleteProductParams) { }); } +// ========== 产品需求 API ========== +const REQUIREMENT_PREFIX = `${WEB_SERVICE_PREFIX}/project/product/requirement`; + +type RequirementResponse = Omit< + Api.Product.Requirement, + 'id' | 'parentId' | 'moduleId' | 'proposerId' | 'currentHandlerUserId' | 'implementProjectId' | 'sourceBizId' +> & { + id: string | number; + parentId: string | number; + moduleId: string | number; + proposerId: string | number; + currentHandlerUserId?: string | number | null; + implementProjectId?: string | number | null; + sourceBizId?: string | number | null; + children?: RequirementResponse[]; +}; + +type RequirementPageResponse = Api.Product.PageResult; + +function normalizeRequirement(requirement: RequirementResponse): Api.Product.Requirement { + return { + ...requirement, + id: normalizeStringId(requirement.id), + parentId: normalizeStringId(requirement.parentId), + moduleId: normalizeStringId(requirement.moduleId), + proposerId: normalizeStringId(requirement.proposerId), + currentHandlerUserId: normalizeNullableStringId(requirement.currentHandlerUserId), + implementProjectId: normalizeNullableStringId(requirement.implementProjectId), + sourceBizId: normalizeNullableStringId(requirement.sourceBizId), + children: requirement.children?.map(normalizeRequirement) + }; +} + +/** 获取需求分页列表 */ +export async function fetchGetRequirementPage(params?: Api.Product.RequirementSearchParams) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/page`, + method: 'get', + params + }); + + return mapServiceResult(result as ServiceRequestResult, data => ({ + ...data, + list: data.list.map(normalizeRequirement) + })); +} + +/** 获取需求树形列表(支持分页,pageSize只算父需求) */ +export async function fetchGetRequirementTree(params?: Api.Product.RequirementSearchParams) { + const result = await request>({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/tree`, + method: 'get', + params + }); + + return mapServiceResult(result as ServiceRequestResult>, data => ({ + ...data, + list: data.list.map(normalizeRequirement) + })); +} + +/** 获取需求详情 */ +export async function fetchGetRequirement(id: string, productId: string) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/get`, + method: 'get', + params: { id, productId } + }); + + return mapServiceResult(result as ServiceRequestResult, normalizeRequirement); +} + +/** 创建需求 */ +export async function fetchCreateRequirement(data: Api.Product.SaveRequirementParams) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/create`, + method: 'post', + data + }); + + return mapServiceResult(result as ServiceRequestResult, normalizeStringId); +} + +/** 更新需求 */ +export function fetchUpdateRequirement(data: Api.Product.UpdateRequirementParams) { + return request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/update`, + method: 'put', + data + }); +} + +/** 变更需求状态 */ +export function fetchChangeRequirementStatus(data: Api.Product.ChangeRequirementStatusParams) { + return request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/change-status`, + method: 'post', + data + }); +} + +/** 删除需求 */ +export function fetchDeleteRequirement(data: Api.Product.DeleteRequirementParams) { + return request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/delete`, + method: 'post', + data + }); +} + +/** 拆分需求 */ +export async function fetchSplitRequirement(data: Api.Product.SplitRequirementParams) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/split`, + method: 'post', + data + }); + + return mapServiceResult(result as ServiceRequestResult, normalizeStringId); +} + +/** 关闭需求 */ +export function fetchCloseRequirement(data: Api.Product.CloseRequirementParams) { + return request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/close`, + method: 'post', + data + }); +} + +/** 获取需求可执行的状态动作列表 */ +export async function fetchGetRequirementAllowedTransitions(requirementId: string, productId: string) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/allowed-transitions`, + method: 'get', + params: { requirementId, productId } + }); + + return mapServiceResult(result as ServiceRequestResult, data => data); +} + +/** 获取需求生命周期信息 */ +export async function fetchGetRequirementLifecycle(requirementId: string, productId: string) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/lifecycle`, + method: 'get', + params: { requirementId, productId } + }); + + return mapServiceResult(result as ServiceRequestResult, data => data); +} + +// ========== 模块管理 API ========== +type RequirementModuleResponse = Omit & { + id: string | number; + parentId: string | number; + productId: string | number; + children?: RequirementModuleResponse[]; +}; + +function normalizeRequirementModule(module: RequirementModuleResponse): Api.Product.RequirementModule { + return { + ...module, + id: normalizeStringId(module.id), + parentId: normalizeStringId(module.parentId), + productId: normalizeStringId(module.productId), + children: module.children?.map(normalizeRequirementModule) + }; +} + +/** 获取需求模块树 */ +export async function fetchGetRequirementModuleTree(productId: string) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/module/tree`, + method: 'get', + params: { productId } + }); + + return mapServiceResult(result as ServiceRequestResult, data => + data.map(normalizeRequirementModule) + ); +} + +/** 创建需求模块 */ +export async function fetchCreateRequirementModule(data: Api.Product.SaveRequirementModuleParams) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/module/create`, + method: 'post', + data + }); + + return mapServiceResult(result as ServiceRequestResult, normalizeStringId); +} + +/** 更新需求模块 */ +export function fetchUpdateRequirementModule(data: Api.Product.SaveRequirementModuleParams) { + return request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/module/update`, + method: 'put', + data + }); +} + +/** 删除需求模块 */ +export function fetchDeleteRequirementModule(data: Api.Product.DeleteRequirementModuleParams) { + return request({ + ...safeJsonRequestConfig, + url: `${REQUIREMENT_PREFIX}/module/delete`, + method: 'post', + data + }); +} + export async function fetchGetProductSettings(id: string) { const result = await request({ ...safeJsonRequestConfig, diff --git a/src/typings/api/product.d.ts b/src/typings/api/product.d.ts index 1a13cb4..db9fec4 100644 --- a/src/typings/api/product.d.ts +++ b/src/typings/api/product.d.ts @@ -215,5 +215,214 @@ declare namespace Api { interface InactiveProductMemberParams { reason?: string | null; } + + // ========== 产品需求相关类型定义 ========== + /** 需求状态编码 */ + type RequirementStatusCode = + | 'pending_confirm' + | 'pending_review' + | 'pending_dispatch' + | 'implementing' + | 'accepted' + | 'closed' + | 'rejected' + | 'cancelled'; + + /** 需求来源类型 */ + type RequirementSourceType = 'manual' | 'work_order'; + + /** 需求优先级 */ + type RequirementPriority = 0 | 1 | 2 | 3; + + /** 是否需要评审 */ + type RequirementReviewRequired = 0 | 1; + + // ========== 需求实体 ========== + + interface Requirement { + /** 需求编号 */ + id: string; + /** 产品 ID */ + productId: string; + /** 父需求编号(0表示顶级需求) */ + parentId: string; + /** 所属模块编号 */ + moduleId: string; + /** 是否需要评审(0不需要;1需要) */ + reviewRequired: RequirementReviewRequired; + /** 需求标题 */ + title: string; + /** 需求描述(富文本) */ + description?: string | null; + /** 需求分类字典值 */ + category: string; + /** 需求分类名称 */ + categoryName?: string | null; + /** 来源类型 */ + sourceType: RequirementSourceType; + /** 来源业务ID */ + sourceBizId?: string | null; + /** 优先级(0低 1中 2高 3紧急) */ + priority: RequirementPriority; + /** 优先级名称 */ + priorityName?: string | null; + /** 当前状态编码 */ + statusCode: RequirementStatusCode; + /** 当前状态名称 */ + statusName?: string | null; + /** 最近一次状态动作原因 */ + lastStatusReason?: string | null; + /** 提出人用户编号 */ + proposerId: string; + /** 提出人用户姓名 */ + proposerNickname?: string | null; + /** 当前处理人用户编号 */ + currentHandlerUserId?: string | null; + /** 当前处理人姓名 */ + currentHandlerUserNickname?: string | null; + /** 默认实现项目编号 */ + implementProjectId?: string | null; + /** 实现项目名称 */ + implementProjectName?: string | null; + /** 预期完成时间 */ + completionDate: string; + /** 排序值 */ + sort: number; + /** 创建时间 */ + createTime: string; + /** 更新时间 */ + updateTime: string; + /** 子需求列表(树形结构) */ + children?: Requirement[]; + /** 是否为终态 */ + terminal?: boolean; + } + + // ========== 需求模块实体 ========== + + interface RequirementModule { + /** 模块编号 */ + id: string | undefined; + /** 父模块编号(0表示顶级) */ + parentId: string | undefined; + /** 所属产品编号 */ + productId: string; + /** 模块名称 */ + moduleName: string; + /** 模块说明 */ + remark?: string | null; + /** 图标 */ + icon?: string | null; + /** 排序值 */ + sort: number; + /** 子模块列表 */ + children?: RequirementModule[]; + } + + // ========== 需求生命周期 ========== + + interface RequirementLifecycleAction { + actionCode: string; + actionName: string; + toStatusCode: string; + toStatusName: string; + needReason: boolean; + } + + interface RequirementLifecycleInfo { + statusCode: RequirementStatusCode; + statusName?: string | null; + lastStatusReason?: string | null; + terminal: boolean; + allowEdit: boolean; + availableActions: RequirementLifecycleAction[]; + } + + // ========== 请求参数类型 ========== + + /** 需求分页查询参数 */ + type RequirementSearchParams = CommonType.RecordNullable< + Pick & + Pick< + Requirement, + 'moduleId' | 'category' | 'priority' | 'statusCode' | 'currentHandlerUserId' | 'sourceType' + > & { + productId: string; + title?: string; + } + >; + + /** 创建需求参数 */ + type SaveRequirementParams = Pick< + Requirement, + | 'productId' + | 'moduleId' + | 'reviewRequired' + | 'title' + | 'description' + | 'category' + | 'priority' + | 'proposerId' + | 'currentHandlerUserId' + | 'implementProjectId' + | 'completionDate' + | 'sort' + >; + + /** 更新需求参数 */ + type UpdateRequirementParams = { id: string } & SaveRequirementParams; + + /** 变更需求状态参数 */ + interface ChangeRequirementStatusParams { + id: string; + productId: string; + actionCode: string; + reason?: string | null; + implementProjectId?: string | null; + } + + /** 关闭需求参数 */ + interface CloseRequirementParams { + id: string; + productId: string; + reason: string; + } + + /** 拆分需求参数 */ + type SplitRequirementParams = Pick< + Requirement, + | 'parentId' + | 'productId' + | 'moduleId' + | 'reviewRequired' + | 'title' + | 'description' + | 'category' + | 'priority' + | 'proposerId' + | 'currentHandlerUserId' + | 'completionDate' + | 'sort' + >; + + /** 删除需求参数 */ + interface DeleteRequirementParams { + id: string; + productId: string; + } + + // ========== 模块请求参数 ========== + + /** 保存模块参数 */ + type SaveRequirementModuleParams = Pick< + RequirementModule, + 'id' | 'productId' | 'parentId' | 'moduleName' | 'remark' | 'icon' | 'sort' + >; + + /** 删除模块参数 */ + interface DeleteRequirementModuleParams { + id: string | undefined; + productId: string; + } } } diff --git a/src/typings/components.d.ts b/src/typings/components.d.ts index bb79c3e..00caf34 100644 --- a/src/typings/components.d.ts +++ b/src/typings/components.d.ts @@ -122,11 +122,17 @@ declare module 'vue' { IconMdiChevronDoubleDown: typeof import('~icons/mdi/chevron-double-down')['default'] IconMdiChevronDoubleUp: typeof import('~icons/mdi/chevron-double-up')['default'] IconMdiChevronDown: typeof import('~icons/mdi/chevron-down')['default'] + IconMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default'] IconMdiDeleteOutline: typeof import('~icons/mdi/delete-outline')['default'] + IconMdiDotsHorizontal: typeof import('~icons/mdi/dots-horizontal')['default'] IconMdiDrag: typeof import('~icons/mdi/drag')['default'] IconMdiFilterVariant: typeof import('~icons/mdi/filter-variant')['default'] + IconMdiFolderOpen: typeof import('~icons/mdi/folder-open')['default'] + IconMdiFolderOutline: typeof import('~icons/mdi/folder-outline')['default'] + IconMdiFolderPlusOutline: typeof import('~icons/mdi/folder-plus-outline')['default'] IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default'] IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default'] + IconMdiMenuDown: typeof import('~icons/mdi/menu-down')['default'] IconMdiPencilOutline: typeof import('~icons/mdi/pencil-outline')['default'] IconMdiPlus: typeof import('~icons/mdi/plus')['default'] IconMdiRefresh: typeof import('~icons/mdi/refresh')['default'] @@ -137,6 +143,8 @@ declare module 'vue' { LookForward: typeof import('./../components/custom/look-forward.vue')['default'] MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default'] PinToggler: typeof import('./../components/common/pin-toggler.vue')['default'] + PrioritySelect: typeof import('../views/product/requirement/modules/priority-select.vue')['default'] + ReadonlyField: typeof import('./../components/custom/readonly-field.vue')['default'] ReloadButton: typeof import('./../components/common/reload-button.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] diff --git a/src/views/product/requirement/index.vue b/src/views/product/requirement/index.vue index e1b32d5..792e3f3 100644 --- a/src/views/product/requirement/index.vue +++ b/src/views/product/requirement/index.vue @@ -1,7 +1,717 @@ - + + diff --git a/src/views/product/requirement/modules/member-select-option.vue b/src/views/product/requirement/modules/member-select-option.vue new file mode 100644 index 0000000..43d057f --- /dev/null +++ b/src/views/product/requirement/modules/member-select-option.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/src/views/product/requirement/modules/module-tree-node.vue b/src/views/product/requirement/modules/module-tree-node.vue new file mode 100644 index 0000000..38cb082 --- /dev/null +++ b/src/views/product/requirement/modules/module-tree-node.vue @@ -0,0 +1,313 @@ + + + + + diff --git a/src/views/product/requirement/modules/requirement-action-dialog.vue b/src/views/product/requirement/modules/requirement-action-dialog.vue new file mode 100644 index 0000000..5b1ea9a --- /dev/null +++ b/src/views/product/requirement/modules/requirement-action-dialog.vue @@ -0,0 +1,154 @@ + + + diff --git a/src/views/product/requirement/modules/requirement-create-dialog.vue b/src/views/product/requirement/modules/requirement-create-dialog.vue new file mode 100644 index 0000000..66bdeca --- /dev/null +++ b/src/views/product/requirement/modules/requirement-create-dialog.vue @@ -0,0 +1,294 @@ + + + + + diff --git a/src/views/product/requirement/modules/requirement-detail-dialog.vue b/src/views/product/requirement/modules/requirement-detail-dialog.vue new file mode 100644 index 0000000..711332f --- /dev/null +++ b/src/views/product/requirement/modules/requirement-detail-dialog.vue @@ -0,0 +1,433 @@ + + + + + diff --git a/src/views/product/requirement/modules/requirement-module-tree.vue b/src/views/product/requirement/modules/requirement-module-tree.vue new file mode 100644 index 0000000..bdb3e49 --- /dev/null +++ b/src/views/product/requirement/modules/requirement-module-tree.vue @@ -0,0 +1,451 @@ + + + + + diff --git a/src/views/product/requirement/modules/requirement-search.vue b/src/views/product/requirement/modules/requirement-search.vue new file mode 100644 index 0000000..629fe29 --- /dev/null +++ b/src/views/product/requirement/modules/requirement-search.vue @@ -0,0 +1,115 @@ + + + + + diff --git a/src/views/product/requirement/modules/requirement-split-dialog.vue b/src/views/product/requirement/modules/requirement-split-dialog.vue new file mode 100644 index 0000000..50d624b --- /dev/null +++ b/src/views/product/requirement/modules/requirement-split-dialog.vue @@ -0,0 +1,248 @@ + + + + + diff --git a/src/views/product/requirement/shared/requirement-master-data.ts b/src/views/product/requirement/shared/requirement-master-data.ts new file mode 100644 index 0000000..77764b3 --- /dev/null +++ b/src/views/product/requirement/shared/requirement-master-data.ts @@ -0,0 +1,98 @@ +import { transformRecordToOption } from '@/utils/common'; + +export type RequirementStatusActionCode = + | 'claim_to_review' + | 'claim_to_dispatch' + | 'reject' + | 'to_dispatch' + | 'dispatch' + | 'cancel' + | 'accept' + | 'close'; + +export const requirementStatusRecord: Record = { + pending_confirm: '待确认', + pending_review: '待评审', + pending_dispatch: '待分流', + implementing: '实施中', + accepted: '已验收', + closed: '已关闭', + rejected: '已拒绝', + cancelled: '已取消' +}; + +export const requirementStatusOptions = transformRecordToOption(requirementStatusRecord); + +export const requirementStatusActionRecord: Record = { + claim_to_review: '认领', + claim_to_dispatch: '认领', + reject: '拒绝', + to_dispatch: '评审通过', + dispatch: '分流', + cancel: '取消', + accept: '验收通过', + close: '关闭' +}; + +export function getRequirementStatusLabel(status: Api.Product.RequirementStatusCode) { + return requirementStatusRecord[status]; +} + +export function getRequirementStatusTagType(status: Api.Product.RequirementStatusCode): UI.ThemeColor { + const statusTagTypeMap: Record = { + pending_confirm: 'info', + pending_review: 'warning', + pending_dispatch: 'primary', + implementing: 'primary', + accepted: 'success', + closed: 'info', + rejected: 'danger', + cancelled: 'danger' + }; + + return statusTagTypeMap[status]; +} + +export function getRequirementActionLabel(actionCode: RequirementStatusActionCode) { + return requirementStatusActionRecord[actionCode]; +} + +export function getRequirementActionTagType( + actionCode: RequirementStatusActionCode +): 'primary' | 'success' | 'warning' | 'danger' | 'info' { + const actionTagTypeMap: Record = { + claim_to_review: 'primary', + claim_to_dispatch: 'primary', + reject: 'danger', + to_dispatch: 'success', + dispatch: 'primary', + cancel: 'danger', + accept: 'success', + close: 'info' + }; + + return actionTagTypeMap[actionCode]; +} + +export function isRequirementActionTerminal(actionCode: RequirementStatusActionCode) { + const terminalActions: RequirementStatusActionCode[] = ['reject', 'cancel', 'close']; + return terminalActions.includes(actionCode); +} + +export function isRequirementActionNeedProject(actionCode: RequirementStatusActionCode) { + return actionCode === 'dispatch'; +} + +export function isRequirementActionNeedReviewChoice(actionCode: RequirementStatusActionCode) { + return actionCode === 'claim_to_review' || actionCode === 'claim_to_dispatch'; +} + +export function getRequirementActionDisplayName(action: Api.Product.RequirementLifecycleAction): string { + const code = action.actionCode as RequirementStatusActionCode; + + if (code === 'claim_to_review' || code === 'claim_to_dispatch') { + return '认领'; + } + + return action.actionName; +}