mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-uniapp.git
synced 2026-11-06 17:09:01 +08:00
feat(uniapp): 首页楼层模板支持装修样式字段,修复 tpl_notice 样式缺少闭合括号
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="home-page" :style="themeStyle">
|
||||
<view class="home-page" :style="[themeStyle, pageBgStyle]">
|
||||
<view
|
||||
v-for="(item, index) in pageData.list"
|
||||
:key="`${item.type}-${index}`"
|
||||
@@ -105,6 +105,29 @@ const enableLoad = ref(false)
|
||||
const loading = ref(false)
|
||||
const searchBarWidth = ref(0)
|
||||
|
||||
// 页面设置-样式:画布背景(背景色 / 背景图 + 填充方式)
|
||||
const pageBgStyle = computed(() => {
|
||||
const data: any = pageData.value
|
||||
if (data?.bgType === 'image' && data?.bgImage) {
|
||||
const size =
|
||||
data.bgFill === 'contain'
|
||||
? 'contain'
|
||||
: data.bgFill === 'cover'
|
||||
? 'cover'
|
||||
: '100% 100%'
|
||||
return {
|
||||
backgroundImage: `url(${data.bgImage})`,
|
||||
backgroundSize: size,
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundColor: data.bgColor || '#f9f9f9',
|
||||
}
|
||||
}
|
||||
if (data?.bgColor) {
|
||||
return { backgroundColor: data.bgColor }
|
||||
}
|
||||
return {}
|
||||
})
|
||||
|
||||
const searchBarStyle = computed(() => {
|
||||
if (searchBarWidth.value) {
|
||||
return { width: `${searchBarWidth.value}px` }
|
||||
@@ -148,6 +171,10 @@ function init() {
|
||||
pageData.value = Array.isArray(result?.list)
|
||||
? { ...result, list: result.list.filter((i: any) => !i?.hidden) }
|
||||
: { list: [] }
|
||||
// 页面设置-内容:页面标题同步到导航栏
|
||||
if (result?.title) {
|
||||
uni.setNavigationBarTitle({ title: result.title })
|
||||
}
|
||||
const lastItem = pageData.value.list[pageData.value.list.length - 1]
|
||||
enableLoad.value =
|
||||
lastItem?.type === 'goods' || lastItem?.model === 'goods'
|
||||
|
||||
@@ -243,3 +243,35 @@ function seacnCode() {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** 广告魔方各布局共用的样式字段(装修面板配置;老数据无字段时返回空对象,保持原样) */
|
||||
export function cubeWrapperStyle(res) {
|
||||
if (!res || res.paddingTop === undefined) return {};
|
||||
const r = (v) => `${Number(v) || 0}rpx`;
|
||||
const bg =
|
||||
!res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`;
|
||||
return {
|
||||
padding: `${r(res.paddingTop)} ${r(res.paddingX)} ${r(res.paddingBottom)}`,
|
||||
background: bg,
|
||||
borderRadius: r(res.borderRadius),
|
||||
marginBottom: r(res.pageGap),
|
||||
overflow: Number(res.borderRadius) > 0 ? "hidden" : "visible",
|
||||
};
|
||||
}
|
||||
|
||||
/** 内容圆角(作用于单张图片容器) */
|
||||
export function cubeImgStyle(res) {
|
||||
if (!res || res.imgRadius === undefined) return {};
|
||||
return {
|
||||
borderRadius: `${Number(res.imgRadius) || 0}rpx`,
|
||||
overflow: "hidden",
|
||||
};
|
||||
}
|
||||
|
||||
/** 内容间距(作用于图片布局容器,flex gap) */
|
||||
export function cubeGapStyle(res) {
|
||||
if (!res || res.contentGap === undefined) return {};
|
||||
return { gap: `${Number(res.contentGap) || 0}rpx` };
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="tpl-article" :style="wrapperStyle" v-if="articleList.length">
|
||||
<view class="tpl-article" :class="{ styled: styled }" :style="wrapperStyle" v-if="articleList.length">
|
||||
<view class="article-title" v-if="title">{{ title }}</view>
|
||||
<view
|
||||
class="article-item"
|
||||
@@ -8,7 +8,7 @@
|
||||
@click="goDetail(item)"
|
||||
>
|
||||
<view class="article-text">
|
||||
<view class="article-name">{{ item.title }}</view>
|
||||
<view class="article-name" :style="nameStyle">{{ item.title }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -26,13 +26,38 @@ const title = computed(() => props.res?.list?.[0]?.title || "");
|
||||
const count = computed(() => props.res?.list?.[0]?.count || 3);
|
||||
const categoryId = computed(() => props.res?.list?.[0]?.categoryId || "");
|
||||
|
||||
const wrapperStyle = computed(() => ({
|
||||
background: props.res?.bgColor || "#FFFFFF",
|
||||
padding: `20rpx ${props.res?.paddingX ?? 0}rpx`,
|
||||
marginBottom: `${props.res?.pageGap ?? 10}rpx`,
|
||||
borderRadius: `${props.res?.borderRadius ?? 0}rpx`,
|
||||
overflow: "hidden",
|
||||
}));
|
||||
// 面板保存过样式字段才启用自定义样式,老数据保持原样
|
||||
const styled = computed(() => props.res && props.res.paddingTop !== undefined);
|
||||
const wrapperStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
if (styled.value) {
|
||||
const bg =
|
||||
!res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`;
|
||||
const radius = Number(res.borderRadius) || 0;
|
||||
return {
|
||||
background: bg,
|
||||
padding: `${Number(res.paddingTop) || 0}rpx ${Number(res.paddingX) || 0}rpx ${Number(res.paddingBottom) || 0}rpx`,
|
||||
marginBottom: `${Number(res.pageGap) || 0}rpx`,
|
||||
borderRadius: `${radius}rpx`,
|
||||
overflow: radius > 0 ? "hidden" : "visible",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
rowGap: `${Number(res.contentGap) || 0}rpx`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
background: res.bgColor || "#FFFFFF",
|
||||
padding: `20rpx ${res.paddingX ?? 0}rpx`,
|
||||
marginBottom: `${res.pageGap ?? 10}rpx`,
|
||||
borderRadius: `${res.borderRadius ?? 0}rpx`,
|
||||
overflow: "hidden",
|
||||
};
|
||||
});
|
||||
const nameStyle = computed(() =>
|
||||
styled.value && props.res.titleColor ? { color: props.res.titleColor } : {}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
const params: any = { pageNumber: 1, pageSize: count.value };
|
||||
@@ -61,6 +86,10 @@ function goDetail(item: any) {
|
||||
padding: 16rpx 24rpx;
|
||||
border-top: 2rpx solid #f5f5f5;
|
||||
}
|
||||
.tpl-article.styled .article-item {
|
||||
border-top: none;
|
||||
padding: 0;
|
||||
}
|
||||
.article-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
@@ -1,55 +1,190 @@
|
||||
<template>
|
||||
<view class="layout">
|
||||
<view class="banner-box">
|
||||
<u-swiper
|
||||
v-if="res && res.list && res.list.length"
|
||||
class="home-banner-swiper"
|
||||
:list="res.list"
|
||||
keyName="img"
|
||||
height="350rpx"
|
||||
imgMode="scaleToFill"
|
||||
interval="5000"
|
||||
duration="500"
|
||||
circular
|
||||
indicator
|
||||
@click="clickSwiper"
|
||||
>
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-swiper>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
function clickSwiper(index: number) {
|
||||
modelNavigateTo(props.res.list[index]);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
|
||||
.banner-box {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.home-banner-swiper),
|
||||
:deep(.u-swiper),
|
||||
:deep(.u-swiper__wrapper) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.u-swiper__wrapper__item),
|
||||
:deep(.u-swiper__wrapper__item__wrapper) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.u-swiper__wrapper__item__wrapper__image) {
|
||||
width: 100% !important;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<view class="layout" :style="wrapperStyle">
|
||||
<view class="banner-box" :style="cardMode ? {} : boxStyle">
|
||||
<u-swiper
|
||||
v-if="!cardMode && res && res.list && res.list.length"
|
||||
class="home-banner-swiper"
|
||||
:list="res.list"
|
||||
keyName="img"
|
||||
height="350rpx"
|
||||
imgMode="scaleToFill"
|
||||
interval="5000"
|
||||
duration="500"
|
||||
circular
|
||||
:indicator="!customIndicator"
|
||||
@change="onChange"
|
||||
@click="clickSwiper"
|
||||
>
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-swiper>
|
||||
<swiper
|
||||
v-if="cardMode && res && res.list && res.list.length"
|
||||
class="card-swiper"
|
||||
autoplay
|
||||
:interval="5000"
|
||||
:duration="500"
|
||||
circular
|
||||
previous-margin="68rpx"
|
||||
next-margin="68rpx"
|
||||
@change="onChange"
|
||||
>
|
||||
<swiper-item v-for="(item, i) in res.list" :key="i">
|
||||
<view class="card-item" :class="{ active: i === current }" @click="clickSwiper(i)">
|
||||
<image class="card-img" :style="boxStyle" :src="item.img" mode="scaleToFill" />
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<view
|
||||
v-if="customIndicator && res.indicatorStyle !== 'none' && res.list.length > 1"
|
||||
class="indicator"
|
||||
:class="'pos-' + (res.indicatorPosition || 'center')"
|
||||
>
|
||||
<view
|
||||
v-for="(item, i) in res.list"
|
||||
:key="i"
|
||||
class="dot"
|
||||
:class="[res.indicatorStyle || 'round', { active: i === current }]"
|
||||
:style="i === current ? { background: indicatorColor } : {}"
|
||||
></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from "vue";
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const current = ref(0);
|
||||
|
||||
// 装修面板保存过样式字段才启用自定义样式/指示器,老数据保持原样
|
||||
const customIndicator = computed(() => props.res && props.res.indicatorStyle !== undefined);
|
||||
|
||||
const cardMode = computed(() => props.res && props.res.swiperStyle === "card");
|
||||
|
||||
const indicatorColor = computed(() =>
|
||||
props.res.indicatorColorMode === "custom" && props.res.indicatorColor
|
||||
? props.res.indicatorColor
|
||||
: "var(--theme-primary, #ff3c2a)"
|
||||
);
|
||||
|
||||
const toRpx = (val: any) => `${Number(val) || 0}rpx`;
|
||||
|
||||
const wrapperStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
if (res.paddingTop === undefined) return {};
|
||||
const bg =
|
||||
!res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`;
|
||||
return {
|
||||
padding: `${toRpx(res.paddingTop)} ${toRpx(res.paddingX)} ${toRpx(res.paddingBottom)}`,
|
||||
background: bg,
|
||||
borderRadius: toRpx(res.borderRadius),
|
||||
marginBottom: toRpx(res.pageGap),
|
||||
};
|
||||
});
|
||||
|
||||
const boxStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
if (res.imgRadius === undefined) return {};
|
||||
return { borderRadius: toRpx(res.imgRadius) };
|
||||
});
|
||||
|
||||
function onChange(e: any) {
|
||||
current.value = typeof e === "number" ? e : e.current;
|
||||
}
|
||||
|
||||
function clickSwiper(index: number) {
|
||||
modelNavigateTo(props.res.list[index]);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
|
||||
.banner-box {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card-swiper {
|
||||
width: 100%;
|
||||
height: 350rpx;
|
||||
}
|
||||
|
||||
.card-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 8rpx;
|
||||
box-sizing: border-box;
|
||||
transform: scale(0.92);
|
||||
transition: transform 0.4s ease;
|
||||
}
|
||||
|
||||
.card-item.active {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.card-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
position: absolute;
|
||||
bottom: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
z-index: 2;
|
||||
|
||||
&.pos-left {
|
||||
left: 24rpx;
|
||||
}
|
||||
&.pos-center {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
&.pos-right {
|
||||
right: 24rpx;
|
||||
}
|
||||
|
||||
.dot {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
transition: background 0.3s ease;
|
||||
|
||||
&.round {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-radius: 50%;
|
||||
margin: 0 6rpx;
|
||||
}
|
||||
&.line {
|
||||
width: 24rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 3rpx;
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.home-banner-swiper),
|
||||
:deep(.u-swiper),
|
||||
:deep(.u-swiper__wrapper) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.u-swiper__wrapper__item),
|
||||
:deep(.u-swiper__wrapper__item__wrapper) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.u-swiper__wrapper__item__wrapper__image) {
|
||||
width: 100% !important;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,9 +7,18 @@ import { computed } from "vue";
|
||||
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const blankStyle = computed(() => ({
|
||||
background: props.res?.bgColor || "#f5f5f5",
|
||||
height: `${props.res?.height ?? 10}rpx`,
|
||||
margin: `0 ${props.res?.paddingX ?? 0}rpx ${props.res?.pageGap ?? 10}rpx`,
|
||||
}));
|
||||
const blankStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
const bg =
|
||||
res.bgColor1 !== undefined
|
||||
? !res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`
|
||||
: res.bgColor || "#f5f5f5";
|
||||
return {
|
||||
background: bg,
|
||||
height: `${res.height ?? 10}rpx`,
|
||||
margin: `0 ${res.paddingX ?? 0}rpx ${res.pageGap ?? 10}rpx`,
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,32 +1,163 @@
|
||||
<template>
|
||||
<view class="tpl-coupon" :style="wrapperStyle" v-if="couponList.length">
|
||||
<view class="tpl-coupon" :class="{ styled: styled }" :style="wrapperStyle" v-if="couponList.length || !styled">
|
||||
<view class="coupon-title" v-if="title">{{ title }}</view>
|
||||
<view
|
||||
class="coupon-item"
|
||||
v-for="item in couponList"
|
||||
:key="item.id"
|
||||
@click="receive(item)"
|
||||
|
||||
<!-- 老数据保持原样 -->
|
||||
<block v-if="!styled">
|
||||
<view
|
||||
class="coupon-item"
|
||||
v-for="item in couponList"
|
||||
:key="item.id"
|
||||
@click="receive(item)"
|
||||
>
|
||||
<view class="coupon-left">
|
||||
<view class="coupon-amount">
|
||||
<template v-if="item.couponType == 'DISCOUNT'">
|
||||
<text class="coupon-value">{{ item.couponDiscount }}</text>
|
||||
<text class="coupon-symbol">折</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="coupon-symbol">¥</text>
|
||||
<text class="coupon-value">{{ unitPrice(item.price) }}</text>
|
||||
</template>
|
||||
</view>
|
||||
<view class="coupon-threshold">满{{ unitPrice(item.consumeThreshold) }}元可用</view>
|
||||
</view>
|
||||
<view class="coupon-right">
|
||||
<view class="coupon-name">
|
||||
{{ item.storeName == 'platform' ? '全平台通用' : item.storeName + '店铺' }}
|
||||
</view>
|
||||
<view class="coupon-btn">立即领取</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<!-- 样式一:红色大卡横滑,右侧竖排领取 -->
|
||||
<scroll-view
|
||||
v-else-if="layoutStyle === '1'"
|
||||
scroll-x
|
||||
class="cp-scroll"
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view class="coupon-left">
|
||||
<view class="coupon-amount">
|
||||
<template v-if="item.couponType == 'DISCOUNT'">
|
||||
<text class="coupon-value">{{ item.couponDiscount }}</text>
|
||||
<text class="coupon-symbol">折</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="coupon-symbol">¥</text>
|
||||
<text class="coupon-value">{{ unitPrice(item.price) }}</text>
|
||||
</template>
|
||||
<view
|
||||
class="cp1-item"
|
||||
v-for="item in couponList"
|
||||
:key="item.id"
|
||||
@click="receive(item)"
|
||||
>
|
||||
<view class="cp1-left">
|
||||
<view class="cp1-amount">
|
||||
<template v-if="item.couponType == 'DISCOUNT'">
|
||||
<text class="cp1-value">{{ item.couponDiscount }}</text>
|
||||
<text class="cp1-symbol">折</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="cp1-symbol">¥</text>
|
||||
<text class="cp1-value">{{ unitPrice(item.price) }}</text>
|
||||
</template>
|
||||
</view>
|
||||
<view class="cp1-threshold">{{ thresholdText(item) }}</view>
|
||||
</view>
|
||||
<view class="coupon-threshold">满{{ unitPrice(item.consumeThreshold) }}元可用</view>
|
||||
</view>
|
||||
<view class="coupon-right">
|
||||
<view class="coupon-name">
|
||||
{{ item.storeName == 'platform' ? '全平台通用' : item.storeName + '店铺' }}
|
||||
<view class="cp1-right">
|
||||
<text>领</text><text>取</text>
|
||||
</view>
|
||||
<view class="coupon-btn">立即领取</view>
|
||||
<view class="cp-notch cp-notch-t"></view>
|
||||
<view class="cp-notch cp-notch-b"></view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 样式二:白底小卡,底部去领取按钮 -->
|
||||
<scroll-view
|
||||
v-else-if="layoutStyle === '2'"
|
||||
scroll-x
|
||||
class="cp-scroll"
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view
|
||||
class="cp2-item"
|
||||
v-for="item in couponList"
|
||||
:key="item.id"
|
||||
@click="receive(item)"
|
||||
>
|
||||
<view class="cp2-top">
|
||||
<view class="cp2-amount">
|
||||
<template v-if="item.couponType == 'DISCOUNT'">
|
||||
<text class="cp2-value">{{ item.couponDiscount }}</text>
|
||||
<text class="cp2-symbol">折</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="cp2-symbol">¥</text>
|
||||
<text class="cp2-value">{{ unitPrice(item.price) }}</text>
|
||||
</template>
|
||||
</view>
|
||||
<view class="cp2-threshold">{{ thresholdText(item) }}</view>
|
||||
</view>
|
||||
<view class="cp2-btn">去领取</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 样式三:浅粉虚线卡,右侧竖排立即领取 -->
|
||||
<scroll-view
|
||||
v-else-if="layoutStyle === '3'"
|
||||
scroll-x
|
||||
class="cp-scroll"
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<view
|
||||
class="cp3-item"
|
||||
v-for="item in couponList"
|
||||
:key="item.id"
|
||||
@click="receive(item)"
|
||||
>
|
||||
<view class="cp3-left">
|
||||
<view class="cp3-amount">
|
||||
<template v-if="item.couponType == 'DISCOUNT'">
|
||||
<text class="cp3-value">{{ item.couponDiscount }}</text>
|
||||
<text class="cp3-symbol">折</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="cp3-symbol">¥</text>
|
||||
<text class="cp3-value">{{ unitPrice(item.price) }}</text>
|
||||
</template>
|
||||
</view>
|
||||
<view class="cp3-threshold">{{ thresholdText(item) }}</view>
|
||||
</view>
|
||||
<view class="cp3-right">
|
||||
<text>立</text><text>即</text><text>领</text><text>取</text>
|
||||
</view>
|
||||
<view class="cp-notch cp-notch-t"></view>
|
||||
<view class="cp-notch cp-notch-b"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 样式四:浅粉渐变 + 右侧渐变竖条领取 -->
|
||||
<scroll-view v-else scroll-x class="cp-scroll" :show-scrollbar="false">
|
||||
<view
|
||||
class="cp4-item"
|
||||
v-for="item in couponList"
|
||||
:key="item.id"
|
||||
@click="receive(item)"
|
||||
>
|
||||
<view class="cp4-left">
|
||||
<view class="cp4-amount">
|
||||
<template v-if="item.couponType == 'DISCOUNT'">
|
||||
<text class="cp4-value">{{ item.couponDiscount }}</text>
|
||||
<text class="cp4-symbol">折</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<text class="cp4-symbol">¥</text>
|
||||
<text class="cp4-value">{{ unitPrice(item.price) }}</text>
|
||||
</template>
|
||||
</view>
|
||||
<view class="cp4-threshold">{{ thresholdText(item) }}</view>
|
||||
</view>
|
||||
<view class="cp4-right">
|
||||
<text>领</text><text>取</text>
|
||||
</view>
|
||||
<view class="cp-notch cp-notch-t"></view>
|
||||
<view class="cp-notch cp-notch-b"></view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -41,15 +172,35 @@ const props = defineProps<{ res: any }>();
|
||||
const couponList = ref<any[]>([]);
|
||||
|
||||
const title = computed(() => props.res?.list?.[0]?.title || "");
|
||||
const count = computed(() => props.res?.list?.[0]?.count || 3);
|
||||
const count = computed(() => props.res?.list?.[0]?.count || 5);
|
||||
const layoutStyle = computed(() => (props.res && props.res.layoutStyle) || "1");
|
||||
|
||||
const wrapperStyle = computed(() => ({
|
||||
background: props.res?.bgColor || "#FFFFFF",
|
||||
padding: `20rpx ${props.res?.paddingX ?? 0}rpx`,
|
||||
marginBottom: `${props.res?.pageGap ?? 10}rpx`,
|
||||
borderRadius: `${props.res?.borderRadius ?? 0}rpx`,
|
||||
overflow: "hidden",
|
||||
}));
|
||||
// 面板保存过样式字段才启用自定义样式,老数据保持原样
|
||||
const styled = computed(() => props.res && props.res.paddingTop !== undefined);
|
||||
const wrapperStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
if (styled.value) {
|
||||
const bg =
|
||||
!res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`;
|
||||
const radius = Number(res.borderRadius) || 0;
|
||||
return {
|
||||
background: bg,
|
||||
padding: `${Number(res.paddingTop) || 0}rpx ${Number(res.paddingX) || 0}rpx ${Number(res.paddingBottom) || 0}rpx`,
|
||||
marginBottom: `${Number(res.pageGap) || 0}rpx`,
|
||||
borderRadius: `${radius}rpx`,
|
||||
overflow: "hidden",
|
||||
};
|
||||
}
|
||||
return {
|
||||
background: res.bgColor || "#FFFFFF",
|
||||
padding: `20rpx ${res.paddingX ?? 0}rpx`,
|
||||
marginBottom: `${res.pageGap ?? 10}rpx`,
|
||||
borderRadius: `${res.borderRadius ?? 0}rpx`,
|
||||
overflow: "hidden",
|
||||
};
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getAllCoupons({ pageNumber: 1, pageSize: count.value }).then((res) => {
|
||||
@@ -59,6 +210,11 @@ onMounted(() => {
|
||||
});
|
||||
});
|
||||
|
||||
function thresholdText(item: any) {
|
||||
const t = Number(unitPrice(item.consumeThreshold));
|
||||
return t > 0 ? `满${unitPrice(item.consumeThreshold)}元可用` : "无门槛使用";
|
||||
}
|
||||
|
||||
function receive(item: any) {
|
||||
if (!isLogin("auth")) {
|
||||
navigateToLogin("redirectTo");
|
||||
@@ -90,6 +246,9 @@ function receive(item: any) {
|
||||
overflow: hidden;
|
||||
background: linear-gradient(90deg, #ffece8, #fff5f4);
|
||||
}
|
||||
.tpl-coupon.styled .coupon-item {
|
||||
margin: 0;
|
||||
}
|
||||
.coupon-left {
|
||||
width: 180rpx;
|
||||
padding: 24rpx 0;
|
||||
@@ -124,4 +283,214 @@ function receive(item: any) {
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
/* 参考 CRMEB 优惠券组件 */
|
||||
.tpl-coupon.styled .coupon-title {
|
||||
padding: 0 0 16rpx;
|
||||
}
|
||||
.cp-scroll {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.cp-notch {
|
||||
position: absolute;
|
||||
right: 60rpx;
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
}
|
||||
.cp-notch-t {
|
||||
top: -8rpx;
|
||||
}
|
||||
.cp-notch-b {
|
||||
bottom: -8rpx;
|
||||
}
|
||||
|
||||
/* 样式一:红色大卡 */
|
||||
.cp1-item {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
width: 264rpx;
|
||||
height: 132rpx;
|
||||
margin-right: 16rpx;
|
||||
border-radius: 12rpx;
|
||||
background: linear-gradient(135deg, #ff5a36, #e1251b);
|
||||
color: #fff;
|
||||
vertical-align: top;
|
||||
}
|
||||
.cp1-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding-left: 24rpx;
|
||||
}
|
||||
.cp1-value {
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.cp1-symbol {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
.cp1-threshold {
|
||||
font-size: 20rpx;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
.cp1-right {
|
||||
width: 60rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24rpx;
|
||||
border-left: 2rpx dashed rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
.cp1-item .cp-notch {
|
||||
right: 52rpx;
|
||||
}
|
||||
|
||||
/* 样式二:白底小卡 */
|
||||
.cp2-item {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
width: 148rpx;
|
||||
margin-right: 16rpx;
|
||||
border-radius: 12rpx;
|
||||
background: #fff;
|
||||
border-left: 4rpx solid #f5483a;
|
||||
border-right: 4rpx solid #f5483a;
|
||||
overflow: hidden;
|
||||
vertical-align: top;
|
||||
}
|
||||
.cp2-top {
|
||||
padding: 16rpx 0 12rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.cp2-value {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #e1251b;
|
||||
}
|
||||
.cp2-symbol {
|
||||
font-size: 20rpx;
|
||||
color: #e1251b;
|
||||
}
|
||||
.cp2-threshold {
|
||||
font-size: 18rpx;
|
||||
color: #999;
|
||||
margin-top: 2rpx;
|
||||
}
|
||||
.cp2-btn {
|
||||
background: linear-gradient(90deg, #ff6a3d, #e1251b);
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
text-align: center;
|
||||
padding: 8rpx 0;
|
||||
}
|
||||
|
||||
/* 样式三:浅粉虚线卡 */
|
||||
.cp3-item {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
width: 276rpx;
|
||||
height: 120rpx;
|
||||
margin-right: 16rpx;
|
||||
border-radius: 12rpx;
|
||||
background: #fdf1ec;
|
||||
border: 2rpx dashed #f5483a;
|
||||
vertical-align: top;
|
||||
}
|
||||
.cp3-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding-left: 24rpx;
|
||||
}
|
||||
.cp3-value {
|
||||
font-size: 38rpx;
|
||||
font-weight: 700;
|
||||
color: #e1251b;
|
||||
}
|
||||
.cp3-symbol {
|
||||
font-size: 20rpx;
|
||||
color: #e1251b;
|
||||
}
|
||||
.cp3-threshold {
|
||||
font-size: 20rpx;
|
||||
color: #e1251b;
|
||||
margin-top: 2rpx;
|
||||
}
|
||||
.cp3-right {
|
||||
width: 64rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22rpx;
|
||||
color: #e1251b;
|
||||
border-left: 2rpx dashed #f5483a;
|
||||
}
|
||||
.cp3-item .cp-notch {
|
||||
right: 56rpx;
|
||||
}
|
||||
|
||||
/* 样式四:浅粉渐变 + 渐变竖条 */
|
||||
.cp4-item {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
width: 232rpx;
|
||||
height: 108rpx;
|
||||
margin-right: 16rpx;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
vertical-align: top;
|
||||
}
|
||||
.cp4-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
padding-left: 20rpx;
|
||||
background: linear-gradient(135deg, #fff3ec, #ffe0d3);
|
||||
}
|
||||
.cp4-value {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #e1251b;
|
||||
}
|
||||
.cp4-symbol {
|
||||
font-size: 20rpx;
|
||||
color: #e1251b;
|
||||
}
|
||||
.cp4-threshold {
|
||||
font-size: 18rpx;
|
||||
color: #e1251b;
|
||||
margin-top: 2rpx;
|
||||
}
|
||||
.cp4-right {
|
||||
width: 52rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22rpx;
|
||||
color: #fff;
|
||||
background: linear-gradient(180deg, #ff6a3d, #e1251b);
|
||||
}
|
||||
.cp4-item .cp-notch {
|
||||
right: 44rpx;
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
}
|
||||
.cp4-item .cp-notch-t {
|
||||
top: -6rpx;
|
||||
}
|
||||
.cp4-item .cp-notch-b {
|
||||
bottom: -6rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<u-image width="140rpx" mode="aspectFit" height="140rpx" @click="modelNavigateTo(item)" class="image-mode" v-for="(item,index) in res.list" :key="index" :src="item.img" alt="">
|
||||
<div class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<u-image width="100%" mode="aspectFit" height="140rpx" @click="modelNavigateTo(item)" class="image-mode flex-img" :style="imgStyle" v-for="(item,index) in res.list" :key="index" :src="item.img" alt="">
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
import { computed } from "vue";
|
||||
import { modelNavigateTo, cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./tpl";
|
||||
|
||||
defineProps<{ res: any }>();
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const wrapperStyle = computed(() => cubeWrapperStyle(props.res));
|
||||
const imgStyle = computed(() => cubeImgStyle(props.res));
|
||||
const gapStyle = computed(() => cubeGapStyle(props.res));
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
@@ -20,4 +25,8 @@ defineProps<{ res: any }>();
|
||||
justify-content: center;
|
||||
background-size: cover;
|
||||
}
|
||||
</style>
|
||||
.flex-img {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,24 +1,33 @@
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<u-image height="175rpx" mode="aspectFit" width="175rpx" @click="modelNavigateTo(item)" class="image-mode" :src="item.img" v-for="(item,index) in res.list" :key="index">
|
||||
<div class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<u-image height="175rpx" mode="aspectFit" width="100%" @click="modelNavigateTo(item)" class="image-mode flex-img" :style="imgStyle" :src="item.img" v-for="(item,index) in res.list" :key="index">
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
import { computed } from "vue";
|
||||
import { modelNavigateTo, cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./tpl";
|
||||
|
||||
defineProps<{ res: any }>();
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const wrapperStyle = computed(() => cubeWrapperStyle(props.res));
|
||||
const imgStyle = computed(() => cubeImgStyle(props.res));
|
||||
const gapStyle = computed(() => cubeGapStyle(props.res));
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
.layout {
|
||||
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-size: cover;
|
||||
}
|
||||
</style>
|
||||
.flex-img {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<u-image @click="modelNavigateTo(item)" height="240rpx" width="240rpx" class="image-mode" :src="item.img" v-for="(item, index) in res.list" :key="index">
|
||||
<div class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<u-image @click="modelNavigateTo(item)" height="240rpx" width="100%" class="image-mode flex-img" :style="imgStyle" :src="item.img" v-for="(item, index) in res.list" :key="index">
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
import { computed } from "vue";
|
||||
import { modelNavigateTo, cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./tpl";
|
||||
|
||||
defineProps<{ res: any }>();
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const wrapperStyle = computed(() => cubeWrapperStyle(props.res));
|
||||
const imgStyle = computed(() => cubeImgStyle(props.res));
|
||||
const gapStyle = computed(() => cubeGapStyle(props.res));
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
@@ -22,4 +27,8 @@ defineProps<{ res: any }>();
|
||||
justify-content: center;
|
||||
background-size: cover;
|
||||
}
|
||||
</style>
|
||||
.flex-img {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<view class="layout">
|
||||
<view class="flex-two">
|
||||
<view class="flex-item" @click="modelNavigateTo(res.list[0])">
|
||||
<view class="layout" :style="wrapperStyle">
|
||||
<view class="flex-two" :style="gapStyle">
|
||||
<view class="flex-item" :style="imgStyle" @click="modelNavigateTo(res.list[0])">
|
||||
<u-image height="250rpx" width="100%" mode="scaleToFill" :src="res.list[0].img" alt>
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</view>
|
||||
<view class="flex-item" @click="modelNavigateTo(res.list[1])">
|
||||
<view class="flex-item" :style="imgStyle" @click="modelNavigateTo(res.list[1])">
|
||||
<u-image height="250rpx" width="100%" mode="scaleToFill" :src="res.list[1].img" alt>
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
@@ -15,9 +15,14 @@
|
||||
</view>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
import { computed } from "vue";
|
||||
import { modelNavigateTo, cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./tpl";
|
||||
|
||||
defineProps<{ res: any }>();
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const wrapperStyle = computed(() => cubeWrapperStyle(props.res));
|
||||
const imgStyle = computed(() => cubeImgStyle(props.res));
|
||||
const gapStyle = computed(() => cubeGapStyle(props.res));
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
@@ -27,6 +32,7 @@ defineProps<{ res: any }>();
|
||||
overflow: hidden;
|
||||
}
|
||||
.flex-item {
|
||||
width: 50%;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,265 +1,678 @@
|
||||
<template>
|
||||
<view class="layout">
|
||||
<u-sticky>
|
||||
<view class="goods-cell-title">
|
||||
<view
|
||||
class="goods-item-title"
|
||||
:class="{ 'selected-title': selected.index == index }"
|
||||
@click="handleClickTitle(title, index)"
|
||||
v-for="(title, index) in res.list[0].titleWay"
|
||||
:key="index"
|
||||
>
|
||||
<h4 class="h4">{{ title.title }}</h4>
|
||||
<view class="title-desc">{{ title.desc }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-sticky>
|
||||
<view class="goods-list">
|
||||
<template v-for="(item, item_index) in res.list[0].listWay" :key="item_index">
|
||||
<view
|
||||
v-if="
|
||||
item.___index != undefined
|
||||
? selected.index == item.___index
|
||||
: selected.val == item.type
|
||||
"
|
||||
@click="handleClick(item)"
|
||||
class="goods-item"
|
||||
>
|
||||
<view class="goods-img">
|
||||
<u-image
|
||||
:src="item.img"
|
||||
height="350rpx"
|
||||
mode="aspectFit"
|
||||
width="100%"
|
||||
>
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</view>
|
||||
<view class="goods-desc">
|
||||
<view class="goods-title">
|
||||
{{ item.title }}
|
||||
</view>
|
||||
<view class="goods-bottom">
|
||||
<view class="goods-price">
|
||||
¥<span class="price-int"
|
||||
>{{ goodsFormatPrice(item.price)[0] }} </span
|
||||
>.{{ goodsFormatPrice(item.price)[1] }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template
|
||||
v-if="currentTitleWay?.bindCategory && goodsData.length"
|
||||
>
|
||||
<view
|
||||
v-for="(item, index) in goodsData"
|
||||
:key="index"
|
||||
class="goods-item"
|
||||
@click="handleClick(item)"
|
||||
>
|
||||
<view class="goods-img">
|
||||
<u-image
|
||||
:src="item.thumbnail"
|
||||
height="350rpx"
|
||||
mode="aspectFit"
|
||||
width="100%"
|
||||
>
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</view>
|
||||
<view class="goods-desc">
|
||||
<view class="goods-title">
|
||||
{{ item.goodsName }}
|
||||
</view>
|
||||
<view class="goods-bottom">
|
||||
<view class="goods-price">
|
||||
¥<span class="price-int"
|
||||
>{{ goodsFormatPrice(item.price)[0] }} </span
|
||||
>.{{ goodsFormatPrice(item.price)[1] }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch, onMounted, onUnmounted, computed } from "vue";
|
||||
import { getGoodsList } from "@/api/goods.js";
|
||||
import { goodsFormatPrice } from "@/utils/filters.js";
|
||||
|
||||
const props = defineProps<{ res: any; enableBottomLoad?: boolean }>();
|
||||
|
||||
const selected = reactive({
|
||||
index: 0,
|
||||
val: "",
|
||||
});
|
||||
|
||||
const currentTitleWay = computed(() => {
|
||||
return props.res?.list?.[0]?.titleWay?.[selected.index]
|
||||
})
|
||||
const params = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 100,
|
||||
categoryId: "",
|
||||
});
|
||||
const goodsData = ref<any[]>([]);
|
||||
const goodsResult = ref<any>("");
|
||||
|
||||
watch(
|
||||
() => props.res,
|
||||
(val) => {
|
||||
if (!val?.list?.[0]) return
|
||||
const firstListWay = val.list[0].listWay?.[0]
|
||||
selected.val = firstListWay?.type || ''
|
||||
const firstTitleWay = val.list[0].titleWay?.[0]
|
||||
if (firstTitleWay?.bindCategory) {
|
||||
initGoods(firstTitleWay)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
uni.$on("onReachBottom", () => {
|
||||
if (
|
||||
props.enableBottomLoad &&
|
||||
goodsResult.value.totalElements >=
|
||||
params.pageNumber * params.pageSize
|
||||
) {
|
||||
params.pageNumber++;
|
||||
initGoods(props.res.list[0].titleWay[selected.index]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
uni.$off("onReachBottom");
|
||||
});
|
||||
|
||||
function handleClick(item: any) {
|
||||
if (!item?.id || !item?.goodsId) return
|
||||
uni.navigateTo({
|
||||
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
|
||||
})
|
||||
}
|
||||
|
||||
async function initGoods(val: any) {
|
||||
if (props.enableBottomLoad) params.pageSize = 20;
|
||||
val ? (params.categoryId = val.bindCategory.id) : "";
|
||||
const res = await getGoodsList(params);
|
||||
if (res.data.success) {
|
||||
goodsResult.value = res.data.result;
|
||||
const result = res.data.result.records;
|
||||
goodsData.value.push(...result);
|
||||
console.log(goodsData.value);
|
||||
}
|
||||
}
|
||||
|
||||
function handleClickTitle(val: any, index: number) {
|
||||
selected.index = index;
|
||||
selected.val = val.title;
|
||||
if (val.bindCategory) {
|
||||
params.pageNumber = 1;
|
||||
goodsData.value = [];
|
||||
initGoods(val);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
$w_94: 94%;
|
||||
|
||||
.layout {
|
||||
padding: 8px 0;
|
||||
background: #f9f9f9;
|
||||
}
|
||||
|
||||
.selected-title {
|
||||
.h4 {
|
||||
font-size: 30rpx;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.title-desc {
|
||||
font-weight: bold;
|
||||
color: $main-color !important;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-cell-title {
|
||||
background: #f9f9f9;
|
||||
padding: 10px;
|
||||
transition: 0.35s;
|
||||
display: flex;
|
||||
|
||||
> .goods-item-title {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
|
||||
.h4 {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.title-desc {
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods-list {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.goods-item {
|
||||
width: 50%;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 0.4em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.goods-img {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
// width: 158px;
|
||||
width: $w_94;
|
||||
border-top-left-radius: 20rpx;
|
||||
border-top-right-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.goods-desc {
|
||||
border-bottom-left-radius: 20rpx;
|
||||
border-bottom-right-radius: 20rpx;
|
||||
width: $w_94;
|
||||
background: #fff;
|
||||
padding: 8rpx 0 8rpx 8rpx;
|
||||
margin: 0 auto;
|
||||
> .goods-title {
|
||||
font-size: 24rpx;
|
||||
height: 67rpx;
|
||||
display: -webkit-box;
|
||||
font-weight: 500;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
> .goods-bottom {
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
> .goods-price {
|
||||
line-height: 2;
|
||||
color: $main-color;
|
||||
.price-int {
|
||||
font-size: 42rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<!-- 新版商品列表 -->
|
||||
<view v-if="isNew" class="goods-new" :style="wrapperStyle">
|
||||
<view v-if="displayGoods.length" class="g-list" :class="'mode-' + layout" :style="listStyle">
|
||||
<view
|
||||
class="g-item"
|
||||
v-for="(g, i) in displayGoods"
|
||||
:key="i"
|
||||
:style="itemStyle"
|
||||
@click="goDetail(g)"
|
||||
>
|
||||
<view class="g-imgbox">
|
||||
<image :src="g.img" mode="aspectFill" class="g-img" />
|
||||
</view>
|
||||
<view class="g-info">
|
||||
<view v-if="showName" class="g-name" :style="nameStyle">{{ g.name }}</view>
|
||||
<view v-if="showPrice" class="g-price" :style="priceStyle">
|
||||
¥{{ goodsFormatPrice(g.price)[0] }}.{{ goodsFormatPrice(g.price)[1] }}
|
||||
</view>
|
||||
<view class="g-bottom">
|
||||
<text
|
||||
v-if="showSales && g.sales != null"
|
||||
class="g-sales"
|
||||
:style="salesStyle"
|
||||
>已售{{ g.sales }}件</text
|
||||
>
|
||||
<view
|
||||
v-if="cartShow"
|
||||
class="g-cart"
|
||||
:class="'cart-' + cartStyle"
|
||||
:style="{ background: priceColor }"
|
||||
@click.stop="onCartBtn(g)"
|
||||
>
|
||||
<text v-if="cartStyle === 'text'" class="cart-text-label">加购</text>
|
||||
<text v-else-if="cartStyle === 'plus'" class="cart-plus-label">+</text>
|
||||
<u-icon v-else name="shopping-cart" color="#ffffff" size="28rpx"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="g-empty">暂无商品</view>
|
||||
</view>
|
||||
|
||||
<!-- 旧版:分类标签 + 绑定商品 -->
|
||||
<view v-else class="layout">
|
||||
<u-sticky>
|
||||
<view class="goods-cell-title">
|
||||
<view
|
||||
class="goods-item-title"
|
||||
:class="{ 'selected-title': selected.index == index }"
|
||||
@click="handleClickTitle(title, index)"
|
||||
v-for="(title, index) in res.list[0].titleWay"
|
||||
:key="index"
|
||||
>
|
||||
<h4 class="h4">{{ title.title }}</h4>
|
||||
<view class="title-desc">{{ title.desc }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-sticky>
|
||||
<view class="goods-list">
|
||||
<template v-for="(item, item_index) in res.list[0].listWay" :key="item_index">
|
||||
<view
|
||||
v-if="
|
||||
item.___index != undefined
|
||||
? selected.index == item.___index
|
||||
: selected.val == item.type
|
||||
"
|
||||
@click="handleClick(item)"
|
||||
class="goods-item"
|
||||
>
|
||||
<view class="goods-img">
|
||||
<u-image
|
||||
:src="item.img"
|
||||
height="350rpx"
|
||||
mode="aspectFit"
|
||||
width="100%"
|
||||
>
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</view>
|
||||
<view class="goods-desc">
|
||||
<view class="goods-title">
|
||||
{{ item.title }}
|
||||
</view>
|
||||
<view class="goods-bottom">
|
||||
<view class="goods-price">
|
||||
¥<span class="price-int"
|
||||
>{{ goodsFormatPrice(item.price)[0] }} </span
|
||||
>.{{ goodsFormatPrice(item.price)[1] }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template
|
||||
v-if="currentTitleWay?.bindCategory && goodsData.length"
|
||||
>
|
||||
<view
|
||||
v-for="(item, index) in goodsData"
|
||||
:key="index"
|
||||
class="goods-item"
|
||||
@click="handleClick(item)"
|
||||
>
|
||||
<view class="goods-img">
|
||||
<u-image
|
||||
:src="item.thumbnail"
|
||||
height="350rpx"
|
||||
mode="aspectFit"
|
||||
width="100%"
|
||||
>
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</view>
|
||||
<view class="goods-desc">
|
||||
<view class="goods-title">
|
||||
{{ item.goodsName }}
|
||||
</view>
|
||||
<view class="goods-bottom">
|
||||
<view class="goods-price">
|
||||
¥<span class="price-int"
|
||||
>{{ goodsFormatPrice(item.price)[0] }} </span
|
||||
>.{{ goodsFormatPrice(item.price)[1] }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch, onMounted, onUnmounted, computed } from "vue";
|
||||
import { getGoodsList } from "@/api/goods.js";
|
||||
import { addToCart } from "@/api/trade.js";
|
||||
import { goodsFormatPrice } from "@/utils/filters.js";
|
||||
import storage from "@/utils/storage.js";
|
||||
|
||||
const props = defineProps<{ res: any; enableBottomLoad?: boolean }>();
|
||||
|
||||
const selected = reactive({
|
||||
index: 0,
|
||||
val: "",
|
||||
});
|
||||
|
||||
// ---------- 新版商品列表 ----------
|
||||
const first = computed(() => props.res?.list?.[0] || {});
|
||||
const isNew = computed(() => first.value.layout !== undefined);
|
||||
const layout = computed(() => first.value.layout || "list");
|
||||
const showName = computed(() => first.value.showName !== false);
|
||||
const showPrice = computed(() => first.value.showPrice !== false);
|
||||
const showSales = computed(() => first.value.showSales !== false);
|
||||
const cartShow = computed(() => first.value.cartShow !== false);
|
||||
const cartStyle = computed(() => first.value.cartStyle || "plus");
|
||||
const priceColor = computed(() =>
|
||||
props.res?.toneMode === "custom" && props.res?.priceColor
|
||||
? props.res.priceColor
|
||||
: "var(--theme-primary, #ff3c2a)"
|
||||
);
|
||||
const priceStyle = computed(() => ({ color: priceColor.value }));
|
||||
const nameStyle = computed(() =>
|
||||
props.res?.titleColor ? { color: props.res.titleColor } : {}
|
||||
);
|
||||
const salesStyle = computed(() =>
|
||||
props.res?.salesColor ? { color: props.res.salesColor } : {}
|
||||
);
|
||||
const rpx = (v: any) => `${(Number(v) || 0) * 2}rpx`;
|
||||
const wrapperStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
const bg =
|
||||
!res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1 || "#ffffff"
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`;
|
||||
return {
|
||||
background: bg,
|
||||
padding: `${rpx(res.paddingTop)} ${rpx(res.paddingX)} ${rpx(res.paddingBottom)}`,
|
||||
borderRadius: rpx(res.borderRadius),
|
||||
overflow: Number(res.borderRadius) > 0 ? "hidden" : "visible",
|
||||
};
|
||||
});
|
||||
const listStyle = computed(() => ({ gap: rpx(props.res?.contentGap ?? 10) }));
|
||||
const itemStyle = computed(() => ({
|
||||
borderRadius: rpx(props.res?.contentRadius ?? 7),
|
||||
overflow: Number(props.res?.contentRadius ?? 7) > 0 ? "hidden" : "visible",
|
||||
}));
|
||||
|
||||
const newGoodsData = ref<any[]>([]);
|
||||
|
||||
const displayGoods = computed(() => {
|
||||
if (!isNew.value) return [];
|
||||
return newGoodsData.value;
|
||||
});
|
||||
|
||||
// 触底加载(新版)
|
||||
const newParams = reactive({ pageNumber: 1 });
|
||||
const newTotal = ref(0);
|
||||
|
||||
function buildQuery() {
|
||||
const item = first.value;
|
||||
const params: Record<string, any> = {
|
||||
pageNumber: newParams.pageNumber,
|
||||
pageSize: item.count || 6,
|
||||
};
|
||||
if (item.dataMode === "brand" && item.brandId) params.brandId = item.brandId;
|
||||
if (item.dataMode === "store" && item.storeId) params.storeId = item.storeId;
|
||||
if (item.dataMode === "category" && item.categoryId) params.categoryId = item.categoryId;
|
||||
if (item.dataMode === "goods") {
|
||||
const ids = (item.goodsList || []).map((g: any) => g.id).filter(Boolean);
|
||||
if (!ids.length) return null;
|
||||
params.ids = ids.join(",");
|
||||
params.pageSize = ids.length;
|
||||
}
|
||||
if (item.sortType === "sales") {
|
||||
params.sort = "buyCount";
|
||||
params.order = "desc";
|
||||
} else if (item.sortType === "price") {
|
||||
params.sort = "price";
|
||||
params.order = "asc";
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
async function loadNewGoods(reset = true) {
|
||||
const params = buildQuery();
|
||||
if (!params) {
|
||||
newGoodsData.value = [];
|
||||
newTotal.value = 0;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await getGoodsList(params);
|
||||
if (!res.data.success) return;
|
||||
const result = res.data.result || {};
|
||||
const records = result.records || [];
|
||||
const mapped = records.map((g: any) => ({
|
||||
id: g.id,
|
||||
goodsId: g.goodsId,
|
||||
name: g.goodsName,
|
||||
price: g.price,
|
||||
img: g.thumbnail || g.small,
|
||||
sales: g.buyCount != null ? g.buyCount : null,
|
||||
}));
|
||||
if (reset) {
|
||||
// 指定商品:按选择顺序排列
|
||||
if (first.value.dataMode === "goods") {
|
||||
const byGoodsId: Record<string, any> = {};
|
||||
mapped.forEach((g: any) => {
|
||||
byGoodsId[g.goodsId] = g;
|
||||
});
|
||||
newGoodsData.value = (first.value.goodsList || [])
|
||||
.map((g: any) => byGoodsId[g.id])
|
||||
.filter(Boolean);
|
||||
} else {
|
||||
newGoodsData.value = mapped;
|
||||
}
|
||||
} else {
|
||||
newGoodsData.value.push(...mapped);
|
||||
}
|
||||
newTotal.value = result.totalElements ?? newGoodsData.value.length;
|
||||
} catch (e) {
|
||||
// 静默失败,保持已有数据
|
||||
}
|
||||
}
|
||||
|
||||
function goDetail(g: any) {
|
||||
if (!g?.id || !g?.goodsId) return;
|
||||
uni.navigateTo({
|
||||
url: `/pages/product/goods?id=${g.id}&goodsId=${g.goodsId}`,
|
||||
});
|
||||
}
|
||||
|
||||
function onCartBtn(g: any) {
|
||||
if (first.value.cartAction !== "add") {
|
||||
goDetail(g);
|
||||
return;
|
||||
}
|
||||
if (!g?.id) return;
|
||||
if (!storage.getAccessToken()) {
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.navigateTo({ url: "/pages/passport/wechatMPLogin" });
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.navigateTo({ url: "/pages/passport/login" });
|
||||
// #endif
|
||||
return;
|
||||
}
|
||||
addToCart({ skuId: g.id, num: 1 }).then((res: any) => {
|
||||
uni.showToast({
|
||||
title: res.data.success ? "已加入购物车" : res.data.message || "加购失败",
|
||||
icon: "none",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [
|
||||
first.value.dataMode,
|
||||
first.value.categoryId,
|
||||
first.value.brandId,
|
||||
first.value.storeId,
|
||||
first.value.count,
|
||||
first.value.sortType,
|
||||
first.value.goodsList,
|
||||
],
|
||||
() => {
|
||||
if (!isNew.value) return;
|
||||
newParams.pageNumber = 1;
|
||||
loadNewGoods(true);
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
);
|
||||
|
||||
// ---------- 旧版逻辑 ----------
|
||||
const currentTitleWay = computed(() => {
|
||||
return props.res?.list?.[0]?.titleWay?.[selected.index]
|
||||
})
|
||||
const params = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 100,
|
||||
categoryId: "",
|
||||
});
|
||||
const goodsData = ref<any[]>([]);
|
||||
const goodsResult = ref<any>("");
|
||||
|
||||
watch(
|
||||
() => props.res,
|
||||
(val) => {
|
||||
if (!val?.list?.[0]) return
|
||||
if (val.list[0].layout !== undefined) return
|
||||
const firstListWay = val.list[0].listWay?.[0]
|
||||
selected.val = firstListWay?.type || ''
|
||||
const firstTitleWay = val.list[0].titleWay?.[0]
|
||||
if (firstTitleWay?.bindCategory) {
|
||||
initGoods(firstTitleWay)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
uni.$on("onReachBottom", () => {
|
||||
if (!props.enableBottomLoad) return;
|
||||
if (isNew.value) {
|
||||
// 新版:仅分类/品牌/商户模式支持触底加载
|
||||
if (first.value.dataMode === "goods") return;
|
||||
if (newTotal.value > newGoodsData.value.length) {
|
||||
newParams.pageNumber++;
|
||||
loadNewGoods(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (
|
||||
goodsResult.value.totalElements >=
|
||||
params.pageNumber * params.pageSize
|
||||
) {
|
||||
params.pageNumber++;
|
||||
initGoods(props.res.list[0].titleWay[selected.index]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
uni.$off("onReachBottom");
|
||||
});
|
||||
|
||||
function handleClick(item: any) {
|
||||
if (!item?.id || !item?.goodsId) return
|
||||
uni.navigateTo({
|
||||
url: `/pages/product/goods?id=${item.id}&goodsId=${item.goodsId}`,
|
||||
})
|
||||
}
|
||||
|
||||
async function initGoods(val: any) {
|
||||
if (props.enableBottomLoad) params.pageSize = 20;
|
||||
val ? (params.categoryId = val.bindCategory.id) : "";
|
||||
const res = await getGoodsList(params);
|
||||
if (res.data.success) {
|
||||
goodsResult.value = res.data.result;
|
||||
const result = res.data.result.records;
|
||||
goodsData.value.push(...result);
|
||||
}
|
||||
}
|
||||
|
||||
function handleClickTitle(val: any, index: number) {
|
||||
selected.index = index;
|
||||
selected.val = val.title;
|
||||
if (val.bindCategory) {
|
||||
params.pageNumber = 1;
|
||||
goodsData.value = [];
|
||||
initGoods(val);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
$w_94: 94%;
|
||||
|
||||
// ---------- 新版 ----------
|
||||
.goods-new {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.g-empty {
|
||||
padding: 60rpx 0;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.g-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.g-item {
|
||||
background: #fff;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.g-imgbox {
|
||||
flex-shrink: 0;
|
||||
background: #f5f5f5;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.g-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.g-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.g-name {
|
||||
font-size: 26rpx;
|
||||
color: #282828;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.g-bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.g-price {
|
||||
color: $main-color;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.g-sales {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 22rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.g-cart {
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
background: $main-color;
|
||||
}
|
||||
|
||||
.cart-plus,
|
||||
.cart-cart {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.cart-plus-label {
|
||||
font-size: 28rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.cart-text {
|
||||
border-radius: 18rpx;
|
||||
padding: 4rpx 18rpx;
|
||||
}
|
||||
|
||||
.cart-text-label {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
/* 卡片:左图右文 */
|
||||
.mode-card {
|
||||
flex-direction: column;
|
||||
.g-item {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 16rpx;
|
||||
}
|
||||
.g-imgbox {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
.g-name {
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
.g-cart {
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* 列表:两列网格 */
|
||||
.mode-list {
|
||||
.g-item {
|
||||
width: calc(50% - 10rpx);
|
||||
flex-direction: column;
|
||||
}
|
||||
.g-imgbox {
|
||||
width: 100%;
|
||||
height: 330rpx;
|
||||
}
|
||||
.g-info {
|
||||
padding: 12rpx;
|
||||
min-height: 120rpx;
|
||||
}
|
||||
.g-name {
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 三列 */
|
||||
.mode-three {
|
||||
.g-item {
|
||||
width: calc(33.33% - 12rpx);
|
||||
flex-direction: column;
|
||||
}
|
||||
.g-imgbox {
|
||||
width: 100%;
|
||||
height: 220rpx;
|
||||
}
|
||||
.g-info {
|
||||
padding: 10rpx;
|
||||
min-height: 96rpx;
|
||||
}
|
||||
.g-name {
|
||||
-webkit-line-clamp: 1;
|
||||
font-size: 24rpx;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
.g-price {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
.g-sales {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 大图:一行一个 */
|
||||
.mode-big {
|
||||
flex-direction: column;
|
||||
.g-item {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
.g-imgbox {
|
||||
width: 100%;
|
||||
height: 420rpx;
|
||||
}
|
||||
.g-info {
|
||||
padding: 16rpx;
|
||||
min-height: 110rpx;
|
||||
}
|
||||
.g-name {
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 旧版 ----------
|
||||
.layout {
|
||||
padding: 8px 0;
|
||||
background: #f9f9f9;
|
||||
}
|
||||
|
||||
.selected-title {
|
||||
.h4 {
|
||||
font-size: 30rpx;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.title-desc {
|
||||
font-weight: bold;
|
||||
color: $main-color !important;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-cell-title {
|
||||
background: #f9f9f9;
|
||||
padding: 10px;
|
||||
transition: 0.35s;
|
||||
display: flex;
|
||||
|
||||
> .goods-item-title {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
|
||||
.h4 {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.title-desc {
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods-list {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.goods-item {
|
||||
width: 50%;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 0.4em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.goods-img {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
width: $w_94;
|
||||
border-top-left-radius: 20rpx;
|
||||
border-top-right-radius: 20rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.goods-desc {
|
||||
border-bottom-left-radius: 20rpx;
|
||||
border-bottom-right-radius: 20rpx;
|
||||
width: $w_94;
|
||||
background: #fff;
|
||||
padding: 8rpx 0 8rpx 8rpx;
|
||||
margin: 0 auto;
|
||||
> .goods-title {
|
||||
font-size: 24rpx;
|
||||
height: 67rpx;
|
||||
display: -webkit-box;
|
||||
font-weight: 500;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
> .goods-bottom {
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
> .goods-price {
|
||||
line-height: 2;
|
||||
color: $main-color;
|
||||
.price-int {
|
||||
font-size: 42rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,31 +1,90 @@
|
||||
<template>
|
||||
<view class="layout" :style="wrapperStyle" v-if="goodsList.length">
|
||||
<view class="join-list">
|
||||
<view class="join-title" @click="goMore">
|
||||
<view class="join-title-name">{{ title }}</view>
|
||||
<view class="join-title-more">更多</view>
|
||||
<view class="integral-head" :style="headStyle" @click="goMore">
|
||||
<image
|
||||
v-if="styled && options.titleType === 'image' && options.titleImage"
|
||||
class="integral-head-img"
|
||||
:src="options.titleImage"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view v-else class="integral-head-title" :style="headTitleStyle">{{ title }}</view>
|
||||
<view class="integral-head-space"></view>
|
||||
<view class="integral-head-more" :style="moreStyle">更多 ></view>
|
||||
</view>
|
||||
<!-- 老数据:原始四列样式 -->
|
||||
<view v-if="!styled" class="join-box">
|
||||
<view
|
||||
class="join-item"
|
||||
@click="goDetail(item)"
|
||||
v-for="(item, index) in goodsList"
|
||||
:key="index"
|
||||
>
|
||||
<view class="item-img-box">
|
||||
<u-image
|
||||
class="item-img"
|
||||
width="150rpx"
|
||||
height="150rpx"
|
||||
:src="item.goodsImage || item.thumbnail"
|
||||
/>
|
||||
</view>
|
||||
<view class="item-price">
|
||||
<span class="price-text">{{ item.points != null ? item.points : 0 }}积分</span>
|
||||
</view>
|
||||
<view class="item-line-through" v-if="item.originalPrice">
|
||||
<span class="price-text">¥{{ item.originalPrice }}</span>
|
||||
</view>
|
||||
</view>
|
||||
<view class="join-box">
|
||||
<view
|
||||
class="join-item"
|
||||
@click="goDetail(item)"
|
||||
v-for="(item, index) in goodsList"
|
||||
:key="index"
|
||||
>
|
||||
<view class="item-img-box">
|
||||
<u-image
|
||||
class="item-img"
|
||||
width="150rpx"
|
||||
height="150rpx"
|
||||
:src="item.goodsImage || item.thumbnail"
|
||||
/>
|
||||
</view>
|
||||
<view class="item-price">
|
||||
<span class="price-text">{{ item.points != null ? item.points : 0 }}积分</span>
|
||||
</view>
|
||||
<view class="item-line-through" v-if="item.originalPrice">
|
||||
<span class="price-text">¥{{ item.originalPrice }}</span>
|
||||
</view>
|
||||
<!-- 样式一:两列大卡片 -->
|
||||
<view v-else-if="layoutStyle === '1'" class="ig-grid2">
|
||||
<view
|
||||
class="ig-g2-item"
|
||||
v-for="(item, index) in goodsList"
|
||||
:key="index"
|
||||
@click="goDetail(item)"
|
||||
>
|
||||
<view class="ig-g2-img-box" :style="imgRadiusStyle">
|
||||
<image class="ig-img" :src="item.goodsImage || item.thumbnail" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="ig-g2-name">{{ item.goodsName || item.promotionName || '' }}</view>
|
||||
<view class="ig-price-row">
|
||||
<view class="ig-price" :style="pointStyle">
|
||||
<text class="ig-coin">¥</text>{{ item.points != null ? item.points : 0 }}积分
|
||||
</view>
|
||||
<text class="ig-original" v-if="item.originalPrice">¥{{ item.originalPrice }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 样式二:三列,积分价在名称上方 -->
|
||||
<view v-else-if="layoutStyle === '2'" class="ig-grid3">
|
||||
<view
|
||||
class="ig-g3-item"
|
||||
v-for="(item, index) in goodsList"
|
||||
:key="index"
|
||||
@click="goDetail(item)"
|
||||
>
|
||||
<view class="ig-g3-img-box" :style="imgRadiusStyle">
|
||||
<image class="ig-img" :src="item.goodsImage || item.thumbnail" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="ig-price ig-price-sm" :style="pointStyle">
|
||||
<text class="ig-coin ig-coin-sm">¥</text>{{ item.points != null ? item.points : 0 }}积分
|
||||
</view>
|
||||
<view class="ig-g3-name">{{ item.goodsName || item.promotionName || '' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 样式三:三列,仅积分价 -->
|
||||
<view v-else class="ig-grid3">
|
||||
<view
|
||||
class="ig-g3-item"
|
||||
v-for="(item, index) in goodsList"
|
||||
:key="index"
|
||||
@click="goDetail(item)"
|
||||
>
|
||||
<view class="ig-g3-img-box" :style="imgRadiusStyle">
|
||||
<image class="ig-img" :src="item.goodsImage || item.thumbnail" mode="aspectFill" />
|
||||
</view>
|
||||
<view class="ig-price ig-price-sm" :style="pointStyle">
|
||||
<text class="ig-coin ig-coin-sm">¥</text>{{ item.points != null ? item.points : 0 }}积分
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -39,16 +98,76 @@ const props = defineProps<{ res: any }>();
|
||||
|
||||
const goodsList = ref<any[]>([]);
|
||||
|
||||
const title = computed(() => props.res?.list?.[0]?.title || "积分商城");
|
||||
const count = computed(() => props.res?.list?.[0]?.count || 3);
|
||||
const options = computed(() => props.res || {});
|
||||
const title = computed(() => props.res?.list?.[0]?.title || "积分兑好礼");
|
||||
const count = computed(() => props.res?.list?.[0]?.count || 6);
|
||||
const layoutStyle = computed(() => props.res?.layoutStyle || "1");
|
||||
|
||||
const wrapperStyle = computed(() => ({
|
||||
background: props.res?.bgColor || "#FFFFFF",
|
||||
padding: `0 ${props.res?.paddingX ?? 0}rpx`,
|
||||
marginBottom: `${props.res?.pageGap ?? 10}rpx`,
|
||||
borderRadius: `${props.res?.borderRadius ?? 0}rpx`,
|
||||
overflow: "hidden",
|
||||
}));
|
||||
// 面板保存过样式字段才启用新样式,老数据保持原样
|
||||
const styled = computed(() => props.res && props.res.paddingTop !== undefined);
|
||||
|
||||
const wrapperStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
if (styled.value) {
|
||||
const bg =
|
||||
!res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`;
|
||||
const radius = Number(res.borderRadius) || 0;
|
||||
return {
|
||||
background: bg,
|
||||
padding: `${Number(res.paddingTop) || 0}rpx ${Number(res.paddingX) || 0}rpx ${Number(res.paddingBottom) || 0}rpx`,
|
||||
marginBottom: `${Number(res.pageGap) || 0}rpx`,
|
||||
borderRadius: `${radius}rpx`,
|
||||
overflow: radius > 0 ? "hidden" : "visible",
|
||||
};
|
||||
}
|
||||
return {
|
||||
background: res.bgColor || "#FFFFFF",
|
||||
padding: `0 ${res.paddingX ?? 0}rpx`,
|
||||
marginBottom: `${res.pageGap ?? 10}rpx`,
|
||||
borderRadius: `${res.borderRadius ?? 0}rpx`,
|
||||
overflow: "hidden",
|
||||
};
|
||||
});
|
||||
|
||||
const headStyle = computed(() => {
|
||||
if (!styled.value) return {};
|
||||
const res = props.res;
|
||||
if (res.headBgType === "image" && res.headBgImage) {
|
||||
return {
|
||||
backgroundImage: `url(${res.headBgImage});background-size:cover;background-position:center;`,
|
||||
};
|
||||
}
|
||||
const bg =
|
||||
!res.headBg2 || res.headBg1 === res.headBg2
|
||||
? res.headBg1
|
||||
: `linear-gradient(90deg, ${res.headBg1}, ${res.headBg2})`;
|
||||
return { background: bg };
|
||||
});
|
||||
|
||||
const headTitleStyle = computed(() => {
|
||||
if (!styled.value) return {};
|
||||
const res = props.res;
|
||||
return {
|
||||
color: res.titleColor || "#FFFFFF",
|
||||
fontWeight: res.titleStyle === "bold" ? "bold" : "normal",
|
||||
fontStyle: res.titleStyle === "italic" ? "italic" : "normal",
|
||||
};
|
||||
});
|
||||
|
||||
const moreStyle = computed(() =>
|
||||
styled.value ? { color: props.res.moreColor || "#999999" } : {}
|
||||
);
|
||||
const pointStyle = computed(() =>
|
||||
styled.value && props.res.pointColor ? { color: props.res.pointColor } : {}
|
||||
);
|
||||
const gapStyle = computed(() =>
|
||||
styled.value ? { gap: `${Number(props.res.contentGap) || 0}rpx` } : {}
|
||||
);
|
||||
const imgRadiusStyle = computed(() =>
|
||||
styled.value ? { borderRadius: `${Number(props.res.imgRadius) || 0}rpx`, overflow: "hidden" } : {}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
API_Promotions.getPointsGoods({
|
||||
@@ -71,6 +190,33 @@ function goDetail(item: any) {
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
.integral-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
min-height: 96rpx;
|
||||
padding: 0 24rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
.integral-head-img {
|
||||
width: 154rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
.integral-head-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
.integral-head-space {
|
||||
flex: 1;
|
||||
}
|
||||
.integral-head-more {
|
||||
flex-shrink: 0;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
.join-box {
|
||||
display: flex;
|
||||
}
|
||||
@@ -96,20 +242,109 @@ function goDetail(item: any) {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.join-title {
|
||||
|
||||
/* 积分商品卡片(参考 CRMEB 积分商城) */
|
||||
.ig-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
.ig-grid2 {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.ig-g2-item {
|
||||
width: 49%;
|
||||
background: #fff;
|
||||
height: 100rpx;
|
||||
padding: 0 20rpx;
|
||||
.join-title-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
.join-title-more {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
padding-bottom: 16rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.ig-g2-img-box {
|
||||
width: 100%;
|
||||
height: 300rpx;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.ig-g2-name {
|
||||
margin: 16rpx 16rpx 0;
|
||||
font-size: 26rpx;
|
||||
color: #282828;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ig-price-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin: 10rpx 16rpx 0;
|
||||
min-width: 0;
|
||||
}
|
||||
.ig-price {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #f5483a;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ig-price-sm {
|
||||
font-size: 24rpx;
|
||||
margin: 10rpx 16rpx 0;
|
||||
}
|
||||
.ig-coin {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #ffe29a, #f5a623);
|
||||
color: #fff;
|
||||
font-size: 18rpx;
|
||||
font-weight: bold;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 6rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.ig-coin-sm {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
font-size: 16rpx;
|
||||
}
|
||||
.ig-original {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-left: 10rpx;
|
||||
white-space: nowrap;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
.ig-grid3 {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.ig-g3-item {
|
||||
width: 32.5%;
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
padding-bottom: 16rpx;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
.ig-g3-img-box {
|
||||
width: 100%;
|
||||
height: 184rpx;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.ig-g3-name {
|
||||
margin: 8rpx 16rpx 0;
|
||||
font-size: 24rpx;
|
||||
color: #282828;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
|
||||
<template>
|
||||
|
||||
<view class="layout">
|
||||
<view class="view-height-150" @click="modelNavigateTo(res.list[0])">
|
||||
<view class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<view class="view-height-150" :style="imgStyle" @click="modelNavigateTo(res.list[0])">
|
||||
<u-image width="100%" height="340rpx" class="image-mode" :src="res.list[0].img">
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</view>
|
||||
<view class="view-height-150">
|
||||
<view class="view-height-75" @click="modelNavigateTo(res.list[1])">
|
||||
<view class="view-height-75" :style="imgStyle" @click="modelNavigateTo(res.list[1])">
|
||||
<u-image width="100%" height="170rpx" class="image-mode" :src="res.list[1].img" alt>
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</view>
|
||||
<view class="view-height-75" @click="modelNavigateTo(res.list[2])">
|
||||
<view class="view-height-75" :style="imgStyle" @click="modelNavigateTo(res.list[2])">
|
||||
<u-image width="100%" height="170rpx" class="image-mode" :src="res.list[2].img" alt>
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
@@ -22,9 +22,14 @@
|
||||
</view>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
import { computed } from "vue";
|
||||
import { modelNavigateTo, cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./tpl";
|
||||
|
||||
defineProps<{ res: any }>();
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const wrapperStyle = computed(() => cubeWrapperStyle(props.res));
|
||||
const imgStyle = computed(() => cubeImgStyle(props.res));
|
||||
const gapStyle = computed(() => cubeGapStyle(props.res));
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
@@ -35,4 +40,7 @@ defineProps<{ res: any }>();
|
||||
|
||||
background-size: cover;
|
||||
}
|
||||
</style>
|
||||
.view-height-150 {
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
|
||||
<template>
|
||||
<div class="layout ">
|
||||
<div class="layout " :style="[wrapperStyle, gapStyle]">
|
||||
<div class="view-height-150">
|
||||
<div class="view-height-75" @click="modelNavigateTo(res.list[0])">
|
||||
<div class="view-height-75" :style="imgStyle" @click="modelNavigateTo(res.list[0])">
|
||||
<u-image class="image-mode" width="100%" height="150rpx" :src="res.list[0].img" ></u-image>
|
||||
</div>
|
||||
<div class="view-height-75" @click="modelNavigateTo(res.list[1])">
|
||||
<div class="view-height-75" :style="imgStyle" @click="modelNavigateTo(res.list[1])">
|
||||
<u-image class="image-mode" width="100%" height="150rpx" :src="res.list[1].img" ></u-image>
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-height-150" @click="modelNavigateTo(res.list[2])">
|
||||
<div class="view-height-150" :style="imgStyle" @click="modelNavigateTo(res.list[2])">
|
||||
<u-image class="image-mode" width="100%" height="300rpx" :src="res.list[2].img" ></u-image>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
import { computed } from "vue";
|
||||
import { modelNavigateTo, cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./tpl";
|
||||
|
||||
defineProps<{ res: any }>();
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const wrapperStyle = computed(() => cubeWrapperStyle(props.res));
|
||||
const imgStyle = computed(() => cubeImgStyle(props.res));
|
||||
const gapStyle = computed(() => cubeGapStyle(props.res));
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
@@ -30,5 +35,8 @@ defineProps<{ res: any }>();
|
||||
|
||||
background-size: cover;
|
||||
}
|
||||
.view-height-150 {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -9,11 +9,20 @@ import { computed } from "vue";
|
||||
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const wrapperStyle = computed(() => ({
|
||||
background: props.res?.bgColor || "#FFFFFF",
|
||||
padding: `0 ${props.res?.paddingX ?? 0}rpx`,
|
||||
marginBottom: `${props.res?.pageGap ?? 10}rpx`,
|
||||
}));
|
||||
const wrapperStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
const bg =
|
||||
res.bgColor1 !== undefined
|
||||
? !res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`
|
||||
: res.bgColor || "#FFFFFF";
|
||||
return {
|
||||
background: bg,
|
||||
padding: `0 ${res.paddingX ?? 0}rpx`,
|
||||
marginBottom: `${res.pageGap ?? 10}rpx`,
|
||||
};
|
||||
});
|
||||
|
||||
const lineStyle = computed(() => ({
|
||||
borderTop: `${props.res?.lineHeight ?? 1}rpx ${props.res?.lineStyle || "dashed"} ${props.res?.lineColor || "#979797"}`,
|
||||
|
||||
@@ -1,31 +1,61 @@
|
||||
<template>
|
||||
<view class="layout">
|
||||
<view class="menu-list">
|
||||
<view class="layout" :style="wrapperStyle">
|
||||
<view
|
||||
class="menu-list"
|
||||
:class="{ styled: styled, single: isSingle }"
|
||||
:style="listStyle"
|
||||
>
|
||||
<view
|
||||
class="menu-item"
|
||||
:style="itemStyle"
|
||||
@click="modelNavigateTo(item)"
|
||||
v-for="(item, index) in res.list"
|
||||
v-for="(item, index) in visibleList"
|
||||
:key="index"
|
||||
>
|
||||
<view class="menu-img-wrap">
|
||||
<u-image
|
||||
width="88rpx"
|
||||
height="88rpx"
|
||||
class="menu-img"
|
||||
:src="item.img"
|
||||
>
|
||||
<view class="menu-img-wrap" :style="imgStyle">
|
||||
<u-image width="88rpx" height="88rpx" class="menu-img" :src="item.img">
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</view>
|
||||
<view class="menu-title">{{ item.title }}</view>
|
||||
<view class="menu-title" :style="titleStyle">{{ item.title }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
import { computed } from "vue";
|
||||
import { modelNavigateTo, cubeWrapperStyle, cubeImgStyle } from "./tpl";
|
||||
|
||||
defineProps<{ res: any }>();
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
// 面板保存过样式字段才启用自定义样式,老数据保持原样
|
||||
const styled = computed(() => props.res && props.res.paddingTop !== undefined);
|
||||
const wrapperStyle = computed(() => cubeWrapperStyle(props.res));
|
||||
const imgStyle = computed(() => cubeImgStyle(props.res));
|
||||
const listStyle = computed(() =>
|
||||
styled.value ? { rowGap: `${Number(props.res.contentGap) || 0}rpx` } : {}
|
||||
);
|
||||
const titleStyle = computed(() =>
|
||||
styled.value && props.res.textColor ? { color: props.res.textColor } : {}
|
||||
);
|
||||
|
||||
// 展示设置:单行/多行、行数、每行个数,超出部分不展示
|
||||
const isSingle = computed(
|
||||
() => styled.value && props.res.menuDisplay === "single"
|
||||
);
|
||||
const cols = computed(() =>
|
||||
styled.value ? Number(props.res.menuCols) || 4 : 4
|
||||
);
|
||||
const rows = computed(() => Number(props.res.menuRows) || 3);
|
||||
const itemStyle = computed(() =>
|
||||
styled.value ? { flex: `0 0 ${100 / cols.value}%` } : {}
|
||||
);
|
||||
const visibleList = computed(() => {
|
||||
const list = props.res.list || [];
|
||||
if (!styled.value) return list;
|
||||
const limit = isSingle.value ? cols.value : rows.value * cols.value;
|
||||
return list.slice(0, limit);
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
@@ -34,17 +64,28 @@ defineProps<{ res: any }>();
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
|
||||
&.single {
|
||||
flex-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
> .menu-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
width: 20%;
|
||||
flex: 0 0 20%;
|
||||
width: 25%;
|
||||
flex: 0 0 25%;
|
||||
box-sizing: border-box;
|
||||
margin: 20rpx 0;
|
||||
padding: 0 4rpx;
|
||||
}
|
||||
|
||||
&.styled > .menu-item,
|
||||
&.single > .menu-item {
|
||||
margin: 0;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-img-wrap {
|
||||
@@ -72,4 +113,4 @@ defineProps<{ res: any }>();
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,55 +1,103 @@
|
||||
<template>
|
||||
<div class="layout" v-if="list.length">
|
||||
<div class="background">
|
||||
<u-notice-bar
|
||||
direction="column"
|
||||
:bg-color="noticeStyle.bk_color"
|
||||
:color="noticeStyle.color"
|
||||
:text="list"
|
||||
></u-notice-bar>
|
||||
</div>
|
||||
</div>
|
||||
<view class="layout" :class="{ styled: styled }" v-if="texts.length" :style="wrapperStyle">
|
||||
<view class="notice-bar">
|
||||
<image
|
||||
v-if="styled && item.img"
|
||||
class="notice-icon"
|
||||
:src="item.img"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view class="notice-body" :style="{ textAlign: alignValue }">
|
||||
<u-notice-bar
|
||||
:direction="directionValue"
|
||||
:bg-color="styled ? 'transparent' : item.bk_color"
|
||||
:color="item.color"
|
||||
:text="texts"
|
||||
@click="clickNotice"
|
||||
></u-notice-bar>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const noticeStyle = computed(() => props.res?.list?.[0] || {});
|
||||
const item = computed(() => props.res?.list?.[0] || {});
|
||||
|
||||
const list = computed(() => {
|
||||
const titles = props.res?.list?.[0]?.title;
|
||||
if (!Array.isArray(titles)) return [];
|
||||
return titles
|
||||
.map((item: { context?: string }) => (item?.context == null ? "" : String(item.context)))
|
||||
.filter((text: string) => text.length > 0);
|
||||
// 面板保存过样式字段才启用自定义样式,老数据保持原样
|
||||
const styled = computed(() => props.res && props.res.paddingX !== undefined);
|
||||
|
||||
const wrapperStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
if (!styled.value) return {};
|
||||
const bg =
|
||||
!res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`;
|
||||
const radius = Number(res.borderRadius) || 0;
|
||||
return {
|
||||
padding: `${Number(res.paddingTop) || 0}rpx ${Number(res.paddingX) || 0}rpx ${Number(res.paddingBottom) || 0}rpx`,
|
||||
background: bg,
|
||||
borderRadius: `${radius}rpx`,
|
||||
marginBottom: `${Number(res.pageGap) || 0}rpx`,
|
||||
overflow: radius > 0 ? "hidden" : "visible",
|
||||
};
|
||||
});
|
||||
|
||||
const directionValue = computed(() =>
|
||||
styled.value && item.value.direction === "horizontal" ? "row" : "column"
|
||||
);
|
||||
const alignValue = computed(() =>
|
||||
styled.value ? item.value.textAlign || "left" : "left"
|
||||
);
|
||||
|
||||
const titleList = computed(() => {
|
||||
const titles = item.value?.title;
|
||||
if (!Array.isArray(titles)) return [];
|
||||
return titles.filter(
|
||||
(t: any) => t && t.context != null && String(t.context).length > 0
|
||||
);
|
||||
});
|
||||
|
||||
const texts = computed(() =>
|
||||
titleList.value.map((t: any) => String(t.context))
|
||||
);
|
||||
|
||||
function clickNotice(index: number) {
|
||||
const target = titleList.value[index];
|
||||
if (target && target.url) {
|
||||
modelNavigateTo(target);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
.background {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: 84rpx;
|
||||
text-align: left;
|
||||
font-size: 20rpx;
|
||||
background-size: cover;
|
||||
}
|
||||
.layout {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
height: 84rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
background: #ffffff;
|
||||
}
|
||||
.title {
|
||||
line-height: 84rpx;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
@import "./tpl.scss";
|
||||
.layout {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 84rpx;
|
||||
background: #ffffff;
|
||||
}
|
||||
.layout.styled {
|
||||
height: auto;
|
||||
}
|
||||
.notice-bar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.notice-icon {
|
||||
width: 130rpx;
|
||||
height: 36rpx;
|
||||
flex-shrink: 0;
|
||||
margin: 0 16rpx;
|
||||
}
|
||||
.notice-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,13 +9,22 @@ import { computed } from "vue";
|
||||
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const wrapperStyle = computed(() => ({
|
||||
background: props.res?.bgColor || "#FFFFFF",
|
||||
padding: `0 ${props.res?.paddingX ?? 12}rpx`,
|
||||
marginBottom: `${props.res?.pageGap ?? 10}rpx`,
|
||||
borderRadius: `${props.res?.borderRadius ?? 0}rpx`,
|
||||
overflow: "hidden",
|
||||
}));
|
||||
const wrapperStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
const bg =
|
||||
res.bgColor1 !== undefined
|
||||
? !res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`
|
||||
: res.bgColor || "#FFFFFF";
|
||||
return {
|
||||
background: bg,
|
||||
padding: `0 ${res.paddingX ?? 12}rpx`,
|
||||
marginBottom: `${res.pageGap ?? 10}rpx`,
|
||||
borderRadius: `${res.borderRadius ?? 0}rpx`,
|
||||
overflow: "hidden",
|
||||
};
|
||||
});
|
||||
|
||||
// 防止富文本内图片超宽撑破布局
|
||||
const processedHtml = computed(() => {
|
||||
|
||||
@@ -1,57 +1,123 @@
|
||||
<template>
|
||||
<view class="layout">
|
||||
<view class="search" @click="handleSearch">
|
||||
<u-icon name="search"></u-icon>
|
||||
<text class="search-text">{{ res.list[0].title }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{ res: any; storeId?: string }>();
|
||||
|
||||
function handleSearch() {
|
||||
if (props.storeId) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/navigation/search/searchPage?storeId=${props.storeId}`,
|
||||
});
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: `/pages/navigation/search/searchPage`,
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
|
||||
.search {
|
||||
width: 100%;
|
||||
height: 64rpx;
|
||||
border-radius: 10rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0 10rpx;
|
||||
background: #ededed;
|
||||
color: #999;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-text {
|
||||
margin-left: 12rpx;
|
||||
font-size: 26rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.layout {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<view class="layout" :style="wrapperStyle">
|
||||
<view class="search" :style="boxStyle" @click="handleSearch">
|
||||
<u-icon name="search"></u-icon>
|
||||
<text class="search-text">{{ displayText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch, onBeforeUnmount } from "vue";
|
||||
|
||||
const props = defineProps<{ res: any; storeId?: string }>();
|
||||
|
||||
// 面板保存过样式字段才启用自定义样式,老数据保持原样
|
||||
const styled = computed(() => props.res && props.res.paddingX !== undefined);
|
||||
|
||||
// 搜索热词轮播:有热词时按切换时间轮换展示,否则展示提示文字
|
||||
const hotIndex = ref(0);
|
||||
let hotTimer: ReturnType<typeof setInterval> | null = null;
|
||||
const validWords = computed(() =>
|
||||
(props.res.hotWords || []).filter((w: string) => w && w.trim())
|
||||
);
|
||||
const displayText = computed(() => {
|
||||
if (validWords.value.length) {
|
||||
return validWords.value[hotIndex.value % validWords.value.length];
|
||||
}
|
||||
return props.res.list?.[0]?.title || "";
|
||||
});
|
||||
|
||||
function stopHot() {
|
||||
if (hotTimer) {
|
||||
clearInterval(hotTimer);
|
||||
hotTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [validWords.value.length, props.res.hotInterval],
|
||||
() => {
|
||||
stopHot();
|
||||
hotIndex.value = 0;
|
||||
if (validWords.value.length > 1) {
|
||||
hotTimer = setInterval(() => {
|
||||
hotIndex.value = (hotIndex.value + 1) % validWords.value.length;
|
||||
}, (Number(props.res.hotInterval) || 3) * 1000);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
onBeforeUnmount(stopHot);
|
||||
|
||||
const wrapperStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
if (!styled.value) return {};
|
||||
const bg =
|
||||
!res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`;
|
||||
return {
|
||||
padding: `0 ${Number(res.paddingX) || 0}rpx`,
|
||||
background: bg,
|
||||
borderRadius: `${Number(res.borderRadius) || 0}rpx`,
|
||||
overflow: Number(res.borderRadius) > 0 ? "hidden" : "visible",
|
||||
};
|
||||
});
|
||||
const boxStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
if (!styled.value) return {};
|
||||
return {
|
||||
background: res.boxColor || "#EDEDED",
|
||||
color: res.textColor || "#999999",
|
||||
borderRadius: `${Number(res.boxRadius) || 0}rpx`,
|
||||
justifyContent: res.textAlign === "center" ? "center" : "flex-start",
|
||||
};
|
||||
});
|
||||
|
||||
function handleSearch() {
|
||||
if (props.storeId) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/navigation/search/searchPage?storeId=${props.storeId}`,
|
||||
});
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: `/pages/navigation/search/searchPage`,
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
|
||||
.search {
|
||||
width: 100%;
|
||||
height: 64rpx;
|
||||
border-radius: 10rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0 10rpx;
|
||||
background: #ededed;
|
||||
color: #999;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-text {
|
||||
margin-left: 12rpx;
|
||||
font-size: 26rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.layout {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,62 +1,357 @@
|
||||
<template>
|
||||
<view class="tpl-shop" :style="wrapperStyle" v-if="shopList.length">
|
||||
<view class="shop-title" v-if="title">{{ title }}</view>
|
||||
<view class="shop-grid">
|
||||
<view class="shop-head" v-if="headIcon || title || moreUrl">
|
||||
<image v-if="headIcon" class="shop-head-icon" :src="headIcon" mode="aspectFit" />
|
||||
<view class="shop-title" :style="titleStyle">{{ title }}</view>
|
||||
<view v-if="moreUrl" class="shop-more" :style="moreStyle" @click="goMore">更多 ></view>
|
||||
</view>
|
||||
|
||||
<!-- 老数据:两列网格 -->
|
||||
<view v-if="!styled" class="shop-grid-legacy">
|
||||
<view
|
||||
class="shop-item"
|
||||
v-for="item in shopList"
|
||||
:key="item.id"
|
||||
@click="goShop(item)"
|
||||
>
|
||||
<image class="shop-logo" :src="item.storeLogo" mode="aspectFill" />
|
||||
<view class="shop-name">{{ item.storeName }}</view>
|
||||
<image
|
||||
v-if="showLogo"
|
||||
class="shop-logo"
|
||||
:src="item.storeLogo"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view v-if="showName" class="shop-name">{{ item.storeName }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 样式一:店铺卡片(商品图背景 + Logo + 名称 + 类型标签) -->
|
||||
<view v-else-if="shopStyle === 'one'" class="shop-grid">
|
||||
<view
|
||||
class="shop-card"
|
||||
v-for="item in shopList"
|
||||
:key="item.id"
|
||||
@click="goShop(item)"
|
||||
>
|
||||
<image
|
||||
v-if="(item.goodsImages && item.goodsImages.length) || item.storeBanner"
|
||||
class="shop-card-bg"
|
||||
:style="logoStyle"
|
||||
:src="item.goodsImages && item.goodsImages.length ? item.goodsImages[0] : item.storeBanner"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view v-else class="shop-card-bg shop-card-bg-empty" :style="logoStyle"></view>
|
||||
<view class="shop-card-body">
|
||||
<image
|
||||
v-if="showLogo"
|
||||
class="shop-card-logo"
|
||||
:src="item.storeLogo"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view v-if="showName" class="shop-card-name" :style="nameStyle">{{ item.storeName }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 样式二:店铺卡片 + 商品列表 -->
|
||||
<view v-else class="shop-street-list">
|
||||
<view
|
||||
class="shop-street-card"
|
||||
v-for="item in shopList"
|
||||
:key="item.id"
|
||||
@click="goShop(item)"
|
||||
>
|
||||
<view class="shop-street-head">
|
||||
<image v-if="showLogo" class="shop-street-logo" :src="item.storeLogo" mode="aspectFit" />
|
||||
<view v-if="showName" class="shop-street-name" :style="nameStyle">{{ item.storeName }}</view>
|
||||
<image
|
||||
v-if="(item.goods && item.goods.length) || item.storeBanner"
|
||||
class="shop-street-banner"
|
||||
:style="logoStyle"
|
||||
:src="item.goods && item.goods.length ? item.goods[0].img : item.storeBanner"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
<view class="shop-street-goods">
|
||||
<view
|
||||
class="street-goods-item"
|
||||
v-for="(g, gi) in item.goods || []"
|
||||
:key="gi"
|
||||
>
|
||||
<image class="street-goods-img" :src="g.img" mode="aspectFill" />
|
||||
<view class="street-goods-name">{{ g.name }}</view>
|
||||
<view class="street-goods-price">
|
||||
¥{{ goodsFormatPrice(g.price)[0] }}.{{ goodsFormatPrice(g.price)[1] }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { getStoreList } from "@/api/store.js";
|
||||
import { getGoodsList } from "@/api/goods.js";
|
||||
import { goodsFormatPrice } from "@/utils/filters.js";
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const shopList = ref<any[]>([]);
|
||||
|
||||
const title = computed(() => props.res?.list?.[0]?.title || "");
|
||||
const count = computed(() => props.res?.list?.[0]?.count || 4);
|
||||
const first = computed(() => props.res?.list?.[0] || {});
|
||||
const title = computed(() => first.value.title || "");
|
||||
const count = computed(() => first.value.count || 4);
|
||||
const headIcon = computed(() => first.value.icon || "");
|
||||
const moreUrl = computed(() => first.value.moreUrl || "");
|
||||
const showLogo = computed(() => first.value.showLogo !== false);
|
||||
const showName = computed(() => first.value.showName !== false);
|
||||
const shopStyle = computed(() => first.value.shopStyle || "one");
|
||||
|
||||
const wrapperStyle = computed(() => ({
|
||||
background: props.res?.bgColor || "#FFFFFF",
|
||||
padding: `20rpx ${props.res?.paddingX ?? 0}rpx`,
|
||||
marginBottom: `${props.res?.pageGap ?? 10}rpx`,
|
||||
borderRadius: `${props.res?.borderRadius ?? 0}rpx`,
|
||||
overflow: "hidden",
|
||||
}));
|
||||
// 面板保存过样式字段才启用自定义样式,老数据保持原样
|
||||
const styled = computed(() => props.res && props.res.paddingTop !== undefined);
|
||||
const wrapperStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
if (styled.value) {
|
||||
const bg =
|
||||
!res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`;
|
||||
const radius = Number(res.borderRadius) || 0;
|
||||
return {
|
||||
background: bg,
|
||||
padding: `${Number(res.paddingTop) || 0}rpx ${Number(res.paddingX) || 0}rpx ${Number(res.paddingBottom) || 0}rpx`,
|
||||
marginBottom: `${Number(res.pageGap) || 0}rpx`,
|
||||
borderRadius: `${radius}rpx`,
|
||||
overflow: radius > 0 ? "hidden" : "visible",
|
||||
};
|
||||
}
|
||||
return {
|
||||
background: res.bgColor || "#FFFFFF",
|
||||
padding: `20rpx ${res.paddingX ?? 0}rpx`,
|
||||
marginBottom: `${res.pageGap ?? 10}rpx`,
|
||||
borderRadius: `${res.borderRadius ?? 0}rpx`,
|
||||
overflow: "hidden",
|
||||
};
|
||||
});
|
||||
const titleStyle = computed(() =>
|
||||
styled.value && props.res.titleColor ? { color: props.res.titleColor } : {}
|
||||
);
|
||||
const nameStyle = computed(() =>
|
||||
styled.value && props.res.nameColor ? { color: props.res.nameColor } : {}
|
||||
);
|
||||
const moreStyle = computed(() =>
|
||||
styled.value && props.res.moreColor ? { color: props.res.moreColor } : {}
|
||||
);
|
||||
const logoStyle = computed(() =>
|
||||
styled.value ? { borderRadius: `${Number(props.res.imgRadius) || 0}rpx` } : {}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
getStoreList({ pageNumber: 1, pageSize: count.value }).then((res) => {
|
||||
async function loadShops() {
|
||||
const item = first.value;
|
||||
if (item.dataMode === "custom" && Array.isArray(item.shopList) && item.shopList.length) {
|
||||
shopList.value = item.shopList;
|
||||
} else {
|
||||
const res = await getStoreList({ pageNumber: 1, pageSize: count.value });
|
||||
if (res.data.success && res.data.result) {
|
||||
shopList.value = (res.data.result.records || []).slice(0, count.value);
|
||||
}
|
||||
}
|
||||
loadGoodsImages();
|
||||
}
|
||||
|
||||
// 样式二:按店铺拉取商品(图片、名称、价格)
|
||||
function loadGoodsImages() {
|
||||
if (shopStyle.value !== "two") return;
|
||||
shopList.value.forEach((shop: any, i: number) => {
|
||||
getGoodsList({ storeId: shop.id, pageNumber: 1, pageSize: 3 }).then((res: any) => {
|
||||
const records = res?.data?.result?.records || res?.data?.result || [];
|
||||
const goods = (Array.isArray(records) ? records : [])
|
||||
.map((g: any) => ({
|
||||
name: g.goodsName,
|
||||
price: g.price,
|
||||
img: g.thumbnail || g.small || g.goodsImage,
|
||||
}))
|
||||
.filter((g: any) => g.img);
|
||||
shopList.value[i].goods = goods;
|
||||
shopList.value[i].goodsImages = goods.map((g: any) => g.img);
|
||||
}).catch(() => {});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [first.value.dataMode, first.value.shopList, count.value],
|
||||
() => loadShops(),
|
||||
{ immediate: true, deep: true }
|
||||
);
|
||||
|
||||
function goShop(item: any) {
|
||||
uni.navigateTo({ url: `/pages/product/shopPage?id=${item.id}` });
|
||||
}
|
||||
|
||||
function goMore() {
|
||||
modelNavigateTo({ url: moreUrl.value });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shop-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
.shop-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8rpx 24rpx 20rpx;
|
||||
}
|
||||
.shop-head-icon {
|
||||
width: 124rpx;
|
||||
height: 32rpx;
|
||||
flex-shrink: 0;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
.shop-title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.shop-more {
|
||||
flex-shrink: 0;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
// 样式一:三列店铺卡片
|
||||
.shop-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 24rpx;
|
||||
gap: 16rpx;
|
||||
}
|
||||
.shop-card {
|
||||
width: calc((100% - 32rpx) / 3);
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
background: #f7f7f7;
|
||||
}
|
||||
.shop-card-bg {
|
||||
width: 100%;
|
||||
height: 140rpx;
|
||||
display: block;
|
||||
background: #eee;
|
||||
}
|
||||
.shop-card-bg-empty {
|
||||
background: linear-gradient(135deg, #f5f5f5, #e8e8e8);
|
||||
}
|
||||
.shop-card-body {
|
||||
padding: 8rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.shop-card-logo {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
width: 96rpx;
|
||||
height: 32rpx;
|
||||
margin: -16rpx auto 0;
|
||||
background: #fff;
|
||||
}
|
||||
.shop-card-name {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
// 样式二:店铺卡片 + 商品列表
|
||||
.shop-street-list {
|
||||
padding: 0 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
.shop-street-card {
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
background: #f7f7f7;
|
||||
}
|
||||
.shop-street-head {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 68rpx;
|
||||
padding: 0 16rpx;
|
||||
box-sizing: border-box;
|
||||
background: var(--theme-primary, #ff3c2a);
|
||||
overflow: hidden;
|
||||
}
|
||||
.shop-street-logo {
|
||||
flex-shrink: 0;
|
||||
width: 88rpx;
|
||||
height: 32rpx;
|
||||
padding: 2rpx 6rpx;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
.shop-street-name {
|
||||
max-width: calc(100% - 220rpx);
|
||||
margin-left: 12rpx;
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.shop-street-banner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 160rpx;
|
||||
height: 68rpx;
|
||||
}
|
||||
.shop-street-goods {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
padding: 12rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.street-goods-item {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
border-radius: 8rpx;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
}
|
||||
.street-goods-img {
|
||||
width: 100%;
|
||||
height: 116rpx;
|
||||
display: block;
|
||||
background: #eee;
|
||||
}
|
||||
.street-goods-name {
|
||||
padding: 8rpx 8rpx 0;
|
||||
color: #282828;
|
||||
font-size: 22rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.street-goods-price {
|
||||
padding: 4rpx 8rpx 10rpx;
|
||||
color: var(--theme-primary, #ff3c2a);
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
// 老数据两列网格(未配置样式时)
|
||||
.shop-grid-legacy {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
.shop-item {
|
||||
width: 50%;
|
||||
@@ -68,10 +363,11 @@ function goShop(item: any) {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 50%;
|
||||
background: #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.shop-name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
margin-left: 16rpx;
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="view-list">
|
||||
<div class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<div class="view-list" :style="gapStyle">
|
||||
<div class="view-item" @click="modelNavigateTo(res.list[0])">
|
||||
<div class="-item-tilte">{{res.list[0].title}}</div>
|
||||
<div class="-item-image">
|
||||
<div class="-item-image" :style="imgStyle">
|
||||
<u-image width="120rpx" mode="scaleToFill" height="120rpx" :src="res.list[0].img" alt ></u-image>
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-item" @click="modelNavigateTo(res.list[1])">
|
||||
<div class="-item-tilte">{{res.list[1].title}}</div>
|
||||
<div class="-item-image">
|
||||
<div class="-item-image" :style="imgStyle">
|
||||
<u-image width="120rpx" mode="scaleToFill" height="120rpx" :src="res.list[1].img" alt ></u-image>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-list">
|
||||
<div class="view-list" :style="gapStyle">
|
||||
<div class="view-item" @click="modelNavigateTo(res.list[2])">
|
||||
<div class="-item-tilte">{{res.list[2].title}}</div>
|
||||
<div class="-item-image">
|
||||
<div class="-item-image" :style="imgStyle">
|
||||
<u-image width="120rpx" mode="scaleToFill" height="120rpx" :src="res.list[2].img" alt ></u-image>
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-item" @click="modelNavigateTo(res.list[3])">
|
||||
<div class="-item-tilte">{{res.list[3].title}}</div>
|
||||
<div class="-item-image">
|
||||
<div class="-item-image" :style="imgStyle">
|
||||
<u-image width="120rpx" mode="scaleToFill" height="120rpx" :src="res.list[3].img" alt ></u-image>
|
||||
</div>
|
||||
</div>
|
||||
@@ -33,9 +33,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
import { computed } from "vue";
|
||||
import { modelNavigateTo, cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./tpl";
|
||||
|
||||
defineProps<{ res: any }>();
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const wrapperStyle = computed(() => cubeWrapperStyle(props.res));
|
||||
const imgStyle = computed(() => cubeImgStyle(props.res));
|
||||
const gapStyle = computed(() => cubeGapStyle(props.res));
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
@@ -48,15 +53,15 @@ defineProps<{ res: any }>();
|
||||
padding: 0;
|
||||
}
|
||||
.-item-image {
|
||||
padding: 10px;
|
||||
padding: 20rpx;
|
||||
}
|
||||
.-item-tilte {
|
||||
background: $aider-light-color;
|
||||
height: 60rpx;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-size: 28rpx;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
line-height: 60rpx;
|
||||
}
|
||||
|
||||
.view-list {
|
||||
@@ -66,10 +71,11 @@ defineProps<{ res: any }>();
|
||||
background: #fff;
|
||||
border-top-left-radius: 0.8em;
|
||||
border-top-right-radius: 0.8em;
|
||||
border: 1px solid #ededed;
|
||||
|
||||
border: 2rpx solid #ededed;
|
||||
|
||||
> .view-item {
|
||||
width: 50%;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
> .view-item:nth-of-type(1) {
|
||||
> .-item-tilte {
|
||||
@@ -82,4 +88,4 @@ defineProps<{ res: any }>();
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,50 +1,149 @@
|
||||
<template>
|
||||
<div class="layout" :style="{textAlign: res.list[0].textAlign}" @click="modelNavigateTo(res.list[0])" >
|
||||
<div class="background" :style="{ backgroundColor: res.list[0].bk_color}">
|
||||
<div class="title" :style="{ color: res.list[0].color }">
|
||||
{{ res.list[0].title }}
|
||||
</div>
|
||||
<div style="position: absolute;right: 10px;top:2px;color: #fff;line-height: 42px;font-size: 10px">
|
||||
<a :style="{ color: res.list[0].color1 }" style="text-decoration: none">{{ res.list[0].title1 }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
|
||||
defineProps<{ res: any }>();
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
.background {
|
||||
// background: url("/static/title.png") no-repeat;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: 84rpx;
|
||||
background-position-x: center;
|
||||
background-position-y: center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.layout {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
height: 84rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.title {
|
||||
line-height: 84rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<view class="layout" :style="wrapperStyle">
|
||||
<!-- 新样式:左标题+副标题,右侧更多 -->
|
||||
<view v-if="styled" class="title-bar" :style="barStyle">
|
||||
<view class="title-left">
|
||||
<text class="title-main" :style="mainStyle">{{ item.title }}</text>
|
||||
<text class="title-sub" :style="subStyle" v-if="item.title1">{{ item.title1 }}</text>
|
||||
</view>
|
||||
<view
|
||||
v-if="item.showMore"
|
||||
class="title-more"
|
||||
:style="moreStyle"
|
||||
@click="goMore"
|
||||
>
|
||||
{{ item.moreText || "更多" }} >
|
||||
</view>
|
||||
</view>
|
||||
<!-- 老数据:保持原样 -->
|
||||
<view
|
||||
v-else
|
||||
class="background"
|
||||
:style="{ backgroundColor: item.bk_color, textAlign: item.textAlign }"
|
||||
@click="modelNavigateTo(item)"
|
||||
>
|
||||
<view class="title" :style="{ color: item.color }">{{ item.title }}</view>
|
||||
<view class="legacy-sub">
|
||||
<a :style="{ color: item.color1 }" style="text-decoration: none">{{ item.title1 }}</a>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const item = computed(() => props.res?.list?.[0] || {});
|
||||
|
||||
// 面板保存过样式字段才启用新样式,老数据保持原样
|
||||
const styled = computed(() => props.res && props.res.paddingTop !== undefined);
|
||||
|
||||
const wrapperStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
if (!styled.value) return {};
|
||||
return {
|
||||
padding: `${Number(res.paddingTop) || 0}rpx ${Number(res.paddingX) || 0}rpx ${Number(res.paddingBottom) || 0}rpx`,
|
||||
marginBottom: `${Number(res.pageGap) || 0}rpx`,
|
||||
borderRadius: `${Number(res.radiusTop) || 0}rpx ${Number(res.radiusTop) || 0}rpx ${Number(res.radiusBottom) || 0}rpx ${Number(res.radiusBottom) || 0}rpx`,
|
||||
overflow:
|
||||
Number(res.radiusTop) > 0 || Number(res.radiusBottom) > 0 ? "hidden" : "visible",
|
||||
background: "#ffffff",
|
||||
boxSizing: "border-box",
|
||||
};
|
||||
});
|
||||
|
||||
const barStyle = computed(() => {
|
||||
const it = item.value;
|
||||
if (it.bgType === "image" && it.bgImage) {
|
||||
return {
|
||||
backgroundImage: `url(${it.bgImage})`,
|
||||
backgroundSize: "cover",
|
||||
backgroundPosition: "center",
|
||||
};
|
||||
}
|
||||
return { backgroundColor: it.bk_color || "#FFFFFF" };
|
||||
});
|
||||
|
||||
const mainStyle = computed(() => ({
|
||||
color: item.value.color || "#282828",
|
||||
fontSize: `${(Number(item.value.fontSize) || 20) * 2}rpx`,
|
||||
fontWeight: item.value.textStyle === "bold" ? "bold" : "normal",
|
||||
fontStyle: item.value.textStyle === "italic" ? "italic" : "normal",
|
||||
}));
|
||||
const subStyle = computed(() => ({
|
||||
color: item.value.color1 || "#282828",
|
||||
fontSize: `${(Number(item.value.subFontSize) || 14) * 2}rpx`,
|
||||
}));
|
||||
const moreStyle = computed(() => ({
|
||||
color: item.value.moreColor || "#282828",
|
||||
fontSize: `${(Number(item.value.moreFontSize) || 12) * 2}rpx`,
|
||||
}));
|
||||
|
||||
function goMore() {
|
||||
if (item.value.moreUrl) {
|
||||
modelNavigateTo({ url: item.value.moreUrl });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
.layout {
|
||||
background: #ffffff;
|
||||
}
|
||||
.title-bar {
|
||||
width: 100%;
|
||||
min-height: 84rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 24rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.title-left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
overflow: hidden;
|
||||
}
|
||||
.title-main {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.title-sub {
|
||||
margin-left: 12rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.title-more {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.background {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: 84rpx;
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.legacy-sub {
|
||||
position: absolute;
|
||||
right: 20rpx;
|
||||
top: 4rpx;
|
||||
color: #fff;
|
||||
line-height: 84rpx;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
.title {
|
||||
line-height: 84rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="view-width-100" @click="modelNavigateTo(res.list[0])">
|
||||
<div class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<div class="view-width-100" :style="imgStyle" @click="modelNavigateTo(res.list[0])">
|
||||
<u-image class="image-mode" width="100%" height="200rpx" :src="res.list[0].img">
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</div>
|
||||
<div class="view-width-100" @click="modelNavigateTo(res.list[1])">
|
||||
<div class="view-height-85">
|
||||
<div class="view-width-100" :style="gapStyle" @click="modelNavigateTo(res.list[1])">
|
||||
<div class="view-height-85" :style="imgStyle">
|
||||
<u-image class="image-mode" width="100%" height="170rpx" :src="res.list[1].img">
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</div>
|
||||
<div class="view-height-85" @click="modelNavigateTo(res.list[2])">
|
||||
<div class="view-height-85" :style="imgStyle" @click="modelNavigateTo(res.list[2])">
|
||||
<u-image class="image-mode" width="100%" height="170rpx" :src="res.list[2].img">
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
@@ -22,9 +22,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
import { computed } from "vue";
|
||||
import { modelNavigateTo, cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./tpl";
|
||||
|
||||
defineProps<{ res: any }>();
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const wrapperStyle = computed(() => cubeWrapperStyle(props.res));
|
||||
const imgStyle = computed(() => cubeImgStyle(props.res));
|
||||
const gapStyle = computed(() => cubeGapStyle(props.res));
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
@@ -41,16 +46,17 @@ defineProps<{ res: any }>();
|
||||
// height: 85px !important;
|
||||
// }
|
||||
.view-width-100 {
|
||||
padding: 1px 0;
|
||||
padding: 2rpx 0;
|
||||
display: flex;
|
||||
height: 200rpx;
|
||||
width: 100%;
|
||||
}
|
||||
.view-height-85 {
|
||||
padding: 0 1px;
|
||||
width: 50%;
|
||||
padding: 0 2rpx;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.image-mode{
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="view-width-100">
|
||||
<div class="view-height-85" @click="modelNavigateTo(res.list[0])">
|
||||
<div class="layout" :style="[wrapperStyle, gapStyle]">
|
||||
<div class="view-width-100" :style="gapStyle">
|
||||
<div class="view-height-85" :style="imgStyle" @click="modelNavigateTo(res.list[0])">
|
||||
<u-image
|
||||
class="image-mode"
|
||||
height="170rpx"
|
||||
@@ -12,7 +12,7 @@
|
||||
<template #loading><u-loading-icon></u-loading-icon></template>
|
||||
</u-image>
|
||||
</div>
|
||||
<div class="view-height-85" @click="modelNavigateTo(res.list[1])">
|
||||
<div class="view-height-85" :style="imgStyle" @click="modelNavigateTo(res.list[1])">
|
||||
<u-image
|
||||
class="image-mode"
|
||||
height="170rpx"
|
||||
@@ -24,7 +24,7 @@
|
||||
</u-image>
|
||||
</div>
|
||||
</div>
|
||||
<div class="view-width-100" @click="modelNavigateTo(res.list[2])">
|
||||
<div class="view-width-100" :style="imgStyle" @click="modelNavigateTo(res.list[2])">
|
||||
<u-image
|
||||
class="image-mode"
|
||||
height="200rpx"
|
||||
@@ -38,9 +38,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { modelNavigateTo } from "./tpl";
|
||||
import { computed } from "vue";
|
||||
import { modelNavigateTo, cubeWrapperStyle, cubeImgStyle, cubeGapStyle } from "./tpl";
|
||||
|
||||
defineProps<{ res: any }>();
|
||||
const props = defineProps<{ res: any }>();
|
||||
|
||||
const wrapperStyle = computed(() => cubeWrapperStyle(props.res));
|
||||
const imgStyle = computed(() => cubeImgStyle(props.res));
|
||||
const gapStyle = computed(() => cubeGapStyle(props.res));
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./tpl.scss";
|
||||
@@ -57,14 +62,15 @@ defineProps<{ res: any }>();
|
||||
// height: 85px !important;
|
||||
// }
|
||||
.view-width-100 {
|
||||
padding: 1px 0;
|
||||
padding: 2rpx 0;
|
||||
display: flex;
|
||||
height: 200rpx;
|
||||
width: 100%;
|
||||
}
|
||||
.view-height-85 {
|
||||
padding: 0 1px;
|
||||
width: 50%;
|
||||
padding: 0 2rpx;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.image-mode{
|
||||
width: 100%;
|
||||
|
||||
@@ -91,7 +91,26 @@ const item = computed(() => {
|
||||
return data;
|
||||
});
|
||||
|
||||
// 面板保存过样式字段(options 级)才启用新样式,老数据走 item.style 体系
|
||||
const styled = computed(() => props.res && props.res.paddingTop !== undefined);
|
||||
|
||||
const wrapperStyle = computed(() => {
|
||||
const res = props.res || {};
|
||||
if (styled.value) {
|
||||
const bg =
|
||||
!res.bgColor2 || res.bgColor1 === res.bgColor2
|
||||
? res.bgColor1
|
||||
: `linear-gradient(180deg, ${res.bgColor1}, ${res.bgColor2})`;
|
||||
const radius = Number(res.borderRadius) || 0;
|
||||
return {
|
||||
padding: `${Number(res.paddingTop) || 0}rpx ${Number(res.paddingX) || 0}rpx ${Number(res.paddingBottom) || 0}rpx`,
|
||||
background: bg,
|
||||
borderRadius: `${radius}rpx`,
|
||||
marginBottom: `${Number(res.pageGap) || 0}rpx`,
|
||||
overflow: radius > 0 ? "hidden" : "visible",
|
||||
boxSizing: "border-box",
|
||||
};
|
||||
}
|
||||
const style = item.value.style;
|
||||
return {
|
||||
margin: `${style.margin || 0}px`,
|
||||
@@ -102,6 +121,14 @@ const wrapperStyle = computed(() => {
|
||||
});
|
||||
|
||||
const boxStyle = computed(() => {
|
||||
if (styled.value) {
|
||||
return {
|
||||
borderRadius: `${Number(props.res.imgRadius) || 0}rpx`,
|
||||
overflow: "hidden",
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
};
|
||||
}
|
||||
const style = item.value.style;
|
||||
const box: Record<string, string> = {
|
||||
borderRadius: `${style.bgRadius || 0}px`,
|
||||
|
||||
Reference in New Issue
Block a user