项目初始化

This commit is contained in:
2026-03-23 18:18:16 +08:00
parent dc5a92880e
commit cc4bb9188b
5 changed files with 348 additions and 285 deletions

View File

@@ -119,6 +119,52 @@ public class ErpScreenController {
return chartDataItems; return chartDataItems;
} }
/**
* 季度收支
*/
@RequestMapping(value = "getErpQuarterChart")
@ResponseBody
public List<ChartDataItem> getErpQuarterChart(ErpTransactionFlow erpTransactionFlow) {
List<ChartDataItem> chartDataItems = new ArrayList<>();
List<ErpTransactionFlow> flowList = erpTransactionFlowService.findList(erpTransactionFlow);
Map<String, Map<String, Object>> accountMap = flowList.stream()
.collect(Collectors.groupingBy(
flow -> flow.getYearDate() + "Q" + (Integer.parseInt(flow.getMonthDate()) + 2) / 3,
Collectors.collectingAndThen(Collectors.toList(), list -> {
BigDecimal sumValue01 = list.stream()
.filter(flow -> flow.getFlowType().equals("2"))
.map(ErpTransactionFlow::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal sumValue02 = list.stream()
.filter(flow -> flow.getFlowType().equals("1"))
.map(ErpTransactionFlow::getAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal sumValue04 = BigDecimalUtils.subtract(sumValue01, sumValue02);
BigDecimal sumValue03 = BigDecimalUtils.percent(sumValue02, sumValue01);
return Map.of(
"sumValue01", sumValue01,
"sumValue02", sumValue02,
"sumValue03", sumValue03,
"sumValue04", sumValue04
);
})
));
List<Map.Entry<String, Map<String, Object>>> sortedList = new ArrayList<>(accountMap.entrySet());
sortedList.sort(Map.Entry.comparingByKey());
for (Map.Entry<String, Map<String, Object>> entry : sortedList) {
String quarter = entry.getKey();
Map<String, Object> map = entry.getValue();
ChartDataItem item = new ChartDataItem();
item.setAxisName(quarter);
item.setValue01(map.get("sumValue01").toString());
item.setValue02(map.get("sumValue02").toString());
item.setValue03(map.get("sumValue03").toString());
item.setValue04(map.get("sumValue04").toString());
chartDataItems.add(item);
}
return chartDataItems;
}
/** /**
* 分类收支 * 分类收支
@@ -151,12 +197,10 @@ public class ErpScreenController {
BigDecimal v2 = (BigDecimal) e2.getValue().get("sumValue02"); BigDecimal v2 = (BigDecimal) e2.getValue().get("sumValue02");
return v2.compareTo(v1); return v2.compareTo(v1);
}); });
long soring = 1;
for (Map.Entry<String, Map<String, Object>> entry : sortedList) { for (Map.Entry<String, Map<String, Object>> entry : sortedList) {
String categoryName = entry.getKey(); String categoryName = entry.getKey();
Map<String, Object> map = entry.getValue(); Map<String, Object> map = entry.getValue();
ChartDataItem item = new ChartDataItem(); ChartDataItem item = new ChartDataItem();
item.setSoring(soring++);
item.setAxisName(categoryName); item.setAxisName(categoryName);
item.setValue01(map.get("sumValue01").toString()); item.setValue01(map.get("sumValue01").toString());
item.setValue02(map.get("sumValue02").toString()); item.setValue02(map.get("sumValue02").toString());

View File

@@ -202,8 +202,8 @@
const expenseVal = index02Yuan[idx]; const expenseVal = index02Yuan[idx];
const netProfitVal = index03Yuan[idx]; const netProfitVal = index03Yuan[idx];
return ` return `
<div style="text-align:center; font-weight:bold; margin-bottom:6px">${title}</div> <div style="text-align:center; font-weight:bold; margin-bottom:6px">${title}</div>
<table style="width:100%; border-collapse:collapse; text-align:center"> <table style="width:100%; border-collapse:collapse; text-align:center">
<tr> <tr>
<td style="border:1px solid #409EFF; padding:4px; font-weight:bold">收入(元)</td> <td style="border:1px solid #409EFF; padding:4px; font-weight:bold">收入(元)</td>
<td style="border:1px solid #409EFF; padding:4px; font-weight:bold">支出(元)</td> <td style="border:1px solid #409EFF; padding:4px; font-weight:bold">支出(元)</td>
@@ -214,7 +214,7 @@
<td style="border:1px solid #409EFF; padding:4px">${expenseVal}</td> <td style="border:1px solid #409EFF; padding:4px">${expenseVal}</td>
<td style="border:1px solid #409EFF; padding:4px">${netProfitVal}</td> <td style="border:1px solid #409EFF; padding:4px">${netProfitVal}</td>
</tr> </tr>
</table> </table>
`; `;
}; };

View File

@@ -237,8 +237,8 @@
const rateVal = index03[idx]; const rateVal = index03[idx];
const profitVal = index04Yuan[idx]; const profitVal = index04Yuan[idx];
return ` return `
<div style="text-align:center; font-weight:bold; margin-bottom:6px">${title}</div> <div style="text-align:center; font-weight:bold; margin-bottom:6px">${title}</div>
<table style="width:100%; border-collapse:collapse; text-align:center"> <table style="width:100%; border-collapse:collapse; text-align:center">
<tr> <tr>
<td style="border:1px solid #409EFF; padding:4px; font-weight:bold">收入(元)</td> <td style="border:1px solid #409EFF; padding:4px; font-weight:bold">收入(元)</td>
<td style="border:1px solid #409EFF; padding:4px; font-weight:bold">支出(元)</td> <td style="border:1px solid #409EFF; padding:4px; font-weight:bold">支出(元)</td>
@@ -251,7 +251,7 @@
<td style="border:1px solid #409EFF; padding:4px">${rateVal}</td> <td style="border:1px solid #409EFF; padding:4px">${rateVal}</td>
<td style="border:1px solid #409EFF; padding:4px">${profitVal}</td> <td style="border:1px solid #409EFF; padding:4px">${profitVal}</td>
</tr> </tr>
</table> </table>
`; `;
}; };

View File

@@ -7,46 +7,59 @@
</div> </div>
</template> </template>
<script setup> <script lang="ts" setup>
import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue' import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue';
import * as echarts from 'echarts' import * as echarts from 'echarts';
// import { getItemInfoList } from '@/api/bizApi' import { ChartDataItem, getErpQuarterChart } from '@jeesite/erp/api/erp/screen';
const props = defineProps({ const props = defineProps({
formParams: { formParams: {
type: Object, type: Object,
default: () => ({}) default: () => ({}),
} },
}); });
const vList = ref([]) const vList = ref<ChartDataItem[]>([]);
const chartRef = ref(null) const chartRef = ref<HTMLDivElement | null>(null);
let chartInstance = null let chartInstance: echarts.ECharts | null = null;
const resizeHandler = () => chartInstance?.resize() const resizeHandler = () => chartInstance?.resize();
async function getList() { async function getList() {
try { try {
const params = { const params = {
...props.formParams, ...props.formParams,
itemCode: 'ERP_YEARQUAR_Y001', };
} const res = await getErpQuarterChart(params);
const res = await getItemInfoList(params) vList.value = res || [];
vList.value = res || []
} catch (error) { } catch (error) {
console.error(error) console.error(error);
vList.value = [] vList.value = [];
}
} }
}
function initChart() { const parseAmount = (value?: string | number) => {
const el = chartRef.value const parsed = Number(value ?? 0);
if (!el) return return Number.isFinite(parsed) ? parsed : 0;
};
const parseRate = (value?: string | number) => {
const parsed = Number(value ?? 0);
return Number.isFinite(parsed) ? Number(parsed.toFixed(2)) : 0;
};
function initChart() {
const el = chartRef.value;
if (!el) return;
if (!chartInstance) { if (!chartInstance) {
chartInstance = echarts.init(el) chartInstance = echarts.init(el);
} }
const baseOption = { const baseOption: echarts.EChartsOption & {
tooltip: echarts.TooltipComponentOption;
xAxis: Array<echarts.XAXisComponentOption & { data: string[] }>;
series: echarts.SeriesOption[];
} = {
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { type: 'cross' }, axisPointer: { type: 'cross' },
@@ -55,20 +68,20 @@ function initChart() {
borderWidth: 1, borderWidth: 1,
textStyle: { color: '#0a3b70' }, textStyle: { color: '#0a3b70' },
padding: [8, 12], padding: [8, 12],
borderRadius: 6 borderRadius: 6,
}, },
legend: { legend: {
top: '10', top: '10',
left: 'center', left: 'center',
textStyle: { fontSize: 12, color: '#e0e6ff' }, textStyle: { fontSize: 12, color: '#e0e6ff' },
data: ['收入', '支出', '占比'] data: ['收入', '支出', '占比'],
}, },
grid: { grid: {
left: '5%', left: '5%',
right: '5%', right: '5%',
bottom: '10%', bottom: '10%',
top: '15%', top: '15%',
containLabel: true containLabel: true,
}, },
xAxis: [ xAxis: [
{ {
@@ -77,11 +90,11 @@ function initChart() {
axisLabel: { axisLabel: {
fontSize: 11, fontSize: 11,
interval: 0, interval: 0,
color: '#b4c7e7' color: '#b4c7e7',
}, },
axisLine: { lineStyle: { color: '#1a508b' } }, axisLine: { lineStyle: { color: '#1a508b' } },
boundaryGap: true boundaryGap: true,
} },
], ],
yAxis: [ yAxis: [
{ {
@@ -90,7 +103,7 @@ function initChart() {
nameTextStyle: { fontSize: 12, color: '#b4c7e7' }, nameTextStyle: { fontSize: 12, color: '#b4c7e7' },
axisLabel: { formatter: '{value}', color: '#b4c7e7' }, axisLabel: { formatter: '{value}', color: '#b4c7e7' },
axisLine: { lineStyle: { color: '#1a508b' } }, axisLine: { lineStyle: { color: '#1a508b' } },
splitLine: { lineStyle: { color: 'rgba(26, 80, 139, 0.3)' } } splitLine: { lineStyle: { color: 'rgba(26, 80, 139, 0.3)' } },
}, },
{ {
type: 'value', type: 'value',
@@ -101,8 +114,8 @@ function initChart() {
splitLine: { lineStyle: { color: 'rgba(26, 80, 139, 0.2)' } }, splitLine: { lineStyle: { color: 'rgba(26, 80, 139, 0.2)' } },
min: 'dataMin', min: 'dataMin',
max: 'dataMax', max: 'dataMax',
scale: true scale: true,
} },
], ],
series: [ series: [
{ {
@@ -114,17 +127,17 @@ function initChart() {
itemStyle: { itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#85E868' }, { offset: 0, color: '#85E868' },
{ offset: 1, color: '#67C23A' } { offset: 1, color: '#67C23A' },
]), ]),
borderRadius: [8, 8, 0, 0] borderRadius: [8, 8, 0, 0],
}, },
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
fontSize: 10, fontSize: 10,
color: '#fff', color: '#fff',
formatter: '{c}' formatter: '{c}',
} },
}, },
{ {
name: '支出', name: '支出',
@@ -135,17 +148,17 @@ function initChart() {
itemStyle: { itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#FF8A8A' }, { offset: 0, color: '#FF8A8A' },
{ offset: 1, color: '#F56C6C' } { offset: 1, color: '#F56C6C' },
]), ]),
borderRadius: [8, 8, 0, 0] borderRadius: [8, 8, 0, 0],
}, },
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
fontSize: 10, fontSize: 10,
color: '#fff', color: '#fff',
formatter: '{c}' formatter: '{c}',
} },
}, },
{ {
name: '占比', name: '占比',
@@ -158,47 +171,46 @@ function initChart() {
symbolSize: 5, symbolSize: 5,
label: { label: {
show: true, show: true,
position: 'outside', position: 'top',
fontSize: 10, fontSize: 10,
color: '#409EFF', color: '#409EFF',
formatter: '{c}', formatter: '{c}',
offset: [0, -5] offset: [0, -5],
}, },
areaStyle: { areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(64, 158, 255, 0.3)' }, { offset: 0, color: 'rgba(64, 158, 255, 0.3)' },
{ offset: 1, color: 'rgba(64, 158, 255, 0.0)' } { offset: 1, color: 'rgba(64, 158, 255, 0.0)' },
]) ]),
}, },
emphasis: { emphasis: {
lineStyle: { width: 2 }, focus: 'series',
symbolSize: 7 },
} },
} ],
] };
}
if (vList.value.length > 0) { if (vList.value.length > 0) {
const xData = vList.value.map(item => { const xData = vList.value.map((item) => {
const xaxis = item.xaxis || '' const xaxis = item.axisName || '';
return xaxis; return xaxis;
}) });
const index01Yuan = vList.value.map(item => item.index01 || 0) const value01Yuan = vList.value.map((item) => parseAmount(item.value01));
const index02Yuan = vList.value.map(item => item.index02 || 0) const value02Yuan = vList.value.map((item) => parseAmount(item.value02));
const index03 = vList.value.map(item => item.index03 || 0) const value03 = vList.value.map((item) => parseRate(item.value03));
const index01Wan = index01Yuan.map(val => (val / 10000).toFixed(2)) const value01Wan = value01Yuan.map((val) => Number((val / 10000).toFixed(2)));
const index02Wan = index02Yuan.map(val => (val / 10000).toFixed(2)) const value02Wan = value02Yuan.map((val) => Number((val / 10000).toFixed(2)));
baseOption.tooltip.formatter = function (params) { baseOption.tooltip.formatter = function (params) {
const title = params[0].axisValue const title = params[0].axisValue;
const idx = params[0].dataIndex const idx = params[0].dataIndex;
const incomeVal = index01Yuan[idx] const incomeVal = value01Yuan[idx];
const expenseVal = index02Yuan[idx] const expenseVal = value02Yuan[idx];
const rateVal = index03[idx] const rateVal = value03[idx];
return ` return `
<div style="text-align:center; font-weight:bold; margin-bottom:6px">${title}</div> <div style="text-align:center; font-weight:bold; margin-bottom:6px">${title}</div>
<table style="width:100%; border-collapse:collapse; text-align:center"> <table style="width:100%; border-collapse:collapse; text-align:center">
<tr> <tr>
<td style="border:1px solid #409EFF; padding:4px; font-weight:bold">收入(元)</td> <td style="border:1px solid #409EFF; padding:4px; font-weight:bold">收入(元)</td>
<td style="border:1px solid #409EFF; padding:4px; font-weight:bold">支出(元)</td> <td style="border:1px solid #409EFF; padding:4px; font-weight:bold">支出(元)</td>
@@ -209,57 +221,61 @@ function initChart() {
<td style="border:1px solid #409EFF; padding:4px">${expenseVal}</td> <td style="border:1px solid #409EFF; padding:4px">${expenseVal}</td>
<td style="border:1px solid #409EFF; padding:4px">${rateVal}</td> <td style="border:1px solid #409EFF; padding:4px">${rateVal}</td>
</tr> </tr>
</table> </table>
` `;
};
baseOption.xAxis[0].data = xData;
baseOption.series[0].data = value01Wan;
baseOption.series[1].data = value02Wan;
baseOption.series[2].data = value03;
} }
baseOption.xAxis[0].data = xData chartInstance.setOption(baseOption, true);
baseOption.series[0].data = index01Wan
baseOption.series[1].data = index02Wan
baseOption.series[2].data = index03
} }
chartInstance.setOption(baseOption, true) watch(
}
watch(
() => props.formParams, () => props.formParams,
() => { () => {
getList() getList();
}, },
{ deep: true, immediate: true } { deep: true, immediate: true },
) );
watch(vList, () => { watch(
nextTick(() => initChart()) vList,
}, { deep: true }) () => {
nextTick(() => initChart());
},
{ deep: true },
);
onMounted(() => { onMounted(() => {
initChart() initChart();
window.addEventListener('resize', resizeHandler) window.addEventListener('resize', resizeHandler);
}) });
onUnmounted(() => { onUnmounted(() => {
window.removeEventListener('resize', resizeHandler) window.removeEventListener('resize', resizeHandler);
if (chartInstance) { if (chartInstance) {
chartInstance.dispose() chartInstance.dispose();
chartInstance = null chartInstance = null;
} }
}) });
</script> </script>
<style scoped> <style scoped>
.chart-card { .chart-card {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
background: rgba(0, 0, 0, 0.1) url("@/assets/chart/box/16.png") no-repeat; background: rgba(0, 0, 0, 0.1) url('@jeesite/assets/chart/box/16.png') no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
} }
.chart-card-header { .chart-card-header {
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
padding: 0 16px; padding: 0 16px;
@@ -267,20 +283,20 @@ onUnmounted(() => {
border-bottom: 1px solid #1a508b; border-bottom: 1px solid #1a508b;
display: flex; display: flex;
align-items: center; align-items: center;
background: rgba(0, 0, 0, 0.1) url("@/assets/chart/title/23.png") no-repeat; background: rgba(0, 0, 0, 0.1) url('@jeesite/assets/chart/title/23.png') no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
} }
.chart-card-title { .chart-card-title {
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 600;
color: #409EFF; color: #409eff;
letter-spacing: 0.5px; letter-spacing: 0.5px;
} }
.bar-line-chart-container { .bar-line-chart-container {
flex: 1; flex: 1;
width: 100%; width: 100%;
height: calc(100% - 40px); height: calc(100% - 40px);
} }
</style> </style>

View File

@@ -60,3 +60,6 @@ export const CategoryChart = (params?: ErpTransactionFlow | any) =>
export const ErpAccountChart = (params?: ErpTransactionFlow | any) => export const ErpAccountChart = (params?: ErpTransactionFlow | any) =>
defHttp.get<ChartDataItem[]>({ url: adminPath + '/erp/screen/getErpAccountChart', params }); defHttp.get<ChartDataItem[]>({ url: adminPath + '/erp/screen/getErpAccountChart', params });
export const getErpQuarterChart = (params?: ErpTransactionFlow | any) =>
defHttp.get<ChartDataItem[]>({ url: adminPath + '/erp/screen/getErpQuarterChart', params });