From 59b3117eb925ba2f3fe77b584f185e6a3f1295d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=92=E5=AF=92?= <2596194220@qq.com> Date: Sat, 18 Apr 2026 23:52:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=AE=89=E5=85=A8=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/ai/translate/route.ts | 110 ++++++------- app/api/translate/route.ts | 70 ++++---- app/page.tsx | 165 +++++++------------ app/settings/export/page.tsx | 28 +--- config.json | 19 +++ eslint.config.mjs | 17 +- lib/config.ts | 300 +++++++++++++++++----------------- 7 files changed, 332 insertions(+), 377 deletions(-) create mode 100644 config.json diff --git a/app/api/ai/translate/route.ts b/app/api/ai/translate/route.ts index c6a8411..b4c18ee 100644 --- a/app/api/ai/translate/route.ts +++ b/app/api/ai/translate/route.ts @@ -1,68 +1,68 @@ -import { NextRequest, NextResponse } from "next/server"; -import { batchTranslateWithAI, translateWithAI } from "@/lib/ai-translate"; +import {NextRequest, NextResponse} from "next/server"; +import {batchTranslateWithAI, translateWithAI} from "@/lib/ai-translate"; // 单个翻译 export async function GET(request: NextRequest) { - const { searchParams } = new URL(request.url); - const word = searchParams.get("word"); + const {searchParams} = new URL(request.url); + const word = searchParams.get("word"); - if (!word) { - return NextResponse.json({ error: "缺少 word 参数" }, { status: 400 }); - } - - try { - const translation = await translateWithAI(word); - - if (!translation) { - return NextResponse.json({ error: "翻译失败" }, { status: 500 }); + if (!word) { + return NextResponse.json({error: "缺少 word 参数"}, {status: 400}); } - return NextResponse.json({ - word, - english: translation, - }); - } catch (error) { - console.error("AI translation error:", error); - return NextResponse.json( - { error: String(error) }, - { status: 500 } - ); - } + try { + const translation = await translateWithAI(word); + + if (!translation) { + return NextResponse.json({error: "翻译失败"}, {status: 500}); + } + + return NextResponse.json({ + word, + english: translation, + }); + } catch (error) { + console.error("AI translation error:", error); + return NextResponse.json( + {error: String(error)}, + {status: 500} + ); + } } // 批量翻译 export async function POST(request: NextRequest) { - try { - const body = await request.json(); - const { words } = body as { words: string[] }; + try { + const body = await request.json(); + const {words} = body as { words: string[] }; - if (!Array.isArray(words) || words.length === 0) { - return NextResponse.json({ error: "缺少 words 参数(数组)" }, { status: 400 }); + if (!Array.isArray(words) || words.length === 0) { + return NextResponse.json({error: "缺少 words 参数(数组)"}, {status: 400}); + } + + // 检查 API Key + if (!process.env.AI_API_KEY) { + return NextResponse.json({error: "AI 配置未完成,请先在设置中配置 AI"}, {status: 500}); + } + + const results = await batchTranslateWithAI(words); + + // 转换为响应格式 + const translations: Record = {}; + for (const word of words) { + translations[word] = results.get(word) || null; + } + + return NextResponse.json({ + translations, + total: words.length, + success: results.size, + }); + } catch (error) { + console.error("AI batch translation error:", error); + return NextResponse.json( + {error: String(error)}, + {status: 500} + ); } - - // 检查 API Key - if (!process.env.AI_API_KEY) { - return NextResponse.json({ error: "AI 配置未完成,请先在设置中配置 AI" }, { status: 500 }); - } - - const results = await batchTranslateWithAI(words); - - // 转换为响应格式 - const translations: Record = {}; - for (const word of words) { - translations[word] = results.get(word) || null; - } - - return NextResponse.json({ - translations, - total: words.length, - success: results.size, - }); - } catch (error) { - console.error("AI batch translation error:", error); - return NextResponse.json( - { error: String(error) }, - { status: 500 } - ); - } } diff --git a/app/api/translate/route.ts b/app/api/translate/route.ts index d2e61ba..e8d25d5 100644 --- a/app/api/translate/route.ts +++ b/app/api/translate/route.ts @@ -1,48 +1,48 @@ -import { NextRequest, NextResponse } from "next/server"; -import { getTranslation, getTranslations } from "@/lib/database"; +import {NextRequest, NextResponse} from "next/server"; +import {getTranslation, getTranslations} from "@/lib/database"; // 单个翻译查询 export async function GET(request: NextRequest) { - const { searchParams } = new URL(request.url); - const chineseName = searchParams.get("name"); + const {searchParams} = new URL(request.url); + const chineseName = searchParams.get("name"); - if (!chineseName) { - return NextResponse.json({ error: "缺少 name 参数" }, { status: 400 }); - } + if (!chineseName) { + return NextResponse.json({error: "缺少 name 参数"}, {status: 400}); + } - const translation = await getTranslation(chineseName); + const translation = await getTranslation(chineseName); - return NextResponse.json({ - chinese: chineseName, - english: translation, - found: translation !== null, - }); + return NextResponse.json({ + chinese: chineseName, + english: translation, + found: translation !== null, + }); } // 批量翻译查询 export async function POST(request: NextRequest) { - try { - const body = await request.json(); - const { names } = body as { names: string[] }; + try { + const body = await request.json(); + const {names} = body as { names: string[] }; - if (!names || !Array.isArray(names)) { - return NextResponse.json({ error: "缺少 names 参数(数组)" }, { status: 400 }); + if (!names || !Array.isArray(names)) { + return NextResponse.json({error: "缺少 names 参数(数组)"}, {status: 400}); + } + + const translations = await getTranslations(names); + + const result: Record = {}; + for (const name of names) { + result[name] = translations.get(name) || null; + } + + return NextResponse.json({ + translations: result, + total: names.length, + found: translations.size, + }); + } catch (error) { + console.error("Translation API error:", error); + return NextResponse.json({error: "请求格式错误"}, {status: 400}); } - - const translations = await getTranslations(names); - - const result: Record = {}; - for (const name of names) { - result[name] = translations.get(name) || null; - } - - return NextResponse.json({ - translations: result, - total: names.length, - found: translations.size, - }); - } catch (error) { - console.error("Translation API error:", error); - return NextResponse.json({ error: "请求格式错误" }, { status: 400 }); - } } diff --git a/app/page.tsx b/app/page.tsx index 9e8143a..1907de4 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,34 +1,35 @@ "use client"; -import {useState, useCallback, useEffect} from "react"; +import {useCallback, useEffect, useState} from "react"; import { - Upload, - Sun, + AlertCircle, Apple, - UtensilsCrossed, + CheckCircle2, Cookie, + Download, Heart, Languages, - CheckCircle2, Loader2, + Settings, + Sun, + Upload, + UtensilsCrossed, X, - AlertCircle, - Download, Settings, } from "lucide-react"; import Link from 'next/link' import {Button} from "@/components/ui/button"; import { + AlignmentType, Document, Packer, - Table, - TableRow, - TableCell, - TextRun, - Paragraph, - AlignmentType, - WidthType, PageOrientation, - TableLayoutType + Paragraph, + Table, + TableCell, + TableLayoutType, + TableRow, + TextRun, + WidthType } from "docx"; // 餐食类型 @@ -228,7 +229,7 @@ export default function RecipeTranslationPage() { orientation: "landscape", secondPageHeader: false, }; - const { untranslatedColor, orientation, secondPageHeader } = exportConfig; + const {untranslatedColor, orientation, secondPageHeader} = exportConfig; // 2. 收集所有需要翻译的内容(星期、餐食类型、菜品) const allNames: string[] = []; @@ -343,7 +344,7 @@ export default function RecipeTranslationPage() { const isLandscape = orientation === "landscape"; const pageWidth = isLandscape ? 11906 : 16838; const pageHeight = isLandscape ? 16838 : 11906; - + // 构建表头行 const buildHeaderRow = (): TableRow => { const headerCells: TableCell[] = []; @@ -374,102 +375,48 @@ export default function RecipeTranslationPage() { }) ); } - return new TableRow({children: headerCells, tableHeader: true}); + return new TableRow({ children: headerCells, tableHeader: false }); }; - - // 估算每页能放多少天(横向 A4 约 27cm 高度,减去标题和边距约 25cm,每行约 1cm) - const daysPerPage = 5; - + // 构建 sections - // eslint-disable-next-line @typescript-eslint/no-explicit-any const sections: any[] = []; - - if (secondPageHeader && dayRows.length > daysPerPage) { - // 分页模式:每页有表头 - const pageCount = Math.ceil(dayRows.length / daysPerPage); - - for (let page = 0; page < pageCount; page++) { - const startIdx = page * daysPerPage; - const endIdx = Math.min(startIdx + daysPerPage, dayRows.length); - const pageRows = dayRows.slice(startIdx, endIdx); - - sections.push({ - properties: { - page: { - size: { - orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT, - width: pageWidth, - height: pageHeight, - }, - margin: { - top: 700, - bottom: 700, - left: 900, - right: 900, - }, - }, + + sections.push({ + properties: { + page: { + size: { + orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT, + width: pageWidth, + height: pageHeight, }, - children: [ - ...(page === 0 ? [ - new Paragraph({ - children: [ - new TextRun({ - text: t(data.title?.zh || "食谱") || data.title?.zh || "食谱", - bold: true, - size: 32, - }), - ], - alignment: AlignmentType.CENTER, - }), - new Paragraph({children: []}), - ] : []), - new Table({ - rows: [buildHeaderRow(), ...pageRows], - width: {size: 100, type: WidthType.PERCENTAGE}, - layout: TableLayoutType.FIXED, - }), - ], - }); - } - } else { - // 普通模式:单表格无分页表头 - sections.push({ - properties: { - page: { - size: { - orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT, - width: pageWidth, - height: pageHeight, - }, - margin: { - top: 700, - bottom: 700, - left: 900, - right: 900, - }, + margin: { + top: 700, + bottom: 700, + left: 900, + right: 900, }, }, - children: [ - new Paragraph({ - children: [ - new TextRun({ - text: t(data.title?.zh || "食谱") || data.title?.zh || "食谱", - bold: true, - size: 32, - }), - ], - alignment: AlignmentType.CENTER, - }), - new Paragraph({children: []}), - new Table({ - rows: [buildHeaderRow(), ...dayRows], - width: {size: 100, type: WidthType.PERCENTAGE}, - layout: TableLayoutType.FIXED, - }), - ], - }); - } - + }, + children: [ + new Paragraph({ + children: [ + new TextRun({ + text: `${fileName || data.title?.zh || "食谱翻译"}`, + bold: true, + size: 32, + }), + ], + alignment: AlignmentType.CENTER, + }), + new Paragraph({children: []}), + new Table({ + rows: [buildHeaderRow(), ...dayRows], + width: {size: 100, type: WidthType.PERCENTAGE}, + layout: TableLayoutType.FIXED + }), + ], + }); + const doc = new Document({ sections, }); @@ -755,7 +702,7 @@ export default function RecipeTranslationPage() { key={dish.id} className="flex flex-col gap-0.5 text-sm" > - + {dish.name.zh} {dish.name.en && ( @@ -788,7 +735,7 @@ export default function RecipeTranslationPage() { key={dish.id} className="flex flex-col gap-0.5" > - + {dish.name.zh} {dish.name.en && ( diff --git a/app/settings/export/page.tsx b/app/settings/export/page.tsx index 39a5183..720551b 100644 --- a/app/settings/export/page.tsx +++ b/app/settings/export/page.tsx @@ -1,19 +1,17 @@ "use client"; -import { useState, useEffect } from "react"; -import { useToast } from "@/components/ui/toast"; -import { Loader2 } from "lucide-react"; -import { Button } from "@/components/ui/button"; +import {useEffect, useState} from "react"; +import {useToast} from "@/components/ui/toast"; +import {Loader2} from "lucide-react"; +import {Button} from "@/components/ui/button"; interface ExportConfig { untranslatedColor: string; - secondPageHeader: boolean; orientation: "portrait" | "landscape"; } const defaultExportConfig: ExportConfig = { untranslatedColor: "CC0000", - secondPageHeader: false, orientation: "landscape", }; @@ -30,7 +28,6 @@ export default function ExportPage() { if (result.success && result.data) { setConfig({ untranslatedColor: result.data.export?.untranslatedColor || defaultExportConfig.untranslatedColor, - secondPageHeader: result.data.export?.secondPageHeader ?? defaultExportConfig.secondPageHeader, orientation: result.data.export?.orientation || defaultExportConfig.orientation, }); } @@ -136,19 +133,6 @@ export default function ExportPage() { - -
- -

启用后,如果内容超过一页,第二页及后续页面会在顶部显示表头行

-
{/* 预览 */} @@ -169,10 +153,6 @@ export default function ExportPage() { 页面方向: {config.orientation === "landscape" ? "横向 (Landscape)" : "纵向 (Portrait)"} -
- 第二页表头: - {config.secondPageHeader ? "启用" : "禁用"} -
diff --git a/config.json b/config.json new file mode 100644 index 0000000..4c1efe0 --- /dev/null +++ b/config.json @@ -0,0 +1,19 @@ +{ + "db": { + "host": "100.66.1.4", + "port": 3306, + "user": "root", + "password": "sc666666", + "database": "recipe_tools" + }, + "ai": { + "apiKey": "6a81b361620a44fd86aa3c8fcd833800.PjPyiU8kSzCTviYa", + "baseUrl": "https://open.bigmodel.cn/api/paas/v4/", + "model": "GLM-4.7-Flash" + }, + "export": { + "untranslatedColor": "#FF0000", + "secondPageHeader": false, + "orientation": "landscape" + } +} \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index 05e726d..76555a4 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,10 +1,25 @@ -import { defineConfig, globalIgnores } from "eslint/config"; +import {defineConfig, globalIgnores} from "eslint/config"; import nextVitals from "eslint-config-next/core-web-vitals"; import nextTs from "eslint-config-next/typescript"; const eslintConfig = defineConfig([ ...nextVitals, ...nextTs, + { + rules: { + // Allow any type + "@typescript-eslint/no-explicit-any": "off", + // Allow unused variables with underscore prefix + "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }], + // Reduce required type annotations + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + // Allow console logs + "no-console": "off", + // Reduce strictness + "react-hooks/exhaustive-deps": "warn", + } + }, // Override default ignores of eslint-config-next. globalIgnores([ // Default ignores of eslint-config-next: diff --git a/lib/config.ts b/lib/config.ts index ffc1e63..59aee5f 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -2,32 +2,30 @@ import fs from "fs"; import path from "path"; export interface DBConfig { - host: string; - port: number; - user: string; - password: string; - database: string; + host: string; + port: number; + user: string; + password: string; + database: string; } export interface AIConfig { - apiKey: string; - baseUrl: string; - model: string; + apiKey: string; + baseUrl: string; + model: string; } export interface ExportConfig { - // 未翻译文本的颜色 (hex) - untranslatedColor: string; - // 第二页是否显示表头 - secondPageHeader: boolean; - // 页面方向: portrait | landscape - orientation: "portrait" | "landscape"; + // 未翻译文本的颜色 (hex) + untranslatedColor: string; + // 页面方向: portrait | landscape + orientation: "portrait" | "landscape"; } export interface AppConfig { - db: DBConfig; - ai: AIConfig; - export: ExportConfig; + db: DBConfig; + ai: AIConfig; + export: ExportConfig; } // 配置文件路径(单一文件) @@ -37,29 +35,29 @@ const configPath = path.join(process.cwd(), "config.json"); * 检查配置文件是否存在 */ function configFileExists(): boolean { - return fs.existsSync(configPath); + return fs.existsSync(configPath); } /** * 读取完整配置(仅从 config.json) */ function readConfigFile(): Partial { - try { - if (configFileExists()) { - const content = fs.readFileSync(configPath, "utf-8"); - return JSON.parse(content); + try { + if (configFileExists()) { + const content = fs.readFileSync(configPath, "utf-8"); + return JSON.parse(content); + } + } catch (e) { + console.error("Config read error:", e); } - } catch (e) { - console.error("Config read error:", e); - } - return {}; + return {}; } /** * 写入完整配置 */ function writeConfigFile(config: AppConfig): void { - fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8"); + fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8"); } /** @@ -67,185 +65,181 @@ function writeConfigFile(config: AppConfig): void { * 从环境变量读取初始值写入 config.json */ function initConfigFromEnv(): AppConfig { - const initialConfig: AppConfig = { - db: { - host: process.env.DB_HOST || "127.0.0.1", - port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306, - user: process.env.DB_USER || "root", - password: process.env.DB_PASSWORD || "", - database: process.env.DB_NAME || "recipe_tools", - }, - ai: { - apiKey: process.env.AI_API_KEY || "", - baseUrl: process.env.AI_BASE_URL || "https://api.openai.com/v1", - model: process.env.AI_MODEL || "gpt-3.5-turbo", - }, - export: { - untranslatedColor: process.env.EXPORT_UNTRANSLATED_COLOR || "CC0000", - secondPageHeader: process.env.EXPORT_SECOND_PAGE_HEADER === "true", - orientation: (process.env.EXPORT_ORIENTATION as "portrait" | "landscape") || "landscape", - }, - }; + const initialConfig: AppConfig = { + db: { + host: process.env.DB_HOST || "127.0.0.1", + port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 3306, + user: process.env.DB_USER || "root", + password: process.env.DB_PASSWORD || "", + database: process.env.DB_NAME || "recipe_tools", + }, + ai: { + apiKey: process.env.AI_API_KEY || "", + baseUrl: process.env.AI_BASE_URL || "https://api.openai.com/v1", + model: process.env.AI_MODEL || "gpt-3.5-turbo", + }, + export: { + untranslatedColor: process.env.EXPORT_UNTRANSLATED_COLOR || "CC0000", + orientation: (process.env.EXPORT_ORIENTATION as "portrait" | "landscape") || "landscape", + }, + }; - writeConfigFile(initialConfig); - return initialConfig; + writeConfigFile(initialConfig); + return initialConfig; } /** * 获取数据库配置(仅从 config.json) */ export function getDBConfig(): DBConfig { - // 如果配置文件不存在,从环境变量初始化 - if (!configFileExists()) { - return initConfigFromEnv().db; - } + // 如果配置文件不存在,从环境变量初始化 + if (!configFileExists()) { + return initConfigFromEnv().db; + } - const fileConfig = readConfigFile(); - const defaultConfig: DBConfig = { - host: "127.0.0.1", - port: 3306, - user: "root", - password: "", - database: "recipe_tools", - }; - - if (fileConfig.db) { - return { - host: fileConfig.db.host || defaultConfig.host, - port: fileConfig.db.port || defaultConfig.port, - user: fileConfig.db.user || defaultConfig.user, - password: fileConfig.db.password ?? defaultConfig.password, - database: fileConfig.db.database || defaultConfig.database, + const fileConfig = readConfigFile(); + const defaultConfig: DBConfig = { + host: "127.0.0.1", + port: 3306, + user: "root", + password: "", + database: "recipe_tools", }; - } - return defaultConfig; + if (fileConfig.db) { + return { + host: fileConfig.db.host || defaultConfig.host, + port: fileConfig.db.port || defaultConfig.port, + user: fileConfig.db.user || defaultConfig.user, + password: fileConfig.db.password ?? defaultConfig.password, + database: fileConfig.db.database || defaultConfig.database, + }; + } + + return defaultConfig; } /** * 获取AI翻译配置(仅从 config.json) */ export function getAIConfig(): AIConfig { - // 如果配置文件不存在,从环境变量初始化 - if (!configFileExists()) { - return initConfigFromEnv().ai; - } + // 如果配置文件不存在,从环境变量初始化 + if (!configFileExists()) { + return initConfigFromEnv().ai; + } - const fileConfig = readConfigFile(); - const defaultConfig: AIConfig = { - apiKey: "", - baseUrl: "https://api.openai.com/v1", - model: "gpt-3.5-turbo", - }; - - if (fileConfig.ai) { - return { - apiKey: fileConfig.ai.apiKey ?? defaultConfig.apiKey, - baseUrl: fileConfig.ai.baseUrl || defaultConfig.baseUrl, - model: fileConfig.ai.model || defaultConfig.model, + const fileConfig = readConfigFile(); + const defaultConfig: AIConfig = { + apiKey: "", + baseUrl: "https://api.openai.com/v1", + model: "gpt-3.5-turbo", }; - } - return defaultConfig; + if (fileConfig.ai) { + return { + apiKey: fileConfig.ai.apiKey ?? defaultConfig.apiKey, + baseUrl: fileConfig.ai.baseUrl || defaultConfig.baseUrl, + model: fileConfig.ai.model || defaultConfig.model, + }; + } + + return defaultConfig; } /** * 获取导出配置(仅从 config.json) */ export function getExportConfig(): ExportConfig { - // 如果配置文件不存在,从环境变量初始化 - if (!configFileExists()) { - return initConfigFromEnv().export; - } + // 如果配置文件不存在,从环境变量初始化 + if (!configFileExists()) { + return initConfigFromEnv().export; + } - const fileConfig = readConfigFile(); - const defaultConfig: ExportConfig = { - untranslatedColor: "CC0000", - secondPageHeader: false, - orientation: "landscape", - }; - - if (fileConfig.export) { - return { - untranslatedColor: fileConfig.export.untranslatedColor || defaultConfig.untranslatedColor, - secondPageHeader: fileConfig.export.secondPageHeader ?? defaultConfig.secondPageHeader, - orientation: fileConfig.export.orientation || defaultConfig.orientation, + const fileConfig = readConfigFile(); + const defaultConfig: ExportConfig = { + untranslatedColor: "CC0000", + orientation: "landscape", }; - } - return defaultConfig; + if (fileConfig.export) { + return { + untranslatedColor: fileConfig.export.untranslatedColor || defaultConfig.untranslatedColor, + orientation: fileConfig.export.orientation || defaultConfig.orientation, + }; + } + + return defaultConfig; } /** * 获取完整配置 */ export function getAllConfig(): AppConfig { - // 如果配置文件不存在,从环境变量初始化 - if (!configFileExists()) { - return initConfigFromEnv(); - } + // 如果配置文件不存在,从环境变量初始化 + if (!configFileExists()) { + return initConfigFromEnv(); + } - return { - db: getDBConfig(), - ai: getAIConfig(), - export: getExportConfig(), - }; + return { + db: getDBConfig(), + ai: getAIConfig(), + export: getExportConfig(), + }; } /** * 保存AI翻译配置 */ export function saveAIConfig(config: Partial): AIConfig { - // 确保配置文件存在 - if (!configFileExists()) { - initConfigFromEnv(); - } + // 确保配置文件存在 + if (!configFileExists()) { + initConfigFromEnv(); + } - const current = getAIConfig(); + const current = getAIConfig(); - const newConfig: AIConfig = { - apiKey: config.apiKey ?? current.apiKey, - baseUrl: config.baseUrl ?? current.baseUrl, - model: config.model ?? current.model, - }; + const newConfig: AIConfig = { + apiKey: config.apiKey ?? current.apiKey, + baseUrl: config.baseUrl ?? current.baseUrl, + model: config.model ?? current.model, + }; - // 读取现有配置,更新 ai 部分 - const fileConfig = readConfigFile(); - const updatedConfig: AppConfig = { - db: fileConfig.db ?? { host: "127.0.0.1", port: 3306, user: "root", password: "", database: "recipe_tools" }, - export: fileConfig.export ?? { untranslatedColor: "CC0000", secondPageHeader: false, orientation: "landscape" }, - ai: newConfig, - }; - writeConfigFile(updatedConfig); + // 读取现有配置,更新 ai 部分 + const fileConfig = readConfigFile(); + const updatedConfig: AppConfig = { + db: fileConfig.db ?? {host: "127.0.0.1", port: 3306, user: "root", password: "", database: "recipe_tools"}, + export: fileConfig.export ?? {untranslatedColor: "CC0000", orientation: "landscape"}, + ai: newConfig, + }; + writeConfigFile(updatedConfig); - return newConfig; + return newConfig; } /** * 保存导出配置 */ export function saveExportConfig(config: Partial): ExportConfig { - // 确保配置文件存在 - if (!configFileExists()) { - initConfigFromEnv(); - } + // 确保配置文件存在 + if (!configFileExists()) { + initConfigFromEnv(); + } - const current = getExportConfig(); + const current = getExportConfig(); - const newConfig: ExportConfig = { - untranslatedColor: config.untranslatedColor ?? current.untranslatedColor, - secondPageHeader: config.secondPageHeader ?? current.secondPageHeader, - orientation: config.orientation ?? current.orientation, - }; + const newConfig: ExportConfig = { + untranslatedColor: config.untranslatedColor ?? current.untranslatedColor, + orientation: config.orientation ?? current.orientation, + }; - // 读取现有配置,更新 export 部分 - const fileConfig = readConfigFile(); - const updatedConfig: AppConfig = { - db: fileConfig.db ?? { host: "127.0.0.1", port: 3306, user: "root", password: "", database: "recipe_tools" }, - ai: fileConfig.ai ?? { apiKey: "", baseUrl: "https://api.openai.com/v1", model: "gpt-3.5-turbo" }, - export: newConfig, - }; - writeConfigFile(updatedConfig); + // 读取现有配置,更新 export 部分 + const fileConfig = readConfigFile(); + const updatedConfig: AppConfig = { + db: fileConfig.db ?? {host: "127.0.0.1", port: 3306, user: "root", password: "", database: "recipe_tools"}, + ai: fileConfig.ai ?? {apiKey: "", baseUrl: "https://api.openai.com/v1", model: "gpt-3.5-turbo"}, + export: newConfig, + }; + writeConfigFile(updatedConfig); - return newConfig; + return newConfig; }