纵的
yzb
yzb 6 months ago
parent 08ee6ccec3
commit a3e3b33da7

@ -728,13 +728,340 @@ const analyzeProductPrice = async (event) => {
}
};
// 初始化T_category表如果不存在则创建
const initCategoryTable = async () => {
try {
// 默认类别数据
const defaultCategories = [
{ categoryName: '电子产品', description: '手机、电脑、配件', icon: '📱', iconBg: '#2196F3', sort: 1, enabled: true },
{ categoryName: '图书文具', description: '教材、小说、专业书籍、文具', icon: '📚', iconBg: '#4CAF50', sort: 2, enabled: true },
{ categoryName: '服装鞋帽', description: '衣服、鞋子、配饰', icon: '👕', iconBg: '#FF9800', sort: 3, enabled: true },
{ categoryName: '家居用品', description: '桌椅、小家电、生活用品', icon: '🛋️', iconBg: '#795548', sort: 4, enabled: true },
{ categoryName: '运动户外', description: '健身器材、球类运动、户外用品', icon: '⚽', iconBg: '#9C27B0', sort: 5, enabled: true },
{ categoryName: '美妆个护', description: '美妆护肤产品', icon: '💄', iconBg: '#FF6B9D', sort: 6, enabled: true },
{ categoryName: '其他', description: '其他各类商品', icon: '📦', iconBg: '#757575', sort: 7, enabled: true }
];
// 检查T_category表是否存在数据
const existing = await db.collection("T_category").get();
if (existing.data.length === 0) {
// 初始化类别数据使用循环add避免batch兼容性问题
for (const category of defaultCategories) {
await db.collection("T_category").add({
data: {
...category,
createTime: new Date(),
updateTime: new Date()
}
});
}
console.log('T_category表初始化成功');
}
return {
success: true,
message: 'T_category表已初始化'
};
} catch (e) {
console.error('初始化T_category表失败:', e);
return {
success: false,
error: e.message
};
}
};
// 获取所有商品类别优先从T_category表获取如果没有则从T_product表提取
const getProductCategories = async () => {
try {
// 先尝试从T_category表获取
let categoriesResult = await db.collection("T_category")
.where({
enabled: true
})
.orderBy('sort', 'asc')
.get();
if (categoriesResult.data.length > 0) {
// 从T_category表获取类别
const categories = categoriesResult.data.map(item => ({
id: item.categoryName,
name: item.categoryName,
description: item.description || getCategoryDescription(item.categoryName),
icon: item.icon || getCategoryIcon(item.categoryName),
iconBg: item.iconBg || getCategoryBg(item.categoryName)
}));
return {
success: true,
data: categories,
source: 'T_category'
};
}
// 如果T_category表为空从T_product表提取兼容旧数据
console.log('T_category表为空从T_product表提取类别');
const result = await db.collection("T_product").field({
productCategory: true
}).get();
// 提取不重复的类别
const categoriesSet = new Set();
result.data.forEach(item => {
if (item.productCategory) {
categoriesSet.add(item.productCategory);
}
});
// 转换为数组并返回
const categories = Array.from(categoriesSet).map(category => ({
id: category,
name: category,
description: getCategoryDescription(category),
icon: getCategoryIcon(category),
iconBg: getCategoryBg(category)
}));
// 如果数据库中没有类别,返回默认类别
if (categories.length === 0) {
return {
success: true,
data: [
{ id: '电子产品', name: '电子产品', description: '手机、电脑、配件', icon: '📱', iconBg: '#2196F3' },
{ id: '图书文具', name: '图书文具', description: '教材、小说、专业书籍、文具', icon: '📚', iconBg: '#4CAF50' },
{ id: '服装鞋帽', name: '服装鞋帽', description: '衣服、鞋子、配饰', icon: '👕', iconBg: '#FF9800' },
{ id: '家居用品', name: '家居用品', description: '桌椅、小家电、生活用品', icon: '🛋️', iconBg: '#795548' },
{ id: '运动户外', name: '运动户外', description: '健身器材、球类运动、户外用品', icon: '⚽', iconBg: '#9C27B0' },
{ id: '美妆个护', name: '美妆个护', description: '美妆护肤产品', icon: '💄', iconBg: '#FF6B9D' },
{ id: '其他', name: '其他', description: '其他各类商品', icon: '📦', iconBg: '#757575' }
],
source: 'default'
};
}
return {
success: true,
data: categories,
source: 'T_product'
};
} catch (e) {
console.error('获取商品类别失败:', e);
return {
success: false,
error: e.message
};
}
};
// 获取类别描述
const getCategoryDescription = (category) => {
const descriptions = {
'电子产品': '手机、电脑、配件',
'图书文具': '教材、小说、专业书籍、文具',
'服装鞋帽': '衣服、鞋子、配饰',
'家居用品': '桌椅、小家电、生活用品',
'运动户外': '健身器材、球类运动、户外用品',
'美妆个护': '美妆护肤产品',
'其他': '其他各类商品'
};
return descriptions[category] || '各类商品';
};
// 获取类别图标使用emoji
const getCategoryIcon = (category) => {
const icons = {
'电子产品': '📱',
'图书文具': '📚',
'服装鞋帽': '👕',
'家居用品': '🛋️',
'运动户外': '⚽',
'美妆个护': '💄',
'其他': '📦'
};
return icons[category] || '📦';
};
// 获取类别背景色
const getCategoryBg = (category) => {
const colors = {
'电子产品': '#2196F3',
'图书文具': '#4CAF50',
'服装鞋帽': '#FF9800',
'家居用品': '#795548',
'运动户外': '#9C27B0',
'美妆个护': '#FF6B9D',
'其他': '#757575'
};
return colors[category] || '#757575';
};
// 更新用户兴趣
const updateUserInterests = async (event) => {
try {
const wxContext = cloud.getWXContext();
const openid = wxContext.OPENID;
const interests = event.interests || [];
console.log('更新用户兴趣openid:', openid, 'interests:', interests);
if (!openid) {
return {
success: false,
error: 'openid不能为空'
};
}
// 查询用户是否存在
const userResult = await db.collection("T_user").where({
_openid: openid
}).get();
if (userResult.data.length === 0) {
return {
success: false,
error: '用户不存在'
};
}
// 更新用户兴趣
const updateResult = await db.collection("T_user").where({
_openid: openid
}).update({
data: {
interests: interests,
updateTime: new Date()
}
});
console.log('更新用户兴趣成功:', updateResult);
return {
success: true,
data: {
interests: interests,
updateResult: updateResult
}
};
} catch (e) {
console.error('更新用户兴趣失败:', e);
return {
success: false,
error: e.message
};
}
};
// 类别名称映射(兼容旧数据)
const mapCategoryName = (category) => {
const categoryMap = {
'化妆品': '美妆个护',
'二手书': '图书文具',
'运动器材': '运动户外',
'家具家电': '家居用品',
'文具用品': '图书文具',
'其他商品': '其他'
};
return categoryMap[category] || category;
};
// 根据用户兴趣推荐商品
const getRecommendedProducts = async (event) => {
try {
const wxContext = cloud.getWXContext();
const openid = wxContext.OPENID;
const userInterests = event.interests || [];
const limit = event.limit || 8;
console.log('获取推荐商品openid:', openid, '原始interests:', userInterests);
// 映射类别名称(兼容旧数据)
const mappedInterests = userInterests.map(cat => mapCategoryName(cat));
console.log('映射后的interests:', mappedInterests);
// 构建查询条件 - 微信云开发中所有条件必须在一个where中
let queryCondition = {
status: '在售'
};
// 如果有用户兴趣,添加类别筛选条件
if (mappedInterests.length > 0) {
queryCondition.productCategory = db.command.in(mappedInterests);
}
console.log('查询条件:', JSON.stringify(queryCondition, null, 2));
// 查询商品
const result = await db.collection("T_product")
.where(queryCondition)
.orderBy('createTime', 'desc')
.limit(limit)
.get();
console.log('首次查询结果数量:', result.data.length);
console.log('查询到的商品类别:', result.data.map(item => item.productCategory));
// 如果筛选结果太少,优先尝试补充用户感兴趣的其他类别商品
// 例如:如果用户选择了"服装鞋帽"和"化妆品",但只有"服装鞋帽"的商品
// 则不再补充其他类别的商品,只返回找到的商品
// 这样能确保推荐的商品都是用户感兴趣的类别
// 如果确实没有商品,返回空数组(前端会显示"暂无推荐商品"
// 不补充不相关的商品,保证推荐质量
// 格式化商品数据
const products = result.data.map(item => ({
id: item._id,
name: item.productName || '商品名称',
price: item.salePrice || item.suggestedPrice || item.originalPrice || 0,
image: item.productImage || 'https://via.placeholder.com/280x200/4285F4/ffffff?text=商品',
tag: item.productCategory || '其他',
category: item.productCategory || '其他',
description: item.productDescription || '',
createTime: item.createTime
}));
console.log('推荐商品查询结果:', {
originalInterests: userInterests,
mappedInterests: mappedInterests,
totalCount: products.length,
products: products.map(p => ({ name: p.name, category: p.category }))
});
// 即使没有商品也返回success: truedata为空数组
return {
success: true,
data: products,
count: products.length,
message: products.length === 0 ? '暂无推荐商品' : '推荐商品加载成功'
};
} catch (e) {
console.error('获取推荐商品失败:', e);
return {
success: false,
error: e.message
};
}
};
// 云函数入口函数
exports.main = async (event, context) => {
try {
console.log('云函数被调用type:', event.type);
// 获取操作类型,支持多种参数格式
const type = event.type || event.userInfo?.type || (event.tcbContext && event.tcbContext.type);
console.log('云函数被调用type:', type);
console.log('event参数:', JSON.stringify(event, null, 2));
switch (event.type) {
if (!type) {
return {
success: false,
error: '缺少操作类型参数(type)',
event: event
};
}
switch (type) {
case "getOpenId":
return await getOpenId();
case "getMiniProgramCode":
@ -755,12 +1082,20 @@ exports.main = async (event, context) => {
return await addTestUser(event);
case "analyzeProductPrice":
return await analyzeProductPrice(event);
case "getUserByOpenId":
return await getUserByOpenId(event);
case "getUserByOpenId":
return await getUserByOpenId(event);
case "getProductCategories":
return await getProductCategories();
case "updateUserInterests":
return await updateUserInterests(event);
case "getRecommendedProducts":
return await getRecommendedProducts(event);
case "initCategoryTable":
return await initCategoryTable();
default:
return {
success: false,
error: '未知的操作类型: ' + event.type
error: '未知的操作类型: ' + type
};
}
} catch (error) {
@ -769,11 +1104,13 @@ exports.main = async (event, context) => {
console.error('错误堆栈:', error.stack);
console.error('event:', JSON.stringify(event, null, 2));
const type = event.type || event.userInfo?.type || (event.tcbContext && event.tcbContext.type);
return {
success: false,
error: error.message || '云函数执行失败',
details: error.stack,
type: event.type
type: type
};
}
};

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 463 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

@ -9,7 +9,11 @@
"pages/publish/publish",
"pages/buy/buy",
"pages/purchase/purchase",
"pages/profile/profile"
"pages/profile/profile",
"pages/myProducts/myProducts",
"pages/orders/orders",
"pages/product-detail/product-detail",
"pages/wanted-list/wanted-list"
],
"window": {
"navigationBarTextStyle": "black",

@ -184,13 +184,13 @@ Page({
currentPage: 1,
hasMore: true
});
// 重新加载数据
this.loadProducts(true);
this.setData({
refreshing: false
});
wx.stopPullDownRefresh();
this.loadProducts(true);
this.setData({
refreshing: false
});
wx.stopPullDownRefresh();
},
/**
@ -236,7 +236,7 @@ Page({
this.setData({
loading: true
});
// 构建查询条件
let query = db.collection('T_product').where({
status: '在售' // 只显示在售商品
@ -265,9 +265,9 @@ Page({
this.processProducts(res.data).then((processedProducts) => {
const products = refresh ? processedProducts : [...this.data.products, ...processedProducts];
this.setData({
products: products,
filteredProducts: products,
this.setData({
products: products,
filteredProducts: products,
currentPage: page,
hasMore: res.data.length === pageSize,
loading: false
@ -298,8 +298,8 @@ Page({
duration: 2000
});
this.setData({
loading: false
});
loading: false
});
}
});
},
@ -449,7 +449,7 @@ Page({
*/
sortProducts() {
let sorted = [...this.data.filteredProducts];
switch (this.data.selectedSort) {
case 0: // 最新发布
sorted.sort((a, b) => {
@ -468,7 +468,7 @@ Page({
sorted.sort((a, b) => parseFloat(b.sellerRating || 0) - parseFloat(a.sellerRating || 0));
break;
}
this.setData({
filteredProducts: sorted
});

@ -5,55 +5,57 @@ Page({
* 页面的初始数据
*/
data: {
// 商品类别数据
// 商品类别数据默认类别使用中文类别名作为ID
// 注意类别名称必须与数据库中T_product表的productCategory字段值一致
categories: [
{
id: 'cosmetics',
name: '化妆品',
description: '美妆护肤产品',
icon: 'https://via.placeholder.com/60x60/FF6B9D/ffffff?text=💄'
},
{
id: 'books',
name: '二手书',
description: '教材、小说、专业书籍',
icon: 'https://via.placeholder.com/60x60/4CAF50/ffffff?text=📚'
},
{
id: 'electronics',
id: '电子产品',
name: '电子产品',
description: '手机、电脑、配件',
icon: 'https://via.placeholder.com/60x60/2196F3/ffffff?text=📱'
icon: '💻',
iconBg: '#2196F3'
},
{
id: 'clothing',
id: '图书文具',
name: '图书文具',
description: '教材、小说、专业书籍、文具',
icon: '📖',
iconBg: '#4CAF50'
},
{
id: '服装鞋帽',
name: '服装鞋帽',
description: '衣服、鞋子、配饰',
icon: 'https://via.placeholder.com/60x60/FF9800/ffffff?text=👕'
icon: '👔',
iconBg: '#FF9800'
},
{
id: 'sports',
name: '运动器材',
description: '健身器材、球类运动',
icon: 'https://via.placeholder.com/60x60/9C27B0/ffffff?text=⚽'
id: '家居用品',
name: '家居用品',
description: '桌椅、小家电、生活用品',
icon: '🏠',
iconBg: '#795548'
},
{
id: 'furniture',
name: '家具家电',
description: '桌椅、小家电',
icon: 'https://via.placeholder.com/60x60/795548/ffffff?text=🛋️'
id: '运动户外',
name: '运动户外',
description: '健身器材、球类运动、户外用品',
icon: '🏃',
iconBg: '#9C27B0'
},
{
id: 'stationery',
name: '文具用品',
description: '笔、本、学习用品',
icon: 'https://via.placeholder.com/60x60/607D8B/ffffff?text=✏️'
id: '美妆个护',
name: '美妆个护',
description: '美妆护肤产品',
icon: '💅',
iconBg: '#FF6B9D'
},
{
id: 'others',
name: '其他商品',
id: '其他',
name: '其他',
description: '其他各类商品',
icon: 'https://via.placeholder.com/60x60/757575/ffffff?text=📦'
icon: '📦',
iconBg: '#757575'
}
],
// 已选择的商品类别
@ -66,71 +68,252 @@ Page({
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 从本地存储获取用户信息,检查是否已有选择的兴趣
const userInfo = wx.getStorageSync('userInfo');
if (userInfo && userInfo.interests) {
this.setData({
selectedInterests: userInfo.interests
});
this.updateCategoriesSelection();
}
// 延迟执行所有初始化操作,避免立即执行导致的问题
setTimeout(() => {
try {
// 先初始化选中状态映射(使用默认类别)
this.updateCategoriesSelection();
// 加载商品类别列表(已禁用云函数调用)
this.loadCategories();
// 从本地存储获取用户信息,检查是否已有选择的兴趣
const userInfo = wx.getStorageSync('userInfo');
if (userInfo && userInfo.interests && userInfo.interests.length > 0) {
this.setData({
selectedInterests: userInfo.interests
});
// 更新选中状态
this.updateCategoriesSelection();
}
} catch (e) {
console.error('onLoad初始化失败:', e);
}
}, 100);
},
/**
* 更新商品类别的选中状态
* 从云数据库加载商品类别
*/
updateCategoriesSelection() {
const categorySelectedMap = {};
this.data.categories.forEach(category => {
categorySelectedMap[category.id] = this.data.selectedInterests.indexOf(category.id) > -1;
});
loadCategories() {
// 暂时禁用云函数调用,直接使用前端默认类别数据
// 这样可以确保图标正确显示
console.log('使用前端默认类别数据');
// 直接使用data中定义的默认类别
this.setData({
categorySelectedMap: categorySelectedMap
categories: this.data.categories
});
// 更新选中状态映射
this.updateCategoriesSelection();
// 如果需要从云数据库加载,取消下面的注释
/*
wx.showLoading({
title: '加载类别中...',
mask: true
});
wx.cloud.callFunction({
name: 'quickstartFunctions',
data: {
type: 'getProductCategories'
},
success: (res) => {
wx.hideLoading();
console.log('获取商品类别成功:', res);
if (res.result && res.result.success && res.result.data && res.result.data.length > 0) {
// 使用从数据库获取的类别,但强制使用前端图标映射
const iconMap = {
'电子产品': '💻',
'图书文具': '📖',
'服装鞋帽': '👔',
'家居用品': '🏠',
'运动户外': '🏃',
'美妆个护': '💅',
'其他': '📦'
};
const categories = res.result.data.map(item => ({
id: item.id || item.name,
name: item.name,
description: item.description || '各类商品',
icon: iconMap[item.name] || item.icon || '📦', // 优先使用前端图标映射
iconBg: item.iconBg || '#757575'
}));
console.log('从数据库获取的类别:', categories);
this.setData({
categories: categories
});
// 更新选中状态映射
this.updateCategoriesSelection();
} else {
console.warn('数据库中没有商品类别,使用默认类别');
}
},
fail: (err) => {
wx.hideLoading();
console.error('获取商品类别失败:', err);
}
});
*/
},
/**
* 更新商品类别的选中状态
*/
updateCategoriesSelection() {
try {
const categorySelectedMap = {};
const categories = this.data.categories || [];
const selectedInterests = this.data.selectedInterests || [];
categories.forEach(category => {
// 检查是否选中(支持多种匹配方式)
const isSelected = selectedInterests.some(selected => {
// 直接匹配ID
if (selected === category.id) return true;
// 匹配名称
if (selected === category.name) return true;
return false;
});
categorySelectedMap[category.id] = isSelected;
});
// 使用同步方式更新,不传入任何回调
this.setData({
categorySelectedMap: categorySelectedMap
});
} catch (e) {
console.error('更新选中状态失败:', e);
}
},
/**
* 商品类别点击事件
*/
onCategoryTap(e) {
const categoryId = e.currentTarget.dataset.id;
let selectedInterests = [...this.data.selectedInterests];
// 立即阻止所有默认行为和事件冒泡
if (e) {
try {
if (typeof e.preventDefault === 'function') e.preventDefault();
if (typeof e.stopPropagation === 'function') e.stopPropagation();
if (typeof e.stopImmediatePropagation === 'function') e.stopImmediatePropagation();
} catch (err) {
// 静默处理
}
}
// 获取类别ID
let categoryId = null;
try {
categoryId = e && e.currentTarget && e.currentTarget.dataset && e.currentTarget.dataset.id;
} catch (err) {
return false;
}
if (!categoryId) {
return false;
}
console.log('点击了商品类别:', categoryId);
console.log('当前已选择:', selectedInterests);
// 使用防抖,避免快速连续点击
const now = Date.now();
if (this._lastTapTime && now - this._lastTapTime < 300) {
return false;
}
this._lastTapTime = now;
// 复制当前选择列表
let selectedInterests = [...(this.data.selectedInterests || [])];
// 切换选择状态
const index = selectedInterests.indexOf(categoryId);
let index = selectedInterests.indexOf(categoryId);
if (index === -1) {
const category = (this.data.categories || []).find(cat => cat.id === categoryId);
if (category) {
index = selectedInterests.findIndex(selected => selected === category.name || selected === category.id);
}
}
if (index > -1) {
// 取消选择
selectedInterests.splice(index, 1);
console.log('取消选择:', categoryId);
} else {
// 检查是否已达到最大选择数量4个
if (selectedInterests.length >= 4) {
if (selectedInterests.length >= 2) {
wx.showToast({
title: '最多只能选择4种商品类别',
icon: 'none'
title: '最多只能选择2种商品类别',
icon: 'none',
duration: 2000
});
return;
return false;
}
// 选择
selectedInterests.push(categoryId);
console.log('选择:', categoryId);
}
console.log('更新后选择:', selectedInterests);
// 直接计算选中状态映射
const categorySelectedMap = {};
this.data.categories.forEach(category => {
categorySelectedMap[category.id] = selectedInterests.indexOf(category.id) > -1;
(this.data.categories || []).forEach(category => {
categorySelectedMap[category.id] = selectedInterests.indexOf(category.id) > -1 ||
selectedInterests.indexOf(category.name) > -1;
});
// 一次性更新所有数据
this.setData({
selectedInterests: selectedInterests,
categorySelectedMap: categorySelectedMap
// 直接同步更新数据,不使用任何延迟或回调
try {
this.setData({
selectedInterests: selectedInterests,
categorySelectedMap: categorySelectedMap
});
} catch (error) {
console.error('更新数据失败:', error);
}
return false;
},
/**
* 保存用户兴趣到数据库
*/
saveUserInterests(interests) {
return new Promise((resolve, reject) => {
wx.cloud.callFunction({
name: 'quickstartFunctions',
data: {
type: 'updateUserInterests',
interests: interests
},
success: (res) => {
console.log('保存用户兴趣成功:', res);
if (res.result && res.result.success) {
// 更新本地存储的用户信息
const userInfo = wx.getStorageSync('userInfo') || {};
userInfo.interests = interests;
wx.setStorageSync('userInfo', userInfo);
resolve(res.result);
} else {
// 即使云函数失败,也保存到本地,确保用户可以继续使用
const userInfo = wx.getStorageSync('userInfo') || {};
userInfo.interests = interests;
wx.setStorageSync('userInfo', userInfo);
console.warn('云函数保存失败,已保存到本地:', res.result?.error);
resolve({ success: true, localOnly: true });
}
},
fail: (err) => {
console.error('保存用户兴趣失败:', err);
// 即使云函数失败,也保存到本地,确保用户可以继续使用
const userInfo = wx.getStorageSync('userInfo') || {};
userInfo.interests = interests;
wx.setStorageSync('userInfo', userInfo);
console.warn('云函数调用失败,已保存到本地');
resolve({ success: true, localOnly: true });
}
});
});
},
@ -146,38 +329,74 @@ Page({
return;
}
// 显示注册成功提示
wx.showToast({
title: '注册成功,请重新登录',
icon: 'success',
duration: 2000
// 显示加载中
wx.showLoading({
title: '保存兴趣中...',
});
// 延迟跳转到登录界面index页面
setTimeout(() => {
wx.redirectTo({
url: '/pages/index/index'
// 保存用户兴趣到数据库
this.saveUserInterests(this.data.selectedInterests)
.then(() => {
wx.hideLoading();
// 显示注册成功提示
wx.showToast({
title: '兴趣保存成功',
icon: 'success',
duration: 2000
});
// 延迟跳转到主页面
setTimeout(() => {
wx.redirectTo({
url: '/pages/main/main'
});
}, 2000);
})
.catch((err) => {
wx.hideLoading();
console.error('保存兴趣失败:', err);
wx.showToast({
title: '保存失败,请重试',
icon: 'none'
});
});
}, 2000);
},
/**
* 跳过选择
*/
onSkip() {
// 显示注册成功提示
wx.showToast({
title: '注册成功,请重新登录',
icon: 'success',
duration: 2000
// 显示加载中
wx.showLoading({
title: '处理中...',
});
// 延迟跳转到登录界面index页面
setTimeout(() => {
wx.redirectTo({
url: '/pages/index/index'
// 保存空兴趣数组到数据库
this.saveUserInterests([])
.then(() => {
wx.hideLoading();
// 显示注册成功提示
wx.showToast({
title: '注册成功',
icon: 'success',
duration: 2000
});
// 延迟跳转到主页面
setTimeout(() => {
wx.redirectTo({
url: '/pages/main/main'
});
}, 2000);
})
.catch((err) => {
wx.hideLoading();
console.error('跳过选择失败:', err);
wx.showToast({
title: '处理失败,请重试',
icon: 'none'
});
});
}, 2000);
},
/**
@ -191,7 +410,8 @@ Page({
* 生命周期函数--监听页面显示
*/
onShow() {
// 完全禁用onShow中的所有操作避免任何可能的跳转或闪烁
// 不执行任何操作,确保页面稳定
},
/**

@ -1,3 +1,5 @@
{
"usingComponents": {}
"usingComponents": {},
"navigationBarTitleText": "选择兴趣",
"enablePullDownRefresh": false
}

@ -3,35 +3,47 @@
<view class="interests-container">
<!-- 顶部引导信息 -->
<view class="header">
<image class="welcome-icon" src="../" mode="aspectFit"></image>
<view class="welcome-icon-wrapper">
<text class="welcome-emoji">🎯</text>
</view>
<text class="title">欢迎来到校园二手交易平台!</text>
<text class="question">请选择您感兴趣的商品类别</text>
<text class="subtitle">我们将根据您的选择为您推荐相关商品</text>
</view>
<!-- 商品类别选择区域 -->
<view class="interests-section">
<text class="section-title">选择您感兴趣的商品类别</text>
<text class="section-subtitle">可多选,用于个性化商品推荐)</text>
<text class="section-subtitle">最多选择2个,用于个性化商品推荐)</text>
<view class="interests-grid">
<view
class="interest-card {{categorySelectedMap[category.id] ? 'selected' : ''}}"
wx:for="{{categories}}"
wx:key="id"
bindtap="onCategoryTap"
wx:for-item="category"
catchtap="onCategoryTap"
data-id="{{category.id}}"
hover-class="none"
hover-stop-propagation="true"
>
<image class="interest-icon" src="{{category.icon}}" mode="aspectFit"></image>
<view class="interest-icon-wrapper">
<text class="interest-icon">{{category.icon}}</text>
</view>
<text class="interest-name">{{category.name}}</text>
<text class="interest-desc">{{category.description}}</text>
</view>
</view>
<!-- 调试信息:如果类别为空,显示提示 -->
<view wx:if="{{categories.length === 0}}" class="empty-tip">
<text>正在加载类别...</text>
</view>
</view>
<!-- 选择提示 -->
<view class="selection-hint" wx:if="{{selectedInterests.length > 0}}">
<text class="hint-text">已选择 {{selectedInterests.length}}/4 个商品类别</text>
<text class="hint-text">已选择 {{selectedInterests.length}}/2 个商品类别</text>
<text class="hint-tip">系统将根据您的选择为您推荐相关商品</text>
</view>

@ -17,27 +17,32 @@
.header {
text-align: center;
margin-bottom: 50rpx;
padding-top: 20rpx;
}
.welcome-icon {
width: 120rpx;
height: 120rpx;
margin-bottom: 20rpx;
.welcome-icon-wrapper {
width: 140rpx;
height: 140rpx;
margin: 0 auto 25rpx;
background: linear-gradient(135deg, #4285F4 0%, #34A853 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 20rpx rgba(66, 133, 244, 0.3);
}
.welcome-emoji {
font-size: 80rpx;
line-height: 1;
}
.title {
display: block;
font-size: 36rpx;
font-size: 38rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
}
.subtitle {
display: block;
font-size: 28rpx;
color: #666;
margin-bottom: 5rpx;
margin-bottom: 15rpx;
}
.question {
@ -45,6 +50,14 @@
font-size: 32rpx;
color: #4285F4;
font-weight: 600;
margin-bottom: 10rpx;
}
.subtitle {
display: block;
font-size: 26rpx;
color: #999;
margin-top: 10rpx;
}
/* 商品类别选择区域 */
@ -74,20 +87,43 @@
}
.interest-card {
background: #f8f9fa;
border: 2rpx solid #e9ecef;
border-radius: 15rpx;
padding: 30rpx 20rpx;
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
border: 3rpx solid #e9ecef;
border-radius: 20rpx;
padding: 35rpx 20rpx;
text-align: center;
transition: all 0.3s ease;
cursor: pointer;
position: relative;
overflow: hidden;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
}
.interest-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 6rpx;
background: linear-gradient(90deg, #4285F4 0%, #34A853 50%, #EA4335 100%);
opacity: 0;
transition: opacity 0.3s ease;
}
.interest-card:active {
transform: scale(0.95);
}
.interest-card.selected {
background: #4285F4;
background: linear-gradient(135deg, #4285F4 0%, #5C9FF7 100%);
border-color: #4285F4;
transform: translateY(-5rpx);
box-shadow: 0 10rpx 20rpx rgba(66, 133, 244, 0.3);
box-shadow: 0 12rpx 30rpx rgba(66, 133, 244, 0.4);
}
.interest-card.selected::before {
opacity: 1;
}
.interest-card.selected .interest-name,
@ -95,48 +131,128 @@
color: white;
}
.interest-card.selected .interest-icon-wrapper {
transform: scale(1.15) rotate(5deg);
box-shadow: 0 12rpx 32rpx rgba(66, 133, 244, 0.4), 0 0 0 4rpx rgba(255, 255, 255, 0.3);
background: linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.85)) !important;
animation: iconPulse 0.6s ease-out;
}
.interest-card.selected .interest-icon {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.98), rgba(255, 255, 255, 0.9)) !important;
transform: scale(1.2) rotate(-5deg);
box-shadow: 0 8rpx 24rpx rgba(66, 133, 244, 0.5), inset 0 2rpx 8rpx rgba(255, 255, 255, 0.5);
filter: drop-shadow(0 4rpx 8rpx rgba(66, 133, 244, 0.3));
}
@keyframes iconPulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.25) rotate(8deg);
}
100% {
transform: scale(1.15) rotate(5deg);
}
}
.interest-icon-wrapper {
width: 120rpx;
height: 120rpx;
margin: 0 auto 20rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 28rpx;
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
position: relative;
overflow: hidden;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.7));
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.12);
}
.interest-icon-wrapper::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.1));
pointer-events: none;
border-radius: 28rpx;
opacity: 0;
transition: opacity 0.3s ease;
}
.interest-card:active .interest-icon-wrapper::before {
opacity: 1;
}
.interest-icon {
width: 60rpx;
height: 60rpx;
margin-bottom: 15rpx;
width: 96rpx;
height: 96rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 56rpx;
line-height: 1;
text-align: center;
border-radius: 24rpx;
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.15), inset 0 2rpx 4rpx rgba(255, 255, 255, 0.3);
position: relative;
z-index: 1;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.85));
backdrop-filter: blur(10rpx);
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", "Android Emoji", sans-serif;
-webkit-font-smoothing: antialiased;
user-select: none;
white-space: nowrap;
overflow: hidden;
}
.interest-name {
display: block;
font-size: 28rpx;
font-size: 30rpx;
font-weight: 600;
color: #333;
margin-bottom: 5rpx;
margin-bottom: 8rpx;
transition: color 0.3s ease;
}
.interest-desc {
display: block;
font-size: 22rpx;
font-size: 24rpx;
color: #666;
transition: color 0.3s ease;
}
/* 选择提示 */
.selection-hint {
background: #e8f4fd;
border: 1rpx solid #b3e0ff;
border-radius: 10rpx;
padding: 20rpx;
background: linear-gradient(135deg, #e8f4fd 0%, #f0f9ff 100%);
border: 2rpx solid #b3e0ff;
border-radius: 15rpx;
padding: 25rpx;
margin-bottom: 30rpx;
text-align: center;
box-shadow: 0 4rpx 12rpx rgba(0, 102, 204, 0.1);
}
.hint-text {
display: block;
font-size: 28rpx;
font-size: 30rpx;
color: #0066cc;
font-weight: 600;
margin-bottom: 5rpx;
margin-bottom: 8rpx;
}
.hint-tip {
display: block;
font-size: 24rpx;
color: #666;
line-height: 1.6;
}
/* 底部操作按钮 */
@ -159,19 +275,27 @@
.confirm-btn {
flex: 1;
background: #4285F4;
background: linear-gradient(135deg, #4285F4 0%, #34A853 100%);
color: white;
border: none;
border-radius: 50rpx;
font-size: 28rpx;
height: 80rpx;
line-height: 80rpx;
font-size: 30rpx;
height: 88rpx;
line-height: 88rpx;
font-weight: 600;
box-shadow: 0 8rpx 20rpx rgba(66, 133, 244, 0.3);
transition: all 0.3s ease;
}
.confirm-btn:active {
transform: scale(0.98);
box-shadow: 0 4rpx 12rpx rgba(66, 133, 244, 0.2);
}
.confirm-btn.disabled {
background: #ccc;
background: #e0e0e0;
color: #999;
box-shadow: none;
}
/* 进度提示 */
@ -183,4 +307,12 @@
.progress-text {
font-size: 24rpx;
color: #999;
}
/* 空状态提示 */
.empty-tip {
text-align: center;
padding: 60rpx 20rpx;
color: #999;
font-size: 28rpx;
}

@ -70,6 +70,7 @@ Page({
onLoad(options) {
this.loadUserInfo();
this.loadRecommendations();
this.loadHotWanted();
},
/**
@ -96,10 +97,107 @@ Page({
const userInfo = wx.getStorageSync('userInfo') || {};
const userInterests = userInfo.interests || [];
// 这里可以调用后端API获取基于用户兴趣的推荐
// 目前使用模拟数据
console.log('用户兴趣:', userInterests);
console.log('加载智能推荐商品...');
// 显示加载提示
wx.showLoading({
title: '加载推荐中...',
mask: false
});
// 从云数据库获取推荐商品
wx.cloud.callFunction({
name: 'quickstartFunctions',
data: {
type: 'getRecommendedProducts',
interests: userInterests,
limit: 8
},
success: (res) => {
wx.hideLoading();
console.log('获取推荐商品响应:', res);
if (res.result && res.result.success) {
const productsData = res.result.data || [];
if (productsData.length > 0) {
// 格式化商品数据
const products = productsData.map(item => ({
id: item.id,
name: item.name,
price: typeof item.price === 'number' ? item.price.toFixed(2) : item.price,
image: item.image,
tag: item.tag || item.category || '其他'
}));
this.setData({
recommendProducts: products
});
console.log('智能推荐商品加载完成,数量:', products.length);
} else {
// 数据库中没有商品,显示友好提示
console.log('数据库中暂无推荐商品');
this.loadEmptyProducts();
}
} else {
console.warn('获取推荐商品失败:', res.result?.error);
this.loadDefaultProducts();
}
},
fail: (err) => {
wx.hideLoading();
console.error('获取推荐商品失败:', err);
// 失败时使用默认商品数据
this.loadDefaultProducts();
}
});
},
/**
* 加载空商品数据当数据库中没有商品时
*/
loadEmptyProducts() {
const emptyProducts = [
{
id: 'empty',
name: '暂无推荐商品',
price: '0.00',
tag: '其他',
image: 'https://via.placeholder.com/280x200/E0E0E0/999999?text=暂无商品',
isEmpty: true
}
];
this.setData({
recommendProducts: emptyProducts
});
},
/**
* 加载默认商品数据当云数据库查询失败时
*/
loadDefaultProducts() {
const defaultProducts = [
{
id: 'error',
name: '加载失败,请稍后重试',
price: '0.00',
tag: '其他',
image: 'https://via.placeholder.com/280x200/FF6B6B/ffffff?text=加载失败',
isError: true
}
];
this.setData({
recommendProducts: defaultProducts
});
wx.showToast({
title: '推荐加载失败',
icon: 'none',
duration: 2000
});
},
/**
@ -146,28 +244,154 @@ Page({
*/
onProductTap(e) {
const productId = e.currentTarget.dataset.id;
const product = this.data.recommendProducts.find(p => p.id === productId);
wx.showModal({
title: product.name,
content: `价格: ¥${product.price}\n类别: ${product.tag}`,
showCancel: false,
confirmText: '知道了'
wx.navigateTo({
url: `/pages/product-detail/product-detail?id=${productId}`
});
},
/**
* 求购点击事件
* 加载热门求购点击量前三
*/
onWantedTap(e) {
async loadHotWanted() {
try {
const db = wx.cloud.database();
// 查询所有活跃的求购信息微信云数据库不支持多个orderBy先用viewCount排序
const result = await db.collection('T_want')
.where({
status: 'active'
})
.orderBy('viewCount', 'desc')
.limit(20) // 先获取更多数据,然后在客户端排序
.get();
console.log('热门求购查询结果:', result);
if (result.data && result.data.length > 0) {
// 在客户端进行排序:先按点击量降序,点击量相同则按时间降序(时间更晚的在前)
const sortedData = result.data.sort((a, b) => {
const viewCountA = a.viewCount || 0;
const viewCountB = b.viewCount || 0;
// 如果点击量不同,按点击量降序
if (viewCountA !== viewCountB) {
return viewCountB - viewCountA;
}
// 点击量相同,按时间降序(时间更晚的在前)
const timeA = new Date(a.createTime).getTime();
const timeB = new Date(b.createTime).getTime();
return timeB - timeA;
});
// 取前3条
const hotWanted = sortedData.slice(0, 3).map(item => ({
id: item._id,
title: item.productName || '求购商品',
budget: item.expectedPrice || 0,
time: this.formatTime(item.createTime),
viewCount: item.viewCount || 0,
category: item.productCategory || '其他'
}));
this.setData({
hotWanted: hotWanted
});
} else {
// 如果没有数据,显示空状态
this.setData({
hotWanted: []
});
}
} catch (err) {
console.error('加载热门求购失败:', err);
// 失败时使用默认数据
this.setData({
hotWanted: []
});
}
},
/**
* 格式化时间
*/
formatTime(date) {
if (!date) return '';
const d = new Date(date);
const now = new Date();
const diff = now - d;
const minute = 60 * 1000;
const hour = 60 * minute;
const day = 24 * hour;
if (diff < minute) {
return '刚刚';
} else if (diff < hour) {
return Math.floor(diff / minute) + '分钟前';
} else if (diff < day) {
return Math.floor(diff / hour) + '小时前';
} else if (diff < 7 * day) {
return Math.floor(diff / day) + '天前';
} else {
return d.getMonth() + 1 + '月' + d.getDate() + '日';
}
},
/**
* 求购点击事件增加点击量
*/
async onWantedTap(e) {
const wantedId = e.currentTarget.dataset.id;
const wanted = this.data.hotWanted.find(w => w.id === wantedId);
wx.showModal({
title: wanted.title,
content: `预算: ¥${wanted.budget}\n发布时间: ${wanted.time}`,
showCancel: false,
confirmText: '知道了'
if (!wanted) return;
// 增加点击量
try {
const db = wx.cloud.database();
const _ = db.command;
await db.collection('T_want').doc(wantedId).update({
data: {
viewCount: _.inc(1),
updateTime: new Date()
}
});
// 更新本地数据
const updatedWanted = this.data.hotWanted.map(item => {
if (item.id === wantedId) {
return {
...item,
viewCount: (item.viewCount || 0) + 1
};
}
return item;
});
this.setData({
hotWanted: updatedWanted
});
// 跳转到求购详情页面(或显示详情)
wx.navigateTo({
url: `/pages/wanted-list/wanted-list?id=${wantedId}`
});
} catch (err) {
console.error('更新点击量失败:', err);
// 即使更新失败也跳转
wx.navigateTo({
url: `/pages/wanted-list/wanted-list?id=${wantedId}`
});
}
},
/**
* 跳转到求购列表页面
*/
onViewMoreWanted() {
wx.navigateTo({
url: '/pages/wanted-list/wanted-list'
});
},

@ -67,14 +67,25 @@
<!-- 热门求购 -->
<view class="wanted-section">
<view class="section-header">
<text class="section-title">热门求购</text>
<text class="section-subtitle">大家都在找什么</text>
<view class="header-left">
<text class="section-title">热门求购</text>
<text class="section-subtitle">大家都在找什么</text>
</view>
<view class="more-btn" bindtap="onViewMoreWanted">
<text>更多</text>
<text class="more-arrow"></text>
</view>
</view>
<view class="wanted-list">
<view class="wanted-item" wx:for="{{hotWanted}}" wx:key="id" bindtap="onWantedTap" data-id="{{item.id}}">
<text class="wanted-title">{{item.title}}</text>
<text class="wanted-price">预算: ¥{{item.budget}}</text>
<text class="wanted-time">{{item.time}}</text>
<view class="wanted-info">
<text class="wanted-price">预算: ¥{{item.budget}}</text>
<text class="wanted-time">{{item.time}}</text>
</view>
</view>
<view class="empty-wanted" wx:if="{{hotWanted.length === 0}}">
<text>暂无热门求购</text>
</view>
</view>
</view>

@ -113,6 +113,33 @@
.section-header {
margin-bottom: 25rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.header-left {
flex: 1;
}
.more-btn {
display: flex;
align-items: center;
color: #4285F4;
font-size: 26rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
transition: background-color 0.2s ease;
}
.more-btn:active {
background-color: rgba(66, 133, 244, 0.1);
}
.more-arrow {
margin-left: 4rpx;
font-size: 32rpx;
font-weight: bold;
}
.section-title {
@ -202,6 +229,13 @@
border-left: 6rpx solid #4285F4;
}
.wanted-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10rpx;
}
.wanted-title {
font-size: 28rpx;
color: #333;
@ -213,8 +247,7 @@
.wanted-price {
font-size: 24rpx;
color: #FF6B35;
display: block;
margin-bottom: 8rpx;
font-weight: 600;
}
.wanted-time {
@ -222,6 +255,13 @@
color: #999;
}
.empty-wanted {
text-align: center;
padding: 40rpx 0;
color: #999;
font-size: 26rpx;
}
/* 底部导航栏 */
.tab-bar {
position: fixed;

@ -0,0 +1,8 @@
{
"usingComponents": {},
"navigationBarTitleText": "我的商品",
"navigationBarBackgroundColor": "#4285F4",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": true,
"backgroundTextStyle": "dark"
}

@ -0,0 +1,371 @@
// pages/myProducts/myProducts.js
Page({
/**
* 页面的初始数据
*/
data: {
// 商品分类
categories: ['全部', '电子产品', '图书文具', '服装鞋帽', '运动户外', '美妆个护', '家居生活', '其他'],
selectedCategory: 0,
// 商品状态筛选
statusFilter: ['全部', '在售', '已售出', '已下架'],
selectedStatus: 0,
// 排序选项
sortOptions: ['默认排序', '发布时间', '价格从低到高', '价格从高到低'],
selectedSort: 0,
// 商品列表数据
products: [],
filteredProducts: [],
// 加载状态
isLoading: true,
error: '',
// 分页相关
currentPage: 1,
pageSize: 10,
hasMore: true
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.loadMyProducts();
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
// 每次显示页面时重新加载数据,确保数据最新
this.loadMyProducts();
},
/**
* 加载我的商品列表
*/
loadMyProducts() {
this.setData({
isLoading: true,
error: ''
});
// 模拟从后端获取数据
setTimeout(() => {
// 模拟商品数据
const mockProducts = [
{
id: 'p1',
name: '二手iPhone 13 128GB 白色',
price: '2999',
originalPrice: '5999',
image: 'https://via.placeholder.com/200x200/4285F4/ffffff?text=iPhone13',
category: '电子产品',
status: 'selling', // selling: 在售, sold: 已售出, off: 已下架
publishTime: '2024-04-15 10:30',
views: 156,
likes: 23,
comments: 8
},
{
id: 'p2',
name: 'Java编程思想 第4版 九成新',
price: '35',
originalPrice: '108',
image: 'https://via.placeholder.com/200x200/34A853/ffffff?text=Java',
category: '图书文具',
status: 'selling',
publishTime: '2024-04-14 16:45',
views: 89,
likes: 12,
comments: 5
},
{
id: 'p3',
name: '耐克运动鞋 Air Max 270',
price: '180',
originalPrice: '899',
image: 'https://via.placeholder.com/200x200/EA4335/ffffff?text=运动鞋',
category: '服装鞋帽',
status: 'sold',
publishTime: '2024-04-12 09:15',
views: 210,
likes: 35,
comments: 12
},
{
id: 'p4',
name: '戴尔笔记本电脑 XPS 13',
price: '3200',
originalPrice: '9999',
image: 'https://via.placeholder.com/200x200/FBBC05/ffffff?text=笔记本',
category: '电子产品',
status: 'selling',
publishTime: '2024-04-10 14:20',
views: 178,
likes: 28,
comments: 9
},
{
id: 'p5',
name: '瑜伽垫 加厚加宽 蓝色',
price: '68',
originalPrice: '158',
image: 'https://via.placeholder.com/200x200/4285F4/ffffff?text=瑜伽垫',
category: '运动户外',
status: 'off',
publishTime: '2024-04-08 11:30',
views: 67,
likes: 9,
comments: 3
},
{
id: 'p6',
name: 'SK-II神仙水 75ml',
price: '380',
originalPrice: '590',
image: 'https://via.placeholder.com/200x200/FF6B9D/ffffff?text=神仙水',
category: '美妆个护',
status: 'selling',
publishTime: '2024-04-05 15:45',
views: 234,
likes: 42,
comments: 18
}
];
this.setData({
products: mockProducts,
filteredProducts: mockProducts,
isLoading: false,
hasMore: false // 模拟没有更多数据
});
}, 1500);
},
/**
* 切换商品分类
*/
onCategoryChange(e) {
const index = e.currentTarget.dataset.index;
this.setData({
selectedCategory: index,
currentPage: 1
});
this.filterProducts();
},
/**
* 切换商品状态筛选
*/
onStatusChange(e) {
const index = e.currentTarget.dataset.index;
this.setData({
selectedStatus: index,
currentPage: 1
});
this.filterProducts();
},
/**
* 切换排序方式
*/
onSortChange(e) {
const index = e.currentTarget.dataset.index;
this.setData({
selectedSort: index
});
this.filterProducts();
},
/**
* 根据筛选条件过滤商品
*/
filterProducts() {
let filtered = [...this.data.products];
// 按分类筛选
if (this.data.selectedCategory !== 0) {
const category = this.data.categories[this.data.selectedCategory];
filtered = filtered.filter(item => item.category === category);
}
// 按状态筛选
if (this.data.selectedStatus !== 0) {
const statusMap = {
1: 'selling',
2: 'sold',
3: 'off'
};
const status = statusMap[this.data.selectedStatus];
filtered = filtered.filter(item => item.status === status);
}
// 排序
switch (this.data.selectedSort) {
case 1: // 发布时间
filtered.sort((a, b) => new Date(b.publishTime) - new Date(a.publishTime));
break;
case 2: // 价格从低到高
filtered.sort((a, b) => parseFloat(a.price) - parseFloat(b.price));
break;
case 3: // 价格从高到低
filtered.sort((a, b) => parseFloat(b.price) - parseFloat(a.price));
break;
default: // 默认排序
// 保持原顺序
break;
}
this.setData({
filteredProducts: filtered
});
},
/**
* 查看商品详情
*/
onProductDetail(e) {
const productId = e.currentTarget.dataset.id;
wx.navigateTo({
url: `/pages/product-detail/product-detail?id=${productId}`
});
},
/**
* 下架商品
*/
onOffShelf(e) {
const productId = e.currentTarget.dataset.id;
wx.showModal({
title: '确认下架',
content: '确定要下架该商品吗?下架后可重新上架。',
success: (res) => {
if (res.confirm) {
// 更新商品状态
const updatedProducts = this.data.products.map(item => {
if (item.id === productId && item.status === 'selling') {
return { ...item, status: 'off' };
}
return item;
});
this.setData({
products: updatedProducts
});
this.filterProducts();
wx.showToast({
title: '商品已下架',
icon: 'success'
});
}
}
});
},
/**
* 重新上架商品
*/
onReShelf(e) {
const productId = e.currentTarget.dataset.id;
wx.showModal({
title: '重新上架',
content: '确定要重新上架该商品吗?',
success: (res) => {
if (res.confirm) {
// 更新商品状态
const updatedProducts = this.data.products.map(item => {
if (item.id === productId && item.status === 'off') {
return { ...item, status: 'selling', publishTime: new Date().toLocaleString('zh-CN') };
}
return item;
});
this.setData({
products: updatedProducts
});
this.filterProducts();
wx.showToast({
title: '商品已重新上架',
icon: 'success'
});
}
}
});
},
/**
* 编辑商品
*/
onEditProduct(e) {
const productId = e.currentTarget.dataset.id;
wx.navigateTo({
url: `/pages/publish/publish?mode=edit&id=${productId}`
});
},
/**
* 删除商品
*/
onDeleteProduct(e) {
const productId = e.currentTarget.dataset.id;
wx.showModal({
title: '确认删除',
content: '确定要删除该商品吗?删除后无法恢复。',
success: (res) => {
if (res.confirm) {
// 删除商品
const updatedProducts = this.data.products.filter(item => item.id !== productId);
this.setData({
products: updatedProducts
});
this.filterProducts();
wx.showToast({
title: '商品已删除',
icon: 'success'
});
}
}
});
},
/**
* 上拉加载更多
*/
onReachBottom() {
if (this.data.hasMore && !this.data.isLoading) {
// 实际项目中这里应该请求下一页数据
wx.showToast({
title: '没有更多数据了',
icon: 'none'
});
}
},
/**
* 下拉刷新
*/
onPullDownRefresh() {
this.loadMyProducts();
wx.stopPullDownRefresh();
},
/**
* 返回上一页
*/
onBack() {
wx.navigateBack();
}
});

@ -0,0 +1,130 @@
<!--pages/myProducts/myProducts.wxml-->
<view class="page-container">
<!-- 加载状态 -->
<view wx:if="{{isLoading}}" class="loading-container">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 错误状态 -->
<view wx:elif="{{error}}" class="error-container">
<text class="error-icon">⚠</text>
<text class="error-text">{{error}}</text>
<button class="retry-btn" bindtap="loadMyProducts">重新加载</button>
</view>
<!-- 商品列表内容 -->
<view wx:else class="content">
<!-- 分类筛选 -->
<scroll-view class="category-scroll" scroll-x enable-flex>
<view class="category-item {{selectedCategory === index ? 'selected' : ''}}" wx:for="{{categories}}" wx:key="index" data-index="{{index}}" bindtap="onCategoryChange">
<text>{{item}}</text>
</view>
</scroll-view>
<!-- 状态筛选和排序 -->
<view class="filter-section">
<view class="filter-group">
<text class="filter-label">状态:</text>
<view class="filter-options">
<view class="filter-item {{selectedStatus === index ? 'selected' : ''}}" wx:for="{{statusFilter}}" wx:key="index" data-index="{{index}}" bindtap="onStatusChange">
<text>{{item}}</text>
</view>
</view>
</view>
<view class="sort-group">
<text class="filter-label">排序:</text>
<view class="filter-options">
<view class="filter-item {{selectedSort === index ? 'selected' : ''}}" wx:for="{{sortOptions}}" wx:key="index" data-index="{{index}}" bindtap="onSortChange">
<text>{{item}}</text>
</view>
</view>
</view>
</view>
<!-- 商品列表 -->
<view class="product-list">
<view wx:if="{{filteredProducts.length === 0}}" class="empty-state">
<image class="empty-image" src="/images/empty.png" mode="aspectFit"></image>
<text class="empty-text">暂无商品</text>
<button class="publish-btn" bindtap="onPublish">去发布商品</button>
</view>
<view wx:else class="product-items">
<view class="product-item" wx:for="{{filteredProducts}}" wx:key="id">
<!-- 商品基本信息 -->
<view class="product-info" bindtap="onProductDetail" data-id="{{item.id}}">
<!-- 商品图片 -->
<view class="product-image-container">
<image class="product-image" src="{{item.image}}" mode="aspectFill"></image>
<view class="product-status" wx:if="{{item.status === 'sold'}}">
<text>已售出</text>
</view>
<view class="product-status off" wx:if="{{item.status === 'off'}}">
<text>已下架</text>
</view>
</view>
<!-- 商品详情 -->
<view class="product-details">
<view class="product-title">
<text>{{item.name}}</text>
</view>
<view class="product-meta">
<text class="product-category">{{item.category}}</text>
<text class="publish-time">{{item.publishTime}}</text>
</view>
<view class="product-stats">
<view class="stat-item">
<text class="stat-icon">👁</text>
<text class="stat-text">{{item.views}}</text>
</view>
<view class="stat-item">
<text class="stat-icon">❤️</text>
<text class="stat-text">{{item.likes}}</text>
</view>
<view class="stat-item">
<text class="stat-icon">💬</text>
<text class="stat-text">{{item.comments}}</text>
</view>
</view>
</view>
<!-- 价格信息 -->
<view class="price-info">
<text class="current-price">¥{{item.price}}</text>
<text class="original-price" wx:if="{{item.originalPrice}}">¥{{item.originalPrice}}</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="action-buttons">
<!-- 在售商品的操作 -->
<view wx:if="{{item.status === 'selling'}}">
<button class="action-btn edit-btn" bindtap="onEditProduct" data-id="{{item.id}}">编辑</button>
<button class="action-btn off-btn" bindtap="onOffShelf" data-id="{{item.id}}">下架</button>
<button class="action-btn delete-btn" bindtap="onDeleteProduct" data-id="{{item.id}}">删除</button>
</view>
<!-- 已售出商品的操作 -->
<view wx:elif="{{item.status === 'sold'}}">
<button class="action-btn view-btn" bindtap="onProductDetail" data-id="{{item.id}}">查看详情</button>
<button class="action-btn delete-btn" bindtap="onDeleteProduct" data-id="{{item.id}}">删除</button>
</view>
<!-- 已下架商品的操作 -->
<view wx:else>
<button class="action-btn edit-btn" bindtap="onEditProduct" data-id="{{item.id}}">编辑</button>
<button class="action-btn on-btn" bindtap="onReShelf" data-id="{{item.id}}">重新上架</button>
<button class="action-btn delete-btn" bindtap="onDeleteProduct" data-id="{{item.id}}">删除</button>
</view>
</view>
</view>
</view>
</view>
</view>
</view>

@ -0,0 +1,466 @@
/* pages/myProducts/myProducts.wxss */
/* 全局样式 - 与小程序保持一致 */
page {
height: 100%;
background-color: #f5f5f5;
}
.page-container {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 头部样式 - 使用小程序主色调 */
.header {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
padding: 0 30rpx;
background-color: #4285F4;
color: white;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
}
.back-btn {
width: 88rpx;
height: 88rpx;
display: flex;
align-items: center;
justify-content: center;
}
.back-icon {
font-size: 40rpx;
font-weight: bold;
color: white;
}
.header-title {
font-size: 36rpx;
font-weight: bold;
color: white;
}
.header-right {
width: 88rpx;
}
/* 内容区域 */
.content {
padding-top: 88rpx;
}
/* 分类滚动 - 与main页面风格一致 */
.category-scroll {
display: flex;
white-space: nowrap;
background-color: white;
padding: 20rpx 0;
border-bottom: 1rpx solid #eee;
}
.category-item {
display: inline-flex;
padding: 0 30rpx;
height: 60rpx;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #333;
position: relative;
}
.category-item.selected {
color: #4285F4;
font-weight: bold;
}
.category-item.selected::after {
content: '';
position: absolute;
bottom: 0;
left: 30%;
width: 40%;
height: 6rpx;
background-color: #4285F4;
border-radius: 3rpx;
}
/* 筛选区域 - 卡片式设计 */
.filter-section {
background-color: white;
padding: 20rpx 30rpx;
margin-bottom: 20rpx;
border-bottom: 1rpx solid #eee;
border-radius: 0 0 10rpx 10rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
.filter-group,
.sort-group {
display: flex;
align-items: center;
margin-bottom: 10rpx;
}
.sort-group {
margin-bottom: 0;
}
.filter-label {
font-size: 28rpx;
color: #666;
margin-right: 20rpx;
width: 80rpx;
}
.filter-options {
display: flex;
flex-wrap: wrap;
flex: 1;
}
.filter-item {
padding: 8rpx 20rpx;
margin-right: 20rpx;
margin-bottom: 10rpx;
background-color: #f8f9fa;
border-radius: 20rpx;
font-size: 26rpx;
color: #666;
transition: all 0.3s ease;
border: 1rpx solid #e9ecef;
}
.filter-item.selected {
background-color: #4285F4;
color: white;
border-color: #4285F4;
}
/* 加载状态 */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 60vh;
}
.loading-spinner {
width: 60rpx;
height: 60rpx;
border: 8rpx solid #f3f3f3;
border-top: 8rpx solid #4285F4;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
margin-top: 20rpx;
font-size: 28rpx;
color: #999;
}
/* 错误状态 */
.error-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 60vh;
padding: 40rpx;
text-align: center;
}
.error-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
color: #ff6b6b;
}
.error-text {
font-size: 28rpx;
color: #666;
margin-bottom: 30rpx;
}
.retry-btn {
background-color: #4285F4;
color: white;
font-size: 28rpx;
padding: 0 40rpx;
border-radius: 44rpx;
box-shadow: 0 4rpx 12rpx rgba(66, 133, 244, 0.3);
}
.retry-btn:active {
transform: scale(0.95);
opacity: 0.9;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 60vh;
background-color: white;
margin: 20rpx;
border-radius: 16rpx;
padding: 40rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.empty-image {
width: 200rpx;
height: 200rpx;
margin-bottom: 30rpx;
opacity: 0.5;
}
.empty-text {
font-size: 28rpx;
color: #999;
margin-bottom: 30rpx;
}
.publish-btn {
background-color: #4285F4;
color: white;
font-size: 28rpx;
padding: 0 60rpx;
border-radius: 44rpx;
box-shadow: 0 4rpx 12rpx rgba(66, 133, 244, 0.3);
}
.publish-btn:active {
transform: scale(0.95);
opacity: 0.9;
}
/* 商品列表 */
.product-list {
padding: 0 20rpx 20rpx;
}
.product-item {
background-color: white;
border-radius: 16rpx;
margin-bottom: 20rpx;
overflow: hidden;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.product-info {
display: flex;
padding: 20rpx;
}
.product-image-container {
position: relative;
width: 180rpx;
height: 180rpx;
margin-right: 20rpx;
border-radius: 8rpx;
overflow: hidden;
}
.product-image {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
.product-status {
position: absolute;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.6);
color: white;
padding: 8rpx 16rpx;
font-size: 24rpx;
border-radius: 8rpx 0 8rpx 0;
}
.product-status.off {
background-color: rgba(150, 150, 150, 0.6);
}
.product-details {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.product-title {
font-size: 30rpx;
color: #333;
line-height: 44rpx;
margin-bottom: 10rpx;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.product-meta {
display: flex;
justify-content: space-between;
font-size: 24rpx;
color: #999;
margin-bottom: 10rpx;
}
.product-stats {
display: flex;
align-items: center;
}
.stat-item {
display: flex;
align-items: center;
margin-right: 20rpx;
font-size: 24rpx;
color: #999;
}
.stat-icon {
margin-right: 4rpx;
}
.price-info {
display: flex;
align-items: baseline;
justify-content: flex-end;
}
.current-price {
font-size: 36rpx;
font-weight: bold;
color: #ff6b81;
}
.original-price {
font-size: 24rpx;
color: #999;
text-decoration: line-through;
margin-left: 10rpx;
}
/* 操作按钮 - 优化样式,确保更小并在同一行排列 */
.action-buttons {
display: flex;
padding: 12rpx 15rpx;
border-top: 1rpx solid #f0f0f0;
justify-content: flex-end;
}
.action-btn {
flex: 0 0 auto; /* 不自动拉伸,保持按钮固定大小 */
margin: 0 6rpx;
font-size: 22rpx; /* 进一步减小字体大小 */
line-height: 50rpx; /* 进一步减小按钮高度 */
padding: 0 25rpx; /* 添加水平内边距,确保文字不会太拥挤 */
border-radius: 8rpx;
transition: all 0.3s ease;
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1);
border: none;
white-space: nowrap; /* 确保按钮文字不会换行 */
}
/* 增加按钮点击效果 */
.action-btn:active {
transform: scale(0.95);
opacity: 0.9;
}
/* 保持与小程序整体色彩一致 */
.edit-btn {
background-color: #4285F4; /* 主色调 */
color: white;
}
.off-btn {
background-color: #ff9800; /* 下架按钮使用橙色 */
color: white;
}
.on-btn {
background-color: #34C759; /* 上架按钮使用绿色 */
color: white;
}
.delete-btn {
background-color: #ff6b81; /* 删除按钮使用红色,与订单页面一致 */
color: white;
}
.view-btn {
background-color: #2196f3; /* 查看按钮使用蓝色变体 */
color: white;
}
/* 响应式调整 - 确保小屏幕上按钮仍能在一行显示 */
@media screen and (max-width: 320px) {
.filter-options {
flex-direction: column;
}
.filter-item {
margin-right: 0;
margin-bottom: 10rpx;
}
.product-info {
flex-direction: column;
}
.product-image-container {
width: 100%;
height: 300rpx;
margin-right: 0;
margin-bottom: 20rpx;
}
.price-info {
justify-content: flex-start;
}
.action-buttons {
padding: 8rpx 10rpx;
overflow-x: auto; /* 允许在极端情况下水平滚动 */
}
.action-btn {
font-size: 20rpx;
line-height: 45rpx;
margin: 0 4rpx;
padding: 0 20rpx;
}
}
/* 文本样式 - 与app.wxss保持一致 */
text {
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', SimSun, sans-serif;
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}

@ -0,0 +1,355 @@
// pages/orders/orders.js
Page({
/**
* 页面的初始数据
*/
data: {
// 当前选中的标签
activeTab: 'all',
// 订单数据
orders: [],
// 加载状态
isLoading: true
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 可以从选项中获取初始标签
if (options.tab) {
this.setData({
activeTab: options.tab
});
}
this.loadOrders();
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
// 页面显示时重新加载订单数据
this.loadOrders();
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
this.loadOrders();
},
/**
* 切换订单标签
*/
switchTab(e) {
const tab = e.currentTarget.dataset.tab;
this.setData({
activeTab: tab,
isLoading: true
});
this.loadOrders();
},
/**
* 加载订单数据
*/
loadOrders() {
this.setData({ isLoading: true });
// 模拟从后端获取订单数据
setTimeout(() => {
// 模拟不同状态的订单数据
const mockOrders = [
{
id: '1',
orderNumber: '20240501123456',
orderTime: '2024-05-01 14:30:25',
status: 'pending',
statusText: '待付款',
totalCount: 2,
totalPrice: 199.80,
products: [
{
productId: 'p1',
name: '智能手表 运动监测 心率血压多功能防水',
specs: '黑色 标准版',
price: 129.90,
count: 1,
image: '/images/product1.jpg'
},
{
productId: 'p2',
name: '蓝牙耳机 无线降噪 长续航',
specs: '白色',
price: 69.90,
count: 1,
image: '/images/product2.jpg'
}
]
},
{
id: '2',
orderNumber: '20240428987654',
orderTime: '2024-04-28 09:15:42',
status: 'paid',
statusText: '待发货',
totalCount: 1,
totalPrice: 899.00,
products: [
{
productId: 'p3',
name: '平板电脑 10.2英寸 128GB WiFi版',
specs: '深空灰',
price: 899.00,
count: 1,
image: '/images/product3.jpg'
}
]
},
{
id: '3',
orderNumber: '20240420567890',
orderTime: '2024-04-20 16:45:18',
status: 'shipped',
statusText: '待收货',
totalCount: 3,
totalPrice: 258.70,
products: [
{
productId: 'p4',
name: '便携式充电宝 20000mAh 双向快充',
specs: '白色',
price: 79.90,
count: 2,
image: '/images/product4.jpg'
},
{
productId: 'p5',
name: '手机支架 桌面懒人支架 可调节',
specs: '银色',
price: 98.90,
count: 1,
image: '/images/product5.jpg'
}
]
},
{
id: '4',
orderNumber: '20240410135790',
orderTime: '2024-04-10 11:20:36',
status: 'completed',
statusText: '已完成',
totalCount: 1,
totalPrice: 359.00,
products: [
{
productId: 'p6',
name: '无线充电器 15W快充 兼容多设备',
specs: '黑色',
price: 359.00,
count: 1,
image: '/images/product6.jpg'
}
]
},
{
id: '5',
orderNumber: '20240401246800',
orderTime: '2024-04-01 18:50:12',
status: 'cancelled',
statusText: '已取消',
totalCount: 1,
totalPrice: 599.00,
products: [
{
productId: 'p7',
name: '智能音箱 AI语音助手 360°环绕音效',
specs: '红色',
price: 599.00,
count: 1,
image: '/images/product7.jpg'
}
]
}
];
// 根据当前标签筛选订单
let filteredOrders = mockOrders;
if (this.data.activeTab !== 'all') {
filteredOrders = mockOrders.filter(order => order.status === this.data.activeTab);
}
this.setData({
orders: filteredOrders,
isLoading: false
});
// 停止下拉刷新
wx.stopPullDownRefresh();
}, 1000);
},
/**
* 取消订单
*/
cancelOrder(e) {
const orderId = e.currentTarget.dataset.id;
wx.showModal({
title: '取消订单',
content: '确定要取消该订单吗?',
success: (res) => {
if (res.confirm) {
// 这里可以调用接口取消订单
wx.showToast({
title: '订单已取消',
icon: 'success'
});
// 重新加载订单数据
setTimeout(() => {
this.loadOrders();
}, 500);
}
}
});
},
/**
* 去付款
*/
payOrder(e) {
const orderId = e.currentTarget.dataset.id;
console.log('开始支付流程订单ID:', orderId);
// 显示加载中状态
wx.showLoading({
title: '处理中...',
});
// 1. 查找当前订单信息
const order = this.data.orders.find(item => item.id === orderId);
if (!order) {
wx.hideLoading();
wx.showToast({
title: '订单信息不存在',
icon: 'error'
});
console.error('未找到订单信息:', orderId);
return;
}
console.log('找到订单信息:', order);
// 2. 模拟调用支付接口获取支付参数实际项目中应调用后端API
setTimeout(() => {
// 模拟支付参数(实际项目中由后端返回)
const payParams = {
timeStamp: '' + Math.floor(Date.now() / 1000),
nonceStr: 'noncestr' + Math.random().toString(36).substr(2, 15),
package: 'prepay_id=wx202405011234567890abcdef1234567890',
signType: 'MD5',
paySign: '8A40C1194B39B20B16238888A8D8DF2E'
};
console.log('准备调用支付接口,参数:', payParams);
// 3. 调用微信支付接口
wx.requestPayment({
...payParams,
success: (res) => {
console.log('支付成功:', res);
wx.hideLoading();
wx.showToast({
title: '支付成功',
icon: 'success'
});
// 4. 重新加载订单数据,更新订单状态
setTimeout(() => {
this.loadOrders();
}, 1000);
// 5. 支付成功后跳转到订单详情页
setTimeout(() => {
this.viewOrderDetail(e);
}, 1500);
},
fail: (err) => {
console.error('支付失败:', err);
wx.hideLoading();
// 判断是否是用户取消支付
if (err.errMsg.indexOf('cancel') >= 0) {
wx.showToast({
title: '支付已取消',
icon: 'none'
});
} else {
// 其他支付失败情况
wx.showToast({
title: '支付失败,请重试',
icon: 'error'
});
}
}
// 移除重复的complete回调因为success和fail中已经处理了hideLoading
});
}, 1000);
},
/**
* 确认收货
*/
confirmReceipt(e) {
const orderId = e.currentTarget.dataset.id;
wx.showModal({
title: '确认收货',
content: '确认已收到商品吗?',
success: (res) => {
if (res.confirm) {
// 这里可以调用接口确认收货
wx.showToast({
title: '已确认收货',
icon: 'success'
});
// 重新加载订单数据
setTimeout(() => {
this.loadOrders();
}, 500);
}
}
});
},
/**
* 查看订单详情
*/
viewOrderDetail(e) {
const orderId = e.currentTarget.dataset.id;
wx.navigateTo({
url: `/pages/order-detail/order-detail?id=${orderId}`
});
},
/**
* 再次购买
*/
buyAgain(e) {
const orderId = e.currentTarget.dataset.id;
wx.showToast({
title: '将商品加入购物车',
icon: 'none'
});
// 这里可以实现再次购买的逻辑
},
/**
* 去购物
*/
goShopping() {
wx.switchTab({
url: '/pages/main/main'
});
}
});

@ -0,0 +1,8 @@
{
"usingComponents": {},
"navigationBarTitleText": "我的订单",
"navigationBarBackgroundColor": "#f5f5f5",
"navigationBarTextStyle": "black",
"enablePullDownRefresh": true,
"backgroundTextStyle": "dark"
}

@ -0,0 +1,90 @@
<!--pages/orders/orders.wxml-->
<view class="orders-container">
<!-- 订单状态筛选标签 -->
<view class="order-tabs">
<view class="tab-item {{activeTab === 'all' ? 'active' : ''}}" bindtap="switchTab" data-tab="all">全部</view>
<view class="tab-item {{activeTab === 'pending' ? 'active' : ''}}" bindtap="switchTab" data-tab="pending">待付款</view>
<view class="tab-item {{activeTab === 'paid' ? 'active' : ''}}" bindtap="switchTab" data-tab="paid">待发货</view>
<view class="tab-item {{activeTab === 'shipped' ? 'active' : ''}}" bindtap="switchTab" data-tab="shipped">待收货</view>
<view class="tab-item {{activeTab === 'completed' ? 'active' : ''}}" bindtap="switchTab" data-tab="completed">已完成</view>
<view class="tab-item {{activeTab === 'cancelled' ? 'active' : ''}}" bindtap="switchTab" data-tab="cancelled">已取消</view>
</view>
<!-- 订单列表 -->
<view class="order-list">
<!-- 加载状态 -->
<view wx:if="{{isLoading}}" class="loading-container">
<view class="loading-spinner"></view>
<text class="loading-text">加载中...</text>
</view>
<!-- 空状态 -->
<view wx:elif="{{orders.length === 0 && !isLoading}}" class="empty-container">
<image class="empty-icon" src="/images/empty-orders.png" mode="aspectFit"></image>
<text class="empty-text">暂无订单</text>
<text class="empty-subtext">去逛逛,发现心仪商品</text>
<button class="go-shopping-btn" bindtap="goShopping">去购物</button>
</view>
<!-- 订单列表 -->
<block wx:else>
<view wx:for="{{orders}}" wx:key="id" class="order-item">
<!-- 订单头部 -->
<view class="order-header">
<view class="order-info">
<text class="order-number">订单号:{{item.orderNumber}}</text>
<text class="order-time">{{item.orderTime}}</text>
</view>
<text class="order-status {{item.status}}">{{item.statusText}}</text>
</view>
<!-- 订单商品 -->
<view class="order-products">
<view wx:for="{{item.products}}" wx:for-item="product" wx:key="productId" class="product-item">
<image class="product-image" src="{{product.image || '/images/default-product.png'}}" mode="aspectFit"></image>
<view class="product-info">
<text class="product-name">{{product.name}}</text>
<text class="product-specs">{{product.specs || '规格:标准'}}</text>
<view class="product-price-count">
<text class="product-price">¥{{product.price}}</text>
<text class="product-count">x{{product.count}}</text>
</view>
</view>
</view>
</view>
<!-- 订单尾部 -->
<view class="order-footer">
<view class="order-total">
<view class="total-info">
<text class="total-label">共{{item.totalCount}}件商品</text>
</view>
<view class="price-container">
<text class="total-price-label">合计:</text>
<text class="currency-symbol">¥</text>
<text class="total-price-value">{{item.totalPrice}}</text>
</view>
</view>
<view class="order-actions">
<button wx:if="{{item.status === 'pending'}}" class="action-btn secondary" bindtap="cancelOrder" data-id="{{item.id}}">
取消订单
</button>
<button wx:if="{{item.status === 'pending'}}" class="action-btn primary" bindtap="payOrder" data-id="{{item.id}}">
去付款
</button>
<button wx:if="{{item.status === 'shipped'}}" class="action-btn primary" bindtap="confirmReceipt" data-id="{{item.id}}">
确认收货
</button>
<button wx:if="{{item.status === 'completed' || item.status === 'cancelled'}}" class="action-btn secondary" bindtap="viewOrderDetail" data-id="{{item.id}}">
查看详情
</button>
<button wx:if="{{item.status === 'completed'}}" class="action-btn secondary" bindtap="buyAgain" data-id="{{item.id}}">
再次购买
</button>
</view>
</view>
</view>
</block>
</view>
</view>

@ -0,0 +1,351 @@
/* pages/orders/orders.wxss *//* 订单页面样式 */
.orders-container {
padding-bottom: 100rpx;
background-color: #f5f5f5;
}
/* 订单状态筛选标签 */
.order-tabs {
display: flex;
background-color: #fff;
border-bottom: 1px solid #e0e0e0;
padding: 0 20rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
}
.tab-item {
flex: 1;
text-align: center;
padding: 28rpx 0;
font-size: 28rpx;
color: #666;
position: relative;
}
.tab-item.active {
color: #667eea;
}
.tab-item.active::after {
content: '';
position: absolute;
bottom: 0;
left: 30%;
width: 40%;
height: 6rpx;
background-color: #667eea;
border-radius: 3rpx;
}
/* 订单列表 */
.order-list {
margin-top: 90rpx;
}
/* 加载状态 */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.loading-spinner {
width: 60rpx;
height: 60rpx;
border: 6rpx solid #f3f3f3;
border-top: 6rpx solid #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading-text {
margin-top: 20rpx;
font-size: 28rpx;
color: #999;
}
/* 空状态 */
.empty-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 150rpx 0;
background-color: #fff;
margin: 20rpx;
border-radius: 20rpx;
}
.empty-icon {
width: 200rpx;
height: 200rpx;
opacity: 0.5;
}
.empty-text {
margin-top: 40rpx;
font-size: 32rpx;
color: #333;
}
.empty-subtext {
margin-top: 20rpx;
font-size: 28rpx;
color: #999;
}
.go-shopping-btn {
margin-top: 40rpx;
background-color: #667eea;
color: #fff;
border-radius: 40rpx;
font-size: 28rpx;
padding: 20rpx 60rpx;
}
/* 订单项 */
.order-item {
background-color: #fff;
margin: 20rpx;
border-radius: 20rpx;
overflow: hidden;
}
/* 订单头部 */
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 28rpx 32rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.order-info {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.order-number {
font-size: 26rpx;
color: #999;
}
.order-time {
font-size: 24rpx;
color: #ccc;
}
.order-status {
font-size: 28rpx;
font-weight: 500;
}
.order-status.pending {
color: #ff9500;
}
.order-status.paid {
color: #5ac8fa;
}
.order-status.shipped {
color: #34c759;
}
.order-status.completed {
color: #667eea;
}
.order-status.cancelled {
color: #999;
}
/* 订单商品 */
.order-products {
padding: 28rpx 32rpx;
}
.product-item {
display: flex;
gap: 24rpx;
padding: 16rpx 0;
}
.product-image {
width: 180rpx;
height: 180rpx;
border-radius: 12rpx;
background-color: #f5f5f5;
}
.product-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.product-name {
font-size: 28rpx;
color: #333;
line-height: 40rpx;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.product-specs {
font-size: 24rpx;
color: #999;
margin: 8rpx 0;
}
.product-price-count {
display: flex;
justify-content: space-between;
align-items: center;
}
.product-price {
font-size: 28rpx;
color: #ff6b81;
font-weight: 500;
}
.product-count {
font-size: 26rpx;
color: #999;
}
/* 订单尾部 */
.order-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx 32rpx;
border-top: 1rpx solid #f0f0f0;
background-color: #fafafa;
}
.order-total {
display: flex;
flex-direction: column;
gap: 10rpx;
}
.total-info {
display: flex;
align-items: center;
}
.total-label {
font-size: 26rpx;
color: #666;
}
.price-container {
display: flex;
align-items: baseline;
}
.total-price-label {
font-size: 26rpx;
color: #666;
margin-right: 8rpx;
}
.currency-symbol {
font-size: 24rpx;
color: #ff6b81;
font-weight: bold;
}
.total-price-value {
font-size: 32rpx;
color: #ff6b81;
font-weight: bold;
}
.order-actions {
display: flex;
gap: 20rpx;
flex-wrap: wrap;
justify-content: flex-end;
}
.action-btn {
padding: 0 32rpx;
font-size: 26rpx;
line-height: 68rpx;
border-radius: 34rpx;
margin: 0;
min-width: 160rpx;
text-align: center;
}
.action-btn.primary {
background-color: #667eea;
color: #fff;
font-weight: 500;
box-shadow: 0 2rpx 10rpx rgba(102, 126, 234, 0.2);
}
.action-btn.secondary {
background-color: #fff;
color: #666;
border: 1rpx solid #e0e0e0;
}
/* 按钮点击效果 */
.action-btn:active {
opacity: 0.8;
}
/* 响应式调整优化 */
@media (max-width: 375px) {
.product-item {
flex-direction: column;
align-items: flex-start;
}
.product-image {
width: 100%;
height: 300rpx;
}
.order-footer {
flex-direction: column;
align-items: flex-start;
gap: 20rpx;
}
.order-actions {
width: 100%;
justify-content: flex-start;
}
.action-btn {
flex: 1;
min-width: auto;
margin-right: 12rpx;
}
.action-btn:last-child {
margin-right: 0;
}
}

@ -5,7 +5,8 @@ Page({
*/
data: {
// 图片相关
imagePath: '',
imagePath: '', // 临时路径
imageFileID: '', // 云存储路径(上传后)
showResult: false,
isAnalyzing: false,
@ -46,6 +47,7 @@ Page({
const tempFilePath = res.tempFiles[0].tempFilePath;
this.setData({
imagePath: tempFilePath,
imageFileID: '', // 重置云存储路径
showResult: false // 重置结果,等待用户点击智能定价按钮
});
@ -79,6 +81,7 @@ Page({
const tempFilePath = res.tempFiles[0].tempFilePath;
this.setData({
imagePath: tempFilePath,
imageFileID: '', // 重置云存储路径
showResult: false // 重置结果,等待用户点击智能定价按钮
});
@ -153,12 +156,15 @@ Page({
success: (uploadRes) => {
console.log('图片上传成功:', uploadRes);
// 保存云存储路径
const imageFileID = uploadRes.fileID;
// 调用云函数进行AI分析
wx.cloud.callFunction({
name: 'quickstartFunctions',
data: {
type: 'analyzeProductPrice',
fileID: uploadRes.fileID,
fileID: imageFileID,
originalPrice: parseFloat(this.data.originalPrice)
},
success: (res) => {
@ -172,10 +178,11 @@ Page({
const resultData = res.result.data;
console.log('解析结果数据:', JSON.stringify(resultData, null, 2));
this.setData({
isAnalyzing: false,
showResult: true,
this.setData({
isAnalyzing: false,
showResult: true,
imageFileID: imageFileID, // 保存云存储路径
suggestedPrice: resultData.suggestedPrice || '0.00',
conditionLevel: resultData.conditionLevel || '--',
aiScore: resultData.aiScore || '--',
@ -184,12 +191,12 @@ Page({
productCategory: resultData.productCategory || '--',
productDescription: resultData.analysisReport || '请先进行AI分析以生成商品信息',
marketRange: `¥${(parseFloat(resultData.suggestedPrice || 0) * 0.9).toFixed(2)}${(parseFloat(resultData.suggestedPrice || 0) * 1.1).toFixed(2)}`
});
wx.showToast({
title: 'AI分析完成',
icon: 'success'
});
});
wx.showToast({
title: 'AI分析完成',
icon: 'success'
});
} else {
// 云函数返回了错误
const errorMsg = res.result.error || 'AI分析失败';
@ -417,9 +424,12 @@ Page({
return;
}
// 优先使用云存储路径,如果没有则使用临时路径
const imagePath = this.data.imageFileID || this.data.imagePath;
// 准备跳转参数
const params = {
imagePath: this.data.imagePath,
imagePath: imagePath, // 使用云存储路径或临时路径
productName: this.data.productName,
productCategory: this.data.productCategory,
productDescription: this.data.productDescription,
@ -431,14 +441,28 @@ Page({
analysisReport: this.data.analysisReport
};
// 编码参数
// 编码参数(处理特殊字符和长文本)
const queryString = Object.keys(params)
.map(key => `${key}=${encodeURIComponent(params[key])}`)
.filter(key => params[key] !== undefined && params[key] !== null && params[key] !== '')
.map(key => {
const value = params[key];
// 对于长文本,可能需要截断,但这里先完整传递
return `${key}=${encodeURIComponent(value)}`;
})
.join('&');
console.log('跳转到发布页面,参数:', params);
// 跳转到发布商品页面
wx.navigateTo({
url: `/pages/publish/publish?${queryString}`
url: `/pages/publish/publish?${queryString}`,
fail: (err) => {
console.error('跳转失败:', err);
wx.showToast({
title: '跳转失败,请重试',
icon: 'none'
});
}
});
},

@ -0,0 +1,639 @@
// pages/product-detail/product-detail.js
Page({
/**
* 页面的初始数据
*/
data: {
productId: '',
product: null,
loading: true,
// 图片相关
imageUrls: [],
currentImageIndex: 0,
// 购物车数量
cartCount: 0,
// 收藏状态
isFavorite: false,
// 是否已添加到购物车
inCart: false
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
const productId = options.id;
if (!productId) {
wx.showToast({
title: '商品不存在',
icon: 'none'
});
setTimeout(() => {
wx.navigateBack();
}, 1500);
return;
}
this.setData({
productId: productId
});
this.loadProductDetail();
this.loadCartCount();
this.loadFavoriteStatus();
},
/**
* 加载收藏状态
*/
loadFavoriteStatus() {
try {
const favorites = wx.getStorageSync('favorites') || [];
const isFavorite = favorites.includes(this.data.productId);
this.setData({
isFavorite: isFavorite
});
} catch (err) {
console.error('加载收藏状态失败:', err);
}
},
/**
* 加载商品详情
*/
async loadProductDetail() {
wx.showLoading({
title: '加载中...',
mask: true
});
try {
const db = wx.cloud.database();
const result = await db.collection('T_product').doc(this.data.productId).get();
if (!result.data) {
throw new Error('商品不存在');
}
const product = result.data;
console.log('========== 商品详情加载 ==========');
console.log('商品ID:', this.data.productId);
console.log('完整商品数据:', JSON.stringify(product, null, 2));
console.log('商品图片字段(productImage):', product.productImage);
console.log('图片字段类型:', typeof product.productImage);
console.log('是否为数组:', Array.isArray(product.productImage));
// 获取图片URL
let imageUrls = [];
// 获取图片数据(支持多种格式)
let rawImages = [];
if (product.productImage) {
if (Array.isArray(product.productImage)) {
rawImages = product.productImage.filter(img => img); // 过滤空值
} else if (typeof product.productImage === 'string') {
rawImages = [product.productImage];
}
}
// 兼容其他可能的字段名
if (rawImages.length === 0) {
if (product.imageUrls && Array.isArray(product.imageUrls)) {
rawImages = product.imageUrls.filter(img => img);
} else if (product.imageUrl) {
rawImages = [product.imageUrl];
}
}
console.log('原始图片数据:', rawImages);
console.log('原始图片数量:', rawImages.length);
if (rawImages.length === 0) {
console.warn('⚠️ 商品没有图片数据');
imageUrls.push('/images/default-product.png');
} else {
// 处理每张图片
for (let i = 0; i < rawImages.length; i++) {
const img = rawImages[i];
console.log(`\n处理图片 ${i + 1}/${rawImages.length}:`, img);
if (!img || typeof img !== 'string') {
console.warn(`图片 ${i + 1} 无效,跳过`);
continue;
}
// 云存储路径处理
if (img.startsWith('cloud://')) {
console.log('检测到云存储路径获取临时URL...');
try {
const tempFileURL = await wx.cloud.getTempFileURL({
fileList: [img]
});
console.log('getTempFileURL返回:', tempFileURL);
if (tempFileURL && tempFileURL.fileList && tempFileURL.fileList.length > 0) {
const tempURL = tempFileURL.fileList[0].tempFileURL;
if (tempURL) {
console.log(`✅ 成功获取临时URL: ${tempURL}`);
imageUrls.push(tempURL);
} else {
console.warn(`⚠️ 临时URL为空`);
// 尝试直接使用原路径
imageUrls.push(img);
}
} else {
console.warn('⚠️ fileList为空或不存在');
// 尝试直接使用原路径
imageUrls.push(img);
}
} catch (err) {
console.error(`❌ 获取临时URL失败:`, err);
console.error('错误详情:', JSON.stringify(err, null, 2));
// 即使出错,也尝试使用原路径
imageUrls.push(img);
}
}
// HTTP/HTTPS网络URL
else if (img.startsWith('http://') || img.startsWith('https://')) {
console.log('使用网络URL:', img);
imageUrls.push(img);
}
// 相对路径
else if (img.startsWith('/')) {
console.log('使用相对路径:', img);
imageUrls.push(img);
}
// 其他情况,直接尝试使用
else {
console.log('使用其他格式路径:', img);
imageUrls.push(img);
}
}
}
// 确保至少有一张图片
if (imageUrls.length === 0) {
console.warn('⚠️ 所有图片处理失败,使用默认图片');
imageUrls.push('/images/default-product.png');
}
// 如果图片URLs为空或者都是无效的添加一个测试图片
const validUrls = imageUrls.filter(url => url && url !== '/images/default-product.png');
if (validUrls.length === 0 && imageUrls.length > 0) {
console.warn('⚠️ 所有图片URL无效尝试使用测试图片');
imageUrls.push('https://via.placeholder.com/750x750/4285F4/ffffff?text=测试图片');
}
console.log('\n========== 图片处理结果 ==========');
console.log('最终图片URLs:', imageUrls);
console.log('图片URLs数量:', imageUrls.length);
console.log('第一张图片URL:', imageUrls[0]);
console.log('=====================================\n');
// 确保 imageUrls 不为空
if (!imageUrls || imageUrls.length === 0) {
console.error('❌ imageUrls为空设置默认值');
imageUrls = ['https://via.placeholder.com/750x750/4285F4/ffffff?text=暂无图片'];
}
// 获取卖家信息 - 优先从product.sellerInfo获取
let sellerInfo = {
name: '用户',
avatar: '/images/default-avatar.png',
sno: '',
phone: ''
};
// 如果product中有sellerInfo字段直接使用
if (product.sellerInfo) {
sellerInfo = {
name: product.sellerInfo.sname || product.sellerInfo.name || '用户',
avatar: product.sellerInfo.avatar || '/images/default-avatar.png',
sno: product.sellerInfo.sno || '',
phone: product.sellerInfo.phone || ''
};
} else if (product.sellerOpenId) {
// 如果没有sellerInfo尝试从T_user表查询
try {
const sellerResult = await db.collection('T_user')
.where({
_openid: product.sellerOpenId
})
.get();
if (sellerResult.data && sellerResult.data.length > 0) {
const seller = sellerResult.data[0];
sellerInfo = {
name: seller.sname || seller.nickName || '用户',
avatar: seller.avatar || '/images/default-avatar.png',
sno: seller.sno || '',
phone: seller.phone || '',
openId: seller._openid
};
}
} catch (err) {
console.error('获取卖家信息失败:', err);
}
}
console.log('卖家信息:', sellerInfo);
// 格式化时间
const publishTime = this.formatTime(product.createTime);
this.setData({
product: {
...product,
imageUrls: imageUrls,
sellerInfo: sellerInfo,
publishTime: publishTime,
conditionText: product.conditionLevel || '未知',
aiScoreText: product.aiScore || '--',
priceRangeText: product.priceRange || '--'
},
imageUrls: imageUrls,
loading: false
});
wx.hideLoading();
} catch (err) {
console.error('加载商品详情失败:', err);
wx.hideLoading();
wx.showToast({
title: '加载失败',
icon: 'none'
});
setTimeout(() => {
wx.navigateBack();
}, 1500);
}
},
/**
* 格式化时间
*/
formatTime(date) {
if (!date) return '';
const d = new Date(date);
const now = new Date();
const diff = now - d;
const minute = 60 * 1000;
const hour = 60 * minute;
const day = 24 * hour;
if (diff < minute) {
return '刚刚';
} else if (diff < hour) {
return Math.floor(diff / minute) + '分钟前';
} else if (diff < day) {
return Math.floor(diff / hour) + '小时前';
} else if (diff < 7 * day) {
return Math.floor(diff / day) + '天前';
} else {
return d.getMonth() + 1 + '月' + d.getDate() + '日';
}
},
/**
* 图片加载错误处理
*/
onImageError(e) {
const index = e.currentTarget.dataset.index;
const failedUrl = this.data.imageUrls[index];
console.error('========== 图片加载失败 ==========');
console.error('图片索引:', index);
console.error('失败URL:', failedUrl);
console.error('错误对象:', e.detail);
console.error('=====================================');
wx.showToast({
title: '图片加载失败',
icon: 'none',
duration: 2000
});
// 如果失败的是云存储临时URL尝试重新获取
if (failedUrl && failedUrl.includes('myqcloud.com')) {
console.log('尝试重新获取云存储图片URL...');
// 可以在这里添加重试逻辑
}
},
/**
* 图片轮播切换
*/
onImageChange(e) {
this.setData({
currentImageIndex: e.detail.current
});
},
/**
* 预览图片
*/
onPreviewImage(e) {
const index = e.currentTarget.dataset.index !== undefined ? e.currentTarget.dataset.index : this.data.currentImageIndex;
const imageUrls = this.data.imageUrls || [];
if (!imageUrls || imageUrls.length === 0) {
wx.showToast({
title: '暂无图片',
icon: 'none'
});
return;
}
// 确保索引有效
const currentIndex = index >= 0 && index < imageUrls.length ? index : 0;
wx.previewImage({
urls: imageUrls,
current: imageUrls[currentIndex],
fail: (err) => {
console.error('预览图片失败:', err);
wx.showToast({
title: '预览失败',
icon: 'none'
});
}
});
},
/**
* 联系卖家
*/
onContactSeller() {
const sellerInfo = this.data.product?.sellerInfo;
if (!sellerInfo) {
wx.showToast({
title: '卖家信息不存在',
icon: 'none'
});
return;
}
// 构建联系方式信息
let contactContent = `卖家:${sellerInfo.name || '未知'}\n`;
if (sellerInfo.sno) {
contactContent += `学号:${sellerInfo.sno}\n`;
}
if (sellerInfo.phone) {
contactContent += `手机号:${sellerInfo.phone}`;
} else if (this.data.product.contactInfo) {
contactContent += `联系方式:${this.data.product.contactInfo}`;
} else {
contactContent += '联系方式:暂无';
}
// 显示联系方式
wx.showModal({
title: '联系卖家',
content: contactContent,
showCancel: true,
confirmText: '复制联系方式',
cancelText: '知道了',
success: (res) => {
if (res.confirm) {
// 优先复制手机号其次复制contactInfo
const copyText = sellerInfo.phone || this.data.product.contactInfo || '';
if (copyText) {
wx.setClipboardData({
data: copyText,
success: () => {
wx.showToast({
title: '已复制',
icon: 'success'
});
}
});
} else {
wx.showToast({
title: '无联系方式',
icon: 'none'
});
}
}
}
});
},
/**
* 加入购物车
*/
onAddToCart() {
if (this.data.product.status !== '在售') {
wx.showToast({
title: '商品已下架或已售出',
icon: 'none'
});
return;
}
try {
// 获取购物车数据
let cart = wx.getStorageSync('cart') || [];
// 检查商品是否已在购物车中
const existingIndex = cart.findIndex(item => item.id === this.data.productId);
if (existingIndex >= 0) {
wx.showToast({
title: '已在购物车中',
icon: 'none'
});
return;
}
// 添加到购物车
const cartItem = {
id: this.data.productId,
name: this.data.product.productName,
price: this.data.product.salePrice || this.data.product.suggestedPrice,
image: this.data.imageUrls[0] || '/images/default-product.png',
category: this.data.product.productCategory,
sellerName: this.data.product.sellerInfo?.name || '用户',
addTime: new Date()
};
cart.push(cartItem);
wx.setStorageSync('cart', cart);
this.setData({
inCart: true,
cartCount: cart.length
});
wx.showToast({
title: '已加入购物车',
icon: 'success'
});
} catch (err) {
console.error('加入购物车失败:', err);
wx.showToast({
title: '操作失败',
icon: 'none'
});
}
},
/**
* 加载购物车数量
*/
loadCartCount() {
try {
const cart = wx.getStorageSync('cart') || [];
const inCart = cart.some(item => item.id === this.data.productId);
this.setData({
cartCount: cart.length,
inCart: inCart
});
} catch (err) {
console.error('加载购物车失败:', err);
}
},
/**
* 立即购买
*/
onBuyNow() {
if (this.data.product.status !== '在售') {
wx.showToast({
title: '商品已下架或已售出',
icon: 'none'
});
return;
}
// 显示确认购买对话框
wx.showModal({
title: '确认购买',
content: `确定要购买"${this.data.product.productName}"吗?\n价格:¥${this.data.product.salePrice || this.data.product.suggestedPrice}`,
confirmText: '确认购买',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
this.processPayment();
}
}
});
},
/**
* 处理支付模拟支付流程
*/
processPayment() {
wx.showLoading({
title: '处理中...',
mask: true
});
// 模拟支付流程
setTimeout(() => {
wx.hideLoading();
// 更新商品状态为已售
const db = wx.cloud.database();
db.collection('T_product').doc(this.data.productId).update({
data: {
status: '已售',
updateTime: new Date()
},
success: () => {
wx.showModal({
title: '购买成功',
content: '商品已加入您的订单,请尽快联系卖家完成交易',
showCancel: false,
confirmText: '知道了',
success: () => {
// 刷新商品详情
this.loadProductDetail();
}
});
},
fail: (err) => {
console.error('更新商品状态失败:', err);
wx.showToast({
title: '操作失败,请重试',
icon: 'none'
});
}
});
}, 1500);
},
/**
* 收藏/取消收藏
*/
onToggleFavorite() {
try {
let favorites = wx.getStorageSync('favorites') || [];
const index = favorites.indexOf(this.data.productId);
if (index >= 0) {
// 取消收藏
favorites.splice(index, 1);
this.setData({
isFavorite: false
});
wx.showToast({
title: '已取消收藏',
icon: 'success'
});
} else {
// 添加收藏
favorites.push(this.data.productId);
this.setData({
isFavorite: true
});
wx.showToast({
title: '已收藏',
icon: 'success'
});
}
wx.setStorageSync('favorites', favorites);
} catch (err) {
console.error('收藏操作失败:', err);
wx.showToast({
title: '操作失败',
icon: 'none'
});
}
},
/**
* 分享商品
*/
onShareAppMessage() {
return {
title: `推荐商品:${this.data.product?.productName || '商品'}`,
path: `/pages/product-detail/product-detail?id=${this.data.productId}`,
imageUrl: this.data.imageUrls[0] || ''
};
},
/**
* 查看购物车
*/
onViewCart() {
wx.showToast({
title: '购物车功能开发中',
icon: 'none'
});
// TODO: 跳转到购物车页面
// wx.switchTab({
// url: '/pages/cart/cart'
// });
}
});

@ -0,0 +1,9 @@
{
"usingComponents": {},
"navigationBarTitleText": "商品详情",
"navigationBarBackgroundColor": "#4285F4",
"navigationBarTextStyle": "white",
"enablePullDownRefresh": false,
"disableScroll": false
}

@ -0,0 +1,142 @@
<!--pages/product-detail/product-detail.wxml-->
<scroll-view class="scroll-container" scroll-y="true" wx:if="{{!loading}}">
<view class="container">
<!-- 商品图片轮播 -->
<view class="image-section">
<swiper
class="detail_swiper"
autoplay="{{imageUrls.length > 1}}"
interval="3000"
duration="500"
indicator-dots="{{imageUrls.length > 1}}"
bindchange="onImageChange"
>
<block wx:for="{{imageUrls}}" wx:key="{{index}}">
<swiper-item>
<view class="image-wrapper" bindtap="onPreviewImage" data-index="{{currentImageIndex}}">
<image
src="{{item}}"
mode="widthFix"
class="slide-image"
binderror="onImageError"
data-index="{{index}}"
lazy-load="{{false}}"
/>
</view>
</swiper-item>
</block>
</swiper>
<!-- 如果没有图片,显示占位图 -->
<view class="no-image-placeholder" wx:if="{{imageUrls.length === 0 || !imageUrls[0]}}">
<text class="placeholder-text">暂无图片</text>
</view>
<!-- 商品状态标签 -->
<view class="status-badge" wx:if="{{product.status !== '在售'}}">
<text>{{product.status === '已售' ? '已售出' : '已下架'}}</text>
</view>
</view>
<!-- 商品基本信息 -->
<view class="info-section">
<view class="price-row">
<view class="price-group">
<text class="current-price">¥{{product.salePrice || product.suggestedPrice || 0}}</text>
<text class="original-price" wx:if="{{product.originalPrice}}">¥{{product.originalPrice}}</text>
</view>
<view class="favorite-btn" bindtap="onToggleFavorite">
<text class="iconfont" wx:if="{{isFavorite}}">❤️</text>
<text class="iconfont" wx:else>🤍</text>
</view>
</view>
<view class="product-title">
<text>{{product.productName}}</text>
</view>
<view class="product-tags">
<text class="tag category-tag">{{product.productCategory}}</text>
<text class="tag condition-tag">{{product.conditionText}}</text>
<text class="tag score-tag" wx:if="{{product.aiScore && product.aiScore !== '--'}}">⭐ {{product.aiScore}}分</text>
</view>
</view>
<!-- 商品详细信息 -->
<view class="detail-section">
<view class="section-title">商品信息</view>
<view class="detail-item">
<text class="detail-label">商品描述</text>
<text class="detail-value">{{product.productDescription || '暂无描述'}}</text>
</view>
<view class="detail-item" wx:if="{{product.priceRangeText && product.priceRangeText !== '--'}}">
<text class="detail-label">价格范围</text>
<text class="detail-value">{{product.priceRangeText}}</text>
</view>
<view class="detail-item" wx:if="{{product.analysisReport}}">
<text class="detail-label">AI分析报告</text>
<text class="detail-value">{{product.analysisReport}}</text>
</view>
<view class="detail-item">
<text class="detail-label">交易方式</text>
<text class="detail-value">{{product.transactionMethod || '面交'}}</text>
</view>
</view>
<!-- 卖家信息 -->
<view class="seller-section">
<view class="section-title">卖家信息</view>
<view class="seller-card" bindtap="onContactSeller">
<image class="seller-avatar" src="{{product.sellerInfo.avatar}}" mode="aspectFill" />
<view class="seller-info">
<text class="seller-name">{{product.sellerInfo.name}}</text>
<text class="seller-sno" wx:if="{{product.sellerInfo.sno}}">学号:{{product.sellerInfo.sno}}</text>
<text class="seller-time">发布于 {{product.publishTime}}</text>
</view>
<view class="contact-btn">
<text>联系</text>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 底部操作栏 -->
<view class="bottom-bar" wx:if="{{!loading}}">
<view class="bar-left">
<view class="icon-btn" bindtap="onViewCart">
<text class="icon">🛒</text>
<text class="badge" wx:if="{{cartCount > 0}}">{{cartCount}}</text>
</view>
<view class="icon-btn" bindtap="onToggleFavorite">
<text class="icon">{{isFavorite ? '❤️' : '🤍'}}</text>
</view>
</view>
<view class="bar-right">
<button
class="cart-btn {{inCart ? 'added' : ''}}"
bindtap="onAddToCart"
disabled="{{product.status !== '在售' || inCart}}"
>
{{inCart ? '已在购物车' : '加入购物车'}}
</button>
<button
class="buy-btn"
bindtap="onBuyNow"
disabled="{{product.status !== '在售'}}"
>
立即购买
</button>
</view>
</view>
<!-- 加载状态 -->
<view class="loading-container" wx:if="{{loading}}">
<view class="loading-content">
<text class="loading-text">加载中...</text>
</view>
</view>

@ -0,0 +1,511 @@
/* pages/product-detail/product-detail.wxss */
page {
height: 100%;
}
.scroll-container {
height: calc(100vh - env(safe-area-inset-top));
padding-top: 0;
padding-bottom: calc(150rpx + env(safe-area-inset-bottom));
}
.container {
width: 100%;
padding-top: 0;
padding-bottom: 40rpx;
background: linear-gradient(to bottom, #f8f9fa 0%, #f5f5f5 100%);
box-sizing: border-box;
}
/* 图片轮播区域 */
.image-section {
position: relative;
width: 100%;
height: auto;
min-height: 500rpx;
margin-top: 0;
padding-top: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
overflow: hidden;
}
.detail_swiper {
width: 100%;
height: 100%;
}
.swiper-item {
width: 100%;
height: auto;
min-height: 500rpx;
}
.image-wrapper {
width: 100%;
min-height: 500rpx;
display: flex;
align-items: flex-start;
justify-content: center;
background-color: #f8f9fa;
overflow: hidden;
box-sizing: border-box;
}
.slide-image {
width: 100%;
height: auto;
display: block;
min-height: 500rpx;
}
.no-image-placeholder {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
}
.placeholder-text {
font-size: 28rpx;
color: #999;
}
.status-badge {
position: absolute;
top: 30rpx;
right: 30rpx;
background: linear-gradient(135deg, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.5) 100%);
backdrop-filter: blur(10rpx);
color: #fff;
padding: 12rpx 24rpx;
border-radius: 40rpx;
font-size: 24rpx;
font-weight: 500;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.2);
z-index: 10;
}
/* 商品基本信息 */
.info-section {
width: 100%;
box-sizing: border-box;
background: linear-gradient(to bottom, #fff 0%, #fafafa 100%);
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 20rpx rgba(0, 0, 0, 0.04);
border-radius: 0;
}
.price-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
padding-bottom: 24rpx;
border-bottom: 1rpx solid rgba(0, 0, 0, 0.05);
}
.price-group {
display: flex;
align-items: baseline;
gap: 20rpx;
}
.current-price {
font-size: 56rpx;
font-weight: 700;
background: linear-gradient(135deg, #FF6B6B 0%, #FF4444 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
letter-spacing: -1rpx;
}
.original-price {
font-size: 28rpx;
color: #bbb;
text-decoration: line-through;
font-weight: 400;
}
.favorite-btn {
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #ffeef0 0%, #ffe0e6 100%);
border-radius: 50%;
font-size: 40rpx;
transition: transform 0.2s ease;
}
.favorite-btn:active {
transform: scale(0.95);
}
.product-title {
font-size: 36rpx;
font-weight: 600;
color: #1a1a1a;
line-height: 1.6;
margin-bottom: 24rpx;
letter-spacing: 0.5rpx;
width: 100%;
box-sizing: border-box;
word-break: break-all;
overflow-wrap: break-word;
}
.product-tags {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
.tag {
padding: 10rpx 20rpx;
border-radius: 30rpx;
font-size: 24rpx;
font-weight: 500;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
transition: transform 0.2s ease;
}
.tag:active {
transform: scale(0.95);
}
.category-tag {
background: linear-gradient(135deg, #E3F2FD 0%, #BBDEFB 100%);
color: #1565C0;
border: 1rpx solid rgba(21, 101, 192, 0.2);
}
.condition-tag {
background: linear-gradient(135deg, #FFF3E0 0%, #FFE0B2 100%);
color: #E65100;
border: 1rpx solid rgba(230, 81, 0, 0.2);
}
.score-tag {
background: linear-gradient(135deg, #F3E5F5 0%, #E1BEE7 100%);
color: #6A1B9A;
border: 1rpx solid rgba(106, 27, 154, 0.2);
}
/* 详细信息区域 */
.detail-section {
width: 100%;
box-sizing: border-box;
background: #fff;
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 20rpx rgba(0, 0, 0, 0.04);
border-radius: 0;
}
.section-title {
font-size: 34rpx;
font-weight: 600;
color: #1a1a1a;
margin-bottom: 30rpx;
padding-bottom: 20rpx;
border-bottom: 2rpx solid #f0f0f0;
position: relative;
}
.section-title::after {
content: '';
position: absolute;
bottom: -2rpx;
left: 0;
width: 60rpx;
height: 4rpx;
background: linear-gradient(90deg, #4285F4 0%, #764ba2 100%);
border-radius: 2rpx;
}
.detail-item {
width: 100%;
box-sizing: border-box;
margin-bottom: 30rpx;
padding: 20rpx;
background: #fafafa;
border-radius: 16rpx;
transition: background-color 0.2s ease;
min-height: 80rpx;
}
.detail-item:last-child {
margin-bottom: 0;
}
.detail-label {
font-size: 26rpx;
color: #888;
display: block;
margin-bottom: 12rpx;
font-weight: 500;
letter-spacing: 0.5rpx;
}
.detail-value {
font-size: 30rpx;
color: #333;
line-height: 1.8;
display: block;
word-break: break-all;
width: 100%;
box-sizing: border-box;
overflow-wrap: break-word;
}
/* 卖家信息区域 */
.seller-section {
width: 100%;
box-sizing: border-box;
background: #fff;
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 20rpx rgba(0, 0, 0, 0.04);
border-radius: 0;
}
.seller-card {
width: 100%;
box-sizing: border-box;
display: flex;
align-items: center;
padding: 30rpx;
background: linear-gradient(135deg, #f8f9ff 0%, #f0f4ff 100%);
border-radius: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(66, 133, 244, 0.1);
transition: transform 0.2s ease, box-shadow 0.2s ease;
min-height: 140rpx;
}
.seller-card:active {
transform: translateY(-2rpx);
box-shadow: 0 6rpx 20rpx rgba(66, 133, 244, 0.15);
}
.seller-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
margin-right: 24rpx;
border: 3rpx solid #fff;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}
.seller-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 10rpx;
overflow: hidden;
}
.seller-name {
font-size: 30rpx;
font-weight: 600;
color: #1a1a1a;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
margin-bottom: 4rpx;
}
.seller-sno {
font-size: 24rpx;
color: #666;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
margin-bottom: 4rpx;
}
.seller-time {
font-size: 24rpx;
color: #999;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
.contact-btn {
flex-shrink: 0;
padding: 14rpx 28rpx;
background: linear-gradient(135deg, #4285F4 0%, #667eea 100%);
color: #fff;
border-radius: 50rpx;
font-size: 26rpx;
font-weight: 500;
box-shadow: 0 4rpx 12rpx rgba(66, 133, 244, 0.3);
transition: transform 0.2s ease, box-shadow 0.2s ease;
white-space: nowrap;
}
.contact-btn:active {
transform: scale(0.95);
box-shadow: 0 2rpx 8rpx rgba(66, 133, 244, 0.25);
}
/* 底部操作栏 */
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(to top, #fff 0%, #fafafa 100%);
padding: 24rpx 30rpx;
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.08);
z-index: 100;
backdrop-filter: blur(20rpx);
height: calc(136rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.bar-left {
display: flex;
gap: 24rpx;
}
.icon-btn {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 88rpx;
height: 88rpx;
font-size: 44rpx;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-radius: 50%;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
transition: transform 0.2s ease;
}
.icon-btn:active {
transform: scale(0.9);
}
.badge {
position: absolute;
top: -4rpx;
right: -4rpx;
background: linear-gradient(135deg, #FF6B6B 0%, #FF4444 100%);
color: #fff;
font-size: 20rpx;
font-weight: 600;
padding: 4rpx 10rpx;
border-radius: 20rpx;
min-width: 32rpx;
text-align: center;
line-height: 1.2;
box-shadow: 0 2rpx 8rpx rgba(255, 68, 68, 0.4);
border: 2rpx solid #fff;
}
.bar-right {
display: flex;
gap: 16rpx;
flex: 1;
justify-content: flex-end;
}
.cart-btn,
.buy-btn {
padding: 20rpx 32rpx;
border-radius: 50rpx;
font-size: 28rpx;
font-weight: 600;
border: none;
flex: 1;
max-width: 240rpx;
transition: all 0.3s ease;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
white-space: nowrap;
}
.cart-btn {
background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%);
color: #4285F4;
border: 2rpx solid #4285F4;
box-shadow: 0 4rpx 12rpx rgba(66, 133, 244, 0.2);
}
.cart-btn:active {
transform: scale(0.95);
box-shadow: 0 2rpx 8rpx rgba(66, 133, 244, 0.15);
}
.cart-btn.added {
background: linear-gradient(135deg, #f0f0f0 0%, #e0e0e0 100%);
color: #999;
border-color: #d0d0d0;
box-shadow: none;
}
.buy-btn {
background: linear-gradient(135deg, #4285F4 0%, #667eea 100%);
color: #fff;
box-shadow: 0 4rpx 16rpx rgba(66, 133, 244, 0.4);
}
.buy-btn:active {
transform: scale(0.95);
box-shadow: 0 2rpx 10rpx rgba(66, 133, 244, 0.3);
}
.buy-btn[disabled],
.cart-btn[disabled] {
background: linear-gradient(135deg, #e0e0e0 0%, #d0d0d0 100%);
color: #bbb;
border-color: #d0d0d0;
box-shadow: none;
opacity: 0.6;
}
/* 加载状态 */
.loading-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: linear-gradient(to bottom, #f8f9fa 0%, #f5f5f5 100%);
}
.loading-content {
text-align: center;
padding: 40rpx;
}
.loading-text {
font-size: 28rpx;
color: #999;
margin-top: 20rpx;
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}

@ -62,11 +62,9 @@ Page({
* 我的商品点击事件
*/
onMyProducts() {
wx.showToast({
title: '跳转到我的商品',
icon: 'none'
wx.navigateTo({
url: '/pages/myProducts/myProducts'
});
// 这里可以跳转到我的商品页面
},
/**
@ -84,9 +82,8 @@ Page({
* 我的订单点击事件
*/
onMyOrders() {
wx.showToast({
title: '跳转到我的订单',
icon: 'none'
wx.navigateTo({
url: '/pages/orders/orders'
});
// 这里可以跳转到我的订单页面
},

@ -46,25 +46,56 @@ Page({
// 从页面参数中获取数据
if (options && Object.keys(options).length > 0) {
// 解码URL参数小程序路由参数会自动编码需要手动解码
const decodeParam = (value) => {
if (!value) return '';
try {
return decodeURIComponent(value);
} catch (e) {
console.warn('参数解码失败:', value, e);
return value;
}
};
// 解码所有参数
const decodedOptions = {};
Object.keys(options).forEach(key => {
decodedOptions[key] = decodeParam(options[key]);
});
console.log('解码后的参数:', decodedOptions);
// 从AI定价页面跳转带完整参数
const receivedCategory = decodedOptions.productCategory || '';
// 查找类别在列表中的索引
let categoryIndex = 0;
if (receivedCategory) {
const index = this.data.categories.indexOf(receivedCategory);
if (index >= 0) {
categoryIndex = index;
}
}
this.setData({
fromAIPricing: true,
productImage: options.imagePath || '',
productName: options.productName || '',
productCategory: options.productCategory || '',
productDescription: options.productDescription || '',
originalPrice: options.originalPrice || '',
suggestedPrice: options.suggestedPrice || '',
priceRange: options.priceRange || '',
conditionLevel: options.conditionLevel || '',
aiScore: options.aiScore || '',
analysisReport: options.analysisReport || ''
productImage: decodedOptions.imagePath || '',
productName: decodedOptions.productName || '',
productCategory: receivedCategory, // 保存类别名称,但用户需要自己选择
productDescription: decodedOptions.productDescription || '',
originalPrice: decodedOptions.originalPrice || '',
suggestedPrice: decodedOptions.suggestedPrice || '',
priceRange: decodedOptions.priceRange || '',
conditionLevel: decodedOptions.conditionLevel || '',
aiScore: decodedOptions.aiScore || '',
analysisReport: decodedOptions.analysisReport || '',
categoryIndex: categoryIndex // 设置类别选择器的索引
});
// 如果传递了建议价格,设置为默认售价
if (options.suggestedPrice) {
if (decodedOptions.suggestedPrice) {
this.setData({
salePrice: options.suggestedPrice
salePrice: decodedOptions.suggestedPrice
});
}
@ -81,7 +112,8 @@ Page({
productName: '',
productCategory: '',
productDescription: '',
salePrice: ''
salePrice: '',
categoryIndex: 0
});
// 显示直接发布的提示
@ -272,12 +304,18 @@ Page({
* 发布商品
*/
onPublish() {
// 防止重复提交:如果正在发布中,直接返回
if (this.data.isPublishing) {
console.log('正在发布中,忽略重复点击');
return;
}
// 验证数据
if (!this.validateForm()) {
return;
}
// 设置发布状态
// 设置发布状态(立即设置,防止重复点击)
this.setData({
isPublishing: true
});
@ -296,6 +334,12 @@ Page({
* 上传图片并发布商品
*/
uploadImageAndPublish() {
// 双重检查:防止并发调用
if (!this.data.isPublishing) {
console.warn('发布状态异常,取消操作');
return;
}
const { productImage, fromAIPricing } = this.data;
console.log('准备上传图片,图片路径:', productImage);
@ -324,6 +368,7 @@ Page({
icon: 'none',
duration: 3000
});
// 重置发布状态
this.setData({
isPublishing: false
});
@ -341,6 +386,12 @@ Page({
* 保存商品信息到数据库
*/
saveProductToDatabase(imageFileID) {
// 双重检查:防止并发调用
if (!this.data.isPublishing) {
console.warn('发布状态异常,取消保存操作');
return;
}
const db = wx.cloud.database();
const {
productName,
@ -396,36 +447,71 @@ Page({
};
console.log('准备保存商品数据:', productData);
console.log('商品名称:', productName);
console.log('商品描述:', productDescription);
console.log('商品类别:', productCategory);
console.log('成色:', conditionLevel);
console.log('价格范围:', priceRange);
// 检查是否有URL编码的字符如果还有编码字符说明解码失败
const hasEncodedChars = (str) => {
if (!str) return false;
return /%[0-9A-F]{2}/i.test(str);
};
if (hasEncodedChars(productName) || hasEncodedChars(productDescription) || hasEncodedChars(conditionLevel)) {
console.warn('检测到可能未解码的URL编码字符');
console.warn('productName:', productName);
console.warn('productDescription:', productDescription);
console.warn('conditionLevel:', conditionLevel);
}
// 保存到数据库
db.collection('T_product').add({
data: productData,
success: (res) => {
console.log('商品发布成功:', res);
// 先隐藏loading
wx.hideLoading();
// 立即重置发布状态,防止重复提交
this.setData({
isPublishing: false
});
// 显示发布成功提示
wx.showToast({
title: '发布成功',
icon: 'success',
duration: 2000
duration: 2000,
mask: true
});
// 延迟跳转到主页面
// 等待提示显示完整后再跳转到首页
setTimeout(() => {
wx.switchTab({
url: '/pages/main/main'
wx.reLaunch({
url: '/pages/main/main',
success: () => {
console.log('成功跳转到首页');
},
fail: (err) => {
console.error('跳转失败:', err);
// 如果reLaunch失败尝试使用navigateBack
wx.navigateBack({
delta: 999 // 返回首页(如果有多个页面)
});
}
});
}, 1500);
}, 2000);
},
fail: (err) => {
console.error('商品保存失败:', err);
// 先隐藏loading
wx.hideLoading();
// 重置发布状态
this.setData({
isPublishing: false
});

@ -37,7 +37,6 @@
<view class="info-group">
<view class="info-item">
<text class="info-label">商品名称</text>
<text class="info-value" wx:if="{{fromAIPricing}}">{{productName}}</text>
<input
type="text"
placeholder="请输入商品名称"
@ -45,18 +44,15 @@
bindinput="onProductNameInput"
class="info-input"
placeholder-class="placeholder"
wx:else
/>
</view>
<view class="info-item">
<text class="info-label">商品类别</text>
<text class="info-value" wx:if="{{fromAIPricing}}">{{productCategory}}</text>
<picker
range="{{categories}}"
value="{{categoryIndex}}"
bindchange="onCategoryChange"
class="category-picker"
wx:else
>
<view class="picker-display">
<text class="picker-text">{{categories[categoryIndex] || '请选择商品类别'}}</text>
@ -66,7 +62,6 @@
</view>
<view class="info-item">
<text class="info-label">商品描述</text>
<text class="info-value description-text" wx:if="{{fromAIPricing}}">{{productDescription}}</text>
<textarea
placeholder="请输入商品描述"
value="{{productDescription}}"
@ -75,7 +70,6 @@
placeholder-class="placeholder"
maxlength="200"
auto-height
wx:else
/>
</view>
</view>
@ -181,11 +175,12 @@
<view class="publish-actions">
<button
class="publish-btn {{isPublishing ? 'disabled' : ''}}"
bindtap="onPublish"
catchtap="onPublish"
disabled="{{isPublishing}}"
loading="{{isPublishing}}"
>
{{isPublishing ? '发布中...' : '确认发布'}}
</button>
<button class="back-btn" bindtap="onBack">返回修改</button>
<button class="back-btn" bindtap="onBack" disabled="{{isPublishing}}">返回修改</button>
</view>
</view>

@ -38,7 +38,30 @@ Page({
*/
onLoad(options) {
console.log('求购页面加载', options);
// 可以在这里初始化一些数据,比如从缓存中读取用户信息
// 确保云开发已初始化
if (!wx.cloud) {
wx.showModal({
title: '初始化失败',
content: '请使用 2.2.3 或以上的基础库以使用云能力',
showCancel: false
});
return;
}
// 尝试初始化云开发(如果还未初始化)
try {
const app = getApp();
if (app && app.globalData && app.globalData.env) {
wx.cloud.init({
env: app.globalData.env,
traceUser: true
});
console.log('云开发初始化成功');
}
} catch (err) {
console.error('云开发初始化失败:', err);
}
},
/**
@ -213,53 +236,129 @@ Page({
isPublishing: true
});
// 模拟发布过程
wx.showLoading({
title: '发布中...'
title: '发布中...',
mask: true
});
// 构建求购数据
const purchaseData = {
// 基本信息
productName: this.data.productName,
category: this.data.categories[this.data.categoryIndex],
description: this.data.purchaseDescription,
expectedPrice: this.data.expectedPrice,
condition: this.data.conditionLevels[this.data.conditionIndex],
brand: this.data.expectedBrand,
model: this.data.expectedModel,
expectedDate: this.data.expectedDate,
validity: this.data.validityPeriods[this.data.validityIndex],
productCategory: this.data.categories[this.data.categoryIndex],
description: this.data.purchaseDescription || '',
// 求购要求
expectedPrice: this.data.expectedPrice ? parseFloat(this.data.expectedPrice) : 0,
conditionLevel: this.data.conditionLevels[this.data.conditionIndex],
expectedBrand: this.data.expectedBrand || '',
expectedModel: this.data.expectedModel || '',
// 时间信息
expectedDate: this.data.expectedDate || '',
validityPeriod: this.data.validityPeriods[this.data.validityIndex],
// 联系方式
contactName: this.data.contactName,
contactPhone: this.data.contactPhone,
wechat: this.data.contactWechat,
tradeMethod: this.data.tradeMethods[this.data.tradeMethodIndex],
publishTime: new Date().toISOString(),
status: 'active'
contactWechat: this.data.contactWechat || '',
// 交易方式
transactionMethod: this.data.tradeMethods[this.data.tradeMethodIndex],
// 状态和时间
status: 'active',
viewCount: 0,
responseCount: 0,
createTime: new Date(),
updateTime: new Date()
};
console.log('发布求购数据:', purchaseData);
// 模拟网络请求
setTimeout(() => {
// 检查云开发是否可用
if (!wx.cloud) {
wx.hideLoading();
// 发布成功
wx.showToast({
title: '发布成功!',
icon: 'success',
duration: 2000
wx.showModal({
title: '功能不可用',
content: '云开发功能未初始化,请检查网络连接或重新打开小程序',
showCancel: false,
confirmText: '知道了'
});
this.setData({
isPublishing: false
});
return;
}
// 保存到云数据库 T_want
try {
const db = wx.cloud.database();
// 发布成功后返回上一页或跳转到求购列表
setTimeout(() => {
wx.navigateBack();
}, 1500);
}, 2000);
db.collection('T_want').add({
data: purchaseData,
success: (res) => {
console.log('求购发布成功数据库ID:', res._id);
wx.hideLoading();
// 发布成功提示
wx.showToast({
title: '发布成功!',
icon: 'success',
duration: 2000,
mask: true
});
this.setData({
isPublishing: false
});
// 发布成功后返回上一页或跳转到求购列表
setTimeout(() => {
wx.navigateBack();
}, 2000);
},
fail: (err) => {
console.error('求购发布失败:', err);
console.error('错误详情:', JSON.stringify(err, null, 2));
wx.hideLoading();
let errorMsg = '发布求购失败,请重试。';
if (err.errMsg) {
if (err.errMsg.includes('Failed to fetch') || err.errMsg.includes('network')) {
errorMsg = '网络连接失败,请检查网络后重试。';
} else if (err.errMsg.includes('permission')) {
errorMsg = '没有权限访问数据库,请联系管理员。';
} else {
errorMsg = '错误信息:' + err.errMsg;
}
}
wx.showModal({
title: '发布失败',
content: errorMsg,
showCancel: false,
confirmText: '知道了'
});
this.setData({
isPublishing: false
});
}
});
} catch (error) {
console.error('数据库操作异常:', error);
wx.hideLoading();
wx.showModal({
title: '操作失败',
content: '数据库操作异常,请重试。\n错误' + (error.message || '未知错误'),
showCancel: false,
confirmText: '知道了'
});
this.setData({
isPublishing: false
});
}
},
/**

@ -1,10 +1,6 @@
<!-- 发布求购页面 -->
<view class="purchase-container">
<!-- 页面标题 -->
<view class="page-header">
<text class="page-title">发布求购</text>
<text class="page-subtitle">发布您的求购需求,让卖家主动联系您</text>
</view>
<!-- 求购商品信息 -->
<view class="section">
@ -209,11 +205,12 @@
<view class="publish-actions">
<button
class="publish-btn {{isPublishing ? 'disabled' : ''}}"
bindtap="onPublish"
catchtap="onPublish"
disabled="{{isPublishing}}"
loading="{{isPublishing}}"
>
{{isPublishing ? '发布中...' : '确认发布求购'}}
</button>
<button class="back-btn" bindtap="onBack">取消</button>
<button class="back-btn" bindtap="onBack" disabled="{{isPublishing}}">取消</button>
</view>
</view>

@ -336,7 +336,7 @@ Page({
// 跳转到兴趣爱好引导页面
setTimeout(() => {
wx.navigateTo({
wx.redirectTo({
url: '/pages/interests/interests'
});
}, 1500);

@ -0,0 +1,339 @@
// pages/wanted-list/wanted-list.js
Page({
/**
* 页面的初始数据
*/
data: {
wantedList: [],
searchText: '',
loading: false,
refreshing: false,
hasMore: true,
pageSize: 20,
currentPage: 0,
selectedWantedId: null // 用于详情页
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
console.log('求购列表页面加载', options);
// 如果传入了id说明是从热门求购点击进入的先加载该条目的详情
if (options.id) {
this.setData({
selectedWantedId: options.id
});
}
this.loadWantedList();
},
/**
* 搜索输入
*/
onSearchInput(e) {
this.setData({
searchText: e.detail.value
});
},
/**
* 搜索确认
*/
onSearch() {
this.setData({
currentPage: 0,
wantedList: [],
hasMore: true
});
this.loadWantedList();
},
/**
* 加载求购列表
*/
async loadWantedList() {
if (this.data.loading) return;
this.setData({
loading: true
});
try {
const db = wx.cloud.database();
const _ = db.command;
// 构建查询条件
let query = db.collection('T_want').where({
status: 'active'
});
// 如果有搜索关键词,获取更多数据以便筛选;否则正常分页
const limit = this.data.searchText.trim() ? 100 : (this.data.pageSize * 2);
const skip = this.data.searchText.trim() ? 0 : (this.data.currentPage * this.data.pageSize);
// 查询数据按点击量降序排序微信云数据库不支持多个orderBy
const result = await query
.orderBy('viewCount', 'desc')
.skip(skip)
.limit(limit)
.get();
console.log('求购列表查询结果:', result);
if (result.data && result.data.length > 0) {
// 如果有搜索关键词,先进行筛选
let filteredData = result.data;
if (this.data.searchText.trim()) {
const searchText = this.data.searchText.trim().toLowerCase();
filteredData = result.data.filter(item => {
const name = (item.productName || '').toLowerCase();
const desc = (item.description || '').toLowerCase();
const category = (item.productCategory || '').toLowerCase();
return name.includes(searchText) || desc.includes(searchText) || category.includes(searchText);
});
}
// 在客户端进行排序:先按点击量降序,点击量相同则按时间降序(时间更晚的在前)
const sortedData = filteredData.sort((a, b) => {
const viewCountA = a.viewCount || 0;
const viewCountB = b.viewCount || 0;
// 如果点击量不同,按点击量降序
if (viewCountA !== viewCountB) {
return viewCountB - viewCountA;
}
// 点击量相同,按时间降序(时间更晚的在前)
const timeA = new Date(a.createTime).getTime();
const timeB = new Date(b.createTime).getTime();
return timeB - timeA;
});
// 限制返回数量
const limitedData = sortedData.slice(0, this.data.pageSize);
const wantedList = limitedData.map(item => ({
...item,
formattedTime: this.formatTime(item.createTime)
}));
// 判断是否还有更多数据
// 如果有搜索关键词,不显示"加载更多"(因为已经获取了所有数据)
// 如果没有搜索关键词,判断是否还有更多数据
const hasMoreData = !this.data.searchText.trim() && result.data.length === limit;
// 如果有搜索关键词,只显示筛选后的数据;否则追加数据
const finalList = this.data.searchText.trim()
? wantedList
: (this.data.currentPage === 0 ? wantedList : [...this.data.wantedList, ...wantedList]);
this.setData({
wantedList: finalList,
hasMore: hasMoreData && limitedData.length > 0,
loading: false,
refreshing: false
});
// 如果传入了id滚动到对应位置
if (this.data.selectedWantedId) {
wx.nextTick(() => {
const index = this.data.wantedList.findIndex(item => item._id === this.data.selectedWantedId);
if (index >= 0) {
// 可以添加滚动到指定位置的逻辑
}
});
}
} else {
this.setData({
hasMore: false,
loading: false,
refreshing: false
});
if (this.data.currentPage === 0) {
this.setData({
wantedList: []
});
}
}
} catch (err) {
console.error('加载求购列表失败:', err);
wx.showToast({
title: '加载失败',
icon: 'none'
});
this.setData({
loading: false,
refreshing: false
});
}
},
/**
* 格式化时间
*/
formatTime(date) {
if (!date) return '';
const d = new Date(date);
const now = new Date();
const diff = now - d;
const minute = 60 * 1000;
const hour = 60 * minute;
const day = 24 * hour;
if (diff < minute) {
return '刚刚';
} else if (diff < hour) {
return Math.floor(diff / minute) + '分钟前';
} else if (diff < day) {
return Math.floor(diff / hour) + '小时前';
} else if (diff < 7 * day) {
return Math.floor(diff / day) + '天前';
} else {
return d.getMonth() + 1 + '月' + d.getDate() + '日';
}
},
/**
* 求购点击事件增加点击量并显示详情
*/
async onWantedTap(e) {
const wantedId = e.currentTarget.dataset.id;
// 增加点击量
try {
const db = wx.cloud.database();
const _ = db.command;
await db.collection('T_want').doc(wantedId).update({
data: {
viewCount: _.inc(1),
updateTime: new Date()
}
});
// 更新本地列表中的点击量
const updatedList = this.data.wantedList.map(item => {
if (item._id === wantedId) {
return {
...item,
viewCount: (item.viewCount || 0) + 1
};
}
return item;
});
this.setData({
wantedList: updatedList
});
} catch (err) {
console.error('更新点击量失败:', err);
}
// 显示求购详情
const wanted = this.data.wantedList.find(w => w._id === wantedId);
if (wanted) {
this.showWantedDetail(wanted);
}
},
/**
* 显示求购详情
*/
showWantedDetail(wanted) {
let detailContent = `商品名称:${wanted.productName}\n`;
detailContent += `商品类别:${wanted.productCategory || '其他'}\n`;
if (wanted.description) {
detailContent += `求购描述:${wanted.description}\n`;
}
detailContent += `预算价格:¥${wanted.expectedPrice || 0}\n`;
if (wanted.conditionLevel) {
detailContent += `成色要求:${wanted.conditionLevel}\n`;
}
if (wanted.expectedBrand) {
detailContent += `期望品牌:${wanted.expectedBrand}\n`;
}
if (wanted.expectedModel) {
detailContent += `期望型号:${wanted.expectedModel}\n`;
}
if (wanted.validityPeriod) {
detailContent += `有效期:${wanted.validityPeriod}\n`;
}
detailContent += `联系人:${wanted.contactName}\n`;
detailContent += `联系方式:${wanted.contactPhone}\n`;
if (wanted.contactWechat) {
detailContent += `微信号:${wanted.contactWechat}\n`;
}
detailContent += `交易方式:${wanted.transactionMethod || '面交'}\n`;
detailContent += `浏览量:${wanted.viewCount || 0}`;
wx.showModal({
title: '求购详情',
content: detailContent,
showCancel: true,
confirmText: '联系TA',
cancelText: '关闭',
success: (res) => {
if (res.confirm) {
// 复制联系方式
const contactInfo = wanted.contactPhone || wanted.contactWechat || '';
if (contactInfo) {
wx.setClipboardData({
data: contactInfo,
success: () => {
wx.showToast({
title: '已复制联系方式',
icon: 'success'
});
}
});
}
}
}
});
},
/**
* 下拉刷新
*/
onRefresh() {
this.setData({
currentPage: 0,
wantedList: [],
hasMore: true,
refreshing: true
});
this.loadWantedList();
},
/**
* 加载更多
*/
onLoadMore() {
if (!this.data.hasMore || this.data.loading) return;
this.setData({
currentPage: this.data.currentPage + 1
});
this.loadWantedList();
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
// 如果从其他页面返回,刷新列表
if (this.data.wantedList.length > 0) {
this.setData({
currentPage: 0,
wantedList: [],
hasMore: true
});
this.loadWantedList();
}
}
})

@ -0,0 +1,6 @@
{
"usingComponents": {},
"navigationBarTitleText": "求购列表",
"enablePullDownRefresh": true
}

@ -0,0 +1,89 @@
<!--pages/wanted-list/wanted-list.wxml-->
<view class="page-container">
<!-- 搜索栏 -->
<view class="search-section">
<view class="search-bar">
<image class="search-icon" src="/images/search.png" mode="aspectFit"></image>
<input
class="search-input"
placeholder="搜索求购信息"
value="{{searchText}}"
bindinput="onSearchInput"
bindconfirm="onSearch"
/>
</view>
</view>
<!-- 求购列表 -->
<scroll-view
class="wanted-list-container"
scroll-y="true"
refresher-enabled="{{true}}"
refresher-triggered="{{refreshing}}"
bindrefresherrefresh="onRefresh"
bindscrolltolower="onLoadMore"
>
<view class="wanted-list">
<view
class="wanted-card"
wx:for="{{wantedList}}"
wx:key="_id"
bindtap="onWantedTap"
data-id="{{item._id}}"
>
<view class="card-header">
<text class="wanted-title">{{item.productName}}</text>
<text class="wanted-category">{{item.productCategory}}</text>
</view>
<text class="wanted-description" wx:if="{{item.description}}">{{item.description}}</text>
<view class="wanted-details">
<view class="detail-row">
<text class="detail-label">预算价格:</text>
<text class="detail-value price">¥{{item.expectedPrice || 0}}</text>
</view>
<view class="detail-row" wx:if="{{item.conditionLevel}}">
<text class="detail-label">成色要求:</text>
<text class="detail-value">{{item.conditionLevel}}</text>
</view>
<view class="detail-row" wx:if="{{item.expectedBrand}}">
<text class="detail-label">期望品牌:</text>
<text class="detail-value">{{item.expectedBrand}}</text>
</view>
</view>
<view class="card-footer">
<view class="footer-left">
<text class="wanted-time">{{item.formattedTime}}</text>
<text class="wanted-views">👁 {{item.viewCount || 0}}</text>
</view>
<view class="contact-info">
<text class="contact-name">{{item.contactName}}</text>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" wx:if="{{wantedList.length === 0 && !loading}}">
<text class="empty-text">暂无求购信息</text>
</view>
<!-- 加载更多 -->
<view class="load-more" wx:if="{{hasMore && !loading}}">
<text>加载更多...</text>
</view>
<!-- 没有更多 -->
<view class="no-more" wx:if="{{!hasMore && wantedList.length > 0}}">
<text>没有更多了</text>
</view>
</view>
</scroll-view>
<!-- 加载中 -->
<view class="loading-container" wx:if="{{loading && wantedList.length === 0}}">
<text class="loading-text">加载中...</text>
</view>
</view>

@ -0,0 +1,192 @@
/* pages/wanted-list/wanted-list.wxss */
.page-container {
min-height: 100vh;
background: linear-gradient(to bottom, #f8f9fa 0%, #f5f5f5 100%);
}
.search-section {
background: white;
padding: 20rpx 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.search-bar {
display: flex;
align-items: center;
background: #f5f5f5;
border-radius: 50rpx;
padding: 16rpx 24rpx;
}
.search-icon {
width: 32rpx;
height: 32rpx;
margin-right: 16rpx;
}
.search-input {
flex: 1;
font-size: 28rpx;
color: #333;
}
.wanted-list-container {
height: calc(100vh - 120rpx);
padding: 20rpx;
box-sizing: border-box;
}
.wanted-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.wanted-card {
background: white;
border-radius: 20rpx;
padding: 30rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.wanted-card:active {
transform: translateY(-2rpx);
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.12);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.wanted-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
flex: 1;
}
.wanted-category {
font-size: 24rpx;
color: #4285F4;
background: rgba(66, 133, 244, 0.1);
padding: 6rpx 16rpx;
border-radius: 20rpx;
}
.wanted-description {
font-size: 26rpx;
color: #666;
line-height: 1.6;
margin-bottom: 20rpx;
display: block;
}
.wanted-details {
margin-bottom: 20rpx;
padding: 20rpx;
background: #f8f9fa;
border-radius: 12rpx;
}
.detail-row {
display: flex;
align-items: center;
margin-bottom: 12rpx;
}
.detail-row:last-child {
margin-bottom: 0;
}
.detail-label {
font-size: 24rpx;
color: #999;
margin-right: 10rpx;
}
.detail-value {
font-size: 26rpx;
color: #333;
}
.detail-value.price {
color: #FF6B35;
font-weight: bold;
font-size: 28rpx;
}
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 20rpx;
border-top: 1rpx solid #f0f0f0;
}
.footer-left {
display: flex;
align-items: center;
gap: 20rpx;
}
.wanted-time {
font-size: 24rpx;
color: #999;
}
.wanted-views {
font-size: 24rpx;
color: #666;
}
.contact-info {
display: flex;
align-items: center;
}
.contact-name {
font-size: 24rpx;
color: #4285F4;
font-weight: 500;
}
.empty-state {
text-align: center;
padding: 100rpx 20rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
.load-more {
text-align: center;
padding: 30rpx;
color: #999;
font-size: 26rpx;
}
.no-more {
text-align: center;
padding: 30rpx;
color: #ccc;
font-size: 24rpx;
}
.loading-container {
display: flex;
justify-content: center;
align-items: center;
padding: 100rpx 20rpx;
}
.loading-text {
font-size: 28rpx;
color: #999;
}
Loading…
Cancel
Save