v1.1.2 合并至github项目

This commit is contained in:
liqianjin 2023-08-10 11:34:43 +08:00
commit 8674f1125c
82 changed files with 9635 additions and 397 deletions

28
.env Normal file
View File

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

7
404.html Normal file
View File

@ -0,0 +1,7 @@
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>

View File

@ -0,0 +1,145 @@
<?php
/**
* PagesController.php
*
* @copyright 2022 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2022-08-08 15:07:33
* @modified 2022-08-08 15:07:33
*/
namespace Beike\Admin\Http\Controllers;
use Beike\Admin\Http\Requests\PageRequest;
use Beike\Admin\Repositories\InquiryRepo;
use Beike\Models\Page;
use Beike\Shop\Http\Resources\InquiryDetail;
use Beike\Shop\Http\Resources\ProductSimple;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class InquiryController extends Controller
{
/**
* 显示单页列表
*
* @return mixed
*/
public function index()
{
$pageList = InquiryRepo::getList();
$data = [
'pages' => $pageList,
'pages_format' => InquiryDetail::collection($pageList)->jsonSerialize(),
];
return view('admin::pages.inquiry.index', $data);
}
/**
* 创建页面
*
* @return mixed
*/
public function create()
{
return view('admin::pages.pages.form', ['page' => new Page()]);
}
/**
* 保存新建
*
* @param PageRequest $request
* @return RedirectResponse
*/
public function store(PageRequest $request)
{
try {
$requestData = $request->all();
$page = PageRepo::createOrUpdate($requestData);
hook_action('admin.page.store.after', ['request_data' => $requestData, 'page' => $page]);
return redirect(admin_route('pages.index'));
} catch (\Exception $e) {
return redirect(admin_route('pages.index'))->withErrors(['error' => $e->getMessage()]);
}
}
/**
* @param Request $request
* @param Page $page
* @return mixed
*/
public function edit(Request $request, Page $page)
{
$page->load(['products.description', 'category.description']);
$data = [
'page' => $page,
'products' => ProductSimple::collection($page->products)->jsonSerialize(),
'descriptions' => PageRepo::getDescriptionsByLocale($page->id),
];
return view('admin::pages.pages.form', $data);
}
/**
* 保存更新
*
* @param PageRequest $request
* @param int $pageId
* @return RedirectResponse
*/
public function update(PageRequest $request, int $pageId)
{
try {
$requestData = $request->all();
$requestData['id'] = $pageId;
$page = PageRepo::createOrUpdate($requestData);
hook_action('admin.page.update.after', ['request_data' => $requestData, 'page' => $page]);
return redirect()->to(admin_route('pages.index'));
} catch (\Exception $e) {
return redirect(admin_route('pages.index'))->withErrors(['error' => $e->getMessage()]);
}
}
/**
* 删除单页
*
* @param Request $request
* @param int $pageId
* @return array
*/
public function destroy(Request $request, int $pageId): array
{
InquiryRepo::deleteById($pageId);
return json_success(trans('common.deleted_success'));
}
/**
* 搜索页面标题自动完成
* @param Request $request
* @return array
*/
public function autocomplete(Request $request): array
{
$products = PageRepo::autocomplete($request->get('name') ?? '');
return json_success(trans('common.get_success'), $products);
}
/**
* 获取单页名称
* @param Page $page
* @return array
*/
public function name(Page $page): array
{
$name = $page->description->title ?? '';
return json_success(trans('common.get_success'), $name);
}
}

View File

@ -0,0 +1,136 @@
<?php
/**
* PageRepo.php
*
* @copyright 2022 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2022-07-26 21:08:07
* @modified 2022-07-26 21:08:07
*/
namespace Beike\Admin\Repositories;
use Beike\Models\Inquiry;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
class InquiryRepo
{
/**
* 获取列表页数据
*
* @return LengthAwarePaginator
*/
public static function getList(): LengthAwarePaginator
{
$builder = Inquiry::query()->with([
'productsku' => function($query) {
$query->withTrashed();
},
'productsku.product' => function($query) {
$query->withTrashed()->with(['description']);
},
])->orderByDesc('updated_at');
return $builder->paginate(perPage());
}
public static function findByPageId($pageId)
{
$page = Inquiry::query()->findOrFail($pageId);
$page->load(['descriptions']);
return $page;
}
public static function getDescriptionsByLocale($pageId)
{
$page = self::findByPageId($pageId);
return $page->descriptions->keyBy('locale')->toArray();
}
public static function createOrUpdate($data)
{
try {
DB::beginTransaction();
$region = self::pushPage($data);
DB::commit();
return $region;
} catch (\Exception $e) {
DB::rollBack();
throw $e;
}
}
public static function pushPage($data)
{
$id = $data['id'] ?? 0;
if ($id) {
$page = Inquiry::query()->findOrFail($id);
} else {
$page = new Inquiry();
}
$page->fill([
'page_category_id' => (int) ($data['page_category_id'] ?? 0),
'position' => (int) ($data['position'] ?? 0),
'active' => (bool) ($data['active'] ?? true),
'author' => $data['author'] ?? '',
'views' => (int) ($data['views'] ?? 0),
]);
$page->saveOrFail();
$page->descriptions()->delete();
$page->descriptions()->createMany($data['descriptions']);
$products = $data['products'] ?? [];
if ($products) {
$items = [];
foreach ($products as $item) {
$items[] = [
'product_id' => $item,
];
}
$page->pageProducts()->delete();
$page->pageProducts()->createMany($items);
}
$page->load(['descriptions', 'pageProducts']);
return $page;
}
public static function deleteById($id)
{
$page = Inquiry::query()->findOrFail($id);
$page->delete();
}
/**
* 页面内容自动完成
*
* @param $name
* @return array
*/
public static function autocomplete($name): array
{
$pages = Inquiry::query()->with('description')
->whereHas('description', function ($query) use ($name) {
$query->where('title', 'like', "{$name}%");
})->limit(10)->get();
$results = [];
foreach ($pages as $page) {
$results[] = [
'id' => $page->id,
'name' => $page->description->title,
'status' => $page->active,
];
}
return $results;
}
}

View File

@ -169,6 +169,10 @@ Route::prefix($adminName)
Route::middleware('can:pages_update')->put('pages/{page}', [Controllers\PagesController::class, 'update'])->name('pages.update'); Route::middleware('can:pages_update')->put('pages/{page}', [Controllers\PagesController::class, 'update'])->name('pages.update');
Route::middleware('can:pages_delete')->delete('pages/{page}', [Controllers\PagesController::class, 'destroy'])->name('pages.destroy'); Route::middleware('can:pages_delete')->delete('pages/{page}', [Controllers\PagesController::class, 'destroy'])->name('pages.destroy');
// 文章
Route::middleware('can:inquiry_index')->get('inquiry', [Controllers\InquiryController::class, 'index'])->name('inquiry.index');
Route::middleware('can:inquiry_delete')->delete('inquiry/{page}', [Controllers\InquiryController::class, 'destroy'])->name('inquiry.destroy');
// 文章分类 // 文章分类
Route::middleware('can:page_categories_index')->get('page_categories', [Controllers\PageCategoryController::class, 'index'])->name('page_categories.index'); Route::middleware('can:page_categories_index')->get('page_categories', [Controllers\PageCategoryController::class, 'index'])->name('page_categories.index');
Route::middleware('can:page_categories_index')->get('page_categories/autocomplete', [Controllers\PageCategoryController::class, 'autocomplete'])->name('page_categories.autocomplete'); Route::middleware('can:page_categories_index')->get('page_categories/autocomplete', [Controllers\PageCategoryController::class, 'autocomplete'])->name('page_categories.autocomplete');

View File

@ -35,6 +35,7 @@ class ProductService
$product->skus()->delete(); $product->skus()->delete();
$product->descriptions()->delete(); $product->descriptions()->delete();
$product->attributes()->delete(); $product->attributes()->delete();
$product->numPrices()->delete();
} }
$descriptions = []; $descriptions = [];
@ -58,6 +59,10 @@ class ProductService
} }
$product->skus()->createMany($skus); $product->skus()->createMany($skus);
if($data['price_setting'] == 'num' && !empty($data['numPrices'])){
$product->numPrices()->createMany(list_sort_by($data['numPrices'],'num') ?? []);
}
$product->categories()->sync($data['categories'] ?? []); $product->categories()->sync($data['categories'] ?? []);
$product->relations()->sync($data['relations'] ?? []); $product->relations()->sync($data['relations'] ?? []);

View File

@ -79,6 +79,7 @@ class Header extends Component
['name' => trans('admin/common.customer'), 'route' => 'customers.index', 'code' => 'Customer'], ['name' => trans('admin/common.customer'), 'route' => 'customers.index', 'code' => 'Customer'],
['name' => trans('admin/common.page'), 'route' => 'pages.index', 'code' => 'Page'], ['name' => trans('admin/common.page'), 'route' => 'pages.index', 'code' => 'Page'],
['name' => trans('admin/common.setting'), 'route' => 'settings.index', 'code' => 'Setting'], ['name' => trans('admin/common.setting'), 'route' => 'settings.index', 'code' => 'Setting'],
['name' => trans('admin/common.inquiry'), 'route' => 'inquiry.index', 'code' => 'Inquiry'],
]; ];
return hook_filter('admin.header_menus', $menus); return hook_filter('admin.header_menus', $menus);

View File

@ -69,6 +69,11 @@ class Sidebar extends Component
foreach ($routes as $route) { foreach ($routes as $route) {
$this->addLink($route, $this->equalRoute($route['route']), (bool) ($route['blank'] ?? false), $route['hide_mobile'] ?? 0); $this->addLink($route, $this->equalRoute($route['route']), (bool) ($route['blank'] ?? false), $route['hide_mobile'] ?? 0);
} }
} elseif (Str::startsWith($routeName, $this->getInquirySubPrefix())) {
$routes = $this->getInquirySubRoutes();
foreach ($routes as $route) {
$this->addLink($route, $this->equalRoute($route['route']), (bool) ($route['blank'] ?? false), $route['hide_mobile'] ?? 0);
}
} }
return view('admin::components.sidebar'); return view('admin::components.sidebar');
@ -157,6 +162,16 @@ class Sidebar extends Component
return hook_filter('admin.sidebar.page.prefix', $prefix); return hook_filter('admin.sidebar.page.prefix', $prefix);
} }
/**
* 获取后台寻盘子页面路由前缀列表
*/
private function getInquirySubPrefix()
{
$prefix = ['inquiry.'];
return hook_filter('admin.sidebar.page.prefix', $prefix);
}
/** /**
* 获取后台系统设置子页面路由前缀列表 * 获取后台系统设置子页面路由前缀列表
*/ */
@ -243,6 +258,19 @@ class Sidebar extends Component
return hook_filter('admin.sidebar.pages_routes', $routes); return hook_filter('admin.sidebar.pages_routes', $routes);
} }
/**
* 获取寻盘管理子页面路由
* @return mixed
*/
public function getInquirySubRoutes()
{
$routes = [
['route' => 'inquiry.index', 'icon' => 'fa fa-tachometer-alt'],
];
return hook_filter('admin.sidebar.pages_routes', $routes);
}
/** /**
* 获取系统设置子页面路由 * 获取系统设置子页面路由
* @return mixed * @return mixed

View File

@ -718,3 +718,53 @@ function perPage(): int
{ {
return (int) system_setting('base.product_per_page', 20); return (int) system_setting('base.product_per_page', 20);
} }
/**
* php多维数组指定列排序
* @param $list
* @param $field
* @param $sortby
* @return array|false
*/
function list_sort_by($list, $field, $sortby='asc') {
if(is_array($list)){ //判断是否数组
$refer = $resultSet = array(); //初始化数组变量
foreach ($list as $i => $data) //foreach数组
$refer[$i] = &$data[$field]; //存储要排序的数组字段键和值
switch ($sortby) {//进行排序
case 'asc': // 正向排序
asort($refer);
break;
case 'desc':// 逆向排序
arsort($refer);
break;
case 'nat': // 自然排序
natcasesort($refer);
break;
}
foreach ( $refer as $key=> $val)//重新组合排序后的数组
$resultSet[] = &$list[$key];
return $resultSet;
}
return false;
}

21
beike/Models/Inquiry.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace Beike\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Inquiry extends Base
{
use HasFactory;
protected $fillable = ['product_sku_id', 'contacts', 'email', 'content'];
public function productsku()
{
return $this->belongsTo(ProductSku::class, 'product_sku_id', 'id');
}
}

View File

@ -11,14 +11,26 @@ class Product extends Base
use HasFactory; use HasFactory;
use SoftDeletes; use SoftDeletes;
protected $fillable = ['images', 'video', 'position', 'brand_id', 'tax_class_id', 'active', 'variables']; protected $fillable = ['images', 'video', 'position', 'brand_id', 'tax_class_id', 'active', 'variables', 'price_setting'];
protected $casts = [ protected $casts = [
'active' => 'boolean', 'active' => 'boolean',
'variables' => 'array', 'variables' => 'array',
'images' => 'array', 'images' => 'array',
'numPrices' => 'array',
]; ];
public function getNumPricesByNum($num)
{
$descNumPrices = array_reverse($this->numprices->toArray());
foreach($descNumPrices as $numprice){
if($num >= $numprice['num']){
return $numprice['price'];
}
}
return FALSE;
}
protected $appends = ['image']; protected $appends = ['image'];
public function categories() public function categories()
@ -46,6 +58,11 @@ class Product extends Base
return $this->hasMany(ProductSku::class); return $this->hasMany(ProductSku::class);
} }
public function numPrices()
{
return $this->hasMany(ProductNumPrice::class);
}
public function attributes(): HasMany public function attributes(): HasMany
{ {
return $this->hasMany(ProductAttribute::class); return $this->hasMany(ProductAttribute::class);

View File

@ -0,0 +1,22 @@
<?php
namespace Beike\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class ProductNumPrice extends Base
{
use HasFactory;
protected $fillable = ['product_id', 'num', 'price'];
protected $casts = [
];
protected $appends = [];
public function product()
{
return $this->belongsTo(Product::class);
}
}

View File

@ -3,10 +3,12 @@
namespace Beike\Models; namespace Beike\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
class ProductSku extends Base class ProductSku extends Base
{ {
use HasFactory; use HasFactory;
use SoftDeletes;
protected $fillable = ['product_id', 'variants', 'position', 'images', 'model', 'sku', 'price', 'origin_price', 'cost_price', 'quantity', 'is_default']; protected $fillable = ['product_id', 'variants', 'position', 'images', 'model', 'sku', 'price', 'origin_price', 'cost_price', 'quantity', 'is_default'];

View File

@ -0,0 +1,68 @@
<?php
/**
* AddressRepo.php
*
* @copyright 2022 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2022-06-28 15:22:05
* @modified 2022-06-28 15:22:05
*/
namespace Beike\Repositories;
use Beike\Models\Inquiry;
class InquiryRepo
{
/**
* 创建一个address记录
* @param $data
* @return mixed
*/
public static function create($data)
{
return Inquiry::query()->create($data);
}
/**
* @param $address
* @param $data
* @return mixed
* @throws \Exception
*/
public static function update($address, $data)
{
if (! $address instanceof Inquiry) {
$address = Inquiry::query()->find($address);
}
if (! $address) {
throw new \Exception("地址id {$address} 不存在");
}
$address->update($data);
return $address;
}
/**
* @param $id
* @return mixed
*/
public static function find($id)
{
return Inquiry::query()->find($id);
}
/**
* @param $id
* @return void
*/
public static function delete($id)
{
$address = Inquiry::query()->find($id);
if ($address) {
$address->delete();
}
}
}

View File

@ -200,17 +200,18 @@ class OrderRepo
$shippingAddress = Address::query()->findOrFail($shippingAddressId); $shippingAddress = Address::query()->findOrFail($shippingAddressId);
$paymentAddress = Address::query()->findOrFail($paymentAddressId); $paymentAddress = Address::query()->findOrFail($paymentAddressId);
$shippingAddress->country = $shippingAddress->country->name ?? ''; $email = $customer->email;
$shippingAddress->country_id = $shippingAddress->country->id ?? 0;
$paymentAddress->country = $paymentAddress->country->name ?? '';
$paymentAddress->country_id = $paymentAddress->country->id ?? 0;
$email = $customer->email;
} else { } else {
$shippingAddress = new Address($current['guest_shipping_address'] ?? []); $shippingAddress = new Address($current['guest_shipping_address'] ?? []);
$paymentAddress = new Address($current['guest_payment_address'] ?? []); $paymentAddress = new Address($current['guest_payment_address'] ?? []);
$email = $current['guest_shipping_address']['email']; $email = $current['guest_shipping_address']['email'];
} }
$shippingAddress->country = $shippingAddress->country->name ?? '';
$shippingAddress->country_id = $shippingAddress->country->id ?? 0;
$paymentAddress->country = $paymentAddress->country->name ?? '';
$paymentAddress->country_id = $paymentAddress->country->id ?? 0;
$shippingMethodCode = $current['shipping_method_code'] ?? ''; $shippingMethodCode = $current['shipping_method_code'] ?? '';
$paymentMethodCode = $current['payment_method_code'] ?? ''; $paymentMethodCode = $current['payment_method_code'] ?? '';

View File

@ -153,13 +153,8 @@ class PageCategoryRepo
* @param $page * @param $page
* @return string * @return string
*/ */
public static function getName($pageCategoryId) public static function getName($page)
{ {
// 根据 pageCategoryId 获取 name判断是否存在 return $page->description->title ?? '';
$pageCategory = PageCategory::query()->whereHas('description', function ($query) use ($pageCategoryId) {
$query->where('page_category_id', $pageCategoryId);
})->first();
return $pageCategory->description->title ?? '';
} }
} }

View File

@ -39,7 +39,7 @@ class ProductRepo
if (is_int($product)) { if (is_int($product)) {
$product = Product::query()->findOrFail($product); $product = Product::query()->findOrFail($product);
} }
$product->load('description', 'skus', 'masterSku', 'brand', 'relations'); $product->load('description', 'skus', 'masterSku', 'brand', 'relations', 'numPrices');//, 'price_setting', 'numPrices');
hook_filter('repo.product.get_detail', $product); hook_filter('repo.product.get_detail', $product);
@ -94,7 +94,8 @@ class ProductRepo
}); });
$builder->leftJoin('product_skus', function ($build) { $builder->leftJoin('product_skus', function ($build) {
$build->whereColumn('product_skus.product_id', 'products.id') $build->whereColumn('product_skus.product_id', 'products.id')
->where('is_default', 1); ->where('is_default', 1)
->where('product_skus.deleted_at', null);
}); });
$builder->select(['products.*', 'pd.name', 'pd.content', 'pd.meta_title', 'pd.meta_description', 'pd.meta_keywords', 'pd.name', 'product_skus.price']); $builder->select(['products.*', 'pd.name', 'pd.content', 'pd.meta_title', 'pd.meta_description', 'pd.meta_keywords', 'pd.name', 'product_skus.price']);

View File

@ -85,15 +85,20 @@ class CartController extends Controller
*/ */
public function update(CartRequest $request, $cartId): array public function update(CartRequest $request, $cartId): array
{ {
$customer = current_customer(); try {
$quantity = (int) $request->get('quantity'); $customer = current_customer();
CartService::updateQuantity($customer, $cartId, $quantity); $quantity = (int) $request->get('quantity');
CartService::updateQuantity($customer, $cartId, $quantity);
$data = CartService::reloadData(); $data = CartService::reloadData();
$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) {
return json_fail($e->getMessage());
}
} }
/** /**

View File

@ -0,0 +1,22 @@
<?php
namespace Beike\Shop\Http\Controllers;
use Beike\Shop\Http\Requests\InquiryRequest;
use Beike\Shop\Services\InquiryService;
class InquiryController extends Controller
{
public function store(InquiryRequest $request)
{
try {
$requestData = $request->all();
$data = (new InquiryService)->create($requestData);
return hook_filter('inquiry.store.data', $data);
} catch (\Exception $e) {
return json_fail($e->getMessage());
}
}
}

View File

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

View File

@ -0,0 +1,37 @@
<?php
namespace Beike\Shop\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class InquiryRequest extends FormRequest{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(){
return TRUE;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(){
return [
'email' => 'required',
'contacts' => 'required',
'product_sku_id' => 'required|exists:product_skus,id',
'content' => 'required',
];
}
public function attributes(){
return [
'email' => trans('inquiry.email'),
'contacts' => trans('inquiry.contacts'),
'product_id' => trans('inquiry.product_id'),
'content' => trans('inquiry.content'),
];
}
}

View File

@ -19,7 +19,11 @@ class CartDetail extends JsonResource
{ {
$sku = $this->sku; $sku = $this->sku;
$product = $sku->product; $product = $sku->product;
$price = $sku->price; if($product->price_setting == 'num'){
$price = $product->getNumPricesByNum($this->product_quantity_sum);
}else{
$price = $sku->price;
};
$skuCode = $sku->sku; $skuCode = $sku->sku;
$description = $product->description; $description = $product->description;
$productName = $description->name; $productName = $description->name;

View File

@ -0,0 +1,62 @@
<?php
/**
* PageDetail.php
*
* @copyright 2022 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2022-08-11 18:45:02
* @modified 2022-08-11 18:45:02
*/
namespace Beike\Shop\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class InquiryDetail extends JsonResource{
/**
* @param Request $request
* @return array
* @throws \Exception
*/
public function toArray($request):array{
$productsku = $this->productsku;
if($this->productsku == NULL){
return [
'id' => $this->id,
'product_sku_id' => $this->product_sku_id,
'contacts' => $this->contacts,
'email' => $this->email,
'content' => $this->content,
'product_sku_sku' => '',
'product_name' => '',
'product_images' => [],
'product_id' => '',
'created_at' => time_format($this->created_at),
'updated_at' => time_format($this->updated_at),
];
}
$product = $productsku->product;
$description = $product->description;
return [
'id' => $this->id,
'product_sku_id' => $this->product_sku_id,
'contacts' => $this->contacts,
'email' => $this->email,
'content' => $this->content,
'product_sku_sku' => $productsku->sku,
'product_name' => $description->name ?? '',
'product_images' => array_map(function($image){
return [
'preview' => image_resize($image,500,500),
'popup' => image_resize($image,800,800),
'thumb' => image_resize($image,150,150),
];
},$product->images ?? []),
'product_id' => $productsku->product_id,
'created_at' => time_format($this->created_at),
'updated_at' => time_format($this->updated_at),
];
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* SkuDetail.php
*
* @copyright 2022 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
* @created 2022-07-20 11:33:06
* @modified 2022-07-20 11:33:06
*/
namespace Beike\Shop\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class NumPricesDetail extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'num' => $this->num,
'price' => $this->price,
'price_format' => currency_format($this->price),
];
}
}

View File

@ -52,6 +52,8 @@ class ProductDetail extends JsonResource
'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 ?? '',
'numPrices' => NumPricesDetail::collection($this->numprices)->jsonSerialize() ?? '',
]; ];
} }

View File

@ -49,6 +49,8 @@ class ProductSimple extends JsonResource
'origin_price_format' => currency_format($masterSku->origin_price), 'origin_price_format' => currency_format($masterSku->origin_price),
'category_id' => $this->category_id ?? null, 'category_id' => $this->category_id ?? null,
'in_wishlist' => $this->inCurrentWishlist->id ?? 0, 'in_wishlist' => $this->inCurrentWishlist->id ?? 0,
'price_setting' => $this->price_setting ?? '',
'numprices' => NumPricesDetail::collection($this->numprices)->jsonSerialize() ?? '',
'images' => array_map(function ($item) { 'images' => array_map(function ($item) {
return image_resize($item, 400, 400); return image_resize($item, 400, 400);

View File

@ -18,6 +18,7 @@ use Beike\Shop\Http\Controllers\CheckoutController;
use Beike\Shop\Http\Controllers\CurrencyController; use Beike\Shop\Http\Controllers\CurrencyController;
use Beike\Shop\Http\Controllers\FileController; use Beike\Shop\Http\Controllers\FileController;
use Beike\Shop\Http\Controllers\HomeController; use Beike\Shop\Http\Controllers\HomeController;
use Beike\Shop\Http\Controllers\InquiryController;
use Beike\Shop\Http\Controllers\LanguageController; use Beike\Shop\Http\Controllers\LanguageController;
use Beike\Shop\Http\Controllers\PageCategoryController; use Beike\Shop\Http\Controllers\PageCategoryController;
use Beike\Shop\Http\Controllers\PageController; use Beike\Shop\Http\Controllers\PageController;
@ -68,6 +69,8 @@ Route::prefix('/')
Route::get('plugin/{code}/{path}', [PluginController::class, 'asset'])->where('path', '(.*)')->name('plugin.asset'); Route::get('plugin/{code}/{path}', [PluginController::class, 'asset'])->where('path', '(.*)')->name('plugin.asset');
Route::post('inquiry', [InquiryController::class, 'store'])->name('brands.index');
Route::middleware('checkout_auth:' . Customer::AUTH_GUARD) Route::middleware('checkout_auth:' . Customer::AUTH_GUARD)
->group(function () { ->group(function () {
Route::get('carts', [CartController::class, 'index'])->name('carts.index'); Route::get('carts', [CartController::class, 'index'])->name('carts.index');

View File

@ -50,6 +50,16 @@ class CartService
return $description && $product; return $description && $product;
}); });
$productQuantitySumList = [];
foreach($cartItems as $item) {
$productId = $item->product_id;
$productQuantitySumList[$productId] = $productQuantitySumList[$productId] ?? 0;
$productQuantitySumList[$productId] += $item->quantity;
}
foreach($cartItems as $item) {
$productId = $item->product_id;
$item->product_quantity_sum = $productQuantitySumList[$productId];
}
return CartDetail::collection($cartItems)->jsonSerialize(); return CartDetail::collection($cartItems)->jsonSerialize();
} }

View File

@ -0,0 +1,24 @@
<?php
/**
* CheckoutService.php
*
* @copyright 2022 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author Edward Yang <yangjin@guangda.work>
* @created 2022-06-30 19:37:05
* @modified 2022-06-30 19:37:05
*/
namespace Beike\Shop\Services;
use Beike\Repositories\InquiryRepo;
class InquiryService
{
public static function create($data)
{
$inquiry = InquiryRepo::create($data);
return $inquiry;
}
}

View File

@ -86,7 +86,12 @@
"config": { "config": {
"optimize-autoloader": true, "optimize-autoloader": true,
"preferred-install": "dist", "preferred-install": "dist",
"sort-packages": true "sort-packages": true,
"platform": {
"ext-pcntl": "7.2",
"ext-posix": "7.2"
}
}, },
"minimum-stability": "dev", "minimum-stability": "dev",
"prefer-stable": true, "prefer-stable": true,

445
composer.lock generated

File diff suppressed because it is too large Load Diff

7572
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -20,5 +20,8 @@
"resolve-url-loader": "^4.0.0", "resolve-url-loader": "^4.0.0",
"sass": "^1.38.1", "sass": "^1.38.1",
"sass-loader": "^12.1.0" "sass-loader": "^12.1.0"
},
"dependencies": {
"element-ui": "^2.15.13"
} }
} }

7
public/.htaccess Normal file
View File

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

BIN
public/image/logo-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
public/image/logo-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 20 KiB

0
public/nginx.htaccess Normal file
View File

View File

@ -0,0 +1,8 @@
{
"/app.js": "/app.js?id=0f8def17167a0224d5704239fefc69ea",
"/app-dark.css": "/app-dark.css?id=23ca8adc130382f74688c6e36ce89407",
"/app.css": "/app.css?id=7357f6239c73ee903eba42be0458d3ab",
"/img/favicon.png": "/img/favicon.png?id=1542bfe8a0010dcbee710da13cce367f",
"/img/horizon.svg": "/img/horizon.svg?id=904d5b5185fefb09035384e15bfca765",
"/img/sprite.svg": "/img/sprite.svg?id=afc4952b74895bdef3ab4ebe9adb746f"
}

View File

@ -15,7 +15,7 @@
<div class="tab-content w-max-1000" id="myTabContent"> <div class="tab-content w-max-1000" id="myTabContent">
@foreach (locales() as $locale) @foreach (locales() as $locale)
<div class="tab-pane fade {{ $loop->first ? 'show active' : ''}}" id="{{ $locale['code'] }}-pane" role="tabpanel" aria-labelledby="{{ $locale['code'] }}"> <div class="tab-pane fade {{ $loop->first ? 'show active' : ''}}" id="{{ $locale['code'] }}-pane" role="tabpanel" aria-labelledby="{{ $locale['code'] }}">
<textarea rows="4" type="text" name="{{ $name }}[{{ $locale['code'] }}]" class="tinymce" placeholder="{{ $title }}">{{ $value[$locale['code']] ?? '' }}</textarea> <textarea rows="4" type="text" name="{{ $name }}[{{ $locale['code'] }}]" class="tinymce" placeholder="{{ $title }}">{{ $value[$locale['code']] }}</textarea>
</div> </div>
@endforeach @endforeach
</div> </div>

View File

@ -20,7 +20,7 @@
@hookwrapper('admin.header.vip') @hookwrapper('admin.header.vip')
<li class="nav-item vip-serve"> <li class="nav-item vip-serve">
<a href="{{ config('beike.api_url') }}/vip/subscription?domain={{ config('app.url') }}&developer_token={{ system_setting('base.developer_token') }}" target="_blank" class="nav-link"> <a href="javascript:void(0)" class="nav-link">
<img src="/image/vip-icon.png" class="img-fluid"> <img src="/image/vip-icon.png" class="img-fluid">
<span class="vip-text ms-1">VIP</span> <span class="vip-text ms-1">VIP</span>
<div class="expired-text text-danger ms-2" style="display: none">@lang('admin/common.expired_at')<span class="ms-0"></span></div> <div class="expired-text text-danger ms-2" style="display: none">@lang('admin/common.expired_at')<span class="ms-0"></span></div>
@ -157,13 +157,13 @@
layer.close(updatePop) layer.close(updatePop)
}); });
// $('.vip-serve').click(function(event) { $('.vip-serve').click(function(event) {
// layer.open({ layer.open({
// type: 2, type: 2,
// title: '', title: '',
// area: ['840px', '80%'], area: ['840px', '80%'],
// content: `${config.api_url}/api/vip_rights?domain=${config.app_url}&developer_token={{ system_setting('base.developer_token') }}`, content: `${config.api_url}/api/vip_rights?domain=${config.app_url}&developer_token={{ system_setting('base.developer_token') }}`,
// }); });
// }); });
</script> </script>
@endpush @endpush

View File

@ -22,7 +22,7 @@
<link rel="shortcut icon" href="{{ image_origin(system_setting('base.favicon')) }}"> <link rel="shortcut icon" href="{{ image_origin(system_setting('base.favicon')) }}">
<link href="{{ mix('build/beike/admin/css/app.css') }}" rel="stylesheet"> <link href="{{ mix('build/beike/admin/css/app.css') }}" rel="stylesheet">
<script src="{{ mix('build/beike/admin/js/app.js') }}"></script> <script src="{{ mix('build/beike/admin/js/app.js') }}"></script>
<title>BeikeShop - @yield('title')</title> <title>万有引力 - @yield('title')</title>
@stack('header') @stack('header')
{{-- <x-analytics /> --}} {{-- <x-analytics /> --}}
</head> </head>
@ -43,7 +43,8 @@
@yield('content') @yield('content')
<p class="text-center text-secondary mt-5"> <p class="text-center text-secondary mt-5">
<a href="https://beikeshop.com/" class="ms-2" target="_blank">BeikeShop</a> {{-- <a href="https://beikeshop.com/" class="ms-2" target="_blank">BeikeShop</a>--}}
<a href="#" class="ms-2">万有引力</a>
v{{ config('beike.version') }}({{ config('beike.build') }}) v{{ config('beike.version') }}({{ config('beike.build') }})
&copy; {{ date('Y') }} All Rights Reserved</p> &copy; {{ date('Y') }} All Rights Reserved</p>
</div> </div>

View File

@ -179,7 +179,7 @@
cancelButtonText: '{{__('common.cancel')}}', cancelButtonText: '{{__('common.cancel')}}',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
$http.delete(`attributes/{{ $attribute['id'] }}/values/${id}`).then((res) => { $http.delete(`customers/{{ $attribute['id'] }}/addresses/${id}`).then((res) => {
this.$message.success(res.message); this.$message.success(res.message);
this.source.attributeValues.splice(index, 1) this.source.attributeValues.splice(index, 1)
}) })

View File

@ -2,6 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<!-- Google tag (gtag.js) --> <script async src="https://www.googletagmanager.com/gtag/js?id=AW-11146062373"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'AW-11146062373'); </script>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">

View File

@ -2,6 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<!-- Google tag (gtag.js) --> <script async src="https://www.googletagmanager.com/gtag/js?id=AW-11146062373"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'AW-11146062373'); </script>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" <meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
@ -20,9 +21,6 @@
<script src="{{ asset('vendor/tinymce/5.9.1/tinymce.min.js') }}"></script> <script src="{{ asset('vendor/tinymce/5.9.1/tinymce.min.js') }}"></script>
<script src="{{ asset('vendor/element-ui/2.15.6/js.js') }}"></script> <script src="{{ asset('vendor/element-ui/2.15.6/js.js') }}"></script>
<link rel="stylesheet" href="{{ asset('vendor/element-ui/2.15.6/css.css') }}"> <link rel="stylesheet" href="{{ asset('vendor/element-ui/2.15.6/css.css') }}">
@if (locale() != 'zh_cn')
<script src="{{ asset('vendor/element-ui/language/' . locale() . '.js') }}"></script>
@endif
<link rel="stylesheet" type="text/css" href="{{ asset('/build/beike/admin/css/design.css') }}"> <link rel="stylesheet" type="text/css" href="{{ asset('/build/beike/admin/css/design.css') }}">
@stack('header') @stack('header')
<script> <script>

View File

@ -0,0 +1,212 @@
@extends('admin::layouts.master')
@section('title', __('admin/page.index'))
@section('body-class', 'page-pages-form')
@push('header')
<script src="{{ asset('vendor/vue/Sortable.min.js') }}"></script>
<script src="{{ asset('vendor/vue/vuedraggable.js') }}"></script>
<script src="{{ asset('vendor/tinymce/5.9.1/tinymce.min.js') }}"></script>
@endpush
@section('page-title-right')
<x-admin::form.row title="">
<button type="button" class="mt-3 btn btn-primary submit-form">{{ __('common.save') }}</button>
</x-admin::form.row>
@endsection
@section('content')
<ul class="nav nav-tabs nav-bordered mb-3" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#tab-content" type="button" >{{ __('admin/product.basic_information') }}</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-data" type="button">{{ __('common.data') }}</button>
</li>
</ul>
<div id="app" class="card h-min-600">
<div class="card-body">
<form novalidate class="needs-validation" action="{{ $page->id ? admin_route('pages.update', [$page->id]) : admin_route('pages.store') }}" method="POST">
<div class="tab-content">
<div class="tab-pane fade show active" id="tab-content">
@csrf
@method($page->id ? 'PUT' : 'POST')
<ul class="nav nav-tabs mb-3" role="tablist">
@foreach (locales() as $language)
<li class="nav-item" role="presentation">
<button class="nav-link {{ $loop->first ? 'active' : '' }}" data-bs-toggle="tab" data-bs-target="#tab-{{ $language['code'] }}" type="button" >{{ $language['name'] }}</button>
</li>
@endforeach
</ul>
<div class="tab-content">
@foreach (locales() as $language)
<div class="tab-pane fade {{ $loop->first ? 'show active' : '' }}" id="tab-{{ $language['code'] }}">
@php
$error_title = $errors->first("descriptions.{$language['code']}.title");
@endphp
<x-admin-form-input
error="{{ $error_title }}"
name="descriptions[{{ $language['code'] }}][title]"
title="{{ __('admin/page.info_title') }}"
:required="true"
value="{{ old('descriptions.' . $language['code'] . '.title', $descriptions[$language['code']]['title'] ?? '') }}"
/>
<x-admin::form.row title="{{ __('page_category.text_summary') }}">
<div class="input-group w-max-400">
<textarea rows="3" type="text" name="descriptions[{{ $language['code'] }}][summary]" class="form-control wp-400" placeholder="{{ __('page_category.text_summary') }}">{{ old('descriptions.' . $language['code'] . '.summary', $descriptions[$language['code']]['summary'] ?? '') }}</textarea>
</div>
</x-admin::form.row>
<x-admin::form.row title="{{ __('admin/page.info_content') }}">
<div class="w-max-1000">
<textarea name="descriptions[{{ $language['code'] }}][content]" data-tinymce-height="600" class="form-control tinymce">
{{ old('descriptions.' . $language['code'] . '.content', $descriptions[$language['code']]['content'] ?? '') }}
</textarea>
</div>
@if ($errors->has("descriptions.{$language['code']}.content"))
<span class="invalid-feedback d-block" role="alert">{{ $errors->first("descriptions.{$language['code']}.content") }}</span>
@endif
</x-admin::form.row>
<input type="hidden" name="descriptions[{{ $language['code'] }}][locale]" value="{{ $language['code'] }}">
<x-admin-form-input name="descriptions[{{ $language['code'] }}][meta_title]" title="{{ __('admin/setting.meta_title') }}" value="{{ old('descriptions.' . $language['code'] . '.meta_title', $descriptions[$language['code']]['meta_title'] ?? '') }}" />
<x-admin-form-input name="descriptions[{{ $language['code'] }}][meta_description]" title="{{ __('admin/setting.meta_description') }}" value="{{ old('descriptions.' . $language['code'] . '.meta_description', $descriptions[$language['code']]['meta_description'] ?? '') }}" />
<x-admin-form-input name="descriptions[{{ $language['code'] }}][meta_keywords]" title="{{ __('admin/setting.meta_keywords') }}" value="{{ old('descriptions.' . $language['code'] . '.meta_keywords', $descriptions[$language['code']]['meta_keywords'] ?? '') }}" />
</div>
@endforeach
</div>
</div>
<div class="tab-pane fade" id="tab-data">
@hook('admin.page.data.before')
<x-admin-form-input name="author" title="{{ __('page_category.author') }}" value="{{ old('author', $page->author ?? '') }}" />
<x-admin::form.row title="{{ __('admin/page_category.index') }}">
<div class="wp-400">
<el-autocomplete
v-model="page_category_name"
value-key="name"
size="small"
name="category_name"
class="w-100"
:fetch-suggestions="(keyword, cb) => {relationsQuerySearch(keyword, cb, 'page_categories')}"
placeholder="{{ __('common.input') }}"
@select="(e) => {handleSelect(e, 'page_categories')}"
></el-autocomplete>
<input type="hidden" name="page_category_id" :value="page_category_name ? page_category_id : ''" />
</div>
</x-admin::form.row>
<x-admin-form-input name="views" title="{{ __('page_category.views') }}" value="{{ old('views', $page->views ?? '') }}" />
<x-admin::form.row title="{{ __('admin/product.product_relations') }}">
<div class="module-edit-group wp-600">
<div class="autocomplete-group-wrapper">
<el-autocomplete
class="inline-input"
v-model="relations.keyword"
value-key="name"
size="small"
:fetch-suggestions="(keyword, cb) => {relationsQuerySearch(keyword, cb, 'products')}"
placeholder="{{ __('admin/builder.modules_keywords_search') }}"
@select="(e) => {handleSelect(e, 'product_relations')}"
></el-autocomplete>
<div class="item-group-wrapper" v-loading="relations.loading">
<template v-if="relations.products.length">
<draggable
ghost-class="dragabble-ghost"
:list="relations.products"
:options="{animation: 330}"
>
<div v-for="(item, index) in relations.products" :key="index" class="item">
<div>
<i class="el-icon-s-unfold"></i>
<span>@{{ item.name }}</span>
</div>
<i class="el-icon-delete right" @click="relationsRemoveProduct(index)"></i>
<input type="text" :name="'products['+ index +']'" v-model="item.id" class="form-control d-none">
</div>
</draggable>
</template>
<template v-else>{{ __('admin/builder.modules_please_products') }}</template>
</div>
</div>
</div>
</x-admin::form.row>
@hook('admin.page.data.after')
<x-admin-form-switch name="active" title="{{ __('common.status') }}" value="{{ old('active', $page->active ?? 1) }}" />
</div>
</div>
<button type="submit" class="d-none">{{ __('common.save') }}</button>
</form>
</div>
</div>
@hook('admin.page.form.footer')
@endsection
@push('footer')
<script>
$('.submit-form').click(function () {
$('.needs-validation').find('button[type="submit"]').click()
})
var app = new Vue({
el: '#app',
data: {
relations: {
keyword: '',
products: [],
loading: null,
},
page_category_name: '{{ old('category_name', $page->category->description->title ?? '') }}',
page_category_id: '{{ old('categories_id', $page->category->id ?? '') }}',
},
created() {
const products = @json($page['products'] ?? []);
if (products.length) {
this.relations.products = products.map(v => {
return {
id: v.id,
name: v.description.name,
}
})
}
},
methods: {
relationsQuerySearch(keyword, cb, url) {
$http.get(url + '/autocomplete?name=' + encodeURIComponent(keyword), null, {hload:true}).then((res) => {
cb(res.data);
})
},
handleSelect(item, key) {
if (key == 'product_relations') {
if (!this.relations.products.find(v => v == item.id)) {
this.relations.products.push(item);
}
this.relations.keyword = ""
} else {
this.page_category_name = item.name
this.page_category_id = item.id
}
},
relationsRemoveProduct(index) {
this.relations.products.splice(index, 1);
},
}
})
</script>
@endpush

View File

@ -0,0 +1,256 @@
@extends('admin::layouts.master')
@section('title', __('admin/page.inquiry'))
@section('content')
@if ($errors->has('error'))
<x-admin-alert type="danger" msg="{{ $errors->first('error') }}" class="mt-4" />
@endif
<div id="tax-classes-app" class="card">
<div class="card-body h-min-600">
{{-- <div class="d-flex justify-content-between mb-4">--}}
{{-- <a href="{{ admin_route('pages.create') }}" class="btn btn-primary">{{ __('common.add') }}</a>--}}
{{-- </div>--}}
<div class="table-push">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>{{ __('admin/product.products_img') }}</th>
<th>{{ __('admin/product.products_name') }}-{{ __('common.sku') }}</th>
<th>{{ __('common.contacts') }}</th>
<th>{{ __('common.email') }}</th>
<th>{{ __('common.content') }}</th>
<th>{{ __('common.created_at') }}</th>
<th>{{ __('common.updated_at') }}</th>
@hook('admin.page.list.column')
<th class="text-end">{{ __('common.action') }}</th>
</tr>
</thead>
<tbody>
@if (count($pages_format))
@foreach ($pages_format as $pageKey=>$page)
<tr>
<td>{{ $page['id'] }}</td>
<td>
<div class="wh-60 border d-flex justify-content-between align-items-center"><img src="{{ $page['product_images'][0]['preview'] ?? 'image/placeholder.png' }}" class="img-fluid"></div>
</td>
<td>
<div ><a class="text-dark" href="{{ shop_route('products.show', $page['product_id']) }}" target="_blank">{{ $page['product_name'] ?? '' }}-{{ $page['product_sku_sku'] ?? '' }}</a></div>
</td>
<td>
<div title="">{{ $page['contacts'] ?? '' }}</div><!-- -->
</td>
{{-- <td class="{{ $page['active'] ? 'text-success' : 'text-secondary' }}">--}}
{{-- {{ $page['active'] ? __('common.enable') : __('common.disable') }}--}}
{{-- </td>--}}
<td>
<div title="">{{ $page['email'] ?? '' }}</div>
</td>
<td>
<div title="">{{ $page['content'] ?? '' }}</div>
</td>
<td>{{ $page['created_at'] }}</td>
<td>{{ $page['updated_at'] }}</td>
@hook('admin.page.list.column_value')
<td class="text-end">
<button @click="checkedCreate('edit', {{$pageKey}})"
class="btn btn-outline-secondary btn-sm">{{ __('common.view') }}</button>
<button class="btn btn-outline-danger btn-sm delete-btn" type='button'
data-id="{{ $page['id'] }}">{{ __('common.delete') }}</button>
@hook('admin.page.list.action')
</td>
</tr>
@endforeach
@else
<tr><td colspan="5" class="border-0"><x-admin-no-data /></td></tr>
@endif
</tbody>
</table>
</div>
{{ $pages->links('admin::vendor/pagination/bootstrap-4') }}
</div>
<el-dialog title="{{ __('admin/page.inquiry') }}" :visible.sync="dialog.show" width="870px"
@close="closeCustomersDialog('form')" :close-on-click-modal="false">
<el-descriptions class="margin-top" :column="2" :size="size" border>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-user"></i>
{{ __('admin/product.products_img') }}
</template>
<div class="wh-100 border d-flex justify-content-between align-items-center">
<img :src="dialog.form.product_images.length !== 0 ? dialog.form.product_images[0].preview : 'image/placeholder.png'" class="img-fluid">
</div>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-user"></i>
{{ __('admin/product.products_name') }}-{{ __('common.sku') }}
</template>
<a class="text-dark" :href="'/products/' + dialog.form.product_id" target="_blank">@{{ dialog.form.product_name }}-@{{ dialog.form.product_sku_sku}}</a>
</el-descriptions-item>
</el-descriptions>
<el-descriptions class="margin-top" :column="2" :size="size" border>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-tickets"></i>
{{ __('common.contacts') }}
</template>
@{{ dialog.form.contacts }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-mobile-phone"></i>
{{ __('common.email') }}
</template>
@{{ dialog.form.email }}
</el-descriptions-item>
</el-descriptions>
<el-descriptions class="margin-top" :column="1" :size="size" border>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-tickets"></i>
{{ __('common.content') }}
</template>
@{{ dialog.form.content }}
</el-descriptions-item>
</el-descriptions>
<el-descriptions class="margin-top" :column="2" :size="size" border>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-location-outline"></i>
{{ __('common.created_at') }}
</template>
@{{ dialog.form.created_at }}
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-location-outline"></i>
{{ __('common.updated_at') }}
</template>
@{{ dialog.form.updated_at }}
</el-descriptions-item>
</el-descriptions>
</el-dialog>
</div>
@hook('admin.page.list.content.footer')
@endsection
@push('footer')
<script>
new Vue({
el: '#tax-classes-app',
data: {
size: '',
currencies: @json($pages_format ?? []),
dialog: {
show: false,
index: null,
type: 'add',
form: {
product_images: [],
product_id: '',
product_name: '',
product_sku_sku: '',
contacts: '',
email: '',
content: '',
created_at: '',
updated_at: '',
},
},
rules: {
name: [{required: true,message: '{{ __('common.error_required', ['name' => __('common.name')]) }}', trigger: 'blur'}, ],
code: [{required: true,message: '{{ __('common.error_required', ['name' => __('currency.code')]) }}', trigger: 'blur'}, ],
value: [{required: true,message: '{{ __('common.error_required', ['name' => __('currency.value')]) }}',trigger: 'blur'}, ],
decimal_place: [{required: true,message: '{{ __('common.error_required', ['name' => __('currency.decimal_place')]) }}',trigger: 'blur'}, ],
}
},
methods: {
checkedCreate(type, index) {
this.dialog.show = true
this.dialog.type = type
this.dialog.index = index
if (type == 'edit') {
this.dialog.form = JSON.parse(JSON.stringify(this.currencies[index]))
}
},
addFormSubmit(form) {
const self = this;
const type = this.dialog.type == 'add' ? 'post' : 'put';
const url = this.dialog.type == 'add' ? 'currencies' : 'currencies/' + this.dialog.form.id;
this.$refs[form].validate((valid) => {
if (!valid) {
this.$message.error('{{ __('common.error_form') }}');
return;
}
$http[type](url, this.dialog.form).then((res) => {
this.$message.success(res.message);
if (this.dialog.type == 'add') {
this.currencies.push(res.data)
} else {
this.currencies[this.dialog.index] = res.data
}
this.dialog.show = false
})
});
},
deleteCustomer(id, index) {
const self = this;
this.$confirm('{{ __('common.confirm_delete') }}', '{{ __('common.text_hint') }}', {
confirmButtonText: '{{ __('common.confirm') }}',
cancelButtonText: '{{ __('common.cancel') }}',
type: 'warning'
}).then(() => {
$http.delete('currencies/' + id).then((res) => {
this.$message.success(res.message);
self.currencies.splice(index, 1)
})
}).catch(()=>{})
},
closeCustomersDialog(form) {
// this.$refs[form].resetFields();
Object.keys(this.dialog.form).forEach(key => this.dialog.form[key] = '')
// this.dialog.show = false
}
}
})
$('.delete-btn').click(function(event) {
const id = $(this).data('id');
const self = $(this);
layer.confirm('{{ __('common.confirm_delete') }}', {
title: "{{ __('common.text_hint') }}",
btn: ['{{ __('common.cancel') }}', '{{ __('common.confirm') }}'],
area: ['400px'],
btn2: () => {
$http.delete(`inquiry/${id}`).then((res) => {
layer.msg(res.message);
window.location.reload();
})
}
})
});
</script>
@endpush

View File

@ -43,24 +43,18 @@
</div> </div>
<div class="mb-4"> <div class="mb-4">
@if ($data['available']) @if ($data['downloadable'])
@if ($data['downloadable']) <button class="btn btn-primary btn-lg" @click="downloadPlugin"><i class="bi bi-cloud-arrow-down-fill"></i> {{ __('admin/marketing.download_plugin') }}</button>
<button class="btn btn-primary btn-lg" @click="downloadPlugin"><i class="bi bi-cloud-arrow-down-fill"></i> {{ __('admin/marketing.download_plugin') }}</button> <div class="mt-3 d-none download-help"><a href="{{ admin_route('plugins.index') }}" class=""><i class="bi bi-cursor-fill"></i> <span></span></a></div>
<div class="mt-3 d-none download-help"><a href="{{ admin_route('plugins.index') }}" class=""><i class="bi bi-cursor-fill"></i> <span></span></a></div>
@else
<div class="mb-2 fw-bold">{{ __('admin/marketing.select_pay') }}</div>
<div class="mb-4">
<el-radio-group v-model="payCode" size="small" class="radio-group">
<el-radio class="rounded-0 me-1" label="wechatpay" border><img src="{{ asset('image/wechat.png') }}" class="img-fluid"></el-radio>
<el-radio class="rounded-0" label="alipay" border><img src="{{ asset('image/alipay.png') }}" class="img-fluid"></el-radio>
</el-radio-group>
</div>
<button class="btn btn-primary btn-lg" @click="marketingBuy">{{ __('admin/marketing.btn_buy') }} ({{ $data['price_format'] }})</button>
@endif
@else @else
<div class="alert alert-warning" role="alert"> <div class="mb-2 fw-bold">{{ __('admin/marketing.select_pay') }}</div>
{!! __('admin/marketing.version_compatible_text') !!} <div class="mb-4">
<el-radio-group v-model="payCode" size="small" class="radio-group">
<el-radio class="rounded-0 me-1" label="wechatpay" border><img src="{{ asset('image/wechat.png') }}" class="img-fluid"></el-radio>
<el-radio class="rounded-0" label="alipay" border><img src="{{ asset('image/alipay.png') }}" class="img-fluid"></el-radio>
</el-radio-group>
</div> </div>
<button class="btn btn-primary btn-lg" @click="marketingBuy">{{ __('admin/marketing.btn_buy') }} ({{ $data['price_format'] }})</button>
@endif @endif
</div> </div>
</div> </div>

View File

@ -84,25 +84,11 @@
}, },
installedPlugin(code, type, index) { installedPlugin(code, type, index) {
if (type == 'uninstall') { const self = this;
layer.confirm('{{ __('admin/plugin.uninstall_hint') }}', {
title: "{{ __('common.text_hint') }}",
btn: ['{{ __('common.cancel') }}', '{{ __('common.confirm') }}'],
area: ['400px'],
btn2: () => {
this.installedPluginXhr(code, type, index);
}
})
return;
}
this.installedPluginXhr(code, type, index);
},
installedPluginXhr(code, type, index) {
$http.post(`plugins/${code}/${type}`).then((res) => { $http.post(`plugins/${code}/${type}`).then((res) => {
layer.msg(res.message) layer.msg(res.message)
this.plugins[index].installed = type == 'install' ? true : false; self.plugins[index].installed = type == 'install' ? true : false;
}) })
} }
} }

View File

@ -107,12 +107,89 @@
<div> <div>
<h5 class="border-bottom pb-3 mb-4">{{ __('admin/product.stocks') }}</h5> <h5 class="border-bottom pb-3 mb-4">{{ __('admin/product.stocks') }}</h5>
<x-admin::form.row title="{{ __('admin/product.price_setting_by.num') }}">
<div class="mb-1 mt-2">
<div class="form-check form-check-inline">
<input v-model="form.price_setting" class="form-check-input" id="price_setting-sku" type="radio" name="price_setting" id="price_setting-sku" value="sku" {{ $product->price_setting == 'sku' ? 'checked' : '' }}>
<label class="form-check-label" for="price_setting-sku">{{ __('common.disable') }}</label>
</div>
<div class="form-check form-check-inline">
<input v-model="form.price_setting" class="form-check-input" id="price_setting-num" type="radio" name="price_setting" id="price_setting-num" value="num" {{ $product->price_setting == 'num' ? 'checked' : '' }}>
<label class="form-check-label" for="price_setting-num">{{ __('common.enable') }}</label>
</div>
</div>
</x-admin::form.row>
<span v-if="form.price_setting === 'num'">
{{-- 阶梯价格设置表格 (最小起订量,产品价格),预览,可以增加删除价格区间--}}
<div class="price_setting_by_num">
<div class="left">
<div class="head">
<div class="num" >最小起订量</div>
<div class="price">产品价格</div>
<div class="delete">删除</div>
</div>
<div class="body">
<div class="item" v-for="(item,index) in form.numPrices">
<div class="num">
<div class="top">
<div class="title"></div>
<input type="number" :name="'numPrices[' + index + '][num]'" v-model="item.num" value="" placeholder="数量" required="required" class="form-control wp-100">
</div>
<div class="tip" ><span style="visibility: hidden;">_</span><span v-if="item.num">@{{ item.num }}</span></div>
</div>
<div class="price">
<input type="number" :name="'numPrices[' + index + '][price]'" v-model="item.price" value="" placeholder="价格" required="required" step="any" class="form-control wp-100">
<div class="tip" v-if="item.price">@{{ item.num }}件等于价格:@{{ item.num * item.price }}</div>
</div>
<div class="delete" @click="removeNumPrices(index)"><a style="cursor: pointer;color: #0072ff;">删除</a></div>
</div>
</div>
<div class="bottom">
<a style="cursor: pointer;color: #0072ff;" @click="addNumPrices"> {{ __('admin/product.add_variable') }}</a>
</div>
</div>
<div class="right">
<div class="head">
预览
</div>
<div class="body">
<div class="item" v-for="(item,index) in form.numPrices">
<div class="num" v-if="index < form.numPrices.length - 1">@{{ item.num }} ~ @{{ form.numPrices[index + 1].num - 1 }}</div>
<div class="num" v-else-if="item.num">@{{ item.num }}</div>
<div class="price" v-if="item.price">价格@{{ item.price }}</div>
</div>
</div>
</div>
</div>
<input class="form-control d-none" :value="numPricesIsEmpty" required>
<div class="invalid-feedback" style="font-size: 16px;margin-left: 200px;"><i class="bi bi-exclamation-circle-fill"></i> 新增价格区间</div>
<template>
{{-- <input type="hidden" value="{{ old('skus.0.image', $product->skus[0]->image ?? '') }}" name="skus[0][image]">--}}
{{-- <x-admin-form-input name="skus[0][model]" :title="__('admin/product.model')" :value="old('skus.0.model', $product->skus[0]->model ?? '')" />--}}
{{-- <x-admin-form-input name="skus[0][sku]" title="sku" :value="old('skus.0.sku', $product->skus[0]->sku ?? '')" required />--}}
{{-- <x-admin-form-input name="skus[0][price]" type="number" :title="__('admin/product.price')" :value="old('skus.0.price', $product->skus[0]->price ?? '')" step="any" required />--}}
{{-- --}}{{-- <x-admin-form-input name="skus[0][origin_price]" type="number" :title="__('admin/product.origin_price')" :value="old('skus.0.origin_price', $product->skus[0]->origin_price ?? '')" step="any" required />--}}
{{-- <x-admin-form-input name="skus[0][cost_price]" type="number" :title="__('admin/product.cost_price')" :value="old('skus.0.cost_price', $product->skus[0]->cost_price ?? '')" />--}}
{{-- <x-admin-form-input name="skus[0][quantity]" type="number" :title="__('admin/product.quantity')" :value="old('skus.0.quantity', $product->skus[0]->quantity ?? '')" />--}}
{{-- <input type="hidden" name="skus[0][price]" placeholder="variants" :value="form.numPrices.length !== 0 ? form.numPrices[0].price : ''">--}}
{{-- <input type="hidden" name="skus[0][origin_price]" placeholder="position" :value="form.numPrices.length !== 0 ? form.numPrices[form.numPrices.length - 1].price : ''">--}}
{{-- <input type="hidden" name="skus[0][variants]" placeholder="variants" value="">--}}
{{-- <input type="hidden" name="skus[0][position]" placeholder="position" value="0">--}}
{{-- <input type="hidden" name="skus[0][is_default]" placeholder="is_default" value="1">--}}
</template>
</span>
<input type="hidden" name="variables" :value="JSON.stringify(form.variables)">
<input type="hidden" name="numPrices" value="" v-if="form.price_setting === 'sku'">
<x-admin::form.row :title="__('admin/product.enable_multi_spec')"> <x-admin::form.row :title="__('admin/product.enable_multi_spec')">
<el-switch v-model="editing.isVariable" @change="isVariableChange" class="mt-2"></el-switch> <el-switch v-model="editing.isVariable" @change="isVariableChange" class="mt-2"></el-switch>
</x-admin::form.row> </x-admin::form.row>
<input type="hidden" name="variables" :value="JSON.stringify(form.variables)">
<div class="row g-3 mb-3" v-if="editing.isVariable"> <div class="row g-3 mb-3" v-if="editing.isVariable">
<label for="" class="wp-200 col-form-label text-end"></label> <label for="" class="wp-200 col-form-label text-end"></label>
<div class="col-auto wp-200-"> <div class="col-auto wp-200-">
@ -190,8 +267,8 @@
</div> </div>
<input type="text" class="form-control me-2 bg-white" v-model="variablesBatch.model" placeholder="{{ __('admin/product.model') }}"> <input type="text" class="form-control me-2 bg-white" v-model="variablesBatch.model" placeholder="{{ __('admin/product.model') }}">
<input type="text" class="form-control me-2 bg-white" v-model="variablesBatch.sku" placeholder="sku"> <input type="text" class="form-control me-2 bg-white" v-model="variablesBatch.sku" placeholder="sku">
<input type="number" class="form-control me-2 bg-white" v-model="variablesBatch.price" placeholder="{{ __('admin/product.price') }}"> <input type="number" v-if="form.price_setting === 'sku'" class="form-control me-2 bg-white" v-model="variablesBatch.price" placeholder="{{ __('admin/product.price') }}">
<input type="number" class="form-control me-2 bg-white" v-model="variablesBatch.origin_price" placeholder="{{ __('admin/product.origin_price') }}"> <input type="number" v-if="form.price_setting === 'sku'" class="form-control me-2 bg-white" v-model="variablesBatch.origin_price" placeholder="{{ __('admin/product.origin_price') }}">
<input type="number" class="form-control me-2 bg-white" v-model="variablesBatch.cost_price" placeholder="{{ __('admin/product.cost_price') }}"> <input type="number" class="form-control me-2 bg-white" v-model="variablesBatch.cost_price" placeholder="{{ __('admin/product.cost_price') }}">
<input type="number" class="form-control me-2 bg-white" v-model="variablesBatch.quantity" placeholder="{{ __('admin/product.quantity') }}"> <input type="number" class="form-control me-2 bg-white" v-model="variablesBatch.quantity" placeholder="{{ __('admin/product.quantity') }}">
<button type="button" class="btn btn-primary text-nowrap" @click="batchSettingVariant">{{ __('common.batch_setting') }}</button> <button type="button" class="btn btn-primary text-nowrap" @click="batchSettingVariant">{{ __('common.batch_setting') }}</button>
@ -205,8 +282,8 @@
<th width="106px">{{ __('common.image') }}</th> <th width="106px">{{ __('common.image') }}</th>
<th class="w-min-100">{{ __('admin/product.model') }}</th> <th class="w-min-100">{{ __('admin/product.model') }}</th>
<th class="w-min-100">sku</th> <th class="w-min-100">sku</th>
<th class="w-min-100">{{ __('admin/product.price') }}</th> <th v-if="form.price_setting === 'sku'" class="w-min-100">{{ __('admin/product.price') }}</th>
<th class="w-min-100">{{ __('admin/product.origin_price') }}</th> <th v-if="form.price_setting === 'sku'" class="w-min-100">{{ __('admin/product.origin_price') }}</th>
<th class="w-min-100">{{ __('admin/product.cost_price') }}</th> <th class="w-min-100">{{ __('admin/product.cost_price') }}</th>
<th class="w-min-100">{{ __('admin/product.quantity') }}</th> <th class="w-min-100">{{ __('admin/product.quantity') }}</th>
</thead> </thead>
@ -241,15 +318,19 @@
<span role="alert" class="invalid-feedback">{{ __('common.error_required', ['name' => 'sku']) }}</span> <span role="alert" class="invalid-feedback">{{ __('common.error_required', ['name' => 'sku']) }}</span>
<span v-if="sku.is_default" class="text-success">{{ __('admin/product.default_main_product') }}</span> <span v-if="sku.is_default" class="text-success">{{ __('admin/product.default_main_product') }}</span>
</td> </td>
<td> <td v-if="form.price_setting === 'sku'">
<input type="number" class="form-control" v-model="sku.price" :name="'skus[' + skuIndex + '][price]'" step="any" <input type="number" class="form-control" v-model="sku.price" :name="'skus[' + skuIndex + '][price]'" step="any"
placeholder="{{ __('admin/product.price') }}" required> placeholder="{{ __('admin/product.price') }}" required>
<span role="alert" class="invalid-feedback">{{ __('common.error_required', ['name' => __('admin/product.price')]) }}</span> <span role="alert" class="invalid-feedback">{{ __('common.error_required', ['name' => __('admin/product.price')]) }}</span>
</td> </td>
<td><input type="number" class="form-control" v-model="sku.origin_price" :name="'skus[' + skuIndex + '][origin_price]'" step="any" <td v-if="form.price_setting === 'sku'">
placeholder="{{ __('admin/product.origin_price') }}" required> <input type="number" class="form-control" v-model="sku.origin_price" :name="'skus[' + skuIndex + '][origin_price]'" step="any"
<span role="alert" class="invalid-feedback">{{ __('common.error_required', ['name' => __('admin/product.origin_price')]) }}</span> placeholder="{{ __('admin/product.origin_price') }}" required>
</td> <span role="alert" class="invalid-feedback">{{ __('common.error_required', ['name' => __('admin/product.origin_price')]) }}</span>
</td>
<input type="hidden" :name="'skus[' + skuIndex + '][price]'" v-if="form.price_setting === 'num'" placeholder="variants" :value="form.numPrices.length !== 0 ? form.numPrices[0].price : ''">
<input type="hidden" :name="'skus[' + skuIndex + '][origin_price]'" v-if="form.price_setting === 'num'" placeholder="position" :value="form.numPrices.length !== 0 ? form.numPrices[form.numPrices.length - 1].price : ''">
<td><input type="number" class="form-control" v-model="sku.cost_price" :name="'skus[' + skuIndex + '][cost_price]'" <td><input type="number" class="form-control" v-model="sku.cost_price" :name="'skus[' + skuIndex + '][cost_price]'"
placeholder="{{ __('admin/product.cost_price') }}"> placeholder="{{ __('admin/product.cost_price') }}">
</td> </td>
@ -269,10 +350,14 @@
<input type="hidden" value="{{ old('skus.0.image', $product->skus[0]->image ?? '') }}" name="skus[0][image]"> <input type="hidden" value="{{ old('skus.0.image', $product->skus[0]->image ?? '') }}" name="skus[0][image]">
<x-admin-form-input name="skus[0][model]" :title="__('admin/product.model')" :value="old('skus.0.model', $product->skus[0]->model ?? '')" /> <x-admin-form-input name="skus[0][model]" :title="__('admin/product.model')" :value="old('skus.0.model', $product->skus[0]->model ?? '')" />
<x-admin-form-input name="skus[0][sku]" title="sku" :value="old('skus.0.sku', $product->skus[0]->sku ?? '')" required /> <x-admin-form-input name="skus[0][sku]" title="sku" :value="old('skus.0.sku', $product->skus[0]->sku ?? '')" required />
<x-admin-form-input name="skus[0][price]" type="number" :title="__('admin/product.price')" :value="old('skus.0.price', $product->skus[0]->price ?? '')" step="any" required /> <span v-if="form.price_setting === 'sku'">
<x-admin-form-input name="skus[0][origin_price]" type="number" :title="__('admin/product.origin_price')" :value="old('skus.0.origin_price', $product->skus[0]->origin_price ?? '')" step="any" required /> <x-admin-form-input name="skus[0][price]" type="number" :title="__('admin/product.price')" :value="old('skus.0.price', $product->skus[0]->price ?? '')" step="any" required />
<x-admin-form-input name="skus[0][origin_price]" type="number" :title="__('admin/product.origin_price')" :value="old('skus.0.origin_price', $product->skus[0]->origin_price ?? '')" step="any" required />
</span>
<x-admin-form-input name="skus[0][cost_price]" type="number" :title="__('admin/product.cost_price')" :value="old('skus.0.cost_price', $product->skus[0]->cost_price ?? '')" /> <x-admin-form-input name="skus[0][cost_price]" type="number" :title="__('admin/product.cost_price')" :value="old('skus.0.cost_price', $product->skus[0]->cost_price ?? '')" />
<x-admin-form-input name="skus[0][quantity]" type="number" :title="__('admin/product.quantity')" :value="old('skus.0.quantity', $product->skus[0]->quantity ?? '')" /> <x-admin-form-input name="skus[0][quantity]" type="number" :title="__('admin/product.quantity')" :value="old('skus.0.quantity', $product->skus[0]->quantity ?? '')" />
<input type="hidden" name="skus[0][price]" v-if="form.price_setting === 'num'" placeholder="variants" :value="form.numPrices.length !== 0 ? form.numPrices[0].price : ''">
<input type="hidden" name="skus[0][origin_price]" v-if="form.price_setting === 'num'" placeholder="position" :value="form.numPrices.length !== 0 ? form.numPrices[form.numPrices.length - 1].price : ''">
<input type="hidden" name="skus[0][variants]" placeholder="variants" value=""> <input type="hidden" name="skus[0][variants]" placeholder="variants" value="">
<input type="hidden" name="skus[0][position]" placeholder="position" value="0"> <input type="hidden" name="skus[0][position]" placeholder="position" value="0">
<input type="hidden" name="skus[0][is_default]" placeholder="is_default" value="1"> <input type="hidden" name="skus[0][is_default]" placeholder="is_default" value="1">
@ -490,6 +575,8 @@
status: @json($product->skus[0]['status'] ?? false), status: @json($product->skus[0]['status'] ?? false),
variables: @json($product->variables ?? []), variables: @json($product->variables ?? []),
skus: @json(old('skus', $product->skus) ?? []), skus: @json(old('skus', $product->skus) ?? []),
price_setting: @json(old('price_setting', $product->price_setting) ?? 'sku'),
numPrices: @json(old('numPrices', $product->numprices) ?? []),
}, },
variablesBatch: { variablesBatch: {
@ -555,6 +642,10 @@
skuIsEmpty() { skuIsEmpty() {
return (this.form.skus.length && this.form.skus[0].variants.length) || '' return (this.form.skus.length && this.form.skus[0].variants.length) || ''
},
numPricesIsEmpty() {
return (this.form.numPrices.length && this.form.price_setting === 'num') || this.form.price_setting === 'sku' || ''
} }
}, },
@ -589,7 +680,21 @@
} }
}, },
created() {
if(this.form.numPrices.length === 0){
this.addNumPrices();
}
},
methods: { methods: {
addNumPrices() {
this.form.numPrices.push({num: '', price: ''});
},
removeNumPrices(index) {
this.form.numPrices.splice(index, 1)
},
// 表单提交,检测是否开启多规格 做处理 // 表单提交,检测是否开启多规格 做处理
productsSubmit() { productsSubmit() {
if (!this.editing.isVariable) { if (!this.editing.isVariable) {
@ -948,5 +1053,104 @@
} }
}); });
}); });
</script> </script lang="scss">
<style>
.price_setting_by_num{
display: flex;
margin-left: 200px;
border: 1px solid #aaaaaa;
border-radius: 15px;
margin-bottom: 20px;
}
.left{
flex: 4;
border-right: 1px solid #aaaaaa;
.head{
display: flex;
border-bottom: 1px solid #aaaaaa;
padding: 8px 0px 8px 30px;
.num{
flex: 4;
}
.price{
flex: 4;
}
.delete{
visibility: hidden;
flex: 1;
}
}
.body{
.item {
display: flex;
border-bottom: 1px solid #aaaaaa;
padding: 20px 0px 20px 30px;
.num {
flex: 4;
.top {
display: flex;
.title {
line-height: 33px;
margin-right: 10px;
}
}
.tip {
font-size: 12px;
color: #aaaaaa;
margin: 10px 0 0 20px;
}
}
.price {
flex: 4;
.tip {
font-size: 12px;
color: #aaaaaa;
margin: 10px 0 0 0;
}
}
.delete {
flex: 1;
}
}
}
.bottom {
padding: 8px 0px 8px 30px;
}
}
.right{
flex: 1;
.head{
border-bottom: 1px solid #aaaaaa;
padding: 8px 0px 8px 10px;
}
.body{
.item{
padding: 8px 0px 8px 10px;
display: flex;
.num{
flex: 1;
}
.price{
flex: 1;
}
}
}
}
}
</style>
@endpush @endpush

View File

@ -15,17 +15,9 @@
justify-content: center; // flex-end | center | space-between justify-content: center; // flex-end | center | space-between
box-shadow: 0 6px 18px rgba(0, 0, 0, .07); box-shadow: 0 6px 18px rgba(0, 0, 0, .07);
margin-bottom: 10px; margin-bottom: 10px;
height: 120px; height: 90px;
overflow: hidden;
border: 1px solid transparent;
transition: all 0.3s ease-in-out;
&:hover { >img {
box-shadow: 0 6px 18px rgba(0, 0, 0, .1);
border: 1px solid $primary;
}
> img {
max-height: 100%; max-height: 100%;
} }
} }

View File

@ -123,7 +123,7 @@
.price-old { .price-old {
color: #aaa; color: #aaa;
margin-left: 4px; margin-left: 4px;
text-decoration: line-through; text-decoration:line-through;
} }
} }
} }

View File

@ -71,6 +71,10 @@ $(function () {
}) })
function updateMiniCartData(res) { function updateMiniCartData(res) {
let cart_item_price = $('.offcanvas-right-cart-item-price');
for(var i = 0 ; i< cart_item_price.length ;i++){
cart_item_price.contents()[i * 3].nodeValue =' ' + res.data.carts[i].price_format + ' x ';
}
$('.offcanvas-right-cart-count').text(res.data.quantity); $('.offcanvas-right-cart-count').text(res.data.quantity);
$('.offcanvas-right-cart-amount').text(res.data.amount_format); $('.offcanvas-right-cart-amount').text(res.data.amount_format);
} }

View File

@ -34,6 +34,8 @@ return [
'customer_group' => 'Customer Groups', 'customer_group' => 'Customer Groups',
'customer' => 'Customers', 'customer' => 'Customers',
'page' => 'Content', 'page' => 'Content',
'inquiry' => 'Inquiry',
'page_category' => 'Page Category', 'page_category' => 'Page Category',
'setting' => 'Settings', 'setting' => 'Settings',
'plugin' => 'Plugin', 'plugin' => 'Plugin',
@ -61,6 +63,8 @@ return [
'regions_index' => 'Regions', 'regions_index' => 'Regions',
'tax_rates_index' => 'Tax Rates', 'tax_rates_index' => 'Tax Rates',
'pages_index' => 'Articles', 'pages_index' => 'Articles',
'inquiry_index' => 'Inquiry',
'page_categories_index' => 'Catalogs', 'page_categories_index' => 'Catalogs',
'tax_classes_index' => 'Tax Classes', 'tax_classes_index' => 'Tax Classes',
'currencies_index' => 'Currencies', 'currencies_index' => 'Currencies',

View File

@ -10,30 +10,28 @@
*/ */
return [ return [
'marketing_list' => 'Marketing', 'marketing_list' => 'Marketing',
'marketing_index' => 'Index', 'marketing_index' => 'Index',
'marketing_show' => 'Detail', 'marketing_show' => 'Detail',
'marketing_buy' => 'Buy', 'marketing_buy' => 'Buy',
'marketing_download' => 'Download', 'marketing_download' => 'Download',
'set_token' => 'Set Token', 'set_token' => 'Set Token',
'get_token_text' => 'Log in to BeikeShop official website personal center - bind domain name, add current domain name', 'get_token_text' => 'Log in to BeikeShop official website personal center - bind domain name, add current domain name',
'get_token' => 'Get Token', 'get_token' => 'Get Token',
'download_count' => 'download count', 'download_count' => 'download count',
'last_update' => 'last update', 'last_update' => 'last update',
'text_version' => 'version', 'text_version' => 'version',
'text_compatibility' => 'compatibility', 'text_compatibility' => 'compatibility',
'text_author' => 'plug-in author', 'text_author' => 'plug-in author',
'download_plugin' => 'download plugin', 'download_plugin' => 'download plugin',
'download_description' => 'Plugin description', 'download_description' => 'Plugin description',
'text_free' => 'free', 'text_free' => 'free',
'btn_buy' => 'Buy', 'btn_buy' => 'Buy',
'text_pay' => 'Payment Amount', 'text_pay' => 'Payment Amount',
'select_pay' => 'select payment method', 'select_pay' => 'select payment method',
'wxpay' => 'WeChat scan code payment!', 'wxpay' => 'WeChat scan code payment!',
'pay_success_title' => 'Payment successful!', 'pay_success_title' => 'Payment successful!',
'pay_success_text' => 'The plug-in purchase is successful, click OK to refresh the page', 'pay_success_text' => 'The plug-in purchase is successful, click OK to refresh the page',
'ali_pay_success' => 'Payment completed? ', 'ali_pay_success' => 'Payment completed? ',
'ali_pay_text' => 'Payment has been completed, please refresh the page', 'ali_pay_text' => 'Payment has been completed, please refresh the page',
'version_compatible_text' => 'This plugin is not compatible with the current system version, please upgrade to <a href="' . config('beike.api_url') . '/download" target="_blank">Latest Version</a>',
'to_update' => 'To Upgrade',
]; ];

View File

@ -19,4 +19,5 @@ return [
'pages_show' => 'Detail', 'pages_show' => 'Detail',
'pages_update' => 'Edit', 'pages_update' => 'Edit',
'pages_delete' => 'Delete', 'pages_delete' => 'Delete',
'inquiry' => 'Inquiry',
]; ];

View File

@ -18,7 +18,6 @@ return [
'plugins_install' => 'Install', 'plugins_install' => 'Install',
'plugins_uninstall' => 'Uninstall', 'plugins_uninstall' => 'Uninstall',
'to_enable' => 'To Enable', 'to_enable' => 'To Enable',
'uninstall_hint' => 'Uninstalling the plug-in will delete all related data of the plug-in, are you sure you want to uninstall? ',
'plugin_list' => 'Plugin List', 'plugin_list' => 'Plugin List',
'plugin_code' => 'Code', 'plugin_code' => 'Code',

View File

@ -11,6 +11,8 @@
return [ return [
'products_index' => 'Index', 'products_index' => 'Index',
'products_name' => 'Products Name',
'products_img' => 'Products Image',
'products_create' => 'Create', 'products_create' => 'Create',
'products_show' => 'Detail', 'products_show' => 'Detail',
'products_update' => 'Edit', 'products_update' => 'Edit',

View File

@ -86,6 +86,10 @@ return [
'error_page' => 'The data you accessed does not exist or has been deleted~', 'error_page' => 'The data you accessed does not exist or has been deleted~',
'error_page_btn' => 'Return to previous page', 'error_page_btn' => 'Return to previous page',
'contacts' => 'Contacts',
'content' => 'Content',
'sku' => 'Sku',
'order' => [ 'order' => [
'unpaid' => 'Unpaid', 'unpaid' => 'Unpaid',
'paid' => 'Paid', 'paid' => 'Paid',

View File

@ -18,4 +18,9 @@ return [
'in_stock' => 'In Stock', 'in_stock' => 'In Stock',
'out_stock' => 'Out Stock', 'out_stock' => 'Out Stock',
'model' => 'Model', 'model' => 'Model',
'quantity_error' => 'Quantity Error',
'inquiry' => 'Inquiry',
'enter_contacts' => 'Please enter contacts',
'enter_email' => 'Please enter email',
'enter_content' => 'Please enter content',
]; ];

View File

@ -18,7 +18,6 @@ return [
'plugins_install' => 'Instalar', 'plugins_install' => 'Instalar',
'plugins_uninstall' => 'desinstalar', 'plugins_uninstall' => 'desinstalar',
'to_enable' => 'To Enable', 'to_enable' => 'To Enable',
'uninstall_hint' => 'Desinstalar el complemento eliminará todos los datos relacionados con el complemento, ¿está seguro de que desea desinstalarlo? ',
'plugin_list' => 'Lista de complementos', 'plugin_list' => 'Lista de complementos',
'plugin_code' => 'código de complemento', 'plugin_code' => 'código de complemento',

View File

@ -18,7 +18,6 @@ return [
'plugins_install' => 'installer', 'plugins_install' => 'installer',
'plugins_uninstall' => 'désinstaller', 'plugins_uninstall' => 'désinstaller',
'to_enable' => 'To Enable', 'to_enable' => 'To Enable',
'uninstall_hint' => 'La désinstallation du plug-in supprimera toutes les données associées au plug-in, êtes-vous sûr de vouloir désinstaller ? ',
'plugin_list' => 'liste des plugins', 'plugin_list' => 'liste des plugins',
'plugin_code' => 'code du plugin', 'plugin_code' => 'code du plugin',

View File

@ -18,7 +18,6 @@ return [
'plugins_install' => 'installa', 'plugins_install' => 'installa',
'plugins_uninstall' => 'disinstalla', 'plugins_uninstall' => 'disinstalla',
'to_enable' => 'To Enable', 'to_enable' => 'To Enable',
'uninstall_hint' => 'La disinstallazione del plug-in eliminerà tutti i relativi dati del plug-in, sei sicuro di voler disinstallare? ',
'plugin_list' => 'elenco dei plugin', 'plugin_list' => 'elenco dei plugin',
'plugin_code' => 'codice plugin', 'plugin_code' => 'codice plugin',

View File

@ -18,7 +18,6 @@ return [
'plugins_install' => 'インストール', 'plugins_install' => 'インストール',
'plugins_uninstall' => 'アンインストール', 'plugins_uninstall' => 'アンインストール',
'to_enable' => '有効にする', 'to_enable' => '有効にする',
'uninstall_hint' => 'プラグインをアンインストールすると、プラグインに関連するすべてのデータが削除されます。本当にアンインストールしますか? ',
'plugin_list' => 'プラグイン リスト', 'plugin_list' => 'プラグイン リスト',
'plugin_code' => 'プラグインコード', 'plugin_code' => 'プラグインコード',

View File

@ -18,7 +18,6 @@ return [
'plugins_install' => 'Установить', 'plugins_install' => 'Установить',
'plugins_uninstall' => 'удалить', 'plugins_uninstall' => 'удалить',
'to_enable' => 'включить', 'to_enable' => 'включить',
'uninstall_hint' => 'Удаление плагина приведет к удалению всех связанных с ним данных. Вы уверены, что хотите удалить? ',
'plugin_list' => 'Список плагинов', 'plugin_list' => 'Список плагинов',
'plugin_code' => 'код плагина', 'plugin_code' => 'код плагина',

View File

@ -34,6 +34,7 @@ return [
'customer_group' => '客户组管理', 'customer_group' => '客户组管理',
'customer' => '客户管理', 'customer' => '客户管理',
'page' => '文章管理', 'page' => '文章管理',
'inquiry' => '询盘管理',
'page_category' => '文章分类', 'page_category' => '文章分类',
'setting' => '系统设置', 'setting' => '系统设置',
'plugin' => '插件管理', 'plugin' => '插件管理',
@ -65,6 +66,7 @@ return [
'languages_index' => '语言管理', 'languages_index' => '语言管理',
'design_index' => '首页装修', 'design_index' => '首页装修',
'pages_index' => '文章管理', 'pages_index' => '文章管理',
'inquiry_index' => '询盘管理',
'page_categories_index' => '文章分类', 'page_categories_index' => '文章分类',
'design_footer_index' => '页尾装修', 'design_footer_index' => '页尾装修',
'design_menu_index' => '导航配置', 'design_menu_index' => '导航配置',

View File

@ -10,6 +10,6 @@
*/ */
return [ return [
'plugins_index' => '登录到 BeikeShop 后台', 'plugins_index' => '登录到 万有引力 后台',
'log_in' => '登录', 'log_in' => '登录',
]; ];

View File

@ -10,31 +10,28 @@
*/ */
return [ return [
'marketing_list' => '插件市场', 'marketing_list' => '插件市场',
'marketing_index' => '市场首页', 'marketing_index' => '市场首页',
'marketing_show' => '插件详情', 'marketing_show' => '插件详情',
'marketing_buy' => '购买插件', 'marketing_buy' => '购买插件',
'marketing_download' => '下载插件', 'marketing_download' => '下载插件',
'set_token' => '设置 Token', 'set_token' => '设置 Token',
'get_token_text' => '登录 BeikeShop 官网个人中心-绑定域名,添加当前域名', 'get_token_text' => '登录 BeikeShop 官网个人中心-绑定域名,添加当前域名',
'get_token' => '点击获取 Token', 'get_token' => '点击获取 Token',
'download_count' => '下载次数', 'download_count' => '下载次数',
'last_update' => '最后更新', 'last_update' => '最后更新',
'text_version' => '版本', 'text_version' => '版本',
'text_compatibility' => '兼容性', 'text_compatibility' => '兼容性',
'text_author' => '插件作者', 'text_author' => '插件作者',
'download_plugin' => '下载插件', 'download_plugin' => '下载插件',
'download_description' => '插件描述', 'download_description' => '插件描述',
'text_free' => '免费', 'text_free' => '免费',
'btn_buy' => '购买', 'btn_buy' => '购买',
'text_pay' => '支付金额', 'text_pay' => '支付金额',
'select_pay' => '选择支付方式', 'select_pay' => '选择支付方式',
'wxpay' => '微信扫码支付!', 'wxpay' => '微信扫码支付!',
'pay_success_title' => '支付成功!', 'pay_success_title' => '支付成功!',
'pay_success_text' => '插件购买成功,点击确定刷新页面', 'pay_success_text' => '插件购买成功,点击确定刷新页面',
'ali_pay_success' => '已完成支付?', 'ali_pay_success' => '已完成支付?',
'ali_pay_text' => '已完成支付,请刷新页面', 'ali_pay_text' => '已完成支付,请刷新页面',
'ali_pay_text' => '已完成支付,请刷新页面',
'version_compatible_text' => '该插件不兼容当前系统版本,请升级到 <a href="' . config('beike.api_url') . '/download" target="_blank">最新版本</a>',
'to_update' => '去升级',
]; ];

View File

@ -19,4 +19,6 @@ return [
'pages_show' => '文章详情', 'pages_show' => '文章详情',
'pages_update' => '文章编辑', 'pages_update' => '文章编辑',
'pages_delete' => '删除文章', 'pages_delete' => '删除文章',
'inquiry' => '询盘管理',
]; ];

View File

@ -18,7 +18,6 @@ return [
'plugins_install' => '安装', 'plugins_install' => '安装',
'plugins_uninstall' => '卸载', 'plugins_uninstall' => '卸载',
'to_enable' => '去启用', 'to_enable' => '去启用',
'uninstall_hint' => '卸载插件会删除该插件的所有相关数据,确定要卸载吗?',
'plugin_list' => '插件设置', 'plugin_list' => '插件设置',
'plugin_code' => '插件代码', 'plugin_code' => '插件代码',

View File

@ -11,6 +11,8 @@
return [ return [
'products_index' => '商品列表', 'products_index' => '商品列表',
'products_name' => '商品名称',
'products_img' => '商品图片',
'products_create' => '创建商品', 'products_create' => '创建商品',
'products_show' => '商品详情', 'products_show' => '商品详情',
'products_update' => '更新商品', 'products_update' => '更新商品',
@ -31,7 +33,7 @@ return [
'price' => '价格', 'price' => '价格',
'origin_price' => '原价', 'origin_price' => '原价',
'cost_price' => '成本价', 'cost_price' => '成本价',
'quantity' => '数量', 'quantity' => '库存数量',
'enable_multi_spec' => '启用多规格', 'enable_multi_spec' => '启用多规格',
'image_help' => '第一张图片将作为商品主图,支持同时上传多张图片,多张图片之间可随意调整位置', 'image_help' => '第一张图片将作为商品主图,支持同时上传多张图片,多张图片之间可随意调整位置',
'add_variable' => '添加规格', 'add_variable' => '添加规格',
@ -44,4 +46,9 @@ return [
'confirm_batch_status' => '确认要批量修改选中的商品的状态吗?', 'confirm_batch_status' => '确认要批量修改选中的商品的状态吗?',
'confirm_batch_restore' => '确认要恢复选中的商品吗?', 'confirm_batch_restore' => '确认要恢复选中的商品吗?',
'confirm_delete_restore' => '确认要清空回收站吗?', 'confirm_delete_restore' => '确认要清空回收站吗?',
'price_setting' => '价格设置',
'price_setting_by' =>[
'sku' => '根据规格设置价格',
'num' => '根据数量设置价格',
]
]; ];

View File

@ -85,6 +85,11 @@ return [
'error_page' => '您访问的数据不存在或已被删除~', 'error_page' => '您访问的数据不存在或已被删除~',
'error_page_btn' => '返回上一页', 'error_page_btn' => '返回上一页',
'contacts' => '联系人',
'content' => '内容',
'sku' => 'Sku',
'order' => [ 'order' => [
'unpaid' => '待支付', 'unpaid' => '待支付',
'paid' => '已支付', 'paid' => '已支付',

View File

@ -18,4 +18,9 @@ return [
'in_stock' => '有货', 'in_stock' => '有货',
'out_stock' => '缺货', 'out_stock' => '缺货',
'model' => '型号', 'model' => '型号',
'quantity_error' => '数量错误',
'inquiry' => '咨询',
'enter_contacts' => '请输入联系人',
'enter_email' => '请输入邮件',
'enter_content' => '请输入内容',
]; ];

View File

@ -10,30 +10,28 @@
*/ */
return [ return [
'marketing_list' => '插件市場', 'marketing_list' => '插件市場',
'marketing_index' => '市場首頁', 'marketing_index' => '市場首頁',
'marketing_show' => '插件詳情', 'marketing_show' => '插件詳情',
'marketing_buy' => '購買插件', 'marketing_buy' => '購買插件',
'marketing_download' => '下載插件', 'marketing_download' => '下載插件',
'set_token' => '設置 Token', 'set_token' => '設置 Token',
'get_token_text' => '登錄 BeikeShop 官網個人中心-綁定域名,添加當前域名', 'get_token_text' => '登錄 BeikeShop 官網個人中心-綁定域名,添加當前域名',
'get_token' => '點擊獲取 Token', 'get_token' => '點擊獲取 Token',
'download_count' => '下載次數', 'download_count' => '下載次數',
'last_update' => '最後更新', 'last_update' => '最後更新',
'text_version' => '版本', 'text_version' => '版本',
'text_compatibility' => '兼容性', 'text_compatibility' => '兼容性',
'text_author' => '插件作者', 'text_author' => '插件作者',
'download_plugin' => '下載插件', 'download_plugin' => '下載插件',
'download_description' => '插件描述', 'download_description' => '插件描述',
'text_free' => '免費', 'text_free' => '免費',
'btn_buy' => '購買', 'btn_buy' => '購買',
'text_pay' => '支付金額', 'text_pay' => '支付金額',
'select_pay' => '選擇支付方式', 'select_pay' => '選擇支付方式',
'wxpay' => '微信掃碼支付!', 'wxpay' => '微信掃碼支付!',
'pay_success_title' => '支付成功!', 'pay_success_title' => '支付成功!',
'pay_success_text' => '插件購買成功,點擊確定刷新頁面', 'pay_success_text' => '插件購買成功,點擊確定刷新頁面',
'ali_pay_success' => '已完成支付? ', 'ali_pay_success' => '已完成支付? ',
'ali_pay_text' => '已完成支付,請刷新頁面', 'ali_pay_text' => '已完成支付,請刷新頁面',
'version_compatible_text' => '該插件不兼容當前系統版本,請升級到 <a href="' . config('beike.api_url') . '/download" target="_blank">最新版本</a>',
'to_update' => '去升級',
]; ];

View File

@ -18,7 +18,6 @@ return [
'plugins_install' => '安裝', 'plugins_install' => '安裝',
'plugins_uninstall' => '卸載', 'plugins_uninstall' => '卸載',
'to_enable' => '去啟用', 'to_enable' => '去啟用',
'uninstall_hint' => '卸載插件會刪除該插件的所有相關數據,確定要卸載嗎? ',
'plugin_list' => '插件設置', 'plugin_list' => '插件設置',
'plugin_code' => '插件代碼', 'plugin_code' => '插件代碼',

1
storage/installed Normal file
View File

@ -0,0 +1 @@
Laravel Installer successfully INSTALLED on 2023/04/03 08:09:23am

View File

@ -13,13 +13,13 @@
<div class="select-wrap"> <div class="select-wrap">
<i class="bi {{ $cart['selected'] ? 'bi-check-circle-fill' : 'bi-circle' }}" data-id="{{ $cart['cart_id'] }}"></i> <i class="bi {{ $cart['selected'] ? 'bi-check-circle-fill' : 'bi-circle' }}" data-id="{{ $cart['cart_id'] }}"></i>
</div> </div>
<div class="product-info d-flex align-items-center"> <div class="product-info d-flex align-items-center" style="width: 100%;">
<div class="left"><a href="{{ shop_route('products.show', $cart['product_id']) }}" class="d-flex justify-content-between align-items-center h-100"><img src="{{ $cart['image'] }}" class="img-fluid"></a></div> <div class="left"><a href="{{ shop_route('products.show', $cart['product_id']) }}" class="d-flex justify-content-between align-items-center h-100"><img src="{{ $cart['image'] }}" class="img-fluid"></a></div>
<div class="right flex-grow-1"> <div class="right flex-grow-1">
<a href="{{ shop_route('products.show', $cart['product_id']) }}" class="name fs-sm fw-bold mb-2 text-dark text-truncate-2" title="{{ $cart['name'] }}">{{ $cart['name'] }}</a> <a href="{{ shop_route('products.show', $cart['product_id']) }}" class="name fs-sm fw-bold mb-2 text-dark text-truncate-2" title="{{ $cart['name'] }}">{{ $cart['name'] }}</a>
<div class="text-muted mb-1 text-size-min">{{ $cart['variant_labels'] }}</div> <div class="text-muted mb-1 text-size-min">{{ $cart['variant_labels'] }}</div>
<div class="product-bottom d-flex justify-content-between align-items-center"> <div class="product-bottom d-flex justify-content-between align-items-center">
<div class="price d-flex align-items-center"> <div class="price d-flex align-items-center offcanvas-right-cart-item-price">
{{ $cart['price_format'] }} x {{ $cart['price_format'] }} x
<input type="text" onkeyup="this.value=this.value.replace(/\D/g,'')" onafterpaste="this.value=this.value.replace(/\D/g,'')" <input type="text" onkeyup="this.value=this.value.replace(/\D/g,'')" onafterpaste="this.value=this.value.replace(/\D/g,'')"
data-id="{{ $cart['cart_id'] }}" data-sku="{{ $cart['sku_id'] }}" class="form-control p-1" value="{{ $cart['quantity'] }}"> data-id="{{ $cart['cart_id'] }}" data-sku="{{ $cart['sku_id'] }}" class="form-control p-1" value="{{ $cart['quantity'] }}">

View File

@ -85,7 +85,7 @@
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col"> <div class="col">
<div class="d-flex"> <div class="d-flex">
Powered By&nbsp;<a href="https://beikeshop.com/" target="_blank" rel="noopener">BeikeShop</a>&nbsp; Powered By
{!! $footer_content['bottom']['copyright'][$locale] ?? '' !!} {!! $footer_content['bottom']['copyright'][$locale] ?? '' !!}
</div> </div>
</div> </div>

View File

@ -76,7 +76,9 @@
</div> </div>
@endhookwrapper @endhookwrapper
<div class="menu-wrap"> <div class="menu-wrap">
@include('shared.menu-pc') @if (!is_mobile())
@include('shared.menu-pc')
@endif
</div> </div>
<div class="right-btn"> <div class="right-btn">
<ul class="navbar-nav flex-row"> <ul class="navbar-nav flex-row">
@ -149,7 +151,9 @@
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div> </div>
<div class="offcanvas-body mobile-menu-wrap"> <div class="offcanvas-body mobile-menu-wrap">
@include('shared.menu-mobile') @if (is_mobile())
@include('shared.menu-mobile')
@endif
</div> </div>
</div> </div>

View File

@ -1,11 +1,13 @@
<!doctype html> <!doctype html>
<html lang="{{ locale() }}"> <html lang="{{ locale() }}">
<head> <head>
<!-- Google tag (gtag.js) --> <script async src="https://www.googletagmanager.com/gtag/js?id=AW-11146062373"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'AW-11146062373'); </script>
<!-- Event snippet for Website sale conversion page --> <script> gtag('event', 'conversion', { 'send_to': 'AW-11146062373/ilbpCKSvzZkYEKXU7cIp', 'transaction_id': '' }); </script>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="csrf-token" content="{{ csrf_token() }}"> <meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title', system_setting('base.meta_title', 'BeikeShop开源好用的跨境电商系统 - BeikeShop官网') . ' Powered By BeikeShop')</title> <title>@yield('title', system_setting('base.meta_title', 'BeikeShop开源好用的跨境电商系统 - BeikeShop官网') . ' Powered By 万有引力')</title>
<meta name="keywords" content="@yield('keywords', system_setting('base.meta_keywords'))"> <meta name="keywords" content="@yield('keywords', system_setting('base.meta_keywords'))">
<meta name="description" content="@yield('description', system_setting('base.meta_description'))"> <meta name="description" content="@yield('description', system_setting('base.meta_description'))">
<meta name="generator" content="BeikeShop v{{ config('beike.version') }}({{ config('beike.build') }})"> <meta name="generator" content="BeikeShop v{{ config('beike.version') }}({{ config('beike.build') }})">

View File

@ -9,6 +9,9 @@
<script src="{{ asset('vendor/swiper/swiper-bundle.min.js') }}"></script> <script src="{{ asset('vendor/swiper/swiper-bundle.min.js') }}"></script>
<script src="{{ asset('vendor/zoom/jquery.zoom.min.js') }}"></script> <script src="{{ asset('vendor/zoom/jquery.zoom.min.js') }}"></script>
<link rel="stylesheet" href="{{ asset('vendor/swiper/swiper-bundle.min.css') }}"> <link rel="stylesheet" href="{{ asset('vendor/swiper/swiper-bundle.min.css') }}">
<script src="{{ asset('vendor/element-ui/2.15.6/js.js') }}"></script>
<link rel="stylesheet" href="{{ asset('vendor/element-ui/2.15.6/css.css') }}">
@endpush @endpush
@section('content') @section('content')
@ -55,14 +58,35 @@
@hookwrapper('product.detail.name') @hookwrapper('product.detail.name')
<h1 class="mb-4 product-name">{{ $product['name'] }}</h1> <h1 class="mb-4 product-name">{{ $product['name'] }}</h1>
@endhookwrapper @endhookwrapper
@hookwrapper('product.detail.price') <div class="price-wrap d-flex align-items-end" v-if="price_setting === 'sku'">
<div class="price-wrap d-flex align-items-end">
<div class="new-price fs-1 lh-1 fw-bold me-2">@{{ product.price_format }}</div> <div class="new-price fs-1 lh-1 fw-bold me-2">@{{ product.price_format }}</div>
<div class="old-price text-muted text-decoration-line-through" v-if="product.price != product.origin_price && product.origin_price !== 0"> <div class="old-price text-muted text-decoration-line-through" v-if="product.price != product.origin_price && product.origin_price !== 0">
@{{ product.origin_price_format }} @{{ product.origin_price_format }}
</div> </div>
</div> </div>
@endhookwrapper <div class="price-wrap d-flex align-items-end" v-if="price_setting === 'num'" style="
border-top: 1px solid #aaaaaa;
border-bottom: 1px solid #aaaaaa;
padding: 10px;">
<div class="item" v-for="(item,index) in numPrices" style="flex: 1">
<div class="num" v-if="index < numPrices.length - 1" style="
white-space: nowrap;
color: #777777;
font-size: 18px;
margin-bottom: 5px;
">@{{ item.num }} - @{{ numPrices[index + 1].num - 1 }} sets</div>
<div class="num" v-else-if="item.num" style="
white-space: nowrap;
color: #777777;
font-size: 18px;
margin-bottom: 5px;
">>= @{{ item.num }} sets</div>
<div class="price">
<div class="new-price fs-3 lh-1 fw-bold me-2">@{{ item.price_format }}</div>
</div>
</div>
</div>
<div class="stock-and-sku mb-4"> <div class="stock-and-sku mb-4">
@hookwrapper('product.detail.quantity') @hookwrapper('product.detail.quantity')
<div class="d-flex"> <div class="d-flex">
@ -149,12 +173,77 @@
</button> </button>
</div> </div>
@else @else
<div class="text-danger"><i class="bi bi-exclamation-circle-fill"></i> {{ __('product.has_been_inactive') }}</div> {{-- <div class="text-danger"><i class="bi bi-exclamation-circle-fill"></i> {{ __('product.has_been_inactive') }}</div>--}}
@endif @endif
<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> </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>
</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"> <div class="product-description">
<div class="nav nav-tabs nav-overflow justify-content-start justify-content-md-center border-bottom mb-3"> <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"> <a class="nav-link fw-bold active fs-5" data-bs-toggle="tab" href="#product-description">
@ -240,6 +329,39 @@
source: { source: {
skus: @json($product['skus']), skus: @json($product['skus']),
variables: @json($product['variables'] ?? []), variables: @json($product['variables'] ?? []),
},
price_setting: @json($product['price_setting'] ?? 'sku'),
numPrices: @json($product['numPrices'] ?? []),
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'
}, ],
},
},
created() {
if(this.price_setting === 'num'){
this.quantity = this.numPrices[0].num;
} }
}, },
@ -271,6 +393,28 @@
}, },
methods: { methods: {
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_idnex, value_index, value) { checkedVariableValue(variable_idnex, value_index, value) {
$('.product-image .swiper .swiper-slide').eq(0).addClass('active').siblings().removeClass('active'); $('.product-image .swiper .swiper-slide').eq(0).addClass('active').siblings().removeClass('active');
this.source.variables[variable_idnex].values.forEach((v, i) => { this.source.variables[variable_idnex].values.forEach((v, i) => {
@ -307,6 +451,11 @@
}, },
addCart(isBuyNow = false) { addCart(isBuyNow = false) {
if(this.price_setting === 'num' && this.quantity < this.numPrices[0].num){
layer.msg( '{{ __('shop/products.quantity_error') }}' );
return;
}
bk.addCart({sku_id: this.product.id, quantity: this.quantity, isBuyNow}); bk.addCart({sku_id: this.product.id, quantity: this.quantity, isBuyNow});
}, },

View File

@ -21,7 +21,11 @@
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-placement="top" data-bs-placement="top"
title="{{ __('shop/products.add_to_cart') }}" title="{{ __('shop/products.add_to_cart') }}"
onclick="bk.addCart({sku_id: '{{ $product['sku_id'] }}'}, this)"> @if ($product['price_setting'] === 'num')
onclick="bk.addCart({sku_id: '{{ $product['sku_id'] }}',quantity: {{$product['numprices'][0]['num']}} }, this)">
@else
onclick="bk.addCart({sku_id: '{{ $product['sku_id'] }}'}, this)">
@endif
<i class="bi bi-cart"></i> <i class="bi bi-cart"></i>
</button> </button>
</div> </div>
@ -30,10 +34,29 @@
<div class="product-bottom-info"> <div class="product-bottom-info">
<div class="product-name">{{ $product['name_format'] }}</div> <div class="product-name">{{ $product['name_format'] }}</div>
<div class="product-price"> <div class="product-price">
<span class="price-new">{{ $product['price_format'] }}</span> @if ($product['price_setting'] === 'sku')
@if ($product['price'] != $product['origin_price'] && $product['origin_price'] > 0) <span class="price-new">{{ $product['price_format'] }}</span>
<span class="price-old">{{ $product['origin_price_format'] }}</span> <span>-</span>
@if ($product['price'] != $product['origin_price'] && $product['origin_price'] > 0)
<span class="price-new">{{ $product['origin_price_format'] }}</span>
@endif
@elseif($product['price_setting'] === 'num')
@if ($product['price'] != $product['origin_price'] && $product['origin_price'] > 0)
<span class="price-new">{{ $product['origin_price_format'] }}</span>
@endif
<span>-</span>
<span class="price-new">{{ $product['price_format'] }}</span>
@endif @endif
<span style="color:#aaa">/pieces</span>
</div>
<!--yt修改-->
<div class="product-price">
@if ($product['price_setting'] === 'num')
<span class="price-new">{{$product['numprices'][0]['num']}} pieces</span>
@else
<span class="price-new">1 pieces</span>
@endif
<span style="color:#aaa;margin-left:4px">(Min Order)</span>
</div> </div>
@if (request('style_list') == 'list') @if (request('style_list') == 'list')