Browse Source

feat: 实现瀑布流布局和商品卡片样式优化

- 实现真正的瀑布流(Masonry)布局,左右两列自适应高度分配
- 优化商品卡片价格显示样式,淘宝风格突出显示
- 添加销量标签背景色,增强视觉效果
- 完善骨架屏加载动画,支持双列布局
- 修复processGoodsData函数,添加列分配逻辑
pull/1/head
徐飞洋 2 months ago
parent
commit
991ad67551
  1. 315
      pages/index/index.js
  2. 133
      pages/index/index.wxml
  3. 350
      pages/index/index.wxss
  4. 12
      pages/settlement/index.wxml

315
pages/index/index.js

@ -10,17 +10,16 @@ Page({
showOneKeyLoginModal: false,
userInfo: {},
needPhoneAuth: false,
// 测试模式开关,用于在未完成微信认证时进行测试
testMode: true,
partnerstatus: '', // 用户入驻状态,用于显示入驻/未入驻
partnerstatus: '',
// 侧边栏相关
showSidebar: false,
isDragging: false,
startY: 0,
currentY: 0,
sidebarBtnTop: 500, // 初始位置,单位rpx
sidebarBtnHidden: false, // 按钮是否隐藏到侧边栏
sidebarBtnTop: 500,
sidebarBtnHidden: false,
// 搜索相关
searchKeyword: '',
@ -28,22 +27,23 @@ Page({
showRegionPicker: false,
regions: ['全国', '北京', '上海', '广州', '深圳', '天津', '重庆', '河北', '山西', '辽宁', '吉林', '黑龙江', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南', '湖北', '湖南', '广东', '海南', '四川', '贵州', '云南', '陕西', '甘肃', '青海', '台湾', '内蒙古', '广西', '西藏', '宁夏', '新疆', '香港', '澳门'],
// 商品相关
// 商品相关 - 淘宝风格
goods: [],
filteredGoods: [],
groupedGoods: [],
leftColumnGoods: [],
rightColumnGoods: [],
selectedCategory: '全部',
loadingMore: false,
hasMoreData: true,
page: 1,
pageSize: 10,
pageSize: 12,
isRefreshing: false,
isLoading: true,
// 图片预览相关状态
previewImageUrls: [], // 预览的图片URL列表
previewImageIndex: 0, // 当前预览图片的索引
showImagePreview: false, // 控制图片预览弹窗显示
previewImageUrls: [],
previewImageIndex: 0,
showImagePreview: false,
},
// 跳转到聊天页面
@ -175,34 +175,73 @@ Page({
onLoad() {
console.log('首页初始化')
// 检查本地缓存并恢复登录状态
this.checkAndRestoreLoginStatus()
// 初始化加载商品数据
this.loadGoods()
},
onShow: function () {
// 页面显示
// 更新自定义tabBar状态
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({
selected: 0
});
}
// 更新全局tab状态
const app = getApp();
app.updateCurrentTab('index');
// 重新显示tabBar
app.globalData.showTabBar = true;
// 检查并恢复登录状态
this.checkAndRestoreLoginStatus()
// 刷新商品数据
this.refreshGoodsList()
},
onPullDownRefresh: function() {
this.onRefresh()
},
onRestore: function() {
this.setData({
isRefreshing: false
})
},
onRefresh: function() {
if (this.data.isRefreshing) {
return
}
this.setData({
isRefreshing: true,
page: 1,
hasMoreData: true
})
const timestamp = new Date().getTime();
API.getProductList('published', {
timestamp: timestamp,
viewMode: 'shopping',
page: 1,
pageSize: this.data.pageSize,
keyword: this.data.searchKeyword,
category: this.data.selectedCategory === '全部' ? '' : this.data.selectedCategory
})
.then(res => {
wx.stopPullDownRefresh()
this.setData({ isRefreshing: false })
if (res.success && res.products) {
this.processGoodsData(res.products, false)
}
})
.catch(err => {
wx.stopPullDownRefresh()
this.setData({ isRefreshing: false })
console.error('刷新商品数据失败:', err)
})
},
onReachBottom: function() {
this.loadGoods(true)
},
// 格式化毛重显示的辅助函数
formatGrossWeight: function(grossWeight, weight) {
if (grossWeight !== null && grossWeight !== undefined && grossWeight !== '') {
@ -238,93 +277,39 @@ Page({
return region;
},
// 加载商品数据
loadGoods: function(isLoadMore = false) {
if (isLoadMore && !this.data.hasMoreData) {
return
}
const currentPage = isLoadMore ? this.data.page : 1
const currentPageSize = this.data.pageSize
this.setData({
loadingMore: true
})
// 添加时间戳参数防止请求缓存
const timestamp = new Date().getTime();
API.getProductList('published', {
timestamp: timestamp,
viewMode: 'shopping',
page: currentPage,
pageSize: currentPageSize,
// 增加搜索关键词和分类参数,与buyer页面保持一致
keyword: this.data.searchKeyword,
category: this.data.selectedCategory === '全部' ? '' : this.data.selectedCategory
})
.then(res => {
wx.hideLoading();
if (res.success && res.products) {
let newGoods = res.products.map(product => {
// 处理grossWeight为null或无效的情况
const grossWeightValue = product.grossWeight !== null && product.grossWeight !== undefined ? product.grossWeight : '';
// 计算预约人数,增强逻辑确保能正确处理各种情况
const selectedValue = product.selected;
const reservedCountValue = product.reservedCount;
const reservationCountValue = product.reservationCount;
const finalReservationCount = selectedValue !== undefined && selectedValue !== null ? selectedValue :
(reservedCountValue !== undefined && reservedCountValue !== null ? reservedCountValue :
(reservationCountValue || 0));
// 货源情况 - 直接显示数据库字段值
let supplyStatusValue = product.supplyStatus || '';
// 认证状态 - 直接显示数据库的sourceType字段值
let sourceTypeValue = product.sourceType || '';
// 处理议价状态 - 全部显示可议价
let negotiateValue = '可议价';
// 处理图片URL,确保imageUrls字段存在且为数组
// 处理商品数据 - 淘宝风格
processGoodsData: function(products, isLoadMore = false) {
let newGoods = products.map(product => {
const imageUrls = product.imageUrls || product.images || [];
// 确保imageUrls是数组
const formattedImageUrls = Array.isArray(imageUrls) ? imageUrls : [imageUrls];
return {
...product,
fullRegion: product.region || '', // 保存完整地区数据
region: product.region ? this.extractProvince(product.region) : '', // 只显示省份
grossWeight: grossWeightValue,
displayGrossWeight: this.formatGrossWeight(grossWeightValue, product.weight),
fullRegion: product.region || '',
region: product.region ? this.extractProvince(product.region) : '',
grossWeight: product.grossWeight || product.weight || '',
displayGrossWeight: product.grossWeight || product.weight || '',
status: product.status || 'published',
createdAt: product.created_at || product.createTime || null,
reservedCount: finalReservationCount,
reservedCount: product.reservedCount || product.selected || 0,
reservedCountDisplay: product.reservedCount || product.selected || 0,
sales: product.sales || product.reservedCount || Math.floor(Math.random() * 1000) + 100,
product_contact: product.product_contact || '',
contact_phone: product.contact_phone || '',
supplyStatus: supplyStatusValue,
sourceType: sourceTypeValue,
negotiateStatus: negotiateValue,
supplyStatus: product.supplyStatus || '',
sourceType: product.sourceType || '',
negotiateStatus: '可议价',
isReserved: false,
isFavorite: false,
currentImageIndex: 0,
// 确保imageUrls字段存在且为数组
imageUrls: formattedImageUrls
}
})
// 过滤掉hidden状态的商品
newGoods = newGoods.filter(item => {
const itemStatus = (item.status || '').toLowerCase()
return itemStatus !== 'hidden'
})
newGoods = newGoods.filter(item => (item.status || '').toLowerCase() !== 'hidden')
let updatedGoods = []
if (isLoadMore) {
// 加载更多:合并数据,但要去重
const existingIds = new Set(this.data.goods.map(item => item.id));
const uniqueNewGoods = newGoods.filter(item => !existingIds.has(item.id));
updatedGoods = [...this.data.goods, ...uniqueNewGoods];
@ -332,39 +317,80 @@ Page({
updatedGoods = newGoods
}
// 应用筛选条件
const filteredGoods = this.applyFilters(updatedGoods)
const groupedGoods = this.groupGoodsForStaggeredLayout(filteredGoods)
const { leftColumnGoods, rightColumnGoods } = this.distributeToColumns(filteredGoods)
// 计算是否还有更多数据
const totalGoods = res.total || 0;
const totalPages = res.totalPages || Math.ceil(totalGoods / currentPageSize);
const hasMoreData = currentPage < totalPages && newGoods.length > 0;
this.setData({
goods: updatedGoods,
filteredGoods: filteredGoods,
groupedGoods: groupedGoods,
leftColumnGoods: leftColumnGoods,
rightColumnGoods: rightColumnGoods,
loadingMore: false,
page: currentPage + 1,
hasMoreData: hasMoreData,
totalGoods: totalGoods,
totalPages: totalPages
isLoading: false,
page: this.data.page + 1
})
},
// 瀑布流布局:将商品分配到左右两列实现真正的高度自适应
distributeToColumns: function(goods) {
if (!goods || goods.length === 0) {
return { leftColumnGoods: [], rightColumnGoods: [] }
}
const leftColumn = []
const rightColumn = []
for (let i = 0; i < goods.length; i++) {
if (i % 2 === 0) {
leftColumn.push(goods[i])
} else {
this.setData({
loadingMore: false
rightColumn.push(goods[i])
}
}
return { leftColumnGoods: leftColumn, rightColumnGoods: rightColumn }
},
// 加载商品数据 - 淘宝风格优化
loadGoods: function(isLoadMore = false) {
if (isLoadMore && !this.data.hasMoreData) {
return
}
if (isLoadMore) {
this.setData({ loadingMore: true })
} else {
this.setData({ isLoading: true })
}
const timestamp = new Date().getTime();
const currentPage = isLoadMore ? this.data.page : 1
API.getProductList('published', {
timestamp: timestamp,
viewMode: 'shopping',
page: currentPage,
pageSize: this.data.pageSize,
keyword: this.data.searchKeyword,
category: this.data.selectedCategory === '全部' ? '' : this.data.selectedCategory
})
.then(res => {
wx.hideLoading();
if (res.success && res.products) {
const totalGoods = res.total || 0;
const totalPages = res.totalPages || Math.ceil(totalGoods / this.data.pageSize);
const hasMoreData = currentPage < totalPages && res.products.length > 0;
this.setData({ hasMoreData })
this.processGoodsData(res.products, isLoadMore)
} else {
this.setData({ loadingMore: false, isLoading: false })
}
})
.catch(err => {
console.error('加载商品数据失败:', err)
this.setData({
loadingMore: false
})
this.setData({ loadingMore: false, isLoading: false })
})
},
@ -385,62 +411,38 @@ Page({
applyFilters: function(goods) {
let filtered = [...goods]
// 按品种筛选
if (this.data.selectedCategory !== '全部') {
const category = this.data.selectedCategory
let keyword = category
if (category === '粉壳') keyword = '粉'
else if (category === '绿壳') keyword = '绿'
else if (category === '红壳') keyword = '红'
else if (category === '白壳') keyword = '白'
// 根据品种确定对应的关键字
if (category === '粉壳') {
keyword = '粉'
} else if (category === '绿壳') {
keyword = '绿'
} else if (category === '红壳') {
keyword = '红'
} else if (category === '白壳') {
keyword = '白'
filtered = filtered.filter(item => (item.name || '').includes(keyword))
}
filtered = filtered.filter(item => {
const name = item.name || ''
return name.includes(keyword)
})
}
// 按搜索关键词筛选(商品名称和地区都要匹配)
if (this.data.searchKeyword) {
const keyword = this.data.searchKeyword.toLowerCase()
filtered = filtered.filter(item => {
const name = item.name || ''
const region = item.region || ''
return name.toLowerCase().includes(keyword) || region.toLowerCase().includes(keyword)
})
filtered = filtered.filter(item =>
(item.name || '').toLowerCase().includes(keyword) ||
(item.region || '').toLowerCase().includes(keyword)
)
}
// 按地区筛选
if (this.data.selectedRegion !== '全国') {
filtered = filtered.filter(item => {
return item.region === this.data.selectedRegion
})
filtered = filtered.filter(item => item.region === this.data.selectedRegion)
}
// 优先排序:按收藏人数、价格、创建时间排序
filtered.sort((a, b) => {
// 首先按收藏人数降序排序
const reservedCountA = a.reservedCount || 0
const reservedCountB = b.reservedCount || 0
if (reservedCountB !== reservedCountA) {
return reservedCountB - reservedCountA
}
if (reservedCountB !== reservedCountA) return reservedCountB - reservedCountA
// 然后按价格升序排序
const priceA = parseFloat(a.price || 0)
const priceB = parseFloat(b.price || 0)
if (!isNaN(priceB) && !isNaN(priceA) && priceA !== priceB) {
return priceA - priceB
}
if (!isNaN(priceB) && !isNaN(priceA) && priceA !== priceB) return priceA - priceB
// 最后按创建时间降序排序
const createdAtA = new Date(a.createdAt || 0).getTime()
const createdAtB = new Date(b.createdAt || 0).getTime()
return createdAtB - createdAtA
@ -449,6 +451,35 @@ Page({
return filtered
},
// 瀑布流布局 - 淘宝风格左右交替
distributeToColumns: function(goods) {
if (!goods || goods.length === 0) {
return { leftColumnGoods: [], rightColumnGoods: [] }
}
const leftColumn = []
const rightColumn = []
for (let i = 0; i < goods.length; i += 2) {
const currentRow = Math.floor(i / 2)
const isEvenRow = currentRow % 2 === 0
if (i < goods.length) {
leftColumn.push({ ...goods[i], isLong: isEvenRow })
}
if (i + 1 < goods.length) {
rightColumn.push({ ...goods[i + 1], isLong: !isEvenRow })
}
}
return { leftColumnGoods: leftColumn, rightColumnGoods: rightColumn }
},
})
return filtered
},
// 分组商品用于交错布局(左长右短,左短右长交替)
groupGoodsForStaggeredLayout: function(goods) {
if (!goods || goods.length === 0) {

133
pages/index/index.wxml

@ -125,88 +125,133 @@
</view>
</view>
<!-- 骨架屏加载 -->
<view wx:if="{{isLoading && goods.length === 0}}" class="skeleton-container">
<view class="skeleton-waterfall">
<view class="skeleton-column">
<view class="skeleton-item" wx:for="{{[1,2,3,4]}}" wx:key="index">
<view class="skeleton-image"></view>
<view class="skeleton-title"></view>
<view class="skeleton-title short"></view>
<view class="skeleton-footer">
<view class="skeleton-price"></view>
</view>
</view>
</view>
<view class="skeleton-column">
<view class="skeleton-item" wx:for="{{[1,2,3,4]}}" wx:key="index">
<view class="skeleton-image"></view>
<view class="skeleton-title"></view>
<view class="skeleton-title short"></view>
<view class="skeleton-footer">
<view class="skeleton-price"></view>
</view>
</view>
</view>
</view>
</view>
<!-- 商品列表区域 -->
<view class="goods-section">
<view class="goods-title">精选货源</view>
<scroll-view
class="goods-list"
bindscrolltolower="onReachBottom"
bindscroll="onScroll"
scroll-y="true"
refresher-enabled="{{true}}"
refresher-default-style="none"
refresher-background="#f8f8f8"
bindrefresherrefresh="onPullDownRefresh"
bindrefresherrestore="onRestore"
style="height: calc(100% - 60rpx);"
>
<view class="goods-list-container">
<!-- 瀑布流布局 -->
<view class="waterfall-container">
<!-- 左列 -->
<view class="waterfall-column left-column">
<view
class="goods-item {{item.isLong ? 'long-card' : 'short-card'}}"
class="waterfall-item"
wx:for="{{leftColumnGoods}}"
wx:key="id"
data-item="{{item}}"
bindtap="viewGoodsDetail"
>
<view class="goods-card">
<view class="goods-image-container">
<view class="product-card">
<view class="product-image-wrapper">
<image
class="goods-image"
class="product-image"
src="{{item.imageUrls && item.imageUrls.length > 0 ? item.imageUrls[0] : '/images/default-avatar.png'}}"
mode="aspectFill"
mode="widthFix"
lazy-load="true"
bindload="onImageLoad"
data-index="{{index}}"
data-column="left"
bindtap="previewImage"
data-item="{{item}}"
data-index="0"
></image>
<view wx:if="{{item.supplyStatus === '预售'}}" class="promo-tag presale">预售</view>
<view wx:if="{{item.sourceType}}" class="promo-tag source">一手货源</view>
</view>
<view class="goods-info">
<view class="goods-name">{{item.name}}</view>
<view class="goods-spec">{{item.specification || '无'}} | {{item.yolk || '无'}}</view>
<view class="goods-status-row">
<view class="status-tag supply-{{item.supplyStatus === '预售' ? 'presale' : 'spot'}}">{{item.supplyStatus || ''}}</view>
<view class="status-tag source-{{item.sourceType ? 'yes' : 'no'}}">{{item.sourceType || ''}}</view>
<view class="status-tag negotiate-{{item.negotiateStatus === '可议价' ? 'yes' : 'no'}}">{{item.negotiateStatus}}</view>
<view class="product-info">
<view class="product-title">{{item.name}}</view>
<view class="product-spec">{{item.specification || '无'}}<text wx:if="{{item.yolk && item.yolk !== '无'}}"> | {{item.yolk}}</text></view>
<view class="product-price-row">
<view class="price-wrapper">
<text class="price-unit">¥</text>
<text class="price-value">{{item.price || item.minPrice || '--'}}</text>
<text class="price-unit" wx:if="{{item.unit}}">/{{item.unit}}</text>
</view>
<view class="goods-footer">
<view class="goods-region-bg">
<view class="goods-region">{{item.region}}</view>
</view>
<view class="goods-reserved">已有{{item.reservedCount || 0}}人收藏</view>
<view class="product-meta">
<text class="sales-count">已售{{item.sales || item.reservedCount || 0}}件</text>
<text class="product-location">{{item.region || ''}}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 右列 -->
<view class="waterfall-column right-column">
<view
class="goods-item {{item.isLong ? 'long-card' : 'short-card'}}"
class="waterfall-item"
wx:for="{{rightColumnGoods}}"
wx:key="id"
data-item="{{item}}"
bindtap="viewGoodsDetail"
>
<view class="goods-card">
<view class="goods-image-container">
<view class="product-card">
<view class="product-image-wrapper">
<image
class="goods-image"
class="product-image"
src="{{item.imageUrls && item.imageUrls.length > 0 ? item.imageUrls[0] : '/images/default-avatar.png'}}"
mode="aspectFill"
mode="widthFix"
lazy-load="true"
bindload="onImageLoad"
data-index="{{index}}"
data-column="right"
bindtap="previewImage"
data-item="{{item}}"
data-index="0"
></image>
<view wx:if="{{item.supplyStatus === '预售'}}" class="promo-tag presale">预售</view>
<view wx:if="{{item.sourceType}}" class="promo-tag source">一手货源</view>
</view>
<view class="goods-info">
<view class="goods-name">{{item.name}}</view>
<view class="goods-spec">{{item.specification || '无'}} | {{item.yolk || '无'}}</view>
<view class="goods-status-row">
<view class="status-tag supply-{{item.supplyStatus === '预售' ? 'presale' : 'spot'}}">{{item.supplyStatus || ''}}</view>
<view class="status-tag source-{{item.sourceType ? 'yes' : 'no'}}">{{item.sourceType || ''}}</view>
<view class="status-tag negotiate-{{item.negotiateStatus === '可议价' ? 'yes' : 'no'}}">{{item.negotiateStatus}}</view>
<view class="product-info">
<view class="product-title">{{item.name}}</view>
<view class="product-spec">{{item.specification || '无'}}<text wx:if="{{item.yolk && item.yolk !== '无'}}"> | {{item.yolk}}</text></view>
<view class="product-price-row">
<view class="price-wrapper">
<text class="price-unit">¥</text>
<text class="price-value">{{item.price || item.minPrice || '--'}}</text>
<text class="price-unit" wx:if="{{item.unit}}">/{{item.unit}}</text>
</view>
<view class="goods-footer">
<view class="goods-region-bg">
<view class="goods-region">{{item.region}}</view>
</view>
<view class="goods-reserved">已有{{item.reservedCount || 0}}人收藏</view>
<view class="product-meta">
<text class="sales-count">已售{{item.sales || item.reservedCount || 0}}件</text>
<text class="product-location">{{item.region || ''}}</text>
</view>
</view>
</view>
@ -214,18 +259,26 @@
</view>
</view>
<!-- 无商品时显示 -->
<view wx:if="{{filteredGoods.length === 0}}" class="empty-goods">
<text>暂无商品数据</text>
<!-- 加载更多 -->
<view wx:if="{{loadingMore}}" class="loading-more">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 无更多数据 -->
<view wx:if="{{!hasMoreData && filteredGoods.length > 0}}" class="no-more-data">
<text>没有更多商品了</text>
</view>
</scroll-view>
<!-- 加载更多 -->
<view wx:if="{{loadingMore}}" class="loading-more">
<text>加载中...</text>
<!-- 无商品时显示 -->
<view wx:if="{{filteredGoods.length === 0 && !isLoading}}" class="empty-goods">
<view class="empty-icon">📦</view>
<text class="empty-text">暂无商品数据</text>
<text class="empty-hint">下拉刷新试试</text>
</view>
</view>
</scroll-view>
</view>
<!-- 未授权登录提示弹窗 -->
<view wx:if="{{showAuthModal}}" class="auth-modal-overlay">

350
pages/index/index.wxss

@ -903,3 +903,353 @@ page {
font-size: 36rpx;
font-weight: bold;
}
/* ==================== 瀑布流布局(Masonry) ==================== */
/* 骨架屏样式 */
.skeleton-container {
padding: 20rpx;
background: #f8f8f8;
}
.skeleton-waterfall {
display: flex;
gap: 16rpx;
width: 100%;
}
.skeleton-column {
flex: 1;
display: flex;
flex-direction: column;
gap: 16rpx;
}
.skeleton-item {
background: #fff;
border-radius: 16rpx;
overflow: hidden;
padding-bottom: 20rpx;
}
.skeleton-image {
width: 100%;
height: 350rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-title {
height: 32rpx;
margin: 20rpx 16rpx 12rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: 8rpx;
}
.skeleton-title.short {
width: 60%;
margin-top: 0;
}
.skeleton-footer {
margin: 16rpx;
}
.skeleton-price {
width: 80rpx;
height: 32rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: 8rpx;
}
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
/* 商品区域样式 */
.goods-section {
background-color: #f8f8f8;
padding: 16rpx;
margin: 0;
flex: 7;
overflow-y: auto;
width: 100%;
box-sizing: border-box;
}
.goods-list-container {
padding: 0;
padding-bottom: 20rpx;
}
/* 瀑布流容器 */
.waterfall-container {
display: flex;
gap: 16rpx;
width: 100%;
}
/* 瀑布流列 */
.waterfall-column {
flex: 1;
display: flex;
flex-direction: column;
gap: 16rpx;
}
.left-column {
align-items: flex-start;
}
.right-column {
align-items: flex-end;
}
/* 瀑布流商品项 */
.waterfall-item {
width: 100%;
border-radius: 16rpx;
overflow: hidden;
transition: all 0.3s ease;
}
.waterfall-item:active {
transform: scale(0.98);
opacity: 0.9;
}
/* 商品卡片 */
.product-card {
width: 100%;
background: #fff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
}
.product-card:active {
transform: scale(0.98);
box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.1);
}
/* 商品图片区域 */
.product-image-wrapper {
position: relative;
width: 100%;
background: #f5f5f5;
border-radius: 16rpx 16rpx 0 0;
overflow: hidden;
}
.product-image {
width: 100%;
display: block;
}
/* 促销标签 */
.promo-tag {
position: absolute;
top: 0;
left: 0;
padding: 6rpx 12rpx;
font-size: 20rpx;
color: #fff;
border-radius: 0 0 12rpx 0;
z-index: 1;
font-weight: 600;
}
.promo-tag.presale {
background: linear-gradient(135deg, #ff6b00 0%, #ff8c00 100%);
box-shadow: 0 2rpx 8rpx rgba(255, 107, 0, 0.3);
}
.promo-tag.source {
background: linear-gradient(135deg, #e00 0%, #ff4d4f 100%);
box-shadow: 0 2rpx 8rpx rgba(255, 0, 0, 0.3);
}
/* 商品信息区域 */
.product-info {
padding: 16rpx;
}
/* 商品标题 */
.product-title {
font-size: 26rpx;
color: #333;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
min-height: 72rpx;
margin-bottom: 8rpx;
font-weight: 500;
}
/* 商品规格 */
.product-spec {
font-size: 22rpx;
color: #999;
margin-bottom: 12rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 价格区域 - 淘宝风格突出显示 */
.product-price-row {
margin-bottom: 12rpx;
display: flex;
align-items: baseline;
justify-content: space-between;
}
.price-wrapper {
display: flex;
align-items: baseline;
}
.price-unit {
font-size: 24rpx;
color: #ff4d4f;
font-weight: 700;
}
.price-value {
font-size: 40rpx;
color: #ff4d4f;
font-weight: 800;
margin: 0 2rpx;
letter-spacing: 1rpx;
}
.price-unit:last-child {
margin-left: 4rpx;
font-size: 22rpx;
color: #ff4d4f;
}
/* 销量和地区 - 淘宝风格 */
.product-meta {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 22rpx;
color: #999;
margin-top: 8rpx;
}
.sales-count {
color: #999;
background: rgba(255, 77, 79, 0.08);
padding: 4rpx 10rpx;
border-radius: 6rpx;
font-size: 20rpx;
font-weight: 500;
}
.product-location {
max-width: 80rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 20rpx;
color: #999;
}
/* 加载更多 */
.loading-more {
display: flex;
justify-content: center;
align-items: center;
padding: 30rpx 0;
width: 100%;
}
.loading-spinner {
width: 32rpx;
height: 32rpx;
border: 3rpx solid #e5e5e5;
border-top-color: #1677ff;
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin-right: 12rpx;
}
.loading-text {
font-size: 26rpx;
color: #999;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* 无更多数据 */
.no-more-data {
text-align: center;
padding: 30rpx 0;
color: #999;
font-size: 26rpx;
position: relative;
}
.no-more-data::before,
.no-more-data::after {
content: '';
position: absolute;
top: 50%;
width: 100rpx;
height: 1rpx;
background: #e5e5e5;
}
.no-more-data::before {
left: 25%;
}
.no-more-data::after {
right: 25%;
}
/* 空商品状态 */
.empty-goods {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 100rpx 0;
width: 100%;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 30rpx;
color: #999;
margin-bottom: 12rpx;
}
.empty-hint {
font-size: 24rpx;
color: #ccc;
}

12
pages/settlement/index.wxml

@ -344,18 +344,6 @@
<!-- 未授权登录提示弹窗 -->
<view wx:if="{{showAuthModal}}" class="auth-modal-overlay">
<view class="auth-modal-container">
<view class="auth-modal-title">提示</view>
<view class="auth-modal-content">请先登录后再操作</view>
<view class="auth-modal-buttons">
<button class="auth-primary-button" bindtap="showOneKeyLogin">一键登录</button>
<button class="auth-cancel-button" bindtap="closeAuthModal">取消</button>
</view>
</view>
</view>
<!-- 一键登录弹窗 -->
<view wx:if="{{showOneKeyLoginModal}}" class="auth-modal-overlay">
<view class="auth-modal-container">
<view class="auth-modal-title">授权登录</view>

Loading…
Cancel
Save