zen_admin 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +76 -0
- data/Rakefile +6 -0
- data/app/assets/stylesheets/zen_admin/application.css +15 -0
- data/app/controllers/zen_admin/application_controller.rb +27 -0
- data/app/controllers/zen_admin/dashboard_controller.rb +9 -0
- data/app/controllers/zen_admin/sessions_controller.rb +40 -0
- data/app/controllers/zen_admin/ui/resources_controller.rb +331 -0
- data/app/helpers/zen_admin/application_helper.rb +80 -0
- data/app/javascript/controllers/application.js +9 -0
- data/app/javascript/controllers/bulk_actions_controller.js +55 -0
- data/app/javascript/controllers/clipboard_controller.js +27 -0
- data/app/javascript/controllers/index.js +5 -0
- data/app/javascript/controllers/layout_controller.js +48 -0
- data/app/javascript/controllers/menu_group_controller.js +40 -0
- data/app/javascript/controllers/modal_controller.js +31 -0
- data/app/javascript/controllers/toast_controller.js +14 -0
- data/app/javascript/controllers/tom_select_controller.js +35 -0
- data/app/javascript/zen_admin/application.js +12 -0
- data/app/jobs/zen_admin/application_job.rb +4 -0
- data/app/mailers/zen_admin/application_mailer.rb +6 -0
- data/app/models/zen_admin/application_record.rb +5 -0
- data/app/models/zen_admin/asset.rb +43 -0
- data/app/models/zen_admin/audit_log.rb +116 -0
- data/app/models/zen_admin/permission.rb +73 -0
- data/app/models/zen_admin/role.rb +45 -0
- data/app/models/zen_admin/trash_item.rb +98 -0
- data/app/models/zen_admin/user.rb +37 -0
- data/app/policies/zen_admin/application_policy.rb +53 -0
- data/app/policies/zen_admin/resource_policy.rb +48 -0
- data/app/views/layouts/zen_admin/_flash.html.erb +9 -0
- data/app/views/layouts/zen_admin/_sidebar.html.erb +115 -0
- data/app/views/layouts/zen_admin/application.html.erb +98 -0
- data/app/views/zen_admin/builtin/_copy_button.html.erb +9 -0
- data/app/views/zen_admin/builtin/_file_link.html.erb +22 -0
- data/app/views/zen_admin/dashboard/index.html.erb +27 -0
- data/app/views/zen_admin/sessions/new.html.erb +47 -0
- data/app/views/zen_admin/ui/resources/_form.html.erb +226 -0
- data/app/views/zen_admin/ui/resources/_row.html.erb +170 -0
- data/app/views/zen_admin/ui/resources/create.turbo_stream.erb +2 -0
- data/app/views/zen_admin/ui/resources/destroy.turbo_stream.erb +2 -0
- data/app/views/zen_admin/ui/resources/edit.html.erb +11 -0
- data/app/views/zen_admin/ui/resources/index.html.erb +285 -0
- data/app/views/zen_admin/ui/resources/new.html.erb +11 -0
- data/app/views/zen_admin/ui/resources/show.html.erb +133 -0
- data/app/views/zen_admin/ui/resources/update.turbo_stream.erb +2 -0
- data/config/importmap.rb +10 -0
- data/config/locales/en.yml +107 -0
- data/config/locales/zh-CN.yml +110 -0
- data/config/routes.rb +24 -0
- data/lib/generators/zen_admin/admin_user/admin_user_generator.rb +55 -0
- data/lib/generators/zen_admin/install/install_generator.rb +50 -0
- data/lib/generators/zen_admin/install/templates/asset.rb +46 -0
- data/lib/generators/zen_admin/install/templates/create_zen_admin_assets.rb.erb +9 -0
- data/lib/generators/zen_admin/install/templates/create_zen_admin_audit_logs.rb.erb +18 -0
- data/lib/generators/zen_admin/install/templates/create_zen_admin_rbac.rb.erb +57 -0
- data/lib/generators/zen_admin/install/templates/create_zen_admin_trash_items.rb.erb +13 -0
- data/lib/generators/zen_admin/install/templates/zen_admin.rb +17 -0
- data/lib/generators/zen_admin/install/templates/zh-CN.yml +15 -0
- data/lib/generators/zen_admin/model/model_generator.rb +65 -0
- data/lib/generators/zen_admin/model/templates/zen_admin_config.rb.erb +80 -0
- data/lib/generators/zen_admin/rbac_install/rbac_install_generator.rb +52 -0
- data/lib/generators/zen_admin/rbac_install/templates/create_zen_admin_rbac.rb.erb +42 -0
- data/lib/generators/zen_admin/rbac_install/templates/seeds.rb.erb +13 -0
- data/lib/tasks/zen_admin_tasks.rake +4 -0
- data/lib/zen_admin/authenticatable.rb +44 -0
- data/lib/zen_admin/builtin.rb +77 -0
- data/lib/zen_admin/configuration.rb +17 -0
- data/lib/zen_admin/core/field.rb +34 -0
- data/lib/zen_admin/core/filter.rb +16 -0
- data/lib/zen_admin/core/resource.rb +175 -0
- data/lib/zen_admin/core.rb +3 -0
- data/lib/zen_admin/engine.rb +67 -0
- data/lib/zen_admin/link_registerable.rb +11 -0
- data/lib/zen_admin/registerable.rb +17 -0
- data/lib/zen_admin/registry.rb +83 -0
- data/lib/zen_admin/schema/serializer.rb +15 -0
- data/lib/zen_admin/schema.rb +1 -0
- data/lib/zen_admin/version.rb +3 -0
- data/lib/zen_admin.rb +51 -0
- metadata +233 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
zh-CN:
|
|
2
|
+
zen_admin:
|
|
3
|
+
ui:
|
|
4
|
+
search: "搜索"
|
|
5
|
+
show_limit: "显示"
|
|
6
|
+
items_per_page: "条"
|
|
7
|
+
no_data: "暂无数据。"
|
|
8
|
+
list_suffix: "列表"
|
|
9
|
+
actions: "操作"
|
|
10
|
+
confirm_delete: "确认删除吗?"
|
|
11
|
+
confirm_bulk_delete: "确认删除选中的 {count} 条数据吗?"
|
|
12
|
+
select_all: "全选"
|
|
13
|
+
close: "关闭"
|
|
14
|
+
save: "保存"
|
|
15
|
+
cancel: "取消"
|
|
16
|
+
errors: "错误!"
|
|
17
|
+
related_records: "相关记录"
|
|
18
|
+
no_related_records: "暂无相关记录。"
|
|
19
|
+
click_to_upload: "点击上传 %{field}"
|
|
20
|
+
change_image: "点击更换预览图"
|
|
21
|
+
multi_select_help: "按住 Ctrl (或 Command) 可选择多个"
|
|
22
|
+
actions:
|
|
23
|
+
new: "新增 %{resource}"
|
|
24
|
+
view: "查看"
|
|
25
|
+
edit: "编辑"
|
|
26
|
+
delete: "删除"
|
|
27
|
+
bulk_delete: "批量删除"
|
|
28
|
+
copy: "复制"
|
|
29
|
+
restore: "还原"
|
|
30
|
+
move_to_trash: "移至回收站"
|
|
31
|
+
messages:
|
|
32
|
+
created: "成功创建了 %{resource}。"
|
|
33
|
+
updated: "成功更新了 %{resource}。"
|
|
34
|
+
destroyed: "成功删除了 %{resource}。"
|
|
35
|
+
bulk_destroyed: "成功批量删除了 %{count} 条记录。"
|
|
36
|
+
restored: "成功还原了 %{resource}。"
|
|
37
|
+
moved_to_trash: "已将 %{resource} 移至回收站。"
|
|
38
|
+
|
|
39
|
+
# 内置核心资源定义
|
|
40
|
+
builtin:
|
|
41
|
+
assets: "文件资源库"
|
|
42
|
+
attachments: "系统自动附件"
|
|
43
|
+
attachment_info: "这里列出了系统中所有通过 ActiveStorage 自动关联的附件(如富文本插图等)。"
|
|
44
|
+
|
|
45
|
+
models:
|
|
46
|
+
# 映射 ActiveStorage 关联
|
|
47
|
+
"active_storage/attachment":
|
|
48
|
+
one: "系统自动附件"
|
|
49
|
+
other: "系统自动附件"
|
|
50
|
+
fields:
|
|
51
|
+
id: "预览"
|
|
52
|
+
record_type: "所属模型"
|
|
53
|
+
name: "字段名"
|
|
54
|
+
blob_filename: "原始文件名"
|
|
55
|
+
blob_id: "复制链接"
|
|
56
|
+
record: "来源对象"
|
|
57
|
+
blob: "原始文件"
|
|
58
|
+
# 映射内置资产模型
|
|
59
|
+
asset:
|
|
60
|
+
one: "文件资源库"
|
|
61
|
+
other: "文件资源库"
|
|
62
|
+
fields:
|
|
63
|
+
file: "预览"
|
|
64
|
+
name: "文件名称/备注"
|
|
65
|
+
id: "复制链接"
|
|
66
|
+
created_at: "上传时间"
|
|
67
|
+
user:
|
|
68
|
+
one: "系统用户"
|
|
69
|
+
other: "系统用户"
|
|
70
|
+
fields:
|
|
71
|
+
username: "用户名"
|
|
72
|
+
password: "密码"
|
|
73
|
+
roles: "角色"
|
|
74
|
+
created_at: "创建时间"
|
|
75
|
+
role:
|
|
76
|
+
one: "角色管理"
|
|
77
|
+
other: "角色管理"
|
|
78
|
+
fields:
|
|
79
|
+
name: "角色名称"
|
|
80
|
+
code: "标识符"
|
|
81
|
+
permissions: "拥有权限"
|
|
82
|
+
permission_ids: "权限分配"
|
|
83
|
+
permission:
|
|
84
|
+
one: "权限点"
|
|
85
|
+
other: "权限点"
|
|
86
|
+
fields:
|
|
87
|
+
name: "权限名称"
|
|
88
|
+
code: "标识符"
|
|
89
|
+
audit_log:
|
|
90
|
+
one: "操作审计"
|
|
91
|
+
other: "操作审计"
|
|
92
|
+
fields:
|
|
93
|
+
admin_username: "操作员"
|
|
94
|
+
resource_type: "资源类型"
|
|
95
|
+
resource_id: "ID"
|
|
96
|
+
action: "操作"
|
|
97
|
+
created_at: "时间"
|
|
98
|
+
changes_data: "数据变更"
|
|
99
|
+
ip_address: "IP地址"
|
|
100
|
+
trash_item:
|
|
101
|
+
one: "回收站记录"
|
|
102
|
+
other: "回收站记录"
|
|
103
|
+
|
|
104
|
+
views:
|
|
105
|
+
pagination:
|
|
106
|
+
first: "首页"
|
|
107
|
+
last: "末页"
|
|
108
|
+
previous: "上一页"
|
|
109
|
+
next: "下一页"
|
|
110
|
+
truncate: "..."
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
ZenAdmin::Engine.routes.draw do
|
|
2
|
+
next unless ZenAdmin.configuration.enable_ui
|
|
3
|
+
|
|
4
|
+
# 1. 静态路由优先级最高 (防止被作为资源名匹配)
|
|
5
|
+
get "login", to: "sessions#new", as: :login
|
|
6
|
+
post "login", to: "sessions#create"
|
|
7
|
+
delete "logout", to: "sessions#destroy", as: :logout
|
|
8
|
+
root to: "dashboard#index"
|
|
9
|
+
|
|
10
|
+
# 2. 动态资源路由 (直接挂载在 / 下)
|
|
11
|
+
# /admin/posts, /admin/posts/5, etc.
|
|
12
|
+
scope ":resource_name", module: "ui" do
|
|
13
|
+
post "bulk_action", to: "resources#bulk_action", as: :ui_resource_bulk_action
|
|
14
|
+
post ":id/member_action", to: "resources#member_action", as: :ui_resource_member_action
|
|
15
|
+
post ":id/restore", to: "resources#restore", as: :ui_resource_restore
|
|
16
|
+
delete "empty_trash", to: "resources#empty_trash", as: :ui_resource_empty_trash
|
|
17
|
+
|
|
18
|
+
resources :resources,
|
|
19
|
+
as: :ui_resource,
|
|
20
|
+
path: "",
|
|
21
|
+
only: [:index, :new, :create, :edit, :update, :show, :destroy],
|
|
22
|
+
param: :id
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module ZenAdmin
|
|
2
|
+
module Generators
|
|
3
|
+
class AdminUserGenerator < Rails::Generators::Base
|
|
4
|
+
desc "创建 ZenAdmin 管理员账号"
|
|
5
|
+
|
|
6
|
+
def create_admin_user
|
|
7
|
+
username = ask("请输入用户名 [admin]:")
|
|
8
|
+
username = "admin" if username.blank?
|
|
9
|
+
|
|
10
|
+
# 检查用户是否已存在
|
|
11
|
+
if ZenAdmin::User.exists?(username: username)
|
|
12
|
+
say "错误: 用户 '#{username}' 已存在。", :red
|
|
13
|
+
return
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
password = ask_password("请输入密码:")
|
|
17
|
+
|
|
18
|
+
# 简单强度校验:长度小于 8 位
|
|
19
|
+
if password.length < 8
|
|
20
|
+
say "提示: 密码长度小于 8 位,安全性较低。", :yellow
|
|
21
|
+
unless yes?("确定要使用这个弱密码吗?(y/n)")
|
|
22
|
+
password = ask_password("请重新输入更强的密码:")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
begin
|
|
27
|
+
user = ZenAdmin::User.new(username: username, password: password)
|
|
28
|
+
|
|
29
|
+
# 自动确保“超级管理员”角色存在
|
|
30
|
+
admin_role = ZenAdmin::Role.find_or_create_by!(code: "superadmin") do |r|
|
|
31
|
+
r.name = "超级管理员"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
user.roles << admin_role
|
|
35
|
+
say "已分配超级管理员权限。", :green
|
|
36
|
+
|
|
37
|
+
if user.save
|
|
38
|
+
say "成功: 管理员 '#{username}' 已创建!", :green
|
|
39
|
+
else
|
|
40
|
+
say "保存失败: #{user.errors.full_messages.join(', ')}", :red
|
|
41
|
+
end
|
|
42
|
+
rescue => e
|
|
43
|
+
say "发生异常: #{e.message}", :red
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def ask_password(prompt)
|
|
50
|
+
# echo: false 隐藏输入,安全性更好
|
|
51
|
+
ask(prompt, echo: false)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require "rails/generators/active_record"
|
|
2
|
+
|
|
3
|
+
module ZenAdmin
|
|
4
|
+
module Generators
|
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
|
6
|
+
include ActiveRecord::Generators::Migration
|
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
|
8
|
+
|
|
9
|
+
desc "安装 ZenAdmin 基础配置(硬编码登录模式)"
|
|
10
|
+
|
|
11
|
+
def copy_initializer
|
|
12
|
+
template "zen_admin.rb", "config/initializers/zen_admin.rb"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def copy_migrations
|
|
16
|
+
migration_template "create_zen_admin_assets.rb.erb", "db/migrate/create_zen_admin_assets.rb"
|
|
17
|
+
migration_template "create_zen_admin_audit_logs.rb.erb", "db/migrate/create_zen_admin_audit_logs.rb"
|
|
18
|
+
migration_template "create_zen_admin_trash_items.rb.erb", "db/migrate/create_zen_admin_trash_items.rb"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def copy_models
|
|
22
|
+
template "asset.rb", "app/models/zen_admin/asset.rb"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def copy_locales
|
|
26
|
+
template "zh-CN.yml", "config/locales/zen_admin.zh-CN.yml"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def install_dependencies
|
|
30
|
+
say "正在安装 Action Text 和 Active Storage 依赖...", :yellow
|
|
31
|
+
rails_command "action_text:install"
|
|
32
|
+
# 注意: action_text:install 也会顺带安装 active_storage:install
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def add_routes
|
|
36
|
+
route 'mount ZenAdmin::Engine => "/admin"'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def show_readme
|
|
40
|
+
say "\nZenAdmin 基础配置安装成功!", :green
|
|
41
|
+
say "你可以通过 config/initializers/zen_admin.rb 修改默认账号密码。", :yellow
|
|
42
|
+
say "接下来请运行: ", :yellow
|
|
43
|
+
say " bin/rails db:migrate", :white
|
|
44
|
+
say "直接访问 /admin 使用硬编码方式登录。", :white
|
|
45
|
+
say "\n如果你需要基于数据库的角色权限管理,请运行:", :yellow
|
|
46
|
+
say " bin/rails g zen_admin:rbac_install", :white
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module ZenAdmin
|
|
2
|
+
class Asset < ApplicationRecord
|
|
3
|
+
self.table_name = "zen_admin_assets"
|
|
4
|
+
has_one_attached :file
|
|
5
|
+
|
|
6
|
+
zen_admin do |resource|
|
|
7
|
+
resource.label :'zen_admin.builtin.assets', :'zen_admin.builtin.assets'
|
|
8
|
+
resource.menu label: :'zen_admin.builtin.assets', icon: "fas fa-images", position: 99
|
|
9
|
+
resource.enable_batch_actions
|
|
10
|
+
resource.batch_action :delete, label: I18n.t('zen_admin.actions.bulk_delete'), type: :danger, confirm: I18n.t('zen_admin.ui.confirm_delete') do |records|
|
|
11
|
+
records.destroy_all
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
resource.search do
|
|
15
|
+
field :name
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
resource.list do
|
|
19
|
+
field :file, label: "Preview"
|
|
20
|
+
field :name, label: "Name"
|
|
21
|
+
field :id, label: I18n.t('zen_admin.actions.copy') do |asset|
|
|
22
|
+
if asset.file.attached?
|
|
23
|
+
url = Rails.application.routes.url_helpers.rails_blob_url(asset.file, host: "localhost:3000") rescue "#"
|
|
24
|
+
render "zen_admin/builtin/copy_button", url: url
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
field :created_at, label: "Created At"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
resource.form do
|
|
31
|
+
field :name, label: "Name/Note", required: true
|
|
32
|
+
field :file, label: "File", type: :file
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
resource.show do
|
|
36
|
+
field :name, label: "Name"
|
|
37
|
+
field :file, label: "Attachment Details" do |asset|
|
|
38
|
+
if asset.file.attached?
|
|
39
|
+
url = Rails.application.routes.url_helpers.rails_blob_url(asset.file, host: "localhost:3000") rescue ""
|
|
40
|
+
render "zen_admin/builtin/file_link", url: url
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class CreateZenAdminAuditLogs < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :zen_admin_audit_logs do |t|
|
|
4
|
+
t.string :admin_user_id
|
|
5
|
+
t.string :admin_username
|
|
6
|
+
t.string :resource_type
|
|
7
|
+
t.string :resource_id
|
|
8
|
+
t.string :action
|
|
9
|
+
t.text :note
|
|
10
|
+
t.json :changes_data
|
|
11
|
+
t.string :ip_address
|
|
12
|
+
t.string :user_agent
|
|
13
|
+
t.datetime :created_at
|
|
14
|
+
end
|
|
15
|
+
add_index :zen_admin_audit_logs, [:resource_type, :resource_id]
|
|
16
|
+
add_index :zen_admin_audit_logs, :admin_user_id
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
class CreateZenAdminRbac < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
# 1. 用户表
|
|
4
|
+
create_table :zen_admin_users do |t|
|
|
5
|
+
t.string :username, null: false
|
|
6
|
+
t.string :password_digest, null: false
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
add_index :zen_admin_users, :username, unique: true
|
|
10
|
+
|
|
11
|
+
# 2. 角色表
|
|
12
|
+
create_table :zen_admin_roles do |t|
|
|
13
|
+
t.string :name, null: false
|
|
14
|
+
t.string :code, null: false
|
|
15
|
+
t.timestamps
|
|
16
|
+
end
|
|
17
|
+
add_index :zen_admin_roles, :name, unique: true
|
|
18
|
+
add_index :zen_admin_roles, :code, unique: true
|
|
19
|
+
|
|
20
|
+
# 3. 权限表
|
|
21
|
+
create_table :zen_admin_permissions do |t|
|
|
22
|
+
t.string :name, null: false
|
|
23
|
+
t.string :code, null: false
|
|
24
|
+
t.timestamps
|
|
25
|
+
end
|
|
26
|
+
add_index :zen_admin_permissions, :code, unique: true
|
|
27
|
+
|
|
28
|
+
# 4. 用户与角色的多对多中间表
|
|
29
|
+
create_table :zen_admin_users_roles, id: false do |t|
|
|
30
|
+
t.belongs_to :user, null: false, foreign_key: { to_table: :zen_admin_users }
|
|
31
|
+
t.belongs_to :role, null: false, foreign_key: { to_table: :zen_admin_roles }
|
|
32
|
+
end
|
|
33
|
+
add_index :zen_admin_users_roles, [:user_id, :role_id], unique: true
|
|
34
|
+
|
|
35
|
+
# 5. 角色与权限的多对多中间表
|
|
36
|
+
create_table :zen_admin_roles_permissions, id: false do |t|
|
|
37
|
+
t.belongs_to :role, null: false, foreign_key: { to_table: :zen_admin_roles }
|
|
38
|
+
t.belongs_to :permission, null: false, foreign_key: { to_table: :zen_admin_permissions }
|
|
39
|
+
end
|
|
40
|
+
add_index :zen_admin_roles_permissions, [:role_id, :permission_id], unique: true
|
|
41
|
+
|
|
42
|
+
# 6. 审计日志表
|
|
43
|
+
create_table :zen_admin_audit_logs do |t|
|
|
44
|
+
t.string :admin_user_id # 支持硬编码或数据库用户
|
|
45
|
+
t.string :admin_username
|
|
46
|
+
t.string :resource_type
|
|
47
|
+
t.string :resource_id
|
|
48
|
+
t.string :action
|
|
49
|
+
t.json :changes_data
|
|
50
|
+
t.string :ip_address
|
|
51
|
+
t.string :user_agent
|
|
52
|
+
t.datetime :created_at
|
|
53
|
+
end
|
|
54
|
+
add_index :zen_admin_audit_logs, [:resource_type, :resource_id]
|
|
55
|
+
add_index :zen_admin_audit_logs, :admin_user_id
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateZenAdminTrashItems < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
create_table :zen_admin_trash_items do |t|
|
|
4
|
+
t.string :resource_type, null: false
|
|
5
|
+
t.string :resource_id, null: false
|
|
6
|
+
t.string :resource_label
|
|
7
|
+
t.json :data
|
|
8
|
+
t.string :admin_username
|
|
9
|
+
t.datetime :created_at
|
|
10
|
+
end
|
|
11
|
+
add_index :zen_admin_trash_items, [:resource_type, :resource_id]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
ZenAdmin.configure do |config|
|
|
2
|
+
# 是否开启管理后台 UI
|
|
3
|
+
config.enable_ui = true
|
|
4
|
+
|
|
5
|
+
# 后台访问路径
|
|
6
|
+
config.admin_path = "/admin"
|
|
7
|
+
|
|
8
|
+
# 设置显示时区
|
|
9
|
+
config.time_zone = "Beijing"
|
|
10
|
+
|
|
11
|
+
# 设置默认语言 (zh-CN, en)
|
|
12
|
+
config.default_locale = :"zh-CN"
|
|
13
|
+
|
|
14
|
+
# 默认初始账号(建议在通过指令创建真实用户后修改或删除)
|
|
15
|
+
config.username = "admin"
|
|
16
|
+
config.password = "password"
|
|
17
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require "rails/generators/active_record"
|
|
2
|
+
|
|
3
|
+
module ZenAdmin
|
|
4
|
+
module Generators
|
|
5
|
+
class ModelGenerator < Rails::Generators::NamedBase
|
|
6
|
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
|
8
|
+
|
|
9
|
+
def create_model_if_missing
|
|
10
|
+
path = File.join("app/models", class_path, "#{file_name}.rb")
|
|
11
|
+
unless File.exist?(path)
|
|
12
|
+
say "Model #{class_name} not found. Generating it...", :yellow
|
|
13
|
+
generate "model", "#{class_name} #{attributes.map { |a| "#{a.name}:#{a.type}" }.join(' ')}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def inject_zen_admin_config
|
|
18
|
+
path = File.join("app/models", class_path, "#{file_name}.rb")
|
|
19
|
+
|
|
20
|
+
if File.exist?(path)
|
|
21
|
+
@attrs = derived_attributes
|
|
22
|
+
|
|
23
|
+
# 渲染模板
|
|
24
|
+
template_path = File.expand_path("templates/zen_admin_config.rb.erb", __dir__)
|
|
25
|
+
config_content = ERB.new(File.read(template_path), trim_mode: '-').result(binding)
|
|
26
|
+
|
|
27
|
+
inject_into_class path, class_name do
|
|
28
|
+
config_content
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
say "Successfully integrated ZenAdmin DSL into #{class_name}", :green
|
|
32
|
+
else
|
|
33
|
+
say "Error: Model file #{path} not found.", :red
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# 获取模型属性,用于自动填充 list/form
|
|
40
|
+
def derived_attributes
|
|
41
|
+
if attributes.any?
|
|
42
|
+
attributes
|
|
43
|
+
else
|
|
44
|
+
begin
|
|
45
|
+
# 尝试从数据库加载(如果模型已存在)
|
|
46
|
+
klass = class_name.safe_constantize
|
|
47
|
+
if klass && klass < ActiveRecord::Base
|
|
48
|
+
columns = klass.columns.reject { |c| %w[id created_at updated_at deleted_at].include?(c.name) }
|
|
49
|
+
columns.map { |c| Rails::Generators::GeneratedAttribute.new(c.name, c.type.to_s) }
|
|
50
|
+
else
|
|
51
|
+
[]
|
|
52
|
+
end
|
|
53
|
+
rescue
|
|
54
|
+
[]
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# 内部绑定使用的变量
|
|
60
|
+
def attrs
|
|
61
|
+
@attrs || []
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
|
|
2
|
+
# ZenAdmin 资源配置
|
|
3
|
+
zen_admin do |resource|
|
|
4
|
+
# 1. 基础信息配置
|
|
5
|
+
resource.label "<%= class_name %>", "<%= class_name.pluralize %>"
|
|
6
|
+
# group: 菜单分组名称,支持自动折叠
|
|
7
|
+
resource.menu label: "<%= class_name.pluralize.titleize %>", icon: "fas fa-circle", position: 10, visible: true, group: "应用管理"
|
|
8
|
+
|
|
9
|
+
# 2. 功能特性开关
|
|
10
|
+
resource.enable_batch_actions # 开启批量操作 (默认包含批量删除)
|
|
11
|
+
resource.exportable! # 开启 CSV 导出功能
|
|
12
|
+
# resource.soft_delete = true # 开启回收站 (默认开启,设为 false 则为物理删除)
|
|
13
|
+
|
|
14
|
+
# 3. 快捷页签 (Scopes)
|
|
15
|
+
# 静态页签示例 (支持 Ransack 查询语法: _eq, _cont, _gteq 等)
|
|
16
|
+
resource.scope :all, label: "全部", default: true
|
|
17
|
+
# resource.scope :recent, label: "最近一周", query: { created_at_gteq: 1.week.ago }
|
|
18
|
+
|
|
19
|
+
# 动态页签示例 (从数据库加载)
|
|
20
|
+
# resource.scope do
|
|
21
|
+
# Category.all.map { |cat| { name: "cat_#{cat.id}", label: cat.name, query: { category_id_eq: cat.id } } }
|
|
22
|
+
# end
|
|
23
|
+
|
|
24
|
+
# 4. 自定义批量操作 (Batch Actions)
|
|
25
|
+
# resource.batch_action :verify, label: "批量审核", type: :success, method: :bulk_verify_method
|
|
26
|
+
# resource.batch_action :archive, label: "批量归档", handler: ->(records) {
|
|
27
|
+
# records.update_all(archived: true)
|
|
28
|
+
# # 记录审计日志: zen_admin_audit(资源类, 动作名, note: 说明, changes: 数据)
|
|
29
|
+
# zen_admin_audit(<%= class_name %>, "archive", note: "批量归档操作", changes: { ids: records.ids })
|
|
30
|
+
# }
|
|
31
|
+
|
|
32
|
+
# 5. 单条记录操作 (Member Actions)
|
|
33
|
+
# resource.member_action :copy, label: "复制", icon: "fas fa-copy", type: :info, method: :copy_instance_method
|
|
34
|
+
# resource.member_action :highlight, label: "精选", handler: ->(record) { record.touch }
|
|
35
|
+
|
|
36
|
+
# 6. 列表页字段定义 (List View)
|
|
37
|
+
resource.list do
|
|
38
|
+
field :id, sortable: true
|
|
39
|
+
<% attrs.each do |a| -%>
|
|
40
|
+
field :<%= a.name %>, label: "<%= a.name.humanize %>"
|
|
41
|
+
<% end -%>
|
|
42
|
+
field :created_at, label: "创建时间", sortable: true
|
|
43
|
+
# 自定义渲染示例
|
|
44
|
+
# field :status, collection: [["草稿", "draft"], ["发布", "published"]] do |record, label|
|
|
45
|
+
# css = record.status == 'published' ? 'success' : 'secondary'
|
|
46
|
+
# content_tag :span, label, class: "badge bg-#{css}"
|
|
47
|
+
# end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# 7. 高级筛选面板 (Filters)
|
|
51
|
+
resource.filters do
|
|
52
|
+
<% attrs.select { |a| [:string, :text, :integer, :date, :datetime].include?(a.type.to_sym) }.each do |a| -%>
|
|
53
|
+
field :<%= a.name %>, label: "<%= a.name.humanize %>", type: :<%= a.type %>
|
|
54
|
+
<% end -%>
|
|
55
|
+
field :created_at, type: :date_range, label: "时间范围"
|
|
56
|
+
# 支持类型: :string, :select, :date_range
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# 8. 表单页字段定义 (Form View)
|
|
60
|
+
resource.form do
|
|
61
|
+
<% attrs.each do |a| -%>
|
|
62
|
+
field :<%= a.name %>, label: "<%= a.name.humanize %>", type: :<%= a.type == "datetime" ? "date" : a.type %>
|
|
63
|
+
<% end -%>
|
|
64
|
+
# 常用类型参考:
|
|
65
|
+
# :string, :text, :password, :number, :email, :date, :datetime
|
|
66
|
+
# :file (ActiveStorage), :rich_text (ActionText)
|
|
67
|
+
# :select (单选), :multi_select (多选), :radio (单选组)
|
|
68
|
+
# 示例:
|
|
69
|
+
# field :author_id, type: :select, collection: -> { Author.all.map{|a| [a.name, a.id]} }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# 9. 详情页字段定义 (Show View - 默认自动发现所有字段)
|
|
73
|
+
# resource.show do
|
|
74
|
+
# field :id
|
|
75
|
+
# field :created_at
|
|
76
|
+
# field :cover_image do |record|
|
|
77
|
+
# image_tag url_for(record.cover_image) if record.cover_image.attached?
|
|
78
|
+
# end
|
|
79
|
+
# end
|
|
80
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
require "rails/generators/active_record"
|
|
2
|
+
|
|
3
|
+
module ZenAdmin
|
|
4
|
+
module Generators
|
|
5
|
+
class RbacInstallGenerator < Rails::Generators::Base
|
|
6
|
+
include ActiveRecord::Generators::Migration
|
|
7
|
+
source_root File.expand_path("templates", __dir__)
|
|
8
|
+
|
|
9
|
+
desc "安装 ZenAdmin RBAC 数据库支持"
|
|
10
|
+
|
|
11
|
+
def invoke_base_install
|
|
12
|
+
invoke "zen_admin:install"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def copy_migrations
|
|
16
|
+
migration_template "create_zen_admin_rbac.rb.erb", "db/migrate/create_zen_admin_rbac.rb"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def enable_rbac_config
|
|
20
|
+
path = "config/initializers/zen_admin.rb"
|
|
21
|
+
if File.exist?(path)
|
|
22
|
+
# 如果已经存在,则更新为 true
|
|
23
|
+
if File.read(path).include?("config.rbac_enable")
|
|
24
|
+
gsub_file path, /config\.rbac_enable\s*=\s*.*/, "config.rbac_enable = true"
|
|
25
|
+
else
|
|
26
|
+
# 插入在 configure 块的下一行
|
|
27
|
+
inject_into_file path, after: "ZenAdmin.configure do |config|\n" do
|
|
28
|
+
<<-RUBY
|
|
29
|
+
# 开启 RBAC 数据库验证模式
|
|
30
|
+
config.rbac_enable = true
|
|
31
|
+
RUBY
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def add_seeds
|
|
38
|
+
append_to_file "db/seeds.rb" do
|
|
39
|
+
ERB.new(File.read(File.expand_path("templates/seeds.rb.erb", __dir__))).result
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def show_readme
|
|
44
|
+
say "\nZenAdmin RBAC 迁移文件已生成!", :green
|
|
45
|
+
say "接下来请运行: ", :yellow
|
|
46
|
+
say " bin/rails db:migrate", :white
|
|
47
|
+
say "然后创建管理员账号: ", :yellow
|
|
48
|
+
say " bin/rails g zen_admin:admin_user", :white
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
class CreateZenAdminRbac < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
|
|
2
|
+
def change
|
|
3
|
+
# 1. 用户表
|
|
4
|
+
create_table :zen_admin_users do |t|
|
|
5
|
+
t.string :username, null: false
|
|
6
|
+
t.string :password_digest, null: false
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
add_index :zen_admin_users, :username, unique: true
|
|
10
|
+
|
|
11
|
+
# 2. 角色表
|
|
12
|
+
create_table :zen_admin_roles do |t|
|
|
13
|
+
t.string :name, null: false
|
|
14
|
+
t.string :code, null: false
|
|
15
|
+
t.timestamps
|
|
16
|
+
end
|
|
17
|
+
add_index :zen_admin_roles, :name, unique: true
|
|
18
|
+
add_index :zen_admin_roles, :code, unique: true
|
|
19
|
+
|
|
20
|
+
# 3. 权限表
|
|
21
|
+
create_table :zen_admin_permissions do |t|
|
|
22
|
+
t.string :name, null: false
|
|
23
|
+
t.string :code, null: false
|
|
24
|
+
t.timestamps
|
|
25
|
+
end
|
|
26
|
+
add_index :zen_admin_permissions, :code, unique: true
|
|
27
|
+
|
|
28
|
+
# 4. 用户与角色的多对多中间表
|
|
29
|
+
create_table :zen_admin_users_roles, id: false do |t|
|
|
30
|
+
t.belongs_to :user, null: false, foreign_key: { to_table: :zen_admin_users }
|
|
31
|
+
t.belongs_to :role, null: false, foreign_key: { to_table: :zen_admin_roles }
|
|
32
|
+
end
|
|
33
|
+
add_index :zen_admin_users_roles, [:user_id, :role_id], unique: true
|
|
34
|
+
|
|
35
|
+
# 5. 角色与权限的多对多中间表
|
|
36
|
+
create_table :zen_admin_roles_permissions, id: false do |t|
|
|
37
|
+
t.belongs_to :role, null: false, foreign_key: { to_table: :zen_admin_roles }
|
|
38
|
+
t.belongs_to :permission, null: false, foreign_key: { to_table: :zen_admin_permissions }
|
|
39
|
+
end
|
|
40
|
+
add_index :zen_admin_roles_permissions, [:role_id, :permission_id], unique: true
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# ZenAdmin RBAC 初始数据
|
|
2
|
+
puts "正在初始化 ZenAdmin RBAC 数据..."
|
|
3
|
+
|
|
4
|
+
# 1. 创建基础权限 (可选,这里可以先空着让用户通过后台加)
|
|
5
|
+
|
|
6
|
+
# 2. 创建超级管理员角色
|
|
7
|
+
admin_role = ZenAdmin::Role.find_or_create_by!(code: "superadmin") do |r|
|
|
8
|
+
r.name = "超级管理员"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# 3. 如果有需要,可以给角色分配所有权限 (这里逻辑可以在后台手动操作,或写一个通配符逻辑)
|
|
12
|
+
|
|
13
|
+
puts "ZenAdmin RBAC 数据初始化完成!"
|