统计模块的订单分析

This commit is contained in:
yis 2025-08-04 11:31:52 +08:00
parent 2f163bafd9
commit 499d80ab0e

View File

@ -12,7 +12,7 @@
<div class="card-header">
<span class="card-label">总订单量</span>
<div class="card-icon">
<svg-icon icon-class="shopping" class="text-blue-600" />
<svg-icon icon-class="shopping" />
</div>
</div>
<div class="card-value">{{ summaryData.totalOrderCount }}</div>
@ -23,7 +23,7 @@
<div class="card-header">
<span class="card-label">订单总金额</span>
<div class="card-icon">
<svg-icon icon-class="money" class="text-green-600" />
<svg-icon icon-class="money" />
</div>
</div>
<div class="card-value">¥{{ summaryData.totalOrderAmount }}</div>
@ -125,7 +125,7 @@
<div class="double-chart-section">
<!-- 无人机型号分布图 -->
<div class="chart-box">
<h3 class="chart-title">无人机型号分布</h3>
<h3 class="chart-title">无人机型号订单占比</h3>
<div class="chart-wrapper" ref="modelChartContainer"></div>
</div>
@ -493,9 +493,9 @@ export default {
}
console.log('接口响应数据:', response);
this.summaryData.totalOrderAmount = response.totalOrderAmount;
this.summaryData.totalOrderCount = response.totalOrderCount;
this.processResponseData(response.data);
this.processResponseData(response);
} catch (error) {
console.error('获取订单分析数据失败:', error);
this.$message.error('获取数据失败,请重试');
@ -599,15 +599,17 @@ export default {
break;
}
}
//
const maxValue = Math.max(...seriesData);
const maxIndex = seriesData.indexOf(maxValue);
const option = {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: params => {
const value = params[0].value;
const axisValue = params[0].axisValue;
return `${axisValue}<br/>订单量: ${value}`;
return `${axisValue}<br/>当前订单量: ${value}`;
}
},
grid: {
@ -618,20 +620,25 @@ export default {
},
xAxis: {
type: 'category',
name:'时间',
data: xAxisData,
axisTick: {
show: true,
inside: true, //
},
axisLabel: {
rotate: xAxisData.length > 10 ? 45 : 0,
formatter: value => {
switch (this.filter.timeRange) {
case 'day': return value.split('-').slice(1).join('-');
case 'month': return value.split('-')[1];
case 'year': return value;
case 'month': return value.split('-')[1]+ '月';
case 'year': return value+ '年';
default: return value;
}
}
}
},
yAxis: { type: 'value', minInterval: 1 },
yAxis: { type: 'value', minInterval: 1,name: '订单数量'},
series: [{
name: '订单数量',
data: seriesData,
@ -639,11 +646,23 @@ export default {
smooth: true,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(58, 77, 233, 0.8)' },
{ offset: 1, color: 'rgba(58, 77, 233, 0.1)' }
{ offset: 0, color: 'rgb(121, 165, 247,0.5)' },
{ offset: 1, color: 'rgb(234, 241, 253, 0.1)' }
])
},
itemStyle: { color: '#3a4de9' }
axisTick: {
show: true,
inside: true, //
},
itemStyle: { color: '#4E80EE' },
markPoint: {
data: [{
type: 'max',
label: {
formatter: '{c}'
}
}]
}
}]
};
@ -673,13 +692,14 @@ export default {
legend: {
orient: 'vertical',
right: 10,
top: 'center'
top: 'center',
itemGap: 10
},
series: [{
name: '无人机型号分布',
name: '无人机型号分布',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
center: ['40%', '50%'],
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
@ -705,72 +725,102 @@ export default {
},
updateRouteChart(routeDistribution) {
if (!this.routeChart) return;
// 0线
const topRoutes = (routeDistribution || [])
.filter(item => item.count > 0)
.sort((a, b) => b.count - a.count)
.slice(0, 10);
if (topRoutes.length === 0) {
this.routeChart.setOption({
title: {
text: '暂无数据',
left: 'center',
top: 'center'
}
});
return;
}
this.routeChart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: params => {
const route = topRoutes[params[0].dataIndex];
return `路线: ${route.routeName || '未知路线'}<br>景区: ${route.scenicName || '未知景区'}<br>订单量: ${params[0].data}`;
if (!this.routeChart) return;
// 1.
const topRoutes = (routeDistribution || [])
.filter(item => item.count > 0)
.sort((a, b) => b.count - a.count)
.slice(0, 10);
if (topRoutes.length === 0) {
this.routeChart.setOption({
title: {
text: '暂无数据',
left: 'center',
top: 'center'
}
});
return;
}
// 2.
const maxValue = Math.max(...topRoutes.map(item => item.count));
const colorGradient = (value) => {
const ratio = value / maxValue;
return new echarts.graphic.LinearGradient(0, 0, 0,1, [
{ offset: 0, color: `rgba(9, 71, 179, ${0.3 + ratio * 0.7})` }, //
{ offset: 1, color: `rgba(58, 119, 233, ${0.3 + ratio * 0.7})` } //
]);
};
// 3.
this.routeChart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: (params) => {
const data = params[0];
return `
<div style="font-weight:bold">${data.name}</div>
<div>订单量: ${data.value}</div>
${data.data.percentage ? `<div>完成率: ${data.data.percentage}</div>` : ''}
`;
}
},
grid: {
left: '3%',
right: '12%', //
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value',
name: '订单数量(件)',
splitLine: {
show: false // 线
},
axisTick: {
show: false //
},
max: Math.max(50, maxValue) //
},
yAxis: {
type: 'category',
name: '景区路线',
nameLocation: 'start',
data: topRoutes.map(item => item.routeName),
axisLabel: {
formatter: (name) => name.length > 8 ? `${name.substring(0, 8)}...` : name
},
axisTick: {
show: false //
},
splitLine: {
show: false // 线
},
inverse: true // Y使
},
series: [{
name: '订单量',
type: 'bar',
data: topRoutes.map(item => ({
value: item.count,
name: item.routeName,
percentage: item.count.toString().includes('%') ? item.count : null,
itemStyle: {
color: colorGradient(item.count)
}
})),
barWidth: '40%',
label: {
show: true,
position: 'right', //
formatter: ({ data }) => data.displayValue || `${data.value}`,
}
}]
}, true);
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01],
minInterval: 1 //
},
yAxis: {
type: 'category',
data: topRoutes.map(item => item.routeName || `路线 ${item.routeId}`),
axisLabel: {
formatter: value => value.length > 6 ? value.substring(0, 6) + '...' : value
}
},
series: [{
name: '订单量',
type: 'bar',
data: topRoutes.map(item => item.count),
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: '#83bff6' },
{ offset: 0.5, color: '#188df0' },
{ offset: 1, color: '#0052d9' }
]),
borderRadius: [0, 5, 5, 0]
},
label: {
show: true,
position: 'right',
formatter: '{c}'
}
}]
}, true);
},
handleJump() {
const page = parseInt(this.jumpPage);
@ -837,11 +887,9 @@ export default {
</script>
<style scoped>
/* 保持原有的样式不变 */
.order-analysis-container {
min-height: 100vh;
background-color: #f5f7fa;
font-family: 'Arial', sans-serif;
}
.main-content {
@ -858,61 +906,48 @@ export default {
}
.page-title {
font-size: 24px;
font-size: 20px;
font-weight: 600;
color: #333;
margin-bottom: 24px;
margin-bottom: 16px;
display: flex;
align-items: center;
}
/* 卡片布局 */
.card-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
margin-bottom: 16px;
gap: 8px;
margin-bottom: 12px;
}
.stat-card {
padding: 16px;
min-height: 70px;
border-radius: 8px;
padding: 12px;
min-height: 60px;
border-radius: 6px;
transition: transform 0.2s;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.stat-card:hover {
transform: translateY(-5px);
}
.blue-card {
background: linear-gradient(to bottom right, #ebf4ff, white);
}
.green-card {
background: linear-gradient(to bottom right, #ecfdf5, white);
}
.purple-card {
background: linear-gradient(to bottom right, #e5e2f4, white);
}
.orange-card {
background: linear-gradient(to bottom right, #f7f5e7, white);
}
/* 卡片颜色变体 */
.blue-card { background: linear-gradient(to bottom right, #ebf4ff, white) }
.green-card { background: linear-gradient(to bottom right, #ecfdf5, white) }
.purple-card { background: linear-gradient(to bottom right, #e5e2f4, white) }
.orange-card { background: linear-gradient(to bottom right, #f7f5e7, white) }
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 14px;
margin-bottom: 8px;
}
.card-label {
font-size: 18px;
font-size: 14px;
font-weight: 700;
}
.card-icon {
width: 40px;
height: 40px;
@ -920,63 +955,53 @@ export default {
display: flex;
align-items: center;
justify-content: center;
background-color: inherit;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.card-value {
font-size: 28px;
font-size: 22px;
font-weight: 700;
margin-bottom: 12px;
margin-bottom: 8px;
}
/* 筛选区域 */
.filter-section {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
padding: 16px;
margin-bottom: 16px;
padding: 12px;
background-color: #f9fafb;
border-radius: 8px;
flex-wrap: wrap;
gap: 16px;
}
.filter-group {
display: flex;
gap: 24px;
flex-wrap: wrap;
}
.filter-item {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.filter-label {
font-size: 14px;
color: #666;
margin-right: 12px;
white-space: nowrap;
}
.time-range-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.time-button {
padding: 8px 16px;
border-radius: 4px;
border: 1px solid #dcdfe6;
background: #f5f7fa;
cursor: pointer;
transition: all 0.3s;
font-size: 14px;
white-space: nowrap;
}
.time-button.active {
background: #409eff;
color: white;
@ -989,45 +1014,34 @@ export default {
gap: 10px;
}
.separator {
color: #606266;
padding: 0 5px;
/* 图表区域 */
.chart-section, .data-table-section {
margin-bottom: 24px;
}
.action-buttons {
display: flex;
align-items: center;
gap: 10px;
}
.chart-section {
margin-bottom: 32px;
}
.double-chart-section {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
margin-bottom: 32px;
margin-bottom: 24px;
}
.chart-box {
.chart-box, .data-table-section {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
padding: 20px;
padding: 16px;
}
.chart-title {
font-size: 18px;
.chart-title, .table-title {
font-weight: 500;
color: #333;
margin-bottom: 16px;
margin-bottom: 12px;
display: flex;
align-items: center;
}
.chart-title { font-size: 16px }
.table-title { font-size: 18px }
.chart-title::before {
.chart-title::before, .table-title::before {
content: '';
display: inline-block;
width: 4px;
@ -1039,39 +1053,13 @@ export default {
.chart-wrapper {
width: 100%;
height: 400px;
}
.data-table-section {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
padding: 20px;
}
.table-title {
font-size: 18px;
font-weight: 500;
color: #333;
margin-bottom: 16px;
display: flex;
align-items: center;
}
.table-title::before {
content: '';
display: inline-block;
width: 4px;
height: 16px;
background-color: #3a4de9;
margin-right: 8px;
border-radius: 2px;
height: 320px;
}
/* 表格区域 */
.table-wrapper {
overflow-x: auto;
}
.pagination-wrapper {
display: flex;
justify-content: space-between;
@ -1080,113 +1068,39 @@ export default {
padding: 10px 0;
border-top: 1px solid #ebeef5;
}
.pagination-info {
font-size: 12px;
color: #606266;
}
.pagination-jump {
display: flex;
align-items: center;
gap: 8px;
}
.jump-text {
font-size: 14px;
color: #606266;
}
.jump-input {
width: 60px;
}
.jump-input .el-input__inner {
text-align: center;
padding: 0 5px;
}
.jump-button {
padding: 8px 15px;
}
/* 响应式布局 */
@media (max-width: 1200px) {
.card-grid {
grid-template-columns: repeat(2, 1fr);
}
.double-chart-section {
grid-template-columns: 1fr;
}
.date-range-picker {
width: 280px;
}
.card-grid { grid-template-columns: repeat(2, 1fr) }
.double-chart-section { grid-template-columns: 1fr }
}
@media (max-width: 768px) {
.card-grid {
grid-template-columns: 1fr;
}
.filter-section {
flex-direction: column;
gap: 16px;
}
.filter-group {
flex-direction: column;
width: 100%;
gap: 12px;
}
.filter-item {
flex-direction: column;
align-items: flex-start;
width: 100%;
gap: 8px;
}
.date-range-picker {
width: 100%;
}
.time-range-buttons {
width: 100%;
}
.action-buttons {
width: 100%;
justify-content: flex-end;
}
.card-grid { grid-template-columns: 1fr }
.filter-section { flex-direction: column }
.filter-group, .time-range-buttons, .date-range-picker { width: 100% }
.filter-item { flex-direction: column; align-items: flex-start }
}
@media (max-width: 480px) {
.main-content {
padding: 10px;
}
.content-box {
padding: 16px;
}
.card-grid {
gap: 12px;
}
.chart-wrapper {
height: 300px;
}
.main-content { padding: 10px }
.content-box { padding: 16px }
.chart-wrapper { height: 280px }
}
</style>
</style>