Compare commits
57 Commits
master
...
feature-lo
| Author | SHA1 | Date |
|---|---|---|
|
|
698f5166f4 | |
|
|
71f1c85492 | |
|
|
61129e6971 | |
|
|
b35b056803 | |
|
|
2f608f6861 | |
|
|
eede9e4e25 | |
|
|
84d6777d0f | |
|
|
dcdfd1facc | |
|
|
5663f321dc | |
|
|
9ad042da88 | |
|
|
650184d860 | |
|
|
6af1a100e0 | |
|
|
bfc31e06c4 | |
|
|
525a6c1740 | |
|
|
3c62e8af7d | |
|
|
19df41c8d4 | |
|
|
2e36e35e17 | |
|
|
b679d66adb | |
|
|
39cd329a47 | |
|
|
35114816fe | |
|
|
cf7825d519 | |
|
|
82af4074de | |
|
|
64fea5c8b3 | |
|
|
5a83342538 | |
|
|
df8cff8869 | |
|
|
caf65fa1d8 | |
|
|
e6882598fe | |
|
|
8e34333803 | |
|
|
5e978f268c | |
|
|
f55c520015 | |
|
|
3c96766442 | |
|
|
4032cfaaa1 | |
|
|
1dd22c3e02 | |
|
|
a318806058 | |
|
|
a4bbe1c4d8 | |
|
|
ab24642207 | |
|
|
399bff4def | |
|
|
270623a9ad | |
|
|
18546a254c | |
|
|
f1f2a7d1bf | |
|
|
afa9b0be6f | |
|
|
3a7aacb474 | |
|
|
7f31190de9 | |
|
|
64d4c6c860 | |
|
|
8ab026f52e | |
|
|
b7a2839ee4 | |
|
|
be017fdb7e | |
|
|
8d7c37509f | |
|
|
d1c5514682 | |
|
|
67c3c7ab61 | |
|
|
39c5134312 | |
|
|
70c2160aca | |
|
|
02516b7825 | |
|
|
bfb1ea283c | |
|
|
fa79136adc | |
|
|
1713d0712c | |
|
|
4f1beb97d9 |
28
.env
|
|
@ -1,28 +0,0 @@
|
||||||
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=
|
|
||||||
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
/public/hot
|
/public/hot
|
||||||
/public/storage
|
/public/storage
|
||||||
/public/upload/avatar
|
/public/upload/avatar
|
||||||
/public/build/beike/*
|
#/public/build/beike/*
|
||||||
/public/install/css/*
|
/public/install/css/*
|
||||||
/public/sitemap.xml
|
/public/sitemap.xml
|
||||||
/storage/*.key
|
/storage/*.key
|
||||||
|
|
|
||||||
119
LICENSE
|
|
@ -52,121 +52,4 @@ Licensor's trademarks, copyrights, patents, trade secrets or any other
|
||||||
intellectual property. No patent license is granted to make, use, sell, offer
|
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
|
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
|
licensed claims defined in Section 2. No license is granted to the trademarks
|
||||||
of Licensor even if such marks are included in the Original Work. Nothing in
|
of Licensor even if suc
|
||||||
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.
|
|
||||||
|
|
@ -33,7 +33,7 @@ class CountryController extends Controller
|
||||||
|
|
||||||
public function store(Request $request)
|
public function store(Request $request)
|
||||||
{
|
{
|
||||||
$country = CountryRepo::create($request->only('name', 'code', 'sort_order', 'status'));
|
$country = CountryRepo::create($request->only('name', 'icon', 'code', 'sort_order', 'status'));
|
||||||
|
|
||||||
hook_action('admin.country.store.after', $country);
|
hook_action('admin.country.store.after', $country);
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ class CountryController extends Controller
|
||||||
|
|
||||||
public function update(Request $request, int $id)
|
public function update(Request $request, int $id)
|
||||||
{
|
{
|
||||||
$country = CountryRepo::update($id, $request->only('name', 'code', 'sort_order', 'status'));
|
$country = CountryRepo::update($id, $request->only('name', 'icon', 'code', 'sort_order', 'status'));
|
||||||
|
|
||||||
hook_action('admin.country.store.after', $country);
|
hook_action('admin.country.store.after', $country);
|
||||||
|
|
||||||
|
|
@ -57,4 +57,16 @@ class CountryController extends Controller
|
||||||
|
|
||||||
return json_success(trans('common.deleted_success'));
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class DesignController extends Controller
|
||||||
$data = [
|
$data = [
|
||||||
'editors' => [
|
'editors' => [
|
||||||
'editor-slide_show', 'editor-image401', 'editor-tab_product', 'editor-product', 'editor-image100',
|
'editor-slide_show', 'editor-image401', 'editor-tab_product', 'editor-product', 'editor-image100',
|
||||||
'editor-brand', 'editor-icons', 'editor-rich_text', 'editor-image200', 'editor-image300',
|
'editor-brand', 'editor-icons', 'editor-rich_text', 'editor-image200', 'editor-image300','editor-slide_show_video',
|
||||||
],
|
],
|
||||||
'design_settings' => system_setting('base.design_setting'),
|
'design_settings' => system_setting('base.design_setting'),
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,255 @@
|
||||||
|
<?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'), []);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -118,6 +118,22 @@ 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)
|
public function destroy(Request $request, Product $product)
|
||||||
{
|
{
|
||||||
$product->delete();
|
$product->delete();
|
||||||
|
|
@ -147,20 +163,21 @@ class ProductController extends Controller
|
||||||
$product = hook_filter('admin.product.form.product', $product);
|
$product = hook_filter('admin.product.form.product', $product);
|
||||||
$taxClasses = TaxClassRepo::getList();
|
$taxClasses = TaxClassRepo::getList();
|
||||||
array_unshift($taxClasses, ['title' => trans('admin/builder.text_no'), 'id' => 0]);
|
array_unshift($taxClasses, ['title' => trans('admin/builder.text_no'), 'id' => 0]);
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'product' => $product,
|
'product' => $product,
|
||||||
'descriptions' => $descriptions ?? [],
|
'descriptions' => $descriptions ?? [],
|
||||||
'category_ids' => $categoryIds ?? [],
|
'category_ids' => $categoryIds ?? [],
|
||||||
'product_attributes' => ProductAttributeResource::collection($product->attributes),
|
'product_attributes' => ProductAttributeResource::collection($product->attributes),
|
||||||
'relations' => ProductResource::collection($product->relations)->resource,
|
'relations' => ProductResource::collection($product->relations)->resource,
|
||||||
'languages' => LanguageRepo::all(),
|
'languages' => LanguageRepo::all(),
|
||||||
'tax_classes' => $taxClasses,
|
'tax_classes' => $taxClasses,
|
||||||
'weight_classes' => Weight::getWeightUnits(),
|
'weight_classes' => Weight::getWeightUnits(),
|
||||||
'source' => [
|
'source' => [
|
||||||
'categories' => CategoryRepo::flatten(locale(), false),
|
'categories' => CategoryRepo::flatten(locale(),FALSE),
|
||||||
],
|
],
|
||||||
'_redirect' => $this->getRedirect(),
|
'_redirect' => $this->getRedirect(),
|
||||||
|
'unit_list' => Product::getUnitList(),
|
||||||
|
'trade_term' => Product::getTradeTermList(),
|
||||||
];
|
];
|
||||||
|
|
||||||
$data = hook_filter('admin.product.form.data', $data);
|
$data = hook_filter('admin.product.form.data', $data);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
namespace Beike\Admin\Http\Controllers;
|
namespace Beike\Admin\Http\Controllers;
|
||||||
|
|
||||||
use Beike\Admin\Repositories\RegionRepo;
|
use Beike\Admin\Repositories\RegionRepo;
|
||||||
|
use Beike\Models\Region;
|
||||||
use Beike\Repositories\CountryRepo;
|
use Beike\Repositories\CountryRepo;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
|
@ -52,4 +53,19 @@ class RegionController
|
||||||
|
|
||||||
return json_success(trans('common.deleted_success'));
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
<?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'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,14 +32,27 @@ class ProductRequest extends FormRequest
|
||||||
*/
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
$rules = [
|
||||||
'descriptions.*.name' => 'required|string|min:3|max:128',
|
'descriptions.*.name' => 'required|string|min:3|max:128',
|
||||||
'brand_id' => 'int',
|
'brand_id' => 'int',
|
||||||
'skus.*.sku' => 'required|string',
|
// 'skus.*.sku' => 'required|string',
|
||||||
'skus.*.price' => 'required|numeric',
|
'skus.*.price' => 'required|numeric',
|
||||||
'skus.*.origin_price' => 'required|numeric',
|
// 'skus.*.origin_price' => 'required|numeric',
|
||||||
'skus.*.cost_price' => '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()
|
public function attributes()
|
||||||
|
|
@ -47,10 +60,12 @@ class ProductRequest extends FormRequest
|
||||||
return [
|
return [
|
||||||
'descriptions.*.name' => trans('product.name'),
|
'descriptions.*.name' => trans('product.name'),
|
||||||
'brand_id' => trans('product.brand'),
|
'brand_id' => trans('product.brand'),
|
||||||
'skus.*.sku' => trans('product.sku'),
|
// 'skus.*.sku' => trans('product.sku'),
|
||||||
'skus.*.price' => trans('product.price'),
|
'skus.*.price' => trans('product.price'),
|
||||||
'skus.*.origin_price' => trans('product.origin_price'),
|
'skus.*.origin_price' => trans('product.origin_price'),
|
||||||
'skus.*.cost_price' => trans('product.cost_price'),
|
// 'skus.*.cost_price' => trans('product.cost_price'),
|
||||||
|
'minimum_order' => trans('product.minimum_order'),
|
||||||
|
'piece_to_batch' => trans('product.one_batch_is_equal_to'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,9 +24,9 @@ class ProductResource extends JsonResource
|
||||||
return image_resize($image);
|
return image_resize($image);
|
||||||
}, $this->images ?? []),
|
}, $this->images ?? []),
|
||||||
'name' => $this->description->name ?? '',
|
'name' => $this->description->name ?? '',
|
||||||
'model' => $masterSku->model,
|
'model' => $masterSku->model ?? '',
|
||||||
'quantity' => $masterSku->quantity,
|
'quantity' => $masterSku->quantity ?? 0,
|
||||||
'price_formatted' => currency_format($masterSku->price),
|
'price_formatted' => currency_format($masterSku->price ?? 0),
|
||||||
'active' => $this->active,
|
'active' => $this->active,
|
||||||
'position' => $this->position,
|
'position' => $this->position,
|
||||||
'url' => $this->url,
|
'url' => $this->url,
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ use Beike\Admin\View\Components\Form\InputLocale;
|
||||||
use Beike\Admin\View\Components\Form\RichText;
|
use Beike\Admin\View\Components\Form\RichText;
|
||||||
use Beike\Admin\View\Components\Form\Select;
|
use Beike\Admin\View\Components\Form\Select;
|
||||||
use Beike\Admin\View\Components\Form\SwitchRadio;
|
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\Form\Textarea;
|
||||||
use Beike\Admin\View\Components\Header;
|
use Beike\Admin\View\Components\Header;
|
||||||
use Beike\Admin\View\Components\NoData;
|
use Beike\Admin\View\Components\NoData;
|
||||||
|
|
@ -139,6 +140,7 @@ class AdminServiceProvider extends ServiceProvider
|
||||||
'alert' => Alert::class,
|
'alert' => Alert::class,
|
||||||
'form-input-locale' => InputLocale::class,
|
'form-input-locale' => InputLocale::class,
|
||||||
'form-switch' => SwitchRadio::class,
|
'form-switch' => SwitchRadio::class,
|
||||||
|
'form-switch-status' => SwitchRadioStatus::class,
|
||||||
'form-input' => Input::class,
|
'form-input' => Input::class,
|
||||||
'form-select' => Select::class,
|
'form-select' => Select::class,
|
||||||
'form-image' => Image::class,
|
'form-image' => Image::class,
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ Route::prefix($adminName)
|
||||||
Route::middleware('can:countries_create')->post('countries', [Controllers\CountryController::class, 'store'])->name('countries.store');
|
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_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_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');
|
Route::middleware('can:zones_index')->get('zones', [Controllers\ZoneController::class, 'index'])->name('zones.index');
|
||||||
|
|
@ -214,15 +215,27 @@ Route::prefix($adminName)
|
||||||
Route::middleware('can:products_index')->get('products', [Controllers\ProductController::class, 'index'])->name('products.index');
|
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')->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_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_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_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: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_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_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_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_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
|
// RMA
|
||||||
Route::middleware('can:rmas_update')->post('rmas/history/{id}', [Controllers\RmaController::class, 'addHistory'])->name('rmas.add_history');
|
Route::middleware('can:rmas_update')->post('rmas/history/{id}', [Controllers\RmaController::class, 'addHistory'])->name('rmas.add_history');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,28 @@ class ProductService
|
||||||
return $this->createOrUpdate($product, $data);
|
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
|
protected function createOrUpdate(Product $product, array $data): Product
|
||||||
{
|
{
|
||||||
$isUpdating = $product->id > 0;
|
$isUpdating = $product->id > 0;
|
||||||
|
|
@ -55,8 +77,8 @@ class ProductService
|
||||||
$skus = [];
|
$skus = [];
|
||||||
foreach ($data['skus'] as $index => $sku) {
|
foreach ($data['skus'] as $index => $sku) {
|
||||||
$sku['position'] = $index;
|
$sku['position'] = $index;
|
||||||
$sku['origin_price'] = (float) $sku['origin_price'];
|
$sku['origin_price'] = (float) ($sku['origin_price'] ?? 0);
|
||||||
$sku['cost_price'] = (float) $sku['cost_price'];
|
$sku['cost_price'] = (float) ($sku['cost_price'] ?? 0);
|
||||||
$sku['quantity'] = (int) $sku['quantity'];
|
$sku['quantity'] = (int) $sku['quantity'];
|
||||||
$skus[] = $sku;
|
$skus[] = $sku;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -124,6 +124,13 @@ class Sidebar extends Component
|
||||||
'prefixes' => $this->getInquirySubPrefix(),
|
'prefixes' => $this->getInquirySubPrefix(),
|
||||||
'children' => $this->getInquirySubRoutes(),
|
'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);
|
return hook_filter('admin.components.sidebar.menus', $menus);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?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' => '',
|
||||||
|
'style' => 'font-size: 40px;',
|
||||||
|
];
|
||||||
|
|
||||||
|
return view('admin::pages.design.module.slideshow_video', $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,8 +14,8 @@ namespace Beike\Libraries;
|
||||||
class Weight
|
class Weight
|
||||||
{
|
{
|
||||||
public const WEIGHT_CLASS = [
|
public const WEIGHT_CLASS = [
|
||||||
'kg' => 0.001,
|
|
||||||
'g' => 1,
|
'g' => 1,
|
||||||
|
'kg' => 0.001,
|
||||||
'oz' => 0.035,
|
'oz' => 0.035,
|
||||||
'lb' => 0.0022046,
|
'lb' => 0.0022046,
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class Country extends Base
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $fillable = ['name', 'country_id', 'code', 'sort_order', 'status'];
|
protected $fillable = ['name','icon', 'country_id', 'code', 'sort_order', 'status'];
|
||||||
|
|
||||||
public function zones(): HasMany
|
public function zones(): HasMany
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
<?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');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?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() : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ use Beike\Notifications\UpdateOrderNotification;
|
||||||
use Beike\Services\StateMachineService;
|
use Beike\Services\StateMachineService;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
|
||||||
class Order extends Base
|
class Order extends Base
|
||||||
|
|
@ -64,6 +65,10 @@ class Order extends Base
|
||||||
return $this->hasMany(OrderPayment::class);
|
return $this->hasMany(OrderPayment::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function logistics(): HasOne{
|
||||||
|
return $this->hasOne(Logistics::class,'id','shipping_method_code');
|
||||||
|
}
|
||||||
|
|
||||||
public function subTotal()
|
public function subTotal()
|
||||||
{
|
{
|
||||||
$totals = $this->orderTotals;
|
$totals = $this->orderTotals;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,26 @@ class Product extends Base
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use SoftDeletes;
|
use SoftDeletes;
|
||||||
|
|
||||||
protected $fillable = ['images', 'video', 'position', 'brand_id', 'tax_class_id', 'weight', 'weight_class', 'active', 'variables', 'price_setting'];
|
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 $casts = [
|
protected $casts = [
|
||||||
'active' => 'boolean',
|
'active' => 'boolean',
|
||||||
|
|
@ -23,8 +42,11 @@ class Product extends Base
|
||||||
public function getNumPricesByNum($num)
|
public function getNumPricesByNum($num)
|
||||||
{
|
{
|
||||||
$descNumPrices = array_reverse($this->numprices->toArray());
|
$descNumPrices = array_reverse($this->numprices->toArray());
|
||||||
|
$multiple = 1;
|
||||||
|
if($this->sales_method == 'batches') $multiple = (int)$this->piece_to_batch;
|
||||||
|
|
||||||
foreach($descNumPrices as $numprice){
|
foreach($descNumPrices as $numprice){
|
||||||
if($num >= $numprice['num']){
|
if($num >= ($numprice['num'] * $multiple)){
|
||||||
return $numprice['price'];
|
return $numprice['price'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -105,4 +127,138 @@ class Product extends Base
|
||||||
|
|
||||||
return $images[0] ?? '';
|
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'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,5 @@ class ProductDescription extends Base
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $fillable = ['locale', 'name', 'content', 'meta_title', 'meta_description', 'meta_keywords'];
|
protected $fillable = ['locale', 'name', 'content', 'meta_title', 'meta_description', 'meta_keywords', 'unit'];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
namespace Beike\Repositories;
|
namespace Beike\Repositories;
|
||||||
|
|
||||||
use Beike\Models\Country;
|
use Beike\Models\Country;
|
||||||
|
use Beike\Models\RegionZone;
|
||||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
|
@ -26,6 +27,7 @@ class CountryRepo
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'name' => $data['name'] ?? '',
|
'name' => $data['name'] ?? '',
|
||||||
|
'icon' => $data['icon'] ?? '',
|
||||||
'code' => $data['code'] ?? '',
|
'code' => $data['code'] ?? '',
|
||||||
'sort_order' => (int) $data['sort_order'] ?? 0,
|
'sort_order' => (int) $data['sort_order'] ?? 0,
|
||||||
'status' => (bool) $data['status'] ?? 0,
|
'status' => (bool) $data['status'] ?? 0,
|
||||||
|
|
@ -121,4 +123,60 @@ class CountryRepo
|
||||||
{
|
{
|
||||||
return Country::query()->select('id', 'name')->get();
|
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 [];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,390 @@
|
||||||
|
<?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 ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ namespace Beike\Repositories;
|
||||||
|
|
||||||
use Beike\Models\Address;
|
use Beike\Models\Address;
|
||||||
use Beike\Models\Customer;
|
use Beike\Models\Customer;
|
||||||
|
use Beike\Models\Logistics;
|
||||||
use Beike\Models\Order;
|
use Beike\Models\Order;
|
||||||
use Beike\Services\StateMachineService;
|
use Beike\Services\StateMachineService;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
|
@ -121,6 +122,11 @@ class OrderRepo
|
||||||
$builder->where('status', $status);
|
$builder->where('status', $status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$builder->with(['orderShipments','logistics'=>function($query){
|
||||||
|
$query->select(['id','day_min','day_max']);
|
||||||
|
}]);
|
||||||
|
|
||||||
|
|
||||||
return $builder;
|
return $builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,6 +223,12 @@ class OrderRepo
|
||||||
$currency = CurrencyRepo::findByCode($currencyCode);
|
$currency = CurrencyRepo::findByCode($currencyCode);
|
||||||
$currencyValue = $currency->value ?? 1;
|
$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([
|
$order = new Order([
|
||||||
'number' => self::generateOrderNumber(),
|
'number' => self::generateOrderNumber(),
|
||||||
'customer_id' => $customer->id ?? 0,
|
'customer_id' => $customer->id ?? 0,
|
||||||
|
|
@ -235,7 +247,7 @@ class OrderRepo
|
||||||
'user_agent' => request()->userAgent(),
|
'user_agent' => request()->userAgent(),
|
||||||
'status' => StateMachineService::CREATED,
|
'status' => StateMachineService::CREATED,
|
||||||
'shipping_method_code' => $shippingMethodCode,
|
'shipping_method_code' => $shippingMethodCode,
|
||||||
'shipping_method_name' => trans($shippingMethodCode),
|
'shipping_method_name' => $shipping_method_name,
|
||||||
'shipping_customer_name' => $shippingAddress->name,
|
'shipping_customer_name' => $shippingAddress->name,
|
||||||
'shipping_calling_code' => $shippingAddress->calling_code ?? 0,
|
'shipping_calling_code' => $shippingAddress->calling_code ?? 0,
|
||||||
'shipping_telephone' => $shippingAddress->phone ?? '',
|
'shipping_telephone' => $shippingAddress->phone ?? '',
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ class DesignService
|
||||||
$content['module_code'] = $moduleCode;
|
$content['module_code'] = $moduleCode;
|
||||||
if ($moduleCode == 'slideshow') {
|
if ($moduleCode == 'slideshow') {
|
||||||
return self::handleSlideShow($content);
|
return self::handleSlideShow($content);
|
||||||
|
} elseif ($moduleCode == 'slideshow_video') {
|
||||||
|
return self::handleSlideShow($content);
|
||||||
} elseif (in_array($moduleCode, ['image401', 'image100', 'image200', 'image300'])) {
|
} elseif (in_array($moduleCode, ['image401', 'image100', 'image200', 'image300'])) {
|
||||||
return self::handleImage401($content);
|
return self::handleImage401($content);
|
||||||
} elseif ($moduleCode == 'brand') {
|
} elseif ($moduleCode == 'brand') {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
namespace Beike\Services;
|
namespace Beike\Services;
|
||||||
|
|
||||||
use Beike\Admin\Http\Resources\PluginResource;
|
use Beike\Admin\Http\Resources\PluginResource;
|
||||||
|
use Beike\Models\Logistics;
|
||||||
use Beike\Repositories\PluginRepo;
|
use Beike\Repositories\PluginRepo;
|
||||||
use Beike\Shop\Services\CheckoutService;
|
use Beike\Shop\Services\CheckoutService;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
@ -43,12 +44,27 @@ class ShippingMethodService
|
||||||
if ($quotes) {
|
if ($quotes) {
|
||||||
$pluginResource = (new PluginResource($plugin))->jsonSerialize();
|
$pluginResource = (new PluginResource($plugin))->jsonSerialize();
|
||||||
$shippingMethods[] = [
|
$shippingMethods[] = [
|
||||||
|
'is_logistics' => 0,
|
||||||
'code' => $pluginCode,
|
'code' => $pluginCode,
|
||||||
'name' => $pluginResource['name'],
|
'name' => $pluginResource['name'],
|
||||||
'quotes' => $quotes,
|
'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;
|
return $shippingMethods;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ namespace Beike\Shop\Http\Controllers;
|
||||||
use Beike\Models\ProductSku;
|
use Beike\Models\ProductSku;
|
||||||
use Beike\Shop\Http\Requests\CartRequest;
|
use Beike\Shop\Http\Requests\CartRequest;
|
||||||
use Beike\Shop\Services\CartService;
|
use Beike\Shop\Services\CartService;
|
||||||
|
use Beike\Shop\Services\CheckoutService;
|
||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
|
@ -18,7 +19,7 @@ class CartController extends Controller
|
||||||
$data = [
|
$data = [
|
||||||
'data' => CartService::reloadData(),
|
'data' => CartService::reloadData(),
|
||||||
];
|
];
|
||||||
|
$data['totals'] = $this->getOrderMoney($data['data']['carts']);
|
||||||
$data = hook_filter('cart.index.data', $data);
|
$data = hook_filter('cart.index.data', $data);
|
||||||
|
|
||||||
return view('cart/cart', $data);
|
return view('cart/cart', $data);
|
||||||
|
|
@ -38,6 +39,7 @@ class CartController extends Controller
|
||||||
CartService::select($customer, $cartIds);
|
CartService::select($customer, $cartIds);
|
||||||
|
|
||||||
$data = CartService::reloadData();
|
$data = CartService::reloadData();
|
||||||
|
$data['totals'] = $this->getOrderMoney($data['carts']);
|
||||||
|
|
||||||
$data = hook_filter('cart.select.data', $data);
|
$data = hook_filter('cart.select.data', $data);
|
||||||
|
|
||||||
|
|
@ -56,21 +58,30 @@ class CartController extends Controller
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$skuId = $request->sku_id;
|
$skuId = $request->sku_id;
|
||||||
$quantity = $request->quantity ?? 1;
|
$quantity = $request->quantity ?? 1;
|
||||||
$buyNow = (bool) $request->buy_now ?? false;
|
$buyNow = (bool) $request->buy_now ?? false;
|
||||||
$customer = current_customer();
|
$customer = current_customer();
|
||||||
|
// 多规格批量加入购物车处理
|
||||||
$sku = ProductSku::query()
|
if(is_int($skuId)){
|
||||||
->whereRelation('product', 'active', '=', true)
|
// 非多规格下单
|
||||||
->findOrFail($skuId);
|
$sku = ProductSku::query()->findOrFail($skuId);
|
||||||
|
$cart = CartService::add($sku, $quantity, $customer);
|
||||||
$cart = CartService::add($sku, $quantity, $customer);
|
$cartIds = [$cart->id];
|
||||||
if ($buyNow) {
|
}else{
|
||||||
CartService::select($customer, [$cart->id]);
|
// 多规格下单
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$cart = hook_filter('cart.store.data', $cart);
|
|
||||||
|
|
||||||
|
if ($buyNow) CartService::select($customer, $cartIds);
|
||||||
|
$cart = hook_filter('cart.store.data', $cart);
|
||||||
return json_success(trans('shop/carts.added_to_cart'), $cart);
|
return json_success(trans('shop/carts.added_to_cart'), $cart);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return json_fail($e->getMessage());
|
return json_fail($e->getMessage());
|
||||||
|
|
@ -85,17 +96,18 @@ class CartController extends Controller
|
||||||
*/
|
*/
|
||||||
public function update(CartRequest $request, $cartId): array
|
public function update(CartRequest $request, $cartId): array
|
||||||
{
|
{
|
||||||
|
// return json_fail("错误");
|
||||||
try {
|
try {
|
||||||
$customer = current_customer();
|
$customer = current_customer();
|
||||||
$quantity = (int) $request->get('quantity');
|
$quantity = (int) $request->get('quantity');
|
||||||
CartService::updateQuantity($customer, $cartId, $quantity);
|
CartService::updateQuantity($customer, $cartId, $quantity);
|
||||||
|
|
||||||
$data = CartService::reloadData();
|
$data = CartService::reloadData();
|
||||||
|
$data['totals'] = $this->getOrderMoney($data['carts']);
|
||||||
|
|
||||||
$data = hook_filter('cart.update.data', $data);
|
$data = hook_filter('cart.update.data', $data);
|
||||||
|
|
||||||
return json_success(trans('common.updated_success'), $data);
|
return json_success(trans('common.updated_success'), $data);
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return json_fail($e->getMessage());
|
return json_fail($e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
@ -136,4 +148,22 @@ class CartController extends Controller
|
||||||
|
|
||||||
return json_success(trans('common.success'), $data);
|
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'];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
namespace Beike\Shop\Http\Controllers;
|
namespace Beike\Shop\Http\Controllers;
|
||||||
|
|
||||||
|
use Beike\Models\Product;
|
||||||
use Beike\Repositories\OrderRepo;
|
use Beike\Repositories\OrderRepo;
|
||||||
use Beike\Shop\Services\CheckoutService;
|
use Beike\Shop\Services\CheckoutService;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
@ -22,6 +23,41 @@ class CheckoutController extends Controller
|
||||||
try {
|
try {
|
||||||
$data = (new CheckoutService)->checkoutData();
|
$data = (new CheckoutService)->checkoutData();
|
||||||
$data = hook_filter('checkout.index.data', $data);
|
$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);
|
return view('checkout', $data);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|
@ -57,7 +93,14 @@ class CheckoutController extends Controller
|
||||||
public function confirm()
|
public function confirm()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$data = (new CheckoutService)->confirm();
|
$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();
|
||||||
|
|
||||||
return hook_filter('checkout.confirm.data', $data);
|
return hook_filter('checkout.confirm.data', $data);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,21 @@
|
||||||
|
|
||||||
namespace Beike\Shop\Http\Controllers;
|
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\Product;
|
||||||
|
use Beike\Models\ProductSku;
|
||||||
|
use Beike\Repositories\AddressRepo;
|
||||||
|
use Beike\Repositories\CartRepo;
|
||||||
|
use Beike\Repositories\PluginRepo;
|
||||||
use Beike\Repositories\ProductRepo;
|
use Beike\Repositories\ProductRepo;
|
||||||
|
use Beike\Services\ShippingMethodService;
|
||||||
use Beike\Shop\Http\Resources\ProductDetail;
|
use Beike\Shop\Http\Resources\ProductDetail;
|
||||||
use Beike\Shop\Http\Resources\ProductSimple;
|
use Beike\Shop\Http\Resources\ProductSimple;
|
||||||
|
use Beike\Shop\Services\CartService;
|
||||||
|
use Beike\Shop\Services\CheckoutService;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class ProductController extends Controller
|
class ProductController extends Controller
|
||||||
|
|
@ -22,6 +33,7 @@ class ProductController extends Controller
|
||||||
$product = ProductRepo::getProductDetail($product);
|
$product = ProductRepo::getProductDetail($product);
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
|
'product_id' => $product->id,
|
||||||
'product' => (new ProductDetail($product))->jsonSerialize(),
|
'product' => (new ProductDetail($product))->jsonSerialize(),
|
||||||
'relations' => ProductRepo::getProductsByIds($relationIds)->jsonSerialize(),
|
'relations' => ProductRepo::getProductsByIds($relationIds)->jsonSerialize(),
|
||||||
];
|
];
|
||||||
|
|
@ -56,4 +68,154 @@ class ProductController extends Controller
|
||||||
|
|
||||||
return view('search', $data);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* ZoneController.php
|
* ZoneController.php
|
||||||
*
|
|
||||||
* @copyright 2022 beikeshop.com - All Rights Reserved
|
* @copyright 2022 beikeshop.com - All Rights Reserved
|
||||||
* @link https://beikeshop.com
|
* @link https://beikeshop.com
|
||||||
* @author TL <mengwb@guangda.work>
|
* @author TL <mengwb@guangda.work>
|
||||||
|
|
@ -11,21 +10,52 @@
|
||||||
|
|
||||||
namespace Beike\Shop\Http\Controllers;
|
namespace Beike\Shop\Http\Controllers;
|
||||||
|
|
||||||
|
use Beike\Models\Region;
|
||||||
|
use Beike\Repositories\CountryRepo;
|
||||||
use Beike\Repositories\ZoneRepo;
|
use Beike\Repositories\ZoneRepo;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class ZoneController extends Controller
|
class ZoneController extends Controller{
|
||||||
{
|
public function index(Request $request,int $countryId){
|
||||||
public function index(Request $request, int $countryId)
|
|
||||||
{
|
|
||||||
ZoneRepo::listByCountry($countryId);
|
ZoneRepo::listByCountry($countryId);
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'zones' => ZoneRepo::listByCountry($countryId),
|
'zones' => ZoneRepo::listByCountry($countryId),
|
||||||
];
|
];
|
||||||
|
$data = hook_filter('zone.index.data',$data);
|
||||||
$data = hook_filter('zone.index.data', $data);
|
return json_success(trans('common.success'),$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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,8 @@ class CartRequest extends FormRequest
|
||||||
$skuId = (int) $this->get('sku_id');
|
$skuId = (int) $this->get('sku_id');
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'sku_id' => 'required|int',
|
'sku_id' => 'required',
|
||||||
'quantity' => ['required', 'int', function ($attribute, $value, $fail) use ($skuId) {
|
'quantity' => ['required', 'int', /*function ($attribute, $value, $fail) use ($skuId) {
|
||||||
$sku = ProductSku::query()->where('id', $skuId)->first();
|
$sku = ProductSku::query()->where('id', $skuId)->first();
|
||||||
$skuQuantity = $sku->quantity;
|
$skuQuantity = $sku->quantity;
|
||||||
if ($value > $skuQuantity) {
|
if ($value > $skuQuantity) {
|
||||||
|
|
@ -46,7 +46,7 @@ class CartRequest extends FormRequest
|
||||||
if($sku->product->price_setting == 'num' && $value < $sku->product->numprices[0]->num){
|
if($sku->product->price_setting == 'num' && $value < $sku->product->numprices[0]->num){
|
||||||
$fail(trans('shop/products.quantity_error'));
|
$fail(trans('shop/products.quantity_error'));
|
||||||
}
|
}
|
||||||
}],
|
}*/],
|
||||||
'buy_now' => 'bool',
|
'buy_now' => 'bool',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ class CartDetail extends JsonResource
|
||||||
};
|
};
|
||||||
$skuCode = $sku->sku;
|
$skuCode = $sku->sku;
|
||||||
$description = $product->description;
|
$description = $product->description;
|
||||||
|
|
||||||
$productName = $description->name;
|
$productName = $description->name;
|
||||||
|
$unit = $this->unit;//$description->unit ?? '';
|
||||||
$subTotal = $price * $this->quantity;
|
$subTotal = $price * $this->quantity;
|
||||||
$image = $sku->image ?: $product->image;
|
$image = $sku->image ?: $product->image;
|
||||||
|
|
||||||
|
|
@ -37,6 +39,9 @@ class CartDetail extends JsonResource
|
||||||
'product_sku' => $skuCode,
|
'product_sku' => $skuCode,
|
||||||
'name' => $productName,
|
'name' => $productName,
|
||||||
'name_format' => sub_string($productName),
|
'name_format' => sub_string($productName),
|
||||||
|
'unit' => $unit,
|
||||||
|
'unit_format' => $unit,
|
||||||
|
'trade_term' => $this->trade_term,
|
||||||
'image' => $image,
|
'image' => $image,
|
||||||
'image_url' => image_resize($image),
|
'image_url' => image_resize($image),
|
||||||
'quantity' => $this->quantity,
|
'quantity' => $this->quantity,
|
||||||
|
|
@ -47,6 +52,10 @@ class CartDetail extends JsonResource
|
||||||
'subtotal' => $subTotal,
|
'subtotal' => $subTotal,
|
||||||
'subtotal_format' => currency_format($subTotal),
|
'subtotal_format' => currency_format($subTotal),
|
||||||
'variant_labels' => trim($sku->getVariantLabel()),
|
'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);
|
return hook_filter('resource.cart.detail', $result);
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class InquiryDetail extends JsonResource{
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$product = $productsku->product;
|
$product = $productsku->product;
|
||||||
$description = $product->description;
|
$description = $product->description ?? '';
|
||||||
return [
|
return [
|
||||||
'id' => $this->id,
|
'id' => $this->id,
|
||||||
'product_sku_id' => $this->product_sku_id,
|
'product_sku_id' => $this->product_sku_id,
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,15 @@ namespace Beike\Shop\Http\Resources;
|
||||||
|
|
||||||
use Illuminate\Http\Resources\Json\JsonResource;
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
class NumPricesDetail extends JsonResource
|
class NumPricesDetail extends JsonResource{
|
||||||
{
|
|
||||||
public function toArray($request): array
|
public function toArray($request): array{
|
||||||
{
|
|
||||||
return [
|
return [
|
||||||
'id' => $this->id,
|
'id' => $this->id,
|
||||||
'num' => $this->num,
|
'num' => $this->sales_method == 'piece' ? $this->num : $this->num * $this->piece_to_batch,
|
||||||
'price' => $this->price,
|
'price' => $this->price,
|
||||||
'price_format' => currency_format($this->price),
|
'price_format' => currency_format($this->price),
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,29 +31,46 @@ 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 [
|
return [
|
||||||
'id' => $this->id,
|
'id' => $this->id,
|
||||||
'name' => $this->description->name ?? '',
|
'name' => $this->description->name ?? '',
|
||||||
'description' => $this->description->content ?? '',
|
'unit' => $this->unit,//$this->description->unit ?? '',
|
||||||
'meta_title' => $this->description->meta_title ?? '',
|
'trade_term' => $this->trade_term,
|
||||||
'meta_keywords' => $this->description->meta_keywords ?? '',
|
'description' => $this->description->content ?? '',
|
||||||
|
'meta_title' => $this->description->meta_title ?? '',
|
||||||
|
'meta_keywords' => $this->description->meta_keywords ?? '',
|
||||||
'meta_description' => $this->description->meta_description ?? '',
|
'meta_description' => $this->description->meta_description ?? '',
|
||||||
'brand_id' => $this->brand->id ?? 0,
|
'brand_id' => $this->brand->id ?? 0,
|
||||||
'brand_name' => $this->brand->name ?? '',
|
'brand_name' => $this->brand->name ?? '',
|
||||||
'video' => $this->video ?? '',
|
'video' => $this->video ?? '',
|
||||||
'images' => array_map(function ($image) {
|
'images' => array_map(function($image){
|
||||||
return [
|
return [
|
||||||
'preview' => image_resize($image, 500, 500),
|
'preview' => image_resize($image,500,500),
|
||||||
'popup' => image_resize($image, 800, 800),
|
'popup' => image_resize($image,800,800),
|
||||||
'thumb' => image_resize($image, 150, 150),
|
'thumb' => image_resize($image,150,150),
|
||||||
];
|
];
|
||||||
}, $this->images ?? []),
|
},$this->images ?? []),
|
||||||
'attributes' => $attributes,
|
'attributes' => $attributes,
|
||||||
'variables' => $this->decodeVariables($this->variables),
|
'variables' => $this->decodeVariables($this->variables),
|
||||||
'skus' => SkuDetail::collection($this->skus)->jsonSerialize(),
|
'skus' => SkuDetail::collection($this->skus)->jsonSerialize(),
|
||||||
'in_wishlist' => $this->inCurrentWishlist->id ?? 0,
|
'in_wishlist' => $this->inCurrentWishlist->id ?? 0,
|
||||||
'active' => (bool) $this->active,
|
'active' => (bool)$this->active,
|
||||||
'price_setting' => $this->price_setting ?? '',
|
'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() ?? '',
|
'numPrices' => NumPricesDetail::collection($this->numprices)->jsonSerialize() ?? '',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -75,6 +92,7 @@ class ProductDetail extends JsonResource
|
||||||
return array_map(function ($item) use ($lang) {
|
return array_map(function ($item) use ($lang) {
|
||||||
return [
|
return [
|
||||||
'name' => $item['name'][$lang] ?? '',
|
'name' => $item['name'][$lang] ?? '',
|
||||||
|
'isNumSelect' => $item['isNumSelect'] ?? FALSE,
|
||||||
'values' => array_map(function ($item) use ($lang) {
|
'values' => array_map(function ($item) use ($lang) {
|
||||||
return [
|
return [
|
||||||
'name' => $item['name'][$lang] ?? '',
|
'name' => $item['name'][$lang] ?? '',
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,17 @@ class ProductSimple extends JsonResource
|
||||||
}
|
}
|
||||||
|
|
||||||
$name = $this->description->name ?? '';
|
$name = $this->description->name ?? '';
|
||||||
$images = $this->images;
|
$unit = $this->unit;//$this->description->unit ?? '';
|
||||||
|
$images = $this->images != NULL ? $this->images : [];
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'id' => $this->id,
|
'id' => $this->id,
|
||||||
'sku_id' => $masterSku->id,
|
'sku_id' => $masterSku->id,
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'name_format' => $name,
|
'name_format' => $name,
|
||||||
|
'unit' => $unit,
|
||||||
|
'unit_format' => $unit,
|
||||||
|
'trade_term' => $this->trade_term,
|
||||||
'url' => $this->url,
|
'url' => $this->url,
|
||||||
'price' => $masterSku->price,
|
'price' => $masterSku->price,
|
||||||
'origin_price' => $masterSku->origin_price,
|
'origin_price' => $masterSku->origin_price,
|
||||||
|
|
@ -47,7 +51,7 @@ class ProductSimple extends JsonResource
|
||||||
'in_wishlist' => $this->inCurrentWishlist->id ?? 0,
|
'in_wishlist' => $this->inCurrentWishlist->id ?? 0,
|
||||||
'price_setting' => $this->price_setting ?? '',
|
'price_setting' => $this->price_setting ?? '',
|
||||||
'numprices' => NumPricesDetail::collection($this->numprices)->jsonSerialize() ?? '',
|
'numprices' => NumPricesDetail::collection($this->numprices)->jsonSerialize() ?? '',
|
||||||
|
'minimum_order' => $this->minimum_order,
|
||||||
'images' => array_map(function ($item) {
|
'images' => array_map(function ($item) {
|
||||||
return image_resize($item, 400, 400);
|
return image_resize($item, 400, 400);
|
||||||
}, $images),
|
}, $images),
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,10 @@ Route::prefix('/')
|
||||||
Route::get('categories/{category}', [CategoryController::class, 'show'])->name('categories.show');
|
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/{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');
|
Route::get('currency/{currency}', [CurrencyController::class, 'index'])->name('currency.switch');
|
||||||
|
|
||||||
|
|
@ -65,6 +69,8 @@ Route::prefix('/')
|
||||||
|
|
||||||
Route::get('products/search', [ProductController::class, 'search'])->name('products.search');
|
Route::get('products/search', [ProductController::class, 'search'])->name('products.search');
|
||||||
Route::get('products/{product}', [ProductController::class, 'show'])->name('products.show');
|
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::get('register', [RegisterController::class, 'index'])->name('register.index');
|
||||||
Route::post('register', [RegisterController::class, 'store'])->name('register.store');
|
Route::post('register', [RegisterController::class, 'store'])->name('register.store');
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,10 @@
|
||||||
|
|
||||||
namespace Beike\Shop\Services;
|
namespace Beike\Shop\Services;
|
||||||
|
|
||||||
|
use Beike\Models\Cart;
|
||||||
use Beike\Models\CartProduct;
|
use Beike\Models\CartProduct;
|
||||||
|
use Beike\Models\Product;
|
||||||
|
use Beike\Models\ProductSku;
|
||||||
use Beike\Repositories\CartRepo;
|
use Beike\Repositories\CartRepo;
|
||||||
use Beike\Shop\Http\Resources\CartDetail;
|
use Beike\Shop\Http\Resources\CartDetail;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
@ -27,18 +30,22 @@ class CartService
|
||||||
* @param bool $selected
|
* @param bool $selected
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public static function list($customer, bool $selected = false): array
|
public static function list($customer, bool $selected = false): array{
|
||||||
{
|
if (self::$cartList !== null) return self::$cartList;
|
||||||
if (self::$cartList !== null) {
|
|
||||||
return self::$cartList;
|
|
||||||
}
|
|
||||||
|
|
||||||
$cartBuilder = CartRepo::allCartProductsBuilder($customer->id ?? 0);
|
$cartBuilder = CartRepo::allCartProductsBuilder($customer->id ?? 0);
|
||||||
if ($selected) {
|
if ($selected) $cartBuilder->where('selected', true);
|
||||||
$cartBuilder->where('selected', true);
|
|
||||||
}
|
|
||||||
$cartItems = $cartBuilder->get();
|
$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) {
|
$cartItems = $cartItems->filter(function ($item) {
|
||||||
$description = $item->sku->product->description ?? '';
|
$description = $item->sku->product->description ?? '';
|
||||||
$product = $item->product ?? null;
|
$product = $item->product ?? null;
|
||||||
|
|
@ -57,7 +64,6 @@ class CartService
|
||||||
|
|
||||||
return $description && $product;
|
return $description && $product;
|
||||||
});
|
});
|
||||||
|
|
||||||
$productQuantitySumList = [];
|
$productQuantitySumList = [];
|
||||||
foreach($cartItems as $item) {
|
foreach($cartItems as $item) {
|
||||||
$productId = $item->product_id;
|
$productId = $item->product_id;
|
||||||
|
|
@ -189,6 +195,13 @@ class CartService
|
||||||
if (empty($carts)) {
|
if (empty($carts)) {
|
||||||
$carts = self::list(current_customer());
|
$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);
|
$cartList = collect($carts)->where('selected', 1);
|
||||||
|
|
||||||
|
|
@ -206,4 +219,5 @@ class CartService
|
||||||
|
|
||||||
return hook_filter('service.cart.data', $data);
|
return hook_filter('service.cart.data', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ class CheckoutService
|
||||||
$this->cart = CartRepo::createCart($this->customer);
|
$this->cart = CartRepo::createCart($this->customer);
|
||||||
$this->selectedProducts = CartRepo::selectedCartProducts($this->customer->id ?? 0);
|
$this->selectedProducts = CartRepo::selectedCartProducts($this->customer->id ?? 0);
|
||||||
if ($this->selectedProducts->count() == 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'];
|
$shippingMethodCode = $current['shipping_method_code'];
|
||||||
if (! PluginRepo::shippingEnabled($shippingMethodCode)) {
|
if (! PluginRepo::shippingEnabled($shippingMethodCode) && !is_int($shippingMethodCode)) {
|
||||||
throw new \Exception(trans('shop/carts.invalid_shipping_method'));
|
throw new \Exception(trans('shop/carts.invalid_shipping_method'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -252,16 +252,19 @@ class CheckoutService
|
||||||
foreach ($shipments as $shipment) {
|
foreach ($shipments as $shipment) {
|
||||||
$shipmentCodes = array_merge($shipmentCodes, array_column($shipment['quotes'], 'code'));
|
$shipmentCodes = array_merge($shipmentCodes, array_column($shipment['quotes'], 'code'));
|
||||||
}
|
}
|
||||||
if (! in_array($currentCart->shipping_method_code, $shipmentCodes)) {
|
// 判断:如果运费方式不存在 并且不是物流则设置第一个物流信息
|
||||||
|
if (!in_array($currentCart->shipping_method_code, $shipmentCodes) && !is_int($currentCart->shipping_method_code)) {
|
||||||
$this->updateShippingMethod($shipmentCodes[0] ?? '');
|
$this->updateShippingMethod($shipmentCodes[0] ?? '');
|
||||||
$this->totalService->setShippingMethod($shipmentCodes[0] ?? '');
|
$this->totalService->setShippingMethod($shipmentCodes[0] ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$shippingMethodCode = !empty($currentCart->shipping_method_code) ? $currentCart->shipping_method_code : ($shipments[0]['code'] ?? '');
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'current' => [
|
'current' => [
|
||||||
'shipping_address_id' => $currentCart->shipping_address_id,
|
'shipping_address_id' => $currentCart->shipping_address_id,
|
||||||
'guest_shipping_address' => $currentCart->guest_shipping_address,
|
'guest_shipping_address' => $currentCart->guest_shipping_address,
|
||||||
'shipping_method_code' => $currentCart->shipping_method_code,
|
'shipping_method_code' => $shippingMethodCode,
|
||||||
'payment_address_id' => $currentCart->payment_address_id,
|
'payment_address_id' => $currentCart->payment_address_id,
|
||||||
'guest_payment_address' => $currentCart->guest_payment_address,
|
'guest_payment_address' => $currentCart->guest_payment_address,
|
||||||
'payment_method_code' => $currentCart->payment_method_code,
|
'payment_method_code' => $currentCart->payment_method_code,
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,9 @@ class TotalService
|
||||||
*/
|
*/
|
||||||
private function getTotalClassMaps()
|
private function getTotalClassMaps()
|
||||||
{
|
{
|
||||||
$maps = [];
|
$maps = [
|
||||||
|
'sum_quantity' => "\Beike\\Shop\\Services\\TotalServices\\SumQuantityService"
|
||||||
|
];
|
||||||
foreach (self::TOTAL_CODES as $code) {
|
foreach (self::TOTAL_CODES as $code) {
|
||||||
$serviceName = Str::studly($code) . 'Service';
|
$serviceName = Str::studly($code) . 'Service';
|
||||||
$maps[$code] = "\Beike\\Shop\\Services\\TotalServices\\{$serviceName}";
|
$maps[$code] = "\Beike\\Shop\\Services\\TotalServices\\{$serviceName}";
|
||||||
|
|
|
||||||
|
|
@ -12,52 +12,254 @@
|
||||||
|
|
||||||
namespace Beike\Shop\Services\TotalServices;
|
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 Beike\Shop\Services\CheckoutService;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class ShippingService
|
class ShippingService{
|
||||||
{
|
protected static $country;// 国家信息
|
||||||
/**
|
protected static $productsList;// 商品分组统计列表
|
||||||
* @param CheckoutService $checkout
|
|
||||||
* @return array|null
|
// 运费计算
|
||||||
* @throws \Exception|\Throwable
|
public static function getTotal(CheckoutService $checkout): ?array{
|
||||||
*/
|
// 计算运费
|
||||||
public static function getTotal(CheckoutService $checkout): ?array
|
|
||||||
{
|
|
||||||
$totalService = $checkout->totalService;
|
$totalService = $checkout->totalService;
|
||||||
$shippingMethod = $totalService->getShippingMethod();
|
$shippingMethod = $totalService->getShippingMethod();
|
||||||
if (empty($shippingMethod)) {
|
$amount = 0;
|
||||||
return null;
|
$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);
|
||||||
}
|
}
|
||||||
|
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;// 还需要除以计抛比
|
||||||
|
|
||||||
$shippingPluginCode = self::parseShippingPluginCode($shippingMethod);
|
return [
|
||||||
$pluginCode = Str::studly($shippingPluginCode);
|
'product_id' => $firstInfo['product_id'],
|
||||||
|
'sum_quantity' => $sumQuantity,
|
||||||
if (! app('plugin')->checkActive($shippingPluginCode)) {
|
'sum_weight' => $sumWeight,
|
||||||
$cart = $checkout->cart;
|
'volume_weight' => $volumeWeight,
|
||||||
$cart->shipping_method_code = '';
|
'weight' => $weight,
|
||||||
$cart->saveOrFail();
|
'weight_class' => $weightClass,
|
||||||
|
];
|
||||||
return [];
|
});
|
||||||
|
// 通过物流进行计算
|
||||||
|
$logisticsId = (int)$checkout->cart->shipping_method_code;
|
||||||
|
$logisticsInfo = self::getLogistics($logisticsId,$checkout->cart);
|
||||||
|
if($logisticsInfo) $amount = $logisticsInfo['amount'];// 存在物流 获取运费
|
||||||
|
else $showTips = 1;// 如果还是不能获取物流信息 则显示 待协商
|
||||||
}
|
}
|
||||||
|
|
||||||
$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 = [
|
$totalData = [
|
||||||
'code' => 'shipping',
|
'code' => 'shipping',
|
||||||
'title' => trans('shop/carts.shipping_fee'),
|
'title' => trans('shop/carts.shipping_fee'),
|
||||||
'amount' => $amount,
|
'amount' => $amount,
|
||||||
'amount_format' => currency_format($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->amount += $totalData['amount'];
|
||||||
$totalService->totals[] = $totalData;
|
$totalService->totals[] = $totalData;
|
||||||
|
|
||||||
return $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() : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过配送方式获取插件编码
|
* 通过配送方式获取插件编码
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?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' => '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<!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>
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"axios": "^0.21",
|
"axios": "^0.21",
|
||||||
"bootstrap": "^5.2.2",
|
"bootstrap": "^5.2.2",
|
||||||
"laravel-mix": "^6.0.6",
|
"laravel-mix": "^6.0.49",
|
||||||
"lodash": "^4.17.19",
|
"lodash": "^4.17.19",
|
||||||
"resolve-url-loader": "^4.0.0",
|
"resolve-url-loader": "^4.0.0",
|
||||||
"sass": "^1.38.1",
|
"sass": "^1.38.1",
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -1,8 +0,0 @@
|
||||||
*
|
|
||||||
!Bestseller
|
|
||||||
!FlatShipping
|
|
||||||
!LatestProducts
|
|
||||||
!Openai
|
|
||||||
!Paypal
|
|
||||||
!Social
|
|
||||||
!Stripe
|
|
||||||
|
|
@ -53,13 +53,13 @@ class Bootstrap
|
||||||
$amount = $totalService->amount;
|
$amount = $totalService->amount;
|
||||||
$shippingType = plugin_setting('flat_shipping.type', 'fixed');
|
$shippingType = plugin_setting('flat_shipping.type', 'fixed');
|
||||||
$shippingValue = plugin_setting('flat_shipping.value', 0);
|
$shippingValue = plugin_setting('flat_shipping.value', 0);
|
||||||
if ($shippingType == 'fixed') {
|
$sumQuantity = $totalService->countProducts();
|
||||||
return $shippingValue;
|
if($sumQuantity > 0){
|
||||||
} elseif ($shippingType == 'percent') {
|
if ($shippingType == 'fixed') return $shippingValue;
|
||||||
return $amount * $shippingValue / 100;
|
elseif ($shippingType == 'percent') return $amount * $shippingValue / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class MenusController extends Controller
|
||||||
{
|
{
|
||||||
$products = ProductRepo::getBuilder(
|
$products = ProductRepo::getBuilder(
|
||||||
[
|
[
|
||||||
'active' => 1,
|
// 'active' => 1,
|
||||||
'sort' => 'created_at',
|
'sort' => 'created_at',
|
||||||
'order' => 'desc',
|
'order' => 'desc',
|
||||||
])
|
])
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
<IfModule mod_rewrite.c>
|
|
||||||
RewriteEngine on
|
|
||||||
RewriteBase /
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-d
|
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
|
||||||
RewriteRule ^(.*)$ index.php?s=/$1 [QSA,PT,L]
|
|
||||||
</IfModule>
|
|
||||||
|
|
@ -0,0 +1,649 @@
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,288 @@
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
*
|
|
||||||
!.gitignore
|
|
||||||
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 188 KiB |
|
Before Width: | Height: | Size: 189 KiB |
|
Before Width: | Height: | Size: 382 KiB |
|
Before Width: | Height: | Size: 387 KiB |
|
Before Width: | Height: | Size: 328 KiB |
|
Before Width: | Height: | Size: 325 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 51 KiB |