更新安全配置项目
This commit is contained in:
+19
-72
@@ -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";
|
||||
|
||||
// 餐食类型
|
||||
@@ -374,65 +375,12 @@ 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,
|
||||
},
|
||||
},
|
||||
},
|
||||
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: {
|
||||
@@ -453,7 +401,7 @@ export default function RecipeTranslationPage() {
|
||||
new Paragraph({
|
||||
children: [
|
||||
new TextRun({
|
||||
text: t(data.title?.zh || "食谱") || data.title?.zh || "食谱",
|
||||
text: `${fileName || data.title?.zh || "食谱翻译"}`,
|
||||
bold: true,
|
||||
size: 32,
|
||||
}),
|
||||
@@ -464,11 +412,10 @@ export default function RecipeTranslationPage() {
|
||||
new Table({
|
||||
rows: [buildHeaderRow(), ...dayRows],
|
||||
width: {size: 100, type: WidthType.PERCENTAGE},
|
||||
layout: TableLayoutType.FIXED,
|
||||
layout: TableLayoutType.FIXED
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
const doc = new Document({
|
||||
sections,
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
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() {
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={config.secondPageHeader}
|
||||
onChange={(e) => setConfig({ ...config, secondPageHeader: e.target.checked })}
|
||||
className="h-4 w-4 cursor-pointer rounded border-input"
|
||||
/>
|
||||
<span className="text-sm">第二页显示表头</span>
|
||||
</label>
|
||||
<p className="text-xs text-muted-foreground">启用后,如果内容超过一页,第二页及后续页面会在顶部显示表头行</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 预览 */}
|
||||
@@ -169,10 +153,6 @@ export default function ExportPage() {
|
||||
<span className="text-sm">页面方向:</span>
|
||||
<span className="text-sm">{config.orientation === "landscape" ? "横向 (Landscape)" : "纵向 (Portrait)"}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm">第二页表头:</span>
|
||||
<span className="text-sm">{config.secondPageHeader ? "启用" : "禁用"}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+19
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,21 @@ 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:
|
||||
|
||||
+1
-7
@@ -18,8 +18,6 @@ export interface AIConfig {
|
||||
export interface ExportConfig {
|
||||
// 未翻译文本的颜色 (hex)
|
||||
untranslatedColor: string;
|
||||
// 第二页是否显示表头
|
||||
secondPageHeader: boolean;
|
||||
// 页面方向: portrait | landscape
|
||||
orientation: "portrait" | "landscape";
|
||||
}
|
||||
@@ -82,7 +80,6 @@ function initConfigFromEnv(): AppConfig {
|
||||
},
|
||||
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",
|
||||
},
|
||||
};
|
||||
@@ -161,14 +158,12 @@ export function getExportConfig(): ExportConfig {
|
||||
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,
|
||||
};
|
||||
}
|
||||
@@ -213,7 +208,7 @@ export function saveAIConfig(config: Partial<AIConfig>): AIConfig {
|
||||
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" },
|
||||
export: fileConfig.export ?? {untranslatedColor: "CC0000", orientation: "landscape"},
|
||||
ai: newConfig,
|
||||
};
|
||||
writeConfigFile(updatedConfig);
|
||||
@@ -234,7 +229,6 @@ export function saveExportConfig(config: Partial<ExportConfig>): ExportConfig {
|
||||
|
||||
const newConfig: ExportConfig = {
|
||||
untranslatedColor: config.untranslatedColor ?? current.untranslatedColor,
|
||||
secondPageHeader: config.secondPageHeader ?? current.secondPageHeader,
|
||||
orientation: config.orientation ?? current.orientation,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user