统一排行榜对齐星雀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,452 @@
// pages/fenlei/fenlei.js
const app = getApp()
Page({
/**
* 页面的初始数据
*/
data: {
// 全局变量引用
ossImageUrl: '',
// 搜索相关
searchText: '',
showSearchResult: false,
searchResults: [],
isSearching: false,
// 页面数据
shangpinleixing: [],
shangpinzhuanqu: [],
shangpinliebiao: [],
// 状态控制
selectedLeixingId: null,
selectedZhuanquId: null,
filteredZhuanquList: [], // 当前类型下的专区列表
filteredShangpinList: [], // 当前专区下的商品列表
zhuanquByLeixing: {}, // 类型->专区映射对象
shangpinByZhuanqu: {}, // 专区->商品映射对象
zhuanquGoodsCount: {}, // 专区商品数量统计
// UI状态
isLoading: false,
hasInitialized: false
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 获取全局变量
this.setData({
ossImageUrl: app.globalData.ossImageUrl
})
// 初始化数据
this.initPageData()
},
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()
};
}
},
/**
* 初始化页面数据
*/
initPageData() {
const that = this
this.setData({ isLoading: true })
// 检查全局变量是否已有数据
if (app.globalData.shangpinleixing && app.globalData.shangpinleixing.length > 0) {
this.initFromGlobalData()
} else {
this.loadAllShangpinData()
}
},
/**
* 从全局变量初始化数据
*/
initFromGlobalData() {
const leixingList = app.globalData.shangpinleixing || []
const zhuanquList = app.globalData.shangpinzhuanqu || []
const shangpinList = app.globalData.shangpinliebiao || []
// 构建映射关系对象
const zhuanquByLeixing = {}
const shangpinByZhuanqu = {}
const zhuanquGoodsCount = {}
// 1. 构建专区映射 (按类型分组)
zhuanquList.forEach(zhuanqu => {
const leixingId = zhuanqu.leixing_id
if (!zhuanquByLeixing[leixingId]) {
zhuanquByLeixing[leixingId] = []
}
zhuanquByLeixing[leixingId].push(zhuanqu)
})
// 2. 构建商品映射 (按专区分组)并统计数量
shangpinList.forEach(shangpin => {
const zhuanquId = shangpin.zhuanqu_id
if (zhuanquId) {
if (!shangpinByZhuanqu[zhuanquId]) {
shangpinByZhuanqu[zhuanquId] = []
zhuanquGoodsCount[zhuanquId] = 0
}
// 处理价格显示
const jiage = parseFloat(shangpin.jiage) || 0
const priceInteger = Math.floor(jiage)
let priceDecimal = '00'
const decimalPart = (jiage - priceInteger).toFixed(2)
if (decimalPart > 0) {
priceDecimal = decimalPart.toString().split('.')[1]
}
shangpinByZhuanqu[zhuanquId].push({
id: shangpin.id,
biaoqian: shangpin.biaoqian,
jiage: jiage,
priceInteger: priceInteger,
priceDecimal: priceDecimal,
tupian_url: shangpin.tupian_url,
duiwai_xiaoliang: shangpin.duiwai_xiaoliang || 0
})
zhuanquGoodsCount[zhuanquId] += 1
}
})
// 设置初始选中状态(第一个类型和第一个专区)
const firstLeixing = leixingList[0]
let firstZhuanquId = null
if (firstLeixing && zhuanquByLeixing[firstLeixing.id]) {
firstZhuanquId = zhuanquByLeixing[firstLeixing.id][0]?.id || null
}
this.setData({
shangpinleixing: leixingList,
shangpinzhuanqu: zhuanquList,
shangpinliebiao: shangpinList,
zhuanquByLeixing: zhuanquByLeixing,
shangpinByZhuanqu: shangpinByZhuanqu,
zhuanquGoodsCount: zhuanquGoodsCount,
selectedLeixingId: firstLeixing?.id || null,
selectedZhuanquId: firstZhuanquId
}, () => {
// 筛选数据
this.filterData()
this.setData({ isLoading: false, hasInitialized: true })
})
},
/**
* 加载所有商品数据
*/
loadAllShangpinData() {
const that = this
wx.request({
url: app.globalData.apiBaseUrl + '/shangpin/shangpinhuoqu/',
method: 'POST',
header: {
'content-type': 'application/json'
},
success(res) {
if (res.statusCode === 200 && res.data) {
const data = res.data
// 存储到全局变量
app.globalData.shangpinleixing = data.shangpinleixing || []
app.globalData.shangpinzhuanqu = data.shangpinzhuanqu || []
app.globalData.shangpinliebiao = data.shangpinliebiao || []
// 从全局变量初始化
that.initFromGlobalData()
} else {
that.handleLoadError()
}
},
fail(error) {
console.error('加载商品数据失败:', error)
that.handleLoadError()
}
})
},
/**
* 处理加载错误
*/
handleLoadError() {
wx.showToast({
title: '加载失败,请重试',
icon: 'none'
})
this.setData({ isLoading: false })
},
/**
* 筛选数据
*/
filterData() {
const { selectedLeixingId, selectedZhuanquId, zhuanquByLeixing, shangpinByZhuanqu } = this.data
// 1. 筛选专区列表
const filteredZhuanquList = zhuanquByLeixing[selectedLeixingId] || []
// 2. 筛选商品列表
let filteredShangpinList = []
if (selectedZhuanquId) {
filteredShangpinList = shangpinByZhuanqu[selectedZhuanquId] || []
} else if (filteredZhuanquList.length > 0) {
// 如果没有选中的专区,默认显示第一个专区的商品
const firstZhuanquId = filteredZhuanquList[0].id
filteredShangpinList = shangpinByZhuanqu[firstZhuanquId] || []
this.setData({ selectedZhuanquId: firstZhuanquId })
}
this.setData({
filteredZhuanquList: filteredZhuanquList,
filteredShangpinList: filteredShangpinList
})
},
/**
* 选择商品类型
*/
selectLeixing(e) {
const leixingId = e.currentTarget.dataset.id
if (this.data.selectedLeixingId === leixingId) {
return
}
// 获取该类型下的第一个专区
const zhuanquList = this.data.zhuanquByLeixing[leixingId] || []
const firstZhuanquId = zhuanquList.length > 0 ? zhuanquList[0].id : null
this.setData({
selectedLeixingId: leixingId,
selectedZhuanquId: firstZhuanquId
}, () => {
this.filterData()
})
},
/**
* 选择商品专区
*/
selectZhuanqu(e) {
const zhuanquId = e.currentTarget.dataset.id
if (this.data.selectedZhuanquId === zhuanquId) {
return
}
this.setData({
selectedZhuanquId: zhuanquId
}, () => {
// 筛选商品列表
const filteredShangpinList = this.data.shangpinByZhuanqu[zhuanquId] || []
this.setData({
filteredShangpinList: filteredShangpinList
})
})
},
/**
* 搜索框输入事件
*/
onSearchInput(e) {
this.setData({
searchText: e.detail.value
})
},
/**
* 点击搜索按钮
*/
onSearchTap() {
const searchText = this.data.searchText.trim()
if (!searchText) {
wx.showToast({
title: '请输入搜索内容',
icon: 'none'
})
return
}
// 开始搜索
this.startSearch(searchText)
},
/**
* 键盘搜索事件(回车键)
*/
onSearchConfirm(e) {
const searchText = e.detail.value.trim()
if (!searchText) {
wx.showToast({
title: '请输入搜索内容',
icon: 'none'
})
return
}
this.setData({
searchText: searchText
})
// 开始搜索
this.startSearch(searchText)
},
/**
* 执行搜索
*/
startSearch(searchText) {
this.setData({
isSearching: true,
showSearchResult: true
})
// 本地搜索
const allShangpin = this.data.shangpinliebiao
const results = []
for (const item of allShangpin) {
if (item.biaoqian && item.biaoqian.toLowerCase().includes(searchText.toLowerCase())) {
// 处理价格显示
const jiage = parseFloat(item.jiage) || 0
const priceInteger = Math.floor(jiage)
let priceDecimal = '00'
const decimalPart = (jiage - priceInteger).toFixed(2)
if (decimalPart > 0) {
priceDecimal = decimalPart.toString().split('.')[1]
}
results.push({
id: item.id,
biaoqian: item.biaoqian,
jiage: jiage,
priceInteger: priceInteger,
priceDecimal: priceDecimal,
tupian_url: item.tupian_url,
leixing_name: this.getLeixingName(item.leixing_id),
zhuanqu_name: this.getZhuanquName(item.zhuanqu_id)
})
}
}
this.setData({
searchResults: results,
isSearching: false
})
},
/**
* 清空搜索
*/
onClearSearch() {
this.setData({
searchText: '',
showSearchResult: false,
searchResults: []
})
},
/**
* 根据类型ID获取类型名称
*/
getLeixingName(leixingId) {
const leixingList = this.data.shangpinleixing
for (const item of leixingList) {
if (item.id === leixingId) {
return item.jieshao
}
}
return ''
},
/**
* 根据专区ID获取专区名称
*/
getZhuanquName(zhuanquId) {
const zhuanquList = this.data.shangpinzhuanqu
for (const item of zhuanquList) {
if (item.id === zhuanquId) {
return item.mingzi
}
}
return ''
},
/**
* 跳转到商品详情页
*/
goToDetail(e) {
const shangpinId = e.currentTarget.dataset.id
if (!shangpinId) return
wx.navigateTo({
url: `/pages/shangpinxiangqing/shangpinxiangqing?id=${shangpinId}`
})
},
/**
* 刷新所有数据
*/
onRefresh() {
wx.showLoading({
title: '刷新中...',
mask: true
})
// 清空全局变量
app.globalData.shangpinleixing = []
app.globalData.shangpinzhuanqu = []
app.globalData.shangpinliebiao = []
this.loadAllShangpinData()
setTimeout(() => {
wx.hideLoading()
wx.showToast({
title: '刷新成功',
icon: 'success',
duration: 1500
})
}, 1000)
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
return {
title: '阿龙电竞 - 商品分类',
path: '/pages/fenlei/fenlei'
}
}
})

View File

@@ -0,0 +1,6 @@
{
"usingComponents": {
"global-notification": "/components/global-notification/global-notification",
"custom-tab-bar": "/custom-tab-bar/index"
}
}

View File

@@ -0,0 +1,217 @@
<!-- pages/fenlei/fenlei.wxml -->
<view class="fenlei-page">
<!-- 1. 毛玻璃搜索框区域(固定) -->
<view class="search-container">
<view class="search-box">
<image src="/images/sousuo.png" class="search-icon" />
<input
type="text"
class="search-input"
placeholder="输入商品名称搜索"
placeholder-class="search-placeholder"
value="{{searchText}}"
bindinput="onSearchInput"
bindconfirm="onSearchConfirm"
confirm-type="search"
/>
<!-- 清空按钮 -->
<view wx:if="{{searchText}}" class="search-clear" bindtap="onClearSearch">
<image src="/images/clear.png" class="clear-icon" />
</view>
<!-- 搜索按钮 -->
<view class="search-btn" bindtap="onSearchTap">
<text class="search-btn-text">搜索</text>
</view>
</view>
</view>
<!-- 2. 商品类型选择区域 -->
<view class="leixing-container" wx:if="{{!showSearchResult}}">
<scroll-view
class="leixing-scroll"
scroll-x
scroll-with-animation
show-scrollbar="{{false}}"
>
<view class="leixing-list">
<block wx:for="{{shangpinleixing}}" wx:key="id" wx:for-index="idx">
<view
class="leixing-item {{selectedLeixingId === item.id ? 'leixing-active' : ''}}"
bindtap="selectLeixing"
data-id="{{item.id}}"
data-index="{{idx}}"
>
<view class="leixing-image-box">
<image
src="{{ossImageUrl}}{{item.tupian_url}}"
mode="aspectFill"
class="leixing-image"
/>
<view class="leixing-glow {{selectedLeixingId === item.id ? 'leixing-glow-active' : ''}}"></view>
</view>
<text class="leixing-text">{{item.jieshao}}</text>
<view class="leixing-active-bar {{selectedLeixingId === item.id ? 'leixing-active-bar-show' : ''}}"></view>
</view>
</block>
</view>
</scroll-view>
</view>
<!-- 3. 左右分栏布局 -->
<view class="main-container" wx:if="{{!showSearchResult}}">
<!-- 左边:商品专区列表 -->
<scroll-view
class="zhuanqu-left"
scroll-y
enhanced="{{true}}"
show-scrollbar="{{false}}"
scroll-into-view="{{'zhuanqu-' + selectedZhuanquId}}"
>
<view class="zhuanqu-list">
<block wx:for="{{filteredZhuanquList}}" wx:key="id">
<view
id="zhuanqu-{{item.id}}"
class="zhuanqu-item {{selectedZhuanquId === item.id ? 'zhuanqu-active' : ''}}"
bindtap="selectZhuanqu"
data-id="{{item.id}}"
>
<view class="zhuanqu-item-content">
<text class="zhuanqu-name">{{item.mingzi}}</text>
<text class="zhuanqu-count">({{zhuanquGoodsCount[item.id] || 0}})</text>
</view>
<view class="zhuanqu-active-indicator {{selectedZhuanquId === item.id ? 'zhuanqu-indicator-glow' : ''}}"></view>
</view>
</block>
<view wx:if="{{filteredZhuanquList.length === 0}}" class="zhuanqu-empty">
<image src="/images/empty-box.png" class="zhuanqu-empty-icon" />
<text class="zhuanqu-empty-text">暂无专区</text>
</view>
</view>
</scroll-view>
<!-- 右边:商品列表 -->
<scroll-view
class="shangpin-right"
scroll-y
enhanced="{{true}}"
show-scrollbar="{{false}}"
>
<view class="shangpin-list">
<block wx:for="{{filteredShangpinList}}" wx:key="id">
<view
class="shangpin-item"
bindtap="goToDetail"
data-id="{{item.id}}"
>
<view class="shangpin-image-box">
<image
src="{{ossImageUrl}}{{item.tupian_url}}"
mode="aspectFill"
class="shangpin-image"
/>
</view>
<view class="shangpin-info">
<view class="shangpin-title">
<text class="shangpin-title-text">{{item.biaoqian}}</text>
</view>
<view class="shangpin-footer">
<view class="shangpin-price">
<text class="price-icon">¥</text>
<text class="price-integer">{{item.priceInteger}}</text>
<text class="price-decimal">.{{item.priceDecimal}}</text>
</view>
<view class="detail-btn">
<text class="detail-btn-text">了解详情</text>
</view>
</view>
</view>
<view class="item-divider" wx:if="{{index !== filteredShangpinList.length - 1}}"></view>
</view>
</block>
<view wx:if="{{filteredShangpinList.length === 0 && !isLoading}}" class="shangpin-empty">
<image src="/images/empty-goods.png" class="shangpin-empty-icon" />
<text class="shangpin-empty-text">该专区暂无商品</text>
</view>
</view>
<view class="load-more" wx:if="{{filteredShangpinList.length > 0}}">
<text class="load-more-text">— 已加载全部商品 —</text>
</view>
</scroll-view>
</view>
<!-- 搜索结果弹窗 -->
<view class="search-results-container" wx:if="{{showSearchResult}}">
<view class="search-mask" bindtap="onClearSearch"></view>
<view class="search-results-content">
<view class="search-results-header">
<view class="search-header-left">
<text class="search-results-title">搜索结果</text>
<text class="search-results-count">({{searchResults.length}}个)</text>
</view>
<view class="search-close" bindtap="onClearSearch">
<image src="/images/close.png" class="close-icon" />
</view>
</view>
<scroll-view class="search-results-list" scroll-y>
<block wx:for="{{searchResults}}" wx:key="id">
<view class="search-result-item" bindtap="goToDetail" data-id="{{item.id}}">
<view class="search-result-left">
<image
src="{{ossImageUrl}}{{item.tupian_url}}"
mode="aspectFill"
class="search-result-image"
/>
</view>
<view class="search-result-right">
<view class="search-result-title">{{item.biaoqian}}</view>
<view class="search-result-info">
<text class="search-result-category">{{item.leixing_name}}</text>
<text class="search-result-separator">·</text>
<text class="search-result-zhuanqu">{{item.zhuanqu_name}}</text>
</view>
<view class="search-result-price">
<text class="price-icon">¥</text>
<text class="price-integer">{{item.priceInteger}}</text>
<text class="price-decimal">.{{item.priceDecimal}}</text>
</view>
</view>
</view>
</block>
<view wx:if="{{searchResults.length === 0 && !isSearching}}" class="search-empty">
<image src="/images/search-empty.png" class="search-empty-icon" />
<text class="search-empty-text">未找到相关商品</text>
</view>
<view wx:if="{{isSearching}}" class="search-loading">
<view class="search-spinner">
<view class="spinner-circle"></view>
</view>
<text class="search-loading-text">搜索中...</text>
</view>
</scroll-view>
</view>
</view>
<!-- 全局加载状态 -->
<view class="loading-toast" 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>
<!-- 底部自定义TabBar -->
<custom-tab-bar />
<global-notification id="global-notification" />

File diff suppressed because it is too large Load Diff