新增加载动画, information页面优化
This commit is contained in:
parent
c2c1802937
commit
8895de2bd0
@ -6,7 +6,13 @@ const router = createRouter({
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/host-center' // 默认重定向,实际会被路由守卫处理
|
||||
redirect: '/loading' // 默认跳转到加载页面
|
||||
},
|
||||
{
|
||||
path: '/loading',
|
||||
name: 'loading',
|
||||
component: () => import('../views/LoadingView.vue'),
|
||||
meta: { requiresAuth: false, isPublic: true }
|
||||
},
|
||||
{
|
||||
path: '/host-center',
|
||||
|
||||
@ -390,10 +390,11 @@ class RouteGuard {
|
||||
*/
|
||||
isPublicPage(path) {
|
||||
const publicPages = [
|
||||
PAGES.APPLY, // 申请页面 - 重要:申请页面不需要权限检查
|
||||
PAGES.NOT_APP, // 错误页面 - APP连接失败时的页面
|
||||
'/404', // 404页面
|
||||
'/error' // 通用错误页面
|
||||
'/loading', // 加载页面 - 不需要任何检查
|
||||
PAGES.APPLY, // 申请页面 - 重要:申请页面不需要权限检查
|
||||
PAGES.NOT_APP, // 错误页面 - APP连接失败时的页面
|
||||
'/404', // 404页面
|
||||
'/error' // 通用错误页面
|
||||
]
|
||||
return publicPages.includes(path)
|
||||
}
|
||||
|
||||
@ -18,8 +18,8 @@
|
||||
|
||||
<!-- 交易记录列表 -->
|
||||
<div v-else class="transaction-list">
|
||||
<div
|
||||
v-for="transaction in transactions"
|
||||
<div
|
||||
v-for="transaction in transactions"
|
||||
:key="transaction.id"
|
||||
class="transaction-item"
|
||||
>
|
||||
@ -28,7 +28,7 @@
|
||||
<h3>{{ transaction.eventDescribe }}</h3>
|
||||
<p class="transaction-time">{{ transaction.createTime }}</p>
|
||||
</div>
|
||||
<div class="transaction-amount" :class="transaction.amount > 0 ? 'positive' : 'negative'">
|
||||
<div class="transaction-amount" :class="transaction.isPositive ? 'positive' : 'negative'">
|
||||
{{ transaction.amount > 0 ? '+' : '' }}{{ Math.abs(transaction.amount) }}
|
||||
</div>
|
||||
</div>
|
||||
@ -48,6 +48,7 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
import MobileHeader from '../components/MobileHeader.vue'
|
||||
import { getWalletDetails, formatDateTime, formatEventDescribe } from '../api/wallet.js'
|
||||
import {formatUTCTime} from "@/utils/utcFormat.js";
|
||||
|
||||
const transactions = ref([])
|
||||
const loading = ref(false)
|
||||
@ -57,20 +58,51 @@ const error = ref(null)
|
||||
const fetchData = async () => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
|
||||
|
||||
try {
|
||||
const response = await getWalletDetails()
|
||||
|
||||
|
||||
// 处理API返回的数据
|
||||
if (response.body && Array.isArray(response.body)) {
|
||||
transactions.value = response.body.map(item => ({
|
||||
id: item.id,
|
||||
eventDescribe: formatEventDescribe(item.eventDescribe),
|
||||
createTime: formatDateTime(item.createTime),
|
||||
amount: item.type === 0 ? -Math.abs(item.amount) : item.amount, // type=0表示支出,显示为负数
|
||||
balance: item.balance,
|
||||
event: item.event
|
||||
}))
|
||||
transactions.value = response.body.map(item => {
|
||||
// 根据 event 字段判断是加还是减
|
||||
let amount = item.amount
|
||||
let isPositive = false
|
||||
|
||||
// 根据事件类型判断显示
|
||||
switch (item.event) {
|
||||
case 'RECEIVE_TRANSFER':
|
||||
// 接收转账 - 显示为正数(绿色)
|
||||
amount = Math.abs(item.amount)
|
||||
isPositive = true
|
||||
break
|
||||
case 'TRANSFER':
|
||||
// 转账支出 - 显示为负数(红色)
|
||||
amount = -Math.abs(item.amount)
|
||||
isPositive = false
|
||||
break
|
||||
default:
|
||||
// 其他情况根据 type 字段处理
|
||||
// type: 0 表示收入,1 表示支出
|
||||
if (item.type === 0) {
|
||||
amount = Math.abs(item.amount)
|
||||
isPositive = true
|
||||
} else {
|
||||
amount = -Math.abs(item.amount)
|
||||
isPositive = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
eventDescribe: formatEventDescribe(item.eventDescribe),
|
||||
createTime: formatUTCTime(item.createTime),
|
||||
amount: amount,
|
||||
balance: item.balance,
|
||||
event: item.event,
|
||||
isPositive: isPositive
|
||||
}
|
||||
})
|
||||
} else {
|
||||
transactions.value = []
|
||||
}
|
||||
|
||||
427
src/views/LoadingView.vue
Normal file
427
src/views/LoadingView.vue
Normal file
@ -0,0 +1,427 @@
|
||||
<template>
|
||||
<div class="loading-view">
|
||||
<!-- 背景渐变 -->
|
||||
<div class="loading-background"></div>
|
||||
|
||||
<!-- 加载内容 -->
|
||||
<div class="loading-content">
|
||||
<!-- Logo或品牌标识 -->
|
||||
<div class="logo-container">
|
||||
<div class="logo">
|
||||
<div class="logo-icon">📱</div>
|
||||
<h1 class="logo-text">LikeI</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 加载动画 -->
|
||||
<div class="loading-animation">
|
||||
<div class="spinner">
|
||||
<div class="spinner-ring"></div>
|
||||
<div class="spinner-ring"></div>
|
||||
<div class="spinner-ring"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 加载文本 -->
|
||||
<div class="loading-text">
|
||||
<p class="main-text">{{ loadingText }}</p>
|
||||
<p class="sub-text">{{ subText }}</p>
|
||||
</div>
|
||||
|
||||
<!-- 进度条 -->
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" :style="{ width: progress + '%' }"></div>
|
||||
</div>
|
||||
<span class="progress-text">{{ Math.round(progress) }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部提示 -->
|
||||
<div class="loading-footer">
|
||||
<p>Powered by LikeI Team</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { isInApp, connectApplication, parseAccessOrigin, parseHeader, setHttpHeaders } from '../utils/appBridge.js'
|
||||
import { appConnectionManager } from '../utils/appConnectionManager.js'
|
||||
import { getTeamEntry } from '../api/wallet.js'
|
||||
import { setUserInfo } from '../utils/userStore.js'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 响应式数据
|
||||
const loadingText = ref('Initializing...')
|
||||
const subText = ref('Setting up your experience')
|
||||
const progress = ref(0)
|
||||
|
||||
// 加载步骤配置
|
||||
const loadingSteps = [
|
||||
{ text: 'Connecting to server...', subText: 'Establishing secure connection', duration: 800 },
|
||||
{ text: 'Loading user data...', subText: 'Retrieving your information', duration: 1000 },
|
||||
{ text: 'Setting up interface...', subText: 'Preparing your workspace', duration: 600 },
|
||||
{ text: 'Almost ready...', subText: 'Finalizing setup', duration: 400 }
|
||||
]
|
||||
|
||||
let currentStepIndex = 0
|
||||
let progressInterval = null
|
||||
let stepTimeout = null
|
||||
|
||||
// 开始加载流程
|
||||
const startLoading = async () => {
|
||||
try {
|
||||
// 如果在APP环境中,进行连接
|
||||
if (isInApp()) {
|
||||
await connectToApp()
|
||||
}
|
||||
|
||||
// 开始进度动画
|
||||
startProgressAnimation()
|
||||
|
||||
// 等待加载完成
|
||||
await waitForLoadingComplete()
|
||||
|
||||
// 跳转到目标页面
|
||||
await navigateToTargetPage()
|
||||
|
||||
} catch (error) {
|
||||
console.error('Loading failed:', error)
|
||||
// 出错时跳转到错误页面
|
||||
router.replace('/not_app')
|
||||
}
|
||||
}
|
||||
|
||||
// APP连接
|
||||
const connectToApp = async () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
connectApplication(async (access) => {
|
||||
try {
|
||||
const result = parseAccessOrigin(access)
|
||||
|
||||
if (result.success) {
|
||||
const headerInfo = parseHeader(result.data)
|
||||
await setHttpHeaders(headerInfo)
|
||||
appConnectionManager.setConnected(headerInfo)
|
||||
resolve()
|
||||
} else {
|
||||
reject(new Error(result.error))
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 开始进度动画
|
||||
const startProgressAnimation = () => {
|
||||
let currentProgress = 0
|
||||
const totalDuration = loadingSteps.reduce((sum, step) => sum + step.duration, 0)
|
||||
const progressIncrement = 100 / totalDuration * 50 // 每50ms增加的进度
|
||||
|
||||
progressInterval = setInterval(() => {
|
||||
currentProgress += progressIncrement
|
||||
progress.value = Math.min(currentProgress, 100)
|
||||
|
||||
if (progress.value >= 100) {
|
||||
clearInterval(progressInterval)
|
||||
}
|
||||
}, 50)
|
||||
|
||||
// 更新步骤文本
|
||||
updateLoadingStep()
|
||||
}
|
||||
|
||||
// 更新加载步骤
|
||||
const updateLoadingStep = () => {
|
||||
if (currentStepIndex < loadingSteps.length) {
|
||||
const step = loadingSteps[currentStepIndex]
|
||||
loadingText.value = step.text
|
||||
subText.value = step.subText
|
||||
|
||||
stepTimeout = setTimeout(() => {
|
||||
currentStepIndex++
|
||||
updateLoadingStep()
|
||||
}, step.duration)
|
||||
}
|
||||
}
|
||||
|
||||
// 等待加载完成
|
||||
const waitForLoadingComplete = () => {
|
||||
return new Promise(resolve => {
|
||||
const totalTime = loadingSteps.reduce((sum, step) => sum + step.duration, 0)
|
||||
setTimeout(resolve, totalTime)
|
||||
})
|
||||
}
|
||||
|
||||
// 跳转到目标页面
|
||||
const navigateToTargetPage = async () => {
|
||||
try {
|
||||
// 尝试获取用户信息来判断跳转目标
|
||||
const response = await getTeamEntry()
|
||||
|
||||
if (response && response.status && response.body) {
|
||||
// 有团队信息,根据默认设置跳转到主页
|
||||
const memberProfile = response.body.memberProfile
|
||||
if (memberProfile) {
|
||||
setUserInfo(memberProfile, response.body.teamProfile)
|
||||
|
||||
// 根据用户身份跳转到对应页面
|
||||
// 这里可以根据需要调整默认跳转逻辑
|
||||
router.replace('/host-center')
|
||||
} else {
|
||||
router.replace('/apply')
|
||||
}
|
||||
} else {
|
||||
// 没有团队信息,跳转到申请页面
|
||||
router.replace('/apply')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to get team entry:', error)
|
||||
// 出错时跳转到申请页面
|
||||
router.replace('/apply')
|
||||
}
|
||||
}
|
||||
|
||||
// 清理定时器
|
||||
const cleanup = () => {
|
||||
if (progressInterval) {
|
||||
clearInterval(progressInterval)
|
||||
progressInterval = null
|
||||
}
|
||||
if (stepTimeout) {
|
||||
clearTimeout(stepTimeout)
|
||||
stepTimeout = null
|
||||
}
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
startLoading()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
cleanup()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.loading-view {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.loading-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background:
|
||||
radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.3) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.15) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(120, 219, 255, 0.1) 0%, transparent 50%);
|
||||
animation: backgroundShift 10s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes backgroundShift {
|
||||
0%, 100% { transform: translateX(0) translateY(0); }
|
||||
50% { transform: translateX(-10px) translateY(-5px); }
|
||||
}
|
||||
|
||||
.loading-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
z-index: 2;
|
||||
text-align: center;
|
||||
max-width: 300px;
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
animation: logoFloat 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes logoFloat {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-10px); }
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
font-size: 60px;
|
||||
margin-bottom: 16px;
|
||||
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.2));
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
margin: 0;
|
||||
letter-spacing: 2px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.loading-animation {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
position: relative;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.spinner-ring {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 3px solid transparent;
|
||||
border-top: 3px solid rgba(255, 255, 255, 0.8);
|
||||
border-radius: 50%;
|
||||
animation: spin 1.5s linear infinite;
|
||||
}
|
||||
|
||||
.spinner-ring:nth-child(2) {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
top: 10%;
|
||||
left: 10%;
|
||||
border-top-color: rgba(255, 255, 255, 0.6);
|
||||
animation-duration: 2s;
|
||||
animation-direction: reverse;
|
||||
}
|
||||
|
||||
.spinner-ring:nth-child(3) {
|
||||
width: 60%;
|
||||
height: 60%;
|
||||
top: 20%;
|
||||
left: 20%;
|
||||
border-top-color: rgba(255, 255, 255, 0.4);
|
||||
animation-duration: 2.5s;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
margin-bottom: 30px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.main-text {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 8px 0;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.sub-text {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
opacity: 0.8;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #fff, rgba(255, 255, 255, 0.8));
|
||||
border-radius: 2px;
|
||||
transition: width 0.3s ease;
|
||||
box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.loading-footer {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.loading-footer p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 480px) {
|
||||
.loading-content {
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
font-size: 50px;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.main-text {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 暗黑模式支持 */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.loading-view {
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
x
Reference in New Issue
Block a user