refactor(components): migrate to vant and tailwind, clean up

- refactor TopNavBar: replace legacy flex classes with Tailwind utilities, swap uni-icons to Vant van-icon, remove unused status bar placeholder and old SCSS classes
- overhaul ImageSwiper: replace custom swiper with Vant Swipe components, migrate all SCSS styles to inline Tailwind classes, remove unused znicons font assets and delete old stylesheet
- clean up goods page: remove unused TopNavBar import and template, remove dead navOpacity scroll logic, remove local style import
This commit is contained in:
DEV_DSW
2026-05-28 09:35:03 +08:00
parent b98687bebc
commit 4edf19ce0c
5 changed files with 29 additions and 140 deletions

View File

@@ -1,33 +1,32 @@
<template> <template>
<div class="image-swiper"> <div class="relative w-full">
<swiper class="swiper-box" :style="swiperStyle" :autoplay="false" :interval="3000" :duration="1000" <van-swipe class="overflow-hidden" :style="swiperStyle" @change="handleSwiperChange">
:current="active" @change="handleSwiperChange"> <van-swipe-item class="swiper-item" v-for="(item, index) in thumbnails" :key="index">
<swiper-item class="swiper-item" v-for="(item, index) in thumbnails" :key="index"> <img class="w-full h-full" :src="item.photoUrl">
<img class="swiper-item-image" :src="item.photoUrl" mode="aspectFill"> </van-swipe-item>
</swiper-item> </van-swipe>
</swiper>
<!-- 缩略图部分 --> <!-- 缩略图部分 -->
<div v-if="showThumbnails && thumbnails.length > 0" class="thumbnail-box" :style="thumbnailBoxStyle"> <div v-if="showThumbnails && thumbnails.length"
<div class="thumbnail-scroll" scroll-x="true" :scroll-left="scrollLeft" :scroll-with-animation="true" class="absolute left-[12px] right-[12px] flex items-end flex-row flex-nowrap" :style="thumbnailBoxStyle">
show-scrollbar="false"> <div class="flex-auto h-full whitespace-nowrap" scroll-x="true" :scroll-left="scrollLeft"
<div class="thumbnail-list" v-if="thumbnails.length > 1"> :scroll-with-animation="true" show-scrollbar="false">
<div class="flex items-center gap-[10px]" v-if="thumbnails.length > 1">
<div v-for="(thumb, index) in thumbnails" :key="index" <div v-for="(thumb, index) in thumbnails" :key="index"
:class="['thumbnail-item', { active: index === active }]" :id="`thumbnail-${index}`" :class="['shrink-0 text-center', index === active && 'border-2 border-white border-solid']"
@click="handleThumbnailClick(index)"> :id="`thumbnail-${index}`" @click="handleThumbnailClick(index)">
<img class="thumbnail-image" :src="thumb.photoUrl" mode="aspectFill"> <img class="w-[36px] h-[36px] rounded-card border border-[#171717] border-solid" :src="thumb.photoUrl">
</div> </div>
</div> </div>
</div> </div>
<div class="custom-indicator" @click="handlePredivClick"> <div class="ml-[12px] bg-[rgba(0,0,0,0.5)] rounded-[50px] p-[0_6px_4px_8px] flex-auto shrink-0 whitespace-nowrap"
<uni-icons fontFamily="znicons" size="10" color="#fff">{{ @click="handlePredivClick">
zniconsMap["zn-camera"] <van-icon name="arrow-left" size="10" color="#fff"></van-icon>
}}</uni-icons> <span class="mx-[4px] text-[10px] text-white align-center">
<span class="custom-indicator-text">{{ thumbnails.length }}</span> {{ thumbnails.length }}
<uni-icons fontFamily="znicons" size="10" color="#fff">{{ </span>
zniconsMap["zn-nav-arrow-right"] <van-icon name="arrow-right" size="10" color="#fff"></van-icon>
}}</uni-icons>
</div> </div>
</div> </div>
</div> </div>
@@ -35,8 +34,8 @@
<script setup> <script setup>
import { ref, computed, nextTick } from "vue"; import { ref, computed, nextTick } from "vue";
import { zniconsMap } from "@/assets/fonts/znicons";
import { usePictureStore } from "@/store"; import { usePictureStore } from "@/store";
const pictureStore = usePictureStore(); const pictureStore = usePictureStore();
// Props定义 // Props定义
@@ -144,7 +143,3 @@ const handlePredivClick = () => {
}); });
}; };
</script> </script>
<style scoped lang="scss">
@import "./styles/index.scss";
</style>

View File

@@ -1,85 +0,0 @@
@font-face {
font-family: znicons;
src: url("@/assets/fonts/znicons.ttf");
}
.image-swiper {
position: relative;
width: 100%;
}
.swiper-box {
overflow: hidden;
// 高度和圆角通过内联样式动态设置
}
.swiper-item .swiper-item-image {
width: 100%;
height: 100%;
}
.thumbnail-box {
position: absolute;
left: 12px;
right: 12px;
display: flex;
align-items: flex-end;
flex-direction: row;
flex-wrap: nowrap;
max-width: 100%;
box-sizing: border-box;
}
.custom-indicator {
margin-left: 12px;
background: rgba(0, 0, 0, 0.5);
border-radius: 50px;
padding: 0 6px 4px 8px;
flex: 0 0 auto;
flex-shrink: 0;
white-space: nowrap;
}
.custom-indicator-text {
margin: 0 4px;
font-size: 10px;
text-align: center;
align-items: center;
color: #fff;
}
.thumbnail-scroll {
flex: 1 1 auto;
min-width: 0; // 允许在flex容器中收缩以适配剩余空间
overflow: auto; // 防止超出thumbnail-box的宽度
height: 100%;
white-space: nowrap;
}
.thumbnail-list {
display: flex;
align-items: center;
gap: 10px;
}
.thumbnail-item {
flex-shrink: 0;
text-align: center;
transition: all 0.3s ease;
&.active {
.thumbnail-image {
border: 2px solid #fff;
}
}
}
.thumbnail-item .thumbnail-image {
width: 36px;
height: 36px;
border-radius: 8px;
box-sizing: border-box;
border: 1px solid #171717;
transition: all 0.3s ease;
display: block;
}

View File

@@ -1,30 +1,26 @@
<template> <template>
<div :class="navBarClass" :style="navBarStyle"> <div :class="navBarClass" :style="navBarStyle">
<!-- 状态栏占位 -->
<div :style="{ height: statusBarHeight + 'px' }" v-if="!hideStatusBar"></div>
<!-- 导航栏内容 --> <!-- 导航栏内容 -->
<div class="flex flex-items-center flex-justify-between border-box pl-8 pr-8" <div class="flex items-center justify-between px-[8px]" :style="{ height: navBarHeight + 'px' }">
:style="{ height: navBarHeight + 'px' }">
<!-- 左侧返回按钮 --> <!-- 左侧返回按钮 -->
<div class="nav-bar-left flex flex-items-center flex-justify-center" v-if="showBack" @click="handleBack"> <div class="w-[30px] h-[30px] flex items-center justify-center" v-if="showBack" @click="handleBack">
<uni-icons type="left" size="20" :color="backIconColor" /> <van-icon name="arrow-left" size="20" :color="backIconColor" />
</div> </div>
<!-- 中间标题区域 --> <!-- 中间标题区域 -->
<div :class="[ <div :class="[
'nav-bar-center flex flex-items-center flex-justify-center', 'flex-1 h-[30px] px-[20px] flex items-center justify-center',
`nav-bar-center--${titleAlign}`, `nav-bar-center--${titleAlign}`,
]"> ]">
<slot name="title"> <slot name="title">
<text class="font-size-17 font-500 color-000" :style="{ color: titleColor }"> <text class="text-[17px] font-medium text-black" :style="{ color: titleColor }">
{{ title }} {{ title }}
</text> </text>
</slot> </slot>
</div> </div>
<!-- 右侧操作区域 --> <!-- 右侧操作区域 -->
<div class="nav-bar-right"> <div class="w-[30px] h-[30px]">
<slot name="right"></slot> <slot name="right"></slot>
</div> </div>
</div> </div>

View File

@@ -12,11 +12,6 @@
} }
} }
.nav-bar-left,
.nav-bar-right {
width: 30px;
height: 30px;
}
.nav-bar-center { .nav-bar-center {
flex: 1; flex: 1;

View File

@@ -1,12 +1,8 @@
<template> <template>
<div class="flex flex-col h-screen bg-white"> <div class="flex flex-col h-screen bg-white">
<TopNavBar :title="navOpacity < 0.5 ? '' : '商品详情'" :background="`rgba(217, 238, 255, ${navOpacity})`"
:titleColor="navOpacity < 0.5 ? '#ffffff' : '#000000'"
:backIconColor="navOpacity < 0.5 ? '#ffffff' : '#000000'" />
<!-- 滚动区域 --> <!-- 滚动区域 -->
<div class="flex-1 overflow-y-auto" @scroll="handleScroll"> <div class="flex-1 overflow-y-auto" @scroll="handleScroll">
<imgSwiper :border-radius="0" :height="300" :images="goodsData.commodityPhotoList" thumbnailBottom="42px" /> <ImageSwiper :border-radius="0" :height="300" :images="goodsData.commodityPhotoList" thumbnailBottom="42px" />
<div class="bg-white py-[20px] relative -mt-[30px] z-10 rounded-t-[28px]"> <div class="bg-white py-[20px] relative -mt-[30px] z-10 rounded-t-[28px]">
<!-- 商品信息组件 --> <!-- 商品信息组件 -->
@@ -66,7 +62,6 @@ import { ref } from "vue";
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { goodsDetail, commodityDailyPriceList } from "@/api/goods"; import { goodsDetail, commodityDailyPriceList } from "@/api/goods";
import { DateUtils } from "@/utils/dateUtils"; import { DateUtils } from "@/utils/dateUtils";
import TopNavBar from "@/components/TopNavBar/index.vue";
import ImageSwiper from "@/components/ImageSwiper/index.vue"; import ImageSwiper from "@/components/ImageSwiper/index.vue";
import GoodInfo from "./components/GoodInfo/index.vue"; import GoodInfo from "./components/GoodInfo/index.vue";
import Calender from "@/components/Calender/index.vue"; import Calender from "@/components/Calender/index.vue";
@@ -81,7 +76,6 @@ import { useSelectedDateStore } from "@/store";
const router = useRouter() const router = useRouter()
// 导航栏透明度 - 默认透明,随滚动变为不透明 // 导航栏透明度 - 默认透明,随滚动变为不透明
const navOpacity = ref(0);
const calendarVisible = ref(false); const calendarVisible = ref(false);
const goodsData = ref({}); const goodsData = ref({});
@@ -90,8 +84,6 @@ const handleScroll = (e) => {
const scrollTop = e.detail.scrollTop; const scrollTop = e.detail.scrollTop;
// 设置一个阈值当滚动超过200px时导航栏完全不透明 // 设置一个阈值当滚动超过200px时导航栏完全不透明
const threshold = 200; const threshold = 200;
// 计算透明度范围从0到1
navOpacity.value = Math.min(scrollTop / threshold, 1);
}; };
const selectedDate = ref({ const selectedDate = ref({
@@ -247,7 +239,3 @@ const navigateToPay = ({ commodityId }) => {
}) })
}; };
</script> </script>
<style scoped lang="scss">
@import "./styles/index.scss";
</style>