bonus-ui/src/views/inventoryCount/total/shop.vue

823 lines
25 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-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="90px">
<!-- 库存日期 -->
<el-form-item label="库存日期">
<el-date-picker
v-model="dateRange"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
style="width: 240px"
:picker-options="pickerOptions">
</el-date-picker>
</el-form-item>
<!-- 货品类别 - 级联多选 -->
<el-form-item label="货品类别">
<el-cascader
v-model="selectedCategories"
:options="goodsCategoryOptions"
:show-all-levels="false"
:props="categoryProps"
clearable
filterable
multiple
collapse-tags
placeholder="请选择货品类别"
style="width: 200px;"
@change="handleCategoryChange">
</el-cascader>
</el-form-item>
<!-- 所属区域 -->
<el-form-item label="所属区域">
<el-cascader
v-model="selectedOrg"
:options="orgOptions"
:show-all-levels="false"
clearable
filterable
placeholder="请选择所属区域"
:props="{
checkStrictly: true,
expandTrigger: 'hover',
value: 'orgId',
label: 'orgName',
children: 'children'
}"
style="width: 200px;"
@change="handleAreaChange">
</el-cascader>
</el-form-item>
<!-- 货品仓库 -->
<el-form-item label="货品仓库" prop="warehouseId">
<el-select
v-model="queryParams.warehouseIdList"
multiple
placeholder="请选择货品仓库"
clearable
style="width: 200px"
@change="handleWarehouseChange">
<el-option
v-for="item in wareHouseOptions"
:key="item.warehouseId"
:label="item.warehouseName"
:value="item.warehouseId">
</el-option>
</el-select>
</el-form-item>
<!-- 货品名称 -->
<el-form-item label="货品名称" prop="goodsName">
<el-input
v-model="queryParams.materialName"
placeholder="请输入货品名称"
maxlength="100"
clearable
style="width: 200px"/>
</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-form-item>
</el-form>
<!-- 操作按钮栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
>导出</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-printer"
size="mini"
@click="handlePrint"
>打印</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 数据表格 - 只修改字段名称以匹配后端实体 -->
<el-table
v-loading="loading"
:data="tableListData"
border
show-summary
:summary-method="getSummaries">
<el-table-column label="序号" align="center" width="60" type="index">
<template slot-scope="scope">
<span>{{(queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1}}</span>
</template>
</el-table-column>
<!-- 货品编码 -->
<el-table-column label="货品编码" align="center" prop="materialCode" width="120" :show-overflow-tooltip="true"/>
<!-- 货品名称 -->
<el-table-column label="货品名称" align="center" prop="materialName" width="150" :show-overflow-tooltip="true"/>
<!-- 货品类别 -->
<el-table-column label="货品类别" align="center" prop="categoryName" width="120" :show-overflow-tooltip="true"/>
<!-- 计量单位 -->
<el-table-column label="计量单位" align="center" prop="unitName" width="100"/>
<!-- 货品规格 -->
<el-table-column label="货品规格" align="center" prop="size" width="120" :show-overflow-tooltip="true"/>
<!-- 所属区域 -->
<el-table-column label="所属区域" align="center" prop="areaName" width="120" :show-overflow-tooltip="true"/>
<!-- 所属仓库 -->
<el-table-column label="所属仓库" align="center" prop="warehouseName" width="120" :show-overflow-tooltip="true"/>
<!-- 入库数量 -->
<el-table-column label="入库数量" align="center" prop="intoTotalNum" width="100">
<template slot-scope="scope">
<span>{{ formatNumber(scope.row.intoTotalNum) }}</span>
</template>
</el-table-column>
<!-- 入库金额(元) -->
<el-table-column label="入库金额(元)" align="center" prop="intoTotalAmount" width="120">
<template slot-scope="scope">
<span>{{ formatMoney((scope.row.intoTotalAmount || 0) / 100) }}</span>
</template>
</el-table-column>
<!-- 入库总数量 -->
<el-table-column label="入库总数量" align="center" prop="intoTotalNum" width="120">
<template slot-scope="scope">
<span>{{ formatNumber(scope.row.intoTotalNum) }}</span>
</template>
</el-table-column>
<!-- 入库总金额(元) -->
<el-table-column label="入库总金额(元)" align="center" prop="intoTotalAmount" width="130">
<template slot-scope="scope">
<span>{{ formatMoney((scope.row.intoTotalAmount || 0) / 100) }}</span>
</template>
</el-table-column>
<!-- 出库数量 -->
<el-table-column label="出库数量" align="center" prop="outTotalNum" width="100">
<template slot-scope="scope">
<span>{{ formatNumber(scope.row.outTotalNum) }}</span>
</template>
</el-table-column>
<!-- 出库金额(元) -->
<el-table-column label="出库金额(元)" align="center" prop="outTotalAmount" width="120">
<template slot-scope="scope">
<span>{{ formatMoney((scope.row.outTotalAmount || 0) / 100) }}</span>
</template>
</el-table-column>
<!-- 出库总数量 -->
<el-table-column label="出库总数量" align="center" prop="outTotalNum" width="120">
<template slot-scope="scope">
<span>{{ formatNumber(scope.row.outTotalNum) }}</span>
</template>
</el-table-column>
<!-- 出库总金额(元) -->
<el-table-column label="出库总金额(元)" align="center" prop="outTotalAmount" width="130">
<template slot-scope="scope">
<span>{{ formatMoney((scope.row.outTotalAmount || 0) / 100) }}</span>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</template>
<script>
// 导入区域树API
import {getTreeArea} from "@/api/canteen/canteenRecord";
// 导入库存入库相关API
import {
listInventoryIntoDetail,
exportInventoryIntoDetail,
getWarehouseList,
getGoodsCategoryDict
} from "@/api/report/inventoryhz";
export default {
name: "InventoryIntoDetail",
dicts: [],
data() {
return {
// 合计数据
summaryData: null,
// 区域树选项
orgOptions: [],
selectedOrg: [], // 选中的区域
// 货品类别相关数据
goodsCategoryOptions: [], // 货品类别树形数据
selectedCategories: [], // 选中的货品类别路径数组
categoryProps: {
expandTrigger: 'hover',
value: 'id',
label: 'dictLabel',
children: 'children',
emitPath: true,
multiple: true
},
// 仓库下拉选项
wareHouseOptions: [],
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 表格数据
tableListData: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
recordId: null, // 单据编号
categoryIdList: [], // 货品类别ID列表包含所有子节点
warehouseIdList: [], // 仓库ID列表
goodsName: null, // 货品名称
areaIdList: [], // 区域ID列表
startDate: null, // 开始日期
endDate: null // 结束日期
},
// 日期范围
dateRange: this.defaultDateRange(),
// 日期选择器快捷选项
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick: (picker) => {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 6);
picker.$emit('pick', [this.formatDate(start), this.formatDate(end)]);
}
}, {
text: '最近一个月',
onClick: (picker) => {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [this.formatDate(start), this.formatDate(end)]);
}
}, {
text: '最近三个月',
onClick: (picker) => {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 91);
picker.$emit('pick', [this.formatDate(start), this.formatDate(end)]);
}
}]
}
};
},
created() {
// 初始化数据
this.getTree(); // 获取区域树
this.getWareHouseData(); // 获取仓库列表
this.getGoodsCategoryData(); // 获取货品类别树
this.getList(); // 获取列表数据
},
methods: {
/**
* 自定义合计计算方法
*/
getSummaries(param) {
const { columns, data } = param;
const sums = [];
columns.forEach((column, index) => {
if (index === 0) {
// 第一列显示"合计"
sums[index] = '合计';
return;
}
// 根据列名判断需要合计的字段
const columnProp = column.property;
if (this.summaryData) {
switch(columnProp) {
case 'intoTotalNum':
sums[index] = this.formatNumber(this.summaryData.intoTotalNum || 0);
break;
case 'intoTotalAmount':
sums[index] = this.formatMoney((this.summaryData.intoTotalAmount || 0) / 100);
break;
case 'outTotalNum':
sums[index] = this.formatNumber(this.summaryData.outTotalNum || 0);
break;
case 'outTotalAmount':
sums[index] = this.formatMoney((this.summaryData.outTotalAmount || 0) / 100);
break;
default:
// 其他列显示空
sums[index] = '';
break;
}
} else {
sums[index] = '';
}
});
return sums;
},
// 保持其他方法不变
getGoodsCategoryData() {
getGoodsCategoryDict().then((response) => {
const data = response.data || response || [];
console.log('获取到的货品类别数据:', data);
// 如果后端返回的是平铺数据,需要转换为树形结构
if (data.length > 0 && !data[0].children) {
// 使用我们自己的构建树方法
this.goodsCategoryOptions = this.buildCategoryTree(data);
} else {
// 如果已经是树形结构,直接使用
this.goodsCategoryOptions = data;
}
console.log('转换后的货品类别树:', this.goodsCategoryOptions);
}).catch(error => {
console.error('获取货品类别失败:', error);
this.goodsCategoryOptions = [];
});
},
buildCategoryTree(list) {
// 创建根节点(原料和商品)
const tree = [];
// 1. 原料根节点
const materialRoot = {
id: 25595457052610560,
dictLabel: "原料",
parentId: -1,
children: []
};
// 2. 商品根节点
const goodsRoot = {
id: 65595542415085568,
dictLabel: "商品",
parentId: -1,
children: []
};
// 将数据按 parent_id 分组
const map = {};
list.forEach(item => {
if (!map[item.id]) {
map[item.id] = { ...item, children: [] };
}
});
// 构建树结构
list.forEach(item => {
if (item.parentId === 25595457052610560) {
const child = { ...item, children: [] };
materialRoot.children.push(child);
map[item.id] = child;
} else if (item.parentId === 65595542415085568) {
const child = { ...item, children: [] };
goodsRoot.children.push(child);
map[item.id] = child;
} else if (item.parentId !== -1 && map[item.parentId]) {
const child = { ...item, children: [] };
if (!map[item.parentId].children) {
map[item.parentId].children = [];
}
map[item.parentId].children.push(child);
map[item.id] = child;
}
});
tree.push(materialRoot, goodsRoot);
return tree;
},
handleCategoryChange(values) {
if (!values || values.length === 0) {
this.queryParams.categoryIdList = [];
return;
}
let allCategoryIds = [];
values.forEach(path => {
const selectedId = path[path.length - 1];
const node = this.findCategoryNodeById(selectedId, this.goodsCategoryOptions);
if (node) {
allCategoryIds.push(...this.getAllChildCategoryIds(node));
}
});
this.queryParams.categoryIdList = Array.from(new Set(allCategoryIds));
console.log("最终传给后台的 categoryIdList =", this.queryParams.categoryIdList);
},
findCategoryNodeById(id, options) {
for (const node of options) {
if (node.id === id) {
return node;
}
if (node.children && node.children.length > 0) {
const found = this.findCategoryNodeById(id, node.children);
if (found) return found;
}
}
return null;
},
getAllChildCategoryIds(node) {
if (!node) return [];
let ids = [node.id];
if (node.children && node.children.length > 0) {
node.children.forEach(child => {
ids = ids.concat(this.getAllChildCategoryIds(child));
});
}
return ids;
},
getSelectedOrgIds() {
if (!this.selectedOrg || this.selectedOrg.length === 0) return [];
const lastValue = this.selectedOrg[this.selectedOrg.length - 1];
// 找到对应节点
let stack = [...this.orgOptions];
let node = null;
for (let val of this.selectedOrg) {
node = stack.find(item => item.orgId === val);
if (!node) break;
stack = node.children || [];
}
if (node) {
return this.getAllChildIds(node, this.orgOptions);
}
return [lastValue];
},
getAllChildIds(node, options) {
let ids = [node.orgId]; // 当前节点
if (node.children && node.children.length > 0) {
node.children.forEach(child => {
ids = ids.concat(this.getAllChildIds(child, options)); // 递归获取子节点
});
}
return ids;
},
getTree() {
getTreeArea().then(res => {
this.orgOptions = res || [];
});
},
handleAreaChange() {
// 区域变化时,重新加载仓库列表
this.queryParams.warehouseIdList = [];
this.getWareHouseData();
},
handleWarehouseChange() {
// 可以在这里添加其他逻辑
},
getWareHouseData() {
const areaIdList = this.getSelectedOrgIds();
console.log("选中的 orgId 列表:", areaIdList, Array.isArray(areaIdList));
if (!areaIdList || !areaIdList.length) {
this.wareHouseOptions = [];
return;
}
getWarehouseList({
areaIdList: areaIdList.join(',')
}).then(res => {
this.wareHouseOptions = res.data || [];
});
},
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
resetQuery() {
this.dateRange = this.defaultDateRange();
this.selectedOrg = [];
this.selectedCategories = [];
this.summaryData = null;
this.queryParams = {
pageNum: 1,
pageSize: 10,
recordId: null,
categoryIdList: [],
warehouseIdList: [],
goodsName: null,
areaIdList: [],
startDate: null,
endDate: null
};
this.resetForm("queryForm");
this.getWareHouseData();
this.handleQuery();
},
getList() {
this.loading = true;
// 获取选中的区域ID列表
this.queryParams.areaIdList = this.getSelectedOrgIds();
// 处理日期范围
if (this.dateRange && this.dateRange.length === 2) {
this.queryParams.startDate = this.dateRange[0];
this.queryParams.endDate = this.dateRange[1];
} else {
this.queryParams.startDate = null;
this.queryParams.endDate = null;
}
// 调用API查询
listInventoryIntoDetail(this.queryParams).then(response => {
const data = response.data || response;
this.tableListData = data.rows || [];
this.total = Number(data.total) || 0;
this.summaryData = data.summary || null;
console.log('合计数据:', this.summaryData);
this.loading = false;
}).catch(() => {
this.loading = false;
});
},
handleExport() {
// 处理日期范围
let startDate = '';
let endDate = '';
if (this.dateRange && this.dateRange.length === 2) {
const start = this.dateRange[0];
const end = this.dateRange[1];
startDate = start + ' 00:00:00';
endDate = end + ' 23:59:59';
}
// 构建参数
let param = {
pageNum: this.queryParams.pageNum,
pageSize: this.queryParams.pageSize,
recordId: this.queryParams.recordId,
categoryIdList: this.queryParams.categoryIdList,
warehouseIdList: this.queryParams.warehouseIdList,
materialName: this.queryParams.materialName,
areaIdList: this.getSelectedOrgIds(),
startDate: startDate,
endDate: endDate
};
// 使用download方法参考另一个模块
this.$modal.confirm('是否确认导出货品汇总明细数据项?').then(() => {
this.loading = true;
// 使用download方法
this.download(
'/smart-canteen/drp/inventoryIntohz/goods/summary/export',
param,
`货品库存明细_${new Date().getTime()}.xlsx`
).then(() => {
this.loading = false;
this.$modal.msgSuccess("导出成功");
}).catch(error => {
this.loading = false;
console.error('导出失败:', error);
this.$modal.msgError("导出失败");
});
}).catch(() => {
// 用户取消
});
},
handlePrint() {
const printData = {
title: '库存入库明细表',
columns: [
{ prop: 'materialCode', label: '货品编码', width: 120 },
{ prop: 'materialName', label: '货品名称', width: 150 },
{ prop: 'categoryName', label: '货品类别', width: 120 },
{ prop: 'unitName', label: '计量单位', width: 100 },
{ prop: 'size', label: '货品规格', width: 120 },
{ prop: 'areaName', label: '所属区域', width: 120 },
{ prop: 'warehouseName', label: '所属仓库', width: 120 },
{ prop: 'intoTotalNum', label: '入库数量', width: 100 },
{ prop: 'intoTotalAmount', label: '入库金额(元)', width: 120 },
{ prop: 'intoTotalNum', label: '入库总数量', width: 120 },
{ prop: 'intoTotalAmount', label: '入库总金额(元)', width: 130 },
{ prop: 'outTotalNum', label: '出库数量', width: 100 },
{ prop: 'outTotalAmount', label: '出库金额(元)', width: 120 },
{ prop: 'outTotalNum', label: '出库总数量', width: 120 },
{ prop: 'outTotalAmount', label: '出库总金额(元)', width: 130 }
],
data: this.tableListData,
queryParams: this.queryParams
};
const printWindow = window.open('', '_blank');
const printContent = this.generatePrintHTML(printData);
printWindow.document.write(printContent);
printWindow.document.close();
printWindow.focus();
setTimeout(() => {
printWindow.print();
printWindow.close();
}, 500);
},
generatePrintHTML(printData) {
let html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${printData.title}</title>
<style>
body { font-family: "Microsoft YaHei", Arial, sans-serif; margin: 20px; }
h2 { text-align: center; margin-bottom: 20px; }
table { width: 100%; border-collapse: collapse; font-size: 12px; }
th, td { border: 1px solid #000; padding: 8px; text-align: center; }
th { background-color: #f0f0f0; font-weight: bold; }
.print-info { margin-bottom: 20px; font-size: 12px; }
@media print {
@page { size: landscape; margin: 10mm; }
}
</style>
</head>
<body>
<h2>${printData.title}</h2>
<div class="print-info">
<p>打印时间${new Date().toLocaleString()}</p>
<p> ${printData.data.length} 条记录</p>
</div>
<table>
<thead>
<tr>
`;
printData.columns.forEach(col => {
html += `<th>${col.label}</th>`;
});
html += `
</tr>
</thead>
<tbody>
`;
printData.data.forEach(row => {
html += '<tr>';
printData.columns.forEach(col => {
let value = row[col.prop] || '-';
if (col.prop === 'intoTotalAmount' || col.prop === 'outTotalAmount') {
value = this.formatMoney((value || 0) / 100);
} else if (col.prop === 'intoTotalNum' || col.prop === 'outTotalNum') {
value = this.formatNumber(value);
}
html += `<td>${value}</td>`;
});
html += '</tr>';
});
html += `
</tbody>
</table>
</body>
</html>
`;
return html;
},
defaultDateRange() {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 30 * 24 * 60 * 60 * 1000);
return [this.formatDate(start), this.formatDate(end)];
},
formatDate(date) {
if (!date) return '';
const d = new Date(date);
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
},
formatDateTime(date) {
if (!date) return '';
const d = new Date(date);
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
const hours = String(d.getHours()).padStart(2, '0');
const minutes = String(d.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}`;
},
formatMoney(value) {
if (value === null || value === undefined || value === '') return '0.00';
return Number(value).toFixed(2);
},
formatNumber(value) {
if (value === null || value === undefined || value === '') return '0';
return Number(value);
}
}
};
</script>
<style scoped>
.app-container {
padding: 20px;
}
/* 合计行样式增强 */
::v-deep .el-table__footer-wrapper tbody td {
background-color: #f5f7fa;
font-weight: bold;
color: #303133;
}
::v-deep .el-table__footer-wrapper tbody td:first-child {
font-weight: bold;
color: #409EFF;
}
/* 表格样式 */
::v-deep .el-table__fixed-right td.blue-record-col .cell {
color: #1677ff !important;
font-weight: 600;
}
/* 调整表单样式 */
::v-deep .el-form-item {
margin-bottom: 18px;
}
/* 调整按钮样式 */
::v-deep .el-button {
margin-left: 0;
}
::v-deep .el-form-item__content {
line-height: 32px;
}
/* 调整表格样式 */
::v-deep .el-table {
margin-top: 20px;
}
::v-deep .el-table th {
background-color: #f5f7fa;
color: #303133;
}
::v-deep .el-pagination {
margin-top: 15px;
}
</style>