Files
klp-oa/klp-ui/src/views/wms/delivery/components/wayBill.vue
砂糖 2d30a2f3fb feat(发货单): 增加发货单钢卷选择范围限制功能
- 在CoilSelector组件中新增rangeMode和rangeData属性,支持传入可选钢卷列表
- 发货单明细表增加对可选钢卷列表的支持,并在增删改时刷新列表
- 发货单打印时自动填充取货地点为实际库位前三位
- 调整操作列固定显示在表格右侧
2025-12-09 14:37:46 +08:00

651 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 发货单组件 -->
<template>
<div>
<div class="waybill-container" ref="waybillRef">
<!-- 头部信息 -->
<div class="waybill-header">
<div class="header-left">
<span class="label">收货单位</span>
<input type="text" class="editable-input transparent-input" v-model="localWaybill.consigneeUnit" />
</div>
<div class="header-center">
<input type="text" class="editable-input date-input transparent-input" v-model="localWaybill.deliveryYear" />
<span class="label date-label"></span>
<input type="text" class="editable-input date-input transparent-input" v-model="localWaybill.deliveryMonth" />
<span class="label date-label"></span>
<input type="text" class="editable-input date-input transparent-input" v-model="localWaybill.deliveryDay" />
<span class="label date-label"></span>
</div>
<div class="header-right">
<span class="label">发货单位</span>
<input type="text" class="editable-input transparent-input" v-model="localWaybill.senderUnit" />
</div>
</div>
<div class="waybill-header">
<div class="header-left">
<span class="label">负责人</span>
<input type="text" class="editable-input transparent-input" v-model="localWaybill.principal" />
</div>
<div class="header-right">
<span class="label">电话</span>
<input type="text" class="editable-input transparent-input" v-model="localWaybill.principalPhone" />
</div>
<div class="header-center">
<span class="label">车牌</span>
<input type="text" class="editable-input transparent-input" v-model="localWaybill.licensePlate" />
</div>
</div>
<!-- 表格 -->
<table class="waybill-table">
<thead>
<tr>
<th>品名</th>
<th>切边</th>
<th>包装</th>
<th>结算</th>
<th>原料厂家</th>
<th>卷号</th>
<th>规格</th>
<th>材质</th>
<th>数量</th>
<th>重量kg</th>
<th>单价</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<!-- 无明细提示 -->
<tr v-if="localWaybillDetails.length === 0">
<td colspan="12" class="no-data">
<div class="no-data-content">
<el-empty description="暂无发货单明细" />
</div>
</td>
</tr>
<!-- 明细数据 -->
<tr v-for="(item, index) in localWaybillDetails" :key="index">
<td><input type="text" class="transparent-input" v-model="item.productName" /></td>
<td><input type="text" class="table-input transparent-input" v-model="item.edgeType" />
</td>
<td><input type="text" class="table-input transparent-input" v-model="item.packageType" />
</td>
<td><input type="text" class="table-input transparent-input" v-model="item.settlementType" />
</td>
<td><input type="text" class="table-input transparent-input" v-model="item.rawMaterialFactory" />
</td>
<td><input type="text" class="table-input transparent-input" v-model="item.coilNumber" /></td>
<td><input type="text" class="table-input transparent-input" v-model="item.specification" /></td>
<td><input type="text" class="table-input transparent-input" v-model="item.material" />
</td>
<td><input type="number" class="table-input transparent-input" v-model.number="item.quantity"
placeholder="0" /></td>
<td><input type="number" class="table-input transparent-input" v-model.number="item.weight"
placeholder="0.00" /></td>
<td><input type="number" class="table-input transparent-input" v-model.number="item.unitPrice"
placeholder="0.00" /></td>
<td>
<div class="table-cell-with-action">
<input type="text" class="table-input transparent-input" v-model="item.remark" />
</div>
</td>
</tr>
</tbody>
</table>
<!-- 备注说明 -->
<div class="waybill-remarks">
<p>1品名冷硬钢卷酸连轧冷轧钢卷脱脂退火火拉矫镀锌卷板镀锌管料镀锌分剪料2切边净边/毛边3包装裸包周三径四简包1周三径四内外护角简包2周三径四+防锈纸普包周三径四+内外护角+防锈纸+端护板精包1周三径四+内外护角+防锈纸+薄膜+端护板+内外护板精包2周三径四+内外护角+防锈纸+薄膜+端护板+内外护板+木托</p>
</div>
<div class="waybill-pickup-location">
<!-- <div class="pickup-location-item inline"> -->
<span class="label">取货地点</span>
<input type="text" class="editable-input full-input transparent-input" v-model="localWaybill.pickupLocation" />
<!-- </div> -->
</div>
<!-- 签名栏 -->
<div class="waybill-footer">
<div class="footer-item inline">
<span class="label">销售</span>
<input type="text" class="editable-input signature-input transparent-input" />
</div>
<div class="footer-item inline">
<span class="label">发货</span>
<input type="text" class="editable-input signature-input transparent-input" />
</div>
<div class="footer-item inline">
<span class="label">磅房</span>
<input type="text" class="editable-input signature-input transparent-input" />
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="waybill-actions">
<el-button type="primary" @click="saveAsImage">保存为图片</el-button>
<el-button type="success" @click="printWaybill">打印</el-button>
</div>
</div>
</template>
<script>
import domtoimage from 'dom-to-image';
import printJS from 'print-js';
export default {
props: {
// 组件接收完整的发货单内容, 渲染发货单,这个面板内包括一个保存为图片(已经安装了dom-to-image)和一个打印按钮(已经安装了print-js)
waybill: {
type: Object,
default: () => { }
},
waybillDetails: {
type: Array,
default: () => []
}
},
data() {
return {
// 本地可编辑的发货单数据
localWaybill: {
consigneeUnit: '',
senderUnit: '',
deliveryYear: '',
deliveryMonth: '',
deliveryDay: '',
principal: '',
principalPhone: '',
licensePlate: '',
pickupLocation: ''
},
// 本地可编辑的发货单明细
localWaybillDetails: []
};
},
watch: {
// 监听props变化更新本地数据
waybill: {
handler(newVal) {
if (newVal) {
this.localWaybill = {
consigneeUnit: newVal.consigneeUnit || '',
senderUnit: newVal.senderUnit || '',
deliveryYear: this.getYearFromDate(newVal.deliveryTime) || '',
deliveryMonth: this.getMonthFromDate(newVal.deliveryTime) || '',
deliveryDay: this.getDayFromDate(newVal.deliveryTime) || '',
principal: newVal.principal || '',
principalPhone: newVal.principalPhone || '',
licensePlate: newVal.licensePlate || '',
pickupLocation: newVal.pickupLocation || ''
};
}
},
immediate: true,
deep: true
},
waybillDetails: {
handler(newVal) {
console.log('waybillDetails', newVal);
if (newVal && Array.isArray(newVal)) {
this.localWaybillDetails = [...newVal];
} else {
this.localWaybillDetails = [];
}
},
immediate: true,
deep: true
}
},
methods: {
// 从日期字符串中提取年份
getYearFromDate(dateStr) {
if (dateStr) {
const date = new Date(dateStr);
return date.getFullYear().toString();
}
return '';
},
// 从日期字符串中提取月份
getMonthFromDate(dateStr) {
if (dateStr) {
const date = new Date(dateStr);
return (date.getMonth() + 1).toString();
}
return '';
},
// 从日期字符串中提取日
getDayFromDate(dateStr) {
if (dateStr) {
const date = new Date(dateStr);
return date.getDate().toString();
}
return '';
},
// 保存为图片
saveAsImage() {
const node = this.$refs.waybillRef;
// 确保容器在保存图片时能完整显示所有内容
const originalWidth = node.style.width;
const originalOverflow = node.style.overflow;
// 临时调整容器样式,确保所有内容可见
node.style.width = 'auto';
node.style.overflow = 'visible';
// 获取实际内容宽度
const contentWidth = node.scrollWidth;
const contentHeight = node.scrollHeight;
domtoimage.toPng(node, {
width: contentWidth,
height: contentHeight,
style: {
width: `${contentWidth}px`,
height: `${contentHeight}px`,
overflow: 'visible'
}
})
.then(dataUrl => {
const link = document.createElement('a');
link.download = `发货单_${this.waybill.waybillName || this.waybill.waybillNo || Date.now()}.png`;
link.href = dataUrl;
link.click();
})
.catch(error => {
console.error('保存图片失败:', error);
this.$message.error('保存图片失败');
})
.finally(() => {
// 恢复原始样式
node.style.width = originalWidth;
node.style.overflow = originalOverflow;
});
},
// 打印发货单
printWaybill() {
const node = this.$refs.waybillRef;
// 确保容器在打印时能完整显示所有内容
const originalWidth = node.style.width;
const originalOverflow = node.style.overflow;
// 临时调整容器样式,确保所有内容可见
node.style.width = 'auto';
node.style.overflow = 'visible';
// 获取实际内容宽度
const contentWidth = node.scrollWidth;
printJS({
printable: node,
maxWidth: contentWidth,
type: 'html',
scanStyles: true,
style: `
@page { size: A4 landscape; margin: 1cm; }
.waybill-container { width: 100%; max-width: none; overflow: visible; }
.waybill-table { width: 100%; table-layout: auto; }
`,
targetStyles: ['*']
});
// 恢复原始样式
setTimeout(() => {
node.style.width = originalWidth;
node.style.overflow = originalOverflow;
}, 100);
}
}
}
</script>
<style scoped>
.waybill-container {
width: 850px;
max-width: none;
min-width: 850px;
margin: 0 auto;
padding: 20px;
background: white;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
font-family: SimSun, serif;
overflow-x: auto;
overflow-y: visible;
}
/* 头部样式 */
.waybill-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
font-size: 16px;
}
.header-left,
.header-right {
display: flex;
align-items: center;
}
.header-center {
display: flex;
align-items: center;
gap: 5px;
}
.label {
font-weight: bold;
display: inline-block;
width: 80px;
text-align: right;
white-space: nowrap;
}
.date-label {
width: 1em;
}
/* 可编辑输入框样式 */
.editable-input {
padding: 4px 8px;
border: 1px solid #dcdfe6;
border-radius: 4px;
font-size: 14px;
font-family: SimSun, serif;
outline: none;
transition: all 0.2s;
border-bottom: 1px dashed #dcdfe6;
}
.editable-input:focus {
border-color: #409eff;
}
.date-input {
width: 30px;
text-align: center;
margin-right: 5px;
}
/* 透明输入框样式 */
.transparent-input {
border: none;
border-radius: 0;
background-color: transparent;
}
.transparent-input:focus {
border-bottom-color: #409eff;
background-color: rgba(64, 158, 255, 0.05);
}
/* 表格样式 */
.waybill-table {
width: 100%;
max-width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
font-size: 12px;
table-layout: fixed;
}
.waybill-table th,
.waybill-table td {
border: 1px solid #000;
box-sizing: border-box;
line-height: 24px;
text-align: center;
vertical-align: middle;
word-wrap: break-word;
word-break: break-all;
}
/* 表格列宽设置 */
.waybill-table th:nth-child(1),
.waybill-table td:nth-child(1) {
width: 80px; /* 品名 */
}
.waybill-table th:nth-child(2),
.waybill-table td:nth-child(2) {
width: 40px; /* 切边 */
}
.waybill-table th:nth-child(3),
.waybill-table td:nth-child(3) {
width: 40px; /* 包装 */
}
.waybill-table th:nth-child(4),
.waybill-table td:nth-child(4) {
width: 40px; /* 结算 */
}
.waybill-table th:nth-child(5),
.waybill-table td:nth-child(5) {
width: 80px; /* 原料厂家 */
}
.waybill-table th:nth-child(6),
.waybill-table td:nth-child(6) {
width: 100px; /* 卷号 */
}
.waybill-table th:nth-child(7),
.waybill-table td:nth-child(7) {
width: 80px; /* 规格 */
}
.waybill-table th:nth-child(8),
.waybill-table td:nth-child(8) {
width: 40px; /* 材质 */
}
.waybill-table th:nth-child(9),
.waybill-table td:nth-child(9) {
width: 60px; /* 数量(件) */
}
.waybill-table th:nth-child(10),
.waybill-table td:nth-child(10) {
width: 60px; /* 重量kg */
}
.waybill-table th:nth-child(11),
.waybill-table td:nth-child(11) {
width: 40px; /* 单价 */
}
.waybill-table th:nth-child(12),
.waybill-table td:nth-child(12) {
width: 100px; /* 备注 */
}
.waybill-table th {
background-color: #f5f7fa;
font-weight: bold;
}
.waybill-table tr:nth-child(even) {
background-color: #fafafa;
}
/* 表格输入框样式 */
.table-input {
box-sizing: border-box;
width: 100%;
height: 100%;
padding: 4px;
}
.table-input:focus {
border-color: #409eff;
}
/* 无数据样式 */
.no-data {
height: 200px;
vertical-align: middle;
}
.no-data-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
}
/* 表格单元格操作区 */
.table-cell-with-action {
display: flex;
align-items: center;
gap: 5px;
}
/* 备注样式 */
.waybill-remarks {
margin-bottom: 30px;
font-size: 14px;
line-height: 1.5;
text-align: justify;
}
.waybill-remarks p {
margin: 5px 0;
}
/* 底部签名样式 */
.waybill-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
font-size: 16px;
}
.waybill-pickup-location {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
font-size: 16px;
}
.waybill-pickup-location label {
font-size: 14px;
margin-right: 10px;
width: 40px;
}
.footer-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
width: 200px;
}
.footer-item.inline {
flex-direction: row;
align-items: center;
width: auto;
}
.footer-item .label {
font-size: 14px;
margin-right: 10px;
width: 40px;
}
.signature-input {
min-width: 150px;
}
.full-input {
/* 占满本行的剩余空间父容器不是flex */
flex: 1;
}
/* 操作按钮样式 */
.waybill-actions {
display: flex;
justify-content: center;
gap: 20px;
margin-top: 30px;
}
/* 添加明细按钮 */
.add-detail-btn {
display: flex;
justify-content: center;
margin-bottom: 20px;
}
/* 打印样式 */
@media print {
.waybill-container {
width: 100%;
max-width: none;
box-shadow: none;
padding: 0;
}
.waybill-header {
flex-wrap: nowrap;
justify-content: space-between;
}
.waybill-footer {
flex-wrap: nowrap;
justify-content: space-between;
}
.label {
width: 80px;
text-align: right;
white-space: nowrap;
}
.waybill-actions {
display: none;
}
}
/* 响应式设计 */
@media (max-width: 768px) {
.waybill-header {
flex-direction: column;
gap: 15px;
align-items: flex-start;
}
.waybill-table {
font-size: 12px;
}
.waybill-table th,
.waybill-table td {
padding: 2px;
}
.waybill-footer {
flex-direction: column;
align-items: flex-start;
gap: 15px;
}
.footer-item.inline {
width: 100%;
}
.label {
width: auto;
text-align: left;
}
}
</style>