feat(reportmanage): 经营分析+数据导出前端页面

This commit is contained in:
2026-06-18 15:16:13 +08:00
parent 0887dd5c29
commit 7d196f83fc
2 changed files with 212 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
import request from '@/utils/request'
export function generateReport(data) {
return request({
url: '/report/analytics/generate',
method: 'post',
data: data
})
}
export function exportToExcel(params) {
return request({
url: '/report/analytics/export',
method: 'get',
params: params
})
}
export function getDashboardData(params) {
return request({
url: '/dashboard/data',
method: 'get',
params: params
})
}
export function getDashboardCharts(params) {
return request({
url: '/dashboard/charts',
method: 'get',
params: params
})
}

View File

@@ -0,0 +1,179 @@
<template>
<div class="app-container">
<el-card shadow="never">
<template #header>
<div class="card-header">
<span>经营分析报告</span>
<div>
<el-button type="success" :loading="exportLoading" @click="handleExport">导出Excel</el-button>
</div>
</div>
</template>
<el-form :model="queryParams" inline>
<el-form-item label="科室">
<el-input v-model="queryParams.departmentName" placeholder="科室名称" clearable style="width:160px" />
</el-form-item>
<el-form-item label="开始日期">
<el-date-picker v-model="queryParams.startDate" type="date" value-format="YYYY-MM-DD" placeholder="开始日期" style="width:160px" />
</el-form-item>
<el-form-item label="结束日期">
<el-date-picker v-model="queryParams.endDate" type="date" value-format="YYYY-MM-DD" placeholder="结束日期" style="width:160px" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="loading" @click="handleQuery">查询</el-button>
<el-button @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card v-if="report" shadow="never" class="mt16">
<template #header>
<span>汇总数据</span>
</template>
<el-row :gutter="16">
<el-col :span="4">
<el-card shadow="hover" :body-style="{padding:'12px'}">
<div style="text-align:center">
<div style="font-size:20px;font-weight:bold;color:#409eff">{{ report.totalRecords || 0 }}</div>
<div style="font-size:12px;color:#999">总记录数</div>
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="hover" :body-style="{padding:'12px'}">
<div style="text-align:center">
<div style="font-size:20px;font-weight:bold;color:#409eff">{{ formatMoney(report.totalRevenue) }}</div>
<div style="font-size:12px;color:#999">总收入()</div>
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="hover" :body-style="{padding:'12px'}">
<div style="text-align:center">
<div style="font-size:20px;font-weight:bold;color:#67c23a">{{ formatMoney(report.totalCost) }}</div>
<div style="font-size:12px;color:#999">总成本()</div>
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="hover" :body-style="{padding:'12px'}">
<div style="text-align:center">
<div style="font-size:20px;font-weight:bold;color:#e6a23c">{{ formatMoney(report.totalProfit) }}</div>
<div style="font-size:12px;color:#999">总利润()</div>
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="hover" :body-style="{padding:'12px'}">
<div style="text-align:center">
<div style="font-size:20px;font-weight:bold;color:#f56c6c">{{ report.totalPatients || 0 }}</div>
<div style="font-size:12px;color:#999">总患者数</div>
</div>
</el-card>
</el-col>
<el-col :span="4">
<el-card shadow="hover" :body-style="{padding:'12px'}">
<div style="text-align:center">
<div style="font-size:20px;font-weight:bold;color:#909399">{{ report.profitRate || 0 }}%</div>
<div style="font-size:12px;color:#999">利润率</div>
</div>
</el-card>
</el-col>
</el-row>
</el-card>
<el-card v-if="report && report.records && report.records.length" shadow="never" class="mt16">
<template #header>
<span>明细数据</span>
</template>
<el-table :data="report.records" border stripe>
<el-table-column prop="statDate" label="日期" width="120" align="center" />
<el-table-column prop="departmentName" label="科室" width="140" align="center" show-overflow-tooltip />
<el-table-column label="收入(万元)" width="120" align="right">
<template #default="{ row }">{{ formatMoney(row.revenue) }}</template>
</el-table-column>
<el-table-column label="成本(万元)" width="120" align="right">
<template #default="{ row }">{{ formatMoney(row.cost) }}</template>
</el-table-column>
<el-table-column label="利润(万元)" width="120" align="right">
<template #default="{ row }">{{ formatMoney(row.profit) }}</template>
</el-table-column>
<el-table-column prop="patientCount" label="患者数" width="90" align="center" />
<el-table-column prop="bedCount" label="床位数" width="90" align="center" />
<el-table-column prop="bedOccupancyRate" label="床位率(%)" width="100" align="center" />
<el-table-column prop="avgStayDays" label="平均住院日" width="100" align="center" />
<el-table-column label="平均费用(万)" width="110" align="right">
<template #default="{ row }">{{ formatMoney(row.avgCost) }}</template>
</el-table-column>
</el-table>
</el-card>
</div>
</template>
<script setup name="BusinessAnalytics" lang="ts">
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { generateReport, exportToExcel } from '@/api/reportmanage'
const loading = ref(false)
const exportLoading = ref(false)
const report = ref(null)
const queryParams = reactive({
departmentName: '',
startDate: '',
endDate: ''
})
function formatMoney(val) {
if (!val) return '0.00'
return (val / 10000).toFixed(2)
}
function handleQuery() {
loading.value = true
generateReport(queryParams).then(res => {
report.value = res.data
}).catch(() => {
report.value = null
ElMessage.error('查询失败')
}).finally(() => {
loading.value = false
})
}
function handleReset() {
queryParams.departmentName = ''
queryParams.startDate = ''
queryParams.endDate = ''
report.value = null
}
function handleExport() {
exportLoading.value = true
exportToExcel(queryParams).then(res => {
if (res.data && res.data.headers) {
ElMessage.success('导出数据共 ' + res.data.totalRows + ' 条,请查看返回数据')
}
}).catch(() => {
ElMessage.error('导出失败')
}).finally(() => {
exportLoading.value = false
})
}
</script>
<style lang="scss" scoped>
.app-container {
padding: 20px;
}
.mt16 {
margin-top: 16px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>