From eeefce8145b5ab47ba7148fc05092e2d01e1fc8e Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期三, 28 一月 2026 09:13:19 +0800
Subject: [PATCH] fix: 搬迁军泰生产-可上传图片、下载流转码

---
 src/views/productionManagement/productStructure/Detail/index.vue            |  757 ++++++++++++++++++++++-------------------
 src/api/productionManagement/workOrder.js                                   |   10 
 src/views/productionManagement/workOrder/components/filesDia.vue            |  202 +++++++++++
 src/views/productionManagement/workOrder/index.vue                          |   66 +++
 src/views/productionManagement/productionDispatching/components/formDia.vue |    1 
 src/api/productionManagement/productWorkOrderFile.js                        |   29 +
 src/views/productionManagement/productionReporting/components/formDia.vue   |    1 
 7 files changed, 720 insertions(+), 346 deletions(-)

diff --git a/src/api/productionManagement/productWorkOrderFile.js b/src/api/productionManagement/productWorkOrderFile.js
new file mode 100644
index 0000000..9fc04a9
--- /dev/null
+++ b/src/api/productionManagement/productWorkOrderFile.js
@@ -0,0 +1,29 @@
+import request from "@/utils/request";
+
+// 鏌ヨ宸ュ崟闄勪欢鍒楄〃
+export function productWorkOrderFileListPage(query) {
+  return request({
+    url: "/productWorkOrderFile/listPage",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鏂板宸ュ崟闄勪欢
+export function productWorkOrderFileAdd(data) {
+  return request({
+    url: "/productWorkOrderFile/add",
+    method: "post",
+    data,
+  });
+}
+
+// 鍒犻櫎宸ュ崟闄勪欢
+export function productWorkOrderFileDel(data) {
+  return request({
+    url: "/productWorkOrderFile/del",
+    method: "delete",
+    data,
+  });
+}
+
diff --git a/src/api/productionManagement/workOrder.js b/src/api/productionManagement/workOrder.js
index bf4b381..7e8bd86 100644
--- a/src/api/productionManagement/workOrder.js
+++ b/src/api/productionManagement/workOrder.js
@@ -23,3 +23,13 @@
     data: data,
   });
 }
+
+// 涓嬭浇宸ュ崟娴佽浆鍗★紙杩斿洖鏂囦欢娴侊級
+export function downProductWorkOrder(id) {
+  return request({
+    url: "/productWorkOrder/down",
+    method: "post",
+    data: { id },
+    responseType: "blob",
+  });
+}
diff --git a/src/views/productionManagement/productStructure/Detail/index.vue b/src/views/productionManagement/productStructure/Detail/index.vue
index 360a124..3a76e48 100644
--- a/src/views/productionManagement/productStructure/Detail/index.vue
+++ b/src/views/productionManagement/productStructure/Detail/index.vue
@@ -2,85 +2,136 @@
   <div class="app-container">
     <PageHeader content="浜у搧缁撴瀯璇︽儏">
       <template #right-button>
-        <el-button v-if="!dataValue.isEdit && !isOrderPage" type="primary" @click="dataValue.isEdit = true">缂栬緫
+        <el-button v-if="!dataValue.isEdit && !isOrderPage"
+                   type="primary"
+                   @click="dataValue.isEdit = true">缂栬緫
         </el-button>
-        <el-button v-if="dataValue.isEdit && !isOrderPage" type="primary" @click="cancelEdit">鍙栨秷
+        <el-button v-if="dataValue.isEdit && !isOrderPage"
+                   type="primary"
+                   @click="cancelEdit">鍙栨秷
         </el-button>
-        <el-button v-if="!isOrderPage" type="primary" :loading="dataValue.loading" @click="submit"
-          :disabled="!dataValue.isEdit">纭
+        <el-button v-if="!isOrderPage"
+                   type="primary"
+                   :loading="dataValue.loading"
+                   @click="submit"
+                   :disabled="!dataValue.isEdit">纭
         </el-button>
       </template>
     </PageHeader>
-    <el-table :data="tableData" border :preserve-expanded-content="false" :default-expand-all="true"
-      style="width: 100%">
+    <el-table :data="tableData"
+              border
+              :preserve-expanded-content="false"
+              :default-expand-all="true"
+              style="width: 100%">
       <el-table-column type="expand">
         <template #default="props">
-          <el-form ref="form" :model="dataValue">
-            <el-table :data="dataValue.dataList" row-key="tempId" default-expand-all
-              :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" style="width: 100%">
-              <el-table-column prop="productName" label="浜у搧" />
-              <el-table-column prop="model" label="瑙勬牸">
+          <el-form ref="form"
+                   :model="dataValue">
+            <el-table :data="dataValue.dataList"
+                      row-key="tempId"
+                      default-expand-all
+                      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+                      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"
-                    :rules="[{ required: true, message: '璇烽�夋嫨瑙勬牸', trigger: ['blur', 'change'] }]" style="margin: 0">
-                    <el-select v-model="row.model" placeholder="璇烽�夋嫨瑙勬牸" clearable
-                      :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)"
-                      style="width: 100%" @visible-change="(v) => { if (v) openDialog(row.tempId) }">
-                      <el-option v-if="row.model" :label="row.model" :value="row.model" />
+                                :rules="[{ required: true, message: '璇烽�夋嫨瑙勬牸', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-select v-model="row.model"
+                               placeholder="璇烽�夋嫨瑙勬牸"
+                               clearable
+                               :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)"
+                               style="width: 100%"
+                               @visible-change="(v) => { if (v) openDialog(row.tempId) }">
+                      <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="processName" label="娑堣�楀伐搴�">
+              <el-table-column prop="processName"
+                               label="娑堣�楀伐搴�">
                 <template #default="{ row, $index }">
                   <el-form-item v-if="dataValue.isEdit"
-                    :rules="dataValue.dataList.some(item => (item as any).tempId === row.tempId) ? [] : [{ required: true, message: '璇烽�夋嫨娑堣�楀伐搴�', trigger: 'change' }]"
-                    style="margin: 0">
-                    <el-select v-model="row.processId" placeholder="璇烽�夋嫨" filterable clearable style="width: 100%"
-                      :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)">
-                      <el-option v-for="item in dataValue.processOptions" :key="item.id" :label="item.name"
-                        :value="item.id" />
+                                :rules="dataValue.dataList.some(item => (item as any).tempId === row.tempId) ? [] : [{ required: true, message: '璇烽�夋嫨娑堣�楀伐搴�', trigger: 'change' }]"
+                                style="margin: 0">
+                    <el-select v-model="row.processId"
+                               placeholder="璇烽�夋嫨"
+                               filterable
+                               clearable
+                               style="width: 100%"
+                               :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)">
+                      <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="鍗曚綅浜у嚭鎵�闇�鏁伴噺">
+              <el-table-column prop="unitQuantity"
+                               label="鍗曚綅浜у嚭鎵�闇�鏁伴噺">
                 <template #default="{ row, $index }">
                   <el-form-item v-if="dataValue.isEdit"
-                    :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 || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
+                                :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 || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
                   </el-form-item>
                 </template>
               </el-table-column>
-              <el-table-column v-if="isOrderPage" prop="demandedQuantity" label="闇�姹傛�婚噺">
+              <el-table-column v-if="isOrderPage"
+                               prop="demandedQuantity"
+                               label="闇�姹傛�婚噺">
                 <template #default="{ row, $index }">
                   <el-form-item v-if="dataValue.isEdit"
-                    :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 || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
+                                :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 || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
                   </el-form-item>
                 </template>
               </el-table-column>
-              <el-table-column prop="unit" label="鍗曚綅">
+              <el-table-column prop="unit"
+                               label="鍗曚綅">
                 <template #default="{ row, $index }">
                   <el-form-item v-if="dataValue.isEdit"
-                    :rules="[{ required: true, message: '璇疯緭鍏ュ崟浣�', trigger: ['blur', 'change'] }]" style="margin: 0">
-                    <el-input v-model="row.unit" placeholder="璇疯緭鍏ュ崟浣�" clearable
-                      :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
+                                :rules="[{ required: true, message: '璇疯緭鍏ュ崟浣�', trigger: ['blur','change'] }]"
+                                style="margin: 0">
+                    <el-input v-model="row.unit"
+                              placeholder="璇疯緭鍏ュ崟浣�"
+                              clearable
+                               :disabled="!dataValue.isEdit || dataValue.dataList.some(item => (item as any).tempId === row.tempId)" />
                   </el-form-item>
                 </template>
               </el-table-column>
-              <el-table-column label="鎿嶄綔" fixed="right" width="200">
+              <el-table-column label="鎿嶄綔"
+                               fixed="right"
+                               width="200">
                 <template #default="{ row, $index }">
-                  <el-button
-                    v-if="dataValue.isEdit && !dataValue.dataList.some(item => (item as any).tempId === row.tempId)"
-                    type="danger" text @click="removeItem(row.tempId)">鍒犻櫎
+                  <el-button v-if="dataValue.isEdit && !dataValue.dataList.some(item => (item as any).tempId === row.tempId)"
+                             type="danger"
+                             text
+                             @click="removeItem(row.tempId)">鍒犻櫎
                   </el-button>
-                  <el-button v-if="dataValue.isEdit" type="primary" text @click="addItem2(row.tempId)">娣诲姞
+                  <el-button v-if="dataValue.isEdit"
+                             type="primary"
+                             text
+                             @click="addItem2(row.tempId)">娣诲姞
                   </el-button>
                 </template>
               </el-table-column>
@@ -88,271 +139,175 @@
           </el-form>
         </template>
       </el-table-column>
-      <el-table-column label="BOM缂栧彿" prop="bomNo" />
-      <el-table-column label="浜у搧鍚嶇О" prop="productName" />
-      <el-table-column label="瑙勬牸鍨嬪彿" prop="model" />
+      <el-table-column label="BOM缂栧彿"
+                       prop="bomNo" />
+      <el-table-column label="浜у搧鍚嶇О"
+                       prop="productName" />
+      <el-table-column label="瑙勬牸鍨嬪彿"
+                       prop="model" />
     </el-table>
-    <product-select-dialog v-if="dataValue.showProductDialog" v-model:model-value="dataValue.showProductDialog"
-      @confirm="handleProduct" />
+    <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 { listProcessBom } from "@/api/productionManagement/productionOrder.js";
-import { list } from "@/api/productionManagement/productionProcess";
-import { ElMessage } from "element-plus";
-import { useRoute, useRouter } from "vue-router";
+  import {
+    computed,
+    defineAsyncComponent,
+    defineComponent,
+    onMounted,
+    reactive,
+    ref,
+  } from "vue";
+  import { queryList, add } from "@/api/productionManagement/productStructure.js";
+  import { listProcessBom } from "@/api/productionManagement/productionOrder.js";
+  import { list } from "@/api/productionManagement/productionProcess";
+  import { ElMessage } from "element-plus";
+  import { useRoute, useRouter } from "vue-router";
 
-defineComponent({
-  name: "StructureEdit",
-});
+  defineComponent({
+    name: "StructureEdit",
+  });
 
-const ProductSelectDialog = defineAsyncComponent(
-  () => import("@/views/basicData/product/ProductSelectDialog.vue")
-);
-const emit = defineEmits(["update:router"]);
-const form = ref();
+  const ProductSelectDialog = defineAsyncComponent(
+    () => import("@/views/basicData/product/ProductSelectDialog.vue")
+  );
+  const emit = defineEmits(["update:router"]);
+  const form = ref();
 
-const route = useRoute();
-const router = useRouter();
-const routeId = computed({
-  get() {
-    return route.query.id;
-  },
+  const route = useRoute();
+  const router = useRouter();
+  const routeId = computed({
+    get() {
+      return route.query.id;
+    },
 
-  set(val) {
-    emit("update:router", val);
-  },
-});
+    set(val) {
+      emit("update:router", val);
+    },
+  });
 
-// 浠庤矾鐢卞弬鏁拌幏鍙栦骇鍝佷俊鎭�
-const routeBomNo = computed(() => route.query.bomNo || "");
-const routeProductName = computed(() => route.query.productName || "");
-const routeProductModelName = computed(
-  () => route.query.productModelName || ""
-);
-const routeOrderId = computed(() => route.query.orderId);
-const pageType = computed(() => route.query.type);
-const isOrderPage = computed(
-  () => pageType.value === "order" && routeOrderId.value
-);
+  // 浠庤矾鐢卞弬鏁拌幏鍙栦骇鍝佷俊鎭�
+  const routeBomNo = computed(() => route.query.bomNo || "");
+  const routeProductName = computed(() => route.query.productName || "");
+  const routeProductModelName = computed(
+    () => route.query.productModelName || ""
+  );
+  const routeOrderId = computed(() => route.query.orderId);
+  const pageType = computed(() => route.query.type);
+  const isOrderPage = computed(
+    () => pageType.value === "order" && routeOrderId.value
+  );
 
-const dataValue = reactive({
-  dataList: [],
-  productOptions: [],
-  processOptions: [],
-  showProductDialog: false,
-  currentRowIndex: null,
-  currentRowName: null,
-  loading: false,
-  isEdit: false,
-});
+  const dataValue = reactive({
+    dataList: [],
+    productOptions: [],
+    processOptions: [],
+    showProductDialog: false,
+    currentRowIndex: null,
+    currentRowName: null,
+    loading: false,
+    isEdit: false,
+  });
 
-const tableData = reactive([
-  {
-    productName: "",
-    model: "",
-    bomNo: "",
-  },
-]);
+  const tableData = reactive([
+    {
+      productName: "",
+      model: "",
+      bomNo: "",
+    },
+  ]);
 
-const openDialog = (tempId: any) => {
-  console.log(tempId, "tempId");
-  dataValue.currentRowName = tempId;
-  dataValue.showProductDialog = true;
-};
+  const openDialog = (tempId: any) => {
+    console.log(tempId, "tempId");
+    dataValue.currentRowName = tempId;
+    dataValue.showProductDialog = true;
+  };
 
-const fetchData = async () => {
-  if (isOrderPage.value) {
-    // 璁㈠崟鎯呭喌锛氫娇鐢ㄨ鍗曠殑浜у搧缁撴瀯鎺ュ彛
-    const { data } = await listProcessBom({ orderId: routeOrderId.value });
-    dataValue.dataList = (data as any) || [];
-  } else {
-    // 闈炶鍗曟儏鍐碉細浣跨敤鍘熸潵鐨勬帴鍙�
-    const { data } = await queryList(routeId.value);
-    dataValue.dataList = (data as any) || [];
-    // 涓烘墍鏈夐」鍙婂叾瀛愰」璁剧疆name灞炴��
-    const setNameRecursively = (items: any[]) => {
-      items.forEach((item: any) => {
-        item.tempId = item.id;
-        item.processName =
-          dataValue.processOptions.find(option => option.id === item.processId)
-            ?.name || "";
-        if (item.children && item.children.length > 0) {
-          setNameRecursively(item.children);
+  const fetchData = async () => {
+    if (isOrderPage.value) {
+      // 璁㈠崟鎯呭喌锛氫娇鐢ㄨ鍗曠殑浜у搧缁撴瀯鎺ュ彛
+      const { data } = await listProcessBom({ orderId: routeOrderId.value });
+      dataValue.dataList = (data as any) || [];
+    } else {
+      // 闈炶鍗曟儏鍐碉細浣跨敤鍘熸潵鐨勬帴鍙�
+      const { data } = await queryList(routeId.value);
+      dataValue.dataList = (data as any) || [];
+      // 涓烘墍鏈夐」鍙婂叾瀛愰」璁剧疆name灞炴��
+      const setNameRecursively = (items: any[]) => {
+        items.forEach((item: any) => {
+          item.tempId = item.id;
+          item.processName =
+            dataValue.processOptions.find(option => option.id === item.processId)
+              ?.name || "";
+          if (item.children && item.children.length > 0) {
+            setNameRecursively(item.children);
+          }
+        });
+      };
+      setNameRecursively(dataValue.dataList);
+      console.log(dataValue.dataList, "dataValue.dataList");
+    }
+  };
+
+  const fetchProcessOptions = async () => {
+    const { data } = await list();
+    dataValue.processOptions = data as any;
+  };
+
+  const handleProduct = (row: any) => {
+    if (row?.length > 1) {
+      ElMessage.error("鍙兘閫夋嫨涓�涓骇鍝�");
+    }
+    const productData = row[0];
+
+    //  鏈�澶栧眰缁勪欢涓紝涓庡綋鍓嶄骇鍝佺浉鍚岀殑浜у搧鍙兘鏈変竴涓�
+    const isTopLevel = dataValue.dataList.some(item => (item as any).tempId === dataValue.currentRowName);
+    if (isTopLevel) {
+      if (productData.productName === tableData[0].productName &&
+        productData.model === tableData[0].model) {
+        //  鏌ユ壘鏄惁宸茬粡鏈夊叾浠栭《灞傝宸茬粡鏄繖涓骇鍝�
+        const hasOther = dataValue.dataList.some(item =>
+          (item as any).tempId !== dataValue.currentRowName &&
+          (item as any).productName === tableData[0].productName &&
+          (item as any).model === tableData[0].model
+        );
+        if (hasOther) {
+          ElMessage.warning("鏈�澶栧眰鍜屽綋鍓嶄骇鍝佷竴鏍风殑涓�绾у彧鑳芥湁涓�涓�");
+          return;
         }
-      });
-    };
-    setNameRecursively(dataValue.dataList);
-    console.log(dataValue.dataList, "dataValue.dataList");
-  }
-};
-
-const fetchProcessOptions = async () => {
-  const { data } = await list();
-  dataValue.processOptions = data as any;
-};
-
-const handleProduct = (row: any) => {
-  if (row?.length > 1) {
-    ElMessage.error("鍙兘閫夋嫨涓�涓骇鍝�");
-  }
-  const productData = row[0];
-
-  //  鏈�澶栧眰缁勪欢涓紝涓庡綋鍓嶄骇鍝佺浉鍚岀殑浜у搧鍙兘鏈変竴涓�
-  const isTopLevel = dataValue.dataList.some(item => (item as any).tempId === dataValue.currentRowName);
-  if (isTopLevel) {
-    if (productData.productName === tableData[0].productName &&
-      productData.model === tableData[0].model) {
-      //  鏌ユ壘鏄惁宸茬粡鏈夊叾浠栭《灞傝宸茬粡鏄繖涓骇鍝�
-      const hasOther = dataValue.dataList.some(item =>
-        (item as any).tempId !== dataValue.currentRowName &&
-        (item as any).productName === tableData[0].productName &&
-        (item as any).model === tableData[0].model
-      );
-      if (hasOther) {
-        ElMessage.warning("鏈�澶栧眰鍜屽綋鍓嶄骇鍝佷竴鏍风殑涓�绾у彧鑳芥湁涓�涓�");
-        return;
       }
     }
-  }
-  // 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.dataList[dataValue.currentRowIndex].unit = row[0].unit || "";
-  dataValue.dataList.map(item => {
-    if (item.tempId === dataValue.currentRowName) {
+    // 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.dataList[dataValue.currentRowIndex].unit = row[0].unit || "";
+    dataValue.dataList.map(item => {
+      if (item.tempId === dataValue.currentRowName) {
+        item.productName = productData.productName;
+        item.model = productData.model;
+        item.productModelId = productData.id;
+        item.unit = productData.unit || "";
+        return;
+      }
+      childItem(item, dataValue.currentRowName, productData);
+    });
+    dataValue.showProductDialog = false;
+  };
+  const childItem = (item: any, tempId: any, productData: any) => {
+    if (item.tempId === tempId) {
       item.productName = productData.productName;
       item.model = productData.model;
       item.productModelId = productData.id;
       item.unit = productData.unit || "";
-      return;
+      return true;
     }
-    childItem(item, dataValue.currentRowName, productData);
-  });
-  dataValue.showProductDialog = false;
-};
-const childItem = (item: any, tempId: any, productData: any) => {
-  if (item.tempId === tempId) {
-    item.productName = productData.productName;
-    item.model = productData.model;
-    item.productModelId = productData.id;
-    item.unit = productData.unit || "";
-    return true;
-  }
-  if (item.children && item.children.length > 0) {
-    for (let child of item.children) {
-      if (childItem(child, tempId, productData)) {
-        return true;
-      }
-    }
-  }
-  return false;
-};
-
-// 閫掑綊鏍¢獙鎵�鏈夊眰绾х殑琛ㄥ崟鏁版嵁
-const validateAll = () => {
-  let isValid = true;
-
-  // 鏍¢獙鍑芥暟
-  const validateItem = (item: any, isTopLevel = false) => {
-    // 鏍¢獙褰撳墠椤圭殑蹇呭~瀛楁
-    if (!item.model) {
-      ElMessage.error("璇烽�夋嫨瑙勬牸");
-      isValid = false;
-      return;
-    }
-    if (!isTopLevel && !item.processId) {
-      ElMessage.error("璇烽�夋嫨娑堣�楀伐搴�");
-      isValid = false;
-      return;
-    }
-    if (!item.unitQuantity) {
-      ElMessage.error("璇疯緭鍏ュ崟浣嶄骇鍑烘墍闇�鏁伴噺");
-      isValid = false;
-      return;
-    }
-    if (isOrderPage.value && !item.demandedQuantity) {
-      ElMessage.error("璇疯緭鍏ラ渶姹傛�婚噺");
-      isValid = false;
-      return;
-    }
-    if (!item.unit) {
-      ElMessage.error("璇疯緭鍏ュ崟浣�");
-      isValid = false;
-      return;
-    }
-
-    // 閫掑綊鏍¢獙瀛愰」
     if (item.children && item.children.length > 0) {
-      item.children.forEach(child => {
-        validateItem(child, false);
-      });
-    }
-  };
-
-  // 閬嶅巻鎵�鏈夐《灞傞」
-  dataValue.dataList.forEach(item => {
-    validateItem(item, true);
-  });
-
-  return isValid;
-};
-
-const submit = () => {
-  dataValue.loading = true;
-
-  // 鍏堣繘琛岃〃鍗曟牎楠�
-  const valid = validateAll();
-  console.log(dataValue.dataList, "dataValue.dataList");
-  if (valid) {
-    add({
-      bomId: routeId.value,
-      children: dataValue.dataList || [],
-    })
-      .then(res => {
-        router.push({
-          path: "/productionManagement/productionManagement/productStructure/index",
-        });
-        ElMessage.success("淇濆瓨鎴愬姛");
-        dataValue.loading = false;
-      })
-      .catch(() => {
-        dataValue.loading = false;
-      });
-  } else {
-    dataValue.loading = false;
-  }
-};
-
-const removeItem = (tempId: string) => {
-  // 鍏堝皾璇曚粠椤跺眰鍒犻櫎
-  const topIndex = dataValue.dataList.findIndex(item => item.tempId === tempId);
-  if (topIndex !== -1) {
-    dataValue.dataList.splice(topIndex, 1);
-    return;
-  }
-
-  // 閫掑綊鍒犻櫎瀛愰」
-  const delchildItem = (items: any[], tempId: any) => {
-    for (let i = 0; i < items.length; i++) {
-      const item = items[i];
-      if (item.tempId === tempId) {
-        items.splice(i, 1);
-        return true;
-      }
-      if (item.children && item.children.length > 0) {
-        if (delchildItem(item.children, tempId)) {
+      for (let child of item.children) {
+        if (childItem(child, tempId, productData)) {
           return true;
         }
       }
@@ -360,15 +315,142 @@
     return false;
   };
 
-  dataValue.dataList.forEach(item => {
-    if (item.children && item.children.length > 0) {
-      delchildItem(item.children, tempId);
+  // 閫掑綊鏍¢獙鎵�鏈夊眰绾х殑琛ㄥ崟鏁版嵁
+  const validateAll = () => {
+    let isValid = true;
+
+    // 鏍¢獙鍑芥暟
+    const validateItem = (item: any, isTopLevel = false) => {
+      // 鏍¢獙褰撳墠椤圭殑蹇呭~瀛楁
+      if (!item.model) {
+        ElMessage.error("璇烽�夋嫨瑙勬牸");
+        isValid = false;
+        return;
+      }
+      if (!isTopLevel && !item.processId) {
+        ElMessage.error("璇烽�夋嫨娑堣�楀伐搴�");
+        isValid = false;
+        return;
+      }
+      if (!item.unitQuantity) {
+        ElMessage.error("璇疯緭鍏ュ崟浣嶄骇鍑烘墍闇�鏁伴噺");
+        isValid = false;
+        return;
+      }
+      if (isOrderPage.value && !item.demandedQuantity) {
+        ElMessage.error("璇疯緭鍏ラ渶姹傛�婚噺");
+        isValid = false;
+        return;
+      }
+      // if (!item.unit) {
+      //   ElMessage.error("璇疯緭鍏ュ崟浣�");
+      //   isValid = false;
+      //   return;
+      // }
+
+      // 閫掑綊鏍¢獙瀛愰」
+      if (item.children && item.children.length > 0) {
+        item.children.forEach(child => {
+          validateItem(child, false);
+        });
+      }
+    };
+
+    // 閬嶅巻鎵�鏈夐《灞傞」
+    dataValue.dataList.forEach(item => {
+      validateItem(item, true);
+    });
+
+    return isValid;
+  };
+
+  const submit = () => {
+    dataValue.loading = true;
+
+    // 鍏堣繘琛岃〃鍗曟牎楠�
+    const valid = validateAll();
+    console.log(dataValue.dataList, "dataValue.dataList");
+    if (valid) {
+      add({
+        bomId: routeId.value,
+        children: dataValue.dataList || [],
+      })
+        .then(res => {
+          router.push({
+            path: "/productionManagement/productionManagement/productStructure/index",
+          });
+          ElMessage.success("淇濆瓨鎴愬姛");
+          dataValue.loading = false;
+        })
+        .catch(() => {
+          dataValue.loading = false;
+        });
+    } else {
+      dataValue.loading = false;
     }
-  });
-};
-const addItem2 = tempId => {
-  dataValue.dataList.map(item => {
+  };
+
+  const removeItem = (tempId:string) => {
+    // 鍏堝皾璇曚粠椤跺眰鍒犻櫎
+    const topIndex = dataValue.dataList.findIndex(item => item.tempId === tempId);
+    if (topIndex !== -1) {
+      dataValue.dataList.splice(topIndex, 1);
+      return;
+    }
+
+    // 閫掑綊鍒犻櫎瀛愰」
+    const delchildItem = (items: any[], tempId: any) => {
+      for (let i = 0; i < items.length; i++) {
+        const item = items[i];
+        if (item.tempId === tempId) {
+          items.splice(i, 1);
+          return true;
+        }
+        if (item.children && item.children.length > 0) {
+          if (delchildItem(item.children, tempId)) {
+            return true;
+          }
+        }
+      }
+      return false;
+    };
+
+    dataValue.dataList.forEach(item => {
+      if (item.children && item.children.length > 0) {
+        delchildItem(item.children, tempId);
+      }
+    });
+  };
+  const addItem2 = tempId => {
+    dataValue.dataList.map(item => {
+      if (item.tempId === tempId) {
+        if (!item.children) {
+          item.children = [];
+        }
+        item.children.push({
+          parentId: item.id || "",
+          parentTempId: item.tempId || "",
+          productName: "",
+          productId: "",
+          model: undefined,
+          productModelId: undefined,
+          processId: "",
+          processName: "",
+          unitQuantity: 0,
+          demandedQuantity: 0,
+          unit: "",
+          children: [],
+
+          tempId: new Date().getTime(),
+        });
+        return;
+      }
+      addchildItem(item, tempId);
+    });
+  };
+  const addchildItem = (item: any, tempId: any) => {
     if (item.tempId === tempId) {
+      console.log(item, "item");
       if (!item.children) {
         item.children = [];
       }
@@ -380,70 +462,55 @@
         model: undefined,
         productModelId: undefined,
         processId: "",
-        processName: "",
         unitQuantity: 0,
         demandedQuantity: 0,
-        unit: "",
         children: [],
-
+        unit: "",
         tempId: new Date().getTime(),
       });
-      return;
+      return true;
     }
-    addchildItem(item, tempId);
-  });
-};
-const addchildItem = (item: any, tempId: any) => {
-  if (item.tempId === tempId) {
-    console.log(item, "item");
-    if (!item.children) {
-      item.children = [];
-    }
-    item.children.push({
-      parentId: item.id || "",
-      parentTempId: item.tempId || "",
-      productName: "",
-      productId: "",
-      model: undefined,
-      productModelId: undefined,
-      processId: "",
-      unitQuantity: 0,
-      demandedQuantity: 0,
-      children: [],
-      unit: "",
-      tempId: new Date().getTime(),
-    });
-    return true;
-  }
-  if (item.children && item.children.length > 0) {
-    for (let child of item.children) {
-      if (addchildItem(child, tempId)) {
-        return true;
+    if (item.children && item.children.length > 0) {
+      for (let child of item.children) {
+        if (addchildItem(child, tempId)) {
+          return true;
+        }
       }
     }
-  }
-  return false;
-};
+    return false;
+  };
 
-const cancelEdit = () => {
-  dataValue.isEdit = false;
-  // dataValue.dataList = dataValue.dataList.filter(item => item.id !== undefined);
-  fetchData();
-};
+  const getPropPath = (row, field) => {
+    // 涓烘瘡涓猺ow鐢熸垚鍞竴鐨勮矾寰�
+    // 浣跨敤row.id鎴栫储寮曚綔涓哄敮涓�鏍囪瘑
+    let path = "dataList";
 
-onMounted(async () => {
-  // 浠庤矾鐢卞弬鏁板洖鏄炬暟鎹�
-  tableData[0].productName = routeProductName.value as string;
-  tableData[0].model = routeProductModelName.value as string;
-  tableData[0].bomNo = routeBomNo.value as string;
+    // 绠�鍗曞疄鐜帮細浣跨敤row鐨刬d鎴栦竴涓敮涓�鏍囪瘑
+    const uniqueId = row.id || Math.floor(Math.random() * 10000);
+    path += `.${uniqueId}`;
 
-  // 璁㈠崟鎯呭喌涓嬬鐢ㄧ紪杈�
-  if (isOrderPage.value) {
+    return path + `.${field}`;
+  };
+
+  const cancelEdit = () => {
     dataValue.isEdit = false;
-  }
+    // dataValue.dataList = dataValue.dataList.filter(item => item.id !== undefined);
+    fetchData();
+  };
 
-  // 鍏堝姞杞藉伐搴忛�夐」锛屽啀鍔犺浇鏁版嵁锛岀‘淇漞l-select鑳藉姝g‘鍥炴樉
-  await fetchProcessOptions();
-  await fetchData();
-});
+  onMounted(async () => {
+    // 浠庤矾鐢卞弬鏁板洖鏄炬暟鎹�
+    tableData[0].productName = routeProductName.value as string;
+    tableData[0].model = routeProductModelName.value as string;
+    tableData[0].bomNo = routeBomNo.value as string;
+
+    // 璁㈠崟鎯呭喌涓嬬鐢ㄧ紪杈�
+    if (isOrderPage.value) {
+      dataValue.isEdit = false;
+    }
+
+    // 鍏堝姞杞藉伐搴忛�夐」锛屽啀鍔犺浇鏁版嵁锛岀‘淇漞l-select鑳藉姝g‘鍥炴樉
+    await fetchProcessOptions();
+    await fetchData();
+  });
 </script>
\ 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 e7e6e15..971bc6e 100644
--- a/src/views/productionManagement/productionDispatching/components/formDia.vue
+++ b/src/views/productionManagement/productionDispatching/components/formDia.vue
@@ -111,6 +111,7 @@
 
 <script setup>
 import {ref} from "vue";
+import {getStaffJoinInfo, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js";
 import {userListNoPageByTenantId} from "@/api/system/user.js";
 import {productionDispatch} from "@/api/productionManagement/productionOrder.js";
 import useUserStore from "@/store/modules/user.js";
diff --git a/src/views/productionManagement/productionReporting/components/formDia.vue b/src/views/productionManagement/productionReporting/components/formDia.vue
index 126c5b0..2eb1c0b 100644
--- a/src/views/productionManagement/productionReporting/components/formDia.vue
+++ b/src/views/productionManagement/productionReporting/components/formDia.vue
@@ -94,6 +94,7 @@
 
 <script setup>
 import {ref} from "vue";
+import {getStaffJoinInfo, staffJoinAdd, staffJoinUpdate} from "@/api/personnelManagement/onboarding.js";
 import {userListNoPageByTenantId} from "@/api/system/user.js";
 import {productionReport, productionReportUpdate} from "@/api/productionManagement/productionReporting.js";
 const { proxy } = getCurrentInstance()
diff --git a/src/views/productionManagement/workOrder/components/filesDia.vue b/src/views/productionManagement/workOrder/components/filesDia.vue
new file mode 100644
index 0000000..ca6d7a9
--- /dev/null
+++ b/src/views/productionManagement/workOrder/components/filesDia.vue
@@ -0,0 +1,202 @@
+<template>
+  <div>
+    <el-dialog v-model="dialogVisible" title="宸ュ崟闄勪欢" width="50%" @close="closeDia">
+      <div style="margin-bottom: 10px; text-align: right">
+        <el-upload
+          v-model:file-list="fileList"
+          class="upload-demo"
+          :action="uploadUrl"
+          :on-success="handleUploadSuccess"
+          :on-error="handleUploadError"
+          :before-upload="beforeUpload"
+          name="file"
+          :show-file-list="false"
+          :headers="headers"
+          accept="image/*"
+          style="display: inline; margin-right: 10px"
+        >
+          <el-button type="primary">涓婁紶鍥剧墖</el-button>
+        </el-upload>
+        <el-button type="danger" plain @click="handleDelete">鍒犻櫎</el-button>
+      </div>
+
+      <PIMTable
+        rowKey="id"
+        :column="tableColumn"
+        :tableData="tableData"
+        :page="page"
+        :total="page.total"
+        :tableLoading="tableLoading"
+        :isSelection="true"
+        @selection-change="handleSelectionChange"
+        @pagination="paginationSearch"
+        height="500"
+      />
+
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="closeDia">鍏抽棴</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <filePreview ref="filePreviewRef" />
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, getCurrentInstance } from "vue";
+import { ElMessageBox } from "element-plus";
+import { getToken } from "@/utils/auth.js";
+import PIMTable from "@/components/PIMTable/PIMTable.vue";
+import filePreview from "@/components/filePreview/index.vue";
+import {
+  productWorkOrderFileAdd,
+  productWorkOrderFileDel,
+  productWorkOrderFileListPage,
+} from "@/api/productionManagement/productWorkOrderFile.js";
+
+const { proxy } = getCurrentInstance();
+const emit = defineEmits(["close"]);
+
+const dialogVisible = ref(false);
+const currentWorkOrderId = ref("");
+const selectedRows = ref([]);
+const filePreviewRef = ref();
+
+const tableColumn = ref([
+  {
+    label: "鏂囦欢鍚嶇О",
+    prop: "name",
+  },
+  {
+    dataType: "action",
+    label: "鎿嶄綔",
+    align: "center",
+    width: 120,
+    operation: [
+      {
+        name: "涓嬭浇",
+        type: "text",
+        clickFun: row => {
+          proxy.$download.name(row.url);
+        },
+      },
+      {
+        name: "棰勮",
+        type: "text",
+        clickFun: row => {
+          filePreviewRef.value?.open(row.url);
+        },
+      },
+    ],
+  },
+]);
+
+const page = reactive({
+  current: 1,
+  size: 100,
+  total: 0,
+});
+const tableData = ref([]);
+const fileList = ref([]);
+const tableLoading = ref(false);
+const headers = ref({
+  Authorization: "Bearer " + getToken(),
+});
+const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/file/upload");
+
+const beforeUpload = file => {
+  const isImage = file?.type?.startsWith("image/");
+  if (!isImage) {
+    proxy.$modal.msgError("鍙兘涓婁紶鍥剧墖鏂囦欢");
+  }
+  return isImage;
+};
+
+const openDialog = row => {
+  dialogVisible.value = true;
+  currentWorkOrderId.value = row.id;
+  page.current = 1;
+  getList();
+};
+
+const closeDia = () => {
+  dialogVisible.value = false;
+  emit("close");
+};
+
+const paginationSearch = obj => {
+  page.current = obj.page;
+  page.size = obj.limit;
+  getList();
+};
+
+const getList = () => {
+  tableLoading.value = true;
+  productWorkOrderFileListPage({
+    workOrderId: currentWorkOrderId.value,
+    current: page.current,
+    size: page.size,
+  })
+    .then(res => {
+      tableData.value = res.data.records || [];
+      page.total = res.data.total || 0;
+    })
+    .finally(() => {
+      tableLoading.value = false;
+    });
+};
+
+const handleSelectionChange = selection => {
+  selectedRows.value = selection;
+};
+
+function handleUploadSuccess(res) {
+  if (res.code == 200) {
+    const fileRow = {
+      name: res.data.originalName,
+      url: res.data.tempPath,
+      workOrderId: currentWorkOrderId.value,
+    };
+    productWorkOrderFileAdd(fileRow).then(() => {
+      proxy.$modal.msgSuccess("鏂囦欢涓婁紶鎴愬姛");
+      getList();
+    });
+  } else {
+    proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触");
+  }
+}
+
+function handleUploadError() {
+  proxy.$modal.msgError("鏂囦欢涓婁紶澶辫触");
+}
+
+const handleDelete = () => {
+  if (selectedRows.value.length === 0) {
+    proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+    return;
+  }
+  const ids = selectedRows.value.map(item => item.id);
+  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎", {
+    confirmButtonText: "纭",
+    cancelButtonText: "鍙栨秷",
+    type: "warning",
+  })
+    .then(() => {
+      productWorkOrderFileDel(ids).then(() => {
+        proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+        getList();
+      });
+    })
+    .catch(() => {
+      proxy.$modal.msg("宸插彇娑�");
+    });
+};
+
+defineExpose({
+  openDialog,
+});
+</script>
+
+<style scoped></style>
+
diff --git a/src/views/productionManagement/workOrder/index.vue b/src/views/productionManagement/workOrder/index.vue
index de91893..291ca18 100644
--- a/src/views/productionManagement/workOrder/index.vue
+++ b/src/views/productionManagement/workOrder/index.vue
@@ -209,6 +209,7 @@
         </span>
       </template>
     </el-dialog>
+    <FilesDia ref="workOrderFilesRef" />
   </div>
 </template>
 
@@ -220,10 +221,12 @@
     productWorkOrderPage,
     updateProductWorkOrder,
     addProductMain,
+    downProductWorkOrder,
   } from "@/api/productionManagement/workOrder.js";
   import { getUserProfile, userListNoPageByTenantId } from "@/api/system/user.js";
   import QRCode from "qrcode";
   import { getCurrentInstance, reactive, toRefs } from "vue";
+  import FilesDia from "./components/filesDia.vue";
   const { proxy } = getCurrentInstance();
 
   const tableColumn = ref([
@@ -307,7 +310,13 @@
         {
           name: "娴佽浆鍗�",
           clickFun: row => {
-            showTransferCard(row);
+            downloadAndPrintWorkOrder(row);
+          },
+        },
+        {
+          name: "闄勪欢",
+          clickFun: row => {
+            openWorkOrderFiles(row);
           },
         },
         {
@@ -330,6 +339,7 @@
   const transferCardQrUrl = ref("");
   const transferCardRowData = ref(null);
   const reportDialogVisible = ref(false);
+  const workOrderFilesRef = ref(null);
   const userOptions = ref([]);
   const reportForm = reactive({
     planQuantity: 0,
@@ -395,6 +405,56 @@
       });
   };
 
+  // 涓嬭浇骞舵墦鍗板伐鍗曟祦杞崱锛堟枃浠舵祦锛�
+  const downloadAndPrintWorkOrder = async row => {
+    if (!row || !row.id) {
+      proxy.$modal.msgError("缂哄皯宸ュ崟ID锛屾棤娉曚笅杞芥祦杞崱");
+      return;
+    }
+    const fileName = row.workOrderNo
+      ? `宸ュ崟娴佽浆鍗${row.workOrderNo}.xlsx`
+      : "宸ュ崟娴佽浆鍗�.xlsx";
+    try {
+      // 璋冪敤鎺ュ彛锛屼互 responseType: 'blob' 鑾峰彇鏂囦欢娴�
+      const blob = await downProductWorkOrder(row.id);
+
+      if (!blob) {
+        proxy.$modal.msgError("鏈幏鍙栧埌娴佽浆鍗℃枃浠�");
+        return;
+      }
+
+      // 鍒涘缓 Blob URL
+      const fileBlob =
+        blob instanceof Blob ? blob : new Blob([blob], { type: blob.type || "application/octet-stream" });
+      const url = window.URL.createObjectURL(fileBlob);
+
+      // 鍒涘缓闅愯棌 iframe锛岀敤浜庤Е鍙戞祻瑙堝櫒鎵撳嵃
+      const iframe = document.createElement("iframe");
+      iframe.style.position = "fixed";
+      iframe.style.right = "0";
+      iframe.style.bottom = "0";
+      iframe.style.width = "0";
+      iframe.style.height = "0";
+      iframe.style.border = "0";
+      iframe.src = url;
+      document.body.appendChild(iframe);
+
+      iframe.onload = () => {
+        try {
+          iframe.contentWindow?.focus();
+          iframe.contentWindow?.print();
+        } catch (e) {
+          console.error("鑷姩璋冪敤鎵撳嵃澶辫触", e);
+          // 閫�鑰屾眰鍏舵锛屾墦寮�鏂扮獥鍙g敱鐢ㄦ埛鎵嬪姩鎵撳嵃
+          window.open(url);
+        }
+      };
+    } catch (e) {
+      console.error("涓嬭浇宸ュ崟娴佽浆鍗″け璐�", e);
+      proxy.$modal.msgError("涓嬭浇宸ュ崟娴佽浆鍗″け璐�");
+    }
+  };
+
   const showTransferCard = async row => {
     transferCardRowData.value = row;
     const qrContent = String(row.id);
@@ -450,6 +510,10 @@
     reportDialogVisible.value = true;
   };
 
+  const openWorkOrderFiles = row => {
+    workOrderFilesRef.value?.openDialog(row);
+  };
+
   const handleReport = () => {
     if (reportForm.planQuantity <= 0) {
       ElMessageBox.alert("寰呯敓浜ф暟閲忎负0锛屾棤娉曟姤宸�", "鎻愮ず", {

--
Gitblit v1.9.3