1202 lines
32 KiB
Vue
1202 lines
32 KiB
Vue
<template>
|
||
<view class="regional-service-analysis">
|
||
<!-- 报表标题 -->
|
||
<view class="report-header">
|
||
<text class="report-title">按行政区域的服务区经营状态分析</text>
|
||
<view class="report-period">
|
||
<text class="period-label">分析周期:</text>
|
||
<text class="period-value">{{ analysisPeriod }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 对比分析切换 -->
|
||
<view class="analysis-tabs">
|
||
<view class="tab-item"
|
||
:class="{ active: activeAnalysisType === 'compare' }"
|
||
@click="switchAnalysisType('compare')">
|
||
<text class="tab-text">对比分析</text>
|
||
</view>
|
||
<view class="tab-item"
|
||
:class="{ active: activeAnalysisType === 'yearOverYear' }"
|
||
@click="switchAnalysisType('yearOverYear')">
|
||
<text class="tab-text">同比分析</text>
|
||
</view>
|
||
<view class="tab-item"
|
||
:class="{ active: activeAnalysisType === 'monthOverMonth' }"
|
||
@click="switchAnalysisType('monthOverMonth')">
|
||
<text class="tab-text">环比分析</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 关键指标卡片 -->
|
||
<view class="metrics-row">
|
||
<view class="metric-card">
|
||
<view class="metric-icon regions"></view>
|
||
<text class="metric-title">覆盖区域</text>
|
||
<view class="metric-value-container">
|
||
<text class="metric-value">{{ regionCount }}</text>
|
||
<text class="metric-unit">个</text>
|
||
</view>
|
||
</view>
|
||
<view class="metric-card">
|
||
<view class="metric-icon service-areas"></view>
|
||
<text class="metric-title">服务区总数</text>
|
||
<view class="metric-value-container">
|
||
<text class="metric-value">{{ serviceAreaCount }}</text>
|
||
<text class="metric-unit">个</text>
|
||
</view>
|
||
</view>
|
||
<view class="metric-card">
|
||
<view class="metric-icon avg-revenue"></view>
|
||
<text class="metric-title">平均营业额</text>
|
||
<view class="metric-value-container">
|
||
<text class="metric-value">¥{{ formatMoney(avgRevenue) }}</text>
|
||
<text class="metric-unit">万元</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 各区域营业额对比图 -->
|
||
<view class="chart-card">
|
||
<view class="chart-header">
|
||
<text class="chart-title">各行政区域营业额{{ getAnalysisTypeName() }}</text>
|
||
<text class="chart-subtitle">不同区域服务区营业额表现对比</text>
|
||
</view>
|
||
<view class="chart-content">
|
||
<view class="bar-chart-container">
|
||
<QiunDataCharts
|
||
type="column"
|
||
:opts="regionRevenueChartOpts"
|
||
:chartData="regionRevenueChartData"
|
||
:canvas2d="true"
|
||
:inScrollView="true"
|
||
canvasId="regionRevenueChart"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 区域服务区分布饼图 -->
|
||
<view class="chart-card">
|
||
<view class="chart-header">
|
||
<text class="chart-title">服务区区域分布</text>
|
||
<text class="chart-subtitle">各区域服务区数量占比</text>
|
||
</view>
|
||
<view class="chart-content">
|
||
<view class="pie-chart-container">
|
||
<QiunDataCharts
|
||
type="pie"
|
||
:opts="distributionChartOpts"
|
||
:chartData="distributionChartData"
|
||
:canvas2d="true"
|
||
:inScrollView="true"
|
||
canvasId="distributionChart"
|
||
/>
|
||
<view class="pie-legend">
|
||
<view class="legend-item" v-for="(item, index) in distributionData" :key="index">
|
||
<view class="legend-color" :style="{ backgroundColor: item.color }"></view>
|
||
<text class="legend-name">{{ item.name }}</text>
|
||
<text class="legend-value">{{ item.count }}个({{ item.percentage }}%)</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 区域经营指标热力图 -->
|
||
<view class="chart-card">
|
||
<view class="chart-header">
|
||
<text class="chart-title">区域经营指标热力图</text>
|
||
<text class="chart-subtitle">多维度指标综合表现</text>
|
||
</view>
|
||
<view class="chart-content">
|
||
<view class="region-metrics-cards">
|
||
<view class="region-card" v-for="(region, index) in heatmapData" :key="index">
|
||
<view class="region-header">
|
||
<text class="region-name">{{ region.name }}区域</text>
|
||
<view class="overall-score">
|
||
<text class="score-label">综合评分</text>
|
||
<text class="score-value">{{ Math.round((region.traffic + region.vehicles + region.revenue + region.profit + region.growth) / 5) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="metrics-grid">
|
||
<view class="metric-box">
|
||
<text class="metric-label">客流量</text>
|
||
<text :class="['metric-score', region.traffic >= 85 ? 'high' : region.traffic >= 70 ? 'medium-high' : region.traffic >= 55 ? 'medium' : region.traffic >= 40 ? 'medium-low' : 'low']">{{ region.traffic }}</text>
|
||
</view>
|
||
<view class="metric-box">
|
||
<text class="metric-label">车流量</text>
|
||
<text :class="['metric-score', region.vehicles >= 85 ? 'high' : region.vehicles >= 70 ? 'medium-high' : region.vehicles >= 55 ? 'medium' : region.vehicles >= 40 ? 'medium-low' : 'low']">{{ region.vehicles }}</text>
|
||
</view>
|
||
<view class="metric-box">
|
||
<text class="metric-label">营业额</text>
|
||
<text :class="['metric-score', region.revenue >= 85 ? 'high' : region.revenue >= 70 ? 'medium-high' : region.revenue >= 55 ? 'medium' : region.revenue >= 40 ? 'medium-low' : 'low']">{{ region.revenue }}</text>
|
||
</view>
|
||
<view class="metric-box">
|
||
<text class="metric-label">利润率</text>
|
||
<text :class="['metric-score', region.profit >= 85 ? 'high' : region.profit >= 70 ? 'medium-high' : region.profit >= 55 ? 'medium' : region.profit >= 40 ? 'medium-low' : 'low']">{{ region.profit }}</text>
|
||
</view>
|
||
<view class="metric-box">
|
||
<text class="metric-label">增长率</text>
|
||
<text :class="['metric-score', region.growth >= 85 ? 'high' : region.growth >= 70 ? 'medium-high' : region.growth >= 55 ? 'medium' : region.growth >= 40 ? 'medium-low' : 'low']">{{ region.growth }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="heatmap-legend">
|
||
<view class="legend-items">
|
||
<view class="legend-item">
|
||
<view class="legend-color low"></view>
|
||
<text class="legend-text">低 (0-39)</text>
|
||
</view>
|
||
<view class="legend-item">
|
||
<view class="legend-color medium-low"></view>
|
||
<text class="legend-text">中低 (40-54)</text>
|
||
</view>
|
||
<view class="legend-item">
|
||
<view class="legend-color medium"></view>
|
||
<text class="legend-text">中 (55-69)</text>
|
||
</view>
|
||
<view class="legend-item">
|
||
<view class="legend-color medium-high"></view>
|
||
<text class="legend-text">中高 (70-84)</text>
|
||
</view>
|
||
<view class="legend-item">
|
||
<view class="legend-color high"></view>
|
||
<text class="legend-text">高 (85-100)</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 区域类型分析图 -->
|
||
<view class="chart-card">
|
||
<view class="chart-header">
|
||
<text class="chart-title">各区域服务区类型分析</text>
|
||
<text class="chart-subtitle">A类、B类、C类服务区分布对比</text>
|
||
</view>
|
||
<view class="chart-content">
|
||
<view class="stacked-bar-container">
|
||
<QiunDataCharts
|
||
type="column"
|
||
:opts="typeAnalysisChartOpts"
|
||
:chartData="typeAnalysisChartData"
|
||
:canvas2d="true"
|
||
:inScrollView="true"
|
||
canvasId="typeAnalysisChart"
|
||
/>
|
||
</view>
|
||
<view class="type-legend">
|
||
<view class="legend-item">
|
||
<view class="legend-color type-a"></view>
|
||
<text class="legend-text">A类服务区</text>
|
||
</view>
|
||
<view class="legend-item">
|
||
<view class="legend-color type-b"></view>
|
||
<text class="legend-text">B类服务区</text>
|
||
</view>
|
||
<view class="legend-item">
|
||
<view class="legend-color type-c"></view>
|
||
<text class="legend-text">C类服务区</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 服务区详细数据卡片 -->
|
||
<view class="chart-card">
|
||
<view class="chart-header">
|
||
<text class="chart-title">服务区详细数据</text>
|
||
<view class="table-actions">
|
||
<text class="action-btn" @click="exportData">导出数据</text>
|
||
</view>
|
||
</view>
|
||
<view class="service-area-cards">
|
||
<view class="area-card" v-for="(item, index) in detailData" :key="index">
|
||
<view class="card-header">
|
||
<view class="area-info">
|
||
<text class="area-name">{{ item.serviceAreaName }}</text>
|
||
<text class="region-name">{{ item.regionName }}</text>
|
||
</view>
|
||
<view class="area-badges">
|
||
<text class="type-badge"
|
||
:class="item.serviceType === 'A类' ? 'type-a' :
|
||
item.serviceType === 'B类' ? 'type-b' :
|
||
item.serviceType === 'C类' ? 'type-c' : ''">
|
||
{{ item.serviceType }}
|
||
</text>
|
||
<text class="status-badge"
|
||
:class="item.operationalStatus === '正常运营' ? 'success' :
|
||
item.operationalStatus === '升级改造' ? 'warning' :
|
||
item.operationalStatus === '暂停营业' ? 'error' : 'info'">
|
||
{{ item.operationalStatus }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
<view class="card-content">
|
||
<view class="metrics-row">
|
||
<view class="metric-item">
|
||
<text class="metric-label">经营面积</text>
|
||
<text class="metric-value">{{ item.businessArea }}㎡</text>
|
||
</view>
|
||
<view class="metric-item">
|
||
<text class="metric-label">营业额</text>
|
||
<text class="metric-value">¥{{ formatMoney(item.revenue) }}</text>
|
||
</view>
|
||
<view class="metric-item">
|
||
<text class="metric-label">利润率</text>
|
||
<text class="metric-value profit-rate"
|
||
:class="item.profitRate >= 15 ? 'high' :
|
||
item.profitRate >= 10 ? 'positive' :
|
||
item.profitRate >= 5 ? 'neutral' : 'negative'">
|
||
{{ formatProfit(item.profitRate) }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import QiunDataCharts from './qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue'
|
||
|
||
export default {
|
||
components: {
|
||
QiunDataCharts
|
||
},
|
||
|
||
data() {
|
||
return {
|
||
analysisPeriod: '2024年10月',
|
||
activeAnalysisType: 'compare',
|
||
regionCount: 16,
|
||
serviceAreaCount: 42,
|
||
avgRevenue: 3425,
|
||
|
||
// 区域分布数据
|
||
distributionData: [
|
||
{ name: '昆明', count: 8, percentage: 19, color: '#576EFF' },
|
||
{ name: '曲靖', count: 6, percentage: 14, color: '#52C41A' },
|
||
{ name: '大理', count: 7, percentage: 17, color: '#FAAD14' },
|
||
{ name: '红河', count: 5, percentage: 12, color: '#FF7875' },
|
||
{ name: '昭通', count: 4, percentage: 10, color: '#B37FEB' },
|
||
{ name: '其他', count: 12, percentage: 28, color: '#666666' }
|
||
],
|
||
|
||
// 区域营业额数据
|
||
regionData: [
|
||
{
|
||
name: '昆明',
|
||
totalRevenue: 15678,
|
||
currentPeriod: [4567, 3890, 3214, 4007],
|
||
lastPeriod: [3890, 3456, 2987, 3543],
|
||
serviceAreas: 8
|
||
},
|
||
{
|
||
name: '曲靖',
|
||
totalRevenue: 9876,
|
||
currentPeriod: [2345, 1987, 2134, 3410],
|
||
lastPeriod: [1987, 1765, 1876, 2987],
|
||
serviceAreas: 6
|
||
},
|
||
{
|
||
name: '大理',
|
||
totalRevenue: 8765,
|
||
currentPeriod: [2123, 2345, 1876, 2421],
|
||
lastPeriod: [1876, 1987, 1654, 2098],
|
||
serviceAreas: 7
|
||
},
|
||
{
|
||
name: '红河',
|
||
totalRevenue: 7654,
|
||
currentPeriod: [1765, 1598, 2134, 2157],
|
||
lastPeriod: [1598, 1432, 1876, 1987],
|
||
serviceAreas: 5
|
||
},
|
||
{
|
||
name: '昭通',
|
||
totalRevenue: 6543,
|
||
currentPeriod: [1432, 1321, 1876, 1914],
|
||
lastPeriod: [1321, 1234, 1654, 1765],
|
||
serviceAreas: 4
|
||
}
|
||
],
|
||
|
||
// 热力图数据
|
||
heatmapData: [
|
||
{
|
||
name: '昆明',
|
||
traffic: 95,
|
||
vehicles: 88,
|
||
revenue: 92,
|
||
profit: 78,
|
||
growth: 85
|
||
},
|
||
{
|
||
name: '曲靖',
|
||
traffic: 75,
|
||
vehicles: 82,
|
||
revenue: 78,
|
||
profit: 72,
|
||
growth: 68
|
||
},
|
||
{
|
||
name: '大理',
|
||
traffic: 82,
|
||
vehicles: 76,
|
||
revenue: 74,
|
||
profit: 85,
|
||
growth: 79
|
||
},
|
||
{
|
||
name: '红河',
|
||
traffic: 68,
|
||
vehicles: 72,
|
||
revenue: 70,
|
||
profit: 68,
|
||
growth: 72
|
||
},
|
||
{
|
||
name: '昭通',
|
||
traffic: 62,
|
||
vehicles: 68,
|
||
revenue: 65,
|
||
profit: 70,
|
||
growth: 75
|
||
}
|
||
],
|
||
|
||
// 服务区类型数据
|
||
typeData: [
|
||
{
|
||
region: '昆明',
|
||
typeA: 3,
|
||
typeB: 3,
|
||
typeC: 2
|
||
},
|
||
{
|
||
region: '曲靖',
|
||
typeA: 2,
|
||
typeB: 2,
|
||
typeC: 2
|
||
},
|
||
{
|
||
region: '大理',
|
||
typeA: 2,
|
||
typeB: 3,
|
||
typeC: 2
|
||
},
|
||
{
|
||
region: '红河',
|
||
typeA: 1,
|
||
typeB: 2,
|
||
typeC: 2
|
||
},
|
||
{
|
||
region: '昭通',
|
||
typeA: 1,
|
||
typeB: 2,
|
||
typeC: 1
|
||
}
|
||
],
|
||
|
||
// 详细数据
|
||
detailData: [
|
||
{
|
||
regionName: '昆明市',
|
||
serviceAreaName: '昆明服务区',
|
||
serviceType: 'A类',
|
||
operationalStatus: '正常运营',
|
||
businessArea: 8500,
|
||
revenue: 4567,
|
||
profitRate: 18.5
|
||
},
|
||
{
|
||
regionName: '昆明市',
|
||
serviceAreaName: '安宁服务区',
|
||
serviceType: 'B类',
|
||
operationalStatus: '正常运营',
|
||
businessArea: 6200,
|
||
revenue: 3890,
|
||
profitRate: 15.2
|
||
},
|
||
{
|
||
regionName: '曲靖市',
|
||
serviceAreaName: '曲靖服务区',
|
||
serviceType: 'A类',
|
||
operationalStatus: '升级改造',
|
||
businessArea: 7800,
|
||
revenue: 2345,
|
||
profitRate: -2.3
|
||
},
|
||
{
|
||
regionName: '大理州',
|
||
serviceAreaName: '大理服务区',
|
||
serviceType: 'B类',
|
||
operationalStatus: '正常运营',
|
||
businessArea: 5500,
|
||
revenue: 2123,
|
||
profitRate: 16.8
|
||
},
|
||
{
|
||
regionName: '红河州',
|
||
serviceAreaName: '开远服务区',
|
||
serviceType: 'C类',
|
||
operationalStatus: '正常运营',
|
||
businessArea: 4200,
|
||
revenue: 1765,
|
||
profitRate: 12.3
|
||
}
|
||
]
|
||
}
|
||
},
|
||
|
||
computed: {
|
||
// 区域营业额图表数据
|
||
regionRevenueChartData() {
|
||
return {
|
||
categories: this.regionData.map(item => item.name),
|
||
series: [
|
||
{
|
||
name: this.activeAnalysisType === 'compare' ? '本期' :
|
||
this.activeAnalysisType === 'yearOverYear' ? '今年' : '本月',
|
||
data: this.regionData.map(item => item.totalRevenue)
|
||
},
|
||
...(this.activeAnalysisType !== 'compare' ? [{
|
||
name: this.activeAnalysisType === 'yearOverYear' ? '去年' : '上月',
|
||
data: this.regionData.map(item => Math.round(item.totalRevenue * 0.88))
|
||
}] : [])
|
||
]
|
||
}
|
||
},
|
||
|
||
// 区域营业额图表配置
|
||
regionRevenueChartOpts() {
|
||
return {
|
||
color: ['#576EFF', '#52C41A', '#FAAD14'],
|
||
padding: [15, 15, 15, 15],
|
||
dataLabel: false,
|
||
legend: {
|
||
show: true,
|
||
position: 'top'
|
||
},
|
||
xAxis: {
|
||
itemCount: 3,
|
||
scrollShow: true,
|
||
scrollAlign: 'right'
|
||
},
|
||
yAxis: {
|
||
gridType: 'dash',
|
||
dashLength: 2,
|
||
data: [{
|
||
min: 0
|
||
}]
|
||
},
|
||
extra: {
|
||
column: {
|
||
type: 'group',
|
||
width: this.activeAnalysisType === 'compare' ? 35 : 25,
|
||
activeBgColor: '#000000',
|
||
activeBgOpacity: 0.08,
|
||
linearType: 'custom',
|
||
barBorderCircle: true
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
// 区域分布饼图数据
|
||
distributionChartData() {
|
||
return {
|
||
series: [{
|
||
data: this.distributionData.map(item => ({
|
||
name: item.name,
|
||
value: item.count
|
||
}))
|
||
}]
|
||
}
|
||
},
|
||
|
||
// 区域分布饼图配置
|
||
distributionChartOpts() {
|
||
return {
|
||
color: this.distributionData.map(item => item.color),
|
||
padding: [5, 5, 5, 5],
|
||
dataLabel: true,
|
||
legend: {
|
||
show: false
|
||
},
|
||
extra: {
|
||
pie: {
|
||
activeOpacity: 0.5,
|
||
activeRadius: 10,
|
||
offsetAngle: 0,
|
||
labelWidth: 15,
|
||
border: false,
|
||
borderWidth: 3,
|
||
borderColor: '#FFFFFF'
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
// 类型分析图表数据
|
||
typeAnalysisChartData() {
|
||
return {
|
||
categories: this.typeData.map(item => item.region),
|
||
series: [
|
||
{
|
||
name: 'A类服务区',
|
||
data: this.typeData.map(item => item.typeA)
|
||
},
|
||
{
|
||
name: 'B类服务区',
|
||
data: this.typeData.map(item => item.typeB)
|
||
},
|
||
{
|
||
name: 'C类服务区',
|
||
data: this.typeData.map(item => item.typeC)
|
||
}
|
||
]
|
||
}
|
||
},
|
||
|
||
// 类型分析图表配置
|
||
typeAnalysisChartOpts() {
|
||
return {
|
||
color: ['#576EFF', '#52C41A', '#FAAD14'],
|
||
padding: [15, 15, 15, 15],
|
||
dataLabel: false,
|
||
legend: {
|
||
show: false
|
||
},
|
||
xAxis: {
|
||
itemCount: 3,
|
||
scrollShow: true,
|
||
scrollAlign: 'right'
|
||
},
|
||
yAxis: {
|
||
gridType: 'dash',
|
||
dashLength: 2,
|
||
data: [{
|
||
min: 0
|
||
}]
|
||
},
|
||
extra: {
|
||
column: {
|
||
type: 'stack',
|
||
width: 40,
|
||
activeBgColor: '#000000',
|
||
activeBgOpacity: 0.08,
|
||
linearType: 'custom',
|
||
barBorderCircle: true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
methods: {
|
||
switchAnalysisType(type) {
|
||
this.activeAnalysisType = type;
|
||
},
|
||
|
||
getAnalysisTypeName() {
|
||
const names = {
|
||
compare: '对比',
|
||
yearOverYear: '同比',
|
||
monthOverMonth: '环比'
|
||
};
|
||
return names[this.activeAnalysisType] || '';
|
||
},
|
||
|
||
getHeatmapClass(value) {
|
||
if (value >= 85) return 'high';
|
||
if (value >= 70) return 'medium-high';
|
||
if (value >= 55) return 'medium';
|
||
if (value >= 40) return 'medium-low';
|
||
return 'low';
|
||
},
|
||
|
||
getTypeClass(type) {
|
||
const classMap = {
|
||
'A类': 'type-a',
|
||
'B类': 'type-b',
|
||
'C类': 'type-c'
|
||
};
|
||
return classMap[type] || '';
|
||
},
|
||
|
||
getStatusClass(status) {
|
||
const classMap = {
|
||
'正常运营': 'success',
|
||
'升级改造': 'warning',
|
||
'暂停营业': 'error',
|
||
'规划建设': 'info'
|
||
};
|
||
return classMap[status] || '';
|
||
},
|
||
|
||
getProfitClass(profit) {
|
||
if (profit >= 15) return 'high';
|
||
if (profit >= 10) return 'positive';
|
||
if (profit >= 5) return 'neutral';
|
||
return 'negative';
|
||
},
|
||
|
||
formatNumber(num) {
|
||
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||
},
|
||
|
||
formatMoney(amount) {
|
||
return amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||
},
|
||
|
||
formatProfit(profit) {
|
||
return profit + '%'
|
||
},
|
||
|
||
exportData() {
|
||
console.log('导出区域服务区数据');
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="less">
|
||
@primary-color: #667eea;
|
||
@success-color: #52c41a;
|
||
@warning-color: #faad14;
|
||
@error-color: #ff4d4f;
|
||
@info-color: #1890ff;
|
||
@text-primary: #333;
|
||
@text-secondary: #666;
|
||
@text-light: #999;
|
||
@bg-white: #ffffff;
|
||
@border-radius: 16rpx;
|
||
@shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||
|
||
.regional-service-analysis {
|
||
.report-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 32rpx;
|
||
padding: 0 8rpx;
|
||
|
||
.report-title {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: @text-primary;
|
||
}
|
||
|
||
.report-period {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.period-label {
|
||
font-size: 24rpx;
|
||
color: @text-secondary;
|
||
margin-right: 8rpx;
|
||
}
|
||
|
||
.period-value {
|
||
font-size: 24rpx;
|
||
color: @primary-color;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
}
|
||
|
||
.analysis-tabs {
|
||
display: flex;
|
||
background: @bg-white;
|
||
border-radius: @border-radius;
|
||
padding: 8rpx;
|
||
margin-bottom: 32rpx;
|
||
box-shadow: @shadow;
|
||
|
||
.tab-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 16rpx 12rpx;
|
||
border-radius: 12rpx;
|
||
transition: all 0.3s ease;
|
||
|
||
&.active {
|
||
background: @primary-color;
|
||
|
||
.tab-text {
|
||
color: white;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
|
||
.tab-text {
|
||
font-size: 26rpx;
|
||
color: @text-secondary;
|
||
transition: all 0.3s ease;
|
||
}
|
||
}
|
||
}
|
||
|
||
.metrics-row {
|
||
display: flex;
|
||
gap: 24rpx;
|
||
margin-bottom: 32rpx;
|
||
|
||
.metric-card {
|
||
flex: 1;
|
||
background: @bg-white;
|
||
border-radius: @border-radius;
|
||
padding: 32rpx 24rpx;
|
||
box-shadow: @shadow;
|
||
text-align: center;
|
||
position: relative;
|
||
|
||
.metric-icon {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
margin: 0 auto 16rpx;
|
||
border-radius: 50%;
|
||
position: relative;
|
||
|
||
&.regions {
|
||
background: linear-gradient(135deg, #576EFF, #7C8FFF);
|
||
&::after { content: '🗺️'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24rpx; }
|
||
}
|
||
|
||
&.service-areas {
|
||
background: linear-gradient(135deg, #52C41A, #73D13D);
|
||
&::after { content: '🏪'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24rpx; }
|
||
}
|
||
|
||
&.avg-revenue {
|
||
background: linear-gradient(135deg, #FAAD14, #FFC53D);
|
||
&::after { content: '📊'; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 24rpx; }
|
||
}
|
||
}
|
||
|
||
.metric-title {
|
||
font-size: 24rpx;
|
||
color: @text-secondary;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.metric-value-container {
|
||
margin-bottom: 8rpx;
|
||
|
||
.metric-value {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: @text-primary;
|
||
display: block;
|
||
}
|
||
|
||
.metric-unit {
|
||
font-size: 20rpx;
|
||
color: @text-light;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.chart-card {
|
||
background: @bg-white;
|
||
border-radius: @border-radius;
|
||
padding: 24rpx;
|
||
box-shadow: @shadow;
|
||
margin-bottom: 24rpx;
|
||
|
||
.chart-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
margin-bottom: 20rpx;
|
||
|
||
.chart-title {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: @text-primary;
|
||
}
|
||
|
||
.chart-subtitle {
|
||
font-size: 22rpx;
|
||
color: @text-light;
|
||
margin-top: 4rpx;
|
||
}
|
||
|
||
.table-actions {
|
||
display: flex;
|
||
gap: 16rpx;
|
||
|
||
.action-btn {
|
||
font-size: 24rpx;
|
||
color: @primary-color;
|
||
padding: 8rpx 16rpx;
|
||
border: 1rpx solid @primary-color;
|
||
border-radius: 8rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.chart-content {
|
||
.pie-chart-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
.pie-legend {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
gap: 16rpx;
|
||
margin-top: 16rpx;
|
||
|
||
.legend-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
|
||
.legend-color {
|
||
width: 12rpx;
|
||
height: 12rpx;
|
||
border-radius: 2rpx;
|
||
}
|
||
|
||
.legend-name {
|
||
font-size: 22rpx;
|
||
color: @text-secondary;
|
||
}
|
||
|
||
.legend-value {
|
||
font-size: 22rpx;
|
||
color: @text-primary;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.bar-chart-container,
|
||
.stacked-bar-container {
|
||
width: 100%;
|
||
}
|
||
|
||
.type-legend {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 32rpx;
|
||
margin-top: 16rpx;
|
||
|
||
.legend-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
|
||
.legend-color {
|
||
width: 12rpx;
|
||
height: 12rpx;
|
||
border-radius: 2rpx;
|
||
|
||
&.type-a { background: #576EFF; }
|
||
&.type-b { background: #52C41A; }
|
||
&.type-c { background: #FAAD14; }
|
||
}
|
||
|
||
.legend-text {
|
||
font-size: 22rpx;
|
||
color: @text-secondary;
|
||
}
|
||
}
|
||
}
|
||
|
||
.region-metrics-cards {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20rpx;
|
||
|
||
.region-card {
|
||
background: #f8f9fa;
|
||
border-radius: 12rpx;
|
||
padding: 20rpx;
|
||
border: 1rpx solid #e9ecef;
|
||
|
||
.region-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 16rpx;
|
||
padding-bottom: 12rpx;
|
||
border-bottom: 1rpx solid #e9ecef;
|
||
|
||
.region-name {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: @text-primary;
|
||
}
|
||
|
||
.overall-score {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
background: @primary-color;
|
||
color: white;
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 8rpx;
|
||
|
||
.score-label {
|
||
font-size: 18rpx;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.score-value {
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
}
|
||
|
||
.metrics-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 12rpx;
|
||
|
||
.metric-box {
|
||
flex: 1;
|
||
min-width: calc(33.333% - 8rpx);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 12rpx 8rpx;
|
||
background: white;
|
||
border-radius: 8rpx;
|
||
border: 1rpx solid #f0f0f0;
|
||
|
||
.metric-label {
|
||
font-size: 20rpx;
|
||
color: @text-secondary;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.metric-score {
|
||
font-size: 26rpx;
|
||
font-weight: 600;
|
||
|
||
&.high {
|
||
color: #e74c3c;
|
||
}
|
||
|
||
&.medium-high {
|
||
color: #e67e22;
|
||
}
|
||
|
||
&.medium {
|
||
color: #f1c40f;
|
||
}
|
||
|
||
&.medium-low {
|
||
color: #2ecc71;
|
||
}
|
||
|
||
&.low {
|
||
color: #3498db;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.heatmap-legend {
|
||
margin-top: 24rpx;
|
||
|
||
.legend-items {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
gap: 16rpx;
|
||
|
||
.legend-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
|
||
.legend-color {
|
||
width: 16rpx;
|
||
height: 16rpx;
|
||
border-radius: 4rpx;
|
||
|
||
&.low {
|
||
background: #3498db;
|
||
}
|
||
|
||
&.medium-low {
|
||
background: #2ecc71;
|
||
}
|
||
|
||
&.medium {
|
||
background: #f1c40f;
|
||
}
|
||
|
||
&.medium-high {
|
||
background: #e67e22;
|
||
}
|
||
|
||
&.high {
|
||
background: #e74c3c;
|
||
}
|
||
}
|
||
|
||
.legend-text {
|
||
font-size: 20rpx;
|
||
color: @text-secondary;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.service-area-cards {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16rpx;
|
||
|
||
.area-card {
|
||
background: #f8f9fa;
|
||
border-radius: 12rpx;
|
||
padding: 20rpx;
|
||
border: 1rpx solid #e9ecef;
|
||
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
margin-bottom: 16rpx;
|
||
|
||
.area-info {
|
||
flex: 1;
|
||
|
||
.area-name {
|
||
display: block;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: @text-primary;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.region-name {
|
||
display: block;
|
||
font-size: 24rpx;
|
||
color: @text-secondary;
|
||
}
|
||
}
|
||
|
||
.area-badges {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
align-items: flex-end;
|
||
|
||
.type-badge {
|
||
padding: 6rpx 12rpx;
|
||
border-radius: 8rpx;
|
||
font-size: 20rpx;
|
||
color: white;
|
||
font-weight: 500;
|
||
|
||
&.type-a {
|
||
background: #576EFF;
|
||
}
|
||
|
||
&.type-b {
|
||
background: #52C41A;
|
||
}
|
||
|
||
&.type-c {
|
||
background: #FAAD14;
|
||
}
|
||
}
|
||
|
||
.status-badge {
|
||
padding: 6rpx 12rpx;
|
||
border-radius: 8rpx;
|
||
font-size: 20rpx;
|
||
color: white;
|
||
font-weight: 500;
|
||
|
||
&.success {
|
||
background: @success-color;
|
||
}
|
||
|
||
&.warning {
|
||
background: @warning-color;
|
||
}
|
||
|
||
&.error {
|
||
background: @error-color;
|
||
}
|
||
|
||
&.info {
|
||
background: @info-color;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.card-content {
|
||
.metrics-row {
|
||
display: flex;
|
||
gap: 16rpx;
|
||
|
||
.metric-item {
|
||
flex: 1;
|
||
background: white;
|
||
padding: 16rpx;
|
||
border-radius: 8rpx;
|
||
border: 1rpx solid #f0f0f0;
|
||
text-align: center;
|
||
|
||
.metric-label {
|
||
display: block;
|
||
font-size: 22rpx;
|
||
color: @text-secondary;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.metric-value {
|
||
display: block;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: @text-primary;
|
||
|
||
&.profit-rate {
|
||
&.high {
|
||
color: @success-color;
|
||
}
|
||
|
||
&.positive {
|
||
color: @success-color;
|
||
}
|
||
|
||
&.neutral {
|
||
color: @warning-color;
|
||
}
|
||
|
||
&.negative {
|
||
color: @error-color;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style> |