dcat-admin 开源框架在 erp 项目中的应用
项目背景
某知名连锁餐饮企业的供应链系统开发,并且在一个月内上线一个初步版本,
包括:
- 基本信息管理,
- 和所有的门店能实现报单,
- 后台审核汇总导出打印功能。
- 后续在实现生成加工,按批次号基本进销存(严格按照批次号出入库存),物料生成加工,员工上班排班,车辆管理等常用erp功能。
技术选型
为什么选择dcat-admin?
(我以EasyUI,laravel-admin,dcat-admin作为比较)
- EasyUI:
在选择dcat-admin之前我考虑过EasyUI作为后台界面框架,因为EasyUI是基本可以满足大部分erp的业务需求点。但是缺点是我要造轮子。基本的增删改查界面渲染都要重新来过,时间成本比较高,而且界面太传统,操作体验一般。 - laravel-admin
laravel-admin是非常优秀的开源框架,基础功能做的十分完善,常用的拓展都有开发者在贡献(包括我自己),节省了后端开发的时间成本,满足大部分后台的需求,但是在erp这一块我当时觉得界面和操作体验还是和我需求的有一定的差异。 - dcat-admin:
我是在一个月之前在社区了解到这个开源项目的,当时我还在为到底是用laravel-admin还是EasyUI去纠结,我体验了dcat-admin后,我最终选择了这一款开源框架作为项目的开发。他的基本使用和laravel-admin差不多,但是在laravel-admin的基础上做了更多的体验和界面优化,而且其中的报表功能和弹出框形式的新增和编辑完美的契合了我的需求点,虽然当时dcat-admin还是内测阶段,但是我还是选择使用这套框架进行项目开发。
使用心得
基本的使用方法大家可以参考文档,我这里主要列举的是在erp项目中一些特定的场景比较特殊的使用方法
双击唤起快捷编辑功能
需求:用户在在后台需求双击数据表格的行直接唤起编辑
代码实现:在项目的app/Admin目录的bootstrap.php添加如下进行全局修改$script = <<<JS $("#grid-table > tbody > tr").on("dblclick",function() { var obj = $(this).find(".feather.icon-edit"); if (obj.length == 1) { obj.trigger("click") } }) JS; Admin::script($script);
对dcat-admin的view文件进行修改
需求:我需要对dcat-admin的布局文件进行自定义
实现:将vendor下dcat包的views文件夹拷贝到项目目录的resources/views/vendor/laravel-admin,并在app/Admin目录的bootstrap.php添加如下进行全局修改app('view')->prependNamespace('admin', resource_path('views/vendor/laravel-admin'));
用dcat-admin实现报表
需求:erp常用的采购入库单,门店报货单在dcat-admin的实现
代码:
public function show($id, Content $content) { $orderinfo = StoreOrderModel::find($id); return $content->header("门店要货单") // 表头内容映入自定义模版 ->body(view("receipt.order", compact("orderinfo"))) // 表单明细使用dcat-admin自带的IFrameGrid ->body(IFrameGrid::make(new StoreOrderItem(["mdepts", "material"]), function(Grid $grid) use ($orderinfo) { $grid->combine('订单数量', ['should_number', 'actual_number']); $grid->responsive(); $grid->rowSelector() ->style('success') ->click(); // 更改表格外层容器 $grid->wrap(function(Renderable $view) { return $view; }); $grid->model()->where("order_item_no", $orderinfo['number'])->where("mdept_id", Admin::user()->mdept_id); $grid->column("order_item_no", "订单号")->responsive(); $grid->column("material.name", "物料名称")->responsive(); $grid->column("mdepts.name", "部门机构")->responsive(); $grid->column("should_number", "报货数量")->responsive(); $grid->column("actual_number", "实送数量")->responsive(); $grid->column("price", "报单价格")->responsive(); $grid->column("status", "发货状态")->using(StoreOrderItemModel::STATUS)->responsive(); $grid->disablePagination(); $grid->disableActions(); $grid->disableCreateButton(); $grid->disableRowSelector(); $orderinfo->status === StoreOrderModel::STATUS_WAIT_REVIEW && $grid->tools($this->review($orderinfo->id, "store_order")); $orderinfo->status === StoreOrderModel::STATUS_REVIEW && $grid->tools($this->reReview($orderinfo->id, "store_order")); $orderinfo->status === StoreOrderModel::STATUS_WAIT_REVIEW && $grid->tools(new SaveOrder()); $grid->tools((new OrderPrint())->setKey($orderinfo->id)); //$grid->disableRefreshButton(); Admin::collectAssets('select2'); Admin::script(" $('.order_detail_select2').select2();"); })); }
订单的审核
需求:增加一个按钮对单据进行审核
实现:php artisan admin:action 根据提示选择1 grid-batch生成App\Admin\Actions\Grid\Review动作
App\Admin\Actions\Grid\Review代码如下
<?php namespace App\Admin\Actions\Grid; use App\Models\StoreOrder; use Dcat\Admin\Actions\Response; use Dcat\Admin\Grid\BatchAction; use Dcat\Admin\Traits\HasPermissions; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; class Review extends BatchAction { protected $style = 'btn btn-sm btn-default'; protected $title = '单据审核'; /** * Handle the action request. * * @param Request $request * */ public function handle(Request $request) { $keys = $this->getKey(); // 单据审核 foreach (StoreOrder::find($keys) as $order) { $order->status = StoreOrder::STATUS_REVIEW; $order->save(); } return $this->response()->success("单据审核成功!")->refresh(); } public function confirm() { // return ['Confirm?', 'contents']; return ['确定审核单据?']; } /** * @param Model|Authenticatable|HasPermissions|null $user * * @return bool */ protected function authorize($user): bool { return true; } /** * @return array */ protected function parameters() { return []; } public function actionScript(){ $warning = "请选择审核的单据!"; return <<<JS function (data, target, action) { var key = {$this->getSelectedKeysScript()} if (key.length === 0) { Dcat.warning('{$warning}'); return false; } // 设置主键为复选框选中的行ID数组 action.options.key = key; } JS; } protected function html() { return <<<HTML <a {$this->formatHtmlAttributes()}><button class="btn btn-primary btn-mini">{$this->title()}</button></a> HTML; } }
在grid引入审核按钮
$grid->tools(new Review());
单据的批量打印
需求:
思路:通过dcat的动作类获取要打印的订单id的数组,在将打印的数据循环渲染到html页面,在用jqprint调用打印
代码实现:php artisan admin:action 创建一个grid-batch动作
代码如下:
<?php namespace App\Admin\Actions\Grid; use App\Models\StoreOrder; use Dcat\Admin\Actions\Response; use Dcat\Admin\Grid\BatchAction; use Dcat\Admin\Traits\HasPermissions; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; class PrintStoreOrders extends BatchAction { /** * @return string */ protected $title = '单据打印'; /** * Handle the action request. * * @param Request $request * * @return Response */ public function handle(Request $request) { $keys = $this->getKey(); $url = route("print_store_order",["ids"=>implode("-", $keys)]); if (count($keys) > 0) { return $this->response()->script("window.open('{$url}')"); } } /** * @return string|array|void */ public function confirm() { // return ['Confirm?', 'contents']; } /** * @param Model|Authenticatable|HasPermissions|null $user * * @return bool */ protected function authorize($user): bool { return true; } /** * @return array */ protected function parameters() { return []; } public function actionScript(){ $warning = "请选择打印的单据!"; return <<<JS function (data, target, action) { var key = {$this->getSelectedKeysScript()} if (key.length === 0) { Dcat.warning('{$warning}'); return false; } // 设置主键为复选框选中的行ID数组 action.options.key = key; } JS; } protected function html() { return <<<HTML <a {$this->formatHtmlAttributes()}><button class="btn btn-primary btn-mini">{$this->title()}</button></a> HTML; } }
打印控制器代码
/** * @return Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function printStoreOrder(Request $request) { $ids = $request->input("ids"); $ids = explode("-", $ids); if (count($ids)>0) { $data = StoreOrderModel::find($ids); return view("receipt.store_order", compact("data")); } }
打印的页面实现
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>门店要货单明细</title> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="{{asset("wxy/css/bootstarp.css")}}" crossorigin="anonymous"> </head> <body> <div class="print"> <style> .table-bordered > tbody > tr > td, .table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > tfoot > tr > td { text-align: left; } .card { position: relative; } .card .postImg{ width: 160px; height: 100px; position: absolute; right: 115px; top: 80px; z-index: 999; } .card .postImg img{ width: 100%; } </style> @foreach($data as $order) <div class="card "> <table class="table table-bordered"> <i class="postImg"><img src="{{store_order_img($order->status)}}"/></i> <caption class="text-center"><h3>门店报货单</h3></caption> <thead> <tr> <th colspan="3">单号:{{$order->number}}</th> <th colspan="4">门店:{{$order->store->name}}</th> </tr> <tr> <th colspan="3">部门机构:{{$order->mdepts->name}}</th> <th colspan="4">业务日期:{{$order->created_at}}</th> </tr> <tr> <th colspan="3">报单人:{{$order->store_user->name}}({{$order->store_user->phone}})</th> <th colspan="4">整单备注:{{$order->other}}</th> </tr> <tr> <th>订单号</th> <th>物料名称</th> <th>部门机构</th> <th>报货数量</th> <th>实送数量</th> <th>报单价格</th> <th>发货状态</th> </tr> </thead> <tbody> @foreach($order->details as $detail) <tr> <th>{{$detail->order_item_no}}</th> <th>{{$detail->material->name}}</th> <th>{{$detail->mdepts->name ?? ''}}</th> <th>{{$detail->should_number ?? ''}}</th> <th>{{$detail->actual_number}}</th> <th>{{$detail->price}}</th> <th>{{$detail->status_str}}</th> </tr> @endforeach </tbody> </table> </div> @endforeach </div> </body> <script language="javascript" src="{{asset('wxy/js/jquery-1.4.4.min.js')}}"></script> <script language="javascript" src="{{asset('wxy/js/jquery.jqprint-0.3.js')}}"></script> <script> $(".print").jqprint(); </script> </html>
双击弹出一个ifarm
需求:
实现:php artisan admin:action 创建一个grid-row动作
RowAction代码如下
<?php namespace App\Admin\Actions\Grid; use Dcat\Admin\Actions\Response; use Dcat\Admin\Admin; use Dcat\Admin\Form; use Dcat\Admin\Grid\RowAction; use Dcat\Admin\Traits\HasPermissions; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; class ShowStoreDetail extends RowAction { /** * [[@return](https://learnku.com/users/31554)](https://learnku.com/users/31554) string */ protected $title = 'Title'; /** * Handle the action request. * * @param Request $request * * @return Response */ public function handle(Request $request) { } /** * @return string|array|void */ public function confirm() { // return ['Confirm?', 'contents']; } /** * @param Model|Authenticatable|HasPermissions|null $user * * @return bool */ protected function authorize($user): bool { return true; } /** * @return array */ protected function parameters() { return []; } public function render() { $id = $this->getRow()->get("material_id"); parent::render(); return <<<HTML <span class="grid-expand"> <a href="javascript:void(0)" class="store-order" id="form-store-order-J59Kp-select-resourc{$id}">报单门店</a> </span> HTML; } public function script() { $id = $this->getRow()->get("material_id"); $url = route("order_store", $id); Admin::collectAssets('@resource-selector'); return <<<JS Dcat.ResourceSelector({ title: '<h2 style="margin-top:10px">报单门店</h2>', source: "{$url}", selector: '#form-store-order-J59Kp-select-resourc'+{$id}, maxItem: 3, area: ["70%","60%"], queryName: '_resource_', items: {}, showCloseButton: false, }) JS; } }
控制器引入
// grid代码 $grid->actions(new ShowStoreDetail()); $this->order_store_script(".store-order");
双击事件
public function order_store_script(string $str) { $script = <<<JS $("#grid-table > tbody > tr").on("dblclick",function() { var obj = $(this).find("{$str}"); if (obj.length == 1) { obj.trigger("click") } }) JS; Admin::script($script); }
dcat-admin 连表取数据
public function orderSummary(Content $content) { return $content->header("要货汇总")->body(Grid::make(new StoreOrderItem(['material', "material.units"]), function(Grid $grid) { $grid->combine("汇总数据", ["sum_num", "store_num"]); $grid->model() ->select([ 'material_id', DB::raw("count(distinct(store_id)) as store_num"), DB::raw("sum(should_number) as sum_num") ]) ->leftJoin("store_order", "store_order_item.order_item_no", "=", "store_order.number") ->where("store_order.status", StoreOrderModel::STATUS_REVIEW) ->where("store_order_item.mdept_id",Admin::user()->mdept_id ?? '') ->groupBy("material_id"); $grid->column("material_id", "物料id"); $grid->column("material.name", "物料名称"); $grid->column("material.units", "单位")->display(function($val) { return $val['name'] ?? ''; }); $grid->column("sum_num", "汇总数量"); $grid->column("store_num", "要货门店数"); $grid->actions(new ShowStoreDetail()); $grid->disableRowSelector(); $grid->disableCreateButton(); $grid->disableDeleteButton(); $grid->disableViewButton(); $grid->export()->rows(function(array $rows) { $tmp = []; foreach ($rows as $index => $row) { $tmp[] = [ '序号' => $index, '物料名称' => $row['material']['name'] ?? '', '物料单位' => $row['material']['units']['name'] ?? '', '物料规格' => $row['material']['specs'] ?? '', '要货门店数' => $row['store_num'] ?? '', '报货数量' => $row['sum_num'] ?? '', ]; } return $tmp; }); $this->order_store_script(".store-order"); // $grid->disableEditButton(); })); }
总结:这些是我在dcat-admin自己根据文档实现的一些需求,在使用过程中我们要使用好dcat-admin的动作,他能帮我们实现大部分想要的操作, 后续在使用过程中有新的心得体会我会继续补充。他的一般操作大家可以多多阅读文档,最后真心感觉社区能涌现这样优质的开源项目,提升了我们的工作效率,我们只需要思考业务本身的逻辑。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: