Ver Fonte

Merge branch 'refs/heads/master' into stable

moshaorui há 1 mês atrás
pai
commit
7acf481cef
61 ficheiros alterados com 2318 adições e 142 exclusões
  1. 4 0
      .env.dev
  2. 34 0
      app/Admin/Actions/Grid/ProductAudit.php
  3. 67 0
      app/Admin/Actions/Grid/RpcAlbumImport.php
  4. 0 4
      app/Admin/Actions/Tools/InquiryHandle.php
  5. 46 0
      app/Admin/Controllers/ApiController.php
  6. 3 2
      app/Admin/Controllers/BaseProductController.php
  7. 11 3
      app/Admin/Controllers/DistAdminDistributorController.php
  8. 1 0
      app/Admin/Controllers/DistAppearanceTemplateController.php
  9. 171 0
      app/Admin/Controllers/DistProductAuditController.php
  10. 239 0
      app/Admin/Controllers/ImportProductController.php
  11. 12 6
      app/Admin/Forms/AceLeft.php
  12. 123 0
      app/Admin/Forms/ImportAlbum.php
  13. 9 4
      app/Admin/Forms/InquiryAssignment.php
  14. 52 0
      app/Admin/Forms/ProductAudit.php
  15. 15 0
      app/Admin/Repositories/DistAdminDistributor.php
  16. 7 0
      app/Admin/Repositories/DistInquiry.php
  17. 73 0
      app/Admin/Repositories/DistProduct.php
  18. 129 0
      app/Admin/Repositories/DistProductCategory.php
  19. 55 0
      app/Admin/Repositories/DistProductImage.php
  20. 94 0
      app/Admin/Repositories/RpcAlbum.php
  21. 49 0
      app/Admin/Repositories/RpcAlbumFolder.php
  22. 6 0
      app/Admin/bootstrap.php
  23. 11 0
      app/Admin/routes.php
  24. 1 1
      app/Distributor/Controllers/ApiController.php
  25. 2 2
      app/Distributor/Controllers/DistInquiryController.php
  26. 80 13
      app/Distributor/Controllers/DistProductController.php
  27. 226 0
      app/Distributor/Controllers/SiteAlbumController.php
  28. 8 2
      app/Distributor/Controllers/SiteBannerController.php
  29. 8 0
      app/Distributor/Controllers/SiteMenuController.php
  30. 2 2
      app/Distributor/Forms/ImportProduct.php
  31. 1 1
      app/Distributor/Repositories/DistProduct.php
  32. 102 0
      app/Distributor/Repositories/RpcAlbum.php
  33. 58 0
      app/Distributor/Repositories/RpcAlbumFolder.php
  34. 10 0
      app/Distributor/Repositories/SiteBanner.php
  35. 4 0
      app/Distributor/routes.php
  36. 96 11
      app/Libraries/CommonHelper.php
  37. 43 0
      app/Libraries/RpcClient.php
  38. 17 0
      app/Models/BaseProduct.php
  39. 2 0
      app/Models/BaseProductImage.php
  40. 4 0
      app/Models/DistProduct.php
  41. 12 0
      app/Models/NullModel.php
  42. 1 1
      app/helpers.php
  43. 1 0
      composer.json
  44. 52 1
      composer.lock
  45. 6 0
      config/dictionary.php
  46. 31 0
      database/migrations/2025_01_10_085440_update_dist_admin_distributor_table.php
  47. 32 0
      database/migrations/2025_02_08_030746_update_table_v001_1.php
  48. 31 0
      database/migrations/2025_03_05_023833_update_site_menu.php
  49. 1 0
      lang/en/admin.php
  50. 19 0
      lang/en/global.php
  51. 3 1
      lang/zh_CN/admin.php
  52. 21 0
      lang/zh_CN/global.php
  53. 2 0
      lang/zh_CN/menu.php
  54. 31 1
      public/vendor/grapes/grapes.init.js
  55. 1 1
      resources/views/admin/form/footer.blade.php
  56. 11 10
      resources/views/admin/pages-custom/ace.blade.php
  57. 51 37
      resources/views/admin/pages/login.blade.php
  58. 50 0
      resources/views/admin/partials_custom/menu.blade.php
  59. 1 1
      resources/views/distributor/form/footer.blade.php
  60. 35 0
      resources/views/distributor/form_custom/review-footer.blade.php
  61. 51 38
      resources/views/distributor/pages/login.blade.php

+ 4 - 0
.env.dev

@@ -86,3 +86,7 @@ MAIL_TO_ADDRESS=""
 MAIL_CC_NAME=""
 MAIL_CC_ADDRESS=""
 
+#相册系统RPC调用配置
+ALBUM_RPC_URL=https://album-prime-control.mietubl.com.cn/rpc
+ALBUM_RPC_SECRET=MtbSecretVBUC
+

+ 34 - 0
app/Admin/Actions/Grid/ProductAudit.php

@@ -0,0 +1,34 @@
+<?php
+namespace App\Admin\Actions\Grid;
+
+
+use App\Admin\Forms\ProductAudit as ProductAuditForm;
+use Dcat\Admin\Grid\BatchAction;
+use Dcat\Admin\Grid\RowAction;
+use Dcat\Admin\Widgets\Modal;
+
+class ProductAudit extends RowAction
+{
+
+
+    public function title()
+    {
+        return  admin_trans_label('product_audit');
+    }
+
+
+    public function render()
+    {
+        $form = ProductAuditForm::make()->payload(['id' => $this->getKey(),'review_reply'=>$this->row->review_reply]);
+        // 实例化表单类
+        return Modal::make()
+            ->lg()
+            ->title($this->title)
+            ->body($form)
+            ->button($this->title());
+    }
+
+
+
+
+}

+ 67 - 0
app/Admin/Actions/Grid/RpcAlbumImport.php

@@ -0,0 +1,67 @@
+<?php
+namespace App\Admin\Actions\Grid;
+
+
+use App\Admin\Forms\ImportAlbum;
+use App\Distributor\Forms\ImportProduct;
+use Dcat\Admin\Grid\BatchAction;
+use Dcat\Admin\Widgets\Modal;
+
+class RpcAlbumImport extends BatchAction
+{
+
+    protected $title = 'import';
+
+
+
+    public function render()
+    {
+        // 实例化表单类
+        $form = ImportAlbum::make();
+
+        return Modal::make()
+            ->lg()
+            ->title(admin_trans_label($this->title))
+            ->body($form)
+            // 因为此处使用了表单异步加载功能,所以一定要用 onLoad 方法
+            // 如果是非异步方式加载表单,则需要改成 onShow 方法
+            ->onShow($this->getModalScript())
+            ->button($this->getButtonHTML());
+
+    }
+
+    protected function getModalScript()
+    {
+        // 弹窗显示后往隐藏的id表单中写入批量选中的行ID
+        return <<<JS
+        // 获取选中的ID数组
+        var key = {$this->getSelectedKeysScript()}
+
+        $('#album_ids').val(key);
+        JS;
+    }
+
+
+    /**
+     * 获取按钮的 HTML
+     * @return string
+     */
+
+    public function title()
+    {
+        return '<i class="feather icon-shopping-cart"></i> &nbsp;'.admin_trans('admin.import');
+    }
+
+    protected function getButtonHTML()
+    {
+        $title='<i class="feather icon-shopping-cart"></i> &nbsp;'.admin_trans('admin.import');
+
+
+        return <<<HTML
+        <button class="btn btn-success">
+            <i class="feather"></i> {$title}
+        </button>
+        HTML;
+    }
+
+}

+ 0 - 4
app/Admin/Actions/Tools/InquiryHandle.php

@@ -48,15 +48,11 @@ class InquiryHandle extends AbstractTool
         $keys = $request->input('_key');
         if (is_array($keys) && count($keys) > 0) {
             $result = DistInquiry::distSetStatusProcessed($keys);
-            if ($result === false) {
-                return $this->response()->error('Failed to process!')->refresh();
-            }
             return $this->response()->success(admin_trans_label('update_success'))->refresh();
         } else {
             return $this->response()->error('No data selected!')->refresh();
         }
         // 你的代码逻辑
-
     }
 
 

+ 46 - 0
app/Admin/Controllers/ApiController.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Admin\Repositories\DistAdminDistributor;
+use Illuminate\Http\Request;
+use Illuminate\Routing\Controller;
+
+class ApiController extends Controller
+{
+    /**
+     * dist 分销商
+     */
+    public function dist(Request $request)
+    {
+        $q = $request->get('q');
+        if ($q != null) {
+            // 模糊搜索
+            $obj = new DistAdminDistributor();
+            return $obj->model()->where('client_code', 'like', "%$q%")->paginate(null, ['id', 'client_code as text']);
+        } else {
+            // 获取最新的N个
+            $selectOptionsNew =  DistAdminDistributor::selectOptionsNew();
+            return $this->changeOptions($selectOptionsNew);
+        }
+    }
+
+
+    /*
+ * 把数据转换成select需要的格式
+ */
+    private function changeOptions($data) {
+        // 初始化结果数组
+        $result = [];
+        // 遍历原始数据并转换格式
+        foreach ($data as $id => $text) {
+            $result[] = [
+                'id' => (int)$id,  // 将字符串转换为整数
+                'text' => $text
+            ];
+        }
+        return $result;
+    }
+
+
+}

+ 3 - 2
app/Admin/Controllers/BaseProductController.php

@@ -55,7 +55,7 @@ class BaseProductController extends AdminController
                 $dataImages = array_slice($dataImages, 0, 1);
                 return CommonHelper::displayImage($dataImages,100);
             });
-            $grid->column('order');
+            $grid->column('order')->sortable();
             $grid->column('enabled')->switch();
             $grid->column('created_at')->sortable();
             $grid->column('updated_at')->sortable();
@@ -69,7 +69,8 @@ class BaseProductController extends AdminController
                 $filter->equal('enabled')->select(admin_trans_array(config('dictionary.enabled')))->width(2);
             });
             //排序
-            $grid->model()->orderBy("order",'desc')->orderBy('id','desc');
+            $grid->model()->orderBy('created_at','desc');
+            $grid->disableViewButton();
         });
     }
 

+ 11 - 3
app/Admin/Controllers/DistAdminDistributorController.php

@@ -6,6 +6,7 @@ use App\Admin\Actions\Grid\InitAppearance;
 use App\Admin\Repositories\BaseIso3166;
 use App\Admin\Repositories\DistAdminDistributor;
 use App\Admin\Repositories\DistAppearance;
+use App\Admin\Repositories\RpcAlbumFolder;
 use App\Libraries\CommonHelper;
 use App\Models\DistProductCategory;
 use Dcat\Admin\Form;
@@ -68,6 +69,7 @@ class DistAdminDistributorController extends AdminController
             $grid->enableDialogCreate();
             $grid->disableEditButton();
             $grid->disableDeleteButton();
+            $grid->disableViewButton();
             //批量操作
             $grid->batchActions(function (Grid\Tools\BatchActions $batch) {
                 $batch->disableDelete();
@@ -108,14 +110,20 @@ class DistAdminDistributorController extends AdminController
      */
     protected function form()
     {
-        return Form::make(new DistAdminDistributor(), function (Form $form) {
+        $allNodes = RpcAlbumFolder::allNodes();
+        return Form::make(new DistAdminDistributor(), function (Form $form) use ($allNodes) {
             $form->text('client_code',admin_trans_label('client_code'))->width(9,3)->required();
-            $form->text('dist_email',admin_trans_label('email'))->width(9,3);
-            $form->text('dist_contact_number',admin_trans_label('contact_number'))->width(9,3);
             $form->select('country')
                 ->options(BaseIso3166::selectOptions())
                 ->width(9,3)->required();
             $form->text('country_lang')->width(9,3)->required()->help(admin_trans_label('country_lang_tips'));
+            $form->tree('album_folder',admin_trans_label('album_folder'))
+                ->setTitleColumn('title')
+                ->nodes($allNodes)
+                ->exceptParentNode(false)
+                ->width(9,3);
+            $form->text('dist_email',admin_trans_label('email'))->width(9,3);
+            $form->text('dist_contact_number',admin_trans_label('contact_number'))->width(9,3);
             $form->text('address')->width(9,3);
             $form->switch('enabled')->width(9,3)->default(1);
             $form->hidden('country_alpha_2');

+ 1 - 0
app/Admin/Controllers/DistAppearanceTemplateController.php

@@ -78,6 +78,7 @@ class DistAppearanceTemplateController extends AdminController
                 //返回模版修改日志内容
                 $logId = $request->get('log_id');
                 $rows = DistAppearanceTemplateLog::fetchTemplateLogContent($logId);
+                $rows->previous_content = htmlspecialchars($rows->previous_content);
                 return response()->json($rows);
             } else if ($request->get('act') == 'restore_template_log') {
                 //还原模版修改日志

+ 171 - 0
app/Admin/Controllers/DistProductAuditController.php

@@ -0,0 +1,171 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+use App\Admin\Actions\Grid\AppearanceImport;
+use App\Admin\Actions\Grid\ProductAudit;
+use App\Admin\Repositories\DistAdminDistributor;
+use App\Admin\Repositories\DistAppearance;
+use App\Admin\Repositories\DistProduct;
+use App\Admin\Repositories\DistProductCategory;
+use App\Libraries\CommonHelper;
+use Dcat\Admin\Admin;
+use Dcat\Admin\Form;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Http\Controllers\AdminController;
+use Dcat\Admin\Layout\Content;
+use Dcat\Admin\Show;
+
+class DistProductAuditController extends AdminController
+{
+
+    protected function title()
+    {
+        return admin_trans('admin.product');
+    }
+    /**
+     * page index
+     */
+    public function index(Content $content)
+    {
+        return $content
+            ->header(admin_trans( 'admin.products_list'))
+            ->description(admin_trans('admin.all'))
+            ->description('')
+            //->breadcrumb(['text'=>'Product Management','url'=>''])
+            ->body($this->grid());
+    }
+
+
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(DistProduct::with(['distProductCategory','images']), function (Grid $grid)  {
+            //默认分页条数
+            $grid->paginate(config('admin.per_page'));
+            //$grid->model()->where('dist_id', getDistributorId());
+            $grid->column('id')->display(function () {
+                return $this->_index+1;
+            });
+            $grid->column('title');
+            $grid->column('sku');
+            $grid->column('dist_product_category.name',admin_trans_label('category_name'));
+            $grid->column('images')->display(function ($images) {
+                $images = $images->toArray();
+                $dataImages = array_column($images, 'image_url');
+                // 限制最多显示1个缩略图
+                $dataImages = array_slice($dataImages, 0, 1);
+                return CommonHelper::displayImage($dataImages,100);
+            });
+
+            $grid->column('dist_id',admin_trans_label('distributor_name')) ->display(function () {
+                return DistAdminDistributor::getClientCodeById($this->dist_id);
+            });
+
+            $grid->column('status')->using(admin_trans_array(config('dictionary.dist_product_status')))->label([
+                -1 => 'danger',
+                0 => 'gray',
+                1 => 'orange1',
+                2 => 'success',
+            ])->display(function($status) {
+                if ($this->status == -1) {
+                    return '<span  class="status-popover" data-container="body" data-toggle="popover" data-placement="top" data-content="'.$this->review_reply.'">'.$status.'</span>';
+                }
+                return $status;
+            });
+            $grid->column('created_at')->sortable();
+            //$grid->column('updated_at')->sortable();
+            // 筛选
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->expand();
+                $filter->like('title')->width(2);
+                $filter->equal('sku')->width(2);
+                $filter->equal('status')->select(admin_trans_array(config('dictionary.dist_product_status')))->width(2);
+                $filter->equal('dist_id',admin_trans_label('distributor_name'))->select(DistAdminDistributor::selectOptionsNew(300))->width(3);
+            });
+            //排序
+            $grid->model()->orderBy('created_at', 'desc')->orderBy('id', 'desc');
+            //操作
+            $grid->actions(function (Grid\Displayers\Actions $actions) {
+                if ($this->status == 1) {
+                    $actions->append(new ProductAudit());
+                }
+            });
+            //按钮
+            //$grid->disableViewButton();
+            $grid->disableDeleteButton();
+            $grid->disableEditButton();
+            $grid->disableBatchDelete();
+            $this->addGridJS();
+        });
+    }
+
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+
+        return Show::make($id, DistProduct::with(['distProductCategory','images']), function (Show $show) {
+            $show->field('title');
+            $show->field('sku');
+            $show->field('dist_product_category.name',admin_trans_label('category_name'));
+            $show->field('keywords');
+            $show->field('description');
+            $show->field('issuance_date');
+            $show->field('parameters',admin_trans_label('attribute_name'))->as(function ($items) {
+                if (is_array($items)) {
+                    // 创建表格的表头
+                    $table = '<table class="table table-bordered table-condensed">';
+                    // 遍历数组并将数据填充到表格中
+                    foreach ($items as $item) {
+                        $table .= '<tr>';
+                        $table .= '<td style="vertical-align: middle !important;width: 20%">' . $item['key'] . '</td>';    // 商品名称
+                        $table .= '<td style="vertical-align: middle !important;">' . $item['value'] . '</td>'; // 数量
+                        $table .= '</tr>';
+                    }
+                    $table .= '</table>';
+                    return $table;
+                }
+                return ''; // 当没有数组数据时
+            })->unescape();
+            $show->field('images')->as(function ($images) {
+                // 开始生成 HTML
+                $dataImages = array_column($images, 'image_url');
+                return CommonHelper::displayImage($dataImages,150);
+            })->unescape();
+            $show->field('content')->unescape();
+            $show->field('seo_title');
+            $show->field('seo_keywords');
+            $show->field('seo_description');
+            $show->field('order');
+            $show->field('enabled')->using(admin_trans_array(config('dictionary.enabled')));
+            $show->field('created_at');
+            $show->field('updated_at');
+            $show->disableEditButton();
+            $show->disableDeleteButton();
+        });
+    }
+
+    private function addGridJS()
+    {
+        Admin::script(
+            <<<JS
+ $('.status-popover').popover({
+    container: 'body',
+     trigger: 'hover'
+  })
+JS
+        );
+    }
+}

+ 239 - 0
app/Admin/Controllers/ImportProductController.php

@@ -0,0 +1,239 @@
+<?php
+
+namespace App\Admin\Controllers;
+
+
+use App\Admin\Actions\Grid\RpcAlbumImport;
+use App\Admin\Forms\RpcAlbumImportForm;
+use App\Admin\Repositories\BaseProductCategory;
+use App\Admin\Repositories\RpcAlbum;
+use App\Admin\Repositories\RpcAlbumFolder;
+use App\Distributor\Actions\Extensions\DistProductImportForm;
+use App\Distributor\Repositories\BaseProduct;
+use App\Libraries\CommonHelper;
+use Dcat\Admin\Admin;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Http\Controllers\AdminController;
+use Dcat\Admin\Layout\Content;
+use Dcat\Admin\Show;
+
+class ImportProductController extends AdminController
+{
+    public function title()
+    {
+        return admin_trans( 'admin.product_import');
+    }
+
+    /**
+     * page index
+     */
+    public function index(Content $content)
+    {
+        return $content
+            ->header(admin_trans( 'admin.product_import'))
+            ->description('<span style="color: red; font-weight: bold;">'.admin_trans_label('select_products_to_import').'</span>')
+            ->breadcrumb(['text'=>'list','url'=>''])
+            ->body($this->grid());
+    }
+
+
+
+    //屏蔽删除
+    public function destroy($id)
+    {
+        abort(404);
+    }
+
+    //屏蔽创建
+    public function create(Content $content)
+    {
+        abort(404);
+    }
+
+    //屏蔽编辑
+    public function edit($id, Content $content)
+    {
+        abort(404);
+    }
+
+
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        return Grid::make(new RpcAlbum(), function (Grid $grid) {
+            $lang = config('app.locale');//当前语言
+            $grid->view('admin.grid.table');
+            $grid->column('id')->display(function () {
+                return $this->_index+1;
+            })->width('8%');
+            $grid->column('cover')->display(function ($images) {
+                $images = json_decode($images);
+                // 限制最多显示2个缩略图
+                $dataImages = array_slice($images, 0, 1);
+                return CommonHelper::displayImage($dataImages,100);
+            });
+            if ($lang == 'en') {
+                $grid->column('title_en');
+            } else {
+                $grid->column('title');
+            }
+            $grid->column('model');
+            $grid->column('missing_content')->display(function ($missing_content) {
+                $missing_content = [];
+                if ($this->cover == '[]') {$missing_content[] = '主图';}
+                if ($this->en_detail == '[]') {$missing_content[] = '英文详情';}
+                if ($this->cn_detail == '[]') {$missing_content[] = '中文详情';}
+                if ($this->video == '[]') {$missing_content[] = '视频';}
+                if ($this->poster == '[]') {$missing_content[] = '海报';}
+                if ($this->cert == '[]') {$missing_content[] = '证书';}
+                if ($this->pdf == '[]') {$missing_content[] = 'PDF';}
+                return implode(' / ', $missing_content);
+            });
+
+            $grid->column('created_at')->sortable();
+            $grid->column('updated_at')->sortable();
+
+            // 筛选
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->expand();
+                $filter->equal('model')->width(2);
+                $lang = config('app.locale');//当前语言
+                $filter->equal('folder_id',admin_trans_label('product_category'))->select(RpcAlbumFolder::selectOptions($lang))->width(3);
+            });
+            // 删除新增按钮
+            $grid->disableCreateButton();
+            //$grid->disableViewButton();
+            $grid->disableEditButton();
+            $grid->disableDeleteButton();
+            $grid->disableBatchDelete();
+            // 添加批量复制操作
+            $grid->batchActions(function ($batch) {
+                //$batch->add(new BatchCopy()); 只能2选1
+            });
+
+            $grid->tools([
+                new RpcAlbumImport(),
+            ]);
+
+            $grid->model()->where('enabled',1)->orderBy("order",'desc')->orderBy("created_at",'desc');
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new RpcAlbum(), function (Show $show) {
+            $lang = config('app.locale');//当前语言
+            if ($lang == 'en') {
+                $show->field('title_en');
+            } else {
+                $show->field('title');
+            }
+
+            $show->field('model');
+            $show->field('parameters',admin_trans_label('attribute'))->as(function ($items) {
+                $items = json_decode($items);
+                if (is_array($items)) {
+                    // 创建表格的表头
+                    $table = '<table class="table table-bordered table-condensed">';
+                    // 遍历数组并将数据填充到表格中
+                    foreach ($items as $item) {
+                        $item = (array)$item;
+                        $table .= '<tr>';
+                        $table .= '<td style="vertical-align: middle !important;width: 20%">' . $item['key'] . '</td>';    // 商品名称
+                        $table .= '<td style="vertical-align: middle !important;">' . $item['value'] . '</td>'; // 数量
+                        $table .= '</tr>';
+                    }
+                    $table .= '</table>';
+                    return $table;
+                }
+                return ''; // 当没有数组数据时
+            })->unescape();
+            $show->field('cover')->as(function ($images) {
+                $images = json_decode($images);
+                return CommonHelper::displayImage($images,150);
+            })->unescape();
+
+            $show->field('en_detail')->as(function ($images) {
+                $images = json_decode($images);
+                $html = '<div style="text-align: center">';
+                foreach ($images as $key => $image) {
+                    $url = CommonHelper::ossUrl($image);
+                    $html .= '<img src="' . $url . '" style="max-width:90%;margin-bottom:10px">';
+                }
+                $html .= '</div>';
+                return $html;
+            })->unescape();
+
+            $show->field('cn_detail')->as(function ($images) {
+                $images = json_decode($images);
+                $html = '<div style="text-align: center">';
+                foreach ($images as $key => $image) {
+                    $url = CommonHelper::ossUrl($image);
+                    $html .= '<img src="' . $url . '" style="max-width:90%;margin-bottom:10px">';
+                }
+                $html .= '</div>';
+                return $html;
+            })->unescape();
+
+            $show->field('video')->as(function ($items) {
+                $items = json_decode($items);
+                return CommonHelper::displayVideo($items,'cover','video_src','150');
+            })->unescape();
+
+            $show->field('poster')->as(function ($images) {
+                $images = json_decode($images);
+                return CommonHelper::displayImage($images,150);
+            })->unescape();
+
+            $show->field('cert')->as(function ($images) {
+                $images = json_decode($images);
+                return CommonHelper::displayImage($images,150);
+            })->unescape();
+
+            $show->field('cert')->as(function ($images) {
+                $images = json_decode($images);
+                return CommonHelper::displayImage($images,150);
+            })->unescape();
+
+            $show->field('pdf')->as(function ($items) {
+                $items = json_decode($items);
+                if (is_array($items)) {
+                    // 创建表格的表头
+                    $table = '<table class="table table-bordered table-condensed">';
+                    // 遍历数组并将数据填充到表格中
+                    foreach ($items as $item) {
+                        $table .= '<tr>';
+                        $table .= '<td style="vertical-align: middle !important;width: 20%">' . $item->pdf_title . '</td>';    // 商品名称
+                        $table .= '<td style="vertical-align: middle !important;"><a target="_blank" href="' . CommonHelper::ossUrl($item->pdf_src). '">查看</a></td>'; // 数量
+                        $table .= '</tr>';
+                    }
+                    $table .= '</table>';
+                    return $table;
+                }
+                return ''; // 当没有数组数据时
+            })->unescape();
+
+            // 禁用操作
+            $show->disableEditButton();
+            $show->disableDeleteButton();
+
+
+        });
+    }
+
+
+
+
+}

+ 12 - 6
app/Admin/Forms/AceLeft.php

@@ -7,19 +7,25 @@ use App\Models\DistAdminDistributor;
 use Symfony\Component\HttpFoundation\Response;
 use Dcat\Admin\Widgets\Form;
 use App\Admin\Repositories\DistAppearance;
+use App\Admin\Repositories\DistAdminDistributor as DistAdminDistributorRepository;
 
 class AceLeft extends Form
 {
     public function form()
     {
         //供应商选择
-        $this->selectTable('dist_id', 'Select Distributor')
+//        $this->selectTable('dist_id', 'Select Distributor')
+//            ->width(12,12)
+//            ->setLabelClass('d-flex')
+//            ->title('Select Distributor')
+//            ->placeholder('Select Distributor')
+//            ->model(DistAdminDistributor::class, 'id', 'client_code')
+//            ->from(DistDistributorTable::make());
+
+        $this->select('dist_id', 'Select Distributor')
+            ->options(DistAdminDistributorRepository::selectOptionsNew(1000))
             ->width(12,12)
-            ->setLabelClass('d-flex')
-            ->title('Select Distributor')
-            ->placeholder('Select Distributor')
-            ->model(DistAdminDistributor::class, 'id', 'client_code')
-            ->from(DistDistributorTable::make());
+            ->setLabelClass('d-flex');
 
         //主题选择
         $this->select('appearance_id','Select Appearance')

+ 123 - 0
app/Admin/Forms/ImportAlbum.php

@@ -0,0 +1,123 @@
+<?php
+
+namespace App\Admin\Forms;
+use App\Admin\Repositories\BaseProductCategory;
+use App\Admin\Repositories\RpcAlbum;
+use App\Libraries\CommonHelper;
+use App\Models\BaseProduct;
+use App\Distributor\Repositories\DistProductCategory;
+use App\Models\BaseProductImage;
+use App\Models\DistProduct;
+use App\Models\DistProductImage;
+use Dcat\Admin\Widgets\Form;
+
+class ImportAlbum extends Form
+{
+    /**
+     * Handle the form request.
+     *
+     * @param array $input
+     *
+     * @return mixed
+     */
+    public function handle(array $input)
+    {
+        $albumIds = explode(',', $input['album_ids']);
+        $categoryId = $input['category_id'];
+
+        // 检查 product_ids 是否为空
+
+        if (empty($input['album_ids'])) {
+            return $this
+                ->response()
+                ->error('请选择要导入的产品');
+        }
+
+        try {
+            //RPC读取相册
+            $rpcAlbum = new RpcAlbum();
+            $albumResult = $rpcAlbum->getByids($albumIds);
+
+            if ($albumResult['status']= false) {
+                return $this
+                    ->response()
+                    ->error('RPC获取相册失败:'. $albumResult['msg']);
+            }
+
+            foreach ($albumResult['data'] as $item) {
+                $title = isset($item['title']) ? $item['title'] : $item['model'];
+                // 创建新的 DistProduct 记录
+                $distProduct = BaseProduct::create([
+                    'category_id' => $categoryId,
+                    'title' => $title,
+                    'sku' => $item['model'], // 假设 $baseProduct 也有 sku 字段
+                    'issuance_date' => null, // 假设 $baseProduct 也有 issuance_date 字段
+                    'order' => 0, // 假设 $baseProduct 也有 order 字段
+                    'enabled' => 1, // 假设 $baseProduct 也有 enabled 字段
+                    'content' => $this->changeToimagesHTML($item['en_detail']), // 假设 $baseProduct 也有 content 字段
+                    'parameters' => $item['parameters'], // 假设 $baseProduct 也有 parameters 字段
+                    'seo_title'=> $title,
+                    'seo_keywords' => '',
+                    'seo_description' => '',
+                    'created_at' => now(), // 自动填充创建时间
+                    'updated_at' => now(), // 自动填充更新时间
+                ]);
+                //DistProduct::where('id', $distProduct->id)->update(['slug' => $distProduct->id]);
+                // 遍历 base_product_image 表中的记录,并插入到 dist_product_image 表中
+                $cover = json_decode($item['cover'], true);
+                foreach ($cover as $baseImage) {
+                    $i = 1;
+                    BaseProductImage::create([
+                        'image_url' => $baseImage,
+                        'product_id' => $distProduct->id, // 使用新创建的 DistProduct 的 ID
+                        'order' => $i,
+                        'created_at' => now(), // 自动填充创建时间
+                        'updated_at' => now(), // 自动填充更新时间
+                    ]);
+                    $i++;
+                }
+            }
+            return $this
+                ->response()
+                ->success('导入成功')
+                ->refresh();
+        } catch (\Exception $e) {
+            throw $e;
+            return $this
+                ->response()
+                ->error('导入失败: ' . $e->getMessage());
+        }
+    }
+
+    public function changeToimagesHTML($content) {
+        $content = json_decode($content, true);
+        $html = '';
+        foreach ($content as $item) {
+            $item = CommonHelper::ossUrl($item);
+            $html.= '<img src="'. $item. '">';
+        }
+        return $html;
+    }
+
+    /**
+     * Build a form here.
+     */
+    public function form()
+    {
+        // 设置隐藏表单,传递用户id
+        $this->select('category_id', admin_trans_label('category_name'))
+            ->options(BaseProductCategory::selectOptions())
+            ->required();
+        $this->hidden('album_ids')->attribute('id', 'album_ids');
+    }
+
+    /**
+     * The data of the form.
+     *
+     * @return array
+     */
+    public function default()
+    {
+        return [];
+    }
+}

+ 9 - 4
app/Admin/Forms/InquiryAssignment.php

@@ -25,6 +25,9 @@ class InquiryAssignment extends Form
         if (DistInquiry::assessTheStatus($ids) == false) {
             return $this->response()->error('The data cannot be assigned!');
         }
+        if (empty($distId)) {
+            return $this->response()->error('No distributor selected!');
+        }
         //分配数据
         DistInquiry::assignData($ids, $distId);
         return $this->response()->success(admin_trans_label('update_success'))->refresh();
@@ -32,10 +35,12 @@ class InquiryAssignment extends Form
 
     public function form()
     {
-        $this->selectTable('distId', admin_trans_label('select_distributor'))
-            ->title(admin_trans_label('select_distributor'))
-            ->model(DistAdminDistributor::class, 'id', 'client_code')
-            ->from(DistDistributorTable::make());
+//        $this->selectTable('distId', admin_trans_label('select_distributor'))
+//            ->title(admin_trans_label('select_distributor'))
+//            ->model(DistAdminDistributor::class, 'id', 'client_code')
+//            ->from(DistDistributorTable::make());
+
+        $this->select('distId',admin_trans_label('select_distributor'))->options('/api/dist')->required();
         // 设置隐藏表单,传递用户id
         $this->hidden('id')->attribute('id', 'inquiryIds');
     }

+ 52 - 0
app/Admin/Forms/ProductAudit.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace App\Admin\Forms;
+use App\Admin\Repositories\BaseProductCategory;
+use App\Admin\Repositories\DistProduct;
+use App\Admin\Repositories\RpcAlbum;
+use App\Libraries\CommonHelper;
+use App\Models\BaseProduct;
+use App\Models\BaseProductImage;
+use Dcat\Admin\Widgets\Form;
+
+class ProductAudit extends Form
+{
+
+
+
+    /**
+     * Handle the form request.
+     *
+     * @param array $input
+     *
+     * @return mixed
+     */
+    public function handle(array $input)
+    {
+        $id = $this->payload['id'] ?? null;
+
+        $reviewReply = $input['review_reply'] ?? '';
+        $status = $input['status'] ?? null;
+        if (empty($status) || empty($id)) {
+            return $this->response()->error('参数错误');
+        }
+        DistProduct::auditProduct($id, $status, $reviewReply);
+        return $this
+            ->response()
+            ->success('审批成功')
+            ->refresh();
+    }
+
+
+    /**
+     * Build a form here.
+     */
+    public function form()
+    {
+        $review_reply = $this->payload['review_reply'] ?? null;
+        // 设置隐藏表单,传递用户id
+        $this->radio('status')->options(['2' => admin_trans_label('audit_success'), '-1'=> admin_trans_label('approval_failed')])->default('2');
+        $this->textarea('review_reply')->default($review_reply);
+    }
+
+}

+ 15 - 0
app/Admin/Repositories/DistAdminDistributor.php

@@ -21,6 +21,21 @@ class DistAdminDistributor extends EloquentRepository
      */
     protected $eloquentClass = Model::class;
 
+    public static function  getClientCodeById($id)
+    {
+        return Model::where('id', $id)->value('client_code');
+    }
+
+    /*
+     * 查找最新的N个标签
+     */
+    public static function selectOptionsNew($limit=30)
+    {
+        return Model::orderBy('created_at', 'desc')->limit($limit)->pluck('client_code', 'id');
+    }
+
+
+
     /*
      * 清缓存
      */

+ 7 - 0
app/Admin/Repositories/DistInquiry.php

@@ -61,4 +61,11 @@ class DistInquiry extends EloquentRepository
         return $result;
     }
 
+    /*
+     *  供应商获取未处理的询价单
+     */
+    public static  function getProcessingNum() {
+        return Model::where('status', 0)->count();
+    }
+
 }

+ 73 - 0
app/Admin/Repositories/DistProduct.php

@@ -0,0 +1,73 @@
+<?php
+
+namespace App\Admin\Repositories;
+
+use App\Traits\DistSlugTrait;
+use Dcat\Admin\Form;
+use App\Models\DistProduct as Model;
+use Dcat\Admin\Repositories\EloquentRepository;
+
+class DistProduct extends EloquentRepository
+{
+    use DistSlugTrait;
+    /**
+     * Model.
+     *
+     * @var string
+     */
+    protected $eloquentClass = Model::class;
+
+    public function delete(Form $form, array $originalData)
+    {
+        collect(explode(',', $form->getKey()))->filter()->each(function ($id) {
+
+            $product = Model::find($id);
+            if ($product) {
+                // 验证 dist_id
+                if ($product->dist_id !== getDistributorId()) {
+                    throw new \Exception('Unable to modify the product because the distributor ID does not match.');
+                    return;
+                }
+                Model::find($id)->images()->delete();
+                Model::find($id)->delete();
+            }
+        });
+        return true;
+    }
+
+    public static function findById($id)
+    {
+        return Model::find($id); // 查找并返回相应的记录
+    }
+
+
+    /*
+     * 查找最新的N个产品
+     */
+    public static function selectOptionsNew($limit=30)
+    {
+        return Model::where('dist_id', getDistributorId())->where('enabled', 1)->orderBy('created_at', 'desc')->limit($limit)->pluck('title', 'id');
+    }
+
+
+    /*
+     * 获取一个标签
+     */
+    public static function getOneById($id)
+    {
+        return Model::where('id', $id)->where('dist_id', getDistributorId())->first();
+    }
+
+    /*
+     * 审核产品
+     */
+    public static function auditProduct($id , $status , $reviewReply) {
+        $product = Model::find($id);
+        if ($product) {
+            $product->status = $status;
+            $product->review_reply = $reviewReply;
+            $product->save();
+        }
+        return true;
+    }
+}

+ 129 - 0
app/Admin/Repositories/DistProductCategory.php

@@ -0,0 +1,129 @@
+<?php
+
+namespace App\Admin\Repositories;
+
+use App\Models\DistProductCategory as Model;
+
+use App\Models\DistProductParameter;
+use App\Traits\DistSlugTrait;
+use Dcat\Admin\Repositories\EloquentRepository;
+use Illuminate\Support\Facades\Session;
+use function Symfony\Component\Translation\t;
+
+class DistProductCategory extends EloquentRepository
+{
+    use DistSlugTrait;
+    /**
+     * Model.
+     *
+     * @var string
+     */
+    protected $eloquentClass = Model::class;
+
+    public function __construct()
+    {
+
+    }
+
+    // 调用模型方法
+    public static function selectMainOptions(\Closure $closure = null)
+    {
+        // 获取当前登录的分销商/经销商
+        $distributor=Session::get('distributor');
+
+        // 过滤分销商
+        if (!$distributor)
+        {
+            return false;
+        }
+
+        $query = Model::query();
+
+        //通过传入条件限制
+        if ($closure) {
+            $closure($query);
+        }
+
+        // 确保查询构造器不为空
+        if (!$query) {
+            return [];
+        }
+
+        // 过滤分销商
+        $query->where('dist_id', getDistributorId());
+
+        // 返回键值对数组,并按 'name' 和 'order' 字段排序
+        $results = $query->orderBy('order', 'desc')->orderBy('id', 'desc')->pluck('name', 'id');
+
+        // 在每个 name 前面加上 '├─'
+        $results = $results->map(function ($name) {
+            return '├─' . $name;
+        });
+
+        $all = array_replace([0 => admin_trans_label('root')],$results->all());
+        return $all;
+    }
+
+    // 调用模型方法
+    public static function selectOptions(\Closure $closure = null)
+    {
+        if (!$closure) {
+            $closure = function ($query) {
+                $query = $query->where('dist_id', getDistributorId());
+                $query = $query->where('enabled', 1);
+                $query =  $query->orderBy('order', 'desc')->orderBy('id', 'desc');
+                return $query;
+            };
+        }
+
+        $selectOptions = Model::class::selectOptions($closure);
+        return $selectOptions;
+    }
+
+
+    public static function getParameter($categoryId)
+    {
+        $categoryRow =  Model::Class::find($categoryId);
+        if ($categoryRow->baseProductParameter) {
+            return $categoryRow->baseProductParameter->content;
+        } else {
+            return "[]";
+        }
+    }
+
+    /**
+     * @param Grid\Model|\Dcat\Admin\Grid\Model $model
+     * @return array
+     */
+    public function get(Grid\Model|\Dcat\Admin\Grid\Model $model)
+    {
+
+        // 获取模型对象
+        $obj = $this->model();
+
+        $obj = $obj->where('dist_id', getDistributorId())->orderBy('order', 'desc')->orderBy('id', 'desc');
+
+        // 获取数据
+        $tree = $obj->get();
+
+        // 构建树形结构
+        $data=buildTree($tree->toArray());
+        // 转换成一维数组
+        $data=flattenTree($data);
+
+        return $model->makePaginator(
+            count($data), // 传入总记录数
+            $data // 传入数据二维数组
+        );
+    }
+
+
+
+    /*
+     * 获取一个分类
+     */
+    public static function getOneById($id)
+    {
+        return Model::where('id', $id)->where('dist_id', getDistributorId())->first();
+    }
+}

+ 55 - 0
app/Admin/Repositories/DistProductImage.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace App\Admin\Repositories;
+
+use App\Models\DistProductImage as Model;
+use Dcat\Admin\Repositories\EloquentRepository;
+
+class DistProductImage extends EloquentRepository
+{
+    /**
+     * Model.
+     *
+     * @var string
+     */
+    protected $eloquentClass = Model::class;
+
+    //格式化数据,用于多图上传
+    public static function formatData($productId, $images)
+    {
+        $existingImages = Model::where('product_id', $productId)->get();
+        // 用于存储结果的数组
+        $result = [];
+        // 处理条件1:如果数据在 base_product_image 中有,但在 $images 上无
+        foreach ($existingImages as $existingImage) {
+            if (!in_array($existingImage->image_url, $images)) {
+                $result[] = [
+                    'id' => $existingImage->id,
+                    'image_url' => $existingImage->image_url,
+                    '_remove_' => 1,
+                ];
+            }
+        }
+        // 处理条件2:如果数据在 base_product_image 中无,但在 $images 有
+        $i = 1;
+        foreach ($images as $image) {
+            $found = $existingImages->firstWhere('image_url', $image);
+            if (!$found) {
+                $result[] = [
+                    'id' => 0,
+                    'image_url' => $image,
+                    'order' => $i,
+                ];
+            }
+            //更新库中排序
+            foreach ($existingImages as $existingImage) {
+                if ($existingImage->image_url == $image) {
+                    $existingImage->order = $i;
+                    $existingImage->save();
+                }
+            }
+            $i++;
+        }
+        return $result;
+    }
+}

+ 94 - 0
app/Admin/Repositories/RpcAlbum.php

@@ -0,0 +1,94 @@
+<?php
+
+namespace App\Admin\Repositories;
+
+use App\Libraries\RpcClient;
+use App\Models\NullModel as Model;
+use Dcat\Admin\Form;
+use Dcat\Admin\Repositories\EloquentRepository;
+use Dcat\Admin\Show;
+use JsonRPC\Client;
+
+/*
+ * RPC调用相册
+ */
+class RpcAlbum extends EloquentRepository
+{
+    /**
+     * Model.
+     *
+     * @var string
+     */
+    protected $eloquentClass = Model::class;
+
+
+    public function execute($method, $params = [])
+    {
+        return RpcClient::albumExecute($method, $params);
+    }
+
+    /*
+     * 通过IDS获取相册详情
+     */
+    public function getByids($ids)
+    {
+        return $this->execute('siteAlbumGetByIds', [
+            'ids' => $ids,
+        ]);
+    }
+
+    /*
+     * 获取相册列表
+     */
+    public function get(Grid\Model|\Dcat\Admin\Grid\Model $model)
+    {
+        // 获取当前页数
+        $currentPage = $model->getCurrentPage();
+        // 获取每页显示行数
+        $perPage = $model->getPerPage();
+        //排序
+        $sort = $model->getSort();
+        if (empty($sort) == true || $sort[0] == null) {
+            $sort = [];
+        }
+        // 获取筛选参数
+        $filterModel = $model->filter()->input('model', '');
+        $folder_id = $model->filter()->input('folder_id', '');
+
+        $filter = [
+            'model' => $filterModel,
+            'folder_id' => $folder_id,
+        ];
+
+        $result = $this->execute('siteAlbumPaginate', [
+            'filter' => $filter,
+            'sort' => $sort,
+            'perPage'=>$perPage,
+            'page' => $currentPage,
+        ]);
+
+        $data = $result['data'] ?? [];
+
+        return $model->makePaginator(
+          $data['total'] ?? 0,
+           $data['data'] ?? [] // 传入数据二维数组
+        );
+    }
+
+
+    /*
+     * 获取相册详情
+     */
+    public function detail(Show $show): array
+    {
+        // 获取数据主键值
+        $id = $show->getKey();
+        $result = $this->execute('siteAlbumGet', [
+            'id' => $id,
+        ]);
+        $data = $result['data'] ?? [];
+        return $data;
+    }
+
+
+}

+ 49 - 0
app/Admin/Repositories/RpcAlbumFolder.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace App\Admin\Repositories;
+
+use App\Libraries\RpcClient;
+use App\Models\NullModel as Model;
+use Dcat\Admin\Repositories\EloquentRepository;
+use Dcat\Admin\Show;
+
+/*
+ * RPC调用相册
+ */
+
+class RpcAlbumFolder extends EloquentRepository
+{
+    /**
+     * Model.
+     *
+     * @var string
+     */
+    protected $eloquentClass = Model::class;
+
+    public function execute($method, $params = [])
+    {
+        return RpcClient::albumExecute($method, $params);
+    }
+
+
+    /*
+     * 获取相册文件夹列表
+     * $inIds 传入的相册ID数组,为空时获取全部相册
+     */
+    public static function selectOptions($lang = 'en') {
+        $self = new self();
+        $result = $self->execute('siteAlbumFolderSelectOptionsAll', ['lang'=>$lang]);
+        $data = $result['data'] ?? [];
+        return $data;
+    }
+
+    /*
+     * 获取相册文件夹树
+     */
+    public static function allNodes() {
+        $self = new self();
+        $result = $self->execute('siteAlbumFolderAllNodes', []);
+        $data = $result['data'] ?? [];
+        return $data;
+    }
+}

+ 6 - 0
app/Admin/bootstrap.php

@@ -11,6 +11,7 @@ use Dcat\Admin\Show;
 use Dcat\Admin\Layout\Content;
 use Illuminate\Support\ServiceProvider;
 use Dcat\Admin\Form\Field\Editor;
+use Dcat\Admin\Layout\Menu;
 
 /**
  * Dcat-admin - admin builder based on Laravel.
@@ -58,6 +59,11 @@ Editor::resolving(function (Editor $editor) {
     ]);
 });
 
+//菜单
+Admin::menu(function (Menu $menu) {
+    $menu->view('admin.partials_custom.menu');
+});
+
 //裁剪图片表单扩展
 Form::extend('multipleCutImage', MultipleCutImage::class);
 Form::extend('cutImage', CutImage::class);

+ 11 - 0
app/Admin/routes.php

@@ -47,6 +47,17 @@ Route::group([
 
     // 不需要登录的路由
     $router->get('captcha','CaptchaController@generate');
+
+    //api接口
+    $router->get('api/dist', 'ApiController@dist');
+
+    //产品导入
+    $router->get('import-product', 'ImportProductController@index');
+    $router->get('import-product/{id}', 'ImportProductController@show');
+
+    //产品审核
+    $router->get('dist-product-audit', 'DistProductAuditController@index');
+    $router->get('dist-product-audit/{id}', 'DistProductAuditController@show');
 });
 
 /*

+ 1 - 1
app/Distributor/Controllers/ApiController.php

@@ -22,7 +22,7 @@ class ApiController extends Controller
         if ($q != null) {
             // 模糊搜索
             $obj = new DistProduct();
-            return $obj->model()->where('title', 'like', "%$q%")->where('dist_id', getDistributorId())->paginate(null, ['id', 'title as text']);
+            return $obj->model()->where('title', 'like', "%$q%")->where('dist_id', getDistributorId())->where('status', 2)->paginate(null, ['id', 'title as text']);
         } else {
             // 获取最新的N个
             $selectOptionsNew =  DistProduct::selectOptionsNew();

+ 2 - 2
app/Distributor/Controllers/DistInquiryController.php

@@ -69,10 +69,10 @@ class DistInquiryController extends AdminDistController
             $grid->disableDeleteButton();
             $grid->disableBatchDelete();
             $grid->showViewButton();
-            if ($status == 1) {
+          //  if ($status == 1) {
                 //批量操作
                 $grid->tools([new InquiryHandle()]);
-            }
+          //  }
             //权限与排序
             $grid->model()->where('dist_id', getDistributorId())->where('status','>',0)->orderBy("id",'desc');
         });

+ 80 - 13
app/Distributor/Controllers/DistProductController.php

@@ -56,6 +56,7 @@ class DistProductController extends AdminDistController
             });
             $grid->column('title');
             $grid->column('sku');
+
             $grid->column('dist_product_category.name',admin_trans_label('category_name'));
             $grid->column('images')->display(function ($images) {
                 $images = $images->toArray();
@@ -64,10 +65,21 @@ class DistProductController extends AdminDistController
                 $dataImages = array_slice($dataImages, 0, 1);
                 return CommonHelper::displayImage($dataImages,100);
             });
-            $grid->column('order');
+            $grid->column('order')->sortable();
             $grid->column('enabled')->switch();
+            $grid->column('status')->using(admin_trans_array(config('dictionary.dist_product_status')))->label([
+                -1 => 'danger',
+                0 => 'gray',
+                1 => 'orange1',
+                2 => 'success',
+            ])->display(function($status) {
+                if ($this->status == -1) {
+                    return '<span  class="status-popover" data-container="body" data-toggle="popover" data-placement="top" data-content="'.$this->review_reply.'">'.$status.'</span>';
+                }
+                return $status;
+            });
             $grid->column('created_at')->sortable();
-            $grid->column('updated_at')->sortable();
+            //$grid->column('updated_at')->sortable();
             // 筛选
             $grid->filter(function (Grid\Filter $filter) {
                 $filter->panel();
@@ -78,13 +90,15 @@ class DistProductController extends AdminDistController
                 $filter->equal('enabled', admin_trans_label('enabled'))->select(admin_trans_array( config('dictionary.enabled')))->width(2);
             });
             //排序
-            $grid->model()->orderBy("order",'desc')->orderBy('id', 'desc');
+            $grid->model()->orderBy('created_at', 'desc')->orderBy('id', 'desc');
             // 传入数组
             $grid->tools([
                 new DistProductImport(),
             ]);
             //按钮
-            $grid->disableCreateButton();
+            //$grid->disableCreateButton();
+            $grid->disableViewButton();
+            $this->addGridJS();
         });
 
     }
@@ -152,7 +166,6 @@ class DistProductController extends AdminDistController
     protected function form()
     {
         return Form::make(DistProduct::with('images'), function (Form $form) {
-            //$form->display('id');
             $form->select('category_id', admin_trans_label('category_name'))
                 ->options(DistProductCategory::selectOptions())
                 ->required();
@@ -195,13 +208,18 @@ class DistProductController extends AdminDistController
             $form->switch('enabled')->default(1);
             //隐藏字段
             $form->hidden('dist_id'); // 隐藏dist_id字段,用于保存
+            $form->hidden('status');
+            $form->hidden('review_reply');
+            $form->hidden('action');
+
+            $form->ignore(['action']);
+
             //插入JS
             $this->addParametersJs();
             //提交前
             // 在保存时进行验证
             $form->saving(function (Form $form) {
                 $form->dist_id =getDistributorId();//保存时直接写死dist_id
-
                 $id = $form->getKey();
                 //slug配置是否重复
                 if ($form->slug != '') {
@@ -218,18 +236,35 @@ class DistProductController extends AdminDistController
                 if ($form->seo_title == '') {
                     $form->seo_title = $form->title;
                 }
+                //增加审核状态
+                $action = isset($_POST['action']) ? $_POST['action'] : '';
+                if ($form->isCreating()) {
+                    if ($action == 'save') {
+                        $form->status = 0;//待审核
+                    } else {
+                        $form->status = 1;//审核中
+                    }
+                } else {
+                    if (($form->status == 0 || $form->status == -1) && $action == 'submit') {
+                        $form->status = 1;//审核中
+                    }
+                }
             });
+            //页面底部自定义
+            $status = $form->model()->status ?? 0;
+            $isCreating = $form->isCreating() ? 0 : 1;
+            $form->builder()->footer()->view('distributor.form_custom.review-footer',['status'=>$status,'isCreating'=>$isCreating]);
+            //插入JS
+            $this->addReviewJS();
+            //禁用按钮
+            $form->disableViewCheck();
+            $form->disableEditingCheck();
+            $form->disableViewButton();
         });
-    }
 
-    /*
-     * 禁用新增
-     */
-    public function store()
-    {
-        abort(404);
     }
 
+
     /*
      * 以json型式返回产品参数
      */
@@ -251,6 +286,38 @@ class DistProductController extends AdminDistController
             <<<JS
 
 
+JS
+        );
+    }
+
+    /*
+     * 用于点击按钮时,将按钮的 value 值设置到隐藏 input[name="action"] 的值,并提交表单
+     */
+    private function addReviewJS()
+    {
+        Admin::script(
+            <<<JS
+  $('.submit').click(function(e) {
+    // 阻止默认提交行为(可选,视需求而定)
+    e.preventDefault();
+    // 获取当前按钮的 value
+    var value = $(this).val();
+    // 将 value 设置为隐藏 input[name="action"] 的值
+    $('input[name="action"]').val(value);
+    // 提交表单
+    $(this).closest('form').trigger('submit');
+  });
+JS
+        );
+    }
+    private function addGridJS()
+    {
+        Admin::script(
+<<<JS
+ $('.status-popover').popover({
+    container: 'body',
+     trigger: 'hover'
+  })
 JS
         );
     }

+ 226 - 0
app/Distributor/Controllers/SiteAlbumController.php

@@ -0,0 +1,226 @@
+<?php
+
+namespace App\Distributor\Controllers;
+
+
+use App\Admin\Actions\Grid\RpcAlbumImport;
+use App\Admin\Forms\RpcAlbumImportForm;
+use App\Distributor\Repositories\RpcAlbum;
+use App\Distributor\Repositories\RpcAlbumFolder;
+use App\Distributor\Repositories\DistAdminDistributor;
+use App\Libraries\CommonHelper;
+use Dcat\Admin\Grid;
+use Dcat\Admin\Http\Controllers\AdminController;
+use Dcat\Admin\Layout\Content;
+use Dcat\Admin\Show;
+
+class SiteAlbumController extends AdminController
+{
+    public function title()
+    {
+        return admin_trans( 'admin.promotional_materials');
+    }
+
+    /**
+     * page index
+     */
+    public function index(Content $content)
+    {
+        return $content
+            ->header(admin_trans( 'admin.promotional_materials'))
+            ->breadcrumb(['text'=>'list','url'=>''])
+            ->body($this->grid());
+    }
+
+
+
+    //屏蔽删除
+    public function destroy($id)
+    {
+        abort(404);
+    }
+
+    //屏蔽创建
+    public function create(Content $content)
+    {
+        abort(404);
+    }
+
+    //屏蔽编辑
+    public function edit($id, Content $content)
+    {
+        abort(404);
+    }
+
+
+    /**
+     * Make a grid builder.
+     *
+     * @return Grid
+     */
+    protected function grid()
+    {
+        //相册分类
+        $lang = config('app.locale');//当前语言
+        $rpcAlbumFolder = RpcAlbumFolder::selectOptions($lang);
+        return Grid::make(new RpcAlbum(), function (Grid $grid) use ($rpcAlbumFolder, $lang) {
+            $grid->view('admin.grid.table');
+            $grid->column('id')->display(function () {
+                return $this->_index+1;
+            });
+            $grid->column('cover')->display(function ($images) {
+                $images = json_decode($images);
+                // 限制最多显示2个缩略图
+                $dataImages = array_slice($images, 0, 1);
+                return CommonHelper::displayImage($dataImages,100);
+            });
+            if ($lang == 'en') {
+                $grid->column('title_en');
+            } else {
+                $grid->column('title');
+            }
+            $grid->column('model');
+            $grid->column('updated_at')->sortable();
+            // 筛选
+            $grid->filter(function (Grid\Filter $filter) use ($rpcAlbumFolder, $lang) {
+                $filter->panel();
+                $filter->expand();
+                $filter->equal('model')->width(2);
+                $filter->equal('folder_id',admin_trans_label('product_category'))->select($rpcAlbumFolder)->width(3);
+            });
+            // 删除新增按钮
+            $grid->disableCreateButton();
+            $grid->disableEditButton();
+            $grid->disableDeleteButton();
+            $grid->disableBatchDelete();
+            // 添加批量复制操作
+            $grid->batchActions(function ($batch) {
+                //$batch->add(new BatchCopy()); 只能2选1
+            });
+            $grid->model()->where('enabled',1)->orderBy("order",'desc')->orderBy("created_at",'desc');
+        });
+    }
+
+    /**
+     * Make a show builder.
+     *
+     * @param mixed $id
+     *
+     * @return Show
+     */
+    protected function detail($id)
+    {
+        return Show::make($id, new RpcAlbum(), function (Show $show) {
+            $lang = config('app.locale');//当前语言
+            //名称
+            if ($lang == 'en') {
+                $show->field('title');
+            } else {
+                $show->field('title_en');
+            }
+            //型号
+            if ($show->model()->model) {
+                $show->field('model');
+            }
+
+            //属性
+            if ($show->model()->parameters) {
+                $show->field('parameters', admin_trans_label('attribute'))->as(function ($items) {
+                    $items = json_decode($items);
+                    if (is_array($items)) {
+                        // 创建表格的表头
+                        $table = '<table class="table table-bordered table-condensed">';
+                        // 遍历数组并将数据填充到表格中
+                        foreach ($items as $item) {
+                            $item = (array)$item;
+                            $table .= '<tr>';
+                            $table .= '<td style="vertical-align: middle !important;width: 20%">' . $item['key'] . '</td>';    // 商品名称
+                            $table .= '<td style="vertical-align: middle !important;">' . $item['value'] . '</td>'; // 数量
+                            $table .= '</tr>';
+                        }
+                        $table .= '</table>';
+                        return $table;
+                    }
+                    return ''; // 当没有数组数据时
+                })->unescape();
+            }
+
+            //封面
+            if (empty($show->model()->cover) == false && $show->model()->cover!= '[]') {
+                $show->field('cover')->as(function ($images) {
+                    $images = json_decode($images);
+                    return CommonHelper::displayImage($images,150);
+                })->unescape();
+            }
+            //详情
+            if (empty($show->model()->en_detail) == false && $show->model()->en_detail!= '[]') {
+                $show->field('en_detail')->as(function ($images) {
+                    $images = json_decode($images);
+                    $html = '<div style="text-align: center">';
+                    foreach ($images as $key => $image) {
+                        $url = CommonHelper::ossUrl($image);
+                        $html .= '<img src="' . $url . '" style="max-width:90%;margin-bottom:10px">';
+                    }
+                    $html .= '</div>';
+                    return $html;
+                })->unescape();
+            }
+            //视频
+            if (empty($show->model()->video) == false && $show->model()->video!= '[]') {
+                $show->field('video')->as(function ($items) {
+                    $items = json_decode($items);
+                    //倒序
+                    $items = array_reverse($items);
+                    return CommonHelper::displayVideo($items,'cover','video_src','150');
+                })->unescape();
+            }
+            //海报
+            if (empty($show->model()->poster) == false && $show->model()->poster!= '[]') {
+                $show->field('poster')->as(function ($images) {
+                    $images = json_decode($images);
+                    return CommonHelper::displayImage($images,150);
+                })->unescape();
+            }
+            //证书
+            if (empty($show->model()->cert) == false && $show->model()->cert != '[]') {
+                $show->field('cert')->as(function ($images) {
+                    $images = json_decode($images);
+                    return CommonHelper::displayImage($images,150);
+                })->unescape();
+            }
+            //PDF
+            if (empty($show->model()->pdf) == false && $show->model()->pdf != '[]') {
+                $show->field('pdf')->as(function ($items) use ($lang) {
+                    $items = json_decode($items);
+                    if (is_array($items)) {
+                        //倒序
+                        $items = array_reverse($items);
+                        // 创建表格的表头
+                        $table = '<table class="table table-bordered table-condensed">';
+                        // 遍历数组并将数据填充到表格中
+                        foreach ($items as $item) {
+                            $pdf_title = $item->pdf_title;
+                            if ($lang == 'en') {
+                                $pdf_title = $item->pdf_title_en;
+                            }
+                            $table .= '<tr>';
+                            $table .= '<td style="vertical-align: middle !important;width: 20%">' . $pdf_title . '</td>';    // 商品名称
+                            $table .= '<td style="vertical-align: middle !important;"><a target="_blank" href="' . CommonHelper::ossUrl($item->pdf_src). '">查看</a></td>'; // 数量
+                            $table .= '</tr>';
+                        }
+                        $table .= '</table>';
+                        return $table;
+                    }
+                    return ''; // 当没有数组数据时
+                })->unescape();
+            }
+            // 禁用操作
+            $show->disableEditButton();
+            $show->disableDeleteButton();
+        });
+    }
+
+
+
+
+}

+ 8 - 2
app/Distributor/Controllers/SiteBannerController.php

@@ -48,6 +48,13 @@ class SiteBannerController extends AdminDistController
             $grid->column('position');
             $grid->column('order');
             $grid->column('show')->switch();
+            $grid->filter(function (Grid\Filter $filter) {
+                $filter->panel();
+                $filter->expand();
+                $filter->equal('title')->width(2);
+                $filter->equal('position')->select(SiteBanner::getPositionOptions())->width(2);
+            });
+
             $grid->disableViewButton();
             $grid->showQuickEditButton();
             $grid->enableDialogCreate();
@@ -66,8 +73,7 @@ class SiteBannerController extends AdminDistController
         return Form::make(new SiteBanner(), function (Form $form) {
             $form->text('title')->required();
             $form->text('subtitle');
-            $form->cutImage("image_url", admin_trans_label('images'))
-                ->aspectRatio(1920/600)// 设置裁剪框比例
+            $form->image("image_url", admin_trans_label('images'))
                 ->retainable()//禁止删OSS图
                 ->autoUpload()
                 ->uniqueName()

+ 8 - 0
app/Distributor/Controllers/SiteMenuController.php

@@ -98,6 +98,14 @@ class SiteMenuController extends AdminDistController
                 }))->required();
             //标题
             $form->text('title')->required();
+            //图片
+            $form->image("image_url", admin_trans_label('images'))
+                ->retainable()//禁止删OSS图
+                ->autoUpload()
+                ->uniqueName()
+                ->accept(config('distributor.upload.oss_image.accept'))
+                ->maxSize(config('distributor.upload.oss_image.max_size'))
+                ->dir(config("distributor.upload.directory.image").'/menu/'.date("Ymd"));
             //类型
             $form->select('menu_type')->options(admin_trans_array(config('dictionary.menu_type')))->required()
                 ->when(1, function (Form $form) use ($menuConfig,$thisObject) {

+ 2 - 2
app/Distributor/Forms/ImportProduct.php

@@ -48,8 +48,6 @@ class ImportProduct extends Form
 // 获取 base_product_image 表中与当前 base_product 相关的图片记录
                 $baseProductImages = BaseProductImage::where('product_id', $baseProduct->id)->get();
 
-
-
                 // 创建新的 DistProduct 记录
                 $distProduct = DistProduct::create([
                     'category_id' => $categoryId,
@@ -66,6 +64,8 @@ class ImportProduct extends Form
                     'dist_id' => getDistributorId(),
                     'created_at' => now(), // 自动填充创建时间
                     'updated_at' => now(), // 自动填充更新时间
+                    'status' => 2,
+                    'review_reply' => '',
                 ]);
                 DistProduct::where('id', $distProduct->id)->update(['slug' => $distProduct->id]);
                 // 遍历 base_product_image 表中的记录,并插入到 dist_product_image 表中

+ 1 - 1
app/Distributor/Repositories/DistProduct.php

@@ -46,7 +46,7 @@ class DistProduct extends EloquentRepository
      */
     public static function selectOptionsNew($limit=30)
     {
-        return Model::where('dist_id', getDistributorId())->where('enabled', 1)->orderBy('created_at', 'desc')->limit($limit)->pluck('title', 'id');
+        return Model::where('dist_id', getDistributorId())->where('enabled', 1)->where('status', 2)->orderBy('created_at', 'desc')->limit($limit)->pluck('title', 'id');
     }
 
 

+ 102 - 0
app/Distributor/Repositories/RpcAlbum.php

@@ -0,0 +1,102 @@
+<?php
+
+namespace App\Distributor\Repositories;
+
+use App\Libraries\RpcClient;
+use App\Models\NullModel as Model;
+use Dcat\Admin\Form;
+use Dcat\Admin\Repositories\EloquentRepository;
+use Dcat\Admin\Show;
+use JsonRPC\Client;
+
+/*
+ * RPC调用相册
+ */
+class RpcAlbum extends EloquentRepository
+{
+    /**
+     * Model.
+     *
+     * @var string
+     */
+    protected $eloquentClass = Model::class;
+
+    public $albumFolder = [];
+
+    public function __construct($modelOrRelations = [])
+    {
+        $this->initModel($modelOrRelations);
+        $distInfo = DistAdminDistributor::getInfo();
+        //可查看的相册目录
+        $albumFolder = empty($distInfo->album_folder) ? [] : json_decode($distInfo->album_folder);
+        $this->albumFolder = $albumFolder;
+    }
+
+    /*
+     * 执行RPC调用
+     */
+    public function execute($method, $params = [])
+    {
+        return RpcClient::albumExecute($method, $params);
+    }
+
+    /*
+     * 获取相册列表
+     */
+    public function get(Grid\Model|\Dcat\Admin\Grid\Model $model)
+    {
+        $self = new self();
+        // 获取当前页数
+        $currentPage = $model->getCurrentPage();
+        // 获取每页显示行数
+        $perPage = $model->getPerPage();
+        //排序
+        $sort = $model->getSort();
+        if (empty($sort) == true || $sort[0] == null) {
+            $sort = [];
+        }
+        // 获取筛选参数
+        $filterModel = $model->filter()->input('model', '');
+        $folder_id = $model->filter()->input('folder_id', '');
+
+        $filter = [
+            'model' => $filterModel,
+            'folder_id' => $folder_id,
+            'album_folder' => $self->albumFolder,//把查询结果限定在album_folder中
+        ];
+
+        $result = $this->execute('siteAlbumPaginate', [
+            'filter' => $filter,
+            'sort' => $sort,
+            'perPage'=>$perPage,
+            'page' => $currentPage,
+        ]);
+
+
+
+
+        $data = $result['data'] ?? [];
+
+        return $model->makePaginator(
+          $data['total'] ?? 0,
+           $data['data'] ?? [] // 传入数据二维数组
+        );
+    }
+
+
+    /*
+     * 获取相册详情
+     */
+    public function detail(Show $show): array
+    {
+        // 获取数据主键值
+        $id = $show->getKey();
+        $result = $this->execute('siteAlbumGet', [
+            'id' => $id,
+        ]);
+        $data = $result['data'] ?? [];
+        return $data;
+    }
+
+
+}

+ 58 - 0
app/Distributor/Repositories/RpcAlbumFolder.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace App\Distributor\Repositories;
+
+use App\Libraries\RpcClient;
+use App\Models\NullModel as Model;
+use Dcat\Admin\Repositories\EloquentRepository;
+use Dcat\Admin\Show;
+
+/*
+ * RPC调用相册
+ */
+
+class RpcAlbumFolder extends EloquentRepository
+{
+    /**
+     * Model.
+     *
+     * @var string
+     */
+    protected $eloquentClass = Model::class;
+
+    public $albumFolder = [];
+
+    public function __construct($modelOrRelations = [])
+    {
+        $this->initModel($modelOrRelations);
+        $distInfo = DistAdminDistributor::getInfo();
+        $albumFolder = empty($distInfo->album_folder) ? [] : json_decode($distInfo->album_folder,true);
+
+        $this->albumFolder = $albumFolder;
+    }
+
+    /*
+     * RPC调用
+     */
+    public function execute($method, $params = [])
+    {
+        return RpcClient::albumExecute($method, $params);
+    }
+
+    /*
+     * 获取相册文件夹列表
+     * $inIds 传入的相册ID数组,为空时获取全部相册
+     */
+    public static function selectOptions($lang = 'en') {
+        $self = new self();
+        $inIds = $self->albumFolder;
+        if ($lang == 'en') {
+            $result = $self->execute('siteAlbumFolderSelectOptionsEn', ['inIds' => $inIds]);
+        } else {
+            $result = $self->execute('siteAlbumFolderSelectOptions', ['inIds' => $inIds]);
+        }
+
+        $data = $result['data'] ?? [];
+        return $data;
+    }
+}

+ 10 - 0
app/Distributor/Repositories/SiteBanner.php

@@ -13,4 +13,14 @@ class SiteBanner extends EloquentRepository
      * @var string
      */
     protected $eloquentClass = Model::class;
+
+    /**
+     * 得到位置数据
+     * @param $id
+     * @return mixed
+     */
+    public static function getPositionOptions() {
+        $data = Model::query()->where('dist_id',getDistributorId())->select('position')->distinct()->get()->pluck('position', 'position');
+        return $data;
+    }
 }

+ 4 - 0
app/Distributor/routes.php

@@ -73,6 +73,10 @@ Route::group([
     $router->post('visual-editor/upload', 'VisualEditorController@upload');
     $router->post('visual-editor/preview-save', 'VisualEditorController@previewSave');
     $router->post('visual-editor/publish', 'VisualEditorController@publish');
+    //宣传资料
+    $router->get('site-album', 'SiteAlbumController@index');
+    $router->get('site-album/{id}', 'SiteAlbumController@show');
+
 });
 
 /*

+ 96 - 11
app/Libraries/CommonHelper.php

@@ -50,17 +50,6 @@ class CommonHelper
             }
 
             return $html;
-
-//
-//            //默认用等比例缩放
-//            $process = "?x-oss-process=image/resize,h_{$imgSize},m_lfit";
-//            $html = '<div style="display: flex; flex-wrap: wrap; gap: 5px;">';
-//            foreach ($images as $image) {
-//                $html .= "<div style='width: {$boxSize}px; height: {$boxSize}px; padding: 3px; border: 1px solid #ddd; border-radius: 3px; background-color: #f9f9f9; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); display: flex; align-items: center; justify-content: center;'>
-//                    <img  data-action='preview-img'   src='" . self::ossUrl($image).$process . "' style='max-width: 100%; max-height: 100%; object-fit: contain;'>
-//                  </div>";
-//            }
-//            $html .= '</div>';
         }
         return $html;
     }
@@ -80,6 +69,102 @@ class CommonHelper
         return "http://".env('OSS_BUCKET').'.'.env('OSS_ENDPOINT').'/'.$imageUrl;
     }
 
+
+
+    /*
+     * 显示视频
+     */
+    public static function displayVideo($items,$videoCover,$videoSrc,$boxSize=150)
+    {
+        $html = '';
+        if (is_array($items)) {
+            foreach ($items as $item) {
+                $item = (array) $item;
+                $cover = $item[$videoCover];
+                $src = $item[$videoSrc];
+                $videoUrl = CommonHelper::ossUrl($src);
+                $thumbnailUrl = CommonHelper::ossUrl($cover) ."?x-oss-process=image/resize,m_pad,h_{$boxSize},w_{$boxSize},color_ffffff";;
+                $html .= '<div class="video-container"><a href="#" class="playVideo" videoUrl="'.$videoUrl.'")"><img src="'.$thumbnailUrl.'" alt="Video Thumbnail"><div class="play-button"></div></a></div>';
+            }
+            $html .= '<div class="video-popup" id="videoPopup"><span class="close-btn">&times;</span> <iframe src="" frameborder="0" allowfullscreen></iframe></div>';
+        }
+        //视频播放CSS
+        Admin::style("
+        .video-container {
+            position: relative;
+            display: inline-block;
+        }
+        .video-container img {
+            height: 200px;
+            margin-right: 5px;
+            border: 1px solid #ececf1;
+        }
+        .play-button {
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            width: 50px;
+            height: 50px;
+            background-color: rgba(0, 0, 0, 0.7);
+            border-radius: 50%;
+            cursor: pointer;
+        }
+        .play-button::after {
+            content: '▶';
+            font-size: 30px;
+            color: white;
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+        }
+        .video-popup {
+            display: none;
+            position: fixed;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            background-color: white;
+            padding: 25px;
+            box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
+            z-index: 1000;
+        }
+        .video-popup iframe {
+            width: 800px;
+            height: 450px;
+        }
+        .close-btn {
+            position: absolute;
+            top: 0px;
+            right: 5px;
+            font-size: 24px;
+            color: #000;
+            cursor: pointer;
+        }
+        .close-btn:hover {
+            color: #f00;
+        }
+        ");
+
+        Admin::script("
+        $('.playVideo').on('click', function(e) {
+            e.preventDefault(); // 阻止默认跳转行为
+            var videoUrl = $(this).attr('videoUrl'); // 获取 videoUrl 属性
+            $('#videoPopup').css('display', 'block');
+            $('#videoPopup iframe').attr('src', videoUrl); // 设置 iframe 的 src
+        });
+
+
+        // 点击关闭按钮关闭视频
+        $('.close-btn').on('click', function() {
+            $('#videoPopup').css('display', 'none');
+            $('#videoPopup iframe').attr('src', ''); // 停止播放视频
+        });
+        ");
+        return $html; // 当没有数组数据时
+    }
+
     /*
      * 替换新增与编辑的url,在后边加上指定的参数 (适用于弹出框的新增与编辑)
      * $addButton = '.tree-quick-create';

+ 43 - 0
app/Libraries/RpcClient.php

@@ -0,0 +1,43 @@
+<?php
+
+// app/Libraries/CommonHelper.php
+namespace App\Libraries;
+
+use Dcat\Admin\Admin;
+use JsonRPC\Client;
+use Illuminate\Http\Request;
+use JsonRPC\HttpClient;
+
+class RpcClient
+{
+    // RPC超时时间
+    public static $timeout = 5;
+
+    /*
+     * RPC客户端
+     * @param $procedure string 远程过程调用名称
+     * @param $params array 远程过程调用参数
+     */
+    public static function albumExecute ($procedure, $params = []) {
+        $apiKey =  time();
+        $apiSecret = env('ALBUM_RPC_SECRET');
+        $payload = json_encode($params);
+        $payload = $payload.$apiKey;
+        $signature = hash_hmac('sha256', $payload, $apiSecret);
+        $clientHost = env('ALBUM_RPC_URL');
+        $httpClient = new HttpClient($clientHost);
+        $httpClient = $httpClient->withTimeout(self::$timeout);
+        $client = new Client($clientHost,false,$httpClient);
+        try {
+            $result = $client->execute( $procedure, $params,[], null,[
+                    'X-API-Key: '.$apiKey,
+                    'X-API-Signature: '.$signature,
+            ]);
+            return $result;
+        } catch (\Exception $e) {
+            return ['status'=>false,'msg'=>$e->getMessage(),'data'=>[]];
+        }
+    }
+
+}
+

+ 17 - 0
app/Models/BaseProduct.php

@@ -34,6 +34,23 @@ class BaseProduct extends Model
         return $this->hasOne(BaseProductCategory::class,'id','category_id');
     }
 
+    protected $fillable = [
+        'id',
+        'title',
+        'keywords',
+        'description',
+        'sku',
+        'category_id',
+        'issuance_date',
+        'order',
+        'enabled',
+        'content',
+        'parameters',
+        'created_at',
+        'updated_at',
+        'is_pinned',
+    ];
+
     // 一对多关联
     public function images()
     {

+ 2 - 0
app/Models/BaseProductImage.php

@@ -18,6 +18,8 @@ class BaseProductImage extends Model
         'image_url',
         'product_id',
         'order',
+        'created_at',
+        'updated_at',
     ];
 
     // 反向关联,属于某个产品

+ 4 - 0
app/Models/DistProduct.php

@@ -45,6 +45,8 @@ class DistProduct extends Model
         'seo_keywords',
         'seo_description',
         'slug',
+        'status',
+        'review_reply'
     ];
 
 
@@ -59,4 +61,6 @@ class DistProduct extends Model
         return $this->hasMany(DistProductImage::class, 'product_id')->orderBy('order', 'asc')->orderBy('id', 'asc');
     }
 
+
+
 }

+ 12 - 0
app/Models/NullModel.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace App\Models;
+
+use Dcat\Admin\Traits\HasDateTimeFormatter;
+use Illuminate\Database\Eloquent\Model;
+
+class NullModel extends Model
+{
+    use HasDateTimeFormatter;
+
+}

+ 1 - 1
app/helpers.php

@@ -249,7 +249,7 @@ if (!function_exists('generateSlug')) {
         // 2. 将空格替换为短横线(-)
         $slug = str_replace(' ', '-', $slug);
         // 3. 将不合法的字符(!@#$%^&*?=+)替换为空
-        $slug = preg_replace('/[!@#$%^&*()?=+]+/', '', $slug);
+        $slug = preg_replace('/[::^!@#$%^&*()?=+]+/', '', $slug);
         // 4. 清理多余的短横线
         $slug = preg_replace('/-+/', '-', $slug);
         // 5. 去除开头和结尾的短横线

+ 1 - 0
composer.json

@@ -8,6 +8,7 @@
         "php": "^8.0.2",
         "alphasnow/aliyun-oss-laravel": "^4.7",
         "dcat-plus/laravel-admin": "^1.2",
+        "fguillot/json-rpc": "^1.3",
         "guzzlehttp/guzzle": "^7.2",
         "laravel/framework": "^9.19",
         "laravel/sanctum": "^3.0",

+ 52 - 1
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "844cf77b80234d7e06f624e36d3e2e9c",
+    "content-hash": "e553ae528c395a7344fa7214a3e381b3",
     "packages": [
         {
             "name": "aliyuncs/oss-sdk-php",
@@ -1047,6 +1047,57 @@
             ],
             "time": "2023-10-06T06:47:41+00:00"
         },
+        {
+            "name": "fguillot/json-rpc",
+            "version": "v1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/matasarei/json-rpc.git",
+                "reference": "85112c916c3c55f7e899c827550c52e9edb4928a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/matasarei/json-rpc/zipball/85112c916c3c55f7e899c827550c52e9edb4928a",
+                "reference": "85112c916c3c55f7e899c827550c52e9edb4928a",
+                "shasum": "",
+                "mirrors": [
+                    {
+                        "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+                        "preferred": true
+                    }
+                ]
+            },
+            "require": {
+                "ext-json": "*",
+                "php": ">=7.4"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.6",
+                "squizlabs/php_codesniffer": "^3.9"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "JsonRPC": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Frédéric Guillot"
+                }
+            ],
+            "description": "Simple JSON-RPC client/server library that just works",
+            "homepage": "https://github.com/matasarei/JsonRPC",
+            "support": {
+                "issues": "https://github.com/matasarei/json-rpc/issues",
+                "source": "https://github.com/matasarei/json-rpc/tree/v1.3.0"
+            },
+            "time": "2024-06-08T19:21:47+00:00"
+        },
         {
             "name": "fruitcake/php-cors",
             "version": "v1.3.0",

+ 6 - 0
config/dictionary.php

@@ -97,4 +97,10 @@ return [
     'landing_page_contact_us_template' => 'pages_sp_contact.liquid',
 
 
+    'dist_product_status' => [
+        -1 => 'approval_failed',
+        0 => 'draft',
+        1 => 'pending_approval',
+        2 => 'published',
+    ],
 ];

+ 31 - 0
database/migrations/2025_01_10_085440_update_dist_admin_distributor_table.php

@@ -0,0 +1,31 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        //
+        Schema::table('dist_admin_distributor', function (Blueprint $table) {
+            $table->string('album_folder',1000);
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        //
+    }
+};

+ 32 - 0
database/migrations/2025_02_08_030746_update_table_v001_1.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        //供应商增加新建产品增加审核
+        Schema::table('dist_product', function (Blueprint $table) {
+            $table->tinyInteger('status')->default('1')->comment('状态 0待审核 1审核中 2正常 -1审核失败 ');
+            $table->string('review_reply', 100)->nullable()->comment('审核回复');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        //
+    }
+};

+ 31 - 0
database/migrations/2025_03_05_023833_update_site_menu.php

@@ -0,0 +1,31 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        //菜单增加图片字段
+        Schema::table('site_menu', function (Blueprint $table) {
+            $table->string('image_url', 255)->nullable()->comment('菜单图片');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        //
+    }
+};

+ 1 - 0
lang/en/admin.php

@@ -258,4 +258,5 @@ return [
     'inquiry_list' => 'Inquiry List',
     'banner_list' => 'Banner List',
     'crop_the_image' => 'Crop the Image',
+    'promotional_materials' => 'Promotional Materials',
 ];

+ 19 - 0
lang/en/global.php

@@ -93,6 +93,20 @@ return [
         'target_type'           => 'Type',
         'target_ids'            => 'Target Ids',
         'message_title'         => 'Message Title',
+        'model'                 => 'Model',
+        'missing_content'       => 'Missing Content',
+        'cover'                 => 'Cover',
+        'en_detail'             => 'English Detail',
+        'cn_detail'             => 'Chinese Detail',
+        'video'                 => 'Video',
+        'poster'                => 'Poster',
+        'cert'                  => 'Cert',
+        'pdf'                   => 'PDF',
+        'pdf_title'             => 'PDF Title',
+        'pdf_src'               => 'PDF Src',
+        'video_src'             => 'Video Src',
+        'review_reply'          => 'Review Reply',
+        'title_en'              => 'Title',
     ],
     'labels' => [
         'list'                  => 'List',
@@ -189,6 +203,11 @@ return [
         'visual_edit'           => 'Visual Edit',
         'top_menu'              => 'Top Menu',
         'bottom_menu'           => 'Bottom Menu',
+        'album_folder'          => 'Album Folder',
+        'approval_failed'       => 'Approval Failed',
+        'pending_approval'      => 'Pending Approval',
+        'distributor_name'      => 'Distributor Name',
+        'product_audit'         => 'Product Audit',
     ],
     'options' => [
         //

+ 3 - 1
lang/zh_CN/admin.php

@@ -258,5 +258,7 @@ return [
     'banner_list' => '轮播图列表',
     'landing_page' => '独立页',
     'messages' => '消息',
-    'crop_the_image' => '裁剪图片'
+    'crop_the_image' => '裁剪图片',
+    'promotional_materials' => '宣传资料',
+    'save' => '保存',
 ];

+ 21 - 0
lang/zh_CN/global.php

@@ -99,6 +99,21 @@ return [
         'video_category' => '视频分类',
         'is_read' => '是否已读',
         'time' => '时间',
+        'model'                 => '型号',
+        'missing_content'       => '缺少内容',
+        'cover'                 => '主图',
+        'en_detail'             => '英文详情',
+        'cn_detail'             => '中文详情',
+        'video'                 => '视频',
+        'poster'                => '海报',
+        'cert'                  => '证书',
+        'pdf'                   => 'PDF',
+        'pdf_title'             => 'PDF标题',
+        'pdf_src'               => 'PDF文件',
+        'video_src'             => '视频文件',
+        'review_reply'          => '回复',
+        'title_en'              => '标题',
+
     ],
     'labels' => [
         'list'         => '列表',
@@ -196,6 +211,12 @@ return [
         'visual_edit'           => '可视化编辑',
         'top_menu'              => '顶部菜单',
         'bottom_menu'           => '底部菜单',
+        'album_folder'          => '相册分类',
+        'approval_failed'       => '审批失败',
+        'pending_approval'      => '待审批',
+        'distributor_name'      => '分销商名称',
+        'audit_success'      => '审核成功',
+        'product_audit'      => '产品审核',
     ],
     'options' => [
         //

+ 2 - 0
lang/zh_CN/menu.php

@@ -53,5 +53,7 @@ return [
         'landing_page' => '独立页',
         'messages' => '消息',
         'banner'  =>'轮播图',
+        'promotional_materials'  =>'宣传资料',
+        'product_audit'  =>'产品审核',
     ],
 ];

+ 31 - 1
public/vendor/grapes/grapes.init.js

@@ -228,8 +228,9 @@ editor.on('component:selected', (comp) => {
         selectedComponent.set({
             toolbar: [
             ]
+
         });
-    } else {
+    }  else {
         selectedComponent.set({
             toolbar: [
             ]
@@ -246,6 +247,35 @@ editor.on('component:selected', (comp) => {
         });
     }
 
+    if (selectedComponent.get('attributes').mtb_toolbar != '' && selectedComponent.get('attributes').mtb_toolbar != undefined) {
+        const mtbToolbar = selectedComponent.get('attributes').mtb_toolbar;
+        const safeActions = mtbToolbar.split(',');
+
+        console.log(safeActions);
+
+        let inputToolbar = [];
+        if (safeActions.includes('clone')) {
+            // 执行克隆相关逻辑
+            inputToolbar.push({
+                    attributes: { class: 'fa fa-clone' },
+                    command: 'tlb-clone',
+                    title: 'Clone',
+            });
+        }
+        if (safeActions.includes('delete')) {
+            // 执行删除相关逻辑
+            inputToolbar.push({
+                        attributes: { class: 'fa fa-trash' },
+                        command: 'tlb-delete',
+                        title: 'Delete',
+            });
+        }
+
+        selectedComponent.set({
+            toolbar: inputToolbar
+        });
+    }
+
 
     // 双击图片时弹出图片管理器
     if (selectedComponent.attributes.tagName == 'img') {

+ 1 - 1
resources/views/admin/form/footer.blade.php

@@ -21,4 +21,4 @@
         </div>
         @endif
     </div>
-</div>
+</div>

+ 11 - 10
resources/views/admin/pages-custom/ace.blade.php

@@ -282,7 +282,7 @@
 
         var changeIframeUrl = function() {
             appearance_id = $('select[name="appearance_id"]').val();
-            dist_id = $('input[name="dist_id"]').val();
+            dist_id = $('select[name="dist_id"]').val();
             var iframeUrl = '/prime-control/dist-template-var?templateCode=' + fileId + '&appearanceId=' + appearance_id+'&distId=' + dist_id;
             $('#iframeModal iframe').attr('src', iframeUrl);
         }
@@ -302,7 +302,7 @@
                     shade: [0.5, '#000'] // 设置遮罩层
                 });
                 var appearance_id = $('select[name="appearance_id"]').val();
-                var dist_id = $('input[name="dist_id"]').val();
+                var dist_id = $('select[name="dist_id"]').val();
                 fileId = $(this).attr('file_id');
                 //得到文件名
                 fileName = $(this).html();
@@ -337,7 +337,7 @@
         // 加载代码树
         var postData = function () {
             var appearance_id = $('select[name="appearance_id"]').val();
-            var dist_id = $('input[name="dist_id"]').val();
+            var dist_id = $('select[name="dist_id"]').val();
             //改变iframe的url
             changeIframeUrl();
             $.ajax({
@@ -373,7 +373,7 @@
             postData();
         });
         //分销商选择
-        $('input[name="dist_id"]').change(function() {
+        $('select[name="dist_id"]').change(function() {
             //console.log('-------0-----')
             loadingIndex = layer.load(1, {
                 shade: [0.5, '#000'] // 设置遮罩层
@@ -413,7 +413,8 @@
         // 模版编辑保存
         $("#save-btn").click(function() {
             var appearance_id = $('select[name="appearance_id"]').val();
-            var dist_id = $('input[name="dist_id"]').val();
+            var dist_id = $('select[name="dist_id"]').val();
+            // console.log(dist_id);
             var template_id = $("#template_id").val();
             var content = editor.getValue();
             if (template_id == '') {
@@ -452,7 +453,7 @@
         // 模版发布
         $("#publish-btn").click(function() {
             var appearance_id = $('select[name="appearance_id"]').val();
-            var dist_id = $('input[name="dist_id"]').val();
+            var dist_id = $('select[name="dist_id"]').val();
 
             if (appearance_id == '') {
                 Dcat.error('操作失败:请先选择主题');
@@ -516,7 +517,7 @@
             var filePathName = $('#file_path_name').val();
             var fileInputName = $('#file_name_input').val();
             var appearanceId = $('select[name="appearance_id"]').val();
-            var distId = $('input[name="dist_id"]').val();
+            var distId = $('select[name="dist_id"]').val();
             if (fileInputName == '') {
                 Dcat.error('文件名不能为空');
                 return false;
@@ -533,7 +534,7 @@
                     var filePathName = "";
                     var fileInputName = "";
                     var appearanceId = $('select[name="appearance_id"]').val();
-                    var distId = $('input[name="dist_id"]').val();
+                    var distId = $('select[name="dist_id"]').val();
                     var fileId = $(this).attr('file_id');
                     var fileName = $(this).attr('file_name');
                     msg = '确定要删除'+fileName+'吗?';
@@ -551,7 +552,7 @@
                 shade: [0.5, '#000'] // 设置遮罩层
             });
             var appearanceId = $('select[name="appearance_id"]').val();
-            var distId = $('input[name="dist_id"]').val();
+            var distId = $('select[name="dist_id"]').val();
             if (fileId == '') {
                 Dcat.error('操作失败:请先选择模板');
                 return false;
@@ -604,7 +605,7 @@
             //
             var logId = $(this).attr('log_id');
             var appearanceId = $('select[name="appearance_id"]').val();
-            var distId = $('input[name="dist_id"]').val();
+            var distId = $('select[name="dist_id"]').val();
             $.ajax({
                 url: '/prime-control/dist-template/ace',
                 method: 'POST',

+ 51 - 37
resources/views/admin/pages/login.blade.php

@@ -12,6 +12,7 @@
     .login-btn {
         padding-left: 2rem!important;;
         padding-right: 1.5rem!important;
+        width: 100%;
     }
     .content {
         overflow-x: hidden;
@@ -19,6 +20,18 @@
     .form-group .control-label {
         text-align: left;
     }
+    .justify-content-between {
+        margin-top: 1rem;
+    }
+    #captcha {
+        width: 185px;
+        float: left;
+    }
+    .captcha-img {
+        float: right;
+        width: 110px;
+        height: 34px;
+    }
 </style>
 
 <div class="login-page bg-40">
@@ -28,7 +41,7 @@
         </div>
         <div class="card">
             <div class="card-body login-card-body shadow-100">
-{{--                <p class="login-box-msg mt-1 mb-1">{{ __('admin.welcome_back') }}</p>--}}
+                {{--                <p class="login-box-msg mt-1 mb-1">{{ __('admin.welcome_back') }}</p>--}}
 
                 <form id="login-form" method="POST" action="{{ admin_url('auth/login') }}">
 
@@ -36,13 +49,13 @@
 
                     <fieldset class="form-label-group form-group position-relative has-icon-left">
                         <input
-                                type="text"
-                                class="form-control {{ $errors->has('username') ? 'is-invalid' : '' }}"
-                                name="username"
-                                placeholder="{{ trans('admin.username') }}"
-                                value="{{ old('username') }}"
-                                required
-                                autofocus
+                            type="text"
+                            class="form-control {{ $errors->has('username') ? 'is-invalid' : '' }}"
+                            name="username"
+                            placeholder="{{ trans('admin.username') }}"
+                            value="{{ old('username') }}"
+                            required
+                            autofocus
                         >
 
                         <div class="form-control-position">
@@ -63,15 +76,15 @@
 
                     <fieldset class="form-label-group form-group position-relative has-icon-left">
                         <input
-                                minlength="5"
-                                maxlength="20"
-                                id="password"
-                                type="password"
-                                class="form-control {{ $errors->has('password') ? 'is-invalid' : '' }}"
-                                name="password"
-                                placeholder="{{ trans('admin.password') }}"
-                                required
-                                autocomplete="current-password"
+                            minlength="5"
+                            maxlength="20"
+                            id="password"
+                            type="password"
+                            class="form-control {{ $errors->has('password') ? 'is-invalid' : '' }}"
+                            name="password"
+                            placeholder="{{ trans('admin.password') }}"
+                            required
+                            autocomplete="current-password"
                         >
 
                         <div class="form-control-position">
@@ -100,6 +113,7 @@
                             placeholder="{{ trans('admin.captcha') }}"
                             required
                         >
+                        <img src="/prime-control/captcha?{{ time() }}" alt="captcha" class="captcha-img" onclick="this.src='/prime-control/captcha?'+Math.random()">
 
                         <div class="form-control-position">
                             <i class="feather icon-image"></i>
@@ -114,39 +128,39 @@
                                 @endforeach
                             </span>
                         @endif
-                        <img src="/prime-control/captcha?{{ time() }}" alt="captcha" class="captcha-img" onclick="this.src='/prime-control/captcha?'+Math.random()">
+
                     </fieldset>
 
+                    <button type="submit" class="btn btn-primary float-letf login-btn">
+                        {{ __('admin.login') }}
+                        <i class="feather icon-arrow-right"></i>
+                    </button>
+
                     <div class="form-group d-flex justify-content-between align-items-center">
                         <div class="text-left">
                             @if(config('admin.auth.remember'))
-                            <fieldset class="checkbox">
-                                <div class="vs-checkbox-con vs-checkbox-primary">
-                                    <input id="remember" name="remember"  value="1" type="checkbox" {{ old('remember') ? 'checked' : '' }}>
-                                    <span class="vs-checkbox">
+                                <fieldset class="checkbox">
+                                    <div class="vs-checkbox-con vs-checkbox-primary">
+                                        <input id="remember" name="remember"  value="1" type="checkbox" {{ old('remember') ? 'checked' : '' }}>
+                                        <span class="vs-checkbox">
                                                         <span class="vs-checkbox--check">
                                                           <i class="vs-icon feather icon-check"></i>
                                                         </span>
                                                     </span>
-                                    <span> {{ trans('admin.remember_me') }}</span>
-                                </div>
-                            </fieldset>
+                                        <span> {{ trans('admin.remember_me') }}</span>
+                                    </div>
+                                </fieldset>
                             @endif
                         </div>
                     </div>
+
                     <div class="form-group d-flex justify-content-between align-items-center">
                         <div class="text-left">
                             <a class="lang-item" href="javascript:void(0);" data-lang="en">English</a>
-                           / <a class="lang-item" href="javascript:void(0);" data-lang="zh_CN">中文</a>
+                            / <a class="lang-item" href="javascript:void(0);" data-lang="zh_CN">中文</a>
                         </div>
                     </div>
 
-                    <button type="submit" class="btn btn-primary float-right login-btn">
-
-                        {{ __('admin.login') }}
-                        &nbsp;
-                        <i class="feather icon-arrow-right"></i>
-                    </button>
                 </form>
 
             </div>
@@ -155,12 +169,12 @@
 </div>
 
 <script>
-Dcat.ready(function () {
-    // ajax表单提交
-    $('#login-form').form({
-        validate: true,
+    Dcat.ready(function () {
+        // ajax表单提交
+        $('#login-form').form({
+            validate: true,
+        });
     });
-});
 </script>
 <script>
     document.querySelectorAll('.lang-item').forEach(item => {

+ 50 - 0
resources/views/admin/partials_custom/menu.blade.php

@@ -0,0 +1,50 @@
+@php
+    $depth = $item['depth'] ?? 0;
+
+    $horizontal = config('admin.layout.horizontal_menu');
+
+    $defaultIcon = config('admin.menu.default_icon', 'feather icon-circle');
+
+    $num = \App\Admin\Repositories\DistInquiry::getProcessingNum();
+@endphp
+
+@if($builder->visible($item))
+    @if(empty($item['children']))
+        <li class="nav-item">
+            <a data-id="{{ $item['id'] ?? '' }}" @if(mb_strpos($item['uri'], '://') !== false) target="_blank" @endif
+               href="{{ $builder->getUrl($item['uri']) }}"
+               class="nav-link {!! $builder->isActive($item) ? 'active' : '' !!}">
+                {!! str_repeat('&nbsp;', $depth) !!}<i class="fa fa-fw {{ $item['icon'] ?: $defaultIcon }}"></i>
+                <p>
+                    {!! $builder->translate($item['title']) !!}
+                </p>
+                @if($item['title'] == 'Inquiry' && $num > 0) <i class="badge badge-danger">{{$num}}</i>@endif
+            </a>
+        </li>
+    @else
+
+        <li class="{{ $horizontal ? 'dropdown' : 'has-treeview' }} {{ $depth > 0 ? 'dropdown-submenu' : '' }} nav-item {{ $builder->isActive($item) ? 'menu-open' : '' }}">
+            <a href="#"  data-id="{{ $item['id'] ?? '' }}"
+               class="nav-link {{ $builder->isActive($item) ? ($horizontal ? 'active' : '') : '' }}
+                    {{ $horizontal ? 'dropdown-toggle' : '' }}">
+                {!! str_repeat('&nbsp;', $depth) !!}<i class="fa fa-fw {{ $item['icon'] ?: $defaultIcon }}"></i>
+                <p>
+                    {!! $builder->translate($item['title']) !!}
+
+                    @if(! $horizontal)
+                        <i class="right fa fa-angle-left"></i>
+                    @endif
+                </p>
+            </a>
+            <ul class="nav {{ $horizontal ? 'dropdown-menu' : 'nav-treeview' }}">
+                @foreach($item['children'] as $item)
+                    @php
+                        $item['depth'] = $depth + 1;
+                    @endphp
+
+                    @include('admin::partials.menu', ['item' => $item])
+                @endforeach
+            </ul>
+        </li>
+    @endif
+@endif

+ 1 - 1
resources/views/distributor/form/footer.blade.php

@@ -21,4 +21,4 @@
         </div>
         @endif
     </div>
-</div>
+</div>

+ 35 - 0
resources/views/distributor/form_custom/review-footer.blade.php

@@ -0,0 +1,35 @@
+<div class="box-footer">
+
+    <div class="col-md-{{$width['label']}} d-md-block" style="display: none"></div>
+
+    <div class="col-md-{{$width['field']}}">
+
+        @if(! empty($buttons['submit']))
+            <div class="pull-right">
+                @if ($isCreating == 0)
+                <button  class="btn btn-primary submit" name="action" value="save" ><i class="feather icon-save"></i> {{ trans('admin.save') }}</button>
+
+                <button  class="btn btn-success submit" style="margin-left: 10px" name="action" value="submit" ><i class="feather icon-chevron-right"></i> {{ trans('admin.submit') }}</button>
+                @else
+                    @if ($status != 1)
+                    <button  class="btn btn-primary submit" name="action" value="save" ><i class="feather icon-save"></i> {{ trans('admin.save') }}</button>
+                    @endif
+                    @if ($status == 0 || $status == -1)
+                        <button  class="btn btn-success submit" style="margin-left: 10px" name="action" value="submit" ><i class="feather icon-chevron-right"></i> {{ trans('admin.submit') }}</button>
+                    @endif
+                @endif
+            </div>
+
+            @if($checkboxes)
+                <div class="pull-right d-md-flex" style="margin:10px 15px 0 0;display: none">{!! $checkboxes !!}</div>
+            @endif
+
+        @endif
+
+        @if(! empty($buttons['reset']))
+        <div class="btn-group pull-left">
+            <button type="reset" class="btn btn-white"><i class="feather icon-rotate-ccw"></i> {{ trans('admin.reset') }}</button>
+        </div>
+        @endif
+    </div>
+</div>

+ 51 - 38
resources/views/distributor/pages/login.blade.php

@@ -12,6 +12,7 @@
     .login-btn {
         padding-left: 2rem!important;;
         padding-right: 1.5rem!important;
+        width: 100%;
     }
     .content {
         overflow-x: hidden;
@@ -19,6 +20,18 @@
     .form-group .control-label {
         text-align: left;
     }
+    .justify-content-between {
+        margin-top: 1rem;
+    }
+    #captcha {
+        width: 185px;
+        float: left;
+    }
+    .captcha-img {
+        float: right;
+        width: 110px;
+        height: 34px;
+    }
 </style>
 
 <div class="login-page bg-40">
@@ -28,7 +41,7 @@
         </div>
         <div class="card">
             <div class="card-body login-card-body shadow-100">
-{{--                <p class="login-box-msg mt-1 mb-1">{{ __('admin.welcome_back') }}</p>--}}
+                {{--                <p class="login-box-msg mt-1 mb-1">{{ __('admin.welcome_back') }}</p>--}}
 
                 <form id="login-form" method="POST" action="{{ admin_url('auth/login') }}">
 
@@ -36,13 +49,13 @@
 
                     <fieldset class="form-label-group form-group position-relative has-icon-left">
                         <input
-                                type="text"
-                                class="form-control {{ $errors->has('username') ? 'is-invalid' : '' }}"
-                                name="username"
-                                placeholder="{{ trans('admin.username') }}"
-                                value="{{ old('username') }}"
-                                required
-                                autofocus
+                            type="text"
+                            class="form-control {{ $errors->has('username') ? 'is-invalid' : '' }}"
+                            name="username"
+                            placeholder="{{ trans('admin.username') }}"
+                            value="{{ old('username') }}"
+                            required
+                            autofocus
                         >
 
                         <div class="form-control-position">
@@ -63,15 +76,15 @@
 
                     <fieldset class="form-label-group form-group position-relative has-icon-left">
                         <input
-                                minlength="5"
-                                maxlength="20"
-                                id="password"
-                                type="password"
-                                class="form-control {{ $errors->has('password') ? 'is-invalid' : '' }}"
-                                name="password"
-                                placeholder="{{ trans('admin.password') }}"
-                                required
-                                autocomplete="current-password"
+                            minlength="5"
+                            maxlength="20"
+                            id="password"
+                            type="password"
+                            class="form-control {{ $errors->has('password') ? 'is-invalid' : '' }}"
+                            name="password"
+                            placeholder="{{ trans('admin.password') }}"
+                            required
+                            autocomplete="current-password"
                         >
 
                         <div class="form-control-position">
@@ -89,7 +102,6 @@
                         @endif
 
                     </fieldset>
-
                     <fieldset class="form-label-group form-group position-relative has-icon-left">
                         <input
                             minlength="6"
@@ -101,6 +113,7 @@
                             placeholder="{{ trans('admin.captcha') }}"
                             required
                         >
+                        <img src="/dist/captcha?{{ time() }}" alt="captcha" class="captcha-img" onclick="this.src='/dist/captcha?'+Math.random()">
 
                         <div class="form-control-position">
                             <i class="feather icon-image"></i>
@@ -115,38 +128,39 @@
                                 @endforeach
                             </span>
                         @endif
-                        <img src="/dist/captcha?{{ time() }}" alt="captcha" class="captcha-img" onclick="this.src='/dist/captcha?'+Math.random()">
+
                     </fieldset>
 
-              <div class="form-group d-flex justify-content-between align-items-center">
+                    <button type="submit" class="btn btn-primary float-letf login-btn">
+                        {{ __('admin.login') }}
+                        <i class="feather icon-arrow-right"></i>
+                    </button>
+
+                    <div class="form-group d-flex justify-content-between align-items-center">
                         <div class="text-left">
                             @if(config('admin.auth.remember'))
-                            <fieldset class="checkbox">
-                                <div class="vs-checkbox-con vs-checkbox-primary">
-                                    <input id="remember" name="remember"  value="1" type="checkbox" {{ old('remember') ? 'checked' : '' }}>
-                                    <span class="vs-checkbox">
+                                <fieldset class="checkbox">
+                                    <div class="vs-checkbox-con vs-checkbox-primary">
+                                        <input id="remember" name="remember"  value="1" type="checkbox" {{ old('remember') ? 'checked' : '' }}>
+                                        <span class="vs-checkbox">
                                                         <span class="vs-checkbox--check">
                                                           <i class="vs-icon feather icon-check"></i>
                                                         </span>
                                                     </span>
-                                    <span> {{ trans('admin.remember_me') }}</span>
-                                </div>
-                            </fieldset>
+                                        <span> {{ trans('admin.remember_me') }}</span>
+                                    </div>
+                                </fieldset>
                             @endif
                         </div>
                     </div>
+
                     <div class="form-group d-flex justify-content-between align-items-center">
                         <div class="text-left">
                             <a class="lang-item" href="javascript:void(0);" data-lang="en">English</a>
                             / <a class="lang-item" href="javascript:void(0);" data-lang="zh_CN">中文</a>
                         </div>
                     </div>
-                    <button type="submit" class="btn btn-primary float-right login-btn">
 
-                        {{ __('admin.login') }}
-                        &nbsp;
-                        <i class="feather icon-arrow-right"></i>
-                    </button>
                 </form>
 
             </div>
@@ -155,14 +169,13 @@
 </div>
 
 <script>
-Dcat.ready(function () {
-    // ajax表单提交
-    $('#login-form').form({
-        validate: true,
+    Dcat.ready(function () {
+        // ajax表单提交
+        $('#login-form').form({
+            validate: true,
+        });
     });
-});
 </script>
-
 <script>
     document.querySelectorAll('.lang-item').forEach(item => {
         item.addEventListener('click', function() {