From 60501db2df54c0b5b80f19714c39d4b789da4cc6 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期三, 14 一月 2026 15:44:38 +0800
Subject: [PATCH] fix: 合并军泰伟业 生产模块代码

---
 src/views/productionManagement/productionProcess/index.vue                          |  218 +
 src/views/productionManagement/processRoute/index.vue                               |  194 +
 src/api/productionManagement/productionProductInput.js                              |   11 
 src/views/basicData/product/ProductSelectDialog.vue                                 |  163 +
 src/api/productionManagement/workOrder.js                                           |   25 
 src/views/productionManagement/workOrder/index.vue                                  |  587 ++++
 src/views/productionManagement/processRoute/processRouteItem/index.vue              |  503 +++
 src/api/productionManagement/productionOrder.js                                     |   71 
 src/views/productionManagement/productionDispatching/components/formDia.vue         |   25 
 src/views/productionManagement/productionDispatching/index.vue                      |  464 +++
 src/views/productionManagement/productionProcess/Edit.vue                           |  102 
 src/views/productManagement/productIdentifier/index.vue                             | 1187 ++++----
 src/views/productionManagement/productStructure/index.vue                           |  113 
 src/views/productionManagement/processRoute/ItemsForm.vue                           |  531 ++++
 src/api/productionManagement/processRouteItem.js                                    |   19 
 src/views/productionManagement/processRoute/New.vue                                 |  181 +
 src/api/productionManagement/processRoute.js                                        |   34 
 src/api/productionManagement/productStructure.js                                    |   18 
 src/views/productionManagement/operationScheduling/components/formDia.vue           |  121 
 src/views/productionManagement/productionReporting/Output.vue                       |  106 
 src/api/productionManagement/productionProcess.js                                   |   51 
 src/views/productionManagement/productionCosting/index.vue                          |   30 
 src/api/productionManagement/productionReporting.js                                 |   10 
 src/views/productionManagement/productionOrder/ProcessRouteItemForm.vue             |  555 ++++
 src/views/productionManagement/operationScheduling/index.vue                        |   47 
 src/views/productionManagement/productStructure/StructureEdit.vue                   |  311 ++
 src/views/productionManagement/productionReporting/Input.vue                        |  107 
 src/api/productionManagement/productProcessRoute.js                                 |   28 
 src/api/productionManagement/productionProductOutput.js                             |   11 
 src/views/productionManagement/productStructure/Detail/index.vue                    |  293 ++
 src/views/productionManagement/productionDispatching/components/autoDispatchDia.vue |  153 +
 src/views/productionManagement/productionReporting/index.vue                        |  888 +++---
 src/api/productionManagement/productionProductMain.js                               |   11 
 src/views/productionManagement/productionOrder/index.vue                            |  367 +-
 src/api/basicData/productModel.js                                                   |    9 
 src/views/productionManagement/processRoute/Edit.vue                                |  186 +
 src/views/productionManagement/productionProcess/New.vue                            |   96 
 src/views/productionManagement/productionReporting/components/formDia.vue           |   38 
 38 files changed, 6,645 insertions(+), 1,219 deletions(-)

diff --git a/src/api/basicData/productModel.js b/src/api/basicData/productModel.js
new file mode 100644
index 0000000..f048f9e
--- /dev/null
+++ b/src/api/basicData/productModel.js
@@ -0,0 +1,9 @@
+import request from "@/utils/request.js";
+
+export function productModelList(query) {
+    return request({
+        url: '/basic/product/pageModel',
+        method: 'get',
+        params: query
+    })
+}
\ No newline at end of file
diff --git a/src/api/productionManagement/processRoute.js b/src/api/productionManagement/processRoute.js
new file mode 100644
index 0000000..4d16775
--- /dev/null
+++ b/src/api/productionManagement/processRoute.js
@@ -0,0 +1,34 @@
+// 宸ヨ壓璺嚎椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ
+export function listPage(query) {
+  return request({
+    url: "/processRoute/page",
+    method: "get",
+    params: query,
+  });
+}
+
+export function add(data) {
+  return request({
+    url: "/processRoute",
+    method: "post",
+    data: data,
+  });
+}
+
+export function del(ids) {
+  return request({
+    url: '/processRoute/' + ids,
+    method: 'delete',
+  })
+}
+
+export function update(data) {
+  return request({
+    url: '/processRoute',
+    method: 'put',
+    data: data,
+  })
+}
\ No newline at end of file
diff --git a/src/api/productionManagement/processRouteItem.js b/src/api/productionManagement/processRouteItem.js
new file mode 100644
index 0000000..ad4861c
--- /dev/null
+++ b/src/api/productionManagement/processRouteItem.js
@@ -0,0 +1,19 @@
+// 宸ヨ壓璺嚎椤圭洰椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒楄〃鏌ヨ
+export function findProcessRouteItemList(query) {
+    return request({
+        url: "/processRouteItem/list",
+        method: "get",
+        params: query,
+    });
+}
+
+export function addOrUpdateProcessRouteItem(data) {
+    return request({
+        url: "/processRouteItem",
+        method: "post",
+        data: data,
+    });
+}
\ No newline at end of file
diff --git a/src/api/productionManagement/productProcessRoute.js b/src/api/productionManagement/productProcessRoute.js
new file mode 100644
index 0000000..d756e26
--- /dev/null
+++ b/src/api/productionManagement/productProcessRoute.js
@@ -0,0 +1,28 @@
+// 宸ヨ壓璺嚎椤圭洰椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒楄〃鏌ヨ
+export function findProductProcessRouteItemList(query) {
+    return request({
+        url: "/productProcessRoute/list",
+        method: "get",
+        params: query,
+    });
+}
+
+export function addOrUpdateProductProcessRouteItem(data) {
+    return request({
+        url: "/productProcessRoute/updateRouteItem",
+        method: "post",
+        data: data,
+    });
+}
+
+// 鍒犻櫎瀹㈡埛妗f
+export function deleteRouteItem(ids) {
+    return request({
+        url: '/productProcessRoute/deleteRouteItem',
+        method: 'delete',
+        data: ids
+    })
+}
\ No newline at end of file
diff --git a/src/api/productionManagement/productStructure.js b/src/api/productionManagement/productStructure.js
new file mode 100644
index 0000000..074e27a
--- /dev/null
+++ b/src/api/productionManagement/productStructure.js
@@ -0,0 +1,18 @@
+// 浜у搧缁撴瀯椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ
+export function queryList(id) {
+  return request({
+    url: "/productStructure/listByproductModelId/" + id,
+    method: "get",
+  });
+}
+
+export function add(data) {
+  return request({
+    url: "/productStructure",
+    method: "post",
+    data: data,
+  });
+}
\ No newline at end of file
diff --git a/src/api/productionManagement/productionOrder.js b/src/api/productionManagement/productionOrder.js
index ab3dc06..0c37be1 100644
--- a/src/api/productionManagement/productionOrder.js
+++ b/src/api/productionManagement/productionOrder.js
@@ -9,6 +9,43 @@
     params: query,
   });
 }
+
+
+export function productOrderListPage(query) {
+  return request({
+    url: "/productOrder/page",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鑾峰彇鐐掓満姝e湪宸ヤ綔閲忔暟鎹�
+export function schedulingList(query) {
+  return request({
+    url: "/salesLedger/scheduling/list",
+    method: "get",
+    params: query,
+  });
+}
+
+// 淇濆瓨鐐掓満璁剧疆
+export function addSpeculatTrading(data) {
+  return request({
+    url: "/salesLedger/scheduling/addSpeculatTrading",
+    method: "post",
+    data: data,
+  });
+}
+
+// 淇敼鐐掓満璁剧疆
+export function updateSpeculatTrading(data) {
+  return request({
+    url: "/salesLedger/scheduling/updateSpeculatTrading",
+    method: "post",
+    data: data,
+  });
+}
+
 // 鐢熶骇娲惧伐
 export function productionDispatch(query) {
   return request({
@@ -16,4 +53,38 @@
     method: "post",
     data: query,
   });
+}
+// 鑷姩娲惧伐
+export function productionDispatchList(query) {
+  return request({
+    url: "/salesLedger/scheduling/productionDispatchList",
+    method: "post",
+    data: query,
+  });
+}
+
+// 鏌ヨ鎹熻�楃巼
+export function getLossRate() {
+  return request({
+    url: "/salesLedger/scheduling/loss",
+    method: "get",
+  });
+}
+
+// 鏂板鎹熻�楃巼
+export function addLossRate(data) {
+  return request({
+    url: "/salesLedger/scheduling/addLoss",
+    method: "post",
+    data: data,
+  });
+}
+
+// 淇敼鎹熻�楃巼
+export function updateLossRate(data) {
+  return request({
+    url: "/salesLedger/scheduling/updateLoss",
+    method: "post",
+    data: data,
+  });
 }
\ No newline at end of file
diff --git a/src/api/productionManagement/productionProcess.js b/src/api/productionManagement/productionProcess.js
new file mode 100644
index 0000000..e3cd929
--- /dev/null
+++ b/src/api/productionManagement/productionProcess.js
@@ -0,0 +1,51 @@
+// 宸ュ簭椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ
+export function listPage(query) {
+  return request({
+    url: "/productProcess/listPage",
+    method: "get",
+    params: query,
+  });
+}
+
+export function processList(query) {
+  return request({
+    url: "/productProcess/list",
+    method: "get",
+    params: query,
+  });
+}
+
+export function add(data) {
+  return request({
+    url: "/productProcess",
+    method: "post",
+    data: data,
+  });
+}
+
+export function del(data) {
+  return request({
+    url: '/productProcess/batchDelete',
+    method: 'delete',
+    data: data,
+  })
+}
+
+export function update(data) {
+  return request({
+    url: '/productProcess/update',
+    method: 'put',
+    data: data,
+  })
+}
+
+// 宸ュ簭鏌ヨ
+export function list() {
+    return request({
+        url: "/productProcess/list",
+        method: "get",
+    });
+}
\ No newline at end of file
diff --git a/src/api/productionManagement/productionProductInput.js b/src/api/productionManagement/productionProductInput.js
new file mode 100644
index 0000000..f72cd9b
--- /dev/null
+++ b/src/api/productionManagement/productionProductInput.js
@@ -0,0 +1,11 @@
+// 鐢熶骇鎶曞叆椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ
+export function productionProductInputListPage(query) {
+    return request({
+        url: "/productionProductInput/listPage",
+        method: "get",
+        params: query,
+    });
+}
diff --git a/src/api/productionManagement/productionProductMain.js b/src/api/productionManagement/productionProductMain.js
new file mode 100644
index 0000000..0493f8b
--- /dev/null
+++ b/src/api/productionManagement/productionProductMain.js
@@ -0,0 +1,11 @@
+// 鐢熶骇鎶ュ伐椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ
+export function productionProductMainListPage(query) {
+    return request({
+        url: "/productionProductMain/listPage",
+        method: "get",
+        params: query,
+    });
+}
diff --git a/src/api/productionManagement/productionProductOutput.js b/src/api/productionManagement/productionProductOutput.js
new file mode 100644
index 0000000..10095e9
--- /dev/null
+++ b/src/api/productionManagement/productionProductOutput.js
@@ -0,0 +1,11 @@
+// 鐢熶骇浜у嚭椤甸潰鎺ュ彛
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ
+export function productionProductOutputListPage(query) {
+    return request({
+        url: "/productionProductOutput/listPage",
+        method: "get",
+        params: query,
+    });
+}
diff --git a/src/api/productionManagement/productionReporting.js b/src/api/productionManagement/productionReporting.js
index fdec712..3e29943 100644
--- a/src/api/productionManagement/productionReporting.js
+++ b/src/api/productionManagement/productionReporting.js
@@ -32,4 +32,12 @@
     method: "post",
     data: query,
   });
-}
\ No newline at end of file
+}
+// 鐢熶骇鎶ュ伐-鍒犻櫎
+export function productionReportDelete(query) {
+  return request({
+    url: "/productionProductMain/delete",
+    method: "delete",
+    data: query,
+  });
+}
diff --git a/src/api/productionManagement/workOrder.js b/src/api/productionManagement/workOrder.js
new file mode 100644
index 0000000..bf4b381
--- /dev/null
+++ b/src/api/productionManagement/workOrder.js
@@ -0,0 +1,25 @@
+import request from "@/utils/request";
+
+export function productWorkOrderPage(query) {
+  return request({
+    url: "/productWorkOrder/page",
+    method: "get",
+    params: query,
+  });
+}
+
+export function updateProductWorkOrder(data) {
+  return request({
+    url: "/productWorkOrder/updateProductWorkOrder",
+    method: "post",
+    data: data,
+  });
+}
+
+export function addProductMain(data) {
+  return request({
+    url: "/productionProductMain/addProductMain",
+    method: "post",
+    data: data,
+  });
+}
diff --git a/src/views/basicData/product/ProductSelectDialog.vue b/src/views/basicData/product/ProductSelectDialog.vue
new file mode 100644
index 0000000..246cd88
--- /dev/null
+++ b/src/views/basicData/product/ProductSelectDialog.vue
@@ -0,0 +1,163 @@
+<template>
+  <el-dialog
+      v-model="visible"
+      title="閫夋嫨浜у搧"
+      width="900px"
+      destroy-on-close
+      :close-on-click-modal="false"
+  >
+    <el-form :inline="true" :model="query" class="mb-2">
+      <el-form-item label="浜у搧澶х被">
+        <el-input
+            v-model="query.productName"
+            placeholder="杈撳叆浜у搧澶х被"
+            clearable
+            @keyup.enter="onSearch"
+        />
+      </el-form-item>
+
+      <el-form-item label="鍨嬪彿鍚嶇О">
+        <el-input
+            v-model="query.model"
+            placeholder="杈撳叆鍨嬪彿鍚嶇О"
+            clearable
+            @keyup.enter="onSearch"
+        />
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" @click="onSearch">鎼滅储</el-button>
+        <el-button @click="onReset">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 鍒楄〃 -->
+    <el-table
+        v-loading="loading"
+        :data="tableData"
+        height="420"
+        highlight-current-row
+        row-key="id"
+        @selection-change="handleSelectionChange"
+    >
+      <el-table-column type="selection" width="55" />
+      <el-table-column type="index" label="#" width="60"/>
+      <el-table-column prop="productName" label="浜у搧澶х被" min-width="160"/>
+      <el-table-column prop="model" label="鍨嬪彿鍚嶇О" min-width="200"/>
+      <el-table-column prop="unit" label="鍗曚綅" min-width="160"/>
+    </el-table>
+
+    <div class="mt-3 flex justify-end">
+      <el-pagination
+          background
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="total"
+          v-model:page-size="page.pageSize"
+          v-model:current-page="page.pageNum"
+          :page-sizes="[10, 20, 50, 100]"
+          @size-change="onPageChange"
+          @current-change="onPageChange"
+      />
+    </div>
+
+    <template #footer>
+      <el-button @click="close()">鍙栨秷</el-button>
+      <el-button type="primary" :disabled="multipleSelection.length === 0" @click="onConfirm">
+        纭畾
+      </el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import {computed, onMounted, reactive, ref, watch} from "vue";
+import {ElMessage} from "element-plus";
+import {productModelList} from '@/api/basicData/productModel'
+
+export type ProductRow = {
+  id: number;
+  productName: string;
+  model: string;
+  unit?: string;
+};
+
+const props = defineProps<{
+  modelValue: boolean;
+}>();
+
+const emit = defineEmits(['update:modelValue', 'confirm']);
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (v) => emit("update:modelValue", v),
+});
+
+const query = reactive({
+  productName: "",
+  model: "",
+});
+
+const page = reactive({
+  pageNum: 1,
+  pageSize: 10,
+});
+
+const loading = ref(false);
+const tableData = ref<ProductRow[]>([]);
+const total = ref(0);
+const multipleSelection = ref<ProductRow[]>([])
+
+function close() {
+  visible.value = false;
+}
+
+const handleSelectionChange = (val: ProductRow[]) => {
+  multipleSelection.value = val
+}
+
+function onSearch() {
+  page.pageNum = 1;
+  loadData();
+}
+
+function onReset() {
+  query.productName = "";
+  query.model = "";
+  page.pageNum = 1;
+  loadData();
+}
+
+function onPageChange() {
+  loadData();
+}
+
+function onConfirm() {
+  if (multipleSelection.value.length === 0) {
+    ElMessage.warning("璇烽�夋嫨涓�鏉′骇鍝�");
+    return;
+  }
+  emit("confirm", multipleSelection.value);
+  close();
+}
+
+async function loadData() {
+  loading.value = true;
+  try {
+    multipleSelection.value = []; // 缈婚〉/鎼滅储鍚庢竻绌洪�夋嫨鏇寸鍚堥鏈�
+    const res = await productModelList({
+      productName: query.productName.trim(),
+      model: query.model.trim(),
+      current: page.pageNum,
+      size: page.pageSize,
+    });
+    tableData.value = res.records;
+    total.value = res.total;
+  } finally {
+    loading.value = false;
+  }
+}
+
+onMounted(() => {
+  loadData()
+})
+</script>
diff --git a/src/views/productManagement/productIdentifier/index.vue b/src/views/productManagement/productIdentifier/index.vue
index 59d8f90..519f745 100644
--- a/src/views/productManagement/productIdentifier/index.vue
+++ b/src/views/productManagement/productIdentifier/index.vue
@@ -1,159 +1,231 @@
 <template>
   <div class="app-container">
     <el-card class="box-card">
-             <!-- 鎼滅储鍖哄煙 -->
-       <el-row :gutter="20" class="search-row">
-         <el-col :span="6">
-           <el-input
-             v-model="searchForm.productName"
-             placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
-             clearable
-             @keyup.enter="handleSearch"
-           >
-             <template #prefix>
-               <el-icon><Search /></el-icon>
-             </template>
-           </el-input>
-         </el-col>
-         <el-col :span="6">
-           <el-select v-model="searchForm.identifierType" placeholder="璇烽�夋嫨鏍囪瘑绫诲瀷" clearable>
-             <el-option label="浜岀淮鐮�" value="浜岀淮鐮�"></el-option>
-             <el-option label="闃蹭吉鐮�" value="闃蹭吉鐮�"></el-option>
-           </el-select>
-         </el-col>
-         <el-col :span="6">
-           <el-select v-model="searchForm.status" placeholder="璇烽�夋嫨鐘舵��" clearable>
-             <el-option label="宸茬敓鎴�" value="宸茬敓鎴�"></el-option>
-             <el-option label="宸插垎閰�" value="宸插垎閰�"></el-option>
-             <el-option label="宸蹭娇鐢�" value="宸蹭娇鐢�"></el-option>
-             <el-option label="宸蹭綔搴�" value="宸蹭綔搴�"></el-option>
-           </el-select>
-         </el-col>
-         <el-col :span="6">
-           <el-button type="primary" @click="handleSearch">鎼滅储</el-button>
-           <el-button @click="resetSearch">閲嶇疆</el-button>
-           <el-button style="float: right;" type="primary" @click="handleAdd">
-             鏂板鏍囪瘑
-           </el-button>
-         </el-col>
-       </el-row>
-
+      <!-- 鎼滅储鍖哄煙 -->
+      <el-row :gutter="20"
+              class="search-row">
+        <el-col :span="6">
+          <el-input v-model="searchForm.productName"
+                    placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
+                    clearable
+                    @keyup.enter="handleSearch">
+            <template #prefix>
+              <el-icon>
+                <Search />
+              </el-icon>
+            </template>
+          </el-input>
+        </el-col>
+        <el-col :span="6">
+          <el-select v-model="searchForm.identifierType"
+                     placeholder="璇烽�夋嫨鏍囪瘑绫诲瀷"
+                     clearable>
+            <el-option label="浜岀淮鐮�"
+                       value="浜岀淮鐮�"></el-option>
+            <el-option label="闃蹭吉鐮�"
+                       value="闃蹭吉鐮�"></el-option>
+          </el-select>
+        </el-col>
+        <el-col :span="6">
+          <el-select v-model="searchForm.status"
+                     placeholder="璇烽�夋嫨鐘舵��"
+                     clearable>
+            <el-option label="宸茬敓鎴�"
+                       value="宸茬敓鎴�"></el-option>
+            <el-option label="宸插垎閰�"
+                       value="宸插垎閰�"></el-option>
+            <el-option label="宸蹭娇鐢�"
+                       value="宸蹭娇鐢�"></el-option>
+            <el-option label="宸蹭綔搴�"
+                       value="宸蹭綔搴�"></el-option>
+          </el-select>
+        </el-col>
+        <el-col :span="6">
+          <el-button type="primary"
+                     @click="handleSearch">鎼滅储</el-button>
+          <el-button @click="resetSearch">閲嶇疆</el-button>
+          <el-button style="float: right;"
+                     type="primary"
+                     @click="handleAdd">
+            鏂板鏍囪瘑
+          </el-button>
+        </el-col>
+      </el-row>
       <!-- 浜у搧鏍囪瘑鍒楄〃 -->
-      <el-table
-        :data="filteredList"
-        style="width: 100%"
-        v-loading="loading"
-        border
-        stripe
-        height="calc(100vh - 22em)"
-      >
-        <el-table-column prop="id" label="ID" width="80" align="center"/>
-        <el-table-column prop="productName" label="浜у搧鍚嶇О" width="150" />
-        <el-table-column prop="productCode" label="浜у搧缂栫爜" width="120" />
-        <el-table-column prop="batchNo" label="鎵规鍙�" width="120" />
-        <el-table-column prop="identifierType" label="鏍囪瘑绫诲瀷" width="100">
+      <el-table :data="filteredList"
+                style="width: 100%"
+                v-loading="loading"
+                border
+                stripe
+                height="calc(100vh - 22em)">
+        <el-table-column prop="id"
+                         label="ID"
+                         width="80"
+                         align="center" />
+        <el-table-column prop="productName"
+                         label="浜у搧鍚嶇О"
+                         width="150" />
+        <el-table-column prop="productCode"
+                         label="浜у搧缂栫爜"
+                         width="120" />
+        <el-table-column prop="batchNo"
+                         label="鎵规鍙�"
+                         width="120" />
+        <el-table-column prop="identifierType"
+                         label="鏍囪瘑绫诲瀷"
+                         width="100">
           <template #default="scope">
             <el-tag :type="getIdentifierTypeType(scope.row.identifierType)">
               {{ scope.row.identifierType }}
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="identifierCode" label="鏍囪瘑鐮�" />
-        <el-table-column prop="status" label="鐘舵��" width="100">
+        <el-table-column prop="identifierCode"
+                         label="鏍囪瘑鐮�" />
+        <el-table-column prop="status"
+                         label="鐘舵��"
+                         width="100">
           <template #default="scope">
             <el-tag :type="getStatusType(scope.row.status)">
               {{ scope.row.status }}
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column prop="generateTime" label="鐢熸垚鏃堕棿" width="160" />
-        <el-table-column label="鎿嶄綔" fixed="right" align="center" width="280">
+        <el-table-column prop="generateTime"
+                         label="鐢熸垚鏃堕棿"
+                         width="160" />
+        <el-table-column label="鎿嶄綔"
+                         fixed="right"
+                         align="center"
+                         width="280">
           <template #default="scope">
-            <el-button link type="primary" @click="handleView(scope.row)">鏌ョ湅</el-button>
-            <el-button link type="primary" @click="handleEdit(scope.row)">缂栬緫</el-button>
-            <el-button link type="success" @click="generateQRCode(scope.row)">鐢熸垚浜岀淮鐮�</el-button>
-            <el-button link type="primary" @click="handleExport(scope.row)">瀵煎嚭</el-button>
-            <el-button link type="primary" @click="handleReassign(scope.row)" v-if="scope.row.status === '宸插垎閰�'">閲嶆柊鍒嗛厤</el-button>
-            <el-button link type="danger" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+            <el-button link
+                       type="primary"
+                       @click="handleView(scope.row)">鏌ョ湅</el-button>
+            <el-button link
+                       type="primary"
+                       @click="handleEdit(scope.row)">缂栬緫</el-button>
+            <el-button link
+                       type="success"
+                       @click="generateQRCode(scope.row)">鐢熸垚浜岀淮鐮�</el-button>
+            <el-button link
+                       type="primary"
+                       @click="handleExport(scope.row)">瀵煎嚭</el-button>
+            <el-button link
+                       type="primary"
+                       @click="handleReassign(scope.row)"
+                       v-if="scope.row.status === '宸插垎閰�'">閲嶆柊鍒嗛厤</el-button>
+            <el-button link
+                       type="danger"
+                       @click="handleDelete(scope.row)">鍒犻櫎</el-button>
           </template>
         </el-table-column>
       </el-table>
-
       <!-- 鍒嗛〉 -->
-      <pagination
-        :total="pagination.total"
-        layout="total, sizes, prev, pager, next, jumper"
-        :page="pagination.currentPage"
-        :limit="pagination.pageSize"
-        @pagination="handleCurrentChange"
-      />
+      <pagination :total="pagination.total"
+                  layout="total, sizes, prev, pager, next, jumper"
+                  :page="pagination.currentPage"
+                  :limit="pagination.pageSize"
+                  @pagination="handleCurrentChange" />
     </el-card>
-
     <!-- 鏂板/缂栬緫瀵硅瘽妗� -->
-    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="700px">
-      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
+    <el-dialog v-model="dialogVisible"
+               :title="dialogTitle"
+               width="700px">
+      <el-form :model="form"
+               :rules="rules"
+               ref="formRef"
+               label-width="100px">
         <el-row :gutter="20">
           <el-col :span="12">
-            <el-form-item label="浜у搧鍚嶇О" prop="productName">
-              <el-input v-model="form.productName" placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"></el-input>
+            <el-form-item label="浜у搧鍚嶇О"
+                          prop="productName">
+              <el-input v-model="form.productName"
+                        placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"></el-input>
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="浜у搧缂栫爜" prop="productCode">
-              <el-input v-model="form.productCode" placeholder="璇疯緭鍏ヤ骇鍝佺紪鐮�"></el-input>
+            <el-form-item label="浜у搧缂栫爜"
+                          prop="productCode">
+              <el-input v-model="form.productCode"
+                        placeholder="璇疯緭鍏ヤ骇鍝佺紪鐮�"></el-input>
             </el-form-item>
           </el-col>
         </el-row>
         <el-row :gutter="20">
           <el-col :span="12">
-            <el-form-item label="鎵规鍙�" prop="batchNo">
-              <el-input v-model="form.batchNo" placeholder="璇疯緭鍏ユ壒娆″彿"></el-input>
+            <el-form-item label="鎵规鍙�"
+                          prop="batchNo">
+              <el-input v-model="form.batchNo"
+                        placeholder="璇疯緭鍏ユ壒娆″彿"></el-input>
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="鏍囪瘑绫诲瀷" prop="identifierType">
-              <el-select v-model="form.identifierType" placeholder="璇烽�夋嫨鏍囪瘑绫诲瀷" style="width: 100%">
-                <el-option label="浜岀淮鐮�" value="浜岀淮鐮�"></el-option>
-                <el-option label="闃蹭吉鐮�" value="闃蹭吉鐮�"></el-option>
+            <el-form-item label="鏍囪瘑绫诲瀷"
+                          prop="identifierType">
+              <el-select v-model="form.identifierType"
+                         placeholder="璇烽�夋嫨鏍囪瘑绫诲瀷"
+                         style="width: 100%">
+                <el-option label="浜岀淮鐮�"
+                           value="浜岀淮鐮�"></el-option>
+                <el-option label="闃蹭吉鐮�"
+                           value="闃蹭吉鐮�"></el-option>
               </el-select>
             </el-form-item>
           </el-col>
         </el-row>
         <el-row :gutter="20">
           <el-col :span="12">
-            <el-form-item label="鐢熸垚鏁伴噺" prop="quantity">
-              <el-input-number v-model="form.quantity" :min="1" :max="10000" style="width: 100%"></el-input-number>
+            <el-form-item label="鐢熸垚鏁伴噺"
+                          prop="quantity">
+              <el-input-number v-model="form.quantity"
+                               :min="1"
+                               :max="10000"
+                               style="width: 100%"></el-input-number>
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="鐘舵��" prop="status">
-              <el-select v-model="form.status" placeholder="璇烽�夋嫨鐘舵��" style="width: 100%">
-                <el-option label="宸茬敓鎴�" value="宸茬敓鎴�"></el-option>
-                <el-option label="宸插垎閰�" value="宸插垎閰�"></el-option>
-                <el-option label="宸蹭娇鐢�" value="宸蹭娇鐢�"></el-option>
-                <el-option label="宸蹭綔搴�" value="宸蹭綔搴�"></el-option>
+            <el-form-item label="鐘舵��"
+                          prop="status">
+              <el-select v-model="form.status"
+                         placeholder="璇烽�夋嫨鐘舵��"
+                         style="width: 100%">
+                <el-option label="宸茬敓鎴�"
+                           value="宸茬敓鎴�"></el-option>
+                <el-option label="宸插垎閰�"
+                           value="宸插垎閰�"></el-option>
+                <el-option label="宸蹭娇鐢�"
+                           value="宸蹭娇鐢�"></el-option>
+                <el-option label="宸蹭綔搴�"
+                           value="宸蹭綔搴�"></el-option>
               </el-select>
             </el-form-item>
           </el-col>
         </el-row>
         <el-row :gutter="20">
           <el-col :span="24">
-            <el-form-item label="澶囨敞" prop="remark">
-              <el-input type="textarea" v-model="form.remark" placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�" rows="3"></el-input>
+            <el-form-item label="澶囨敞"
+                          prop="remark">
+              <el-input type="textarea"
+                        v-model="form.remark"
+                        placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�"
+                        rows="3"></el-input>
             </el-form-item>
           </el-col>
         </el-row>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="handleSubmit">纭� 瀹�</el-button>
           <el-button @click="dialogVisible = false">鍙� 娑�</el-button>
-          <el-button type="primary" @click="handleSubmit">纭� 瀹�</el-button>
         </div>
       </template>
     </el-dialog>
-
     <!-- 鏍囪瘑鐢熸垚瀵硅瘽妗� -->
-    <el-dialog v-model="generateDialogVisible" title="鏍囪瘑鐢熸垚" width="500px">
+    <el-dialog v-model="generateDialogVisible"
+               title="鏍囪瘑鐢熸垚"
+               width="500px">
       <el-form label-width="100px">
         <el-form-item label="浜у搧鍚嶇О">
           <span>{{ currentProduct.productName }}</span>
@@ -167,30 +239,45 @@
         <el-form-item label="鏍囪瘑绫诲瀷">
           <span>{{ currentProduct.identifierType }}</span>
         </el-form-item>
-        <el-form-item label="鐢熸垚鏁伴噺" prop="generateQuantity">
-          <el-input-number v-model="generateQuantity" :min="1" :max="10000" style="width: 100%"></el-input-number>
+        <el-form-item label="鐢熸垚鏁伴噺"
+                      prop="generateQuantity">
+          <el-input-number v-model="generateQuantity"
+                           :min="1"
+                           :max="10000"
+                           style="width: 100%"></el-input-number>
         </el-form-item>
-        <el-form-item label="缂栫爜瑙勫垯" prop="codeRule">
-          <el-select v-model="codeRule" placeholder="璇烽�夋嫨缂栫爜瑙勫垯" style="width: 100%">
-            <el-option label="浜у搧缂栫爜+鎵规鍙�+搴忓彿" value="浜у搧缂栫爜+鎵规鍙�+搴忓彿"></el-option>
-            <el-option label="鏃堕棿鎴�+闅忔満鏁�" value="鏃堕棿鎴�+闅忔満鏁�"></el-option>
-            <el-option label="鑷畾涔夎鍒�" value="鑷畾涔夎鍒�"></el-option>
+        <el-form-item label="缂栫爜瑙勫垯"
+                      prop="codeRule">
+          <el-select v-model="codeRule"
+                     placeholder="璇烽�夋嫨缂栫爜瑙勫垯"
+                     style="width: 100%">
+            <el-option label="浜у搧缂栫爜+鎵规鍙�+搴忓彿"
+                       value="浜у搧缂栫爜+鎵规鍙�+搴忓彿"></el-option>
+            <el-option label="鏃堕棿鎴�+闅忔満鏁�"
+                       value="鏃堕棿鎴�+闅忔満鏁�"></el-option>
+            <el-option label="鑷畾涔夎鍒�"
+                       value="鑷畾涔夎鍒�"></el-option>
           </el-select>
         </el-form-item>
-        <el-form-item label="鑷畾涔夊墠缂�" prop="customPrefix" v-if="codeRule === '鑷畾涔夎鍒�'">
-          <el-input v-model="customPrefix" placeholder="璇疯緭鍏ヨ嚜瀹氫箟鍓嶇紑"></el-input>
+        <el-form-item label="鑷畾涔夊墠缂�"
+                      prop="customPrefix"
+                      v-if="codeRule === '鑷畾涔夎鍒�'">
+          <el-input v-model="customPrefix"
+                    placeholder="璇疯緭鍏ヨ嚜瀹氫箟鍓嶇紑"></el-input>
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="generateIdentifiers">鐢� 鎴�</el-button>
           <el-button @click="generateDialogVisible = false">鍙� 娑�</el-button>
-          <el-button type="primary" @click="generateIdentifiers">鐢� 鎴�</el-button>
         </div>
       </template>
     </el-dialog>
-
     <!-- 閲嶆柊鍒嗛厤瀵硅瘽妗� -->
-    <el-dialog v-model="reassignDialogVisible" title="閲嶆柊鍒嗛厤鏍囪瘑" width="500px">
+    <el-dialog v-model="reassignDialogVisible"
+               title="閲嶆柊鍒嗛厤鏍囪瘑"
+               width="500px">
       <el-form label-width="100px">
         <el-form-item label="浜у搧鍚嶇О">
           <span>{{ currentProduct.productName }}</span>
@@ -198,26 +285,38 @@
         <el-form-item label="鏍囪瘑鐮�">
           <span>{{ currentProduct.identifierCode }}</span>
         </el-form-item>
-        <el-form-item label="鏂版壒娆″彿" prop="newBatchNo">
-          <el-input v-model="newBatchNo" placeholder="璇疯緭鍏ユ柊鎵规鍙�"></el-input>
+        <el-form-item label="鏂版壒娆″彿"
+                      prop="newBatchNo">
+          <el-input v-model="newBatchNo"
+                    placeholder="璇疯緭鍏ユ柊鎵规鍙�"></el-input>
         </el-form-item>
-        <el-form-item label="鍒嗛厤鍘熷洜" prop="reassignReason">
-          <el-input type="textarea" v-model="reassignReason" rows="3" placeholder="璇疯緭鍏ラ噸鏂板垎閰嶅師鍥�"></el-input>
+        <el-form-item label="鍒嗛厤鍘熷洜"
+                      prop="reassignReason">
+          <el-input type="textarea"
+                    v-model="reassignReason"
+                    rows="3"
+                    placeholder="璇疯緭鍏ラ噸鏂板垎閰嶅師鍥�"></el-input>
         </el-form-item>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="saveReassign">纭� 瀹�</el-button>
           <el-button @click="reassignDialogVisible = false">鍙� 娑�</el-button>
-          <el-button type="primary" @click="saveReassign">纭� 瀹�</el-button>
         </div>
       </template>
     </el-dialog>
-
     <!-- 浜岀淮鐮侀瑙堝璇濇 -->
-    <el-dialog v-model="qrCodeDialogVisible" title="浜岀淮鐮侀瑙�" width="500px" center>
+    <el-dialog v-model="qrCodeDialogVisible"
+               title="浜岀淮鐮侀瑙�"
+               width="500px"
+               center>
       <div class="qr-preview-container">
-        <div v-if="qrCodeUrl" class="qr-image-container">
-          <img :src="qrCodeUrl" alt="浜岀淮鐮�" class="qr-image" />
+        <div v-if="qrCodeUrl"
+             class="qr-image-container">
+          <img :src="qrCodeUrl"
+               alt="浜岀淮鐮�"
+               class="qr-image" />
           <div class="qr-info">
             <p><strong>浜у搧鍚嶇О锛�</strong>{{ currentQRProduct.productName }}</p>
             <p><strong>浜у搧缂栫爜锛�</strong>{{ currentQRProduct.productCode }}</p>
@@ -226,29 +325,27 @@
             <p><strong>鏍囪瘑绫诲瀷锛�</strong>{{ currentQRProduct.identifierType }}</p>
           </div>
         </div>
-        <div v-else class="qr-loading">
-          <el-icon class="is-loading"><Loading /></el-icon>
+        <div v-else
+             class="qr-loading">
+          <el-icon class="is-loading">
+            <Loading />
+          </el-icon>
           <p>姝e湪鐢熸垚浜岀淮鐮�...</p>
         </div>
       </div>
-      
       <template #footer>
         <div class="dialog-footer">
           <el-button @click="qrCodeDialogVisible = false">鍏抽棴</el-button>
-          <el-button 
-            v-if="qrCodeUrl" 
-            type="primary" 
-            @click="copyQRContent" 
-            icon="CopyDocument"
-          >
+          <el-button v-if="qrCodeUrl"
+                     type="primary"
+                     @click="copyQRContent"
+                     icon="CopyDocument">
             澶嶅埗鍐呭
           </el-button>
-          <el-button 
-            v-if="qrCodeUrl" 
-            type="success" 
-            @click="downloadQRCode" 
-            icon="Download"
-          >
+          <el-button v-if="qrCodeUrl"
+                     type="success"
+                     @click="downloadQRCode"
+                     icon="Download">
             涓嬭浇浜岀淮鐮�
           </el-button>
         </div>
@@ -258,451 +355,465 @@
 </template>
 
 <script setup>
-import { ref, reactive, computed } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import { Plus, Search, Loading, Download } from '@element-plus/icons-vue'
-import Pagination from '@/components/PIMTable/Pagination.vue'
-import QRCode from 'qrcode'
+  import { ref, reactive, computed } from "vue";
+  import { ElMessage, ElMessageBox } from "element-plus";
+  import { Plus, Search, Loading, Download } from "@element-plus/icons-vue";
+  import Pagination from "@/components/PIMTable/Pagination.vue";
+  import QRCode from "qrcode";
 
-// 鍝嶅簲寮忔暟鎹�
-const loading = ref(false)
-const searchForm = reactive({
-  productName: '',
-  identifierType: '',
-  status: ''
-})
+  // 鍝嶅簲寮忔暟鎹�
+  const loading = ref(false);
+  const searchForm = reactive({
+    productName: "",
+    identifierType: "",
+    status: "",
+  });
 
-const identifierList = ref([
-  {
-    id: 1,
-    productName: '宸ヤ笟浼犳劅鍣ˋ鍨�',
-    productCode: 'SENSOR001',
-    batchNo: 'B202312001',
-    identifierType: '浜岀淮鐮�',
-    identifierCode: 'QR_SENSOR001_B202312001_001',
-    status: '宸插垎閰�',
-    generateTime: '2023-12-01 10:00:00',
-    remark: '閲嶈浜у搧鏍囪瘑'
-  },
-  {
-    id: 2,
-    productName: '鎺у埗闈㈡澘B鍨�',
-    productCode: 'PANEL002',
-    batchNo: 'B202312002',
-    identifierType: '闃蹭吉鐮�',
-    identifierCode: 'SEC_PANEL002_B202312002_001',
-    status: '宸茬敓鎴�',
-    generateTime: '2023-12-02 14:30:00',
-    remark: '甯歌浜у搧鏍囪瘑'
-  },
-  {
-    id: 3,
-    productName: '鏁版嵁閲囬泦鍣–鍨�',
-    productCode: 'COLLECTOR003',
-    batchNo: 'B202312003',
-    identifierType: '闃蹭吉鐮�',
-    identifierCode: 'SEC_COLLECTOR003_B202312003_001',
-    status: '宸蹭娇鐢�',
-    generateTime: '2023-12-03 09:15:00',
-    remark: '娴嬭瘯浜у搧鏍囪瘑'
-  }
-])
+  const identifierList = ref([
+    {
+      id: 1,
+      productName: "宸ヤ笟浼犳劅鍣ˋ鍨�",
+      productCode: "SENSOR001",
+      batchNo: "B202312001",
+      identifierType: "浜岀淮鐮�",
+      identifierCode: "QR_SENSOR001_B202312001_001",
+      status: "宸插垎閰�",
+      generateTime: "2023-12-01 10:00:00",
+      remark: "閲嶈浜у搧鏍囪瘑",
+    },
+    {
+      id: 2,
+      productName: "鎺у埗闈㈡澘B鍨�",
+      productCode: "PANEL002",
+      batchNo: "B202312002",
+      identifierType: "闃蹭吉鐮�",
+      identifierCode: "SEC_PANEL002_B202312002_001",
+      status: "宸茬敓鎴�",
+      generateTime: "2023-12-02 14:30:00",
+      remark: "甯歌浜у搧鏍囪瘑",
+    },
+    {
+      id: 3,
+      productName: "鏁版嵁閲囬泦鍣–鍨�",
+      productCode: "COLLECTOR003",
+      batchNo: "B202312003",
+      identifierType: "闃蹭吉鐮�",
+      identifierCode: "SEC_COLLECTOR003_B202312003_001",
+      status: "宸蹭娇鐢�",
+      generateTime: "2023-12-03 09:15:00",
+      remark: "娴嬭瘯浜у搧鏍囪瘑",
+    },
+  ]);
 
-const pagination = reactive({
-  total: 3,
-  currentPage: 1,
-  pageSize: 10
-})
+  const pagination = reactive({
+    total: 3,
+    currentPage: 1,
+    pageSize: 10,
+  });
 
-const dialogVisible = ref(false)
-const dialogTitle = ref('鏂板鏍囪瘑')
-const form = reactive({
-  productName: '',
-  productCode: '',
-  batchNo: '',
-  identifierType: '',
-  quantity: 1,
-  status: '宸茬敓鎴�',
-  remark: ''
-})
+  const dialogVisible = ref(false);
+  const dialogTitle = ref("鏂板鏍囪瘑");
+  const form = reactive({
+    productName: "",
+    productCode: "",
+    batchNo: "",
+    identifierType: "",
+    quantity: 1,
+    status: "宸茬敓鎴�",
+    remark: "",
+  });
 
-const rules = {
-  productName: [{ required: true, message: '璇疯緭鍏ヤ骇鍝佸悕绉�', trigger: 'blur' }],
-  productCode: [{ required: true, message: '璇疯緭鍏ヤ骇鍝佺紪鐮�', trigger: 'blur' }],
-  batchNo: [{ required: true, message: '璇疯緭鍏ユ壒娆″彿', trigger: 'blur' }],
-  identifierType: [{ required: true, message: '璇烽�夋嫨鏍囪瘑绫诲瀷', trigger: 'change' }],
-  quantity: [{ required: true, message: '璇疯緭鍏ョ敓鎴愭暟閲�', trigger: 'blur' }],
-  status: [{ required: true, message: '璇烽�夋嫨鐘舵��', trigger: 'change' }]
-}
+  const rules = {
+    productName: [{ required: true, message: "璇疯緭鍏ヤ骇鍝佸悕绉�", trigger: "blur" }],
+    productCode: [{ required: true, message: "璇疯緭鍏ヤ骇鍝佺紪鐮�", trigger: "blur" }],
+    batchNo: [{ required: true, message: "璇疯緭鍏ユ壒娆″彿", trigger: "blur" }],
+    identifierType: [
+      { required: true, message: "璇烽�夋嫨鏍囪瘑绫诲瀷", trigger: "change" },
+    ],
+    quantity: [{ required: true, message: "璇疯緭鍏ョ敓鎴愭暟閲�", trigger: "blur" }],
+    status: [{ required: true, message: "璇烽�夋嫨鐘舵��", trigger: "change" }],
+  };
 
-const isEdit = ref(false)
-const editId = ref(null)
-const generateDialogVisible = ref(false)
-const reassignDialogVisible = ref(false)
-const currentProduct = ref({})
-const generateQuantity = ref(1)
-const codeRule = ref('')
-const customPrefix = ref('')
-const newBatchNo = ref('')
-const reassignReason = ref('')
-const formRef = ref()
+  const isEdit = ref(false);
+  const editId = ref(null);
+  const generateDialogVisible = ref(false);
+  const reassignDialogVisible = ref(false);
+  const currentProduct = ref({});
+  const generateQuantity = ref(1);
+  const codeRule = ref("");
+  const customPrefix = ref("");
+  const newBatchNo = ref("");
+  const reassignReason = ref("");
+  const formRef = ref();
 
-// 浜岀淮鐮佺浉鍏冲彉閲�
-const qrCodeDialogVisible = ref(false)
-const qrCodeUrl = ref('')
-const currentQRProduct = ref({})
+  // 浜岀淮鐮佺浉鍏冲彉閲�
+  const qrCodeDialogVisible = ref(false);
+  const qrCodeUrl = ref("");
+  const currentQRProduct = ref({});
 
-// 璁$畻灞炴��
-const filteredList = computed(() => {
-  let list = identifierList.value
-  if (searchForm.productName) {
-    list = list.filter(item => item.productName.includes(searchForm.productName))
-  }
-  if (searchForm.identifierType) {
-    list = list.filter(item => item.identifierType === searchForm.identifierType)
-  }
-  if (searchForm.status) {
-    list = list.filter(item => item.status === searchForm.status)
-  }
-  return list
-})
+  // 璁$畻灞炴��
+  const filteredList = computed(() => {
+    let list = identifierList.value;
+    if (searchForm.productName) {
+      list = list.filter(item =>
+        item.productName.includes(searchForm.productName)
+      );
+    }
+    if (searchForm.identifierType) {
+      list = list.filter(
+        item => item.identifierType === searchForm.identifierType
+      );
+    }
+    if (searchForm.status) {
+      list = list.filter(item => item.status === searchForm.status);
+    }
+    return list;
+  });
 
-// 鏂规硶
-const getIdentifierTypeType = (type) => {
-  const typeMap = {
-    '浜岀淮鐮�': 'success',
-    '闃蹭吉鐮�': 'warning'
-  }
-  return typeMap[type] || 'info'
-}
+  // 鏂规硶
+  const getIdentifierTypeType = type => {
+    const typeMap = {
+      浜岀淮鐮�: "success",
+      闃蹭吉鐮�: "warning",
+    };
+    return typeMap[type] || "info";
+  };
 
-const getStatusType = (status) => {
-  const statusMap = {
-    '宸茬敓鎴�': 'info',
-    '宸插垎閰�': 'primary',
-    '宸蹭娇鐢�': 'success',
-    '宸蹭綔搴�': 'danger'
-  }
-  return statusMap[status] || 'info'
-}
+  const getStatusType = status => {
+    const statusMap = {
+      宸茬敓鎴�: "info",
+      宸插垎閰�: "primary",
+      宸蹭娇鐢�: "success",
+      宸蹭綔搴�: "danger",
+    };
+    return statusMap[status] || "info";
+  };
 
-const handleSearch = () => {
-  // 鎼滅储閫昏緫宸插湪computed涓鐞�
-}
+  const handleSearch = () => {
+    // 鎼滅储閫昏緫宸插湪computed涓鐞�
+  };
 
-const resetSearch = () => {
-  searchForm.productName = ''
-  searchForm.identifierType = ''
-  searchForm.status = ''
-}
+  const resetSearch = () => {
+    searchForm.productName = "";
+    searchForm.identifierType = "";
+    searchForm.status = "";
+  };
 
-const handleAdd = () => {
-  dialogTitle.value = '鏂板鏍囪瘑'
-  isEdit.value = false
-  form.productName = ''
-  form.productCode = ''
-  form.batchNo = ''
-  form.identifierType = ''
-  form.quantity = 1
-  form.status = '宸茬敓鎴�'
-  form.remark = ''
-  dialogVisible.value = true
-}
+  const handleAdd = () => {
+    dialogTitle.value = "鏂板鏍囪瘑";
+    isEdit.value = false;
+    form.productName = "";
+    form.productCode = "";
+    form.batchNo = "";
+    form.identifierType = "";
+    form.quantity = 1;
+    form.status = "宸茬敓鎴�";
+    form.remark = "";
+    dialogVisible.value = true;
+  };
 
-const handleView = (row) => {
-  // 鏌ョ湅鏍囪瘑璇︽儏
-  ElMessage.info('鏌ョ湅鏍囪瘑璇︽儏鍔熻兘寰呭疄鐜�')
-}
+  const handleView = row => {
+    // 鏌ョ湅鏍囪瘑璇︽儏
+    ElMessage.info("鏌ョ湅鏍囪瘑璇︽儏鍔熻兘寰呭疄鐜�");
+  };
 
-const handleEdit = (row) => {
-  dialogTitle.value = '缂栬緫鏍囪瘑'
-  isEdit.value = true
-  editId.value = row.id
-  Object.assign(form, row)
-  dialogVisible.value = true
-}
+  const handleEdit = row => {
+    dialogTitle.value = "缂栬緫鏍囪瘑";
+    isEdit.value = true;
+    editId.value = row.id;
+    Object.assign(form, row);
+    dialogVisible.value = true;
+  };
 
-const handleExport = (row) => {
-  // 瀵煎嚭鏍囪瘑
-  ElMessage.success(`宸插鍑烘爣璇�: ${row.identifierCode}`)
-}
+  const handleExport = row => {
+    // 瀵煎嚭鏍囪瘑
+    ElMessage.success(`宸插鍑烘爣璇�: ${row.identifierCode}`);
+  };
 
-const handleReassign = (row) => {
-  currentProduct.value = row
-  newBatchNo.value = ''
-  reassignReason.value = ''
-  reassignDialogVisible.value = true
-}
+  const handleReassign = row => {
+    currentProduct.value = row;
+    newBatchNo.value = "";
+    reassignReason.value = "";
+    reassignDialogVisible.value = true;
+  };
 
-const handleDelete = (row) => {
-  ElMessageBox.confirm('纭鍒犻櫎璇ユ爣璇嗗悧锛�', '鎻愮ず', {
-    confirmButtonText: '纭畾',
-    cancelButtonText: '鍙栨秷',
-    type: 'warning'
-  }).then(() => {
-    const index = identifierList.value.findIndex(item => item.id === row.id)
+  const handleDelete = row => {
+    ElMessageBox.confirm("纭鍒犻櫎璇ユ爣璇嗗悧锛�", "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    }).then(() => {
+      const index = identifierList.value.findIndex(item => item.id === row.id);
+      if (index > -1) {
+        identifierList.value.splice(index, 1);
+        pagination.total--;
+        ElMessage.success("鍒犻櫎鎴愬姛");
+      }
+    });
+  };
+
+  // 鐢熸垚浜岀淮鐮�
+  const generateQRCode = async row => {
+    try {
+      // 妫�鏌ュ繀瑕佸瓧娈�
+      if (!row.productName || !row.productCode || !row.batchNo) {
+        ElMessage.warning("浜у搧淇℃伅涓嶅畬鏁达紝鏃犳硶鐢熸垚浜岀淮鐮�");
+        return;
+      }
+
+      currentQRProduct.value = row;
+      qrCodeUrl.value = "";
+      qrCodeDialogVisible.value = true;
+
+      // 鏋勫缓浜岀淮鐮佸唴瀹�
+      let qrContent = "";
+      if (row.identifierType === "浜岀淮鐮�") {
+        qrContent = `${row.productName}|${row.productCode}|${row.batchNo}|${row.identifierCode}`;
+      } else if (row.identifierType === "闃蹭吉鐮�") {
+        // 闃蹭吉鐮佹牸寮忥細SEC_浜у搧缂栫爜_鎵规鍙穇鏃堕棿鎴砡闅忔満鏁�
+        const timestamp = Date.now();
+        const random = Math.random().toString(36).substr(2, 8);
+        qrContent = `SEC_${row.productCode}_${row.batchNo}_${timestamp}_${random}`;
+      }
+
+      // 鐢熸垚浜岀淮鐮�
+      qrCodeUrl.value = await QRCode.toDataURL(qrContent, {
+        width: 256,
+        margin: 2,
+        color: {
+          dark: "#000000",
+          light: "#FFFFFF",
+        },
+        errorCorrectionLevel: row.identifierType === "闃蹭吉鐮�" ? "H" : "M",
+      });
+
+      ElMessage.success("浜岀淮鐮佺敓鎴愭垚鍔燂紒");
+    } catch (error) {
+      console.error("鐢熸垚浜岀淮鐮佸け璐�:", error);
+      ElMessage.error("鐢熸垚浜岀淮鐮佸け璐ワ細" + error.message);
+      qrCodeDialogVisible.value = false;
+    }
+  };
+
+  // 涓嬭浇浜岀淮鐮�
+  const downloadQRCode = () => {
+    if (!qrCodeUrl.value) {
+      ElMessage.warning("璇峰厛鐢熸垚浜岀淮鐮�");
+      return;
+    }
+
+    const a = document.createElement("a");
+    a.href = qrCodeUrl.value;
+    a.download = `${currentQRProduct.value.productName}_${
+      currentQRProduct.value.identifierType
+    }_${new Date().getTime()}.png`;
+    document.body.appendChild(a);
+    a.click();
+    document.body.removeChild(a);
+    ElMessage.success("涓嬭浇鎴愬姛锛�");
+  };
+
+  // 澶嶅埗浜岀淮鐮佸唴瀹�
+  const copyQRContent = async () => {
+    if (!currentQRProduct.value) {
+      ElMessage.warning("娌℃湁鍙鍒剁殑鍐呭");
+      return;
+    }
+
+    try {
+      let content = "";
+      if (currentQRProduct.value.identifierType === "浜岀淮鐮�") {
+        content = `${currentQRProduct.value.productName}|${currentQRProduct.value.productCode}|${currentQRProduct.value.batchNo}|${currentQRProduct.value.identifierCode}`;
+      } else if (currentQRProduct.value.identifierType === "闃蹭吉鐮�") {
+        const timestamp = Date.now();
+        const random = Math.random().toString(36).substr(2, 8);
+        content = `SEC_${currentQRProduct.value.productCode}_${currentQRProduct.value.batchNo}_${timestamp}_${random}`;
+      }
+
+      await navigator.clipboard.writeText(content);
+      ElMessage.success("鍐呭宸插鍒跺埌鍓创鏉�");
+    } catch (error) {
+      // 闄嶇骇鏂规
+      const textArea = document.createElement("textarea");
+      textArea.value = content;
+      document.body.appendChild(textArea);
+      textArea.select();
+      document.execCommand("copy");
+      document.body.removeChild(textArea);
+      ElMessage.success("鍐呭宸插鍒跺埌鍓创鏉�");
+    }
+  };
+
+  const generateIdentifiers = () => {
+    if (!codeRule.value) {
+      ElMessage.warning("璇烽�夋嫨缂栫爜瑙勫垯");
+      return;
+    }
+
+    // 鐢熸垚鏍囪瘑鐨勯�昏緫
+    const newIdentifiers = [];
+    for (let i = 1; i <= generateQuantity.value; i++) {
+      let identifierCode = "";
+      if (codeRule.value === "浜у搧缂栫爜+鎵规鍙�+搴忓彿") {
+        identifierCode = `${currentProduct.value.productCode}_${
+          currentProduct.value.batchNo
+        }_${String(i).padStart(3, "0")}`;
+      } else if (codeRule.value === "鏃堕棿鎴�+闅忔満鏁�") {
+        identifierCode = `TS_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
+      } else if (codeRule.value === "鑷畾涔夎鍒�") {
+        identifierCode = `${customPrefix.value || "CUSTOM"}_${Date.now()}_${i}`;
+      }
+
+      newIdentifiers.push({
+        id: Math.max(...identifierList.value.map(item => item.id)) + i,
+        productName: currentProduct.value.productName,
+        productCode: currentProduct.value.productCode,
+        batchNo: currentProduct.value.batchNo,
+        identifierType: currentProduct.value.identifierType,
+        identifierCode: identifierCode,
+        status: "宸茬敓鎴�",
+        generateTime: new Date().toLocaleString(),
+        remark: "鎵归噺鐢熸垚",
+      });
+    }
+
+    identifierList.value.push(...newIdentifiers);
+    pagination.total += newIdentifiers.length;
+    ElMessage.success(`鎴愬姛鐢熸垚 ${newIdentifiers.length} 涓爣璇哷);
+    generateDialogVisible.value = false;
+  };
+
+  const saveReassign = () => {
+    if (!newBatchNo.value) {
+      ElMessage.warning("璇疯緭鍏ユ柊鎵规鍙�");
+      return;
+    }
+
+    const index = identifierList.value.findIndex(
+      item => item.id === currentProduct.value.id
+    );
     if (index > -1) {
-      identifierList.value.splice(index, 1)
-      pagination.total--
-      ElMessage.success('鍒犻櫎鎴愬姛')
+      identifierList.value[index].batchNo = newBatchNo.value;
+      identifierList.value[index].status = "宸插垎閰�";
+      ElMessage.success("鏍囪瘑閲嶆柊鍒嗛厤鎴愬姛");
+      reassignDialogVisible.value = false;
     }
-  })
-}
+  };
 
-// 鐢熸垚浜岀淮鐮�
-const generateQRCode = async (row) => {
-  try {
-    // 妫�鏌ュ繀瑕佸瓧娈�
-    if (!row.productName || !row.productCode || !row.batchNo) {
-      ElMessage.warning('浜у搧淇℃伅涓嶅畬鏁达紝鏃犳硶鐢熸垚浜岀淮鐮�')
-      return
-    }
-    
-    currentQRProduct.value = row
-    qrCodeUrl.value = ''
-    qrCodeDialogVisible.value = true
-    
-    // 鏋勫缓浜岀淮鐮佸唴瀹�
-    let qrContent = ''
-    if (row.identifierType === '浜岀淮鐮�') {
-      qrContent = `${row.productName}|${row.productCode}|${row.batchNo}|${row.identifierCode}`
-    } else if (row.identifierType === '闃蹭吉鐮�') {
-      // 闃蹭吉鐮佹牸寮忥細SEC_浜у搧缂栫爜_鎵规鍙穇鏃堕棿鎴砡闅忔満鏁�
-      const timestamp = Date.now()
-      const random = Math.random().toString(36).substr(2, 8)
-      qrContent = `SEC_${row.productCode}_${row.batchNo}_${timestamp}_${random}`
-    }
-    
-    // 鐢熸垚浜岀淮鐮�
-    qrCodeUrl.value = await QRCode.toDataURL(qrContent, {
-      width: 256,
-      margin: 2,
-      color: {
-        dark: '#000000',
-        light: '#FFFFFF'
-      },
-      errorCorrectionLevel: row.identifierType === '闃蹭吉鐮�' ? 'H' : 'M'
-    })
-    
-    ElMessage.success('浜岀淮鐮佺敓鎴愭垚鍔燂紒')
-    
-  } catch (error) {
-    console.error('鐢熸垚浜岀淮鐮佸け璐�:', error)
-    ElMessage.error('鐢熸垚浜岀淮鐮佸け璐ワ細' + error.message)
-    qrCodeDialogVisible.value = false
-  }
-}
+  const handleSubmit = () => {
+    formRef.value.validate(valid => {
+      if (valid) {
+        if (isEdit.value) {
+          // 缂栬緫
+          const index = identifierList.value.findIndex(
+            item => item.id === editId.value
+          );
+          if (index > -1) {
+            identifierList.value[index] = { ...form, id: editId.value };
+            ElMessage.success("缂栬緫鎴愬姛");
+          }
+        } else {
+          // 鏂板
+          const newId =
+            Math.max(...identifierList.value.map(item => item.id)) + 1;
 
-// 涓嬭浇浜岀淮鐮�
-const downloadQRCode = () => {
-  if (!qrCodeUrl.value) {
-    ElMessage.warning('璇峰厛鐢熸垚浜岀淮鐮�')
-    return
-  }
-  
-  const a = document.createElement('a')
-  a.href = qrCodeUrl.value
-  a.download = `${currentQRProduct.value.productName}_${currentQRProduct.value.identifierType}_${new Date().getTime()}.png`
-  document.body.appendChild(a)
-  a.click()
-  document.body.removeChild(a)
-  ElMessage.success('涓嬭浇鎴愬姛锛�')
-}
+          // 鏍规嵁鏍囪瘑绫诲瀷鐢熸垚涓嶅悓鐨勬爣璇嗙爜
+          let identifierCode = "";
+          if (form.identifierType === "浜岀淮鐮�") {
+            identifierCode = `QR_${form.productCode}_${form.batchNo}_001`;
+          } else if (form.identifierType === "闃蹭吉鐮�") {
+            identifierCode = `SEC_${form.productCode}_${form.batchNo}_001`;
+          }
 
-// 澶嶅埗浜岀淮鐮佸唴瀹�
-const copyQRContent = async () => {
-  if (!currentQRProduct.value) {
-    ElMessage.warning('娌℃湁鍙鍒剁殑鍐呭')
-    return
-  }
-  
-  try {
-    let content = ''
-    if (currentQRProduct.value.identifierType === '浜岀淮鐮�') {
-      content = `${currentQRProduct.value.productName}|${currentQRProduct.value.productCode}|${currentQRProduct.value.batchNo}|${currentQRProduct.value.identifierCode}`
-    } else if (currentQRProduct.value.identifierType === '闃蹭吉鐮�') {
-      const timestamp = Date.now()
-      const random = Math.random().toString(36).substr(2, 8)
-      content = `SEC_${currentQRProduct.value.productCode}_${currentQRProduct.value.batchNo}_${timestamp}_${random}`
-    }
-    
-    await navigator.clipboard.writeText(content)
-    ElMessage.success('鍐呭宸插鍒跺埌鍓创鏉�')
-  } catch (error) {
-    // 闄嶇骇鏂规
-    const textArea = document.createElement('textarea')
-    textArea.value = content
-    document.body.appendChild(textArea)
-    textArea.select()
-    document.execCommand('copy')
-    document.body.removeChild(textArea)
-    ElMessage.success('鍐呭宸插鍒跺埌鍓创鏉�')
-  }
-}
-
-const generateIdentifiers = () => {
-  if (!codeRule.value) {
-    ElMessage.warning('璇烽�夋嫨缂栫爜瑙勫垯')
-    return
-  }
-  
-  // 鐢熸垚鏍囪瘑鐨勯�昏緫
-  const newIdentifiers = []
-  for (let i = 1; i <= generateQuantity.value; i++) {
-    let identifierCode = ''
-    if (codeRule.value === '浜у搧缂栫爜+鎵规鍙�+搴忓彿') {
-      identifierCode = `${currentProduct.value.productCode}_${currentProduct.value.batchNo}_${String(i).padStart(3, '0')}`
-    } else if (codeRule.value === '鏃堕棿鎴�+闅忔満鏁�') {
-      identifierCode = `TS_${Date.now()}_${Math.floor(Math.random() * 1000)}`
-    } else if (codeRule.value === '鑷畾涔夎鍒�') {
-      identifierCode = `${customPrefix.value || 'CUSTOM'}_${Date.now()}_${i}`
-    }
-    
-    newIdentifiers.push({
-      id: Math.max(...identifierList.value.map(item => item.id)) + i,
-      productName: currentProduct.value.productName,
-      productCode: currentProduct.value.productCode,
-      batchNo: currentProduct.value.batchNo,
-      identifierType: currentProduct.value.identifierType,
-      identifierCode: identifierCode,
-      status: '宸茬敓鎴�',
-      generateTime: new Date().toLocaleString(),
-      remark: '鎵归噺鐢熸垚'
-    })
-  }
-  
-  identifierList.value.push(...newIdentifiers)
-  pagination.total += newIdentifiers.length
-  ElMessage.success(`鎴愬姛鐢熸垚 ${newIdentifiers.length} 涓爣璇哷)
-  generateDialogVisible.value = false
-}
-
-const saveReassign = () => {
-  if (!newBatchNo.value) {
-    ElMessage.warning('璇疯緭鍏ユ柊鎵规鍙�')
-    return
-  }
-  
-  const index = identifierList.value.findIndex(item => item.id === currentProduct.value.id)
-  if (index > -1) {
-    identifierList.value[index].batchNo = newBatchNo.value
-    identifierList.value[index].status = '宸插垎閰�'
-    ElMessage.success('鏍囪瘑閲嶆柊鍒嗛厤鎴愬姛')
-    reassignDialogVisible.value = false
-  }
-}
-
-const handleSubmit = () => {
-  formRef.value.validate((valid) => {
-    if (valid) {
-      if (isEdit.value) {
-        // 缂栬緫
-        const index = identifierList.value.findIndex(item => item.id === editId.value)
-        if (index > -1) {
-          identifierList.value[index] = { ...form, id: editId.value }
-          ElMessage.success('缂栬緫鎴愬姛')
+          identifierList.value.push({
+            ...form,
+            id: newId,
+            identifierCode: identifierCode,
+            generateTime: new Date().toLocaleString(),
+          });
+          pagination.total++;
+          ElMessage.success("鏂板鎴愬姛");
         }
-             } else {
-         // 鏂板
-         const newId = Math.max(...identifierList.value.map(item => item.id)) + 1
-         
-         // 鏍规嵁鏍囪瘑绫诲瀷鐢熸垚涓嶅悓鐨勬爣璇嗙爜
-         let identifierCode = ''
-         if (form.identifierType === '浜岀淮鐮�') {
-           identifierCode = `QR_${form.productCode}_${form.batchNo}_001`
-         } else if (form.identifierType === '闃蹭吉鐮�') {
-           identifierCode = `SEC_${form.productCode}_${form.batchNo}_001`
-         }
-         
-         identifierList.value.push({
-           ...form,
-           id: newId,
-           identifierCode: identifierCode,
-           generateTime: new Date().toLocaleString()
-         })
-         pagination.total++
-         ElMessage.success('鏂板鎴愬姛')
-       }
-      dialogVisible.value = false
-    }
-  })
-}
+        dialogVisible.value = false;
+      }
+    });
+  };
 
-const handleCurrentChange = (val) => {
-  pagination.currentPage = val.page
-  pagination.pageSize = val.limit
-}
+  const handleCurrentChange = val => {
+    pagination.currentPage = val.page;
+    pagination.pageSize = val.limit;
+  };
 </script>
 
 <style scoped>
-.search-row {
-  margin-bottom: 20px;
-}
+  .search-row {
+    margin-bottom: 20px;
+  }
 
-.quick-actions-row {
-  margin-bottom: 20px;
-}
+  .quick-actions-row {
+    margin-bottom: 20px;
+  }
 
-.quick-actions-row .el-alert {
-  margin-bottom: 0;
-}
+  .quick-actions-row .el-alert {
+    margin-bottom: 0;
+  }
 
-.quick-actions-row .el-alert p {
-  margin: 5px 0;
-  font-size: 14px;
-  line-height: 1.5;
-}
+  .quick-actions-row .el-alert p {
+    margin: 5px 0;
+    font-size: 14px;
+    line-height: 1.5;
+  }
 
-/* 浜岀淮鐮侀瑙堟牱寮� */
-.qr-preview-container {
-  text-align: center;
-  padding: 20px;
-}
+  /* 浜岀淮鐮侀瑙堟牱寮� */
+  .qr-preview-container {
+    text-align: center;
+    padding: 20px;
+  }
 
-.qr-image-container {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  gap: 20px;
-}
+  .qr-image-container {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    gap: 20px;
+  }
 
-.qr-image {
-  max-width: 100%;
-  height: auto;
-  border: 2px solid #e0e0e0;
-  border-radius: 8px;
-  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
-}
+  .qr-image {
+    max-width: 100%;
+    height: auto;
+    border: 2px solid #e0e0e0;
+    border-radius: 8px;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+  }
 
-.qr-info {
-  text-align: left;
-  background: #f8f9fa;
-  padding: 15px;
-  border-radius: 8px;
-  min-width: 300px;
-}
+  .qr-info {
+    text-align: left;
+    background: #f8f9fa;
+    padding: 15px;
+    border-radius: 8px;
+    min-width: 300px;
+  }
 
-.qr-info p {
-  margin: 8px 0;
-  color: #666;
-  font-size: 14px;
-}
+  .qr-info p {
+    margin: 8px 0;
+    color: #666;
+    font-size: 14px;
+  }
 
-.qr-loading {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  gap: 15px;
-  padding: 40px 0;
-}
+  .qr-loading {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    gap: 15px;
+    padding: 40px 0;
+  }
 
-.qr-loading .el-icon {
-  font-size: 32px;
-  color: #409EFF;
-}
+  .qr-loading .el-icon {
+    font-size: 32px;
+    color: #409eff;
+  }
 
-.qr-loading p {
-  color: #666;
-  margin: 0;
-}
+  .qr-loading p {
+    color: #666;
+    margin: 0;
+  }
 </style>
diff --git a/src/views/productionManagement/operationScheduling/components/formDia.vue b/src/views/productionManagement/operationScheduling/components/formDia.vue
index a4f36bf..06b46ac 100644
--- a/src/views/productionManagement/operationScheduling/components/formDia.vue
+++ b/src/views/productionManagement/operationScheduling/components/formDia.vue
@@ -8,20 +8,47 @@
     >
       <el-button type="primary" @click="addRow" style="margin-bottom: 10px;">鏂板</el-button>
 			<span style="font-size: 18px;margin-left: 10px">寰呮帓浜ф暟閲忥細{{pendingNum}}</span>
+<!--			<div style="margin-bottom: 10px; margin-left: 10px;">-->
+<!--				<el-form-item label="棰嗙敤锛�" style="margin-bottom: 0;">-->
+<!--					<el-input v-model="receive" placeholder="璇疯緭鍏ラ鐢�" style="width: 200px;" />-->
+<!--				</el-form-item>-->
+<!--			</div>-->
       <el-table :data="tableData" border style="width: 100%" :summary-method="summarizeMainTable" show-summary :row-key="row => row.id">
-        <el-table-column label="搴忓彿" width="60">
+        <el-table-column label="搴忓彿" width="60" align="center">
           <template #default="scope">
             {{ scope.$index + 1 }}
           </template>
         </el-table-column>
-        <el-table-column label="宸ュ簭" prop="process">
+        <el-table-column label="宸ュ簭" prop="process" width="150">
           <template #default="scope">
 						<el-input v-model="scope.row.process" placeholder="璇疯緭鍏ュ伐搴�" />
           </template>
         </el-table-column>
-        <el-table-column label="鍗曚綅" prop="unit">
+		<el-table-column label="浜х嚎" prop="productionLine" width="150">
+          <template #default="scope">
+            <el-select
+              v-model="scope.row.productionLine"
+              placeholder="閫夋嫨浜х嚎"
+              style="width: 100%;"
+              clearable
+            >
+              <el-option
+                v-for="line in productionLines"
+                :key="line.value"
+                :label="line.label"
+                :value="line.value"
+              />
+            </el-select>
+          </template>
+        </el-table-column>
+        <el-table-column label="鍗曚綅" prop="unit" width="90">
           <template #default="scope">
             <el-input v-model="scope.row.unit" placeholder="璇疯緭鍏ュ崟浣�" />
+          </template>
+        </el-table-column>
+        <el-table-column label="鍙e懗/鍝佸悕/瑙勬牸" prop="type" width="150">
+          <template #default="scope">
+            <el-input v-model="scope.row.type" placeholder="璇疯緭鍏�" />
           </template>
         </el-table-column>
         <el-table-column label="鎺掍骇鏁伴噺" width="200" prop="schedulingNum">
@@ -50,17 +77,20 @@
 						/>
           </template>
         </el-table-column>
-        <el-table-column label="鎺掍骇鏃ユ湡" prop="schedulingDate">
+        <el-table-column label="鎺掍骇鏃ユ湡" prop="schedulingDate" width="200">
           <template #default="scope">
             <el-date-picker v-model="scope.row.schedulingDate" type="date" placeholder="閫夋嫨鏃ユ湡" style="width: 100%;" value-format="YYYY-MM-DD" format="YYYY-MM-DD"/>
           </template>
         </el-table-column>
-        <el-table-column label="鎺掍骇浜�" prop="schedulingUserId">
+        <el-table-column label="鎺掍骇浜�" prop="schedulingUserId" width="150">
           <template #default="scope">
 						<el-select
 							v-model="scope.row.schedulingUserId"
 							placeholder="閫夋嫨浜哄憳"
 							style="width: 100%;"
+              filterable
+              default-first-option
+              :reserve-keyword="false"
 						>
 							<el-option
 								v-for="user in userList"
@@ -69,6 +99,11 @@
 								:value="user.userId"
 							/>
 						</el-select>
+          </template>
+        </el-table-column>
+        <el-table-column label="澶囨敞" prop="remark" width="200">
+          <template #default="scope">
+            <el-input v-model="scope.row.remark" placeholder="璇疯緭鍏ュ娉�" />
           </template>
         </el-table-column>
         <el-table-column label="鎿嶄綔" width="80">
@@ -88,7 +123,7 @@
 </template>
 
 <script setup>
-import {ref} from "vue";
+import {ref, getCurrentInstance} from "vue";
 import {userListNoPageByTenantId} from "@/api/system/user.js";
 import {processScheduling} from "@/api/productionManagement/operationScheduling.js";
 const { proxy } = getCurrentInstance()
@@ -97,33 +132,56 @@
 
 const dialogFormVisible = ref(false);
 const operationType = ref('')
-const tableData = ref([
-	{ process: '', schedulingDate: '', schedulingNum: '', schedulingUserId: '', workHours: '', unit: '' }
-]);
+const tableData = ref([]);
 const unitFromRow = ref('');
 const idFromRow = ref('');
-const pendingNum = ref('');
+const specificationModelFromRow = ref('');
+const pendingNum = ref(0);
 const userList = ref([])
+const receive = ref('')
+const sunqianUserId = ref('')
+// 浜х嚎閫夐」
+const productionLines = ref([
+  { label: '浜х嚎1', value: '浜х嚎1' },
+  { label: '浜х嚎2', value: '浜х嚎2' },
+  { label: '浜х嚎3', value: '浜х嚎3' },
+  { label: '浜х嚎4', value: '浜х嚎4' }
+])
 
 // 鎵撳紑寮规
 const openDialog = (type, row) => {
   operationType.value = type;
   dialogFormVisible.value = true;
+	pendingNum.value = row?.pendingNum ?? 0;
+	unitFromRow.value = row?.unit ?? '';
+	idFromRow.value = row?.id ?? '';
+	specificationModelFromRow.value = row?.specificationModel ?? '';
+	
 	userListNoPageByTenantId().then((res) => {
 		userList.value = res.data;
+		// 鎵惧埌瀛欏�╃殑鐢ㄦ埛ID骞惰缃负榛樿鍊�
+		const sunqianUser = userList.value.find(user => user.nickName === '瀛欏��');
+		if (sunqianUser) {
+			sunqianUserId.value = sunqianUser.userId;
+		}
+		// 鍦ㄧ敤鎴峰垪琛ㄥ姞杞藉畬鎴愬悗鍒涘缓琛屾暟鎹紝骞跺皢浜х嚎鏁版嵁甯﹀叆
+		tableData.value = [createRow(row)];
 	});
-	pendingNum.value = row.pendingNum
-  if (row && row.unit !== undefined) {
-    unitFromRow.value = row.unit;
-    idFromRow.value = row.id;
-    tableData.value.forEach(item => {
-      item.unit = row.unit;
-      item.id = row.id;
-    });
-  } else {
-    unitFromRow.value = '';
-  }
 }
+
+const createRow = (row) => ({
+	id: idFromRow.value,
+	process: '鍖呰',
+	schedulingDate: '',
+	schedulingNum: null,
+	schedulingUserId: sunqianUserId.value, // 榛樿璁剧疆涓哄瓩鍊╃殑鐢ㄦ埛ID
+	workHours: null,
+	unit: unitFromRow.value,
+	remark: '',
+	type: specificationModelFromRow.value,
+	productionLine: row?.productionLine ?? '', // 浠庤鏁版嵁涓幏鍙栦骇绾夸俊鎭�
+});
+
 const submitForm = () => {
 	// 1. 妫�鏌ユ瘡涓�琛屾槸鍚﹀~鍐欏畬鏁�
 	for (let i = 0; i < tableData.value.length; i++) {
@@ -134,7 +192,8 @@
 			row.schedulingNum === '' || row.schedulingNum === null ||
 			!row.schedulingUserId ||
 			row.workHours === '' || row.workHours === null ||
-			!row.unit
+			!row.unit ||
+			!row.productionLine
 		) {
 			proxy.$modal.msgError(`绗�${i + 1}琛屾暟鎹湭濉啓瀹屾暣`);
 			return;
@@ -148,7 +207,15 @@
 		proxy.$modal.msgError('鎺掍骇鏁伴噺鍚堣涓嶈兘瓒呰繃寰呮帓浜ф暟閲�');
 		return;
 	}
-	processScheduling(tableData.value).then((res) => {
+	// 3. 灏� receive 瀛楁娣诲姞鍒版瘡鏉℃暟鎹腑锛屽苟绉婚櫎 loss 瀛楁
+	const submitData = tableData.value.map(row => {
+		const { loss, ...rest } = row;
+		return {
+			...rest,
+			receive: receive.value
+		};
+	});
+	processScheduling(submitData).then((res) => {
 		proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
 		closeDia();
 	})
@@ -159,6 +226,12 @@
 // 鍏抽棴寮规
 const closeDia = () => {
   dialogFormVisible.value = false;
+  receive.value = '';
+  tableData.value = [];
+  unitFromRow.value = '';
+  idFromRow.value = '';
+  specificationModelFromRow.value = '';
+  pendingNum.value = 0;
   emit('close')
 };
 defineExpose({
@@ -166,7 +239,7 @@
 });
 
 const addRow = () => {
-  tableData.value.push({ id: idFromRow.value, process: '', unit: unitFromRow.value, schedulingNum: '', workHours: '', schedulingDate: '', schedulingUserId: '' });
+  tableData.value.push(createRow());
 };
 const removeRow = (index) => {
   tableData.value.splice(index, 1);
diff --git a/src/views/productionManagement/operationScheduling/index.vue b/src/views/productionManagement/operationScheduling/index.vue
index 082b782..a9c06fb 100644
--- a/src/views/productionManagement/operationScheduling/index.vue
+++ b/src/views/productionManagement/operationScheduling/index.vue
@@ -7,11 +7,16 @@
 										style="width: 200px;"
 										@change="handleQuery" />
 				</el-form-item>
-				<el-form-item label="椤圭洰鍚嶇О:">
-					<el-input v-model="searchForm.projectName" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+				<el-form-item label="鍚堝悓鍙�:">
+					<el-input v-model="searchForm.salesContractNo" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
 										style="width: 200px;"
 										@change="handleQuery" />
 				</el-form-item>
+<!--				<el-form-item label="椤圭洰鍚嶇О:">-->
+<!--					<el-input v-model="searchForm.projectName" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"-->
+<!--										style="width: 200px;"-->
+<!--										@change="handleQuery" />-->
+<!--				</el-form-item>-->
 				<el-form-item label="娲惧伐鏃ユ湡:">
 					<el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
 													placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
@@ -60,10 +65,12 @@
 const data = reactive({
 	searchForm: {
 		staffName: "",
+		customerName: "",
+		salesContractNo: "",
 		status: 1,
-		entryDate: null, // 褰曞叆鏃ユ湡
-		entryDateStart: undefined,
-		entryDateEnd: undefined,
+		entryDate: [dayjs().format("YYYY-MM-DD"), dayjs().format("YYYY-MM-DD")], // 褰曞叆鏃ユ湡锛岄粯璁ゅ綋澶�
+		entryDateStart: dayjs().format("YYYY-MM-DD"),
+		entryDateEnd: dayjs().format("YYYY-MM-DD"),
 	},
 });
 const { searchForm } = toRefs(data);
@@ -105,21 +112,21 @@
 		prop: "salesContractNo",
 		width: 200,
 	},
-	{
-		label: "瀹㈡埛鍚堝悓鍙�",
-		prop: "customerContractNo",
-		width: 200,
-	},
+	// {
+	// 	label: "瀹㈡埛鍚堝悓鍙�",
+	// 	prop: "customerContractNo",
+	// 	width: 200,
+	// },
 	{
 		label: "瀹㈡埛鍚嶇О",
 		prop: "customerName",
 		width: 200,
 	},
-	{
-		label: "椤圭洰鍚嶇О",
-		prop: "projectName",
-		width:300
-	},
+	// {
+	// 	label: "椤圭洰鍚嶇О",
+	// 	prop: "projectName",
+	// 	width:300
+	// },
 	{
 		label: "浜у搧澶х被",
 		prop: "productCategory",
@@ -131,6 +138,16 @@
 		width: 150,
 	},
 	{
+		label: "缁戝畾鏈哄櫒",
+		prop: "speculativeTradingName",
+		width: 220,
+	},
+	// {
+	// 	label: "浜х嚎",
+	// 	prop: "productionLine",
+	// 	width: 220,
+	// },
+	{
 		label: "鍗曚綅",
 		prop: "unit",
 	},
diff --git a/src/views/productionManagement/processRoute/Edit.vue b/src/views/productionManagement/processRoute/Edit.vue
new file mode 100644
index 0000000..2ede998
--- /dev/null
+++ b/src/views/productionManagement/processRoute/Edit.vue
@@ -0,0 +1,186 @@
+<template>
+  <div>
+    <el-dialog
+        v-model="isShow"
+        title="缂栬緫宸ヨ壓璺嚎"
+        width="400"
+        @close="closeModal"
+    >
+      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
+        <el-form-item
+            label="浜у搧澶х被锛�"
+            prop="productId"
+            :rules="[
+                {
+                required: true,
+                message: '璇烽�夋嫨浜у搧澶х被',
+              }
+            ]"
+        >
+          <el-tree-select
+              v-model="formState.productId"
+              placeholder="璇烽�夋嫨"
+              clearable
+              check-strictly
+              @change="getModels"
+              :data="productOptions"
+              :render-after-expand="false"
+              style="width: 100%"
+          />
+        </el-form-item>
+
+        <el-form-item
+            label="瑙勬牸鍨嬪彿锛�"
+            prop="productModelId"
+            :rules="[
+                {
+                required: true,
+                message: '璇烽�夋嫨瑙勬牸鍨嬪彿',
+              }
+            ]"
+        >
+          <el-select
+              v-model="formState.productModelId"
+              placeholder="璇烽�夋嫨"
+              clearable
+          >
+            <el-option
+                v-for="item in productModelsOptions"
+                :key="item.id"
+                :label="item.model"
+                :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="澶囨敞" prop="description">
+          <el-input v-model="formState.description" type="textarea" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="handleSubmit">纭</el-button>
+          <el-button @click="closeModal">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {ref, computed, getCurrentInstance, onMounted} from "vue";
+import {update} from "@/api/productionManagement/processRoute.js";
+import {modelList, productTreeList} from "@/api/basicData/product.js";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+  },
+
+  record: {
+    type: Object,
+    required: true,
+  }
+});
+
+const emit = defineEmits(['update:visible', 'completed']);
+
+// 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
+const formState = ref({});
+
+const isShow = computed({
+  get() {
+    return props.visible;
+  },
+  set(val) {
+    emit('update:visible', val);
+  },
+});
+
+let { proxy } = getCurrentInstance()
+const productModelsOptions = ref([])
+const productOptions = ref([])
+
+const closeModal = () => {
+  isShow.value = false;
+};
+
+const setFormData = () => {
+  formState.value = props.record
+}
+
+const getProductOptions = () => {
+  productTreeList().then((res) => {
+    productOptions.value = convertIdToValue(res);
+  });
+};
+const getModels = (value) => {
+  formState.value.productModelId = undefined;
+  productModelsOptions.value = [];
+  if (value) {
+    modelList({ id: value }).then((res) => {
+      productModelsOptions.value = res;
+    });
+  }
+};
+
+const findNodeById = (nodes, productId) => {
+  for (let i = 0; i < nodes.length; i++) {
+    if (nodes[i].value === productId) {
+      return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥炶鑺傜偣鐨刲abel
+    }
+    if (nodes[i].children && nodes[i].children.length > 0) {
+      const foundNode = findNodeById(nodes[i].children, productId);
+      if (foundNode) {
+        return foundNode; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝鐩存帴杩斿洖锛堝凡缁忔槸label瀛楃涓诧級
+      }
+    }
+  }
+  return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
+};
+
+function convertIdToValue(data) {
+  return data.map((item) => {
+    const { id, children, ...rest } = item;
+    const newItem = {
+      ...rest,
+      value: id, // 灏� id 鏀逛负 value
+    };
+    if (children && children.length > 0) {
+      newItem.children = convertIdToValue(children);
+    }
+
+    return newItem;
+  });
+}
+
+const handleSubmit = () => {
+  proxy.$refs["formRef"].validate(valid => {
+    if (valid) {
+      update(formState.value).then(res => {
+        // 鍏抽棴妯℃�佹
+        isShow.value = false;
+        // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
+        emit('completed');
+        proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+      })
+    }
+  })
+};
+
+defineExpose({
+  closeModal,
+  handleSubmit,
+  isShow,
+});
+
+
+onMounted(() => {
+  getProductOptions()
+  getModels(props.record.productId)
+  nextTick(() => {
+    setFormData()
+  });
+})
+</script>
diff --git a/src/views/productionManagement/processRoute/ItemsForm.vue b/src/views/productionManagement/processRoute/ItemsForm.vue
new file mode 100644
index 0000000..ed6e499
--- /dev/null
+++ b/src/views/productionManagement/processRoute/ItemsForm.vue
@@ -0,0 +1,531 @@
+<template>
+  <div>
+    <el-dialog
+        v-model="isShow"
+        title="宸ヨ壓璺嚎椤圭洰"
+        width="800px"
+        @close="closeModal"
+    >
+      <div class="operate-button">
+        <el-button
+            type="primary"
+            @click="isShowProductSelectDialog = true"
+            class="mb5"
+            style="margin-bottom: 10px;"
+        >
+          閫夋嫨浜у搧
+        </el-button>
+
+        <el-switch
+            v-model="isTable"
+            inline-prompt
+            active-text="琛ㄦ牸"
+            inactive-text="鍒楄〃"
+            @change="handleViewChange"
+        />
+      </div>
+
+      <el-table
+          v-if="isTable"
+          ref="multipleTable"
+          v-loading="tableLoading"
+          border
+          :data="routeItems"
+          :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
+          row-key="id"
+          tooltip-effect="dark"
+          class="lims-table"
+          style="cursor: move;"
+      >
+        <el-table-column align="center" label="搴忓彿" width="60">
+          <template #default="scope">
+            {{ scope.$index + 1 }}
+          </template>
+        </el-table-column>
+
+        <el-table-column
+            v-for="(item, index) in tableColumn"
+            :key="index"
+            :label="item.label"
+            :width="item.width"
+            show-overflow-tooltip
+        >
+          <template #default="scope" v-if="item.dataType === 'action'">
+            <el-button
+                v-for="(op, opIndex) in item.operation"
+                :key="opIndex"
+                :type="op.type"
+                :link="op.link"
+                size="small"
+                @click.stop="op.clickFun(scope.row)"
+            >
+              {{ op.name }}
+            </el-button>
+          </template>
+
+          <template #default="scope" v-else>
+            <template v-if="item.prop === 'processId'">
+              <el-select
+                  v-model="scope.row[item.prop]"
+                  style="width: 100%;"
+                  @mousedown.stop
+              >
+                <el-option
+                    v-for="process in processOptions"
+                    :key="process.id"
+                    :label="process.name"
+                    :value="process.id"
+                />
+              </el-select>
+            </template>
+            <template v-else>
+              {{ scope.row[item.prop] || '-' }}
+            </template>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 浣跨敤鏅�歞iv鏇夸唬el-steps -->
+      <div
+          v-else
+          ref="stepsContainer"
+          class="mb5 custom-steps"
+          style="padding: 10px 0; display: flex; flex-wrap: nowrap; gap: 20px; align-items: flex-start;"
+      >
+        <div
+            v-for="(item, index) in routeItems"
+            :key="item.id"
+            class="custom-step draggable-step"
+            :data-id="item.id"
+            style="cursor: move; flex: 0 0 auto; min-width: 220px;"
+        >
+          <div class="step-content">
+            <div class="step-number">{{ index + 1 }}</div>
+            <el-card
+                :header="item.productName"
+                class="step-card"
+                style="cursor: move;"
+            >
+              <div class="step-card-content">
+                <p>{{ item.model }}</p>
+                <p>{{ item.unit }}</p>
+                <el-select
+                    v-model="item.processId"
+                    style="width: 100%;"
+                    @mousedown.stop
+                >
+                  <el-option
+                      v-for="process in processOptions"
+                      :key="process.id"
+                      :label="process.name"
+                      :value="process.id"
+                  />
+                </el-select>
+              </div>
+              <template #footer>
+                <div class="step-card-footer">
+                  <el-button type="danger" link size="small" @click.stop="removeItemByID(item.id)">鍒犻櫎</el-button>
+                </div>
+              </template>
+            </el-card>
+          </div>
+        </div>
+      </div>
+
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="handleSubmit">纭</el-button>
+          <el-button @click="closeModal">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <ProductSelectDialog
+        v-model="isShowProductSelectDialog"
+        @confirm="handelSelectProducts"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, getCurrentInstance, onMounted, onUnmounted, nextTick } from "vue";
+import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+import { findProcessRouteItemList, addOrUpdateProcessRouteItem } from "@/api/productionManagement/processRouteItem.js";
+import { processList } from "@/api/productionManagement/productionProcess.js";
+import Sortable from 'sortablejs';
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+    default: false
+  },
+  record: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+});
+
+const emit = defineEmits(['update:visible', 'completed']);
+
+const processOptions = ref([]);
+const tableLoading = ref(false);
+const isShowProductSelectDialog = ref(false);
+const routeItems = ref([]);
+let tableSortable = null;
+let stepsSortable = null;
+const multipleTable = ref(null);
+const stepsContainer = ref(null);
+const isTable = ref(true);
+
+const isShow = computed({
+  get() {
+    return props.visible;
+  },
+  set(val) {
+    emit('update:visible', val);
+  }
+});
+
+const tableColumn = ref([
+  { label: "浜у搧鍚嶇О", prop: "productName", width: 180 },
+  { label: "瑙勬牸鍚嶇О", prop: "model", width: 150 },
+  { label: "鍗曚綅", prop: "unit", width: 80 },
+  { label: "宸ュ簭鍚嶇О", prop: "processId", width: 180 },
+  {
+    dataType: "action",
+    label: "鎿嶄綔",
+    align: "center",
+    fixed: "right",
+    width: 100,
+    operation: [
+      {
+        name: "鍒犻櫎",
+        type: "danger",
+        link: true,
+        clickFun: (row) => {
+          const idx = routeItems.value.findIndex(item => item.id === row.id);
+          if (idx > -1) {
+            removeItem(idx)
+          }
+        }
+      }
+    ]
+  }
+]);
+
+const removeItem = (index) => {
+  routeItems.value.splice(index, 1);
+  nextTick(() => initSortable());
+};
+
+const removeItemByID = (id) => {
+  const idx = routeItems.value.findIndex(item => item.id === id);
+  if (idx > -1) {
+    routeItems.value.splice(idx, 1);
+    nextTick(() => initSortable());
+  }
+};
+
+const closeModal = () => {
+  isShow.value = false;
+};
+
+const handelSelectProducts = (products) => {
+  destroySortable();
+
+  const newData = products.map(({ id, ...product }) => ({
+    ...product,
+    productModelId: id,
+    routeId: props.record.id,
+    id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
+    processId: undefined
+  }));
+
+  console.log('閫夋嫨浜у搧鍓嶆暟缁�:', routeItems.value);
+  routeItems.value.push(...newData);
+  routeItems.value = [...routeItems.value];
+  console.log('閫夋嫨浜у搧鍚庢暟缁�:', routeItems.value);
+
+  // 寤惰繜鍒濆鍖栵紝纭繚DOM瀹屽叏娓叉煋
+  nextTick(() => {
+    // 寮哄埗閲嶆柊娓叉煋缁勪欢
+    if (proxy?.$forceUpdate) {
+      proxy.$forceUpdate();
+    }
+
+    const temp = [...routeItems.value];
+    routeItems.value = [];
+    nextTick(() => {
+      routeItems.value = temp;
+      initSortable();
+    });
+  });
+};
+
+const findProcessRouteItems = () => {
+  tableLoading.value = true;
+  findProcessRouteItemList({ routeId: props.record.id })
+      .then(res => {
+        tableLoading.value = false;
+        routeItems.value = res.data.map(item => ({
+          ...item,
+          processId: item.processId === 0 ? undefined : item.processId
+        }));
+        // 寤惰繜鍒濆鍖栵紝纭繚DOM瀹屽叏娓叉煋
+        nextTick(() => {
+          setTimeout(() => initSortable(), 100);
+        });
+      })
+      .catch(err => {
+        tableLoading.value = false;
+        console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
+      });
+};
+
+const findProcessList = () => {
+  processList({})
+      .then(res => {
+        processOptions.value = res.data;
+      })
+      .catch(err => {
+        console.error("鑾峰彇宸ュ簭澶辫触锛�", err);
+      });
+};
+
+const { proxy } = getCurrentInstance() || {};
+
+const handleSubmit = () => {
+  const hasEmptyProcess = routeItems.value.some(item => !item.processId);
+  if (hasEmptyProcess) {
+    proxy?.$modal?.msgError("璇蜂负鎵�鏈夐」鐩�夋嫨宸ュ簭");
+    return;
+  }
+
+  addOrUpdateProcessRouteItem({
+    routeId: props.record.id,
+    processRouteItem: routeItems.value.map(({ id, ...item }) => item)
+  })
+      .then(res => {
+        isShow.value = false;
+        emit('completed');
+        proxy?.$modal?.msgSuccess("鎻愪氦鎴愬姛");
+      })
+      .catch(err => {
+        proxy?.$modal?.msgError(`鎻愪氦澶辫触锛�${err.msg || "缃戠粶寮傚父"}`);
+      });
+};
+
+const destroySortable = () => {
+  if (tableSortable) {
+    tableSortable.destroy();
+    tableSortable = null;
+  }
+  if (stepsSortable) {
+    stepsSortable.destroy();
+    stepsSortable = null;
+  }
+};
+
+const initSortable = () => {
+  destroySortable();
+
+  if (isTable.value) {
+    if (!multipleTable.value) return;
+    const tbody = multipleTable.value.$el.querySelector('.el-table__body tbody') ||
+        multipleTable.value.$el.querySelector('.el-table__body-wrapper > table > tbody');
+    if (!tbody) return;
+
+    tableSortable = new Sortable(tbody, {
+      animation: 150,
+      ghostClass: 'sortable-ghost',
+      handle: '.el-table__row',
+      filter: '.el-button, .el-select',
+      onEnd: (evt) => {
+        if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex]) return;
+
+        // 浣跨敤鏁扮粍 splice 鏂规硶閲嶆柊鎺掑簭锛屼笌琛ㄦ牸妯″紡淇濇寔涓�鑷�
+        const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
+        routeItems.value.splice(evt.newIndex, 0, moveItem);
+        routeItems.value = [...routeItems.value];
+        console.log('鎺掑簭鍚庢暟缁�:', routeItems.value);
+      }
+    });
+  } else {
+    if (!stepsContainer.value) return;
+
+    // 淇敼锛氱洿鎺ヤ娇鐢╯tepsContainer.value浣滀负鎷栨嫿瀹瑰櫒
+    const stepsList = stepsContainer.value;
+    if (!stepsList) {
+      console.warn('鏈壘鍒版楠ゆ潯鎷栨嫿瀹瑰櫒');
+      return;
+    }
+
+    // 淇敼锛氱畝鍖栨嫋鎷介厤缃�
+    stepsSortable = new Sortable(stepsList, {
+      animation: 150,
+      ghostClass: 'sortable-ghost',
+      draggable: '.draggable-step', // 鍙嫋鎷藉厓绱�
+      handle: '.draggable-step, .step-card', // 鎷栨嫿鎵嬫焺
+      filter: '.el-button, .el-select, .el-input', // 杩囨护鎸夐挳/閫夋嫨鍣�
+      forceFallback: true,
+      fallbackClass: 'sortable-fallback',
+      preventOnFilter: true,
+      scroll: true,
+      scrollSensitivity: 30,
+      scrollSpeed: 10,
+      bubbleScroll: true,
+      onEnd: (evt) => {
+        if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex]) return;
+
+        // 浣跨敤鏁扮粍 splice 鏂规硶閲嶆柊鎺掑簭
+        const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
+        routeItems.value.splice(evt.newIndex, 0, moveItem);
+        routeItems.value = [...routeItems.value];
+      }
+    });
+
+    // 璋冭瘯锛氭墦鍗板鍣ㄥ拰瀹炰緥锛岀‘璁ょ粦瀹氭垚鍔�
+    console.log('姝ラ鏉℃嫋鎷藉鍣�:', stepsList);
+    console.log('Sortable瀹炰緥:', stepsSortable);
+  }
+};
+
+const handleViewChange = () => {
+  destroySortable();
+  // 寤惰繜鍒濆鍖栵紝纭繚瑙嗗浘鍒囨崲鍚嶥OM瀹屽叏娓叉煋
+  nextTick(() => {
+    setTimeout(() => initSortable(), 100);
+  });
+};
+
+onMounted(() => {
+  findProcessRouteItems();
+  findProcessList();
+});
+
+onUnmounted(() => {
+  destroySortable();
+});
+
+defineExpose({
+  closeModal,
+  handleSubmit,
+  isShow
+});
+</script>
+
+<style scoped>
+:deep(.sortable-ghost) {
+  opacity: 0.6;
+  background-color: #f5f7fa !important;
+}
+
+:deep(.el-table__row) {
+  transition: background-color 0.2s;
+}
+
+:deep(.el-table__row:hover) {
+  background-color: #f9fafc !important;
+}
+
+:deep(.el-card__footer){
+  padding: 0 !important;
+}
+
+.operate-button {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+/* 淇敼锛氳嚜瀹氫箟姝ラ鏉″鍣ㄦ牱寮� */
+.custom-steps {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: flex-start;
+  gap: 20px;
+  min-height: 100px;
+}
+
+/* 淇敼锛氳嚜瀹氫箟姝ラ椤规牱寮� */
+.custom-step {
+  cursor: move !important;
+  padding: 8px;
+  position: relative;
+  transition: all 0.2s ease;
+  flex: 0 0 auto;
+  min-width: 220px;
+  touch-action: none;
+}
+
+/* 鎷栨嫿鎮诞鏍峰紡锛屾彁绀哄彲鎷栨嫿 */
+.custom-step:hover {
+  background-color: rgba(64, 158, 255, 0.05);
+  transform: translateY(-2px);
+}
+
+.sortable-ghost {
+  opacity: 0.4;
+  background-color: #f5f7fa !important;
+  border: 2px dashed #409eff;
+  margin: 10px;
+  transform: scale(1.02);
+}
+
+.sortable-fallback {
+  opacity: 0.9;
+  background-color: #f5f7fa;
+  border: 1px solid #409eff;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+  transform: rotate(2deg);
+  margin: 10px;
+}
+
+.step-card {
+  cursor: move !important;
+  transition: box-shadow 0.2s ease;
+  user-select: none;
+  -webkit-user-select: none;
+  pointer-events: auto;
+  margin: 10px;
+  height: 240px;
+}
+
+.step-card:hover {
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.step-content {
+  width: 220px;
+  user-select: none;
+}
+
+.step-card-content {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.step-card-footer {
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  padding: 10px;
+}
+
+/* 鑷畾涔夊簭鍙锋牱寮忎紭鍖� */
+.step-number {
+  font-weight: bold;
+  text-align: center;
+  width: 36px;
+  height: 36px;
+  line-height: 36px;
+  margin: 0 auto 10px;
+  background: #409eff;
+  color: #fff;
+  border-radius: 50%;
+  font-size: 14px;
+}
+</style>
diff --git a/src/views/productionManagement/processRoute/New.vue b/src/views/productionManagement/processRoute/New.vue
new file mode 100644
index 0000000..856a2a4
--- /dev/null
+++ b/src/views/productionManagement/processRoute/New.vue
@@ -0,0 +1,181 @@
+<template>
+  <div>
+    <el-dialog
+        v-model="isShow"
+        title="鏂板宸ヨ壓璺嚎"
+        width="400"
+        @close="closeModal"
+    >
+      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
+        <el-form-item
+            label="浜у搧澶х被锛�"
+            prop="productId"
+            :rules="[
+                {
+                required: true,
+                message: '璇烽�夋嫨浜у搧澶х被',
+              }
+            ]"
+        >
+          <el-tree-select
+              v-model="formState.productId"
+              placeholder="璇烽�夋嫨"
+              clearable
+              check-strictly
+              @change="getModels"
+              :data="productOptions"
+              :render-after-expand="false"
+              style="width: 100%"
+          />
+        </el-form-item>
+
+        <el-form-item
+            label="瑙勬牸鍨嬪彿锛�"
+            prop="productModelId"
+            :rules="[
+                {
+                required: true,
+                message: '璇烽�夋嫨瑙勬牸鍨嬪彿',
+              }
+            ]"
+        >
+          <el-select
+              v-model="formState.productModelId"
+              placeholder="璇烽�夋嫨"
+              clearable
+          >
+            <el-option
+                v-for="item in productModelsOptions"
+                :key="item.id"
+                :label="item.model"
+                :value="item.id"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="澶囨敞" prop="description">
+          <el-input v-model="formState.description" type="textarea" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="handleSubmit">纭</el-button>
+          <el-button @click="closeModal">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {ref, computed, getCurrentInstance, onMounted} from "vue";
+import {add} from "@/api/productionManagement/processRoute.js";
+import {modelList, productTreeList} from "@/api/basicData/product.js";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+  },
+});
+
+const emit = defineEmits(['update:visible', 'completed']);
+
+// 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
+const formState = ref({
+  productId: undefined,
+  productModelId: undefined,
+  description: '',
+});
+
+const isShow = computed({
+  get() {
+    return props.visible;
+  },
+  set(val) {
+    emit('update:visible', val);
+  },
+});
+
+const productModelsOptions = ref([])
+const productOptions = ref([])
+
+let { proxy } = getCurrentInstance()
+
+const closeModal = () => {
+  isShow.value = false;
+};
+
+const getProductOptions = () => {
+  productTreeList().then((res) => {
+    productOptions.value = convertIdToValue(res);
+  });
+};
+const getModels = (value) => {
+  formState.value.productId = undefined;
+  formState.value.productModelId = undefined;
+  productModelsOptions.value = [];
+
+  if (value) {
+    formState.value.productId = findNodeById(productOptions.value, value) || undefined;
+    modelList({ id: value }).then((res) => {
+      productModelsOptions.value = res;
+    });
+  }
+};
+
+const findNodeById = (nodes, productId) => {
+  for (let i = 0; i < nodes.length; i++) {
+    if (nodes[i].value === productId) {
+      return nodes[i].label; // 鎵惧埌鑺傜偣锛岃繑鍥炶鑺傜偣鐨刲abel
+    }
+    if (nodes[i].children && nodes[i].children.length > 0) {
+      const foundNode = findNodeById(nodes[i].children, productId);
+      if (foundNode) {
+        return foundNode; // 鍦ㄥ瓙鑺傜偣涓壘鍒帮紝鐩存帴杩斿洖锛堝凡缁忔槸label瀛楃涓诧級
+      }
+    }
+  }
+  return null; // 娌℃湁鎵惧埌鑺傜偣锛岃繑鍥瀗ull
+};
+
+function convertIdToValue(data) {
+  return data.map((item) => {
+    const { id, children, ...rest } = item;
+    const newItem = {
+      ...rest,
+      value: id, // 灏� id 鏀逛负 value
+    };
+    if (children && children.length > 0) {
+      newItem.children = convertIdToValue(children);
+    }
+
+    return newItem;
+  });
+}
+
+const handleSubmit = () => {
+  proxy.$refs["formRef"].validate(valid => {
+    if (valid) {
+      add(formState.value).then(res => {
+        // 鍏抽棴妯℃�佹
+        isShow.value = false;
+        // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
+        emit('completed');
+        proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+      })
+    }
+  })
+};
+
+
+defineExpose({
+  closeModal,
+  handleSubmit,
+  isShow,
+});
+
+onMounted(() => {
+  getProductOptions()
+})
+</script>
diff --git a/src/views/productionManagement/processRoute/index.vue b/src/views/productionManagement/processRoute/index.vue
new file mode 100644
index 0000000..7d5ab5d
--- /dev/null
+++ b/src/views/productionManagement/processRoute/index.vue
@@ -0,0 +1,194 @@
+<template>
+  <div class="app-container">
+    <div class="search_form">
+      <el-form :model="searchForm" :inline="true">
+        <el-form-item label="瑙勬牸鍚嶇О:">
+          <el-input v-model="searchForm.model" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
+                    style="width: 200px;"
+                    @change="handleQuery" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <div class="table_list">
+      <div style="text-align: right" class="mb10">
+        <el-button type="primary" @click="showNewModal">鏂板宸ヨ壓璺嚎</el-button>
+        <el-button type="danger" @click="handleDelete" :disabled="selectedRows.length === 0" plain>鍒犻櫎宸ヨ壓璺嚎</el-button>
+      </div>
+      <PIMTable
+          rowKey="id"
+          :column="tableColumn"
+          :tableData="tableData"
+          :page="page"
+          :isSelection="true"
+          @selection-change="handleSelectionChange"
+          :tableLoading="tableLoading"
+          @pagination="pagination"
+          :total="page.total"
+      />
+    </div>
+    <new-process
+        v-if="isShowNewModal"
+        v-model:visible="isShowNewModal"
+        @completed="getList"
+    />
+
+    <edit-process
+        v-if="isShowEditModal"
+        v-model:visible="isShowEditModal"
+        :record="record"
+        @completed="getList"
+    />
+
+    <route-item-form
+        v-if="isShowItemModal"
+        v-model:visible="isShowItemModal"
+        :record="record"
+        @completed="getList"
+    />
+  </div>
+</template>
+
+<script setup>
+import {onMounted, ref} from "vue";
+import NewProcess from "@/views/productionManagement/processRoute/New.vue";
+import EditProcess from "@/views/productionManagement/processRoute/Edit.vue";
+import RouteItemForm from "@/views/productionManagement/processRoute/ItemsForm.vue";
+import {listPage, del} from "@/api/productionManagement/processRoute.js";
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
+const data = reactive({
+  searchForm: {
+    model: "",
+  },
+});
+const { searchForm } = toRefs(data);
+const tableColumn = ref([
+  {
+    label: "宸ヨ壓璺嚎缂栧彿",
+    prop: "processRouteCode",
+  },
+  {
+    label: "浜у搧鍚嶇О",
+    prop: "productName",
+  },
+  {
+    label: "瑙勬牸鍚嶇О",
+    prop: "model",
+  },
+  {
+    label: "鎻忚堪",
+    prop: "description",
+  },
+  {
+    dataType: "action",
+    label: "鎿嶄綔",
+    align: "center",
+    fixed: "right",
+    width: 280,
+    operation: [
+      {
+        name: "缂栬緫",
+        type: "text",
+        clickFun: (row) => {
+          showEditModal(row);
+        }
+      },
+      {
+        name: "璺嚎椤圭洰",
+        type: "text",
+        clickFun: (row) => {
+          showItemModal(row);
+        }
+      }
+    ]
+  }
+]);
+const tableData = ref([]);
+const selectedRows = ref([]);
+const tableLoading = ref(false);
+const isShowNewModal = ref(false);
+const isShowEditModal = ref(false);
+const isShowItemModal = ref(false);
+const record = ref({});
+const page = reactive({
+  current: 1,
+  size: 100,
+  total: 0,
+});
+const { proxy } = getCurrentInstance()
+
+// 鏌ヨ鍒楄〃
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  page.current = 1;
+  getList();
+};
+
+const pagination = (obj) => {
+  page.current = obj.page;
+  page.size = obj.limit;
+  getList();
+};
+const getList = () => {
+  tableLoading.value = true;
+  const params = { ...searchForm.value, ...page };
+  params.entryDate = undefined
+  listPage(params).then(res => {
+    tableLoading.value = false;
+    tableData.value = res.data.records.map(item => ({
+      ...item,
+    }));
+    page.total = res.data.total;
+  }).catch(err => {
+    tableLoading.value = false;
+  })
+};
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const handleSelectionChange = (selection) => {
+  selectedRows.value = selection;
+};
+
+// 鎵撳紑鏂板寮规
+const showNewModal = () => {
+  isShowNewModal.value = true
+};
+
+const showEditModal = (row) => {
+  isShowEditModal.value = true
+  record.value = row
+};
+
+const showItemModal = (row) => {
+  router.push({
+    path: '/productionManagement/processRouteItem',
+    query: {
+      id: row.id
+    }
+  })
+};
+
+// 鍒犻櫎
+function handleDelete() {
+  const ids = selectedRows.value.map((item) => item.id);
+  proxy.$modal
+      .confirm('鏄惁纭鍒犻櫎宸插嬀閫夌殑鏁版嵁椤癸紵')
+      .then(function () {
+        return del(ids);
+      })
+      .then(() => {
+        getList();
+        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+      })
+      .catch(() => {});
+}
+
+onMounted(() => {
+  getList();
+});
+</script>
+
+<style scoped></style>
diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
new file mode 100644
index 0000000..641ffff
--- /dev/null
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -0,0 +1,503 @@
+<template>
+  <div class="app-container">
+    <div class="operate-button">
+      <div style="margin-bottom: 15px;">
+        <el-button
+            type="primary"
+            @click="isShowProductSelectDialog = true"
+        >
+          閫夋嫨浜у搧
+        </el-button>
+        <el-button type="primary" @click="handleSubmit">纭</el-button>
+      </div>
+
+      <el-switch
+          v-model="isTable"
+          inline-prompt
+          active-text="琛ㄦ牸"
+          inactive-text="鍒楄〃"
+          @change="handleViewChange"
+      />
+    </div>
+    <el-table
+        v-if="isTable"
+        ref="multipleTable"
+        v-loading="tableLoading"
+        border
+        :data="routeItems"
+        :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
+        row-key="id"
+        tooltip-effect="dark"
+        class="lims-table"
+        style="cursor: move;"
+    >
+      <el-table-column align="center" label="搴忓彿" width="60">
+        <template #default="scope">
+          {{ scope.$index + 1 }}
+        </template>
+      </el-table-column>
+
+      <el-table-column
+          v-for="(item, index) in tableColumn"
+          :key="index"
+          :label="item.label"
+          :width="item.width"
+          show-overflow-tooltip
+      >
+        <template #default="scope" v-if="item.dataType === 'action'">
+          <el-button
+              v-for="(op, opIndex) in item.operation"
+              :key="opIndex"
+              :type="op.type"
+              :link="op.link"
+              size="small"
+              @click.stop="op.clickFun(scope.row)"
+          >
+            {{ op.name }}
+          </el-button>
+        </template>
+
+        <template #default="scope" v-else>
+          <template v-if="item.prop === 'processId'">
+            <el-select
+                v-model="scope.row[item.prop]"
+                style="width: 100%;"
+                @mousedown.stop
+            >
+              <el-option
+                  v-for="process in processOptions"
+                  :key="process.id"
+                  :label="process.name"
+                  :value="process.id"
+              />
+            </el-select>
+          </template>
+          <template v-else>
+            {{ scope.row[item.prop] || '-' }}
+          </template>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 浣跨敤鏅�歞iv鏇夸唬el-steps -->
+    <div
+        v-else
+        ref="stepsContainer"
+        class="mb5 custom-steps"
+    >
+      <div
+          v-for="(item, index) in routeItems"
+          :key="item.id"
+          class="custom-step draggable-step"
+          :data-id="item.id"
+          style="cursor: move; flex: 0 0 auto; min-width: 220px;"
+      >
+        <div class="step-content">
+          <div class="step-number">{{ index + 1 }}</div>
+          <el-card
+              :header="item.productName"
+              class="step-card"
+              style="cursor: move;"
+          >
+            <div class="step-card-content">
+              <p>{{ item.model }}</p>
+              <p>{{ item.unit }}</p>
+              <el-select
+                  v-model="item.processId"
+                  style="width: 100%;"
+                  @mousedown.stop
+              >
+                <el-option
+                    v-for="process in processOptions"
+                    :key="process.id"
+                    :label="process.name"
+                    :value="process.id"
+                />
+              </el-select>
+            </div>
+            <template #footer>
+              <div class="step-card-footer">
+                <el-button type="danger" link size="small" @click.stop="removeItemByID(item.id)">鍒犻櫎</el-button>
+              </div>
+            </template>
+          </el-card>
+        </div>
+      </div>
+    </div>
+
+    <ProductSelectDialog
+        v-model="isShowProductSelectDialog"
+        @confirm="handelSelectProducts"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, getCurrentInstance, onMounted, onUnmounted, nextTick } from "vue";
+import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+import { findProcessRouteItemList, addOrUpdateProcessRouteItem } from "@/api/productionManagement/processRouteItem.js";
+import { processList } from "@/api/productionManagement/productionProcess.js";
+import Sortable from 'sortablejs';
+import { useRoute, useRouter } from 'vue-router'
+
+const processOptions = ref([]);
+const tableLoading = ref(false);
+const isShowProductSelectDialog = ref(false);
+const routeItems = ref([]);
+let tableSortable = null;
+let stepsSortable = null;
+const multipleTable = ref(null);
+const stepsContainer = ref(null);
+const isTable = ref(true);
+
+const route = useRoute()
+const router = useRouter()
+const routeId = computed({
+  get() {
+    return route.query.id;
+  },
+
+  set(val) {
+    emit('update:router', val)
+  }
+});
+
+
+const tableColumn = ref([
+  { label: "浜у搧鍚嶇О", prop: "productName"},
+  { label: "瑙勬牸鍚嶇О", prop: "model" },
+  { label: "鍗曚綅", prop: "unit" },
+  { label: "宸ュ簭鍚嶇О", prop: "processId", width: 200 },
+  {
+    dataType: "action",
+    label: "鎿嶄綔",
+    align: "center",
+    fixed: "right",
+    width: 100,
+    operation: [
+      {
+        name: "鍒犻櫎",
+        type: "danger",
+        link: true,
+        clickFun: (row) => {
+          const idx = routeItems.value.findIndex(item => item.id === row.id);
+          if (idx > -1) {
+            removeItem(idx)
+          }
+        }
+      }
+    ]
+  }
+]);
+
+const removeItem = (index) => {
+  routeItems.value.splice(index, 1);
+  nextTick(() => initSortable());
+};
+
+const removeItemByID = (id) => {
+  const idx = routeItems.value.findIndex(item => item.id === id);
+  if (idx > -1) {
+    routeItems.value.splice(idx, 1);
+    nextTick(() => initSortable());
+  }
+};
+
+const handelSelectProducts = (products) => {
+  destroySortable();
+
+  const newData = products.map(({ id, ...product }) => ({
+    ...product,
+    productModelId: id,
+    routeId: routeId.value,
+    id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
+    processId: undefined
+  }));
+
+  console.log('閫夋嫨浜у搧鍓嶆暟缁�:', routeItems.value);
+  routeItems.value.push(...newData);
+  routeItems.value = [...routeItems.value];
+  console.log('閫夋嫨浜у搧鍚庢暟缁�:', routeItems.value);
+
+  // 寤惰繜鍒濆鍖栵紝纭繚DOM瀹屽叏娓叉煋
+  nextTick(() => {
+    // 寮哄埗閲嶆柊娓叉煋缁勪欢
+    if (proxy?.$forceUpdate) {
+      proxy.$forceUpdate();
+    }
+
+    const temp = [...routeItems.value];
+    routeItems.value = [];
+    nextTick(() => {
+      routeItems.value = temp;
+      initSortable();
+    });
+  });
+};
+
+const findProcessRouteItems = () => {
+  tableLoading.value = true;
+  findProcessRouteItemList({ routeId: routeId.value })
+      .then(res => {
+        tableLoading.value = false;
+        routeItems.value = res.data.map(item => ({
+          ...item,
+          processId: item.processId === 0 ? undefined : item.processId
+        }));
+        // 寤惰繜鍒濆鍖栵紝纭繚DOM瀹屽叏娓叉煋
+        nextTick(() => {
+          setTimeout(() => initSortable(), 100);
+        });
+      })
+      .catch(err => {
+        tableLoading.value = false;
+        console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
+      });
+};
+
+const findProcessList = () => {
+  processList({})
+      .then(res => {
+        processOptions.value = res.data;
+      })
+      .catch(err => {
+        console.error("鑾峰彇宸ュ簭澶辫触锛�", err);
+      });
+};
+
+const { proxy } = getCurrentInstance() || {};
+
+const handleSubmit = () => {
+  const hasEmptyProcess = routeItems.value.some(item => !item.processId);
+  if (hasEmptyProcess) {
+    proxy?.$modal?.msgError("璇蜂负鎵�鏈夐」鐩�夋嫨宸ュ簭");
+    return;
+  }
+
+  addOrUpdateProcessRouteItem({
+    routeId: routeId.value,
+    processRouteItem: routeItems.value.map(({ id, ...item }) => item)
+  })
+      .then(res => {
+        router.push({
+          path: '/productionManagement/processRoute',
+        })
+        proxy?.$modal?.msgSuccess("鎻愪氦鎴愬姛");
+      })
+      .catch(err => {
+        proxy?.$modal?.msgError(`鎻愪氦澶辫触锛�${err.msg || "缃戠粶寮傚父"}`);
+      });
+};
+
+const destroySortable = () => {
+  if (tableSortable) {
+    tableSortable.destroy();
+    tableSortable = null;
+  }
+  if (stepsSortable) {
+    stepsSortable.destroy();
+    stepsSortable = null;
+  }
+};
+
+const initSortable = () => {
+  destroySortable();
+
+  if (isTable.value) {
+    if (!multipleTable.value) return;
+    const tbody = multipleTable.value.$el.querySelector('.el-table__body tbody') ||
+        multipleTable.value.$el.querySelector('.el-table__body-wrapper > table > tbody');
+    if (!tbody) return;
+
+    tableSortable = new Sortable(tbody, {
+      animation: 150,
+      ghostClass: 'sortable-ghost',
+      handle: '.el-table__row',
+      filter: '.el-button, .el-select',
+      onEnd: (evt) => {
+        if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex]) return;
+
+        // 浣跨敤鏁扮粍 splice 鏂规硶閲嶆柊鎺掑簭锛屼笌琛ㄦ牸妯″紡淇濇寔涓�鑷�
+        const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
+        routeItems.value.splice(evt.newIndex, 0, moveItem);
+        routeItems.value = [...routeItems.value];
+        console.log('鎺掑簭鍚庢暟缁�:', routeItems.value);
+      }
+    });
+  } else {
+    if (!stepsContainer.value) return;
+
+    // 淇敼锛氱洿鎺ヤ娇鐢╯tepsContainer.value浣滀负鎷栨嫿瀹瑰櫒
+    const stepsList = stepsContainer.value;
+    if (!stepsList) {
+      console.warn('鏈壘鍒版楠ゆ潯鎷栨嫿瀹瑰櫒');
+      return;
+    }
+
+    // 淇敼锛氱畝鍖栨嫋鎷介厤缃�
+    stepsSortable = new Sortable(stepsList, {
+      animation: 150,
+      ghostClass: 'sortable-ghost',
+      draggable: '.draggable-step', // 鍙嫋鎷藉厓绱�
+      handle: '.draggable-step, .step-card', // 鎷栨嫿鎵嬫焺
+      filter: '.el-button, .el-select, .el-input', // 杩囨护鎸夐挳/閫夋嫨鍣�
+      forceFallback: true,
+      fallbackClass: 'sortable-fallback',
+      preventOnFilter: true,
+      scroll: true,
+      scrollSensitivity: 30,
+      scrollSpeed: 10,
+      bubbleScroll: true,
+      onEnd: (evt) => {
+        if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex]) return;
+
+        // 浣跨敤鏁扮粍 splice 鏂规硶閲嶆柊鎺掑簭
+        const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
+        routeItems.value.splice(evt.newIndex, 0, moveItem);
+        routeItems.value = [...routeItems.value];
+      }
+    });
+
+    // 璋冭瘯锛氭墦鍗板鍣ㄥ拰瀹炰緥锛岀‘璁ょ粦瀹氭垚鍔�
+    console.log('姝ラ鏉℃嫋鎷藉鍣�:', stepsList);
+    console.log('Sortable瀹炰緥:', stepsSortable);
+  }
+};
+
+const handleViewChange = () => {
+  destroySortable();
+  // 寤惰繜鍒濆鍖栵紝纭繚瑙嗗浘鍒囨崲鍚嶥OM瀹屽叏娓叉煋
+  nextTick(() => {
+    setTimeout(() => initSortable(), 100);
+  });
+};
+
+onMounted(() => {
+  findProcessRouteItems();
+  findProcessList();
+});
+
+onUnmounted(() => {
+  destroySortable();
+});
+
+defineExpose({
+  handleSubmit,
+});
+</script>
+
+<style scoped>
+:deep(.sortable-ghost) {
+  opacity: 0.6;
+  background-color: #f5f7fa !important;
+}
+
+:deep(.el-table__row) {
+  transition: background-color 0.2s;
+}
+
+:deep(.el-table__row:hover) {
+  background-color: #f9fafc !important;
+}
+
+:deep(.el-card__footer){
+  padding: 0 !important;
+}
+
+.operate-button {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+/* 淇敼锛氳嚜瀹氫箟姝ラ鏉″鍣ㄦ牱寮� */
+.custom-steps {
+  min-height: 100px;
+  padding: 10px 0;
+  display: flex;
+  flex-wrap: wrap;
+  gap: 20px;
+  align-items: flex-start;
+}
+
+/* 淇敼锛氳嚜瀹氫箟姝ラ椤规牱寮� */
+.custom-step {
+  cursor: move !important;
+  padding: 8px;
+  position: relative;
+  transition: all 0.2s ease;
+  flex: 0 0 auto;
+  min-width: 220px;
+  touch-action: none;
+}
+
+/* 鎷栨嫿鎮诞鏍峰紡锛屾彁绀哄彲鎷栨嫿 */
+.custom-step:hover {
+  background-color: rgba(64, 158, 255, 0.05);
+  transform: translateY(-2px);
+}
+
+.sortable-ghost {
+  opacity: 0.4;
+  background-color: #f5f7fa !important;
+  border: 2px dashed #409eff;
+  margin: 10px;
+  transform: scale(1.02);
+}
+
+.sortable-fallback {
+  opacity: 0.9;
+  background-color: #f5f7fa;
+  border: 1px solid #409eff;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+  transform: rotate(2deg);
+  margin: 10px;
+}
+
+.step-card {
+  cursor: move !important;
+  transition: box-shadow 0.2s ease;
+  user-select: none;
+  -webkit-user-select: none;
+  pointer-events: auto;
+  margin: 10px;
+  height: 260px;
+}
+
+.step-card:hover {
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+}
+
+.step-content {
+  width: 245px;
+  user-select: none;
+}
+
+.step-card-content {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  height: 140px;
+}
+
+.step-card-footer {
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  padding: 10px;
+}
+
+/* 鑷畾涔夊簭鍙锋牱寮忎紭鍖� */
+.step-number {
+  font-weight: bold;
+  text-align: center;
+  width: 36px;
+  height: 36px;
+  line-height: 36px;
+  margin: 0 auto 10px;
+  background: #409eff;
+  color: #fff;
+  border-radius: 50%;
+  font-size: 14px;
+}
+</style>
diff --git a/src/views/productionManagement/productStructure/Detail/index.vue b/src/views/productionManagement/productStructure/Detail/index.vue
new file mode 100644
index 0000000..a131830
--- /dev/null
+++ b/src/views/productionManagement/productStructure/Detail/index.vue
@@ -0,0 +1,293 @@
+<template>
+  <div class="app-container">
+    <el-button v-if="dataValue.isEdit"
+               type="primary"
+               @click="addItem"
+               style="margin-bottom: 10px">娣诲姞
+    </el-button>
+    <el-button v-if="!dataValue.isEdit"
+               type="primary"
+               @click="dataValue.isEdit = true"
+               style="margin-bottom: 10px">缂栬緫
+    </el-button>
+    <el-button v-if="dataValue.isEdit"
+               type="primary"
+               @click="cancelEdit"
+               style="margin-bottom: 10px">鍙栨秷
+    </el-button>
+    <el-button type="primary"
+               :loading="dataValue.loading"
+               @click="submit"
+               :disabled="!dataValue.isEdit"
+               style="margin-bottom: 10px">纭
+    </el-button>
+    <el-table
+        :data="tableData"
+        border
+        :preserve-expanded-content="false"
+        style="width: 100%"
+    >
+      <el-table-column type="expand">
+        <template #default="props">
+          <el-form ref="form"
+                   :model="dataValue">
+            <el-table :data="dataValue.dataList"
+                      style="width: 100%">
+              <el-table-column prop="productName"
+                               label="浜у搧"/>
+              <el-table-column prop="model"
+                               label="瑙勬牸">
+                <template #default="{ row, $index }">
+                  <el-form-item v-if="dataValue.isEdit"
+                                :prop="`dataList.${$index}.model`"
+                                :rules="[{ required: true, message: '璇烽�夋嫨瑙勬牸', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-select v-model="row.model"
+                               placeholder="璇烽�夋嫨瑙勬牸"
+                               clearable
+                               :disabled="!dataValue.isEdit"
+                               style="width: 100%"
+                               @visible-change="(v) => { if (v) openDialog($index) }">
+                      <el-option v-if="row.model"
+                                 :label="row.model"
+                                 :value="row.model" />
+                    </el-select>
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="processId"
+                               label="娑堣�楀伐搴�">
+                <template #default="{ row, $index }">
+                  <el-form-item :prop="`dataList.${$index}.processId`"
+                                :rules="[{ required: true, message: '璇烽�夋嫨娑堣�楀伐搴�', trigger: 'change' }]"
+                                style="margin: 0">
+                    <el-select v-model="row.processId"
+                               placeholder="璇烽�夋嫨"
+                               filterable
+                               clearable
+                               style="width: 100%"
+                               :disabled="!dataValue.isEdit">
+                      <el-option v-for="item in dataValue.processOptions"
+                                 :key="item.id"
+                                 :label="item.name"
+                                 :value="item.id" />
+                    </el-select>
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="unitQuantity"
+                               label="鍗曚綅浜у嚭鎵�闇�鏁伴噺">
+                <template #default="{ row, $index }">
+                  <el-form-item :prop="`dataList.${$index}.unitQuantity`"
+                                :rules="[{ required: true, message: '璇疯緭鍏ュ崟浣嶄骇鍑烘墍闇�鏁伴噺', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input-number v-model="row.unitQuantity"
+                                     :min="0"
+                                     :precision="2"
+                                     :step="1"
+                                     controls-position="right"
+                                     style="width: 100%"
+                                     :disabled="!dataValue.isEdit" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="demandedQuantity"
+                               label="闇�姹傛�婚噺">
+                <template #default="{ row, $index }">
+                  <el-form-item :prop="`dataList.${$index}.demandedQuantity`"
+                                :rules="[{ required: true, message: '璇疯緭鍏ラ渶姹傛�婚噺', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input-number v-model="row.demandedQuantity"
+                                     :min="0"
+                                     :precision="2"
+                                     :step="1"
+                                     controls-position="right"
+                                     style="width: 100%"
+                                     :disabled="!dataValue.isEdit" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="unit"
+                               label="鍗曚綅">
+                <template #default="{ row, $index }">
+                  <el-form-item :prop="`dataList.${$index}.unit`"
+                                :rules="[{ required: true, message: '璇疯緭鍏ュ崟浣�', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input v-model="row.unit"
+                              placeholder="璇疯緭鍏ュ崟浣�"
+                              clearable
+                              :disabled="!dataValue.isEdit" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="diskQuantity"
+                               label="鐩樻暟锛堢洏锛�">
+                <template #default="{ row, $index }">
+                  <el-form-item :prop="`dataList.${$index}.diskQuantity`"
+                                :rules="[{ required: true, message: '璇疯緭鍏ョ洏鏁�', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input-number v-model="row.diskQuantity"
+                                     :min="0"
+                                     :precision="0"
+                                     :step="1"
+                                     controls-position="right"
+                                     style="width: 100%"
+                                     :disabled="!dataValue.isEdit" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column label="鎿嶄綔" fixed="right" width="100">
+                <template #default="{ row, $index }">
+                  <el-button type="danger"
+                             text
+                             @click="dataValue.dataList.splice($index, 1)">鍒犻櫎
+                  </el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </el-form>
+        </template>
+      </el-table-column>
+      <el-table-column label="浜у搧缂栫爜" prop="productCode" />
+      <el-table-column label="浜у搧鍚嶇О" prop="productName" />
+      <el-table-column label="瑙勬牸鍨嬪彿" prop="model" />
+      <el-table-column label="鍗曚綅" prop="unit" />
+    </el-table>
+
+    <product-select-dialog v-if="dataValue.showProductDialog"
+                           v-model:model-value="dataValue.showProductDialog"
+                           @confirm="handleProduct" />
+  </div>
+</template>
+
+<script setup lang="ts">
+import {
+  computed,
+  defineAsyncComponent,
+  defineComponent,
+  onMounted,
+  reactive,
+  ref,
+} from "vue";
+import { queryList, add } from "@/api/productionManagement/productStructure.js";
+import { list } from "@/api/productionManagement/productionProcess";
+import { ElMessage } from "element-plus";
+import {useRoute, useRouter} from "vue-router";
+
+defineComponent({
+  name: "StructureEdit",
+});
+
+const ProductSelectDialog = defineAsyncComponent(
+    () => import("@/views/basicData/product/ProductSelectDialog.vue")
+);
+const form = ref();
+
+const route = useRoute()
+const router = useRouter()
+const routeId = computed({
+  get() {
+    return route.query.id;
+  },
+
+  set(val) {
+    emit('update:router', val)
+  }
+});
+
+
+const dataValue = reactive({
+  dataList: [],
+  productOptions: [],
+  processOptions: [],
+  showProductDialog: false,
+  currentRowIndex: null,
+  loading: false,
+  isEdit: false,
+});
+
+const tableData = reactive([
+  {
+    productName: "",
+    model: "",
+    unit: "",
+    productCode: "",
+  }
+])
+
+const openDialog = index => {
+  dataValue.currentRowIndex = index;
+  dataValue.showProductDialog = true;
+};
+
+const fetchData = async () => {
+  const { data } = await queryList(routeId.value);
+  tableData[0].productName = data.productName;
+  tableData[0].model = data.model;
+  tableData[0].unit = data.unit;
+  tableData[0].productCode = data.productCode;
+  dataValue.dataList = data.productStructureList;
+};
+
+const fetchProcessOptions = async () => {
+  const { data } = await list(routeId.value);
+  dataValue.processOptions = data;
+};
+
+const handleProduct = row => {
+  if (row?.length > 1) {
+    ElMessage.error("鍙兘閫夋嫨涓�涓骇鍝�");
+  }
+  dataValue.dataList[dataValue.currentRowIndex].productName =
+      row[0].productName;
+  dataValue.dataList[dataValue.currentRowIndex].model = row[0].model;
+  dataValue.dataList[dataValue.currentRowIndex].productModelId = row[0].id;
+  dataValue.showProductDialog = false;
+};
+
+const submit = () => {
+  form.value
+      .validate(valid => {
+        dataValue.loading = true;
+        if (valid) {
+          add({
+            parentId: routeId.value,
+            productStructureList: dataValue.dataList || [],
+          }).then(res => {
+            router.push({
+              path: '/productionManagement/productionManagement/productStructure/index',
+            })
+            ElMessage.success("淇濆瓨鎴愬姛");
+            dataValue.loading = false;
+          });
+        }
+      })
+      .finally(() => {
+        dataValue.loading = false;
+      });
+};
+
+const addItem = () => {
+  dataValue.dataList.push({
+    productName: "",
+    productId: "",
+    model: undefined,
+    productModelId: undefined,
+    processId: "",
+    unitQuantity: 0,
+    demandedQuantity: 0,
+    unit: "",
+    diskQuantity: 0,
+  });
+};
+
+const cancelEdit = () => {
+  dataValue.isEdit = false;
+  dataValue.dataList = dataValue.dataList.filter(item => item.id !== undefined);
+};
+
+onMounted(() => {
+  fetchData();
+  fetchProcessOptions();
+});
+</script>
\ No newline at end of file
diff --git a/src/views/productionManagement/productStructure/StructureEdit.vue b/src/views/productionManagement/productStructure/StructureEdit.vue
new file mode 100644
index 0000000..4d07f5d
--- /dev/null
+++ b/src/views/productionManagement/productStructure/StructureEdit.vue
@@ -0,0 +1,311 @@
+<template>
+  <el-dialog v-model="visible"
+             title="缁撴瀯"
+             width="1200"
+             close-on-click-modal
+             @close="visible = false">
+    <el-button v-if="dataValue.isEdit"
+               type="primary"
+               @click="addItem"
+               style="margin-bottom: 10px">娣诲姞
+    </el-button>
+    <el-button v-if="!dataValue.isEdit"
+               type="primary"
+               @click="dataValue.isEdit = true"
+               style="margin-bottom: 10px">缂栬緫
+    </el-button>
+    <el-button v-if="dataValue.isEdit"
+               type="primary"
+               @click="cancelEdit"
+               style="margin-bottom: 10px">鍙栨秷
+    </el-button>
+
+    <el-table
+        :data="tableData"
+        border
+        :preserve-expanded-content="false"
+        style="width: 100%"
+    >
+      <el-table-column type="expand">
+        <template #default="props">
+          <el-form ref="form"
+                   :model="dataValue">
+            <el-table :data="dataValue.dataList"
+                      style="width: 100%">
+              <el-table-column prop="productName"
+                               label="浜у搧"
+                               width="150" />
+              <el-table-column prop="model"
+                               label="瑙勬牸"
+                               width="150">
+                <template #default="{ row, $index }">
+                  <el-form-item v-if="dataValue.isEdit"
+                                :prop="`dataList.${$index}.model`"
+                                :rules="[{ required: true, message: '璇烽�夋嫨瑙勬牸', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-select v-model="row.model"
+                               placeholder="璇烽�夋嫨浜у搧"
+                               clearable
+                               :disabled="!dataValue.isEdit"
+                               style="width: 100%"
+                               @visible-change="(v) => { if (v) openDialog($index) }">
+                      <el-option v-if="row.model"
+                                 :label="row.model"
+                                 :value="row.model" />
+                    </el-select>
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="processId"
+                               label="娑堣�楀伐搴�"
+                               width="150">
+                <template #default="{ row, $index }">
+                  <el-form-item :prop="`dataList.${$index}.processId`"
+                                :rules="[{ required: true, message: '璇烽�夋嫨娑堣�楀伐搴�', trigger: 'change' }]"
+                                style="margin: 0">
+                    <el-select v-model="row.processId"
+                               placeholder="璇烽�夋嫨"
+                               filterable
+                               clearable
+                               style="width: 100%"
+                               :disabled="!dataValue.isEdit">
+                      <el-option v-for="item in dataValue.processOptions"
+                                 :key="item.id"
+                                 :label="item.name"
+                                 :value="item.id" />
+                    </el-select>
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="unitQuantity"
+                               label="鍗曚綅浜у嚭鎵�闇�鏁伴噺"
+                               width="150">
+                <template #default="{ row, $index }">
+                  <el-form-item :prop="`dataList.${$index}.unitQuantity`"
+                                :rules="[{ required: true, message: '璇疯緭鍏ュ崟浣嶄骇鍑烘墍闇�鏁伴噺', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input-number v-model="row.unitQuantity"
+                                     :min="0"
+                                     :precision="2"
+                                     :step="1"
+                                     controls-position="right"
+                                     style="width: 100%"
+                                     :disabled="!dataValue.isEdit" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="demandedQuantity"
+                               label="闇�姹傛�婚噺"
+                               width="150">
+                <template #default="{ row, $index }">
+                  <el-form-item :prop="`dataList.${$index}.demandedQuantity`"
+                                :rules="[{ required: true, message: '璇疯緭鍏ラ渶姹傛�婚噺', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input-number v-model="row.demandedQuantity"
+                                     :min="0"
+                                     :precision="2"
+                                     :step="1"
+                                     controls-position="right"
+                                     style="width: 100%"
+                                     :disabled="!dataValue.isEdit" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="unit"
+                               label="鍗曚綅"
+                               width="150">
+                <template #default="{ row, $index }">
+                  <el-form-item :prop="`dataList.${$index}.unit`"
+                                :rules="[{ required: true, message: '璇疯緭鍏ュ崟浣�', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input v-model="row.unit"
+                              placeholder="璇疯緭鍏ュ崟浣�"
+                              clearable
+                              :disabled="!dataValue.isEdit" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column prop="diskQuantity"
+                               label="鐩樻暟锛堢洏锛�"
+                               width="150">
+                <template #default="{ row, $index }">
+                  <el-form-item :prop="`dataList.${$index}.diskQuantity`"
+                                :rules="[{ required: true, message: '璇疯緭鍏ョ洏鏁�', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input-number v-model="row.diskQuantity"
+                                     :min="0"
+                                     :precision="0"
+                                     :step="1"
+                                     controls-position="right"
+                                     style="width: 100%"
+                                     :disabled="!dataValue.isEdit" />
+                  </el-form-item>
+                </template>
+              </el-table-column>
+              <el-table-column label="鎿嶄綔">
+                <template #default="{ row, $index }">
+                  <el-button type="danger"
+                             text
+                             @click="dataValue.dataList.splice($index, 1)">鍒犻櫎
+                  </el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </el-form>
+        </template>
+      </el-table-column>
+      <el-table-column label="浜у搧缂栫爜" prop="productCode" />
+      <el-table-column label="浜у搧鍚嶇О" prop="productName" />
+      <el-table-column label="瑙勬牸鍨嬪彿" prop="model" />
+      <el-table-column label="鍗曚綅" prop="unit" />
+    </el-table>
+
+    <product-select-dialog v-if="dataValue.showProductDialog"
+                           v-model:model-value="dataValue.showProductDialog"
+                           @confirm="handleProduct" />
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button type="primary"
+                   :loading="dataValue.loading"
+                   @click="submit"
+                   :disabled="!dataValue.isEdit">
+          纭
+        </el-button>
+        <el-button @click="visible = false">鍙栨秷</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+  import {
+    computed,
+    defineAsyncComponent,
+    defineComponent,
+    onMounted,
+    reactive,
+    ref,
+  } from "vue";
+  import { queryList, add } from "@/api/productionManagement/productStructure.js";
+  import { list } from "@/api/productionManagement/productionProcess";
+  import { ElMessage } from "element-plus";
+
+  defineComponent({
+    name: "StructureEdit",
+  });
+
+  const ProductSelectDialog = defineAsyncComponent(
+    () => import("@/views/basicData/product/ProductSelectDialog.vue")
+  );
+  const form = ref();
+
+  const props = defineProps({
+    showModel: {
+      type: Boolean,
+      default: false,
+    },
+    record: {
+      type: Object,
+      required: true,
+    },
+  });
+
+  const emits = defineEmits(["update:showModel"]);
+  const visible = computed({
+    get() {
+      return props.showModel;
+    },
+    set(val) {
+      emits("update:showModel", val);
+    },
+  });
+
+  const dataValue = reactive({
+    dataList: [],
+    productOptions: [],
+    processOptions: [],
+    showProductDialog: false,
+    currentRowIndex: null,
+    loading: false,
+    isEdit: false,
+  });
+
+  const tableData = [
+    {
+      productName: props.record.productName,
+      model: props.record.model,
+      unit: props.record.unit,
+      productCode: props.record.productCode,
+    }
+  ]
+
+  const openDialog = index => {
+    dataValue.currentRowIndex = index;
+    dataValue.showProductDialog = true;
+  };
+
+  const fetchData = async () => {
+    const { data } = await queryList(props.record.id);
+    dataValue.dataList = data;
+  };
+
+  const fetchProcessOptions = async () => {
+    const { data } = await list(props.record.id);
+    dataValue.processOptions = data;
+  };
+
+  const handleProduct = row => {
+    if (row?.length > 1) {
+      ElMessage.error("鍙兘閫夋嫨涓�涓骇鍝�");
+    }
+    dataValue.dataList[dataValue.currentRowIndex].productName =
+      row[0].productName;
+    dataValue.dataList[dataValue.currentRowIndex].model = row[0].model;
+    dataValue.dataList[dataValue.currentRowIndex].productModelId = row[0].id;
+    dataValue.showProductDialog = false;
+  };
+
+  const submit = () => {
+    form.value
+      .validate(valid => {
+        dataValue.loading = true;
+        if (valid) {
+          add({
+            parentId: props.record.id,
+            productStructureList: dataValue.dataList || [],
+          }).then(res => {
+            ElMessage.success("淇濆瓨鎴愬姛");
+            visible.value = false;
+            dataValue.loading = false;
+          });
+        }
+      })
+      .finally(() => {
+        dataValue.loading = false;
+      });
+  };
+
+  const addItem = () => {
+    dataValue.dataList.push({
+      productName: "",
+      productId: "",
+      model: undefined,
+      productModelId: undefined,
+      processId: "",
+      unitQuantity: 0,
+      demandedQuantity: 0,
+      unit: "",
+      diskQuantity: 0,
+    });
+  };
+
+  const cancelEdit = () => {
+    dataValue.isEdit = false;
+    dataValue.dataList = dataValue.dataList.filter(item => item.id !== undefined);
+  };
+
+  onMounted(() => {
+    fetchData();
+    fetchProcessOptions();
+  });
+</script>
\ No newline at end of file
diff --git a/src/views/productionManagement/productStructure/index.vue b/src/views/productionManagement/productStructure/index.vue
new file mode 100644
index 0000000..e32ff8d
--- /dev/null
+++ b/src/views/productionManagement/productStructure/index.vue
@@ -0,0 +1,113 @@
+<template>
+  <div class="app-container">
+    <PIMTable
+        rowKey="id"
+        :column="tableColumn"
+        :tableData="tableData"
+        :page="page"
+        :isSelection="true"
+        @selection-change="handleSelectionChange"
+        :tableLoading="tableLoading"
+        @pagination="pagination"
+    >
+      <template #detail="{row}">
+        <el-button
+            type="primary"
+            text
+            @click="showDetail(row.id)">{{ row.productName }}
+        </el-button>
+      </template>
+    </PIMTable>
+    <StructureEdit v-if="showEdit" v-model:show-model="showEdit" :record="currentRow"/>
+  </div>
+</template>
+
+<script setup>
+import {ref} from "vue";
+import {productModelList} from "@/api/basicData/productModel.js";
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
+const StructureEdit = defineAsyncComponent(() => import('@/views/productionManagement/productStructure/StructureEdit.vue'))
+
+const tableColumn = ref([
+  {
+    label: "浜у搧缂栫爜",
+    prop: "productCode",
+    slot: "detail"
+  },
+  {
+    label: "浜у搧鍚嶇О",
+    prop: "productName",
+    dataType: 'slot',
+    slot: "detail"
+  },
+  {
+    label: "瑙勬牸鍨嬪彿",
+    prop: "model",
+  },
+  {
+    label: "鍗曚綅",
+    prop: "unit",
+  }
+]);
+const tableData = ref([]);
+const tableLoading = ref(false);
+const showEdit = ref(false);
+const selectedRows = ref([]);
+const currentRow = ref({});
+const page = reactive({
+  current: 1,
+  size: 10,
+  total: 0,
+});
+const data = reactive({
+  form: {
+    productName: "",
+  },
+  rules: {
+    productName: [{required: true, message: "璇疯緭鍏�", trigger: "blur"}],
+  },
+  modelForm: {
+    otherModel: '',
+    model: "",
+    unit: "",
+    speculativeTradingName: [],
+  },
+});
+const {form, rules} = toRefs(data);
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const handleSelectionChange = (selection) => {
+  selectedRows.value = selection;
+};
+
+// 鏌ヨ瑙勬牸鍨嬪彿
+const pagination = (obj) => {
+  page.current = obj.page;
+  page.size = obj.limit;
+  getModelList();
+};
+
+const showDetail = (id) => {
+  router.push({
+    path: '/productionManagement/productStructureDetail',
+    query: {
+      id: id
+    }
+  })
+}
+const getModelList = () => {
+  tableLoading.value = true;
+  productModelList({
+    current: page.current,
+    size: page.size,
+  }).then((res) => {
+    tableData.value = res.records;
+    page.total = res.total;
+    tableLoading.value = false;
+  });
+};
+onMounted(() => {
+  getModelList();
+})
+</script>
diff --git a/src/views/productionManagement/productionCosting/index.vue b/src/views/productionManagement/productionCosting/index.vue
index 76e7414..a597317 100644
--- a/src/views/productionManagement/productionCosting/index.vue
+++ b/src/views/productionManagement/productionCosting/index.vue
@@ -14,6 +14,15 @@
 					clearable
 					prefix-icon="Search"
 				/>
+				<span class="search_title ml10">鍚堝悓鍙凤細</span>
+				<el-input
+					v-model="searchForm.salesContractNo"
+					style="width: 240px"
+					placeholder="璇疯緭鍏�"
+					@change="handleQuery"
+					clearable
+					prefix-icon="Search"
+				/>
 				<el-button type="primary" @click="handleQuery" style="margin-left: 10px"
 				>鎼滅储</el-button
 				>
@@ -61,21 +70,21 @@
 		prop: "salesContractNo",
 		width: 220,
 	},
-	{
-		label: "瀹㈡埛鍚堝悓鍙�",
-		prop: "customerContractNo",
-		width: 250,
-	},
+	// {
+	// 	label: "瀹㈡埛鍚堝悓鍙�",
+	// 	prop: "customerContractNo",
+	// 	width: 250,
+	// },
 	{
 		label: "瀹㈡埛鍚嶇О",
 		prop: "customerName",
 		width: 250,
 	},
-	{
-		label: "椤圭洰鍚嶇О",
-		prop: "projectName",
-		width:300
-	},
+	// {
+	// 	label: "椤圭洰鍚嶇О",
+	// 	prop: "projectName",
+	// 	width:300
+	// },
 	{
 		label: "浜у搧澶х被",
 		prop: "productCategory",
@@ -121,6 +130,7 @@
 const data = reactive({
 	searchForm: {
 		schedulingUserName: "",
+		salesContractNo: "",
 		entryDate: [
 			dayjs().format("YYYY-MM-DD"),
 			dayjs().add(1, "day").format("YYYY-MM-DD"),
diff --git a/src/views/productionManagement/productionDispatching/components/autoDispatchDia.vue b/src/views/productionManagement/productionDispatching/components/autoDispatchDia.vue
new file mode 100644
index 0000000..b4a76f6
--- /dev/null
+++ b/src/views/productionManagement/productionDispatching/components/autoDispatchDia.vue
@@ -0,0 +1,153 @@
+<template>
+  <div>
+    <el-dialog
+        v-model="dialogFormVisible"
+        title="鑷姩娲惧伐"
+        width="80%"
+        @close="closeDia"
+    >
+      <el-form :model="form" label-width="140px" label-position="top" ref="formRef">
+        <el-divider content-position="left">娲惧伐鍒楄〃</el-divider>
+        
+        <el-table
+            :data="dispatchList"
+            border
+            style="width: 100%; margin-top: 20px;"
+            :row-class-name="tableRowClassName"
+        >
+          <el-table-column label="搴忓彿" type="index" width="60" align="center" />
+          <el-table-column label="鍚堝悓鍙�" prop="salesContractNo" width="200" />
+          <el-table-column label="瀹㈡埛鍚嶇О" prop="customerName" width="200" />
+          <!-- <el-table-column label="椤圭洰鍚嶇О" prop="projectName" width="250" /> -->
+          <el-table-column label="浜у搧澶х被" prop="productCategory" width="150" />
+          <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" width="200" />
+          <el-table-column label="缁戝畾鏈哄櫒" prop="speculativeTradingName" width="120" />
+          <el-table-column label="鎬绘暟閲�" prop="quantity" width="100" align="right" />
+          <el-table-column label="宸叉帓浜�" prop="schedulingNum" width="100" align="right" fixed="right" />
+          <el-table-column label="寰呮帓浜�" prop="pendingQuantity" width="100" align="right" fixed="right" />
+          <el-table-column label="鏈鎺掍骇" width="150" align="center" fixed="right">
+            <template #default="{ row }">
+              <el-input-number
+                  v-model="row.schedulingNum"
+                  :min="0"
+                  :max="row.pendingQuantity"
+                  :step="1"
+                  :precision="0"
+                  size="small"
+                  style="width: 120px"
+                  @change="(value) => changeCurrentNum(value, row)"
+              />
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-form>
+      
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">纭娲惧伐</el-button>
+          <el-button @click="closeDia">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {ref, reactive, toRefs, computed} from "vue";
+import {productionDispatch, productionDispatchList} from "@/api/productionManagement/productionOrder.js";
+
+const { proxy } = getCurrentInstance()
+const emit = defineEmits(['close'])
+
+const dialogFormVisible = ref(false);
+const operationType = ref('')
+
+const data = reactive({
+  form: {},
+  dispatchList: [], // 娲惧伐鍒楄〃鏁版嵁
+});
+
+const { form, dispatchList } = toRefs(data);
+
+
+// 琛ㄦ牸琛屾牱寮�
+const tableRowClassName = ({ rowIndex }) => {
+  if (rowIndex % 2 === 1) {
+    return 'even-row'
+  }
+  return ''
+}
+
+// 淇敼鏈鎺掍骇鏁伴噺
+const changeCurrentNum = (value, row) => {
+  if (value > row.pendingQuantity) {
+    row.schedulingNum = row.pendingQuantity
+    proxy.$modal.msgWarning('鎺掍骇鏁伴噺涓嶅彲澶т簬寰呮帓浜ф暟閲�')
+  }
+}
+
+// 鎵撳紑寮规
+const openDialog = (type, rows) => {
+  operationType.value = type;
+  dialogFormVisible.value = true;
+  
+  // 澶勭悊浼犲叆鐨勬暟鎹�
+  dispatchList.value = rows.map(row => ({
+    ...row,
+    schedulingNum: 0, // 鍒濆鍖栨湰娆℃帓浜ф暟閲忎负0
+    pendingQuantity: (Number(row.quantity) || 0) - (Number(row.schedulingNum) || 0) // 璁$畻寰呮帓浜ф暟閲�
+  }))
+}
+
+// 鎻愪氦琛ㄥ崟
+const submitForm = () => {
+  // 妫�鏌ユ槸鍚︽湁鎺掍骇鏁版嵁
+  const hasSchedulingData = dispatchList.value.some(item => item.schedulingNum > 0)
+  if (!hasSchedulingData) {
+    proxy.$modal.msgWarning('璇疯嚦灏戜负涓�鏉¤褰曡缃帓浜ф暟閲�')
+    return
+  }
+  
+  // 鏋勯�犳彁浜ゆ暟鎹� - 鐩存帴浼犻�掓暟缁勶紝涓嶈繃婊�
+  const submitData = dispatchList.value
+  
+  console.log('鎻愪氦鑷姩娲惧伐鏁版嵁:', submitData)
+  
+  // 璋冪敤API锛堣繖閲岄渶瑕佹牴鎹疄闄呮帴鍙h皟鏁达級
+  productionDispatchList(submitData).then(res => {
+    proxy.$modal.msgSuccess(res.msg);
+    closeDia();
+  }).catch(err => {
+    proxy.$modal.msgError("娲惧伐澶辫触");
+    console.error('娲惧伐澶辫触:', err);
+  })
+}
+
+// 鍏抽棴寮规
+const closeDia = () => {
+  proxy.resetForm("formRef");
+  dialogFormVisible.value = false;
+  dispatchList.value = []
+  emit('close')
+};
+
+defineExpose({
+  openDialog,
+});
+</script>
+
+<style scoped>
+:deep(.even-row) {
+  background-color: #fafafa;
+}
+
+:deep(.el-table .cell) {
+  padding: 8px 12px;
+}
+
+:deep(.el-table th) {
+  background-color: #f5f7fa;
+  color: #606266;
+  font-weight: 600;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/productionManagement/productionDispatching/components/formDia.vue b/src/views/productionManagement/productionDispatching/components/formDia.vue
index a60f751..971bc6e 100644
--- a/src/views/productionManagement/productionDispatching/components/formDia.vue
+++ b/src/views/productionManagement/productionDispatching/components/formDia.vue
@@ -7,7 +7,7 @@
         @close="closeDia"
     >
       <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
-        <el-row :gutter="30">
+        <!-- <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="椤圭洰鍚嶇О锛�" prop="projectName">
               <el-input v-model="form.projectName" placeholder="璇疯緭鍏�" clearable disabled/>
@@ -16,6 +16,18 @@
           <el-col :span="12">
             <el-form-item label="浜у搧澶х被锛�" prop="productCategory">
               <el-input v-model="form.productCategory" placeholder="璇疯緭鍏�" clearable disabled/>
+            </el-form-item>
+          </el-col>
+        </el-row> -->
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="瑙勬牸鍨嬪彿锛�" prop="specificationModel">
+              <el-input v-model="form.specificationModel" placeholder="璇疯緭鍏�" clearable disabled/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="缁戝畾鏈哄櫒锛�" prop="speculativeTradingName">
+              <el-input v-model="form.speculativeTradingName" placeholder="鑷姩鑾峰彇" clearable disabled/>
             </el-form-item>
           </el-col>
         </el-row>
@@ -46,6 +58,11 @@
 							/>
 						</el-form-item>
           </el-col>
+          <el-col :span="12">
+            <el-form-item label="浜у搧澶х被锛�" prop="productCategory">
+              <el-input v-model="form.productCategory" placeholder="璇疯緭鍏�" clearable disabled/>
+            </el-form-item>
+          </el-col>
         </el-row>
         <el-row :gutter="30">
 					<el-col :span="12">
@@ -54,6 +71,9 @@
 								v-model="form.schedulingUserId"
 								placeholder="閫夋嫨浜哄憳"
 								style="width: 100%;"
+                filterable
+                default-first-option
+                :reserve-keyword="false"
 							>
 								<el-option
 									v-for="user in userList"
@@ -105,11 +125,13 @@
   form: {
 		projectName: "",
 		productCategory: "",
+		specificationModel: "", // 瑙勬牸鍨嬪彿
 		quantity: "",
 		schedulingNum: "",
 		schedulingUserId: "",
 		schedulingDate: "",
 		pendingQuantity: "",
+		speculativeTradingName: "", // 缁戝畾鏈哄櫒鍚嶇О
   },
   rules: {
 		schedulingNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" },],
@@ -121,6 +143,7 @@
 const userList = ref([])
 const userStore = useUserStore()
 
+
 // 鎵撳紑寮规
 const openDialog = (type, row) => {
   operationType.value = type;
diff --git a/src/views/productionManagement/productionDispatching/index.vue b/src/views/productionManagement/productionDispatching/index.vue
index 527880f..2d39890 100644
--- a/src/views/productionManagement/productionDispatching/index.vue
+++ b/src/views/productionManagement/productionDispatching/index.vue
@@ -1,5 +1,32 @@
 <template>
 	<div class="app-container">
+		<!-- 鐐掓満1-4 灞曠ず锛堟�婚噺 / 姝e湪鐢熶骇閲� / 绌轰綑閲忥級 -->
+		<div class="machines-grid">
+			<div v-for="machine in machines" :key="machine.id" class="machine-card">
+				<div class="machine-title">{{ machine.name }}</div>
+				<div class="machine-metrics">
+				<div class="machine-control">
+					<span>鎬婚噺(kg)锛�</span>
+					<el-input-number v-model="machineData[machine.name].workLoad" :min="0" :step="1" size="small" />
+				</div>
+				<div><span> 棰勮鎶曞叆閲�(kg)锛�</span><span>{{ machineData[machine.name].currentWorkLoad }}</span></div>
+				<div><span>绌轰綑宸ヤ綔閲�(kg)锛�</span><span>{{ machineData[machine.name].vacant }}</span></div>
+			</div>
+			</div>
+			<div class="save-button-container">
+				<div class="loss-rate-container">
+					<span class="loss-rate-label">鎹熻�楃巼(%)锛�</span>
+					<el-select v-model="rate" placeholder="璇烽�夋嫨鎹熻�楃巼" style="width: 120px" size="small">
+						<el-option label="6" :value="6" />
+						<el-option label="7" :value="7" />
+						<el-option label="8" :value="8" />
+						<el-option label="9" :value="9" />
+						<el-option label="10" :value="10" />
+					</el-select>
+				</div>
+				<el-button type="primary" @click="saveMachineTotals" size="small">淇濆瓨璁剧疆</el-button>
+			</div>
+		</div>
 		<div class="search_form">
 			<div>
 				<span class="search_title">瀹㈡埛鍚嶇О锛�</span>
@@ -11,24 +38,40 @@
 					clearable
 					prefix-icon="Search"
 				/>
-				<span class="search_title ml10">椤圭洰鍚嶇О锛�</span>
+				<span class="search_title ml10">鍚堝悓鍙凤細</span>
 				<el-input
-					v-model="searchForm.projectName"
+					v-model="searchForm.salesContractNo"
 					style="width: 240px"
 					placeholder="璇疯緭鍏�"
 					@change="handleQuery"
 					clearable
 					prefix-icon="Search"
 				/>
+<!--				<span class="search_title ml10">椤圭洰鍚嶇О锛�</span>-->
+<!--				<el-input-->
+<!--					v-model="searchForm.projectName"-->
+<!--					style="width: 240px"-->
+<!--					placeholder="璇疯緭鍏�"-->
+<!--					@change="handleQuery"-->
+<!--					clearable-->
+<!--					prefix-icon="Search"-->
+<!--				/>-->
 				<span class="search_title ml10">褰曞叆鏃ユ湡锛�</span>
 				<el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
-												placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
+										placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
+				<el-checkbox
+					style="margin-left: 10px"
+					v-model="searchForm.status"
+					label="涓嶆樉绀哄緟鎺掓暟閲忎负0"
+					@change="handleQuery"
+				/>
 				<el-button type="primary" @click="handleQuery" style="margin-left: 10px">鎼滅储</el-button>
 			</div>
 			<div>
-				<el-button type="primary" @click="openForm('add')">鐢熶骇娲惧伐</el-button>
-				<el-button @click="handleOut">瀵煎嚭</el-button>
-			</div>
+			<el-button type="primary" @click="openForm('add')">鐢熶骇娲惧伐</el-button>
+			<el-button type="success" @click="openAutoDispatch">鑷姩娲惧伐</el-button>
+			<el-button @click="handleOut">瀵煎嚭</el-button>
+		</div>
 		</div>
 		<div class="table_list">
 			<PIMTable
@@ -44,23 +87,27 @@
 			></PIMTable>
 		</div>
 		<form-dia ref="formDia" @close="handleQuery"></form-dia>
+		<auto-dispatch-dia ref="autoDispatchDia" @close="handleQuery"></auto-dispatch-dia>
 	</div>
 </template>
 
 <script setup>
-import {onMounted, ref} from "vue";
+import {onMounted, ref, reactive, toRefs, getCurrentInstance, nextTick, computed, watch} from "vue";
 import FormDia from "@/views/productionManagement/productionDispatching/components/formDia.vue";
+import AutoDispatchDia from "@/views/productionManagement/productionDispatching/components/autoDispatchDia.vue";
 import dayjs from "dayjs";
-import {schedulingListPage} from "@/api/productionManagement/productionOrder.js";
+import {schedulingListPage, schedulingList, addSpeculatTrading, updateSpeculatTrading, getLossRate, addLossRate, updateLossRate} from "@/api/productionManagement/productionOrder.js";
 import { ElMessageBox } from "element-plus";
 
 const data = reactive({
 	searchForm: {
 		customerName: "",
+		salesContractNo: "",
 		projectName: "",
-		entryDate: null, // 褰曞叆鏃ユ湡
-		entryDateStart: undefined,
-		entryDateEnd: undefined,
+		status: true,
+		entryDate: [dayjs().format("YYYY-MM-DD"), dayjs().format("YYYY-MM-DD")], // 褰曞叆鏃ユ湡锛岄粯璁ゅ綋澶�
+		entryDateStart: dayjs().format("YYYY-MM-DD"),
+		entryDateEnd: dayjs().format("YYYY-MM-DD"),
 	},
 });
 const { searchForm } = toRefs(data);
@@ -71,19 +118,9 @@
 		width: 220,
 	},
 	{
-		label: "瀹㈡埛鍚堝悓鍙�",
-		prop: "customerContractNo",
-		width: 250,
-	},
-	{
 		label: "瀹㈡埛鍚嶇О",
 		prop: "customerName",
 		width: 250,
-	},
-	{
-		label: "椤圭洰鍚嶇О",
-		prop: "projectName",
-		width:300
 	},
 	{
 		label: "浜у搧澶х被",
@@ -93,7 +130,12 @@
 	{
 		label: "瑙勬牸鍨嬪彿",
 		prop: "specificationModel",
-		width: 220,
+		width: 120,
+	},
+	{
+		label: "缁戝畾鏈哄櫒",
+		prop: "speculativeTradingName",
+		width: 160,
 	},
 	{
 		label: "鍗曚綅",
@@ -104,6 +146,32 @@
 		label: "褰曞叆鏃ユ湡",
 		prop: "entryDate",
 		width: 120,
+	},
+	{
+		label: "鐘舵��",
+		prop: "status",
+		dataType: "tag",
+		formatType: (params) => {
+			if (params == '鐢熶骇涓�') {
+				return "warning";
+			} else if (params == '鏈紑濮�') {
+				return "danger";
+			} else {
+				return "success";
+			}
+		},
+	},
+	{
+		label: "鐢熶骇杩涘害",
+		prop: "progress",
+		formatData: (cellValue) => {
+			// 濡傛灉鍊间负绌烘垨undefined锛屾樉绀虹┖瀛楃涓�
+			if (cellValue === null || cellValue === undefined || cellValue === '') {
+				return '';
+			}
+			// 鐩存帴鍦ㄦ暟瀛楀悗闈㈡坊鍔犵櫨鍒嗗彿
+			return `${cellValue}%`;
+		}
 	},
 	{
 		label: "鏁伴噺",
@@ -118,6 +186,7 @@
 		label: "寰呮帓鏁伴噺",
 		prop: "pendingQuantity",
 		width: 100,
+		fixed: 'right',
 	},
 ]);
 const tableData = ref([]);
@@ -129,7 +198,140 @@
 	total: 0,
 });
 const formDia = ref()
+const autoDispatchDia = ref()
 const { proxy } = getCurrentInstance()
+
+// 鐐掓満鏁版嵁
+const machineData = reactive({
+	"鐐掓満1": { workLoad: 0, currentWorkLoad: 0, vacant: 0 },
+	"鐐掓満2": { workLoad: 0, currentWorkLoad: 0, vacant: 0 },
+	"鐐掓満3": { workLoad: 0, currentWorkLoad: 0, vacant: 0 },
+	"鐐掓満4": { workLoad: 0, currentWorkLoad: 0, vacant: 0 }
+})
+
+// 鐐掓満閰嶇疆鏁扮粍
+const machines = [
+	{ id: 1, name: '鐐掓満1' },
+	{ id: 2, name: '鐐掓満2' },
+	{ id: 3, name: '鐐掓満3' },
+	{ id: 4, name: '鐐掓満4' }
+]
+
+// 淇濆瓨鐐掓満鎬婚噺璁剧疆
+const saveMachineTotals = () => {
+	// 楠岃瘉鎹熻�楃巼鏄惁宸查�夋嫨
+	if (rate.value === null || rate.value === undefined || isNaN(rate.value)) {
+		proxy.$message.warning('璇烽�夋嫨鎹熻�楃巼');
+		return;
+	}
+	
+	// 鏋勯�犱繚瀛樻暟鎹暟缁勶紝浣跨敤machines鏁扮粍寰幆鏋勫缓
+	const saveData = machines.map(machine => {
+		const saveItem = {
+			name: machine.name, // 鐐掓満鍚嶇О
+			workLoad: machineData[machine.name].workLoad,  // 鎬婚噺
+			currentWorkLoad: machineData[machine.name].currentWorkLoad, //  棰勮鎶曞叆閲�
+			vacant: machineData[machine.name].vacant   // 绌轰綑閲�
+		};
+		
+		// 濡傛灉鏄慨鏀规搷浣滐紝闇�瑕佷紶閫抜d瀛楁
+		if (hasQueryData.value) {
+			const queryData = getMachineQueryData(machine.id);
+			if (queryData && queryData.id) {
+				saveItem.id = queryData.id;
+			}
+		}
+		
+		return saveItem;
+	});
+	
+	// 鏋勯�犳崯鑰楃巼鏁版嵁
+	const rateData = {
+		rate: rate.value
+	};
+	
+	// 濡傛灉鏈塈D锛岃鏄庢槸淇敼鎿嶄綔
+	if (rateId.value) {
+		rateData.id = rateId.value;
+	}
+	
+	// 鏍规嵁鏄惁鏈夋煡璇㈡暟鎹喅瀹氳皟鐢ㄦ柊澧炴帴鍙h繕鏄慨鏀规帴鍙�
+	const saveApi = hasQueryData.value ? updateSpeculatTrading : addSpeculatTrading;
+	const successMessage = hasQueryData.value ? '鐐掓満璁剧疆淇敼鎴愬姛' : '鐐掓満璁剧疆鏂板鎴愬姛';
+	
+	// 鏍规嵁鏄惁鏈塈D鍐冲畾璋冪敤鏂板鎺ュ彛杩樻槸淇敼鎺ュ彛
+	const rateApi = rateId.value ? updateLossRate : addLossRate;
+	const rateSuccessMessage = rateId.value ? '鎹熻�楃巼淇敼鎴愬姛' : '鎹熻�楃巼鏂板鎴愬姛';
+	
+	// 骞惰璋冪敤涓や釜鎺ュ彛
+	Promise.all([
+		saveApi(saveData),
+		rateApi(rateData)
+	]).then(([saveRes, rateRes]) => {
+		proxy.$message.success(successMessage);
+		proxy.$message.success(rateSuccessMessage);
+		
+		// 淇濆瓨鎴愬姛鍚庯紝璁剧疆hasQueryData涓簍rue锛屼笅娆′繚瀛樺皢璋冪敤淇敼鎺ュ彛
+		if (!hasQueryData.value) {
+			hasQueryData.value = true;
+		}
+		
+		// 濡傛灉杩斿洖浜咺D锛屼繚瀛樿捣鏉�
+		if (rateRes && rateRes.data && rateRes.data.id) {
+			rateId.value = rateRes.data.id;
+		}
+		
+		// 淇濆瓨鎴愬姛鍚庨噸鏂拌皟鐢ㄦ煡璇㈤〉闈�
+		getList();
+	}).catch(err => {
+		proxy.$message.error('淇濆瓨澶辫触');
+		console.error('淇濆瓨澶辫触:', err);
+	});
+}
+
+// 鑾峰彇鐐掓満鏌ヨ鏁版嵁
+const machineQueryData = ref([]);
+
+const getMachineQueryData = (machineId) => {
+	return machineQueryData.value.find(item => item.id === machineId);
+};
+
+const getMachineIndex = (item) => {
+	// 鍏煎澶氱瀛楁鍛藉悕锛岃繑鍥� 1-4 涔嬩竴锛屽惁鍒欒繑鍥� 0锛堟湭鐭ワ級
+	const candidates = [item.machineId, item.machineNo, item.machine, item.deviceNo, item.deviceId]
+	for (const v of candidates) {
+		if (v === undefined || v === null) continue
+		const n = Number(String(v).replace(/[^\d]/g, "")) // 鎶藉彇鏁板瓧
+		if ([1,2,3,4].includes(n)) return n
+	}
+	return 0
+}
+
+const computeTodaySummary = () => {
+	const todayStr = dayjs().format("YYYY-MM-DD")
+	
+	// 閲嶇疆鎵�鏈夌倰鏈烘暟鎹�
+	machines.forEach(machine => {
+		machineData[machine.name] = { workLoad: 0, currentWorkLoad: 0, vacant: 0 }
+	})
+	
+	tableData.value.forEach(item => {
+		// 浠呯粺璁″綋澶�
+		const isToday = dayjs(item.entryDate).format("YYYY-MM-DD") === todayStr
+		if (!isToday) return
+		
+		// 浣跨敤姝g‘鐨勫瓧娈靛悕锛歸orkLoad锛堢倰鏈哄伐浣滈噺锛�, currentWorkLoad锛堢倰鏈烘鍦ㄥ伐浣滈噺锛�
+		const workLoad = Number(item.workLoad) || 0
+		const currentWorkLoad = Number(item.currentWorkLoad) || 0
+		const machineName = item.speculativeTradingName || '鐐掓満1'
+		
+		if (machineData[machineName]) {
+			machineData[machineName].workLoad += workLoad
+			machineData[machineName].currentWorkLoad += currentWorkLoad
+			machineData[machineName].vacant = machineData[machineName].workLoad - machineData[machineName].currentWorkLoad
+		}
+	})
+}
 
 // 鏌ヨ鍒楄〃
 /** 鎼滅储鎸夐挳鎿嶄綔 */
@@ -137,6 +339,56 @@
 	page.current = 1;
 	getList();
 };
+
+// 鏄惁鏈夋煡璇㈡暟鎹�
+const hasQueryData = ref(false)
+// 鎹熻�楃巼
+const rate = ref(6)
+// 鎹熻�楃巼ID
+const rateId = ref(null)
+
+// 鑾峰彇鐐掓満姝e湪宸ヤ綔閲忔暟鎹�
+const getMachineProductionData = () => {
+	schedulingList().then((res) => {
+		// 澶勭悊鐐掓満姝e湪宸ヤ綔閲忔暟鎹�
+		if (res.data && Array.isArray(res.data)) {
+			// 璁剧疆鏄惁鏈夋煡璇㈡暟鎹�
+			hasQueryData.value = res.data.length > 0
+			
+			// 淇濆瓨鏌ヨ鏁版嵁鍒癿achineQueryData
+			machineQueryData.value = res.data;
+			
+			// 閲嶇疆鎵�鏈夌倰鏈烘暟鎹�
+			machines.forEach(machine => {
+				machineData[machine.name] = { workLoad: 0, currentWorkLoad: 0, vacant: 0 }
+			});
+			
+			// 閬嶅巻鏁版嵁锛屾牴鎹煡璇㈣繑鍥炵殑鏁版嵁缁撴瀯澶勭悊
+			res.data.forEach(item => {
+				// 鏍规嵁name瀛楁纭畾鐐掓満
+				const machineName = item.name || '鐐掓満1';
+				
+				if (machineData[machineName]) {
+					// 濡傛灉鏌ヨ鏁版嵁涓湁workLoad锛屽垯鍒濆鍖栫倰鏈烘�婚噺
+					if (item.workLoad !== null && item.workLoad !== undefined) {
+						machineData[machineName].workLoad = Number(item.workLoad) || 0;
+					}
+					
+					// 濡傛灉鏌ヨ鏁版嵁涓湁currentWorkLoad锛屽垯璁剧疆姝e湪宸ヤ綔閲�
+					if (item.currentWorkLoad !== null && item.currentWorkLoad !== undefined) {
+						machineData[machineName].currentWorkLoad = Number(item.currentWorkLoad) || 0;
+					}
+					
+					// 璁$畻绌轰綑宸ヤ綔閲�
+					machineData[machineName].vacant = machineData[machineName].workLoad - machineData[machineName].currentWorkLoad;
+				}
+			});
+		}
+	}).catch(err => {
+		console.error('鑾峰彇鐐掓満姝e湪宸ヤ綔閲忔暟鎹け璐�:', err);
+	});
+};
+
 const changeDaterange = (value) => {
 	if (value) {
 		searchForm.value.entryDateStart = value[0];
@@ -165,9 +417,33 @@
 			pendingQuantity: (Number(item.quantity) || 0) - (Number(item.schedulingNum) || 0)
 		}));
 		page.total = res.data.total;
+		computeTodaySummary()
+		
+		// 鍚屾椂鑾峰彇鐐掓満姝e湪宸ヤ綔閲忔暟鎹�
+		getMachineProductionData();
+		// 鑾峰彇鎹熻�楃巼鏁版嵁
+		getLossRateData();
 	}).catch(() => {
 		tableLoading.value = false;
 	})
+};
+
+// 鑾峰彇鎹熻�楃巼鏁版嵁
+const getLossRateData = () => {
+	getLossRate().then((res) => {
+		const data = res.data || res;
+		if (data && data.rate !== undefined && data.rate !== null) {
+			rate.value = Number(data.rate); // 纭繚杞崲涓烘暟瀛�
+			rateId.value = data.id || null;
+		} else {
+			rate.value = 6;
+			rateId.value = null;
+		}
+	}).catch(err => {
+		console.error('鑾峰彇鎹熻�楃巼鏁版嵁澶辫触:', err);
+		rate.value = 6;
+		rateId.value = null;
+	});
 };
 // 琛ㄦ牸閫夋嫨鏁版嵁
 const handleSelectionChange = (selection) => {
@@ -189,6 +465,26 @@
 	})
 };
 
+// 鎵撳紑鑷姩娲惧伐寮规
+const openAutoDispatch = () => {
+	if (selectedRows.value.length === 0) {
+		proxy.$message.error("璇烽�夋嫨鑷冲皯涓�鏉℃暟鎹�");
+		return;
+	}
+	
+	// 杩囨护鎺夊緟鎺掍骇鏁伴噺涓�0鐨勬暟鎹�
+	const validRows = selectedRows.value.filter(row => row.pendingQuantity > 0);
+	
+	if (validRows.length === 0) {
+		proxy.$message.warning("閫変腑鐨勬暟鎹棤闇�娲惧伐");
+		return;
+	}
+	
+	nextTick(() => {
+		autoDispatchDia.value?.openDialog('auto', validRows)
+	})
+};
+
 // 瀵煎嚭
 const handleOut = () => {
 	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
@@ -206,7 +502,129 @@
 
 onMounted(() => {
 	getList();
+	getLossRateData();
 });
 </script>
 
-<style scoped></style>
+<style scoped>
+.summary-bar{
+	display: flex;
+	gap: 16px;
+	margin: 10px 0 16px 0;
+}
+.summary-item{
+	background: #f5f7fa;
+	border: 1px solid #ebeef5;
+	border-radius: 6px;
+	padding: 10px 16px;
+	min-width: 160px;
+}
+.summary-label{
+	color: #909399;
+	font-size: 12px;
+	margin-bottom: 6px;
+}
+.summary-value{
+	color: #303133;
+	font-size: 20px;
+	font-weight: 600;
+}
+.summary-control{
+	display: flex;
+	align-items: center;
+	height: 28px;
+}
+.machines-grid{
+	display: grid;
+	grid-template-columns: repeat(4, 1fr);
+	gap: 16px;
+	margin-bottom: 20px;
+	padding: 16px;
+	background: #f8f9fa;
+	border-radius: 8px;
+	border: 1px solid #e9ecef;
+}
+.machine-card{
+	border: 1px solid #dee2e6;
+	border-radius: 8px;
+	padding: 16px;
+	background: #fff;
+	box-shadow: 0 2px 4px rgba(0,0,0,0.05);
+	transition: all 0.3s ease;
+}
+.machine-card:hover{
+	transform: translateY(-2px);
+	box-shadow: 0 4px 8px rgba(0,0,0,0.1);
+}
+.machine-title{
+	font-weight: 600;
+	font-size: 16px;
+	margin-bottom: 12px;
+	color: #2c3e50;
+	text-align: center;
+	padding-bottom: 8px;
+	border-bottom: 2px solid #3498db;
+}
+.machine-metrics{
+	display: flex;
+	flex-direction: column;
+	gap: 10px;
+	color: #495057;
+}
+.machine-control{
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	gap: 8px;
+	padding: 8px 0;
+	border-bottom: 1px solid #f1f3f4;
+}
+.machine-control span{
+	font-size: 14px;
+	white-space: nowrap;
+	color: #6c757d;
+	font-weight: 500;
+}
+.machine-metrics > div:not(.machine-control) {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	padding: 4px 0;
+	font-size: 14px;
+}
+.machine-metrics > div:not(.machine-control) span:first-child {
+	color: #6c757d;
+}
+.machine-metrics > div:not(.machine-control) span:last-child {
+	font-weight: 600;
+	color: #2c3e50;
+}
+.save-button-container{
+	grid-column: 1 / -1;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	gap: 16px;
+	margin-top: 16px;
+	padding-top: 16px;
+	border-top: 1px solid #e9ecef;
+}
+.loss-rate-container{
+	display: flex;
+	align-items: center;
+	gap: 8px;
+}
+.loss-rate-label{
+	font-size: 14px;
+	color: #6c757d;
+	font-weight: 500;
+	white-space: nowrap;
+}
+</style>
+
+
+
+
+
+
+
diff --git a/src/views/productionManagement/productionOrder/ProcessRouteItemForm.vue b/src/views/productionManagement/productionOrder/ProcessRouteItemForm.vue
new file mode 100644
index 0000000..354f9de
--- /dev/null
+++ b/src/views/productionManagement/productionOrder/ProcessRouteItemForm.vue
@@ -0,0 +1,555 @@
+<template>
+  <div>
+    <el-dialog v-model="isShow"
+               title="宸ヨ壓璺嚎椤圭洰"
+               width="800px"
+               @close="closeModal">
+      <div class="operate-button">
+        <el-button type="primary"
+                   @click="isShowProductSelectDialog = true"
+                   class="mb5"
+                   style="margin-bottom: 10px;">
+          閫夋嫨浜у搧
+        </el-button>
+        <el-switch v-model="isTable"
+                   inline-prompt
+                   active-text="琛ㄦ牸"
+                   inactive-text="鍒楄〃"
+                   @change="handleViewChange" />
+      </div>
+      <el-table v-if="isTable"
+                ref="multipleTable"
+                v-loading="tableLoading"
+                border
+                :data="routeItems"
+                :header-cell-style="{ background: '#F0F1F5', color: '#333333' }"
+                row-key="id"
+                tooltip-effect="dark"
+                class="lims-table"
+                style="cursor: move;">
+        <el-table-column align="center"
+                         label="搴忓彿"
+                         width="60">
+          <template #default="scope">
+            {{ scope.$index + 1 }}
+          </template>
+        </el-table-column>
+        <el-table-column v-for="(item, index) in tableColumn"
+                         :key="index"
+                         :label="item.label"
+                         :width="item.width"
+                         show-overflow-tooltip>
+          <template #default="scope"
+                    v-if="item.dataType === 'action'">
+            <el-button v-for="(op, opIndex) in item.operation"
+                       :key="opIndex"
+                       :type="op.type"
+                       :link="op.link"
+                       size="small"
+                       @click.stop="op.clickFun(scope.row)">
+              {{ op.name }}
+            </el-button>
+          </template>
+          <template #default="scope"
+                    v-else>
+            <template v-if="item.prop === 'processId'">
+              <el-select v-model="scope.row[item.prop]"
+                         style="width: 100%;"
+                         @mousedown.stop>
+                <el-option v-for="process in processOptions"
+                           :key="process.id"
+                           :label="process.name"
+                           :value="process.id" />
+              </el-select>
+            </template>
+            <template v-else>
+              {{ scope.row[item.prop] || '-' }}
+            </template>
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 浣跨敤鏅�歞iv鏇夸唬el-steps -->
+      <div v-else
+           ref="stepsContainer"
+           class="mb5 custom-steps"
+           style="padding: 10px 0; display: flex; flex-wrap: nowrap; gap: 20px; align-items: flex-start;">
+        <div v-for="(item, index) in routeItems"
+             :key="item.id"
+             class="custom-step draggable-step"
+             :data-id="item.id"
+             style="cursor: move; flex: 0 0 auto; min-width: 220px;">
+          <div class="step-content">
+            <div class="step-number">{{ index + 1 }}</div>
+            <el-card :header="item.productName"
+                     class="step-card"
+                     style="cursor: move;">
+              <div class="step-card-content">
+                <p>{{ item.model }}</p>
+                <p>{{ item.unit }}</p>
+                <el-select v-model="item.processId"
+                           style="width: 100%;"
+                           @mousedown.stop>
+                  <el-option v-for="process in processOptions"
+                             :key="process.id"
+                             :label="process.name"
+                             :value="process.id" />
+                </el-select>
+              </div>
+              <template #footer>
+                <div class="step-card-footer">
+                  <el-button type="danger"
+                             link
+                             size="small"
+                             @click.stop="removeItemByID(item.id)">鍒犻櫎</el-button>
+                </div>
+              </template>
+            </el-card>
+          </div>
+        </div>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="handleSubmit">纭</el-button>
+          <el-button @click="closeModal">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <ProductSelectDialog v-model="isShowProductSelectDialog"
+                         @confirm="handelSelectProducts" />
+  </div>
+</template>
+
+<script setup>
+  import {
+    ref,
+    computed,
+    getCurrentInstance,
+    onMounted,
+    onUnmounted,
+    nextTick,
+  } from "vue";
+  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+  import {
+    findProductProcessRouteItemList,
+    addOrUpdateProductProcessRouteItem,
+    deleteRouteItem,
+  } from "@/api/productionManagement/productProcessRoute.js";
+  import { processList } from "@/api/productionManagement/productionProcess.js";
+  import Sortable from "sortablejs";
+
+  const props = defineProps({
+    visible: {
+      type: Boolean,
+      required: true,
+      default: false,
+    },
+    record: {
+      type: Object,
+      required: true,
+      default: () => ({}),
+    },
+  });
+
+  const emit = defineEmits(["update:visible", "completed"]);
+
+  const processOptions = ref([]);
+  const tableLoading = ref(false);
+  const isShowProductSelectDialog = ref(false);
+  const routeItems = ref([]);
+  let tableSortable = null;
+  let stepsSortable = null;
+  const multipleTable = ref(null);
+  const stepsContainer = ref(null);
+  const isTable = ref(true);
+
+  const isShow = computed({
+    get() {
+      return props.visible;
+    },
+    set(val) {
+      emit("update:visible", val);
+    },
+  });
+
+  const tableColumn = ref([
+    { label: "浜у搧鍚嶇О", prop: "productName", width: 180 },
+    { label: "瑙勬牸鍚嶇О", prop: "model", width: 150 },
+    { label: "鍗曚綅", prop: "unit", width: 80 },
+    { label: "宸ュ簭鍚嶇О", prop: "processId", width: 180 },
+    {
+      dataType: "action",
+      label: "鎿嶄綔",
+      align: "center",
+      fixed: "right",
+      width: 100,
+      operation: [
+        {
+          name: "鍒犻櫎",
+          type: "danger",
+          link: true,
+          clickFun: row => {
+            console.log(row.id, "鍒犻櫎");
+
+            const dragSortx = routeItems.value.findIndex(
+              item => item.dragSort === row.dragSort
+            );
+            const idx = routeItems.value.findIndex(item => item.id === row.id);
+            console.log(idx, "idx");
+            if (row.id) {
+              deleteRouteItemByIds({ id: row.id }, idx);
+            } else {
+              removeItem(dragSortx);
+            }
+          },
+        },
+      ],
+    },
+  ]);
+
+  const removeItem = index => {
+    console.log("杞垹闄�", index);
+
+    routeItems.value.splice(index, 1);
+    updateDragSort();
+    nextTick(() => initSortable());
+  };
+
+  const removeItemByID = id => {
+    const idx = routeItems.value.findIndex(item => item.id === id);
+    if (idx > -1) {
+      routeItems.value.splice(idx, 1);
+      updateDragSort();
+      nextTick(() => initSortable());
+    }
+  };
+
+  const deleteRouteItemByIds = (ids, index) => {
+    deleteRouteItem(ids).then(res => {
+      routeItems.value.splice(index, 1);
+      updateDragSort();
+      nextTick(() => initSortable());
+    });
+  };
+
+  const closeModal = () => {
+    isShow.value = false;
+  };
+
+  const updateDragSort = () => {
+    routeItems.value.forEach((item, index) => {
+      item.dragSort = index + 1;
+    });
+    routeItems.value = [...routeItems.value];
+    console.log("鏇存柊鍚庣殑鏁扮粍:", routeItems.value);
+  };
+
+  const handelSelectProducts = products => {
+    destroySortable();
+
+    // 璁$畻鏂扮殑dragSort鍊艰捣濮嬬偣
+    const maxDragSort =
+      routeItems.value.length > 0
+        ? Math.max(...routeItems.value.map(item => item.dragSort || 0))
+        : 0;
+
+    const newData = products.map(({ id, ...product }, index) => ({
+      ...product,
+      productModelId: id,
+      routeId: props.record.id,
+      // id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
+      processId: undefined,
+      dragSort: maxDragSort + index + 1,
+    }));
+
+    console.log("閫夋嫨浜у搧鍓嶆暟缁�:", routeItems.value);
+    routeItems.value.push(...newData);
+    updateDragSort();
+    console.log("閫夋嫨浜у搧鍚庢暟缁�:", routeItems.value);
+
+    // 寤惰繜鍒濆鍖栵紝纭繚DOM瀹屽叏娓叉煋
+    nextTick(() => {
+      // 寮哄埗閲嶆柊娓叉煋缁勪欢
+      if (proxy?.$forceUpdate) {
+        proxy.$forceUpdate();
+      }
+
+      const temp = [...routeItems.value];
+      routeItems.value = [];
+      nextTick(() => {
+        routeItems.value = temp;
+        initSortable();
+      });
+    });
+  };
+
+  const findProcessRouteItems = () => {
+    tableLoading.value = true;
+    findProductProcessRouteItemList({ orderId: props.record.id })
+      .then(res => {
+        tableLoading.value = false;
+        routeItems.value = res.data.map(item => ({
+          ...item,
+          processId: item.processId === 0 ? undefined : item.processId,
+        }));
+        // 寤惰繜鍒濆鍖栵紝纭繚DOM瀹屽叏娓叉煋
+        nextTick(() => {
+          setTimeout(() => initSortable(), 100);
+        });
+      })
+      .catch(err => {
+        tableLoading.value = false;
+        console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
+      });
+  };
+
+  const findProcessList = () => {
+    processList({})
+      .then(res => {
+        processOptions.value = res.data;
+      })
+      .catch(err => {
+        console.error("鑾峰彇宸ュ簭澶辫触锛�", err);
+      });
+  };
+
+  const { proxy } = getCurrentInstance() || {};
+
+  const handleSubmit = () => {
+    const hasEmptyProcess = routeItems.value.some(item => !item.processId);
+    if (hasEmptyProcess) {
+      proxy?.$modal?.msgError("璇蜂负鎵�鏈夐」鐩�夋嫨宸ュ簭");
+      return;
+    }
+
+    addOrUpdateProductProcessRouteItem({
+      routeId: props.record.id,
+      processRouteItem: routeItems.value,
+    })
+      .then(res => {
+        isShow.value = false;
+        emit("completed");
+        proxy?.$modal?.msgSuccess("鎻愪氦鎴愬姛");
+      })
+      .catch(err => {
+        proxy?.$modal?.msgError(`鎻愪氦澶辫触锛�${err.msg || "缃戠粶寮傚父"}`);
+      });
+  };
+
+  const destroySortable = () => {
+    if (tableSortable) {
+      tableSortable.destroy();
+      tableSortable = null;
+    }
+    if (stepsSortable) {
+      stepsSortable.destroy();
+      stepsSortable = null;
+    }
+  };
+
+  const initSortable = () => {
+    destroySortable();
+
+    if (isTable.value) {
+      if (!multipleTable.value) return;
+      const tbody =
+        multipleTable.value.$el.querySelector(".el-table__body tbody") ||
+        multipleTable.value.$el.querySelector(
+          ".el-table__body-wrapper > table > tbody"
+        );
+      if (!tbody) return;
+
+      tableSortable = new Sortable(tbody, {
+        animation: 150,
+        ghostClass: "sortable-ghost",
+        handle: ".el-table__row",
+        filter: ".el-button, .el-select",
+        onEnd: evt => {
+          if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex])
+            return;
+
+          // 浣跨敤鏁扮粍 splice 鏂规硶閲嶆柊鎺掑簭锛屼笌琛ㄦ牸妯″紡淇濇寔涓�鑷�
+          const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
+          routeItems.value.splice(evt.newIndex, 0, moveItem);
+          updateDragSort();
+          console.log("鎺掑簭鍚庢暟缁�:", routeItems.value);
+        },
+      });
+    } else {
+      if (!stepsContainer.value) return;
+
+      // 淇敼锛氱洿鎺ヤ娇鐢╯tepsContainer.value浣滀负鎷栨嫿瀹瑰櫒
+      const stepsList = stepsContainer.value;
+      if (!stepsList) {
+        console.warn("鏈壘鍒版楠ゆ潯鎷栨嫿瀹瑰櫒");
+        return;
+      }
+
+      // 淇敼锛氱畝鍖栨嫋鎷介厤缃�
+      stepsSortable = new Sortable(stepsList, {
+        animation: 150,
+        ghostClass: "sortable-ghost",
+        draggable: ".draggable-step", // 鍙嫋鎷藉厓绱�
+        handle: ".draggable-step, .step-card", // 鎷栨嫿鎵嬫焺
+        filter: ".el-button, .el-select, .el-input", // 杩囨护鎸夐挳/閫夋嫨鍣�
+        forceFallback: true,
+        fallbackClass: "sortable-fallback",
+        preventOnFilter: true,
+        scroll: true,
+        scrollSensitivity: 30,
+        scrollSpeed: 10,
+        bubbleScroll: true,
+        onEnd: evt => {
+          if (evt.oldIndex === evt.newIndex || !routeItems.value[evt.oldIndex])
+            return;
+
+          // 浣跨敤鏁扮粍 splice 鏂规硶閲嶆柊鎺掑簭
+          const moveItem = routeItems.value.splice(evt.oldIndex, 1)[0];
+          routeItems.value.splice(evt.newIndex, 0, moveItem);
+          updateDragSort();
+        },
+      });
+
+      // 璋冭瘯锛氭墦鍗板鍣ㄥ拰瀹炰緥锛岀‘璁ょ粦瀹氭垚鍔�
+      console.log("姝ラ鏉℃嫋鎷藉鍣�:", stepsList);
+      console.log("Sortable瀹炰緥:", stepsSortable);
+    }
+  };
+
+  const handleViewChange = () => {
+    destroySortable();
+    // 寤惰繜鍒濆鍖栵紝纭繚瑙嗗浘鍒囨崲鍚嶥OM瀹屽叏娓叉煋
+    nextTick(() => {
+      setTimeout(() => initSortable(), 100);
+    });
+  };
+
+  onMounted(() => {
+    findProcessRouteItems();
+    findProcessList();
+  });
+
+  onUnmounted(() => {
+    destroySortable();
+  });
+
+  defineExpose({
+    closeModal,
+    handleSubmit,
+    isShow,
+  });
+</script>
+
+<style scoped>
+  :deep(.sortable-ghost) {
+    opacity: 0.6;
+    background-color: #f5f7fa !important;
+  }
+
+  :deep(.el-table__row) {
+    transition: background-color 0.2s;
+  }
+
+  :deep(.el-table__row:hover) {
+    background-color: #f9fafc !important;
+  }
+
+  :deep(.el-card__footer) {
+    padding: 0 !important;
+  }
+
+  .operate-button {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+
+  /* 淇敼锛氳嚜瀹氫箟姝ラ鏉″鍣ㄦ牱寮� */
+  .custom-steps {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: flex-start;
+    gap: 20px;
+    min-height: 100px;
+  }
+
+  /* 淇敼锛氳嚜瀹氫箟姝ラ椤规牱寮� */
+  .custom-step {
+    cursor: move !important;
+    padding: 8px;
+    position: relative;
+    transition: all 0.2s ease;
+    flex: 0 0 auto;
+    min-width: 220px;
+    touch-action: none;
+  }
+
+  /* 鎷栨嫿鎮诞鏍峰紡锛屾彁绀哄彲鎷栨嫿 */
+  .custom-step:hover {
+    background-color: rgba(64, 158, 255, 0.05);
+    transform: translateY(-2px);
+  }
+
+  .sortable-ghost {
+    opacity: 0.4;
+    background-color: #f5f7fa !important;
+    border: 2px dashed #409eff;
+    margin: 10px;
+    transform: scale(1.02);
+  }
+
+  .sortable-fallback {
+    opacity: 0.9;
+    background-color: #f5f7fa;
+    border: 1px solid #409eff;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+    transform: rotate(2deg);
+    margin: 10px;
+  }
+
+  .step-card {
+    cursor: move !important;
+    transition: box-shadow 0.2s ease;
+    user-select: none;
+    -webkit-user-select: none;
+    pointer-events: auto;
+    margin: 10px;
+    height: 240px;
+  }
+
+  .step-card:hover {
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+  }
+
+  .step-content {
+    width: 220px;
+    user-select: none;
+  }
+
+  .step-card-content {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+  }
+
+  .step-card-footer {
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    padding: 10px;
+  }
+
+  /* 鑷畾涔夊簭鍙锋牱寮忎紭鍖� */
+  .step-number {
+    font-weight: bold;
+    text-align: center;
+    width: 36px;
+    height: 36px;
+    line-height: 36px;
+    margin: 0 auto 10px;
+    background: #409eff;
+    color: #fff;
+    border-radius: 50%;
+    font-size: 14px;
+  }
+</style>
diff --git a/src/views/productionManagement/productionOrder/index.vue b/src/views/productionManagement/productionOrder/index.vue
index 9928d46..df7f950 100644
--- a/src/views/productionManagement/productionOrder/index.vue
+++ b/src/views/productionManagement/productionOrder/index.vue
@@ -1,198 +1,193 @@
 <template>
-	<div class="app-container">
-		<div class="search_form">
-			<div>
-				<span class="search_title">瀹㈡埛鍚嶇О锛�</span>
-				<el-input
-					v-model="searchForm.customerName"
-					style="width: 240px"
-					placeholder="璇疯緭鍏�"
-					@change="handleQuery"
-					clearable
-					prefix-icon="Search"
-				/>
-				<span class="search_title ml10">椤圭洰鍚嶇О锛�</span>
-				<el-input
-					v-model="searchForm.projectName"
-					style="width: 240px"
-					placeholder="璇疯緭鍏�"
-					@change="handleQuery"
-					clearable
-					prefix-icon="Search"
-				/>
-				<span class="search_title ml10">褰曞叆鏃ユ湡锛�</span>
-				<el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
-												placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
-				<el-button type="primary" @click="handleQuery" style="margin-left: 10px"
-				>鎼滅储</el-button
-				>
-			</div>
-			<div>
-				<el-button @click="handleOut">瀵煎嚭</el-button>
-			</div>
-		</div>
-		<div class="table_list">
-			<PIMTable
-				rowKey="id"
-				:column="tableColumn"
-				:tableData="tableData"
-				:page="page"
-				:tableLoading="tableLoading"
-				@pagination="pagination"
-			></PIMTable>
-		</div>
-	</div>
+  <div class="app-container">
+    <div class="search_form">
+      <el-form :model="searchForm"
+               :inline="true">
+        <el-form-item label="瀹㈡埛鍚嶇О:">
+          <el-input v-model="searchForm.customerName"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    prefix-icon="Search"
+                    style="width: 200px;"
+                    @change="handleQuery" />
+        </el-form-item>
+        <el-form-item label="鍚堝悓鍙�:">
+          <el-input v-model="searchForm.salesContractNo"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    prefix-icon="Search"
+                    style="width: 200px;"
+                    @change="handleQuery" />
+        </el-form-item>
+        <el-form-item label="浜у搧鍚嶇О:">
+          <el-input v-model="searchForm.productCategory"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    prefix-icon="Search"
+                    style="width: 200px;"
+                    @change="handleQuery" />
+        </el-form-item>
+        <el-form-item label="瑙勬牸:">
+          <el-input v-model="searchForm.specificationModel"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    prefix-icon="Search"
+                    style="width: 200px;"
+                    @change="handleQuery" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary"
+                     @click="handleQuery">鎼滅储</el-button>
+        </el-form-item>
+      </el-form>
+      <div>
+        <el-button @click="handleOut">瀵煎嚭</el-button>
+      </div>
+    </div>
+    <div class="table_list">
+      <PIMTable rowKey="id"
+                :column="tableColumn"
+                :tableData="tableData"
+                :page="page"
+                :tableLoading="tableLoading"
+                @pagination="pagination"></PIMTable>
+    </div>
+    <process-route-item-form v-if="isShowItemModal"
+                             v-model:visible="isShowItemModal"
+                             :record="record"
+                             @completed="getList" />
+  </div>
 </template>
 
 <script setup>
-import {onMounted, ref} from "vue";
-import { ElMessageBox } from "element-plus";
-import dayjs from "dayjs";
-import {schedulingListPage} from "@/api/productionManagement/productionOrder.js";
-const { proxy } = getCurrentInstance();
+  import { onMounted, ref } from "vue";
+  import { ElMessageBox } from "element-plus";
+  import dayjs from "dayjs";
+  import { productOrderListPage } from "@/api/productionManagement/productionOrder.js";
+  const { proxy } = getCurrentInstance();
+  import ProcessRouteItemForm from "@/views/productionManagement/productionOrder/ProcessRouteItemForm.vue";
 
-const tableColumn = ref([
-	{
-		label: "褰曞叆鏃ユ湡",
-		prop: "entryDate",
-		width: 120,
-	},
-	{
-		label: "鍚堝悓鍙�",
-		prop: "salesContractNo",
-		width: 220,
-	},
-	{
-		label: "瀹㈡埛鍚堝悓鍙�",
-		prop: "customerContractNo",
-		width: 250,
-	},
-	{
-		label: "瀹㈡埛鍚嶇О",
-		prop: "customerName",
-		width: 250,
-	},
-	{
-		label: "椤圭洰鍚嶇О",
-		prop: "projectName",
-		width:300
-	},
-	{
-		label: "浠樻鐘舵��",
-		prop: "status",
-		dataType: "tag",
-		formatType: (params) => {
-			if (params == '鏈畬鎴�') {
-				return "danger";
-			} else if (params == '宸插畬鎴�') {
-				return "success";
-			} else {
-				return null;
-			}
-		},
-	},
-	{
-		label: "浜у搧澶х被",
-		prop: "productCategory",
-		width: 160,
-	},
-	{
-		label: "瑙勬牸鍨嬪彿",
-		prop: "specificationModel",
-		width: 220,
-	},
-	{
-		label: "鍗曚綅",
-		prop: "unit",
-		width:90
-	},
-	{
-		label: "鏁伴噺",
-		prop: "quantity",
-	},
-	{
-		label: "鎺掍骇鏁伴噺",
-		prop: "schedulingNum",
-		width: 100,
-	},
-	{
-		label: "瀹屽伐鏁伴噺",
-		prop: "successNum",
-		width: 100,
-	},
-]);
-const tableData = ref([]);
-const tableLoading = ref(false);
-const page = reactive({
-	current: 1,
-	size: 100,
-	total: 0,
-});
+  const tableColumn = ref([
+    {
+      label: "鐢熶骇璁㈠崟鍙�",
+      prop: "npsNo",
+    },
+    {
+      label: "閿�鍞悎鍚屽彿",
+      prop: "salesContractNo",
+    },
+    {
+      label: "瀹㈡埛鍚嶇О",
+      prop: "customerName",
+    },
+    {
+      label: "浜у搧鍚嶇О",
+      prop: "productCategory",
+    },
+    {
+      label: "瑙勬牸",
+      prop: "specificationModel",
+    },
+    {
+      dataType: "action",
+      label: "鎿嶄綔",
+      align: "center",
+      fixed: "right",
+      width: 200,
+      operation: [
+        {
+          name: "宸ヨ壓璺嚎",
+          type: "text",
+          clickFun: row => {
+            showRouteItemModal(row);
+          },
+        },
+      ],
+    },
+  ]);
+  const tableData = ref([]);
+  const tableLoading = ref(false);
+  const page = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
 
-const data = reactive({
-	searchForm: {
-		customerName: "",
-		projectName: "",
-		entryDate: null, // 褰曞叆鏃ユ湡
-		entryDateStart: undefined,
-		entryDateEnd: undefined,
-	},
-});
-const { searchForm } = toRefs(data);
+  const data = reactive({
+    searchForm: {
+      customerName: "",
+      salesContractNo: "",
+      projectName: "",
+      productCategory: "",
+      specificationModel: "",
+    },
+  });
+  const { searchForm } = toRefs(data);
 
-// 鏌ヨ鍒楄〃
-/** 鎼滅储鎸夐挳鎿嶄綔 */
-const handleQuery = () => {
-	page.current = 1;
-	getList();
-};
-const pagination = (obj) => {
-	page.current = obj.page;
-	page.size = obj.limit;
-	getList();
-};
-const changeDaterange = (value) => {
-	if (value) {
-		searchForm.value.entryDateStart = value[0];
-		searchForm.value.entryDateEnd = value[1];
-	} else {
-		searchForm.value.entryDateStart = undefined;
-		searchForm.value.entryDateEnd = undefined;
-	}
-	handleQuery();
-};
-const getList = () => {
-	tableLoading.value = true;
-	// 鏋勯�犱竴涓柊鐨勫璞★紝涓嶅寘鍚玡ntryDate瀛楁
-	const params = { ...searchForm.value, ...page };
-	params.entryDate = undefined
-	schedulingListPage(params).then((res) => {
-		tableLoading.value = false;
-		tableData.value = res.data.records;
-		page.total = res.data.total;
-	}).catch(() => {
-		tableLoading.value = false;
-	})
-};
+  // 鏌ヨ鍒楄〃
+  /** 鎼滅储鎸夐挳鎿嶄綔 */
+  const handleQuery = () => {
+    page.current = 1;
+    getList();
+  };
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getList();
+  };
+  const changeDaterange = value => {
+    if (value) {
+      searchForm.value.entryDateStart = value[0];
+      searchForm.value.entryDateEnd = value[1];
+    } else {
+      searchForm.value.entryDateStart = undefined;
+      searchForm.value.entryDateEnd = undefined;
+    }
+    handleQuery();
+  };
+  const getList = () => {
+    tableLoading.value = true;
+    // 鏋勯�犱竴涓柊鐨勫璞★紝涓嶅寘鍚玡ntryDate瀛楁
+    const params = { ...searchForm.value, ...page };
+    params.entryDate = undefined;
+    productOrderListPage(params)
+      .then(res => {
+        tableLoading.value = false;
+        tableData.value = res.data.records;
+        page.total = res.data.total;
+      })
+      .catch(() => {
+        tableLoading.value = false;
+      });
+  };
 
-// 瀵煎嚭
-const handleOut = () => {
-	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
-		confirmButtonText: "纭",
-		cancelButtonText: "鍙栨秷",
-		type: "warning",
-	})
-		.then(() => {
-			proxy.download("/salesLedger/scheduling/export", {}, "鐢熶骇璁㈠崟.xlsx");
-		})
-		.catch(() => {
-			proxy.$modal.msg("宸插彇娑�");
-		});
-};
+  const isShowItemModal = ref(false);
+  const record = ref({});
+  const showRouteItemModal = row => {
+    isShowItemModal.value = true;
+    record.value = row;
+  };
 
-onMounted(() => {
-	getList();
-});
+  // 瀵煎嚭
+  const handleOut = () => {
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        proxy.download("/salesLedger/scheduling/export", {}, "鐢熶骇璁㈠崟.xlsx");
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+  };
+
+  const handleConfirmRoute = () => {};
+
+  onMounted(() => {
+    getList();
+  });
 </script>
 
 <style scoped lang="scss"></style>
diff --git a/src/views/productionManagement/productionProcess/Edit.vue b/src/views/productionManagement/productionProcess/Edit.vue
new file mode 100644
index 0000000..31e3109
--- /dev/null
+++ b/src/views/productionManagement/productionProcess/Edit.vue
@@ -0,0 +1,102 @@
+<template>
+  <div>
+    <el-dialog
+        v-model="isShow"
+        title="缂栬緫宸ュ簭"
+        width="400"
+        @close="closeModal"
+    >
+      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
+        <el-form-item
+            label="宸ュ簭鍚嶇О锛�"
+            prop="name"
+            :rules="[
+                {
+                required: true,
+                message: '璇疯緭鍏ュ伐搴忓悕绉�',
+              },
+              {
+                max: 100,
+                message: '鏈�澶�100涓瓧绗�',
+              }
+            ]">
+          <el-input v-model="formState.name" />
+        </el-form-item>
+        <el-form-item label="宸ヨ祫瀹氶" prop="salaryQuota">
+          <el-input v-model="formState.salaryQuota" type="number" :step="0.001" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="formState.remark" type="textarea" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="handleSubmit">纭</el-button>
+          <el-button @click="closeModal">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, getCurrentInstance } from "vue";
+import {update} from "@/api/productionManagement/productionProcess.js";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+  },
+
+  record: {
+    type: Object,
+    required: true,
+  }
+});
+
+const emit = defineEmits(['update:visible', 'completed']);
+
+// 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
+const formState = ref({
+  id: props.record.id,
+  name: props.record.name,
+  remark: props.record.remark,
+  salaryQuota: props.record.salaryQuota,
+});
+
+const isShow = computed({
+  get() {
+    return props.visible;
+  },
+  set(val) {
+    emit('update:visible', val);
+  },
+});
+
+let { proxy } = getCurrentInstance()
+
+const closeModal = () => {
+  isShow.value = false;
+};
+
+const handleSubmit = () => {
+  proxy.$refs["formRef"].validate(valid => {
+    if (valid) {
+      update(formState.value).then(res => {
+        // 鍏抽棴妯℃�佹
+        isShow.value = false;
+        // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
+        emit('completed');
+        proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+      })
+    }
+  })
+};
+
+defineExpose({
+  closeModal,
+  handleSubmit,
+  isShow,
+});
+</script>
diff --git a/src/views/productionManagement/productionProcess/New.vue b/src/views/productionManagement/productionProcess/New.vue
new file mode 100644
index 0000000..a73a755
--- /dev/null
+++ b/src/views/productionManagement/productionProcess/New.vue
@@ -0,0 +1,96 @@
+<template>
+  <div>
+    <el-dialog
+        v-model="isShow"
+        title="鏂板宸ュ簭"
+        width="400"
+        @close="closeModal"
+    >
+      <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
+        <el-form-item
+            label="宸ュ簭鍚嶇О锛�"
+            prop="name"
+            :rules="[
+                {
+                required: true,
+                message: '璇疯緭鍏ュ伐搴忓悕绉�',
+              },
+              {
+                max: 100,
+                message: '鏈�澶�100涓瓧绗�',
+              }
+            ]">
+          <el-input v-model="formState.name" />
+        </el-form-item>
+        <el-form-item label="宸ヨ祫瀹氶" prop="salaryQuota">
+          <el-input v-model="formState.salaryQuota" type="number" :step="0.001" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="formState.remark" type="textarea" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="handleSubmit">纭</el-button>
+          <el-button @click="closeModal">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, getCurrentInstance } from "vue";
+import {add} from "@/api/productionManagement/productionProcess.js";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+  },
+});
+
+const emit = defineEmits(['update:visible', 'completed']);
+
+// 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
+const formState = ref({
+  name: '',
+  remark: '',
+  salaryQuota:  '',
+});
+
+const isShow = computed({
+  get() {
+    return props.visible;
+  },
+  set(val) {
+    emit('update:visible', val);
+  },
+});
+
+let { proxy } = getCurrentInstance()
+
+const closeModal = () => {
+  isShow.value = false;
+};
+
+const handleSubmit = () => {
+  proxy.$refs["formRef"].validate(valid => {
+    if (valid) {
+      add(formState.value).then(res => {
+        // 鍏抽棴妯℃�佹
+        isShow.value = false;
+        // 鍛婄煡鐖剁粍浠跺凡瀹屾垚
+        emit('completed');
+        proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+      })
+    }
+  })
+};
+
+defineExpose({
+  closeModal,
+  handleSubmit,
+  isShow,
+});
+</script>
diff --git a/src/views/productionManagement/productionProcess/index.vue b/src/views/productionManagement/productionProcess/index.vue
new file mode 100644
index 0000000..ea2a341
--- /dev/null
+++ b/src/views/productionManagement/productionProcess/index.vue
@@ -0,0 +1,218 @@
+<template>
+  <div class="app-container">
+    <div class="search_form">
+      <el-form :model="searchForm"
+               :inline="true">
+        <el-form-item label="宸ュ簭鍚嶇О:">
+          <el-input v-model="searchForm.name"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    prefix-icon="Search"
+                    style="width: 200px;"
+                    @change="handleQuery" />
+        </el-form-item>
+        <el-form-item label="宸ュ簭缂栧彿:">
+          <el-input v-model="searchForm.no"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    prefix-icon="Search"
+                    style="width: 200px;"
+                    @change="handleQuery" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary"
+                     @click="handleQuery">鎼滅储</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <div class="table_list">
+      <div style="text-align: right"
+           class="mb10">
+        <el-button type="primary"
+                   @click="showNewModal">鏂板宸ュ簭</el-button>
+        <el-button type="danger"
+                   @click="handleDelete"
+                   :disabled="selectedRows.length === 0"
+                   plain>鍒犻櫎宸ュ簭</el-button>
+      </div>
+      <PIMTable rowKey="id"
+                :column="tableColumn"
+                :tableData="tableData"
+                :page="page"
+                :isSelection="true"
+                @selection-change="handleSelectionChange"
+                :tableLoading="tableLoading"
+                @pagination="pagination"
+                :total="page.total"></PIMTable>
+    </div>
+    <new-process v-if="isShowNewModal"
+                 v-model:visible="isShowNewModal"
+                 @completed="getList" />
+    <edit-process v-if="isShowEditModal"
+                  v-model:visible="isShowEditModal"
+                  :record="record"
+                  @completed="getList" />
+  </div>
+</template>
+
+<script setup>
+  import { onMounted, ref } from "vue";
+  import NewProcess from "@/views/productionManagement/productionProcess/New.vue";
+  import EditProcess from "@/views/productionManagement/productionProcess/Edit.vue";
+  import { listPage, del } from "@/api/productionManagement/productionProcess.js";
+
+  const data = reactive({
+    searchForm: {
+      name: "",
+      no: "",
+    },
+  });
+  const { searchForm } = toRefs(data);
+  const tableColumn = ref([
+    {
+      label: "宸ュ簭鍚嶇О",
+      prop: "name",
+    },
+    {
+      label: "宸ュ簭缂栧彿",
+      prop: "no",
+    },
+    {
+      label: "宸ヨ祫瀹氶",
+      prop: "salaryQuota",
+    },
+    {
+      label: "澶囨敞",
+      prop: "remark",
+    },
+    {
+      dataType: "action",
+      label: "鎿嶄綔",
+      align: "center",
+      fixed: "right",
+      width: 280,
+      operation: [
+        {
+          name: "缂栬緫",
+          type: "text",
+          clickFun: row => {
+            showEditModal(row);
+          },
+        },
+      ],
+    },
+  ]);
+  const tableData = ref([]);
+  const selectedRows = ref([]);
+  const tableLoading = ref(false);
+  const isShowNewModal = ref(false);
+  const isShowEditModal = ref(false);
+  const record = ref({});
+  const page = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
+  const { proxy } = getCurrentInstance();
+
+  // 鏌ヨ鍒楄〃
+  /** 鎼滅储鎸夐挳鎿嶄綔 */
+  const handleQuery = () => {
+    page.current = 1;
+    getList();
+  };
+
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getList();
+  };
+  const getList = () => {
+    tableLoading.value = true;
+    const params = { ...searchForm.value, ...page };
+    params.entryDate = undefined;
+    listPage(params)
+      .then(res => {
+        tableLoading.value = false;
+        tableData.value = res.data.records.map(item => ({
+          ...item,
+        }));
+        page.total = res.data.total;
+      })
+      .catch(err => {
+        tableLoading.value = false;
+      });
+  };
+  // 琛ㄦ牸閫夋嫨鏁版嵁
+  const handleSelectionChange = selection => {
+    selectedRows.value = selection;
+  };
+
+  // 鎵撳紑鏂板寮规
+  const showNewModal = () => {
+    isShowNewModal.value = true;
+  };
+
+  const showEditModal = row => {
+    isShowEditModal.value = true;
+    record.value = row;
+  };
+
+  // 鍒犻櫎
+  function handleDelete() {
+    const no = selectedRows.value.map(item => item.no);
+    const ids = selectedRows.value.map(item => item.id);
+    if (no.length > 2) {
+      proxy.$modal
+        .confirm(
+          '鏄惁纭鍒犻櫎宸ュ簭缂栧彿涓�"' +
+            no[0] +
+            "銆�" +
+            no[1] +
+            '"绛�' +
+            no.length +
+            "鏉℃暟鎹」锛�"
+        )
+        .then(function () {
+          return del(ids);
+        })
+        .then(() => {
+          getList();
+          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+        })
+        .catch(() => {});
+    } else {
+      proxy.$modal
+        .confirm('鏄惁纭鍒犻櫎宸ュ簭缂栧彿涓�"' + no + '"鐨勬暟鎹」锛�')
+        .then(function () {
+          return del(ids);
+        })
+        .then(() => {
+          getList();
+          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+        })
+        .catch(() => {});
+    }
+  }
+
+  // 瀵煎嚭
+  // const handleOut = () => {
+  // 	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+  // 		confirmButtonText: "纭",
+  // 		cancelButtonText: "鍙栨秷",
+  // 		type: "warning",
+  // 	})
+  // 		.then(() => {
+  // 			proxy.download("/salesLedger/scheduling/exportTwo", {}, "宸ュ簭鎺掍骇.xlsx");
+  // 		})
+  // 		.catch(() => {
+  // 			proxy.$modal.msg("宸插彇娑�");
+  // 		});
+  // };
+
+  onMounted(() => {
+    getList();
+  });
+</script>
+
+<style scoped></style>
diff --git a/src/views/productionManagement/productionReporting/Input.vue b/src/views/productionManagement/productionReporting/Input.vue
new file mode 100644
index 0000000..7959848
--- /dev/null
+++ b/src/views/productionManagement/productionReporting/Input.vue
@@ -0,0 +1,107 @@
+<template>
+  <div>
+    <el-dialog
+        v-model="isShow"
+        title="鎶曞叆"
+        @close="closeModal"
+    >
+      <PIMTable
+          rowKey="id"
+          :column="tableColumn"
+          :tableData="data"
+          :page="page"
+          :tableLoading="tableLoading"
+          @pagination="pagination"
+      ></PIMTable>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="closeModal">鍏抽棴</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {ref, computed, onMounted} from "vue";
+import { productionProductInputListPage } from "@/api/productionManagement/productionProductInput";
+
+const props = defineProps({
+  visible: {
+    type: Boolean,
+    required: true,
+  },
+  productionProductMainId: {
+    type: Number,
+    required: true,
+  },
+});
+
+const emit = defineEmits(['update:visible', 'completed']);
+
+const page = reactive({
+  current: 1,
+  size: 100,
+  total: 0
+});
+
+const pagination = (obj) => {
+  page.current = obj.page;
+  page.size = obj.limit;
+  fetchData();
+};
+
+const tableLoading = ref(false);
+
+const tableColumn = [
+  {
+    label: '鎶ュ伐鍗曞彿',
+    prop: 'productNo',
+  },
+  {
+    label: '浜у搧鍨嬪彿',
+    prop: 'model',
+  },
+  {
+    label: '鎶曞叆鏁伴噺',
+    prop: 'quantity',
+  },
+]
+
+const isShow = computed({
+  get() {
+    return props.visible;
+  },
+  set(val) {
+    emit('update:visible', val);
+  },
+});
+
+const data = ref([])
+
+const closeModal = () => {
+  isShow.value = false;
+};
+
+const fetchData = () => {
+  tableLoading.value = true;
+  const params = { productMainId: props.productionProductMainId, ...page };
+
+  productionProductInputListPage(params).then(res => {
+    tableLoading.value = false;
+    data.value = res.data.records;
+    page.total = res.data.total;
+  }).catch(err => {
+    tableLoading.value = false;
+  })
+};
+
+defineExpose({
+  closeModal,
+  isShow,
+});
+
+onMounted(() => {
+  fetchData()
+})
+</script>
diff --git a/src/views/productionManagement/productionReporting/Output.vue b/src/views/productionManagement/productionReporting/Output.vue
new file mode 100644
index 0000000..4eeac43
--- /dev/null
+++ b/src/views/productionManagement/productionReporting/Output.vue
@@ -0,0 +1,106 @@
+<template>
+  <div>
+    <el-dialog v-model="isShow"
+               title="浜у嚭"
+               @close="closeModal">
+      <PIMTable rowKey="id"
+                :column="tableColumn"
+                :tableData="data"
+                :page="page"
+                :tableLoading="tableLoading"
+                @pagination="pagination"></PIMTable>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary"
+                     @click="closeModal">鍏抽棴</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+  import { ref, computed, onMounted } from "vue";
+  import { productionProductOutputListPage } from "@/api/productionManagement/productionProductOutput.js";
+
+  const props = defineProps({
+    visible: {
+      type: Boolean,
+      required: true,
+    },
+    productionProductMainId: {
+      type: Number,
+      required: true,
+    },
+  });
+
+  const emit = defineEmits(["update:visible", "completed"]);
+
+  const page = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
+
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    fetchData();
+  };
+
+  const tableLoading = ref(false);
+
+  const tableColumn = [
+    {
+      label: "鎶ュ伐鍗曞彿",
+      prop: "productNo",
+    },
+    {
+      label: "浜у搧鍨嬪彿",
+      prop: "model",
+    },
+    {
+      label: "浜у嚭鏁伴噺",
+      prop: "quantity",
+    },
+  ];
+
+  const isShow = computed({
+    get() {
+      return props.visible;
+    },
+    set(val) {
+      emit("update:visible", val);
+    },
+  });
+
+  const data = ref([]);
+
+  const closeModal = () => {
+    isShow.value = false;
+  };
+
+  const fetchData = () => {
+    tableLoading.value = true;
+    const params = { productMainId: props.productionProductMainId, ...page };
+
+    productionProductOutputListPage(params)
+      .then(res => {
+        tableLoading.value = false;
+        data.value = res.data.records;
+        page.total = res.data.total;
+      })
+      .catch(err => {
+        tableLoading.value = false;
+      });
+  };
+
+  defineExpose({
+    closeModal,
+    isShow,
+  });
+
+  onMounted(() => {
+    fetchData();
+  });
+</script>
diff --git a/src/views/productionManagement/productionReporting/components/formDia.vue b/src/views/productionManagement/productionReporting/components/formDia.vue
index 89f6c76..2eb1c0b 100644
--- a/src/views/productionManagement/productionReporting/components/formDia.vue
+++ b/src/views/productionManagement/productionReporting/components/formDia.vue
@@ -13,8 +13,15 @@
               <el-input v-model="form.schedulingNum" placeholder="璇疯緭鍏�" clearable disabled/>
             </el-form-item>
           </el-col>
-          <el-col :span="12">
-            <el-form-item label="鏈鐢熶骇鏁伴噺锛�" prop="finishedNum">
+					<el-col :span="12">
+						<el-form-item label="寰呯敓浜ф暟閲忥細" prop="pendingNum">
+							<el-input v-model="form.pendingNum" placeholder="璇疯緭鍏�" clearable disabled/>
+						</el-form-item>
+					</el-col>
+        </el-row>
+        <el-row :gutter="30">
+					<el-col :span="12">
+						<el-form-item label="鏈鐢熶骇鏁伴噺锛�" prop="finishedNum">
 							<el-input-number
 								v-model="form.finishedNum"
 								placeholder="璇疯緭鍏�"
@@ -25,13 +32,18 @@
 								style="width: 100%"
 								@change="changeNum"
 							/>
+						</el-form-item>
+					</el-col>
+          <el-col :span="12">
+            <el-form-item label="鍗曚环(鍏�)锛�" prop="unitPrice">
+              <el-input v-model="form.unitPrice" placeholder="璇疯緭鍏�" clearable @input="calculateTotalPrice"/>
             </el-form-item>
           </el-col>
         </el-row>
         <el-row :gutter="30">
           <el-col :span="12">
-            <el-form-item label="寰呯敓浜ф暟閲忥細" prop="pendingNum">
-              <el-input v-model="form.pendingNum" placeholder="璇疯緭鍏�" clearable disabled/>
+            <el-form-item label="鎬讳环(鍏�)锛�" prop="totalPrice">
+              <el-input v-model="form.totalPrice" placeholder="璇疯緭鍏�" clearable disabled/>
             </el-form-item>
           </el-col>
         </el-row>
@@ -42,6 +54,9 @@
 								v-model="form.schedulingUserId"
 								placeholder="閫夋嫨浜哄憳"
 								style="width: 100%;"
+                filterable
+                default-first-option
+                :reserve-keyword="false"
 							>
 								<el-option
 									v-for="user in userList"
@@ -95,6 +110,8 @@
 		finishedNum: "",
 		schedulingUserId: "",
 		schedulingDate: "",
+		unitPrice: "",
+		totalPrice: "",
   },
   rules: {
 		schedulingNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" },],
@@ -118,6 +135,19 @@
 		proxy.$modal.msgWarning('鏈鐢熶骇鏁伴噺涓嶅彲澶т簬鎺掍骇鏁伴噺')
 	}
 	form.value.pendingNum = form.value.schedulingNum - form.value.finishedNum;
+	calculateTotalPrice();
+}
+
+// 璁$畻鎬讳环
+const calculateTotalPrice = () => {
+	const quantity = Number(form.value.finishedNum ?? 0);
+	const unitPrice = Number(form.value.unitPrice ?? 0);
+	
+	if (quantity > 0 && unitPrice > 0) {
+		form.value.totalPrice = (quantity * unitPrice).toFixed(2);
+	} else {
+		form.value.totalPrice = '0.00';
+	}
 }
 // 鎻愪氦浜у搧琛ㄥ崟
 const submitForm = () => {
diff --git a/src/views/productionManagement/productionReporting/index.vue b/src/views/productionManagement/productionReporting/index.vue
index 996a00b..b34b14e 100644
--- a/src/views/productionManagement/productionReporting/index.vue
+++ b/src/views/productionManagement/productionReporting/index.vue
@@ -1,427 +1,481 @@
 <template>
-	<div class="app-container">
-		<div class="search_form">
-			<el-form :model="searchForm" :inline="true">
-				<el-form-item label="瀹㈡埛鍚嶇О:">
-					<el-input v-model="searchForm.customerName" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
-										style="width: 200px;"
-										@change="handleQuery" />
-				</el-form-item>
-				<el-form-item label="椤圭洰鍚嶇О:">
-					<el-input v-model="searchForm.projectName" placeholder="璇疯緭鍏�" clearable prefix-icon="Search"
-										style="width: 200px;"
-										@change="handleQuery" />
-				</el-form-item>
-				<el-form-item label="鎺掍骇鏃ユ湡:">
-					<el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
-													placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
-				</el-form-item>
-				<el-form-item label="鐘舵��:">
-					<el-select v-model="searchForm.status" placeholder="璇烽�夋嫨鐘舵��" style="width: 140px" clearable>
-						<el-option label="寰呯敓浜�" :value="1"></el-option>
-						<el-option label="宸叉姤宸�" :value="3"></el-option>
-						<el-option label="鐢熶骇涓�" :value="2"></el-option>
-					</el-select>
-				</el-form-item>
-				<el-form-item>
-					<el-button type="primary" @click="handleQuery">鎼滅储</el-button>
-				</el-form-item>
-			</el-form>
-		</div>
-		<div class="table_list">
-			<div style="text-align: right" class="mb10">
-				<el-button type="primary" @click="openForm('add')">鐢熶骇鎶ュ伐</el-button>
-				<el-button @click="handleOut">瀵煎嚭</el-button>
-			</div>
-			<PIMTable
-				rowKey="id"
-				:column="tableColumn"
-				:tableData="tableData"
-				:page="page"
-				:isSelection="true"
-				:expandRowKeys="expandedRowKeys"
-				@expand-change="expandChange"
-				@selection-change="handleSelectionChange"
-				:tableLoading="tableLoading"
-				@pagination="pagination"
-				:total="page.total"
-			>
-				<template #expand="{ row }">
-					<el-table
-						:data="expandData"
-						border
-						show-summary
-						:summary-method="summarizeMainTable"
-						v-loading="childrenLoading"
-					>
-						<el-table-column
-							align="center"
-							label="搴忓彿"
-							type="index"
-							width="60"
-						/>
-						<el-table-column label="鏈鐢熶骇鏁伴噺" prop="finishedNum" align="center" width="400">
-							<template #default="scope">
-								<el-input-number :step="0.01" :min="0" style="width: 100%"
-																 v-model="scope.row.finishedNum"
-																 :disabled="!scope.row.editType"
-																 :precision="2"
-																 placeholder="璇疯緭鍏�"
-																 clearable
-																 @change="changeNum(scope.row)"
-								/>
-							</template>
-						</el-table-column>
-<!--						<el-table-column label="寰呯敓浜ф暟閲�" prop="pendingNum" width="240" align="center"></el-table-column>-->
-						<el-table-column label="鐢熶骇浜�" prop="schedulingUserId" width="400">
-							<template #default="scope">
-								<el-select
-									v-model="scope.row.schedulingUserId"
-									placeholder="閫夋嫨浜哄憳"
-									:disabled="!scope.row.editType"
-									style="width: 100%;"
-								>
-									<el-option
-										v-for="user in userList"
-										:key="user.userId"
-										:label="user.nickName"
-										:value="user.userId"
-									/>
-								</el-select>
-							</template>
-						</el-table-column>
-						<el-table-column label="鐢熶骇鏃ユ湡" prop="schedulingDate" width="400">
-							<template #default="scope">
-								<el-date-picker
-									v-model="scope.row.schedulingDate"
-									type="date"
-									:disabled="!scope.row.editType"
-									placeholder="璇烽�夋嫨鏃ユ湡"
-									value-format="YYYY-MM-DD"
-									format="YYYY-MM-DD"
-									clearable
-									style="width: 100%"
-								/>
-							</template>
-						</el-table-column>
-						<el-table-column label="鎿嶄綔" width="60">
-							<template #default="scope">
-								<el-button
-									link
-									type="primary"
-									size="small"
-									@click="changeEditType(scope.row)"
-									v-if="!scope.row.editType"
-								>缂栬緫</el-button
-								>
-								<el-button
-									link
-									type="primary"
-									size="small"
-									@click="saveReceiptPayment(scope.row)"
-									v-if="scope.row.editType"
-								>淇濆瓨</el-button
-								>
-							</template>
-						</el-table-column>
-					</el-table>
-				</template>
-			</PIMTable>
-		</div>
-		<form-dia ref="formDia" @close="handleQuery"></form-dia>
-	</div>
+  <div class="app-container">
+    <div class="search_form">
+      <el-form :model="searchForm"
+               :inline="true">
+        <el-form-item label="鎶ュ伐浜哄憳鍚嶇О:">
+          <el-input v-model="searchForm.nickName"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    prefix-icon="Search"
+                    style="width: 200px;"
+                    @change="handleQuery" />
+        </el-form-item>
+        <el-form-item label="宸ュ崟鍙�:">
+          <el-input v-model="searchForm.workOrderNo"
+                    placeholder="璇疯緭鍏�"
+                    clearable
+                    prefix-icon="Search"
+                    style="width: 200px;"
+                    @change="handleQuery" />
+        </el-form-item>
+        <el-form-item label="宸ュ崟鐘舵��:">
+          <el-select v-model="searchForm.workOrderStatus"
+                     placeholder="璇烽�夋嫨宸ュ崟鐘舵��"
+                     style="width: 140px"
+                     clearable>
+            <el-option label="寰呯‘璁�"
+                       :value="1"></el-option>
+            <el-option label="寰呯敓浜�"
+                       :value="2"></el-option>
+            <el-option label="鐢熶骇涓�"
+                       :value="3"></el-option>
+            <el-option label="宸茬敓浜�"
+                       :value="4"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary"
+                     @click="handleQuery">鎼滅储</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+    <div class="table_list">
+      <div style="text-align: right"
+           class="mb10">
+        <!-- <el-button type="primary"
+                   @click="openForm('add')">鐢熶骇鎶ュ伐</el-button> -->
+        <el-button @click="handleOut">瀵煎嚭</el-button>
+      </div>
+      <PIMTable rowKey="id"
+                :column="tableColumn"
+                :tableData="tableData"
+                :page="page"
+                :isSelection="true"
+                :expandRowKeys="expandedRowKeys"
+                @expand-change="expandChange"
+                @selection-change="handleSelectionChange"
+                :tableLoading="tableLoading"
+                @pagination="pagination"
+                :total="page.total">
+        <template #expand="{ row }">
+          <el-table :data="expandData"
+                    border
+                    show-summary
+                    :summary-method="summarizeMainTable"
+                    v-loading="childrenLoading">
+            <el-table-column align="center"
+                             label="搴忓彿"
+                             type="index"
+                             width="60" />
+            <el-table-column label="鏈鐢熶骇鏁伴噺"
+                             prop="finishedNum"
+                             align="center"
+                             width="400">
+              <template #default="scope">
+                <el-input-number :step="0.01"
+                                 :min="0"
+                                 style="width: 100%"
+                                 v-model="scope.row.finishedNum"
+                                 :disabled="!scope.row.editType"
+                                 :precision="2"
+                                 placeholder="璇疯緭鍏�"
+                                 clearable
+                                 @change="changeNum(scope.row)" />
+              </template>
+            </el-table-column>
+            <!--						<el-table-column label="寰呯敓浜ф暟閲�" prop="pendingNum" width="240" align="center"></el-table-column>-->
+            <el-table-column label="鐢熶骇浜�"
+                             prop="schedulingUserId"
+                             width="400">
+              <template #default="scope">
+                <el-select v-model="scope.row.schedulingUserId"
+                           placeholder="閫夋嫨浜哄憳"
+                           :disabled="!scope.row.editType"
+                           style="width: 100%;">
+                  <el-option v-for="user in userList"
+                             :key="user.userId"
+                             :label="user.nickName"
+                             :value="user.userId" />
+                </el-select>
+              </template>
+            </el-table-column>
+            <el-table-column label="鐢熶骇鏃ユ湡"
+                             prop="schedulingDate"
+                             width="400">
+              <template #default="scope">
+                <el-date-picker v-model="scope.row.schedulingDate"
+                                type="date"
+                                :disabled="!scope.row.editType"
+                                placeholder="璇烽�夋嫨鏃ユ湡"
+                                value-format="YYYY-MM-DD"
+                                format="YYYY-MM-DD"
+                                clearable
+                                style="width: 100%" />
+              </template>
+            </el-table-column>
+            <el-table-column label="鎿嶄綔"
+                             width="60">
+              <template #default="scope">
+                <el-button link
+                           type="primary"
+                           size="small"
+                           @click="changeEditType(scope.row)"
+                           v-if="!scope.row.editType"
+                           :disabled="scope.row.parentStatus === 3">缂栬緫</el-button>
+                <el-button link
+                           type="primary"
+                           size="small"
+                           @click="saveReceiptPayment(scope.row)"
+                           v-if="scope.row.editType">淇濆瓨</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </template>
+      </PIMTable>
+    </div>
+    <form-dia ref="formDia"
+              @close="handleQuery"></form-dia>
+    <input-modal v-if="isShowInput"
+                 v-model:visible="isShowInput"
+                 :production-product-main-id="isShowingId" />
+    <output-modal v-if="isShowOutput"
+                  v-model:visible="isShowOutput"
+                  :production-product-main-id="isShowingId" />
+  </div>
 </template>
 
 <script setup>
-import {onMounted, ref} from "vue";
-import FormDia from "@/views/productionManagement/productionReporting/components/formDia.vue";
-import {staffJoinDel, staffJoinListPage} from "@/api/personnelManagement/onboarding.js";
-import {ElMessageBox} from "element-plus";
-import dayjs from "dayjs";
-import {
-	productionReportUpdate,
-	workListPage,
-	workListPageById
-} from "@/api/productionManagement/productionReporting.js";
-import {userListNoPageByTenantId} from "@/api/system/user.js";
+  import { onMounted, ref } from "vue";
+  import FormDia from "@/views/productionManagement/productionReporting/components/formDia.vue";
+  import { ElMessageBox } from "element-plus";
+  import {
+    productionReportUpdate,
+    workListPageById,
+    productionReportDelete,
+  } from "@/api/productionManagement/productionReporting.js";
+  import { productionProductMainListPage } from "@/api/productionManagement/productionProductMain.js";
+  import { userListNoPageByTenantId } from "@/api/system/user.js";
+  import InputModal from "@/views/productionManagement/productionReporting/Input.vue";
+  import OutputModal from "@/views/productionManagement/productionReporting/Output.vue";
 
-const data = reactive({
-	searchForm: {
-		staffName: "",
-		entryDate: null, // 褰曞叆鏃ユ湡
-		entryDateStart: undefined,
-		entryDateEnd: undefined,
-	},
-});
-const { searchForm } = toRefs(data);
-const expandedRowKeys = ref([]);
-const expandData = ref([]);
-const userList = ref([])
-const tableColumn = ref([
-	{
-		type: "expand",
-		dataType: "slot",
-		slot: "expand",
-	},
-	{
-		label: "鐘舵��",
-		prop: "status",
-		dataType: "tag",
-		formatData: (params) => {
-			if (params == 3) {
-				return "宸叉姤宸�";
-			} else if (params == 1) {
-				return "寰呯敓浜�";
-			} else {
-				return '鐢熶骇涓�';
-			}
-		},
-		formatType: (params) => {
-			if (params == 3) {
-				return "success";
-			} else if (params == 1) {
-				return "primary";
-			} else {
-				return 'warning';
-			}
-		},
-	},
-	{
-		label: "鎺掍骇鏃ユ湡",
-		prop: "schedulingDate",
-		width: 120,
-	},
-	{
-		label: "鎺掍骇浜�",
-		prop: "schedulingUserName",
-	},
-	{
-		label: "鍚堝悓鍙�",
-		prop: "salesContractNo",
-		width: 200,
-	},
-	{
-		label: "瀹㈡埛鍚堝悓鍙�",
-		prop: "customerContractNo",
-		width: 200,
-	},
-	{
-		label: "瀹㈡埛鍚嶇О",
-		prop: "customerName",
-		width: 200,
-	},
-	{
-		label: "椤圭洰鍚嶇О",
-		prop: "projectName",
-		width:300
-	},
-	{
-		label: "浜у搧澶х被",
-		prop: "productCategory",
-		width: 150,
-	},
-	{
-		label: "瑙勬牸鍨嬪彿",
-		prop: "specificationModel",
-		width: 150,
-	},
-	{
-		label: "鍗曚綅",
-		prop: "unit",
-	},
-	{
-		label: "宸ュ簭",
-		prop: "process",
-	},
-	{
-		label: "鎺掍骇鏁伴噺",
-		prop: "schedulingNum",
-		width: 100,
-	},
-	{
-		label: "鐢熶骇鏁伴噺",
-		prop: "finishedNum",
-		width: 100,
-	},
-	{
-		label: "寰呯敓浜ф暟閲�",
-		prop: "pendingFinishNum",
-		width: 100,
-	},
-]);
-const tableData = ref([]);
-const selectedRows = ref([]);
-const tableLoading = ref(false);
-const childrenLoading = ref(false);
-const page = reactive({
-	current: 1,
-	size: 100,
-	total: 0,
-});
-const formDia = ref()
-const { proxy } = getCurrentInstance()
+  const data = reactive({
+    searchForm: {
+      nickName: "",
+      workOrderNo: "",
+      workOrderStatus: "",
+    },
+  });
+  const { searchForm } = toRefs(data);
+  const expandedRowKeys = ref([]);
+  const expandData = ref([]);
+  const userList = ref([]);
+  const tableColumn = ref([
+    {
+      label: "鎶ュ伐鍗曞彿",
+      prop: "productNo",
+      width: 120,
+    },
+    {
+      label: "鎶ュ伐浜哄憳",
+      prop: "nickName",
+      width: 120,
+    },
+    {
+      label: "宸ュ崟缂栧彿",
+      prop: "workOrderNo",
+      width: 120,
+    },
+    {
+      label: "鎶ュ伐鐘舵��",
+      prop: "status",
+      dataType: "tag",
+      formatData: params => {
+        if (params == 3) {
+          return "宸叉姤宸�";
+        } else if (params == 1) {
+          return "寰呯敓浜�";
+        } else {
+          return "鐢熶骇涓�";
+        }
+      },
+      formatType: params => {
+        if (params == 3) {
+          return "success";
+        } else if (params == 1) {
+          return "primary";
+        } else {
+          return "warning";
+        }
+      },
+    },
+    {
+      label: "宸ュ崟鐘舵��",
+      prop: "workOrderStatus",
+      dataType: "tag",
+      formatData: params => {
+        switch (params) {
+          case "1":
+            return "寰呯‘璁�";
+          case "2":
+            return "寰呯敓浜�";
+          case "3":
+            return "鐢熶骇涓�";
+          case "4":
+            return "宸茬敓浜�";
+          default:
+            return "";
+        }
+      },
+      formatType: params => {
+        switch (params) {
+          case "1":
+            return "primary";
+          case "2":
+            return "info";
+          case "3":
+            return "warning";
+          case "4":
+            return "success";
+          default:
+            return "";
+        }
+      },
+    },
+    {
+      label: "鐢熶骇鏃堕棿",
+      prop: "createTime",
+      width: 120,
+      formatData: params => {
+        const date = new Date(params);
+        return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(
+          2,
+          "0"
+        )}-${String(date.getDate()).padStart(2, "0")}`;
+      },
+    },
+    {
+      dataType: "action",
+      label: "鎿嶄綔",
+      align: "center",
+      fixed: "right",
+      width: 230,
+      operation: [
+        {
+          name: "鏌ョ湅鎶曞叆",
+          type: "text",
+          clickFun: row => {
+            showInput(row);
+          },
+        },
+        {
+          name: "鏌ョ湅浜у嚭",
+          type: "text",
+          clickFun: row => {
+            showOutput(row);
+          },
+        },
+        {
+          name: "鍒犻櫎",
+          type: "danger",
+          clickFun: row => {
+            deleteReport(row);
+          },
+        },
+      ],
+    },
+  ]);
+  const tableData = ref([]);
+  const selectedRows = ref([]);
+  const tableLoading = ref(false);
+  const childrenLoading = ref(false);
+  const page = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
+  const formDia = ref();
+  const { proxy } = getCurrentInstance();
 
-// 鏌ヨ鍒楄〃
-/** 鎼滅储鎸夐挳鎿嶄綔 */
-const handleQuery = () => {
-	page.current = 1;
-	getList();
-};
-const changeDaterange = (value) => {
-	if (value) {
-		searchForm.value.entryDateStart = value[0];
-		searchForm.value.entryDateEnd = value[1];
-	} else {
-		searchForm.value.entryDateStart = undefined;
-		searchForm.value.entryDateEnd = undefined;
-	}
-	handleQuery();
-};
-const pagination = (obj) => {
-	page.current = obj.page;
-	page.size = obj.limit;
-	getList();
-};
-const getList = () => {
-	tableLoading.value = true;
-	const params = { ...searchForm.value, ...page };
-	params.entryDate = undefined
-	expandedRowKeys.value = []
-	workListPage(params).then(res => {
-		tableLoading.value = false;
-		tableData.value = res.data.records.map(item => ({
-			...item,
-			pendingFinishNum: (Number(item.schedulingNum) || 0) - (Number(item.finishedNum) || 0)
-		}));
-		page.total = res.data.total;
-	}).catch(err => {
-		tableLoading.value = false;
-	})
-};
-// 灞曞紑琛�
-const expandChange = (row, expandedRows) => {
-	userListNoPageByTenantId().then((res) => {
-		userList.value = res.data;
-	});
-	if (expandedRows.length > 0) {
-		nextTick(() => {
-			expandedRowKeys.value = [];
-			try {
-				childrenLoading.value = true;
-				workListPageById({ id: row.id }).then((res) => {
-					childrenLoading.value = false;
-					const index = tableData.value.findIndex((item) => item.id === row.id);
-					if (index > -1) {
-						expandData.value = res.data.map(item => ({
-							...item,
-							pendingNum: (Number(item.schedulingNum) || 0) - (Number(item.finishedNum) || 0),
-							parentStatus: row.status // 鏂板鐖惰〃鐘舵��
-						}));
-					}
-					expandedRowKeys.value.push(row.id);
-				});
-			} catch (error) {
-				childrenLoading.value = false;
-				console.log(error);
-			}
-		})
-	} else {
-		expandedRowKeys.value = [];
-	}
-};
-const changeNum = (row) => {
-	// 鎵惧埌鐖惰〃鏍兼暟鎹�
-	const parentRow = tableData.value.find(item => item.id === expandedRowKeys.value[0]);
-	// 璁$畻鎵�鏈夊瓙琛ㄦ牸 finishedNum 鐨勬�诲拰
-	const totalFinishedNum = expandData.value.reduce((sum, item) => sum + (Number(item.finishedNum) || 0), 0);
-	// 鐖惰〃鏍肩殑鎺掍骇鏁伴噺
-	const schedulingNum = parentRow ? Number(parentRow.schedulingNum) : 0;
-	
-	if (totalFinishedNum > schedulingNum) {
-		// 鍥為��鏈杈撳叆
-		row.finishedNum = schedulingNum - (totalFinishedNum - Number(row.finishedNum));
-		proxy.$modal.msgWarning('鎵�鏈夋湰娆$敓浜ф暟閲忎箣鍜屼笉鍙ぇ浜庢帓浜ф暟閲�');
-	}
-	row.pendingNum = row.schedulingNum - row.finishedNum;
-}
-// 缂栬緫淇敼鐘舵��
-const changeEditType = (row) => {
-	row.editType = !row.editType;
-};
-// 淇濆瓨璁板綍
-const saveReceiptPayment = (row) => {
-	productionReportUpdate(row).then((res) => {
-		row.editType = !row.editType;
-		getList();
-		proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-	});
-};
-// 琛ㄦ牸閫夋嫨鏁版嵁
-const handleSelectionChange = (selection) => {
-	selectedRows.value = selection;
-};
-const summarizeMainTable = (param) => {
-	return proxy.summarizeTable(param, [
-		"finishedNum"
-	]);
-};
-// 鎵撳紑寮规
-const openForm = (type, row) => {
-	if (selectedRows.value.length !== 1) {
-		proxy.$message.error("璇烽�夋嫨涓�鏉℃暟鎹�");
-		return;
-	}
-	if (selectedRows.value[0].pendingFinishNum == 0) {
-		proxy.$message.warning("鏃犻渶鍐嶆姤宸�");
-		return;
-	}
-	nextTick(() => {
-		const rowInfo = type === 'add' ? selectedRows.value[0] : row
-		formDia.value?.openDialog(type, rowInfo)
-	})
-};
+  // 鏌ヨ鍒楄〃
+  /** 鎼滅储鎸夐挳鎿嶄綔 */
+  const handleQuery = () => {
+    page.current = 1;
+    getList();
+  };
+  const changeDaterange = value => {
+    if (value) {
+      searchForm.value.entryDateStart = value[0];
+      searchForm.value.entryDateEnd = value[1];
+    } else {
+      searchForm.value.entryDateStart = undefined;
+      searchForm.value.entryDateEnd = undefined;
+    }
+    handleQuery();
+  };
+  const deleteReport = row => {
+    ElMessageBox.confirm("纭畾鍒犻櫎璇ユ姤宸ュ悧锛�", "鎻愮ず", {
+      confirmButtonText: "纭畾",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    }).then(() => {
+      productionReportDelete({ id: row.id }).then(res => {
+        if (res.code === 200) {
+          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+          getList();
+        } else {
+          ElMessageBox.alert(res.msg || "鍒犻櫎澶辫触", "鎻愮ず", {
+            confirmButtonText: "纭畾",
+          });
+        }
+      });
+    });
+  };
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getList();
+  };
+  const getList = () => {
+    tableLoading.value = true;
+    const params = { ...searchForm.value, ...page };
+    params.entryDate = undefined;
+    expandedRowKeys.value = [];
+    productionProductMainListPage(params)
+      .then(res => {
+        tableLoading.value = false;
+        tableData.value = res.data.records.map(item => ({
+          ...item,
+          pendingFinishNum:
+            (Number(item.schedulingNum) || 0) - (Number(item.finishedNum) || 0),
+        }));
+        page.total = res.data.total;
+      })
+      .catch(err => {
+        tableLoading.value = false;
+      });
+  };
+  // 灞曞紑琛�
+  const expandChange = (row, expandedRows) => {
+    userListNoPageByTenantId().then(res => {
+      userList.value = res.data;
+    });
+    if (expandedRows.length > 0) {
+      nextTick(() => {
+        expandedRowKeys.value = [];
+        try {
+          childrenLoading.value = true;
+          workListPageById({ id: row.id }).then(res => {
+            childrenLoading.value = false;
+            const index = tableData.value.findIndex(item => item.id === row.id);
+            if (index > -1) {
+              expandData.value = res.data.map(item => ({
+                ...item,
+                pendingNum:
+                  (Number(item.schedulingNum) || 0) -
+                  (Number(item.finishedNum) || 0),
+                parentStatus: row.status, // 鏂板鐖惰〃鐘舵��
+              }));
+            }
+            expandedRowKeys.value.push(row.id);
+          });
+        } catch (error) {
+          childrenLoading.value = false;
+          console.log(error);
+        }
+      });
+    } else {
+      expandedRowKeys.value = [];
+    }
+  };
+  const changeNum = row => {
+    // 鎵惧埌鐖惰〃鏍兼暟鎹�
+    const parentRow = tableData.value.find(
+      item => item.id === expandedRowKeys.value[0]
+    );
+    // 璁$畻鎵�鏈夊瓙琛ㄦ牸 finishedNum 鐨勬�诲拰
+    const totalFinishedNum = expandData.value.reduce(
+      (sum, item) => sum + (Number(item.finishedNum) || 0),
+      0
+    );
+    // 鐖惰〃鏍肩殑鎺掍骇鏁伴噺
+    const schedulingNum = parentRow ? Number(parentRow.schedulingNum) : 0;
 
-// 鍒犻櫎
-const handleDelete = () => {
-	let ids = [];
-	if (selectedRows.value.length > 0) {
-		ids = selectedRows.value.map((item) => item.id);
-	} else {
-		proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
-		return;
-	}
-	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "瀵煎嚭", {
-		confirmButtonText: "纭",
-		cancelButtonText: "鍙栨秷",
-		type: "warning",
-	})
-		.then(() => {
-			staffJoinDel(ids).then((res) => {
-				proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-				getList();
-			});
-		})
-		.catch(() => {
-			proxy.$modal.msg("宸插彇娑�");
-		});
-};
-// 瀵煎嚭
-const handleOut = () => {
-	ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
-		confirmButtonText: "纭",
-		cancelButtonText: "鍙栨秷",
-		type: "warning",
-	})
-		.then(() => {
-			proxy.download("/salesLedger/work/export", {}, "鐢熶骇鎶ュ伐.xlsx");
-		})
-		.catch(() => {
-			proxy.$modal.msg("宸插彇娑�");
-		});
-};
-onMounted(() => {
-	getList();
-});
+    if (totalFinishedNum > schedulingNum) {
+      // 鍥為��鏈杈撳叆
+      row.finishedNum =
+        schedulingNum - (totalFinishedNum - Number(row.finishedNum));
+      proxy.$modal.msgWarning("鎵�鏈夋湰娆$敓浜ф暟閲忎箣鍜屼笉鍙ぇ浜庢帓浜ф暟閲�");
+    }
+    row.pendingNum = row.schedulingNum - row.finishedNum;
+  };
+  // 缂栬緫淇敼鐘舵��
+  const changeEditType = row => {
+    row.editType = !row.editType;
+  };
+  // 淇濆瓨璁板綍
+  const saveReceiptPayment = row => {
+    productionReportUpdate(row).then(res => {
+      row.editType = !row.editType;
+      getList();
+      proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+    });
+  };
+  // 琛ㄦ牸閫夋嫨鏁版嵁
+  const handleSelectionChange = selection => {
+    selectedRows.value = selection;
+  };
+  const summarizeMainTable = param => {
+    return proxy.summarizeTable(param, ["finishedNum"]);
+  };
+  // 鎵撳紑寮规
+  const openForm = (type, row) => {
+    if (selectedRows.value.length !== 1) {
+      proxy.$message.error("璇烽�夋嫨涓�鏉℃暟鎹�");
+      return;
+    }
+    if (selectedRows.value[0].pendingFinishNum == 0) {
+      proxy.$message.warning("鏃犻渶鍐嶆姤宸�");
+      return;
+    }
+    nextTick(() => {
+      const rowInfo = type === "add" ? selectedRows.value[0] : row;
+      formDia.value?.openDialog(type, rowInfo);
+    });
+  };
+
+  // 鎵撳紑鎶曞叆妯℃�佹
+  const isShowInput = ref(false);
+  const isShowingId = ref(0);
+  const showInput = row => {
+    isShowInput.value = true;
+    isShowingId.value = row.id;
+  };
+
+  // 鎵撳紑浜у嚭妯℃�佹
+  const isShowOutput = ref(false);
+  const showOutput = row => {
+    isShowOutput.value = true;
+    isShowingId.value = row.id;
+  };
+
+  // 瀵煎嚭
+  const handleOut = () => {
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        proxy.download("/salesLedger/work/export", {}, "鐢熶骇鎶ュ伐.xlsx");
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+  };
+  onMounted(() => {
+    getList();
+  });
 </script>
 
 <style scoped></style>
diff --git a/src/views/productionManagement/workOrder/index.vue b/src/views/productionManagement/workOrder/index.vue
new file mode 100644
index 0000000..34b368d
--- /dev/null
+++ b/src/views/productionManagement/workOrder/index.vue
@@ -0,0 +1,587 @@
+<template>
+  <div class="app-container">
+    <div class="search_form">
+      <div class="search-row">
+        <div class="search-item">
+          <span class="search_title">宸ュ崟缂栧彿锛�</span>
+          <el-input v-model="searchForm.workOrderNo"
+                    style="width: 240px"
+                    placeholder="璇疯緭鍏�"
+                    @change="handleQuery"
+                    clearable
+                    prefix-icon="Search" />
+        </div>
+        <div class="search-item">
+          <el-button type="primary"
+                     @click="handleQuery">鎼滅储</el-button>
+        </div>
+      </div>
+    </div>
+    <div class="table_list">
+      <PIMTable rowKey="id"
+                :column="tableColumn"
+                :tableData="tableData"
+                :page="page"
+                :tableLoading="tableLoading"
+                @pagination="pagination"></PIMTable>
+    </div>
+    <el-dialog v-model="editDialogVisible"
+               title="缂栬緫鏃堕棿"
+               width="500px">
+      <el-form :model="editrow"
+               label-width="120px">
+        <el-form-item label="璁″垝寮�濮嬫椂闂�">
+          <el-date-picker v-model="editrow.planStartTime"
+                          type="date"
+                          placeholder="璇烽�夋嫨"
+                          value-format="YYYY-MM-DD"
+                          style="width: 300px" />
+        </el-form-item>
+        <el-form-item label="璁″垝缁撴潫鏃堕棿">
+          <el-date-picker v-model="editrow.planEndTime"
+                          type="date"
+                          placeholder="璇烽�夋嫨"
+                          value-format="YYYY-MM-DD"
+                          style="width: 300px" />
+        </el-form-item>
+        <el-form-item label="瀹為檯寮�濮嬫椂闂�">
+          <el-date-picker v-model="editrow.actualStartTime"
+                          type="date"
+                          placeholder="璇烽�夋嫨"
+                          value-format="YYYY-MM-DD"
+                          style="width: 300px" />
+        </el-form-item>
+        <el-form-item label="瀹為檯缁撴潫鏃堕棿">
+          <el-date-picker v-model="editrow.actualEndTime"
+                          type="date"
+                          placeholder="璇烽�夋嫨"
+                          value-format="YYYY-MM-DD"
+                          style="width: 300px" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary"
+                     @click="handleUpdate">纭畾</el-button>
+          <el-button @click="editDialogVisible = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <el-dialog v-model="transferCardVisible"
+               title="娴佽浆鍗�"
+               width="1000px">
+      <div class="transfer-card-title">宸ュ崟娴佽浆鍗�</div>
+      <div class="transfer-card-container">
+        <div class="transfer-card-info">
+          <div class="info-group">
+            <div class="info-item">
+              <span class="info-label">宸ュ崟缂栧彿</span>
+              <span class="info-value">{{ transferCardRowData.workOrderNo }}</span>
+            </div>
+            <!-- <div class="info-item">
+              <span class="info-label">浜у搧缂栧彿</span>
+              <span class="info-value">{{ transferCardRowData.productNo }}</span>
+            </div> -->
+            <div class="info-item">
+              <span class="info-label">浜у搧鍚嶇О</span>
+              <span class="info-value">{{ transferCardRowData.productName }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">浜у搧瑙勬牸</span>
+              <span class="info-value">{{ transferCardRowData.model }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">宸ュ崟鐘舵��</span>
+              <span class="info-value">{{ 
+                transferCardRowData.status === 1 ? '寰呯‘璁�' : 
+                transferCardRowData.status === 2 ? '寰呯敓浜�' : 
+                transferCardRowData.status === 3 ? '鐢熶骇涓�' : 
+                transferCardRowData.status === 4 ? '宸茬敓浜�' : 
+                transferCardRowData.status 
+              }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">璁″垝寮�濮嬫椂闂�</span>
+              <span class="info-value">{{ transferCardRowData.planStartTime }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">璁″垝缁撴潫鏃堕棿</span>
+              <span class="info-value">{{ transferCardRowData.planEndTime }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">澶囨敞</span>
+              <span class="info-value">{{ transferCardRowData.remark }}</span>
+            </div>
+          </div>
+          <div class="info-group">
+            <div class="info-item">
+              <span class="info-label">&nbsp;</span>
+              <span class="info-value">&nbsp;</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">璁″垝鏁伴噺</span>
+              <span class="info-value">{{ transferCardRowData.planQuantity }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">鑹搧鏁伴噺</span>
+              <span class="info-value">0</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">涓嶈壇鍝佹暟</span>
+              <span class="info-value">0</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">瀹為檯寮�濮嬫椂闂�</span>
+              <span class="info-value">{{ transferCardRowData.actualStartTime }}</span>
+            </div>
+            <div class="info-item">
+              <span class="info-label">瀹為檯缁撴潫鏃堕棿</span>
+              <span class="info-value">{{ transferCardRowData.actualEndTime }}</span>
+            </div>
+          </div>
+        </div>
+        <div class="transfer-card-qr">
+          <div class="qr-container">
+            <img :src="transferCardQrUrl"
+                 alt="娴佽浆鍗′簩缁寸爜"
+                 style="width: 200px; height: 200px;" />
+            <!-- <div class="qr-tip"
+                 style="margin-top: 10px; text-align: center;">娴佽浆鍗′簩缁寸爜</div> -->
+          </div>
+        </div>
+      </div>
+      <div class="print-button-container"
+           style=" text-align: center;
+      margin-bottom: 40px;">
+        <el-button type="primary"
+                   style="margin-top: 20px;"
+                   @click="printTransferCard">鎵撳嵃娴佽浆鍗�</el-button>
+      </div>
+    </el-dialog>
+    <el-dialog v-model="reportDialogVisible"
+               title="鎶ュ伐"
+               width="500px">
+      <el-form :model="reportForm"
+               label-width="120px">
+        <el-form-item label="寰呯敓浜ф暟閲�">
+          <el-input v-model="reportForm.planQuantity"
+                    readonly
+                    style="width: 300px" />
+        </el-form-item>
+        <el-form-item label="鏈鐢熶骇鏁伴噺">
+          <el-input v-model.number="reportForm.quantity"
+                    type="number"
+                    min="1"
+                    style="width: 300px"
+                    placeholder="璇疯緭鍏ユ湰娆$敓浜ф暟閲�" />
+        </el-form-item>
+        <el-form-item label="鐝粍淇℃伅">
+          <el-input v-model="reportForm.userName"
+                    style="width: 300px"
+                    readonly
+                    placeholder="璇疯緭鍏ョ彮缁勪俊鎭�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary"
+                     @click="handleReport">纭畾</el-button>
+          <el-button @click="reportDialogVisible = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+  import { onMounted, ref } from "vue";
+  import { ElMessageBox } from "element-plus";
+  import dayjs from "dayjs";
+  import {
+    productWorkOrderPage,
+    updateProductWorkOrder,
+    addProductMain,
+  } from "@/api/productionManagement/workOrder.js";
+  import { getUserProfile } from "@/api/system/user.js";
+  import QRCode from "qrcode";
+  import { getCurrentInstance, reactive, toRefs } from "vue";
+  const { proxy } = getCurrentInstance();
+
+  const tableColumn = ref([
+    {
+      label: "宸ュ崟缂栧彿",
+      prop: "workOrderNo",
+      width: "140",
+    },
+    {
+      label: "鐢熶骇璁㈠崟鍙�",
+      prop: "productOrderNpsNo",
+      width: "140",
+    },
+    {
+      label: "浜у搧鍚嶇О",
+      prop: "productName",
+      width: "140",
+    },
+    {
+      label: "瑙勬牸",
+      prop: "model",
+    },
+    {
+      label: "鍗曚綅",
+      prop: "unit",
+    },
+    {
+      label: "宸ュ簭鍚嶇О",
+      prop: "processName",
+    },
+    {
+      label: "寰呯敓浜ф暟閲�",
+      prop: "planQuantity",
+      width: "140",
+    },
+    {
+      label: "璁″垝鐢熶骇鏁伴噺",
+      prop: "quantity",
+      width: "140",
+    },
+    {
+      label: "璁″垝寮�濮嬫椂闂�",
+      prop: "planStartTime",
+      width: "140",
+    },
+    {
+      label: "璁″垝缁撴潫鏃堕棿",
+      prop: "planEndTime",
+      width: "140",
+    },
+    {
+      label: "瀹為檯寮�濮嬫椂闂�",
+      prop: "actualStartTime",
+      width: "140",
+    },
+    {
+      label: "瀹為檯缁撴潫鏃堕棿",
+      prop: "actualEndTime",
+      width: "140",
+    },
+    {
+      label: "鎿嶄綔",
+      width: "200",
+      align: "center",
+      dataType: "action",
+      fixed: "right",
+      operation: [
+        {
+          name: "缂栬緫",
+          clickFun: row => {
+            handleEdit(row);
+          },
+        },
+        {
+          name: "娴佽浆鍗�",
+          clickFun: row => {
+            showTransferCard(row);
+          },
+        },
+        {
+          name: "鎶ュ伐",
+          clickFun: row => {
+            showReportDialog(row);
+          },
+          disabled: row => row.planQuantity <= 0,
+        },
+      ],
+    },
+  ]);
+  const tableData = ref([]);
+  const tableLoading = ref(false);
+  const qrCodeUrl = ref("");
+  const qrRowData = ref(null);
+  const editDialogVisible = ref(false);
+  const transferCardVisible = ref(false);
+  const transferCardData = ref([]);
+  const transferCardQrUrl = ref("");
+  const transferCardRowData = ref(null);
+  const reportDialogVisible = ref(false);
+  const reportForm = reactive({
+    planQuantity: 0,
+    quantity: 0,
+    userName: "",
+    workOrderId: "",
+    reportWork: "",
+    productProcessRouteItemId: "",
+    userId: "",
+    productMainId: null,
+  });
+  const currentReportRowData = ref(null);
+  const page = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
+
+  const data = reactive({
+    searchForm: {
+      workOrderNo: "",
+    },
+  });
+  const { searchForm } = toRefs(data);
+  let editrow = ref(null);
+
+  // 鏌ヨ鍒楄〃
+  /** 鎼滅储鎸夐挳鎿嶄綔 */
+  const handleQuery = () => {
+    page.current = 1;
+    getList();
+  };
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getList();
+  };
+  const getList = () => {
+    tableLoading.value = true;
+    const params = { ...searchForm.value, ...page };
+    productWorkOrderPage(params)
+      .then(res => {
+        tableLoading.value = false;
+        tableData.value = res.data.records;
+        page.total = res.data.total;
+      })
+      .catch(() => {
+        tableLoading.value = false;
+      });
+  };
+
+  const showTransferCard = async row => {
+    transferCardRowData.value = row;
+    const qrContent =
+      proxy.javaApi + "/work-order?orderRow=" + JSON.stringify(row);
+    console.log(qrContent, "qrContent");
+
+    transferCardQrUrl.value = await QRCode.toDataURL(qrContent);
+    transferCardVisible.value = true;
+  };
+
+  const printTransferCard = () => {
+    window.print();
+  };
+
+  const handleEdit = row => {
+    editrow.value = JSON.parse(JSON.stringify(row));
+    editDialogVisible.value = true;
+  };
+
+  const handleUpdate = () => {
+    updateProductWorkOrder(editrow.value)
+      .then(res => {
+        proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+        editDialogVisible.value = false;
+        getList();
+      })
+      .catch(() => {
+        ElMessageBox.alert("淇敼澶辫触", "鎻愮ず", {
+          confirmButtonText: "纭畾",
+        });
+      });
+  };
+
+  const showReportDialog = row => {
+    currentReportRowData.value = row;
+    reportForm.planQuantity = row.planQuantity;
+    reportForm.quantity = row.quantity;
+    reportForm.productProcessRouteItemId = row.productProcessRouteItemId;
+    reportForm.workOrderId = row.id;
+    reportForm.reportWork = row.reportWork;
+    reportForm.productMainId = row.productMainId;
+    // 鑾峰彇褰撳墠鐧诲綍鐢ㄦ埛淇℃伅
+
+    reportDialogVisible.value = true;
+  };
+
+  const handleReport = () => {
+    if (reportForm.planQuantity <= 0) {
+      ElMessageBox.alert("寰呯敓浜ф暟閲忎负0锛屾棤娉曟姤宸�", "鎻愮ず", {
+        confirmButtonText: "纭畾",
+      });
+      return;
+    }
+    if (!reportForm.quantity || reportForm.quantity <= 0) {
+      ElMessageBox.alert("璇疯緭鍏ユ湁鏁堢殑鏈鐢熶骇鏁伴噺", "鎻愮ず", {
+        confirmButtonText: "纭畾",
+      });
+      return;
+    }
+    if (reportForm.quantity > reportForm.planQuantity) {
+      ElMessageBox.alert("鏈鐢熶骇鏁伴噺涓嶈兘瓒呰繃寰呯敓浜ф暟閲�", "鎻愮ず", {
+        confirmButtonText: "纭畾",
+      });
+      return;
+    }
+    // console.log(reportForm);
+    addProductMain(reportForm).then(res => {
+      if (res.code === 200) {
+        proxy.$modal.msgSuccess("鎶ュ伐鎴愬姛");
+        reportDialogVisible.value = false;
+        getList();
+      } else {
+        ElMessageBox.alert(res.msg || "鎶ュ伐澶辫触", "鎻愮ず", {
+          confirmButtonText: "纭畾",
+        });
+      }
+    });
+  };
+
+  onMounted(() => {
+    getList();
+    getUserProfile()
+      .then(res => {
+        if (res.code === 200) {
+          reportForm.userName = res.data.userName;
+          reportForm.userId = res.data.userId;
+        }
+      })
+      .catch(err => {
+        console.error("鑾峰彇鐢ㄦ埛淇℃伅澶辫触", err);
+      });
+  });
+</script>
+
+<style scoped lang="scss">
+  .search_form {
+    margin-bottom: 20px;
+    .search-row {
+      display: flex;
+      gap: 20px;
+      align-items: center;
+      .search-item {
+        display: flex;
+        align-items: center;
+        gap: 10px;
+      }
+    }
+  }
+
+  .transfer-card-title {
+    font-size: 24px;
+    font-weight: bold;
+    text-align: center;
+    margin-bottom: 20px;
+  }
+
+  .transfer-card-container {
+    display: flex;
+    gap: 20px;
+    height: 350px;
+    .transfer-card-info {
+      flex: 1;
+      overflow: auto;
+      .info-group {
+        width: 50%;
+        float: left;
+      }
+      .info-item {
+        display: flex;
+        margin-bottom: 15px;
+        .info-label {
+          width: 120px;
+          font-weight: bold;
+          margin-right: 20px;
+        }
+        .info-value {
+          flex: 1;
+        }
+      }
+    }
+    .transfer-card-qr {
+      width: 240px;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: flex-start;
+    }
+  }
+</style>
+
+<style  lang="scss">
+  @media print {
+    @page {
+      size: landscape;
+    }
+    body * {
+      visibility: hidden;
+    }
+    .el-dialog__wrapper,
+    .el-dialog,
+    .el-dialog__body,
+    .transfer-card-title,
+    .transfer-card-container,
+    .transfer-card-container *,
+    .info-item,
+    .info-label,
+    .info-value {
+      visibility: visible;
+    }
+    .print-button-container {
+      visibility: hidden;
+    }
+    .el-dialog__wrapper {
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      margin: 0;
+    }
+    .el-dialog {
+      width: 100% !important;
+      max-width: 800px;
+      margin: 0 auto !important;
+    }
+    .el-dialog__header,
+    .el-dialog__footer {
+      display: none;
+    }
+    .el-dialog__body {
+      padding: 20px;
+    }
+    .transfer-card-container {
+      height: auto;
+      display: flex;
+      gap: 20px;
+    }
+    .transfer-card-info {
+      flex: 1;
+      .info-group {
+        width: 100%;
+        float: none;
+        margin-bottom: 20px;
+      }
+      .info-item {
+        display: flex;
+        margin-bottom: 10px;
+        .info-label {
+          width: 100px;
+          font-weight: bold;
+          margin-right: 15px;
+          white-space: nowrap;
+        }
+        .info-value {
+          flex: 1;
+          word-break: break-word;
+        }
+      }
+    }
+    .transfer-card-qr {
+      width: 160px;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: flex-start;
+    }
+    .qr-container img {
+      width: 140px !important;
+      height: 140px !important;
+    }
+  }
+</style>

--
Gitblit v1.9.3