From c5116e93fc42e6f4d732a7ea990068ad9d0e0736 Mon Sep 17 00:00:00 2001
From: baoshiwei <baoshiwei@shlanbao.cn>
Date: 星期四, 24 七月 2025 14:52:56 +0800
Subject: [PATCH] feat(dry): 新增设备维护记录功能、新增维修记录功能、设备主数据增加履历相关字段

---
 src/views/dry/dataDefine/DryEquipment.data.ts                 |   98 ++
 src/views/dry/maint/DryMaintenanceRecord_menu_insert.sql      |   26 
 src/views/dry/spareParts/DrySpareStockLog.data.ts             |  206 +++++
 src/views/dry/spareParts/components/DrySparePartsModal.vue    |   66 +
 src/views/dry/maint/components/DryMaintenanceRecordForm.vue   |   70 +
 src/views/dry/repair/DrySpareStockLogListRepair.vue           |  192 +++++
 src/views/dry/spareParts/DrySpareStockLogList.vue             |  170 ++++
 src/views/dry/spareParts/components/DrySpareStockLogModal.vue |   89 ++
 src/views/dry/maint/components/DryMaintenanceRecordModal.vue  |   72 +
 src/views/dry/spareParts/DrySpareParts.data.ts                |  274 +++++++
 src/views/dry/spareParts/DrySpareParts_menu_insert.sql        |   26 
 src/views/dry/spareParts/DrySparePartsList.vue                |  197 +++++
 src/views/dry/maint/DryMaintenanceRecord.api.ts               |   76 ++
 src/views/dry/spareParts/components/DrySparePartsForm.vue     |   70 +
 src/views/dry/spareParts/components/DrySpareStockLogForm.vue  |   70 +
 src/views/dry/spareParts/DrySpareParts.api.ts                 |   82 ++
 src/views/dry/spareParts/DrySpareStockLog_menu_insert.sql     |   26 
 src/views/dry/maint/DryMaintenanceRecordList.vue              |  207 +++++
 src/views/dry/spareParts/DrySpareStockLog.api.ts              |   85 ++
 src/views/dry/maint/DryMaintenanceRecord.data.ts              |  175 ++++
 20 files changed, 2,274 insertions(+), 3 deletions(-)

diff --git a/src/views/dry/dataDefine/DryEquipment.data.ts b/src/views/dry/dataDefine/DryEquipment.data.ts
index 215fbd8..1e6d6bd 100644
--- a/src/views/dry/dataDefine/DryEquipment.data.ts
+++ b/src/views/dry/dataDefine/DryEquipment.data.ts
@@ -21,6 +21,47 @@
 		dataIndex: 'type_dictText',
 	},
 	{
+		title: '鍒堕�犲晢',
+		align: 'center',
+		dataIndex: 'manufacturer',
+	},
+	{
+		title: '璐叆鏃ユ湡',
+		align: 'center',
+		dataIndex: 'purchaseDate',
+	},
+	{
+		title: '鍚敤鏃ユ湡',
+		align: 'center',
+		dataIndex: 'startDate',
+	},
+	{
+		title: '瀹夎浣嶇疆',
+		align: 'center',
+		dataIndex: 'location',
+	},
+	{
+		title: '璁惧鐘舵��',
+		align: 'center',
+		dataIndex: 'status',
+		customRender: ({text}) => {
+			// 0-'鏂板',1-'鍦ㄧ敤', 2-'鍋滅敤', 3-'缁翠慨', 4-'鎶ュ簾'
+      const options = {
+        '0': '鏂板',
+        '1': '鍦ㄧ敤',
+        '2': '鍋滅敤',
+        '3': '缁翠慨',
+        '4': '鎶ュ簾'
+      };
+      return options[text] || text;
+    }
+	},
+	{
+		title: '鎶ュ簾鏃ユ湡',
+		align: 'center',
+		dataIndex: 'scrapDate',
+	},
+	{
 		title: '璁惧鎻忚堪',
 		align: 'center',
 		dataIndex: 'remark',
@@ -99,6 +140,57 @@
 		},
 	},
 	{
+		label: '鍒堕�犲晢',
+		field: 'manufacturer',
+		component: 'Input',
+	},
+	{
+		label: '璐叆鏃ユ湡',
+		field: 'purchaseDate',
+		component: 'DatePicker',
+		componentProps: {
+			valueFormat: 'YYYY-MM-DD',
+			format: 'YYYY-MM-DD'
+		},
+	},
+	{
+		label: '鍚敤鏃ユ湡',
+		field: 'startDate',
+		component: 'DatePicker',
+		componentProps: {
+			valueFormat: 'YYYY-MM-DD',
+			format: 'YYYY-MM-DD'
+		},
+	},
+	{
+		label: '瀹夎浣嶇疆',
+		field: 'location',
+		component: 'Input',
+	},
+	{
+		label: '璁惧鐘舵��',
+		field: 'status',
+		component: 'Select',
+		componentProps: {
+			options: [ // '鍛ㄤ繚','鏈堜繚','瀛d繚','骞翠繚'
+              { label: '鏂板', value: '0' },
+              { label: '鍦ㄧ敤', value: '1' },
+              { label: '鍋滅敤', value: '2' },
+              { label: '缁翠慨', value: '3' },
+              { label: '鎶ュ簾', value: '4' }
+            ]
+		},
+	},
+	{
+		label: '鎶ュ簾鏃ユ湡',
+		field: 'scrapDate',
+		component: 'DatePicker',
+		componentProps: {
+			valueFormat: 'YYYY-MM-DD',
+			format: 'YYYY-MM-DD'
+		},
+	},
+	{
 		label: '璁惧IP',
 		field: 'ip',
 		component: 'Input',
@@ -113,7 +205,7 @@
 		componentProps: {
 			dictCode: 'dry_eqp_type,name,id,tenant_id=' + getTenantId(),
 		},
-		dynamicRules: ({ model, schema }) => {
+		dynamicRules: ({  }) => {
 			return [{ required: true, message: '璇疯緭鍏ヨ澶囩被鍨�!' }]
 		},
 	},
@@ -129,7 +221,7 @@
 		componentProps: {
 			dictCode: 'dry_shop,name,id,tenant_id=' + getTenantId(),
 		},
-		dynamicRules: ({ model, schema }) => {
+		dynamicRules: ({ }) => {
 			return [{ required: true, message: '璇疯緭鍏ヨ溅闂磇d!' }]
 		},
 	},
@@ -138,7 +230,7 @@
 		field: 'enable',
 		component: 'JSwitch',
 		componentProps: {},
-		dynamicRules: ({ model, schema }) => {
+		dynamicRules: ({  }) => {
 			return [{ required: true, message: '璇疯緭鍏ュ惎鐢ㄧ姸鎬�!' }]
 		},
 	},
diff --git a/src/views/dry/maint/DryMaintenanceRecord.api.ts b/src/views/dry/maint/DryMaintenanceRecord.api.ts
new file mode 100644
index 0000000..f8af753
--- /dev/null
+++ b/src/views/dry/maint/DryMaintenanceRecord.api.ts
@@ -0,0 +1,76 @@
+import {defHttp} from '/@/utils/http/axios';
+import { useMessage } from "/@/hooks/web/useMessage";
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  list = '/dry/dryMaintenanceRecord/list',
+  save='/dry/dryMaintenanceRecord/add',
+  edit='/dry/dryMaintenanceRecord/edit',
+  deleteOne = '/dry/dryMaintenanceRecord/delete',
+  deleteBatch = '/dry/dryMaintenanceRecord/deleteBatch',
+  importExcel = '/dry/dryMaintenanceRecord/importExcel',
+  exportXls = '/dry/dryMaintenanceRecord/exportXls',
+  generateMaintenanceNo = '/dry/dryMaintenanceRecord/generateMaintenanceNo'
+}
+/**
+ * 瀵煎嚭api
+ * @param params
+ */
+export const getExportUrl = Api.exportXls;
+/**
+ * 瀵煎叆api
+ */
+export const getImportUrl = Api.importExcel;
+/**
+ * 鍒楄〃鎺ュ彛
+ * @param params
+ */
+export const list = (params) => {
+  if (params.planNumber) {
+    params.relatedOrder = params.planNumber;
+    delete params.planNumber;
+  }
+  return defHttp.get({url: Api.list, params});
+};
+
+/**
+ * 鍒犻櫎鍗曚釜
+ */
+export const deleteOne = (params,handleSuccess) => {
+  return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
+    handleSuccess();
+  });
+}
+/**
+ * 鎵归噺鍒犻櫎
+ * @param params
+ */
+export const batchDelete = (params, handleSuccess) => {
+  createConfirm({
+    iconType: 'warning',
+    title: '纭鍒犻櫎',
+    content: '鏄惁鍒犻櫎閫変腑鏁版嵁',
+    okText: '纭',
+    cancelText: '鍙栨秷',
+    onOk: () => {
+      return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
+        handleSuccess();
+      });
+    }
+  });
+}
+/**
+ * 淇濆瓨鎴栬�呮洿鏂�
+ * @param params
+ */
+export const saveOrUpdate = (params, isUpdate) => {
+  let url = isUpdate ? Api.edit : Api.save;
+  return defHttp.post({url: url, params});
+}
+/**
+ * 鐢熸垚淇濆吇鍗曠紪鍙�
+ */
+export const generateMaintenanceNo = () => {
+  return defHttp.get({url: Api.generateMaintenanceNo},{ successMessageMode: 'none' });
+}
diff --git a/src/views/dry/maint/DryMaintenanceRecord.data.ts b/src/views/dry/maint/DryMaintenanceRecord.data.ts
new file mode 100644
index 0000000..2327516
--- /dev/null
+++ b/src/views/dry/maint/DryMaintenanceRecord.data.ts
@@ -0,0 +1,175 @@
+import {BasicColumn} from '/@/components/Table';
+import {FormSchema} from '/@/components/Table';
+import { rules} from '/@/utils/helper/validator';
+import { render } from '/@/utils/common/renderUtils';
+//鍒楄〃鏁版嵁
+export const columns: BasicColumn[] = [
+   {
+    title: '淇濆吇缂栧彿',
+    align:"center",
+    dataIndex: 'planNumber'
+   },
+   {
+    title: '璁惧缂栧彿',
+    align:"center",
+    dataIndex: 'equipmentId_dictText'
+   },
+   {
+    title: '淇濆吇鍛ㄦ湡',
+    align:"center",
+    dataIndex: 'cycle',
+    
+   },
+   {
+    title: '淇濆吇鏃堕棿',
+    align:"center",
+    dataIndex: 'maintDate',
+    customRender:({text}) =>{
+      return !text?"":(text.length>10?text.substr(0,10):text)
+    },
+   },
+   {
+    title: '淇濆吇浜哄憳',
+    align:"center",
+    dataIndex: 'technician'
+   },
+   {
+    title: '淇濆吇鍐呭',
+    align:"center",
+    dataIndex: 'content'
+   },
+   {
+    title: '澶囦欢浣跨敤鎯呭喌',
+    align:"center",
+    dataIndex: 'spareParts'
+   },
+   {
+    title: '澶囨敞',
+    align:"center",
+    dataIndex: 'remark'
+   },
+];
+//鏌ヨ鏁版嵁
+export const searchFormSchema: FormSchema[] = [
+	{
+      label: "璁惧缂栧彿",
+      field: 'equipmentId',
+      component: 'JDictSelectTag',
+      componentProps:{
+          dictCode:"dry_equipment,name,id"
+      },
+      colProps: {span: 6},
+ 	},
+  {
+    label: '淇濆吇鍛ㄦ湡',
+    field: 'cycle',
+    component: 'JDictSelectTag',
+    componentProps:{
+            options: [ // '鍛ㄤ繚','鏈堜繚','瀛d繚','骞翠繚'
+              { label: '鍛ㄤ繚', value: '鍛ㄤ繚' },
+              { label: '鏈堜繚', value: '鏈堜繚' },
+              { label: '瀛d繚', value: '瀛d繚' },
+              { label: '骞翠繚', value: '骞翠繚' }
+            ]
+        },
+    colProps: {span: 6},
+  },
+{
+      label: "淇濆吇鏃堕棿",
+      field: 'maintDate',
+      component: 'DatePicker',
+      componentProps: {
+         showTime:true,
+         valueFormat: 'YYYY-MM-DD HH:mm:ss'
+       },
+      colProps: {span: 6},
+ 	},
+	{
+      label: "淇濆吇浜哄憳",
+      field: 'technician',
+      component: 'Input',
+      colProps: {span: 6},
+ 	},
+];
+//琛ㄥ崟鏁版嵁
+export const formSchema: FormSchema[] = [
+  {
+    label: '淇濆吇缂栧彿',
+    field: 'planNumber',
+    component: 'Input',
+    dynamicDisabled:true
+  },
+  {
+    label: '璁惧缂栧彿',
+    field: 'equipmentId',
+    component: 'JDictSelectTag',
+    componentProps:{
+        dictCode:"dry_equipment,name,id"
+     },
+    rules: [{ required: true, message: '璇烽�夋嫨璁惧缂栧彿' }],
+  },
+  {
+    label: '淇濆吇鍛ㄦ湡',
+    field: 'cycle',
+    component: 'JDictSelectTag',
+    componentProps:{
+            options: [ // '鍛ㄤ繚','鏈堜繚','瀛d繚','骞翠繚'
+              { label: '鍛ㄤ繚', value: '鍛ㄤ繚' },
+              { label: '鏈堜繚', value: '鏈堜繚' },
+              { label: '瀛d繚', value: '瀛d繚' },
+              { label: '骞翠繚', value: '骞翠繚' }
+            ]
+        },
+    rules: [{ required: true, message: '璇烽�夋嫨淇濆吇鍛ㄦ湡' }],
+  },
+  {
+    label: '淇濆吇鏃堕棿',
+    field: 'maintDate',
+    component: 'DatePicker',
+    componentProps: {
+       showTime: true,
+       valueFormat: 'YYYY-MM-DD HH:mm:ss'
+     },
+    rules: [{ required: true, message: '璇烽�夋嫨淇濆吇鏃堕棿' }],
+  },
+  {
+    label: '淇濆吇浜哄憳',
+    field: 'technician',
+    component: 'Input',
+    rules: [{ required: true, message: '璇疯緭鍏ヤ繚鍏讳汉鍛�' }],
+  },
+  {
+    label: '淇濆吇鍐呭',
+    field: 'content',
+    component: 'InputTextArea',
+    rules: [{ required: true, message: '璇疯緭鍏ヤ繚鍏诲唴瀹�' }],
+  },
+  {
+    label: '澶囦欢浣跨敤鎯呭喌',
+    field: 'spareParts',
+    component: 'Input',
+  },
+  {
+    label: '澶囨敞',
+    field: 'remark',
+    component: 'Input',
+  },
+	// TODO 涓婚敭闅愯棌瀛楁锛岀洰鍓嶅啓姝讳负ID
+	{
+	  label: '',
+	  field: 'id',
+	  component: 'Input',
+	  show: false
+	},
+];
+
+
+
+/**
+* 娴佺▼琛ㄥ崟璋冪敤杩欎釜鏂规硶鑾峰彇formSchema
+* @param param
+*/
+export function getBpmFormSchema(_formData): FormSchema[]{
+  // 榛樿鍜屽師濮嬭〃鍗曚繚鎸佷竴鑷� 濡傛灉娴佺▼涓厤缃簡鏉冮檺鏁版嵁锛岃繖閲岄渶瑕佸崟鐙鐞唂ormSchema
+  return formSchema;
+}
\ No newline at end of file
diff --git a/src/views/dry/maint/DryMaintenanceRecordList.vue b/src/views/dry/maint/DryMaintenanceRecordList.vue
new file mode 100644
index 0000000..dd89a43
--- /dev/null
+++ b/src/views/dry/maint/DryMaintenanceRecordList.vue
@@ -0,0 +1,207 @@
+<template>
+  <div>
+    <!--寮曠敤琛ㄦ牸-->
+   <BasicTable @register="registerTable" :rowSelection="rowSelection">
+     <!--鎻掓Ы:table鏍囬-->
+      <template #tableTitle>
+          <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 鏂板</a-button>
+          <a-button  type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 瀵煎嚭</a-button>
+          <j-upload-button  type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">瀵煎叆</j-upload-button>
+          <a-dropdown v-if="selectedRowKeys.length > 0">
+              <template #overlay>
+                <a-menu>
+                  <a-menu-item key="1" @click="batchHandleDelete">
+                    <Icon icon="ant-design:delete-outlined"></Icon>
+                    鍒犻櫎
+                  </a-menu-item>
+                </a-menu>
+              </template>
+              <a-button>鎵归噺鎿嶄綔
+                <Icon icon="mdi:chevron-down"></Icon>
+              </a-button>
+        </a-dropdown>
+      </template>
+       <!--鎿嶄綔鏍�-->
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
+      </template>
+      <!--瀛楁鍥炴樉鎻掓Ы-->
+      <template #htmlSlot="{text}">
+         <div v-html="text"></div>
+      </template>
+      <!--鐪佸競鍖哄瓧娈靛洖鏄炬彃妲�-->
+      <template #pcaSlot="{text}">
+         {{ getAreaTextByCode(text) }}
+      </template>
+      <template #fileSlot="{text}">
+         <span v-if="!text" style="font-size: 12px;font-style: italic;">鏃犳枃浠�</span>
+         <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">涓嬭浇</a-button>
+      </template>
+    </BasicTable>
+    <!-- 琛ㄥ崟鍖哄煙 -->
+    <DryMaintenanceRecordModal @register="registerModal" @success="handleSuccess"></DryMaintenanceRecordModal>
+  </div>
+  <!--瀛愯〃琛ㄦ牸tab-->
+  <a-tabs defaultActiveKey="1" style="margin: 10px">
+    <a-tab-pane tab="浣跨敤澶囦欢鍒楄〃" key="1" forceRender>
+      <DrySpareStockLogListRepair />
+    </a-tab-pane>
+  </a-tabs>
+</template>
+
+<script lang="ts" name="dry-dryMaintenanceRecord" setup>
+  import {ref, computed, unref, provide} from 'vue';
+  import {BasicTable, useTable, TableAction} from '/@/components/Table';
+  import {useModal} from '/@/components/Modal';
+  import { useListPage } from '/@/hooks/system/useListPage'
+  import DryMaintenanceRecordModal from './components/DryMaintenanceRecordModal.vue'
+  import DrySpareStockLogListRepair from '../repair/DrySpareStockLogListRepair.vue';
+  import {columns, searchFormSchema} from './DryMaintenanceRecord.data';
+  import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './DryMaintenanceRecord.api';
+  import { downloadFile } from '/@/utils/common/renderUtils';
+  const checkedKeys = ref<Array<string | number>>([]);
+  //娉ㄥ唽model
+  const [registerModal, {openModal}] = useModal();
+  const selectedRows = ref<Array<any>>([]); // 鏂板锛氱敤浜庡瓨鍌ㄩ�変腑鐨勮鏁版嵁
+  //娉ㄥ唽table鏁版嵁
+  const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
+      tableProps:{
+           title: 'dry_maintenance_record',
+           api: list,
+           columns,
+           canResize:false,
+           formConfig: {
+              //labelWidth: 120,
+              schemas: searchFormSchema,
+              autoSubmitOnEnter:true,
+              showAdvancedButton:true,
+              fieldMapToNumber: [
+              ],
+              fieldMapToTime: [
+              ],
+            },
+           actionColumn: {
+               width: 120,
+               fixed:'right'
+            },
+           rowSelection: {
+             type: 'radio',
+             onChange: (selectedRowKeyList: (string | number)[], selectedRowList: any[]) => {
+               selectedRowKeys.value = selectedRowKeyList;
+               selectedRows.value = selectedRowList;
+             },
+           },
+           customRow: (record) => {
+             return {
+               onClick: () => {
+                 if (selectedRowKeys.value.includes(record.id)) {
+                   selectedRowKeys.value = [];
+                   selectedRows.value = [];
+                 } else {
+                   selectedRowKeys.value = [record.id];
+                   selectedRows.value = [record];
+                 }
+               },
+             };
+           },
+      },
+       exportConfig: {
+            name:"dry_maintenance_record",
+            url: getExportUrl,
+          },
+          importConfig: {
+            url: getImportUrl,
+            success: handleSuccess
+          },
+  })
+
+  const [registerTable, {reload, updateTableDataRecord},{ rowSelection, selectedRowKeys }] = tableContext
+
+  const planNumber = computed(() => (
+     unref(selectedRows).length > 0 ? unref(selectedRows)[0].planNumber : '1'))
+  ;
+  //涓嬪彂 planNumber,瀛愮粍浠舵帴鏀�
+  provide('orderNumber', planNumber);
+
+   /**
+    * 鏂板浜嬩欢
+    */
+  function handleAdd() {
+     openModal(true, {
+       isUpdate: false,
+       showFooter: true,
+     });
+  }
+   /**
+    * 缂栬緫浜嬩欢
+    */
+  function handleEdit(record: Recordable) {
+     openModal(true, {
+       record,
+       isUpdate: true,
+       showFooter: true,
+     });
+   }
+   /**
+    * 璇︽儏
+   */
+  function handleDetail(record: Recordable) {
+     openModal(true, {
+       record,
+       isUpdate: true,
+       showFooter: false,
+     });
+   }
+   /**
+    * 鍒犻櫎浜嬩欢
+    */
+  async function handleDelete(record) {
+     await deleteOne({id: record.id}, handleSuccess);
+   }
+   /**
+    * 鎵归噺鍒犻櫎浜嬩欢
+    */
+  async function batchHandleDelete() {
+     await batchDelete({ids: selectedRowKeys.value}, handleSuccess);
+   }
+   /**
+    * 鎴愬姛鍥炶皟
+    */
+  function handleSuccess() {
+      (selectedRowKeys.value = []) && reload();
+   }
+   /**
+      * 鎿嶄綔鏍�
+      */
+  function getTableAction(record){
+       return [
+         {
+           label: '缂栬緫',
+           onClick: handleEdit.bind(null, record),
+         }
+       ]
+   }
+     /**
+        * 涓嬫媺鎿嶄綔鏍�
+        */
+  function getDropDownAction(record){
+       return [
+         {
+           label: '璇︽儏',
+           onClick: handleDetail.bind(null, record),
+         }, {
+           label: '鍒犻櫎',
+           popConfirm: {
+             title: '鏄惁纭鍒犻櫎',
+             confirm: handleDelete.bind(null, record),
+           }
+         }
+       ]
+   }
+
+
+</script>
+
+<style scoped>
+
+</style>
\ No newline at end of file
diff --git a/src/views/dry/maint/DryMaintenanceRecord_menu_insert.sql b/src/views/dry/maint/DryMaintenanceRecord_menu_insert.sql
new file mode 100644
index 0000000..ba91170
--- /dev/null
+++ b/src/views/dry/maint/DryMaintenanceRecord_menu_insert.sql
@@ -0,0 +1,26 @@
+-- 娉ㄦ剰锛氳椤甸潰瀵瑰簲鐨勫墠鍙扮洰褰曚负views/dry鏂囦欢澶逛笅
+-- 濡傛灉浣犳兂鏇存敼鍒板叾浠栫洰褰曪紝璇蜂慨鏀箂ql涓璫omponent瀛楁瀵瑰簲鐨勫��
+
+
+INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external) 
+VALUES ('2025072104423690330', NULL, 'dry_maintenance_record', '/dry/dryMaintenanceRecordList', 'dry/maint/DryMaintenanceRecordList', NULL, NULL, 0, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2025-07-21 16:42:33', NULL, NULL, 0);
+
+-- 鏉冮檺鎺у埗sql
+-- 鏂板
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025072104423690331', '2025072104423690330', '娣诲姞dry_maintenance_record', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_maintenance_record:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-21 16:42:33', NULL, NULL, 0, 0, '1', 0);
+-- 缂栬緫
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025072104423690332', '2025072104423690330', '缂栬緫dry_maintenance_record', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_maintenance_record:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-21 16:42:33', NULL, NULL, 0, 0, '1', 0);
+-- 鍒犻櫎
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025072104423690333', '2025072104423690330', '鍒犻櫎dry_maintenance_record', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_maintenance_record:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-21 16:42:33', NULL, NULL, 0, 0, '1', 0);
+-- 鎵归噺鍒犻櫎
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025072104423690334', '2025072104423690330', '鎵归噺鍒犻櫎dry_maintenance_record', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_maintenance_record:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-21 16:42:33', NULL, NULL, 0, 0, '1', 0);
+-- 瀵煎嚭excel
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025072104423690335', '2025072104423690330', '瀵煎嚭excel_dry_maintenance_record', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_maintenance_record:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-21 16:42:33', NULL, NULL, 0, 0, '1', 0);
+-- 瀵煎叆excel
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025072104423690336', '2025072104423690330', '瀵煎叆excel_dry_maintenance_record', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_maintenance_record:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-21 16:42:33', NULL, NULL, 0, 0, '1', 0);
diff --git a/src/views/dry/maint/components/DryMaintenanceRecordForm.vue b/src/views/dry/maint/components/DryMaintenanceRecordForm.vue
new file mode 100644
index 0000000..6981066
--- /dev/null
+++ b/src/views/dry/maint/components/DryMaintenanceRecordForm.vue
@@ -0,0 +1,70 @@
+<template>
+    <div style="min-height: 400px">
+        <BasicForm @register="registerForm"></BasicForm>
+        <div style="width: 100%;text-align: center" v-if="!formDisabled">
+            <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">鎻� 浜�</a-button>
+        </div>
+    </div>
+</template>
+
+<script lang="ts">
+    import {BasicForm, useForm} from '/@/components/Form/index';
+    import {computed, defineComponent} from 'vue';
+    import {defHttp} from '/@/utils/http/axios';
+    import { propTypes } from '/@/utils/propTypes';
+    import {getBpmFormSchema} from '../DryMaintenanceRecord.data';
+    import {saveOrUpdate} from '../DryMaintenanceRecord.api';
+    
+    export default defineComponent({
+        name: "DryMaintenanceRecordForm",
+        components:{
+            BasicForm
+        },
+        props:{
+            formData: propTypes.object.def({}),
+            formBpm: propTypes.bool.def(true),
+        },
+        setup(props){
+            const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
+                labelWidth: 150,
+                schemas: getBpmFormSchema(props.formData),
+                showActionButtonGroup: false,
+                baseColProps: {span: 12}
+            });
+
+            const formDisabled = computed(()=>{
+                if(props.formData.disabled === false){
+                    return false;
+                }
+                return true;
+            });
+
+            let formData = {};
+            const queryByIdUrl = '/dry/dryMaintenanceRecord/queryById';
+            async function initFormData(){
+                let params = {id: props.formData.dataId};
+                const data = await defHttp.get({url: queryByIdUrl, params});
+                formData = {...data}
+                //璁剧疆琛ㄥ崟鐨勫��
+                await setFieldsValue(formData);
+                //榛樿鏄鐢�
+                await setProps({disabled: formDisabled.value})
+            }
+
+            async function submitForm() {
+                let data = getFieldsValue();
+                let params = Object.assign({}, formData, data);
+                console.log('琛ㄥ崟鏁版嵁', params)
+                await saveOrUpdate(params, true)
+            }
+
+            initFormData();
+            
+            return {
+                registerForm,
+                formDisabled,
+                submitForm,
+            }
+        }
+    });
+</script>
\ No newline at end of file
diff --git a/src/views/dry/maint/components/DryMaintenanceRecordModal.vue b/src/views/dry/maint/components/DryMaintenanceRecordModal.vue
new file mode 100644
index 0000000..784ba73
--- /dev/null
+++ b/src/views/dry/maint/components/DryMaintenanceRecordModal.vue
@@ -0,0 +1,72 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="896" @ok="handleSubmit">
+      <BasicForm @register="registerForm"/>
+  </BasicModal>
+</template>
+
+<script lang="ts" setup>
+    import {ref, computed, unref} from 'vue';
+    import {BasicModal, useModalInner} from '/@/components/Modal';
+    import {BasicForm, useForm} from '/@/components/Form/index';
+    import {formSchema} from '../DryMaintenanceRecord.data';
+    import {saveOrUpdate, generateMaintenanceNo} from '../DryMaintenanceRecord.api';
+    // Emits澹版槑
+    const emit = defineEmits(['register','success']);
+    const isUpdate = ref(true);
+    //琛ㄥ崟閰嶇疆
+    const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
+        //labelWidth: 150,
+        schemas: formSchema,
+        showActionButtonGroup: false,
+        baseColProps: {span: 24}
+    });
+    //琛ㄥ崟璧嬪��
+    const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
+        //閲嶇疆琛ㄥ崟
+        await resetFields();
+        setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
+        isUpdate.value = !!data?.isUpdate;
+        if (unref(isUpdate)) {
+            //琛ㄥ崟璧嬪��
+            await setFieldsValue({
+                ...data.record,
+            });
+        } else {
+            // 鏂板妯″紡涓嬬敓鎴愪繚鍏诲崟缂栧彿
+            const result = await generateMaintenanceNo();
+            await setFieldsValue({
+                planNumber: result,
+            });
+        }
+        // 闅愯棌搴曢儴鏃剁鐢ㄦ暣涓〃鍗�
+       setProps({ disabled: !data?.showFooter })
+    });
+    //璁剧疆鏍囬
+    const title = computed(() => (!unref(isUpdate) ? '鏂板' : '缂栬緫'));
+    //琛ㄥ崟鎻愪氦浜嬩欢
+    async function handleSubmit(v) {
+        try {
+            let values = await validate();
+            setModalProps({confirmLoading: true});
+            //鎻愪氦琛ㄥ崟
+            await saveOrUpdate(values, isUpdate.value);
+            //鍏抽棴寮圭獥
+            closeModal();
+            //鍒锋柊鍒楄〃
+            emit('success');
+        } finally {
+            setModalProps({confirmLoading: false});
+        }
+    }
+</script>
+
+<style lang="less" scoped>
+	/** 鏃堕棿鍜屾暟瀛楄緭鍏ユ鏍峰紡 */
+  :deep(.ant-input-number){
+		width: 100%
+	}
+
+	:deep(.ant-calendar-picker){
+		width: 100%
+	}
+</style>
\ No newline at end of file
diff --git a/src/views/dry/repair/DrySpareStockLogListRepair.vue b/src/views/dry/repair/DrySpareStockLogListRepair.vue
new file mode 100644
index 0000000..19b9a60
--- /dev/null
+++ b/src/views/dry/repair/DrySpareStockLogListRepair.vue
@@ -0,0 +1,192 @@
+<template>
+  <div>
+    <!--寮曠敤琛ㄦ牸-->
+   <BasicTable @register="registerTable" :rowSelection="rowSelection" :search-info="searchInfo">
+     <!--鎻掓Ы:table鏍囬-->
+      <template #tableTitle>
+          <a-button type="primary" @click="handleCreate" preIcon="ant-design:plus-outlined"> 鏂板</a-button>
+          <a-dropdown v-if="selectedRowKeys.length > 0">
+              <template #overlay>
+                <a-menu>
+                  <a-menu-item key="1" @click="batchHandleDelete">
+                    <Icon icon="ant-design:delete-outlined"></Icon>
+                    鍒犻櫎
+                  </a-menu-item>
+
+                </a-menu>
+              </template>
+              <a-button>鎵归噺鎿嶄綔
+                <Icon icon="mdi:chevron-down"></Icon>
+              </a-button>
+        </a-dropdown>
+      </template>
+      <!--鎿嶄綔鏍�-->
+      <!-- <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
+      </template> -->
+
+   </BasicTable>
+    <!-- 琛ㄥ崟鍖哄煙 -->
+    <DrySpareStockLogModal @register="registerModal" @success="handleSuccess"></DrySpareStockLogModal>
+  </div>
+</template>
+
+<script lang="ts" name="dry-drySpareStockLogRepair" setup>
+  import {ref, computed, unref, inject, watch, nextTick} from 'vue';
+  import {BasicTable, useTable, TableAction} from '/@/components/Table';
+  import {useModal} from '/@/components/Modal';
+  import {useMessage} from '/@/hooks/web/useMessage';
+  import { useListPage } from '/@/hooks/system/useListPage'
+  import DrySpareStockLogModal from '../spareParts/components/DrySpareStockLogModal.vue'
+  import {columns, searchFormSchema} from '../spareParts/DrySpareStockLog.data';
+  import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from '../spareParts/DrySpareStockLog.api';
+  import { downloadFile } from '/src/utils/common/renderUtils';
+  import {isEmpty} from "/src/utils/is";
+  const checkedKeys = ref<Array<string | number>>([]);
+  //娉ㄥ唽model
+
+  const [registerModal, {openModal}] = useModal();
+  const searchInfo = {};
+
+  // 浠庣埗缁勪欢娉ㄥ叆 orderNumber
+  const orderNumber = inject('orderNumber', ''); // 淇濇寔鍚嶇О涓簉epairRecordId锛屼絾瀹為檯浼犻�掔殑鏄痮rderNumber
+
+  //娉ㄥ唽table鏁版嵁
+  const { prefixCls,tableContext } = useListPage({
+      tableProps:{
+           title: '鍑哄叆搴撹褰曡〃',
+           api: list,
+           columns,
+           canResize:false,
+        useSearchForm:false,
+           formConfig: {
+              //labelWidth: 120,
+              schemas: searchFormSchema,
+              autoSubmitOnEnter:true,
+              showAdvancedButton:true,
+              fieldMapToNumber: [
+              ],
+              fieldMapToTime: [
+              ],
+            },
+           // actionColumn: {
+           //     width: 120,
+           //     fixed:'right'
+           //  },
+           showActionColumn: false,
+           // 鏍规嵁 orderNumber 杩囨护鏁版嵁
+           beforeFetch: (params) => {
+              console.log('beforeFetch', params, orderNumber.value)
+             return Object.assign(params, { orderNumber: unref(orderNumber) });
+           },
+      },
+       exportConfig: {
+            name:"鍑哄叆搴撹褰曡〃",
+            url: getExportUrl,
+          },
+          importConfig: {
+            url: getImportUrl,
+            success: handleSuccess
+          },
+  })
+
+  const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
+
+  // 鐩戝惉 orderNumber 鍙樺寲锛岄噸鏂板姞杞借〃鏍兼暟鎹�
+  watch(orderNumber, () => {
+    reload();
+  });
+
+   /**
+    * 鏂板浜嬩欢
+    */
+  function handleCreate() {
+    console.log('handleCreate', unref(orderNumber));
+     const {createMessage} = useMessage();
+     if (isEmpty(unref(orderNumber)) || orderNumber.value === '1') { // 妫�鏌rderNumber鏄惁涓虹┖
+       createMessage.warning('璇烽�夋嫨涓�涓淮淇崟');
+       return;
+     }
+     nextTick(() => {
+       console.log('handleCreate', unref(orderNumber));
+       openModal(true, {
+       isUpdate: false,
+       showFooter: true,
+        stockType: 'out',
+         relatedOrder: unref(orderNumber), // 浼犻�� orderNumber
+       });
+     });
+  }
+   /**
+    * 缂栬緫浜嬩欢
+    */
+  function handleEdit(record: Recordable) {
+     openModal(true, {
+       record,
+       isUpdate: true,
+       showFooter: true,
+     });
+   }
+   /**
+    * 璇︽儏
+   */
+  function handleDetail(record: Recordable) {
+     openModal(true, {
+       record,
+       isUpdate: true,
+       showFooter: false,
+     });
+   }
+   /**
+    * 鍒犻櫎浜嬩欢
+    */
+  async function handleDelete(record) {
+     await deleteOne({id: record.id}, handleSuccess);
+   }
+   /**
+    * 鎵归噺鍒犻櫎浜嬩欢
+    */
+  async function batchHandleDelete() {
+     await batchDelete({ids: selectedRowKeys.value}, handleSuccess);
+   }
+   /**
+    * 鎴愬姛鍥炶皟
+    */
+  function handleSuccess() {
+      (selectedRowKeys.value = []) && reload();
+   }
+   /**
+      * 鎿嶄綔鏍�
+      */
+  // function getTableAction(record){
+  //      return [
+  //        {
+  //          label: '缂栬緫',
+  //          onClick: handleEdit.bind(null, record),
+  //        }
+  //      ]
+  //  }
+  //    /**
+  //       * 涓嬫媺鎿嶄綔鏍�
+  //       */
+  // function getDropDownAction(record){
+  //      return [
+  //        {
+  //          label: '璇︽儏',
+  //          onClick: handleDetail.bind(null, record),
+  //        }, {
+  //          label: '鍒犻櫎',
+  //          popConfirm: {
+  //            title: '鏄惁纭鍒犻櫎',
+  //            confirm: handleDelete.bind(null, record),
+  //          }
+  //        }
+  //      ]
+  //  }
+
+
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/dry/spareParts/DrySpareParts.api.ts b/src/views/dry/spareParts/DrySpareParts.api.ts
new file mode 100644
index 0000000..ab5e72c
--- /dev/null
+++ b/src/views/dry/spareParts/DrySpareParts.api.ts
@@ -0,0 +1,82 @@
+import {defHttp} from '/@/utils/http/axios';
+import { useMessage } from "/@/hooks/web/useMessage";
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  list = '/dry/drySpareParts/list',
+  save='/dry/drySpareParts/add',
+  edit='/dry/drySpareParts/edit',
+  deleteOne = '/dry/drySpareParts/delete',
+  deleteBatch = '/dry/drySpareParts/deleteBatch',
+  importExcel = '/dry/drySpareParts/importExcel',
+  exportXls = '/dry/drySpareParts/exportXls',
+  addStock = '/dry/drySpareParts/addStock',
+  reduceStock = '/dry/drySpareParts/reduceStock',
+}
+/**
+ * 瀵煎嚭api
+ * @param params
+ */
+export const getExportUrl = Api.exportXls;
+/**
+ * 瀵煎叆api
+ */
+export const getImportUrl = Api.importExcel;
+/**
+ * 鍒楄〃鎺ュ彛
+ * @param params
+ */
+export const list = (params) =>
+  defHttp.get({url: Api.list, params});
+
+/**
+ * 鍒犻櫎鍗曚釜
+ */
+export const deleteOne = (params,handleSuccess) => {
+  return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
+    handleSuccess();
+  });
+}
+/**
+ * 鎵归噺鍒犻櫎
+ * @param params
+ */
+export const batchDelete = (params, handleSuccess) => {
+  createConfirm({
+    iconType: 'warning',
+    title: '纭鍒犻櫎',
+    content: '鏄惁鍒犻櫎閫変腑鏁版嵁',
+    okText: '纭',
+    cancelText: '鍙栨秷',
+    onOk: () => {
+      return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
+        handleSuccess();
+      });
+    }
+  });
+}
+/**
+ * 淇濆瓨鎴栬�呮洿鏂�
+ * @param params
+ */
+export const saveOrUpdate = (params, isUpdate) => {
+  let url = isUpdate ? Api.edit : Api.save;
+  return defHttp.post({url: url, params});
+}
+
+/**
+ * 澧炲姞搴撳瓨
+ * @param params
+ */
+export const addStock = (params) => {
+  return defHttp.post({url: Api.addStock, params});
+}
+
+/**
+ * 鍑忓皯搴撳瓨
+ * @param params
+ */
+export const reduceStock = (params) => {
+  return defHttp.post({url: Api.reduceStock, params});
+}
diff --git a/src/views/dry/spareParts/DrySpareParts.data.ts b/src/views/dry/spareParts/DrySpareParts.data.ts
new file mode 100644
index 0000000..ee0de47
--- /dev/null
+++ b/src/views/dry/spareParts/DrySpareParts.data.ts
@@ -0,0 +1,274 @@
+import {BasicColumn} from '/@/components/Table';
+import {FormSchema} from '/@/components/Table';
+import { rules} from '/@/utils/helper/validator';
+import { render } from '/@/utils/common/renderUtils';
+//鍒楄〃鏁版嵁
+export const columns: BasicColumn[] = [
+   {
+    title: '闆朵欢缂栧彿',
+    align:"center",
+    dataIndex: 'partNumber'
+   },
+   {
+    title: '闆朵欢鍚嶇О',
+    align:"center",
+    dataIndex: 'partName'
+   },
+   {
+    title: '鍒嗙被',
+    align:"center",
+    dataIndex: 'category',
+    customRender: ({ text }) => {
+      // 鏈烘绫伙細濡傝酱鎵裤�侀娇杞�侀摼鏉$瓑銆�
+// 鐢垫皵绫伙細濡傜户鐢靛櫒銆佹帴瑙﹀櫒銆佺數缂嗙瓑銆�
+// 娑插帇绫伙細濡傛车銆侀榾銆佹补绠$瓑銆�
+// 姘斿姩绫伙細濡傛皵缂搞�佺數纾侀榾銆佺┖姘旇繃婊ゅ櫒绛夈��
+// 鐢靛瓙绫伙細濡傜數璺澘銆佷紶鎰熷櫒銆佹樉绀哄櫒绛夈��
+			return render.renderSwitch(text, [
+				{ text: '鏈烘绫�', value: '0' },
+				{ text: '鐢垫皵绫�', value: '1' },
+				{ text: '娑插帇绫�', value: '2' },
+				{ text: '姘斿姩绫�', value: '3' },
+				{ text: '鐢靛瓙绫�', value: '4' },
+			])
+		},
+   },
+   {
+    title: '瑙勬牸鍨嬪彿',
+    align:"center",
+    dataIndex: 'specModel'
+   },
+   {
+    title: '鍗曚綅',
+    align:"center",
+    dataIndex: 'unit',
+    customRender: ({ text }) => {
+//       涓�/浠讹細閫傜敤浜庡ぇ澶氭暟鐙珛鐨勯浂閮ㄤ欢锛屽铻轰笣銆佽灪甯姐�佺户鐢靛櫒绛夈��
+// 绫筹紙m锛夛細閫傚悎绾挎潗绫讳骇鍝侊紝濡傜數绾裤�佺數缂嗐�佺閬撶瓑銆�
+// 鍏枻锛坘g锛夛細鐢ㄤ簬琛¢噺閲嶉噺杈冨ぇ鐨勬潗鏂欐垨浜у搧锛屼緥濡傛鼎婊戞补銆侀噾灞炲潡绛夈��
+// 鍗囷紙L锛夛細娑蹭綋绫荤墿璧勶紝濡傛満娌广�佸喎鍗存恫绛夈��
+// 濂楋細鍖呭惈澶氫釜鐩稿叧灏忛儴浠剁粍鎴愮殑闆嗗悎浣擄紝濡傚伐鍏峰瑁呫�佺淮淇寘绛夈��
+// 鍗凤細鏌愪簺闀挎潯褰笖闅句互绮剧‘璁℃暟鐨勭墿鍝侊紝濡傝兌甯︺�佺粷缂樺甫绛夈��
+// 缁勶細鐢辫嫢骞蹭釜鐩稿悓鎴栦笉鍚岀被鍨嬬殑鍏冧欢缁勬垚鐨勫姛鑳藉崟鍏冿紝濡備竴缁勭數姹犮�佷竴濂楅榾闂ㄧ粍绛夈��
+
+			return render.renderSwitch(text, [
+				{ text: '涓�/浠�', value: '0' },
+				{ text: '绫筹紙m锛�', value: '1' },
+				{ text: '鍏枻锛坘g锛�', value: '2' },
+				{ text: '鍗囷紙L锛�', value: '3' },
+				{ text: '濂�', value: '4' },
+				{ text: '鍗�', value: '5' },
+				{ text: '缁�', value: '6' },
+			])
+		},
+   },
+   {
+    title: '渚涘簲鍟�',
+    align:"center",
+    dataIndex: 'supplier'
+   },
+   {
+    title: '鏈�浣庡簱瀛�',
+    align:"center",
+    dataIndex: 'minStock'
+   },
+   {
+    title: '瀹夊叏搴撳瓨',
+    align:"center",
+    dataIndex: 'safeStock'
+   },
+   {
+    title: '搴撲綅',
+    align:"center",
+    dataIndex: 'location'
+   },
+   {
+    title: '褰撳墠搴撳瓨',
+    align:"center",
+    dataIndex: 'currentStock'
+   },
+   {
+    title: '鍐荤粨搴撳瓨',
+    align:"center",
+    dataIndex: 'frozenStock'
+   },
+   {
+    title: '澶囨敞',
+    align:"center",
+    dataIndex: 'remark'
+   },
+   {
+    title: '鐘舵��',
+    align:"center",
+    dataIndex: 'status',
+    customRender: ({ text }) => {
+      // '鍙敤', '鍐荤粨', '鎶ュ簾'
+			return render.renderSwitch(text, [
+				{ text: '鍙敤', value: '0' },
+				{ text: '鍐荤粨', value: '1' },
+				{ text: '鎶ュ簾', value: '2' },
+			])
+		},
+   },
+];
+//鏌ヨ鏁版嵁
+export const searchFormSchema: FormSchema[] = [
+	{
+      label: "闆朵欢缂栧彿",
+      field: 'partNumber',
+      component: 'Input',
+      colProps: {span: 6},
+ 	},
+	{
+      label: "闆朵欢鍚嶇О",
+      field: 'partName',
+      component: 'Input',
+      colProps: {span: 6},
+ 	},
+];
+//琛ㄥ崟鏁版嵁
+export const formSchema: FormSchema[] = [
+  {
+    label: '闆朵欢缂栧彿',
+    field: 'partNumber',
+    component: 'Input',
+    dynamicRules: ({model,schema}) => {
+          return [
+                 { required: true, message: '璇疯緭鍏ラ浂浠剁紪鍙�!'},
+                 {...rules.duplicateCheckRule('dry_spare_parts', 'part_number',model,schema)[0]},
+          ];
+     },
+  },
+  {
+    label: '闆朵欢鍚嶇О',
+    field: 'partName',
+    component: 'Input',
+    dynamicRules: ({}) => {
+          return [
+                 { required: true, message: '璇疯緭鍏ラ浂浠跺悕绉�!'},
+          ];
+     },
+  },
+  {
+    label: '鍒嗙被',
+    field: 'category',
+    component: 'Select',
+    componentProps:{
+      options: [
+        { label: '鏈烘绫�', value: '0' },
+        { label: '鐢垫皵绫�', value: '1' },
+        { label: '娑插帇绫�', value: '2' },
+        { label: '姘斿姩绫�', value: '3' },
+        { label: '鐢靛瓙绫�', value: '4' },
+      ]
+     },
+    dynamicRules: ({}) => {
+          return [                 
+            { required: true, message: '璇疯緭鍏ュ垎绫�!'},
+          ];
+     },
+  },
+  {
+    label: '瑙勬牸鍨嬪彿',
+    field: 'specModel',
+    component: 'Input',
+  },
+  {
+    label: '鍗曚綅',
+    field: 'unit',
+    component: 'Select',
+
+    componentProps:{
+        options: [
+          { label: '涓�/浠�', value: '0' },
+          { label: '绫筹紙m锛�', value: '1' },
+          { label: '鍏枻锛坘g锛�', value: '2' },
+          { label: '鍗囷紙L锛�', value: '3' },
+          { label: '濂�', value: '4' },
+          { label: '鍗�', value: '5' },
+          { label: '缁�', value: '6' },
+        ]
+     },
+    dynamicRules: ({}) => {
+          return [
+                 { required: true, message: '璇疯緭鍏ュ崟浣�!'},
+          ];
+     },
+  },
+  {
+    label: '渚涘簲鍟�',
+    field: 'supplier',
+    component: 'Input',
+  },
+  {
+    label: '鏈�浣庡簱瀛�',
+    field: 'minStock',
+    component: 'InputNumber',
+  },
+  {
+    label: '瀹夊叏搴撳瓨',
+    field: 'safeStock',
+    component: 'InputNumber',
+  },
+  {
+    label: '搴撲綅',
+    field: 'location',
+    component: 'Input',
+  },
+  {
+    label: '褰撳墠搴撳瓨',
+    field: 'currentStock',
+    component: 'InputNumber',
+    dynamicRules: ({}) => {
+          return [
+                 { required: true, message: '璇疯緭鍏ュ綋鍓嶅簱瀛�!'},
+          ];
+     },
+  },
+  {
+    label: '鍐荤粨搴撳瓨',
+    field: 'frozenStock',
+    component: 'InputNumber',
+  },
+  {
+    label: '澶囨敞',
+    field: 'remark',
+    component: 'InputTextArea',
+  },
+  {
+    label: '鐘舵��',
+    field: 'status',
+    component: 'Select',
+    defaultValue: '0',
+    componentProps:{
+        options: [
+          { label: '鍙敤', value: '0' },
+          { label: '鍐荤粨', value: '1' },
+          { label: '鎶ュ簾', value: '2' },
+        ]
+     },
+    dynamicRules: ({}) => {
+          return [
+                 { required: true, message: '璇疯緭鍏ョ姸鎬�!'},
+          ];
+     },
+  },
+	// TODO 涓婚敭闅愯棌瀛楁锛岀洰鍓嶅啓姝讳负ID
+	{
+	  label: '',
+	  field: 'id',
+	  component: 'Input',
+	  show: false
+	},
+];
+
+
+
+/**
+* 娴佺▼琛ㄥ崟璋冪敤杩欎釜鏂规硶鑾峰彇formSchema
+* @param param
+*/
+export function getBpmFormSchema(_formData): FormSchema[]{
+  // 榛樿鍜屽師濮嬭〃鍗曚繚鎸佷竴鑷� 濡傛灉娴佺▼涓厤缃簡鏉冮檺鏁版嵁锛岃繖閲岄渶瑕佸崟鐙鐞唂ormSchema
+  return formSchema;
+}
\ No newline at end of file
diff --git a/src/views/dry/spareParts/DrySparePartsList.vue b/src/views/dry/spareParts/DrySparePartsList.vue
new file mode 100644
index 0000000..5f942af
--- /dev/null
+++ b/src/views/dry/spareParts/DrySparePartsList.vue
@@ -0,0 +1,197 @@
+<template>
+  <div>
+    <!--寮曠敤琛ㄦ牸-->
+   <BasicTable @register="registerTable" :rowSelection="rowSelection">
+     <!--鎻掓Ы:table鏍囬-->
+      <template #tableTitle>
+          <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 鏂板</a-button>
+          <a-button type="primary" @click="handleStockIn" preIcon="ant-design:download-outlined"> 鍏ュ簱</a-button>
+          <a-button type="primary" @click="handleStockOut" preIcon="ant-design:upload-outlined"> 鍑哄簱</a-button>
+          <a-button  type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 瀵煎嚭</a-button>
+          <j-upload-button  type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">瀵煎叆</j-upload-button>
+          <a-dropdown v-if="selectedRowKeys.length > 0">
+              <template #overlay>
+                <a-menu>
+                  <a-menu-item key="1" @click="batchHandleDelete">
+                    <Icon icon="ant-design:delete-outlined"></Icon>
+                    鍒犻櫎
+                  </a-menu-item>
+                </a-menu>
+              </template>
+              <a-button>鎵归噺鎿嶄綔
+                <Icon icon="mdi:chevron-down"></Icon>
+              </a-button>
+        </a-dropdown>
+      </template>
+       <!--鎿嶄綔鏍�-->
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
+      </template>
+      <!--瀛楁鍥炴樉鎻掓Ы-->
+      <template #htmlSlot="{text}">
+         <div v-html="text"></div>
+      </template>
+
+      <template #fileSlot="{text}">
+         <span v-if="!text" style="font-size: 12px;font-style: italic;">鏃犳枃浠�</span>
+         <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">涓嬭浇</a-button>
+      </template>
+    </BasicTable>
+    <!-- 琛ㄥ崟鍖哄煙 -->
+    <DrySparePartsModal @register="registerModal" @success="handleSuccess"></DrySparePartsModal>
+    <DrySpareStockLogModal @register="registerStockLogModal" @success="handleSuccess"></DrySpareStockLogModal>
+  </div>
+</template>
+
+<script lang="ts" name="dry-drySpareParts" setup>
+  import {ref, computed, unref} from 'vue';
+  import {BasicTable, useTable, TableAction} from '/@/components/Table';
+  import {useModal} from '/@/components/Modal';
+  import { useListPage } from '/@/hooks/system/useListPage'
+  import DrySparePartsModal from './components/DrySparePartsModal.vue'
+  import DrySpareStockLogModal from './components/DrySpareStockLogModal.vue'
+  import {columns, searchFormSchema} from './DrySpareParts.data';
+  import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './DrySpareParts.api';
+  import { downloadFile } from '/@/utils/common/renderUtils';
+  const checkedKeys = ref<Array<string | number>>([]);
+  //娉ㄥ唽model
+  const [registerModal, {openModal}] = useModal();
+  const [registerStockLogModal, {openModal: openStockLogModal}] = useModal();
+  //娉ㄥ唽table鏁版嵁
+  const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
+      tableProps:{
+           title: '澶囧搧澶囦欢',
+           api: list,
+           columns,
+           canResize:false,
+           formConfig: {
+              //labelWidth: 120,
+              schemas: searchFormSchema,
+              autoSubmitOnEnter:true,
+              showAdvancedButton:true,
+              fieldMapToNumber: [
+              ],
+              fieldMapToTime: [
+              ],
+            },
+           actionColumn: {
+               width: 120,
+               fixed:'right'
+            },
+      },
+       exportConfig: {
+            name:"澶囧搧澶囦欢",
+            url: getExportUrl,
+          },
+          importConfig: {
+            url: getImportUrl,
+            success: handleSuccess
+          },
+  })
+
+  const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
+
+   /**
+    * 鏂板浜嬩欢
+    */
+  function handleAdd() {
+     openModal(true, {
+       isUpdate: false,
+       showFooter: true,
+     });
+  }
+
+  /**
+   * 鍏ュ簱浜嬩欢
+   */
+  function handleStockIn() {
+    openStockLogModal(true, {
+      isUpdate: false,
+      showFooter: true,
+      stockType: 'in'
+    });
+  }
+
+  /**
+   * 鍑哄簱浜嬩欢
+   */
+  function handleStockOut() {
+    openStockLogModal(true, {
+      isUpdate: false,
+      showFooter: true,
+      stockType: 'out'
+    });
+  }
+   /**
+    * 缂栬緫浜嬩欢
+    */
+  function handleEdit(record: Recordable) {
+     openModal(true, {
+       record,
+       isUpdate: true,
+       showFooter: true,
+     });
+   }
+   /**
+    * 璇︽儏
+   */
+  function handleDetail(record: Recordable) {
+     openModal(true, {
+       record,
+       isUpdate: true,
+       showFooter: false,
+     });
+   }
+   /**
+    * 鍒犻櫎浜嬩欢
+    */
+  async function handleDelete(record) {
+     await deleteOne({id: record.id}, handleSuccess);
+   }
+   /**
+    * 鎵归噺鍒犻櫎浜嬩欢
+    */
+  async function batchHandleDelete() {
+     await batchDelete({ids: selectedRowKeys.value}, handleSuccess);
+   }
+   /**
+    * 鎴愬姛鍥炶皟
+    */
+  function handleSuccess() {
+      (selectedRowKeys.value = []) && reload();
+   }
+   /**
+      * 鎿嶄綔鏍�
+      */
+  function getTableAction(record){
+       return [
+         {
+           label: '缂栬緫',
+           onClick: handleEdit.bind(null, record),
+         }
+       ]
+   }
+     /**
+        * 涓嬫媺鎿嶄綔鏍�
+        */
+  function getDropDownAction(record){
+       return [
+         {
+           label: '璇︽儏',
+           onClick: handleDetail.bind(null, record),
+         }, {
+           label: '鍒犻櫎',
+           popConfirm: {
+             title: '鏄惁纭鍒犻櫎',
+             confirm: handleDelete.bind(null, record),
+           }
+         }
+       ]
+   }
+
+
+</script>
+
+<style scoped>
+
+</style>
\ No newline at end of file
diff --git a/src/views/dry/spareParts/DrySpareParts_menu_insert.sql b/src/views/dry/spareParts/DrySpareParts_menu_insert.sql
new file mode 100644
index 0000000..e0c7c37
--- /dev/null
+++ b/src/views/dry/spareParts/DrySpareParts_menu_insert.sql
@@ -0,0 +1,26 @@
+-- 娉ㄦ剰锛氳椤甸潰瀵瑰簲鐨勫墠鍙扮洰褰曚负views/dry鏂囦欢澶逛笅
+-- 濡傛灉浣犳兂鏇存敼鍒板叾浠栫洰褰曪紝璇蜂慨鏀箂ql涓璫omponent瀛楁瀵瑰簲鐨勫��
+
+
+INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external) 
+VALUES ('2025072308576550150', NULL, '澶囧搧澶囦欢', '/dry/drySparePartsList', 'dry/spareParts/DrySparePartsList', NULL, NULL, 0, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2025-07-23 08:57:15', NULL, NULL, 0);
+
+-- 鏉冮檺鎺у埗sql
+-- 鏂板
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025072308576550151', '2025072308576550150', '娣诲姞澶囧搧澶囦欢', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_spare_parts:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-23 08:57:15', NULL, NULL, 0, 0, '1', 0);
+-- 缂栬緫
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025072308576550152', '2025072308576550150', '缂栬緫澶囧搧澶囦欢', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_spare_parts:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-23 08:57:15', NULL, NULL, 0, 0, '1', 0);
+-- 鍒犻櫎
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025072308576550153', '2025072308576550150', '鍒犻櫎澶囧搧澶囦欢', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_spare_parts:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-23 08:57:15', NULL, NULL, 0, 0, '1', 0);
+-- 鎵归噺鍒犻櫎
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025072308576550154', '2025072308576550150', '鎵归噺鍒犻櫎澶囧搧澶囦欢', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_spare_parts:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-23 08:57:15', NULL, NULL, 0, 0, '1', 0);
+-- 瀵煎嚭excel
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025072308576550155', '2025072308576550150', '瀵煎嚭excel_澶囧搧澶囦欢', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_spare_parts:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-23 08:57:15', NULL, NULL, 0, 0, '1', 0);
+-- 瀵煎叆excel
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('2025072308576550156', '2025072308576550150', '瀵煎叆excel_澶囧搧澶囦欢', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_spare_parts:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-23 08:57:15', NULL, NULL, 0, 0, '1', 0);
diff --git a/src/views/dry/spareParts/DrySpareStockLog.api.ts b/src/views/dry/spareParts/DrySpareStockLog.api.ts
new file mode 100644
index 0000000..e8409a8
--- /dev/null
+++ b/src/views/dry/spareParts/DrySpareStockLog.api.ts
@@ -0,0 +1,85 @@
+import {defHttp} from '/@/utils/http/axios';
+import {addStock, reduceStock} from './DrySpareParts.api';
+import { useMessage } from "/@/hooks/web/useMessage";
+
+const { createConfirm } = useMessage();
+
+enum Api {
+  list = '/dry/drySpareStockLog/list',
+  save='/dry/drySpareStockLog/add',
+  edit='/dry/drySpareStockLog/edit',
+  deleteOne = '/dry/drySpareStockLog/delete',
+  deleteBatch = '/dry/drySpareStockLog/deleteBatch',
+  importExcel = '/dry/drySpareStockLog/importExcel',
+  exportXls = '/dry/drySpareStockLog/exportXls',
+  generateStockNo = '/dry/drySpareStockLog/generateStockNo',
+}
+/**
+ * 瀵煎嚭api
+ * @param params
+ */
+export const getExportUrl = Api.exportXls;
+/**
+ * 瀵煎叆api
+ */
+export const getImportUrl = Api.importExcel;
+/**
+ * 鍒楄〃鎺ュ彛
+ * @param params
+ */
+export const list = (params) => {
+  if (params.orderNumber) {
+    return defHttp.get({url: Api.list, params: { ...params, relatedOrder: params.orderNumber }});
+  } else {
+    return defHttp.get({url: Api.list, params});
+  }
+};
+
+/**
+ * 鍒犻櫎鍗曚釜
+ */
+export const deleteOne = (params,handleSuccess) => {
+  return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
+    handleSuccess();
+  });
+}
+/**
+ * 鎵归噺鍒犻櫎
+ * @param params
+ */
+export const batchDelete = (params, handleSuccess) => {
+  createConfirm({
+    iconType: 'warning',
+    title: '纭鍒犻櫎',
+    content: '鏄惁鍒犻櫎閫変腑鏁版嵁',
+    okText: '纭',
+    cancelText: '鍙栨秷',
+    onOk: () => {
+      return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
+        handleSuccess();
+      });
+    }
+  });
+}
+/**
+ * 淇濆瓨鎴栬�呮洿鏂�
+ * @param params
+ */
+export const saveOrUpdate = (params, isUpdate) => {
+  let url = isUpdate ? Api.edit : Api.save;
+  // 鏍规嵁鎿嶄綔绫诲瀷鏇存柊搴撳瓨
+  // if (params.operationType === '0') { // 鍏ュ簱
+  //   // 璋冪敤澶囧搧澶囦欢鐨勬洿鏂版帴鍙o紝澧炲姞搴撳瓨
+  //   addStock({partNumber: params.partNumber, quantity: params.quantity});
+  // } else if (params.operationType === '1') { // 鍑哄簱
+  //   // 璋冪敤澶囧搧澶囦欢鐨勬洿鏂版帴鍙o紝鍑忓皯搴撳瓨
+  //   reduceStock({partNumber: params.partNumber, quantity: params.quantity});
+  // }
+  return defHttp.post({url: url, params});
+}
+
+/**
+ * 鐢熸垚鍑哄叆搴撳崟鍙�
+ * @param type 鍗曞彿绫诲瀷锛孋K锛堝嚭搴擄級鎴� RK锛堝叆搴擄級
+ */
+export const generateStockNo = (type) => defHttp.get({url: Api.generateStockNo, params: {type}},{ successMessageMode: 'none' });
diff --git a/src/views/dry/spareParts/DrySpareStockLog.data.ts b/src/views/dry/spareParts/DrySpareStockLog.data.ts
new file mode 100644
index 0000000..da07d93
--- /dev/null
+++ b/src/views/dry/spareParts/DrySpareStockLog.data.ts
@@ -0,0 +1,206 @@
+import { BasicColumn } from '/@/components/Table'
+import { FormSchema } from '/@/components/Table'
+//鍒楄〃鏁版嵁
+export const columns: BasicColumn[] = [
+	{
+		title: '鎿嶄綔缂栧彿',
+		align: 'center',
+		dataIndex: 'operationNumber',
+	},
+	{
+		title: '闆朵欢缂栧彿',
+		align: 'center',
+		dataIndex: 'partNumber_dictText',
+	},
+	{
+		title: '鎿嶄綔绫诲瀷',
+		align: 'center',
+		dataIndex: 'operationType',
+		customRender: ({ text }) => {
+			// '鍏ュ簱', '鍑哄簱', '璋冩嫧', '鎶ュ簾'
+			const options = {
+				'0': '鍏ュ簱',
+				'1': '鍑哄簱',
+				'2': '璋冩嫧',
+				'3': '鎶ュ簾',
+			}
+			return options[text] || text
+		},
+	},
+	{
+		title: '鏁伴噺',
+		align: 'center',
+		dataIndex: 'quantity',
+	},
+	// {
+	// 	title: '鎿嶄綔鏃堕棿',
+	// 	align: 'center',
+	// 	dataIndex: 'operationTime',
+	// 	customRender: ({ text }) => {
+	// 		return !text ? '' : text.length > 10 ? text.substr(0, 10) : text
+	// 	},
+	// },
+	// {
+	// 	title: '鎿嶄綔浜�',
+	// 	align: 'center',
+	// 	dataIndex: 'operator',
+	// },
+	// {
+	// 	title: '鍏宠仈鍗曞彿锛堝缁翠慨鍗曞彿锛�',
+	// 	align: 'center',
+	// 	dataIndex: 'relatedOrder',
+	// },
+	// {
+	// 	title: '搴撲綅',
+	// 	align: 'center',
+	// 	dataIndex: 'location',
+	// },
+	{
+		title: '澶囨敞',
+		align: 'center',
+		dataIndex: 'remark',
+	},
+]
+//鏌ヨ鏁版嵁
+export const searchFormSchema: FormSchema[] = [
+	{
+		label: '闆朵欢缂栧彿',
+		field: 'partNumber',
+		component: 'JDictSelectTag',
+		componentProps: {
+			dictCode: 'dry_spare_parts,part_name,part_number',
+		},
+		colProps: { span: 6 },
+	},
+	{
+		label: '鎿嶄綔绫诲瀷',
+		field: 'operationType',
+		component: 'JDictSelectTag',
+		componentProps: {
+			options: [
+				{ label: '鍏ュ簱', value: '0' },
+				{ label: '鍑哄簱', value: '1' },
+				{ label: '璋冩嫧', value: '2' },
+				{ label: '鎶ュ簾', value: '3' },
+			],
+		},
+		colProps: { span: 6 },
+	},
+]
+//琛ㄥ崟鏁版嵁
+export const formSchema = (params): FormSchema[] => {
+	const { isUpdate, stockType } = params
+	return [
+	
+    {
+      label: '鍑哄叆搴撳崟鍙�',
+      field: 'operationNumber',
+      component: 'Input',
+      dynamicDisabled: ({}) => {
+        return true
+      },
+      dynamicRules: ({}) => {
+        return [
+          { required: !isUpdate, message: '璇疯緭鍏ュ嚭鍏ュ簱鍗曞彿!' },
+        ]
+      },
+    },
+		{
+			label: '闆朵欢缂栧彿',
+			field: 'partNumber',
+			component: 'JDictSelectTag',
+			componentProps: {
+				dictCode: 'dry_spare_parts,part_name,part_number',
+			},
+			dynamicRules: ({}) => {
+				return [
+					{ required: true, message: '璇疯緭鍏ラ浂浠剁紪鍙�!' }, // 濮嬬粓蹇呭~
+				]
+			},
+		},
+		{
+			label: '鎿嶄綔绫诲瀷',
+			field: 'operationType',
+			component: 'JDictSelectTag',
+			defaultValue: stockType === 'in' ? '0' : stockType === 'out' ? '1' : undefined,
+			componentProps: {
+				options: [
+					{ label: '鍏ュ簱', value: '0' },
+					{ label: '鍑哄簱', value: '1' },
+					{ label: '璋冩嫧', value: '2' },
+					{ label: '鎶ュ簾', value: '3' },
+				],
+			},
+			dynamicRules: ({}) => {
+				return [
+					{ required: true, message: '璇疯緭鍏ユ搷浣滅被鍨�!' }, // 濮嬬粓蹇呭~
+				]
+			},
+		},
+		{
+			label: '鏁伴噺',
+			field: 'quantity',
+			component: 'Input',
+			dynamicRules: ({}) => {
+				return [
+					{ required: true, message: '璇疯緭鍏ユ暟閲�!' }, // 濮嬬粓蹇呭~
+					{ pattern: /^[0-9]*$/, message: '璇疯緭鍏ユ暟瀛�!' },
+					{
+						validator: (_, value) => {
+							if (stockType === 'in' && parseFloat(value) <= 0) {
+								return Promise.reject('鍏ュ簱鏁伴噺蹇呴』澶т簬0')
+							}
+							if (stockType === 'out' && parseFloat(value) <= 0) {
+								return Promise.reject('鍑哄簱鏁伴噺蹇呴』澶т簬0')
+							}
+							return Promise.resolve()
+						},
+						trigger: 'change',
+					},
+				]
+			},
+		},
+		// {
+		// 	label: '鎿嶄綔鏃堕棿',
+		// 	field: 'operationTime',
+		// 	component: 'DatePicker',
+		// },
+		// {
+		// 	label: '鎿嶄綔浜�',
+		// 	field: 'operator',
+		// 	component: 'Input',
+		// },
+		{
+			label: '鍏宠仈鍗曞彿锛堝缁翠慨鍗曞彿锛�',
+			field: 'relatedOrder',
+			component: 'Input',
+      show: false,
+		},
+		// {
+		// 	label: '搴撲綅',
+		// 	field: 'location',
+		// 	component: 'Input',
+		// },
+		{
+			label: '澶囨敞',
+			field: 'remark',
+			component: 'InputTextArea',
+		},
+		// TODO 涓婚敭闅愯棌瀛楁锛岀洰鍓嶅啓姝讳负ID
+		{
+			label: '',
+			field: 'id',
+			component: 'Input',
+			show: false,
+		},
+	]
+}
+
+/**
+ * 娴佺▼琛ㄥ崟璋冪敤杩欎釜鏂规硶鑾峰彇formSchema
+ * @param param
+ */
+export function getBpmFormSchema(_formData): FormSchema[] {
+	// 榛樿鍜屽師濮嬭〃鍗曚繚鎸佷竴鑷� 濡傛灉娴佺▼涓厤缃簡鏉冮檺鏁版嵁锛岃繖閲岄渶瑕佸崟鐙鐞唂ormSchema
+  return formSchema(_formData);
+}
diff --git a/src/views/dry/spareParts/DrySpareStockLogList.vue b/src/views/dry/spareParts/DrySpareStockLogList.vue
new file mode 100644
index 0000000..82325ec
--- /dev/null
+++ b/src/views/dry/spareParts/DrySpareStockLogList.vue
@@ -0,0 +1,170 @@
+<template>
+  <div>
+    <!--寮曠敤琛ㄦ牸-->
+   <BasicTable @register="registerTable" :rowSelection="rowSelection">
+     <!--鎻掓Ы:table鏍囬-->
+      <template #tableTitle>
+          <!-- <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 鏂板</a-button> -->
+          <!-- <a-button  type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 瀵煎嚭</a-button>
+          <j-upload-button  type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">瀵煎叆</j-upload-button> -->
+          <a-dropdown v-if="selectedRowKeys.length > 0">
+              <template #overlay>
+                <a-menu>
+                  <a-menu-item key="1" @click="batchHandleDelete">
+                    <Icon icon="ant-design:delete-outlined"></Icon>
+                    鍒犻櫎
+                  </a-menu-item>
+                </a-menu>
+              </template>
+              <a-button>鎵归噺鎿嶄綔
+                <Icon icon="mdi:chevron-down"></Icon>
+              </a-button>
+        </a-dropdown>
+      </template>
+       <!--鎿嶄綔鏍�-->
+      <template #action="{ record }">
+        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
+      </template>
+      <!--瀛楁鍥炴樉鎻掓Ы-->
+      <template #htmlSlot="{text}">
+         <div v-html="text"></div>
+      </template>
+      <template #fileSlot="{text}">
+         <span v-if="!text" style="font-size: 12px;font-style: italic;">鏃犳枃浠�</span>
+         <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">涓嬭浇</a-button>
+      </template>
+    </BasicTable>
+    <!-- 琛ㄥ崟鍖哄煙 -->
+    <DrySpareStockLogModal @register="registerModal" @success="handleSuccess"></DrySpareStockLogModal>
+  </div>
+</template>
+
+<script lang="ts" name="dry-drySpareStockLog" setup>
+  import {ref, computed, unref} from 'vue';
+  import {BasicTable, useTable, TableAction} from '/@/components/Table';
+  import {useModal} from '/@/components/Modal';
+  import { useListPage } from '/@/hooks/system/useListPage'
+  import DrySpareStockLogModal from './components/DrySpareStockLogModal.vue'
+  import {columns, searchFormSchema} from './DrySpareStockLog.data';
+  import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './DrySpareStockLog.api';
+  import { downloadFile } from '/@/utils/common/renderUtils';
+  const checkedKeys = ref<Array<string | number>>([]);
+  //娉ㄥ唽model
+  const [registerModal, {openModal}] = useModal();
+  //娉ㄥ唽table鏁版嵁
+  const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
+      tableProps:{
+           title: '鍑哄叆搴撹褰曡〃',
+           api: list,
+           columns,
+           canResize:false,
+           formConfig: {
+              //labelWidth: 120,
+              schemas: searchFormSchema,
+              autoSubmitOnEnter:true,
+              showAdvancedButton:true,
+              fieldMapToNumber: [
+              ],
+              fieldMapToTime: [
+              ],
+            },
+           actionColumn: {
+               width: 120,
+               fixed:'right'
+            },
+        showActionColumn: false
+      },
+       exportConfig: {
+            name:"鍑哄叆搴撹褰曡〃",
+            url: getExportUrl,
+          },
+          importConfig: {
+            url: getImportUrl,
+            success: handleSuccess
+          },
+  })
+
+  const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
+
+   /**
+    * 鏂板浜嬩欢
+    */
+  function handleAdd() {
+     openModal(true, {
+       isUpdate: false,
+       showFooter: true,
+     });
+  }
+   /**
+    * 缂栬緫浜嬩欢
+    */
+  function handleEdit(record: Recordable) {
+     openModal(true, {
+       record,
+       isUpdate: true,
+       showFooter: true,
+     });
+   }
+   /**
+    * 璇︽儏
+   */
+  function handleDetail(record: Recordable) {
+     openModal(true, {
+       record,
+       isUpdate: true,
+       showFooter: false,
+     });
+   }
+   /**
+    * 鍒犻櫎浜嬩欢
+    */
+  async function handleDelete(record) {
+     await deleteOne({id: record.id}, handleSuccess);
+   }
+   /**
+    * 鎵归噺鍒犻櫎浜嬩欢
+    */
+  async function batchHandleDelete() {
+     await batchDelete({ids: selectedRowKeys.value}, handleSuccess);
+   }
+   /**
+    * 鎴愬姛鍥炶皟
+    */
+  function handleSuccess() {
+      (selectedRowKeys.value = []) && reload();
+   }
+   /**
+      * 鎿嶄綔鏍�
+      */
+  function getTableAction(record){
+       return [
+         {
+           label: '缂栬緫',
+           onClick: handleEdit.bind(null, record),
+         }
+       ]
+   }
+     /**
+        * 涓嬫媺鎿嶄綔鏍�
+        */
+  function getDropDownAction(record){
+       return [
+         {
+           label: '璇︽儏',
+           onClick: handleDetail.bind(null, record),
+         }, {
+           label: '鍒犻櫎',
+           popConfirm: {
+             title: '鏄惁纭鍒犻櫎',
+             confirm: handleDelete.bind(null, record),
+           }
+         }
+       ]
+   }
+
+
+</script>
+
+<style scoped>
+
+</style>
diff --git a/src/views/dry/spareParts/DrySpareStockLog_menu_insert.sql b/src/views/dry/spareParts/DrySpareStockLog_menu_insert.sql
new file mode 100644
index 0000000..2882a23
--- /dev/null
+++ b/src/views/dry/spareParts/DrySpareStockLog_menu_insert.sql
@@ -0,0 +1,26 @@
+-- 娉ㄦ剰锛氳椤甸潰瀵瑰簲鐨勫墠鍙扮洰褰曚负views/dry鏂囦欢澶逛笅
+-- 濡傛灉浣犳兂鏇存敼鍒板叾浠栫洰褰曪紝璇蜂慨鏀箂ql涓璫omponent瀛楁瀵瑰簲鐨勫��
+
+
+INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external) 
+VALUES ('202507230857430230', NULL, '鍑哄叆搴撹褰曡〃', '/dry/drySpareStockLogList', 'dry/spareParts/DrySpareStockLogList', NULL, NULL, 0, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2025-07-23 08:57:23', NULL, NULL, 0);
+
+-- 鏉冮檺鎺у埗sql
+-- 鏂板
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('202507230857430231', '202507230857430230', '娣诲姞鍑哄叆搴撹褰曡〃', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_spare_stock_log:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-23 08:57:23', NULL, NULL, 0, 0, '1', 0);
+-- 缂栬緫
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('202507230857430232', '202507230857430230', '缂栬緫鍑哄叆搴撹褰曡〃', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_spare_stock_log:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-23 08:57:23', NULL, NULL, 0, 0, '1', 0);
+-- 鍒犻櫎
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('202507230857430233', '202507230857430230', '鍒犻櫎鍑哄叆搴撹褰曡〃', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_spare_stock_log:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-23 08:57:23', NULL, NULL, 0, 0, '1', 0);
+-- 鎵归噺鍒犻櫎
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('202507230857430234', '202507230857430230', '鎵归噺鍒犻櫎鍑哄叆搴撹褰曡〃', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_spare_stock_log:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-23 08:57:23', NULL, NULL, 0, 0, '1', 0);
+-- 瀵煎嚭excel
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('202507230857430235', '202507230857430230', '瀵煎嚭excel_鍑哄叆搴撹褰曡〃', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_spare_stock_log:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-23 08:57:23', NULL, NULL, 0, 0, '1', 0);
+-- 瀵煎叆excel
+INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
+VALUES ('202507230857430236', '202507230857430230', '瀵煎叆excel_鍑哄叆搴撹褰曡〃', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_spare_stock_log:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-07-23 08:57:23', NULL, NULL, 0, 0, '1', 0);
diff --git a/src/views/dry/spareParts/components/DrySparePartsForm.vue b/src/views/dry/spareParts/components/DrySparePartsForm.vue
new file mode 100644
index 0000000..23f1640
--- /dev/null
+++ b/src/views/dry/spareParts/components/DrySparePartsForm.vue
@@ -0,0 +1,70 @@
+<template>
+    <div style="min-height: 400px">
+        <BasicForm @register="registerForm"></BasicForm>
+        <div style="width: 100%;text-align: center" v-if="!formDisabled">
+            <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">鎻� 浜�</a-button>
+        </div>
+    </div>
+</template>
+
+<script lang="ts">
+    import {BasicForm, useForm} from '/@/components/Form/index';
+    import {computed, defineComponent} from 'vue';
+    import {defHttp} from '/@/utils/http/axios';
+    import { propTypes } from '/@/utils/propTypes';
+    import {getBpmFormSchema} from '../DrySpareParts.data';
+    import {saveOrUpdate} from '../DrySpareParts.api';
+    
+    export default defineComponent({
+        name: "DrySparePartsForm",
+        components:{
+            BasicForm
+        },
+        props:{
+            formData: propTypes.object.def({}),
+            formBpm: propTypes.bool.def(true),
+        },
+        setup(props){
+            const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
+                labelWidth: 150,
+                schemas: getBpmFormSchema(props.formData),
+                showActionButtonGroup: false,
+                baseColProps: {span: 24}
+            });
+
+            const formDisabled = computed(()=>{
+                if(props.formData.disabled === false){
+                    return false;
+                }
+                return true;
+            });
+
+            let formData = {};
+            const queryByIdUrl = '/dry/drySpareParts/queryById';
+            async function initFormData(){
+                let params = {id: props.formData.dataId};
+                const data = await defHttp.get({url: queryByIdUrl, params});
+                formData = {...data}
+                //璁剧疆琛ㄥ崟鐨勫��
+                await setFieldsValue(formData);
+                //榛樿鏄鐢�
+                await setProps({disabled: formDisabled.value})
+            }
+
+            async function submitForm() {
+                let data = getFieldsValue();
+                let params = Object.assign({}, formData, data);
+                console.log('琛ㄥ崟鏁版嵁', params)
+                await saveOrUpdate(params, true)
+            }
+
+            initFormData();
+            
+            return {
+                registerForm,
+                formDisabled,
+                submitForm,
+            }
+        }
+    });
+</script>
\ No newline at end of file
diff --git a/src/views/dry/spareParts/components/DrySparePartsModal.vue b/src/views/dry/spareParts/components/DrySparePartsModal.vue
new file mode 100644
index 0000000..6e56954
--- /dev/null
+++ b/src/views/dry/spareParts/components/DrySparePartsModal.vue
@@ -0,0 +1,66 @@
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
+      <BasicForm @register="registerForm"/>
+  </BasicModal>
+</template>
+
+<script lang="ts" setup>
+    import {ref, computed, unref} from 'vue';
+    import {BasicModal, useModalInner} from '/@/components/Modal';
+    import {BasicForm, useForm} from '/@/components/Form/index';
+    import {formSchema} from '../DrySpareParts.data';
+    import {saveOrUpdate} from '../DrySpareParts.api';
+    // Emits澹版槑
+    const emit = defineEmits(['register','success']);
+    const isUpdate = ref(true);
+    //琛ㄥ崟閰嶇疆
+    const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
+        //labelWidth: 150,
+        schemas: formSchema,
+        showActionButtonGroup: false,
+        baseColProps: {span: 24}
+    });
+    //琛ㄥ崟璧嬪��
+    const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
+        //閲嶇疆琛ㄥ崟
+        await resetFields();
+        setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
+        isUpdate.value = !!data?.isUpdate;
+        if (unref(isUpdate)) {
+            //琛ㄥ崟璧嬪��
+            await setFieldsValue({
+                ...data.record,
+            });
+        }
+        // 闅愯棌搴曢儴鏃剁鐢ㄦ暣涓〃鍗�
+       setProps({ disabled: !data?.showFooter })
+    });
+    //璁剧疆鏍囬
+    const title = computed(() => (!unref(isUpdate) ? '鏂板' : '缂栬緫'));
+    //琛ㄥ崟鎻愪氦浜嬩欢
+    async function handleSubmit(v) {
+        try {
+            let values = await validate();
+            setModalProps({confirmLoading: true});
+            //鎻愪氦琛ㄥ崟
+            await saveOrUpdate(values, isUpdate.value);
+            //鍏抽棴寮圭獥
+            closeModal();
+            //鍒锋柊鍒楄〃
+            emit('success');
+        } finally {
+            setModalProps({confirmLoading: false});
+        }
+    }
+</script>
+
+<style lang="less" scoped>
+	/** 鏃堕棿鍜屾暟瀛楄緭鍏ユ鏍峰紡 */
+  :deep(.ant-input-number){
+		width: 100%
+	}
+
+	:deep(.ant-calendar-picker){
+		width: 100%
+	}
+</style>
\ No newline at end of file
diff --git a/src/views/dry/spareParts/components/DrySpareStockLogForm.vue b/src/views/dry/spareParts/components/DrySpareStockLogForm.vue
new file mode 100644
index 0000000..c0ea7dd
--- /dev/null
+++ b/src/views/dry/spareParts/components/DrySpareStockLogForm.vue
@@ -0,0 +1,70 @@
+<template>
+	<div style="min-height: 400px">
+		<BasicForm @register="registerForm" />
+		<div style="width: 100%; text-align: center" v-if="!formDisabled">
+			<a-button @click="submitForm" pre-icon="ant-design:check" type="primary">鎻� 浜�</a-button>
+		</div>
+	</div>
+</template>
+
+<script lang="ts">
+	import { BasicForm, useForm } from '/@/components/Form/index'
+	import { computed, defineComponent } from 'vue'
+	import { defHttp } from '/@/utils/http/axios'
+	import { propTypes } from '/@/utils/propTypes'
+	import { getBpmFormSchema } from '../DrySpareStockLog.data'
+	import { saveOrUpdate } from '../DrySpareStockLog.api'
+
+	export default defineComponent({
+		name: 'DrySpareStockLogForm',
+		components: {
+			BasicForm,
+		},
+		props: {
+			formData: propTypes.object.def({}),
+			formBpm: propTypes.bool.def(true),
+		},
+		setup(props) {
+			const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
+				labelWidth: 150,
+				schemas: getBpmFormSchema(props.formData),
+				showActionButtonGroup: false,
+				baseColProps: { span: 24 },
+			})
+
+			const formDisabled = computed(() => {
+				if (props.formData.disabled === false) {
+					return false
+				}
+				return true
+			})
+
+			let formData = {}
+			const queryByIdUrl = '/dry/drySpareStockLog/queryById'
+			async function initFormData() {
+				let params = { id: props.formData.dataId }
+				const data = await defHttp.get({ url: queryByIdUrl, params })
+				formData = { ...data }
+				//璁剧疆琛ㄥ崟鐨勫��
+				await setFieldsValue(formData)
+				//榛樿鏄鐢�
+				await setProps({ disabled: formDisabled.value })
+			}
+
+			async function submitForm() {
+				let data = getFieldsValue()
+				let params = Object.assign({}, formData, data)
+				console.log('琛ㄥ崟鏁版嵁', params)
+				await saveOrUpdate(params, true)
+			}
+
+			initFormData()
+
+			return {
+				registerForm,
+				formDisabled,
+				submitForm,
+			}
+		},
+	})
+</script>
diff --git a/src/views/dry/spareParts/components/DrySpareStockLogModal.vue b/src/views/dry/spareParts/components/DrySpareStockLogModal.vue
new file mode 100644
index 0000000..df466a7
--- /dev/null
+++ b/src/views/dry/spareParts/components/DrySpareStockLogModal.vue
@@ -0,0 +1,89 @@
+<template>
+	<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
+		<BasicForm @register="registerForm" />
+	</BasicModal>
+</template>
+
+<script lang="ts" setup>
+	import { ref, computed, unref, reactive, nextTick } from 'vue'
+	import { BasicModal, useModalInner } from '/@/components/Modal'
+	import { BasicForm, useForm } from '/@/components/Form/index'
+	import { formSchema } from '../DrySpareStockLog.data'
+	import { formatToDate } from '/@/utils/dateUtil'
+	import { saveOrUpdate } from '../DrySpareStockLog.api'
+    import { generateStockNo } from '../DrySpareStockLog.api'
+	// Emits澹版槑
+	const emit = defineEmits(['register', 'success'])
+	const isUpdate = ref(true)
+  const stockType = ref('in')
+  const relatedOrder = ref('');
+	const modalParams = reactive({
+    isUpdate: isUpdate,
+    stockType: stockType,
+  })
+	//琛ㄥ崟閰嶇疆
+	const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
+		//labelWidth: 150,
+		schemas: formSchema(unref(modalParams)),
+		showActionButtonGroup: false,
+		baseColProps: { span: 24 },
+	})
+	//琛ㄥ崟璧嬪��
+	const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+		//閲嶇疆琛ㄥ崟
+		await resetFields()
+		Object.assign(modalParams, data.record || {});
+		isUpdate.value = !!data?.isUpdate
+    stockType.value = data.stockType
+    relatedOrder.value = data.relatedOrder;
+
+    // 纭繚鍦―OM鏇存柊鍚庢墽琛岋紝閬垮厤useModal瀹炰緥鏈畾涔夌殑闂
+    await nextTick(async () => {
+		  setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter})
+		  if (unref(isUpdate)) {
+			  //琛ㄥ崟璧嬪��
+			  await setFieldsValue({
+				  operationTime: data.record.operationTime ? formatToDate(data.record.operationTime, 'YYYY-MM-DD') : null,
+				  ...data.record,
+			  })
+		  }
+      if (!unref(isUpdate)) {
+        // 鏂板鏃剁敓鎴愬嚭鍏ュ簱鍗曞彿
+        const type = stockType.value === 'in' ? 'RK' : 'CK';
+        const res = await generateStockNo(type);
+        await setFieldsValue({ operationNumber: res, operationType: stockType.value === 'in' ? '0' : '1', relatedOrder: unref(relatedOrder) });
+      }
+		  // 闅愯棌搴曢儴鏃剁鐢ㄦ暣涓〃鍗�
+		  setProps({ disabled: !data?.showFooter })
+    })
+	})
+	//璁剧疆鏍囬
+	const title = computed(() => (!unref(isUpdate) ? '鏂板' : '缂栬緫'))
+	//琛ㄥ崟鎻愪氦浜嬩欢
+	async function handleSubmit(v) {
+		try {
+			let values = await validate()
+			setModalProps({ confirmLoading: true })
+      values.relatedOrder = unref(relatedOrder);
+			//鎻愪氦琛ㄥ崟
+			await saveOrUpdate(values, unref(isUpdate))
+			//鍏抽棴寮圭獥
+			closeModal()
+			//鍒锋柊鍒楄〃
+			emit('success')
+		} finally {
+			setModalProps({ confirmLoading: false })
+		}
+	}
+</script>
+
+<style lang="less" scoped>
+	/** 鏃堕棿鍜屾暟瀛楄緭鍏ユ鏍峰紡 */
+	:deep(.ant-input-number) {
+		width: 100%;
+	}
+
+	:deep(.ant-calendar-picker) {
+		width: 100%;
+	}
+</style>

--
Gitblit v1.9.3