在多个页面中通过骨架屏优化加载状态。
在章节、礼物代付详情、阅读和搜索结果页面,用骨架屏替换传统加载指示器,以提升数据获取过程中的用户体验。 更新骨架屏样式,使加载状态更加美观。 实现章节和配置信息的缓存策略,以优化性能并减少冷启动问题。
This commit is contained in:
@@ -17,10 +17,21 @@
|
||||
<!-- 导航栏占位 -->
|
||||
<view class="nav-placeholder" style="height: {{statusBarHeight + 44}}px;"></view>
|
||||
|
||||
<!-- 目录加载中 -->
|
||||
<view class="parts-loading" wx:if="{{partsLoading}}">
|
||||
<view class="parts-loading-spinner"></view>
|
||||
<text class="parts-loading-text">加载目录中...</text>
|
||||
<!-- 目录骨架屏:加载中时展示 -->
|
||||
<view class="parts-skeleton" wx:if="{{partsLoading}}">
|
||||
<view class="skeleton-book-card">
|
||||
<view class="skeleton-book-icon"></view>
|
||||
<view class="skeleton-book-info">
|
||||
<view class="skeleton-line skeleton-title"></view>
|
||||
<view class="skeleton-line skeleton-subtitle"></view>
|
||||
</view>
|
||||
<view class="skeleton-count"></view>
|
||||
</view>
|
||||
<view class="skeleton-part-list">
|
||||
<view class="skeleton-part-item" wx:for="{{[1,2,3,4,5]}}" wx:key="*this">
|
||||
<view class="skeleton-part-header"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 书籍信息卡 -->
|
||||
|
||||
@@ -75,32 +75,75 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ===== 目录加载中 ===== */
|
||||
.parts-loading {
|
||||
/* ===== 目录骨架屏 ===== */
|
||||
.parts-skeleton {
|
||||
padding: 32rpx;
|
||||
}
|
||||
|
||||
.skeleton-book-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
padding: 32rpx;
|
||||
background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%);
|
||||
border-radius: 32rpx;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.skeleton-book-icon {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 24rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.skeleton-book-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.skeleton-line {
|
||||
height: 32rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.skeleton-title { width: 70%; }
|
||||
.skeleton-subtitle { width: 50%; }
|
||||
|
||||
.skeleton-count {
|
||||
width: 80rpx;
|
||||
height: 64rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.skeleton-part-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.parts-loading-spinner {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border: 6rpx solid rgba(255, 255, 255, 0.1);
|
||||
border-top-color: #00CED1;
|
||||
border-radius: 50%;
|
||||
animation: parts-spin 0.8s linear infinite;
|
||||
.skeleton-part-item .skeleton-part-header {
|
||||
height: 100rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
|
||||
.parts-loading-text {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
@keyframes parts-spin {
|
||||
to { transform: rotate(360deg); }
|
||||
@keyframes skeleton-shimmer {
|
||||
0% { background-position: 200% 0; }
|
||||
100% { background-position: -200% 0; }
|
||||
}
|
||||
|
||||
/* ===== 书籍信息卡 ===== */
|
||||
|
||||
@@ -14,9 +14,20 @@
|
||||
|
||||
<view class="content" style="padding-top: calc({{statusBarHeight}}px + 88rpx);">
|
||||
<block wx:if="{{loading}}">
|
||||
<view class="loading-box">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">加载中...</text>
|
||||
<view class="skeleton-wrap">
|
||||
<view class="skeleton-hero">
|
||||
<view class="skeleton-hero-badge"></view>
|
||||
<view class="skeleton-hero-title"></view>
|
||||
<view class="skeleton-hero-desc"></view>
|
||||
<view class="skeleton-hero-amount"></view>
|
||||
</view>
|
||||
<view class="skeleton-card">
|
||||
<view class="skeleton-avatar"></view>
|
||||
<view class="skeleton-info">
|
||||
<view class="skeleton-line"></view>
|
||||
<view class="skeleton-line short"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<block wx:elif="{{detail}}">
|
||||
|
||||
@@ -56,32 +56,97 @@
|
||||
padding: 24rpx 24rpx 200rpx;
|
||||
}
|
||||
|
||||
/* 加载 */
|
||||
.loading-box {
|
||||
/* 骨架屏 */
|
||||
.skeleton-wrap {
|
||||
padding: 24rpx 0;
|
||||
}
|
||||
|
||||
.skeleton-hero {
|
||||
background: rgba(24, 24, 27, 0.8);
|
||||
border-radius: 32rpx;
|
||||
padding: 40rpx;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.skeleton-hero-badge {
|
||||
width: 120rpx;
|
||||
height: 40rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.skeleton-hero-title {
|
||||
width: 80%;
|
||||
height: 48rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.skeleton-hero-desc {
|
||||
width: 60%;
|
||||
height: 32rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.skeleton-hero-amount {
|
||||
width: 200rpx;
|
||||
height: 64rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.skeleton-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
padding: 32rpx;
|
||||
background: rgba(24, 24, 27, 0.6);
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
.skeleton-avatar {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.skeleton-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 120rpx 0;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border: 4rpx solid rgba(20, 184, 166, 0.2);
|
||||
border-top-color: #14b8a6;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
.skeleton-info .skeleton-line {
|
||||
height: 32rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
.skeleton-info .skeleton-line { width: 70%; }
|
||||
.skeleton-info .skeleton-line.short { width: 45%; }
|
||||
|
||||
.loading-text {
|
||||
margin-top: 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
@keyframes skeleton-shimmer {
|
||||
0% { background-position: 200% 0; }
|
||||
100% { background-position: -200% 0; }
|
||||
}
|
||||
|
||||
/* 产品 Hero 卡片 */
|
||||
|
||||
@@ -52,19 +52,6 @@
|
||||
<view class="banner-action"><text class="banner-action-text">开始阅读</text><view class="banner-arrow">→</view></view>
|
||||
</view>
|
||||
|
||||
<!-- 阅读进度(设计稿:最新更新→阅读进度→超级个体) -->
|
||||
<view class="progress-card" wx:if="{{isLoggedIn}}" bindtap="goToChapters">
|
||||
<view class="progress-header">
|
||||
<text class="progress-title">阅读进度</text>
|
||||
<text class="progress-count">已读 {{readCount}}/{{totalSections}}</text>
|
||||
</view>
|
||||
<view class="progress-bar-wrapper">
|
||||
<view class="progress-bar-bg">
|
||||
<view class="progress-bar-fill" style="width: {{readCount && totalSections ? (readCount / totalSections * 100) : 0}}%;"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 超级个体(横向滚动,已去掉「查看全部」;审核模式隐藏) -->
|
||||
<view class="section" wx:if="{{!auditMode}}">
|
||||
<view class="section-header">
|
||||
|
||||
@@ -4,9 +4,7 @@
|
||||
<!-- 自定义导航栏 -->
|
||||
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px;">
|
||||
<view class="nav-content">
|
||||
<view class="nav-settings" bindtap="openSettings">
|
||||
<text class="settings-icon">⚙️</text>
|
||||
</view>
|
||||
<view class="nav-left-placeholder"></view>
|
||||
<text class="nav-title">找伙伴</text>
|
||||
<view class="nav-right-placeholder"></view>
|
||||
</view>
|
||||
|
||||
@@ -27,15 +27,9 @@
|
||||
padding: 0 32rpx;
|
||||
}
|
||||
|
||||
.nav-settings {
|
||||
.nav-left-placeholder {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
background: #1c1c1e;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
@@ -51,10 +45,6 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.settings-icon {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.nav-placeholder {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -24,8 +24,26 @@
|
||||
|
||||
<!-- 阅读内容 -->
|
||||
<view class="read-content">
|
||||
<!-- 章节标题 -->
|
||||
<view class="chapter-header">
|
||||
<!-- 骨架屏:加载中时展示,模拟章节标题+正文布局 -->
|
||||
<view class="skeleton-wrap" wx:if="{{accessState === 'unknown' && loading}}">
|
||||
<view class="skeleton-header">
|
||||
<view class="skeleton-meta"></view>
|
||||
<view class="skeleton-title"></view>
|
||||
</view>
|
||||
<view class="skeleton-lines">
|
||||
<view class="skeleton skeleton-1"></view>
|
||||
<view class="skeleton skeleton-2"></view>
|
||||
<view class="skeleton skeleton-3"></view>
|
||||
<view class="skeleton skeleton-4"></view>
|
||||
<view class="skeleton skeleton-5"></view>
|
||||
<view class="skeleton skeleton-6"></view>
|
||||
<view class="skeleton skeleton-7"></view>
|
||||
<view class="skeleton skeleton-8"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 章节标题(加载完成后) -->
|
||||
<view class="chapter-header" wx:elif="{{!loading}}">
|
||||
<view class="chapter-meta">
|
||||
<text class="chapter-id">{{section.id}}</text>
|
||||
<text class="tag tag-free" wx:if="{{section.isFree}}">免费</text>
|
||||
@@ -33,15 +51,6 @@
|
||||
<text class="chapter-title" user-select>{{section.title}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading-state" wx:if="{{accessState === 'unknown' && loading}}">
|
||||
<view class="skeleton skeleton-1"></view>
|
||||
<view class="skeleton skeleton-2"></view>
|
||||
<view class="skeleton skeleton-3"></view>
|
||||
<view class="skeleton skeleton-4"></view>
|
||||
<view class="skeleton skeleton-5"></view>
|
||||
</view>
|
||||
|
||||
<!-- 完整内容 - 免费或已购买(支持 @ mention / #linkTag / 图片) -->
|
||||
<view class="article" wx:if="{{accessState === 'free' || accessState === 'unlocked_purchased'}}">
|
||||
<view class="paragraph" wx:for="{{contentSegments}}" wx:key="index">
|
||||
|
||||
@@ -144,8 +144,35 @@
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* ===== 加载状态 ===== */
|
||||
.loading-state {
|
||||
/* ===== 骨架屏 ===== */
|
||||
.skeleton-wrap {
|
||||
padding-top: 24rpx;
|
||||
}
|
||||
|
||||
.skeleton-header {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.skeleton-meta {
|
||||
width: 120rpx;
|
||||
height: 48rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-loading 1.5s ease-in-out infinite;
|
||||
border-radius: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.skeleton-title {
|
||||
width: 85%;
|
||||
height: 52rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-loading 1.5s ease-in-out infinite;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.skeleton-lines {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 32rpx;
|
||||
@@ -164,6 +191,9 @@
|
||||
.skeleton-3 { width: 65%; }
|
||||
.skeleton-4 { width: 85%; }
|
||||
.skeleton-5 { width: 70%; }
|
||||
.skeleton-6 { width: 80%; }
|
||||
.skeleton-7 { width: 60%; }
|
||||
.skeleton-8 { width: 88%; }
|
||||
|
||||
@keyframes skeleton-loading {
|
||||
0% { background-position: 200% 0; }
|
||||
@@ -439,21 +469,24 @@
|
||||
|
||||
.action-row-inline {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.action-btn-inline {
|
||||
flex: 1;
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
padding: 24rpx 16rpx;
|
||||
padding: 24rpx 12rpx;
|
||||
border-radius: 16rpx;
|
||||
border: none;
|
||||
background: transparent;
|
||||
line-height: normal;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.action-btn-inline::after {
|
||||
@@ -473,12 +506,18 @@
|
||||
|
||||
.action-icon-small {
|
||||
font-size: 28rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.action-text-small {
|
||||
font-size: 24rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.share-tip-inline {
|
||||
|
||||
@@ -65,10 +65,15 @@
|
||||
|
||||
<!-- 搜索结果 -->
|
||||
<view class="results-section" wx:if="{{searched}}">
|
||||
<!-- 加载中 -->
|
||||
<view class="loading-wrap" wx:if="{{loading}}">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">搜索中...</text>
|
||||
<!-- 搜索结果骨架屏 -->
|
||||
<view class="skeleton-results" wx:if="{{loading}}">
|
||||
<view class="skeleton-result-item" wx:for="{{[1,2,3,4,5]}}" wx:key="*this">
|
||||
<view class="skeleton-result-rank"></view>
|
||||
<view class="skeleton-result-content">
|
||||
<view class="skeleton-result-title"></view>
|
||||
<view class="skeleton-result-meta"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 结果列表 -->
|
||||
|
||||
@@ -284,30 +284,57 @@
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading-wrap {
|
||||
/* 搜索结果骨架屏 */
|
||||
.skeleton-results {
|
||||
padding: 24rpx 0;
|
||||
}
|
||||
|
||||
.skeleton-result-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
padding: 28rpx 0;
|
||||
border-bottom: 1rpx solid rgba(255,255,255,0.06);
|
||||
}
|
||||
|
||||
.skeleton-result-rank {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
border-radius: 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.skeleton-result-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 100rpx 0;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border: 4rpx solid rgba(0, 206, 209, 0.3);
|
||||
border-top-color: #00CED1;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
.skeleton-result-title {
|
||||
width: 85%;
|
||||
height: 36rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
.skeleton-result-meta {
|
||||
width: 50%;
|
||||
height: 28rpx;
|
||||
background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
margin-top: 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: rgba(255,255,255,0.5);
|
||||
@keyframes skeleton-shimmer {
|
||||
0% { background-position: 200% 0; }
|
||||
100% { background-position: -200% 0; }
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
|
||||
Reference in New Issue
Block a user