From f10cf167372f2d8c4c0e14f42f361d7ab96d8347 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期五, 03 七月 2026 13:59:00 +0800
Subject: [PATCH] Merge branch 'dev_pro2.0' of http://114.132.189.42:9002/r/mom-pro2-before into dev_pro2.0

---
 src/views/mes/pd/project/data.ts |  535 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 535 insertions(+), 0 deletions(-)

diff --git a/src/views/mes/pd/project/data.ts b/src/views/mes/pd/project/data.ts
new file mode 100644
index 0000000..9d9cac6
--- /dev/null
+++ b/src/views/mes/pd/project/data.ts
@@ -0,0 +1,535 @@
+import type { VbenFormApi, VbenFormSchema } from '#/adapter/form';
+import type { VxeTableGridOptions } from '#/adapter/vxe-table';
+import type { MesMdItemApi } from '#/api/mes/md/item';
+import type { MesPdProjectApi } from '#/api/mes/pd/project';
+
+import { h, markRaw } from 'vue';
+
+import {
+  DICT_TYPE,
+  MesAutoCodeRuleCode,
+  MesPdDocumentTypeEnum,
+} from '#/packages/constants/src';
+import { getDictOptions } from '#/packages/effects/hooks/src';
+
+import { Button } from 'ant-design-vue';
+
+import { generateAutoCode } from '#/api/mes/md/autocode/record';
+import { isPdMockEnabled } from '#/api/mes/pd/mock/enabled';
+import { mockNextCode } from '#/api/mes/pd/mock/utils';
+import { getRangePickerDefaultProps } from '#/utils';
+import { MdItemSelect } from '#/views/mes/md/item/components';
+
+export type FormType = 'create' | 'detail' | 'update';
+
+async function generateTaskCode(formApi?: VbenFormApi) {
+  const code = isPdMockEnabled()
+    ? mockNextCode('DT-2026', Math.floor(Date.now() % 10_000))
+    : await generateAutoCode(MesAutoCodeRuleCode.PD_PROJECT_CODE);
+  await formApi?.setFieldValue('code', code);
+}
+
+async function syncTaskNameFromProduct(
+  formApi: VbenFormApi | undefined,
+  productName?: string,
+) {
+  const version = (await formApi?.getFieldValue('version')) as
+    | string
+    | undefined;
+  const name = productName?.trim();
+  if (name && version?.trim()) {
+    await formApi?.setFieldValue('name', `${name} ${version.trim()} 璁捐`);
+  }
+}
+
+/** 浜у搧閫夋嫨锛氶�夋嫨鍚庤嚜鍔ㄥ甫鍑轰骇鍝佺紪鐮� */
+function useProductSelectSchema(
+  formApi: VbenFormApi | undefined,
+  readonly: boolean,
+): VbenFormSchema[] {
+  if (readonly) {
+    return [
+      {
+        fieldName: 'productCode',
+        label: '浜у搧缂栫爜',
+        component: 'Input',
+        componentProps: { disabled: true },
+      },
+      {
+        fieldName: 'productName',
+        label: '浜у搧鍚嶇О',
+        component: 'Input',
+        componentProps: { disabled: true },
+      },
+    ];
+  }
+  return [
+    {
+      fieldName: 'productId',
+      label: '浜у搧',
+      component: markRaw(MdItemSelect),
+      componentProps: {
+        placeholder: '璇烽�夋嫨浜у搧',
+        onChange: async (item: MesMdItemApi.Item | undefined) => {
+          await formApi?.setFieldValue('productCode', item?.code ?? '');
+          await formApi?.setFieldValue('productName', item?.name ?? '');
+          await syncTaskNameFromProduct(formApi, item?.name);
+        },
+      },
+      rules: 'selectRequired',
+    },
+    {
+      fieldName: 'productCode',
+      label: '浜у搧缂栫爜',
+      component: 'Input',
+      componentProps: {
+        disabled: true,
+        placeholder: '閫夋嫨浜у搧鍚庤嚜鍔ㄥ甫鍑�',
+      },
+      rules: 'required',
+    },
+    {
+      fieldName: 'productName',
+      component: 'Input',
+      dependencies: { triggerFields: [''], show: () => false },
+    },
+  ];
+}
+
+/** 鍙戣捣璁捐浠诲姟琛ㄥ崟 */
+export function useCreateTaskSchema(formApi?: VbenFormApi): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'id',
+      component: 'Input',
+      dependencies: { triggerFields: [''], show: () => false },
+    },
+    {
+      fieldName: 'code',
+      label: '浠诲姟缂栫爜',
+      component: 'Input',
+      componentProps: { placeholder: '璇风偣鍑荤敓鎴愪换鍔$紪鐮�' },
+      rules: 'required',
+      suffix: () =>
+        h(
+          Button,
+          { type: 'default', onClick: () => generateTaskCode(formApi) },
+          { default: () => '鐢熸垚' },
+        ),
+    },
+    ...useProductSelectSchema(formApi, false),
+    {
+      fieldName: 'version',
+      label: '璁捐鐗堟湰',
+      component: 'Input',
+      componentProps: { placeholder: '濡� V1.0銆乂2.1' },
+      rules: 'required',
+    },
+    {
+      fieldName: 'name',
+      label: '浠诲姟鍚嶇О',
+      component: 'Input',
+      componentProps: { placeholder: '灏嗘牴鎹骇鍝佷笌鐗堟湰鑷姩鐢熸垚锛屽彲淇敼' },
+      rules: 'required',
+      dependencies: {
+        triggerFields: ['productName', 'version'],
+        trigger(values: Record<string, string>, form) {
+          const productName = values.productName?.trim();
+          const version = values.version?.trim();
+          if (productName && version) {
+            form.setFieldValue('name', `${productName} ${version} 璁捐`);
+          }
+        },
+      },
+    },
+    {
+      fieldName: 'designerName',
+      label: '涓昏璁″笀',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヤ富璁捐甯�' },
+      rules: 'required',
+    },
+    {
+      fieldName: 'reviewerName',
+      label: '瀹℃牳浜�',
+      component: 'Input',
+      componentProps: { placeholder: '璧勬枡瀹℃牳璐熻矗浜�' },
+      rules: 'required',
+    },
+    {
+      fieldName: 'planDateRange',
+      label: '璁″垝鍛ㄦ湡',
+      component: 'RangePicker',
+      componentProps: getRangePickerDefaultProps(),
+      rules: 'required',
+    },
+    {
+      fieldName: 'remark',
+      label: '浠诲姟璇存槑',
+      component: 'Textarea',
+      componentProps: {
+        placeholder: '绠�杩版湰娆¤璁$洰鏍囥�佸彉鏇磋儗鏅瓑',
+        rows: 3,
+      },
+      formItemClass: 'col-span-2',
+    },
+  ];
+}
+
+/** 鍙戝竷浠诲姟纭琛ㄥ崟 */
+export function usePublishFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'publishRemark',
+      label: '鍙戝竷璇存槑',
+      component: 'Textarea',
+      componentProps: {
+        placeholder: '鍙�夛紝濉啓鍙戝竷瑕佹眰鎴栨敞鎰忎簨椤�',
+        rows: 3,
+      },
+      formItemClass: 'col-span-2',
+    },
+  ];
+}
+
+export type WorkflowActionType = 'approve' | 'archive' | 'submit-review';
+
+export interface WorkflowActionConfig {
+  title: string;
+  alertMessage: string;
+  alertDescription: string;
+  remarkLabel: string;
+  remarkTag: string;
+  remarkPlaceholder: string;
+  successMessage: string;
+  alertType: 'info' | 'success' | 'warning';
+}
+
+export const WORKFLOW_ACTION_CONFIG: Record<
+  WorkflowActionType,
+  WorkflowActionConfig
+> = {
+  'submit-review': {
+    title: '鎻愪氦璧勬枡瀹℃牳',
+    alertMessage: '纭鎻愪氦瀹℃牳',
+    alertDescription:
+      '鎻愪氦鍚庡皢杩涘叆璧勬枡瀹℃牳鐜妭锛岃纭璁捐璧勬枡宸蹭笂浼犲畬鏁淬�傛彁浜ゅ悗璁捐甯堝皢鏃犳硶缁х画淇敼璧勬枡銆�',
+    remarkLabel: '鎻愪氦璇存槑',
+    remarkTag: '銆愭彁浜よ鏄庛��',
+    remarkPlaceholder: '鍙�夛紝濉啓鎻愪氦瀹℃牳鐨勮ˉ鍏呰鏄�',
+    successMessage: '宸叉彁浜よ祫鏂欏鏍�',
+    alertType: 'info',
+  },
+  approve: {
+    title: '瀹℃牳閫氳繃',
+    alertMessage: '纭瀹℃牳閫氳繃',
+    alertDescription:
+      '瀹℃牳閫氳繃鍚庝换鍔¤繘鍏ャ�屽凡閫氳繃銆嶇姸鎬侊紝鍙繘琛屽綊妗f搷浣溿�傝纭璁捐璧勬枡绗﹀悎瑕佹眰銆�',
+    remarkLabel: '瀹℃牳鎰忚',
+    remarkTag: '銆愬鏍告剰瑙併��',
+    remarkPlaceholder: '鍙�夛紝濉啓瀹℃牳閫氳繃鎰忚',
+    successMessage: '璧勬枡瀹℃牳閫氳繃',
+    alertType: 'success',
+  },
+  archive: {
+    title: '褰掓。璁捐浠诲姟',
+    alertMessage: '纭褰掓。',
+    alertDescription:
+      '褰掓。鍚庡皢鐢熸垚鏈増鏈綊妗e彴璐﹁褰曪紝浠诲姟鐘舵�佸彉涓哄凡褰掓。锛岃璁¤祫鏂欏皢閿佸畾涓嶅彲淇敼銆�',
+    remarkLabel: '褰掓。璇存槑',
+    remarkTag: '銆愬綊妗h鏄庛��',
+    remarkPlaceholder: '鍙�夛紝濉啓褰掓。澶囨敞',
+    successMessage: '浠诲姟宸插綊妗�',
+    alertType: 'warning',
+  },
+};
+
+/** 娴佺▼鎿嶄綔纭琛ㄥ崟 */
+export function useWorkflowActionFormSchema(
+  action: WorkflowActionType,
+): VbenFormSchema[] {
+  const config = WORKFLOW_ACTION_CONFIG[action];
+  return [
+    {
+      fieldName: 'actionRemark',
+      label: config.remarkLabel,
+      component: 'Textarea',
+      componentProps: {
+        placeholder: config.remarkPlaceholder,
+        rows: 3,
+      },
+      formItemClass: 'col-span-2',
+    },
+  ];
+}
+
+export function useGridFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'code',
+      label: '浠诲姟缂栫爜',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヤ换鍔$紪鐮�' },
+    },
+    {
+      fieldName: 'name',
+      label: '浠诲姟鍚嶇О',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヤ换鍔″悕绉�' },
+    },
+    {
+      fieldName: 'productCode',
+      label: '浜у搧缂栫爜',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヤ骇鍝佺紪鐮�' },
+    },
+    {
+      fieldName: 'status',
+      label: '浠诲姟鐘舵��',
+      component: 'Select',
+      componentProps: {
+        allowClear: true,
+        options: getDictOptions(DICT_TYPE.MES_PD_PROJECT_STATUS, 'number'),
+        placeholder: '璇烽�夋嫨椤圭洰鐘舵��',
+      },
+    },
+  ];
+}
+
+export function useGridColumns(): VxeTableGridOptions['columns'] {
+  return [
+    { field: 'code', title: '浠诲姟缂栫爜', minWidth: 140, slots: { default: 'code' } },
+    { field: 'name', title: '浠诲姟鍚嶇О', minWidth: 160 },
+    { field: 'productCode', title: '浜у搧缂栫爜', minWidth: 120 },
+    { field: 'productName', title: '浜у搧鍚嶇О', minWidth: 140 },
+    { field: 'version', title: '鐗堟湰鍙�', minWidth: 90 },
+    {
+      field: 'status',
+      title: '鐘舵��',
+      minWidth: 100,
+      cellRender: {
+        name: 'CellDict',
+        props: { type: DICT_TYPE.MES_PD_PROJECT_STATUS },
+      },
+    },
+    { field: 'designerName', title: '涓昏璁″笀', minWidth: 100 },
+    { field: 'reviewerName', title: '瀹℃牳浜�', minWidth: 100 },
+    { field: 'planEndDate', title: '璁″垝瀹屾垚', minWidth: 120, formatter: 'formatDate' },
+    { field: 'createTime', title: '鍒涘缓鏃堕棿', minWidth: 160, formatter: 'formatDateTime' },
+    { field: 'action', title: '鎿嶄綔', width: 220, fixed: 'right', slots: { default: 'actions' } },
+  ];
+}
+
+export function useFormSchema(
+  formType: FormType,
+  formApi?: VbenFormApi,
+): VbenFormSchema[] {
+  const readonly = formType === 'detail';
+  return [
+    {
+      fieldName: 'id',
+      component: 'Input',
+      dependencies: { triggerFields: [''], show: () => false },
+    },
+    {
+      fieldName: 'code',
+      label: '浠诲姟缂栫爜',
+      component: 'Input',
+      componentProps: { disabled: readonly, placeholder: '璇疯緭鍏ヤ换鍔$紪鐮�' },
+      rules: 'required',
+      suffix:
+        formType === 'detail'
+          ? undefined
+          : () =>
+              h(
+                Button,
+                {
+                  type: 'default',
+                  onClick: async () => {
+                    const code = isPdMockEnabled()
+                      ? mockNextCode(
+                          'DT-2026',
+                          Math.floor(Date.now() % 10_000),
+                        )
+                      : await generateAutoCode(
+                          MesAutoCodeRuleCode.PD_PROJECT_CODE,
+                        );
+                    await formApi?.setFieldValue('code', code);
+                  },
+                },
+                { default: () => '鐢熸垚' },
+              ),
+    },
+    ...useProductSelectSchema(formApi, readonly),
+    {
+      fieldName: 'version',
+      label: '鐗堟湰鍙�',
+      component: 'Input',
+      componentProps: { disabled: readonly, placeholder: '濡� V1.0' },
+      rules: 'required',
+    },
+    {
+      fieldName: 'name',
+      label: '浠诲姟鍚嶇О',
+      component: 'Input',
+      componentProps: { disabled: readonly, placeholder: '濡傦細鏅鸿兘娓╂帶妯$粍 V1.0 璁捐' },
+      rules: 'required',
+      dependencies: {
+        triggerFields: ['productName', 'version'],
+        trigger(values: Record<string, string>, form) {
+          if (readonly) {
+            return;
+          }
+          const productName = values.productName?.trim();
+          const version = values.version?.trim();
+          if (productName && version) {
+            form.setFieldValue('name', `${productName} ${version} 璁捐`);
+          }
+        },
+      },
+    },
+    {
+      fieldName: 'status',
+      label: '浠诲姟鐘舵��',
+      component: 'Select',
+      componentProps: {
+        disabled: true,
+        options: getDictOptions(DICT_TYPE.MES_PD_PROJECT_STATUS, 'number'),
+      },
+      dependencies: { triggerFields: [''], show: () => formType !== 'create' },
+    },
+    {
+      fieldName: 'designerName',
+      label: '涓昏璁″笀',
+      component: 'Input',
+      componentProps: { disabled: readonly, placeholder: '璇疯緭鍏ヤ富璁捐甯�' },
+    },
+    {
+      fieldName: 'reviewerName',
+      label: '瀹℃牳浜�',
+      component: 'Input',
+      componentProps: { disabled: readonly, placeholder: '璇疯緭鍏ュ鏍镐汉' },
+    },
+    {
+      fieldName: 'planDateRange',
+      label: '璁″垝鍛ㄦ湡',
+      component: 'RangePicker',
+      componentProps: {
+        ...getRangePickerDefaultProps(),
+        disabled: readonly,
+      },
+    },
+    {
+      fieldName: 'remark',
+      label: '澶囨敞',
+      component: 'Textarea',
+      componentProps: { disabled: readonly, placeholder: '璇疯緭鍏ュ娉�', rows: 3 },
+      formItemClass: 'col-span-3',
+    },
+  ];
+}
+
+/** 璁捐璧勬枡绫诲瀷閫夐」锛堜笂浼犳椂閫夋嫨锛� */
+export function getPdDocumentTypeOptions() {
+  return [
+    { label: '璁捐鏂规', value: MesPdDocumentTypeEnum.SCHEME },
+    { label: '璁捐鍥剧焊', value: MesPdDocumentTypeEnum.DRAWING },
+    { label: '璁捐妯″瀷', value: MesPdDocumentTypeEnum.MODEL },
+    { label: '浜у搧璁捐', value: MesPdDocumentTypeEnum.PRODUCT_DESIGN },
+    { label: 'BOM 娓呭崟', value: MesPdDocumentTypeEnum.BOM },
+    { label: '鍏朵粬闄勪欢', value: MesPdDocumentTypeEnum.OTHER },
+  ];
+}
+
+/** 缁熶竴璁捐璧勬枡鍒楄〃鍒� */
+export function useMaterialListGridFormSchema(): VbenFormSchema[] {
+  return [
+    {
+      fieldName: 'name',
+      label: '璧勬枡鍚嶇О',
+      component: 'Input',
+      componentProps: { placeholder: '璇疯緭鍏ヨ祫鏂欏悕绉�' },
+    },
+    {
+      fieldName: 'documentType',
+      label: '璧勬枡绫诲瀷',
+      component: 'Select',
+      componentProps: {
+        allowClear: true,
+        options: getPdDocumentTypeOptions(),
+        placeholder: '璇烽�夋嫨璧勬枡绫诲瀷',
+      },
+    },
+  ];
+}
+
+export function useMaterialListGridColumns(): VxeTableGridOptions['columns'] {
+  return [
+    {
+      field: 'documentType',
+      title: '璧勬枡绫诲瀷',
+      minWidth: 110,
+      formatter: ({ cellValue }: { cellValue: number }) => {
+        const option = getPdDocumentTypeOptions().find(
+          (item) => item.value === cellValue,
+        );
+        return option?.label ?? String(cellValue ?? '');
+      },
+    },
+    { field: 'name', title: '璧勬枡鍚嶇О', minWidth: 180 },
+    { field: 'version', title: '鐗堟湰', minWidth: 90 },
+    { field: 'fileName', title: '鏂囦欢鍚�', minWidth: 200 },
+    { field: 'uploaderName', title: '涓婁紶浜�', minWidth: 100 },
+    {
+      field: 'createTime',
+      title: '涓婁紶鏃堕棿',
+      minWidth: 160,
+      formatter: 'formatDateTime',
+    },
+    {
+      field: 'action',
+      title: '鎿嶄綔',
+      width: 140,
+      fixed: 'right',
+      slots: { default: 'actions' },
+    },
+  ];
+}
+
+/** @deprecated 鎸夌被鍨嬪垎 Tab 鏃朵娇鐢紝璇锋敼鐢� useMaterialListGridColumns */
+export function useDocumentGridColumns(documentType: number): VxeTableGridOptions['columns'] {
+  const typeLabel =
+    documentType === MesPdDocumentTypeEnum.SCHEME
+      ? '鏂规'
+      : documentType === MesPdDocumentTypeEnum.DRAWING
+        ? '鍥剧焊'
+        : documentType === MesPdDocumentTypeEnum.MODEL
+          ? '妯″瀷'
+          : documentType === MesPdDocumentTypeEnum.PRODUCT_DESIGN
+            ? '浜у搧璁捐'
+            : '璧勬枡';
+
+  return [
+    { field: 'name', title: `${typeLabel}鍚嶇О`, minWidth: 180 },
+    { field: 'version', title: '鐗堟湰', minWidth: 90 },
+    { field: 'fileName', title: '鏂囦欢鍚�', minWidth: 200 },
+    { field: 'uploaderName', title: '涓婁紶浜�', minWidth: 100 },
+    { field: 'createTime', title: '涓婁紶鏃堕棿', minWidth: 160, formatter: 'formatDateTime' },
+    { field: 'action', title: '鎿嶄綔', width: 140, slots: { default: 'actions' } },
+  ];
+}
+
+/** BOM 琛ㄦ牸鍒� */
+export function useBomGridColumns(): VxeTableGridOptions['columns'] {
+  return [
+    { field: 'itemCode', title: '鐗╂枡缂栫爜', minWidth: 120 },
+    { field: 'itemName', title: '鐗╂枡鍚嶇О', minWidth: 160 },
+    { field: 'specification', title: '瑙勬牸鍨嬪彿', minWidth: 140 },
+    { field: 'unitMeasureName', title: '鍗曚綅', minWidth: 80 },
+    { field: 'quantity', title: '鐢ㄩ噺', minWidth: 90 },
+    { field: 'remark', title: '澶囨敞', minWidth: 140 },
+    { field: 'action', title: '鎿嶄綔', width: 140, slots: { default: 'actions' } },
+  ];
+}
+
+export type { MesPdProjectApi };

--
Gitblit v1.9.3