You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
livestock-trading/src/components/SalesChannelStats.vue

207 lines
3.9 KiB

<template>
<BaseCard title="销售渠道统计">
<div class="stats-container">
<div class="stats-row">
<div class="stat-item">
<div class="stat-value">1,234</div>
<div class="stat-label">线上销售</div>
</div>
<div class="stat-item">
<div class="stat-value">856</div>
<div class="stat-label">线下销售</div>
</div>
<div class="stat-item">
<div class="stat-value">2,090</div>
<div class="stat-label">总销售额</div>
</div>
</div>
<div class="chart-container">
<div ref="chartRef" class="chart"></div>
</div>
</div>
</BaseCard>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'
import BaseCard from './BaseCard.vue'
const chartRef = ref(null)
let chartInstance = null
// 图例数据
const legendData = [
{ name: '直销', color: '#409EFF', percent: 50 },
{ name: '代销', color: '#67C23A', percent: 30 },
{ name: '其他', color: '#E6A23C', percent: 20 }
]
// 模拟数据
const data = [
{ name: '直销', value: 50 },
{ name: '代销', value: 30 },
{ name: '其他', value: 20 }
]
const initChart = () => {
if (!chartRef.value) return
chartInstance = echarts.init(chartRef.value)
const option = {
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(0, 0, 0, 0.8)',
borderColor: '#409EFF',
textStyle: {
color: '#fff',
fontSize: 12
},
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
series: [{
name: '销售渠道',
type: 'pie',
radius: ['40%', '65%'],
center: ['50%', '50%'],
data: data.map((item, index) => ({
...item,
itemStyle: {
color: legendData[index].color,
borderRadius: 8,
borderColor: '#1a1f2e',
borderWidth: 2
}
})),
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
labelLine: {
show: false
},
label: {
show: false
}
}]
}
chartInstance.setOption(option)
// 自动高亮效果
let currentIndex = 0
const timer = setInterval(() => {
chartInstance.dispatchAction({
type: 'downplay',
seriesIndex: 0,
dataIndex: currentIndex
})
currentIndex = (currentIndex + 1) % data.length
chartInstance.dispatchAction({
type: 'highlight',
seriesIndex: 0,
dataIndex: currentIndex
})
}, 2000)
chartInstance._autoTimer = timer
}
onMounted(() => {
initChart()
window.addEventListener('resize', () => {
chartInstance?.resize()
})
})
onUnmounted(() => {
if (chartInstance) {
if (chartInstance._autoTimer) {
clearInterval(chartInstance._autoTimer)
}
chartInstance.dispose()
}
})
</script>
<style scoped>
.channel-container {
display: flex;
flex-direction: column;
height: 100%;
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-bottom: 12px;
}
.stat-item {
text-align: center;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 4px;
padding: 8px;
}
.stat-label {
font-size: 11px;
color: #a0a8b8;
margin-bottom: 4px;
}
.stat-value {
font-size: 13px;
font-weight: bold;
color: #409EFF;
}
.chart-section {
flex: 1;
display: flex;
align-items: center;
gap: 16px;
}
.chart {
flex: 1;
height: 150px;
}
.chart-legend {
display: flex;
flex-direction: column;
gap: 8px;
min-width: 80px;
}
.legend-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
}
.legend-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.legend-text {
color: #a0a8b8;
flex: 1;
}
.legend-percent {
color: #409EFF;
font-weight: bold;
}
</style>