源文件

This commit is contained in:
gyq
2025-04-25 09:49:53 +08:00
commit 791d82b9e3
640 changed files with 130029 additions and 0 deletions

View File

@@ -0,0 +1,430 @@
<template>
<!-- 文章列表 -->
<view class="oh" :style="style_container">
<view class="oh" :style="style_img_container">
<view class="pr oh" :style="style">
<view v-if="!['4'].includes(article_theme)" class="flex-wrap" :class="article_theme_class" :style="article_theme !== '3' ? article_spacing : ''">
<view v-for="(item, index) in data_list" :key="index" class="item oh" :style="article_style" :data-value="item.data.url" @tap="url_event">
<view :class="article_theme == '0' ? 'flex-row oh' : 'flex-col oh ht-auto'" :style="article_img_style">
<template v-if="article_theme !== '3'">
<view class="oh pr flex-row">
<template v-if="item.new_cover.length > 0">
<image :src="item.new_cover[0].url" class="img" :style="img_radius + img_size" mode="aspectFill" />
</template>
<template v-else>
<image :src="item.data.cover" class="img" :style="img_radius + img_size" mode="aspectFill" />
</template>
<!-- 角标 -->
<subscriptIndex :propValue="propValue"></subscriptIndex>
</view>
</template>
<view v-if="field_show.includes('0') || field_show.includes('1') || field_show.includes('2') || field_show.includes('3')" class="jc-sb flex-1" :class="article_theme == '3' ? 'flex-row align-c' : 'flex-col'" :style="article_theme !== '0' ? content_padding : ''">
<view class="flex-col" :class="article_theme == '3' ? 'flex-1 flex-width' : ''" :style="'gap:' + name_desc_space + 'px;'">
<view v-if="field_show.includes('3')" class="title" :class="article_theme == '3' ? 'text-line-1' : 'text-line-2'" :style="article_name">{{ item.new_title ? item.new_title : item.data.title }}</view>
<view v-if="field_show.includes('2')" :class="'desc ' + field_desc_row == '2' ? 'text-line-2' : 'text-line-1'" :style="article_desc">{{ item.data.describe || '' }}</view>
</view>
<view class="flex-row jc-sb gap-8" :class="article_theme == '3' ? 'margin-left' : 'align-e margin-top'">
<view :style="article_date">{{ field_show.includes('0') ? item.data.add_time : '' }}</view>
<view v-show="field_show.includes('1')" class="flex-row align-c gap-3" :style="article_page_view">
<iconfont name="icon-eye" propContainerDisplay="flex"></iconfont>
<view>
{{ item.data.access_count ? item.data.access_count : '' }}
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view v-else class="oh" :class="article_theme_class">
<swiper class="swiper" circular :autoplay="is_roll ? true : false" :interval="interval_time" :next-margin="next_margin" :display-multiple-items="slides_per_group" :style="'height:' + carousel_height_computer">
<swiper-item v-for="(item1, index1) in article_carousel_list" :key="index1">
<view class="flex-row ht-auto" :style="article_spacing">
<view v-for="(item, index) in item1.carousel_list" :key="index" class="item oh ht-auto" :style="article_style" :data-value="item.data.url" @tap="url_event">
<view class="oh flex-col ht-auto" :style="article_img_style">
<view class="oh pr wh-auto ht-auto flex-row">
<template v-if="item.new_cover.length > 0">
<image :src="item.new_cover[0].url" class="img" :style="img_radius + 'height:100%;'" mode="aspectFill" />
</template>
<template v-else>
<image :src="item.data.cover" class="img" :style="img_radius + 'height:100%;'" mode="aspectFill" />
</template>
<template v-if="field_show.includes('3') && name_float == '1'">
<view class="text-line-1" :style="article_name + float_name_style">{{ item.new_title ? item.new_title : item.data.title }}</view>
</template>
<!-- 角标 -->
<subscriptIndex :propValue="propValue"></subscriptIndex>
</view>
<view v-if="field_show.includes('0') || field_show.includes('1') || field_show.includes('2') || (field_show.includes('3') && name_float == '0')" class="jc-sb flex-1 flex-col" :style="article_theme !== '0' ? content_padding : ''">
<view class="flex-col" :style="'gap:' + name_desc_space + 'px;'">
<view v-if="field_show.includes('3') && name_float == '0'" class="title text-line-2" :style="article_name + article_name_height_computer">{{ item.new_title ? item.new_title : item.data.title }}</view>
<view v-if="field_show.includes('2')" :class="'desc ' + field_desc_row == '2' ? 'text-line-2' : 'text-line-1'" :style="article_desc">{{ item.data.describe || '' }}</view>
</view>
<view :class="'flex-row jc-sb gap-8 align-e' + ((field_show.includes('3') && name_float == '0') || field_show.includes('2') ? ' margin-top' : '')">
<view :style="article_date">{{ field_show.includes('0') ? item.data.add_time : '' }}</view>
<view v-show="field_show.includes('1')" class="flex-row align-c gap-3" :style="article_page_view">
<iconfont name="icon-eye" propContainerDisplay="flex"></iconfont>
<view>
{{ item.data.access_count ? item.data.access_count : '' }}
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { isEmpty, common_styles_computer, common_img_computer, padding_computer, radius_computer, get_math, gradient_handle, background_computer, gradient_computer, margin_computer, box_shadow_computer, border_computer, old_margin } from '@/common/js/common/common.js';
import subscriptIndex from '@/components/diy/modules/subscript/index.vue';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
components: {
subscriptIndex,
},
props: {
propValue: {
type: Object,
default: () => {},
},
// 是否使用公共样式
propIsCommonStyle: {
type: Boolean,
default: true,
},
// 关键key
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
// 数据
data_list: [],
// 风格
article_theme: '0',
// 是否显示
field_show: ['0', '1'],
// 文章
article_name: '',
// 描述
article_desc: '',
// 日期
article_date: '',
// 浏览量
article_page_view: '',
// 内容圆角
content_radius: '',
// 图片圆角
img_radius: '',
// 内间距
content_padding: '',
// 内容间距
content_spacing: '',
// 文章间距
article_spacing: '',
// article_item_height: '',
article_style: '',
article_img_style: '',
// 轮播图定时轮播
interval_time: 2000,
// 轮播图是否滚动
is_roll: 1,
article_theme_class: '',
// 轮播高度
carousel_height_computer: '',
// 文章内容高度
article_name_height_computer: '',
// 文章名称浮动样式
float_name_style: '',
name_float: '0',
// 图片大小
img_size: '',
// 文章轮播数据
article_carousel_list: [],
// 文章描述间距
name_desc_space: 0,
// 一行显示的数量
slides_per_group: 1,
next_margin: '0rpx',
field_desc_row: '1',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
// 描述样式
const desc_size = new_style.desc_size;
let desc_style = 'font-size:' + desc_size + 'px;line-height:' + desc_size + 'px;height:' + desc_size + 'px;color:' + new_style.desc_color + ';';
if (new_content.field_desc_row == '2') {
desc_style = 'font-size:' + desc_size + 'px;line-height:' + (desc_size > 0 ? desc_size + 3 : 0 ) + 'px;height:'+ (desc_size > 0 ? (desc_size + 3) * 2 : 0) + 'px;color:' + new_style.desc_color + ';';
}
this.setData({
field_desc_row: new_content.field_desc_row,
name_float: !isEmpty(new_content.name_float) ? new_content.name_float : '0',
// 判断是自动还是手动
data_list:
new_content.data_type == '0'
? new_content.data_list
: new_content.data_auto_list && new_content.data_auto_list.length > 0
? new_content.data_auto_list.map((item) => ({
id: get_math(),
new_title: '',
new_cover: [],
data: item,
}))
: [],
article_theme_class: this.article_theme_class_computer(new_content.theme),
article_theme: new_content.theme,
field_show: new_content.field_show,
// 样式
article_name: 'font-size:' + new_style.name_size + 'px;' + 'font-weight:' + new_style.name_weight + ';' + 'color:' + new_style.name_color + ';',
article_desc: desc_style,
article_date: 'font-size:' + new_style.time_size + 'px;' + 'font-weight:' + new_style.time_weight + ';' + 'color:' + new_style.time_color + ';',
article_page_view: 'font-size:' + new_style.page_view_size + 'px;' + 'font-weight:' + new_style.page_view_weight + ';' + 'color:' + new_style.page_view_color + ';',
content_radius: radius_computer(new_style.content_radius),
img_radius: radius_computer(new_style.img_radius),
// 内间距
content_padding: padding_computer(new_style.padding),
// 内容间距
content_spacing: `gap: ${new_style.content_spacing}px;`,
// 文章间距
article_spacing: `gap: ${new_style.article_spacing}px;`,
// 描述间距
name_desc_space: parseInt(new_style.name_desc_space),
next_margin: new_style.rolling_fashion == 'translation' ? '-' + new_style.article_spacing_margin + 'px' : '0rpx',
// 文章内容高度
slides_per_group: new_style.rolling_fashion == 'translation' ? Number(new_content.carousel_col) + 1 : 1,
});
// 默认数据
const product_style_list = [
{ name: '单列展示', value: '0', width:110, height: 83 },
{ name: '两列展示(纵向)', value: '1', width:180, height: 180 },
{ name: '大图展示', value: '2', width:0, height: 180 },
{ name: '无图模式', value: '3', width:0, height: 0 },
{ name: '左右滑动展示', value: '4', width:0, height: 0 },
];
const scale = sys_width / 390;
let img_style = ``;
if (['0'].includes(new_content.theme)) {
// 图片宽度
if (typeof new_style.content_img_width == 'number') {
img_style += `width: ${ new_style.content_img_width * scale }px;`;
} else {
const list = product_style_list.filter(item => item.value == new_content.theme);
if (list.length > 0) {
img_style += `width: ${ list[0].width * scale }px;`;
} else {
img_style += 'width: auto;';
}
}
}
if (!['3', '4'].includes(new_content.theme)) {
// 图片宽度
if (typeof new_style.content_img_height == 'number') {
img_style += `height: ${ new_style.content_img_height * scale }px;`;
} else {
const list = product_style_list.filter(item => item.value == new_content.theme);
if (list.length > 0) {
img_style += `height: ${ list[0].height * scale }px;`;
} else {
img_style += 'height: auto;';
}
}
}
// 背景图的处理
const article_data = {
background_img_style: new_style?.article_background_img_style || '',
background_img: new_style?.article_background_img || '',
}
const article_margin = new_style.value?.margin || old_margin;
const margin_width = article_margin.margin_left + article_margin.margin_right;
// 渐变效果
const all_style = gradient_handle(new_style?.article_color_list || [], new_style?.article_direction || '') + margin_computer(article_margin) + box_shadow_computer(new_style) + border_computer(new_style);
// 文章样式
if (this.article_theme == '0') {
this.setData({
img_size: img_style,
article_style: this.content_radius + all_style,
article_img_style: this.content_spacing + this.content_padding + background_computer(article_data)
});
} else if (this.article_theme == '1') {
this.setData({
img_size: img_style,
article_style: `width: calc(50% - ${new_style.article_spacing + (margin_width * 2) / 2}px);` + this.content_radius + all_style,
article_img_style: background_computer(article_data)
});
} else if (this.article_theme == '2') {
this.setData({
img_size: img_style,
article_style: this.content_radius + all_style,
article_img_style: background_computer(article_data)
});
} else if (this.article_theme == '3') {
this.setData({
style: `padding: 0 ${new_style.content_spacing}px;background:#fff;` + this.content_radius,
});
} else if (this.article_theme == '4') {
// 更新轮播图的key确保更换时能重新更新轮播图
const temp_carousel_col = new_content.carousel_col || '1';
// 计算间隔的空间。(gap * gap数量) / 模块数量
let gap = temp_carousel_col !== '0' ? ((new_style.article_spacing * temp_carousel_col - 1) + (margin_width * temp_carousel_col)) / temp_carousel_col : '0';
const multicolumn_columns_width = new_style.rolling_fashion == 'translation' ? `margin-right: ${ new_style.article_spacing + article_margin.margin_right }px;width:100%;` : `width:calc(${100 / (Number(temp_carousel_col) + 1)}% - ${gap * 2}rpx);min-width:calc(${100 / (Number(temp_carousel_col) + 1)}% - ${gap * 2}rpx);`;
const { name_bg_color_list = [], name_bg_direction = '180deg', name_bg_radius, name_bg_padding, name_bg_margin } = new_style;
const data = {
color_list: name_bg_color_list,
direction: name_bg_direction,
}
let location = 'position:absolute;bottom:0;left:0;right:0;'
// 轮播宽度
this.setData({
// 滚动时间
interval_time: (new_style.interval_time || 2) * 1000,
// 是否滚动修改
is_roll: new_style.is_roll,
// article_item_height: `height: ${new_style.article_height }px`,
article_style: this.content_radius + all_style + multicolumn_columns_width,
// 轮播高度
carousel_height_computer: new_style.article_height * scale + 'px',
// 文章内容高度
article_name_height_computer: `height:${new_style.name_size * 2.4 * 2}rpx;line-height:${new_style.name_size * 1.2 * 2}rpx;`,
float_name_style: gradient_computer(data) + (!isEmpty(name_bg_radius) ? radius_computer(name_bg_radius) : '') + (!isEmpty(name_bg_padding) ? padding_computer(name_bg_padding) : '' ) + (!isEmpty(name_bg_padding) ? margin_computer(name_bg_margin) : '') + location,
article_img_style: background_computer(article_data)
});
// 文章轮播数据
const cloneList = JSON.parse(JSON.stringify(this.data_list));
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (new_style.rolling_fashion != 'translation') {
if (cloneList.length > 0) {
// 每页显示的数量
const num = Number(temp_carousel_col) + 1;
// 存储数据显示
let nav_list = [];
// 拆分的数量
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) });
}
this.setData({
article_carousel_list: nav_list,
});
} else {
// 否则的话,就返回全部的信息
this.setData({
article_carousel_list: [{ carousel_list: cloneList }],
});
}
} else {
// 存储数据显示
let nav_list = [];
cloneList.forEach((item) => {
nav_list.push({
carousel_list: [item],
});
});
this.setData({
article_carousel_list: nav_list,
});
}
}
if (this.propIsCommonStyle) {
this.setData({
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
}
},
// 文章主题样式
article_theme_class_computer(theme) {
switch (theme) {
case '0':
return 'style1 flex-col';
case '1':
return 'style2 flex-row flex-wrap';
case '2':
return 'style3 flex-col';
case '3':
return 'style4 flex-col';
default:
return 'style5';
}
},
// 跳转链接
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style lang="scss" scoped>
.style1 {
.item {
max-width: 100%;
}
}
.style2 {
.item {
.img {
width: 100%;
}
}
}
.style3 {
.item {
width: 100%;
.img {
width: 100%;
}
}
}
.style4 {
.item {
width: 100%;
&:not(:last-child) {
border-bottom: 2rpx solid #eee;
}
}
}
.style5 {
.item {
width: 100%;
.img {
width: 100%;
}
}
}
</style>

View File

@@ -0,0 +1,264 @@
<template>
<!-- 文章列表 -->
<view class="article-tabs ou" :class="'article-tabs-' + propKey" :style="style_container">
<view class="ou" :style="style_img_container">
<componentDiyModulesTabsView :propKey="propKey" :propValue="article_tabs" :propIsTop="top_up == '1'" :propTop="sticky_top" :propStyle="tabs_style" :propsTabsContainer="tabs_container" :propsTabsImgContainer="tabs_img_container" :propCustomNavHeight="propIsTabsUseSafeDistance ? (propCustomNavHeight * 2 + 'rpx') : '0rpx'" :propTabsBackground="tabs_background" :propTabsSlidingFixedBg="tabs_sliding_fixed_bg" @onTabsTap="tabs_click_event"></componentDiyModulesTabsView>
<view :style="article_margin_top">
<view :style="article_container">
<view :style="article_img_container">
<componentDiyArticleList :propKey="diy_key" :propValue="article_tabs" :propIsCommonStyle="false"></componentDiyArticleList>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, padding_computer, margin_computer, background_computer, gradient_computer, radius_computer, isEmpty, box_shadow_computer, border_computer, old_border_and_box_shadow, old_margin, old_padding } from '@/common/js/common/common.js';
import componentDiyModulesTabsView from '@/components/diy/modules/tabs-view';
import componentDiyArticleList from '@/components/diy/article-list'; // 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
props: {
propValue: {
type: Object,
default: () => {},
},
// 距离顶部高度
propTop: {
type: Number,
default: 0,
},
// 自定义导航栏高度
propCustomNavHeight: {
type: Number,
default: 33,
},
// 滚动距离
propScrollTop: {
type: Number,
default: 0,
},
// 顶部导航是否开启沉浸模式
propIsImmersionModel: {
type: Boolean,
default: false,
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
// 选项卡是否使用安全距离
propIsTabsUseSafeDistance: {
type: Boolean,
default: true
}
},
components: {
componentDiyModulesTabsView,
componentDiyArticleList,
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
article_tabs: {},
// 是否滑动置顶
top_up: '0',
tabs_style: '',
tabs_top: 0,
tabs_background: 'background:transparent',
custom_nav_height: 33,
diy_key: '',
// 选项卡背景设置
article_margin_top: '',
tabs_container: '',
tabs_img_container: '',
tabs_sliding_fixed_bg: '',
// 商品区域背景设置
article_container: '',
article_img_container: '',
// #ifdef MP
nav_safe_space: bar_height + 5,
// #endif
// #ifdef H5 || MP-TOUTIAO
nav_safe_space: bar_height + 7,
// #endif
// #ifdef APP
nav_safe_space: bar_height + 0,
// #endif
// 选项卡默认数据
tabs_index: 0,
sticky_top: 0,
};
},
watch: {
// 监听滚动距离
propScrollTop(newVal) {
if (newVal + this.sticky_top + this.custom_nav_height > this.tabs_top + this.nav_safe_space && this.top_up == '1') {
let new_style = this.propValue.style || {};
let tabs_bg = new_style.common_style.color_list;
let new_tabs_background = '';
if (tabs_bg.length > 0 && (tabs_bg[0].color || null) != null) {
new_tabs_background = gradient_computer(new_style.common_style);
}
let new_tabs_background_img = background_computer(new_style.common_style);
if (new_tabs_background_img.length > 0) {
new_tabs_background_img += 'background-position: top left;';
}
this.tabs_background = (new_tabs_background.length > 0 ? new_tabs_background : 'background:#fff;') + new_tabs_background_img;
} else {
this.tabs_background = 'background:transparent';
}
},
propTop(val) {
this.init();
},
propKey(val) {
this.setData({
diy_key: val,
});
// 初始化
this.init();
},
},
created() {
this.init();
},
mounted() {
this.$nextTick(() => {
const self = this;
setTimeout(() => {
self.getTop();
});
// #ifdef H5 || MP-TOUTIAO
// 获取自定义导航栏高度
this.setData({
custom_nav_height: this.propCustomNavHeight,
});
// #endif
});
},
methods: {
// 初始化数据
init() {
let new_content = this.propValue.content || {};
let new_style = this.propValue.style || {};
let new_data = JSON.parse(JSON.stringify(this.propValue));
const new_tabs_data = new_data.content.tabs_list[this.tabs_index] || {};
new_data.content.theme = new_data.content.article_theme;
new_data.content.data_type = new_tabs_data.data_type;
new_data.content.category = new_tabs_data.category;
new_data.content.carousel_col = new_data.content.article_carousel_col;
new_data.content.data_list = new_tabs_data.data_list;
new_data.content.data_auto_list = new_tabs_data.data_auto_list;
new_data.content.data_ids = new_tabs_data.data_ids;
new_data.content.number = new_tabs_data.number;
new_data.content.sort = new_tabs_data.sort;
new_data.content.sort_rules = new_tabs_data.sort_rules;
new_data.content.field_show = new_data.content.field_show;
new_data.content.is_cover = new_tabs_data.is_cover;
// 公共样式
const common_style = new_style.common_style;
let tabs_style_obj = {
padding_top: common_style.padding_top - this.propCustomNavHeight < 0 ? 0 : common_style.padding_top - this.propCustomNavHeight,
padding_left: common_style.padding_left,
padding_right: common_style.padding_right,
};
let new_tabs_style = padding_computer(tabs_style_obj) + margin_computer(tabs_style_obj) + `position:relative;left: -${tabs_style_obj.padding_left * 2}rpx;right: -${tabs_style_obj.padding_right * 2}rpx;width:100%;`;
// 如果是历史数据的话,就执行默认添加下边距
if (isEmpty(new_style.tabs_padding)) {
new_tabs_style += 'padding-bottom: 20rpx;';
}
const { tabs_bg_color_list = [], tabs_bg_direction = '', tabs_bg_background_img_style = '', tabs_bg_background_img = [], tabs_radius = old_radius, tabs_padding = old_padding, article_content_color_list = [], article_content_direction = '', article_content_background_img_style = '', article_content_background_img = [], article_content_margin = old_margin, article_content_padding = old_padding, article_content_radius = old_radius } = new_style;
// 选项卡背景设置
const tabs_data = {
color_list: tabs_bg_color_list,
direction: tabs_bg_direction,
background_img_style: tabs_bg_background_img_style,
background_img: tabs_bg_background_img,
};
// 文章区域背景设置
const article_content_data = {
color_list: article_content_color_list,
direction: article_content_direction,
background_img_style: article_content_background_img_style,
background_img: article_content_background_img,
};
const article_content = new_style?.article_content || old_border_and_box_shadow;
const tabs_content = new_style?.tabs_content || old_border_and_box_shadow;
this.setData({
top_up: new_content.tabs_top_up,
sticky_top: this.propTop + (new_style?.tabs_margin?.margin_top || 0),
article_tabs: new_data,
style_container: common_styles_computer(common_style),
style_img_container: common_img_computer(common_style, this.propIndex),
tabs_style: new_tabs_style,
article_margin_top: 'margin-top:' + (new_style?.article_content_spacing || 0) * 2 + 'rpx',
tabs_sliding_fixed_bg: gradient_computer(tabs_data),
tabs_container: gradient_computer(tabs_data) + radius_computer(tabs_radius) + margin_computer(new_style?.tabs_margin || old_margin) + border_computer(tabs_content) + box_shadow_computer(tabs_content) + 'overflow: hidden;',
tabs_img_container: background_computer(tabs_data) + padding_computer(tabs_padding) + 'box-sizing: border-box;overflow: hidden;',
article_container: gradient_computer(article_content_data) + margin_computer(article_content_margin) + radius_computer(article_content_radius) + box_shadow_computer(article_content) + border_computer(article_content) + 'overflow: hidden;',
article_img_container: background_computer(article_content_data) + padding_computer(article_content_padding) + 'box-sizing: border-box;overflow: hidden;',
});
},
// tabs切换事件
tabs_click_event(index) {
let new_data = JSON.parse(JSON.stringify(this.propValue));
new_data.content.theme = new_data.content.article_theme;
new_data.content.data_type = new_data.content.tabs_list[index].data_type;
new_data.content.category = new_data.content.tabs_list[index].category;
new_data.content.carousel_col = new_data.content.article_carousel_col;
new_data.content.data_list = new_data.content.tabs_list[index].data_list;
new_data.content.data_auto_list = new_data.content.tabs_list[index].data_auto_list;
new_data.content.data_ids = new_data.content.tabs_list[index].data_ids;
new_data.content.number = new_data.content.tabs_list[index].number;
new_data.content.sort = new_data.content.tabs_list[index].sort;
new_data.content.sort_rules = new_data.content.tabs_list[index].sort_rules;
new_data.content.field_show = new_data.content.field_show;
new_data.content.is_cover = new_data.content.tabs_list[index].is_cover;
this.setData({
tabs_index: index,
article_tabs: new_data,
diy_key: Math.random(),
});
},
// 获取商品距离顶部的距离
getTop() {
const query = uni.createSelectorQuery().in(this);
query
.select('.article-tabs-' + this.propKey)
.boundingClientRect((res) => {
if ((res || null) != null) {
let new_data = typeof this.propValue == 'string' ? JSON.parse(JSON.stringify(this.propValue)) : this.propValue;
const new_style = new_data.style || {};
this.setData({
tabs_top: res.top - (new_style.common_style?.margin_top || 0) + (new_style?.tabs_margin?.margin_top || 0),
});
}
})
.exec();
},
},
};
</script>
<style lang="scss" scoped>
.tabs-container {
position: relative;
width: 100%;
left: 0;
right: 0;
}
</style>

View File

@@ -0,0 +1,64 @@
<template>
<view :style="style_container">
<view :style="style_img_container">
<view :style="style"></view>
</view>
</view>
</template>
<script>
import { common_styles_computer, common_img_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
init() {
const { height } = this.propValue.content;
const { line_color, common_style } = this.propValue.style;
this.setData({
style: `height: ${height * 2}rpx;background: ${line_color || 'transparent'};`,
style_container: common_styles_computer(common_style),
style_img_container: common_img_computer(common_style, this.propIndex),
});
},
},
};
</script>
<style scoped lang="scss">
.right-0 {
top: 50%;
transform: translateY(-50%);
}
</style>

View File

@@ -0,0 +1,64 @@
<template>
<!-- 横线 -->
<view :style="style_container">
<view :style="style_img_container">
<view :style="style"></view>
</view>
</view>
</template>
<script>
import { common_styles_computer, common_img_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
// key
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
// 边框设置
let border_content = `border-bottom-style: ${new_content?.styles || 'solid'};`;
// 边框颜色设置
let border_style = `border-bottom-width: ${new_style.line_width * 2 || 2}rpx; border-bottom-color: ${new_style.line_color || 'rgba(204, 204, 204, 1)'};`;
this.setData({
style: border_content + border_style,
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
},
},
};
</script>
<style></style>

371
components/diy/carousel.vue Normal file
View File

@@ -0,0 +1,371 @@
<template>
<view class="pr" :style="style_container + swiper_bg_style">
<view class="pa top-0 wh-auto ht-auto" :style="swiper_bg_img_style"></view>
<view class="pr" :style="style_img_container + (!isEmpty(swiper_bg_img_style) ? swiper_bg_img_style_null : '')">
<swiper circular="true" :autoplay="form.is_roll == '1'" :interval="form.interval_time * 1000" :display-multiple-items="slides_per_group" :duration="500" :style="{ height: swiper_height }" :previous-margin="previousMargin" :next-margin="nextMargin" @change="slideChange">
<block v-if="form.carousel_type == 'card'">
<swiper-item v-for="(item, index) in new_list" :key="index">
<view class="flex-row align-c wt-auto ht-auto" :data-value="item.carousel_link.page" @tap="url_open">
<view class="swiper-item" :style="img_style" :class="['scale-defalt', { 'scale-1': animationData === index }]">
<view class="wh-auto ht-auto">
<imageEmpty :propImageSrc="item.carousel_img[0]" :propStyle="img_style" :propImgFit="img_fit" propErrorStyle="width: 100rpx;height: 100rpx;"></imageEmpty>
</view>
</view>
<view v-if="new_style.video_is_show == '1' && item.carousel_video.length > 0" :class="{ 'x-middle': new_style.video_location == 'center', 'right-0': new_style.video_location == 'flex-end' }" class="video-class pa oh" :style="{'bottom': new_style.video_bottom * 2 + 'rpx'}">
<view class="flex-row gap-5 align-c" :style="video_style" :data-value="item.carousel_video" @tap.stop="video_play">
<block v-if="new_style.video_type == 'img'">
<view class="video_img">
<imageEmpty :propImageSrc="new_style.video_img[0]" propImgFit="aspectFill" propErrorStyle="width: 28rpx;height: 28rpx;"></imageEmpty>
</view>
</block>
<block v-else>
<iconfont :name="!isEmpty(new_style.video_icon_class) ? 'icon-' + new_style.video_icon_class : 'icon-bofang'" size="'28rpx'" :color="new_style.video_icon_color" propContainerDisplay="flex"></iconfont>
</block>
<text v-if="!isEmpty(item.video_title)" :style="{ color: new_style.video_title_color, 'font-size': new_style.video_title_size * 2 + 'rpx', 'text-wrap': 'nowrap' }">{{ item.video_title }}</text>
</view>
</view>
</view>
</swiper-item>
</block>
<block v-else>
<swiper-item v-for="(item, index) in new_list" :key="index">
<view class="ht-auto" :style="['oneDragOne', 'twoDragOne'].includes(form.carousel_type) ? 'padding-right:' + new_style.image_spacing * 2 + 'rpx;' : ''" :data-value="item.carousel_link.page" @tap="url_open">
<view class="wh-auto ht-auto pr" :style="img_style">
<imageEmpty :propImageSrc="item.carousel_img[0]" :propStyle="img_style" :propImgFit="img_fit" propErrorStyle="width: 100rpx;height: 100rpx;"></imageEmpty>
</view>
<view v-if="new_style.video_is_show == '1' && item.carousel_video.length > 0" :class="{ 'x-middle': new_style.video_location == 'center', 'right-0': new_style.video_location == 'flex-end' }" class="video-class pa oh" :style="{'bottom': new_style.video_bottom * 2 + 'rpx'}">
<view class="flex-row gap-5 align-c" :style="video_style" :data-value="item.carousel_video" @tap.stop="video_play">
<block v-if="new_style.video_type == 'img'">
<view class="video_img">
<imageEmpty :propImageSrc="new_style.video_img[0]" propImgFit="aspectFill" propErrorStyle="width: 28rpx;height: 28rpx;"></imageEmpty>
</view>
</block>
<block v-else>
<iconfont :name="!isEmpty(new_style.video_icon_class) ? 'icon-' + new_style.video_icon_class : 'icon-bofang'" size="'28rpx'" :color="new_style.video_icon_color" propContainerDisplay="flex"></iconfont>
</block>
<text v-if="!isEmpty(item.video_title)" :style="{ color: new_style.video_title_color, 'font-size': new_style.video_title_size * 2 + 'rpx', 'text-wrap': 'nowrap' }">{{ item.video_title }}</text>
</view>
</view>
</view>
</swiper-item>
</block>
</swiper>
<view v-if="new_style.is_show == '1'" :class="['left', 'right'].includes(new_style.indicator_new_location) ? 'indicator_up_down_location' : 'indicator_about_location'" :style="indicator_location_style">
<template v-if="new_style.indicator_style == 'num'">
<view :style="indicator_style" class="dot-item">
<text :style="{ color: new_style.actived_color }">{{ actived_index + 1 }}</text>
<text>/{{ form.carousel_list.length }}</text>
</view>
</template>
<template v-else>
<view v-for="(item, index2) in form.carousel_list" :key="index2" :style="indicator_style + (actived_index == index2 ? 'background:' + new_style.actived_color : '')" class="dot-item" />
</template>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, radius_computer, isEmpty, gradient_computer, padding_computer, get_indicator_location_style, get_indicator_style, background_computer } from '@/common/js/common/common.js';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
components: {
imageEmpty,
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propIsCommon: {
type: Boolean,
default: true,
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
propOuterContainerPadding: {
type: Number,
default: 0,
}
},
data() {
return {
form: {},
new_style: {},
// 通用样式显示
style_container: '',
style_img_container: '',
// 图片的设置
img_style: '',
// 指示器的样式
indicator_style: '',
seat_list: [],
new_list: [],
// 指示器选中的位置
actived_index: 0,
interval_types: '',
img_fit: '',
dot_style: '',
video_style: '',
popup_width: '0rpx',
popup_height: '0rpx',
// 样式二的处理
animation: '',
animationData: 0,
previousMargin: '0rpx',
nextMargin: '0rpx',
slides_per_group: 1,
// hackReset: true,
// 轮播图的高度
swiper_height: 50,
// 轮播时的背景样式
swiper_bg_style: '',
swiper_bg_img_style: '',
swiper_bg_img_style_null: `background-image: url('')`
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
isEmpty,
init() {
const new_form = this.propValue.content;
const new_style = this.propValue.style;
// 获取当前手机的宽度
const { windowWidth } = uni.getSystemInfoSync();
// 将80%的宽度分成16份
const block = (windowWidth * 0.8) / 16;
const { common_style, actived_color } = new_style;
// scaleToFill 对应 fill aspectFit 对应 contain aspectFill 对应 cover
let fit = '';
if (new_form.img_fit == 'contain') {
fit = 'aspectFit';
} else if (new_form.img_fit == 'fill') {
fit = 'scaleToFill';
} else if (new_form.img_fit == 'cover') {
fit = 'aspectFill';
}
const { margin_left, margin_right, padding_left, padding_right } = new_style.common_style;
const width = sys_width - margin_left - margin_right - padding_left - padding_right - this.propOuterContainerPadding;
const scale = width / 390;
this.setData({
form: new_form,
new_style: new_style,
seat_list: this.get_seat_list(new_form),
new_list: new_form.carousel_list.concat(this.get_seat_list(new_form)),
popup_width: block * 16 * 2 + 'rpx', // 视频的宽度依照16:9比例来算
popup_height: block * 9 * 2 + 'rpx', // 视频的高度
style_container: this.propIsCommon ? common_styles_computer(common_style) : '', // 公共样式显示
style_img_container: this.propIsCommon ? common_img_computer(common_style, this.propIndex) : '', // 公共样式显示
img_style: radius_computer(new_style), // 图片的设置
indicator_style: get_indicator_style(new_style), // 指示器的样式
indicator_location_style: get_indicator_location_style(new_style),
dot_style: `bottom: ${ new_style.indicator_bottom * scale }px;`, // 指示器位置
img_fit: fit, // 图片风格 默认为aspectFill
video_style: this.get_video_style(new_style), // 视频播放按钮显示逻辑
swiper_height: new_form.height * scale + 'px', // 轮播图高度
swiper_bg_style: this.get_swiper_bg_style(new_form, 0),
swiper_bg_img_style: this.get_swiper_bg_img_style(new_form, 0),
});
// 风格二显示逻辑
if (new_form.carousel_type == 'card') {
// this.$nextTick(() => {
this.setData({
previousMargin: '41px',
nextMargin: '41px',
animationData: 0,
});
// });
} else if (new_form.carousel_type != 'inherit') {
// 风格三,四显示逻辑
// this.$nextTick(() => {
this.setData({
nextMargin: '50px',
slides_per_group: new_form.carousel_type == 'twoDragOne' ? 2 : 1,
});
// });
}
},
get_swiper_bg_style(form, actived_index) {
if (!this.propIsCommon) {
return '';
}
const style = form?.carousel_list?.[actived_index]?.style;
if (style && !isEmpty(style.color_list)) {
const color_list = style.color_list;
const list = color_list.filter((item) => !isEmpty(item.color));
if (list.length > 0) {
try {
return gradient_computer(style);
} catch (error) {
return '';
}
}
return '';
}
return '';
},
get_swiper_bg_img_style(form, actived_index) {
if (!this.propIsCommon) {
return '';
}
const { carousel_img, style = {} } = form?.carousel_list[actived_index] || {};
// 如果是自定义的图片 判断图片是否存在
if (!isEmpty(carousel_img) && style?.background_type == 'carousel') {
// 如果是使用轮播图,判断轮播图是否存在
const data = {
background_img: carousel_img,
background_img_style: style?.background_img_style || '2',
}
return background_computer(data) + (style.is_background_img_blur == '1' ? `filter: blur(14px);opacity: 0.6;` : '');
} else if (!isEmpty(style?.background_img)) {
return background_computer(style) + (style.is_background_img_blur == '1' ? `filter: blur(14px);opacity: 0.6;` : '');
}
return '';
},
get_seat_list(form) {
if (form.carousel_list.length > 3) {
return [];
} else {
let seat_list = [];
const list = JSON.parse(JSON.stringify(form.carousel_list));
switch (list.length) {
case 1:
seat_list = [...list, ...list, ...list];
break;
case 2:
seat_list.push(...list);
break;
case 3:
seat_list.push(...list);
break;
default:
break;
}
return seat_list;
}
},
slideChange(e) {
let actived_index = e.detail.current;
if (e.detail.current > this.form.carousel_list.length - 1) {
const seat_length = this.seat_list.length;
if (this.form.carousel_list.length > 1) {
actived_index = actived_index - seat_length;
} else {
actived_index = 0;
}
}
if (!this.propIsCommon) {
this.$emit('slideChange', actived_index);
}
this.setData({
animationData: e.detail.current,
actived_index: actived_index,
swiper_bg_style: this.get_swiper_bg_style(this.form, actived_index),
swiper_bg_img_style: this.get_swiper_bg_img_style(this.form, actived_index),
});
},
get_video_style(new_style) {
const { video_radius, video_color_list, video_direction, video_title_color, video_padding } = new_style;
let style = ``;
if (!isEmpty(video_radius)) {
style += radius_computer(video_radius);
}
const data = {
color_list: video_color_list,
direction: video_direction,
};
style += gradient_computer(data) + padding_computer(video_padding) + `color: ${video_title_color};box-sizing: border-box;`;
return style;
},
video_play(e) {
const list = e.currentTarget.dataset.value;
if (!isEmpty(list)) {
this.$emit('onVideoPlay', list[0].url, this.popup_width, this.popup_height);
}
},
url_open(link) {
app.globalData.url_event(link);
},
},
};
</script>
<style scoped lang="scss">
.dot-center {
left: 50%;
transform: translateX(-50%);
}
.dot-right {
right: 0;
}
.dot {
z-index: 1;
padding-left: 20rpx;
padding-right: 20rpx;
.dot-item {
margin: 0 6rpx;
}
}
.swiper-container {
display: flex;
align-items: center;
}
.swiper-item {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
margin-left: auto;
margin-right: auto;
height: 90%;
width: 90%;
text-align: center;
}
.scale-defalt {
position: relative;
border-radius: 20rpx;
transform: scale(1);
transition: -webkit-transform 400ms linear, transform 400ms linear;
transform-origin: 50% 50% 0px;
&.scale-1 {
transform: scale(1.1);
}
}
.video_img {
max-width: 120rpx;
height: 28rpx;
}
.video-class {
max-width: 100%;
padding-right: 20rpx;
padding-left: 20rpx;
}
.x-middle {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
</style>

1013
components/diy/coupon.vue Normal file

File diff suppressed because it is too large Load Diff

337
components/diy/custom.vue Normal file
View File

@@ -0,0 +1,337 @@
<template>
<view :style="style_container">
<view :style="style_img_container">
<view :style="style_content_container">
<view class="w h pr" :style="style_content_img_container">
<template v-if="!isEmpty(form.data_source) && form.data_source_is_loop !== '0'">
<template v-if="data_source_content_list.length > 0 && form.data_source_direction == 'vertical'">
<view class="flex-row flex-wrap" :style="'row-gap:' + new_style.row_gap + 'px;column-gap:' + new_style.column_gap + 'px;'">
<view v-for="(item, index) in data_source_content_list" :key="index" class="ht-auto" :style="gap_width">
<view v-for="(item1, index1) in item.split_list" :key="index1">
<view :style="style_chunk_container">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="index1" :propSourceList="item1" :propConfigLoop="form.data_source_is_loop || '1'" :propGroupSourceList="data_source_content_list" :propFieldList="field_list" :propDataHeight="form.height" :propScale="scale" :propDataIndex="index" :propDataSplitIndex="index1" :propIsCustom="form.is_custom_data == '1'" :propShowData="show_data" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
</view>
</view>
</template>
<view v-else-if="data_source_content_list.length > 0 && ['vertical-scroll', 'horizontal'].includes(form.data_source_direction)" class="oh ht-auto">
<swiper class="w flex" circular="true" :vertical="form.data_source_direction != 'horizontal'" :autoplay="new_style.is_roll == '1'" :interval="new_style.interval_time * 1000" :duration="500" :display-multiple-items="slides_per_view" :style="{ width: '100%', height: swiper_height + 'px' }" @change="slideChange">
<swiper-item v-for="(item, index) in data_source_content_list" :key="index">
<view :class="form.data_source_direction != 'horizontal' ? 'ht-auto ' : 'flex-row'" :style="form.data_source_direction == 'horizontal' ? 'column-gap:' + new_style.column_gap + 'px;' : ''">
<view v-for="(item1, index1) in item.split_list" :key="index1" class="wh-auto ht-auto" :style="style_chunk_container + swiper_width + (form.data_source_direction == 'horizontal' ? gap_width : 'margin-bottom:' + content_outer_spacing_magin)">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="index1" :propSourceList="item1" :propConfigLoop="form.data_source_is_loop || '1'" :propGroupSourceList="data_source_content_list" :propFieldList="field_list" :propScale="scale" :propDataIndex="index" :propDataSplitIndex="index1" :propIsCustom="form.is_custom_data == '1'" :propShowData="show_data" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
</swiper-item>
</swiper>
<view v-if="new_style.is_show == '1' && data_source_content_list.length > 1" :class="['left', 'right'].includes(new_style.indicator_new_location) ? 'indicator_up_down_location' : 'indicator_about_location'" :style="indicator_location_style">
<block v-if="new_style.indicator_style == 'num'">
<view :style="indicator_style" class="dot-item">
<text :style="{ color: new_style.actived_color }">{{ actived_index + 1 }}</text>
<text>/{{ data_source_content_list.length }}</text>
</view>
</block>
<block v-else>
<view v-for="(item, index) in data_source_content_list" :key="index" :style="indicator_style + (actived_index == index ? 'background:' + new_style.actived_color : '')" class="dot-item" />
</block>
</view>
</view>
<template v-else>
<view :style="style_chunk_container">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propConfigLoop="form.data_source_is_loop || '1'" :propFieldList="field_list" :propDataHeight="form.height" :propScale="scale" @url_event="url_event"></dataRendering>
</view>
</view>
</template>
</template>
<template v-else-if="!isEmpty(form.data_source) && form.data_source_is_loop == '0'">
<view class="h" :style="style_chunk_container">
<view class="w h oh" :style="style_chunk_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propSourceList="{}" :propConfigLoop="form.data_source_is_loop || '1'" :propGroupSourceList="data_source_content_list" :propFieldList="field_list" :propDataHeight="form.height" :propScale="scale" :propIsCustom="form.is_custom_data == '1'" :propShowData="show_data" @url_event="url_event"></dataRendering>
</view>
</view>
</template>
<template v-else>
<view :style="style_chunk_container">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propConfigLoop="form.data_source_is_loop || '1'" :propFieldList="field_list" :propDataHeight="form.height" :propScale="scale" @url_event="url_event"></dataRendering>
</view>
</view>
</template>
</view>
</view>
</view>
</view>
</template>
<script>
import { common_styles_computer, common_img_computer, percentage_count, isEmpty, get_indicator_style, get_indicator_location_style, border_width } from '@/common/js/common/common.js';
import dataRendering from '@/components/diy/modules/custom/data-rendering.vue';
const app = getApp();
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
components: {
dataRendering
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
propIsCommonStyle: {
type: Boolean,
default: true,
},
propOuterContainerPadding: {
type: Number,
default: 0,
}
},
data() {
return {
form: {},
new_style: {},
scale: 1,
style_container: '',
style_img_container: '',
div_width: 0,
div_height: 0,
custom_list_length: 0,
source_list: {
// 存放手动输入的id
data_ids: [],
// 手动输入
data_list: [],
// 自动
data_auto_list: [],
},
data_source_content_list: [],
data_source: '',
// 内容样式
style_content_container: '',
style_content_img_container: '',
// 数据样式
style_chunk_container: '',
style_chunk_img_container: '',
// 指示器选中的下标
actived_index: 0,
// 轮播高度
swiper_height: 0,
swiper_width: 'width: 100%;',
// 指示器样式
indicator_location_style: '',
indicator_style: '',
slides_per_view: 1,
show_data: { data_key: 'id', data_name: 'name' },
old_data_style: {
color_list: [{ color: 'rgb(244, 252, 255)', color_percentage: undefined }],
direction: '180deg',
background_img_style: '2',
background_img: [],
radius: 0,
radius_top_left: 0,
radius_top_right: 0,
radius_bottom_left: 0,
radius_bottom_right: 0,
padding: 0,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
margin: 0,
margin_top: 0,
margin_bottom: 0,
margin_left: 0,
margin_right: 0,
},
content_outer_spacing_magin: '0rpx',
gap_width: '',
field_list: [],
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
mounted() {
this.init();
},
methods: {
percentage_count,
isEmpty,
init() {
const new_form = this.propValue.content;
const new_style = this.propValue.style;
// 不包含新创建的数组时,将历史数据放到手动添加数组中
if (!Object.keys(new_form.data_source_content).includes('data_auto_list') && !Object.keys(new_form.data_source_content).includes('data_list')) {
//深拷贝一下,保留历史数据
const data = JSON.parse(JSON.stringify(new_form.data_source_content));
new_form.data_source_content = this.source_list;
// 如果老数组中有数据,将数据放到新数组中
if (!isEmpty(data)) {
new_form.data_source_content.data_list = [ data ];
}
}
// 数据来源的内容
let list = [];
if (new_form.is_custom_data == '1') {
if (Number(new_form.data_source_content.data_type) === 0) {
list = new_form.data_source_content?.data_list || [];
} else {
list = !isEmpty(new_form.data_source_content) ?
new_form.data_source_content.data_auto_list.map(item => ({
id: Math.random(),
new_cover: [],
new_title: '',
data: item,
})) : [];
}
} else {
list = new_form.data_source_content?.data_list || [];
}
// 数组处理
const new_list = list.length > 0 ? this.get_list(list, new_form, new_style) : [];
// 初始化数据
const { common_style, data_content_style, data_style } = new_style;
// 外层左右间距
const outer_spacing = (common_style?.margin_left || 0) + (common_style?.margin_right || 0) + (common_style?.padding_left || 0) + (common_style?.padding_right || 0) + border_width(common_style);
// 内容左右间距
const content_spacing = (data_content_style?.margin_left || 0) + (data_content_style?.margin_right || 0) + (data_content_style?.padding_left || 0) + (data_content_style?.padding_right || 0) + border_width(data_content_style);
// 数据左右间距
const internal_spacing = (data_style?.margin_left || 0) + (data_style?.margin_right || 0) + (data_style?.padding_left || 0) + (data_style?.padding_right || 0) + border_width(data_style);
// 一行显示的数量
const carousel_col = Number(new_form.data_source_carousel_col);
// 数据间距
const data_spacing = ['vertical', 'horizontal'].includes(new_form.data_source_direction) ? new_style.column_gap * (carousel_col - 1) : 0;
// 自定义组件宽度
const width = sys_width - outer_spacing - content_spacing - internal_spacing - data_spacing - this.propOuterContainerPadding;
const new_data_style = !isEmpty(new_style.data_style) ? new_style.data_style : this.old_data_style;
const new_data_content_style = !isEmpty(new_style.data_content_style)? new_style.data_content_style : this.old_data_style;
// 判断是平移还是整屏滚动
const { padding_top = 0, padding_bottom = 0, margin_bottom = 0, margin_top = 0 } = new_data_style;
let swiper_height = 0;
const scale_number = width / 390;
const new_scale = scale_number > 0 ? scale_number : 0;
// 间距
const space_between = new_form.data_source_direction == 'horizontal' ? new_style.column_gap : new_style.row_gap;
let col = Number(new_form.data_source_carousel_col);
// 轮播图高度控制
if (new_form.data_source_direction == 'horizontal') {
swiper_height = new_form.height * new_scale + padding_top + padding_bottom + margin_bottom + margin_top;
} else {
// 商品数量大于列数的时候,高度是列数,否则是当前的数量
col = new_list.length > carousel_col ? carousel_col : new_list.length;
swiper_height = (new_form.height * new_scale + padding_top + padding_bottom + margin_bottom + margin_top) * col + ((Number(new_form.data_source_carousel_col) - 1) * space_between);
}
// 计算间隔的空间。(gap * gap数量) / 模块数量
let gap = (new_style.column_gap * (carousel_col - 1)) / carousel_col;
// 横向的时候,根据选择的行数和每行显示的个数来区分具体是显示多少个
const swiper_width = (new_form.data_source_direction == 'horizontal' && new_style.rolling_fashion != 'translation') ? `width: ${ 100 / carousel_col }%;`: 'width: 100%;';
this.setData({
form: new_form,
new_style: new_style,
div_width: width,
scale: new_scale,
custom_list_length: new_form.custom_list.length - 1,
style_container: this.propIsCommonStyle ? common_styles_computer(new_style.common_style) + 'box-sizing: border-box;' : '', // 用于样式显示
style_img_container: this.propIsCommonStyle ? common_img_computer(new_style.common_style, this.propIndex) : '',
style_content_container: common_styles_computer(new_data_content_style) + 'box-sizing: border-box;', // 用于样式显示
style_content_img_container: common_img_computer(new_data_content_style),
style_chunk_container: common_styles_computer(new_data_style) + 'box-sizing: border-box;', // 用于样式显示
style_chunk_img_container: common_img_computer(new_data_style),
style_chunk_width: width,
div_height: new_form.height,
data_source_content_list: new_list,
data_source: !isEmpty(new_form.data_source)? new_form.data_source : '',
indicator_style: get_indicator_style(new_style), // 指示器的样式
indicator_location_style: get_indicator_location_style(new_style),
swiper_height: swiper_height,
swiper_width: swiper_width,
content_outer_spacing_magin: space_between + 'px',
gap_width: `width: calc(${100 / carousel_col}% - ${gap}px);`,
slides_per_view: new_style.rolling_fashion == 'translation' ? (new_form.data_source_direction != 'horizontal' ? col : new_form.data_source_carousel_col ) : 1,
show_data: new_form?.show_data || { data_key: 'id', data_name: 'name' },
field_list: new_form?.field_list || [],
});
},
get_list(list, form, new_style) {
// 深拷贝一下,确保不会出现问题
const cloneList = JSON.parse(JSON.stringify(list));
if (new_style.rolling_fashion != 'translation' && form.data_source_direction != 'vertical') {
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (cloneList.length > 0) {
// 每页显示的数量
const num = form.data_source_carousel_col;
// 存储数据显示
let 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,
},
];
}
} else {
// 存储数据显示
let nav_list = [];
cloneList.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
return nav_list;
}
},
slideChange(e) {
this.setData({
actived_index: e.detail.current,
});
},
url_event(e, index, split_index) {
if (this.data_source == 'goods' && this.data_source_content_list.length > 0) {
const list = this.data_source_content_list[index];
if (!isEmpty(list) && !isEmpty(list.split_list[split_index])) {
const new_list = list.split_list[split_index];
if (!isEmpty(new_list)) {
// 缓存商品数据
app.globalData.goods_data_cache_handle(new_list.data.id, new_list.data);
}
}
}
app.globalData.url_open(e);
},
},
};
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,434 @@
<template>
<view class="img-magic" :style="'height:' + container_size + ';' + style_container">
<view class="magic-container w h pr" :style="style_img_container">
<view class="pr" :style="'width:calc(100% + ' + outer_spacing + ');height:calc(100% + ' + outer_spacing + ');margin:-' + spacing + ';'">
<!-- 风格9 -->
<template v-if="form.style_actived == 7">
<view class="flex-row align-c jc-c style-size flex-wrap">
<view v-for="(item, index) in data_magic_list" :key="index" :style="'margin:' + spacing + ';' + ([0, 1].includes(index) ? 'width:calc(50% - ' + outer_spacing + ');height:calc(50% - ' + outer_spacing + ')' : 'width:calc((100% / 3) - ' + outer_spacing + ');height:calc(50% - ' + outer_spacing + ')')" class="style9">
<view class="w h flex-row" :style="item.data_style.background_style + content_radius">
<view class="re flex-1 oh" :style="item.data_style.background_img_style">
<template v-if="item.data_content.data_type == 'goods'">
<view class="w h flex-col" :style="'gap:'+ item.data_style.title_data_gap * 2 + 'rpx;'">
<view v-if="(!isEmpty(item.data_content.heading_title) || !isEmpty(item.data_content.subtitle)) && [0, 1].includes(index)" :class="'tl' + (item.data_style.title_line == '1' ? ' flex-row align-c' : ' flex-col')" :style="'gap:' + item.data_style.title_gap * 2 + 'rpx;'">
<template v-if="item.data_content.heading_title_type && item.data_content.heading_title_type == 'image'">
<view v-if="item.data_content.heading_title_img.length > 0" class="re oh" :style="'width:100%;height:' + (!isEmpty(item.data_style.heading_img_height) ? item.data_style.heading_img_height * magic_scale : 0) + 'px'">
<image :src="item.data_content.heading_title_img[0].url" class="ht-auto max-w" mode="heightFix" />
</view>
</template>
<template v-else>
<view class="ma-0 w text-word-break text-line-1 flex-basis-shrink" :style="item.data_style.heading_style">{{ item.data_content.heading_title || '' }}</view>
</template>
<template v-if="item.data_content.subtitle_title_type && item.data_content.subtitle_title_type == 'image'">
<view v-if="item.data_content.subtitle_title_img.length > 0" class="re oh" :style="'width:100%;height:' + (!isEmpty(item.data_style.subtitle_img_height) ? item.data_style.subtitle_img_height * magic_scale : 0) + 'px'">
<image :src="item.data_content.subtitle_title_img[0].url" class="ht-auto max-w" mode="heightFix" />
</view>
</template>
<template v-else>
<view class="ma-0 w text-word-break text-line-1 flex-basis-shrink" :style="item.data_style.subtitle_style">{{ item.data_content.subtitle || '' }}</view>
</template>
</view>
<view class="w h flex-1 oh flex-row">
<magic-carousel class="flex-1" :propKey="propKey + index" :propValue="item" :propGoodStyle="item.data_style" :propActived="form.style_actived" propType="product" :propDataIndex="index" @onCarouselChange="carousel_change"></magic-carousel>
</view>
</view>
</template>
<template v-else-if="item.data_content.data_type == 'images'">
<view class="w h flex-1 oh flex-row">
<magic-carousel class="flex-1" :propKey="propKey + index" :propValue="item" propType="img" :propActived="form.style_actived" :propDataIndex="index" @onCarouselChange="carousel_change"></magic-carousel>
</view>
</template>
<template v-else-if="item.data_content.data_type == 'custom'">
<customIndex :propKey="propKey + index" :propValue="item" :propMagicScale="magic_scale" :propDataSpacing="new_style.image_spacing" :propDataIndex="index" @onCarouselChange="carousel_change"></customIndex>
</template>
<template v-else>
<videoIndex :propKey="propKey + index" :propValue="item.data_content" :propDataStyle="item.data_style"></videoIndex>
</template>
<view v-if="item.data_style.is_show == '1' && item.data_content.list.length > 1" :class="['left', 'right'].includes(item.data_style.indicator_new_location) ? 'indicator_up_down_location' : 'indicator_about_location'" :style="item.data_style.indicator_location_style">
<template v-if="item.data_style.indicator_style == 'num'">
<view :style="item.data_style.indicator_styles" class="dot-item">
<text class="num-active" :style="{ color: item.data_style.actived_color }">{{ item.actived_index + 1 }}</text
><text>/{{ item.data_content.list.length }}</text>
</view>
</template>
<template v-else>
<view v-for="(item3, index3) in item.data_content.list" :key="index3" :style="item.data_style.indicator_styles + style_actived_color(item, index3)" class="dot-item" />
</template>
</view>
</view>
</view>
</view>
</view>
</template>
<template v-else>
<view v-for="(item, index) in data_magic_list" :key="index" class="cr-main cube-selected" :style="selected_style(item) + ';margin:' + spacing + ';'">
<view class="w h flex-row" :style="item.data_style.background_style + content_radius">
<view class="re flex-1 oh" :style="item.data_style.background_img_style">
<template v-if="item.data_content.data_type == 'goods'">
<view class="w h flex-col" :style="'gap:'+ item.data_style.title_data_gap * 2 + 'rpx;'">
<view v-if="!isEmpty(item.data_content.heading_title) || !isEmpty(item.data_content.subtitle)" :class="'tl' + (item.data_style.title_line == '1' ? ' flex-row align-c' : ' flex-col')" :style="'gap:' + item.data_style.title_gap * 2 + 'rpx;'">
<template v-if="item.data_content.heading_title_type && item.data_content.heading_title_type == 'image'">
<view v-if="item.data_content.heading_title_img.length > 0" class="re oh" :style="'width:100%;height:' + (!isEmpty(item.data_style.heading_img_height) ? item.data_style.heading_img_height * magic_scale : 0) + 'px'">
<image :src="item.data_content.heading_title_img[0].url" class="ht-auto max-w" mode="heightFix" />
</view>
</template>
<template v-else>
<view class="ma-0 w text-word-break text-line-1 flex-basis-shrink" :style="item.data_style.heading_style">{{ item.data_content.heading_title || '' }}</view>
</template>
<template v-if="item.data_content.subtitle_title_type && item.data_content.subtitle_title_type == 'image'">
<view v-if="item.data_content.subtitle_title_img.length > 0" class="re oh" :style="'width:100%;height:' + (!isEmpty(item.data_style.subtitle_img_height) ? item.data_style.subtitle_img_height * magic_scale : 0) + 'px'">
<image :src="item.data_content.subtitle_title_img[0].url" class="ht-auto max-w" mode="heightFix" />
</view>
</template>
<template v-else>
<view class="ma-0 w text-word-break text-line-1 flex-basis-shrink" :style="item.data_style.subtitle_style">{{ item.data_content.subtitle || '' }}</view>
</template>
</view>
<view class="w h oh flex-row">
<magic-carousel class="flex-1" :propKey="propKey + index" :propValue="item" :propGoodStyle="item.data_style" propType="product" :propActived="form.style_actived" :propDataIndex="index" @onCarouselChange="carousel_change"></magic-carousel>
</view>
</view>
</template>
<template v-else-if="item.data_content.data_type == 'images'">
<view class="w h oh flex-row">
<magic-carousel class="flex-1" :propKey="propKey + index" :propValue="item" propType="img" :propActived="form.style_actived" :propDataIndex="index" @onCarouselChange="carousel_change"></magic-carousel>
</view>
</template>
<template v-else-if="item.data_content.data_type == 'custom'">
<customIndex :propKey="propKey + index" :propValue="item" :propMagicScale="magic_scale" :propDataSpacing="new_style.image_spacing" :propDataIndex="index" @onCarouselChange="carousel_change"></customIndex>
</template>
<template v-else>
<videoIndex :propKey="propKey + index" :propValue="item.data_content" :propDataStyle="item.data_style"></videoIndex>
</template>
<view v-if="item.data_style.is_show == '1' && item.data_content.list.length > 1" :class="['left', 'right'].includes(item.data_style.indicator_new_location) ? 'indicator_up_down_location' : 'indicator_about_location'" :style="item.data_style.indicator_location_style">
<template v-if="item.data_style.indicator_style == 'num'">
<view :style="item.data_style.indicator_styles" class="dot-item">
<text class="num-active" :style="{ color: item.data_style.actived_color }">{{ item.actived_index + 1 }}</text>
<text>/{{ item.data_content.list.length }}</text>
</view>
</template>
<template v-else>
<view v-for="(item3, index3) in item.data_content.list" :key="index3" :style="item.data_style.indicator_styles + style_actived_color(item, index3)" class="dot-item" />
</template>
</view>
</view>
</view>
</view>
</template>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import magicCarousel from '@/components/diy/modules/data-magic/magic-carousel.vue';
import customIndex from '@/components/diy/modules/data-magic/custom/index.vue';
import videoIndex from '@/components/diy/modules/data-magic/video/index.vue';
import { background_computer, common_styles_computer, common_img_computer, gradient_computer, radius_computer, percentage_count, isEmpty, padding_computer, margin_computer, old_border_and_box_shadow, old_margin, old_padding, box_shadow_computer, border_computer, get_indicator_location_style, get_indicator_style, border_width } from '@/common/js/common/common.js';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
components: {
magicCarousel,
customIndex,
videoIndex
},
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
propOuterContainerPadding: {
type: Number,
default: 0,
}
},
data() {
return {
form: {},
new_style: {},
outer_spacing: '',
// 图片间距设置
spacing: '',
// 内容圆角设置
content_radius: '',
// 图片圆角设置
// content_img_radius: '',
data_magic_list: [],
cubeCellWidth: 0,
container_size: 0,
style_container: '',
style_img_container: '',
div_width: 0,
magic_scale: 1,
// is_unlimited_size: false,
};
},
computed: {
// 根据当前页面大小计算成百分比
selected_style() {
return (item) => {
return `overflow: hidden;width: calc(${this.percentage(this.getSelectedWidth(item))} - ${this.outer_spacing} ); height: calc(${this.percentage(this.getSelectedHeight(item))} - ${this.outer_spacing} ); top: ${this.percentage(this.getSelectedTop(item))}; left: ${this.percentage(this.getSelectedLeft(item))};`;
};
},
style_actived_color() {
return (item, index) => {
return item.actived_index == index ? `background: ${item.data_style.actived_color};` : '';
};
},
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
mounted() {
this.init();
},
methods: {
isEmpty,
init() {
const new_form = this.propValue.content;
const new_style = this.propValue.style;
const container_height = !isEmpty(new_form.container_height) ? new_form.container_height : sys_width;
const density = !isEmpty(new_form.magic_cube_density) ? new_form.magic_cube_density : 4;
const { margin_left, margin_right } = new_style.common_style;
const width = sys_width - margin_left - margin_right - border_width(new_style.common_style) - this.propOuterContainerPadding;
this.setData({
form: new_form,
new_style: new_style,
outer_spacing: new_style.image_spacing * 2 + 'rpx',
spacing: new_style.image_spacing + 'rpx',
content_radius: radius_computer(new_style.data_radius),
// content_img_radius: radius_computer(new_style.img_radius),
data_magic_list: this.get_data_magic_list(new_form.data_magic_list, new_style),
style_container: common_styles_computer(new_style.common_style) + 'box-sizing: border-box;', // 用于样式显示
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
magic_scale: width / 390,
div_width: sys_width,
cubeCellWidth: sys_width / density,
container_size: container_height * (width / 390) + 'px',
});
},
get_data_magic_list(data, new_style) {
data.forEach((item) => {
const data_content = item.data_content;
const data_style = item.data_style;
item.actived_index = 0;
// 指示器样式
data_style.indicator_styles = get_indicator_style(data_style);
data_style.indicator_location_style = get_indicator_location_style(data_style);
// 获取当前的margin
const chunk_margin = data_style?.chunk_margin || old_margin;
// 计算左右间距
const left_right_width_margin = (chunk_margin?.margin_left || 0) + (chunk_margin?.margin_right || 0);
// 计算上下间距
const top_bottom_height_margin = (chunk_margin?.margin_top || 0) + (chunk_margin?.margin_bottom || 0);
data_style.background_style = gradient_computer(data_style) + margin_computer(data_style?.chunk_margin || old_margin) + `width: calc(100% - ${ left_right_width_margin }px);height:calc(100% - ${ top_bottom_height_margin }px);` + box_shadow_computer(data_style?.data_common_style || old_border_and_box_shadow) + border_computer(data_style?.data_common_style || old_border_and_box_shadow);
data_style.background_img_style = background_computer(data_style) + padding_computer(data_style?.chunk_padding || old_padding);
// 商品价格处理
data_style.goods_price_symbol_style = this.goods_trends_config(data_style, 'price_symbol');
data_style.goods_price_unit_style = this.goods_trends_config(data_style, 'price_unit');
let fit = '';
if (data_content.img_fit == 'contain') {
fit = 'aspectFit';
} else if (data_content.img_fit =='fill') {
fit = 'scaleToFill';
} else if (data_content.img_fit == 'cover') {
fit = 'aspectFill';
}
data_content.fit = fit;
// 商品名称和价格样式
data_style.goods_title_style = this.goods_trends_config(data_style, 'title') + `line-height: ${ (item.data_style.goods_title_size + 3) * 2 }rpx;height: ${ (item.data_style.goods_title_size + 3) * 2 }rpx;`;
data_style.goods_price_style = this.goods_trends_config(data_style, 'price') + `line-height: ${ item.data_style.goods_price_size * 2 }rpx;height: ${ (item.data_style.goods_title_size + 3) * 2 }rpx;`;
const radius = !isEmpty(data_style.img_radius) ? data_style.img_radius : { radius: 4, radius_top_left: 4, radius_top_right: 4, radius_bottom_left: 4, radius_bottom_right: 4 };
data_style.get_img_radius = radius_computer(radius);
data_style.chunk_padding_data = padding_computer(data_style.chunk_padding) + 'box-sizing: border-box;';
data_style.heading_style = this.trends_config(data_style, 'heading');
data_style.subtitle_style = this.trends_config(data_style, 'subtitle');
if (data_content.data_type == 'goods') {
data_content.list = this.commodity_list(data_content.goods_list, data_content.goods_num, data_content, data_style);
} else if (data_content.data_type == 'custom' && ['vertical-scroll', 'horizontal'].includes(data_content.data_source_direction)) {
// 是自定义并且是轮播状态的时候,添加数据
const list = this.data_source_content_list(data_content);
const carousel_col = data_content?.data_source_carousel_col || 1;
const num = new_style.rolling_fashion == 'translation' ? list.length : Math.ceil(list.length / carousel_col);
data_content.list = Array(num);
} else {
data_content.list = data_content.images_list;
}
});
return data;
},
// 数据来源的内容
data_source_content_list(data_content){
if (data_content.is_custom_data == '1') {
if (Number(data_content.data_source_content.data_type) === 0) {
return data_content.data_source_content.data_list;
} else {
return data_content.data_source_content.data_auto_list.map((item) => ({
id: Math.random(),
new_cover: [],
new_title: '',
data: item,
}));
}
} else {
return data_content.data_source_content.data_list;
}
},
/*
** 组装产品的数据
** @param {Array} list 商品列表
** @param {Number} num 显示数量
** @return {Array}
*/
commodity_list(list, num, data_content, data_style) {
if (list.length > 0) {
// 深拷贝一下,确保不会出现问题
const goods_list = JSON.parse(JSON.stringify(list)).map((item) => ({
...item.data,
title: !isEmpty(item.new_title) ? item.new_title : item.data.title,
new_cover: item.new_cover,
}));
// 存储数据显示
let nav_list = [];
// 如果是滑动,需要根据每行显示的个数来区分来拆分数据 translation 表示的是平移
if (data_style.rolling_fashion != 'translation') {
// 拆分的数量
const split_num = Math.ceil(goods_list.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({ split_list: goods_list.slice(i * num, (i + 1) * num) });
}
return nav_list;
} else {
return this.rotation_calculation(goods_list, num, data_content, data_style);
}
} else {
return [];
}
},
rotation_calculation(list, num, data_content, data_style) {
// 存储数据显示
let nav_list = [];
const goods_outerflex = data_content.goods_outerflex;
const rotation_direction = data_style.rotation_direction;
// 如果是商品是横排的,轮播也是横排的,就不对商品进行拆分/如果商品是竖排的,轮播也是竖排的,不对商品进行拆分
if ((goods_outerflex == 'row' && rotation_direction == 'horizontal') || (goods_outerflex == 'col' && rotation_direction == 'vertical')) {
list.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
} else {
// 拆分的数量
const split_num = Math.ceil(list.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({ split_list: list.slice(i * num, (i + 1) * num) });
}
return nav_list;
}
return nav_list;
},
getSelectedWidth(item) {
return (item.end.x - item.start.x + 1) * this.cubeCellWidth;
},
//计算选中层的高度。
getSelectedHeight(item) {
return (item.end.y - item.start.y + 1) * this.cubeCellWidth;
},
//计算选中层的右边距离。
getSelectedTop(item) {
return (item.start.y - 1) * this.cubeCellWidth;
},
//计算选中层的左边距离。
getSelectedLeft(item) {
return (item.start.x - 1) * this.cubeCellWidth;
},
// 计算成百分比
percentage(num) {
return percentage_count(num, this.div_width);
},
goods_trends_config(style, key) {
return this.text_style(style[`goods_${key}_typeface`], style[`goods_${key}_size`], style[`goods_${key}_color`]);
},
// 根据传递的参数,从对象中取值
trends_config(style, key) {
return this.text_style(style[`${key}_typeface`], style[`${key}_size`], style[`${key}_color`]);
},
text_style(typeface, size, color) {
return `font-weight:${typeface}; font-size: ${size * 2}rpx; color: ${color};`;
},
carousel_change(actived_index, index) {
if (this.data_magic_list[index]) {
this.data_magic_list[index].actived_index = actived_index;
}
},
},
};
</script>
<style lang="scss" scoped>
// 图片魔方是一个正方形,根据宽度计算高度
.img-magic {
overflow: hidden;
}
.cube-selected {
position: absolute;
text-align: center;
box-sizing: border-box;
}
.text-line-1 {
word-break: break-word;
overflow-wrap: break-word;
word-wrap: break-word;
}
.style-size {
height: 100%;
width: 100%;
.style9 {
position: relative;
overflow: hidden;
}
}
.dot-center {
left: 50%;
transform: translateX(-50%);
}
.dot-right {
right: 0;
}
.dot {
z-index: 3;
.dot-item {
margin: 0 6rpx;
}
}
.gap-20 {
gap: 40rpx;
}
.w {
width: 100%;
}
.h {
height: 100%;
}
.flex-basis-shrink {
flex-basis: content;
flex-shrink: 1;
}
</style>

View File

@@ -0,0 +1,334 @@
<template>
<view class="data-tabs ou" :class="'data-tabs-' + propKey" :style="style_container">
<view class="ou" :style="style_img_container">
<componentDiyModulesTabsView :propKey="propKey" :propValue="data_tabs" :propIsTop="top_up == '1'" :propTop="sticky_top" :propStyle="tabs_style" :propsTabsContainer="tabs_container" :propsTabsImgContainer="tabs_img_container" :propCustomNavHeight="propIsTabsUseSafeDistance ? (propCustomNavHeight * 2 + 'rpx') : '0rpx'" :propTabsSlidingFixedBg="tabs_sliding_fixed_bg" :propTabsBackground="tabs_background" @onTabsTap="tabs_click_event"></componentDiyModulesTabsView>
<view :style="data_margin_top">
<view :style="data_container">
<view :style="data_img_container">
<template v-if="tabs_data_type == 'goods'">
<view class="oh" :style="data_content_container">
<view class="oh" :style="data_content_img_container">
<componentGoodsList ref="diy_goods_list" :propKey="diy_key" :propDiyIndex="propDiyIndex" :propValue="tabs_list" :propIsCommonStyle="false" @goods_buy_event="goods_buy_event"></componentGoodsList>
</view>
</view>
</template>
<template v-else-if="tabs_data_type == 'article'">
<view class="oh" :style="data_content_container">
<view class="oh" :style="data_content_img_container">
<componentArticleList :propKey="diy_key" :propValue="tabs_list" :propIsCommonStyle="false"></componentArticleList>
</view>
</view>
</template>
<template v-else-if="tabs_data_type == 'custom'">
<componentCustomList :propKey="diy_key" :propValue="tabs_list" :propOuterContainerPadding="outer_container_width" :propIsCommonStyle="false"></componentCustomList>
</template>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, padding_computer, margin_computer, background_computer, gradient_computer, isEmpty, old_border_and_box_shadow, old_margin, old_radius, old_padding, border_computer, box_shadow_computer, radius_computer, get_math } from '@/common/js/common/common.js';
import componentDiyModulesTabsView from '@/components/diy/modules/tabs-view';
import componentGoodsList from '@/components/diy/goods-list';
import componentArticleList from '@/components/diy/article-list';
import componentCustomList from '@/components/diy/custom';
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
components: {
componentDiyModulesTabsView,
componentGoodsList,
componentArticleList,
componentCustomList,
},
props: {
propValue: {
type: Object,
default: () => ({}),
},
propTop: {
type: Number,
default: 0,
},
propCustomNavHeight: {
type: Number,
default: 33,
},
propScrollTop: {
type: Number,
default: 0,
},
// 顶部导航是否开启沉浸模式
propIsImmersionModel: {
type: Boolean,
default: false,
},
propKey: {
type: [String, Number],
default: '',
},
propDiyIndex: {
type: Number,
default: 0,
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 0,
},
// 选项卡是否使用安全距离
propIsTabsUseSafeDistance: {
type: Boolean,
default: true
}
},
data() {
return {
style_container: '',
style_img_container: '',
data_tabs: {},
tabs_list: {},
// 是否滑动置顶
top_up: '0',
tabs_top: 0,
tabs_background: 'background:transparent',
custom_nav_height: 33,
diy_key: '',
// 选项卡背景设置
tabs_container: '',
tabs_img_container: '',
// 商品区域背景设置
data_margin_top: '',
data_container: '',
data_img_container: '',
// #ifdef MP
nav_safe_space: bar_height + 5,
// #endif
// #ifdef H5 || MP-TOUTIAO
nav_safe_space: bar_height + 7,
// #endif
// #ifdef APP
nav_safe_space: bar_height + 0,
// #endif
tabs_style: '',
// 选项卡默认数据
tabs_index: 0,
sticky_top: 0,
tabs_data_type: 'goods',
outer_container_width: 0,
// 数据样式
data_content_container: '',
data_content_img_container: '',
tabs_sliding_fixed_bg: '',
// 滑动固定的背景
tabs_sliding_fixed_bg: '',
data_content_style: {
color_list: [{ color: '', color_percentage: undefined }],
direction: '180deg',
background_img_style: '2',
background_img: [],
radius: 0,
radius_top_left: 0,
radius_top_right: 0,
radius_bottom_left: 0,
radius_bottom_right: 0,
padding: 0,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
margin: 0,
margin_top: 0,
margin_bottom: 0,
margin_left: 0,
margin_right: 0,
// 边框样式
border_is_show: '0',
border_color: '#FF3F3F',
border_style: 'solid',
border_size: {
padding: 1,
padding_top: 1,
padding_right: 1,
padding_bottom: 1,
padding_left: 1,
},
// 阴影
box_shadow_color: '',
box_shadow_x: 0,
box_shadow_y: 0,
box_shadow_blur: 0,
box_shadow_spread: 0,
}
};
},
watch: {
propScrollTop(newVal) {
if (newVal + this.sticky_top + this.custom_nav_height > this.tabs_top + this.nav_safe_space && this.top_up == '1') {
let new_style = this.propValue.style || {};
let tabs_bg = new_style.common_style.color_list;
let new_tabs_background = '';
if (tabs_bg.length > 0 && (tabs_bg[0].color || null) != null) {
new_tabs_background = gradient_computer(new_style.common_style);
}
let new_tabs_background_img = background_computer(new_style.common_style);
if (new_tabs_background_img.length > 0) {
new_tabs_background_img += 'background-position: top left;';
}
this.tabs_background = (new_tabs_background.length > 0 ? new_tabs_background : 'background:#fff;') + new_tabs_background_img;
} else {
this.tabs_background = 'background:transparent';
}
},
propTop(val) {
this.init();
},
propKey(val) {
// 初始化
this.setData({
diy_key: val,
});
this.init();
},
},
created() {
this.init();
},
mounted() {
this.$nextTick(() => {
const self = this;
setTimeout(() => {
self.getTop();
});
// #ifdef H5 || MP-TOUTIAO
// 获取自定义导航栏高度
this.setData({
custom_nav_height: this.propCustomNavHeight,
});
// #endif
});
},
methods: {
init() {
let new_data = typeof this.propValue == 'string' ? JSON.parse(JSON.stringify(this.propValue)) : this.propValue;
const new_content = new_data.content || {};
const new_style = new_data.style || {};
// 公共样式
const common_style = new_style.common_style;
let tabs_style_obj = {
padding_top: (common_style.padding_top - this.propCustomNavHeight) < 0 ? 0 : common_style.padding_top - this.propCustomNavHeight,
padding_left: common_style.padding_left,
padding_right: common_style.padding_right,
};
let new_tabs_style = padding_computer(tabs_style_obj) + `position:relative;left: -${tabs_style_obj.padding_left * 2}rpx;right: -${tabs_style_obj.padding_right * 2}rpx;width:100%;`;
// 如果是历史数据的话,就执行默认添加下边距
if (isEmpty(new_style.tabs_padding)) {
new_tabs_style += 'padding-bottom: 20rpx;';
}
const { tabs_bg_color_list = [], tabs_bg_direction = '', tabs_bg_background_img_style = '', tabs_bg_background_img = [], tabs_radius = old_radius, tabs_padding = old_padding, data_content_color_list = [], data_content_direction = '', data_content_background_img_style = '', data_content_background_img = [], data_content_margin = old_margin, data_content_padding = old_padding, data_content_radius = old_radius } = new_style;
// 选项卡背景设置
const tabs_data = {
color_list: tabs_bg_color_list,
direction: tabs_bg_direction,
background_img_style: tabs_bg_background_img_style,
background_img: tabs_bg_background_img,
};
// 商品区域背景设置
const data_content_data = {
color_list: data_content_color_list,
direction: data_content_direction,
background_img_style: data_content_background_img_style,
background_img: data_content_background_img,
};
const data_content = new_style?.data_content || old_border_and_box_shadow;
const tabs_content = new_style?.tabs_content || old_border_and_box_shadow;
//显示的数据处理
this.tabs_click_event(0);
this.setData({
top_up: new_content.tabs_top_up,
sticky_top: this.propTop + (new_style?.tabs_margin?.margin_top || 0),
data_tabs: new_data,
// 自定义需要做等比缩放,因此宽度需要减去 外层通用的宽度和内容区域的宽度
outer_container_width: common_style.margin_left + common_style.margin_right + common_style.padding_left + common_style.padding_right + new_style.data_content_margin.margin_left + new_style.data_content_margin.margin_right + new_style.data_content_padding.padding_left + new_style.data_content_padding.padding_right,
style_container: common_styles_computer(common_style),
style_img_container: common_img_computer(common_style, this.propIndex),
tabs_style: new_tabs_style,
tabs_sliding_fixed_bg: gradient_computer(tabs_data),
tabs_container: gradient_computer(tabs_data) + radius_computer(tabs_radius) + margin_computer(new_style?.tabs_margin || old_margin) + border_computer(tabs_content) + box_shadow_computer(tabs_content) + 'overflow: hidden;',
tabs_img_container: background_computer(tabs_data) + padding_computer(tabs_padding) + 'box-sizing: border-box;overflow: hidden;',
data_margin_top: 'margin-top:' + (new_style?.data_content_spacing || 0) * 2 + 'rpx;',
data_container: gradient_computer(data_content_data) + margin_computer(data_content_margin) + radius_computer(data_content_radius) + box_shadow_computer(data_content) + border_computer(data_content) + 'overflow: hidden;',
data_img_container: background_computer(data_content_data) + padding_computer(data_content_padding) + 'box-sizing: border-box;overflow: hidden;',
});
},
tabs_click_event(index) {
let new_data = JSON.parse(JSON.stringify(this.propValue));
// 显示的数据处理
const tabs_data_list = new_data.content.tabs_list[index] || {};
const tabs_data_type = tabs_data_list?.tabs_data_type || '';
let tabs_list = {};
// 内容样式
let data_content_container = '';
let data_content_img_container = '';
if (tabs_data_type === 'goods') {
tabs_list = tabs_data_list.goods_config;
const new_style = tabs_data_list.goods_config.style;
// 内容样式
data_content_container = common_styles_computer(new_style?.data_content_style || this.data_content_style);
data_content_img_container = common_img_computer(new_style?.data_content_style || this.data_content_style);
} else if (tabs_data_type === 'article') {
tabs_list = tabs_data_list.article_config;
const new_style = tabs_data_list.article_config.style;
// 内容样式
data_content_container = common_styles_computer(new_style?.data_content_style || this.data_content_style);
data_content_img_container = common_img_computer(new_style?.data_content_style || this.data_content_style);
} else if (tabs_data_type === 'custom') {
tabs_list = tabs_data_list.custom_config;
}
this.setData({
tabs_data_type: tabs_data_type,
tabs_index: index,
tabs_list: tabs_list,
data_content_container: data_content_container,
data_content_img_container: data_content_img_container,
diy_key: get_math(),
});
},
// 获取商品距离顶部的距离
getTop() {
const query = uni.createSelectorQuery().in(this);
query
.select('.data-tabs-' + this.propKey)
.boundingClientRect((res) => {
if ((res || null) != null) {
let new_data = typeof this.propValue == 'string' ? JSON.parse(JSON.stringify(this.propValue)) : this.propValue;
const new_style = new_data.style || {};
this.setData({
tabs_top: res.top - (new_style.common_style?.margin_top || 0),
});
}
})
.exec();
},
goods_buy_event(index, goods = {}, params = {}, back_data = null) {
this.$emit('goods_buy_event', index, goods, params, back_data);
},
goods_cart_back_event(e) {
if ((this.$refs.diy_goods_list || null) != null) {
this.$refs.diy_goods_list.goods_cart_back_event(e);
}
},
},
};
</script>
<style scoped lang="scss"></style>

784
components/diy/diy.vue Normal file
View File

@@ -0,0 +1,784 @@
<template>
<view :style="page_style">
<view :style="page_img_style">
<scroll-view :scroll-y="true" class="ht" @scroll="on_scroll_event" @scrolltolower="on_scroll_lower_event" @scrolltoupper="on_scroll_upper_event" lower-threshold="60" scroll-with-animation="true">
<!-- 头部小程序兼容 -->
<view class="pr header">
<componentDiyHeader :propKey="header_data.id" :propValue="header_data.com_data" :propScrollTop="head_scroll_top" @onImmersionModelCallBack="immersion_model_call_back" @onLocationBack="choice_location_back"></componentDiyHeader>
</view>
<view :style="content_padding">
<view class="content flex-col" :style="'padding-top:calc(' + (temp_is_header_top ? temp_header_top + temp_sticky_top + 'px)' : '0px)')">
<view v-for="item in tabs_data" :key="item.key">
<template v-if="item.is_enable == '1'">
<componentDiyTabs v-if="item.key == 'tabs'" :propIndex="is_immersive_style_and_general_safe_distance_value ? item.index : -1" :propContentPadding="content_padding" :propValue="item.com_data" :propTop="get_tabs_data_prop_top" :propStickyTop="get_tabs_data_prop_sticky_top" :propIsImmersionModel="is_immersion_model && is_the_safe_distance_enabled && item.com_data.content.tabs_top_up == '1'" :propNavIsTop="is_header_top" :propTabsIsTop="true" @onComputerHeight="tabs_height_event" @onTabsTap="tabs_click_event"></componentDiyTabs>
<componentDiyTabsCarousel v-else-if="item.key == 'tabs-carousel'" :propIndex="is_immersive_style_and_general_safe_distance_value ? item.index : -1" :propContentPadding="content_padding" :propValue="item.com_data" :propTop="get_tabs_data_prop_top" :propStickyTop="get_tabs_data_prop_sticky_top" :propIsImmersionModel="is_immersion_model && is_the_safe_distance_enabled" :propScrollTop="scroll_top" :propTabsIsTop="true" @onComputerHeight="tabs_height_event" @onTabsTap="tabs_click_event" @onVideoPlay="video_play"></componentDiyTabsCarousel>
</template>
</view>
<template v-if="is_tabs_type">
<template v-if="diy_data.length > 0">
<view v-for="(item, index) in diy_data" :key="index" :style="'margin-top:' + (['float-window'].includes(item.key) ? '0rpx;z-index:1' : -(item.com_data.style.common_style.floating_up * 2 || 0) + 'rpx;z-index:' + (!isEmpty(item.com_data.style.common_style.module_z_index) ? item.com_data.style.common_style.module_z_index : 0))">
<!-- 基础组件 -->
<template v-if="item.is_enable == '1'">
<componentDiySearch v-if="item.key == 'search'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiySearch>
<componentDiyCarousel v-else-if="item.key == 'carousel'" :propOuterContainerPadding="outer_container_padding" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data" @onVideoPlay="video_play"></componentDiyCarousel>
<componentDiyNavGroup v-else-if="item.key == 'nav-group'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyNavGroup>
<componentDiyUserInfo v-else-if="item.key == 'user-info'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyUserInfo>
<componentDiyNotice v-else-if="item.key == 'notice'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyNotice>
<componentDiyVideo v-else-if="item.key == 'video'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyVideo>
<componentDiyArticleList v-else-if="item.key == 'article-list'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyArticleList>
<componentDiyArticleTabs v-else-if="item.key == 'article-tabs'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data" :propTop="get_diy_prop_top(item.com_data)" :propScrollTop="scroll_top" :propCustomNavHeight="get_diy_custom_nav_height(item.com_data)" :propIsTabsUseSafeDistance="getPropIsTabsUseSafeDistance"></componentDiyArticleTabs>
<componentDataTabs v-else-if="item.key == 'data-tabs'" :ref="'diy_goods_buy' + index" :propIndex="get_prop_index(item)" :propDiyIndex="index" :propKey="item.id + index" :propValue="item.com_data" :propTop="get_diy_prop_top(item.com_data)" :propScrollTop="scroll_top" :propCustomNavHeight="get_diy_custom_nav_height(item.com_data)" :propIsTabsUseSafeDistance="getPropIsTabsUseSafeDistance" @goods_buy_event="goods_buy_event"></componentDataTabs>
<componentDiyGoodsTabs v-else-if="item.key == 'goods-tabs'" :ref="'diy_goods_buy' + index" :propIndex="get_prop_index(item)" :propDiyIndex="index" :propKey="item.id + index" :propValue="item.com_data" :propTop="get_diy_prop_top(item.com_data)" :propScrollTop="scroll_top" :propCustomNavHeight="get_diy_custom_nav_height(item.com_data)" :propIsTabsUseSafeDistance="getPropIsTabsUseSafeDistance" @goods_buy_event="goods_buy_event"></componentDiyGoodsTabs>
<componentDiyGoodsList v-else-if="item.key == 'goods-list'" :ref="'diy_goods_buy' + index" :propIndex="get_prop_index(item)" :propDiyIndex="index" :propKey="item.id + index" :propValue="item.com_data" @goods_buy_event="goods_buy_event"></componentDiyGoodsList>
<componentDiyDataMagic v-else-if="item.key == 'data-magic'" :propOuterContainerPadding="outer_container_padding" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyDataMagic>
<componentDiyCustom v-else-if="item.key == 'custom'" :propOuterContainerPadding="outer_container_padding" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyCustom>
<componentDiyImgMagic v-else-if="item.key == 'img-magic'" :propOuterContainerPadding="outer_container_padding" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyImgMagic>
<componentDiyHotZone v-else-if="item.key == 'hot-zone'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyHotZone>
<componentDiySeckill v-else-if="item.key == 'seckill'" :propOuterContainerPadding="outer_container_padding" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiySeckill>
<!-- 插件 -->
<componentDiyCoupon v-else-if="item.key == 'coupon'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyCoupon>
<!-- 工具组件 -->
<componentDiyFloatWindow v-else-if="item.key == 'float-window'" :propKey="item.id + index" :propValue="item.com_data"></componentDiyFloatWindow>
<componentDiyTitle v-else-if="item.key == 'title'" :propKey="item.id + index" :propIndex="get_prop_index(item)" :propValue="item.com_data"></componentDiyTitle>
<componentDiyAuxiliaryLine v-else-if="item.key == 'row-line'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyAuxiliaryLine>
<componentDiyRichText v-else-if="item.key == 'rich-text'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyRichText>
<componentDiyAuxiliaryBlank v-else-if="item.key == 'auxiliary-blank'" :propIndex="get_prop_index(item)" :propKey="item.id + index" :propValue="item.com_data"></componentDiyAuxiliaryBlank>
</template>
</view>
</template>
<!-- diy底部卡槽 -->
<slot name="diy-bottom-content"></slot>
<slot name="diy-bottom-common"></slot>
</template>
<template v-else>
<!-- 商品九宫格列表 -->
<view v-if="goods_list.length > 0" class="padding-horizontal-main padding-top-main oh">
<component-goods-list :propData="{ style_type: goods_show_type_value, goods_list: goods_list, random: random_value }" :propLabel="plugins_label_data" :propCurrencySymbol="currency_symbol"></component-goods-list>
</view>
<view v-else class="pr">
<!-- 提示信息 -->
<component-no-data :propStatus="goods_list_loding_status" :propMsg="goods_list_loding_msg" propLoadingLogoTop="30%"></component-no-data>
</view>
<!-- diy底部卡槽 -->
<template v-if="goods_bottom_line_status">
<slot name="diy-bottom-content"></slot>
</template>
<slot name="diy-bottom-common"></slot>
</template>
<view class="z-i-deep">
<!-- 商品购买 -->
<component-goods-buy ref="goods_buy" v-on:CartSuccessEvent="goods_cart_back_event"></component-goods-buy>
<!-- 视频播放 -->
<uni-popup ref="popup" type="center" border-radius="20rpx" :mask-click="false">
<view class="flex-col align-c jc-c gap-10">
<video :src="video_src" id="carousel_video" :autoplay="true" :controls="true" show-fullscreen-btn class="radius-md" :style="{ width: popup_width, height: popup_height }"></video>
<iconfont name="icon-qiandao-tancguanbi" size="56rpx" color="#ccc" propContainerDisplay="flex" @tap="video_close"></iconfont>
</view>
</uni-popup>
</view>
</view>
</view>
<!-- 当前diy页面底部菜单非公共底部菜单 -->
<block v-if="is_show_footer">
<componentDiyFooter :propKey="footer_data.id" :propValue="footer_data.com_data" @onFooterHeight="footer_height_value_event"></componentDiyFooter>
<view v-if="footer_height_value > 0" :style="'height:' + footer_height_value + 'rpx;'"></view>
</block>
<!-- 底部卡槽 -->
<slot name="bottom"></slot>
</scroll-view>
</view>
</view>
</template>
<script>
const app = getApp();
import { isEmpty, common_styles_computer, background_computer, padding_computer } from '@/common/js/common/common.js';
import componentDiyHeader from '@/components/diy/header';
import componentDiyFooter from '@/components/diy/footer';
import componentDiyTabs from '@/components/diy/tabs';
import componentDiySearch from '@/components/diy/search';
import componentDiyCarousel from '@/components/diy/carousel';
import componentDiyUserInfo from '@/components/diy/user-info';
import componentDiyNotice from '@/components/diy/notice';
import componentDiyVideo from '@/components/diy/video';
import componentDiyArticleList from '@/components/diy/article-list';
import componentDiyArticleTabs from '@/components/diy/article-tabs';
import componentDiyHotZone from '@/components/diy/hot-zone';
import componentDiyCoupon from '@/components/diy/coupon';
import componentDiyFloatWindow from '@/components/diy/float-window';
import componentDiyTitle from '@/components/diy/title';
import componentDiyAuxiliaryLine from '@/components/diy/auxiliary-line';
import componentDiyRichText from '@/components/diy/rich-text';
import componentDiyAuxiliaryBlank from '@/components/diy/auxiliary-blank';
import componentDiyNavGroup from '@/components/diy/nav-group';
import componentDiyGoodsList from '@/components/diy/goods-list';
import componentDiyGoodsTabs from '@/components/diy/goods-tabs';
import componentDiyDataMagic from '@/components/diy/data-magic';
import componentDiyCustom from '@/components/diy/custom';
import componentDiyImgMagic from '@/components/diy/img-magic';
import componentDiySeckill from '@/components/diy/seckill';
import componentDiyTabsCarousel from '@/components/diy/tabs-carousel';
import componentDataTabs from '@/components/diy/data-tabs';
import componentGoodsList from '@/components/goods-list/goods-list';
import componentNoData from '@/components/no-data/no-data';
import componentBottomLine from '@/components/bottom-line/bottom-line';
import componentGoodsBuy from '@/components/goods-buy/goods-buy';
import componentSearch from '@/components/search/search';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
name: 'diy',
props: {
propValue: {
type: Object,
default: () => ({}),
},
propDataId: {
type: [String, Number],
default: '',
},
propKey: {
type: [String, Number],
default: 0,
},
},
components: {
componentDiyHeader,
componentDiyFooter,
componentDiyTabs,
componentDiySearch,
componentDiyCarousel,
componentDiyUserInfo,
componentDiyNotice,
componentDiyVideo,
componentDiyArticleList,
componentDiyArticleTabs,
componentDiyHotZone,
componentDiyCoupon,
componentDiyAuxiliaryLine,
componentDiyRichText,
componentDiyFloatWindow,
componentDiyTitle,
componentDiyAuxiliaryBlank,
componentDiyNavGroup,
componentDiyGoodsList,
componentDiyGoodsTabs,
componentDiyDataMagic,
componentDiyCustom,
componentDiyImgMagic,
componentDiySeckill,
componentDiyTabsCarousel,
componentDataTabs,
componentGoodsList,
componentNoData,
componentBottomLine,
componentGoodsBuy,
componentSearch,
},
data() {
return {
// 基础配置
currency_symbol: app.globalData.currency_symbol(),
// 是否有选项卡
is_tabs: false,
// 是否是模块数据或者是九宫格商品分类样式数据, 默认模块数据
is_tabs_type: true,
// 是否开启沉浸模式
is_immersion_model: false,
// 5,7,0 是误差,, 10 是下边距66是高度bar_height是不同小程序下的导航栏距离顶部的高度
// #ifdef MP
sticky_top: bar_height + 5 + 10,
// #endif
// #ifdef H5 || MP-TOUTIAO
sticky_top: bar_height + 7 + 10,
// #endif
// #ifdef APP
sticky_top: bar_height + 0 + 10,
// #endif
header_top: '',
temp_sticky_top: 0,
temp_header_top: '0px',
is_header_top: false,
temp_is_header_top: false,
scroll_top: 0,
// 选项卡高度
tabs_height: 0,
header_data: {},
footer_data: {},
// 选项卡数据
tabs_data: {},
diy_data: [],
page_style: '',
page_img_style: '',
is_show_footer: false,
tabs_home_id: this.propDataId,
// 商品列表
goods_list: [],
goods_total: 0,
goods_page_total: 0,
goods_page: 1,
// 数据展示样式0图文、1九方格
goods_show_type_value: 1,
// 增加随机数,避免无法监听数据列表内部数据更新
random_value: 0,
// 标签插件
plugins_label_data: null,
goods_list_loding_status: 1,
goods_list_loding_msg: '',
goods_bottom_line_status: false,
// 判断数据是否在加载中
data_is_loading: 0,
// 缓存key
cache_key: app.globalData.data.cache_diy_data_key,
// 底部导航高度
footer_height_value: 0,
// 商品ref索引
goods_index: 0,
// 视频播放逻辑
video_src: '',
popup_width: '0rpx',
popup_height: '0rpx',
// 顶部导航是否换行
is_search_alone_row: false,
data_alone_row_space: 0,
content_padding: '',
outer_container_padding: 0,
// 滚动延迟器
head_scroll_top: 0,
scroll_throttle_timeout: null,
// 是否开启安全距离
is_the_safe_distance_enabled: false,
// 是否开启置顶
is_tabs_data_topped: false,
};
},
computed: {
get_tabs_data_prop_top() {
// 开启了沉浸式时的处理
if (this.is_immersion_model) {
// 并且开启了安全距离
return this.is_the_safe_distance_enabled ? this.temp_header_top : 0;
} else {
return this.temp_header_top;
}
},
get_tabs_data_prop_sticky_top() {
// 开启了沉浸式时的处理
if (this.is_immersion_model) {
// 并且开启了安全距离
return this.is_the_safe_distance_enabled ? this.temp_sticky_top : 0;
} else {
return this.temp_sticky_top;
}
},
get_prop_index() {
return (item) => {
return this.is_the_safe_distance_enabled && this.tabs_data.length == 0 ? item.index : -1;
}
},
get_diy_prop_top() {
return (item) => {
// 不开启沉浸模式时的处理
if (!this.is_immersion_model) {
return this.temp_sticky_top + this.tabs_height;
} else {
// 开启沉浸模式且开启选项卡置顶时
if (this.is_tabs_data_topped) {
return this.tabs_height;
} else {
// 开启安全距离
let is_general_safe_distance_num = this.temp_sticky_top;
// #ifdef H5 || MP-TOUTIAO
is_general_safe_distance_num = this.is_header_top ? this.temp_sticky_top : 0
// #endif
if (this.is_the_safe_distance_enabled) {
return is_general_safe_distance_num;
} else {
if (item?.content?.is_general_safe_distance == '1') {
return is_general_safe_distance_num;
} else {
return 0;
}
}
}
}
}
},
get_diy_custom_nav_height() {
return (item) => {
let header_height = (this.is_search_alone_row ? 66 + this.data_alone_row_space : 33);
// #ifdef H5 || MP-TOUTIAO
header_height = this.is_header_top ? header_height : 0;
// #endif
// 不开启沉浸模式时的处理
if (!this.is_immersion_model) {
return header_height;
} else {
// 开启沉浸模式且开启选项卡置顶时
if (this.is_tabs_data_topped) {
return 0;
} else {
// 开启沉浸模式时并且开启安全距离
if (this.is_the_safe_distance_enabled) {
return this.is_search_alone_row ? 66 + this.data_alone_row_space : 33;
} else {
if (item?.content?.is_general_safe_distance == '1') {
return header_height;
} else {
return 0;
}
}
}
}
}
},
getPropIsTabsUseSafeDistance() {
let is_tabs_use_safe_distance = this.is_immersion_model;
// #ifdef H5 || MP-TOUTIAO
is_tabs_use_safe_distance = this.is_immersion_model && this.is_header_top;
// #endif
return is_tabs_use_safe_distance || !this.is_immersion_model;
}
},
watch: {
propKey(val) {
// 如果当前存在别的diy或者商品分类tabs则不更新数据
if ((this.tabs_id || null) == null) {
// 初始化
this.init();
}
},
},
created() {
// 初始化配置
this.init_config();
// 初始化
this.init();
},
methods: {
isEmpty,
// 初始化配置
init_config(status) {
if ((status || false) == true) {
// 是否显示底部菜单如果当前地址已经存在系统底部菜单中则不显示当前diy页面自定义的底部菜单
var is_show_footer = parseInt(this.propValue.header.com_data.content.bottom_navigation_show || 0) == 1;
var is_tabbar = app.globalData.is_tabbar_pages();
this.setData({
is_show_footer: is_show_footer && !is_tabbar,
});
// diy页面不显示底部菜单则设置底部菜单高度为0
if(!this.is_show_footer) {
// 存储diy页面底部菜单高度
if(app.globalData.current_page(false) == 'pages/diy/diy') {
app.globalData.app_diy_tabbar_height_save(0);
}
}
} else {
app.globalData.is_config(this, 'init_config');
}
},
// 初始化
init() {
const { header = {}, diy_data = [], tabs_data = [] } = this.propValue;
// 头部的样式
let header_style = header.com_data.style;
let new_diy_index = 0;
let new_tabs_data = [];
let new_diy_data = [];
if (tabs_data.length > 0) {
tabs_data.forEach((item) => {
// 修改item的内容
item = this.get_index_content(new_diy_index, header, header_style, item);
new_tabs_data.push(item);
new_diy_index++;
});
new_diy_data = diy_data;
} else {
new_tabs_data = tabs_data;
// 过滤数据
diy_data.forEach((item) => {
// 判断是否是商品列表
if (item.com_name == 'float-window') {
item.index = -1;
} else {
// 修改item的内容
item = this.get_index_content(new_diy_index, header, header_style, item);
new_diy_data.push(item);
new_diy_index++;
}
});
}
const { padding_right = 0, padding_left = 0 } = header_style.common_style;
const new_is_search_alone_row = header.com_data.content.data_alone_row_value.length > 0 ? true : false;
const new_data_alone_row_space = parseInt(header_style.data_alone_row_space || 0);
// tabs选项卡数据过滤
this.setData({
header_data: header,
footer_data: this.propValue.footer,
diy_data: new_diy_data,
tabs_data: new_tabs_data,
page_style: common_styles_computer(header_style.common_style),
page_img_style: background_computer(header_style.common_style),
// 内间距
content_padding: `padding: 0px ${padding_right}px 0px ${padding_left}px;` + 'box-sizing:border-box;',
outer_container_padding: padding_right + padding_left,
// 判断顶部导航是否置顶
is_header_top: parseInt(header_style.up_slide_display) == 1 ? true : false,
is_tabs_data_topped: new_tabs_data[0]?.com_data?.content?.tabs_top_up == '1' || false,
temp_sticky_top: this.sticky_top,
temp_header_top: (new_is_search_alone_row ? 66 + new_data_alone_row_space : 33),
header_top: (new_is_search_alone_row ? 66 + new_data_alone_row_space : 33),
is_immersion_model: header_style.header_background_type !== 'color_image' && header_style.immersive_style == '1',
// 顶部导航高度是否变化--------------------------------------------------
is_search_alone_row: new_is_search_alone_row,
data_alone_row_space: new_data_alone_row_space,
is_immersive_style_and_general_safe_distance_value: header_style.immersive_style == '1' && header_style.general_safe_distance_value == '1',
is_the_safe_distance_enabled: header_style.immersive_style == '1' && header_style.general_safe_distance_value == '1',// diy_data是否开启安全距离
});
// 缓存数据
uni.setStorageSync(this.cache_key + this.tabs_home_id, diy_data);
},
// 顶部导航沉浸模式回调
// immersion_model_call_back(bool) {
// this.setData({
// is_immersion_model: bool,
// });
// },
get_index_content(new_diy_index, header, header_style, item) {
item.index = new_diy_index;
if (new_diy_index == 0) {
// 判断是否开启沉浸模式和是否开启安全距离 如果为true则除了选项卡和选项卡轮播外 第一个组件则加上安全距离样式的padding_top加上顶部导航的高度和安全距离的高度
if (header_style.immersive_style == '1' && header_style.general_safe_distance_value == '1') {
let new_data = JSON.parse(JSON.stringify(item));
// 顶部导航的高度
let header_top_height = (header.com_data.content.data_alone_row_value.length > 0 ? parseInt(header.com_data.style.data_alone_row_space || 5) : 0) + 33 + (header.com_data.content.data_alone_row_value.length > 0 ? 33 : 0);
new_data.com_data.style.common_style.padding_top = parseInt(new_data.com_data.style.common_style.padding_top) + header_top_height;
return new_data;
}
return item;
}
return item;
},
// 选项卡回调更新数据
tabs_click_event(tabs_id, bool, params = {}) {
let new_data = [];
this.setData({
is_tabs_type: bool,
tabs_id: tabs_id,
});
let new_params = {
...params,
id: tabs_id,
};
if (tabs_id) {
new_data = uni.getStorageSync(this.cache_key + tabs_id) || [];
if (new_data.length > 0) {
// 先使用缓存数据展示
this.setData({
diy_data: new_data,
});
// 已有本地缓存则直接取远程有效数据(默认首次取的是远程缓存数据)
new_params['is_cache'] = 0;
}
// diy数据
if (bool) {
uni.showLoading({
title: this.$t('common.loading_in_text'),
mask: true,
});
uni.request({
url: app.globalData.get_request_url('index', 'diy'),
method: 'POST',
data: new_params,
dataType: 'json',
success: (res) => {
uni.hideLoading();
// 数据处理
let data = res.data.data.data;
if (res.data.code == 0) {
new_data = data?.config.diy_data || [];
uni.setStorageSync(this.cache_key + tabs_id, new_data);
this.setData({
diy_data: new_data,
});
// 是否需要重新加载数据
if (parseInt(data.is_result_data_cache || 0) == 1) {
this.tabs_click_event(tabs_id, bool, { is_cache: 0 });
}
} else {
app.globalData.showToast(res.data.msg);
}
},
fail: () => {
uni.hideLoading();
app.globalData.showToast(this.$t('common.internet_error_tips'));
},
});
} else {
this.setData({
goods_page: 1,
goods_list: [],
goods_list_loding_status: 1,
goods_bottom_line_status: false,
});
this.get_goods_list(1);
}
} else {
if (tabs_id == '') {
new_data = uni.getStorageSync(this.cache_key + this.tabs_home_id) || [];
}
// 先使用缓存数据展示
this.setData({
diy_data: new_data,
});
}
},
// 选项卡高度
tabs_height_event(height) {
let new_tabs_height = 0;
// 判断是否有选项卡切选项卡数组数据内的字段is_enable值是否为1
if (this.tabs_data.length > 0) {
this.tabs_data.forEach((item, index) => {
if (item.is_enable == '1') {
new_tabs_height = height;
}
});
}
this.setData({
tabs_height: new_tabs_height,
});
},
// 滚动加载
on_scroll_lower_event(e) {
if (!this.is_tabs_type) {
this.get_goods_list();
}
},
// 滚动到顶部
on_scroll_upper_event() {
setTimeout(() => {
this.head_scroll_top = 0;
});
},
// 查询商品
get_goods_list(is_mandatory) {
// 分页是否还有数据
if ((is_mandatory || 0) == 0) {
if (this.goods_bottom_line_status == true) {
uni.stopPullDownRefresh();
return false;
}
}
// 是否加载中
if (this.data_is_loading == 1) {
return false;
}
this.setData({
data_is_loading: 1,
});
// 获取数据
if (this.goods_page > 1) {
uni.showLoading({
title: this.$t('common.loading_in_text'),
});
}
let new_data = {
category_id: this.tabs_id,
page: this.goods_page,
};
// 九宫格数据
uni.request({
url: app.globalData.get_request_url('datalist', 'search'),
method: 'POST',
data: new_data,
dataType: 'json',
success: (res) => {
if (this.goods_page > 1) {
uni.hideLoading();
}
uni.stopPullDownRefresh();
if (res.data.code == 0) {
var data = res.data.data;
if (data.data.length > 0) {
if (this.goods_page <= 1) {
var temp_goods_list = data.data;
} else {
var temp_goods_list = this.goods_list || [];
var temp_data = data.data;
for (var i in temp_data) {
temp_goods_list.push(temp_data[i]);
}
}
this.setData({
goods_list: temp_goods_list,
random_value: Math.random(),
goods_total: data.total,
goods_page_total: data.page_total,
goods_list_loding_status: 3,
goods_list_loding_msg: '',
goods_page: this.goods_page + 1,
data_is_loading: 0,
});
// 是否还有数据
this.setData({
goods_bottom_line_status: this.goods_page > 1 && this.goods_page > this.goods_page_total,
});
} else {
this.setData({
goods_list_loding_status: 0,
goods_list_loding_msg: res.data.msg,
goods_total: 0,
data_is_loading: 0,
});
if (this.goods_page <= 1) {
this.setData({
goods_list: [],
goods_bottom_line_status: false,
});
}
}
} else {
this.setData({
goods_list_loding_status: 0,
goods_list_loding_msg: res.data.msg,
data_is_loading: 0,
});
app.globalData.is_login_check(res.data, this, 'get_goods_list', is_mandatory);
}
},
fail: () => {
if (this.goods_page > 1) {
uni.hideLoading();
}
uni.stopPullDownRefresh();
this.setData({
goods_list_loding_status: 2,
goods_list_loding_msg: this.$t('common.internet_error_tips'),
data_is_loading: 0,
});
},
});
},
// 页面滚动事件
on_scroll_event(e) {
const scroll_num = parseInt(e.detail.scrollTop);
if (scroll_num <= 20) {
this.head_scroll_top = 0;
} else {
if (scroll_num / (this.sticky_top + 33) <= 1) {
// 更新数据的逻辑
this.head_scroll_top = scroll_num;
} else {
this.head_scroll_top = this.sticky_top + 100;
}
}
// #ifdef H5 || MP-TOUTIAO
// 判断顶部导航是否置顶
if (!this.is_header_top && !this.is_immersion_model) {
if (scroll_num >= this.sticky_top + 33 + (this.is_search_alone_row ? 0 : 33 + this.data_alone_row_space)) {
this.temp_sticky_top = 0;
this.temp_header_top = 0;
this.temp_sticky_no_h5_top = 0;
this.temp_is_header_top = true;
} else {
this.temp_header_top = this.header_top;
this.temp_sticky_top = this.sticky_top;
this.temp_sticky_no_h5_top = this.sticky_top;
this.temp_is_header_top = false;
}
}
//#endif
this.scroll_timer_compute(scroll_num);
},
scroll_timer_compute(scroll_num) {
// 使用节流技术减少事件触发的处理次数
if (!this.scroll_throttle_timeout) {
const self = this;
this.scroll_throttle_timeout = setTimeout(() => {
this.scroll_top = scroll_num;
// 清除定时器
this.scroll_throttle_timeout = null;
}, 20); // 可以根据实际情况调整延时时间
}
},
// 底部菜单高度
footer_height_value_event(value) {
this.setData({
footer_height_value: (value*2)+20,
});
// 存储diy页面底部菜单高度
if(app.globalData.current_page(false) == 'pages/diy/diy') {
app.globalData.app_diy_tabbar_height_save(value);
}
},
// 商品数量更新回调
goods_buy_event(index, goods = {}, params = {}, back_data = null) {
if ((this.$refs.goods_buy || null) != null) {
this.goods_index = index;
this.$refs.goods_buy.init(goods, params, back_data);
}
},
// 商品加购回调
goods_cart_back_event(e) {
if ((this.$refs[`diy_goods_buy${this.goods_index}`][0] || null) != null) {
this.$refs[`diy_goods_buy${this.goods_index}`][0].goods_cart_back_event(e);
}
},
// 视频播放
video_play(url, width, height) {
this.setData({
video_src: url,
popup_width: width,
popup_height: height,
});
this.$refs.popup.open();
const videoContext = uni.createVideoContext('carousel_video');
if (!isEmpty(videoContext)) {
videoContext.play();
}
},
// 视频关闭
video_close() {
const videoContext = uni.createVideoContext('carousel_video');
if (!isEmpty(videoContext)) {
videoContext.pause();
}
this.$refs.popup.close();
},
// 位置回调
choice_location_back(e) {
// 如果存在tabs_id则表示当前有选择tab数据则仅当前模块更新无需给上级回调位置
if ((this.tabs_id || null) == null) {
this.$emit('onLocationBack', e);
} else {
this.tabs_click_event(this.tabs_id, this.is_tabs_type);
}
},
},
};
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,164 @@
<template>
<movable-area class="float-window-movable-container">
<movable-view :x="x" :y="y" direction="all" class="float-window-spread flex-row align-c jc-c" @tap="btn_event">
<block v-if="style.float_style == 'diffuse'">
<view class="ring" :style="content_style"></view>
<view class="ring" :style="content_style"></view>
</block>
<view class="img oh" :style="content_style">
<block v-if="(form || null) != null && form.button_jump == 'customer_service'">
<component-online-service :propChatImage="img_url" :propIsSpread="false" :propIsMovable="false"></component-online-service>
</block>
<block v-else>
<imageEmpty :propImageSrc="img_url" propImgFit="aspectFill" propErrorStyle="width: 60rpx;height: 60rpx;"></imageEmpty>
</block>
</view>
</movable-view>
<component-quick-nav ref="quick_nav" :propIsBtn="false"></component-quick-nav>
</movable-area>
</template>
<script>
const app = getApp();
import { isEmpty } from '@/common/js/common/common.js';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
import componentOnlineService from '@/components/online-service/online-service';
import componentQuickNav from '@/components/quick-nav/quick-nav';
export default {
components: {
imageEmpty,
componentOnlineService,
componentQuickNav
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
}
},
data() {
return {
form: {},
style: {},
img_url: '',
x: 0,
y: 0,
content_style: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
}
},
created() {
this.init();
},
methods: {
init() {
// 获取内容
let form = this.propValue.content || {};
// 获取图片
let img_url = (form.button_img || null) != null ? (form.button_img[0] || null) : null;
if (img_url != null) {
img_url = img_url.url || null;
}
const { float_style, float_style_color, display_location, offset_number_percentage } = this.propValue.style;
// 获取当前手机的宽度和高度
const { windowWidth, windowHeight } = uni.getSystemInfoSync();
// 计算出距离左边的距离
let x = display_location == 'left' ? 10 : windowWidth - 60;
// 计算出距离顶部的距离
const y = Math.ceil(windowHeight * (1 - Number(offset_number_percentage)) - 20);
this.setData({
form: form,
style: this.propValue.style,
img_url: img_url,
content_style: float_style == 'shadow' ? `box-shadow: 0 0 40rpx ${float_style_color};border-radius: 50%;` : `background-color: ${float_style_color};border-radius: 50%;`,
x: x,
y: y
});
},
// 按钮事件
btn_event() {
const { button_jump, button_link } = this.form;
switch(button_jump) {
// 链接
case 'link' :
if (!isEmpty(button_link)) {
app.globalData.url_open(button_link.page);
}
break;
// 快捷导航
case 'quick_nav' :
if ((this.$refs.quick_nav || null) != null) {
this.$refs.quick_nav.quick_open_event();
}
break;
}
},
},
};
</script>
<style scoped lang="scss">
.img {
width: 90rpx;
height: 90rpx;
border-radius: 50%;
z-index: 2;
}
.float-window-movable-container {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: transparent;
pointer-events: none;
z-index: 103;
}
/**
* 呼吸灯
*/
.float-window-spread {
position: relative;
pointer-events: auto;
z-index: 1;
width: 100rpx;
height: 100rpx;
border-radius: 50%;
}
.float-window-spread .ring {
/* 速度为1.5 * 层数 = 实际运行速度,速度修改则 animation-delay 属性也修改相同速度 */
animation: pulsing 1.5s ease-out infinite;
border-radius: 100%;
width: 100rpx;
height: 100rpx;
position: absolute;
}
/* 速度为1*层数 */
.float-window-spread .ring:nth-of-type(1) {
-webkit-animation-delay: -1.5s;
animation-delay: -1.5s;
}
/* 速度为1*层数 */
.float-window-spread .ring:nth-of-type(2) {
-webkit-animation-delay: -2s;
animation-delay: -2s;
}
@keyframes pulsing {
100% {
transform: scale(1.35);
opacity: 0;
}
}
</style>

201
components/diy/footer.vue Normal file
View File

@@ -0,0 +1,201 @@
<template>
<!-- 底部导航 -->
<view v-if="(propValue || null) !== null" class="footer-nav flex-row jc-c align-c" :class="nav_type == 1 ? 'bottom-line-exclude' : ''">
<view class="flex-1 wh-auto" :style="style_container">
<view class="footer-nav-content flex-row jc-c align-c wh-auto" :style="style_img_container">
<view class="flex-row jc-c align-c wh-auto" :class="nav_type == 0 ? 'bottom-line-exclude' : ''">
<view class="flex-row jc-sa align-c wh padding-0">
<block v-for="(item, index) in nav_content" :key="index">
<view class="flex-1 flex-col jc-c align-c gap-5 pr" :data-value="item.link.page || ''" @tap="url_event">
<view v-if="nav_style != 2" class="img-content pr">
<view class="img-item pa border-radius-xs animate-linear" :class="active_index != index ? 'active' : ''">
<template v-if="item.img.length > 0">
<image :src="item.img[0].url" class="img dis-block" model="widthFix"></image>
</template>
</view>
<view class="img-item pa border-radius-xs animate-linear" :class="active_index == index ? 'active' : ''">
<template v-if="item.img_checked.length > 0">
<image :src="item.img_checked[0].url" class="img dis-block" model="widthFix"></image>
</template>
</view>
</view>
<text v-if="nav_style != 1" class="animate-linear text-size-xs pr z-i" :style="active_index == index ? text_color_checked : default_text_color">{{ item.name }}</text>
<view v-if="(item.badge || null) != null" class="pa badge-icon">
<component-badge :propNumber="item.badge"></component-badge>
</view>
</view>
</block>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
let app = getApp();
import { common_styles_computer, common_img_computer } from '@/common/js/common/common.js';
import componentBadge from '@/components/badge/badge';
export default {
props: {
propKey: {
type: [String,Number],
default: '',
},
propValue: {
type: Object,
default: null,
},
// 底部选中索引
propFooterActiveIndex: {
type: Number,
default: 0,
}
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
nav_content: [],
nav_type: 0,
nav_style: 0,
default_text_color: '',
text_color_checked: '',
active_index: 0,
};
},
components: {
componentBadge,
},
// 属性值改变监听
watch: {
// 唯一key
propKey(value, old_value) {
this.init();
},
// 选中索引
propFooterActiveIndex(value, old_value) {
this.init();
},
},
// 页面被展示
created: function () {
this.init();
},
methods: {
// 初始化
init() {
if ((this.propValue || null) !== null) {
let new_content = this.propValue.content || {};
let new_style = this.propValue.style || {};
let nav_content = new_content.nav_content || [];
let page = app.globalData.current_page() || null;
let active_index = this.propFooterActiveIndex;
if (page != null) {
// 角标链接定义
let badge_arr = {
'/pages/cart/cart': 'cart',
'/pages/cart-page/cart-page': 'cart',
};
for (var i in nav_content) {
if ((nav_content[i]['link'] || null) != null && (nav_content[i]['link']['page'] || null) != null) {
// 选中索引
if (nav_content[i]['link']['page'] == '/' + page) {
active_index = i;
}
// 获取角标数据
var badge_key = badge_arr[nav_content[i]['link']['page']];
if (badge_key !== undefined) {
nav_content[i]['badge'] = app.globalData.get_tab_bar_badge(badge_key);
}
}
}
}
this.setData({
nav_content: nav_content,
nav_type: new_content.nav_type || 0,
nav_style: new_content.nav_style || 0,
active_index: active_index,
default_text_color: 'color:' + new_style.default_text_color || 'rgba(0, 0, 0, 1)',
text_color_checked: 'color:' + new_style.text_color_checked || 'rgba(204, 204, 204, 1)',
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style),
});
// 高度计算
let nav_min_height = 70;
var nav_height = parseInt(new_style.common_style.padding_top) + parseInt(new_style.common_style.padding_bottom) + 44;
if (nav_height < nav_min_height) {
nav_height = nav_min_height;
}
let footer_height = nav_height + parseInt(new_style.common_style.margin_top) + parseInt(new_style.common_style.margin_bottom);
// #ifndef H5
// 底部菜单距离底部的安全距离减去20、默认的安全距离太高了
var safe_areaInsets = uni.getSystemInfoSync().safeAreaInsets || {};
var bottom = parseInt(safe_areaInsets.bottom || 0);
if (bottom > 0) {
bottom -= 24;
}
footer_height += bottom;
// #endif
// 回调高度
this.$emit('onFooterHeight', footer_height);
}
},
// 跳转链接
url_event(e) {
let index = e.currentTarget.dataset.index;
let item = this.nav_content[index];
app.globalData.url_event(e);
this.$emit('onFooterTap', index, item);
},
},
};
</script>
<style lang="scss" scoped>
.footer-nav {
z-index: 102;
position: fixed;
bottom: 0;
left: 0;
right: 0;
width: 100%;
margin: 0 auto;
background-color: transparent;
.footer-nav-content {
min-height: 140rpx;
box-sizing: border-box;
.img-content {
width: 44rpx;
height: 44rpx;
.img-item {
width: 44rpx;
height: 44rpx;
opacity: 0;
&.active {
opacity: 1;
}
.img {
width: 44rpx;
height: 44rpx;
}
}
}
.badge-icon {
margin-top: -70rpx;
margin-right: -70rpx;
}
}
}
/* #ifdef H5 */
@media only screen and (min-width: 1600rpx) {
.footer-nav {
max-width: 1600rpx;
}
}
/* #endif */
</style>

View File

@@ -0,0 +1,607 @@
<template>
<view v-if="!isEmpty(list)" class="oh" :style="style_container" @tap.stop="onTap">
<view class="oh" :style="style_img_container">
<view :class="outer_class" :style="onter_style">
<block v-if="!['5'].includes(theme)">
<view v-for="(item, index) in list" :key="index" class="pr oh" :style="layout_style" :data-index="index" :data-value="item.goods_url" @tap.stop="url_event">
<view :class="layout_type" :style="layout_img_style">
<block v-if="theme == '6'">
<view :class="['flex-row align-c jc-sb ptb-15 mlr-10 gap-20', { 'br-b-e': index != list.length - 1 }]">
<view v-if="is_show('title')" :class="text_line" :style="title_style">{{ item.title }}</view>
<view v-if="is_show('price') && !isEmpty(item.min_price)" class="num nowrap" :style="'color:' + new_style.shop_price_color">
<text class="identifying">{{ item.show_price_symbol }}</text>
<text :style="price_style">{{ item.min_price }}</text>
<text v-if="is_show('price_unit')" class="identifying">{{ item.show_price_unit }}</text>
</view>
</view>
</block>
<block v-else>
<block v-if="!isEmpty(item)">
<view class="oh pr" :class="img_size">
<view v-if="!isEmpty(item.new_cover)" :style="img_size">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="content_img_radius" propErrorStyle="width: 100rpx;height: 100rpx;"></imageEmpty>
</view>
<view v-else :style="img_size">
<imageEmpty :propImageSrc="item.images" :propStyle="content_img_radius" propErrorStyle="width: 100rpx;height: 100rpx;"></imageEmpty>
</view>
<!-- 角标 -->
<subscriptIndex :propValue="propValue"></subscriptIndex>
</view>
</block>
<view v-if="is_show('title') || is_show('simple_desc') || is_show('price') || is_show('original_price') || is_show('sales_count') || is_show('plugins_view_icon') || form.is_shop_show == '1'" class="flex-col flex-1 jc-sb content gap-10" :style="content_style">
<view class="flex-col gap-10 top-title">
<view 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 * 2 + 'rpx' }">
<view v-if="is_show('title')" :class="text_line" :style="title_style">{{ item.title }}</view>
<view v-if="['0', '1', '2', '3', '5'].includes(theme) && is_show('simple_desc')" :class="form.simple_desc_row == '2' ? 'text-line-2' : 'text-line-1'" :style="simple_desc">{{ item.simple_desc }}</view>
</view>
<view v-if="show_content && is_show('plugins_view_icon') && !isEmpty(item.plugins_view_icon_data)" class="flex-row gap-5 align-c">
<view v-for="(icon_data, icon_index) in item.plugins_view_icon_data" :key="icon_index" class="radius text-size-xsss padding-horizontal-xs" :style="{ background: icon_data.bg_color, color: icon_data.color, border: '1rpx solid' + (!isEmpty(icon_data.br_color) ? icon_data.br_color : icon_data.bg_color) }">{{ icon_data.name }}</view>
</view>
</view>
<view v-if="!['3', '4', '5'].includes(form.theme)" class="flex-col gap-5">
<view :class="[form.is_price_solo == '1' ? 'flex-row align-c nowrap' : 'flex-col gap-5']">
<view v-if="is_show('price') && !isEmpty(item.min_price)" class="num" :style="'color:' + new_style.shop_price_color">
<text :style="price_symbol">{{ item.show_price_symbol }}</text>
<text :style="price_style">{{ item.min_price }}</text>
<text v-if="is_show('price_unit')" :style="price_unit">{{ item.show_price_unit }}</text>
</view>
<view v-if="show_content && is_show('original_price') && !isEmpty(item.min_original_price)" class="text-size-xss flex-row">
<!-- <image v-if="form.static_img.length > 0" class="original-price-left" :src="form.static_img[0].url" model="widthFix"></image> -->
<text :class="['original-price text-line-1', { 'flex-1': form.is_price_solo == '1' }]" :style="original_price">
{{ item.show_original_price_symbol }}{{ item.min_original_price }}
<block v-if="is_show('original_price_unit')">
{{ item.show_original_price_unit }}
</block>
</text>
</view>
</view>
<view class="flex-row jc-sb align-e">
<view>
<view v-if="show_content" class="flex-row align-c text-size-xss">
<view v-if="is_show('sales_count') && !isEmpty(item.sales_count)" class="pr-5" :style="sold_number_style">已售{{ item.sales_count || 0 }}</view>
<!-- <view v-if="is_show('sales_count')" :class="['pr-5', {'br-r-e': is_show('sales_count') && is_show('4')}]" :style="sold_number_style>已售{{ item.sales_count }}件</view> -->
<!-- <view v-if="is_show('4')" class="pl-5" :style="score_style">评分0</view> -->
</view>
</view>
<view v-if="(form.is_shop_show == '1' && form.shop_button_effect == '1' && item.is_error == 0) || (form.is_shop_show == '1' && form.shop_button_effect == '0')" class="pr" :data-index="index" @tap.stop="goods_button_event">
<block v-if="form.shop_type == 'text'">
<view class="plr-11 padding-vertical-xs round" :style="button_style + ('color:' + new_style.shop_button_text_color)">{{ form.shop_button_text }}</view>
</block>
<view v-else class="round padding-horizontal-sm ptb-5" :style="button_gradient">
<iconfont :name="'icon-' + (!isEmpty(form.shop_button_icon_class) ? form.shop_button_icon_class : 'cart')" :color="new_style.shop_icon_color" :size="new_style.shop_icon_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
<view v-if="form.shop_button_effect == '1'" class="cart-badge-icon pa badge-style">
<component-badge :propNumber="item.user_cart_count || 0"></component-badge>
</view>
</view>
</view>
</view>
<view v-else class="flex-row align-c jc-sb">
<view class="flex-row align-c nowrap">
<view v-if="is_show('price') && !isEmpty(item.min_price)" class="num" :style="'color:' + new_style.shop_price_color">
<text :style="price_symbol">{{ item.show_price_symbol }}</text>
<text :style="price_style">{{ item.min_price }}</text>
<text v-if="is_show('price_unit')" :style="price_unit">{{ item.show_price_unit }}</text>
</view>
<view v-if="show_content && is_show('original_price') && !isEmpty(item.min_original_price)" class="text-size-xss flex-row">
<!-- <image v-if="form.static_img.length > 0" class="original-price-left" :src="form.static_img[0].url" model="widthFix"></image> -->
<text :class="['original-price text-line-1', { 'flex-1': form.is_price_solo == '1' }]" :style="original_price">
{{ item.show_original_price_symbol }}{{ item.min_original_price }}
<block v-if="is_show('original_price_unit')">
{{ item.show_original_price_unit }}
</block>
</text>
</view>
</view>
<view v-if="(form.is_shop_show == '1' && form.shop_button_effect == '1' && item.is_error == 0) || (form.is_shop_show == '1' && form.shop_button_effect == '0')" class="pr" :data-index="index" @tap.stop="goods_button_event">
<block v-if="form.shop_type == 'text'">
<view class="plr-11 padding-vertical-xs round" :style="button_style + ('color:' + new_style.shop_button_text_color)">{{ form.shop_button_text }}</view>
</block>
<view v-else class="round padding-horizontal-sm ptb-5" :style="button_gradient">
<iconfont :name="'icon-' + (!isEmpty(form.shop_button_icon_class) ? form.shop_button_icon_class : 'cart')" :color="new_style.shop_icon_color" :size="new_style.shop_icon_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
<view v-if="form.shop_button_effect == '1'" class="cart-badge-icon pa badge-style">
<component-badge :propNumber="item.user_cart_count || 0"></component-badge>
</view>
</view>
</view>
</view>
</block>
</view>
</view>
</block>
<block v-else>
<swiper circular="true" :autoplay="new_style.is_roll == '1'" :interval="new_style.interval_time * 1000" :duration="500" :next-margin="new_style.rolling_fashion == 'translation' ? '-' + content_outer_spacing_magin : '0rpx'" :display-multiple-items="slides_per_group" :style="{ width: '100%', height: new_style.content_outer_height * new_scale + 'px' }">
<swiper-item v-for="(item1, index1) in shop_content_list" :key="index1">
<view class="flex-row wh-auto ht-auto" :style="onter_style">
<view v-for="(item, index) in item1.split_list" :key="index" class="pr oh" :style="layout_style" :data-index="index1" :data-split-index="index" :data-value="item.goods_url" @tap.stop="url_event">
<view :class="layout_type" :style="layout_img_style">
<block v-if="!isEmpty(item)">
<view class="oh pr" :class="'flex-img' + theme">
<view v-if="!isEmpty(item.new_cover)" :class="'flex-img' + theme">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="content_img_radius" propErrorStyle="width: 100rpx;height: 100rpx;"></imageEmpty>
</view>
<view v-else :class="'flex-img' + theme">
<imageEmpty :propImageSrc="item.images" :propStyle="content_img_radius" propErrorStyle="width: 100rpx;height: 100rpx;"></imageEmpty>
</view>
<!-- 角标 -->
<subscriptIndex :propValue="propValue"></subscriptIndex>
</view>
</block>
<view 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">
<view class="flex-col gap-10 top-title">
<view 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 * 2 + 'rpx' }">
<view v-if="is_show('title')" :class="text_line" :style="title_style">{{ item.title }}</view>
<view v-if="['0', '1', '2', '3', '5'].includes(theme) && is_show('simple_desc')" :class="form.simple_desc_row == '2' ? 'text-line-2' : 'text-line-1'" :style="simple_desc">{{ item.simple_desc }}</view>
</view>
<view v-if="show_content && is_show('plugins_view_icon') && !isEmpty(item.plugins_view_icon_data)" class="flex-row gap-5 align-c">
<view v-for="(icon_data, icon_index) in item.plugins_view_icon_data" :key="icon_index" class="radius text-size-xsss padding-horizontal-xs" :style="{ background: icon_data.bg_color, color: icon_data.color, border: '1rpx solid' + (!isEmpty(icon_data.br_color) ? icon_data.br_color : icon_data.bg_color) }">{{ icon_data.name }}</view>
</view>
</view>
<view class="flex-row align-c jc-sb">
<view class="flex-row align-c nowrap">
<view v-if="is_show('price') && !isEmpty(item.min_price)" class="num" :style="'color:' + new_style.shop_price_color">
<text :style="price_symbol">{{ item.show_price_symbol }}</text>
<text :style="price_style">{{ item.min_price }}</text>
<text v-if="is_show('price_unit')" :style="price_unit">{{ item.show_price_unit }}</text>
</view>
<view v-if="show_content && is_show('original_price') && !isEmpty(item.min_original_price)" class="text-size-xss flex">
<!-- <image v-if="form.static_img.length > 0" class="original-price-left" :src="form.static_img[0].url" model="widthFix"></image> -->
<text :class="['original-price text-line-1', { 'flex-1': form.is_price_solo == '1' }]" :style="original_price">
{{ item.show_original_price_symbol }}{{ item.min_original_price }}
<block v-if="is_show('original_price_unit')">
{{ item.show_original_price_unit }}
</block>
</text>
</view>
</view>
<view v-if="(form.is_shop_show == '1' && form.shop_button_effect == '1' && item.is_error == 0) || (form.is_shop_show == '1' && form.shop_button_effect == '0')" class="pr" :data-index="index1" :data-split-index="index" @tap.stop="goods_button_event">
<block v-if="form.shop_type == 'text'">
<view class="plr-11 padding-vertical-xs round" :style="button_style + ('color:' + new_style.shop_button_text_color)">{{ form.shop_button_text }}</view>
</block>
<view v-else class="round padding-horizontal-sm ptb-5" :style="button_gradient">
<iconfont :name="'icon-' + (!isEmpty(form.shop_button_icon_class) ? form.shop_button_icon_class : 'cart')" :color="new_style.shop_icon_color" :size="new_style.shop_icon_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
<view v-if="form.shop_button_effect == '1'" class="cart-badge-icon pa badge-style">
<component-badge :propNumber="item.user_cart_count || 0"></component-badge>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
</block>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { isEmpty, common_styles_computer, common_img_computer, gradient_handle, padding_computer, radius_computer, background_computer, border_computer, box_shadow_computer, old_margin, margin_computer } from '@/common/js/common/common.js';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
import subscriptIndex from '@/components/diy/modules/subscript/index.vue';
import componentBadge from '@/components/badge/badge';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
components: {
imageEmpty,
componentBadge,
subscriptIndex
},
props: {
propValue: {
type: Object,
default: () => ({}),
},
propIsCommonStyle: {
type: Boolean,
default: true,
},
propKey: {
type: [String, Number],
default: '',
},
propDiyIndex: {
type: Number,
default: 0,
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 0,
},
},
data() {
return {
form: {},
new_style: {},
propIsCartParaCurve: false,
list: [],
content_radius: '', // 圆角设置
content_img_radius: '', // 图片圆角设置
content_padding: '', // 内边距设置
theme: '', // 选择的风格
content_outer_spacing: '', // 商品间距
content_outer_spacing_magin: '', // 商品间距
// 最外层不同风格下的显示
outer_class: '',
onter_style: '',
// 不同风格下的样式
layout_type: '',
layout_style: '',
layout_img_style: '',
content_style: '', // 内容区域的样式
show_content: false, // 显示除标题外的其他区域
text_line: '', // 超过多少行隐藏
style_container: '', // 公共样式
style_img_container: '',
shop_content_list: [],
slides_per_group: 1,
// 内容样式
title_style: '',
price_style: '',
sold_number_style: '',
score_style: '',
button_style: '',
simple_desc: '',
price_symbol: '',
price_unit: '',
original_price: '',
// 按钮背景色
button_gradient: '',
// 图片大小
img_size: '',
new_scale: 1,
};
},
watch: {
propKey(val) {
this.init();
},
propValue(new_value, old_value) {
this.init();
},
},
created() {
this.init();
},
methods: {
isEmpty,
init() {
const new_form = this.propValue.content || null;
const new_style = this.propValue.style || null;
if (new_form != null && new_style != null) {
let new_list = [];
// 指定商品并且指定商品数组不为空
if (!isEmpty(new_form.data_list) && new_form.data_type == '0') {
new_list = new_form.data_list.map((item) => ({
...item.data,
title: !isEmpty(item.new_title) ? item.new_title : item.data.title,
new_cover: item.new_cover,
}));
} else if (!isEmpty(new_form.data_auto_list) && new_form.data_type == '1') {
// 筛选商品并且筛选商品数组不为空
new_list = new_form.data_auto_list;
}
// 最外层不同风格下的显示
const flex = ['0', '2', '6'].includes(new_form.theme) ? 'flex-col ' : 'flex-row ';
const wrap = new_form.theme == '5' ? '' : 'flex-wrap ';
const background = ['6'].includes(new_form.theme) ? 'bg-white ' : '';
const button_gradient = gradient_handle(new_style.shop_button_color, '180deg');
// 默认数据
const product_style_list = [
{ name: '单列展示', value: '0', width: 110, height: 120 },
{ name: '大图展示', value: '2', width: 166, height: 166 },
{ name: '无图模式', value: '6', width: 0, height: 0 },
{ name: '两列展示(纵向)', value: '1', width: 180, height: 180 },
{ name: '两列展示(横向)', value: '4', width: 70, height: 70 },
{ name: '三列展示', value: '3', width: 116, height: 114 },
{ name: '左右滑动展示', value: '5', width: 0, height: 0 },
];
const scale = sys_width / 390;
let img_style = ``;
if (['0', '4'].includes(new_form.theme)) {
// 图片宽度
if (typeof new_style.content_img_width == 'number') {
img_style += `width: ${ new_style.content_img_width * scale }px;`;
} else {
const list = product_style_list.filter(item => item.value == new_form.theme);
if (list.length > 0) {
img_style += `width: ${ list[0].width * scale }px;`;
} else {
img_style += 'width: auto;';
}
}
}
if (!['5', '6'].includes(new_form.theme)) {
// 图片宽度
if (typeof new_style.content_img_height == 'number') {
img_style += `height: ${ new_style.content_img_height * scale }px;`;
} else {
const list = product_style_list.filter(item => item.value == new_form.theme);
if (list.length > 0) {
img_style += `height: ${ list[0].height * scale }px;`;
} else {
img_style += 'height: auto;';
}
}
}
this.setData({
form: new_form,
new_style: new_style,
outer_class: flex + wrap + background + 'oh',
list: new_list,
new_scale: scale,
content_radius: radius_computer(new_style.shop_radius), // 圆角设置
content_img_radius: radius_computer(new_style.shop_img_radius), // 图片圆角设置
content_padding: padding_computer(new_style.shop_padding) + 'box-sizing: border-box;', // 内边距设置
theme: new_form.theme, // 选择的风格
content_outer_spacing: new_style.content_outer_spacing, // 商品间距
content_outer_spacing_magin: new_style.content_outer_spacing * 2 + 'rpx',
onter_style: new_form.theme == '6' ? radius_computer(new_style.shop_radius) : `gap: ${new_style.content_outer_spacing * 2 + 'rpx'};`,
// 不同风格下的样式
layout_type: ['0', '4'].includes(new_form.theme) ? 'flex-row wh-auto ht-auto oh' : 'flex-col wh-auto ht-auto oh',
layout_style: this.get_layout_style(new_style, new_form),
layout_img_style: this.get_layout_img_style(new_style, new_form),
content_style: this.get_content_style(new_style, new_form), // 内容区域的样式
show_content: ['0', '1', '2'].includes(new_form.theme), // 显示除标题外的其他区域
text_line: this.get_text_line(new_form), // 超过多少行隐藏
style_container: this.propIsCommonStyle ? common_styles_computer(new_style.common_style) : '', // 公共样式
style_img_container: this.propIsCommonStyle ? common_img_computer(new_style.common_style, this.propIndex) : '', // 图片样式
// 内容样式设置
button_gradient: button_gradient,
title_style: this.trends_config(new_style, 'title', 'title', new_form),
price_style: this.trends_config(new_style, 'price'),
sold_number_style: this.trends_config(new_style, 'sold_number'),
score_style: this.trends_config(new_style, 'score'),
button_style: this.trends_config(new_style, 'button', 'buy_button') + button_gradient,
simple_desc: this.trends_config(new_style, 'simple_desc', 'desc', new_form),
price_symbol: !isEmpty(new_style.shop_price_symbol_color) ? this.trends_config(new_style, 'price_symbol') : 'font-size: 18rpx;color: #EA3323;' ,
price_unit: !isEmpty(new_style.shop_price_unit_color) ? this.trends_config(new_style, 'price_unit') : 'font-size: 18rpx;color: #EA3323;',
original_price: this.trends_config(new_style, 'original_price'),
shop_content_list: this.get_shop_content_list(new_list, new_form, new_style),
slides_per_group: new_style.rolling_fashion == 'translation' ? new_form.carousel_col : 1,
img_size: img_style,
});
}
},
get_shop_content_list(list, form, new_style) {
// 深拷贝一下,确保不会出现问题
const cloneList = JSON.parse(JSON.stringify(list));
if (new_style.rolling_fashion != 'translation') {
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (cloneList.length > 0) {
// 每页显示的数量
const num = form.carousel_col;
// 存储数据显示
let 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,
},
];
}
} else {
// 存储数据显示
let nav_list = [];
cloneList.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
return nav_list;
}
},
get_text_line(form) {
let line = '';
if (['1', '6'].includes(form.theme)) {
line = 'text-line-1';
} else if (['0', '2', '3', '4', '5'].includes(form.theme)) {
line = 'text-line-2';
}
return line;
},
// 不同风格下的样式
get_layout_style(new_style, form) {
const { shop_margin = old_margin } = new_style;
const radius = form.theme == '6' ? '' : radius_computer(new_style.shop_radius);
const style = form.theme != '6' ? gradient_handle(new_style?.shop_color_list || [], new_style?.shop_direction || '') + margin_computer(shop_margin) + border_computer(new_style) + box_shadow_computer(new_style) : '';
let size_style = ``;
const shop_left_right_width = shop_margin.margin_left + shop_margin.margin_right;
if (['1', '4'].includes(form.theme)) {
size_style = `width: calc((100% - ${new_style.content_outer_spacing * 2 + (shop_left_right_width * 4) + 'rpx'}) / 2);`;
} else if (form.theme == '3') {
size_style = `width: calc((100% - ${new_style.content_outer_spacing * 4 + (shop_left_right_width * 6) + 'rpx'}) / 3);`;
} else if (form.theme == '5') {
// 如果不是平移的时候执行
if (new_style.rolling_fashion != 'translation') {
size_style = `width: ${this.get_multicolumn_columns_width(new_style, form)};min-width: ${this.get_multicolumn_columns_width(new_style, form)};height: ${new_style.content_outer_height * (sys_width / 390) + 'px'};`;
} else {
size_style = `margin-right: ${ (new_style.content_outer_spacing * 2) + (shop_margin.margin_right * 2) }rpx;width: 100%;height: 100%;`;
}
} else if (form.theme == '0') {
size_style = `width: calc(100% - ${ shop_left_right_width }px);`;
}
return `${radius} ${ style } ${size_style}`;
},
get_layout_img_style(new_style, form) {
const padding = ['0', '4'].includes(form.theme) ? padding_computer(new_style.shop_padding) + 'box-sizing: border-box;' : '';
const data = {
background_img_style: new_style?.shop_background_img_style || '',
background_img: new_style?.shop_background_img || '',
}
const background = form.theme != '6' ? background_computer(data) : '';
return `${padding} ${background}`;
},
get_multicolumn_columns_width(new_style, form) {
const { carousel_col } = form;
// 计算间隔的空间。(gap * gap数量) / 模块数量
let gap = (new_style.content_outer_spacing * (carousel_col - 1)) / carousel_col;
return `calc(${100 / carousel_col}% - ${gap * 2}rpx)`;
},
get_content_style(new_style, form) {
const spacing_value = new_style.content_spacing;
let spacing = '';
if (['0', '4'].includes(form.theme)) {
spacing = `margin-left: ${spacing_value * 2}rpx;`;
} else {
spacing = padding_computer(new_style.shop_padding) + 'box-sizing: border-box;';
}
return `${spacing}`;
},
// 判断是否显示对应的内容
is_show(index) {
return this.form.is_show.includes(index);
},
// 根据传递的参数,从对象中取值
trends_config(new_style, key, type, new_form) {
return this.style_config(new_style[`shop_${key}_typeface`], new_style[`shop_${key}_size`], new_style[`shop_${key}_color`], type, new_form);
},
// 根据传递的值,显示不同的内容
style_config(typeface, size, color, type, new_form) {
let style = `font-weight:${typeface}; font-size: ${size * 2}rpx;`;
if (type == 'title') {
if (['1', '6'].includes(new_form.theme)) {
style += `line-height: ${size * 2}rpx;height: ${size * 2}rpx;color: ${color};`;
} else if (['0', '2', '3', '4', '5'].includes(new_form.theme)) {
style += `line-height: ${size > 0 ? (size + 3) * 2 : 0}rpx;height: ${size > 0 ? (size + 3) * 4 : 0}rpx;color: ${color};`;
}
} else if (type == 'desc') {
if (new_form.simple_desc_row == '2') {
style += `line-height: ${size > 0 ? (size + 3) * 2 : 0}rpx;height: ${size > 0 ? (size + 3) * 4 : 0}rpx;color: ${color};`;
} else {
style += `line-height: ${size * 2}rpx;height: ${size * 2}rpx;color: ${color};`;
}
} else {
if (type != 'buy_button') {
style += `color: ${color};`;
}
}
return style;
},
// icon标志显示样式
icon_style(item) {
let style = `background: ${item.bg_color};color: ${item.color};`;
if (!isEmpty(item.br_color)) {
style += `border: 2rpx solid ${item.br_color};`;
} else {
style += `border: 2rpx solid ${item.bg_color};`;
}
return style;
},
url_event(e) {
let index = e.currentTarget.dataset.index || 0;
let goods = this.list[index];
let split_index = 0;
if (this.theme == '5') {
split_index = e.currentTarget.dataset.splitIndex || 0;
goods = this.shop_content_list[index].split_list[split_index];
}
app.globalData.goods_data_cache_handle(goods.id, goods);
app.globalData.url_event(e);
},
goods_button_event(e) {
this.goods_cart_event(e);
},
// 加入购物车
goods_cart_event(e) {
let index = e.currentTarget.dataset.index || 0;
let split_index = 0;
let goods = this.list[index];
if (this.theme == '5') {
split_index = e.currentTarget.dataset.splitIndex || 0;
goods = this.shop_content_list[index].split_list[split_index];
}
if (this.form.shop_button_effect == '0') {
app.globalData.goods_data_cache_handle(goods.id, goods);
app.globalData.url_open(goods.goods_url);
} else {
// 开启购物车抛物线效果则展示提示操作
let is_success_tips = this.propIsCartParaCurve ? 0 : 1;
this.$emit(
'goods_buy_event',
this.propDiyIndex,
goods,
{
buy_event_type: 'cart',
is_direct_cart: 1,
is_success_tips: is_success_tips,
},
{
index: index,
split_index: split_index,
pos: e,
}
);
}
},
// 加入购物车成功回调
goods_cart_back_event(e) {
app.globalData.showToast('加入成功', 'success');
// 增加数量
var { index, split_index } = e.back_data;
let new_data = this.list;
let goods = new_data[index];
if (this.theme == '5') {
new_data = this.shop_content_list;
goods = new_data[index][split_index];
}
goods['user_cart_count'] = parseInt(goods['user_cart_count'] || 0) + parseInt(e.stock);
if (goods['user_cart_count'] > 99) {
goods['user_cart_count'] = '99+';
}
if (this.theme != '5') {
new_data[index] = goods;
this.setData({
list: new_data,
});
} else {
new_data[index][split_index] = goods;
this.setData({
shop_content_list: new_data,
});
}
},
},
};
</script>
<style scoped lang="scss">
.identifying {
font-size: 18rpx;
}
.original-price {
padding: 0 20rpx;
}
.flex-img5 {
width: 100%;
height: 100%;
}
// .original-price-left {
// width: 20rpx;
// height: 28rpx;
// }
.br-b-e {
border-bottom: 2rpx solid #eee;
}
.badge-style {
top: -20rpx;
right: 10rpx;
}
</style>

View File

@@ -0,0 +1,253 @@
<template>
<view class="goods-tabs ou" :class="'goods-tabs-' + propKey" :style="style_container">
<view class="ou" :style="style_img_container">
<componentDiyModulesTabsView :propKey="propKey" :propValue="goods_tabs" :propIsTop="top_up == '1'" :propTop="sticky_top" :propStyle="tabs_style" :propsTabsContainer="tabs_container" :propsTabsImgContainer="tabs_img_container" :propCustomNavHeight="propIsTabsUseSafeDistance ? (propCustomNavHeight * 2 + 'rpx') : '0rpx'" :propTabsSlidingFixedBg="tabs_sliding_fixed_bg" :propTabsBackground="tabs_background" @onTabsTap="tabs_click_event"></componentDiyModulesTabsView>
<view :style="shop_margin_top">
<view :style="shop_container">
<view :style="shop_img_container">
<componentGoodsList ref="diy_goods_list" :propKey="diy_key" :propDiyIndex="propDiyIndex" :propValue="goods_tabs" :propIsCommonStyle="false" @goods_buy_event="goods_buy_event"></componentGoodsList>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, padding_computer, margin_computer, background_computer, gradient_computer, isEmpty, old_border_and_box_shadow, old_margin, old_radius, old_padding, border_computer, box_shadow_computer, radius_computer } from '@/common/js/common/common.js';
import componentDiyModulesTabsView from '@/components/diy/modules/tabs-view';
import componentGoodsList from '@/components/diy/goods-list';
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
components: {
componentDiyModulesTabsView,
componentGoodsList,
},
props: {
propValue: {
type: Object,
default: () => ({}),
},
propTop: {
type: Number,
default: 0,
},
propCustomNavHeight: {
type: Number,
default: 33,
},
propScrollTop: {
type: Number,
default: 0,
},
// 顶部导航是否开启沉浸模式
propIsImmersionModel: {
type: Boolean,
default: false,
},
propKey: {
type: [String, Number],
default: '',
},
propDiyIndex: {
type: Number,
default: 0,
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 0,
},
propIsTabsUseSafeDistance: {
type: Boolean,
default: true
}
},
data() {
return {
style_container: '',
style_img_container: '',
goods_tabs: {},
// 是否滑动置顶
top_up: '0',
tabs_top: 0,
tabs_background: 'background:transparent',
custom_nav_height: 33,
diy_key: '',
// 选项卡背景设置
tabs_container: '',
tabs_img_container: '',
tabs_sliding_fixed_bg: '',
// 商品区域背景设置
shop_margin_top: '',
shop_container: '',
shop_img_container: '',
// #ifdef MP
nav_safe_space: bar_height + 5,
// #endif
// #ifdef H5 || MP-TOUTIAO
nav_safe_space: bar_height + 7,
// #endif
// #ifdef APP
nav_safe_space: bar_height + 0,
// #endif
// 选项卡默认数据
tabs_index: 0,
sticky_top: 0,
};
},
watch: {
propScrollTop(newVal) {
if (newVal + this.sticky_top + this.custom_nav_height > this.tabs_top + this.nav_safe_space && this.top_up == '1') {
let new_style = this.propValue.style || {};
let tabs_bg = new_style.common_style.color_list;
let new_tabs_background = '';
if (tabs_bg.length > 0 && (tabs_bg[0].color || null) != null) {
new_tabs_background = gradient_computer(new_style.common_style);
}
let new_tabs_background_img = background_computer(new_style.common_style);
if (new_tabs_background_img.length > 0) {
new_tabs_background_img += 'background-position: top left;';
}
this.tabs_background = (new_tabs_background.length > 0 ? new_tabs_background : 'background:#fff;') + new_tabs_background_img;
} else {
this.tabs_background = 'background:transparent';
}
},
propTop(val) {
this.init();
},
propKey(val) {
// 初始化
this.setData({
diy_key: val,
});
this.init();
},
},
created() {
this.init();
},
mounted() {
this.$nextTick(() => {
const self = this;
setTimeout(() => {
self.getTop();
});
// #ifdef H5 || MP-TOUTIAO
// 获取自定义导航栏高度
this.setData({
custom_nav_height: this.propCustomNavHeight,
});
// #endif
});
},
methods: {
init() {
let new_data = typeof this.propValue == 'string' ? JSON.parse(JSON.stringify(this.propValue)) : this.propValue;
const new_content = new_data.content || {};
const new_style = new_data.style || {};
const new_tabs_data = new_data.content.tabs_list[this.tabs_index] || {};
// 产品的值
new_data.content.data_type = new_tabs_data.data_type;
new_data.content.category = new_tabs_data.category;
new_data.content.brand = new_tabs_data.brand;
new_data.content.number = new_tabs_data.number;
new_data.content.sort = new_tabs_data.sort;
new_data.content.sort_rules = new_tabs_data.sort_rules;
new_data.content.data_list = new_tabs_data.data_list;
new_data.content.data_auto_list = new_tabs_data.data_auto_list;
// 公共样式
const common_style = new_style.common_style;
let tabs_style_obj = {
padding_top: common_style.padding_top - this.propCustomNavHeight < 0 ? 0 : common_style.padding_top - this.propCustomNavHeight,
padding_left: common_style.padding_left,
padding_right: common_style.padding_right,
};
let new_tabs_style = padding_computer(tabs_style_obj) + `position:relative;left: -${tabs_style_obj.padding_left * 2}rpx;right: -${tabs_style_obj.padding_right * 2}rpx;width:100%;`;
// 如果是历史数据的话,就执行默认添加下边距
if (isEmpty(new_style.tabs_padding)) {
new_tabs_style += 'padding-bottom: 20rpx;';
}
const { tabs_bg_color_list = [], tabs_bg_direction = '', tabs_bg_background_img_style = '', tabs_bg_background_img = [], tabs_radius = old_radius, tabs_padding = old_padding, shop_content_color_list = [], shop_content_direction = '', shop_content_background_img_style = '', shop_content_background_img = [], shop_content_margin = old_margin, shop_content_padding = old_padding, shop_content_radius = old_radius } = new_style;
// 选项卡背景设置
const tabs_data = {
color_list: tabs_bg_color_list,
direction: tabs_bg_direction,
background_img_style: tabs_bg_background_img_style,
background_img: tabs_bg_background_img,
};
// 商品区域背景设置
const shop_content_data = {
color_list: shop_content_color_list,
direction: shop_content_direction,
background_img_style: shop_content_background_img_style,
background_img: shop_content_background_img,
};
const shop_content = new_style?.shop_content || old_border_and_box_shadow;
const tabs_content = new_style?.tabs_content || old_border_and_box_shadow;
this.setData({
top_up: new_content.tabs_top_up,
sticky_top: this.propTop + (new_style?.tabs_margin?.margin_top || 0),
goods_tabs: new_data,
style_container: common_styles_computer(common_style),
style_img_container: common_img_computer(common_style, this.propIndex),
tabs_style: new_tabs_style,
tabs_sliding_fixed_bg: gradient_computer(tabs_data),
tabs_container: gradient_computer(tabs_data) + radius_computer(tabs_radius) + margin_computer(new_style?.tabs_margin || old_margin) + border_computer(tabs_content) + box_shadow_computer(tabs_content) + 'overflow: hidden;',
tabs_img_container: background_computer(tabs_data) + padding_computer(tabs_padding) + 'box-sizing: border-box;overflow: hidden;',
shop_margin_top: 'margin-top:' + (new_style?.shop_content_spacing || 0) * 2 + 'rpx;',
shop_container: gradient_computer(shop_content_data) + margin_computer(shop_content_margin) + radius_computer(shop_content_radius) + box_shadow_computer(shop_content) + border_computer(shop_content) + 'overflow: hidden;',
shop_img_container: background_computer(shop_content_data) + padding_computer(shop_content_padding) + 'box-sizing: border-box;overflow: hidden;',
});
},
tabs_click_event(index) {
let new_data = JSON.parse(JSON.stringify(this.propValue));
new_data.content.data_type = new_data.content.tabs_list[index].data_type;
new_data.content.category = new_data.content.tabs_list[index].category;
new_data.content.brand = new_data.content.tabs_list[index].brand;
new_data.content.number = new_data.content.tabs_list[index].number;
new_data.content.sort = new_data.content.tabs_list[index].sort;
new_data.content.sort_rules = new_data.content.tabs_list[index].sort_rules;
new_data.content.data_list = new_data.content.tabs_list[index].data_list;
new_data.content.data_auto_list = new_data.content.tabs_list[index].data_auto_list;
this.setData({
tabs_index: index,
goods_tabs: new_data,
diy_key: Math.random(),
});
},
// 获取商品距离顶部的距离
getTop() {
const query = uni.createSelectorQuery().in(this);
query
.select('.goods-tabs-' + this.propKey)
.boundingClientRect((res) => {
if ((res || null) != null) {
let new_data = typeof this.propValue == 'string' ? JSON.parse(JSON.stringify(this.propValue)) : this.propValue;
const new_style = new_data.style || {};
this.setData({
tabs_top: res.top - (new_style.common_style?.margin_top || 0),
});
}
})
.exec();
},
goods_buy_event(index, goods = {}, params = {}, back_data = null) {
this.$emit('goods_buy_event', index, goods, params, back_data);
},
goods_cart_back_event(e) {
if ((this.$refs.diy_goods_list || null) != null) {
this.$refs.diy_goods_list.goods_cart_back_event(e);
}
},
},
};
</script>
<style scoped lang="scss"></style>

420
components/diy/header.vue Normal file
View File

@@ -0,0 +1,420 @@
<template>
<view v-if="(propValue || null) !== null" class="header-container">
<view class="header-around wh-auto" :style="roll_style + position">
<view class="wh-auto" :style="roll_img_style">
<view class="wh-auto ht-auto pa up_slide_bg" :style="up_slide_style">
<view class="wh-auto ht-auto" :style="up_slide_img_style"></view>
</view>
<view :style="top_content_style">
<view class="header-content flex-row align-s">
<view class="model-top flex-1 mt-1">
<view class="roll pr z-i">
<view class="model-head pr padding-left-main padding-right-main" style="box-sizing: border-box">
<view class="flex-col" :style="'gap:' + data_alone_row_space">
<view class="model-head-content flex-row align-c jc-sb gap-16 wh-auto pr" :style="header_style">
<!-- 支付宝小程序自带返回按钮这里就不给返回按钮了这里给留出一点空间就行 -->
<!-- #ifndef MP-ALIPAY -->
<view v-if="!is_tabbar_pages" class="z-i dis-inline-block margin-top-xs" @tap="top_nav_left_back_event">
<iconfont name="icon-arrow-left" size="40rpx" propContainerDisplay="flex" :color="form.style.left_back_btn_color || '#333'"></iconfont>
</view>
<!-- #endif -->
<!-- #ifdef MP-ALIPAY -->
<view class="dis-inline-block padding-left-sm"></view>
<!-- #endif -->
<view v-if="['1', '2', '3'].includes(form.content.theme)" class="flex-1">
<view class="flex-row align-c jc-c ht-auto gap-16" :class="position_class" :style="text_style + 'justify-content:' + form.content.indicator_location || 'center'">
<template v-if="['2', '3'].includes(form.content.theme)">
<view v-if="form.content.logo.length > 0" class="logo-outer-style re flex-row align-c">
<template v-if="form.style.up_slide_logo && form.style.up_slide_logo.length > 0">
<!-- 有上滑logo的处理逻辑 -->
<view v-if="(propScrollTop - 5) / (header_top + 33) < 1" class="logo-style" :style="up_slide_old_logo_style">
<image class="logo-style" :src="form.content.logo[0].url" mode="heightFix" />
</view>
<view :class="['logo-style left-0', { pa: (propScrollTop - 5) / (header_top + 33) < 1 }]" :style="'opacity:0;' + up_slide_opacity">
<image class="logo-style" :src="form.style.up_slide_logo[0].url" mode="heightFix" />
</view>
</template>
<template v-else>
<!-- 没有上滑logo的处理逻辑 -->
<image class="logo-style" :src="form.content.logo[0].url" mode="heightFix" />
</template>
</view>
</template>
<view v-if="['1', '2', '3'].includes(form.content.theme) && !isEmpty(form.content.title)">{{ form.content.title }}</view>
<template v-if="['3'].includes(form.content.theme) && !is_search_alone_row">
<view class="flex-1 fw-n">
<componentDiySearch :propValue="form" :propIsPageSettings="true"></componentDiySearch>
</view>
</template>
</view>
</view>
<view v-else-if="['4', '5'].includes(form.content.theme)" class="flex-1 flex-row align-c">
<view v-if="form.content.positioning_name_float !== '1'" class="flex-row align-c gap-2">
<view :style="location_margin">
<component-choice-location :propLocationContainerStyle="style_location_container" :propLocationImgContainerStyle="style_location_img_container" :propBaseColor="location_color" :propTextDefaultName="form.content.positioning_name" :propIsLeftIconArrow="form.content.is_location_left_icon_show == '1'" :propLeftImgValue="form.content.location_left_img" :propLeftIconValue="'icon-' + form.content.location_left_icon" :propIconLocationSize="location_left_size" :propIconArrowSize="location_right_size" :propIsRightIconArrow="form.content.is_location_right_icon_show == '1'" :propRightImgValue="form.content.location_right_img" :propRightIconValue="'icon-' + form.content.location_right_icon" :propTextMaxWidth="location_name_style" propContainerDisplay="flex" @onBack="choice_location_back"></component-choice-location>
</view>
</view>
<template v-if="['5'].includes(form.content.theme) && !is_search_alone_row">
<view class="flex-1">
<componentDiySearch :propValue="form" :propIsPageSettings="true" :propLocationMargin="location_margin" propSearchType="header" :propLocationContainerStyle="style_location_container" :propLocationImgContainerStyle="style_location_img_container" :propBaseColor="location_color" :propIconLocationSize="location_left_size" :propIconArrowSize="location_right_size" @onBack="choice_location_back"></componentDiySearch>
</view>
</template>
</view>
<view v-if="!isEmpty(form.content.icon_setting) && !is_icon_alone_row" class="flex-row align-c z-i" :class="['1'].includes(form.content.theme) ? 'right-0' : ''" :style="{ gap: form.style.img_space * 2 + 'rpx' }">
<view v-for="(item, index) in form.content.icon_setting" :key="index" class="pr" :style="{ width: form.style.img_size * 2 + 'rpx', height: form.style.img_size * 2 + 'rpx' }" :data-value="item.link.page" @tap="url_event">
<imageEmpty v-if="item.img.length > 0" :propImageSrc="item.img[0].url" :propErrorStyle="'width: ' + Number(form.style.img_size) * 2 + 'rpx;height:' + Number(form.style.img_size) * 2 + 'rpx;'"></imageEmpty>
<iconfont v-else :name="'icon-' + item.icon" :size="form.style.img_size * 2 + 'rpx'" :color="form.style.img_color" propContainerDisplay="flex"></iconfont>
<view v-if="!isEmpty(item.badge) && item.badge !== 0" class="pa badge-style">
<component-badge :propNumber="item.badge || 0"></component-badge>
</view>
</view>
</view>
</view>
<view v-if="is_search_alone_row || is_icon_alone_row" class="model-head-content flex-row align-c gap-16">
<template v-if="['3', '5'].includes(form.content.theme) && is_search_alone_row">
<view class="flex-1">
<componentDiySearch :propValue="form" :propIsPageSettings="true" :propLocationMargin="location_margin" propSearchType="header" :propLocationContainerStyle="style_location_container" :propLocationImgContainerStyle="style_location_img_container" :propBaseColor="location_color" :propIconLocationSize="location_left_size" :propIconArrowSize="location_right_size" @onBack="choice_location_back"></componentDiySearch>
</view>
</template>
<view v-if="!isEmpty(form.content.icon_setting) && is_icon_alone_row" class="flex-row align-c z-i" :class="['1'].includes(form.content.theme) ? 'right-0' : ''" :style="{ gap: form.style.img_space * 2 + 'rpx' }">
<view v-for="(item, index) in form.content.icon_setting" :key="index" class="pr" :style="{ width: form.style.img_size * 2 + 'rpx', height: form.style.img_size * 2 + 'rpx' }" :data-value="item.link.page" @tap="url_event">
<imageEmpty v-if="item.img.length > 0" :propImageSrc="item.img[0].url" :propErrorStyle="'width: ' + Number(form.style.img_size) * 2 + 'rpx;height:' + Number(form.style.img_size) * 2 + 'rpx;'"></imageEmpty>
<iconfont v-else :name="'icon-' + item.icon" :size="form.style.img_size * 2 + 'rpx'" :color="form.style.img_color" propContainerDisplay="flex"></iconfont>
<view v-if="!isEmpty(item.badge) && item.badge !== 0" class="pa badge-style">
<component-badge :propNumber="item.badge || 0"></component-badge>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<block v-if="!is_immersion_model">
<view v-if="!is_positon_realative" class="nav-seat" :style="top_content_style">
<view :style="'height:' + (is_search_alone_row || is_icon_alone_row ? 'calc(132rpx + ' + data_alone_row_space + ');' : '66rpx;')"></view>
</view>
</block>
<!-- #ifndef H5 || MP-TOUTIAO -->
<!-- <view v-if="is_positon_realative" class="wh-auto pf top-0 left-0 right-0" :style="roll_style">
<view :style="top_content_style">
<view :style="'height:' + (is_search_alone_row || is_icon_alone_row ? 'calc(132rpx + ' + data_alone_row_space + ');' : '66rpx;')"></view>
</view>
</view> -->
<!-- #endif -->
</view>
</template>
<script>
const app = getApp();
import componentDiySearch from '@/components/diy/search';
import imageEmpty from '@/components/diy/modules/image-empty';
import componentChoiceLocation from '@/components/choice-location/choice-location';
import componentBadge from '@/components/badge/badge';
import { isEmpty, background_computer, gradient_computer, margin_computer, padding_computer, radius_computer } from '@/common/js/common/common.js';
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
props: {
propValue: {
type: [String, Number, Object],
default: '',
},
// 滚动距离
propScrollTop: {
type: Number,
default: 0,
},
propKey: {
type: [String, Number],
default: '',
},
},
components: {
componentDiySearch,
imageEmpty,
componentChoiceLocation,
componentBadge,
},
data() {
return {
form: {},
new_style: {},
position: '',
roll_style: '',
text_style: '',
header_style: 'max-width:100%',
common_app_is_header_nav_fixed: 0,
// 5,7,0 是误差,, 10 是下边距66是高度bar_height是不同小程序下的导航栏距离顶部的高度
// #ifdef MP
top_content_style: 'padding-top:' + (bar_height + 5) + 'px;padding-bottom:10px;',
// #endif
// #ifdef H5 || MP-TOUTIAO
top_content_style: 'padding-top:' + (bar_height + 7) + 'px;padding-bottom:10px;',
// #endif
// #ifdef APP
top_content_style: 'padding-top:' + bar_height + 'px;padding-bottom:10px;',
// #endif
is_positon_realative: false,
// 顶部背景样式类别
header_background_type: 'color_image',
// #ifdef MP
header_top: bar_height + 5 + 10 + 33,
// #endif
// #ifdef H5 || MP-TOUTIAO
header_top: bar_height + 7 + 10 + 33,
// #endif
// #ifdef APP
header_top: bar_height + 0 + 10 + 33,
// #endif
// 判断是否是沉浸模式
is_immersion_model: false,
up_slide_opacity: '',
up_slide_old_logo_style: '',
up_slide_style: '',
up_slide_img_style: '',
// 当前页面是否在底部菜单中
is_tabbar_pages: app.globalData.is_tabbar_pages(),
// 判断header的查询是否独行
is_search_alone_row: false,
is_icon_alone_row: false,
data_alone_row_space: '0rpx',
// 定位设置
style_location_container: '',
style_location_img_container: '',
location_left_size: '24rpx',
location_right_size: '24rpx',
location_margin: '', // 悬浮之后有间距所以要将margin设置成外padding
location_color: '', // 定位颜色
location_name_style: '', // 定位样式
// 默认数据
old_radius: { radius: 0, radius_top_left: 0, radius_top_right: 0, radius_bottom_left: 0, radius_bottom_right: 0 },
old_padding: { padding: 0, padding_top: 0, padding_bottom: 0, padding_left: 0, padding_right: 0 },
old_margin: { margin: 0, margin_top: 0, margin_bottom: 0, margin_left: 0, margin_right: 0 },
};
},
watch: {
// 监听滚动距离
propScrollTop(newVal) {
const { up_slide_background_color_list, up_slide_background_direction, up_slide_background_img, up_slide_background_img_style, up_slide_display } = this.propValue.style || {};
if (up_slide_display == '1') {
// 渐变
const gradient = { color_list: up_slide_background_color_list, direction: up_slide_background_direction };
// 背景图
const back = { background_img: up_slide_background_img, background_img_style: up_slide_background_img_style };
const up_slide_opacity = 'opacity:' + ((newVal - 20) / this.header_top > 1 ? 1 : ((newVal - 20) / this.header_top).toFixed(2)) + ';';
this.up_slide_opacity = up_slide_opacity;
// 来的logo要比新的隐藏的快所以要比原来的logo快一点
this.up_slide_old_logo_style = 'opacity:' + ((newVal - 5) / this.header_top > 1 ? 0 : (1 - (newVal - 5) / this.header_top).toFixed(2)) + ';';
// =0是大小误差
this.up_slide_style = gradient_computer(gradient) + up_slide_opacity;
this.up_slide_img_style = background_computer(back);
}
},
propKey(val) {
if ((this.propValue || null) !== null) {
this.init();
}
},
},
created() {
// 判断是否有值初始化
if ((this.propValue || null) !== null) {
this.init();
}
},
methods: {
// 判断是否为空
isEmpty,
// 初始化
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
let new_roll_style = '';
let new_roll_img_style = '';
const { header_background_img, header_background_img_style, header_background_color_list, header_background_direction, header_background_type, immersive_style } = new_style;
if (header_background_type === 'color_image') {
// 渐变
const gradient = { color_list: header_background_color_list, direction: header_background_direction };
// 背景图
const back = { background_img: header_background_img, background_img_style: header_background_img_style };
new_roll_style += gradient_computer(gradient);
new_roll_img_style += background_computer(back);
} else {
new_roll_style += `background: transparent;`;
}
// 小程序下,获取小程序胶囊的宽度
let menu_button_info = 'max-width:100%';
let new_text_style = `font-weight:${new_style.header_background_title_typeface}; font-size: ${new_style.header_background_title_size * 2}rpx; color: ${new_style.header_background_title_color};`;
// #ifndef MP-TOUTIAO
// #ifdef MP
// 判断是否有胶囊
const is_current_single_page = app.globalData.is_current_single_page();
// 如果有胶囊的时候,做处理
if (is_current_single_page == 0) {
const custom = uni.getMenuButtonBoundingClientRect();
menu_button_info = `max-width:calc(100% - ${custom.width + 10}px);`;
new_text_style += `right:-${custom.width + 10}px;`;
}
// #endif
// #endif
const { location_margin = this.old_margin } = new_style;
this.setData({
form: this.propValue,
position: new_style.up_slide_display == '1' ? 'position:fixed;' : new_style.immersive_style === '1' ? 'position:absolute;' : 'position:relative;',
is_positon_realative: new_style.up_slide_display == '1' ? false : true,
roll_style: new_roll_style,
roll_img_style: new_roll_img_style,
text_style: new_text_style,
position_class: new_content.indicator_location == 'center' ? `indicator-center` : '',
header_style: menu_button_info,
header_background_type: header_background_type,
is_immersion_model: header_background_type !== 'color_image' && immersive_style == '1',
data_alone_row_space: new_style.data_alone_row_space * 2 + 'rpx',
is_search_alone_row: new_content.data_alone_row_value && new_content.data_alone_row_value.includes('search'),
is_icon_alone_row: new_content.data_alone_row_value && new_content.data_alone_row_value.includes('icon'),
style_location_container: this.get_style_location_container(new_style),
style_location_img_container: this.get_style_location_img_container(new_style),
location_left_size: !isEmpty(new_style.location_left_icon_size) ? new_style.location_left_icon_size * 2 + 'rpx' : '24rpx',
location_right_size: !isEmpty(new_style.location_right_icon_size) ? new_style.location_right_icon_size * 2 + 'rpx' : '24rpx',
location_color: !isEmpty(new_style.location_color) ? new_style.location_color : new_style?.position_color || '',
location_name_style: this.get_location_name_style(new_content),
location_margin: `padding: ${location_margin.margin_top * 2}rpx ${location_margin.margin_right * 2}rpx ${location_margin.margin_bottom * 2}rpx ${location_margin.margin_left * 2}rpx;`, // 悬浮之后有间距所以要将margin设置成外padding
});
// this.$emit('onImmersionModelCallBack', this.is_immersion_model);
},
get_location_name_style(new_content) {
const is_search_alone_row = new_content.data_alone_row_value && new_content.data_alone_row_value.includes('search');
const is_icon_alone_row = new_content.data_alone_row_value && new_content.data_alone_row_value.includes('icon');
let width = 0;
if (is_search_alone_row && is_icon_alone_row) {
width = 200;
} else if (is_search_alone_row || is_icon_alone_row) {
width = 100;
}
if (new_content.theme == '4') {
return `${(150 + width) * 2}rpx;`;
} else {
return `${(100 + width) * 2}rpx;`;
}
},
// 定位设置
get_style_location_container(new_style) {
const { location_margin = this.old_margin, location_radius = this.old_radius } = new_style;
const style = {
color_list: new_style?.location_color_list || [],
direction: new_style?.location_direction || '',
};
const height = 32 - location_margin.margin_top - location_margin.margin_bottom;
return gradient_computer(style) + radius_computer(location_radius) + `height: ${height * 2}rpx;line-height: ${height * 2}rpx;`;
},
// 背景图片
get_style_location_img_container(new_style) {
const { location_background_img = [], location_background_img_style = '2', location_border_show = '0', location_padding = this.old_padding, location_margin = this.old_margin, location_border_size = this.old_padding, location_border_color = '', location_border_style = 'solid' } = new_style;
const style = {
background_img: location_background_img,
background_img_style: location_background_img_style,
};
let border = ``;
if (location_border_show == '1') {
border += `border-width: ${location_border_size.padding_top}px ${location_border_size.padding_right}px ${location_border_size.padding_bottom}px ${location_border_size.padding_left}px;border-style: ${location_border_style};border-color: ${location_border_color};`;
}
const height = 32 - (location_margin.margin_top || 0) - (location_margin.margin_bottom || 0);
return background_computer(style) + padding_computer(location_padding) + border + `height: ${(height > 0 ? height : 0) * 2}rpx;line-height: ${(height > 0 ? height : 0) * 2}rpx;box-sizing: border-box;`;
},
// 获取顶部导航高度
get_nav_height() {
const query = uni.createSelectorQuery().in(this);
query
.select('.article-tabs')
.boundingClientRect((res) => {
if ((res || null) != null) {
this.setData({
tabs_top: res.top,
});
}
})
.exec();
},
// 位置回调
choice_location_back(e) {
this.$emit('onLocationBack', e);
},
// 打开地址
url_event(e) {
app.globalData.url_event(e);
},
// 返回事件
top_nav_left_back_event() {
app.globalData.page_back_prev_event();
},
},
};
</script>
<style lang="scss" scoped>
.header-container {
width: 100%;
.header-around {
z-index: 12;
}
.model-top {
.roll {
width: 100%;
height: 100%;
margin: 0 auto;
}
.img {
width: 680rpx;
}
}
.model-head {
.model-head-content {
height: 66rpx;
// overflow: hidden;
top: -1rpx;
/* #ifdef H5 || MP-TOUTIAO */
top: 4rpx;
/* #endif */
}
}
.model-head-icon {
position: absolute;
right: 0;
bottom: 0;
height: 66rpx;
.function-icon {
height: 66rpx;
}
}
.logo-outer-style {
height: 56rpx;
.logo-style {
height: 56rpx;
width: 100%;
}
}
}
.indicator-center {
position: absolute;
left: 0;
right: 0;
text-align: center;
top: 0;
padding-left: 0;
}
.up_slide_bg {
z-index: -1;
}
.badge-style {
top: -20rpx;
right: 5rpx;
}
</style>

122
components/diy/hot-zone.vue Normal file
View File

@@ -0,0 +1,122 @@
<template>
<!-- 热区 -->
<view ref="containerRef" class="oh container" :style="style_container">
<view :style="style_img_container">
<view ref="hotRef" class="hot pr" :style="style">
<image :src="img" class="wh-auto dis-block" mode="widthFix" @load="on_load_img" />
<view v-for="(item, index) in hot_data" :key="index" class="hot_box" :style="'left: ' + item.drag_start.x * w_scale1 * w_scale2 + 'px;top:' + item.drag_start.y * h_scale1 * h_scale2 + 'px;width: ' + Math.max(item.drag_end.width * w_scale1 * w_scale2, 1) + 'px;height: ' + Math.max(item.drag_end.height * h_scale1 * h_scale2, 1) + 'px;display: flex;'" :data-value="item.link.page" @tap="url_event"> </view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
img: '',
hot_data: [],
img_width: 1,
img_height: 1,
container_ref_h: 0,
hot_ref_h: 0,
hot_ref_w: 0,
w_scale1: 1,
h_scale1: 1,
w_scale2: 1,
h_scale2: 1,
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
this.setData({
img: new_content.img[0].url,
img_width: new_content.hot.img_width || 1,
img_height: new_content.hot.img_height || 1,
hot_data: new_content.hot.data || [],
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
},
// 图片加载完成 获取宽高
on_load_img(e) {
const query = uni.createSelectorQuery();
// 选择我们想要的元素
query
.in(this)
.select('.container')
.boundingClientRect((res) => {
if ((res || null) != null) {
// data包含元素的宽度、高度等信息
this.setData({
container_ref_h: res.height,
w_scale1: res.height / this.img_width,
h_scale1: res.height / this.img_height,
});
}
})
.exec(); // 执行查询
query
.in(this)
.select('.hot')
.boundingClientRect((res) => {
if ((res || null) != null) {
// data包含元素的宽度、高度等信息
this.setData({
w_scale2: res.width / this.container_ref_h,
h_scale2: res.height / this.container_ref_h,
});
}
})
.exec(); // 执行查询
},
// 跳转链接
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style lang="scss" scoped>
.hot {
min-height: 20rpx;
.hot_box {
// background: rgba(42, 148, 255, 0.15);
// border: 2rpx dashed rgba(142, 198, 255, 0.5);
position: absolute;
opacity: 0.4;
}
}
</style>

View File

@@ -0,0 +1,228 @@
<template>
<!-- 图片魔方 -->
<view class="img-magic" :style="style_container + 'height:' + (form.style_actived == 10 ? '100%' : container_size)">
<view class="magic-container wh-auto ht-auto" :style="style_img_container">
<view class="pr" :style="outer_style">
<!-- 风格3 -->
<template v-if="form.style_actived == 2">
<view class="flex-row align-c jc-c style-size">
<view v-for="(item, index) in form.img_magic_list" :key="index" class="three flex-row" :style="img_spacing" :data-value="item.img_link ? item.img_link.page : ''" @tap="url_event">
<view class="wh-auto flex-row" :style="content_img_container">
<view class="flex-1" :style="content_img_style_container">
<image v-if="item.img.length > 0" :src="item.img[0].url" class="dis-block wh-auto ht-auto" :mode="img_fit" :style="content_img_radius"></image>
</view>
</view>
</view>
</view>
</template>
<!-- 风格9 -->
<template v-else-if="form.style_actived == 8">
<view class="flex-row align-c jc-c style-size flex-wrap">
<view v-for="(item, index) in form.img_magic_list" :key="index" :class="['flex-row', { 'style9-top': [0, 1].includes(index), 'style9-bottom': ![0, 1].includes(index) }]" :style="img_spacing" :data-value="item.img_link ? item.img_link.page : ''" @tap="url_event">
<view class="wh-auto flex-row" :style="content_img_container">
<view class="flex-1" :style="content_img_style_container">
<image v-if="item.img.length > 0" :src="item.img[0].url" class="dis-block wh-auto ht-auto" :mode="img_fit" :style="content_img_radius"></image>
</view>
</view>
</view>
</view>
</template>
<template v-else-if="form.style_actived == 10">
<template v-if="form.limit_size == '0'">
<view v-for="(item, index) in form.img_magic_list" :key="index" class="cr-main flex-row" :style="img_spacing + selected_style(item)" :data-value="item.img_link ? item.img_link.page : ''" @tap="url_event">
<view class="wh-auto flex-row" :style="content_img_container">
<view class="flex-1" :style="content_img_style_container">
<image v-if="item.img.length > 0" :src="item.img[0].url" class="dis-block wh-auto" mode="widthFix" :style="content_img_radius"></image>
</view>
</view>
</view>
</template>
<template v-else>
<view v-for="(item, index) in form.img_magic_list" :key="index" class="cr-main flex-row" :style="img_spacing + selected_style(item) + ';height:' + image_height" :data-value="item.img_link ? item.img_link.page : ''" @tap="url_event">
<view class="wh-auto flex-row" :style="content_img_container">
<view class="flex-1" :style="content_img_style_container">
<image v-if="item.img.length > 0" :src="item.img[0].url" class="dis-block wh-auto ht-auto" :mode="img_fit" :style="content_img_radius"></image>
</view>
</view>
</view>
</template>
</template>
<template v-else>
<view v-for="(item, index) in form.img_magic_list" :key="index" class="cube-selected cr-main flex-row" :style="img_spacing + selected_style(item)" :data-value="item.img_link ? item.img_link.page : ''" @tap="url_event">
<view class="wh-auto flex-row" :style="content_img_container">
<view class="flex-1" :style="content_img_style_container">
<image v-if="item.img.length > 0" :src="item.img[0].url" class="dis-block wh-auto ht-auto" :mode="img_fit" :style="content_img_radius"></image>
</view>
</view>
</view>
</template>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, radius_computer, percentage_count, isEmpty, margin_computer, padding_computer, old_padding, old_margin, border_width } from '@/common/js/common/common.js';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
propOuterContainerPadding: {
type: Number,
default: 0,
}
},
data() {
return {
form: {},
style_container: '',
style_img_container: '',
// 外部样式
outer_style: '',
// 图片间距设置
img_spacing: '',
// 图片圆角
content_img_radius: '',
cube_cell: '',
container_size: '',
div_width: 0,
image_height: '',
img_fit: '',
content_img_container: '',
content_img_style_container: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
computed: {
// 根据当前页面大小计算成百分比
selected_style() {
return (item) => {
return `overflow: hidden;width: ${this.percentage(this.getSelectedWidth(item))}; height: ${this.percentage(this.getSelectedHeight(item))}; top: ${this.percentage(this.getSelectedTop(item))}; left: ${this.percentage(this.getSelectedLeft(item))};`;
};
},
},
mounted() {
this.init();
},
methods: {
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
const new_style_spacing = new_content.style_actived === 10 ? 0 : new_style.image_spacing;
// 外部样式
const outer_spacing = `calc(100% + ${new_style_spacing * 2}rpx)`;
const outer_sx = `-${new_style_spacing}rpx`;
// 图片间距设置
const spacing = `${new_style_spacing}rpx`;
// scaleToFill 对应 cover aspectFit 对应 contain center 对应 none
let fit = '';
if (new_content.img_fit == 'contain') {
fit = 'aspectFit';
} else if (new_content.img_fit == 'fill') {
fit = 'scaleToFill';
} else if (new_content.img_fit == 'cover') {
fit = 'aspectFill';
}
const container_height = !isEmpty(new_content.container_height) ? new_content.container_height : sys_width;
const density = !isEmpty(new_content.magic_cube_density) ? new_content.magic_cube_density : 4;
const { margin_left, margin_right } = new_style.common_style;
const width = sys_width - margin_left - margin_right - border_width(new_style.common_style) - this.propOuterContainerPadding;
const scale = width / 390;
this.setData({
form: this.propValue.content,
new_style: this.propValue.style,
outer_style: `width:${outer_spacing};height:${outer_spacing};margin:${outer_sx};`,
img_spacing: `padding:${spacing};`,
content_img_radius: radius_computer(new_style),
style_container: common_styles_computer(new_style.common_style) + 'box-sizing: border-box;',
style_img_container: common_img_computer(new_style.common_style, this.propIndex) + 'box-sizing: border-box;',
img_fit: fit,
div_width: sys_width,
container_size: container_height * scale + 'px',
cube_cell: sys_width / density,
image_height: this.propValue.content.image_height * scale + 'px',
content_img_container: common_styles_computer(new_style) + margin_computer(new_style?.margin || old_margin) + 'box-sizing: border-box;',
content_img_style_container: common_img_computer(new_style) + padding_computer(new_style?.padding || old_padding) + 'box-sizing: border-box;',
});
},
getSelectedWidth(item) {
return (item.end.x - item.start.x + 1) * this.cube_cell;
},
//计算选中层的高度。
getSelectedHeight(item) {
return (item.end.y - item.start.y + 1) * this.cube_cell;
},
//计算选中层的右边距离。
getSelectedTop(item) {
return (item.start.y - 1) * this.cube_cell;
},
//计算选中层的左边距离。
getSelectedLeft(item) {
return (item.start.x - 1) * this.cube_cell;
},
// 计算成百分比
percentage(num) {
return percentage_count(num, this.div_width);
},
// 跳转链接
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style lang="scss" scoped>
// 图片魔方是一个正方形,根据宽度计算高度
.img-magic {
overflow: hidden;
box-sizing: border-box;
}
.cube-selected {
position: absolute;
text-align: center;
box-sizing: border-box;
}
.style-size {
height: 100%;
width: 100%;
box-sizing: border-box;
.three {
width: 33%;
height: 100%;
position: relative;
box-sizing: border-box;
}
.style9-top {
width: 50%;
height: 50%;
position: relative;
box-sizing: border-box;
}
.style9-bottom {
width: calc(100% / 3);
height: 50%;
position: relative;
box-sizing: border-box;
}
}
</style>

View File

@@ -0,0 +1,279 @@
<template>
<view :class="'wh-auto pr allSignList-' + propIndex + propKey" :style="'height:' + propDataHeight * propScale + 'px;'">
<view v-for="(item, index) in new_list" :key="index" :data-id="item.id" :data-location-x="item.location.x" :data-location-y="item.location.y" :class="'sign-' + propIndex + propKey + ' main-content ' + get_animation_class(item.com_data)" :style="'left:' + get_percentage_count(item.location.x, item.com_data.data_follow, 'left') + ';top:' + get_percentage_count(item.location.y, item.com_data.data_follow, 'top') + ';width:' + get_percentage_count(item.com_data.com_width, item.com_data.data_follow, 'width', item.com_data.is_width_auto, item.com_data.max_width, item.key) + ';height:' + get_percentage_count(item.com_data.com_height, item.com_data.data_follow, 'height', item.com_data.is_height_auto, item.com_data.max_height, item.key) + ';z-index:' + (new_list.length - 1 > 0 ? (new_list.length - 1) - index : 0)">
<template v-if="item.key == 'text'">
<model-text :propKey="propKey" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" :propTitleParams="propShowData.data_name" @url_event="url_event"></model-text>
</template>
<template v-else-if="item.key == 'img'">
<model-image :propKey="propKey" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" :propImgParams="propShowData.data_logo" @url_event="url_event"></model-image>
</template>
<template v-else-if="item.key == 'auxiliary-line'">
<model-lines :propKey="propKey" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId"></model-lines>
</template>
<template v-else-if="item.key == 'icon'">
<model-icon :propKey="propKey" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" @url_event="url_event"></model-icon>
</template>
<template v-else-if="item.key == 'panel'">
<model-panel :propKey="propKey" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" @url_event="url_event"></model-panel>
</template>
</view>
</view>
</template>
<script>
import modelText from '@/components/diy/modules/custom/model-text.vue';
import modelLines from '@/components/diy/modules/custom/model-lines.vue';
import modelImage from '@/components/diy/modules/custom/model-image.vue';
import modelIcon from '@/components/diy/modules/custom/model-icon.vue';
import modelPanel from '@/components/diy/modules/custom/model-panel.vue';
import { location_compute, isEmpty } from '@/common/js/common/common.js';
export default {
components: {
modelText,
modelLines,
modelImage,
modelIcon,
modelPanel,
},
props: {
propCustomList: {
type: Array,
default: () => {
return [];
},
required: true,
},
propIndex: {
type: Number,
default: 0,
},
propSourceList: {
type: Object,
default: () => {
return {};
}
},
propDataHeight: {
type: Number,
default: 0,
},
propScale: {
type: Number,
default: 1,
},
propDataIndex: {
type: Number,
default: 1,
},
propDataSplitIndex: {
type: Number,
default: 1,
},
propIsCustom: {
type: Boolean,
default: false,
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propShowData: {
type: Object,
default: () => ({
data_key: 'id',
data_name: 'name'
}),
},
propKey: {
type: [String, Number],
default: '',
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propFieldList: {
type: Array,
default: () => {
return [];
}
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
new_list: [],
custom_width: 0,
};
},
watch: {
propKey(val) {
// 初始化
this.init(this.propCustomList);
},
propCustomList(val) {
this.init(val);
}
},
computed: {
get_percentage_count() {
return (num, data_follow, type, is_auto = '0', max_size = 0, key = '') => {
// 检查类型是否为'left'或'top',如果是,则根据跟随数据计算样式
if (['left', 'top'].includes(type)) {
const { id = '', type: follow_type = 'left' } = data_follow || { id: '', type: 'left' };
// 如果id不为空且follow_type与type匹配则返回原始值的字符串表示
if (id !== '' && follow_type === type) {
return `${num}px`;
}
// 如果条件不满足则根据比例缩放num并返回
return `${num * this.propScale}px`;
} else {
// 如果is_auto设置为'1'则根据type和max_size计算自动样式
if (is_auto === '1') {
if (type === 'width' || type === 'height') {
if (typeof max_size === 'number' && max_size >= 0) {
const scaledMaxSize = max_size * this.propScale;
const autoStyle = 'auto;';
const maxSizeStyle = scaledMaxSize > 0 ? ` max-${type}: ${scaledMaxSize}px;` : '';
const whiteSpaceStyle = type === 'width' && scaledMaxSize <= 0 ? ' white-space:nowrap;' : '';
return `${ autoStyle }${ maxSizeStyle }${ whiteSpaceStyle }`;
} else {
return 'auto;';
}
}
} else {
// 微信小程序图片等比缩放对小数点后的内容支持的不是特别的好,需要取向上取整数
if (key == 'img' && ['width', 'height'].includes(type)) {
// 如果is_auto未设置或条件不满足则根据比例缩放num并返回
return `${ Math.round(num * this.propScale) }px`;
} else {
// 如果is_auto未设置或条件不满足则根据比例缩放num并返回
return `${num * this.propScale}px`;
}
}
}
};
},
get_animation_class() {
return (data) => {
const { type = 'none', number = 'infinite' } = data?.animation_style || {};
if (type != 'none') {
return type + (number == 'infinite' ? `-${number}` : '');
} else {
return '';
}
};
}
},
mounted() {
this.init(this.propCustomList);
},
methods: {
async init(val) {
// 如果为空就不进行渲染
if (isEmpty(val)) {
return;
}
await this.get_custom_width();
this.set_new_list(val);
},
get_custom_width() {
// 获取当前容器的宽度
const query = uni.createSelectorQuery().in(this);
query.select('.allSignList-' + this.propIndex + this.propKey)
.boundingClientRect((res) => {
if (res) {
this.setData({
custom_width: res.width,
});
}
})
.exec();
},
async set_new_list(val) {
// 第一次渲染先渲染全部数据
this.setData({
new_list: val
});
// 判断是否有跟随的数据
const follow_list = val.filter(item => item.com_data.data_follow && item.com_data?.data_follow?.id !== '');
if (follow_list.length > 0) {
// 等待页面渲染完成之后再获取内容
await this.$nextTick();
// 第二次如果有跟随数据,更新对应数据的内容, 如果有超出容器范围的数据,限制其超出容器范围
const query = uni.createSelectorQuery().in(this);
query.selectAll('.sign-' + this.propIndex + this.propKey)
.boundingClientRect((rect) => {
if (rect) {
// 将返回的内容转成map对象方便快速查找节省性能
const idMap = new Map(rect.map(item => [item.dataset.id, item]));
// 历史数据拷贝,方便后续操作避免每次都更新数据,统一重新渲染
const val = JSON.parse(JSON.stringify(this.new_list));
val.forEach((item1) => {
const { data_follow } = item1.com_data;
const targetItem = idMap.get(data_follow?.id);
if (targetItem) {
const text_item = item1.key == 'text' ? idMap.get((item1?.id || '')+ '') : undefined;
if (data_follow?.type === 'left') {
// 更新位置信息
const location_x = this.updateLocation(targetItem, data_follow, this.propScale, true);
// 获取组件的宽度,如果是宽度自适应,则需要重新计算位置
let item_width = item1.com_data.com_width;
// 如果是宽度自适应,需要重新判断一下处理逻辑
if (item1.com_data?.is_width_auto === '1' && text_item) {
item_width = text_item.width;
}
// 根据容器信息更新位置信息
item1.location.x = location_compute(item_width, location_x, this.custom_width);
} else if (data_follow?.type === 'top') {
// 更新位置信息
const location_y = this.updateLocation(targetItem, data_follow, this.propScale, false);
// 获取组件的宽度,如果是宽度自适应,则需要重新计算位置
let item_height = item1.com_data.com_height;
// 如果是高度自适应,需要重新判断一下处理逻辑
if (item1.com_data?.is_height_auto === '1' && text_item) {
item_height = text_item.height;
}
// 根据容器信息更新位置信息
item1.location.y = location_compute(item_height, location_y, this.propDataHeight * this.propScale);
}
}
});
this.setData({
new_list: val
});
}
})
.exec();
}
},
updateLocation(targetItem, data_follow, scale, isX) {
try {
const locationValueStr = targetItem.dataset[`location${isX ? 'X' : 'Y'}`];
if (locationValueStr == null) {
return;
}
const locationValue = parseFloat(locationValueStr);
if (isNaN(locationValue) || scale <= 0 || (isX ? targetItem.width < 0 : targetItem.height < 0)) return;
return ((locationValue + (data_follow?.spacing || 0)) * scale) + (isX ? targetItem.width : targetItem.height);
} catch (error) {
console.error(`Error updating location ${isX ? 'X' : 'Y'}:`, error);
}
},
url_event(e) {
this.$emit('url_event', e, this.propDataIndex, this.propDataSplitIndex);
}
},
};
</script>
<style lang="scss" scoped>
.main-content {
position: absolute;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,290 @@
<template>
<view :class="'wh-auto pr allSignList-' + propIndex + propKey" :style="'height:' + propDataHeight * propScale + 'px;'">
<view v-for="(item, index) in new_list" :key="index" :data-id="item.id" :data-location-x="item.location.x" :data-location-y="item.location.y" :class="'sign-' + propIndex + propKey + ' main-content ' + get_animation_class(item.com_data)" :style="'left:' + get_percentage_count(item.location.x, item.com_data.data_follow, 'left') + ';top:' + get_percentage_count(item.location.y, item.com_data.data_follow, 'top') + ';width:' + get_percentage_count(item.com_data.com_width, item.com_data.data_follow, 'width', item.com_data.is_width_auto, item.com_data.max_width, item.key) + ';height:' + get_percentage_count(item.com_data.com_height, item.com_data.data_follow, 'height', item.com_data.is_height_auto, item.com_data.max_height, item.key) + ';z-index:' + (new_list.length - 1 > 0 ? (new_list.length - 1) - index : 0)">
<template v-if="item.key == 'text'">
<model-text :propKey="propKey + item.id" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" :propTitleParams="propShowData.data_name" @url_event="url_event"></model-text>
</template>
<template v-else-if="item.key == 'img'">
<model-image :propKey="propKey + item.id" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" :propImgParams="propShowData.data_logo" @url_event="url_event"></model-image>
</template>
<template v-else-if="item.key == 'auxiliary-line'">
<model-lines :propKey="propKey + item.id" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId"></model-lines>
</template>
<template v-else-if="item.key == 'icon'">
<model-icon :propKey="propKey + item.id" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" @url_event="url_event"></model-icon>
</template>
<template v-else-if="item.key == 'panel'">
<model-panel :propKey="propKey + item.id" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propSourceList="propSourceList" :propConfigLoop="propConfigLoop" :propIsCustom="propIsCustom" :propIsCustomGroup="propIsCustomGroup" :propCustomGroupFieldId="propCustomGroupFieldId" @url_event="url_event"></model-panel>
</template>
<template v-else-if="item.key == 'custom-group'">
<model-custom-group :propKey="propKey + item.id" :propValue="item.com_data" :propScale="propScale" :propFieldList="propFieldList" :propConfigLoop="propConfigLoop" :propDataWidth="item.com_data.com_width" :propDataHeight="item.com_data.custom_height" :propSourceList="propSourceList" :propGroupSourceList="propGroupSourceList" :propIsCustom="propIsCustom" :propShowData="propShowData"></model-custom-group>
</template>
</view>
</view>
</template>
<script>
import modelText from '@/components/diy/modules/custom/model-text.vue';
import modelLines from '@/components/diy/modules/custom/model-lines.vue';
import modelImage from '@/components/diy/modules/custom/model-image.vue';
import modelIcon from '@/components/diy/modules/custom/model-icon.vue';
import modelPanel from '@/components/diy/modules/custom/model-panel.vue';
import modelCustomGroup from '@/components/diy/modules/custom/model-custom-group.vue';
import { location_compute, isEmpty } from '@/common/js/common/common.js';
export default {
components: {
modelText,
modelLines,
modelImage,
modelIcon,
modelPanel,
modelCustomGroup
},
props: {
propCustomList: {
type: Array,
default: () => {
return [];
},
required: true,
},
propIndex: {
type: Number,
default: 0,
},
propSourceList: {
type: Object,
default: () => {
return {};
}
},
propGroupSourceList: {
type: Array,
default: () => {
return [];
}
},
propDataHeight: {
type: Number,
default: 0,
},
propScale: {
type: Number,
default: 1,
},
propDataIndex: {
type: Number,
default: 1,
},
propDataSplitIndex: {
type: Number,
default: 1,
},
propIsCustom: {
type: Boolean,
default: false,
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propShowData: {
type: Object,
default: () => ({
data_key: 'id',
data_name: 'name'
}),
},
propKey: {
type: [String, Number],
default: '',
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propFieldList: {
type: Array,
default: () => {
return [];
}
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
new_list: [],
custom_width: 0,
};
},
watch: {
propKey(val) {
// 初始化
this.init(this.propCustomList);
},
propCustomList(val) {
this.init(val);
}
},
computed: {
get_percentage_count() {
return (num, data_follow, type, is_auto = '0', max_size = 0, key = '') => {
// 检查类型是否为'left'或'top',如果是,则根据跟随数据计算样式
if (['left', 'top'].includes(type)) {
const { id = '', type: follow_type = 'left' } = data_follow || { id: '', type: 'left' };
// 如果id不为空且follow_type与type匹配则返回原始值的字符串表示
if (id !== '' && follow_type === type) {
return `${num}px`;
}
// 如果条件不满足则根据比例缩放num并返回
return `${num * this.propScale}px`;
} else {
// 如果is_auto设置为'1'则根据type和max_size计算自动样式
if (is_auto === '1') {
if (type === 'width' || type === 'height') {
if (typeof max_size === 'number' && max_size >= 0) {
const scaledMaxSize = max_size * this.propScale;
const autoStyle = 'auto;';
const maxSizeStyle = scaledMaxSize > 0 ? ` max-${type}: ${scaledMaxSize}px;` : '';
const whiteSpaceStyle = type === 'width' && scaledMaxSize <= 0 ? ' white-space:nowrap;' : '';
return `${ autoStyle }${ maxSizeStyle }${ whiteSpaceStyle }`;
} else {
return 'auto;';
}
}
} else {
// 微信小程序图片等比缩放对小数点后的内容支持的不是特别的好,需要取向上取整数
if (key == 'img' && ['width', 'height'].includes(type)) {
// 如果is_auto未设置或条件不满足则根据比例缩放num并返回
return `${ Math.round(num * this.propScale) }px`;
} else {
// 如果is_auto未设置或条件不满足则根据比例缩放num并返回
return `${num * this.propScale}px`;
}
}
}
};
},
get_animation_class() {
return (data) => {
const { type = 'none', number = 'infinite' } = data?.animation_style || {};
if (type != 'none') {
return type + (number == 'infinite' ? `-${number}` : '');
} else {
return '';
}
};
}
},
mounted() {
this.init(this.propCustomList);
},
methods: {
async init(val) {
// 如果为空就不进行渲染
if (isEmpty(val)) {
return;
}
await this.get_custom_width();
this.set_new_list(val);
},
get_custom_width() {
// 获取当前容器的宽度
const query = uni.createSelectorQuery().in(this);
query.select('.allSignList-' + this.propIndex + this.propKey)
.boundingClientRect((res) => {
if (res) {
this.setData({
custom_width: res.width,
});
}
})
.exec();
},
async set_new_list(val) {
// 第一次渲染先渲染全部数据
this.setData({
new_list: val
});
// 判断是否有跟随的数据
const follow_list = val.filter(item => item.com_data.data_follow && item.com_data?.data_follow?.id !== '');
if (follow_list.length > 0) {
await this.$nextTick();
// 第二次如果有跟随数据,更新对应数据的内容, 如果有超出容器范围的数据,限制其超出容器范围
const query = uni.createSelectorQuery().in(this);
query.selectAll('.sign-' + this.propIndex + this.propKey)
.boundingClientRect((rect) => {
if (rect) {
// 将返回的内容转成map对象方便快速查找节省性能
const idMap = new Map(rect.map(item => [item.dataset.id, item]));
// 历史数据拷贝,方便后续操作避免每次都更新数据,统一重新渲染
const val = JSON.parse(JSON.stringify(this.new_list));
val.forEach((item1) => {
const { data_follow } = item1.com_data;
const targetItem = idMap.get(data_follow?.id);
if (targetItem) {
const text_item = item1.key == 'text' ? idMap.get((item1?.id || '')+ '') : undefined;
if (data_follow?.type === 'left') {
// 更新位置信息
const location_x = this.updateLocation(targetItem, data_follow, this.propScale, true);
// 获取组件的宽度,如果是宽度自适应,则需要重新计算位置
let item_width = item1.com_data.com_width;
// 如果是宽度自适应,需要重新判断一下处理逻辑
if (item1.com_data.is_width_auto === '1' && text_item) {
item_width = text_item.width;
}
// 根据容器信息更新位置信息
item1.location.x = location_compute(item_width, location_x, this.custom_width);
} else if (data_follow?.type === 'top') {
// 更新位置信息
const location_y = this.updateLocation(targetItem, data_follow, this.propScale, false);
// 获取组件的宽度,如果是宽度自适应,则需要重新计算位置
let item_height = item1.com_data.com_height;
// 如果是高度自适应,需要重新判断一下处理逻辑
if (item1.com_data?.is_height_auto === '1' && text_item) {
item_height = text_item.height;
}
// 根据容器信息更新位置信息
item1.location.y = location_compute(item_height, location_y, this.propDataHeight * this.propScale);
}
}
});
this.setData({
new_list: val
});
}
})
.exec();
}
},
updateLocation(targetItem, data_follow, scale, isX) {
try {
const locationValueStr = targetItem.dataset[`location${isX ? 'X' : 'Y'}`];
if (locationValueStr == null) {
return;
}
const locationValue = parseFloat(locationValueStr);
if (isNaN(locationValue) || scale <= 0 || (isX ? targetItem.width < 0 : targetItem.height < 0)) return;
const computedValue = ((locationValue + (data_follow?.spacing || 0)) * scale) + (isX ? targetItem.width : targetItem.height);
return computedValue;
} catch (error) {
console.error(`Error updating location ${isX ? 'X' : 'Y'}:`, error);
}
},
url_event(e) {
this.$emit('url_event', e, this.propDataIndex, this.propDataSplitIndex);
}
},
};
</script>
<style lang="scss" scoped>
.main-content {
position: absolute;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,378 @@
<template>
<view v-if="is_show" class="ht-auto" :style="style_container">
<view :style="style_img_container">
<view :style="style_content_container">
<view class="w h pr" :style="style_content_img_container">
<template v-if="data_source_content_list.length > 0 && form.data_source_direction == 'vertical'">
<view class="flex-row flex-wrap" :style="'row-gap:' + new_style.row_gap + 'px;column-gap:' + new_style.column_gap + 'px;'">
<view v-for="(item, index) in data_source_content_list" :key="index" class="ht-auto" :style="gap_width">
<view v-for="(item1, index1) in item.split_list" :key="index1">
<view :style="style_chunk_container">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataGroupRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="index1" :propSourceList="item1" :propFieldList="propFieldList" :propDataHeight="propDataHeight" :propScale="custom_scale" :propIsCustom="propIsCustom" :propIsCustomGroup="true" :propShowData="propShowData" :propConfigLoop="propConfigLoop !== '1' ? form.is_use_parent_data : '1'" :propDataIndex="index" :propDataSplitIndex="index1" @url_event="url_event"></dataGroupRendering>
</view>
</view>
</view>
</view>
</view>
</template>
<view v-else-if="data_source_content_list.length > 0 && ['vertical-scroll', 'horizontal'].includes(form.data_source_direction)" class="oh ht-auto">
<swiper class="w flex" circular="true" :vertical="form.data_source_direction != 'horizontal'" :autoplay="new_style.is_roll == '1'" :interval="new_style.interval_time * 1000" :duration="500" :display-multiple-items="slides_per_view" :style="{ width: '100%', height: swiper_height + 'px' }" @change="slideChange">
<swiper-item v-for="(item, index) in data_source_content_list" :key="index">
<view :class="form.data_source_direction != 'horizontal' ? 'wh-auto ht-auto' : 'flex-row'" :style="form.data_source_direction == 'horizontal' ? 'column-gap:' + new_style.column_gap + 'px;' : ''">
<view v-for="(item1, index1) in item.split_list" :key="index1" class="wh-auto ht-auto" :style="style_chunk_container + swiper_width + (form.data_source_direction == 'horizontal' ? gap_width : 'margin-bottom:' + content_outer_spacing_magin)">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataGroupRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="index1" :propSourceList="item1" :propFieldList="propFieldList" :propDataHeight="propDataHeight" :propScale="custom_scale" :propIsCustom="propIsCustom" :propIsCustomGroup="true" :propShowData="propShowData" :propConfigLoop="propConfigLoop !== '1' ? form.is_use_parent_data : '1'" :propDataIndex="index" :propDataSplitIndex="index1" @url_event="url_event"></dataGroupRendering>
</view>
</view>
</view>
</swiper-item>
</swiper>
<view v-if="new_style.is_show == '1' && data_source_content_list.length > 1" :class="['left', 'right'].includes(new_style.indicator_new_location) ? 'indicator_up_down_location' : 'indicator_about_location'" :style="indicator_location_style">
<block v-if="new_style.indicator_style == 'num'">
<view :style="indicator_style" class="dot-item">
<text :style="{ color: new_style.actived_color }">{{ actived_index + 1 }}</text>
<text>/{{ data_source_content_list.length }}</text>
</view>
</block>
<block v-else>
<view v-for="(item, index) in data_source_content_list" :key="index" :style="indicator_style + (actived_index == index ? 'background:' + new_style.actived_color : '')" class="dot-item" />
</block>
</view>
</view>
<template v-else>
<view :style="style_chunk_container">
<view class="wh-auto ht-auto oh" :style="style_chunk_img_container">
<dataGroupRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propFieldList="propFieldList" :propDataHeight="propDataHeight" :propScale="custom_scale" :propConfigLoop="propConfigLoop !== '1' ? form.is_use_parent_data : '1'" @url_event="url_event"></dataGroupRendering>
</view>
</view>
</template>
</view>
</view>
</view>
</view>
</template>
<script>
import { common_styles_computer, common_img_computer, percentage_count, isEmpty, get_indicator_style, get_indicator_location_style, border_width, get_is_eligible } from '@/common/js/common/common.js';
// a组件调用b组件 b组件调用a组件为了避免循环引用在uniapp中出问题复制一个相同的data-group-rendering组件
import dataGroupRendering from '@/components/diy/modules/custom/data-group-rendering.vue';
const app = getApp();
export default {
components: {
dataGroupRendering
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
required: true,
},
propSourceList: {
type: [ Object, Array ],
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
propScale: {
type: Number,
default: 1
},
propDataWidth: {
type: Number,
default: 0,
},
propDataHeight: {
type: Number,
default: 0,
},
propIsCustom: {
type: Boolean,
default: false
},
propFieldList: {
type: Array,
default: []
},
propGroupSourceList: {
type: Array,
default: () => {
return [];
}
},
propShowData: {
type: Object,
default: () => ({
data_key: 'id',
data_name: 'name',
})
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
form: {},
new_style: {},
custom_scale: 1,
style_container: '',
style_img_container: '',
custom_list_length: 0,
custom_group_field_id: '',
source_list: {
// 存放手动输入的id
data_ids: [],
// 手动输入
data_list: [],
// 自动
data_auto_list: [],
},
data_source_content_list: [],
data_source: '',
// 内容样式
style_content_container: '',
style_content_img_container: '',
// 数据样式
style_chunk_container: '',
style_chunk_img_container: '',
// 指示器选中的下标
actived_index: 0,
// 轮播高度
swiper_height: 0,
swiper_width: 'width: 100%;',
// 指示器样式
indicator_location_style: '',
indicator_style: '',
slides_per_view: 1,
show_data: { data_key: 'id', data_name: 'name' },
old_data_style: {
color_list: [{ color: 'rgb(244, 252, 255)', color_percentage: undefined }],
direction: '180deg',
background_img_style: '2',
background_img: [],
radius: 0,
radius_top_left: 0,
radius_top_right: 0,
radius_bottom_left: 0,
radius_bottom_right: 0,
padding: 0,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
margin: 0,
margin_top: 0,
margin_bottom: 0,
margin_left: 0,
margin_right: 0,
},
content_outer_spacing_magin: '0rpx',
gap_width: '',
is_show: true
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
mounted() {
this.init();
},
methods: {
percentage_count,
init() {
const new_form = this.propValue;
const new_style = this.propValue.data_style;
const data_source_id = new_form?.data_source_field?.id || '';
// 自定义组的数据源内容切换, 判断是取自定义组数据源内容还是取自定义数据源内容
const is_data_source_id = this.propFieldList.filter((item) => item.field == data_source_id);
let new_list = [];
// 判断是否是循环内容
if (this.propConfigLoop == '1') {
// 如果自定义组选择了数据源,就按照自定义组的数据源的方式走,否则的话就按照自定义的数据走
if (is_data_source_id.length > 0) {
const list = this.get_data_source_content_list(this.propSourceList, new_form);
// 数据来源的内容
new_list = list.length > 0 ? this.get_list(list, new_form, new_style) : [];
} else {
if (!isEmpty(this.propSourceList)) {
const new_source_list = [ this.propSourceList ];
new_list = [{ split_list: new_source_list }];
} else {
new_list = [];
}
}
} else {
// 如果使用父级数据,就直接使用父级的全部数据,否则的话就没有任何数据
if (new_form.is_use_parent_data == '1') {
new_list = this.propGroupSourceList;
} else {
new_list = [];
}
}
// 初始化数据
const { common_style, data_content_style, data_style } = new_style;
const old_width = this.propDataWidth * this.propScale;
// 外层左右间距
const outer_spacing = (common_style?.margin_left || 0) + (common_style?.margin_right || 0) + (common_style?.padding_left || 0) + (common_style?.padding_right || 0) + border_width(common_style);
// 内容左右间距
const content_spacing = (data_content_style?.margin_left || 0) + (data_content_style?.margin_right || 0) + (data_content_style?.padding_left || 0) + (data_content_style?.padding_right || 0) + border_width(data_content_style);
// 数据左右间距
const internal_spacing = (data_style?.margin_left || 0) + (data_style?.margin_right || 0) + (data_style?.padding_left || 0) + (data_style?.padding_right || 0) + border_width(data_style);
// 一行显示的数量
const carousel_col = Number(new_form.data_source_carousel_col);
// 数据间距
const data_spacing = ['vertical', 'horizontal'].includes(new_form.data_source_direction) ? new_style.column_gap * (carousel_col - 1) : 0;
// 自定义组件宽度
const width = old_width - outer_spacing - content_spacing - internal_spacing - data_spacing;
// 自定义组的比例
const scale_number = width / this.propDataWidth;
const custom_scale = scale_number > 0 ? scale_number : 0;
const new_data_style = !isEmpty(new_style.data_style) ? new_style.data_style : this.old_data_style;
const new_data_content_style = !isEmpty(new_style.data_content_style)? new_style.data_content_style : this.old_data_style;
// 判断是平移还是整屏滚动
const { padding_top = 0, padding_bottom = 0, margin_bottom = 0, margin_top = 0 } = new_data_style;
let swiper_height = 0;
let col = Number(new_form.data_source_carousel_col);
// 间距
const space_between = new_form.data_source_direction == 'horizontal' ? new_style.column_gap : new_style.row_gap;
// 轮播图高度控制
if (new_form.data_source_direction == 'horizontal') {
swiper_height = this.propDataHeight * custom_scale + padding_top + padding_bottom + margin_bottom + margin_top;
} else {
// 商品数量大于列数的时候,高度是列数,否则是当前的数量
col = new_list.length > carousel_col ? carousel_col : new_list.length;
swiper_height = (this.propDataHeight * custom_scale + padding_top + padding_bottom + margin_bottom + margin_top) * col + ((Number(new_form.data_source_carousel_col) - 1) * space_between);
}
// 计算间隔的空间。(gap * gap数量) / 模块数量
let gap = (new_style.column_gap * (carousel_col - 1)) / carousel_col;
// 横向的时候,根据选择的行数和每行显示的个数来区分具体是显示多少个
const swiper_width = (new_form.data_source_direction == 'horizontal' && new_style.rolling_fashion != 'translation') ? `width: ${ 100 / carousel_col }%;`: 'width: 100%;';
this.setData({
form: new_form,
new_style: new_style,
custom_scale: custom_scale,
custom_list_length: new_form.custom_list.length - 1,
style_container: common_styles_computer(new_style.common_style) + (new_form.is_scroll_bar == '1' ? 'overflow: auto;' : '') + 'box-sizing: border-box;', // 用于样式显示
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
style_content_container: common_styles_computer(new_data_content_style) + 'box-sizing: border-box;', // 用于样式显示
style_content_img_container: common_img_computer(new_data_content_style),
style_chunk_container: common_styles_computer(new_data_style) + 'box-sizing: border-box;', // 用于样式显示
style_chunk_img_container: common_img_computer(new_data_style),
style_chunk_width: width,
data_source_content_list: new_list,
data_source: !isEmpty(new_form.data_source)? new_form.data_source : '',
indicator_style: get_indicator_style(new_style), // 指示器的样式
indicator_location_style: get_indicator_location_style(new_style),
swiper_height: swiper_height,
swiper_width: swiper_width,
content_outer_spacing_magin: space_between + 'px',
gap_width: `width: calc(${100 / carousel_col}% - ${gap}px);`,
slides_per_view: new_style.rolling_fashion == 'translation' ? (new_form.data_source_direction != 'horizontal' ? col : new_form.data_source_carousel_col ) : 1,
is_show: this.get_is_show(new_form),
custom_group_field_id: new_form.data_source_field.id,
});
},
get_is_show(form) {
if (this.propConfigLoop == '1') {
// 取出条件判断的内容
const condition = form?.condition || { field: '', type: '', value: '' };
return get_is_eligible(this.propFieldList, condition, this.propSourceList, this.propIsCustom, false, this.propCustomGroupFieldId);
} else {
return true;
}
},
get_data_source_content_list(sourceList, form) {
if (!isEmpty(sourceList)) {
const data_source_id = form?.data_source_field.id || '';
let list = this.get_nested_property(sourceList, data_source_id);
// 如果是自定义标题,进一步处理嵌套对象中的数据
if (sourceList.data) {
list = this.get_nested_property(sourceList.data, data_source_id);
}
return list == '' ? [] : list;
} else {
return [];
}
},
get_nested_property(obj, path) {
// 检查路径参数是否为字符串且非空,若不满足条件则返回空字符串
if (typeof path !== 'string' || !path) return [];
// 将属性路径字符串拆分为属性键数组
const keys = path.split('.');
// 使用reduce方法遍历属性键数组逐层访问对象属性
// 如果当前对象存在且拥有下一个属性键,则继续访问;否则返回空字符串
return keys.reduce((o, key) => (o != null && o[key] != null ? o[key] : []), obj) ?? [];
},
get_list(list, form, new_style) {
// 深拷贝一下,确保不会出现问题
const cloneList = JSON.parse(JSON.stringify(list));
if (new_style.rolling_fashion != 'translation' && form.data_source_direction != 'vertical') {
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (cloneList.length > 0) {
// 每页显示的数量
const num = form.data_source_carousel_col;
// 存储数据显示
let 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,
},
];
}
} else {
// 存储数据显示
let nav_list = [];
cloneList.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
return nav_list;
}
},
slideChange(e) {
this.setData({
actived_index: e.detail.current,
});
},
url_event(e, index, split_index) {
if (this.data_source == 'goods' && this.data_source_content_list.length > 0) {
const list = this.data_source_content_list[index];
if (!isEmpty(list) && !isEmpty(list.split_list[split_index])) {
const new_list = list.split_list[split_index];
if (!isEmpty(new_list)) {
// 缓存商品数据
app.globalData.goods_data_cache_handle(new_list.data.id, new_list.data);
}
}
}
app.globalData.url_open(e);
},
},
};
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,161 @@
<template>
<view v-if="is_show" class="img-outer pr oh flex-row align-c wh-auto ht-auto" :style="com_style" @tap="url_event">
<iconfont :name="'icon-' + icon_class" :color="form.icon_color" :size="form.icon_size * scale + 'px'" propContainerDisplay="flex"></iconfont>
</view>
</template>
<script>
import { radius_computer, padding_computer, gradient_handle, isEmpty, get_nested_property, get_custom_link, get_is_eligible } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => {
return {};
},
required: true,
},
propSourceList: {
type: [ Object, Array ],
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
propScale: {
type: Number,
default: 1,
},
propIsCustom: {
type: Boolean,
default: false
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propFieldList: {
type: Array,
default: []
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
form: {},
com_style: '',
scale: 1,
icon_class: '',
icon_url: '',
is_show: true,
};
},
watch: {
propKey(val) {
this.init();
}
},
created() {
this.init();
},
methods: {
init() {
const new_form = this.propValue;
let icon_class_value = '';
if (!isEmpty(new_form.icon_class)) {
icon_class_value = new_form.icon_class;
} else {
if (!isEmpty(this.propSourceList)) {
let icon = '';
// 获取数据源ID
const data_source_id = !isEmpty(new_form?.data_source_field?.id || '') && this.propConfigLoop == '1' ? new_form.data_source_field.id : '';
// 数据源内容
const option = new_form?.data_source_field?.option || {};
if (data_source_id.includes(';')) {
const ids = data_source_id.split(';');
let url = '';
ids.forEach((item, index) => {
url += this.data_handling(item) + (index != ids.length - 1 ? (option?.join || '') : '');
});
icon = url;
} else {
// 不输入商品, 文章和品牌时,从外层处理数据
icon = this.data_handling(data_source_id);
}
icon_class_value = (option?.first || '') + icon + (option?.last || '');
} else {
icon_class_value = '';
}
}
this.setData({
form: new_form,
scale: this.propScale,
com_style: this.get_com_style(new_form, this.propScale),
icon_class: icon_class_value,
icon_url: this.get_icon_link(new_form),
is_show: this.get_is_show(new_form),
});
},
get_is_show(form) {
if (this.propConfigLoop == '1') {
// 取出条件判断的内容
const condition = form?.condition || { field: '', type: '', value: '' };
return get_is_eligible(this.propFieldList, condition, this.propSourceList, this.propIsCustom, this.propIsCustomGroup, this.propCustomGroupFieldId);
} else {
return true;
}
},
get_icon_link(new_form) {
let url = '';
if (!isEmpty(new_form.icon_link)) {
url = new_form.icon_link?.page || '';
} else {
// 获取数据源ID
const data_source_link_id = !isEmpty(new_form?.data_source_link_field?.id || '') && this.propConfigLoop == '1' ? new_form?.data_source_link_field?.id : '';
// 数据源内容
const source_link_option = new_form?.data_source_link_field?.option || {};
url = get_custom_link(data_source_link_id, this.propSourceList, source_link_option)
}
return url;
},
data_handling(data_source_id) {
// 不输入商品, 文章和品牌时,从外层处理数据
let icon = get_nested_property(this.propSourceList, data_source_id);
// 如果是商品,品牌,文章的图片, 其他的切换为从data中取数据
if (this.propIsCustom && !isEmpty(this.propSourceList.data)) {
icon = get_nested_property(this.propSourceList.data, data_source_id);
}
return icon;
},
get_com_style(form, scale) {
let style = `${ gradient_handle(form.color_list, form.direction) } ${ radius_computer(form.bg_radius, scale, true) };transform: rotate(${form.icon_rotate}deg);${ padding_computer(form.icon_padding, scale, true) };`;
if (form.border_show == '1') {
style += `border: ${form.border_size * scale }px ${form.border_style} ${form.border_color};box-sizing: border-box;`;
}
if (form.icon_location == 'center') {
style += `justify-content: center;`;
} else if (form.icon_location == 'left') {
style += `justify-content: flex-start;`;
} else if (form.icon_location == 'right') {
style += `justify-content: flex-end;`;
}
return style;
},
url_event(e) {
this.$emit('url_event', this.icon_url);
},
},
};
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,178 @@
<template>
<view v-if="is_show" class="img-outer pr wh-auto ht-auto" :style="border_style" @tap="url_event">
<imageEmpty :propImageSrc="img" :propStyle="image_style" propErrorStyle="width: 60rpx;height: 60rpx;"></imageEmpty>
</view>
</template>
<script>
import { percentage_count, radius_computer, isEmpty, get_nested_property, get_custom_link, get_is_eligible } from '@/common/js/common/common.js';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
export default {
components: {
imageEmpty,
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
required: true,
},
propSourceList: {
type: [ Object, Array ],
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
propScale: {
type: Number,
default: 1
},
propIsCustom: {
type: Boolean,
default: false
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propImgParams: {
type: String,
default: ''
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propFieldList: {
type: Array,
default: []
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
form: {},
img: '',
img_url: '',
image_style: '',
border_style: '',
keyMap: {
goods: 'images',
article: 'cover',
brand: 'logo'
},
is_show: true,
};
},
watch: {
propKey(val) {
this.init();
}
},
created() {
this.init();
},
methods: {
init() {
const new_form = this.propValue;
this.setData({
form: new_form,
img: this.get_img_url(new_form),
image_style: this.get_image_style(new_form, this.propScale),
border_style: this.get_border_style(new_form, this.propScale),
img_url: this.get_img_link(new_form),
is_show: this.get_is_show(new_form),
});
},
get_is_show(form) {
if (this.propConfigLoop == '1') {
// 取出条件判断的内容
const condition = form?.condition || { field: '', type: '', value: '' };
return get_is_eligible(this.propFieldList, condition, this.propSourceList, this.propIsCustom, this.propIsCustomGroup, this.propCustomGroupFieldId);
} else {
return true;
}
},
get_img_link(form) {
let url = '';
if (!isEmpty(form.link)) {
url = form.link?.page || '';
} else {
// 获取数据源ID
const data_source_link_id = !isEmpty(form?.data_source_link_field?.id || '') && this.propConfigLoop == '1' ? form.data_source_link_field.id : '';
// 数据源内容
const source_link_option = form?.data_source_link_field?.option || {};
url = get_custom_link(data_source_link_id, this.propSourceList, source_link_option)
}
return url;
},
get_img_url(form) {
if (!isEmpty(form.img[0])) {
return form.img[0];
} else {
if (!isEmpty(this.propSourceList)) {
let image_url = '';
// 获取数据源ID
const data_source_id = !isEmpty(form?.data_source_field?.id || '') && this.propConfigLoop == '1' ? form.data_source_field.id : '';
// 数据源内容
const option = form?.data_source_field?.option || {};
if (data_source_id.includes(';')) {
const ids = data_source_id.split(';');
let url = '';
ids.forEach((item, index) => {
url += this.data_handling(item) + (index != ids.length - 1 ? (option?.join || '') : '');
});
image_url = url;
} else {
image_url = this.data_handling(data_source_id);
}
return (option?.first || '') + image_url + (option?.last || '');
} else {
return '';
}
}
},
data_handling(data_source_id) {
// 不输入商品, 文章和品牌时,从外层处理数据
let image_url = get_nested_property(this.propSourceList, data_source_id);
// 如果是商品,品牌,文章的图片, 其他的切换为从data中取数据
if (this.propIsCustom && !isEmpty(this.propSourceList.data)) {
// 判断是否是同一标志
if (data_source_id == this.propImgParams) {
// 如果是符合条件的标志,先判断新的图片是否存在,存在就取新的图片,否则的话取原来的图片
image_url = !isEmpty(this.propSourceList.new_cover)? this.propSourceList.new_cover[0]?.url || '' : get_nested_property(this.propSourceList.data, data_source_id);
} else {
image_url = get_nested_property(this.propSourceList.data, data_source_id);
}
}
return image_url;
},
get_image_style(form, scale) {
return `width: ${percentage_count(form.img_width, form.com_width)}; height: ${percentage_count(form.img_height, form.com_height)};transform: rotate(${form.img_rotate}deg); ${radius_computer(form.img_radius, scale, true)};`;
},
get_border_style(form, scale) {
let style = ``;
if (form.border_show == '1') {
style += `border: ${form.border_size * scale }px ${form.border_style} ${form.border_color}; ${radius_computer(form.border_radius, scale, true)};box-sizing: border-box;`;
}
return style;
},
url_event(e) {
this.$emit('url_event', this.img_url);
},
},
};
</script>
<style lang="scss" scoped>
.img-outer {
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,94 @@
<template>
<view v-if="is_show" :style="border_style"></view>
</template>
<script>
import { get_is_eligible } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => {
return {};
},
required: true,
},
propSourceList: {
type: [ Object, Array ],
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
propScale: {
type: Number,
default: 1,
},
propIsCustom: {
type: Boolean,
default: false
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propFieldList: {
type: Array,
default: []
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
form: {},
border_style: '',
is_show: true,
};
},
watch: {
propKey(val) {
this.init();
}
},
created() {
this.init();
},
methods: {
init() {
const new_form = this.propValue;
this.setData({
form: new_form,
border_style: this.get_border_style(new_form, this.propScale),
is_show: this.get_is_show(new_form),
});
},
get_is_show(form) {
if (this.propConfigLoop == '1') {
// 取出条件判断的内容
const condition = form?.condition || { field: '', type: '', value: '' };
return get_is_eligible(this.propFieldList, condition, this.propSourceList, this.propIsCustom, this.propIsCustomGroup, this.propCustomGroupFieldId);
} else {
return true;
}
},
get_border_style(form, scale) {
if (form.line_settings === 'horizontal') {
return `margin: 10rpx 0;border-bottom: ${form.line_size * scale }px ${form.line_style} ${form.line_color};`;
} else {
return `margin: 0 10rpx;height:100%;border-right: ${form.line_size * scale }px ${form.line_style} ${form.line_color};`;
}
}
},
};
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,133 @@
<template>
<view v-if="is_show" class="wh-auto ht-auto re oh" :style="com_style" @tap="url_event">
<view class="wh-auto ht-auto" :style="com_img_style"></view>
</view>
</template>
<script>
import { radius_computer, background_computer, gradient_handle, isEmpty, get_custom_link, get_is_eligible } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => {
return {};
},
required: true,
},
propSourceList: {
type: [ Object, Array ],
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
propScale: {
type: Number,
default: 1
},
propIsCustom: {
type: Boolean,
default: false
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propFieldList: {
type: Array,
default: []
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
form: {},
text_title: '',
text_style: '',
com_style: '',
panel_url: '',
is_show: true,
};
},
watch: {
propKey(val) {
this.init();
}
},
created() {
this.init();
},
methods: {
init() {
const new_form = this.propValue;
let url = '';
if (!isEmpty(new_form.link)) {
url = new_form.link?.page || '';
} else {
// 获取数据源ID
const data_source_link_id = !isEmpty(new_form?.data_source_link_field?.id || '') && this.propConfigLoop == '1' ? new_form.data_source_link_field.id : '';
// 数据源内容
const source_link_option = new_form?.data_source_link_field?.option || {};
url = get_custom_link(data_source_link_id, this.propSourceList, source_link_option);
}
this.setData({
form: new_form,
com_style: this.get_com_style(new_form, this.propScale),
com_img_style: this.get_com_img_style(new_form),
panel_url: url,
is_show: this.get_is_show(new_form),
});
},
get_is_show(form) {
if (this.propConfigLoop == '1') {
// 取出条件判断的内容
const condition = form?.condition || { field: '', type: '', value: '' };
return get_is_eligible(this.propFieldList, condition, this.propSourceList, this.propIsCustom, this.propIsCustomGroup, this.propCustomGroupFieldId);
} else {
return true;
}
},
get_com_style(form, scale) {
let style = `${ gradient_handle(form.color_list, form.direction) } ${radius_computer(form.bg_radius, scale, true)}; transform: rotate(${form.panel_rotate}deg);`;
if (form.border_show == '1') {
style += `border: ${form.border_size * scale }px ${form.border_style} ${form.border_color};box-sizing: border-box;`;
}
return style;
},
get_com_img_style(form) {
const data = {
background_img: form?.background_img || [],
background_img_style: form?.background_img_style || '2'
}
return background_computer(data);
},
url_event(e) {
this.$emit('url_event', this.panel_url);
},
},
};
</script>
<style lang="scss" scoped>
.break {
word-wrap: break-word;
word-break: break-all;
}
.rich-text-content {
white-space: normal;
word-break: break-all;
* {
max-width: 100%;
}
}
</style>

View File

@@ -0,0 +1,236 @@
<template>
<view v-if="is_show" class="img-outer wh-auto ht-auto re oh" :style="com_style" @tap="url_event">
<view :style="text_style" :class="'break ' + text_line_class">
<template v-if="form.is_rich_text == '1'">
<mp-html :content="text_title" />
</template>
<template v-else>
{{ text_title }}
</template>
</view>
</view>
</template>
<script>
import { radius_computer, padding_computer, isEmpty, gradient_handle, get_nested_property, get_custom_link, get_is_eligible, custom_condition_data } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => {
return {};
},
required: true,
},
propSourceList: {
type: [ Object, Array ],
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
propScale: {
type: Number,
default: 1
},
propIsCustom: {
type: Boolean,
default: false
},
propIsCustomGroup: {
type: Boolean,
default: false
},
propCustomGroupFieldId: {
type: String,
default: ''
},
propTitleParams: {
type: String,
default: 'name'
},
propFieldList: {
type: Array,
default: []
},
propConfigLoop: {
type: String,
default: "1"
}
},
data() {
return {
form: {},
text_title: '',
text_style: '',
com_style: '',
text_url: '',
keyMap: {
goods: 'title',
article: 'title',
brand: 'name'
},
is_show: true,
text_line_class: ''
};
},
watch: {
propKey(val) {
this.init();
}
},
created() {
this.init();
},
methods: {
init() {
const new_form = this.propValue;
let url = '';
if (!isEmpty(new_form.text_link)) {
url = new_form.text_link?.page || '';
} else {
// 获取数据源ID
const data_source_link_id = !isEmpty(new_form?.data_source_link_field?.id || '') && this.propConfigLoop == '1' ? new_form.data_source_link_field.id : '';
// 数据源内容
const source_link_option = new_form?.data_source_link_field?.option || {};
// 调用方法处理数据显示
url = get_custom_link(data_source_link_id, this.propSourceList, source_link_option);
}
this.setData({
form: new_form,
text_title: (new_form?.text_captions || '') + this.get_text_title(new_form),
text_style: this.get_text_style(new_form, this.propScale),
com_style: this.get_com_style(new_form, this.propScale),
text_url: url,
is_show: this.get_is_show(new_form),
text_line_class: new_form.width_omit_num == '0' || new_form.is_rich_text == '1' ? '' : `text-line-${ new_form?.width_omit_num || '' }`
});
},
get_is_show(form) {
if (this.propConfigLoop == '1') {
// 取出条件判断的内容
const condition = form?.condition || { field: '', type: '', value: '' };
return get_is_eligible(this.propFieldList, condition, this.propSourceList, this.propIsCustom, this.propIsCustomGroup, this.propCustomGroupFieldId);
} else {
return true;
}
},
get_text_title(form) {
let text = '';
if (!isEmpty(form.text_title)) {
// 存储待处理的文本标题
let new_title = JSON.parse(JSON.stringify((form.text_title)));
let new_field_list = this.propFieldList;
// 判断是否是自定义组
if (this.propIsCustomGroup && !isEmpty(this.propCustomGroupFieldId)) {
// 取出对应自定义组的内容
const group_option_list = new_field_list.find((item) => item.field === this.propCustomGroupFieldId);
// 取出自定义组内部数据源参数的详细数据
new_field_list = group_option_list?.data || [];
}
// 遍历字段列表,替换文本标题中的占位符
if (!isEmpty(new_field_list)) {
new_field_list.forEach((item) => {
const new_field = '${' + item.field + '}';
if (form.text_title.includes(new_field)) {
// 获取到字段的真实数据
const field_value = custom_condition_data(item.field, item, this.propSourceList, this.propIsCustom);
// 使用正则表达式替换文本标题
const regular = new RegExp(`\\$\\{\\s*${item.field}\\s*\\}`, 'g');
// 替换后的内容赋值给原内容, 确保后续可以继续替换
new_title = new_title.replace(regular, field_value);
}
});
}
// 将内容替换为处理后的标题
text = new_title;
} else {
let text_title = '';
// 获取数据源ID
const data_source_id = !isEmpty(form?.data_source_field?.id || []) && this.propConfigLoop == '1' ? form?.data_source_field?.id : [];
// 数据源内容
const option = form?.data_source_field?.option || [];
// 多选判断
if (data_source_id.length > 0) {
text_title += form?.data_split?.left || '';
// 遍历取出所有的值
data_source_id.forEach((source_id, index) => {
const sourceList = option.find((item) => item.field == source_id);
// 根据数据源ID是否包含点号来区分处理方式
if (source_id.includes(';')) {
const ids = source_id.split(';');
let source_text = '';
ids.forEach((item, index) => {
source_text += this.data_handling(item) + (index != ids.length - 1 ? (sourceList?.join || '') : '');
});
text_title += (sourceList?.first || '') + source_text + (sourceList?.last || '');
} else {
text_title += (sourceList?.first || '') + this.data_handling(source_id) + (sourceList?.last || '');
}
if (index < data_source_id.length - 1) {
text_title += form?.data_split?.middle || '';
}
});
text_title += form?.data_split?.right || '';
}
// 如果是商品的标题或者是品牌的名称,需要判断是否有新的标题,没有的话就取原来的标题
text = text_title;
}
return text;
},
data_handling(data_source_id) {
let text = get_nested_property(this.propSourceList, data_source_id);
// 如果是商品的标题或者是品牌的名称,需要判断是否有新的标题,没有的话就取原来的标题
if (this.propIsCustom && !isEmpty(this.propSourceList.data)) {
// 其他的切换为从data中取数据
if (data_source_id == this.propTitleParams) {
// 如果是符合条件的标志,先判断新的标题是否存在,存在就取新的标题,否则的话取原来的标题
text = !isEmpty(this.propSourceList.new_title) ? this.propSourceList.new_title : get_nested_property(this.propSourceList.data, data_source_id);
} else {
text = get_nested_property(this.propSourceList.data, data_source_id);
}
}
return text;
},
get_text_style(form, scale) {
let style = `font-size: ${form.text_size * scale }px;line-height: ${ (typeof form.line_text_size === "number" ? form.line_text_size : form.text_size) * scale }px;color: ${form.text_color}; text-align: ${form.text_location}; transform: rotate(${form.text_rotate}deg);text-decoration: ${form.text_option};${padding_computer(form.text_padding, scale, true)};box-sizing: border-box;`;
if (form.text_weight == 'italic') {
style += `font-style: italic`;
} else if (['bold', '500'].includes(form.text_weight)) {
style += `font-weight: bold;`;
}
return style;
},
get_com_style(form, scale) {
let style = `${ gradient_handle(form.color_list, form.direction) } ${radius_computer(form.bg_radius, scale, true)}`;
if (form.border_show == '1') {
style += `border: ${form.border_size * scale }px ${form.border_style} ${form.border_color};box-sizing: border-box;`;
}
// 是富文本并且开启了上下滚动的开关
if (form.is_rich_text == '1' && form.is_up_down == '1') {
style += `overflow-y: auto;`;
}
return style;
},
url_event(e) {
this.$emit('url_event', this.text_url)
},
},
};
</script>
<style lang="scss" scoped>
.break {
word-wrap: break-word;
word-break: break-all;
}
.rich-text-content {
white-space: normal;
word-break: break-all;
* {
max-width: 100%;
}
}
</style>

View File

@@ -0,0 +1,304 @@
<template>
<view :style="style_content_container">
<view :style="style_content_img_container">
<template v-if="!isEmpty(form.data_source) && form.data_source_is_loop !== '0'">
<view v-if="data_source_content_list.length > 0 && form.data_source_direction == 'vertical'">
<view class="flex-row flex-wrap" :style="'row-gap:' + new_style.row_gap + 'px;column-gap:' + new_style.column_gap + 'px;'">
<view v-for="(item, index) in data_source_content_list" :key="index" class="ht-auto" :style="gap_width">
<view v-for="(item1, index1) in item.split_list" :key="index1">
<view :style="style_container">
<view class="wh-auto ht-auto oh" :style="style_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="index1" :propSourceList="item1" :propConfigLoop="form.data_source_is_loop || '1'" :propGroupSourceList="data_source_content_list" :propSourceType="form.data_source" :propDataHeight="form.height" :propScale="scale" :propDataIndex="index" :propDataSplitIndex="index1" :propIsCustom="form.is_custom_data == '1'" :propShowData="show_data" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
</view>
</view>
</view>
<view v-else-if="data_source_content_list.length > 0 && ['vertical-scroll', 'horizontal'].includes(form.data_source_direction)" class="oh pr">
<swiper class="w flex" circular="true" :vertical="form.data_source_direction != 'horizontal'" :autoplay="new_style.is_roll == '1'" :interval="new_style.interval_time * 1000" :duration="500" :display-multiple-items="slides_per_view" :style="{ width: '100%', height: swiper_height + 'px' }" @change="slideChange">
<swiper-item v-for="(item, index) in data_source_content_list" :key="index">
<view :class="form.data_source_direction != 'horizontal' ? '' : 'flex-row'" :style="form.data_source_direction == 'horizontal' ? 'column-gap:' + new_style.column_gap + 'px;' : ''">
<view v-for="(item1, index1) in item.split_list" :key="index1" :style="style_container + swiper_width + (form.data_source_direction == 'horizontal' ? gap_width : 'margin-bottom:' + content_outer_spacing_magin)">
<view class="wh-auto ht-auto oh" :style="style_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="index1" :propSourceList="item1" :propConfigLoop="form.data_source_is_loop || '1'" :propGroupSourceList="data_source_content_list" :propSourceType="form.data_source" :propDataHeight="form.height" :propScale="scale" :propDataIndex="index" :propDataSplitIndex="index1" :propIsCustom="form.is_custom_data == '1'" :propShowData="show_data" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
</swiper-item>
</swiper>
</view>
<view v-else>
<view :style="style_container">
<view class="wh-auto ht-auto oh" :style="style_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propConfigLoop="form.data_source_is_loop || '1'" :propDataHeight="form.height" :propScale="scale" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
</template>
<view v-else-if="!isEmpty(form.data_source) && form.data_source_is_loop == '0'">
<view :style="style_container">
<view class="w h oh" :style="style_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propSourceList="{}" :propConfigLoop="form.data_source_is_loop || '1'" :propGroupSourceList="data_source_content_list" :propSourceType="form.data_source" :propDataHeight="form.height" :propScale="scale" :propIsCustom="form.is_custom_data == '1'" :propShowData="show_data" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
<view v-else>
<view :style="style_container">
<view class="wh-auto ht-auto oh" :style="style_img_container">
<dataRendering :propKey="propKey" :propCustomList="form.custom_list" :propIndex="0" :propConfigLoop="form.data_source_is_loop || '1'" :propDataHeight="form.height" :propScale="scale" @url_event="url_event"></dataRendering>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { padding_computer, isEmpty, margin_computer, gradient_computer, radius_computer, background_computer, common_styles_computer, common_img_computer, border_width, box_shadow_computer, border_computer, old_border_and_box_shadow, old_margin, old_padding, old_radius } from '@/common/js/common/common.js';
import dataRendering from '@/components/diy/modules/custom/data-rendering.vue';
const app = getApp();
export default {
components: {
dataRendering
},
props: {
propKey: {
type: String,
default: '',
},
propValue: {
type: Object,
default: () => {
return {};
},
},
propMagicScale: {
type: Number,
default: 1,
},
propDataSpacing: {
type: Number,
default: 0,
},
propDataIndex: {
type: Number,
default: 0,
},
},
data() {
return {
form: {},
new_style: {},
scale: 1,
style_container: '',
style_img_container: '',
div_width: 0,
div_height: 0,
custom_list_length: 0,
data_source_content_list: [],
data_source: '',
// 一屏显示的数量
slides_per_view: 1,
// 轮播高度
swiper_height: 0,
swiper_width: 'width: 100%;',
show_data: { data_key: 'id', data_name: 'name' },
gap_width: '',
content_outer_spacing_magin: '0rpx',
defalt_style: {
color_list: [{ color: '', color_percentage: undefined }],
direction: '180deg',
background_img_style: '2',
background_img: [],
radius: 0,
radius_top_left: 0,
radius_top_right: 0,
radius_bottom_left: 0,
radius_bottom_right: 0,
padding: 0,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
margin: 0,
margin_top: 0,
margin_bottom: 0,
margin_left: 0,
margin_right: 0,
},
style_content_container: '',
style_content_img_container: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
computed: {
get_percentage_count() {
return (num) => {
return num * this.scale + 'px';
};
},
},
mounted() {
this.init();
},
methods: {
isEmpty,
init() {
if (!isEmpty(this.propValue)) {
const new_form = this.propValue.data_content;
const new_style = this.propValue.data_style;
const { data_common_style = {}, data_color_list = [], data_direction = '180deg', data_radius = old_radius, data_background_img = [], data_background_img_style = '2', data_chunk_padding = old_padding, data_chunk_margin = old_margin, data_content_style = {}, data_pattern = old_border_and_box_shadow } = new_style;
const style_data = {
color_list: data_color_list,
direction: data_direction,
}
const style_img_data = {
background_img: data_background_img,
background_img_style: data_background_img_style,
}
// 数据来源的内容
let list = [];
if (new_form.is_custom_data == '1') {
if (Number(new_form.data_source_content.data_type) === 0) {
list = new_form.data_source_content.data_list;
} else {
list = new_form.data_source_content.data_auto_list.map(item => ({
id: Math.random(),
new_cover: [],
new_title: '',
data: item,
}));
}
} else {
list = new_form.data_source_content.data_list;
}
const new_list = list.length > 0 ? this.get_list(list, new_form, new_style) : [];
// 计算宽度
const { padding_left, padding_right, padding_top, padding_bottom } = data_chunk_padding;
const { margin_left, margin_right, margin_bottom, margin_top } = data_chunk_margin;
const old_width = new_form.width * this.propMagicScale;
// 数据宽度
const data_style = padding_left + padding_right + margin_left + margin_right + border_width(data_pattern);
// 通用样式
const chunk_padding = new_style?.chunk_padding || old_padding;
const chunk_margin = new_style?.chunk_margin || old_margin;
const common_styles = (chunk_margin?.margin_left || 0) + (chunk_margin?.margin_right || 0) + (chunk_padding?.padding_left || 0) + (chunk_padding?.padding_right || 0) + border_width(data_common_style);
// 内容左右间距
const content_spacing = (data_content_style?.margin_left || 0) + (data_content_style?.margin_right || 0) + (data_content_style?.padding_left || 0) + (data_content_style?.padding_right || 0) + border_width(data_content_style);
const carousel_col = Number(new_form.data_source_carousel_col);
// 数据间距
const data_spacing = ['vertical', 'horizontal'].includes(new_form.data_source_direction) ? new_style.column_gap * (carousel_col - 1) : 0;
// 当前容器的宽度 减去 左右两边的padding值 再减去 数据间距的一半 再除以 容器的宽度 得到比例 再乘以数据魔方的比例
const width = old_width - data_style - content_spacing - common_styles - data_spacing - (this.propDataSpacing / 2);
// 计算缩放比例
// 比例增加最小值判断
const scale_number = width / new_form.width;
const new_scale = scale_number > 0 ? scale_number : 0;
// 间距
const space_between = new_form.data_source_direction == 'horizontal' ? new_style.column_gap : new_style.row_gap;
// 判断是平移还是整屏滚动
let swiper_height = 0;
// 商品数量大于列数的时候,高度是列数,否则是当前的数量
const col = new_list.length > carousel_col ? carousel_col : new_list.length;
// 轮播图高度控制
if (new_form.data_source_direction == 'horizontal') {
swiper_height = new_form.height * new_scale + padding_top + padding_bottom + margin_bottom + margin_top;
} else {
swiper_height = (new_form.height * new_scale + padding_top + padding_bottom + margin_bottom + margin_top) * col + ((carousel_col - 1) * space_between);
}
// 计算间隔的空间。(gap * gap数量) / 模块数量
let gap = (new_style.column_gap * (carousel_col - 1)) / carousel_col;
// 横向的时候,根据选择的行数和每行显示的个数来区分具体是显示多少个
const swiper_width = (new_form.data_source_direction == 'horizontal' && new_style.rolling_fashion != 'translation') ? `width: ${ 100 / carousel_col }%;`: 'width: 100%;';
const content_style = !isEmpty(new_style.data_content_style)? new_style.data_content_style : this.defalt_style;
this.setData({
form: new_form,
new_style: new_style,
div_width: width,
scale: new_scale,
custom_list_length: new_form.custom_list.length - 1,
style_content_container: common_styles_computer(content_style),
style_content_img_container: common_img_computer(content_style),
style_container: gradient_computer(style_data) + radius_computer(data_radius) + margin_computer(data_chunk_margin) + box_shadow_computer(data_pattern) + border_computer(data_pattern), // 用于样式显示
style_img_container: padding_computer(data_chunk_padding) + background_computer(style_img_data) + 'box-sizing: border-box;',
data_source_content_list: new_list,
data_source: !isEmpty(new_form.data_source)? new_form.data_source : '',
slides_per_view: new_style.rolling_fashion == 'translation' ? (new_form.data_source_direction != 'horizontal' ? col : carousel_col) : 1,
swiper_height: swiper_height,
swiper_width: swiper_width,
show_data: new_form?.show_data || { data_key: 'id', data_name: 'name' },
content_outer_spacing_magin: space_between + 'px',
gap_width: `width: calc(${100 / carousel_col}% - ${gap}px);`,
});
}
},
get_list(list, form, new_style) {
// 深拷贝一下,确保不会出现问题
const cloneList = JSON.parse(JSON.stringify(list));
if (new_style.rolling_fashion != 'translation' && form.data_source_direction != 'vertical') {
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (cloneList.length > 0) {
// 每页显示的数量
const num = form.data_source_carousel_col;
// 存储数据显示
let 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,
},
];
}
} else {
// 存储数据显示
let nav_list = [];
cloneList.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
return nav_list;
}
},
slideChange(e) {
this.$emit('onCarouselChange', e.detail.current, this.propDataIndex);
},
url_event(e, index, split_index) {
if (this.form.data_source == 'goods' && this.data_source_content_list.length > 0) {
const list = this.data_source_content_list[index];
if (!isEmpty(list) && !isEmpty(list.split_list[split_index])) {
const new_list = list.split_list[split_index];
if (!isEmpty(new_list)) {
// 缓存商品数据
app.globalData.goods_data_cache_handle(new_list.data.id, new_list.data);
}
}
}
app.globalData.url_open(e);
},
},
};
</script>
<style scoped lang="scss">
.main-content {
position: absolute;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,142 @@
<template>
<view class="wh-auto ht-auto oh" :style="style_container">
<view class="pr oh wh-auto ht-auto" :style="style_img_container">
<swiper circular="true" :autoplay="propValue.data_style.is_roll == '1'" :interval="propValue.data_style.interval_time * 1000" :duration="500" :vertical="propValue.data_style.rotation_direction == 'vertical'" :next-margin="next_margin" :display-multiple-items="slides_per_view" class="swiper" style="height: 100%" @change="carousel_change">
<swiper-item v-for="(item1, index1) in propValue.data_content.list" :key="index1">
<template v-if="propType === 'img'">
<view class="wh-auto ht-auto" :data-value="item1.carousel_link.page" @tap="url_event">
<imageEmpty :propImageSrc="item1.carousel_img[0]" :propStyle="propValue.data_style.get_img_radius" :propImgFit="propValue.data_content.fit" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<template v-else>
<view class="wh-auto ht-auto" :style="shop_spacing">
<product-list-show :propKey="propKey" :propOuterflex="propValue.data_content.goods_outerflex" :propFlex="propValue.data_content.goods_flex" :propNum="show_num" :propActived="propActived" :propIsShow="propValue.data_content.is_show" :propChunkPadding="propValue.data_style.chunk_padding" :propValue="item1.split_list" :propGoodStyle="propGoodStyle" :propContentImgRadius="propValue.data_style.get_img_radius" @url_event="url_event"></product-list-show>
</view>
</template>
</swiper-item>
</swiper>
</view>
</view>
</template>
<script>
import { gradient_computer, radius_computer, padding_computer, background_computer, isEmpty, border_computer, box_shadow_computer, old_border_and_box_shadow, old_margin, old_radius, old_padding, margin_computer } from "@/common/js/common/common.js";
const app = getApp();
import imageEmpty from '@/components/diy/modules/image-empty.vue';
import productListShow from '@/components/diy/modules/data-magic/product-list-show.vue';
export default {
components: {
imageEmpty,
productListShow,
},
props: {
propValue: {
type: Object,
default: () => ({}),
},
propContentImgRadius: {
type: String,
default: () => '',
},
propType: {
type: String,
default: () => '',
},
propActived: {
type: Number,
default: () => 0,
},
propGoodStyle: {
type: Object,
default: () => {},
},
propDataIndex: {
type: Number,
default: () => 0,
},
propKey: {
type: [String, Number],
default: '',
},
},
data() {
return {
style_container: '',
style_img_container: '',
slides_per_view: 1,
show_num: 1,
shop_spacing: '',
next_margin: '0rpx',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
mounted() {
this.init();
},
methods: {
init() {
if (!isEmpty(this.propValue)) {
const { data_color_list = [], data_direction = '180deg', data_chunk_margin = old_margin, data_radius = old_radius, data_pattern = old_border_and_box_shadow, data_background_img = [], data_background_img_style = '2', data_chunk_padding = old_padding } = this.propValue.data_style;
const style_data = {
color_list: data_color_list,
direction: data_direction,
}
const style_img_data = {
background_img: data_background_img,
background_img_style: data_background_img_style,
}
let slides_per_view = 1;
let show_num = 1;
// 商品时的处理逻辑
const { goods_outerflex, goods_num } = this.propValue.data_content;
const { rotation_direction, rolling_fashion, data_goods_gap } = this.propValue.data_style;
// 图片时的处理
if (this.propType === 'img') {
slides_per_view = 1; // 能够同时显示的slides数量
} else {
// 判断是平移还是整屏滚动, 平移的时候是一个为1组如果是整屏滚动就为一屏为一组
if (rolling_fashion == 'translation') {
// 如果是商品是横排的,轮播也是横排的,就不对商品进行拆分/如果商品是竖排的,轮播也是竖排的,不对商品进行拆分
if ((goods_outerflex == 'row' && rotation_direction == 'horizontal') || (goods_outerflex == 'col' && rotation_direction == 'vertical')) {
slides_per_view = goods_num; // 能够同时显示的slides数量
show_num = 1; // 一屏显示的数量 用于商品内部处理显示
} else {
slides_per_view = 1; // 能够同时显示的slides数量
show_num = goods_num; // 一屏显示的数量 用于商品内部处理显示
}
} else {
// 切屏的时候为多个为一组
show_num = goods_num; // 一屏显示的数量 用于商品内部处理显示
slides_per_view = 1; // 能够同时显示的slides数量
}
}
this.setData({
style_container: gradient_computer(style_data) + radius_computer(data_radius) + margin_computer(data_chunk_margin) + box_shadow_computer(data_pattern) + border_computer(data_pattern), // 用于样式显示
style_img_container: padding_computer(data_chunk_padding) + background_computer(style_img_data) + 'box-sizing: border-box;',
next_margin: rolling_fashion == 'translation' && rotation_direction == 'horizontal' ? `-${ data_goods_gap * 2 }rpx` : '0rpx',
shop_spacing: this.propType === 'img' ? 'margin-right: 0px;' : `margin-right: ${ data_goods_gap * 2 }rpx;`,
slides_per_view: slides_per_view,
show_num: show_num,
});
} else {
return '';
}
},
carousel_change(e) {
this.$emit('onCarouselChange', e.detail.current, this.propDataIndex);
},
// 跳转链接
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style></style>

View File

@@ -0,0 +1,233 @@
<template>
<view :class="['align-c flex-1 w h', (propOuterflex == 'row' ? 'flex-row' : 'flex-col')]" :style="'gap:' + propGoodStyle.data_goods_gap + 'px;'">
<template v-if="propFlex === 'row'">
<view v-for="(item, index) in propValue" :key="index" :style="block_size" class="w h">
<view class="w h oh" :style="style_container">
<view class="w h flex-row gap-10" :style="style_img_container" :data-index="index" :data-value="item.goods_url" @tap="url_event">
<template v-if="!isEmpty(item.new_cover)">
<view class="w h">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="propContentImgRadius" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<template v-else>
<view class="w h">
<imageEmpty :propImageSrc="item.images" :propStyle="propContentImgRadius" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<view v-if="!isEmpty(propIsShow)" class="flex-col w h tl jc-sb">
<view v-if="propIsShow.includes('title')" class="text-line-2" :style="propGoodStyle.goods_title_style + 'height:'+ ((propGoodStyle.goods_title_size + 3) * 4) + 'rpx;'">{{ item.title || '' }}</view>
<view v-if="propIsShow.includes('price')" :style="propGoodStyle.goods_price_style">
<text :style="propGoodStyle.goods_price_symbol_style">{{ item.show_price_symbol || '' }}</text>
{{ item.min_price || '' }}
<template v-if="propIsShow.includes('price_unit')">
<text :style="propGoodStyle.goods_price_unit_style">{{ item.show_price_unit || '' }}</text>
</template>
</view>
</view>
</view>
</view>
</view>
</template>
<template v-else-if="propFlex === 'col_price_float'">
<view v-for="(item, index) in propValue" :key="index" :style="block_size" class="w h">
<view class="w h oh" :style="style_container">
<view class="w h flex-col gap-10" :style="style_img_container" :data-index="index" :data-value="item.goods_url" @tap="url_event">
<view class="w h flex-1 pr oh">
<template v-if="!isEmpty(item.new_cover)">
<view class="w h">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="propContentImgRadius" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<template v-else>
<view class="w h">
<imageEmpty :propImageSrc="item.images" :propStyle="propContentImgRadius" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<view v-if="propIsShow.includes('price')" class="pa" :style="propGoodStyle.goods_price_style + float_pirce_style">
<text :style="propGoodStyle.goods_price_symbol_style">{{ item.show_price_symbol || ''}}</text>{{ item.min_price || ''}}
<template v-if="propIsShow.includes('price_unit')">
<text :style="propGoodStyle.goods_price_unit_style">{{ item.show_price_unit || ''}}</text>
</template>
</view>
</view>
<view v-if="propIsShow.includes('title')" class="text-line-1 tl w" :style="propGoodStyle.goods_title_style + 'height:'+ ((propGoodStyle.goods_title_size + 3) * 2) + 'rpx;'">{{ item.title || '' }}</view>
</view>
</view>
</view>
</template>
<template v-else>
<view v-for="(item, index) in propValue" :key="index" :style="block_size" class="w h">
<view class="w h oh" :style="style_container">
<view class="w h flex-col" :style="style_img_container" :data-index="index" :data-value="item.goods_url" @tap="url_event">
<template v-if="!isEmpty(item.new_cover)">
<view class="w h">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="propContentImgRadius" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<template v-else>
<view class="w h">
<imageEmpty :propImageSrc="item.images" :propStyle="propContentImgRadius" propErrorStyle="width: 80rpx;height: 80rpx;"></imageEmpty>
</view>
</template>
<view v-if="!isEmpty(propIsShow)" class="flex-col w h tl jc-sb">
<view v-if="propIsShow.includes('title')" class="text-line-2" :style="propGoodStyle.goods_title_style + 'height:'+ ((propGoodStyle.goods_title_size + 3) * 4) + 'rpx;'">{{ item.title || '' }}</view>
<view v-if="propIsShow.includes('price')" :style="propGoodStyle.goods_price_style">
<text :style="propGoodStyle.goods_price_symbol_style">{{ item.show_price_symbol || ''}}</text>{{ item.min_price || '' }}
<template v-if="propIsShow.includes('price_unit')">
<text :style="propGoodStyle.goods_price_unit_style">{{ item.show_price_unit || ''}}</text>
</template>
</view>
</view>
</view>
</view>
</view>
</template>
</view>
</template>
<script>
const app = getApp();
import { gradient_computer, radius_computer, padding_computer, background_computer, isEmpty, margin_computer, box_shadow_computer, border_computer, old_margin, old_border_and_box_shadow, old_padding } from "@/common/js/common/common.js";
import imageEmpty from '@/components/diy/modules/image-empty.vue';
export default {
components: {
imageEmpty,
},
props: {
propValue: {
type: Array,
default: () => [],
},
propOuterflex: {
type: String,
default: () => '',
},
propFlex: {
type: String,
default: () => '',
},
propContentImgRadius: {
type: String,
default: () => '',
},
propNum: {
type: Number,
default: () => 0,
},
propActived: {
type: Number,
default: () => 0,
},
propIsShow: {
type: Array,
default: () => [],
},
propChunkPadding: {
type: Object,
default: () => {},
},
propGoodStyle: {
type: Object,
default: () => {},
},
propKey: {
type: [String, Number],
default: '',
},
},
data() {
return {
style_container: '',
style_img_container: '',
block_size: '',
float_pirce_style: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
mounted() {
this.init();
},
computed: {
img_padding_computer() {
if (!isEmpty(this.propChunkPadding)) {
return padding_computer(this.propChunkPadding) + ';box-sizing: border-box;';
} else {
return '';
}
},
},
methods: {
isEmpty,
init() {
if (!isEmpty(this.propGoodStyle)) {
const { goods_color_list = [], goods_direction = '180deg', goods_radius = old_radius, goods_background_img = [], goods_background_img_style = '2', goods_chunk_padding = old_padding, goods_price_color_list = [], goods_price_direction = '180deg', goods_price_radius = old_radius, goods_price_padding = old_padding, goods_price_margin = old_margin, goods_price_location = 'center', goods_chunk_margin = old_margin} = this.propGoodStyle;
const style_data = {
color_list: goods_color_list,
direction: goods_direction,
}
const style_img_data = {
background_img: goods_background_img,
background_img_style: goods_background_img_style,
}
const data = {
color_list: goods_price_color_list,
direction: goods_price_direction,
}
let location = 'left:50%;transform:translateX(-50%);bottom:0;'
if (goods_price_location == 'left') {
location = 'left:0;bottom:0;';
} else if (goods_price_location == 'right') {
location = 'right:0;bottom:0;';
}
// 左右间距
const shop_left_right_width_margin = goods_chunk_margin?.margin_left || 0 + goods_chunk_margin?.margin_right || 0;
// 上下间距
const shop_top_bottom_width_margin = goods_chunk_margin?.margin_top || 0 + goods_chunk_margin?.margin_bottom || 0;
// 内容间距
const total_gap = this.propGoodStyle.data_goods_gap * (this.propValue.length - 1);
// 总的宽度
const all_width = total_gap + (shop_left_right_width_margin.value * this.propNum);
// 总的高度
const all_height = total_gap + (shop_top_bottom_width_margin.value * this.propNum);
this.setData({
float_pirce_style: gradient_computer(data) + radius_computer(goods_price_radius) + padding_computer(goods_price_padding) + margin_computer(goods_price_margin) + location,
style_container: gradient_computer(style_data) + radius_computer(goods_radius) + margin_computer(goods_chunk_margin) + border_computer(this.propGoodStyle) + box_shadow_computer(this.propGoodStyle), // 用于样式显示
style_img_container: this.propFlex == 'col' ? background_computer(style_img_data) : padding_computer(goods_chunk_padding) + background_computer(style_img_data) + 'box-sizing: border-box;',
block_size: this.propOuterflex == 'row' ? 'height:calc(100% - ' + shop_top_bottom_width_margin.value + 'px);width:calc((100% - ' + all_width + 'px) / ' + this.propNum + ');' : 'width:calc(100% - ' + shop_left_right_width_margin.value + 'px);height:calc((100% - ' + all_height + 'px) / ' + this.propNum + ');',
});
} else {
return '';
}
},
url_event(e) {
// 存储数据显示缓存
let index = e.currentTarget.dataset.index || 0;
let goods = this.propValue[index];
app.globalData.goods_data_cache_handle(goods.id, goods);
this.$emit('url_event', e);
},
},
};
</script>
<style scoped lang="scss">
.w {
width: 100%;
}
.h {
height: 100%;
}
.half-width {
width: 50%;
}
.gap-20 {
gap: 40rpx;
}
</style>

View File

@@ -0,0 +1,57 @@
<template>
<!-- 视频 -->
<view class="video pr wh-auto ht-auto">
<video :src="video" class="wh-auto ht-auto" :poster="video_img" objectFit="cover" :style="'object-fit: cover;' + video_style"></video>
</view>
</template>
<script>
import { padding_computer, radius_computer } from '@/common/js/common/common.js';
export default {
props: {
propKey: {
type: [String, Number],
default: '',
},
propValue: {
type: Object,
default: () => ({}),
},
propDataStyle: {
type: Object,
default: () => ({}),
}
},
data() {
return {
style_container: '',
video_img: '',
video: '',
video_style: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue || {};
// 视频比例
this.setData({
video_img: new_content.video_img.length > 0 ? new_content.video_img[0].url : '',
video: new_content.video.length > 0 ? new_content.video[0].url : '',
video_style: radius_computer(this.propDataStyle.img_radius),
});
}
},
};
</script>
<style></style>

View File

@@ -0,0 +1,79 @@
<template>
<view :class="['oh img_wh', propClass]" :style="empty_outer_style + propStyle">
<image :src="img_url" :mode="propImgFit" :style="empty_style + 'display: block;'" />
</view>
</template>
<script>
import { is_obj, isEmpty } from '@/common/js/common/common.js';
export default {
props: {
propImageSrc: {
type: [Object, String],
default: () => {},
},
propErrorStyle: {
type: String,
default: () => '',
},
propImgFit: {
type: String,
default: () => 'aspectFill',
},
propStyle: {
type: String,
default: () => '',
},
propClass: {
type: String,
default: () => '',
},
},
data() {
return {
empty_outer_style: '',
empty_style: 'width: 100%; height: 100%;', // 有图片的时候显示为100%
img_url: '',
default_image: '/static/images/common/image-empty.png',
};
},
watch: {
propImageSrc(val) {
this.init();
}
},
mounted() {
this.init();
},
methods: {
init() {
let img_url = this.propImageSrc;
if (is_obj(this.propImageSrc)) {
img_url = !isEmpty(this.propImageSrc) ? this.propImageSrc.url : '';
}
// 没有图片的时候根据默认值来算
if (img_url == undefined || img_url == null || img_url == '') {
this.setData({
empty_outer_style: 'background: #f4fcff;display:flex;align-items: center;justify-content: center;',
empty_style: `${this.propErrorStyle}`,
});
img_url = this.default_image;
} else {
this.setData({
empty_outer_style: '',
empty_style: 'width: 100%; height: 100%;', // 有图片的时候显示为100%
});
}
this.setData({
img_url: img_url,
});
},
},
};
</script>
<style lang="scss" scoped>
.img_wh {
width: 100%;
height: 100%;
}
</style>

View File

@@ -0,0 +1,98 @@
<template>
<view v-if="form.seckill_subscript_show == '1'" class="corner-marker" :style="corner_marker">
<view class="flex-row nowrap" :style="corner_img_marker">
<template v-if="form.subscript_type == 'img-icon'">
<template v-if="!isEmpty(form.subscript_img_src)">
<image :src="form.subscript_img_src[0].url" mode="aspectFill" :style="img_style" />
</template>
<template v-else>
<iconfont :name="'icon-' + form.subscript_icon_class" propContainerDisplay="flex" :size="new_style.subscript_style.text_or_icon_size * 2 + 'rpx'" :color="new_style.subscript_style.text_or_icon_color"></iconfont>
</template>
</template>
<template v-else>
<span class="text-line-1" :style="text_size">{{ form.subscript_text }}</span>
</template>
</view>
</view>
</template>
<script>
import { common_img_computer, common_styles_computer, isEmpty } from '@/common/js/common/common.js';
import iconfont from '@/components/iconfont/iconfont';
export default {
components: {
iconfont,
},
props: {
propValue: {
type: Object,
default: () => ({}),
},
propType: {
type: String,
default: 'outer',
},
},
data() {
return {
form: {},
new_style: {},
corner_img_marker: '',
img_style: '',
text_size: '',
};
},
created() {
this.init();
},
methods: {
isEmpty,
// 初始化数据
init() {
if (!isEmpty(this.propValue)) {
const new_content = this.propValue.content || {};
const new_style = this.propType == 'outer' ? this.propValue.style || {} : { subscript_style: this.propValue.style || {} };
if (!isEmpty(new_style.subscript_style)) {
// 视频比例
this.setData({
form: new_content,
new_style: new_style,
corner_marker: this.get_corner_marker(new_style),
text_size: `font-size: ${ new_style.subscript_style.text_or_icon_size * 2 }rpx;color: ${ new_style.subscript_style.text_or_icon_color };`,
corner_img_marker: common_img_computer(new_style.subscript_style),
img_style: `height: ${new_style.subscript_style.img_height * 2}rpx; width: ${new_style.subscript_style.img_width * 2}rpx`,
});
}
}
},
get_corner_marker(new_style) {
const { subscript_style } = new_style;
let location = common_styles_computer(subscript_style);
// 获取内部的显示数据
const { seckill_subscript_location, top_or_bottom_spacing, left_or_right_spacing } = subscript_style;
// 圆角根据图片的圆角来计算 对角线是同样的圆角
if (seckill_subscript_location == 'top-left') {
location += `top: ${ top_or_bottom_spacing * 2 }rpx;left: ${ left_or_right_spacing * 2 }rpx;`;
} else if (seckill_subscript_location == 'top-center') {
location += 'top: 0;left: 50%;transform: translateX(-50%);';
} else if (seckill_subscript_location == 'top-right') {
location += `top: ${ top_or_bottom_spacing * 2 }rpx;right:${ left_or_right_spacing * 2 }rpx;`;
} else if (seckill_subscript_location == 'bottom-left') {
location += `bottom: ${ top_or_bottom_spacing * 2 }rpx;left: ${ left_or_right_spacing * 2 }rpx;`;
} else if (seckill_subscript_location == 'bottom-center') {
location += 'bottom: 0;left: 50%;transform: translateX(-50%);';
} else if (seckill_subscript_location == 'bottom-right') {
location += `bottom: ${ top_or_bottom_spacing * 2 }rpx;right: ${ left_or_right_spacing * 2 }rpx;`;
}
return location;
},
},
};
</script>
<style>
.corner-marker {
position: absolute;
max-width: 100%;
}
</style>

View File

@@ -0,0 +1,592 @@
<template>
<!-- 66rpx是自定义顶部导航栏的高度-->
<view class="tabs-view" :style="tabs_sticky">
<view class="tabs-view" :style="propStyle + propTabsBackground">
<view class="pr" :style="propsTabsContainer">
<view v-if="propIsRotatingBackground" class="pa top-0 wh-auto ht-auto" :style="propBgImgStyle"></view>
<view class="flex-row gap-10 jc-sb align-c" :style="propsTabsImgContainer">
<view class="tabs flex-1 flex-width">
<scroll-view :scroll-x="true" :show-scrollbar="false" :scroll-with-animation="tabs_list_is_sliding_fixed" :scroll-left="scroll_left" :class="'wh-auto interior-area-' + propKey">
<view :class="'flex-row ' + flex_class" :style="'height:' + tabs_height + ';width:' + tabs_scroll_width + 'px;'">
<view v-for="(item, index) in tabs_list" :key="index" :class="'item nowrap flex-col jc-c align-c gap-4 scroll-item-' + propKey + ' ' + tabs_theme + (index == active_index ? ' active' : '') + ((tabs_theme_index == '0' && tabs_theme_1_style) || tabs_theme_index == '1' || tabs_theme_index == '2' ? ' pb-0' : '')" :style="'flex:0 0 auto;padding-left:' + (index == 0 ? '0' : tabs_spacing) + 'rpx;padding-right:' + (index + 1 == tabs_list.length ? '0' : tabs_spacing) + 'rpx;' + get_item_style(item.is_sliding_fixed)" :data-index="index" @tap="handle_event">
<view class="nowrap ma-auto">
<view v-if="tabs_theme_index == '4'" :class="'img oh pr z-i-deep ' + (!isEmpty(item.img) ? 'img-no-empty' : '')" :style="tabs_theme_style.tabs_top_img">
<imageEmpty :propImageSrc="item.img[0]" propImgFit="aspectFit" propErrorStyle="width: 20rpx;height: 20rpx;"></imageEmpty>
<!-- <image :src="item.img[0].url" class="img" mode="aspectFit" /> -->
</view>
<template v-if="item.tabs_type == '1'">
<template v-if="!isEmpty(item.tabs_icon)">
<view :class="tabs_theme_index == '4' ? 'title pr z-i' : 'title pr z-i-deep'" :style="(tabs_theme_index == '4' ? tabs_sign_spacing : '') + (index == active_index ? (['2', '4'].includes(tabs_theme_index) ? tabs_check : '') : '' + tabs_padding_bottom)">
<iconfont :name="'icon-' + item.tabs_icon" :color="index == active_index ? tabs_icon_checked_color : tabs_icon_color" propContainerDisplay="flex" :size="index == active_index ? tabs_icon_checked_size : tabs_icon_size"></iconfont>
</view>
</template>
<template v-else>
<view :class="tabs_theme_index == '4' ? 'title pr z-i' : 'title pr z-i-deep'" :style="(tabs_theme_index == '4' ? tabs_sign_spacing : '') + index == active_index ? new_style.is_tabs_img_background == '1' && ['2', '4'].includes(tabs_theme_index) ? tabs_check : '' : tabs_padding_bottom">
<imageEmpty :propImageSrc="item.tabs_img[0]" :propStyle="index == active_index ? tabs_theme_style.tabs_img_checked : tabs_theme_style.tabs_img" propImgFit="heightFix" propErrorStyle="width: 40rpx;height: 40rpx;"></imageEmpty>
</view>
</template>
</template>
<template v-else>
<view :class="tabs_theme_index == '4' ? 'title pr z-i' : 'title pr z-i-deep'" :style="(tabs_theme_index == '4' ? tabs_sign_spacing : '') + (index == active_index ? ['2', '4'].includes(tabs_theme_index) ? tabs_theme_style.tabs_title_checked + tabs_check : tabs_theme_style.tabs_title_checked : tabs_theme_style.tabs_title + tabs_padding_bottom)">{{ item.title }}</view>
</template>
<view class="desc pr z-i wh-auto" :style="tabs_sign_spacing + (tabs_theme_index == '1' && index == active_index ? tabs_check : '')">{{ item.desc }}</view>
<template v-if="tabs_theme_index == '3' && index == active_index">
<template v-if="!isEmpty(form.tabs_adorn_icon)">
<view class="icon pr z-i wh-auto flex-row jc-c align-c" :style="tabs_sign_spacing">
<iconfont :name="'icon-' + form.tabs_adorn_icon" :style="icon_tabs_check" propContainerDisplay="flex" :size="tabs_adorn_icon_size"></iconfont>
</view>
</template>
<template v-else>
<view class="pr z-i wh-auto flex-row jc-c align-c ma-auto" :style="tabs_sign_spacing">
<imageEmpty :propImageSrc="form.tabs_adorn_img[0]" :propStyle="tabs_adorn_img_style" propImgFit="aspectFit" propErrorStyle="width: 20rpx;height: 20rpx;"></imageEmpty>
</view>
</template>
</template>
<view class="bottom_line z-i" :class="tabs_bottom_line_theme" :style="tabs_check + tabs_sign_spacing"></view>
</view>
</view>
</view>
</scroll-view>
</view>
<view v-if="propIsTabsIcon && form.show_more !== '0'" :style="tabs_padding_bottom">
<iconfont :name="'icon-' + icon.more_icon_class || 'category-more'" :size="icon.more_icon_size + '' || '14'" :color="icon.more_icon_color || '#000'" propContainerDisplay="flex" @click="category_check_event"></iconfont>
</view>
</view>
</view>
</view>
<!-- 选项卡更多弹窗 -->
<componentPopup :propShow="popup_status" :propIsBar="propIsBar" propPosition="top" :propMask="true" :propTop="newPropTop" :propStyle="newPropStyle" @onclose="quick_close_event">
<view :class="'padding-bottom-lg ' + (['toutiao', 'app', 'h5'].includes(platform) ? 'padding-top-lg' : 'padding-top')">
<view class="padding-left-main padding-bottom-main">全部选项卡</view>
<view class="divider-b">
<view class="nav-list-more">
<view class="flex-row flex-wrap align-c">
<view v-for="(item, index) in tabs_list" :key="index" class="item tc cr-base cp text-size-xs" :data-index="index" @tap="handle_event">
<view class="dis-inline-block padding-vertical-xs padding-horizontal round" :class="active_index == index ? 'bg-main border-color-main cr-white' : ''">
{{ item.title }}
</view>
</view>
</view>
</view>
</view>
<view class="tc padding-top-lg flex-row jc-c align-c" @tap="quick_close_event">
<text class="padding-right-sm">{{ $t('nav-more.nav-more.h9g4b1') }}</text>
<iconfont name="icon-arrow-top" color="#ccc" propContainerDisplay="flex"></iconfont>
</view>
</view>
</componentPopup>
</view>
</template>
<script>
const app = getApp();
import { gradient_computer, isEmpty, radius_computer } from '@/common/js/common/common.js';
import componentPopup from '@/components/popup/popup';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
components: {
componentPopup,
imageEmpty,
},
props: {
propValue: {
type: Object,
default: () => {},
},
// 是否需要icon
propIsTabsIcon: {
type: Boolean,
default: false,
},
// 是否需要粘性定位置顶
propIsTop: {
type: Boolean,
default: false,
},
// 指定样式
propStyle: {
type: String,
default: '',
},
// 层级
propZIndex: {
type: Number,
default: 11,
},
// 自定义导航栏高度
propCustomNavHeight: {
type: String,
default: '66rpx',
},
// tabs背景色
propTabsBackground: {
type: String,
default: 'transparent',
},
// 置顶距离顶部高度
propTop: {
type: [String, Number],
default: '0',
},
propsTabsContainer: {
type: String,
default: ''
},
propsTabsImgContainer: {
type: String,
default: '',
},
propIsRotatingBackground: {
type: Boolean,
default: false,
},
propBgImgStyle: {
type: String,
default: '',
},
propKey: {
type: [String, Number],
default: '',
},
propTabsSlidingFixedBg: {
type: String,
default: ''
}
},
data() {
return {
form: {},
new_style: {},
tabs_theme_index: '',
tabs_theme: '',
tabs_check: '',
icon_tabs_check: '',
tabs_spacing: '',
tabs_sign_spacing: '',
tabs_padding_bottom: '',
tabs_list: [],
active_index: 0,
tabs_theme_style: {
tabs_title_checked: '',
tabs_title: '',
tabs_img_checked: '',
tabs_img: '',
tabs_top_img: ''
},
tabs_icon_checked_size: '',
tabs_icon_size: '',
tabs_icon_checked_color: '',
tabs_icon_color: '',
icon: {
more_icon_class: '',
more_icon_size: '',
more_icon_color: '',
},
// 过滤弹窗
popup_status: false,
propIsBar: false,
tabs_bottom_line_theme: '',
tabs_sticky: '',
tabs_height: '100%',
tabs_adorn_img_style: '',
tabs_width: 0,
tabs_scroll_width: 0,
// #ifdef MP
sticky_top: bar_height,
// #endif
// #ifdef H5 || MP-TOUTIAO
sticky_top: bar_height,
// #endif
// #ifdef APP
sticky_top: bar_height,
// #endif
newPropTop: '',
newPropStyle: '',
platform: app.globalData.application_client_type(),
is_out_of_range: false,
tabs_list_is_sliding_fixed: true,
scroll_left: 0,
tabs_adorn_icon_size: '0rpx',
// 默认数据
old_radius: { radius: 0, radius_top_left: 0, radius_top_right: 0, radius_bottom_left: 0, radius_bottom_right: 0 },
old_padding: { padding: 0, padding_top: 0, padding_bottom: 0, padding_left: 0, padding_right: 0 },
old_margin: { margin: 0, margin_top: 10, margin_bottom: 0, margin_left: 0, margin_right: 0 },
};
},
computed: {
flex_class() {
if (this.is_out_of_range) {
return this.form.justification == 'center' ? 'jc-c' : this.form.justification == 'right' ? 'jc-e': '';
} else {
return '';
}
},
get_item_style() {
return (val) => {
return val == '1' ? `${ this.propTabsSlidingFixedBg };position: sticky;left: 0;z-index: 11;` : ''
}
}
},
watch: {
propValue() {
this.init();
},
propIsTop: {
deep: true,
immediate: true,
handler(new_val) {
if (new_val) {
this.tabs_sticky = 'position: sticky;top: calc(' + parseInt(this.propTop) + 'px + ' + this.propCustomNavHeight + ');z-index: ' + this.propZIndex + ';';
} else {
this.tabs_sticky = '';
}
},
},
propTop(new_val, old_val) {
if (this.propIsTop) {
this.tabs_sticky = 'position: sticky;top: calc(' + parseInt(this.propTop) + 'px + ' + this.propCustomNavHeight + ');z-index: ' + this.propZIndex + ';';
} else {
this.tabs_sticky = '';
}
},
},
mounted() {
this.init();
},
methods: {
// 判断是否为空
isEmpty,
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
const new_tabs_check = this.tabs_check_computer(new_style);
const new_icon = {
more_icon_class: new_style.more_icon_class,
more_icon_size: new_style.more_icon_size,
more_icon_color: new_style.more_icon_color,
};
const tabs_top_img_height = new_style?.tabs_top_img_height || 39;
const tabs_top_img_radius = new_style?.tabs_top_img_radius || { radius: 100, radius_top_left: 100, radius_top_right: 100, radius_bottom_left: 100, radius_bottom_right: 100}
// 标题样式
const new_tabs_theme_style = {
tabs_title_checked: `font-weight: ${new_style.tabs_weight_checked};font-size: ${new_style.tabs_size_checked * 2}rpx;line-height: ${new_style.tabs_size_checked * 2}rpx;color:${new_style.tabs_color_checked};`,
tabs_title: `font-weight: ${new_style.tabs_weight};font-size: ${new_style.tabs_size * 2}rpx;line-height: ${new_style.tabs_size * 2}rpx;color:${new_style.tabs_color};`,
tabs_img_checked: `height: ${(new_style?.tabs_img_height || 0) * 2}rpx;` + radius_computer(new_style?.tabs_img_radius || this.old_radius),
tabs_img: `height: ${(new_style?.tabs_img_height || 0) * 2}rpx;` + radius_computer(new_style?.tabs_img_radius || this.old_radius),
tabs_top_img: `height: ${tabs_top_img_height * 2 }rpx;width: ${tabs_top_img_height * 2 }rpx;box-sizing: border-box;` + radius_computer(tabs_top_img_radius),
};
const { tabs_size_checked, tabs_size, tabs_icon_size_checked = 0, tabs_icon_size = 0, tabs_img_height = 0, tabs_sign_spacing = 0 } = new_style || {};
let default_height = 0;
if (new_content.tabs_theme == '2') {
default_height = 12; // 选中的时候,风格二的内间距
} else if (new_content.tabs_theme == '4') {
// const top_index = new_content?.tabs_list?.findIndex((item) => !isEmpty(item.img)) ?? -1;
// default_height = 4 + (top_index > -1 ? tabs_top_img_height + tabs_sign_spacing : 0); // 选中的时候,风格二的内间距 加上上边图片的大小和上边图片之间的间距
default_height = 4 + tabs_top_img_height + tabs_sign_spacing;
}
// 筛选出所有的icon
const is_icon = new_content?.tabs_list?.findIndex((item) => item.tabs_type === '1' && !isEmpty(item.tabs_icon)) ?? -1;
// 如果有icon则取选中的icon大小和未选中的icon大小取最大值作为图标的高度
let icon_height = 0;
if (is_icon > -1) {
icon_height = Math.max(tabs_icon_size_checked + default_height, tabs_icon_size);
}
// 筛选出所有的图片, 没有选择图标的时候默认是图片
const is_img = new_content?.tabs_list?.findIndex((item) => item.tabs_type === '1' && isEmpty(item.tabs_icon)) ?? -1;
// 选项卡高度 五个值,作为判断依据,因为图片没有未选中的大小设置,所以高度判断的时候只取选中的高度, 其余的icon和标题都分别取选中和未选中的大小对比取出最大的值作为选项卡的高度避免选项卡切换时会出现抖动问题
const height = Math.max(tabs_size_checked + default_height, tabs_size, icon_height, is_img > -1 ? (tabs_img_height + default_height) : '');
const findIndex = new_content.tabs_list.findIndex(item => item.is_sliding_fixed == '1');
// 参数设置
this.setData({
form: new_content,
tabs_list_is_sliding_fixed: findIndex == -1,
newPropTop: `calc(${ this.sticky_top * 2}rpx);`,
newPropStyle: `padding-top: ${ this.sticky_top * 2 }rpx;margin-top: -${ this.sticky_top * 2 }rpx;`,
new_style: new_style,
tabs_spacing: Number(new_style.tabs_spacing),
tabs_sign_spacing: !isEmpty(new_style.tabs_sign_spacing) ? `margin-top: ${new_style.tabs_sign_spacing * 2}rpx;` : 'margin-top: 8rpx;',
tabs_list: new_content.tabs_list,
tabs_padding_bottom: this.get_padding_bottom(new_content, new_style) + 'z-index: 11;',
// 选项卡主题
tabs_theme: this.get_tabs_theme(new_content),
tabs_theme_index: new_content.tabs_theme,
// 选项卡样式
tabs_check: new_tabs_check,
icon_tabs_check: `${new_tabs_check};line-height: 1;background-clip: text;-webkit-background-clip: text;-webkit-text-fill-color: transparent;`,
icon: new_icon,
tabs_icon_checked_size: (new_style?.tabs_icon_size_checked || 0) * 2 + 'rpx',
tabs_icon_size: (new_style?.tabs_icon_size || 0) * 2 + 'rpx',
tabs_icon_checked_color: new_style?.tabs_icon_color_checked || '',
tabs_icon_color: new_style?.tabs_icon_color || '',
tabs_theme_style: new_tabs_theme_style,
tabs_bottom_line_theme: new_style.tabs_one_theme == '1' ? 'tabs-bottom-line-theme' : '',
tabs_theme_1_style: new_style.tabs_one_theme == '1',
tabs_height: ['2', '4'].includes(new_content.tabs_theme) ? height * 2 + 'rpx' : '100%;',
tabs_adorn_img_style: this.get_tabs_adorn_img_style(new_style),
tabs_adorn_icon_size: (new_style?.tabs_adorn_icon_size || 0) * 2 + 'rpx',
});
// 只有居中居右的才重新获取dom判断
// if (['center', 'right'].includes(this.form.justification)) {
setTimeout(() => {
const query = uni.createSelectorQuery().in(this);
query.select('.interior-area-' + this.propKey)
.fields({ size: true, scrollOffset: true},(res) => {
if ((res || null) != null) {
const { scrollWidth, width } = res;
this.setData({
is_out_of_range: scrollWidth <= width,
tabs_scroll_width: scrollWidth,
tabs_width: width
});
}
})
.exec();
}, 0)
// }
},
get_tabs_adorn_img_style(new_style) {
return radius_computer(new_style?.tabs_adorn_img_radius || this.old_radius) + `height: ${(new_style?.tabs_adorn_img_height || 10) * 2}rpx;${ new_style.is_tabs_adorn_img_background == '1' ? tabs_check.value : ''}`;
},
// 获取选项卡主题
get_tabs_theme(data) {
let arr = {
1: 'tabs-style-2',
2: 'tabs-style-3',
3: 'tabs-style-4',
4: 'tabs-style-5',
};
let value = arr[data.tabs_theme];
return value === undefined ? 'tabs-style-1' : value;
},
get_padding_bottom(form, new_style) {
let bottom = 0;
if (form.tabs_theme == '0') {
if (new_style.tabs_one_theme == '1') {
bottom = 6;
} else {
bottom = 3;
}
} else if (form.tabs_theme == '3') {
bottom = !isEmpty(form.tabs_adorn_icon) ? new_style.tabs_adorn_icon_size : new_style.tabs_adorn_img_height;
}
const tabs_sign_spacing = !isEmpty(new_style.tabs_sign_spacing) ? new_style.tabs_sign_spacing : 4;
return ['1', '2', '4'].includes(form.tabs_theme) ? '' : `padding-bottom: ${(tabs_sign_spacing + bottom) * 2 }rpx;`;
},
// 选中的背景渐变色样式
tabs_check_computer(data) {
const new_gradient_params = {
color_list: data.tabs_checked,
direction: data.tabs_direction,
};
return gradient_computer(new_gradient_params);
},
// 事件
// tabs切换事件
handle_event(e) {
const index = e.currentTarget.dataset.index;
const tabs_list_item = this.tabs_list[index];
this.setData({
active_index: index,
popup_status: false,
});
this.set_scoll_left(index);
this.$emit('onTabsTap', index, tabs_list_item);
setTimeout(() => {
this.$emit('tabsZindex', 11)
}, 200)
},
// 将选中的内容定位到中间
set_scoll_left(index) {
const query = uni.createSelectorQuery().in(this);
query.selectAll(`.scroll-item-` + this.propKey)
.boundingClientRect((rect) => {
const tabs_index = this.form.tabs_list.findIndex(item => item.is_sliding_fixed == '1');
// 如果第一个悬浮了就取第二个的left加上 第一个的宽度和left
let new_width = tabs_index == 0 && index != 0 ? rect[1].left - rect[0].width - rect[0].left : rect[0].left;
// 如果悬浮的不是第一个并且选中的是悬浮的内容
if (index > 0 && tabs_index == index) {
new_width = rect[0].left - rect[index + 1].left + rect[index].width;
}
const scrollLeft =
rect[index].left +
rect[index].width / 2 -
this.tabs_width / 2 -
new_width
this.setData({
scroll_left: scrollLeft,
});
})
.exec();
},
// 分类选择事件
category_check_event() {
this.setData({
popup_status: true,
});
setTimeout(() => {
this.$emit('tabsZindex', 13)
}, 0)
},
// 关闭分类选择事件
quick_close_event(e) {
this.setData({
popup_status: false,
});
setTimeout(() => {
this.$emit('tabsZindex', 11)
}, 200)
},
},
};
</script>
<style lang="scss" scoped>
.tabs {
.item {
// padding: 0 0 10rpx 0;
position: relative;
&:first-of-type {
margin-left: 0;
}
&:last-of-type {
margin-right: 0;
}
.title {
font-size: 28rpx;
text-align: center;
}
.desc {
font-size: 22rpx;
color: #999;
text-align: center;
display: none;
}
.bottom_line {
width: 100%;
height: 6rpx;
border-radius: 20rpx;
background-color: red;
// position: absolute;
left: 0;
right: 0;
bottom: 0;
display: none;
}
.icon {
// position: absolute;
bottom: 0;
text-align: center;
font-size: 40rpx;
line-height: 20rpx !important;
display: none;
}
.img {
// width: 78rpx;
// height: 78rpx;
border-radius: 100%;
border: 2rpx solid transparent;
display: none;
margin: 0 auto;
}
&.tabs-style-1 {
&.active {
.bottom_line {
display: block;
}
}
.tabs-bottom-line-theme {
opacity: 0.6;
bottom: 0;
z-index: 0;
height: 14rpx;
border-radius: 0;
// left: 12%;
}
}
&.tabs-style-2 {
&.active {
.desc {
background: #ff5e5e;
color: #fff;
}
}
.desc {
border-radius: 40rpx;
padding: 4rpx 12rpx;
box-sizing: border-box;
display: block;
}
}
&.tabs-style-3 {
&.active {
.title {
// background: #ff2222;
border-radius: 40rpx;
padding: 6px 12px;
box-sizing: border-box;
color: #fff;
}
}
}
&.tabs-style-4 {
// padding-bottom: 28rpx;
&.active {
.title {
color: #ff2222;
}
.icon {
color: #ff2222;
display: flex;
}
}
}
&.tabs-style-5 {
align-items: center;
&.active {
.title {
// font-size: 22rpx;
// background: #ff5e5e;
border-radius: 40rpx;
padding: 2px 7px;
box-sizing: border-box;
// color: #fff;
}
.img {
border-color: #ff5e5e;
}
}
.img-no-empty {
width: 100% !important;
}
.img {
display: block;
}
}
}
}
.nav-list-more {
width: 100%;
max-height: 550rpx;
padding-bottom: 20rpx;
overflow-y: auto;
}
.nav-list-more .item {
width: 20%;
padding: 20rpx 0;
}
.pb-14 {
padding-bottom: 28rpx;
}
.pb-0 {
padding-bottom: 0 !important;
}
.ma-auto {
margin: auto;
}
</style>

View File

@@ -0,0 +1,248 @@
<template>
<view :style="style_container">
<view class="pr" :style="style_img_container">
<swiper class="swiper" circular="true" :autoplay="new_style.is_roll == '1'" :interval="new_style.interval_time * 1000" :duration="500" :display-multiple-items="slides_per_group" :style="{ height: new_height }" @change="slideChange">
<swiper-item v-for="(item, index) in nav_content_list" :key="index" class="swiper-item">
<view class="banner-img flex-row flex-wrap wh-auto" :class="'banner-img-' + propKey" :style="space">
<view v-for="(item1, index1) in item.split_list" :key="index1" class="flex-col gap-10 align-c" :style="group_width + nav_title_space" :data-value="item1.link.page" @tap="url_open_event">
<view v-if="['image_with_text', 'image'].includes(nav_style)" class="flex-row align-c jc-c pr">
<view class="top-img" :style="img_size">
<imageEmpty :propImageSrc="item1.img[0]" :propStyle="img_style" propErrorStyle="width: 60rpx;height: 60rpx;"></imageEmpty>
</view>
<!-- 角标 -->
<subscriptIndex :propValue="item1.subscript" propType="nav-group"></subscriptIndex>
</view>
<view v-if="['image_with_text', 'text'].includes(nav_style)" class="wh-auto size-12 ma-0 nowrap oh tc" :style="text_style">{{ item1.title }}</view>
</view>
</view>
</swiper-item>
</swiper>
<view v-if="form.display_style == 'slide' && new_style.is_show == '1'" :class="['left', 'right'].includes(new_style.indicator_new_location) ? 'indicator_up_down_location' : 'indicator_about_location'" :style="indicator_location_style">
<block v-if="new_style.indicator_style == 'num'">
<view :style="indicator_style" class="dot-item">
<text :style="{ color: new_style.actived_color }">{{ actived_index + 1 }}</text>
<text>/{{ nav_content_list.length }}</text>
</view>
</block>
<block v-else>
<view v-for="(item, index) in nav_content_list" :key="index" :style="indicator_style + (actived_index == index ? 'background:' + new_style.actived_color : '')" class="dot-item" />
</block>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { isEmpty, common_styles_computer, common_img_computer, radius_computer, padding_computer, get_indicator_style, get_indicator_location_style } from '@/common/js/common/common.js';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
import subscriptIndex from '@/components/diy/modules/subscript/index.vue';
export default {
components: {
imageEmpty,
subscriptIndex,
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
form: {},
new_style: {},
style_container: '',
style_img_container: '',
img_style: '',
text_style: '',
indicator_style: '',
new_height: '300rpx',
actived_index: 0,
group_width: '',
nav_content_list: [],
nav_title_space: 'row-gap:20rpx', // 导航标题间距
space: 'row-gap:20rpx', // 导航间距
img_size: '72rpx',
slides_per_group: 1,
indicator_location_style: '',
subscriptStyle: {
seckill_subscript_location: 'top-left',
text_or_icon_color: '#fff',
text_or_icon_size: 12,
img_width: 20,
img_height: 20,
direction: '90deg',
top_or_bottom_spacing: 0,
left_or_right_spacing: 0,
color_list: [{ color: '#FF7607', color_percentage: undefined }],
background_img_style: '2',
background_img: [],
margin: 0,
margin_top: 0,
margin_bottom: 0,
margin_left: 0,
margin_right: 0,
radius: 4,
radius_top_left: 4,
radius_top_right: 4,
radius_bottom_left: 4,
radius_bottom_right: 4,
padding: 0,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
box_shadow_color: '',
box_shadow_x: 0,
box_shadow_y: 0,
box_shadow_blur: 0,
box_shadow_spread: 0,
}
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
mounted() {
this.init();
},
methods: {
init() {
const new_content = this.propValue.content;
const new_style = this.propValue.style;
let group = 1;
let group_width = `width: ${100 / (new_content.single_line || 4)}%;`;
// 判断是否是轮播图
if (new_content?.display_style == 'slide') {
if (new_content.row == 1 && new_style.rolling_fashion == 'translation') {
group = new_content.single_line || 4;
group_width = 'width: 100%;';
} else {
group = 1;
group_width = `width: ${100 / (new_content.single_line || 4)}%;`;
}
}
this.setData({
form: new_content,
new_style: new_style,
style_container: common_styles_computer(new_style.common_style), // 用于样式显示
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
img_style: radius_computer(new_style), // 图片的设置
text_style: `font-size: ${new_style.title_size * 2 || 24}rpx; color: ${new_style.title_color || '#000'};`, // 标题的样式
indicator_style: get_indicator_style(new_style), // 指示器的样式
indicator_location_style: get_indicator_location_style(new_style), // 指示器位置处理
actived_color: new_style.actived_color || '#2A94FF', // 轮播图显示样式
slides_per_group: group, // 每个轮播图显示的个数
group_width: group_width, // 每个导航所占位置
nav_title_space: 'row-gap:' + (new_style.title_space || 0) * 2 + 'rpx', // 导航标题间距
space: 'row-gap:' + (new_style.space || 0) * 2 + 'rpx;' + padding_computer(new_style.data_padding), // 导航间距
img_size: 'width:' + (new_style.img_size || 0) * 2 + 'rpx;height:' + (new_style.img_size || 0) * 2 + 'rpx;', // 图片大小
nav_style: new_content.nav_style || 'image_with_text', // 是否显示文字和图片
nav_content_list: this.get_nav_content_list(new_content, new_style),
});
setTimeout(() => {
const query = uni.createSelectorQuery().in(this);
// 选择我们想要的元素
query
.select('.banner-img-' + this.propKey)
.boundingClientRect((res) => {
if ((res || null) != null) {
// data包含元素的宽度、高度等信息
this.setData({
new_height: res.height + 'px',
});
}
})
.exec(); // 执行查询
}, 50);
},
get_nav_content_list(data, new_style) {
// 深拷贝一下,确保不会出现问题
const list = JSON.parse(JSON.stringify(data.nav_content_list || Array(4))).map(item => ({
...item,
// 角标配置
subscript: isEmpty(item.subscript) ?
{
content: {
seckill_subscript_show: '0',
subscript_type: 'text',
subscript_img_src: [],
subscript_icon_class: '',
subscript_text: '',
},
style: this.subscriptStyle,
} : item.subscript,
}));
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (list.length > 0 && data.display_style == 'slide') {
// 存储数据显示
let nav_list = [];
if (data.row == 1 && new_style.rolling_fashion == 'translation') {
// 拆分的数量
list.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
return nav_list;
} else {
// 每页显示的数量
const num = (data.single_line || 4) * (data.row || 1);
// 拆分的数量
const split_num = Math.ceil(list.length / num);
for (let i = 0; i < split_num; i++) {
nav_list.push({
split_list: list.slice(i * num, (i + 1) * num),
});
}
return nav_list;
}
} else {
// 否则的话,就返回全部的信息
return [
{
split_list: list,
},
];
}
},
slideChange(e) {
this.setData({
actived_index: e.detail.current,
});
},
url_open_event(link) {
app.globalData.url_event(link);
},
},
};
</script>
<style scoped lang="scss">
.top-img {
height: 72rpx;
width: 72rpx;
border-radius: 8rpx;
}
.gap-x-10 {
row-gap: 20rpx;
}
</style>

211
components/diy/notice.vue Normal file
View File

@@ -0,0 +1,211 @@
<template>
<!-- 公告 -->
<view :style="style_container">
<view :style="style_img_container">
<template v-if="form_content.notice_style == 'inherit'">
<view class="news-box" :style="container_background_style + container_height">
<view class="flex-row align-c gap-8" :style="container_background_img_style">
<template v-if="form_content.title_type == 'img-icon'">
<view v-if="form_content.icon_class">
<iconfont :name="'icon-' + form_content.icon_class" :size="form_style.icon_size * 2 + 'rpx'" :color="form_style.icon_color" propContainerDisplay="flex"></iconfont>
</view>
<view v-else>
<image v-if="form_content.img_src.length > 0" :src="form_content.img_src[0].url" class="border-radius-sm dis-block" mode="aspectFill" :style="img_style"></image>
</view>
</template>
<template v-else>
<view :style="title_style" class="padding-horizontal-sm border-radius-sm">{{ form_content.title || '公告' }}</view>
</template>
<swiper class="swiper flex-1" circular :indicator-dots="false" :autoplay="true" :interval="interval_time" :vertical="direction_type == 'vertical'" :style="container_height">
<swiper-item v-for="(item, index) in notice_list" :key="index">
<view class="swiper-item flex-row align-c ht-auto" :style="content_title_style + 'color:' + form_style.news_color" :data-value="item.notice_link.page" @tap="url_event">
<view class="text-line-1">{{ item.notice_title }}</view>
</view>
</swiper-item>
</swiper>
<view v-if="form_content.is_right_button == '1'" class="flex-row align-c" :style="'color: ' + form_style.right_button_color + ';font-size:' + form_style.right_button_size * 2 + 'rpx;'" :data-value="form_content.more_link.page" @tap="url_event">
{{ form_content.right_title }}
<view class="pr">
<iconfont name="icon-arrow-right" :color="form_style.right_button_color || '#999'" :size="form_style.right_button_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
</view>
</template>
<template v-else>
<view class="news-card" :style="container_background_style">
<view class="flex-col gap-10" :style="container_background_img_style">
<view class="flex-row wh-auto jc-sb align-c">
<template v-if="form_content.title_type == 'img-icon'">
<template v-if="form_content.icon_class">
<iconfont :name="'icon-' + form_content.icon_class" :size="form_style.icon_size * 2 + 'rpx'" :color="form_style.icon_color" propContainerDisplay="flex"></iconfont>
</template>
<template v-else>
<image v-if="form_content.img_src.length > 0" :src="form_content.img_src[0].url" class="border-radius-sm dis-block" mode="aspectFill" :style="img_style"></image>
</template>
</template>
<template v-else>
<view :style="title_style" class="padding-horizontal-sm border-radius-sm">{{ form_content.title }}</view>
</template>
<view v-if="form_content.is_right_button == '1'" class="flex-row align-c" :style="'color: ' + form_style.right_button_color + ';font-size:' + form_style.right_button_size * 2 + 'rpx;'" :data-value="form_content.more_link.page" @tap="url_event">
{{ form_content.right_title }}
<view class="pr">
<iconfont name="icon-arrow-right" :color="form_style.right_button_color || '#999'" :size="form_style.right_button_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
<view v-for="(item, index) in notice_list" :key="index" class="flex-row" :style="content_title_style" :data-value="item.notice_link.page" @tap="url_event">
<view class="num" :class="'one' + (index + 1)">{{ index + 1 }}</view>
<view class="break" :style="'color:' + form_style.news_color">{{ item.notice_title }}</view>
</view>
</view>
</view>
</template>
</view>
</view>
</template>
<script>
const app = getApp();
import { background_computer, common_styles_computer, common_img_computer, gradient_computer, gradient_handle, radius_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
form_content: {},
form_style: {},
style_container: '',
style_img_container: '',
// 容器高度
container_height: '',
// 容器背景
container_background_style: '',
// 图片设置
img_style: '',
// 标题的设置
title_style: '',
// 内容标题设置
content_title_style: '',
// 指示器的样式
// 轮播图定时显示
interval_time: 2000,
// 轮播图滚动方向 // vertical' | 'horizontal
direction_type: 'vertical',
// 记录当前显示的轮播图的数据
interval_list: {
time: 2000,
direction: 'vertical',
notice_length: 1,
},
// 公告数据
notice_list: [],
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
// 容器背景
const { container_color_list, container_direction, container_background_img_style, container_background_img } = new_style;
const temp_obj = {
color_list: container_color_list,
direction: container_direction,
background_img: container_background_img,
background_img_style: container_background_img_style,
};
const temp_container_background_style = gradient_computer(temp_obj) + radius_computer(new_style.container_radius) + `overflow:hidden;`;
const temp_container_background_img_style = background_computer(temp_obj);
// 标题渐变色处理
const gradient = gradient_handle(new_style.title_color_list, '90deg');
const time = (new_style.interval_time || 2) * 1000;
const direction = new_content.direction;
const new_notice_list = new_content.notice_list.filter((item) => item.is_show == '1');
// 判断长度是否相等
const notice_length = new_notice_list.length;
const new_interval_list = {
time: time,
direction: direction,
notice_length: notice_length,
};
this.setData({
form_content: new_content,
form_style: new_style,
container_height: 'height:' + new_style.container_height * 2 + 'rpx',
container_background_style: temp_container_background_style,
container_background_img_style: temp_container_background_img_style,
img_style: `height: ${new_style.title_height * 2}rpx; width: ${new_style.title_width * 2}rpx`,
title_style: `color:${new_style.title_color}; font-size: ${new_style.title_size * 2}rpx; font-weight: ${new_style.title_typeface}; ${gradient}`,
content_title_style: `font-size: ${new_style.news_size * 2}rpx; font-weight: ${new_style.news_typeface};`,
notice_list: new_notice_list,
interval_time: time,
direction_type: direction,
interval_list: new_interval_list,
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
},
// 跳转链接
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style lang="scss" scoped>
.news-box {
overflow: hidden;
padding: 0 20rpx;
background: #fff;
}
.news-card {
padding: 30rpx;
background: #fff;
}
.num {
padding-right: 14rpx;
color: #999;
}
.one1 {
color: #ea3323;
}
.one2 {
color: #ff7303;
}
.one3 {
color: #ffc300;
}
.two-style {
width: 48rpx;
height: 48rpx;
}
.break {
word-break: break-word;
overflow-wrap: break-word;
word-wrap: break-word;
}
</style>

View File

@@ -0,0 +1,73 @@
<template>
<!-- 富文本 -->
<view class="diy-rich-text" :style="style_container">
<view :style="style_img_container">
<mp-html :content="content" />
</view>
</view>
</template>
<script>
import { common_styles_computer, common_img_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
content: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
this.setData({
content: new_content.html,
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
},
},
};
</script>
<style>
.diy-rich-text {
* {
max-width: 100%;
}
/* #ifdef H5 */
[id^=v] {
width: 100%;
}
/* #endif */
}
.diy-rich-text video {
width: 100%;
}
</style>

327
components/diy/search.vue Normal file
View File

@@ -0,0 +1,327 @@
<template>
<view :style="style_container">
<view :style="style_img_container">
<view class="search wh-auto pr">
<view class="box oh flex-row align-c" :style="box_style">
<view v-if="form.positioning_name_float == '1' && propSearchType == 'header'" :style="propLocationMargin">
<component-choice-location propType="search" :propLocationContainerStyle="propLocationContainerStyle" :propLocationImgContainerStyle="propLocationImgContainerStyle" :propBaseColor="propBaseColor" :propTextDefaultName="form.positioning_name" :propIsLeftIconArrow="form.is_location_left_icon_show == '1'" :propLeftImgValue="form.location_left_img" :propLeftIconValue="'icon-' + form.location_left_icon" :propIconLocationSize="propIconLocationSize" :propIconArrowSize="propIconArrowSize" :propIsRightIconArrow="form.is_location_right_icon_show == '1'" :propRightImgValue="form.location_right_img" :propRightIconValue="'icon-' + form.location_right_icon" :propTextMaxWidth="['4'].includes(form.theme) ? '300rpx' : '200rpx'" propContainerDisplay="flex" @onBack.stop="choice_location_back"></component-choice-location>
</view>
<view :class="'oh flex-1 ht-auto flex-row align-c gap-10' + (form.is_center == '1' ? ' tips-float' : '')" @tap.stop="search_tap">
<view v-if="form.is_icon_show == '1'" class="pr">
<view class="search-icon-before" :style="(form.positioning_name_float == '1' && propSearchType == 'header') || form.is_center == '1' ? '' : 'left: -' + (new_style.search_padding_left ? new_style.search_padding_left * 2 : 30) + 'rpx;'" @tap.stop="search_icon_tap"></view>
<view class="wh-auto ht-auto">
<template v-if="form.icon_img.length > 0">
<view class="img-box">
<image :src="form.icon_img[0].url" class="img" mode="heightFix"></image>
</view>
</template>
<template v-else>
<view>
<iconfont :name="!isEmpty(form.icon_class) ? 'icon-' + form.icon_class : 'icon-search-max'" size="28rpx" :color="new_style.icon_color" propContainerDisplay="flex"></iconfont>
</view>
</template>
</view>
</view>
<view v-if="!isEmpty(form.hot_word_list) && form.is_hot_word_show == '1'" :style="form.is_center == '1' ? `min-width:100px;` : 'width:100%;'">
<swiper circular="true" :autoplay="new_style.is_roll == '1'" :interval="new_style.interval_time * 1000" :vertical="true" :duration="500" class="swiper_style" @change="slideChange">
<swiper-item v-for="(item, index) in form.hot_word_list" :key="index">
<view class="flex-row align-c wh-auto ht-auto" :style="{ color: !isEmpty(item.color) ? item.color : !isEmpty(new_style.hot_words_color) ? new_style.hot_words_color : '#999' }" :data-value="item.value" @tap.stop="serch_event">
{{ item.value }}
</view>
</swiper-item>
</swiper>
</view>
<template v-else>
<text v-if="form.is_tips_show == '1'" :class="[propIsPageSettings ? 'text-size-xs text-line-1' : 'text-size-md text-line-1']" :style="'color:' + new_style.tips_color">{{ form.tips }}</text>
</template>
</view>
</view>
<view class="flex-row align-c pa search-outer-botton oh" :style="right_icon_style">
<view v-if="form.is_right_icon_show == '1' && (form.right_icon_img.length > 0 || !isEmpty(form.right_icon_class))" class="pr margin-right-main">
<view class="search-icon-before" @tap.stop="search_right_icon_tap"></view>
<view class="wh-auto ht-auto">
<template v-if="form.right_icon_img.length > 0">
<view class="img-box right_icon_height flex-row align-c">
<image :src="form.right_icon_img[0].url" class="img" mode="heightFix"></image>
</view>
</template>
<template v-else>
<view class="right_icon_height flex-row align-c">
<iconfont :name="!isEmpty(form.right_icon_class) ? 'icon-' + form.right_icon_class : 'icon-search-max'" size="28rpx" :color="new_style.right_icon_color" propContainerDisplay="flex"></iconfont>
</view>
</template>
</view>
</view>
<view v-if="form.is_search_show == '1'" class="flex-row align-c jc-c">
<view class="search-botton flex-row align-c jc-c z-i" :style="search_button_style" @tap.stop="serch_button_event">
<view class="oh" :style="search_button_img_style">
<template v-if="form.search_type === 'text'">
<view class="text-size-xs">{{ form.search_tips }}</view>
</template>
<template v-else-if="!isEmpty(form.search_botton_img) && form.search_botton_img.length > 0">
<image :src="form.search_botton_img[0].url" class="img" :style="search_button_height" mode="heightFix"></image>
</template>
<template v-else>
<view class="text-size-xs">
<iconfont :name="!isEmpty(form.search_botton_icon) ? 'icon-' + form.search_botton_icon : ''" size="28rpx" propContainerDisplay="flex"></iconfont>
</view>
</template>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import componentChoiceLocation from '@/components/choice-location/choice-location';
const app = getApp();
import { background_computer, common_styles_computer, common_img_computer, gradient_computer, radius_computer, isEmpty, padding_computer, old_padding, old_radius, margin_computer } from '@/common/js/common/common.js';
export default {
components: {
componentChoiceLocation,
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propIsPageSettings: {
type: Boolean,
default: false,
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
propSearchType: {
type: String,
default: 'search',
},
propBaseColor: {
type: String,
default: '',
},
propLocationMargin: {
type: String,
default: '',
},
propLocationContainerStyle: {
type: String,
default: '',
},
propLocationImgContainerStyle: {
type: String,
default: '',
},
propIconLocationSize: {
type: String,
default: '',
},
propIconArrowSize: {
type: String,
default: '',
},
},
data() {
return {
search_content: '',
form: {},
new_style: {},
style: '',
style_container: '',
style_img_container: '',
search_button_radius: '',
box_style: '',
keywords: '',
right_icon_style: '',
search_button_style: '',
search_button_img_style: '',
search_button_height: '',
button_padding: { padding: 0, padding_bottom: 3, padding_left: 12, padding_right: 12, padding_top: 3 },
button_margin: { margin: 0, margin_bottom: 2, margin_left: 0, margin_right: 2, margin_top: 2 },
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
isEmpty,
init() {
const new_form = this.propValue.content;
const new_style = this.propValue.style;
const { search_button_radius, common_style } = new_style;
this.setData({
form: new_form,
new_style: new_style,
search_button_img_style: this.get_search_button_img_style(new_style),
search_button_height: this.get_search_button_height(new_style),
// style: this.get_style(new_style), // 内部样式
style_container: this.propIsPageSettings ? '' : common_styles_computer(common_style), // 全局样式
style_img_container: this.propIsPageSettings ? '' : common_img_computer(common_style, this.propIndex),
search_button_radius: radius_computer(search_button_radius), // 按钮圆角
box_style: this.get_box_style(new_style, new_form), // 搜索框设置
search_box_style: `border: 2rpx solid ${new_style.search_border == '#fff' ? '#eee' : new_style.search_border};`,
search_button_style: this.get_search_button_style(new_form, new_style), // 搜索按钮显示
right_icon_style: `border-radius: 0px ${ new_style.search_border_radius?.radius_top_right * 2 || 0 }rpx ${ new_style.search_border_radius?.radius_bottom_right * 2 || 0 }rpx 0px;`,
});
},
get_search_button_style(form, new_style) {
const { search_botton_color_list = [], search_botton_direction, search_botton_background_img_style, search_button_radius = old_radius, search_botton_background_img, search_botton_margin = this.button_margin, search_botton_border_show = '0', search_botton_border_size = old_padding, search_botton_border_style = 'solid', search_botton_border_color = '' } = new_style;
let style = radius_computer(search_button_radius);
if (form.search_type != 'img') {
const data = {
color_list: search_botton_color_list,
direction: search_botton_direction,
background_img: search_botton_background_img,
background_img_style: search_botton_background_img_style,
}
style += gradient_computer(data) + margin_computer(search_botton_margin) + `color: ${ new_style.button_inner_color };`;
}
let border = ``;
if (search_botton_border_show == '1') {
border += `border-width: ${search_botton_border_size.padding_top * 2}rpx ${search_botton_border_size.padding_right * 2}rpx ${search_botton_border_size.padding_bottom * 2}rpx ${search_botton_border_size.padding_left * 2}rpx;border-style: ${ search_botton_border_style };border-color: ${search_botton_border_color};`
}
const height = 32 - search_botton_margin.margin_top - search_botton_margin.margin_bottom - search_botton_border_size.padding_top - search_botton_border_size.padding_bottom;
return style + border + `height: ${height > 0 ? height * 2 : 0}rpx;line-height: ${height > 0 ? height * 2 : 0}rpx;`;
},
get_search_button_height(new_style) {
const { search_botton_border_size = old_padding, search_botton_padding = this.button_padding } = new_style;
const height = 32 - search_botton_border_size.padding_top - search_botton_border_size.padding_bottom - search_botton_padding.padding_top - search_botton_padding.padding_bottom;
return `height: ${height > 0 ? height * 2 : 0}rpx !important;line-height: ${height > 0 ? height * 2 : 0}rpx;`;
},
// 搜索按钮圆角
get_search_button_img_style(new_style) {
const { search_botton_background_img_style, search_botton_background_img } = new_style;
const data = {
background_img: search_botton_background_img,
background_img_style: search_botton_background_img_style,
}
return background_computer(data) + padding_computer(new_style?.search_botton_padding || this.button_padding) + 'box-sizing: border-box;';
},
// get_style(new_style) {
// let common_styles = '';
// if (new_style.text_style == 'italic') {
// common_styles += `font-style: italic`;
// } else if (new_style.text_style == '500') {
// common_styles += `font-weight: 500`;
// }
// return common_styles;
// },
get_box_style(new_style, form) {
let style = `background: ${ new_style?.search_bg_color || '' };border: 2rpx solid ${new_style.search_border}; ${radius_computer(new_style.search_border_radius)};box-sizing: border-box;`;
if (form.positioning_name_float == '1' && this.propSearchType == 'header') {
style += `padding-left: ${ (new_style.search_padding_left? new_style.search_padding_left : 15) * 2 }rpx;`;
} else if (form.is_center != '1') {
style += `padding-left: ${ (new_style.search_padding_left ? new_style.search_padding_left : 15) * 2 }rpx;`;
}
return style;
},
search_tap() {
app.globalData.url_open('/pages/goods-search-start/goods-search-start?keywords=' + this.keywords);
},
serch_event() {
app.globalData.url_open('/pages/goods-search-start/goods-search-start?keywords=' + this.keywords);
},
serch_button_event() {
if (!isEmpty(this.keywords)) {
app.globalData.url_open('pages/goods-search/goods-search?keywords=' + this.keywords);
}
app.globalData.url_open('/pages/goods-search-start/goods-search-start?keywords=' + this.keywords);
},
search_icon_tap() {
if (isEmpty(this.form.icon_link)) {
this.search_tap();
return;
}
app.globalData.url_open(this.form.icon_link.page);
},
search_right_icon_tap() {
if (isEmpty(this.form.right_icon_link)) {
this.search_tap();
return;
}
app.globalData.url_open(this.form.right_icon_link.page);
},
slideChange(e) {
let actived_index = e.detail.current;
this.setData({
keywords: this.form.hot_word_list[actived_index].value,
})
},
// 位置回调
onBack(e) {
this.$emit('onBack', e);
},
},
};
</script>
<style lang="scss" scoped>
.search {
.box {
height: 64rpx;
// padding: 12rpx 30rpx;
}
.swiper_style {
height: 64rpx;
width: 100%;
}
.img-box {
height: 100%;
.img {
height: 36rpx;
display: block;
}
}
.search-outer-botton {
height: 64rpx;
top: 0;
right: 0;
.search-botton {
height: 64rpx;
.img {
height: 64rpx;
min-width: 6rpx;
max-width: 20rpx;
}
}
}
.right_icon_height {
position: relative;
height: 56rpx !important;
}
.search-icon {
position: relative;
}
.search-icon-before {
position: absolute;
z-index: 10; // 确保悬浮在内容上层
top: -20rpx;
right: -20rpx;
bottom: -20rpx;
left: -20rpx
}
.tips-float {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
}
</style>

643
components/diy/seckill.vue Normal file
View File

@@ -0,0 +1,643 @@
<template>
<view v-if="!isEmpty(list) || !isEmpty(sckill_list)" :style="style_container">
<view :style="style_img_container">
<view class="flex-col gap-10">
<view v-if="form.head_state == '1'" class="oh" :style="seckill_head_style">
<view class="seckill-head flex-row align-c jc-sb" :style="seckill_head_img_style">
<view :class="['flex-row align-c', { 'gap-10': form.theme != '1', 'jc-sb wh-auto': form.theme == '2' }]">
<view class="seckill-title">
<imageEmpty v-if="form.title_type == 'image'" :propImageSrc="form.title_src[0]" propImgFit="heightFix" propErrorStyle="width:42rpx; height: 20rpx;"></imageEmpty>
<text v-else :style="{ color: new_style.title_color, 'font-size': new_style.title_size * 2 + 'rpx', 'line-height': '42rpx', 'font-weight': 600 }">{{ form.title_text }}</text>
</view>
<view v-if="form.theme == '1'" class="padding-horizontal-sm cr-white">|</view>
<view v-if="intervalId != undefined" class="flex-row align-c gap-4">
<text class="text-size-xss" :style="{ color: new_style.end_text_color }">{{ seckill_time.time_first_text }}</text>
<view class="flex-row gap-3 jc-c align-c" :style="form.theme == '4' ? time_bg + 'padding: 6rpx 8rpx;border-radius: 22rpx;' : ''">
<image v-if="form.theme == '4' && form.theme_4_static_img.length > 0" class="seckill-head-icon radius-xs" :src="form.theme_4_static_img[0].url" />
<view v-for="(item, index) in time_config" :key="item.key" class="flex-row gap-3 jc-c align-c">
<template v-if="form.theme == '4'">
<view class="text-size-xs flex-row jc-c align-c" :style="'min-width:50rpx;color:' + new_style.countdown_color">{{ item.value }}</view>
<text v-if="[0, 1].includes(index)" class="colon" :style="{ color: new_style.countdown_color }">:</text>
</template>
<template v-else>
<view class="time-config text-size-xs flex-row jc-c align-c" :style="time_bg + 'min-width:50rpx;color:' + new_style.countdown_color">{{ item.value }}</view>
<text v-if="[0, 1].includes(index)" class="colon" :style="icon_time_check">:</text>
</template>
</view>
</view>
</view>
<view v-else class="flex-row align-c gap-4">
<text class="text-size-xss" :style="{ color: new_style.end_text_color }">已结束</text>
</view>
</view>
<view v-if="form.button_status == '1'" class="flex-row align-c" :style="{ color: new_style.head_button_color }" :data-value="'/pages/plugins/seckill/index/index'" @tap="url_event">
<text :style="{ 'font-size': new_style.head_button_size * 2 + 'rpx' }">{{ form.button_text }}</text>
<iconfont name="icon-arrow-right" :color="new_style.head_button_color" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
<view class="oh" :style="shop_container">
<view class="oh" :style="shop_img_container">
<template v-if="form.shop_style_type != '3'">
<view class="flex-row flex-wrap wh-auto ht-auto" :style="{ gap: content_outer_spacing }">
<view v-for="(item, index) in sckill_list" :key="index" :style="layout_style + layout_type_style + content_radius" :data-value="item.goods_url" @tap="url_event">
<view :class="layout_type" :style="layout_img_style">
<template v-if="!isEmpty(item)">
<view class="oh pr">
<view v-if="!isEmpty(item.new_cover)" :style="img_size">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="content_img_radius" propErrorStyle="width:100rpx; height: 100rpx;"></imageEmpty>
</view>
<view v-else :style="img_size">
<imageEmpty :propImageSrc="item.images" :propStyle="content_img_radius" propErrorStyle="width:100rpx; height: 100rpx;"></imageEmpty>
</view>
<!-- 角标 -->
<subscriptIndex :propValue="propValue"></subscriptIndex>
</view>
</template>
<view v-if="is_show('title') || is_show('simple_desc') || is_show('price') || is_show('original_price') || form.is_shop_show == '1'" class="flex-col gap-10 wh-auto flex-1 jc-sb oh" :style="content_style">
<view class="flex-col gap-10 wh-auto">
<!-- 标题 -->
<view v-if="is_show('title') || is_show('simple_desc')" class="flex-col" :style="{'gap': new_style.title_simple_desc_spacing * 2 + 'rpx' }">
<view v-if="is_show('title')" :style="title_style" class="text-line-2">{{ item.title }}</view>
<view v-if="is_show('simple_desc')" class="text-line-1" :style="simple_desc">{{ item.simple_desc }}</view>
</view>
<!-- 进度条 -->
<!-- <view v-if="form.shop_style_type == '1'" class="flex-row align-c gap-6">
<view class="re flex-1">
<view class="slide-bottom" :style="`background: ${new_style.progress_bg_color}`"></view>
<view class="slide-top" :style="` width: 51%; ${slide_active_color}`">
<view class="slide-top-icon round" :style="`background: ${new_style.progress_button_color}`"><icon name="a-miaosha" :color="new_style.progress_button_icon_color" size="9"></icon></view>
</view>
</view>
<text class="text-size-xss" :style="`color: ${new_style.progress_text_color}`">已抢51%</text>
</view> -->
</view>
<view class="flex-row align-e gap-10 jc-sb">
<view class="flex-col gap-5">
<view v-if="is_show('price') && !isEmpty(item.min_price)" class="num" :style="{ color: new_style.shop_price_color }">
<text v-if="form.shop_style_type == '1'" class="text-size-xss pr-4">{{ form.seckill_pirce_title ? form.seckill_pirce_title : ''}}</text>
<text :style="price_symbol">{{ item.show_price_symbol }}</text>
<text :style="price_style">{{ item.min_price }}</text>
<text v-if="is_show('price_unit')" :style="price_unit">{{ item.show_price_unit }}</text>
</view>
<view v-if="is_show('original_price') && !isEmpty(item.min_original_price)" class="size-11 flex" :style="original_price">
<text class="original-price text-line-1 flex-1">
{{ item.show_original_price_symbol }}{{ item.min_original_price }}
<template v-if="is_show('original_price_unit')">
{{ item.show_original_price_unit }}
</template>
</text>
</view>
</view>
<view v-if="form.is_shop_show == '1'">
<template v-if="form.shop_type == 'text'">
<view class="plr-11 padding-vertical-xs round" :style="button_style + 'color:' + new_style.shop_button_text_color">{{ form.shop_button_text }}</view>
</template>
<view v-else class="round padding-horizontal-sm ptb-5" :styles="button_gradient">
<iconfont :name="'icon-' + (!isEmpty(form.shop_button_icon_class) ? form.shop_button_icon_class : 'cart')" :color="new_style.shop_icon_color" :size="new_style.shop_icon_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<template v-else>
<swiper circular="false" :autoplay="new_style.is_roll == '1'" :next-margin="new_style.rolling_fashion == 'translation' ? '-' + content_outer_spacing_magin : '0rpx'" :interval="new_style.interval_time * 1000" :duration="500" :display-multiple-items="slides_per_group" :style="{ height: new_style.content_outer_height * 2 + 'rpx' }">
<swiper-item v-for="(item1, index1) in list" :key="index1">
<view class="wh-auto ht-auto" :class="{ 'flex-row': new_style.rolling_fashion != 'translation' }" :style="new_style.rolling_fashion != 'translation' ? 'gap:' + content_outer_spacing_magin : ''">
<view v-for="(item, index) in item1.split_list" :key="index" :style="layout_style + content_radius + (new_style.rolling_fashion != 'translation' ? layout_type_style : 'margin-right:' + content_outer_spacing_magin + ';height: 100%;whith: 100%')" :data-value="item.goods_url" @tap="url_event">
<view :class="layout_type" :style="layout_img_style">
<template v-if="!isEmpty(item)">
<view class="oh pr wh-auto ht-auto">
<view v-if="!isEmpty(item.new_cover)" class="wh-auto ht-auto">
<imageEmpty :propImageSrc="item.new_cover[0]" :propStyle="content_img_radius" propErrorStyle="width:100rpx; height: 100rpx;"></imageEmpty>
</view>
<view v-else class="wh-auto ht-auto">
<imageEmpty :propImageSrc="item.images" :propStyle="content_img_radius" propErrorStyle="width:100rpx; height: 100rpx;"></imageEmpty>
</view>
<!-- 角标 -->
<subscriptIndex :propValue="propValue"></subscriptIndex>
</view>
</template>
<view v-if="is_show('title') || is_show('simple_desc') || is_show('price') || is_show('original_price') || form.is_shop_show == '1'" class="flex-col gap-10 wh-auto flex-1 jc-sb" :style="content_style">
<view class="flex-col gap-10 wh-auto">
<!-- 标题 -->
<view v-if="is_show('title') || is_show('simple_desc')" class="flex-col" :style="{'gap': new_style.title_simple_desc_spacing * 2 + 'rpx' }">
<view v-if="is_show('title')" :style="title_style" class="text-line-2">{{ item.title }}</view>
<view v-if="is_show('simple_desc')" class="text-line-1" :style="simple_desc">{{ item.simple_desc }}</view>
</view>
<!-- 进度条 -->
<!-- <view v-if="form.shop_style_type == '1'" class="flex-row align-c gap-6">
<view class="re flex-1">
<view class="slide-bottom" :style="{ 'background': new_style.progress_bg_color }"></view>
<view class="slide-top" :style="'width: 51%;' + slide_active_color ">
<view class="slide-top-icon round" :style="{ 'background': new_style.progress_button_color}"><icon name="a-miaosha" :color="new_style.progress_button_icon_color" size="9"></icon></view>
</view>
</view>
<text class="text-size-xss" :style="{ 'color': new_style.progress_text_color }">已抢51%</text>
</view> -->
</view>
<view class="flex-row align-e gap-10 jc-sb">
<view class="flex-col gap-5">
<view v-if="is_show('price') && !isEmpty(item.min_price)" class="num" :style="{ color: new_style.shop_price_color }">
<text v-if="form.shop_style_type == '1'" class="text-size-xss pr-4">{{ form.seckill_pirce_title ? form.seckill_pirce_title : ''}}</text>
<text :style="price_symbol">{{ item.show_price_symbol }}</text>
<text :style="price_style">{{ item.min_price }}</text>
<text v-if="is_show('price_unit')" :style="price_unit">{{ item.show_price_unit }}</text>
</view>
<view v-if="is_show('original_price') && !isEmpty(item.min_original_price)" class="size-11 flex" :style="original_price">
<text class="original-price text-line-1 flex-1">
{{ item.show_original_price_symbol }}{{ item.min_original_price }}
<template v-if="is_show('original_price_unit')">
{{ item.show_original_price_unit }}
</template>
</text>
</view>
</view>
<view v-if="form.is_shop_show == '1'">
<template v-if="form.shop_type == 'text'">
<view class="plr-11 padding-vertical-xs round" :style="button_style + 'color:' + new_style.shop_button_text_color">{{ form.shop_button_text }}</view>
</template>
<view v-else class="round padding-horizontal-sm ptb-5" :styles="button_gradient">
<iconfont :name="'icon-' + (!isEmpty(form.shop_button_icon_class) ? form.shop_button_icon_class : 'cart')" :color="new_style.shop_icon_color" :size="new_style.shop_icon_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
</template>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { background_computer, common_styles_computer, common_img_computer, gradient_computer, gradient_handle, padding_computer, radius_computer, isEmpty, margin_computer, box_shadow_computer, border_computer, old_margin, old_border_and_box_shadow } from '@/common/js/common/common.js';
import imageEmpty from '@/components/diy/modules/image-empty.vue';
import subscriptIndex from '@/components/diy/modules/subscript/index.vue';
var system = app.globalData.get_system_info(null, null, true);
var sys_width = app.globalData.window_width_handle(system.windowWidth);
export default {
components: {
imageEmpty,
subscriptIndex
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
form: {},
new_style: {},
time_bg: '',
slide_active_color: '',
seckill_head_style: '',
style_container: '',
style_img_container: '',
time_config: [
{ key: 'hour', value: '00' },
{ key: 'minute', value: '00' },
{ key: 'second', value: '00' },
],
seckill_time: {},
// 商品间距
content_outer_spacing: '',
content_outer_spacing_magin: '',
// 圆角设置
content_radius: '',
// 内边距设置
content_padding: '',
// 内容区域的样式
content_style: '',
// 不同风格下的样式
img_size: '',
layout_type: '',
layout_type_style: '',
layout_img_style: '',
layout_style: '',
//图片圆角设置
content_img_radius: '',
//角标设置
corner_marker: '',
// 定时器
intervalId: null,
// 数据存储
list: [],
// 一屏显示的数量
slides_per_group: '',
// 内容样式
title_style: '',
price_style: '',
button_style: '',
simple_desc: '',
price_symbol: '',
price_unit: '',
original_price: '',
shop_container: '',
shop_img_container: '',
// 商品列表
sckill_list: [],
};
},
computed: {
icon_time_check() {
return `${this.time_bg};line-height: 1;background-clip: text;-webkit-background-clip: text;-webkit-text-fill-color: transparent;`;
},
// 按钮渐变色处理
button_gradient() {
return gradient_handle(this.new_style.shop_button_color, '180deg');
},
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
beforeDestroy() {
// 如果有定时任务执行,在离开的时候清空掉定时任务
if (!isEmpty(this.intervalId)) {
clearInterval(this.intervalId);
}
},
methods: {
isEmpty,
init() {
const new_form = this.propValue.content;
const new_style = this.propValue.style;
const data = new_form.data;
let new_list = [];
if (data && !isEmpty(data.current)) {
if (!isEmpty(data.current.goods)) {
new_list = data.current.goods;
}
const { status, time_first_text } = data.current.time;
this.setData({
seckill_time: {
time_end_number: Number(data.current.time_end_number + '000'),
time_start_number: Number(data.current.time_start_number + '000'),
status: status,
time_first_text: time_first_text,
},
});
// 先执行一次倒计时,后续的等待倒计时执行
setTimeout(() => {
this.updateCountdown();
}, 0);
this.setData({
intervalId: setInterval(this.updateCountdown, 1000),
});
}
// 默认数据
const product_style_list = [
{ name: '单列', value: '1', width: 110, height: 120 },
{ name: '双列', value: '2', width: 180, height: 180 },
{ name: '横向滑动', value: '3', width: 0, height: 0 },
];
const scale = sys_width / 390;
let img_style = ``;
if (['1'].includes(new_form.shop_style_type)) {
// 图片宽度
if (typeof new_style.content_img_width == 'number') {
img_style += `width: ${ new_style.content_img_width * scale }px;`;
} else {
const list = product_style_list.filter(item => item.value == new_form.shop_style_type);
if (list.length > 0) {
img_style += `width: ${ list[0].width * scale }px;`;
} else {
img_style += 'width: auto;';
}
}
}
if (!['3'].includes(new_form.shop_style_type)) {
// 图片宽度
if (typeof new_style.content_img_height == 'number') {
img_style += `height: ${ new_style.content_img_height * scale }px;`;
} else {
const list = product_style_list.filter(item => item.value == new_form.shop_style_type);
if (list.length > 0) {
img_style += `height: ${ list[0].height * scale }px;`;
} else {
img_style += 'height: auto;';
}
}
}
const shop_style = new_style?.shop_style || old_border_and_box_shadow;
this.setData({
form: this.propValue.content,
new_style: this.propValue.style,
time_bg: this.get_time_bg(new_style),
slide_active_color: this.get_slide_active_color(new_style),
seckill_head_style: this.get_seckill_head_style(new_style, '1'),
seckill_head_img_style: this.get_seckill_head_style(new_style, '2'),
style_container: common_styles_computer(new_style.common_style) + 'box-sizing: border-box;',
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
content_outer_spacing: new_style.content_outer_spacing + 'px',
content_outer_spacing_magin: new_style.content_outer_spacing * 2 + 'rpx',
content_radius: radius_computer(new_style.shop_radius),
content_padding: padding_computer(new_style.shop_padding) + 'box-sizing: border-box;',
content_style: this.get_content_style(new_style, new_form),
layout_type: new_form.shop_style_type == '1' ? 'flex-row wh-auto ht-auto oh' : 'flex-col wh-auto ht-auto oh',
layout_img_style: this.get_layout_img_style(new_style, new_form),
layout_type_style: this.get_layout_type_style(new_style, new_form),
layout_style: gradient_handle(new_style.shop_color_list, new_style.shop_direction) + margin_computer(new_style?.shop_margin || old_margin) + border_computer(shop_style) + box_shadow_computer(shop_style),
content_img_radius: radius_computer(new_style.shop_img_radius),
corner_marker: this.get_corner_marker(new_style),
slides_per_group: new_style.rolling_fashion == 'translation' ? new_form.carousel_col : 1,
// 内容样式设置
title_style: this.trends_config(new_style, 'title', 'title'),
price_style: this.trends_config(new_style, 'price'),
button_style: this.trends_config(new_style, 'button', 'gradient'),
simple_desc: this.trends_config(new_style, 'simple_desc', 'desc'),
price_symbol: !isEmpty(new_style.shop_price_symbol_color) ? this.trends_config(new_style, 'price_symbol') : 'font-size: 18rpx;color: #EA3323;' ,
price_unit: !isEmpty(new_style.shop_price_unit_color) ? this.trends_config(new_style, 'price_unit') : 'font-size: 18rpx;color: #EA3323;',
original_price: this.trends_config(new_style, 'original_price'),
list: this.get_shop_content_list(new_list, new_form, new_style),
shop_container: this.get_shop_container(new_style),
shop_img_container: this.get_shop_img_container(new_style),
sckill_list: new_list,
img_size: img_style,
});
},
// 商品内容区域显示
get_shop_container(new_style){
const { shop_content_color_list = [], shop_content_direction = '', shop_content_radius = old_radius, shop_content_margin = old_margin, shop_content = old_border_and_box_shadow } = new_style;
// 渐变
const gradient = { color_list: shop_content_color_list, direction: shop_content_direction };
return gradient_computer(gradient) + radius_computer(shop_content_radius) + margin_computer(shop_content_margin) + border_computer(shop_content) + box_shadow_computer(shop_content);
},
get_shop_img_container (new_style) {
const { shop_content_background_img_style = '2', shop_content_background_img = [], shop_content_padding = old_padding } = new_style;
// 背景图
const back = { background_img: shop_content_background_img, background_img_style: shop_content_background_img_style };
return padding_computer(shop_content_padding) + background_computer(back);
},
get_shop_content_list(list, form, new_style) {
// 深拷贝一下,确保不会出现问题
const cloneList = JSON.parse(JSON.stringify(list));
if (new_style.rolling_fashion != 'translation') {
// 如果是分页滑动情况下,根据选择的行数和每行显示的个数来区分具体是显示多少个
if (cloneList.length > 0) {
// 每页显示的数量
const num = form.carousel_col;
// 存储数据显示
let 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,
},
];
}
} else {
// 存储数据显示
let nav_list = [];
cloneList.forEach((item) => {
nav_list.push({
split_list: [item],
});
});
return nav_list;
}
},
get_time_bg(new_style) {
const { countdown_bg_color_list, countdown_direction } = new_style;
// 渐变
const gradient = { color_list: countdown_bg_color_list, direction: countdown_direction };
return gradient_computer(gradient);
},
get_slide_active_color(new_style) {
const { progress_actived_color_list, progress_actived_direction } = new_style;
// 渐变
const gradient = { color_list: progress_actived_color_list, direction: progress_actived_direction };
return gradient_computer(gradient);
},
get_seckill_head_style(new_style, num) {
const { header_background_img, header_background_img_style, header_background_color_list, header_background_direction, seckill_head_padding, seckill_head_radius, seckill_head_margin = old_margin, seckill_head_style = old_border_and_box_shadow } = new_style;
// 渐变
const gradient = { color_list: header_background_color_list, direction: header_background_direction };
// 背景图
const back = { background_img: header_background_img, background_img_style: header_background_img_style };
if (num == '1') {
return gradient_computer(gradient) + radius_computer(seckill_head_radius) + margin_computer(seckill_head_margin) + border_computer(seckill_head_style) + box_shadow_computer(seckill_head_style);
} else {
// 秒杀头部内间距设置, 没有的时候默认15px
const padding = !isEmpty(seckill_head_padding) ? seckill_head_padding : { padding: 0, padding_top: 15, padding_bottom: 15, padding_left: 13, padding_right: 13};
return background_computer(back) + padding_computer(padding) + 'box-sizing: border-box;';
}
},
updateCountdown() {
let time_end_number = this.seckill_time.time_end_number;
if (this.seckill_time.status === 0) {
time_end_number = this.seckill_time.time_start_number;
}
// 先获取秒杀结束时间和当前时间的差值
const distance = time_end_number - new Date().getTime();
// 如果倒计时结束,显示结束信息
if (distance <= 1000) {
clearInterval(this.intervalId);
// 如果是待开始状态,则显示开始倒计时,并且在结束的时候根据结束时候再执行一个定时器
if (this.seckill_time.status === 0) {
this.setData({
seckill_time: {
time_end_number: this.seckill_time.time_end_number,
time_start_number: this.seckill_time.time_start_number,
status: 1,
time_first_text: '距结束',
},
});
// 先执行一次倒计时,后续的等待倒计时执行
setTimeout(() => {
this.updateCountdown();
}, 0);
this.setData({
intervalId: setInterval(this.updateCountdown, 1000),
});
}
return;
}
// 计算时间
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
this.time_config.forEach((item) => {
if (item.key == 'hour') {
item.value = hours < 10 ? '0' + hours : hours.toString();
} else if (item.key == 'minute') {
item.value = minutes < 10 ? '0' + minutes : minutes.toString();
} else if (item.key == 'second') {
item.value = seconds < 10 ? '0' + seconds : seconds.toString();
}
});
},
get_content_style(new_style, form) {
const spacing_value = new_style.content_spacing;
let spacing = '';
if (form.shop_style_type == '1') {
spacing = `margin-left: ${spacing_value * 2}rpx;`;
} else {
spacing = padding_computer(new_style.shop_padding) + 'box-sizing: border-box;';
}
return `${spacing}`;
},
get_layout_type_style(new_style, form) {
return ['1', '2'].includes(form.shop_style_type) ? `height: 100%;width: ${this.get_multicolumn_columns_width(new_style, form)};min-width: ${this.get_multicolumn_columns_width(new_style, form)};` : `height: ${new_style.content_outer_height * 2}rpx;width: ${this.get_multicolumn_columns_width(new_style, form)};min-width: ${this.get_multicolumn_columns_width(new_style, form)};`;
},
get_layout_img_style(new_style, form) {
const padding = ['1'].includes(form.shop_style_type) ? padding_computer(new_style.shop_padding) + 'box-sizing: border-box;' : '';
const data = {
background_img_style: new_style.shop_background_img_style,
background_img: new_style.shop_background_img,
}
return `${padding} ${ background_computer(data) }`;
},
// 根据传递的参数,从对象中取值
trends_config(new_style, key, type) {
return this.style_config(new_style[`shop_${key}_typeface`], new_style[`shop_${key}_size`], new_style[`shop_${key}_color`], type, new_style);
},
// 根据传递的值,显示不同的内容
style_config(typeface, size, color, type, new_style) {
let style = `font-weight:${typeface}; font-size: ${size * 2}rpx;`;
if (type == 'gradient') {
style += gradient_handle(new_style.shop_button_color, '180deg');
} else if (type == 'title') {
style += `line-height: ${size > 0 ? (size + 3) * 2 : 0}rpx;height: ${size > 0 ? (size + 3) * 4 : 0}rpx;color: ${color};`;
} else if (type == 'desc') {
style += `line-height: ${size * 2}rpx;height: ${size * 2}rpx;color: ${color};`;
} else {
style += `color: ${color};`;
}
return style;
},
get_multicolumn_columns_width(new_style, form) {
let model_number = form.carousel_col;
if (form.shop_style_type == '1') {
model_number = 1;
} else if (form.shop_style_type == '2') {
model_number = 2;
}
// 计算间隔的空间。(gap * gap数量) / 模块数量
let gap = (new_style.content_outer_spacing * (model_number - 1)) / model_number;
return `calc(${100 / model_number}% - ${gap}px)`;
},
is_show(index) {
return this.form.is_show.includes(index);
},
get_corner_marker(new_style) {
const { seckill_subscript_location, shop_img_radius, seckill_subscript_bg_color, seckill_subscript_text_color } = new_style;
let location = `background: ${seckill_subscript_bg_color};color: ${seckill_subscript_text_color};`;
// 圆角根据图片的圆角来计算 对角线是同样的圆角
if (seckill_subscript_location == 'top-left') {
location += `top: 0;left: 0;border-radius: ${shop_img_radius.radius_top_left * 2}rpx 0 ${shop_img_radius.radius_top_left * 2}rpx 0;`;
} else if (seckill_subscript_location == 'top-right') {
location += `top: 0;right: 0;border-radius: 0 ${shop_img_radius.radius_top_right * 2}rpx 0 ${shop_img_radius.radius_top_right * 2}rpx;`;
} else if (seckill_subscript_location == 'bottom-left') {
location += `bottom: 0;left: 0;border-radius: 0 ${shop_img_radius.radius_bottom_left * 2}rpx 0 ${shop_img_radius.radius_bottom_left * 2}rpx;`;
} else if (seckill_subscript_location == 'bottom-right') {
location += `bottom: 0;right: 0;border-radius: ${shop_img_radius.radius_bottom_right * 2}rpx 0 ${shop_img_radius.radius_bottom_right * 2}rpx 0;`;
}
return location;
},
// 跳转链接
url_event(link) {
app.globalData.url_event(link);
},
},
};
</script>
<style scoped lang="scss">
.seckill-head {
padding: 30rpx 26rpx;
width: 100%;
height: 102rpx;
border-radius: 16rpx 16rpx 0 0;
box-sizing: border-box;
.seckill-title {
width: auto;
height: 42rpx;
}
.time-config {
padding: 2rpx 10rpx;
box-sizing: border-box;
line-height: 34rpx;
border-radius: 8rpx;
}
}
.seckill-head-icon {
width: 32rpx;
height: 32rpx;
}
.colon {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 33rpx;
top: -2rpx;
}
.slide-bottom {
height: 20rpx;
border-radius: 10rpx;
background: red;
}
.slide-top {
position: absolute;
height: 20rpx;
top: 0;
left: 0;
border-radius: 10rpx;
.slide-top-icon {
position: absolute;
top: -6rpx;
right: 0;
width: 32rpx;
height: 32rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
.original-price {
text-decoration-line: line-through;
}
.size-11 {
font-size: 22rpx;
}
</style>

View File

@@ -0,0 +1,231 @@
<template>
<view class="ou pr" :style="style_container + swiper_bg_style">
<view class="pa top-0 wh-auto ht-auto" :style="swiper_bg_img_style"></view>
<view class="ou wh-auto" :style="style_img_container + (!isEmpty(swiper_bg_img_style) ? swiper_bg_img_style_null : '')">
<componentDiyTabs :propKey="propKey" :propContentPadding="propContentPadding" :propValue="propValue" :propTop="propTop" :propIsRotatingBackground="is_rotating_background" :propBgStyle="swiper_bg_style" :propBgImgStyle="swiper_bg_img_style" :propStickyTop="propStickyTop" :propIsImmersionModel="propIsImmersionModel" :propNewIsTabsSafeDistance="new_is_tabs_safe_distance" :propNavIsTop="propNavIsTop" :propTabsIsTop="propTabsIsTop" :propIsCommon="false" :propsTabsContainer="tabs_container" :propsTabsImgContainer="tabs_img_container" :propSpacingCommonStyle="spacing_common_style" :propTabsSlidingFixedBg="tabs_sliding_fixed_bg" @onComputerHeight="tabs_height_event" @onTabsTap="tabs_click_event"></componentDiyTabs>
<view :style="carousel_margin_top">
<view :style="carousel_container">
<view :style="carousel_img_container">
<componentDiycarousel :propValue="propValue" :propIsCommon="false" @onVideoPlay="video_play" @slideChange="slideChange"></componentDiycarousel>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, padding_computer, isEmpty, gradient_computer, margin_computer, background_computer, radius_computer, old_margin, old_padding, old_radius, old_border_and_box_shadow, border_computer, box_shadow_computer, } from '@/common/js/common/common.js';
import componentDiyTabs from '@/components/diy/tabs';
import componentDiycarousel from '@/components/diy/carousel';
export default {
components: {
componentDiyTabs,
componentDiycarousel,
},
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propTop: {
type: [String, Number],
default: 0,
},
propStickyTop: {
type: Number,
default: 0,
},
// 是否导航栏置顶
propNavIsTop: {
type: Boolean,
default: true,
},
// 是否选项卡置顶
propTabsIsTop: {
type: Boolean,
default: false,
},
propKey: {
type: [String, Number],
default: '',
},
propContentPadding: {
type: String,
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
// 选项卡是否使用安全距离
propIsImmersionModel: {
type: Boolean,
default: false
}
},
data() {
return {
style_container: '',
style_img_container: '',
style_margin_container: '',
spacing_common_style: {
padding: 0,
padding_top: 0,
padding_bottom: 0,
padding_left: 0,
padding_right: 0,
margin: 0,
margin_top: 0,
margin_bottom: 0,
margin_left: 0,
margin_right: 0,
},
tabs_padding_style: '',
// 选项卡内容
tabs_container: '',
tabs_img_container: '',
tabs_sliding_fixed_bg: '',
// 轮播图内容
carousel_margin_top: '',
carousel_container: '',
carousel_img_container: '',
// top_up: '0',
actived_index: 0,
// 轮播图背景
swiper_bg_style: '',
swiper_bg_img_style: '',
swiper_bg_img_style_null: `background-image: url('')`,
is_rotating_background: false,
new_is_tabs_safe_distance: false,
};
},
created() {
this.init();
},
// 属性值改变监听
watch: {
// 数据
propTabsIsTop(value, old_value) {
this.init();
},
propKey(val) {
// 初始化
this.init();
},
},
methods: {
isEmpty,
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
const { tabs_bg_color_list = [], tabs_bg_direction = '', tabs_bg_background_img_style = '', tabs_bg_background_img = [], tabs_radius = old_radius, tabs_padding = old_padding, tabs_margin = old_margin, tabs_content = old_border_and_box_shadow, carousel_content_color_list = [], carousel_content_direction = '', carousel_content_background_img_style = '', carousel_content_background_img = [], carousel_content_margin = old_margin, carousel_content_padding = old_padding, carousel_content_radius = old_radius, carousel_content = old_border_and_box_shadow } = new_style;
// 选项卡背景设置
const tabs_data = {
color_list: tabs_bg_color_list,
direction: tabs_bg_direction,
background_img_style: tabs_bg_background_img_style,
background_img: tabs_bg_background_img,
}
// 商品区域背景设置
const carousel_content_data = {
color_list: carousel_content_color_list,
direction: carousel_content_direction,
background_img_style: carousel_content_background_img_style,
background_img: carousel_content_background_img,
}
// 头部的高度
const newPropTop = app.globalData.rpx_to_px(this.propTop);
// 选项卡的外边距
const new_tabs_top = app.globalData.rpx_to_px(tabs_margin?.margin_top || 0);
// 选项卡的实际外边距
const tabs_margin_top = new_content.is_tabs_safe_distance == '1' ? newPropTop + this.propStickyTop : 0;
// 选项卡的内边距处理
const new_padding_top = new_style.common_style.padding_top - newPropTop;
this.setData({
// style_container: `${common_styles_computer(common_style)};gap:${new_style.data_spacing * 2}rpx`,
style_container: `${common_styles_computer(new_style.common_style)};`,
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
carousel_margin_top: 'margin-top:' + new_style.data_spacing * 2 + 'rpx',
new_is_tabs_safe_distance: new_content.is_tabs_safe_distance == '1',
// 是否开启轮播图背景设置
is_rotating_background: new_content.rotating_background == '1',
tabs_sliding_fixed_bg: gradient_computer(tabs_data),
tabs_container: gradient_computer(tabs_data) + radius_computer(tabs_radius) + margin_computer(tabs_margin) + border_computer(tabs_content) + box_shadow_computer(tabs_content) + `overflow: hidden;margin-top: ${ new_tabs_top - tabs_margin_top }px;`,
tabs_img_container: background_computer(tabs_data) + padding_computer(tabs_padding) + `box-sizing: border-box;overflow: hidden;padding-top: ${ (tabs_padding?.padding_top || 0) + tabs_margin_top }px;`,
carousel_container: gradient_computer(carousel_content_data) + margin_computer(carousel_content_margin) + radius_computer(carousel_content_radius) + border_computer(carousel_content) + box_shadow_computer(carousel_content) + 'overflow: hidden;',
carousel_img_container: background_computer(carousel_content_data) + padding_computer(carousel_content_padding) + 'box-sizing: border-box;overflow: hidden;',
spacing_common_style: {
padding: 0,
padding_top: (this.propIsImmersionModel && new_content.is_tabs_safe_distance == '1' ? new_style.common_style.padding_top + this.propStickyTop : new_padding_top > 0 ? new_padding_top : 0),
padding_bottom: 0,
padding_left: new_style.common_style.padding_left,
padding_right: new_style.common_style.padding_right,
margin: 0,
margin_top: new_style.common_style.margin_top,
margin_left: new_style.common_style.margin_left,
margin_right: new_style.common_style.margin_right,
},
swiper_bg_style: this.get_swiper_bg_style(new_content, 0),
swiper_bg_img_style: this.get_swiper_bg_img_style(new_content, 0),
});
},
// tab点击
tabs_click_event(tabs_id, is_micro_page) {
this.$emit('onTabsTap', tabs_id, is_micro_page);
},
// tab高度
tabs_height_event(height) {
this.$emit('onComputerHeight', height);
},
// 视频播放
video_play(url, popup_width, popup_height) {
this.$emit('onVideoPlay', url, popup_width, popup_height);
},
get_swiper_bg_style(form, actived_index) {
const style = form?.carousel_list?.[actived_index]?.style;
if (style && !isEmpty(style.color_list)) {
const color_list = style.color_list;
const list = color_list.filter((item) => !isEmpty(item.color));
if (list.length > 0) {
try {
return gradient_computer(style);
} catch (error) {
return '';
}
}
return '';
}
return '';
},
get_swiper_bg_img_style(form, actived_index) {
const { carousel_img, style = {} } = form?.carousel_list[actived_index] || {};
// 如果是自定义的图片 判断图片是否存在
if (!isEmpty(carousel_img) && style?.background_type == 'carousel') {
// 如果是使用轮播图,判断轮播图是否存在
const data = {
background_img: carousel_img,
background_img_style: style?.background_img_style || '2',
}
return background_computer(data) + (style.is_background_img_blur == '1' ? `filter: blur(14px);opacity: 0.6;` : '');
} else if (!isEmpty(style?.background_img)) {
return background_computer(style) + (style.is_background_img_blur == '1' ? `filter: blur(14px);opacity: 0.6;` : '');
}
return '';
},
slideChange(index) {
this.setData({
actived_index: index,
swiper_bg_style: this.get_swiper_bg_style(this.propValue.content || {}, index),
swiper_bg_img_style: this.get_swiper_bg_img_style(this.propValue.content || {}, index),
});
},
},
};
</script>
<style scoped lang="scss"></style>

270
components/diy/tabs.vue Normal file
View File

@@ -0,0 +1,270 @@
<template>
<!-- 选项卡 -->
<view class="tabs-container pr" :style="tabs_z_index">
<view :class="top_up == '1' ? 'tabs-top' : ''" :style="tabs_top_style + (top_up == '1' ? propContentPadding : '')">
<view :style="style_margin_container">
<view class="tabs-contents bs-bb pr" :style="style_container">
<view :class="top_up == '1' ? 'bs-bb' : 'wh-auto bs-bb'" :style="style_img_container">
<componentDiyModulesTabsView :propKey="propKey" :propValue="tabs_data" :propIsTabsIcon="true" :propTop="propTop" :propsTabsContainer="propsTabsContainer + (propIsRotatingBackground ? propBgStyle : '')" :propIsRotatingBackground="propIsRotatingBackground" :propBgImgStyle="propBgImgStyle" :propsTabsImgContainer="propsTabsImgContainer" :propStyle="propStyle" :propTabsSlidingFixedBg="tabs_sliding_fixed_bg" @onTabsTap="tabs_click_event" @tabsZindex="tabsZindex"></componentDiyModulesTabsView>
</view>
</view>
</view>
</view>
<view v-if="top_up == '1'" class="tabs-seat" :style="'height:' + (propIsCommon ? tabs_seat_height : tabs_carousel_seat_height) + 'px;'"></view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, padding_computer, margin_computer, gradient_computer, background_computer } from '@/common/js/common/common.js';
import componentDiyModulesTabsView from '@/components/diy/modules/tabs-view';
// 状态栏高度
var bar_height = parseInt(app.globalData.get_system_info('statusBarHeight', 0));
// #ifdef MP-TOUTIAO
bar_height = 0;
// #endif
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
// 置顶距离顶部高度
propTop: {
type: [String, Number],
default: '0',
},
propStickyTop: {
type: Number,
default: 0,
},
// 是否导航栏置顶
propNavIsTop: {
type: Boolean,
default: true,
},
// 是否选项卡置顶
propTabsIsTop: {
type: Boolean,
default: false,
},
propIsCommon: {
type: Boolean,
default: true,
},
propSpacingCommonStyle: {
type: Object,
default: () => ({}),
},
// 样式
propStyle: {
type: String,
default: '',
},
propKey: {
type: [String, Number],
default: '',
},
propsTabsContainer: {
type: String,
default: ''
},
propsTabsImgContainer: {
type: String,
default: '',
},
propContentPadding: {
type: String,
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
propIsImmersionModel: {
type: Boolean,
default: false
},
propNewIsTabsSafeDistance: {
type: Boolean,
default: true
},
propTabsSlidingFixedBg: {
type: String,
default: ''
},
propIsRotatingBackground: {
type: Boolean,
default: false
},
propBgStyle: {
type: String,
default: ''
},
propBgImgStyle: {
type: String,
default: ''
}
},
components: {
componentDiyModulesTabsView,
},
data() {
return {
style_container: '',
style_img_container: '',
tabs_sliding_fixed_bg: '',
content: '',
tabs_data: {},
// 是否滑动置顶
top_up: '0',
// 置顶时,选项卡高度
tabs_seat_height: 0,
// 置顶时,轮播选项卡高度
tabs_carousel_seat_height: 0,
// 置顶时,选项卡样式
tabs_top_style: '',
style_margin_container: '',
tabs_z_index: ''
};
},
created() {
this.init();
},
mounted() {
setTimeout(() => {
this.get_tabs_height();
}, 100);
},
// 属性值改变监听
watch: {
// 数据
propTabsIsTop(value, old_value) {
this.init();
setTimeout(() => {
this.get_tabs_height();
}, 100);
},
propTop(val) {
this.init();
},
propStickyTop(val) {
this.init();
},
propKey(val) {
// 初始化
this.init();
},
propSpacingCommonStyle(val) {
// 初始化
this.init();
},
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
let new_tabs_data = JSON.parse(JSON.stringify(this.propValue));
new_tabs_data.content.tabs_list.unshift(new_tabs_data.content.home_data);
// 判断选项卡是否置顶
let other_style = this.propTop;
let new_tabs_top_style = this.propNavIsTop || this.propTabsIsTop || new_content.tabs_top_up == '1' ? (new_content.tabs_top_up == '1' ? 'top:calc(' + (this.propStickyTop > 0 ? this.propStickyTop + 'px + ' : '') + other_style * 2 + 'rpx);z-index:11;' : '') : '';
let new_top_up = new_content.tabs_top_up;
// #ifdef H5 || MP-TOUTIAO
// if (this.propTabsIsTop) {
// other_style = '0';
// }
new_tabs_top_style = 'top:calc(' + (this.propStickyTop > 0 ? this.propStickyTop + 'px + ' : '') + other_style * 2 + 'rpx);z-index:11;';
new_top_up = this.propNavIsTop || this.propTabsIsTop ? new_content.tabs_top_up : '0';
// #endif
let tabs_bg = new_style.common_style.color_list;
let new_tabs_background = '';
if (!Array.isArray(tabs_bg) || tabs_bg.length === 0 || !tabs_bg[0] || !tabs_bg[0].color) {
new_tabs_background = 'background:#fff;';
}
const newPropTop = this.propIsCommon ? (app.globalData.rpx_to_px(this.propTop) + this.propStickyTop) : (app.globalData.rpx_to_px(this.propTop + this.propStickyTop));
this.setData({
tabs_data: new_tabs_data,
tabs_sliding_fixed_bg: this.propIsCommon ? gradient_computer(new_style.common_style) : this.propTabsSlidingFixedBg,
style_container: this.propIsCommon ? new_tabs_background + common_styles_computer(new_style.common_style) : new_content.tabs_top_up == '1' ? new_tabs_background + gradient_computer(new_style.common_style) + margin_computer(this.propSpacingCommonStyle) : '', // 如果是选项卡轮播,不需要走默认样式
// 如果开了滑动置顶并且开了沉浸式不需要走传递过来的index否则的话就用传递过来的index
style_img_container: this.propIsCommon ? common_img_computer(new_style.common_style, this.propIndex) : new_content.tabs_top_up == '1' ? background_computer(new_style.common_style) + padding_computer(this.propSpacingCommonStyle, 1, false) + 'box-sizing: border-box;' : '', // 如果是选项卡轮播,不需要走默认样式
tabs_top_style: new_tabs_top_style,
// 沉浸模式下并且开通了安全距离 会显示-的margin
style_margin_container: this.propIsImmersionModel && this.propNewIsTabsSafeDistance ? `margin-top: -${ newPropTop }px;` : '',
// 判断是否置顶
top_up: new_top_up,
tabs_z_index: 'z-index: 11;'
});
},
// 获取选项卡高度
get_tabs_height() {
if (this.top_up == '1') {
// 选择我们想要的元素
const query = uni.createSelectorQuery();
query
.in(this)
.select('.tabs-top')
.boundingClientRect((res) => {
if ((res || null) != null) {
const newPropTop = app.globalData.rpx_to_px(this.propTop);
// data包含元素的宽度、高度等信息
this.setData({
tabs_seat_height: res.height + (this.propIsImmersionModel ? newPropTop + this.propStickyTop : 0),
tabs_carousel_seat_height: (res.height + (this.propIsImmersionModel ? newPropTop + this.propStickyTop : 0)) - this.propSpacingCommonStyle.padding_top - this.propSpacingCommonStyle.margin_top, // 轮播选项卡置顶时去掉顶部间距
});
this.$emit('onComputerHeight', this.tabs_seat_height);
}
})
.exec();
} else {
this.$emit('onComputerHeight', 0);
}
},
tabsZindex(val) {
this.setData({
tabs_z_index: `z-index: ${ val };`
});
},
// 选项卡回调
tabs_click_event(index, item) {
let tabs_id = '';
// 抽象出获取 tabs_id 的逻辑
tabs_id = this.get_tabs_id(item, index);
// 是否是商品分类页面
const is_micro_page = item.data_type == '0';
this.$emit('onTabsTap', tabs_id, is_micro_page);
},
// 获取 tabs_id
get_tabs_id(item, index) {
if (item.data_type === '0') {
return index !== 0 ? item.micro_page_list?.id : '';
} else {
return index !== 0 ? item.classify?.id : '';
}
},
},
};
</script>
<style lang="scss" scoped>
.tabs-container {
z-index: 11;
.tabs-top {
position: fixed;
left: 0;
right: 0;
max-width: 100%;
}
}
.tabs-contents {
max-width: 1600rpx !important;
}
@media only screen and (min-width: 1600rpx) {
.tabs-container .tabs-top {
left: calc(50% - 400px) !important;
}
}
</style>

143
components/diy/title.vue Normal file
View File

@@ -0,0 +1,143 @@
<template>
<view :style="style_container">
<view :style="style_img_container">
<view class="flex-col gap-10">
<view class="pr flex-row" :class="title_center">
<view class="z-i flex-row align-c gap-10">
<template v-if="!isEmpty(form.img_src) && !isEmpty(form.img_src[0].url)">
<view class="wh-auto" :style="{'height': new_style.img_height * 2 + 'rpx' }">
<image :src="form.img_src[0].url" class="ht-auto" mode="heightFix"></image>
</view>
</template>
<template v-else-if="!isEmpty(form.icon_class)">
<iconfont :name="'icon-' + form.icon_class" :size="new_style.icon_size * 2 + 'rpx'" :color="new_style.icon_color" propContainerDisplay="flex"></iconfont>
</template>
<view v-if="!isEmpty(form.title)" class="nowrap" :style="title_style" :data-value="!isEmpty(form.title_link) ? form.title_link.page : ''" @tap="url_event">{{ form.title }}</view>
<view v-if="!isEmpty(form.subtitle) && new_style.title_line == '1'" class="text-word-break nowrap" :style="subtitle_style">{{ form.subtitle }}</view>
</view>
<view class="flex-row gap-10 align-c right-0 pa">
<template v-if="form.keyword_show == '1'">
<view class="flex-row align-c" :style="keyword_gap">
<view v-for="item in keyword_list" :key="item.id" :style="keyword_style" :data-value="!isEmpty(item.link) ? item.link.page : ''" @tap="url_event">
{{ item.title }}
</view>
</view>
</template>
<view v-if="form.right_show == '1'" class="nowrap flex-row align-c" :style="right_style" :data-value="!isEmpty(form.right_link) ? form.right_link.page : ''" @tap="url_event"
>{{ form.right_title }}
<iconfont name="icon-arrow-right" :color="new_style.right_color" :size="new_style.right_size * 2 + 'rpx'" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
<view v-if="!isEmpty(form.subtitle) && new_style.title_line != '1'" class="text-word-break" :style="subtitle_style">{{ form.subtitle }}</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, isEmpty } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => {
return {};
},
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
form: {},
new_style: {},
style_container: '',
style_img_container: '',
title_center: '',
subtitle_style: '',
keyword_list: [],
keyword_style: '',
keyword_gap: '',
right_style: '',
right_size: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 判断是否为空
isEmpty,
// 初始化数据
init() {
const new_form = this.propValue.content;
const new_style = this.propValue.style;
const { keyword_color, keyword_size, right_color, right_size, common_style, title_weight, title_color, title_size, keyword_spacing = 10 } = new_style;
// 标题样式设置
let common_styles = '';
if (title_weight == 'italic') {
common_styles += `font-style: italic`;
} else if (['bold', '500'].includes(title_weight)) {
common_styles += `font-weight: bold`;
}
// 是否居中
this.setData({
form: new_form,
new_style: new_style,
title_center: new_form.is_title_center == '1' ? 'jc-c' : '',
keyword_list: new_form.keyword_list.filter((item) => item.is_show == '1'), // 关键字
keyword_style: `color:${keyword_color}; font-size: ${keyword_size * 2}rpx;`, // 关键字设置
keyword_gap: !isEmpty(keyword_spacing) ? `gap: ${ keyword_spacing * 2}rpx` : 'gap: 20rpx;', // 关键字间距设置
right_size: right_size * 2 + 'rpx', // 右边按钮设置
right_style: `color:${right_color}; font-size: ${right_size * 2}rpx;`, //右侧按钮样式
title_style: `color:${title_color}; font-size: ${title_size * 2}rpx; ${common_styles}`, // 标题样式设置
subtitle_style: this.get_subtitle_style(new_style), // 副标题样式设置
style_container: common_styles_computer(common_style), // 通用样式区
style_img_container: common_img_computer(common_style, this.propIndex), // 通用图片样式区
});
},
// 副标题样式设置
get_subtitle_style(new_style) {
let common_styles = '';
if (new_style.subtitle_weight == 'italic') {
common_styles += `font-style: italic`;
} else if (['bold', '500'].includes(new_style.subtitle_weight)) {
common_styles += `font-weight: bold;`;
}
return `color:${new_style.subtitle_color}; font-size: ${new_style.subtitle_size * 2}rpx; ${common_styles}`;
},
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style scoped lang="scss">
.right-0 {
top: 50%;
transform: translateY(-50%);
}
.break {
word-break: break-word;
overflow-wrap: break-word;
word-wrap: break-word;
}
</style>

View File

@@ -0,0 +1,164 @@
<template>
<!-- 用户信息 -->
<view :style="style_container">
<view :style="style_img_container">
<view class="pr padding-xxl" :style="style">
<view class="flex-row jc-sb align-c margin-bottom-xxl">
<view class="flex-1 flex-row align-c gap-12">
<image :src="(user_info.user || null) !== null ? user_info.user.avatar : user.avatar" class="circle" mode="widthFix" :style="'width:' + base_data.user_avatar_size * 2 + 'rpx;height:' + base_data.user_avatar_size * 2 + 'rpx;'" data-value="/pages/personal/personal" @tap="url_event" />
<view class="flex-col gap-8" data-value="/pages/personal/personal" @tap="url_event">
<view class="text-size fw-b" :style="user_name_style">{{ (user_info.user || null) !== null ? user_info.user.user_name_view : user.user_name_view }}</view>
<view v-if="id_bool && (user_info.user || null) !== null" class="padding-horizontal-sm padding-vertical-xsss border-radius-sm" :style="number_code_style">ID:{{ user_info.user.number_code }}</view>
</view>
</view>
<view class="flex-row align-c" :style="'gap:' + base_data.img_space * 2 + 'rpx;'">
<view v-for="(item, index) in icon_setting" :key="index" :style="{ width: base_data.img_size + 'px', height: base_data.img_size + 'px' }" :data-value="item.link.page || ''" @tap="url_event">
<image v-if="item.img.length > 0" :src="item.img[0].url" class="border-radius-sm" mode="scaleToFill" :style="{ width: base_data.img_size + 'px', height: base_data.img_size + 'px' }" />
<iconfont v-else :name="'icon-' + item.icon" :size="base_data.img_size * 2 + 'rpx'" color="#666" propContainerDisplay="flex"></iconfont>
</view>
</view>
</view>
<view class="flex-row jc-sa align-c">
<template v-for="(item, index) in stats_list">
<view v-if="config.includes(item.id)" :key="index" class="tc" :data-value="'/pages/' + item.url + '/' + item.url" @tap="url_event">
<view class="text-size fw-b margin-bottom-sm" :style="stats_number_style">{{ item.value }}</view>
<view class="text-size-xs" :style="stats_name_style">{{ item.name }}</view>
</view>
</template>
</view>
</view>
</view>
</view>
</template>
<script>
const app = getApp();
import { common_styles_computer, common_img_computer, gradient_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String,Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
id_bool: true,
stats_list: [
{ id: 'order_count', name: '订单总数', value: '100', url: 'user-order' },
{ id: 'goods_favor_count', name: '商品收藏', value: '10', url: 'user-favor' },
{ id: 'goods_browse_count', name: '我的足迹', value: '1000', url: 'user-goods-browse' },
{ id: 'integral_number', name: '我的积分', value: '10000', url: 'user-integral' },
],
config: ['order_count', 'goods_favor_count', 'goods_browse_count', 'integral_number'],
icon_setting: [
{ id: '1', img: [], icon: '', link: {} },
{ id: '2', img: [], icon: '', link: {} },
],
base_data: {},
// 样式
user_name_style: '',
number_code_style: '',
stats_name_style: '',
stats_number_style: '',
// 用户信息
user_info: {
user: null,
order_count: '0',
goods_favor_count: '0',
goods_browse_count: '0',
message_unread_count: '0',
integral_number: '0',
},
user: {
avatar: app.globalData.data.default_user_head_src,
user_name_view: '用户名',
number_code: '',
},
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
const temp_base_data = {
// 头像
user_avatar_size: new_style.user_avatar_size,
// 人物
user_name_color: new_style.user_name_color,
user_name_weight: new_style.user_name_weight,
user_name_size: new_style.user_name_size,
// id
number_code_color_list: new_style.number_code_color_list,
number_code_color: new_style.number_code_color,
number_code_direction: new_style.number_code_direction,
number_code_weight: new_style.number_code_weight,
number_code_size: new_style.number_code_size,
// 图标设置
img_size: new_style.img_size,
img_space: new_style.img_space,
stats_name_color: new_style.stats_name_color,
stats_name_weight: new_style.stats_name_weight,
stats_name_size: new_style.stats_name_size,
stats_number_color: new_style.stats_number_color,
stats_number_weight: new_style.stats_number_weight,
stats_number_size: new_style.stats_number_size,
};
// id样式
const new_gradient_obj = {
color_list: temp_base_data.number_code_color_list,
direction: temp_base_data.number_code_direction,
};
let temp_stats_list = this.stats_list;
temp_stats_list.map((item) => {
if (new_content.config.includes(item.id)) {
item.value = new_content.data[item.id];
}
});
this.setData({
user_info: new_content.data,
config: new_content.config,
icon_setting: new_content.icon_setting,
base_data: temp_base_data,
id_bool: new_content.config ? new_content.config.includes('number_code') : true,
stats_list: temp_stats_list,
// 人物名称样式
user_name_style: 'color:' + temp_base_data.user_name_color + ';' + 'font-size:' + temp_base_data.user_name_size * 2 + 'rpx;' + 'font-weight:' + temp_base_data.user_name_weight + ';',
number_code_style: gradient_computer(new_gradient_obj) + 'color:' + temp_base_data.number_code_color + ';' + 'font-size:' + temp_base_data.number_code_size * 2 + 'rpx;' + 'font-weight:' + temp_base_data.number_code_weight + ';',
// 统计名称样式
stats_name_style: 'color:' + temp_base_data.stats_name_color + ';' + 'font-size:' + temp_base_data.stats_name_size * 2 + 'rpx;' + 'font-weight:' + temp_base_data.stats_name_weight + ';',
stats_number_style: 'color:' + temp_base_data.stats_number_color + ';' + 'font-size:' + temp_base_data.stats_number_size * 2 + 'rpx;' + 'font-weight:' + temp_base_data.stats_number_weight + ';',
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
},
// 跳转链接
url_event(e) {
app.globalData.url_event(e);
},
},
};
</script>
<style></style>

86
components/diy/video.vue Normal file
View File

@@ -0,0 +1,86 @@
<template>
<!-- 视频 -->
<view :style="style_container">
<view :style="style_img_container">
<view class="video pr" :style="style">
<video :src="video" class="wh-auto ht-auto" :poster="video_img" objectFit="cover" style="object-fit: cover"></video>
</view>
</view>
</view>
</template>
<script>
import { common_styles_computer, common_img_computer } from '@/common/js/common/common.js';
export default {
props: {
propValue: {
type: Object,
default: () => ({}),
},
propKey: {
type: [String, Number],
default: '',
},
// 组件渲染的下标
propIndex: {
type: Number,
default: 1000000,
},
},
data() {
return {
style_container: '',
style_img_container: '',
style: '',
video_img: '',
video: '',
};
},
watch: {
propKey(val) {
// 初始化
this.init();
},
},
created() {
this.init();
},
methods: {
// 初始化数据
init() {
const new_content = this.propValue.content || {};
const new_style = this.propValue.style || {};
// 视频比例
this.get_video_height(new_content.video_ratio);
this.setData({
video_img: new_content.video_img.length > 0 ? new_content.video_img[0].url : '',
video: new_content.video.length > 0 ? new_content.video[0].url : '',
style_container: common_styles_computer(new_style.common_style),
style_img_container: common_img_computer(new_style.common_style, this.propIndex),
});
},
// 获取视频高度
get_video_height(data) {
uni.getSystemInfo({
success: (res) => {
let video_ratio = ``;
const width = res.windowWidth;
if (data == '4:3') {
video_ratio = `height: ${(((width * 3) / 4) * 2).toFixed(2)}rpx;`;
} else if (data == '1:1') {
video_ratio = `height: ${width * 2}rpx;`;
} else {
// 16:9 保留两位小数
video_ratio = `height: ${(((width * 9) / 16) * 2).toFixed(2)}rpx;`;
}
this.setData({
style: video_ratio,
});
},
});
},
},
};
</script>
<style></style>