15 changed files with 1122 additions and 5 deletions
@ -0,0 +1,237 @@ |
|||
Page({ |
|||
data: { |
|||
productName: '', |
|||
specifications: [], |
|||
loading: false, |
|||
error: '' |
|||
}, |
|||
onLoad(options) { |
|||
let productName = ''; |
|||
// 首先检查URL参数
|
|||
if (options.productName) { |
|||
productName = options.productName; |
|||
} else { |
|||
// 然后检查本地存储(用于wx.switchTab导航)
|
|||
productName = wx.getStorageSync('selectedProductName') || ''; |
|||
// 清除本地存储中的商品名称,避免影响下次进入
|
|||
if (productName) { |
|||
wx.removeStorageSync('selectedProductName'); |
|||
} |
|||
} |
|||
|
|||
if (productName) { |
|||
this.setData({ productName: productName }); |
|||
this.loadSpecifications(productName); |
|||
} else { |
|||
// 如果没有商品名称参数,跳转到商品列表页面
|
|||
wx.redirectTo({ |
|||
url: '/pages/evaluate1/product-list' |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
loadSpecifications(productName) { |
|||
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'); |
|||
api.getProducts().then(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('未找到对应商品名称的商品'); |
|||
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 (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()); |
|||
|
|||
console.log(`规格数组:`, specs); |
|||
console.log(`价格数组:`, prices); |
|||
|
|||
// 将规格和价格配对
|
|||
specs.forEach((spec, index) => { |
|||
if (spec.length > 0) { |
|||
// 确保价格索引不超出范围
|
|||
const priceIndex = index % prices.length; |
|||
const matchedPrice = prices[priceIndex] || ''; |
|||
console.log(`规格'${spec}' 配对价格: '${matchedPrice}'`); |
|||
|
|||
// 只有当价格不为空时才添加该规格
|
|||
if (matchedPrice) { |
|||
// 直接使用商品的原始价格,不做任何处理
|
|||
specPriceMap[spec] = matchedPrice; |
|||
} else { |
|||
console.log(`规格'${spec}' 价格为空,不添加`); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
}); |
|||
|
|||
console.log('规格价格映射:', specPriceMap); |
|||
|
|||
// 检查specPriceMap是否为空
|
|||
if (Object.keys(specPriceMap).length === 0) { |
|||
console.error('未提取到有效规格'); |
|||
this.setData({ |
|||
error: '未提取到有效规格', |
|||
loading: false |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
// 转换为规格对象数组
|
|||
const specifications = Object.keys(specPriceMap).map(spec => { |
|||
const price = specPriceMap[spec]; |
|||
console.log(`最终规格'${spec}' 对应价格: '${price}'`); |
|||
|
|||
// 解析规格
|
|||
const specInfo = this.parseSpecification(spec); |
|||
let finalPrice = price; |
|||
let finalPriceText = price; |
|||
|
|||
// 根据规格类型和价格水平计算最终价格
|
|||
if (specInfo && parseFloat(price) < 10) { |
|||
if (specInfo.type === '净重') { |
|||
// 净重:规格平均值 × 价格
|
|||
finalPrice = specInfo.avg * parseFloat(price); |
|||
finalPriceText = finalPrice.toFixed(2); |
|||
console.log(`规格'${spec}' 是净重,计算最终价格: ${finalPriceText}`); |
|||
} else if (specInfo.type === '毛重') { |
|||
// 毛重:(规格平均值 - 5) × 价格
|
|||
finalPrice = (specInfo.avg - 5) * parseFloat(price); |
|||
finalPriceText = finalPrice.toFixed(2); |
|||
console.log(`规格'${spec}' 是毛重,计算最终价格: ${finalPriceText}`); |
|||
} |
|||
} |
|||
|
|||
return { |
|||
name: spec, |
|||
price: price, |
|||
priceText: price, |
|||
finalPrice: finalPrice, |
|||
finalPriceText: finalPriceText |
|||
}; |
|||
}); |
|||
|
|||
console.log('提取的规格和价格:', specifications); |
|||
|
|||
this.setData({ |
|||
specifications: specifications, |
|||
error: '', // 清除之前的错误
|
|||
loading: false |
|||
}); |
|||
}, |
|||
|
|||
// 跳转到规格详情页面
|
|||
goToSpecDetail(e) { |
|||
const specItem = e.currentTarget.dataset.spec; |
|||
wx.navigateTo({ |
|||
url: `/pages/evaluate1/spec-detail?productName=${encodeURIComponent(this.data.productName)}&specification=${encodeURIComponent(specItem.name)}&price=${encodeURIComponent(specItem.finalPrice)}` |
|||
}); |
|||
}, |
|||
|
|||
// 返回上一页
|
|||
goBack() { |
|||
wx.navigateBack(); |
|||
}, |
|||
|
|||
// 返回商品列表页面
|
|||
goBackToProductList() { |
|||
wx.redirectTo({ |
|||
url: '/pages/evaluate1/product-list' |
|||
}); |
|||
} |
|||
}); |
|||
@ -0,0 +1,4 @@ |
|||
{ |
|||
"navigationBarTitleText": "估价", |
|||
"usingComponents": {} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
<view class="container"> |
|||
<view class="header"> |
|||
<text class="title">选择规格</text> |
|||
</view> |
|||
|
|||
<view class="content"> |
|||
<!-- 加载中状态 --> |
|||
<view wx:if="{{loading}}" class="loading"> |
|||
<view class="loading-spinner"></view> |
|||
<text>加载中...</text> |
|||
</view> |
|||
|
|||
<!-- 错误提示 --> |
|||
<view wx:if="{{error}}" class="error"> |
|||
<view class="error-icon">⚠️</view> |
|||
<text>{{error}}</text> |
|||
<button bindtap="loadSpecifications" data-product="{{productName}}" type="primary" class="retry-button">重试</button> |
|||
</view> |
|||
|
|||
<!-- 商品信息 --> |
|||
<view wx:else class="product-info"> |
|||
<text class="product-label">商品名称:</text> |
|||
<text class="product-value">{{productName}}</text> |
|||
<button bindtap="goBackToProductList" type="default" class="back-button">返回上一步</button> |
|||
</view> |
|||
|
|||
<!-- 规格列表 --> |
|||
<view wx:if="{{!loading && !error && specifications.length > 0}}" class="spec-list"> |
|||
<view class="section-header"> |
|||
<text class="section-title">可用规格</text> |
|||
<text class="section-count">{{specifications.length}}个</text> |
|||
</view> |
|||
<view class="spec-items"> |
|||
<view |
|||
wx:for="{{specifications}}" |
|||
wx:key="item.name" |
|||
class="spec-item" |
|||
data-spec="{{item}}" |
|||
bindtap="goToSpecDetail" |
|||
> |
|||
<view class="spec-info"> |
|||
<text class="spec-name">{{item.name}}</text> |
|||
</view> |
|||
<view class="spec-price-arrow"> |
|||
<text class="spec-price">¥{{item.finalPriceText}}</text> |
|||
<view class="spec-arrow">→</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 无规格提示 --> |
|||
<view wx:if="{{!loading && !error && specifications.length === 0}}" class="hint"> |
|||
<view class="hint-icon">📋</view> |
|||
<text>该商品暂无可用规格</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
@ -0,0 +1,285 @@ |
|||
.container { |
|||
display: flex; |
|||
flex-direction: column; |
|||
min-height: 100vh; |
|||
background-color: #f5f5f5; |
|||
font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif; |
|||
} |
|||
|
|||
.header { |
|||
padding: 30rpx 0; |
|||
text-align: center; |
|||
background-color: #fff; |
|||
border-bottom: 1rpx solid #eee; |
|||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|||
} |
|||
|
|||
.title { |
|||
font-size: 36rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
letter-spacing: 2rpx; |
|||
} |
|||
|
|||
.content { |
|||
flex: 1; |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
.product-info { |
|||
background-color: #fff; |
|||
border-radius: 12rpx; |
|||
padding: 30rpx; |
|||
margin-bottom: 30rpx; |
|||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.08); |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: flex-start; |
|||
gap: 20rpx; |
|||
} |
|||
|
|||
.back-button { |
|||
width: 100%; |
|||
height: 70rpx; |
|||
line-height: 70rpx; |
|||
font-size: 28rpx; |
|||
border-radius: 35rpx; |
|||
margin-top: 10rpx; |
|||
} |
|||
|
|||
.product-label { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
margin-right: 20rpx; |
|||
} |
|||
|
|||
.product-value { |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
color: #FF6B81; |
|||
flex: 1; |
|||
} |
|||
|
|||
.loading { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 100rpx 0; |
|||
color: #666; |
|||
} |
|||
|
|||
.loading-spinner { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
border: 6rpx solid #f3f3f3; |
|||
border-top: 6rpx solid #FF6B81; |
|||
border-radius: 50%; |
|||
animation: spin 1s linear infinite; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
@keyframes spin { |
|||
0% { transform: rotate(0deg); } |
|||
100% { transform: rotate(360deg); } |
|||
} |
|||
|
|||
.error { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 40rpx; |
|||
background-color: #fff; |
|||
border-radius: 12rpx; |
|||
margin-bottom: 20rpx; |
|||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|||
} |
|||
|
|||
.error-icon { |
|||
font-size: 60rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.error text { |
|||
display: block; |
|||
color: #ff4d4f; |
|||
margin-bottom: 30rpx; |
|||
font-size: 28rpx; |
|||
text-align: center; |
|||
} |
|||
|
|||
.retry-button { |
|||
width: 200rpx; |
|||
height: 70rpx; |
|||
line-height: 70rpx; |
|||
font-size: 28rpx; |
|||
border-radius: 35rpx; |
|||
background-color: #FF6B81; |
|||
color: #fff; |
|||
} |
|||
|
|||
.section-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.section-title { |
|||
font-size: 30rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
padding-left: 15rpx; |
|||
border-left: 5rpx solid #FF6B81; |
|||
} |
|||
|
|||
.section-count { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
background-color: #f5f5f5; |
|||
padding: 5rpx 15rpx; |
|||
border-radius: 20rpx; |
|||
} |
|||
|
|||
.product-list, |
|||
.spec-list { |
|||
background-color: #fff; |
|||
border-radius: 12rpx; |
|||
padding: 30rpx; |
|||
margin-bottom: 30rpx; |
|||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.08); |
|||
} |
|||
|
|||
.product-items, |
|||
.spec-items { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 15rpx; |
|||
} |
|||
|
|||
.product-item { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 30rpx; |
|||
border-radius: 10rpx; |
|||
background-color: #f9f9f9; |
|||
border: 1rpx solid #eee; |
|||
transition: all 0.3s ease; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.spec-item { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: flex-start; |
|||
padding: 30rpx; |
|||
border-radius: 10rpx; |
|||
background-color: #f9f9f9; |
|||
border: 1rpx solid #eee; |
|||
transition: all 0.3s ease; |
|||
cursor: pointer; |
|||
flex-direction: column; |
|||
gap: 15rpx; |
|||
} |
|||
|
|||
.spec-info { |
|||
flex: 1; |
|||
} |
|||
|
|||
.spec-price-arrow { |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
align-items: center; |
|||
gap: 10rpx; |
|||
width: 100%; |
|||
} |
|||
|
|||
.product-item:hover, |
|||
.spec-item:hover { |
|||
background-color: #f0f0f0; |
|||
transform: translateY(-2rpx); |
|||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1); |
|||
} |
|||
|
|||
.product-item.selected { |
|||
background-color: #FFE6E6; |
|||
border-color: #FF6B81; |
|||
color: #FF6B81; |
|||
} |
|||
|
|||
.product-name, |
|||
.spec-name { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
flex: 1; |
|||
} |
|||
|
|||
.spec-price { |
|||
font-size: 28rpx; |
|||
font-weight: bold; |
|||
color: #FF6B81; |
|||
margin-right: 20rpx; |
|||
} |
|||
|
|||
.product-item.selected .product-name { |
|||
color: #FF6B81; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.product-arrow, |
|||
.spec-arrow { |
|||
font-size: 30rpx; |
|||
color: #999; |
|||
margin-left: 20rpx; |
|||
} |
|||
|
|||
.product-item.selected .product-arrow { |
|||
color: #FF6B81; |
|||
} |
|||
|
|||
.hint { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 100rpx 0; |
|||
color: #999; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.hint-icon { |
|||
font-size: 80rpx; |
|||
margin-bottom: 30rpx; |
|||
} |
|||
|
|||
/* 响应式设计 */ |
|||
@media (max-width: 375px) { |
|||
.content { |
|||
padding: 15rpx; |
|||
} |
|||
|
|||
.product-list, |
|||
.spec-list { |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
.product-item, |
|||
.spec-item { |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
.title { |
|||
font-size: 32rpx; |
|||
} |
|||
|
|||
.section-title { |
|||
font-size: 26rpx; |
|||
} |
|||
|
|||
.product-name, |
|||
.spec-name { |
|||
font-size: 26rpx; |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
Page({ |
|||
data: { |
|||
productNames: [], |
|||
loading: false, |
|||
error: '' |
|||
}, |
|||
onLoad(options) { |
|||
this.loadProductNames(); |
|||
}, |
|||
|
|||
loadProductNames() { |
|||
this.setData({ loading: true, error: '' }); |
|||
|
|||
const api = require('../../utils/api'); |
|||
api.getProducts().then(products => { |
|||
// 提取唯一的productName
|
|||
const uniqueProductNames = [...new Set(products.map(product => product.productName).filter(Boolean))]; |
|||
this.setData({ |
|||
productNames: uniqueProductNames, |
|||
loading: false |
|||
}); |
|||
}).catch(err => { |
|||
console.error('获取商品列表失败:', err); |
|||
this.setData({ |
|||
error: '获取商品列表失败,请稍后重试', |
|||
loading: false |
|||
}); |
|||
}); |
|||
}, |
|||
|
|||
selectProduct(e) { |
|||
const productName = e.currentTarget.dataset.product; |
|||
console.log('选择商品:', productName); |
|||
// 将商品名称存储到本地存储,因为wx.switchTab不支持URL参数
|
|||
wx.setStorageSync('selectedProductName', productName); |
|||
// 使用wx.switchTab导航到tabBar页面
|
|||
wx.switchTab({ |
|||
url: '/pages/evaluate1/index', |
|||
success: function(res) { |
|||
console.log('跳转成功:', res); |
|||
}, |
|||
fail: function(err) { |
|||
console.error('跳转失败:', err); |
|||
} |
|||
}); |
|||
} |
|||
}); |
|||
@ -0,0 +1,4 @@ |
|||
{ |
|||
"navigationBarTitleText": "选择商品", |
|||
"usingComponents": {} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
<view class="container"> |
|||
<view class="header"> |
|||
<text class="title">选择商品</text> |
|||
</view> |
|||
|
|||
<view class="content"> |
|||
<!-- 加载中状态 --> |
|||
<view wx:if="{{loading}}" class="loading"> |
|||
<view class="loading-spinner"></view> |
|||
<text>加载中...</text> |
|||
</view> |
|||
|
|||
<!-- 错误提示 --> |
|||
<view wx:if="{{error}}" class="error"> |
|||
<view class="error-icon">⚠️</view> |
|||
<text>{{error}}</text> |
|||
<button bindtap="loadProductNames" type="primary" class="retry-button">重试</button> |
|||
</view> |
|||
|
|||
<!-- 商品名称列表 --> |
|||
<view wx:else class="product-list"> |
|||
<view class="section-header"> |
|||
<text class="section-title">商品名称</text> |
|||
<text class="section-count">{{productNames.length}}个</text> |
|||
</view> |
|||
<view class="product-items"> |
|||
<view |
|||
wx:for="{{productNames}}" |
|||
wx:key="*this" |
|||
class="product-item" |
|||
data-product="{{item}}" |
|||
bindtap="selectProduct" |
|||
> |
|||
<text class="product-name">{{item}}</text> |
|||
<view class="product-arrow">→</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
@ -0,0 +1,180 @@ |
|||
.container { |
|||
display: flex; |
|||
flex-direction: column; |
|||
min-height: 100vh; |
|||
background-color: #f5f5f5; |
|||
font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif; |
|||
} |
|||
|
|||
.header { |
|||
padding: 30rpx 0; |
|||
text-align: center; |
|||
background-color: #fff; |
|||
border-bottom: 1rpx solid #eee; |
|||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|||
} |
|||
|
|||
.title { |
|||
font-size: 36rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
letter-spacing: 2rpx; |
|||
} |
|||
|
|||
.content { |
|||
flex: 1; |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
.loading { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 100rpx 0; |
|||
color: #666; |
|||
} |
|||
|
|||
.loading-spinner { |
|||
width: 60rpx; |
|||
height: 60rpx; |
|||
border: 6rpx solid #f3f3f3; |
|||
border-top: 6rpx solid #FF6B81; |
|||
border-radius: 50%; |
|||
animation: spin 1s linear infinite; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
@keyframes spin { |
|||
0% { transform: rotate(0deg); } |
|||
100% { transform: rotate(360deg); } |
|||
} |
|||
|
|||
.error { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 40rpx; |
|||
background-color: #fff; |
|||
border-radius: 12rpx; |
|||
margin-bottom: 20rpx; |
|||
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05); |
|||
} |
|||
|
|||
.error-icon { |
|||
font-size: 60rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.error text { |
|||
display: block; |
|||
color: #ff4d4f; |
|||
margin-bottom: 30rpx; |
|||
font-size: 28rpx; |
|||
text-align: center; |
|||
} |
|||
|
|||
.retry-button { |
|||
width: 200rpx; |
|||
height: 70rpx; |
|||
line-height: 70rpx; |
|||
font-size: 28rpx; |
|||
border-radius: 35rpx; |
|||
background-color: #FF6B81; |
|||
color: #fff; |
|||
} |
|||
|
|||
.section-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.section-title { |
|||
font-size: 30rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
padding-left: 15rpx; |
|||
border-left: 5rpx solid #FF6B81; |
|||
} |
|||
|
|||
.section-count { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
background-color: #f5f5f5; |
|||
padding: 5rpx 15rpx; |
|||
border-radius: 20rpx; |
|||
} |
|||
|
|||
.product-list { |
|||
background-color: #fff; |
|||
border-radius: 12rpx; |
|||
padding: 30rpx; |
|||
margin-bottom: 30rpx; |
|||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.08); |
|||
} |
|||
|
|||
.product-items { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 15rpx; |
|||
} |
|||
|
|||
.product-item { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 30rpx; |
|||
border-radius: 10rpx; |
|||
background-color: #f9f9f9; |
|||
border: 1rpx solid #eee; |
|||
transition: all 0.3s ease; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.product-item:hover { |
|||
background-color: #f0f0f0; |
|||
transform: translateY(-2rpx); |
|||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1); |
|||
} |
|||
|
|||
.product-name { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
flex: 1; |
|||
} |
|||
|
|||
.product-arrow { |
|||
font-size: 30rpx; |
|||
color: #999; |
|||
margin-left: 20rpx; |
|||
} |
|||
|
|||
/* 响应式设计 */ |
|||
@media (max-width: 375px) { |
|||
.content { |
|||
padding: 15rpx; |
|||
} |
|||
|
|||
.product-list { |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
.product-item { |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
.title { |
|||
font-size: 32rpx; |
|||
} |
|||
|
|||
.section-title { |
|||
font-size: 26rpx; |
|||
} |
|||
|
|||
.product-name { |
|||
font-size: 26rpx; |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
Page({ |
|||
data: { |
|||
productName: '', |
|||
specification: '', |
|||
price: 0, |
|||
quantity: 1, |
|||
totalPrice: 0, |
|||
loading: false, |
|||
error: '' |
|||
}, |
|||
onLoad(options) { |
|||
if (options.productName && options.specification && options.price) { |
|||
const price = parseFloat(options.price) || 0; |
|||
this.setData({ |
|||
productName: decodeURIComponent(options.productName), |
|||
specification: decodeURIComponent(options.specification), |
|||
price: price, |
|||
totalPrice: price |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
// 件数输入变化
|
|||
onQuantityChange(e) { |
|||
const quantity = parseInt(e.detail.value) || 1; |
|||
this.setData({ |
|||
quantity: quantity, |
|||
totalPrice: this.data.price * quantity |
|||
}); |
|||
}, |
|||
|
|||
// 计算价格
|
|||
calculatePrice() { |
|||
const totalPrice = this.data.price * this.data.quantity; |
|||
this.setData({ |
|||
totalPrice: totalPrice |
|||
}); |
|||
wx.showToast({ |
|||
title: '计算完成', |
|||
icon: 'success', |
|||
duration: 1000 |
|||
}); |
|||
}, |
|||
|
|||
// 返回上一页
|
|||
goBack() { |
|||
wx.navigateBack(); |
|||
} |
|||
}); |
|||
@ -0,0 +1,4 @@ |
|||
{ |
|||
"navigationBarTitleText": "规格详情", |
|||
"usingComponents": {} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
<view class="container"> |
|||
<view class="header"> |
|||
<text class="title">{{productName}}</text> |
|||
</view> |
|||
|
|||
<view class="content"> |
|||
<view class="spec-card"> |
|||
<view class="spec-item"> |
|||
<text class="label">规格:</text> |
|||
<text class="value">{{specification}}</text> |
|||
</view> |
|||
<view class="spec-item"> |
|||
<text class="label">单价:</text> |
|||
<text class="value price">¥{{price.toFixed(2)}}</text> |
|||
</view> |
|||
<view class="spec-info"> |
|||
<text>您已选择此规格进行估价</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="quantity-section"> |
|||
<text class="label">件数:</text> |
|||
<input |
|||
class="quantity-input" |
|||
type="number" |
|||
value="{{quantity}}" |
|||
bindinput="onQuantityChange" |
|||
min="1" |
|||
/> |
|||
</view> |
|||
|
|||
<view class="calculate-section"> |
|||
<button bindtap="calculatePrice" type="primary" class="calculate-button">计算价格</button> |
|||
</view> |
|||
|
|||
<view class="result-section"> |
|||
<text class="result-label">总价:</text> |
|||
<text class="result-value">¥{{totalPrice.toFixed(2)}}</text> |
|||
</view> |
|||
|
|||
<view class="action"> |
|||
<button bindtap="goBack" type="default">返回</button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
@ -0,0 +1,127 @@ |
|||
.container { |
|||
min-height: 100vh; |
|||
background-color: #f5f5f5; |
|||
} |
|||
|
|||
.header { |
|||
padding: 30rpx 0; |
|||
text-align: center; |
|||
background-color: #fff; |
|||
border-bottom: 1rpx solid #eee; |
|||
} |
|||
|
|||
.title { |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
} |
|||
|
|||
.content { |
|||
padding: 30rpx; |
|||
} |
|||
|
|||
.spec-card { |
|||
background-color: #fff; |
|||
border-radius: 12rpx; |
|||
padding: 40rpx; |
|||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.08); |
|||
margin-bottom: 40rpx; |
|||
} |
|||
|
|||
.spec-item { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 30rpx; |
|||
} |
|||
|
|||
.label { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
margin-right: 20rpx; |
|||
width: 120rpx; |
|||
} |
|||
|
|||
.value { |
|||
font-size: 32rpx; |
|||
font-weight: bold; |
|||
color: #FF6B81; |
|||
flex: 1; |
|||
} |
|||
|
|||
.price { |
|||
color: #FF6B81; |
|||
} |
|||
|
|||
.spec-info { |
|||
padding-top: 20rpx; |
|||
border-top: 1rpx solid #eee; |
|||
text-align: center; |
|||
} |
|||
|
|||
.spec-info text { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.quantity-section { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 30rpx; |
|||
} |
|||
|
|||
.quantity-input { |
|||
flex: 1; |
|||
height: 80rpx; |
|||
border: 2rpx solid #ddd; |
|||
border-radius: 8rpx; |
|||
padding: 0 20rpx; |
|||
font-size: 28rpx; |
|||
} |
|||
|
|||
.calculate-section { |
|||
display: flex; |
|||
justify-content: center; |
|||
margin-bottom: 30rpx; |
|||
} |
|||
|
|||
.calculate-button { |
|||
width: 80%; |
|||
height: 80rpx; |
|||
font-size: 28rpx; |
|||
border-radius: 40rpx; |
|||
} |
|||
|
|||
.result-section { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 40rpx; |
|||
padding: 20rpx; |
|||
background-color: #f9f9f9; |
|||
border-radius: 8rpx; |
|||
} |
|||
|
|||
.result-label { |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
margin-right: 20rpx; |
|||
width: 120rpx; |
|||
} |
|||
|
|||
.result-value { |
|||
font-size: 36rpx; |
|||
font-weight: bold; |
|||
color: #FF6B81; |
|||
flex: 1; |
|||
} |
|||
|
|||
.action { |
|||
display: flex; |
|||
justify-content: center; |
|||
} |
|||
|
|||
button { |
|||
width: 80%; |
|||
height: 80rpx; |
|||
font-size: 28rpx; |
|||
border-radius: 40rpx; |
|||
} |
|||
Loading…
Reference in new issue