diff --git a/.vscode/settings.json b/.vscode/settings.json index a775463..c4b0d32 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,7 +5,9 @@ "editor.codeActionsOnSave": { "source.fixAll": "explicit" }, - "[css]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, // Tailwind CSS Autocomplete, add more if used in projects "tailwindCSS.classAttributes": [ "class", @@ -14,4 +16,4 @@ "containerClassName" ], "typescript.preferences.importModuleSpecifier": "non-relative" -} +} \ No newline at end of file diff --git a/CHANGELOG b/CHANGELOG index 9e60908..ac6b914 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +## [100.1.1] - 2026-02-27 + +### Changed + +- 搜索页使用虚拟滚动,优化滚动性能 + ## [100.1.0] - 2026-02-27 ### Added diff --git a/VERSION.txt b/VERSION.txt index 5aa8d51..f890b16 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -100.1.0 \ No newline at end of file +100.1.1 \ No newline at end of file diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..d816a7a --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,39 @@ +version: '3.8' + +services: + redis: + image: redis:7-alpine + container_name: lunatv-redis + volumes: + - redis-data:/data + command: redis-server --appendonly yes + healthcheck: + test: ['CMD', 'redis-cli', 'ping'] + interval: 5s + timeout: 3s + retries: 5 + + app: + build: + context: . + dockerfile: Dockerfile + container_name: lunatv-app + ports: + - '3000:3000' + depends_on: + redis: + condition: service_healthy + environment: + # 存储类型:使用 redis + - NEXT_PUBLIC_STORAGE_TYPE=redis + # Redis 连接地址(容器内通过 service name 访问) + - REDIS_URL=redis://redis:6379 + # 站长账号 + - USERNAME=admin + # 站长密码 + - PASSWORD=admin123 + # 站点名称(可选) + - NEXT_PUBLIC_SITE_NAME=MoonTV + +volumes: + redis-data: diff --git a/package.json b/package.json index e5be487..ee4f82f 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@dnd-kit/utilities": "^3.2.2", "@headlessui/react": "^2.2.4", "@heroicons/react": "^2.2.0", + "@tanstack/react-virtual": "^3.13.19", "@types/crypto-js": "^4.2.2", "@upstash/redis": "^1.25.0", "@vidstack/react": "^1.12.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7aa9521..b679f62 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,9 @@ importers: '@heroicons/react': specifier: ^2.2.0 version: 2.2.0(react@18.3.1) + '@tanstack/react-virtual': + specifier: ^3.13.19 + version: 3.13.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/crypto-js': specifier: ^4.2.2 version: 4.2.2 @@ -1410,14 +1413,14 @@ packages: peerDependencies: tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1' - '@tanstack/react-virtual@3.13.10': - resolution: {integrity: sha512-nvrzk4E9mWB4124YdJ7/yzwou7IfHxlSef6ugCFcBfRmsnsma3heciiiV97sBNxyc3VuwtZvmwXd0aB5BpucVw==} + '@tanstack/react-virtual@3.13.19': + resolution: {integrity: sha512-KzwmU1IbE0IvCZSm6OXkS+kRdrgW2c2P3Ho3NC+zZXWK6oObv/L+lcV/2VuJ+snVESRlMJ+w/fg4WXI/JzoNGQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/virtual-core@3.13.10': - resolution: {integrity: sha512-sPEDhXREou5HyZYqSWIqdU580rsF6FGeN7vpzijmP3KTiOGjOMZASz4Y6+QKjiFQwhWrR58OP8izYaNGVxvViA==} + '@tanstack/virtual-core@3.13.19': + resolution: {integrity: sha512-/BMP7kNhzKOd7wnDeB8NrIRNLwkf5AhCYCvtfZV2GXWbBieFm/el0n6LOAXlTi6ZwHICSNnQcIxRCWHrLzDY+g==} '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} @@ -6318,7 +6321,7 @@ snapshots: '@floating-ui/react': 0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@react-aria/focus': 3.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@react-aria/interactions': 3.25.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@tanstack/react-virtual': 3.13.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tanstack/react-virtual': 3.13.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.5.0(react@18.3.1) @@ -6865,13 +6868,13 @@ snapshots: mini-svg-data-uri: 1.4.4 tailwindcss: 3.4.17(ts-node@10.9.2(@types/node@24.0.3)(typescript@4.9.5)) - '@tanstack/react-virtual@3.13.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tanstack/react-virtual@3.13.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/virtual-core': 3.13.10 + '@tanstack/virtual-core': 3.13.19 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@tanstack/virtual-core@3.13.10': {} + '@tanstack/virtual-core@3.13.19': {} '@testing-library/dom@10.4.0': dependencies: @@ -8220,8 +8223,8 @@ snapshots: '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@4.9.5) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1) @@ -8244,7 +8247,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1(supports-color@9.4.0) @@ -8255,22 +8258,22 @@ snapshots: tinyglobby: 0.2.14 unrs-resolver: 1.9.0 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 5.62.0(eslint@8.57.1)(typescript@4.9.5) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -8281,7 +8284,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 diff --git a/scripts/dev-docker.sh b/scripts/dev-docker.sh new file mode 100755 index 0000000..50bd82d --- /dev/null +++ b/scripts/dev-docker.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# 本地构建并启动 Docker 镜像 + Redis +# 用法: ./scripts/dev-docker.sh [up|down|rebuild|logs] + +set -e + +COMPOSE_FILE="docker-compose.dev.yml" + +case "${1:-up}" in + up) + echo "🚀 构建并启动服务..." + docker compose -f "$COMPOSE_FILE" up -d --build + echo "" + echo "✅ 服务已启动" + echo " 应用: http://localhost:3000" + echo " Redis: localhost:6379" + echo "" + echo " 默认账号: admin / admin123" + echo " 查看日志: ./scripts/dev-docker.sh logs" + echo " 停止服务: ./scripts/dev-docker.sh down" + ;; + down) + echo "🛑 停止并移除服务..." + docker compose -f "$COMPOSE_FILE" down + echo "✅ 已停止" + ;; + rebuild) + echo "🔄 重新构建并启动..." + docker compose -f "$COMPOSE_FILE" down + docker compose -f "$COMPOSE_FILE" up -d --build --force-recreate + echo "✅ 已重新构建并启动" + ;; + logs) + docker compose -f "$COMPOSE_FILE" logs -f + ;; + *) + echo "用法: $0 [up|down|rebuild|logs]" + exit 1 + ;; +esac diff --git a/src/app/douban/page.tsx b/src/app/douban/page.tsx index 33b0207..b8c355c 100644 --- a/src/app/douban/page.tsx +++ b/src/app/douban/page.tsx @@ -19,6 +19,7 @@ import DoubanCustomSelector from '@/components/DoubanCustomSelector'; import DoubanSelector from '@/components/DoubanSelector'; import PageLayout from '@/components/PageLayout'; import VideoCard from '@/components/VideoCard'; +import VirtualGrid from '@/components/VirtualGrid'; function DoubanPageClient() { const searchParams = useSearchParams(); @@ -754,12 +755,18 @@ function DoubanPageClient() { {/* 内容展示区域 */}