Merge branch 'github-master' into master-github-histories
# Conflicts: # beike/Admin/View/Components/Header.php # beike/Admin/View/Components/Sidebar.php # beike/Helpers.php # beike/Models/Product.php # beike/Repositories/OrderRepo.php # beike/Repositories/PageCategoryRepo.php # beike/Shop/Services/CartService.php # composer.lock # resources/beike/admin/views/components/header.blade.php # resources/beike/admin/views/pages/design/builder/index.blade.php # resources/beike/admin/views/pages/marketing/show.blade.php # resources/beike/admin/views/pages/products/form/form.blade.php # resources/lang/en/admin/marketing.php # resources/lang/zh_cn/admin/common.php # resources/lang/zh_cn/admin/marketing.php # resources/lang/zh_cn/admin/product.php # themes/default/cart/mini.blade.php # themes/default/layout/footer.blade.php # themes/default/layout/master.blade.php # themes/default/product/product.blade.php
This commit is contained in:
commit
f344d2deab
|
|
@ -56,8 +56,6 @@ PUSHER_APP_CLUSTER=mt1
|
|||
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
|
||||
# 前端热更新, PROXY: vhost or path, HOST: 多设备实时
|
||||
MIX_PROXY=beikeshop.test
|
||||
MIX_HOST=192.168.0.122
|
||||
|
||||
# Dusk Browser Test
|
||||
DUSK_START_MAXIMIZED=true
|
||||
DUSK_HEADLESS_DISABLED=false
|
||||
|
|
|
|||
47
README.md
47
README.md
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
|
||||
## BeikeShop介绍
|
||||
BeikeShop 是基于 Laravel 开发的一款开源商城系统,主要面向外贸,跨境行业提供的商品管理、订单管理、会员管理、支付、物流、系统管理等功能。
|
||||
<br>
|
||||
BeikeShop 是基于 Laravel 开发的一款开源商城系统
|
||||
主要面向外贸,跨境行业提供的商品管理、订单管理、会员管理、支付、物流、系统管理等功能
|
||||
<br>
|
||||
|
||||
|
||||
|
|
@ -14,17 +14,22 @@ BeikeShop 是基于 Laravel 开发的一款开源商城系统,主要面向外
|
|||
<img src="https://beikeshop.com/readme/README-1.png">
|
||||
</a>
|
||||
|
||||
请保留我公司版权信息,如要移除,需要我公司授权。
|
||||
注意:请保留我公司版权信息,如要移除,需要我公司授权!
|
||||
|
||||
|
||||
## BeikeShop系统亮点
|
||||
- **全新!支持ChatGPT:BeikeShop1.3.2 集成ChatGPT,打造高效智能化跨境电商系统**
|
||||
- 开源免费:BeikeShop以开源方式发布,OSL-3.0协议允许用户自由使用、修改。
|
||||
- 轻量级框架:BeikeShop基于Laravel框架开发,具有高效、灵活、易用等特点。
|
||||
- 灵活的插件机制:BeikeShop提供灵活的Hook插件机制,可以方便地扩展和定制系统功能。
|
||||
- 多语言多货币支持:支持多种语言/货币切换,可灵活扩展其他语言/货币。
|
||||
- 多种支付方式:支持 PayPal、Stripe、Alipay、WeChat Pay 等多种支付方式,覆盖全球主要外贸国家
|
||||
- 快速搭建:向VIP提供一键安装脚本,帮助用户快速搭建起一个稳定、高效、易用的跨境电商独立站。
|
||||
- 0元起步:BeikeShop 是真正的独立站,代码100%开源,数据信息100%自主可控
|
||||
- 基于 Laravel 9 框架:BeikeShop 使用 Laravel 9 框架进行开发,拥有成熟的框架支持
|
||||
- 无佣金和手续费:BeikeShop 没有佣金、年费或手续费,降低了建站成本
|
||||
- 微内核和插件化:采用微内核架构和插件化设计,使系统易维护 & 扩展
|
||||
- 清晰的代码分层和格式规范:系统代码采用分层清晰、格式规范的结构,提高代码的可读性和可维护性
|
||||
- Event 机制实现 Hook 功能:通过 Event 机制实现了灵活的 Hook 功能,方便扩展和定制化开发
|
||||
- 丰富的插件市场:官方提供了丰富的插件市场,可以方便地购买需要的功能
|
||||
- 多语言和多货币支持:系统支持多语言和多货币,方便面向不同地区和国家的用户
|
||||
- 界面美观和可视化装修:系统界面设计美观,支持可视化装修,提供良好的用户体验
|
||||
- 严格遵循 MVC 架构:系统严格遵循 MVC 架构,提高了代码的可维护性和可扩展性
|
||||
- 操作简单易上手:BeikeShop 操作简单,易于上手,可以快速上线使用
|
||||
|
||||

|
||||
|
||||
<br>
|
||||
|
|
@ -38,27 +43,32 @@ BeikeShop 是基于 Laravel 开发的一款开源商城系统,主要面向外
|
|||
</p>
|
||||
|
||||
|
||||
|
||||
<a href="https://demo.beikeshop.com/" target="_blank">点击立刻体验:BeikeShop演示站</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## 页面展示
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## 技术服务展示
|
||||

|
||||
|
||||
## 软件架构
|
||||
PHP语言开发,基于 Laravel 框架,前端 Blade 模板 + Vue
|
||||
使用语言 PHP 8.0
|
||||
基于 Laravel 9 框架
|
||||
前端 Blade 模板 + Vue
|
||||
|
||||
## 环境要求
|
||||
- 独立服务器(不能使用虚拟空间)
|
||||
- CentOS 7.0+ 或 Ubuntu 20.04+
|
||||
- PHP 8.0.2+
|
||||
- MySQL 5.7+
|
||||
- Apache httpd 2.4+ 或者 Nginx 1.10+
|
||||
|
|
@ -89,13 +99,13 @@ PHP语言开发,基于 Laravel 框架,前端 Blade 模板 + Vue
|
|||
1. 将解压文件夹下的 public 设置为网站根目录
|
||||
1. 通过浏览器访问网站根据提示完成安装
|
||||
1. <a href="https://docs.beikeshop.com/install/bt.html" target="_blank">BeikeShop详细安装指引</a>
|
||||
1. 如需升级请下载最新版覆盖到服务器后网站根目录运行`composer install && php artisan migrate`
|
||||
1. 如需升级, 请下载最新版覆盖到服务器(必须保留原有.env文件), 然后在网站根目录运行`php artisan migrate`
|
||||
|
||||
## 安装教程(面向开发者)
|
||||
1. 打开命令行克隆代码 `git clone https://gitee.com/beikeshop/beikeshop.git`
|
||||
1. 命令行进入 `beikeshop` 目录, 执行 `composer install` 安装第三方包
|
||||
1. 接着执行 `cp .env.example .env` 创建配置文件
|
||||
1. 接着执行 `npm install`(node 版本需16+) 以及 `npm run dev` 编译前端 js 和 css 文件
|
||||
1. 接着执行 `npm install`(node 版本需16+) 以及 `npm run prod` 编译前端 js 和 css 文件
|
||||
1. 将项目文件夹下的 `public` 设置为网站根目录
|
||||
1. 通过浏览器访问网站, 根据提示完成安装
|
||||
1. 如需升级请在服务器端网站根目录运行`git pull && composer install && php artisan migrate`
|
||||
|
|
@ -106,5 +116,10 @@ PHP语言开发,基于 Laravel 框架,前端 Blade 模板 + Vue
|
|||
1. 提交代码
|
||||
1. 新建 Merge Request
|
||||
|
||||
## 特别鸣谢
|
||||
插件开发者:撸串青年、Aegis
|
||||
PR贡献者:nilsir、what_村长、tanxiaoyong、Lucky、So、licy、老北、Teemo
|
||||
感谢你们参与到BeikeShop的开发中,共同为BeikeShop添砖加瓦!
|
||||
|
||||
## QQ交流群:
|
||||
群1: 639108380
|
||||
|
|
|
|||
|
|
@ -64,11 +64,18 @@ class Kernel extends HttpKernel
|
|||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
||||
'throttle:api',
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'admin_api' => [
|
||||
\App\Http\Middleware\AdminApiAuthenticate::class,
|
||||
'throttle:api',
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
/**
|
||||
* AdminAuthenticate.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-04-20 14:44:54
|
||||
* @modified 2023-04-20 14:44:54
|
||||
*/
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Beike\Repositories\AdminUserTokenRepo;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Validation\UnauthorizedException;
|
||||
|
||||
class AdminApiAuthenticate
|
||||
{
|
||||
public const ADMIN_API_PREFIX = 'admin_api.';
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, \Closure $next)
|
||||
{
|
||||
$token = $request->header('token');
|
||||
if (empty($token)) {
|
||||
$token = $request->get('token');
|
||||
}
|
||||
|
||||
$token = AdminUserTokenRepo::getAdminUserTokenByToken($token);
|
||||
if (empty($token)) {
|
||||
throw new UnauthorizedException(trans('customer.unauthorized_without_token'));
|
||||
}
|
||||
|
||||
$adminUser = $token->adminUser;
|
||||
if (! $this->hasPermission($adminUser)) {
|
||||
throw new UnauthorizedException(trans('customer.unauthorized_without_permission'));
|
||||
}
|
||||
|
||||
register('admin_user', $adminUser);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
private function hasPermission($adminUser)
|
||||
{
|
||||
// $routeUri = Route::current()->uri();
|
||||
$routeName = Route::currentRouteName();
|
||||
|
||||
$routePath = str_replace(self::ADMIN_API_PREFIX, '', $routeName);
|
||||
if ($routePath == 'me') {
|
||||
return true;
|
||||
}
|
||||
|
||||
$permissionName = $this->mapPermissionByRoute($routePath);
|
||||
if (empty($permissionName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $adminUser->can($permissionName);
|
||||
}
|
||||
|
||||
private function mapPermissionByRoute($routePath)
|
||||
{
|
||||
$maps = [
|
||||
'categories.index' => 'categories_index',
|
||||
'categories.show' => 'categories_show',
|
||||
'categories.create' => 'categories_create',
|
||||
'categories.update' => 'categories_update',
|
||||
'categories.delete' => 'categories_delete',
|
||||
|
||||
'brands.index' => 'brands_index',
|
||||
'brands.show' => 'brands_show',
|
||||
'brands.create' => 'brands_create',
|
||||
'brands.update' => 'brands_update',
|
||||
'brands.delete' => 'brands_delete',
|
||||
|
||||
'orders.index' => 'orders_index',
|
||||
'orders.show' => 'orders_show',
|
||||
'orders.update_status' => 'orders_update_status',
|
||||
'orders.update_shipment' => 'orders_update_status',
|
||||
|
||||
'products.index' => 'products_index',
|
||||
'products.show' => 'products_show',
|
||||
'products.create' => 'products_create',
|
||||
'products.update' => 'products_update',
|
||||
'products.delete' => 'products_delete',
|
||||
];
|
||||
|
||||
return $maps[$routePath] ?? '';
|
||||
}
|
||||
}
|
||||
|
|
@ -13,5 +13,6 @@ class VerifyCsrfToken extends Middleware
|
|||
*/
|
||||
protected $except = [
|
||||
'callback/*',
|
||||
'api/*',
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
/**
|
||||
* BrandController.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-04-20 17:19:51
|
||||
* @modified 2023-04-20 17:19:51
|
||||
*/
|
||||
|
||||
namespace Beike\API\Controllers\Admin;
|
||||
|
||||
use Beike\Models\Brand;
|
||||
use Beike\Repositories\BrandRepo;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BrandController
|
||||
{
|
||||
/**
|
||||
* 显示品牌列表
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$brands = BrandRepo::list($request->only('name', 'first', 'status'));
|
||||
$data = [
|
||||
'brands' => $brands,
|
||||
];
|
||||
|
||||
return hook_filter('admin_api.brand.index.data', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建品牌
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Brand $brand
|
||||
* @return Brand
|
||||
*/
|
||||
public function show(Request $request, Brand $brand): Brand
|
||||
{
|
||||
return hook_filter('admin_api.brand.show.data', $brand);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建品牌
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function store(Request $request): array
|
||||
{
|
||||
$requestData = $request->all();
|
||||
$data = [
|
||||
'request_data' => $requestData,
|
||||
];
|
||||
|
||||
hook_action('admin_api.brand.store.before', $data);
|
||||
$brand = BrandRepo::create($requestData);
|
||||
hook_action('admin_api.brand.store.after', ['brand' => $brand, 'request_data' => $requestData]);
|
||||
|
||||
return json_success(trans('common.created_success'), $brand);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Brand $brand
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function update(Request $request, Brand $brand): array
|
||||
{
|
||||
$requestData = $request->all();
|
||||
$data = [
|
||||
'brand_id' => $brand,
|
||||
'request_data' => $requestData,
|
||||
];
|
||||
hook_action('admin_api.brand.update.before', $data);
|
||||
$brand = BrandRepo::update($brand, $requestData);
|
||||
hook_action('admin_api.brand.update.after', $data);
|
||||
|
||||
return json_success(trans('common.updated_success'), $brand);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Brand $brand
|
||||
* @return array
|
||||
*/
|
||||
public function destroy(Request $request, Brand $brand): array
|
||||
{
|
||||
hook_action('admin_api.brand.destroy.before', $brand);
|
||||
BrandRepo::delete($brand);
|
||||
hook_action('admin_api.brand.destroy.after', $brand);
|
||||
|
||||
return json_success(trans('common.deleted_success'));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
/**
|
||||
* CategoryController.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-04-20 17:00:40
|
||||
* @modified 2023-04-20 17:00:40
|
||||
*/
|
||||
|
||||
namespace Beike\API\Controllers\Admin;
|
||||
|
||||
use Beike\Admin\Http\Requests\CategoryRequest;
|
||||
use Beike\Admin\Http\Resources\CategoryResource;
|
||||
use Beike\Admin\Services\CategoryService;
|
||||
use Beike\Models\Category;
|
||||
use Beike\Repositories\CategoryRepo;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CategoryController
|
||||
{
|
||||
/**
|
||||
* 商品分类列表
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$categories = CategoryRepo::getAdminList();
|
||||
$data = [
|
||||
'categories' => CategoryResource::collection($categories),
|
||||
];
|
||||
|
||||
return hook_filter('admin_api.category.index.data', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个商品分类
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Category $category
|
||||
* @return mixed
|
||||
*/
|
||||
public function show(Request $request, Category $category)
|
||||
{
|
||||
if (! $category->active) {
|
||||
return [];
|
||||
}
|
||||
$category->load('description');
|
||||
|
||||
return hook_filter('admin_api.category.show.data', $category);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存商品分类
|
||||
*
|
||||
* @param CategoryRequest $request
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function store(CategoryRequest $request)
|
||||
{
|
||||
$requestData = $request->all();
|
||||
$category = (new CategoryService())->createOrUpdate($requestData, null);
|
||||
$data = [
|
||||
'category' => $category,
|
||||
'request_data' => $requestData,
|
||||
];
|
||||
|
||||
hook_action('admin_api.category.save.after', $data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新产品分类
|
||||
*
|
||||
* @param CategoryRequest $request
|
||||
* @param Category $category
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function update(CategoryRequest $request, Category $category)
|
||||
{
|
||||
$requestData = $request->all();
|
||||
$category = (new CategoryService())->createOrUpdate($requestData, $category);
|
||||
$data = [
|
||||
'category' => $category,
|
||||
'request_data' => $requestData,
|
||||
];
|
||||
|
||||
hook_action('admin_api.category.save.after', $data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除分类
|
||||
* @param Request $request
|
||||
* @param Category $category
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function destroy(Request $request, Category $category): array
|
||||
{
|
||||
CategoryRepo::delete($category);
|
||||
hook_action('admin.category.destroy.after', $category);
|
||||
|
||||
return json_success(trans('common.deleted_success'));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
/**
|
||||
* OrderController.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-04-20 16:40:35
|
||||
* @modified 2023-04-20 16:40:35
|
||||
*/
|
||||
|
||||
namespace Beike\API\Controllers\Admin;
|
||||
|
||||
use Beike\Models\Order;
|
||||
use Beike\Models\OrderShipment;
|
||||
use Beike\Repositories\OrderRepo;
|
||||
use Beike\Services\ShipmentService;
|
||||
use Beike\Services\StateMachineService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class OrderController
|
||||
{
|
||||
/**
|
||||
* 获取订单列表
|
||||
*
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$orders = OrderRepo::filterOrders($request->all());
|
||||
|
||||
return hook_filter('admin_api.order.index.data', $orders);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看单个订单
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Order $order
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function show(Request $request, Order $order)
|
||||
{
|
||||
$order->load(['orderTotals', 'orderHistories', 'orderShipments']);
|
||||
$data = hook_filter('admin.order.show.data', ['order' => $order, 'html_items' => []]);
|
||||
$data['statuses'] = StateMachineService::getInstance($order)->nextBackendStatuses();
|
||||
|
||||
return hook_filter('admin_api.order.show.data', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新订单状态,添加订单更新日志
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Order $order
|
||||
* @return array
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function updateStatus(Request $request, Order $order)
|
||||
{
|
||||
$status = $request->get('status');
|
||||
$comment = $request->get('comment');
|
||||
$notify = $request->get('notify');
|
||||
|
||||
$shipment = ShipmentService::handleShipment(\request('express_code'), \request('express_number'));
|
||||
|
||||
$stateMachine = new StateMachineService($order);
|
||||
$stateMachine->setShipment($shipment)->changeStatus($status, $comment, $notify);
|
||||
|
||||
$orderStatusData = $request->all();
|
||||
|
||||
hook_action('admin.order.update_status.after', $orderStatusData);
|
||||
|
||||
return json_success(trans('common.updated_success'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新发货信息
|
||||
*/
|
||||
public function updateShipment(Request $request, Order $order, int $orderShipmentId): array
|
||||
{
|
||||
$data = $request->all();
|
||||
$orderShipment = OrderShipment::query()->where('order_id', $order->id)->findOrFail($orderShipmentId);
|
||||
ShipmentService::updateShipment($orderShipment, $data);
|
||||
hook_action('admin.order.update_shipment.after', [
|
||||
'request_data' => $data,
|
||||
'shipment' => $orderShipment,
|
||||
]);
|
||||
|
||||
return json_success(trans('common.updated_success'));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
/**
|
||||
* ProductController.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-04-20 16:48:47
|
||||
* @modified 2023-04-20 16:48:47
|
||||
*/
|
||||
|
||||
namespace Beike\API\Controllers\Admin;
|
||||
|
||||
use Beike\Admin\Http\Requests\ProductRequest;
|
||||
use Beike\Admin\Http\Resources\ProductResource;
|
||||
use Beike\Admin\Services\ProductService;
|
||||
use Beike\Models\Product;
|
||||
use Beike\Repositories\ProductRepo;
|
||||
use Beike\Shop\Http\Resources\ProductDetail;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProductController
|
||||
{
|
||||
/**
|
||||
* 产品列表
|
||||
*
|
||||
* @param Request $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$requestData = $request->all();
|
||||
if (! isset($requestData['sort'])) {
|
||||
$requestData['sort'] = 'products.updated_at';
|
||||
}
|
||||
$productList = ProductRepo::list($requestData);
|
||||
$products = ProductResource::collection($productList);
|
||||
|
||||
return hook_filter('admin_api.product.index.data', $products);
|
||||
}
|
||||
|
||||
/**
|
||||
* 产品列表
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Product $product
|
||||
* @return mixed
|
||||
*/
|
||||
public function show(Request $request, Product $product)
|
||||
{
|
||||
$relationIds = $product->relations->pluck('id')->toArray();
|
||||
$product = ProductRepo::getProductDetail($product);
|
||||
|
||||
$data = [
|
||||
'product' => (new ProductDetail($product))->jsonSerialize(),
|
||||
'relations' => ProductRepo::getProductsByIds($relationIds)->jsonSerialize(),
|
||||
];
|
||||
|
||||
return hook_filter('admin_api.product.show.data', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建商品
|
||||
*
|
||||
* @param ProductRequest $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function store(ProductRequest $request)
|
||||
{
|
||||
try {
|
||||
$requestData = $request->all();
|
||||
$product = (new ProductService)->create($requestData);
|
||||
|
||||
$data = [
|
||||
'request_data' => $requestData,
|
||||
'product' => $product,
|
||||
];
|
||||
hook_action('admin_api.product.store.after', $data);
|
||||
|
||||
return json_success(trans('common.created_success'));
|
||||
} catch (\Exception $e) {
|
||||
return json_fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑商品
|
||||
*
|
||||
* @param ProductRequest $request
|
||||
* @param Product $product
|
||||
* @return mixed
|
||||
*/
|
||||
public function update(ProductRequest $request, Product $product)
|
||||
{
|
||||
try {
|
||||
$requestData = $request->all();
|
||||
$product = (new ProductService)->update($product, $requestData);
|
||||
|
||||
$data = [
|
||||
'request_data' => $requestData,
|
||||
'product' => $product,
|
||||
];
|
||||
hook_action('admin_api.product.update.after', $data);
|
||||
|
||||
return json_success(trans('common.updated_success'));
|
||||
} catch (\Exception $e) {
|
||||
return json_fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除商品
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Product $product
|
||||
* @return array
|
||||
*/
|
||||
public function destroy(Request $request, Product $product)
|
||||
{
|
||||
$product->delete();
|
||||
hook_action('admin_api.product.destroy.after', $product);
|
||||
|
||||
return json_success(trans('common.deleted_success'));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* UserController.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-04-20 14:51:26
|
||||
* @modified 2023-04-20 14:51:26
|
||||
*/
|
||||
|
||||
namespace Beike\API\Controllers\Admin;
|
||||
|
||||
class UserController
|
||||
{
|
||||
public function me()
|
||||
{
|
||||
return registry('admin_user');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
/**
|
||||
* AuthController.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-04-11 17:44:26
|
||||
* @modified 2023-04-11 17:44:26
|
||||
*/
|
||||
|
||||
namespace Beike\API\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Beike\Shop\Http\Resources\CustomerResource;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
/**
|
||||
* Create a new AuthController instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// $this->middleware('auth:api', ['except' => ['login']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a JWT via given credentials.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function login()
|
||||
{
|
||||
$credentials = request(['email', 'password']);
|
||||
|
||||
if (! $token = auth('api_customer')->attempt($credentials)) {
|
||||
return response()->json(['error' => 'Unauthorized'], 401);
|
||||
}
|
||||
|
||||
return $this->respondWithToken($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the authenticated User.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function me()
|
||||
{
|
||||
$customer = auth('api_customer')->user();
|
||||
|
||||
return response()->json(new CustomerResource($customer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the user out (Invalidate the token).
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function logout()
|
||||
{
|
||||
auth('api_customer')->logout();
|
||||
|
||||
return response()->json(['message' => 'Successfully logged out']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh a token.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function refresh()
|
||||
{
|
||||
return $this->respondWithToken(auth('api_customer')->refresh());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token array structure.
|
||||
*
|
||||
* @param string $token
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function respondWithToken($token)
|
||||
{
|
||||
return response()->json([
|
||||
'access_token' => $token,
|
||||
'token_type' => 'bearer',
|
||||
'expires_in' => auth('api_customer')->factory()->getTTL() * 60,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
/**
|
||||
* APIServiceProvider.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-04-11 16:08:04
|
||||
* @modified 2023-04-11 16:08:04
|
||||
*/
|
||||
|
||||
namespace Beike\API\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class APIServiceProvider extends ServiceProvider
|
||||
{
|
||||
public function boot()
|
||||
{
|
||||
if (is_installer()) {
|
||||
return;
|
||||
}
|
||||
$this->loadRoutesFrom(__DIR__ . '/../Routes/api.php');
|
||||
$this->registerGuard();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册后台用户 guard
|
||||
*/
|
||||
protected function registerGuard()
|
||||
{
|
||||
Config::set('auth.guards.api_customer', [
|
||||
'driver' => 'jwt',
|
||||
'provider' => 'shop_customer',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
/**
|
||||
* api.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-04-11 17:36:05
|
||||
* @modified 2023-04-11 17:36:05
|
||||
*/
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::prefix('api')
|
||||
->middleware(['api'])
|
||||
->name('api.')
|
||||
->group(function () {
|
||||
Route::post('login', [\Beike\API\Controllers\AuthController::class, 'login']);
|
||||
Route::post('logout', [\Beike\API\Controllers\AuthController::class, 'logout']);
|
||||
Route::post('refresh', [\Beike\API\Controllers\AuthController::class, 'refresh']);
|
||||
Route::get('me', [\Beike\API\Controllers\AuthController::class, 'me']);
|
||||
});
|
||||
|
||||
Route::prefix('admin_api')
|
||||
->middleware(['admin_api'])
|
||||
->name('admin_api.')
|
||||
->group(function () {
|
||||
Route::get('brands', [\Beike\API\Controllers\Admin\BrandController::class, 'index'])->name('brands.index');
|
||||
Route::get('brands/{brand}', [\Beike\API\Controllers\Admin\BrandController::class, 'show'])->name('brands.show');
|
||||
Route::post('brands', [\Beike\API\Controllers\Admin\BrandController::class, 'store'])->name('brands.create');
|
||||
Route::put('brands/{brand}', [\Beike\API\Controllers\Admin\BrandController::class, 'update'])->name('brands.update');
|
||||
Route::delete('brands/{brand}', [\Beike\API\Controllers\Admin\BrandController::class, 'destroy'])->name('brands.delete');
|
||||
|
||||
Route::get('categories', [\Beike\API\Controllers\Admin\CategoryController::class, 'index'])->name('categories.index');
|
||||
Route::get('categories/{category}', [\Beike\API\Controllers\Admin\CategoryController::class, 'show'])->name('categories.show');
|
||||
Route::post('categories', [\Beike\API\Controllers\Admin\CategoryController::class, 'store'])->name('categories.create');
|
||||
Route::put('categories/{category}', [\Beike\API\Controllers\Admin\CategoryController::class, 'update'])->name('categories.update');
|
||||
Route::delete('categories/{category}', [\Beike\API\Controllers\Admin\CategoryController::class, 'destroy'])->name('categories.delete');
|
||||
|
||||
Route::get('me', [\Beike\API\Controllers\Admin\UserController::class, 'me'])->name('me');
|
||||
|
||||
Route::get('orders', [\Beike\API\Controllers\Admin\OrderController::class, 'index'])->name('orders.index');
|
||||
Route::get('orders/{order}', [\Beike\API\Controllers\Admin\OrderController::class, 'show'])->name('orders.show');
|
||||
Route::put('orders/{order}/status', [\Beike\API\Controllers\Admin\OrderController::class, 'updateStatus'])->name('orders.update_status');
|
||||
Route::put('orders/{order}/shipments/{shipment}', [\Beike\API\Controllers\Admin\OrderController::class, 'updateShipment'])->name('orders.update_shipment');
|
||||
|
||||
Route::get('products', [\Beike\API\Controllers\Admin\ProductController::class, 'index'])->name('products.index');
|
||||
Route::get('products/{product}', [\Beike\API\Controllers\Admin\ProductController::class, 'show'])->name('products.show');
|
||||
Route::post('products', [\Beike\API\Controllers\Admin\ProductController::class, 'store'])->name('products.create');
|
||||
Route::put('products/{product}', [\Beike\API\Controllers\Admin\ProductController::class, 'update'])->name('products.update');
|
||||
Route::delete('products/{product}', [\Beike\API\Controllers\Admin\ProductController::class, 'destroy'])->name('products.delete');
|
||||
});
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
/**
|
||||
* BrandController.php
|
||||
*
|
||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author TL <mengwb@guangda.work>
|
||||
* @created 2022-07-27 21:17:04
|
||||
* @modified 2022-07-27 21:17:04
|
||||
*/
|
||||
|
||||
namespace Beike\Admin\Http\Controllers;
|
||||
|
||||
use Beike\Admin\Repositories\AdminUserRepo;
|
||||
use Beike\Repositories\AdminUserTokenRepo;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AccountController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$user = current_user();
|
||||
$data = [
|
||||
'current_user' => $user,
|
||||
'tokens' => AdminUserTokenRepo::getTokenByAdminUser($user)->pluck('token')->toArray(),
|
||||
];
|
||||
|
||||
return view('admin::pages.account.index', $data);
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
$user = current_user();
|
||||
|
||||
$adminUserData = $request->all();
|
||||
AdminUserRepo::updateAdminUser($user->id, $adminUserData);
|
||||
|
||||
return response()->redirectTo('admin/account')->with('success', trans('common.updated_success'));
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
namespace Beike\Admin\Http\Controllers;
|
||||
|
||||
use Beike\Admin\Http\Requests\CurrencyRequest;
|
||||
use Beike\Models\Order;
|
||||
use Beike\Repositories\CurrencyRepo;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
|
|
@ -66,9 +67,19 @@ class CurrencyController extends Controller
|
|||
|
||||
public function destroy(Request $request, int $currencyId)
|
||||
{
|
||||
try {
|
||||
$currency = CurrencyRepo::find($currencyId);
|
||||
$orderExist = Order::query()->where('currency_code', $currency->code)->exists();
|
||||
if ($orderExist) {
|
||||
throw new \Exception(trans('admin/currency.order_exist'));
|
||||
}
|
||||
|
||||
CurrencyRepo::delete($currencyId);
|
||||
hook_action('admin.currency.destroy.after', $currencyId);
|
||||
|
||||
return json_success(trans('common.deleted_success'));
|
||||
} catch (\Exception $e) {
|
||||
return json_fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,10 +31,12 @@ class FileManagerController extends Controller
|
|||
public function getFiles(Request $request): array
|
||||
{
|
||||
$baseFolder = $request->get('base_folder');
|
||||
$sort = $request->get('sort', 'created');
|
||||
$order = $request->get('order', 'desc');
|
||||
$page = (int) $request->get('page');
|
||||
$perPage = (int) $request->get('per_page');
|
||||
|
||||
$data = (new FileManagerService)->getFiles($baseFolder, $page, $perPage);
|
||||
$data = (new FileManagerService)->getFiles($baseFolder, $sort, $order, $page, $perPage);
|
||||
|
||||
return hook_filter('admin.file_manager.files.data', $data);
|
||||
}
|
||||
|
|
@ -124,7 +126,7 @@ class FileManagerController extends Controller
|
|||
$savePath = $request->get('path');
|
||||
|
||||
$originName = $file->getClientOriginalName();
|
||||
$filePath = $file->storeAs($savePath, $originName, 'catalog');
|
||||
$filePath = (new FileManagerService)->uploadFile($file, $savePath, $originName);
|
||||
|
||||
return [
|
||||
'name' => $originName,
|
||||
|
|
|
|||
|
|
@ -5,13 +5,16 @@ namespace Beike\Admin\Http\Controllers;
|
|||
use App\Http\Controllers\Controller;
|
||||
use Beike\Admin\Repositories\DashboardRepo;
|
||||
use Beike\Admin\Repositories\Report\OrderReportRepo;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$data = [
|
||||
'views' => DashboardRepo::getCustomerViewData(),
|
||||
'products' => DashboardRepo::getProductData(),
|
||||
// 'views' => DashboardRepo::getCustomerViewData(),
|
||||
'orders' => DashboardRepo::getOrderData(),
|
||||
'customers' => DashboardRepo::getCustomerData(),
|
||||
'order_totals' => DashboardRepo::getTotalData(),
|
||||
|
|
@ -24,4 +27,54 @@ class HomeController extends Controller
|
|||
|
||||
return view('admin::pages.home', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过关键字搜索菜单
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function menus()
|
||||
{
|
||||
$keyword = trim(request('keyword'));
|
||||
$menus = [];
|
||||
$routes = Route::getRoutes();
|
||||
foreach ($routes as $route) {
|
||||
$routeName = $route->getName();
|
||||
if (! Str::startsWith($routeName, 'admin')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$method = $route->methods()[0];
|
||||
if ($method != 'GET') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$routeName = str_replace('admin.', '', $routeName);
|
||||
$permissionRoute = str_replace('.', '_', $routeName);
|
||||
|
||||
try {
|
||||
$url = admin_route($routeName);
|
||||
} catch (\Exception $e) {
|
||||
$url = '';
|
||||
}
|
||||
if (empty($url)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$title = trans("admin/common.{$permissionRoute}");
|
||||
if (stripos($title, 'admin/common.') !== false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($keyword && stripos($title, $keyword) !== false) {
|
||||
$menus[] = [
|
||||
'route' => $routeName,
|
||||
'url' => admin_route($routeName),
|
||||
'title' => $title,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $menus;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
/**
|
||||
* AttributeController.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author TL <mengwb@guangda.work>
|
||||
* @created 2023-01-04 19:45:41
|
||||
* @modified 2023-01-04 19:45:41
|
||||
*/
|
||||
|
||||
namespace Beike\Admin\Http\Controllers;
|
||||
|
||||
use Beike\Admin\Repositories\AttributeRepo;
|
||||
use Beike\Repositories\SettingRepo;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class MultiFilterController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$multiFilter = system_setting('base.multi_filter') ?: [];
|
||||
$multiFilter['attribute'] = $multiFilter['attribute'] ?? [];
|
||||
|
||||
if ($attributeIds = $multiFilter['attribute'] ?? []) {
|
||||
$multiFilter['attribute'] = AttributeRepo::getByIds($attributeIds);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'multi_filter' => $multiFilter,
|
||||
];
|
||||
|
||||
return view('admin::pages.multi_filter.index', $data);
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$settings = $request->all();
|
||||
foreach ($settings as $key => $value) {
|
||||
SettingRepo::storeValue($key, $value);
|
||||
}
|
||||
|
||||
return redirect(admin_route('multi_filter.index'))->with('success', trans('common.updated_success'));
|
||||
}
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ class OrderController extends Controller
|
|||
*/
|
||||
public function show(Request $request, Order $order)
|
||||
{
|
||||
$order->load(['orderTotals', 'orderHistories', 'orderShipments']);
|
||||
$order->load(['orderTotals', 'orderHistories', 'orderShipments', 'orderPayments']);
|
||||
$data = hook_filter('admin.order.show.data', ['order' => $order, 'html_items' => []]);
|
||||
$data['statuses'] = StateMachineService::getInstance($order)->nextBackendStatuses();
|
||||
$data = hook_filter('admin.order.show.data', $data);
|
||||
|
|
|
|||
|
|
@ -32,6 +32,111 @@ class PluginController extends Controller
|
|||
return view('admin::pages.plugins.index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function shipping()
|
||||
{
|
||||
$type = 'shipping';
|
||||
$plugins = app('plugin')->getPlugins();
|
||||
$plugins = $plugins->where('type', $type);
|
||||
$data['plugins'] = array_values(PluginResource::collection($plugins)->jsonSerialize());
|
||||
$data['type'] = $type;
|
||||
$data = hook_filter('admin.plugin.index.data', $data);
|
||||
|
||||
return view('admin::pages.plugins.index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function payment()
|
||||
{
|
||||
$type = 'payment';
|
||||
$plugins = app('plugin')->getPlugins();
|
||||
$plugins = $plugins->where('type', $type);
|
||||
$data['plugins'] = array_values(PluginResource::collection($plugins)->jsonSerialize());
|
||||
$data['type'] = $type;
|
||||
$data = hook_filter('admin.plugin.index.data', $data);
|
||||
|
||||
return view('admin::pages.plugins.index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function total()
|
||||
{
|
||||
$type = 'total';
|
||||
$plugins = app('plugin')->getPlugins();
|
||||
$plugins = $plugins->where('type', $type);
|
||||
$data['plugins'] = array_values(PluginResource::collection($plugins)->jsonSerialize());
|
||||
$data['type'] = $type;
|
||||
$data = hook_filter('admin.plugin.index.data', $data);
|
||||
|
||||
return view('admin::pages.plugins.index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function social()
|
||||
{
|
||||
$type = 'social';
|
||||
$plugins = app('plugin')->getPlugins();
|
||||
$plugins = $plugins->where('type', $type);
|
||||
$data['plugins'] = array_values(PluginResource::collection($plugins)->jsonSerialize());
|
||||
$data['type'] = $type;
|
||||
$data = hook_filter('admin.plugin.index.data', $data);
|
||||
|
||||
return view('admin::pages.plugins.index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function feature()
|
||||
{
|
||||
$type = 'feature';
|
||||
$plugins = app('plugin')->getPlugins();
|
||||
$plugins = $plugins->where('type', $type);
|
||||
$data['plugins'] = array_values(PluginResource::collection($plugins)->jsonSerialize());
|
||||
$data['type'] = $type;
|
||||
$data = hook_filter('admin.plugin.index.data', $data);
|
||||
|
||||
return view('admin::pages.plugins.index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function language()
|
||||
{
|
||||
$type = 'language';
|
||||
$plugins = app('plugin')->getPlugins();
|
||||
$plugins = $plugins->where('type', $type);
|
||||
$data['plugins'] = array_values(PluginResource::collection($plugins)->jsonSerialize());
|
||||
$data['type'] = $type;
|
||||
$data = hook_filter('admin.plugin.index.data', $data);
|
||||
|
||||
return view('admin::pages.plugins.index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function theme()
|
||||
{
|
||||
$type = 'theme';
|
||||
$plugins = app('plugin')->getPlugins();
|
||||
$plugins = $plugins->where('type', $type);
|
||||
$data['plugins'] = array_values(PluginResource::collection($plugins)->jsonSerialize());
|
||||
$data['type'] = $type;
|
||||
$data = hook_filter('admin.plugin.index.data', $data);
|
||||
|
||||
return view('admin::pages.plugins.index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传插件
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use Beike\Admin\Http\Resources\ProductAttributeResource;
|
|||
use Beike\Admin\Http\Resources\ProductResource;
|
||||
use Beike\Admin\Repositories\TaxClassRepo;
|
||||
use Beike\Admin\Services\ProductService;
|
||||
use Beike\Libraries\Weight;
|
||||
use Beike\Models\Product;
|
||||
use Beike\Repositories\CategoryRepo;
|
||||
use Beike\Repositories\LanguageRepo;
|
||||
|
|
@ -20,6 +21,9 @@ class ProductController extends Controller
|
|||
public function index(Request $request)
|
||||
{
|
||||
$requestData = $request->all();
|
||||
if (! isset($requestData['sort'])) {
|
||||
$requestData['sort'] = 'products.updated_at';
|
||||
}
|
||||
$productList = ProductRepo::list($requestData);
|
||||
$products = ProductResource::collection($productList);
|
||||
$productsFormat = $products->jsonSerialize();
|
||||
|
|
@ -152,8 +156,9 @@ class ProductController extends Controller
|
|||
'relations' => ProductResource::collection($product->relations)->resource,
|
||||
'languages' => LanguageRepo::all(),
|
||||
'tax_classes' => $taxClasses,
|
||||
'weight_classes' => Weight::getWeightUnits(),
|
||||
'source' => [
|
||||
'categories' => CategoryRepo::flatten(locale()),
|
||||
'categories' => CategoryRepo::flatten(locale(), false),
|
||||
],
|
||||
'_redirect' => $this->getRedirect(),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class SettingController extends Controller
|
|||
{
|
||||
$themes = ThemeRepo::getAllThemes();
|
||||
|
||||
$tax_address = [
|
||||
$taxAddress = [
|
||||
['value' => 'shipping', 'label' => trans('admin/setting.shipping_address')],
|
||||
['value' => 'payment', 'label' => trans('admin/setting.payment_address')],
|
||||
];
|
||||
|
|
@ -38,7 +38,7 @@ class SettingController extends Controller
|
|||
$data = [
|
||||
'countries' => CountryRepo::listEnabled(),
|
||||
'currencies' => CurrencyRepo::listEnabled(),
|
||||
'tax_address' => $tax_address,
|
||||
'tax_address' => $taxAddress,
|
||||
'customer_groups' => CustomerGroupDetail::collection(CustomerGroupRepo::list())->jsonSerialize(),
|
||||
'themes' => $themes,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class PageRequest extends FormRequest
|
|||
{
|
||||
$rules = [
|
||||
'descriptions.*.title' => 'required|string|min:3|max:128',
|
||||
'descriptions.*.summary' => 'string|max:180',
|
||||
'descriptions.*.content' => 'required|string',
|
||||
'descriptions.*.locale' => 'required|string',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class TaxRateRequest extends FormRequest
|
|||
public function rules(): array
|
||||
{
|
||||
$rule = [
|
||||
'name' => 'required|string|max:10',
|
||||
'name' => 'required|string|max:32',
|
||||
'rate' => 'required|numeric',
|
||||
'type' => 'required|in:percent,flat',
|
||||
'region_id' => 'required|int',
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class UploadRequest extends FormRequest
|
|||
public function rules()
|
||||
{
|
||||
return [
|
||||
'file' => 'required|image|mimes:jpg,png,jpeg,gif,svg|max:2048',
|
||||
'file' => 'required|mimes:jpg,png,jpeg,gif,svg,mp4|max:20480',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,13 +22,13 @@ class PageCategoryResource extends JsonResource
|
|||
return [
|
||||
'id' => $this->id,
|
||||
'active' => $this->active,
|
||||
'title' => $description->title,
|
||||
'title_format' => sub_string($description->title, 64),
|
||||
'summary' => $description->summary,
|
||||
'summary_format' => sub_string($description->summary, 128),
|
||||
'meta_title' => $description->meta_title,
|
||||
'meta_description' => $description->meta_description,
|
||||
'meta_keywords' => $description->meta_keywords,
|
||||
'title' => $description->title ?? '',
|
||||
'title_format' => sub_string($description->title ?? '', 64),
|
||||
'summary' => $description->summary ?? '',
|
||||
'summary_format' => sub_string($description->summary ?? '', 128),
|
||||
'meta_title' => $description->meta_title ?? '',
|
||||
'meta_description' => $description->meta_description ?? '',
|
||||
'meta_keywords' => $description->meta_keywords ?? '',
|
||||
'created_at' => time_format($this->created_at),
|
||||
'updated_at' => time_format($this->updated_at),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ class ProductResource extends JsonResource
|
|||
return image_resize($image);
|
||||
}, $this->images ?? []),
|
||||
'name' => $this->description->name ?? '',
|
||||
'model' => $masterSku->model,
|
||||
'quantity' => $masterSku->quantity,
|
||||
'price_formatted' => currency_format($masterSku->price),
|
||||
'active' => $this->active,
|
||||
'position' => $this->position,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ class AdminServiceProvider extends ServiceProvider
|
|||
|
||||
load_settings();
|
||||
$this->loadRoutesFrom(__DIR__ . '/../Routes/admin.php');
|
||||
$this->registerGuard();
|
||||
|
||||
$adminName = admin_name();
|
||||
if (! Str::startsWith($uri, "/{$adminName}")) {
|
||||
|
|
@ -69,8 +70,6 @@ class AdminServiceProvider extends ServiceProvider
|
|||
|
||||
$this->loadAdminViewComponents();
|
||||
|
||||
$this->registerGuard();
|
||||
|
||||
Config::set('filesystems.disks.catalog', [
|
||||
'driver' => 'local',
|
||||
'root' => public_path('catalog'),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace Beike\Admin\Repositories;
|
|||
|
||||
use Beike\Admin\Http\Resources\AdminUserDetail;
|
||||
use Beike\Models\AdminUser;
|
||||
use Beike\Repositories\AdminUserTokenRepo;
|
||||
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
|
||||
|
||||
class AdminUserRepo
|
||||
|
|
@ -73,7 +74,14 @@ class AdminUserRepo
|
|||
$userData['password'] = bcrypt($password);
|
||||
}
|
||||
$adminUser->update($userData);
|
||||
$adminUser->syncRoles($data['roles']);
|
||||
|
||||
$roles = $data['roles'] ?? [];
|
||||
if ($roles) {
|
||||
$adminUser->syncRoles($roles);
|
||||
}
|
||||
|
||||
$tokens = $data['tokens'] ?? [];
|
||||
AdminUserTokenRepo::updateTokensByUser($adminUser, $tokens);
|
||||
|
||||
return $adminUser;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@ class AttributeGroupRepo
|
|||
|
||||
public static function delete($id)
|
||||
{
|
||||
if ($id == 1) {
|
||||
throw new \Exception(trans('admin/attribute_group.error_cannot_delete_default_group'));
|
||||
}
|
||||
$group = AttributeGroup::query()->findOrFail($id);
|
||||
if ($group->attributes->count()) {
|
||||
throw new \Exception(trans('admin/attribute_group.error_cannot_delete_attribute_used', ['attributes' => implode(', ', $group->attributes->pluck('id')->toArray())]));
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
namespace Beike\Admin\Repositories;
|
||||
|
||||
use Beike\Models\Attribute;
|
||||
use Beike\Models\AttributeDescription;
|
||||
use Beike\Models\AttributeValue;
|
||||
use Beike\Models\ProductAttribute;
|
||||
|
||||
|
|
@ -138,4 +139,14 @@ class AttributeRepo
|
|||
|
||||
return $builder->limit(10)->get();
|
||||
}
|
||||
|
||||
public static function getByIds($ids)
|
||||
{
|
||||
return AttributeDescription::query()
|
||||
->where('locale', locale())
|
||||
->whereIn('attribute_id', $ids)
|
||||
->select(['attribute_id as id', 'name'])
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@
|
|||
|
||||
namespace Beike\Admin\Repositories;
|
||||
|
||||
use Beike\Models\Product;
|
||||
use Beike\Repositories\CustomerRepo;
|
||||
use Beike\Repositories\OrderRepo;
|
||||
use Beike\Repositories\ProductRepo;
|
||||
|
||||
class DashboardRepo
|
||||
{
|
||||
|
|
@ -21,12 +21,22 @@ class DashboardRepo
|
|||
* 获取商品总数
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getProductData(): array
|
||||
{
|
||||
$today = ProductRepo::getBuilder(['created_start' => today()->subDay(), 'created_end' => today()])->count();
|
||||
$yesterday = ProductRepo::getBuilder(['created_start' => today()->subDays(2), 'created_end' => today()->subDay()])->count();
|
||||
$difference = $today - $yesterday;
|
||||
if ($difference && $yesterday) {
|
||||
$percentage = round(($difference / $yesterday) * 100);
|
||||
} else {
|
||||
$percentage = 0;
|
||||
}
|
||||
|
||||
return [
|
||||
'total' => quantity_format(Product::query()->count()),
|
||||
'percentage' => 0,
|
||||
'total' => $today,
|
||||
'percentage' => $percentage,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ class PageRepo
|
|||
$page->descriptions()->createMany($data['descriptions']);
|
||||
|
||||
$products = $data['products'] ?? [];
|
||||
$page->pageProducts()->delete();
|
||||
|
||||
if ($products) {
|
||||
$items = [];
|
||||
foreach ($products as $item) {
|
||||
|
|
@ -90,7 +92,6 @@ class PageRepo
|
|||
'product_id' => $item,
|
||||
];
|
||||
}
|
||||
$page->pageProducts()->delete();
|
||||
$page->pageProducts()->createMany($items);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ class PermissionRepo
|
|||
*/
|
||||
private function getProductPermissions(): array
|
||||
{
|
||||
$routes = ['products_index', 'products_create', 'products_show', 'products_update', 'products_delete', 'products_trashed', 'products_restore'];
|
||||
$routes = ['products_index', 'products_create', 'products_show', 'products_update', 'products_delete', 'products_trashed', 'products_restore', 'products_filter_index', 'products_filter_update'];
|
||||
$items = $this->getPermissionList('product', $routes);
|
||||
|
||||
return hook_filter('role.product_permissions', $items);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ Route::prefix($adminName)
|
|||
Route::middleware('admin_auth:' . \Beike\Models\AdminUser::AUTH_GUARD)
|
||||
->group(function () {
|
||||
Route::get('/', [Controllers\HomeController::class, 'index'])->name('home.index');
|
||||
Route::get('/menus', [Controllers\HomeController::class, 'menus'])->name('home.menus');
|
||||
|
||||
//个人中心
|
||||
Route::middleware('can:account_index')->get('account', [Controllers\AccountController::class, 'index'])->name('account.index');
|
||||
Route::middleware('can:account_update')->put('account', [Controllers\AccountController::class, 'update'])->name('account.update');
|
||||
|
||||
// 属性
|
||||
Route::middleware('can:attributes_update')->post('attributes/{id}/values', [Controllers\AttributeController::class, 'storeValue'])->name('attributes.values.store');
|
||||
|
|
@ -32,6 +37,10 @@ Route::prefix($adminName)
|
|||
Route::middleware('can:attributes_update')->put('attributes/{id}', [Controllers\AttributeController::class, 'update'])->name('attributes.update');
|
||||
Route::middleware('can:attributes_delete')->delete('attributes/{id}', [Controllers\AttributeController::class, 'destroy'])->name('attributes.destroy');
|
||||
|
||||
// 高级筛选
|
||||
Route::middleware('can:products_filter_index')->get('multi_filter', [Controllers\MultiFilterController::class, 'index'])->name('multi_filter.index');
|
||||
Route::middleware('can:products_filter_update')->post('multi_filter', [Controllers\MultiFilterController::class, 'store'])->name('multi_filter.store');
|
||||
|
||||
// 属性组
|
||||
Route::middleware('can:attribute_groups_index')->get('attribute_groups', [Controllers\AttributeGroupController::class, 'index'])->name('attribute_groups.index');
|
||||
Route::middleware('can:attribute_groups_create')->post('attribute_groups', [Controllers\AttributeGroupController::class, 'store'])->name('attribute_groups.store');
|
||||
|
|
@ -153,6 +162,15 @@ Route::prefix($adminName)
|
|||
Route::middleware('can:plugins_install')->post('plugins/{code}/install', [Controllers\PluginController::class, 'install'])->name('plugins.install');
|
||||
Route::middleware('can:plugins_uninstall')->post('plugins/{code}/uninstall', [Controllers\PluginController::class, 'uninstall'])->name('plugins.uninstall');
|
||||
|
||||
// 插件分组
|
||||
Route::middleware('can:plugins_index')->get('plugins/shipping', [Controllers\PluginController::class, 'shipping'])->name('plugins.shipping');
|
||||
Route::middleware('can:plugins_index')->get('plugins/payment', [Controllers\PluginController::class, 'payment'])->name('plugins.payment');
|
||||
Route::middleware('can:plugins_index')->get('plugins/total', [Controllers\PluginController::class, 'total'])->name('plugins.total');
|
||||
Route::middleware('can:plugins_index')->get('plugins/social', [Controllers\PluginController::class, 'social'])->name('plugins.social');
|
||||
Route::middleware('can:plugins_index')->get('plugins/feature', [Controllers\PluginController::class, 'feature'])->name('plugins.feature');
|
||||
Route::middleware('can:plugins_index')->get('plugins/language', [Controllers\PluginController::class, 'language'])->name('plugins.language');
|
||||
Route::middleware('can:plugins_index')->get('plugins/theme', [Controllers\PluginController::class, 'theme'])->name('plugins.theme');
|
||||
|
||||
// 插件市场
|
||||
Route::middleware('can:marketing_index')->get('marketing', [Controllers\MarketingController::class, 'index'])->name('marketing.index');
|
||||
Route::middleware('can:marketing_show')->get('marketing/{code}', [Controllers\MarketingController::class, 'show'])->name('marketing.show');
|
||||
|
|
@ -196,7 +214,7 @@ Route::prefix($adminName)
|
|||
Route::middleware('can:products_index')->get('products', [Controllers\ProductController::class, 'index'])->name('products.index');
|
||||
Route::middleware('can:products_create')->get('products/create', [Controllers\ProductController::class, 'create'])->name('products.create');
|
||||
Route::middleware('can:products_create')->post('products', [Controllers\ProductController::class, 'store'])->name('products.store');
|
||||
Route::middleware('can:products_update')->get('products/{product}/edit', [Controllers\ProductController::class, 'edit'])->name('products.edit');
|
||||
Route::middleware('can:products_show')->get('products/{product}/edit', [Controllers\ProductController::class, 'edit'])->name('products.edit');
|
||||
Route::middleware('can:products_update')->put('products/{product}', [Controllers\ProductController::class, 'update'])->name('products.update');
|
||||
Route::middleware('can:products_delete')->delete('products/{product}', [Controllers\ProductController::class, 'destroy'])->name('products.destroy');
|
||||
|
||||
|
|
|
|||
|
|
@ -13,11 +13,13 @@ namespace Beike\Admin\Services;
|
|||
|
||||
class FileManagerService
|
||||
{
|
||||
private $fileBasePath = '';
|
||||
protected $fileBasePath = '';
|
||||
|
||||
protected $basePath = '';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->fileBasePath = public_path('catalog');
|
||||
$this->fileBasePath = public_path('catalog') . $this->basePath;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -26,6 +28,7 @@ class FileManagerService
|
|||
public function getDirectories($baseFolder = '/'): array
|
||||
{
|
||||
$currentBasePath = rtrim($this->fileBasePath . $baseFolder, '/');
|
||||
|
||||
$directories = glob("{$currentBasePath}/*", GLOB_ONLYDIR);
|
||||
|
||||
$result = [];
|
||||
|
|
@ -49,18 +52,34 @@ class FileManagerService
|
|||
* 获取某个目录下的文件和文件夹
|
||||
*
|
||||
* @param $baseFolder
|
||||
* @param $sort
|
||||
* @param $order
|
||||
* @param int $page
|
||||
* @param int $perPage
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getFiles($baseFolder, int $page = 1, int $perPage = 20): array
|
||||
public function getFiles($baseFolder, $sort, $order, int $page = 1, int $perPage = 20): array
|
||||
{
|
||||
$currentBasePath = rtrim($this->fileBasePath . $baseFolder, '/');
|
||||
$files = glob($currentBasePath . '/*');
|
||||
|
||||
if ($sort == 'created') {
|
||||
if ($order == 'desc') {
|
||||
usort($files, function ($a, $b) {
|
||||
return filemtime($a) - filemtime($b) < 0;
|
||||
});
|
||||
} else {
|
||||
usort($files, function ($a, $b) {
|
||||
return filemtime($a) - filemtime($b) >= 0;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
natcasesort($files);
|
||||
if ($order == 'desc') {
|
||||
$files = array_reverse($files);
|
||||
}
|
||||
}
|
||||
|
||||
$images = [];
|
||||
foreach ($files as $file) {
|
||||
|
|
@ -68,7 +87,7 @@ class FileManagerService
|
|||
if ($baseName == 'index.html') {
|
||||
continue;
|
||||
}
|
||||
$fileName = str_replace($this->fileBasePath, '', $file);
|
||||
$fileName = str_replace(public_path('catalog'), '', $file);
|
||||
if (is_file($file)) {
|
||||
$images[] = $this->handleImage($fileName, $baseName);
|
||||
}
|
||||
|
|
@ -98,7 +117,7 @@ class FileManagerService
|
|||
*/
|
||||
public function createDirectory($folderName)
|
||||
{
|
||||
$catalogFolderPath = "catalog/{$folderName}";
|
||||
$catalogFolderPath = "catalog{$this->basePath}/{$folderName}";
|
||||
$folderPath = public_path($catalogFolderPath);
|
||||
if (is_dir($folderPath)) {
|
||||
throw new \Exception(trans('admin/file_manager.directory_already_exist'));
|
||||
|
|
@ -114,7 +133,7 @@ class FileManagerService
|
|||
*/
|
||||
public function deleteDirectoryOrFile($filePath)
|
||||
{
|
||||
$filePath = public_path("catalog/{$filePath}");
|
||||
$filePath = public_path("catalog{$this->basePath}/{$filePath}");
|
||||
if (is_dir($filePath)) {
|
||||
$files = glob($filePath . '/*');
|
||||
if ($files) {
|
||||
|
|
@ -138,7 +157,7 @@ class FileManagerService
|
|||
return;
|
||||
}
|
||||
foreach ($files as $file) {
|
||||
$filePath = public_path("catalog/{$basePath}/$file");
|
||||
$filePath = public_path("catalog{$this->basePath}/{$basePath}/$file");
|
||||
if (file_exists($filePath)) {
|
||||
@unlink($filePath);
|
||||
}
|
||||
|
|
@ -154,7 +173,7 @@ class FileManagerService
|
|||
*/
|
||||
public function updateName($originPath, $newPath)
|
||||
{
|
||||
$originPath = public_path("catalog/{$originPath}");
|
||||
$originPath = public_path("catalog{$this->basePath}/{$originPath}");
|
||||
if (! is_dir($originPath) && ! file_exists($originPath)) {
|
||||
throw new \Exception(trans('admin/file_manager.target_not_exist'));
|
||||
}
|
||||
|
|
@ -166,6 +185,13 @@ class FileManagerService
|
|||
@rename($originPath, $newPath);
|
||||
}
|
||||
|
||||
public function uploadFile($file, $savePath, $originName)
|
||||
{
|
||||
$savePath = $this->basePath . $savePath;
|
||||
|
||||
return $file->storeAs($savePath, $originName, 'catalog');
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理文件夹
|
||||
*
|
||||
|
|
@ -189,7 +215,7 @@ class FileManagerService
|
|||
*/
|
||||
private function hasSubFolders($folderPath): bool
|
||||
{
|
||||
$path = public_path("catalog/{$folderPath}");
|
||||
$path = public_path("catalog{$this->basePath}/{$folderPath}");
|
||||
$subFiles = glob($path . '/*');
|
||||
foreach ($subFiles as $subFile) {
|
||||
if (is_dir($subFile)) {
|
||||
|
|
@ -211,11 +237,18 @@ class FileManagerService
|
|||
private function handleImage($filePath, $baseName): array
|
||||
{
|
||||
$path = "catalog{$filePath}";
|
||||
$realPath = $this->fileBasePath . $filePath;
|
||||
|
||||
$mime = '';
|
||||
if(file_exists($realPath)) {
|
||||
$mime = mime_content_type($realPath);
|
||||
}
|
||||
|
||||
return [
|
||||
'path' => $path,
|
||||
'name' => $baseName,
|
||||
'origin_url' => image_origin($path),
|
||||
'mime' => $mime,
|
||||
'selected' => false,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,12 @@ class ProductService
|
|||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
$data['brand_id'] = (int) $data['brand_id'];
|
||||
$data['variables'] = json_decode($data['variables']);
|
||||
$data['brand_id'] = (int) ($data['brand_id'] ?? 0);
|
||||
$data['position'] = (int) ($data['position'] ?? 0);
|
||||
$data['weight'] = (float) ($data['weight'] ?? 0);
|
||||
$data['variables'] = json_decode($data['variables'] ?? '[]');
|
||||
$product->fill($data);
|
||||
$product->updated_at = now();
|
||||
$product->save();
|
||||
|
||||
if ($isUpdating) {
|
||||
|
|
|
|||
|
|
@ -14,16 +14,19 @@ class Select extends Component
|
|||
|
||||
public array $options;
|
||||
|
||||
public string $width;
|
||||
|
||||
public string $key;
|
||||
|
||||
public string $label;
|
||||
|
||||
public function __construct(string $name, string $value, string $title, array $options, ?string $key = 'value', ?string $label = 'label')
|
||||
public function __construct(string $name, string $value, string $title, array $options, string $width = '400', ?string $key = 'value', ?string $label = 'label')
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->title = $title;
|
||||
$this->value = $value;
|
||||
$this->options = $options;
|
||||
$this->width = $width;
|
||||
$this->key = $key;
|
||||
$this->label = $label;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace Beike\Admin\View\Components;
|
||||
|
||||
use Beike\Models\AdminUser;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class Header extends Component
|
||||
|
|
@ -11,6 +12,10 @@ class Header extends Component
|
|||
|
||||
private ?AdminUser $adminUser;
|
||||
|
||||
public array $commonLinks;
|
||||
|
||||
public array $historyLinks;
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*
|
||||
|
|
@ -28,82 +33,102 @@ class Header extends Component
|
|||
*/
|
||||
public function render()
|
||||
{
|
||||
$sidebar = new Sidebar();
|
||||
$preparedMenus = $this->prepareMenus();
|
||||
|
||||
foreach ($preparedMenus as $menu) {
|
||||
$menuCode = $menu['code'] ?? '';
|
||||
if ($menuCode) {
|
||||
$routes = [];
|
||||
$subRoutesMethod = "get{$menu['code']}SubRoutes";
|
||||
if (method_exists($sidebar, $subRoutesMethod)) {
|
||||
$sideMenuRoutes = $sidebar->{"get{$menu['code']}SubRoutes"}();
|
||||
foreach ($sideMenuRoutes as $route) {
|
||||
$routeFirst = explode('.', $route['route'])[0] ?? '';
|
||||
$routes[] = 'admin.' . $route['route'];
|
||||
$routes[] = 'admin.' . $routeFirst . '.edit';
|
||||
$routes[] = 'admin.' . $routeFirst . '.show';
|
||||
}
|
||||
}
|
||||
|
||||
$data = [
|
||||
'menu_code' => $menuCode,
|
||||
'routes' => $routes,
|
||||
];
|
||||
$filterRoutes = hook_filter('admin.components.header.routes', $data);
|
||||
$routes = $filterRoutes['routes'] ?? [];
|
||||
if (empty($routes)) {
|
||||
$is_route = equal_route('admin.' . $menu['route']);
|
||||
} else {
|
||||
$is_route = equal_route($routes);
|
||||
}
|
||||
} else {
|
||||
$is_route = equal_route('admin.' . $menu['route']);
|
||||
}
|
||||
|
||||
$this->addLink($menu['name'], $menu['route'], $is_route);
|
||||
}
|
||||
$this->commonLinks = $this->getCommonLinks();
|
||||
$this->historyLinks = $this->handleHistoryLinks();
|
||||
|
||||
return view('admin::components.header');
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认菜单
|
||||
* 常用功能链接
|
||||
*/
|
||||
private function prepareMenus()
|
||||
private function getCommonLinks()
|
||||
{
|
||||
$menus = [
|
||||
['name' => trans('admin/common.home'), 'route' => 'home.index', 'code' => ''],
|
||||
['name' => trans('admin/common.order'), 'route' => 'orders.index', 'code' => 'Order'],
|
||||
['name' => trans('admin/common.product'), 'route' => 'products.index', 'code' => 'Product'],
|
||||
['name' => trans('admin/common.customer'), 'route' => 'customers.index', 'code' => 'Customer'],
|
||||
['name' => trans('admin/common.page'), 'route' => 'pages.index', 'code' => 'Page'],
|
||||
['name' => trans('admin/common.setting'), 'route' => 'settings.index', 'code' => 'Setting'],
|
||||
['name' => trans('admin/common.inquiry'), 'route' => 'inquiry.index', 'code' => 'Inquiry'],
|
||||
$commonLinks = [
|
||||
['route' => 'design.index', 'icon' => 'bi bi-palette', 'blank' => true],
|
||||
['route' => 'design_footer.index', 'icon' => 'bi bi-palette', 'blank' => true],
|
||||
['route' => 'design_menu.index', 'icon' => 'bi bi-list', 'blank' => false],
|
||||
['route' => 'languages.index', 'icon' => 'bi bi-globe2', 'blank' => false],
|
||||
['route' => 'currencies.index', 'icon' => 'bi bi-currency-dollar', 'blank' => false],
|
||||
['route' => 'plugins.index', 'icon' => 'bi bi-plug', 'blank' => false],
|
||||
];
|
||||
|
||||
return hook_filter('admin.header_menus', $menus);
|
||||
foreach ($commonLinks as $index => $commonLink) {
|
||||
$route = $commonLink['route'];
|
||||
$permissionRoute = str_replace('.', '_', $route);
|
||||
$commonLinks[$index]['url'] = admin_route($route);
|
||||
$commonLinks[$index]['title'] = trans("admin/common.{$permissionRoute}");
|
||||
}
|
||||
|
||||
return hook_filter('admin.components.header.common_links', $commonLinks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加后台顶部菜单链接
|
||||
*
|
||||
* @param $title
|
||||
* @param $route
|
||||
* @param false $active
|
||||
* 处理最近访问链接
|
||||
*/
|
||||
private function addLink($title, $route, bool $active = false)
|
||||
private function handleHistoryLinks(): array
|
||||
{
|
||||
$permissionRoute = str_replace('.', '_', $route);
|
||||
if ($this->adminUser->cannot($permissionRoute) && $route != 'home.index') {
|
||||
return;
|
||||
$links = [];
|
||||
$histories = $this->getHistoryRoutesFromSession();
|
||||
foreach ($histories as $history) {
|
||||
$routeName = str_replace('admin.', '', $history);
|
||||
$permissionRoute = str_replace('.', '_', $routeName);
|
||||
|
||||
if (stripos($routeName, 'plugins.') !== false) {
|
||||
$type = str_replace('plugins.', '', $routeName);
|
||||
if ($type == 'index') {
|
||||
$title = trans("admin/common.{$permissionRoute}");
|
||||
} else {
|
||||
$title = trans("admin/plugin.{$type}");
|
||||
}
|
||||
} else {
|
||||
$title = trans("admin/common.{$permissionRoute}");
|
||||
}
|
||||
|
||||
$url = admin_route($route);
|
||||
$this->links[] = [
|
||||
'title' => $title,
|
||||
if (stripos($title, 'admin/common.') !== false) {
|
||||
$tempRouteName = str_replace('s.index', '', $routeName);
|
||||
$title = trans("admin/common.{$tempRouteName}");
|
||||
}
|
||||
|
||||
try {
|
||||
$url = admin_route($routeName);
|
||||
} catch (\Exception $e) {
|
||||
$url = '';
|
||||
}
|
||||
if (empty($url)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$links[] = [
|
||||
'route' => $routeName,
|
||||
'url' => $url,
|
||||
'active' => $active,
|
||||
'title' => $title,
|
||||
];
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 session 获取最近访问的链接
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getHistoryRoutesFromSession(): array
|
||||
{
|
||||
$histories = session('histories', []);
|
||||
|
||||
$currentRoute = request()->route()->getName();
|
||||
$routeName = str_replace('admin.', '', $currentRoute);
|
||||
|
||||
if (in_array($routeName, ['edit.locale', 'home.menus'])) {
|
||||
return $histories;
|
||||
}
|
||||
|
||||
array_unshift($histories, $currentRoute);
|
||||
$histories = array_slice(array_unique($histories), 0, 6);
|
||||
session(['histories' => $histories]);
|
||||
|
||||
return $histories;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,17 +3,23 @@
|
|||
namespace Beike\Admin\View\Components;
|
||||
|
||||
use Beike\Models\AdminUser;
|
||||
use Illuminate\Support\Str;
|
||||
use Beike\Plugin\Plugin;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class Sidebar extends Component
|
||||
{
|
||||
public array $links = [];
|
||||
|
||||
public ?array $currentLink;
|
||||
|
||||
private string $adminName;
|
||||
|
||||
private ?string $routeNameWithPrefix;
|
||||
|
||||
private ?string $currentRouteName;
|
||||
|
||||
private ?string $currentPrefix;
|
||||
|
||||
private ?AdminUser $adminUser;
|
||||
|
||||
/**
|
||||
|
|
@ -24,8 +30,13 @@ class Sidebar extends Component
|
|||
public function __construct()
|
||||
{
|
||||
$this->adminName = admin_name();
|
||||
$this->routeNameWithPrefix = request()->route()->getName();
|
||||
$this->adminUser = current_user();
|
||||
|
||||
$this->routeNameWithPrefix = request()->route()->getName();
|
||||
$this->currentRouteName = str_replace($this->adminName . '.', '', $this->routeNameWithPrefix);
|
||||
|
||||
$routeData = explode('.', $this->currentRouteName);
|
||||
$this->currentPrefix = $routeData[0] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -35,81 +46,168 @@ class Sidebar extends Component
|
|||
*/
|
||||
public function render()
|
||||
{
|
||||
$adminName = $this->adminName;
|
||||
$routeNameWithPrefix = request()->route()->getName();
|
||||
$routeName = str_replace($adminName . '.', '', $routeNameWithPrefix);
|
||||
|
||||
if (Str::startsWith($routeName, $this->getHomeSubPrefix())) {
|
||||
$routes = $this->getHomeSubRoutes();
|
||||
foreach ($routes as $route) {
|
||||
$this->addLink($route, $this->equalRoute($route['route']), (bool) ($route['blank'] ?? false), $route['hide_mobile'] ?? 0);
|
||||
}
|
||||
} elseif (Str::startsWith($routeName, $this->getProductSubPrefix())) {
|
||||
$routes = $this->getProductSubRoutes();
|
||||
foreach ($routes as $route) {
|
||||
$this->addLink($route, $this->equalRoute($route['route']), (bool) ($route['blank'] ?? false), $route['hide_mobile'] ?? 0);
|
||||
}
|
||||
} elseif (Str::startsWith($routeName, $this->getCustomerSubPrefix())) {
|
||||
$routes = $this->getCustomerSubRoutes();
|
||||
foreach ($routes as $route) {
|
||||
$this->addLink($route, $this->equalRoute($route['route']), (bool) ($route['blank'] ?? false), $route['hide_mobile'] ?? 0);
|
||||
}
|
||||
} elseif (Str::startsWith($routeName, $this->getOrderSubPrefix())) {
|
||||
$routes = $this->getOrderSubRoutes();
|
||||
foreach ($routes as $route) {
|
||||
$this->addLink($route, $this->equalRoute($route['route']), (bool) ($route['blank'] ?? false), $route['hide_mobile'] ?? 0);
|
||||
}
|
||||
} elseif (Str::startsWith($routeName, $this->getPageSubPrefix())) {
|
||||
$routes = $this->getPageSubRoutes();
|
||||
foreach ($routes as $route) {
|
||||
$this->addLink($route, $this->equalRoute($route['route']), (bool) ($route['blank'] ?? false), $route['hide_mobile'] ?? 0);
|
||||
}
|
||||
} elseif (Str::startsWith($routeName, $this->getSettingSubPrefix())) {
|
||||
$routes = $this->getSettingSubRoutes();
|
||||
foreach ($routes as $route) {
|
||||
$this->addLink($route, $this->equalRoute($route['route']), (bool) ($route['blank'] ?? false), $route['hide_mobile'] ?? 0);
|
||||
}
|
||||
} elseif (Str::startsWith($routeName, $this->getInquirySubPrefix())) {
|
||||
$routes = $this->getInquirySubRoutes();
|
||||
foreach ($routes as $route) {
|
||||
$this->addLink($route, $this->equalRoute($route['route']), (bool) ($route['blank'] ?? false), $route['hide_mobile'] ?? 0);
|
||||
}
|
||||
}
|
||||
$this->links = $this->getMenus();
|
||||
$this->handleMenus();
|
||||
$this->currentLink = $this->getCurrentLink();
|
||||
|
||||
return view('admin::components.sidebar');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加左侧菜单链接
|
||||
* 返回所有菜单
|
||||
* 小图标地址 https://icons.getbootstrap.com/
|
||||
*
|
||||
* @param $routeData
|
||||
* @param $active
|
||||
* @param false $newWindow
|
||||
* @param int $hide_mobile
|
||||
* @return mixed
|
||||
*/
|
||||
private function addLink($routeData, $active, bool $newWindow = false, int $hide_mobile = 0)
|
||||
private function getMenus(): mixed
|
||||
{
|
||||
$route = $routeData['route'];
|
||||
$icon = $routeData['icon'] ?? '';
|
||||
$title = $routeData['title'] ?? '';
|
||||
|
||||
$permissionRoute = str_replace('.', '_', $route);
|
||||
if ($this->adminUser->cannot($permissionRoute)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($title)) {
|
||||
$title = trans("admin/common.{$permissionRoute}");
|
||||
}
|
||||
$url = admin_route($route);
|
||||
$this->links[] = [
|
||||
'title' => $title,
|
||||
'url' => $url,
|
||||
'icon' => $icon,
|
||||
'active' => $active,
|
||||
'hide_mobile' => $hide_mobile,
|
||||
'new_window' => $newWindow,
|
||||
$menus = [
|
||||
[
|
||||
'route' => 'home.index',
|
||||
'title' => trans('admin/common.home'),
|
||||
'icon' => 'bi bi-house',
|
||||
'prefixes' => $this->getHomeSubPrefix(),
|
||||
],
|
||||
[
|
||||
'route' => 'orders.index',
|
||||
'title' => trans('admin/common.order'),
|
||||
'icon' => 'bi bi-clipboard-check',
|
||||
'prefixes' => $this->getOrderSubPrefix(),
|
||||
'children' => $this->getOrderSubRoutes(),
|
||||
],
|
||||
[
|
||||
'route' => 'products.index',
|
||||
'title' => trans('admin/common.product'),
|
||||
'icon' => 'bi bi-box-seam',
|
||||
'prefixes' => $this->getProductSubPrefix(),
|
||||
'children' => $this->getProductSubRoutes(),
|
||||
],
|
||||
[
|
||||
'route' => 'customers.index',
|
||||
'title' => trans('admin/common.customer'),
|
||||
'icon' => 'bi bi-person-circle',
|
||||
'prefixes' => $this->getCustomerSubPrefix(),
|
||||
'children' => $this->getCustomerSubRoutes(),
|
||||
],
|
||||
[
|
||||
'route' => 'pages.index',
|
||||
'title' => trans('admin/common.page'),
|
||||
'icon' => 'bi bi-file-earmark-text',
|
||||
'prefixes' => $this->getPageSubPrefix(),
|
||||
'children' => $this->getPageSubRoutes(),
|
||||
],
|
||||
[
|
||||
'route' => 'theme.index',
|
||||
'title' => trans('admin/common.design'),
|
||||
'icon' => 'bi bi-palette',
|
||||
'prefixes' => $this->getDesignSubPrefix(),
|
||||
'children' => $this->getDesignSubRoutes(),
|
||||
],
|
||||
[
|
||||
'route' => 'plugins.index',
|
||||
'title' => trans('admin/common.plugin'),
|
||||
'icon' => 'bi bi-shop',
|
||||
'prefixes' => $this->getPluginSubPrefix(),
|
||||
'children' => $this->getPluginSubRoutes(),
|
||||
],
|
||||
[
|
||||
'route' => 'settings.index',
|
||||
'title' => trans('admin/common.setting'),
|
||||
'icon' => 'bi bi-gear',
|
||||
'prefixes' => $this->getSettingSubPrefix(),
|
||||
'children' => $this->getSettingSubRoutes(),
|
||||
],
|
||||
[
|
||||
'route' => 'inquiry.index',
|
||||
'title' => trans('admin/common.inquiry'),
|
||||
'icon' => 'bi bi-gear',
|
||||
'prefixes' => $this->getInquirySubPrefix(),
|
||||
'children' => $this->getInquirySubRoutes(),
|
||||
],
|
||||
];
|
||||
|
||||
return hook_filter('admin.components.sidebar.menus', $menus);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取二级菜单
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private function getCurrentLink(): array|null
|
||||
{
|
||||
foreach ($this->links as $link) {
|
||||
$prefixes = $link['prefixes'] ?? [];
|
||||
if ($prefixes && in_array($this->currentPrefix, $prefixes)) {
|
||||
return $link;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理是否选中等数据
|
||||
*/
|
||||
private function handleMenus()
|
||||
{
|
||||
foreach ($this->links as $index => $link) {
|
||||
$prefixes = $link['prefixes'] ?? [];
|
||||
if ($prefixes && in_array($this->currentPrefix, $prefixes)) {
|
||||
$this->links[$index]['active'] = true;
|
||||
} else {
|
||||
$this->links[$index]['active'] = false;
|
||||
}
|
||||
|
||||
$url = $link['url'] ?? '';
|
||||
if (empty($url)) {
|
||||
$this->links[$index]['url'] = admin_route($link['route']);
|
||||
}
|
||||
|
||||
$title = $link['title'] ?? '';
|
||||
if (empty($title)) {
|
||||
$permissionRoute = str_replace('.', '_', $this->currentRouteName);
|
||||
$this->links[$index]['title'] = trans("admin/common.{$permissionRoute}");
|
||||
}
|
||||
|
||||
if (! isset($link['blank'])) {
|
||||
$this->links[$index]['blank'] = false;
|
||||
}
|
||||
|
||||
$icon = $link['icon'] ?? '';
|
||||
if (empty($icon)) {
|
||||
$this->links[$index]['icon'] = 'bi bi-link-45deg';
|
||||
}
|
||||
|
||||
$children = $link['children'] ?? [];
|
||||
if ($children) {
|
||||
foreach ($children as $key => $item) {
|
||||
$childPrefixes = $item['prefixes'] ?? [];
|
||||
$excludes = $item['excludes'] ?? [];
|
||||
if ($prefixes && in_array($this->currentPrefix, $childPrefixes)
|
||||
&& (! $excludes || ! in_array($this->currentRouteName, $excludes))) {
|
||||
$this->links[$index]['children'][$key]['active'] = true;
|
||||
} else {
|
||||
$this->links[$index]['children'][$key]['active'] = false;
|
||||
}
|
||||
|
||||
$url = $item['url'] ?? '';
|
||||
if (empty($url)) {
|
||||
$this->links[$index]['children'][$key]['url'] = admin_route($item['route']);
|
||||
}
|
||||
|
||||
$title = $item['title'] ?? '';
|
||||
if (empty($title)) {
|
||||
$permissionRoute = str_replace('.', '_', $item['route']);
|
||||
$this->links[$index]['children'][$key]['title'] = trans("admin/common.{$permissionRoute}");
|
||||
}
|
||||
|
||||
if (! isset($item['blank'])) {
|
||||
$this->links[$index]['children'][$key]['blank'] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -117,7 +215,7 @@ class Sidebar extends Component
|
|||
*/
|
||||
private function getHomeSubPrefix()
|
||||
{
|
||||
$prefix = ['home.'];
|
||||
$prefix = ['home'];
|
||||
|
||||
return hook_filter('admin.sidebar.home.prefix', $prefix);
|
||||
}
|
||||
|
|
@ -127,7 +225,7 @@ class Sidebar extends Component
|
|||
*/
|
||||
private function getProductSubPrefix()
|
||||
{
|
||||
$prefix = ['products.', 'categories.', 'brands.', 'attribute_groups.', 'attributes.'];
|
||||
$prefix = ['products', 'multi_filter', 'categories', 'brands', 'attribute_groups', 'attributes'];
|
||||
|
||||
return hook_filter('admin.sidebar.product.prefix', $prefix);
|
||||
}
|
||||
|
|
@ -137,7 +235,7 @@ class Sidebar extends Component
|
|||
*/
|
||||
private function getCustomerSubPrefix()
|
||||
{
|
||||
$prefix = ['customers.', 'customer_groups.'];
|
||||
$prefix = ['customers', 'customer_groups'];
|
||||
|
||||
return hook_filter('admin.sidebar.customer.prefix', $prefix);
|
||||
}
|
||||
|
|
@ -147,7 +245,7 @@ class Sidebar extends Component
|
|||
*/
|
||||
private function getOrderSubPrefix()
|
||||
{
|
||||
$prefix = ['orders.', 'rmas.', 'rma_reasons.'];
|
||||
$prefix = ['orders', 'rmas', 'rma_reasons'];
|
||||
|
||||
return hook_filter('admin.sidebar.order.prefix', $prefix);
|
||||
}
|
||||
|
|
@ -157,7 +255,7 @@ class Sidebar extends Component
|
|||
*/
|
||||
private function getPageSubPrefix()
|
||||
{
|
||||
$prefix = ['pages.', 'page_categories.'];
|
||||
$prefix = ['pages', 'page_categories'];
|
||||
|
||||
return hook_filter('admin.sidebar.page.prefix', $prefix);
|
||||
}
|
||||
|
|
@ -172,12 +270,35 @@ class Sidebar extends Component
|
|||
return hook_filter('admin.sidebar.page.prefix', $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后台设计子页面路由前缀列表
|
||||
*/
|
||||
private function getDesignSubPrefix()
|
||||
{
|
||||
$prefix = ['theme', 'design_menu'];
|
||||
|
||||
return hook_filter('admin.sidebar.design.prefix', $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后台设计子页面路由前缀列表
|
||||
*/
|
||||
private function getPluginSubPrefix()
|
||||
{
|
||||
$prefix = ['plugins', 'marketing'];
|
||||
|
||||
return hook_filter('admin.sidebar.plugin.prefix', $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后台系统设置子页面路由前缀列表
|
||||
*/
|
||||
private function getSettingSubPrefix()
|
||||
{
|
||||
$prefix = ['settings.', 'admin_users.', 'admin_roles.', 'plugins.', 'theme.', 'marketing.', 'tax_classes', 'tax_rates', 'regions', 'currencies', 'languages', 'design_menu', 'countries', 'zones'];
|
||||
$prefix = [
|
||||
'settings', 'admin_users', 'admin_roles', 'tax_classes', 'tax_rates',
|
||||
'regions', 'currencies', 'languages', 'countries', 'zones', 'account',
|
||||
];
|
||||
|
||||
return hook_filter('admin.sidebar.setting.prefix', $prefix);
|
||||
}
|
||||
|
|
@ -187,14 +308,7 @@ class Sidebar extends Component
|
|||
*/
|
||||
public function getHomeSubRoutes()
|
||||
{
|
||||
$routes = [
|
||||
['route' => 'design.index', 'icon' => 'fa fa-tachometer-alt', 'blank' => 1, 'hide_mobile' => 1],
|
||||
['route' => 'design_footer.index', 'icon' => 'fa fa-tachometer-alt', 'blank' => 1, 'hide_mobile' => 1],
|
||||
['route' => 'design_menu.index', 'icon' => 'fa fa-tachometer-alt', 'hide_mobile' => 1],
|
||||
['route' => 'languages.index', 'icon' => 'fa fa-tachometer-alt', 'hide_mobile' => 1],
|
||||
['route' => 'currencies.index', 'icon' => 'fa fa-tachometer-alt', 'hide_mobile' => 1],
|
||||
['route' => 'plugins.index', 'icon' => 'fa fa-tachometer-alt', 'hide_mobile' => 1],
|
||||
];
|
||||
$routes = [];
|
||||
|
||||
return hook_filter('admin.sidebar.home_routes', $routes);
|
||||
}
|
||||
|
|
@ -205,12 +319,13 @@ class Sidebar extends Component
|
|||
public function getProductSubRoutes()
|
||||
{
|
||||
$routes = [
|
||||
['route' => 'categories.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'products.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'brands.index', 'icon' => 'fa fa-tachometer-alt', 'hide_mobile' => 1],
|
||||
['route' => 'attribute_groups.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'attributes.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'products.trashed', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'products.index', 'prefixes' => ['products'], 'excludes' => ['products.trashed']],
|
||||
['route' => 'categories.index', 'prefixes' => ['categories']],
|
||||
['route' => 'brands.index', 'prefixes' => ['brands']],
|
||||
['route' => 'attribute_groups.index', 'prefixes' => ['attribute_groups']],
|
||||
['route' => 'attributes.index', 'prefixes' => ['attributes']],
|
||||
['route' => 'multi_filter.index', 'prefixes' => ['multi_filter']],
|
||||
['route' => 'products.trashed', 'prefixes' => ['products'], 'excludes' => ['products.index', 'products.edit']],
|
||||
];
|
||||
|
||||
return hook_filter('admin.sidebar.product_routes', $routes);
|
||||
|
|
@ -222,9 +337,9 @@ class Sidebar extends Component
|
|||
public function getCustomerSubRoutes()
|
||||
{
|
||||
$routes = [
|
||||
['route' => 'customers.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'customer_groups.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'customers.trashed', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'customers.index', 'prefixes' => ['customers'], 'excludes' => ['customers.trashed']],
|
||||
['route' => 'customer_groups.index', 'prefixes' => ['customer_groups']],
|
||||
['route' => 'customers.trashed', 'prefixes' => ['customers'], 'excludes' => ['customers.index', 'customers.edit']],
|
||||
];
|
||||
|
||||
return hook_filter('admin.sidebar.customer_routes', $routes);
|
||||
|
|
@ -236,9 +351,9 @@ class Sidebar extends Component
|
|||
public function getOrderSubRoutes()
|
||||
{
|
||||
$routes = [
|
||||
['route' => 'orders.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'rmas.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'rma_reasons.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'orders.index', 'prefixes' => ['orders']],
|
||||
['route' => 'rmas.index', 'prefixes' => ['rmas']],
|
||||
['route' => 'rma_reasons.index', 'prefixes' => ['rma_reasons']],
|
||||
];
|
||||
|
||||
return hook_filter('admin.sidebar.order_routes', $routes);
|
||||
|
|
@ -251,8 +366,8 @@ class Sidebar extends Component
|
|||
public function getPageSubRoutes()
|
||||
{
|
||||
$routes = [
|
||||
['route' => 'page_categories.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'pages.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'pages.index', 'prefixes' => ['pages']],
|
||||
['route' => 'page_categories.index', 'prefixes' => ['page_categories']],
|
||||
];
|
||||
|
||||
return hook_filter('admin.sidebar.pages_routes', $routes);
|
||||
|
|
@ -271,6 +386,46 @@ class Sidebar extends Component
|
|||
return hook_filter('admin.sidebar.pages_routes', $routes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设计子页面路由
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDesignSubRoutes()
|
||||
{
|
||||
$routes = [
|
||||
['route' => 'theme.index', 'prefixes' => ['theme'], 'hide_mobile' => true],
|
||||
['route' => 'design_menu.index', 'prefixes' => ['design_menu'], 'hide_mobile' => 1],
|
||||
['route' => 'design.index', 'prefixes' => ['design'], 'blank' => true, 'hide_mobile' => true],
|
||||
['route' => 'design_footer.index', 'prefixes' => ['design_footer'], 'blank' => true, 'hide_mobile' => true],
|
||||
];
|
||||
|
||||
return hook_filter('admin.sidebar.design_routes', $routes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件子页面路由
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPluginSubRoutes()
|
||||
{
|
||||
$types = collect(Plugin::TYPES);
|
||||
$types = $types->map(function ($item) {
|
||||
return 'plugins.' . $item;
|
||||
});
|
||||
|
||||
$routes[] = ['route' => 'plugins.index', 'prefixes' => ['plugins'], 'excludes' => $types->toArray()];
|
||||
|
||||
$originTypes = $types->push('plugins.index', 'plugins.edit')->push();
|
||||
foreach (Plugin::TYPES as $type) {
|
||||
$types = $originTypes->reject("plugins.{$type}");
|
||||
$routes[] = ['route' => "plugins.{$type}", 'prefixes' => ['plugins'], 'title' => trans("admin/plugin.{$type}"), 'excludes' => $types->toArray()];
|
||||
}
|
||||
|
||||
$routes[] = ['route' => 'marketing.index', 'prefixes' => ['marketing']];
|
||||
|
||||
return hook_filter('admin.sidebar.plugins_routes', $routes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统设置子页面路由
|
||||
* @return mixed
|
||||
|
|
@ -278,36 +433,18 @@ class Sidebar extends Component
|
|||
public function getSettingSubRoutes()
|
||||
{
|
||||
$routes = [
|
||||
['route' => 'settings.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'admin_users.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'plugins.index', 'icon' => 'fa fa-tachometer-alt', 'hide_mobile' => 1],
|
||||
['route' => 'theme.index', 'icon' => 'fa fa-tachometer-alt', 'hide_mobile' => 1],
|
||||
['route' => 'marketing.index', 'icon' => 'fa fa-tachometer-alt', 'hide_mobile' => 1],
|
||||
['route' => 'regions.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'tax_rates.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'tax_classes.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'currencies.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'languages.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'countries.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'zones.index', 'icon' => 'fa fa-tachometer-alt'],
|
||||
['route' => 'design.index', 'icon' => 'fa fa-tachometer-alt', 'blank' => true, 'hide_mobile' => 1],
|
||||
['route' => 'design_footer.index', 'icon' => 'fa fa-tachometer-alt', 'blank' => true, 'hide_mobile' => 1],
|
||||
['route' => 'design_menu.index', 'icon' => 'fa fa-tachometer-alt', 'hide_mobile' => 1],
|
||||
['route' => 'settings.index', 'prefixes' => ['settings']],
|
||||
['route' => 'account.index', 'prefixes' => ['account']],
|
||||
['route' => 'admin_users.index', 'prefixes' => ['admin_users', 'admin_roles']],
|
||||
['route' => 'regions.index', 'prefixes' => ['regions']],
|
||||
['route' => 'tax_rates.index', 'prefixes' => ['tax_rates']],
|
||||
['route' => 'tax_classes.index', 'prefixes' => ['tax_classes']],
|
||||
['route' => 'currencies.index', 'prefixes' => ['currencies']],
|
||||
['route' => 'languages.index', 'prefixes' => ['languages']],
|
||||
['route' => 'countries.index', 'prefixes' => ['countries']],
|
||||
['route' => 'zones.index', 'prefixes' => ['zones']],
|
||||
];
|
||||
|
||||
return hook_filter('admin.sidebar.setting_routes', $routes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为当前访问路由
|
||||
*
|
||||
* @param $routeName
|
||||
* @return bool
|
||||
*/
|
||||
private function equalRoute($routeName): bool
|
||||
{
|
||||
$currentRouteName = str_replace($this->adminName . '.', '', $this->routeNameWithPrefix);
|
||||
|
||||
return $routeName == $currentRouteName;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
|
||||
return [
|
||||
'api_url' => env('BEIKE_API_URL', 'https://beikeshop.com'),
|
||||
'version' => '1.3.4',
|
||||
'build' => '20230324',
|
||||
'version' => '1.3.8',
|
||||
'build' => '20230719',
|
||||
|
||||
'admin_name' => env('ADMIN_NAME'),
|
||||
'force_url_https' => env('APP_FORCE_HTTPS', false),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* CartException.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-06-02 17:08:18
|
||||
* @modified 2023-06-02 17:08:18
|
||||
*/
|
||||
|
||||
namespace Beike\Exceptions;
|
||||
|
||||
class CartException extends \Exception
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* InvalidException.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-05-25 14:48:12
|
||||
* @modified 2023-05-25 14:48:12
|
||||
*/
|
||||
|
||||
namespace Beike\Exceptions;
|
||||
|
||||
class InvalidException extends \Exception
|
||||
{
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ use Beike\Repositories\CurrencyRepo;
|
|||
use Beike\Repositories\LanguageRepo;
|
||||
use Beike\Services\CurrencyService;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
|
@ -209,7 +210,12 @@ function equal_route($routeName): bool
|
|||
*/
|
||||
function current_user(): ?AdminUser
|
||||
{
|
||||
return auth()->guard(AdminUser::AUTH_GUARD)->user();
|
||||
$user = auth()->guard(AdminUser::AUTH_GUARD)->user();
|
||||
if (empty($user)) {
|
||||
$user = registry('admin_user');
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -515,25 +521,28 @@ function quantity_format($quantity)
|
|||
/**
|
||||
* 返回json序列化结果
|
||||
*/
|
||||
function json_success($message, $data = []): array
|
||||
function json_success($message, $data = [])
|
||||
{
|
||||
return [
|
||||
'status' => 'success',
|
||||
'message' => $message,
|
||||
'data' => $data,
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回json序列化结果
|
||||
*/
|
||||
function json_fail($message, $data = []): array
|
||||
function json_fail($message, $data = [], $status = 422): JsonResponse
|
||||
{
|
||||
return [
|
||||
$data = [
|
||||
'status' => 'fail',
|
||||
'message' => $message,
|
||||
'data' => $data,
|
||||
];
|
||||
|
||||
return response()->json($data, $status);
|
||||
}
|
||||
|
||||
if (! function_exists('sub_string')) {
|
||||
|
|
@ -768,3 +777,42 @@ function list_sort_by($list, $field, $sortby='asc') {
|
|||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param $value
|
||||
* @param bool $force
|
||||
*/
|
||||
function register($key, $value, bool $force = false)
|
||||
{
|
||||
\Beike\Libraries\Registry::set($key, $value, $force);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
function registry($key, $default = null): mixed
|
||||
{
|
||||
return \Beike\Libraries\Registry::get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check domain ha license.
|
||||
* 删除版权信息, 请先购买授权 https://beikeshop.com/vip/subscription
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function check_license(): bool
|
||||
{
|
||||
$configLicenceCode = system_setting('base.license_code');
|
||||
$appDomain = config('app.url');
|
||||
$domain = new \Utopia\Domains\Domain($appDomain);
|
||||
$registerDomain = $domain->getRegisterable();
|
||||
if (empty($registerDomain)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $configLicenceCode == md5(mb_substr(md5($registerDomain), 2, 8));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,6 @@ class Hook
|
|||
return $this->get($hook, $params, $callback, $htmlContent);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $hook
|
||||
* @param array $params
|
||||
|
|
@ -114,11 +113,15 @@ class Hook
|
|||
$priority = null;
|
||||
}
|
||||
|
||||
if (isset($this->watch[$hook][$priority])) {
|
||||
$priority++;
|
||||
}
|
||||
|
||||
$this->watch[$hook][$priority] = [
|
||||
'function' => $function,
|
||||
'caller' => [
|
||||
//'file' => $caller['file'],
|
||||
//'line' => $caller['line'],
|
||||
'file' => $caller['file'],
|
||||
'line' => $caller['line'],
|
||||
'class' => Arr::get($caller, 'class'),
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace Beike\Hook;
|
|||
use Beike\Hook\Console\HookListeners;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class HookServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
|
@ -19,7 +20,6 @@ class HookServiceProvider extends ServiceProvider
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
public function boot()
|
||||
{
|
||||
$this->bootHookDirectives();
|
||||
|
|
@ -37,6 +37,7 @@ class HookServiceProvider extends ServiceProvider
|
|||
$parameters = explode(',', $parameter);
|
||||
|
||||
$name = trim($parameters[0], "'");
|
||||
$definedVars = $this->parseParameters($parameters);
|
||||
|
||||
return ' <?php
|
||||
$__definedVars = (get_defined_vars()["__data"]);
|
||||
|
|
@ -44,6 +45,7 @@ class HookServiceProvider extends ServiceProvider
|
|||
{
|
||||
$__definedVars = [];
|
||||
}
|
||||
'. $definedVars .'
|
||||
$output = \Hook::getHook("' . $name . '",["data"=>$__definedVars],function($data) { return null; });
|
||||
if ($output)
|
||||
echo $output;
|
||||
|
|
@ -51,7 +53,6 @@ class HookServiceProvider extends ServiceProvider
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加 blade wrapper hook 标签
|
||||
*
|
||||
|
|
@ -86,4 +87,23 @@ class HookServiceProvider extends ServiceProvider
|
|||
?>';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse parameters from Blade
|
||||
*
|
||||
* @param $parameters
|
||||
* @return string
|
||||
*/
|
||||
protected function parseParameters($parameters):string
|
||||
{
|
||||
$definedVars = '';
|
||||
foreach ($parameters as $paraItem) {
|
||||
$paraItem = trim($paraItem);
|
||||
if (Str::startsWith($paraItem,'$')) {
|
||||
$paraKey = trim($paraItem, '$');
|
||||
$definedVars .= '$__definedVars["'.$paraKey.'"] = $'.$paraKey.';';
|
||||
}
|
||||
}
|
||||
return $definedVars;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Beike\Installer\Controllers;
|
||||
|
||||
use Illuminate\Routing\Controller;
|
||||
|
||||
class BaseController extends Controller
|
||||
{
|
||||
protected function checkInstalled()
|
||||
{
|
||||
if (installed()) {
|
||||
exit('Already installed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,11 +5,10 @@ namespace Beike\Installer\Controllers;
|
|||
use Beike\Admin\Repositories\AdminUserRepo;
|
||||
use Beike\Installer\Helpers\DatabaseManager;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class DatabaseController extends Controller
|
||||
class DatabaseController extends BaseController
|
||||
{
|
||||
/**
|
||||
* @var DatabaseManager
|
||||
|
|
@ -31,6 +30,7 @@ class DatabaseController extends Controller
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->checkInstalled();
|
||||
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
|
||||
$rows = DB::select('SHOW TABLES');
|
||||
$database = config('database.connections.mysql.database');
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@
|
|||
namespace Beike\Installer\Controllers;
|
||||
|
||||
use Beike\Installer\Helpers\EnvironmentManager;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Routing\Redirector;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class EnvironmentController extends Controller
|
||||
class EnvironmentController extends BaseController
|
||||
{
|
||||
/**
|
||||
* @var EnvironmentManager
|
||||
|
|
@ -32,6 +32,7 @@ class EnvironmentController extends Controller
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->checkInstalled();
|
||||
$steps = 4;
|
||||
|
||||
return view('installer::environment-wizard', compact('steps'));
|
||||
|
|
@ -46,6 +47,7 @@ class EnvironmentController extends Controller
|
|||
*/
|
||||
public function saveWizard(Request $request, Redirector $redirect): RedirectResponse
|
||||
{
|
||||
$this->checkInstalled();
|
||||
$rules = config('installer.environment.form.rules');
|
||||
$messages = [
|
||||
'environment_custom.required_if' => trans('installer::installer_messages.environment.name_required'),
|
||||
|
|
@ -74,10 +76,11 @@ class EnvironmentController extends Controller
|
|||
* 数据库信息检测
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
* @return JsonResponse|array
|
||||
*/
|
||||
public function validateDatabase(Request $request): array
|
||||
public function validateDatabase(Request $request): JsonResponse|array
|
||||
{
|
||||
$this->checkInstalled();
|
||||
$rules = config('installer.environment.form.rules');
|
||||
$messages = [
|
||||
'environment_custom.required_if' => trans('installer::installer_messages.environment.name_required'),
|
||||
|
|
@ -108,6 +111,7 @@ class EnvironmentController extends Controller
|
|||
*/
|
||||
private function checkDatabaseConnection(Request $request): bool|array
|
||||
{
|
||||
$this->checkInstalled();
|
||||
$connection = $request->input('database_connection');
|
||||
|
||||
$settings = config("database.connections.$connection");
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@ namespace Beike\Installer\Controllers;
|
|||
use Beike\Installer\Helpers\EnvironmentManager;
|
||||
use Beike\Installer\Helpers\FinalInstallManager;
|
||||
use Beike\Installer\Helpers\InstalledFileManager;
|
||||
use Illuminate\Routing\Controller;
|
||||
|
||||
class FinalController extends Controller
|
||||
class FinalController extends BaseController
|
||||
{
|
||||
/**
|
||||
* Update installed file and display finished view.
|
||||
|
|
@ -19,6 +18,8 @@ class FinalController extends Controller
|
|||
*/
|
||||
public function index(InstalledFileManager $fileManager, FinalInstallManager $finalInstall, EnvironmentManager $environment)
|
||||
{
|
||||
$this->checkInstalled();
|
||||
|
||||
$finalMessages = $finalInstall->runFinal();
|
||||
$finalStatusMessage = $fileManager->update();
|
||||
$finalEnvFile = $environment->getEnvContent();
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@
|
|||
namespace Beike\Installer\Controllers;
|
||||
|
||||
use Beike\Installer\Helpers\PermissionsChecker;
|
||||
use Illuminate\Routing\Controller;
|
||||
|
||||
class PermissionsController extends Controller
|
||||
class PermissionsController extends BaseController
|
||||
{
|
||||
/**
|
||||
* @var PermissionsChecker
|
||||
|
|
@ -27,6 +26,7 @@ class PermissionsController extends Controller
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->checkInstalled();
|
||||
$permissions = $this->permissions->check(
|
||||
config('installer.permissions')
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@
|
|||
namespace Beike\Installer\Controllers;
|
||||
|
||||
use Beike\Installer\Helpers\RequirementsChecker;
|
||||
use Illuminate\Routing\Controller;
|
||||
|
||||
class RequirementsController extends Controller
|
||||
class RequirementsController extends BaseController
|
||||
{
|
||||
/**
|
||||
* @var RequirementsChecker
|
||||
|
|
@ -27,6 +26,7 @@ class RequirementsController extends Controller
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->checkInstalled();
|
||||
$phpSupportInfo = $this->requirements->checkPHPversion(
|
||||
config('installer.core.minPhpVersion')
|
||||
);
|
||||
|
|
|
|||
|
|
@ -11,10 +11,9 @@
|
|||
|
||||
namespace Beike\Installer\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
|
||||
class WelcomeController extends Controller
|
||||
class WelcomeController extends BaseController
|
||||
{
|
||||
private $languages = [
|
||||
'zh_cn' => '简体中文',
|
||||
|
|
@ -23,10 +22,7 @@ class WelcomeController extends Controller
|
|||
|
||||
public function index()
|
||||
{
|
||||
if (installed()) {
|
||||
exit('Already installed');
|
||||
}
|
||||
|
||||
$this->checkInstalled();
|
||||
$data['languages'] = $this->languages;
|
||||
$data['locale'] = $_COOKIE['locale'] ?? 'zh_cn';
|
||||
$data['steps'] = 1;
|
||||
|
|
|
|||
|
|
@ -230,7 +230,12 @@
|
|||
$('.database-loading').addClass('d-none');
|
||||
},
|
||||
success: function(json) {
|
||||
if (json.status == 'fail') {
|
||||
$('.database-link-wrap input').addClass('is-valid')
|
||||
$('.title-status .text-success').removeClass('d-none')
|
||||
$('.admin-data-wrap').removeClass('d-none')
|
||||
},
|
||||
error: function(json) {
|
||||
json = json.responseJSON;
|
||||
var data = Object.keys(json.data);
|
||||
|
||||
data.forEach((e)=> {
|
||||
|
|
@ -247,13 +252,6 @@
|
|||
|
||||
$('.admin-data-wrap').addClass('d-none')
|
||||
}
|
||||
|
||||
if (json.status == 'success') {
|
||||
$('.database-link-wrap input').addClass('is-valid')
|
||||
$('.title-status .text-success').removeClass('d-none')
|
||||
$('.admin-data-wrap').removeClass('d-none')
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -15,8 +15,7 @@
|
|||
<script src="{{ asset('vendor/jquery/jquery-3.6.0.min.js') }}"></script>
|
||||
<script src="{{ asset('vendor/layer/3.5.1/layer.js') }}"></script>
|
||||
<link rel="shortcut icon" href="{{ asset('/image/favicon.png') }}">
|
||||
{{-- <script src="{{ asset('vendor/bootstrap/5.1.3/js/bootstrap.min.js') }}"></script> --}}
|
||||
<script src="{{ asset('vendor/bootstrap/5.1.3/js/bootstrap.bundle.min.js') }}"></script>
|
||||
<script src="{{ asset('vendor/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
|
||||
<link rel="stylesheet" type="text/css" href="{{ asset('/install/css/app.css') }}">
|
||||
@yield('style')
|
||||
</head>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
$primary: #fd560f;
|
||||
|
||||
body {
|
||||
background-color: #f3f6f8;
|
||||
|
||||
.install-box {
|
||||
background-color: #f3f6f8;
|
||||
display: flex;
|
||||
|
|
@ -45,6 +47,10 @@ body {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table {
|
||||
--bs-table-bg: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@import "steps";
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ return [
|
|||
'PDO',
|
||||
'Tokenizer',
|
||||
'XML',
|
||||
'ZIP',
|
||||
'GD',
|
||||
'PDO_MYSQL',
|
||||
],
|
||||
'apache' => [
|
||||
'mod_rewrite',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
/**
|
||||
* Registry.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-04-20 16:29:54
|
||||
* @modified 2023-04-20 16:29:54
|
||||
*/
|
||||
|
||||
namespace Beike\Libraries;
|
||||
|
||||
class Registry
|
||||
{
|
||||
private array $data = [];
|
||||
|
||||
private static $registry;
|
||||
|
||||
public static function getSingleton(): self
|
||||
{
|
||||
if (self::$registry instanceof self) {
|
||||
return self::$registry;
|
||||
}
|
||||
|
||||
return self::$registry = new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($key, $default = null): mixed
|
||||
{
|
||||
return self::getSingleton()->getValue($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param $value
|
||||
* @param bool $force
|
||||
*/
|
||||
public static function set($key, $value, bool $force = false)
|
||||
{
|
||||
if (self::getSingleton()->has($key) && ! $force) {
|
||||
return;
|
||||
}
|
||||
self::getSingleton()->setValue($key, $value);
|
||||
}
|
||||
|
||||
public function destroy()
|
||||
{
|
||||
self::$registry = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param null $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue($key, $default = null): mixed
|
||||
{
|
||||
return $this->data[$key] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param $value
|
||||
*/
|
||||
public function setValue($key, $value)
|
||||
{
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key): bool
|
||||
{
|
||||
return isset($this->data[$key]);
|
||||
}
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ class Url
|
|||
return $value->url ?? '';
|
||||
} elseif ($type == 'page') {
|
||||
if (! $value instanceof \Beike\Models\Page) {
|
||||
$value = \Beike\Models\Page::query()->find($value);
|
||||
$value = \Beike\Models\Page::query()->where('active', 1)->find($value);
|
||||
}
|
||||
|
||||
return $value->url ?? '';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
/**
|
||||
* Weight.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author TL <mengwb@guangda.work>
|
||||
* @created 2023-03-21 16:29:54
|
||||
* @modified 2023-03-21 16:29:54
|
||||
*/
|
||||
|
||||
namespace Beike\Libraries;
|
||||
|
||||
class Weight
|
||||
{
|
||||
public const WEIGHT_CLASS = [
|
||||
'kg' => 0.001,
|
||||
'g' => 1,
|
||||
'oz' => 0.035,
|
||||
'lb' => 0.0022046,
|
||||
];
|
||||
|
||||
public const DEFAULT_CLASS = 'g';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public static function getWeightUnits(): array
|
||||
{
|
||||
return array_keys(self::WEIGHT_CLASS);
|
||||
}
|
||||
|
||||
public static function convert($weight, $from, $to = '')
|
||||
{
|
||||
if (! $to) {
|
||||
$to = self::DEFAULT_CLASS;
|
||||
}
|
||||
if (empty($weight)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $weight * self::WEIGHT_CLASS[$to] / self::WEIGHT_CLASS[$from];
|
||||
}
|
||||
}
|
||||
|
|
@ -4,11 +4,13 @@ namespace Beike\Models;
|
|||
|
||||
use Beike\Notifications\AdminForgottenNotification;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Foundation\Auth\User as AuthUser;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
use Tymon\JWTAuth\Contracts\JWTSubject;
|
||||
|
||||
class AdminUser extends AuthUser
|
||||
class AdminUser extends AuthUser implements JWTSubject
|
||||
{
|
||||
use HasFactory, HasRoles;
|
||||
use Notifiable;
|
||||
|
|
@ -17,6 +19,11 @@ class AdminUser extends AuthUser
|
|||
|
||||
protected $fillable = ['name', 'email', 'locale', 'password', 'active'];
|
||||
|
||||
public function tokens(): HasMany
|
||||
{
|
||||
return $this->hasMany(AdminUserToken::class);
|
||||
}
|
||||
|
||||
public function notifyVerifyCodeForForgotten($code)
|
||||
{
|
||||
$useQueue = system_setting('base.use_queue', true);
|
||||
|
|
@ -26,4 +33,24 @@ class AdminUser extends AuthUser
|
|||
$this->notifyNow(new AdminForgottenNotification($this, $code));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identifier that will be stored in the subject claim of the JWT.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getJWTIdentifier()
|
||||
{
|
||||
return $this->getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a key value array, containing any custom claims to be added to the JWT.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJWTCustomClaims()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/**
|
||||
* AdminUserToken.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-04-20 10:18:56
|
||||
* @modified 2023-04-20 10:18:56
|
||||
*/
|
||||
|
||||
namespace Beike\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class AdminUserToken extends Base
|
||||
{
|
||||
protected $fillable = ['admin_user_id', 'token'];
|
||||
|
||||
public function adminUser(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(AdminUser::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,8 +11,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Tymon\JWTAuth\Contracts\JWTSubject;
|
||||
|
||||
class Customer extends Authenticatable
|
||||
class Customer extends Authenticatable implements JWTSubject
|
||||
{
|
||||
use HasFactory;
|
||||
use SoftDeletes;
|
||||
|
|
@ -66,4 +67,24 @@ class Customer extends Authenticatable
|
|||
$this->notifyNow(new ForgottenNotification($this, $code));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identifier that will be stored in the subject claim of the JWT.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getJWTIdentifier()
|
||||
{
|
||||
return $this->getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a key value array, containing any custom claims to be added to the JWT.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getJWTCustomClaims()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,11 @@ class Order extends Base
|
|||
return $this->hasMany(OrderShipment::class);
|
||||
}
|
||||
|
||||
public function orderPayments(): HasMany
|
||||
{
|
||||
return $this->hasMany(OrderPayment::class);
|
||||
}
|
||||
|
||||
public function subTotal()
|
||||
{
|
||||
$totals = $this->orderTotals;
|
||||
|
|
@ -68,7 +73,9 @@ class Order extends Base
|
|||
|
||||
public function getStatusFormatAttribute()
|
||||
{
|
||||
return trans('order.' . $this->status);
|
||||
$statusMap = array_column(StateMachineService::getAllStatuses(), 'name', 'status');
|
||||
|
||||
return $statusMap[$this->status];
|
||||
}
|
||||
|
||||
public function getTotalFormatAttribute()
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
namespace Beike\Models;
|
||||
|
||||
use Beike\Services\StateMachineService;
|
||||
|
||||
class OrderHistory extends Base
|
||||
{
|
||||
protected $fillable = [
|
||||
|
|
@ -21,6 +23,8 @@ class OrderHistory extends Base
|
|||
|
||||
public function getStatusFormatAttribute()
|
||||
{
|
||||
return trans("order.{$this->status}");
|
||||
$statusMap = array_column(StateMachineService::getAllStatuses(), 'name', 'status');
|
||||
|
||||
return $statusMap[$this->status];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
/**
|
||||
* OrderPayment.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-05-25 10:02:52
|
||||
* @modified 2023-05-25 10:02:52
|
||||
*/
|
||||
|
||||
namespace Beike\Models;
|
||||
|
||||
class OrderPayment extends Base
|
||||
{
|
||||
protected $table = 'order_payments';
|
||||
|
||||
protected $fillable = [
|
||||
'order_id', 'transaction_id', 'request', 'response', 'callback', 'receipt',
|
||||
];
|
||||
}
|
||||
|
|
@ -11,12 +11,11 @@
|
|||
|
||||
namespace Beike\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
|
||||
class PageCategory extends Model
|
||||
class PageCategory extends Base
|
||||
{
|
||||
protected $table = 'page_categories';
|
||||
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@
|
|||
|
||||
namespace Beike\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class PageCategoryDescription extends Model
|
||||
class PageCategoryDescription extends Base
|
||||
{
|
||||
protected $table = 'page_category_descriptions';
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,9 @@
|
|||
|
||||
namespace Beike\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class PageProduct extends Model
|
||||
class PageProduct extends Base
|
||||
{
|
||||
protected $table = 'page_products';
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class Product extends Base
|
|||
use HasFactory;
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = ['images', 'video', 'position', 'brand_id', 'tax_class_id', 'active', 'variables', 'price_setting'];
|
||||
protected $fillable = ['images', 'video', 'position', 'brand_id', 'tax_class_id', 'weight', 'weight_class', 'active', 'variables', 'price_setting'];
|
||||
|
||||
protected $casts = [
|
||||
'active' => 'boolean',
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ class ProductSku extends Base
|
|||
$product = $this->product;
|
||||
$localeCode = locale();
|
||||
$variantLabel = '';
|
||||
|
||||
if(empty($product->variables)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
foreach ($product->variables as $index => $variable) {
|
||||
$valueIndex = $this->variants[$index];
|
||||
$variantName = $variable['name'][$localeCode] ?? '';
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@ use Illuminate\Support\Str;
|
|||
class Plugin implements Arrayable, \ArrayAccess
|
||||
{
|
||||
public const TYPES = [
|
||||
'shipping', // 配送方式
|
||||
'payment', // 支付方式
|
||||
'shipping', // 配送方式
|
||||
'theme', // 主题模板
|
||||
'feature', // 功能模块
|
||||
'total', // 订单金额
|
||||
'social', // 社交网络
|
||||
'feature', // 功能模块
|
||||
'language', // 语言翻译
|
||||
'theme', // 主题模板
|
||||
];
|
||||
|
||||
protected $type;
|
||||
|
|
@ -171,6 +171,13 @@ class Plugin implements Arrayable, \ArrayAccess
|
|||
$item['label'] = trans($languageKey);
|
||||
}
|
||||
|
||||
$descriptionKey = $item['description_key'] ?? '';
|
||||
$description = $item['description'] ?? '';
|
||||
if (empty($description) && $descriptionKey) {
|
||||
$languageKey = "{$this->dirName}::{$descriptionKey}";
|
||||
$item['description'] = trans($languageKey);
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
/**
|
||||
* AdminUserTokenRepo.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-04-20 10:21:25
|
||||
* @modified 2023-04-20 10:21:25
|
||||
*/
|
||||
|
||||
namespace Beike\Repositories;
|
||||
|
||||
use Beike\Models\AdminUser;
|
||||
use Beike\Models\AdminUserToken;
|
||||
|
||||
class AdminUserTokenRepo
|
||||
{
|
||||
/**
|
||||
* @param $adminUser
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getTokenByAdminUser($adminUser)
|
||||
{
|
||||
$adminUserId = self::getAdminUserId($adminUser);
|
||||
if (empty($adminUserId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return AdminUserToken::query()->where('admin_user_id', $adminUserId)->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $token
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getAdminUserTokenByToken($token)
|
||||
{
|
||||
return AdminUserToken::query()->where('token', $token)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $adminUser
|
||||
* @param $tokens
|
||||
* @return void
|
||||
*/
|
||||
public static function updateTokensByUser($adminUser, $tokens)
|
||||
{
|
||||
$adminUserId = self::getAdminUserId($adminUser);
|
||||
if (empty($adminUserId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AdminUserToken::query()->where('admin_user_id', $adminUserId)->delete();
|
||||
if (empty($tokens)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($tokens as $token) {
|
||||
AdminUserToken::query()->create([
|
||||
'admin_user_id' => $adminUserId,
|
||||
'token' => $token,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $adminUser
|
||||
* @return int|mixed
|
||||
*/
|
||||
private static function getAdminUserId($adminUser)
|
||||
{
|
||||
$adminUserId = 0;
|
||||
if ($adminUser instanceof AdminUser) {
|
||||
$adminUserId = $adminUser->id;
|
||||
} elseif (is_int($adminUser)) {
|
||||
$adminUserId = $adminUser;
|
||||
}
|
||||
|
||||
return $adminUserId;
|
||||
}
|
||||
}
|
||||
|
|
@ -55,14 +55,18 @@ class CategoryRepo
|
|||
}
|
||||
}
|
||||
|
||||
public static function flatten(string $locale, $separator = ' > '): array
|
||||
public static function flatten(string $locale, $includeInactive = true, $separator = ' > '): array
|
||||
{
|
||||
$sql = "SELECT cp.category_id AS id, TRIM(LOWER(GROUP_CONCAT(cd1.name ORDER BY cp.level SEPARATOR '{$separator}'))) AS name, c1.parent_id, c1.position";
|
||||
$sql .= ' FROM category_paths cp';
|
||||
$sql .= ' LEFT JOIN categories c1 ON (cp.category_id = c1.id)';
|
||||
$sql .= ' LEFT JOIN categories c2 ON (cp.path_id = c2.id)';
|
||||
$sql .= ' LEFT JOIN category_descriptions cd1 ON (cp.path_id = cd1.category_id)';
|
||||
$sql .= " WHERE cd1.locale = '" . $locale . "' GROUP BY cp.category_id ORDER BY name ASC";
|
||||
$sql .= " WHERE cd1.locale = '" . $locale . "' ";
|
||||
if (! $includeInactive) {
|
||||
$sql .= ' AND c1.active = 1 ';
|
||||
}
|
||||
$sql .= ' GROUP BY cp.category_id ORDER BY c1.position ASC';
|
||||
|
||||
return DB::select($sql);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,6 +98,6 @@ class LanguageRepo
|
|||
*/
|
||||
public static function enabled()
|
||||
{
|
||||
return Language::query()->where('status', true)->get();
|
||||
return Language::query()->where('status', true)->orderBy('sort_order', 'asc')->get();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
/**
|
||||
* OrderPaymentRepo.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-05-25 10:02:39
|
||||
* @modified 2023-05-25 10:02:39
|
||||
*/
|
||||
|
||||
namespace Beike\Repositories;
|
||||
|
||||
use Beike\Models\OrderPayment;
|
||||
|
||||
class OrderPaymentRepo
|
||||
{
|
||||
/**
|
||||
* @param $orderId
|
||||
* @param $data
|
||||
* @return mixed
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public static function createOrUpdatePayment($orderId, $data): mixed
|
||||
{
|
||||
$orderId = (int) $orderId;
|
||||
if (empty($orderId) || empty($data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$orderPayment = OrderPayment::query()->where('order_id', $orderId)->first();
|
||||
if (empty($orderPayment)) {
|
||||
$orderPayment = new OrderPayment();
|
||||
}
|
||||
|
||||
$paymentData = [
|
||||
'order_id' => $orderId,
|
||||
];
|
||||
|
||||
if (isset($data['transaction_id'])) {
|
||||
$paymentData['transaction_id'] = $data['transaction_id'];
|
||||
}
|
||||
if (isset($data['request'])) {
|
||||
$paymentData['request'] = json_encode($data['request'] ?? []);
|
||||
}
|
||||
if (isset($data['response'])) {
|
||||
$paymentData['response'] = json_encode($data['response'] ?? []);
|
||||
}
|
||||
if (isset($data['callback'])) {
|
||||
$paymentData['callback'] = json_encode($data['callback'] ?? []);
|
||||
}
|
||||
if (isset($data['receipt'])) {
|
||||
$paymentData['receipt'] = $data['receipt'];
|
||||
}
|
||||
|
||||
$orderPayment->fill($paymentData);
|
||||
$orderPayment->saveOrFail();
|
||||
|
||||
return $orderPayment;
|
||||
}
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ class OrderRepo
|
|||
*/
|
||||
public static function filterAll(array $filters = [])
|
||||
{
|
||||
$builder = self::getListBuilder($filters)->orderByDesc('created_at');
|
||||
$builder = static::getListBuilder($filters)->orderByDesc('created_at');
|
||||
|
||||
return $builder->get();
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ class OrderRepo
|
|||
*/
|
||||
public static function getListByCustomer($customer): LengthAwarePaginator
|
||||
{
|
||||
$builder = self::getListBuilder(['customer' => $customer])->orderByDesc('created_at');
|
||||
$builder = static::getListBuilder(['customer' => $customer])->orderByDesc('created_at');
|
||||
|
||||
return $builder->paginate(perPage());
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ class OrderRepo
|
|||
*/
|
||||
public static function getLatestOrders($customer, $limit)
|
||||
{
|
||||
return self::getListBuilder(['customer' => $customer])
|
||||
return static::getListBuilder(['customer' => $customer])
|
||||
->orderByDesc('created_at')
|
||||
->take($limit)
|
||||
->get();
|
||||
|
|
@ -68,7 +68,7 @@ class OrderRepo
|
|||
*/
|
||||
public static function filterOrders(array $filters = []): LengthAwarePaginator
|
||||
{
|
||||
$builder = self::getListBuilder($filters)->orderByDesc('created_at');
|
||||
$builder = static::getListBuilder($filters)->orderByDesc('created_at');
|
||||
|
||||
return $builder->paginate(perPage());
|
||||
}
|
||||
|
|
@ -79,7 +79,7 @@ class OrderRepo
|
|||
*/
|
||||
public static function getListBuilder(array $filters = []): Builder
|
||||
{
|
||||
$builder = Order::query()->with(['orderProducts']);
|
||||
$builder = Order::query()->with(['orderProducts'])->where('status', '<>', StateMachineService::CREATED);
|
||||
|
||||
$number = $filters['number'] ?? 0;
|
||||
if ($number) {
|
||||
|
|
@ -199,14 +199,12 @@ class OrderRepo
|
|||
|
||||
$shippingAddress = Address::query()->findOrFail($shippingAddressId);
|
||||
$paymentAddress = Address::query()->findOrFail($paymentAddressId);
|
||||
|
||||
$email = $customer->email;
|
||||
} else {
|
||||
$shippingAddress = new Address($current['guest_shipping_address'] ?? []);
|
||||
$paymentAddress = new Address($current['guest_payment_address'] ?? []);
|
||||
$email = $current['guest_shipping_address']['email'];
|
||||
}
|
||||
|
||||
$shippingAddress->country = $shippingAddress->country->name ?? '';
|
||||
$shippingAddress->country_id = $shippingAddress->country->id ?? 0;
|
||||
$paymentAddress->country = $paymentAddress->country->name ?? '';
|
||||
|
|
@ -268,6 +266,8 @@ class OrderRepo
|
|||
OrderProductRepo::createOrderProducts($order, $carts['carts']);
|
||||
OrderTotalRepo::createTotals($order, $totals);
|
||||
|
||||
hook_filter('repository.order.create.after', ['order' => $order, 'data' => $data]);
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,12 +31,16 @@ class PageCategoryRepo
|
|||
|
||||
/**
|
||||
* @param array $filters
|
||||
* @return LengthAwarePaginator
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getActiveList(array $filters = []): LengthAwarePaginator
|
||||
public static function getActiveList(array $filters = []): mixed
|
||||
{
|
||||
$filters['is_active'] = 1;
|
||||
$limit = (int) ($filters['limit'] ?? 0);
|
||||
$builder = self::getBuilder($filters);
|
||||
if ($limit > 0) {
|
||||
return $builder->limit($limit)->get();
|
||||
}
|
||||
|
||||
return $builder->paginate(perPage());
|
||||
}
|
||||
|
|
@ -150,11 +154,20 @@ class PageCategoryRepo
|
|||
}
|
||||
|
||||
/**
|
||||
* @param $page
|
||||
* @param $pageCategory
|
||||
* @return string
|
||||
*/
|
||||
public static function getName($page)
|
||||
public static function getName($pageCategory): string
|
||||
{
|
||||
return $page->description->title ?? '';
|
||||
if ($pageCategory instanceof PageCategory) {
|
||||
return $pageCategory->description->title ?? '';
|
||||
}
|
||||
|
||||
$pageCategoryId = (int) $pageCategory;
|
||||
$pageCategory = PageCategory::query()->whereHas('description', function ($query) use ($pageCategoryId) {
|
||||
$query->where('page_category_id', $pageCategoryId);
|
||||
})->first();
|
||||
|
||||
return $pageCategory->description->title ?? '';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,20 +189,6 @@ class PluginRepo
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get plugin by code
|
||||
*
|
||||
* @param $code
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getPlugin($code): mixed
|
||||
{
|
||||
$code = Str::camel($code);
|
||||
$plugins = self::getPluginsByCode();
|
||||
|
||||
return $plugins->get($code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断插件是否安装
|
||||
*
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class ProductRepo
|
|||
*/
|
||||
public static function getProductsByCategory($categoryId, $filterData)
|
||||
{
|
||||
$builder = self::getBuilder(array_merge(['category_id' => $categoryId, 'active' => 1], $filterData));
|
||||
$builder = static::getBuilder(array_merge(['category_id' => $categoryId, 'active' => 1], $filterData));
|
||||
|
||||
return $builder->with('inCurrentWishlist')
|
||||
->paginate($filterData['per_page'] ?? perPage())
|
||||
|
|
@ -72,7 +72,7 @@ class ProductRepo
|
|||
if (! $productIds) {
|
||||
return ProductSimple::collection(new Collection());
|
||||
}
|
||||
$builder = self::getBuilder(['product_ids' => $productIds])->whereHas('masterSku');
|
||||
$builder = static::getBuilder(['product_ids' => $productIds])->whereHas('masterSku');
|
||||
$products = $builder->with('inCurrentWishlist')->get();
|
||||
|
||||
return ProductSimple::collection($products);
|
||||
|
|
@ -81,10 +81,11 @@ class ProductRepo
|
|||
/**
|
||||
* 获取商品筛选对象
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $filters
|
||||
* @return Builder
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getBuilder(array $data = []): Builder
|
||||
public static function getBuilder(array $filters = []): Builder
|
||||
{
|
||||
$builder = Product::query()->with('description', 'skus', 'masterSku', 'attributes');
|
||||
|
||||
|
|
@ -99,26 +100,26 @@ class ProductRepo
|
|||
});
|
||||
$builder->select(['products.*', 'pd.name', 'pd.content', 'pd.meta_title', 'pd.meta_description', 'pd.meta_keywords', 'pd.name', 'product_skus.price']);
|
||||
|
||||
if (isset($data['category_id'])) {
|
||||
$builder->whereHas('categories', function ($query) use ($data) {
|
||||
if (is_array($data['category_id'])) {
|
||||
$query->whereIn('category_id', $data['category_id']);
|
||||
if (isset($filters['category_id'])) {
|
||||
$builder->whereHas('categories', function ($query) use ($filters) {
|
||||
if (is_array($filters['category_id'])) {
|
||||
$query->whereIn('category_id', $filters['category_id']);
|
||||
} else {
|
||||
$query->where('category_id', $data['category_id']);
|
||||
$query->where('category_id', $filters['category_id']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$productIds = $data['product_ids'] ?? [];
|
||||
$productIds = $filters['product_ids'] ?? [];
|
||||
if ($productIds) {
|
||||
$builder->whereIn('products.id', $productIds);
|
||||
$productIds = implode(',', $productIds);
|
||||
$builder->orderByRaw("FIELD(products.id, {$productIds})");
|
||||
}
|
||||
|
||||
// attr 格式:attr=10:10/13|11:34/23|3:4
|
||||
if (isset($data['attr']) && $data['attr']) {
|
||||
$attributes = self::parseFilterParamsAttr($data['attr']);
|
||||
// attr 格式:attr=10:10,13|11:34,23|3:4
|
||||
if (isset($filters['attr']) && $filters['attr']) {
|
||||
$attributes = self::parseFilterParamsAttr($filters['attr']);
|
||||
foreach ($attributes as $attribute) {
|
||||
$builder->whereHas('attributes', function ($query) use ($attribute) {
|
||||
$query->where('attribute_id', $attribute['attr'])
|
||||
|
|
@ -127,21 +128,21 @@ class ProductRepo
|
|||
}
|
||||
}
|
||||
|
||||
if (isset($data['sku']) || isset($data['model'])) {
|
||||
$builder->whereHas('skus', function ($query) use ($data) {
|
||||
if (isset($data['sku'])) {
|
||||
$query->where('sku', 'like', "%{$data['sku']}%");
|
||||
if (isset($filters['sku']) || isset($filters['model'])) {
|
||||
$builder->whereHas('skus', function ($query) use ($filters) {
|
||||
if (isset($filters['sku'])) {
|
||||
$query->where('sku', 'like', "%{$filters['sku']}%");
|
||||
}
|
||||
if (isset($data['model'])) {
|
||||
$query->where('model', 'like', "%{$data['model']}%");
|
||||
if (isset($filters['model'])) {
|
||||
$query->where('model', 'like', "%{$filters['model']}%");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isset($data['price']) && $data['price']) {
|
||||
$builder->whereHas('skus', function ($query) use ($data) {
|
||||
if (isset($filters['price']) && $filters['price']) {
|
||||
$builder->whereHas('skus', function ($query) use ($filters) {
|
||||
// price 格式:price=30-100
|
||||
$prices = explode('-', $data['price']);
|
||||
$prices = explode('-', $filters['price']);
|
||||
if (! $prices[1]) {
|
||||
$query->where('price', '>', $prices[0] ?: 0)->where('is_default', 1);
|
||||
} else {
|
||||
|
|
@ -150,34 +151,54 @@ class ProductRepo
|
|||
});
|
||||
}
|
||||
|
||||
if (isset($data['name'])) {
|
||||
$builder->where('pd.name', 'like', "%{$data['name']}%");
|
||||
if (isset($filters['name'])) {
|
||||
$builder->where('pd.name', 'like', "%{$filters['name']}%");
|
||||
}
|
||||
|
||||
$keyword = $data['keyword'] ?? '';
|
||||
$keyword = trim($filters['keyword'] ?? '');
|
||||
if ($keyword) {
|
||||
$builder->where(function (Builder $query) use ($keyword) {
|
||||
$query->whereHas('skus', function (Builder $query) use ($keyword) {
|
||||
$query->where('sku', 'like', "%{$keyword}%")
|
||||
$keywords = explode(' ', $keyword);
|
||||
$keywords = array_unique($keywords);
|
||||
$keywords = array_diff($keywords, ['']);
|
||||
$builder->where(function (Builder $query) use ($keywords) {
|
||||
$query->whereHas('skus', function (Builder $query) use ($keywords) {
|
||||
$keywordFirst = array_shift($keywords);
|
||||
$query->where('sku', 'like', "%{$keywordFirst}%")
|
||||
->orWhere('model', 'like', "%{$keywordFirst}%");
|
||||
|
||||
foreach ($keywords as $keyword) {
|
||||
$query->orWhere('sku', 'like', "%{$keyword}%")
|
||||
->orWhere('model', 'like', "%{$keyword}%");
|
||||
})->orWhere('pd.name', 'like', "%{$keyword}%");
|
||||
}
|
||||
});
|
||||
foreach ($keywords as $keyword) {
|
||||
$query->orWhere('pd.name', 'like', "%{$keyword}%");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isset($data['active'])) {
|
||||
$builder->where('active', (int) $data['active']);
|
||||
if (isset($filters['created_start'])) {
|
||||
$builder->where('products.created_at', '>', $filters['created_start']);
|
||||
}
|
||||
|
||||
if (isset($filters['created_end'])) {
|
||||
$builder->where('products.created_at', '>', $filters['created_end']);
|
||||
}
|
||||
|
||||
if (isset($filters['active'])) {
|
||||
$builder->where('active', (int) $filters['active']);
|
||||
}
|
||||
|
||||
// 回收站
|
||||
if (isset($data['trashed']) && $data['trashed']) {
|
||||
if (isset($filters['trashed']) && $filters['trashed']) {
|
||||
$builder->onlyTrashed();
|
||||
}
|
||||
|
||||
$sort = $data['sort'] ?? 'products.position';
|
||||
$order = $data['order'] ?? 'desc';
|
||||
$sort = $filters['sort'] ?? 'products.position';
|
||||
$order = $filters['order'] ?? 'desc';
|
||||
$builder->orderBy($sort, $order);
|
||||
|
||||
return $builder;
|
||||
return hook_filter('repo.product.builder', $builder);
|
||||
}
|
||||
|
||||
public static function parseFilterParamsAttr($attr)
|
||||
|
|
@ -191,7 +212,7 @@ class ProductRepo
|
|||
|
||||
return [
|
||||
'attr' => $itemArr[0],
|
||||
'value' => explode('/', $itemArr[1]),
|
||||
'value' => explode(',', $itemArr[1]),
|
||||
];
|
||||
}, $attributes);
|
||||
|
||||
|
|
@ -200,13 +221,18 @@ class ProductRepo
|
|||
|
||||
public static function getFilterAttribute($data): array
|
||||
{
|
||||
$builder = self::getBuilder($data)
|
||||
$builder = static::getBuilder(array_diff_key($data, ['attr' => '', 'price' => '']))
|
||||
->select(['pa.attribute_id', 'pa.attribute_value_id'])
|
||||
->with(['attributes.attribute.description', 'attributes.attribute_value.description'])
|
||||
->leftJoin('product_attributes as pa', 'pa.product_id', 'products.id')
|
||||
->whereNotNull('pa.attribute_id')
|
||||
->distinct()
|
||||
->reorder('pa.attribute_id');
|
||||
|
||||
if ($attributesIds = system_setting('base.multi_filter', [])['attribute'] ?? []) {
|
||||
$builder->whereIn('pa.attribute_id', $attributesIds);
|
||||
}
|
||||
|
||||
$productAttributes = $builder->get()->toArray();
|
||||
|
||||
$attributeMap = array_column(Attribute::query()->with('description')->orderBy('sort_order')->get()->toArray(), null, 'id');
|
||||
|
|
@ -249,7 +275,7 @@ class ProductRepo
|
|||
{
|
||||
$selectPrice = $data['price'] ?? '-';
|
||||
// unset($data['price']);
|
||||
$builder = self::getBuilder(['category_id' => $data['category_id']])->leftJoin('product_skus as ps', 'products.id', 'ps.product_id')
|
||||
$builder = static::getBuilder(['category_id' => $data['category_id']])->leftJoin('product_skus as ps', 'products.id', 'ps.product_id')
|
||||
->where('ps.is_default', 1);
|
||||
$min = $builder->min('ps.price');
|
||||
$max = $builder->max('ps.price');
|
||||
|
|
@ -268,7 +294,7 @@ class ProductRepo
|
|||
|
||||
public static function list($data = [])
|
||||
{
|
||||
return self::getBuilder($data)->paginate($data['per_page'] ?? 20);
|
||||
return static::getBuilder($data)->paginate($data['per_page'] ?? 20);
|
||||
}
|
||||
|
||||
public static function autocomplete($name)
|
||||
|
|
@ -328,7 +354,7 @@ class ProductRepo
|
|||
}
|
||||
|
||||
$items = [];
|
||||
$products = self::getBuilder()->select('id')->get();
|
||||
$products = static::getBuilder()->select('id')->get();
|
||||
foreach ($products as $product) {
|
||||
$items[$product->id] = [
|
||||
'id' => $product->id,
|
||||
|
|
|
|||
|
|
@ -95,9 +95,9 @@ class RmaRepo
|
|||
|
||||
/**
|
||||
* @param $data
|
||||
* @return LengthAwarePaginator
|
||||
* @return Builder
|
||||
*/
|
||||
public static function list($data): LengthAwarePaginator
|
||||
public static function getBuilder($data): Builder
|
||||
{
|
||||
$builder = Rma::query();
|
||||
|
||||
|
|
@ -124,7 +124,16 @@ class RmaRepo
|
|||
}
|
||||
$builder->orderBy('id', 'DESC');
|
||||
|
||||
return $builder->paginate(perPage())->withQueryString();
|
||||
return $builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @return LengthAwarePaginator
|
||||
*/
|
||||
public static function list($data): LengthAwarePaginator
|
||||
{
|
||||
return self::getBuilder($data)->paginate(perPage())->withQueryString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace Beike\Repositories;
|
|||
|
||||
use Beike\Models\Setting;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
class SettingRepo
|
||||
{
|
||||
|
|
@ -117,6 +118,7 @@ class SettingRepo
|
|||
];
|
||||
}
|
||||
Setting::query()->insert($rows);
|
||||
self::clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -154,5 +156,17 @@ class SettingRepo
|
|||
} else {
|
||||
$setting->update($settingData);
|
||||
}
|
||||
self::clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all cache.
|
||||
*/
|
||||
public static function clearCache()
|
||||
{
|
||||
Artisan::call('cache:clear');
|
||||
Artisan::call('config:clear');
|
||||
Artisan::call('view:clear');
|
||||
Artisan::call('optimize:clear');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,12 +42,17 @@ class CurrencyService
|
|||
return $amount;
|
||||
}
|
||||
|
||||
$symbol_left = $this->currencies[$currency]->symbol_left;
|
||||
$symbol_right = $this->currencies[$currency]->symbol_right;
|
||||
$decimal_place = $this->currencies[$currency]->decimal_place;
|
||||
$currencyRow = $this->currencies[$currency] ?? null;
|
||||
if (empty($currencyRow)) {
|
||||
return $amount;
|
||||
}
|
||||
|
||||
$symbol_left = $currencyRow->symbol_left;
|
||||
$symbol_right = $currencyRow->symbol_right;
|
||||
$decimal_place = $currencyRow->decimal_place;
|
||||
|
||||
if (! $value) {
|
||||
$value = $this->currencies[$currency]->value;
|
||||
$value = $currencyRow->value;
|
||||
}
|
||||
|
||||
$amount = $value ? (float) $amount * $value : (float) $amount;
|
||||
|
|
@ -70,7 +75,7 @@ class CurrencyService
|
|||
$string .= number_format(abs($amount), (int) $decimal_place, __('currency.decimal_point'), __('currency.thousand_point'));
|
||||
|
||||
if ($symbol_right) {
|
||||
$string .= $symbol_right;
|
||||
$string .= ' ' . $symbol_right;
|
||||
}
|
||||
|
||||
return $string;
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class DesignService
|
|||
return self::handleRichText($content);
|
||||
}
|
||||
|
||||
return hook_filter('admin.service.design.module.content', $content);
|
||||
return hook_filter('service.design.module.content', $content);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class ImageService
|
|||
|
||||
private $imagePath;
|
||||
|
||||
public const PLACEHOLDER_IMAGE = 'catalog/placeholder.png';
|
||||
private $placeholderImage = 'catalog/placeholder.png';
|
||||
|
||||
/**
|
||||
* @param $image
|
||||
|
|
@ -28,7 +28,8 @@ class ImageService
|
|||
*/
|
||||
public function __construct($image)
|
||||
{
|
||||
$this->image = $image ?: self::PLACEHOLDER_IMAGE;
|
||||
$this->placeholderImage = system_setting('base.placeholder');
|
||||
$this->image = $image ?: $this->placeholderImage;
|
||||
$this->imagePath = public_path($this->image);
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ class ImageService
|
|||
public function setPluginDirName($dirName): static
|
||||
{
|
||||
$originImage = $this->image;
|
||||
if ($this->image == self::PLACEHOLDER_IMAGE) {
|
||||
if ($this->image == $this->placeholderImage) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
@ -48,7 +49,7 @@ class ImageService
|
|||
if (file_exists($this->imagePath)) {
|
||||
$this->image = strtolower('plugin/' . $dirName . $originImage);
|
||||
} else {
|
||||
$this->image = self::PLACEHOLDER_IMAGE;
|
||||
$this->image = $this->placeholderImage;
|
||||
$this->imagePath = public_path($this->image);
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +66,7 @@ class ImageService
|
|||
{
|
||||
try {
|
||||
if (! file_exists($this->imagePath)) {
|
||||
$this->image = self::PLACEHOLDER_IMAGE;
|
||||
$this->image = $this->placeholderImage;
|
||||
$this->imagePath = public_path($this->image);
|
||||
}
|
||||
if (! file_exists($this->imagePath)) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use Beike\Models\Order;
|
|||
use Beike\Models\OrderHistory;
|
||||
use Beike\Models\OrderShipment;
|
||||
use Beike\Models\Product;
|
||||
use Beike\Repositories\OrderPaymentRepo;
|
||||
use Throwable;
|
||||
|
||||
class StateMachineService
|
||||
|
|
@ -29,6 +30,8 @@ class StateMachineService
|
|||
|
||||
private array $shipment;
|
||||
|
||||
private array $payment;
|
||||
|
||||
public const CREATED = 'created'; // 已创建
|
||||
|
||||
public const UNPAID = 'unpaid'; // 待支付
|
||||
|
|
@ -116,6 +119,19 @@ class StateMachineService
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置支付信息
|
||||
*
|
||||
* @param array $payment
|
||||
* @return $this
|
||||
*/
|
||||
public function setPayment(array $payment = []): self
|
||||
{
|
||||
$this->payment = $payment;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有订单状态列表
|
||||
*
|
||||
|
|
@ -136,7 +152,25 @@ class StateMachineService
|
|||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
return hook_filter('service.state_machine.all_statuses', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有有效订单状态
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getValidStatuses(): array
|
||||
{
|
||||
$statuses = [
|
||||
self::CREATED,
|
||||
self::UNPAID,
|
||||
self::PAID,
|
||||
self::SHIPPED,
|
||||
self::COMPLETED,
|
||||
self::CANCELLED,
|
||||
];
|
||||
|
||||
return $statuses;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -198,6 +232,8 @@ class StateMachineService
|
|||
}
|
||||
$this->{$function}($oldStatusCode, $status);
|
||||
}
|
||||
|
||||
hook_filter('service.state_machine.change_status.after', ['order' => $order, 'status' => $status, 'comment' => $comment, 'notify' => $notify]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -208,11 +244,6 @@ class StateMachineService
|
|||
*/
|
||||
private function validStatusCode($statusCode)
|
||||
{
|
||||
if (! in_array($statusCode, self::ORDER_STATUS)) {
|
||||
$statusCodeString = implode(', ', self::ORDER_STATUS);
|
||||
|
||||
throw new \Exception("Invalid order status, must be one of the '{$statusCodeString}'");
|
||||
}
|
||||
$orderId = $this->orderId;
|
||||
$orderNumber = $this->order->number;
|
||||
$currentStatusCode = $this->order->status;
|
||||
|
|
@ -340,6 +371,18 @@ class StateMachineService
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加发货单号
|
||||
* @throws Throwable
|
||||
*/
|
||||
private function addPayment($oldCode, $newCode)
|
||||
{
|
||||
if (empty($this->payment)) {
|
||||
return;
|
||||
}
|
||||
OrderPaymentRepo::createOrUpdatePayment($this->orderId, $this->payment);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送新订单通知
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class AddressController extends Controller
|
|||
{
|
||||
$addresses = AddressRepo::listByCustomer(current_customer());
|
||||
$data = [
|
||||
'countries' => CountryRepo::all(),
|
||||
'countries' => CountryRepo::listEnabled(),
|
||||
'addresses' => AddressResource::collection($addresses),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class EditController extends Controller
|
|||
{
|
||||
$customer = current_customer();
|
||||
$data['customer'] = $customer;
|
||||
$data = hook_filter('account.edit.index', $data);
|
||||
|
||||
return view('account/edit', $data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,13 @@ class LoginController extends Controller
|
|||
|
||||
public function store(LoginRequest $request)
|
||||
{
|
||||
$data = [
|
||||
'request_data' => $request->all(),
|
||||
];
|
||||
|
||||
try {
|
||||
hook_action('shop.account.login.before', $data);
|
||||
|
||||
$guestCartProduct = CartRepo::allCartProducts(0);
|
||||
if (! auth(Customer::AUTH_GUARD)->attempt($request->only('email', 'password'))) {
|
||||
throw new NotAcceptableHttpException(trans('shop/login.email_or_password_error'));
|
||||
|
|
@ -50,5 +57,8 @@ class LoginController extends Controller
|
|||
CartRepo::mergeGuestCart($customer, $guestCartProduct);
|
||||
|
||||
return json_success(trans('shop/login.login_successfully'));
|
||||
} catch (\Exception $e) {
|
||||
return json_fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,21 +57,6 @@ class OrderController extends Controller
|
|||
return view('account/order_info', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单提交成功页
|
||||
*
|
||||
* @param Request $request
|
||||
* @param $number
|
||||
* @return View
|
||||
*/
|
||||
public function success(Request $request, $number): View
|
||||
{
|
||||
$customer = current_customer();
|
||||
$order = OrderRepo::getOrderByNumber($number, $customer);
|
||||
|
||||
return view('account/order_success', ['order' => $order]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单支付页面
|
||||
*
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
namespace Beike\Shop\Http\Controllers;
|
||||
|
||||
use Beike\Repositories\OrderRepo;
|
||||
use Beike\Shop\Services\CheckoutService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
|
|
@ -32,9 +33,9 @@ class CheckoutController extends Controller
|
|||
* 更改结算信息
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
* @return mixed
|
||||
*/
|
||||
public function update(Request $request): array
|
||||
public function update(Request $request): mixed
|
||||
{
|
||||
try {
|
||||
$requestData = $request->all();
|
||||
|
|
@ -55,8 +56,23 @@ class CheckoutController extends Controller
|
|||
*/
|
||||
public function confirm()
|
||||
{
|
||||
try {
|
||||
$data = (new CheckoutService)->confirm();
|
||||
|
||||
return hook_filter('checkout.confirm.data', $data);
|
||||
} catch (\Exception $e) {
|
||||
return json_fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function success()
|
||||
{
|
||||
$order_number = request('order_number');
|
||||
|
||||
$customer = current_customer();
|
||||
$order = OrderRepo::getOrderByNumber($order_number, $customer);
|
||||
$data = hook_filter('account.order.show.data', ['order' => $order, 'html_items' => []]);
|
||||
|
||||
return view('checkout/success', $data);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,35 @@ namespace Beike\Shop\Http\Controllers;
|
|||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use League\Csv\CannotInsertRecord;
|
||||
|
||||
class Controller extends \App\Http\Controllers\Controller
|
||||
{
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
|
||||
/**
|
||||
* @param $filename
|
||||
* @param $header
|
||||
* @param $records
|
||||
* @return mixed
|
||||
* @throws CannotInsertRecord
|
||||
*/
|
||||
public function downloadCsv($filename, $header, $records): mixed
|
||||
{
|
||||
if (! str_contains($filename, '.csv')) {
|
||||
$filename = $filename . '-' . date('YmdHis') . '.csv';
|
||||
}
|
||||
|
||||
$headers = [
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'Content-Transfer-Encoding' => 'binary',
|
||||
'Content-Disposition' => 'attachment; filename=' . $filename,
|
||||
];
|
||||
|
||||
$csv = \League\Csv\Writer::createFromString('');
|
||||
$csv->insertOne($header);
|
||||
$csv->insertAll($records);
|
||||
|
||||
return response($csv, 200, $headers);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
/**
|
||||
* OrderController.php
|
||||
*
|
||||
* @copyright 2023 beikeshop.com - All Rights Reserved
|
||||
* @link https://beikeshop.com
|
||||
* @author Edward Yang <yangjin@guangda.work>
|
||||
* @created 2023-06-06 10:47:27
|
||||
* @modified 2023-06-06 10:47:27
|
||||
*/
|
||||
|
||||
namespace Beike\Shop\Http\Controllers;
|
||||
|
||||
use Beike\Models\Order;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
public function show(Request $request, int $number)
|
||||
{
|
||||
$email = trim($request->get('email'));
|
||||
if (empty($email)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$order = Order::query()->where('number', $number)->where('email', $email)->firstOrFail();
|
||||
$data = hook_filter('order.show.data', ['order' => $order, 'html_items' => []]);
|
||||
|
||||
return view('order_info', $data);
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ class PageCategoryController extends Controller
|
|||
'active_page_categories' => PageCategoryRepo::getActiveList(),
|
||||
'active_pages' => PageRepo::getCategoryPages(),
|
||||
];
|
||||
$data = hook_filter('page_categories.home.data', $data);
|
||||
|
||||
return view('page_categories/home', $data);
|
||||
}
|
||||
|
|
@ -41,10 +42,11 @@ class PageCategoryController extends Controller
|
|||
|
||||
$data = [
|
||||
'category' => new PageCategoryDetail($pageCategory),
|
||||
'active_page_categories' => PageCategoryRepo::getActiveList(),
|
||||
'active_page_categories' => PageCategoryRepo::getActiveList(['limit' => 5]),
|
||||
'breadcrumb' => $breadCrumb,
|
||||
'category_pages' => $pageCategory->pages()->paginate(perPage()),
|
||||
'category_pages' => $pageCategory->pages()->paginate(12),
|
||||
];
|
||||
$data = hook_filter('page_categories.show.data', $data);
|
||||
|
||||
return view('page_categories/show', $data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,10 +32,6 @@ class PageController extends Controller
|
|||
|
||||
$data = hook_filter('page.show.data', $data);
|
||||
|
||||
if ($page->category) {
|
||||
return view('pages/article', $data);
|
||||
}
|
||||
|
||||
return view('pages/single', $data);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class ProductController extends Controller
|
|||
|
||||
$data = hook_filter('product.show.data', $data);
|
||||
|
||||
return view('product', $data);
|
||||
return view('product/product', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ class AddressRequest extends FormRequest
|
|||
{
|
||||
return [
|
||||
'name' => 'required|min:2|max:16',
|
||||
'phone' => 'required|min:6|max:16',
|
||||
'country_id' => 'required|exists:countries,id',
|
||||
'zone_id' => 'required|exists:zones,id',
|
||||
'address_1' => 'required',
|
||||
|
|
@ -36,7 +35,6 @@ class AddressRequest extends FormRequest
|
|||
{
|
||||
return [
|
||||
'name' => trans('address.name'),
|
||||
'phone' => trans('address.phone'),
|
||||
'country_id' => trans('address.country_id'),
|
||||
'zone_id' => trans('address.zone_id'),
|
||||
'address_1' => trans('address.address_1'),
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class CartDetail extends JsonResource
|
|||
$subTotal = $price * $this->quantity;
|
||||
$image = $sku->image ?: $product->image;
|
||||
|
||||
return [
|
||||
$result = [
|
||||
'cart_id' => $this->id,
|
||||
'product_id' => $this->product_id,
|
||||
'sku_id' => $this->product_sku_id,
|
||||
|
|
@ -48,5 +48,7 @@ class CartDetail extends JsonResource
|
|||
'subtotal_format' => currency_format($subTotal),
|
||||
'variant_labels' => trim($sku->getVariantLabel()),
|
||||
];
|
||||
|
||||
return hook_filter('resource.cart.detail', $result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class CategoryDetail extends JsonResource
|
|||
$item = [
|
||||
'id' => $this->id,
|
||||
'name' => $this->description->name ?? '',
|
||||
'url' => shop_route('categories.show', ['category' => $this]),
|
||||
'url' => $this->url,
|
||||
];
|
||||
|
||||
if ($this->relationLoaded('activeChildren') && $this->activeChildren->count() > 0) {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ class ProductDetail extends JsonResource
|
|||
'meta_description' => $this->description->meta_description ?? '',
|
||||
'brand_id' => $this->brand->id ?? 0,
|
||||
'brand_name' => $this->brand->name ?? '',
|
||||
'video' => $this->video ?? '',
|
||||
'images' => array_map(function ($image) {
|
||||
return [
|
||||
'preview' => image_resize($image, 500, 500),
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue