首页模块添加 商品模块 和 图标链接模块
This commit is contained in:
parent
2743057667
commit
59f47ec84c
|
|
@ -18,7 +18,7 @@ class DesignController extends Controller
|
|||
public function index(Request $request): View
|
||||
{
|
||||
$data = [
|
||||
'editors' => ['editor-slide_show', 'editor-image401', 'editor-tab_product', 'editor-image100', 'editor-brand'],
|
||||
'editors' => ['editor-slide_show', 'editor-image401', 'editor-tab_product', 'editor-product', 'editor-image100', 'editor-brand', 'editor-icons'],
|
||||
'design_settings' => system_setting('base.design_setting'),
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
<?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 Icons 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' => 'icons',
|
||||
'sort' => 0,
|
||||
'name' => trans('admin/design_builder.module_icons'),
|
||||
'icon' => '',
|
||||
];
|
||||
|
||||
return view('admin::pages.design.module.icons', $data);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?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\View\Component;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
class Product 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' => 'product',
|
||||
'sort' => 0,
|
||||
'name' => trans('admin/design_builder.module_product'),
|
||||
'icon' => '',
|
||||
];
|
||||
|
||||
return view('admin::pages.design.module.product', $data);
|
||||
}
|
||||
}
|
||||
|
|
@ -49,6 +49,10 @@ class DesignService
|
|||
return self::handleBrand($content);
|
||||
} elseif ($moduleCode == 'tab_product') {
|
||||
return self::handleTabProducts($content);
|
||||
} elseif ($moduleCode == 'product') {
|
||||
return self::handleProducts($content);
|
||||
} elseif ($moduleCode == 'icons') {
|
||||
return self::handleIcons($content);
|
||||
}
|
||||
|
||||
return $content;
|
||||
|
|
@ -111,6 +115,35 @@ class DesignService
|
|||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 icons 模块
|
||||
*
|
||||
* @param $content
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function handleIcons($content): array
|
||||
{
|
||||
$content['title'] = $content['title'][locale()] ?? '';
|
||||
|
||||
if (empty($content['images'])) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$images = [];
|
||||
foreach ($content['images'] as $image) {
|
||||
$images[] = [
|
||||
'image' => image_origin($image['image'] ?? ''),
|
||||
'text' => $image['text'][locale()] ?? '',
|
||||
'link' => self::handleLink($image['link']['type'] ?? '', $image['link']['value'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
$content['images'] = $images;
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理选项卡商品列表模块
|
||||
*
|
||||
|
|
@ -137,6 +170,19 @@ class DesignService
|
|||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理商品模块
|
||||
*
|
||||
* @param $content
|
||||
* @return array
|
||||
*/
|
||||
private static function handleProducts($content): array
|
||||
{
|
||||
$content['products'] = ProductRepo::getProductsByIds($content['products'])->jsonSerialize();
|
||||
$content['title'] = $content['title'][locale()] ?? '';
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理图片以及链接
|
||||
* @throws \Exception
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
<template id="module-editor-icons-template">
|
||||
<div class="image-edit-wrapper">
|
||||
<div class="module-editor-row">{{ __('admin/builder.text_set_up') }}</div>
|
||||
<div class="module-edit-group">
|
||||
<div class="module-edit-title">{{ __('admin/builder.text_module_title') }}</div>
|
||||
<text-i18n v-model="module.title"></text-i18n>
|
||||
</div>
|
||||
<div class="module-edit-group" style="margin-bottom: 200px;">
|
||||
<div class="module-edit-title">{{ __('admin/builder.text_add_pictures') }}</div>
|
||||
<div class="pb-images-selector" v-for="(item, index) in module.images" :key="index">
|
||||
<div class="selector-head" @click="itemShow(index)">
|
||||
<div class="left">
|
||||
|
||||
<img :src="thumbnail(item.image, 40, 40)" class="img-responsive">
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<span @click="removeItem(index)" class="remove-item"><i class="el-icon-delete"></i></span>
|
||||
<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" :is-language="false"></pb-image-selector>
|
||||
<div class="tag">{{ __('admin/builder.text_suggested_size') }}: 200x200</div>
|
||||
</div>
|
||||
<text-i18n v-model="item.text" style="margin-bottom: 10px"></text-i18n>
|
||||
<link-selector v-model="item.link"></link-selector>
|
||||
</div>
|
||||
</div>
|
||||
<div class="add-items" style="margin-top: 20px">
|
||||
<el-button icon="el-icon-circle-plus-outline" type="primary" size="small" style="width: 100%" @click="addItems" plain>{{ __('common.add') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
Vue.component('module-editor-icons', {
|
||||
template: '#module-editor-icons-template',
|
||||
|
||||
props: ['module'],
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
//
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
module: {
|
||||
handler: function (val) {
|
||||
this.$emit('on-changed', val);
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
|
||||
created: function () {
|
||||
//
|
||||
},
|
||||
|
||||
methods: {
|
||||
itemShow(index) {
|
||||
this.module.images.find((e, key) => {if (index != key) return e.show = false});
|
||||
this.module.images[index].show = !this.module.images[index].show;
|
||||
},
|
||||
|
||||
addItems() {
|
||||
this.module.images.push({
|
||||
image: '',
|
||||
link: {
|
||||
type: 'product',
|
||||
value:''
|
||||
},
|
||||
text: languagesFill(''),
|
||||
show: true
|
||||
})
|
||||
|
||||
this.module.images.find((e, key) => {if (this.module.images.length - 1 != key) return e.show = false});
|
||||
},
|
||||
|
||||
removeItem(index) {
|
||||
this.module.images.splice(index, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
const make = {
|
||||
style: {
|
||||
background_color: ''
|
||||
},
|
||||
title: languagesFill('{{ __('admin/builder.text_module_title') }}'),
|
||||
floor: languagesFill(''),
|
||||
images: []
|
||||
}
|
||||
|
||||
let register = @json($register);
|
||||
|
||||
register.make = make;
|
||||
app.source.modules.push(register)
|
||||
}, 100)
|
||||
</script>
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<template id="module-editor-product-template">
|
||||
<div class="module-editor-product-template">
|
||||
<div class="module-editor-row">{{ __('admin/builder.text_set_up') }}</div>
|
||||
<div class="module-edit-group">
|
||||
<div class="module-edit-title">{{ __('admin/builder.text_module_title') }}</div>
|
||||
<text-i18n v-model="module.title"></text-i18n>
|
||||
</div>
|
||||
|
||||
<div class="module-editor-row">{{ __('admin/builder.modules_content') }}</div>
|
||||
<div class="module-edit-group">
|
||||
<div class="module-edit-title">{{ __('admin/builder.modules_set_product') }}</div>
|
||||
<div class="tab-info">
|
||||
<div class="module-edit-group">
|
||||
<div class="autocomplete-group-wrapper">
|
||||
<el-autocomplete
|
||||
class="inline-input"
|
||||
v-model="keyword"
|
||||
value-key="name"
|
||||
size="small"
|
||||
:fetch-suggestions="querySearch"
|
||||
placeholder="{{ __('admin/builder.modules_keywords_search') }}"
|
||||
:highlight-first-item="true"
|
||||
@select="handleSelect"
|
||||
></el-autocomplete>
|
||||
|
||||
<div class="item-group-wrapper" v-loading="loading">
|
||||
<template v-if="productData.length">
|
||||
<draggable
|
||||
ghost-class="dragabble-ghost"
|
||||
:list="productData"
|
||||
@change="itemChange"
|
||||
:options="{animation: 330}"
|
||||
>
|
||||
<div v-for="(item, index) in productData" :key="index" class="item">
|
||||
<div>
|
||||
<i class="el-icon-s-unfold"></i>
|
||||
<span>${item.name}</span>
|
||||
</div>
|
||||
<i class="el-icon-delete right" @click="removeProduct(index)"></i>
|
||||
</div>
|
||||
</draggable>
|
||||
</template>
|
||||
<template v-else>{{ __('admin/builder.modules_please_products') }}</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
Vue.component('module-editor-product', {
|
||||
delimiters: ['${', '}'],
|
||||
template: '#module-editor-product-template',
|
||||
props: ['module'],
|
||||
data: function () {
|
||||
return {
|
||||
keyword: '',
|
||||
productData: [],
|
||||
loading: null,
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
module: {
|
||||
handler: function (val) {
|
||||
this.$emit('on-changed', val);
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
},
|
||||
|
||||
created: function () {
|
||||
this.tabsValueProductData();
|
||||
},
|
||||
|
||||
computed: {
|
||||
},
|
||||
|
||||
methods: {
|
||||
tabTitleLanguage(titles) {
|
||||
return titles['{{ locale() }}'];
|
||||
},
|
||||
|
||||
tabsValueProductData() {
|
||||
var that = this;
|
||||
|
||||
if (!this.module.products.length) return;
|
||||
this.loading = true;
|
||||
|
||||
$http.get('products/names?product_ids='+this.module.products.join(','), {hload: true}).then((res) => {
|
||||
this.loading = false;
|
||||
that.productData = res.data;
|
||||
})
|
||||
},
|
||||
|
||||
querySearch(keyword, cb) {
|
||||
$http.get('products/autocomplete?name=' + encodeURIComponent(keyword), null, {hload:true}).then((res) => {
|
||||
cb(res.data);
|
||||
})
|
||||
},
|
||||
|
||||
handleSelect(item) {
|
||||
if (!this.module.products.find(v => v == item.id)) {
|
||||
this.module.products.push(item.id * 1);
|
||||
this.productData.push(item);
|
||||
}
|
||||
this.keyword = ""
|
||||
},
|
||||
|
||||
itemChange(evt) {
|
||||
this.module.products = this.productData.map(e => e.id * 1);
|
||||
},
|
||||
|
||||
removeProduct(index) {
|
||||
this.productData.splice(index, 1)
|
||||
this.module.products.splice(index, 1);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
const make = {
|
||||
style: {
|
||||
background_color: ''
|
||||
},
|
||||
floor: languagesFill(''),
|
||||
products: [],
|
||||
title: languagesFill('{{ __('admin/builder.text_module_title') }}'),
|
||||
}
|
||||
|
||||
let register = @json($register);
|
||||
|
||||
register.make = make;
|
||||
app.source.modules.push(register)
|
||||
}, 100)
|
||||
</script>
|
||||
|
|
@ -31,27 +31,39 @@
|
|||
.swiper-button-prev,.swiper-button-next {
|
||||
width: 34px;
|
||||
height: 37px;
|
||||
color: #333;
|
||||
color: #999;
|
||||
@media (max-width: 768px) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:after {
|
||||
font-size: 22px;
|
||||
&:hover {
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
&:after {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.swiper-button-prev {
|
||||
left: -50px;
|
||||
left: -40px;
|
||||
}
|
||||
|
||||
.swiper-button-next {
|
||||
right: -50px;
|
||||
right: -40px;
|
||||
}
|
||||
|
||||
.swiper-pagination {
|
||||
.swiper-pagination-bullet-active {
|
||||
background: $primary;
|
||||
}
|
||||
|
||||
&.rectangle {
|
||||
span {
|
||||
border-radius: 0;
|
||||
height: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -120,7 +120,7 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.price-lod {
|
||||
.price-old {
|
||||
color: #aaa;
|
||||
margin-left: 4px;
|
||||
text-decoration: line-through;
|
||||
|
|
|
|||
|
|
@ -15,4 +15,6 @@ return [
|
|||
'module_four_image_pro' => 'eine Zeile vier Bilder PRO',
|
||||
'module_slideshow' => 'Folienmodul',
|
||||
'module_tab_products' => 'Tab-Produkte',
|
||||
'module_product' => 'Warenmodul',
|
||||
'module_icons' => 'Symbolmodul',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -15,4 +15,6 @@ return [
|
|||
'module_four_image_pro' => 'For Image PRO Module',
|
||||
'module_slideshow' => 'Slideshow Module',
|
||||
'module_tab_products' => 'Tab Products Module',
|
||||
'module_product' => 'Products Module',
|
||||
'module_icons' => 'Icon module',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -15,4 +15,6 @@ return [
|
|||
'module_four_image_pro' => 'Cuatro imágenes en una línea PRO',
|
||||
'module_slideshow' => 'módulo de presentación de diapositivas',
|
||||
'module_tab_products' => 'elemento de pestaña',
|
||||
'module_product' => 'módulo de productos básicos',
|
||||
'module_icons' => 'módulo de iconos',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -15,4 +15,6 @@ return [
|
|||
'module_four_image_pro' => 'une ligne quatre images PRO',
|
||||
'module_slideshow' => 'module diapositive',
|
||||
'module_tab_products' => 'onglet produits',
|
||||
'module_product' => 'module marchandise',
|
||||
'module_icons' => 'module d\'icônes',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -15,4 +15,6 @@ return [
|
|||
'module_four_image_pro' => 'una riga quattro immagini PRO',
|
||||
'module_slideshow' => 'modulo diapositiva',
|
||||
'module_tab_products' => 'scheda prodotti',
|
||||
'module_product' => 'modulo merci',
|
||||
'module_icons' => 'modulo icona',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -15,4 +15,6 @@ return [
|
|||
'module_four_image_pro' => '1 行 4 つの画像 PRO',
|
||||
'module_slideshow' => 'スライド モジュール',
|
||||
'module_tab_products' => 'タブ製品',
|
||||
'module_product' => '商品モジュール',
|
||||
'module_icons' => 'アイコンモジュール',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -15,4 +15,6 @@ return [
|
|||
'module_four_image_pro' => 'Четыре картинки в одну строку PRO',
|
||||
'module_slideshow' => 'модуль слайд-шоу',
|
||||
'module_tab_products' => 'элемент вкладки',
|
||||
'module_product' => 'товарный модуль',
|
||||
'module_icons' => 'модуль значков',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -15,4 +15,6 @@ return [
|
|||
'module_four_image_pro' => '一行四图 PRO',
|
||||
'module_slideshow' => '幻灯片模块',
|
||||
'module_tab_products' => '选项卡商品',
|
||||
'module_product' => '商品模块',
|
||||
'module_icons' => '图标模块',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -15,4 +15,6 @@ return [
|
|||
'module_four_image_pro' => '一行四圖 PRO',
|
||||
'module_slideshow' => '幻燈片模塊',
|
||||
'module_tab_products' => '選項卡商品',
|
||||
'module_icons' => '圖標模塊',
|
||||
'module_product' => '商品模塊',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
<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">
|
||||
<div class="module-title">{{ $content['title'] }}</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
@foreach ($content['images'] as $image)
|
||||
<div class="col-6 col-md-4 col-lg-2">
|
||||
<a href="{{ $image['link'] }}" class="text-decoration-none">
|
||||
<div class="image-item">
|
||||
<img src="{{ $image['image'] }}" class="img-fluid">
|
||||
</div>
|
||||
<p class="text-center text-dark mb-4 mt-2">{{ $image['text'] }}</p>
|
||||
</a>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<section class="module-item {{ $design ? 'module-item-design' : ''}}" id="module-{{ $module_id }}">
|
||||
@include('design._partial._module_tool')
|
||||
|
||||
<div class="module-info module-product mb-3 mb-md-5 swiper-style-plus">
|
||||
<div class="container position-relative">
|
||||
<div class="module-title">{{ $content['title'] }}</div>
|
||||
@if ($content['products'])
|
||||
<div class="swiper module-product-{{ $module_id }} module-slideshow">
|
||||
<div class="swiper-wrapper">
|
||||
@foreach ($content['products'] as $product)
|
||||
<div class="swiper-slide">
|
||||
@include('shared.product')
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
<div class="swiper-pagination rectangle module-product-{{ $module_id }}-pagination"></div>
|
||||
<div class="swiper-button-prev product-prev"></div>
|
||||
<div class="swiper-button-next product-next"></div>
|
||||
@elseif (!$content['products'] and $design)
|
||||
<div class="row">
|
||||
@for ($s = 0; $s < 4; $s++)
|
||||
<div class="col-6 col-md-4 col-lg-3">
|
||||
<div class="product-wrap">
|
||||
<div class="image"><a href="javascript:void(0)"><img src="{{ asset('catalog/placeholder.png') }}" class="img-fluid"></a></div>
|
||||
<div class="product-name">请配置商品</div>
|
||||
<div class="product-price">
|
||||
<span class="price-new">66.66</span>
|
||||
<span class="price-lod">99.99</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endfor
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
new Swiper ('.module-product-{{ $module_id }}', {
|
||||
loop: 1,
|
||||
watchSlidesProgress: true,
|
||||
breakpoints:{
|
||||
320: {
|
||||
slidesPerView: 2,
|
||||
spaceBetween: 10,
|
||||
},
|
||||
768: {
|
||||
slidesPerView: 4,
|
||||
spaceBetween: 30,
|
||||
},
|
||||
},
|
||||
spaceBetween: 30,
|
||||
// 如果需要分页器
|
||||
pagination: {
|
||||
el: '.module-product-{{ $module_id }}-pagination',
|
||||
clickable: true,
|
||||
},
|
||||
|
||||
// 如果需要前进后退按钮
|
||||
navigation: {
|
||||
nextEl: '.product-next',
|
||||
prevEl: '.product-prev',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
</section>
|
||||
|
|
@ -31,7 +31,9 @@
|
|||
<div class="product-name">{{ $product['name_format'] }}</div>
|
||||
<div class="product-price">
|
||||
<span class="price-new">{{ $product['price_format'] }}</span>
|
||||
<span class="price-lod">{{ $product['origin_price_format'] }}</span>
|
||||
@if ($product['price'] != $product['origin_price'] && $product['origin_price'] > 0)
|
||||
<span class="price-old">{{ $product['origin_price_format'] }}</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if (request('style_list') == 'list')
|
||||
|
|
|
|||
Loading…
Reference in New Issue