ylj20011123 fcab7c2e8c update
2025-10-15 11:13:52 +08:00

3693 lines
89 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view :class="'province-theme-' + currentProvinceCode">
<!-- 加载骨架屏 -->
<view class="loading-skeleton" v-if="isLoading">
<view class="skeleton-header"></view>
<view class="skeleton-cards">
<view class="skeleton-card" v-for="i in 3" :key="i"></view>
</view>
<view class="skeleton-list">
<view class="skeleton-item" v-for="i in 5" :key="i"></view>
</view>
</view>
<view class="page-body page-fade-in" v-if="showPage">
<AnhHead :today="today" :headMsg="headMsg" :lastDay="lastDay" :groupType="theRequest && theRequest.GroupType"
:provinceCode="theRequest && theRequest.ProvinceCode" @bindDateChange="bindDateChange" :isup="isup"
:nowDay="nowDay" :selectDate="theRequest && theRequest.time" @toggle="toggleCard" :currentMoney="currentMoney">
</AnhHead>
<!--营收占比的上面组件 -->
<view class="main-content">
<!--安徽省 昨日营收占比 -->
<view v-if="theRequest && theRequest.GroupType != 1010">
<view class="section-header">
<view class="section-title">
<view class="section-icon">
<text class="icon-emoji">📊</text>
</view>
<text class="section-text">营收占比</text>
</view>
</view>
<view class="analysis-container">
<view class="modern-tabs">
<view class="tab-item" @click="selectTab('nowTab', 1)" v-if="theRequest.ProvinceCode == 340000"
:class="{ 'active': nowTab == 1 }">
<view class="tab-icon">🏢</view>
<view class="tab-label">经营模式</view>
</view>
<view class="tab-item" @click="selectTab('nowTab', 2)" :class="{ 'active': nowTab == 2 }">
<view class="tab-icon">🏪</view>
<view class="tab-label">经营业态</view>
</view>
<view class="tab-item" @click="selectTab('nowTab', 3)"
v-if="areaProgress.length && areaProgress.length > 1" :class="{ 'active': nowTab == 3 }">
<view class="tab-icon">🗺</view>
<view class="tab-label">区域营收</view>
</view>
<view class="tab-item" @click="selectTab('nowTab', 4)"
v-if="theRequest.ProvinceCode == 340000 && theRequest.GroupType == 1000"
:class="{ 'active': nowTab == 4 }">
<view class="tab-icon">🚗</view>
<view class="tab-label">车流分析</view>
</view>
</view>
<view class="content-wrapper">
<view class="chart-section" v-show="nowTab == 1"
v-if="modelProgress.length > 0 && theRequest.ProvinceCode == 340000">
<view class="chart-container chart-optimized">
<canvas canvas-id="modelCont" id="modelCont" class="modern-chart performance-chart"
@touchstart="touchPie($event, 'modelCont')"></canvas>
</view>
<view class="data-cards">
<view class="data-card" v-for="(item, o) in modelProgress" :key="o">
<view class="card-header">
<view class="category-name">{{ item.name }}</view>
<view class="percentage">{{ item.bili }}%</view>
</view>
<view class="amount">¥{{ item.data }}</view>
<view class="progress-bar">
<view class="progress-fill progress-animated" :class="'progress-delay-' + o"
:style="{ 'width': showPage ? item.bili + '%' : '0%' }"></view>
</view>
</view>
</view>
</view>
<view class="chart-section" v-show="nowTab == 2" v-if="regionProgress.length > 0">
<view class="chart-container chart-optimized">
<canvas canvas-id="businessCont" id="businessCont" class="modern-chart performance-chart"
@touchstart="touchPie($event, 'businessCont')"></canvas>
</view>
<view class="data-cards">
<view class="data-card" v-for="(item, o) in regionProgress" :key="o">
<view class="card-header">
<view class="category-name">{{ item.name }}</view>
<view class="percentage">{{ item.bili }}%</view>
</view>
<view class="amount">¥{{ item.data }}</view>
<view class="progress-bar">
<view class="progress-fill progress-animated" :class="'progress-delay-' + o"
:style="{ 'width': showPage ? item.bili + '%' : '0%' }"></view>
</view>
</view>
</view>
</view>
<view class="chart-section" v-show="nowTab == 3" v-if="areaProgress.length > 0">
<view class="chart-container chart-optimized">
<canvas canvas-id="areaCont" id="areaCont" class="modern-chart performance-chart"
@touchstart="touchPie($event, 'areaCont')"></canvas>
</view>
<view class="data-cards">
<view class="data-card" v-for="(item, o) in areaProgress" :key="o">
<view class="card-header">
<view class="category-name">{{ item.name }}</view>
<view class="percentage">{{ item.bili }}%</view>
</view>
<view class="amount">¥{{ item.data }}</view>
<view class="progress-bar">
<view class="progress-fill progress-animated" :class="'progress-delay-' + o"
:style="{ 'width': showPage ? item.bili + '%' : '0%' }"></view>
</view>
</view>
</view>
</view>
<!-- 车流分析区域 - 修复显示问题 -->
<view v-if="theRequest.ProvinceCode == 340000 && theRequest.GroupType == 1000" v-show="nowTab == 4">
<view class="traffic-analysis" v-if="bayonetProgress && bayonetProgress.length > 0">
<view class="traffic-header">
<view class="traffic-title">
<view class="traffic-icon">🚗</view>
<text>车流分析概览</text>
</view>
<view class="traffic-summary">
{{ bayonetProgress.length }}个区域
</view>
</view>
<scroll-view class="traffic-scroll-container" scroll-y="true">
<view class="traffic-regions">
<view class="region-card" v-for="(item, o) in bayonetProgress" :key="o">
<!-- 区域标题 -->
<view class="region-header" @click="toggleRegionCollapse(item)">
<view class="region-badge">
<view class="region-title">{{ item.name }}</view>
</view>
<view class="region-actions">
<view class="region-count">{{ item.list.length }}个服务区</view>
<view class="region-arrow" :class="{ 'collapsed': item.collapsed }">▼</view>
</view>
</view>
<!-- 服务区列表 -->
<view class="service-areas service-areas-expand" v-if="!item.collapsed">
<view class="service-area-item service-item-slide" v-for="(child, idx) in item.list"
:key="idx" :style="'animation-delay: ' + (idx * 0.1) + 's'">
<view class="service-header service-header-interactive"
@click="toggleServiceArea(item, idx)">
<view class="service-info">
<view class="service-name">{{ child.name }}</view>
<view class="service-ratio">占比 {{ child.bili }}%</view>
</view>
<view class="service-actions">
<view class="flow-stats">
<view class="flow-item flow-item-counter">
<text class="flow-label">入区</text>
<text class="flow-value" :class="'counter-animate-' + o + '-' + idx">{{ child.data
}}</text>
</view>
<view class="flow-divider">|</view>
<view class="flow-item flow-item-counter">
<text class="flow-label">断面</text>
<text class="flow-value" :class="'counter-animate-' + o + '-' + idx">{{ child.flow
}}</text>
</view>
</view>
<view class="expand-arrow" :class="{ 'expanded': child.expanded }">▼</view>
</view>
</view>
<!-- 方向车流数据 -->
<view class="direction-list direction-expand-animation"
v-if="child.expanded && child.list && child.list.length > 0">
<view class="direction-item direction-item-slide" v-for="(direction, didx) in child.list"
:key="didx" :style="'animation-delay: ' + (didx * 0.1) + 's'">
<view class="direction-header">
<view class="direction-info">
<text class="direction-name">{{ direction.name }}区</text>
<text class="direction-percent direction-percent-highlight">{{ direction.bili
}}%</text>
</view>
<view class="vehicle-stats">
<view class="vehicle-type-group">
<view class="vehicle-type-item vehicle-item-bounce"
:style="'animation-delay: ' + (didx * 0.1 + 0.2) + 's'">
<text class="vehicle-type-label">大型车</text>
<text class="vehicle-count-value vehicle-count-pulse">{{
direction.LargeVehicle_Count }}</text>
</view>
<view class="vehicle-type-item vehicle-item-bounce"
:style="'animation-delay: ' + (didx * 0.1 + 0.3) + 's'">
<text class="vehicle-type-label">中型车</text>
<text class="vehicle-count-value vehicle-count-pulse">{{
direction.MediumVehicle_Count }}</text>
</view>
<view class="vehicle-type-item vehicle-item-bounce"
:style="'animation-delay: ' + (didx * 0.1 + 0.4) + 's'">
<text class="vehicle-type-label">小型车</text>
<text class="vehicle-count-value vehicle-count-pulse">{{
direction.MinVehicle_Count }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 无数据提示 -->
<view class="no-traffic-data" v-if="!bayonetProgress || bayonetProgress.length === 0">
<view class="no-data-icon">🚗</view>
<text class="no-data-text">暂无车流分析数据</text>
</view>
</view>
</view>
</view>
</view>
<!-- 日结上传区域 -->
<view class="upload-section" v-if="regionList.length > 0">
<view class="upload-header">
<view class="upload-title">
<view class="upload-icon">📊</view>
<view class="upload-text">日结上传状态</view>
</view>
<view class="upload-summary">
<view class="summary-label">上传情况</view>
<view class="summary-value">{{ headMsg.uploadState }}</view>
</view>
</view>
</view>
<template v-if="theRequest && theRequest.GroupType == 1020 && regionList.length">
<view class="service-list">
<view v-for="(item, i) in regionList[0].child" :key="i" class="service-card" @click="toDetail(item)">
<view class="service-header">
<view class="service-info">
<view class="service-name">🏢 {{ item.serverpart_Name }}</view>
<view class="service-revenue">¥{{ $util.fmoney(item.cashpay, 2) }}</view>
</view>
<view class="service-status">
<view class="upload-rate" :class="{ 'warning': item.uploadcount != item.totalcount }">
{{ item.totalcount > item.uploadcount ?
$util.fmoney((item.uploadcount / item.totalcount) * 100, 2) : 100 }}%
</view>
<view class="upload-fraction" :class="{ 'incomplete': item.uploadcount != item.totalcount }">
{{ item.uploadcount }}/{{ item.totalcount }}
</view>
</view>
</view>
<view class="service-indicator">
<view class="status-dot"
:class="{ 'complete': item.uploadcount == item.totalcount, 'incomplete': item.uploadcount != item.totalcount }">
</view>
<view class="arrow-icon"></view>
</view>
</view>
</view>
</template>
<template v-else-if="theRequest && theRequest.GroupType == 1000">
<view class="region-list list-fade-in" v-show="regionShow1">
<block v-for="(item, i) in regionList" :key="i">
<view class="region-card region-card-slide" @click="toggleRegion(item)"
:style="'animation-delay: ' + (i * 0.1) + 's'">
<view class="region-main">
<view class="region-info">
<view class="region-name">{{ item.name }}</view>
<view class="region-revenue">{{ $util.fmoney(item.cashpay, 2) }}</view>
</view>
<view class="region-metrics">
<view class="metric-item">
<view class="metric-label">上传率</view>
<view class="metric-value rate" :class="{ 'incomplete': item.uploadcount != item.totalcount }">
{{ (item.totalcount > item.uploadcount) ?
$util.fmoney((item.uploadcount / item.totalcount) * 100, 2) : '100.00'
}}%
</view>
</view>
<view class="metric-item">
<view class="metric-label">状态</view>
<view class="metric-value fraction"
:class="{ 'incomplete': item.uploadcount != item.totalcount }">
{{ item.uploadcount }}/{{ item.totalcount }}
</view>
</view>
<view class="expand-indicator" :class="{ 'expanded': item.show }">
<view class="expand-icon"></view>
</view>
</view>
</view>
<view class="service-sublist expand-collapse" v-show="item.show">
<view class="subservice-card subservice-slide" v-for="(child, index) in item.child"
:class="{ 'visited': child.visited }" :key="index" @click.stop="toDetail(child)"
:style="'animation-delay: ' + (index * 0.05) + 's'">
<view class="subservice-info">
<view class="subservice-name">{{ child.serverpart_Name }}</view>
<view class="subservice-revenue">{{ $util.fmoney(child.cashpay, 2) }}
</view>
</view>
<view class="subservice-status" :class="{ 'incomplete': child.uploadcount != child.totalcount }">
{{ child.uploadcount }}/{{ child.totalcount }}
</view>
<view class="subservice-arrow"></view>
</view>
</view>
</view>
</block>
</view>
</template>
<template v-else>
<shopCell v-for="(item, i) in regionList" :key="i" :item='item' @toggleShow="toggleShow" :i='i' />
</template>
</view>
<template
v-if="theRequest && theRequest.ProvinceCode == '620000' || theRequest.ProvinceCode == '530000' || theRequest.ProvinceCode == '734100'">
<view class="uni-inline-item modle-title">
<image src="/static/images/revenue/product-ranking.png" mode="aspectFit"></image>
<text class="strong-text">商品销售排行</text>
</view>
<RankContent :wechatPushSalesList="wechatPushSalesList" :provinceCode="currentProvinceCode" v-if="!isLoading"></RankContent>
</template>
</view>
<view v-if="!showPage && !isLoading">
<noFound :nodata="!showPage && !isLoading" :text="'您暂无' + lastDay + '营收数据'" />
</view>
</view>
</template>
<script>
import {
mapState
} from 'vuex';
import uCharts from '@/components/u-charts.js';
import shopCell from './components/listUnit.vue'
import AnhHead from './components/anhHead.vue'
import RankContent from './components/RankContent.vue'
import anhuiYestodayRevenueData from './components/anhuiYestodayRevenueData.js'
var rincanvas = {};
import request from '@/util/index.js'
export default {
data() {
const lastDay = this.$util.cutDate(new Date(), 'YYYY-MM-DD', -1)
const nowDay = this.$util.cutDate(new Date(), 'MM月DD日')
return {
showPage: false,
opacity: 0, // 背景颜色透明度
customBarH: this.CustomBar,
statusBarH: this.StatusBar,
isLoading: true,
theRequest: null,
sevenDate: null,
lastDay: lastDay, // 该页面的最近有效日期
nowDay: nowDay,
headMsg: null,
today: null, // 今日数据
regionList: null,
nowTab: 1,
regionShow1: true,
regionProgress: null,
tradeType: null,
areaProgress: null,
bayonetProgress: [], // 初始化为空数组
modelProgress: null,
isup: true,
wechatPushSalesList: null, // 甘肃单品排行数据
currentMoney: '',
currentProvinceCode: '' // 当前省份代码
}
},
computed: {
...mapState({
'PushAuthority': (state) => state.userData.PushAuthority,
'ProvinceCode': (state) => state.userData.ProvinceCode,
}),
hasSeverpartIndexAuthority() {
let theRequest = this.theRequest
if (this.theRequest && theRequest.ProvinceCode && this.PushAuthority) {
return this.PushAuthority.some(n =>
n.ProvinceCode == theRequest.ProvinceCode && n.ShopAnalysisType == 1
)
}
return false
},
// 省份主题配色
provinceTheme() {
const themes = {
'330200': { // 宁波
primary: '#1890FF',
secondary: '#69C0FF',
background: '#E6F7FF',
accent: '#40A9FF'
},
'340000': { // 安徽
primary: '#748ED6',
secondary: '#91A7E3',
background: '#F0F4FF',
accent: '#A3B8E8'
},
'500000': { // 重庆
primary: '#FA541C',
secondary: '#FF7A45',
background: '#FFF2E8',
accent: '#FF8C73'
},
'510000': { // 四川
primary: '#FA8C16',
secondary: '#FFA940',
background: '#FFF7E6',
accent: '#FFAB65'
},
'520000': { // 贵州
primary: '#52C41A',
secondary: '#73D13D',
background: '#F6FFED',
accent: '#7CB342'
},
'530000': { // 云南(保持原色)
primary: '#27B25F',
secondary: '#4CCC7F',
background: '#F6FFED',
accent: '#5CDB87'
},
'630000': { // 青海
primary: '#13C2C2',
secondary: '#36CFC9',
background: '#E6FFFB',
accent: '#5CDBD3'
},
'734100': { // 建工
primary: '#E91E63',
secondary: '#F06292',
background: '#FCE4EC',
accent: '#F48FB1'
}
}
return themes[this.currentProvinceCode] || themes['340000'] // 如果找不到对应省份,使用云南配色
}
},
components: {
shopCell,
AnhHead,
RankContent
},
methods: {
// 解析二维码参数 - uniapp兼容版本
parseQRCodeParams: function (qParam) {
try {
// 解码URL
var decodedUrl = decodeURIComponent(qParam)
console.log('解码后的URL:', decodedUrl)
// 提取查询参数部分
var urlParts = decodedUrl.split('?')
if (urlParts.length < 2) {
console.log('二维码URL没有参数使用默认值')
return this.getDefaultParams()
}
var queryString = urlParts[1]
var params = {}
// 解析查询参数
var pairs = queryString.split('&')
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i]
var equalIndex = pair.indexOf('=')
if (equalIndex > -1) {
var key = pair.substring(0, equalIndex)
var value = pair.substring(equalIndex + 1)
if (key && value !== '') {
try {
params[decodeURIComponent(key)] = decodeURIComponent(value)
} catch (e) {
// 解码失败时使用原值
params[key] = value
}
}
}
}
console.log('解析的参数对象:', params)
// 确保必需参数存在
var finalParams = {
ProvinceCode: params.ProvinceCode || '340000', // 默认云南
GroupType: params.GroupType || '1000',
time: params.time || this.lastDay,
ServerpartIds: params.ServerpartIds || ''
}
// 处理时间格式
if (finalParams.time) {
var timeStr = finalParams.time
console.log('原始时间字符串:', timeStr)
// 处理URL编码的+号(空格被编码为+
timeStr = timeStr.replace(/\+/g, ' ')
console.log('处理+号后:', timeStr)
// 分离日期和时间部分
var datePart = ''
var timePart = ''
if (timeStr.indexOf(' ') > -1) {
var parts = timeStr.split(' ')
datePart = parts[0]
timePart = parts[1] || ''
} else {
datePart = timeStr
}
// 处理日期部分2025/9/1 -> 2025/09/01
if (datePart.indexOf('/') > -1) {
var dateSegments = datePart.split('/')
if (dateSegments.length === 3) {
var year = dateSegments[0]
var month = dateSegments[1].length === 1 ? '0' + dateSegments[1] : dateSegments[1]
var day = dateSegments[2].length === 1 ? '0' + dateSegments[2] : dateSegments[2]
datePart = year + '/' + month + '/' + day
}
}
// 处理时间部分:确保格式正确
if (timePart && timePart.indexOf(':') > -1) {
var timeSegments = timePart.split(':')
if (timeSegments.length >= 2) {
var hour = timeSegments[0].length === 1 ? '0' + timeSegments[0] : timeSegments[0]
var minute = timeSegments[1].length === 1 ? '0' + timeSegments[1] : timeSegments[1]
var second = '00'
if (timeSegments[2]) {
second = timeSegments[2].length === 1 ? '0' + timeSegments[2] : timeSegments[2]
}
timePart = hour + ':' + minute + ':' + second
}
}
// 组合最终时间字符串
if (timePart) {
finalParams.time = datePart + ' ' + timePart
} else {
finalParams.time = datePart
}
console.log('最终时间格式:', finalParams.time)
// 验证时间格式
if (finalParams.time.length < 8) {
console.log('时间格式不正确,使用默认时间')
finalParams.time = this.lastDay
}
}
console.log('最终参数:', finalParams)
return finalParams
} catch (error) {
console.log('解析二维码参数失败:', error)
return this.getDefaultParams()
}
},
// 获取默认参数 - uniapp兼容版本
getDefaultParams: function () {
return {
ProvinceCode: '340000', // 默认云南
GroupType: '1000',
time: this.lastDay,
ServerpartIds: ''
}
},
// 初始化省份代码
initProvinceCode() {
let provinceCode = ''
// 优先使用theRequest中的省份代码
if (this.theRequest && this.theRequest.ProvinceCode) {
provinceCode = this.theRequest.ProvinceCode
} else if (this.ProvinceCode) {
// 备用方案使用store中的省份代码
provinceCode = this.ProvinceCode
}
// 只有在确实找到省份代码时才设置和更新
if (provinceCode) {
this.currentProvinceCode = provinceCode
console.log('设置省份主题:', provinceCode)
// 强制更新以应用新主题
this.$forceUpdate()
}
},
// 获取主题颜色(小程序兼容版本)
getThemeColors() {
return this.provinceTheme
},
toggleCard(isup) {
this.isup = !isup
// 移除不必要的$forceUpdate由Vue自动检测变化
},
bindDateChange(e) { // 切换日期 加载选中日期的营收数据
// let nowDate = this.theRequest.time
let selectT = new Date(e.detail.value)
if (selectT <= new Date(this.lastDay)) {
this.theRequest.time = e.detail.value
this.theRequest.month = this.$util.cutDate(e.detail.value, 'YYYYMM')
this.sevenDate = [this.$util.cutDate(selectT, 'MM.DD', -13),
this.$util.cutDate(selectT, 'MM.DD', -7)]
uni.showLoading({
title: '正在加载...',
mask: false // 减少遮罩,提升用户体验
})
// 异步并行加载数据
Promise.all([
this.initData(),
this.todayAmount()
]).catch(err => {
console.error('数据刷新失败:', err)
uni.hideLoading()
})
// 移除不必要的$forceUpdate
}
},
toDetail(item, provinceId) { // 如果当前页面至存在一个初始化的省份编码 则不需要传入参数provinceId
let date = this.theRequest.time
let pcode = provinceId || this.theRequest.ProvinceCode
let severpartIndexPath = '/pages/everdayRenven/AnhuiServerpart?ServerpartIds=' + item.serverpart_Id +
'&time=' + date + '&ProvinceCode=' + pcode
let serverpartUploadPath = '/pages/everdayRenven/detail?id=' + item.serverpart_Id + '&time=' + date +
'&provinceId=' + pcode
// 是否有权限进入服务区营收分析页面
let canToSeverpartIndex = !provinceId ? this.hasSeverpartIndexAuthority : this.PushAuthority.some(n => {
return n.ProvinceCode == provinceId && n.ShopAnalysisType == 1
})
this.$util.toNextRoute('navigateTo', severpartIndexPath)
// 使用Vue.set确保响应式更新
this.$set(item, 'visited', true)
},
selectTab(name, index) {
this[name] = index
// 当切换到车流分析标签时,强制触发响应式更新
if (name === 'nowTab' && index === 4) {
console.log('切换到车流分析标签')
console.log('当前bayonetProgress:', this.bayonetProgress)
console.log('bayonetProgress类型:', typeof this.bayonetProgress)
console.log('bayonetProgress长度:', this.bayonetProgress ? this.bayonetProgress.length : 'null/undefined')
console.log('条件判断结果:')
console.log('- 有数据条件:', !!(this.bayonetProgress && this.bayonetProgress.length > 0))
console.log('- 无数据条件:', !!(!this.bayonetProgress || this.bayonetProgress.length === 0))
// 使用$nextTick确保DOM更新
this.$nextTick(() => {
console.log('车流分析DOM更新完成')
})
}
},
toggleRegion(item) {
// 使用Vue.set确保响应式更新
this.$set(item, 'show', !item.show)
},
// 切换区域展开/收起状态
toggleRegionCollapse(item) {
this.$set(item, 'collapsed', !item.collapsed)
},
// 切换服务区展开/收起状态
toggleServiceArea(region, serviceIndex) {
const service = region.list[serviceIndex]
this.$set(service, 'expanded', !service.expanded)
},
touchPie(e, id) {
rincanvas[id].showToolTip(e, {
format: function (item) {
return item.name + ':' + item.data
}
});
},
todayAmount() {
let _this = this
let theRequest = this.theRequest
this.$request.$get('getCurRevenue', {
pushProvinceCode: theRequest.ProvinceCode,
serverPartId: theRequest.GroupType == 1000 ? '' : theRequest.ServerpartIds
}).then(res => {
if (res.ResultCode != 100) return
let avrticket = (res.Data.TOTALTICKET != 0 && res.Data.TOTALPRICE != 0) ? res.Data.TOTALPRICE /
res.Data
.TOTALTICKET : 0
_this.today = {
timeMoney: this.$util.fmoney(res.Data.TOTALPRICE, 2),
totalTicketCount: res.Data.TOTALTICKET,
avrticketCount: this.$util.fmoney(avrticket, 2),
}
// 移除不必要的$forceUpdate
})
},
showPie(obj) {
let data = {
series: []
}
const ctx = uni.createCanvasContext(obj.id, this);
data.series = data.series.concat(obj.data)
// 微信小程序专用优化配置 - 完全去除动画和过渡效果
rincanvas[obj.id] = new uCharts({
context: ctx,
color: obj.colors,
type: 'ring',
fontSize: 12,
padding: [15, 15, 25, 15],
legend: {
show: false,
padding: 5,
lineHeight: 11,
margin: 0,
},
background: '#FFFFFF',
pixelRatio: 1, // 使用较低的像素比率减少渲染负担
series: data.series,
width: uni.upx2px(686),
height: uni.upx2px(510),
dataLabel: true,
// 核心性能优化 - 完全禁用所有动画和交互
animation: false, // 禁用所有动画
animationDuration: 0, // 动画时长为0
animationEasing: 'linear', // 使用最简单的缓动函数
enableScroll: false, // 禁用滚动
enableMarkLine: false, // 禁用标记线
enableDataLabel: true, // 仅启用数据标签
disableTouch: true, // 禁用触摸交互以提高性能
extra: {
ring: {
ringWidth: 40,
labelWidth: 16,
border: true,
borderWidth: 1,
borderColor: '#fff',
// 完全禁用交互效果
activeOpacity: 1, // 禁用透明度变化
activeRadius: 0, // 禁用半径变化
// 微信小程序特殊优化
linearGradient: false, // 禁用渐变以提高渲染性能
disableAnimation: true // uCharts自定义禁用动画参数
}
},
// 微信小程序Canvas优化
onRenderComplete: function () {
// 渲染完成后立即绘制,避免视觉延迟
ctx.draw(false);
}
});
// 立即绘制,不使用回调以避免异步延迟
ctx.draw(false);
console.log('图表立即渲染完成:', obj.id);
},
getDetail(obj) {
console.log('obj', obj)
let _this = this
this.$request.$webGet('CommercialApi/Revenue/GetServerpartEndAccountList', {
Serverpart_ID: obj.ServerpartIds,
pushProvinceCode: obj.ProvinceCode,
Statistics_Date: obj.time,
}).then(res => {
if (res.Result_Code != 100) return
res.Result_Data.ShopEndaccountList.map(n => {
n.show = false
n.detail = this.getListDetail(n)
})
_this.regionList = res.Result_Data.ShopEndaccountList
console.log('_this.regionList', _this.regionList)
})
},
getListDetail(data) {
let arr = []
let keyJson = {
SHOWMORE_SIGN: {
1: '【长款】',
2: '【异常长款】'
}, // 长款
SHOWLESS_SIGN: {
1: '【短款】',
2: '【异常短款】'
}, // 短款
SHOWABNORMAL_SIGN: {
1: '【异常校验】'
}, // 异常日结
SHOWSCAN_SIGN: {
1: '【扫】'
}, // 扫码上传
SHOWSSUPPLY_SIGN: {
1: '【补】'
}, // 账期补录
SHOWCHECK_SIGN: {
1: '【稽核检查】'
}, // 稽核检查
INTERFACE_SIGN: {
1: '【接口传输】'
}, // 接口传输
}
var keyCode = ['SHOWABNORMAL_SIGN', 'SHOWCHECK_SIGN', 'SHOWMORE_SIGN', 'SHOWLESS_SIGN', 'SHOWSCAN_SIGN',
'SHOWSSUPPLY_SIGN', 'INTERFACE_SIGN'
]
keyCode.map(n => {
if (keyJson[n][data[n]]) arr.push(keyJson[n][data[n]])
})
return arr
},
toggleShow(i) {
let item = this.regionList[i]
// 使用Vue.set确保响应式更新
this.$set(item, 'show', !item.show)
},
defaultMsg() {
let option = null
let _this = this
if (this.PushAuthority && this.PushAuthority.length > 1) {
option = this.PushAuthority.find(n => n.ProvinceCode === _this.ProvinceCode)
} else {
option = this.PushAuthority[0]
}
return {
...option
}
},
async initData() { // 初始化营收数据
let _this = this
console.log('this.theRequest1', this.theRequest)
// 检查theRequest是否存在如果不存在则使用默认值
if (!this.theRequest) {
console.error('theRequest未初始化')
this.isLoading = false
this.showPage = false
uni.hideLoading()
return
}
this.theRequest.GroupType = 1000
this.theRequest.ServerpartIds = ''
try {
// 异步加载数据,添加错误处理
const result = await anhuiYestodayRevenueData.getData(this.theRequest)
if (!result) {
console.error('数据获取失败')
this.isLoading = false
this.showPage = false
uni.hideLoading()
return
}
const [reginList, totalData, busniessTypePie, busniessTradePie, busniessAreaPie, bayonetPie] = result
if (this.theRequest.GroupType !== 1010) {
console.log('reginList', reginList)
this.regionList = reginList // 营收上传列表 reginListModel[]
}
// 组合及格式化 头部卡片总营收数据。
totalData.tickave = totalData.ticketCount > 0 ? this.$util.fmoney(
totalData.cashPay / totalData.ticketCount, 2) : 0
totalData.countave = totalData.totalCount > 0 ? this.$util.fmoney(
totalData.cashPay / totalData.totalCount, 2) : 0
totalData.totalMoneyShow = this.$util.fmoney(totalData.cashPay) // 营收总数据格式化
totalData.dayOfShow = this.$util.cutDate(this.theRequest.time, 'MM月DD日') // 当前日期
totalData.budgetamoutShow = totalData.budgetAmount ?
_this.$util.fmoney(totalData.budgetAmount) : 0.00 // 预算总营收
busniessTypePie.forEach((n, i) => {
if (n.name === '自营') {
// 预算和实际差额
totalData.diffBudgetTotal = Math.abs(totalData.budgetAmount - n.data)
// 预算和实际增长额比例
totalData.diffBili = totalData.budgetAmount > 0 ?
this.$util.fmoney((totalData.diffBudgetTotal /
totalData.budgetAmount) * 100, 2) : '100'
}
});
this.headMsg = totalData
// 饼图分析及数据条形分析
const [progressList, pieList] = this.getProgressData(busniessTypePie, totalData.cashPay)
this.modelProgress = progressList
// 优化图表渲染,减少卡顿
this.$nextTick(() => {
// 使用requestAnimationFrame优化渲染时机
const renderCharts = () => {
try {
// 经营类型分析
var colors1 = ['#FFAC37', '#6B75B8'];
if (pieList.length > 0) {
this.showPie({
id: 'modelCont',
data: pieList,
colors: colors1,
});
}
// 由于已优化单个图表性能,现在可以立即渲染所有图表
// 第二个图表 - 饼图分析及数据条形分析
const [tprogressList, tpieList] = this.getProgressData(busniessTradePie, totalData.cashPay)
this.regionProgress = tprogressList
// 经营类型分析
var colors2 = ['#5E67B4', '#4E5699', '#75B7AD', '#AFB7E6'];
if (tpieList.length > 0) {
this.showPie({
id: 'businessCont',
data: tpieList,
colors: colors2,
});
}
// 第三个图表 - 区域营收分析(立即渲染)
const [aprogressList, apieList] = this.getProgressData(busniessAreaPie, totalData.cashPay)
this.areaProgress = aprogressList
// 区域营收分析
var colors3 = ['#FFAC37', '#d8ece9', '#f7f5f6', '#b2b7e3', '#F4B27A', '#F3B1C9', '#e0e3f7', '#FFE886', '#f7f5f6'];
if (apieList.length > 0) {
this.showPie({
id: 'areaCont',
data: apieList,
colors: colors3,
});
}
} catch (error) {
console.error('图表渲染出错:', error);
uni.hideLoading();
}
};
// 立即执行图表渲染,无需延时
renderCharts();
});
// 安徽省本级显示的内容
if (this.theRequest.GroupType == 1000 && this.theRequest.ProvinceCode == 340000) {
// 显示片区车流量数据分析
console.log('处理车流分析数据:', bayonetPie)
console.log('bayonetPie类型和长度:', typeof bayonetPie, bayonetPie ? bayonetPie.length : 'null/undefined')
if (bayonetPie && bayonetPie.length > 0) {
console.log('开始处理车流数据...')
const [bayonetList] = this.getBayonetData(bayonetPie)
console.log('getBayonetData返回结果:', bayonetList)
console.log('bayonetList长度:', bayonetList ? bayonetList.length : 'null/undefined')
// 使用Vue.set确保响应式更新
this.$set(this, 'bayonetProgress', bayonetList || [])
console.log('最终设置bayonetProgress:', this.bayonetProgress)
console.log('bayonetProgress.length:', this.bayonetProgress.length)
} else {
// 没有数据时设置为空数组
this.$set(this, 'bayonetProgress', [])
console.log('无车流分析数据设置为空数组bayonetPie:', bayonetPie)
}
} else {
// 不是安徽本级的情况,确保数据为空
console.log('不是安徽本级,清空车流数据')
this.$set(this, 'bayonetProgress', [])
}
// 数据加载完成后显示页面并隐藏loading
// 即使没有数据也要显示页面,避免白屏
this.showPage = true
this.isLoading = false
uni.hideLoading()
} catch (error) {
console.error('initData执行错误:', error)
this.isLoading = false
this.showPage = false
uni.hideLoading()
uni.showToast({
title: '数据加载失败',
icon: 'none'
})
}
},
getProgressData(data, total) {
var _data1 = [];
var _data2 = [];
let _this = this
data.forEach((n, i) => {
_data1.push({
...n,
textColor: '#999',
formatter: function (arg) {
if (typeof arg === 'number') {
return [n.name, (arg * 100).toFixed(2) + '%']
} else {
return [arg.name, (arg._proportion_ * 100).toFixed(2) + '%']
}
}
});
_data2.push({
name: n.name,
num: n.data,
data: _this.$util.fmoney(n.data, 2), //+Number(data2[n[0]])
bili: _this.$util.fmoney((n.data / total) * 100, 2)
});
});
_data1.sort(function (a, b) {
return b.data - a.data
})
_data2.sort(function (a, b) {
return b.num - a.num
})
return [_data2, _data1];
},
getBayonetData(data) {
var _data1 = [];
let _this = this
data.forEach((n, i) => {
var childData = [];
n.spList.forEach((m, i) => {
// 获取服务区各个方位的入区信息
var _list = [];
m.regionList.forEach((o, i) => {
_list.push({
name: o.name,
data: o.Vehicle_Count,
flow: o.SectionFlow_Count,
bili: o.SectionFlow_Count == 0 ? 100 : _this.$util.fmoney(
(o.Vehicle_Count / o.SectionFlow_Count) * 100, 2),
MinVehicle_Count: o.MinVehicle_Count,
MediumVehicle_Count: o.MediumVehicle_Count,
LargeVehicle_Count: o.LargeVehicle_Count
});
});
_list.sort(function (a, b) {
return b.bili - a.bili
})
childData.push({
name: m.name,
data: m.Vehicle_Count, //+Number(data2[n[0]])
flow: m.SectionFlow_Count,
bili: m.SectionFlow_Count == 0 ? 0 : _this.$util.fmoney(
(m.Vehicle_Count / m.SectionFlow_Count) * 100, 2),
list: _list
});
});
childData.sort(function (a, b) {
return b.bili - a.bili
})
_data1.push({
name: n.name,
index: n.index,
list: childData
});
});
_data1.sort(function (a, b) {
return a.index - b.index
})
return [_data1];
},
async getRankContent() { // 甘肃需要单品排行显示
const {
ProvinceCode,
time
} = this.theRequest
const data = await this.$request.$webGet('CommercialApi/Revenue/GetWechatPushSalesList', {
pushProvinceCode: ProvinceCode,
Statistics_Date: time
})
if (data.Result_Code === 100 && data.Result_Data.TotalCount > 0) {
this.wechatPushSalesList = {}
data.Result_Data.List.forEach(n => {
this.wechatPushSalesList[n.Data_Type] = n.GoodsList
})
} else {
this.wechatPushSalesList = {}
}
// 移除不必要的$forceUpdate
},
// 实时营收
handleRealRevenue() {
const {
ProvinceCode,
} = this.theRequest
const date = new Date()
let y = date.getFullYear()
let m = date.getMonth() + 1
let d = date.getDate()
if (m < 10) {
m = '0' + m
}
if (d < 10) {
d = '0' + d
}
let req = {
pushProvinceCode: ProvinceCode,
StatisticsDate: `${y}-${m}-${d}`
}
request.$webGet('CommercialApi/Revenue/GetCurRevenue', req).then(res => {
console.log('res222222', res)
this.currentMoney = this.$util.fmoney(res.Result_Data.CurRevenueAmount, 2)
})
},
},
onUnload() {
this.$util.addUserBehavior()
},
onPageScroll(options) {
this.opacity = (options.scrollTop - 30) / 68
},
onLoad(option) {
console.log('AnhuiIndex onLoad 接收参数:', option)
// 处理扫码进入的情况
if (option.q) {
console.log('扫码进入,解析二维码参数')
option = this.parseQRCodeParams(option.q)
console.log('解析后的参数:', option)
}
// 显示更友好的加载提示
uni.showLoading({
title: '正在加载...',
mask: false // 允许用户操作,避免阻塞感
})
if (option.ProvinceCode) { // 从推送进入或扫码进入
console.log('进入页面,参数:', JSON.stringify(option))
this.theRequest = option
// 确保时间格式正确
if (option.time) {
console.log('处理前的时间:', option.time)
// 如果是完整的日期时间格式提取日期部分用于API调用
var dateForApi = option.time
if (option.time.indexOf(' ') > -1) {
dateForApi = option.time.split(' ')[0]
}
// 转换为API需要的格式 YYYY-MM-DD
option.time = this.$util.cutDate(dateForApi, 'YYYY-MM-DD')
option.month = this.$util.cutDate(option.time, 'YYYYMM')
console.log('API用时间格式:', option.time, '月份:', option.month)
} else {
option.time = this.lastDay
option.month = this.$util.cutDate(this.lastDay, 'YYYYMM')
}
console.log('处理后的theRequest:', JSON.stringify(this.theRequest))
// 初始化省份主题
this.initProvinceCode()
// 优化加载顺序:先显示基础界面,再异步加载数据
Promise.all([
this.initData(),
this.handleRealRevenue()
]).catch(err => {
console.error('数据加载失败:', err)
this.isLoading = false
this.showPage = false
uni.hideLoading()
uni.showToast({
title: '加载失败,请重试',
icon: 'none'
})
})
} else { // 默认
if (this.PushAuthority.length > 0) {
console.log('11111')
this.theRequest = this.defaultMsg() || {}
let storeTime = uni.getStorageSync('lastDay')
if (storeTime) {
this.theRequest.time = storeTime
this.theRequest.month = this.$util.cutDate(storeTime, 'YYYYMM')
} else {
this.theRequest.time = this.lastDay
this.theRequest.month = this.$util.cutDate(this.lastDay, 'YYYYMM')
}
// 初始化省份主题
this.initProvinceCode()
// 优化加载顺序:先显示基础界面,再异步加载数据
Promise.all([
this.initData(),
this.handleRealRevenue()
]).catch(err => {
console.error('数据加载失败:', err)
this.isLoading = false
this.showPage = false
uni.hideLoading()
uni.showToast({
title: '加载失败,请重试',
icon: 'none'
})
})
} else {
uni.hideLoading()
this.theRequest = null
this.isLoading = false
this.showPage = false
this.opacity = 1
// 即使没有权限,也设置一个默认省份以避免样式异常
this.currentProvinceCode = '340000'
}
}
this.nowTab = this.theRequest && this.theRequest.ProvinceCode == 340000 ? 1 : 2
this.todayAmount()
if (this.theRequest && this.theRequest.GroupType == 1010) {
this.getDetail(this.theRequest)
}
// 若省份为甘肃,则加载甘肃单品排行
if (this.theRequest && (this.theRequest.ProvinceCode == "620000" || this.theRequest.ProvinceCode == "530000" || this.theRequest.ProvinceCode == "734100")) {
this.getRankContent()
}
},
}
</script>
<style lang="scss" scoped>
// 省份主题定义 - 创建mixin以便复用
@mixin theme-colors($primary, $secondary) {
// 主要操作按钮和强调元素 - 使用主题色
.section-badge {
background: $primary;
}
.tab-item.active {
background: $primary;
}
.operation-tab-box .operation-tab-unit.active {
background: linear-gradient(135deg, $primary, $secondary);
}
// 装饰性元素 - 使用主题色
.data-card::before {
background: $primary;
}
.service-card::before {
background: $primary;
}
.region-main::before {
background: $primary;
}
.progress-fill {
background: $primary;
}
.operation-c-list .progress .bgO {
background: linear-gradient(90deg, $primary, $secondary);
}
.region-cell-area li:nth-child(2n+1):before {
background: linear-gradient(135deg, $primary, $secondary);
}
.status-dot.complete {
background: $primary;
}
// 关键数据和金额 - 使用主题色突出显示
.amount {
color: $primary;
}
.service-revenue {
color: $primary;
}
.region-revenue {
color: $primary;
}
.subservice-revenue {
color: $primary;
}
.flow-value {
color: $primary;
}
// 百分比和比率 - 使用主题色
.percentage {
color: $primary;
}
.service-ratio {
color: $primary;
}
.direction-percent {
color: $primary;
}
.metric-value.rate {
color: $primary;
}
// 状态和上传相关 - 使用主题色
.upload-count {
color: $primary;
}
.upload-rate {
color: $primary;
}
.summary-value {
color: $primary;
}
// 特殊状态 - 使用主题色
.subservice-card.visited .subservice-name {
color: $primary;
}
.uni-icon-arrowright.active {
color: $primary;
}
// 注释掉的部分,改用默认灰色/黑色,不跟随主题变化
// .flow-ratio { color: $primary; }
// .stat-value { color: $primary; }
// .direction-ratio { color: $primary; }
}
// 宁波 330200
.province-theme-330200 {
@include theme-colors(#1890FF, #69C0FF);
}
// 安徽 340000
.province-theme-340000 {
@include theme-colors(#748ED6, #91A7E3);
}
// 重庆 500000
.province-theme-500000 {
@include theme-colors(#FA541C, #FF7A45);
}
// 四川 510000
.province-theme-510000 {
@include theme-colors(#FA8C16, #FFA940);
}
// 贵州 520000
.province-theme-520000 {
@include theme-colors(#52C41A, #73D13D);
}
// 云南 530000
.province-theme-530000 {
@include theme-colors(#27B25F, #4CCC7F);
}
// 青海 630000
.province-theme-630000 {
@include theme-colors(#13C2C2, #36CFC9);
}
// 海南 734100
.province-theme-734100 {
@include theme-colors(#E91E63, #F06292);
}
.page-body {
background-color: #f8f9fa;
position: relative;
box-sizing: border-box;
min-height: 100vh;
padding-bottom: env(safe-area-inset-bottom);
}
.uni-flex-column {
flex-direction: column;
}
.justify-between {
justify-content: space-between;
}
.justify-around {
justify-content: space-around;
}
.uni-icon-arrowright {
font-size: 32rpx;
color: #5A5A5A;
}
.uni-icon-arrowright.active {
transform: rotate(90deg);
}
.ct01 {
color: #383838;
}
.strong-text {
font-weight: 700;
}
.ct-red {
color: #ff4757 !important;
font-weight: 600;
}
.new-content {
background: url('https://eshangtech.com/ShopICO/ahyd-BID/revenue/banner.png') no-repeat bottom left;
height: 383rpx;
background-size: 100%;
color: #fff;
box-sizing: border-box;
padding-top: 20rpx;
}
.new-content .text-title {
flex: 1;
text-align: center;
font-weight: 600;
}
.uni-icon-arrowleft {
font-size: 38rpx;
padding: 8rpx 16rpx;
}
.page-title {
display: flex;
align-items: center;
padding-right: 70rpx;
width: 100%;
color: #fff;
background-color: #3b64a3;
}
.page-title .uni-icon-arrowleft,
.page-title .uni-icon-arrowleft:before {
color: #fff;
}
.new-content .header-card {
background-size: contain;
background-repeat: no-repeat;
background-position: center right;
padding: 0 30rpx;
color: #BED4F4;
padding-top: 8rpx;
position: relative;
}
.head-log {
position: absolute;
right: 12rpx;
top: 0rpx;
width: 376rpx;
height: 198rpx;
}
.new-content .top-number {
padding-top: 28rpx;
font-size: 76rpx;
padding-bottom: 16rpx;
color: #fff;
padding-left: 4rpx;
font-family: 'Bahnschrift Regular';
letter-spacing: 2rpx;
line-height: 1.1;
transition: all 1s;
}
.new-content .header-today-info {
align-items: baseline;
}
.new-content .header-today-info text {
font-family: 'Bahnschrift Regular';
font-size: 30rpx;
}
.ml10 {
margin-left: 20rpx;
}
.new-content .header-today-info text.fs12,
text.fs12 {
font-size: 24rpx;
}
.ml-135 {
margin-left: 135rpx;
}
.top-number view {
line-height: 1.2;
}
/* 图表性能优化样式 */
/* 性能优化的图表容器 */
.chart-optimized {
/* 减少重绘和回流 */
transform: translateZ(0);
will-change: auto;
backface-visibility: hidden;
/* 避免子元素的布局计算影响 */
contain: layout style paint;
}
/* 性能优化的Canvas */
.performance-chart {
/* 硬件加速 */
transform: translate3d(0, 0, 0);
/* 减少触摸延迟 */
touch-action: manipulation;
/* 优化渲染性能 */
image-rendering: optimizeSpeed;
image-rendering: -webkit-optimize-contrast;
/* 固定尺寸避免重新计算 */
width: 686rpx !important;
height: 510rpx !important;
/* 避免内容重排 */
flex-shrink: 0;
}
/* 减少动画对图表的影响 */
.chart-container.chart-scale-in {
/* 简化入场动画,减少性能消耗 */
animation-duration: 0.3s;
animation-timing-function: ease-out;
}
/* 图表加载态优化 */
.chart-section {
/* 预留空间避免布局跳动 */
min-height: 510rpx;
/* 减少layout thrashing */
contain: layout;
}
/* 禁用不必要的交互效果 */
.performance-chart:active,
.performance-chart:hover,
.performance-chart:focus {
/* 移除默认的交互反馈 */
transform: translate3d(0, 0, 0) !important;
transition: none !important;
}
.heade-text {
text-align: right;
line-height: 1;
letter-spacing: 0;
color: #BED4F4;
font-family: 'Bahnschrift Regular';
display: flex;
align-items: center;
}
/* 营收汇总 数据概览卡片 */
.revenue-card-cont {
padding: 6rpx 0 32rpx 0;
border-radius: 12rpx;
margin: 0 32rpx;
box-shadow: 0rpx 0rpx 6rpx 0px rgba(224, 224, 224, 0.54);
}
.head-cost-text {
color: #BED4F4;
font-size: 28rpx;
}
.top-revenue-card {
padding: 16rpx 30rpx 6rpx 30rpx;
font-family: 'Bahnschrift Regular';
color: #D7B89A;
align-items: flex-end;
}
.top-revenue-card .top-number {
font-size: 56rpx;
color: #D0AC8B;
line-height: 1;
}
.top-revenue-card .upLoad-text {
font-size: 32rpx;
}
.check-unit {
font-size: 24rpx;
margin-top: 16rpx;
flex: 1;
text-align: center;
}
.check-price-color {
font-size: 32rpx;
color: #868686;
line-height: 1.2;
font-family: 'Bahnschrift Regular';
}
.check-price-color text {
font-size: 22rpx;
}
/* end */
.modle-title {
padding: 0 32rpx 24rpx 32rpx;
font-size: 28rpx;
font-weight: 700;
color: #2c3e50;
display: flex;
align-items: center;
margin: 32rpx 20rpx 0 20rpx;
box-sizing: border-box;
image {
width: 40rpx;
height: 40rpx;
margin-right: 12rpx;
}
}
.modle-title .uni-icon-arrowdown {
font-size: 24rpx;
}
.modle-title picker {
color: #000000;
padding-right: 24rpx;
position: relative;
width: 164rpx;
}
.modle-title picker .revenue-date:before {
content: '';
position: absolute;
width: 0rpx;
height: 0rpx;
border: 0rpx;
border-left: 12rpx solid transparent;
border-right: 12rpx solid transparent;
border-top: 12rpx solid #000;
right: 22rpx;
top: 22rpx;
display: block;
border-radius: 6rpx;
}
.upload-count {
font-family: 'DIN Alternate', 'Bahnschrift', sans-serif;
font-size: 32rpx;
font-weight: 600;
color: #27B25F;
margin-left: 16rpx;
margin-right: 36rpx;
}
.region-title {
padding: 20rpx 60rpx 20rpx 80rpx;
position: relative;
font-size: 30rpx;
font-weight: bolder;
}
.region-title:before {
content: '';
position: absolute;
width: 20rpx;
height: 20rpx;
background: url(/static/images/revenue/select.png) no-repeat center;
background-size: contain;
left: 40rpx;
top: 36rpx;
}
.active.region-title:before {
background: url(/static/images/revenue/select_active.png) no-repeat center;
background-size: contain;
}
/*日结上传*/
.region-cell {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx 24rpx;
background-color: #fff;
margin-bottom: 12rpx;
border-radius: 12rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
border: 1rpx solid #f5f5f5;
transition: all 0.3s ease;
&:active {
transform: scale(0.98);
box-shadow: 0 4rpx 20rpx rgba(39, 178, 95, 0.15);
}
}
.region-cell .region-cell-unit {
flex: 2;
display: flex;
flex-direction: column;
color: #666;
font-size: 28rpx;
line-height: 1.4;
text-align: right;
.strong-text {
font-size: 28rpx;
font-weight: 700;
color: #2c3e50;
font-family: 'DIN Alternate', 'Bahnschrift', sans-serif;
}
}
.region-cell .region-cell-unit:nth-child(1) {
flex: 3;
text-align: left;
font-size: 30rpx;
font-weight: 600;
color: #333;
}
.region-cell .region-cell-unit:nth-child(3) {
line-height: 1.2;
font-size: 26rpx;
font-weight: 500;
padding: 8rpx 12rpx;
background-color: #f8f9fa;
border-radius: 8rpx;
min-width: 80rpx;
text-align: center;
}
.region-cell-image {
width: 48rpx;
display: flex;
align-items: center;
justify-content: center;
height: 48rpx;
.uni-icon-arrowright {
font-size: 28rpx;
color: #999;
transition: all 0.3s ease;
&.active {
transform: rotate(90deg);
color: #27B25F;
}
}
}
.region-cell image {
width: 14rpx;
height: 23rpx;
}
/*日结上传服务区*/
.region-cell-area {
max-height: 600rpx;
overflow: auto;
-webkit-overflow-scrolling: touch;
background-color: #f8f9fa;
border-radius: 12rpx;
margin: 12rpx 0 0 0;
padding: 16rpx 0;
}
.region-cell-area li.visited view:first-child {
color: #007AFF;
}
.region-cell-area li:after {
content: '';
font-size: 24rpx;
color: #999;
display: block;
position: absolute;
right: 16rpx;
top: 50%;
transform: translateY(-50%);
}
.region-cell-area li:nth-child(2n+1) {
background-color: #fff;
}
.region-cell-area li:nth-child(2n+1):before {
content: '';
background: linear-gradient(135deg, #27B25F, #4CCC7F);
width: 8rpx;
height: 8rpx;
border-radius: 50%;
display: block;
position: absolute;
left: 20rpx;
top: 50%;
transform: translateY(-50%);
}
.region-cell-area li:nth-child(2n):before {
content: '';
background: linear-gradient(135deg, #ff9f43, #ff6b6b);
width: 8rpx;
height: 8rpx;
border-radius: 50%;
display: block;
position: absolute;
left: 20rpx;
top: 50%;
transform: translateY(-50%);
}
.region-cell-area li {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 24rpx 20rpx 48rpx;
position: relative;
text-align: right;
color: #333;
background-color: #fff;
margin: 8rpx 12rpx;
border-radius: 8rpx;
border: 1rpx solid #f0f0f0;
transition: all 0.2s ease;
&:active {
background-color: #f8f9fa;
transform: scale(0.99);
}
}
.region-cell-area li>div {
flex: 2;
font-size: 22rpx;
}
.region-cell-area li>div:nth-child(1) {
flex: 3;
text-align: left;
}
/*车流量分析*/
.bayonet-cell-area {
/* display: none; */
max-height: 460rpx;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
.bayonet-cell-area li {
width: 100%;
}
.bayonet-cell-area li .server {
width: 30%;
display: inline-block;
text-align: left;
padding-left: 15rpx;
}
.bayonet-cell-area li .type {
width: 35%;
display: inline-block;
text-align: center;
}
.bayonet-cell-area li .carType {
width: 30%;
display: inline-block;
text-align: right;
}
.bayonet-cell-area li:nth-child(2n+1):before {
content: '';
background-color: #5596F9;
width: 8rpx;
height: 8rpx;
border-radius: 8rpx;
display: block;
position: absolute;
left: 0;
top: 40%;
}
.bayonet-cell-area li:nth-child(2n):before {
content: '';
background-color: #FE6D67;
width: 8rpx;
height: 8rpx;
border-radius: 8rpx;
display: block;
position: absolute;
left: 0;
top: 40%;
}
.bayonet-cell-area li {
width: 100%;
/*display: flex;*/
/*align-items: center;*/
/*justify-content: space-around;*/
position: relative;
}
.bayonet-cell-area li>div {
flex: 2;
}
.bayonet-cell-area li>div:nth-child(1) {
flex: 3;
text-align: left;
}
.bayonet-c-list {
padding: 16rpx 32rpx;
width: 100%;
position: relative;
}
.bayonet-c-list div:before {
content: '';
background: url(/static/images/authority/fwq.png) no-repeat center;
width: 35rpx;
height: 35rpx;
display: inline-block;
position: absolute;
left: 0;
top: 7px;
}
.modle-title image {
max-width: 36rpx;
max-height: 36rpx;
}
.line-tab-unit {
margin-top: 20rpx;
/* width: 60rpx; */
height: 44rpx;
font-size: 28rpx;
color: #A9A9A9;
background: #ececec;
border-radius: 8rpx;
text-align: center;
line-height: 44rpx;
margin-right: 32rpx;
padding: 0 10rpx;
font-size: 22rpx;
}
.line-tab-unit.active {
background-color: #667ED5;
color: #fff;
}
.revenue-line-box {
background-color: #fff;
margin: 0 16rpx;
border-radius: 20rpx;
padding: 34rpx 16rpx 34rpx 16rpx;
box-shadow: 1rpx 0rpx 6rpx 0px rgba(224, 224, 224, 0.54);
}
.revenue-line-box+.revenue-line-box {
margin-top: 38rpx;
}
.revenue-line-data {
width: 216rpx;
height: 144rpx;
background-color: #F8F8F8;
border-radius: 12rpx;
color: #A5A5A5;
font-size: 24rpx;
text-align: center;
padding-top: 20rpx;
border: 2rpx solid #F8F8F8;
box-sizing: border-box;
}
.revenue-line-data+.revenue-line-data {
margin-left: 24rpx;
}
.revenue-line-data p:last-child {
font-size: 40rpx;
font-family: 'Bahnschrift Regular';
line-height: 1.2;
}
.revenue-line-data.active {
border: 2rpx solid #889DED;
background-color: #f1f3fb;
color: #667ED5;
}
.revenue-line-box .text-title {
color: #9498A4;
font-size: 24rpx;
margin-top: 38rpx;
}
.revenue-line-box .up-text-title {
color: #6eb92b;
font-family: 'Bahnschrift Regular';
font-size: 36rpx;
line-height: 1.5;
}
.up-text-title:after {
content: "";
margin-left: 12rpx;
width: 20rpx;
height: 22rpx;
display: inline-block;
background: url('/static/images/revenue/up-arrow.png') no-repeat center;
background-size: contain;
}
.text-week {
color: #C2C2C2;
margin-left: 32rpx;
}
.down-text-title:after {
content: "";
margin-left: 12rpx;
width: 20rpx;
height: 22rpx;
display: inline-block;
background: url('/static/images/revenue/down-arrow.png') no-repeat center;
background-size: contain;
}
.revenue-line-box .down-text-title {
color: #F07878;
font-family: 'Bahnschrift Regular';
font-size: 36rpx;
line-height: 1.5;
}
.revenue-line-box .title {
color: #000;
font-size: 28rpx;
font-weight: bolder;
}
.revenue-line-box canvas.operation-content {
height: 350rpx;
width: 686rpx;
margin: 0 auto 0 auto;
background-color: #fff;
}
/*区域营收占比*/
/* //.operation-bgfc {
// background-color: #FCFCFC;
// padding-bottom: 32rpx;
//} */
.box-operation {
background-color: #fff;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
margin: 0 0 24rpx 0;
border-radius: 16rpx;
overflow: hidden;
border: 1rpx solid #f0f0f0;
}
.operation-tab-box {
background: #f8f9fa;
border-radius: 0;
display: flex;
align-items: center;
text-align: center;
overflow: hidden;
margin: 0;
}
.operation-tab-box .operation-tab-unit {
color: #666;
flex: 1;
height: 88rpx;
line-height: 88rpx;
position: relative;
font-size: 28rpx;
font-weight: 500;
transition: all 0.3s ease;
cursor: pointer;
}
.operation-tab-box .operation-tab-unit.active {
background: linear-gradient(135deg, #27B25F, #4CCC7F);
color: #fff;
font-weight: 600;
position: relative;
z-index: 2;
box-shadow: 0 4rpx 12rpx rgba(39, 178, 95, 0.3);
border-radius: 8rpx;
margin: 4rpx;
height: 80rpx;
line-height: 80rpx;
}
.operation-tab-box .operation-tab-unit.active:after {
content: none;
}
.operation-tab-box .operation-tab-unit:after {
content: none;
}
.operation-content-box {
width: 100%;
overflow: hidden;
padding: 24rpx;
background-color: #fff;
}
canvas.operation-content {
height: 400rpx;
width: 100%;
max-width: 600rpx;
margin: 0 auto;
background-color: #fff;
border-radius: 8rpx;
}
.operation-c-list {
padding: 20rpx 0;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
}
.operation-cl-unit {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
color: #333;
margin-bottom: 8rpx;
text:first-child {
font-weight: 600;
color: #27B25F;
}
text:last-child {
font-weight: 600;
font-family: 'DIN Alternate', 'Bahnschrift', sans-serif;
}
}
.operation-cl-unit1 {
width: 100%;
font-size: 26rpx;
display: flex;
justify-content: space-between;
}
.operation-cl-unit1 .server {
width: 30%;
display: inline-block;
text-align: left;
padding-left: 30rpx;
}
.operation-cl-unit1 .type {
width: 40%;
display: inline-block;
text-align: left;
}
.operation-cl-unit1 .carType {
width: 30%;
display: inline-block;
text-align: center;
}
.operation-c-list .progress {
width: 100%;
height: 8rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
margin-top: 12rpx;
overflow: hidden;
}
.operation-c-list .progress .bgO {
height: 100%;
border-radius: 4rpx;
background: linear-gradient(90deg, #27B25F, #4CCC7F);
transition: width 0.6s ease;
}
/*排行*/
.ranking-tab-box {
margin: 20rpx 20rpx;
border: 2rpx solid #565656;
border-radius: 8rpx;
display: flex;
align-items: center;
text-align: center;
}
.ranking-tab-box .ranking-tab-unit {
color: #565656;
flex: 1;
height: 64rpx;
line-height: 60rpx;
}
.ranking-tab-box .ranking-tab-unit.active {
background-color: #565656;
color: #fff
}
.ranking-tab-box .ranking-tab-unit+.ranking-tab-unit {
border-left: 2rpx solid #565656;
}
.ranking-content {
/* display: none; */
min-height: 160rpx;
}
.ranking-content {
margin: 0 20rpx;
}
.ranking-content .ranking-list {
display: flex;
align-items: center;
color: #000;
padding: 24rpx 32rpx;
}
.ranking-content .rank-index {
width: 72rpx;
height: 72rpx;
text-align: center;
line-height: 72rpx;
font-size: 30rpx;
font-weight: bolder;
margin-right: 32rpx;
}
.ranking-content .ranking-list:nth-child(1) .rank-index {
background: url('https://eshangtech.com/ShopICO/ahyd-BID/revenue/top1.png') no-repeat center;
background-size: contain;
font-size: 0;
}
.ranking-content .ranking-list:nth-child(2) .rank-index {
background: url('https://eshangtech.com/ShopICO/ahyd-BID/revenue/top1.png') no-repeat center;
background-size: contain;
font-size: 0;
}
.ranking-content .ranking-list:nth-child(3) .rank-index {
background: url('https://eshangtech.com/ShopICO/ahyd-BID/revenue/top3.png') no-repeat center;
background-size: contain;
font-size: 0;
}
.ranking-content .ranking-list-unit {
font-size: 28rpx;
flex: 1;
}
.ranking-content .ranking-unit-info {
/* margin-top: 16rpx; */
display: flex;
align-items: center;
/* font-size: .13rem; */
}
.ranking-content .ranking-unit-info text {
flex: 2;
color: #929292;
}
.ranking-content .ranking-unit-info text:nth-child(2) {
flex: 3;
}
.ranking-content .ranking-unit-info text:nth-child(2n) {
color: #000
}
.ranking-ico {
width: 72rpx;
height: 72rpx;
}
// 新增主内容区域样式
.main-content {
background-color: #fff;
margin: 0 20rpx 24rpx 20rpx;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
border: 1rpx solid #f0f0f0;
}
/* 现代化页面重设计样式 */
.section-header {
padding: 24rpx;
border-bottom: 2rpx solid #f1f3f4;
display: flex;
align-items: center;
justify-content: space-between;
background: #f8f9fa;
}
.section-title {
display: flex;
align-items: center;
}
.section-icon {
margin-right: 12rpx;
}
.icon-emoji {
font-size: 32rpx;
}
.section-text {
font-size: 28rpx;
font-weight: 700;
color: #2c3e50;
}
.section-badge {
background: #27B25F;
color: white;
font-size: 22rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-weight: 500;
}
/* 分析容器 */
.analysis-container {
background: #fff;
margin-top: 16rpx;
}
/* 现代化标签页 */
.modern-tabs {
display: flex;
background: #f8f9fa;
padding: 8rpx;
margin: 0 24rpx 24rpx;
border-radius: 16rpx;
}
.tab-item {
flex: 1;
padding: 24rpx 16rpx;
text-align: center;
border-radius: 12rpx;
transition: all 0.3s ease;
cursor: pointer;
border: none;
background: transparent;
color: #666;
margin: 0 8rpx;
}
.tab-item.active {
background: #27B25F;
color: white;
box-shadow: 0 4rpx 16rpx rgba(39, 178, 95, 0.2);
}
.tab-icon {
font-size: 32rpx;
margin-bottom: 8rpx;
line-height: 1;
}
.tab-label {
font-size: 26rpx;
font-weight: 500;
line-height: 1.2;
}
/* 内容包装器 */
.content-wrapper {
padding: 0 24rpx 32rpx;
}
/* 图表区域 */
.chart-section {
// margin-bottom: 32rpx;
}
.chart-container {
background: #f8f9fa;
border-radius: 16rpx;
// padding: 24rpx;
margin-bottom: 24rpx;
// box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.04);
// border: 1rpx solid rgba(0, 0, 0, 0.04);
}
.modern-chart {
width: 100%;
height: 500rpx;
border-radius: 8rpx;
background: white;
}
/* 数据卡片布局 - 使用flex替代grid */
.data-cards {
display: flex;
flex-wrap: wrap;
margin: -8rpx;
}
.data-card {
background: white;
border-radius: 12rpx;
padding: 24rpx 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
border: 1rpx solid rgba(0, 0, 0, 0.04);
position: relative;
overflow: hidden;
width: calc(50% - 16rpx);
margin: 8rpx;
box-sizing: border-box;
}
.data-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4rpx;
background: #27B25F;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16rpx;
}
.category-name {
font-size: 26rpx;
font-weight: 600;
color: #2c3e50;
}
.percentage {
font-size: 24rpx;
font-weight: 600;
color: #27B25F;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
}
.amount {
font-size: 26rpx;
font-weight: 700;
color: #27B25F;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
margin-bottom: 12rpx;
line-height: 1.1;
}
.progress-bar {
width: 100%;
height: 8rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
overflow: hidden;
}
.progress-fill {
height: 100%;
border-radius: 4rpx;
background: #27B25F;
transition: width 0.6s ease;
}
/* 车流分析区域 - 重构版本 */
.traffic-analysis {
// margin: 0 20rpx 32rpx 20rpx;
background: #fff;
border-radius: 12rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
border: 1rpx solid #f0f0f0;
overflow: hidden;
}
.traffic-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 24rpx;
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
border-bottom: 2rpx solid #f1f3f4;
}
.traffic-title {
display: flex;
align-items: center;
gap: 12rpx;
}
.traffic-icon {
font-size: 28rpx;
}
.traffic-title text {
font-size: 28rpx;
font-weight: 600;
color: #2c3e50;
}
.traffic-summary {
font-size: 24rpx;
color: #6c757d;
background: #fff;
padding: 6rpx 12rpx;
border-radius: 16rpx;
border: 1rpx solid #e9ecef;
}
.traffic-scroll-container {
height: 700rpx;
background: #fff;
}
.traffic-regions {
padding: 20rpx;
}
.region-card {
background: #f8f9fa;
border-radius: 12rpx;
margin-bottom: 20rpx;
border: 1rpx solid rgba(0, 0, 0, 0.04);
overflow: hidden;
}
.region-card:last-child {
margin-bottom: 0;
}
.region-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 20rpx;
background: linear-gradient(135deg, #fff, #f8f9fa);
border-bottom: 1rpx solid #e9ecef;
}
.region-badge {
display: flex;
align-items: center;
gap: 10rpx;
}
.region-icon {
font-size: 24rpx;
}
.region-title {
font-size: 28rpx;
font-weight: 700;
color: #2c3e50;
}
.region-actions {
display: flex;
align-items: center;
gap: 12rpx;
}
.region-count {
font-size: 22rpx;
color: #6c757d;
background: rgba(108, 117, 125, 0.1);
padding: 4rpx 12rpx;
border-radius: 12rpx;
}
.region-arrow {
font-size: 20rpx;
color: #999;
transition: transform 0.3s ease;
}
.region-arrow.collapsed {
transform: rotate(-90deg);
}
.service-areas {
padding: 12rpx;
}
.service-area-item {
background: #fff;
border-radius: 8rpx;
margin-bottom: 12rpx;
border: 1rpx solid rgba(0, 0, 0, 0.04);
overflow: hidden;
}
.service-area-item:last-child {
margin-bottom: 0;
}
.service-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 16rpx 12rpx;
border-bottom: 1rpx solid #f1f3f4;
cursor: pointer;
}
.service-actions {
display: flex;
align-items: center;
gap: 12rpx;
}
.service-info {
flex: 1;
}
.service-name {
font-size: 26rpx;
font-weight: 600;
color: #2c3e50;
margin-bottom: 4rpx;
}
.service-ratio {
font-size: 22rpx;
color: #27B25F;
font-weight: 500;
}
.flow-stats {
display: flex;
align-items: center;
gap: 12rpx;
}
.flow-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 2rpx;
}
.flow-label {
font-size: 20rpx;
color: #6c757d;
}
.flow-value {
font-size: 24rpx;
font-weight: 600;
color: #27B25F;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
}
// 一些保持灰色的数据文字
.stat-value {
font-size: 28rpx;
font-weight: 600;
color: #495057;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
}
.direction-ratio {
font-size: 22rpx;
font-weight: 600;
color: #6c757d;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
}
.flow-divider {
color: #e9ecef;
font-size: 24rpx;
}
.expand-arrow {
font-size: 18rpx;
color: #999;
transition: transform 0.3s ease;
margin-left: 12rpx;
}
.expand-arrow.expanded {
transform: rotate(180deg);
}
.direction-list {
padding: 12rpx 16rpx 16rpx;
}
.direction-item {
background: #f8f9fa;
border-radius: 6rpx;
padding: 12rpx;
margin-bottom: 8rpx;
}
.direction-item:last-child {
margin-bottom: 0;
}
.direction-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.direction-info {
display: flex;
flex-direction: column;
gap: 4rpx;
}
.direction-name {
font-size: 24rpx;
font-weight: 600;
color: #2c3e50;
}
.direction-percent {
font-size: 20rpx;
color: #27B25F;
font-weight: 500;
}
.vehicle-stats {
display: flex;
align-items: center;
gap: 16rpx;
}
.vehicle-type-group {
display: flex;
gap: 16rpx;
flex-wrap: wrap;
}
.vehicle-type-item {
display: flex;
flex-direction: column;
align-items: center;
background: #f8f9fa;
padding: 8rpx 12rpx;
border-radius: 8rpx;
border: 1rpx solid #e9ecef;
min-width: 60rpx;
}
.vehicle-type-label {
font-size: 20rpx;
color: #6c757d;
margin-bottom: 4rpx;
}
.vehicle-count-value {
font-size: 22rpx;
font-weight: 600;
color: #2c3e50;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
}
/* 上传数据区域 */
.upload-section {
background: white;
border-radius: 16rpx;
padding: 24rpx;
margin: 0 20rpx 24rpx 20rpx;
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.06);
border: 1rpx solid rgba(0, 0, 0, 0.04);
}
.upload-header {
display: flex;
align-items: center;
justify-content: space-between;
// margin-bottom: 24rpx;
// padding-bottom: 16rpx;
// border-bottom: 2rpx solid #f1f3f4;
}
.upload-title {
display: flex;
align-items: center;
}
.upload-icon {
font-size: 28rpx;
margin-right: 12rpx;
}
.upload-text {
font-size: 28rpx;
font-weight: 700;
color: #2c3e50;
}
.upload-summary {
display: flex;
align-items: center;
gap: 12rpx;
}
.summary-label {
font-size: 24rpx;
color: #6c757d;
}
.summary-value {
font-size: 28rpx;
font-weight: 600;
color: #27B25F;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
}
/* 服务列表 */
.service-list {
background: white;
border-radius: 16rpx;
padding: 24rpx;
margin: 0 20rpx 32rpx 20rpx;
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.06);
border: 1rpx solid rgba(0, 0, 0, 0.04);
}
.service-card {
background: #f8f9fa;
border-radius: 12rpx;
padding: 24rpx 20rpx;
margin-bottom: 16rpx;
border: 1rpx solid rgba(0, 0, 0, 0.04);
position: relative;
overflow: hidden;
}
.service-card:last-child {
margin-bottom: 0;
}
.service-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4rpx;
background: #27B25F;
}
.service-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16rpx;
}
.service-info {
flex: 1;
}
.service-name {
font-size: 30rpx;
font-weight: 600;
color: #2c3e50;
margin-bottom: 8rpx;
}
.service-revenue {
font-size: 32rpx;
font-weight: 700;
color: #27B25F;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
}
.service-status {
text-align: right;
}
.upload-rate {
font-size: 28rpx;
font-weight: 600;
color: #27B25F;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
margin-bottom: 4rpx;
}
.upload-rate.warning {
color: #ff9f43;
}
.upload-fraction {
font-size: 22rpx;
color: #6c757d;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
}
.upload-fraction.incomplete {
color: #ff4757;
}
.service-indicator {
display: flex;
align-items: center;
gap: 8rpx;
margin-top: 16rpx;
}
.status-dot {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: #ff4757;
}
.status-dot.complete {
background: #27B25F;
}
.arrow-icon {
font-size: 24rpx;
color: #999;
font-weight: 600;
}
/* 区域列表 */
.region-list {
background: white;
border-radius: 16rpx;
padding: 0 0 24rpx;
margin: 0 20rpx;
// box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.06);
// border: 1rpx solid rgba(0, 0, 0, 0.04);
}
.region-card {
background: #f8f9fa;
border-radius: 12rpx;
margin-bottom: 16rpx;
border: 1rpx solid rgba(0, 0, 0, 0.04);
overflow: hidden;
}
.region-card:last-child {
margin-bottom: 0;
}
.region-main {
padding: 24rpx 20rpx;
position: relative;
}
.region-main::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4rpx;
background: #27B25F;
}
.region-info {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16rpx;
}
.region-name {
font-size: 26rpx;
font-weight: 600;
color: #2c3e50;
}
.region-revenue {
font-size: 24rpx;
font-weight: 700;
color: #27B25F;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
}
.region-metrics {
display: flex;
align-items: center;
gap: 24rpx;
}
.metric-item {
text-align: center;
}
.metric-label {
font-size: 22rpx;
color: #6c757d;
margin-bottom: 4rpx;
}
.metric-value {
font-size: 26rpx;
font-weight: 600;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
}
.metric-value.rate {
color: #27B25F;
}
.metric-value.rate.incomplete {
color: #ff9f43;
}
.metric-value.fraction {
color: #6c757d;
}
.metric-value.fraction.incomplete {
color: #ff4757;
}
.expand-indicator {
margin-left: auto;
}
.expand-icon {
font-size: 20rpx;
color: #999;
}
.service-sublist {
background: white;
border-top: 1rpx solid #e9ecef;
padding: 16rpx 0 0;
}
.subservice-card {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 20rpx;
margin: 8rpx 16rpx;
background: #f8f9fa;
border-radius: 8rpx;
border: 1rpx solid rgba(0, 0, 0, 0.04);
}
.subservice-card.visited .subservice-name {
color: #27B25F;
}
.subservice-info {
flex: 1;
}
.subservice-name {
font-size: 26rpx;
font-weight: 600;
color: #2c3e50;
margin-bottom: 4rpx;
}
.subservice-revenue {
font-size: 24rpx;
font-weight: 600;
color: #27B25F;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
}
.subservice-status {
font-size: 22rpx;
font-weight: 600;
color: #6c757d;
font-family: 'DIN Alternate', 'Bahnschrift', monospace;
margin-right: 16rpx;
}
.subservice-status.incomplete {
color: #ff4757;
}
.subservice-arrow {
font-size: 20rpx;
color: #999;
font-weight: 600;
}
/* 微信小程序兼容性修正 - 移除不支持的特性 */
/* 移除 @media 查询,改用 flex 替代 grid */
/* 加载骨架屏样式 */
.loading-skeleton {
padding: 20rpx;
background-color: #f8f9fa;
min-height: 100vh;
}
.skeleton-header {
height: 300rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
border-radius: 16rpx;
margin-bottom: 24rpx;
}
.skeleton-cards {
display: flex;
gap: 16rpx;
margin-bottom: 24rpx;
}
.skeleton-card {
flex: 1;
height: 120rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
border-radius: 12rpx;
}
.skeleton-list {
display: flex;
flex-direction: column;
gap: 12rpx;
}
.skeleton-item {
height: 100rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
border-radius: 12rpx;
}
@keyframes loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
/* 页面入场动画 */
.page-fade-in {
animation: pageSlideUp 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
opacity: 0;
transform: translateY(30px);
}
@keyframes pageSlideUp {
to {
opacity: 1;
transform: translateY(0);
}
}
/* 分析容器滑入动画 */
.slide-up-animation {
animation: slideUpFade 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.3s forwards;
opacity: 0;
transform: translateY(20px);
}
@keyframes slideUpFade {
to {
opacity: 1;
transform: translateY(0);
}
}
/* Tab淡入动画 */
.tab-fade-in {
animation: tabFadeIn 0.5s ease-out 0.4s forwards;
opacity: 0;
}
@keyframes tabFadeIn {
to {
opacity: 1;
}
}
/* Tab切换动画 */
.tab-item {
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
transform: scale(1);
}
.tab-item.active {
transform: scale(1.05);
}
.tab-item:active {
transform: scale(0.95);
}
/* 图表容器动画 */
.chart-fade-in {
animation: chartFadeIn 0.6s ease-out forwards;
opacity: 0;
}
@keyframes chartFadeIn {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.chart-scale-in {
animation: chartScaleIn 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) 0.2s forwards;
opacity: 0;
transform: scale(0.8);
}
@keyframes chartScaleIn {
to {
opacity: 1;
transform: scale(1);
}
}
/* 数据卡片动画 */
.card-slide-up {
animation: cardSlideUp 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
opacity: 0;
transform: translateY(30px);
}
@keyframes cardSlideUp {
to {
opacity: 1;
transform: translateY(0);
}
}
.data-card {
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.data-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
}
/* 进度条动画 */
.progress-fill {
transition: width 1.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
width: 0;
}
.progress-delay-0 {
transition-delay: 0.8s;
}
.progress-delay-1 {
transition-delay: 0.9s;
}
.progress-delay-2 {
transition-delay: 1.0s;
}
.progress-delay-3 {
transition-delay: 1.1s;
}
.progress-delay-4 {
transition-delay: 1.2s;
}
/* 卡片交错动画 */
.cards-stagger .data-card:nth-child(1) {
animation-delay: 0.1s;
}
.cards-stagger .data-card:nth-child(2) {
animation-delay: 0.2s;
}
.cards-stagger .data-card:nth-child(3) {
animation-delay: 0.3s;
}
.cards-stagger .data-card:nth-child(4) {
animation-delay: 0.4s;
}
.cards-stagger .data-card:nth-child(5) {
animation-delay: 0.5s;
}
/* 区域列表动画 */
.list-fade-in {
animation: listFadeIn 0.6s ease-out 0.6s forwards;
opacity: 0;
}
@keyframes listFadeIn {
to {
opacity: 1;
}
}
.region-card-slide {
animation: regionSlideUp 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
opacity: 0;
transform: translateY(20px);
}
@keyframes regionSlideUp {
to {
opacity: 1;
transform: translateY(0);
}
}
/* 展开收起动画 */
.expand-indicator {
transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.expand-indicator.expanded {
transform: rotate(180deg);
}
.service-sublist {
overflow: hidden;
transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.subservice-slide {
animation: subserviceSlideIn 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
opacity: 0;
transform: translateX(-10px);
}
@keyframes subserviceSlideIn {
to {
opacity: 1;
transform: translateX(0);
}
}
/* 卡片悬浮效果优化 */
.region-card {
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.region-card:active {
transform: scale(0.98);
opacity: 0.9;
}
.subservice-card {
transition: all 0.2s ease-out;
}
.subservice-card:active {
transform: translateX(5px);
background-color: rgba(0, 0, 0, 0.05);
}
/* 车流分析动画效果 */
.traffic-fade-in {
animation: trafficFadeIn 0.6s ease-out forwards;
opacity: 0;
}
@keyframes trafficFadeIn {
to {
opacity: 1;
}
}
.traffic-header-slide {
animation: trafficHeaderSlide 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.2s forwards;
opacity: 0;
transform: translateY(-20px);
}
@keyframes trafficHeaderSlide {
to {
opacity: 1;
transform: translateY(0);
}
}
.traffic-icon-bounce {
animation: trafficIconBounce 1s cubic-bezier(0.68, -0.55, 0.265, 1.55) 0.5s forwards;
transform: scale(0.8);
}
@keyframes trafficIconBounce {
0% {
transform: scale(0.8);
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
.traffic-summary-fade {
animation: summaryFadeIn 0.5s ease-out 0.8s forwards;
opacity: 0;
}
@keyframes summaryFadeIn {
to {
opacity: 1;
}
}
.traffic-region-slide {
animation: regionSlideIn 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
opacity: 0;
transform: translateX(-30px);
}
@keyframes regionSlideIn {
to {
opacity: 1;
transform: translateX(0);
}
}
/* 区域卡片交互动画 */
.region-header {
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.region-header:active {
transform: scale(0.98);
background-color: rgba(0, 0, 0, 0.05);
}
.region-arrow {
transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.region-arrow.collapsed {
transform: rotate(-90deg);
}
/* 服务区展开动画 */
.service-areas-expand {
animation: serviceAreasExpand 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
overflow: hidden;
max-height: 0;
}
@keyframes serviceAreasExpand {
to {
max-height: 1000px;
}
}
.service-item-slide {
animation: serviceItemSlide 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
opacity: 0;
transform: translateY(15px);
}
@keyframes serviceItemSlide {
to {
opacity: 1;
transform: translateY(0);
}
}
.service-header-interactive {
transition: all 0.2s ease-out;
}
.service-header-interactive:active {
transform: scale(0.98);
background-color: rgba(0, 0, 0, 0.03);
}
/* 数值动画效果 */
.flow-item-counter .flow-value {
animation: counterPulse 0.8s ease-out 1.5s forwards;
transform: scale(1);
}
/* 无车流数据提示样式 */
.no-traffic-data {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx 40rpx;
background: #f8f9fa;
border-radius: 12rpx;
margin: 20rpx;
}
.no-data-icon {
font-size: 48rpx;
margin-bottom: 16rpx;
opacity: 0.6;
}
.no-data-text {
font-size: 26rpx;
color: #6c757d;
text-align: center;
}
@keyframes counterPulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
color: #1890ff;
}
100% {
transform: scale(1);
}
}
.expand-arrow {
transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.expand-arrow.expanded {
transform: rotate(180deg);
}
/* 方向数据展开动画 */
.direction-expand-animation {
animation: directionExpand 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
overflow: hidden;
max-height: 0;
}
@keyframes directionExpand {
to {
max-height: 500px;
}
}
.direction-item-slide {
animation: directionItemSlide 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
opacity: 0;
transform: translateX(20px);
}
@keyframes directionItemSlide {
to {
opacity: 1;
transform: translateX(0);
}
}
.direction-percent-highlight {
animation: percentHighlight 1s ease-in-out 1s forwards;
position: relative;
}
@keyframes percentHighlight {
0%,
100% {
color: inherit;
}
50% {
color: #52c41a;
font-weight: bold;
}
}
/* 车型数据弹跳动画 */
.vehicle-item-bounce {
animation: vehicleItemBounce 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards;
opacity: 0;
transform: scale(0.8) translateY(10px);
}
@keyframes vehicleItemBounce {
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.vehicle-count-pulse {
animation: vehicleCountPulse 2s ease-in-out infinite;
}
@keyframes vehicleCountPulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.7;
color: #1890ff;
}
}
</style>