<template>
|
<div class="app-container">
|
<!-- 查询条件 -->
|
<el-form :model="searchForm" :inline="true" class="search-form">
|
<el-form-item label="时间范围:">
|
<el-date-picker
|
v-model="searchForm.dateRange"
|
type="daterange"
|
range-separator="至"
|
start-placeholder="开始日期"
|
end-placeholder="结束日期"
|
format="YYYY-MM-DD"
|
value-format="YYYY-MM-DD"
|
style="width: 240px"
|
/>
|
</el-form-item>
|
<el-form-item label="产品大类:">
|
<el-tree-select
|
v-model="searchForm.productCategory"
|
placeholder="请选择商品类别"
|
clearable
|
check-strictly
|
:data="productOptions"
|
:render-after-expand="false"
|
style="width: 200px"
|
/>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" @click="handleSearch" :loading="loading">
|
查询
|
</el-button>
|
<el-button @click="resetSearch">
|
重置
|
</el-button>
|
<el-button type="info" @click="exportReport">
|
<el-icon><Download /></el-icon>
|
导出
|
</el-button>
|
</el-form-item>
|
</el-form>
|
|
<!-- 报表内容 -->
|
<el-card class="report-content" shadow="never">
|
<!-- 采购业务汇总表 -->
|
<div class="report-section">
|
<div class="section-header">
|
<h3>采购业务汇总表</h3>
|
<div class="summary-stats">
|
<div class="stat-item">
|
<span class="stat-label">采购总额:</span>
|
<span class="stat-value">¥{{ businessSummaryStats.totalAmount.toLocaleString() }}</span>
|
</div>
|
<div class="stat-item">
|
<span class="stat-label">商品种类:</span>
|
<span class="stat-value">{{ businessSummaryStats.productTypes }}</span>
|
</div>
|
</div>
|
</div>
|
|
<PIMTable
|
:table-data="businessSummaryData"
|
:column="tableColumns"
|
:table-loading="loading"
|
:is-selection="false"
|
:border="true"
|
:is-show-pagination="true"
|
:page="page"
|
@pagination="handlePagination"
|
/>
|
</div>
|
</el-card>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, onMounted } from 'vue'
|
import { ElMessage } from 'element-plus'
|
import { Download } from '@element-plus/icons-vue'
|
import PIMTable from '@/components/PIMTable/PIMTable.vue'
|
import { procurementBusinessSummaryListPage } from '@/api/procurementManagement/procurementReport'
|
import { productTreeList } from '@/api/basicData/product'
|
|
// 响应式数据
|
const loading = ref(false)
|
|
// 搜索表单
|
const searchForm = reactive({
|
dateRange: [],
|
productCategory: ''
|
})
|
|
// 产品类别树选项
|
const productOptions = ref([])
|
|
// 统计数据
|
const businessSummaryStats = ref({
|
totalAmount: 0,
|
productTypes: 0
|
})
|
|
// 表格列配置(根据后端字段定义)
|
const tableColumns = ref([
|
{
|
label: '产品大类',
|
prop: 'productCategory',
|
width: 150,
|
},
|
{
|
label: '规格型号',
|
prop: 'specificationModel',
|
width: 180
|
},
|
{
|
label: '采购数量',
|
prop: 'purchaseNum',
|
width: 120,
|
formatData: (val) => {
|
return val ? parseFloat(val).toLocaleString() : '0'
|
}
|
},
|
{
|
label: '采购金额',
|
prop: 'purchaseAmount',
|
width: 140,
|
formatData: (val) => {
|
return val ? `¥${parseFloat(val).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` : '¥0.00'
|
}
|
},
|
{
|
label: '采购次数',
|
prop: 'purchaseTimes',
|
width: 100
|
},
|
{
|
label: '平均单价',
|
prop: 'averagePrice',
|
width: 120,
|
formatData: (val) => {
|
return val ? `¥${parseFloat(val).toFixed(2)}` : '¥0.00'
|
}
|
},
|
{
|
label: '供应商名称',
|
prop: 'supplierName',
|
width: 200
|
},
|
{
|
label: '录入日期',
|
prop: 'entryDate',
|
width: 120
|
}
|
])
|
|
// 采购业务汇总表数据
|
const businessSummaryData = ref([])
|
|
// 分页参数(后端返回:total/size/current/pages)
|
const page = reactive({
|
total: 0,
|
current: 1,
|
size: 50,
|
})
|
|
// 转换产品树数据,将 id 改为 value
|
function convertIdToValue(data) {
|
return data.map((item) => {
|
const { id, children, ...rest } = item
|
const newItem = {
|
...rest,
|
value: id,
|
}
|
if (children && children.length > 0) {
|
newItem.children = convertIdToValue(children)
|
}
|
return newItem
|
})
|
}
|
|
// 获取产品类别树数据
|
const getProductOptions = () => {
|
return productTreeList().then((res) => {
|
productOptions.value = convertIdToValue(res)
|
}).catch((error) => {
|
console.error('获取产品树失败:', error)
|
ElMessage.error('获取产品类别失败')
|
})
|
}
|
|
// 根据 id 查找产品类别名称
|
const findNodeLabelById = (nodes, id) => {
|
if (!id) return null
|
for (let i = 0; i < nodes.length; i++) {
|
if (nodes[i].value === id) {
|
return nodes[i].label
|
}
|
if (nodes[i].children && nodes[i].children.length > 0) {
|
const found = findNodeLabelById(nodes[i].children, id)
|
if (found) return found
|
}
|
}
|
return null
|
}
|
|
// 查询列表
|
const handleSearch = async () => {
|
try {
|
loading.value = true
|
const params = {}
|
|
// 时间范围
|
if (searchForm.dateRange && searchForm.dateRange.length === 2) {
|
params.entryDateStart = searchForm.dateRange[0]
|
params.entryDateEnd = searchForm.dateRange[1]
|
}
|
|
// 产品类别
|
if (searchForm.productCategory) {
|
const categoryName = findNodeLabelById(productOptions.value, searchForm.productCategory)
|
if (categoryName) {
|
params.productCategory = categoryName
|
}
|
}
|
|
// 分页参数
|
params.current = page.current
|
params.size = page.size
|
|
const res = await procurementBusinessSummaryListPage(params)
|
if (res && res.data) {
|
// 兼容后端可能直接返回数组/或返回分页对象
|
businessSummaryData.value = Array.isArray(res.data) ? res.data : (res.data.records || [])
|
|
if (!Array.isArray(res.data)) {
|
page.total = Number(res.data.total ?? 0)
|
page.current = Number(res.data.current ?? page.current)
|
page.size = Number(res.data.size ?? page.size)
|
}
|
|
// 计算统计数据
|
if (businessSummaryData.value.length > 0) {
|
businessSummaryStats.value.totalAmount = businessSummaryData.value.reduce((sum, item) => {
|
return sum + (parseFloat(item.purchaseAmount) || 0)
|
}, 0)
|
businessSummaryStats.value.productTypes = new Set(businessSummaryData.value.map(item => item.productCategory)).size
|
} else {
|
businessSummaryStats.value = {
|
totalAmount: 0,
|
productTypes: 0
|
}
|
}
|
}
|
} catch (error) {
|
console.error('查询失败:', error)
|
} finally {
|
loading.value = false
|
}
|
}
|
|
// 翻页/切换每页条数
|
const handlePagination = ({ page: current, limit }) => {
|
page.current = current
|
page.size = limit
|
handleSearch()
|
}
|
|
const resetSearch = () => {
|
Object.assign(searchForm, {
|
dateRange: [],
|
productCategory: ''
|
})
|
page.current = 1
|
handleSearch()
|
}
|
|
const exportReport = () => {
|
ElMessage.success('导出功能开发中...')
|
}
|
|
|
onMounted(() => {
|
// 初始化产品类别树
|
getProductOptions()
|
|
// 设置默认时间范围为最近30天
|
const endDate = new Date()
|
const startDate = new Date()
|
startDate.setDate(startDate.getDate() - 30)
|
|
searchForm.dateRange = [
|
startDate.toISOString().split('T')[0],
|
endDate.toISOString().split('T')[0]
|
]
|
|
// 初始加载数据
|
handleSearch()
|
})
|
</script>
|
|
<style scoped>
|
.app-container {
|
padding: 20px;
|
background-color: #f5f7fa;
|
min-height: 100vh;
|
}
|
|
.page-header {
|
text-align: center;
|
margin-bottom: 20px;
|
padding: 20px;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
border-radius: 10px;
|
color: white;
|
}
|
|
.page-header h2 {
|
margin: 0 0 10px 0;
|
font-size: 28px;
|
font-weight: 600;
|
}
|
|
.page-header p {
|
margin: 0;
|
font-size: 16px;
|
opacity: 0.9;
|
}
|
|
.report-content {
|
border-radius: 8px;
|
}
|
|
.report-section {
|
min-height: 400px;
|
}
|
|
.section-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20px;
|
padding-bottom: 15px;
|
border-bottom: 2px solid #e4e7ed;
|
}
|
|
.section-header h3 {
|
margin: 0;
|
font-size: 20px;
|
font-weight: 600;
|
color: #303133;
|
}
|
|
.summary-stats {
|
display: flex;
|
gap: 30px;
|
}
|
|
.stat-item {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
}
|
|
.stat-label {
|
font-size: 14px;
|
color: #606266;
|
margin-bottom: 5px;
|
}
|
|
.stat-value {
|
font-size: 18px;
|
font-weight: 600;
|
color: #409EFF;
|
}
|
|
.delay-text {
|
color: #F56C6C;
|
font-weight: 600;
|
}
|
|
|
</style>
|