✨ feat:
修改二维码生成逻辑,改为一次性二维码,存储更多信息
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
<template>
|
||||
<el-select remote filterable v-model="_value" :remote-method="remoteSearchVendor" :loading="vendorLoading" placeholder="请选择供应商">
|
||||
<el-select
|
||||
remote
|
||||
clearable
|
||||
filterable
|
||||
v-model="_value"
|
||||
:remote-method="remoteSearchVendor"
|
||||
:loading="vendorLoading"
|
||||
placeholder="请选择供应商"
|
||||
>
|
||||
<el-option v-for="item in vendorList" :key="item.supplierId" :label="item.name" :value="item.supplierId" />
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
import QRCode from 'qrcode';
|
||||
|
||||
/**
|
||||
* 通用js方法封装处理
|
||||
@@ -231,3 +231,117 @@ export function tansParams(params) {
|
||||
export function blobValidate(data) {
|
||||
return data.type !== 'application/json'
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存二维码为图片
|
||||
* @param {string} code 二维码内容
|
||||
* @param {string} text 下方文字
|
||||
* @param {number} index 索引,用于生成文件名
|
||||
* @param {Object} context Vue组件实例上下文(需要包含$message、barcodeWidth、barcodeHeight)
|
||||
*/
|
||||
export async function saveAsImage(code, text, index, context) {
|
||||
// 确保上下文存在
|
||||
if (!context) {
|
||||
console.error('缺少组件上下文,请传入Vue实例');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 检查QRCode库是否加载
|
||||
if (typeof QRCode === 'undefined') {
|
||||
throw new Error('QRCode库未加载,请确保已引入');
|
||||
}
|
||||
|
||||
// 创建临时canvas用于绘制二维码和文字
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) {
|
||||
throw new Error('无法获取canvas上下文');
|
||||
}
|
||||
|
||||
// 从上下文获取尺寸参数,提供默认值
|
||||
const barcodeWidth = context.barcodeWidth || 200;
|
||||
const barcodeHeight = context.barcodeHeight || 200;
|
||||
const textHeight = 30; // 文字区域高度
|
||||
|
||||
canvas.width = barcodeWidth;
|
||||
canvas.height = barcodeHeight + textHeight;
|
||||
|
||||
// 绘制二维码
|
||||
const qrCanvas = document.createElement('canvas');
|
||||
qrCanvas.width = barcodeWidth;
|
||||
qrCanvas.height = barcodeHeight;
|
||||
|
||||
// 等待二维码绘制完成
|
||||
await new Promise((resolve, reject) => {
|
||||
QRCode.toCanvas(qrCanvas, code, {
|
||||
width: barcodeWidth,
|
||||
height: barcodeHeight,
|
||||
margin: 0,
|
||||
errorCorrectionLevel: 'M'
|
||||
}, (error) => {
|
||||
if (error) reject(error);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
|
||||
// 将二维码绘制到主canvas
|
||||
ctx.drawImage(qrCanvas, 0, 0);
|
||||
|
||||
// 绘制文字(处理文字过长情况)
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.font = '14px Arial, sans-serif';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'top';
|
||||
|
||||
// 处理过长文字,最多显示2行
|
||||
const maxTextWidth = barcodeWidth - 20; // 预留边距
|
||||
let displayText = text;
|
||||
if (ctx.measureText(text).width > maxTextWidth) {
|
||||
// 尝试截断并添加省略号
|
||||
let i = text.length;
|
||||
while (ctx.measureText(text.substring(0, i)).width > maxTextWidth && i > 0) {
|
||||
i--;
|
||||
}
|
||||
displayText = text.substring(0, i) + '...';
|
||||
}
|
||||
ctx.fillText(displayText, barcodeWidth / 2, barcodeHeight + 5);
|
||||
|
||||
// 创建图片链接并下载
|
||||
canvas.toBlob(blob => {
|
||||
if (!blob) {
|
||||
throw new Error('无法生成图片blob对象');
|
||||
}
|
||||
|
||||
try {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
|
||||
// 处理文件名特殊字符
|
||||
a.download = `二维码_${index + 1}.png`;
|
||||
a.href = url;
|
||||
|
||||
// 模拟点击下载
|
||||
document.body.appendChild(a);
|
||||
const event = new MouseEvent('click');
|
||||
a.dispatchEvent(event);
|
||||
|
||||
// 清理资源
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}, 100);
|
||||
|
||||
context.$message.success('图片保存成功');
|
||||
} catch (urlError) {
|
||||
console.error('创建下载链接失败:', urlError);
|
||||
context.$message.error('创建下载链接失败');
|
||||
}
|
||||
}, 'image/png'); // 明确指定MIME类型
|
||||
|
||||
} catch (error) {
|
||||
console.error('保存图片失败:', error);
|
||||
context.$message.error(`保存图片失败: ${error.message || '未知错误'}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,11 @@
|
||||
<div class="greeting-desc">愿你天黑有灯,下雨有伞</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 全部应用 -->
|
||||
<AllApplications />
|
||||
<!-- 数据概览区 -->
|
||||
<div class="data-overview">
|
||||
<!-- <div class="data-overview">
|
||||
<div v-for="(card, index) in dataCards" :key="index"
|
||||
class="data-card">
|
||||
<div class="data-card-header">
|
||||
@@ -25,10 +28,10 @@
|
||||
<div ref="charts" class="chart-inner"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- 业务功能区 -->
|
||||
<div class="business-modules">
|
||||
<!-- <div class="business-modules">
|
||||
<div v-for="(module, index) in businessModules" :key="index"
|
||||
class="business-module" @click="handleLink(module.link)">
|
||||
<div :class="['business-module-icon', getModuleBg(module.bgColor)]">
|
||||
@@ -36,10 +39,7 @@
|
||||
</div>
|
||||
<h3 class="business-module-title">{{ module.title }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 全部应用 -->
|
||||
<AllApplications />
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
295
klp-ui/src/views/wms/print/components/CodeRenderer.vue
Normal file
295
klp-ui/src/views/wms/print/components/CodeRenderer.vue
Normal file
@@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<div class="barcode-simple-layout">
|
||||
<!-- 仅保留预览区 -->
|
||||
<div class="barcode-preview-area">
|
||||
<div class="iframe-wrapper">
|
||||
<iframe ref="previewIframe" class="barcode-iframe" frameborder="0" :style="iframeStyle"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import QRCode from 'qrcode';
|
||||
export default {
|
||||
name: 'BarcodeRenderer',
|
||||
props: {
|
||||
barcodes: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
// 可通过props传入配置,保持一定灵活性
|
||||
layoutConfig: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
perRow: 3,
|
||||
barcodeWidth: 180,
|
||||
paperSize: 'A4',
|
||||
paperOrientation: 'portrait',
|
||||
customPaperWidth: 210,
|
||||
customPaperHeight: 297
|
||||
})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
barcodeConfigs: [], // 二维码配置数组 [{code, count, textTpl}]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 从传入的配置中获取参数
|
||||
perRow() {
|
||||
return this.layoutConfig.perRow || 3;
|
||||
},
|
||||
barcodeWidth() {
|
||||
return this.layoutConfig.barcodeWidth || 180;
|
||||
},
|
||||
barcodeHeight() {
|
||||
return this.layoutConfig.barcodeWidth || 180; // 保持宽高一致
|
||||
},
|
||||
paperSize() {
|
||||
return this.layoutConfig.paperSize || 'A4';
|
||||
},
|
||||
paperOrientation() {
|
||||
return this.layoutConfig.paperOrientation || 'portrait';
|
||||
},
|
||||
customPaperWidth() {
|
||||
return this.layoutConfig.customPaperWidth || 210;
|
||||
},
|
||||
customPaperHeight() {
|
||||
return this.layoutConfig.customPaperHeight || 297;
|
||||
},
|
||||
// 展开后的二维码列表(考虑生成数量)
|
||||
expandedBarcodes() {
|
||||
let arr = [];
|
||||
this.barcodeConfigs.forEach(cfg => {
|
||||
for (let i = 0; i < (cfg.count || 1); i++) {
|
||||
arr.push(cfg.code);
|
||||
}
|
||||
});
|
||||
return arr;
|
||||
},
|
||||
// 展开后的文字列表
|
||||
expandedBarcodeTexts() {
|
||||
let arr = [];
|
||||
this.barcodeConfigs.forEach(cfg => {
|
||||
for (let i = 0; i < (cfg.count || 1); i++) {
|
||||
let text = cfg.textTpl || cfg.code;
|
||||
text = text.replace(/\{\{n\}\}/g, i + 1); // 替换序号变量
|
||||
arr.push(text);
|
||||
}
|
||||
});
|
||||
return arr;
|
||||
},
|
||||
// 按行分组的二维码
|
||||
barcodeRows() {
|
||||
const rows = [];
|
||||
const arr = this.expandedBarcodes;
|
||||
for (let i = 0; i < arr.length; i += this.perRow) {
|
||||
rows.push(arr.slice(i, i + this.perRow));
|
||||
}
|
||||
return rows;
|
||||
},
|
||||
// 按行分组的文字
|
||||
barcodeTextRows() {
|
||||
const rows = [];
|
||||
const arr = this.expandedBarcodeTexts;
|
||||
for (let i = 0; i < arr.length; i += this.perRow) {
|
||||
rows.push(arr.slice(i, i + this.perRow));
|
||||
}
|
||||
return rows;
|
||||
},
|
||||
// iframe样式
|
||||
iframeStyle() {
|
||||
const { width, height } = this.getPaperPx();
|
||||
return {
|
||||
width: width + 'px',
|
||||
minHeight: height + 'px',
|
||||
height: height + 'px',
|
||||
background: '#fff',
|
||||
border: 'none',
|
||||
display: 'block',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
};
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 监听barcodes变化,重新初始化和渲染
|
||||
barcodes: {
|
||||
handler(newVal) {
|
||||
this.barcodeConfigs = newVal.map(b => ({
|
||||
code: b.code || b,
|
||||
count: b.count || 1,
|
||||
textTpl: b.textTpl || (b.code || b)
|
||||
}));
|
||||
this.$nextTick(this.renderPreviewIframe);
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
// 监听配置变化,重新渲染
|
||||
layoutConfig: {
|
||||
handler() {
|
||||
this.$nextTick(this.renderPreviewIframe);
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
// 监听二维码配置变化,重新渲染
|
||||
barcodeConfigs: {
|
||||
handler() {
|
||||
this.$nextTick(this.renderPreviewIframe);
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 生成二维码ID
|
||||
getBarcodeId(row, col) {
|
||||
return `barcode-canvas-${row}-${col}`;
|
||||
},
|
||||
// 获取二维码下方文字
|
||||
getBarcodeText(row, col, code) {
|
||||
if (
|
||||
this.barcodeTextRows[row] &&
|
||||
typeof this.barcodeTextRows[row][col] !== 'undefined' &&
|
||||
this.barcodeTextRows[row][col] !== null &&
|
||||
this.barcodeTextRows[row][col] !== ''
|
||||
) {
|
||||
return this.barcodeTextRows[row][col];
|
||||
}
|
||||
return code;
|
||||
},
|
||||
// 将纸张尺寸从mm转换为px(1mm ≈ 3.78px)
|
||||
getPaperPx() {
|
||||
let width, height;
|
||||
if (this.paperSize === 'A4') {
|
||||
width = 210 * 3.78;
|
||||
height = 297 * 3.78;
|
||||
} else if (this.paperSize === 'A5') {
|
||||
width = 148 * 3.78;
|
||||
height = 210 * 3.78;
|
||||
} else if (this.paperSize === 'A6') {
|
||||
width = 105 * 3.78;
|
||||
height = 148 * 3.78;
|
||||
} else {
|
||||
width = this.customPaperWidth * 3.78;
|
||||
height = this.customPaperHeight * 3.78;
|
||||
}
|
||||
// 处理横向/纵向
|
||||
if (this.paperOrientation === 'landscape') {
|
||||
return { width: height, height: width };
|
||||
}
|
||||
return { width, height };
|
||||
},
|
||||
// 生成打印用的HTML
|
||||
getPrintHtml() {
|
||||
const { width, height } = this.getPaperPx();
|
||||
let html = `
|
||||
<html>
|
||||
<head>
|
||||
<title>二维码预览</title>
|
||||
<style>
|
||||
body { margin: 0; padding: 0; background: #fff; }
|
||||
.barcode-list { width: ${width}px; min-height: ${height}px; margin: 0 auto; background: #fff; padding: 20px; box-sizing: border-box; }
|
||||
.barcode-row { display: flex; margin-bottom: 24px; justify-content: space-around; }
|
||||
.barcode-item { flex: 1; text-align: center; padding: 0 10px; box-sizing: border-box; }
|
||||
.barcode-text { margin-top: 8px; font-size: 14px; word-break: break-all; }
|
||||
html, body { overflow-x: hidden; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="barcode-list">
|
||||
`;
|
||||
// 生成二维码行
|
||||
this.barcodeRows.forEach((row, rowIndex) => {
|
||||
html += '<div class="barcode-row">';
|
||||
row.forEach((code, colIndex) => {
|
||||
const id = this.getBarcodeId(rowIndex, colIndex);
|
||||
const text = this.getBarcodeText(rowIndex, colIndex, code);
|
||||
html += `<div class="barcode-item">
|
||||
<canvas id="${id}" width="${this.barcodeWidth}" height="${this.barcodeHeight}"></canvas>
|
||||
<div class="barcode-text">${text}</div>
|
||||
</div>`;
|
||||
});
|
||||
html += '</div>';
|
||||
});
|
||||
html += '</div></body></html>';
|
||||
return html;
|
||||
},
|
||||
// 渲染预览iframe
|
||||
async renderPreviewIframe() {
|
||||
const iframe = this.$refs.previewIframe;
|
||||
if (!iframe) return;
|
||||
|
||||
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
||||
doc.open();
|
||||
doc.write(this.getPrintHtml());
|
||||
doc.close();
|
||||
|
||||
// 绘制二维码
|
||||
setTimeout(async () => {
|
||||
for (let rowIndex = 0; rowIndex < this.barcodeRows.length; rowIndex++) {
|
||||
const row = this.barcodeRows[rowIndex];
|
||||
for (let colIndex = 0; colIndex < row.length; colIndex++) {
|
||||
const code = row[colIndex];
|
||||
const id = this.getBarcodeId(rowIndex, colIndex);
|
||||
const el = doc.getElementById(id);
|
||||
if (el) {
|
||||
await QRCode.toCanvas(el, code, {
|
||||
width: this.barcodeWidth,
|
||||
height: this.barcodeHeight,
|
||||
margin: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 初始化二维码配置
|
||||
this.barcodeConfigs = this.barcodes.map(b => ({
|
||||
code: b.code || b,
|
||||
count: b.count || 1,
|
||||
textTpl: b.textTpl || (b.code || b)
|
||||
}));
|
||||
this.renderPreviewIframe();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.barcode-simple-layout {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.barcode-preview-area {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.iframe-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
background: #f8f8f8;
|
||||
overflow: auto;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.barcode-iframe {
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
||||
70
klp-ui/src/views/wms/print/index-simple.vue
Normal file
70
klp-ui/src/views/wms/print/index-simple.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div style="position: relative;">
|
||||
<!-- 录入二维码列表 -->
|
||||
<el-form inline style="position: relative;">
|
||||
<el-form-item label="物料类型">
|
||||
<el-select v-model="itemType" placeholder="请选择物料类型" clearable>
|
||||
<el-option v-for="dict in dict.type.stock_item_type" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="物料信息">
|
||||
<ProductSelect can-add v-if="itemType === 'product'" v-model="itemId" placeholder="请选择产品"
|
||||
@change="onItemChange" />
|
||||
<SemiSelect can-add v-else-if="itemType === 'semi'" v-model="itemId" placeholder="请选择半成品"
|
||||
@change="onItemChange" />
|
||||
<RawMaterialSelect can-add v-else-if="itemType === 'raw_material'" v-model="itemId" placeholder="请选择原材料"
|
||||
@change="onItemChange" />
|
||||
<el-input v-else disabled v-model="itemId" placeholder="请先选择物料类型" :disabled="true" style="width: 100%;" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div class="print-container" v-loading="loading">
|
||||
<BarCode :barcodes="drawerBarcodeData" @delete="handleDelete" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BarCode from '../stockIo/panels/barcode.vue';
|
||||
import ProductSelect from '@/components/KLPService/ProductSelect/index.vue';
|
||||
import SemiSelect from '@/components/KLPService/SemiSelect/index.vue';
|
||||
import RawMaterialSelect from '@/components/KLPService/RawMaterialSelect/index.vue';
|
||||
|
||||
export default {
|
||||
name: 'Print',
|
||||
components: { BarCode, ProductSelect, SemiSelect, RawMaterialSelect },
|
||||
dicts: ['stock_item_type'],
|
||||
data() {
|
||||
return {
|
||||
drawerBarcodeData: [], // 条码数据
|
||||
loading: false, // 加载状态
|
||||
itemId: undefined, // 物料ID
|
||||
itemType: undefined, // 物料类型
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onItemChange(item) {
|
||||
// 选中后构造条码数据并插入
|
||||
console.log(item);
|
||||
const itemType = this.itemType;
|
||||
const name = (itemType == 'semi' || itemType == 'product') ? item.productName : item.rawMaterialName;
|
||||
const code = (itemType == 'semi' || itemType == 'product') ? item.productCode : item.rawMaterialCode;
|
||||
const itemId = (itemType == 'semi' || itemType == 'product') ? item.productId : item.rawMaterialId;
|
||||
const o = {
|
||||
code: encodeURIComponent(`${itemType}__${itemId || ''}`),
|
||||
count: 1,
|
||||
textTpl: `${name}[${code}]`
|
||||
}
|
||||
this.drawerBarcodeData.push(o);
|
||||
},
|
||||
handleDelete(cfg, idx) {
|
||||
console.log(cfg, idx);
|
||||
this.drawerBarcodeData.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
@@ -1,48 +1,147 @@
|
||||
<template>
|
||||
<div style="position: relative;">
|
||||
<!-- 录入二维码列表 -->
|
||||
<el-form inline style="position: relative;">
|
||||
<el-form-item label="物料类型">
|
||||
<el-select v-model="itemType" placeholder="请选择物料类型" clearable>
|
||||
<el-option v-for="dict in dict.type.stock_item_type" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="物料信息">
|
||||
<ProductSelect can-add v-if="itemType === 'product'" v-model="itemId" placeholder="请选择产品"
|
||||
@change="onItemChange" />
|
||||
<SemiSelect can-add v-else-if="itemType === 'semi'" v-model="itemId" placeholder="请选择半成品"
|
||||
@change="onItemChange" />
|
||||
<RawMaterialSelect can-add v-else-if="itemType === 'raw_material'" v-model="itemId" placeholder="请选择原材料"
|
||||
@change="onItemChange" />
|
||||
<el-input v-else disabled v-model="itemId" placeholder="请先选择物料类型" :disabled="true" style="width: 100%;" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<div class="left-container">
|
||||
<el-button type="primary" @click="handleAdd">添加二维码</el-button>
|
||||
<el-form label-width="80px" size="small" label-position="top">
|
||||
<div v-for="(cfg, idx) in drawerBarcodeData" :key="idx"
|
||||
style="margin-bottom: 16px; border: 1px solid #eee; border-radius: 4px; padding: 12px 16px; background: #fafbfc;">
|
||||
<div style="margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<span style="font-weight: bold; color: #666;">二维码 {{ idx + 1 }}</span>
|
||||
<el-button type="text" size="mini" @click="saveAsImage(idx)"
|
||||
icon="el-icon-download">
|
||||
另存为图片
|
||||
</el-button>
|
||||
<el-button type="text" size="mini" @click="handleDelete(cfg, idx)" icon="el-icon-delete">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="操作类型" style="margin-bottom: 8px;">
|
||||
<el-select v-model="cfg.ioType" placeholder="请选择操作类型">
|
||||
<el-option label="入库" value="in" />
|
||||
<el-option label="出库" value="out" />
|
||||
<el-option label="移库" value="transfer" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="单据" style="margin-bottom: 8px;">
|
||||
<el-select clearable filterable size="mini" v-model="cfg.stockIoId" placeholder="请选择挂载单据"
|
||||
class="form-input">
|
||||
<el-option v-for="item in masterList.filter(i => i.ioType === cfg.ioType)" :key="item.stockIoId"
|
||||
:label="item.stockIoCode"
|
||||
:value="item.stockIoId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="目标仓库" style="margin-bottom: 8px;">
|
||||
<WarehouseSelect v-model="cfg.warehouseId" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="源仓库" v-if="cfg.ioType === 'transfer'" style="margin-bottom: 8px;">
|
||||
<WarehouseSelect v-model="cfg.fromWarehouseId" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div class="print-container" v-loading="loading">
|
||||
<BarCode :barcodes="drawerBarcodeData" @delete="handleDelete" />
|
||||
</div>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="物料类型" style="margin-bottom: 8px;">
|
||||
<el-select v-model="cfg.itemType" placeholder="请选择物料类型">
|
||||
<el-option v-for="dict in dict.type.stock_item_type" :key="dict.value" :label="dict.label"
|
||||
:value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="物料信息" style="margin-bottom: 8px;">
|
||||
<ProductSelect v-if="cfg.itemType === 'product'" v-model="cfg.itemId" placeholder="请选择产品" />
|
||||
<SemiSelect v-else-if="cfg.itemType === 'semi'" v-model="cfg.itemId" placeholder="请选择半成品" />
|
||||
<RawMaterialSelect v-else-if="cfg.itemType === 'raw_material'" v-model="cfg.itemId"
|
||||
placeholder="请选择原材料" />
|
||||
<el-input v-else disabled v-model="cfg.itemId" placeholder="请先选择物料类型" :disabled="true"
|
||||
style="width: 100%;" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="数量" style="margin-bottom: 8px;">
|
||||
<el-input-number v-model="cfg.count" :min="1" :max="100" size="mini" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="下方文字" style="margin-bottom: 8px;">
|
||||
<el-input v-model="cfg.text" size="mini" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<div class="right-container">
|
||||
<div v-loading="loading">
|
||||
<BarCode :barcodes="barCodeConfigs" />
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BarCode from '../stockIo/panels/barcode.vue';
|
||||
import BarCode from './components/CodeRenderer.vue';
|
||||
import ProductSelect from '@/components/KLPService/ProductSelect/index.vue';
|
||||
import SemiSelect from '@/components/KLPService/SemiSelect/index.vue';
|
||||
import RawMaterialSelect from '@/components/KLPService/RawMaterialSelect/index.vue';
|
||||
import WarehouseSelect from '@/components/WarehouseSelect/index.vue';
|
||||
import { saveAsImage } from '@/utils/klp';
|
||||
|
||||
import { listStockIo } from '@/api/wms/stockIo';
|
||||
|
||||
export default {
|
||||
name: 'Print',
|
||||
components: { BarCode, ProductSelect, SemiSelect, RawMaterialSelect },
|
||||
dicts: ['stock_item_type'],
|
||||
components: { BarCode, ProductSelect, SemiSelect, RawMaterialSelect, WarehouseSelect },
|
||||
dicts: ['stock_item_type', 'stock_io_type'],
|
||||
data() {
|
||||
return {
|
||||
drawerBarcodeData: [], // 条码数据
|
||||
loading: false, // 加载状态
|
||||
itemId: undefined, // 物料ID
|
||||
itemType: undefined, // 物料类型
|
||||
masterList: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.fetchMaster();
|
||||
},
|
||||
computed: {
|
||||
barCodeConfigs() {
|
||||
return this.drawerBarcodeData.map(b => ({
|
||||
code: JSON.stringify({
|
||||
ioType: b.ioType,
|
||||
stockIoId: b.stockIoId,
|
||||
fromWarehouseId: b.fromWarehouseId,
|
||||
warehouseId: b.warehouseId,
|
||||
itemType: b.itemType,
|
||||
itemId: b.itemId,
|
||||
batchNo: b.batchNo
|
||||
}),
|
||||
count: b.count || 1,
|
||||
textTpl: b.text || ''
|
||||
}));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onItemChange(item) {
|
||||
// 选中后构造条码数据并插入
|
||||
@@ -50,20 +149,97 @@ export default {
|
||||
const itemType = this.itemType;
|
||||
const name = (itemType == 'semi' || itemType == 'product') ? item.productName : item.rawMaterialName;
|
||||
const code = (itemType == 'semi' || itemType == 'product') ? item.productCode : item.rawMaterialCode;
|
||||
const itemId = (itemType == 'semi' || itemType == 'product') ? item.productId : item.rawMaterialId;
|
||||
const o = {
|
||||
code: encodeURIComponent(`${itemType}__${item.itemId || ''}`),
|
||||
code: encodeURIComponent(`${itemType}__${itemId || ''}`),
|
||||
count: 1,
|
||||
textTpl: `${name}[${code}]`
|
||||
}
|
||||
this.drawerBarcodeData.push(o);
|
||||
},
|
||||
fetchMaster() {
|
||||
listStockIo({ pageSize: 9999, pageNum: 1 }).then(res => {
|
||||
console.log("获取挂载单据", res);
|
||||
this.masterList = res.rows || [];
|
||||
}).catch(error => {
|
||||
console.error("获取挂载单据失败", error);
|
||||
this.$message.error("获取挂载单据失败");
|
||||
});
|
||||
},
|
||||
handleDelete(cfg, idx) {
|
||||
console.log(cfg, idx);
|
||||
this.drawerBarcodeData.splice(idx, 1);
|
||||
},
|
||||
handleAdd() {
|
||||
const o = {
|
||||
ioType: undefined,
|
||||
stockIoId: undefined,
|
||||
|
||||
fromWarehouseId: undefined,
|
||||
warehouseId: undefined,
|
||||
|
||||
itemType: undefined,
|
||||
itemId: undefined,
|
||||
batchNo: 'auto',
|
||||
count: 0,
|
||||
|
||||
text: '默认文字',
|
||||
}
|
||||
|
||||
this.drawerBarcodeData.push(o);
|
||||
},
|
||||
// 补充saveAsImage方法的空实现,避免控制台报错
|
||||
saveAsImage(index) {
|
||||
saveAsImage(this.barCodeConfigs[index].code, this.barCodeConfigs[index].textTpl, index, this);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
// 左侧容器样式
|
||||
.left-container {
|
||||
height: calc(100vh - 84px); // 高度为视口高度减去顶部间距
|
||||
overflow-y: auto; // 纵向滚动
|
||||
padding-right: 10px; // 避免内容与滚动条重叠
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// 右侧容器样式
|
||||
.right-container {
|
||||
height: calc(100vh - 84px); // 与左侧保持一致的高度
|
||||
overflow-y: auto;
|
||||
padding-left: 10px; // 与左侧间距区分
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// 美化滚动条
|
||||
::-webkit-scrollbar {
|
||||
width: 6px; // 滚动条宽度
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #ddd;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
// 解决scoped样式无法穿透到滚动条的问题
|
||||
::v-deep ::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::v-deep ::-webkit-scrollbar-thumb {
|
||||
background: #ddd;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::v-deep ::-webkit-scrollbar-track {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
</li>
|
||||
</ul>
|
||||
</el-col>
|
||||
<el-col :span="20" class="main-panel">
|
||||
<!-- <el-col :span="20" class="main-panel">
|
||||
<div class="form-panel">
|
||||
<el-form inline>
|
||||
<el-form-item label="库位" class="form-item">
|
||||
<el-form-item label="目标库位" class="form-item">
|
||||
<WarehouseSelect size="mini" style="width: 200px;" v-model="defaultForm.warehouseId" />
|
||||
</el-form-item>
|
||||
<el-form-item label="挂载单据" class="form-item">
|
||||
<el-select size="mini" v-model="defaultForm.stockIoId" placeholder="请选择挂载单据" clearable class="form-input">
|
||||
<el-select size="mini" filterable v-model="defaultForm.stockIoId" placeholder="请选择挂载单据" clearable class="form-input">
|
||||
<el-option
|
||||
v-for="item in masterList"
|
||||
:key="item.stockIoId"
|
||||
@@ -52,9 +52,9 @@
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="table-panel">
|
||||
<el-table :data="messageList" style="width: 100%" height="100%" class="message-table" stripe @selection-change="handleSelectionChange">
|
||||
<el-table height="100%" :data="messageList" style="width: 100%" class="message-table" stripe @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column prop="time" label="时间" width="60" align="center" />
|
||||
<el-table-column prop="time" label="时间" width="150" align="center" />
|
||||
<el-table-column prop="itemId" label="物料" align="center">
|
||||
<template #default="scope">
|
||||
<ProductInfo v-if="scope.row.itemType == 'product' || scope.row.itemType == 'semi'" :productId="scope.row.itemId" />
|
||||
@@ -68,7 +68,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="stockIoId" label="挂载单据" align="center">
|
||||
<template #default="scope">
|
||||
<el-select v-model="scope.row.stockIoId" placeholder="请选择挂载单据" clearable class="table-select">
|
||||
<el-select v-model="scope.row.stockIoId" filterable placeholder="请选择挂载单据" clearable class="table-select">
|
||||
<el-option
|
||||
v-for="item in masterList"
|
||||
:key="item.stockIoId"
|
||||
@@ -100,15 +100,15 @@
|
||||
<el-input v-model="scope.row.batchNo" class="table-input" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="120" fixed="right">
|
||||
<el-table-column label="操作" align="center" width="140" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" size="mini" @click="handleDelete(scope.row)">删除</el-button>
|
||||
<el-button type="primary" size="mini" @click="handleConfirm(scope.row)">确认</el-button>
|
||||
<el-button size="mini" type="text" @click="handleDelete(scope.row)">删除</el-button>
|
||||
<el-button size="mini" type="text" @click="handleConfirm(scope.row)">确认</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-col> -->
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
@@ -441,6 +441,7 @@ export default {
|
||||
|
||||
.table-panel {
|
||||
flex: 1;
|
||||
height: calc(100vh - 220px);
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
<el-input v-model="queryParams.owner" placeholder="请输入负责人" />
|
||||
</el-form-item>
|
||||
<!-- 远程搜索供应商 -->
|
||||
<el-form-item label="供应商" prop="supplierId">
|
||||
<!-- <el-form-item label="供应商" prop="supplierId">
|
||||
<VendorSelect v-model="queryParams.supplierId" />
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="关联订单ID" prop="orderId">
|
||||
<el-input v-model="queryParams.orderId" placeholder="请输入关联订单ID" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
|
||||
Reference in New Issue
Block a user