From 029559f7cedded183bd1fbd09a5ebb576a9fa21d Mon Sep 17 00:00:00 2001
From: zhou zhou <zozhouo3o@gmail.com>
Date: 星期五, 17 四月 2026 12:34:23 +0800
Subject: [PATCH] #条码自定义

---
 rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-canvas.vue |  416 ++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 367 insertions(+), 49 deletions(-)

diff --git a/rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-canvas.vue b/rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-canvas.vue
index 378f365..932bab5 100644
--- a/rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-canvas.vue
+++ b/rsf-design/src/views/basic-info/wh-mat/modules/matnr-print-canvas.vue
@@ -17,7 +17,8 @@
         class="matnr-print-element"
         :class="{
           'is-selected': interactive && selectedElementId === element.id,
-          'is-hidden': element.visible === false
+          'is-hidden': element.visible === false,
+          'is-table': element.type === 'table'
         }"
         :style="getElementBoxStyle(element)"
         @mousedown.stop="handleElementMouseDown($event, element)"
@@ -69,44 +70,44 @@
           :style="getRectStyle(element)"
         ></div>
 
-        <table
+        <div
           v-else-if="element.type === 'table'"
           class="matnr-print-element__table"
           :style="getTableStyle(element)"
         >
-          <colgroup>
-            <col
-              v-for="(column, columnIndex) in element.columns"
-              :key="`${element.id}_col_${columnIndex}`"
-              :style="{ width: getUnitValue(column.width || 10) }"
-            />
-          </colgroup>
-          <tbody>
-            <tr
-              v-for="(row, rowIndex) in element.rows"
-              :key="`${element.id}_row_${rowIndex}`"
-              :style="{ height: getUnitValue(row.height || 6) }"
+          <div
+            v-for="cell in element.resolvedCells"
+            :key="`${element.id}_${cell.row}_${cell.col}`"
+            class="matnr-print-element__table-cell"
+            :style="getTableCellStyle(cell, element)"
+          >
+            <div
+              class="matnr-print-element__table-cell-content"
+              :style="getTableCellContentStyle()"
             >
-              <template
-                v-for="cell in getTableCellsForRow(element, rowIndex)"
-                :key="`${element.id}_${rowIndex}_${cell.col}`"
-              >
-                <td
-                  :colspan="cell.colspan || 1"
-                  :rowspan="cell.rowspan || 1"
-                  :style="getTableCellStyle(cell, element)"
-                >
-                  <div
-                    class="matnr-print-element__table-cell-content"
-                    :style="getTableCellContentStyle(cell, element)"
-                  >
-                    {{ cell.resolvedText }}
-                  </div>
-                </td>
-              </template>
-            </tr>
-          </tbody>
-        </table>
+              {{ cell.resolvedText }}
+            </div>
+          </div>
+        </div>
+
+        <template
+          v-if="interactive && selectedElementId === element.id && element.type === 'table'"
+        >
+          <span
+            v-for="handle in getTableColumnHandles(element)"
+            :key="`${element.id}_col_handle_${handle.index}`"
+            class="matnr-print-element__table-divider is-column"
+            :style="getTableDividerStyle(element, 'column', handle)"
+            @mousedown.stop="handleTableResizeMouseDown($event, element, 'column', handle.index)"
+          ></span>
+          <span
+            v-for="handle in getTableRowHandles(element)"
+            :key="`${element.id}_row_handle_${handle.index}`"
+            class="matnr-print-element__table-divider is-row"
+            :style="getTableDividerStyle(element, 'row', handle)"
+            @mousedown.stop="handleTableResizeMouseDown($event, element, 'row', handle.index)"
+          ></span>
+        </template>
 
         <template v-if="interactive && selectedElementId === element.id && element.type !== 'line'">
           <span
@@ -168,6 +169,8 @@
 
   const canvasRef = ref(null)
   const interactionState = ref(null)
+  const TABLE_MIN_COLUMN_WIDTH = 6
+  const TABLE_MIN_ROW_HEIGHT = 4
   const resizeHandles = [
     { direction: 'top-left' },
     { direction: 'top' },
@@ -259,8 +262,17 @@
     return {
       fontSize: editorMode.value ? mmToPx(style.fontSize, props.scale) : `${style.fontSize}mm`,
       color: style.color,
-      tableLayout: 'fixed'
+      position: 'relative'
     }
+  }
+
+  function getTableColumnSpanWidth(element, cell) {
+    const columns = Array.isArray(element?.columns) ? element.columns : []
+    const startCol = Number(cell?.col) || 0
+    const colspan = Math.max(1, Number(cell?.colspan) || 1)
+    return columns
+      .slice(startCol, startCol + colspan)
+      .reduce((total, column) => total + (Number(column?.width) || 6), 0)
   }
 
   function getTableCellSpanHeight(element, cell) {
@@ -274,23 +286,44 @@
 
   function getTableCellStyle(cell, element) {
     const style = cell.style || {}
+    const borderWidth = editorMode.value
+      ? mmToPx(style.borderWidth || 0.2, props.scale)
+      : `${style.borderWidth || 0.2}mm`
+    const borderColor = style.borderColor || '#111111'
+    const columns = Array.isArray(element?.columns) ? element.columns : []
+    const rows = Array.isArray(element?.rows) ? element.rows : []
+    const left = columns
+      .slice(0, Math.max(0, Number(cell?.col) || 0))
+      .reduce((total, column) => total + (Number(column?.width) || 6), 0)
+    const top = rows
+      .slice(0, Math.max(0, Number(cell?.row) || 0))
+      .reduce((total, row) => total + (Number(row?.height) || 6), 0)
+
     return {
-      border: `${editorMode.value ? mmToPx(style.borderWidth || 0.2, props.scale) : `${style.borderWidth || 0.2}mm`} solid ${style.borderColor || '#111111'}`,
+      position: 'absolute',
+      left: getUnitValue(left),
+      top: getUnitValue(top),
+      width: getUnitValue(getTableColumnSpanWidth(element, cell)),
+      height: getUnitValue(getTableCellSpanHeight(element, cell)),
+      borderTop: Number(cell?.row) === 0 ? `${borderWidth} solid ${borderColor}` : 'none',
+      borderLeft: Number(cell?.col) === 0 ? `${borderWidth} solid ${borderColor}` : 'none',
+      borderRight: `${borderWidth} solid ${borderColor}`,
+      borderBottom: `${borderWidth} solid ${borderColor}`,
       backgroundColor: style.backgroundColor || '#FFFFFF',
       textAlign: style.textAlign || 'left',
       fontWeight: style.fontWeight || 400,
-      height: getUnitValue(getTableCellSpanHeight(element, cell)),
+      boxSizing: 'border-box',
       padding: 0,
       overflow: 'hidden'
     }
   }
 
-  function getTableCellContentStyle(cell, element) {
+  function getTableCellContentStyle() {
     const paddingY = editorMode.value ? `${0.4 * props.scale}px` : '0.4mm'
     const paddingX = editorMode.value ? `${0.8 * props.scale}px` : '0.8mm'
     return {
       width: '100%',
-      height: getUnitValue(getTableCellSpanHeight(element, cell)),
+      height: '100%',
       boxSizing: 'border-box',
       padding: `${paddingY} ${paddingX}`,
       overflow: 'hidden',
@@ -301,10 +334,36 @@
     }
   }
 
-  function getTableCellsForRow(element, rowIndex) {
-    return (Array.isArray(element?.resolvedCells) ? element.resolvedCells : []).filter(
-      (cell) => Number(cell?.row) === rowIndex
-    )
+  function getTableColumnHandles(element) {
+    const columns = Array.isArray(element?.columns) ? element.columns : []
+    let offset = 0
+    return columns.slice(0, -1).map((column, index) => {
+      offset += Number(column?.width) || 0
+      return {
+        index,
+        offset
+      }
+    })
+  }
+
+  function getTableRowHandles(element) {
+    const rows = Array.isArray(element?.rows) ? element.rows : []
+    let offset = 0
+    return rows.slice(0, -1).map((row, index) => {
+      offset += Number(row?.height) || 0
+      return {
+        index,
+        offset
+      }
+    })
+  }
+
+  function getTableDividerStyle(element, axis, handle) {
+    const baseOffset = Number(handle?.offset) || 0
+
+    return axis === 'column'
+      ? { left: getUnitValue(baseOffset) }
+      : { top: getUnitValue(baseOffset) }
   }
 
   function getQrcodeSize(element) {
@@ -338,10 +397,44 @@
       return
     }
     emit('select-element', element.id)
-    startInteraction(event, element, 'resize', direction)
+    startInteraction(event, element, 'resize', direction, {
+      tableColumns:
+        element.type === 'table' && Array.isArray(element?.columns)
+          ? element.columns.map((column) => ({
+              width: Number(column?.width) || TABLE_MIN_COLUMN_WIDTH
+            }))
+          : [],
+      tableRows:
+        element.type === 'table' && Array.isArray(element?.rows)
+          ? element.rows.map((row) => ({
+              height: Number(row?.height) || TABLE_MIN_ROW_HEIGHT
+            }))
+          : [],
+      elementType: element?.type || ''
+    })
   }
 
-  function startInteraction(event, element, type, direction = '') {
+  function handleTableResizeMouseDown(event, element, axis, index) {
+    if (!props.interactive) {
+      return
+    }
+    emit('select-element', element.id)
+    startInteraction(event, element, `table-${axis}-resize`, '', {
+      index,
+      columns: Array.isArray(element?.columns)
+        ? element.columns.map((column) => ({
+            width: Number(column?.width) || TABLE_MIN_COLUMN_WIDTH
+          }))
+        : [],
+      rows: Array.isArray(element?.rows)
+        ? element.rows.map((row) => ({
+            height: Number(row?.height) || TABLE_MIN_ROW_HEIGHT
+          }))
+        : []
+    })
+  }
+
+  function startInteraction(event, element, type, direction = '', extra = {}) {
     interactionState.value = {
       type,
       direction,
@@ -353,7 +446,8 @@
         y: Number(element.y) || 0,
         w: Number(element.w) || 0,
         h: Number(element.h) || 0
-      }
+      },
+      ...extra
     }
     window.addEventListener('mousemove', handleWindowMouseMove)
     window.addEventListener('mouseup', stopInteraction)
@@ -374,7 +468,29 @@
         y: Math.max(0, origin.y + dy)
       }
     } else if (interactionState.value.type === 'resize') {
-      patch = buildResizePatch(origin, dx, dy, interactionState.value.direction)
+      patch =
+        interactionState.value.elementType === 'table'
+          ? buildTableElementResizePatch(
+              origin,
+              dx,
+              dy,
+              interactionState.value.direction,
+              interactionState.value.tableColumns,
+              interactionState.value.tableRows
+            )
+          : buildResizePatch(origin, dx, dy, interactionState.value.direction)
+    } else if (interactionState.value.type === 'table-column-resize') {
+      patch = buildTableColumnResizePatch(
+        interactionState.value.columns,
+        interactionState.value.index,
+        dx
+      )
+    } else if (interactionState.value.type === 'table-row-resize') {
+      patch = buildTableRowResizePatch(
+        interactionState.value.rows,
+        interactionState.value.index,
+        dy
+      )
     }
 
     emit('update-element', {
@@ -414,6 +530,149 @@
     }
 
     return next
+  }
+
+  function buildTableElementResizePatch(origin, dx, dy, direction, columns, rows) {
+    const basePatch = buildResizePatch(origin, dx, dy, direction)
+    const nextColumns = scaleTableTracks(columns, 'width', basePatch.w, TABLE_MIN_COLUMN_WIDTH)
+    const nextRows = scaleTableTracks(rows, 'height', basePatch.h, TABLE_MIN_ROW_HEIGHT)
+    const nextWidth = Number(
+      nextColumns.reduce((total, column) => total + (Number(column?.width) || 0), 0).toFixed(2)
+    )
+    const nextHeight = Number(
+      nextRows.reduce((total, row) => total + (Number(row?.height) || 0), 0).toFixed(2)
+    )
+
+    return {
+      ...basePatch,
+      x: direction.includes('left') ? Math.max(0, origin.x + origin.w - nextWidth) : basePatch.x,
+      y: direction.includes('top') ? Math.max(0, origin.y + origin.h - nextHeight) : basePatch.y,
+      w: nextWidth,
+      h: nextHeight,
+      columns: nextColumns,
+      rows: nextRows
+    }
+  }
+
+  function scaleTableTracks(tracks, key, targetTotal, minValue) {
+    const source = Array.isArray(tracks)
+      ? tracks.map((track) => ({
+          [key]: Number(track?.[key]) || minValue
+        }))
+      : []
+    if (!source.length) {
+      return []
+    }
+
+    const minimumTotal = minValue * source.length
+    const effectiveTarget = Math.max(minimumTotal, Number(targetTotal) || minimumTotal)
+    const sourceTotal = source.reduce((total, track) => total + (Number(track?.[key]) || 0), 0)
+
+    let nextTracks =
+      sourceTotal > 0
+        ? source.map((track) => ({
+            [key]: Math.max(
+              minValue,
+              Number((((Number(track?.[key]) || 0) / sourceTotal) * effectiveTarget).toFixed(2))
+            )
+          }))
+        : source.map(() => ({
+            [key]: Number((effectiveTarget / source.length).toFixed(2))
+          }))
+
+    let remainder = Number(
+      (
+        effectiveTarget -
+        nextTracks.reduce((total, track) => total + (Number(track?.[key]) || 0), 0)
+      ).toFixed(2)
+    )
+
+    if (Math.abs(remainder) < 0.01) {
+      return nextTracks
+    }
+
+    if (remainder > 0) {
+      nextTracks[nextTracks.length - 1] = {
+        ...nextTracks[nextTracks.length - 1],
+        [key]: Number((nextTracks[nextTracks.length - 1][key] + remainder).toFixed(2))
+      }
+      return nextTracks
+    }
+
+    let remaining = Math.abs(remainder)
+    for (let index = nextTracks.length - 1; index >= 0 && remaining > 0.009; index -= 1) {
+      const currentValue = Number(nextTracks[index]?.[key]) || minValue
+      const adjustable = Number(Math.max(0, currentValue - minValue).toFixed(2))
+      if (adjustable <= 0) {
+        continue
+      }
+      const deduction = Math.min(adjustable, remaining)
+      nextTracks[index] = {
+        ...nextTracks[index],
+        [key]: Number((currentValue - deduction).toFixed(2))
+      }
+      remaining = Number((remaining - deduction).toFixed(2))
+    }
+
+    return nextTracks
+  }
+
+  function buildTableColumnResizePatch(columns, index, delta) {
+    const nextColumns = Array.isArray(columns)
+      ? columns.map((column) => ({
+          width: Number(column?.width) || TABLE_MIN_COLUMN_WIDTH
+        }))
+      : []
+    if (!nextColumns[index] || !nextColumns[index + 1]) {
+      return {}
+    }
+
+    const leftWidth = nextColumns[index].width
+    const rightWidth = nextColumns[index + 1].width
+    const boundedDelta = Math.min(
+      Math.max(delta, TABLE_MIN_COLUMN_WIDTH - leftWidth),
+      rightWidth - TABLE_MIN_COLUMN_WIDTH
+    )
+
+    nextColumns[index] = {
+      ...nextColumns[index],
+      width: Number((leftWidth + boundedDelta).toFixed(2))
+    }
+    nextColumns[index + 1] = {
+      ...nextColumns[index + 1],
+      width: Number((rightWidth - boundedDelta).toFixed(2))
+    }
+
+    return {
+      columns: nextColumns,
+      w: Number(
+        nextColumns.reduce((total, column) => total + (Number(column?.width) || 0), 0).toFixed(2)
+      )
+    }
+  }
+
+  function buildTableRowResizePatch(rows, index, delta) {
+    const nextRows = Array.isArray(rows)
+      ? rows.map((row) => ({
+          height: Number(row?.height) || TABLE_MIN_ROW_HEIGHT
+        }))
+      : []
+    if (!nextRows[index]) {
+      return {}
+    }
+
+    const currentHeight = nextRows[index].height
+    const boundedDelta = Math.max(delta, TABLE_MIN_ROW_HEIGHT - currentHeight)
+
+    nextRows[index] = {
+      ...nextRows[index],
+      height: Number((currentHeight + boundedDelta).toFixed(2))
+    }
+
+    return {
+      rows: nextRows,
+      h: Number(nextRows.reduce((total, row) => total + (Number(row?.height) || 0), 0).toFixed(2))
+    }
   }
 
   function stopInteraction() {
@@ -466,6 +725,10 @@
     box-sizing: border-box;
     overflow: hidden;
     user-select: none;
+  }
+
+  .matnr-print-element.is-table {
+    overflow: visible;
   }
 
   .matnr-print-element.is-selected {
@@ -541,10 +804,11 @@
   }
 
   .matnr-print-element__table {
-    border-collapse: collapse;
+    position: relative;
+    overflow: hidden;
   }
 
-  .matnr-print-element__table td {
+  .matnr-print-element__table-cell {
     box-sizing: border-box;
     vertical-align: middle;
   }
@@ -553,6 +817,60 @@
     box-sizing: border-box;
   }
 
+  .matnr-print-element__table-divider {
+    position: absolute;
+    z-index: 2;
+    background: transparent;
+    color: rgba(37, 99, 235, 0.7);
+    transition:
+      color 0.15s ease,
+      opacity 0.15s ease;
+    opacity: 0.95;
+  }
+
+  .matnr-print-element__table-divider::before {
+    position: absolute;
+    content: '';
+    background: currentColor;
+    border-radius: 999px;
+    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.92);
+  }
+
+  .matnr-print-element__table-divider.is-column {
+    top: 0;
+    bottom: 0;
+    width: 12px;
+    transform: translateX(-6px);
+    cursor: ew-resize;
+  }
+
+  .matnr-print-element__table-divider.is-column::before {
+    top: calc(50% - 12px);
+    left: calc(50% - 2px);
+    width: 4px;
+    height: 24px;
+  }
+
+  .matnr-print-element__table-divider.is-row {
+    left: 0;
+    right: 0;
+    height: 12px;
+    transform: translateY(-6px);
+    cursor: ns-resize;
+  }
+
+  .matnr-print-element__table-divider.is-row::before {
+    top: calc(50% - 2px);
+    left: calc(50% - 12px);
+    width: 24px;
+    height: 4px;
+  }
+
+  .matnr-print-element__table-divider:hover {
+    color: rgba(37, 99, 235, 1);
+    opacity: 1;
+  }
+
   .matnr-print-element__handle {
     position: absolute;
     width: 10px;

--
Gitblit v1.9.1