From d9ff374c80f3ed9077eae5136a7edea37668afbf Mon Sep 17 00:00:00 2001
From: zhou zhou <zozhouo3o@gmail.com>
Date: 星期五, 17 四月 2026 09:59:08 +0800
Subject: [PATCH] #条码自定义增加图片

---
 rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-property-panel.vue |  228 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 226 insertions(+), 2 deletions(-)

diff --git a/rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-property-panel.vue b/rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-property-panel.vue
index cec4625..5a5e529 100644
--- a/rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-property-panel.vue
+++ b/rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-property-panel.vue
@@ -180,7 +180,7 @@
                 }}</span>
                 <ElInputNumber
                   :model-value="selectedElement.h"
-                  :min="0.4"
+                  :min="selectedElement.type === 'image' ? imageMinHeight : 0.4"
                   :step="0.2"
                   controls-position="right"
                   @update:model-value="updateElement('h', $event)"
@@ -341,6 +341,77 @@
                     @focus="setPlaceholderTarget('valueTemplate')"
                     @update:model-value="updateElement('valueTemplate', $event)"
                   />
+                </label>
+              </div>
+            </template>
+
+            <template v-else-if="selectedElement.type === 'image'">
+              <div class="matnr-print-property-panel__grid">
+                <label class="matnr-print-property-panel__span-2">
+                  <span>{{
+                    t('pages.basicInfo.whMat.printTemplate.propertyPanel.fields.imageSource')
+                  }}</span>
+                  <ElInput
+                    :model-value="selectedElement.src"
+                    @update:model-value="updateElement('src', $event)"
+                    @change="handleImageSourceChange"
+                  />
+                </label>
+                <label>
+                  <span>{{
+                    t('pages.basicInfo.whMat.printTemplate.propertyPanel.fields.fitMode')
+                  }}</span>
+                  <ElSelect
+                    :model-value="selectedElement.objectFit"
+                    @update:model-value="updateElement('objectFit', $event)"
+                  >
+                    <ElOption
+                      value="contain"
+                      :label="
+                        t('pages.basicInfo.whMat.printTemplate.propertyPanel.options.contain')
+                      "
+                    />
+                    <ElOption
+                      value="cover"
+                      :label="t('pages.basicInfo.whMat.printTemplate.propertyPanel.options.cover')"
+                    />
+                    <ElOption
+                      value="fill"
+                      :label="t('pages.basicInfo.whMat.printTemplate.propertyPanel.options.fill')"
+                    />
+                  </ElSelect>
+                </label>
+                <label class="matnr-print-property-panel__upload">
+                  <span>{{
+                    t('pages.basicInfo.whMat.printTemplate.propertyPanel.actions.uploadImage')
+                  }}</span>
+                  <ElUpload
+                    accept="image/*"
+                    :auto-upload="false"
+                    :show-file-list="false"
+                    @change="handleImageFileChange"
+                  >
+                    <ElButton>
+                      {{
+                        t('pages.basicInfo.whMat.printTemplate.propertyPanel.actions.uploadImage')
+                      }}
+                    </ElButton>
+                  </ElUpload>
+                </label>
+                <label class="matnr-print-property-panel__span-2">
+                  <span>{{
+                    t('pages.basicInfo.whMat.printTemplate.propertyPanel.fields.previewImage')
+                  }}</span>
+                  <div class="matnr-print-property-panel__image-preview">
+                    <ElImage
+                      v-if="selectedElement.src"
+                      :src="selectedElement.src"
+                      fit="contain"
+                      preview-teleported
+                      class="matnr-print-property-panel__image-preview-inner"
+                    />
+                    <div v-else class="matnr-print-property-panel__image-preview-empty"></div>
+                  </div>
                 </label>
               </div>
             </template>
@@ -623,8 +694,13 @@
 
 <script setup>
   import { computed } from 'vue'
+  import { ElMessage } from 'element-plus'
   import { useI18n } from 'vue-i18n'
-  import { getFieldListTableRows, updateFieldListTableRows } from '../matnrPrintTemplate.helpers'
+  import {
+    getFieldListTableRows,
+    getImageMinHeight,
+    updateFieldListTableRows
+  } from '../matnrPrintTemplate.helpers'
 
   defineOptions({ name: 'MatnrPrintPropertyPanel' })
 
@@ -650,6 +726,11 @@
   ])
 
   const tableRows = computed(() => getFieldListTableRows(props.selectedElement))
+  const imageMinHeight = computed(() =>
+    props.selectedElement?.type === 'image'
+      ? getImageMinHeight(props.selectedElement?.w, props.selectedElement, props.selectedElement?.h)
+      : 0.4
+  )
 
   function updateTemplateMeta(field, value) {
     emit('update-template-meta', { field, value })
@@ -742,6 +823,116 @@
       id: props.selectedElement?.id,
       patch: updateFieldListTableRows(props.selectedElement, rows)
     })
+  }
+
+  function validateImageFile(rawFile) {
+    const isImageFile =
+      String(rawFile?.type || '').startsWith('image/') ||
+      /\.(png|jpe?g|gif|bmp|webp|svg)$/i.test(rawFile?.name || '')
+    if (!isImageFile) {
+      ElMessage.error(
+        t('pages.basicInfo.whMat.printTemplate.propertyPanel.messages.uploadOnlyImage')
+      )
+      return false
+    }
+
+    const isLt5MB = Number(rawFile?.size || 0) / 1024 / 1024 < 5
+    if (!isLt5MB) {
+      ElMessage.error(
+        t('pages.basicInfo.whMat.printTemplate.propertyPanel.messages.uploadSizeLimit')
+      )
+      return false
+    }
+
+    return true
+  }
+
+  function readFileAsDataUrl(file) {
+    return new Promise((resolve, reject) => {
+      const reader = new FileReader()
+      reader.onload = () => resolve(String(reader.result || ''))
+      reader.onerror = reject
+      reader.readAsDataURL(file)
+    })
+  }
+
+  function loadImageNaturalSize(src) {
+    return new Promise((resolve, reject) => {
+      const image = new Image()
+      image.onload = () => {
+        resolve({
+          naturalWidth: Number(image.naturalWidth) || 0,
+          naturalHeight: Number(image.naturalHeight) || 0
+        })
+      }
+      image.onerror = reject
+      image.src = src
+    })
+  }
+
+  async function applyImageSource(src) {
+    const nextSrc = String(src || '').trim()
+    if (!props.selectedElement?.id) {
+      return
+    }
+    if (!nextSrc) {
+      emit('update-element', {
+        id: props.selectedElement.id,
+        patch: {
+          src: '',
+          naturalWidth: 0,
+          naturalHeight: 0
+        }
+      })
+      return
+    }
+
+    const size = await loadImageNaturalSize(nextSrc)
+    const nextHeight = getImageMinHeight(props.selectedElement?.w, size, props.selectedElement?.h)
+    emit('update-element', {
+      id: props.selectedElement.id,
+      patch: {
+        src: nextSrc,
+        naturalWidth: size.naturalWidth,
+        naturalHeight: size.naturalHeight,
+        h: Math.max(Number(props.selectedElement?.h) || 0, nextHeight)
+      }
+    })
+  }
+
+  async function handleImageSourceChange(value) {
+    const nextSrc = String(value || '').trim()
+    if (!nextSrc) {
+      await applyImageSource('')
+      return
+    }
+    try {
+      await applyImageSource(nextSrc)
+    } catch (error) {
+      ElMessage.error(
+        error?.message ||
+          t('pages.basicInfo.whMat.printTemplate.propertyPanel.messages.readImageFailed')
+      )
+    }
+  }
+
+  async function handleImageFileChange(uploadFile) {
+    const rawFile = uploadFile?.raw
+    if (!rawFile || !props.selectedElement?.id) {
+      return
+    }
+    if (!validateImageFile(rawFile)) {
+      return
+    }
+    try {
+      const dataUrl = await readFileAsDataUrl(rawFile)
+      await applyImageSource(dataUrl)
+    } catch (error) {
+      ElMessage.error(
+        error?.message ||
+          t('pages.basicInfo.whMat.printTemplate.propertyPanel.messages.readImageFailed')
+      )
+    }
   }
 </script>
 
@@ -847,6 +1038,39 @@
     color: var(--art-text-primary);
   }
 
+  .matnr-print-property-panel__upload {
+    justify-content: flex-end;
+  }
+
+  .matnr-print-property-panel__image-preview {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    min-height: 120px;
+    padding: 12px;
+    border: 1px dashed rgba(148, 163, 184, 0.35);
+    border-radius: 12px;
+    background: rgba(248, 250, 252, 0.75);
+  }
+
+  .matnr-print-property-panel__image-preview-inner {
+    width: 100%;
+    height: 140px;
+    border-radius: 8px;
+    overflow: hidden;
+    background: #ffffff;
+  }
+
+  .matnr-print-property-panel__image-preview-empty {
+    width: 100%;
+    height: 96px;
+    border-radius: 10px;
+    border: 1px dashed rgba(148, 163, 184, 0.5);
+    background:
+      linear-gradient(135deg, rgba(226, 232, 240, 0.75), rgba(241, 245, 249, 0.35)),
+      linear-gradient(45deg, rgba(203, 213, 225, 0.35), transparent);
+  }
+
   @media (max-width: 1280px) {
     .matnr-print-property-panel__grid {
       grid-template-columns: minmax(0, 1fr);

--
Gitblit v1.9.1