叶静 3 týždňov pred
rodič
commit
7b90f584d5
3 zmenil súbory, kde vykonal 212 pridanie a 68 odobranie
  1. 28 4
      src/components/product/product.vue
  2. 1 1
      src/pages.json
  3. 183 63
      src/pages/index/index.vue

+ 28 - 4
src/components/product/product.vue

@@ -21,6 +21,10 @@ const props = defineProps({
     type: [Number, String],
     default: 260,
   },
+  borderRadius: {
+    type: [Number, String],
+    default: 6,
+  },
 })
 
 const emit = defineEmits(['itemClick'])
@@ -32,18 +36,38 @@ function handleClick() {
 
 <template>
   <view
-    class="flex flex-col items-center"
-    :style="{ maxWidth: Number.isFinite(width) ? `${width}rpx` : width, width: Number.isFinite(width) ? `${width}rpx` : width }"
+    class="flex flex-col items-center overflow-hidden"
+    :style="{
+      maxWidth: Number.isFinite(width) ? `${width}rpx` : width,
+      width: Number.isFinite(width) ? `${width}rpx` : width,
+      borderRadius: Number.isFinite(borderRadius) ? `${borderRadius}rpx` : borderRadius,
+    }"
     @click="handleClick"
   >
-    <view :style="{ maxWidth: Number.isFinite(width) ? `${width}rpx` : width, width: Number.isFinite(width) ? `${width}rpx` : width, height: Number.isFinite(height) ? `${height}rpx` : height }">
+    <view
+      class="overflow-hidden"
+      :style="{
+        maxWidth: Number.isFinite(width) ? `${width}rpx` : width,
+        width: Number.isFinite(width) ? `${width}rpx` : width,
+        height: Number.isFinite(height) ? `${height}rpx` : height,
+        borderTopLeftRadius: Number.isFinite(borderRadius) ? `${borderRadius}rpx` : borderRadius,
+        borderTopRightRadius: Number.isFinite(borderRadius) ? `${borderRadius}rpx` : borderRadius,
+      }"
+    >
       <image
         :src="item.image"
         class="h-full w-full"
         mode="aspectFit"
       />
     </view>
-    <view class="box-border w-full bg-white px-14rpx pb-8rpx pt-10rpx" :style="{ fontSize: Number.isFinite(titleFontSize) ? `${titleFontSize}rpx` : titleFontSize }">
+    <view
+      class="box-border w-full bg-white px-14rpx pb-8rpx pt-10rpx"
+      :style="{
+        fontSize: Number.isFinite(titleFontSize) ? `${titleFontSize}rpx` : titleFontSize,
+        borderBottomLeftRadius: Number.isFinite(borderRadius) ? `${borderRadius}rpx` : borderRadius,
+        borderBottomRightRadius: Number.isFinite(borderRadius) ? `${borderRadius}rpx` : borderRadius,
+      }"
+    >
       <view class="mb-3px truncate">
         {{ item.productName }}
       </view>

+ 1 - 1
src/pages.json

@@ -332,4 +332,4 @@
     }
   ],
   "subPackages": []
-}
+}

+ 183 - 63
src/pages/index/index.vue

@@ -127,22 +127,29 @@ const priceTabList = ref([
 ])
 
 const dataList = ref<any>([])
+const isProductListLoading = ref(false) // 商品列表加载状态
+
 async function queryList(pageNo: number, pageSize: number) {
-  // 获取当前选中tab的价格范围,默认为第一个tab (0-300)
-  // const currentTab = priceTabList.value[priceTab.value] || priceTabList.value[0]
-  // const minPrice = currentTab?.minPrice ?? 0
-  // const maxPrice = currentTab?.maxPrice ?? 300
+  // 如果是第一页,显示骨架屏
+  if (pageNo === 1) {
+    isProductListLoading.value = true
+  }
 
-  const params = {
-    page: pageNo,
-    size: pageSize,
-    sort: 'SALES_DESC',
-    price: priceTab.value,
-    // minPrice,
-    // maxPrice,
+  try {
+    const params = {
+      page: pageNo,
+      size: pageSize,
+      sort: 'SALES_DESC',
+      price: priceTab.value,
+    }
+    const res = await getList(params)
+    paging.value.complete(res.data.list)
+  }
+  finally {
+    if (pageNo === 1) {
+      isProductListLoading.value = false
+    }
   }
-  const res = await getList(params)
-  paging.value.complete(res.data.list)
 }
 
 async function getBannerList() {
@@ -151,6 +158,8 @@ async function getBannerList() {
 }
 
 const unread = ref(0)
+const isPageLoading = ref(true) // 页面加载状态
+
 async function getUnread() {
   try {
     const res = await noticeUnread()
@@ -161,10 +170,18 @@ async function getUnread() {
   catch {}
 }
 
-onShow(() => {
-  getUnread()
-  getNewList()
-  getBannerList()
+onShow(async () => {
+  try {
+    isPageLoading.value = true
+    await Promise.all([
+      getUnread(),
+      getNewList(),
+      getBannerList(),
+    ])
+  }
+  finally {
+    isPageLoading.value = false
+  }
 })
 </script>
 
@@ -194,63 +211,166 @@ onShow(() => {
         </view>
       </view>
     </template>
-    <wd-swiper
-      v-model:current="current"
-      :list="swiperList"
-      value-key="image"
-      autoplay
-      :indicator="{ type: 'fraction' }"
-      indicator-position="bottom-right"
-      @click="handleSwiperClick"
-    />
-    <view class="px-24rpx pb-24rpx">
-      <view class="flex items-end justify-between pb-22rpx pt-24rpx">
-        <view
-          v-for="(item, index) in navIcons"
-          :key="index"
-          class="flex flex-col items-center"
-          @click="toPage(item.url)"
-        >
-          <image :src="item.image" :style="`width: ${item.size}; height: ${item.size};`" />
-          <view class="mt-14rpx whitespace-pre-line text-center text-22rpx text-#898989 font-bold">
-            {{ $t(item.title) }}
-          </view>
+    <!-- 页面加载时显示骨架屏 -->
+    <template v-if="isPageLoading">
+      <!-- 轮播图骨架屏 -->
+      <wd-skeleton
+        :row-col="[{ height: '400rpx' }]"
+        animation="gradient"
+      />
+
+      <view class="px-24rpx pb-24rpx">
+        <!-- 导航图标区域骨架屏 -->
+        <view class="pb-22rpx pt-24rpx">
+          <wd-skeleton
+            :row-col="[
+              [
+                { width: '100rpx', height: '100rpx', type: 'circle' },
+                { width: '100rpx', height: '100rpx', type: 'circle' },
+                { width: '100rpx', height: '100rpx', type: 'circle' },
+                { width: '100rpx', height: '100rpx', type: 'circle' },
+                { width: '100rpx', height: '100rpx', type: 'circle' },
+              ],
+              [
+                { width: '60rpx', height: '24rpx' },
+                { width: '60rpx', height: '24rpx' },
+                { width: '60rpx', height: '24rpx' },
+                { width: '60rpx', height: '24rpx' },
+                { width: '60rpx', height: '24rpx' },
+              ],
+            ]"
+            animation="gradient"
+          />
+        </view>
+
+        <!-- 新品区域骨架屏 -->
+        <view class="mb-32rpx">
+          <wd-skeleton
+            :row-col="[
+              { width: '120rpx', height: '32rpx', marginBottom: '16rpx' }, // 标题
+              [
+                { width: '260rpx', height: '260rpx' },
+                { width: '260rpx', height: '260rpx', marginLeft: '16rpx' },
+                { width: '260rpx', height: '260rpx', marginLeft: '16rpx' },
+              ],
+            ]"
+            animation="gradient"
+          />
+        </view>
+
+        <!-- 价格分类标签骨架屏 -->
+        <view class="mb-20rpx">
+          <wd-skeleton
+            :row-col="[
+              [
+                { width: '80rpx', height: '36rpx' },
+                { width: '80rpx', height: '36rpx', marginLeft: '20rpx' },
+                { width: '80rpx', height: '36rpx', marginLeft: '20rpx' },
+                { width: '80rpx', height: '36rpx', marginLeft: '20rpx' },
+                { width: '80rpx', height: '36rpx', marginLeft: '20rpx' },
+              ],
+            ]"
+            animation="gradient"
+          />
+        </view>
+
+        <!-- 商品列表骨架屏 -->
+        <view class="grid grid-cols-2 gap-20rpx">
+          <wd-skeleton
+            v-for="i in 6"
+            :key="i"
+            :row-col="[
+              { height: '340rpx' }, // 商品图片
+              { width: '180rpx', height: '40rpx', marginTop: '10rpx' }, // 商品名称
+              [
+                { width: '80rpx', height: '24rpx' }, // 价格
+                { width: '60rpx', height: '20rpx' }, // 销量
+              ],
+            ]"
+            animation="gradient"
+          />
         </view>
       </view>
-      <view v-if="newProducts.length">
-        <view class="mb-16rpx text-32rpx">
-          {{ $t('home.news') }}
+    </template>
+
+    <!-- 实际内容 -->
+    <template v-else>
+      <wd-swiper
+        v-model:current="current"
+        :list="swiperList"
+        value-key="image"
+        autoplay
+        indicator
+        indicator-position="bottom-right"
+        @click="handleSwiperClick"
+      />
+      <view class="px-24rpx pb-24rpx">
+        <view class="flex items-end justify-between pb-22rpx pt-24rpx">
+          <view
+            v-for="(item, index) in navIcons"
+            :key="index"
+            class="flex flex-col items-center"
+            @click="toPage(item.url)"
+          >
+            <image :src="item.image" :style="`width: ${item.size}; height: ${item.size};`" />
+            <view class="mt-14rpx whitespace-pre-line text-center text-22rpx text-#898989 font-bold">
+              {{ $t(item.title) }}
+            </view>
+          </view>
         </view>
-        <scroll-view scroll-x>
-          <view class="flex items-center gap-16rpx">
+        <view v-if="newProducts.length">
+          <view class="mb-16rpx text-32rpx">
+            {{ $t('home.news') }}
+          </view>
+          <scroll-view scroll-x class="whitespace-nowrap">
+            <view class="flex items-center gap-16rpx pb-20rpx" style="min-width: max-content;">
+              <Product
+                v-for="(item, index) in newProducts"
+                :key="index"
+                :title-font-size="18"
+                :item="item"
+                class="shrink-0"
+                @item-click="toPage('/pages/productDetail/productDetail', { productId: item.productId })"
+              />
+            </view>
+          </scroll-view>
+        </view>
+        <view class="productList">
+          <wd-tabs v-model="priceTab" slidable="always" :line-width="0" :line-height="0" @click="() => queryList(1, 20)">
+            <template v-for="item in priceTabList" :key="item">
+              <wd-tab :title="$t(item.title)" :name="item.value" />
+            </template>
+          </wd-tabs>
+          <!-- Tab切换时的商品列表骨架屏 -->
+          <view v-if="isProductListLoading" class="grid grid-cols-2 gap-20rpx">
+            <wd-skeleton
+              v-for="i in 6"
+              :key="i"
+              :row-col="[
+                { height: '340rpx', borderRadius: '12rpx' }, // 商品图片
+                { width: '180rpx', height: '40rpx', marginTop: '10rpx' }, // 商品名称
+                [
+                  { width: '80rpx', height: '24rpx' }, // 价格
+                  { width: '60rpx', height: '20rpx' }, // 销量
+                ],
+              ]"
+              animation="gradient"
+            />
+          </view>
+          <!-- 实际商品列表 -->
+          <view v-else class="grid grid-cols-2 gap-20rpx">
             <Product
-              v-for="(item, index) in newProducts"
+              v-for="(item, index) in dataList"
               :key="index"
-              :title-font-size="18"
+              width="100%"
+              :height="340"
               :item="item"
               @item-click="toPage('/pages/productDetail/productDetail', { productId: item.productId })"
             />
           </view>
-        </scroll-view>
-      </view>
-      <view class="productList">
-        <wd-tabs v-model="priceTab" slidable="always" :line-width="0" :line-height="0" @click="() => queryList(1, 20)">
-          <template v-for="item in priceTabList" :key="item">
-            <wd-tab :title="$t(item.title)" :name="item.value" />
-          </template>
-        </wd-tabs>
-        <view class="grid grid-cols-2 gap-20rpx">
-          <Product
-            v-for="(item, index) in dataList"
-            :key="index"
-            width="100%"
-            :height="340"
-            :item="item"
-            @item-click="toPage('/pages/productDetail/productDetail', { productId: item.productId })"
-          />
         </view>
       </view>
-    </view>
+    </template>
   </z-paging>
 </template>