feat(发货单): 新增发货单打印功能及明细管理

refactor(组件): 重构发货单明细表格为独立组件
feat(组件): 添加发货单打印预览组件
fix(登录页): 移除默认用户名和密码
style(钢卷面板): 添加显示控制属性并优化布局
chore: 添加vitest测试配置和依赖
This commit is contained in:
砂糖
2025-11-28 11:02:19 +08:00
parent 5af31e83fa
commit 54fe3496fa
13 changed files with 779 additions and 92 deletions

View File

@@ -9,7 +9,8 @@
"build:prod": "vue-cli-service build",
"stage": "vue-cli-service serve --mode stage",
"preview": "node build/index.js --preview",
"lint": "eslint --ext .js,.vue src"
"lint": "eslint --ext .js,.vue src",
"test": "vitest"
},
"husky": {
"hooks": {
@@ -79,6 +80,7 @@
},
"devDependencies": {
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
"@vitest/browser": "^4.0.14",
"@vue/cli-plugin-babel": "4.4.6",
"@vue/cli-plugin-eslint": "4.4.6",
"@vue/cli-service": "4.4.6",
@@ -93,11 +95,13 @@
"eslint": "7.15.0",
"eslint-plugin-vue": "7.2.0",
"lint-staged": "10.5.3",
"playwright": "^1.57.0",
"runjs": "4.4.2",
"sass": "1.32.13",
"sass-loader": "10.1.1",
"script-ext-html-webpack-plugin": "2.1.5",
"svg-sprite-loader": "5.1.1",
"vitest": "^4.0.14",
"vue-template-compiler": "2.6.12"
},
"engines": {

View File

@@ -27,7 +27,16 @@
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="材质">
<el-input v-model="queryParams.material" placeholder="请输入材质" clearable @keyup.enter.native="handleQuery" />
<MemoInput storageKey="material" v-model="queryParams.material" placeholder="请输入材质" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="厂家">
<MemoInput storageKey="manufacturer" v-model="queryParams.manufacturer" placeholder="请输入厂家" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="表面处理">
<MemoInput storageKey="surfaceTreatmentDesc" v-model="queryParams.surfaceTreatmentDesc" placeholder="请输入表面处理" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="锌层">
<MemoInput storageKey="zincLayer" v-model="queryParams.zincLayer" placeholder="请输入锌层" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
@@ -78,9 +87,13 @@
<script>
import { listProduct } from '@/api/wms/product';
import MemoInput from '@/components/MemoInput/index.vue';
export default {
name: 'ProductSelector',
components: {
MemoInput
},
props: {
value: {
type: String,

View File

@@ -27,7 +27,16 @@
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="材质">
<el-input v-model="queryParams.material" placeholder="请输入材质" clearable @keyup.enter.native="handleQuery" />
<MemoInput storageKey="material" v-model="queryParams.material" placeholder="请输入材质" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="厂家">
<MemoInput storageKey="manufacturer" v-model="queryParams.manufacturer" placeholder="请输入厂家" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="表面处理">
<MemoInput storageKey="surfaceTreatmentDesc" v-model="queryParams.surfaceTreatmentDesc" placeholder="请输入表面处理" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="锌层">
<MemoInput storageKey="zincLayer" v-model="queryParams.zincLayer" placeholder="请输入锌层" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
@@ -82,9 +91,13 @@
<script>
import { listRawMaterial } from '@/api/wms/rawMaterial';
import MemoInput from '@/components/MemoInput/index.vue'
export default {
name: 'RawMaterialSelector',
components: {
MemoInput
},
props: {
value: {
type: String,

View File

@@ -1,5 +1,5 @@
<template>
<el-autocomplete class="inline-input" v-model="inputValue" :fetch-suggestions="querySearch" placeholder="请输入内容"
<el-autocomplete class="inline-input" v-model="inputValue" :fetch-suggestions="querySearch" :placeholder="placeholder"
:trigger-on-focus="triggerMode === 'focus'" @select="handleSelect" @change="handleInputChange"
clearable></el-autocomplete>
</template>
@@ -18,6 +18,16 @@ export default {
required: true,
validator: (value) => value.trim() !== '' // 校验非空
},
// 输入框占位符,默认“请输入内容”
placeholder: {
type: String,
default: '请输入内容'
},
// // 当localstorage中不存在缓存时自动创建一个数组作为默认值
// autoCreateDefault: {
// type: Array,
// default: () => []
// },
// 最大缓存数量默认100条
maxCacheCount: {
type: Number,

View File

@@ -75,8 +75,8 @@ export default {
return {
codeUrl: "",
loginForm: {
username: "admin",
password: "admin123",
username: "",
password: "",
rememberMe: false,
code: "",
uuid: ""

View File

@@ -51,7 +51,7 @@
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-row :gutter="10" class="mb8" v-if="showControl">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增</el-button>
</el-col>
@@ -76,11 +76,6 @@
<el-table-column label="厂家卷号" align="center" prop="supplierCoilNo" />
<el-table-column label="逻辑库位" align="center" prop="warehouseName" v-if="!hideWarehouseQuery" />
<el-table-column label="实际库区" align="center" prop="actualWarehouseName" v-if="!hideWarehouseQuery" />
<!-- <el-table-column label="物料类型" align="center" prop="itemType">
<template slot-scope="scope">
{{ scope.row.itemType == 'raw_material' ? '原料' : '产品' }}
</template>
</el-table-column> -->
<el-table-column label="物料类型" align="center" prop="materialType" />
<el-table-column label="产品类型" align="center" prop="itemName">
<template slot-scope="scope">
@@ -96,11 +91,6 @@
</RawMaterialInfo>
</template>
</el-table-column>
<!-- <el-table-column label="数据类型" align="center" prop="dataType">
<template slot-scope="scope">
{{ scope.row.dataType == 0 ? '历史数据' : '当前数据' }}
</template>
</el-table-column> -->
<el-table-column label="更新时间" align="center" prop="updateTime" />
<el-table-column label="更新人" align="center" prop="updateBy" />
@@ -144,8 +134,8 @@
<el-button size="mini" type="text" icon="el-icon-view" @click="handlePreviewLabel(scope.row)">
导出标签
</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleCheck(scope.row)">修正</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleCheck(scope.row)" v-if="showControl">修正</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-if="showControl">删除</el-button>
<el-button size="mini" type="text" icon="el-icon-search" @click="handleTrace(scope.row)">追溯</el-button>
</template>
</el-table-column>
@@ -296,6 +286,10 @@ export default {
hideType: {
type: Boolean,
default: false,
},
showControl: {
type: Boolean,
default: true,
}
},
data() {

View File

@@ -17,6 +17,7 @@ export default {
materialType: '成品',
status: 1
},
showControl: false,
labelType: '3',
showStatus: false,
hideType: false,

View File

@@ -166,7 +166,7 @@
</el-select>
</el-form-item>
<el-form-item label="真实库区" prop="warehouseId">
<el-form-item label="真实库区" prop="actualWarehouseId">
<ActualWarehouseSelect
v-model="updateForm.actualWarehouseId"
placeholder="请选择真实库区"

View File

@@ -0,0 +1,579 @@
<!-- 发货单组件 -->
<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>
<!-- 表格 -->
<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包装</p>
<p>
裸包周三径四简包1周三径四内外护角简包2周三径四+防锈纸普包周三径四+内外护角+防锈纸+端护板精包1周三径四+内外护角+防锈纸+薄膜+端护板+内外护板精包2周三径四+内外护角+防锈纸+薄膜+端护板+内外护板+木托
</p>
</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: ''
},
// 本地可编辑的发货单明细
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) || ''
};
}
},
immediate: true,
deep: true
},
waybillDetails: {
handler(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 '';
},
// 添加明细
addDetail() {
this.localWaybillDetails.push({
productName: '',
edgeType: '',
packageType: '',
settlementType: '',
rawMaterialFactory: '',
coilNumber: '',
specification: '',
material: '',
quantity: 0,
weight: 0,
unitPrice: 0,
remark: ''
});
},
// 删除明细
deleteDetail(index) {
this.localWaybillDetails.splice(index, 1);
},
// 保存为图片
saveAsImage() {
const node = this.$refs.waybillRef;
domtoimage.toPng(node)
.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('保存图片失败');
});
},
// 打印发货单
printWaybill() {
const node = this.$refs.waybillRef;
printJS({
printable: node,
maxWidth: 1200,
type: 'html',
scanStyles: true,
targetStyles: ['*']
});
}
}
}
</script>
<style scoped>
.waybill-container {
width: 1200px;
max-width: 1200px;
min-width: 1200px;
margin: 0 auto;
padding: 20px;
box-sizing: border-box;
background: white;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
font-family: SimSun, serif;
}
/* 头部样式 */
.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;
margin-right: 10px;
display: inline-block;
width: 80px;
text-align: right;
white-space: nowrap;
}
.date-label {
width: 16px;
}
/* 可编辑输入框样式 */
.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: 60px;
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: 80px; /* 卷号 */
}
.waybill-table th:nth-child(7),
.waybill-table td:nth-child(7) {
width: 60px; /* 规格 */
}
.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: 12px;
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;
}
.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;
}
/* 操作按钮样式 */
.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>

View File

@@ -2,18 +2,12 @@
<div class="app-container">
<el-row :gutter="10">
<el-col :span="4">
<div class="plan-container">
<el-input v-model="planQueryParams.planName" placeholder="计划名称" clearable
@change="handlePlanQuery" style="width: 100%;" />
<el-tree
v-loading="planLoading"
:data="planTreeData"
:props="planTreeProps"
@node-click="handlePlanSelect"
default-expand-all
style="margin-top: 10px; height: 550px; overflow: auto;"
>
<el-card>
<el-input v-model="planQueryParams.planName" placeholder="计划名称" clearable @change="handlePlanQuery"
style="width: 100%;" />
<el-tree v-loading="planLoading" :data="planTreeData" :props="planTreeProps" @node-click="handlePlanSelect"
default-expand-all style="margin-top: 10px; height: 550px; overflow: auto;">
<template slot-scope="{ node, data }">
<span class="plan-node">
<span class="plan-name">{{ data.planName }}</span>
@@ -21,65 +15,71 @@
</template>
</el-tree>
<pagination v-show="planTotal > 0" :total="planTotal" :page.sync="planQueryParams.pageNum"
:limit.sync="planQueryParams.pageSize" @pagination="getPlanList" style="margin-top: 10px;" layout="total, prev, jumper, next"/>
</div>
:limit.sync="planQueryParams.pageSize" @pagination="getPlanList" style="margin-top: 10px;"
layout="total, prev, jumper, next" />
</el-card>
</el-col>
<el-col :span="20">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"
label-width="68px">
<el-form-item label="发货单名称" prop="waybillName">
<el-input v-model="queryParams.waybillName" placeholder="请输入发货单名称" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="收货单位" prop="consigneeUnit">
<el-input v-model="queryParams.consigneeUnit" placeholder="请输入收货单位" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" :disabled="!selectedPlan"
title="请先选择发货计划">新增</el-button>
<el-button type="success" plain icon="el-icon-refresh" size="mini" @click="handleQuery">刷新</el-button>
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
</el-form-item>
</el-form>
<el-card>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"
label-width="68px">
<el-form-item label="发货单名称" prop="waybillName">
<el-input v-model="queryParams.waybillName" placeholder="请输入发货单名称" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="收货单位" prop="consigneeUnit">
<el-input v-model="queryParams.consigneeUnit" placeholder="请输入收货单位" clearable
@keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
:disabled="!selectedPlan" title="请先选择发货计划">新增</el-button>
<el-button type="success" plain icon="el-icon-refresh" size="mini" @click="handleQuery">刷新</el-button>
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" border :data="deliveryWaybillList" highlight-current-row @row-click="handleRowClick">
<el-table-column label="发货单唯一ID" align="center" prop="waybillId" v-if="false" />
<el-table-column label="发货单名称" align="center" prop="waybillName" />
<el-table-column label="车牌" align="center" prop="licensePlate" width="100" />
<el-table-column label="发货单位" align="center" prop="senderUnit" />
<el-table-column label="发货时间" align="center" prop="deliveryTime" width="100">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.deliveryTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="负责人" align="center" prop="principal" />
<el-table-column label="负责人电话" align="center" prop="principalPhone" width="100" />
<el-table-column label="完成状态" align="center" prop="status" width="120">
<template slot-scope="scope">
<el-select v-model="scope.row.status" placeholder="请选择完成状态" @change="handleStatusChange(scope.row)">
<el-option label="已发货" :value="1" />
<el-option label="发货" :value="0" />
</el-select>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-table v-loading="loading" border :data="deliveryWaybillList" highlight-current-row
@row-click="handleRowClick">
<el-table-column label="发货单唯一ID" align="center" prop="waybillId" v-if="false" />
<el-table-column label="发货单名称" align="center" prop="waybillName" />
<el-table-column label="车牌" align="center" prop="licensePlate" width="100" />
<el-table-column label="发货单位" align="center" prop="senderUnit" />
<el-table-column label="发货时间" align="center" prop="deliveryTime" width="100">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.deliveryTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="负责人" align="center" prop="principal" />
<el-table-column label="负责人电话" align="center" prop="principalPhone" width="100" />
<el-table-column label="完成状态" align="center" prop="status" width="120">
<template slot-scope="scope">
<el-select v-model="scope.row.status" placeholder="请选择完成状态" @change="handleStatusChange(scope.row)">
<el-option label="发货" :value="1" />
<el-option label="未发货" :value="0" />
</el-select>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click.stop="handlePrint(scope.row)">打印发货单</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleUpdate(scope.row)">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click.stop="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize" @pagination="getList" />
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize" @pagination="getList" />
<el-divider style="margin: 20px 0;" />
<el-divider style="margin: 20px 0;" />
<DeliveryWaybillDetail ref="detailTable" :waybillId="waybillId" />
</el-card>
<DeliveryWaybillDetail ref="detailTable" :waybillId="waybillId" />
</el-col>
</el-row>
@@ -126,20 +126,34 @@
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<!-- 打印发货单对话框 -->
<el-dialog
title="打印发货单"
:visible.sync="printDialogVisible"
width="90%"
append-to-body>
<WayBill
:waybill="currentWaybill"
:waybillDetails="currentWaybillDetails" />
</el-dialog>
</div>
</template>
<script>
import { listDeliveryWaybill, getDeliveryWaybill, delDeliveryWaybill, addDeliveryWaybill, updateDeliveryWaybill } from "@/api/wms/deliveryWaybill";
import { listDeliveryPlan } from "@/api/wms/deliveryPlan"; // 导入发货计划API
import { listDeliveryWaybillDetail } from "@/api/wms/deliveryWaybillDetail";
import MemoInput from "@/components/MemoInput";
import DeliveryWaybillDetail from "../componnets/detailTable.vue";
import DeliveryWaybillDetail from "../components/detailTable.vue";
import WayBill from "../components/wayBill.vue";
export default {
name: "DeliveryWaybill",
components: {
MemoInput,
DeliveryWaybillDetail,
WayBill
},
data() {
return {
@@ -160,6 +174,10 @@ export default {
// 发货单主表格数据
deliveryWaybillList: [],
waybillId: null,
// 打印相关数据
printDialogVisible: false,
currentWaybill: {},
currentWaybillDetails: [],
// 弹出层标题
title: "",
// 是否显示弹出层
@@ -285,7 +303,7 @@ export default {
this.planLoading = false;
});
},
// 发货计划搜索
handlePlanQuery() {
this.planQueryParams.pageNum = 1;
@@ -378,6 +396,41 @@ export default {
this.download('wms/deliveryWaybill/export', {
...this.queryParams
}, `deliveryWaybill_${new Date().getTime()}.xlsx`)
},
/** 打印发货单 */
handlePrint(row) {
this.loading = true;
// 设置当前发货单
this.currentWaybill = row;
// 获取发货单明细
listDeliveryWaybillDetail({
waybillId: row.waybillId,
pageNum: 1,
pageSize: 1000 // 获取所有明细
}).then(response => {
// 处理字段映射确保与wayBill组件使用的字段名一致
this.currentWaybillDetails = response.rows.map(item => ({
productName: item.productName,
edgeType: item.edgeType,
packageType: item.packaging, // 映射packaging到packageType
settlementType: item.settlementType,
rawMaterialFactory: item.rawMaterialFactory,
coilNumber: item.coilNo, // 映射coilNo到coilNumber
specification: item.specification,
material: item.material,
quantity: item.quantity,
weight: item.weight,
unitPrice: item.unitPrice,
remark: item.remark
}));
// 打开打印对话框
this.printDialogVisible = true;
this.loading = false;
}).catch(error => {
console.error('获取发货单明细失败:', error);
this.$modal.msgError('获取发货单明细失败');
this.loading = false;
});
}
}
};
@@ -389,6 +442,7 @@ export default {
height: 100%;
border-right: 1px solid #ebeef5;
}
.plan-container h3 {
margin: 0 0 10px 0;
padding: 0;

View File

@@ -34,18 +34,18 @@
<el-button type="primary" icon="el-icon-plus" size="mini" @click="openQuickDialog()">
快速新增 1~3
</el-button>
<el-button type="success" icon="el-icon-plus" size="mini" @click="openCreateDialog()">
<!-- <el-button type="success" icon="el-icon-plus" size="mini" @click="openCreateDialog()">
新增单级节点
</el-button>
</el-button> -->
<el-button type="warning" icon="el-icon-download" size="mini" @click="handleDownloadTemplate">
下载导入模板
</el-button>
<el-button type="danger" icon="el-icon-upload2" size="mini" @click="triggerImport">
导入
</el-button>
<el-button type="info" icon="el-icon-sort" size="mini" @click="toggleExpand">
<!-- <el-button type="info" icon="el-icon-sort" size="mini" @click="toggleExpand">
{{ isExpandAll ? '折叠全部' : '展开全部' }}
</el-button>
</el-button> -->
<input
ref="importInput"
type="file"
@@ -624,12 +624,12 @@ export default {
};
runner()
.then(() => {
this.$modal.msgSuccess("已完成录入");
this.$message.success("批量新增成功");
this.quickDialog.visible = false;
this.getTreeData();
})
.catch(() => {
this.$modal.msgError("批量新增失败,请重试");
this.$message.error("批量新增失败,请重试");
})
.finally(() => {
this.quickDialog.loading = false;

19
klp-ui/vitest.config.js Normal file
View File

@@ -0,0 +1,19 @@
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
// 使用jsdom环境进行测试
environment: 'jsdom',
// 测试文件匹配模式
include: ['src/**/*.test.js'],
// 覆盖率配置
coverage: {
reporter: ['text', 'json', 'html']
}
},
resolve: {
alias: {
'@': './src'
}
}
})