Compare commits

..

No commits in common. "feature-logistics" and "master" have entirely different histories.

216 changed files with 8658 additions and 56883 deletions

28
.env Normal file
View File

@ -0,0 +1,28 @@
APP_NAME='wyyl'
APP_ENV=
APP_KEY=base64:PItUypiY6FmV8oQTVIcIHyNQJAuuS36FmEs8exQbYAw=
APP_DEBUG=false
APP_LOG_LEVEL=
APP_URL=http://43.153.17.83
BEIKE_API_URL=https://beikeshop.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=wyyl
DB_USERNAME=wyyl
DB_PASSWORD='wyyl@2023'
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_CONNECTION=sync
MAIL_DRIVER=
MAIL_HOST=
MAIL_PORT=
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=

2
.gitignore vendored
View File

@ -4,7 +4,7 @@
/public/hot
/public/storage
/public/upload/avatar
#/public/build/beike/*
/public/build/beike/*
/public/install/css/*
/public/sitemap.xml
/storage/*.key

119
LICENSE
View File

@ -52,4 +52,121 @@ Licensor's trademarks, copyrights, patents, trade secrets or any other
intellectual property. No patent license is granted to make, use, sell, offer
for sale, have made, or import embodiments of any patent claims other than the
licensed claims defined in Section 2. No license is granted to the trademarks
of Licensor even if suc
of Licensor even if such marks are included in the Original Work. Nothing in
this License shall be interpreted to prohibit Licensor from licensing under
terms different from this License any Original Work that Licensor otherwise
would have a right to license.
5) External Deployment. The term "External Deployment" means the use,
distribution, or communication of the Original Work or Derivative Works in any
way such that the Original Work or Derivative Works may be used by anyone
other than You, whether those works are distributed or communicated to those
persons or made available as an application intended for use over a network.
As an express condition for the grants of license hereunder, You must treat
any External Deployment by You of the Original Work or a Derivative Work as a
distribution under section 1(c).
6) Attribution Rights. You must retain, in the Source Code of any Derivative
Works that You create, all copyright, patent, or trademark notices from the
Source Code of the Original Work, as well as any notices of licensing and any
descriptive text identified therein as an "Attribution Notice." You must cause
the Source Code for any Derivative Works that You create to carry a prominent
Attribution Notice reasonably calculated to inform recipients that You have
modified the Original Work.
7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that
the copyright in and to the Original Work and the patent rights granted herein
by Licensor are owned by the Licensor or are sublicensed to You under the
terms of this License with the permission of the contributor(s) of those
copyrights and patent rights. Except as expressly stated in the immediately
preceding sentence, the Original Work is provided under this License on an "AS
IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without
limitation, the warranties of non-infringement, merchantability or fitness for
a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK
IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this
License. No license to the Original Work is granted by this License except
under this disclaimer.
8) Limitation of Liability. Under no circumstances and under no legal theory,
whether in tort (including negligence), contract, or otherwise, shall the
Licensor be liable to anyone for any indirect, special, incidental, or
consequential damages of any character arising as a result of this License or
the use of the Original Work including, without limitation, damages for loss
of goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses. This limitation of liability shall not
apply to the extent applicable law prohibits such limitation.
9) Acceptance and Termination. If, at any time, You expressly assented to this
License, that assent indicates your clear and irrevocable acceptance of this
License and all of its terms and conditions. If You distribute or communicate
copies of the Original Work or a Derivative Work, You must make a reasonable
effort under the circumstances to obtain the express assent of recipients to
the terms of this License. This License conditions your rights to undertake
the activities listed in Section 1, including your right to create Derivative
Works based upon the Original Work, and doing so without honoring these terms
and conditions is prohibited by copyright law and international treaty.
Nothing in this License is intended to affect copyright exceptions and
limitations (including "fair use" or "fair dealing"). This License shall
terminate immediately and You may no longer exercise any of the rights granted
to You by this License upon your failure to honor the conditions in Section
1(c).
10) Termination for Patent Action. This License shall terminate automatically
and You may no longer exercise any of the rights granted to You by this
License as of the date You commence an action, including a cross-claim or
counterclaim, against Licensor or any licensee alleging that the Original Work
infringes a patent. This termination provision shall not apply for an action
alleging patent infringement by combinations of the Original Work with other
software or hardware.
11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this
License may be brought only in the courts of a jurisdiction wherein the
Licensor resides or in which Licensor conducts its primary business, and under
the laws of that jurisdiction excluding its conflict-of-law provisions. The
application of the United Nations Convention on Contracts for the
International Sale of Goods is expressly excluded. Any use of the Original
Work outside the scope of this License or after its termination shall be
subject to the requirements and penalties of copyright or patent law in the
appropriate jurisdiction. This section shall survive the termination of this
License.
12) Attorneys' Fees. In any action to enforce the terms of this License or
seeking damages relating thereto, the prevailing party shall be entitled to
recover its costs and expenses, including, without limitation, reasonable
attorneys' fees and costs incurred in connection with such action, including
any appeal of such action. This section shall survive the termination of this
License.
13) Miscellaneous. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent necessary
to make it enforceable.
14) Definition of "You" in This License. "You" throughout this License,
whether in upper or lower case, means an individual or a legal entity
exercising rights under, and complying with all of the terms of, this License.
For legal entities, "You" includes any entity that controls, is controlled by,
or is under common control with you. For purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the direction or
management of such entity, whether by contract or otherwise, or (ii) ownership
of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
ownership of such entity.
15) Right to Use. You may use the Original Work in all ways not otherwise
restricted or conditioned by this License or by law, and Licensor promises not
to interfere with or be responsible for such uses by You.
16) Modification of This License. This License is Copyright © 2005 Lawrence
Rosen. Permission is granted to copy, distribute, or communicate this License
without modification. Nothing in this License permits You to modify this
License as applied to the Original Work or to Derivative Works. However, You
may modify the text of this License and copy, distribute or communicate your
modified version (the "Modified License") and apply it to other original works
of authorship subject to the following conditions: (i) You may not indicate in
any way that your Modified License is the "Open Software License" or "OSL" and
you may not use those names in the name of your Modified License; (ii) You
must replace the notice specified in the first paragraph above with the notice
"Licensed under <insert your license name here>" or with a notice of your own
that is not confusingly similar to the notice in this License; and (iii) You
may not claim that your original works are open source software unless your
Modified License has been approved by Open Source Initiative (OSI) and You
comply with its license review and certification process.

0
artisan Normal file → Executable file
View File

View File

@ -33,7 +33,7 @@ class CountryController extends Controller
public function store(Request $request)
{
$country = CountryRepo::create($request->only('name', 'icon', 'code', 'sort_order', 'status'));
$country = CountryRepo::create($request->only('name', 'code', 'sort_order', 'status'));
hook_action('admin.country.store.after', $country);
@ -42,7 +42,7 @@ class CountryController extends Controller
public function update(Request $request, int $id)
{
$country = CountryRepo::update($id, $request->only('name', 'icon', 'code', 'sort_order', 'status'));
$country = CountryRepo::update($id, $request->only('name', 'code', 'sort_order', 'status'));
hook_action('admin.country.store.after', $country);
@ -57,16 +57,4 @@ class CountryController extends Controller
return json_success(trans('common.deleted_success'));
}
/**
* @param Request $request
* @return array
*/
public function autocomplete(Request $request): array
{
$brands = CountryRepo::autocomplete($request->get('name') ?? '', 0);
return json_success(trans('common.get_success'), $brands);
}
}

View File

@ -20,7 +20,7 @@ class DesignController extends Controller
$data = [
'editors' => [
'editor-slide_show', 'editor-image401', 'editor-tab_product', 'editor-product', 'editor-image100',
'editor-brand', 'editor-icons', 'editor-rich_text', 'editor-image200', 'editor-image300','editor-slide_show_video',
'editor-brand', 'editor-icons', 'editor-rich_text', 'editor-image200', 'editor-image300',
],
'design_settings' => system_setting('base.design_setting'),
];

View File

@ -1,255 +0,0 @@
<?php
namespace Beike\Admin\Http\Controllers;
use Beike\Admin\Http\Requests\LogisticsRequest;
use Beike\Admin\Http\Resources\LogisticsAttributeResource;
use Beike\Admin\Http\Resources\LogisticsResource;
use Beike\Admin\Repositories\TaxClassRepo;
use Beike\Admin\Services\LogisticsService;
use Beike\Admin\View\DesignBuilders\Product;
use Beike\Libraries\Weight;
use Beike\Models\Country;
use Beike\Models\Logistics;
use Beike\Models\LogisticsWeight;
use Beike\Repositories\CategoryRepo;
use Beike\Repositories\CountryRepo;
use Beike\Repositories\LanguageRepo;
use Beike\Repositories\LogisticsRepo;
use Beike\Repositories\SettingRepo;
use Illuminate\Http\Request;
class LogisticsController extends Controller
{
protected string $defaultRoute = 'logistics.index';
public function index(Request $request)
{
$requestData = $request->all();
if (! isset($requestData['sort'])) {
$requestData['sort'] = 'logistics.updated_at';
}
$logisticsList = LogisticsRepo::list($requestData);
$logistics = LogisticsResource::collection($logisticsList);
$logisticsFormat = $logistics->jsonSerialize();
$countries = LogisticsRepo::getDefaultCountries();
$data = [
'logistics_format' => $logisticsFormat,
'logistics' => $logistics,
'type' => 'logistics',
'default_countries' => $countries,
'default_countries_name' => is_array($countries) && count($countries) > 0 ? array_values($countries)[0]['name'] : ''
];
$data = hook_filter('admin.logistics.index.data', $data);
if ($request->expectsJson()) {
return $logisticsFormat;
}
return view('admin::pages.logistics.index', $data);
}
public function trashed(Request $request)
{
$requestData = $request->all();
$requestData['trashed'] = true;
$productList = LogisticsRepo::list($requestData);
$products = LogisticsResource::collection($productList);
$productsFormat = $products->jsonSerialize();
$data = [
'categories' => CategoryRepo::flatten(locale()),
'products_format' => $productsFormat,
'products' => $products,
'type' => 'trashed',
];
$data = hook_filter('admin.product.trashed.data', $data);
if ($request->expectsJson()) {
return $products;
}
return view('admin::pages.products.index', $data);
}
public function create(Request $request)
{
return $this->form($request, new Logistics());
}
public function store(LogisticsRequest $request)
{
try {
$requestData = $request->all();
$logistics = (new LogisticsService)->create($requestData);
$data = [
'request_data' => $requestData,
'logistics' => $logistics,
];
hook_action('admin.logistics.store.after', $data);
return redirect()->to(admin_route('logistics.index'))
->with('success', trans('common.created_success'));
} catch (\Exception $e) {
return redirect(admin_route('logistics.create'))
->withInput()
->withErrors(['error' => $e->getMessage()]);
}
}
public function edit(Request $request, Logistics $logistics)
{
return $this->form($request, $logistics);
}
public function update(LogisticsRequest $request, Logistics $logistics)
{
try {
$requestData = $request->all();
$logistics = (new LogisticsService)->update($logistics, $requestData);
$data = [
'request_data' => $requestData,
'logistics' => $logistics,
];
hook_action('admin.logistics.update.after', $data);
return redirect()->to($this->getRedirect())->with('success', trans('common.updated_success'));
} catch (\Exception $e) {
return redirect(admin_route('logistics.index'))->withErrors(['error' => $e->getMessage()]);
}
}
public function copy(LogisticsRequest $request, Logistics $product)
{
try {
$product = (new LogisticsService)->copy($product);
$data = [
'product' => $product,
];
hook_action('admin.product.copy.after', $data);
return json_success(trans('common.copy_success'), []);
} catch (\Exception $e) {
return json_encode($e->getMessage());
}
}
public function destroy(Request $request, Logistics $logistics)
{
$logistics->delete();
hook_action('admin.logistics.destroy.after', $logistics);
return json_success(trans('common.deleted_success'));
}
public function restore(Request $request)
{
$productId = $request->id ?? 0;
Logistics::withTrashed()->find($productId)->restore();
hook_action('admin.product.restore.after', $productId);
return ['success' => true];
}
protected function form(Request $request, Logistics $logistics)
{
$logistics = hook_filter('admin.logistics.form.logistics', $logistics);
$types = [
['title' => 'weight','name'=>trans('admin/logistics.type_weight')],
['title' => 'num','name'=>trans('admin/logistics.type_num')],
['title' => 'free','name'=>trans('admin/logistics.type_free')],
];
// 获取
$countryIds = (array)explode(',',$logistics->country_ids);
$logistics->country_ids = CountryRepo::getInList($countryIds);
$logistics->logistics_weights = LogisticsWeight::getList($logistics->id);
$data = [
'logistics' => $logistics,
'types' => $types,
'_redirect' => $this->getRedirect(),
];
$data = hook_filter('admin.logistics.form.data', $data);
return view('admin::pages.logistics.form.form', $data);
}
public function name(int $id)
{
$name = LogisticsRepo::getName($id);
return json_success(trans('common.get_success'), $name);
}
/**
* 根据商品ID批量获取商品名称
*
* @param Request $request
* @return array
*/
public function getNames(Request $request): array
{
$productIds = explode(',', $request->get('product_ids'));
$name = LogisticsRepo::getNames($productIds);
return json_success(trans('common.get_success'), $name);
}
public function autocomplete(Request $request)
{
$products = LogisticsRepo::autocomplete($request->get('name') ?? '');
return json_success(trans('common.get_success'), $products);
}
public function updateStatus(Request $request)
{
LogisticsRepo::updateStatusByIds($request->get('ids'), $request->get('status'));
return json_success(trans('common.updated_success'), []);
}
public function destroyByIds(Request $request)
{
$productIds = $request->get('ids');
LogisticsRepo::DeleteByIds($productIds);
hook_action('admin.product.destroy_by_ids.after', $productIds);
return json_success(trans('common.deleted_success'), []);
}
public function trashedClear()
{
LogisticsRepo::forceDeleteTrashed();
}
/**
* Common: 设置默认物流国家
* Author: wu-hui
* Time: 2023/08/25 13:48
* @return array
* @throws \Throwable
*/
public function defaultCountries(){
$country = request()->post('country');
SettingRepo::storeValue('default_country', $country,'logistics','plugin');
return json_success(trans('common.updated_success'), []);
}
}

View File

@ -118,22 +118,6 @@ class ProductController extends Controller
}
}
public function copy(ProductRequest $request, Product $product)
{
try {
$product = (new ProductService)->copy($product);
$data = [
'product' => $product,
];
hook_action('admin.product.copy.after', $data);
return json_success(trans('common.copy_success'), []);
} catch (\Exception $e) {
return json_encode($e->getMessage());
}
}
public function destroy(Request $request, Product $product)
{
$product->delete();
@ -163,21 +147,20 @@ class ProductController extends Controller
$product = hook_filter('admin.product.form.product', $product);
$taxClasses = TaxClassRepo::getList();
array_unshift($taxClasses, ['title' => trans('admin/builder.text_no'), 'id' => 0]);
$data = [
'product' => $product,
'descriptions' => $descriptions ?? [],
'category_ids' => $categoryIds ?? [],
'product_attributes' => ProductAttributeResource::collection($product->attributes),
'relations' => ProductResource::collection($product->relations)->resource,
'languages' => LanguageRepo::all(),
'tax_classes' => $taxClasses,
'weight_classes' => Weight::getWeightUnits(),
'source' => [
'categories' => CategoryRepo::flatten(locale(),FALSE),
'product' => $product,
'descriptions' => $descriptions ?? [],
'category_ids' => $categoryIds ?? [],
'product_attributes' => ProductAttributeResource::collection($product->attributes),
'relations' => ProductResource::collection($product->relations)->resource,
'languages' => LanguageRepo::all(),
'tax_classes' => $taxClasses,
'weight_classes' => Weight::getWeightUnits(),
'source' => [
'categories' => CategoryRepo::flatten(locale(), false),
],
'_redirect' => $this->getRedirect(),
'unit_list' => Product::getUnitList(),
'trade_term' => Product::getTradeTermList(),
];
$data = hook_filter('admin.product.form.data', $data);

View File

@ -12,7 +12,6 @@
namespace Beike\Admin\Http\Controllers;
use Beike\Admin\Repositories\RegionRepo;
use Beike\Models\Region;
use Beike\Repositories\CountryRepo;
use Illuminate\Http\Request;
@ -53,19 +52,4 @@ class RegionController
return json_success(trans('common.deleted_success'));
}
/**
* Common: 获取全部的区域分组
* Author: wu-hui
* Time: 2023/08/31 9:25
* @return array
*/
public function regionsAll(){
$list = Region::query()->select(['id','name'])->get();
return json_success(trans('common.get_success'), $list);
}
}

View File

@ -1,70 +0,0 @@
<?php
/**
* PageRequest.php
*
* @copyright 2022 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2022-08-19 21:58:20
* @modified 2022-08-19 21:58:20
*/
namespace Beike\Admin\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class LogisticsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'name' => 'string',
'warehouse_name' => 'string',
'country_id' => 'int',
'type' => 'string',
'first_weight' => 'numeric',
'first_weight_fee' => 'numeric',
'continuation_weight_max' => 'numeric',
'add_weight' => 'numeric',
'continuation_weight_fee' => 'numeric',
'num_fee' => 'numeric',
'throwing_ratio' => 'int',
'day_min' => 'int',
'day_max' => 'int',
];
}
public function attributes()
{
return [
'name' => trans('logistics.name'),
'warehouse_name' => trans('logistics.warehouse_name'),
'country_id' => trans('logistics.country_id'),
'type' => trans('logistics.type'),
'first_weight' => trans('logistics.first_weight'),
'first_weight_fee' => trans('logistics.first_weight_fee'),
'continuation_weight_max' => trans('logistics.continuation_weight_max'),
'add_weight' => trans('logistics.add_weight'),
'continuation_weight_fee' => trans('logistics.continuation_weight_fee'),
'num_fee' => trans('logistics.num_fee'),
'throwing_ratio' => trans('logistics.throwing_ratio'),
'day_min' => trans('logistics.day_min'),
'day_max' => trans('logistics.day_max'),
];
}
}

View File

@ -32,27 +32,14 @@ class ProductRequest extends FormRequest
*/
public function rules(): array
{
$rules = [
return [
'descriptions.*.name' => 'required|string|min:3|max:128',
'brand_id' => 'int',
// 'skus.*.sku' => 'required|string',
'skus.*.sku' => 'required|string',
'skus.*.price' => 'required|numeric',
// 'skus.*.origin_price' => 'required|numeric',
// 'skus.*.cost_price' => 'numeric',
'skus.*.origin_price' => 'required|numeric',
'skus.*.cost_price' => 'numeric',
];
// 判断:根据是否为 直接下单产品 进行对应的判断
if($this->active == 1){
// 直接下单产品 - 最小起订量必填
$rules['minimum_order'] = 'required|numeric|gt:0';
}else{
$rules['skus.*.origin_price'] = 'required|numeric';
}
// 判断:销售方式为按 批 卖每批的数量必须大于0
if($this->sales_method == 'batches'){
$rules['piece_to_batch'] = 'required|numeric|gt:0';
}
return $rules;
}
public function attributes()
@ -60,12 +47,10 @@ class ProductRequest extends FormRequest
return [
'descriptions.*.name' => trans('product.name'),
'brand_id' => trans('product.brand'),
// 'skus.*.sku' => trans('product.sku'),
'skus.*.sku' => trans('product.sku'),
'skus.*.price' => trans('product.price'),
'skus.*.origin_price' => trans('product.origin_price'),
// 'skus.*.cost_price' => trans('product.cost_price'),
'minimum_order' => trans('product.minimum_order'),
'piece_to_batch' => trans('product.one_batch_is_equal_to'),
'skus.*.cost_price' => trans('product.cost_price'),
];
}
}

View File

@ -1,47 +0,0 @@
<?php
namespace Beike\Admin\Http\Resources;
use Beike\Repositories\CountryRepo;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class LogisticsResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param Request $request
* @return array
* @throws \Exception
*/
public function toArray($request): array{
$countryIds = (array)explode(',',$this->country_ids);
$countryList = CountryRepo::getInList($countryIds);
$countryNames = implode('',array_column($countryList,'name'));
$data = [
'id' => $this->id,
'name' => $this->name ?? '',
'warehouse_name' => $this->warehouse_name ?? '',
'country_id' => $this->country_id,
'country' => $countryNames,//$this->country ? $this->country->name : '',
'type' => $this->type,
'first_weight' => $this->first_weight,
'first_weight_fee' => $this->first_weight_fee,
'continuation_weight_max' => $this->continuation_weight_max,
'add_weight' => $this->add_weight,
'continuation_weight_fee' => $this->continuation_weight_fee,
'num_fee' => $this->num_fee,
'throwing_ratio' => $this->throwing_ratio,
'day_min' => $this->day_min,
'day_max' => $this->day_max,
'position' => $this->position,
'created_at' => time_format($this->created_at),
'deleted_at' => $this->deleted_at ? time_format($this->deleted_at) : '',
'url_edit' => admin_route('logistics.edit', $this->id),
];
return hook_filter('resource.product', $data);
}
}

View File

@ -24,9 +24,9 @@ class ProductResource extends JsonResource
return image_resize($image);
}, $this->images ?? []),
'name' => $this->description->name ?? '',
'model' => $masterSku->model ?? '',
'quantity' => $masterSku->quantity ?? 0,
'price_formatted' => currency_format($masterSku->price ?? 0),
'model' => $masterSku->model,
'quantity' => $masterSku->quantity,
'price_formatted' => currency_format($masterSku->price),
'active' => $this->active,
'position' => $this->position,
'url' => $this->url,

View File

@ -19,7 +19,6 @@ use Beike\Admin\View\Components\Form\InputLocale;
use Beike\Admin\View\Components\Form\RichText;
use Beike\Admin\View\Components\Form\Select;
use Beike\Admin\View\Components\Form\SwitchRadio;
use Beike\Admin\View\Components\Form\SwitchRadioStatus;
use Beike\Admin\View\Components\Form\Textarea;
use Beike\Admin\View\Components\Header;
use Beike\Admin\View\Components\NoData;
@ -140,7 +139,6 @@ class AdminServiceProvider extends ServiceProvider
'alert' => Alert::class,
'form-input-locale' => InputLocale::class,
'form-switch' => SwitchRadio::class,
'form-switch-status' => SwitchRadioStatus::class,
'form-input' => Input::class,
'form-select' => Select::class,
'form-image' => Image::class,

View File

@ -71,7 +71,6 @@ Route::prefix($adminName)
Route::middleware('can:countries_create')->post('countries', [Controllers\CountryController::class, 'store'])->name('countries.store');
Route::middleware('can:countries_update')->put('countries/{id}', [Controllers\CountryController::class, 'update'])->name('countries.update');
Route::middleware('can:countries_delete')->delete('countries/{id}', [Controllers\CountryController::class, 'destroy'])->name('countries.destroy');
Route::middleware('can:countries_index')->get('countries/autocomplete', [Controllers\CountryController::class, 'autocomplete'])->name('countries.autocomplete');
// 省份
Route::middleware('can:zones_index')->get('zones', [Controllers\ZoneController::class, 'index'])->name('zones.index');
@ -215,27 +214,15 @@ 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_copy')->post('products/{product}/copy', [Controllers\ProductController::class, 'copy'])->name('products.copy');
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');
// 物流
Route::middleware('can:logistics_index')->get('logistics', [Controllers\LogisticsController::class, 'index'])->name('logistics.index');
Route::middleware('can:logistics_create')->get('logistics/create', [Controllers\LogisticsController::class, 'create'])->name('logistics.create');
Route::middleware('can:logistics_create')->post('logistics', [Controllers\LogisticsController::class, 'store'])->name('logistics.store');
Route::middleware('can:logistics_show')->get('logistics/{logistics}/edit', [Controllers\LogisticsController::class, 'edit'])->name('logistics.edit');
Route::middleware('can:logistics_update')->put('logistics/{logistics}', [Controllers\LogisticsController::class, 'update'])->name('logistics.update');
Route::middleware('can:logistics_delete')->delete('logistics/{logistics}', [Controllers\LogisticsController::class, 'destroy'])->name('logistics.destroy');
Route::middleware('can:logistics_default_countries')->post('logistics/default_countries', [Controllers\LogisticsController::class, 'defaultCountries'])->name('logistics.default_countries');
// 区域组
Route::middleware('can:regions_index')->get('regions', [Controllers\RegionController::class, 'index'])->name('regions.index');
Route::middleware('can:regions_create')->post('regions', [Controllers\RegionController::class, 'store'])->name('regions.store');
Route::middleware('can:regions_update')->put('regions/{id}', [Controllers\RegionController::class, 'update'])->name('regions.update');
Route::middleware('can:regions_delete')->delete('regions/{id}', [Controllers\RegionController::class, 'destroy'])->name('regions.destroy');
Route::middleware('can:regions_all')->get('regions/get_all', [Controllers\RegionController::class, 'regionsAll'])->name('regions.regions_all');
// RMA
Route::middleware('can:rmas_update')->post('rmas/history/{id}', [Controllers\RmaController::class, 'addHistory'])->name('rmas.add_history');

View File

@ -1,99 +0,0 @@
<?php
namespace Beike\Admin\Services;
use Beike\Models\Logistics;
use Beike\Models\LogisticsWeight;
use Beike\Models\Product;
use Illuminate\Support\Facades\DB;
class LogisticsService
{
public function create(array $data): Logistics
{
$logistics = new Logistics;
return $this->createOrUpdate($logistics, $data);
}
public function update(Logistics $logistics, array $data): Logistics
{
return $this->createOrUpdate($logistics, $data);
}
public function copy(Product $oldProduct): Product
{
$product = new Product;
$data = $oldProduct->toArray();
$data['id'] = 0;
$data['created_at'] = now();
$data['variables'] = json_encode($data['variables'],JSON_UNESCAPED_UNICODE);
$data['descriptions'] = $oldProduct->descriptions()->get()->toArray();
foreach ($data['descriptions'] as $locale => $description) {
$data['descriptions'][$description['locale']] = $description;
unset($data['descriptions'][$locale]);
}
$data['attributes'] = $oldProduct->attributes()->get()->toArray();
$data['skus'] = $oldProduct->skus()->get()->toArray();
$data['numPrices'] = $oldProduct->numPrices()->get()->toArray();
$data['categories'] = $oldProduct->categories()->get()->toArray();
$data['relations'] = $oldProduct->relations()->get()->toArray();
$data['categories'] = array_column($data['categories'],'id');
$data['relations'] = array_column($data['relations'],'id');
return $this->createOrUpdate($product, $data);
}
protected function createOrUpdate(Logistics $logistics, array $data): Logistics
{
$isUpdating = $logistics->id > 0;
try {
DB::beginTransaction();
$data['country_ids'] = (String) ($data['country_ids'] ? implode(',',$data['country_ids']) : '');
$data['country_id'] = (int) ($data['country_id'] ?? 0);
$data['throwing_ratio'] = (int) ($data['throwing_ratio'] ?? 0);
$data['day_min'] = (int) ($data['day_min'] ?? 0);
$data['day_max'] = (int) ($data['day_max'] ?? 0);
$data['first_weight'] = (float) ($data['first_weight'] ?? 0);
$data['first_weight_fee'] = (float) ($data['first_weight_fee'] ?? 0);
$data['continuation_weight_max'] = (float) ($data['continuation_weight_max'] ?? 0);
$data['add_weight'] = (float) ($data['add_weight'] ?? 0);
$data['continuation_weight_fee'] = (float) ($data['continuation_weight_fee'] ?? 0);
$data['num_fee'] = (float) ($data['num_fee'] ?? 0);
$data['variables'] = json_decode($data['variables'] ?? '[]');
$logistics->fill($data);
$logistics->updated_at = now();
$logistics->save();
if ($isUpdating) {}
// 续重范围处理
$continuationWeightList = (array)($data['continuation_weight_list'] ?? []);
if(count($continuationWeightList) > 0){
$logisticsId = $logistics->id;
$newWeightList = array_filter(array_map(function($item) use ($logisticsId){
if($item['max'] > 0){
$item['logistics_id'] = $logisticsId;
return $item;
}
return [];
},$continuationWeightList));
// 删除旧数据
LogisticsWeight::query()->where('logistics_id',$logisticsId)->delete();
// 添加新数据
LogisticsWeight::query()->insert($newWeightList);
}
DB::commit();
return $logistics;
} catch (\Exception $e) {
DB::rollBack();
throw $e;
}
}
}

View File

@ -19,28 +19,6 @@ class ProductService
return $this->createOrUpdate($product, $data);
}
public function copy(Product $oldProduct): Product
{
$product = new Product;
$data = $oldProduct->toArray();
$data['id'] = 0;
$data['created_at'] = now();
$data['variables'] = json_encode($data['variables'],JSON_UNESCAPED_UNICODE);
$data['descriptions'] = $oldProduct->descriptions()->get()->toArray();
foreach ($data['descriptions'] as $locale => $description) {
$data['descriptions'][$description['locale']] = $description;
unset($data['descriptions'][$locale]);
}
$data['attributes'] = $oldProduct->attributes()->get()->toArray();
$data['skus'] = $oldProduct->skus()->get()->toArray();
$data['numPrices'] = $oldProduct->numPrices()->get()->toArray();
$data['categories'] = $oldProduct->categories()->get()->toArray();
$data['relations'] = $oldProduct->relations()->get()->toArray();
$data['categories'] = array_column($data['categories'],'id');
$data['relations'] = array_column($data['relations'],'id');
return $this->createOrUpdate($product, $data);
}
protected function createOrUpdate(Product $product, array $data): Product
{
$isUpdating = $product->id > 0;
@ -77,8 +55,8 @@ class ProductService
$skus = [];
foreach ($data['skus'] as $index => $sku) {
$sku['position'] = $index;
$sku['origin_price'] = (float) ($sku['origin_price'] ?? 0);
$sku['cost_price'] = (float) ($sku['cost_price'] ?? 0);
$sku['origin_price'] = (float) $sku['origin_price'];
$sku['cost_price'] = (float) $sku['cost_price'];
$sku['quantity'] = (int) $sku['quantity'];
$skus[] = $sku;
}

View File

@ -1,26 +0,0 @@
<?php
namespace Beike\Admin\View\Components\Form;
use Illuminate\View\Component;
class SwitchRadioStatus extends Component
{
public string $name;
public string $value;
public string $title;
public function __construct(string $name, string $value, string $title)
{
$this->name = $name;
$this->title = $title;
$this->value = $value;
}
public function render()
{
return view('admin::components.form.switch-radio-status');
}
}

View File

@ -124,13 +124,6 @@ class Sidebar extends Component
'prefixes' => $this->getInquirySubPrefix(),
'children' => $this->getInquirySubRoutes(),
],
[
'route' => 'logistics.index',
'title' => trans('admin/common.logistics'),
'icon' => 'bi bi-gear',
'prefixes' => $this->getInquirySubPrefix(),
'children' => $this->getInquirySubRoutes(),
],
];
return hook_filter('admin.components.sidebar.menus', $menus);

View File

@ -1,45 +0,0 @@
<?php
/**
* Render.php
*
* @copyright 2022 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2022-07-08 17:09:15
* @modified 2022-07-08 17:09:15
*/
namespace Beike\Admin\View\DesignBuilders;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class SlideShowVideo extends Component
{
/**
* Create a new component instance.
*
* @return void
*/
public function __construct()
{
}
/**
* Get the view / contents that represent the component.
*
* @return View
*/
public function render(): View
{
$data['register'] = [
'code' => 'slideshow_video',
'sort' => 0,
'name' => trans('admin/design_builder.module_slideshow_video'),
'icon' => '&#xe61b;',
'style' => 'font-size: 40px;',
];
return view('admin::pages.design.module.slideshow_video', $data);
}
}

View File

@ -14,8 +14,8 @@ namespace Beike\Libraries;
class Weight
{
public const WEIGHT_CLASS = [
'g' => 1,
'kg' => 0.001,
'g' => 1,
'oz' => 0.035,
'lb' => 0.0022046,
];

View File

@ -18,7 +18,7 @@ class Country extends Base
{
use HasFactory;
protected $fillable = ['name','icon', 'country_id', 'code', 'sort_order', 'status'];
protected $fillable = ['name', 'country_id', 'code', 'sort_order', 'status'];
public function zones(): HasMany
{

View File

@ -1,106 +0,0 @@
<?php
namespace Beike\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
class Logistics extends Base{
use HasFactory;
use SoftDeletes;
protected $fillable = [
'name',
'warehouse_name',
'country_id',
'country_ids',
'type',
'first_weight',
'first_weight_fee',
'continuation_weight_max',
'add_weight',
'continuation_weight_fee',
'throwing_ratio',
'num_fee',
'day_min',
'day_max',
'position'
];
/**
* Common: 根据国家ID 获取所有关联的物流列表
* Author: wu-hui
* Time: 2023/08/22 10:31
* @param int $countryId
* @param string[] $field
* @return array
*/
public static function getAll(int $countryId,$field = ['id','name','warehouse_name','type','day_min','day_max']){
$list = self::select($field)
->whereRaw(\DB::raw('FIND_IN_SET('.$countryId.',country_ids)'))
->with(['weights'])
->orderBy('position','ASC')
->orderBy('id','ASC')
->get();
return $list ? $list->toArray() : [];
}
/**
* Common: 物流过滤 - 根据商品和物流列表过滤 仅返回有效的物流
* Author: wu-hui
* Time: 2023/08/29 10:02
* @param $logisticsList
* @param $productsList
* @return array|array[]
*/
public static function LogisticsFiltering($logisticsList,$productsList){
$eligibleList = array_map(function($logisticsItem) use ($productsList){
// 判断:物流是否按照重量计算 weight按重量计费num按数量计费free卖家包邮
if($logisticsItem['type'] == 'weight'){
$weights = (array)$logisticsItem['weights'];// 续重区间列表
$firstWeight = (float)$logisticsItem['first_weight'];// 首重
if(!$weights) {
$logisticsItem = [];
}else{
// 循环判断:只要有一个商品不符合条件 则当前物流不可用;
foreach($productsList as $productItem){
// 计算体积重 并且取数值大的作为重量计算
$volumeWeight = $productItem['volume_weight'] / $logisticsItem['throwing_ratio'];
$sumWeight = $productItem['sum_weight'] > $volumeWeight ? $productItem['sum_weight'] : $volumeWeight;
// 总重量 - 减首重 = 剩余重量
$surplusWeight = $sumWeight - $firstWeight;
// 判断如果剩余重量小于等于0 则符合条件;否则计算续重区间
if($surplusWeight <= 0){
continue;
}else{
$eligibleCount = collect($weights)
->where('min','<=',$sumWeight)//$surplusWeight
->where('max','>',$sumWeight)//$surplusWeight
->count();
if($eligibleCount <= 0) {
$logisticsItem = [];
break;
}
}
}
}
}
return $logisticsItem;
},$logisticsList);
return array_filter($eligibleList);
}
public function country(){
return $this->belongsTo(Country::class,'country_id','id');
}
public function weights(){
return $this->hasMany(LogisticsWeight::class,'logistics_id','id');
}
}

View File

@ -1,37 +0,0 @@
<?php
namespace Beike\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
class LogisticsWeight extends Base{
public $timestamps = false;
protected $fillable = [
'logistics_id',
'min',
'max',
];
/**
* Common: 根据物流ID获取全部续重范围
* Author: wu-hui
* Time: 2023/08/28 17:36
* @param $logisticsId
* @return array|mixed[]
*/
public static function getList($logisticsId){
$list = self::query()->where('logistics_id',$logisticsId)->orderBy('id','asc')->get();
return $list ? $list->toArray() : [];
}
}

View File

@ -16,7 +16,6 @@ use Beike\Notifications\UpdateOrderNotification;
use Beike\Services\StateMachineService;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Notifications\Notifiable;
class Order extends Base
@ -65,10 +64,6 @@ class Order extends Base
return $this->hasMany(OrderPayment::class);
}
public function logistics(): HasOne{
return $this->hasOne(Logistics::class,'id','shipping_method_code');
}
public function subTotal()
{
$totals = $this->orderTotals;

View File

@ -11,26 +11,7 @@ class Product extends Base
use HasFactory;
use SoftDeletes;
protected $fillable = [
'images',
'video',
'position',
'brand_id',
'tax_class_id',
'weight',
'weight_class',
'active',
'variables',
'price_setting',
'length',
'width',
'height',
'minimum_order',
'sales_method',
'piece_to_batch',
'trade_term',
'unit'
];
protected $fillable = ['images', 'video', 'position', 'brand_id', 'tax_class_id', 'weight', 'weight_class', 'active', 'variables', 'price_setting'];
protected $casts = [
'active' => 'boolean',
@ -42,11 +23,8 @@ class Product extends Base
public function getNumPricesByNum($num)
{
$descNumPrices = array_reverse($this->numprices->toArray());
$multiple = 1;
if($this->sales_method == 'batches') $multiple = (int)$this->piece_to_batch;
foreach($descNumPrices as $numprice){
if($num >= ($numprice['num'] * $multiple)){
if($num >= $numprice['num']){
return $numprice['price'];
}
}
@ -127,138 +105,4 @@ class Product extends Base
return $images[0] ?? '';
}
public static function getUnitList(){
return [
//A
['title' => 'Acre/Acres'],
['title' => 'Ampere/Amperes'],
//B
['title' => 'Bag/Bags'],
['title' => 'Barrel/Barrels'],
['title' => 'Blade/Blades'],
['title' => 'Box/Boxes'],
['title' => 'Bushel/Bushels'],
// C
['title' => 'Carat/Carats'],
['title' => 'Carton/Cartons'],
['title' => 'Case/Cases'],
['title' => 'Centimeter/Centimeters'],
['title' => 'Chain/Chains'],
['title' => 'Combo/Combos'],
['title' => 'Cubic Centimeter/Cubic Centimeters'],
['title' => 'Cubic Foot/Cubic Feet'],
['title' => 'Cubic Inch/Cubic Inches'],
['title' => 'Cubic Meter/Cubic Meters'],
['title' => 'Cubic Yard/Cubic Yards'],
// D
['title' => 'Degrees Fahrenheit'],
['title' => 'Dozen/Dozens'],
['title' => 'Dram/Drams'],
// F
['title' => 'Fluid Ounce/Fluid Ounces'],
['title' => 'Foot/Feet'],
['title' => 'Forty-FootContainer'],
['title' => 'Furlong/Furlongs'],
// G
['title' => 'Gallon/Gallons'],
['title' => 'Gill/Gills'],
['title' => 'Grain/Grains'],
['title' => 'Gram/Grams'],
['title' => 'Gross'],
// H
['title' => 'Hectare/Hectares'],
['title' => 'Hertz'],
// I
['title' => 'Inch/Inches'],
// K
['title' => 'Kiloampere/Kiloamperes'],
['title' => 'Kilogram/Kilograms'],
['title' => 'Kilohertz'],
['title' => 'Kilometer/kilometers'],
['title' => 'Kiloohm/Kiloohms'],
['title' => 'Kilovolt/Kilovolts'],
['title' => 'Kilowatt/Kilowatts'],
// L
['title' => 'Liter/Liters'],
['title' => 'Long Ton/Long Tons'],
// M
['title' => 'Megahertz'],
['title' => 'Meter/Meters'],
['title' => 'Metric Ton/Metric Tons'],
['title' => 'Mile/Miles'],
['title' => 'Milliampere/Milliamperes'],
['title' => 'Milligram/Milligrams'],
['title' => 'Millihertz'],
['title' => 'Milliliter/Milliliters'],
['title' => 'Milliohm/Milliohms'],
['title' => 'Millivolt/Millivolts'],
['title' => 'Milliwatt/Milliwatts'],
// N
['title' => 'Nautical Mile/Nautical Miles'],
// O
['title' => 'Ohm/Ohms'],
['title' => 'Ounce/Ounces'],
// P
['title' => 'Pack/Packs'],
['title' => 'Pair/Pairs'],
['title' => 'Pallet/Pallets'],
['title' => 'Parcel/Parcels'],
['title' => 'Perch/Perches'],
['title' => 'Piece/Pieces'],
['title' => 'Pint/Pints'],
['title' => 'Plant/Plants'],
['title' => 'Pole/Poles'],
['title' => 'Pound/Pounds'],
// Q
['title' => 'Quart/Quarts'],
['title' => 'Quarter/Quarters'],
// R
['title' => 'Rod/Rods'],
['title' => 'Roll/Rolls'],
// S
['title' => 'Set/Sets'],
['title' => 'Sheet/Sheets'],
['title' => 'Short Ton/Short Tons'],
['title' => 'Square Centimeter/Square Centimeters'],
['title' => 'Square Foot/Square Feet'],
['title' => 'Square Inch/Square Inches'],
['title' => 'Square Meter/Square Meters'],
['title' => 'Square Mile/Square Miles'],
['title' => 'Square Yard/Square Yards'],
['title' => 'Stone/Stones'],
['title' => 'Strand/Strands'],
// T
['title' => 'Ton/Tons'],
['title' => 'Tonne/Tonnes'],
['title' => 'Tray/Trays'],
['title' => 'Twenty-Foot Container'],
// U
['title' => 'Unit/Units'],
// V
['title' => 'Volt/Volts'],
// W
['title' => 'Watt/Watts'],
['title' => 'Wp'],
// Y
['title' => 'Yard/Yards'],
];
}
public static function getTradeTermList(){
return [
['title'=>'EXW'],
['title'=>'FCA'],
['title'=>'FAS'],
['title'=>'FOB'],
['title'=>'CFR'],
['title'=>'CIF'],
['title'=>'CPT'],
['title'=>'CIP'],
['title'=>'DAT'],
['title'=>'DAP'],
['title'=>'DDP'],
];
}
}

View File

@ -8,5 +8,5 @@ class ProductDescription extends Base
{
use HasFactory;
protected $fillable = ['locale', 'name', 'content', 'meta_title', 'meta_description', 'meta_keywords', 'unit'];
protected $fillable = ['locale', 'name', 'content', 'meta_title', 'meta_description', 'meta_keywords'];
}

View File

@ -12,7 +12,6 @@
namespace Beike\Repositories;
use Beike\Models\Country;
use Beike\Models\RegionZone;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
@ -27,7 +26,6 @@ class CountryRepo
{
return [
'name' => $data['name'] ?? '',
'icon' => $data['icon'] ?? '',
'code' => $data['code'] ?? '',
'sort_order' => (int) $data['sort_order'] ?? 0,
'status' => (bool) $data['status'] ?? 0,
@ -123,60 +121,4 @@ class CountryRepo
{
return Country::query()->select('id', 'name')->get();
}
/**
* Common: 查询国家列表
* Author: wu-hui
* Time: 2023/08/21 16:10
* @param $name
* @param int $onlyActive
* @return Builder[]|Collection
*/
public static function autocomplete($name, $onlyActive = 1){
// 参数获取
$pageSize = request()->input('page_size',10);
$regionsId = (int)request()->input('regions_id');
// 列表获取
$builder = Country::query()
->where('name', 'like', "$name%")
->when($regionsId > 0,function($query) use ($regionsId){
$ids = RegionZone::query()
->where('region_id',$regionsId)
->where('zone_id',0)
->pluck('country_id');
$ids = $ids ? $ids->toArray() : [];
if(is_array($ids) && count($ids) > 0) $query->whereIn('id',$ids);
})
->select('id', 'name', 'icon', 'status', 'code')
->orderBy('sort_order','ASC')
->orderBy('id','ASC');
// if ($onlyActive) {
// $builder->where('status', 1);
// }
if($pageSize == 'all') return $builder->get();
else return $builder->limit($pageSize)->get();
}
/**
* Common: 根据国家ID 获取对应的列表
* Author: wu-hui
* Time: 2023/08/21 16:13
* @param array $ids
* @return array|mixed[]
*/
public static function getInList(array $ids){
$list = Country::query()
->whereIn('id', $ids)
->select('id', 'name', 'code')
->orderBy('sort_order','ASC')
->orderBy('id','ASC')
->get();
if($list) {
$list = $list->toArray();
return array_column($list,null,'id');
}
return [];
}
}

View File

@ -1,390 +0,0 @@
<?php
/**
* LogisticsRepo.php
*
* @copyright 2022 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2022-06-23 11:19:23
* @modified 2022-06-23 11:19:23
*/
namespace Beike\Repositories;
use Beike\Models\Attribute;
use Beike\Models\AttributeValue;
use Beike\Models\Logistics;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\HigherOrderBuilderProxy;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Support\Facades\DB;
class LogisticsRepo
{
private static $allLogisticssWithName;
/**
* 获取商品详情
*/
public static function getLogisticsDetail($product)
{
if (is_int($product)) {
$product = Logistics::query()->findOrFail($product);
}
$product->load('description', 'skus', 'masterSku', 'brand', 'relations', 'numPrices');//, 'price_setting', 'numPrices');
hook_filter('repo.product.get_detail', $product);
return $product;
}
/**
* 通过单个或多个商品分类获取商品列表
*
* @param $categoryId
* @param $filterData
* @return LengthAwarePaginator
*/
public static function getLogisticssByCategory($categoryId, $filterData)
{
$builder = static::getBuilder(array_merge(['category_id' => $categoryId, 'active' => 1], $filterData));
return $builder->with('inCurrentWishlist')
->paginate($filterData['per_page'] ?? perPage())
->withQueryString();
}
/**
* 通过商品ID获取商品列表
* @param $productIds
* @return AnonymousResourceCollection
*/
public static function getLogisticssByIds($productIds): AnonymousResourceCollection
{
if (! $productIds) {
return LogisticsSimple::collection(new Collection());
}
$builder = static::getBuilder(['product_ids' => $productIds])->whereHas('masterSku');
$products = $builder->with('inCurrentWishlist')->get();
return LogisticsSimple::collection($products);
}
/**
* 获取商品筛选对象
*
* @param array $filters
* @return Builder
* @throws \Exception
*/
public static function getBuilder(array $filters = []): Builder
{
$builder = Logistics::query()->with('country');
$builder->leftJoin('countries as c', function ($build) {
$build->whereColumn('c.id', 'logistics.id');
});
$builder->select(['logistics.*', 'c.name as country_name']);
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', $filters['category_id']);
}
});
}
// 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'])
->whereIn('attribute_value_id', $attribute['value']);
});
}
}
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($filters['model'])) {
$query->where('model', 'like', "%{$filters['model']}%");
}
});
}
if (isset($filters['price']) && $filters['price']) {
$builder->whereHas('skus', function ($query) use ($filters) {
// price 格式:price=30-100
$prices = explode('-', $filters['price']);
if (! $prices[1]) {
$query->where('price', '>', $prices[0] ?: 0)->where('is_default', 1);
} else {
$query->whereBetween('price', [$prices[0] ?? 0, $prices[1]])->where('is_default', 1);
}
});
}
if (isset($filters['created_start'])) {
$builder->where('logistics.created_at', '>', $filters['created_start']);
}
if (isset($filters['created_end'])) {
$builder->where('logistics.created_at', '>', $filters['created_end']);
}
$sort = $filters['sort'] ?? 'logistics.position';
$order = $filters['order'] ?? 'desc';
$builder->orderBy($sort, $order);
return hook_filter('repo.logistics.builder', $builder);
}
public static function parseFilterParamsAttr($attr)
{
$attributes = explode('|', $attr);
$attributes = array_map(function ($item) {
$itemArr = explode(':', $item);
if (count($itemArr) != 2) {
throw new \Exception('Params attr has an error format!');
}
return [
'attr' => $itemArr[0],
'value' => explode(',', $itemArr[1]),
];
}, $attributes);
return $attributes;
}
public static function getFilterAttribute($data): array
{
$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');
$attributeValueMap = array_column(AttributeValue::query()->with('description')->get()->toArray(), null, 'id');
$attributes = isset($data['attr']) ? self::parseFilterParamsAttr($data['attr']) : [];
$attributeMaps = array_column($attributes, 'value', 'attr');
$results = [];
foreach ($productAttributes as $item) {
if (! isset($attributeMap[$item['attribute_id']]) || ! isset($attributeValueMap[$item['attribute_value_id']])) {
continue;
}
$attribute = $attributeMap[$item['attribute_id']];
$attributeValue = $attributeValueMap[$item['attribute_value_id']];
if (! isset($results[$item['attribute_id']])) {
$results[$item['attribute_id']] = [
'id' => $attribute['id'],
'name' => $attribute['description']['name'],
];
}
if (! isset($results[$item['attribute_id']]['values'][$item['attribute_value_id']])) {
$results[$item['attribute_id']]['values'][$item['attribute_value_id']] = [
'id' => $attributeValue['id'],
'name' => $attributeValue['description']['name'],
'selected' => in_array($attributeValue['id'], $attributeMaps[$attribute['id']] ?? []),
];
}
}
$results = array_map(function ($item) {
$item['values'] = array_values($item['values']);
return $item;
}, $results);
return array_values($results);
}
public static function getFilterPrice($data)
{
$selectPrice = $data['price'] ?? '-';
// unset($data['price']);
$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');
$priceArr = explode('-', $selectPrice);
$selectMin = $priceArr[0];
$selectMax = $priceArr[1];
return [
'min' => $min,
'max' => $max,
'select_min' => ($selectMin && $selectMin > $min) ? $selectMin : $min,
'select_max' => ($selectMax && $selectMax < $max) ? $selectMax : $max,
];
}
public static function list($data = [])
{
return static::getBuilder($data)->paginate($data['per_page'] ?? 20);
}
public static function autocomplete($name)
{
$products = Logistics::query()->with('description')
->whereHas('description', function ($query) use ($name) {
$query->where('name', 'like', "%{$name}%");
})->limit(10)->get();
$results = [];
foreach ($products as $product) {
$results[] = [
'id' => $product->id,
'name' => $product->description->name,
'status' => $product->active,
'image' => $product->image,
];
}
return $results;
}
/**
* 获取商品ID获取单个商品名称
*
* @param $id
* @return HigherOrderBuilderProxy|mixed|string
*/
public static function getNameById($id)
{
$product = Logistics::query()->find($id);
if ($product) {
return $product->description->name;
}
return '';
}
/**
* 通过商品ID获取商品名称
* @param $id
* @return mixed|string
*/
public static function getName($id)
{
return self::getNameById($id);
}
/**
* 获取所有商品ID和名称列表
*
* @return array|null
*/
public static function getAllLogisticssWithName(): ?array
{
if (self::$allLogisticssWithName !== null) {
return self::$allLogisticssWithName;
}
$items = [];
$products = static::getBuilder()->select('id')->get();
foreach ($products as $product) {
$items[$product->id] = [
'id' => $product->id,
'name' => $product->description->name ?? '',
];
}
return self::$allLogisticssWithName = $items;
}
/**
* @param $productIds
* @return array
*/
public static function getNames($productIds): array
{
$products = self::getListByLogisticsIds($productIds);
return $products->map(function ($product) {
return [
'id' => $product->id,
'name' => $product->description->name ?? '',
];
})->toArray();
}
/**
* 通过商品ID获取商品列表
* @return array|Builder[]|Collection
*/
public static function getListByLogisticsIds($productIds)
{
if (empty($productIds)) {
return [];
}
$products = Logistics::query()
->with(['description'])
->whereIn('id', $productIds)
->orderByRaw(DB::raw('FIELD(id, ' . implode(',', $productIds) . ')'))
->get();
return $products;
}
public static function DeleteByIds($ids)
{
Logistics::query()->whereIn('id', $ids)->delete();
}
public static function updateStatusByIds($ids, $status)
{
Logistics::query()->whereIn('id', $ids)->update(['active' => $status]);
}
public static function forceDeleteTrashed()
{
$products = Logistics::onlyTrashed();
$productsIds = $products->pluck('id')->toArray();
LogisticsRelation::query()->whereIn('product_id', $productsIds)->orWhere('relation_id', $productsIds)->delete();
LogisticsAttribute::query()->whereIn('product_id', $productsIds)->delete();
LogisticsCategory::query()->whereIn('product_id', $productsIds)->delete();
LogisticsSku::query()->whereIn('product_id', $productsIds)->delete();
LogisticsDescription::query()->whereIn('product_id', $productsIds)->delete();
$products->forceDelete();
}
/**
* Common: 获取默认国家
* Author: wu-hui
* Time: 2023/08/25 13:50
* @param false $isGetInfo
* @return array|mixed
*/
public static function getDefaultCountries($isGetInfo = false){
$set = plugin_setting('logistics.default_country');
if($set && !$isGetInfo) $set = array_column([$set],null,'id');
return $set ?? [];
}
}

View File

@ -13,7 +13,6 @@ namespace Beike\Repositories;
use Beike\Models\Address;
use Beike\Models\Customer;
use Beike\Models\Logistics;
use Beike\Models\Order;
use Beike\Services\StateMachineService;
use Carbon\Carbon;
@ -122,11 +121,6 @@ class OrderRepo
$builder->where('status', $status);
}
$builder->with(['orderShipments','logistics'=>function($query){
$query->select(['id','day_min','day_max']);
}]);
return $builder;
}
@ -223,12 +217,6 @@ class OrderRepo
$currency = CurrencyRepo::findByCode($currencyCode);
$currencyValue = $currency->value ?? 1;
$shipping_method_name = trans($shippingMethodCode);
if(is_int($shippingMethodCode)) {
$logisticsName = Logistics::query()->where('id',$shippingMethodCode)->value('name');
$shipping_method_name = $logisticsName ?? $shipping_method_name;
}
$order = new Order([
'number' => self::generateOrderNumber(),
'customer_id' => $customer->id ?? 0,
@ -247,7 +235,7 @@ class OrderRepo
'user_agent' => request()->userAgent(),
'status' => StateMachineService::CREATED,
'shipping_method_code' => $shippingMethodCode,
'shipping_method_name' => $shipping_method_name,
'shipping_method_name' => trans($shippingMethodCode),
'shipping_customer_name' => $shippingAddress->name,
'shipping_calling_code' => $shippingAddress->calling_code ?? 0,
'shipping_telephone' => $shippingAddress->phone ?? '',

View File

@ -50,8 +50,6 @@ class DesignService
$content['module_code'] = $moduleCode;
if ($moduleCode == 'slideshow') {
return self::handleSlideShow($content);
} elseif ($moduleCode == 'slideshow_video') {
return self::handleSlideShow($content);
} elseif (in_array($moduleCode, ['image401', 'image100', 'image200', 'image300'])) {
return self::handleImage401($content);
} elseif ($moduleCode == 'brand') {

View File

@ -12,7 +12,6 @@
namespace Beike\Services;
use Beike\Admin\Http\Resources\PluginResource;
use Beike\Models\Logistics;
use Beike\Repositories\PluginRepo;
use Beike\Shop\Services\CheckoutService;
use Illuminate\Support\Str;
@ -44,27 +43,12 @@ class ShippingMethodService
if ($quotes) {
$pluginResource = (new PluginResource($plugin))->jsonSerialize();
$shippingMethods[] = [
'is_logistics' => 0,
'code' => $pluginCode,
'name' => $pluginResource['name'],
'quotes' => $quotes,
];
}
}
// 获取根据收货地址 获取物流信息
$cart = $checkout->cart->toArray();
$address = $checkout->cart->guest_shipping_address ?? $checkout->cart->guest_payment_address ?? $cart['payment_address'] ?? $cart['shipping_address'];
if($address){
$logisticsList = Logistics::getAll($address['country_id']);
foreach($logisticsList as $logisticsItem){
$shippingMethods[] = [
'is_logistics' => 1,
'code' => $logisticsItem['id'],
'name' => $logisticsItem['name'],
'quotes' => $logisticsItem,
];
}
}
return $shippingMethods;
}

View File

@ -5,7 +5,6 @@ namespace Beike\Shop\Http\Controllers;
use Beike\Models\ProductSku;
use Beike\Shop\Http\Requests\CartRequest;
use Beike\Shop\Services\CartService;
use Beike\Shop\Services\CheckoutService;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
@ -19,7 +18,7 @@ class CartController extends Controller
$data = [
'data' => CartService::reloadData(),
];
$data['totals'] = $this->getOrderMoney($data['data']['carts']);
$data = hook_filter('cart.index.data', $data);
return view('cart/cart', $data);
@ -39,7 +38,6 @@ class CartController extends Controller
CartService::select($customer, $cartIds);
$data = CartService::reloadData();
$data['totals'] = $this->getOrderMoney($data['carts']);
$data = hook_filter('cart.select.data', $data);
@ -58,30 +56,21 @@ class CartController extends Controller
{
try {
$skuId = $request->sku_id;
$quantity = $request->quantity ?? 1;
$quantity = $request->quantity ?? 1;
$buyNow = (bool) $request->buy_now ?? false;
$customer = current_customer();
// 多规格批量加入购物车处理
if(is_int($skuId)){
// 非多规格下单
$sku = ProductSku::query()->findOrFail($skuId);
$cart = CartService::add($sku, $quantity, $customer);
$cartIds = [$cart->id];
}else{
// 多规格下单
$skuList = array_values(json_decode($skuId,TRUE));
foreach($skuList as $item){
if((int)$item['quantity'] > 0){
$sku = ProductSku::query()->findOrFail($item['id']);
$cart = CartService::add($sku, $item['quantity'], $customer);
$cartIds[] = $cart->id;
}
}
$sku = ProductSku::query()
->whereRelation('product', 'active', '=', true)
->findOrFail($skuId);
$cart = CartService::add($sku, $quantity, $customer);
if ($buyNow) {
CartService::select($customer, [$cart->id]);
}
if ($buyNow) CartService::select($customer, $cartIds);
$cart = hook_filter('cart.store.data', $cart);
return json_success(trans('shop/carts.added_to_cart'), $cart);
} catch (\Exception $e) {
return json_fail($e->getMessage());
@ -96,18 +85,17 @@ class CartController extends Controller
*/
public function update(CartRequest $request, $cartId): array
{
// return json_fail("错误");
try {
$customer = current_customer();
$quantity = (int) $request->get('quantity');
CartService::updateQuantity($customer, $cartId, $quantity);
$data = CartService::reloadData();
$data['totals'] = $this->getOrderMoney($data['carts']);
$data = hook_filter('cart.update.data', $data);
return json_success(trans('common.updated_success'), $data);
} catch (\Exception $e) {
return json_fail($e->getMessage());
}
@ -148,22 +136,4 @@ class CartController extends Controller
return json_success(trans('common.success'), $data);
}
/**
* Common: 实时计算购物车订单金额
* Author: wu-hui
* Time: 2023/08/18 13:56
* @param $carts
* @return mixed
* @throws \Exception
*/
public function getOrderMoney($carts){
$carts = collect($carts)->where('selected', 1)->toArray();
$checkoutService = new CheckoutService();
$totalClass = hook_filter('service.checkout.total_service','Beike\Shop\Services\TotalService');
$checkoutService->totalService = (new $totalClass($checkoutService->cart,$carts));
$checkoutData = $checkoutService->checkoutData();
return $checkoutData['totals'];
}
}

View File

@ -11,7 +11,6 @@
namespace Beike\Shop\Http\Controllers;
use Beike\Models\Product;
use Beike\Repositories\OrderRepo;
use Beike\Shop\Services\CheckoutService;
use Illuminate\Http\Request;
@ -23,41 +22,6 @@ class CheckoutController extends Controller
try {
$data = (new CheckoutService)->checkoutData();
$data = hook_filter('checkout.index.data', $data);
// 判断:当前购物车商品是否符合下单条件
$carts = $data['carts']['carts'] ?? [];
$cartQuantity = collect($carts)->groupBy('product_id')->map(function($productGroup){
$first = $productGroup->first();
return [
'product_id' => $first['product_id'],
'total_quantity' => $productGroup->sum('quantity'),
'name_format' => $first['name_format'],
'minimum_order' => $first['minimum_order'],
'sales_method' => $first['sales_method'],
'piece_to_batch' => $first['piece_to_batch'],
];
})->toArray();
foreach($carts as $cartItem){
$productInfo = $cartQuantity[$cartItem['product_id']];
// 判断:起订量 如果大于购买数量 不符合下单条件
$minimumOrder = $productInfo['sales_method'] == 'batches' ? ($productInfo['minimum_order'] * $productInfo['piece_to_batch']) : $productInfo['minimum_order'];// 起订量
if($minimumOrder > $productInfo['total_quantity']){
throw new \Exception(trans('product.quantity_error_mini',[
'goods_name'=>$productInfo['name_format'],
'num'=>$minimumOrder,
]));
break;
}
// 判断:批量销售商品 购买总数量必须是N的倍数 否则不符合下单条件
$pieceToBatch = $productInfo['sales_method'] == 'batches' ? $productInfo['piece_to_batch'] : 1;// 倍数
if(($productInfo['total_quantity'] % $pieceToBatch) != 0){
throw new \Exception(trans('product.quantity_error_multiple',[
'goods_name'=>$productInfo['name_format'],
'num'=>$pieceToBatch,
]));
break;
}
}
return view('checkout', $data);
} catch (\Exception $e) {
@ -93,14 +57,7 @@ class CheckoutController extends Controller
public function confirm()
{
try {
$checkoutService = new CheckoutService;
$selectedProducts = $checkoutService->selectedProducts->toArray();
foreach($selectedProducts as $product){
if($product['product']['active'] == FALSE){
return json_fail(trans('common.product_active_false'));
}
}
$data = $checkoutService->confirm();
$data = (new CheckoutService)->confirm();
return hook_filter('checkout.confirm.data', $data);
} catch (\Exception $e) {

View File

@ -2,21 +2,10 @@
namespace Beike\Shop\Http\Controllers;
use Beike\Models\Cart;
use Beike\Models\CartProduct;
use Beike\Models\Country;
use Beike\Models\Logistics;
use Beike\Models\Product;
use Beike\Models\ProductSku;
use Beike\Repositories\AddressRepo;
use Beike\Repositories\CartRepo;
use Beike\Repositories\PluginRepo;
use Beike\Repositories\ProductRepo;
use Beike\Services\ShippingMethodService;
use Beike\Shop\Http\Resources\ProductDetail;
use Beike\Shop\Http\Resources\ProductSimple;
use Beike\Shop\Services\CartService;
use Beike\Shop\Services\CheckoutService;
use Illuminate\Http\Request;
class ProductController extends Controller
@ -33,7 +22,6 @@ class ProductController extends Controller
$product = ProductRepo::getProductDetail($product);
$data = [
'product_id' => $product->id,
'product' => (new ProductDetail($product))->jsonSerialize(),
'relations' => ProductRepo::getProductsByIds($relationIds)->jsonSerialize(),
];
@ -68,154 +56,4 @@ class ProductController extends Controller
return view('search', $data);
}
/**
* Common: 计算当前商品订单金额信息
* Author: wu-hui
* Time: 2023/08/18 10:37
* @param Request $request
* @return array|\Illuminate\Http\JsonResponse
* @throws \Exception
*/
public function computeOrderMoney(Request $request){
// 参数获取
$changeLogisticsId = (int)$request->change_logistics_id ?? 0;
$list = json_decode($request->list,TRUE) ?? '';
if(!is_array($list)) return json_fail(trans('shop/products.buy_sku_error'));
// 生成模拟数据
$diyData = [];
foreach($list as $item){
$diyData[] = [
'id' => 0,
'selected' => 1,
'product_id' => $item['product_id'],
'product_sku_id' => $item['product_sku_id'],
'quantity' => $item['quantity'],
'product' => Product::where('id',$item['product_id'])->first()->toArray(),
'sku' => ProductSku::where('id',$item['product_sku_id'])->first()->toArray(),
];
}
$cartItems = collect($diyData)->mapInto(CartProduct::class);
$cartList = CartService::cartListHandle($cartItems);
// 价格计算
$customer = current_customer();
$customerId = $customer->id ?? 0;
$sessionId = session()->getId();
$defaultAddress = AddressRepo::listByCustomer($customer)->first();
$defaultAddressId = $defaultAddress->id ?? 0;
$shippingMethod = PluginRepo::getShippingMethods()->first();
$paymentMethod = PluginRepo::getPaymentMethods()->first();
$shippingMethodCode = $shippingMethod->code ?? '';
$cart = collect([[
'customer_id' => $customerId,
'session_id' => $sessionId,
'shipping_address_id' => $defaultAddressId,
'shipping_method_code' => (int)$changeLogisticsId > 0 ? (int)$changeLogisticsId : $shippingMethodCode ,
'payment_address_id' => $defaultAddressId,
'payment_method_code' => $paymentMethod->code ?? '',
// "id" => 28,
// "customer_id" => 0,
// "session_id" => "Chwm3kGAY5YX4CC58yTKWREvSoplb9gFXl2UNnsb",
// "shipping_address_id" => 0,
// "guest_shipping_address" => null,
// "shipping_method_code" => "",
// "payment_address_id" => 0,
// "guest_payment_address" => null,
// "payment_method_code" => "paypal",
// "extra" => null,
// "created_at" => "2023-08-18 01:35:12",
// "updated_at" => "2023-08-18 01:35:12",
// "shipping_address" => null,
// "payment_address" => null,
]])->mapInto(Cart::class);
$cart = $cart[0];
// 计算
$checkoutService = new CheckoutService();
$checkoutData = $checkoutService->update([
// 'customer_id' => $customerId,
// 'session_id' => $sessionId,
'shipping_address_id' => $defaultAddressId,
'shipping_method_code' => (int)$changeLogisticsId > 0 ? (int)$changeLogisticsId : $shippingMethodCode ,
'payment_address_id' => $defaultAddressId,
'payment_method_code' => $paymentMethod->code ?? '']);
// $totalClass = hook_filter('service.checkout.total_service','Beike\Shop\Services\TotalService');
// $checkoutService->totalService = (new $totalClass($cart,$cartList));
// $checkoutData = $checkoutService->checkoutData();
$totals = $checkoutData['totals'];
return json_success(trans('common.success'),$totals);
}
/**
* Common: 获取物流列表
* Author: wu-hui
* Time: 2023/08/29 10:06
* @param Request $request
* @return array
* @throws \Exception
*/
public function productsLogistics(Request $request){
$data = [
'list' => [],
'country' => [],
];
// 参数获取
$countryId = (int)$request->country_id;
$goodsList = json_decode($request->goods_list,TRUE) ?? '';
if($countryId){
// 刷新国家信息
$country = Country::query()->where('id',$countryId)->first();
$data['country'] = $country ? $country->toArray() : [];
}
// 信息获取
$list = Logistics::query()
->select([
'id',
'name',
'day_min',
'day_max',
])
->when($countryId > 0,function($q) use ($countryId){
$q->whereRaw(\DB::raw('FIND_IN_SET('.$countryId.',country_ids)'));
})
->orderBy('position','ASC')
->orderBy('id','ASC')
->get();
$data['list'] = $list ? $list->toArray() : [];
// 循环处理内容
$time = time();
foreach($data['list'] as &$logisticsItem){
// 预计到达时间处理
// $startTime = date('Y-m-d',strtotime("+{$logisticsItem['day_min']} day", $time));
// $endTime = date('Y-m-d',strtotime("+{$logisticsItem['day_max']} day", $time));
// $logisticsItem['estimated_time'] = trans('order.expected_arrival',['start_time'=>$startTime,'end_time'=>$endTime]);
$logisticsItem['estimated_time'] = trans('order.work_days',['start_day'=>$logisticsItem['day_min'],'end_day'=>$logisticsItem['day_max']]);
// 判断:如果存在商品 计算物流运费信息
if(count($goodsList) > 0){
$request->list = $request->goods_list;
$request->change_logistics_id = $logisticsItem['id'];
$totals = $this->computeOrderMoney($request);
$shipping = array_column($totals['data'],null,'code')['shipping'] ?? [];
// $logisticsItem['shipping_fee'] = $shipping['show_tips'] == 0 ? $shipping['amount_format'] : trans('shop/carts.to_be_negotiated');
if($shipping['show_tips'] == 0) {
$logisticsItem['shipping_fee'] = $shipping['amount_format'];
$logisticsItem['amount'] = $shipping['amount'];
}else $logisticsItem = [];
}
}
// 存在多个物流 根据物流费用进行排序
if(count($data['list']) > 1){
$amounts = array_column($data['list'],'amount');
if($amounts) array_multisort($amounts,SORT_ASC,$data['list']);
}
$data['list'] = array_filter($data['list']);
return json_success(trans('common.success'),$data);
}
}

View File

@ -1,6 +1,7 @@
<?php
/**
* ZoneController.php
*
* @copyright 2022 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
@ -10,52 +11,21 @@
namespace Beike\Shop\Http\Controllers;
use Beike\Models\Region;
use Beike\Repositories\CountryRepo;
use Beike\Repositories\ZoneRepo;
use Illuminate\Http\Request;
class ZoneController extends Controller{
public function index(Request $request,int $countryId){
class ZoneController extends Controller
{
public function index(Request $request, int $countryId)
{
ZoneRepo::listByCountry($countryId);
$data = [
'zones' => ZoneRepo::listByCountry($countryId),
];
$data = hook_filter('zone.index.data',$data);
return json_success(trans('common.success'),$data);
$data = hook_filter('zone.index.data', $data);
return json_success(trans('common.success'), $data);
}
/**
* Common: 获取全部的国家列表
* Author: wu-hui
* Time: 2023/08/25 16:57
* @param Request $request
* @return array
*/
public function countries(Request $request){
$brands = CountryRepo::autocomplete($request->get('name') ?? '', 0);
return json_success(trans('common.get_success'), $brands);
}
/**
* Common: 获取全部的区域分组
* Author: wu-hui
* Time: 2023/08/31 9:25
* @return array
*/
public function regionsAll(){
$list = Region::query()->select(['id','name'])->get();
return json_success(trans('common.get_success'), $list);
}
}

View File

@ -36,8 +36,8 @@ class CartRequest extends FormRequest
$skuId = (int) $this->get('sku_id');
return [
'sku_id' => 'required',
'quantity' => ['required', 'int', /*function ($attribute, $value, $fail) use ($skuId) {
'sku_id' => 'required|int',
'quantity' => ['required', 'int', function ($attribute, $value, $fail) use ($skuId) {
$sku = ProductSku::query()->where('id', $skuId)->first();
$skuQuantity = $sku->quantity;
if ($value > $skuQuantity) {
@ -46,7 +46,7 @@ class CartRequest extends FormRequest
if($sku->product->price_setting == 'num' && $value < $sku->product->numprices[0]->num){
$fail(trans('shop/products.quantity_error'));
}
}*/],
}],
'buy_now' => 'bool',
];
}

View File

@ -26,9 +26,7 @@ class CartDetail extends JsonResource
};
$skuCode = $sku->sku;
$description = $product->description;
$productName = $description->name;
$unit = $this->unit;//$description->unit ?? '';
$subTotal = $price * $this->quantity;
$image = $sku->image ?: $product->image;
@ -39,9 +37,6 @@ class CartDetail extends JsonResource
'product_sku' => $skuCode,
'name' => $productName,
'name_format' => sub_string($productName),
'unit' => $unit,
'unit_format' => $unit,
'trade_term' => $this->trade_term,
'image' => $image,
'image_url' => image_resize($image),
'quantity' => $this->quantity,
@ -52,10 +47,6 @@ class CartDetail extends JsonResource
'subtotal' => $subTotal,
'subtotal_format' => currency_format($subTotal),
'variant_labels' => trim($sku->getVariantLabel()),
'active' => $product->active,
'minimum_order' => $product->minimum_order,
'sales_method' => $product->sales_method,
'piece_to_batch' => $product->piece_to_batch
];
return hook_filter('resource.cart.detail', $result);

View File

@ -38,7 +38,7 @@ class InquiryDetail extends JsonResource{
];
}
$product = $productsku->product;
$description = $product->description ?? '';
$description = $product->description;
return [
'id' => $this->id,
'product_sku_id' => $this->product_sku_id,

View File

@ -13,15 +13,15 @@ namespace Beike\Shop\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class NumPricesDetail extends JsonResource{
public function toArray($request): array{
class NumPricesDetail extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'num' => $this->sales_method == 'piece' ? $this->num : $this->num * $this->piece_to_batch,
'num' => $this->num,
'price' => $this->price,
'price_format' => currency_format($this->price),
];
}
}

View File

@ -31,46 +31,29 @@ class ProductDetail extends JsonResource
];
}
$salesMethod = $this->sales_method ?? 'piece';// 销售方式piece=按件卖batches=按批卖
$pieceToBatch = $this->piece_to_batch ?? 1;// 按批卖,每批等于多少件
$this->numprices = $this->numprices->map(function($numprices) use ($salesMethod,$pieceToBatch){
$numprices->sales_method = $salesMethod;
$numprices->piece_to_batch = $pieceToBatch;
return $numprices;
});
return [
'id' => $this->id,
'name' => $this->description->name ?? '',
'unit' => $this->unit,//$this->description->unit ?? '',
'trade_term' => $this->trade_term,
'description' => $this->description->content ?? '',
'meta_title' => $this->description->meta_title ?? '',
'meta_keywords' => $this->description->meta_keywords ?? '',
'name' => $this->description->name ?? '',
'description' => $this->description->content ?? '',
'meta_title' => $this->description->meta_title ?? '',
'meta_keywords' => $this->description->meta_keywords ?? '',
'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){
'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),
'popup' => image_resize($image,800,800),
'thumb' => image_resize($image,150,150),
'preview' => image_resize($image, 500, 500),
'popup' => image_resize($image, 800, 800),
'thumb' => image_resize($image, 150, 150),
];
},$this->images ?? []),
}, $this->images ?? []),
'attributes' => $attributes,
'variables' => $this->decodeVariables($this->variables),
'skus' => SkuDetail::collection($this->skus)->jsonSerialize(),
'in_wishlist' => $this->inCurrentWishlist->id ?? 0,
'active' => (bool)$this->active,
'active' => (bool) $this->active,
'price_setting' => $this->price_setting ?? '',
'minimum_order' => $this->minimum_order ?? 0,
'sales_method' => $this->sales_method ?? 'piece',
'piece_to_batch' => $this->piece_to_batch ?? 1,
'numPrices' => NumPricesDetail::collection($this->numprices)->jsonSerialize() ?? '',
];
}
@ -92,7 +75,6 @@ class ProductDetail extends JsonResource
return array_map(function ($item) use ($lang) {
return [
'name' => $item['name'][$lang] ?? '',
'isNumSelect' => $item['isNumSelect'] ?? FALSE,
'values' => array_map(function ($item) use ($lang) {
return [
'name' => $item['name'][$lang] ?? '',

View File

@ -31,17 +31,13 @@ class ProductSimple extends JsonResource
}
$name = $this->description->name ?? '';
$unit = $this->unit;//$this->description->unit ?? '';
$images = $this->images != NULL ? $this->images : [];
$images = $this->images;
$data = [
'id' => $this->id,
'sku_id' => $masterSku->id,
'name' => $name,
'name_format' => $name,
'unit' => $unit,
'unit_format' => $unit,
'trade_term' => $this->trade_term,
'url' => $this->url,
'price' => $masterSku->price,
'origin_price' => $masterSku->origin_price,
@ -51,7 +47,7 @@ class ProductSimple extends JsonResource
'in_wishlist' => $this->inCurrentWishlist->id ?? 0,
'price_setting' => $this->price_setting ?? '',
'numprices' => NumPricesDetail::collection($this->numprices)->jsonSerialize() ?? '',
'minimum_order' => $this->minimum_order,
'images' => array_map(function ($item) {
return image_resize($item, 400, 400);
}, $images),

View File

@ -43,10 +43,6 @@ Route::prefix('/')
Route::get('categories/{category}', [CategoryController::class, 'show'])->name('categories.show');
Route::get('countries/{id}/zones', [ZoneController::class, 'index'])->name('countries.zones.index');
Route::get('countries/autocomplete', [ZoneController::class, 'countries'])->name('countries.zones.countries');
Route::get('regions/get_all', [ZoneController::class, 'regionsAll'])->name('regions.regions_all');
Route::get('currency/{currency}', [CurrencyController::class, 'index'])->name('currency.switch');
@ -69,8 +65,6 @@ Route::prefix('/')
Route::get('products/search', [ProductController::class, 'search'])->name('products.search');
Route::get('products/{product}', [ProductController::class, 'show'])->name('products.show');
Route::post('products/computeOrderMoney', [ProductController::class, 'computeOrderMoney'])->name('products.computeOrderMoney');
Route::post('products/productsLogistics', [ProductController::class, 'productsLogistics'])->name('products.productsLogistics');
Route::get('register', [RegisterController::class, 'index'])->name('register.index');
Route::post('register', [RegisterController::class, 'store'])->name('register.store');

View File

@ -11,10 +11,7 @@
namespace Beike\Shop\Services;
use Beike\Models\Cart;
use Beike\Models\CartProduct;
use Beike\Models\Product;
use Beike\Models\ProductSku;
use Beike\Repositories\CartRepo;
use Beike\Shop\Http\Resources\CartDetail;
use Exception;
@ -30,22 +27,18 @@ class CartService
* @param bool $selected
* @return array
*/
public static function list($customer, bool $selected = false): array{
if (self::$cartList !== null) return self::$cartList;
public static function list($customer, bool $selected = false): array
{
if (self::$cartList !== null) {
return self::$cartList;
}
$cartBuilder = CartRepo::allCartProductsBuilder($customer->id ?? 0);
if ($selected) $cartBuilder->where('selected', true);
if ($selected) {
$cartBuilder->where('selected', true);
}
$cartItems = $cartBuilder->get();
return self::cartListHandle($cartItems);
}
/**
* Common: 处理指定的购物车商品
* Author: wu-hui
* Time: 2023/08/17 17:28
* @param $cartItems
* @return mixed
*/
public static function cartListHandle($cartItems){
$cartItems = $cartItems->filter(function ($item) {
$description = $item->sku->product->description ?? '';
$product = $item->product ?? null;
@ -64,6 +57,7 @@ class CartService
return $description && $product;
});
$productQuantitySumList = [];
foreach($cartItems as $item) {
$productId = $item->product_id;
@ -195,13 +189,6 @@ class CartService
if (empty($carts)) {
$carts = self::list(current_customer());
}
// 判断:禁止选中 非直接下单产品
$carts = array_map(function($cartItem){
if(!$cartItem['active']){
$cartItem['selected'] = 0;
}
return $cartItem;
},$carts);
$cartList = collect($carts)->where('selected', 1);
@ -219,5 +206,4 @@ class CartService
return hook_filter('service.cart.data', $data);
}
}

View File

@ -50,7 +50,7 @@ class CheckoutService
$this->cart = CartRepo::createCart($this->customer);
$this->selectedProducts = CartRepo::selectedCartProducts($this->customer->id ?? 0);
if ($this->selectedProducts->count() == 0) {
// throw new CartException(trans('shop/carts.empty_selected_products'));
throw new CartException(trans('shop/carts.empty_selected_products'));
}
}
@ -174,7 +174,7 @@ class CheckoutService
}
$shippingMethodCode = $current['shipping_method_code'];
if (! PluginRepo::shippingEnabled($shippingMethodCode) && !is_int($shippingMethodCode)) {
if (! PluginRepo::shippingEnabled($shippingMethodCode)) {
throw new \Exception(trans('shop/carts.invalid_shipping_method'));
}
@ -252,19 +252,16 @@ class CheckoutService
foreach ($shipments as $shipment) {
$shipmentCodes = array_merge($shipmentCodes, array_column($shipment['quotes'], 'code'));
}
// 判断:如果运费方式不存在 并且不是物流则设置第一个物流信息
if (!in_array($currentCart->shipping_method_code, $shipmentCodes) && !is_int($currentCart->shipping_method_code)) {
if (! in_array($currentCart->shipping_method_code, $shipmentCodes)) {
$this->updateShippingMethod($shipmentCodes[0] ?? '');
$this->totalService->setShippingMethod($shipmentCodes[0] ?? '');
}
$shippingMethodCode = !empty($currentCart->shipping_method_code) ? $currentCart->shipping_method_code : ($shipments[0]['code'] ?? '');
$data = [
'current' => [
'shipping_address_id' => $currentCart->shipping_address_id,
'guest_shipping_address' => $currentCart->guest_shipping_address,
'shipping_method_code' => $shippingMethodCode,
'shipping_method_code' => $currentCart->shipping_method_code,
'payment_address_id' => $currentCart->payment_address_id,
'guest_payment_address' => $currentCart->guest_payment_address,
'payment_method_code' => $currentCart->payment_method_code,

View File

@ -114,9 +114,7 @@ class TotalService
*/
private function getTotalClassMaps()
{
$maps = [
'sum_quantity' => "\Beike\\Shop\\Services\\TotalServices\\SumQuantityService"
];
$maps = [];
foreach (self::TOTAL_CODES as $code) {
$serviceName = Str::studly($code) . 'Service';
$maps[$code] = "\Beike\\Shop\\Services\\TotalServices\\{$serviceName}";

View File

@ -12,254 +12,52 @@
namespace Beike\Shop\Services\TotalServices;
use Beike\Libraries\Weight;
use Beike\Models\Country;
use Beike\Models\Logistics;
use Beike\Models\Product;
use Beike\Repositories\LogisticsRepo;
use Beike\Shop\Services\CheckoutService;
use Illuminate\Support\Str;
class ShippingService{
protected static $country;// 国家信息
protected static $productsList;// 商品分组统计列表
// 运费计算
public static function getTotal(CheckoutService $checkout): ?array{
// 计算运费
class ShippingService
{
/**
* @param CheckoutService $checkout
* @return array|null
* @throws \Exception|\Throwable
*/
public static function getTotal(CheckoutService $checkout): ?array
{
$totalService = $checkout->totalService;
$shippingMethod = $totalService->getShippingMethod();
$amount = 0;
$logisticsInfo = [];// 物流信息
self::$country = LogisticsRepo::getDefaultCountries(true);// 国家信息
$showTips = 0;// 是否显示【待协商】提示
if($shippingMethod && !is_int($checkout->cart->shipping_method_code)){
// 通过插件(固定运费) 进行计算
if(empty($shippingMethod)) return NULL;
$shippingPluginCode = self::parseShippingPluginCode($shippingMethod);
$pluginCode = Str::studly($shippingPluginCode);
if(!app('plugin')->checkActive($shippingPluginCode)){
$cart = $checkout->cart;
$cart->shipping_method_code = '';
$cart->saveOrFail();
return [];
}
$className = "Plugin\\{$pluginCode}\\Bootstrap";
if(!method_exists($className,'getShippingFee')){
throw new \Exception("请在插件 {$className} 实现方法: public function getShippingFee(CheckoutService \$checkout)");
}
$amount = (float)(new $className)->getShippingFee($checkout);
if (empty($shippingMethod)) {
return null;
}
else{
// 商品信息处理 获取同一个商品的数量、重量
$products = $totalService->getCartProducts();
self::$productsList = collect($products)->groupBy('product_id')->map(function($group){
$firstInfo = $group->first();
$productWeightInfo = Product::query()
->select(['id','weight','weight_class','length','width','height'])
->find($firstInfo['product_id']);
if($productWeightInfo) $productWeightInfo = $productWeightInfo->toArray();
// 重量转换 - 物流重量单位为 千克;商品重量需要进行转换
$weight = $productWeightInfo['weight'] ?? 0;
$sumQuantity = $group->sum('quantity');
// 判断:如果是按批卖 则数量需要除以每批的数量 在计算总重量。按件卖则直接计算
$weightQuantity = $firstInfo['sales_method'] == 'batches' ? ($sumQuantity / $firstInfo['piece_to_batch']) : $sumQuantity;// 多少件/多少批 根据销售方式获取
$sumWeight = (float)sprintf("%.2f",($weight * $weightQuantity));
$weightClass = $productWeightInfo['weight_class'];
$sumWeight = Weight::convert($sumWeight,$weightClass);// 总重量 单位:克
// 获取 体积重 长cm*宽cm*高cm*数量/抛比=体积重
$volumeWeight = ($productWeightInfo['length'] * $productWeightInfo['width'] * $productWeightInfo['height']) * $weightQuantity;// 还需要除以计抛比
return [
'product_id' => $firstInfo['product_id'],
'sum_quantity' => $sumQuantity,
'sum_weight' => $sumWeight,
'volume_weight' => $volumeWeight,
'weight' => $weight,
'weight_class' => $weightClass,
];
});
// 通过物流进行计算
$logisticsId = (int)$checkout->cart->shipping_method_code;
$logisticsInfo = self::getLogistics($logisticsId,$checkout->cart);
if($logisticsInfo) $amount = $logisticsInfo['amount'];// 存在物流 获取运费
else $showTips = 1;// 如果还是不能获取物流信息 则显示 待协商
$shippingPluginCode = self::parseShippingPluginCode($shippingMethod);
$pluginCode = Str::studly($shippingPluginCode);
if (! app('plugin')->checkActive($shippingPluginCode)) {
$cart = $checkout->cart;
$cart->shipping_method_code = '';
$cart->saveOrFail();
return [];
}
$className = "Plugin\\{$pluginCode}\\Bootstrap";
if (! method_exists($className, 'getShippingFee')) {
throw new \Exception("请在插件 {$className} 实现方法: public function getShippingFee(CheckoutService \$checkout)");
}
$amount = (float) (new $className)->getShippingFee($checkout);
$totalData = [
'code' => 'shipping',
'title' => trans('shop/carts.shipping_fee'),
'amount' => $amount,
'amount_format' => currency_format($amount),
'amount_tips' => trans('shop/carts.to_be_negotiated'),
'show_tips' => $showTips,
'country' => self::$country,// 国家信息
'logistics' => $logisticsInfo,// 物流信息
];
$totalService->amount += $totalData['amount'];
$totalService->totals[] = $totalData;
return $totalData;
}
/**
* Common: 运费计算 - 物流运费 - 获取使用的物流
* Author: wu-hui
* Time: 2023/08/22 11:55
* @param $logisticsId
* @param $cart
* @return array|mixed|mixed[]
*/
private static function getLogistics($logisticsId,$cart){
$logisticsField = ['id','type', 'name','first_weight','first_weight_fee','continuation_weight_max','add_weight','continuation_weight_fee','num_fee','throwing_ratio'];
// 判断:客服端是否切换国家 如果存在切换的国家id 则使用切换的国家id不存在则继续执行
$countryId = (int)request()->input('products_country_id');
if($countryId > 0) {
// 切换国家或者物流
// 仅存在国家ID直接获取当前国家全部物流信息取其中一个
// 同时存在国家ID和物流ID 则获取对应物流ID如果未查询到该物流 则执行仅存在国家ID时的操作
if($logisticsId) {
// 同时存在国家ID 获取物流ID
$logisticsInfo = Logistics::query()
->whereRaw(\DB::raw('FIND_IN_SET('.$countryId.',country_ids)'))
->with(['weights'])
->where('id',$logisticsId)
->select($logisticsField)
->first();
if($logisticsInfo) {
// 刷新国家信息
self::refreshCurrentCountry($countryId);
$logisticsList = [$logisticsInfo->toArray()];
goto logisticsListHandle;
}
}
// 仅存在国家ID时
goto CountryGetLogistics;
}
// 不存在切换国家id 正常流程
if($logisticsId <= 0){
// 获取当前收货地址国家ID
$cartArr = $cart->toArray();
$address = $cart->guest_shipping_address ?? $cart->guest_payment_address ?? $cartArr['payment_address'] ?? $cartArr['shipping_address'];
if($address) $countryId = (int)$address['country_id'];// 存在收货地址(用户已经登录 || 用户设置收货地址)
else $countryId = (int)self::$country['id'];// 不存在收货地址(用户未登录 || 用户已登录但是未设置收货地址) 获取默认国家收货地址
// 获取全部物流 返回第一个物流信息ID
CountryGetLogistics:
$logisticsList = Logistics::getAll($countryId,$logisticsField);
// 刷新国家信息
self::refreshCurrentCountry($countryId);
}else{
// 获取物流信息
$logisticsInfo = Logistics::query()
->with(['weights'])
->select($logisticsField)
->find($logisticsId);
$logisticsList = [$logisticsInfo ? $logisticsInfo->toArray() : []];
}
// 根据已查询的物流列表 筛选出符合条件的物流并且返回第一个
logisticsListHandle:
$eligibleLogisticsList = Logistics::LogisticsFiltering($logisticsList,self::$productsList);
// 循环计算物流运费
foreach($eligibleLogisticsList as &$eligibleLogisticsListItem){
// weight按重量计费num按数量计费free卖家包邮
$logisticsHandleFun = 'compute_'.$eligibleLogisticsListItem['type'];
$eligibleLogisticsListItem['amount'] = (float)self::$logisticsHandleFun($eligibleLogisticsListItem);
}
// 存在多个物流 需要获取运费最小的物流
if(count($eligibleLogisticsList) > 1){
$amounts = array_column($eligibleLogisticsList,'amount');
array_multisort($amounts,SORT_ASC,$eligibleLogisticsList);
}
return $eligibleLogisticsList[0] ?? [];
}
/**
* Common: 运费计算 - 物流运费 - 运费计算(按重量计费)
* Author: wu-hui
* Time: 2023/08/22 15:34
* @param $logisticsInfo
* @return float|int
*/
private static function compute_weight($logisticsInfo){
/**
* 计算规则:
* 总重量 - 低于首重;则运费 = 【首重运费】
* 总重量 - 高于首重;则运费 = 首重按照【首重运费】计算;(剩余重量 / 每增加重量 * 续重运费
*/
$amount = (int)0;
$firstWeight = (float)$logisticsInfo['first_weight'];// 首重
$firstWeightFee = (float)$logisticsInfo['first_weight_fee'];// 首重运费
$addWeight = (float)$logisticsInfo['add_weight'];// 每增加重量
$continuationWeightFee = (float)$logisticsInfo['continuation_weight_fee'];// 续重运费
// 循环处理商品
foreach(self::$productsList as $productInfo){
// 计算体积重 并且取数值大的作为重量计算
$volumeWeight = $productInfo['volume_weight'] / $logisticsInfo['throwing_ratio'];
$volumeWeight = (float)sprintf("%.2f",Weight::convert($volumeWeight,$productInfo['weight_class']));
$sumWeight = $productInfo['sum_weight'] > $volumeWeight ? $productInfo['sum_weight'] : $volumeWeight;
// 首重运费
$amount += (float)sprintf("%.2f",$firstWeightFee);
// 判断:如果总重量超过首重 则计算续重运费
$surplus = (float)$sumWeight - $firstWeight;
if($surplus > 0) $amount += (float)sprintf("%.2f",(ceil($surplus / $addWeight)) * $continuationWeightFee);
}
return $amount;
}
/**
* Common: 运费计算 - 物流运费 - 运费计算(按数量计费)
* Author: wu-hui
* Time: 2023/08/22 14:47
* @param $logisticsInfo
* @return float|int
*/
private static function compute_num($logisticsInfo){
/**
* 计算规则:
* 购买数量 - 低于首件数量;则运费 = 【首件运费】
* 购买数量 - 高于首件数量;则运费 = 首件数量按照【首件运费】计算;(剩余购买数量 / 每增加件数量 * 续件运费
*/
$amount = (int)0;
$firstWeight = (int)$logisticsInfo['first_weight'];// 首件数量
$firstWeightFee = (float)$logisticsInfo['first_weight_fee'];// 首件运费
$addWeight = (int)$logisticsInfo['add_weight'];// 每增加件
$continuationWeightFee = (float)$logisticsInfo['continuation_weight_fee'];// 续件运费
// 循环处理商品
foreach(self::$productsList as $productInfo){
// 首件运费
$amount += (float)sprintf("%.2f",$firstWeightFee);
// 判断:如果数量超过首件数量 则计算续件运费
$surplus = (int)$productInfo['sum_quantity'] - $firstWeight;
if($surplus > 0) $amount += (float)sprintf("%.2f",($surplus / $addWeight) * $continuationWeightFee);
}
return $amount;
}
/**
* Common: 运费计算 - 物流运费 - 运费计算(卖家包邮)
* Author: wu-hui
* Time: 2023/08/22 11:59
* @param $logisticsInfo
* @return int
*/
private static function compute_free($logisticsInfo){
return 0;
}
/**
* Common: 刷新当前选择国家信息
* Author: wu-hui
* Time: 2023/08/28 14:50
*/
private static function refreshCurrentCountry($countryId){
$newCountry = Country::query()->where('id',$countryId)->first();
self::$country = $newCountry ? $newCountry->toArray() : [];
}
/**
* 通过配送方式获取插件编码

View File

@ -1,22 +0,0 @@
<?php
namespace Beike\Shop\Services\TotalServices;
use Beike\Shop\Services\CheckoutService;
class SumQuantityService{
public static function getTotal(CheckoutService $checkout): ?array{
$totalService = $checkout->totalService;
$sumQuantity = $totalService->countProducts();
$totalData = [
'code' => 'sum_quantity',
'title' => trans('admin/dashboard.product_total'),
'amount' => $sumQuantity,
'amount_format' => $sumQuantity,
];
$totalService->totals[] = $totalData;
return $totalData;
}
}

449
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +0,0 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class LogisticsFactory extends Factory{
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'id' => 0,
'image' => 'path/to/image.jpg',
'video' => '',
'sort_order' => 0,
'status' => true,
'variable' => '',
];
}
}

View File

@ -1,39 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>恭喜,站点创建成功!</title>
<style>
.container {
width: 60%;
margin: 10% auto 0;
background-color: #f0f0f0;
padding: 2% 5%;
border-radius: 10px
}
ul {
padding-left: 20px;
}
ul li {
line-height: 2.3
}
a {
color: #20a53a
}
</style>
</head>
<body>
<div class="container">
<h1>恭喜, 站点创建成功!</h1>
<h3>这是默认index.html本页面由系统自动生成</h3>
<ul>
<li>本页面在FTP根目录下的index.html</li>
<li>您可以修改、删除或覆盖本页面</li>
<li>FTP相关信息请到“面板系统后台 > FTP” 查看</li>
</ul>
</div>
</body>
</html>

7572
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@
"devDependencies": {
"axios": "^0.21",
"bootstrap": "^5.2.2",
"laravel-mix": "^6.0.49",
"laravel-mix": "^6.0.6",
"lodash": "^4.17.19",
"resolve-url-loader": "^4.0.0",
"sass": "^1.38.1",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

8
plugins/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
*
!Bestseller
!FlatShipping
!LatestProducts
!Openai
!Paypal
!Social
!Stripe

View File

@ -53,13 +53,13 @@ class Bootstrap
$amount = $totalService->amount;
$shippingType = plugin_setting('flat_shipping.type', 'fixed');
$shippingValue = plugin_setting('flat_shipping.value', 0);
$sumQuantity = $totalService->countProducts();
if($sumQuantity > 0){
if ($shippingType == 'fixed') return $shippingValue;
elseif ($shippingType == 'percent') return $amount * $shippingValue / 100;
if ($shippingType == 'fixed') {
return $shippingValue;
} elseif ($shippingType == 'percent') {
return $amount * $shippingValue / 100;
}
return 0;
return 0;
}
}

View File

@ -29,7 +29,7 @@ class MenusController extends Controller
{
$products = ProductRepo::getBuilder(
[
// 'active' => 1,
'active' => 1,
'sort' => 'created_at',
'order' => 'desc',
])

7
public/.htaccess Normal file
View File

@ -0,0 +1,7 @@
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?s=/$1 [QSA,PT,L]
</IfModule>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,649 +0,0 @@
@charset "UTF-8";
/**
* @copyright 2022 beikeshop.com - All Rights Reserved.
* @link https://beikeshop.com
* @Author pu shuo <pushuo@guangda.work>
* @Date 2022-08-09 10:53:07
* @LastEditTime 2022-09-16 19:07:03
*/
@font-face {
font-family: "iconfont";
src: url("/fonts/design/iconfont.woff") format("woff"), url("/fonts/design/iconfont.ttf") format("truetype"); /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
}
body.page-design {
background-color: #fff;
padding: 0;
margin: 0;
font-size: 14px;
height: 100vh;
overflow: hidden;
}
body.page-design .iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0;
-moz-osx-font-smoothing: grayscale;
}
body.page-design [class*=" el-icon-"], body.page-design [class^=el-icon-] {
font-weight: 600;
}
body.page-design .el-tabs__header {
margin-bottom: 0;
}
body.page-design .tag {
margin: 8px 0;
color: #777;
font-size: 12px;
}
body.page-design .icon-rank {
cursor: move;
}
body.page-design .design-box {
display: flex;
height: 100vh;
}
body.page-design .design-box .design-head {
display: flex;
align-items: center;
justify-content: space-between;
}
body.page-design .design-box .design-head > div {
flex: 1;
height: 40px;
color: #fff;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
background-color: #0072ff;
text-align: center;
font-size: 0.8rem;
transition: all 0.2s ease-in-out;
border-right: 1px solid #5692ff;
}
body.page-design .design-box .design-head > div:last-of-type {
border-right: none;
}
body.page-design .design-box .design-head > div i {
margin-right: 7px;
}
body.page-design .design-box .design-head > div:hover {
background-color: #005bcc;
}
body.page-design .design-box .sidebar-edit-wrap {
width: 300px;
background-color: #fff;
border-right: 1px solid #eee;
}
body.page-design .design-box .sidebar-edit-wrap .module-edit {
padding: 0 10px 14px;
overflow-y: auto;
height: 100%;
}
body.page-design .design-box .sidebar-edit-wrap .module-edit .module-editor-row {
height: 47px;
line-height: 47px;
background-color: #f5f5f5;
padding: 0 20px;
margin: 0 -14px 14px;
font-size: 16px;
color: #212121;
}
body.page-design .design-box .sidebar-edit-wrap .module-edit .module-edit-group {
margin-bottom: 20px;
}
body.page-design .design-box .sidebar-edit-wrap .module-edit .module-edit-group:last-of-type {
border-bottom: none;
}
body.page-design .design-box .sidebar-edit-wrap .module-edit .module-edit-group .module-edit-title {
margin-bottom: 10px;
position: relative;
padding-left: 6px;
display: flex;
justify-content: space-between;
}
body.page-design .design-box .sidebar-edit-wrap .module-edit .module-edit-group .module-edit-title:before {
content: "";
position: absolute;
left: 0;
top: 3px;
width: 2px;
height: 14px;
background: #0072ff;
}
body.page-design .design-box .sidebar-edit-wrap .modules-list {
background: #e6e9ec;
height: 100%;
overflow-y: auto;
padding: 0 3px 30px;
}
body.page-design .design-box .sidebar-edit-wrap .module-list {
text-align: center;
padding: 4px;
cursor: pointer;
}
body.page-design .design-box .sidebar-edit-wrap .module-list .module-info {
background: #fff;
color: #556068;
transition: all 0.25s ease-in-out;
}
body.page-design .design-box .sidebar-edit-wrap .module-list .module-info:hover {
color: #0072ff;
box-shadow: 0 6px 23px rgba(0, 0, 0, 0.2);
}
body.page-design .design-box .sidebar-edit-wrap .module-list .icon {
padding: 12px 0 7px;
height: 36px;
display: flex;
justify-content: center;
align-items: center;
}
body.page-design .design-box .sidebar-edit-wrap .module-list .icon .img-icon {
width: 36px;
height: 36px;
}
body.page-design .design-box .sidebar-edit-wrap .module-list .icon .img-icon img {
max-width: 100%;
max-height: auto;
}
body.page-design .design-box .sidebar-edit-wrap .module-list .icon i {
font-size: 26px;
}
body.page-design .design-box .sidebar-edit-wrap .module-list .name {
font-size: 12px;
height: 27px;
overflow: hidden;
}
body.page-design .design-box .preview-iframe {
flex: 1;
}
body.page-design .pb-images-selector {
border: 1px solid #eee;
margin-bottom: 10px;
border-radius: 2px;
}
body.page-design .pb-images-selector:hover {
border-color: #ddd;
}
body.page-design .pb-images-selector:hover .selector-head {
background: #eee;
}
body.page-design .pb-images-selector .pb-images-selector-add {
width: 100%;
margin-top: 16px;
padding: 10px 20px;
}
body.page-design .pb-images-selector .selector-head {
display: flex;
align-items: center;
background-color: #f5f5f5;
padding: 4px 10px;
cursor: pointer;
justify-content: space-between;
}
body.page-design .pb-images-selector .selector-head > div.left {
display: flex;
align-items: center;
}
body.page-design .pb-images-selector .selector-head > div.left i {
margin-right: 10px;
}
body.page-design .pb-images-selector .selector-head > div.left img {
width: 24px;
}
body.page-design .pb-images-selector .selector-head > div.right {
display: flex;
align-items: center;
}
body.page-design .pb-images-selector .selector-head > div.right i {
color: #999;
font-size: 20px;
}
body.page-design .pb-images-selector .selector-head > div.right .remove-item {
margin-right: 8px;
padding-right: 8px;
position: relative;
}
body.page-design .pb-images-selector .selector-head > div.right .remove-item:after {
content: "";
border-right: 1px solid #ccc;
position: absolute;
right: 1px;
top: 20%;
height: 60%;
}
body.page-design .pb-images-selector .selector-head > div.right .remove-item i {
font-size: 15px;
}
body.page-design .pb-images-selector .pb-images-list {
padding: 7px;
padding-bottom: 8px;
position: relative;
display: none;
}
body.page-design .pb-images-selector .pb-images-list.active {
display: block;
}
body.page-design .pb-images-selector .pb-images-list .remove-item {
margin-top: 20px;
background: #ffc8c8;
color: #c70000;
z-index: 9;
height: 20px;
line-height: 20px;
padding: 5px 10px;
text-align: center;
cursor: pointer;
}
body.page-design .pb-images-selector .pb-images-list .remove-item i {
font-size: 14px;
}
body.page-design .pb-images-selector .pb-images-list .pb-image-selector {
cursor: pointer;
min-width: 50px;
min-height: 50px;
}
body.page-design .pb-images-selector .pb-images-list .pb-images-btns button {
margin-left: 0 !important;
padding: 9px 10px;
}
body.page-design .pb-images-selector .pb-images-list .el-input-group__prepend {
padding: 0 10px;
}
body.page-design .module-editor-tab-product-template .tab-info {
margin-top: 10px;
padding: 10px;
background: #f2f2f2;
border-radius: 6px;
}
body.page-design .module-editor-tab-product-template .manufacturer-list > div {
margin-bottom: 10px;
border: 1px solid #f4f4f4;
padding: 5px;
border-radius: 6px;
background: #f4f4f4;
padding-bottom: 8px;
position: relative;
}
body.page-design .module-editor-tab-product-template .manufacturer-list > div:hover .remove-btn {
display: block;
}
body.page-design .module-editor-tab-product-template .manufacturer-list > div .remove-btn {
position: absolute;
display: none;
top: 0;
right: 0;
background: red;
color: #fff;
z-index: 9;
padding: 0 2px;
cursor: pointer;
border-radius: 0 0 0 4px;
}
body.page-design .module-editor-tab-product-template .manufacturer-list > div .remove-btn:hover {
background: #c70000;
}
body.page-design .module-editor-tab-product-template .add-category {
display: flex;
justify-content: flex-end;
}
body.page-design .module-editor-tab-product-template .manufacturers .module-edit-group {
margin-top: 5px;
}
body.page-design .module-editor-tab-product-template .tab-edit-category > .el-tabs__header > .el-tabs__nav-wrap .el-tabs__item {
padding: 0 10px !important;
}
body.page-design .module-editor-tab-product-template .autocomplete-group-wrapper .item-group-wrapper {
background: #fff;
}
body.page-design .autocomplete-group-wrapper .inline-input {
margin-bottom: 10px;
width: 100%;
}
body.page-design .autocomplete-group-wrapper .item-group-wrapper {
padding: 10px;
min-height: 230px;
overflow: auto;
background-color: #f5f5f5;
border-radius: 6px;
}
body.page-design .autocomplete-group-wrapper .item-group-wrapper .item {
overflow: hidden;
position: relative;
padding: 5px 8px;
margin-bottom: 4px;
background: #fff;
border: 1px solid #eee;
cursor: move;
display: flex;
align-items: center;
justify-content: space-between;
}
body.page-design .autocomplete-group-wrapper .item-group-wrapper .item:hover {
border-color: #aaa;
}
body.page-design .autocomplete-group-wrapper .item-group-wrapper .item div {
display: flex;
line-height: 1;
width: calc(100% - 16px);
align-items: center;
}
body.page-design .autocomplete-group-wrapper .item-group-wrapper .item div i {
margin-right: 4px;
}
body.page-design .autocomplete-group-wrapper .item-group-wrapper .item span {
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
body.page-design .autocomplete-group-wrapper .item-group-wrapper .item i {
color: #999;
font-weight: 400;
}
body.page-design .autocomplete-group-wrapper .item-group-wrapper .item i.right {
cursor: pointer;
}
body.page-design .autocomplete-group-wrapper .item-group-wrapper .item i.right:hover {
color: #222;
}
.footer-link-item {
padding: 6px 10px;
background: #f5f5f5;
margin-bottom: 10px;
position: relative;
}
.footer-link-item:hover .remove-item {
display: block;
}
.footer-link-item .icon-rank {
position: absolute;
top: 11px;
left: 10px;
z-index: 9;
}
.footer-link-item .link-selector-wrap > .title {
padding-left: 20px;
}
.footer-link-item .remove-item {
position: absolute;
display: none;
top: 0;
right: 0;
background: red;
color: #fff;
z-index: 9;
padding: 0 4px;
cursor: pointer;
border-radius: 0 0 0 4px;
}
.footer-link-item .remove-item:hover {
background: #c70000;
}
.file-manager-box .layui-layer-title {
background-color: #293042;
color: #fff;
border-color: #404e72;
}
.file-manager-box .layui-layer-ico {
background: url("/image/close.png") no-repeat;
background-size: cover;
background-position: center center;
}
.link-selector-wrap > .title {
margin-bottom: 6px;
position: relative;
font-size: 12px;
}
.link-selector-wrap > .title i {
margin-right: 4px;
color: #0072ff;
}
.link-selector-wrap .selector-type {
position: relative;
outline: none;
}
.link-selector-wrap .selector-type .title {
border: 1px solid #eee;
padding: 6px 16px 6px 6px;
font-size: 12px;
cursor: pointer;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
border-radius: 2px;
background-color: #fff;
}
.link-selector-wrap .selector-type .title:hover {
border-color: #ddd;
}
.link-selector-wrap .selector-type .title:before {
content: "\f282";
font-family: "bootstrap-icons";
position: absolute;
right: 10px;
top: 8px;
}
.link-selector-wrap .selector-type .selector-content {
position: absolute;
z-index: 999;
top: calc(100% + 2px);
border-radius: 2px;
left: 0;
width: 100%;
background-color: #fff;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.25);
display: none;
}
.link-selector-wrap .selector-type .selector-content.active {
display: block;
}
.link-selector-wrap .selector-type .selector-content > div {
padding: 6px 10px;
cursor: pointer;
}
.link-selector-wrap .selector-type .selector-content > div:hover {
background-color: #e0fcf6;
}
.link-dialog-box .el-dialog__header {
padding: 0;
}
.link-dialog-box .el-dialog__header .el-dialog__headerbtn {
top: 14px;
font-size: 20px;
}
.link-dialog-box .el-dialog__header .el-dialog__headerbtn i {
color: #fff;
}
.link-dialog-box .el-dialog__body {
padding-bottom: 10px;
}
.link-dialog-box .el-dialog__footer .el-button {
padding: 10px 20px;
min-width: 80px;
}
.link-dialog-box .link-dialog-header {
padding: 10px 20px;
background-color: #409eff;
}
.link-dialog-box .link-dialog-header .title {
font-weight: bold;
color: #fff;
font-size: 16px;
}
.link-dialog-box .link-dialog-header div.input-with-select {
margin-top: 16px;
display: flex;
align-items: center;
}
.link-dialog-box .link-dialog-header div.input-with-select input {
height: 34px;
border: none;
border-radius: 4px 0 0 4px;
flex: 1;
padding: 0 10px;
outline: none;
}
.link-dialog-box .link-dialog-header div.input-with-select button {
color: #333;
border: none;
background-color: #eee;
height: 34px;
line-height: 36px;
border-radius: 0 4px 4px 0;
padding: 0 14px;
overflow: hidden;
}
.link-dialog-box .link-dialog-content .product-search {
margin: -30px -20px 10px;
padding: 6px 20px;
text-align: left;
background-color: #f3f4f7;
display: flex;
justify-content: space-between;
align-items: center;
}
.link-dialog-box .link-dialog-content .product-search > a {
border: 1px solid #ccc;
background-color: #fff;
padding: 0 10px;
border-radius: 4px;
height: 26px;
line-height: 26px;
color: #333;
text-decoration: none;
}
.link-dialog-box .link-dialog-content .product-search .el-input-group__append {
background-color: #0072ff;
color: #fff;
margin-top: -1px;
}
.link-dialog-box .link-dialog-content .product-info {
height: 340px;
}
.link-dialog-box .link-dialog-content .product-info .product-info-title {
background-color: #dee1e9;
display: flex;
justify-content: space-between;
padding: 6px 20px 6px 38px;
text-align: left;
color: #30344f;
font-size: 14px;
}
.link-dialog-box .link-dialog-content .product-info .product-list {
padding-left: 0;
list-style: none;
margin-top: 4px;
margin-bottom: 0;
height: 314px;
overflow-y: auto;
}
.link-dialog-box .link-dialog-content .product-info .product-list.static {
height: 340px;
}
.link-dialog-box .link-dialog-content .product-info .product-list li {
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px 10px;
border-bottom: 1px solid #eee;
}
.link-dialog-box .link-dialog-content .product-info .product-list li:not(.no-status) {
cursor: pointer;
}
.link-dialog-box .link-dialog-content .product-info .product-list li:not(.no-status):hover {
background-color: #e0fcf6;
}
.link-dialog-box .link-dialog-content .product-info .product-list li .left {
flex: 1;
display: flex;
align-items: center;
margin-right: 20px;
}
.link-dialog-box .link-dialog-content .product-info .product-list li .left .checkbox-plus {
margin-right: 12px;
flex: 0 0 14px;
height: 14px;
border: 1px solid #ddd;
border-radius: 50%;
display: flex;
position: relative;
justify-content: center;
align-items: center;
}
.link-dialog-box .link-dialog-content .product-info .product-list li .left .checkbox-plus:not(.no-status) {
cursor: pointer;
}
.link-dialog-box .link-dialog-content .product-info .product-list li .left .checkbox-plus:before {
content: "";
width: 10px;
height: 10px;
background-color: #0072ff;
border-radius: 50%;
display: none;
}
.link-dialog-box .link-dialog-content .product-info .product-list li .left .checkbox-plus.active {
border-color: #0072ff;
box-shadow: 0px 0px 4px #0072ff;
}
.link-dialog-box .link-dialog-content .product-info .product-list li .left .checkbox-plus.active:before {
display: block;
}
.link-dialog-box .link-dialog-content .product-info .product-list li .left .checkbox-plus.no-status {
background-color: #ddd;
}
.link-dialog-box .link-dialog-content .product-info .product-list li .left > div {
display: -webkit-box;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
word-break: break-all;
overflow: hidden;
}
.link-dialog-box .link-dialog-content .product-info .product-list li .right {
border: 1px solid #ddd;
border-radius: 2px;
padding: 2px 6px;
color: #aaa;
font-size: 12px;
background-color: #fff;
}
.link-dialog-box .link-dialog-content .product-info .product-list li .right.ok {
color: #52c41a;
border-color: #b7eb8f;
}
.link-dialog-box .link-dialog-content .product-info .product-list li img {
width: 46px;
margin-right: 10px;
}
.link-dialog-box .link-dialog-content .product-info-no {
text-align: center;
font-size: 14px;
}
.link-dialog-box .link-dialog-content .product-info-no > div {
display: block;
}
.link-dialog-box .link-dialog-content .product-info-no .icon {
margin: 50px 0 20px;
}
.link-dialog-box .link-dialog-content .product-info-no .icon i {
font-size: 100px;
color: #8c8c8c;
}
.link-dialog-box .link-dialog-content .product-info-no .no-text {
font-size: 16px;
}
.link-dialog-box .link-dialog-content .product-info-no a {
color: #0072ff;
text-decoration: underline;
}
.link-dialog-box .el-dialog__footer {
background-color: #f6f6f6;
padding: 10px 20px;
}

View File

@ -1,288 +0,0 @@
@charset "UTF-8";
/**
* @copyright 2022 beikeshop.com - All Rights Reserved.
* @link https://beikeshop.com
* @Author pu shuo <pushuo@guangda.work>
* @Date 2022-08-02 19:19:52
* @LastEditTime 2022-09-16 19:06:56
*/
[v-cloak] {
display: none;
}
body.page-filemanager {
height: 100vh;
overflow: hidden;
font-size: 12px;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none; /* CSS3属性 */
}
body.page-filemanager [class*=" el-icon-"], body.page-filemanager [class^=el-icon-] {
font-weight: 600;
}
body.page-filemanager .filemanager-wrap {
display: flex;
height: 100vh;
position: relative;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar {
width: 20%;
background-color: #293042;
overflow-y: auto;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar::-webkit-scrollbar {
width: 2px;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar::-webkit-scrollbar-thumb {
background: #409EFF;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar::-webkit-scrollbar-track {
background: transparent;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar .el-tree {
background-color: transparent;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar .el-tree .el-tree-node__content {
color: #eee;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar .el-tree .el-tree-node__content:hover {
background-color: #434d66;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar .el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
background-color: #434d66;
color: #fff;
border-left: 2px solid #409EFF;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar .tree-wrap .el-tree-node.is-current > .el-tree-node__content .right {
display: block;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar .tree-wrap .el-tree-node__content {
height: 32px;
background-color: transparent;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar .tree-wrap .el-tree-node__content:hover .right {
display: block;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar .tree-wrap .custom-tree-node {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar .tree-wrap .custom-tree-node .right {
display: none;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar .tree-wrap .custom-tree-node .right span {
margin-right: 6px;
}
body.page-filemanager .filemanager-wrap .filemanager-navbar .tree-wrap .custom-tree-node .right span:hover {
color: #409EFF;
}
body.page-filemanager .filemanager-wrap .filemanager-divider {
top: 0;
width: 4px;
cursor: col-resize;
}
body.page-filemanager .filemanager-wrap .filemanager-divider:hover {
background: #409EFF;
}
body.page-filemanager .filemanager-wrap .filemanager-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-head {
height: 56px;
position: relative;
display: flex;
background-color: #fff;
align-items: center;
justify-content: space-between;
padding: 0 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-head .left a {
margin-right: 36px;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-head .left a:not(.is-disabled) {
color: #17191c;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-head .left a.is-disabled i {
color: #a6d2ff;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-head .left a i {
color: #409EFF;
font-weight: 600;
}
@media (max-width: 768px) {
body.page-filemanager .filemanager-wrap .filemanager-content .content-head {
height: 140px;
display: block;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-head .left {
margin-bottom: 5px;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-head .left a {
margin-right: 25px;
}
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-center {
height: calc(100% - 56px);
background: #f7f9fc;
padding: 16px 6px;
overflow-y: auto;
align-content: flex-start;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-center::-webkit-scrollbar {
width: 8px;
height: 1px;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-center::-webkit-scrollbar-thumb {
border-radius: 4px;
background: #ccc;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-center::-webkit-scrollbar-track {
background: transparent;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-center .image-list {
display: inline-block;
background: #fff;
margin: 0 8px 16px;
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.07);
cursor: pointer;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-center .image-list .img {
width: 137px;
height: 137px;
display: flex;
align-items: center;
justify-content: center;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-center .image-list .img img {
max-width: 100%;
max-height: 100%;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-center .image-list .img i {
font-size: 86px;
color: #333;
font-weight: 400;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-center .image-list.active {
outline: 1px solid #409EFF;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-center .image-list .text {
border-top: 1px solid #eee;
font-size: 12px;
width: 137px;
padding: 6px 8px;
display: flex;
align-items: center;
justify-content: space-between;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-center .image-list .text span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-center .image-list .text .el-icon-check {
color: #409EFF;
font-size: 18px;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-footer {
height: 56px;
padding: 0 16px;
background-color: #fff;
display: flex;
box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.04);
align-items: center;
justify-content: space-between;
}
@media (max-width: 768px) {
body.page-filemanager .filemanager-wrap .filemanager-content .content-footer {
height: 120px;
padding: 0;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-footer .el-pagination {
white-space: inherit;
padding: 0;
text-align: center;
}
body.page-filemanager .filemanager-wrap .filemanager-content .content-footer .el-pagination__jump {
margin: 0;
margin-top: 10px;
}
}
body.page-filemanager .upload-wrap .el-dialog__body {
padding-top: 10px;
}
body.page-filemanager .upload-wrap .upload-image {
min-height: 200px;
max-height: 300px;
overflow-y: auto;
margin-right: -4px;
padding-right: 6px;
}
body.page-filemanager .upload-wrap .upload-image::-webkit-scrollbar {
width: 8px;
height: 1px;
}
body.page-filemanager .upload-wrap .upload-image::-webkit-scrollbar-thumb {
border-radius: 4px;
background: #ddd;
}
body.page-filemanager .upload-wrap .upload-image .list {
margin-bottom: 12px;
padding-bottom: 14px;
font-size: 12px;
border-bottom: 1px solid #f1f1f1;
}
body.page-filemanager .upload-wrap .upload-image .list .info {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
body.page-filemanager .upload-wrap .upload-image .list .name {
color: #111111;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
body.page-filemanager .upload-wrap .upload-image .list .status {
white-space: nowrap;
}
body.page-filemanager .upload-wrap .el-progress-bar__outer {
background-color: #ccc;
}
body.page-filemanager .photos-upload {
overflow: hidden;
}
body.page-filemanager .photos-upload .el-upload {
width: 100%;
display: block;
margin-bottom: 10px;
}
body.page-filemanager .photos-upload .el-loading-spinner {
top: 35%;
}
body.page-filemanager .photos-upload .el-upload-dragger {
width: 100%;
height: auto;
background-color: transparent;
transition: all 0.3s ease-in-out;
}
body.page-filemanager .photos-upload .el-upload-dragger:hover {
border-color: #8874d8;
}
body.page-filemanager .photos-upload .el-upload-dragger .el-icon-upload {
margin: 10px 0;
}
body.page-filemanager .photos-upload .el-upload-dragger .el-upload__text {
margin-bottom: 10px;
color: #aaa;
}
body.page-filemanager .photos-upload input[type=file] {
display: none;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2
public/cache/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

0
public/cache/index.html vendored Executable file
View File

BIN
public/catalog/demo/banner/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
public/catalog/demo/product/.DS_Store vendored Normal file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More