Skip to content

字段定义

语法

ruby
field :name, :type, **options

支持的类型

类型说明表单组件
:string单行文本ProFormText
:text多行文本ProFormTextArea
:rich_text富文本RichTextEditor
:integer整数ProFormDigit
:decimal小数ProFormDigit
:float浮点数ProFormDigit
:boolean布尔值ProFormSwitch
:date日期ProFormDatePicker
:datetime日期时间ProFormDateTimePicker
:enum枚举ProFormRadio / ProFormSelect
:image图片ImageUpload
:file文件FileUpload
:tags标签TagInput
:jsonJSONJsonEditor
:color颜色ColorPicker
:urlURLProFormText
:email邮箱ProFormText
:reference关联引用ProFormSelect
自定义通过 Plugin System 注册自定义组件

选项

选项说明示例
required必填field :title, :string, required: true
default默认值field :status, :enum, default: "draft"
placeholder占位符field :title, :string, placeholder: "请输入标题"
values枚举值field :status, :enum, values: %w[draft published]
accept文件类型field :cover, :image, accept: %w[image/jpeg image/png]
max_size最大文件大小field :cover, :image, max_size: 10.megabytes

enum 自动集成

ruby
# 这行 DSL 会自动调用 ActiveRecord enum
field :status, :enum, values: %w[draft published archived]

# 等价于手动写:
# enum :status, { draft: 0, published: 1, archived: 2 }

字段权限(visible_if)

使用 visible_if 选项可以基于当前用户角色控制字段可见性:

ruby
class Product < ApplicationRecord
  include Zen::ModelDsl

  field :name, :string, required: true
  field :price, :decimal, visible_if: ->(user) { user.has_any_role?(:admin, :finance) }
  field :cost, :decimal, visible_if: ->(user) { user.has_role?(:admin) }
  field :internal_notes, :text, visible_if: ->(user) { user.has_role?(:admin) }
end

说明:

  • visible_if 接收一个 Lambda,参数是当前用户对象
  • 返回 true 表示字段可见,false 表示隐藏
  • 使用 Rolify 的 has_role?has_any_role? 等方法
  • super_admin 角色始终可见所有字段
  • 后端在 zen_meta(user) 中过滤字段,前端自动只渲染可见字段

权限检查顺序:

  1. super_admin → 绕过所有检查
  2. visible_if Lambda → 动态判断
  3. 默认 → 可见

字段联动条件

visible_when(显示条件)

当指定字段满足条件时显示:

ruby
# 简单值匹配
field :end_date, :date, visible_when: { status: "published" }

# 数组匹配(任一满足)
field :reason, :text, visible_when: { status: ["rejected", "cancelled"] }

# 多条件(AND 逻辑)
field :priority, :enum, visible_when: { status: "published", type: "urgent" }

disabled_when(禁用条件)

当指定字段满足条件时禁用:

ruby
# 已验证时禁用邮箱字段
field :email, :string, disabled_when: { verified: true }

# 已发布时禁用价格字段
field :price, :decimal, disabled_when: { status: "published" }

# 多条件
field :discount, :decimal, disabled_when: { status: "published", locked: true }

required_when(必填条件)

当指定字段满足条件时变为必填:

ruby
# 拒绝时必须填写原因
field :reason, :text, required_when: { status: "rejected" }

# 短信联系时必须填手机号
field :phone, :string, required_when: { contact_method: "sms" }

# 多条件
field :notes, :text, required_when: { status: "published", type: "important" }

组合使用

ruby
field :end_date, :date,
  visible_when: { status: "published" },
  disabled_when: { locked: true },
  required_when: { type: "important" }

操作符

除了简单值匹配,还支持操作符:

ruby
# 大于
field :discount, :decimal, visible_when: { price: { operator: "gt", value: 100 } }

# 包含
field :notes, :text, visible_when: { tags: { operator: "contains", value: "vip" } }

# 非空
field :reason, :text, visible_when: { rejected_at: { operator: "notEmpty" } }
操作符说明示例
eq等于{ operator: "eq", value: "draft" }
neq不等于{ operator: "neq", value: "published" }
in在数组中{ operator: "in", value: ["draft", "review"] }
notIn不在数组中{ operator: "notIn", value: ["archived"] }
gt大于{ operator: "gt", value: 100 }
gte大于等于{ operator: "gte", value: 0 }
lt小于{ operator: "lt", value: 1000 }
lte小于等于{ operator: "lte", value: 999 }
contains包含字符串{ operator: "contains", value: "vip" }
notEmpty非空{ operator: "notEmpty" }
empty为空{ operator: "empty" }

自定义字段类型

通过 Plugin System 可以注册自定义字段类型:

tsx
// app/frontend/entrypoints/fieldTypes.ts
import { registerFieldType } from '@/modules/dsl'
import MapPicker from '@/components/MapPicker'
import MapView from '@/components/MapView'

registerFieldType('map', {
  FormComponent: MapPicker,      // 表单组件
  DisplayComponent: MapView,     // 展示组件
  ColumnComponent: MapPreview,   // 列表组件(可选)
  parse: (value) => JSON.parse(value),      // 反序列化
  serialize: (value) => JSON.stringify(value), // 序列化
})
ruby
# Model DSL 中使用
class Store < ApplicationRecord
  include Zen::ModelDsl

  field :name, :string, required: true
  field :location, :map  # 使用自定义类型
end

注册选项:

选项说明必填
FormComponent表单编辑组件
DisplayComponent只读展示组件
ColumnComponent列表列渲染组件
parse存储值 → 组件值
serialize组件值 → 存储值
defaultProps默认属性
searchable是否支持搜索

API:

tsx
import {
  registerFieldType,
  getFieldType,
  hasFieldType,
  parseFieldValue,
  serializeFieldValue,
} from '@/modules/dsl'