Compare commits
32 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
6eb320c202 | 2 hours ago |
|
|
03e6d805c0 | 2 hours ago |
|
|
426f56affa | 6 hours ago |
|
|
fdbd49a0ae | 8 hours ago |
|
|
71011970b9 | 24 hours ago |
|
|
297079020a | 24 hours ago |
|
|
263854cb0e | 1 day ago |
|
|
43b350a60f | 1 day ago |
|
|
4d8710fd00 | 2 days ago |
|
|
16e319c302 | 2 days ago |
|
|
5431341886 | 2 days ago |
|
|
f92b8b768e | 2 days ago |
|
|
b9c6c93599 | 2 days ago |
|
|
23e59cd082 | 2 days ago |
|
|
4709720d19 | 3 days ago |
|
|
715f007e89 | 3 days ago |
|
|
339c155ce6 | 3 days ago |
|
|
c6e7f507e0 | 4 days ago |
|
|
610c70ee1c | 4 days ago |
|
|
64e1b8cded | 4 days ago |
|
|
4b1461ed77 | 4 days ago |
|
|
f7358d2994 | 4 days ago |
|
|
bcf5df3d08 | 4 days ago |
|
|
f0202060b3 | 4 days ago |
|
|
4a6939d895 | 4 days ago |
|
|
43fbb57cd7 | 4 days ago |
|
|
0982db2b2e | 4 days ago |
|
|
3f844a2c43 | 4 days ago |
|
|
79aa3ef0a2 | 4 days ago |
|
|
1599530766 | 4 days ago |
|
|
8ca89d22a3 | 4 days ago |
|
|
6efd3fbd84 | 4 days ago |
34 changed files with 4057 additions and 6388 deletions
@ -1,552 +0,0 @@ |
|||||
Page({ |
|
||||
data: { |
|
||||
productName: '', |
|
||||
category: '', |
|
||||
specifications: [], |
|
||||
selectedSpec: '', |
|
||||
loading: false, |
|
||||
error: '', |
|
||||
navigating: false |
|
||||
}, |
|
||||
// 导航锁机制,防止多次点击导致多次跳转
|
|
||||
navigateLock: function(cb) { |
|
||||
if (this.data.navigating) { |
|
||||
return false; |
|
||||
} |
|
||||
this.setData({ navigating: true }); |
|
||||
cb(); |
|
||||
// 延迟重置导航锁,确保导航操作有足够时间完成
|
|
||||
setTimeout(() => { |
|
||||
this.setData({ navigating: false }); |
|
||||
}, 1000); |
|
||||
return true; |
|
||||
}, |
|
||||
onLoad(options) { |
|
||||
let productName = ''; |
|
||||
let category = ''; |
|
||||
// 首先检查URL参数
|
|
||||
if (options.productName) { |
|
||||
productName = options.productName; |
|
||||
} else { |
|
||||
// 然后检查本地存储(用于wx.switchTab导航)
|
|
||||
productName = wx.getStorageSync('selectedProductName') || ''; |
|
||||
// 清除本地存储中的商品名称,避免影响下次进入
|
|
||||
if (productName) { |
|
||||
wx.removeStorageSync('selectedProductName'); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 获取分类信息
|
|
||||
category = wx.getStorageSync('selectedCategory') || ''; |
|
||||
|
|
||||
if (productName) { |
|
||||
this.setData({ |
|
||||
productName: productName, |
|
||||
category: category |
|
||||
}); |
|
||||
this.loadSpecifications(productName); |
|
||||
} else { |
|
||||
// 如果没有商品名称参数,跳转到商品列表页面
|
|
||||
wx.redirectTo({ |
|
||||
url: '/pages/evaluate2/product-list' |
|
||||
}); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
loadSpecifications(productName) { |
|
||||
this.setData({ |
|
||||
loading: true, |
|
||||
specifications: [], |
|
||||
error: '' // 清空错误信息,确保加载时只显示加载状态
|
|
||||
}); |
|
||||
|
|
||||
// 尝试从本地存储获取商品规格映射数据
|
|
||||
const productSpecsMap = wx.getStorageSync('evaluate2ProductSpecsMap') || {}; |
|
||||
|
|
||||
if (productSpecsMap && productSpecsMap[productName]) { |
|
||||
console.log('从本地存储获取商品规格数据'); |
|
||||
|
|
||||
// 提取该商品的规格和价格信息
|
|
||||
const productSpecs = productSpecsMap[productName]; |
|
||||
|
|
||||
// 处理规格数据
|
|
||||
const specPriceMap = {}; |
|
||||
productSpecs.forEach(item => { |
|
||||
const specStr = item.specification; |
|
||||
const price = item.price; |
|
||||
|
|
||||
// 价格为空的不参与计算
|
|
||||
if (!price || price.trim() === '') { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (specStr.length > 0) { |
|
||||
// 处理逗号分隔的多个规格
|
|
||||
let specs = specStr.split(',').map(spec => spec.trim()).filter(spec => spec.length > 0); |
|
||||
|
|
||||
// 进一步处理规格,确保每个规格都是独立的
|
|
||||
const processedSpecs = []; |
|
||||
specs.forEach(spec => { |
|
||||
if (spec.includes(',')) { |
|
||||
const subSpecs = spec.split(',').map(s => s.trim()).filter(s => s.length > 0); |
|
||||
processedSpecs.push(...subSpecs); |
|
||||
} else { |
|
||||
processedSpecs.push(spec); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
specs = processedSpecs; |
|
||||
|
|
||||
// 处理逗号分隔的多个价格
|
|
||||
const prices = price.split(',').map(p => p.trim()).filter(p => p && p.trim() !== ''); |
|
||||
|
|
||||
// 价格为空的不参与计算
|
|
||||
if (prices.length === 0) { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 将规格和价格配对
|
|
||||
specs.forEach((spec, index) => { |
|
||||
if (spec.length > 0) { |
|
||||
// 确保价格索引不超出范围
|
|
||||
const priceIndex = index % prices.length; |
|
||||
const matchedPrice = prices[priceIndex] || ''; |
|
||||
|
|
||||
// 只有当价格不为空时才添加该规格
|
|
||||
if (matchedPrice && matchedPrice.trim() !== '') { |
|
||||
// 收集相同规格的所有价格,以便计算平均值
|
|
||||
if (!specPriceMap[spec]) { |
|
||||
specPriceMap[spec] = []; |
|
||||
} |
|
||||
specPriceMap[spec].push(matchedPrice); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 转换为规格对象数组
|
|
||||
const specifications = Object.keys(specPriceMap).map(spec => { |
|
||||
const prices = specPriceMap[spec]; |
|
||||
|
|
||||
// 解析规格
|
|
||||
const specInfo = this.parseSpecification(spec); |
|
||||
|
|
||||
// 处理每个价格
|
|
||||
const processedPrices = prices.map(price => { |
|
||||
if (!price || price.trim() === '' || isNaN(parseFloat(price))) { |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
const priceValue = parseFloat(price); |
|
||||
|
|
||||
// 价格<10的需要按照公式计算
|
|
||||
if (priceValue < 10 && specInfo) { |
|
||||
if (specInfo.type === '净重') { |
|
||||
// 净重:规格平均值 × 价格
|
|
||||
return specInfo.avg * priceValue; |
|
||||
} else if (specInfo.type === '毛重') { |
|
||||
// 毛重:(规格平均值 - 5) × 价格
|
|
||||
return (specInfo.avg - 5) * priceValue; |
|
||||
} |
|
||||
} |
|
||||
// 价格>=10的直接使用
|
|
||||
return priceValue; |
|
||||
}).filter(price => price > 0); // 过滤掉0值
|
|
||||
|
|
||||
// 计算处理后价格的平均值
|
|
||||
let finalPrice = 0; |
|
||||
let finalPriceText = ''; |
|
||||
|
|
||||
if (processedPrices.length > 0) { |
|
||||
const sum = processedPrices.reduce((acc, price) => acc + price, 0); |
|
||||
finalPrice = sum / processedPrices.length; |
|
||||
finalPriceText = finalPrice.toFixed(2); |
|
||||
} |
|
||||
|
|
||||
const specObj = { |
|
||||
name: spec, |
|
||||
price: finalPriceText, |
|
||||
priceText: finalPriceText, |
|
||||
finalPrice: finalPrice, |
|
||||
finalPriceText: finalPriceText |
|
||||
}; |
|
||||
|
|
||||
return specObj; |
|
||||
}); |
|
||||
|
|
||||
// 对规格进行排序
|
|
||||
specifications.sort((a, b) => { |
|
||||
// 解析两个规格
|
|
||||
const specA = this.parseSpecification(a.name); |
|
||||
const specB = this.parseSpecification(b.name); |
|
||||
|
|
||||
// 如果有一个规格解析失败,保持原顺序
|
|
||||
if (!specA || !specB) { |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
// 1. 按类型排序:净重在前,毛重在后
|
|
||||
if (specA.type !== specB.type) { |
|
||||
return specA.type === '净重' ? -1 : 1; |
|
||||
} |
|
||||
|
|
||||
// 2. 按规格最小值排序:从小到大
|
|
||||
return specA.min - specB.min; |
|
||||
}); |
|
||||
|
|
||||
this.setData({ |
|
||||
specifications: specifications, |
|
||||
error: '', // 清除之前的错误
|
|
||||
loading: false |
|
||||
}); |
|
||||
|
|
||||
// 结束下拉刷新
|
|
||||
wx.stopPullDownRefresh(); |
|
||||
} else { |
|
||||
// 如果本地存储中没有数据,尝试从本地存储获取原始商品数据
|
|
||||
const allProducts = wx.getStorageSync('allProducts') || []; |
|
||||
|
|
||||
if (allProducts.length > 0) { |
|
||||
console.log('从本地存储获取原始商品数据'); |
|
||||
this.processSpecifications(productName, allProducts); |
|
||||
} else { |
|
||||
// 如果本地没有数据,再请求服务器
|
|
||||
console.log('本地存储中没有数据,请求服务器获取商品数据'); |
|
||||
const api = require('../../utils/api'); |
|
||||
api.getProducts(1, 1000, 'all', '').then(result => { |
|
||||
// 从返回对象中提取products数组
|
|
||||
const products = result.products || []; |
|
||||
this.processSpecifications(productName, products); |
|
||||
}).catch(err => { |
|
||||
console.error('获取规格失败:', err); |
|
||||
this.setData({ |
|
||||
error: '获取规格失败,请稍后重试', |
|
||||
loading: false |
|
||||
}); |
|
||||
|
|
||||
// 结束下拉刷新
|
|
||||
wx.stopPullDownRefresh(); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
// 解析规格,提取类型(净重/毛重)和数值范围
|
|
||||
parseSpecification(spec) { |
|
||||
// 匹配 "净重X-Y" 或 "毛重X-Y" 格式
|
|
||||
const rangeMatch = spec.match(/(净重|毛重)(\d+)-(\d+)/); |
|
||||
if (rangeMatch) { |
|
||||
const type = rangeMatch[1]; // 净重或毛重
|
|
||||
const min = parseFloat(rangeMatch[2]); |
|
||||
const max = parseFloat(rangeMatch[3]); |
|
||||
const avg = (min + max) / 2; |
|
||||
return { |
|
||||
type: type, |
|
||||
min: min, |
|
||||
max: max, |
|
||||
avg: avg |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
// 匹配 "净重X+" 或 "毛重X+" 格式
|
|
||||
const plusMatch = spec.match(/(净重|毛重)(\d+)\+/); |
|
||||
if (plusMatch) { |
|
||||
const type = plusMatch[1]; // 净重或毛重
|
|
||||
const value = parseFloat(plusMatch[2]); |
|
||||
return { |
|
||||
type: type, |
|
||||
min: value, |
|
||||
max: value, |
|
||||
avg: value |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
// 匹配 "净重X" 或 "毛重X" 格式
|
|
||||
const singleMatch = spec.match(/(净重|毛重)(\d+)/); |
|
||||
if (singleMatch) { |
|
||||
const type = singleMatch[1]; // 净重或毛重
|
|
||||
const value = parseFloat(singleMatch[2]); |
|
||||
return { |
|
||||
type: type, |
|
||||
min: value, |
|
||||
max: value, |
|
||||
avg: value |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
return null; |
|
||||
}, |
|
||||
|
|
||||
processSpecifications(productName, products) { |
|
||||
console.log('处理的商品数据数量:', products.length); |
|
||||
console.log('当前处理的商品名称:', productName); |
|
||||
|
|
||||
// 检查products是否为空
|
|
||||
if (!products || products.length === 0) { |
|
||||
console.error('商品数据为空'); |
|
||||
this.setData({ |
|
||||
error: '商品数据为空', |
|
||||
loading: false |
|
||||
}); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 过滤出当前商品名称的所有商品
|
|
||||
const filteredProducts = products.filter(product => { |
|
||||
const match = product.productName === productName; |
|
||||
console.log('商品:', product.productName, '规格:', product.specification, '价格:', product.price, '匹配:', match); |
|
||||
return match; |
|
||||
}); |
|
||||
|
|
||||
console.log('过滤后的商品数量:', filteredProducts.length); |
|
||||
console.log('过滤后的商品详情:', filteredProducts); |
|
||||
|
|
||||
// 检查filteredProducts是否为空
|
|
||||
if (filteredProducts.length === 0) { |
|
||||
console.error('未找到商品名称为"' + productName + '"的商品'); |
|
||||
this.setData({ |
|
||||
error: '未找到对应商品名称的商品', |
|
||||
loading: false |
|
||||
}); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 提取规格和价格,处理可能的空值和空格
|
|
||||
const specPriceMap = {}; |
|
||||
filteredProducts.forEach((product, productIndex) => { |
|
||||
const specStr = (product.specification || product.spec || '').trim(); |
|
||||
const price = product.price || ''; |
|
||||
|
|
||||
console.log(`处理第${productIndex + 1}个商品: 规格字符串='${specStr}', 价格字符串='${price}'`); |
|
||||
|
|
||||
// 价格为空的不参与计算
|
|
||||
if (!price || price.trim() === '') { |
|
||||
console.log(`商品价格为空,跳过处理`); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (specStr.length > 0) { |
|
||||
// 处理逗号分隔的多个规格,确保每个规格都被正确分割
|
|
||||
// 首先按逗号分割
|
|
||||
let specs = specStr.split(',').map(spec => spec.trim()).filter(spec => spec.length > 0); |
|
||||
|
|
||||
// 进一步处理规格,确保每个规格都是独立的
|
|
||||
const processedSpecs = []; |
|
||||
specs.forEach(spec => { |
|
||||
// 检查规格是否包含多个规格(例如:"净重29-30,净重31-32")
|
|
||||
if (spec.includes(',')) { |
|
||||
// 按中文逗号分割
|
|
||||
const subSpecs = spec.split(',').map(s => s.trim()).filter(s => s.length > 0); |
|
||||
processedSpecs.push(...subSpecs); |
|
||||
} else { |
|
||||
processedSpecs.push(spec); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
specs = processedSpecs; |
|
||||
|
|
||||
// 处理逗号分隔的多个价格
|
|
||||
const prices = (price || '').split(',').map(p => p.trim()).filter(p => p && p.trim() !== ''); |
|
||||
|
|
||||
console.log(`规格数组:`, specs); |
|
||||
console.log(`价格数组:`, prices); |
|
||||
|
|
||||
// 价格为空的不参与计算
|
|
||||
if (prices.length === 0) { |
|
||||
console.log(`价格数组为空,跳过处理`); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 将规格和价格配对
|
|
||||
specs.forEach((spec, index) => { |
|
||||
if (spec.length > 0) { |
|
||||
// 确保价格索引不超出范围
|
|
||||
const priceIndex = index % prices.length; |
|
||||
const matchedPrice = prices[priceIndex] || ''; |
|
||||
console.log(`规格'${spec}' 配对价格: '${matchedPrice}'`); |
|
||||
|
|
||||
// 只有当价格不为空时才添加该规格
|
|
||||
if (matchedPrice && matchedPrice.trim() !== '') { |
|
||||
// 收集相同规格的所有价格,以便计算平均值
|
|
||||
if (!specPriceMap[spec]) { |
|
||||
specPriceMap[spec] = []; |
|
||||
} |
|
||||
specPriceMap[spec].push(matchedPrice); |
|
||||
} else { |
|
||||
console.log(`规格'${spec}' 价格为空,不添加`); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 转换为规格对象数组
|
|
||||
const specifications = Object.keys(specPriceMap).map(spec => { |
|
||||
const prices = specPriceMap[spec]; |
|
||||
console.log(`规格'${spec}' 的所有原始价格:`, prices); |
|
||||
|
|
||||
// 解析规格
|
|
||||
const specInfo = this.parseSpecification(spec); |
|
||||
|
|
||||
// 处理每个价格
|
|
||||
const processedPrices = prices.map(price => { |
|
||||
if (!price || price.trim() === '' || isNaN(parseFloat(price))) { |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
const priceValue = parseFloat(price); |
|
||||
console.log(`处理价格: ${priceValue}`); |
|
||||
|
|
||||
// 价格<10的需要按照公式计算
|
|
||||
if (priceValue < 10 && specInfo) { |
|
||||
console.log(`价格 ${priceValue} < 10,按照公式计算`); |
|
||||
if (specInfo.type === '净重') { |
|
||||
// 净重:规格平均值 × 价格
|
|
||||
return specInfo.avg * priceValue; |
|
||||
} else if (specInfo.type === '毛重') { |
|
||||
// 毛重:(规格平均值 - 5) × 价格
|
|
||||
return (specInfo.avg - 5) * priceValue; |
|
||||
} |
|
||||
} |
|
||||
// 价格>=10的直接使用
|
|
||||
return priceValue; |
|
||||
}).filter(price => price > 0); // 过滤掉0值
|
|
||||
|
|
||||
console.log(`规格'${spec}' 处理后的价格:`, processedPrices); |
|
||||
|
|
||||
// 计算处理后价格的平均值
|
|
||||
let finalPrice = 0; |
|
||||
let finalPriceText = ''; |
|
||||
|
|
||||
if (processedPrices.length > 0) { |
|
||||
const sum = processedPrices.reduce((acc, price) => acc + price, 0); |
|
||||
finalPrice = sum / processedPrices.length; |
|
||||
finalPriceText = finalPrice.toFixed(2); |
|
||||
console.log(`规格'${spec}' 处理后价格的平均值: ${finalPriceText} (基于 ${processedPrices.length} 个价格)`); |
|
||||
} else { |
|
||||
console.log(`规格'${spec}' 没有有效价格`); |
|
||||
} |
|
||||
|
|
||||
const specObj = { |
|
||||
name: spec, |
|
||||
price: finalPriceText, |
|
||||
priceText: finalPriceText, |
|
||||
finalPrice: finalPrice, |
|
||||
finalPriceText: finalPriceText |
|
||||
}; |
|
||||
|
|
||||
console.log('创建的规格对象:', specObj); |
|
||||
|
|
||||
return specObj; |
|
||||
}); |
|
||||
|
|
||||
console.log('提取的规格和价格:', specifications); |
|
||||
|
|
||||
// 对规格进行排序
|
|
||||
specifications.sort((a, b) => { |
|
||||
// 解析两个规格
|
|
||||
const specA = this.parseSpecification(a.name); |
|
||||
const specB = this.parseSpecification(b.name); |
|
||||
|
|
||||
// 如果有一个规格解析失败,保持原顺序
|
|
||||
if (!specA || !specB) { |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
// 1. 按类型排序:净重在前,毛重在后
|
|
||||
if (specA.type !== specB.type) { |
|
||||
return specA.type === '净重' ? -1 : 1; |
|
||||
} |
|
||||
|
|
||||
// 2. 按规格最小值排序:从小到大
|
|
||||
return specA.min - specB.min; |
|
||||
}); |
|
||||
|
|
||||
console.log('排序后的规格:', specifications); |
|
||||
|
|
||||
this.setData({ |
|
||||
specifications: specifications, |
|
||||
error: '', // 清除之前的错误
|
|
||||
loading: false |
|
||||
}); |
|
||||
|
|
||||
// 结束下拉刷新
|
|
||||
wx.stopPullDownRefresh(); |
|
||||
}, |
|
||||
|
|
||||
// 选择规格
|
|
||||
selectSpec(e) { |
|
||||
this.navigateLock(() => { |
|
||||
const specItem = e.currentTarget.dataset.spec; |
|
||||
this.setData({ |
|
||||
selectedSpec: specItem.name |
|
||||
}); |
|
||||
console.log('选择的规格项:', specItem); |
|
||||
console.log('传递的价格:', specItem.finalPriceText); |
|
||||
console.log('传递的分类:', this.data.category); |
|
||||
wx.navigateTo({ |
|
||||
url: `/pages/evaluate2/spec-detail?productName=${encodeURIComponent(this.data.productName)}&specification=${encodeURIComponent(specItem.name)}&price=${encodeURIComponent(specItem.finalPriceText)}&category=${encodeURIComponent(this.data.category)}` |
|
||||
}); |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 跳转到规格详情页面
|
|
||||
goToSpecDetail(e) { |
|
||||
this.selectSpec(e); |
|
||||
}, |
|
||||
|
|
||||
// 返回上一页
|
|
||||
goBack() { |
|
||||
this.navigateLock(() => { |
|
||||
wx.navigateBack(); |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 返回商品列表页面
|
|
||||
goBackToProductList() { |
|
||||
this.navigateLock(() => { |
|
||||
// 从本地存储中获取之前选择的分类
|
|
||||
const selectedCategory = wx.getStorageSync('selectedCategory') || ''; |
|
||||
console.log('返回商品列表页面,之前选择的分类:', selectedCategory); |
|
||||
|
|
||||
// 构建跳转URL,如果有分类参数就传递
|
|
||||
let url = '/pages/evaluate2/product-list'; |
|
||||
if (selectedCategory) { |
|
||||
url += '?category=' + encodeURIComponent(selectedCategory); |
|
||||
} |
|
||||
|
|
||||
wx.redirectTo({ |
|
||||
url: url |
|
||||
}); |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 下拉刷新
|
|
||||
onPullDownRefresh() { |
|
||||
console.log('开始下拉刷新'); |
|
||||
if (this.data.productName) { |
|
||||
this.loadSpecifications(this.data.productName); |
|
||||
} else { |
|
||||
wx.stopPullDownRefresh(); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
// 分享配置
|
|
||||
onShareAppMessage() { |
|
||||
return { |
|
||||
title: '专业的估价平台,专为估蛋而生', |
|
||||
imageUrl: 'https://my-supplier-photos.oss-cn-chengdu.aliyuncs.com/products/%E7%BD%97%E6%9B%BC%E7%81%B0/image/c00cbcbfd12d747b44621701aed2ae02.jpeg', |
|
||||
path: '/pages/evaluate2/index' |
|
||||
}; |
|
||||
}, |
|
||||
|
|
||||
// 朋友圈分享配置
|
|
||||
onShareTimeline() { |
|
||||
return { |
|
||||
title: '专业的估价平台,专为估蛋而生', |
|
||||
imageUrl: 'https://my-supplier-photos.oss-cn-chengdu.aliyuncs.com/products/%E7%BD%97%E6%9B%BC%E7%81%B0/image/c00cbcbfd12d747b44621701aed2ae02.jpeg', |
|
||||
query: '' |
|
||||
}; |
|
||||
} |
|
||||
}); |
|
||||
@ -1,5 +0,0 @@ |
|||||
{ |
|
||||
"usingComponents": {}, |
|
||||
"enablePullDownRefresh": true, |
|
||||
"backgroundTextStyle": "dark" |
|
||||
} |
|
||||
@ -1,108 +0,0 @@ |
|||||
<view class="container"> |
|
||||
<!-- 步骤指示器 --> |
|
||||
<view class="step-indicator"> |
|
||||
<view class="step-item active"> |
|
||||
<view class="step-number">1</view> |
|
||||
<text class="step-text">选择品种</text> |
|
||||
</view> |
|
||||
<view class="step-line active"></view> |
|
||||
<view class="step-item active"> |
|
||||
<view class="step-number">2</view> |
|
||||
<text class="step-text">选择产品</text> |
|
||||
</view> |
|
||||
<view class="step-line active"></view> |
|
||||
<view class="step-item active"> |
|
||||
<view class="step-number">3</view> |
|
||||
<text class="step-text">选择规格</text> |
|
||||
</view> |
|
||||
<view class="step-line"></view> |
|
||||
<view class="step-item"> |
|
||||
<view class="step-number">4</view> |
|
||||
<text class="step-text">查看估价</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 头部导航栏 --> |
|
||||
<view class="header"> |
|
||||
<view class="header-content"> |
|
||||
<button class="back-button" bindtap="goBackToProductList"> |
|
||||
<text class="back-icon" style="position: relative; left: -139rpx; top: 2rpx; width: 62rpx; display: block; box-sizing: border-box">←</text> |
|
||||
</button> |
|
||||
<text class="title">规格选择</text> |
|
||||
<view class="header-right"></view> <!-- 占位,保持标题居中 --> |
|
||||
</view> |
|
||||
</view> |
|
||||
<view class="content"> |
|
||||
<!-- 加载中状态 --> |
|
||||
<view wx:if="{{loading}}" class="loading"> |
|
||||
<view class="loading-spinner"></view> |
|
||||
<text class="loading-text">正在加载规格数据...</text> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 错误提示 --> |
|
||||
<view wx:if="{{error}}" class="error-card"> |
|
||||
<view class="error-icon">⚠️</view> |
|
||||
<text class="error-text">{{error}}</text> |
|
||||
<view class="button-group"> |
|
||||
<button bindtap="loadSpecifications" data-product="{{productName}}" class="btn-primary">重新加载</button> |
|
||||
<button bindtap="goBackToProductList" class="btn-secondary">返回商品列表</button> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 商品信息 --> |
|
||||
<view wx:else class="product-card"> |
|
||||
<view class="product-info-container"> |
|
||||
<view class="product-info-row"> |
|
||||
<view class="product-image-container"> |
|
||||
<image |
|
||||
src="../../images/OIP-C.png" |
|
||||
mode="aspectFill" |
|
||||
class="product-image" |
|
||||
/> |
|
||||
</view> |
|
||||
<view class="product-details"> |
|
||||
<text class="product-name">{{productName}}</text> |
|
||||
<text class="product-hint">请选择重量规格</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 规格列表 --> |
|
||||
<view wx:if="{{!loading && !error && specifications.length > 0}}" class="spec-section"> |
|
||||
<view class="section-header"> |
|
||||
<text class="section-title">可用规格</text> |
|
||||
<text class="section-count">({{specifications.length}})</text> |
|
||||
</view> |
|
||||
<view class="spec-list"> |
|
||||
<view |
|
||||
wx:for="{{specifications}}" |
|
||||
wx:key="item.name" |
|
||||
class="spec-item" |
|
||||
data-spec="{{item}}" |
|
||||
bindtap="selectSpec" |
|
||||
> |
|
||||
<view class="spec-info"> |
|
||||
<text class="spec-name">{{item.name}}</text> |
|
||||
</view> |
|
||||
<view class="spec-radio {{selectedSpec === item.name ? 'selected' : ''}}"> |
|
||||
<view wx:if="selectedSpec === item.name" class="radio-inner"></view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 暂无更多规格提示 --> |
|
||||
<view wx:if="{{specifications.length > 0}}" class="no-more"> |
|
||||
<text class="no-more-text">已经到底啦</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 无规格提示 --> |
|
||||
<view wx:if="{{!loading && !error && specifications.length === 0}}" class="empty-state"> |
|
||||
<view class="empty-icon">📋</view> |
|
||||
<text class="empty-text">该商品暂无可用规格</text> |
|
||||
<text class="empty-subtext">试试其他商品吧</text> |
|
||||
<button bindtap="goBackToProductList" class="btn-secondary">返回商品列表</button> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
@ -1,671 +0,0 @@ |
|||||
.container { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
min-height: 100vh; |
|
||||
background: linear-gradient(135deg, #F9FAFB 0%, #F2F4F8 100%); |
|
||||
font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif; |
|
||||
} |
|
||||
|
|
||||
/* 步骤指示器 */ |
|
||||
.step-indicator { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
padding: 32rpx; |
|
||||
background: #FFFFFF; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|
||||
margin-bottom: 24rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.step-item { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
flex: 1; |
|
||||
} |
|
||||
|
|
||||
.step-number { |
|
||||
width: 48rpx; |
|
||||
height: 48rpx; |
|
||||
border-radius: 50%; |
|
||||
background: #E5E5E5; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
font-size: 24rpx; |
|
||||
font-weight: 600; |
|
||||
color: #999; |
|
||||
margin-bottom: 8rpx; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.step-text { |
|
||||
font-size: 20rpx; |
|
||||
color: #999; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.step-item.active .step-number { |
|
||||
background: #007AFF; |
|
||||
color: #FFFFFF; |
|
||||
box-shadow: 0 0 0 8rpx rgba(0,122,255,0.1); |
|
||||
} |
|
||||
|
|
||||
.step-item.active .step-text { |
|
||||
color: #007AFF; |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
.step-line { |
|
||||
flex: 1; |
|
||||
height: 2rpx; |
|
||||
background: #E5E5E5; |
|
||||
margin: 0 16rpx; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.step-line.active { |
|
||||
background: #007AFF; |
|
||||
} |
|
||||
|
|
||||
.header { |
|
||||
background: #FFFFFF; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|
||||
position: sticky; |
|
||||
top: 0; |
|
||||
z-index: 10; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.header-content { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
padding: 24rpx 32rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
height: 100rpx; |
|
||||
max-width: 100%; |
|
||||
position: relative; |
|
||||
} |
|
||||
|
|
||||
.back-button { |
|
||||
width: 100rpx; |
|
||||
height: 60rpx; |
|
||||
line-height: 60rpx; |
|
||||
font-size: 36rpx; |
|
||||
color: #4a90e2; |
|
||||
background: transparent; |
|
||||
border: none; |
|
||||
padding: 0 16rpx 0 0; |
|
||||
margin: 0; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: flex-start; |
|
||||
border-radius: 8rpx; |
|
||||
transition: all 0.3s ease; |
|
||||
flex-shrink: 0; |
|
||||
overflow: visible; |
|
||||
} |
|
||||
|
|
||||
.back-button:hover { |
|
||||
color: #3b82f6; |
|
||||
background: rgba(74, 144, 226, 0.1); |
|
||||
} |
|
||||
|
|
||||
.back-icon { |
|
||||
margin-right: 8rpx; |
|
||||
font-size: 36rpx; |
|
||||
font-weight: 600; |
|
||||
transition: all 0.3s ease; |
|
||||
display: inline-block; |
|
||||
} |
|
||||
|
|
||||
.back-button:hover .back-icon { |
|
||||
transform: translateX(-12rpx); |
|
||||
font-weight: 700; |
|
||||
} |
|
||||
|
|
||||
.back-text { |
|
||||
font-size: 36rpx; |
|
||||
font-weight: 700; |
|
||||
} |
|
||||
|
|
||||
.title { |
|
||||
font-size: 36rpx; |
|
||||
font-weight: 700; |
|
||||
color: #2c3e50; |
|
||||
letter-spacing: 2rpx; |
|
||||
text-align: center; |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
position: absolute; |
|
||||
left: 50%; |
|
||||
transform: translateX(-50%); |
|
||||
width: 100%; |
|
||||
max-width: 300rpx; |
|
||||
} |
|
||||
|
|
||||
.header-right { |
|
||||
width: 60rpx; |
|
||||
flex-shrink: 0; |
|
||||
} |
|
||||
|
|
||||
.content { |
|
||||
flex: 1; |
|
||||
padding: 32rpx; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
overflow-x: hidden; |
|
||||
} |
|
||||
|
|
||||
/* 加载状态 */ |
|
||||
.loading { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
bottom: 0; |
|
||||
background: rgba(255, 255, 255, 0.9); |
|
||||
z-index: 9999; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.loading-spinner { |
|
||||
width: 80rpx; |
|
||||
height: 80rpx; |
|
||||
border: 8rpx solid rgba(74, 144, 226, 0.2); |
|
||||
border-top: 8rpx solid #4a90e2; |
|
||||
border-radius: 50%; |
|
||||
animation: spin 1s linear infinite; |
|
||||
margin-bottom: 32rpx; |
|
||||
} |
|
||||
|
|
||||
@keyframes spin { |
|
||||
0% { transform: rotate(0deg); } |
|
||||
100% { transform: rotate(360deg); } |
|
||||
} |
|
||||
|
|
||||
.loading-text { |
|
||||
font-size: 28rpx; |
|
||||
color: #666; |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
/* 错误提示卡片 */ |
|
||||
.error-card { |
|
||||
background: #fff; |
|
||||
border-radius: 16rpx; |
|
||||
padding: 48rpx; |
|
||||
margin: 32rpx 0; |
|
||||
box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.08); |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
text-align: center; |
|
||||
} |
|
||||
|
|
||||
.error-icon { |
|
||||
font-size: 80rpx; |
|
||||
margin-bottom: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.error-text { |
|
||||
font-size: 28rpx; |
|
||||
color: #e74c3c; |
|
||||
margin-bottom: 32rpx; |
|
||||
line-height: 1.5; |
|
||||
} |
|
||||
|
|
||||
/* 按钮样式 */ |
|
||||
.button-group { |
|
||||
display: flex; |
|
||||
gap: 16rpx; |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
|
||||
.btn-primary { |
|
||||
flex: 1; |
|
||||
height: 80rpx; |
|
||||
line-height: 80rpx; |
|
||||
font-size: 32rpx; |
|
||||
font-weight: 600; |
|
||||
border-radius: 40rpx; |
|
||||
background: linear-gradient(135deg, #007AFF 0%, #0051D5 100%); |
|
||||
color: #fff; |
|
||||
border: none; |
|
||||
box-shadow: 0 4rpx 8rpx rgba(0,122,255,0.2); |
|
||||
transition: all 0.3s ease; |
|
||||
padding: 0 48rpx; |
|
||||
} |
|
||||
|
|
||||
.btn-primary:hover { |
|
||||
transform: translateY(-2rpx); |
|
||||
box-shadow: 0 6rpx 16rpx rgba(0,122,255,0.3); |
|
||||
} |
|
||||
|
|
||||
.btn-secondary { |
|
||||
flex: 1; |
|
||||
height: 80rpx; |
|
||||
line-height: 80rpx; |
|
||||
font-size: 32rpx; |
|
||||
font-weight: 600; |
|
||||
border-radius: 40rpx; |
|
||||
background: #fff; |
|
||||
color: #007AFF; |
|
||||
border: 1rpx solid #007AFF; |
|
||||
transition: all 0.3s ease; |
|
||||
padding: 0 48rpx; |
|
||||
} |
|
||||
|
|
||||
.btn-secondary:hover { |
|
||||
background: rgba(0,122,255,0.05); |
|
||||
transform: translateY(-2rpx); |
|
||||
} |
|
||||
|
|
||||
.btn-change-product { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
padding: 12rpx 24rpx; |
|
||||
font-size: 28rpx; |
|
||||
color: #007AFF; |
|
||||
background: transparent; |
|
||||
border: 1rpx solid #007AFF; |
|
||||
border-radius: 20rpx; |
|
||||
transition: all 0.3s ease; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|
||||
} |
|
||||
|
|
||||
.btn-change-product:hover { |
|
||||
background: rgba(0,122,255,0.1); |
|
||||
transform: translateY(-2rpx); |
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,122,255,0.2); |
|
||||
} |
|
||||
|
|
||||
.btn-change-product-text { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
/* 商品信息卡片 */ |
|
||||
.product-card { |
|
||||
background: #FFFFFF; |
|
||||
border-radius: 12rpx; |
|
||||
padding: 32rpx; |
|
||||
margin: 24rpx 0 32rpx; |
|
||||
width: 100%; |
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.04); |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.product-info-container { |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.product-info-row { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.product-image-container { |
|
||||
width: 120rpx; |
|
||||
height: 120rpx; |
|
||||
border-radius: 8rpx; |
|
||||
overflow: hidden; |
|
||||
margin-right: 24rpx; |
|
||||
background: #F5F5F5; |
|
||||
flex-shrink: 0; |
|
||||
} |
|
||||
|
|
||||
.product-image { |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
object-fit: cover; |
|
||||
} |
|
||||
|
|
||||
.product-details { |
|
||||
flex: 1; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 8rpx; |
|
||||
} |
|
||||
|
|
||||
.product-name { |
|
||||
font-size: 32rpx; |
|
||||
font-weight: 600; |
|
||||
color: #333; |
|
||||
line-height: 1.4; |
|
||||
word-break: break-word; |
|
||||
display: -webkit-box; |
|
||||
-webkit-line-clamp: 2; |
|
||||
-webkit-box-orient: vertical; |
|
||||
overflow: hidden; |
|
||||
} |
|
||||
|
|
||||
.product-hint { |
|
||||
font-size: 24rpx; |
|
||||
color: #999; |
|
||||
line-height: 1.4; |
|
||||
} |
|
||||
|
|
||||
/* 规格列表区域 */ |
|
||||
.spec-section { |
|
||||
margin-top: 32rpx; |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
|
||||
.section-header { |
|
||||
display: flex; |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
margin-bottom: 24rpx; |
|
||||
padding: 0; |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
|
||||
.section-title { |
|
||||
font-size: 32rpx; |
|
||||
font-weight: 600; |
|
||||
color: #333; |
|
||||
padding-left: 16rpx; |
|
||||
border-left: 6rpx solid #007AFF; |
|
||||
border-radius: 4rpx; |
|
||||
line-height: 32rpx; |
|
||||
} |
|
||||
|
|
||||
.section-count { |
|
||||
font-size: 28rpx; |
|
||||
color: #999; |
|
||||
} |
|
||||
|
|
||||
/* 规格列表布局 */ |
|
||||
.spec-list { |
|
||||
margin-top: 16rpx; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 16rpx; |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
|
||||
/* 规格项 */ |
|
||||
.spec-item { |
|
||||
background: #FFFFFF; |
|
||||
border: 1rpx solid #E5E5E5; |
|
||||
border-radius: 12rpx; |
|
||||
padding: 32rpx 32rpx; |
|
||||
cursor: pointer; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.02); |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.spec-radio { |
|
||||
width: 32rpx; |
|
||||
height: 32rpx; |
|
||||
border: 2rpx solid #E5E5E5; |
|
||||
border-radius: 50%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
flex-shrink: 0; |
|
||||
transition: all 0.3s ease; |
|
||||
margin-left: 16rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-item:hover { |
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.05); |
|
||||
transform: translateY(-2rpx); |
|
||||
} |
|
||||
|
|
||||
.spec-item:active { |
|
||||
background: #F5F5F5; |
|
||||
} |
|
||||
|
|
||||
.spec-item.selected { |
|
||||
border: 2rpx solid #007AFF; |
|
||||
box-shadow: 0 0 0 4rpx rgba(0,122,255,0.1); |
|
||||
} |
|
||||
|
|
||||
.spec-info { |
|
||||
flex: 1; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 8rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-name { |
|
||||
font-size: 30rpx; |
|
||||
font-weight: 500; |
|
||||
color: #333; |
|
||||
line-height: 1.4; |
|
||||
word-break: break-word; |
|
||||
padding: 0 8rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-details { |
|
||||
font-size: 24rpx; |
|
||||
color: #666; |
|
||||
line-height: 1.4; |
|
||||
padding: 0 8rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-radio { |
|
||||
width: 32rpx; |
|
||||
height: 32rpx; |
|
||||
border: 2rpx solid #E5E5E5; |
|
||||
border-radius: 50%; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
flex-shrink: 0; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.spec-radio.selected { |
|
||||
border-color: #007AFF; |
|
||||
background: #007AFF; |
|
||||
} |
|
||||
|
|
||||
.radio-inner { |
|
||||
width: 16rpx; |
|
||||
height: 16rpx; |
|
||||
border-radius: 50%; |
|
||||
background: #FFFFFF; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
/* 空状态 */ |
|
||||
.empty-state { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
padding: 120rpx 0; |
|
||||
text-align: center; |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
|
||||
.empty-icon { |
|
||||
font-size: 120rpx; |
|
||||
margin-bottom: 40rpx; |
|
||||
opacity: 0.6; |
|
||||
color: #CCC; |
|
||||
} |
|
||||
|
|
||||
.empty-text { |
|
||||
font-size: 32rpx; |
|
||||
font-weight: 600; |
|
||||
color: #333; |
|
||||
margin-bottom: 16rpx; |
|
||||
line-height: 1.4; |
|
||||
padding: 0 40rpx; |
|
||||
} |
|
||||
|
|
||||
.empty-subtext { |
|
||||
font-size: 28rpx; |
|
||||
color: #999; |
|
||||
margin-bottom: 48rpx; |
|
||||
line-height: 1.4; |
|
||||
padding: 0 40rpx; |
|
||||
} |
|
||||
|
|
||||
/* 响应式设计 */ |
|
||||
@media (max-width: 750rpx) { |
|
||||
.content { |
|
||||
padding: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.product-card { |
|
||||
padding: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.title { |
|
||||
font-size: 32rpx; |
|
||||
} |
|
||||
|
|
||||
.section-title { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.product-name { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.button-group { |
|
||||
flex-direction: column; |
|
||||
gap: 12rpx; |
|
||||
} |
|
||||
|
|
||||
.btn-primary, |
|
||||
.btn-secondary { |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
|
||||
.spec-item { |
|
||||
padding: 28rpx 20rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-name { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-details { |
|
||||
font-size: 22rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-list { |
|
||||
gap: 16rpx; |
|
||||
} |
|
||||
|
|
||||
.product-image-container { |
|
||||
width: 100rpx; |
|
||||
height: 100rpx; |
|
||||
} |
|
||||
|
|
||||
.product-name { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.product-hint { |
|
||||
font-size: 22rpx; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 小屏幕设备适配 */ |
|
||||
@media (max-width: 414rpx) { |
|
||||
.content { |
|
||||
padding: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.title { |
|
||||
font-size: 32rpx; |
|
||||
} |
|
||||
|
|
||||
.section-title { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.product-name { |
|
||||
font-size: 26rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-item { |
|
||||
padding: 24rpx 16rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-name { |
|
||||
font-size: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-details { |
|
||||
font-size: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.btn-change-product { |
|
||||
padding: 10rpx 20rpx; |
|
||||
} |
|
||||
|
|
||||
.btn-change-product-text { |
|
||||
font-size: 26rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-list { |
|
||||
gap: 12rpx; |
|
||||
} |
|
||||
|
|
||||
.product-image-container { |
|
||||
width: 80rpx; |
|
||||
height: 80rpx; |
|
||||
} |
|
||||
|
|
||||
.product-name { |
|
||||
font-size: 26rpx; |
|
||||
} |
|
||||
|
|
||||
.product-hint { |
|
||||
font-size: 20rpx; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 暂无更多规格提示 */ |
|
||||
.no-more { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
padding: 48rpx 0; |
|
||||
margin-top: 32rpx; |
|
||||
margin-bottom: 40rpx; |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
|
||||
.no-more::before, |
|
||||
.no-more::after { |
|
||||
content: ''; |
|
||||
flex: 1; |
|
||||
height: 1rpx; |
|
||||
background: #E5E5E5; |
|
||||
margin: 0 16rpx; |
|
||||
} |
|
||||
|
|
||||
.no-more-text { |
|
||||
font-size: 24rpx; |
|
||||
color: #BBB; |
|
||||
font-weight: 500; |
|
||||
white-space: nowrap; |
|
||||
} |
|
||||
@ -1,845 +0,0 @@ |
|||||
Page({ |
|
||||
data: { |
|
||||
productName: '', |
|
||||
specifications: [], |
|
||||
loading: false, |
|
||||
error: '', |
|
||||
categories: [], |
|
||||
selectedCategory: '', |
|
||||
showOneKeyLoginModal: false, // 是否显示登录弹窗
|
|
||||
categoryDescriptions: { |
|
||||
'粉壳': '粉壳蛋由白壳与褐壳蛋鸡杂交而成,蛋壳呈粉红色,表面清洁带白霜。其蛋清浓稠、蛋黄大而饱满,营养丰富且口感细腻', |
|
||||
'绿壳': '绿壳蛋是我国特有珍稀鸡种所产的蛋,具有"五黑一绿"特点(黑毛、黑皮、黑骨、黑内脏,产绿壳蛋)。其蛋黄大、蛋清稠,氨基酸含量比普通鸡蛋高5-10倍,被誉为"保健蛋"', |
|
||||
'褐壳': '褐壳蛋是我国市场上最常见的鸡蛋类型,由洛岛红、新汉夏等褐壳蛋鸡品种产出。蛋壳呈褐色至深褐色,质地厚实耐运输,蛋黄比例大,适合家庭日常烹饪。常见品种有海兰褐、罗曼褐等。', |
|
||||
'土鸡蛋': '土鸡蛋指农家散养鸡在自然环境中觅食所产的蛋,个头较小、蛋壳较薄,蛋清粘稠、蛋黄饱满。口感风味浓郁。' |
|
||||
} |
|
||||
}, |
|
||||
onLoad(options) { |
|
||||
let productName = ''; |
|
||||
// 首先检查URL参数
|
|
||||
if (options.productName) { |
|
||||
productName = options.productName; |
|
||||
} else { |
|
||||
// 然后检查本地存储(用于wx.switchTab导航)
|
|
||||
productName = wx.getStorageSync('selectedProductName') || ''; |
|
||||
// 清除本地存储中的商品名称,避免影响下次进入
|
|
||||
if (productName) { |
|
||||
wx.removeStorageSync('selectedProductName'); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 增加用户估价点击次数(进入页面时自动增加)
|
|
||||
const API = require('../../utils/api'); |
|
||||
API.incrementAppraisalNum().then(res => { |
|
||||
console.log('增加估价次数成功:', res); |
|
||||
}).catch(err => { |
|
||||
console.error('增加估价次数失败:', err); |
|
||||
// 即使失败也不影响主流程
|
|
||||
}); |
|
||||
|
|
||||
if (productName) { |
|
||||
this.setData({ productName: productName }); |
|
||||
this.loadSpecifications(productName); |
|
||||
} else { |
|
||||
// 如果没有商品名称参数,加载所有商品数据并计算
|
|
||||
this.loadAllData(); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
// 下拉刷新
|
|
||||
onPullDownRefresh() { |
|
||||
console.log('开始下拉刷新'); |
|
||||
this.loadAllData(); |
|
||||
}, |
|
||||
|
|
||||
// 加载所有商品数据并计算
|
|
||||
loadAllData() { |
|
||||
this.setData({ |
|
||||
loading: true, |
|
||||
categories: [] // 清空分类数据,确保加载时只显示加载状态
|
|
||||
}); |
|
||||
|
|
||||
const api = require('../../utils/api'); |
|
||||
// 使用正确的参数调用getProducts方法
|
|
||||
api.getProducts(1, 1000, 'all', '').then(result => { |
|
||||
console.log('API返回结果:', result); |
|
||||
// 从返回对象中提取products数组,如果不存在则使用空数组
|
|
||||
const products = result.products || []; |
|
||||
console.log('提取的products数组:', products); |
|
||||
console.log('products数组长度:', products.length); |
|
||||
|
|
||||
// 存储原始商品数据到本地存储
|
|
||||
wx.setStorageSync('allProducts', products); |
|
||||
|
|
||||
// 过滤出有有效category字段的商品(忽略空字符串和仅包含空格的分类)
|
|
||||
const productsWithCategory = products.filter(product => { |
|
||||
if (!product.category) return false; |
|
||||
const categoryStr = String(product.category).trim(); |
|
||||
return categoryStr !== ''; |
|
||||
}); |
|
||||
console.log('有有效category的商品:', productsWithCategory); |
|
||||
console.log('有有效category的商品数量:', productsWithCategory.length); |
|
||||
|
|
||||
// 提取所有分类(去除前后空格)
|
|
||||
const categories = [...new Set(productsWithCategory.map(product => { |
|
||||
let category = String(product.category).trim(); |
|
||||
// 将"白壳"替换为"土鸡蛋"
|
|
||||
if (category === '白壳') { |
|
||||
category = '土鸡蛋'; |
|
||||
} |
|
||||
return category; |
|
||||
}))]; |
|
||||
console.log('提取的分类数组:', categories); |
|
||||
console.log('分类数量:', categories.length); |
|
||||
|
|
||||
// 存储分类数据到本地存储
|
|
||||
wx.setStorageSync('evaluate2Categories', categories); |
|
||||
|
|
||||
// 计算每个分类下的商品名称列表
|
|
||||
const categoryProductsMap = {}; |
|
||||
categories.forEach(category => { |
|
||||
// 过滤出该分类下的商品
|
|
||||
const categoryProducts = productsWithCategory.filter(product => { |
|
||||
const productCategory = String(product.category).trim(); |
|
||||
return productCategory === category; |
|
||||
}); |
|
||||
|
|
||||
// 提取商品名称(去除前后空格,去重)
|
|
||||
const productNames = [...new Set(categoryProducts.map(product => { |
|
||||
// 尝试从多个字段获取商品名称
|
|
||||
const nameFields = [product.productName, product.name, product.product]; |
|
||||
for (const field of nameFields) { |
|
||||
if (field) { |
|
||||
const trimmedName = String(field).trim(); |
|
||||
if (trimmedName !== '') { |
|
||||
return trimmedName; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
return ''; |
|
||||
}).filter(name => name !== ''))]; |
|
||||
|
|
||||
categoryProductsMap[category] = productNames; |
|
||||
}); |
|
||||
|
|
||||
// 存储分类商品映射到本地存储
|
|
||||
wx.setStorageSync('evaluate2CategoryProductsMap', categoryProductsMap); |
|
||||
|
|
||||
// 计算每个商品的规格和价格信息
|
|
||||
const productSpecsMap = {}; |
|
||||
products.forEach(product => { |
|
||||
// 尝试从多个字段获取商品名称
|
|
||||
const nameFields = [product.productName, product.name, product.product]; |
|
||||
let productName = ''; |
|
||||
for (const field of nameFields) { |
|
||||
if (field) { |
|
||||
const trimmedName = String(field).trim(); |
|
||||
if (trimmedName !== '') { |
|
||||
productName = trimmedName; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (productName) { |
|
||||
if (!productSpecsMap[productName]) { |
|
||||
productSpecsMap[productName] = []; |
|
||||
} |
|
||||
|
|
||||
// 提取规格和价格
|
|
||||
const specStr = (product.specification || product.spec || '').trim(); |
|
||||
const price = product.price || ''; |
|
||||
|
|
||||
if (specStr && price) { |
|
||||
productSpecsMap[productName].push({ |
|
||||
specification: specStr, |
|
||||
price: price |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 存储商品规格映射到本地存储
|
|
||||
wx.setStorageSync('evaluate2ProductSpecsMap', productSpecsMap); |
|
||||
|
|
||||
// 加载分类数据到页面
|
|
||||
this.setData({ |
|
||||
categories: categories, |
|
||||
loading: false |
|
||||
}); |
|
||||
|
|
||||
// 结束下拉刷新
|
|
||||
wx.stopPullDownRefresh(); |
|
||||
}).catch(err => { |
|
||||
console.error('获取商品数据失败:', err); |
|
||||
this.setData({ |
|
||||
error: '获取商品数据失败,请稍后重试', |
|
||||
loading: false |
|
||||
}); |
|
||||
|
|
||||
// 结束下拉刷新
|
|
||||
wx.stopPullDownRefresh(); |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 加载商品分类数据
|
|
||||
loadCategories() { |
|
||||
this.setData({ loading: true }); |
|
||||
|
|
||||
// 尝试从本地存储中获取分类数据
|
|
||||
let categories = wx.getStorageSync('evaluate2Categories') || []; |
|
||||
|
|
||||
// 处理分类数据,将"白壳"替换为"土鸡蛋"
|
|
||||
categories = categories.map(category => { |
|
||||
if (category === '白壳') { |
|
||||
return '土鸡蛋'; |
|
||||
} |
|
||||
return category; |
|
||||
}); |
|
||||
|
|
||||
if (categories.length > 0) { |
|
||||
// 如果本地存储中有分类数据,直接使用
|
|
||||
console.log('从本地存储获取分类数据:', categories); |
|
||||
this.setData({ |
|
||||
categories: categories, |
|
||||
loading: false |
|
||||
}); |
|
||||
} else { |
|
||||
// 如果本地存储中没有分类数据,重新加载所有数据
|
|
||||
this.loadAllData(); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
loadSpecifications(productName) { |
|
||||
if (!productName) { |
|
||||
this.setData({ |
|
||||
error: '请先选择商品', |
|
||||
loading: false |
|
||||
}); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
this.setData({ loading: true, specifications: [] }); |
|
||||
|
|
||||
// 直接从本地存储获取商品数据,避免重复请求
|
|
||||
const localGoods = wx.getStorageSync('goods') || []; |
|
||||
console.log('从本地存储获取的商品数量:', localGoods.length); |
|
||||
|
|
||||
if (localGoods.length > 0) { |
|
||||
this.processSpecifications(productName, localGoods); |
|
||||
} else { |
|
||||
// 如果本地没有数据,再请求服务器
|
|
||||
const api = require('../../utils/api'); |
|
||||
// 使用正确的参数调用getProducts方法
|
|
||||
api.getProducts(1, 1000, 'all', '').then(result => { |
|
||||
// 从返回对象中提取products数组
|
|
||||
const products = result.products || []; |
|
||||
this.processSpecifications(productName, products); |
|
||||
}).catch(err => { |
|
||||
console.error('获取规格失败:', err); |
|
||||
this.setData({ |
|
||||
error: '获取规格失败,请稍后重试', |
|
||||
loading: false |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
// 解析规格,提取类型(净重/毛重)和数值范围
|
|
||||
parseSpecification(spec) { |
|
||||
const weightMatch = spec.match(/(净重|毛重)(\d+)-(\d+)/); |
|
||||
if (weightMatch) { |
|
||||
const type = weightMatch[1]; // 净重或毛重
|
|
||||
const min = parseFloat(weightMatch[2]); |
|
||||
const max = parseFloat(weightMatch[3]); |
|
||||
const avg = (min + max) / 2; |
|
||||
return { |
|
||||
type: type, |
|
||||
min: min, |
|
||||
max: max, |
|
||||
avg: avg |
|
||||
}; |
|
||||
} |
|
||||
return null; |
|
||||
}, |
|
||||
|
|
||||
processSpecifications(productName, products) { |
|
||||
console.log('处理的商品数据数量:', products.length); |
|
||||
console.log('当前处理的商品名称:', productName); |
|
||||
|
|
||||
// 检查products是否为空
|
|
||||
if (!products || products.length === 0) { |
|
||||
console.error('商品数据为空'); |
|
||||
this.setData({ |
|
||||
error: '商品数据为空', |
|
||||
loading: false |
|
||||
}); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 过滤出当前商品名称的所有商品
|
|
||||
const filteredProducts = products.filter(product => { |
|
||||
const match = product.productName === productName; |
|
||||
console.log('商品:', product.productName, '规格:', product.specification, '价格:', product.price, '匹配:', match); |
|
||||
return match; |
|
||||
}); |
|
||||
|
|
||||
console.log('过滤后的商品数量:', filteredProducts.length); |
|
||||
console.log('过滤后的商品详情:', filteredProducts); |
|
||||
|
|
||||
// 检查filteredProducts是否为空
|
|
||||
if (filteredProducts.length === 0) { |
|
||||
console.error('未找到商品名称为"' + productName + '"的商品'); |
|
||||
this.setData({ |
|
||||
error: '未找到对应商品名称的商品', |
|
||||
loading: false |
|
||||
}); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 提取规格和价格,处理可能的空值和空格
|
|
||||
const specPriceMap = {}; |
|
||||
filteredProducts.forEach((product, productIndex) => { |
|
||||
const specStr = (product.specification || product.spec || '').trim(); |
|
||||
const price = product.price || ''; |
|
||||
|
|
||||
console.log(`处理第${productIndex + 1}个商品: 规格字符串='${specStr}', 价格字符串='${price}'`); |
|
||||
|
|
||||
// 价格为空的不参与计算
|
|
||||
if (!price || price.trim() === '') { |
|
||||
console.log(`商品价格为空,跳过处理`); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (specStr.length > 0) { |
|
||||
// 处理逗号分隔的多个规格,确保每个规格都被正确分割
|
|
||||
// 首先按逗号分割
|
|
||||
let specs = specStr.split(',').map(spec => spec.trim()).filter(spec => spec.length > 0); |
|
||||
|
|
||||
// 进一步处理规格,确保每个规格都是独立的
|
|
||||
const processedSpecs = []; |
|
||||
specs.forEach(spec => { |
|
||||
// 检查规格是否包含多个规格(例如:"净重29-30,净重31-32")
|
|
||||
if (spec.includes(',')) { |
|
||||
// 按中文逗号分割
|
|
||||
const subSpecs = spec.split(',').map(s => s.trim()).filter(s => s.length > 0); |
|
||||
processedSpecs.push(...subSpecs); |
|
||||
} else { |
|
||||
processedSpecs.push(spec); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
specs = processedSpecs; |
|
||||
|
|
||||
// 处理逗号分隔的多个价格
|
|
||||
const prices = (price || '').split(',').map(p => p.trim()).filter(p => p && p.trim() !== ''); |
|
||||
|
|
||||
console.log(`规格数组:`, specs); |
|
||||
console.log(`价格数组:`, prices); |
|
||||
|
|
||||
// 价格为空的不参与计算
|
|
||||
if (prices.length === 0) { |
|
||||
console.log(`价格数组为空,跳过处理`); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 将规格和价格配对
|
|
||||
specs.forEach((spec, index) => { |
|
||||
if (spec.length > 0) { |
|
||||
// 确保价格索引不超出范围
|
|
||||
const priceIndex = index % prices.length; |
|
||||
const matchedPrice = prices[priceIndex] || ''; |
|
||||
console.log(`规格'${spec}' 配对价格: '${matchedPrice}'`); |
|
||||
|
|
||||
// 只有当价格不为空时才添加该规格
|
|
||||
if (matchedPrice && matchedPrice.trim() !== '') { |
|
||||
// 收集相同规格的所有价格,以便计算平均值
|
|
||||
if (!specPriceMap[spec]) { |
|
||||
specPriceMap[spec] = []; |
|
||||
} |
|
||||
specPriceMap[spec].push(matchedPrice); |
|
||||
} else { |
|
||||
console.log(`规格'${spec}' 价格为空,不添加`); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 转换为规格对象数组
|
|
||||
const specifications = Object.keys(specPriceMap).map(spec => { |
|
||||
const prices = specPriceMap[spec]; |
|
||||
console.log(`规格'${spec}' 的所有原始价格:`, prices); |
|
||||
|
|
||||
// 解析规格
|
|
||||
const specInfo = this.parseSpecification(spec); |
|
||||
|
|
||||
// 处理每个价格
|
|
||||
const processedPrices = prices.map(price => { |
|
||||
if (!price || price.trim() === '' || isNaN(parseFloat(price))) { |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
const priceValue = parseFloat(price); |
|
||||
console.log(`处理价格: ${priceValue}`); |
|
||||
|
|
||||
// 价格<10的需要按照公式计算
|
|
||||
if (priceValue < 10 && specInfo) { |
|
||||
console.log(`价格 ${priceValue} < 10,按照公式计算`); |
|
||||
if (specInfo.type === '净重') { |
|
||||
// 净重:规格平均值 × 价格
|
|
||||
return specInfo.avg * priceValue; |
|
||||
} else if (specInfo.type === '毛重') { |
|
||||
// 毛重:(规格平均值 - 5) × 价格
|
|
||||
return (specInfo.avg - 5) * priceValue; |
|
||||
} |
|
||||
} |
|
||||
// 价格>=10的直接使用
|
|
||||
return priceValue; |
|
||||
}).filter(price => price > 0); // 过滤掉0值
|
|
||||
|
|
||||
console.log(`规格'${spec}' 处理后的价格:`, processedPrices); |
|
||||
|
|
||||
// 计算处理后价格的平均值
|
|
||||
let finalPrice = 0; |
|
||||
let finalPriceText = ''; |
|
||||
|
|
||||
if (processedPrices.length > 0) { |
|
||||
const sum = processedPrices.reduce((acc, price) => acc + price, 0); |
|
||||
finalPrice = sum / processedPrices.length; |
|
||||
finalPriceText = finalPrice.toFixed(2); |
|
||||
console.log(`规格'${spec}' 处理后价格的平均值: ${finalPriceText} (基于 ${processedPrices.length} 个价格)`); |
|
||||
} else { |
|
||||
console.log(`规格'${spec}' 没有有效价格`); |
|
||||
} |
|
||||
|
|
||||
const specObj = { |
|
||||
name: spec, |
|
||||
price: finalPriceText, |
|
||||
priceText: finalPriceText, |
|
||||
finalPrice: finalPrice, |
|
||||
finalPriceText: finalPriceText |
|
||||
}; |
|
||||
|
|
||||
console.log('创建的规格对象:', specObj); |
|
||||
|
|
||||
return specObj; |
|
||||
}); |
|
||||
|
|
||||
console.log('提取的规格和价格:', specifications); |
|
||||
|
|
||||
// 对规格进行排序
|
|
||||
specifications.sort((a, b) => { |
|
||||
// 解析两个规格
|
|
||||
const specA = this.parseSpecification(a.name); |
|
||||
const specB = this.parseSpecification(b.name); |
|
||||
|
|
||||
// 如果有一个规格解析失败,保持原顺序
|
|
||||
if (!specA || !specB) { |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
// 1. 按类型排序:净重在前,毛重在后
|
|
||||
if (specA.type !== specB.type) { |
|
||||
return specA.type === '净重' ? -1 : 1; |
|
||||
} |
|
||||
|
|
||||
// 2. 按规格最小值排序:从小到大
|
|
||||
return specA.min - specB.min; |
|
||||
}); |
|
||||
|
|
||||
console.log('排序后的规格:', specifications); |
|
||||
|
|
||||
this.setData({ |
|
||||
specifications: specifications, |
|
||||
error: '', // 清除之前的错误
|
|
||||
loading: false |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 跳转到规格详情页面
|
|
||||
goToSpecDetail(e) { |
|
||||
const specItem = e.currentTarget.dataset.spec; |
|
||||
console.log('点击的规格项:', specItem); |
|
||||
console.log('传递的价格:', specItem.finalPriceText); |
|
||||
|
|
||||
// 增加用户估价点击次数
|
|
||||
const API = require('../../utils/api'); |
|
||||
API.incrementAppraisalNum().then(res => { |
|
||||
console.log('增加估价次数成功:', res); |
|
||||
}).catch(err => { |
|
||||
console.error('增加估价次数失败:', err); |
|
||||
// 即使失败也不影响主流程
|
|
||||
}); |
|
||||
|
|
||||
wx.navigateTo({ |
|
||||
url: `/pages/evaluate1/spec-detail?productName=${encodeURIComponent(this.data.productName)}&specification=${encodeURIComponent(specItem.name)}&price=${encodeURIComponent(specItem.finalPriceText)}` |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 返回上一页
|
|
||||
goBack() { |
|
||||
wx.navigateBack(); |
|
||||
}, |
|
||||
|
|
||||
// 返回商品列表页面
|
|
||||
goBackToProductList() { |
|
||||
wx.redirectTo({ |
|
||||
url: '/pages/evaluate1/product-list' |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 选择分类,跳转到商品列表页面
|
|
||||
selectCategory(e) { |
|
||||
const category = e.currentTarget.dataset.category; |
|
||||
console.log('选择分类:', category); |
|
||||
|
|
||||
// 检查用户登录状态
|
|
||||
const openid = wx.getStorageSync('openid'); |
|
||||
const userId = wx.getStorageSync('userId'); |
|
||||
|
|
||||
if (!openid || !userId) { |
|
||||
// 用户未登录,显示登录弹窗
|
|
||||
console.log('用户未登录,显示登录弹窗'); |
|
||||
this.setData({ |
|
||||
showOneKeyLoginModal: true |
|
||||
}); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 记录用户点击分类的踪迹
|
|
||||
const API = require('../../utils/api'); |
|
||||
const traceData = { |
|
||||
action: 'click_category', |
|
||||
category: category, |
|
||||
timestamp: new Date().toISOString(), |
|
||||
page: 'evaluate2/one' |
|
||||
}; |
|
||||
|
|
||||
// 调用用户踪迹记录API
|
|
||||
API.addUserTrace(traceData).then(res => { |
|
||||
console.log('用户分类点击记录成功:', res); |
|
||||
}).catch(err => { |
|
||||
console.error('用户分类点击记录失败:', err); |
|
||||
// 即使记录失败,也不影响主流程
|
|
||||
}); |
|
||||
|
|
||||
// 跳过合作状态检查,直接跳转到商品列表页面
|
|
||||
|
|
||||
// 跳转到商品列表页面,并传递分类参数
|
|
||||
wx.redirectTo({ |
|
||||
url: `/pages/evaluate2/product-list?category=${encodeURIComponent(category)}` |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 关闭登录弹窗
|
|
||||
closeOneKeyLoginModal() { |
|
||||
this.setData({ |
|
||||
showOneKeyLoginModal: false |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 处理登录授权
|
|
||||
async onGetPhoneNumber(e) { |
|
||||
console.log('收到手机号授权事件:', e.detail); |
|
||||
|
|
||||
// 关闭手机号授权弹窗
|
|
||||
this.setData({ showOneKeyLoginModal: false }); |
|
||||
|
|
||||
// 用户点击拒绝授权
|
|
||||
if (e.detail.errMsg === 'getPhoneNumber:fail user deny') { |
|
||||
wx.showToast({ |
|
||||
title: '需要授权手机号才能使用', |
|
||||
icon: 'none', |
|
||||
duration: 2000 |
|
||||
}); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 处理没有权限的情况
|
|
||||
if (e.detail.errMsg === 'getPhoneNumber:fail no permission') { |
|
||||
wx.showToast({ |
|
||||
title: '当前环境无法获取手机号权限', |
|
||||
icon: 'none', |
|
||||
duration: 3000 |
|
||||
}); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 检查是否已经登录,避免重复授权
|
|
||||
const existingOpenid = wx.getStorageSync('openid'); |
|
||||
const existingUserId = wx.getStorageSync('userId'); |
|
||||
const existingUserInfo = wx.getStorageSync('userInfo'); |
|
||||
|
|
||||
if (existingOpenid && existingUserId && existingUserInfo && existingUserInfo.phoneNumber) { |
|
||||
console.log('用户已登录且手机号有效,登录流程已完成'); |
|
||||
wx.showToast({ |
|
||||
title: '您已登录', |
|
||||
icon: 'success', |
|
||||
duration: 1500 |
|
||||
}); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
wx.showLoading({ title: '登录中...', mask: true }); |
|
||||
|
|
||||
try { |
|
||||
if (e.detail.errMsg === 'getPhoneNumber:ok') { |
|
||||
// 用户同意授权,实际处理授权流程
|
|
||||
console.log('用户同意授权获取手机号'); |
|
||||
|
|
||||
// 1. 先执行微信登录获取code
|
|
||||
const loginRes = await new Promise((resolve, reject) => { |
|
||||
wx.login({ |
|
||||
success: resolve, |
|
||||
fail: reject |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
if (!loginRes.code) { |
|
||||
throw new Error('获取登录code失败'); |
|
||||
} |
|
||||
|
|
||||
console.log('获取登录code成功:', loginRes.code); |
|
||||
|
|
||||
// 2. 使用code换取openid
|
|
||||
const API = require('../../utils/api'); |
|
||||
const openidRes = await API.getOpenid(loginRes.code); |
|
||||
|
|
||||
// 改进错误处理逻辑,更宽容地处理服务器返回格式
|
|
||||
let openid = null; |
|
||||
let userId = null; |
|
||||
console.log('openidRes完整响应:', JSON.stringify(openidRes)); |
|
||||
|
|
||||
if (openidRes && typeof openidRes === 'object') { |
|
||||
// 适配服务器返回格式:{success: true, code: 200, message: '获取openid成功', data: {openid, userId}}
|
|
||||
if (openidRes.data && typeof openidRes.data === 'object') { |
|
||||
console.log('识别到标准服务器返回格式,从data字段提取信息'); |
|
||||
openid = openidRes.data.openid || openidRes.data.OpenID || null; |
|
||||
userId = openidRes.data.userId || null; |
|
||||
} else { |
|
||||
// 尝试从响应对象中直接提取openid
|
|
||||
console.log('尝试从根对象直接提取openid'); |
|
||||
openid = openidRes.openid || openidRes.OpenID || null; |
|
||||
userId = openidRes.userId || null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (!openid) { |
|
||||
console.error('无法从服务器响应中提取openid,完整响应:', JSON.stringify(openidRes)); |
|
||||
throw new Error(`获取openid失败: 服务器返回数据格式可能不符合预期`); |
|
||||
} |
|
||||
|
|
||||
console.log('获取openid成功:', openid); |
|
||||
|
|
||||
// 3. 存储openid和session_key
|
|
||||
wx.setStorageSync('openid', openid); |
|
||||
|
|
||||
// 从服务器返回中获取session_key
|
|
||||
if (openidRes && openidRes.session_key) { |
|
||||
wx.setStorageSync('sessionKey', openidRes.session_key); |
|
||||
} else if (openidRes && openidRes.data && openidRes.data.session_key) { |
|
||||
wx.setStorageSync('sessionKey', openidRes.data.session_key); |
|
||||
} |
|
||||
|
|
||||
// 优先使用从服务器响应data字段中提取的userId
|
|
||||
if (userId) { |
|
||||
wx.setStorageSync('userId', userId); |
|
||||
console.log('使用从服务器data字段提取的userId:', userId); |
|
||||
} else if (openidRes && openidRes.userId) { |
|
||||
wx.setStorageSync('userId', openidRes.userId); |
|
||||
console.log('使用服务器根对象中的userId:', openidRes.userId); |
|
||||
} else { |
|
||||
// 生成临时userId
|
|
||||
const tempUserId = 'user_' + Date.now(); |
|
||||
wx.setStorageSync('userId', tempUserId); |
|
||||
console.log('生成临时userId:', tempUserId); |
|
||||
} |
|
||||
|
|
||||
// 4. 上传手机号加密数据到服务器解密
|
|
||||
const phoneData = { |
|
||||
...e.detail, |
|
||||
openid: openid |
|
||||
}; |
|
||||
|
|
||||
console.log('准备上传手机号加密数据到服务器'); |
|
||||
const phoneRes = await API.uploadPhoneNumberData(phoneData); |
|
||||
|
|
||||
// 改进手机号解密结果的处理逻辑
|
|
||||
if (!phoneRes || (!phoneRes.success && !phoneRes.phoneNumber)) { |
|
||||
// 如果服务器返回格式不标准但包含手机号,也接受
|
|
||||
if (phoneRes && phoneRes.phoneNumber) { |
|
||||
console.warn('服务器返回格式可能不符合预期,但成功获取手机号'); |
|
||||
} else { |
|
||||
throw new Error('获取手机号失败: ' + (phoneRes && phoneRes.message ? phoneRes.message : '未知错误')); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 检查是否有手机号冲突
|
|
||||
const hasPhoneConflict = phoneRes.phoneNumberConflict || false; |
|
||||
const isNewPhone = phoneRes.isNewPhone || true; |
|
||||
const phoneNumber = phoneRes.phoneNumber || null; |
|
||||
|
|
||||
// 如果有手机号冲突且没有返回手机号,使用实际返回的手机号
|
|
||||
const finalPhoneNumber = phoneNumber; |
|
||||
|
|
||||
console.log('手机号解密结果:', { |
|
||||
phoneNumber: finalPhoneNumber, |
|
||||
hasPhoneConflict: hasPhoneConflict, |
|
||||
isNewPhone: isNewPhone |
|
||||
}); |
|
||||
|
|
||||
// 5. 获取用户微信名称和头像
|
|
||||
let userProfile = null; |
|
||||
try { |
|
||||
userProfile = await new Promise((resolve, reject) => { |
|
||||
wx.getUserProfile({ |
|
||||
desc: '用于完善会员资料', |
|
||||
success: resolve, |
|
||||
fail: reject |
|
||||
}); |
|
||||
}); |
|
||||
console.log('获取用户信息成功:', userProfile); |
|
||||
} catch (err) { |
|
||||
console.warn('获取用户信息失败:', err); |
|
||||
// 如果获取失败,使用默认值
|
|
||||
} |
|
||||
|
|
||||
// 6. 创建用户信息
|
|
||||
const tempUserInfo = { |
|
||||
name: userProfile ? (userProfile.userInfo.name || userProfile.userInfo.nickName) : '微信用户', |
|
||||
// 获取微信头像失败时使用微信默认头像
|
|
||||
avatarUrl: userProfile ? userProfile.userInfo.avatarUrl : 'https://my-supplier-photos.oss-cn-chengdu.aliyuncs.com/products/%E6%B5%B7%E8%93%9D%E7%81%B0/image/7a2a8a17a83ba4d3d4270828531e2041.jpeg', |
|
||||
gender: userProfile ? userProfile.userInfo.gender : 0, |
|
||||
country: userProfile ? userProfile.userInfo.country : '', |
|
||||
province: userProfile ? userProfile.userInfo.province : '', |
|
||||
city: userProfile ? userProfile.userInfo.city : '', |
|
||||
language: userProfile ? userProfile.userInfo.language : 'zh_CN', |
|
||||
phoneNumber: finalPhoneNumber |
|
||||
}; |
|
||||
|
|
||||
// 7. 保存用户信息
|
|
||||
const storedUserId = wx.getStorageSync('userId'); |
|
||||
const users = wx.getStorageSync('users') || {}; |
|
||||
const currentUserType = users[storedUserId] && users[storedUserId].type ? users[storedUserId].type : 'buyer'; |
|
||||
|
|
||||
console.log('用户身份类型:', currentUserType); |
|
||||
|
|
||||
// 8. 保存用户信息到本地和服务器
|
|
||||
console.log('开始保存用户信息...'); |
|
||||
await this.saveUserInfo(tempUserInfo, currentUserType); |
|
||||
console.log('用户信息保存完成'); |
|
||||
|
|
||||
wx.hideLoading(); |
|
||||
|
|
||||
// 根据服务器返回的结果显示不同的提示
|
|
||||
if (phoneRes && phoneRes.phoneNumberConflict) { |
|
||||
wx.showModal({ |
|
||||
title: '登录成功', |
|
||||
content: '您的手机号已被其他账号绑定', |
|
||||
showCancel: false, |
|
||||
confirmText: '我知道了', |
|
||||
success(res) { |
|
||||
if (res.confirm) { |
|
||||
console.log('用户点击了我知道了'); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 1. 登录成功提示
|
|
||||
wx.showToast({ |
|
||||
title: '登录成功', |
|
||||
icon: 'success', |
|
||||
duration: 1500 |
|
||||
}); |
|
||||
|
|
||||
// 2. 从服务器获取最新用户信息
|
|
||||
const userInfoRes = await API.getUserInfo(openid); |
|
||||
|
|
||||
// 3. 获取服务器返回的partnerstatus
|
|
||||
const serverUserInfo = userInfoRes.data; |
|
||||
const partnerStatus = serverUserInfo.partnerstatus || 'pending'; |
|
||||
|
|
||||
// 4. 更新本地缓存
|
|
||||
const localUserInfo = wx.getStorageSync('userInfo') || {}; |
|
||||
const updatedUserInfo = { |
|
||||
...localUserInfo, |
|
||||
partnerstatus: partnerStatus |
|
||||
}; |
|
||||
wx.setStorageSync('userInfo', updatedUserInfo); |
|
||||
|
|
||||
// 跳过合作状态检查,不再显示入驻提示
|
|
||||
} |
|
||||
} catch (error) { |
|
||||
wx.hideLoading(); |
|
||||
console.error('登录失败:', error); |
|
||||
wx.showToast({ |
|
||||
title: '登录失败,请重试', |
|
||||
icon: 'none', |
|
||||
duration: 2000 |
|
||||
}); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
// 保存用户信息
|
|
||||
async saveUserInfo(userInfo, userType = 'buyer') { |
|
||||
return new Promise(async (resolve, reject) => { |
|
||||
try { |
|
||||
const storedUserId = wx.getStorageSync('userId'); |
|
||||
const users = wx.getStorageSync('users') || {}; |
|
||||
|
|
||||
// 更新用户信息
|
|
||||
users[storedUserId] = { |
|
||||
...users[storedUserId], |
|
||||
...userInfo, |
|
||||
type: userType, |
|
||||
lastLogin: new Date().toISOString() |
|
||||
}; |
|
||||
|
|
||||
// 保存到本地存储
|
|
||||
wx.setStorageSync('users', users); |
|
||||
wx.setStorageSync('userInfo', userInfo); |
|
||||
|
|
||||
// 上传用户信息到服务器
|
|
||||
const API = require('../../utils/api'); |
|
||||
const submitData = { |
|
||||
openid: wx.getStorageSync('openid'), |
|
||||
userId: storedUserId, |
|
||||
...userInfo, |
|
||||
type: userType |
|
||||
}; |
|
||||
|
|
||||
await API.request('/api/user/update', 'POST', submitData).then(res => { |
|
||||
console.log('用户信息上传成功:', res); |
|
||||
resolve({ success: true, message: '用户信息保存成功' }); |
|
||||
}).catch(err => { |
|
||||
console.error('用户信息上传失败:', err); |
|
||||
// 即使服务器上传失败,也继续流程,只在本地保存
|
|
||||
resolve({ success: false, message: '本地保存成功,服务器同步失败' }); |
|
||||
}); |
|
||||
} catch (error) { |
|
||||
console.error('保存用户信息失败:', error); |
|
||||
reject(error); |
|
||||
} |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 分享配置
|
|
||||
onShareAppMessage() { |
|
||||
return { |
|
||||
title: '专业的估价平台,专为估蛋而生', |
|
||||
imageUrl: 'https://my-supplier-photos.oss-cn-chengdu.aliyuncs.com/products/%E7%BD%97%E6%9B%BC%E7%81%B0/image/c00cbcbfd12d747b44621701aed2ae02.jpeg', |
|
||||
path: '/pages/evaluate2/one' |
|
||||
}; |
|
||||
}, |
|
||||
|
|
||||
// 朋友圈分享配置
|
|
||||
onShareTimeline() { |
|
||||
return { |
|
||||
title: '专业的估价平台,专为估蛋而生', |
|
||||
imageUrl: 'https://my-supplier-photos.oss-cn-chengdu.aliyuncs.com/products/%E7%BD%97%E6%9B%BC%E7%81%B0/image/c00cbcbfd12d747b44621701aed2ae02.jpeg', |
|
||||
query: '' |
|
||||
}; |
|
||||
} |
|
||||
}); |
|
||||
@ -1,5 +0,0 @@ |
|||||
{ |
|
||||
"usingComponents": {}, |
|
||||
"enablePullDownRefresh": true, |
|
||||
"backgroundTextStyle": "dark" |
|
||||
} |
|
||||
@ -1,221 +0,0 @@ |
|||||
<view class="container"> |
|
||||
<!-- 步骤指示器 --> |
|
||||
<view class="step-indicator"> |
|
||||
<view class="step-item active"> |
|
||||
<view class="step-number">1</view> |
|
||||
<text class="step-text">选择品种</text> |
|
||||
</view> |
|
||||
<view class="step-line"></view> |
|
||||
<view class="step-item"> |
|
||||
<view class="step-number">2</view> |
|
||||
<text class="step-text">选择产品</text> |
|
||||
</view> |
|
||||
<view class="step-line"></view> |
|
||||
<view class="step-item"> |
|
||||
<view class="step-number">3</view> |
|
||||
<text class="step-text">选择规格</text> |
|
||||
</view> |
|
||||
<view class="step-line"></view> |
|
||||
<view class="step-item"> |
|
||||
<view class="step-number">4</view> |
|
||||
<text class="step-text">查看估价</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 登录弹窗 --> |
|
||||
<view wx:if="{{showOneKeyLoginModal}}" class="auth-modal-overlay"> |
|
||||
<view class="auth-modal-container"> |
|
||||
<view class="auth-modal-title">授权登录</view> |
|
||||
<view class="auth-modal-content">授权您的手机号后才能使用完整功能</view> |
|
||||
<view class="auth-modal-buttons"> |
|
||||
<button |
|
||||
class="auth-primary-button" |
|
||||
open-type="getPhoneNumber" |
|
||||
bindgetphonenumber="onGetPhoneNumber" |
|
||||
> |
|
||||
授权手机号 |
|
||||
</button> |
|
||||
<button |
|
||||
class="auth-cancel-button" |
|
||||
bindtap="closeOneKeyLoginModal" |
|
||||
> |
|
||||
取消 |
|
||||
</button> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<view class="content"> |
|
||||
<!-- 加载中状态 --> |
|
||||
<view wx:if="{{loading}}" class="loading"> |
|
||||
<view class="loading-spinner"></view> |
|
||||
<text class="loading-text">正在加载数据...</text> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 错误提示 --> |
|
||||
<view wx:if="{{error}}" class="error-card"> |
|
||||
<view class="error-icon">⚠️</view> |
|
||||
<text class="error-text">{{error}}</text> |
|
||||
<view class="button-group"> |
|
||||
<button bindtap="loadCategories" class="btn-primary">重新加载</button> |
|
||||
<button bindtap="goBackToProductList" class="btn-secondary">返回商品列表</button> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 分类选择区域 --> |
|
||||
<view wx:else class="category-section"> |
|
||||
<view class="section-header"> |
|
||||
<text class="section-title">商品分类</text> |
|
||||
<text class="section-count">{{categories.length}}个</text> |
|
||||
</view> |
|
||||
<view class="category-grid"> |
|
||||
<view |
|
||||
wx:for="{{categories}}" |
|
||||
wx:key="item" |
|
||||
class="category-card {{item === '粉壳' ? 'pink-shell-card' : item === '绿壳' ? 'green-shell-card' : item === '褐壳' ? 'brown-shell-card' : 'free-range-card'}}" |
|
||||
data-category="{{item}}" |
|
||||
bindtap="selectCategory" |
|
||||
> |
|
||||
<!-- 标签系统 --> |
|
||||
<view class="card-labels"> |
|
||||
<view class="feature-label"> |
|
||||
<text class="feature-label-text">{{item === '绿壳' ? '珍稀品种' : item === '粉壳' ? '杂交品种' : item === '褐壳' ? '常见品种' : '农家散养'}}</text> |
|
||||
</view> |
|
||||
<view class="category-label"> |
|
||||
<text class="category-label-text">{{item}}系列</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 产品图片区域 --> |
|
||||
<view class="product-image-container"> |
|
||||
<image |
|
||||
src="../../images/OIP-C.png" |
|
||||
mode="aspectFill" |
|
||||
class="product-image" |
|
||||
/> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 文字信息区域 --> |
|
||||
<view class="card-content"> |
|
||||
<text class="product-title">{{item}}鸡蛋</text> |
|
||||
<text class="product-description">{{categoryDescriptions[item] || '点击选择此类型查看相关商品'}}</text> |
|
||||
|
|
||||
<!-- 卖点标签 --> |
|
||||
<view class="selling-points"> |
|
||||
<!-- 粉壳鸡蛋卖点 --> |
|
||||
<view wx:if="{{item === '粉壳'}}" class="selling-point"> |
|
||||
<text class="selling-point-icon">🧬</text> |
|
||||
<text class="selling-point-text">杂交品种</text> |
|
||||
</view> |
|
||||
<view wx:if="{{item === '粉壳'}}" class="selling-point"> |
|
||||
<text class="selling-point-icon">🌸</text> |
|
||||
<text class="selling-point-text">粉红蛋壳</text> |
|
||||
</view> |
|
||||
<view wx:if="{{item === '粉壳'}}" class="selling-point"> |
|
||||
<text class="selling-point-icon">👅</text> |
|
||||
<text class="selling-point-text">口感细腻</text> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 绿壳鸡蛋卖点 --> |
|
||||
<view wx:if="{{item === '绿壳'}}" class="selling-point"> |
|
||||
<text class="selling-point-icon">🍃</text> |
|
||||
<text class="selling-point-text">五黑一绿</text> |
|
||||
</view> |
|
||||
<view wx:if="{{item === '绿壳'}}" class="selling-point"> |
|
||||
<text class="selling-point-icon">💪</text> |
|
||||
<text class="selling-point-text">高氨基酸</text> |
|
||||
</view> |
|
||||
<view wx:if="{{item === '绿壳'}}" class="selling-point"> |
|
||||
<text class="selling-point-icon">🏥</text> |
|
||||
<text class="selling-point-text">保健功效</text> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 褐壳鸡蛋卖点 --> |
|
||||
<view wx:if="{{item === '褐壳'}}" class="selling-point"> |
|
||||
<text class="selling-point-icon">🔥</text> |
|
||||
<text class="selling-point-text">常见品种</text> |
|
||||
</view> |
|
||||
<view wx:if="{{item === '褐壳'}}" class="selling-point"> |
|
||||
<text class="selling-point-icon">📦</text> |
|
||||
<text class="selling-point-text">耐运输</text> |
|
||||
</view> |
|
||||
<view wx:if="{{item === '褐壳'}}" class="selling-point"> |
|
||||
<text class="selling-point-icon">🍳</text> |
|
||||
<text class="selling-point-text">日常烹饪</text> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 土鸡蛋卖点 --> |
|
||||
<view wx:if="{{item === '土鸡蛋'}}" class="selling-point"> |
|
||||
<text class="selling-point-icon">🌾</text> |
|
||||
<text class="selling-point-text">自然觅食</text> |
|
||||
</view> |
|
||||
<view wx:if="{{item === '土鸡蛋'}}" class="selling-point"> |
|
||||
<text class="selling-point-icon">🥚</text> |
|
||||
<text class="selling-point-text">蛋黄饱满</text> |
|
||||
</view> |
|
||||
<view wx:if="{{item === '土鸡蛋'}}" class="selling-point"> |
|
||||
<text class="selling-point-icon">🥘</text> |
|
||||
<text class="selling-point-text">风味浓郁</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 按钮区域 --> |
|
||||
<view class="card-footer"> |
|
||||
<button class="select-button" bindtap="selectCategory" data-category="{{item}}"> |
|
||||
<text class="select-button-icon">→</text> |
|
||||
<text class="select-button-text">选择</text> |
|
||||
</button> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 商品信息 (当有productName时显示) --> |
|
||||
<view wx:if="{{productName}}" class="product-card"> |
|
||||
<view class="product-info-container"> |
|
||||
<view class="product-info-row"> |
|
||||
<text class="product-label">商品名称</text> |
|
||||
<button bindtap="goBackToProductList" class="btn-back"> |
|
||||
<text class="btn-back-icon">←</text> |
|
||||
<text class="btn-back-text">更换商品</text> |
|
||||
</button> |
|
||||
</view> |
|
||||
<text class="product-name">{{productName}}</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 规格列表 (当有specifications时显示) --> |
|
||||
<view wx:if="{{!loading && !error && specifications.length > 0}}" class="spec-section"> |
|
||||
<view class="section-header"> |
|
||||
<text class="section-title">可用规格</text> |
|
||||
<text class="section-count">{{specifications.length}}个</text> |
|
||||
</view> |
|
||||
<view class="spec-grid"> |
|
||||
<view |
|
||||
wx:for="{{specifications}}" |
|
||||
wx:key="item.name" |
|
||||
class="spec-card" |
|
||||
data-spec="{{item}}" |
|
||||
bindtap="goToSpecDetail" |
|
||||
> |
|
||||
<view class="spec-info"> |
|
||||
<text class="spec-name">{{item.name}}</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 暂无更多规格提示 --> |
|
||||
<view wx:if="{{specifications.length > 0}}" class="no-more"> |
|
||||
<text class="no-more-text">暂无更多规格选择</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 无规格提示 --> |
|
||||
<view wx:if="{{!loading && !error && specifications.length === 0 && productName}}" class="empty-state"> |
|
||||
<view class="empty-icon">📋</view> |
|
||||
<text class="empty-text">该商品暂无可用规格</text> |
|
||||
<button bindtap="goBackToProductList" class="btn-secondary">返回商品列表</button> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
@ -1,857 +0,0 @@ |
|||||
.container { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
min-height: 100vh; |
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); |
|
||||
font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif; |
|
||||
} |
|
||||
|
|
||||
/* 步骤指示器 */ |
|
||||
.step-indicator { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
padding: 32rpx; |
|
||||
background: #FFFFFF; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|
||||
margin-bottom: 24rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.step-item { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
flex: 1; |
|
||||
} |
|
||||
|
|
||||
.step-number { |
|
||||
width: 48rpx; |
|
||||
height: 48rpx; |
|
||||
border-radius: 50%; |
|
||||
background: #E5E5E5; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
font-size: 24rpx; |
|
||||
font-weight: 600; |
|
||||
color: #999; |
|
||||
margin-bottom: 8rpx; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.step-text { |
|
||||
font-size: 20rpx; |
|
||||
color: #999; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.step-item.active .step-number { |
|
||||
background: #007AFF; |
|
||||
color: #FFFFFF; |
|
||||
box-shadow: 0 0 0 8rpx rgba(0,122,255,0.1); |
|
||||
} |
|
||||
|
|
||||
.step-item.active .step-text { |
|
||||
color: #007AFF; |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
.step-line { |
|
||||
flex: 1; |
|
||||
height: 2rpx; |
|
||||
background: #E5E5E5; |
|
||||
margin: 0 16rpx; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.step-line.active { |
|
||||
background: #007AFF; |
|
||||
} |
|
||||
|
|
||||
.header { |
|
||||
background: #FFFFFF; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|
||||
position: sticky; |
|
||||
top: 0; |
|
||||
z-index: 10; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.header-content { |
|
||||
padding: 24rpx 32rpx; |
|
||||
text-align: center; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
height: 100rpx; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
max-width: 100%; |
|
||||
} |
|
||||
|
|
||||
.title { |
|
||||
font-size: 36rpx; |
|
||||
font-weight: 700; |
|
||||
color: #2c3e50; |
|
||||
letter-spacing: 2rpx; |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
.content { |
|
||||
flex: 1; |
|
||||
padding: 32rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
overflow-x: hidden; |
|
||||
} |
|
||||
|
|
||||
/* 加载状态 */ |
|
||||
.loading { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
bottom: 0; |
|
||||
background: rgba(255, 255, 255, 0.9); |
|
||||
z-index: 9999; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.loading-spinner { |
|
||||
width: 80rpx; |
|
||||
height: 80rpx; |
|
||||
border: 8rpx solid rgba(74, 144, 226, 0.2); |
|
||||
border-top: 8rpx solid #4a90e2; |
|
||||
border-radius: 50%; |
|
||||
animation: spin 1s linear infinite; |
|
||||
margin-bottom: 32rpx; |
|
||||
} |
|
||||
|
|
||||
@keyframes spin { |
|
||||
0% { transform: rotate(0deg); } |
|
||||
100% { transform: rotate(360deg); } |
|
||||
} |
|
||||
|
|
||||
.loading-text { |
|
||||
font-size: 28rpx; |
|
||||
color: #666; |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
/* 错误提示卡片 */ |
|
||||
.error-card { |
|
||||
background: #fff; |
|
||||
border-radius: 16rpx; |
|
||||
padding: 48rpx; |
|
||||
margin: 32rpx 0; |
|
||||
box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.08); |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
text-align: center; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.error-icon { |
|
||||
font-size: 80rpx; |
|
||||
margin-bottom: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.error-text { |
|
||||
font-size: 28rpx; |
|
||||
color: #e74c3c; |
|
||||
margin-bottom: 32rpx; |
|
||||
line-height: 1.5; |
|
||||
} |
|
||||
|
|
||||
/* 按钮样式 */ |
|
||||
.button-group { |
|
||||
display: flex; |
|
||||
gap: 16rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.btn-primary { |
|
||||
flex: 1; |
|
||||
height: 80rpx; |
|
||||
line-height: 80rpx; |
|
||||
font-size: 28rpx; |
|
||||
font-weight: 600; |
|
||||
border-radius: 40rpx; |
|
||||
background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%); |
|
||||
color: #fff; |
|
||||
border: none; |
|
||||
box-shadow: 0 4rpx 12rpx rgba(96, 165, 250, 0.4); |
|
||||
transition: all 0.3s ease; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.btn-primary:hover { |
|
||||
transform: translateY(-2rpx); |
|
||||
box-shadow: 0 6rpx 16rpx rgba(74, 144, 226, 0.5); |
|
||||
} |
|
||||
|
|
||||
.btn-secondary { |
|
||||
flex: 1; |
|
||||
height: 80rpx; |
|
||||
line-height: 80rpx; |
|
||||
font-size: 28rpx; |
|
||||
font-weight: 600; |
|
||||
border-radius: 40rpx; |
|
||||
background: #fff; |
|
||||
color: #4a90e2; |
|
||||
border: 2rpx solid #4a90e2; |
|
||||
transition: all 0.3s ease; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.btn-back { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
padding: 8rpx 24rpx; |
|
||||
font-size: 24rpx; |
|
||||
color: #4a90e2; |
|
||||
background: transparent; |
|
||||
border: 1rpx solid #4a90e2; |
|
||||
border-radius: 20rpx; |
|
||||
transition: all 0.3s ease; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|
||||
} |
|
||||
|
|
||||
.btn-back:hover { |
|
||||
background: rgba(74, 144, 226, 0.1); |
|
||||
transform: translateY(-2rpx); |
|
||||
box-shadow: 0 4rpx 12rpx rgba(74, 144, 226, 0.2); |
|
||||
} |
|
||||
|
|
||||
.btn-back-icon { |
|
||||
margin-right: 8rpx; |
|
||||
font-size: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.btn-back-text { |
|
||||
font-size: 22rpx; |
|
||||
} |
|
||||
|
|
||||
/* 商品信息卡片 */ |
|
||||
.product-card { |
|
||||
background: rgba(255, 255, 255, 0.95); |
|
||||
border-radius: 20rpx; |
|
||||
padding: 32rpx; |
|
||||
margin-bottom: 24rpx; |
|
||||
box-shadow: 0 6rpx 20rpx rgba(0,0,0,0.08); |
|
||||
backdrop-filter: blur(8rpx); |
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.3); |
|
||||
} |
|
||||
|
|
||||
.product-info-container { |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.product-info-row { |
|
||||
display: flex; |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
margin-bottom: 16rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.product-label { |
|
||||
font-size: 24rpx; |
|
||||
color: #666; |
|
||||
font-weight: 500; |
|
||||
flex: 1; |
|
||||
} |
|
||||
|
|
||||
.product-name { |
|
||||
font-size: 32rpx; |
|
||||
font-weight: 700; |
|
||||
color: #2c3e50; |
|
||||
line-height: 1.4; |
|
||||
word-break: break-word; |
|
||||
margin-top: 12rpx; |
|
||||
padding: 12rpx 0; |
|
||||
border-top: 1rpx solid #f1f5f9; |
|
||||
} |
|
||||
|
|
||||
/* 规格列表区域 */ |
|
||||
.spec-section { |
|
||||
margin-top: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.section-header { |
|
||||
display: flex; |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
margin-bottom: 24rpx; |
|
||||
padding: 0 8rpx; |
|
||||
} |
|
||||
|
|
||||
.section-title { |
|
||||
font-size: 30rpx; |
|
||||
font-weight: 700; |
|
||||
color: #2c3e50; |
|
||||
padding-left: 16rpx; |
|
||||
border-left: 6rpx solid #4a90e2; |
|
||||
} |
|
||||
|
|
||||
.section-count { |
|
||||
font-size: 24rpx; |
|
||||
color: #999; |
|
||||
background: rgba(74, 144, 226, 0.1); |
|
||||
padding: 6rpx 16rpx; |
|
||||
border-radius: 20rpx; |
|
||||
} |
|
||||
|
|
||||
/* 规格网格布局 */ |
|
||||
.spec-grid { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 16rpx; |
|
||||
margin-top: 16rpx; |
|
||||
} |
|
||||
|
|
||||
/* 规格卡片 */ |
|
||||
.spec-card { |
|
||||
background: #f5f5f5; |
|
||||
border-radius: 12rpx; |
|
||||
padding: 40rpx 24rpx; |
|
||||
cursor: pointer; |
|
||||
min-height: 120rpx; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
|
|
||||
.spec-info { |
|
||||
flex: 1; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
|
|
||||
.spec-name { |
|
||||
font-size: 30rpx; |
|
||||
font-weight: 500; |
|
||||
color: #333; |
|
||||
line-height: 1.4; |
|
||||
word-break: break-word; |
|
||||
text-align: center; |
|
||||
padding-right: 0; |
|
||||
} |
|
||||
|
|
||||
/* 空状态 */ |
|
||||
.empty-state { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
padding: 160rpx 0; |
|
||||
text-align: center; |
|
||||
} |
|
||||
|
|
||||
.empty-icon { |
|
||||
font-size: 160rpx; |
|
||||
margin-bottom: 40rpx; |
|
||||
opacity: 0.6; |
|
||||
} |
|
||||
|
|
||||
.empty-text { |
|
||||
font-size: 32rpx; |
|
||||
color: #666; |
|
||||
margin-bottom: 48rpx; |
|
||||
line-height: 1.4; |
|
||||
padding: 0 40rpx; |
|
||||
} |
|
||||
|
|
||||
/* 响应式设计 */ |
|
||||
@media (max-width: 750rpx) { |
|
||||
.content { |
|
||||
padding: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-grid { |
|
||||
gap: 16rpx; |
|
||||
} |
|
||||
|
|
||||
.product-card { |
|
||||
padding: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-card { |
|
||||
padding: 36rpx 20rpx; |
|
||||
min-height: 110rpx; |
|
||||
} |
|
||||
|
|
||||
.title { |
|
||||
font-size: 32rpx; |
|
||||
} |
|
||||
|
|
||||
.section-title { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.product-name { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.button-group { |
|
||||
flex-direction: column; |
|
||||
gap: 12rpx; |
|
||||
} |
|
||||
|
|
||||
.btn-primary, |
|
||||
.btn-secondary { |
|
||||
width: 100%; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 小屏幕设备适配 */ |
|
||||
@media (max-width: 414rpx) { |
|
||||
.spec-grid { |
|
||||
gap: 12rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-card { |
|
||||
padding: 32rpx 16rpx; |
|
||||
min-height: 100rpx; |
|
||||
} |
|
||||
|
|
||||
.spec-name { |
|
||||
font-size: 24rpx; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 暂无更多规格提示 */ |
|
||||
.no-more { |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
padding: 48rpx 0; |
|
||||
margin-top: 16rpx; |
|
||||
margin-bottom: 100rpx; /* 添加底部边距,避免被导航栏遮挡 */ |
|
||||
} |
|
||||
|
|
||||
.no-more-text { |
|
||||
font-size: 24rpx; |
|
||||
color: #999; |
|
||||
background: rgba(0, 0, 0, 0.03); |
|
||||
padding: 12rpx 32rpx; |
|
||||
border-radius: 30rpx; |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
/* 分类选择区域 */ |
|
||||
.category-section { |
|
||||
margin-top: 24rpx; |
|
||||
margin-bottom: 40rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.category-grid { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
margin-top: 16rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
/* 卡片基础框架 */ |
|
||||
.category-card { |
|
||||
background: #ffffff; |
|
||||
border-radius: 20rpx; |
|
||||
padding: 0 32rpx 32rpx; |
|
||||
cursor: pointer; |
|
||||
min-height: 600rpx; |
|
||||
margin-bottom: 32rpx; |
|
||||
transition: all 0.3s ease; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
box-shadow: 0 8rpx 16rpx rgba(0,0,0,0.08); |
|
||||
position: relative; |
|
||||
overflow: hidden; |
|
||||
} |
|
||||
|
|
||||
.category-card:hover { |
|
||||
transform: translateY(-8rpx); |
|
||||
box-shadow: 0 12rpx 24rpx rgba(0,0,0,0.12); |
|
||||
} |
|
||||
|
|
||||
/* 品类专属背景 */ |
|
||||
.pink-shell-card { |
|
||||
background: linear-gradient(135deg, #FECDD3 0%, #FDE2E2 100%); |
|
||||
} |
|
||||
|
|
||||
.green-shell-card { |
|
||||
background: linear-gradient(135deg, #D1FAE5 0%, #E6F7EE 100%); |
|
||||
} |
|
||||
|
|
||||
.brown-shell-card { |
|
||||
background: linear-gradient(135deg, #FEF3C7 0%, #FFF9E6 100%); |
|
||||
} |
|
||||
|
|
||||
.free-range-card { |
|
||||
background: linear-gradient(135deg, #FEF3C7 0%, #FFF9E6 100%); |
|
||||
} |
|
||||
|
|
||||
/* 标签系统 */ |
|
||||
.card-labels { |
|
||||
position: absolute; |
|
||||
top: 20rpx; |
|
||||
left: 20rpx; |
|
||||
right: 20rpx; |
|
||||
display: flex; |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
z-index: 2; |
|
||||
} |
|
||||
|
|
||||
.feature-label { |
|
||||
background: rgba(255, 255, 255, 0.9); |
|
||||
padding: 8rpx 16rpx; |
|
||||
border-radius: 20rpx; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|
||||
} |
|
||||
|
|
||||
.feature-label-text { |
|
||||
font-size: 20rpx; |
|
||||
font-weight: 500; |
|
||||
color: #1E293B; |
|
||||
} |
|
||||
|
|
||||
.category-label { |
|
||||
background: rgba(0, 0, 0, 0.7); |
|
||||
padding: 8rpx 16rpx; |
|
||||
border-radius: 20rpx; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1); |
|
||||
} |
|
||||
|
|
||||
.category-label-text { |
|
||||
font-size: 20rpx; |
|
||||
font-weight: 500; |
|
||||
color: #FFFFFF; |
|
||||
} |
|
||||
|
|
||||
/* 产品图片区域 */ |
|
||||
.product-image-container { |
|
||||
position: absolute; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
height: 300rpx; |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: flex-start; |
|
||||
z-index: 1; |
|
||||
overflow: hidden; |
|
||||
} |
|
||||
|
|
||||
.product-image { |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
border-radius: 0; |
|
||||
object-fit: cover; |
|
||||
border: none; |
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.12); |
|
||||
transform: none; |
|
||||
} |
|
||||
|
|
||||
/* 文字信息区域 */ |
|
||||
.card-content { |
|
||||
width: 100%; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 16rpx; |
|
||||
position: relative; |
|
||||
z-index: 2; |
|
||||
padding-top: 320rpx; |
|
||||
padding-bottom: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.product-title { |
|
||||
font-size: 36rpx; |
|
||||
font-weight: 700; |
|
||||
color: #1E293B; |
|
||||
line-height: 48rpx; |
|
||||
text-align: center; |
|
||||
margin-bottom: 8rpx; |
|
||||
} |
|
||||
|
|
||||
.product-description { |
|
||||
font-size: 24rpx; |
|
||||
font-weight: 400; |
|
||||
color: #64748B; |
|
||||
line-height: 36rpx; |
|
||||
text-align: center; |
|
||||
margin-bottom: 16rpx; |
|
||||
padding: 0 16rpx; |
|
||||
} |
|
||||
|
|
||||
/* 卖点标签 */ |
|
||||
.selling-points { |
|
||||
display: flex; |
|
||||
flex-wrap: wrap; |
|
||||
justify-content: center; |
|
||||
gap: 12rpx; |
|
||||
margin: 16rpx 0 24rpx; |
|
||||
} |
|
||||
|
|
||||
.selling-point { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
background: rgba(255, 255, 255, 0.8); |
|
||||
padding: 8rpx 16rpx; |
|
||||
border-radius: 20rpx; |
|
||||
box-shadow: 0 2rpx 4rpx rgba(0,0,0,0.05); |
|
||||
} |
|
||||
|
|
||||
.selling-point-icon { |
|
||||
font-size: 20rpx; |
|
||||
margin-right: 8rpx; |
|
||||
} |
|
||||
|
|
||||
.selling-point-text { |
|
||||
font-size: 20rpx; |
|
||||
font-weight: 400; |
|
||||
color: #475569; |
|
||||
} |
|
||||
|
|
||||
/* 按钮区域 */ |
|
||||
.card-footer { |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
margin-top: 16rpx; |
|
||||
width: 100%; |
|
||||
padding: 0 20rpx; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.select-button { |
|
||||
background: #4A90E2; |
|
||||
color: #FFFFFF; |
|
||||
border: none; |
|
||||
border-radius: 24rpx; |
|
||||
padding: 12rpx 32rpx; |
|
||||
font-size: 24rpx; |
|
||||
font-weight: 600; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
gap: 8rpx; |
|
||||
box-shadow: 0 4rpx 12rpx rgba(74, 144, 226, 0.3); |
|
||||
transition: all 0.3s ease; |
|
||||
width: 100%; |
|
||||
max-width: 200rpx; |
|
||||
} |
|
||||
|
|
||||
.select-button:hover { |
|
||||
background: #3B82F6; |
|
||||
transform: translateY(-2rpx); |
|
||||
box-shadow: 0 6rpx 16rpx rgba(74, 144, 226, 0.4); |
|
||||
} |
|
||||
|
|
||||
.select-button-icon { |
|
||||
font-size: 20rpx; |
|
||||
font-weight: 600; |
|
||||
} |
|
||||
|
|
||||
.select-button-text { |
|
||||
font-size: 24rpx; |
|
||||
font-weight: 600; |
|
||||
} |
|
||||
|
|
||||
/* 响应式设计 */ |
|
||||
@media (max-width: 750rpx) { |
|
||||
.category-card { |
|
||||
padding: 0 24rpx 24rpx; |
|
||||
min-height: 550rpx; |
|
||||
margin-bottom: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.product-image-container { |
|
||||
height: 280rpx; |
|
||||
} |
|
||||
|
|
||||
.card-content { |
|
||||
padding-top: 300rpx; |
|
||||
} |
|
||||
|
|
||||
.product-title { |
|
||||
font-size: 32rpx; |
|
||||
line-height: 40rpx; |
|
||||
} |
|
||||
|
|
||||
.product-description { |
|
||||
font-size: 22rpx; |
|
||||
line-height: 32rpx; |
|
||||
} |
|
||||
|
|
||||
.selling-point { |
|
||||
padding: 6rpx 12rpx; |
|
||||
} |
|
||||
|
|
||||
.selling-point-text { |
|
||||
font-size: 18rpx; |
|
||||
} |
|
||||
|
|
||||
.select-button { |
|
||||
padding: 10rpx 28rpx; |
|
||||
font-size: 22rpx; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@media (max-width: 414rpx) { |
|
||||
.category-card { |
|
||||
padding: 0 20rpx 20rpx; |
|
||||
min-height: 500rpx; |
|
||||
margin-bottom: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.product-image-container { |
|
||||
height: 240rpx; |
|
||||
} |
|
||||
|
|
||||
.card-content { |
|
||||
padding-top: 260rpx; |
|
||||
} |
|
||||
|
|
||||
.product-title { |
|
||||
font-size: 28rpx; |
|
||||
line-height: 36rpx; |
|
||||
} |
|
||||
|
|
||||
.product-description { |
|
||||
font-size: 20rpx; |
|
||||
line-height: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.selling-point { |
|
||||
padding: 4rpx 10rpx; |
|
||||
} |
|
||||
|
|
||||
.selling-point-text { |
|
||||
font-size: 16rpx; |
|
||||
} |
|
||||
|
|
||||
.select-button { |
|
||||
padding: 8rpx 24rpx; |
|
||||
font-size: 20rpx; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 调整规格网格布局,确保不被导航栏遮挡 */ |
|
||||
.spec-section { |
|
||||
margin-bottom: 40rpx; |
|
||||
} |
|
||||
|
|
||||
/* 登录弹窗样式 */ |
|
||||
.auth-modal-overlay { |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
bottom: 0; |
|
||||
background-color: rgba(0, 0, 0, 0.5); |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
z-index: 9999; |
|
||||
} |
|
||||
|
|
||||
.auth-modal-container { |
|
||||
background: rgba(255, 255, 255, 0.95); |
|
||||
border-radius: 20rpx; |
|
||||
padding: 48rpx; |
|
||||
width: 80%; |
|
||||
max-width: 500rpx; |
|
||||
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.2); |
|
||||
backdrop-filter: blur(12rpx); |
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.3); |
|
||||
} |
|
||||
|
|
||||
.auth-modal-title { |
|
||||
font-size: 36rpx; |
|
||||
font-weight: 700; |
|
||||
text-align: center; |
|
||||
margin-bottom: 24rpx; |
|
||||
color: #2c3e50; |
|
||||
letter-spacing: 2rpx; |
|
||||
} |
|
||||
|
|
||||
.auth-modal-content { |
|
||||
font-size: 28rpx; |
|
||||
text-align: center; |
|
||||
margin-bottom: 48rpx; |
|
||||
color: #666; |
|
||||
line-height: 1.5; |
|
||||
padding: 0 20rpx; |
|
||||
} |
|
||||
|
|
||||
.auth-modal-buttons { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.auth-primary-button { |
|
||||
background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%); |
|
||||
color: white; |
|
||||
border: none; |
|
||||
border-radius: 12rpx; |
|
||||
padding: 24rpx; |
|
||||
font-size: 30rpx; |
|
||||
font-weight: 700; |
|
||||
min-height: 88rpx; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
box-shadow: 0 4rpx 16rpx rgba(96, 165, 250, 0.4); |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.auth-primary-button:hover { |
|
||||
transform: translateY(-2rpx); |
|
||||
box-shadow: 0 8rpx 24rpx rgba(96, 165, 250, 0.6); |
|
||||
} |
|
||||
|
|
||||
.auth-cancel-button { |
|
||||
background: rgba(255, 255, 255, 0.9); |
|
||||
color: #2c3e50; |
|
||||
border: 2rpx solid #e2e8f0; |
|
||||
border-radius: 12rpx; |
|
||||
padding: 24rpx; |
|
||||
font-size: 28rpx; |
|
||||
font-weight: 600; |
|
||||
min-height: 88rpx; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
transition: all 0.3s ease; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); |
|
||||
} |
|
||||
|
|
||||
.auth-cancel-button:hover { |
|
||||
background: rgba(236, 240, 241, 0.8); |
|
||||
transform: translateY(-2rpx); |
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1); |
|
||||
} |
|
||||
@ -1,526 +0,0 @@ |
|||||
Page({ |
|
||||
data: { |
|
||||
productNames: [], |
|
||||
loading: false, |
|
||||
error: '', |
|
||||
category: '', |
|
||||
priceRange: { // 价格范围
|
|
||||
min: 0, |
|
||||
max: 0, |
|
||||
hasPrice: false |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
// 解析规格,提取类型(净重/毛重)和数值范围
|
|
||||
parseSpecification(spec) { |
|
||||
const weightMatch = spec.match(/(净重|毛重)(\d+)-(\d+)/); |
|
||||
if (weightMatch) { |
|
||||
const type = weightMatch[1]; // 净重或毛重
|
|
||||
const min = parseFloat(weightMatch[2]); |
|
||||
const max = parseFloat(weightMatch[3]); |
|
||||
const avg = (min + max) / 2; |
|
||||
return { |
|
||||
type: type, |
|
||||
min: min, |
|
||||
max: max, |
|
||||
avg: avg |
|
||||
}; |
|
||||
} |
|
||||
return null; |
|
||||
}, |
|
||||
onLoad(options) { |
|
||||
// 对分类参数进行 URL 解码
|
|
||||
const category = options.category ? decodeURIComponent(options.category) : ''; |
|
||||
this.setData({ category: category }); |
|
||||
console.log('解码后的分类:', category); |
|
||||
this.loadProductNames(); |
|
||||
}, |
|
||||
|
|
||||
loadProductNames() { |
|
||||
this.setData({ |
|
||||
loading: true, |
|
||||
error: '', |
|
||||
productNames: [] // 清空商品名称数据,确保加载时只显示加载状态
|
|
||||
}); |
|
||||
|
|
||||
console.log('开始加载商品列表,当前分类:', this.data.category); |
|
||||
|
|
||||
// 尝试从本地存储中获取分类商品映射数据
|
|
||||
const categoryProductsMap = wx.getStorageSync('evaluate2CategoryProductsMap') || {}; |
|
||||
|
|
||||
if (categoryProductsMap && Object.keys(categoryProductsMap).length > 0) { |
|
||||
console.log('从本地存储获取分类商品映射数据'); |
|
||||
|
|
||||
// 获取当前分类下的商品名称列表
|
|
||||
let productNames = []; |
|
||||
if (this.data.category) { |
|
||||
productNames = categoryProductsMap[this.data.category] || []; |
|
||||
console.log(`分类"${this.data.category}"下的商品数量:`, productNames.length); |
|
||||
console.log('商品名称列表:', productNames); |
|
||||
} else { |
|
||||
// 如果没有分类参数,获取所有商品名称
|
|
||||
const allProductNames = []; |
|
||||
Object.values(categoryProductsMap).forEach(names => { |
|
||||
allProductNames.push(...names); |
|
||||
}); |
|
||||
// 去重
|
|
||||
productNames = [...new Set(allProductNames)]; |
|
||||
console.log('所有商品数量:', productNames.length); |
|
||||
} |
|
||||
|
|
||||
// 计算价格范围
|
|
||||
let priceRange = { min: 0, max: 0, hasPrice: false }; |
|
||||
|
|
||||
// 从原始商品数据中计算价格范围
|
|
||||
const allProducts = wx.getStorageSync('allProducts') || []; |
|
||||
console.log('本地存储中的商品总数:', allProducts.length); |
|
||||
console.log('当前分类:', this.data.category); |
|
||||
if (allProducts.length > 0) { |
|
||||
// 过滤出当前分类下的商品
|
|
||||
const categoryProducts = allProducts.filter(product => { |
|
||||
if (!product.category) return false; |
|
||||
const productCategory = String(product.category).trim(); |
|
||||
return productCategory === this.data.category; |
|
||||
}); |
|
||||
console.log('当前分类下的商品数量:', categoryProducts.length); |
|
||||
console.log('当前分类下的商品详情:', categoryProducts); |
|
||||
|
|
||||
// 按规格分组计算平均价格
|
|
||||
const specPriceMap = {}; |
|
||||
categoryProducts.forEach(product => { |
|
||||
console.log('处理商品:', product.productName || product.name, '价格:', product.price, '规格:', product.specification || product.spec); |
|
||||
if (product.price && (product.specification || product.spec)) { |
|
||||
const priceStr = String(product.price).trim(); |
|
||||
const specStr = String(product.specification || product.spec).trim(); |
|
||||
|
|
||||
console.log('商品价格字符串:', priceStr, '规格字符串:', specStr); |
|
||||
|
|
||||
// 处理逗号分隔的多个价格
|
|
||||
const priceArray = priceStr.split(',').map(p => p.trim()).filter(p => p && p.trim() !== ''); |
|
||||
console.log('处理后的价格数组:', priceArray); |
|
||||
|
|
||||
// 处理逗号分隔的多个规格
|
|
||||
let specs = specStr.split(',').map(spec => spec.trim()).filter(spec => spec.length > 0); |
|
||||
console.log('处理后的规格数组:', specs); |
|
||||
|
|
||||
// 进一步处理规格,确保每个规格都是独立的
|
|
||||
const processedSpecs = []; |
|
||||
specs.forEach(spec => { |
|
||||
if (spec.includes(',')) { |
|
||||
// 按中文逗号分割
|
|
||||
const subSpecs = spec.split(',').map(s => s.trim()).filter(s => s.length > 0); |
|
||||
processedSpecs.push(...subSpecs); |
|
||||
} else { |
|
||||
processedSpecs.push(spec); |
|
||||
} |
|
||||
}); |
|
||||
specs = processedSpecs; |
|
||||
console.log('最终规格数组:', specs); |
|
||||
|
|
||||
// 将规格和价格配对
|
|
||||
specs.forEach((spec, index) => { |
|
||||
console.log('处理规格:', spec, '对应价格索引:', index); |
|
||||
if (spec.length > 0 && index < priceArray.length) { |
|
||||
const price = priceArray[index]; |
|
||||
if (price && price.trim() !== '') { |
|
||||
const priceValue = parseFloat(price); |
|
||||
if (!isNaN(priceValue)) { |
|
||||
// 解析规格
|
|
||||
const specInfo = this.parseSpecification(spec); |
|
||||
console.log('解析规格结果:', specInfo); |
|
||||
|
|
||||
// 价格<10的需要按照公式计算
|
|
||||
let finalPrice = priceValue; |
|
||||
if (priceValue < 10 && specInfo) { |
|
||||
if (specInfo.type === '净重') { |
|
||||
// 净重:规格平均值 × 价格
|
|
||||
finalPrice = specInfo.avg * priceValue; |
|
||||
} else if (specInfo.type === '毛重') { |
|
||||
// 毛重:(规格平均值 - 5) × 价格
|
|
||||
finalPrice = (specInfo.avg - 5) * priceValue; |
|
||||
} |
|
||||
console.log('价格计算:', priceValue, '->', finalPrice); |
|
||||
} |
|
||||
|
|
||||
// 按规格分组存储价格
|
|
||||
if (!specPriceMap[spec]) { |
|
||||
specPriceMap[spec] = []; |
|
||||
} |
|
||||
specPriceMap[spec].push(finalPrice); |
|
||||
console.log('存储价格:', finalPrice, '到规格:', spec); |
|
||||
} else { |
|
||||
console.log('价格解析失败:', price); |
|
||||
} |
|
||||
} else { |
|
||||
console.log('价格为空或无效:', price); |
|
||||
} |
|
||||
} else { |
|
||||
console.log('规格长度为0或价格索引超出范围:', spec.length, index, priceArray.length); |
|
||||
} |
|
||||
}); |
|
||||
} else { |
|
||||
console.log('商品缺少价格或规格:', !product.price ? '无价格' : '', !product.specification && !product.spec ? '无规格' : ''); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 计算每个规格的平均价格,然后找出最低和最高
|
|
||||
const specAvgPrices = []; |
|
||||
Object.keys(specPriceMap).forEach(spec => { |
|
||||
const prices = specPriceMap[spec]; |
|
||||
if (prices.length > 0) { |
|
||||
const avgPrice = prices.reduce((sum, price) => sum + price, 0) / prices.length; |
|
||||
specAvgPrices.push(avgPrice); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
if (specAvgPrices.length > 0) { |
|
||||
priceRange.min = Math.round(Math.min(...specAvgPrices) * 100) / 100; |
|
||||
priceRange.max = Math.round(Math.max(...specAvgPrices) * 100) / 100; |
|
||||
priceRange.hasPrice = true; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
this.setData({ |
|
||||
productNames: productNames, |
|
||||
priceRange: priceRange, |
|
||||
loading: false |
|
||||
}); |
|
||||
|
|
||||
// 结束下拉刷新
|
|
||||
wx.stopPullDownRefresh(); |
|
||||
} else { |
|
||||
// 如果本地存储中没有数据,尝试从本地存储获取原始商品数据
|
|
||||
const allProducts = wx.getStorageSync('allProducts') || []; |
|
||||
|
|
||||
if (allProducts.length > 0) { |
|
||||
console.log('从本地存储获取原始商品数据'); |
|
||||
|
|
||||
// 过滤出有有效category字段的商品
|
|
||||
const productsWithCategory = allProducts.filter(product => { |
|
||||
if (!product.category) return false; |
|
||||
const categoryStr = String(product.category).trim(); |
|
||||
return categoryStr !== ''; |
|
||||
}); |
|
||||
|
|
||||
// 移除价格过滤,获取所有商品
|
|
||||
let filteredProducts = productsWithCategory; |
|
||||
|
|
||||
// 如果有分类参数,过滤出该分类下的商品
|
|
||||
if (this.data.category) { |
|
||||
console.log('当前分类:', this.data.category); |
|
||||
const targetCategory = String(this.data.category).trim(); |
|
||||
filteredProducts = filteredProducts.filter(product => { |
|
||||
if (!product.category) return false; |
|
||||
const productCategory = String(product.category).trim(); |
|
||||
return productCategory === targetCategory; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 提取商品名称,支持多种字段
|
|
||||
const productNames = filteredProducts.map(product => { |
|
||||
// 尝试从多个字段获取商品名称
|
|
||||
const nameFields = [product.productName, product.name, product.product]; |
|
||||
for (const field of nameFields) { |
|
||||
if (field) { |
|
||||
const trimmedName = String(field).trim(); |
|
||||
if (trimmedName !== '') { |
|
||||
return trimmedName; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
return ''; |
|
||||
}).filter(name => name !== ''); |
|
||||
|
|
||||
// 去重
|
|
||||
const uniqueProductNames = [...new Set(productNames)]; |
|
||||
console.log('最终商品名称列表:', uniqueProductNames); |
|
||||
|
|
||||
// 计算价格范围
|
|
||||
let priceRange = { min: 0, max: 0, hasPrice: false }; |
|
||||
|
|
||||
// 从过滤后的商品中计算价格范围,按规格分组计算平均价格
|
|
||||
const specPriceMap = {}; |
|
||||
filteredProducts.forEach(product => { |
|
||||
if (product.price && (product.specification || product.spec)) { |
|
||||
const priceStr = String(product.price).trim(); |
|
||||
const specStr = String(product.specification || product.spec).trim(); |
|
||||
|
|
||||
// 处理逗号分隔的多个价格
|
|
||||
const priceArray = priceStr.split(',').map(p => p.trim()).filter(p => p && p.trim() !== ''); |
|
||||
|
|
||||
// 处理逗号分隔的多个规格
|
|
||||
let specs = specStr.split(',').map(spec => spec.trim()).filter(spec => spec.length > 0); |
|
||||
|
|
||||
// 进一步处理规格,确保每个规格都是独立的
|
|
||||
const processedSpecs = []; |
|
||||
specs.forEach(spec => { |
|
||||
if (spec.includes(',')) { |
|
||||
// 按中文逗号分割
|
|
||||
const subSpecs = spec.split(',').map(s => s.trim()).filter(s => s.length > 0); |
|
||||
processedSpecs.push(...subSpecs); |
|
||||
} else { |
|
||||
processedSpecs.push(spec); |
|
||||
} |
|
||||
}); |
|
||||
specs = processedSpecs; |
|
||||
|
|
||||
// 将规格和价格配对
|
|
||||
specs.forEach((spec, index) => { |
|
||||
if (spec.length > 0 && index < priceArray.length) { |
|
||||
const price = priceArray[index]; |
|
||||
if (price && price.trim() !== '') { |
|
||||
const priceValue = parseFloat(price); |
|
||||
if (!isNaN(priceValue)) { |
|
||||
// 解析规格
|
|
||||
const specInfo = this.parseSpecification(spec); |
|
||||
|
|
||||
// 价格<10的需要按照公式计算
|
|
||||
let finalPrice = priceValue; |
|
||||
if (priceValue < 10 && specInfo) { |
|
||||
if (specInfo.type === '净重') { |
|
||||
// 净重:规格平均值 × 价格
|
|
||||
finalPrice = specInfo.avg * priceValue; |
|
||||
} else if (specInfo.type === '毛重') { |
|
||||
// 毛重:(规格平均值 - 5) × 价格
|
|
||||
finalPrice = (specInfo.avg - 5) * priceValue; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 按规格分组存储价格
|
|
||||
if (!specPriceMap[spec]) { |
|
||||
specPriceMap[spec] = []; |
|
||||
} |
|
||||
specPriceMap[spec].push(finalPrice); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 计算每个规格的平均价格,然后找出最低和最高
|
|
||||
const specAvgPrices = []; |
|
||||
Object.keys(specPriceMap).forEach(spec => { |
|
||||
const prices = specPriceMap[spec]; |
|
||||
if (prices.length > 0) { |
|
||||
const avgPrice = prices.reduce((sum, price) => sum + price, 0) / prices.length; |
|
||||
specAvgPrices.push(avgPrice); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
if (specAvgPrices.length > 0) { |
|
||||
priceRange.min = Math.round(Math.min(...specAvgPrices) * 100) / 100; |
|
||||
priceRange.max = Math.round(Math.max(...specAvgPrices) * 100) / 100; |
|
||||
priceRange.hasPrice = true; |
|
||||
} |
|
||||
|
|
||||
this.setData({ |
|
||||
productNames: uniqueProductNames, |
|
||||
priceRange: priceRange, |
|
||||
loading: false |
|
||||
}); |
|
||||
|
|
||||
// 结束下拉刷新
|
|
||||
wx.stopPullDownRefresh(); |
|
||||
} else { |
|
||||
// 如果本地存储中也没有原始商品数据,调用API获取数据
|
|
||||
console.log('本地存储中没有数据,调用API获取商品数据...'); |
|
||||
const api = require('../../utils/api'); |
|
||||
// 使用正确的参数调用getProducts方法
|
|
||||
api.getProducts(1, 1000, 'all', '').then(result => { |
|
||||
console.log('API返回结果:', result); |
|
||||
|
|
||||
// 从返回对象中提取products数组,如果不存在则使用空数组
|
|
||||
const products = result.products || []; |
|
||||
|
|
||||
// 过滤出有有效category字段的商品
|
|
||||
const productsWithCategory = products.filter(product => { |
|
||||
if (!product.category) return false; |
|
||||
const categoryStr = String(product.category).trim(); |
|
||||
return categoryStr !== ''; |
|
||||
}); |
|
||||
|
|
||||
// 移除价格过滤,获取所有商品
|
|
||||
let filteredProducts = productsWithCategory; |
|
||||
|
|
||||
// 如果有分类参数,过滤出该分类下的商品
|
|
||||
if (this.data.category) { |
|
||||
console.log('当前分类:', this.data.category); |
|
||||
const targetCategory = String(this.data.category).trim(); |
|
||||
filteredProducts = filteredProducts.filter(product => { |
|
||||
if (!product.category) return false; |
|
||||
const productCategory = String(product.category).trim(); |
|
||||
return productCategory === targetCategory; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 提取商品名称,支持多种字段
|
|
||||
const productNames = filteredProducts.map(product => { |
|
||||
// 尝试从多个字段获取商品名称
|
|
||||
const nameFields = [product.productName, product.name, product.product]; |
|
||||
for (const field of nameFields) { |
|
||||
if (field) { |
|
||||
const trimmedName = String(field).trim(); |
|
||||
if (trimmedName !== '') { |
|
||||
return trimmedName; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
return ''; |
|
||||
}).filter(name => name !== ''); |
|
||||
|
|
||||
// 去重
|
|
||||
const uniqueProductNames = [...new Set(productNames)]; |
|
||||
console.log('最终商品名称列表:', uniqueProductNames); |
|
||||
|
|
||||
// 计算价格范围
|
|
||||
let priceRange = { min: 0, max: 0, hasPrice: false }; |
|
||||
|
|
||||
// 从过滤后的商品中计算价格范围,按规格分组计算平均价格
|
|
||||
const specPriceMap = {}; |
|
||||
filteredProducts.forEach(product => { |
|
||||
if (product.price && (product.specification || product.spec)) { |
|
||||
const priceStr = String(product.price).trim(); |
|
||||
const specStr = String(product.specification || product.spec).trim(); |
|
||||
|
|
||||
// 处理逗号分隔的多个价格
|
|
||||
const priceArray = priceStr.split(',').map(p => p.trim()).filter(p => p && p.trim() !== ''); |
|
||||
|
|
||||
// 处理逗号分隔的多个规格
|
|
||||
let specs = specStr.split(',').map(spec => spec.trim()).filter(spec => spec.length > 0); |
|
||||
|
|
||||
// 进一步处理规格,确保每个规格都是独立的
|
|
||||
const processedSpecs = []; |
|
||||
specs.forEach(spec => { |
|
||||
if (spec.includes(',')) { |
|
||||
// 按中文逗号分割
|
|
||||
const subSpecs = spec.split(',').map(s => s.trim()).filter(s => s.length > 0); |
|
||||
processedSpecs.push(...subSpecs); |
|
||||
} else { |
|
||||
processedSpecs.push(spec); |
|
||||
} |
|
||||
}); |
|
||||
specs = processedSpecs; |
|
||||
|
|
||||
// 将规格和价格配对
|
|
||||
specs.forEach((spec, index) => { |
|
||||
if (spec.length > 0 && index < priceArray.length) { |
|
||||
const price = priceArray[index]; |
|
||||
if (price && price.trim() !== '') { |
|
||||
const priceValue = parseFloat(price); |
|
||||
if (!isNaN(priceValue)) { |
|
||||
// 解析规格
|
|
||||
const specInfo = this.parseSpecification(spec); |
|
||||
|
|
||||
// 价格<10的需要按照公式计算
|
|
||||
let finalPrice = priceValue; |
|
||||
if (priceValue < 10 && specInfo) { |
|
||||
if (specInfo.type === '净重') { |
|
||||
// 净重:规格平均值 × 价格
|
|
||||
finalPrice = specInfo.avg * priceValue; |
|
||||
} else if (specInfo.type === '毛重') { |
|
||||
// 毛重:(规格平均值 - 5) × 价格
|
|
||||
finalPrice = (specInfo.avg - 5) * priceValue; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 按规格分组存储价格
|
|
||||
if (!specPriceMap[spec]) { |
|
||||
specPriceMap[spec] = []; |
|
||||
} |
|
||||
specPriceMap[spec].push(finalPrice); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 计算每个规格的平均价格,然后找出最低和最高
|
|
||||
const specAvgPrices = []; |
|
||||
Object.keys(specPriceMap).forEach(spec => { |
|
||||
const prices = specPriceMap[spec]; |
|
||||
if (prices.length > 0) { |
|
||||
const avgPrice = prices.reduce((sum, price) => sum + price, 0) / prices.length; |
|
||||
specAvgPrices.push(avgPrice); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
if (specAvgPrices.length > 0) { |
|
||||
priceRange.min = Math.round(Math.min(...specAvgPrices) * 100) / 100; |
|
||||
priceRange.max = Math.round(Math.max(...specAvgPrices) * 100) / 100; |
|
||||
priceRange.hasPrice = true; |
|
||||
} |
|
||||
|
|
||||
this.setData({ |
|
||||
productNames: uniqueProductNames, |
|
||||
priceRange: priceRange, |
|
||||
loading: false |
|
||||
}); |
|
||||
|
|
||||
// 结束下拉刷新
|
|
||||
wx.stopPullDownRefresh(); |
|
||||
}).catch(err => { |
|
||||
console.error('获取商品列表失败:', err); |
|
||||
this.setData({ |
|
||||
error: '获取商品列表失败,请稍后重试', |
|
||||
loading: false |
|
||||
}); |
|
||||
|
|
||||
// 结束下拉刷新
|
|
||||
wx.stopPullDownRefresh(); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
selectProduct(e) { |
|
||||
const productName = e.currentTarget.dataset.product; |
|
||||
console.log('选择商品:', productName); |
|
||||
// 将商品名称和当前分类存储到本地存储
|
|
||||
wx.setStorageSync('selectedProductName', productName); |
|
||||
wx.setStorageSync('selectedCategory', this.data.category); // 存储当前分类
|
|
||||
// 使用wx.switchTab导航到tabBar页面
|
|
||||
wx.switchTab({ |
|
||||
url: '/pages/evaluate2/index', |
|
||||
success: function(res) { |
|
||||
console.log('跳转成功:', res); |
|
||||
}, |
|
||||
fail: function(err) { |
|
||||
console.error('跳转失败:', err); |
|
||||
} |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 返回分类选择页面
|
|
||||
goBackToCategories() { |
|
||||
console.log('返回分类选择页面'); |
|
||||
wx.redirectTo({ |
|
||||
url: '/pages/evaluate2/one' |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 下拉刷新
|
|
||||
onPullDownRefresh() { |
|
||||
console.log('开始下拉刷新'); |
|
||||
this.loadProductNames(); |
|
||||
}, |
|
||||
|
|
||||
// 分享配置
|
|
||||
onShareAppMessage() { |
|
||||
return { |
|
||||
title: '专业的估价平台,专为估蛋而生', |
|
||||
imageUrl: 'https://my-supplier-photos.oss-cn-chengdu.aliyuncs.com/products/%E7%BD%97%E6%9B%BC%E7%81%B0/image/c00cbcbfd12d747b44621701aed2ae02.jpeg', |
|
||||
path: '/pages/evaluate2/product-list' |
|
||||
}; |
|
||||
}, |
|
||||
|
|
||||
// 朋友圈分享配置
|
|
||||
onShareTimeline() { |
|
||||
return { |
|
||||
title: '专业的估价平台,专为估蛋而生', |
|
||||
imageUrl: 'https://my-supplier-photos.oss-cn-chengdu.aliyuncs.com/products/%E7%BD%97%E6%9B%BC%E7%81%B0/image/c00cbcbfd12d747b44621701aed2ae02.jpeg', |
|
||||
query: '' |
|
||||
}; |
|
||||
} |
|
||||
}); |
|
||||
@ -1,6 +0,0 @@ |
|||||
{ |
|
||||
"usingComponents": {}, |
|
||||
"enablePullDownRefresh": true, |
|
||||
"backgroundTextStyle": "dark", |
|
||||
"disableSwipeBack": true |
|
||||
} |
|
||||
@ -1,97 +0,0 @@ |
|||||
<view class="container"> |
|
||||
<!-- 步骤指示器 --> |
|
||||
<view class="step-indicator"> |
|
||||
<view class="step-item active"> |
|
||||
<view class="step-number">1</view> |
|
||||
<text class="step-text">选择品种</text> |
|
||||
</view> |
|
||||
<view class="step-line active"></view> |
|
||||
<view class="step-item active"> |
|
||||
<view class="step-number">2</view> |
|
||||
<text class="step-text">选择产品</text> |
|
||||
</view> |
|
||||
<view class="step-line"></view> |
|
||||
<view class="step-item"> |
|
||||
<view class="step-number">3</view> |
|
||||
<text class="step-text">选择规格</text> |
|
||||
</view> |
|
||||
<view class="step-line"></view> |
|
||||
<view class="step-item"> |
|
||||
<view class="step-number">4</view> |
|
||||
<text class="step-text">查看估价</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 头部导航栏 --> |
|
||||
<view class="header"> |
|
||||
<view class="header-content"> |
|
||||
<button class="back-button" bindtap="goBackToCategories"> |
|
||||
<text class="back-icon" style="width: 75rpx; display: block; box-sizing: border-box; position: relative; left: -162rpx; top: 2rpx">←</text> |
|
||||
</button> |
|
||||
<text class="title">商品选择</text> |
|
||||
<view class="header-right"></view> <!-- 占位,保持标题居中 --> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<view class="content"> |
|
||||
<!-- 加载中状态 --> |
|
||||
<view wx:if="{{loading}}" class="loading"> |
|
||||
<view class="loading-spinner"></view> |
|
||||
<text class="loading-text">正在加载商品数据...</text> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 非加载状态 --> |
|
||||
<view wx:else> |
|
||||
<!-- 错误提示 --> |
|
||||
<view wx:if="{{error}}" class="error-card"> |
|
||||
<view class="error-icon">⚠️</view> |
|
||||
<text class="error-text">{{error}}</text> |
|
||||
<button bindtap="loadProductNames" class="btn-primary">重新加载</button> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 商品名称列表 --> |
|
||||
<view wx:else class="product-section"> |
|
||||
<view class="section-header"> |
|
||||
<text class="section-title">{{category}}系列的商品列表</text> |
|
||||
<view class="section-count">{{productNames.length}}个商品</view> |
|
||||
</view> |
|
||||
<view class="section-divider"></view> |
|
||||
|
|
||||
<!-- 有商品时显示网格 --> |
|
||||
<view wx:if="{{productNames.length > 0}}" class="product-grid"> |
|
||||
<view |
|
||||
wx:for="{{productNames}}" |
|
||||
wx:key="*this" |
|
||||
class="product-card" |
|
||||
data-product="{{item}}" |
|
||||
bindtap="selectProduct" |
|
||||
> |
|
||||
<view class="product-image-container"> |
|
||||
<image |
|
||||
src="../../images/OIP-C.png" |
|
||||
mode="aspectFill" |
|
||||
class="product-image" |
|
||||
/> |
|
||||
</view> |
|
||||
<text class="product-name">{{item}}</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 无商品时显示空状态 --> |
|
||||
<view wx:if="{{productNames.length === 0}}" class="empty-state"> |
|
||||
<view class="empty-icon">📦</view> |
|
||||
<text class="empty-text"> |
|
||||
{{category ? '该分类下暂无商品' : '暂无商品'}} |
|
||||
</text> |
|
||||
<text class="empty-subtext">试试其他分类吧</text> |
|
||||
<button bindtap="loadProductNames" class="btn-secondary">刷新</button> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 暂无更多商品提示 --> |
|
||||
<view wx:if="{{productNames.length > 0}}" class="no-more"> |
|
||||
<text class="no-more-text">已经到底啦</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
@ -1,608 +0,0 @@ |
|||||
.container { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
min-height: 100vh; |
|
||||
background: linear-gradient(135deg, #F9FAFB 0%, #F2F4F8 100%); |
|
||||
font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif; |
|
||||
overflow-x: hidden; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
/* 步骤指示器 */ |
|
||||
.step-indicator { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
padding: 32rpx; |
|
||||
background: #FFFFFF; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|
||||
margin-bottom: 24rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.step-item { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
flex: 1; |
|
||||
} |
|
||||
|
|
||||
.step-number { |
|
||||
width: 48rpx; |
|
||||
height: 48rpx; |
|
||||
border-radius: 50%; |
|
||||
background: #E5E5E5; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
font-size: 24rpx; |
|
||||
font-weight: 600; |
|
||||
color: #999; |
|
||||
margin-bottom: 8rpx; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.step-text { |
|
||||
font-size: 20rpx; |
|
||||
color: #999; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.step-item.active .step-number { |
|
||||
background: #007AFF; |
|
||||
color: #FFFFFF; |
|
||||
box-shadow: 0 0 0 8rpx rgba(0,122,255,0.1); |
|
||||
} |
|
||||
|
|
||||
.step-item.active .step-text { |
|
||||
color: #007AFF; |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
.step-line { |
|
||||
flex: 1; |
|
||||
height: 2rpx; |
|
||||
background: #E5E5E5; |
|
||||
margin: 0 16rpx; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.step-line.active { |
|
||||
background: #007AFF; |
|
||||
} |
|
||||
|
|
||||
.header { |
|
||||
background: #FFFFFF; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|
||||
position: sticky; |
|
||||
top: 0; |
|
||||
z-index: 10; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.header-content { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
padding: 24rpx 32rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
height: 100rpx; |
|
||||
max-width: 100%; |
|
||||
} |
|
||||
|
|
||||
.title { |
|
||||
font-size: 36rpx; |
|
||||
font-weight: 700; |
|
||||
color: #2c3e50; |
|
||||
letter-spacing: 2rpx; |
|
||||
text-align: center; |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
flex: 1; |
|
||||
position: absolute; |
|
||||
left: 50%; |
|
||||
transform: translateX(-50%); |
|
||||
width: 100%; |
|
||||
max-width: 300rpx; |
|
||||
} |
|
||||
|
|
||||
.header-content { |
|
||||
position: relative; |
|
||||
} |
|
||||
|
|
||||
/* 返回按钮样式 */ |
|
||||
.back-button { |
|
||||
width: 100rpx; |
|
||||
height: 60rpx; |
|
||||
line-height: 60rpx; |
|
||||
font-size: 36rpx; |
|
||||
color: #4a90e2; |
|
||||
background: transparent; |
|
||||
border: none; |
|
||||
padding: 0 16rpx 0 0; |
|
||||
margin: 0; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: flex-start; |
|
||||
border-radius: 8rpx; |
|
||||
transition: all 0.3s ease; |
|
||||
flex-shrink: 0; |
|
||||
overflow: visible; |
|
||||
} |
|
||||
|
|
||||
.back-button:hover { |
|
||||
color: #3b82f6; |
|
||||
background: rgba(74, 144, 226, 0.1); |
|
||||
} |
|
||||
|
|
||||
.back-icon { |
|
||||
margin-right: 8rpx; |
|
||||
font-size: 36rpx; |
|
||||
font-weight: 600; |
|
||||
transition: all 0.3s ease; |
|
||||
display: inline-block; |
|
||||
} |
|
||||
|
|
||||
.back-button:hover .back-icon { |
|
||||
transform: translateX(-12rpx); |
|
||||
font-weight: 700; |
|
||||
} |
|
||||
|
|
||||
.back-text { |
|
||||
font-size: 36rpx; |
|
||||
font-weight: 700; |
|
||||
} |
|
||||
|
|
||||
.title { |
|
||||
font-size: 36rpx; |
|
||||
font-weight: 700; |
|
||||
color: #2c3e50; |
|
||||
letter-spacing: 2rpx; |
|
||||
flex: 1; |
|
||||
text-align: center; |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
.header-right { |
|
||||
width: 60rpx; |
|
||||
flex-shrink: 0; |
|
||||
} |
|
||||
|
|
||||
.content { |
|
||||
flex: 1; |
|
||||
padding: 32rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
overflow-x: hidden; |
|
||||
} |
|
||||
|
|
||||
/* 加载状态 */ |
|
||||
.loading { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
bottom: 0; |
|
||||
background: rgba(255, 255, 255, 0.9); |
|
||||
z-index: 9999; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.loading-spinner { |
|
||||
width: 80rpx; |
|
||||
height: 80rpx; |
|
||||
border: 8rpx solid rgba(74, 144, 226, 0.2); |
|
||||
border-top: 8rpx solid #4a90e2; |
|
||||
border-radius: 50%; |
|
||||
animation: spin 1s linear infinite; |
|
||||
margin-bottom: 32rpx; |
|
||||
} |
|
||||
|
|
||||
@keyframes spin { |
|
||||
0% { transform: rotate(0deg); } |
|
||||
100% { transform: rotate(360deg); } |
|
||||
} |
|
||||
|
|
||||
.loading-text { |
|
||||
font-size: 28rpx; |
|
||||
color: #666; |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
/* 错误提示卡片 */ |
|
||||
.error-card { |
|
||||
background: #fff; |
|
||||
border-radius: 16rpx; |
|
||||
padding: 48rpx; |
|
||||
margin: 32rpx 0; |
|
||||
box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.08); |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
text-align: center; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.error-icon { |
|
||||
font-size: 80rpx; |
|
||||
margin-bottom: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.error-text { |
|
||||
font-size: 28rpx; |
|
||||
color: #e74c3c; |
|
||||
margin-bottom: 32rpx; |
|
||||
line-height: 1.5; |
|
||||
} |
|
||||
|
|
||||
/* 按钮样式 */ |
|
||||
.btn-primary { |
|
||||
width: 240rpx; |
|
||||
height: 80rpx; |
|
||||
line-height: 80rpx; |
|
||||
font-size: 28rpx; |
|
||||
font-weight: 600; |
|
||||
border-radius: 40rpx; |
|
||||
background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%); |
|
||||
color: #fff; |
|
||||
border: none; |
|
||||
box-shadow: 0 4rpx 12rpx rgba(96, 165, 250, 0.4); |
|
||||
transition: all 0.3s ease; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.btn-primary:hover { |
|
||||
transform: translateY(-2rpx); |
|
||||
box-shadow: 0 6rpx 16rpx rgba(74, 144, 226, 0.5); |
|
||||
} |
|
||||
|
|
||||
/* 商品列表区域 */ |
|
||||
.product-section { |
|
||||
margin-top: 24rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
/* 当前选择的分类 */ |
|
||||
.current-category { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
background: #F8F9FA; |
|
||||
padding: 24rpx 32rpx; |
|
||||
border-radius: 16rpx; |
|
||||
margin-bottom: 32rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.current-category-content { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
margin-bottom: 8rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.current-category-name { |
|
||||
font-size: 36rpx; |
|
||||
font-weight: 600; |
|
||||
color: #333; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
/* 价格范围 */ |
|
||||
.price-range { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.price-range-value { |
|
||||
font-size: 28rpx; |
|
||||
color: #666; |
|
||||
} |
|
||||
|
|
||||
.section-header { |
|
||||
display: flex; |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
margin-bottom: 24rpx; |
|
||||
padding: 0; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.section-title { |
|
||||
font-size: 32rpx; |
|
||||
font-weight: 600; |
|
||||
color: #2c3e50; |
|
||||
padding-left: 0; |
|
||||
border-left: none; |
|
||||
} |
|
||||
|
|
||||
.section-count { |
|
||||
font-size: 28rpx; |
|
||||
color: #999; |
|
||||
background: #F0F0F0; |
|
||||
padding: 8rpx 16rpx; |
|
||||
border-radius: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.section-divider { |
|
||||
height: 1rpx; |
|
||||
background: #EDEDED; |
|
||||
margin-bottom: 24rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
/* 商品网格布局 */ |
|
||||
.product-grid { |
|
||||
display: grid; |
|
||||
grid-template-columns: repeat(2, 1fr); |
|
||||
gap: 24rpx; |
|
||||
margin-top: 0; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
overflow-x: hidden; |
|
||||
} |
|
||||
|
|
||||
/* 商品卡片 */ |
|
||||
.product-card { |
|
||||
background: #FFFFFF; |
|
||||
border-radius: 16rpx; |
|
||||
padding: 16rpx; |
|
||||
cursor: pointer; |
|
||||
min-height: 240rpx; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
justify-content: flex-start; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.04); |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.product-image-container { |
|
||||
width: 100%; |
|
||||
height: 160rpx; |
|
||||
margin-bottom: 16rpx; |
|
||||
border-radius: 12rpx; |
|
||||
overflow: hidden; |
|
||||
background: #F5F5F5; |
|
||||
} |
|
||||
|
|
||||
.product-image { |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
object-fit: cover; |
|
||||
} |
|
||||
|
|
||||
.product-card:hover { |
|
||||
box-shadow: 0 8rpx 20rpx rgba(0,0,0,0.08); |
|
||||
transform: translateY(-2rpx); |
|
||||
} |
|
||||
|
|
||||
.product-name { |
|
||||
font-size: 28rpx; |
|
||||
font-weight: 500; |
|
||||
color: #333; |
|
||||
line-height: 1.4; |
|
||||
word-break: break-word; |
|
||||
text-align: center; |
|
||||
padding: 0 8rpx; |
|
||||
display: -webkit-box; |
|
||||
-webkit-line-clamp: 2; |
|
||||
-webkit-box-orient: vertical; |
|
||||
overflow: hidden; |
|
||||
} |
|
||||
|
|
||||
/* 响应式设计 */ |
|
||||
@media (max-width: 750rpx) { |
|
||||
.content { |
|
||||
padding: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.product-grid { |
|
||||
gap: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.product-card { |
|
||||
padding: 16rpx; |
|
||||
min-height: 220rpx; |
|
||||
} |
|
||||
|
|
||||
.product-image-container { |
|
||||
height: 140rpx; |
|
||||
} |
|
||||
|
|
||||
.title { |
|
||||
font-size: 32rpx; |
|
||||
} |
|
||||
|
|
||||
.section-title { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.product-name { |
|
||||
font-size: 26rpx; |
|
||||
} |
|
||||
|
|
||||
.current-category { |
|
||||
padding: 20rpx 24rpx; |
|
||||
} |
|
||||
|
|
||||
.current-category-name { |
|
||||
font-size: 32rpx; |
|
||||
} |
|
||||
|
|
||||
.price-range-value { |
|
||||
font-size: 24rpx; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 小屏幕设备适配 */ |
|
||||
@media (max-width: 414rpx) { |
|
||||
.content { |
|
||||
padding: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.product-grid { |
|
||||
gap: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.product-card { |
|
||||
padding: 16rpx; |
|
||||
min-height: 200rpx; |
|
||||
} |
|
||||
|
|
||||
.product-image-container { |
|
||||
height: 120rpx; |
|
||||
} |
|
||||
|
|
||||
.product-name { |
|
||||
font-size: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.title { |
|
||||
font-size: 32rpx; |
|
||||
} |
|
||||
|
|
||||
.section-title { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.current-category { |
|
||||
padding: 16rpx 20rpx; |
|
||||
} |
|
||||
|
|
||||
.current-category-name { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.price-range-value { |
|
||||
font-size: 22rpx; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 大屏幕适配 */ |
|
||||
@media (min-width: 900px) { |
|
||||
.product-grid { |
|
||||
grid-template-columns: repeat(4, 1fr); |
|
||||
max-width: 1200rpx; |
|
||||
margin: 0 auto; |
|
||||
} |
|
||||
|
|
||||
.current-category { |
|
||||
max-width: 1200rpx; |
|
||||
margin: 0 auto 32rpx; |
|
||||
} |
|
||||
|
|
||||
.section-header { |
|
||||
max-width: 1200rpx; |
|
||||
margin: 0 auto 24rpx; |
|
||||
} |
|
||||
|
|
||||
.section-divider { |
|
||||
max-width: 1200rpx; |
|
||||
margin: 0 auto 24rpx; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 平板适配 */ |
|
||||
@media (min-width: 600px) and (max-width: 899px) { |
|
||||
.product-grid { |
|
||||
grid-template-columns: repeat(3, 1fr); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 暂无更多商品提示 */ |
|
||||
.no-more { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
padding: 48rpx 0; |
|
||||
margin-top: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.no-more::before, |
|
||||
.no-more::after { |
|
||||
content: ''; |
|
||||
flex: 1; |
|
||||
height: 1rpx; |
|
||||
background: #E5E5E5; |
|
||||
margin: 0 16rpx; |
|
||||
} |
|
||||
|
|
||||
.no-more-text { |
|
||||
font-size: 24rpx; |
|
||||
color: #BBB; |
|
||||
font-weight: 500; |
|
||||
white-space: nowrap; |
|
||||
} |
|
||||
|
|
||||
/* 空状态 */ |
|
||||
.empty-state { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
padding: 120rpx 0; |
|
||||
text-align: center; |
|
||||
} |
|
||||
|
|
||||
.empty-icon { |
|
||||
font-size: 120rpx; |
|
||||
margin-bottom: 40rpx; |
|
||||
opacity: 0.6; |
|
||||
color: #CCC; |
|
||||
} |
|
||||
|
|
||||
.empty-text { |
|
||||
font-size: 32rpx; |
|
||||
font-weight: 600; |
|
||||
color: #333; |
|
||||
margin-bottom: 16rpx; |
|
||||
line-height: 1.4; |
|
||||
padding: 0 40rpx; |
|
||||
} |
|
||||
|
|
||||
.empty-subtext { |
|
||||
font-size: 28rpx; |
|
||||
color: #999; |
|
||||
margin-bottom: 48rpx; |
|
||||
line-height: 1.4; |
|
||||
padding: 0 40rpx; |
|
||||
} |
|
||||
|
|
||||
.btn-secondary { |
|
||||
width: 200rpx; |
|
||||
height: 80rpx; |
|
||||
line-height: 80rpx; |
|
||||
font-size: 32rpx; |
|
||||
font-weight: 600; |
|
||||
border-radius: 48rpx; |
|
||||
background: #fff; |
|
||||
color: #4a90e2; |
|
||||
border: 1rpx solid #4a90e2; |
|
||||
transition: all 0.3s ease; |
|
||||
padding: 0 48rpx; |
|
||||
} |
|
||||
|
|
||||
.btn-secondary:hover { |
|
||||
background: rgba(74, 144, 226, 0.05); |
|
||||
transform: translateY(-2rpx); |
|
||||
} |
|
||||
@ -1,125 +0,0 @@ |
|||||
Page({ |
|
||||
data: { |
|
||||
productName: '', |
|
||||
category: '', |
|
||||
specification: '', |
|
||||
price: 0, |
|
||||
quantity: 1, |
|
||||
totalPrice: 0, |
|
||||
loading: false, |
|
||||
error: '' |
|
||||
}, |
|
||||
onLoad(options) { |
|
||||
console.log('接收到的参数:', options); |
|
||||
// 即使参数不完整,也要尝试获取并设置
|
|
||||
const productName = options.productName ? decodeURIComponent(options.productName) : ''; |
|
||||
const category = options.category ? decodeURIComponent(options.category) : ''; |
|
||||
const specification = options.specification ? decodeURIComponent(options.specification) : ''; |
|
||||
let price = 0; |
|
||||
|
|
||||
if (options.price) { |
|
||||
const decodedPrice = decodeURIComponent(options.price); |
|
||||
console.log('解码后的价格:', decodedPrice); |
|
||||
price = parseFloat(decodedPrice) || 0; |
|
||||
} |
|
||||
|
|
||||
console.log('解析后的参数:', { productName, category, specification, price }); |
|
||||
|
|
||||
this.setData({ |
|
||||
productName: productName, |
|
||||
category: category, |
|
||||
specification: specification, |
|
||||
price: price, |
|
||||
totalPrice: 0 // 初始时总价为0,不显示
|
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 件数输入变化
|
|
||||
onQuantityChange(e) { |
|
||||
const quantity = parseInt(e.detail.value) || 1; |
|
||||
this.setData({ |
|
||||
quantity: quantity |
|
||||
// 只更新件数,不更新总价,等待点击计算按钮
|
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 减少数量
|
|
||||
decreaseQuantity() { |
|
||||
if (this.data.quantity > 1) { |
|
||||
this.setData({ |
|
||||
quantity: this.data.quantity - 1 |
|
||||
}); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
// 增加数量
|
|
||||
increaseQuantity() { |
|
||||
this.setData({ |
|
||||
quantity: this.data.quantity + 1 |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 计算价格
|
|
||||
calculatePrice() { |
|
||||
const totalPrice = Math.round(this.data.price * this.data.quantity * 10) / 10; |
|
||||
this.setData({ |
|
||||
totalPrice: totalPrice |
|
||||
}); |
|
||||
wx.showToast({ |
|
||||
title: '计算完成', |
|
||||
icon: 'success', |
|
||||
duration: 1000 |
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
// 返回上一页
|
|
||||
goBack() { |
|
||||
wx.navigateBack(); |
|
||||
}, |
|
||||
|
|
||||
// 下拉刷新
|
|
||||
onPullDownRefresh() { |
|
||||
console.log('开始下拉刷新'); |
|
||||
// 重新加载页面数据
|
|
||||
// 由于spec-detail页面的数据是通过URL参数传递的,这里可以重新获取参数并设置数据
|
|
||||
const options = this.options || {}; |
|
||||
const productName = options.productName ? decodeURIComponent(options.productName) : ''; |
|
||||
const category = options.category ? decodeURIComponent(options.category) : ''; |
|
||||
const specification = options.specification ? decodeURIComponent(options.specification) : ''; |
|
||||
let price = 0; |
|
||||
|
|
||||
if (options.price) { |
|
||||
const decodedPrice = decodeURIComponent(options.price); |
|
||||
price = parseFloat(decodedPrice) || 0; |
|
||||
} |
|
||||
|
|
||||
this.setData({ |
|
||||
productName: productName, |
|
||||
category: category, |
|
||||
specification: specification, |
|
||||
price: price, |
|
||||
totalPrice: 0 // 初始时总价为0,不显示
|
|
||||
}); |
|
||||
|
|
||||
// 结束下拉刷新
|
|
||||
wx.stopPullDownRefresh(); |
|
||||
}, |
|
||||
|
|
||||
// 分享配置
|
|
||||
onShareAppMessage() { |
|
||||
return { |
|
||||
title: '专业的估价平台,专为估蛋而生', |
|
||||
imageUrl: 'https://my-supplier-photos.oss-cn-chengdu.aliyuncs.com/products/%E7%BD%97%E6%9B%BC%E7%81%B0/image/c00cbcbfd12d747b44621701aed2ae02.jpeg', |
|
||||
path: '/pages/evaluate2/spec-detail' |
|
||||
}; |
|
||||
}, |
|
||||
|
|
||||
// 朋友圈分享配置
|
|
||||
onShareTimeline() { |
|
||||
return { |
|
||||
title: '专业的估价平台,专为估蛋而生', |
|
||||
imageUrl: 'https://my-supplier-photos.oss-cn-chengdu.aliyuncs.com/products/%E7%BD%97%E6%9B%BC%E7%81%B0/image/c00cbcbfd12d747b44621701aed2ae02.jpeg', |
|
||||
query: '' |
|
||||
}; |
|
||||
} |
|
||||
}); |
|
||||
@ -1,5 +0,0 @@ |
|||||
{ |
|
||||
"usingComponents": {}, |
|
||||
"enablePullDownRefresh": true, |
|
||||
"backgroundTextStyle": "dark" |
|
||||
} |
|
||||
@ -1,82 +0,0 @@ |
|||||
<view class="page-container"> |
|
||||
<!-- 步骤指示器 --> |
|
||||
<view class="step-indicator"> |
|
||||
<view class="step-item active"> |
|
||||
<view class="step-number">1</view> |
|
||||
<text class="step-text">选择品种</text> |
|
||||
</view> |
|
||||
<view class="step-line active"></view> |
|
||||
<view class="step-item active"> |
|
||||
<view class="step-number">2</view> |
|
||||
<text class="step-text">选择产品</text> |
|
||||
</view> |
|
||||
<view class="step-line active"></view> |
|
||||
<view class="step-item active"> |
|
||||
<view class="step-number">3</view> |
|
||||
<text class="step-text">选择规格</text> |
|
||||
</view> |
|
||||
<view class="step-line active"></view> |
|
||||
<view class="step-item active"> |
|
||||
<view class="step-number">4</view> |
|
||||
<text class="step-text">查看估价</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 主要内容区域 --> |
|
||||
<view class="main-content"> |
|
||||
<!-- 规格信息卡片 --> |
|
||||
<view class="info-card"> |
|
||||
<text class="card-subtitle">规格信息</text> |
|
||||
<view class="info-row"> |
|
||||
<text class="info-label">分类</text> |
|
||||
<text class="info-value">{{category}}</text> |
|
||||
</view> |
|
||||
<view class="info-row"> |
|
||||
<text class="info-label">名称</text> |
|
||||
<text class="info-value">{{productName}}</text> |
|
||||
</view> |
|
||||
<view class="info-row"> |
|
||||
<text class="info-label">规格</text> |
|
||||
<text class="info-value">{{specification}}</text> |
|
||||
</view> |
|
||||
|
|
||||
<text class="info-hint">您已选择此规格进行估价</text> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 数量设置卡片 --> |
|
||||
<view class="control-card"> |
|
||||
<text class="card-subtitle">件数设置</text> |
|
||||
<view class="quantity-box"> |
|
||||
<button bindtap="decreaseQuantity" class="quantity-btn minus" style="width: 180rpx; display: flex; box-sizing: border-box; left: 0rpx; top: 0rpx">-</button> |
|
||||
<input style="width: 420rpx; display: block; box-sizing: border-box" |
|
||||
class="quantity-input" |
|
||||
type="number" |
|
||||
value="{{quantity}}" |
|
||||
bindinput="onQuantityChange" |
|
||||
min="1" |
|
||||
/> |
|
||||
<button bindtap="increaseQuantity" class="quantity-btn plus" style="width: 180rpx; display: flex; box-sizing: border-box; left: 0rpx; top: 0rpx">+</button> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 计算按钮 --> |
|
||||
<view class="button-section"> |
|
||||
<button bindtap="calculatePrice" class="primary-btn" style="height: 120rpx; display: block; box-sizing: border-box; left: 0rpx; top: 0rpx">计算预估价格</button> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 结果展示卡片 --> |
|
||||
<view class="result-card" wx:if="{{totalPrice > 0}}"> |
|
||||
<text class="card-subtitle">预估结果</text> |
|
||||
<view class="result-row"> |
|
||||
<text class="result-label">预估总价</text> |
|
||||
<text class="result-value">¥{{totalPrice}}</text> |
|
||||
</view> |
|
||||
<text class="result-hint">此价格为预估价格,仅供参考,解释权归又鸟蛋平台,实际价格可能会有所变动</text> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 返回按钮 --> |
|
||||
<view class="button-section bottom"> |
|
||||
<button bindtap="goBack" class="secondary-btn" style="height: 120rpx; display: block; box-sizing: border-box; left: 0rpx; top: 0rpx">返回规格列表</button> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
@ -1,465 +0,0 @@ |
|||||
/* 页面容器 */ |
|
||||
.page-container { |
|
||||
min-height: 100vh; |
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); |
|
||||
font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
/* 步骤指示器 */ |
|
||||
.step-indicator { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
padding: 32rpx; |
|
||||
background: #FFFFFF; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|
||||
margin-bottom: 24rpx; |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.step-item { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
flex: 1; |
|
||||
} |
|
||||
|
|
||||
.step-number { |
|
||||
width: 48rpx; |
|
||||
height: 48rpx; |
|
||||
border-radius: 50%; |
|
||||
background: #E5E5E5; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
font-size: 24rpx; |
|
||||
font-weight: 600; |
|
||||
color: #999; |
|
||||
margin-bottom: 8rpx; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.step-text { |
|
||||
font-size: 20rpx; |
|
||||
color: #999; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.step-item.active .step-number { |
|
||||
background: #007AFF; |
|
||||
color: #FFFFFF; |
|
||||
box-shadow: 0 0 0 8rpx rgba(0,122,255,0.1); |
|
||||
} |
|
||||
|
|
||||
.step-item.active .step-text { |
|
||||
color: #007AFF; |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
.step-line { |
|
||||
flex: 1; |
|
||||
height: 2rpx; |
|
||||
background: #E5E5E5; |
|
||||
margin: 0 16rpx; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.step-line.active { |
|
||||
background: #007AFF; |
|
||||
} |
|
||||
|
|
||||
/* 头部样式 */ |
|
||||
.page-header { |
|
||||
background: rgba(255, 255, 255, 0.95); |
|
||||
backdrop-filter: blur(10rpx); |
|
||||
box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.05); |
|
||||
padding: 28rpx 0; |
|
||||
text-align: center; |
|
||||
position: sticky; |
|
||||
top: 0; |
|
||||
z-index: 10; |
|
||||
} |
|
||||
|
|
||||
.header-title { |
|
||||
font-size: 30rpx; |
|
||||
font-weight: 700; |
|
||||
color: #2c3e50; |
|
||||
letter-spacing: 1rpx; |
|
||||
} |
|
||||
|
|
||||
/* 主要内容区域 */ |
|
||||
.main-content { |
|
||||
flex: 1; |
|
||||
padding: 24rpx; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
gap: 24rpx; |
|
||||
padding-bottom: 48rpx; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
/* 卡片基础样式 */ |
|
||||
.info-card, |
|
||||
.control-card, |
|
||||
.result-card { |
|
||||
background: rgba(255, 255, 255, 0.95); |
|
||||
border-radius: 24rpx; |
|
||||
padding: 32rpx; |
|
||||
box-shadow: 0 6rpx 24rpx rgba(0,0,0,0.08); |
|
||||
backdrop-filter: blur(12rpx); |
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.4); |
|
||||
transition: all 0.3s ease; |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
.info-card:hover, |
|
||||
.control-card:hover, |
|
||||
.result-card:hover { |
|
||||
box-shadow: 0 8rpx 32rpx rgba(0,0,0,0.12); |
|
||||
transform: translateY(-4rpx); |
|
||||
background: rgba(255, 255, 255, 0.98); |
|
||||
} |
|
||||
|
|
||||
/* 卡片标题 */ |
|
||||
.card-subtitle { |
|
||||
font-size: 26rpx; |
|
||||
font-weight: 600; |
|
||||
color: #64748b; |
|
||||
margin-bottom: 24rpx; |
|
||||
padding-bottom: 16rpx; |
|
||||
border-bottom: 1rpx solid #f1f5f9; |
|
||||
} |
|
||||
|
|
||||
/* 信息行样式 */ |
|
||||
.info-row { |
|
||||
display: flex; |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
padding: 16rpx 0; |
|
||||
border-bottom: 1rpx solid #f8fafc; |
|
||||
} |
|
||||
|
|
||||
.info-row:last-child { |
|
||||
border-bottom: none; |
|
||||
} |
|
||||
|
|
||||
.info-label { |
|
||||
font-size: 26rpx; |
|
||||
color: #64748b; |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
.info-value { |
|
||||
font-size: 26rpx; |
|
||||
font-weight: 600; |
|
||||
color: #2c3e50; |
|
||||
} |
|
||||
|
|
||||
.info-value.price { |
|
||||
color: #ef4444; |
|
||||
font-size: 32rpx; |
|
||||
font-weight: 700; |
|
||||
} |
|
||||
|
|
||||
/* 提示信息 */ |
|
||||
.info-hint { |
|
||||
font-size: 22rpx; |
|
||||
color: #94a3b8; |
|
||||
text-align: center; |
|
||||
margin-top: 20rpx; |
|
||||
padding-top: 20rpx; |
|
||||
border-top: 1rpx solid #f1f5f9; |
|
||||
} |
|
||||
|
|
||||
/* 数量控制 */ |
|
||||
.quantity-box { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: 24rpx; |
|
||||
margin-top: 12rpx; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
|
|
||||
.quantity-btn { |
|
||||
width: 80rpx; |
|
||||
height: 80rpx; |
|
||||
border-radius: 40rpx; |
|
||||
font-size: 36rpx; |
|
||||
font-weight: bold; |
|
||||
background: rgba(255, 255, 255, 0.9); |
|
||||
color: #3b82f6; |
|
||||
border: 2rpx solid rgba(96, 165, 250, 0.4); |
|
||||
backdrop-filter: blur(12rpx); |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
transition: all 0.3s ease; |
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.08); |
|
||||
} |
|
||||
|
|
||||
.quantity-btn:hover { |
|
||||
background: rgba(59, 130, 246, 0.1); |
|
||||
border-color: rgba(59, 130, 246, 0.6); |
|
||||
transform: scale(1.1); |
|
||||
} |
|
||||
|
|
||||
.quantity-btn.minus:hover { |
|
||||
background: rgba(239, 68, 68, 0.1); |
|
||||
border-color: rgba(239, 68, 68, 0.6); |
|
||||
color: #ef4444; |
|
||||
} |
|
||||
|
|
||||
.quantity-btn.plus:hover { |
|
||||
background: rgba(16, 185, 129, 0.1); |
|
||||
border-color: rgba(16, 185, 129, 0.6); |
|
||||
color: #10b981; |
|
||||
} |
|
||||
|
|
||||
.quantity-input { |
|
||||
width: 160rpx; |
|
||||
height: 80rpx; |
|
||||
border: 2rpx solid rgba(96, 165, 250, 0.4); |
|
||||
border-radius: 16rpx; |
|
||||
padding: 0 24rpx; |
|
||||
font-size: 28rpx; |
|
||||
font-weight: 600; |
|
||||
text-align: center; |
|
||||
color: #2c3e50; |
|
||||
background: rgba(255, 255, 255, 0.95); |
|
||||
backdrop-filter: blur(12rpx); |
|
||||
box-shadow: inset 0 2rpx 8rpx rgba(0,0,0,0.08); |
|
||||
box-sizing: border-box; |
|
||||
} |
|
||||
|
|
||||
/* 按钮区域 */ |
|
||||
.button-section { |
|
||||
margin-top: 12rpx; |
|
||||
} |
|
||||
|
|
||||
.button-section.bottom { |
|
||||
margin-top: 20rpx; |
|
||||
} |
|
||||
|
|
||||
/* 按钮样式 */ |
|
||||
.primary-btn { |
|
||||
width: 100%; |
|
||||
height: 92rpx; |
|
||||
line-height: 92rpx; |
|
||||
font-size: 28rpx; |
|
||||
font-weight: 600; |
|
||||
border-radius: 46rpx; |
|
||||
background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); |
|
||||
color: #fff; |
|
||||
border: none; |
|
||||
box-shadow: 0 6rpx 24rpx rgba(59, 130, 246, 0.4); |
|
||||
transition: all 0.3s ease; |
|
||||
text-align: center; |
|
||||
} |
|
||||
|
|
||||
.primary-btn:hover { |
|
||||
transform: translateY(-4rpx); |
|
||||
box-shadow: 0 8rpx 32rpx rgba(59, 130, 246, 0.5); |
|
||||
} |
|
||||
|
|
||||
.secondary-btn { |
|
||||
width: 100%; |
|
||||
height: 84rpx; |
|
||||
line-height: 84rpx; |
|
||||
font-size: 26rpx; |
|
||||
font-weight: 600; |
|
||||
border-radius: 42rpx; |
|
||||
background: rgba(255, 255, 255, 0.95); |
|
||||
color: #3b82f6; |
|
||||
border: 2rpx solid rgba(59, 130, 246, 0.4); |
|
||||
backdrop-filter: blur(12rpx); |
|
||||
transition: all 0.3s ease; |
|
||||
text-align: center; |
|
||||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.08); |
|
||||
} |
|
||||
|
|
||||
.secondary-btn:hover { |
|
||||
background: rgba(59, 130, 246, 0.08); |
|
||||
border-color: rgba(59, 130, 246, 0.6); |
|
||||
transform: translateY(-4rpx); |
|
||||
box-shadow: 0 6rpx 24rpx rgba(59, 130, 246, 0.3); |
|
||||
} |
|
||||
|
|
||||
/* 结果卡片 */ |
|
||||
.result-card { |
|
||||
animation: fadeInUp 0.6s ease-out; |
|
||||
} |
|
||||
|
|
||||
@keyframes fadeInUp { |
|
||||
from { |
|
||||
opacity: 0; |
|
||||
transform: translateY(30rpx); |
|
||||
} |
|
||||
to { |
|
||||
opacity: 1; |
|
||||
transform: translateY(0); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 结果行 */ |
|
||||
.result-row { |
|
||||
display: flex; |
|
||||
justify-content: space-between; |
|
||||
align-items: center; |
|
||||
padding: 20rpx 0; |
|
||||
} |
|
||||
|
|
||||
.result-label { |
|
||||
font-size: 26rpx; |
|
||||
color: #64748b; |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
.result-value { |
|
||||
font-size: 36rpx; |
|
||||
font-weight: 700; |
|
||||
color: #ef4444; |
|
||||
text-shadow: 0 2rpx 4rpx rgba(239, 68, 68, 0.2); |
|
||||
} |
|
||||
|
|
||||
/* 结果提示 */ |
|
||||
.result-hint { |
|
||||
font-size: 22rpx; |
|
||||
color: #94a3b8; |
|
||||
text-align: center; |
|
||||
margin-top: 16rpx; |
|
||||
padding-top: 16rpx; |
|
||||
border-top: 1rpx solid #f1f5f9; |
|
||||
line-height: 1.5; |
|
||||
} |
|
||||
|
|
||||
/* 响应式设计 */ |
|
||||
@media (max-width: 750rpx) { |
|
||||
.main-content { |
|
||||
padding: 20rpx; |
|
||||
gap: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.info-card, |
|
||||
.control-card, |
|
||||
.result-card { |
|
||||
padding: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.header-title { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.card-subtitle { |
|
||||
font-size: 24rpx; |
|
||||
margin-bottom: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.info-label, |
|
||||
.info-value { |
|
||||
font-size: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.info-value.price { |
|
||||
font-size: 30rpx; |
|
||||
} |
|
||||
|
|
||||
.result-value { |
|
||||
font-size: 32rpx; |
|
||||
} |
|
||||
|
|
||||
.quantity-box { |
|
||||
gap: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.quantity-btn { |
|
||||
width: 72rpx; |
|
||||
height: 72rpx; |
|
||||
font-size: 32rpx; |
|
||||
} |
|
||||
|
|
||||
.quantity-input { |
|
||||
width: 140rpx; |
|
||||
height: 72rpx; |
|
||||
font-size: 26rpx; |
|
||||
} |
|
||||
|
|
||||
.primary-btn { |
|
||||
height: 84rpx; |
|
||||
line-height: 84rpx; |
|
||||
font-size: 26rpx; |
|
||||
} |
|
||||
|
|
||||
.secondary-btn { |
|
||||
height: 76rpx; |
|
||||
line-height: 76rpx; |
|
||||
font-size: 24rpx; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 小屏幕设备适配 */ |
|
||||
@media (max-width: 375rpx) { |
|
||||
.main-content { |
|
||||
padding: 16rpx; |
|
||||
gap: 16rpx; |
|
||||
} |
|
||||
|
|
||||
.info-card, |
|
||||
.control-card, |
|
||||
.result-card { |
|
||||
padding: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.header-title { |
|
||||
font-size: 26rpx; |
|
||||
} |
|
||||
|
|
||||
.card-subtitle { |
|
||||
font-size: 22rpx; |
|
||||
margin-bottom: 16rpx; |
|
||||
} |
|
||||
|
|
||||
.info-label, |
|
||||
.info-value { |
|
||||
font-size: 22rpx; |
|
||||
} |
|
||||
|
|
||||
.info-value.price { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.result-value { |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.quantity-box { |
|
||||
gap: 16rpx; |
|
||||
} |
|
||||
|
|
||||
.quantity-btn { |
|
||||
width: 64rpx; |
|
||||
height: 64rpx; |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
|
|
||||
.quantity-input { |
|
||||
width: 120rpx; |
|
||||
height: 64rpx; |
|
||||
font-size: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.primary-btn { |
|
||||
height: 76rpx; |
|
||||
line-height: 76rpx; |
|
||||
font-size: 24rpx; |
|
||||
} |
|
||||
|
|
||||
.secondary-btn { |
|
||||
height: 68rpx; |
|
||||
line-height: 68rpx; |
|
||||
font-size: 22rpx; |
|
||||
} |
|
||||
} |
|
||||
File diff suppressed because it is too large
Loading…
Reference in new issue