Skip to content

Conversation

@Hemomoo
Copy link
Collaborator

@Hemomoo Hemomoo commented Jan 31, 2026

PR 描述

PR 类型

  1. 将前端模版存储在数据库
  2. 增加删除,更新模版
  • 🐛 Bug 修复
  • ✨ 新功能
  • 💄 UI/UX 改进
  • ♻️ 重构
  • 🚀 性能优化
  • 📝 文档更新
  • 🔄 其他

Issue 关联

Closes #

其他信息

邓亮 added 3 commits January 31, 2026 11:11
- 新增模板类型定义和服务 API 模块
- 替换硬编码模板数据为后端 API 调用
- 实现模板的创建、查询和删除功能
- 更新模板列表的过滤和搜索逻辑以使用后端接口
重构模板表单为可复用的组件,支持创建和编辑两种模式
添加 isSystem 字段区分系统模板
将 PUT 请求改为 PATCH 方法
@netlify
Copy link

netlify bot commented Jan 31, 2026

Deploy Preview for ornate-blancmange-89db6b ready!

Name Link
🔨 Latest commit fdc2d87
🔍 Latest deploy log https://app.netlify.com/projects/ornate-blancmange-89db6b/deploys/697da707064c0400088c78ea
😎 Deploy Preview https://deploy-preview-273--ornate-blancmange-89db6b.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @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

  • 模板管理后端化: 将前端硬编码的模板数据迁移至数据库管理,实现了模板的动态加载和持久化。
  • 模板 CRUD 功能: 新增了模板的创建、读取、更新和删除 (CRUD) 功能,用户现在可以完全管理自定义模板。
  • 统一模板表单: 将模板创建和编辑功能整合到一个通用的 TemplateFormDialog 组件中,提升了代码复用性和用户体验。
  • API 服务集成: 引入了 TemplateApi 服务层,用于与后端模板接口进行交互,包括获取、创建、更新和删除模板。
  • 系统模板保护: 对系统预设模板增加了保护机制,非系统模板才允许进行删除和编辑操作。
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a 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.

Comment on lines +280 to 286
<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"
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

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.

Suggested change
<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) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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 的数据结构是正确的。

Comment on lines +162 to 191
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);
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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);
    }
  };
Comment on lines +313 to 320
{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>
))}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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>
                        ))}
Comment on lines +321 to 325
{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>
)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

与上一个评论类似,这里的 template.tags.split(',').length 也没有过滤空标签,可能导致“更多”标签的计数不准确。

建议同样使用 filter(Boolean) 来确保只计算有效的标签数量。

                        {template.tags.split(',').filter(Boolean).length > 3 && (
                          <span className="text-xs text-gray-400 dark:text-gray-500">
                            +{template.tags.split(',').filter(Boolean).length - 3}
                          </span>
                        )}
@@ -53,16 +54,24 @@ const formSchema = z.object({

type FormValues = z.infer<typeof formSchema>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

FormValues 类型目前只在 TemplateFormDialog.tsx 文件内部使用。为了在父组件 TemplatesTab.tsx 中对表单数据进行类型约束,从而提高代码的类型安全性,建议将此类型导出。

Suggested change
type FormValues = z.infer<typeof formSchema>;
export type FormValues = z.infer<typeof formSchema>;
Comment on lines +12 to +18
export interface CreateTemplate {
name: string;
description: string;
content: string;
tags: string;
category?: TemplateCategory;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

CreateTemplate 接口中,category 字段被定义为可选的 (category?: TemplateCategory)。然而,在 TemplateFormDialog 组件中,表单验证 formSchemacategory 字段设置为必填项 (z.string().min(1, '请选择分类'))。

这种类型定义与实际表单验证逻辑不一致。为了保持一致性并增强类型安全,建议将 CreateTemplate 接口中的 category 字段修改为必填项。

Suggested change
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;
}
@Hemomoo Hemomoo changed the title Feat : 模版增加都数据库 Jan 31, 2026
@xun082 xun082 merged commit 611dc0d into xun082:main Jan 31, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants