修改商品 文章 自定义轮播切换处理

v1.1.0
于肖磊 2024-11-15 17:07:06 +08:00
parent 889de555de
commit d14d70a8ad
22 changed files with 474 additions and 274 deletions

View File

@ -30,33 +30,38 @@
</div>
</div>
<div v-else class="oh" :class="article_theme_class">
<el-carousel :key="carousel_key" indicator-position="none" :interval="interval_time" arrow="never" :autoplay="is_roll ? true : false">
<el-carousel-item v-for="(item1, index1) in article_carousel_list" :key="index1" class="flex" :style="article_spacing">
<div v-for="(item, index) in item1.carousel_list" :key="index" class="item oh" :style="article_style">
<div class="h oh flex-col" :style="article_img_style">
<template v-if="item.new_cover.length > 0">
<image-empty v-model="item.new_cover[0].url" class="img" :style="img_radius" :error-img-style="error_img"></image-empty>
</template>
<template v-else> <image-empty v-model="item.data.cover" class="img" :style="img_radius" :error-img-style="error_img"></image-empty> </template>
<div class="jc-sb flex-1 flex-col" :style="article_theme != '0' ? content_padding : ''">
<div class="flex-col" :style="'gap:' + new_style.name_desc_space + 'px;'">
<div class="title text-line-2" :style="article_name">{{ !isEmpty(item.new_title) ? item.new_title : item.data.title }}</div>
<div v-if="field_show.includes('2')" class="desc text-line-1" :style="article_desc">{{ item.data.describe || '' }}</div>
</div>
<div class="flex-row jc-sb gap-8 align-e mt-10">
<div :style="article_date">{{ field_show.includes('0') ? (!is_obj_empty(item.data) ? item.data.add_time : '2020-06-05 15:20') : '' }}</div>
<div v-show="field_show.includes('1')" class="flex-row align-c gap-3" :style="article_page_view">
<icon name="eye"></icon>
<div>
{{ item.data.access_count ? item.data.access_count : '16' }}
</div>
<swiper :key="carousel_key" class="w flex" direction="horizontal" :loop="true" :autoplay="autoplay" :slides-per-view="Number(carousel_col) + 1" :slides-per-group="slides_per_group" :allow-touch-move="false" :space-between="new_style.article_spacing" :pause-on-mouse-enter="true" :modules="modules">
<swiper-slide v-for="(item, index) in data_list" :key="index" class="item oh" :style="article_style">
<div class="h oh flex-col" :style="article_img_style">
<template v-if="item.new_cover.length > 0">
<image-empty v-model="item.new_cover[0].url" class="img" :style="img_radius" :error-img-style="error_img"></image-empty>
</template>
<template v-else> <image-empty v-model="item.data.cover" class="img" :style="img_radius" :error-img-style="error_img"></image-empty> </template>
<div class="jc-sb flex-1 flex-col" :style="article_theme != '0' ? content_padding : ''">
<div class="flex-col" :style="'gap:' + new_style.name_desc_space + 'px;'">
<div class="title text-line-2" :style="article_name">{{ !isEmpty(item.new_title) ? item.new_title : item.data.title }}</div>
<div v-if="field_show.includes('2')" class="desc text-line-1" :style="article_desc">{{ item.data.describe || '' }}</div>
</div>
<div class="flex-row jc-sb gap-8 align-e mt-10">
<div :style="article_date">{{ field_show.includes('0') ? (!is_obj_empty(item.data) ? item.data.add_time : '2020-06-05 15:20') : '' }}</div>
<div v-show="field_show.includes('1')" class="flex-row align-c gap-3" :style="article_page_view">
<icon name="eye"></icon>
<div>
{{ item.data.access_count ? item.data.access_count : '16' }}
</div>
</div>
</div>
</div>
</div>
</swiper-slide>
</swiper>
<!-- <el-carousel :key="carousel_key" indicator-position="none" :interval="interval_time" arrow="never" :autoplay="is_roll ? true : false">
<el-carousel-item v-for="(item1, index1) in article_carousel_list" :key="index1" class="flex" :style="article_spacing">
<div v-for="(item, index) in item1.carousel_list" :key="index" class="item oh" :style="article_style">
</div>
</el-carousel-item>
</el-carousel>
</el-carousel> -->
</div>
</div>
</div>
@ -66,6 +71,10 @@
import { common_styles_computer, padding_computer, radius_computer, get_math, is_obj_empty, common_img_computer, background_computer, gradient_handle } from '@/utils';
import { isEmpty, cloneDeep } from 'lodash';
import ArticleAPI from '@/api/article';
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Autoplay } from 'swiper/modules';
import 'swiper/css';
const modules = [Autoplay];
/**
* @description: 文章列表渲染
* @param value{Object} 样式数据
@ -132,14 +141,6 @@ const default_data_list: ArticleList = {
new_title: '测试文章标题',
new_cover: [],
};
//
const carousel_col = ref('2');
// key
const carousel_key = ref('0');
//
const interval_time = ref(2000);
//
const is_roll = ref(1);
//
const new_content = computed(() => props.value?.content || {});
//
@ -271,6 +272,12 @@ const article_img_height = computed(() => {
}
}
});
//
const carousel_col = ref('2');
// key
const carousel_key = ref('0');
const autoplay = ref<boolean | object>(false);
const slides_per_group = ref(1);
// value
watch(
() => props.value,
@ -317,11 +324,22 @@ watch(
} else if (article_theme.value == '4') {
// key
carousel_col.value = new_content.carousel_col || '1';
carousel_key.value = new_style.interval_time + new_style.is_roll;
//
interval_time.value = (new_style.interval_time || 2) * 1000;
//
is_roll.value = new_style.is_roll;
carousel_key.value = new_style.interval_time + new_style.is_roll + new_style.rolling_fashion;
// //
// interval_time.value = (new_style.interval_time || 2) * 1000;
// //
// is_roll.value = new_style.is_roll;
//
if (new_style.is_roll == '1' && data_list.value.length > 0) {
autoplay.value = {
delay: (new_style.interval_time || 2) * 1000,
pauseOnMouseEnter: true,
};
} else {
autoplay.value = false;
}
//
slides_per_group.value = new_style.rolling_fashion == 'translation' ? 1 : Number(new_content.carousel_col) + 1;
article_item_height.value = `${new_style.article_height}px`;
article_style.value += content_radius.value + gradient;
article_img_style.value = background_computer(article_data);
@ -348,31 +366,6 @@ const article_theme_class = computed(() => {
return 'style5';
}
});
interface ArticleCarouselList {
carousel_list: ArticleList[];
}
//
const article_carousel_list = computed(() => {
//
const cloneList = cloneDeep(data_list.value);
//
if (cloneList.length > 0) {
//
const num = Number(carousel_col.value) + 1;
//
let nav_list: ArticleCarouselList[] = [];
//
const split_num = Math.ceil(cloneList.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({ carousel_list: cloneList.slice(i * num, (i + 1) * num) });
}
return nav_list;
} else {
//
return [{ carousel_list: cloneList }];
}
});
</script>
<style lang="scss" scoped>
.style1 {
@ -413,6 +406,7 @@ const article_carousel_list = computed(() => {
.item {
width: v-bind(multicolumn_columns_width);
min-width: v-bind(multicolumn_columns_width);
height: v-bind(carousel_height_computer);
.img {
width: 100%;
max-height: v-bind(article_item_height);
@ -423,12 +417,4 @@ const article_carousel_list = computed(() => {
}
}
}
:deep(.el-carousel) {
width: 100%;
.el-carousel__container {
// height: v-bind(article_item_height);
height: v-bind(carousel_height_computer);
}
}
</style>

View File

@ -72,9 +72,17 @@
<el-form-item label="自动轮播">
<el-switch v-model="form.is_roll" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item label="间隔时间">
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
</el-form-item>
<template v-if="form.is_roll === 1">
<el-form-item label="间隔时间">
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
</el-form-item>
<el-form-item label="滚动方式">
<el-radio-group v-model="form.rolling_fashion">
<el-radio value="translation">平移</el-radio>
<el-radio value="cut-screen">切屏</el-radio>
</el-radio-group>
</el-form-item>
</template>
</card-container>
</template>
</el-form>

View File

@ -98,9 +98,17 @@
<el-form-item label="自动轮播">
<el-switch v-model="form.is_roll" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item v-if="form.is_roll == 1" label="间隔时间">
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
</el-form-item>
<template v-if="form.is_roll == 1">
<el-form-item label="间隔时间">
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
</el-form-item>
<el-form-item label="滚动方式">
<el-radio-group v-model="form.rolling_fashion">
<el-radio value="translation">平移</el-radio>
<el-radio value="cut-screen">切屏</el-radio>
</el-radio-group>
</el-form-item>
</template>
</card-container>
</template>
</el-form>

View File

@ -1,9 +1,9 @@
<template>
<template v-if="data_source_content_list.length > 0">
<template v-if="data_source_content_list.length > 0 && form.data_source_direction == '1'">
<div v-for="(item1, index1) in data_source_content_list" :key="index1" :style="style_container">
<div class="w h" :style="style_img_container">
<div class="w h re custom-other">
<div v-for="(item, index) in form.custom_list" :key="item.id" class="main-content" :style="{'left': percentage_count(item.location.x, div_width) , 'top': percentage_count(item.location.y, form.height), 'width': percentage_count(item.com_data.com_width, div_width), 'height': percentage_count(item.com_data.com_height, form.height), 'z-index': (form.custom_list.length - 1) - index}">
<div v-for="(item, index) in form.custom_list" :key="item.id" class="main-content" :style="{'left': percentage_count(item.location.x) , 'top': percentage_count(item.location.y), 'width': percentage_count(item.com_data.com_width), 'height': percentage_count(item.com_data.com_height), 'z-index': (form.custom_list.length - 1) - index}">
<template v-if="item.key == 'text'">
<model-text :key="item.com_data" :value="item.com_data" :scale="scale" :source-list="item1" :source-type="form?.data_source || ''" :is-percentage="true"></model-text>
</template>
@ -24,11 +24,51 @@
</div>
</div>
</template>
<div v-else-if="data_source_content_list.length > 0 && form.data_source_direction == '0'" class="re oh">
<swiper :key="carouselKey" class="w flex" direction="horizontal" :loop="true" :autoplay="autoplay" :slides-per-view="form.data_source_carousel_col" :slides-per-group="slides_per_group" :allow-touch-move="false" :pause-on-mouse-enter="true" :modules="modules" @slide-change="slideChange">
<swiper-slide v-for="(item1, index1) in data_source_content_list" :key="index1">
<div :style="style_container">
<div class="w h" :style="style_img_container">
<div class="w re custom-other">
<div v-for="(item, index) in form.custom_list" :key="item.id" class="main-content" :style="{'left': percentage_count(item.location.x) , 'top': percentage_count(item.location.y), 'width': percentage_count(item.com_data.com_width), 'height': percentage_count(item.com_data.com_height), 'z-index': (form.custom_list.length - 1) - index}">
<template v-if="item.key == 'text'">
<model-text :key="item.com_data" :value="item.com_data" :scale="scale" :source-list="item1" :source-type="form?.data_source || ''" :is-percentage="true"></model-text>
</template>
<template v-else-if="item.key == 'img'">
<model-image :key="item.com_data" :value="item.com_data" :scale="scale" :source-list="item1" :source-type="form?.data_source || ''" :is-percentage="true"></model-image>
</template>
<template v-else-if="item.key == 'auxiliary-line'">
<model-lines :key="item.com_data" :value="item.com_data" :scale="scale" :source-list="item1" :source-type="form?.data_source || ''" :is-percentage="true"></model-lines>
</template>
<template v-else-if="item.key == 'icon'">
<model-icon :key="item.com_data" :value="item.com_data" :scale="scale" :source-list="item1" :source-type="form?.data_source || ''" :is-percentage="true"></model-icon>
</template>
<template v-else-if="item.key == 'panel'">
<model-panel :key="item.com_data" :value="item.com_data" :scale="scale" :source-list="item1" :source-type="form?.data_source || ''" :is-percentage="true"></model-panel>
</template>
</div>
</div>
</div>
</div>
</swiper-slide>
</swiper>
<div v-if="new_style.is_show == '1' && dot_list.length > 0" :class="{'x-middle': new_style.indicator_location == 'center', 'right-0': new_style.indicator_location == 'flex-end' }" class="dot flex abs" :style="`bottom: ${new_style.indicator_bottom}px;`">
<template v-if="new_style.indicator_style == 'num'">
<div :style="indicator_style" class="dot-item">
<span class="num-active">{{ actived_index + 1 }}</span
><span>/{{ dot_list.length }}</span>
</div>
</template>
<template v-else>
<div v-for="(item, index) in dot_list" :key="index" :style="indicator_style" :class="{ 'dot-item': true, active: actived_index == index }" />
</template>
</div>
</div>
<template v-else>
<div :style="style_container">
<div class="w h" :style="style_img_container">
<div class="w h re custom-other">
<div v-for="(item, index) in form.custom_list" :key="item.id" class="main-content" :style="{'left': percentage_count(item.location.x, div_width) , 'top': percentage_count(item.location.y, form.height), 'width': percentage_count(item.com_data.com_width, div_width), 'height': percentage_count(item.com_data.com_height, form.height), 'z-index': (form.custom_list.length - 1) - index}">
<div v-for="(item, index) in form.custom_list" :key="item.id" class="main-content" :style="{'left': percentage_count(item.location.x) , 'top': percentage_count(item.location.y), 'width': percentage_count(item.com_data.com_width), 'height': percentage_count(item.com_data.com_height), 'z-index': (form.custom_list.length - 1) - index}">
<template v-if="item.key == 'text'">
<model-text :key="item.com_data" :value="item.com_data" :scale="scale" :is-percentage="true"></model-text>
</template>
@ -52,8 +92,12 @@
</template>
<script setup lang="ts">
import { isEmpty, cloneDeep } from 'lodash';
import { common_img_computer, common_styles_computer } from '@/utils';
import { common_img_computer, common_styles_computer, get_math, radius_computer } from '@/utils';
import { source_list } from '@/config/const/custom';
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Autoplay } from 'swiper/modules';
import 'swiper/css';
const modules = [Autoplay];
const props = defineProps({
value: {
@ -90,13 +134,21 @@ onBeforeMount(() => {
}
}
});
//#region
const custom_height = computed(() => form.value.height * scale.value + 'px');
const div_width = ref(0);
const scale = ref(1);
const percentage_count = (val: number, container_size: number) => {
const percentage_count = (val: number) => {
return val * scale.value + 'px';
};
//
watchEffect(() => {
const { margin_left, margin_right, padding_left, padding_right } = new_style.value.common_style;
//
const width = 390 - margin_left - margin_right - padding_left - padding_right - props.outerContainerPadding;
//
scale.value = width / 390;
});
//#endregion
//
const style_container = computed(() => common_styles_computer(new_style.value.common_style));
const style_img_container = computed(() => common_img_computer(new_style.value.common_style));
@ -118,18 +170,75 @@ let data_source_content_list = computed(() => {
return form.value.data_source_content.data_list;
}
})
//#region
// key
const carouselKey = ref('0');
const autoplay = ref<boolean | object>(false);
const slides_per_group = ref(1);
const dot_list = ref<unknown[]>([]);
//
watchEffect(() => {
const { margin_left, margin_right, padding_left, padding_right } = new_style.value.common_style;
//
div_width.value = 390 - margin_left - margin_right - padding_left - padding_right - props.outerContainerPadding;
//
scale.value = div_width.value / 390;
//
if (new_style.value.is_roll == '1') {
autoplay.value = {
delay: (new_style.value.interval_time || 2) * 1000,
pauseOnMouseEnter: true,
};
} else {
autoplay.value = false;
}
//
slides_per_group.value = new_style.value.rolling_fashion == 'translation' ? 1 : form.value.data_source_carousel_col;
const num = new_style.value.rolling_fashion == 'translation' ? data_source_content_list.value.length : Math.ceil(data_source_content_list.value.length / form.value.data_source_carousel_col);
dot_list.value = Array(num);
// key
carouselKey.value = get_math();
});
//
const actived_color = computed(() => new_style.value?.actived_color || '#2A94FF' );
const indicator_style = computed(() => {
let indicator_styles = '';
if (!isEmpty(new_style.value.indicator_radius)) {
indicator_styles += radius_computer(new_style.value.indicator_radius);
}
const size = new_style.value?.indicator_size || 5;
if (new_style.value.indicator_style == 'num') {
indicator_styles += `color: ${new_style.value?.color || '#DDDDDD'};`;
indicator_styles += `font-size: ${size}px;`;
} else if (new_style.value.indicator_style == 'elliptic') {
indicator_styles += `background: ${new_style.value?.color || '#DDDDDD'};`;
indicator_styles += `width: ${size * 3}px; height: ${size}px;`;
} else {
indicator_styles += `background: ${new_style.value?.color || '#DDDDDD'};`;
indicator_styles += `width: ${size}px; height: ${size}px;`;
}
return indicator_styles;
});
const actived_index = ref(0);
const slideChange = (swiper: { realIndex: number }) => {
//
actived_index.value = new_style.value.rolling_fashion == 'translation' ? swiper.realIndex : (swiper.realIndex / form.value.data_source_carousel_col) > 0 ? (swiper.realIndex / form.value.data_source_carousel_col) : 0;
};
//#endregion
</script>
<style lang="scss" scoped>
.custom-other {
height: v-bind(custom_height);
}
.dot {
z-index: 1;
padding-right: 10px;
padding-left: 10px;
.dot-item {
margin: 0 0.3rem;
&.active {
background: v-bind(actived_color) !important;
}
.num-active {
color: v-bind(actived_color) !important;
}
}
}
.main-content {
position: absolute;
overflow: hidden;

View File

@ -9,8 +9,26 @@
</el-select>
</el-form-item>
</card-container>
<!-- 商品的筛选数据 -->
<!-- 筛选数据 -->
<template v-if="['goods', 'article', 'brand'].includes(form.data_source)">
<div class="divider-line"></div>
<card-container>
<div class="mb-12">显示设置</div>
<el-form-item label="铺满方式">
<el-radio-group v-model="form.data_source_direction">
<el-radio value="0">横向滑动</el-radio>
<el-radio value="1">纵向</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.data_source_direction == '0'" label="每屏显示">
<el-radio-group v-model="form.data_source_carousel_col">
<el-radio :value="1">单列展示</el-radio>
<el-radio :value="2">两列展示</el-radio>
<el-radio :value="3">三列展示</el-radio>
<el-radio :value="4">四列展示</el-radio>
</el-radio-group>
</el-form-item>
</card-container>
<div class="divider-line"></div>
<card-container>
<div class="mb-12">{{ form.data_source == 'goods' ? '商品' : form.data_source == 'article' ? '文章' : '品牌' }}设置</div>
@ -27,7 +45,7 @@
<Dialog ref="dialog" @accomplish="accomplish">
<div class="flex-row h w">
<!-- 左侧和中间区域 -->
<DragIndex ref="draglist" :key="dragkey" v-model:height="center_height" v-model:width="center_width" :source-list="!isEmpty(data_source_content_list) ? data_source_content_list[0] : {}" :source-type="form?.data_source || ''" :list="custom_list" @right-update="right_update"></DragIndex>
<DragIndex ref="draglist" :key="dragkey" v-model:height="center_height" v-model:width="custom_width" :source-list="!isEmpty(data_source_content_list) ? data_source_content_list[0] : {}" :source-type="form?.data_source || ''" :list="custom_list" @right-update="right_update"></DragIndex>
<!-- 右侧配置区域 -->
<div class="settings">
<template v-if="diy_data.key === 'img'">
@ -85,6 +103,14 @@ const dialog = ref<expose | null>(null);
const draglist = ref<diy_data | null>(null);
const form = ref(props.value);
const center_width = ref(props.magicWidth);
const custom_width = computed(() => {
if (['goods', 'article', 'brand'].includes(form.value.data_source) && form.value.data_source_direction == '0') {
return center_width.value / form.value.data_source_carousel_col;
} else {
return center_width.value;
}
})
//
let custom_list = reactive([]);
const center_height = ref(0);

View File

@ -4,7 +4,7 @@
<model-custom-content :value="value.content"></model-custom-content>
</template>
<template v-if="type == '2'">
<model-custom-styles :value="value.style"></model-custom-styles>
<model-custom-styles :value="value.style" :content="value.content" ></model-custom-styles>
</template>
</div>
</template>

View File

@ -1,6 +1,32 @@
<template>
<div class="w">
<common-styles :value="value.common_style" />
<template v-if="data.data_source_direction == '0'">
<el-form :model="form" label-width="70">
<card-container>
<div class="mb-12">轮播设置</div>
<el-form-item label="自动轮播">
<el-switch v-model="form.is_roll" active-value="1" inactive-value="0" />
</el-form-item>
<template v-if="form.is_roll == '1'">
<el-form-item label="间隔时间">
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
</el-form-item>
<el-form-item label="滚动方式">
<el-radio-group v-model="form.rolling_fashion">
<el-radio value="translation">平移</el-radio>
<el-radio value="cut-screen">切屏</el-radio>
</el-radio-group>
</el-form-item>
</template>
</card-container>
<div class="bg-f5 divider-line" />
<card-container>
<carousel-indicator :value="form"></carousel-indicator>
</card-container>
</el-form>
<div class="bg-f5 divider-line" />
</template>
<common-styles :value="form.common_style" />
</div>
</template>
<script setup lang="ts">
@ -8,8 +34,18 @@ const props = defineProps({
value: {
type: Object,
default: () => ({}),
},
content: {
type: Object,
default: () => ({}),
}
});
const state = reactive({
form: props.value,
data: props.content,
});
// 使toRefs
const { form, data } = toRefs(state);
</script>
<style lang="scss" scoped>

View File

@ -1,49 +1,30 @@
<template>
<card-container class="mb-8">
<el-form-item label="底部背景">
<div class="flex-col gap-10 w">
<div class="size-12">背景色</div>
<mult-color-picker :key="form.carouselKey" :value="form.color_list" :type="form.direction" @update:value="mult_color_picker_event"></mult-color-picker>
<div class="flex-row jc-sb align-c">
<div class="size-12">背景图</div>
<bg-btn-style v-model="form.background_img_style"></bg-btn-style>
</div>
<upload v-model="form.background_img" :limit="1"></upload>
</div>
</el-form-item>
<template v-if="['goods', 'images'].includes(tabs_content.data_type)">
<el-form-item label="自动轮播">
<el-switch v-model="form.is_roll" active-value="1" inactive-value="0" />
</el-form-item>
<template v-if="form.is_roll == '1'">
<el-form-item label="轮播方向">
<el-radio-group v-model="form.rotation_direction">
<el-radio value="horizontal">横向</el-radio>
<el-radio value="vertical">纵向</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="间隔时间">
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
<template v-if="tabs_content.data_type != 'custom'">
<card-container>
<div class="mb-12">基础样式</div>
<template v-if="['goods', 'images'].includes(tabs_content.data_type)">
<el-form-item label="自动轮播">
<el-switch v-model="form.is_roll" active-value="1" inactive-value="0" />
</el-form-item>
<template v-if="form.is_roll == '1'">
<el-form-item label="轮播方向">
<el-radio-group v-model="form.rotation_direction">
<el-radio value="horizontal">横向</el-radio>
<el-radio value="vertical">纵向</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="间隔时间">
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
</el-form-item>
</template>
</template>
</template>
<el-form-item v-if="tabs_content.data_type != 'custom'" label="内间距">
<padding :key="form.carouselKey" :value="form.chunk_padding" @update:value="chunk_padding_change"></padding>
</el-form-item>
<template v-if="tabs_content.data_type === 'goods'">
<el-form-item label="标题内间距">
<slider v-model="form.title_gap" :min="0" :max="100"></slider>
<el-form-item v-if="tabs_content.data_type != 'custom'" :label="tabs_content.data_type != 'video' ? '图片圆角' : '视频圆角'">
<radius :key="form.carouselKey" :value="form.img_radius" @update:value="img_radius_change"></radius>
</el-form-item>
<el-form-item label="标题外间距">
<slider v-model="form.title_data_gap" :min="0" :max="100"></slider>
</el-form-item>
</template>
<el-form-item v-if="tabs_content.data_type != 'custom'" :label="tabs_content.data_type != 'video' ? '图片圆角' : '视频圆角'">
<radius :key="form.carouselKey" :value="form.img_radius" @update:value="img_radius_change"></radius>
</el-form-item>
</card-container>
<template v-if="tabs_content.data_type === 'goods'">
</card-container>
<div class="bg-f5 divider-line" />
</template>
<template v-if="tabs_content.data_type === 'goods'">
<card-container>
<div class="mb-12">标题样式</div>
<el-form-item label="主标题">
@ -59,13 +40,19 @@
<el-form-item label="副标题">
<color-text-size-group v-model:color="form.subtitle_color" v-model:typeface="form.subtitle_typeface" v-model:size="form.subtitle_size" default-color="#000000"></color-text-size-group>
</el-form-item>
<el-form-item label="标题内间距">
<slider v-model="form.title_gap" :min="0" :max="100"></slider>
</el-form-item>
<el-form-item label="标题外间距">
<slider v-model="form.title_data_gap" :min="0" :max="100"></slider>
</el-form-item>
<el-form-item label="标题同行">
<el-switch v-model="form.title_line" active-value="1" inactive-value="0" />
</el-form-item>
</card-container>
<div class="bg-f5 divider-line" />
</template>
<template v-if="['goods', 'custom'].includes(tabs_content.data_type)">
<div class="bg-f5 divider-line" />
<card-container>
<div class="mb-12">数据样式</div>
<el-form-item label="背景">
@ -92,9 +79,9 @@
<slider v-model="form.data_goods_gap" :min="0" :max="50"></slider>
</el-form-item>
</card-container>
<div class="bg-f5 divider-line" />
</template>
<template v-if="tabs_content.data_type === 'goods'">
<div class="bg-f5 divider-line" />
<card-container>
<div class="mb-12">商品样式</div>
<el-form-item label="名称">
@ -121,14 +108,31 @@
<radius :key="form.carouselKey" :value="form.goods_radius" @update:value="goods_radius_change"></radius>
</el-form-item>
</card-container>
<div class="bg-f5 divider-line" />
</template>
<template v-if="['goods', 'images'].includes(tabs_content.data_type)">
<div class="bg-f5 divider-line" />
<card-container>
<carousel-indicator :key="form.carouselKey" :value="form"></carousel-indicator>
</card-container>
<div class="bg-f5 divider-line" />
</template>
<card-container class="mb-8">
<div class="mb-12">通用样式</div>
<el-form-item label="底部背景">
<div class="flex-col gap-10 w">
<div class="size-12">背景色</div>
<mult-color-picker :key="form.carouselKey" :value="form.color_list" :type="form.direction" @update:value="mult_color_picker_event"></mult-color-picker>
<div class="flex-row jc-sb align-c">
<div class="size-12">背景图</div>
<bg-btn-style v-model="form.background_img_style"></bg-btn-style>
</div>
<upload v-model="form.background_img" :limit="1"></upload>
</div>
</el-form-item>
<el-form-item v-if="tabs_content.data_type != 'custom'" label="内间距">
<padding :key="form.carouselKey" :value="form.chunk_padding" @update:value="chunk_padding_change"></padding>
</el-form-item>
</card-container>
</template>
<script setup lang="ts">
import { pick, isEmpty, cloneDeep } from 'lodash';

View File

@ -46,7 +46,7 @@ const video = computed(() => {
}
});
//
const style_container = computed(() => padding_computer(props.dataStyle.chunk_padding) + radius_computer(props.value.img_radius));
const style_container = computed(() => padding_computer(props.dataStyle.chunk_padding) + radius_computer(props.dataStyle.img_radius));
</script>
<style lang="scss" scoped>
video {

View File

@ -101,59 +101,57 @@
</div>
</template>
<template v-else>
<el-carousel :key="carouselKey" indicator-position="none" :interval="interval_time" arrow="never" :autoplay="is_roll">
<el-carousel-item v-for="(item1, index1) in shop_content_list" :key="index1" class="flex-row" :style="onter_style">
<div v-for="(item, index) in item1.split_list" :key="index" class="re oh" :class="layout_type" :style="layout_style">
<div :class="['oh w h', ['0', '4'].includes(theme) ? 'flex-row' : 'flex-col' ]" :style="layout_img_style">
<template v-if="!isEmpty(item)">
<template v-if="!isEmpty(item.new_cover)">
<image-empty v-model="item.new_cover[0]" :class="`flex-img${theme}`" :style="content_img_radius"></image-empty>
</template>
<template v-else>
<image-empty v-model="item.images" :class="`flex-img${theme}`" :style="content_img_radius"></image-empty>
</template>
<swiper :key="carouselKey" class="w flex" direction="horizontal" :loop="true" :autoplay="autoplay" :slides-per-view="form.carousel_col" :slides-per-group="slides_per_group" :allow-touch-move="false" :space-between="content_outer_spacing" :pause-on-mouse-enter="true" :modules="modules">
<swiper-slide v-for="(item, index) in list" :key="index" :class="layout_type" :style="layout_style">
<div :class="['oh w h', ['0', '4'].includes(theme) ? 'flex-row' : 'flex-col' ]" :style="layout_img_style">
<template v-if="!isEmpty(item)">
<template v-if="!isEmpty(item.new_cover)">
<image-empty v-model="item.new_cover[0]" :class="`flex-img${theme}`" :style="content_img_radius"></image-empty>
</template>
<div v-if="is_show('title') || is_show('simple_desc') || is_show('price') || is_show('plugins_view_icon') || is_show('original_price') || form.is_shop_show == '1'" class="flex-col flex-1 jc-sb content gap-10" :style="content_style">
<div class="flex-col gap-10 top-title">
<div v-if="is_show('title') || (['0', '1', '2', '3', '5'].includes(theme) && is_show('simple_desc'))" class="flex-col" :style="`gap: ${ new_style.title_simple_desc_spacing }px;`">
<div v-if="is_show('title')" :class="text_line" :style="trends_config('title', 'title')">{{ item.title }}</div>
<div v-if="['0', '1', '2', '3', '5'].includes(theme) && is_show('simple_desc')" class="text-line-1" :style="trends_config('simple_desc', 'desc')">{{ item.simple_desc }}</div>
<template v-else>
<image-empty v-model="item.images" :class="`flex-img${theme}`" :style="content_img_radius"></image-empty>
</template>
</template>
<div v-if="is_show('title') || is_show('simple_desc') || is_show('price') || is_show('plugins_view_icon') || is_show('original_price') || form.is_shop_show == '1'" class="flex-col flex-1 jc-sb content gap-10" :style="content_style">
<div class="flex-col gap-10 top-title">
<div v-if="is_show('title') || (['0', '1', '2', '3', '5'].includes(theme) && is_show('simple_desc'))" class="flex-col" :style="`gap: ${ new_style.title_simple_desc_spacing }px;`">
<div v-if="is_show('title')" :class="text_line" :style="trends_config('title', 'title')">{{ item.title }}</div>
<div v-if="['0', '1', '2', '3', '5'].includes(theme) && is_show('simple_desc')" class="text-line-1" :style="trends_config('simple_desc', 'desc')">{{ item.simple_desc }}</div>
</div>
<div v-if="show_content && is_show('plugins_view_icon') && !isEmpty(item.plugins_view_icon_data)" class="flex-row gap-5 align-c">
<div v-for="(icon_data, icon_index) in item.plugins_view_icon_data" :key="icon_index" class="radius-sm size-9 pl-3 pr-3" :style="icon_style(icon_data)">{{ icon_data.name }}</div>
</div>
</div>
<div class="flex-row align-c jc-sb">
<div class="flex-row align-c nowrap">
<div v-if="is_show('price') && (!isEmpty(item.min_price) || typeof item.min_price == 'number')" class="num" :style="`color: ${new_style.shop_price_color}`">
<span class="identifying">{{ item.show_price_symbol }}</span
><span :style="trends_config('price')">{{ item.min_price }}</span>
<span v-if="is_show('price_unit')" class="identifying">{{ item.show_price_unit }}</span>
</div>
<div v-if="show_content && is_show('plugins_view_icon') && !isEmpty(item.plugins_view_icon_data)" class="flex-row gap-5 align-c">
<div v-for="(icon_data, icon_index) in item.plugins_view_icon_data" :key="icon_index" class="radius-sm size-9 pl-3 pr-3" :style="icon_style(icon_data)">{{ icon_data.name }}</div>
<div v-if="show_content && is_show('original_price') && (!isEmpty(item.min_original_price) || typeof item.min_original_price == 'number')" class="size-10 flex">
<!-- <img class="original-price-left" :src="form.static_img[0].url" /> -->
<span :class="['original-price text-line-1', { 'flex-1': form.is_price_solo == '1' }]"
>{{ item.show_original_price_symbol }}{{ item.min_original_price }}
<template v-if="is_show('original_price_unit')">
{{ item.show_original_price_unit }}
</template>
</span>
</div>
</div>
<div class="flex-row align-c jc-sb">
<div class="flex-row align-c nowrap">
<div v-if="is_show('price') && (!isEmpty(item.min_price) || typeof item.min_price == 'number')" class="num" :style="`color: ${new_style.shop_price_color}`">
<span class="identifying">{{ item.show_price_symbol }}</span
><span :style="trends_config('price')">{{ item.min_price }}</span>
<span v-if="is_show('price_unit')" class="identifying">{{ item.show_price_unit }}</span>
</div>
<div v-if="show_content && is_show('original_price') && (!isEmpty(item.min_original_price) || typeof item.min_original_price == 'number')" class="size-10 flex">
<!-- <img class="original-price-left" :src="form.static_img[0].url" /> -->
<span :class="['original-price text-line-1', { 'flex-1': form.is_price_solo == '1' }]"
>{{ item.show_original_price_symbol }}{{ item.min_original_price }}
<template v-if="is_show('original_price_unit')">
{{ item.show_original_price_unit }}
</template>
</span>
</div>
</div>
<div v-if="form.is_shop_show == '1'">
<template v-if="form.shop_type == 'text'">
<div class="plr-11 ptb-3 round cr-f" :style="trends_config('button', 'gradient') + `color: ${new_style.shop_button_text_color};`">{{ form.shop_button_text }}</div>
</template>
<template v-else>
<icon class="round plr-6 ptb-5" :name="!isEmpty(form.shop_button_icon_class) ? form.shop_button_icon_class : 'cart'" :color="new_style.shop_icon_color" :size="new_style.shop_icon_size + ''" :styles="button_gradient()"></icon>
</template>
</div>
<div v-if="form.is_shop_show == '1'">
<template v-if="form.shop_type == 'text'">
<div class="plr-11 ptb-3 round cr-f" :style="trends_config('button', 'gradient') + `color: ${new_style.shop_button_text_color};`">{{ form.shop_button_text }}</div>
</template>
<template v-else>
<icon class="round plr-6 ptb-5" :name="!isEmpty(form.shop_button_icon_class) ? form.shop_button_icon_class : 'cart'" :color="new_style.shop_icon_color" :size="new_style.shop_icon_size + ''" :styles="button_gradient()"></icon>
</template>
</div>
</div>
</div>
</div>
</el-carousel-item>
</el-carousel>
</swiper-slide>
</swiper>
</template>
</div>
</div>
@ -163,6 +161,10 @@
import { common_img_computer, common_styles_computer, get_math, gradient_handle, padding_computer, radius_computer, background_computer } from '@/utils';
import { isEmpty, cloneDeep, throttle } from 'lodash';
import ShopAPI from '@/api/shop';
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Autoplay } from 'swiper/modules';
import 'swiper/css';
const modules = [Autoplay];
const props = defineProps({
value: {
@ -327,7 +329,6 @@ const onter_style = computed(() => {
const radius = theme.value == '6' ? content_radius.value : `gap: ${new_style.value.content_outer_spacing + 'px'};`;
return `${radius}`;
});
//
const layout_type = computed(() => {
let class_type = '';
@ -455,9 +456,6 @@ const style_img_container = computed(() => {
return '';
}
});
//#region
//
//
const multicolumn_columns_width = computed(() => {
const { carousel_col } = toRefs(form.value);
@ -501,61 +499,26 @@ const goods_img_height = computed(() => {
}
}
});
interface nav_list {
split_list: data_list[];
}
const shop_content_list = computed(() => {
//
const cloneList = cloneDeep(list.value);
//
if (cloneList.length > 0) {
//
const num = form.value.carousel_col;
//
let nav_list: nav_list[] = [];
//
const split_num = Math.ceil(cloneList.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({ split_list: cloneList.slice(i * num, (i + 1) * num) });
}
return nav_list;
} else {
//
return [{ split_list: cloneList }];
}
});
//
const interval_time = ref(2000);
//
const is_roll = ref(true);
//#region
// key
const carouselKey = ref('0');
const interval_list = ref({
time: 2000,
is_roll: '1',
notice_length: 1,
});
const autoplay = ref<boolean | object>(false);
const slides_per_group = ref(1);
//
watchEffect(() => {
const time = (new_style.value.interval_time || 2) * 1000;
const display_is_roll = new_style.value.is_roll;
//
const notice_length = shop_content_list.value.length;
//
if (interval_list.value.time != time || interval_list.value.is_roll != display_is_roll || notice_length != interval_list.value.notice_length) {
//
interval_time.value = time;
//
is_roll.value = display_is_roll == '1' ? true : false;
//
interval_list.value = {
time: time,
is_roll: display_is_roll,
notice_length: notice_length,
//
if (new_style.value.is_roll == '1' && list.value.length > 0) {
autoplay.value = {
delay: (new_style.value.interval_time || 2) * 1000,
pauseOnMouseEnter: true,
};
// key
carouselKey.value = get_math();
} else {
autoplay.value = false;
}
//
slides_per_group.value = new_style.value.rolling_fashion == 'translation' ? 1 : form.value.carousel_col;
// key
carouselKey.value = get_math();
});
//#endregion
</script>
@ -623,7 +586,7 @@ watchEffect(() => {
:deep(.el-carousel) {
width: 100%;
.el-carousel__container {
height: v-bind(content_outer_height);
min-width: v-bind(multicolumn_columns_width);
}
}
</style>

View File

@ -10,10 +10,10 @@
</el-form-item>
<el-form-item v-if="form.theme == '5'" label="轮播列数">
<el-radio-group v-model="form.carousel_col">
<el-radio :value="1">1</el-radio>
<el-radio :value="2">2</el-radio>
<el-radio :value="3">3</el-radio>
<el-radio :value="4">4</el-radio>
<el-radio :value="1">单列展示</el-radio>
<el-radio :value="2">两列展示</el-radio>
<el-radio :value="3">三列展示</el-radio>
<el-radio :value="4">四列展示</el-radio>
</el-radio-group>
</el-form-item>
</card-container>

View File

@ -78,9 +78,17 @@
<el-form-item label="自动轮播">
<el-switch v-model="form.is_roll" active-value="1" inactive-value="0" />
</el-form-item>
<el-form-item v-if="form.is_roll == '1'" label="间隔时间">
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
</el-form-item>
<template v-if="form.is_roll == '1'">
<el-form-item label="间隔时间">
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
</el-form-item>
<el-form-item label="滚动方式">
<el-radio-group v-model="form.rolling_fashion">
<el-radio value="translation">平移</el-radio>
<el-radio value="cut-screen">切屏</el-radio>
</el-radio-group>
</el-form-item>
</template>
</card-container>
<div class="divider-line"></div>
</template>

View File

@ -23,10 +23,10 @@
</el-form-item>
<el-form-item v-if="form.theme == '5'" label="轮播列数">
<el-radio-group v-model="form.carousel_col">
<el-radio :value="1">1</el-radio>
<el-radio :value="2">2</el-radio>
<el-radio :value="3">3</el-radio>
<el-radio :value="4">4</el-radio>
<el-radio :value="1">单列展示</el-radio>
<el-radio :value="2">两列展示</el-radio>
<el-radio :value="3">三列展示</el-radio>
<el-radio :value="4">四列展示</el-radio>
</el-radio-group>
</el-form-item>
</card-container>

View File

@ -97,9 +97,17 @@
<el-form-item label="自动轮播">
<el-switch v-model="form.is_roll" active-value="1" inactive-value="0" />
</el-form-item>
<el-form-item v-if="form.is_roll == '1'" label="间隔时间">
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
</el-form-item>
<template v-if="form.is_roll == '1'">
<el-form-item label="间隔时间">
<slider v-model="form.interval_time" :min="1" :max="100"></slider>
</el-form-item>
<el-form-item label="滚动方式">
<el-radio-group v-model="form.rolling_fashion">
<el-radio value="translation">平移</el-radio>
<el-radio value="cut-screen">切屏</el-radio>
</el-radio-group>
</el-form-item>
</template>
</card-container>
<div class="divider-line"></div>
</template>

View File

@ -10,11 +10,11 @@
<el-radio value="text">文字</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="单行显示">
<el-form-item label="显示设置">
<el-radio-group v-model="form.single_line">
<el-radio :value="3">3</el-radio>
<el-radio :value="4">4</el-radio>
<el-radio :value="5">5</el-radio>
<el-radio :value="3">三列展示</el-radio>
<el-radio :value="4">四列展示</el-radio>
<el-radio :value="5">五列展示</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="展示样式">

View File

@ -44,12 +44,12 @@
<el-radio v-for="item in base_list.shop_style_type_list" :key="item.value" :value="item.value">{{ item.name }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.shop_style_type == '3'" label="单行显示">
<el-form-item v-if="form.shop_style_type == '3'" label="显示设置">
<el-radio-group v-model="form.carousel_col">
<el-radio :value="1">1</el-radio>
<el-radio :value="2">2</el-radio>
<el-radio :value="3">3</el-radio>
<el-radio :value="4">4</el-radio>
<el-radio :value="1">单列展示</el-radio>
<el-radio :value="2">两列展示</el-radio>
<el-radio :value="3">三列展示</el-radio>
<el-radio :value="4">四列展示</el-radio>
</el-radio-group>
</el-form-item>
</card-container>

View File

@ -51,6 +51,7 @@ interface DefaultArticleList {
content_img_height: number | undefined;
interval_time: number; //滚动时间
is_roll: number;
rolling_fashion: string;
common_style: object;
};
}
@ -118,6 +119,7 @@ const defaultArticleList: DefaultArticleList = {
content_img_height: undefined,
interval_time: 3, //滚动时间
is_roll: 1, // 是否轮播
rolling_fashion: 'translation', // 滚动方式 translation 平移 cut-screen 切屏
common_style: { ...defaultCommon, padding: 10, padding_top: 10, padding_bottom: 10, padding_left: 10, padding_right: 10 },
},
};

View File

@ -70,6 +70,7 @@ interface DefaultArticleTabs {
content_img_height: number | undefined;
interval_time: number; //滚动时间
is_roll: number;
rolling_fashion: string;
common_style: object;
};
}
@ -160,6 +161,7 @@ const defaultArticleTabs: DefaultArticleTabs = {
content_img_height: undefined,
interval_time: 3, //滚动时间
is_roll: 1, // 是否轮播
rolling_fashion: 'translation', // 滚动方式 translation 平移 cut-screen 切屏
common_style: { ...defaultCommon, padding: 10, padding_top: 10, padding_left: 10, padding_right: 10, padding_bottom: 10 },
},
};

View File

@ -1,20 +1,4 @@
import defaultCommon from "./index";
interface content {
data_ids: Array<string>;
data_list: Array<object>;
data_auto_list: Array<object>;
}
interface defaultSearch {
content: {
height: number;
data_source: string;
data_source_content: content;
custom_list: string[];
};
style: {
common_style: object;
};
}
export const source_list = {
goods: {
// 存放手动输入的id
@ -85,6 +69,35 @@ export const source_list = {
data_auto_list: [],
}
};
interface content {
data_ids: Array<string>;
data_list: Array<object>;
data_auto_list: Array<object>;
}
interface defaultSearch {
content: {
height: number;
data_source: string;
data_source_content: content;
data_source_direction: string;
data_source_carousel_col: number;
custom_list: string[];
};
style: {
is_roll: string;
interval_time: number;
rolling_fashion: string;
is_show: boolean;
actived_color: string;
color: string;
indicator_style: string;
indicator_bottom: number;
indicator_location: string;
indicator_size: number;
indicator_radius: radiusStyle;
common_style: object;
};
}
const defaultSearch: defaultSearch = {
content: {
// 自定义内容高度
@ -100,10 +113,33 @@ const defaultSearch: defaultSearch = {
},
// 数据源类型 商品(goods) 文章(article) 品牌(brand) 用户信息(user-info)
data_source:'',
// 铺满方式 0 横向 1 纵向
data_source_direction: '1',
// 横向滑动时的显示 轮播数量
data_source_carousel_col: 1,
// 自定义内容列表
custom_list: []
},
style: {
is_roll: '0',
interval_time: 3,
rolling_fashion: 'translation',
is_show: false,
// 指示器选中颜色
actived_color: '#2A94FF',
// 常规颜色
color: '#DDDDDD',
indicator_style: 'dot',
indicator_location: 'center',
indicator_size: 5,
indicator_bottom: 6,
indicator_radius: {
radius: 4,
radius_top_left: 4,
radius_top_right: 4,
radius_bottom_left: 4,
radius_bottom_right: 4,
},
common_style: {
...defaultCommon,
color_list: [{ color: 'rgb(244, 252, 255)', color_percentage: undefined }],

View File

@ -40,6 +40,7 @@ interface DefaultProductList {
content_img_height: number | undefined;
is_roll: string,
interval_time: number,
rolling_fashion: string;
content_spacing: number;
shop_title_typeface: string;
shop_title_size: number;
@ -143,6 +144,7 @@ const defaultProductList: DefaultProductList = {
// 是否滚动
is_roll: '1',
interval_time: 3,
rolling_fashion: 'translation', // 滚动方式 translation 平移 cut-screen 切屏
// 商品内容大小和颜色设置
shop_title_typeface: '500',
shop_title_size: 14,

View File

@ -61,6 +61,7 @@ interface DefaultProductList {
content_img_height: number | undefined;
is_roll: string;
interval_time: number;
rolling_fashion: string;
content_spacing: number;
shop_title_typeface: string;
shop_title_size: number;
@ -185,6 +186,7 @@ const defaultProductList: DefaultProductList = {
// 轮播处理
is_roll: '1',
interval_time: 3,
rolling_fashion: 'translation', // 滚动方式 translation 平移 cut-screen 切屏
// 商品标题等内容处理
shop_title_typeface: '500',
shop_title_size: 14,

View File

@ -210,7 +210,7 @@ const defaultSeckill: DefaultSeckill = {
// 轮播设置
is_roll: '1',
interval_time: 3,
rolling_fashion: 'translation',
rolling_fashion: 'translation', // 滚动方式 translation 平移 cut-screen 切屏
common_style: { ...defaultCommon, padding: 0, padding_top: 2, padding_bottom: 10, padding_left: 10, padding_right: 10 },
},
};