+ {navLinksData?.links?.filter(l => (l.toolboxGroup || 1) === 1).map((link) => (
@@ -2547,21 +2655,66 @@ export function AdminSettingsClient() {
url: link.url,
order: link.order,
isEnabled: link.isEnabled,
+ toolboxGroup: link.toolboxGroup || 1,
});
setEditingNavLink(link.id);
setShowNavLinkDialog(true);
}}
>
-
+
))}
- {(!navLinksData?.links?.length) && (
-
暂无导航链接
+ {(!navLinksData?.links?.filter(l => (l.toolboxGroup || 1) === 1).length) && (
+
暂无链接
+ )}
+
+
+ {siteSettings.toolbox2Name || "工具箱2"} 的链接
+
+ {navLinksData?.links?.filter(l => l.toolboxGroup === 2).map((link) => (
+
+
+
+ {link.name}
+ {!link.isEnabled && 已禁用}
+
+
{link.url} (排序: {link.order})
+
+
+
+
+
+
+ ))}
+ {(!navLinksData?.links?.filter(l => l.toolboxGroup === 2).length) && (
+
暂无链接
)}
diff --git a/app/(chat)/layout.tsx b/app/(chat)/layout.tsx
index 7501c81..842c96e 100644
--- a/app/(chat)/layout.tsx
+++ b/app/(chat)/layout.tsx
@@ -44,8 +44,13 @@ async function SidebarWrapper({ children }: { children: React.ReactNode }) {
id: link.id,
name: link.name,
url: link.url,
- order: link.order
+ order: link.order,
+ toolboxGroup: link.toolboxGroup || 1,
}))}
+ toolbox1Name={siteSettings.toolbox1Name || "工具箱"}
+ toolbox1Enabled={siteSettings.toolbox1Enabled ?? true}
+ toolbox2Name={siteSettings.toolbox2Name || "工具箱2"}
+ toolbox2Enabled={siteSettings.toolbox2Enabled ?? false}
/>
{/* Add padding-top for fixed navbar */}
diff --git a/app/api/nav-links/route.ts b/app/api/nav-links/route.ts
index a436798..3e38916 100644
--- a/app/api/nav-links/route.ts
+++ b/app/api/nav-links/route.ts
@@ -29,6 +29,7 @@ const createSchema = z.object({
url: z.string().url(),
order: z.number().int().default(0),
isEnabled: z.boolean().default(true),
+ toolboxGroup: z.number().int().min(1).max(2).default(1), // 属于哪个工具箱
});
// POST /api/nav-links - 创建导航链接(仅管理员)
@@ -79,6 +80,7 @@ const updateSchema = z.object({
url: z.string().url().optional(),
order: z.number().int().optional(),
isEnabled: z.boolean().optional(),
+ toolboxGroup: z.number().int().min(1).max(2).optional(), // 属于哪个工具箱
});
// PUT /api/nav-links - 更新导航链接(仅管理员)
diff --git a/app/api/site-settings/route.ts b/app/api/site-settings/route.ts
index b3812c3..bda1627 100644
--- a/app/api/site-settings/route.ts
+++ b/app/api/site-settings/route.ts
@@ -34,6 +34,10 @@ export async function GET() {
bannerEnabled: true,
bannerText: "升级会员, Nano Banana Pro 免费365天!",
bannerLink: null,
+ toolbox1Name: "工具箱",
+ toolbox1Enabled: true,
+ toolbox2Name: "工具箱2",
+ toolbox2Enabled: false,
};
const response = NextResponse.json({
@@ -70,6 +74,10 @@ const updateSchema = z.object({
bannerEnabled: z.boolean().optional(),
bannerText: z.string().optional(),
bannerLink: z.string().nullable().optional(),
+ toolbox1Name: z.string().max(64).optional(),
+ toolbox1Enabled: z.boolean().optional(),
+ toolbox2Name: z.string().max(64).optional(),
+ toolbox2Enabled: z.boolean().optional(),
});
// PUT /api/site-settings - 更新网站设置(仅管理员)
@@ -122,6 +130,10 @@ export async function PUT(request: NextRequest) {
bannerEnabled: data.bannerEnabled ?? true,
bannerText: data.bannerText ?? "升级会员, Nano Banana Pro 免费365天!",
bannerLink: data.bannerLink,
+ toolbox1Name: data.toolbox1Name ?? "工具箱",
+ toolbox1Enabled: data.toolbox1Enabled ?? true,
+ toolbox2Name: data.toolbox2Name ?? "工具箱2",
+ toolbox2Enabled: data.toolbox2Enabled ?? false,
updatedBy: session.user.id,
})
.returning();
diff --git a/app/canvas/[id]/layout.tsx b/app/canvas/[id]/layout.tsx
index a4db2ff..cdb8e28 100644
--- a/app/canvas/[id]/layout.tsx
+++ b/app/canvas/[id]/layout.tsx
@@ -40,8 +40,13 @@ async function CanvasLayoutWrapper({ children }: { children: React.ReactNode })
id: link.id,
name: link.name,
url: link.url,
- order: link.order
+ order: link.order,
+ toolboxGroup: link.toolboxGroup || 1,
}))}
+ toolbox1Name={siteSettings.toolbox1Name || "工具箱"}
+ toolbox1Enabled={siteSettings.toolbox1Enabled ?? true}
+ toolbox2Name={siteSettings.toolbox2Name || "工具箱2"}
+ toolbox2Enabled={siteSettings.toolbox2Enabled ?? false}
/>
{children}
diff --git a/components/top-navbar.tsx b/components/top-navbar.tsx
index cb54f7c..9943aa9 100644
--- a/components/top-navbar.tsx
+++ b/components/top-navbar.tsx
@@ -22,17 +22,24 @@ type RankingItem = {
featuredCount: number;
};
+type CustomLink = {
+ id: string;
+ name: string;
+ url: string;
+ order: number;
+ toolboxGroup?: number; // 1 或 2
+};
+
type TopNavbarProps = {
siteName?: string;
siteLogo?: string;
siteLogoDark?: string;
isAdmin?: boolean;
- customLinks?: Array<{
- id: string;
- name: string;
- url: string;
- order: number;
- }>;
+ customLinks?: CustomLink[];
+ toolbox1Name?: string;
+ toolbox1Enabled?: boolean;
+ toolbox2Name?: string;
+ toolbox2Enabled?: boolean;
};
export function TopNavbar({
@@ -41,6 +48,10 @@ export function TopNavbar({
siteLogoDark,
isAdmin = false,
customLinks = [],
+ toolbox1Name = "工具箱",
+ toolbox1Enabled = true,
+ toolbox2Name = "工具箱2",
+ toolbox2Enabled = false,
}: TopNavbarProps) {
const router = useRouter();
const { resolvedTheme } = useTheme();
@@ -145,8 +156,8 @@ export function TopNavbar({
{/* 右侧区域:自定义链接 + 系统设置 */}
- {/* 工具箱 (如果有自定义链接) */}
- {customLinks.length > 0 && (
+ {/* 工具箱1 (如果启用且有链接) */}
+ {toolbox1Enabled && customLinks.filter(l => (l.toolboxGroup || 1) === 1).length > 0 && (
{customLinks
+ .filter(l => (l.toolboxGroup || 1) === 1)
+ .sort((a, b) => a.order - b.order)
+ .map((link) => (
+
+
+
+ {link.name}
+
+
+ ))}
+
+
+
+ )}
+
+ {/* 工具箱2 (如果启用且有链接) */}
+ {toolbox2Enabled && customLinks.filter(l => l.toolboxGroup === 2).length > 0 && (
+
+
+
+
+
+
+ {customLinks
+ .filter(l => l.toolboxGroup === 2)
.sort((a, b) => a.order - b.order)
.map((link) => (
diff --git a/lib/db/queries.ts b/lib/db/queries.ts
index c233755..2c5a273 100644
--- a/lib/db/queries.ts
+++ b/lib/db/queries.ts
@@ -1508,29 +1508,25 @@ export async function getSubModelsByParentModelId(parentModelId: string) {
}
export async function getSiteSettings() {
+ const defaultSettings = {
+ siteName: "AI Chatbot",
+ siteLogo: null,
+ siteLogoDark: null,
+ favicon: null,
+ defaultTheme: "system",
+ bannerEnabled: true,
+ bannerText: "升级会员, Nano Banana Pro 免费365天!",
+ bannerLink: null,
+ toolbox1Name: "工具箱",
+ toolbox1Enabled: true,
+ toolbox2Name: "工具箱2",
+ toolbox2Enabled: false,
+ };
try {
const [settings] = await db.select().from(siteSettings).limit(1);
- return settings || {
- siteName: "AI Chatbot",
- siteLogo: null,
- siteLogoDark: null,
- favicon: null,
- defaultTheme: "system",
- bannerEnabled: true,
- bannerText: "升级会员, Nano Banana Pro 免费365天!",
- bannerLink: null
- };
+ return settings || defaultSettings;
} catch (_error) {
- return {
- siteName: "AI Chatbot",
- siteLogo: null,
- siteLogoDark: null,
- favicon: null,
- defaultTheme: "system",
- bannerEnabled: true,
- bannerText: "升级会员, Nano Banana Pro 免费365天!",
- bannerLink: null
- };
+ return defaultSettings;
}
}
diff --git a/lib/db/schema.ts b/lib/db/schema.ts
index 506f132..505711b 100644
--- a/lib/db/schema.ts
+++ b/lib/db/schema.ts
@@ -296,6 +296,11 @@ export const siteSettings = pgTable("SiteSettings", {
bannerEnabled: boolean("bannerEnabled").notNull().default(true), // 是否显示广告横幅
bannerText: text("bannerText").default("升级会员, Nano Banana Pro 免费365天!"), // 广告横幅文字
bannerLink: text("bannerLink"), // 广告横幅链接(可选)
+ // 工具箱设置
+ toolbox1Name: varchar("toolbox1Name", { length: 64 }).default("工具箱"), // 工具箱1名称
+ toolbox1Enabled: boolean("toolbox1Enabled").notNull().default(true), // 是否显示工具箱1
+ toolbox2Name: varchar("toolbox2Name", { length: 64 }).default("工具箱2"), // 工具箱2名称
+ toolbox2Enabled: boolean("toolbox2Enabled").notNull().default(false), // 是否显示工具箱2
updatedAt: timestamp("updatedAt").notNull().defaultNow(),
updatedBy: uuid("updatedBy").references(() => user.id),
});
@@ -309,6 +314,7 @@ export const navLinks = pgTable("NavLinks", {
url: text("url").notNull(),
order: integer("order").notNull().default(0),
isEnabled: boolean("isEnabled").notNull().default(true),
+ toolboxGroup: integer("toolboxGroup").notNull().default(1), // 属于哪个工具箱:1 或 2
createdAt: timestamp("createdAt").notNull().defaultNow(),
updatedAt: timestamp("updatedAt").notNull().defaultNow(),
createdBy: uuid("createdBy").references(() => user.id),