From ea324e1975ffec307758e00b5736b4399c36e6f6 Mon Sep 17 00:00:00 2001
From: 张诺 <zhang_12370@163.com>
Date: 星期五, 24 四月 2026 10:02:25 +0800
Subject: [PATCH] 绑定工艺路线调整

---
 src/views/productionManagement/productionOrder/BindRouteDialog.vue |  793 +++++++++++++++++++++++++++++++++++++++++
 src/views/productionManagement/productionOrder/index.vue           |  107 +++--
 src/api/productionManagement/productionOrder.js                    |   20 +
 src/components/Upload/ActionFileUpload.vue                         |  181 +++++++++
 4 files changed, 1,051 insertions(+), 50 deletions(-)

diff --git a/src/api/productionManagement/productionOrder.js b/src/api/productionManagement/productionOrder.js
index 6c8dbe2..23d52aa 100644
--- a/src/api/productionManagement/productionOrder.js
+++ b/src/api/productionManagement/productionOrder.js
@@ -129,4 +129,24 @@
     method: "post",
     data: data,
   });
+}
+
+
+// 鐢熶骇璁㈠崟缁戝畾鍏瑰畾浜庡伐鑹鸿矾绾�
+// /productionProductInput/save
+export function saveProductionProductInput(data) {
+    return request({
+        url: "/productionProductInput/save",
+        method: "post",
+        data,
+    })
+}
+
+// 鐢熻景璁㈠崟鏌ョ湅宸ヨ壓璺嚎
+// /productionProductInput/getByProductWordId/{productOrderId}
+export function viewGetByProductWordId(data) {
+    return request({
+        url: "/productionProductInput/getByProductWordId/"+data,
+        method: "post",
+    })
 }
\ No newline at end of file
diff --git a/src/components/Upload/ActionFileUpload.vue b/src/components/Upload/ActionFileUpload.vue
new file mode 100644
index 0000000..2d20b60
--- /dev/null
+++ b/src/components/Upload/ActionFileUpload.vue
@@ -0,0 +1,181 @@
+<template>
+  <el-upload
+    v-model:file-list="innerFileList"
+    :action="action"
+    :name="name"
+    :multiple="multiple"
+    ref="fileUploadRef"
+    :auto-upload="autoUpload"
+    :headers="headers"
+    :before-upload="handleBeforeUpload"
+    :on-error="handleUploadError"
+    :on-success="handleUploadSuccess"
+    :on-remove="handleRemove"
+    :on-preview="handlePreview"
+    :show-file-list="showFileList"
+  >
+    <el-button type="primary">{{ buttonText }}</el-button>
+    <template #file="{ file }">
+      <div style="display:flex; align-items:center; gap: 10px; width: 100%;">
+        <span style="flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
+          {{ file.name }}
+        </span>
+        <div style="display:flex; align-items:center; gap: 6px;">
+          <el-button link type="success" :icon="Download" @click="handleDownload(file)" />
+          <el-button link type="primary" :icon="View" @click="handlePreview(file)" />
+          <el-button link type="danger" :icon="Delete" @click="triggerRemoveFile(file)" />
+        </div>
+      </div>
+    </template>
+    <template #tip>
+      <div class="el-upload__tip">
+        {{ tipText }}
+      </div>
+    </template>
+  </el-upload>
+</template>
+
+<script setup>
+import { computed, ref } from "vue";
+import { Delete, Download, View } from "@element-plus/icons-vue";
+
+const props = defineProps({
+  name: {
+    type: String,
+    default: "file",
+  },
+  fileList: {
+    type: Array,
+    default: () => [],
+  },
+  action: {
+    type: String,
+    required: true,
+  },
+  headers: {
+    type: Object,
+    default: () => ({}),
+  },
+  multiple: {
+    type: Boolean,
+    default: true,
+  },
+  autoUpload: {
+    type: Boolean,
+    default: true,
+  },
+  showFileList: {
+    type: Boolean,
+    default: true,
+  },
+  buttonText: {
+    type: String,
+    default: "涓婁紶",
+  },
+  tipText: {
+    type: String,
+    default: "鏀寔鏂囨。鍜屽浘鐗囨牸寮�",
+  },
+  beforeUpload: {
+    type: Function,
+    default: null,
+  },
+  onError: {
+    type: Function,
+    default: null,
+  },
+  onSuccess: {
+    type: Function,
+    default: null,
+  },
+  onRemove: {
+    type: Function,
+    default: null,
+  },
+  onPreview: {
+    type: Function,
+    default: null,
+  },
+  onDownload: {
+    type: Function,
+    default: null,
+  },
+});
+
+const emit = defineEmits([
+  "update:fileList",
+  "error",
+  "success",
+  "remove",
+  "preview",
+  "download",
+]);
+
+const fileUploadRef = ref(null);
+
+const innerFileList = computed({
+  get: () => props.fileList || [],
+  set: (val) => emit("update:fileList", val),
+});
+
+const getFileUrl = (file) => {
+  return file?.url || file?.response?.data?.tempPath || file?.response?.data?.url || "";
+};
+
+const triggerRemoveFile = (file) => {
+  fileUploadRef.value?.handleRemove?.(file);
+};
+
+const handleBeforeUpload = (...args) => {
+  if (props.beforeUpload) {
+    return props.beforeUpload(...args);
+  }
+  return true;
+};
+
+const handleUploadError = (...args) => {
+  props.onError?.(...args);
+  emit("error", ...args);
+};
+
+const handleUploadSuccess = (...args) => {
+  props.onSuccess?.(...args);
+  emit("success", ...args);
+};
+
+const handleRemove = (...args) => {
+  props.onRemove?.(...args);
+  emit("remove", ...args);
+};
+
+const handlePreview = (file, ...rest) => {
+  if (props.onPreview) {
+    props.onPreview(file, ...rest);
+    emit("preview", file, ...rest);
+    return;
+  }
+  const url = getFileUrl(file);
+  if (url) {
+    window.open(url, "_blank");
+  }
+  emit("preview", file, ...rest);
+};
+
+const handleDownload = (file) => {
+  if (props.onDownload) {
+    props.onDownload(file);
+    emit("download", file);
+    return;
+  }
+  const url = getFileUrl(file);
+  if (!url) return;
+  const link = document.createElement("a");
+  link.href = url;
+  link.download = file?.name || "download";
+  link.target = "_blank";
+  document.body.appendChild(link);
+  link.click();
+  document.body.removeChild(link);
+  emit("download", file);
+};
+</script>
diff --git a/src/views/productionManagement/productionOrder/BindRouteDialog.vue b/src/views/productionManagement/productionOrder/BindRouteDialog.vue
new file mode 100644
index 0000000..a110438
--- /dev/null
+++ b/src/views/productionManagement/productionOrder/BindRouteDialog.vue
@@ -0,0 +1,793 @@
+<template>
+  <FormDialog
+    v-model="visible"
+    :title="type === 'add' ? '缁戝畾宸ヨ壓璺嚎' : '缂栬緫宸ヨ壓璺嚎'"
+    width="1400px"
+    :operation-type="type"
+    :column="8"
+    @close="handleClose"
+    @confirm="handleConfirm"
+    @cancel="handleClose"
+  >
+    <!-- ================= 鍩烘湰淇℃伅 ================= -->
+    <el-descriptions :column="3">
+      <el-descriptions-item label="缂栧彿" align="center" v-if="formData.productOrderList">
+        {{ formData.productOrderList.salesContractNo || "鏆傛棤鏁版嵁" }}
+      </el-descriptions-item>
+
+      <el-descriptions-item label="鍒跺崟鏃ユ湡" align="center" v-if="formData.productOrderList">
+        {{ formData.productOrderList.entryDate || "鏆傛棤鏁版嵁" }}
+      </el-descriptions-item>
+
+      <el-descriptions-item label="浜や粯鏃ユ湡" align="center" v-if="formData.productOrderList">
+        {{ formData.productOrderList.deliveryDate || "鏆傛棤鏁版嵁" }}
+      </el-descriptions-item>
+    </el-descriptions>
+
+    <el-descriptions border :column="4">
+      <el-descriptions-item label="濮旀墭鍗曚綅" :span="2" align="center">
+        {{formData.clientName || "--"}}
+      </el-descriptions-item>
+
+      <el-descriptions-item label="鏁伴噺" :span="1" align="center">
+        {{formData.orderQty || "--"}}
+      </el-descriptions-item>
+
+      <el-descriptions-item label="鎴愬搧灏哄" :span="1" align="center">
+        {{formData.specificationModel || "--"}}
+      </el-descriptions-item>
+
+      <el-descriptions-item label="浜у搧鍚嶇О" :span="2" align="center">
+        {{formData.productName || "--"}}
+      </el-descriptions-item>
+
+      <el-descriptions-item label="鍗曟嵁绫诲瀷" :span="2" align="center">
+        <el-checkbox-group v-model="introductionLetterList">
+          <el-checkbox label="浠嬬粛淇�" value="浠嬬粛淇�" />
+          <el-checkbox label="鍟嗘爣娉ㄥ唽涔�" value="鍟嗘爣娉ㄥ唽涔�" />
+          <el-checkbox label="濮斿嵃鍗�" value="濮斿嵃鍗�" />
+        </el-checkbox-group>
+      </el-descriptions-item>
+    </el-descriptions>
+
+    <!-- ================= 鏉愭枡琛� ================= -->
+    <div class="process-table-header">
+     <div class="section-title">鏉愭枡淇℃伅</div>
+      <el-button type="primary" size="small" @click="addMaterialRow">鏂板涓�琛�</el-button>
+    </div>
+    <el-table border :data="formData.materialInfo" style="width: 100%">
+      <el-table-column label="鏉愭枡鍚嶇О">
+        <template #default="{ row }">
+          <el-tree-select
+            v-model="row.productId"
+            placeholder="璇烽�夋嫨"
+            clearable
+            check-strictly
+            @change="(val) => getModels(val, row)"
+            :data="productOptions"
+            :render-after-expand="false"
+            style="width: 100%"
+          />
+        </template>
+      </el-table-column>
+      <el-table-column label="瑙勬牸">
+        <template #default="{ row }">
+          <el-select
+            v-model="row.productModelId"
+            placeholder="璇烽�夋嫨瑙勬牸"
+            filterable
+            clearable
+            @change="(val) => handleMaterialModelChange(val, row)"
+          >
+            <el-option
+              v-for="item in row.modelOptions || []"
+              :key="item.id"
+              :label="item.model"
+              :value="item.id"
+            />
+          </el-select>
+        </template>
+      </el-table-column>
+      <el-table-column label="鏁伴噺">
+        <template #default="{ row }">
+          <el-input v-model="row.num" placeholder="鏁伴噺">
+            <template #append>{{ row.numSuffix }}</template>
+          </el-input>
+        </template>
+      </el-table-column>
+      <el-table-column label="璁¢噺鍗曚綅">
+        <template #default="{ row }">
+          <el-input v-model="row.unit" placeholder="璁¢噺鍗曚綅" />
+        </template>
+      </el-table-column>
+      <el-table-column label="鍗曚环">
+        <template #default="{ row }">
+          <el-input v-model="row.price" placeholder="鍗曚环">
+            <template #append>{{ row.unitSuffix }}</template>
+          </el-input>
+        </template>
+      </el-table-column>
+      <el-table-column label="閲戦">
+        <template #default="{ row }">
+          <el-input v-model="row.totalAmount" placeholder="閲戦" />
+        </template>
+      </el-table-column>
+      <el-table-column label="鎿嶄綔" width="80">
+        <template #default="{ $index }">
+          <el-button type="danger" size="small" @click="removeMaterialRow($index)">鍒犻櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-descriptions border :column="2" :span="2">
+      <el-descriptions-item
+          label="娉ㄦ剰浜嬮」"
+          :span="2"
+          align="center"
+          style="white-space: pre-line; word-break: break-all; min-height: 60px;"
+      >
+        <el-input
+            v-model="formData.productDescription"
+            :autosize="{ minRows: 2, maxRows: 4 }"
+            type="textarea"
+            placeholder="璇疯緭鍏ユ敞鎰忎簨椤�"
+        />
+      </el-descriptions-item>
+    </el-descriptions>
+    <hr>
+    <!-- ================= 鍒囨枡鍥剧ず ================= -->
+    <div class="section-title">鍒囨枡鍥剧ず</div>
+    <ActionFileUpload
+        style="width: 50%;"
+        v-model:file-list="fileList"
+        :action="upload.url"
+        :headers="upload.headers"
+        :multiple="false"
+        :name="'files'"
+        :onSuccess="uploadSuccess"
+    tip-text="鏀寔鍥剧墖锛坖pg, jpeg, png锛夋牸寮�"
+    />
+    <!-- ================= 鍒囨枡淇℃伅 ================= -->
+    <el-descriptions
+      border
+      :column="6"
+      direction="vertical"
+      style="width: 100%"
+      class="fixed-desc"
+    >
+      <el-descriptions-item label="鍒囨枡灏哄" align="center">
+        <el-input v-model="formData.cutNum" placeholder="鍒囨枡灏哄" />
+      </el-descriptions-item>
+      <el-descriptions-item label="鍒囨枡鏁伴噺" align="center">
+        <el-input v-model="formData.cutSize" placeholder="鍒囨枡灏哄" />
+      </el-descriptions-item>
+      <el-descriptions-item label="涓洅鏁伴噺" align="center">
+        <el-input v-model="formData.mediumBoxQty" placeholder="涓洅鏁伴噺" />
+      </el-descriptions-item>
+      <el-descriptions-item label="灏忕洅鏁伴噺" align="center">
+        <el-input v-model="formData.smallBoxQty" placeholder="灏忕洅鏁伴噺" />
+      </el-descriptions-item>
+      <el-descriptions-item label="姝f暟" align="center">
+        <el-input v-model="formData.positiveQty" placeholder="姝f暟" />
+      </el-descriptions-item>
+      <el-descriptions-item label="鍔犳斁鏁�" align="center">
+        <el-input v-model="formData.allowanceQty" placeholder="鍔犳斁鏁�" />
+      </el-descriptions-item>
+    </el-descriptions>
+
+    <div class="middle-sheet-table">
+      <table class="middle-sheet-table__inner">
+        <tbody>
+          <tr>
+            <th>杞墖鐗�:</th>
+            <th colspan="2">寮�寮犺壊</th>
+            <th>鏅掓澘</th>
+            <th colspan="2">寮�鎷�</th>
+            <th>鍒垁鐗�</th>
+            <th>鑱旇壊鍧�</th>
+          </tr>
+          <tr>
+            <th rowspan="2">鍒剁増</th>
+            <th>璁捐鍒朵綔璐�</th>
+            <th>鎷肩増璐�</th>
+            <th>鍑虹墖璐�</th>
+            <th>鎵撴牱璐�</th>
+            <th>鍒垁鐗堣垂</th>
+            <th>鐑�/鍑哥増璐�</th>
+            <th>灏忚</th>
+          </tr>
+          <tr v-for="(plate, index) in formData.plateMaking" :key="index">
+            <td>
+              <el-input v-model="plate.designProductionFee" placeholder="璇疯緭鍏ヨ璁″埗浣滆垂" />
+            </td>
+            <td>
+              <el-input v-model="plate.impositionFee" placeholder="璇疯緭鍏ユ嫾鐗堣垂" />
+            </td>
+            <td>
+              <el-input v-model="plate.filmOutputFee" placeholder="璇疯緭鍏ュ嚭鐗囪垂" />
+            </td>
+            <td>
+              <el-input v-model="plate.proofingFee" placeholder="璇疯緭鍏ユ墦鏍疯垂" />
+            </td>
+            <td>
+              <el-input v-model="plate.doctorBladePlateFee" placeholder="璇疯緭鍏ュ埆鍒�鐗堣垂" />
+            </td>
+            <td>
+              <el-input v-model="plate.hotEmbossingPlateFee" placeholder="璇疯緭鍏ョ儷/鍑哥増璐�" />
+            </td>
+            <td>
+              <el-input v-model="plate.subtotalFee" placeholder="璇疯緭鍏ュ皬璁�" />
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+<!--  class="section-title" -->
+    <!-- ================= 宸ヨ壓鍔犲伐 ================= -->
+    <div class="process-table-header">
+     <div class="section-title">宸ヨ壓鍔犲伐</div>
+      <el-button type="primary" size="small" @click="addProcessRow">鏂板涓�琛�</el-button>
+    </div>
+    <el-table border :data="formData.processContent" style="width: 100%" :span-method="objectSpanMethod">
+      <el-table-column label="宸ュ簭" width="140">
+        <template #default="{ row }">
+          <el-table-column label="宸ュ簭" width="140">
+            <template #default="{ row }">
+              <el-select
+                  v-model="row.processId"
+                  placeholder="璇烽�夋嫨宸ュ簭"
+                  @change="(val) => onProcessChange(val, row)"
+              >
+                <el-option
+                    v-for="item in processOptions"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.id"
+                />
+              </el-select>
+            </template>
+          </el-table-column>
+        </template>
+      </el-table-column>
+      <el-table-column label="寮�鏁�">
+        <template #default="{ row }">
+          <el-input v-model="row.openCount" placeholder="璇疯緭鍏ュ紑鏁�" />
+        </template>
+      </el-table-column>
+      <el-table-column label="宸ヨ壓姝f暟">
+        <template #default="{ row }">
+          <el-input v-model="row.processPositive" placeholder="璇疯緭鍏ュ伐鑹烘鏁�" />
+        </template>
+      </el-table-column>
+      <el-table-column label="鍔犳斁鏁�">
+        <template #default="{ row }">
+          <el-input v-model="row.allowanceQty" placeholder="璇疯緭鍏ュ姞鏀炬暟" />
+        </template>
+      </el-table-column>
+      <el-table-column label="鏈哄彴" width="180">
+        <template #default="{ row }">
+          <el-select
+            v-model="row.deviceId"
+            placeholder="璇烽�夋嫨鏈哄彴"
+            filterable
+            clearable
+            @change="(val) => handleDeviceChange(val, row)"
+          >
+            <el-option
+              v-for="item in deviceOptions"
+              :key="item.id"
+              :label="item.deviceName"
+              :value="item.id"
+            />
+          </el-select>
+        </template>
+      </el-table-column>
+      <el-table-column label="鎶ュ伐浜�" width="220">
+        <template #default="{ row }">
+          <el-select
+            v-model="row.reportUserIds"
+            placeholder="璇烽�夋嫨鎶ュ伐浜�"
+            filterable
+            clearable
+            multiple
+            collapse-tags
+            collapse-tags-tooltip
+            @change="(val) => handleReportUsersChange(val, row)"
+          >
+            <el-option
+              v-for="item in userOptions"
+              :key="item.userId"
+              :label="item.nickName"
+              :value="item.userId"
+            />
+          </el-select>
+        </template>
+      </el-table-column>
+      <el-table-column label="宸ヨ壓瑕佹眰">
+        <template #default="{ rowIndex }">
+          <el-input
+            v-model="formData.processRequirement"
+            type="textarea"
+            :rows="6"
+            placeholder="璇疯緭鍏ュ伐鑹鸿姹�"
+          />
+        </template>
+      </el-table-column>
+      <el-table-column label="鎿嶄綔" width="80">
+        <template #default="{ $index }">
+          <el-button type="danger" size="small" @click="removeProcessRow($index)">鍒犻櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- ================= 鍖呰淇℃伅 ================= -->
+    <el-descriptions border :column="3" class="mt">
+      <el-descriptions-item label="閫佽揣鍦扮偣" align="center">
+        <el-input v-model="formData.deliveryAddress" placeholder="閫佽揣鍦扮偣" />
+      </el-descriptions-item>
+
+      <el-descriptions-item label="鑱旂郴浜�" align="center">
+        <el-input v-model="formData.contactName" placeholder="鑱旂郴浜�" />
+      </el-descriptions-item>
+
+      <el-descriptions-item label="鍖呰瑕佹眰" align="center">
+        <el-input v-model="formData.packagingRequirement" placeholder="鍖呰瑕佹眰" />
+      </el-descriptions-item>
+
+      <el-descriptions-item label="灏哄" align="center">
+        <el-input v-model="formData.postProcessSize" placeholder="灏哄" />
+      </el-descriptions-item>
+
+      <el-descriptions-item label="瀹氳揣鏁伴噺" align="center">
+        {{formData.orderQty || "--"}}
+      </el-descriptions-item>
+
+      <el-descriptions-item label="瀹炰氦鏁伴噺" :span="3" align="center">
+        <el-input v-model="formData.actualDeliveryQty" placeholder="瀹炰氦鏁伴噺" />
+      </el-descriptions-item>
+    </el-descriptions>
+  </FormDialog>
+</template>
+
+<script setup>
+import { ref, reactive, computed, onMounted, watch } from 'vue'
+import dayjs from 'dayjs'
+import FormDialog from '@/components/Dialog/FormDialog.vue'
+import ActionFileUpload from "@/components/Upload/ActionFileUpload.vue";
+import { list } from "@/api/productionManagement/productionProcess.js"
+import { modelList, productTreeList } from "@/api/basicData/product.js"
+import {getSalesLedgerWithProducts} from "@/api/salesManagement/salesLedger.js"
+import { getDeviceLedger } from "@/api/equipmentManagement/ledger.js"
+import { userListNoPageByTenantId } from "@/api/system/user.js"
+import { getToken } from "@/utils/auth";
+
+const props = defineProps({
+  modelValue: {
+    type: Boolean,
+    default: false
+  },
+  type: {
+    type: String,
+    default: 'add'
+  },
+  orderData: {
+    type: Object,
+    default: () => ({})
+  },
+  rowData: {
+    type: Object,
+    default: null
+  }
+})
+
+const emit = defineEmits(['update:modelValue', 'confirm'])
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (val) => emit('update:modelValue', val)
+})
+
+const processOptions = ref([])
+const deviceOptions = ref([])
+const userOptions = ref([])
+const reportWorkerList = ref([])
+const productOptions = ref([])
+const introductionLetterList = ref([])
+const fileList = ref([])
+const upload = reactive({
+  url: import.meta.env.VITE_APP_BASE_API + '/basic/customer-follow/upload',
+  headers: { Authorization: 'Bearer ' + getToken() }
+})
+
+const formData = reactive({
+  productOrderList:null,
+  salesLedgerId: null,
+  productOrderId: null,
+  printOrderTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+  fileList:[],
+  finishTime: "",
+  no: "",
+  productName: "",
+  productDescription: "",
+  clientName: "",
+  finishedSize: "",
+  cutNum: "",
+  cutSize:"",
+  mediumBoxQty: "",
+  smallBoxQty: "",
+  positiveQty: "",
+  allowanceQty: "",
+  introductionLetter: "",
+  plateMaking: [
+    {
+      designProductionFee: "",
+      impositionFee: "",
+      filmOutputFee: "",
+      proofingFee: "",
+      doctorBladePlateFee: "",
+      hotEmbossingPlateFee: "",
+      subtotalFee: ""
+    }
+  ],
+  processContent: [
+    {
+      id: "1",
+      processId: "",
+      processName: "",
+      mediumBoxQty: "",
+      smallBoxQty: "",
+      openCount: "",
+      processPositive: "",
+      allowanceQty: "",
+      deviceId: "",
+      deviceName: "",
+      reportUserIds: [],
+      reportWorkerList: []
+    }
+  ],
+  materialInfo: [
+    {
+      id: "1",
+      productId: "",
+      name: "",
+      productModelId: "",
+      model: "",
+      modelOptions: [],
+      num: "",
+      numSuffix: "寮�",
+      unitSuffix: "鍏�/kg",
+      unit: "",
+      price: "",
+      totalAmount: ""
+    }
+  ],
+  processRequirement: "",
+  deliveryAddress: "",
+  contactName: "",
+  packagingRequirement: "",
+  postProcessSize: "",
+  orderQty: "",
+  actualDeliveryQty: "",
+  productionDept: "",
+  technicalDept: "",
+  warehouseDept: "",
+  productModelId: "",
+  specificationModel:"",
+})
+
+// 鐩戝惉 checkbox group 鍙樺寲骞跺悓姝ュ埌 introductionLetter 瀛楃涓�
+watch(introductionLetterList, (val) => {
+  formData.introductionLetter = val.join(',')
+})
+const onProcessChange = (processId, row) => {
+  const selected = processOptions.find(item => item.id === processId)
+  row.processName = selected?.name || ''
+}
+const cloneDeep = (val) => JSON.parse(JSON.stringify(val))
+
+const uploadSuccess = (...args) => {
+  console.log(...args)
+}
+
+const mergeRowDataToForm = (source) => {
+  if (!source || typeof source !== 'object') {
+    return
+  }
+
+  Object.keys(formData).forEach((key) => {
+    if (source[key] !== undefined) {
+      formData[key] = Array.isArray(source[key]) ? cloneDeep(source[key]) : source[key]
+    }
+  })
+
+  // 鍏煎 index.vue 閲屽父鐢ㄥ瓧娈靛悕涓庡脊绐楀瓧娈靛悕涓嶄竴鑷寸殑鎯呭喌
+  if (source.productName === undefined && source.productCategory !== undefined) {
+    formData.productName = source.productCategory
+  }
+  if (source.orderQty === undefined && source.quantity !== undefined) {
+    formData.orderQty = source.quantity
+  }
+  if (source.no === undefined && source.salesContractNo !== undefined) {
+    formData.no = source.salesContractNo
+  }
+  if (source.clientName === undefined && source.customerName !== undefined) {
+    formData.clientName = source.customerName
+  }
+  if (source.productOrderId === undefined && source.id !== undefined) {
+    formData.productOrderId = source.id
+  }
+}
+
+// 鑾峰彇閿�鍞鍗�
+const getProductOrder = () => {
+  if(!formData.salesLedgerId) return
+  getSalesLedgerWithProducts({
+    type: "1",
+    id: formData.salesLedgerId
+  }).then(res => {
+    if(res){
+      formData.productOrderList = res
+    }
+    console.log(formData)
+  })
+
+}
+
+watch(() => props.orderData, (val) => {
+  mergeRowDataToForm(val)
+}, { immediate: true, deep: true })
+
+watch(
+  () => props.rowData,
+  (val) => {
+    mergeRowDataToForm(val)
+    getProductOrder()
+  },
+  { immediate: true, deep: true }
+)
+
+const getProcessList = () => {
+  list().then(res => {
+    processOptions.value = res.data
+  })
+}
+
+
+
+const getDeviceList = () => {
+  getDeviceLedger().then(res => {
+    deviceOptions.value = Array.isArray(res?.data) ? res.data : []
+  })
+}
+
+const getUserList = () => {
+  userListNoPageByTenantId().then(res => {
+    userOptions.value = Array.isArray(res?.data) ? res.data : []
+  })
+}
+
+const convertProductOptions = (data) => {
+  return data.map(item => ({
+    label: item.label || item.productName || item.name || "",
+    value: item.id,
+    children: item.children?.length ? convertProductOptions(item.children) : undefined
+  }))
+}
+
+const findProductLabelById = (options, productId) => {
+  for (const item of options) {
+    if (item.value === productId) {
+      return item.label
+    }
+    if (item.children?.length) {
+      const label = findProductLabelById(item.children, productId)
+      if (label) {
+        return label
+      }
+    }
+  }
+  return ""
+}
+
+const getMaterialProductOptions = () => {
+  productTreeList().then(res => {
+    const rawData = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : []
+    productOptions.value = convertProductOptions(rawData)
+  })
+}
+
+const handleProcessChange = (val, row) => {
+  console.log(row)
+  const process = processOptions.value.find(item => item.id === val)
+  if (process) {
+    row.processName = process.name
+    console.log(process)
+    if (process.deviceId) {
+      row.deviceId = process.deviceId || ""
+      row.deviceName = process.deviceName || ""
+    }
+  }
+}
+
+const handleDeviceChange = (val, row) => {
+  const device = deviceOptions.value.find(item => item.id === val)
+  row.deviceName = device?.deviceName || ""
+  row.deviceId = val || ""
+}
+
+const handleReportUsersChange = (val, row) => {
+  const userMap = new Map(
+      userOptions.value.map(item => [item.userId, item.nickName])
+  )
+  row.reportWorkerList = (val || []).map(userId => ({
+    userId,
+    userName: userMap.get(userId) || ''
+  }))
+}
+
+const getModels = (val, row) => {
+  row.productId = val || ""
+  row.name = val ? findProductLabelById(productOptions.value, val) : ""
+  row.productModelId = ""
+  row.model = ""
+  row.unit = ""
+  row.modelOptions = []
+  if (!val) {
+    return
+  }
+  modelList({ id: val }).then(res => {
+    row.modelOptions = Array.isArray(res) ? res : Array.isArray(res?.data) ? res.data : []
+  })
+}
+
+const handleMaterialModelChange = (val, row) => {
+  const currentModel = (row.modelOptions || []).find(item => item.id === val)
+  row.productModelId = val || ""
+  row.model = currentModel?.model || ""
+  row.unit = currentModel?.unit || ""
+}
+
+// 鏉愭枡淇℃伅
+const addMaterialRow = () => {
+  formData.materialInfo.push({
+    id: Date.now().toString(),
+    productId: "",
+    name: "",
+    productModelId: "",
+    model: "",
+    modelOptions: [],
+    num: "",
+    numSuffix: "寮�",
+    unitSuffix: "鍏�/kg",
+    unit: "",
+    price: "",
+    totalAmount: ""
+  })
+}
+
+const removeMaterialRow = (index) => {
+  formData.materialInfo.splice(index, 1)
+}
+
+const addProcessRow = () => {
+  formData.processContent.push({
+    id: Date.now().toString(),
+    processId: "",
+    processName: "",
+    openCount: "",
+    processPositive: "",
+    allowanceQty: "",
+    deviceId: "",
+    deviceName: "",
+    reportUserIds: [],
+    reportWorkerList: []
+  })
+}
+
+const removeProcessRow = (index) => {
+  formData.processContent.splice(index, 1)
+}
+
+const objectSpanMethod = ({ column, rowIndex }) => {
+  if (column.label === "宸ヨ壓瑕佹眰") {
+    if (rowIndex === 0) {
+      return {
+        rowspan: Math.max(formData.processContent.length, 1),
+        colspan: 1,
+      }
+    }
+    return {
+      rowspan: 0,
+      colspan: 0,
+    }
+  }
+}
+
+const handleClose = () => {
+  visible.value = false
+}
+
+const handleConfirm = () => {
+  emit('confirm', JSON.parse(JSON.stringify(formData)))
+}
+
+onMounted(() => {
+  getProcessList()
+  getDeviceList()
+  getUserList()
+  getMaterialProductOptions()
+})
+defineExpose({
+  getProductOrder
+})
+</script>
+
+<style scoped lang="scss">
+.section-title {
+  font-size: 16px;
+  font-weight: bold;
+  margin: 20px 0 10px;
+  padding-left: 10px;
+  border-left: 4px solid #409eff;
+}
+
+.fixed-desc {
+  margin-top: 20px;
+  :deep(.el-descriptions__table) {
+    table-layout: fixed;
+    width: 100%;
+  }
+  :deep(.el-descriptions__cell) {
+    width: 25%;
+    word-break: break-word;
+  }
+}
+
+.middle-sheet-table {
+  margin-top: 20px;
+  width: 100%;
+  border: 1px solid #ebeef5;
+}
+
+.middle-sheet-table__inner {
+  width: 100%;
+  border-collapse: collapse;
+  table-layout: fixed;
+
+  th, td {
+    border: 1px solid #ebeef5;
+    padding: 8px;
+    text-align: center;
+    font-size: 14px;
+  }
+
+  th {
+    background-color: #f5f7fa;
+    color: #606266;
+    font-weight: bold;
+  }
+
+  :deep(.el-input__wrapper) {
+    box-shadow: none !important;
+    background-color: transparent;
+  }
+}
+
+.process-table-header {
+  margin-top: 20px;
+  margin-bottom: 10px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  span {
+    font-size: 16px;
+    font-weight: bold;
+  }
+}
+
+.mt {
+  margin-top: 20px;
+}
+:deep(.el-textarea__inner){
+  box-shadow: none;
+}
+</style>
diff --git a/src/views/productionManagement/productionOrder/index.vue b/src/views/productionManagement/productionOrder/index.vue
index 55fcc05..15e7cba 100644
--- a/src/views/productionManagement/productionOrder/index.vue
+++ b/src/views/productionManagement/productionOrder/index.vue
@@ -11,7 +11,7 @@
                     style="width: 160px;"
                     @change="handleQuery" />
         </el-form-item>
-        <el-form-item label="鍚堝悓鍙�:">
+        <el-form-item label="璁㈠崟缂栧彿:">
           <el-input v-model="searchForm.salesContractNo"
                     placeholder="璇疯緭鍏�"
                     clearable
@@ -41,7 +41,7 @@
         </el-form-item>
       </el-form>
       <div>
-        <el-button type="primary" @click="isShowNewModal = true">鏂板</el-button>
+<!--        <el-button type="primary" @click="isShowNewModal = true">鏂板</el-button>-->
         <el-button type="danger" @click="handleDelete">鍒犻櫎</el-button>
         <el-button @click="handleOut">瀵煎嚭</el-button>
       </div>
@@ -65,32 +65,14 @@
         </template>
       </PIMTable>
     </div>
-    <el-dialog v-model="bindRouteDialogVisible"
-               title="缁戝畾宸ヨ壓璺嚎"
-               width="500px">
-      <el-form label-width="90px">
-        <el-form-item label="宸ヨ壓璺嚎">
-          <el-select v-model="bindForm.routeId"
-                     placeholder="璇烽�夋嫨宸ヨ壓璺嚎"
-                     style="width: 100%;"
-                     :loading="bindRouteLoading">
-            <el-option v-for="item in routeOptions"
-                       :key="item.id"
-                       :label="`${item.processRouteCode || ''}`"
-                       :value="item.id" />
-          </el-select>
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button type="primary"
-                     :loading="bindRouteSaving"
-                     @click="handleBindRouteConfirm">纭� 璁�</el-button>
-          <el-button @click="bindRouteDialogVisible = false">鍙� 娑�</el-button>
-        </span>
-      </template>
-    </el-dialog>
 
+    <BindRouteDialog
+        ref="BindRouteDialogRef"
+        v-model="bindRouteDialogVisible"
+        :type="bindDialogType"
+        :rowData="rowData"
+        @confirm="handleBindRouteSubmit"
+    />
     <new-product-order v-if="isShowNewModal"
                          v-model:visible="isShowNewModal"
                          @completed="handleQuery" />
@@ -98,25 +80,39 @@
 </template>
 
 <script setup>
-  import { onMounted, ref } from "vue";
+  import { defineAsyncComponent, getCurrentInstance, onMounted, reactive, ref, toRefs } from "vue";
   import { ElMessageBox } from "element-plus";
   import dayjs from "dayjs";
   import { useRouter } from "vue-router";
   import {
     productOrderListPage,
-    listProcessRoute,
     bindingRoute,
-    listProcessBom, delProductOrder,
+    delProductOrder,
+    saveProductionProductInput,
+    viewGetByProductWordId
   } from "@/api/productionManagement/productionOrder.js";
   import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
-  import {fileDel} from "@/api/financialManagement/revenueManagement.js";
   import PIMTable from "@/components/PIMTable/PIMTable.vue";
+  import BindRouteDialog from "./BindRouteDialog.vue";
+  import {getDeviceLedger} from "@/api/equipmentManagement/ledger.js";
   const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue"));
 
   const { proxy } = getCurrentInstance();
 
   const router = useRouter();
   const isShowNewModal = ref(false);
+  const MOCK_MODE = true;
+
+  const loading = ref(false)
+  const dialogVisible = ref(false)
+  const bindDialogType = ref('add')
+  const BindRouteDialogRef = ref(null)
+
+  const handleBindRouteSubmit =async (data)=>{
+    const res = await saveProductionProductInput(data)
+    console.log(res)
+
+  }
 
   const tableColumn = ref([
     {
@@ -187,21 +183,22 @@
       label: "鎿嶄綔",
       align: "center",
       fixed: "right",
-      width: 200,
+      width: 300,
       operation: [
-        {
-          name: "宸ヨ壓璺嚎",
-          type: "text",
-          clickFun: row => {
-            showRouteItemModal(row);
-          },
-        },
         {
           name: "缁戝畾宸ヨ壓璺嚎",
           type: "text",
           showHide: row => !row.processRouteCode,
           clickFun: row => {
             openBindRouteDialog(row);
+          },
+        },
+        {
+          name: "鏌ョ湅宸ヨ壓璺嚎",
+          type: "text",
+          showHide: row => row.processRouteCode,
+          clickFun: row => {
+            openBindRouteDialog(row,"view");
           },
         },
         {
@@ -273,25 +270,27 @@
   const bindRouteLoading = ref(false);
   const bindRouteSaving = ref(false);
   const routeOptions = ref([]);
+  const rowData = ref(null)
   const bindForm = reactive({
     orderId: null,
     routeId: null,
   });
 
-  const openBindRouteDialog = async row => {
+  const openBindRouteDialog = async (row,type) => {
     bindForm.orderId = row.id;
     bindForm.routeId = null;
     bindRouteDialogVisible.value = true;
     routeOptions.value = [];
-    if (!row.productModelId) {
-      proxy.$modal.msgWarning("褰撳墠璁㈠崟缂哄皯浜у搧鍨嬪彿锛屾棤娉曟煡璇㈠伐鑹鸿矾绾�");
-      bindRouteDialogVisible.value = false;
-      return;
-    }
     bindRouteLoading.value = true;
+    if(type === "view") {
+      bindDialogType.value = "view"
+      let res = await viewGetByProductWordId(row.id)
+      console.log(res)
+    }
+    BindRouteDialogRef.value?.getProductOrder()
+
     try {
-      const res = await listProcessRoute({ productModelId: row.productModelId });
-      routeOptions.value = res.data || [];
+      rowData.value = row;
     } catch (e) {
       console.error("鑾峰彇宸ヨ壓璺嚎鍒楄〃澶辫触锛�", e);
       proxy.$modal.msgError("鑾峰彇宸ヨ壓璺嚎鍒楄〃澶辫触");
@@ -443,8 +442,6 @@
       });
   };
 
-  const handleConfirmRoute = () => {};
-
   onMounted(() => {
     getList();
   });
@@ -464,10 +461,20 @@
 }
 
 ::v-deep .red {
-  background-color: #f80202;
+  background-color: #ffe5e5;
 }
 
 ::v-deep .purple{
   background-color: #F4DEFA;
 }
+
+:deep(.fixed-desc .el-descriptions__table) {
+  table-layout: fixed;
+  width: 100%;
+}
+
+:deep(.fixed-desc .el-descriptions__cell) {
+  width: 25%;
+  word-break: break-word;
+}
 </style>

--
Gitblit v1.9.3