Files
klp-oa/klp-ui/src/views/wms/stockIo/panels/detail.vue
2025-07-31 10:07:37 +08:00

585 lines
21 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">
<template v-if="stockIo && stockIo.stockIoId">
<!-- 主表信息 -->
<el-descriptions :title="'单号:' + (stockIo.stockIoCode || '-')" :column="2" border>
<el-descriptions-item label="类型">
<el-tag :type="getIoTypeTagType(stockIo.ioType)">
{{ getIoTypeLabel(stockIo.ioType) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="业务类型">
<el-tag :type="getBizTypeTagType(stockIo.bizType)">
{{ getBizTypeLabel(stockIo.bizType) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag :type="stockIo.status === 2 ? 'success' : (stockIo.status === 1 ? 'warning' : 'info')">
{{ stockIo.status === 2 ? '已完成' : (stockIo.status === 0 ? '草稿' : '待审核') }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="备注">{{ stockIo.remark }}</el-descriptions-item>
</el-descriptions>
<br />
<!-- 明细表格 -->
<el-tabs v-model="activeTab" style="margin-bottom: 10px;">
<el-tab-pane label="手动录入" name="manual">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
:disabled="stockIo.status >= 2"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single || stockIo.status >= 2"
@click="handleUpdate"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple || stockIo.status >= 2"
@click="handleDelete"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="manualList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="库区/库位" align="center" prop="warehouseName" />
<el-table-column
v-if="stockIo.ioType === 'transfer'"
label="源库区/库位"
align="center"
prop="fromWarehouseName"
/>
<el-table-column label="物品类型" align="center" prop="itemType">
<template slot-scope="scope">
<dict-tag :options="dict.type.stock_item_type" :value="scope.row.itemType" />
</template>
</el-table-column>
<el-table-column label="物品信息" align="center" prop="itemId">
<template slot-scope="scope">
<raw-material-info v-if="scope.row.itemType === ITEM_TYPE.RAW_MATERIAL" :material-id="scope.row.itemId" />
<product-info v-else-if="scope.row.itemType === ITEM_TYPE.PRODUCT" :product-id="scope.row.itemId" />
<span v-else>{{ scope.row.itemId }}</span>
</template>
</el-table-column>
<el-table-column label="数量" align="center" prop="quantity" />
<el-table-column label="单位" align="center" prop="unit" />
<el-table-column label="批次号" align="center" prop="batchNo" />
<el-table-column label="备注" align="center" prop="remark" />
<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)"
:disabled="stockIo.status >= 2"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
:disabled="stockIo.status >= 2"
>删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="扫码枪" name="scanner">
<el-table :data="scannerList" :show-header="true" :border="true" style="width: 100%;" :default-sort="{}" :highlight-current-row="true">
<el-table-column type="index" width="55" align="center" label="#" />
<el-table-column label="库区/库位" align="center" prop="warehouseName" />
<el-table-column v-if="stockIo.ioType === 'transfer'" label="源库区/库位" align="center" prop="fromWarehouseName" />
<el-table-column label="物品类型" align="center" prop="itemType">
<template slot-scope="scope">
<dict-tag :options="dict.type.stock_item_type" :value="scope.row.itemType" />
</template>
</el-table-column>
<el-table-column label="物品ID" align="center" prop="itemId" />
<el-table-column label="数量" align="center" prop="quantity" />
<el-table-column label="单位" align="center" prop="unit" />
<el-table-column label="批次号" align="center" prop="batchNo" />
<el-table-column label="备注" align="center" prop="remark" />
</el-table>
</el-tab-pane>
</el-tabs>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 操作按钮 -->
<div style="margin-top: 20px; text-align: right;">
<!-- 状态修改按钮 -->
<el-button
v-if="stockIo.status === 0"
type="warning"
:loading="statusLoading"
@click="handleUpdateStatus"
>{{ getStatusButtonText() }}</el-button>
<!-- 审核按钮 -->
<el-button
v-if="stockIo.status === 1 && stockIo.ioType != 'in'"
type="primary"
:loading="auditLoading"
@click="handleAudit"
>{{ getAuditButtonText() }}</el-button>
<!-- 撤回按钮 -->
<el-button
v-if="stockIo.status === 2 && stockIo.ioType != 'in'"
type="warning"
:loading="cancelLoading"
@click="handleCancel"
>撤回</el-button>
</div>
<!-- 明细编辑弹窗 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-form-item label="出入库单ID" prop="stockIoId">
<el-input v-model="form.stockIoId" placeholder="请输入出入库单ID" :disabled="true" />
</el-form-item>
<el-form-item label="库区/库位" prop="warehouseId">
<warehouse-select v-model="form.warehouseId" placeholder="请选择库区/库位" />
</el-form-item>
<el-form-item
v-if="stockIo.ioType === 'transfer'"
label="源库区/库位"
prop="fromWarehouseId"
>
<warehouse-select v-model="form.fromWarehouseId" placeholder="请选择源库区/库位" />
</el-form-item>
<el-form-item label="物品类型" prop="itemType">
<el-select v-model="form.itemType" placeholder="请选择物品类型">
<el-option v-for="dict in dict.type.stock_item_type" :key="dict.value" :label="dict.label"
:value="dict.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="物品ID" prop="itemId">
<RawMaterialSelect
v-if="form.itemType === ITEM_TYPE.RAW_MATERIAL"
v-model="form.itemId"
placeholder="请选择原材料"
@change="onItemChange"
/>
<ProductSelect
v-else-if="form.itemType === ITEM_TYPE.PRODUCT"
v-model="form.itemId"
placeholder="请选择产品"
@change="onItemChange"
/>
<el-input
v-else
v-model="form.itemId"
placeholder="请输入物品ID"
/>
</el-form-item>
<el-form-item label="数量" prop="quantity">
<el-input v-model="form.quantity" placeholder="请输入数量" />
</el-form-item>
<el-form-item label="单位" prop="unit">
<el-input v-model="form.unit" placeholder="请输入单位" :disabled="unitDisabled" />
</el-form-item>
<el-form-item label="批次号" prop="batchNo">
<el-input v-model="form.batchNo" placeholder="请输入批次号" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</template>
<template v-else>
<div style="height:200px;text-align:center;line-height:200px;">未获取到主表数据</div>
</template>
</div>
</template>
<script>
import { listStockIoDetail, getStockIoDetail, delStockIoDetail, addStockIoDetail, updateStockIoDetail } from "@/api/wms/stockIoDetail";
import { auditStockIo, updateStockIoStatus, cancelStockIo, getStockIo } from "@/api/wms/stockIo";
import WarehouseSelect from '@/components/WarehouseSelect';
import RawMaterialSelect from '@/components/KLPService/RawMaterialSelect';
import ProductSelect from '@/components/KLPService/ProductSelect';
import { ITEM_TYPE } from '@/utils/enums';
import {RawMaterialInfo, ProductInfo} from "@/components/KLPService";
export default {
name: "StockIoDetailPanel",
components: {
WarehouseSelect,
RawMaterialSelect,
ProductSelect,
RawMaterialInfo,
ProductInfo
},
dicts: ['stock_item_type'],
props: {
stockIo: {
type: Object,
required: true
}
},
data() {
return {
ITEM_TYPE,
loading: true,
stockIoDetailList: [],
total: 0,
queryParams: {
pageNum: 1,
pageSize: 10,
stockIoId: undefined
},
auditLoading: false,
open: false,
title: '',
form: {},
buttonLoading: false,
rules: {
warehouseId: [
{ required: true, message: "库区/库位不能为空", trigger: "blur" }
],
itemType: [
{ required: true, message: "物品类型不能为空", trigger: "blur" }
],
itemId: [
{ required: true, message: "物品ID不能为空", trigger: "blur" }
],
quantity: [
{ required: true, message: "数量不能为空", trigger: "blur" }
],
unit: [
{ required: true, message: "单位不能为空", trigger: "blur" }
]
},
ids: [],
single: true,
multiple: true,
statusLoading: false, // 新增状态修改按钮加载状态
cancelLoading: false, // 撤回按钮加载状态
unitDisabled: false, // 新增:单位输入框是否禁用
activeTab: 'manual' // 新增:当前激活的标签页
};
},
watch: {
'stockIo.stockIoId': {
handler(val) {
if (val) {
this.queryParams.stockIoId = val;
this.getList();
}
},
immediate: true
}
},
computed: {
// 手动录入的列表,使用recordType区分为0
manualList() {
return this.stockIoDetailList.filter(item => item.recordType === 0);
},
// 来自扫码枪的列表为1
scannerList() {
return this.stockIoDetailList.filter(item => item.recordType === 1);
}
},
mounted() {
if (this.stockIo && this.stockIo.stockIoId) {
this.queryParams.stockIoId = this.stockIo.stockIoId;
this.getList();
}
},
methods: {
getList() {
if (!this.queryParams.stockIoId) return;
this.loading = true;
listStockIoDetail(this.queryParams).then(response => {
this.stockIoDetailList = response.rows;
this.total = response.total;
}).finally(() => {
this.loading = false;
});
},
handleAudit() {
// 检查是否有明细数据
if (!this.stockIoDetailList || this.stockIoDetailList.length === 0) {
this.$modal.msgError('请先添加明细数据');
return;
}
// 确认审核
this.$modal.confirm('确认要审核此出入库单吗?审核后将影响库存数据。').then(() => {
this.auditLoading = true;
auditStockIo(this.stockIo.stockIoId).then(response => {
this.$modal.msgSuccess('审核成功');
// 更新主表状态
this.$set(this.stockIo, 'status', 2);
// 刷新明细列表
this.getList();
// 通知父组件状态已更新
this.$emit('status-changed', this.stockIo);
}).catch(error => {
console.error('审核失败:', error);
this.$modal.msgError('审核失败:' + (error.message || '未知错误'));
}).finally(() => {
this.auditLoading = false;
});
}).catch(() => {
// 用户取消审核
});
},
handleSelectionChange(selection) {
this.ids = selection.map(item => item.detailId)
this.single = selection.length!==1
this.multiple = !selection.length
},
handleAdd() {
this.reset();
this.form.stockIoId = this.stockIo.stockIoId;
this.form.recordType = 0; // 新增时设为手动录入
this.open = true;
this.title = "添加出入库单明细";
},
handleUpdate(row) {
this.loading = true;
this.reset();
const detailId = row.detailId || this.ids
getStockIoDetail(detailId).then(response => {
this.loading = false;
this.form = response.data;
this.form.recordType = 0; // 修改时强制设为手动录入
this.open = true;
this.title = "修改出入库单明细";
});
},
submitForm() {
// 动态添加源库位验证规则
if (this.stockIo.ioType === 'transfer') {
this.$set(this.rules, 'fromWarehouseId', [
{ required: true, message: "源库区/库位不能为空", trigger: "blur" }
]);
} else {
this.$delete(this.rules, 'fromWarehouseId');
}
this.$refs["form"].validate(valid => {
if (valid) {
this.buttonLoading = true;
if (this.form.detailId != null) {
updateStockIoDetail(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
}).finally(() => {
this.buttonLoading = false;
});
} else {
addStockIoDetail(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
// 新增明细后刷新主表状态
this.refreshStockIoStatus();
}).finally(() => {
this.buttonLoading = false;
});
}
}
});
},
handleDelete(row) {
const detailIds = row.detailId || this.ids;
this.$modal.confirm('是否确认删除出入库单明细编号为"' + detailIds + '"的数据项?').then(() => {
this.loading = true;
return delStockIoDetail(detailIds);
}).then(() => {
this.loading = false;
this.getList();
this.$modal.msgSuccess("删除成功");
// 删除明细后刷新主表状态
this.refreshStockIoStatus();
}).catch(() => {
}).finally(() => {
this.loading = false;
});
},
reset() {
this.form = {
detailId: undefined,
stockIoId: undefined,
warehouseId: undefined,
fromWarehouseId: undefined,
itemType: undefined,
itemId: undefined,
quantity: undefined,
unit: undefined,
batchNo: undefined,
remark: undefined
};
this.unitDisabled = false; // 新增:重置单位输入框为可编辑
this.resetForm("form");
},
cancel() {
this.open = false;
this.reset();
},
handleExport() {
this.download('wms/stockIoDetail/export', {
...this.queryParams
}, `stockIoDetail_${new Date().getTime()}.xlsx`)
},
getAuditButtonText() {
if (this.stockIo.ioType === 'in') {
return '审核入库';
} else if (this.stockIo.ioType === 'out') {
return '审核出库';
} else if (this.stockIo.ioType === 'transfer') {
return '审核移库';
} else {
return '审核';
}
},
getStatusButtonText() {
if (this.stockIo.ioType === 'in') {
return '提交入库';
} else if (this.stockIo.ioType === 'out') {
return '提交出库';
} else if (this.stockIo.ioType === 'transfer') {
return '提交移库';
} else {
return '提交';
}
},
handleUpdateStatus() {
// 检查是否有明细数据
if (!this.stockIoDetailList || this.stockIoDetailList.length === 0) {
this.$modal.msgError('请先添加明细数据');
return;
}
this.$modal.confirm('确认要提交此出入库单吗?提交后将无法修改明细。').then(() => {
this.statusLoading = true;
updateStockIoStatus(this.stockIo.stockIoId, 1).then(response => {
this.$modal.msgSuccess('状态更新成功');
// 更新主表状态
this.$set(this.stockIo, 'status', 1);
// 通知父组件状态已更新
this.$emit('status-changed', this.stockIo);
}).catch(error => {
console.error('状态更新失败:', error);
this.$modal.msgError('状态更新失败:' + (error.message || '未知错误'));
}).finally(() => {
this.statusLoading = false;
});
}).catch(() => {
// 用户取消状态更新
});
},
handleCancel() {
this.$modal.confirm('确认要撤回此出入库单吗?撤回后将回滚库存数据。').then(() => {
this.cancelLoading = true;
cancelStockIo(this.stockIo.stockIoId).then(response => {
this.$modal.msgSuccess('撤回成功');
// 更新主表状态
this.$set(this.stockIo, 'status', 1);
// 刷新明细列表
this.getList();
// 通知父组件状态已更新
this.$emit('status-changed', this.stockIo);
}).catch(error => {
console.error('撤回失败:', error);
this.$modal.msgError('撤回失败:' + (error.message || '未知错误'));
}).finally(() => {
this.cancelLoading = false;
});
}).catch(() => {
// 用户取消撤回
});
},
refreshStockIoStatus() {
// 刷新主表状态
if (this.stockIo && this.stockIo.stockIoId) {
getStockIo(this.stockIo.stockIoId).then(response => {
const updatedStockIo = response.data;
// 更新主表状态
this.$set(this.stockIo, 'status', updatedStockIo.status);
// 通知父组件状态已更新
this.$emit('status-changed', this.stockIo);
}).catch(error => {
console.error('刷新主表状态失败:', error);
});
}
},
getIoTypeTagType(type) {
if (type === 'in') return 'success';
if (type === 'out') return 'primary';
if (type === 'transfer') return 'warning';
return '';
},
getIoTypeLabel(type) {
if (type === 'in') return '入库';
if (type === 'out') return '出库';
if (type === 'transfer') return '移库';
return type;
},
getBizTypeLabel(type) {
const map = {
purchase: '采购',
sales: '销售',
return: '退货',
relocation: '调拨',
other: '其他'
};
return map[type] || type;
},
getBizTypeTagType(type) {
if (type === 'purchase') return 'success';
if (type === 'sales') return 'primary';
if (type === 'return') return 'warning';
if (type === 'relocation') return 'info';
return 'default';
},
onItemChange(e) {
if (e && e.unit) {
this.form.unit = e.unit;
this.unitDisabled = true;
}
}
}
}
</script>