Admin 生成器
zen:admin 生成完整的管理后台 CRUD 页面。
基本用法
bash
rails generate zen:admin Article title:string body:text status:enum --rich_text=body模式
Modal 模式(默认)
列表和表单在同一个页面,表单以 Modal 弹出。
bash
rails generate zen:admin Article --modal适合字段较少(≤8 个)的模型。
Page 模式
列表和表单是独立页面,表单在单独 URL。
bash
rails generate zen:admin Article --page适合字段较多或需要富文本编辑的模型。
选项
| 选项 | 说明 | 示例 |
|---|---|---|
--modal | Modal 模式(默认) | --modal |
--page | Page 模式 | --page |
--rich_text | 富文本字段 | --rich_text=body |
--image | 图片上传字段 | --image=cover |
--file | 文件上传字段 | --file=attachment |
--tags | 标签字段 | --tags=keywords |
--enums | 枚举值 (JSON) | --enums='{"status":["draft","published"]}' |
--belongs-to | belongs_to 关联 | --belongs-to=category |
--has-many | has_many 关联 | --has-many=comments |
--product | 产品形态 | --product=kanban |
枚举处理
bash
rails generate zen:admin Article title:string status:enum \
--enums='{"status":["draft","published","archived"]}'生成器会自动在 Model 中添加 enum 声明(通过 DSL field :status, :enum)。
表关联
belongs_to 关联
假设文章属于分类(Category),先创建 Category,再创建 Article 并关联:
bash
# 第一步:创建主表(Category)
rails generate zen:admin Category name:string
# 第二步:创建关联表(Article),使用 --belongs-to 指定关联
rails generate zen:admin Article title:string body:text category_id:integer \
--belongs-to=category生成器会自动:
- Model 中添加
belongs_to :category - Form 中渲染下拉选择框(从 Category 表读取数据)
- Controller 中加载关联数据传给前端
- Table 中显示关联字段名称(而非 ID)
has_many 关联
假设用户有多篇文章:
bash
# 先创建 User
rails generate zen:admin User name:string email:string
# 再创建 Article,关联到 User
rails generate zen:admin Article title:string user_id:integer \
--belongs-to=user多重关联
同时关联多个模型:
bash
rails generate zen:admin Article title:string body:text \
user_id:integer category_id:integer \
--belongs-to=user,category关联后的手动调整
生成后需要手动补充:
1. Model 中添加 has_many
ruby
# app/models/user.rb
class User < ApplicationRecord
include Zen::ModelDsl
has_many :articles, dependent: :destroy
end
# app/models/category.rb
class Category < ApplicationRecord
include Zen::ModelDsl
has_many :articles, dependent: :destroy
end2. Migration 添加外键索引
ruby
# db/migrate/xxx_add_index_to_articles.rb
class AddIndexToArticles < ActiveRecord::Migration[8.0]
def change
add_index :articles, :user_id
add_index :articles, :category_id
end
end3. DSL 配置关联显示
ruby
# app/models/article.rb
class Article < ApplicationRecord
include Zen::ModelDsl
field :title, :string, required: true
field :body, :rich_text
field :status, :enum, values: %w[draft published archived]
belongs_to :user
belongs_to :category
display do
list do
column :title, link: true
column :category_name # 显示关联名称
column :user_name # 显示关联名称
column :status, badge: true
end
form do
section "内容" do
field :title, required: true
field :body, as: :rich_text
field :category_id, as: :select # 关联下拉框
field :user_id, as: :select # 关联下拉框
end
end
end
# 辅助方法:获取关联名称
def category_name
category&.name || '-'
end
def user_name
user&.name || '-'
end
end产品形态
bash
rails generate zen:admin Task title:string status:enum \
--enums='{"status":["todo","done"]}' \
--product=kanban支持形态:crud(默认)、kanban、calendar、gallery。
生成的 Controller
ruby
# app/controllers/admin/articles_controller.rb
module Admin
class ArticlesController < AdminController
before_action :set_article, only: [:show, :update, :destroy]
def index
@articles = policy_scope(Article)
render inertia: "admin/articles/Index",
props: zen_props(Article, articles: @articles.as_json)
end
def create
@article = Article.new(article_params)
authorize @article
# ...
end
end
end生成器自动集成 zen_props(DSL 元数据)、policy_scope(Pundit 权限)、authorize。
分页、搜索、过滤
生成器自动集成 Pagy(分页)和 Ransack(搜索、过滤)。
DSL 配置
在 Model 中配置:
ruby
class User < ApplicationRecord
include Zen::ModelDsl
field :email, :string, required: true
field :username, :string
field :role, :enum, values: %w[admin editor viewer]
display do
list do
paginate true # 启用分页
searchable :email, :username # 启用搜索
filterable :role # 启用过滤
column :email
column :username
column :role, badge: true
end
end
end生成代码说明
后端(Controller)
ruby
# app/controllers/admin/users_controller.rb
class Admin::UsersController < AdminController
include Pagy::Method
def index
q = User.ransack(search_params)
base_query = q.result(distinct: true)
@pagy, users = pagy(:offset, base_query, **pagy_params)
render inertia: "admin/users/Index",
props: zen_props(User, users: users.as_json,
pagination: {
page: @pagy.page,
per_page: @pagy.limit,
total: @pagy.count,
pages: @pagy.pages
}
)
end
private
def search_params
params[:q]&.permit(:email_cont, :username_cont, :role_eq)
end
def pagy_params
{
page: params[:page] || 1,
limit: params[:per_page] || 20
}
end
end前端(DslTable)
生成器生成的前端页面使用 DslTable 组件,自动读取 zen_meta 渲染表格、搜索、过滤、分页:
tsx
// app/frontend/pages/admin/users/Index.tsx
import { DslTable } from '@/modules/dsl/DslTable'
<DslTable
meta={meta} // zen_meta(由 zen_props 自动注入)
data={users} // 列表数据
basePath="/admin/users" // 资源路径
serverSide // 服务端分页模式
pagination={pagination} // 分页元数据(page/per_page/total)
onServerChange={(params) => {
// 分页/搜索/过滤变化时,通过 Inertia 重新请求(保留页面状态)
router.visit('/admin/users', {
data: params,
preserveState: true,
preserveScroll: true,
})
}}
/>提示:
DslTable会根据meta.display.list自动渲染列、搜索框、过滤菜单。分页参数(page、per_page)和搜索参数(q[email_cont]等)会通过onServerChange回调传给后端。
Ransack 谓词
| 谓词 | 说明 | 示例 |
|---|---|---|
_cont | 包含(模糊搜索) | email_cont |
_eq | 等于(精确匹配) | role_eq |
_gteq | 大于等于 | created_at_gteq |
_lteq | 小于等于 | created_at_lteq |
_in | 在列表中 | status_in |
自定义
如果不使用 DSL,可手动在 Controller 中添加:
ruby
include Pagy::Method
def index
q = Model.ransack(params[:q])
@pagy, records = pagy(:offset, q.result, **pagy_params)
# ...
end添加菜单(必做)
生成器不会自动添加菜单项,需要手动配置。
- 打开
app/frontend/config/adminMenus.tsx - 在
menuRoutes.routes数组中添加菜单项:
tsx
{
path: '/admin/articles',
name: '文章管理',
icon: <FileTextOutlined />,
}- 在文件顶部 import 对应图标
提示:不添加菜单项也能通过 URL 直接访问,但侧边栏不会显示入口。
自定义
生成后可直接编辑生成的文件。推荐方式:
- 修改 Model DSL → 页面自动更新
- 如需深度定制,直接编辑
.tsx文件