Files
xgy-oa/klp-ui/src/views/wms/packing/packing.vue
砂糖 602b10b689 feat(wms): 重构报表配置并添加销售员管理功能
重构各报表模块配置,将重复配置提取到config.js统一管理
在打包记录和打包页面添加销售员选择和显示功能
新增销售员管理页面,支持销售员标签的增删改查
2026-03-24 15:38:30 +08:00

439 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 class="app-container">
<el-row :gutter="20">
<!-- 左侧打包表单和明细 -->
<el-col :span="15">
<div class="section-card">
<div class="section-header">
<h3 class="section-title">创建打包单据</h3>
<div class="header-actions">
<el-button :loading="buttonLoading" type="primary" @click="submitForm">保存单据</el-button>
<el-button @click="resetForm">重置</el-button>
<coil-selector dialog-width="1200px" :use-trigger="true" multiple :multiple="true" :filters="coilFilters"
:disableO="true" @confirm="handleBatchAddCoil">
<el-button type="primary" plain icon="el-icon-plus" size="small">批量添加钢卷</el-button>
</coil-selector>
</div>
</div>
<el-form ref="form" :model="form" :rules="rules" label-width="100px" style="margin-bottom: 20px;">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="单据号" prop="packingNo">
<el-input v-model="form.packingNo" placeholder="系统自动生成" disabled />
</el-form-item>
<el-form-item label="批次号" prop="batchNo">
<el-input v-model="form.batchNo" placeholder="请输入批次号" clearable />
</el-form-item>
<el-form-item label="销售员" prop="saleName">
<el-select v-model="form.saleName" placeholder="请选择销售名称" clearable filterable>
<el-option v-for="item in dict.type.wip_pack_saleman" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="打包时间" prop="packingTime">
<el-date-picker v-model="form.packingTime" type="datetime" placeholder="选择打包时间"
value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%" />
</el-form-item>
<el-form-item label="操作人" prop="packingBy">
<el-input v-model="form.packingBy" placeholder="请输入操作人" clearable />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="section-header" style="margin-top: 20px;">
<h3 class="section-title">打包明细</h3>
</div>
<el-table v-loading="detailLoading" :data="detailList" border style="width: 100%" max-height="400">
<el-table-column label="序号" type="index" width="55" align="center" />
<el-table-column label="钢卷号" align="center" prop="coilNo" min-width="120">
<template slot-scope="scope">
<current-coil-no :current-coil-no="scope.row.coilNo" />
</template>
</el-table-column>
<el-table-column label="产品类型" align="center" width="250">
<template slot-scope="scope">
<ProductInfo v-if="scope.row.itemType == 'product'" :product="scope.row" />
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row" />
</template>
</el-table-column>
<!-- <el-table-column label="品名" align="center" prop="itemName" min-width="100" />
<el-table-column label="规格" align="center" prop="specification" min-width="120" show-overflow-tooltip />
<el-table-column label="材质" align="center" prop="material" min-width="80" /> -->
<el-table-column label="重量" align="center" prop="coilNetWeight" width="80">
<template slot-scope="scope">{{ scope.row.coilNetWeight }}t</template>
</el-table-column>
<!-- <el-table-column label="销售名称" align="center" prop="saleName" width="140">
<template slot-scope="scope">
<el-select v-model="scope.row.saleName" placeholder="请选择销售名称">
<el-option v-for="item in dict.type.wip_pack_saleman" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</template>
</el-table-column> -->
<el-table-column label="打包前位置" align="center" prop="fromWarehouseName" min-width="120"
show-overflow-tooltip />
<el-table-column label="操作" align="center" width="100">
<template slot-scope="scope">
<template v-if="scope.row.editing">
<el-button size="mini" type="text" @click="handleSaveDetail(scope.row)">保存</el-button>
<el-button size="mini" type="text" @click="scope.row.editing = false">取消</el-button>
</template>
<template v-else>
<el-button size="mini" type="text" @click="handleDeleteDetail(scope.row)">删除</el-button>
</template>
</template>
</el-table-column>
</el-table>
<div v-if="detailList.length > 0" class="detail-summary">
<el-descriptions :column="3" size="small" border>
<el-descriptions-item label="总卷数">
{{ detailList.length }}
</el-descriptions-item>
<el-descriptions-item label="总重量">
{{ totalWeight }}
</el-descriptions-item>
</el-descriptions>
</div>
</div>
</el-col>
<!-- 右侧最近创建的打包单据 -->
<el-col :span="9">
<div class="section-card">
<div class="section-header">
<h3 class="section-title">最近创建的单据</h3>
<el-button size="mini" icon="el-icon-refresh" @click="getRecentList">刷新</el-button>
</div>
<el-table v-loading="recentLoading" :data="recentList" border style="width: 100%">
<el-table-column label="单据号" align="center" prop="packingNo" show-overflow-tooltip />
<el-table-column label="批次号" align="center" prop="batchNo" show-overflow-tooltip />
<el-table-column label="打包日期" align="center" prop="packingTime" />
<el-table-column label="卷数" align="center" prop="coilCount" width="60" />
<el-table-column label="总重" align="center" prop="totalNetWeight" width="80">
<template slot-scope="scope">{{ scope.row.totalNetWeight }}t</template>
</el-table-column>
</el-table>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
import { addPacking, updatePacking, listPacking, listPackingDetail, updatePackingDetail, delPackingDetail, batchAddPackingDetail, createPacking } from '@/api/wms/packing';
import CoilSelector from '@/components/CoilSelector';
import ProductInfo from "@/components/KLPService/Renderer/ProductInfo";
import RawMaterialInfo from "@/components/KLPService/Renderer/RawMaterialInfo";
export default {
name: 'PackingCreate',
dicts: [],
components: {
CoilSelector,
ProductInfo,
RawMaterialInfo
},
dicts: ['wip_pack_saleman'],
data() {
return {
// 表单参数
form: {
packingNo: '',
batchNo: '',
packingDate: '',
team: '',
packingBy: '',
remark: ''
},
// 当前正在编辑的打包单据ID
currentPackingId: null,
// 打包明细列表
detailList: [],
// 最近创建的单据列表
recentList: [],
// 按钮loading
buttonLoading: false,
// 明细loading
detailLoading: false,
// 最近列表loading
recentLoading: false,
// 表单校验
rules: {
batchNo: [
{ required: true, message: '批次号不能为空', trigger: 'blur' }
],
packingDate: [
{ required: true, message: '打包日期不能为空', trigger: 'change' }
]
},
// 钢卷选择器筛选参数
coilFilters: {
dataType: 1,
status: 0,
selectType: 'product',
excludePacked: true
}
};
},
computed: {
// 总重量
totalWeight() {
return this.detailList.reduce((acc, item) => acc + Number(item.coilNetWeight || 0), 0).toFixed(3);
},
// 已填位置的明细数量
filledPositionCount() {
return this.detailList.filter(item => item.afterPosition).length;
}
},
created() {
// 默认设置为今天
this.form.packingDate = this.formatDate(new Date());
// 设置打包时间默认值为当前时间
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hour = String(now.getHours()).padStart(2, '0');
const minute = String(now.getMinutes()).padStart(2, '0');
const second = String(now.getSeconds()).padStart(2, '0');
this.form.packingTime = `${year}-${month}-${day} ${hour}:${minute}:${second}`;
// 自动生成单据号和批次号格式为yyyyMMddHHmm
const code = `${year}${month}${day}${hour}${minute}`;
this.form.packingNo = code;
this.form.batchNo = code;
// 设置操作人默认值为当前登录用户
this.form.packingBy = this.$store.getters.nickName;
this.getRecentList();
},
methods: {
/** 格式化日期 */
formatDate(date) {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
},
/** 提交表单 */
submitForm() {
this.$refs['form'].validate(valid => {
if (valid) {
if (this.detailList.length === 0) {
this.$modal.msgWarning('请添加打包明细');
return;
}
this.buttonLoading = true;
const data = {
...this.form,
totalNetWeight: this.totalWeight,
details: this.detailList
};
createPacking(data).then(response => {
this.$modal.msgSuccess('创建成功');
this.getRecentList();
this.resetForm();
}).finally(() => {
this.buttonLoading = false;
});
}
});
},
/** 重置表单 */
resetForm() {
// 自动生成新的单据号和批次号格式为yyyyMMddHHmm
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const hour = String(now.getHours()).padStart(2, '0');
const minute = String(now.getMinutes()).padStart(2, '0');
const second = String(now.getSeconds()).padStart(2, '0');
const code = `${year}${month}${day}${hour}${minute}`;
this.form = {
packingNo: code,
batchNo: code,
packingDate: this.formatDate(new Date()),
packingTime: `${year}-${month}-${day} ${hour}:${minute}:${second}`,
packingBy: this.$store.getters.nickName,
remark: ''
};
this.currentPackingId = null;
this.detailList = [];
this.$refs.form.resetFields();
},
/** 批量添加钢卷 */
handleBatchAddCoil(coils) {
const newCoils = coils.map(coil => ({
coilId: coil.coilId,
coilNo: coil.currentCoilNo,
itemName: coil.itemName,
specification: coil.specification,
material: coil.material,
coilNetWeight: coil.netWeight || '',
coilGrossWeight: coil.grossWeight || '0.000',
fromWarehouseId: coil.warehouseId || '',
fromWarehouseName: coil.warehouseName || '',
itemType: coil.itemType || '',
itemId: coil.itemId || '',
// beforePosition: coil.actualWarehouseName || '',
toWarehouseId: '2035892198703349761',
toWarehouseName: '打包待发货'
}));
this.detailList = [...this.detailList, ...newCoils];
this.$modal.msgSuccess('添加成功');
},
/** 获取明细列表 */
getDetailList() {
if (!this.currentPackingId) return;
this.detailLoading = true;
listPackingDetail({ packingId: this.currentPackingId }).then(response => {
this.detailList = response.rows || [];
}).finally(() => {
this.detailLoading = false;
});
},
/** 编辑明细 */
handleEditDetail(row) {
this.$set(row, 'editing', true);
this.$set(row, 'afterPositionBackup', row.afterPosition);
},
/** 保存明细修改 */
handleSaveDetail(row) {
this.detailLoading = true;
updatePackingDetail(row).then(response => {
this.$modal.msgSuccess('保存成功');
row.editing = false;
this.getDetailList();
}).catch(() => {
this.detailLoading = false;
});
},
/** 删除明细 */
handleDeleteDetail(row) {
// 前端删除根据coilId删除所有相关明细
this.detailList = this.detailList.filter(item => item.coilId !== row.coilId);
// this.$modal.confirm('确认删除该钢卷明细吗?').then(() => {
// return delPackingDetail(row.detailId);
// }).then(() => {
// this.$modal.msgSuccess('删除成功');
// this.getDetailList();
// }).catch(() => { });
},
/** 获取最近创建的单据列表 */
getRecentList() {
this.recentLoading = true;
listPacking({
pageNum: 1,
pageSize: 10,
orderByColumn: 'createTime',
isAsc: 'desc'
}).then(response => {
this.recentList = response.rows || [];
}).finally(() => {
this.recentLoading = false;
});
},
/** 查看单据详情 */
handleViewDetail(row) {
this.$router.push({
path: '/wms/packing/record',
query: { packingId: row.packingId }
});
},
/** 继续编辑单据 */
handleContinueEdit(row) {
this.currentPackingId = row.packingId;
this.form.packingNo = row.packingNo;
this.form.batchNo = row.batchNo;
this.form.packingDate = row.packingDate;
this.form.team = row.team;
this.form.operator = row.operator;
this.form.remark = row.remark;
this.getDetailList();
this.$message.success('已加载单据,请继续编辑');
}
}
};
</script>
<style scoped lang="scss">
.app-container {
padding: 20px;
}
.section-card {
background-color: #ffffff;
border: 1px solid #e4e7ed;
border-radius: 4px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 2px solid #e4e7ed;
.section-title {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #303133;
}
.header-actions {
display: flex;
align-items: center;
gap: 12px;
.tip-text {
font-size: 13px;
color: #909399;
}
}
}
.form-footer {
display: flex;
justify-content: center;
gap: 12px;
margin-top: 20px;
}
.empty-tips {
text-align: center;
padding: 60px 20px;
color: #909399;
i {
font-size: 48px;
margin-bottom: 16px;
display: block;
color: #e6a23c;
}
p {
margin: 0;
font-size: 14px;
}
}
.detail-summary {
margin-top: 16px;
padding: 12px;
background-color: #f5f7fa;
border-radius: 4px;
}
</style>