【增加】视频播放装修

【增加】复制商品
【增加】商品长宽高参数
【增加】合并到第三方主版本分支,更新到最新版本
This commit is contained in:
liqianjin 2023-08-11 13:55:00 +08:00
parent f344d2deab
commit 4f1beb97d9
24 changed files with 321 additions and 9 deletions

View File

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

View File

@ -118,6 +118,22 @@ class ProductController extends Controller
}
}
public function copy(ProductRequest $request, Product $product)
{
try {
$product = (new ProductService)->copy($product);
$data = [
'product' => $product,
];
hook_action('admin.product.copy.after', $data);
return json_success(trans('common.copy_success'), []);
} catch (\Exception $e) {
return json_encode($e->getMessage());
}
}
public function destroy(Request $request, Product $product)
{
$product->delete();

View File

@ -214,6 +214,7 @@ Route::prefix($adminName)
Route::middleware('can:products_index')->get('products', [Controllers\ProductController::class, 'index'])->name('products.index');
Route::middleware('can:products_create')->get('products/create', [Controllers\ProductController::class, 'create'])->name('products.create');
Route::middleware('can:products_create')->post('products', [Controllers\ProductController::class, 'store'])->name('products.store');
Route::middleware('can:products_copy')->post('products/{product}/copy', [Controllers\ProductController::class, 'copy'])->name('products.copy');
Route::middleware('can:products_show')->get('products/{product}/edit', [Controllers\ProductController::class, 'edit'])->name('products.edit');
Route::middleware('can:products_update')->put('products/{product}', [Controllers\ProductController::class, 'update'])->name('products.update');
Route::middleware('can:products_delete')->delete('products/{product}', [Controllers\ProductController::class, 'destroy'])->name('products.destroy');

View File

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

View File

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

View File

@ -11,7 +11,7 @@ class Product extends Base
use HasFactory;
use SoftDeletes;
protected $fillable = ['images', 'video', 'position', 'brand_id', 'tax_class_id', 'weight', 'weight_class', 'active', 'variables', 'price_setting'];
protected $fillable = ['images', 'video', 'position', 'brand_id', 'tax_class_id', 'weight', 'weight_class', 'active', 'variables', 'price_setting', 'length', 'width', 'height'];
protected $casts = [
'active' => 'boolean',

View File

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

View File

@ -31,7 +31,7 @@ class ProductSimple extends JsonResource
}
$name = $this->description->name ?? '';
$images = $this->images;
$images = $this->images != NULL ? $this->images : [];
$data = [
'id' => $this->id,

View File

@ -0,0 +1,129 @@
<template id="module-editor-slideshow-video-template">
<div>
<div class="module-editor-row">{{ __('admin/builder.text_set_up') }}</div>
<div class="module-edit-group">
<div class="module-edit-title">{{ __('admin/builder.modules_full_screen') }}</div>
<el-switch v-model="form.full"></el-switch>
</div>
<div class="module-editor-row">{{ __('admin/builder.modules_content') }}</div>
<div class="module-edit-group">
<div class="module-edit-title">{{ __('admin/builder.modules_select_video') }}</div>
<draggable
ghost-class="dragabble-ghost"
:list="form.images"
:options="{animation: 330, handle: '.icon-rank'}"
>
<div class="pb-images-selector" v-for="(item, index) in form.images" :key="index">
<div class="selector-head" @click="itemShow(index)">
<div class="left">
<el-tooltip class="icon-rank" effect="dark" content="{{ __('admin/builder.text_drag_sort') }}" placement="left">
<i class="el-icon-rank"></i>
</el-tooltip>
<img :src="thumbnail(item.image['{{ locale() }}'], 40, 40)" class="img-responsive">
</div>
<div class="right">
<el-tooltip class="" effect="dark" content="{{ __('admin/builder.text_delete') }}" placement="left">
<div class="remove-item" @click.stop="removeImage(index)"><i class="el-icon-delete"></i></div>
</el-tooltip>
<i :class="'el-icon-arrow-'+(item.show ? 'up' : 'down')"></i>
</div>
</div>
<div :class="'pb-images-list ' + (item.show ? 'active' : '')">
<div class="pb-images-top">
<pb-image-selector v-model="item.image"></pb-image-selector>
<div class="tag">{{ __('admin/builder.text_suggested_size') }} 1920 x 600</div>
</div>
<link-selector v-model="item.link"></link-selector>
</div>
</div>
</draggable>
<div class="add-item">
<el-button type="primary" size="small" @click="addImage" icon="el-icon-circle-plus-outline">{{ __('admin/builder.text_add_video') }}</el-button>
</div>
</div>
</div>
</template>
<script type="text/javascript">
Vue.component('module-editor-slideshow-video', {
template: '#module-editor-slideshow-video-template',
props: ['module'],
data: function () {
return {
form: null
}
},
watch: {
form: {
handler: function (val) {
this.$emit('on-changed', val);
},
deep: true,
}
},
created: function () {
this.form = JSON.parse(JSON.stringify(this.module));
},
methods: {
removeImage(index) {
this.form.images.splice(index, 1);
},
itemShow(index) {
this.form.images.find((e, key) => {if (index != key) return e.show = false});
this.form.images[index].show = !this.form.images[index].show;
},
addImage() {
this.form.images.find(e => e.show = false);
this.form.images.push({image: languagesFill('catalog/demo/banner/banner-4-en.jpg'), show: true, link: {type: 'product', value:''}});
}
}
});
</script>
@push('footer-script')
<script>
register = @json($register);
// 定义模块的配置项
register.make = {
style: {
background_color: ''
},
full: true,
floor: languagesFill(''),
images: [
// {
// image: languagesFill('catalog/demo/banner/banner-4-en.jpg'),
// show: true,
// link: {
// type: 'product',
// value:''
// }
// },
{
image: languagesFill('catalog/demo/banner/banner-3-en.jpg'),
show: false,
link: {
type: 'product',
value:''
}
}
]
}
app.source.modules.push(register)
</script>
@endpush

View File

@ -94,6 +94,14 @@
<x-admin-form-input name="position" :title="__('common.sort_order')" :value="old('position', $product->position ?? '0')" />
<x-admin::form.row :title="__('admin/product.length_width_height')">
<div class="d-flex wp-400">
<input type="text" name="length" placeholder="" value="{{ old('weight', $product->length ?? '') }}" class="form-control" style="flex: 0 0 95px" /><div style="width: 40px;line-height: 34px;text-align:center">CM X</div>
<input type="text" name="width" placeholder="" value="{{ old('weight', $product->width ?? '') }}" class="form-control" style="flex: 0 0 95px" /><div style="width: 40px;line-height: 34px;text-align:center">CM X</div>
<input type="text" name="height" placeholder="" value="{{ old('weight', $product->height ?? '') }}" class="form-control" style="flex: 0 0 95px" /><div style="width: 30px;line-height: 34px;text-align:center">CM</div>
</div>
</x-admin::form.row>
<x-admin::form.row :title="__('admin/product.weight_text')">
<div class="d-flex wp-400">
<input type="text" name="weight" placeholder="{{ __('admin/product.weight_text') }}" value="{{ old('weight', $product->weight ?? '') }}" class="form-control" style="flex: 0 0 260px" />

View File

@ -144,6 +144,7 @@
<td class="text-end text-nowrap">
@if ($product['deleted_at'] == '')
<a href="{{ admin_route('products.edit', [$product['id']]) }}" class="btn btn-outline-secondary btn-sm">{{ __('common.edit') }}</a>
<a href="javascript:void(0)" class="btn btn-outline-secondary btn-sm" @click.prevent="copyProduct({{ $loop->index }})">{{ __('common.copy') }}</a>
<a href="javascript:void(0)" class="btn btn-outline-danger btn-sm" @click.prevent="deleteProduct({{ $loop->index }})">{{ __('common.delete') }}</a>
@hook('admin.product.list.action')
@else
@ -255,6 +256,18 @@
location = bk.objectToUrlParams(this.filter, this.url)
},
copyProduct(index) {
const id = this.productIds[index];
this.$confirm('{{ __('common.confirm_copy') }}', '{{ __('common.text_hint') }}', {
type: 'warning'
}).then(() => {
$http.post('products/' + id + '/copy').then((res) => {
this.$message.success(res.message);
location.reload();
})
}).catch(()=>{});
},
deleteProduct(index) {
const id = this.productIds[index];
@ -265,7 +278,7 @@
this.$message.success(res.message);
location.reload();
})
}).catch(()=>{});;
}).catch(()=>{});
},
restoreProduct(index) {

View File

@ -49,6 +49,7 @@ return [
'text_suggested_size' => 'Suggested size (width x height):',
'text_word' => 'Text',
'text_add_pictures' => 'Add pictures',
'text_add_video' => 'Add video',
'text_refresh_cookie' => 'Refresh Cookie',
'text_popup_hint' => 'Note: When the user clicks close, it will no longer be displayed until the browser is closed and reopened. For local testing, click "Refresh Cookie", then refresh the browser, and the pop-up window can be launched again. ',
'text_cookie_refresh_success' => 'Cookie refresh successfully, refresh the browser to see the content of the pop-up window. ',
@ -197,6 +198,7 @@ return [
'modules_content' => 'Content',
'modules_edit_content' => 'Edit content',
'modules_select_image' => 'Select image',
'modules_select_video' => 'Select video',
'modules_quantity_line' => 'Display several in one line',
'modules_please_choose' => 'Please choose',
'modules_choose' => 'Choose',

View File

@ -14,6 +14,7 @@ return [
'module_banner' => 'Banner',
'module_four_image_pro' => 'For Image PRO',
'module_slideshow' => 'Slideshow',
'module_slideshow_video'=> 'SlideshowVideo',
'module_tab_products' => 'Tab Products',
'module_product' => 'Products',
'module_icons' => 'Icons',

View File

@ -45,6 +45,7 @@ return [
'default_main_product' => 'Default main product',
'modify_order' => 'Double-click to modify, drag to adjust the order',
'weight_text' => 'weight',
'length_width_height' => 'Length, width and height',
'weight_class' => 'weight unit',
'confirm_batch_product' => 'Are you sure you want to delete the selected products in batches? ',

View File

@ -22,6 +22,7 @@ return [
'deleted_success' => 'Deleted Successfully!',
'restored_success' => 'Restore Successfully!',
'updated_success' => 'Updated Successfully!',
'copy_success' => 'Copy Successfully!',
'edit_success' => 'Modify Success!',
'get_success' => 'Get Successfully!',
'all' => 'All',
@ -47,12 +48,14 @@ return [
'reset' => 'Reset',
'export' => 'Export',
'edit' => 'Edit',
'copy' => 'Copy',
'action' => 'Action',
'add' => 'Add',
'please_choose' => 'Please Choose',
'recommend_size' => 'Recommend Size',
'pick_datetime' => 'Pick Datetime',
'confirm_delete' => 'You sure you want to delete it?',
'confirm_copy' => 'You sure you want to copy it?',
'text_hint' => 'Hint',
'restore' => 'Restore',
'name' => 'Name',

View File

@ -49,6 +49,7 @@ return [
'text_suggested_size' => '建议尺寸(宽x高): ',
'text_word' => '文字',
'text_add_pictures' => '添加图片',
'text_add_video' => '添加视频',
'text_refresh_cookie' => '刷新 Cookie',
'text_popup_hint' => '注:用户点击关闭将不再显示,直到浏览器关闭重新打开。本地测试可点击 "刷新 Cookie",后刷新浏览器,弹窗可再次启动。 ',
'text_cookie_refresh_success' => 'Cookie 刷新成功,刷新浏览器即可看到弹窗内容。',
@ -197,6 +198,7 @@ return [
'modules_content' => '内容',
'modules_edit_content' => '编辑内容',
'modules_select_image' => '选择图片',
'modules_select_video' => '选择视频',
'modules_quantity_line' => '一行显示几个',
'modules_please_choose' => '请选择',
'modules_choose' => '选择',

View File

@ -14,6 +14,7 @@ return [
'module_banner' => '横幅模块',
'module_four_image_pro' => '一行四图 PRO',
'module_slideshow' => '幻灯片模块',
'module_slideshow_video'=> '视频模块',
'module_tab_products' => '选项卡商品',
'module_product' => '商品模块',
'module_icons' => '图标模块',

View File

@ -45,6 +45,7 @@ return [
'default_main_product' => '默认主商品',
'modify_order' => '双击修改、拖动调整顺序',
'weight_text' => '重量',
'length_width_height' => '长宽高',
'weight_class' => '重量单位',
'confirm_batch_product' => '确认要批量删除选中的商品吗?',

View File

@ -21,6 +21,7 @@ return [
'deleted_success' => '删除成功!',
'restored_success' => '恢复成功!',
'updated_success' => '更新成功!',
'copy_success' => '复制成功!',
'edit_success' => '修改成功!',
'get_success' => '获取成功!',
'all' => '全部',
@ -46,12 +47,14 @@ return [
'reset' => '重置',
'export' => '导出',
'edit' => '编辑',
'copy' => '复制',
'action' => '操作',
'add' => '添加',
'please_choose' => '请选择',
'recommend_size' => '建议尺寸',
'pick_datetime' => '选择时间',
'confirm_delete' => '确定要删除吗?',
'confirm_copy' => '确定要复制吗?',
'text_hint' => '提示',
'restore' => '恢复',
'name' => '名称',

View File

@ -40,6 +40,7 @@ return [
'default_main_product' => '默認主商品',
'modify_order' => '雙擊修改、拖動調整順序',
'weight_text' => '重量',
'length_width_height' => '长宽高',
'weight_class' => '重量單位',
'confirm_batch_product' => '確認要批量刪除選中的商品嗎? ',

View File

@ -53,4 +53,4 @@
slideshowSwiper();
@endif
</script>
</section>
</section>

View File

@ -0,0 +1,61 @@
@push('header')
<script src="{{ asset('vendor/swiper/swiper-bundle.min.js') }}"></script>
<link rel="stylesheet" href="{{ asset('vendor/swiper/swiper-bundle.min.css') }}">
@endpush
<section class="module-item {{ $design ? 'module-item-design' : ''}}" id="module-{{ $module_id }}">
@include('design._partial._module_tool')
<div class="module-info mb-3 mb-md-5 {{ !$content['full'] ? 'container' : '' }}">
<div class="swiper module-swiper-{{ $module_id }} module-slideshow">
<div class="swiper-wrapper">
@foreach($content['images'] as $image)
<div class="swiper-slide">
<a href="{{ $image['link']['link'] ?: 'javascript:void(0)' }}" class="d-flex justify-content-center">
@if(in_array(pathinfo($image['image'])['extension'],['mp4']))
<video src="{{ $image['image'] }}" class="img-fluid" muted autoplay loop>
@else
<img src="{{ $image['image'] }}" class="img-fluid">
@endif
</a>
</div>
@endforeach
</div>
<div class="swiper-pagination slideshow-pagination-{{ $module_id }}"></div>
<div class="swiper-button-prev slideshow-btnprev-{{ $module_id }}"></div>
<div class="swiper-button-next slideshow-btnnext-{{ $module_id }}"></div>
</div>
</div>
<script>
function slideshowSwiper() {
new Swiper ('.module-swiper-{{ $module_id }}', {
loop: '{{ count($content['images']) > 1 ? true : false }}', // 循环模式选项
autoplay: true,
pauseOnMouseEnter: true,
clickable :true,
// 如果需要分页器
pagination: {
el: '.slideshow-pagination-{{ $module_id }}',
clickable :true
},
// 如果需要前进后退按钮
navigation: {
nextEl: '.slideshow-btnnext-{{ $module_id }}',
prevEl: '.slideshow-btnprev-{{ $module_id }}',
},
})
}
@if ($design)
bk.loadStyle('{{ asset('vendor/swiper/swiper-bundle.min.css') }}');
bk.loadScript('{{ asset('vendor/swiper/swiper-bundle.min.js') }}', () => {
slideshowSwiper();
})
@else
slideshowSwiper();
@endif
</script>
</section>

View File

@ -10,8 +10,8 @@
<script src="{{ asset('vendor/zoom/jquery.zoom.min.js') }}"></script>
<link rel="stylesheet" href="{{ asset('vendor/swiper/swiper-bundle.min.css') }}">
<script src="{{ asset('vendor/element-ui/2.15.6/js.js') }}"></script>
<link rel="stylesheet" href="{{ asset('vendor/element-ui/2.15.6/css.css') }}">
<script src="{{ asset('vendor/element-ui/index.js') }}"></script>
<link rel="stylesheet" href="{{ asset('vendor/element-ui/index.css') }}">
@if ($product['video'])
<script src="{{ asset('vendor/video/video.min.js') }}"></script>
<link rel="stylesheet" href="{{ asset('vendor/video/video-js.min.css') }}">

View File

@ -40,7 +40,7 @@
data-bs-placement="top"
title="{{ __('shop/products.add_to_cart') }}"
@if ($product['price_setting'] === 'num')
onclick="bk.addCart({sku_id: '{{ $product['sku_id'] }}',quantity: {{$product['numprices'][0]['num']}} }, this)">
onclick="bk.addCart({sku_id: '{{ $product['sku_id'] }}',quantity: {{$product['numprices'] != [] ? $product['numprices'][0]['num'] : 1}} }, this)">
@else
onclick="bk.addCart({sku_id: '{{ $product['sku_id'] }}'}, this)">
@endif
@ -71,7 +71,7 @@
<!--yt修改-->
<div class="product-price">
@if ($product['price_setting'] === 'num')
<span class="price-new">{{$product['numprices'][0]['num']}} pieces</span>
<span class="price-new">{{$product['numprices'] != [] ? $product['numprices'][0]['num'] : 1}} pieces</span>
@else
<span class="price-new">1 pieces</span>
@endif