新增预警页面
This commit is contained in:
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
53
web-vue/packages/erp/api/erp/summaryAll.ts
Normal file
53
web-vue/packages/erp/api/erp/summaryAll.ts
Normal 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 });
|
||||
Reference in New Issue
Block a user