This commit is contained in:
aaron 2025-02-27 23:56:45 +08:00
parent 60c90357d7
commit 96bedede04

View File

@ -92,15 +92,82 @@
</div>
</div>
<!-- 数据对比图表 -->
<div class="chart-section">
<div class="chart-header">
<div class="header-decoration"></div>
<h3>实时数据对比分析</h3>
<div class="header-decoration"></div>
<div class="bottom-sections">
<!-- 新增小区实时数据模块 -->
<div class="community-section">
<div class="section-header">
<div class="header-decoration"></div>
<h3>小区实时监控数据</h3>
<div class="header-decoration"></div>
</div>
<div class="community-table-container">
<table class="community-table">
<thead>
<tr>
<th>小区名称</th>
<th>实时用户数</th>
<th>总订单量</th>
<th>今日订单量</th>
<th>状态</th>
</tr>
</thead>
<tbody ref="communityTableBody" class="scrollable-tbody">
<tr v-for="(community, index) in communityData" :key="index" :class="{ 'active-row': index % 3 === 0 }">
<td>
<div class="community-name">
<div class="community-icon"><i class="el-icon-s-home"></i></div>
{{ community.name }}
</div>
</td>
<td>
<div class="data-with-trend">
<span class="table-value">{{ community.userCount }}</span>
<span class="table-trend" :class="getTrendClass(community.userTrend)">
<i :class="getTrendIconByValue(community.userTrend)"></i>
{{ formatTrend(community.userTrend) }}
</span>
</div>
</td>
<td>
<div class="data-with-trend">
<span class="table-value">{{ community.totalOrders }}</span>
<span class="table-trend" :class="getTrendClass(community.totalOrdersTrend)">
<i :class="getTrendIconByValue(community.totalOrdersTrend)"></i>
{{ formatTrend(community.totalOrdersTrend) }}
</span>
</div>
</td>
<td>
<div class="data-with-trend">
<span class="table-value">{{ community.todayOrders }}</span>
<span class="table-trend" :class="getTrendClass(community.todayOrdersTrend)">
<i :class="getTrendIconByValue(community.todayOrdersTrend)"></i>
{{ formatTrend(community.todayOrdersTrend) }}
</span>
</div>
</td>
<td>
<span class="status-badge" :class="getStatusClass(community.status)">
{{ community.status }}
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="chart-container">
<div ref="comparisonChart" class="chart"></div>
<!-- 数据对比图表 -->
<div class="chart-section">
<div class="chart-header">
<div class="header-decoration"></div>
<h3>实时数据对比分析</h3>
<div class="header-decoration"></div>
</div>
<div class="chart-container">
<div ref="comparisonChart" class="chart"></div>
</div>
</div>
</div>
</div>
@ -130,11 +197,13 @@ export default {
today_community_count: 0,
yesterday_community_count: 0
},
communityData: [],
loading: false,
lastUpdateTime: null,
comparisonChart: null,
currentTime: '',
timeInterval: null,
scrollInterval: null,
shouldBeautifyData: true //
};
},
@ -167,12 +236,17 @@ export default {
} catch (error) {
console.error('解析查询参数出错:', error);
}
//
this.generateMockCommunityData();
},
mounted() {
this.fetchDashboardData();
// 1
this.autoRefreshInterval = setInterval(() => {
this.fetchDashboardData();
//
this.updateCommunityData();
}, 60 * 1000);
//
@ -180,6 +254,9 @@ export default {
this.timeInterval = setInterval(() => {
this.updateCurrentTime();
}, 1000);
//
this.startTableScroll();
},
beforeDestroy() {
if (this.autoRefreshInterval) {
@ -188,11 +265,30 @@ export default {
if (this.timeInterval) {
clearInterval(this.timeInterval);
}
if (this.scrollInterval) {
clearInterval(this.scrollInterval);
}
if (this.comparisonChart) {
this.comparisonChart.dispose();
}
},
methods: {
//
startTableScroll() {
this.scrollInterval = setInterval(() => {
const tbody = this.$refs.communityTableBody;
if (tbody) {
const scrollAmount = 1; //
// 使 tbody
if (tbody.scrollTop + tbody.clientHeight >= tbody.scrollHeight) {
tbody.scrollTop = 0;
} else {
tbody.scrollTop += scrollAmount;
}
}
}, 50); //
},
updateCurrentTime() {
const now = new Date();
this.currentTime = now.toLocaleTimeString();
@ -315,28 +411,28 @@ export default {
return 'el-icon-minus';
},
initComparisonChart() {
if (!this.$refs.comparisonChart) return;
if (this.comparisonChart) {
this.comparisonChart.dispose();
}
this.comparisonChart = echarts.init(this.$refs.comparisonChart);
const chartDom = this.$refs.comparisonChart;
this.comparisonChart = echarts.init(chartDom);
const option = {
backgroundColor: 'transparent',
grid: {
top: '10%',
left: '3%',
right: '4%',
bottom: '10%',
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
shadowStyle: {
color: 'rgba(0, 180, 220, 0.1)'
}
type: 'shadow'
},
backgroundColor: 'rgba(0, 20, 40, 0.8)',
borderColor: 'rgba(0, 180, 220, 0.8)',
textStyle: {
color: '#fff'
fontSize: 12
}
},
legend: {
@ -344,26 +440,19 @@ export default {
textStyle: {
color: '#00b4dc'
},
itemStyle: {
borderColor: '#00b4dc'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
top: 0
},
xAxis: {
type: 'category',
data: ['用户', '订单', '社区'],
data: ['用户数', '订单数', '社区数'],
axisLine: {
lineStyle: {
color: '#00b4dc'
}
},
axisLabel: {
color: '#00b4dc'
color: '#00b4dc',
fontSize: 12
}
},
yAxis: {
@ -376,11 +465,12 @@ export default {
},
splitLine: {
lineStyle: {
color: 'rgba(0, 180, 220, 0.2)'
color: 'rgba(0, 180, 220, 0.1)'
}
},
axisLabel: {
color: '#00b4dc'
color: '#00b4dc',
fontSize: 12
}
},
series: [
@ -388,9 +478,9 @@ export default {
name: '今日',
type: 'bar',
data: [
this.dashboardData.today_user_count || 0,
this.dashboardData.today_order_count || 0,
this.dashboardData.today_community_count || 0
this.dashboardData.today_user_count,
this.dashboardData.today_order_count,
this.dashboardData.today_community_count
],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
@ -398,21 +488,20 @@ export default {
{ offset: 1, color: '#0066ff' }
])
},
barWidth: '20%',
barGap: '10%'
barWidth: '20%'
},
{
name: '昨日',
type: 'bar',
data: [
this.dashboardData.yesterday_user_count || 0,
this.dashboardData.yesterday_order_count || 0,
this.dashboardData.yesterday_community_count || 0
this.dashboardData.yesterday_user_count,
this.dashboardData.yesterday_order_count,
this.dashboardData.yesterday_community_count
],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#ff9a00' },
{ offset: 1, color: '#ff0000' }
{ offset: 0, color: '#ff9a9e' },
{ offset: 1, color: '#ff6b6b' }
])
},
barWidth: '20%'
@ -426,21 +515,126 @@ export default {
window.addEventListener('resize', () => {
this.comparisonChart.resize();
});
}
},
//
generateMockCommunityData() {
const communityNames = [
'阳光花园小区', '蓝天公寓', '绿地国际城', '和平家园',
'金色家园', '紫荆花园', '龙湖小区', '万科城市花园',
'恒大名都', '碧桂园'
];
this.communityData = communityNames.map((name, index) => {
//
const userCount = Math.floor(Math.random() * 500) + 100;
const totalOrders = Math.floor(Math.random() * 2000) + 500;
const todayOrders = Math.floor(Math.random() * 100) + 10;
//
const userTrend = (Math.random() - 0.3) * 10; //
const totalOrdersTrend = (Math.random() - 0.3) * 15; //
const todayOrdersTrend = (Math.random() - 0.4) * 8; //
//
const statuses = ['正常', '活跃', '火爆', '平稳'];
const status = statuses[Math.floor(Math.random() * statuses.length)];
return {
name,
userCount,
totalOrders,
todayOrders,
userTrend,
totalOrdersTrend,
todayOrdersTrend,
status
};
});
//
this.communityData.sort((a, b) => b.totalOrders - a.totalOrders);
},
//
updateCommunityData() {
this.communityData = this.communityData.map(community => {
//
const userChange = Math.floor(Math.random() * 10) - 3; // -3 6
const totalOrdersChange = Math.floor(Math.random() * 15); // 0 14
const todayOrdersChange = Math.floor(Math.random() * 5); // 0 4
//
const userTrend = (Math.random() - 0.3) * 10;
const totalOrdersTrend = (Math.random() - 0.3) * 15;
const todayOrdersTrend = (Math.random() - 0.4) * 8;
//
const statuses = ['正常', '活跃', '火爆', '平稳'];
const status = Math.random() > 0.8
? statuses[Math.floor(Math.random() * statuses.length)]
: community.status;
return {
...community,
userCount: Math.max(0, community.userCount + userChange),
totalOrders: community.totalOrders + totalOrdersChange,
todayOrders: Math.max(0, community.todayOrders + todayOrdersChange),
userTrend,
totalOrdersTrend,
todayOrdersTrend,
status
};
});
//
this.communityData.sort((a, b) => b.totalOrders - a.totalOrders);
},
//
getTrendClass(trendValue) {
if (trendValue > 0) return 'trend-up';
if (trendValue < 0) return 'trend-down';
return 'trend-flat';
},
//
getTrendIconByValue(trendValue) {
if (trendValue > 0) return 'el-icon-top';
if (trendValue < 0) return 'el-icon-bottom';
return 'el-icon-minus';
},
//
formatTrend(trendValue) {
const absValue = Math.abs(trendValue).toFixed(1);
if (trendValue > 0) return `+${absValue}%`;
if (trendValue < 0) return `-${absValue}%`;
return '0%';
},
//
getStatusClass(status) {
switch (status) {
case '火爆': return 'status-hot';
case '活跃': return 'status-active';
case '平稳': return 'status-stable';
default: return 'status-normal';
}
},
}
};
</script>
<style scoped>
.dashboard-container {
padding: 20px;
padding: 15px;
background-color: #001529;
background-image:
radial-gradient(circle at 10% 20%, rgba(0, 180, 220, 0.05) 0%, transparent 20%),
radial-gradient(circle at 90% 80%, rgba(0, 180, 220, 0.05) 0%, transparent 20%),
linear-gradient(to bottom, rgba(0, 0, 0, 0.8) 0%, rgba(0, 20, 40, 0.8) 100%);
min-height: calc(100vh - 60px);
min-height: 100vh;
color: #00b4dc;
display: flex;
flex-direction: column;
max-height: 100vh;
overflow: hidden;
}
.dashboard-header {
@ -526,7 +720,7 @@ export default {
background-color: rgba(0, 30, 60, 0.7);
border-radius: 8px;
box-shadow: 0 0 20px rgba(0, 180, 220, 0.2);
padding: 20px;
padding: 15px;
transition: all 0.3s;
border: 1px solid rgba(0, 180, 220, 0.3);
position: relative;
@ -637,7 +831,14 @@ export default {
color: #00b4dc;
}
.chart-section {
.bottom-sections {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 20px;
}
.community-section {
background-color: rgba(0, 30, 60, 0.7);
border-radius: 8px;
box-shadow: 0 0 20px rgba(0, 180, 220, 0.2);
@ -646,14 +847,168 @@ export default {
position: relative;
}
.chart-section::before {
content: '';
position: absolute;
top: 0;
left: 0;
.section-header {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.section-header h3 {
margin: 0;
font-size: 18px;
color: #00fcff;
text-shadow: 0 0 10px rgba(0, 252, 255, 0.3);
}
.community-table-container {
height: 300px;
overflow: hidden;
position: relative;
}
.community-table {
width: 100%;
height: 3px;
background: linear-gradient(90deg, transparent, #00b4dc, transparent);
border-collapse: separate;
border-spacing: 0;
color: #00b4dc;
table-layout: fixed;
}
.scrollable-tbody {
display: block;
height: 250px;
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin;
scrollbar-color: rgba(0, 180, 220, 0.5) rgba(0, 30, 60, 0.3);
}
/* 自定义滚动条样式 */
.scrollable-tbody::-webkit-scrollbar {
width: 6px;
}
.scrollable-tbody::-webkit-scrollbar-track {
background: rgba(0, 30, 60, 0.3);
}
.scrollable-tbody::-webkit-scrollbar-thumb {
background-color: rgba(0, 180, 220, 0.5);
border-radius: 3px;
}
.community-table thead {
display: table;
width: calc(100% - 6px); /* 减去滚动条宽度 */
table-layout: fixed;
}
.community-table tbody tr {
display: table;
width: 100%;
table-layout: fixed;
}
/* 设置列宽 */
.community-table th:nth-child(1),
.community-table td:nth-child(1) {
width: 25%;
}
.community-table th:nth-child(2),
.community-table td:nth-child(2),
.community-table th:nth-child(3),
.community-table td:nth-child(3),
.community-table th:nth-child(4),
.community-table td:nth-child(4) {
width: 20%;
}
.community-table th:nth-child(5),
.community-table td:nth-child(5) {
width: 15%;
}
.community-name {
display: flex;
align-items: center;
gap: 10px;
}
.community-icon {
width: 24px;
height: 24px;
border-radius: 50%;
background: linear-gradient(135deg, #0066ff, #00b4dc);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 8px rgba(0, 180, 220, 0.5);
}
.community-icon i {
font-size: 14px;
color: #fff;
}
.data-with-trend {
display: flex;
flex-direction: column;
}
.table-value {
font-weight: bold;
color: #00fcff;
font-size: 16px;
}
.table-trend {
font-size: 12px;
display: flex;
align-items: center;
gap: 3px;
margin-top: 3px;
}
.status-badge {
display: inline-block;
padding: 4px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
}
.status-hot {
background-color: rgba(255, 87, 87, 0.2);
color: #ff5757;
border: 1px solid rgba(255, 87, 87, 0.3);
}
.status-active {
background-color: rgba(255, 193, 7, 0.2);
color: #ffc107;
border: 1px solid rgba(255, 193, 7, 0.3);
}
.status-stable {
background-color: rgba(0, 230, 118, 0.2);
color: #00e676;
border: 1px solid rgba(0, 230, 118, 0.3);
}
.status-normal {
background-color: rgba(0, 180, 220, 0.2);
color: #00b4dc;
border: 1px solid rgba(0, 180, 220, 0.3);
}
.chart-section {
background-color: rgba(0, 30, 60, 0.7);
border-radius: 8px;
box-shadow: 0 0 20px rgba(0, 180, 220, 0.2);
padding: 20px;
border: 1px solid rgba(0, 180, 220, 0.3);
position: relative;
}
.chart-header {
@ -670,10 +1025,25 @@ export default {
}
.chart {
height: 400px;
height: 300px;
width: 100%;
}
@media (max-width: 1200px) {
.bottom-sections {
grid-template-columns: 1fr;
}
.community-table-container,
.chart {
height: 250px;
}
.scrollable-tbody {
height: 200px;
}
}
@media (max-width: 768px) {
.dashboard-content {
grid-template-columns: 1fr;
@ -691,8 +1061,17 @@ export default {
flex-wrap: wrap;
}
.bottom-sections {
grid-template-columns: 1fr;
}
.community-table-container,
.chart {
height: 300px;
height: 200px;
}
.scrollable-tbody {
height: 150px;
}
.title-decoration {