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项目中一些特定的场景比较特殊的使用方法

  • 双击唤起快捷编辑功能

    需求:用户在在后台需求双击数据表格的行直接唤起编辑
    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的实现
    dcat-admin开源框架在erp项目中的应用

    代码:

    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();");
              }));
      }
  • 订单的审核

    需求:增加一个按钮对单据进行审核
    dcat-admin 开源框架在 erp 项目中的应用
    实现:

    • 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-admin开源框架在erp项目中的应用
    dcat-admin开源框架在erp项目中的应用
    思路:通过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

    需求:
    dcat-admin开源框架在erp项目中的应用
    实现:

  • 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 协议》,转载必须注明作者和本文链接
本帖由系统于 2个月前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 21

赞 最快发布的应用了吧 应该是

2个月前 评论

做得很不错,提点小意见,ShowStoreDetail的几个空方法是可以删掉的:handleconfirmparametersauthorize

2个月前 评论
youxx (楼主) 2个月前
jennings 2个月前
ly560020

厉害了

2个月前 评论

太厉害了!

2个月前 评论

composer require dcat-admin-ext/helpers 提示没有这个包

2个月前 评论
Cooper

加油

2个月前 评论

厉害了

2个月前 评论

这个后台挺好看的 :+1:

2个月前 评论

很好的经验分享 :clap:

2个月前 评论

朋友可以关注一下这个项目:github.com/quarkcms/quark-admin

2个月前 评论
小李世界 2个月前

您好 这个分享用dcat开发erp的经验太优秀了!

想请教下 不知道您的erp系统有没有盘点的功能 是不是能分享一下开发的思路 谢谢

1个月前 评论
youxx (楼主) 1个月前
kevinreg (作者) 1个月前
youxx (楼主) 1个月前
kevinreg (作者) 1个月前
youxx (楼主) 1个月前
kevinreg (作者) 1个月前
cydia 1个月前
kevinreg (作者) 1个月前

我喜欢那个盖章的已审核

1个月前 评论

很不错,后台界面很唯美

4周前 评论

很棒的经验分享,

1周前 评论

最近用easyui for vue做了一版进销存,希望有空交流一下 file

1周前 评论

easyui界面还挺好看的,谁有空给LightCMS整合下easyui~

6天前 评论

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!