fix: 财务数据可视化图表

This commit is contained in:
2025-04-18 10:54:33 +08:00
parent f3f870cd8e
commit 6f24ccbef2
4 changed files with 299 additions and 10 deletions

View File

@@ -65,7 +65,7 @@ const chartTabs: TabOption[] = [
<template>
<div class="p-5">
<AnalysisOverview :items="overviewItems" />
<AnalysisOverview :items="overviewItems" :my-card="false" />
<AnalysisChartsTabs :tabs="chartTabs" class="mt-5">
<template #trends>
<AnalyticsTrends />

View File

@@ -40,15 +40,15 @@ const [FormModal, formModalApi] = useVbenModal({
connectedComponent: FormModalDemo,
});
// const showModal = (data = {}, isUpdate = false) => {
// formModalApi.setData({
// // 表单值
// values: data,
// update: isUpdate,
// gridApi,
// });
// formModalApi.open();
// };
const showModal = (data = {}, isUpdate = false) => {
formModalApi.setData({
// 表单值
values: data,
update: isUpdate,
gridApi,
});
formModalApi.open();
};
const openPcWindows = (id: number) => {
openPcWindowsApi(id).then(() => {

View File

@@ -0,0 +1,10 @@
import { requestClient } from '#/api/request';
const prefix = 'visualization-chart/';
/**
* 诊所对账单
* @param data
*/
export async function getReconciliationChartApi(data: any) {
return requestClient.get<any>(`${prefix}curve-chart`, { params: data });
}

View File

@@ -0,0 +1,279 @@
<script setup lang="ts">
import { ref } from 'vue';
import { Page } from '@vben/common-ui';
import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
import { Button, Card, RangePicker } from 'ant-design-vue';
import dayjs from 'dayjs';
import { getReconciliationChartApi } from '#/views/finance/visualization-chart/api';
// 图表相关
const lineChartRef = ref<EchartsUIType>();
const pieChartRef = ref<EchartsUIType>();
const mapChartRef = ref<EchartsUIType>();
const { renderEcharts: renderLine } = useEcharts(lineChartRef);
const { renderEcharts: renderPie } = useEcharts(pieChartRef);
const { renderEcharts: renderMap } = useEcharts(mapChartRef);
// 模拟数据(替换为真实数据接口)
const chartData = ref();
// 初始化图表配置
const initCharts = () => {
// 折线图
renderLine({
tooltip: {
trigger: 'axis',
formatter: '{b}<br/>{a}: ¥{c}',
},
xAxis: {
type: 'category',
data: chartData.value.line.month,
axisLabel: { rotate: 45 },
},
yAxis: {
type: 'value',
axisLabel: { formatter: '¥{value}' },
},
series: [
{
name: chartData.value.line.legend[0],
type: 'line',
smooth: true,
data: chartData.value.line.sale_money.map(Number),
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(58,77,233,0.8)' },
{ offset: 1, color: 'rgba(58,77,233,0.1)' },
],
},
},
lineStyle: { width: 3, color: '#3a4de9' },
itemStyle: { color: '#3a4de9' },
},
],
});
// 饼图
renderPie({
tooltip: {
trigger: 'item',
formatter: '{b}: ¥{c} ({d}%)',
},
series: [
{
type: 'pie',
radius: ['40%', '70%'],
data: chartData.value.drug.data.map((item) => ({
name: item.name,
value: Number.parseFloat(item.value),
})),
itemStyle: {
borderRadius: 5,
borderColor: '#fff',
borderWidth: 2,
},
label: {
formatter: '{b|{b}}\n{hr|}\n¥{d|{c}}',
rich: {
b: { fontSize: 14, lineHeight: 20 },
hr: {
borderColor: '#a8a8a8',
width: '100%',
borderWidth: 1,
height: 0,
},
d: { color: '#4c4c4c', fontSize: 12, padding: [5, 0] },
},
},
color: ['#36c6d9', '#659be0'],
},
],
});
// 地图(这里使用柱状图替代)
renderMap({
tooltip: { trigger: 'item', formatter: '¥{c}' },
xAxis: {
type: 'category',
data: chartData.value.address.address,
},
yAxis: {
type: 'value',
axisLabel: { formatter: '¥{value}' },
},
series: [
{
type: 'bar',
data: chartData.value.address.money.map(Number),
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#36c6d9' },
{ offset: 1, color: '#659be0' },
],
},
borderRadius: [5, 5, 0, 0],
},
barWidth: '60%',
},
],
});
};
// onMounted(() => {
// // initCharts();
// });
// 时间选择器相关
const searchTime = ref([dayjs().startOf('month'), dayjs()]);
const handleTimeChange = (dates: [dayjs.Dayjs, dayjs.Dayjs]) => {
console.log('时间范围变化:', dates);
// 这里可以添加数据更新逻辑
getData();
};
const getData = () => {
getReconciliationChartApi({
search_time: searchTime.value,
}).then((res) => {
chartData.value = res;
initCharts();
});
};
const reload = () => {
searchTime.value = [dayjs().startOf('month'), dayjs()]
getData();
}
getData();
</script>
<template>
<Page>
<!-- 搜索区域 -->
<div class="search-container">
<RangePicker
v-model:value="searchTime"
style="width: 300px"
@change="handleTimeChange"
/>
<Button type="link" @click="getData">刷新</Button>
<Button type="link" @click="reload">重置</Button>
</div>
<!-- 图表容器 -->
<div class="charts-grid">
<div class="chart-card">
<!-- 总金额展示 -->
<!-- <div class="total-money">-->
<!-- <span>总销售金额</span>-->
<!-- </div>-->
<Card title="每日销售趋势">
<template #extra>
<div class="amount">
总销售金额¥{{ chartData?.total_money || 0 }}
</div>
</template>
<EchartsUI ref="lineChartRef" class="chart" />
</Card>
</div>
</div>
<!-- 图表容器 -->
<div class="charts-grid">
<div class="chart-card">
<Card title="药品销售占比">
<EchartsUI ref="pieChartRef" class="chart" />
</Card>
</div>
<div class="chart-card">
<Card title="地区销售分布">
<EchartsUI ref="mapChartRef" class="chart" />
</Card>
</div>
</div>
</Page>
</template>
<style scoped>
.search-container {
padding: 16px;
margin-bottom: 16px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.total-money {
padding: 24px;
margin-bottom: 16px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
text-align: center;
}
.total-money h3 {
color: #666;
margin-bottom: 8px;
font-size: 16px;
}
.amount {
color: #3a4de9;
font-weight: 600;
}
.charts-grid {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
}
.chart-card {
padding: 16px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.chart-card h4 {
margin: 0 0 12px 0;
color: #333;
font-size: 15px;
}
.chart {
height: 400px;
}
@media (max-width: 768px) {
.charts-grid {
grid-template-columns: 1fr;
}
.chart {
height: 300px;
}
.amount {
font-size: 24px;
}
}
</style>