diff --git a/frontend/app/dashboard/categories.tsx b/frontend/app/dashboard/categories.tsx new file mode 100644 index 0000000..c0d3e10 --- /dev/null +++ b/frontend/app/dashboard/categories.tsx @@ -0,0 +1,209 @@ +import { Template } from "interface/template"; +import { + Container, + Heading, + Text, + Box, + Flex, + Table, + Button, + TextField, + ScrollArea, + Dialog, + IconButton +} from "@radix-ui/themes"; +import { + PlusIcon, + MagnifyingGlassIcon, + Pencil1Icon, + TrashIcon, + ChevronRightIcon, +} from "@radix-ui/react-icons"; +import { useState } from "react"; +import type { Category } from "interface/fields"; + +// 模拟分类数据 +const mockCategories: (Category & { id: number; count: number })[] = [ + { + id: 1, + name: "前端开发", + parentId: undefined, + count: 15 + }, + { + id: 2, + name: "React", + parentId: "1", + count: 8 + }, + { + id: 3, + name: "Vue", + parentId: "1", + count: 5 + }, + { + id: 4, + name: "后端开发", + parentId: undefined, + count: 12 + }, + { + id: 5, + name: "Node.js", + parentId: "4", + count: 6 + } +]; + +export default new Template({}, ({ http, args }) => { + const [searchTerm, setSearchTerm] = useState(""); + const [isAddDialogOpen, setIsAddDialogOpen] = useState(false); + const [newCategoryName, setNewCategoryName] = useState(""); + const [selectedParentId, setSelectedParentId] = useState(); + + return ( + + {/* 页面标题和操作栏 */} + + + + 分类管理 + + + 共 {mockCategories.length} 个分类 + + + + + + {/* 搜索栏 */} + + ) => setSearchTerm(e.target.value)} + > + + + + + + + {/* 分类列表 */} + + + + + + 分类名称 + 文章数量 + 父分类 + 操作 + + + + + {mockCategories.map((category) => ( + + + + {category.parentId && ( + + )} + {category.name} + + + + {category.count} 篇 + + + + {category.parentId + ? mockCategories.find(c => c.id.toString() === category.parentId)?.name + : '-' + } + + + + + + + + + + ))} + + + + + + {/* 新建分类对话框 */} + + + 新建分类 + + 创建一个新的文章分类 + + + + + + 分类名称 + + ) => setNewCategoryName(e.target.value)} + /> + + + + + 父分类 + + + + + + + + + + + + + + + + + ); +}); \ No newline at end of file diff --git a/frontend/app/dashboard/comments.tsx b/frontend/app/dashboard/comments.tsx new file mode 100644 index 0000000..287d2c5 --- /dev/null +++ b/frontend/app/dashboard/comments.tsx @@ -0,0 +1,297 @@ +import { Template } from "interface/template"; +import { + Container, + Heading, + Text, + Box, + Flex, + Table, + Button, + TextField, + DropdownMenu, + ScrollArea, + DataList, + Avatar, + Badge +} from "@radix-ui/themes"; +import { + MagnifyingGlassIcon, + CheckIcon, + Cross2Icon, + ChatBubbleIcon, + TrashIcon, +} from "@radix-ui/react-icons"; +import { useState } from "react"; + +// 模拟评论数据 +const mockComments = [ + { + id: 1, + content: "这篇文章写得很好,对我帮助很大!", + author: "张三", + postTitle: "构建现代化的前端开发工作流", + createdAt: new Date("2024-03-15"), + status: "pending", // pending, approved, rejected + avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=1" + }, + { + id: 2, + content: "文章内容很专业,讲解得很清楚。", + author: "李四", + postTitle: "React 18 新特性详解", + createdAt: new Date("2024-03-14"), + status: "approved", + avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=2" + }, + // 可以添加更多模拟数据 +]; + +export default new Template({}, ({ http, args }) => { + const [searchTerm, setSearchTerm] = useState(""); + const [selectedStatus, setSelectedStatus] = useState("all"); + + const getStatusStyle = (status: string) => { + switch(status) { + case 'approved': + return 'bg-[--green-3] text-[--green-11]'; + case 'rejected': + return 'bg-[--red-3] text-[--red-11]'; + default: + return 'bg-[--yellow-3] text-[--yellow-11]'; + } + }; + + const getStatusText = (status: string) => { + switch(status) { + case 'approved': + return '已通过'; + case 'rejected': + return '已拒绝'; + default: + return '待审核'; + } + }; + + return ( + + {/* 页面标题和统计 */} + + + + 评论管理 + + + 共 {mockComments.length} 条评论 + + + + + {/* 搜索和筛选栏 */} + + + ) => setSearchTerm(e.target.value)} + > + + + + + + + + + + + + setSelectedStatus('all')}> + 全部 + + setSelectedStatus('pending')}> + 待审核 + + setSelectedStatus('approved')}> + 已通过 + + setSelectedStatus('rejected')}> + 已拒绝 + + + + + + {/* 评论列表 */} + + + {/* 桌面端表格视图 */} +
+ + + + 评论者 + 评论内容 + 文章 + 状态 + 时间 + 操作 + + + + + {mockComments.map((comment) => ( + + + + + {comment.author} + + + + + {comment.content} + + + + + {comment.postTitle} + + + + + {comment.status === 'approved' && 已通过} + {comment.status === 'pending' && 待审核} + {comment.status === 'rejected' && 已拒绝} + + + + {comment.createdAt.toLocaleDateString()} + + + + + + + + + + ))} + + +
+ + {/* 移动端列表视图 */} +
+ {mockComments.map((comment) => ( + + + 评论者 + + + + {comment.author} + + + + + + 评论内容 + + {comment.content} + + + + + 文章 + + {comment.postTitle} + + + + + 状态 + + + {comment.status === 'approved' && 已通过} + {comment.status === 'pending' && 待审核} + {comment.status === 'rejected' && 已拒绝} + + + + + + 时间 + + {comment.createdAt.toLocaleDateString()} + + + + + 操作 + + + + + + + + + + ))} +
+
+
+
+ ); +}); \ No newline at end of file diff --git a/frontend/app/dashboard/files.tsx b/frontend/app/dashboard/files.tsx new file mode 100644 index 0000000..b9a0ee3 --- /dev/null +++ b/frontend/app/dashboard/files.tsx @@ -0,0 +1,305 @@ +import { Template } from "interface/template"; +import { + Container, + Heading, + Text, + Box, + Flex, + Table, + Button, + TextField, + DropdownMenu, + ScrollArea, + Dialog, + DataList +} from "@radix-ui/themes"; +import { + PlusIcon, + MagnifyingGlassIcon, + FileIcon, + TrashIcon, + DownloadIcon, + DotsHorizontalIcon, + FileTextIcon, + ImageIcon, + VideoIcon +} from "@radix-ui/react-icons"; +import { useState } from "react"; +import type { Resource } from "interface/fields"; + +// 模拟文件数据 +const mockFiles: Resource[] = [ + { + id: 1, + authorId: "1", + name: "前端开发规范.pdf", + sizeBytes: 1024 * 1024 * 2, // 2MB + storagePath: "/files/frontend-guide.pdf", + fileType: "application/pdf", + category: "documents", + description: "前端开发规范文档", + createdAt: new Date("2024-03-15") + }, + { + id: 2, + authorId: "1", + name: "项目架构图.png", + sizeBytes: 1024 * 512, // 512KB + storagePath: "/files/architecture.png", + fileType: "image/png", + category: "images", + description: "项目整体架构示意图", + createdAt: new Date("2024-03-14") + }, + { + id: 3, + authorId: "1", + name: "API文档.md", + sizeBytes: 1024 * 256, // 256KB + storagePath: "/files/api-doc.md", + fileType: "text/markdown", + category: "documents", + description: "API接口文档", + createdAt: new Date("2024-03-13") + } +]; + +export default new Template({}, ({ http, args }) => { + const [searchTerm, setSearchTerm] = useState(""); + const [selectedType, setSelectedType] = useState("all"); + const [isUploadDialogOpen, setIsUploadDialogOpen] = useState(false); + + // 格式化文件大小 + const formatFileSize = (bytes: number) => { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + }; + + // 获取文件图标 + const getFileIcon = (fileType: string) => { + if (fileType.startsWith('image/')) return ; + if (fileType.startsWith('video/')) return ; + if (fileType.startsWith('text/')) return ; + return ; + }; + + return ( + + {/* 页面标题和操作栏 */} + + + + 文件管理 + + + 共 {mockFiles.length} 个文件 + + + + + + {/* 搜索和筛选栏 */} + + + ) => setSearchTerm(e.target.value)} + > + + + + + + + + + + + + setSelectedType('all')}> + 全部 + + setSelectedType('documents')}> + 文档 + + setSelectedType('images')}> + 图片 + + setSelectedType('others')}> + 其他 + + + + + + {/* 文件列表 */} + + + {/* 桌面端表格视图 */} +
+ + + + 文件名 + 大小 + 类型 + 上传时间 + 操作 + + + + + {mockFiles.map((file) => ( + + + + {getFileIcon(file.fileType)} + {file.name} + + + + {formatFileSize(file.sizeBytes)} + + + + {file.fileType.split('/')[1].toUpperCase()} + + + + {file.createdAt.toLocaleDateString()} + + + + + + + + + ))} + + +
+ + {/* 移动端列表视图 */} +
+ {mockFiles.map((file) => ( + + + 文件名 + + + {getFileIcon(file.fileType)} + {file.name} + + + + + + 大小 + + {formatFileSize(file.sizeBytes)} + + + + + 类型 + + {file.fileType.split('/')[1].toUpperCase()} + + + + + 上传时间 + + {file.createdAt.toLocaleDateString()} + + + + + 操作 + + + + + + + + + ))} +
+
+
+ + {/* 上传对话框 */} + + + 上传文件 + + 选择要上传的文件 + + + + { + // 处理文件上传 + console.log(e.target.files); + }} + /> + + + + + + + + + + + + + +
+ ); +}); \ No newline at end of file diff --git a/frontend/app/dashboard/index.tsx b/frontend/app/dashboard/index.tsx new file mode 100644 index 0000000..4447dd1 --- /dev/null +++ b/frontend/app/dashboard/index.tsx @@ -0,0 +1,169 @@ +import { Template } from "interface/template"; +import { Container, Heading, Text, Box, Flex, Card } from "@radix-ui/themes"; +import { + BarChartIcon, + ReaderIcon, + ChatBubbleIcon, + PersonIcon, + EyeOpenIcon, + HeartIcon, + RocketIcon, + LayersIcon, +} from "@radix-ui/react-icons"; +import { useMemo } from "react"; + +// 模拟统计数据 +const stats = [ + { + label: "文章总数", + value: "128", + icon: , + trend: "+12%", + color: "var(--accent-9)", + }, + { + label: "总访问量", + value: "25,438", + icon: , + trend: "+8.2%", + color: "var(--green-9)", + }, + { + label: "评论数", + value: "1,024", + icon: , + trend: "+5.4%", + color: "var(--blue-9)", + }, + { + label: "用户互动", + value: "3,842", + icon: , + trend: "+15.3%", + color: "var(--pink-9)", + }, +]; + +// 模拟最近文章数据 +const recentPosts = [ + { + title: "构建现代化的前端开发工作流", + views: 1234, + comments: 23, + likes: 89, + status: "published", + }, + { + title: "React 18 新特性详解", + views: 892, + comments: 15, + likes: 67, + status: "published", + }, + { + title: "TypeScript 高级特性指南", + views: 756, + comments: 12, + likes: 45, + status: "draft", + }, + { + title: "前端性能优化实践", + views: 645, + comments: 8, + likes: 34, + status: "published", + }, +]; + +export default new Template({}, ({ http, args }) => { + return ( + + {/* 页面标题 */} + + 仪表盘 + + + {/* 统计卡片 */} + + {stats.map((stat, index) => ( + + + + + {stat.label} + + + {stat.value} + + + {stat.trend} + + + + + {stat.icon} + + + + + ))} + + + {/* 最近文章列表 */} + + + 最近文章 + + + {recentPosts.map((post, index) => ( + + + + + {post.title} + + + + + + {post.views} + + + + + + {post.comments} + + + + + + {post.likes} + + + + + + {post.status === 'published' ? '已发布' : '草稿'} + + + + ))} + + + + ); +}); \ No newline at end of file diff --git a/frontend/app/dashboard/layout.tsx b/frontend/app/dashboard/layout.tsx index 5309088..ff4474b 100644 --- a/frontend/app/dashboard/layout.tsx +++ b/frontend/app/dashboard/layout.tsx @@ -9,9 +9,11 @@ import { DashboardIcon, GearIcon, FileTextIcon, - ImageIcon, ReaderIcon, LayersIcon, + FileIcon, + ColorWheelIcon, + HomeIcon, } from "@radix-ui/react-icons"; import { Theme } from "@radix-ui/themes"; import { useState, useEffect } from "react"; @@ -30,11 +32,6 @@ const menuItems = [ label: "文章管理", path: "/dashboard/posts", }, - { - icon: , - label: "媒体管理", - path: "/dashboard/media", - }, { icon: , label: "评论管理", @@ -45,11 +42,31 @@ const menuItems = [ label: "分类管理", path: "/dashboard/categories", }, + { + icon: , + label: "文件管理", + path: "/dashboard/files", + }, + { + icon: , + label: "主题管理", + path: "/dashboard/themes", + }, { icon: , label: "系统设置", path: "/dashboard/settings", }, + { + icon: , + label: "用户管理", + path: "/dashboard/users", + }, + { + icon: , + label: "插件管理", + path: "/dashboard/plugins", + }, ]; export default new Layout(({ children }) => { @@ -187,46 +204,36 @@ export default new Layout(({ children }) => { {/* 右侧用户菜单 */} - + - - - - - - - - - - 个人设置 - - - - - - - 退出登录 - - - - + {/* 返回主页按钮 */} + + + {/* 退出登录按钮 */} + diff --git a/frontend/app/dashboard/login.tsx b/frontend/app/dashboard/login.tsx index b9d1212..fdbc423 100644 --- a/frontend/app/dashboard/login.tsx +++ b/frontend/app/dashboard/login.tsx @@ -2,10 +2,11 @@ import "./styles/login.css"; import { Template } from "interface/template"; import { Container, Heading, Text, Box, Flex, Button } from "@radix-ui/themes"; import { PersonIcon, LockClosedIcon } from "@radix-ui/react-icons"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState, useMemo } from "react"; import { gsap } from "gsap"; import { AnimatedBackground } from 'hooks/Background'; import { useThemeMode, ThemeModeToggle } from 'hooks/ThemeMode'; +import { useNotification } from 'hooks/Notification'; export default new Template({}, ({ http, args }) => { const containerRef = useRef(null); @@ -14,6 +15,8 @@ export default new Template({}, ({ http, args }) => { const [password, setPassword] = useState(""); const [isLoading, setIsLoading] = useState(false); const { mode } = useThemeMode(); + const [hasBackgroundError, setHasBackgroundError] = useState(false); + const notification = useNotification(); useEffect(() => { setIsVisible(true); @@ -58,20 +61,36 @@ export default new Template({}, ({ http, args }) => { // 这里添加登录逻辑 await new Promise(resolve => setTimeout(resolve, 1500)); // 模拟API请求 + // 登录成功的通知 + notification.success('登录成功', '欢迎回来!'); + // 登录成功后的处理 console.log("Login successful"); } catch (error) { + // 登录失败的通知 + notification.error('登录失败', '用户名或密码错误'); console.error("Login failed:", error); } finally { setIsLoading(false); } }; + const handleBackgroundError = () => { + console.log('Background failed to load, switching to fallback'); + setHasBackgroundError(true); + }; + + // 使用 useMemo 包裹背景组件 + const backgroundComponent = useMemo(() => ( + !hasBackgroundError && + ), [hasBackgroundError]); + return ( - <> - +
+ {backgroundComponent} + { @@ -106,36 +125,32 @@ export default new Template({}, ({ http, args }) => {
{/* 用户名输入框 */} - - + setUsername(e.target.value)} required /> + {/* 密码输入框 */} - - + setPassword(e.target.value)} required /> + {/* 登录按钮 */}
); }); \ No newline at end of file diff --git a/frontend/app/dashboard/plugins.tsx b/frontend/app/dashboard/plugins.tsx new file mode 100644 index 0000000..f5f9464 --- /dev/null +++ b/frontend/app/dashboard/plugins.tsx @@ -0,0 +1,283 @@ +import { Template } from "interface/template"; +import { + Container, + Heading, + Text, + Box, + Flex, + Card, + Button, + TextField, + DropdownMenu, + ScrollArea, + Dialog, + Tabs, + Switch, + IconButton +} from "@radix-ui/themes"; +import { + PlusIcon, + MagnifyingGlassIcon, + DownloadIcon, + GearIcon, + CodeIcon, + Cross2Icon, + CheckIcon, + UpdateIcon, + TrashIcon, + ExclamationTriangleIcon +} from "@radix-ui/react-icons"; +import { useState } from "react"; +import type { PluginConfig } from "interface/plugin"; + +// 模拟插件数据 +const mockPlugins: (PluginConfig & { id: number; preview?: string; installed?: boolean })[] = [ + { + id: 1, + name: "comment-system", + displayName: "评论系统", + version: "1.0.0", + description: "支持多种评论系统集成,包括Disqus、Gitalk等", + author: "Admin", + enabled: true, + icon: "https://api.iconify.design/material-symbols:comment.svg", + preview: "https://images.unsplash.com/photo-1516116216624-53e697fedbea?w=500&auto=format", + managePath: "/dashboard/plugins/comment-system", + installed: true, + configuration: { + system: { + title: "评论系统配置", + description: "配置评论系统参数", + data: { + provider: "gitalk", + clientId: "", + clientSecret: "" + } + } + }, + routes: new Set() + }, + { + id: 2, + name: "image-optimization", + displayName: "图片优化", + version: "1.0.0", + description: "自动优化上传的图片,支持压缩、裁剪、水印等功能", + author: "ThirdParty", + enabled: false, + icon: "https://api.iconify.design/material-symbols:image.svg", + preview: "https://images.unsplash.com/photo-1618005198919-d3d4b5a92ead?w=500&auto=format", + installed: true, + configuration: { + system: { + title: "图片优化配置", + description: "配置图片优化参数", + data: { + quality: 80, + maxWidth: 1920, + watermark: false + } + } + }, + routes: new Set() + } +]; + +// 模拟市场插件数据 +interface MarketPlugin { + id: number; + name: string; + displayName: string; + version: string; + description: string; + author: string; + preview?: string; + downloads: number; + rating: number; +} + +const marketPlugins: MarketPlugin[] = [ + { + id: 4, + name: "image-optimization", + displayName: "图片优化", + version: "1.0.0", + description: "自动优化上传的图片,支持压缩、裁剪、水印等功能", + author: "ThirdParty", + preview: "https://images.unsplash.com/photo-1516116216624-53e697fedbea?w=500&auto=format", + downloads: 1200, + rating: 4.5 + }, + { + id: 5, + name: "markdown-plus", + displayName: "Markdown增强", + version: "2.0.0", + description: "增强的Markdown编辑器,支持更多扩展语法和实时预览", + author: "ThirdParty", + preview: "https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=500&auto=format", + downloads: 3500, + rating: 4.8 + } +]; + +export default new Template({}, ({ http, args }) => { + const [searchTerm, setSearchTerm] = useState(""); + const [isAddDialogOpen, setIsAddDialogOpen] = useState(false); + const [isSettingsOpen, setIsSettingsOpen] = useState(false); + const [selectedPlugin, setSelectedPlugin] = useState(null); + + // 处理插件启用/禁用 + const handleTogglePlugin = (pluginId: number) => { + // 这里添加启用/禁用插件的逻辑 + console.log('Toggle plugin:', pluginId); + }; + + return ( + + {/* 页面标题和操作栏 */} + + + + 插件管理 + + + 共 {mockPlugins.length} 个插件 + + + + + + {/* 搜索栏 */} + + ) => setSearchTerm(e.target.value)} + > + + + + + + + {/* 插件列表 */} + + {mockPlugins.map((plugin) => ( + + {/* 插件预览图 */} + {plugin.preview && ( + + {plugin.displayName} + + )} + + {/* 插件信息 */} + + + {plugin.displayName} + handleTogglePlugin(plugin.id)} + /> + + + + 版本 {plugin.version} · 作者 {plugin.author} + + + + {plugin.description} + + + {/* 操作按钮 */} + + {plugin.managePath && plugin.enabled && ( + + )} + + + + + ))} + + + {/* 安装插件对话框 */} + + + 安装插件 + + 上传插件包进行安装 + + + + + { + console.log(e.target.files); + }} + /> + + + + + + + + + + + + + + + + ); +}); \ No newline at end of file diff --git a/frontend/app/dashboard/posts.tsx b/frontend/app/dashboard/posts.tsx new file mode 100644 index 0000000..fca2fba --- /dev/null +++ b/frontend/app/dashboard/posts.tsx @@ -0,0 +1,247 @@ +import { Template } from "interface/template"; +import { + Container, + Heading, + Text, + Box, + Flex, + Table, + Button, + TextField, + DropdownMenu, + ScrollArea, + DataList, + Badge +} from "@radix-ui/themes"; +import { + PlusIcon, + MagnifyingGlassIcon, + DotsHorizontalIcon, + Pencil1Icon, + TrashIcon, + EyeOpenIcon, + ReaderIcon, +} from "@radix-ui/react-icons"; +import { useState } from "react"; +import type { PostDisplay } from "interface/fields"; + +// 模拟文章数据 +const mockPosts: PostDisplay[] = [ + { + id: 1, + title: "构建现代化的前端开发工作流", + content: "在现代前端开发中...", + authorName: "张三", + publishedAt: new Date("2024-03-15"), + status: "published", + isEditor: false, + createdAt: new Date("2024-03-15"), + updatedAt: new Date("2024-03-15"), + metaKeywords: "", + metaDescription: "", + categories: [{ name: "前端开发" }], + tags: [{ name: "工程化" }, { name: "效率提升" }] + }, + // ... 可以添加更多模拟数据 +]; + +export default new Template({}, ({ http, args }) => { + const [searchTerm, setSearchTerm] = useState(""); + const [selectedStatus, setSelectedStatus] = useState("all"); + + return ( + + {/* 页面标题和操作栏 */} + + + 文章管理 + + + + + {/* 搜索和筛选栏 */} + + + ) => setSearchTerm(e.target.value)} + > + + + + + + + + + + + + setSelectedStatus('all')}> + 全部 + + setSelectedStatus('published')}> + 已发布 + + setSelectedStatus('draft')}> + 草稿 + + + + + + {/* 文章列表 */} + + + {/* 桌面端表格视图 */} +
+ + + + 标题 + 作者 + 分类 + 状态 + 发布时间 + 操作 + + + + {mockPosts.map((post) => ( + + + {post.title} + + + {post.authorName} + + + + {post.categories?.map((category) => ( + + {category.name} + + ))} + + + + + {post.status === 'published' ? ( + 已发布 + ) : ( + 草稿 + )} + + + + {post.publishedAt?.toLocaleDateString()} + + + + + + + + + + ))} + + +
+ + {/* 移动端列表视图 */} +
+ {mockPosts.map((post) => ( + + + 标题 + + {post.title} + + + + + 作者 + {post.authorName} + + + + 分类 + + + {post.categories?.map((category) => ( + + {category.name} + + ))} + + + + + + 状态 + + + {post.status === 'published' ? ( + 已发布 + ) : ( + 草稿 + )} + + + + + + 发布时间 + + {post.publishedAt?.toLocaleDateString()} + + + + + 操作 + + + + + + + + + + ))} +
+
+
+
+ ); +}); \ No newline at end of file diff --git a/frontend/app/dashboard/settings.tsx b/frontend/app/dashboard/settings.tsx new file mode 100644 index 0000000..378f110 --- /dev/null +++ b/frontend/app/dashboard/settings.tsx @@ -0,0 +1,230 @@ +import { Template } from "interface/template"; +import { + Container, + Heading, + Text, + Box, + Flex, + Card, + Button, + TextField, + Switch, + Tabs, + TextArea +} from "@radix-ui/themes"; +import { + GearIcon, + PersonIcon, + LockClosedIcon, + BellIcon, + GlobeIcon +} from "@radix-ui/react-icons"; +import { useState } from "react"; + +export default new Template({}, ({ http, args }) => { + const [siteName, setSiteName] = useState("我的博客"); + const [siteDescription, setSiteDescription] = useState("一个优雅的博客系统"); + const [emailNotifications, setEmailNotifications] = useState(true); + + return ( + + + 系统设置 + + + + + + + 常规设置 + + + + 个人资料 + + + + 安全设置 + + + + 通知设置 + + + + {/* 常规设置 */} + + + + + + 站点名称 + + ) => setSiteName(e.target.value)} + /> + + + + + 站点描述 + +