From 3a064eb09fc7df25e6e8fa1f45e1438acf3953f5 Mon Sep 17 00:00:00 2001 From: caozehui <2427765068@qq.com> Date: Fri, 15 May 2026 09:31:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(infra):=20=E6=96=B0=E5=A2=9E=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=9C=BA=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=20-=20=E6=96=B0=E5=A2=9E=E7=8A=B6=E6=80=81=E6=9C=BA?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E5=92=8C=E7=8A=B6=E6=80=81=E6=B5=81=E8=BD=AC?= =?UTF-8?q?=E7=9A=84=E5=AE=8C=E6=95=B4=20CRUD=20=E5=8A=9F=E8=83=BD=20-=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AD=97=E5=85=B8=E7=BC=96=E7=A0=81=20OBJECT?= =?UTF-8?q?=5FSTATUS=5FMODEL=5FOBJECT=5FTYPE=5FDICT=5FCODE=20=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E5=AF=B9=E8=B1=A1=E7=B1=BB=E5=9E=8B=E4=B8=8B=E6=8B=89?= =?UTF-8?q?=E9=80=89=E6=8B=A9=20-=20=E5=AE=9E=E7=8E=B0=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E6=9C=BA=E5=88=97=E8=A1=A8=E9=A1=B5=E3=80=81=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E3=80=81=E6=93=8D=E4=BD=9C=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E6=A1=86=E5=92=8C=E7=8A=B6=E6=80=81=E6=B5=81=E8=BD=AC=E7=AE=A1?= =?UTF-8?q?=E7=90=86=20-=20=E6=96=B0=E5=A2=9E=20infra=20API=20=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=B0=81=E8=A3=85=E5=92=8C=E7=B1=BB=E5=9E=8B=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=20-=20=E9=81=B5=E5=BE=AA=E9=A1=B9=E7=9B=AE=E8=A7=84?= =?UTF-8?q?=E8=8C=83=EF=BC=9A=E4=BD=BF=E7=94=A8=20TableSearchFields=20?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E7=BB=84=E4=BB=B6=E3=80=81BusinessTableActio?= =?UTF-8?q?nCell=20=E6=93=8D=E4=BD=9C=E5=88=97=E3=80=81=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E7=9A=84=E7=8A=B6=E6=80=81=E6=A0=87=E7=AD=BE=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 涉及文件: - src/constants/dict.ts: 新增对象类型字典编码 - src/service/api/infra.ts: 新增状态机和状态流转相关 API - src/typings/api/infra.d.ts: 新增状态机相关类型定义 - src/views/infra/state-machine/: 新增状态机管理页面及子组件 --- src/constants/dict.ts | 8 + src/service/api/index.ts | 1 + src/service/api/infra.ts | 208 +++++++++ src/typings/api/infra.d.ts | 101 +++++ src/views/infra/state-machine/index.vue | 388 ++++++++++++++++- .../modules/state-machine-operate-dialog.vue | 269 ++++++++++++ .../modules/state-machine-search.vue | 142 ++++++ .../modules/state-transition-dialog.vue | 406 ++++++++++++++++++ .../state-transition-operate-dialog.vue | 234 ++++++++++ .../modules/state-transition-search.vue | 97 +++++ src/views/infra/state-machine/shared.ts | 38 ++ 11 files changed, 1891 insertions(+), 1 deletion(-) create mode 100644 src/service/api/infra.ts create mode 100644 src/typings/api/infra.d.ts create mode 100644 src/views/infra/state-machine/modules/state-machine-operate-dialog.vue create mode 100644 src/views/infra/state-machine/modules/state-machine-search.vue create mode 100644 src/views/infra/state-machine/modules/state-transition-dialog.vue create mode 100644 src/views/infra/state-machine/modules/state-transition-operate-dialog.vue create mode 100644 src/views/infra/state-machine/modules/state-transition-search.vue create mode 100644 src/views/infra/state-machine/shared.ts diff --git a/src/constants/dict.ts b/src/constants/dict.ts index f61f04e..63d753d 100644 --- a/src/constants/dict.ts +++ b/src/constants/dict.ts @@ -76,6 +76,14 @@ export const RDMS_PROJECT_TYPE_DICT_CODE = 'rdms_project_type'; */ export const RDMS_PROJECT_EXECUTION_TYPE_DICT_CODE = 'rdms_project_execution_type'; +/** + * 状态机对象类型字典编码 + * + * 对应业务字段:状态机管理中的 objectType / 对象类型 + * 来源口径:用户明确指定对象类型下拉来自运行时字典 object_status_model_object_type + */ +export const OBJECT_STATUS_MODEL_OBJECT_TYPE_DICT_CODE = 'object_status_model_object_type'; + /** * 需求允许删除的状态字典编码 * diff --git a/src/service/api/index.ts b/src/service/api/index.ts index b73862c..308d494 100644 --- a/src/service/api/index.ts +++ b/src/service/api/index.ts @@ -1,6 +1,7 @@ export * from './auth'; export * from './dict'; export * from './file'; +export * from './infra'; export * from './object-context'; export * from './product'; export * from './project'; diff --git a/src/service/api/infra.ts b/src/service/api/infra.ts new file mode 100644 index 0000000..7c13e90 --- /dev/null +++ b/src/service/api/infra.ts @@ -0,0 +1,208 @@ +import { WEB_SERVICE_PREFIX } from '@/constants/service'; +import { request } from '../request'; +import { type ServiceRequestResult, mapServiceResult, normalizeStringId, safeJsonRequestConfig } from './shared'; + +const OBJECT_STATUS_MODEL_PREFIX = `${WEB_SERVICE_PREFIX}/project/status/model`; +const OBJECT_STATUS_TRANSITION_PREFIX = `${WEB_SERVICE_PREFIX}/project/status/transition`; + +type ObjectStatusModelResponse = Omit< + Api.Infra.ObjectStatusModel, + | 'id' + | 'initialFlag' + | 'terminalFlag' + | 'allowEdit' + | 'progressExcludedFlag' + | 'allowCreateProject' + | 'allowCreateRequirement' +> & { + id: string | number; + initialFlag: boolean | number | string | null | undefined; + terminalFlag: boolean | number | string | null | undefined; + allowEdit: boolean | number | string | null | undefined; + progressExcludedFlag: boolean | number | string | null | undefined; + allowCreateProject: boolean | number | string | null | undefined; + allowCreateRequirement: boolean | number | string | null | undefined; +}; + +type ObjectStatusTransitionResponse = Omit & { + id: string | number; + needReason: boolean | number | string | null | undefined; +}; + +type ObjectStatusModelPageResponse = Api.Infra.PageResult; + +type ObjectStatusTransitionPageResponse = Api.Infra.PageResult; + +function createBatchDeleteQuery(ids: string[]) { + const query = new URLSearchParams(); + + ids.forEach(id => { + query.append('ids', id); + }); + + return query.toString(); +} + +function normalizeBooleanFlag(value: boolean | number | string | null | undefined) { + if (typeof value === 'boolean') { + return value; + } + + if (typeof value === 'number') { + return value === 1; + } + + if (typeof value === 'string') { + const normalized = value.trim().toLowerCase(); + + if (!normalized || normalized === '0' || normalized === 'false' || normalized === 'n') { + return false; + } + + return true; + } + + return false; +} + +function normalizeObjectStatusModel(model: ObjectStatusModelResponse): Api.Infra.ObjectStatusModel { + return { + ...model, + id: normalizeStringId(model.id), + initialFlag: normalizeBooleanFlag(model.initialFlag), + terminalFlag: normalizeBooleanFlag(model.terminalFlag), + allowEdit: normalizeBooleanFlag(model.allowEdit), + progressExcludedFlag: normalizeBooleanFlag(model.progressExcludedFlag), + allowCreateProject: normalizeBooleanFlag(model.allowCreateProject), + allowCreateRequirement: normalizeBooleanFlag(model.allowCreateRequirement) + }; +} + +function normalizeObjectStatusTransition(transition: ObjectStatusTransitionResponse): Api.Infra.ObjectStatusTransition { + return { + ...transition, + id: normalizeStringId(transition.id), + needReason: normalizeBooleanFlag(transition.needReason) + }; +} + +export async function fetchGetObjectStatusModelPage(params?: Api.Infra.ObjectStatusModelSearchParams) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${OBJECT_STATUS_MODEL_PREFIX}/page`, + method: 'get', + params + }); + + return mapServiceResult(result as ServiceRequestResult, data => ({ + ...data, + list: data.list.map(normalizeObjectStatusModel) + })); +} + +export async function fetchGetObjectStatusModel(id: string) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${OBJECT_STATUS_MODEL_PREFIX}/get`, + method: 'get', + params: { id } + }); + + return mapServiceResult(result as ServiceRequestResult, normalizeObjectStatusModel); +} + +export async function fetchCreateObjectStatusModel(data: Api.Infra.SaveObjectStatusModelParams) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${OBJECT_STATUS_MODEL_PREFIX}/create`, + method: 'post', + data + }); + + return mapServiceResult(result as ServiceRequestResult, normalizeStringId); +} + +export function fetchUpdateObjectStatusModel(data: { id: string } & Api.Infra.SaveObjectStatusModelParams) { + return request({ + url: `${OBJECT_STATUS_MODEL_PREFIX}/update`, + method: 'put', + data + }); +} + +export function fetchDeleteObjectStatusModel(id: string) { + return request({ + url: `${OBJECT_STATUS_MODEL_PREFIX}/delete`, + method: 'delete', + params: { id } + }); +} + +export function fetchBatchDeleteObjectStatusModel(ids: string[]) { + return request({ + url: `${OBJECT_STATUS_MODEL_PREFIX}/delete-list?${createBatchDeleteQuery(ids)}`, + method: 'delete' + }); +} + +export async function fetchGetObjectStatusTransitionPage(params?: Api.Infra.ObjectStatusTransitionSearchParams) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${OBJECT_STATUS_TRANSITION_PREFIX}/page`, + method: 'get', + params + }); + + return mapServiceResult(result as ServiceRequestResult, data => ({ + ...data, + list: data.list.map(normalizeObjectStatusTransition) + })); +} + +export async function fetchGetObjectStatusTransition(id: string) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${OBJECT_STATUS_TRANSITION_PREFIX}/get`, + method: 'get', + params: { id } + }); + + return mapServiceResult( + result as ServiceRequestResult, + normalizeObjectStatusTransition + ); +} + +export async function fetchCreateObjectStatusTransition(data: Api.Infra.SaveObjectStatusTransitionParams) { + const result = await request({ + ...safeJsonRequestConfig, + url: `${OBJECT_STATUS_TRANSITION_PREFIX}/create`, + method: 'post', + data + }); + + return mapServiceResult(result as ServiceRequestResult, normalizeStringId); +} + +export function fetchUpdateObjectStatusTransition(data: { id: string } & Api.Infra.SaveObjectStatusTransitionParams) { + return request({ + url: `${OBJECT_STATUS_TRANSITION_PREFIX}/update`, + method: 'put', + data + }); +} + +export function fetchDeleteObjectStatusTransition(id: string) { + return request({ + url: `${OBJECT_STATUS_TRANSITION_PREFIX}/delete`, + method: 'delete', + params: { id } + }); +} + +export function fetchBatchDeleteObjectStatusTransition(ids: string[]) { + return request({ + url: `${OBJECT_STATUS_TRANSITION_PREFIX}/delete-list?${createBatchDeleteQuery(ids)}`, + method: 'delete' + }); +} diff --git a/src/typings/api/infra.d.ts b/src/typings/api/infra.d.ts new file mode 100644 index 0000000..d71fffb --- /dev/null +++ b/src/typings/api/infra.d.ts @@ -0,0 +1,101 @@ +declare namespace Api { + /** + * namespace Infra + * + * backend api module: "project/status/*" + */ + namespace Infra { + type CommonStatus = 0 | 1; + + interface PageParams { + pageNo: number; + pageSize: number; + } + + interface PageResult { + total: number; + list: T[]; + } + + interface ObjectStatusModel { + id: string; + objectType: string; + statusCode: string; + statusName: string; + sort: number; + status: CommonStatus; + initialFlag: boolean; + terminalFlag: boolean; + allowEdit: boolean; + progressExcludedFlag: boolean; + allowCreateProject: boolean; + allowCreateRequirement: boolean; + remark?: string | null; + creator?: string | null; + createTime: string; + updater?: string | null; + updateTime: string; + } + + type ObjectStatusModelSearchParams = CommonType.RecordNullable< + Pick & + Pick & { + keyword?: string; + } + >; + + type SaveObjectStatusModelParams = Pick< + ObjectStatusModel, + | 'objectType' + | 'statusCode' + | 'statusName' + | 'sort' + | 'status' + | 'initialFlag' + | 'terminalFlag' + | 'allowEdit' + | 'progressExcludedFlag' + | 'allowCreateProject' + | 'allowCreateRequirement' + > & { + remark?: string | null; + }; + + type ObjectStatusModelList = PageResult; + + interface ObjectStatusTransition { + id: string; + objectType: string; + actionCode: string; + actionName: string; + fromStatusCode: string; + fromStatusName?: string | null; + toStatusCode: string; + toStatusName?: string | null; + needReason: boolean; + status: CommonStatus; + remark?: string | null; + creator?: string | null; + createTime: string; + updater?: string | null; + updateTime: string; + } + + type ObjectStatusTransitionSearchParams = CommonType.RecordNullable< + Pick & + Pick< + ObjectStatusTransition, + 'objectType' | 'fromStatusCode' | 'toStatusCode' | 'status' | 'actionCode' | 'actionName' + > + >; + + type SaveObjectStatusTransitionParams = Pick< + ObjectStatusTransition, + 'objectType' | 'actionCode' | 'actionName' | 'fromStatusCode' | 'toStatusCode' | 'needReason' | 'status' + > & { + remark?: string | null; + }; + + type ObjectStatusTransitionList = PageResult; + } +} diff --git a/src/views/infra/state-machine/index.vue b/src/views/infra/state-machine/index.vue index 1450eab..0c6dbd6 100644 --- a/src/views/infra/state-machine/index.vue +++ b/src/views/infra/state-machine/index.vue @@ -1,3 +1,389 @@ + + + + diff --git a/src/views/infra/state-machine/modules/state-machine-operate-dialog.vue b/src/views/infra/state-machine/modules/state-machine-operate-dialog.vue new file mode 100644 index 0000000..10b1f5a --- /dev/null +++ b/src/views/infra/state-machine/modules/state-machine-operate-dialog.vue @@ -0,0 +1,269 @@ + + + + + diff --git a/src/views/infra/state-machine/modules/state-machine-search.vue b/src/views/infra/state-machine/modules/state-machine-search.vue new file mode 100644 index 0000000..bdfb931 --- /dev/null +++ b/src/views/infra/state-machine/modules/state-machine-search.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/src/views/infra/state-machine/modules/state-transition-dialog.vue b/src/views/infra/state-machine/modules/state-transition-dialog.vue new file mode 100644 index 0000000..f400a9d --- /dev/null +++ b/src/views/infra/state-machine/modules/state-transition-dialog.vue @@ -0,0 +1,406 @@ + + + + + diff --git a/src/views/infra/state-machine/modules/state-transition-operate-dialog.vue b/src/views/infra/state-machine/modules/state-transition-operate-dialog.vue new file mode 100644 index 0000000..e439137 --- /dev/null +++ b/src/views/infra/state-machine/modules/state-transition-operate-dialog.vue @@ -0,0 +1,234 @@ + + + + + diff --git a/src/views/infra/state-machine/modules/state-transition-search.vue b/src/views/infra/state-machine/modules/state-transition-search.vue new file mode 100644 index 0000000..76a4af5 --- /dev/null +++ b/src/views/infra/state-machine/modules/state-transition-search.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/src/views/infra/state-machine/shared.ts b/src/views/infra/state-machine/shared.ts new file mode 100644 index 0000000..84763db --- /dev/null +++ b/src/views/infra/state-machine/shared.ts @@ -0,0 +1,38 @@ +import dayjs from 'dayjs'; + +export const statusOptions: Array<{ label: string; value: Api.Infra.CommonStatus }> = [ + { label: '启用', value: 0 }, + { label: '停用', value: 1 } +]; + +export function getStatusLabel(value?: Api.Infra.CommonStatus | null) { + if (value === 0) { + return '启用'; + } + + if (value === 1) { + return '停用'; + } + + return '--'; +} + +export function getStatusTagType(value?: Api.Infra.CommonStatus | null): UI.ThemeColor { + return value === 0 ? 'success' : 'warning'; +} + +export function getBooleanLabel(value?: boolean | null) { + return value ? '是' : '否'; +} + +export function getBooleanTagType(value?: boolean | null): UI.ThemeColor { + return value ? 'success' : 'info'; +} + +export function formatDateTime(value?: string | number | null) { + if (!value) { + return '--'; + } + + return dayjs(value).format('YYYY-MM-DD HH:mm:ss'); +}