---初始化后台管理web页面项目
This commit is contained in:
621
src/views/erp/report/InventoryAnalysis.vue
Normal file
621
src/views/erp/report/InventoryAnalysis.vue
Normal file
@ -0,0 +1,621 @@
|
||||
<template>
|
||||
<div style="padding: 10px; box-sizing: border-box">
|
||||
<div>
|
||||
<div class="mumber-box">
|
||||
<div class="mumber" v-for="(it, index) in data.Statistics" :key="index">
|
||||
<div class="mum-left">
|
||||
<img class="index-img" :src="'/src/assets/erp/index-icon' + (index + 1) + '.svg'" />
|
||||
</div>
|
||||
<div class="mum-right">
|
||||
<div class="mum-title">{{ it.name }}({{ it.unit }})</div>
|
||||
<div class="mum" :style="{ color: data.colors[index] }">{{ it.num }}</div>
|
||||
<div class="mum-than">
|
||||
{{ index > 1 ? '环比' : '同比' }} {{ it.pecent }}%
|
||||
<Icon
|
||||
:icon="it.isUp ? 'ant-design:caret-up-outlined' : 'ant-design:caret-down-outlined'"
|
||||
:color="it.isUp ? '#3dba84' : '#ee3b3b'"
|
||||
:size="14"
|
||||
class="icon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-row :gutter="10">
|
||||
<a-col :span="7">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">月入库单分析</span>
|
||||
</div>
|
||||
<div class="item" ref="homePieChart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="11">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">入库数量增长趋势</span>
|
||||
</div>
|
||||
<div class="item" ref="homeLine1Chart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">预警类型占比</span>
|
||||
</div>
|
||||
<div class="item" ref="homePieChart2"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">月出库单分析</span>
|
||||
</div>
|
||||
<div class="item" ref="homeRadarChart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="11">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">出库数量增长趋势</span>
|
||||
</div>
|
||||
<div class="item" ref="homeLineChart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">在库原材料占比</span>
|
||||
</div>
|
||||
<div class="item" ref="homePieChart3"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, nextTick, reactive, ref, unref, markRaw } from 'vue';
|
||||
import {
|
||||
getDataInfo,
|
||||
getDocInfo,
|
||||
getDocOutInfo,
|
||||
getTrendInfo,
|
||||
getTrendOutInfo,
|
||||
getWarnInfo,
|
||||
getMaterialsInfo,
|
||||
} from '/@/api/erp/report/inventory';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
const data = reactive({
|
||||
colors: ['#5E95FF', '#00CACF', '#FF9100', '#995EFF', '#5E6EFF'],
|
||||
Statistics: [] as any[],
|
||||
|
||||
bar2Option: {
|
||||
color: ['#5E95FF', '#00CACF', '#5E6EFF', '#995EFF', '#2C8DD6'],
|
||||
title: {
|
||||
text: '单位:单',
|
||||
left: 20,
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontWeight: 'normal',
|
||||
fontSize: 12,
|
||||
lineHeight: 30,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
left: 90,
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
fontSize: 12,
|
||||
lineHeight: 30,
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: 10,
|
||||
right: 40,
|
||||
bottom: 20,
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: [] as any[],
|
||||
},
|
||||
series: [] as any[],
|
||||
},
|
||||
|
||||
line1Option: {
|
||||
color: ['#00CACF', '#995EFF', '#FF8080', '#5E95FF', '#E8D316', '#FF9100'],
|
||||
title: {
|
||||
text: '单位:万元',
|
||||
left: 20,
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontWeight: 'normal',
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(51, 51, 51, 0.7)',
|
||||
formatter: (params) => {
|
||||
let str = '';
|
||||
params.forEach((o) => {
|
||||
str +=
|
||||
o.marker +
|
||||
'<span style="display:inline-flex;justify-content: space-between;width:138px;color:#fff">' +
|
||||
o.seriesName +
|
||||
'<span>' +
|
||||
o.data +
|
||||
'万元</span></span><br/>';
|
||||
});
|
||||
return str;
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
itemWidth: 20,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
right: 20,
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
},
|
||||
data: [] as any[],
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: [] as any[],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [] as any[],
|
||||
},
|
||||
|
||||
// 占比饼图
|
||||
pie2Option: {
|
||||
color: ['#5E95FF', '#FF9100', '#995EFF', '#00CACF'],
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
backgroundColor: 'rgba(51, 51, 51, 0.7)',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
},
|
||||
formatter: '{b}<br/>{c}' + '条' + ' ({d}%)',
|
||||
},
|
||||
legend: {
|
||||
bottom: '10%',
|
||||
left: 'center',
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
},
|
||||
},
|
||||
series: {
|
||||
type: 'pie',
|
||||
radius: ['40%', '60%'],
|
||||
center: ['50%', '38%'],
|
||||
avoidLabelOverlap: false,
|
||||
// selectedMode: 'single',
|
||||
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '20',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
data: [] as any[],
|
||||
},
|
||||
},
|
||||
|
||||
barOption: {
|
||||
color: ['#5E95FF', '#00CACF', '#5E6EFF', '#995EFF', '#2C8DD6'],
|
||||
title: {
|
||||
text: '单位:单',
|
||||
left: 20,
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontWeight: 'normal',
|
||||
fontSize: 12,
|
||||
lineHeight: 30,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
left: 90,
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
fontSize: 12,
|
||||
lineHeight: 30,
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: 10,
|
||||
right: 40,
|
||||
bottom: 20,
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: [] as any[],
|
||||
},
|
||||
series: [] as any[],
|
||||
},
|
||||
|
||||
// 折线图
|
||||
lineOption: {
|
||||
color: ['#00CACF', '#995EFF', '#FF8080', '#5E95FF', '#E8D316', '#FF9100'],
|
||||
title: {
|
||||
text: '单位:万元',
|
||||
left: 20,
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontWeight: 'normal',
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(51, 51, 51, 0.7)',
|
||||
formatter: (params) => {
|
||||
let str = '';
|
||||
params.forEach((o) => {
|
||||
str +=
|
||||
o.marker +
|
||||
'<span style="display:inline-flex;justify-content: space-between;width:138px;color:#fff">' +
|
||||
o.seriesName +
|
||||
'<span>' +
|
||||
o.data +
|
||||
'万元</span></span><br/>';
|
||||
});
|
||||
return str;
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
itemWidth: 20,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
right: 20,
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
},
|
||||
data: [] as any[],
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: [] as any[],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [] as any[],
|
||||
},
|
||||
|
||||
// 饼图
|
||||
|
||||
pie3Option: {
|
||||
color: ['#FF8080', '#9863C1', '#FF9100', '#5E95FF', '#00CACF'],
|
||||
series: {
|
||||
name: '',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
selectedMode: 'single',
|
||||
center: ['50%', '48%'],
|
||||
label: {
|
||||
color: '#6e7079',
|
||||
overflow: 'none',
|
||||
formatter: '{b} {d}%\n{c}',
|
||||
},
|
||||
data: [] as any[],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const homePieChart = ref<HTMLDivElement>();
|
||||
const homePieChart2 = ref<HTMLDivElement>();
|
||||
const homePieChart3 = ref<HTMLDivElement>();
|
||||
const homeLine1Chart = ref<HTMLDivElement>();
|
||||
const homeRadarChart = ref<HTMLDivElement>();
|
||||
const homeLineChart = ref<HTMLDivElement>();
|
||||
|
||||
onMounted(async () => {
|
||||
let res = await getDataInfo();
|
||||
data.Statistics = res || [];
|
||||
let res1 = await getDocInfo();
|
||||
res1.forEach((o, i) => {
|
||||
let arr: any[] = [];
|
||||
o.data.forEach((k: any) => {
|
||||
arr.push(k.value);
|
||||
if (i == 1) data.bar2Option.yAxis.data.push(k.name);
|
||||
});
|
||||
let obj = {
|
||||
name: o.name,
|
||||
type: 'bar',
|
||||
stack: 'total',
|
||||
label: {
|
||||
show: true,
|
||||
color: '#fff',
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series',
|
||||
},
|
||||
data: arr,
|
||||
};
|
||||
data.bar2Option.series.push(obj);
|
||||
});
|
||||
let res2 = await getWarnInfo();
|
||||
res2.forEach((o, i) => {
|
||||
if (i === 0) {
|
||||
data.pie2Option.series.data.push({
|
||||
value: o.value,
|
||||
name: o.name,
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center',
|
||||
normal: {
|
||||
show: true,
|
||||
formatter: '预警类型',
|
||||
position: 'center',
|
||||
fontSize: 16,
|
||||
color: '#5E95FF',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
data.pie2Option.series.data.push({
|
||||
value: o.value,
|
||||
name: o.name,
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center',
|
||||
normal: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
let res3 = await getMaterialsInfo();
|
||||
data.pie3Option.series.data = res3;
|
||||
data.pie3Option.series.data[0].selected = true;
|
||||
let res4 = await getDocOutInfo();
|
||||
if (res4) {
|
||||
res4.forEach((o, i) => {
|
||||
let arr: any[] = [];
|
||||
o.data.forEach((k: any) => {
|
||||
arr.push(k.value);
|
||||
if (i == 1) data.barOption.yAxis.data.push(k.name);
|
||||
});
|
||||
let obj = {
|
||||
name: o.name,
|
||||
type: 'bar',
|
||||
stack: 'total',
|
||||
label: {
|
||||
show: true,
|
||||
color: '#fff',
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'series',
|
||||
},
|
||||
data: arr,
|
||||
};
|
||||
data.barOption.series.push(obj);
|
||||
});
|
||||
}
|
||||
let res5 = await getTrendInfo();
|
||||
res5.forEach((o, i) => {
|
||||
data.line1Option.legend.data.push(o.name);
|
||||
let arr: any[] = [];
|
||||
o.data.forEach((k: any) => {
|
||||
arr.push(k.value);
|
||||
if (i == 1) data.line1Option.xAxis.data.push(k.name);
|
||||
});
|
||||
let obj = {
|
||||
name: o.name,
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
data: arr,
|
||||
};
|
||||
data.line1Option.series.push(obj);
|
||||
});
|
||||
let res6 = await getTrendOutInfo();
|
||||
res6.forEach((o, i) => {
|
||||
data.lineOption.legend.data.push(o.name);
|
||||
let arr: any[] = [];
|
||||
o.data.forEach((k: any) => {
|
||||
arr.push(k.value);
|
||||
if (i == 1) data.lineOption.xAxis.data.push(k.name);
|
||||
});
|
||||
let obj = {
|
||||
name: o.name,
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
data: arr,
|
||||
};
|
||||
data.lineOption.series.push(obj);
|
||||
});
|
||||
|
||||
nextTick(() => {
|
||||
let myChart = markRaw(echarts.init(unref(homePieChart) as HTMLDivElement));
|
||||
myChart.setOption(data.bar2Option, true);
|
||||
myChart.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
let myChart2 = markRaw(echarts.init(unref(homePieChart2) as HTMLDivElement));
|
||||
myChart2.setOption(data.pie2Option, true);
|
||||
myChart2.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
let myChart3 = markRaw(echarts.init(unref(homePieChart3) as HTMLDivElement));
|
||||
myChart3.setOption(data.pie3Option, true);
|
||||
myChart3.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
let myChart4 = markRaw(echarts.init(unref(homeLine1Chart) as HTMLDivElement));
|
||||
myChart4.setOption(data.line1Option, true);
|
||||
myChart4.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
let myChart5 = markRaw(echarts.init(unref(homeRadarChart) as HTMLDivElement));
|
||||
myChart5.setOption(data.barOption, true);
|
||||
myChart5.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
let myChart6 = markRaw(echarts.init(unref(homeLineChart) as HTMLDivElement));
|
||||
myChart6.setOption(data.lineOption, true);
|
||||
myChart6.resize(); //显示区域大小发生改变更新图表
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.title-chose {
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
|
||||
span {
|
||||
text-align: center;
|
||||
padding: 0 12px;
|
||||
display: inline-block;
|
||||
color: #333;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-left: 10px;
|
||||
|
||||
&.cur,
|
||||
&:active {
|
||||
background-color: #5e95ff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.box-bg {
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 3px 6px 1px rgb(0 0 0 / 10%);
|
||||
|
||||
.box-bg-title {
|
||||
border-bottom: 1px solid #eee;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 10px 10px 0;
|
||||
height: 312px;
|
||||
}
|
||||
}
|
||||
|
||||
.mumber-box {
|
||||
margin: 2px -5px 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.mumber {
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
margin: 0 5px;
|
||||
flex: 1;
|
||||
height: 120px;
|
||||
background: #fff;
|
||||
box-shadow: 0 3px 6px 1px rgb(0 0 0 / 10%);
|
||||
border-radius: 5px;
|
||||
|
||||
.mum-left {
|
||||
float: left;
|
||||
width: 24%;
|
||||
margin-top: 6px;
|
||||
|
||||
.index-img {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mum-right {
|
||||
margin-left: 6%;
|
||||
float: left;
|
||||
width: 70%;
|
||||
|
||||
.mum-title {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mum {
|
||||
font-size: 24px;
|
||||
font-weight: bolder;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.mum-than {
|
||||
font-size: 14px;
|
||||
|
||||
.icon {
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-col {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
783
src/views/erp/report/PurchaseAnalysis.vue
Normal file
783
src/views/erp/report/PurchaseAnalysis.vue
Normal file
@ -0,0 +1,783 @@
|
||||
<template>
|
||||
<div style="padding: 10px; box-sizing: border-box">
|
||||
<div>
|
||||
<div class="mumber-box">
|
||||
<div class="mumber" v-for="(it, index) in data.Statistics" :key="index">
|
||||
<div class="mum-title">{{ it.name }}(吨)</div>
|
||||
<div class="mum-right">
|
||||
<div class="mum"> <div>计划采购数量</div>{{ it.plan }} </div>
|
||||
<div class="mum"> <div>实际采购数量</div>{{ it.actual }} </div>
|
||||
<div class="mum-than">
|
||||
<div class="bor">
|
||||
<div :style="{ color: it.isUp ? '#12B700' : '#FF8080' }">
|
||||
{{ it.pecent }}%
|
||||
<Icon :icon="it.isUp ? 'fa-level-up' : 'fa-level-down'" :size="14" class="icon" />
|
||||
</div>
|
||||
偏差值
|
||||
</div>
|
||||
<div class="bor border1"></div>
|
||||
<div class="bor border2"></div>
|
||||
<div class="bor border3"></div>
|
||||
<div class="bor border4"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-row :gutter="10">
|
||||
<a-col :span="7">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">采购成本分析(金额)</span>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="unit">单位:万元</div>
|
||||
<div>
|
||||
<div class="pucharse" v-for="it in data.barData" :key="it.name">
|
||||
<span class="labels">{{ it.name }}</span>
|
||||
<div class="values">
|
||||
<div class="actual" :style="{ width: (it.actual / it.plan) * 100 + '%' }"></div>
|
||||
<div style="position: absolute; right: 10px">{{ it.actual }}/{{ it.plan }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="11">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">本年采购价格偏差分析</span>
|
||||
<div class="title-chose">
|
||||
<span
|
||||
v-for="item in data.priceArray"
|
||||
:key="item.key"
|
||||
:class="item.key == data.cur ? 'cur' : ''"
|
||||
@click="changePrice(item, 1)"
|
||||
>{{ item.name }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item" ref="homeBarlineChart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">采购合同管理</span>
|
||||
</div>
|
||||
<div class="item" ref="homePieChart2"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">原材料采购价格环比</span>
|
||||
</div>
|
||||
<div class="item" ref="homeLineChart2"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="11">
|
||||
<div class="box-bg" style="position: relative">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">供应链趋势</span>
|
||||
<div class="title-chose">
|
||||
<span
|
||||
v-for="item in data.priceArray2"
|
||||
:key="item.key"
|
||||
:class="item.key == data.cur2 ? 'cur' : ''"
|
||||
@click="changePrice(item, 2)"
|
||||
>{{ item.name }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item" ref="homeLineChart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">采购合同类型占比</span>
|
||||
</div>
|
||||
<div class="item" ref="homePieChart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, nextTick, reactive, ref, unref, markRaw, computed } from 'vue';
|
||||
import {
|
||||
getDataInfo,
|
||||
getPriceInfo,
|
||||
getTrendInfo,
|
||||
getTypeInfo,
|
||||
getCostInfo,
|
||||
getDeviationInfo,
|
||||
getContractInfo,
|
||||
} from '/@/api/erp/report/purchase';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
const formatePie = (name) => {
|
||||
let str = name + '(';
|
||||
data.pieData.forEach((o: any) => {
|
||||
if (name == o.name) {
|
||||
str += o.value + ')';
|
||||
}
|
||||
});
|
||||
return str;
|
||||
};
|
||||
const data = reactive({
|
||||
colors: ['#5E95FF', '#00CACF', '#FF9100', '#995EFF', '#5E6EFF'],
|
||||
Statistics: [] as any[],
|
||||
barData: [] as any[],
|
||||
pieData: [],
|
||||
priceArray: [] as any[],
|
||||
priceArray2: [] as any[],
|
||||
cur: 0,
|
||||
cur2: 0,
|
||||
myChart: null as any,
|
||||
myChart4: null as any,
|
||||
// 玫瑰饼图
|
||||
pieOption: {
|
||||
color: ['#FF8080', '#9863C1', '#00CACF', '#5E95FF', '#FF9100'],
|
||||
series: {
|
||||
name: '',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
selectedMode: 'single',
|
||||
center: ['50%', '50%'],
|
||||
label: {
|
||||
color: '#6e7079',
|
||||
overflow: 'none',
|
||||
formatter: '{b} {d}%\n{c}',
|
||||
},
|
||||
data: [] as any,
|
||||
},
|
||||
},
|
||||
|
||||
// 占比饼图
|
||||
pie2Option: {
|
||||
color: ['#5E95FF', '#FF9100', '#995EFF', '#00CACF'],
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
backgroundColor: 'rgba(51, 51, 51, 0.7)',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
},
|
||||
formatter: '{b} {d}%<br/>' + '{c}',
|
||||
},
|
||||
legend: {
|
||||
bottom: '10%',
|
||||
left: 'center',
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
},
|
||||
formatter: formatePie,
|
||||
},
|
||||
series: {
|
||||
type: 'pie',
|
||||
radius: ['40%', '60%'],
|
||||
center: ['50%', '38%'],
|
||||
avoidLabelOverlap: false,
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
data: [] as any[],
|
||||
},
|
||||
},
|
||||
|
||||
barOption2: {
|
||||
color: ['#00CACF', '#5E95FF'],
|
||||
title: {
|
||||
text: '单位:万元',
|
||||
left: 20,
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontWeight: 'normal',
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(51, 51, 51, 0.7)',
|
||||
formatter: (params) => {
|
||||
let str = '';
|
||||
params.forEach((o) => {
|
||||
str +=
|
||||
o.marker +
|
||||
'<span style="display:inline-flex;justify-content: space-between;width:138px;color:#fff">' +
|
||||
o.seriesName +
|
||||
'<span>' +
|
||||
o.data +
|
||||
'万元</span></span><br/>';
|
||||
});
|
||||
return str;
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
right: 20,
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
},
|
||||
data: ['2021年', '2022年'],
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: [],
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '',
|
||||
min: 0,
|
||||
max: 45,
|
||||
interval: 15,
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '',
|
||||
axisLabel: {
|
||||
formatter: '{value}%',
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [] as any,
|
||||
},
|
||||
});
|
||||
|
||||
const homePieChart2 = ref<HTMLDivElement>();
|
||||
const homeBarlineChart = ref<HTMLDivElement>();
|
||||
const homeLineChart2 = ref<HTMLDivElement>();
|
||||
const homeLineChart = ref<HTMLDivElement>();
|
||||
const homePieChart = ref<HTMLDivElement>();
|
||||
|
||||
const lineOption = computed(() => {
|
||||
let access = data.priceArray2[data.cur2].data.access;
|
||||
let dieOut = data.priceArray2[data.cur2].data.dieOut;
|
||||
let total = dieOut ? access.concat(dieOut) : [10];
|
||||
let max = Math.max.apply(null, total);
|
||||
return {
|
||||
color: ['#00CACF', '#995EFF', '#FF9100'],
|
||||
title: {
|
||||
text: '单位:万元',
|
||||
left: 20,
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontWeight: 'normal',
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'none',
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
itemWidth: 20,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
right: 20,
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
},
|
||||
data: ['供应链准入', '供应链淘汰/冻结', '偏差率'],
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: data.priceArray2[data.cur2].category,
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '',
|
||||
min: 0,
|
||||
max: max,
|
||||
interval: 50,
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '',
|
||||
min: 0,
|
||||
max: max / 10,
|
||||
interval: 5,
|
||||
axisLabel: {
|
||||
formatter: '{value}%',
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '供应链准入',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(0, 202, 207, .7)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(255, 255, 255, .7)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
tooltip: {
|
||||
valueFormatter: function (value) {
|
||||
return value + ' 万元';
|
||||
},
|
||||
},
|
||||
data: access,
|
||||
},
|
||||
{
|
||||
name: '供应链淘汰/冻结',
|
||||
type: 'line',
|
||||
stack: 'Total',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(152, 99, 193, .7)',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(255, 255, 255, .7)',
|
||||
},
|
||||
]),
|
||||
},
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
tooltip: {
|
||||
valueFormatter: function (value) {
|
||||
return value + ' 万元';
|
||||
},
|
||||
},
|
||||
data: dieOut,
|
||||
},
|
||||
{
|
||||
name: '偏差率',
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
showSymbol: false,
|
||||
tooltip: {
|
||||
valueFormatter: function (value) {
|
||||
return value + ' 万元';
|
||||
},
|
||||
},
|
||||
data: data.priceArray2[data.cur2].data.deviationRate,
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
const barOption = computed(() => {
|
||||
let plan = data.priceArray[data.cur].data.plan;
|
||||
let actual = data.priceArray[data.cur].data.actual;
|
||||
let deviationRate = data.priceArray[data.cur].data.deviationRate;
|
||||
let total = actual ? plan.concat(actual) : [10];
|
||||
let max = Math.max.apply(null, total);
|
||||
return {
|
||||
color: ['#00CACF', '#5E95FF', '#FF9100'],
|
||||
title: {
|
||||
text: '单位:万元',
|
||||
left: 20,
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontWeight: 'normal',
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 30,
|
||||
bottom: 20,
|
||||
containLabel: true,
|
||||
},
|
||||
legend: {
|
||||
itemWidth: 20,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
right: 20,
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
},
|
||||
data: ['预计利润', '实际利润', '偏差率'],
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: data.priceArray[data.cur].category,
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '',
|
||||
// minInterval: 1,
|
||||
min: 0,
|
||||
max: max,
|
||||
interval: 50,
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '',
|
||||
min: 0,
|
||||
max: max / 10,
|
||||
interval: 5,
|
||||
axisLabel: {
|
||||
formatter: '{value} %',
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '预计利润',
|
||||
type: 'bar',
|
||||
barWidth: data.cur === 1 ? 20 : 'auto',
|
||||
data: plan,
|
||||
},
|
||||
{
|
||||
name: '实际利润',
|
||||
type: 'bar',
|
||||
barWidth: data.cur === 1 ? 20 : 'auto',
|
||||
data: actual,
|
||||
},
|
||||
{
|
||||
name: '偏差率',
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
data: deviationRate,
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
let res = await getDataInfo();
|
||||
data.Statistics = res;
|
||||
let res1 = await getCostInfo();
|
||||
data.barData = res1;
|
||||
let res2 = await getDeviationInfo();
|
||||
data.priceArray = res2;
|
||||
let res3 = await getContractInfo();
|
||||
data.pieData = res3;
|
||||
data.pie2Option.series.data = [];
|
||||
data.pieData.forEach((o: any) => {
|
||||
let obj: any = {
|
||||
value: o.value,
|
||||
name: o.name,
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
};
|
||||
if (o.name == '已完成') {
|
||||
obj.label = {
|
||||
show: false,
|
||||
position: 'center',
|
||||
normal: {
|
||||
show: true,
|
||||
formatter: '合同完成率\n\n{d}%',
|
||||
position: 'center',
|
||||
lineHeight: 15,
|
||||
fontSize: 16,
|
||||
color: '#5E95FF',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
};
|
||||
}
|
||||
data.pie2Option.series.data.push(obj);
|
||||
});
|
||||
let res4 = await getTypeInfo();
|
||||
data.pieOption.series.data = res4;
|
||||
data.pieOption.series.data[0].selected = true;
|
||||
let res5 = await getPriceInfo();
|
||||
data.barOption2.xAxis.data = res5.category;
|
||||
res5.series.forEach((o) => {
|
||||
if (o.name == '偏差率') {
|
||||
data.barOption2.series.push({
|
||||
name: '',
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
lineStyle: {
|
||||
color: 'transparent',
|
||||
},
|
||||
showSymbol: false,
|
||||
tooltip: {
|
||||
show: false,
|
||||
},
|
||||
data: o.data,
|
||||
});
|
||||
} else {
|
||||
data.barOption2.series.push({
|
||||
name: o.name,
|
||||
type: 'bar',
|
||||
tooltip: {
|
||||
valueFormatter: function (value) {
|
||||
return value + ' 万元';
|
||||
},
|
||||
},
|
||||
data: o.data,
|
||||
});
|
||||
}
|
||||
});
|
||||
let res6 = await getTrendInfo();
|
||||
data.priceArray2 = res6;
|
||||
|
||||
nextTick(() => {
|
||||
let myChart3 = markRaw(echarts.init(unref(homePieChart2) as HTMLDivElement));
|
||||
myChart3.setOption(data.pie2Option, true);
|
||||
myChart3.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
data.myChart = markRaw(echarts.init(unref(homeBarlineChart) as HTMLDivElement));
|
||||
data.myChart.setOption(unref(barOption), true);
|
||||
data.myChart.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
let myChart2 = markRaw(echarts.init(unref(homeLineChart2) as HTMLDivElement));
|
||||
myChart2.setOption(data.barOption2, true);
|
||||
myChart2.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
data.myChart4 = markRaw(echarts.init(unref(homeLineChart) as HTMLDivElement));
|
||||
data.myChart4.setOption(unref(lineOption), true);
|
||||
data.myChart4.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
let myChart5 = markRaw(echarts.init(unref(homePieChart) as HTMLDivElement));
|
||||
myChart5.setOption(data.pieOption, true);
|
||||
myChart5.resize(); //显示区域大小发生改变更新图表
|
||||
});
|
||||
});
|
||||
|
||||
const changePrice = (o, i) => {
|
||||
if (i == 1) {
|
||||
data.cur = o.key;
|
||||
data.myChart.setOption(unref(barOption), true);
|
||||
data.myChart.resize(); //显示区域大小发生改变更新图表
|
||||
} else {
|
||||
data.cur2 = o.key;
|
||||
data.myChart4.setOption(unref(lineOption), true);
|
||||
data.myChart4.resize(); //显示区域大小发生改变更新图表
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.title-chose {
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
|
||||
span {
|
||||
text-align: center;
|
||||
padding: 0 12px;
|
||||
display: inline-block;
|
||||
color: #333;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-left: 10px;
|
||||
|
||||
&.cur,
|
||||
&:active {
|
||||
background-color: #5e95ff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.box-bg {
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 3px 6px 1px rgb(0 0 0 / 10%);
|
||||
|
||||
.box-bg-title {
|
||||
border-bottom: 1px solid #eee;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 10px 10px 0;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.unit {
|
||||
font-size: 12px;
|
||||
color: #707070;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.pucharse {
|
||||
display: flex;
|
||||
margin-bottom: 24px;
|
||||
padding: 0 10px;
|
||||
|
||||
.labels {
|
||||
width: 60px;
|
||||
font-size: 12px;
|
||||
color: #707070;
|
||||
line-height: 24px;
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.values {
|
||||
background: rgb(94 149 255 / 50%);
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
text-align: right;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
|
||||
.actual {
|
||||
background: linear-gradient(270deg, #5e95ff 0%, #695eff 100%);
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgb(255 196 96 / 50%);
|
||||
|
||||
.actual {
|
||||
background: linear-gradient(270deg, #ffc45e 0%, #ff9100 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mumber-box {
|
||||
margin: 2px -5px 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.mumber {
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
margin: 0 5px;
|
||||
flex: 1;
|
||||
height: 164px;
|
||||
background: #fff;
|
||||
box-shadow: 0 3px 6px 1px rgb(0 0 0 / 10%);
|
||||
border-radius: 5px;
|
||||
|
||||
.mum-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.mum-right {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-top: 25px;
|
||||
|
||||
.mum {
|
||||
font-size: 24px;
|
||||
font-weight: bolder;
|
||||
line-height: 40px;
|
||||
color: #5e95ff;
|
||||
|
||||
div {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.mum-than {
|
||||
font-size: 14px;
|
||||
width: 77px;
|
||||
height: 59px;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgb(255 255 255 / 50%) 0%,
|
||||
rgb(94 149 255 / 50%) 100%
|
||||
);
|
||||
margin-top: 14px;
|
||||
color: #5e95ff;
|
||||
position: relative;
|
||||
border: 1px solid #5e95ff;
|
||||
|
||||
.bor {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
div {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
|
||||
& > div {
|
||||
font-size: 16px;
|
||||
margin: 4px 0 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.border1 {
|
||||
border-left: 1px solid #fff;
|
||||
height: 38px;
|
||||
width: 0;
|
||||
left: -1px;
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
.border2 {
|
||||
border-left: 1px solid #fff;
|
||||
height: 38px;
|
||||
width: 0;
|
||||
right: -1px;
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
.border3 {
|
||||
border-top: 1px solid #fff;
|
||||
width: 61px;
|
||||
top: -1px;
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.border4 {
|
||||
border-top: 1px solid #fff;
|
||||
width: 61px;
|
||||
bottom: -1px;
|
||||
left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-col {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
580
src/views/erp/report/SalesAnalysis.vue
Normal file
580
src/views/erp/report/SalesAnalysis.vue
Normal file
@ -0,0 +1,580 @@
|
||||
<template>
|
||||
<div style="padding: 10px; box-sizing: border-box">
|
||||
<div>
|
||||
<div class="mumber-box">
|
||||
<div class="mumber" v-for="(it, index) in data.Statistics" :key="index">
|
||||
<div class="mum-left">
|
||||
<img class="index-img" :src="'/src/assets/erp/index-icon' + (index + 1) + '.svg'" />
|
||||
</div>
|
||||
<div class="mum-right">
|
||||
<div class="mum-title">{{ it.name }}(万元)</div>
|
||||
<div class="mum">{{ it.num }}</div>
|
||||
<div class="mum-than">
|
||||
{{ index > 1 ? '环比' : '同比' }} {{ it.pecent }}%
|
||||
<Icon
|
||||
:icon="it.isUp ? 'ant-design:caret-up-outlined' : 'ant-design:caret-down-outlined'"
|
||||
:color="it.isUp ? '#3dba84' : '#ee3b3b'"
|
||||
:size="14"
|
||||
class="icon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-row :gutter="10">
|
||||
<a-col :span="6">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">本月销售占比</span>
|
||||
</div>
|
||||
<div class="item" ref="homePieChart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="18">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">销售分析</span>
|
||||
</div>
|
||||
<div class="item" ref="homeLineChart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">本月收入占比</span>
|
||||
</div>
|
||||
<div class="item" ref="homePieChart2"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<div class="box-bg" style="position: relative">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">出库产品分析</span>
|
||||
</div>
|
||||
<div class="total">
|
||||
<div>{{ data.total }}万元</div>
|
||||
<div class="text">总出库产品金额</div>
|
||||
</div>
|
||||
<div class="item" ref="homeBar2Chart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="11">
|
||||
<div class="box-bg">
|
||||
<div class="box-bg-title">
|
||||
<span class="title-text">本年利润偏差分析</span>
|
||||
</div>
|
||||
<div class="item" ref="homeBarChart"></div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, nextTick, reactive, ref, unref, markRaw } from 'vue';
|
||||
import {
|
||||
getSaleDataInfo,
|
||||
getProportionSaleInfo,
|
||||
getSaleAnalysisInfo,
|
||||
getProportionIncomeInfo,
|
||||
getProductAnalysisInfo,
|
||||
getProfitInfo,
|
||||
} from '/@/api/erp/report/sales';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
const formate = (name) => {
|
||||
let str = name + ':';
|
||||
data.dataOne.forEach((o: any) => {
|
||||
if (name == o.name) {
|
||||
str += o.value + '万元';
|
||||
}
|
||||
});
|
||||
return str;
|
||||
};
|
||||
const data = reactive({
|
||||
colors: ['#5E95FF', '#00CACF', '#FF9100', '#995EFF', '#5E6EFF'],
|
||||
Statistics: [] as any[],
|
||||
dataOne: [],
|
||||
// 玫瑰饼图
|
||||
pieOption: {
|
||||
color: ['#7EAAFF', '#5887E3', '#5E75E6', '#6D68F8', '#9E9BFC', '#8CB1F9'],
|
||||
legend: {
|
||||
orient: 'horizontal',
|
||||
x: 'center',
|
||||
y: 'bottom',
|
||||
top: '78%',
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
padding: [5, 5, 5, 5],
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
},
|
||||
formatter: formate,
|
||||
},
|
||||
series: {
|
||||
name: '',
|
||||
center: ['50%', '32%'],
|
||||
type: 'pie',
|
||||
radius: [0, 85],
|
||||
roseType: 'area',
|
||||
itemStyle: {
|
||||
borderRadius: 0,
|
||||
},
|
||||
label: {
|
||||
color: '#6e7079',
|
||||
overflow: 'none',
|
||||
formatter: '{b}\n{d}%',
|
||||
},
|
||||
data: [],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
lineOption: {
|
||||
color: ['#5E95FF', '#995EFF'],
|
||||
title: {
|
||||
text: '单位:%',
|
||||
left: 20,
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontWeight: 'normal',
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
itemWidth: 20,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
right: 20,
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: [] as any[],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [] as any[],
|
||||
},
|
||||
|
||||
// 占比饼图
|
||||
pie2Option: {
|
||||
color: ['#00FFE5', '#5E95FF', '#995EFF'],
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
backgroundColor: 'rgba(51, 51, 51, 0.7)',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
},
|
||||
formatter: '{b}<br/>{c}' + '万元<br/>' + '{d}%',
|
||||
},
|
||||
legend: {
|
||||
bottom: '10%',
|
||||
left: 'center',
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
},
|
||||
},
|
||||
series: {
|
||||
type: 'pie',
|
||||
radius: ['40%', '60%'],
|
||||
center: ['50%', '38%'],
|
||||
avoidLabelOverlap: false,
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
data: [] as any[],
|
||||
},
|
||||
},
|
||||
|
||||
bar2Option: {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'none',
|
||||
},
|
||||
backgroundColor: 'rgba(51, 51, 51, 0.7)',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
},
|
||||
formatter: '{b}<br/>{c}' + '万元',
|
||||
},
|
||||
grid: {
|
||||
top: 90,
|
||||
left: 20,
|
||||
right: 30,
|
||||
bottom: 20,
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
data: [] as any[],
|
||||
axisTick: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yAxis: {},
|
||||
series: {
|
||||
name: '',
|
||||
type: 'pictorialBar',
|
||||
showBackground: true,
|
||||
backgroundStyle: {
|
||||
color: 'rgba(221, 221, 221, 0.2)',
|
||||
},
|
||||
symbol: 'path://M0,10 L10,10 C5.5,10 5.5,5 5,0 C4.5,5 4.5,10 0,10 z',
|
||||
itemStyle: {
|
||||
opacity: 1,
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#5E95FF', // 0% 处的颜色
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#695EFF', // 100% 处的颜色
|
||||
},
|
||||
],
|
||||
global: false, // 缺省为 false
|
||||
},
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
opacity: 1,
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: '#FFF45E', // 0% 处的颜色
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#FF9100', // 100% 处的颜色
|
||||
},
|
||||
],
|
||||
global: false, // 缺省为 false
|
||||
},
|
||||
},
|
||||
},
|
||||
data: [] as any[],
|
||||
z: 10,
|
||||
},
|
||||
},
|
||||
total: 0,
|
||||
|
||||
barOption: {
|
||||
color: ['#00CACF', '#5E95FF', '#FF9100'],
|
||||
title: {
|
||||
text: '单位:万元',
|
||||
left: 20,
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontWeight: 'normal',
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: 20,
|
||||
right: 30,
|
||||
bottom: 20,
|
||||
containLabel: true,
|
||||
},
|
||||
legend: {
|
||||
itemWidth: 20,
|
||||
itemHeight: 10,
|
||||
itemGap: 20,
|
||||
right: 20,
|
||||
textStyle: {
|
||||
color: '#85878e',
|
||||
},
|
||||
data: ['预计利润', '实际利润', '偏差率'],
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: [] as any[],
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '',
|
||||
min: 0,
|
||||
max: 0,
|
||||
interval: 50,
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '',
|
||||
axisLabel: {
|
||||
formatter: '{value} %',
|
||||
},
|
||||
},
|
||||
],
|
||||
series: [] as any[],
|
||||
},
|
||||
});
|
||||
|
||||
const homePieChart = ref<HTMLDivElement>();
|
||||
const homePieChart2 = ref<HTMLDivElement>();
|
||||
const homeLineChart = ref<HTMLDivElement>();
|
||||
const homeBar2Chart = ref<HTMLDivElement>();
|
||||
const homeBarChart = ref<HTMLDivElement>();
|
||||
|
||||
onMounted(async () => {
|
||||
let res = await getSaleDataInfo();
|
||||
data.Statistics = res || [];
|
||||
let res1 = await getProportionSaleInfo();
|
||||
data.dataOne = res1 || [];
|
||||
data.pieOption.series.data = data.dataOne;
|
||||
let res2 = await getSaleAnalysisInfo();
|
||||
data.lineOption.series = [];
|
||||
data.lineOption.xAxis.data = [];
|
||||
res2.forEach((o, i) => {
|
||||
let arr: any[] = [];
|
||||
o.data.forEach((k: any) => {
|
||||
arr.push(k.value);
|
||||
if (i == 1) data.lineOption.xAxis.data.push(k.name);
|
||||
});
|
||||
let obj = {
|
||||
name: o.name,
|
||||
type: 'line',
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: i === 0 ? '#5E95FF' : '#995EFF',
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: '#fff',
|
||||
},
|
||||
]),
|
||||
},
|
||||
smooth: true,
|
||||
data: arr,
|
||||
};
|
||||
data.lineOption.series.push(obj);
|
||||
});
|
||||
let res3 = await getProportionIncomeInfo();
|
||||
res3.forEach((o) => {
|
||||
let obj = {
|
||||
name: o.name,
|
||||
value: o.value,
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
};
|
||||
data.pie2Option.series.data.push(obj);
|
||||
});
|
||||
let res4 = await getProductAnalysisInfo();
|
||||
res4.forEach((o) => {
|
||||
data.bar2Option.xAxis.data.push(o.name);
|
||||
data.bar2Option.series.data.push(o.value);
|
||||
data.total += o.value;
|
||||
});
|
||||
let res5 = await getProfitInfo();
|
||||
let total: any = [];
|
||||
res5.forEach((o, i) => {
|
||||
let arr: any[] = [];
|
||||
o.data.forEach((k: any) => {
|
||||
arr.push(k.value);
|
||||
if (i == 1) data.barOption.xAxis.data.push(k.name);
|
||||
});
|
||||
let obj: any = {
|
||||
name: o.name,
|
||||
type: o.name == '偏差率' ? 'line' : 'bar',
|
||||
data: arr,
|
||||
};
|
||||
if (o.name == '偏差率') {
|
||||
obj.yAxisIndex = 1;
|
||||
} else {
|
||||
total = total.concat(arr);
|
||||
}
|
||||
data.barOption.series.push(obj);
|
||||
});
|
||||
data.barOption.yAxis[0].max = Math.max.apply(null, total);
|
||||
|
||||
nextTick(() => {
|
||||
let myChart = markRaw(echarts.init(unref(homePieChart) as HTMLDivElement));
|
||||
myChart.setOption(data.pieOption, true);
|
||||
myChart.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
let myChart2 = markRaw(echarts.init(unref(homePieChart2) as HTMLDivElement));
|
||||
myChart2.setOption(data.pie2Option, true);
|
||||
myChart2.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
let myChart4 = markRaw(echarts.init(unref(homeLineChart) as HTMLDivElement));
|
||||
myChart4.setOption(data.lineOption, true);
|
||||
myChart4.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
let myChart5 = markRaw(echarts.init(unref(homeBar2Chart) as HTMLDivElement));
|
||||
myChart5.setOption(data.bar2Option, true);
|
||||
myChart5.resize(); //显示区域大小发生改变更新图表
|
||||
|
||||
let myChart6 = markRaw(echarts.init(unref(homeBarChart) as HTMLDivElement));
|
||||
myChart6.setOption(data.barOption, true);
|
||||
myChart6.resize(); //显示区域大小发生改变更新图表
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.title-chose {
|
||||
float: right;
|
||||
margin-right: 20px;
|
||||
|
||||
span {
|
||||
text-align: center;
|
||||
padding: 0 12px;
|
||||
display: inline-block;
|
||||
color: #333;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-left: 10px;
|
||||
|
||||
&.cur,
|
||||
&:active {
|
||||
background-color: #5e95ff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.box-bg {
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 3px 6px 1px rgb(0 0 0 / 10%);
|
||||
|
||||
.total {
|
||||
margin: 20px 20px 0;
|
||||
height: 46px;
|
||||
padding: 0 10px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
box-shadow: 0 3px 6px 1px rgb(0 0 0 / 16%);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 30px;
|
||||
font-weight: 400;
|
||||
align-items: center;
|
||||
color: #5e95ff;
|
||||
|
||||
.text {
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
color: #707070;
|
||||
}
|
||||
}
|
||||
|
||||
.box-bg-title {
|
||||
border-bottom: 1px solid #eee;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 10px 10px 0;
|
||||
height: 312px;
|
||||
}
|
||||
}
|
||||
|
||||
.mumber-box {
|
||||
margin: 2px -5px 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.mumber {
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
margin: 0 5px;
|
||||
flex: 1;
|
||||
height: 120px;
|
||||
background: #fff;
|
||||
box-shadow: 0 3px 6px 1px rgb(0 0 0 / 10%);
|
||||
border-radius: 5px;
|
||||
|
||||
.mum-left {
|
||||
float: left;
|
||||
width: 24%;
|
||||
margin-top: 6px;
|
||||
|
||||
.index-img {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mum-right {
|
||||
margin-left: 6%;
|
||||
float: left;
|
||||
width: 70%;
|
||||
|
||||
.mum-title {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mum {
|
||||
font-size: 24px;
|
||||
font-weight: bolder;
|
||||
line-height: 40px;
|
||||
color: #5e95ff;
|
||||
}
|
||||
|
||||
.mum-than {
|
||||
font-size: 14px;
|
||||
|
||||
.icon {
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-col {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user