diff --git a/beike/Repositories/CountryRepo.php b/beike/Repositories/CountryRepo.php index c9a75a91..355007ed 100644 --- a/beike/Repositories/CountryRepo.php +++ b/beike/Repositories/CountryRepo.php @@ -137,7 +137,7 @@ class CountryRepo // 列表获取 $builder = Country::query() ->where('name', 'like', "$name%") - ->select('id', 'name', 'status', 'code') + ->select('id', 'name', 'icon', 'status', 'code') ->orderBy('sort_order','ASC') ->orderBy('id','ASC'); // if ($onlyActive) { diff --git a/beike/Shop/Http/Controllers/ProductController.php b/beike/Shop/Http/Controllers/ProductController.php index 9af55380..6e6e1f48 100644 --- a/beike/Shop/Http/Controllers/ProductController.php +++ b/beike/Shop/Http/Controllers/ProductController.php @@ -4,6 +4,8 @@ namespace Beike\Shop\Http\Controllers; use Beike\Models\Cart; use Beike\Models\CartProduct; +use Beike\Models\Country; +use Beike\Models\Logistics; use Beike\Models\Product; use Beike\Models\ProductSku; use Beike\Repositories\AddressRepo; @@ -78,6 +80,7 @@ class ProductController extends Controller */ 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')); // 生成模拟数据 @@ -103,12 +106,13 @@ class ProductController extends Controller $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' => $shippingMethodCode ? $shippingMethodCode . '.0' : '', + 'shipping_method_code' => (int)$changeLogisticsId > 0 ? (int)$changeLogisticsId : $shippingMethodCode , 'payment_address_id' => $defaultAddressId, 'payment_method_code' => $paymentMethod->code ?? '', // "id" => 28, @@ -129,13 +133,71 @@ class ProductController extends Controller $cart = $cart[0]; // 计算 $checkoutService = new CheckoutService(); - $totalClass = hook_filter('service.checkout.total_service','Beike\Shop\Services\TotalService'); - $checkoutService->totalService = (new $totalClass($cart,$cartList)); - $checkoutData = $checkoutService->checkoutData(); + $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); } + // 获取物流列表 + 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]); + // 判断:如果存在商品 计算物流运费信息 + 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'); + } + } + + return json_success(trans('common.success'),$data); + } + } diff --git a/beike/Shop/Http/Controllers/ZoneController.php b/beike/Shop/Http/Controllers/ZoneController.php index 8784d3f9..7b4288ad 100644 --- a/beike/Shop/Http/Controllers/ZoneController.php +++ b/beike/Shop/Http/Controllers/ZoneController.php @@ -1,7 +1,6 @@ @@ -11,21 +10,41 @@ namespace Beike\Shop\Http\Controllers; +use Beike\Repositories\CountryRepo; use Beike\Repositories\ZoneRepo; use Illuminate\Http\Request; -class ZoneController extends Controller -{ - public function index(Request $request, int $countryId) - { +class ZoneController extends Controller{ + public function index(Request $request,int $countryId){ ZoneRepo::listByCountry($countryId); - $data = [ 'zones' => ZoneRepo::listByCountry($countryId), ]; - - $data = hook_filter('zone.index.data', $data); - - return json_success(trans('common.success'), $data); + $data = hook_filter('zone.index.data',$data); + return json_success(trans('common.success'),$data); } + /** + * Common: 获取全部的国家列表 + * Author: wu-hui + * Time: 2023/08/25 16:57 + * @param Request $request + * @return array + */ + public function countries(Request $request){ + + $brands = CountryRepo::autocomplete($request->get('name') ?? '', 0); + + return json_success(trans('common.get_success'), $brands); + } + + + + + + + + + + + } diff --git a/beike/Shop/Routes/shop.php b/beike/Shop/Routes/shop.php index d3c20b98..ab61cd50 100644 --- a/beike/Shop/Routes/shop.php +++ b/beike/Shop/Routes/shop.php @@ -43,6 +43,7 @@ Route::prefix('/') Route::get('categories/{category}', [CategoryController::class, 'show'])->name('categories.show'); Route::get('countries/{id}/zones', [ZoneController::class, 'index'])->name('countries.zones.index'); + Route::get('countries/autocomplete', [ZoneController::class, 'countries'])->name('countries.zones.countries'); Route::get('currency/{currency}', [CurrencyController::class, 'index'])->name('currency.switch'); @@ -66,6 +67,7 @@ Route::prefix('/') Route::get('products/search', [ProductController::class, 'search'])->name('products.search'); Route::get('products/{product}', [ProductController::class, 'show'])->name('products.show'); Route::post('products/computeOrderMoney', [ProductController::class, 'computeOrderMoney'])->name('products.computeOrderMoney'); + Route::post('products/productsLogistics', [ProductController::class, 'productsLogistics'])->name('products.productsLogistics'); Route::get('register', [RegisterController::class, 'index'])->name('register.index'); Route::post('register', [RegisterController::class, 'store'])->name('register.store'); diff --git a/beike/Shop/Services/TotalServices/ShippingService.php b/beike/Shop/Services/TotalServices/ShippingService.php index 1df9e861..d4c43522 100644 --- a/beike/Shop/Services/TotalServices/ShippingService.php +++ b/beike/Shop/Services/TotalServices/ShippingService.php @@ -13,17 +13,25 @@ namespace Beike\Shop\Services\TotalServices; use Beike\Libraries\Weight; +use Beike\Models\Country; use Beike\Models\Logistics; use Beike\Models\Product; +use Beike\Repositories\LogisticsRepo; use Beike\Shop\Services\CheckoutService; use Illuminate\Support\Str; class ShippingService{ + protected static $country; + // 运费计算 public static function getTotal(CheckoutService $checkout): ?array{ + // 计算运费 $totalService = $checkout->totalService; $shippingMethod = $totalService->getShippingMethod(); $amount = 0; + $logisticsInfo = [];// 物流信息 + self::$country = LogisticsRepo::getDefaultCountries(true);// 国家信息 + $showTips = 0;// 是否显示【待协商】提示 if($shippingMethod && !is_int($checkout->cart->shipping_method_code)){ // 通过插件(固定运费) 进行计算 if(empty($shippingMethod)) return NULL; @@ -48,7 +56,6 @@ class ShippingService{ if($logisticsInfo) { // 商品信息处理 获取同一个商品的数量 $products = $totalService->getCartProducts(); - // $products = $checkout->selectedProducts->toArray() ?? []; $productsList = collect($products)->groupBy('product_id')->map(function($group){ $firstInfo = $group->first(); $productWeightInfo = Product::query() @@ -73,13 +80,20 @@ class ShippingService{ $logisticsHandleFun = 'compute_'.$logisticsInfo['type']; $amount = self::$logisticsHandleFun($logisticsInfo,$productsList); } + else{ + // 如果还是不能获取物流信息 则显示 待协商 + $showTips = 1; + } } - $totalData = [ 'code' => 'shipping', 'title' => trans('shop/carts.shipping_fee'), 'amount' => $amount, 'amount_format' => currency_format($amount), + 'amount_tips' => trans('shop/carts.to_be_negotiated'), + 'show_tips' => $showTips, + 'country' => self::$country,// 国家信息 + 'logistics' => $logisticsInfo,// 物流信息 ]; $totalService->amount += $totalData['amount']; $totalService->totals[] = $totalData; @@ -95,24 +109,51 @@ class ShippingService{ * @return array|mixed|mixed[] */ private static function getLogistics($logisticsId,$cart){ + $logisticsField = ['type', 'name','first_weight','first_weight_fee','continuation_weight_max','add_weight','continuation_weight_fee','num_fee']; + // 判断:客服端是否切换国家 如果存在切换的国家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)')) + ->where('id',$logisticsId) + ->select($logisticsField) + ->first(); + if($logisticsInfo) { + // 刷新国家信息 + self::refreshCurrentCountry($countryId); + + return $logisticsInfo->toArray(); + } + } + // 仅存在国家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) return []; - $countryId = $address['country_id']; + if($address) $countryId = (int)$address['country_id'];// 存在收货地址(用户已经登录 || 用户设置收货地址) + else $countryId = (int)self::$country['id'];// 不存在收货地址(用户未登录 || 用户已登录但是未设置收货地址) 获取默认国家收货地址 // 获取全部物流 返回第一个物流信息ID - $logisticsList = Logistics::getAll($countryId,['id','type','first_weight','first_weight_fee','continuation_weight_max','add_weight','continuation_weight_fee','num_fee']); + CountryGetLogistics: + $logisticsList = Logistics::getAll($countryId,$logisticsField); + // 刷新国家信息 + self::refreshCurrentCountry($countryId); return $logisticsList[0] ?? []; }else{ // 获取物流信息 $logisticsInfo = Logistics::query() - ->select(['type','first_weight','first_weight_fee','continuation_weight_max','add_weight','continuation_weight_fee','num_fee']) - ->find($logisticsId) - ->toArray(); + ->select($logisticsField) + ->find($logisticsId); - return $logisticsInfo; + return $logisticsInfo ? $logisticsInfo->toArray() : []; } } /** @@ -130,9 +171,9 @@ class ShippingService{ * 总重量 - 高于首重;则运费 = 首重按照【首重运费】计算;(剩余重量 / 每增加重量 ) * 续重运费 */ $amount = (int)0; - $firstWeight = (int)$logisticsInfo['first_weight'];// 首重 + $firstWeight = (float)$logisticsInfo['first_weight'];// 首重 $firstWeightFee = (float)$logisticsInfo['first_weight_fee'];// 首重运费 - $addWeight = (int)$logisticsInfo['add_weight'];// 每增加重量 + $addWeight = (float)$logisticsInfo['add_weight'];// 每增加重量 $continuationWeightFee = (float)$logisticsInfo['continuation_weight_fee'];// 续重运费 // 循环处理商品 foreach($productsList as $productInfo){ @@ -187,6 +228,17 @@ class ShippingService{ 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() : []; + } + + diff --git a/resources/beike/admin/views/pages/logistics/index.blade.php b/resources/beike/admin/views/pages/logistics/index.blade.php index 2ad4998d..ff353467 100644 --- a/resources/beike/admin/views/pages/logistics/index.blade.php +++ b/resources/beike/admin/views/pages/logistics/index.blade.php @@ -321,6 +321,7 @@ let singleInfo = Object.values(info)[0]; $http.post('logistics/default_countries',{ country: singleInfo }).then((res) => { _this.default_countries_name = singleInfo.name; + _this.$refs['select-countries'].closeSelectCountries(); }) }, // 当前选择国家文字显示 diff --git a/resources/lang/en/admin/logistics.php b/resources/lang/en/admin/logistics.php index 5b269692..4e0d8d66 100644 --- a/resources/lang/en/admin/logistics.php +++ b/resources/lang/en/admin/logistics.php @@ -74,4 +74,6 @@ return [ 'logistics_duration' => 'Delivery within :day working days after shipment', 'unit' => 'unit::unit', 'default_countries' => 'Default display country for the front desk', + 'choose_logistics' => 'Select Shipping Service', + 'estimated_time' => 'Estimated delivery time', ]; diff --git a/resources/lang/en/shop/carts.php b/resources/lang/en/shop/carts.php index 66757033..4123bf48 100644 --- a/resources/lang/en/shop/carts.php +++ b/resources/lang/en/shop/carts.php @@ -1,40 +1,38 @@ * @created 2022-08-19 14:54:21 * @modified 2022-08-19 14:54:21 */ - return [ - 'index' => 'Cart', - 'added_to_cart' => 'Added To Cart', - 'select_all' => 'Select All', - 'commodity' => 'Product', - 'quantity' => 'Quantity', - 'subtotal' => 'Subtotal', - 'product_total' => 'Quantity total',// Product Total - 'customer_discount' => 'Customer Discount', - 'order_total' => 'Order Total', - 'shipping_fee' => 'Shipping Fee', - 'all' => 'All', - 'selected' => 'Selected', - 'to_checkout' => 'Checkout', - 'cart_empty' => 'Your shopping cart is empty', - 'go_buy' => 'You can go and see what you want to buy', - 'go_shopping' => 'Go Shopping', - 'must_select' => 'Please select at least one product', - 'mini' => 'Your cart', - 'delete' => 'Delete', - 'check_cart' => 'Shopping Cart', - + 'index' => 'Cart', + 'added_to_cart' => 'Added To Cart', + 'select_all' => 'Select All', + 'commodity' => 'Product', + 'quantity' => 'Quantity', + 'subtotal' => 'Subtotal', + 'product_total' => 'Quantity total',// Product Total + 'customer_discount' => 'Customer Discount', + 'order_total' => 'Order Total', + 'shipping_fee' => 'Shipping Fee', + 'all' => 'All', + 'selected' => 'Selected', + 'to_checkout' => 'Checkout', + 'cart_empty' => 'Your shopping cart is empty', + 'go_buy' => 'You can go and see what you want to buy', + 'go_shopping' => 'Go Shopping', + 'must_select' => 'Please select at least one product', + 'mini' => 'Your cart', + 'delete' => 'Delete', + 'check_cart' => 'Shopping Cart', 'invalid_customer' => 'Invalid customer.', 'empty_selected_products' => 'Empty selected products.', 'invalid_shipping_address' => 'Invalid shipping address', 'invalid_payment_address' => 'Invalid payment address.', 'invalid_shipping_method' => 'Invalid shipping method.', 'invalid_payment_method' => 'Invalid payment method.', + 'to_be_negotiated' => 'To be negotiated', ]; diff --git a/resources/lang/en/shop/products.php b/resources/lang/en/shop/products.php index a780f465..07144da3 100644 --- a/resources/lang/en/shop/products.php +++ b/resources/lang/en/shop/products.php @@ -27,4 +27,5 @@ return [ 'product_total' => 'Quantity total',// Product Total 'shipping_fee' => 'Shipping Fee', 'order_total' => 'Order Total', + 'ship_to' => 'Ship to', ]; diff --git a/resources/lang/zh_cn/admin/logistics.php b/resources/lang/zh_cn/admin/logistics.php index 959651ff..67dd9293 100644 --- a/resources/lang/zh_cn/admin/logistics.php +++ b/resources/lang/zh_cn/admin/logistics.php @@ -79,4 +79,10 @@ return [ 'logistics_duration' => '发货后:day个工作日内交货', 'unit' => '单位::unit', 'default_countries' => '前台默认展示国家', + 'choose_logistics' => '选择运输服务', + 'estimated_time' => '预计送达时间', + + + + ]; diff --git a/resources/lang/zh_cn/shop/carts.php b/resources/lang/zh_cn/shop/carts.php index df517dc2..402453e0 100644 --- a/resources/lang/zh_cn/shop/carts.php +++ b/resources/lang/zh_cn/shop/carts.php @@ -1,40 +1,38 @@ * @created 2022-08-17 23:10:20 * @modified 2022-08-17 23:10:20 */ - return [ - 'index' => '购物车', - 'added_to_cart' => '已加入购物车', - 'select_all' => '全选', - 'commodity' => '商品', - 'quantity' => '数量', - 'subtotal' => '小计', - 'product_total' => '商品总计', - 'customer_discount' => '会员优惠', - 'order_total' => '应付总金额', - 'shipping_fee' => '运费', - 'all' => '全部', - 'selected' => '已选', - 'to_checkout' => '去结账', - 'cart_empty' => '您的购物车是空的', - 'go_buy' => '您可以去看看有哪些想买的', - 'go_shopping' => '去逛逛', - 'must_select' => '请选择至少一个商品', - 'mini' => '您的购物车', - 'delete' => '删除', - 'check_cart' => '查看购物车', - + 'index' => '购物车', + 'added_to_cart' => '已加入购物车', + 'select_all' => '全选', + 'commodity' => '商品', + 'quantity' => '数量', + 'subtotal' => '小计', + 'product_total' => '商品总计', + 'customer_discount' => '会员优惠', + 'order_total' => '应付总金额', + 'shipping_fee' => '运费', + 'all' => '全部', + 'selected' => '已选', + 'to_checkout' => '去结账', + 'cart_empty' => '您的购物车是空的', + 'go_buy' => '您可以去看看有哪些想买的', + 'go_shopping' => '去逛逛', + 'must_select' => '请选择至少一个商品', + 'mini' => '您的购物车', + 'delete' => '删除', + 'check_cart' => '查看购物车', 'invalid_customer' => '购物车客户无效', 'empty_selected_products' => '购物车选中商品为空', 'invalid_shipping_address' => '配送地址无效', 'invalid_payment_address' => '账单地址无效', 'invalid_shipping_method' => '配送方式不可用', 'invalid_payment_method' => '支付方式不可用', + 'to_be_negotiated' => '待协商', ]; diff --git a/resources/lang/zh_cn/shop/products.php b/resources/lang/zh_cn/shop/products.php index 44ffbc83..08b3165d 100644 --- a/resources/lang/zh_cn/shop/products.php +++ b/resources/lang/zh_cn/shop/products.php @@ -27,4 +27,5 @@ return [ 'product_total' => '产品总计', 'shipping_fee' => '运费', 'order_total' => '订单总额', + 'ship_to' => '运送至', ]; diff --git a/themes/default/checkout.blade.php b/themes/default/checkout.blade.php index 1051a45d..02534891 100644 --- a/themes/default/checkout.blade.php +++ b/themes/default/checkout.blade.php @@ -245,7 +245,7 @@
-
${methods.name}(${methods.quotes.warehouse_name} }})
+
${methods.name}(${methods.quotes.warehouse_name})
${typeText} ${logistics_duration} diff --git a/themes/default/product/product-video.blade.php b/themes/default/product/product-video.blade.php index 6f5e8d6c..a66156bb 100644 --- a/themes/default/product/product-video.blade.php +++ b/themes/default/product/product-video.blade.php @@ -3,7 +3,7 @@ diff --git a/themes/default/product/product.blade.php b/themes/default/product/product.blade.php index 112db3c8..4537ffc8 100644 --- a/themes/default/product/product.blade.php +++ b/themes/default/product/product.blade.php @@ -77,6 +77,46 @@ .quantity-btns .operating-content button:first-child{ margin-left: 0!important; } + + .shipping-line{ + display: inline-flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: center; + justify-content: flex-start; + } + .shipping-line .country{ + display: inline-flex; + flex-direction: row; + flex-wrap: nowrap; + justify-content: flex-start; + align-items: center; + margin-left: 20px; + } + .shipping-line .country .country-name{ + margin-left: 5px; + cursor: pointer; + -webkit-user-select: none; /* Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE 10+ */ + user-select: none; /* 标准语法 */ + } + .shipping-line .country .country-name i{ + margin-left: 3px; + } + + + .logistics-content{ + margin-left: 20px; + cursor: pointer; + -webkit-user-select: none; /* Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE 10+ */ + user-select: none; /* 标准语法 */ + } + .logistics-content i{ + margin-left: 3px; + }
@@ -327,8 +367,33 @@ --}}
  • - @{{ item.title }} - @{{ item.amount_format }} + +
  • @@ -442,6 +507,10 @@ @hook('product.tab.after.pane')
    + {{-- 国家选择器 --}} + + {{-- 物流选择器 --}} +
    @if ($relations && !request('iframe')) @@ -470,13 +539,14 @@ @endsection @push('add-scripts') + @include('shared.select-countries') + @include('shared.select-logistics')