diff --git a/.cursorrules b/.cursorrules
deleted file mode 100644
index c619cba0..00000000
--- a/.cursorrules
+++ /dev/null
@@ -1,83 +0,0 @@
-# v0 Code Generation Rules - Claude Opus
-
-## Model Selection
-- Production components: claude-opus (默认)
-- Rapid prototyping: v0-1.5-turbo
-- Code review: claude-3.5-sonnet
-
-## Code Standards
-- Framework: Next.js App Router
-- Styling: Tailwind CSS v4
-- Components: shadcn/ui
-- No placeholders
-- Production-ready only
-
-## Design System
-- Use design tokens from globals.css
-- Follow color system (3-5 colors max)
-- Max 2 font families
-- Mobile-first approach
-- 所有页面组件保持一致性
-- 使用现有导航系统
-- 遵循毛玻璃设计风格
-- 精简文字,增加流程图
-
-## v0 Usage
-- 使用 @v0 前缀调用v0生成代码
-- 默认使用 claude-opus 模型
-- 生成前先说明需求,确保理解正确
-
----
-
-## 自动部署规则
-
-### 服务器信息(小型宝塔)
-- **服务器IP**: 42.194.232.22
-- **用户**: root
-- **密码**: Zhiqun1984
-- **项目路径**: /www/wwwroot/soul
-- **PM2进程名**: soul
-- **端口**: 3006
-- **宝塔面板**: https://42.194.232.22:9988/ckbpanel (ckb/zhiqun1984)
-
-### GitHub仓库
-- **地址**: https://github.com/fnvtk/Mycontent.git
-- **分支**: soul-content
-
-### 小程序
-- **AppID**: wxb8bbb2b10dec74aa
-- **项目路径**: ./miniprogram
-
-### 部署流程(每次提交后自动执行)
-1. **提交代码到Git**
- ```bash
- git add -A
- git commit -m "描述"
- git push origin soul-content
- ```
-
-2. **部署到小型宝塔服务器**
- ```bash
- # 在项目根目录执行(本地打包 + SSH 上传 + 宝塔 API 重启)
- pip install -r requirements-deploy.txt
- python scripts/devlop.py
- ```
- - 详见 `DEPLOYMENT.md`、`开发文档/8、部署/当前项目部署到线上.md`
-
-4. **上传小程序**
- ```bash
- # 项目根目录一键上传(将 miniprogram/ 代码完整上传到微信公众平台)
- python scripts/autosysc-weixin.py
- ```
- - 需先在 miniprogram/ 下放置 private.key(公众号后台「开发设置」→ 小程序代码上传密钥)。详见 `开发文档/8、部署/当前项目部署到线上.md`。
-
-5. **打开微信公众平台**
- ```bash
- open "https://mp.weixin.qq.com/"
- ```
- 在「版本管理」设为体验版测试
-
-### 注意事项
-- 小程序版本号:未发布前保持 1.14,正式发布后递增
-- 后台部署后需等待约30秒生效
-- 数据库:腾讯云MySQL,读取优先级:数据库 > 本地文件
diff --git a/.env.port.example b/.env.port.example
new file mode 100644
index 00000000..3e5c61ba
--- /dev/null
+++ b/.env.port.example
@@ -0,0 +1,32 @@
+# 端口配置示例
+# 复制此文件为 .env 并根据实际情况修改
+
+# ========================================
+# 应用端口配置(避免多项目端口冲突)
+# ========================================
+
+# 方式1: 本地开发启动(pnpm start)
+# 在终端中设置:
+# Windows PowerShell: $env:PORT=3006; pnpm start
+# Windows CMD: set PORT=3006 && pnpm start
+# Linux/Mac: PORT=3006 pnpm start
+
+# 方式2: Docker Compose 部署
+# 设置 APP_PORT 变量,容器内外端口都使用此值
+APP_PORT=3006
+
+# 方式3: Docker 直接运行
+# docker run -e PORT=3007 -p 3007:3007 soul-book
+
+# ========================================
+# 多项目端口规划建议
+# ========================================
+# soul-book: 3006
+# other-project: 3007
+# api-service: 3008
+# ...
+
+# 注意:
+# 1. 修改端口后,需要同步更新支付回调地址等配置
+# 2. 部署到宝塔面板时,deploy_soul.py 会使用配置的端口
+# 3. 确保防火墙和反向代理配置正确
diff --git a/.gitignore b/.gitignore
index cd6c6f9d..ba64f304 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,9 @@ node_modules/
.trae/
*.log
node_modules
+miniprogram
+my-app
+newpp
# 部署配置(含服务器信息,勿提交)
deploy_config.json
diff --git a/Dockerfile b/Dockerfile
index 6017696a..726d74fa 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -54,9 +54,10 @@ COPY --from=builder /app/.next/static ./.next/static
USER nextjs
-EXPOSE 3000
+# 端口由环境变量指定,不设默认值避免冲突
+# 部署时通过 docker run -e PORT=xxxx 或 docker-compose 设置
+EXPOSE ${PORT:-3006}
-ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
CMD ["node", "server.js"]
diff --git a/README-Phase3.md b/README-Phase3.md
new file mode 100644
index 00000000..59b6ee02
--- /dev/null
+++ b/README-Phase3.md
@@ -0,0 +1,96 @@
+# Phase 3 完成总结
+
+## 概述
+
+Phase 3 成功将 Next.js 的"我的"页及所有子页完整迁移到 Kbone 小程序,并实现了 Zustand 状态管理的跨端适配。
+
+---
+
+## 完成的核心功能
+
+### 1. 状态管理(Zustand + Storage 适配)
+
+创建了 `newpp/src/store/index.js`,实现:
+
+- **用户状态**:user、isLoggedIn、logout、setUser、updateUser
+- **购买逻辑**:hasPurchased、addPurchase、purchaseFullBook
+- **持久化**:用 `adapters/storage.js` 适配小程序 wx.storage 和 Web localStorage
+
+### 2. 我的页面(登录态 + 统计 + 菜单)
+
+- 未登录:登录提示、统计占位
+- 已登录:用户卡片、收益卡片、Tab 切换(概览/我的足迹)、菜单(订单、推广、关于、设置)
+
+### 3. 推广页(邀请码 + 收益)
+
+- 收益概览:待领收益、累计收益、已提现
+- 邀请码展示与复制
+- 推广数据与规则说明
+
+### 4. 设置页
+
+- 账号信息展示
+- 通用设置
+- 退出登录
+
+### 5. 购买记录页
+
+- 订单列表(暂无数据占位)
+
+### 6. 关于页
+
+- 项目介绍、数据统计、联系方式
+
+---
+
+## 文件清单
+
+**页面组件**:
+- `src/pages/MyPage.jsx`
+- `src/pages/ReferralPage.jsx`
+- `src/pages/SettingsPage.jsx`
+- `src/pages/PurchasesPage.jsx`
+- `src/pages/AboutPage.jsx`
+
+**入口文件**:
+- `src/my.jsx`
+- `src/referral.jsx`
+- `src/settings.jsx`
+- `src/purchases.jsx`
+- `src/about.jsx`
+
+**状态管理**:
+- `src/store/index.js`
+
+**配置更新**:
+- `build/webpack.mp.config.js`(新增 5 个入口)
+- `build/miniprogram.config.js`(router.other 新增 5 个路由)
+
+---
+
+## 测试与验收
+
+1. **构建**:`cd newpp && npm run build:mp`
+2. **合并**:`node scripts/merge-kbone-to-miniprogram.js`
+3. **测试路径**:
+ - 首页 → 底部"我的" → 查看用户卡片 + 统计
+ - 我的 → 推广中心 → 查看邀请码 + 收益
+ - 我的 → 设置 → 查看账号信息
+ - 我的 → 我的订单 → 查看空态
+ - 我的 → 关于我们 → 查看项目介绍
+
+---
+
+## 当前进度
+
+- ✅ **Phase 1**:搭架子(适配层、构建、首页/目录/阅读占位)
+- ✅ **Phase 2**:核心页(阅读页接口、ChapterContent、完整目录)
+- ✅ **Phase 3**:我的与子页(Zustand、我的、推广、设置、购买记录、关于)
+- ⏳ **Phase 4**:找伙伴与其余(match、search、底部 tabBar、安全区)
+- ⏳ **Phase 5**:收尾(全量自检、样式对齐、发布流程)
+
+---
+
+## 下一步
+
+进入 Phase 4,迁移找伙伴、搜索,并实现底部 tabBar 与安全区适配。
diff --git a/_tmp_34228_f1906e0fb94e68c4400403820df714df b/_tmp_34228_f1906e0fb94e68c4400403820df714df
new file mode 100644
index 00000000..e69de29b
diff --git a/app/layout.tsx b/app/layout.tsx
index d8ae77ac..28d8a5d9 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -41,7 +41,7 @@ export default function RootLayout({
{children}
-
+ {process.env.NODE_ENV === 'production' && }
)
diff --git a/app/match/page.tsx b/app/match/page.tsx
index 08ada9af..bf72d24d 100644
--- a/app/match/page.tsx
+++ b/app/match/page.tsx
@@ -2,9 +2,9 @@
import { useState, useEffect } from "react"
import { motion, AnimatePresence } from "framer-motion"
-import { Users, X, CheckCircle, Loader2, Lock, Zap, Gift } from "lucide-react"
-import { Home, List, User } from "lucide-react"
+import { Users, X, CheckCircle, Loader2, Lock, Zap } from "lucide-react"
import { useRouter } from "next/navigation"
+import { BottomNav } from "@/components/bottom-nav"
import { useStore } from "@/lib/store"
interface MatchUser {
@@ -122,8 +122,7 @@ export default function MatchPage() {
setTodayMatchCount(getTodayMatchCount())
}, [user])
- if (!mounted) return null // 彻底解决 Hydration 错误
-
+ // 处理函数定义(必须在所有 hooks 之后)
const handleJoinClick = (typeId: string) => {
setJoinType(typeId)
setShowJoinModal(true)
@@ -333,6 +332,9 @@ export default function MatchPage() {
const currentMatchLabel = currentType?.matchLabel || "创业伙伴"
const joinTypeLabel = matchTypes.find((t) => t.id === joinType)?.matchLabel || ""
+ // 等待挂载完成(必须在所有 hooks 和函数定义之后)
+ if (!mounted) return null
+
return (
@@ -843,31 +845,7 @@ export default function MatchPage() {
)}
-
+
)
}
diff --git a/app/my/page.tsx b/app/my/page.tsx
index a478bcf0..b9d40f39 100644
--- a/app/my/page.tsx
+++ b/app/my/page.tsx
@@ -14,6 +14,7 @@ export default function MyPage() {
const [showAuthModal, setShowAuthModal] = useState(false)
const [mounted, setMounted] = useState(false)
const [activeTab, setActiveTab] = useState<"overview" | "footprint">("overview")
+ const [matchEnabled, setMatchEnabled] = useState(false) // 匹配功能是否启用
// 绑定弹窗状态
const [showBindModal, setShowBindModal] = useState(false)
@@ -22,21 +23,29 @@ export default function MyPage() {
const [isBinding, setIsBinding] = useState(false)
const [bindError, setBindError] = useState("")
- useEffect(() => {
- setMounted(true)
- }, [])
-
- if (!mounted) {
- return (
-
- )
- }
-
+ // 计算数据(必须在所有 hooks 之后)
const totalSections = getTotalSectionCount()
const purchasedCount = user?.hasFullBook ? totalSections : user?.purchasedSections?.length || 0
+ useEffect(() => {
+ setMounted(true)
+
+ // 加载功能配置
+ const loadConfig = async () => {
+ try {
+ const res = await fetch('/api/db/config')
+ const data = await res.json()
+ if (data.features) {
+ setMatchEnabled(data.features.matchEnabled === true)
+ }
+ } catch (e) {
+ console.log('Load feature config error:', e)
+ setMatchEnabled(false)
+ }
+ }
+ loadConfig()
+ }, [])
+
// 绑定账号
const handleBind = async () => {
if (!bindValue.trim()) {
@@ -93,6 +102,15 @@ export default function MyPage() {
setShowBindModal(true)
}
+ // 等待挂载完成
+ if (!mounted) {
+ return (
+
+ )
+ }
+
// 未登录状态
if (!isLoggedIn) {
return (
@@ -435,23 +453,25 @@ export default function MyPage() {
)}
- {/* 匹配记录 */}
-
-
-
- 匹配记录
-
-
-
-
暂无匹配记录
-
+ {/* 匹配记录 - 根据配置显示 */}
+ {matchEnabled && (
+
+
+
+ 匹配记录
+
+
+
+
暂无匹配记录
+
+
-
+ )}
>
)}
diff --git a/app/page.tsx b/app/page.tsx
index f1c27421..48c4ced8 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -19,14 +19,11 @@ export default function HomePage() {
const [mounted, setMounted] = useState(false)
const [searchOpen, setSearchOpen] = useState(false)
+ // 计算数据(必须在所有 hooks 之后)
const totalSections = getTotalSectionCount()
const hasFullBook = user?.hasFullBook || false
const purchasedCount = hasFullBook ? totalSections : user?.purchasedSections?.length || 0
- useEffect(() => {
- setMounted(true)
- }, [])
-
// 推荐章节
const featuredSections = [
{ id: "1.1", title: "荷包:电动车出租的被动收入模式", tag: "免费", part: "真实的人" },
@@ -41,6 +38,10 @@ export default function HomePage() {
part: "真实的赚钱",
}
+ useEffect(() => {
+ setMounted(true)
+ }, [])
+
if (!mounted) {
return null
}
diff --git a/components/bottom-nav.tsx b/components/bottom-nav.tsx
index 826078d1..7631116e 100644
--- a/components/bottom-nav.tsx
+++ b/components/bottom-nav.tsx
@@ -10,17 +10,7 @@ export function BottomNav() {
const [matchEnabled, setMatchEnabled] = useState(false) // 默认隐藏,等配置加载后再显示
const [configLoaded, setConfigLoaded] = useState(false) // 配置是否已加载
- // 在文档页面、管理后台、阅读页面和关于页面不显示底部导航
- if (
- pathname.startsWith("/documentation") ||
- pathname.startsWith("/admin") ||
- pathname.startsWith("/read") ||
- pathname.startsWith("/about")
- ) {
- return null
- }
-
- // 加载功能配置
+ // 加载功能配置(必须在所有条件判断之前)
useEffect(() => {
const loadConfig = async () => {
try {
@@ -41,6 +31,16 @@ export function BottomNav() {
loadConfig()
}, [])
+ // 在文档页面、管理后台、阅读页面和关于页面不显示底部导航(必须在所有 hooks 之后)
+ if (
+ pathname.startsWith("/documentation") ||
+ pathname.startsWith("/admin") ||
+ pathname.startsWith("/read") ||
+ pathname.startsWith("/about")
+ ) {
+ return null
+ }
+
const navItems = [
{ href: "/", icon: Home, label: "首页" },
{ href: "/chapters", icon: List, label: "目录" },
diff --git a/docker-compose.yml b/docker-compose.yml
index 6749ee09..7a8efb56 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -8,31 +8,32 @@ services:
container_name: soul_book_app
restart: always
ports:
- - "3000:3000"
+ - "${APP_PORT:-3006}:${APP_PORT:-3006}"
environment:
- NODE_ENV=production
- NEXT_TELEMETRY_DISABLED=1
+ - PORT=${APP_PORT:-3006}
# 支付宝配置
- ALIPAY_PARTNER_ID=${ALIPAY_PARTNER_ID:-2088511801157159}
- ALIPAY_KEY=${ALIPAY_KEY:-lz6ey1h3kl9zqkgtjz3avb5gk37wzbrp}
- ALIPAY_APP_ID=${ALIPAY_APP_ID:-wx432c93e275548671}
- - ALIPAY_RETURN_URL=${ALIPAY_RETURN_URL:-http://192.168.2.201:3000/payment/success}
- - ALIPAY_NOTIFY_URL=${ALIPAY_NOTIFY_URL:-http://192.168.2.201:3000/api/payment/alipay/notify}
+ - ALIPAY_RETURN_URL=${ALIPAY_RETURN_URL:-http://192.168.2.201:${APP_PORT:-3006}/payment/success}
+ - ALIPAY_NOTIFY_URL=${ALIPAY_NOTIFY_URL:-http://192.168.2.201:${APP_PORT:-3006}/api/payment/alipay/notify}
# 微信支付配置
- WECHAT_APP_ID=${WECHAT_APP_ID:-wx432c93e275548671}
- WECHAT_APP_SECRET=${WECHAT_APP_SECRET:-25b7e7fdb7998e5107e242ebb6ddabd0}
- WECHAT_MCH_ID=${WECHAT_MCH_ID:-1318592501}
- WECHAT_API_KEY=${WECHAT_API_KEY:-wx3e31b068be59ddc131b068be59ddc2}
- - WECHAT_NOTIFY_URL=${WECHAT_NOTIFY_URL:-http://192.168.2.201:3000/api/payment/wechat/notify}
+ - WECHAT_NOTIFY_URL=${WECHAT_NOTIFY_URL:-http://192.168.2.201:${APP_PORT:-3006}/api/payment/wechat/notify}
# 基础配置
- - NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL:-http://192.168.2.201:3000}
+ - NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL:-http://192.168.2.201:${APP_PORT:-3006}}
volumes:
- ./book:/app/book:ro
- ./public:/app/public:ro
networks:
- nas-network
healthcheck:
- test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000"]
+ test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:${APP_PORT:-3006}"]
interval: 30s
timeout: 10s
retries: 3
diff --git a/lib/db.ts b/lib/db.ts
index 3f740964..6db6bd4b 100644
--- a/lib/db.ts
+++ b/lib/db.ts
@@ -1,31 +1,47 @@
/**
* 数据库连接配置
* 使用MySQL数据库存储用户、订单、推广关系等数据
+ * 优先从环境变量读取,便于本地/部署分离;未设置时使用默认值
*/
import mysql from 'mysql2/promise'
-// 腾讯云外网数据库配置
const DB_CONFIG = {
- host: '56b4c23f6853c.gz.cdb.myqcloud.com',
- port: 14413,
- user: 'cdb_outerroot',
- password: 'Zhiqun1984',
- database: 'soul_miniprogram',
+ host: process.env.MYSQL_HOST || '56b4c23f6853c.gz.cdb.myqcloud.com',
+ port: Number(process.env.MYSQL_PORT || '14413'),
+ user: process.env.MYSQL_USER || 'cdb_outerroot',
+ password: process.env.MYSQL_PASSWORD || 'Zhiqun1984',
+ database: process.env.MYSQL_DATABASE || 'soul_miniprogram',
charset: 'utf8mb4',
timezone: '+08:00',
- acquireTimeout: 60000,
- timeout: 60000,
+ connectTimeout: 10000, // 10 秒,连接不可达时快速失败,避免长时间挂起
+ acquireTimeout: 15000,
reconnect: true
}
+// 本地无数据库时可通过 SKIP_DB=1 跳过连接,接口将使用默认配置
+const SKIP_DB = process.env.SKIP_DB === '1' || process.env.SKIP_DB === 'true'
+
// 连接池
let pool: mysql.Pool | null = null
+// 连接类错误只打一次日志,避免刷屏
+let connectionErrorLogged = false
+
+function isConnectionError(err: unknown): boolean {
+ const code = (err as NodeJS.ErrnoException)?.code
+ return (
+ code === 'ETIMEDOUT' ||
+ code === 'ECONNREFUSED' ||
+ code === 'PROTOCOL_CONNECTION_LOST' ||
+ code === 'ENOTFOUND'
+ )
+}
/**
- * 获取数据库连接池
+ * 获取数据库连接池(SKIP_DB 时不创建)
*/
-export function getPool() {
+export function getPool(): mysql.Pool | null {
+ if (SKIP_DB) return null
if (!pool) {
pool = mysql.createPool({
...DB_CONFIG,
@@ -41,12 +57,25 @@ export function getPool() {
* 执行SQL查询
*/
export async function query(sql: string, params?: any[]) {
+ const connection = getPool()
+ if (!connection) {
+ throw new Error('数据库未配置或已跳过 (SKIP_DB)')
+ }
try {
- const connection = getPool()
const [results] = await connection.execute(sql, params)
return results
} catch (error) {
- console.error('数据库查询错误:', error)
+ if (isConnectionError(error)) {
+ if (!connectionErrorLogged) {
+ connectionErrorLogged = true
+ console.warn(
+ '[DB] 数据库连接不可用,将使用本地默认配置。',
+ (error as Error).message
+ )
+ }
+ } else {
+ console.error('数据库查询错误:', error)
+ }
throw error
}
}
@@ -295,6 +324,7 @@ async function initDefaultConfig() {
/**
* 获取系统配置
+ * 连接不可达时返回 null,由上层使用本地默认配置,不重复打日志
*/
export async function getConfig(key: string) {
try {
@@ -308,7 +338,9 @@ export async function getConfig(key: string) {
}
return null
} catch (error) {
- console.error('获取配置失败:', error)
+ if (!isConnectionError(error)) {
+ console.error('获取配置失败:', error)
+ }
return null
}
}
diff --git a/miniprogram/.gitignore b/miniprogram/.gitignore
deleted file mode 100644
index 50bae59b..00000000
--- a/miniprogram/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-# 小程序上传密钥(敏感信息,请勿上传)
-private.key
-private.*.key
-
-# 预览二维码
-preview.jpg
-
-# 微信开发者工具生成的文件
-.DS_Store
-node_modules/
diff --git a/miniprogram/README.md b/miniprogram/README.md
deleted file mode 100644
index 2cc11dbf..00000000
--- a/miniprogram/README.md
+++ /dev/null
@@ -1,138 +0,0 @@
-# Soul创业实验 - 微信小程序
-
-> 一场SOUL的创业实验场 - 来自Soul派对房的真实商业故事
-
-## 📱 项目简介
-
-本项目是《一场SOUL的创业实验场》的微信小程序版本,完整还原了Web端的所有UI界面和功能。
-
-## 🎨 设计特点
-
-- **主题色**: Soul青色 (#00CED1)
-- **设计风格**: 深色主题 + 毛玻璃效果
-- **1:1还原**: 完全复刻Web端的UI设计
-
-## 📂 项目结构
-
-```
-miniprogram/
-├── app.js # 应用入口
-├── app.json # 应用配置
-├── app.wxss # 全局样式
-├── custom-tab-bar/ # 自定义TabBar组件
-│ ├── index.js
-│ ├── index.json
-│ ├── index.wxml
-│ └── index.wxss
-├── pages/
-│ ├── index/ # 首页
-│ ├── chapters/ # 目录页
-│ ├── match/ # 找伙伴页
-│ ├── my/ # 我的页面
-│ ├── read/ # 阅读页
-│ ├── about/ # 关于作者
-│ ├── referral/ # 推广中心
-│ ├── purchases/ # 订单页
-│ └── settings/ # 设置页
-├── utils/
-│ ├── util.js # 工具函数
-│ └── payment.js # 支付工具
-├── assets/
-│ └── icons/ # 图标资源
-├── project.config.json # 项目配置
-└── sitemap.json # 站点地图
-```
-
-## 🚀 功能列表
-
-### 核心功能
-- ✅ 首页 - 书籍展示、推荐章节、阅读进度
-- ✅ 目录 - 完整章节列表、篇章折叠展开
-- ✅ 找伙伴 - 匹配动画、匹配类型选择
-- ✅ 我的 - 个人信息、订单、推广中心
-- ✅ 阅读 - 付费墙、章节导航、分享功能
-
-### 特色功能
-- ✅ 自定义TabBar(中间突出的找伙伴按钮)
-- ✅ 阅读进度条
-- ✅ 匹配动画效果
-- ✅ 付费墙与购买流程
-- ✅ 分享海报功能
-- ✅ 推广佣金系统
-
-## 🛠 开发指南
-
-### 环境要求
-- 微信开发者工具 >= 1.06.2308310
-- 基础库版本 >= 3.3.4
-
-### 快速开始
-
-1. **下载微信开发者工具**
- - 前往 [微信开发者工具下载页面](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html)
-
-2. **导入项目**
- - 打开微信开发者工具
- - 选择"导入项目"
- - 项目目录选择 `miniprogram` 文件夹
- - AppID 使用: `wx432c93e275548671`
-
-3. **编译运行**
- - 点击"编译"按钮
- - 在模拟器中预览效果
-
-### 真机调试
-
-1. 点击工具栏的"预览"按钮
-2. 使用微信扫描二维码
-3. 在真机上测试所有功能
-
-## 📝 配置说明
-
-### API配置
-在 `app.js` 中修改 `globalData.baseUrl`:
-
-```javascript
-globalData: {
- baseUrl: 'https://soul.ckb.fit', // 你的API地址
- // ...
-}
-```
-
-### AppID配置
-在 `project.config.json` 中修改:
-
-```json
-{
- "appid": "你的小程序AppID"
-}
-```
-
-## 🎯 上线发布
-
-1. **准备工作**
- - 确保所有功能测试通过
- - 检查API接口是否正常
- - 确认支付功能已配置
-
-2. **上传代码**
- - 在开发者工具中点击"上传"
- - 填写版本号和项目备注
-
-3. **提交审核**
- - 登录[微信公众平台](https://mp.weixin.qq.com)
- - 进入"版本管理"
- - 提交审核
-
-4. **发布上线**
- - 审核通过后点击"发布"
-
-## 🔗 相关链接
-
-- **Web版本**: https://soul.ckb.fit
-- **作者微信**: 28533368
-- **技术支持**: 存客宝
-
-## 📄 版权信息
-
-© 2024 卡若. All rights reserved.
diff --git a/miniprogram/app.js b/miniprogram/app.js
index e0c3901b..a3dd71a6 100644
--- a/miniprogram/app.js
+++ b/miniprogram/app.js
@@ -1,36 +1,17 @@
/**
* Soul创业派对 - 小程序入口
- * 开发: 卡若
*/
App({
globalData: {
- // API基础地址 - 连接真实后端
baseUrl: 'https://soul.quwanzhi.com',
-
- // 小程序配置 - 真实AppID
appId: 'wxb8bbb2b10dec74aa',
-
- // 微信支付配置
- mchId: '1318592501', // 商户号
-
- // 用户信息
userInfo: null,
- openId: null, // 微信openId,支付必需
+ openId: null,
isLoggedIn: false,
-
- // 书籍数据
- bookData: null,
- totalSections: 62,
-
- // 购买记录
purchasedSections: [],
hasFullBook: false,
-
- // 推荐绑定
- pendingReferralCode: null, // 待绑定的推荐码
-
- // 主题配置
+ pendingReferralCode: null,
theme: {
brandColor: '#00CED1',
brandSecondary: '#20B2AA',
@@ -38,149 +19,88 @@ App({
bgColor: '#000000',
cardBg: '#1c1c1e'
},
-
- // 系统信息
systemInfo: null,
statusBarHeight: 44,
navBarHeight: 88,
-
- // TabBar相关
- currentTab: 0
+ capsulePaddingRight: 0,
+ currentTab: 0,
+ features: null,
+ matchEnabled: false,
+ _featureConfigLastFetch: 0
},
onLaunch(options) {
- // 获取系统信息
- this.getSystemInfo()
-
- // 检查登录状态
- this.checkLoginStatus()
-
- // 加载书籍数据
- this.loadBookData()
-
- // 检查更新
- this.checkUpdate()
-
- // 处理分享参数(推荐码绑定)
- this.handleReferralCode(options)
- },
-
- // 小程序显示时也检查分享参数
- onShow(options) {
- this.handleReferralCode(options)
- },
-
- // 处理推荐码绑定
- handleReferralCode(options) {
- const query = options?.query || {}
- const refCode = query.ref || query.referralCode
-
- if (refCode) {
- console.log('[App] 检测到推荐码:', refCode)
-
- // 立即记录访问(不需要登录,用于统计"通过链接进的人数")
- this.recordReferralVisit(refCode)
-
- // 检查是否已经绑定过
- const boundRef = wx.getStorageSync('boundReferralCode')
- if (boundRef && boundRef !== refCode) {
- console.log('[App] 已绑定过其他推荐码,不更换绑定关系')
- // 但仍然记录访问,不return
- } else {
- // 保存待绑定的推荐码
- this.globalData.pendingReferralCode = refCode
- wx.setStorageSync('pendingReferralCode', refCode)
-
- // 如果已登录,立即绑定
- if (this.globalData.isLoggedIn && this.globalData.userInfo) {
- this.bindReferralCode(refCode)
- }
- }
- }
- },
-
- // 记录推荐访问(不需要登录,用于统计)
- async recordReferralVisit(refCode) {
try {
- // 获取openId(如果有)
- const openId = this.globalData.openId || wx.getStorageSync('openId') || ''
- const userId = this.globalData.userInfo?.id || ''
-
- await this.request('/api/referral/visit', {
- method: 'POST',
- data: {
- referralCode: refCode,
- visitorOpenId: openId,
- visitorId: userId,
- source: 'miniprogram',
- page: getCurrentPages()[getCurrentPages().length - 1]?.route || ''
- }
- })
- console.log('[App] 记录推荐访问成功')
+ this.getSystemInfo()
+ this.checkLoginStatus()
+ this.handleReferralCode(options)
+ // 异步请求不阻塞启动,失败也不影响模拟器启动(loadBookData 内部已 catch)
+ this.loadFeatureConfig().catch(() => {})
+ this.loadBookData()
} catch (e) {
- console.log('[App] 记录推荐访问失败:', e.message)
- // 忽略错误,不影响用户体验
- }
- },
-
- // 绑定推荐码到用户
- async bindReferralCode(refCode) {
- try {
- const userId = this.globalData.userInfo?.id
- if (!userId || !refCode) return
-
- // 检查是否已绑定
- const boundRef = wx.getStorageSync('boundReferralCode')
- if (boundRef) {
- console.log('[App] 已绑定推荐码,跳过')
- return
- }
-
- console.log('[App] 绑定推荐码:', refCode, '到用户:', userId)
-
- // 调用API绑定推荐关系
- const res = await this.request('/api/referral/bind', {
- method: 'POST',
- data: {
- userId,
- referralCode: refCode
- }
- })
-
- if (res.success) {
- console.log('[App] 推荐码绑定成功')
- wx.setStorageSync('boundReferralCode', refCode)
- this.globalData.pendingReferralCode = null
- wx.removeStorageSync('pendingReferralCode')
- }
- } catch (e) {
- console.error('[App] 绑定推荐码失败:', e)
+ console.error('[App] onLaunch error', e)
+ }
+ },
+
+ onShow(options) {
+ this.handleReferralCode(options)
+ this.loadFeatureConfig()
+ },
+
+ loadFeatureConfig(forceRefresh) {
+ const now = Date.now()
+ const throttleMs = 15000
+ if (!forceRefresh && this.globalData._featureConfigLastFetch && (now - this.globalData._featureConfigLastFetch < throttleMs)) {
+ return Promise.resolve(this.globalData.features)
+ }
+ return this.request('/api/db/config')
+ .then((res) => {
+ if (res && res.features) {
+ this.globalData.features = res.features
+ this.globalData.matchEnabled = res.features.matchEnabled === true
+ this.globalData._featureConfigLastFetch = Date.now()
+ return this.globalData.features
+ }
+ return this.globalData.features
+ })
+ .catch((e) => {
+ console.log('[App] 加载功能配置失败', e)
+ return this.globalData.features
+ })
+ },
+
+ handleReferralCode(options) {
+ const query = options?.query || {}
+ const refCode = query.ref || query.referralCode
+ if (refCode) {
+ this.globalData.pendingReferralCode = refCode
+ wx.setStorageSync('pendingReferralCode', refCode)
}
},
- // 获取系统信息
getSystemInfo() {
try {
const systemInfo = wx.getSystemInfoSync()
this.globalData.systemInfo = systemInfo
- this.globalData.statusBarHeight = systemInfo.statusBarHeight || 44
-
- // 计算导航栏高度
+ const statusBarHeight = systemInfo.statusBarHeight || 44
+ this.globalData.statusBarHeight = statusBarHeight
const menuButton = wx.getMenuButtonBoundingClientRect()
- if (menuButton) {
- this.globalData.navBarHeight = (menuButton.top - systemInfo.statusBarHeight) * 2 + menuButton.height + systemInfo.statusBarHeight
+ if (menuButton && menuButton.top != null) {
+ this.globalData.navBarHeight = (menuButton.top - statusBarHeight) * 2 + menuButton.height + statusBarHeight
+ const w = systemInfo.windowWidth || 375
+ this.globalData.capsulePaddingRight = Math.ceil(w - menuButton.left + 8)
+ } else {
+ this.globalData.navBarHeight = statusBarHeight + 44
}
} catch (e) {
- console.error('获取系统信息失败:', e)
+ console.error('获取系统信息失败', e)
}
},
- // 检查登录状态
checkLoginStatus() {
try {
const userInfo = wx.getStorageSync('userInfo')
const token = wx.getStorageSync('token')
-
if (userInfo && token) {
this.globalData.userInfo = userInfo
this.globalData.isLoggedIn = true
@@ -188,69 +108,27 @@ App({
this.globalData.hasFullBook = userInfo.hasFullBook || false
}
} catch (e) {
- console.error('检查登录状态失败:', e)
+ console.error('检查登录状态失败', e)
}
},
- // 加载书籍数据
- async loadBookData() {
- try {
- // 先从缓存加载
- const cachedData = wx.getStorageSync('bookData')
- if (cachedData) {
- this.globalData.bookData = cachedData
- }
-
- // 从服务器获取最新数据
- const res = await this.request('/api/book/all-chapters')
- if (res && res.data) {
- this.globalData.bookData = res.data
- wx.setStorageSync('bookData', res.data)
- }
- } catch (e) {
- console.error('加载书籍数据失败:', e)
- }
- },
-
- // 检查更新
- checkUpdate() {
- if (wx.canIUse('getUpdateManager')) {
- const updateManager = wx.getUpdateManager()
-
- updateManager.onCheckForUpdate((res) => {
- if (res.hasUpdate) {
- console.log('发现新版本')
+ loadBookData() {
+ this.request('/api/book/all-chapters')
+ .then((res) => {
+ if (res && res.data) {
+ this.globalData.bookData = res.data
+ wx.setStorageSync('bookData', res.data)
}
})
-
- updateManager.onUpdateReady(() => {
- wx.showModal({
- title: '更新提示',
- content: '新版本已准备好,是否重启应用?',
- success: (res) => {
- if (res.confirm) {
- updateManager.applyUpdate()
- }
- }
- })
- })
-
- updateManager.onUpdateFailed(() => {
- wx.showToast({
- title: '更新失败,请稍后重试',
- icon: 'none'
- })
- })
- }
+ .catch((e) => console.error('加载书籍数据失败', e))
},
- // 统一请求方法
request(url, options = {}) {
return new Promise((resolve, reject) => {
const token = wx.getStorageSync('token')
-
+ const fullUrl = this.globalData.baseUrl + url
wx.request({
- url: this.globalData.baseUrl + url,
+ url: fullUrl,
method: options.method || 'GET',
data: options.data || {},
header: {
@@ -259,206 +137,52 @@ App({
...options.header
},
success: (res) => {
- if (res.statusCode === 200) {
- resolve(res.data)
- } else if (res.statusCode === 401) {
- // 未授权,清除登录状态
+ if (res.statusCode === 200) resolve(res.data)
+ else if (res.statusCode === 401) {
this.logout()
reject(new Error('未授权'))
- } else {
- reject(new Error(res.data?.message || '请求失败'))
- }
+ } else reject(new Error(res.data?.message || '请求失败'))
},
fail: (err) => {
- reject(err)
+ console.warn('[Request] fail', fullUrl, err)
+ reject(err && err.errMsg ? new Error(err.errMsg) : new Error('网络请求失败'))
}
})
})
},
- // 登录方法 - 获取openId用于支付
async login() {
try {
- // 获取微信登录code
- const loginRes = await new Promise((resolve, reject) => {
- wx.login({
- success: resolve,
- fail: reject
- })
- })
-
- console.log('[App] 获取登录code成功')
-
- try {
- // 发送code到服务器获取openId
- const res = await this.request('/api/miniprogram/login', {
- method: 'POST',
- data: { code: loginRes.code }
- })
-
- if (res.success && res.data) {
- // 保存openId
- if (res.data.openId) {
- this.globalData.openId = res.data.openId
- wx.setStorageSync('openId', res.data.openId)
- console.log('[App] 获取openId成功')
- }
-
- // 保存用户信息
- if (res.data.user) {
- this.globalData.userInfo = res.data.user
- this.globalData.isLoggedIn = true
- this.globalData.purchasedSections = res.data.user.purchasedSections || []
- this.globalData.hasFullBook = res.data.user.hasFullBook || false
-
- wx.setStorageSync('userInfo', res.data.user)
- wx.setStorageSync('token', res.data.token || '')
-
- // 登录成功后,检查待绑定的推荐码并执行绑定
- const pendingRef = wx.getStorageSync('pendingReferralCode') || this.globalData.pendingReferralCode
- if (pendingRef) {
- console.log('[App] 登录后自动绑定推荐码:', pendingRef)
- this.bindReferralCode(pendingRef)
- }
- }
-
- return res.data
+ const loginRes = await new Promise((resolve, reject) => wx.login({ success: resolve, fail: reject }))
+ const res = await this.request('/api/miniprogram/login', { method: 'POST', data: { code: loginRes.code } })
+ if (res && res.success && res.data) {
+ if (res.data.openId) {
+ this.globalData.openId = res.data.openId
+ wx.setStorageSync('openId', res.data.openId)
}
- } catch (apiError) {
- console.log('[App] API登录失败:', apiError.message)
- // 不使用模拟登录,提示用户网络问题
- wx.showToast({ title: '网络异常,请重试', icon: 'none' })
- return null
- }
-
- return null
- } catch (e) {
- console.error('[App] 登录失败:', e)
- wx.showToast({ title: '登录失败,请重试', icon: 'none' })
- return null
- }
- },
-
- // 获取openId (支付必需)
- async getOpenId() {
- // 先检查缓存
- const cachedOpenId = wx.getStorageSync('openId')
- if (cachedOpenId) {
- this.globalData.openId = cachedOpenId
- return cachedOpenId
- }
-
- // 没有缓存则登录获取
- try {
- const loginRes = await new Promise((resolve, reject) => {
- wx.login({ success: resolve, fail: reject })
- })
-
- const res = await this.request('/api/miniprogram/login', {
- method: 'POST',
- data: { code: loginRes.code }
- })
-
- if (res.success && res.data?.openId) {
- this.globalData.openId = res.data.openId
- wx.setStorageSync('openId', res.data.openId)
- return res.data.openId
- }
- } catch (e) {
- console.error('[App] 获取openId失败:', e)
- }
-
- return null
- },
-
- // 模拟登录已废弃 - 不再使用
- // 现在必须使用真实的微信登录获取openId作为唯一标识
- mockLogin() {
- console.warn('[App] mockLogin已废弃,请使用真实登录')
- return null
- },
-
- // 手机号登录
- async loginWithPhone(phoneCode) {
- try {
- // 尝试API登录
- const res = await this.request('/api/wechat/phone-login', {
- method: 'POST',
- data: { code: phoneCode }
- })
-
- if (res.success && res.data) {
- this.globalData.userInfo = res.data.user
- this.globalData.isLoggedIn = true
- this.globalData.purchasedSections = res.data.user.purchasedSections || []
- this.globalData.hasFullBook = res.data.user.hasFullBook || false
-
- wx.setStorageSync('userInfo', res.data.user)
- wx.setStorageSync('token', res.data.token)
-
- // 登录成功后绑定推荐码
- const pendingRef = wx.getStorageSync('pendingReferralCode') || this.globalData.pendingReferralCode
- if (pendingRef) {
- console.log('[App] 手机号登录后自动绑定推荐码:', pendingRef)
- this.bindReferralCode(pendingRef)
+ if (res.data.user) {
+ this.globalData.userInfo = res.data.user
+ this.globalData.isLoggedIn = true
+ this.globalData.purchasedSections = res.data.user.purchasedSections || []
+ this.globalData.hasFullBook = res.data.user.hasFullBook || false
+ wx.setStorageSync('userInfo', res.data.user)
+ wx.setStorageSync('token', res.data.token || '')
}
-
return res.data
}
} catch (e) {
- console.log('[App] 手机号登录失败:', e)
+ console.error('登录失败', e)
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
}
-
return null
},
- // 退出登录
logout() {
this.globalData.userInfo = null
this.globalData.isLoggedIn = false
this.globalData.purchasedSections = []
this.globalData.hasFullBook = false
-
wx.removeStorageSync('userInfo')
wx.removeStorageSync('token')
- },
-
- // 检查是否已购买章节
- hasPurchased(sectionId) {
- if (this.globalData.hasFullBook) return true
- return this.globalData.purchasedSections.includes(sectionId)
- },
-
- // 获取章节总数
- getTotalSections() {
- return this.globalData.totalSections
- },
-
- // 切换TabBar
- switchTab(index) {
- this.globalData.currentTab = index
- },
-
- // 显示Toast
- showToast(title, icon = 'none') {
- wx.showToast({
- title,
- icon,
- duration: 2000
- })
- },
-
- // 显示Loading
- showLoading(title = '加载中...') {
- wx.showLoading({
- title,
- mask: true
- })
- },
-
- // 隐藏Loading
- hideLoading() {
- wx.hideLoading()
}
})
diff --git a/miniprogram/app.json b/miniprogram/app.json
index 31f336b6..30ecf360 100644
--- a/miniprogram/app.json
+++ b/miniprogram/app.json
@@ -28,34 +28,18 @@
"backgroundColor": "#1c1c1e",
"borderStyle": "black",
"list": [
- {
- "pagePath": "pages/index/index",
- "text": "首页"
- },
- {
- "pagePath": "pages/chapters/chapters",
- "text": "目录"
- },
- {
- "pagePath": "pages/match/match",
- "text": "找伙伴"
- },
- {
- "pagePath": "pages/my/my",
- "text": "我的"
- }
+ { "pagePath": "pages/index/index", "text": "首页" },
+ { "pagePath": "pages/chapters/chapters", "text": "目录" },
+ { "pagePath": "pages/match/match", "text": "找伙伴" },
+ { "pagePath": "pages/my/my", "text": "我的" }
]
},
"usingComponents": {},
"__usePrivacyCheck__": true,
"permission": {
- "scope.userLocation": {
- "desc": "用于匹配附近的书友"
- }
+ "scope.userLocation": { "desc": "用于匹配附近的书友" }
},
- "requiredPrivateInfos": [
- "getLocation"
- ],
+ "requiredPrivateInfos": ["getLocation"],
"lazyCodeLoading": "requiredComponents",
"style": "v2",
"sitemapLocation": "sitemap.json"
diff --git a/miniprogram/app.wxss b/miniprogram/app.wxss
index 4a79d19f..c2b3ac45 100644
--- a/miniprogram/app.wxss
+++ b/miniprogram/app.wxss
@@ -1,568 +1,12 @@
-/**
- * Soul创业实验 - 全局样式
- * 主题色: #00CED1 (Soul青色)
- * 开发: 卡若
- */
-
-/* ===== 页面基础样式 ===== */
+/** 全局样式 - 与 Web globals 一致 */
page {
- background-color: #000000;
- color: #ffffff;
- font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', 'PingFang SC', 'Microsoft YaHei', sans-serif;
- font-size: 28rpx;
- line-height: 1.5;
- -webkit-font-smoothing: antialiased;
-}
-
-/* ===== 全局容器 ===== */
-.container {
- min-height: 100vh;
- padding: 0;
background: #000000;
- padding-bottom: env(safe-area-inset-bottom);
-}
-
-/* ===== 品牌色系 ===== */
-.brand-color {
- color: #00CED1;
-}
-
-.brand-bg {
- background-color: #00CED1;
-}
-
-.brand-gradient {
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
-}
-
-.gold-color {
- color: #FFD700;
-}
-
-.gold-bg {
- background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
-}
-
-/* ===== 文字渐变 ===== */
-.gradient-text {
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
-}
-
-.gold-gradient-text {
- background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
-}
-
-/* ===== 按钮样式 ===== */
-.btn-primary {
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
color: #ffffff;
- border: none;
- border-radius: 48rpx;
- padding: 28rpx 48rpx;
- font-size: 32rpx;
- font-weight: 600;
- box-shadow: 0 8rpx 24rpx rgba(0, 206, 209, 0.3);
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.btn-primary::after {
- border: none;
-}
-
-.btn-primary:active {
- opacity: 0.85;
- transform: scale(0.98);
-}
-
-.btn-secondary {
- background: rgba(0, 206, 209, 0.1);
- color: #00CED1;
- border: 2rpx solid rgba(0, 206, 209, 0.3);
- border-radius: 48rpx;
- padding: 28rpx 48rpx;
- font-size: 32rpx;
- font-weight: 500;
-}
-
-.btn-secondary::after {
- border: none;
-}
-
-.btn-secondary:active {
- background: rgba(0, 206, 209, 0.2);
-}
-
-.btn-ghost {
- background: rgba(255, 255, 255, 0.05);
- color: #ffffff;
- border: 2rpx solid rgba(255, 255, 255, 0.1);
- border-radius: 48rpx;
- padding: 28rpx 48rpx;
- font-size: 32rpx;
-}
-
-.btn-ghost::after {
- border: none;
-}
-
-.btn-ghost:active {
- background: rgba(255, 255, 255, 0.1);
-}
-
-.btn-gold {
- background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
- color: #000000;
- border: none;
- border-radius: 48rpx;
- padding: 28rpx 48rpx;
- font-size: 32rpx;
- font-weight: 600;
- box-shadow: 0 8rpx 24rpx rgba(255, 215, 0, 0.3);
-}
-
-.btn-gold::after {
- border: none;
-}
-
-/* ===== 卡片样式 ===== */
-.card {
- background: rgba(28, 28, 30, 0.9);
- border-radius: 32rpx;
- padding: 32rpx;
- margin: 24rpx 32rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.card-light {
- background: rgba(44, 44, 46, 0.8);
- border-radius: 24rpx;
- padding: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.08);
-}
-
-.card-gradient {
- background: linear-gradient(135deg, rgba(28, 28, 30, 1) 0%, rgba(44, 44, 46, 1) 100%);
- border-radius: 32rpx;
- padding: 32rpx;
- border: 2rpx solid rgba(0, 206, 209, 0.2);
-}
-
-.card-brand {
- background: linear-gradient(135deg, rgba(0, 206, 209, 0.1) 0%, rgba(32, 178, 170, 0.05) 100%);
- border-radius: 32rpx;
- padding: 32rpx;
- border: 2rpx solid rgba(0, 206, 209, 0.2);
-}
-
-/* ===== 输入框样式 ===== */
-.input-ios {
- background: rgba(0, 0, 0, 0.3);
- border: 2rpx solid rgba(255, 255, 255, 0.1);
- border-radius: 24rpx;
- padding: 28rpx 32rpx;
- font-size: 32rpx;
- color: #ffffff;
-}
-
-.input-ios:focus {
- border-color: rgba(0, 206, 209, 0.5);
-}
-
-.input-ios-placeholder {
- color: rgba(255, 255, 255, 0.3);
-}
-
-/* ===== 列表项样式 ===== */
-.list-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 28rpx 32rpx;
- background: rgba(28, 28, 30, 0.9);
- border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.list-item:first-child {
- border-radius: 24rpx 24rpx 0 0;
-}
-
-.list-item:last-child {
- border-radius: 0 0 24rpx 24rpx;
- border-bottom: none;
-}
-
-.list-item:only-child {
- border-radius: 24rpx;
-}
-
-.list-item:active {
- background: rgba(44, 44, 46, 1);
-}
-
-/* ===== 标签样式 ===== */
-.tag {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- padding: 8rpx 20rpx;
- min-width: 80rpx;
- border-radius: 8rpx;
- font-size: 22rpx;
- font-weight: 500;
- box-sizing: border-box;
- text-align: center;
-}
-
-.tag-brand {
- background: rgba(0, 206, 209, 0.1);
- color: #00CED1;
-}
-
-.tag-gold {
- background: rgba(255, 215, 0, 0.1);
- color: #FFD700;
-}
-
-.tag-pink {
- background: rgba(233, 30, 99, 0.1);
- color: #E91E63;
-}
-
-.tag-purple {
- background: rgba(123, 97, 255, 0.1);
- color: #7B61FF;
-}
-
-.tag-free {
- background: rgba(0, 206, 209, 0.1);
- color: #00CED1;
-}
-
-/* ===== 分隔线 ===== */
-.divider {
- height: 1rpx;
- background: rgba(255, 255, 255, 0.05);
- margin: 24rpx 0;
-}
-
-.divider-vertical {
- width: 2rpx;
- height: 48rpx;
- background: rgba(255, 255, 255, 0.1);
-}
-
-/* ===== 骨架屏动画 ===== */
-.skeleton {
- background: linear-gradient(90deg,
- rgba(28, 28, 30, 1) 25%,
- rgba(44, 44, 46, 1) 50%,
- rgba(28, 28, 30, 1) 75%
- );
- background-size: 200% 100%;
- animation: skeleton-loading 1.5s ease-in-out infinite;
- border-radius: 8rpx;
-}
-
-@keyframes skeleton-loading {
- 0% {
- background-position: 200% 0;
- }
- 100% {
- background-position: -200% 0;
- }
-}
-
-/* ===== 页面过渡动画 ===== */
-.page-transition {
- animation: fadeIn 0.3s ease-out;
-}
-
-@keyframes fadeIn {
- from {
- opacity: 0;
- transform: translateY(20rpx);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-/* ===== 弹窗动画 ===== */
-.modal-overlay {
- animation: modalOverlayIn 0.25s ease-out;
-}
-
-.modal-content {
- animation: modalContentIn 0.3s cubic-bezier(0.32, 0.72, 0, 1);
-}
-
-@keyframes modalOverlayIn {
- from { opacity: 0; }
- to { opacity: 1; }
-}
-
-@keyframes modalContentIn {
- from {
- opacity: 0;
- transform: scale(0.95) translateY(20rpx);
- }
- to {
- opacity: 1;
- transform: scale(1) translateY(0);
- }
-}
-
-/* ===== 脉动动画 ===== */
-.pulse {
- animation: pulse 2s ease-in-out infinite;
-}
-
-@keyframes pulse {
- 0%, 100% {
- transform: scale(1);
- opacity: 1;
- }
- 50% {
- transform: scale(1.05);
- opacity: 0.8;
- }
-}
-
-/* ===== 发光效果 ===== */
-.glow {
- box-shadow: 0 0 40rpx rgba(0, 206, 209, 0.3);
-}
-
-.glow-gold {
- box-shadow: 0 0 40rpx rgba(255, 215, 0, 0.3);
-}
-
-/* ===== 文字样式 ===== */
-.text-xs {
- font-size: 22rpx;
-}
-
-.text-sm {
- font-size: 26rpx;
-}
-
-.text-base {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 28rpx;
}
+.brand-color { color: #00CED1; }
+.gold-color { color: #FFD700; }
-.text-lg {
- font-size: 32rpx;
-}
-
-.text-xl {
- font-size: 36rpx;
-}
-
-.text-2xl {
- font-size: 44rpx;
-}
-
-.text-3xl {
- font-size: 56rpx;
-}
-
-.text-white {
- color: #ffffff;
-}
-
-.text-gray {
- color: rgba(255, 255, 255, 0.6);
-}
-
-.text-muted {
- color: rgba(255, 255, 255, 0.4);
-}
-
-.text-center {
- text-align: center;
-}
-
-.font-medium {
- font-weight: 500;
-}
-
-.font-semibold {
- font-weight: 600;
-}
-
-.font-bold {
- font-weight: 700;
-}
-
-/* ===== Flex布局 ===== */
-.flex {
- display: flex;
-}
-
-.flex-col {
- flex-direction: column;
-}
-
-.items-center {
- align-items: center;
-}
-
-.justify-center {
- justify-content: center;
-}
-
-.justify-between {
- justify-content: space-between;
-}
-
-.justify-around {
- justify-content: space-around;
-}
-
-.flex-1 {
- flex: 1;
-}
-
-.gap-1 {
- gap: 8rpx;
-}
-
-.gap-2 {
- gap: 16rpx;
-}
-
-.gap-3 {
- gap: 24rpx;
-}
-
-.gap-4 {
- gap: 32rpx;
-}
-
-/* ===== 间距 ===== */
-.p-2 { padding: 16rpx; }
-.p-3 { padding: 24rpx; }
-.p-4 { padding: 32rpx; }
-.p-5 { padding: 40rpx; }
-
-.px-4 { padding-left: 32rpx; padding-right: 32rpx; }
-.py-2 { padding-top: 16rpx; padding-bottom: 16rpx; }
-.py-3 { padding-top: 24rpx; padding-bottom: 24rpx; }
-
-.m-4 { margin: 32rpx; }
-.mx-4 { margin-left: 32rpx; margin-right: 32rpx; }
-.my-3 { margin-top: 24rpx; margin-bottom: 24rpx; }
-.mb-2 { margin-bottom: 16rpx; }
-.mb-3 { margin-bottom: 24rpx; }
-.mb-4 { margin-bottom: 32rpx; }
-.mt-4 { margin-top: 32rpx; }
-
-/* ===== 圆角 ===== */
-.rounded { border-radius: 8rpx; }
-.rounded-lg { border-radius: 16rpx; }
-.rounded-xl { border-radius: 24rpx; }
-.rounded-2xl { border-radius: 32rpx; }
-.rounded-full { border-radius: 50%; }
-
-/* ===== 安全区域 ===== */
-.safe-bottom {
- padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx);
-}
-
-.pb-tabbar {
- padding-bottom: 200rpx;
-}
-
-/* ===== 头部导航占位 ===== */
-.nav-placeholder {
- height: calc(88rpx + env(safe-area-inset-top, 44rpx));
-}
-
-/* ===== 隐藏滚动条 ===== */
-::-webkit-scrollbar {
- display: none;
- width: 0;
- height: 0;
-}
-
-/* ===== 触摸反馈 ===== */
-.touch-feedback {
- transition: all 0.15s ease;
-}
-
-.touch-feedback:active {
- opacity: 0.7;
- transform: scale(0.98);
-}
-
-/* ===== 进度条 ===== */
-.progress-bar {
- height: 8rpx;
- background: rgba(44, 44, 46, 1);
- border-radius: 4rpx;
- overflow: hidden;
-}
-
-.progress-fill {
- height: 100%;
- background: linear-gradient(90deg, #00CED1 0%, #20B2AA 100%);
- border-radius: 4rpx;
- transition: width 0.3s ease;
-}
-
-/* ===== 头像样式 ===== */
-.avatar {
- width: 80rpx;
- height: 80rpx;
- border-radius: 50%;
- 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;
- color: #00CED1;
- font-weight: 700;
- font-size: 32rpx;
- border: 4rpx solid rgba(0, 206, 209, 0.3);
-}
-
-.avatar-lg {
- width: 120rpx;
- height: 120rpx;
- font-size: 48rpx;
-}
-
-/* ===== 图标容器 ===== */
-.icon-box {
- width: 64rpx;
- height: 64rpx;
- border-radius: 16rpx;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.icon-box-brand {
- background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, rgba(32, 178, 170, 0.1) 100%);
-}
-
-.icon-box-gold {
- background: linear-gradient(135deg, rgba(255, 215, 0, 0.2) 0%, rgba(255, 165, 0, 0.1) 100%);
-}
-
-/* ===== 渐变背景 ===== */
-.bg-gradient-dark {
- background: linear-gradient(180deg, #000000 0%, #1a1a1a 100%);
-}
-
-.bg-gradient-brand {
- background: linear-gradient(135deg, rgba(0, 206, 209, 0.1) 0%, transparent 100%);
-}
+/* 顶部安全区:避免被状态栏和胶囊遮挡,头部内容需加此类或预留右侧 */
+.safe-header-right { padding-right: 200rpx; box-sizing: border-box; }
diff --git a/miniprogram/assets/icons/home-active.png b/miniprogram/assets/icons/home-active.png
deleted file mode 100644
index b6090d87..00000000
Binary files a/miniprogram/assets/icons/home-active.png and /dev/null differ
diff --git a/miniprogram/assets/icons/home.png b/miniprogram/assets/icons/home.png
deleted file mode 100644
index 0ffba614..00000000
Binary files a/miniprogram/assets/icons/home.png and /dev/null differ
diff --git a/miniprogram/assets/icons/match-active.png b/miniprogram/assets/icons/match-active.png
deleted file mode 100644
index da62b436..00000000
Binary files a/miniprogram/assets/icons/match-active.png and /dev/null differ
diff --git a/miniprogram/assets/icons/match.png b/miniprogram/assets/icons/match.png
deleted file mode 100644
index b15582e3..00000000
Binary files a/miniprogram/assets/icons/match.png and /dev/null differ
diff --git a/miniprogram/assets/icons/my-active.png b/miniprogram/assets/icons/my-active.png
deleted file mode 100644
index da62b436..00000000
Binary files a/miniprogram/assets/icons/my-active.png and /dev/null differ
diff --git a/miniprogram/assets/icons/my.png b/miniprogram/assets/icons/my.png
deleted file mode 100644
index b15582e3..00000000
Binary files a/miniprogram/assets/icons/my.png and /dev/null differ
diff --git a/miniprogram/custom-tab-bar/index.js b/miniprogram/custom-tab-bar/index.js
index 7cd2fbe2..ea68fb97 100644
--- a/miniprogram/custom-tab-bar/index.js
+++ b/miniprogram/custom-tab-bar/index.js
@@ -1,82 +1,44 @@
-/**
- * Soul创业实验 - 自定义TabBar组件
- * 实现中间突出的"找伙伴"按钮
- */
+const app = getApp()
Component({
data: {
selected: 0,
- color: '#8e8e93',
- selectedColor: '#00CED1',
list: [
- {
- pagePath: '/pages/index/index',
- text: '首页',
- iconType: 'home'
- },
- {
- pagePath: '/pages/chapters/chapters',
- text: '目录',
- iconType: 'list'
- },
- {
- pagePath: '/pages/match/match',
- text: '找伙伴',
- iconType: 'match',
- isSpecial: true,
- hidden: true // 默认隐藏,等配置加载后根据后台设置显示
- },
- {
- pagePath: '/pages/my/my',
- text: '我的',
- iconType: 'user'
- }
- ],
- matchEnabled: false // 找伙伴功能开关(默认隐藏,等待后台配置加载)
- },
-
- attached() {
- // 初始化时获取当前页面
- this.loadFeatureConfig()
+ { pagePath: '/pages/index/index', text: '首页', icon: '🏠' },
+ { pagePath: '/pages/chapters/chapters', text: '目录', icon: '📋' },
+ { pagePath: '/pages/match/match', text: '找伙伴', icon: '👥', hidden: true, isCenter: true },
+ { pagePath: '/pages/my/my', text: '我的', icon: '👤' }
+ ]
},
methods: {
- // 加载功能配置
- async loadFeatureConfig() {
- try {
- const app = getApp()
- const res = await app.request('/api/db/config')
-
- if (res.success && res.features) {
- const matchEnabled = res.features.matchEnabled === true
- this.setData({ matchEnabled })
-
- // 更新list,隐藏或显示找伙伴
- const list = this.data.list.map(item => {
- if (item.iconType === 'match') {
- return { ...item, hidden: !matchEnabled }
- }
- return item
- })
- this.setData({ list })
-
- console.log('[TabBar] 功能配置加载成功,找伙伴功能:', matchEnabled ? '开启' : '关闭')
+ syncMatchEnabled() {
+ const matchEnabled = app.globalData.matchEnabled === true
+ const list = this.data.list.map((item) => {
+ if (item.text === '找伙伴') {
+ return { ...item, hidden: !matchEnabled }
}
- } catch (e) {
- console.log('[TabBar] 加载功能配置失败:', e)
- // 失败时默认隐藏找伙伴(与Web版保持一致)
- this.setData({ matchEnabled: false })
- }
+ return item
+ })
+ this.setData({ list })
},
-
+
switchTab(e) {
- const data = e.currentTarget.dataset
- const url = data.path
- const index = data.index
-
- if (this.data.selected === index) return
-
- wx.switchTab({ url })
+ const path = e.currentTarget.dataset.path
+ const index = e.currentTarget.dataset.index
+ this.setData({ selected: index })
+ wx.switchTab({ url: path })
+ }
+ },
+
+ attached() {
+ this.syncMatchEnabled()
+ app.loadFeatureConfig().then(() => this.syncMatchEnabled())
+ },
+
+ pageLifetimes: {
+ show() {
+ app.loadFeatureConfig().then(() => this.syncMatchEnabled())
}
}
})
diff --git a/miniprogram/custom-tab-bar/index.json b/miniprogram/custom-tab-bar/index.json
index 467ce294..a89ef4db 100644
--- a/miniprogram/custom-tab-bar/index.json
+++ b/miniprogram/custom-tab-bar/index.json
@@ -1,3 +1,4 @@
{
- "component": true
+ "component": true,
+ "usingComponents": {}
}
diff --git a/miniprogram/custom-tab-bar/index.wxml b/miniprogram/custom-tab-bar/index.wxml
index a412ddaf..b2f89dd7 100644
--- a/miniprogram/custom-tab-bar/index.wxml
+++ b/miniprogram/custom-tab-bar/index.wxml
@@ -1,56 +1,14 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{list[0].text}}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{list[1].text}}
-
-
-
-
-
-
-
-
-
-
- {{list[2].text}}
-
-
-
-
-
-
-
-
-
-
-
-
- {{list[3].text}}
+
+ {{item.icon}}
+ {{item.text}}
diff --git a/miniprogram/custom-tab-bar/index.wxss b/miniprogram/custom-tab-bar/index.wxss
index 84ad115f..17bf4186 100644
--- a/miniprogram/custom-tab-bar/index.wxss
+++ b/miniprogram/custom-tab-bar/index.wxss
@@ -1,227 +1,8 @@
-/**
- * Soul创业实验 - 自定义TabBar样式
- * 实现中间突出的"找伙伴"按钮
- */
-
-.tab-bar {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- height: 100rpx;
- background: rgba(28, 28, 30, 0.95);
- backdrop-filter: blur(40rpx);
- -webkit-backdrop-filter: blur(40rpx);
- display: flex;
- align-items: flex-end;
- padding-bottom: env(safe-area-inset-bottom);
- z-index: 999;
-}
-
-.tab-bar-border {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- height: 1rpx;
- background: rgba(255, 255, 255, 0.05);
-}
-
-.tab-bar-item {
- flex: 1;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 10rpx 0 16rpx;
-}
-
-.icon-wrapper {
- width: 48rpx;
- height: 48rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-bottom: 4rpx;
-}
-
-.icon {
- width: 44rpx;
- height: 44rpx;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.tab-bar-text {
- font-size: 22rpx;
- line-height: 1;
-}
-
-/* ===== 首页图标 ===== */
-.icon-home {
- position: relative;
- width: 40rpx;
- height: 40rpx;
-}
-
-.home-roof {
- position: absolute;
- top: 4rpx;
- left: 50%;
- transform: translateX(-50%);
- width: 0;
- height: 0;
- border-left: 18rpx solid transparent;
- border-right: 18rpx solid transparent;
- border-bottom: 14rpx solid #8e8e93;
-}
-
-.home-body {
- position: absolute;
- bottom: 4rpx;
- left: 50%;
- transform: translateX(-50%);
- width: 28rpx;
- height: 18rpx;
- background: #8e8e93;
- border-radius: 0 0 4rpx 4rpx;
-}
-
-.icon-active .home-roof {
- border-bottom-color: #00CED1;
-}
-
-.icon-active .home-body {
- background: #00CED1;
-}
-
-/* ===== 目录图标 ===== */
-.icon-list {
- width: 36rpx;
- height: 32rpx;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
-}
-
-.list-line {
- width: 100%;
- height: 6rpx;
- background: #8e8e93;
- border-radius: 3rpx;
-}
-
-.list-line:nth-child(2) {
- width: 75%;
-}
-
-.list-line:nth-child(3) {
- width: 50%;
-}
-
-.icon-active .list-line {
- background: #00CED1;
-}
-
-/* ===== 我的图标 ===== */
-.icon-user {
- position: relative;
- width: 36rpx;
- height: 40rpx;
-}
-
-.user-head {
- position: absolute;
- top: 0;
- left: 50%;
- transform: translateX(-50%);
- width: 16rpx;
- height: 16rpx;
- background: #8e8e93;
- border-radius: 50%;
-}
-
-.user-body {
- position: absolute;
- bottom: 0;
- left: 50%;
- transform: translateX(-50%);
- width: 28rpx;
- height: 18rpx;
- background: #8e8e93;
- border-radius: 14rpx 14rpx 0 0;
-}
-
-.icon-active .user-head,
-.icon-active .user-body {
- background: #00CED1;
-}
-
-/* ===== 找伙伴 - 中间特殊按钮 ===== */
-.special-item {
- position: relative;
- margin-top: -32rpx;
-}
-
-.special-button {
- width: 112rpx;
- height: 112rpx;
- border-radius: 50%;
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- display: flex;
- align-items: center;
- justify-content: center;
- box-shadow: 0 8rpx 32rpx rgba(0, 206, 209, 0.4);
- margin-bottom: 4rpx;
- transition: all 0.2s ease;
-}
-
-.special-button:active {
- transform: scale(0.95);
-}
-
-.special-active {
- box-shadow: 0 8rpx 40rpx rgba(0, 206, 209, 0.6);
-}
-
-.special-text {
- margin-top: 4rpx;
-}
-
-/* ===== 找伙伴图标 (双人) ===== */
-.icon-users {
- position: relative;
- width: 56rpx;
- height: 44rpx;
-}
-
-.user-circle {
- position: absolute;
- width: 28rpx;
- height: 28rpx;
- border-radius: 50%;
- background: #ffffff;
-}
-
-.user-circle::after {
- content: '';
- position: absolute;
- bottom: -12rpx;
- left: 50%;
- transform: translateX(-50%);
- width: 22rpx;
- height: 14rpx;
- background: #ffffff;
- border-radius: 11rpx 11rpx 0 0;
-}
-
-.user-1 {
- top: 0;
- left: 0;
-}
-
-.user-2 {
- top: 0;
- right: 0;
-}
+.tab-bar { position: fixed; bottom: 0; left: 0; right: 0; height: 120rpx; background: #1c1c1e; border-top: 2rpx solid rgba(255,255,255,0.05); display: flex; align-items: flex-end; justify-content: space-around; padding-bottom: env(safe-area-inset-bottom); padding-top: 16rpx; }
+.tab-item { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 8rpx 0; }
+.tab-icon { font-size: 44rpx; line-height: 1; margin-bottom: 4rpx; opacity: 0.7; }
+.tab-item.active .tab-icon { opacity: 1; }
+.tab-text { font-size: 20rpx; color: #8e8e93; }
+.tab-item.active .tab-text { color: #00CED1; font-weight: 500; }
+.tab-item.center .tab-icon { width: 88rpx; height: 88rpx; line-height: 88rpx; text-align: center; font-size: 48rpx; margin-top: -40rpx; margin-bottom: 0; border-radius: 50%; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); box-shadow: 0 8rpx 24rpx rgba(0,206,209,0.3); }
+.tab-item.center.active .tab-icon { opacity: 1; box-shadow: 0 8rpx 28rpx rgba(0,206,209,0.4); }
diff --git a/miniprogram/pages/about/about.js b/miniprogram/pages/about/about.js
index dbbcabb0..cb8f69d6 100644
--- a/miniprogram/pages/about/about.js
+++ b/miniprogram/pages/about/about.js
@@ -1,81 +1,49 @@
-/**
- * Soul创业派对 - 关于作者页
- * 开发: 卡若
- */
const app = getApp()
Page({
data: {
statusBarHeight: 44,
- author: {
+ navBarHeight: 88,
+ authorInfo: {
name: '卡若',
- avatar: 'K',
- title: 'Soul派对房主理人 · 私域运营专家',
- bio: '每天早上6点到9点,在Soul派对房分享真实的创业故事。专注私域运营与项目变现,用"云阿米巴"模式帮助创业者构建可持续的商业体系。本书记录了62个真实商业案例,涵盖电商、内容、传统行业等多个领域。',
- stats: [
- { label: '商业案例', value: '62' },
- { label: '连续直播', value: '365天' },
- { label: '派对分享', value: '1000+' }
- ],
- // 联系方式已移至后台配置
- contact: null,
- highlights: [
- '5年私域运营经验',
- '帮助100+品牌从0到1增长',
- '连续创业者,擅长商业模式设计'
- ]
+ description: '连续创业者,私域运营专家',
+ liveTime: '06:00-09:00',
+ platform: 'Soul派对房'
},
- bookInfo: {
- title: '一场Soul的创业实验',
- totalChapters: 62,
- parts: [
- { name: '真实的人', chapters: 10 },
- { name: '真实的行业', chapters: 15 },
- { name: '真实的错误', chapters: 9 },
- { name: '真实的赚钱', chapters: 20 },
- { name: '真实的社会', chapters: 9 }
- ],
- price: 9.9
- }
+ authorInitial: '卡',
+ stats: [
+ { value: '55+', label: '真实案例', icon: '📖' },
+ { value: '10000+', label: '派对房听众', icon: '👥' },
+ { value: '15年', label: '创业经验', icon: '🏆' },
+ { value: '3000万', label: '最高年流水', icon: '📈' }
+ ],
+ milestones: [
+ { year: '2007-2014', event: '游戏电竞创业历程,从魔兽世界代练起步' },
+ { year: '2015', event: '转型电商,做天猫虚拟充值' },
+ { year: '2016-2019', event: '深耕电商领域,团队扩张到200人,年流水3000万' },
+ { year: '2019-2020', event: '公司变故,重整旗鼓' },
+ { year: '2020-2025', event: '电竞、地摊、大健康、私域多领域探索' },
+ { year: '2025.10.15', event: '在Soul派对房开启每日分享,记录真实商业案例' }
+ ]
},
onLoad() {
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight
- })
- this.loadBookStats()
- },
-
- // 加载书籍统计
- async loadBookStats() {
- try {
- const res = await app.request('/api/book/stats')
- if (res && res.success) {
- this.setData({
- 'bookInfo.totalChapters': res.data?.totalChapters || 62,
- 'author.stats': [
- { label: '商业案例', value: String(res.data?.totalChapters || 62) },
- { label: '连续直播', value: '365天' },
- { label: '派对分享', value: '1000+' }
- ]
- })
- }
- } catch (e) {
- console.log('[About] 加载书籍统计失败,使用默认值')
- }
+ const statusBarHeight = app.globalData.statusBarHeight || 44
+ const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
+ const authorInfo = this.data.authorInfo
+ const authorInitial = authorInfo.name ? authorInfo.name.charAt(0) : '卡'
+ this.setData({ statusBarHeight, navBarHeight, authorInitial })
},
- // 联系方式功能已禁用
- copyWechat() {
- wx.showToast({ title: '请在派对房联系作者', icon: 'none' })
- },
-
- callPhone() {
- wx.showToast({ title: '请在派对房联系作者', icon: 'none' })
- },
-
- // 返回
goBack() {
- wx.navigateBack()
+ wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/index/index' }) })
+ },
+
+ onJoinParty() {
+ wx.showToast({
+ title: '请关注小程序或联系客服加入派对群',
+ icon: 'none',
+ duration: 2500
+ })
}
})
diff --git a/miniprogram/pages/about/about.json b/miniprogram/pages/about/about.json
index e90e9960..7ad45361 100644
--- a/miniprogram/pages/about/about.json
+++ b/miniprogram/pages/about/about.json
@@ -1,4 +1,4 @@
{
- "usingComponents": {},
- "navigationStyle": "custom"
+ "navigationBarTitleText": "关于作者",
+ "usingComponents": {}
}
diff --git a/miniprogram/pages/about/about.wxml b/miniprogram/pages/about/about.wxml
index 598e9464..c4197c8c 100644
--- a/miniprogram/pages/about/about.wxml
+++ b/miniprogram/pages/about/about.wxml
@@ -1,75 +1,54 @@
-
-
- ←
- 关于作者
-
+
+
-
-
-
-
-
- {{author.avatar}}
- {{author.name}}
- {{author.title}}
- {{author.bio}}
-
-
-
-
- {{item.value}}
- {{item.label}}
-
+
+
+ {{authorInitial}}
+ {{authorInfo.name}}
+ {{authorInfo.description}}
+
+ 🕐 每日 {{authorInfo.liveTime}}
+ 💬 {{authorInfo.platform}}
-
-
-
-
- ✓
- {{item}}
+
+
+
+ {{item.icon}}
+ {{item.value}}
+ {{item.label}}
+
+
+
+ 关于这本书
+
+ "这不是一本教你成功的鸡汤书。"
+ 这是我每天早上6点到9点,在Soul派对房和几百个陌生人分享的真实故事。
+ "社会不是靠努力,是靠洞察与选择。"
+
+
+
+ 创业历程
+
+
+
+
+
+
+
+ {{item.year}}
+ {{item.event}}
+
-
-
-
- 📚 {{bookInfo.title}}
-
-
- {{bookInfo.totalChapters}}
- 篇章节
-
-
- 5
- 大篇章
-
-
- ¥{{bookInfo.price}}
- 全书价格
-
-
-
-
- {{item.name}}
- {{item.chapters}}节
-
-
-
-
-
-
- 联系作者
-
- 🎉
-
- Soul派对房
- 每天早上6-9点开播
-
-
-
- 在Soul App搜索"创业实验"或"卡若",加入派对房直接交流
-
+
+ 想听更多真实故事?
+ 每天早上6-9点,卡若在Soul派对房免费分享
+ 💬 加入派对群
diff --git a/miniprogram/pages/about/about.wxss b/miniprogram/pages/about/about.wxss
index 337aa041..22c728be 100644
--- a/miniprogram/pages/about/about.wxss
+++ b/miniprogram/pages/about/about.wxss
@@ -1,40 +1,35 @@
-.page { min-height: 100vh; background: #000; }
-.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; }
-.nav-back { width: 72rpx; height: 72rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32rpx; color: #fff; }
-.nav-title { font-size: 36rpx; font-weight: 600; color: #00CED1; }
-.nav-placeholder { width: 72rpx; }
-.content { padding: 32rpx; }
-.author-card { background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%); border-radius: 32rpx; padding: 48rpx; text-align: center; margin-bottom: 24rpx; border: 2rpx solid rgba(0,206,209,0.2); }
-.author-avatar { width: 160rpx; height: 160rpx; border-radius: 50%; background: linear-gradient(135deg, #00CED1, #20B2AA); display: flex; align-items: center; justify-content: center; margin: 0 auto 24rpx; font-size: 64rpx; color: #fff; font-weight: 700; border: 4rpx solid rgba(0,206,209,0.3); }
-.author-name { font-size: 40rpx; font-weight: 700; color: #fff; display: block; margin-bottom: 8rpx; }
-.author-title { font-size: 26rpx; color: #00CED1; display: block; margin-bottom: 24rpx; }
-.author-bio { font-size: 26rpx; color: rgba(255,255,255,0.7); line-height: 1.8; display: block; margin-bottom: 32rpx; }
-.stats-row { display: flex; justify-content: space-around; padding-top: 32rpx; border-top: 2rpx solid rgba(255,255,255,0.1); }
-.stat-item { text-align: center; }
-.stat-value { font-size: 36rpx; font-weight: 700; color: #00CED1; display: block; }
-.stat-label { font-size: 22rpx; color: rgba(255,255,255,0.5); }
-.contact-card { background: #1c1c1e; border-radius: 32rpx; padding: 32rpx; }
-.card-title { font-size: 28rpx; font-weight: 600; color: #fff; display: block; margin-bottom: 24rpx; }
-.contact-item { display: flex; align-items: center; gap: 24rpx; padding: 24rpx; background: rgba(255,255,255,0.05); border-radius: 16rpx; margin-bottom: 16rpx; }
-.contact-item:last-child { margin-bottom: 0; }
-.contact-icon { font-size: 40rpx; }
-.contact-info { flex: 1; }
-.contact-label { font-size: 22rpx; color: rgba(255,255,255,0.5); display: block; }
-.contact-value { font-size: 28rpx; color: #fff; }
-.contact-btn { padding: 12rpx 24rpx; background: rgba(0,206,209,0.2); color: #00CED1; font-size: 24rpx; border-radius: 16rpx; }
-
-/* 亮点标签 */
-.highlights { display: flex; flex-wrap: wrap; gap: 16rpx; margin-top: 32rpx; padding-top: 24rpx; border-top: 2rpx solid rgba(255,255,255,0.1); justify-content: center; }
-.highlight-tag { display: flex; align-items: center; gap: 8rpx; padding: 12rpx 24rpx; background: rgba(0,206,209,0.15); border-radius: 24rpx; font-size: 24rpx; color: rgba(255,255,255,0.8); }
-.tag-icon { color: #00CED1; font-size: 22rpx; }
-
-/* 书籍信息卡片 */
-.book-info-card { background: #1c1c1e; border-radius: 32rpx; padding: 32rpx; margin-bottom: 24rpx; }
-.book-stats { display: flex; justify-content: space-around; padding: 24rpx 0; margin: 16rpx 0; background: rgba(0,0,0,0.3); border-radius: 16rpx; }
-.book-stat { text-align: center; }
-.book-stat-value { font-size: 36rpx; font-weight: 700; color: #FFD700; display: block; }
-.book-stat-label { font-size: 22rpx; color: rgba(255,255,255,0.5); }
-.parts-list { display: flex; flex-wrap: wrap; gap: 12rpx; margin-top: 16rpx; }
-.part-item { display: flex; align-items: center; gap: 8rpx; padding: 12rpx 20rpx; background: rgba(255,255,255,0.05); border-radius: 12rpx; }
-.part-name { font-size: 24rpx; color: rgba(255,255,255,0.8); }
-.part-chapters { font-size: 22rpx; color: #00CED1; }
+.page { min-height: 100vh; background: #000; padding-bottom: 80rpx; }
+.nav-bar { background: rgba(0,0,0,0.9); border-bottom: 2rpx solid rgba(255,255,255,0.05); box-sizing: border-box; display: flex; flex-direction: column; justify-content: flex-end; }
+.nav-inner { display: flex; align-items: center; padding: 0 24rpx; height: 88rpx; min-height: 44px; flex-shrink: 0; }
+.nav-back { font-size: 32rpx; color: #00CED1; padding: 16rpx 0; }
+.nav-title { flex: 1; text-align: center; font-size: 34rpx; color: #00CED1; }
+.main { padding: 32rpx; }
+.card { border-radius: 32rpx; padding: 40rpx; margin-bottom: 24rpx; background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%); border: 2rpx solid rgba(0,206,209,0.2); }
+.author-card { display: flex; flex-direction: column; align-items: center; text-align: center; }
+.author-avatar { width: 160rpx; height: 160rpx; border-radius: 50%; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); display: flex; align-items: center; justify-content: center; font-size: 60rpx; font-weight: 700; color: #fff; margin-bottom: 24rpx; }
+.author-name { font-size: 40rpx; font-weight: 700; color: #fff; display: block; }
+.author-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); margin-top: 8rpx; display: block; }
+.author-tags { display: flex; gap: 16rpx; margin-top: 24rpx; justify-content: center; flex-wrap: wrap; }
+.tag { font-size: 22rpx; padding: 12rpx 24rpx; border-radius: 32rpx; background: rgba(255,255,255,0.05); color: rgba(255,255,255,0.5); }
+.tag.brand { background: rgba(0,206,209,0.1); color: #00CED1; }
+.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16rpx; margin-bottom: 24rpx; }
+.stat-item { padding: 24rpx; border-radius: 24rpx; background: #1c1c1e; border: 2rpx solid rgba(255,255,255,0.05); text-align: center; }
+.stat-icon { font-size: 40rpx; display: block; margin-bottom: 12rpx; opacity: 0.9; }
+.stat-value { font-size: 32rpx; font-weight: 700; color: #fff; display: block; }
+.stat-label { font-size: 20rpx; color: rgba(255,255,255,0.4); }
+.section-title { font-size: 32rpx; font-weight: 600; color: #fff; display: block; margin-bottom: 16rpx; }
+.section-paras { }
+.section-para { font-size: 28rpx; color: rgba(255,255,255,0.8); line-height: 1.7; display: block; margin-bottom: 16rpx; }
+.section-para.brand { color: #00CED1; font-weight: 500; }
+.timeline { }
+.timeline-item { display: flex; gap: 24rpx; }
+.timeline-dot-wrap { display: flex; flex-direction: column; align-items: center; flex-shrink: 0; }
+.timeline-dot { width: 16rpx; height: 16rpx; border-radius: 50%; background: #00CED1; }
+.timeline-line { width: 4rpx; flex: 1; min-height: 24rpx; background: rgba(255,255,255,0.2); margin-top: 8rpx; }
+.timeline-content { flex: 1; padding-bottom: 24rpx; }
+.milestone-year { font-size: 28rpx; color: #00CED1; font-weight: 600; display: block; margin-bottom: 8rpx; }
+.milestone-event { font-size: 26rpx; color: rgba(255,255,255,0.7); display: block; line-height: 1.5; }
+.join-card { background: linear-gradient(90deg, rgba(0,206,209,0.1) 0%, rgba(32,178,170,0.05) 100%); border-color: rgba(0,206,209,0.2); text-align: center; }
+.join-title { font-size: 32rpx; font-weight: 600; color: #fff; display: block; margin-bottom: 8rpx; }
+.join-desc { font-size: 28rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 24rpx; }
+.btn-join { width: 100%; padding: 24rpx; border-radius: 24rpx; background: #00CED1; color: #fff; font-size: 30rpx; font-weight: 500; text-align: center; }
diff --git a/miniprogram/pages/address-edit/address-edit.js b/miniprogram/pages/address-edit/address-edit.js
index de8252c8..8dfcb509 100644
--- a/miniprogram/pages/address-edit/address-edit.js
+++ b/miniprogram/pages/address-edit/address-edit.js
@@ -1,136 +1,68 @@
-/**
- * Soul创业派对 - 收货地址新建/编辑
- */
+// pages/address-edit/address-edit.js
const app = getApp()
Page({
data: {
statusBarHeight: 44,
- id: '',
- isEdit: false,
- name: '',
- phone: '',
- region: ['广东省', '广州市', '天河区'],
- province: '广东省',
- city: '广州市',
- district: '天河区',
- detail: '',
- isDefault: false,
- loading: false
+ navBarHeight: 88
},
onLoad(options) {
- this.setData({ statusBarHeight: app.globalData.statusBarHeight })
- if (options.id) {
- this.setData({ id: options.id, isEdit: true })
- this.loadItem(options.id)
- }
- },
-
- async loadItem(id) {
- wx.showLoading({ title: '加载中...' })
- try {
- const res = await app.request('/api/user/addresses/' + id)
- wx.hideLoading()
- if (res.success && res.item) {
- const item = res.item
- this.setData({
- name: item.name,
- phone: item.phone,
- province: item.province || '',
- city: item.city || '',
- district: item.district || '',
- region: [item.province || '', item.city || '', item.district || ''],
- detail: item.detail,
- isDefault: item.isDefault
- })
- }
- } catch (e) {
- wx.hideLoading()
- wx.showToast({ title: '加载失败', icon: 'none' })
- }
- },
-
- onNameInput(e) {
- this.setData({ name: e.detail.value.trim() })
- },
-
- onPhoneInput(e) {
- this.setData({ phone: e.detail.value.replace(/\D/g, '').slice(0, 11) })
- },
-
- onRegionChange(e) {
- const region = e.detail.value
- this.setData({
- region,
- province: region[0] || '',
- city: region[1] || '',
- district: region[2] || ''
- })
- },
-
- onDetailInput(e) {
- this.setData({ detail: e.detail.value.trim() })
- },
-
- onDefaultChange(e) {
- this.setData({ isDefault: e.detail.value })
- },
-
- async save() {
- const { id, isEdit, name, phone, province, city, district, detail, isDefault } = this.data
- if (!name) {
- wx.showToast({ title: '请输入收货人姓名', icon: 'none' })
- return
- }
- if (!phone || !/^1[3-9]\d{9}$/.test(phone)) {
- wx.showToast({ title: '请输入正确的手机号', icon: 'none' })
- return
- }
- // 省/市/区为选填,不校验
- if (!detail) {
- wx.showToast({ title: '请输入详细地址', icon: 'none' })
- return
- }
- const userId = app.globalData.userInfo?.id
- if (!userId) {
- wx.showToast({ title: '请先登录', icon: 'none' })
- return
- }
- this.setData({ loading: true })
- try {
- if (isEdit && id) {
- const res = await app.request('/api/user/addresses/' + id, {
- method: 'PUT',
- data: { name, phone, province, city, district, detail, isDefault }
- })
- if (res.success) {
- wx.showToast({ title: '保存成功', icon: 'success' })
- setTimeout(() => wx.navigateBack(), 1500)
- } else {
- wx.showToast({ title: res.message || '保存失败', icon: 'none' })
- this.setData({ loading: false })
- }
- } else {
- const res = await app.request('/api/user/addresses', {
- method: 'POST',
- data: { userId, name, phone, province, city, district, detail, isDefault }
- })
- if (res.success) {
- wx.showToast({ title: '添加成功', icon: 'success' })
- setTimeout(() => wx.navigateBack(), 1500)
- } else {
- wx.showToast({ title: res.message || '添加失败', icon: 'none' })
- this.setData({ loading: false })
- }
- }
- } catch (e) {
- this.setData({ loading: false })
- wx.showToast({ title: '保存失败', icon: 'none' })
- }
+ const statusBarHeight = app.globalData.statusBarHeight || 44
+ const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
+ this.setData({ statusBarHeight, navBarHeight })
},
goBack() {
- wx.navigateBack()
+ wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/my/my' }) })
+ },
+
+ /**
+ * 生命周期函数--监听页面初次渲染完成
+ */
+ onReady() {
+
+ },
+
+ /**
+ * 生命周期函数--监听页面显示
+ */
+ onShow() {
+
+ },
+
+ /**
+ * 生命周期函数--监听页面隐藏
+ */
+ onHide() {
+
+ },
+
+ /**
+ * 生命周期函数--监听页面卸载
+ */
+ onUnload() {
+
+ },
+
+ /**
+ * 页面相关事件处理函数--监听用户下拉动作
+ */
+ onPullDownRefresh() {
+
+ },
+
+ /**
+ * 页面上拉触底事件的处理函数
+ */
+ onReachBottom() {
+
+ },
+
+ /**
+ * 用户点击右上角分享
+ */
+ onShareAppMessage() {
+
}
-})
+})
\ No newline at end of file
diff --git a/miniprogram/pages/address-edit/address-edit.wxml b/miniprogram/pages/address-edit/address-edit.wxml
index a78dbd8f..e53946ef 100644
--- a/miniprogram/pages/address-edit/address-edit.wxml
+++ b/miniprogram/pages/address-edit/address-edit.wxml
@@ -1,40 +1,8 @@
-
-
-
- ‹
-
- {{isEdit ? '编辑地址' : '新增地址'}}
-
+
+
-
-
-
-
- 收货人
-
-
-
- 手机号码
-
-
-
- 所在地区(选填)
-
-
- {{region[0] && region[1] && region[2] ? region[0] + ' ' + region[1] + ' ' + region[2] : '选填,可不选'}}
-
-
-
-
- 详细地址
-
-
-
- 设为默认地址
-
-
-
-
- 保存
+ 待开发
diff --git a/miniprogram/pages/address-edit/address-edit.wxss b/miniprogram/pages/address-edit/address-edit.wxss
index b0a68db5..e767c367 100644
--- a/miniprogram/pages/address-edit/address-edit.wxss
+++ b/miniprogram/pages/address-edit/address-edit.wxss
@@ -1,21 +1,8 @@
-.page { min-height: 100vh; background: #000; padding-bottom: 120rpx; }
-.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; }
-.nav-back { width: 64rpx; height: 64rpx; display: flex; align-items: center; justify-content: center; }
-.back-icon { font-size: 40rpx; color: rgba(255,255,255,0.6); }
-.nav-title { font-size: 34rpx; font-weight: 600; color: #fff; }
-.nav-placeholder { width: 64rpx; }
-
-.form { margin: 24rpx; background: #1c1c1e; border-radius: 24rpx; overflow: hidden; border: 2rpx solid rgba(0,206,209,0.2); }
-.form-item { display: flex; align-items: center; padding: 28rpx 24rpx; border-bottom: 2rpx solid rgba(255,255,255,0.06); }
-.form-item:last-child { border-bottom: none; }
-.form-item.detail { align-items: flex-start; }
-.form-item .label { width: 160rpx; font-size: 28rpx; color: rgba(255,255,255,0.8); flex-shrink: 0; }
-.form-item .input { flex: 1; font-size: 28rpx; color: #fff; }
-.form-item .picker-value { flex: 1; font-size: 28rpx; color: #fff; }
-.form-item .picker-value:empty:before { content: '请选择省市区'; color: rgba(255,255,255,0.4); }
-.form-item .textarea { flex: 1; font-size: 28rpx; color: #fff; min-height: 120rpx; }
-.form-item.switch-row { justify-content: space-between; }
-.form-item switch { transform: scale(0.9); }
-
-.btn-save { margin: 48rpx 24rpx 0; height: 88rpx; line-height: 88rpx; text-align: center; background: #00CED1; color: #000; font-size: 30rpx; font-weight: 600; border-radius: 44rpx; }
-.btn-save.disabled { opacity: 0.6; }
+page { background: #000; color: #fff; }
+.page { min-height: 100vh; }
+.nav-placeholder { width: 100%; }
+.header { display: flex; align-items: center; padding: 24rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
+.nav-back { font-size: 32rpx; color: #00CED1; margin-right: 24rpx; }
+.header-title { flex: 1; text-align: center; font-size: 34rpx; color: #00CED1; }
+.placeholder-body { padding: 48rpx; text-align: center; color: rgba(255,255,255,0.4); font-size: 28rpx; }
+.container { padding: 32rpx; }
diff --git a/miniprogram/pages/address-list/address-list.js b/miniprogram/pages/address-list/address-list.js
index 69317e65..e0036c49 100644
--- a/miniprogram/pages/address-list/address-list.js
+++ b/miniprogram/pages/address-list/address-list.js
@@ -1,83 +1,68 @@
-/**
- * Soul创业派对 - 收货地址列表(增删改查)
- */
+// pages/address-list/address-list.js
const app = getApp()
Page({
data: {
statusBarHeight: 44,
- list: [],
- loading: true
+ navBarHeight: 88
},
- onLoad() {
- this.setData({ statusBarHeight: app.globalData.statusBarHeight })
- this.loadList()
- },
-
- onShow() {
- this.loadList()
- },
-
- async loadList() {
- const userId = app.globalData.userInfo?.id
- if (!userId) {
- this.setData({ list: [], loading: false })
- return
- }
- this.setData({ loading: true })
- try {
- const res = await app.request('/api/user/addresses', { data: { userId } })
- if (res.success && res.list) {
- this.setData({ list: res.list, loading: false })
- } else {
- this.setData({ list: [], loading: false })
- }
- } catch (e) {
- this.setData({ list: [], loading: false })
- }
- },
-
- goAdd() {
- wx.navigateTo({ url: '/pages/address-edit/address-edit' })
- },
-
- goEdit(e) {
- const id = e.currentTarget.dataset.id
- wx.navigateTo({ url: '/pages/address-edit/address-edit?id=' + id })
- },
-
- async deleteAddress(e) {
- const id = e.currentTarget.dataset.id
- const that = this
- wx.showModal({
- title: '删除地址',
- content: '确定要删除该收货地址吗?',
- success(res) {
- if (!res.confirm) return
- that.doDelete(id)
- }
- })
- },
-
- async doDelete(id) {
- wx.showLoading({ title: '删除中...' })
- try {
- const res = await app.request('/api/user/addresses/' + id, { method: 'DELETE' })
- wx.hideLoading()
- if (res.success) {
- wx.showToast({ title: '已删除', icon: 'success' })
- this.loadList()
- } else {
- wx.showToast({ title: res.message || '删除失败', icon: 'none' })
- }
- } catch (e) {
- wx.hideLoading()
- wx.showToast({ title: '删除失败', icon: 'none' })
- }
+ onLoad(options) {
+ const statusBarHeight = app.globalData.statusBarHeight || 44
+ const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
+ this.setData({ statusBarHeight, navBarHeight })
},
goBack() {
- wx.navigateBack()
+ wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/my/my' }) })
+ },
+
+ /**
+ * 生命周期函数--监听页面初次渲染完成
+ */
+ onReady() {
+
+ },
+
+ /**
+ * 生命周期函数--监听页面显示
+ */
+ onShow() {
+
+ },
+
+ /**
+ * 生命周期函数--监听页面隐藏
+ */
+ onHide() {
+
+ },
+
+ /**
+ * 生命周期函数--监听页面卸载
+ */
+ onUnload() {
+
+ },
+
+ /**
+ * 页面相关事件处理函数--监听用户下拉动作
+ */
+ onPullDownRefresh() {
+
+ },
+
+ /**
+ * 页面上拉触底事件的处理函数
+ */
+ onReachBottom() {
+
+ },
+
+ /**
+ * 用户点击右上角分享
+ */
+ onShareAppMessage() {
+
}
-})
+})
\ No newline at end of file
diff --git a/miniprogram/pages/address-list/address-list.json b/miniprogram/pages/address-list/address-list.json
index ef8da4f5..e27fa26a 100644
--- a/miniprogram/pages/address-list/address-list.json
+++ b/miniprogram/pages/address-list/address-list.json
@@ -1,4 +1,4 @@
{
- "navigationBarTitleText": "收货地址",
+ "navigationBarTitleText": "地址管理",
"usingComponents": {}
}
diff --git a/miniprogram/pages/address-list/address-list.wxml b/miniprogram/pages/address-list/address-list.wxml
index f86e71ee..ebd8f73d 100644
--- a/miniprogram/pages/address-list/address-list.wxml
+++ b/miniprogram/pages/address-list/address-list.wxml
@@ -1,46 +1,8 @@
-
-
-
- ‹
-
- 收货地址
-
-
-
-
-
-
- 加载中...
-
-
-
- 📍
- 暂无收货地址
- 点击下方按钮添加
-
-
-
-
-
- {{item.fullAddress}}
-
- 编辑
- 删除
-
-
-
-
- + 新增收货地址
+
+
+ 待开发
diff --git a/miniprogram/pages/address-list/address-list.wxss b/miniprogram/pages/address-list/address-list.wxss
index b4f097fa..e767c367 100644
--- a/miniprogram/pages/address-list/address-list.wxss
+++ b/miniprogram/pages/address-list/address-list.wxss
@@ -1,44 +1,8 @@
-.page { min-height: 100vh; background: #000; padding-bottom: 120rpx; }
-.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; }
-.nav-back { width: 64rpx; height: 64rpx; display: flex; align-items: center; justify-content: center; }
-.back-icon { font-size: 40rpx; color: rgba(255,255,255,0.6); }
-.nav-title { font-size: 34rpx; font-weight: 600; color: #fff; }
-.nav-placeholder { width: 64rpx; }
-
-.content { padding: 24rpx; }
-.loading-wrap, .empty-wrap { text-align: center; padding: 80rpx 0; }
-.loading-text { color: rgba(255,255,255,0.5); font-size: 28rpx; }
-.empty-icon { font-size: 80rpx; display: block; margin-bottom: 24rpx; opacity: 0.6; }
-.empty-text { color: #fff; font-size: 30rpx; display: block; }
-.empty-tip { color: rgba(255,255,255,0.4); font-size: 26rpx; display: block; margin-top: 8rpx; }
-
-.address-list { display: flex; flex-direction: column; gap: 24rpx; }
-.address-card {
- background: #1c1c1e;
- border-radius: 24rpx;
- padding: 28rpx;
- border: 2rpx solid rgba(0,206,209,0.2);
-}
-.card-header { display: flex; align-items: center; gap: 16rpx; margin-bottom: 16rpx; }
-.card-header .name { font-size: 30rpx; font-weight: 600; color: #fff; }
-.card-header .phone { font-size: 26rpx; color: rgba(255,255,255,0.6); }
-.default-tag { margin-left: auto; padding: 4rpx 12rpx; background: rgba(0,206,209,0.25); color: #00CED1; font-size: 22rpx; border-radius: 8rpx; }
-.card-address { font-size: 26rpx; color: rgba(255,255,255,0.7); line-height: 1.5; margin-bottom: 20rpx; }
-.card-actions { display: flex; justify-content: flex-end; gap: 24rpx; }
-.action-btn { font-size: 26rpx; color: #00CED1; }
-.action-btn.delete { color: rgba(255,107,107,0.9); }
-
-.btn-add {
- position: fixed;
- bottom: 0;
- left: 24rpx;
- right: 24rpx;
- height: 88rpx;
- line-height: 88rpx;
- text-align: center;
- background: #00CED1;
- color: #000;
- font-size: 30rpx;
- font-weight: 600;
- border-radius: 44rpx;
-}
+page { background: #000; color: #fff; }
+.page { min-height: 100vh; }
+.nav-placeholder { width: 100%; }
+.header { display: flex; align-items: center; padding: 24rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
+.nav-back { font-size: 32rpx; color: #00CED1; margin-right: 24rpx; }
+.header-title { flex: 1; text-align: center; font-size: 34rpx; color: #00CED1; }
+.placeholder-body { padding: 48rpx; text-align: center; color: rgba(255,255,255,0.4); font-size: 28rpx; }
+.container { padding: 32rpx; }
diff --git a/miniprogram/pages/chapters/chapters.js b/miniprogram/pages/chapters/chapters.js
index 6ac20c0b..27babcd8 100644
--- a/miniprogram/pages/chapters/chapters.js
+++ b/miniprogram/pages/chapters/chapters.js
@@ -1,200 +1,14 @@
-/**
- * Soul创业派对 - 目录页
- * 开发: 卡若
- * 技术支持: 存客宝
- * 数据: 完整真实文章标题
- */
-
const app = getApp()
Page({
data: {
- // 系统信息
statusBarHeight: 44,
navBarHeight: 88,
-
- // 用户状态
- isLoggedIn: false,
+ totalSections: 62,
+ bookData: [],
+ expandedPart: 'part-1',
hasFullBook: false,
purchasedSections: [],
-
- // 书籍数据 - 完整真实标题
- totalSections: 62,
- bookData: [
- {
- id: 'part-1',
- number: '一',
- title: '真实的人',
- subtitle: '人与人之间的底层逻辑',
- chapters: [
- {
- id: 'chapter-1',
- title: '第1章|人与人之间的底层逻辑',
- sections: [
- { id: '1.1', title: '荷包:电动车出租的被动收入模式', isFree: true, price: 1 },
- { id: '1.2', title: '老墨:资源整合高手的社交方法', isFree: false, price: 1 },
- { id: '1.3', title: '笑声背后的MBTI:为什么ENTJ适合做资源,INTP适合做系统', isFree: false, price: 1 },
- { id: '1.4', title: '人性的三角结构:利益、情感、价值观', isFree: false, price: 1 },
- { id: '1.5', title: '沟通差的问题:为什么你说的别人听不懂', isFree: false, price: 1 }
- ]
- },
- {
- id: 'chapter-2',
- title: '第2章|人性困境案例',
- sections: [
- { id: '2.1', title: '相亲故事:你以为找的是人,实际是在找模式', isFree: false, price: 1 },
- { id: '2.2', title: '找工作迷茫者:为什么简历解决不了人生', isFree: false, price: 1 },
- { id: '2.3', title: '撸运费险:小钱困住大脑的真实心理', isFree: false, price: 1 },
- { id: '2.4', title: '游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力', isFree: false, price: 1 },
- { id: '2.5', title: '健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒', isFree: false, price: 1 }
- ]
- }
- ]
- },
- {
- id: 'part-2',
- number: '二',
- title: '真实的行业',
- subtitle: '电商、内容、传统行业解析',
- chapters: [
- {
- id: 'chapter-3',
- title: '第3章|电商篇',
- sections: [
- { id: '3.1', title: '3000万流水如何跑出来(退税模式解析)', isFree: false, price: 1 },
- { id: '3.2', title: '供应链之王 vs 打工人:利润不在前端', isFree: false, price: 1 },
- { id: '3.3', title: '社区团购的底层逻辑', isFree: false, price: 1 },
- { id: '3.4', title: '跨境电商与退税套利', isFree: false, price: 1 }
- ]
- },
- {
- id: 'chapter-4',
- title: '第4章|内容商业篇',
- sections: [
- { id: '4.1', title: '旅游号:30天10万粉的真实逻辑', isFree: false, price: 1 },
- { id: '4.2', title: '做号工厂:如何让一个号变成一个机器', isFree: false, price: 1 },
- { id: '4.3', title: '情绪内容为什么比专业内容更赚钱', isFree: false, price: 1 },
- { id: '4.4', title: '猫与宠物号:为什么宠物赛道永不过时', isFree: false, price: 1 },
- { id: '4.5', title: '直播间里的三种人:演员、技术工、系统流', isFree: false, price: 1 }
- ]
- },
- {
- id: 'chapter-5',
- title: '第5章|传统行业篇',
- sections: [
- { id: '5.1', title: '拍卖行抱朴:一天240万的摇号生意', isFree: false, price: 1 },
- { id: '5.2', title: '土地拍卖:招拍挂背后的游戏规则', isFree: false, price: 1 },
- { id: '5.3', title: '地摊经济数字化:一个月900块的餐车生意', isFree: false, price: 1 },
- { id: '5.4', title: '不良资产拍卖:我错过的一个亿佣金', isFree: false, price: 1 },
- { id: '5.5', title: '桶装水李总:跟物业合作的轻资产模式', isFree: false, price: 1 }
- ]
- }
- ]
- },
- {
- id: 'part-3',
- number: '三',
- title: '真实的错误',
- subtitle: '我和别人犯过的错',
- chapters: [
- {
- id: 'chapter-6',
- title: '第6章|我人生错过的4件大钱',
- sections: [
- { id: '6.1', title: '电商财税窗口:2016年的千万级机会', isFree: false, price: 1 },
- { id: '6.2', title: '供应链金融:我不懂的杠杆游戏', isFree: false, price: 1 },
- { id: '6.3', title: '内容红利:2019年我为什么没做抖音', isFree: false, price: 1 },
- { id: '6.4', title: '数据资产化:我还在观望的未来机会', isFree: false, price: 1 }
- ]
- },
- {
- id: 'chapter-7',
- title: '第7章|别人犯的错误',
- sections: [
- { id: '7.1', title: '投资房年轻人的迷茫:资金 vs 能力', isFree: false, price: 1 },
- { id: '7.2', title: '信息差骗局:永远有人靠卖学习赚钱', isFree: false, price: 1 },
- { id: '7.3', title: '在Soul找恋爱但想赚钱的人', isFree: false, price: 1 },
- { id: '7.4', title: '创业者的三种死法:冲动、轻信、没结构', isFree: false, price: 1 },
- { id: '7.5', title: '人情生意的终点:关系越多亏得越多', isFree: false, price: 1 }
- ]
- }
- ]
- },
- {
- id: 'part-4',
- number: '四',
- title: '真实的赚钱',
- subtitle: '底层结构与真实案例',
- chapters: [
- {
- id: 'chapter-8',
- title: '第8章|底层结构',
- sections: [
- { id: '8.1', title: '流量杠杆:抖音、Soul、飞书', isFree: false, price: 1 },
- { id: '8.2', title: '价格杠杆:供应链与信息差', isFree: false, price: 1 },
- { id: '8.3', title: '时间杠杆:自动化 + AI', isFree: false, price: 1 },
- { id: '8.4', title: '情绪杠杆:咨询、婚恋、生意场', isFree: false, price: 1 },
- { id: '8.5', title: '社交杠杆:认识谁比你会什么更重要', isFree: false, price: 1 },
- { id: '8.6', title: '云阿米巴:分不属于自己的钱', isFree: false, price: 1 }
- ]
- },
- {
- id: 'chapter-9',
- title: '第9章|我在Soul上亲访的赚钱案例',
- sections: [
- { id: '9.1', title: '游戏账号私域:账号即资产', isFree: false, price: 1 },
- { id: '9.2', title: '健康包模式:高复购、高毛利', isFree: false, price: 1 },
- { id: '9.3', title: '药物私域:长期关系赛道', isFree: false, price: 1 },
- { id: '9.4', title: '残疾机构合作:退税 × AI × 人力成本', isFree: false, price: 1 },
- { id: '9.5', title: '私域银行:粉丝即小股东', isFree: false, price: 1 },
- { id: '9.6', title: 'Soul派对房:陌生人成交的最快场景', isFree: false, price: 1 },
- { id: '9.7', title: '飞书中台:从聊天到成交的流程化体系', isFree: false, price: 1 },
- { id: '9.8', title: '餐饮女孩:6万营收、1万利润的死撑生意', isFree: false, price: 1 },
- { id: '9.9', title: '电竞生态:从陪玩到签约到酒店的完整链条', isFree: false, price: 1 },
- { id: '9.10', title: '淘客大佬:损耗30%的白色通道', isFree: false, price: 1 },
- { id: '9.11', title: '蔬菜供应链:农户才是最赚钱的人', isFree: false, price: 1 },
- { id: '9.12', title: '美业整合:一个人的公司如何月入十万', isFree: false, price: 1 },
- { id: '9.13', title: 'AI工具推广:一个隐藏的高利润赛道', isFree: false, price: 1 },
- { id: '9.14', title: '大健康私域:一个月150万的70后', isFree: false, price: 1 }
- ]
- }
- ]
- },
- {
- id: 'part-5',
- number: '五',
- title: '真实的社会',
- subtitle: '未来职业与商业生态',
- chapters: [
- {
- id: 'chapter-10',
- title: '第10章|未来职业的变化趋势',
- sections: [
- { id: '10.1', title: 'AI时代:哪些工作会消失,哪些会崛起', isFree: false, price: 1 },
- { id: '10.2', title: '一人公司:为什么越来越多人选择单干', isFree: false, price: 1 },
- { id: '10.3', title: '为什么链接能力会成为第一价值', isFree: false, price: 1 },
- { id: '10.4', title: '新型公司:Soul-飞书-线下的三位一体', isFree: false, price: 1 }
- ]
- },
- {
- id: 'chapter-11',
- title: '第11章|中国社会商业生态的未来',
- sections: [
- { id: '11.1', title: '私域经济:为什么流量越来越贵', isFree: false, price: 1 },
- { id: '11.2', title: '银发经济与孤独经济:两个被忽视的万亿市场', isFree: false, price: 1 },
- { id: '11.3', title: '流量红利的终局', isFree: false, price: 1 },
- { id: '11.4', title: '大模型 + 供应链的组合拳', isFree: false, price: 1 },
- { id: '11.5', title: '社会分层的最终逻辑', isFree: false, price: 1 }
- ]
- }
- ]
- }
- ],
-
- // 展开状态
- expandedPart: 'part-1',
-
- // 附录
appendixList: [
{ id: 'appendix-1', title: '附录1|Soul派对房精选对话' },
{ id: 'appendix-2', title: '附录2|创业者自检清单' },
@@ -203,49 +17,114 @@ Page({
},
onLoad() {
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight,
- navBarHeight: app.globalData.navBarHeight
- })
- this.updateUserStatus()
+ this.setNavBarHeight()
+ this.loadChapters()
+ this.syncUserStatus()
},
onShow() {
- // 设置TabBar选中状态
- if (typeof this.getTabBar === 'function' && this.getTabBar()) {
- this.getTabBar().setData({ selected: 1 })
- }
- this.updateUserStatus()
+ if (typeof this.getTabBar === 'function' && this.getTabBar()) this.getTabBar().setData({ selected: 1 })
+ this.setNavBarHeight()
+ this.syncUserStatus()
},
- // 更新用户状态
- updateUserStatus() {
- const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData
- this.setData({ isLoggedIn, hasFullBook, purchasedSections })
+ setNavBarHeight() {
+ const statusBarHeight = app.globalData.statusBarHeight || 44
+ const navBarHeight = app.globalData.navBarHeight || (statusBarHeight + 44)
+ this.setData({ statusBarHeight, navBarHeight })
},
- // 切换展开状态
- togglePart(e) {
- const partId = e.currentTarget.dataset.id
- this.setData({
- expandedPart: this.data.expandedPart === partId ? null : partId
+ loadChapters() {
+ app.request('/api/book/all-chapters').then((res) => {
+ if (res && res.data && Array.isArray(res.data)) {
+ const bookData = this.normalizeBookData(res.data)
+ const totalSections = res.totalSections || res.total || res.data.length || 62
+ this.setData({ bookData, totalSections })
+ } else if (res && res.chapters) {
+ const bookData = this.normalizeBookData(res.chapters)
+ this.setData({ bookData, totalSections: res.chapters.length || 62 })
+ }
+ }).catch(() => {})
+ },
+
+ normalizeBookData(list) {
+ if (!Array.isArray(list) || list.length === 0) return []
+ const partOrder = ['part-1', 'part-2', 'part-3', 'part-4', 'part-5']
+ const partTitles = ['真实的人', '真实的行业', '真实的错误', '真实的赚钱', '真实的未来']
+ const partMap = {}
+ const subtitles = ['人性观察与社交逻辑', '社会运作的底层规则', '错过机会比失败更贵', '所有行业的杠杆结构', '人与系统的关系']
+ partOrder.forEach((id, i) => {
+ partMap[id] = {
+ id,
+ number: String(i + 1).padStart(2, '0'),
+ title: partTitles[i] || ('篇' + (i + 1)),
+ subtitle: subtitles[i] || '',
+ chapters: []
+ }
})
+ list.forEach((s) => {
+ let partId = s.partId || s.part_id
+ if (!partId && s.id) {
+ const num = String(s.id).split('.')[0]
+ partId = partOrder[parseInt(num, 10) - 1] || 'part-1'
+ }
+ partId = partId || 'part-1'
+ if (!partMap[partId]) partMap[partId] = { id: partId, number: '99', title: '其他', subtitle: '', chapters: [] }
+ const chId = s.chapterId || s.chapter_id || 'ch1'
+ let ch = partMap[partId].chapters.find(c => c.id === chId)
+ if (!ch) {
+ ch = { id: chId, title: s.chapterTitle || s.chapter_title || '章节', sections: [] }
+ partMap[partId].chapters.push(ch)
+ }
+ ch.sections.push({
+ id: s.id,
+ title: s.sectionTitle || s.title || s.section_title || '',
+ isFree: !!s.isFree || !!s.is_free,
+ price: s.price != null ? s.price : 1
+ })
+ })
+ const out = partOrder.map(id => partMap[id]).filter(p => p.chapters.length > 0)
+ out.forEach(p => {
+ p.sectionCount = p.chapters.reduce((acc, ch) => acc + (ch.sections ? ch.sections.length : 0), 0)
+ })
+ if (out.length === 0) {
+ const sections = list.map(s => ({ id: s.id, title: s.sectionTitle || s.title || s.section_title || '', isFree: !!s.isFree || !!s.is_free, price: s.price != null ? s.price : 1 }))
+ const single = { id: 'part-1', number: '01', title: '全部', subtitle: '', sectionCount: sections.length, chapters: [{ id: 'ch1', title: '章节', sections }] }
+ return [single]
+ }
+ return out
+ },
+
+ syncUserStatus() {
+ const { hasFullBook, purchasedSections } = app.globalData
+ this.setData({ hasFullBook: !!hasFullBook, purchasedSections: purchasedSections || [] })
+ },
+
+ hasPurchased(sectionId) {
+ const { hasFullBook, purchasedSections } = app.globalData
+ if (hasFullBook) return true
+ return (purchasedSections || []).indexOf(sectionId) >= 0
+ },
+
+ togglePart(e) {
+ const id = e.currentTarget.dataset.id
+ const expandedPart = this.data.expandedPart === id ? '' : id
+ this.setData({ expandedPart })
+ },
+
+ goToSearch() {
+ wx.navigateTo({ url: '/pages/search/search' })
},
- // 跳转到阅读页
goToRead(e) {
const id = e.currentTarget.dataset.id
- wx.navigateTo({ url: `/pages/read/read?id=${id}` })
+ if (!id) return
+ wx.navigateTo({ url: '/pages/read/read?id=' + encodeURIComponent(id) })
},
- // 检查是否已购买
- hasPurchased(sectionId) {
- if (this.data.hasFullBook) return true
- return this.data.purchasedSections.includes(sectionId)
- },
-
- // 返回首页
- goBack() {
- wx.switchTab({ url: '/pages/index/index' })
+ onPullDownRefresh() {
+ this.loadChapters()
+ this.syncUserStatus()
+ wx.stopPullDownRefresh()
}
})
diff --git a/miniprogram/pages/chapters/chapters.json b/miniprogram/pages/chapters/chapters.json
index e7696321..208d5f5a 100644
--- a/miniprogram/pages/chapters/chapters.json
+++ b/miniprogram/pages/chapters/chapters.json
@@ -1,6 +1,4 @@
{
- "usingComponents": {},
- "enablePullDownRefresh": false,
- "backgroundTextStyle": "light",
- "backgroundColor": "#000000"
+ "navigationBarTitleText": "目录",
+ "usingComponents": {}
}
diff --git a/miniprogram/pages/chapters/chapters.wxml b/miniprogram/pages/chapters/chapters.wxml
index 222d8fa9..1b2e459f 100644
--- a/miniprogram/pages/chapters/chapters.wxml
+++ b/miniprogram/pages/chapters/chapters.wxml
@@ -1,120 +1,84 @@
-
-
-
-
-
-
- 目录
+
+
+
-
-
-
-
-
-
- 📚
-
+
+ 📖
一场SOUL的创业实验场
- 来自Soul派对房的真实商业故事
+ 来自Soul派对房的真实商业故事
-
- {{totalSections}}
- 章节
+
+ {{totalSections}}
+ 章节
-
-
-
-
-
- 📖
- 序言|为什么我每天早上6点在Soul开播?
-
-
- 免费
- →
-
+
+
+ 📄
+ 序言|为什么我每天早上6点在Soul开播?
+ 免费
+ ›
+
-
-
-
-
-