Compare commits
22 Commits
2d63fbd136
...
b2ad6403c1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2ad6403c1 | ||
|
|
5cb3a903ff | ||
|
|
a3087c09a8 | ||
|
|
a7348c2002 | ||
|
|
921108ee6a | ||
|
|
84f169f792 | ||
|
|
bf5224dabe | ||
|
|
ac8baa7f95 | ||
|
|
0d554eeffc | ||
|
|
0eb4fa876c | ||
|
|
6d4887aa90 | ||
|
|
b7a3eb39ef | ||
|
|
21e64ba6a6 | ||
|
|
213c58e5c5 | ||
|
|
0bbd5a912e | ||
|
|
2527cc5004 | ||
|
|
0758179b93 | ||
|
|
217ee3228a | ||
|
|
66ff10018f | ||
|
|
19c21d2510 | ||
|
|
fad25e951c | ||
|
|
e81b7e662a |
@@ -5,7 +5,15 @@
|
||||
"name": "智念",
|
||||
"placeholder": "快告诉智念您在想什么~",
|
||||
"loginDesc": "您好,欢迎来到智念科技",
|
||||
"logo": "https://oss.nianxx.cn/mp/static/version_101/login/dh_logo.png"
|
||||
"logo": "https://oss.nianxx.cn/mp/static/version_101/login/dh_logo.png",
|
||||
"ipLargeImage": "https://oss.nianxx.cn/mp/static/version_101/dh/dh_large.png",
|
||||
"ipSmallImage": "https://oss.nianxx.cn/mp/static/version_101/dh/dh_small.png",
|
||||
"ipLargeImageHeight": 19687,
|
||||
"ipSmallImageHeight": 3744,
|
||||
"ipLargeImageStep": 147,
|
||||
"ipSmallImageStep": 117,
|
||||
"ipLargeTime": 4,
|
||||
"ipSmallTime": 4
|
||||
},
|
||||
"duohua": {
|
||||
"clientId": "2",
|
||||
@@ -13,7 +21,15 @@
|
||||
"name": "朵朵",
|
||||
"placeholder": "快告诉朵朵您在想什么~",
|
||||
"loginDesc": "您好,欢迎来到朵花温泉",
|
||||
"logo": "https://oss.nianxx.cn/mp/static/version_101/login/dh_logo.png"
|
||||
"logo": "https://oss.nianxx.cn/mp/static/version_101/login/dh_logo.png",
|
||||
"ipLargeImage": "https://oss.nianxx.cn/mp/static/version_101/dh/dh_large.png",
|
||||
"ipSmallImage": "https://oss.nianxx.cn/mp/static/version_101/dh/dh_small.png",
|
||||
"ipLargeImageHeight": 19687,
|
||||
"ipSmallImageHeight": 3744,
|
||||
"ipLargeImageStep": 147,
|
||||
"ipSmallImageStep": 117,
|
||||
"ipLargeTime": 4,
|
||||
"ipSmallTime": 4
|
||||
},
|
||||
"tianmu": {
|
||||
"clientId": "4",
|
||||
@@ -21,6 +37,14 @@
|
||||
"name": "沐沐",
|
||||
"placeholder": "快告诉沐沐您在想什么~",
|
||||
"loginDesc": "您好,欢迎来到天沐温泉",
|
||||
"logo": "https://oss.nianxx.cn/mp/static/version_101/login/tm_logo.png"
|
||||
"logo": "https://oss.nianxx.cn/mp/static/version_101/login/tm_logo.png",
|
||||
"ipLargeImage": "https://oss.nianxx.cn/mp/static/version_101/tm/tm_large.png",
|
||||
"ipSmallImage": "https://oss.nianxx.cn/mp/static/version_101/tm/tm_small.png",
|
||||
"ipLargeImageHeight": 9514,
|
||||
"ipSmallImageHeight": 4736,
|
||||
"ipLargeImageStep": 71,
|
||||
"ipSmallImageStep": 148,
|
||||
"ipLargeTime": 4,
|
||||
"ipSmallTime": 6
|
||||
}
|
||||
}
|
||||
|
||||
2175
package-lock.json
generated
2175
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -131,20 +131,14 @@ const workOrderId = ref(0); // 工单ID
|
||||
|
||||
// 处理图片上传
|
||||
const handleChooseImage = () => {
|
||||
console.log("选择图片");
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: (res) => {
|
||||
const file = res.tempFilePaths[0];
|
||||
updateImagehandle(file);
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error("选择图片失败:", err);
|
||||
uni.showToast({
|
||||
title: "选择图片失败",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
fail: () => {
|
||||
uni.showToast({ title: "选择图片失败", icon: "none" });
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -169,21 +163,18 @@ const handleCall = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!roomId.value.trim()) {
|
||||
uni.showToast({ title: "请填写房间号", icon: "none" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!contactPhone.value.trim()) {
|
||||
uni.showToast({
|
||||
title: "请填写联系电话",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
uni.showToast({ title: "请填写联系电话", icon: "none" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!contactText.value.trim()) {
|
||||
uni.showToast({
|
||||
title: "请填写需求信息描述内容",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
uni.showToast({ title: "请填写需求信息描述内容", icon: "none" });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -208,31 +199,17 @@ const sendCreateWorkOrder = async () => {
|
||||
// 设置成功状态
|
||||
isCallSuccess.value = true;
|
||||
|
||||
uni.showToast({
|
||||
title: "呼叫成功",
|
||||
icon: "success",
|
||||
duration: 2000,
|
||||
});
|
||||
uni.showToast({ title: "呼叫成功", icon: "success" });
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res.message || "呼叫失败",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
uni.showToast({ title: res.message || "呼叫失败", icon: "none" });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("呼叫失败:", error);
|
||||
uni.showToast({
|
||||
title: "网络错误,请重试",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
uni.showToast({ title: "网络错误,请重试", icon: "none" });
|
||||
}
|
||||
};
|
||||
|
||||
// 查看工单
|
||||
const viewWorkOrder = () => {
|
||||
console.log("查看工单:", workOrderId.value);
|
||||
// 这里可以跳转到工单详情页面
|
||||
uni.navigateTo({
|
||||
url: `/pages-service/order/list`,
|
||||
|
||||
@@ -75,20 +75,12 @@ const appName = computed(() => getCurrentConfig().name);
|
||||
|
||||
const handleCall = async () => {
|
||||
if (!contactPhone.value.trim()) {
|
||||
uni.showToast({
|
||||
title: "请填写联系电话",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
uni.showToast({ title: "请填写联系电话", icon: "none" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!contactText.value.trim()) {
|
||||
uni.showToast({
|
||||
title: "请填写意见内容",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
uni.showToast({ title: "请填写意见内容", icon: "none" });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -110,13 +102,11 @@ const sendFeedback = async () => {
|
||||
uni.showToast({
|
||||
title: "反馈意见成功",
|
||||
icon: "success",
|
||||
duration: 2000,
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: res.message || "反馈意见失败",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -124,7 +114,6 @@ const sendFeedback = async () => {
|
||||
uni.showToast({
|
||||
title: "网络错误,请重试",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<view class="mt-16">
|
||||
<view v-if="goodsData.commodityPurchaseInstruction">
|
||||
<view class="border-box border-top-8">
|
||||
<view
|
||||
v-if="goodsData.commodityPurchaseInstruction"
|
||||
class="border-box pl-12 pr-12"
|
||||
>
|
||||
<ModuleTitle
|
||||
v-if="showTitle"
|
||||
:title="goodsData.commodityPurchaseInstruction.templateTitle"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.store-address {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 6px 0;
|
||||
margin: 6px 12px;
|
||||
padding: 16px 12px;
|
||||
background-image: url("./images/loc_bg_img.png"); // 预留背景图片位置,用户手动导入
|
||||
background-size: cover;
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
}
|
||||
|
||||
.stepper-text {
|
||||
width: 30px;
|
||||
max-width: 40px;
|
||||
}
|
||||
|
||||
@@ -1,155 +1,245 @@
|
||||
<template>
|
||||
<view class="swipe-cards" @touchmove.stop>
|
||||
<view class="card border-box pb-12 relative mt-12">
|
||||
<view
|
||||
v-for="(card, index) in visibleCards"
|
||||
:key="`${card.commodityId}-${index}`"
|
||||
class="card"
|
||||
:style="cardStyle(index, card)"
|
||||
@touchstart="touchStart($event, index)"
|
||||
@touchmove="touchMove($event, index)"
|
||||
@touchend="touchEnd($event, index)"
|
||||
class="card-item absolute overflow-hidden"
|
||||
v-for="(card, index) in list"
|
||||
:key="card.__uid"
|
||||
:style="[itemStyle(index, card), transformStyle(index, card)]"
|
||||
@touchstart.stop="touchStart($event, index)"
|
||||
@touchmove.stop.prevent="touchMove($event, index)"
|
||||
@touchend.stop="touchEnd(index)"
|
||||
@touchcancel.stop="touchCancel(index)"
|
||||
@transitionend="onTransitionEnd(index)"
|
||||
>
|
||||
<slot :card="card" />
|
||||
<view class="inner-card bg-white">
|
||||
<!-- 商品大图部分:自适应剩余空间 -->
|
||||
<view class="goods-image-wrapper relative">
|
||||
<image class="w-full h-full" :src="card.commodityPhoto" />
|
||||
<view
|
||||
class="goods-title absolute left-0 right-0 bottom-0 border-box p-12"
|
||||
>
|
||||
<view class="font-size-14 font-500 color-white ellipsis-1">
|
||||
{{ card.commodityName }}
|
||||
</view>
|
||||
<view class="card-price-row color-white">
|
||||
<text class="font-size-11">¥</text>
|
||||
<text class="font-size-14 font-bold">
|
||||
{{ card.specificationPrice }}
|
||||
</text>
|
||||
<text class="font-size-11 ml-2" v-if="card.stockUnitLabel"
|
||||
>/{{ card.stockUnitLabel }}</text
|
||||
>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部相册部分:固定比例或高度 -->
|
||||
<view class="border-box p-12 flex flex-justify-between">
|
||||
<view
|
||||
v-for="(item, index) in card.commodityPhotoList"
|
||||
:key="index"
|
||||
class="album-item relative overflow-hidden bg-f5 rounded-10"
|
||||
>
|
||||
<image :src="item.photoUrl" mode="aspectFill" />
|
||||
<view
|
||||
class="album-title absolute left-0 right-0 bottom-0 color-white font-size-11 font-500 ellipsis-1 flex flex-items-center flex-justify-center"
|
||||
>
|
||||
{{ item.photoName }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, nextTick } from "vue";
|
||||
import { ref, watch } from "vue";
|
||||
import { checkToken } from "@/hooks/useGoLogin";
|
||||
|
||||
const props = defineProps({
|
||||
cardsData: Array,
|
||||
threshold: { type: Number, default: 100 },
|
||||
cardsData: { type: Array, default: () => [] },
|
||||
});
|
||||
|
||||
const cards = ref([...props.cardsData]);
|
||||
const startX = ref(0);
|
||||
const startY = ref(0);
|
||||
const moveX = ref(0);
|
||||
const moveY = ref(0);
|
||||
const currentIndex = ref(null);
|
||||
const isHorizontal = ref(false); // 是否为水平滑动
|
||||
const DURATION = 300;
|
||||
const CLICK_THRESHOLD = 8; // 点击判定的最大偏移阈值(像素)
|
||||
const swipering = ref(false);
|
||||
const animatingOut = ref(false);
|
||||
let reorderTimer = null;
|
||||
|
||||
const visibleCards = computed(() => cards.value.slice(0, 3));
|
||||
const { windowWidth } = uni.getWindowInfo();
|
||||
|
||||
function touchStart(e, index) {
|
||||
const touch = e.touches[0];
|
||||
startX.value = touch.clientX;
|
||||
startY.value = touch.clientY;
|
||||
currentIndex.value = index;
|
||||
isHorizontal.value = false;
|
||||
}
|
||||
let uidCounter = 0;
|
||||
// 始终生成全局唯一的 __uid,避免因重复 key 导致后续卡片无法正确重渲染与绑定事件
|
||||
const genUid = (item) =>
|
||||
`swipe_${item?.commodityId ?? "unknown"}_${uidCounter++}_${Date.now()}`;
|
||||
const normalize = (item) => ({
|
||||
...item,
|
||||
__uid: genUid(item),
|
||||
x: 0,
|
||||
y: 0,
|
||||
opacity: 1,
|
||||
});
|
||||
|
||||
function touchMove(e, index) {
|
||||
if (currentIndex.value !== index) return;
|
||||
const touch = e.touches[0];
|
||||
moveX.value = touch.clientX - startX.value;
|
||||
moveY.value = touch.clientY - startY.value;
|
||||
// 循环队列(全量),堆栈(仅前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);
|
||||
};
|
||||
|
||||
// 第一次移动时判断方向
|
||||
if (
|
||||
!isHorizontal.value &&
|
||||
(Math.abs(moveX.value) > 10 || Math.abs(moveY.value) > 10)
|
||||
) {
|
||||
if (Math.abs(moveX.value) > Math.abs(moveY.value)) {
|
||||
isHorizontal.value = true;
|
||||
watch(
|
||||
() => props.cardsData,
|
||||
(val) => {
|
||||
queue.value = (val || []).map(normalize);
|
||||
updateStack();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 触摸状态
|
||||
const touchState = ref({
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
moving: false,
|
||||
isClickCandidate: false,
|
||||
});
|
||||
|
||||
const touchStart = (e, index) => {
|
||||
if (index !== 0 || animatingOut.value) return;
|
||||
const t = e.changedTouches?.[0];
|
||||
if (!t) return;
|
||||
touchState.value.startX = t.clientX;
|
||||
touchState.value.startY = t.clientY;
|
||||
touchState.value.moving = true;
|
||||
// 初始认为可能是点击,移动过程中如果超过阈值则取消点击
|
||||
touchState.value.isClickCandidate = true;
|
||||
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;
|
||||
// 超过点击阈值则标记为不是点击
|
||||
if (Math.abs(dx) > CLICK_THRESHOLD || Math.abs(dy) > CLICK_THRESHOLD) {
|
||||
touchState.value.isClickCandidate = false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
const touchEnd = (index) => {
|
||||
if (index !== 0 || !touchState.value.moving) return;
|
||||
touchState.value.moving = false;
|
||||
swipering.value = false;
|
||||
const top = list.value[0];
|
||||
if (!top) return;
|
||||
// 若在有效点击范围内,则触发跳转,不进行滑动逻辑
|
||||
if (touchState.value.isClickCandidate) {
|
||||
top.x = 0;
|
||||
top.y = 0;
|
||||
top.opacity = 1;
|
||||
uni.navigateTo({
|
||||
url: `/pages/goods/index?commodityId=${top.commodityId}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const threshold = windowWidth / 4;
|
||||
if (Math.abs(top.x) > threshold) {
|
||||
const direction = top.x > 0 ? 1 : -1;
|
||||
animatingOut.value = true;
|
||||
top.x = direction * (windowWidth + 100);
|
||||
top.opacity = 0;
|
||||
if (reorderTimer) {
|
||||
clearTimeout(reorderTimer);
|
||||
reorderTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是水平滑动,阻止页面滚动
|
||||
if (isHorizontal.value) {
|
||||
e.preventDefault?.();
|
||||
}
|
||||
}
|
||||
|
||||
function touchEnd(e, index) {
|
||||
if (!isHorizontal.value) return; // 如果不是水平滑动,不处理
|
||||
if (Math.abs(moveX.value) > props.threshold) {
|
||||
const direction = moveX.value > 0 ? 1 : -1;
|
||||
animateSwipe(index, direction);
|
||||
reorderTimer = setTimeout(() => {
|
||||
if (animatingOut.value) finalizeReorder();
|
||||
}, DURATION + 40);
|
||||
} else {
|
||||
resetCard(index);
|
||||
// 回弹复位
|
||||
top.x = 0;
|
||||
top.y = 0;
|
||||
top.opacity = 1;
|
||||
}
|
||||
moveX.value = 0;
|
||||
moveY.value = 0;
|
||||
currentIndex.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
function animateSwipe(index, direction) {
|
||||
const card = cards.value[index];
|
||||
if (!card) return;
|
||||
card.swiped = true;
|
||||
card.direction = direction;
|
||||
setTimeout(async () => {
|
||||
const removed = cards.value.splice(index, 1)[0];
|
||||
removed.swiped = false;
|
||||
removed.direction = 0;
|
||||
cards.value.push(removed);
|
||||
await nextTick();
|
||||
}, 300);
|
||||
}
|
||||
const touchCancel = (index) => {
|
||||
if (index !== 0) return;
|
||||
const top = list.value[0];
|
||||
if (!top) return;
|
||||
touchState.value.moving = false;
|
||||
swipering.value = false;
|
||||
top.x = 0;
|
||||
top.y = 0;
|
||||
top.opacity = 1;
|
||||
};
|
||||
|
||||
function resetCard(index) {
|
||||
const card = cards.value[index];
|
||||
if (!card) return;
|
||||
card.swiped = false;
|
||||
card.direction = 0;
|
||||
}
|
||||
const onTransitionEnd = (index) => {
|
||||
if (index !== 0) return;
|
||||
if (!animatingOut.value) return;
|
||||
finalizeReorder();
|
||||
};
|
||||
|
||||
function cardStyle(index, card) {
|
||||
const baseIndex = visibleCards.value.length - index;
|
||||
const zIndex = baseIndex;
|
||||
const scale = 1 - index * 0.05;
|
||||
const translateY = index * 28;
|
||||
// 栈样式:层级、基础位移缩放、过渡时长
|
||||
const itemStyle = (index, card) => {
|
||||
const zIndex = list.value.length - index;
|
||||
const duration = swipering.value ? "0ms" : `${DURATION}ms`;
|
||||
const opacity = card.opacity;
|
||||
return {
|
||||
zIndex,
|
||||
opacity,
|
||||
transition: `transform ${duration} ease, opacity ${duration} ease`,
|
||||
};
|
||||
};
|
||||
|
||||
const translateDrag =
|
||||
currentIndex.value === index
|
||||
? `translate(${moveX.value}px, ${moveY.value}px)`
|
||||
: `translate(0, ${translateY}rpx)`;
|
||||
const rotate =
|
||||
currentIndex.value === index
|
||||
? `rotate(${moveX.value / 15}deg)`
|
||||
: "rotate(0)";
|
||||
|
||||
let opacity = 1;
|
||||
if (card.swiped) opacity = 0;
|
||||
|
||||
let transform = `${translateDrag} scale(${scale}) ${rotate}`;
|
||||
|
||||
if (card.swiped) {
|
||||
const x = card.direction * 500;
|
||||
transform = `translate(${x}px, ${moveY.value}px) rotate(${
|
||||
card.direction * 45
|
||||
}deg)`;
|
||||
}
|
||||
|
||||
return `
|
||||
z-index: ${zIndex};
|
||||
background: ${card.color};
|
||||
transform: ${transform};
|
||||
opacity: ${opacity};
|
||||
transition: ${
|
||||
card.swiped ? "all 0.3s ease-out" : "transform 0.2s ease-out"
|
||||
// 变换样式:顶部卡动态位移/旋转,后续卡预览层级
|
||||
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.94, 0.86];
|
||||
const previewOffsets = [0, 18, 39];
|
||||
const scale = previewScales[index] ?? 0.94;
|
||||
const y = previewOffsets[index] ?? 24;
|
||||
return {
|
||||
transform: `translate3d(0, ${y}px, 0) scale(${scale})`,
|
||||
};
|
||||
};
|
||||
|
||||
// 去下单
|
||||
const placeOrderHandle = (item) => {
|
||||
console.log("去下单", item);
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/goods/index?commodityId=${item.commodityId}`,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.swipe-cards {
|
||||
position: relative;
|
||||
height: 320px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 6px 6px 12px 6px;
|
||||
}
|
||||
|
||||
.card {
|
||||
position: absolute;
|
||||
width: calc(100% - 12px);
|
||||
height: calc(100% - 30px);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 8px 8px rgba(0, 0, 0, 0.08);
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
40
src/components/SwipeCards/styles/index.scss
Normal file
40
src/components/SwipeCards/styles/index.scss
Normal file
@@ -0,0 +1,40 @@
|
||||
.card {
|
||||
height: 308px;
|
||||
}
|
||||
|
||||
.card-item {
|
||||
inset: 0;
|
||||
will-change: transform, opacity;
|
||||
height: 277px;
|
||||
box-shadow: 0 8px 8px rgba(0, 0, 0, 0.08);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.inner-card {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 商品大图部分:撑满除相册外的空间 */
|
||||
.goods-image-wrapper {
|
||||
width: 100%;
|
||||
height: 193px;
|
||||
}
|
||||
|
||||
.goods-title {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(0, 0, 0, 0) 0%,
|
||||
rgba(0, 0, 0, 0.6) 100%
|
||||
);
|
||||
}
|
||||
|
||||
.album-item {
|
||||
width: 96px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.album-title {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
height: 20px;
|
||||
}
|
||||
@@ -11,7 +11,9 @@
|
||||
</template>
|
||||
</TopNavBar>
|
||||
|
||||
<view class="booking-content flex-full border-box p-12">
|
||||
<view
|
||||
class="booking-content flex-full border-box p-12 overflow-hidden scroll-y"
|
||||
>
|
||||
<!-- 预约内容 -->
|
||||
<view class="border-box bg-white p-12 rounded-12 mb-12">
|
||||
<!-- 酒店类型入住离店日期部分 -->
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
'bg-2D91FF': ['1', '2', '3', '4', '5', '6'].includes(statusCode),
|
||||
},
|
||||
]"
|
||||
@click="handleButtonClick"
|
||||
@click="handleButtonClick(orderData)"
|
||||
>
|
||||
{{ buttonText }}
|
||||
</button>
|
||||
@@ -67,7 +67,7 @@ const emit = defineEmits(["refund", "refresh"]);
|
||||
const handleButtonClick = async (orderData) => {
|
||||
try {
|
||||
// 再次预定跳转商品详情
|
||||
if (["3", "5", "6"].includes(statusCode.value)) {
|
||||
if (["1", "2", "3", "4", "5", "6"].includes(statusCode.value)) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/goods/index?commodityId=${orderData.commodityId}`,
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</view>
|
||||
<view
|
||||
v-if="item.commodityFacility"
|
||||
class="font-size-12 line-height-16 color-99A0AE mb-4"
|
||||
class="font-size-12 line-height-16 color-99A0AE mb-4 ellipsis-1"
|
||||
>
|
||||
{{ item.commodityFacility.join(" ") }}
|
||||
</view>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
}
|
||||
|
||||
.right {
|
||||
max-width: 259px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
<template>
|
||||
<view class="mt-16">
|
||||
<view class="mt-16 border-box border-top-8">
|
||||
<view
|
||||
class="border-box pt-12 pl-12 pr-12"
|
||||
v-for="(moduleItem, index) in goodsData.commodityEquipment"
|
||||
:key="index"
|
||||
>
|
||||
<view
|
||||
class="flex flex-items-start flex-col pt-12 pb-12"
|
||||
class="flex flex-items-start flex-col"
|
||||
:class="{
|
||||
'border-bottom': index < goodsData.commodityEquipment.length - 1,
|
||||
}"
|
||||
>
|
||||
<view class="flex flex-items-center flex-row flex-shrink-0 mr-8">
|
||||
<uni-icons fontFamily="znicons" size="20" color="#171717">{{
|
||||
zniconsMap[moduleItem.icon]
|
||||
}}</uni-icons>
|
||||
<text class="font-size-12 color-171717 line-height-20">{{
|
||||
moduleItem.title
|
||||
}}</text>
|
||||
<view class="flex flex-items-center flex-row flex-shrink-0">
|
||||
<uni-icons fontFamily="znicons" size="20" color="#171717">
|
||||
{{ zniconsMap[moduleItem.icon] }}
|
||||
</uni-icons>
|
||||
<text class="font-size-12 color-171717 line-height-20">
|
||||
{{ moduleItem.title }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="border-box flex flex-items-center flex-row mt-4 pb-12">
|
||||
<text
|
||||
class="font-size-12 color-525866 line-height-20 mr-4"
|
||||
v-for="(text, index) in moduleItem.desc"
|
||||
:key="index"
|
||||
>
|
||||
{{ text }}
|
||||
</text>
|
||||
</view>
|
||||
<text class="flex-full font-size-12 color-525866 line-height-20 mt-4">{{
|
||||
moduleItem.desc
|
||||
}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<view class="good-info">
|
||||
<view class="good-info border-box pl-12 pr-12">
|
||||
<!-- 标题区域 -->
|
||||
<view class="title-section">
|
||||
<text class="title">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<view class="goods-container bg-gray">
|
||||
<TopNavBar
|
||||
title="商品详情"
|
||||
:backgroundColor="`rgba(217, 238, 255, ${navOpacity})`"
|
||||
:title="navOpacity < 0.5 ? '' : '商品详情'"
|
||||
:background="`rgba(217, 238, 255, ${navOpacity})`"
|
||||
:titleColor="navOpacity < 0.5 ? '#ffffff' : '#000000'"
|
||||
:backIconColor="navOpacity < 0.5 ? '#ffffff' : '#000000'"
|
||||
/>
|
||||
|
||||
@@ -68,7 +68,7 @@ $button-color: #00a6ff;
|
||||
.goods-content {
|
||||
border-radius: 28px 28px 0 0;
|
||||
background-color: #fff;
|
||||
padding: 20px 12px;
|
||||
padding: 20px 0;
|
||||
position: relative;
|
||||
margin-top: -30px;
|
||||
z-index: 1;
|
||||
|
||||
@@ -36,13 +36,13 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, defineEmits } from "vue";
|
||||
import { ref, defineEmits, defineExpose } from "vue";
|
||||
import { getLoginUserPhone } from "@/request/api/LoginApi";
|
||||
import { NOTICE_EVENT_LOGOUT } from "@/constant/constant";
|
||||
import { useAppStore } from "@/store";
|
||||
|
||||
const appStore = useAppStore();
|
||||
const emits = defineEmits(["closeDrawer"]);
|
||||
const emits = defineEmits(["close"]);
|
||||
|
||||
// 假数据
|
||||
const userInfo = ref({
|
||||
@@ -64,11 +64,6 @@ const menuList = ref([
|
||||
// { label: "订阅消息", type: "action", action: "subscribeMessage" },
|
||||
]);
|
||||
|
||||
// 组件挂载时调用
|
||||
onMounted(() => {
|
||||
getLoginUserPhoneInfo();
|
||||
});
|
||||
|
||||
const getLoginUserPhoneInfo = async () => {
|
||||
const res = await getLoginUserPhone();
|
||||
|
||||
@@ -107,12 +102,14 @@ const handleLogout = () => {
|
||||
uni.clearStorageSync();
|
||||
appStore.setHasToken(false);
|
||||
appStore.setTokenExpired(true);
|
||||
emits("closeDrawer");
|
||||
emits("close");
|
||||
uni.$emit(NOTICE_EVENT_LOGOUT);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ getLoginUserPhoneInfo });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
47
src/pages/index/components/DrawerSection/index.vue
Normal file
47
src/pages/index/components/DrawerSection/index.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<uni-drawer ref="drawerRef" mode="left" :width="320">
|
||||
<view class="drawer-home">
|
||||
<view class="drawer-home-nav">
|
||||
<uni-icons
|
||||
type="closeempty"
|
||||
size="22"
|
||||
color="#333333"
|
||||
class="close-icon"
|
||||
@click="close"
|
||||
/>
|
||||
<text class="title">我的</text>
|
||||
</view>
|
||||
|
||||
<MineSetting ref="mineSettingRef" @close="close" />
|
||||
</view>
|
||||
</uni-drawer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineExpose } from "vue";
|
||||
import { checkToken } from "@/hooks/useGoLogin";
|
||||
import MineSetting from "./components/MineSetting/index.vue";
|
||||
|
||||
const drawerRef = ref(null);
|
||||
|
||||
// 监听抽屉显示事件
|
||||
const mineSettingRef = ref(null);
|
||||
const open = () => {
|
||||
checkToken().then(async () => {
|
||||
await mineSettingRef.value.getLoginUserPhoneInfo();
|
||||
drawerRef.value.open();
|
||||
});
|
||||
};
|
||||
|
||||
// 监听抽屉隐藏事件
|
||||
const close = () => drawerRef.value.close();
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
close,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
@@ -63,12 +63,12 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 录音按钮 -->
|
||||
<RecordingWaveBtn v-if="visibleWaveBtn" ref="recordingWaveBtnRef" />
|
||||
|
||||
<view class="color-99A0AE font-size-9 text-center text-gray-400">
|
||||
内容由AI大模型生成,请仔细鉴别
|
||||
</view>
|
||||
|
||||
<!-- 录音按钮 -->
|
||||
<RecordingWaveBtn v-if="visibleWaveBtn" ref="recordingWaveBtnRef" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<template>
|
||||
<view class="flex flex-col h-screen" @touchend="handleTouchEnd">
|
||||
<view class="flex flex-col h-screen">
|
||||
<!-- 顶部自定义导航栏 -->
|
||||
<view class="header" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||||
<ChatTopNavBar @openDrawer="openDrawer" />
|
||||
<ChatTopNavBar
|
||||
ref="topNavBarRef"
|
||||
:mainPageDataModel="mainPageDataModel"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 消息列表(可滚动区域) -->
|
||||
@@ -15,7 +18,7 @@
|
||||
@scrolltolower="handleScrollToLower"
|
||||
>
|
||||
<!-- welcome栏 -->
|
||||
<ChatTopWelcome :mainPageDataModel="mainPageDataModel" />
|
||||
<ChatTopWelcome ref="welcomeRef" :mainPageDataModel="mainPageDataModel" />
|
||||
|
||||
<view
|
||||
class="area-msg-list-content"
|
||||
@@ -120,7 +123,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, nextTick, onUnmounted, ref, defineEmits } from "vue";
|
||||
import { onMounted, nextTick, onUnmounted, ref } from "vue";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import {
|
||||
SCROLL_TO_BOTTOM,
|
||||
@@ -153,7 +156,7 @@ import {
|
||||
recentConversation,
|
||||
} from "@/request/api/ConversationApi";
|
||||
import WebSocketManager from "@/utils/WebSocketManager";
|
||||
import { IdUtils } from "@/utils";
|
||||
import { ThrottleUtils, IdUtils } from "@/utils";
|
||||
import { checkToken } from "@/hooks/useGoLogin";
|
||||
import { useAppStore } from "@/store";
|
||||
|
||||
@@ -162,6 +165,8 @@ const appStore = useAppStore();
|
||||
const statusBarHeight = ref(20);
|
||||
/// 输入框组件引用
|
||||
const inputAreaRef = ref(null);
|
||||
const topNavBarRef = ref();
|
||||
const welcomeRef = ref();
|
||||
|
||||
const holdKeyboardTimer = ref(null);
|
||||
/// focus时,点击页面的时候不收起键盘
|
||||
@@ -174,9 +179,6 @@ const isKeyboardShow = ref(false);
|
||||
///(控制滚动位置)
|
||||
const scrollTop = ref(99999);
|
||||
|
||||
// 用户滚动状态控制
|
||||
const isUserScrolling = ref(false);
|
||||
|
||||
/// 会话列表
|
||||
const chatMsgList = ref([]);
|
||||
/// 输入口的输入消息
|
||||
@@ -202,10 +204,6 @@ let webSocketConnectStatus = false;
|
||||
// 当前会话的消息ID,用于保持发送和终止的messageId一致
|
||||
let currentSessionMessageId = null;
|
||||
|
||||
// 打开抽屉
|
||||
const emits = defineEmits(["openDrawer"]);
|
||||
const openDrawer = () => emits("openDrawer");
|
||||
|
||||
/// =============事件函数↓================
|
||||
const handleTouchEnd = () => {
|
||||
clearTimeout(holdKeyboardTimer.value);
|
||||
@@ -238,9 +236,10 @@ const handleKeyboardHide = () => {
|
||||
};
|
||||
|
||||
// 处理用户滚动事件
|
||||
const handleScroll = (e) => {
|
||||
// 标记用户正在滚动
|
||||
};
|
||||
const welcomeHeight = ref(0);
|
||||
const handleScroll = ThrottleUtils.createThrottle(({ detail }) => {
|
||||
topNavBarRef.value.show = parseInt(detail.scrollTop) > welcomeHeight.value;
|
||||
}, 50);
|
||||
|
||||
// 处理滚动到底部事件
|
||||
const handleScrollToLower = () => {};
|
||||
@@ -385,6 +384,7 @@ const loadConversationMsgList = async () => {
|
||||
};
|
||||
|
||||
// 获取首页数据
|
||||
|
||||
const getMainPageData = async () => {
|
||||
/// 从个渠道获取如二维,没有的时候就返回首页的数据
|
||||
const sceneId = appStore.sceneId || "";
|
||||
@@ -394,6 +394,11 @@ const getMainPageData = async () => {
|
||||
initData();
|
||||
mainPageDataModel.value = res.data;
|
||||
agentId.value = res.data.agentId;
|
||||
// 数据更新后再次测量欢迎区高度
|
||||
welcomeRef.value.measureWelcomeHeight((data) => {
|
||||
console.log("🚀 ~ getMainPageData ~ data:", data);
|
||||
welcomeHeight.value = data.height;
|
||||
});
|
||||
}
|
||||
appStore.setSceneId(""); // 清空sceneId,分身二次进入展示默认的
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
:key="index"
|
||||
>
|
||||
<text
|
||||
class="font-500 font-size-12 line-height-24 text-center"
|
||||
:class="['font-500 font-size-12 text-center', `color-${index}`]"
|
||||
@click="sendReply(item)"
|
||||
>
|
||||
{{ item }}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
.more-tips-item {
|
||||
background: #f7f7f7;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
padding: 6px 8px;
|
||||
|
||||
&:last-child {
|
||||
@@ -13,3 +14,31 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.color-0 {
|
||||
color: #47c2ff;
|
||||
}
|
||||
|
||||
.color-1 {
|
||||
color: #fb3748;
|
||||
}
|
||||
|
||||
.color-2 {
|
||||
color: #1fc16b;
|
||||
}
|
||||
|
||||
.color-3 {
|
||||
color: #f6b51e;
|
||||
}
|
||||
|
||||
.color-4 {
|
||||
color: #7d52f4;
|
||||
}
|
||||
|
||||
.color-5 {
|
||||
color: #fb4ba3;
|
||||
}
|
||||
|
||||
.color-6 {
|
||||
color: #22d3bb;
|
||||
}
|
||||
|
||||
@@ -1,35 +1,57 @@
|
||||
<template>
|
||||
<view class="nav-bar">
|
||||
<view class="nav-item" @click="showDrawer('showLeft')">
|
||||
<uni-icons type="bars" size="24" color="#333"></uni-icons>
|
||||
<view class="border-box h-44 flex flex-items-center pl-12 pr-12">
|
||||
<uni-icons type="bars" size="24" color="#333" @click="showDrawer" />
|
||||
|
||||
<!-- 隐藏 -->
|
||||
<view class="flex-full h-full flex flex-items-center flex-justify-center">
|
||||
<!-- ChatTopWelcome不在可视区:显示并添加动画;在可视区:隐藏 -->
|
||||
<view
|
||||
v-show="show"
|
||||
:class="['w-32 h-32', { 'image-animated': show }]"
|
||||
:style="getStyle"
|
||||
></view>
|
||||
<text
|
||||
v-show="show"
|
||||
class="font-size-14 font-500 color-171717 ml-10"
|
||||
:class="{ 'text-animated': show }"
|
||||
>沐沐</text
|
||||
>
|
||||
</view>
|
||||
|
||||
<uni-drawer ref="showLeft" mode="left" :width="320">
|
||||
<DrawerHome @closeDrawer="closeDrawer('showLeft')" />
|
||||
</uni-drawer>
|
||||
<view class="w-24 h-24"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import DrawerHome from "../../drawer/DrawerHome/index.vue";
|
||||
import { checkToken } from "@/hooks/useGoLogin";
|
||||
const showLeft = ref(false);
|
||||
import { ref, defineProps, computed, defineExpose } from "vue";
|
||||
import { getCurrentConfig } from "@/constant/base";
|
||||
|
||||
// 打开窗口
|
||||
const showDrawer = async (e) => {
|
||||
await checkToken();
|
||||
const props = defineProps({
|
||||
mainPageDataModel: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
showLeft.value.open();
|
||||
// 发送抽屉显示事件
|
||||
uni.$emit("drawerShow");
|
||||
};
|
||||
// 关闭窗口
|
||||
const closeDrawer = (e) => {
|
||||
showLeft.value.close();
|
||||
// 发送抽屉隐藏事件
|
||||
uni.$emit("drawerHide");
|
||||
};
|
||||
const show = ref(false);
|
||||
|
||||
const getStyle = computed(() => {
|
||||
const config = getCurrentConfig();
|
||||
|
||||
return {
|
||||
"--ipSmallImageStep": config.ipSmallImageStep,
|
||||
"--ipSmallImageHeight": config.ipSmallImageHeight,
|
||||
"--ipSmallTime": config.ipSmallTime,
|
||||
backgroundImage: `url(${config.ipSmallImage})`,
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundSize: "32px auto",
|
||||
backgroundPosition: "0 0",
|
||||
};
|
||||
});
|
||||
|
||||
const showDrawer = () => uni.$emit("SHOW_DRAWER");
|
||||
|
||||
defineExpose({ show });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,16 +1,42 @@
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
padding: 0 15px;
|
||||
// 图片从0%到100%动画
|
||||
.image-animated {
|
||||
animation: logo-scale 0.3s ease-in-out,
|
||||
sprite-play calc(var(--ipSmallTime) * 1s) steps(var(--ipSmallImageStep))
|
||||
infinite;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 10px;
|
||||
@keyframes logo-scale {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
.nav-item-icon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sprite-play {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
/* 117 帧 × 每帧高度约 32px,终点应为 -(117-1)*32 = -5421px */
|
||||
background-position: 0 calc(var(--ipSmallImageHeight) * -1px);
|
||||
}
|
||||
}
|
||||
|
||||
// 文字从0%到100%动画,从左到右
|
||||
.text-animated {
|
||||
animation: text-fade-in 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes text-fade-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
v
|
||||
<template>
|
||||
<view class="welcome-content border-box p-12">
|
||||
<view class="wrap rounded-20">
|
||||
<view
|
||||
class="flex flex-items-center flex-justify-between border-box pl-12 pr-12"
|
||||
>
|
||||
<image
|
||||
class="ip relative"
|
||||
:src="initPageImages?.logoImageUrl"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view class="ip" :style="getStyle"></view>
|
||||
<view
|
||||
class="welcome-text font-size-14 font-500 font-family-misans-vf color-171717 line-height-24"
|
||||
>
|
||||
@@ -22,7 +19,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, computed } from "vue";
|
||||
import { defineProps, computed, getCurrentInstance, defineExpose } from "vue";
|
||||
import { getCurrentConfig } from "@/constant/base";
|
||||
import ChatMoreTips from "../ChatMoreTips/index.vue";
|
||||
|
||||
const props = defineProps({
|
||||
@@ -51,9 +49,40 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const initPageImages = computed(() => props.mainPageDataModel.initPageImages);
|
||||
const getStyle = computed(() => {
|
||||
const config = getCurrentConfig();
|
||||
|
||||
return {
|
||||
"--ipLargeImageStep": config.ipLargeImageStep,
|
||||
"--ipLargeImageHeight": config.ipLargeImageHeight,
|
||||
"--ipLargeTime": config.ipLargeTime,
|
||||
backgroundImage: `url(${config.ipLargeImage})`,
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundSize: "158px auto",
|
||||
backgroundPosition: "0 0",
|
||||
};
|
||||
});
|
||||
const welcomeContent = computed(() => props.mainPageDataModel.welcomeContent);
|
||||
const guideWords = computed(() => props.mainPageDataModel.guideWords);
|
||||
|
||||
// Welcome 可视状态与高度
|
||||
const instance = getCurrentInstance();
|
||||
|
||||
// 测量欢迎区域高度
|
||||
const measureWelcomeHeight = (callback) => {
|
||||
uni
|
||||
.createSelectorQuery()
|
||||
.in(instance)
|
||||
.select(".welcome-content")
|
||||
.boundingClientRect((res) => {
|
||||
if (res && res.height) {
|
||||
callback(res);
|
||||
}
|
||||
})
|
||||
.exec();
|
||||
};
|
||||
|
||||
defineExpose({ measureWelcomeHeight });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -3,8 +3,20 @@
|
||||
}
|
||||
|
||||
.ip {
|
||||
flex: 0 0 123px;
|
||||
width: 123px;
|
||||
height: 166px;
|
||||
margin-top: -20px;
|
||||
flex: 0 0 158px;
|
||||
width: 158px;
|
||||
height: 134px;
|
||||
animation: sprite-play calc(var(--ipLargeTime) * 1s)
|
||||
steps(var(--ipLargeImageStep)) infinite;
|
||||
}
|
||||
|
||||
@keyframes sprite-play {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
/* 40 帧 × 每帧高度约 139px,终点应为 -(40-1)*139 = -5421px */
|
||||
background-position: 0 calc(var(--ipLargeImageHeight) * -1px);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
<template>
|
||||
<view class="drawer-home">
|
||||
<view class="drawer-home-nav">
|
||||
<uni-icons
|
||||
type="closeempty"
|
||||
size="22"
|
||||
color="#333333"
|
||||
class="close-icon"
|
||||
@click="closeDrawer"
|
||||
/>
|
||||
<text class="title">我的</text>
|
||||
</view>
|
||||
|
||||
<MineSetting v-if="isDrawerVisible" @closeDrawer="closeDrawer" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineEmits, ref, onMounted, onUnmounted } from "vue";
|
||||
import MineSetting from "../MineSetting/index.vue";
|
||||
|
||||
const emits = defineEmits(["closeDrawer"]);
|
||||
|
||||
const isDrawerVisible = ref(false);
|
||||
const closeDrawer = () => {
|
||||
console.log("关闭抽屉");
|
||||
isDrawerVisible.value = false;
|
||||
emits("closeDrawer");
|
||||
};
|
||||
|
||||
// 监听抽屉显示事件
|
||||
const handleDrawerShow = () => {
|
||||
isDrawerVisible.value = true;
|
||||
};
|
||||
|
||||
// 监听抽屉隐藏事件
|
||||
const handleDrawerHide = () => {
|
||||
isDrawerVisible.value = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
uni.$on("drawerShow", handleDrawerShow);
|
||||
uni.$on("drawerHide", handleDrawerHide);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
uni.$off("drawerShow", handleDrawerShow);
|
||||
uni.$off("drawerHide", handleDrawerHide);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
@@ -5,7 +5,9 @@
|
||||
}
|
||||
|
||||
.tag-item {
|
||||
background-color: $uni-bg-color;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #fff;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 8px;
|
||||
padding: 4px 10px;
|
||||
margin-right: 8px;
|
||||
|
||||
@@ -2,54 +2,12 @@
|
||||
<view class="container">
|
||||
<ModuleTitle title="相关商品" />
|
||||
|
||||
<SwipeCards :cardsData="commodityList">
|
||||
<template #default="{ card }">
|
||||
<view class="inner-card" @click="placeOrderHandle(card)">
|
||||
<!-- 商品大图部分:自适应剩余空间 -->
|
||||
<view class="goods-image-wrapper">
|
||||
<image
|
||||
class="goods-image"
|
||||
:src="card.commodityPhoto"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<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 normalizedAlbums(commodityList)"
|
||||
:key="index"
|
||||
class="album-item"
|
||||
>
|
||||
<view v-if="item" class="album-image-wrapper">
|
||||
<image
|
||||
class="album-image"
|
||||
:src="item.commodityPhoto"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="album-title">{{ item.commodityName }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</SwipeCards>
|
||||
<SwipeCards :cardsData="commodityList" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from "vue";
|
||||
import { checkToken } from "@/hooks/useGoLogin";
|
||||
import ModuleTitle from "@/components/ModuleTitle/index.vue";
|
||||
import SwipeCards from "@/components/SwipeCards/index.vue";
|
||||
|
||||
@@ -59,136 +17,8 @@ const props = defineProps({
|
||||
default: [],
|
||||
},
|
||||
});
|
||||
|
||||
// 补齐3个的
|
||||
const normalizedAlbums = (list = []) => {
|
||||
const arr = list.slice(0, 3);
|
||||
while (arr.length < 3) arr.push(null);
|
||||
return arr;
|
||||
};
|
||||
|
||||
// 去下单
|
||||
const placeOrderHandle = (item) => {
|
||||
checkToken().then(() => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/goods/index?commodityId=${item.commodityId}`,
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@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>
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
|
||||
<!-- 更多服务 -->
|
||||
<MoreService />
|
||||
|
||||
<!-- 抽屉组件 -->
|
||||
<DrawerSection ref="drawerRef" @close="closeDrawer" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -21,9 +24,11 @@ import { onLoad } from "@dcloudio/uni-app";
|
||||
import { ref, onUnmounted } from "vue";
|
||||
import { getUrlParams } from "@/utils/UrlParams";
|
||||
import { useAppStore } from "@/store";
|
||||
import { checkToken } from "@/hooks/useGoLogin";
|
||||
import ChatMainList from "./components/chat/ChatMainList/index.vue";
|
||||
import Calender from "@/components/Calender/index.vue";
|
||||
import MoreService from "./components/module/MoreService/index.vue";
|
||||
import DrawerSection from "./components/DrawerSection/index.vue";
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
@@ -56,6 +61,19 @@ const getWeixinMiniProgramParams = (e) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 打开窗口
|
||||
const drawerRef = ref(null);
|
||||
const showDrawer = async (e) => {
|
||||
await checkToken();
|
||||
|
||||
drawerRef.value.open();
|
||||
};
|
||||
|
||||
uni.$on("SHOW_DRAWER", showDrawer);
|
||||
|
||||
// 关闭窗口
|
||||
const closeDrawer = (e) => drawerRef.value.close();
|
||||
|
||||
onLoad((e) => {
|
||||
getWeixinMiniProgramParams(e);
|
||||
});
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
border-top: 1px solid #e5e8ee;
|
||||
}
|
||||
|
||||
.border-top-8 {
|
||||
border-top: 8px solid #f5f5f5;
|
||||
}
|
||||
|
||||
.border-bottom {
|
||||
border-bottom: 1px solid #e5e8ee;
|
||||
}
|
||||
|
||||
3
src/static/scss/box-sizing.scss
Normal file
3
src/static/scss/box-sizing.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.border-box {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -9,3 +9,15 @@
|
||||
.h-80 {
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.h-44 {
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.h-32 {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.h-24 {
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
@@ -19,3 +19,4 @@
|
||||
@import "./width.scss";
|
||||
@import "./z-index.scss";
|
||||
@import "./white-space.scss";
|
||||
@import "./box-sizing.scss";
|
||||
|
||||
@@ -59,6 +59,10 @@
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.ml-10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.m-12 {
|
||||
margin: 12px;
|
||||
}
|
||||
|
||||
@@ -6,3 +6,15 @@
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.left-0 {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.right-0 {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.bottom-0 {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.w-24 {
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.w-32 {
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.w-50 {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user