更新安全配置项目
This commit is contained in:
+19
-72
@@ -1,34 +1,35 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {useState, useCallback, useEffect} from "react";
|
import {useCallback, useEffect, useState} from "react";
|
||||||
import {
|
import {
|
||||||
Upload,
|
AlertCircle,
|
||||||
Sun,
|
|
||||||
Apple,
|
Apple,
|
||||||
UtensilsCrossed,
|
CheckCircle2,
|
||||||
Cookie,
|
Cookie,
|
||||||
|
Download,
|
||||||
Heart,
|
Heart,
|
||||||
Languages,
|
Languages,
|
||||||
CheckCircle2,
|
|
||||||
Loader2,
|
Loader2,
|
||||||
|
Settings,
|
||||||
|
Sun,
|
||||||
|
Upload,
|
||||||
|
UtensilsCrossed,
|
||||||
X,
|
X,
|
||||||
AlertCircle,
|
|
||||||
Download, Settings,
|
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import {Button} from "@/components/ui/button";
|
import {Button} from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
|
AlignmentType,
|
||||||
Document,
|
Document,
|
||||||
Packer,
|
Packer,
|
||||||
Table,
|
|
||||||
TableRow,
|
|
||||||
TableCell,
|
|
||||||
TextRun,
|
|
||||||
Paragraph,
|
|
||||||
AlignmentType,
|
|
||||||
WidthType,
|
|
||||||
PageOrientation,
|
PageOrientation,
|
||||||
TableLayoutType
|
Paragraph,
|
||||||
|
Table,
|
||||||
|
TableCell,
|
||||||
|
TableLayoutType,
|
||||||
|
TableRow,
|
||||||
|
TextRun,
|
||||||
|
WidthType
|
||||||
} from "docx";
|
} 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
|
// 构建 sections
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
const sections: 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({
|
sections.push({
|
||||||
properties: {
|
properties: {
|
||||||
page: {
|
page: {
|
||||||
@@ -453,7 +401,7 @@ export default function RecipeTranslationPage() {
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [
|
children: [
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: t(data.title?.zh || "食谱") || data.title?.zh || "食谱",
|
text: `${fileName || data.title?.zh || "食谱翻译"}`,
|
||||||
bold: true,
|
bold: true,
|
||||||
size: 32,
|
size: 32,
|
||||||
}),
|
}),
|
||||||
@@ -464,11 +412,10 @@ export default function RecipeTranslationPage() {
|
|||||||
new Table({
|
new Table({
|
||||||
rows: [buildHeaderRow(), ...dayRows],
|
rows: [buildHeaderRow(), ...dayRows],
|
||||||
width: {size: 100, type: WidthType.PERCENTAGE},
|
width: {size: 100, type: WidthType.PERCENTAGE},
|
||||||
layout: TableLayoutType.FIXED,
|
layout: TableLayoutType.FIXED
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const doc = new Document({
|
const doc = new Document({
|
||||||
sections,
|
sections,
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect } from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {useToast} from "@/components/ui/toast";
|
import {useToast} from "@/components/ui/toast";
|
||||||
import {Loader2} from "lucide-react";
|
import {Loader2} from "lucide-react";
|
||||||
import {Button} from "@/components/ui/button";
|
import {Button} from "@/components/ui/button";
|
||||||
|
|
||||||
interface ExportConfig {
|
interface ExportConfig {
|
||||||
untranslatedColor: string;
|
untranslatedColor: string;
|
||||||
secondPageHeader: boolean;
|
|
||||||
orientation: "portrait" | "landscape";
|
orientation: "portrait" | "landscape";
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultExportConfig: ExportConfig = {
|
const defaultExportConfig: ExportConfig = {
|
||||||
untranslatedColor: "CC0000",
|
untranslatedColor: "CC0000",
|
||||||
secondPageHeader: false,
|
|
||||||
orientation: "landscape",
|
orientation: "landscape",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,7 +28,6 @@ export default function ExportPage() {
|
|||||||
if (result.success && result.data) {
|
if (result.success && result.data) {
|
||||||
setConfig({
|
setConfig({
|
||||||
untranslatedColor: result.data.export?.untranslatedColor || defaultExportConfig.untranslatedColor,
|
untranslatedColor: result.data.export?.untranslatedColor || defaultExportConfig.untranslatedColor,
|
||||||
secondPageHeader: result.data.export?.secondPageHeader ?? defaultExportConfig.secondPageHeader,
|
|
||||||
orientation: result.data.export?.orientation || defaultExportConfig.orientation,
|
orientation: result.data.export?.orientation || defaultExportConfig.orientation,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -136,19 +133,6 @@ export default function ExportPage() {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
|
|
||||||
{/* 预览 */}
|
{/* 预览 */}
|
||||||
@@ -169,10 +153,6 @@ export default function ExportPage() {
|
|||||||
<span className="text-sm">页面方向:</span>
|
<span className="text-sm">页面方向:</span>
|
||||||
<span className="text-sm">{config.orientation === "landscape" ? "横向 (Landscape)" : "纵向 (Portrait)"}</span>
|
<span className="text-sm">{config.orientation === "landscape" ? "横向 (Landscape)" : "纵向 (Portrait)"}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="text-sm">第二页表头:</span>
|
|
||||||
<span className="text-sm">{config.secondPageHeader ? "启用" : "禁用"}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</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([
|
const eslintConfig = defineConfig([
|
||||||
...nextVitals,
|
...nextVitals,
|
||||||
...nextTs,
|
...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.
|
// Override default ignores of eslint-config-next.
|
||||||
globalIgnores([
|
globalIgnores([
|
||||||
// Default ignores of eslint-config-next:
|
// Default ignores of eslint-config-next:
|
||||||
|
|||||||
+1
-7
@@ -18,8 +18,6 @@ export interface AIConfig {
|
|||||||
export interface ExportConfig {
|
export interface ExportConfig {
|
||||||
// 未翻译文本的颜色 (hex)
|
// 未翻译文本的颜色 (hex)
|
||||||
untranslatedColor: string;
|
untranslatedColor: string;
|
||||||
// 第二页是否显示表头
|
|
||||||
secondPageHeader: boolean;
|
|
||||||
// 页面方向: portrait | landscape
|
// 页面方向: portrait | landscape
|
||||||
orientation: "portrait" | "landscape";
|
orientation: "portrait" | "landscape";
|
||||||
}
|
}
|
||||||
@@ -82,7 +80,6 @@ function initConfigFromEnv(): AppConfig {
|
|||||||
},
|
},
|
||||||
export: {
|
export: {
|
||||||
untranslatedColor: process.env.EXPORT_UNTRANSLATED_COLOR || "CC0000",
|
untranslatedColor: process.env.EXPORT_UNTRANSLATED_COLOR || "CC0000",
|
||||||
secondPageHeader: process.env.EXPORT_SECOND_PAGE_HEADER === "true",
|
|
||||||
orientation: (process.env.EXPORT_ORIENTATION as "portrait" | "landscape") || "landscape",
|
orientation: (process.env.EXPORT_ORIENTATION as "portrait" | "landscape") || "landscape",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -161,14 +158,12 @@ export function getExportConfig(): ExportConfig {
|
|||||||
const fileConfig = readConfigFile();
|
const fileConfig = readConfigFile();
|
||||||
const defaultConfig: ExportConfig = {
|
const defaultConfig: ExportConfig = {
|
||||||
untranslatedColor: "CC0000",
|
untranslatedColor: "CC0000",
|
||||||
secondPageHeader: false,
|
|
||||||
orientation: "landscape",
|
orientation: "landscape",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (fileConfig.export) {
|
if (fileConfig.export) {
|
||||||
return {
|
return {
|
||||||
untranslatedColor: fileConfig.export.untranslatedColor || defaultConfig.untranslatedColor,
|
untranslatedColor: fileConfig.export.untranslatedColor || defaultConfig.untranslatedColor,
|
||||||
secondPageHeader: fileConfig.export.secondPageHeader ?? defaultConfig.secondPageHeader,
|
|
||||||
orientation: fileConfig.export.orientation || defaultConfig.orientation,
|
orientation: fileConfig.export.orientation || defaultConfig.orientation,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -213,7 +208,7 @@ export function saveAIConfig(config: Partial<AIConfig>): AIConfig {
|
|||||||
const fileConfig = readConfigFile();
|
const fileConfig = readConfigFile();
|
||||||
const updatedConfig: AppConfig = {
|
const updatedConfig: AppConfig = {
|
||||||
db: fileConfig.db ?? {host: "127.0.0.1", port: 3306, user: "root", password: "", database: "recipe_tools"},
|
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,
|
ai: newConfig,
|
||||||
};
|
};
|
||||||
writeConfigFile(updatedConfig);
|
writeConfigFile(updatedConfig);
|
||||||
@@ -234,7 +229,6 @@ export function saveExportConfig(config: Partial<ExportConfig>): ExportConfig {
|
|||||||
|
|
||||||
const newConfig: ExportConfig = {
|
const newConfig: ExportConfig = {
|
||||||
untranslatedColor: config.untranslatedColor ?? current.untranslatedColor,
|
untranslatedColor: config.untranslatedColor ?? current.untranslatedColor,
|
||||||
secondPageHeader: config.secondPageHeader ?? current.secondPageHeader,
|
|
||||||
orientation: config.orientation ?? current.orientation,
|
orientation: config.orientation ?? current.orientation,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user