<template>
|
<view class="revenue-page">
|
<view class="filter-panel">
|
<view class="quick-tabs">
|
<view
|
v-for="item in quickTabs"
|
:key="item.value"
|
class="quick-tab"
|
:class="{ active: currentRange === item.value }"
|
@tap="currentRange = item.value"
|
>
|
{{ item.label }}
|
</view>
|
</view>
|
|
<view class="date-bar">
|
<text class="date-placeholder">开始时间</text>
|
<text class="date-separator">-</text>
|
<text class="date-placeholder">结束时间</text>
|
</view>
|
</view>
|
|
<view class="section-card metrics-card">
|
<view class="section-title-wrap">
|
<view class="title-bar"></view>
|
<text class="section-title">核心业绩指标</text>
|
</view>
|
|
<view class="metrics-grid">
|
<view v-for="item in metrics" :key="item.label" class="metric-item">
|
<view class="metric-icon-wrap">
|
<image class="metric-icon" :src="item.icon" mode="aspectFit"></image>
|
</view>
|
<view class="metric-copy">
|
<view class="metric-value">{{ item.value }}</view>
|
<view class="metric-label">{{ item.label }}</view>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<view class="section-card chart-card">
|
<view class="section-title-wrap">
|
<view class="title-bar"></view>
|
<text class="section-title">行李类型分布</text>
|
</view>
|
|
<view class="chart-content">
|
<view class="chart-area">
|
<view
|
class="echart-host"
|
:id="chartDomId"
|
:prop="chartOptionText"
|
:change:prop="chartRenderer.renderChart"
|
></view>
|
<view class="chart-fallback"></view>
|
</view>
|
|
<view class="legend-list">
|
<view v-for="item in luggageDistribution" :key="item.name" class="legend-item">
|
<view class="legend-left">
|
<view class="legend-dot" :style="{ background: item.color }"></view>
|
<text class="legend-name">{{ item.name }}</text>
|
</view>
|
<text class="legend-value">{{ item.value }} | {{ item.percent }}%</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
</view>
|
</template>
|
|
<script>
|
export default {
|
data() {
|
return {
|
currentRange: 'custom',
|
chartDomId: 'luggage-distribution-chart',
|
quickTabs: [
|
{ label: '今日', value: 'today' },
|
{ label: '近7日', value: '7days' },
|
{ label: '近30日', value: '30days' },
|
{ label: '近半年', value: 'halfYear' },
|
{ label: '自定义', value: 'custom' }
|
],
|
metrics: [
|
{ label: '寄存订单(个)', value: '125', icon: '/shop/static/icon/yingshou_ic_jicun@2x.png' },
|
{ label: '寄送订单(个)', value: '30', icon: '/shop/static/icon/yingshou_ic_jisong@2x.png' },
|
{ label: '总订单(个)', value: '155', icon: '/shop/static/icon/yingshou_ic_zongdingdan@2x.png' },
|
{ label: '总完成订单(个)', value: '143', icon: '/shop/static/icon/yingshou_ic_zongwancheng@2x.png' },
|
{ label: '总营收(元)', value: '300,000.00', icon: '/shop/static/icon/yingshou_ic_zongyingshou@2x.png' },
|
{ label: '分店分成(元)', value: '10,000.32', icon: '/shop/static/icon/yingshou_ic_fendian@2x.png' },
|
{ label: '退款订单(个)', value: '10', icon: '/shop/static/icon/yingshou_ic_tuikuan@2x.png' },
|
{ label: '责任扣款(元)', value: '300.00', icon: '/shop/static/icon/yingshou_ic_koukuan@2x.png' }
|
],
|
luggageDistribution: [
|
{ name: '大号行李箱', value: 35, percent: 35, color: '#3B82F6' },
|
{ name: '特大号行李箱', value: 20, percent: 20, color: '#64D7C7' },
|
{ name: '中号行李箱', value: 18, percent: 18, color: '#FFD15C' },
|
{ name: '小号行李箱', value: 12, percent: 12, color: '#FF8A47' },
|
{ name: '大号背包', value: 10, percent: 10, color: '#F54786' },
|
{ name: '小号背包', value: 5, percent: 5, color: '#EE6CB2' }
|
]
|
};
|
},
|
computed: {
|
chartOptionText() {
|
return JSON.stringify({
|
color: this.luggageDistribution.map((item) => item.color),
|
series: [
|
{
|
name: '行李类型分布',
|
type: 'pie',
|
radius: ['58%', '78%'],
|
center: ['50%', '50%'],
|
avoidLabelOverlap: false,
|
label: { show: false },
|
labelLine: { show: false },
|
itemStyle: {
|
borderColor: '#ffffff',
|
borderWidth: 4
|
},
|
data: this.luggageDistribution.map((item) => ({
|
value: item.value,
|
name: item.name
|
}))
|
}
|
]
|
});
|
}
|
}
|
};
|
</script>
|
|
<script>
|
import echarts from "echarts"
|
|
export default {
|
data() {
|
return {
|
chart: null
|
};
|
},
|
methods: {
|
renderChart(optionText) {
|
if (!optionText) {
|
return;
|
}
|
|
this.$nextTick(() => {
|
const dom = document.getElementById('luggage-distribution-chart');
|
if (!dom) {
|
return;
|
}
|
|
if (!this.chart) {
|
this.chart = echarts.init(dom);
|
}
|
|
this.chart.setOption(JSON.parse(optionText), true);
|
});
|
}
|
}
|
};
|
</script>
|
|
<style lang="scss" scoped>
|
.revenue-page {
|
min-height: 100vh;
|
padding: 24rpx;
|
background: #f5f7fb;
|
box-sizing: border-box;
|
}
|
|
.filter-panel,
|
.section-card {
|
background: #ffffff;
|
border-radius: 24rpx;
|
box-shadow: 0 12rpx 30rpx rgba(22, 47, 94, 0.04);
|
}
|
|
.filter-panel {
|
padding: 20rpx;
|
}
|
|
.quick-tabs {
|
display: flex;
|
gap: 16rpx;
|
}
|
|
.quick-tab {
|
flex: 1;
|
height: 60rpx;
|
line-height: 60rpx;
|
border-radius: 999rpx;
|
background: #f6f7f9;
|
font-size: 28rpx;
|
font-weight: 500;
|
text-align: center;
|
color: #50555f;
|
}
|
|
.quick-tab.active {
|
background: #0d4f9c;
|
color: #ffffff;
|
}
|
|
.date-bar {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
height: 66rpx;
|
padding: 0 48rpx;
|
margin-top: 18rpx;
|
border-radius: 999rpx;
|
background: #f6f7f9;
|
}
|
|
.date-placeholder,
|
.date-separator {
|
font-size: 28rpx;
|
color: #b1b5be;
|
}
|
|
.metrics-card,
|
.chart-card {
|
margin-top: 22rpx;
|
padding: 28rpx 24rpx 30rpx;
|
}
|
|
.section-title-wrap {
|
display: flex;
|
align-items: center;
|
gap: 14rpx;
|
}
|
|
.title-bar {
|
width: 6rpx;
|
height: 34rpx;
|
border-radius: 999rpx;
|
background: #0d5db8;
|
}
|
|
.section-title {
|
font-size: 38rpx;
|
font-weight: 700;
|
color: #1f2430;
|
}
|
|
.metrics-grid {
|
display: grid;
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
gap: 26rpx 18rpx;
|
margin-top: 30rpx;
|
}
|
|
.metric-item {
|
display: flex;
|
align-items: center;
|
gap: 18rpx;
|
}
|
|
.metric-icon-wrap {
|
flex-shrink: 0;
|
width: 68rpx;
|
height: 68rpx;
|
border-radius: 14rpx;
|
background: #ffffff;
|
box-shadow: 0 6rpx 18rpx rgba(20, 42, 74, 0.08);
|
box-sizing: border-box;
|
}
|
|
.metric-icon,
|
.placeholder-box {
|
width: 100%;
|
height: 100%;
|
border-radius: 8rpx;
|
}
|
|
.placeholder-box {
|
background: linear-gradient(135deg, #48576a 0%, #758398 100%);
|
}
|
|
.metric-copy {
|
min-width: 0;
|
}
|
|
.metric-value {
|
font-size: 44rpx;
|
font-weight: 700;
|
line-height: 1.1;
|
color: #1357a6;
|
}
|
|
.metric-label {
|
margin-top: 8rpx;
|
font-size: 28rpx;
|
line-height: 40rpx;
|
color: #979da8;
|
}
|
|
.chart-content {
|
display: flex;
|
align-items: center;
|
gap: 26rpx;
|
margin-top: 26rpx;
|
}
|
|
.chart-area {
|
position: relative;
|
flex-shrink: 0;
|
width: 270rpx;
|
height: 270rpx;
|
}
|
|
.echart-host,
|
.chart-fallback {
|
width: 100%;
|
height: 100%;
|
}
|
|
.chart-fallback {
|
position: absolute;
|
left: 0;
|
top: 0;
|
border-radius: 50%;
|
background: conic-gradient(#3b82f6 0 35%, #64d7c7 35% 55%, #ffd15c 55% 73%, #ff8a47 73% 85%, #f54786 85% 95%, #ee6cb2 95% 100%);
|
-webkit-mask: radial-gradient(circle, transparent 0 42%, #000 43%);
|
mask: radial-gradient(circle, transparent 0 42%, #000 43%);
|
opacity: 0.2;
|
pointer-events: none;
|
}
|
|
.legend-list {
|
flex: 1;
|
min-width: 0;
|
}
|
|
.legend-item {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
gap: 12rpx;
|
}
|
|
.legend-item + .legend-item {
|
margin-top: 18rpx;
|
}
|
|
.legend-left {
|
display: flex;
|
align-items: center;
|
gap: 14rpx;
|
min-width: 0;
|
}
|
|
.legend-dot {
|
flex-shrink: 0;
|
width: 20rpx;
|
height: 20rpx;
|
border-radius: 50%;
|
}
|
|
.legend-name,
|
.legend-value {
|
font-size: 30rpx;
|
line-height: 42rpx;
|
}
|
|
.legend-name {
|
color: #6f7683;
|
}
|
|
.legend-value {
|
flex-shrink: 0;
|
font-weight: 600;
|
color: #2f333d;
|
}
|
</style>
|