From ccd67e291e00a2ad9c29ad8df43de6fab5a4afed Mon Sep 17 00:00:00 2001
From: 张诺 <zhang_12370@163.com>
Date: 星期四, 09 四月 2026 09:30:08 +0800
Subject: [PATCH] feat(协同审批/报价单): 添加附件上传、预览和下载功能

---
 src/views/productionManagement/productionProcess/New.vue |  200 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 195 insertions(+), 5 deletions(-)

diff --git a/src/views/productionManagement/productionProcess/New.vue b/src/views/productionManagement/productionProcess/New.vue
index 5443e8d..3a6222a 100644
--- a/src/views/productionManagement/productionProcess/New.vue
+++ b/src/views/productionManagement/productionProcess/New.vue
@@ -8,6 +8,21 @@
     >
       <el-form label-width="140px" :model="formState" label-position="top" ref="formRef">
         <el-form-item
+            label="宸ュ簭缂栧彿锛�"
+            prop="no"
+            :rules="[
+                {
+                required: true,
+                message: '璇疯緭鍏ュ伐搴忕紪鍙�',
+              },
+              {
+                max: 100,
+                message: '鏈�澶�100涓瓧绗�',
+              }
+            ]">
+          <el-input v-model="formState.no" placeholder="璇疯緭鍏ュ伐搴忓悕绉�" />
+        </el-form-item>
+        <el-form-item
             label="宸ュ簭鍚嶇О锛�"
             prop="name"
             :rules="[
@@ -20,13 +35,60 @@
                 message: '鏈�澶�100涓瓧绗�',
               }
             ]">
-          <el-input v-model="formState.name" />
+          <el-input v-model="formState.name" placeholder="璇疯緭鍏ュ伐搴忓悕绉�" />
         </el-form-item>
-        <el-form-item label="宸ュ簭缂栧彿" prop="no">
-          <el-input v-model="formState.no"  />
+        <el-form-item
+            label="宸ュ簭鏈哄彴"
+            prop="deviceId"
+            :rules="[
+                {
+                required: true,
+                message: '璇烽�夋嫨宸ュ簭绫诲瀷',
+              }
+            ]"
+        >
+          <el-select
+              v-model="formState.deviceId"
+              placeholder="璇烽�夋嫨宸ュ簭鏈哄彴"
+              filterable
+              remote
+              clearable
+              reserve-keyword
+              :remote-method="handleDeviceRemoteSearch"
+              :loading="deviceLoading"
+              @clear="handleDeviceClear"
+              @change="handleDeviceChange"
+              @visible-change="handleDeviceDropdownVisible"
+              popper-class="device-select-popper"
+          >
+            <el-option v-for="item in equipmentList" :key="item.id" :label="item.deviceName" :value="item.id" />
+            <el-option
+                v-if="equipmentList.length > 0 && deviceHasMore"
+                :value="__deviceLoadMoreSentinel"
+                label="鍔犺浇鏇村鈥�"
+                disabled
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item
+            label="宸ュ簭绫诲瀷"
+            prop="type"
+            :rules="[
+                {
+                required: true,
+                message: '璇烽�夋嫨宸ュ簭绫诲瀷',
+              }
+            ]"
+        >
+          <el-select v-model="formState.type" placeholder="璇烽�夋嫨宸ュ簭绫诲瀷">
+            <el-option label="璁℃椂" :value="0" />
+            <el-option label="璁′欢" :value="1" />
+          </el-select>
         </el-form-item>
         <el-form-item label="宸ヨ祫瀹氶" prop="salaryQuota">
-          <el-input v-model="formState.salaryQuota" type="number" :step="0.001" />
+          <el-input v-model="formState.salaryQuota" type="number" :step="0.001">
+            <template #append>鍏�</template>
+          </el-input>
         </el-form-item>
         <el-form-item label="鏄惁璐ㄦ" prop="isQuality">
           <el-switch v-model="formState.isQuality" :active-value="true" inactive-value="false"/>
@@ -46,8 +108,9 @@
 </template>
 
 <script setup>
-import { ref, computed, getCurrentInstance } from "vue";
+import { ref, computed, onMounted, getCurrentInstance, reactive, nextTick, onBeforeUnmount } from "vue";
 import {add} from "@/api/productionManagement/productionProcess.js";
+import {getLedgerPage} from "@/api/equipmentManagement/ledger.js";
 
 const props = defineProps({
   visible: {
@@ -61,10 +124,26 @@
 // 鍝嶅簲寮忔暟鎹紙鏇夸唬閫夐」寮忕殑 data锛�
 const formState = ref({
   name: '',
+  type: undefined,
   remark: '',
   salaryQuota:  '',
   isQuality: false,
 });
+
+// 鍒嗛〉鏌ヨ鍙傛暟
+const page = reactive({
+  current: 1,
+  size: 10,
+  total: 0,
+});
+
+onMounted(() => {
+  resetDeviceOptions();
+});
+
+const handleDeviceChange = (val) => {
+  formState.value.deviceName = equipmentList.value.find(item => item.id === val)?.deviceName || '';
+};
 
 const isShow = computed({
   get() {
@@ -76,6 +155,112 @@
 });
 
 let { proxy } = getCurrentInstance()
+
+
+const equipmentList = ref([]);
+const deviceLoading = ref(false);
+const deviceQuery = ref("");
+const deviceScrollWrap = ref(null);
+const __deviceLoadMoreSentinel = "__deviceLoadMoreSentinel";
+
+const deviceHasMore = computed(() => {
+  const total = Number(page.total ?? 0);
+  if (!total) {
+    return false;
+  }
+  return equipmentList.value.length < total;
+});
+
+// 鑾峰彇璁惧鍒楄〃锛堝垎椤垫煡璇級
+const getLedgerPageS = async ({ reset = false } = {}) => {
+  if (deviceLoading.value) return;
+  deviceLoading.value = true;
+  try {
+    const res = await getLedgerPage({
+      current: page.current,
+      size: page.size,
+      deviceName: deviceQuery.value ? deviceQuery.value : undefined,
+    });
+    const data = res?.data || {};
+    const records = Array.isArray(data.records) ? data.records : [];
+
+    page.total = Number(data.total ?? page.total ?? 0);
+    page.current = Number(data.current ?? page.current);
+    page.size = Number(data.size ?? page.size);
+
+    equipmentList.value = reset ? records : [...equipmentList.value, ...records];
+  } finally {
+    deviceLoading.value = false;
+  }
+};
+
+const resetDeviceOptions = async () => {
+  page.current = 1;
+  page.size = 10;
+  page.total = 0;
+  equipmentList.value = [];
+  await getLedgerPageS({ reset: true });
+};
+
+const loadMoreDevices = async () => {
+  if (deviceLoading.value) return;
+  if (!deviceHasMore.value) return;
+  page.current += 1;
+  await getLedgerPageS();
+};
+
+let remoteTimer = null;
+const handleDeviceRemoteSearch = (query) => {
+  const nextQuery = (query ?? "").trim();
+  deviceQuery.value = nextQuery;
+  if (remoteTimer) clearTimeout(remoteTimer);
+  remoteTimer = setTimeout(() => {
+    resetDeviceOptions();
+  }, 300);
+};
+
+const handleDeviceClear = () => {
+  deviceQuery.value = "";
+  resetDeviceOptions();
+};
+
+const onDeviceDropdownScroll = (e) => {
+  const el = e.target;
+  if (!el) return;
+  if (el.scrollHeight - el.scrollTop - el.clientHeight <= 20) {
+    loadMoreDevices();
+  }
+};
+
+const unbindDeviceDropdownScroll = () => {
+  if (deviceScrollWrap.value) {
+    deviceScrollWrap.value.removeEventListener("scroll", onDeviceDropdownScroll);
+    deviceScrollWrap.value = null;
+  }
+};
+
+const bindDeviceDropdownScroll = () => {
+  unbindDeviceDropdownScroll();
+  const wrap =
+      document.querySelector(".device-select-popper .el-scrollbar__wrap") ||
+      document.querySelector(".device-select-popper .el-select-dropdown__wrap");
+  if (wrap) {
+    deviceScrollWrap.value = wrap;
+    wrap.addEventListener("scroll", onDeviceDropdownScroll);
+  }
+};
+
+const handleDeviceDropdownVisible = async (visible) => {
+  if (!visible) {
+    unbindDeviceDropdownScroll();
+    return;
+  }
+  if (equipmentList.value.length === 0) {
+    await resetDeviceOptions();
+  }
+  await nextTick();
+  bindDeviceDropdownScroll();
+};
 
 const closeModal = () => {
   isShow.value = false;
@@ -100,4 +285,9 @@
   handleSubmit,
   isShow,
 });
+
+onBeforeUnmount(() => {
+  unbindDeviceDropdownScroll();
+  if (remoteTimer) clearTimeout(remoteTimer);
+});
 </script>

--
Gitblit v1.9.3