feat: 相关商品交互调整
This commit is contained in:
@@ -1,221 +1,348 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="swipe-cards border-box" @touchmove.stop>
|
<view class="card border-box pb-12 relative">
|
||||||
<view
|
<view
|
||||||
v-for="(card, index) in visibleCards"
|
class="card-item"
|
||||||
:key="`${card.commodityId}-${index}`"
|
v-for="(card, index) in list"
|
||||||
class="card"
|
:key="card.__uid"
|
||||||
:style="getCardStyle(index, card)"
|
:style="[itemStyle(index, card), transformStyle(index, card)]"
|
||||||
@touchstart.stop.capture="handleTouchStart($event, index)"
|
@touchstart.stop="touchStart($event, index)"
|
||||||
@touchmove.stop.capture="handleTouchMove($event, index)"
|
@touchmove.stop.prevent="touchMove($event, index)"
|
||||||
@touchend.stop.capture="handleTouchEnd($event, index)"
|
@touchend.stop="touchEnd(index)"
|
||||||
|
@touchcancel.stop="touchCancel(index)"
|
||||||
|
@transitionend="onTransitionEnd(index)"
|
||||||
>
|
>
|
||||||
<slot :card="card" />
|
<view
|
||||||
|
class="inner-card overflow-hidden"
|
||||||
|
@click.stop.prevent.capture="placeOrderHandle(card)"
|
||||||
|
>
|
||||||
|
<!-- 商品大图部分:自适应剩余空间 -->
|
||||||
|
<view class="goods-image-wrapper">
|
||||||
|
<image
|
||||||
|
class="goods-image"
|
||||||
|
:src="card.commodityPhoto"
|
||||||
|
mode="aspectFill"
|
||||||
|
/>
|
||||||
|
<view class="goods-title">
|
||||||
|
<text class="goods-text">{{ card.commodityName }}</text>
|
||||||
|
<view class="card-price-row">
|
||||||
|
<text class="card-price-fu">¥</text>
|
||||||
|
<text class="card-price">{{ card.specificationPrice }}</text>
|
||||||
|
<text class="card-unit" v-if="card.stockUnitLabel"
|
||||||
|
>/{{ card.stockUnitLabel }}</text
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部相册部分:固定比例或高度 -->
|
||||||
|
<view class="album-row">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in card.commodityPhotoList"
|
||||||
|
:key="index"
|
||||||
|
class="album-item"
|
||||||
|
>
|
||||||
|
<view v-if="item" class="album-image-wrapper">
|
||||||
|
<image
|
||||||
|
class="album-image"
|
||||||
|
:src="item.photoUrl"
|
||||||
|
mode="aspectFill"
|
||||||
|
/>
|
||||||
|
<view class="album-title">{{ item.photoName }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, nextTick, defineProps } from "vue";
|
import { ref, watch } from "vue";
|
||||||
|
import { checkToken } from "@/hooks/useGoLogin";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
cardsData: { type: Array, default: () => [] },
|
cardsData: { type: Array, default: () => [] },
|
||||||
threshold: { type: Number, default: 100 },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const cards = ref([...props.cardsData]);
|
const DURATION = 300;
|
||||||
const startX = ref(0);
|
const swipering = ref(false);
|
||||||
const startY = ref(0);
|
const animatingOut = ref(false);
|
||||||
const moveX = ref(0);
|
let reorderTimer = null;
|
||||||
const moveY = ref(0);
|
|
||||||
const currentIndex = ref(null);
|
|
||||||
const isHorizontal = ref(false);
|
|
||||||
|
|
||||||
// 使用对象存储卡片状态,减少响应式数据
|
const { windowWidth } = uni.getWindowInfo();
|
||||||
const cardStates = ref({});
|
|
||||||
|
|
||||||
// 节流控制
|
let uidCounter = 0;
|
||||||
let lastMoveTime = 0;
|
// 始终生成全局唯一的 __uid,避免因重复 key 导致后续卡片无法正确重渲染与绑定事件
|
||||||
const THROTTLE_DELAY = 8; // 8ms 节流,比16ms更流畅
|
const genUid = (item) => `swipe_${item?.commodityId ?? 'unknown'}_${uidCounter++}_${Date.now()}`;
|
||||||
|
const normalize = (item) => ({
|
||||||
|
...item,
|
||||||
|
__uid: genUid(item),
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
opacity: 1,
|
||||||
|
});
|
||||||
|
|
||||||
const visibleCards = computed(() => cards.value.slice(0, 3));
|
// 循环队列(全量),堆栈(仅前3张)
|
||||||
|
const queue = ref((props.cardsData || []).map(normalize));
|
||||||
|
const list = ref(queue.value.slice(0, 3));
|
||||||
|
const updateStack = () => {
|
||||||
|
list.value = queue.value.slice(0, 3);
|
||||||
|
};
|
||||||
|
|
||||||
// 预计算样式常量
|
watch(
|
||||||
const BASE_SCALE = 1;
|
() => props.cardsData,
|
||||||
const SCALE_STEP = 0.05;
|
(val) => {
|
||||||
const TRANSLATE_Y_STEP = 28;
|
queue.value = (val || []).map(normalize);
|
||||||
const ROTATE_RATIO = 24;
|
updateStack();
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
function handleTouchStart(e, index) {
|
// 触摸状态
|
||||||
const touch = e.touches[0];
|
const touchState = ref({ startX: 0, startY: 0, moving: false });
|
||||||
startX.value = touch.clientX;
|
|
||||||
startY.value = touch.clientY;
|
|
||||||
currentIndex.value = index;
|
|
||||||
isHorizontal.value = false;
|
|
||||||
|
|
||||||
// 初始化卡片状态
|
const touchStart = (e, index) => {
|
||||||
if (!cardStates.value[index]) {
|
if (index !== 0 || animatingOut.value) return;
|
||||||
cardStates.value[index] = {
|
const t = e.changedTouches?.[0];
|
||||||
swiped: false,
|
if (!t) return;
|
||||||
direction: 0,
|
touchState.value.startX = t.clientX;
|
||||||
transform: "",
|
touchState.value.startY = t.clientY;
|
||||||
opacity: 1,
|
touchState.value.moving = true;
|
||||||
transition: "none",
|
swipering.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const touchMove = (e, index) => {
|
||||||
|
if (index !== 0 || !touchState.value.moving || animatingOut.value) return;
|
||||||
|
const t = e.changedTouches?.[0];
|
||||||
|
if (!t) return;
|
||||||
|
const dx = t.clientX - touchState.value.startX;
|
||||||
|
const dy = t.clientY - touchState.value.startY;
|
||||||
|
const top = list.value[0];
|
||||||
|
if (!top) return;
|
||||||
|
top.x = dx;
|
||||||
|
top.y = dy;
|
||||||
|
};
|
||||||
|
|
||||||
|
const finalizeReorder = () => {
|
||||||
|
const top = queue.value[0];
|
||||||
|
if (!top) return;
|
||||||
|
const moved = { ...top, x: 0, y: 0, opacity: 1 };
|
||||||
|
queue.value = [...queue.value.slice(1), moved];
|
||||||
|
updateStack();
|
||||||
|
animatingOut.value = false;
|
||||||
|
if (reorderTimer) {
|
||||||
|
clearTimeout(reorderTimer);
|
||||||
|
reorderTimer = null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
function handleTouchMove(e, index) {
|
const touchEnd = (index) => {
|
||||||
if (currentIndex.value !== index) return;
|
if (index !== 0 || !touchState.value.moving) return;
|
||||||
|
touchState.value.moving = false;
|
||||||
const now = Date.now();
|
swipering.value = false;
|
||||||
if (now - lastMoveTime < THROTTLE_DELAY) return;
|
const top = list.value[0];
|
||||||
lastMoveTime = now;
|
if (!top) return;
|
||||||
|
const threshold = windowWidth / 4;
|
||||||
const touch = e.touches[0];
|
if (Math.abs(top.x) > threshold) {
|
||||||
const deltaX = touch.clientX - startX.value;
|
const direction = top.x > 0 ? 1 : -1;
|
||||||
const deltaY = touch.clientY - startY.value;
|
animatingOut.value = true;
|
||||||
|
top.x = direction * (windowWidth + 100);
|
||||||
// 首次移动判断方向
|
top.opacity = 0;
|
||||||
if (!isHorizontal.value) {
|
if (reorderTimer) {
|
||||||
const absX = Math.abs(deltaX);
|
clearTimeout(reorderTimer);
|
||||||
const absY = Math.abs(deltaY);
|
reorderTimer = null;
|
||||||
if (absX > 10 || absY > 10) {
|
|
||||||
isHorizontal.value = absX > absY;
|
|
||||||
}
|
}
|
||||||
}
|
reorderTimer = setTimeout(() => {
|
||||||
|
if (animatingOut.value) finalizeReorder();
|
||||||
if (isHorizontal.value) {
|
}, DURATION + 40);
|
||||||
e.preventDefault?.();
|
|
||||||
moveX.value = deltaX;
|
|
||||||
moveY.value = deltaY;
|
|
||||||
|
|
||||||
// 更新当前卡片的transform,避免重复计算
|
|
||||||
updateCardTransform(index, deltaX, deltaY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTouchEnd(e, index) {
|
|
||||||
if (!isHorizontal.value) return;
|
|
||||||
|
|
||||||
const absX = Math.abs(moveX.value);
|
|
||||||
const absY = Math.abs(moveY.value);
|
|
||||||
|
|
||||||
if (absX > props.threshold && absX >= absY) {
|
|
||||||
const direction = moveX.value > 0 ? 1 : -1;
|
|
||||||
animateSwipe(index, direction);
|
|
||||||
} else {
|
} else {
|
||||||
resetCard(index);
|
// 回弹复位
|
||||||
|
top.x = 0;
|
||||||
|
top.y = 0;
|
||||||
|
top.opacity = 1;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 重置状态
|
const touchCancel = (index) => {
|
||||||
moveX.value = 0;
|
if (index !== 0) return;
|
||||||
moveY.value = 0;
|
const top = list.value[0];
|
||||||
currentIndex.value = null;
|
if (!top) return;
|
||||||
isHorizontal.value = false;
|
touchState.value.moving = false;
|
||||||
}
|
swipering.value = false;
|
||||||
|
top.x = 0;
|
||||||
|
top.y = 0;
|
||||||
|
top.opacity = 1;
|
||||||
|
};
|
||||||
|
|
||||||
function updateCardTransform(index, deltaX, deltaY) {
|
const onTransitionEnd = (index) => {
|
||||||
const state = cardStates.value[index];
|
if (index !== 0) return;
|
||||||
if (!state) return;
|
if (!animatingOut.value) return;
|
||||||
|
finalizeReorder();
|
||||||
const rotate = deltaX / ROTATE_RATIO;
|
};
|
||||||
state.transform = `translate(${deltaX}px, ${deltaY}px) rotate(${rotate}deg)`;
|
|
||||||
state.transition = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
function animateSwipe(index, direction) {
|
|
||||||
const state = cardStates.value[index];
|
|
||||||
if (!state) return;
|
|
||||||
|
|
||||||
state.swiped = true;
|
|
||||||
state.direction = direction;
|
|
||||||
state.transform = `translate(${direction * 500}px, ${moveY.value}px) rotate(${
|
|
||||||
direction * 45
|
|
||||||
}deg)`;
|
|
||||||
state.opacity = 0;
|
|
||||||
state.transition = "all 0.3s ease-out";
|
|
||||||
|
|
||||||
setTimeout(async () => {
|
|
||||||
const removed = cards.value.splice(index, 1)[0];
|
|
||||||
removed.swiped = false;
|
|
||||||
removed.direction = 0;
|
|
||||||
cards.value.push(removed);
|
|
||||||
|
|
||||||
// 清理状态
|
|
||||||
delete cardStates.value[index];
|
|
||||||
await nextTick();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetCard(index) {
|
|
||||||
const state = cardStates.value[index];
|
|
||||||
if (!state) return;
|
|
||||||
|
|
||||||
state.swiped = false;
|
|
||||||
state.direction = 0;
|
|
||||||
state.transform = "";
|
|
||||||
state.transition = "transform 0.2s ease-out";
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCardStyle(index, card) {
|
|
||||||
// 如果有触摸状态,使用预计算的样式
|
|
||||||
if (cardStates.value[index] && currentIndex.value === index) {
|
|
||||||
const state = cardStates.value[index];
|
|
||||||
return {
|
|
||||||
zIndex: 3 - index,
|
|
||||||
transform: state.transform || "translate(0, 0)",
|
|
||||||
opacity: state.opacity,
|
|
||||||
transition: state.transition,
|
|
||||||
willChange: "transform, opacity",
|
|
||||||
backfaceVisibility: "hidden",
|
|
||||||
transformStyle: "preserve-3d",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 静态卡片使用简化计算
|
|
||||||
const zIndex = 3 - index;
|
|
||||||
const scale = BASE_SCALE - index * SCALE_STEP;
|
|
||||||
const translateY = index * TRANSLATE_Y_STEP;
|
|
||||||
const opacity = card.swiped ? 0 : 1;
|
|
||||||
|
|
||||||
let transform = `translate(0, ${translateY}rpx) scale(${scale})`;
|
|
||||||
|
|
||||||
if (card.swiped) {
|
|
||||||
transform = `translate(${card.direction * 500}px, 0) rotate(${
|
|
||||||
card.direction * 45
|
|
||||||
}deg)`;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 栈样式:层级、基础位移缩放、过渡时长
|
||||||
|
const itemStyle = (index, card) => {
|
||||||
|
const zIndex = list.value.length - index;
|
||||||
|
const duration = swipering.value ? "0ms" : `${DURATION}ms`;
|
||||||
|
const opacity = card.opacity;
|
||||||
return {
|
return {
|
||||||
zIndex,
|
zIndex,
|
||||||
transform,
|
|
||||||
opacity,
|
opacity,
|
||||||
transition: card.swiped ? "all 0.3s ease-out" : "transform 0.2s ease-out",
|
transition: `transform ${duration} ease, opacity ${duration} ease`,
|
||||||
willChange: "transform, opacity",
|
|
||||||
backfaceVisibility: "hidden",
|
|
||||||
transformStyle: "preserve-3d",
|
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// 变换样式:顶部卡动态位移/旋转,后续卡预览层级
|
||||||
|
const transformStyle = (index, card) => {
|
||||||
|
if (index === 0) {
|
||||||
|
const deg = card.x / 20;
|
||||||
|
return {
|
||||||
|
transform: `translate3d(${card.x}px, ${card.y}px, 0) rotate(${deg}deg)`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// 预览层:轻微位移与缩放,确保连贯顶上
|
||||||
|
const previewScales = [1, 0.98, 0.96];
|
||||||
|
const previewOffsets = [0, 8, 16];
|
||||||
|
const scale = previewScales[index] ?? 0.94;
|
||||||
|
const y = previewOffsets[index] ?? 24;
|
||||||
|
return {
|
||||||
|
transform: `translate3d(0, ${y}px, 0) scale(${scale})`,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 去下单
|
||||||
|
const placeOrderHandle = (item) => {
|
||||||
|
checkToken().then(() => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/goods/index?commodityId=${item.commodityId}`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped>
|
||||||
.swipe-cards {
|
.card {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 320px;
|
height: 320px;
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 6px 0 12px;
|
|
||||||
will-change: transform; // 启用硬件加速
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card-item {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
inset: 0;
|
||||||
|
will-change: transform, opacity;
|
||||||
height: calc(100% - 30px);
|
height: calc(100% - 30px);
|
||||||
border-radius: 20px;
|
|
||||||
box-shadow: 0 8px 8px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 8px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-card {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
will-change: transform, opacity; // 启用硬件加速
|
border-radius: 20px;
|
||||||
backface-visibility: hidden; // 减少重绘
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 商品大图部分:撑满除相册外的空间 */
|
||||||
|
.goods-image-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 335 / 200;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-title {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
padding: 12px;
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(0, 0, 0, 0) 0%,
|
||||||
|
rgba(0, 0, 0, 0.6) 100%
|
||||||
|
);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-text {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-price-fu {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-price {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-unit {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: normal;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-shrink: 0;
|
||||||
|
aspect-ratio: 335 / 84;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-item {
|
||||||
|
width: calc((100% - 24px) / 3);
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-image-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.album-title {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 8px;
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(0, 0, 0, 0) 0%,
|
||||||
|
rgba(0, 0, 0, 0.6) 100%
|
||||||
|
);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -2,57 +2,12 @@
|
|||||||
<view class="container">
|
<view class="container">
|
||||||
<ModuleTitle title="相关商品" />
|
<ModuleTitle title="相关商品" />
|
||||||
|
|
||||||
<SwipeCards :cardsData="commodityList">
|
<SwipeCards :cardsData="commodityList" />
|
||||||
<template #default="{ card }">
|
|
||||||
<view
|
|
||||||
class="inner-card overflow-hidden"
|
|
||||||
@click.stop.prevent.capture="placeOrderHandle(card)"
|
|
||||||
>
|
|
||||||
<!-- 商品大图部分:自适应剩余空间 -->
|
|
||||||
<view class="goods-image-wrapper">
|
|
||||||
<image
|
|
||||||
class="goods-image"
|
|
||||||
:src="card.commodityPhoto"
|
|
||||||
mode="aspectFill"
|
|
||||||
/>
|
|
||||||
<view class="goods-title">
|
|
||||||
<text class="goods-text">{{ card.commodityName }}</text>
|
|
||||||
<view class="card-price-row">
|
|
||||||
<text class="card-price-fu">¥</text>
|
|
||||||
<text class="card-price">{{ card.specificationPrice }}</text>
|
|
||||||
<text class="card-unit" v-if="card.stockUnitLabel"
|
|
||||||
>/{{ card.stockUnitLabel }}</text
|
|
||||||
>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 底部相册部分:固定比例或高度 -->
|
|
||||||
<view class="album-row">
|
|
||||||
<view
|
|
||||||
v-for="(item, index) in card.commodityPhotoList"
|
|
||||||
:key="index"
|
|
||||||
class="album-item"
|
|
||||||
>
|
|
||||||
<view v-if="item" class="album-image-wrapper">
|
|
||||||
<image
|
|
||||||
class="album-image"
|
|
||||||
:src="item.photoUrl"
|
|
||||||
mode="aspectFill"
|
|
||||||
/>
|
|
||||||
<view class="album-title">{{ item.photoName }}</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
</SwipeCards>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, computed } from "vue";
|
import { defineProps } from "vue";
|
||||||
import { checkToken } from "@/hooks/useGoLogin";
|
|
||||||
import ModuleTitle from "@/components/ModuleTitle/index.vue";
|
import ModuleTitle from "@/components/ModuleTitle/index.vue";
|
||||||
import SwipeCards from "@/components/SwipeCards/index.vue";
|
import SwipeCards from "@/components/SwipeCards/index.vue";
|
||||||
|
|
||||||
@@ -62,129 +17,8 @@ const props = defineProps({
|
|||||||
default: [],
|
default: [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 去下单
|
|
||||||
const placeOrderHandle = (item) => {
|
|
||||||
checkToken().then(() => {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: `/pages/goods/index?commodityId=${item.commodityId}`,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "./styles/index.scss";
|
@import "./styles/index.scss";
|
||||||
|
|
||||||
.inner-card {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 20px;
|
|
||||||
flex-direction: column;
|
|
||||||
// overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 商品大图部分:撑满除相册外的空间 */
|
|
||||||
.goods-image-wrapper {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
aspect-ratio: 335 / 200;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-image {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-title {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
padding: 12px;
|
|
||||||
background: linear-gradient(
|
|
||||||
180deg,
|
|
||||||
rgba(0, 0, 0, 0) 0%,
|
|
||||||
rgba(0, 0, 0, 0.6) 100%
|
|
||||||
);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-text {
|
|
||||||
color: #fff;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-price-fu {
|
|
||||||
color: #fff;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-price {
|
|
||||||
color: #fff;
|
|
||||||
font-size: $uni-font-size-lg;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.card-unit {
|
|
||||||
font-size: 11px;
|
|
||||||
color: #fff;
|
|
||||||
font-weight: normal;
|
|
||||||
margin-left: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.album-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 12px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
flex-shrink: 0;
|
|
||||||
aspect-ratio: 335 / 84;
|
|
||||||
}
|
|
||||||
|
|
||||||
.album-item {
|
|
||||||
width: calc((100% - 24px) / 3);
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 10px;
|
|
||||||
overflow: hidden;
|
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.album-image-wrapper {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.album-image {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.album-title {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: 8px;
|
|
||||||
background: linear-gradient(
|
|
||||||
180deg,
|
|
||||||
rgba(0, 0, 0, 0) 0%,
|
|
||||||
rgba(0, 0, 0, 0.6) 100%
|
|
||||||
);
|
|
||||||
color: #fff;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user