From 19d0e625db87c856377bbbc7780fa3ee21e27f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B9=98=E9=A3=8E?= Date: Thu, 5 Feb 2026 16:58:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=8D=E5=86=8D=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=9A=84=20Docker=20=E7=9B=B8=E5=85=B3=E6=96=87?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E5=8C=85=E6=8B=AC=20Dockerfile=E3=80=81docke?= =?UTF-8?q?r-compose.yml=20=E5=92=8C=20.dockerignore=EF=BC=8C=E7=AE=80?= =?UTF-8?q?=E5=8C=96=E9=A1=B9=E7=9B=AE=E7=BB=93=E6=9E=84=E4=BB=A5=E6=8F=90?= =?UTF-8?q?=E5=8D=87=E5=8F=AF=E7=BB=B4=E6=8A=A4=E6=80=A7=E3=80=82=E5=90=8C?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E6=9B=B4=E6=96=B0=20DEPLOYMENT.md=20?= =?UTF-8?q?=E6=96=87=E6=A1=A3=EF=BC=8C=E8=B0=83=E6=95=B4=20Next.js=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E8=AF=B4=E6=98=8E=EF=BC=8C=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E4=B8=8E=E6=96=B0=E9=83=A8=E7=BD=B2=E6=96=B9=E5=BC=8F=E4=B8=80?= =?UTF-8?q?=E8=87=B4=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 21 - DEPLOYMENT.md | 6 +- Dockerfile | 63 --- app/admin/layout.tsx | 2 +- scripts/devlop.py => devlop.py | 38 +- docker-compose.yml | 44 -- miniprogram/app.js | 4 +- next-env.d.ts | 2 +- package.json | 3 +- scripts/clean-standalone.js | 51 ++ scripts/prepare-standalone.js | 92 ++++ scripts/start-standalone.js | 15 +- scripts/write-standalone-warning.js | 24 + 开发文档/8、部署/Next.js宝塔部署方案.md | 146 +++++ 开发文档/8、部署/Standalone模式说明.md | 12 +- 开发文档/8、部署/WEBHOOK部署的提示词.md | 45 -- 开发文档/8、部署/Webpack代码分割问题修复.md | 337 ------------ 开发文档/8、部署/Webpack性能优化指南.md | 424 --------------- 开发文档/8、部署/soul域名访问问题排查.md | 160 ------ ... GitHub Webhook 与宝塔面板的自动化部署流程文档.md | 103 ---- 开发文档/8、部署/小程序底部导航修复说明.md | 496 ----------------- .../8、部署/小程序支付订单记录修复说明.md | 247 --------- 开发文档/8、部署/小程序样式修复说明.md | 313 ----------- 开发文档/8、部署/屏蔽Webpack警告方法.md | 191 ------- 开发文档/8、部署/当前项目部署到线上.md | 100 ---- 开发文档/8、部署/支付订单完整修复方案.md | 507 ------------------ 开发文档/8、部署/本项目部署总览.md | 27 - 开发文档/8、部署/端口配置说明.md | 187 ------- .../8、部署/管理端分销数据真实接入说明.md | 280 ---------- 开发文档/8、部署/管理端静态资源404排查.md | 263 --------- 开发文档/8、部署/订单状态同步定时任务.md | 379 ------------- 开发文档/8、部署/订单表修复执行指南.md | 287 ---------- 开发文档/8、部署/订单表状态字段修复说明.md | 242 --------- 开发文档/8、部署/订单记录修复说明.md | 307 ----------- 开发文档/8、部署/阅读逻辑分析.md | 96 ---- 35 files changed, 368 insertions(+), 5146 deletions(-) delete mode 100644 .dockerignore delete mode 100644 Dockerfile rename scripts/devlop.py => devlop.py (96%) delete mode 100644 docker-compose.yml create mode 100644 scripts/clean-standalone.js create mode 100644 scripts/prepare-standalone.js create mode 100644 scripts/write-standalone-warning.js create mode 100644 开发文档/8、部署/Next.js宝塔部署方案.md delete mode 100644 开发文档/8、部署/WEBHOOK部署的提示词.md delete mode 100644 开发文档/8、部署/Webpack代码分割问题修复.md delete mode 100644 开发文档/8、部署/Webpack性能优化指南.md delete mode 100644 开发文档/8、部署/soul域名访问问题排查.md delete mode 100644 开发文档/8、部署/基于 GitHub Webhook 与宝塔面板的自动化部署流程文档.md delete mode 100644 开发文档/8、部署/小程序底部导航修复说明.md delete mode 100644 开发文档/8、部署/小程序支付订单记录修复说明.md delete mode 100644 开发文档/8、部署/小程序样式修复说明.md delete mode 100644 开发文档/8、部署/屏蔽Webpack警告方法.md delete mode 100644 开发文档/8、部署/当前项目部署到线上.md delete mode 100644 开发文档/8、部署/支付订单完整修复方案.md delete mode 100644 开发文档/8、部署/本项目部署总览.md delete mode 100644 开发文档/8、部署/端口配置说明.md delete mode 100644 开发文档/8、部署/管理端分销数据真实接入说明.md delete mode 100644 开发文档/8、部署/管理端静态资源404排查.md delete mode 100644 开发文档/8、部署/订单状态同步定时任务.md delete mode 100644 开发文档/8、部署/订单表修复执行指南.md delete mode 100644 开发文档/8、部署/订单表状态字段修复说明.md delete mode 100644 开发文档/8、部署/订单记录修复说明.md delete mode 100644 开发文档/8、部署/阅读逻辑分析.md diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 5ec39791..00000000 --- a/.dockerignore +++ /dev/null @@ -1,21 +0,0 @@ -# 构建/运行不需要的目录,不打包进镜像 -node_modules -.next -.git -.gitignore -*.md -.env* -.DS_Store - -# 部署与开发脚本不打包 -scripts -*.sh -deploy_config.json -deploy_config.example.json -requirements-deploy.txt - -# 小程序、文档、附加模块 -miniprogram -开发文档 -addons -book diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index bb565ae3..f80ca41d 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -20,13 +20,11 @@ | 类型 | 文件/目录 | 说明 | |------|------------|------| | 总览文档 | `DEPLOYMENT.md`(本文件) | 部署步骤、环境变量、支付回调 | -| Docker | `Dockerfile` | Next.js 独立构建(`output: 'standalone'`) | -| Docker 编排 | `docker-compose.yml` | 整站容器、端口 3000、支付/基础环境变量 | -| Next 配置 | `next.config.mjs` | `output: 'standalone'` 供 Docker 使用 | +| Next 配置 | `next.config.mjs` | `output: 'standalone'` 供宝塔 standalone 部署使用 | | **宝塔部署(统一入口)** | **`scripts/devlop.py`** | **本地打包 → SSH 上传解压 → 宝塔 API 重启 Node 项目(Windows/Mac/Linux 通用)** | | 宝塔 API 模块 | `scripts/deploy_baota_pure_api.py` | 被 devlop.py 内部调用(重启 Node);也可单独用于仅重启或触发计划任务 | +| 宝塔方案说明 | `开发文档/8、部署/Next.js宝塔部署方案.md` | 宝塔首次准备与日常部署步骤 | | 宝塔自动化 | `开发文档/8、部署/Next.js自动化部署流程.md` | GitHub Webhook + 宝塔,推送即自动部署 | -| NAS 部署 | `deploy_to_nas.sh`、`redeploy.sh`、`quick_deploy.sh` | 部署到 NAS / 内网环境 | 无 `vercel.json` 时,Vercel 会按默认规则部署本仓库;若需自定义路由或头信息,可再加 `vercel.json`。 diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 36d127c8..00000000 --- a/Dockerfile +++ /dev/null @@ -1,63 +0,0 @@ -# Next.js 应用 Dockerfile -FROM node:18-alpine AS base - -# 安装依赖阶段 -FROM base AS deps -RUN apk add --no-cache libc6-compat -WORKDIR /app - -# 复制依赖文件 -COPY package.json package-lock.json* pnpm-lock.yaml* ./ - -# 优先使用pnpm,如果没有则使用npm -RUN if [ -f pnpm-lock.yaml ]; then \ - corepack enable && corepack prepare pnpm@latest --activate && \ - pnpm install --frozen-lockfile; \ - else \ - npm ci --legacy-peer-deps || npm install --legacy-peer-deps; \ - fi - -# 构建阶段 -FROM base AS builder -WORKDIR /app - -# 启用corepack(如果需要pnpm) -RUN corepack enable || true - -COPY --from=deps /app/node_modules ./node_modules -COPY . . - -# 设置环境变量 -ENV NEXT_TELEMETRY_DISABLED 1 - -# 构建应用 - 优先使用pnpm -RUN if [ -f pnpm-lock.yaml ]; then \ - corepack prepare pnpm@latest --activate && pnpm build; \ - else \ - npm run build; \ - fi - -# 生产运行阶段 -FROM base AS runner -WORKDIR /app - -ENV NODE_ENV production -ENV NEXT_TELEMETRY_DISABLED 1 - -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 nextjs - -# 复制必要文件 -COPY --from=builder /app/public ./public -COPY --from=builder /app/.next/standalone ./ -COPY --from=builder /app/.next/static ./.next/static - -USER nextjs - -# 端口由环境变量指定,不设默认值避免冲突 -# 部署时通过 docker run -e PORT=xxxx 或 docker-compose 设置 -EXPOSE ${PORT:-30006} - -ENV HOSTNAME "0.0.0.0" - -CMD ["node", "server.js"] diff --git a/app/admin/layout.tsx b/app/admin/layout.tsx index 1154a9cb..f49cae51 100644 --- a/app/admin/layout.tsx +++ b/app/admin/layout.tsx @@ -43,7 +43,7 @@ export default function AdminLayout({ children }: { children: React.ReactNode }) // 简化菜单:按功能归类,保留核心功能 // PDF需求:分账管理、分销管理、订单管理三合一 → 交易中心 const menuItems = [ - { icon: LayoutDashboard, label: "数据概览", href: "/admin" }, + { icon: LayoutDashboard, label: "数据概览123", href: "/admin" }, { icon: BookOpen, label: "内容管理", href: "/admin/content" }, { icon: Users, label: "用户管理", href: "/admin/users" }, { icon: Wallet, label: "交易中心", href: "/admin/distribution" }, // 合并:分销+订单+提现 diff --git a/scripts/devlop.py b/devlop.py similarity index 96% rename from scripts/devlop.py rename to devlop.py index b4ca59f1..afc59581 100644 --- a/scripts/devlop.py +++ b/devlop.py @@ -36,7 +36,7 @@ except ImportError: # 端口统一从环境变量 DEPLOY_PORT 读取,未设置时使用此默认值(需与 Nginx proxy_pass、ecosystem.config.cjs 一致) DEPLOY_PM2_APP = "soul" -DEFAULT_DEPLOY_PORT = 30006 +DEFAULT_DEPLOY_PORT = 3006 DEPLOY_PROJECT_PATH = "/www/wwwroot/soul" DEPLOY_SITE_URL = "https://soul.quwanzhi.com" # SSH 端口(支持环境变量 DEPLOY_SSH_PORT,未设置时默认为 22022) @@ -65,7 +65,7 @@ def get_cfg_devlop(): 实际运行目录为 dist_path(切换后新版本在 dist),宝塔 PM2 项目路径必须指向 dist_path, 否则会从错误目录启动导致 .next/static 等静态资源 404。""" cfg = get_cfg().copy() - cfg["base_path"] = os.environ.get("DEVOP_BASE_PATH", "/www/wwwroot/auto-devlop/soul") + cfg["base_path"] = os.environ.get("DEVOP_BASE_PATH", "/www/wwwroot/soul") cfg["dist_path"] = cfg["base_path"] + "/dist" cfg["dist2_path"] = cfg["base_path"] + "/dist2" return cfg @@ -297,13 +297,23 @@ def pack_standalone_tar(root): shutil.rmtree(static_dst) os.makedirs(os.path.dirname(static_dst), exist_ok=True) shutil.copytree(static_src, static_dst) - # 同步构建索引,避免 server 用错 BUILD_ID/manifest 导致静态 404 + # 同步构建索引(与 start-standalone.js 一致),避免宝塔上 server 用错导致页面空白/404 next_root = os.path.join(root, ".next") next_staging = os.path.join(staging, ".next") - for name in ["BUILD_ID", "build-manifest.json", "app-path-routes-manifest.json", "routes-manifest.json"]: + index_files = [ + "BUILD_ID", + "build-manifest.json", + "app-path-routes-manifest.json", + "routes-manifest.json", + "prerender-manifest.json", + "required-server-files.json", + "fallback-build-manifest.json", + ] + for name in index_files: src = os.path.join(next_root, name) if os.path.isfile(src): shutil.copy2(src, os.path.join(next_staging, name)) + print(" [已同步] 构建索引: BUILD_ID, build-manifest, routes-manifest 等") if os.path.isdir(public_src): shutil.copytree(public_src, os.path.join(staging, "public"), dirs_exist_ok=True) if os.path.isfile(ecosystem_src): @@ -474,13 +484,23 @@ def pack_standalone_zip(root): break os.makedirs(os.path.join(staging, ".next"), exist_ok=True) shutil.copytree(static_src, os.path.join(staging, ".next", "static"), dirs_exist_ok=True) - # 同步构建索引,避免 server 用错 BUILD_ID/manifest 导致静态 404 + # 同步构建索引(与 start-standalone.js 一致),避免宝塔上 server 用错导致页面空白/404 next_root = os.path.join(root, ".next") next_staging = os.path.join(staging, ".next") - for name in ["BUILD_ID", "build-manifest.json", "app-path-routes-manifest.json", "routes-manifest.json"]: + index_files = [ + "BUILD_ID", + "build-manifest.json", + "app-path-routes-manifest.json", + "routes-manifest.json", + "prerender-manifest.json", + "required-server-files.json", + "fallback-build-manifest.json", + ] + for name in index_files: src = os.path.join(next_root, name) if os.path.isfile(src): shutil.copy2(src, os.path.join(next_staging, name)) + print(" [已同步] 构建索引: BUILD_ID, build-manifest, routes-manifest 等") if os.path.isdir(public_src): shutil.copytree(public_src, os.path.join(staging, "public"), dirs_exist_ok=True) if os.path.isfile(ecosystem_src): @@ -669,7 +689,11 @@ def main(): args = parser.parse_args() script_dir = os.path.dirname(os.path.abspath(__file__)) - root = os.path.dirname(script_dir) + # 支持 devlop.py 在项目根或 scripts/ 下:以含 package.json 的目录为 root + if os.path.isfile(os.path.join(script_dir, "package.json")): + root = script_dir + else: + root = os.path.dirname(script_dir) if args.mode == "devlop": cfg = get_cfg_devlop() diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index a2e47325..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,44 +0,0 @@ -version: "3.8" - -services: - soul-book: - build: - context: . - dockerfile: Dockerfile - container_name: soul_book_app - restart: always - ports: - - "${APP_PORT:-30006}:${APP_PORT:-30006}" - environment: - - NODE_ENV=production - - NEXT_TELEMETRY_DISABLED=1 - - PORT=${APP_PORT:-30006} - # 支付宝配置 - - 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:${APP_PORT:-30006}/payment/success} - - ALIPAY_NOTIFY_URL=${ALIPAY_NOTIFY_URL:-http://192.168.2.201:${APP_PORT:-30006}/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:${APP_PORT:-30006}/api/payment/wechat/notify} - # 基础配置 - - NEXT_PUBLIC_BASE_URL=${NEXT_PUBLIC_BASE_URL:-http://192.168.2.201:${APP_PORT:-30006}} - volumes: - - ./book:/app/book:ro - - ./public:/app/public:ro - networks: - - nas-network - healthcheck: - test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:${APP_PORT:-30006}"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s - -networks: - nas-network: - external: true diff --git a/miniprogram/app.js b/miniprogram/app.js index 2fe0ad5a..8ba88621 100644 --- a/miniprogram/app.js +++ b/miniprogram/app.js @@ -6,8 +6,8 @@ App({ globalData: { // API基础地址 - 连接真实后端 - // baseUrl: 'https://soul.quwanzhi.com', - baseUrl: 'http://localhost:30006', + baseUrl: 'https://soul.quwanzhi.com', + // baseUrl: 'http://localhost:3006', // 小程序配置 - 真实AppID appId: 'wxb8bbb2b10dec74aa', diff --git a/next-env.d.ts b/next-env.d.ts index 9edff1c7..c4b7818f 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/package.json b/package.json index 4cdee5cd..da51146c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "version": "0.1.0", "private": true, "scripts": { - "build": "next build", + "build": "node scripts/clean-standalone.js && next build && node scripts/write-standalone-warning.js", + "postbuild": "node scripts/prepare-standalone.js", "dev": "next dev -p 3006", "lint": "eslint .", "start": "node scripts/start-standalone.js" diff --git a/scripts/clean-standalone.js b/scripts/clean-standalone.js new file mode 100644 index 00000000..0b21a28f --- /dev/null +++ b/scripts/clean-standalone.js @@ -0,0 +1,51 @@ +#!/usr/bin/env node +/** + * 构建前清理 .next/standalone,避免 Next.js build 时 EBUSY(目录被占用) + * 若曾运行 pnpm start,请先 Ctrl+C 停掉再 build + */ + +const fs = require('fs'); +const path = require('path'); + +const rootDir = path.join(__dirname, '..'); +const standaloneDir = path.join(rootDir, '.next', 'standalone'); + +const RETRIES = 5; +const DELAY_MS = 2000; + +function sleep(ms) { + return new Promise((r) => setTimeout(r, ms)); +} + +async function main() { + if (!fs.existsSync(standaloneDir)) { + return; + } + + for (let attempt = 1; attempt <= RETRIES; attempt++) { + try { + fs.rmSync(standaloneDir, { recursive: true, force: true, maxRetries: 3 }); + console.log('[clean-standalone] 已删除 .next/standalone'); + return; + } catch (e) { + if (e.code === 'EBUSY' || e.code === 'EPERM' || e.code === 'ENOTEMPTY') { + if (attempt < RETRIES) { + console.warn('[clean-standalone] 目录被占用,%ds 后重试 (%d/%d)...', DELAY_MS / 1000, attempt, RETRIES); + await sleep(DELAY_MS); + } else { + console.error('[clean-standalone] 无法删除 .next/standalone:目录被占用'); + console.error(' 请先关闭:pnpm start、或占用该目录的其它程序(如资源管理器、杀毒)'); + console.error(' 然后重新执行:pnpm build'); + process.exit(1); + } + } else { + throw e; + } + } + } +} + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/scripts/prepare-standalone.js b/scripts/prepare-standalone.js new file mode 100644 index 00000000..4d0a8b0b --- /dev/null +++ b/scripts/prepare-standalone.js @@ -0,0 +1,92 @@ +#!/usr/bin/env node +/** + * 构建后准备 standalone 目录: + * - 复制 .next/static 和 public 到 .next/standalone + * - 同步 BUILD_ID / manifest 等索引文件 + * + * 用途: + * - 本地:pnpm build 之后,可直接 node .next/standalone/server.js + * - 宝塔:若项目路径指向 .next/standalone,用 node server.js 即可 + */ + +const fs = require('fs'); +const path = require('path'); + +const rootDir = path.join(__dirname, '..'); +const standaloneDir = path.join(rootDir, '.next', 'standalone'); +const staticSrc = path.join(rootDir, '.next', 'static'); +const staticDst = path.join(standaloneDir, '.next', 'static'); +const publicSrc = path.join(rootDir, 'public'); +const publicDst = path.join(standaloneDir, 'public'); + +function copyDir(src, dst) { + if (!fs.existsSync(src)) { + console.warn(`⚠️ 跳过复制:${src} 不存在`); + return; + } + if (fs.existsSync(dst)) { + fs.rmSync(dst, { recursive: true, force: true }); + } + fs.mkdirSync(dst, { recursive: true }); + const entries = fs.readdirSync(src, { withFileTypes: true }); + for (const entry of entries) { + const srcPath = path.join(src, entry.name); + const dstPath = path.join(dst, entry.name); + if (entry.isDirectory()) { + copyDir(srcPath, dstPath); + } else { + fs.copyFileSync(srcPath, dstPath); + } + } +} + +console.log('🔧 准备 standalone 目录(复制静态资源和索引)...'); + +if (!fs.existsSync(standaloneDir)) { + console.error('❌ 错误:未找到 .next/standalone 目录,请先执行 pnpm build'); + process.exit(1); +} + +if (!fs.existsSync(staticSrc)) { + console.error('❌ 错误:.next/static 不存在,请先执行 pnpm build'); + process.exit(1); +} + +console.log(' .next/static → .next/standalone/.next/static'); +copyDir(staticSrc, staticDst); + +const chunksDir = path.join(staticDst, 'chunks'); +if (!fs.existsSync(chunksDir)) { + console.error('❌ 错误:复制后 .next/standalone/.next/static/chunks 不存在,本地/宝塔都会 404'); + process.exit(1); +} + +console.log(' public → .next/standalone/public'); +copyDir(publicSrc, publicDst); + +// 同步构建索引(与 devlop 打包一致) +const nextRoot = path.join(rootDir, '.next'); +const nextStandalone = path.join(standaloneDir, '.next'); +const indexFiles = [ + 'BUILD_ID', + 'build-manifest.json', + 'app-path-routes-manifest.json', + 'routes-manifest.json', + 'prerender-manifest.json', + 'required-server-files.json', + 'fallback-build-manifest.json', +]; +for (const name of indexFiles) { + const src = path.join(nextRoot, name); + const dst = path.join(nextStandalone, name); + if (fs.existsSync(src)) { + try { + fs.copyFileSync(src, dst); + } catch (e) { + console.warn(' [警告] 复制索引失败 %s: %s', name, e.message); + } + } +} + +console.log('✅ standalone 目录已就绪(可直接 node .next/standalone/server.js)'); + diff --git a/scripts/start-standalone.js b/scripts/start-standalone.js index 6f57ea9e..193d1220 100644 --- a/scripts/start-standalone.js +++ b/scripts/start-standalone.js @@ -46,7 +46,8 @@ function copyDir(src, dst) { } } -console.log('🚀 准备启动 Standalone 服务器...\n'); +console.log('🚀 准备启动 Standalone 服务器...'); +console.log(' (请勿在 .next/standalone 下直接 node server.js,否则会 404,须在项目根用 pnpm start)\n'); // 检查 standalone 目录 if (!fs.existsSync(standaloneDir)) { @@ -73,10 +74,18 @@ if (!fs.existsSync(chunksDir)) { console.log(' public → .next/standalone/public'); copyDir(publicSrc, publicDst); -// 同步构建索引:BUILD_ID、build-manifest 等,避免服务器用错版本导致 404 +// 同步构建索引(与 devlop 打包一致),避免本地/宝塔 server 用错导致页面空白 404 const nextRoot = path.join(rootDir, '.next'); const nextStandalone = path.join(standaloneDir, '.next'); -const indexFiles = ['BUILD_ID', 'build-manifest.json', 'app-path-routes-manifest.json', 'routes-manifest.json']; +const indexFiles = [ + 'BUILD_ID', + 'build-manifest.json', + 'app-path-routes-manifest.json', + 'routes-manifest.json', + 'prerender-manifest.json', + 'required-server-files.json', + 'fallback-build-manifest.json', +]; for (const name of indexFiles) { const src = path.join(nextRoot, name); const dst = path.join(nextStandalone, name); diff --git a/scripts/write-standalone-warning.js b/scripts/write-standalone-warning.js new file mode 100644 index 00000000..90fcecf2 --- /dev/null +++ b/scripts/write-standalone-warning.js @@ -0,0 +1,24 @@ +#!/usr/bin/env node +/** + * postbuild:在 .next/standalone 中写入提示,避免用户 cd 进去直接 node server.js 导致 404 + */ +const fs = require('fs'); +const path = require('path'); + +const standaloneDir = path.join(__dirname, '..', '.next', 'standalone'); +const msg = `不要在此目录直接运行 node server.js! +否则 .next/static 和 public 未复制,页面会空白并报 404。 + +请在项目根目录执行: + pnpm start + +或: + node scripts/start-standalone.js +`; + +const file = path.join(standaloneDir, '请勿直接运行.txt'); +if (fs.existsSync(standaloneDir)) { + try { + fs.writeFileSync(file, msg, 'utf8'); + } catch (_) {} +} diff --git a/开发文档/8、部署/Next.js宝塔部署方案.md b/开发文档/8、部署/Next.js宝塔部署方案.md new file mode 100644 index 00000000..f87a79a6 --- /dev/null +++ b/开发文档/8、部署/Next.js宝塔部署方案.md @@ -0,0 +1,146 @@ +# Next.js 宝塔面板部署方案 + +> 适用于本项目的 Next.js(`output: 'standalone'`)在宝塔面板上的部署。统一入口:`scripts/devlop.py`。 + +--- + +## 一、方案概览 + +| 方式 | 命令 | 说明 | +|------|------|------| +| **推荐:deploy(直接覆盖)** | `python scripts/devlop.py --mode deploy` | 本地 build → 打 tar.gz → SSH 上传解压到 `/www/wwwroot/soul` → 宝塔 API 重启 Node | +| **devlop(dist 切换)** | `python scripts/devlop.py` 或 `--mode devlop` | 本地 build → 打 zip → 上传到 dist2 → 服务器 pnpm install → dist↔dist2 切换 → 重启,运行目录为 `.../soul/dist` | + +- **日常建议**:用 **deploy** 即可,简单、不依赖服务器上的 pnpm。 +- **devlop** 适合需要在服务器上再装依赖或做 dist 双目录无损切换的场景。 + +--- + +## 二、宝塔首次准备(做一次即可) + +### 1. 安装/确认环境 + +- **Node.js**:宝塔 → 软件商店 → 安装「Node 版本管理器」或「PM2 管理器」,并安装 Node(建议 v18+,本项目默认 v22.14.0)。 +- **Nginx**:已安装并可添加站点。 +- **Git**(可选):若用 Webhook 拉代码再构建,需要 Git。 + +### 2. 创建网站目录 + +- 在宝塔「网站」里添加站点,或先建好目录。 +- 本项目默认路径:`/www/wwwroot/soul`(deploy 模式会把包解压到这里)。 + +### 3. 配置 Nginx 反向代理 + +- 域名示例:`soul.quwanzhi.com`。 +- 在对应站点的「设置」→「反向代理」中新增: + - **代理名称**:随意(如 soul) + - **目标 URL**:`http://127.0.0.1:30006` +- 应用端口 **30006** 需与下面 PM2 的 `PORT` 一致。 + +### 4. 添加 Node(PM2)项目 + +- 宝塔 → **Node 项目**(或 PM2 管理器)→ 添加项目: + - **项目名称**:`soul`(与脚本里 `DEPLOY_PM2_APP` 一致) + - **项目路径**: + - deploy 模式:`/www/wwwroot/soul` + - devlop 模式:`/www/wwwroot/soul/dist` + - **启动文件**:`server.js` + - **启动方式**:`node server.js`(不要用 `npm start` / `next start`,standalone 无 next 命令) + - **环境变量**:`PORT=30006`(与 Nginx 反代一致) + +也可在服务器上用 PM2 直接启动(与上面二选一): + +```bash +cd /www/wwwroot/soul +PORT=30006 pm2 start server.js --name soul +# 或 +PORT=30006 pm2 start ecosystem.config.cjs +``` + +### 5. 配置宝塔 API(供脚本重启 Node) + +- 宝塔面板 → 设置 → API 接口:开启并保存 **API 密钥**。 +- 记下面板地址(如 `https://你的服务器IP:9988`)。 +- 脚本通过环境变量使用:`BAOTA_PANEL_URL`、`BAOTA_API_KEY`。 + +--- + +## 三、日常部署 + +### 1. 本机依赖 + +```bash +pip install paramiko requests +# 或 +pip install -r requirements-deploy.txt +``` + +### 2. 执行部署(推荐 deploy) + +在**项目根目录**执行: + +```bash +# 完整流程:构建 + 上传 + 重启 +python scripts/devlop.py --mode deploy +``` + +可选参数: + +- `--no-build`:跳过本地 `pnpm build`(已有 .next/standalone 时用)。 +- `--no-upload`:只打 tar.gz,不上传(用于调试或手动上传)。 +- `--no-api`:上传后不调宝塔 API,不自动重启。 + +### 3. devlop 模式(dist 切换) + +```bash +python scripts/devlop.py +# 或 +python scripts/devlop.py --mode devlop +``` + +流程:本地 build → zip 上传到服务器 `dist2` → 在 dist2 执行 `pnpm install` → dist 与 dist2 互换 → 宝塔 API 重启,运行目录为 `.../soul/dist`。 + +### 4. 仅重启 Node(不传代码) + +代码已通过其他方式更新时,只重启 PM2: + +```bash +python scripts/deploy_baota_pure_api.py +``` + +--- + +## 四、环境变量(可选覆盖默认) + +| 变量 | 说明 | 默认示例 | +|------|------|----------| +| `DEPLOY_HOST` | 服务器 IP | 42.194.232.22 | +| `DEPLOY_USER` | SSH 用户 | root | +| `DEPLOY_PASSWORD` | SSH 密码 | - | +| `DEPLOY_SSH_KEY` | SSH 私钥路径 | - | +| `DEPLOY_SSH_PORT` | SSH 端口 | 22022 | +| `DEPLOY_PROJECT_PATH` | 项目路径(deploy) | /www/wwwroot/soul | +| `DEPLOY_PORT` / `DEPLOY_APP_PORT` | 应用端口 | 30006 | +| `DEPLOY_PM2_APP` | PM2 项目名 | soul | +| `BAOTA_PANEL_URL` | 宝塔面板地址 | https://IP:9988 | +| `BAOTA_API_KEY` | 宝塔 API 密钥 | - | +| `DEPLOY_NODE_PATH` | 服务器 Node 路径 | /www/server/nodejs/v22.14.0/bin | + +--- + +## 五、与现有文档的关系 + +- **DEPLOYMENT.md**:总览与 Vercel/环境变量等。 +- **宝塔配置检查说明.md**:Nginx/PM2/端口 排查。 +- **Next.js自动化部署流程.md**:GitHub Webhook + 宝塔自动部署(可选)。 + +--- + +## 六、注意事项 + +1. **端口一致**:Nginx `proxy_pass`、PM2 的 `PORT`、脚本中的 `DEPLOY_PORT` 必须一致(默认 30006)。 +2. **standalone 必须**:`next.config.mjs` 已设置 `output: 'standalone'`,部署用 `node server.js`,不要用 `next start`。 +3. **PM2 项目路径**:deploy 用 `/www/wwwroot/soul`;devlop 用 `/www/wwwroot/soul/dist`,否则易 404。 +4. **首次部署**:若服务器上还没有代码,先执行一次 `python scripts/devlop.py --mode deploy`,再在宝塔里确认 Node 项目路径与启动方式。 + +按以上步骤即可在宝塔上稳定跑 Next.js 前台、后台和 API。 diff --git a/开发文档/8、部署/Standalone模式说明.md b/开发文档/8、部署/Standalone模式说明.md index 90c7808c..cae7d0bc 100644 --- a/开发文档/8、部署/Standalone模式说明.md +++ b/开发文档/8、部署/Standalone模式说明.md @@ -108,14 +108,10 @@ pm2 start server.js --name soul - 不依赖全局安装的 npm 包 - 减少环境配置问题 -### 4. **适合容器化** 🐳 -- Docker 镜像更小 +### 4. **适合单机/宝塔部署** +- 产出目录小,无需完整 node_modules - 构建和运行环境分离 -- 你的项目 Dockerfile 也使用了 standalone: - -```23:23:DEPLOYMENT.md -| Docker | `Dockerfile` | Next.js 独立构建(`output: 'standalone'`) | -``` +- 本项目使用 standalone 配合宝塔 + `scripts/devlop.py` 部署(见 `Next.js宝塔部署方案.md`)。 ### 5. **安全性更好** 🛡️ - 不暴露开发依赖 @@ -216,7 +212,7 @@ EPERM: operation not permitted, symlink ## 🎯 你的项目使用 Standalone 的原因 1. **宝塔服务器部署**:减少服务器上的依赖安装 -2. **Docker 容器化**:镜像更小,启动更快 +2. **单机/宝塔部署**:目录小,启动快 3. **GitHub Actions 部署**:构建和运行环境分离 4. **团队协作**:减少环境配置问题 diff --git a/开发文档/8、部署/WEBHOOK部署的提示词.md b/开发文档/8、部署/WEBHOOK部署的提示词.md deleted file mode 100644 index 80fded66..00000000 --- a/开发文档/8、部署/WEBHOOK部署的提示词.md +++ /dev/null @@ -1,45 +0,0 @@ -# Webhook 部署提示词 - -## 输出规范 - -### 1. 核心理念 -- **一次配置,自动同步**:强调宝塔面板 + GitHub Webhook 的组合。 -- **自动化**:代码提交即部署,减少人工干预。 - -### 2. 文档结构要求 -- **I. 概述**:一句话解释“这是什么”和“有什么好处”。 -- **II. 前提条件**: - - GitHub 账号与仓库。 - - 宝塔面板(安装 Nginx/Node.js/PM2/Git)。 - - 域名解析完成。 -- **III. 流程图**:必须包含 Mermaid 流程图,展示 `Git Push -> Webhook -> 宝塔面板 -> Pull & Build` 的全过程。 -- **IV. 详细步骤**: - - **Step 1**:宝塔面板安装 Webhook 插件。 - - **Step 2**:配置脚本(提供标准脚本模板,含 `git pull`, `npm install`, `npm run build`, `pm2 reload`)。 - - **Step 3**:GitHub 仓库设置 Payload URL。 -- **V. 故障排查**:列出常见错误(如权限问题、端口冲突)。 - -### 3. 脚本模板(Next.js 示例) -\`\`\`bash -#!/bin/bash -echo "Start deployment..." -cd /www/wwwroot/your_project_path -git pull origin main -npm install -npm run build -pm2 reload all -echo "Deployment finished." -\`\`\` - -### 4. 流程图示例 -\`\`\`mermaid -graph TD - A[提交代码到GitHub] --> B{GitHub Webhook} - B --> C[发送Payload到宝塔面板] - C --> D[宝塔面板接收通知] - D --> E[触发部署脚本] - E --> F[执行 git pull] - F --> G[执行 composer install (可选)] - G --> H[清理网站缓存 (可选)] - H --> I[网站内容自动更新] -\`\`\` diff --git a/开发文档/8、部署/Webpack代码分割问题修复.md b/开发文档/8、部署/Webpack代码分割问题修复.md deleted file mode 100644 index f1544bd3..00000000 --- a/开发文档/8、部署/Webpack代码分割问题修复.md +++ /dev/null @@ -1,337 +0,0 @@ -# Webpack 代码分割问题修复 - -## 📋 问题描述 - -**症状**:微信开发者工具编译时报大量文件缺失错误 - -``` -Error: ENOENT: no such file or directory, -open 'default~chapters~index~my~read~search.js' -open 'vendors~match~read.js' -open 'vendors~index.js' -... 等等 -``` - -**原因**: -- Webpack 的 `splitChunks` 自动生成动态命名的 chunk 文件 -- 某些 chunk 在特定情况下不会生成,但代码引用了它们 -- 小程序环境对代码分割的支持不够稳定 - ---- - -## ✅ 解决方案:禁用代码分割 - -### 修改前(有问题) - -```javascript -// webpack.mp.config.js -optimization: { - runtimeChunk: false, - splitChunks: { - chunks: 'all', - minSize: 1000, - maxSize: 0, - minChunks: 1, - maxAsyncRequests: 100, - maxInitialRequests: 100, - automaticNameDelimiter: '~', - name: false, - cacheGroups: { - vendors: { - test: /[\\/]node_modules[\\/]/, - priority: -10, - name: 'vendors', - }, - default: { - minChunks: 2, - priority: -20, - reuseExistingChunk: true, - name: 'common', - }, - }, - }, -} -``` - -**问题**: -- ❌ 仍然会生成动态命名的 chunk -- ❌ 文件引用和实际生成的文件不一致 -- ❌ 小程序编译失败 - ---- - -### 修改后(已修复) - -```javascript -// webpack.mp.config.js -optimization: { - runtimeChunk: false, // 必需字段,不能修改 - // ✅ 完全禁用代码分割,避免 chunk 文件缺失 - // 小程序环境下,禁用 splitChunks 更稳定 - splitChunks: false, - - minimizer: isOptimize ? [/* ... */] : [], -} -``` - -**效果**: -- ✅ 每个页面的代码完全独立 -- ✅ 不再生成任何 chunk 文件 -- ✅ 文件结构清晰稳定 - ---- - -## 📊 修复前后对比 - -### Before(有问题) - -``` -newpp/dist/mp/common/ -├── about.js -├── chapters.js -├── index.js -├── match.js -├── my.js -├── purchases.js -├── read.js -├── referral.js -├── search.js -├── settings.js -├── vendors.js ❌ 动态生成 -├── common.js ❌ 动态生成 -├── default~chapters~index~my~read~search.js ❌ 缺失 -├── vendors~match~read.js ❌ 缺失 -├── vendors~index.js ❌ 缺失 -└── ... 等等 ❌ 大量缺失 -``` - -**问题**: -- ❌ 代码引用了很多动态生成的 chunk -- ❌ 这些 chunk 实际上没有生成 -- ❌ 导致小程序编译失败 - ---- - -### After(已修复) - -``` -newpp/dist/mp/common/ -├── about.js (224 KB) ✅ 包含所有依赖 -├── chapters.js (246 KB) ✅ 包含所有依赖 -├── index.js (315 KB) ✅ 包含所有依赖 -├── match.js (321 KB) ✅ 包含所有依赖 -├── my.js (300 KB) ✅ 包含所有依赖 -├── purchases.js (270 KB) ✅ 包含所有依赖 -├── read.js (268 KB) ✅ 包含所有依赖 -├── referral.js (276 KB) ✅ 包含所有依赖 -├── search.js (234 KB) ✅ 包含所有依赖 -└── settings.js (270 KB) ✅ 包含所有依赖 -``` - -**效果**: -- ✅ 只有 10 个文件(对应 10 个页面) -- ✅ 每个文件包含该页面的所有依赖 -- ✅ 没有任何动态 chunk 文件 -- ✅ 文件结构清晰稳定 - ---- - -## 🎯 为什么禁用代码分割? - -### 1. 小程序环境特殊性 - -**小程序限制**: -- 单个包大小限制:2MB(主包)+ 8MB(分包) -- 总包大小限制:20MB -- 文件数量限制:1000 个 - -**代码分割的问题**: -- Webpack 动态生成的 chunk 文件不稳定 -- 小程序对动态加载的支持有限 -- 容易出现文件缺失问题 - -### 2. 项目规模适中 - -**当前项目**: -- 10 个页面入口 -- 每个页面 224KB - 321KB -- 总大小约 2.7MB(符合限制) - -**结论**: -- ✅ 项目规模不大,不需要代码分割 -- ✅ 禁用代码分割更稳定 -- ✅ 每个页面独立,加载更快 - -### 3. 性能影响分析 - -**代码分割的优势**(对大型项目): -- 共享代码复用,减少总体积 -- 按需加载,提升首屏速度 - -**禁用代码分割的优势**(对中小型项目): -- ✅ 文件结构简单清晰 -- ✅ 没有动态加载的复杂性 -- ✅ 每个页面独立,不受其他页面影响 -- ✅ 编译稳定,不会出现文件缺失 - -**当前项目的选择**: -- 页面数量:10 个 -- 每个页面大小:224KB - 321KB -- 总大小:2.7MB < 10MB(符合限制) -- **结论**:禁用代码分割是最优方案 - ---- - -## 📈 文件大小分析 - -| 页面 | 文件大小 | 说明 | -|------|---------|------| -| search.js | 224 KB | 搜索页(依赖最少) | -| about.js | 224 KB | 关于页(依赖最少) | -| chapters.js | 246 KB | 目录页 | -| read.js | 268 KB | 阅读页 | -| settings.js | 270 KB | 设置页 | -| purchases.js | 270 KB | 购买记录页 | -| referral.js | 276 KB | 推广中心页 | -| my.js | 300 KB | 我的页 | -| index.js | 315 KB | 首页 | -| match.js | 321 KB | 找伙伴页(依赖最多) | - -**总计**:约 2.7 MB - -**分析**: -- ✅ 所有页面都在 500KB 以内 -- ✅ 总大小远小于 10MB 限制 -- ✅ 符合小程序性能要求 - ---- - -## 🔧 其他优化建议 - -### 1. 如果未来需要代码分割 - -**场景**:项目规模扩大,页面数量增加到 50+ - -**方案**:使用分包 - -```javascript -// miniprogram.config.js -module.exports = { - // ... - packages: [ - { - name: 'packageA', - root: 'packageA', - pages: ['pages/cat', 'pages/dog'] - }, - { - name: 'packageB', - root: 'packageB', - pages: ['pages/apple', 'pages/banana'] - } - ] -} -``` - -### 2. 代码压缩优化 - -**当前**:`isOptimize = false`(未压缩) - -**优化**:生产环境开启压缩 - -```javascript -const isOptimize = process.env.NODE_ENV === 'production' -``` - -**效果**: -- 代码体积减少 30-50% -- 提升加载速度 - -### 3. 图片资源优化 - -**建议**: -- 使用 WebP 格式 -- 图片压缩(TinyPNG) -- CDN 加载 - ---- - -## 🧪 测试清单 - -### 编译测试 - -- [x] 清理 dist 目录 -- [x] 重新编译(`pnpm run build:mp`) -- [x] 检查生成的文件列表 -- [x] 验证只有 10 个 js 文件 -- [x] 没有动态 chunk 文件 - -### 小程序测试 - -- [ ] 微信开发者工具导入项目 -- [ ] 编译成功,无错误 -- [ ] 所有页面正常加载 -- [ ] 页面跳转正常 -- [ ] API 数据加载正常 - -### 性能测试 - -- [ ] 首屏加载时间 < 2s -- [ ] 页面切换流畅 -- [ ] 内存占用正常 -- [ ] 没有卡顿 - ---- - -## 📚 相关文档 - -1. [Webpack 官方文档 - Code Splitting](https://webpack.js.org/guides/code-splitting/) -2. [小程序官方文档 - 代码包大小限制](https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages.html) - ---- - -## ✅ 总结 - -### 核心改动 - -```javascript -// ✅ 唯一的改动 -optimization: { - runtimeChunk: false, - splitChunks: false, // 禁用代码分割 -} -``` - -### 效果 - -1. ✅ **编译稳定**:不再有文件缺失错误 -2. ✅ **结构清晰**:只有 10 个页面文件 -3. ✅ **性能良好**:每个页面独立加载 -4. ✅ **易于维护**:文件关系简单 - -### 适用场景 - -**✅ 适合禁用代码分割**: -- 中小型项目(页面 < 20) -- 总代码量 < 5MB -- 追求稳定性 - -**❌ 不适合禁用代码分割**: -- 大型项目(页面 > 50) -- 总代码量 > 10MB -- 需要极致的包大小优化 - -### 当前项目 - -- 页面数量:10 个 -- 总大小:2.7 MB -- **结论**:✅ 禁用代码分割是最优方案 - ---- - -**🎉 问题彻底解决!小程序编译稳定运行。** - ---- - -**修复日期**:2026-02-03 -**文档版本**:v1.0 diff --git a/开发文档/8、部署/Webpack性能优化指南.md b/开发文档/8、部署/Webpack性能优化指南.md deleted file mode 100644 index 257d6b88..00000000 --- a/开发文档/8、部署/Webpack性能优化指南.md +++ /dev/null @@ -1,424 +0,0 @@ -# Webpack 性能优化指南 - -## 一、当前警告分析 - -### 1.1 警告内容 - -``` -WARNING in entrypoint size limit: -The following entrypoint(s) combined asset size exceeds -the recommended limit (244 KiB). -``` - -### 1.2 各入口体积 - -| 入口 | 体积 | 超出 | 主要组成 | -|------|------|------|----------| -| index | 247 KB | +3 KB | React + Zustand + 业务代码 | -| chapters | 245 KB | +1 KB | React + Zustand + bookData | -| read | 268 KB | +24 KB | React + Zustand + 阅读逻辑 | -| my | 298 KB | +54 KB | React + Zustand + 用户中心 | -| referral | 277 KB | +33 KB | React + Zustand + 推广逻辑 | -| settings | 271 KB | +27 KB | React + Zustand + 设置 | -| purchases | 271 KB | +27 KB | React + Zustand + 订单 | -| match | 319 KB | +75 KB | React + Zustand + 匹配逻辑 | -| about | ~240 KB | 正常 | React + 简单页面 | -| search | ~250 KB | +6 KB | React + 搜索逻辑 | - -### 1.3 体积分布 - -``` -vendor chunks (React + 依赖): ~200 KB -业务代码: ~50-100 KB -``` - ---- - -## 二、是否需要优化? - -### 2.1 对小程序的影响 - -**✅ 影响较小**,原因: - -1. **小程序自动分包** - - 微信小程序会自动对代码进行分包 - - 首次加载只需要主包 + 当前页面 - - 其他页面按需加载 - -2. **构建已做优化** - - 已经做了基本的代码分割 - - vendor chunks(React、Zustand)被共享 - - 业务代码按页面分割 - -3. **真实影响** - - 首屏加载时间:~1-2s(4G 网络) - - 页面切换时间:~0.5s(已缓存) - - 用户体验:良好 - -**⚠️ 建议优化**,如果: -- 用户反馈加载慢 -- 想进一步提升性能 -- 为未来扩展做准备 - ---- - -## 三、优化方案 - -### 方案 A:忽略警告(推荐,当前阶段) - -**适用场景**: -- 首次发布,功能优先 -- 用户体验已满足要求 -- 体积增长仍在可控范围 - -**操作**: -在 `webpack.mp.config.js` 添加: - -```javascript -module.exports = { - // ... 其他配置 - performance: { - hints: false, // 关闭性能警告 - }, -} -``` - -或者只提高阈值: - -```javascript -module.exports = { - // ... 其他配置 - performance: { - maxEntrypointSize: 400000, // 400 KB - maxAssetSize: 400000, - }, -} -``` - ---- - -### 方案 B:基础优化(推荐,有时间再做) - -#### 1. 移除未使用的依赖 - -检查 `package.json`,移除未使用的包: - -```bash -cd newpp -npm install -g depcheck -depcheck -``` - -#### 2. 生产模式构建 - -确保使用 production 模式(已配置): - -```javascript -// webpack.mp.config.js -mode: 'production', // ✅ 已配置 -``` - -#### 3. 压缩优化 - -检查压缩配置(已配置): - -```javascript -// webpack.mp.config.js -optimization: { - minimize: true, // ✅ 已配置 -} -``` - ---- - -### 方案 C:进阶优化(可选,Phase 6) - -#### 1. 按需加载 React 组件 - -**当前**:所有组件同步加载 - -**优化**:使用 `React.lazy()` 按需加载 - -```javascript -// Before -import ChapterContent from '../components/ChapterContent' - -// After -const ChapterContent = React.lazy(() => import('../components/ChapterContent')) - -// 使用时 -加载中...}> - - -``` - -**注意**:小程序对 `React.lazy` 支持有限,需测试。 - -#### 2. 拆分 bookData - -**当前**:所有书籍数据打包在一起 - -**优化**:按需加载章节数据 - -```javascript -// Before -import { bookData } from '../data/bookData' - -// After -// 只导出元数据,内容动态加载 -export const bookMeta = [...] - -// 章节内容按需请求 -const chapter = await request(`/api/book/chapter/${id}`) -``` - -#### 3. 优化 Zustand store - -**当前**:整个 store 都打包 - -**优化**:拆分为多个 store - -```javascript -// Before -import useStore from '../store' - -// After -import { useUserStore } from '../store/user' -import { usePurchaseStore } from '../store/purchase' -import { useConfigStore } from '../store/config' -``` - -#### 4. Tree Shaking - -确保未使用的代码被移除: - -```javascript -// webpack.mp.config.js -optimization: { - usedExports: true, - sideEffects: false, -} -``` - ---- - -### 方案 D:高级优化(可选,长期) - -#### 1. 升级到 Webpack 5 - -**优势**: -- 更好的代码分割 -- 更小的 bundle 体积 -- 持久化缓存 - -**挑战**: -- 需确认当前构建方案对 Webpack 5 的兼容性 -- 需要测试兼容性 - -#### 2. 升级到 React 18 - -**优势**: -- 更小的包体积 -- 更好的性能 - -**挑战**: -- 需要适配与全面测试 - -#### 3. 使用 Preact - -**优势**: -- Preact 体积更小(~3 KB vs 130 KB) -- API 兼容 React - -**挑战**: -- 需要修改构建配置 -- 部分 React 特性不支持 - ---- - -## 四、推荐方案 - -### 阶段 1:当前(发布前) - -✅ **方案 A:忽略警告** - -- 在 `webpack.mp.config.js` 添加 `performance: { hints: false }` -- 专注于功能完善和测试 -- 等待用户反馈 - -**理由**: -- 体积在可接受范围内 -- 功能优先级更高 -- 避免过早优化 - -### 阶段 2:第一次迭代(1-2 周后) - -⏳ **方案 B:基础优化** - -- 移除未使用的依赖 -- 检查压缩配置 -- 优化图片资源 - -**理由**: -- 低成本,高收益 -- 不影响代码结构 -- 提升加载速度 - -### 阶段 3:持续优化(1-2 月后) - -⏳ **方案 C:进阶优化** - -- 按需加载组件 -- 拆分 bookData -- 优化 Zustand store - -**理由**: -- 有用户数据支撑 -- 明确优化方向 -- 投入产出比合理 - -### 阶段 4:架构升级(3-6 月后) - -⏳ **方案 D:高级优化** - -- 考虑升级 Webpack 5 -- 考虑升级 React 18 -- 考虑使用 Preact - -**理由**: -- 技术债务清理 -- 长期可维护性 -- 性能进一步提升 - ---- - -## 五、立即执行(推荐) - -### 修改 webpack.mp.config.js - -```javascript -// newpp/build/webpack.mp.config.js - -module.exports = { - mode: 'production', - - // ... 其他配置 - - // ✅ 添加性能配置 - performance: { - hints: 'warning', // 'warning' | 'error' | false - maxEntrypointSize: 500000, // 500 KB(放宽限制) - maxAssetSize: 500000, - }, - - // ... 其他配置 -} -``` - -### 重新构建 - -```bash -cd newpp -pnpm run build:mp -``` - -**预期结果**:警告消失或减少 - ---- - -## 六、性能监控 - -### 6.1 小程序性能分析 - -**微信开发者工具**: -1. 打开调试器 -2. 点击 Performance -3. 录制页面加载过程 -4. 分析: - - 脚本执行时间 - - 渲染时间 - - 请求时间 - -### 6.2 关键指标 - -| 指标 | 目标 | 当前 | 状态 | -|------|------|------|------| -| 首屏加载 | <3s | ~2s | ✅ | -| 页面切换 | <1s | ~0.5s | ✅ | -| 接口响应 | <1s | ~0.5s | ✅ | -| 包体积 | <2MB | ~800KB | ✅ | - -### 6.3 用户体验指标 - -| 指标 | 目标 | 评估方式 | -|------|------|----------| -| 加载速度 | 快 | 用户反馈 + 性能监控 | -| 流畅度 | 流畅 | 真机测试 | -| 稳定性 | 无崩溃 | 错误监控 | - ---- - -## 七、总结 - -### 当前状态 - -✅ **可以发布** -- 体积在可接受范围内(800 KB 总计) -- 性能满足基本要求(首屏 <2s) -- 用户体验良好 - -### 建议 - -1. **立即**:关闭性能警告(`performance: { hints: false }`) -2. **短期**:收集用户反馈,观察性能数据 -3. **中期**:根据数据进行针对性优化 -4. **长期**:持续迭代,保持最佳状态 - -### 优化优先级 - -``` -P0: 功能完善 > 性能优化 -P1: 用户体验 > 包体积 -P2: 长期维护 > 短期优化 -``` - ---- - -## 附录:快速参考 - -### 关闭警告 - -```javascript -// webpack.mp.config.js -performance: { hints: false } -``` - -### 放宽限制 - -```javascript -// webpack.mp.config.js -performance: { - maxEntrypointSize: 500000, // 500 KB - maxAssetSize: 500000, -} -``` - -### 检查依赖 - -```bash -npm install -g depcheck -cd newpp -depcheck -``` - -### 分析体积 - -```bash -npm install -g webpack-bundle-analyzer -# 生成分析报告 -webpack-bundle-analyzer newpp/dist/mp/stats.json -``` - ---- - -**建议**:当前阶段关闭警告,专注于功能完善和测试。等发布后根据用户反馈再优化。 diff --git a/开发文档/8、部署/soul域名访问问题排查.md b/开发文档/8、部署/soul域名访问问题排查.md deleted file mode 100644 index dfc8694a..00000000 --- a/开发文档/8、部署/soul域名访问问题排查.md +++ /dev/null @@ -1,160 +0,0 @@ -# soul.quwanzhi.com 更新后仍显示旧站/报错 — 原因分析与排查 - -> 现象:宝塔里站点源码已更新,但访问 https://soul.quwanzhi.com 仍显示旧站点或「哎呀, 出错了」;要求域名在 Node 界面访问且后台不出错。 - ---- - -## 一、可能原因概览 - -| 原因 | 说明 | 优先级 | -|------|------|--------| -| **1. 端口不一致** | 部署脚本或 PM2 使用端口 3888,Nginx 反代 30006 → 请求到不了 Node | 高 | -| **2. 未重启 Node** | 代码已更新到服务器,但 PM2 未重启,进程仍在跑旧代码 | 高 | -| **3. 运行目录与更新目录不一致** | 默认 `devlop` 模式跑的是 `auto-devlop/soul/dist`,你改的是 `/www/wwwroot/soul` | 高 | -| **4. Nginx 未反代整站** | 只反代了 `/api`,`/_next/static` 等未到 Node → 静态 404 → 页面报错 | 中 | -| **5. PM2 工作目录错误** | 项目路径填成 `.next/standalone` 或错误目录,缺少 `.next/static` → 404/报错 | 中 | -| **6. 浏览器/CDN/Nginx 缓存** | 旧 HTML 引用已不存在的 chunk,或 CDN 缓存旧页面 | 中 | - -下面按「原因 → 如何确认 → 怎么修」说明。 - ---- - -## 二、原因 1:端口不一致(脚本曾用 3888) - -**说明**:`scripts/devlop.py` 此前默认端口为 **3888**,而 Nginx 与文档约定为 **30006**。若通过脚本创建/更新了宝塔 Node 项目,PM2 可能被配成 `PORT=3888`,应用只监听 3888;Nginx 把请求转到 30006,没有进程监听 → 502 或连接失败,页面上可能看到「出错了」或旧页。 - -**如何确认**(SSH 到服务器): - -```bash -# 应用应在 30006 监听 -ss -tlnp | grep 30006 - -# 若只有 3888 在监听,说明端口不一致 -ss -tlnp | grep 3888 -``` - -**修复**: - -- 本项目已把 `scripts/devlop.py` 的默认端口改为 **30006**,与 Nginx、`ecosystem.config.cjs` 一致。 -- 若你本机已改过环境变量,部署时请统一使用 30006: - - Windows: `set DEPLOY_PORT=30006` - - 然后执行: `python scripts/devlop.py`(或 `--mode deploy`) -- 宝塔 PM2 里为 soul 项目添加环境变量:`PORT=30006`,并重启。 - ---- - -## 三、原因 2:代码更新后未重启 Node - -**说明**:Node 进程会把代码加载进内存。只更新服务器上的文件而不重启 PM2,运行的仍是旧版本,所以你会看到「旧站」或旧逻辑触发的错误。 - -**如何确认**:看文件修改时间 vs 进程启动时间(例如 `pm2 list`、`pm2 describe soul`)。 - -**修复**: - -- 每次更新代码后必须重启 Node: - - 宝塔:PM2 管理器 → 找到 soul → 重启; - - 或 SSH:`pm2 restart soul` -- 使用部署脚本时不要加 `--no-api`,这样上传后会自动调宝塔 API 重启。 - ---- - -## 四、原因 3:运行目录与更新目录不一致(devlop vs deploy) - -**说明**:`python scripts/devlop.py` **默认是 devlop 模式**,部署目标是: - -- 上传到:`/www/wwwroot/auto-devlop/soul/dist2` -- 切换后实际运行目录:`/www/wwwroot/auto-devlop/soul/dist` -- PM2 的「项目路径」应为上述 **dist**,而不是 `/www/wwwroot/soul`。 - -如果你在宝塔文件管理里看到并更新的是 **`/www/wwwroot/soul`**,而线上跑的是 **auto-devlop/soul/dist**,那么你改的目录根本不在跑,所以「源码更新了但访问还是旧站」。 - -**如何确认**: - -- 宝塔 PM2 管理器:看 soul 项目的「项目路径」是 `.../soul` 还是 `.../auto-devlop/soul/dist`。 -- SSH:`pm2 describe soul` 看 `cwd`。 - -**修复(二选一)**: - -- **要用 devlop 模式(dist 切换)**:以后只通过 `python scripts/devlop.py` 部署,不要手动改 `/www/wwwroot/soul`;确认 PM2 项目路径为 `.../auto-devlop/soul/dist`,端口 30006。 -- **要直接更新 /www/wwwroot/soul**:用 **deploy 模式** 部署到该目录,并让 PM2 从该目录启动: - ```bash - python scripts/devlop.py --mode deploy - ``` - 并确认宝塔里 soul 的项目路径为 `/www/wwwroot/soul`,环境变量 `PORT=30006`。 - ---- - -## 五、原因 4:Nginx 未把整站反代到 Node - -**说明**:Next 的前台、后台、`/_next/static/*` 都由同一 Node 服务提供。若 Nginx 只配置了 `location /api` 反代到 30006,而 `location /` 指向静态目录或别的后端,则 HTML 可能来自旧静态或错误源,`/_next/static/...` 请求不到 Node → 404,页面报错「出错了」。 - -**如何确认**:看 Nginx 配置里是否有 `location /` 反代到 `http://127.0.0.1:30006`。 - -**修复**:保证整站走 Node,例如: - -```nginx -location / { - proxy_pass http://127.0.0.1:30006; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; -} -``` - -然后重载 Nginx。 - ---- - -## 六、原因 5:PM2 工作目录错误 - -**说明**:standalone 下 `server.js` 从**当前工作目录**读 `.next/static`、`public`。若 PM2 的「项目路径」填成 `.next/standalone` 或其它错误目录,会找不到静态资源 → 404 或运行时报错,页面上可能显示「出错了」。 - -**修复**:宝塔 PM2 里 soul 的项目路径必须是「解压后包含 `server.js`、`.next/static`、`public` 的那一层」: - -- deploy 模式:`/www/wwwroot/soul` -- devlop 模式:`/www/wwwroot/auto-devlop/soul/dist` - -不要填成 `.../soul/.next/standalone`。 - ---- - -## 七、原因 6:缓存(浏览器 / CDN / Nginx) - -**说明**:构建后 chunk 文件名会变,若浏览器或 CDN 仍用旧 HTML(引用旧 chunk),会 404 或报错。 - -**修复**: - -- 浏览器:强刷 `Ctrl+Shift+R` / `Cmd+Shift+R`,或开发者工具 Network 勾选 Disable cache 再刷新。 -- 若用了 Nginx 的 `proxy_cache` 或 CDN:对 soul 站点 purge 缓存或暂时关缓存再试。 - ---- - -## 八、推荐自检顺序(保证域名走 Node 且后台不出错) - -1. **端口**:服务器上 `ss -tlnp | grep 30006` 应有 Node 在监听;宝塔 soul 项目环境变量 `PORT=30006`。 -2. **运行目录**:PM2 的 soul 项目路径 = 你实际更新并希望生效的目录(deploy 用 `/www/wwwroot/soul`,devlop 用 `.../auto-devlop/soul/dist`)。 -3. **重启**:每次改代码或重新部署后执行一次 `pm2 restart soul`(或宝塔里重启)。 -4. **Nginx**:`location /` 反代到 `http://127.0.0.1:30006`,重载 Nginx。 -5. **静态资源**:`ls /www/wwwroot/soul/.next/static/chunks`(或当前运行目录下的 `.next/static`)应有文件;若 404 先看 [管理端静态资源404排查.md](./管理端静态资源404排查.md)。 -6. **缓存**:强刷、必要时清 CDN/Nginx 缓存。 - -使用项目提供的检查脚本(需能 SSH): - -```bash -python scripts/check_static_files.py -python scripts/check_nginx.py -``` - ---- - -## 九、总结 - -- **「源码更新了但访问还是旧站/报错」** 最常见三种情况:**端口不一致**(Node 听 3888、Nginx 转 30006)、**更新了错误目录**(改的是 soul 目录但跑的是 auto-devlop/soul/dist)、**未重启 Node**。 -- 保证 **soul.quwanzhi.com 在 Node 界面访问且后台不出错**:端口 30006、Nginx 整站反代到 30006、PM2 工作目录正确且每次发布后重启、无静态 404(正确部署 `.next/static`)。 - -脚本默认端口已改为 30006;若你之前用默认 3888 部署过,请在宝塔里把 soul 的 `PORT` 改为 30006 并重启,再按上面顺序自检一遍。 diff --git a/开发文档/8、部署/基于 GitHub Webhook 与宝塔面板的自动化部署流程文档.md b/开发文档/8、部署/基于 GitHub Webhook 与宝塔面板的自动化部署流程文档.md deleted file mode 100644 index b0c7fc3c..00000000 --- a/开发文档/8、部署/基于 GitHub Webhook 与宝塔面板的自动化部署流程文档.md +++ /dev/null @@ -1,103 +0,0 @@ -I. 概述 -本流程文档旨在指导您如何通过 GitHub 的 Webhook 功能,实现代码提交后自动同步到宝塔面板服务器,从而完成网站内容的自动化部署。这将极大地提高您的开发效率,减少手动部署带来的重复劳动和潜在错误。 -II. 前提条件 -在开始配置之前,请确保您已具备以下条件: -1. GitHub 账号:并已拥有一个托管项目代码的仓库。 -2. 宝塔面板服务器:一台已安装宝塔面板的 Linux 服务器,并确保已安装 Nginx/Apache、PHP 和 Git 环境。 -3. 项目代码:您的网站或应用代码已完整地托管在 GitHub 仓库中。 -4. Composer (如果项目是 PHP):PHP 项目需要确保服务器上安装了 Composer,用于管理项目依赖。 -III. 流程概览 -以下是自动化部署的整个流程图: -\`\`\`mermaid -graph TD - A[提交代码到GitHub] --> B{GitHub Webhook}; - B --> C[发送Payload到宝塔面板]; - C --> D[宝塔面板接收通知]; - D --> E[触发部署脚本]; - E --> F[执行 git pull]; - F --> G[执行 composer install (可选)]; - G --> H[清理网站缓存 (可选)]; - H --> I[网站内容自动更新]; -\`\`\` -流程说明: -当您将代码提交(Push)到 GitHub 仓库后,GitHub 会通过预设的 Webhook 向您的宝塔面板服务器发送一个通知(Payload)。宝塔面板接收到这个通知后,会触发一个预设的部署脚本,该脚本通常会执行 git pull 拉取最新代码,并可能运行 composer install 安装依赖,以及清理网站缓存等操作。最终,您的网站内容将自动更新到最新版本。 -IV. 具体配置步骤 -A. GitHub 仓库配置 -5. 选择或创建项目仓库: - 登录您的 GitHub 账号,选择或创建一个您需要自动部署的项目仓库。 -6. 配置 Webhook: - 这是实现自动触发部署的关键。 - * 进入您的 GitHub 仓库页面,点击顶部的 Settings(设置)。 - * 在左侧导航栏中,点击 Webhooks。 - * 点击 Add webhook(添加 Webhook)。 - * 填写以下信息: - * Payload URL:这个 URL 需要从宝塔面板中获取。请暂时留空,我们将在后面的宝塔面板配置步骤中获取并填回。 - * Content type:选择 `application/json`。 - * Secret (可选,但强烈推荐):设置一个随机且复杂的字符串作为密钥。这个密钥用于验证请求是否真的来自 GitHub,增强安全性。例如:`your_github_webhook_secret_123`。请务必牢记这个密钥,稍后宝塔面板中会用到。 - * Which events would you like to trigger this webhook?:选择 `Just the push event.`(仅推送事件)。这意味着只有当您向仓库推送代码时,Webhook 才会触发。 - * Active:确保此选项被勾选。 - * 点击 Add webhook 完成添加。 -B. 宝塔面板服务器配置 -7. 登录宝塔面板: - 使用您的账号密码登录到宝塔面板。 -8. 确保服务器环境: - 确认您的服务器已安装 Nginx (或 Apache)、PHP (版本需与项目兼容,例如 PHP 7.4) 和 Git。如果 Git 未安装,您可以通过宝塔面板的"软件商店"进行安装。 -9. 创建或选择网站: - 在宝塔面板中,进入 网站 菜单,创建或选择您要部署代码的网站。确保网站的根目录(运行目录)与您的项目部署路径一致。 -10. 设置 Git 部署: - 宝塔面板提供了便捷的 Git 部署功能。 - * 在网站列表中,找到对应的网站,点击右侧的 设置。 - * 在网站设置窗口中,找到 Git 选项卡,点击进入。 - * 启用 Git 部署:打开 Git 部署功能。 - * 选择平台:选择 `Github`。 - * 项目地址:填写您的 GitHub 仓库的 HTTPS 地址。例如:`https://github.com/your-username/your-repo-name.git`。 - * 分支:填写您希望自动部署的分支名称,通常是 `main` 或 `master`。 - * Token:填写您在 GitHub Webhook 配置中设置的 `Secret` 密钥。 - * 项目部署目录:宝塔会自动填写网站的根目录,请确保它是您项目代码实际要部署的路径。 - * 部署类型:选择 `拉取`。 - * 部署完成后执行的命令 (最重要):这是自动化部署的核心。当代码拉取完成后,宝塔会执行这些命令。您可以根据您的项目类型填写相应的命令。 - * PHP 项目示例命令(请根据您的项目实际情况调整): - \`\`\`bash - # 进入项目部署目录 (宝塔会自动切换到此目录,但为了保险可以再cd一次) - cd /www/wwwroot/your_website_directory/ - # 拉取最新代码 - git pull - # 安装/更新 Composer 依赖 (如果您的项目使用Composer) - composer install --no-dev --optimize-autoloader - # 清理框架缓存 (如果使用框架,如Laravel, Symfony等) - # 例如 Laravel: - # php artisan cache:clear - # php artisan view:clear - # php artisan config:clear - # php artisan migrate --force # 数据库迁移,请谨慎使用! - # 刷新权限 (有时需要) - # chown -R www:www . - # chmod -R 755 . - # chmod -R 777 storage bootstrap/cache # 给予某些目录写入权限 - \`\`\` - 请根据您的项目实际情况,选择并修改适合的命令。 - * Webhook URL:在您配置完上述信息并保存后,宝塔面板会为您生成一个 `WebHook地址`。复制这个 URL。 -11. 返回 GitHub 配置 Webhook: - * 回到 GitHub 仓库的 Webhook 设置页面。 - * 编辑您之前创建的 Webhook。 - * 将刚刚从宝塔面板复制的 WebHook地址 粘贴到 Payload URL 字段中。 - * 点击 Update webhook 保存。 -V. 常见问题与排查 -- Webhook 接收失败: - * 检查 GitHub Webhook 设置中的 Payload URL 是否正确,是否与宝塔面板生成的 URL 一致。 - * 检查 Secret 密钥是否在 GitHub 和宝塔面板中设置一致。 - * 检查服务器防火墙或安全组,确保 GitHub 的请求可以到达宝塔面板的 8888 端口(或其他自定义的宝塔面板端口)。 - * 查看 GitHub Webhook 的 Recent Deliveries,检查是否有红色感叹号,并查看具体错误信息。 -- 部署命令执行失败: - * 查看宝塔面板中网站的 日志,或在 计划任务 中查看 Git 部署的日志,可以找到具体的错误信息。 - * 确认部署命令中的 PHP 版本、Composer 路径等是否正确。 - * 检查项目依赖是否已正确安装,或 composer install 命令是否正确。 - * 检查文件和目录的权限,确保 www 用户(Nginx/Apache 运行用户)对项目目录有读写权限。 -- 代码未更新: - * 检查 git pull 命令是否执行成功。 - * 如果使用了缓存,确保缓存清理命令正确执行。 -VI. 注意事项 -- 安全性:`Secret` 密钥非常重要,请妥善保管,不要泄露。 -- 分支管理:建议只对生产环境或主分支(如 `main` 或 `production`)进行自动部署,开发分支可以手动部署或使用单独的测试环境。 -- 环境区分:生产环境和开发环境应严格区分,避免将开发中的不稳定代码直接部署到生产环境。 -- 备份:在进行任何部署操作之前,务必定期备份您的网站代码和数据库。 diff --git a/开发文档/8、部署/小程序底部导航修复说明.md b/开发文档/8、部署/小程序底部导航修复说明.md deleted file mode 100644 index d7a0dc12..00000000 --- a/开发文档/8、部署/小程序底部导航修复说明.md +++ /dev/null @@ -1,496 +0,0 @@ -# 小程序底部导航修复说明 - -## 问题描述 - -1. ❌ 点击底部菜单无响应 -2. ❌ 样式与原项目不一致(缺少中间凸起按钮) -3. ❌ 功能未对齐原项目设计 - ---- - -## 根本原因分析 - -### 问题 1:点击无效 - -**原因**:小程序缺少 `tabBar` 配置 - -```javascript -// ❌ 原有配置(miniprogram.config.js) -appExtraConfig: { - sitemapLocation: 'sitemap.json', - // 缺少 tabBar 配置! -}, -``` - -**为什么会无效**: -- 在微信小程序中,使用 `wx.switchTab()` 必须先在 `app.json` 中配置 `tabBar` -- 如果没有配置 `tabBar`,`wx.switchTab()` 会报错或不生效 -- 构建通过 `miniprogram.config.js` 的 `appExtraConfig` 生成 `app.json` - -### 问题 2:样式差异 - -**原项目设计**(components/bottom-nav.tsx): -- ✅ 中间的"找伙伴"按钮是**凸起的圆形按钮** -- ✅ 渐变色背景(#00CED1 → #20B2AA) -- ✅ 阴影效果 -- ✅ 精致的过渡动效 - -**原有实现**(newpp/src/components/BottomNav.jsx): -- ❌ 所有按钮样式一致,没有特殊处理 -- ❌ 使用 emoji 图标,缺少视觉层次 -- ❌ 简单的透明度变化,缺少精致感 - -### 问题 3:功能未对齐 - -**原项目功能**: -- ✅ 动态加载 `matchEnabled` 配置 -- ✅ Web 环境从 `/api/db/config` 加载 -- ✅ 小程序环境从 `app.globalData` 读取 -- ✅ 配置加载前不显示导航 - -**原有实现**: -- ⚠️ 只在小程序环境读取配置 -- ⚠️ Web 环境未实现配置加载 - ---- - -## 完整解决方案 - -### 修复 1:添加 tabBar 配置 - -**文件**:`newpp/build/miniprogram.config.js` - -```javascript -// ✅ 修复后 -appExtraConfig: { - sitemapLocation: 'sitemap.json', - - // ✅ 添加 tabBar 配置 - tabBar: { - custom: false, // 不使用自定义 tabBar,因为我们用自定义组件 - color: '#9ca3af', - selectedColor: '#00CED1', - backgroundColor: '#1c1c1e', - borderStyle: 'white', - list: [ - { - pagePath: 'pages/index/index', - text: '首页', - iconPath: 'assets/home.png', - selectedIconPath: 'assets/home-active.png', - }, - { - pagePath: 'pages/chapters/index', - text: '目录', - iconPath: 'assets/chapters.png', - selectedIconPath: 'assets/chapters-active.png', - }, - { - pagePath: 'pages/match/index', - text: '找伙伴', - iconPath: 'assets/match.png', - selectedIconPath: 'assets/match-active.png', - }, - { - pagePath: 'pages/my/index', - text: '我的', - iconPath: 'assets/my.png', - selectedIconPath: 'assets/my-active.png', - }, - ], - }, -}, -``` - -**关键点**: -1. ✅ `custom: false` - 使用系统 tabBar(会被我们的自定义组件覆盖,但配置必须存在) -2. ✅ `list` 中的 `pagePath` 必须是 tabBar 页面 -3. ✅ 图标路径稍后补充(可以是占位图) - ---- - -### 修复 2:重构 BottomNav 组件 - -**文件**:`newpp/src/components/BottomNav.jsx` - -#### 改动 1:对齐原项目样式设计 - -```javascript -// ✅ tabs 配置添加 isCenter 标记 -const tabs = [ - { id: 'home', path: '/', label: '首页', icon: '🏠' }, - { id: 'chapters', path: '/chapters', label: '目录', icon: '📚' }, - { id: 'match', path: '/match', label: '找伙伴', icon: '👥', isCenter: true }, // ✅ 中间按钮 - { id: 'my', path: '/my', label: '我的', icon: '👤' }, -] - -// ✅ 添加中间按钮样式 -const styles = { - // ... 其他样式 - - // 中间按钮容器 - centerTab: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - padding: '8px 24px', - cursor: 'pointer', - marginTop: -16, // ✅ 凸起效果 - }, - - // 中间按钮样式 - centerButton: { - width: 56, - height: 56, - borderRadius: '50%', - background: 'linear-gradient(135deg, #00CED1 0%, #20B2AA 100%)', // ✅ 渐变 - boxShadow: '0 4px 12px rgba(0,206,209,0.3)', // ✅ 阴影 - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }, -} -``` - -#### 改动 2:渲染逻辑区分普通/中间按钮 - -```javascript -return ( -
-
- {visibleTabs.map((tab) => { - const isActive = current === tab.path - - // ✅ 中间按钮特殊处理 - if (tab.isCenter) { - return ( -
handleTabClick(tab.path)} - > -
-
{tab.icon}
-
- - {tab.label} - -
- ) - } - - // ✅ 普通按钮 - return ( -
handleTabClick(tab.path)} - > - {/* ... 普通按钮内容 ... */} -
- ) - })} -
-
-) -``` - -#### 改动 3:配置加载逻辑对齐 - -```javascript -useEffect(() => { - // ✅ 小程序中读取 globalData.matchEnabled - if (isMiniProgram()) { - try { - const app = getApp() - if (app && app.globalData) { - setMatchEnabled(app.globalData.matchEnabled !== false) - } - } catch (e) { - // 如果出错,默认显示 - } finally { - setConfigLoaded(true) - } - } else { - // ✅ Web 环境加载配置 - fetch('/api/db/config') - .then((res) => res.json()) - .then((data) => { - if (data.features) { - setMatchEnabled(data.features.matchEnabled === true) - } - }) - .catch(() => { - setMatchEnabled(false) - }) - .finally(() => { - setConfigLoaded(true) - }) - } -}, []) -``` - ---- - -### 修复 3:样式优化细节 - -#### Button → Div - -```javascript -// ❌ 原有代码 - - -// ✅ 修复后 -
handleTabClick(tab.path)}> - {/* ... */} -
-``` - -**原因**: -- 小程序中 `