// pages/goods/index.js const API = require('../../utils/api.js') // 媒体类型判断函数 function isVideoUrl(url) { if (!url || typeof url !== 'string') { return false; } const lowerUrl = url.toLowerCase(); const videoExtensions = ['.mp4', '.mov', '.avi', '.wmv', '.flv', '.webm', '.m4v', '.3gp']; for (const ext of videoExtensions) { if (lowerUrl.endsWith(ext)) { return true; } } return false; } Page({ // 分享给朋友/群聊 onShareAppMessage() { return { title: '内部货源管理 - 鸡蛋贸易平台', path: '/pages/goods/index', imageUrl: '/images/你有好蛋.png' } }, // 分享到朋友圈 onShareTimeline() { return { title: '内部货源管理 - 鸡蛋贸易平台', query: '', imageUrl: '/images/你有好蛋.png' } }, /** * 页面的初始数据 */ data: { goodsList: [], isLoading: false, isRefreshing: false, // 下拉刷新状态 currentPage: 1, pageSize: 20, hasMore: true, searchKeyword: '', activeFilter: 'all', // 当前筛选条件:all, small, large filterConfig: { small: ['何佳芹', '李真音'], // 小品种创建者 large: ['吴海燕', '陈骏', '刘琴', '汤敏'] // 大贸易创建者 }, total: 0, // 总数据条数 searchTimer: null // 搜索防抖定时器 }, /** * 生命周期函数--监听页面加载 */ onLoad(options) { this.loadGoodsList() }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady() { }, /** * 生命周期函数--监听页面显示 */ onShow() { }, /** * 生命周期函数--监听页面隐藏 */ onHide() { }, /** * 生命周期函数--监听页面卸载 */ onUnload() { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh() { // 这里的onPullDownRefresh是页面级的,但我们使用的是scroll-view内置的下拉刷新,所以不需要实现这个方法 }, /** * scroll-view下拉刷新事件处理 */ onRefresherRefresh() { if (this.data.isLoading) return this.setData({ isRefreshing: true, currentPage: 1, goodsList: [], hasMore: true, isLoading: false }) this.loadGoodsList() }, /** * 页面上拉触底事件的处理函数 */ onReachBottom() { if (this.data.hasMore && !this.data.isLoading) { this.setData({ currentPage: this.data.currentPage + 1 }) this.loadGoodsList() } }, /** * 处理搜索输入 - 添加防抖功能 */ onSearchInput(e) { const keyword = e.detail.value; this.setData({ searchKeyword: keyword }); // 清除之前的定时器 if (this.data.searchTimer) { clearTimeout(this.data.searchTimer); } // 设置新的定时器,300ms后执行搜索 const searchTimer = setTimeout(() => { this.searchGoods(); }, 300); this.setData({ searchTimer: searchTimer }); }, /** * 执行搜索 */ searchGoods() { if (this.data.isLoading) return this.setData({ currentPage: 1, goodsList: [], hasMore: true }) this.loadGoodsList() }, /** * 清除搜索 */ clearSearch() { this.setData({ searchKeyword: '', currentPage: 1, goodsList: [], hasMore: true }) this.loadGoodsList() }, /** * 筛选条件改变 */ onFilterChange(e) { if (this.data.isLoading) return const filter = e.currentTarget.dataset.filter this.setData({ activeFilter: filter, currentPage: 1, goodsList: [], hasMore: true }) this.loadGoodsList() }, /** * 根据搜索关键词过滤数据 - 完善搜索功能,支持搜索创建人信息 */ searchGoodsList(goodsList, keyword) { if (!keyword || keyword.trim() === '') { return goodsList } const searchTerm = keyword.toLowerCase().trim() return goodsList.filter(item => { // 检查多个字段是否包含搜索关键词 const fieldsToCheck = [ // 产品基本信息 item.productName || item.name || '', // 产品名称 item.specification || item.spec || '', // 规格 item.description || item.remark || '', // 描述 item.region || item.area || '', // 地区 item.yolk || item.variety || '', // 蛋黄 item.price || '', // 价格 item.costprice || '', // 采购价格 item.grossWeight || item.weight || '', // 重量 item.category || '', // 种类 item.minOrder || item.quantity || '', // 最小起订量 // 创建时间 item.formattedCreatedAt || item.created_at || item.createdAt || '' // 创建时间 ] // 检查是否有任何字段包含搜索关键词 let hasMatch = fieldsToCheck.some(field => { return field.toLowerCase().includes(searchTerm) }) // 单独处理创建人信息,确保即使没有创建者信息也能正常搜索 if (!hasMatch) { // 检查创建人相关字段 const creatorFields = [ item.creatorName || '', // 已处理的创建人名称 // 检查seller对象中的创建人信息,确保seller是对象 typeof item.seller === 'object' && item.seller ? item.seller.nickName || '' : '', typeof item.seller === 'object' && item.seller ? item.seller.sellerNickName || '' : '', typeof item.seller === 'object' && item.seller ? item.seller.name || '' : '', item.sellerName || '' // 卖家名称备用字段 ] hasMatch = creatorFields.some(field => { return field.toLowerCase().includes(searchTerm) }) } return hasMatch }) }, /** * 根据筛选条件过滤数据 */ filterGoodsList(goodsList) { const { activeFilter, filterConfig } = this.data if (activeFilter === 'all') { return goodsList } const allowedCreators = filterConfig[activeFilter] || [] return goodsList.filter(item => { return allowedCreators.includes(item.creatorName) }) }, /** * 格式化时间 */ formatDateTime(dateString) { if (!dateString) return '未知时间' // 检查是否已经是格式化好的北京时间字符串(如:2026-01-05 17:30) if (typeof dateString === 'string' && /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2})?$/.test(dateString)) { // 直接返回格式化好的字符串,只保留到分钟 return dateString.slice(0, 16) } // 检查是否是ISO格式的字符串(如:2026-01-05T12:00:00.000Z) if (typeof dateString === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/.test(dateString)) { // 转换ISO格式为本地时间 const date = new Date(dateString) if (isNaN(date.getTime())) { return '未知时间' } const year = date.getFullYear() const month = (date.getMonth() + 1).toString().padStart(2, '0') const day = date.getDate().toString().padStart(2, '0') const hours = date.getHours().toString().padStart(2, '0') const minutes = date.getMinutes().toString().padStart(2, '0') return `${year}-${month}-${day} ${hours}:${minutes}` } // 对于其他格式的字符串,直接返回,不进行转换 // 这是为了避免对后端返回的北京时间字符串进行错误的时区转换 return dateString }, /** * 点击货源项跳转 */ onGoodsItemClick(e) { const goodsItem = e.currentTarget.dataset.item; console.log('点击了货源项:', goodsItem); // 跳转到货源详情页面(goods-update) wx.navigateTo({ url: `/pages/goods-update/goods-update?productId=${goodsItem.id || goodsItem.productId}&goodsData=${encodeURIComponent(JSON.stringify(goodsItem))}` }); }, /** * 加载货源列表 - 优化搜索功能,支持搜索创建人信息 */ async loadGoodsList() { if (this.data.isLoading) return this.setData({ isLoading: true }) console.log('开始加载货源列表,参数:', { page: this.data.currentPage, pageSize: this.data.pageSize, keyword: this.data.searchKeyword, activeFilter: this.data.activeFilter }) // 调用API获取货源列表 try { const res = await API.getGoodsList({ page: this.data.currentPage, pageSize: this.data.pageSize, keyword: this.data.searchKeyword }) console.log('API返回结果:', res) // 检查API返回的状态 if (res.success) { console.log('API调用成功,products:', res.products) console.log('products长度:', res.products.length) console.log('API返回total:', res.total) // 更新总数据条数 const total = res.total || 0 if (res.products && res.products.length > 0) { // 显示第一个产品的详细信息 console.log('第一个产品:', res.products[0]) console.log('第一个产品的seller:', res.products[0].seller) console.log('第一个产品的seller.nickName:', res.products[0].seller?.nickName) console.log('第一个产品的seller.name:', res.products[0].seller?.name) console.log('第一个产品的seller完整结构:', JSON.stringify(res.products[0].seller)) let newGoodsList = res.products || [] // 格式化创建时间并处理创建者信息 newGoodsList = newGoodsList.map((item, index) => { // 详细日志,查看每个产品的seller信息 console.log(`产品${index}的seller信息:`) console.log(`- seller对象:`, item.seller) console.log(`- seller.nickName:`, item.seller?.nickName) console.log(`- seller.sellerNickName:`, item.seller?.sellerNickName) console.log(`- seller.name:`, item.seller?.name) console.log(`- seller完整结构:`, JSON.stringify(item.seller)) // 确定creatorName - 支持多种字段,确保能获取到创建人信息 // 从seller对象中获取创建人信息,优先使用nickName,其次使用sellerNickName,最后使用name const sellerNickName = item.seller?.nickName || item.seller?.sellerNickName || item.seller?.name || '未知'; const creatorName = sellerNickName; console.log('creatorName获取结果:', creatorName) // 处理媒体URL,添加类型信息 const imageUrls = item.imageUrls || item.images || [] const formattedImageUrls = Array.isArray(imageUrls) ? imageUrls : [imageUrls] const mediaItems = formattedImageUrls.map(url => ({ url: url, type: isVideoUrl(url) ? 'video' : 'image' })) // 处理商品状态,将已下架的商品标记为售空 let status = item.status // 检查是否为售空状态 const isSoldOut = status === 'sold_out' || status === 'sold' || status === 'out_of_stock' || (item.supplyStatus && item.supplyStatus.includes('售空')); // 如果商品是售空状态,则标记为sold_out if (isSoldOut) { status = 'sold_out' } else if (status !== 'published') { // 如果不是已上架状态且不是售空状态,仍然标记为已上架,确保预售和现货商品能正常显示 status = 'published' } // 处理价格,只显示第一个规格的价格 let processedPrice = item.price; if (processedPrice && typeof processedPrice === 'string') { // 支持多种逗号分隔符:英文逗号、中文逗号、全角逗号 const priceArray = processedPrice.split(/[,,、]/).map(p => p.trim()).filter(p => p); if (priceArray.length > 0) { processedPrice = priceArray[0]; } } // 确保productName字段存在,优先使用productName,其次使用name const productName = item.productName || item.name || '未命名商品'; return { ...item, productName: productName, // 确保productName字段存在 name: productName, // 确保name字段存在 status: status, // 更新商品状态 price: processedPrice, // 更新为第一个规格的价格 formattedCreatedAt: this.formatDateTime(item.created_at || item.createTime), creatorName: creatorName, // 已处理的创建人名称 imageUrls: formattedImageUrls, mediaItems: mediaItems } }) // 应用筛选条件和搜索过滤 const originalList = [...newGoodsList] // 先应用筛选条件 let filteredList = this.filterGoodsList(newGoodsList) // 再应用搜索过滤 - 在处理后的列表上进行搜索,确保能搜索到creatorName let searchFilteredList = this.searchGoodsList(filteredList, this.data.searchKeyword) // 排序:已上架商品排在前面,售空商品排在后面 - 对搜索过滤后的列表进行排序 searchFilteredList.sort((a, b) => { if (a.status === 'published' && b.status === 'sold_out') { return -1 // 已上架排在前面 } else if (a.status === 'sold_out' && b.status === 'published') { return 1 // 售空排在后面 } else { // 相同状态下,按创建时间倒序排序 const timeA = new Date(a.created_at || a.createTime).getTime() const timeB = new Date(b.created_at || b.createTime).getTime() return timeB - timeA } }) console.log('处理并筛选后的产品列表:', searchFilteredList) console.log('筛选前后数量对比:', originalList.length, '->', searchFilteredList.length) // 处理分页逻辑 - 使用搜索过滤后的列表 let updatedGoodsList = this.data.currentPage === 1 ? searchFilteredList : [...this.data.goodsList, ...searchFilteredList] // 对整个列表进行排序,确保已上架商品始终在前面,售空商品在后面 updatedGoodsList.sort((a, b) => { if (a.status === 'published' && b.status === 'sold_out') { return -1 // 已上架排在前面 } else if (a.status === 'sold_out' && b.status === 'published') { return 1 // 售空排在后面 } else { // 相同状态下,按创建时间倒序排序 const timeA = new Date(a.created_at || a.createTime).getTime() const timeB = new Date(b.created_at || b.createTime).getTime() return timeB - timeA } }) // 判断是否还有更多数据 // 正确逻辑:如果API返回的原始数据数量小于pageSize,说明没有更多数据 // 即使筛选后的数据量小于pageSize,只要API返回的原始数据数量等于pageSize,就应该继续尝试加载更多数据 const hasMore = res.products.length >= this.data.pageSize console.log('分页判断:', { updatedGoodsListLength: updatedGoodsList.length, originalDataLength: res.products.length, pageSize: this.data.pageSize, hasMore: hasMore }) this.setData({ goodsList: updatedGoodsList, hasMore: hasMore, total: total }) // 移除递归调用逻辑,避免无限递归风险 // 用户可以通过正常的上拉加载获取更多数据 } else { console.log('没有产品数据返回') this.setData({ goodsList: [], hasMore: false, total: 0 }) } } else { console.error('API调用失败:', res.message) wx.showToast({ title: res.message || '获取货源列表失败', icon: 'none' }) } } catch (err) { console.error('获取货源列表失败:', err) wx.showToast({ title: '获取货源列表失败: ' + err.message, icon: 'none' }) } finally { // 结束下拉刷新和加载状态 this.setData({ isLoading: false, isRefreshing: false }) } } })