-
Notifications
You must be signed in to change notification settings - Fork 173
Feat : 模版增加到数据库,增加删除,修改模版 #273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- 新增模板类型定义和服务 API 模块 - 替换硬编码模板数据为后端 API 调用 - 实现模板的创建、查询和删除功能 - 更新模板列表的过滤和搜索逻辑以使用后端接口
重构模板表单为可复用的组件,支持创建和编辑两种模式 添加 isSystem 字段区分系统模板 将 PUT 请求改为 PATCH 方法
✅ Deploy Preview for ornate-blancmange-89db6b ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Summary of ChangesHello @Hemomoo, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 此拉取请求对模板管理功能进行了重大升级,从之前的前端硬编码数据转向了后端数据库驱动的模式。这一改变不仅使得模板内容可以动态更新和持久化,还为用户提供了完整的模板生命周期管理能力,包括创建、编辑和删除自定义模板。通过引入专门的 API 服务层和重构前端组件,显著提升了模板功能的灵活性和可维护性。 Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This PR successfully migrates front-end hardcoded templates to a database, implementing full CRUD functionality for template management. While the code structure is clear and component division is reasonable, a critical security oversight exists in the access control logic for the template list. Specifically, the UI allows users to attempt editing system-level templates, which should be restricted to prevent unauthorized modifications of global resources. Additionally, there are opportunities to improve type safety and code reusability to further enhance code quality.
| <Pencil | ||
| onClick={(e) => { | ||
| e.stopPropagation(); | ||
| handleCreateDocument(template); | ||
| handleOpenEditDialog(template); | ||
| }} | ||
| className="h-4 w-4 text-gray-400 dark:text-gray-500 hover:text-blue-500 dark:hover:text-blue-400 opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer" | ||
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The application fails to restrict the edit action for system templates in the user interface. While deletion and creation actions are correctly guarded by a !template.isSystem check (lines 288-305), the edit button (Pencil icon) is rendered for all templates. This allows users to open the edit dialog and potentially send update requests (TemplateApi.UpdateTemplate) for system-level resources, which should typically be read-only for regular users. This inconsistency suggests a broken access control flaw in the UI implementation.
| <Pencil | |
| onClick={(e) => { | |
| e.stopPropagation(); | |
| handleCreateDocument(template); | |
| handleOpenEditDialog(template); | |
| }} | |
| className="h-4 w-4 text-gray-400 dark:text-gray-500 hover:text-blue-500 dark:hover:text-blue-400 opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer" | |
| /> | |
| {!template.isSystem && ( | |
| <Pencil | |
| onClick={(e) => { | |
| e.stopPropagation(); | |
| handleOpenEditDialog(template); | |
| }} | |
| className="h-4 w-4 text-gray-400 dark:text-gray-500 hover:text-blue-500 dark:hover:text-blue-400 opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer" | |
| /> | |
| )} |
|
|
||
| const handleCreateCustomTemplate = () => { | ||
| setCreateDialogOpen(true); | ||
| const handleConfirmTemplateForm = async (formValues: any) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handleConfirmTemplateForm 函数的参数 formValues 类型是 any,这会失去 TypeScript 类型检查的优势,并可能引入潜在的 bug。
为了提高类型安全性,建议从 TemplateFormDialog.tsx 文件中导出 FormValues 类型,并在这里使用它来注解 formValues 参数。
// 在 TemplateFormDialog.tsx 中:
export type FormValues = z.infer<typeof formSchema>;
// 在 TemplatesTab.tsx 中:
import { FormValues } from './folder/components/TemplateFormDialog';
// ...
const handleConfirmTemplateForm = async (formValues: FormValues) => {
// ...
}这将确保传递给 API 的数据结构是正确的。
| const handleConfirmTemplateForm = async (formValues: any) => { | ||
| try { | ||
| if (editingTemplate) { | ||
| // 编辑模式 | ||
| const res = await TemplateApi.UpdateTemplate(editingTemplate.id, formValues); | ||
|
|
||
| if (res.data?.code === 200) { | ||
| toast.success('模板更新成功'); | ||
| } else { | ||
| toast.error('模板更新失败'); | ||
| } | ||
| } else { | ||
| // 创建模式 | ||
| const res = await TemplateApi.CreateTemplate(formValues); | ||
|
|
||
| if (res.data?.code === 200) { | ||
| toast.success('模板创建成功'); | ||
| } else { | ||
| toast.error('模板创建失败'); | ||
| } | ||
| } | ||
|
|
||
| fetchTemplates(selectedCategory, searchQuery); | ||
| } catch { | ||
| toast.error(editingTemplate ? '模板更新失败' : '模板创建失败'); | ||
| } finally { | ||
| setIsFormOpen(false); | ||
| setEditingTemplate(null); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
handleConfirmTemplateForm 函数中存在一些重复的代码逻辑,尤其是在处理创建和编辑模式下的 API 调用和 toast 提示时。这会增加维护成本。
建议将通用逻辑提取出来,以简化代码并提高可读性。例如,可以根据 editingTemplate 是否存在来确定操作类型(创建或更新),然后统一处理 API 调用和结果反馈。
const handleConfirmTemplateForm = async (formValues: any) => {
const isEditing = !!editingTemplate;
const action = isEditing ? '更新' : '创建';
try {
const res = await (isEditing
? TemplateApi.UpdateTemplate(editingTemplate.id, formValues)
: TemplateApi.CreateTemplate(formValues));
if (res.data?.code === 200) {
toast.success(`模板${action}成功`);
} else {
toast.error(res.data?.message || `模板${action}失败`);
}
fetchTemplates(selectedCategory, searchQuery);
} catch {
toast.error(`模板${action}失败`);
} finally {
setIsFormOpen(false);
setEditingTemplate(null);
}
};
| {template.tags.split(',').map((tag) => ( | ||
| <span | ||
| key={tag} | ||
| className="inline-flex items-center px-1.5 py-0.5 rounded text-xs bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400" | ||
| > | ||
| {tag} | ||
| </span> | ||
| ))} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
当 template.tags 是一个空字符串时,template.tags.split(',') 会返回一个包含单个空字符串的数组 ['']。这会导致在 UI 上渲染一个空的标签 <span>。
为了避免这种情况,建议在 map 之前使用 filter(Boolean) 来过滤掉空字符串。
{template.tags.split(',').filter(Boolean).map((tag) => (
<span
key={tag}
className="inline-flex items-center px-1.5 py-0.5 rounded text-xs bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400"
>
{tag}
</span>
))}
| {template.tags.split(',').length > 3 && ( | ||
| <span className="text-xs text-gray-400 dark:text-gray-500"> | ||
| +{template.tags.length - 3} | ||
| +{template.tags.split(',').length - 3} | ||
| </span> | ||
| )} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| @@ -53,16 +54,24 @@ const formSchema = z.object({ | |||
|
|
|||
| type FormValues = z.infer<typeof formSchema>; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| export interface CreateTemplate { | ||
| name: string; | ||
| description: string; | ||
| content: string; | ||
| tags: string; | ||
| category?: TemplateCategory; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
在 CreateTemplate 接口中,category 字段被定义为可选的 (category?: TemplateCategory)。然而,在 TemplateFormDialog 组件中,表单验证 formSchema 将 category 字段设置为必填项 (z.string().min(1, '请选择分类'))。
这种类型定义与实际表单验证逻辑不一致。为了保持一致性并增强类型安全,建议将 CreateTemplate 接口中的 category 字段修改为必填项。
| export interface CreateTemplate { | |
| name: string; | |
| description: string; | |
| content: string; | |
| tags: string; | |
| category?: TemplateCategory; | |
| } | |
| export interface CreateTemplate { | |
| name: string; | |
| description: string; | |
| content: string; | |
| tags: string; | |
| category: TemplateCategory; | |
| } |
PR 描述
PR 类型
Issue 关联
Closes #
其他信息