wyyl/themes/default/product/product.blade.php

1239 lines
49 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@extends('layout.master')
@section('body-class', 'page-product')
@section('title', $product['meta_title'] ?: $product['name'])
@section('keywords', $product['meta_keywords'] ?: system_setting('base.meta_keyword'))
@section('description', $product['meta_description'] ?: system_setting('base.meta_description'))
@push('header')
<script src="{{ asset('vendor/vue/2.7/vue' . (!config('app.debug') ? '.min' : '') . '.js') }}"></script>
<script src="{{ asset('vendor/swiper/swiper-bundle.min.js') }}"></script>
<script src="{{ asset('vendor/zoom/jquery.zoom.min.js') }}"></script>
<link rel="stylesheet" href="{{ asset('vendor/swiper/swiper-bundle.min.css') }}">
<script src="{{ asset('vendor/element-ui/index.js') }}"></script>
<link rel="stylesheet" href="{{ asset('vendor/element-ui/index.css') }}">
@if ($product['video'])
<script src="{{ asset('vendor/video/video.min.js') }}"></script>
<link rel="stylesheet" href="{{ asset('vendor/video/video-js.min.css') }}">
@endif
@endpush
@php
$iframeClass = request('iframe') ? 'd-none' : '';
@endphp
@section('content')
@if (!request('iframe'))
<x-shop-breadcrumb type="product" :value="$product['id']"/>
@endif
<style>
.quantity-btns{
display: flex;
flex-direction: column;
align-items: flex-start;
flex-wrap: nowrap;
justify-content: center;
}
.quantity-btns .add-wishlist{
margin-left: 10px;
}
.quantity-btns .add-wishlist .text-secondary{
height: 100% !important;
}
.quantity-btns .buy-num-list{
width: 100%;
padding: 5px 10px;
}
.quantity-btns .list-box{
width: 100%;
display: inline-flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
margin-bottom: 15px;
}
.quantity-btns .list-box .sku-info{
display: inline-flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
height: 42px;
width: 50%;
align-content: center;
align-items: center;
margin-right: 30px;
}
.quantity-btns .list-box .sku-info span{
margin-right: 20px!important;
}
.quantity-btns .operating-content {
width: 100%;
display: inline-flex;
justify-content: flex-start;
align-items: center;
flex-direction: row;
}
.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{
/* width: 80px;
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;
}
.logistics-bottom-content {
height: 20px!important;
margin-top: 10px;
width: 100%;
display: inline-flex;
justify-content: flex-end;
}
.other-amount-format{
/*margin-right: 100px;*/
}
.trade-term-img{
width: 1200px!important;
max-width: 90vw!important;
max-height: 95vh!important;
}
.popover{
--bs-popover-max-width: 100vw!important;
}
.sku-min-order {
position: absolute;
bottom: -20px;
left: 0;
}
.num-price-content{
border-top: 1px solid #aaaaaa;
border-bottom: 1px solid #aaaaaa;
padding: 10px;
position: relative;
@if($product['sales_method'] == 'batches')
padding-bottom: 30px!important;
@endif
}
.num-min-order{
position: absolute;
bottom: 5px;
left: 85px;
}
.sku-price-wrap{
position: relative;
flex-direction: row;
flex-wrap: wrap;
}
.sku-price-wrap-content{
width: 100%;
line-height: 20px !important;
}
.num-item-content{
white-space: nowrap;
color: #777777;
font-size: 18px;
margin-bottom: 5px;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
}
.num-trade-term-content {
height: 54px;
line-height: 85px;
display: inline-flex;
margin-right:10px;
font-size: calc(1.265rem + 0.18vw) !important;
}
.num-trade-term-content-i{
font-size: 15px;
}
</style>
<div class="container {{ request('iframe') ? 'pt-4' : '' }}" id="product-app" v-cloak>
<div class="row mb-5 mt-3 mt-md-0" id="product-top">
<div class="col-12 col-lg-6 mb-3">
<div class="product-image d-flex align-items-start">
@if(!is_mobile())
<div class="left {{ $iframeClass }}" v-if="images.length">
<div class="swiper" id="swiper">
<div class="swiper-wrapper">
<div class="swiper-slide" :class="!index ? 'active' : ''" v-for="image, index in images">
<a href="javascript:;" :data-image="image.preview" :data-zoom-image="image.popup">
<img :src="image.thumb" class="img-fluid">
</a>
</div>
</div>
<div class="swiper-pager">
<div class="swiper-button-next new-feature-slideshow-next"></div>
<div class="swiper-button-prev new-feature-slideshow-prev"></div>
</div>
</div>
</div>
<div class="right" id="zoom">
@include('product.product-video')
<img :src="images.length ? images[0].preview : '{{ asset('image/placeholder.png') }}'" class="img-fluid">
</div>
@else
@include('product.product-video')
<div class="swiper" id="swiper-mobile">
<div class="swiper-wrapper">
<div class="swiper-slide" v-for="image, index in images">
<img :src="image.preview" class="img-fluid">
</div>
</div>
<div class="swiper-pagination mobile-pagination"></div>
</div>
@endif
</div>
</div>
<div class="col-12 col-lg-6">
<div class="peoduct-info">
@hookwrapper('product.detail.name')
<h1 class="mb-4 product-name">{{ $product['name'] }}</h1>
@endhookwrapper
@hookwrapper('product.detail.price')
{{-- 非阶梯价 --}}
<div class="price-wrap d-flex align-items-end sku-price-wrap" v-if="price_setting === 'sku'">
<div class="sku-price-wrap-content fs-3">
<span>{{ $product['trade_term'] ?? 'DAP'}}<i class="bi bi-question-circle" id="tradeTerm" style="font-size: 15px;"></i></span>
<span class="fw-bold">@{{ product.price_format }}</span>
<span v-if="product.price != product.origin_price && product.origin_price !== 0" class="fw-bold"> - @{{ product.origin_price_format }}</span>
@if($product['unit'])
<span style="font-weight: 700 !important;">/ {{explode('/',$product['unit'])[0]}}</span>
@endif
<span v-if="minimum_order > 0" style="font-size: 18px;">
@if($product['sales_method'] == 'piece')
|&nbsp;{{ $product['minimum_order'] }} {{explode('/',$product['unit'])[1]}}
@else
|&nbsp;{{ ($product['piece_to_batch'] * $product['minimum_order']) }} {{explode('/',$product['unit'])[1]}}
@endif
<span style="color: #777777;">({{__('product.minimum_order')}})</span>
</span>
</div>
{{--<span class="new-price fs-1 lh-1 fw-bold me-2">{{$product['trade_term'] ?? 'DAP'}}</span>--}}
{{--<span class="new-price fs-1 lh-1 fw-bold me-2">@{{ product.price_format }}</span>--}}
{{--<span class="new-price fs-1 lh-1 fw-bold me-2">-</span>--}}
{{--<span class="old-price fs-1 lh-1 fw-bold me-2" v-if="product.price != product.origin_price && product.origin_price !== 0">@{{ product.origin_price_format }}</span>--}}
{{--@if($product['unit'])--}}
{{-- <span class="new-price fs-1 lh-1 fw-bold me-2">/ {{explode('/',$product['unit'])[0]}}&nbsp;|</span>--}}
{{--@endif--}}
{{--<span v-if="minimum_order > 0" style="font-size: 15px;color: #101010;">--}}
{{-- @if($product['sales_method'] == 'piece')--}}
{{-- &nbsp;{{ $product['minimum_order'] }} {{explode('/',$product['unit'])[1]}}<span style="color: #999999;">({{__('product.minimum_order')}})</span>--}}
{{-- @else--}}
{{-- &nbsp;{{ ($product['piece_to_batch'] * $product['minimum_order']) }} {{explode('/',$product['unit'])[1]}}<span style="color: #999999;">({{__('product.minimum_order')}})</span>--}}
{{-- @endif--}}
{{--</span>--}}
@if($product['sales_method'] == 'batches')
<span class="sku-min-order">
{{__('product.minimum_order')}}@{{ minimum_order }}
{{ __('product.batches') }}
1 {{ __('product.batches') }} = {{ $product['piece_to_batch'] }} {{ strpos($product['unit'], '/') !== false ? explode('/',$product['unit'])[0] : $product['unit']}} in total
</span>
@endif
</div>
{{--阶梯价--}}
<div class="num-price-content price-wrap d-flex align-items-end" v-if="price_setting === 'num'">
<div class="item num-trade-term-content">
{{$product['trade_term'] ?? 'DAP'}}<i class="bi bi-question-circle num-trade-term-content-i" id="tradeTerm"></i>
</div>
<div class="item" v-for="(item,index) in numPrices" style="flex: 1" :style="{width: 100 / numPrices.length + '%'}">
<div class="num num-item-content" v-if="index < numPrices.length - 1">
@{{ item.num }} - @{{ numPrices[index + 1].num - 1 }} {{ strpos($product['unit'], '/') !== false ? explode('/',$product['unit'])[1] : $product['unit']}}
</div>
<div class="num num-item-content" v-else-if="item.num">
>= @{{ item.num }} {{ strpos($product['unit'], '/') !== false ? explode('/',$product['unit'])[1] : $product['unit']}}
</div>
<div class="price">
<div class="new-price fs-3 lh-1 fw-bold me-2">@{{ item.price_format }}</div>
</div>
</div>
@if($product['sales_method'] == 'batches')
<div class="num-min-order">
{{__('product.minimum_order')}}@{{ minimum_order }}
{{ __('product.batches') }}
1 {{ __('product.batches') }} = {{ $product['piece_to_batch'] }} {{ strpos($product['unit'], '/') !== false ? explode('/',$product['unit'])[0] : $product['unit']}} in total
</div>
@endif
</div>
@endhookwrapper
<div class="stock-and-sku mb-4">
<div class="d-flex" >
<span class="title text-muted" style="width: auto!important;">{{__('product.product_type')}}</span>
{{ $product['active'] == 1 ? __('common.enable_status') : __('common.disable_status') }}
</div>
@hookwrapper('product.detail.quantity')
<div class="d-flex">
<span class="title text-muted">{{ __('product.quantity') }}</span>
<div :class="product.quantity > 0 ? 'text-success' : 'text-secondary'">
<template v-if="product.quantity > 0">@{{ product.quantity }}</template>
<template v-else>@{{ product.quantity }}</template>
</div>
</div>
@endhookwrapper
@if ($product['brand_id'])
@hookwrapper('product.detail.brand')
<div class="d-flex">
<span class="title text-muted">{{ __('product.brand') }}</span>
<a href="{{ shop_route('brands.show', $product['brand_id']) }}">{{ $product['brand_name'] }}</a>
</div>
@endhookwrapper
@endif
@hookwrapper('product.detail.sku')
<div class="d-flex" v-if="product.sku">
<span class="title text-muted">SKU</span>@{{ product.sku }}
</div>
@endhookwrapper
@hookwrapper('product.detail.model')
<div class="d-flex" v-if="product.model"><span class="title text-muted">{{ __('shop/products.model') }}</span> @{{ product.model }}</div>
@endhookwrapper
{{--<div v-if="minimum_order > 0" class="d-flex" >
<span class="title text-muted" style="width: auto!important;">{{ __('product.sales_method') }}</span>
@if($product['sales_method'] == 'piece')
{{ __('product.sales_method_piece_unit',['unit'=>$product['unit']]) }}
@else
{{ __('product.sales_method_batches') }}{{ __('product.pieces_per_batch',['num'=>$product['piece_to_batch'],'unit'=>$product['unit']]) }}
@endif
</div>--}}
{{--<div class="d-flex" >
<span class="title text-muted" style="width: auto!important;">{{__('product.minimum_order')}}</span>@{{ minimum_order }}
@if($product['sales_method'] == 'piece')
{{ $product['unit'] }}
@else
{{ __('product.batches') }}{{__('product.total_num',['num'=>($product['piece_to_batch'] * $product['minimum_order']),'unit'=>$product['unit']])}}
@endif
</div>--}}
{{--@if(!empty($product['unit']))
<div class="d-flex">
<span class="title text-muted" style="width: auto!important;">{{__('product.unit_of_measurement')}}</span> {{ $product['unit'] }}
</div>
@endif--}}
</div>
@if (0)
<div class="rating-wrap d-flex">
<div class="rating">
@for ($i = 0; $i < 5; $i++)
<i class="iconfont">&#xe628;</i>
@endfor
</div>
<span class="text-muted">132 reviews</span>
</div>
@endif
{{--商品规格--}}
<div class="variables-wrap mb-4" v-if="source.variables.length">
<div class="variable-group mb-2" v-for="variable, variable_index in source.variables" :key="variable_index">
<p class="mb-2">@{{ variable.name }}</p>
<div class="variable-info" v-if="!variable.isNumSelect">
<div
v-for="value, value_index in variable.values"
@click="checkedVariableValue(variable_index, value_index, value)"
:key="value_index"
:class="[value.selected ? 'selected' : '', value.disabled ? 'disabled' : '']">
<span class="image" v-if="value.image"><img :src="value.image" class="img-fluid"></span>
@{{ value.name }}
</div>
</div>
<div class="variable-info-select" v-if="variable.isNumSelect">
<div class="variable-info-select-num" v-for="value, value_index in variable.values" style="display: flex;
align-items: center;
justify-content: space-between;
width: 500px;">
<div class="variable-info-select-num-show">
<div
@click="checkedVariableValue(variable_index, value_index, value)"
:key="value_index">
<span class="image" v-if="value.image"><img :src="value.image" class="img-fluid"></span>
@{{ value.name }}
</div>
</div>
<div class="quantity-wrap">
<input type="text" @change="quantityChange"
class="form-control"
:disabled="!product.quantity"
onkeyup="this.value=this.value.replace(/\D/g,'')"
v-model="value.quantity"
value="0"
:minimum="minimum_order"
:sales_method="sales_method"
:piece_to_batch="piece_to_batch"
:name="'variables['+ variable_index +'].value['+value_index+'].quantity'">
<div class="right">
<i class="bi bi-chevron-up" @click="quantityChange"></i>
<i class="bi bi-chevron-down" @click="quantityChange"></i>
</div>
</div>
</div>
</div>
</div>
</div>
{{-- 购买数量 - 单规格 --}}
<div class="quantity-wrap mb-4" v-if="Object.keys(source.skus).length <= 1">
<input type="text"
class="form-control"
:disabled="!product.quantity"
:value="quantity"
@input="singleQuantityChange($event)"
:minimum="minimum_order"
:sales_method="sales_method"
:piece_to_batch="piece_to_batch"
name="quantity">
<div class="right" v-if="!isNumSelect">
<i class="bi bi-chevron-up"></i>
<i class="bi bi-chevron-down"></i>
</div>
</div>
{{--购买&进入购物车按钮--}}
<div class="quantity-btns">
@hook('product.detail.buy.before')
{{--添加购买规格--}}
<button v-if="product.quantity > 0 && !add_buy_sku[product.id] && Object.keys(source.skus).length > 1" class="btn btn-outline-info btn-sm mb-4" @click="clickAddBuySku">
<i class="bi bi-plus me-1"></i>{{ __('shop/products.add_buy_sku') }}
</button>
{{-- 购买数量 - 多规格 --}}
<div class="buy-num-list mb-4" v-if="Object.keys(add_buy_sku).length > 0">
@hookwrapper('product.detail.quantity.input')
<div class="list-box" v-for="skuValue, skuIndex in add_buy_sku">
<div class="sku-info">
<span v-for="skuInfoValue, skuInfoIndex in skuValue.sku_info">@{{ skuInfoValue.name }}@{{ skuInfoValue.value }}</span>
</div>
<div class="quantity-wrap">
<input type="text"
class="form-control"
:disabled="skuValue.stock <= skuValue.quantity"
onkeyup="this.value=this.value.replace(/\D/g,'')"
:value="add_buy_sku[skuIndex].quantity"
@input="skuQuantityChange($event,skuIndex)"
:minimum="minimum_order"
:sales_method="sales_method"
:piece_to_batch="piece_to_batch"
name="quantity">
<div class="right" v-if="!isNumSelect">
<i class="bi bi-chevron-up"></i>
<i class="bi bi-chevron-down"></i>
</div>
</div>
</div>
@endhookwrapper
</div>
{{--操作内容--}}
<div class="operating-content mb-4">
{{--点击加入购物车--}}
@hookwrapper('product.detail.add_to_cart')
<button class="btn btn-outline-dark ms-md-3 add-cart fw-bold" :product-id="product.id" :product-price="product.price" :disabled="!product.quantity" @click="addCart(false, this)">
<i class="bi bi-cart-fill me-1"></i>{{ __('shop/products.add_to_cart') }}
</button>
@endhookwrapper
{{-- 直接下单产品 - 显示立即购买按钮 --}}
@if ($product['active'])
@hookwrapper('product.detail.buy_now')
<button class="btn btn-dark ms-3 fw-bold" :disabled="!product.quantity" :product-id="product.id" :product-price="product.price" @click="addCart(true, this)">
<i class="bi bi-bag-fill me-1"></i>{{ __('shop/products.buy_now') }}
</button>
@endhookwrapper
@endif
@hook('product.detail.buy.after')
{{-- 加入收藏 --}}
@if ((current_customer() || !request('iframe')))
@hookwrapper('product.detail.wishlist')
<button class="btn btn-link ms-3 text-secondary" data-in-wishlist="{{ $product['in_wishlist'] }}" onclick="bk.addWishlist('{{ $product['id'] }}', this)">
@if ($product['in_wishlist'])
<i class="bi bi-heart-fill me-1"></i> {{ __('shop/products.cancel_to_favorites') }}
@else
<i class="bi bi-heart me-1"></i> {{ __('shop/products.add_to_favorites') }}
@endif
</button>
@endhookwrapper
@endif
</div>
</div>
{{--价格实时计算--}}
<ul class="totals">
{{--<li>
<span>{{ __('shop/products.product_total') }}</span>
<span>@{{ product_total }}</span>
</li>
<li>
<span>{{ __('shop/products.shipping_fee') }}</span>
<span>@{{ shipping_fee }}</span>
</li>
<li>
<span>{{ __('shop/products.order_total') }}</span>
<span>@{{ order_tota }}</span>
</li>--}}
<li v-for="(item,index) in totals">
<template v-if="item.code == 'shipping'">
{{--当内容为运费时--}}
<div style="width: 100%;display: inline-flex;flex-direction: column;flex-wrap: nowrap;justify-content: center;align-items: flex-start;">
<div style="height: 20px !important;width: 100%;display: inline-flex;justify-content: space-between;">
<span class="shipping-line">
<div>@{{ item.title }}</div>
<div class="country" v-if="Object.keys(item.country).length > 0">
{{ __('shop/products.ship_to') }}
<div class="wh-20 border d-flex justify-content-center rounded-2 align-items-center">
<img :src="item.country.icon" class="img-fluid rounded-2">
</div>
<div class="country-name" @click="$refs['select-countries'].showSelectCountries()">
@{{ item.country.name }}<i class="bi bi-chevron-down"></i>
@{{ logisticsChangeCountry(item.country.id) }}
</div>
</div>
</span>
<span class="shipping-line" v-if="item.show_tips != 1" @click="$refs['select-logistics'].showSelectLogistics()">
<div>@{{ item.amount_format }}</div>
{{--<div class="logistics-content" v-if="Object.keys(item.logistics).length > 0">
@{{ item.logistics.name }}<i class="bi bi-chevron-down"></i>
</div>--}}
</span>
<span class="shipping-line" v-else>@{{ item.amount_tips }}</span>
</div>
<div class="logistics-bottom-content" v-if="item.show_tips != 1" @click="$refs['select-logistics'].showSelectLogistics()">
<div class="logistics-content">
@{{ item.logistics.name }}<i class="bi bi-chevron-down"></i>
</div>
</div>
</div>
</template>
<template v-else>
<span>@{{ item.title }}</span>
<span class="other-amount-format">@{{ item.amount_format }}</span>
</template>
</li>
</ul>
<button
style="width: 16em;"
class="btn btn-outline-dark my-lg-2 add-cart fw-bold"
:disabled="!product.quantity"
@click="centerDialogVisable = true"
><i class="bi bi-globe me-1"></i>{{ __('shop/products.inquiry') }}
</button>
</div>
</div>
</div>
<!-- 弹出层 -->
<el-dialog title="Inquiry" :visible.sync="centerDialogVisable" width="700px" center>
<div class="root" style="
display: flex;">
<div class="left" style="
flex: 4;">
<el-form :model="registerForm" ref="registerForm" label-width="90px" :rules="rules">
<!-- 弹出层歌手名列 -->
<el-form-item prop="contacts" label="Contacts" size="mini">
<el-input v-model="registerForm.contacts" placeholder="Contacts"></el-input>
</el-form-item>
<!-- 弹出层歌手性别列 -->
{{-- <el-form-item prop="sex" label="歌手性别" size="mini">--}}
{{-- <el-radio-group v-model="registerForm.sex">--}}
{{-- <el-radio :label="0"></el-radio>--}}
{{-- <el-radio :label="1"></el-radio>--}}
{{-- <el-radio :label="2">组合</el-radio>--}}
{{-- <el-radio :label="3">不明</el-radio>--}}
{{-- </el-radio-group>--}}
{{-- </el-form-item>--}}
<!-- <el-form-item prop="pic" label="歌手头像" size="mini">
<el-input v-model="registerForm.pic" placeholder="歌手头像"></el-input>
</el-form-item> -->
<!-- 弹出层歌手生日列 -->
{{-- <el-form-item prop="birth" label="生日" size="mini">--}}
{{-- <el-date-picker type="date" placeholder="选择日期" v-model="registerForm.birth" style="width: 100%;"></el-date-picker>--}}
{{-- </el-form-item>--}}
<el-form-item prop="email" label="E-mail" size="mini">
<el-input v-model="registerForm.email" placeholder="E-mail"></el-input>
</el-form-item>
<el-form-item prop="content" label="Content" size="mini">
<el-input v-model="registerForm.content" placeholder="Content" type="textarea" :rows="10"></el-input>
</el-form-item>
</el-form>
@hook('product.detail.after')
</div>
<div class="right" style="
flex: 2;
display: flex;
align-items: center;
}">
<div class="product" style="
margin: -35px 10px 0 10px;">
<div class="product-image">
<img :src="images.length ? images[0].preview : '{{ asset('image/placeholder.png') }}'" class="img-fluid">
</div>
<div class="product-info" style="
margin: 5px 5px 0 5px;">
<div class="product-name">{{ $product['name'] }}</div>
</div>
</div>
</div>
</div>
<!-- 取消,确定按钮点击事件 -->
<span slot="footer">
{{-- <el-button size="mini" @click="centerDialogVisable = false">Cancel</el-button>--}}
<el-button size="mini" @click="addSinger('registerForm')">Submit</el-button>
</span>
</el-dialog>
<div class="product-description {{ $iframeClass }}">
<div class="nav nav-tabs nav-overflow justify-content-start justify-content-md-center border-bottom mb-3">
<a class="nav-link fw-bold active fs-5" data-bs-toggle="tab" href="#product-description">
{{ __('shop/products.product_details') }}
</a>
@if ($product['attributes'])
<a class="nav-link fw-bold fs-5" data-bs-toggle="tab" href="#product-attributes">
{{ __('admin/attribute.index') }}
</a>
@endif
@hook('product.tab.after.link')
</div>
<div class="tab-content">
<div class="tab-pane fade show active" id="product-description" role="tabpanel">
{!! $product['description'] !!}
</div>
<div class="tab-pane fade" id="product-attributes" role="tabpanel">
<table class="table table-bordered attribute-table">
@foreach ($product['attributes'] as $group)
<thead class="table-light">
<tr>
<td colspan="2"><strong>{{ $group['attribute_group_name'] }}</strong></td>
</tr>
</thead>
<tbody>
@foreach ($group['attributes'] as $item)
<tr>
<td>{{ $item['attribute'] }}</td>
<td>{{ $item['attribute_value'] }}</td>
</tr>
@endforeach
</tbody>
@endforeach
</table>
</div>
@hook('product.tab.after.pane')
</div>
</div>
{{-- 国家选择器 --}}
<select-countries ref="select-countries" :country_list="country_list" @change-info="changeCountry" :is_single="true"></select-countries>
{{-- 物流选择器 --}}
<select-logistics ref="select-logistics" @change-info="changeLogistics" :country_id="logistics_country_id" :goods_list="goods_list"></select-logistics>
</div>
@if ($relations && !request('iframe'))
<div class="relations-wrap mt-5">
<div class="container position-relative">
<div class="title text-center fs-1 mb-4">{{ __('admin/product.product_relations') }}</div>
<div class="product swiper-style-plus">
<div class="swiper relations-swiper">
<div class="swiper-wrapper">
@foreach ($relations as $item)
<div class="swiper-slide">
@include('shared.product', ['product' => $item])
</div>
@endforeach
</div>
</div>
<div class="swiper-pagination relations-pagination"></div>
<div class="swiper-button-prev relations-swiper-prev"></div>
<div class="swiper-button-next relations-swiper-next"></div>
</div>
</div>
</div>
@endif
@hook('product.detail.footer')
@endsection
@push('add-scripts')
@include('shared.select-countries')
@include('shared.select-logistics')
<script>
let swiperMobile = null;
const isIframe = bk.getQueryString('iframe', false);
let app = new Vue({
el: '#product-app',
data: {
product_total: 0,
shipping_fee: 0,
order_tota: 0,
isNumSelect: false,
selectedVariantsIndex: [], // 选中的变量索引
images: [],
product: {
id: 0,
images: "",
model: "",
origin_price: 0,
origin_price_format: "",
position: 0,
price: 0,
price_format: "",
quantity: 0,
sku: "",
},
quantity: 0,
source: {
skus: @json($product['skus']),
variables: @json($product['variables'] ?? []),
},
product_id: "{{$product_id}}",
price_setting: @json($product['price_setting'] ?? 'sku'),
numPrices: @json($product['numPrices'] ?? []),
minimum_order: @json($product['minimum_order'] ?? 0),
sales_method: @json($product['sales_method'] ?? 'piece'),
piece_to_batch: @json($product['piece_to_batch'] ?? 0),
centerDialogVisable: false, // 设置显示框的状态
registerForm: { // 添加的信息
contacts: '',
email: '',
content: '',
product_sku_id: '',
},
rules: {
contacts: [{
required: true,
message: '{{ __('shop/products.enter_contacts') }}',
trigger: 'blur'
},],
email: [{
required: true,
type: 'email',
message: '{{ __('shop/products.enter_email') }}',
trigger: 'blur'
},],
content: [{
required: true,
message: '{{ __('shop/products.enter_content') }}',
trigger: 'blur'
},],
},
// 多规格批量购买
goods_list: {},
add_buy_sku:{},
totals: {},// 支付金额统计
// 国家选择
country_list: {},
country_id: 0,
// 物流切换
logistics_country_id: 0,
change_logistics_id: 0,
},
watch: {
quantity: function (newval, oldval) {
let _this = this;
let list = {};
// 请求计算运费
list[0] = {
product_id: _this.product_id,
product_sku_id: this.product.id,
quantity: this.quantity,
};
_this.goods_list = list;
_this.computeOrderMoney();
_this.$refs['select-logistics'].getLogisticsList()
// let price = 0;
// if (this.numPrices.light == 0) {
// price = this.product.price
// } else {
// price = this.numPrices[0].price
// this.numPrices.forEach(v => {
// if (this.quantity > v.num) {
// }
// price = v.price
// })
// }
//
// this.product_total = (price * this.quantity).toFixed(2)
// this.shipping_fee = (this.product_total * 0.1).toFixed(2)
// this.order_tota = (this.product_total * 1 + this.shipping_fee * 1).toFixed(2)
},
add_buy_sku: {
deep: true,//true为进行深度监听,false为不进行深度监听
handler(){
let _this = this;
let list = {};
Object.values(this.add_buy_sku).forEach((item,index) =>{
list[index] = {
product_id: _this.product_id,
product_sku_id: item.id,
quantity: item.quantity,
};
})
_this.goods_list = list;
_this.computeOrderMoney();
_this.$refs['select-logistics'].getLogisticsList()
}
}
},
created() {
if (this.price_setting === 'num') {
// this.quantity = this.numPrices[0].num;
}
this.source.variables.forEach(variable => {
if (variable.isNumSelect == true) {
this.isNumSelect = true;
}
variable.values.forEach(value => {
value.quantity = 0
})
})
},
beforeMount() {
const skus = JSON.parse(JSON.stringify(this.source.skus));
const skuDefault = skus.find(e => e.is_default)
this.selectedVariantsIndex = skuDefault.variants
// 为 variables 里面每一个 values 的值添加 selected、disabled 字段
if (this.source.variables.length) {
this.source.variables.forEach(variable => {
variable.values.forEach(value => {
this.$set(value, 'selected', false)
this.$set(value, 'disabled', false)
})
})
this.checkedVariants()
this.getSelectedSku(false);
this.updateSelectedVariantsStatus()
} else {
// 如果没有默认的sku则取第一个sku的第一个变量的第一个值
this.product = skus[0];
this.images = @json($product['images'] ?? []);
}
},
methods: {
quantityChange() {
setTimeout(() => {
let num = 0;
this.source.variables.forEach(variable => {
variable.values.forEach(value => {
num += parseInt(value.quantity);
})
})
this.quantity = num;
console.log(this.quantity, num)
}, 200);
},
addSinger(form) { // 点击确定按钮添加方法
this.$refs[form].validate((valid) => {
if (!valid) {
this.$message.error('{{ __('shop/checkout.check_form') }}');
return;
}
this.registerForm.product_sku_id = this.product.id
$http.post(`/inquiry`, this.registerForm).then((res) => {
layer.msg('Success')
this.registerForm = {
contacts: '',
email: '',
content: '',
product_sku_id: '',
}
})
this.centerDialogVisable = false
});
},
checkedVariableValue(variable_index, value_index, value) {
$('.product-image .swiper .swiper-slide').eq(0).addClass('active').siblings().removeClass('active');
this.source.variables[variable_index].values.forEach((v, i) => {
v.selected = i == value_index
})
this.updateSelectedVariantsIndex();
this.getSelectedSku();
this.updateSelectedVariantsStatus()
},
// 把对应 selectedVariantsIndex 下标选中 variables -> values 的 selected 字段为 true
checkedVariants() {
this.source.variables.forEach((variable, index) => {
variable.values[this.selectedVariantsIndex[index]].selected = true
})
},
getSelectedSku(reload = true) {
// 通过 selectedVariantsIndex 的值比对 skus 的 variables
const sku = this.source.skus.find(sku => sku.variants.toString() == this.selectedVariantsIndex.toString())
this.images =
@json($product['images'] ?? [])
if (reload) {
this.images.unshift(...sku.images)
}
this.product = sku;
if (swiperMobile) {
swiperMobile.slideTo(0, 0, false)
}
this.$nextTick(() => {
$('#zoom img').attr('src', $('#swiper a').attr('data-image'));
$('#zoom').trigger('zoom.destroy');
$('#zoom').zoom({url: $('#swiper a').attr('data-zoom-image')});
})
closeVideo()
},
updateSelectedVariantsIndex() {
// 获取选中的 variables 内 value的 下标 index 填充到 selectedVariantsIndex 中
this.source.variables.forEach((variable, index) => {
variable.values.forEach((value, value_index) => {
if (value.selected) {
this.selectedVariantsIndex[index] = value_index
}
})
})
},
updateSelectedVariantsStatus() {
// skus 里面 quantity 不为 0 的 sku.variants
const skus = this.source.skus.filter(sku => sku.quantity > 0).map(sku => sku.variants);
this.source.variables.forEach((variable, index) => {
variable.values.forEach((value, value_index) => {
const selectedVariantsIndex = this.selectedVariantsIndex.slice(0);
selectedVariantsIndex[index] = value_index;
const selectedSku = skus.find(sku => sku.toString() == selectedVariantsIndex.toString());
if (selectedSku) {
value.disabled = false;
} else {
value.disabled = true;
}
})
});
},
// 提交购物车
addCart(isBuyNow = false) {
let _this = this;
let params = {};
// 判断:当前商品如果是单规格则使用旧版本下单流程 为多规格则使用新版本下单流程
let minimum_order_error = '{{ __('product.minimum_order_error') }}';
let multiple_error = '{{ __('product.multiple_error') }}';
if(Object.keys(_this.source.skus).length > 1){
// 多规格
if (Object.keys(_this.add_buy_sku).length <= 0) {
layer.msg('{{ __('shop/products.buy_sku_error') }}');
return;
}
// 购买数量判断
let total = 0;
Object.values(_this.add_buy_sku).forEach((item,index) =>{
total += parseInt(item.quantity);
})
if(total <= 0){
layer.msg('{{ __('shop/products.quantity_error') }}');
return;
}
// 判断:当前商品购买数量是否大于等于最小起订量
let minimum_order = _this.sales_method === 'batches' ? _this.minimum_order * _this.piece_to_batch : _this.minimum_order;
if(minimum_order > total){
layer.msg(minimum_order_error.replace(':num',minimum_order));
return;
}
// 判断当前销售方式是批量销售购买数量必须是N的倍数
let multiple_num = _this.sales_method === 'batches' ? parseInt(_this.piece_to_batch) : 1;
if(parseInt(total % multiple_num) != 0){
layer.msg(multiple_error.replace(':num',multiple_num));
return;
}
// 请求参数
params = {
isBuyNow,
sku_id: JSON.stringify(_this.add_buy_sku) || {},
quantity: 1,
};
}else{
// 单规格
if (_this.quantity <= 0 || _this.quantity > _this.product.quantity) {
layer.msg('{{ __('shop/products.quantity_error') }}');
return;
}
// 判断:当前商品购买数量是否大于等于最小起订量
let minimum_order = _this.sales_method === 'batches' ? _this.minimum_order * _this.piece_to_batch : _this.minimum_order;
if(minimum_order > _this.quantity){
layer.msg(minimum_order_error.replace(':num',minimum_order));
return;
}
// 判断当前销售方式是批量销售购买数量必须是N的倍数
let multiple_num = _this.sales_method === 'batches' ? parseInt(_this.piece_to_batch) : 1;
if(parseInt(_this.quantity % multiple_num) != 0){
layer.msg(multiple_error.replace(':num',multiple_num));
return;
}
// 请求参数
params = {
sku_id: _this.product.id,
quantity: _this.quantity,
isBuyNow
};
}
// 公共参数加入
params.products_country_id = _this.country_id;
params.change_logistics_id = _this.change_logistics_id;
// 提交内容
bk.addCart(params, null, () => {
if (isIframe) {
let index = parent.layer.getFrameIndex(window.name); //当前iframe层的索引
parent.bk.getCarts();
setTimeout(() => {
parent.layer.close(index);
if (isBuyNow) {
parent.location.href = 'checkout'
} else {
parent.$('.btn-right-cart')[0].click()
}
}, 400);
} else {
if (isBuyNow) {
location.href = 'checkout'
}
}
});
},
// 点击添加公共
clickAddBuySku(){
let _this = this;
let product = {
id: _this.product.id,
model: _this.product.model,
sku: _this.product.sku,
origin_price: _this.product.origin_price,
price: _this.product.price,
total_price: 0,
quantity: 0,// 当前购买数量
stock: _this.product.quantity,// 当前商品库存
sku_info: {},
};
// 获取规格信息
let variables = _this.source.variables;
let variants = _this.product.variants;
variants.forEach((index,key) => {
let variablesInfo = variables[key];
product.sku_info[key] = {
name: variablesInfo.name,
value: variablesInfo.values[index].name,
};
});
_this.add_buy_sku[product.id] = product;
_this.$forceUpdate();
},
// 多规格 - 某个规格的购买数量改变
skuQuantityChange(event,skuIndex){
let _this = this;
let stock = _this.add_buy_sku[skuIndex].stock || 0;
let quantity = event.target.value || 0;
// 判断是否超过库存
quantity = quantity > stock ? stock : quantity;// 不能超过库存
_this.add_buy_sku[skuIndex].quantity = typeof quantity != 'number' ? quantity.replace(/\D/g, '') : quantity;
// 处理深度监听失败的问题
_this.add_buy_sku = Object.assign({}, _this.add_buy_sku);
_this.$forceUpdate();
},
// 单规格 - 购买数量改变
singleQuantityChange(event){
let multiple_error = '{{ __('product.multiple_error') }}';
let _this = this;
let stock = _this.product.quantity || 0;// 库存
let quantity = event.target.value || 0;// 购买数量
// 判断是否超过库存
quantity = quantity > stock ? stock : quantity;// 不能超过库存
_this.quantity = typeof quantity != 'number' ? quantity.replace(/\D/g,'') : quantity;
// 判断:如果是批量购买 只能为N的倍数
let multiple_num = _this.sales_method === 'batches' ? parseInt(_this.piece_to_batch) : 1;
if(parseInt(quantity % multiple_num) != 0){
layer.msg(multiple_error.replace(':num',multiple_num));
return;
}
_this.$forceUpdate();
},
// 计算当前订单总额
computeOrderMoney(){
let _this = this;
$http.post(`products/computeOrderMoney`, {
list: JSON.stringify(_this.goods_list),
products_country_id: _this.country_id,
change_logistics_id: _this.change_logistics_id
}).then((res) => {
if(res.status === 'success'){
_this.totals = res.data;
_this.$forceUpdate();
}
})
},
// 国家改变
changeCountry(info){
let _this = this;
let singleInfo = Object.values(info)[0];
_this.country_id = singleInfo['id'];
_this.computeOrderMoney();
_this.$refs['select-countries'].closeSelectCountries();
},
// 物流切换 - 物流国家改变
logisticsChangeCountry(countryId){
this.logistics_country_id = countryId || 0;
},
// 物流切换 - 使用物流改变
changeLogistics(info){
let id = info.id;
let _this = this;
if(parseInt(id) > 0 && !isNaN(parseInt(id))){
_this.change_logistics_id = info.id;
_this.computeOrderMoney();
}
},
}
});
$(document).on("mouseover", ".product-image #swiper .swiper-slide a", function () {
$(this).parent().addClass('active').siblings().removeClass('active');
$('#zoom').trigger('zoom.destroy');
$('#zoom img').attr('src', $(this).attr('data-image'));
$('#zoom').zoom({url: $(this).attr('data-zoom-image')});
closeVideo()
});
var swiper = new Swiper("#swiper", {
direction: "vertical",
slidesPerView: 1,
spaceBetween: 3,
breakpoints: {
375: {
slidesPerView: 3,
spaceBetween: 3,
},
480: {
slidesPerView: 4,
spaceBetween: 27,
},
768: {
slidesPerView: 6,
spaceBetween: 3,
},
},
navigation: {
nextEl: '.new-feature-slideshow-next',
prevEl: '.new-feature-slideshow-prev',
},
observer: true,
observeParents: true
});
var relationsSwiper = new Swiper('.relations-swiper', {
watchSlidesProgress: true,
breakpoints: {
320: {
slidesPerView: 2,
spaceBetween: 10,
},
768: {
slidesPerView: 4,
spaceBetween: 30,
},
},
spaceBetween: 30,
// 如果需要前进后退按钮
navigation: {
nextEl: '.relations-swiper-next',
prevEl: '.relations-swiper-prev',
},
// 如果需要分页器
pagination: {
el: '.relations-pagination',
clickable: true,
},
})
@if (is_mobile())
swiperMobile = new Swiper("#swiper-mobile", {
slidesPerView: 1,
pagination: {
el: ".mobile-pagination",
},
observer: true,
observeParents: true
});
@endif
$(document).ready(function () {
$('#zoom').trigger('zoom.destroy');
$('#zoom').zoom({url: $('#swiper a').attr('data-zoom-image')});
$('#tradeTerm').popover({
trigger : 'hover',//鼠标以上时触发弹出提示框
html: true,//开启html 为true的话data-content里就能放html代码了
placement: 'bottom',
delay: { "show": 0, "hide": 100 },
content:"<img class='trade-term-img' src='image/trade_term_web.png'>"
});
});
const selectedVariantsIndex = app.selectedVariantsIndex;
const variables = app.source.variables;
const selectedVariants = variables.map((variable, index) => {
return variable.values[selectedVariantsIndex[index]]
});
</script>
@endpush