feat: add toolbox1/toolbox2 name and enable settings in navbar config
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
a76e97eec0
commit
c44c8cff6a
@ -25,7 +25,12 @@ export default async function AdminLayout({
|
||||
name: link.name,
|
||||
url: link.url,
|
||||
order: link.order,
|
||||
toolboxGroup: link.toolboxGroup || 1,
|
||||
}))}
|
||||
toolbox1Name={siteSettings.toolbox1Name || "工具箱"}
|
||||
toolbox1Enabled={siteSettings.toolbox1Enabled ?? true}
|
||||
toolbox2Name={siteSettings.toolbox2Name || "工具箱2"}
|
||||
toolbox2Enabled={siteSettings.toolbox2Enabled ?? false}
|
||||
/>
|
||||
<div className="flex-1 pt-16">{children}</div>
|
||||
</div>
|
||||
|
||||
@ -155,6 +155,10 @@ type SiteSettings = {
|
||||
bannerEnabled: boolean;
|
||||
bannerText: string;
|
||||
bannerLink: string | null;
|
||||
toolbox1Name: string;
|
||||
toolbox1Enabled: boolean;
|
||||
toolbox2Name: string;
|
||||
toolbox2Enabled: boolean;
|
||||
};
|
||||
|
||||
type NavLink = {
|
||||
@ -163,6 +167,7 @@ type NavLink = {
|
||||
url: string;
|
||||
order: number;
|
||||
isEnabled: boolean;
|
||||
toolboxGroup: number; // 1 或 2
|
||||
};
|
||||
|
||||
type DingTalkOrganization = {
|
||||
@ -348,6 +353,10 @@ export function AdminSettingsClient() {
|
||||
bannerEnabled: true,
|
||||
bannerText: "升级会员, Nano Banana Pro 免费365天!",
|
||||
bannerLink: null,
|
||||
toolbox1Name: "工具箱",
|
||||
toolbox1Enabled: true,
|
||||
toolbox2Name: "工具箱2",
|
||||
toolbox2Enabled: false,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@ -366,6 +375,10 @@ export function AdminSettingsClient() {
|
||||
bannerEnabled: siteSettingsData.bannerEnabled ?? true,
|
||||
bannerText: siteSettingsData.bannerText || "升级会员, Nano Banana Pro 免费365天!",
|
||||
bannerLink: siteSettingsData.bannerLink || null,
|
||||
toolbox1Name: siteSettingsData.toolbox1Name || "工具箱",
|
||||
toolbox1Enabled: siteSettingsData.toolbox1Enabled ?? true,
|
||||
toolbox2Name: siteSettingsData.toolbox2Name || "工具箱2",
|
||||
toolbox2Enabled: siteSettingsData.toolbox2Enabled ?? false,
|
||||
});
|
||||
}
|
||||
}, [siteSettingsData]);
|
||||
@ -535,6 +548,7 @@ export function AdminSettingsClient() {
|
||||
url: "",
|
||||
order: 0,
|
||||
isEnabled: true,
|
||||
toolboxGroup: 1, // 默认属于工具箱1
|
||||
});
|
||||
const [showNavLinkDialog, setShowNavLinkDialog] = useState(false);
|
||||
const [editingNavLink, setEditingNavLink] = useState<string | null>(null);
|
||||
@ -1009,7 +1023,7 @@ export function AdminSettingsClient() {
|
||||
|
||||
// --- 导航链接逻辑 ---
|
||||
const resetNavLinkForm = () => {
|
||||
setNavLinkForm({ id: "", name: "", url: "", order: 0, isEnabled: true });
|
||||
setNavLinkForm({ id: "", name: "", url: "", order: 0, isEnabled: true, toolboxGroup: 1 });
|
||||
setEditingNavLink(null);
|
||||
};
|
||||
|
||||
@ -2471,10 +2485,72 @@ export function AdminSettingsClient() {
|
||||
</TabsContent>
|
||||
|
||||
{/* 导航设置 */}
|
||||
<TabsContent value="nav" className="mt-6">
|
||||
<TabsContent value="nav" className="mt-6 space-y-6">
|
||||
{/* 工具箱名称设置 */}
|
||||
<Card>
|
||||
<CardContent className="pt-6">
|
||||
<div className="flex justify-end mb-4">
|
||||
<CardHeader>
|
||||
<CardTitle>工具箱设置</CardTitle>
|
||||
<CardDescription>配置导航栏上的工具箱名称和显示状态</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
{/* 工具箱1 */}
|
||||
<div className="flex items-center justify-between rounded-lg border p-4">
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="flex items-center gap-4">
|
||||
<Label className="text-base w-20">工具箱1</Label>
|
||||
<Input
|
||||
value={siteSettings.toolbox1Name}
|
||||
onChange={(e) => setSiteSettings({ ...siteSettings, toolbox1Name: e.target.value })}
|
||||
placeholder="工具箱名称"
|
||||
className="max-w-[200px]"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
主工具箱,用于展示常用的外部链接
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
checked={siteSettings.toolbox1Enabled}
|
||||
onCheckedChange={(checked) => setSiteSettings({ ...siteSettings, toolbox1Enabled: checked })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 工具箱2 */}
|
||||
<div className="flex items-center justify-between rounded-lg border p-4">
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="flex items-center gap-4">
|
||||
<Label className="text-base w-20">工具箱2</Label>
|
||||
<Input
|
||||
value={siteSettings.toolbox2Name}
|
||||
onChange={(e) => setSiteSettings({ ...siteSettings, toolbox2Name: e.target.value })}
|
||||
placeholder="工具箱2名称"
|
||||
className="max-w-[200px]"
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
第二个工具箱,可用于分类展示不同类型的链接
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
checked={siteSettings.toolbox2Enabled}
|
||||
onCheckedChange={(checked) => setSiteSettings({ ...siteSettings, toolbox2Enabled: checked })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button onClick={handleSaveSiteSettings}>
|
||||
<Save className="mr-2 h-4 w-4" /> 保存工具箱设置
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 导航链接列表 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<CardTitle>导航链接</CardTitle>
|
||||
<CardDescription>添加和管理工具箱中的链接</CardDescription>
|
||||
</div>
|
||||
<Dialog open={showNavLinkDialog} onOpenChange={setShowNavLinkDialog}>
|
||||
<DialogTrigger asChild>
|
||||
<Button onClick={() => { resetNavLinkForm(); setShowNavLinkDialog(true); }}>
|
||||
@ -2482,52 +2558,84 @@ export function AdminSettingsClient() {
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingNavLink ? "编辑链接" : "添加链接"}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label>名称</Label>
|
||||
<Input
|
||||
value={navLinkForm.name}
|
||||
onChange={(e) => setNavLinkForm({ ...navLinkForm, name: e.target.value })}
|
||||
placeholder="链接名称"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>URL</Label>
|
||||
<Input
|
||||
value={navLinkForm.url}
|
||||
onChange={(e) => setNavLinkForm({ ...navLinkForm, url: e.target.value })}
|
||||
placeholder="https://..."
|
||||
/>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingNavLink ? "编辑链接" : "添加链接"}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label>名称</Label>
|
||||
<Input
|
||||
value={navLinkForm.name}
|
||||
onChange={(e) => setNavLinkForm({ ...navLinkForm, name: e.target.value })}
|
||||
placeholder="链接名称"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>URL</Label>
|
||||
<Input
|
||||
value={navLinkForm.url}
|
||||
onChange={(e) => setNavLinkForm({ ...navLinkForm, url: e.target.value })}
|
||||
placeholder="https://..."
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>所属工具箱</Label>
|
||||
<div className="flex gap-4">
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
name="toolboxGroup"
|
||||
value={1}
|
||||
checked={navLinkForm.toolboxGroup === 1}
|
||||
onChange={() => setNavLinkForm({ ...navLinkForm, toolboxGroup: 1 })}
|
||||
className="w-4 h-4"
|
||||
/>
|
||||
<span className="text-sm">{siteSettings.toolbox1Name || "工具箱1"}</span>
|
||||
</label>
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
name="toolboxGroup"
|
||||
value={2}
|
||||
checked={navLinkForm.toolboxGroup === 2}
|
||||
onChange={() => setNavLinkForm({ ...navLinkForm, toolboxGroup: 2 })}
|
||||
className="w-4 h-4"
|
||||
/>
|
||||
<span className="text-sm">{siteSettings.toolbox2Name || "工具箱2"}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>排序 (越小越靠前)</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={navLinkForm.order}
|
||||
onChange={(e) => setNavLinkForm({ ...navLinkForm, order: parseInt(e.target.value) || 0 })}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={navLinkForm.isEnabled}
|
||||
onCheckedChange={(checked) => setNavLinkForm({ ...navLinkForm, isEnabled: checked })}
|
||||
/>
|
||||
<Label>启用</Label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>排序 (越小越靠前)</Label>
|
||||
<Input
|
||||
type="number"
|
||||
value={navLinkForm.order}
|
||||
onChange={(e) => setNavLinkForm({ ...navLinkForm, order: parseInt(e.target.value) || 0 })}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={navLinkForm.isEnabled}
|
||||
onCheckedChange={(checked) => setNavLinkForm({ ...navLinkForm, isEnabled: checked })}
|
||||
/>
|
||||
<Label>启用</Label>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setShowNavLinkDialog(false)}>取消</Button>
|
||||
<Button onClick={handleSaveNavLink}>保存</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setShowNavLinkDialog(false)}>取消</Button>
|
||||
<Button onClick={handleSaveNavLink}>保存</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{/* 工具箱1 的链接 */}
|
||||
<div className="space-y-4">
|
||||
{navLinksData?.links?.map((link) => (
|
||||
<div className="text-sm font-medium text-muted-foreground border-b pb-2">
|
||||
{siteSettings.toolbox1Name || "工具箱1"} 的链接
|
||||
</div>
|
||||
{navLinksData?.links?.filter(l => (l.toolboxGroup || 1) === 1).map((link) => (
|
||||
<div key={link.id} className="flex items-center justify-between p-4 border rounded-lg">
|
||||
<div className="flex-1">
|
||||
<div className="font-medium flex items-center gap-2">
|
||||
@ -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);
|
||||
}}
|
||||
>
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
</Button>
|
||||
<Button variant="destructive" size="sm" onClick={() => handleDeleteNavLink(link.id)}>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{(!navLinksData?.links?.length) && (
|
||||
<div className="text-center text-muted-foreground py-8">暂无导航链接</div>
|
||||
{(!navLinksData?.links?.filter(l => (l.toolboxGroup || 1) === 1).length) && (
|
||||
<div className="text-center text-muted-foreground py-4 text-sm">暂无链接</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 工具箱2 的链接 */}
|
||||
<div className="space-y-4 mt-6">
|
||||
<div className="text-sm font-medium text-muted-foreground border-b pb-2">
|
||||
{siteSettings.toolbox2Name || "工具箱2"} 的链接
|
||||
</div>
|
||||
{navLinksData?.links?.filter(l => l.toolboxGroup === 2).map((link) => (
|
||||
<div key={link.id} className="flex items-center justify-between p-4 border rounded-lg">
|
||||
<div className="flex-1">
|
||||
<div className="font-medium flex items-center gap-2">
|
||||
{link.name}
|
||||
{!link.isEnabled && <span className="text-xs bg-gray-200 px-2 py-0.5 rounded">已禁用</span>}
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">{link.url} (排序: {link.order})</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setNavLinkForm({
|
||||
id: link.id,
|
||||
name: link.name,
|
||||
url: link.url,
|
||||
order: link.order,
|
||||
isEnabled: link.isEnabled,
|
||||
toolboxGroup: link.toolboxGroup || 1,
|
||||
});
|
||||
setEditingNavLink(link.id);
|
||||
setShowNavLinkDialog(true);
|
||||
}}
|
||||
>
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="destructive" size="sm" onClick={() => handleDeleteNavLink(link.id)}>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{(!navLinksData?.links?.filter(l => l.toolboxGroup === 2).length) && (
|
||||
<div className="text-center text-muted-foreground py-4 text-sm">暂无链接</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
<div className="pt-14 flex h-full w-full overflow-hidden"> {/* Add padding-top for fixed navbar */}
|
||||
<AppSidebar user={session?.user} />
|
||||
|
||||
@ -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 - 更新导航链接(仅管理员)
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
<div className="pt-12 h-full">
|
||||
{children}
|
||||
|
||||
@ -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({
|
||||
|
||||
{/* 右侧区域:自定义链接 + 系统设置 */}
|
||||
<div className="flex items-center gap-2">
|
||||
{/* 工具箱 (如果有自定义链接) */}
|
||||
{customLinks.length > 0 && (
|
||||
{/* 工具箱1 (如果启用且有链接) */}
|
||||
{toolbox1Enabled && customLinks.filter(l => (l.toolboxGroup || 1) === 1).length > 0 && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
@ -155,12 +166,49 @@ export function TopNavbar({
|
||||
className="flex items-center gap-1 h-8 px-3 text-white hover:bg-white/15 hover:text-white rounded-full transition-colors"
|
||||
>
|
||||
<Briefcase className="h-4 w-4" />
|
||||
<span className="text-sm font-medium">工具箱</span>
|
||||
<span className="text-sm font-medium">{toolbox1Name}</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-[320px] p-2">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{customLinks
|
||||
.filter(l => (l.toolboxGroup || 1) === 1)
|
||||
.sort((a, b) => a.order - b.order)
|
||||
.map((link) => (
|
||||
<DropdownMenuItem key={link.id} asChild className="cursor-pointer">
|
||||
<a
|
||||
href={link.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex flex-col items-center justify-center gap-2 rounded-md bg-muted/50 p-4 hover:bg-accent hover:text-accent-foreground text-center h-20 transition-colors"
|
||||
>
|
||||
<ExternalLink className="h-5 w-5 opacity-70" />
|
||||
<span className="text-xs font-medium line-clamp-2">{link.name}</span>
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</div>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
|
||||
{/* 工具箱2 (如果启用且有链接) */}
|
||||
{toolbox2Enabled && customLinks.filter(l => l.toolboxGroup === 2).length > 0 && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="flex items-center gap-1 h-8 px-3 text-white hover:bg-white/15 hover:text-white rounded-full transition-colors"
|
||||
>
|
||||
<Briefcase className="h-4 w-4" />
|
||||
<span className="text-sm font-medium">{toolbox2Name}</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-[320px] p-2">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{customLinks
|
||||
.filter(l => l.toolboxGroup === 2)
|
||||
.sort((a, b) => a.order - b.order)
|
||||
.map((link) => (
|
||||
<DropdownMenuItem key={link.id} asChild className="cursor-pointer">
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user