更新开发文档,强调接口路径必须按使用方区分,禁止通用路径混用。新增小程序分享功能,统一使用推荐码,确保用户体验一致性。
This commit is contained in:
223
miniprogram2/pages/index/index.js
Normal file
223
miniprogram2/pages/index/index.js
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* Soul创业派对 - 首页
|
||||
* 开发: 卡若
|
||||
* 技术支持: 存客宝
|
||||
*/
|
||||
|
||||
console.log('[Index] ===== 首页文件开始加载 =====')
|
||||
|
||||
const app = getApp()
|
||||
const PART_NUMBERS = { 'part-1': '一', 'part-2': '二', 'part-3': '三', 'part-4': '四', 'part-5': '五' }
|
||||
|
||||
Page({
|
||||
data: {
|
||||
// 系统信息
|
||||
statusBarHeight: 44,
|
||||
navBarHeight: 88,
|
||||
|
||||
// 用户信息
|
||||
isLoggedIn: false,
|
||||
hasFullBook: false,
|
||||
readCount: 0,
|
||||
|
||||
totalSections: 62,
|
||||
bookData: [],
|
||||
featuredSections: [],
|
||||
latestSection: null,
|
||||
latestLabel: '最新更新',
|
||||
partsList: [],
|
||||
prefaceMid: 0,
|
||||
loading: true
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
console.log('[Index] ===== onLoad 触发 =====')
|
||||
|
||||
// 获取系统信息
|
||||
this.setData({
|
||||
statusBarHeight: app.globalData.statusBarHeight,
|
||||
navBarHeight: app.globalData.navBarHeight
|
||||
})
|
||||
|
||||
// 处理分享参数与扫码 scene(推荐码绑定)
|
||||
if (options && (options.ref || options.scene)) {
|
||||
app.handleReferralCode(options)
|
||||
}
|
||||
|
||||
// 初始化数据
|
||||
this.initData()
|
||||
},
|
||||
|
||||
onShow() {
|
||||
console.log('[Index] onShow 触发')
|
||||
|
||||
// 设置TabBar选中状态
|
||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
||||
const tabBar = this.getTabBar()
|
||||
console.log('[Index] TabBar 组件:', tabBar ? '已找到' : '未找到')
|
||||
|
||||
// 主动触发配置加载
|
||||
if (tabBar && tabBar.loadFeatureConfig) {
|
||||
console.log('[Index] 主动调用 TabBar.loadFeatureConfig()')
|
||||
tabBar.loadFeatureConfig()
|
||||
}
|
||||
|
||||
// 更新选中状态
|
||||
if (tabBar && tabBar.updateSelected) {
|
||||
tabBar.updateSelected()
|
||||
} else if (tabBar) {
|
||||
tabBar.setData({ selected: 0 })
|
||||
}
|
||||
} else {
|
||||
console.log('[Index] TabBar 组件未找到或 getTabBar 方法不存在')
|
||||
}
|
||||
|
||||
// 更新用户状态
|
||||
this.updateUserStatus()
|
||||
},
|
||||
|
||||
// 初始化数据
|
||||
async initData() {
|
||||
this.setData({ loading: true })
|
||||
|
||||
try {
|
||||
await this.loadBookData()
|
||||
} catch (e) {
|
||||
console.error('初始化失败:', e)
|
||||
} finally {
|
||||
this.setData({ loading: false })
|
||||
}
|
||||
},
|
||||
|
||||
async loadBookData() {
|
||||
try {
|
||||
const [chaptersRes, hotRes] = await Promise.all([
|
||||
app.request('/api/miniprogram/book/all-chapters'),
|
||||
app.request('/api/miniprogram/book/hot')
|
||||
])
|
||||
const list = chaptersRes?.data || []
|
||||
const hotList = hotRes?.data || []
|
||||
app.globalData.bookData = list
|
||||
|
||||
const toSection = (ch) => ({
|
||||
id: ch.id,
|
||||
mid: ch.mid || 0,
|
||||
title: ch.sectionTitle || ch.chapterTitle || ch.id,
|
||||
part: ch.partTitle || ''
|
||||
})
|
||||
|
||||
let featuredSections = []
|
||||
if (hotList.length >= 3) {
|
||||
const freeCh = list.find(c => c.isFree || c.id === '1.1' || c.id === 'preface')
|
||||
const picks = []
|
||||
if (freeCh) picks.push({ ...toSection(freeCh), tag: '免费', tagClass: 'tag-free' })
|
||||
hotList.slice(0, 3 - picks.length).forEach((ch, i) => {
|
||||
if (!picks.find(p => p.id === ch.id)) {
|
||||
picks.push({ ...toSection(ch), tag: i === 0 ? '热门' : '推荐', tagClass: i === 0 ? 'tag-pink' : 'tag-purple' })
|
||||
}
|
||||
})
|
||||
featuredSections = picks.slice(0, 3)
|
||||
}
|
||||
if (featuredSections.length < 3 && list.length > 0) {
|
||||
const fallback = list.filter(c => c.id && !['preface', 'epilogue'].includes(c.id)).slice(0, 3)
|
||||
featuredSections = fallback.map((ch, i) => ({
|
||||
...toSection(ch),
|
||||
tag: ch.isFree ? '免费' : (i === 0 ? '热门' : '推荐'),
|
||||
tagClass: ch.isFree ? 'tag-free' : (i === 0 ? 'tag-pink' : 'tag-purple')
|
||||
}))
|
||||
}
|
||||
|
||||
const partMap = {}
|
||||
list.forEach(ch => {
|
||||
if (!ch.partId || ch.id === 'preface' || ch.id === 'epilogue' || (ch.id || '').startsWith('appendix')) return
|
||||
if (!partMap[ch.partId]) {
|
||||
partMap[ch.partId] = { id: ch.partId, number: PART_NUMBERS[ch.partId] || ch.partId, title: ch.partTitle || ch.partId, subtitle: ch.chapterTitle || '' }
|
||||
}
|
||||
})
|
||||
const partsList = Object.values(partMap).sort((a, b) => (a.id || '').localeCompare(b.id || ''))
|
||||
|
||||
const paidCandidates = list.filter(c => c.id && !['preface', 'epilogue'].includes(c.id) && !(c.id || '').startsWith('appendix') && c.partId)
|
||||
const { hasFullBook, purchasedSections } = app.globalData
|
||||
let candidates = paidCandidates
|
||||
if (!hasFullBook && purchasedSections?.length) {
|
||||
const unpurchased = paidCandidates.filter(c => !purchasedSections.includes(c.id))
|
||||
if (unpurchased.length > 0) candidates = unpurchased
|
||||
}
|
||||
const userId = app.globalData.userInfo?.id || wx.getStorageSync('userId') || 'guest'
|
||||
const today = new Date().toISOString().split('T')[0]
|
||||
const seed = (userId + today).split('').reduce((a, b) => a + b.charCodeAt(0), 0)
|
||||
const selectedCh = candidates[seed % Math.max(candidates.length, 1)]
|
||||
const latestSection = selectedCh ? { ...toSection(selectedCh), mid: selectedCh.mid || 0 } : null
|
||||
const latestLabel = candidates.length === paidCandidates.length ? '推荐阅读' : '为你推荐'
|
||||
|
||||
const prefaceCh = list.find(c => c.id === 'preface')
|
||||
const prefaceMid = prefaceCh?.mid || 0
|
||||
|
||||
this.setData({
|
||||
bookData: list,
|
||||
totalSections: list.length || 62,
|
||||
featuredSections,
|
||||
partsList,
|
||||
latestSection,
|
||||
latestLabel,
|
||||
prefaceMid
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('加载书籍数据失败:', e)
|
||||
this.setData({ featuredSections: [], partsList: [], latestSection: null })
|
||||
}
|
||||
},
|
||||
|
||||
// 更新用户状态(已读数 = 用户实际打开过的章节数,仅统计有权限阅读的)
|
||||
updateUserStatus() {
|
||||
const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData
|
||||
const readCount = Math.min(app.getReadCount(), this.data.totalSections || 62)
|
||||
this.setData({
|
||||
isLoggedIn,
|
||||
hasFullBook,
|
||||
readCount
|
||||
})
|
||||
},
|
||||
|
||||
// 跳转到目录
|
||||
goToChapters() {
|
||||
wx.switchTab({ url: '/pages/chapters/chapters' })
|
||||
},
|
||||
|
||||
// 跳转到搜索页
|
||||
goToSearch() {
|
||||
wx.navigateTo({ url: '/pages/search/search' })
|
||||
},
|
||||
|
||||
goToRead(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
const mid = e.currentTarget.dataset.mid || app.getSectionMid(id)
|
||||
const q = mid ? `mid=${mid}` : `id=${id}`
|
||||
wx.navigateTo({ url: `/pages/read/read?${q}` })
|
||||
},
|
||||
|
||||
// 跳转到匹配页
|
||||
goToMatch() {
|
||||
wx.switchTab({ url: '/pages/match/match' })
|
||||
},
|
||||
|
||||
// 跳转到我的页面
|
||||
goToMy() {
|
||||
wx.switchTab({ url: '/pages/my/my' })
|
||||
},
|
||||
|
||||
// 下拉刷新
|
||||
async onPullDownRefresh() {
|
||||
await this.initData()
|
||||
this.updateUserStatus()
|
||||
wx.stopPullDownRefresh()
|
||||
},
|
||||
|
||||
onShareAppMessage() {
|
||||
const ref = app.getMyReferralCode()
|
||||
return {
|
||||
title: 'Soul创业派对 - 真实商业故事',
|
||||
path: ref ? `/pages/index/index?ref=${ref}` : '/pages/index/index'
|
||||
}
|
||||
}
|
||||
})
|
||||
6
miniprogram2/pages/index/index.json
Normal file
6
miniprogram2/pages/index/index.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"usingComponents": {},
|
||||
"enablePullDownRefresh": true,
|
||||
"backgroundTextStyle": "light",
|
||||
"backgroundColor": "#000000"
|
||||
}
|
||||
147
miniprogram2/pages/index/index.wxml
Normal file
147
miniprogram2/pages/index/index.wxml
Normal file
@@ -0,0 +1,147 @@
|
||||
<!--pages/index/index.wxml-->
|
||||
<!--Soul创业派对 - 首页 1:1还原Web版本-->
|
||||
<view class="page page-transition">
|
||||
<!-- 自定义导航栏占位 -->
|
||||
<view class="nav-placeholder" style="height: {{statusBarHeight + 44}}px;"></view>
|
||||
|
||||
<!-- 顶部区域 -->
|
||||
<view class="header">
|
||||
<view class="header-content">
|
||||
<view class="logo-section">
|
||||
<view class="logo-icon">
|
||||
<text class="logo-text">S</text>
|
||||
</view>
|
||||
<view class="logo-info">
|
||||
<view class="logo-title">
|
||||
<text class="text-white">Soul</text>
|
||||
<text class="brand-color">创业派对</text>
|
||||
</view>
|
||||
<text class="logo-subtitle">来自派对房的真实故事</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="header-right">
|
||||
<view class="chapter-badge">{{totalSections}}章</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<view class="search-bar" bindtap="goToSearch">
|
||||
<view class="search-icon">
|
||||
<view class="search-circle"></view>
|
||||
<view class="search-handle"></view>
|
||||
</view>
|
||||
<text class="search-placeholder">搜索章节标题或内容...</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<view class="main-content">
|
||||
<!-- Banner卡片 - 最新章节 -->
|
||||
<view wx:if="{{latestSection}}" class="banner-card" bindtap="goToRead" data-id="{{latestSection.id}}" data-mid="{{latestSection.mid}}">
|
||||
<view class="banner-glow"></view>
|
||||
<view class="banner-tag">最新更新</view>
|
||||
<view class="banner-title">{{latestSection.title}}</view>
|
||||
<view class="banner-part">{{latestSection.part}}</view>
|
||||
<view class="banner-action">
|
||||
<text class="banner-action-text">开始阅读</text>
|
||||
<view class="banner-arrow">→</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 阅读进度卡 -->
|
||||
<view class="progress-card card">
|
||||
<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: {{totalSections > 0 ? (readCount / totalSections) * 100 : 0}}%;"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="progress-stats">
|
||||
<view class="stat-item">
|
||||
<text class="stat-value brand-color">{{readCount}}</text>
|
||||
<text class="stat-label">已读</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-value">{{totalSections - readCount}}</text>
|
||||
<text class="stat-label">待读</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-value">5</text>
|
||||
<text class="stat-label">篇章</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-value">11</text>
|
||||
<text class="stat-label">章节</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 精选推荐 -->
|
||||
<view class="section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">精选推荐</text>
|
||||
<view class="section-more" bindtap="goToChapters">
|
||||
<text class="more-text">查看全部</text>
|
||||
<text class="more-arrow">→</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="featured-list">
|
||||
<view
|
||||
class="featured-item"
|
||||
wx:for="{{featuredSections}}"
|
||||
wx:key="id"
|
||||
bindtap="goToRead"
|
||||
data-id="{{item.id}}"
|
||||
data-mid="{{item.mid}}"
|
||||
>
|
||||
<view class="featured-content">
|
||||
<view class="featured-meta">
|
||||
<text class="featured-id brand-color">{{item.id}}</text>
|
||||
<text class="tag {{item.tagClass}}">{{item.tag}}</text>
|
||||
</view>
|
||||
<text class="featured-title">{{item.title}}</text>
|
||||
<text class="featured-part">{{item.part}}</text>
|
||||
</view>
|
||||
<view class="featured-arrow">→</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 内容概览 -->
|
||||
<view class="section">
|
||||
<text class="section-title">内容概览</text>
|
||||
<view class="parts-list">
|
||||
<view
|
||||
class="part-item"
|
||||
wx:for="{{partsList}}"
|
||||
wx:key="id"
|
||||
bindtap="goToChapters"
|
||||
>
|
||||
<view class="part-icon">
|
||||
<text class="part-number">{{item.number}}</text>
|
||||
</view>
|
||||
<view class="part-info">
|
||||
<text class="part-title">{{item.title}}</text>
|
||||
<text class="part-subtitle">{{item.subtitle}}</text>
|
||||
</view>
|
||||
<view class="part-arrow">→</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 序言入口 -->
|
||||
<view class="preface-card" bindtap="goToRead" data-id="preface" data-mid="{{prefaceMid}}">
|
||||
<view class="preface-content">
|
||||
<text class="preface-title">序言</text>
|
||||
<text class="preface-desc">为什么我每天早上6点在Soul开播?</text>
|
||||
</view>
|
||||
<view class="tag tag-free">免费</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部留白 -->
|
||||
<view class="bottom-space"></view>
|
||||
</view>
|
||||
504
miniprogram2/pages/index/index.wxss
Normal file
504
miniprogram2/pages/index/index.wxss
Normal file
@@ -0,0 +1,504 @@
|
||||
/**
|
||||
* Soul创业实验 - 首页样式
|
||||
* 1:1还原Web版本UI
|
||||
*/
|
||||
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: #000000;
|
||||
padding-bottom: 200rpx;
|
||||
}
|
||||
|
||||
/* ===== 导航栏占位 ===== */
|
||||
.nav-placeholder {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ===== 顶部区域 ===== */
|
||||
.header {
|
||||
padding: 0 32rpx 32rpx;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 32rpx;
|
||||
padding-top: 24rpx;
|
||||
}
|
||||
|
||||
.logo-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 20rpx;
|
||||
background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 206, 209, 0.3);
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
color: #ffffff;
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.logo-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.logo-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.text-white {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.brand-color {
|
||||
color: #00CED1;
|
||||
}
|
||||
|
||||
.logo-subtitle {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.chapter-badge {
|
||||
font-size: 22rpx;
|
||||
color: #00CED1;
|
||||
background: rgba(0, 206, 209, 0.1);
|
||||
padding: 8rpx 16rpx;
|
||||
border-radius: 32rpx;
|
||||
}
|
||||
|
||||
/* ===== 搜索栏 ===== */
|
||||
.search-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
padding: 24rpx 32rpx;
|
||||
background: #1c1c1e;
|
||||
border-radius: 24rpx;
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
position: relative;
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
|
||||
.search-circle {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border: 4rpx solid rgba(255, 255, 255, 0.4);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.search-handle {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 12rpx;
|
||||
height: 4rpx;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
transform: rotate(45deg);
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
|
||||
.search-placeholder {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
/* ===== 主内容区 ===== */
|
||||
.main-content {
|
||||
padding: 0 32rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ===== Banner卡片 ===== */
|
||||
.banner-card {
|
||||
position: relative;
|
||||
padding: 40rpx;
|
||||
border-radius: 32rpx;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(135deg, #0d3331 0%, #1a1a2e 50%, #16213e 100%);
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.banner-glow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 256rpx;
|
||||
height: 256rpx;
|
||||
background: #00CED1;
|
||||
border-radius: 50%;
|
||||
filter: blur(120rpx);
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.banner-tag {
|
||||
display: inline-block;
|
||||
padding: 8rpx 16rpx;
|
||||
background: #00CED1;
|
||||
color: #000000;
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.banner-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
margin-bottom: 16rpx;
|
||||
padding-right: 64rpx;
|
||||
}
|
||||
|
||||
.banner-part {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.banner-action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.banner-action-text {
|
||||
font-size: 28rpx;
|
||||
color: #00CED1;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.banner-arrow {
|
||||
color: #00CED1;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
/* ===== 通用卡片 ===== */
|
||||
.card {
|
||||
background: #1c1c1e;
|
||||
border-radius: 32rpx;
|
||||
padding: 32rpx;
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.05);
|
||||
margin: 0 0 24rpx 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* ===== 阅读进度卡 ===== */
|
||||
.progress-card {
|
||||
width: 100%;
|
||||
background: #1c1c1e;
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx;
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.05);
|
||||
margin: 0 0 24rpx 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.progress-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.progress-title {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.progress-count {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.progress-bar-wrapper {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.progress-bar-bg {
|
||||
width: 100%;
|
||||
height: 16rpx;
|
||||
background: #2c2c2e;
|
||||
border-radius: 8rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-bar-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #00CED1 0%, #20B2AA 100%);
|
||||
border-radius: 8rpx;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.progress-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
/* ===== 区块标题 ===== */
|
||||
.section {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.section-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.more-text {
|
||||
font-size: 24rpx;
|
||||
color: #00CED1;
|
||||
}
|
||||
|
||||
.more-arrow {
|
||||
font-size: 24rpx;
|
||||
color: #00CED1;
|
||||
}
|
||||
|
||||
/* ===== 精选推荐列表 ===== */
|
||||
.featured-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.featured-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: 32rpx;
|
||||
background: #1c1c1e;
|
||||
border-radius: 24rpx;
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.featured-item:active {
|
||||
transform: scale(0.98);
|
||||
background: #2c2c2e;
|
||||
}
|
||||
|
||||
.featured-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.featured-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.featured-id {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22rpx;
|
||||
padding: 6rpx 16rpx;
|
||||
min-width: 80rpx;
|
||||
border-radius: 8rpx;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tag-free {
|
||||
background: rgba(0, 206, 209, 0.1);
|
||||
color: #00CED1;
|
||||
}
|
||||
|
||||
.tag-pink {
|
||||
background: rgba(233, 30, 99, 0.1);
|
||||
color: #E91E63;
|
||||
}
|
||||
|
||||
.tag-purple {
|
||||
background: rgba(123, 97, 255, 0.1);
|
||||
color: #7B61FF;
|
||||
}
|
||||
|
||||
.featured-title {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.featured-part {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.featured-arrow {
|
||||
font-size: 32rpx;
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
/* ===== 内容概览列表 ===== */
|
||||
.parts-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.part-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24rpx;
|
||||
padding: 32rpx;
|
||||
background: #1c1c1e;
|
||||
border-radius: 24rpx;
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.part-item:active {
|
||||
transform: scale(0.98);
|
||||
background: #2c2c2e;
|
||||
}
|
||||
|
||||
.part-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 16rpx;
|
||||
background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, rgba(32, 178, 170, 0.1) 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.part-number {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #00CED1;
|
||||
}
|
||||
|
||||
.part-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.part-title {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.part-subtitle {
|
||||
font-size: 22rpx;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.part-arrow {
|
||||
font-size: 32rpx;
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ===== 序言入口 ===== */
|
||||
.preface-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 32rpx;
|
||||
border-radius: 24rpx;
|
||||
background: linear-gradient(90deg, rgba(0, 206, 209, 0.1) 0%, transparent 100%);
|
||||
border: 2rpx solid rgba(0, 206, 209, 0.2);
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.preface-card:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.preface-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.preface-title {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.preface-desc {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
/* ===== 底部留白 ===== */
|
||||
.bottom-space {
|
||||
height: 40rpx;
|
||||
}
|
||||
Reference in New Issue
Block a user