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