You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

514 lines
16 KiB

// 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
})
}
}
})