统一排行榜对齐星雀UI,页面资源后台配置实时刷新

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
XingQue
2026-06-27 01:26:11 +08:00
parent 1ab2e2080a
commit 21112173f2
292 changed files with 64010 additions and 81 deletions

View File

@@ -0,0 +1,296 @@
// pages/shangpinxiangqing/shangpinxiangqing.js
const app = getApp()
Page({
/**
* 页面的初始数据
*/
data: {
// 全局变量引用
ossImageUrl: '',
apiBaseUrl: '',
// 从上级页面传入的参数
shangpinId: '', // 商品ID
shangpinTitle: '', // 商品标题可选用于SEO
shangpinPrice: '', // 商品价格(可选)
// 商品详情数据
shangpinData: null, // 商品详情数据对象
isLoading: true, // 加载状态
loadError: false, // 加载错误
// 页面字段映射(后端返回字段名)
fieldMapping: {
tupian: 'tupian', // 商品图片URL
biaoti: 'biaoti', // 商品标题
jiage: 'jiage', // 商品价格
xiangqing: 'xiangqing', // 商品详细介绍
xiadanxuzhi: 'xiadanxuzhi', // 下单须知
guize: 'guize', // 规则图片URL
xiaoliang: 'xiaoliang', // 商品销量
kucun: 'kucun', // 商品库存
fenlei: 'fenlei', // 商品分类(可能有用)
pingfen: 'pingfen' // 商品评分(可能有用)
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 获取全局变量
this.setData({
ossImageUrl: app.globalData.ossImageUrl,
apiBaseUrl: app.globalData.apiBaseUrl,
shangpinId: options.id || '',
shangpinTitle: options.title || '',
shangpinPrice: options.price || ''
})
// 验证商品ID
if (!this.data.shangpinId) {
wx.showToast({
title: '商品不存在',
icon: 'none'
})
setTimeout(() => {
wx.navigateBack()
}, 1500)
return
}
// 加载商品详情数据
this.loadShangpinDetail()
},
onShow() {
// 原有代码...
// 🆕 注册通知组件
this.registerNotificationComponent();
},
// 🆕 新增:注册通知组件
registerNotificationComponent() {
const app = getApp();
const notificationComp = this.selectComponent('#global-notification');
if (notificationComp && notificationComp.showNotification) {
console.log('🏪 商城页面注册通知组件');
app.globalData.globalNotification = {
show: (data) => notificationComp.showNotification(data),
hide: () => notificationComp.hideNotification()
};
}
},
/**
* 加载商品详情数据
*/
loadShangpinDetail() {
const that = this
const { shangpinId, apiBaseUrl } = this.data
this.setData({ isLoading: true, loadError: false })
wx.request({
url: apiBaseUrl + '/shangpin/xiangqinghuoqu/',
method: 'POST',
header: {
'content-type': 'application/json'
},
data: {
shangpin_id: shangpinId
},
success(res) {
if (res.statusCode === 200 && res.data && res.data.code === 0) {
// 获取商品数据
const shangpinData = res.data.data || {}
// 处理价格显示
if (shangpinData.jiage) {
const jiage = parseFloat(shangpinData.jiage) || 0
const priceInteger = Math.floor(jiage)
let priceDecimal = '00'
const decimalPart = (jiage - priceInteger).toFixed(2)
if (decimalPart > 0) {
priceDecimal = decimalPart.toString().split('.')[1]
}
shangpinData.priceInteger = priceInteger
shangpinData.priceDecimal = priceDecimal
}
// 处理图片URL确保完整URL
if (shangpinData.tupian && !shangpinData.tupian.startsWith('http')) {
shangpinData.tupian = that.data.ossImageUrl + shangpinData.tupian
}
// 处理规则图片URL
if (shangpinData.guize && !shangpinData.guize.startsWith('http')) {
shangpinData.guize = that.data.ossImageUrl + shangpinData.guize
}
// 处理销量显示
if (shangpinData.xiaoliang) {
shangpinData.xiaoliangText = that.formatXiaoliang(shangpinData.xiaoliang)
}
// 处理库存显示
if (shangpinData.kucun) {
shangpinData.kucunText = shangpinData.kucun + '件'
shangpinData.kucunClass = shangpinData.kucun > 0 ? 'kucun-normal' : 'kucun-none'
}
// 设置页面标题
if (shangpinData.biaoti) {
wx.setNavigationBarTitle({
title: '商品详情'
})
}
that.setData({
shangpinData: shangpinData,
isLoading: false
})
} else {
that.handleLoadError(res.data?.msg || '加载失败')
}
},
fail(error) {
console.error('商品详情加载失败:', error)
that.handleLoadError('网络错误,请重试')
}
})
},
/**
* 格式化销量显示
*/
formatXiaoliang(xiaoliang) {
const num = parseInt(xiaoliang) || 0
if (num >= 10000) {
return (num / 10000).toFixed(1) + '万+'
} else if (num >= 1000) {
return (num / 1000).toFixed(1) + '千+'
} else {
return num + ''
}
},
/**
* 处理加载错误
*/
handleLoadError(errorMsg) {
this.setData({
isLoading: false,
loadError: true
})
wx.showToast({
title: errorMsg || '加载失败',
icon: 'none',
duration: 2000
})
// 3秒后自动返回
setTimeout(() => {
wx.navigateBack()
}, 3000)
},
/**
* 预览商品图片
*/
previewImage() {
const { shangpinData } = this.data
if (!shangpinData || !shangpinData.tupian) return
wx.previewImage({
current: shangpinData.tupian,
urls: [shangpinData.tupian]
})
},
/**
* 点击立即下单按钮
*/
onOrderClick() {
const { shangpinData, shangpinId } = this.data
// 检查库存
if (shangpinData && shangpinData.kucun <= 0) {
wx.showToast({
title: '商品已售罄',
icon: 'none'
})
return
}
// 验证必要数据
if (!shangpinData || !shangpinData.biaoti) {
wx.showToast({
title: '商品信息不完整',
icon: 'none'
})
return
}
// 准备跳转数据
const orderData = {
id: shangpinId, // 商品ID
title: shangpinData.biaoti, // 商品标题
price: shangpinData.jiage, // 商品价格(原价)
priceInteger: shangpinData.priceInteger, // 价格整数部分
priceDecimal: shangpinData.priceDecimal, // 价格小数部分
image: shangpinData.tupian, // 商品图片
kucun: shangpinData.kucun || 0, // 商品库存
xiaoliang: shangpinData.xiaoliang || 0 // 商品销量
}
// 跳转到提交订单页面
wx.navigateTo({
url: `/pages/tijiao/tijiao?data=${encodeURIComponent(JSON.stringify(orderData))}`
})
},
/**
* 刷新页面数据
*/
onRefresh() {
this.loadShangpinDetail()
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
const { shangpinData, shangpinId } = this.data
return {
title: shangpinData?.biaoti || '阿龙电竞 - 商品详情',
path: `/pages/shangpinxiangqing/shangpinxiangqing?id=${shangpinId}`,
imageUrl: shangpinData?.tupian || ''
}
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
// 页面显示时,可以检查是否需要刷新数据
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
// 清理工作
}
})

View File

@@ -0,0 +1,5 @@
{
"usingComponents": {
"global-notification": "/components/global-notification/global-notification"
}
}

View File

@@ -0,0 +1,112 @@
<!--pages/shangpinxiangqing/shangpinxiangqing.wxml-->
<view class="detail-page">
<!-- 加载状态 -->
<view class="loading-container" wx:if="{{isLoading}}">
<view class="loading-content">
<view class="loading-spinner">
<view class="spinner-ring"></view>
<view class="spinner-dot"></view>
</view>
<text class="loading-text">加载中...</text>
</view>
</view>
<!-- 错误状态 -->
<view class="error-container" wx:if="{{!isLoading && loadError}}">
<image src="/images/error.png" class="error-icon" />
<text class="error-text">加载失败</text>
<button class="error-btn" bindtap="onRefresh">重新加载</button>
</view>
<!-- 商品详情内容 -->
<view class="detail-content" wx:if="{{!isLoading && !loadError && shangpinData}}">
<!-- 商品图片区域(占满屏幕) -->
<view class="image-section">
<view class="image-container" bindtap="previewImage">
<image
src="{{shangpinData.tupian}}"
mode="aspectFill"
class="product-image"
/>
<!-- 库存标签 -->
<view class="kucun-tag {{shangpinData.kucunClass || 'kucun-normal'}}">
<text class="kucun-text">{{shangpinData.kucunText || '库存充足'}}</text>
</view>
</view>
</view>
<!-- 下方内容区域(顶部有圆角) -->
<view class="content-area">
<!-- 商品标题和价格区域 -->
<view class="title-price-section">
<view class="title-row">
<text class="product-title">{{shangpinData.biaoti || ''}}</text>
</view>
<view class="price-row">
<!-- 价格 -->
<view class="price-section">
<text class="price-icon">¥</text>
<text class="price-integer">{{shangpinData.priceInteger || '0'}}</text>
<text class="price-decimal">.{{shangpinData.priceDecimal || '00'}}</text>
</view>
<!-- 销量 -->
<view class="sales-section">
<image src="/images/xiaoliang.png" class="sales-icon" />
<text class="sales-text">{{shangpinData.xiaoliangText || '0'}}次购买</text>
</view>
</view>
</view>
<!-- 商品介绍区域 -->
<view class="info-section" wx:if="{{shangpinData.xiangqing}}">
<view class="section-header">
<image src="/images/jieshao.png" class="section-icon" />
<text class="section-title">商品介绍:</text>
</view>
<view class="section-content">
<text class="content-text">{{shangpinData.xiangqing}}</text>
</view>
</view>
<!-- 购买须知区域 -->
<view class="info-section" wx:if="{{shangpinData.xiadanxuzhi}}">
<view class="section-header">
<image src="/images/jieshao.png" class="section-icon" />
<text class="section-title">购买须知:</text>
</view>
<view class="section-content">
<text class="content-text">{{shangpinData.xiadanxuzhi}}</text>
</view>
</view>
<!-- 规则图片区域(只有有图片时才显示) -->
<view class="rule-section" wx:if="{{shangpinData.guize}}">
<image
src="{{shangpinData.guize}}"
mode="widthFix"
class="rule-image"
binderror="onImageError"
/>
</view>
<!-- 占位区域(给底部按钮留空间) -->
<view class="placeholder"></view>
</view>
</view>
<!-- 立即下单按钮(固定在底部,无价格标签) -->
<view class="order-btn-container" wx:if="{{!isLoading && !loadError && shangpinData}}">
<view class="order-btn" bindtap="onOrderClick">
<!-- 按钮发光特效 -->
<view class="btn-glow"></view>
<!-- 按钮流光特效 -->
<view class="btn-streamer"></view>
<!-- 按钮文字 -->
<text class="btn-text">立即预约</text>
</view>
</view>
<global-notification id="global-notification" />
</view>

View File

@@ -0,0 +1,386 @@
/* pages/shangpinxiangqing/shangpinxiangqing.wxss */
.detail-page {
width: 100%;
min-height: 100vh;
background: #f5f5f5;
position: relative;
}
/* 加载状态 */
.loading-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background: #f5f5f5;
z-index: 100;
}
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
}
.loading-spinner {
width: 80rpx;
height: 80rpx;
position: relative;
margin-bottom: 30rpx;
}
.spinner-ring {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 4rpx solid rgba(82, 196, 26, 0.2);
border-radius: 50%;
}
.spinner-dot {
position: absolute;
top: 0;
left: 50%;
width: 12rpx;
height: 12rpx;
background: #52c41a;
border-radius: 50%;
transform: translateX(-50%);
animation: spin 1s linear infinite;
}
.loading-text {
font-size: 28rpx;
color: #666;
font-weight: 500;
}
/* 错误状态 */
.error-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #f5f5f5;
z-index: 100;
}
.error-icon {
width: 180rpx;
height: 180rpx;
opacity: 0.3;
margin-bottom: 30rpx;
}
.error-text {
font-size: 30rpx;
color: #999;
margin-bottom: 40rpx;
}
.error-btn {
background: linear-gradient(135deg, #52c41a, #389e0d);
color: white;
font-size: 26rpx;
font-weight: 500;
padding: 20rpx 50rpx;
border-radius: 50rpx;
box-shadow: 0 8rpx 24rpx rgba(82, 196, 26, 0.3);
}
/* 商品详情内容 */
.detail-content {
padding-bottom: 140rpx; /* 给底部按钮留出空间 */
}
/* 商品图片区域(占满屏幕) */
.image-section {
width: 100%;
background: white;
}
.image-container {
width: 100%;
height: 750rpx; /* 正方形,占满屏幕宽度 */
position: relative;
overflow: hidden;
background: #f5f5f5;
}
.product-image {
width: 100%;
height: 100%;
}
/* 库存标签 */
.kucun-tag {
position: absolute;
bottom: 30rpx;
right: 30rpx;
padding: 12rpx 24rpx;
border-radius: 30rpx;
backdrop-filter: blur(10rpx);
z-index: 10;
}
.kucun-normal {
background: rgba(0, 0, 0, 0.6);
color: white;
}
.kucun-none {
background: rgba(255, 77, 79, 0.8);
color: white;
}
.kucun-text {
font-size: 24rpx;
font-weight: 500;
}
/* 下方内容区域(顶部有圆角,与图片重叠一部分) */
.content-area {
position: relative;
background: white;
border-radius: 40rpx 40rpx 0 0;
margin-top: -40rpx; /* 与图片重叠一部分,实现平滑过渡 */
padding: 40rpx 30rpx 0;
min-height: 400rpx;
}
/* 标题和价格区域 */
.title-price-section {
margin-bottom: 40rpx;
}
.title-row {
margin-bottom: 30rpx;
}
.product-title {
font-size: 38rpx;
font-weight: 700;
color: #222;
line-height: 1.5;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
/* 价格和销量行 */
.price-row {
display: flex;
justify-content: space-between;
align-items: center;
}
/* 价格部分 */
.price-section {
display: flex;
align-items: baseline;
}
.price-icon {
font-size: 34rpx;
color: #ff4d4f;
font-weight: 600;
}
.price-integer {
font-size: 58rpx;
color: #ff4d4f;
font-weight: 700;
margin-left: 4rpx;
}
.price-decimal {
font-size: 34rpx;
color: #ff4d4f;
font-weight: 600;
}
/* 销量部分 */
.sales-section {
display: flex;
align-items: center;
}
.sales-icon {
width: 30rpx;
height: 30rpx;
margin-right: 12rpx;
opacity: 0.6;
}
.sales-text {
font-size: 28rpx;
color: #999;
font-weight: 400;
}
/* 信息区域(商品介绍、购买须知) */
.info-section {
margin-bottom: 40rpx;
padding-top: 30rpx;
border-top: 1rpx solid #f0f0f0;
}
.section-header {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.section-icon {
width: 36rpx;
height: 36rpx;
margin-right: 16rpx;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #52c41a; /* 绿色 */
}
.section-content {
line-height: 1.8;
padding-left: 52rpx; /* 与图标对齐 */
}
.content-text {
font-size: 28rpx;
color: #333;
font-weight: 400;
white-space: pre-line;
line-height: 1.8;
}
/* 规则图片区域 */
.rule-section {
margin: 40rpx 0;
padding-top: 30rpx;
border-top: 1rpx solid #f0f0f0;
}
.rule-image {
width: 100%;
border-radius: 16rpx;
display: block;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
}
/* 占位区域 */
.placeholder {
height: 140rpx;
}
/* 立即下单按钮容器(固定底部) */
.order-btn-container {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 20rpx 30rpx;
background: linear-gradient(transparent, rgba(255, 255, 255, 0.9) 70%);
z-index: 100;
}
/* 立即下单按钮 */
.order-btn {
background: linear-gradient(135deg, #52c41a, #389e0d);
border-radius: 50rpx;
height: 100rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
box-shadow:
0 12rpx 36rpx rgba(82, 196, 26, 0.4),
0 4rpx 12rpx rgba(82, 196, 26, 0.3);
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.order-btn:active {
transform: scale(0.98);
box-shadow:
0 6rpx 24rpx rgba(82, 196, 26, 0.5),
0 2rpx 8rpx rgba(82, 196, 26, 0.4);
}
/* 按钮发光特效 */
.btn-glow {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.1);
animation: glowPulse 2s infinite;
}
/* 按钮流光特效 */
.btn-streamer {
position: absolute;
top: 0;
left: 0;
width: 100rpx;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
transform: translateX(-100%);
animation: streamerFlow 3s infinite;
}
/* 按钮文字 */
.btn-text {
font-size: 36rpx;
color: white;
font-weight: 600;
letter-spacing: 2rpx;
position: relative;
z-index: 2;
}
/* 动画定义 */
@keyframes spin {
0% {
transform: translateX(-50%) rotate(0deg);
}
100% {
transform: translateX(-50%) rotate(360deg);
}
}
@keyframes glowPulse {
0%, 100% {
opacity: 0.5;
}
50% {
opacity: 0.8;
}
}
@keyframes streamerFlow {
0% {
transform: translateX(-100%);
}
50% {
transform: translateX(100%);
}
100% {
transform: translateX(100%);
}
}