Pārlūkot izejas kodu

套打弹窗工单/产品选择改为输入框模糊搜索。

Co-authored-by: Cursor <cursoragent@cursor.com>
unknown 1 mēnesi atpakaļ
vecāks
revīzija
6b622adc25
1 mainītis faili ar 126 papildinājumiem un 70 dzēšanām
  1. 126 70
      vue/src/components/PrintDialog.vue

+ 126 - 70
vue/src/components/PrintDialog.vue

@@ -30,25 +30,29 @@
           </el-form-item>
 
           <el-form-item :label="form.biz_type === 'product' ? '产品编号' : '工单编号'">
-            <el-select
+            <el-autocomplete
               v-model="form.no"
-              filterable
-              allow-create
-              default-first-option
+              :fetch-suggestions="searchBiz"
+              :debounce="300"
+              :trigger-on-focus="false"
               clearable
               :disabled="lockBiz"
-              :placeholder="form.biz_type === 'product' ? '选择已有产品,或直接输入编号' : '选择已有工单,或直接输入编号'"
+              :placeholder="form.biz_type === 'product' ? '输入编号或名称搜索,也可直接手填' : '输入工单号或名称搜索,也可直接手填'"
               style="width:100%"
-              @change="onNoChange"
+              value-key="value"
+              @select="onBizPick"
+              @input="onBizInput"
+              @clear="onBizClear"
             >
-              <el-option
-                v-for="item in bizOptions"
-                :key="item.id"
-                :label="optionLabel(item)"
-                :value="optionValue(item)"
-              />
-            </el-select>
-            <p class="field-hint">{{ isExisting ? '已匹配到本厂记录,将带出保存工艺' : '未匹配记录,可直接填写信息并临时添加工艺后打印' }}</p>
+              <template #default="{ item }">
+                <div class="biz-suggest">
+                  <span class="biz-suggest-no">{{ item.value }}</span>
+                  <span class="biz-suggest-name">{{ item.subtitle }}</span>
+                </div>
+              </template>
+            </el-autocomplete>
+            <p v-if="bizSearching" class="field-hint">正在搜索…</p>
+            <p v-else class="field-hint">{{ isExisting ? '已匹配到本厂记录,将带出保存工艺' : '输入编号或名称搜索后点选;未匹配记录可手填并临时添加工艺' }}</p>
           </el-form-item>
 
           <el-form-item label="名称">
@@ -176,8 +180,8 @@ const syncing = ref(false)
 const emit = defineEmits(['update:modelValue', 'done'])
 
 const templates = ref([])
-const workorders = ref([])
-const products = ref([])
+const bizSearching = ref(false)
+const boundBizNo = ref('')
 const preparing = ref(false)
 const saving = ref(false)
 const preview = ref(null)
@@ -200,7 +204,6 @@ const form = reactive({
 const lockBiz = computed(() => !!props.bizId)
 const lockTemplate = computed(() => !!props.templateId)
 const isExisting = computed(() => Number(form.biz_id) > 0)
-const bizOptions = computed(() => (form.biz_type === 'product' ? products.value : workorders.value))
 const plateTotal = computed(() => sheetCount(form.qty, form.sheet_qty))
 const plateMax = computed(() => Math.max(1, plateTotal.value || 1))
 const sheetHint = computed(() => sheetCountText(form.qty, form.sheet_qty))
@@ -216,13 +219,6 @@ const selectedCover = computed(() => {
 })
 const selectedCells = computed(() => cellsOf(selectedTemplate.value))
 
-function optionValue(item) {
-  if (form.biz_type === 'workorder') {
-    return item.no || ''
-  }
-  return item.code || item.name || ''
-}
-
 function pickName(row, fallback = '') {
   if (!row) {
     return String(fallback || '').trim()
@@ -232,12 +228,49 @@ function pickName(row, fallback = '') {
   ).trim()
 }
 
-function optionLabel(item) {
+function bizSuggestValue(item) {
+  if (form.biz_type === 'workorder') {
+    return String(item.no || '').trim()
+  }
+  return String(item.code || item.name || '').trim()
+}
+
+function bizSuggestSubtitle(item) {
   if (form.biz_type === 'workorder') {
     const name = pickName(item)
-    return name ? (item.no + ' / ' + name) : (item.no || '')
+    const spec = String(item.spec || item.product?.spec || '').trim()
+    const parts = [name, spec].filter(Boolean)
+    return parts.join(' · ') || '—'
+  }
+  const spec = String(item.spec || '').trim()
+  return spec ? item.name + ' · ' + spec : (item.name || '—')
+}
+
+function toBizSuggest(item) {
+  return {
+    id: item.id,
+    value: bizSuggestValue(item),
+    subtitle: bizSuggestSubtitle(item),
+    raw: item,
+  }
+}
+
+async function searchBiz(queryString, cb) {
+  const kw = String(queryString || '').trim()
+  if (!kw || lockBiz.value) {
+    cb([])
+    return
+  }
+  bizSearching.value = true
+  try {
+    const api = form.biz_type === 'product' ? productList : workorderList
+    const data = await api({ keyword: kw, limit: 20 })
+    cb((data.rows || []).map(toBizSuggest).filter((item) => item.value))
+  } catch {
+    cb([])
+  } finally {
+    bizSearching.value = false
   }
-  return (item.code || '无编号') + ' / ' + (item.name || '')
 }
 
 function processLabel(row) {
@@ -291,17 +324,6 @@ function applySavedProcesses(list) {
   draftProcesses.value = []
 }
 
-function findRowByNo(no) {
-  const key = String(no || '').trim()
-  if (!key) {
-    return null
-  }
-  if (form.biz_type === 'workorder') {
-    return workorders.value.find((item) => String(item.no) === key) || null
-  }
-  return products.value.find((item) => String(item.code) === key || String(item.name) === key) || null
-}
-
 function syncPlateRange(prevTotal) {
   const n = plateTotal.value
   if (!(n > 0)) {
@@ -325,6 +347,19 @@ function checkAllProcesses() {
   checkedProcessKeys.value = savedProcesses.value.map((_, index) => String(index))
 }
 
+function clearBizBinding() {
+  form.biz_id = ''
+  boundBizNo.value = ''
+  savedProcesses.value = []
+  checkedProcessKeys.value = []
+  preview.value = null
+}
+
+function resetBizForm() {
+  clearBizBinding()
+  draftProcesses.value = []
+}
+
 function onBizTypeChange() {
   form.biz_id = ''
   form.no = ''
@@ -333,38 +368,50 @@ function onBizTypeChange() {
   form.sheet_qty = ''
   form.sheet_from = 1
   form.sheet_to = 1
-  savedProcesses.value = []
-  checkedProcessKeys.value = []
-  draftProcesses.value = []
-  preview.value = null
+  resetBizForm()
 }
 
-async function onNoChange(val) {
+function onBizInput() {
   if (syncing.value || lockBiz.value) {
     return
   }
-  preview.value = null
-  const key = String(val || '').trim()
-  if (!key) {
-    form.biz_id = ''
-    savedProcesses.value = []
-    checkedProcessKeys.value = []
+  const no = String(form.no || '').trim()
+  if (Number(form.biz_id) > 0 && no !== String(boundBizNo.value || '').trim()) {
+    clearBizBinding()
     return
   }
-  const row = findRowByNo(key)
-  if (row) {
-    await loadBizDetail(row.id)
+  if (!form.biz_id) {
+    preview.value = null
+  }
+}
+
+function onBizClear() {
+  if (lockBiz.value) {
     return
   }
-  form.biz_id = ''
-  savedProcesses.value = []
-  checkedProcessKeys.value = []
+  form.no = ''
+  form.name = ''
+  resetBizForm()
+}
+
+async function onBizPick(item) {
+  if (syncing.value || lockBiz.value || !item?.id) {
+    return
+  }
+  syncing.value = true
+  form.no = item.value || form.no
+  try {
+    await loadBizDetail(item.id)
+  } finally {
+    syncing.value = false
+  }
 }
 
 async function loadBizDetail(id) {
   if (!id) {
     return
   }
+  const wasSyncing = syncing.value
   syncing.value = true
   try {
     if (form.biz_type === 'workorder') {
@@ -383,9 +430,11 @@ async function loadBizDetail(id) {
       form.name = pickName(row, form.name)
       applySavedProcesses(row.processes || [])
     }
+    boundBizNo.value = String(form.no || '').trim()
+    draftProcesses.value = []
     syncPlateRange()
   } finally {
-    syncing.value = false
+    syncing.value = wasSyncing
   }
 }
 
@@ -493,22 +542,9 @@ watch(
     form.sheet_to = 1
     form.copies = 1
     try {
-      const [t, w, p] = await Promise.all([
-        templateList({ printable: 1, limit: 100 }),
-        workorderList({ limit: 100 }),
-        productList({ limit: 100 }),
-      ])
+      const t = await templateList({ printable: 1, limit: 100 })
       templates.value = t.rows || []
-      workorders.value = w.rows || []
-      products.value = p.rows || []
       if (form.biz_id) {
-        const listed = (form.biz_type === 'product' ? products.value : workorders.value).find(
-          (item) => Number(item.id) === Number(form.biz_id)
-        )
-        if (listed) {
-          form.no = form.no || (form.biz_type === 'product' ? listed.code : listed.no) || ''
-          form.name = form.name || pickName(listed)
-        }
         await loadBizDetail(form.biz_id)
       }
     } finally {
@@ -631,6 +667,26 @@ watch(plateTotal, (n, old) => {
   line-height: 1.5;
 }
 .empty-hint { margin: 6px 0 0; color: #c45656; font-size: 12px; }
+.biz-suggest {
+  display: flex;
+  align-items: baseline;
+  gap: 8px;
+  line-height: 1.4;
+  padding: 2px 0;
+}
+.biz-suggest-no {
+  flex-shrink: 0;
+  font-weight: 600;
+  color: #1d4f91;
+}
+.biz-suggest-name {
+  flex: 1;
+  min-width: 0;
+  color: #606266;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
 @media (max-width: 960px) {
   .print-layout { flex-direction: column; }
   .print-right { width: 100%; }