743 lines
16 KiB
Plaintext
743 lines
16 KiB
Plaintext
<template>
|
||
<view class="container" :style="{ height: data.windowHeight + 'px' }">
|
||
<image class="index-bg-img" :style="{ width: windowWidth + 'px' }" src="/static/image/index/index-bg.png"
|
||
mode="widthFix">
|
||
</image>
|
||
<view class="nav-bar-box">
|
||
<view class="status-box" :style="{ height: statusBarHeight + 'px' }"></view>
|
||
<view class="nav-box">
|
||
<view class="left-box" @click="exit">
|
||
<image style="width: 40rpx; height: 40rpx;" src="/static/image/nav-bar/back-black.png"></image>
|
||
</view>
|
||
<view class="title">小宝模拟器</view>
|
||
<view class="right-box"></view>
|
||
</view>
|
||
|
||
</view>
|
||
<view class="content-box" :style="{ height: windowHeight + 'px' }">
|
||
<view>
|
||
<view style="background-color: transparent;" :style="{ height: (44 + statusBarHeight) + 'px' }"></view>
|
||
<view class="user-box">
|
||
<image class="user-bg" :style="{ width: (windowWidth - 32) + 'px' }"
|
||
:src="`/static/image/index/${userInfo.vip > 1 ? (userInfo.vip == 3 ? 'lifetime-vip-bg' : 'vip-bg') : 'no-vip-bg'}.png`"
|
||
mode=""></image>
|
||
<view class="user-info-box" :style="{ width: (windowWidth - 32) + 'px' }">
|
||
<image class="user-avatar" :src="userInfo.avater"></image>
|
||
<view class="user-info">
|
||
<view class="name-box">
|
||
<text class="phone-text">ID:{{ userInfo.user_id }}</text>
|
||
<image v-if="userInfo.vip > 1" class="vip-logo" src="/static/image/index/vip-logo.png">
|
||
</image>
|
||
</view>
|
||
<view class="">
|
||
<text v-if="userInfo.vip > 1" class="vip-end-time">会员到期:{{ userInfo.vip_expire }}</text>
|
||
<text v-else class="vip-end-time">开通会员解锁全部功能</text>
|
||
</view>
|
||
</view>
|
||
<view v-if="userInfo.vip && userInfo.vip != 3" class="btn-box" @click="openVip">
|
||
<image class="open-vip-btn"
|
||
:src="`/static/image/index/${userInfo.vip > 1 ? 'vip-btn' : 'open-vip-btn'}.png`">
|
||
</image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="notice-box" @click="clickNotice">
|
||
<view class="sound-box">
|
||
<uni-icons type="sound" size="18" color="#D8D8D8"></uni-icons>
|
||
</view>
|
||
|
||
<view ref="noticeContainer" class="notice-content-wrapper">
|
||
<view ref="noticeInner" class="notice-inner">
|
||
<text ref="noticeBox" class="notice-content" style="margin-right: 30rpx;">{{ noticeInfo.text
|
||
}}</text>
|
||
<text class="notice-content" style="margin-right: 30rpx;">{{ noticeInfo.text }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="group-box">
|
||
<image class="title-img" src="/static/image/index/shipingjiaocheng.png"></image>
|
||
<view class="video-help-box">
|
||
<view class="video-help-item" v-for="item in videoHelpList" :key="item.id"
|
||
@click="clickVideoHelp(item)">
|
||
<image class="video-help-img" :src="item.icon"></image>
|
||
<text class="video-help-title">{{ item.text }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="group-box">
|
||
<image class="title-img" src="/static/image/index/monixiaobao.png"></image>
|
||
<view class="menu-box">
|
||
<view class="item-box" v-for="item in menuList" :key="item.name" @click="clickMenu(item)">
|
||
<view class="menu-item" :style="{ width: (windowWidth - 50) / 2 + 'px' }">
|
||
<!-- <text class="menu-item-name">{{ item.name }}</text> -->
|
||
<image class="name-img" :src="'/static/image/index/menu-name/' + item.icon + '.png'"
|
||
mode="heightFix">
|
||
</image>
|
||
<image style="width: 108rpx;height: 108rpx; flex-shrink: 0;"
|
||
:src="'/static/image/index/menu-icon/' + item.icon + '.png'"></image>
|
||
</view>
|
||
<image v-if="item.isHot" class="hot-icon" src="/static/image/index/hot-icon.png"></image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="activity-box">
|
||
<image class="alipay-year-bill" :style="{ width: (windowWidth - 32) + 'px' }"
|
||
src="/static/image/index/alipay-year-bill.png" mode="widthFix"
|
||
@click="util.goPage(`/pages/common/alipay-annual-bill/alipay-annual-bill`)"></image>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="footer-box" :class="{ 'ios-padding-bottom': platform === 'ios' }">
|
||
<text class="vision-text">版本:{{ vision }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
|
||
</view>
|
||
</template>
|
||
<script setup>
|
||
import {
|
||
util,
|
||
uiUtil
|
||
} from '@/utils/common.js'
|
||
import {
|
||
storage
|
||
} from '@/utils/storage.js'
|
||
import {
|
||
get
|
||
} from '@/utils/requests.js'
|
||
import {
|
||
ref,
|
||
reactive,
|
||
toRefs,
|
||
getCurrentInstance
|
||
} from 'vue';
|
||
import {
|
||
onLoad,
|
||
onShow
|
||
} from '@dcloudio/uni-app';
|
||
|
||
const instance = getCurrentInstance();
|
||
const { proxy } = instance;
|
||
|
||
// 菜单列表
|
||
const menuList = [{
|
||
icon: "yuemoni",
|
||
name: "余额模拟",
|
||
isHot: false,
|
||
path: "/pages/balance/index"
|
||
},
|
||
{
|
||
icon: "zhangdanshencheng",
|
||
name: "账单生成",
|
||
isHot: false,
|
||
path: "/pages/bill/bill-list/bill-list"
|
||
},
|
||
{
|
||
icon: "licaiheika",
|
||
name: "理财黑卡",
|
||
isHot: true,
|
||
// path: "/pages/finance-management/index"
|
||
path: ""
|
||
},
|
||
{
|
||
icon: "huabei",
|
||
name: "花呗",
|
||
isHot: false,
|
||
path: "/pages/ant-credit-pay/index"
|
||
},
|
||
|
||
]
|
||
|
||
const data = reactive({
|
||
statusBarHeight: 0,
|
||
windowWidth: 0,
|
||
windowHeight: 0,
|
||
userInfo: {},
|
||
videoHelpList: [],
|
||
noticeInfo: {},
|
||
vision: "",
|
||
platform: '' // 添加平台信息
|
||
})
|
||
|
||
const {
|
||
statusBarHeight,
|
||
windowWidth,
|
||
windowHeight,
|
||
userInfo,
|
||
videoHelpList,
|
||
noticeInfo,
|
||
vision,
|
||
platform
|
||
} = toRefs(data);
|
||
|
||
onLoad(async () => {
|
||
// 获取平台信息
|
||
const systemInfo = uni.getSystemInfoSync()
|
||
data.platform = systemInfo.platform
|
||
data.vision = uni.getStorageSync('version')
|
||
})
|
||
|
||
onShow(() => {
|
||
// 启动时获取数据
|
||
fetchUserData()
|
||
// 每次显示时刷新数据
|
||
setUserData()
|
||
// 获取系统信息
|
||
const systemInfo = uni.getSystemInfoSync();
|
||
data.statusBarHeight = systemInfo.statusBarHeight;
|
||
data.windowWidth = systemInfo.windowWidth;
|
||
data.windowHeight = systemInfo.windowHeight;
|
||
// #ifdef APP-PLUS
|
||
util.setAndroidSystemBarColor('#F0F4F9')
|
||
setTimeout(() => {
|
||
plus.navigator.setStatusBarStyle("dark");
|
||
}, 500)
|
||
// #endif
|
||
})
|
||
|
||
/**
|
||
* 获取用户数据(从服务器)
|
||
*/
|
||
const fetchUserData = async () => {
|
||
try {
|
||
// 先设置默认值,避免页面显示异常
|
||
setUserData()
|
||
|
||
// 并行获取用户信息和配置
|
||
const [userResult, configResult] = await Promise.allSettled([
|
||
fetchUserInfo(),
|
||
fetchUserConfig()
|
||
])
|
||
|
||
// 处理用户信息结果
|
||
if (userResult.status === 'fulfilled') {
|
||
console.log('用户信息获取成功')
|
||
} else {
|
||
console.error('获取用户信息失败:', userResult.reason)
|
||
}
|
||
|
||
// 处理用户配置结果
|
||
if (configResult.status === 'fulfilled') {
|
||
console.log('用户配置获取成功')
|
||
} else {
|
||
console.error('获取用户配置失败:', configResult.reason)
|
||
}
|
||
|
||
// 刷新页面数据
|
||
setUserData()
|
||
} catch (error) {
|
||
console.error('获取用户数据异常:', error)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取用户信息
|
||
*/
|
||
const fetchUserInfo = async () => {
|
||
const data = await get('', 'api/user', {})
|
||
if (data.code === 0) {
|
||
uni.setStorageSync('userInfo', data.data)
|
||
return data.data
|
||
} else {
|
||
throw new Error(data.message || '获取用户信息失败')
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取用户配置
|
||
*/
|
||
const fetchUserConfig = async () => {
|
||
const data = await get('', 'api/user/config', {})
|
||
if (data.code === 0) {
|
||
uni.setStorageSync('config', data.data)
|
||
return data.data
|
||
} else {
|
||
throw new Error(data.message || '获取用户配置失败')
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 设置用户数据(从本地存储读取)
|
||
*/
|
||
const setUserData = () => {
|
||
// 用户信息 - 提供默认值
|
||
const userInfoData = storage.get("userInfo")
|
||
data.userInfo = userInfoData || {
|
||
user_id: '加载中...',
|
||
avater: '/static/default-avatar.png',
|
||
vip: 0,
|
||
vip_expire: ''
|
||
}
|
||
|
||
// 配置信息 - 安全访问
|
||
const configData = storage.get("config")
|
||
if (configData && configData.config) {
|
||
data.noticeInfo = configData.config['client.uniapp.notice'] || {
|
||
text: '加载中...',
|
||
url: ''
|
||
}
|
||
data.noticeInfo = configData.config['client.uniapp.notice'] || {
|
||
text: '加载中...',
|
||
url: ''
|
||
}
|
||
// 启动走马灯
|
||
startMarquee();
|
||
data.videoHelpList = configData.config['client.uniapp.alipay.video_help'] || []
|
||
} else {
|
||
data.noticeInfo = {
|
||
text: '加载中...',
|
||
url: ''
|
||
}
|
||
data.videoHelpList = []
|
||
}
|
||
}
|
||
|
||
const clickMenu = (item) => {
|
||
if (!item.path) {
|
||
uiUtil.showError('开发中')
|
||
} else {
|
||
const isOverdue = storage.get("huabei_info_storage").isOverdue || false
|
||
let url = item.path
|
||
if (item.name == "花呗" && isOverdue) {
|
||
url = "/pages/ant-credit-pay/overdue-payment/overdue-payment"
|
||
}
|
||
util.goPage(url)
|
||
}
|
||
}
|
||
|
||
const openVip = () => {
|
||
console.log('openVip')
|
||
util.goPage('/pages/common/recharge/index')
|
||
}
|
||
|
||
/**
|
||
* 点击视频教程
|
||
* @param item
|
||
*/
|
||
const clickVideoHelp = (item) => {
|
||
const url = item.url
|
||
util.goPage(`/pages/common/webview/webview?url=${encodeURIComponent(url)}&title=${item.text}`)
|
||
}
|
||
|
||
/**
|
||
* 点击公告
|
||
*/
|
||
const clickNotice = () => {
|
||
if (!noticeInfo.value.url) return
|
||
const url = noticeInfo.value.url + `&uni_id=${userInfo.value.user_id}`
|
||
util.goPage(`/pages/common/webview/webview?url=${encodeURIComponent(url)}&title=${noticeInfo.value.title}`)
|
||
}
|
||
|
||
/**
|
||
* 退出模拟器
|
||
*/
|
||
const exit = () => {
|
||
plus.runtime.quit()
|
||
}
|
||
|
||
const noticeContainer = ref(null);
|
||
const noticeInner = ref(null);
|
||
const noticeBox = ref(null);
|
||
|
||
// #ifndef H5
|
||
const animation = uni.requireNativePlugin('animation');
|
||
const dom = uni.requireNativePlugin('dom');
|
||
// #endif
|
||
|
||
let marqueeTimer = null;
|
||
const currentMarqueeId = ref(0);
|
||
const lastMarqueeText = ref('');
|
||
|
||
/**
|
||
* 开始走马灯
|
||
*/
|
||
const startMarquee = () => {
|
||
// 避免不必要的重置:如果文本没有变化且正在运行,则忽略
|
||
if (lastMarqueeText.value === noticeInfo.value.text && currentMarqueeId.value > 0) {
|
||
return;
|
||
}
|
||
lastMarqueeText.value = noticeInfo.value.text;
|
||
|
||
// 清除待执行的启动定时器
|
||
if (marqueeTimer) {
|
||
clearTimeout(marqueeTimer);
|
||
marqueeTimer = null;
|
||
}
|
||
|
||
// 增加 ID 以使之前的动画循环失效
|
||
currentMarqueeId.value++;
|
||
const myId = currentMarqueeId.value;
|
||
|
||
// 清除旧动画状态 (重置位置)
|
||
if (noticeInner.value) {
|
||
animation.transition(noticeInner.value, {
|
||
styles: {
|
||
transform: 'translateX(0)'
|
||
},
|
||
duration: 0,
|
||
delay: 0
|
||
});
|
||
}
|
||
|
||
marqueeTimer = setTimeout(() => {
|
||
if (!noticeContainer.value || !noticeInner.value) return;
|
||
// 如果ID不匹配,说明有新的启动请求,放弃当前
|
||
if (myId !== currentMarqueeId.value) return;
|
||
|
||
// 获取容器宽度
|
||
dom.getComponentRect(noticeContainer.value, (resContainer) => {
|
||
const containerWidth = resContainer.size.width;
|
||
if (!containerWidth) return;
|
||
|
||
// 获取文本/内部容器宽度
|
||
dom.getComponentRect(noticeInner.value, (resText) => {
|
||
const textWidth = resText.size.width / 2; // 因为复制了一份
|
||
if (!textWidth) return;
|
||
|
||
// 执行滚动
|
||
runMarqueeAnimation(containerWidth, textWidth, myId);
|
||
});
|
||
});
|
||
}, 1000); // 增加延时确保渲染
|
||
}
|
||
|
||
/**
|
||
* 执行滚动动画循环
|
||
*/
|
||
const runMarqueeAnimation = (containerWidth, textWidth, myId) => {
|
||
// ID 校验:如果当前ID不匹配,说明已被新动画取代,停止递归
|
||
if (myId !== currentMarqueeId.value) return;
|
||
if (!noticeInner.value) return;
|
||
|
||
// 动画的目标:移动 Inner 容器
|
||
const target = noticeInner.value;
|
||
const realScrollDistance = textWidth;
|
||
|
||
// 1. 重置位置到 0 (无感重置)
|
||
animation.transition(target, {
|
||
styles: {
|
||
transform: `translateX(0)`
|
||
},
|
||
duration: 0,
|
||
delay: 0
|
||
}, () => {
|
||
// 再次校验ID
|
||
if (myId !== currentMarqueeId.value) return;
|
||
|
||
// 计算时间
|
||
const speed = 50; // px/s
|
||
const duration = (realScrollDistance / speed) * 1000;
|
||
|
||
// 2. 向左滚动一个文本宽度的距离
|
||
animation.transition(target, {
|
||
styles: {
|
||
transform: `translateX(-${realScrollDistance}px)`
|
||
},
|
||
duration: duration,
|
||
timingFunction: 'linear',
|
||
delay: 0
|
||
}, () => {
|
||
// 3. 循环
|
||
runMarqueeAnimation(containerWidth, textWidth, myId);
|
||
});
|
||
});
|
||
}
|
||
</script>
|
||
<style>
|
||
.container {
|
||
background-color: #F0F4F9;
|
||
}
|
||
|
||
.index-bg-img {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 1;
|
||
}
|
||
|
||
.nav-bar-box {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 999;
|
||
background-color: transparent;
|
||
}
|
||
|
||
.content-box {
|
||
position: fixed;
|
||
top: 0rpx;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 999;
|
||
background-color: transparent;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.status-box {
|
||
width: 100%;
|
||
}
|
||
|
||
.nav-box {
|
||
height: 44px;
|
||
background-color: transparent;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.left-box {
|
||
width: 60px;
|
||
height: 44px;
|
||
background-color: transparent;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.title {
|
||
flex: 1;
|
||
height: 44px;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
color: #1A1A1A;
|
||
font-weight: bold;
|
||
background-color: transparent;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.right-box {
|
||
width: 60px;
|
||
height: 44px;
|
||
background-color: transparent;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.user-box {
|
||
position: relative;
|
||
margin: 24rpx 32rpx 0;
|
||
height: 120rpx;
|
||
z-index: 10;
|
||
}
|
||
|
||
.user-bg {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 120rpx;
|
||
}
|
||
|
||
.user-info-box {
|
||
position: absolute;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 32rpx;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 120rpx;
|
||
z-index: 1;
|
||
}
|
||
|
||
.user-info {
|
||
flex: 1;
|
||
}
|
||
|
||
.user-avatar {
|
||
width: 72rpx;
|
||
height: 72rpx;
|
||
border-radius: 50%;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.name-box {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.phone-text {
|
||
font-size: 28rpx;
|
||
color: #FFFFFF;
|
||
font-weight: bold;
|
||
margin-right: 12rpx;
|
||
}
|
||
|
||
.vip-logo {
|
||
width: 60rpx;
|
||
height: 20rpx;
|
||
}
|
||
|
||
.vip-end-time {
|
||
font-size: 24rpx;
|
||
color: #FFFFFF;
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.open-vip-btn {
|
||
height: 40rpx;
|
||
width: 116rpx;
|
||
}
|
||
|
||
.notice-box {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
margin: 16rpx 32rpx 0;
|
||
background-color: #FFFFFF;
|
||
border-radius: 16rpx 16rpx 16rpx 16rpx;
|
||
padding: 0 16rpx;
|
||
height: 64rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.sound-box {
|
||
height: 64rpx;
|
||
width: 50rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: relative;
|
||
z-index: 10;
|
||
}
|
||
|
||
.notice-content-wrapper {
|
||
flex: 1;
|
||
flex-direction: row;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.notice-inner {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.notice-content {
|
||
font-size: 24rpx;
|
||
color: #767676;
|
||
}
|
||
|
||
.group-box {
|
||
margin: 32rpx;
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.title-img {
|
||
width: 140rpx;
|
||
height: 44rpx;
|
||
}
|
||
|
||
.video-help-box {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
background-color: #FFFFFF;
|
||
padding: 24rpx 32rpx;
|
||
border-radius: 24rpx;
|
||
margin-top: 16rpx;
|
||
}
|
||
|
||
.video-help-item {
|
||
text-align: center;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.video-help-img {
|
||
width: 96rpx;
|
||
height: 96rpx;
|
||
}
|
||
|
||
.video-help-title {
|
||
font-size: 24rpx;
|
||
color: #1A1A1A;
|
||
text-align: center;
|
||
}
|
||
|
||
.menu-box {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
flex-wrap: wrap;
|
||
margin-top: 16rpx;
|
||
}
|
||
|
||
.item-box {
|
||
position: relative;
|
||
height: 156rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: flex-end;
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.menu-item {
|
||
display: flex;
|
||
position: relative;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
background-color: #FFFFFF;
|
||
border-radius: 16rpx;
|
||
padding: 16rpx 32rpx;
|
||
height: 140rpx;
|
||
}
|
||
|
||
|
||
.menu-item-name {
|
||
font-weight: bold;
|
||
font-size: 32rpx;
|
||
color: #000000;
|
||
}
|
||
|
||
.name-img {
|
||
height: 28rpx;
|
||
}
|
||
|
||
.hot-icon {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 68rpx;
|
||
height: 30rpx;
|
||
z-index: 99;
|
||
}
|
||
|
||
.activity-box {
|
||
margin: 0 32rpx;
|
||
}
|
||
|
||
.footer-box {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-top: 40rpx;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.vision-text {
|
||
font-size: 24rpx;
|
||
color: #767676;
|
||
}
|
||
|
||
.ios-padding-bottom {
|
||
margin-bottom: 50rpx;
|
||
}
|
||
</style> |