This commit is contained in:
seb
2026-11-01 20:26:53 +01:00
parent 9c6d5d79e4
commit e7103ac340
2 changed files with 124 additions and 68 deletions

View File

@@ -12,20 +12,15 @@ import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import CancelScheduleSendOutlinedIcon from '@mui/icons-material/CancelScheduleSendOutlined';
import { enqueueSnackbar } from 'notistack';
import { labeltemplate, printer, article } from '../../../api';
import { labeltemplate, article } from '../../../api';
import { companyAutomat } from '../../../automats';
import { notifyError } from '../../../utils/notify';
import BackendError from '../../BackendError';
function filterZplPrinters(agentprinters, fallbackPrinters = []) {
const filteredAgent = (Array.isArray(agentprinters) ? agentprinters : []).filter(
function filterZplPrinters(agentprinters) {
return (Array.isArray(agentprinters) ? agentprinters : []).filter(
(p) => p.enabled !== false && (!p.type || p.type.toLowerCase() === 'zpl'),
);
if (filteredAgent.length > 0) return filteredAgent;
return (Array.isArray(fallbackPrinters) ? fallbackPrinters : []).filter(
(p) => !p.type || p.type.toLowerCase() === 'zpl',
);
}
function getPrinterLabel(p) {
@@ -52,6 +47,20 @@ function getPrinterLabel(p) {
return label;
}
function getPrinterLabelLength(printer, livePrinters = []) {
if (!printer) return null;
const live = (livePrinters || []).find(
(lp) =>
(lp.agentprinterId && lp.agentprinterId === printer.id) ||
(lp.id && lp.id === printer.id) ||
(printer.serial && lp.serial && String(printer.serial).toLowerCase() === String(lp.serial).toLowerCase()),
);
const raw = printer.labelLength ?? live?.labelLength;
if (raw == null) return null;
const num = Number(raw);
return Number.isFinite(num) && num > 0 ? num : null;
}
export default class ArticleLabelTab extends PureComponent {
constructor(props) {
super(props);
@@ -59,20 +68,23 @@ export default class ArticleLabelTab extends PureComponent {
this.state = {
templates: null,
printers: initialPrinters,
livePrinters: Array.isArray(companyAutomat.state.livePrinters)
? companyAutomat.state.livePrinters
: [],
selectedTemplateId: '',
selectedPrinterId: initialPrinters.length > 0 ? initialPrinters[0].id : '',
quantity: 1,
loading: true,
error: null,
};
this.legacyPrinters = [];
}
componentDidMount() {
this.unsub = companyAutomat.subscribe(this, (state) => {
const zplPrinters = filterZplPrinters(state.agentprinters, this.legacyPrinters);
const zplPrinters = filterZplPrinters(state.agentprinters);
this.setState((prev) => ({
printers: zplPrinters,
livePrinters: Array.isArray(state.livePrinters) ? state.livePrinters : [],
selectedPrinterId:
prev.selectedPrinterId && zplPrinters.some((p) => p.id === prev.selectedPrinterId)
? prev.selectedPrinterId
@@ -101,27 +113,16 @@ export default class ArticleLabelTab extends PureComponent {
this.setState({ loading: true, error: null });
const sku = this.props.article?.sku;
try {
const [resTemplates] = await Promise.all([
labeltemplate.list(sku ? { sku } : undefined),
companyAutomat.actions.loadAgentPrinters().catch(() => companyAutomat.state.agentprinters),
]);
const resTemplates = await labeltemplate.list(sku ? { sku } : undefined);
const templates = resTemplates.labeltemplates || [];
let zplPrinters = filterZplPrinters(companyAutomat.state.agentprinters);
if (zplPrinters.length === 0) {
try {
const resPrinters = await printer.list();
this.legacyPrinters = (resPrinters.printers || []).filter((p) => p.type === 'zpl');
zplPrinters = filterZplPrinters(companyAutomat.state.agentprinters, this.legacyPrinters);
} catch {
// ignore legacy fallback error
}
}
const zplPrinters = filterZplPrinters(companyAutomat.state.agentprinters);
this.setState((prev) => ({
templates,
printers: zplPrinters,
livePrinters: Array.isArray(companyAutomat.state.livePrinters)
? companyAutomat.state.livePrinters
: prev.livePrinters,
selectedTemplateId:
templates.length > 0
? prev.selectedTemplateId && templates.some((t) => t.id === prev.selectedTemplateId)
@@ -152,7 +153,6 @@ export default class ArticleLabelTab extends PureComponent {
await article.printLabel(articleData.sku, {
templateId: selectedTemplateId,
agentprinterId: selectedPrinterId,
printerId: selectedPrinterId,
quantity,
});
enqueueSnackbar('Druckauftrag erfolgreich gesendet', { variant: 'success' });
@@ -178,6 +178,7 @@ export default class ArticleLabelTab extends PureComponent {
const {
templates,
printers,
livePrinters,
selectedTemplateId,
selectedPrinterId,
quantity,
@@ -185,6 +186,9 @@ export default class ArticleLabelTab extends PureComponent {
error,
} = this.state;
const currentPrinter = (printers || []).find((p) => p.id === selectedPrinterId);
const printerLabelLength = getPrinterLabelLength(currentPrinter, livePrinters);
if (loading) {
return (
<Box sx={{ p: 4, display: 'flex', justifyContent: 'center' }}>
@@ -227,6 +231,16 @@ export default class ArticleLabelTab extends PureComponent {
<Grid container spacing={2}>
{templates.map((t) => {
const isSelected = t.id === selectedTemplateId;
const dpi = t.dpi === 300 ? 300 : 203;
const tmplHeight = t.height || 200;
const tmplWidth = t.width || 448;
const hasPrinterLength = printerLabelLength != null && printerLabelLength > 0;
const totalHeight = hasPrinterLength ? Math.max(tmplHeight, printerLabelLength) : tmplHeight;
const isCutoff = hasPrinterLength && printerLabelLength < tmplHeight;
const outlineColor = isCutoff ? '#d32f2f' : '#1976d2';
const tmplHeightPercent = (tmplHeight / totalHeight) * 100;
const printerHeightPercent = hasPrinterLength ? (printerLabelLength / totalHeight) * 100 : 100;
return (
<Grid size={{ xs: 12, sm: 6, md: 4 }} key={t.id}>
<Paper
@@ -272,18 +286,81 @@ export default class ArticleLabelTab extends PureComponent {
bgcolor: 'background.default',
borderRadius: 1,
overflow: 'hidden',
position: 'relative',
}}
>
{t.thumb ? (
<Box
sx={{
position: 'relative',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
maxWidth: '100%',
maxHeight: '100%',
aspectRatio: `${tmplWidth} / ${totalHeight}`,
boxSizing: 'border-box',
bgcolor: '#fff',
borderRadius: 0.5,
boxShadow: '0 1px 2px rgba(0,0,0,0.1)',
}}
>
{/* Template preview image */}
<Box
sx={{
width: '100%',
height: `${tmplHeightPercent}%`,
position: 'relative',
display: 'flex',
overflow: 'hidden',
borderRadius: 0.5,
}}
>
<img
src={`data:image/png;base64,${t.thumb}`}
alt={t.name}
style={{
maxWidth: '100%',
maxHeight: '100%',
width: '100%',
height: '100%',
objectFit: 'contain',
display: 'block',
}}
/>
{/* If template exceeds printer label length, shade the cutoff overflow */}
{isCutoff && (
<Box
sx={{
position: 'absolute',
left: 0,
right: 0,
top: `${(printerLabelLength / tmplHeight) * 100}%`,
bottom: 0,
bgcolor: 'rgba(211, 47, 47, 0.22)',
zIndex: 1,
pointerEvents: 'none',
}}
/>
)}
</Box>
{/* Dotted outline for printer label length */}
{hasPrinterLength && (
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
height: `${printerHeightPercent}%`,
border: `2px dotted ${outlineColor}`,
borderRadius: 0.5,
pointerEvents: 'none',
boxSizing: 'border-box',
zIndex: 2,
}}
/>
)}
</Box>
) : (
<Typography variant="caption" color="text.disabled">
Keine Vorschau
@@ -295,7 +372,7 @@ export default class ArticleLabelTab extends PureComponent {
{t.name}
</Typography>
<Typography variant="caption" color="text.secondary">
{t.width}x{t.height} ({t.dpi} dpi)
{t.width}x{t.height} ({dpi} dpi)
</Typography>
</Box>
</Paper>
@@ -314,11 +391,14 @@ export default class ArticleLabelTab extends PureComponent {
onChange={(e) => this.setState({ selectedPrinterId: e.target.value })}
sx={{ minWidth: 200 }}
>
{printers.map((p) => (
{printers.map((p) => {
const len = getPrinterLabelLength(p, livePrinters);
return (
<MenuItem key={p.id} value={p.id}>
{getPrinterLabel(p)}
{getPrinterLabel(p)}{len != null ? ` (${len} px)` : ''}
</MenuItem>
))}
);
})}
</TextInput>
<TextInput

View File

@@ -30,22 +30,17 @@ import { backendErrorFrom } from '../../utils/backendError';
import { notifyError } from '../../utils/notify';
import { setLastArticleSku } from '../../automats/navigationAutomat';
import companyAutomat from '../../automats/companyAutomat';
import { labeltemplate, printer } from '../../api';
import { labeltemplate } from '../../api';
import { enqueueSnackbar } from 'notistack';
import {
BLUE_BG,
SURFACE_BASE,
} from '../../colors';
function filterZplPrinters(agentprinters, fallbackPrinters = []) {
const filteredAgent = (Array.isArray(agentprinters) ? agentprinters : []).filter(
function filterZplPrinters(agentprinters) {
return (Array.isArray(agentprinters) ? agentprinters : []).filter(
(p) => p.enabled !== false && (!p.type || p.type.toLowerCase() === 'zpl'),
);
if (filteredAgent.length > 0) return filteredAgent;
return (Array.isArray(fallbackPrinters) ? fallbackPrinters : []).filter(
(p) => !p.type || p.type.toLowerCase() === 'zpl',
);
}
function getPrinterLabel(p) {
@@ -134,7 +129,6 @@ class ArtikelVorlagenPage extends PureComponent {
};
this._cancelled = false;
this._currentEditId = null;
this._legacyPrinters = [];
this.handleRetry = this.handleRetry.bind(this);
this.handleOpenNew = this.handleOpenNew.bind(this);
this.handleOpenEdit = this.handleOpenEdit.bind(this);
@@ -154,10 +148,8 @@ class ArtikelVorlagenPage extends PureComponent {
this._cancelled = false;
void this.loadTemplates();
this.unsubPrinters = companyAutomat.subscribe(this, (state) => ({
printers: filterZplPrinters(state.agentprinters, this._legacyPrinters),
printers: filterZplPrinters(state.agentprinters),
}));
void companyAutomat.actions.loadAgentPrinters().catch(() => {});
void this.loadFallbackPrinters();
}
componentWillUnmount() {
@@ -165,21 +157,6 @@ class ArtikelVorlagenPage extends PureComponent {
this.unsubPrinters?.();
}
async loadFallbackPrinters() {
try {
const resPrinters = await printer.list();
this._legacyPrinters = (resPrinters.printers || []).filter(
(p) => !p.type || p.type.toLowerCase() === 'zpl',
);
if (this._cancelled) return;
this.setState({
printers: filterZplPrinters(companyAutomat.state.agentprinters, this._legacyPrinters),
});
} catch {
// ignore
}
}
async loadTemplates() {
this.setState({ status: 'loading', error: null });
try {
@@ -358,8 +335,7 @@ class ArtikelVorlagenPage extends PureComponent {
this.setState({ printing: true });
try {
const payload = {
printerId: targetPrinter.id,
agentprinterId: targetPrinter.printeragentId || targetPrinter.id,
agentprinterId: targetPrinter.id,
templateId: editorTemplate?.id,
name: editorName,
width: editorWidth,