Переглянути джерело

feat: 商品详情页面部分开发

liangan 1 місяць тому
батько
коміт
de3b218c15

+ 8 - 1
src/components/product/product.vue

@@ -2,7 +2,7 @@
 defineOptions({
   name: 'Product', // 商品组件
 })
-defineProps({
+const props = defineProps({
   item: {
     type: Object,
     required: true,
@@ -16,12 +16,19 @@ defineProps({
     default: 260,
   },
 })
+
+const emit = defineEmits(['itemClick'])
+
+function handleClick() {
+  emit('itemClick', props.item)
+}
 </script>
 
 <template>
   <view
     class="flex flex-[0.5] flex-col items-center border-1 border-#E0E0E0 border-solid pb-8rpx"
     :style="{ width: Number.isFinite(width) ? `${width}rpx` : width }"
+    @click="handleClick"
   >
     <view :style="{ width: Number.isFinite(width) ? `${width}rpx` : width, height: Number.isFinite(height) ? `${height}rpx` : height }">
       <image

+ 8 - 0
src/pages.json

@@ -97,6 +97,14 @@
         "navigationBarBackgroundColor": "#fff"
       }
     },
+    {
+      "path": "pages/productDetail/productDetail",
+      "type": "page",
+      "layout": "default",
+      "style": {
+        "navigationStyle": "custom"
+      }
+    },
     {
       "path": "pages/referEarn/referEarn",
       "type": "page",

+ 9 - 2
src/pages/index/index.vue

@@ -162,6 +162,13 @@ function queryList(pageNo, pageSize) {
     paging.value.complete(dataList.value)
   }, 1000)
 }
+
+function toProductDetail(item) {
+  const data = JSON.stringify({ id: item.id })
+  uni.navigateTo({
+    url: `/pages/productDetail/productDetail?data=${encodeURIComponent(data)}`,
+  })
+}
 </script>
 
 <template>
@@ -201,7 +208,7 @@ function queryList(pageNo, pageSize) {
         </view>
         <scroll-view scroll-x>
           <view class="flex items-center gap-22rpx pb-24rpx">
-            <product v-for="(item, index) in newProducts" :key="index" :item="item" />
+            <product v-for="(item, index) in newProducts" :key="index" :item="item" @item-click="toProductDetail" />
           </view>
         </scroll-view>
       </view>
@@ -212,7 +219,7 @@ function queryList(pageNo, pageSize) {
           </block>
         </wd-tabs>
         <view class="flex flex-wrap gap-22rpx">
-          <product v-for="(item, index) in newProducts" :key="index" :height="340" :item="item" />
+          <product v-for="(item, index) in newProducts" :key="index" :height="340" :item="item" @item-click="toProductDetail" />
           <view />
         </view>
       </view>

+ 290 - 0
src/pages/productDetail/productDetail.vue

@@ -0,0 +1,290 @@
+<route lang="json5" type="page">
+{
+  layout: 'default',
+  style: {
+    navigationStyle: 'custom',
+  },
+}
+</route>
+
+<script lang="ts" setup>
+// 必须导入需要用到的页面生命周期(即使在当前页面上没有直接使用到)
+// eslint-disable-next-line unused-imports/no-unused-imports
+import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
+import { onMounted, onUnmounted } from 'vue'
+import useZPaging from 'z-paging/components/z-paging/js/hooks/useZPaging.js'
+
+defineOptions({
+  name: 'ProductDetail', // 商品详情
+})
+// 获取屏幕边界到安全区域距离
+const systemInfo = uni.getSystemInfoSync()
+const safeAreaInsets = systemInfo.safeAreaInsets
+
+// z-paging
+const paging = ref(null)
+// 类似mixins,如果是页面滚动务必要写这一行,并传入当前ref绑定的paging,注意此处是paging,而非paging.value
+useZPaging(paging)
+
+// 添加导航栏背景色变量
+const navBgColor = ref('transparent')
+const navIconType = ref('tr') // tr表示透明背景下的图标,默认为白色图标
+const changeNavbarThreshold = 300 // 滚动到这个高度时改变导航栏颜色
+
+onPageScroll((e) => {
+  // 根据滚动高度改变导航栏背景色
+  if (e.scrollTop > changeNavbarThreshold) {
+    navBgColor.value = '#ffffff'
+    navIconType.value = '' // 切换为深色图标
+  }
+  else {
+    navBgColor.value = 'transparent'
+    navIconType.value = 'tr' // 切换为白色图标
+  }
+})
+
+// 搜索结果
+// 轮播图
+const current = ref<number>(0)
+const swiperList = ref([
+  '/static/images/avatar.jpg',
+  '/static/images/vip-info-bg.png',
+  '/static/images/vip-level1.png',
+])
+
+// 添加通知轮播数据
+const notifications = ref([
+  { id: 1, name: 'Aamir Khan', time: '10s' },
+  { id: 2, name: 'John Smith', time: '30s' },
+  { id: 3, name: 'Maria Garcia', time: '1m' },
+])
+const notificationIndex = ref(0)
+// 添加上一个索引的记录,用于判断动画方向
+const prevNotificationIndex = ref(0)
+
+// 设置通知轮播定时器
+let notificationTimer: number | null = null
+
+// 生命周期钩子中启动轮播
+onMounted(() => {
+  startNotificationCarousel()
+})
+
+// 组件卸载时清除定时器
+onUnmounted(() => {
+  if (notificationTimer) {
+    clearInterval(notificationTimer)
+  }
+})
+
+// 启动通知轮播
+function startNotificationCarousel() {
+  notificationTimer = setInterval(() => {
+    // 保存当前索引
+    prevNotificationIndex.value = notificationIndex.value
+
+    // 更新为下一个索引
+    notificationIndex.value = (notificationIndex.value + 1) % notifications.value.length
+  }, 1500) // 每1.5秒切换一次
+}
+
+function handleClick(e) {
+  // console.log(e)
+}
+function onChange(e) {
+  // console.log(e)
+}
+
+// 添加头像列表数据
+const avatarList = ref([
+  '/static/images/avatar.jpg',
+  '/static/images/avatar.jpg',
+  '/static/images/avatar.jpg',
+  '/static/images/avatar.jpg',
+  '/static/images/avatar.jpg',
+  '/static/images/avatar.jpg',
+  '/static/images/avatar.jpg',
+])
+
+const dataList = ref([])
+function queryList(pageNo, pageSize) {
+  // 此处请求仅为演示,请替换为自己项目中的请求
+  setTimeout(() => {
+    dataList.value = [
+      { title: '123' },
+      { title: '123' },
+      { title: '123' },
+      { title: '12345' },
+    ]
+    paging.value.complete(dataList.value)
+  }, 1000)
+}
+</script>
+
+<template>
+  <z-paging ref="paging" refresher-only use-page-scroll @query="queryList">
+    <wd-navbar :bordered="false" safe-area-inset-top fixed :left-arrow="false" :custom-style="`background: ${navBgColor}; transition: background 0.3s;`" custom-class="h-auto!">
+      <template #title>
+        <view class="box-border h-full flex items-center justify-between p-24rpx">
+          <image :src="`/static/icons/left-icon${navBgColor === '#ffffff' ? '-tr' : ''}.png`" class="h-56rpx w-56rpx" />
+          <image :src="`/static/icons/menu-icon${navBgColor === '#ffffff' ? '-tr' : ''}.png`" class="h-56rpx w-56rpx" />
+        </view>
+      </template>
+    </wd-navbar>
+    <view class="relative">
+      <wd-swiper
+        v-model:current="current" :list="swiperList" autoplay height="750rpx"
+        custom-indicator-class="bottom-40rpx!" :indicator="{ type: 'fraction' }" indicator-position="bottom-right"
+        image-mode="aspectFit" @click="handleClick" @change="onChange"
+      />
+      <view class="absolute left-24rpx h-56rpx w-70% overflow-hidden rounded-full" :style="{ top: `${safeAreaInsets?.top + 52}px` }">
+        <view
+          v-for="(notification, index) in notifications"
+          :key="notification.id"
+          class="flex items-center rounded-full bg-#000000/60 py-8rpx pl-8rpx pr-14rpx transition-all duration-500 ease-in-out"
+          :style="{
+            opacity: index === notificationIndex ? 1 : 0,
+            transform: index === notificationIndex ? 'translateY(0)'
+              : (index === prevNotificationIndex ? 'translateY(-100%)' : 'translateY(100%)'),
+            position: 'absolute',
+            top: 0,
+            left: 0,
+            width: '100%',
+            zIndex: index === notificationIndex ? 2 : 1,
+          }"
+        >
+          <wd-img width="40rpx" round height="40rpx" src="/static/images/avatar.jpg" />
+          <text class="ml-12rpx truncate text-24rpx text-white">
+            {{ notification.name }} joined this group {{ notification.time }} ago!
+          </text>
+        </view>
+      </view>
+    </view>
+    <view class="relative -top-24rpx">
+      <view
+        class="flex items-center justify-between rounded-t-24rpx from-[#FF3779] to-[#FF334A] bg-gradient-to-br px-24rpx pb-24rpx pt-18rpx text-white"
+      >
+        <view>
+          <view class="mb-12rpx flex items-center">
+            <text class="text-28rpx">
+              Price
+            </text>
+            <view class="ml-8rpx rounded-t-18rpx rounded-br-18rpx bg-#202221 px-12rpx text-24rpx">
+              20GB
+            </view>
+          </view>
+          <view>
+            <text class="text-48rpx">
+              <text class="text-28rpx">
+                ৳
+              </text>1000.00
+            </text>
+            <text class="ml-22rpx text-28rpx line-through">
+              ৳1999.00
+            </text>
+          </view>
+        </view>
+        <text class="text-28rpx">
+          1.8k sold
+        </text>
+      </view>
+      <view class="bg-white px-24rpx pb-24rpx pt-20rpx text-32rpx">
+        <view class="line-clamp-2font-bold mb-16rpx">
+          BOLON Classic Aviator Polarized Sunglasses, Exclusive Eyewear Brand
+        </view>
+        <view class="flex items-center justify-between">
+          <view>
+            <text class="mr-20rpx">
+              Color
+            </text>
+            <text class="text-#757575">
+              Black Grey
+            </text>
+          </view>
+          <wd-icon name="arrow-right" color="#7D7D7D" size="36rpx" />
+        </view>
+      </view>
+    </view>
+    <view class="mb-20rpx bg-white p-24rpx">
+      <view class="mb-20rpx flex items-center justify-between">
+        <view
+          class="flex items-center before:h-45rpx before:w-8rpx before:rounded-4rpx before:bg-#FF3778 before:content-empty"
+        >
+          <text class="ml-10rpx text-32rpx">
+            Group Rules
+          </text>
+        </view>
+        <view class="flex items-center">
+          <text class="mr-8rpx text-24rpx text-#3A444C">
+            View Rules
+          </text>
+          <wd-icon name="arrow-right" color="#7D7D7D" size="24rpx" />
+        </view>
+      </view>
+      <image src="/static/images/buy-flow.png" class="w-full" mode="widthFix" />
+    </view>
+    <view class="bg-white p-24rpx">
+      <view
+        class="mb-20rpx flex items-center before:h-45rpx before:w-8rpx before:rounded-4rpx before:bg-#FF3778 before:content-empty"
+      >
+        <text class="ml-10rpx text-32rpx">
+          Ongoing Group
+        </text>
+      </view>
+      <view class="flex flex-col gap-24rpx">
+        <view v-for="i in 3" :key="i" class="flex items-center justify-between">
+          <view class="flex items-center">
+            <view>
+              <!-- 头像组 最多五个 -->
+              <view class="mr-16rpx flex items-center">
+                <view
+                  v-for="(avatar, index) in avatarList.slice(0, 5)"
+                  :key="index"
+                  :style="{ marginLeft: index !== 0 ? '-20rpx' : '0', zIndex: 10 - index }"
+                  class="h-56rpx w-56rpx overflow-hidden border-2rpx border-white rounded-full border-solid"
+                >
+                  <image :src="avatar" class="h-full w-full" mode="aspectFill" />
+                </view>
+              </view>
+            </view>
+            <view>
+              <view class="text-28rpx font-bold">
+                Need <text class="text-[var(--wot-color-theme)]">
+                  5
+                </text> More
+              </view>
+            </view>
+          </view>
+          <wd-button size="small">
+            Join Group
+          </wd-button>
+        </view>
+      </view>
+      <view class="bg-white p-24rpx">
+        <view
+          class="mb-20rpx flex items-center before:h-45rpx before:w-8rpx before:rounded-4rpx before:bg-#FF3778 before:content-empty"
+        >
+          <text class="ml-10rpx text-32rpx">
+            Details
+          </text>
+        </view>
+        <view v-for="i in 3" :key="i">
+          <image
+            src="/static/images/avatar.jpg"
+            mode="widthFix"
+            class="w-full"
+          />
+        </view>
+      </view>
+    </view>
+  </z-paging>
+</template>
+
+<style lang="scss" scoped>
+:deep() {
+  .wd-navbar__title {
+    margin: 0;
+    max-width: 100%;
+  }
+}
+</style>

BIN
src/static/icons/left-icon-tr.png


BIN
src/static/icons/left-icon.png


BIN
src/static/icons/menu-icon-tr.png


BIN
src/static/icons/menu-icon.png


BIN
src/static/images/buy-flow.png


+ 1 - 0
src/style/index.scss

@@ -13,6 +13,7 @@ page {
   background: #f5f5f5;
   font-size: 28rpx;
   --wot-table-font-size: 24rpx;
+  --wot-swiper-radius: 0;
   // 修改按主题色
   --wot-color-theme: #e61b28;
   --wot-color-theme-rgb: 230, 27, 40; // RGB值分解,对应#e61b28