From 12282ae4b302aa5317350f0c9d09ac975f10ea33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=92=E5=AF=92?= <2596194220@qq.com> Date: Mon, 13 Apr 2026 01:01:52 +0800 Subject: [PATCH] feat: improve database module and apply untranslated color to UI --- Dockerfile | 33 +++++++---- app/page.tsx | 147 +++++++----------------------------------------- lib/database.ts | 91 +++++++++++++++++------------- 3 files changed, 95 insertions(+), 176 deletions(-) diff --git a/Dockerfile b/Dockerfile index 11cdb94..d71a70f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,16 +4,20 @@ FROM node:20-alpine AS builder WORKDIR /app -# Copy package files +# 国内镜像加速 +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories + COPY package.json pnpm-lock.yaml ./ -# Install dependencies -RUN corepack enable && pnpm install --frozen-lockfile +# 国内源 + 快速启用 pnpm +RUN corepack enable && \ + npm config set registry https://registry.npmmirror.com && \ + pnpm config set registry https://registry.npmmirror.com + +RUN pnpm install --frozen-lockfile -# Copy source code COPY . . -# Build the application RUN pnpm build # Stage 2: Runner @@ -23,21 +27,30 @@ WORKDIR /app ENV NODE_ENV=production -# Create non-root user +# 国内镜像 +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories + +# 创建用户 RUN addgroup --system --gid 1001 nodejs && \ adduser --system --uid 1001 nextjs -# Copy built application +# 复制构建产物 COPY --from=builder /app/public ./public COPY --from=builder /app/.next/standalone ./ COPY --from=builder /app/.next/static ./.next/static -# Set ownership +# ===================== 修复权限错误 ===================== +# 复制项目根目录的配置文件 +COPY --from=builder /app/config.json ./ + +# 给 nextjs 用户授权整个目录 +RUN chown -R nextjs:nodejs /app +# ======================================================== + USER nextjs EXPOSE 3000 - ENV PORT=3000 ENV HOSTNAME="0.0.0.0" -CMD ["node", "server.js"] +CMD ["node", "server.js"] \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index efe7cf0..9e8143a 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -13,7 +13,7 @@ import { Loader2, X, AlertCircle, - Download, Settings, Sparkles, + Download, Settings, } from "lucide-react"; import Link from 'next/link' import {Button} from "@/components/ui/button"; @@ -126,24 +126,6 @@ export default function RecipeTranslationPage() { orientation: "landscape", }); - // 菜品浮框状态 - const [contextMenu, setContextMenu] = useState<{ - visible: boolean; - x: number; - y: number; - dish: Dish | null; - dayCode: number; - mealCode: MealTypeCode; - }>({ visible: false, x: 0, y: 0, dish: null, dayCode: 0, mealCode: MealTypeCode.BREAKFAST }); - const [aiTranslating, setAiTranslating] = useState(false); - - // 关闭浮框 - useEffect(() => { - const handleClick = () => setContextMenu((prev) => ({ ...prev, visible: false })); - document.addEventListener("click", handleClick); - return () => document.removeEventListener("click", handleClick); - }, []); - // 加载导出配置 useEffect(() => { fetch("/api/config") @@ -160,53 +142,6 @@ export default function RecipeTranslationPage() { .catch(console.error); }, []); - // 单个菜品 AI 翻译 - const handleAiTranslateSingle = useCallback(async () => { - if (!contextMenu.dish || !contextMenu.dish.name.zh || !data) return; - - setAiTranslating(true); - try { - const response = await fetch(`/api/ai/translate?word=${encodeURIComponent(contextMenu.dish.name.zh)}`); - const result = await response.json(); - - if (result.english) { - // 更新 data 中的翻译结果 - setData((prevData) => { - if (!prevData) return null; - return { - ...prevData, - meals: prevData.meals.map((meal) => { - if (meal.dayCode === contextMenu.dayCode && meal.mealCode === contextMenu.mealCode) { - return { - ...meal, - dishList: meal.dishList.map((dish) => { - if (dish.id === contextMenu.dish?.id) { - return { - ...dish, - name: { ...dish.name, en: result.english }, - translated: true, - }; - } - return dish; - }), - }; - } - return meal; - }), - }; - }); - setContextMenu((prev) => ({ ...prev, visible: false })); - } else { - alert(result.error || "翻译失败"); - } - } catch (error) { - console.error("AI translation error:", error); - alert("翻译失败,请检查 AI 配置"); - } finally { - setAiTranslating(false); - } - }, [contextMenu.dish, contextMenu.dayCode, contextMenu.mealCode, data]); - // 收集所有未翻译的菜品名称 const getAllDishNames = useCallback((menu: WeeklyMenu): string[] => { const names: string[] = []; @@ -812,34 +747,23 @@ export default function RecipeTranslationPage() { (m) => m.dayCode === day.code && m.mealCode === meal.code ); return ( -