一份dcatadmin不全使用说明

前言

最近在家失业无聊,接了个单,其中有个项目就是关于后台管理系统的,发了几个模板样例给对方,有之前用过的后台框架fastadminlaraveladmin,但是对方看了都觉得怎么跟他之前的后台一个样,要求换一个简洁一点的,于是我在网上搜了一下laravel还有哪些后台框架,于是看到了dcatadmin,把demo网站发给对方看过之后,觉得这个页面还好,比较简单,随交流沟通后,确定用这个后台。

话说dcatadmin

dcatadmin这个框架我还是第一次用到,之前用laravel的后台框架都是用的laravel-admin,相对来说比较熟悉一点,dcatadmin还是第一次听到(因为上份工作都是围绕yii框架来展开的,关于laravel的消息确实查看的不多)。

dcatadmin2.x文档- 简介 | 入门 |《Dcat Admin 中文文档 2.x》| Laravel China 社区

看完整体下来,dcatadmin重点需要了解的其实就是3个部分,也是后台系统中的组成:数据表格、表单、详情

但是还是有些地方没怎么看懂,尤其是关于工具表单异步加载这块儿。时间有限,索性就在开发的过程中,再去搞清楚吧

因为现有文档描述不清,在开发过程中自己也是踩了不少坑,以下内容总结了在dcatadmin2.x中常用的几个功能,如有遇到类似疑问的开发者,可以少走一些弯路

数据表单

工具表单

工具表单的文档: 工具表单 | 数据表单 |《Dcat Admin 中文文档 2.x》| Laravel China 社区

要实现的业务场景

举个例子:

比如说你想在数据列表中,对一行数据增加某个操作按钮,实现”充值“功能

image-20251120105301499.png

在非dcatadmin2.x框架中一般实现思路是,实现按钮的点击功能,当点击“充值”按钮时,打开一个弹窗,弹窗里是个表单,输入你要充值的金额,然后点提交

image-20251120105432750.png

这个功能在dcatadmin中就可以通过“工具表单”来实现,具体实现方法如下:

首先可以把点击充值这个看成一个动作,在项目中的App\Admin\Actions空间下实现一个充值动作类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

class RechargeUserBalance extends RowAction

{

    /**

     * @return string

     */

    protected $title = '';





    public function render()

    {

        // 实例化表单类并传递自定义参数

        $form = RechargeUserBalanceForm::make()->payload(['id' => $this->getKey()]);



        return Modal::make()

            ->lg()

            ->title(admin_trans('admin-user.fields.recharge'))

            ->body($form)

            ->button("<i class='feather'>".admin_trans('admin-user.fields.recharge')."</i>");

    }



}

这个类中其实只需要一个方法就可以了,重点看下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10

Modal::make()

            ->lg()

            ->title(admin_trans('admin-user.fields.recharge'))

            ->body($form)

            ->button("<i class='feather'>".admin_trans('admin-user.fields.recharge')."</i>");

Modal::make() 表示实例一个弹窗

  • lg 弹窗大小,可选参数有sm、xl

  • title 弹窗标题,左上角

  • body 弹窗内容,这里我是实例了一个表单类,下文再讲

  • button 页面显示的按钮

接下来我们看表单内容的具体实现

表单实现

在命名空间App\Admin\Forms定义一个表单类,我是直接把我的代码贴过来了,可以参考下

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

class RechargeUserBalanceForm extends Form implements LazyRenderable

{



    use LazyWidget; // 使用异步加载功能



    /**

     * @param array $input

     * @return \Dcat\Admin\Http\JsonResponse

     * @throws \Throwable

     * @author mingzhongshui

     * @date 2025/6/17

     */

    public function handle(array $input)

    {

        $userId = $this->payload['id'] ?? null;

        if (!$userId) {

            throw new \Exception(admin_trans_field('parameter_error'));

        }



        $balance = $input['balance'];

        if (!is_numeric($balance)) {

            throw new \Exception(admin_trans('yk-user.fields.recharge_balance_format_fail'));

        }



        if (floatval($balance) <= 0) {

            throw new \Exception(admin_trans('yk-user.fields.recharge_balance_must_greater_than_0'));

        }



        if (!preg_match('/^\d+(\.\d{1,3})?$/', $balance)) {

            throw new \Exception(admin_trans('yk-user.fields.recharge_balance_point_fail'));

        }



        $user = AdminUser::find($userId);

        if (!$user) {

            throw new \Exception(admin_trans('yk-user.fields.recharge_user_not_exists'));

        }

        DB::beginTransaction();

        DB::connection('mysql_user_backend')->beginTransaction();

        try {



            // 更新原表balance

            $oldBalance = $user['balance'];

            $newBalance = bcadd($oldBalance, $balance, 3);

            $user->balance = $newBalance;

            $user->save();



            // 增加充值日志

            AdminUserBalanceDetailService::createRecord($userId, AdminUserBalanceDetail::TYPE_RECHARGE, AdminUserBalanceDetail::CHANGE_TYPE_ADD, $oldBalance, $balance, $newBalance);



            DB::commit();

            DB::connection('mysql_user_backend')->commit();

        } catch (\Throwable $e) {

            DB::rollBack();

            DB::connection('mysql_user_backend')->rollBack();

            throw new \Exception($e->getMessage());

        }



        return $this->response()->success(admin_trans_field('handle_success'))->refresh();

    }



    /**

     * Build a form here.

     */

    public function form()

    {

        $this->decimal('balance', admin_trans_field('recharge_balance'))->required();

    }



    /**

     * The data of the form.

     *

     * @return array

     */

    public function default()

    {

        return [];

    }

}

handle 就是表单提交之后的操作

form 表单显示内容

定义好动作类和表单类之后,接下来就是在数据列表中引入了

控制器引入动作类

首先在控制器中的列表方法(grid)中增加一个action

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

protected function grid()

{

    return Grid::make(new AdminUser(), function (Grid $grid) {

        $grid->model()->orderBy('id', 'desc');

        $grid->column('id', admin_trans_field('data_number'))->sortable();

        $grid->column('username');

        $grid->column('balance');

        $grid->column('username');

        $grid->column('status')->radio(YkUserService::getStatusOption());

        $grid->column('created_at')->sortable();



        $grid->filter(function (Grid\Filter $filter) {

            $filter->equal('id');

        });



        $grid->setActionClass(Grid\Displayers\DropdownActions ::class);

        $grid->actions([new RechargeUserBalance()]);

    });

}

$grid->actions([new RechargeUserBalance()]);就是引入咱们上文定义好的动作类。

当然你在增加的时候可以通过某些条件来判断是否要显示,比如

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20

$grid->actions(function ($actions) {

    // 获取当前行数据

    $row = $actions->row;

    // 判断条件

    if ($row->status == \App\Models\YkCollectBloggerTask::STATUS_COMPLETED && $row->collect_data_quantity) {

        // 追加动作(操作按钮)

        $actions->append(new BloggerDataFilterAction());

        $actions->append(new BloggerDataExportAction());

    }

});

又当然,比如你不想使用默认的查看、编辑、删除按钮,要自定义这些,又或者想要按照自己的要求,展示或不展示某些按钮,你还可以这样实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

public function setTextActions(Grid $grid, array $showActions = ['view' => true, 'edit' => true, 'delete' => true]) 

{

    // 不显示原生查看、编辑、删除按钮

    $grid->disableViewButton();

    $grid->disableEditButton();

    $grid->disableDeleteButton();

		

	// 按需实现要展示那些操作按钮

    $grid->actions(function (Grid\Displayers\Actions $actions) use ($grid, $showActions) {

        $key = $actions->getKey();

        $resource = $grid->resource(); // 获取资源路径

        $viewUrl = admin_url("$resource/$key");

        $editUrl = admin_url("$resource/$key/edit");



        $parts = [];



        if (!empty($showActions['view'])) {

            $parts[] = '<a href="'.$viewUrl.'"><i class="feather">查看</i></a>';

        }



        if (!empty($showActions['edit'])) {

            $parts[] = '<a href="'.$editUrl.'"><i class="feather">编辑</i></a>';

        }



        if (!empty($showActions['delete'])) {

            $parts[] = '<a data-url="'.$viewUrl.'" data-message="ID - '.$key.'" data-action="delete" data-redirect="'.$resource.'" class="feather grid-row-delete" >删除</a>';

        }



        $actions->append(implode('&nbsp;|&nbsp;', $parts));

    });

}

这种的使用场景在一些日志类的列表展示中

image-20251120111956141.png

工具表单不仅可以用在动作上,还可以用在工具上,比如说你想在列表上面工具栏,增加“导入”按钮

image-20251120112636090.png

那么你也可以用action来实现

1
2
3
4
5
6

$grid->tools(function (Grid\Tools $tools) {

    $tools->append(new ImportStaticIp());

});

不过ImportStaticIp的父类要发生变化了,从动作的RowAction变为AbstractTool,因为你要实现的是工具类,而非动作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

/**

 * Class ImportAccount

 *

 * @package App\Admin\Actions\YkAccount

 * @author mingzhongshui

 * @date 2025/6/14

 */

class ImportStaticIp extends AbstractTool

{

    /**

     * @return string

     */

    protected $title = '';



    /**

     * @param $title

     */

    public function __construct($title = null)

    {

        $this->title = admin_trans('yk-account.fields.import_account');

    }



    public function render()

    {

        // 实例化表单类并传递自定义参数

        $form = ImportStaticIpForms::make();



        return Modal::make()

            ->lg()

            ->title($this->title)

            ->body($form)

            ->button('<a href="javascript:void(0)" class="btn btn-primary btn-outline">

    <i class="feather icon-plus-circle"></i>&nbsp;&nbsp;导入

</a>');

    }

}

title也可以在__construct中实现。

数据表格

数据行日志展示

比如说有这样一个场景,发送任务列表,要显示发送成功的数量以及日志,那应该怎么处理呢?一般情况来说,就是无非在操作页增加一个日志按钮,点击按钮的时候跳转到一个新页面展示发送日志列表,但是在dcatadmin中有着不同的实现方式

可以在数据行,发送数量字段上,给一个弹框,当点击数量的时候,打开一个弹窗,显示发送日志列表。

实现方式也很简单,只要在grid方法,字段末尾追加一个弹框方法就可以

1
2
3
4
5
6

$grid->column('actual_send_quantity', admin_trans_field('send_success_quantity'))->display(function () {

    return YkMassMessageLog::where('mass_id', $this->id)->where('status', YkMassMessageLog::STATUS_SUCCESS)->count();

})->modal(admin_trans('yk-mass-message-setting.fields.send_success_details'), MassMessageSuccessLogTable::make());

modal方法有两个参数

image-20251120132340610.png

参数1:打开弹窗的标题

参数2:回调函数,也就是要显示的内容

MassMessageSuccessLogTable类实现方式如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

// LazyRenderable异步加载类

class MassMessageSuccessLogTable extends Grid\LazyRenderable

{



    public function grid(): Grid

    {

        return Grid::make(new YkMassMessageLog(), function (Grid $grid) {

            // $this->key 就是当前行的id

            $grid->model()->where('mass_id', $this->key)->where('status', \App\Models\YkMassMessageLog::STATUS_SUCCESS);

            $grid->column('id');

            $grid->column('content_type', admin_trans('yk-mass-message-log.fields.content_type'))->display(function ($v) {

                return self::getTypeOptions()[$v] ?? '';

            });

			

			// 分页数量

            $grid->paginate(10);

            // 不显示操作列

            $grid->disableActions();

            // 不显示多选

            $grid->disableRowSelector();

        });

    }

这样就可以实现,在列表中任意字段实现弹窗列表功能了;

总结

以上就是我认为dcatadmin2.x中,比较常用但是文档描述不清楚的功能点了

当然可能还有一些比较个性化的功能,由于我没有使用到,就没有举例了


作者:命中水

版权声明:转载请注明出处,欢迎技术交流