新增预警页面

This commit is contained in:
2025-12-10 00:08:30 +08:00
parent ec2622743a
commit 2de5b51e07
11 changed files with 638 additions and 116 deletions

View File

@@ -1,19 +1,22 @@
<template>
<!-- 调整卡片和图表容器尺寸让图表整体更小 -->
<a-card title="业务数据柱线图" style="width: 100%; height: 400px; margin: 20px 0;">
<Card title="业务数据柱线图" style="width: 100%; height: 400px; margin: 20px 0;">
<div ref="chartDom" style="width: 100%; height: 300px;"></div>
</a-card>
</Card>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { Card } from 'ant-design-vue';
import { BasicForm, FormSchema } from '@jeesite/core/components/Form';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { ErpSummaryAll, erpSummaryAllListAll } from '@jeesite/erp/api/erp/summaryAll';
import * as echarts from 'echarts';
const listSummary = ref<ErpSummaryAll[]>([]);
const chartDom = ref<HTMLDivElement | null>(null);
let myChart: echarts.ECharts | null = null;
// 封装公共的标签配置(避免重复代码)
const barLabelConfig = {
show: true, // 开启数值显示
position: 'top', // 数值显示在柱子顶部
@@ -23,7 +26,7 @@ const barLabelConfig = {
color: '#333', // 数值字体颜色(深色更清晰)
fontWeight: '500' // 字体加粗
},
formatter: '{c} '
formatter: '{c} '
};
// 折线图标签配置
@@ -36,7 +39,18 @@ const lineLabelConfig = {
color: '#f5222d',
fontWeight: '500'
},
formatter: '{c} '
formatter: '{c} %'
};
const fetchList = async () => {
try {
const params = { fcycle: 'D' , ctype: '1' }
const result = await erpSummaryAllListAll(params);
listSummary.value = result || [];
} catch (error) {
console.error('获取数据列表失败:', error);
listSummary.value = [];
}
};
const initChart = () => {
@@ -45,7 +59,6 @@ const initChart = () => {
const option = {
title: {
text: '月度销售额统计',
left: 'center',
textStyle: { fontSize: 18, color: '#333' }
},
@@ -55,11 +68,10 @@ const initChart = () => {
textStyle: { fontSize: 12 }
},
legend: {
data: ['产品A', '产品B','产品C', '销售额均值'],
top: 40,
data: ['本期金额', '上期金额', '环比'],
top: 10,
textStyle: { fontSize: 12 }
},
// 调整图表内部边距,让绘图区域更紧凑
grid: {
left: '8%',
right: '8%',
@@ -69,45 +81,35 @@ const initChart = () => {
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月'],
data: listSummary.value.map(item => (item.cdate)),
axisLabel: { fontSize: 12 }
},
yAxis: {
type: 'value',
name: '销售额(元)',
min: 0,
name: '交易金额(元)',
axisLabel: { fontSize: 12 }
},
series: [
{
name: '产品A',
name: '本期金额',
type: 'bar',
data: [50, 70, 65, 80, 90, 100],
data: listSummary.value.map(item => (item.thisValue)),
itemStyle: { color: '#1890ff' },
barWidth: 25, // 调整柱子宽度(数值越小越细)
label: barLabelConfig // 引用公共标签配置
},
{
name: '产品B',
name: '上期金额',
type: 'bar',
data: [30, 45, 55, 70, 85, 95],
data: listSummary.value.map(item => (item.prevValue)),
itemStyle: { color: '#52c41a' },
barWidth: 25,
label: barLabelConfig
},
{
name: '产品C',
type: 'bar',
data: [40, 55, 60, 75, 80, 90],
itemStyle: { color: '#f5a623' },
barWidth: 25,
label: barLabelConfig
},
// 新增折线图系列(平滑曲线)
{
name: '销售额均值',
name: '环比',
type: 'line',
data: [40, 56.67, 60, 75, 85, 95], // 三个月产品销售额的平均值
data: listSummary.value.map(item => (item.momRate)), // 三个月产品销售额的平均值
smooth: true, // 开启平滑曲线
symbol: 'circle', // 拐点样式为圆形
symbolSize: 8, // 拐点大小
@@ -134,10 +136,9 @@ const initChart = () => {
myChart.setOption(option);
};
// 窗口缩放时自适应图表
const resizeChart = () => myChart?.resize();
onMounted(() => {
onMounted(async () => {
await fetchList()
initChart();
window.addEventListener('resize', resizeChart);
});

View File

@@ -1,8 +1,7 @@
<template>
<!-- 饼图卡片容器 -->
<a-card title="产品销售额占比图" style="width: 100%; height: 400px; margin: 20px 0;">
<Card title="银行账户余额占比" style="width: 100%; height: 400px; margin: 20px 0;">
<div ref="chartDom" style="width: 100%; height: 300px;"></div>
</a-card>
</Card>
</template>
<script setup lang="ts">
@@ -35,16 +34,13 @@ const generateRandomColors = (count: number): string[] => {
'#7cb305', '#ff7a45', '#ff4d4f', '#6b778c', '#5d7092', '#91d5ff'
];
// 如果需要的颜色数量小于等于预设库数量,随机选取不重复的颜色
if (count <= colorLibrary.length) {
const shuffled = [...colorLibrary].sort(() => 0.5 - Math.random());
return shuffled.slice(0, count);
}
// 如果需要更多颜色,生成随机的柔和颜色
const colors: string[] = [];
for (let i = 0; i < count; i++) {
// 生成HSL颜色保证亮度和饱和度适中颜色美观
const hue = Math.floor(Math.random() * 360);
const saturation = 70 + Math.floor(Math.random() * 20); // 70-90%
const lightness = 45 + Math.floor(Math.random() * 15); // 45-60%
@@ -71,18 +67,14 @@ const initChart = () => {
value: Number(item.currentBalance) || 0
}));
// 生成随机颜色
const colors = generateRandomColors(pieData.length);
// 为每个数据项分配随机颜色
const pieDataWithColor = pieData.map((item, index) => ({
...item,
color: colors[index]
}));
// 计算总销售额
const total = pieDataWithColor.reduce((sum, item) => sum + item.value, 0);
// 计算占比并格式化数据
const formattedData = pieDataWithColor.map(item => ({
name: item.name,
value: item.value,
@@ -92,19 +84,18 @@ const initChart = () => {
const option = {
title: {
text: '银行账户余额占比',
left: 'center',
top: 10,
textStyle: { fontSize: 16, color: '#333', fontWeight: 500 }
},
tooltip: {
trigger: 'item',
formatter: '{b}: {c} 元 ({d}%)', // 格式:名称: 数值 万元 (百分比)
formatter: '{b}: {c} 元 ({d}%)',
textStyle: { fontSize: 12 }
},
legend: {
orient: 'horizontal', // 水平布局
bottom: 10, // 图例在底部
bottom: 5, // 图例在底部
left: 'center',
textStyle: { fontSize: 12, color: '#666' },
itemWidth: 12,

View File

@@ -1,17 +1,17 @@
<template>
<!-- 调整卡片和图表容器尺寸让图表整体更小 -->
<Card>
<ChartBar />
</Card>
<Card>
<ChartLine />
</Card>
<Card>
<ChartPie />
<ChartGauge />
</Card>
<!-- 主容器 -->
<div class="main-container">
<!-- 两列布局限制高度 + 垂直间距 -->
<div class="two-column-layout">
<div class="column left-column">
<ChartPie />
</div>
<div class="column right-column"></div>
</div>
<div class="chart-line-wrapper">
<ChartLine />
</div>
</div>
</template>
<script setup lang="ts">
@@ -19,11 +19,86 @@ import { ref, onMounted, onUnmounted } from 'vue';
import { Card } from 'ant-design-vue';
import ChartPie from './components/ChartPie.vue';
import ChartBar from './components/ChartBar.vue';
import ChartLine from './components/ChartLine.vue';
import ChartGauge from './components/ChartGauge.vue';
</script>
<style scoped lang="less">
</style>
.main-container {
height: 90vh;
background-color: #e6f7ff;
overflow: auto;
padding: 2px;
box-sizing: border-box;
overflow-x: hidden;
&::-webkit-scrollbar {
width: 4px;
height: 4px;
}
&::-webkit-scrollbar-track {
background: #f1f8ff;
border-radius: 4px;
}
&::-webkit-scrollbar-thumb {
background: #b3d9f2;
border-radius: 4px;
&:hover {
background: #8fc5e8;
}
}
scrollbar-width: thin;
scrollbar-color: #b3d9f2 #f1f8ff;
}
// 左右两列布局:限制高度 + 底部间距
.two-column-layout {
display: flex;
gap: 4px; // 两列水平间距
margin-bottom: 4px; // 与下方折线图的垂直间距(核心:缩小间距)
}
// 列通用样式
.column {
flex: 1; // 两列等分宽度
height: 100%;
display: flex;
flex-direction: column;
}
.left-column {
// 可选自定义样式
}
.right-column {
// 可选自定义样式
}
// 折线图容器:控制宽度 + 高度
.chart-line-wrapper {
width: 100%; // 撑满容器宽度(避免过宽)
margin-top: 0; // 覆盖默认间距
box-sizing: border-box;
}
// 卡片样式(优化适配列布局)
.content-card {
width: 100%;
height: 100%;
margin: 0;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
.column-content {
flex: 1;
padding: 4px 0;
overflow: auto;
}
}
// 长内容样式(测试用)
.long-content {
padding: 10px 0;
}
</style>

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
* @author gaoxq
*/
import { defHttp } from '@jeesite/core/utils/http/axios';
import { useGlobSetting } from '@jeesite/core/hooks/setting';
import { BasicModel, Page } from '@jeesite/core/api/model/baseModel';
import { UploadApiResult } from '@jeesite/core/api/sys/upload';
import { UploadFileParams } from '@jeesite/types/axios';
import { AxiosProgressEvent } from 'axios';
const { ctxPath, adminPath } = useGlobSetting();
export interface ErpSummaryAll extends BasicModel<ErpSummaryAll> {
createTime?: string; // 记录时间
cdate?: string; // 汇总日期
ctype: string; // 交易类型
thisValue?: number; // 当期金额
prevValue?: number; // 上期金额
momRate?: number; // 环比
fcycle: string; // 周期类型
}
export const erpSummaryAllList = (params?: ErpSummaryAll | any) =>
defHttp.get<ErpSummaryAll>({ url: adminPath + '/erp/summaryAll/list', params });
export const erpSummaryAllListAll = (params?: ErpSummaryAll | any) =>
defHttp.get<ErpSummaryAll[]>({ url: adminPath + '/erp/summaryAll/listAll', params });
export const erpSummaryAllListData = (params?: ErpSummaryAll | any) =>
defHttp.post<Page<ErpSummaryAll>>({ url: adminPath + '/erp/summaryAll/listData', params });
export const erpSummaryAllForm = (params?: ErpSummaryAll | any) =>
defHttp.get<ErpSummaryAll>({ url: adminPath + '/erp/summaryAll/form', params });
export const erpSummaryAllSave = (params?: any, data?: ErpSummaryAll | any) =>
defHttp.postJson<ErpSummaryAll>({ url: adminPath + '/erp/summaryAll/save', params, data });
export const erpSummaryAllImportData = (
params: UploadFileParams,
onUploadProgress: (progressEvent: AxiosProgressEvent) => void,
) =>
defHttp.uploadFile<UploadApiResult>(
{
url: ctxPath + adminPath + '/erp/summaryAll/importData',
onUploadProgress,
},
params,
);
export const erpSummaryAllDelete = (params?: ErpSummaryAll | any) =>
defHttp.get<ErpSummaryAll>({ url: adminPath + '/erp/summaryAll/delete', params });