<template>
|
<div>
|
<div class="search_form" style="margin-bottom: 10px">
|
<div>
|
<span class="search_title ml10">出库日期:</span>
|
<el-date-picker
|
v-model="searchForm.timeStr"
|
type="date"
|
placeholder="请选择日期"
|
value-format="YYYY-MM-DD"
|
format="YYYY-MM-DD"
|
clearable
|
@change="handleQuery"
|
/>
|
<span class="search_title ml10">来源:</span>
|
<el-select
|
v-model="searchForm.recordType"
|
style="width: 240px"
|
placeholder="请选择"
|
clearable
|
>
|
<el-option
|
v-for="item in stockRecordTypeOptions"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value"
|
/>
|
</el-select>
|
<el-button type="primary" @click="handleQuery" style="margin-left: 10px"
|
>搜索</el-button
|
>
|
</div>
|
<div>
|
<el-button type="primary" @click="handleAdd">新增</el-button>
|
<el-button type="primary" @click="handleBatchApprove">审批</el-button>
|
<el-button @click="handleOut">导出</el-button>
|
<el-button type="danger" plain @click="handleDelete">删除</el-button>
|
<el-button type="primary" plain @click="handlePrint">打印</el-button>
|
</div>
|
</div>
|
<div class="table_list">
|
<el-table
|
:data="tableData"
|
border
|
v-loading="tableLoading"
|
@selection-change="handleSelectionChange"
|
:expand-row-keys="expandedRowKeys"
|
:row-key="(row) => row.id"
|
style="width: 100%"
|
height="calc(100vh - 18.5em)"
|
>
|
<el-table-column align="center" type="selection" width="55" />
|
<el-table-column align="center" label="序号" type="index" width="60" />
|
<el-table-column
|
label="出库批次"
|
prop="outboundBatches"
|
min-width="100"
|
show-overflow-tooltip
|
/>
|
<el-table-column
|
label="出库日期"
|
prop="createTime"
|
show-overflow-tooltip
|
/>
|
<el-table-column
|
label="产品大类"
|
prop="productName"
|
show-overflow-tooltip
|
/>
|
<el-table-column label="规格型号" prop="model" show-overflow-tooltip />
|
<el-table-column label="库位" prop="warehouseName" show-overflow-tooltip />
|
<el-table-column label="批号" prop="batchNo" show-overflow-tooltip />
|
<el-table-column label="单位" prop="unit" show-overflow-tooltip />
|
<el-table-column
|
label="出库数量"
|
prop="stockOutNum"
|
show-overflow-tooltip
|
/>
|
<el-table-column label="出库人" prop="createBy" show-overflow-tooltip />
|
<el-table-column label="来源" prop="recordType" show-overflow-tooltip>
|
<template #default="scope">
|
{{ getRecordType(scope.row.recordType) }}
|
</template>
|
</el-table-column>
|
<el-table-column
|
label="审批状态"
|
prop="approvalStatus"
|
show-overflow-tooltip
|
>
|
<template #default="scope">
|
<el-tag
|
:type="getApprovalStatusTagType(scope.row.approvalStatus)"
|
size="small"
|
>
|
{{ getApprovalStatusLabel(scope.row.approvalStatus) }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="操作" width="120" align="center" fixed="right">
|
<template #default="scope">
|
<el-button
|
v-if="scope.row.approvalStatus !== 1 && scope.row.approvalStatus !== '1' && scope.row.approvalStatus !== 'approved' && scope.row.approvalStatus !== 'APPROVED'"
|
link
|
type="primary"
|
size="small"
|
@click="handleEdit(scope.row)">编辑</el-button>
|
<span v-else style="color: #999; font-size: 12px;">已通过</span>
|
</template>
|
</el-table-column>
|
</el-table>
|
<pagination
|
v-show="total > 0"
|
:total="total"
|
layout="total, sizes, prev, pager, next, jumper"
|
:page="page.current"
|
:limit="page.size"
|
@pagination="paginationChange"
|
/>
|
</div>
|
|
<!-- 新增/编辑对话框 -->
|
<el-dialog v-model="dialogVisible"
|
:title="dialogTitle"
|
width="800"
|
@close="closeDialog">
|
<el-form ref="formRef"
|
:model="formState"
|
label-width="140px"
|
label-position="top">
|
<el-form-item label="产品名称"
|
prop="productModelId"
|
:rules="[
|
{
|
required: true,
|
message: '请选择产品',
|
trigger: 'change',
|
}
|
]">
|
<el-button type="primary"
|
@click="showProductSelect = true">
|
{{ formState.productName ? formState.productName : '选择产品' }}
|
</el-button>
|
</el-form-item>
|
<el-form-item label="规格"
|
prop="productModelName">
|
<el-input v-model="formState.productModelName" disabled />
|
</el-form-item>
|
<el-form-item label="单位"
|
prop="unit">
|
<el-input v-model="formState.unit" disabled />
|
</el-form-item>
|
<el-form-item label="仓库"
|
prop="warehouseInfoId"
|
:rules="[
|
{
|
required: true,
|
message: '请选择仓库',
|
trigger: 'change',
|
}
|
]">
|
<el-select v-model="formState.warehouseInfoId"
|
placeholder="请选择仓库"
|
clearable
|
@change="handleWarehouseChange"
|
style="width: 100%">
|
<el-option v-for="warehouse in warehouseList"
|
:key="warehouse.id"
|
:label="warehouse.warehouseName"
|
:value="warehouse.id" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="批号"
|
prop="batchNo"
|
:rules="[
|
{
|
required: true,
|
message: '请选择批号',
|
trigger: 'change',
|
}
|
]">
|
<el-select v-model="formState.batchNo"
|
placeholder="请选择批号"
|
clearable
|
@change="handleBatchNoChange"
|
style="width: 100%">
|
<el-option v-for="batch in batchNoList"
|
:key="batch"
|
:label="batch + ' (库存: ' + (batchNoStockMap[batch] || 0) + ')'"
|
:value="batch" />
|
</el-select>
|
</el-form-item>
|
<el-form-item v-if="formState.batchNo && batchNoStockMap[formState.batchNo] !== undefined"
|
label="当前批号库存"
|
prop="currentStock">
|
<el-input :model-value="batchNoStockMap[formState.batchNo]" disabled />
|
</el-form-item>
|
<el-form-item label="出库数量"
|
prop="qualitity"
|
:rules="[
|
{
|
required: true,
|
message: '请输入出库数量',
|
trigger: 'blur',
|
},
|
{
|
validator: (rule, value, callback) => {
|
if (formState.maxStock > 0 && value > formState.maxStock) {
|
callback('出库数量不能超过当前批号库存 ' + formState.maxStock);
|
} else {
|
callback();
|
}
|
},
|
trigger: 'blur',
|
}
|
]">
|
<el-input-number v-model="formState.qualitity"
|
:step="1"
|
:min="1"
|
:max="formState.maxStock > 0 ? formState.maxStock : undefined"
|
style="width: 100%" />
|
</el-form-item>
|
<el-form-item v-if="isEdit"
|
label="来源"
|
prop="recordType">
|
<el-select v-model="formState.recordType"
|
placeholder="请选择来源"
|
disabled>
|
<el-option v-for="item in stockRecordTypeOptions"
|
:key="item.value"
|
:label="item.label"
|
:value="item.value" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="库存类型"
|
prop="type"
|
:rules="[
|
{
|
required: true,
|
message: '请选择库存类型',
|
trigger: 'change',
|
}
|
]">
|
<el-select v-model="formState.type"
|
placeholder="请选择库存类型"
|
>
|
<el-option label="合格库存"
|
value="qualified" />
|
<el-option label="不合格库存"
|
value="unqualified" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="备注"
|
prop="remark">
|
<el-input v-model="formState.remark"
|
type="textarea" />
|
</el-form-item>
|
</el-form>
|
<!-- 产品选择弹窗 -->
|
<ProductSelectDialog v-model="showProductSelect"
|
@confirm="handleProductSelect"
|
:top-product-parent-id="props.topParentProductId"
|
request-url="/basic/product/pageModelAndQua"
|
single />
|
<template #footer>
|
<div class="dialog-footer">
|
<el-button type="primary" @click="handleSubmit">确认</el-button>
|
<el-button @click="closeDialog">取消</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import pagination from "@/components/PIMTable/Pagination.vue";
|
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
|
import { ref, reactive, toRefs, computed, getCurrentInstance, watch, onMounted } from "vue";
|
import { ElMessageBox, ElMessage } from "element-plus";
|
import useUserStore from "@/store/modules/user";
|
import { getCurrentDate } from "@/utils/index.js";
|
import {
|
getStockOutPage,
|
delPendingStockOut,
|
batchApproveStockOutRecords,
|
updateStockOutRecord,
|
} from "@/api/inventoryManagement/stockOut.js";
|
import {
|
findAllQualifiedStockOutRecordTypeOptions,
|
findAllUnQualifiedStockOutRecordTypeOptions,
|
} from "@/api/basicData/enum.js";
|
import { addStockOutRecordOnly, getStockInventoryByModelId } from "@/api/inventoryManagement/stockInventory.js";
|
import { addUnqualifiedStockOutRecordOnly } from "@/api/inventoryManagement/stockUninventory.js";
|
import { getWarehouseList } from "@/api/inventoryManagement/warehouse.js";
|
import { productModelListByUrl } from "@/api/basicData/productModel.js";
|
|
const userStore = useUserStore();
|
const { proxy } = getCurrentInstance();
|
const tableData = ref([]);
|
const selectedRows = ref([]);
|
const tableLoading = ref(false);
|
// 来源类型选项
|
const stockRecordTypeOptions = ref([]);
|
// 批号列表(从batchNoMaps获取)
|
const batchNoList = ref([]);
|
// 批号库存映射
|
const batchNoStockMap = ref({});
|
// 仓库列表
|
const warehouseList = ref([]);
|
// 原始batchNoMaps数据
|
const rawBatchNoMaps = ref({});
|
const page = reactive({
|
current: 1,
|
size: 100,
|
});
|
const total = ref(0);
|
|
const props = defineProps({
|
type: {
|
type: String,
|
required: true,
|
default: "0",
|
},
|
topParentProductId: {
|
type: [String, Number],
|
default: undefined,
|
},
|
});
|
|
// 打印相关
|
const printPreviewVisible = ref(false);
|
const printData = ref([]);
|
|
// 用户信息表单弹框数据
|
const data = reactive({
|
searchForm: {
|
supplierName: "",
|
timeStr: "",
|
recordType: "",
|
},
|
});
|
const { searchForm } = toRefs(data);
|
|
// 对话框相关
|
const dialogVisible = ref(false);
|
const dialogType = ref('add'); // 'add' 或 'edit'
|
const dialogTitle = computed(() => dialogType.value === 'add' ? '新增出库记录' : '编辑出库记录');
|
const isEdit = computed(() => dialogType.value === 'edit');
|
const formRef = ref();
|
const showProductSelect = ref(false);
|
|
// 表单数据
|
const formState = ref({
|
id: undefined,
|
productId: undefined,
|
productModelId: undefined,
|
productName: "",
|
productModelName: "",
|
unit: "",
|
warehouseInfoId: null, // 仓库ID
|
type: undefined,
|
qualitity: 0,
|
batchNo: null,
|
recordType: "",
|
remark: "",
|
maxStock: 0, // 当前选中批号的最大库存
|
});
|
|
// 批号为空时转为 null
|
watch(
|
() => formState.value.batchNo,
|
val => {
|
if (val === "") {
|
formState.value.batchNo = null;
|
}
|
}
|
);
|
|
// 查询列表
|
/** 搜索按钮操作 */
|
const handleQuery = () => {
|
page.current = 1;
|
getList();
|
};
|
const paginationChange = (obj) => {
|
page.current = obj.page;
|
page.size = obj.limit;
|
getList();
|
};
|
const getList = () => {
|
tableLoading.value = true;
|
getStockOutPage({
|
...searchForm.value,
|
...page,
|
topParentProductId: props.topParentProductId,
|
})
|
.then((res) => {
|
tableLoading.value = false;
|
tableData.value = res.data.records;
|
tableData.value.map((item) => {
|
item.children = [];
|
});
|
total.value = res.data.total;
|
})
|
.catch(() => {
|
tableLoading.value = false;
|
});
|
};
|
|
const getRecordType = (recordType) => {
|
return (
|
stockRecordTypeOptions.value.find((item) => item.value === recordType)
|
?.label || ""
|
);
|
};
|
|
const approvalStatusLabelMap = {
|
0: "待审批",
|
1: "通过",
|
2: "驳回",
|
3: "待确认",
|
pending: "待审批",
|
approved: "通过",
|
rejected: "驳回",
|
PENDING: "待审批",
|
APPROVED: "通过",
|
REJECTED: "驳回",
|
};
|
|
const getApprovalStatusLabel = (status) => {
|
if (status === null || status === undefined || status === "") {
|
return "待审批";
|
}
|
return approvalStatusLabelMap[status] || "待审批";
|
};
|
|
// 通过/驳回固定色;其余(含待审批、空值、未映射但文案为待审批)统一用 warning 预警色
|
const getApprovalStatusTagType = (status) => {
|
if (
|
status === 1 ||
|
status === "1" ||
|
status === "approved" ||
|
status === "APPROVED"
|
)
|
return "success";
|
if (
|
status === 2 ||
|
status === "2" ||
|
status === "rejected" ||
|
status === "REJECTED"
|
)
|
return "danger";
|
return "warning";
|
};
|
|
// 新增
|
const handleAdd = () => {
|
dialogType.value = 'add';
|
resetForm();
|
// 根据当前tab设置默认库存类型
|
formState.value.type = props.type === '0' ? 'qualified' : 'unqualified';
|
dialogVisible.value = true;
|
};
|
|
// 编辑
|
const handleEdit = async (row) => {
|
dialogType.value = 'edit';
|
resetForm();
|
|
// 先加载所有数据,最后再赋值 formState
|
let loadedWarehouseList = [];
|
let loadedBatchNoList = [];
|
let loadedBatchNoStockMap = {};
|
let loadedMaxStock = 0;
|
let loadedRawBatchNoMaps = {};
|
|
// 编辑时加载仓库列表
|
if (row.warehouseInfoId) {
|
const allWarehouses = await loadWarehouseList();
|
const currentWarehouse = allWarehouses.find(w => String(w.id) === String(row.warehouseInfoId));
|
if (currentWarehouse) {
|
loadedWarehouseList = [{
|
id: currentWarehouse.id,
|
warehouseName: currentWarehouse.warehouseName || currentWarehouse.name || currentWarehouse.warehouseCode || `仓库${currentWarehouse.id}`
|
}];
|
}
|
}
|
|
// 编辑时查询产品库存
|
if (row.productModelId) {
|
try {
|
console.log('编辑时查询库存,productModelId:', row.productModelId);
|
console.log('当前row数据:', row);
|
const res = await productModelListByUrl('/basic/product/pageModelAndQua', {
|
id: row.productModelId,
|
page: 1,
|
size: 1
|
});
|
console.log('查询库存接口返回:', res);
|
// 接口直接返回 {records: [], total: ...},没有 data 层和 code
|
const records = res.records || (res.data && res.data.records) || [];
|
if (records.length > 0) {
|
const product = records[0];
|
console.log('产品数据:', product);
|
console.log('batchNoMaps:', product.batchNoMaps);
|
if (product.batchNoMaps && Object.keys(product.batchNoMaps).length > 0) {
|
loadedRawBatchNoMaps = product.batchNoMaps;
|
// 获取所有仓库信息用于反显名称
|
const allWarehouses = await loadWarehouseList();
|
const warehouseMap = {};
|
allWarehouses.forEach(w => {
|
warehouseMap[w.id] = w.warehouseName || w.name || w.warehouseCode || `仓库${w.id}`;
|
});
|
// 构建仓库列表,确保包含当前记录的仓库
|
const warehouseIds = Object.keys(product.batchNoMaps);
|
// 如果当前记录的仓库不在 product.batchNoMaps 中,添加进去
|
if (row.warehouseInfoId && !warehouseIds.some(id => String(id) === String(row.warehouseInfoId))) {
|
warehouseIds.push(String(row.warehouseInfoId));
|
}
|
loadedWarehouseList = warehouseIds.map(warehouseInfoId => ({
|
id: warehouseInfoId,
|
warehouseName: warehouseMap[warehouseInfoId] || `仓库${warehouseInfoId}`
|
}));
|
console.log('当前仓库ID:', row.warehouseInfoId);
|
console.log('该仓库的batchNoMaps:', product.batchNoMaps[row.warehouseInfoId]);
|
// 如果当前有仓库ID,解析该仓库的批号库存(处理类型不匹配问题)
|
let batchArray = null;
|
if (row.warehouseInfoId) {
|
// 尝试多种方式获取批号数据
|
batchArray = product.batchNoMaps[row.warehouseInfoId] ||
|
product.batchNoMaps[String(row.warehouseInfoId)] ||
|
product.batchNoMaps[Number(row.warehouseInfoId)];
|
}
|
if (batchArray) {
|
console.log('batchArray:', batchArray);
|
const batchMap = {};
|
const batches = [];
|
batchArray.forEach(item => {
|
const batchNo = Object.keys(item)[0];
|
const stock = item[batchNo];
|
console.log('批号:', batchNo, '库存:', stock);
|
batches.push(batchNo);
|
batchMap[batchNo] = stock;
|
});
|
loadedBatchNoList = batches;
|
loadedBatchNoStockMap = batchMap;
|
console.log('batchMap:', batchMap);
|
console.log('当前批号:', row.batchNo);
|
// 设置当前批号的库存
|
if (row.batchNo && batchMap[row.batchNo] !== undefined) {
|
loadedMaxStock = batchMap[row.batchNo];
|
console.log('设置maxStock为:', loadedMaxStock);
|
} else {
|
console.log('未找到当前批号的库存');
|
loadedMaxStock = 0;
|
}
|
} else {
|
console.log('未找到当前仓库的batchNoMaps');
|
loadedBatchNoList = row.batchNo ? [row.batchNo] : [];
|
loadedBatchNoStockMap = {};
|
loadedMaxStock = 0;
|
}
|
} else {
|
console.log('产品没有batchNoMaps');
|
loadedBatchNoList = row.batchNo ? [row.batchNo] : [];
|
loadedBatchNoStockMap = {};
|
loadedMaxStock = 0;
|
}
|
} else {
|
console.log('接口返回数据异常:', res);
|
loadedBatchNoList = row.batchNo ? [row.batchNo] : [];
|
loadedBatchNoStockMap = {};
|
loadedMaxStock = 0;
|
}
|
} catch (error) {
|
console.error('查询产品库存失败', error);
|
loadedBatchNoList = row.batchNo ? [row.batchNo] : [];
|
loadedBatchNoStockMap = {};
|
loadedMaxStock = 0;
|
}
|
} else {
|
console.log('没有productModelId');
|
loadedBatchNoList = row.batchNo ? [row.batchNo] : [];
|
loadedBatchNoStockMap = {};
|
loadedMaxStock = 0;
|
}
|
|
// 所有数据加载完成后,一次性赋值
|
warehouseList.value = loadedWarehouseList;
|
batchNoList.value = loadedBatchNoList;
|
batchNoStockMap.value = loadedBatchNoStockMap;
|
rawBatchNoMaps.value = loadedRawBatchNoMaps;
|
|
// 最后赋值 formState,确保仓库列表已经准备好
|
// 注意:将 warehouseInfoId 转换为字符串,确保与 warehouseList 中的 id 类型匹配
|
formState.value = {
|
id: row.id,
|
productId: row.productId,
|
productModelId: row.productModelId,
|
productName: row.productName,
|
productModelName: row.model,
|
unit: row.unit,
|
type: props.type === '0' ? 'qualified' : 'unqualified',
|
qualitity: row.stockOutNum,
|
batchNo: row.batchNo,
|
warehouseInfoId: row.warehouseInfoId != null ? String(row.warehouseInfoId) : null,
|
recordType: row.recordType,
|
remark: row.remark || "",
|
maxStock: loadedMaxStock,
|
};
|
|
// 所有数据加载完成后再显示弹窗
|
dialogVisible.value = true;
|
};
|
|
// 重置表单
|
const resetForm = () => {
|
formState.value = {
|
id: undefined,
|
productId: undefined,
|
productModelId: undefined,
|
productName: "",
|
productModelName: "",
|
unit: "",
|
warehouseInfoId: null,
|
type: undefined,
|
qualitity: 0,
|
batchNo: null,
|
recordType: "",
|
remark: "",
|
maxStock: 0,
|
};
|
warehouseList.value = [];
|
batchNoList.value = [];
|
batchNoStockMap.value = {};
|
rawBatchNoMaps.value = {};
|
};
|
|
// 关闭对话框
|
const closeDialog = () => {
|
dialogVisible.value = false;
|
resetForm();
|
};
|
|
// 加载仓库列表
|
const loadWarehouseList = async () => {
|
try {
|
const res = await getWarehouseList();
|
if (res.code === 200) {
|
return res.data || [];
|
}
|
} catch (error) {
|
console.error('加载仓库列表失败', error);
|
}
|
return [];
|
};
|
|
// 产品选择处理
|
const handleProductSelect = async products => {
|
if (products && products.length > 0) {
|
const product = products[0];
|
formState.value.productId = product.productId;
|
formState.value.productName = product.productName;
|
formState.value.productModelName = product.model;
|
formState.value.productModelId = product.id;
|
formState.value.unit = product.unit;
|
// 解析batchNoMaps数据,格式为:{ 仓库ID: [{批号: 库存}, {批号: 库存}] }
|
warehouseList.value = [];
|
batchNoList.value = [];
|
batchNoStockMap.value = {};
|
rawBatchNoMaps.value = {};
|
formState.value.warehouseInfoId = null;
|
formState.value.batchNo = null;
|
formState.value.maxStock = 0;
|
|
if (product.batchNoMaps && Object.keys(product.batchNoMaps).length > 0) {
|
rawBatchNoMaps.value = product.batchNoMaps;
|
// 获取所有仓库信息用于反显名称
|
const allWarehouses = await loadWarehouseList();
|
const warehouseMap = {};
|
allWarehouses.forEach(w => {
|
warehouseMap[w.id] = w.warehouseName || w.name || w.warehouseCode || `仓库${w.id}`;
|
});
|
// 构建仓库列表
|
warehouseList.value = Object.keys(product.batchNoMaps).map(warehouseInfoId => ({
|
id: warehouseInfoId,
|
warehouseName: warehouseMap[warehouseInfoId] || `仓库${warehouseInfoId}`
|
}));
|
}
|
showProductSelect.value = false;
|
// 触发表单验证更新
|
proxy.$refs["formRef"]?.validateField("productModelId");
|
}
|
};
|
|
// 仓库选择变化处理
|
const handleWarehouseChange = (warehouseInfoId) => {
|
batchNoList.value = [];
|
batchNoStockMap.value = {};
|
formState.value.batchNo = null;
|
formState.value.maxStock = 0;
|
|
if (warehouseInfoId && rawBatchNoMaps.value[warehouseInfoId]) {
|
// 解析该仓库下的批号数据,格式为:[{批号: 库存}, {批号: 库存}]
|
const batchArray = rawBatchNoMaps.value[warehouseInfoId];
|
const batchMap = {};
|
const batches = [];
|
|
batchArray.forEach(item => {
|
const batchNo = Object.keys(item)[0];
|
const stock = item[batchNo];
|
batches.push(batchNo);
|
batchMap[batchNo] = stock;
|
});
|
|
batchNoList.value = batches;
|
batchNoStockMap.value = batchMap;
|
}
|
};
|
|
// 批号选择变化处理
|
const handleBatchNoChange = (batchNo) => {
|
if (batchNo && batchNoStockMap.value[batchNo]) {
|
formState.value.maxStock = batchNoStockMap.value[batchNo];
|
// 如果当前出库数量超过最大库存,自动调整为最大库存
|
if (formState.value.qualitity > formState.value.maxStock) {
|
formState.value.qualitity = formState.value.maxStock;
|
}
|
} else {
|
formState.value.maxStock = 0;
|
}
|
};
|
|
// 提交表单
|
const handleSubmit = () => {
|
proxy.$refs["formRef"].validate(valid => {
|
if (valid) {
|
// 验证是否选择了产品
|
if (!formState.value.productModelId) {
|
ElMessage.error("请选择产品");
|
return;
|
}
|
|
if (dialogType.value === 'add') {
|
submitAdd();
|
} else {
|
submitEdit();
|
}
|
}
|
});
|
};
|
|
// 提交新增
|
const submitAdd = () => {
|
const params = { ...formState.value };
|
|
if (formState.value.type === "qualified") {
|
addStockOutRecordOnly(params).then(res => {
|
ElMessage.success("新增成功");
|
closeDialog();
|
getList();
|
}).catch(() => {
|
ElMessage.error("新增失败");
|
});
|
} else {
|
addUnqualifiedStockOutRecordOnly(params).then(res => {
|
ElMessage.success("新增成功");
|
closeDialog();
|
getList();
|
}).catch(() => {
|
ElMessage.error("新增失败");
|
});
|
}
|
};
|
|
// 提交编辑
|
const submitEdit = () => {
|
const params = {
|
productId: formState.value.productId,
|
productModelId: formState.value.productModelId,
|
productName: formState.value.productName,
|
model: formState.value.productModelName,
|
unit: formState.value.unit,
|
batchNo: formState.value.batchNo,
|
stockOutNum: formState.value.qualitity,
|
recordType: formState.value.recordType,
|
remark: formState.value.remark,
|
};
|
|
updateStockOutRecord(formState.value.id, params).then(() => {
|
ElMessage.success("编辑成功");
|
closeDialog();
|
getList();
|
}).catch(() => {
|
ElMessage.error("编辑失败");
|
});
|
};
|
|
// 获取来源类型选项
|
const fetchStockRecordTypeOptions = () => {
|
if (props.type === "0") {
|
findAllQualifiedStockOutRecordTypeOptions().then((res) => {
|
stockRecordTypeOptions.value = res.data;
|
});
|
return;
|
}
|
findAllUnQualifiedStockOutRecordTypeOptions().then((res) => {
|
stockRecordTypeOptions.value = res.data;
|
});
|
};
|
|
// 表格选择数据
|
const handleSelectionChange = (selection) => {
|
// 过滤掉子数据
|
selectedRows.value = selection.filter((item) => item.id);
|
console.log("selection", selectedRows.value);
|
};
|
const expandedRowKeys = ref([]);
|
|
const handleBatchApprove = () => {
|
if (selectedRows.value.length === 0) {
|
proxy.$modal.msgWarning("请选择数据");
|
return;
|
}
|
const ids = selectedRows.value.map((item) => item.id);
|
ElMessageBox.confirm("请选择审批结果", "审批", {
|
confirmButtonText: "通过",
|
cancelButtonText: "驳回",
|
type: "warning",
|
distinguishCancelAndClose: true,
|
})
|
.then(() => {
|
batchApproveStockOutRecords({ ids, approvalStatus: 1 })
|
.then(() => {
|
proxy.$modal.msgSuccess("审批通过成功");
|
getList();
|
})
|
.catch(() => {
|
proxy.$modal.msgError("审批通过失败");
|
});
|
})
|
.catch((action) => {
|
if (action === "cancel") {
|
batchApproveStockOutRecords({ ids, approvalStatus: 2 })
|
.then(() => {
|
proxy.$modal.msgSuccess("审批驳回成功");
|
getList();
|
})
|
.catch(() => {
|
proxy.$modal.msgError("审批驳回失败");
|
});
|
return;
|
}
|
proxy.$modal.msg("已取消");
|
});
|
};
|
|
// 导出
|
const handleOut = () => {
|
ElMessageBox.confirm("是否确认导出?", "导出", {
|
confirmButtonText: "确认",
|
cancelButtonText: "取消",
|
type: "warning",
|
})
|
.then(() => {
|
proxy.download(
|
"/stockOutRecord/exportStockOutRecord",
|
{ type: props.type },
|
props.type === "0" ? "合格出库台账.xlsx" : "不合格出库台账.xlsx"
|
);
|
})
|
.catch(() => {
|
proxy.$modal.msg("已取消");
|
});
|
};
|
|
// 删除
|
const handleDelete = () => {
|
let ids = [];
|
if (selectedRows.value.length > 0) {
|
ids = selectedRows.value.map((item) => item.id);
|
} else {
|
proxy.$modal.msgWarning("请选择数据");
|
return;
|
}
|
ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
|
confirmButtonText: "确认",
|
cancelButtonText: "取消",
|
type: "warning",
|
})
|
.then(() => {
|
delPendingStockOut(ids).then((res) => {
|
proxy.$modal.msgSuccess("删除成功");
|
getList();
|
});
|
})
|
.catch(() => {
|
proxy.$modal.msg("已取消");
|
});
|
};
|
|
// 打印功能
|
const handlePrint = () => {
|
if (selectedRows.value.length === 0) {
|
proxy.$modal.msgWarning("请选择要打印的数据");
|
return;
|
}
|
printData.value = [...selectedRows.value];
|
console.log("打印数据:", printData.value);
|
printPreviewVisible.value = true;
|
};
|
|
// 执行打印
|
const executePrint = () => {
|
console.log("开始执行打印,数据条数:", printData.value.length);
|
console.log("打印数据:", printData.value);
|
|
// 创建一个新的打印窗口
|
const printWindow = window.open("", "_blank", "width=800,height=600");
|
|
// 构建打印内容
|
let printContent = `
|
<!DOCTYPE html>
|
<html>
|
<head>
|
<meta charset="UTF-8">
|
<title>打印预览</title>
|
<style>
|
body {
|
margin: 0;
|
padding: 0;
|
font-family: "SimSun", serif;
|
background: white;
|
}
|
.print-page {
|
width: 200mm;
|
height: 75mm;
|
padding: 10mm;
|
padding-left: 20mm;
|
background: white;
|
box-sizing: border-box;
|
page-break-after: always;
|
page-break-inside: avoid;
|
}
|
.print-page:last-child {
|
page-break-after: avoid;
|
}
|
.delivery-note {
|
width: 100%;
|
height: 100%;
|
font-size: 12px;
|
line-height: 1.2;
|
display: flex;
|
flex-direction: column;
|
color: #000;
|
}
|
.header {
|
text-align: center;
|
margin-bottom: 8px;
|
}
|
.company-name {
|
font-size: 18px;
|
font-weight: bold;
|
margin-bottom: 4px;
|
}
|
.document-title {
|
font-size: 16px;
|
font-weight: bold;
|
}
|
.info-section {
|
margin-bottom: 8px;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
}
|
.info-row {
|
line-height: 20px;
|
}
|
.label {
|
font-weight: bold;
|
width: 60px;
|
font-size: 12px;
|
}
|
.value {
|
margin-right: 20px;
|
min-width: 80px;
|
font-size: 12px;
|
}
|
.table-section {
|
margin-bottom: 40px;
|
// flex: 0.6;
|
}
|
.product-table {
|
width: 100%;
|
border-collapse: collapse;
|
border: 1px solid #000;
|
}
|
.product-table th, .product-table td {
|
border: 1px solid #000;
|
padding: 6px;
|
text-align: center;
|
font-size: 12px;
|
line-height: 1.4;
|
}
|
.product-table th {
|
font-weight: bold;
|
}
|
.total-value {
|
font-weight: bold;
|
}
|
.footer-section {
|
margin-top: auto;
|
}
|
.footer-row {
|
display: flex;
|
margin-bottom: 3px;
|
line-height: 22px;
|
justify-content: space-between;
|
}
|
.footer-item {
|
display: flex;
|
margin-right: 20px;
|
}
|
.footer-item .label {
|
font-weight: bold;
|
width: 80px;
|
font-size: 12px;
|
}
|
.footer-item .value {
|
min-width: 80px;
|
font-size: 12px;
|
}
|
.address-item .address-value {
|
min-width: 200px;
|
}
|
@media print {
|
body {
|
margin: 0;
|
padding: 0;
|
}
|
.print-page {
|
margin: 0;
|
padding: 10mm;
|
/* padding-left: 20mm; */
|
page-break-inside: avoid;
|
page-break-after: always;
|
}
|
.print-page:last-child {
|
page-break-after: avoid;
|
}
|
}
|
</style>
|
</head>
|
<body>
|
`;
|
|
// 为每条数据生成打印页面
|
printData.value.forEach((item, index) => {
|
printContent += `
|
<div class="print-page">
|
<div class="delivery-note">
|
<div class="header">
|
<div class="document-title">零售发货单</div>
|
</div>
|
|
<div class="info-section">
|
<div class="info-row">
|
<div>
|
<span class="label">发货日期:</span>
|
<span class="value">${formatDate(item.createTime)}</span>
|
</div>
|
<div>
|
<span class="label">客户名称:</span>
|
<span class="value">${item.supplierName}</span>
|
</div>
|
</div>
|
<div class="info-row">
|
<span class="label">单号:</span>
|
<span class="value">${item.code || ""}</span>
|
</div>
|
</div>
|
|
<div class="table-section">
|
<table class="product-table">
|
<thead>
|
<tr>
|
<th>产品名称</th>
|
<th>规格型号</th>
|
<th>单位</th>
|
<th>单价</th>
|
<th>零售数量</th>
|
<th>零售金额</th>
|
</tr>
|
</thead>
|
<tbody>
|
<tr>
|
<td>${item.productName || "砂灰砖"}</td>
|
<td>${item.model || "标准"}</td>
|
<td>${item.unit || "块"}</td>
|
<td>${item.taxInclusiveUnitPrice || "0"}</td>
|
<td>${item.inboundNum || "2000"}</td>
|
<td>${item.taxInclusiveTotalPrice || "0"}</td>
|
</tr>
|
</tbody>
|
<tfoot>
|
<tr>
|
<td class="label">合计</td>
|
<td class="total-value"></td>
|
<td class="total-value"></td>
|
<td class="total-value"></td>
|
<td class="total-value">${item.inboundNum || "2000"}</td>
|
<td class="total-value">${
|
item.taxInclusiveTotalPrice || "0"
|
}</td>
|
</tr>
|
</tfoot>
|
</table>
|
</div>
|
|
<div class="footer-section">
|
<div class="footer-row">
|
<div class="footer-item">
|
<span class="label">收货电话:</span>
|
<span class="value"></span>
|
</div>
|
<div class="footer-item">
|
<span class="label">收货人:</span>
|
<span class="value"></span>
|
</div>
|
<div class="footer-item address-item">
|
<span class="label">收货地址:</span>
|
<span class="value address-value"></span>
|
</div>
|
</div>
|
<div class="footer-row">
|
<div class="footer-item">
|
<span class="label">操作员:</span>
|
<span class="value">${userStore.nickName || "撕开前"}</span>
|
</div>
|
<div class="footer-item">
|
<span class="label">打印日期:</span>
|
<span class="value">${formatDateTime(new Date())}</span>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
`;
|
});
|
|
printContent += `
|
</body>
|
</html>
|
`;
|
|
// 写入内容到新窗口
|
printWindow.document.write(printContent);
|
printWindow.document.close();
|
|
// 等待内容加载完成后打印
|
printWindow.onload = () => {
|
setTimeout(() => {
|
printWindow.print();
|
printWindow.close();
|
printPreviewVisible.value = false;
|
}, 500);
|
};
|
};
|
|
// 格式化日期
|
const formatDate = (dateString) => {
|
if (!dateString) return getCurrentDate();
|
const date = new Date(dateString);
|
const year = date.getFullYear();
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
const day = String(date.getDate()).padStart(2, "0");
|
return `${year}/${month}/${day}`;
|
};
|
|
// 格式化日期时间
|
const formatDateTime = (date) => {
|
const year = date.getFullYear();
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
const day = String(date.getDate()).padStart(2, "0");
|
const hours = String(date.getHours()).padStart(2, "0");
|
const minutes = String(date.getMinutes()).padStart(2, "0");
|
const seconds = String(date.getSeconds()).padStart(2, "0");
|
return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
|
};
|
onMounted(() => {
|
getList();
|
fetchStockRecordTypeOptions();
|
});
|
|
watch(
|
() => props.topParentProductId,
|
() => {
|
page.current = 1;
|
getList();
|
}
|
);
|
</script>
|
|
<style scoped lang="scss">
|
.print-preview-dialog {
|
.el-dialog__body {
|
padding: 0;
|
max-height: 80vh;
|
overflow-y: auto;
|
}
|
}
|
|
.print-preview-container {
|
.print-preview-header {
|
padding: 15px;
|
border-bottom: 1px solid #e4e7ed;
|
text-align: center;
|
|
.el-button {
|
margin: 0 10px;
|
}
|
}
|
|
.print-preview-content {
|
padding: 20px;
|
background-color: #f5f5f5;
|
min-height: 400px;
|
}
|
}
|
|
.print-page {
|
width: 220mm;
|
height: 90mm;
|
padding: 10mm;
|
margin: 0 auto;
|
background: white;
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
margin-bottom: 10px;
|
box-sizing: border-box;
|
}
|
|
.delivery-note {
|
width: 100%;
|
height: 100%;
|
font-family: "SimSun", serif;
|
font-size: 10px;
|
line-height: 1.2;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.header {
|
text-align: center;
|
margin-bottom: 8px;
|
|
.company-name {
|
font-size: 18px;
|
font-weight: bold;
|
margin-bottom: 4px;
|
}
|
|
.document-title {
|
font-size: 16px;
|
font-weight: bold;
|
}
|
}
|
|
.info-section {
|
margin-bottom: 8px;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
|
.info-row {
|
line-height: 20px;
|
|
.label {
|
font-weight: bold;
|
width: 60px;
|
font-size: 14px;
|
}
|
|
.value {
|
margin-right: 20px;
|
min-width: 80px;
|
font-size: 14px;
|
}
|
}
|
}
|
|
.table-section {
|
margin-bottom: 4px;
|
flex: 1;
|
|
.product-table {
|
width: 100%;
|
border-collapse: collapse;
|
border: 1px solid #000;
|
|
th,
|
td {
|
border: 1px solid #000;
|
padding: 6px;
|
text-align: center;
|
font-size: 14px;
|
line-height: 1.4;
|
}
|
|
th {
|
font-weight: bold;
|
}
|
|
.total-label {
|
text-align: right;
|
font-weight: bold;
|
}
|
|
.total-value {
|
font-weight: bold;
|
}
|
}
|
}
|
|
.footer-section {
|
.footer-row {
|
display: flex;
|
margin-bottom: 3px;
|
line-height: 20px;
|
justify-content: space-between;
|
|
.footer-item {
|
display: flex;
|
margin-right: 20px;
|
|
.label {
|
font-weight: bold;
|
width: 80px;
|
font-size: 14px;
|
}
|
|
.value {
|
min-width: 80px;
|
font-size: 14px;
|
}
|
|
&.address-item {
|
.address-value {
|
min-width: 200px;
|
}
|
}
|
}
|
}
|
}
|
|
@media print {
|
.app-container {
|
display: none;
|
}
|
|
.print-page {
|
box-shadow: none;
|
margin: 0;
|
padding: 10mm;
|
padding-left: 20mm;
|
page-break-inside: avoid;
|
page-break-after: always;
|
}
|
.print-page:last-child {
|
page-break-after: avoid;
|
}
|
}
|
</style>
|