This commit is contained in:
ylj20011123 2025-09-04 18:45:15 +08:00
parent 42f254afb9
commit f82ab7bb6a
7 changed files with 1696 additions and 291 deletions

View File

@ -65,7 +65,7 @@ import request from '@/util/index.js'
// }
const netChange = () => {
uni.onNetworkStatusChange(function(res) {
uni.onNetworkStatusChange(function (res) {
// console.log(res.isConnected)
store.mutations.isConnect(res.isConnected)
if (!res.isConnected) {
@ -187,25 +187,25 @@ function addUserBehaviorNew(obj) {
}
obj.visitChannels = store.state.visitChannels
let userDate = store.state.userData
console.log('userDate123',userDate)
console.log('userDate123', userDate)
let baseInfo = uni.getStorageSync('userInfo')
let req = {
userName:userDate.UserName,
phoneNumber:userDate.Membership_Phone,
userId:userDate.UserId ? userDate.UserId:'',
wechatAppId:'wxa99ef047735c031e',
intoRoute:obj.intoRoute,
outtoRoute:obj.outtoRoute,
visitChannels:obj.visitChannels,
behaviorRecordDesc:'',
userName: userDate.UserName,
phoneNumber: userDate.Membership_Phone,
userId: userDate.UserId ? userDate.UserId : '',
wechatAppId: 'wxa99ef047735c031e',
intoRoute: obj.intoRoute,
outtoRoute: obj.outtoRoute,
visitChannels: obj.visitChannels,
behaviorRecordDesc: '',
LoginIP: baseInfo.ip || '',
LoginPlace: (baseInfo.prov ? baseInfo.prov : '' ) + (baseInfo.prov && baseInfo.city ? '-' : '') + (baseInfo.city ? baseInfo.city : ''),
LoginPlace: (baseInfo.prov ? baseInfo.prov : '') + (baseInfo.prov && baseInfo.city ? '-' : '') + (baseInfo.city ? baseInfo.city : ''),
SOURCE_PLATFORM: '驿行畅旅'
}
console.log('req',req)
request.$webGet('CommercialApi/UserBehavior/AddUserBehavior',req).then(() => {
console.log('req', req)
request.$webGet('CommercialApi/UserBehavior/AddUserBehavior', req).then(() => {
})
}
@ -232,7 +232,7 @@ async function getFieldEnumByField(params) {
}
// 不四舍五入 保留两位小数的金额化方法
function getMoney(money){
function getMoney(money) {
if (!money || isNaN(money)) return "0.00";
let num = parseFloat(money + '') + '';
num = parseInt(money * 100 + '') / 100 + ''
@ -255,32 +255,32 @@ function getMoney(money){
return num;
}
function getMoneyTest(money){
console.log('money',money)
function getMoneyTest(money) {
console.log('money', money)
if (!money || isNaN(money)) return "0.00";
let realMoney = 0
if(money.toString().indexOf('.')>0){
if (money.toString().indexOf('.') > 0) {
let num_per = money.toString().substring(0, money.toString().indexOf('.'))
let num_next = money.toString().substring(money.toString().indexOf('.') + 1).padEnd(2, '0')
console.log('num_per',num_per)
console.log('num_next',num_next)
console.log('num_per', num_per)
console.log('num_next', num_next)
realMoney = Number(num_per + '.' + num_next).toFixed(2)
}else{
} else {
realMoney = money
}
console.log('realMoney',realMoney)
console.log('realMoney', realMoney)
let num = realMoney
// let num = parseFloat(realMoney + '') + '';
// let num = parseFloat(realMoney + '') + '';
// num = (parseInt(realMoney * 100 + '') / 100).toFixed(2) + ''
console.log('num',num)
console.log('num', num)
let reg = /(-?\d+)(\d{3})/;
while (reg.test(num)) {
num = num.replace(reg, "$1,$2");
}
let idx = num.indexOf('.')
console.log('idx',idx)
console.log('idx', idx)
if (idx === -1) {
num = num + '.00'
}
@ -289,8 +289,8 @@ function getMoneyTest(money){
let num_per = num.substring(0, idx) + '.'
let num_next = num.substring(idx + 1).padEnd(2, '0')
console.log('num_per',num_per)
console.log('num_next',num_next)
console.log('num_per', num_per)
console.log('num_next', num_next)
num = num_per + num_next
}
@ -305,6 +305,45 @@ function playVideo(opt) {
return new EZUIPlayer(opt)
}
// 得到这个月有多少天
function getThisMonthDay(value) {
let date = new Date(value)
let y = date.getFullYear()
let m = date.getMonth() + 1
let howDay;
let flag = false
if (y % 4 === 0 && y % 100 !== 0 || y % 400 === 0) {
flag = true
}
switch (m) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
howDay = 31
break
case 4:
case 6:
case 9:
case 11:
howDay = 30
break
case 2:
if (flag) {
howDay = 29
} else {
howDay = 28
}
break
}
return howDay
}
export default {
netChange,
cutDate,
@ -318,5 +357,6 @@ export default {
getFieldEnumByField, // 获取枚举参数
// calculateDistance,
// bMapToQQMap,
playVideo
playVideo,
getThisMonthDay
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
<template>
<view :class="'province-theme-' + currentProvinceCode" scroll-with-animation v-if="showPage">
<view class="page-body">
<div class="box-card" :style="'opacity:' + (1 - opacity) + ';'">
<view class="page-body page-enter">
<div class="box-card card-enter" :style="'opacity:' + (1 - opacity) + ';'">
<div class="box-top-title">
<span class="box-center-title">{{ sMsg.serverpartname }}</span>
<picker mode="date" @change="bindDateChange" :value="theRequest && theRequest.time" :start="startTime"
@ -10,7 +10,7 @@
</picker>
</div>
<view class="top-card">
<view class="top-card data-card-enter">
<div class="box-center-box">
<div class="uni-flex ai-center jc-between" style="margin-bottom: 8rpx;">
<div class="main-amount-title">对客营收()</div>
@ -36,37 +36,37 @@
</div>
<div class="uni-flex jc-between box-center-box">
<div class="check-unit">
<div class="check-unit metric-enter">
<text>长款金额</text>
<div class="check-price-color">{{ $util.fmoney(sMsg.diffMorePrice, 2) }} <text></text></div>
</div>
<div class="check-unit">
<div class="check-unit metric-enter">
<text>短款金额</text>
<div class="check-price-color">{{ $util.fmoney(sMsg.diffLessPrice, 2) }} <text></text></div>
</div>
<div class="check-unit">
<div class="check-unit metric-enter">
<text>客单交易</text>
<div class="check-price-color">{{ $util.fmoney(sMsg.ticketCount, 0) }} <text></text></div>
</div>
<div class="check-unit">
<div class="check-unit metric-enter">
<text>客单均价</text>
<div class="check-price-color">{{ $util.fmoney(sMsg.tickave, 2) }} <text></text></div>
</div>
</div>
<div class="uni-flex jc-between box-center-box mt8">
<div class="check-unit">
<div class="check-unit metric-enter">
<text>优惠金额</text>
<div class="check-price-color">{{ $util.fmoney(sMsg.totalOffAmount, 2) }} <text></text></div>
</div>
<div class="check-unit">
<div class="check-unit metric-enter">
<text>移动支付</text>
<div class="check-price-color">{{ $util.fmoney(sMsg.mobilePayment, 2) }} <text></text></div>
</div>
<div class="check-unit">
<div class="check-unit metric-enter">
<text>商品出售</text>
<div class="check-price-color">{{ $util.fmoney(sMsg.totalCount, 0) }} <text></text></div>
</div>
<div class="check-unit">
<div class="check-unit metric-enter">
<text>商品均价</text>
<div class="check-price-color">{{ $util.fmoney(sMsg.countave, 2) }} <text></text></div>
</div>
@ -82,11 +82,11 @@
<template v-if="cateBrandList.length">
<template v-if="theRequest.ProvinceCode == '340000'">
<view class="uni-flex ai-center analysis-tabs">
<view class="uni-flex ai-center analysis-tabs tabs-enter">
<!-- <view @tap="changeTab(3)" class="tab" :class="{'active': nowTab==3}">经营模式</view> -->
<view @tap="changeTab(4)" class="tab" :class="{ 'active': nowTab == 4 }">车流分析</view>
<view @tap="changeTab(1)" class="tab" :class="{ 'active': nowTab == 1 }">经营分析</view>
<view @tap="changeTab(2)" class="tab" :class="{ 'active': nowTab == 2 }">客群分析</view>
<view @tap="changeTab(4)" class="tab tab-slide" :class="{ 'active': nowTab == 4 }">车流分析</view>
<view @tap="changeTab(1)" class="tab tab-slide" :class="{ 'active': nowTab == 1 }">经营分析</view>
<view @tap="changeTab(2)" class="tab tab-slide" :class="{ 'active': nowTab == 2 }">客群分析</view>
</view>
<!-- <view class="pie-content" v-show="nowTab==3">
@ -98,54 +98,53 @@
</view> -->
</template>
<!-- 经营分析内容 -->
<view v-show="nowTab == 1">
<!-- 安徽省 -->
<div class="pie-content">
<div class="model-busniess">经营业态占比</div>
<ServiceRevenuePie ref="serviceRevenuePie" v-if="ServiceRevenueData.length == 2 && nowTab == 1"
<ServiceRevenuePie ref="serviceRevenuePie" v-show="ServiceRevenueData && ServiceRevenueData.length >= 2"
:data="ServiceRevenueData" @selectCate="selectCate" />
<!-- 其余省份 -->
<!-- <canvas v-else-if="sellData.length>0" canvas-id="sellCate" id="sellCate" class="operation-cate-content" @click="touchPie($event,'sellCate')"></canvas> -->
<div class="model-busniess">
<div class="model-busniess progress-section-enter">
<div>经营模式占比</div>
<div class="progress-content">
<div class="progress-left" :style="'width: ' + operationModel[0].bili + '%;'"></div>
<div class="progress-right" :style="'width: ' + operationModel[1].bili + '%;'"></div>
</div>
<div class="uni-flex jc-between">
<div style="color: #667ED5;font-size: 26rpx;">{{ operationModel[0].name }}: <span
style="font-size: 26rpx;">{{
operationModel[0].bili }}</span>%</div>
<div style="color: #F3AF50;font-size: 26rpx;" v-if="operationModel[1].name">{{ operationModel[1].name
}}:
<span style="font-size: 26rpx;">{{ operationModel[1].bili }}</span>%
<div style="color: #667ED5;">{{ operationModel[0].name }}: <span style="font-size: 32rpx;">{{
operationModel[0].bili }}</span>%</div>
<div style="color: #F3AF50;" v-if="operationModel[1].name">{{ operationModel[1].name }}:
<span style="font-size: 32rpx;">{{ operationModel[1].bili }}</span>%
</div>
</div>
<div class="uni-flex jc-between">
<div style="margin-bottom: 0;font-size: 24rpx;"><span style="font-size: 26rpx;">{{
operationModel[0].data
}}</span>
<div style="margin-bottom: 0;"><span style="font-size: 32rpx;">{{ operationModel[0].data
}}</span>
</div>
<div style="margin-bottom: 0;font-size: 24rpx;" v-if="operationModel[1].data"><span
style="font-size: 26rpx;">{{
operationModel[1].data
<div style="margin-bottom: 0;" v-if="operationModel[1].data"><span style="font-size: 32rpx;">{{
operationModel[1].data
}}</span>
</div>
</div>
</div>
</div>
<!-- 品牌列表 -->
<div class="shop-box">
<scroll-view scroll-x class="tab-shop" scroll-with-animation :scroll-left="scrollLeft">
<div v-for="(n, i) in cateBrandList" :key="i" :id="`tabNum${i}`" class="cate-name"
:class="{ 'active': nowShop == i }" @click="selectCate(i)">{{ n.Bussiness_Name }}</div>
<div class="shop-box brand-list-enter">
<scroll-view scroll-x class="tab-shop tab-shop-slide" scroll-with-animation :scroll-left="scrollLeft">
<div v-for="(n, i) in cateBrandList" :key="i" :id="`tabNum${i}`" class="cate-name brand-tab-stagger"
:class="{ 'active': nowShop == i }" @click="selectCate(i)"
:style="'animation-delay: ' + (i * 0.05) + 's'">{{
n.Bussiness_Name }}</div>
</scroll-view>
<div class="tab-content" v-if="cateBrandList.length">
<div class="shop-card" v-for="(m, i) in cateBrandList[nowShop].listBrandModel" :key="i"
@click="toBrandPage(m, i)">
<div>
<image v-if="m.Brand_ICO" :src="m.Brand_ICO" mode="aspectFit"></image>
<image v-else src="/static/images/revenue/home.png" mode="aspectFit"></image>
<div class="tab-content brand-cards-container" v-if="cateBrandList.length">
<div class="shop-card brand-card-stagger" v-for="(m, i) in cateBrandList[nowShop].listBrandModel" :key="i"
@click="toBrandPage(m, i)" :style="'animation-delay: ' + (i * 0.08) + 's'">
<div class="brand-icon-wrapper">
<image v-if="m.Brand_ICO" :src="m.Brand_ICO" mode="aspectFit" class="brand-icon"></image>
<image v-else src="/static/images/revenue/home.png" mode="aspectFit" class="brand-icon"></image>
</div>
<div class="shop-name">{{ m.Brand_Name }}</div>
<div class="price-num">¥ {{ m.Revenue_Amount ? $util.fmoney(m.Revenue_Amount, 2) : '0.00' }}
@ -155,10 +154,10 @@
</div>
</view>
<view class="" v-show="nowTab == 2">
<view class="tab-content-fade" v-show="nowTab == 2">
<CustomerAnalysis ref="customerAna" :show="nowTab == 2" />
</view>
<view class="" v-show="nowTab == 4">
<view class="tab-content-fade" v-show="nowTab == 4">
<CarAnalysis ref="carAna" :show="nowTab == 4" />
</view>
@ -320,8 +319,7 @@ export default {
let d = date.getDate() - 1
if (d - 8 < 0) {
let num = 8 - d
m = m - 1
let changeMonth = m
let changeMonth = m - 1
if (changeMonth < 10) {
changeMonth = '0' + changeMonth
}
@ -338,8 +336,13 @@ export default {
day = d - 8
startTime = `${y}-${m}-${day}`
}
console.log('mmm', m);
this.startTime = startTime
this.endTime = `${y}-${m}-${d}`
console.log('this.startTimethis.startTime', this.startTime);
console.log('this.startTimethis.endTime', this.endTime);
},
@ -368,7 +371,6 @@ export default {
changeTab(value) {
this.nowTab = value
if (value == 2) {
let opt = this.theRequest
const params = {
serverpartId: opt.ServerpartIds,
@ -480,8 +482,15 @@ export default {
let colors1 = ['#FFAC37', '#d8ece9', '#e0e3f7', '#f7f5f6', '#b2b7e3'];
let list = []
let list2 = []
//
if (!_this.pageData || !_this.pageData.listBusinessModel) {
console.warn('pageData 或 listBusinessModel 无效')
return
}
_this.pageData.listBusinessModel.map((m, i) => {
if (m.Revenue_Amount != 0) {
if (m.Revenue_Amount && m.Revenue_Amount != 0) {
let n = {
name: m.Bussiness_Name,
data: m.Revenue_Amount
@ -506,24 +515,43 @@ export default {
}
})
if (list2.length > 0) {
_this.ServiceRevenueData.push(list2)
_this.sellData = list
//
let fullServiceRevenueData = []
//
if (list2.length > 0) {
fullServiceRevenueData.push(list2)
_this.sellData = list
console.log('第一层数据准备:', list2)
}
//
if (_this.pageData.listCurBusinessModel && _this.pageData.listCurBusinessModel.length > 0) {
let list = []
let list3 = []
_this.pageData.listCurBusinessModel.map(m => {
if (m.Revenue_Amount != 0) {
if (m.Revenue_Amount && m.Revenue_Amount != 0) {
let n = {
name: m.Bussiness_Name,
value: m.Revenue_Amount
}
list.push(n)
list3.push(n)
}
})
_this.ServiceRevenueData.push(list)
if (list3.length > 0) {
fullServiceRevenueData.push(list3)
console.log('第二层数据准备:', list3)
}
}
//
if (fullServiceRevenueData.length > 0) {
_this.$set(_this, 'ServiceRevenueData', fullServiceRevenueData)
console.log('operationFn设置完整数据:', fullServiceRevenueData)
}
console.log('最终 ServiceRevenueData:', _this.ServiceRevenueData)
if (list.length > 0) {
_this.showPie({
id: 'sellCate',
@ -670,7 +698,7 @@ export default {
_this.pageData = res.Result_Data
if (res.Result_Data.listBusinessModel) {
let list = JSON.parse(JSON.stringify(res.Result_Data.listBusinessModel))
console.log('list', list)
console.log('获取到业务数据:', list)
if (list.length > 0) {
let all = {
Bussiness_Name: '全部',
@ -687,12 +715,18 @@ export default {
}
})
_this.cateBrandList = [all, ...list]
_this.operationFn()
// operationFn
_this.$nextTick(() => {
_this.operationFn()
})
return
}
return
}
//
_this.cateBrandList = []
_this.$set(_this, 'ServiceRevenueData', [])
})
},
//
@ -727,8 +761,11 @@ export default {
colors: colors1,
});
//
// this.ServiceRevenueData = []
this.ServiceRevenueData = [busniessTradeFathPie, busniessTradePie]
if (busniessTradeFathPie && busniessTradeFathPie.length > 0 && busniessTradePie && busniessTradePie.length > 0) {
this.$set(this, 'ServiceRevenueData', [busniessTradeFathPie, busniessTradePie])
} else {
this.$set(this, 'ServiceRevenueData', [])
}
if (totalData.uploadCount !== totalData.totalUploadCount) {
this.getUnUpLoadShops(theRequest)
@ -1476,7 +1513,6 @@ cover-view.page-title {
padding: 8rpx 24rpx;
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border-radius: 12rpx;
font-size: 28rpx;
border: 1rpx solid #f0f0f0;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
@ -1507,4 +1543,370 @@ cover-view.page-title {
background: linear-gradient(90deg, #ff9f43, #ffb84d);
border-radius: 0 6rpx 6rpx 0;
}
/* 动画效果 - UniApp微信小程序兼容版本 */
/* 页面入场动画 */
@keyframes pageEnter {
0% {
opacity: 0;
transform: translateY(50rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.page-enter {
animation: pageEnter 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
}
/* 卡片入场动画 */
@keyframes cardEnter {
0% {
opacity: 0;
transform: translateY(30rpx) scale(0.95);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.card-enter {
animation: cardEnter 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.1s forwards;
opacity: 0;
}
/* 数据卡片动画 */
@keyframes dataCardEnter {
0% {
opacity: 0;
transform: translateY(20rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.data-card-enter {
animation: dataCardEnter 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.2s forwards;
opacity: 0;
}
/* 指标动画 */
@keyframes metricEnter {
0% {
opacity: 0;
transform: translateY(15rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.metric-enter:nth-child(1) {
animation: metricEnter 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.3s forwards;
opacity: 0;
}
.metric-enter:nth-child(2) {
animation: metricEnter 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.4s forwards;
opacity: 0;
}
.metric-enter:nth-child(3) {
animation: metricEnter 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.5s forwards;
opacity: 0;
}
.metric-enter:nth-child(4) {
animation: metricEnter 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.6s forwards;
opacity: 0;
}
/* Tab标签页动画 */
@keyframes tabsEnter {
0% {
opacity: 0;
transform: translateY(20rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.tabs-enter {
animation: tabsEnter 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.4s forwards;
opacity: 0;
}
@keyframes tabSlide {
0% {
opacity: 0;
transform: translateX(-20rpx);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
.tab-slide:nth-child(1) {
animation: tabSlide 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.5s forwards;
opacity: 0;
}
.tab-slide:nth-child(2) {
animation: tabSlide 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.6s forwards;
opacity: 0;
}
.tab-slide:nth-child(3) {
animation: tabSlide 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.7s forwards;
opacity: 0;
}
/* Tab内容淡入动画 */
@keyframes tabContentFade {
0% {
opacity: 0;
transform: translateY(10rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.tab-content-fade {
animation: tabContentFade 0.4s ease-out forwards;
}
/* 图表容器动画 */
@keyframes chartContainerEnter {
0% {
opacity: 0;
transform: translateY(30rpx) scale(0.98);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.chart-container-enter {
animation: chartContainerEnter 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.6s forwards;
opacity: 0;
}
/* 标题滑入动画 */
@keyframes titleSlideIn {
0% {
opacity: 0;
transform: translateX(-30rpx);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
.title-slide-in {
animation: titleSlideIn 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.7s forwards;
opacity: 0;
}
/* 进度条区域动画 */
@keyframes progressSectionEnter {
0% {
opacity: 0;
transform: translateY(20rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.progress-section-enter {
animation: progressSectionEnter 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.8s forwards;
opacity: 0;
}
/* 品牌列表动画 */
@keyframes brandListEnter {
0% {
opacity: 0;
transform: translateY(25rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.brand-list-enter {
animation: brandListEnter 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.9s forwards;
opacity: 0;
}
/* 品牌卡片错列动画 */
@keyframes brandCardStagger {
0% {
opacity: 0;
transform: translateY(20rpx) scale(0.95);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.brand-card-stagger {
animation: brandCardStagger 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 1s forwards;
opacity: 0;
}
/* 交互反馈动画 */
.tab-slide:active,
.brand-card-stagger:active {
transform: scale(0.98);
transition: transform 0.1s ease-out;
}
/* 图表加载状态 */
.chart-loading {
display: flex;
justify-content: center;
align-items: center;
height: 300rpx;
color: #999;
font-size: 28rpx;
}
/* 品牌标签页动画 */
@keyframes tabShopSlide {
0% {
opacity: 0;
transform: translateY(15rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.tab-shop-slide {
animation: tabShopSlide 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 1.1s forwards;
opacity: 0;
}
/* 品牌标签错列动画 */
@keyframes brandTabStagger {
0% {
opacity: 0;
transform: translateX(-15rpx);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
.brand-tab-stagger {
animation: brandTabStagger 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) 1.2s forwards;
opacity: 0;
}
/* 品牌卡片容器动画 */
@keyframes brandCardsContainer {
0% {
opacity: 0;
transform: translateY(20rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.brand-cards-container {
animation: brandCardsContainer 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 1.3s forwards;
opacity: 0;
}
/* 品牌图标包装器动画 */
@keyframes brandIconWrapper {
0% {
opacity: 0;
transform: scale(0.8) rotate(-5deg);
}
100% {
opacity: 1;
transform: scale(1) rotate(0deg);
}
}
.brand-icon-wrapper {
animation: brandIconWrapper 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
animation-delay: inherit;
}
.brand-icon {
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
/* 品牌卡片悬浮效果增强 */
.brand-card-stagger:active .brand-icon-wrapper {
transform: scale(0.9) rotate(2deg);
}
.brand-card-stagger:active .shop-name {
transform: translateY(2rpx);
}
.brand-card-stagger:active .price-num {
transform: scale(1.05);
}
/* 品牌标签活跃状态动画 */
.brand-tab-stagger.active {
animation: brandTabStagger 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards,
tabActivePulse 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.2s forwards;
}
@keyframes tabActivePulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
100% {
transform: scale(1);
}
}
</style>

View File

@ -6,11 +6,11 @@
<view class="tab-icon">🏪</view>
<text class="tab-text">商超</text>
</view>
<view class="tab-item" @tap="selectTab(2000)" :class="{ 'active': nowRank == 2000 }">
<view class="tab-item" @tap="selectTab(3000)" :class="{ 'active': nowRank == 3000 }">
<view class="tab-icon">🍽</view>
<text class="tab-text">餐饮</text>
</view>
<view class="tab-item" @tap="selectTab(3000)" :class="{ 'active': nowRank == 3000 }">
<view class="tab-item" @tap="selectTab(2000)" :class="{ 'active': nowRank == 2000 }">
<view class="tab-icon">🍿</view>
<text class="tab-text">小吃</text>
</view>
@ -85,7 +85,7 @@ export default {
/* 现代化选项卡 */
.ranking-tabs {
display: flex;
background: #fff;
background: #f8f9fa;
border-radius: 12rpx;
padding: 6rpx;
margin: 0 20rpx 24rpx;
@ -105,12 +105,12 @@ export default {
cursor: pointer;
&.active {
background: linear-gradient(135deg, #27B25F, #4CCC7F);
box-shadow: 0 4rpx 12rpx rgba(39, 178, 95, 0.3);
background: linear-gradient(135deg, #27B25F, #4CCC7F) !important;
box-shadow: 0 4rpx 12rpx rgba(39, 178, 95, 0.3) !important;
transform: translateY(-2rpx);
.tab-text {
color: #fff;
color: #fff !important;
font-weight: 600;
}
@ -260,7 +260,7 @@ export default {
}
.item-name {
font-size: 24rpx;
font-size: 28rpx;
color: #333;
font-weight: 600;
margin-bottom: 12rpx;
@ -338,7 +338,8 @@ export default {
/* 主题色支持 - 通过全局主题类控制 */
:global(.province-theme-330200) .tab-item.active {
background: linear-gradient(135deg, #1890FF, #69C0FF);
background: linear-gradient(135deg, #1890FF, #69C0FF) !important;
box-shadow: 0 4rpx 12rpx rgba(24, 144, 255, 0.3) !important;
.stat-value.sales-amount {
color: #1890FF;
@ -346,7 +347,8 @@ export default {
}
:global(.province-theme-340000) .tab-item.active {
background: linear-gradient(135deg, #748ED6, #91A7E3);
background: linear-gradient(135deg, #748ED6, #91A7E3) !important;
box-shadow: 0 4rpx 12rpx rgba(116, 142, 214, 0.3) !important;
.stat-value.sales-amount {
color: #748ED6;
@ -354,7 +356,8 @@ export default {
}
:global(.province-theme-500000) .tab-item.active {
background: linear-gradient(135deg, #FA541C, #FF7A45);
background: linear-gradient(135deg, #FA541C, #FF7A45) !important;
box-shadow: 0 4rpx 12rpx rgba(250, 84, 28, 0.3) !important;
.stat-value.sales-amount {
color: #FA541C;
@ -362,7 +365,8 @@ export default {
}
:global(.province-theme-510000) .tab-item.active {
background: linear-gradient(135deg, #FA8C16, #FFA940);
background: linear-gradient(135deg, #FA8C16, #FFA940) !important;
box-shadow: 0 4rpx 12rpx rgba(250, 140, 22, 0.3) !important;
.stat-value.sales-amount {
color: #FA8C16;
@ -370,7 +374,8 @@ export default {
}
:global(.province-theme-520000) .tab-item.active {
background: linear-gradient(135deg, #52C41A, #73D13D);
background: linear-gradient(135deg, #52C41A, #73D13D) !important;
box-shadow: 0 4rpx 12rpx rgba(82, 196, 26, 0.3) !important;
.stat-value.sales-amount {
color: #52C41A;
@ -378,7 +383,8 @@ export default {
}
:global(.province-theme-530000) .tab-item.active {
background: linear-gradient(135deg, #27B25F, #4CCC7F);
background: linear-gradient(135deg, #27B25F, #4CCC7F) !important;
box-shadow: 0 4rpx 12rpx rgba(39, 178, 95, 0.3) !important;
.stat-value.sales-amount {
color: #27B25F;
@ -386,7 +392,8 @@ export default {
}
:global(.province-theme-630000) .tab-item.active {
background: linear-gradient(135deg, #13C2C2, #36CFC9);
background: linear-gradient(135deg, #13C2C2, #36CFC9) !important;
box-shadow: 0 4rpx 12rpx rgba(19, 194, 194, 0.3) !important;
.stat-value.sales-amount {
color: #13C2C2;
@ -394,7 +401,8 @@ export default {
}
:global(.province-theme-734100) .tab-item.active {
background: linear-gradient(135deg, #E91E63, #F06292);
background: linear-gradient(135deg, #E91E63, #F06292) !important;
box-shadow: 0 4rpx 12rpx rgba(233, 30, 99, 0.3) !important;
.stat-value.sales-amount {
color: #E91E63;

View File

@ -1,11 +1,12 @@
<template>
<div>
<div class="analysis-cell" @tap="toggleShow" :class="{ 'active': item.show }" :style="activeCellStyleStr">
<div class="shop-title" :class="'theme-' + provinceCode">
<div class="analysis-cell cell-enter" @tap="toggleShow" :class="{ 'active': item.show }"
:style="activeCellStyleStr">
<div class="shop-title title-slide" :class="'theme-' + provinceCode">
<span> {{ (i > 8 ? i + 1 : '0' + (i + 1)) + ' ' + item.SHOPNAME }}</span>
<p>
<span v-show="item.UNACCOUNT_SIGN"></span>
<span class="cell-price" :style="priceStyleStr">{{
<span class="cell-price price-highlight" :style="priceStyleStr">{{
item.UNACCOUNT_SIGN
? $util.fmoney(item.CASHPAY_TOTAL, 2) : '无结账信息' }}</span>
<image src="/static/images/effective/true.png" mode="aspectFit" class="" v-if='item.SHOWDEAL_SIGN'>
@ -14,11 +15,11 @@
</p>
</div>
<p v-if="item.detail.length > 0" style="line-height: 1.2;">
<p v-if="item.detail.length > 0" style="line-height: 1.2;" class="tags-container">
<span v-for="(child, index) in item.detail" :key="index" class="atribute-tag">{{ child }} </span>
</p>
</div>
<div class="analysis-detail" v-show="item.show">
<div class="analysis-detail" v-if="item.show">
<div class="detail-unit" v-for="(unit, o) in item.ShopEndAccountList" :key="o">
<div class="uni-inline-flex">
<span class="detail-title">结账时间</span>
@ -31,24 +32,23 @@
</div>
<block v-if="unit.DESCRIPTION_DATE">
<div class="detail-title">日结校验{{ unit.DESCRIPTION_DATE }}</div>
<p><span class="detail-title">{{ unit.DESCRIPTION_STAFF }}</span><span style="font-size: 24rpx;">{{
<div class="detail-title" style="font-size: 24rpx;">日结校验{{ unit.DESCRIPTION_DATE }}</div>
<p><span class="detail-title" style="font-size: 24rpx;">{{ unit.DESCRIPTION_STAFF }}</span><span>{{
unit.DIFFERENCE_REASON
}}</span>
}}</span>
</p>
</block>
<block v-if="unit.APPROVE_DATE">
<div><span class="detail-title">日结审核{{ unit.APPROVE_DATE }}</span></div>
<p><span class="detail-title" style="font-size: 24rpx;">{{ unit.APPROVE_STAFF }}</span><span
style="font-size: 24rpx;">{{
unit.APPROVED_INFO }}</span>
<div><span class="detail-title" style="font-size: 24rpx;">日结审核{{ unit.APPROVE_DATE }}</span></div>
<p><span class="detail-title" style="font-size: 24rpx;">{{ unit.APPROVE_STAFF }}</span><span>{{
unit.APPROVED_INFO }}</span>
</p>
</block>
<div>
<p class="detail-short-info" v-show="unit.FACTAMOUNT_SALE">单品报表<text
class="uni-icon uni-icon-checkmarkempty"></text></p>
<p class="detail-short-info" v-show="unit.FACTAMOUNT_CIGARETTE">香烟数据<span
class="uni-icon uni-icon-checkmarkempty"></span></p>
<p class="detail-short-info" v-show="unit.FACTAMOUNT_SALE" style="font-size: 24rpx;"> 单品报表<text
class="uni-icon uni-icon-checkmarkempty" style="font-size: 24rpx;"></text></p>
<p class="detail-short-info" v-show="unit.FACTAMOUNT_CIGARETTE" style="font-size: 24rpx;">香烟数据<span
class="uni-icon uni-icon-checkmarkempty" style="font-size: 24rpx;"></span></p>
</div>
<div><span class="detail-title detail-short-info" style="font-size: 24rpx;">稽核次数{{ unit.CHECK_COUNT }}
</span><span class="detail-short-info" style="font-size: 24rpx;">长短款额{{
@ -128,7 +128,7 @@ export default {
//
activeCellStyleStr() {
if (!this.item.show) return ''
if (!this.item || !this.item.show) return ''
return `border: 2rpx solid ${this.themeColors.primary}; box-shadow: 0 4px 20px rgba(${this.hexToRgb(this.themeColors.primary)}, 0.15);`
},
@ -376,4 +376,159 @@ export default {
font-size: 28rpx;
}
}
/* 动画效果 - listUnit组件 */
/* 单元格入场动画 */
@keyframes cellEnter {
0% {
opacity: 0;
transform: translateY(20rpx) scale(0.95);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.cell-enter {
animation: cellEnter 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
opacity: 0;
}
/* 标题滑入动画 */
@keyframes titleSlide {
0% {
opacity: 0;
transform: translateX(-15rpx);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
.title-slide {
animation: titleSlide 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.1s forwards;
opacity: 0;
}
/* 价格高亮动画 */
@keyframes priceHighlight {
0% {
opacity: 0;
transform: scale(0.8);
}
50% {
transform: scale(1.1);
}
100% {
opacity: 1;
transform: scale(1);
}
}
.price-highlight {
animation: priceHighlight 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55) 0.2s forwards;
opacity: 0;
}
/* 标签容器动画 */
@keyframes tagsContainer {
0% {
opacity: 0;
transform: translateY(10rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.tags-container {
animation: tagsContainer 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.2s forwards;
opacity: 0;
}
/* 标签弹跳动画 */
@keyframes tagBounce {
0% {
opacity: 0;
transform: translateY(-10rpx) scale(0.8);
}
60% {
transform: translateY(2rpx) scale(1.05);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.tag-bounce {
animation: tagBounce 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards;
opacity: 0;
}
/* 详情展开动画 */
@keyframes detailExpand {
0% {
opacity: 0;
max-height: 0;
transform: translateY(-20rpx);
}
100% {
opacity: 1;
max-height: 2000rpx;
transform: translateY(0);
}
}
.detail-expand {
animation: detailExpand 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
overflow: hidden;
}
/* 详情单元淡入动画 */
@keyframes unitFadeIn {
0% {
opacity: 0;
transform: translateY(15rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.unit-fade-in {
animation: unitFadeIn 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
opacity: 0;
}
/* 交互反馈动画 */
.cell-enter:active {
transform: scale(0.98);
transition: transform 0.1s ease-out;
}
.tag-bounce:hover {
transform: translateY(-2rpx) scale(1.02);
transition: all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
/* 单元格活跃状态增强 */
.analysis-cell.active {
transform: translateY(-2rpx);
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
</style>

View File

@ -1,25 +1,21 @@
<template>
<div :class="'province-theme-' + currentProvinceCode" v-if="showPage">
<div class="page-body">
<div class="page-body page-enter">
<div class="service-brand-box">
<div class="uni-flex align-center">
<image v-if="pageMsg.Brand_ICO" :src="pageMsg.Brand_ICO" mode="aspectFit"></image>
<image v-else src="/static/images/revenue/home.png" mode="aspectFit"></image>
<!-- <div class="band-name">{{ pageMsg.Brand_Name }}</div> -->
<div class="band-name">{{ pageMsg.ShopEndaccountList && pageMsg.ShopEndaccountList.length > 0 ?
<div class="service-brand-box brand-header-enter">
<div class="uni-flex align-center brand-info-slide">
<div class="band-name brand-name-slide">{{ pageMsg.ShopEndaccountList &&
pageMsg.ShopEndaccountList.length > 0 ?
pageMsg.ShopEndaccountList[0].SERVERPART_NAME : "" }}</div>
</div>
<div class="price-num">{{ pageMsg.Revenue_Amount ? $util.fmoney(pageMsg.Revenue_Amount, 2) : '0.00' }}
</div>
<div class="price-num price-count-up">{{ pageMsg.Revenue_Amount ? $util.fmoney(pageMsg.Revenue_Amount,
2) : '0.00' }}</div>
</div>
<!--服务区门店分析-->
<div style="margin-top: 0 20rpx;padding: 0 16rpx">
<div style="margin-top: 0 20rpx;padding: 0 16rpx" class="shop-list-container">
<shopCell v-for="(item, i) in pageMsg.ShopEndaccountList" :key="i" :item='item' @toggleShow="toggleShow"
:i='i' :provinceCode='currentProvinceCode'></shopCell>
:i='i' :provinceCode='currentProvinceCode' class="shop-item-stagger"
:style="'animation-delay: ' + (i * 0.1 + 0.8) + 's'" v-if="item"></shopCell>
</div>
</div>
@ -33,7 +29,9 @@ export default {
return {
showPage: false,
pageMsg: {},
pageMsg: {
ShopEndaccountList: [] //
},
currentProvinceCode: '', //
keyJson: {
SHOWMORE_SIGN: { 1: '【长款】', 2: '【异常长款】' }, //
@ -120,10 +118,18 @@ export default {
return this.provinceTheme
},
toggleShow(i) {
if (!this.pageMsg.ShopEndaccountList || !this.pageMsg.ShopEndaccountList[i]) {
return
}
let item = this.pageMsg.ShopEndaccountList[i]
item.show = !item.show
this.$forceUpdate()
let currentItem = this.pageMsg.ShopEndaccountList[i]
let newShowState = !currentItem.show
// 使Vue.set
this.$set(this.pageMsg.ShopEndaccountList, i, {
...currentItem,
show: newShowState
})
},
@ -148,12 +154,16 @@ export default {
}).then(res => {
uni.hideLoading()
if (res.Result_Code != 100) return
res.Result_Data.ShopEndaccountList.map(n => {
n.show = true
n.detail = this.getDetail(n)
//
res.Result_Data.ShopEndaccountList.forEach((n, index) => {
// 使Vue.set
_this.$set(n, 'show', false)
_this.$set(n, 'detail', _this.getDetail(n))
})
_this.pageMsg = res.Result_Data
console.log('_this.pageMsg_this.pageMsg_this.pageMsg', _this.pageMsg);
//
_this.$set(_this, 'pageMsg', res.Result_Data)
_this.showPage = true
})
@ -169,13 +179,20 @@ export default {
}).then(res => {
uni.hideLoading()
if (res.Result_Code != 100) return
res.Result_Data.listBrandShopModel.map(n => {
n.show = true
n.detail = this.getDetail(n)
//
res.Result_Data.listBrandShopModel.forEach((n, index) => {
// 使Vue.set
_this.$set(n, 'show', false)
_this.$set(n, 'detail', _this.getDetail(n))
})
//
res.Result_Data.ShopEndaccountList = res.Result_Data.listBrandShopModel
_this.pageMsg = res.Result_Data
console.log('_this.pageMsg_this.pageMsg_this.pageMsg', _this.pageMsg);
//
_this.$set(_this, 'pageMsg', res.Result_Data)
_this.showPage = true
})
},
@ -318,11 +335,171 @@ export default {
.price-num {
color: #fff;
font-size: 28rpx;
font-size: 32rpx;
font-weight: 700;
font-family: 'PingFang SC', sans-serif;
position: relative;
z-index: 2;
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
/* 动画效果 - UniApp微信小程序兼容版本 */
/* 页面入场动画 */
@keyframes pageEnter {
0% {
opacity: 0;
transform: translateY(30rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.page-enter {
animation: pageEnter 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
}
/* 品牌头部动画 */
@keyframes brandHeaderEnter {
0% {
opacity: 0;
transform: translateY(-30rpx) scale(0.95);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.brand-header-enter {
animation: brandHeaderEnter 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) 0.2s forwards;
opacity: 0;
}
/* 品牌信息滑入动画 */
@keyframes brandInfoSlide {
0% {
opacity: 0;
transform: translateX(-20rpx);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
.brand-info-slide {
animation: brandInfoSlide 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.4s forwards;
opacity: 0;
}
/* 品牌图标弹跳动画 */
@keyframes brandIconBounce {
0% {
opacity: 0;
transform: scale(0.3) rotate(-10deg);
}
50% {
transform: scale(1.1) rotate(5deg);
}
100% {
opacity: 1;
transform: scale(1) rotate(0deg);
}
}
.brand-icon-bounce {
animation: brandIconBounce 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55) 0.6s forwards;
opacity: 0;
}
/* 品牌名称滑入动画 */
@keyframes brandNameSlide {
0% {
opacity: 0;
transform: translateX(-15rpx);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
.brand-name-slide {
animation: brandNameSlide 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.7s forwards;
opacity: 0;
}
/* 价格数字计数动画 */
@keyframes priceCountUp {
0% {
opacity: 0;
transform: translateY(10rpx) scale(0.8);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.price-count-up {
animation: priceCountUp 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.8s forwards;
opacity: 0;
}
/* 门店列表容器动画 */
@keyframes shopListContainer {
0% {
opacity: 0;
transform: translateY(30rpx);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.shop-list-container {
animation: shopListContainer 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.6s forwards;
opacity: 0;
}
/* 门店项目错列动画 */
@keyframes shopItemStagger {
0% {
opacity: 0;
transform: translateX(-30rpx) scale(0.95);
}
100% {
opacity: 1;
transform: translateX(0) scale(1);
}
}
.shop-item-stagger {
animation: shopItemStagger 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
opacity: 0;
}
/* 交互反馈动画 */
.brand-header-enter:active {
transform: scale(0.98);
transition: transform 0.1s ease-out;
}
.brand-icon-bounce:active {
transform: scale(0.9) rotate(5deg);
transition: transform 0.2s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
</style>

View File

@ -12,7 +12,7 @@
</menuModel>
<!-- <view class="home-model">
<div class="model-title">
<text class="text">综合办公</text>
<text class="text">综合办公</text>
</div>
<div class="model-content">
<menuItem v-for="item in modelMenu.zhbg"