1734 lines
40 KiB
Vue
1734 lines
40 KiB
Vue
<template>
|
||
<view class="goods-container">
|
||
<!-- 商品价格 -->
|
||
<view class="price-section">
|
||
<view class="price-main">
|
||
<text class="price-value">{{ goodsInfo.price }}</text>
|
||
</view>
|
||
<view class="" style="display: flex;flex-direction: column;justify-content: space-around;">
|
||
<text class="price-unit">RMB</text>
|
||
<text class="price-per-unit">{{ $t('goods.perUnit') }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 商品图片轮播 -->
|
||
<view class="product-swiper-wrapper">
|
||
<swiper class="product-swiper" :indicator-dots="goodsInfo.imageList.length > 1" :autoplay="true"
|
||
:interval="3000" :duration="500" indicator-color="rgba(255, 255, 255, 0.5)"
|
||
indicator-active-color="#07c160">
|
||
<swiper-item v-for="(image, index) in goodsInfo.imageList" :key="index">
|
||
<view class="swiper-item-wrapper">
|
||
<image :src="image" mode="aspectFit" class="product-image" lazy-load="true"></image>
|
||
</view>
|
||
</swiper-item>
|
||
</swiper>
|
||
</view>
|
||
|
||
<!-- 商品名称 -->
|
||
<view class="product-name">
|
||
<text>{{ goodsInfo.name }}</text>
|
||
</view>
|
||
|
||
<!-- 商品特性 -->
|
||
<view class="features-section">
|
||
<view class="feature-item">
|
||
<view class="feature-icon">
|
||
<image src="@/static/battery-icon.png" mode="aspectFit" class="icon-img" lazy-load="true"></image>
|
||
</view>
|
||
<view class="" style="display: flex;flex-direction: column;align-items: center;">
|
||
<text class="feature-label">{{ $t('goods.features.battery') }}</text>
|
||
<text class="feature-desc">{{ $t('goods.features.batteryDesc') }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="feature-item">
|
||
<view class="feature-icon">
|
||
<image src="@/static/wind-icon.png" mode="aspectFit" class="icon-img" lazy-load="true"></image>
|
||
</view>
|
||
<view class="" style="display: flex;flex-direction: column;align-items: center;">
|
||
<text class="feature-label">{{ $t('goods.features.wind') }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="feature-item">
|
||
<view class="feature-icon">
|
||
<image src="@/static/temp-icon.png" mode="aspectFit" class="icon-img" lazy-load="true"></image>
|
||
</view>
|
||
<view class="" style="display: flex;flex-direction: column;align-items: center;">
|
||
<text class="feature-label">{{ $t('goods.features.temp') }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="feature-item">
|
||
<view class="feature-icon">
|
||
<image src="@/static/charge-icon.png" mode="aspectFit" class="icon-img" lazy-load="true"></image>
|
||
</view>
|
||
<view class="" style="display: flex;flex-direction: column;align-items: center;">
|
||
<text class="feature-label">{{ $t('goods.features.charge') }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 商品详情描述 -->
|
||
<view class="description-section">
|
||
<view class="section-title">{{ $t('goods.productDetail') }}</view>
|
||
<view class="description-content">
|
||
<text class="description-text">
|
||
{{ $t('goods.description') }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部购买按钮 -->
|
||
<view class="footer">
|
||
<view class="" style="display: flex;flex-direction: column;width: 140rpx;align-items: center;margin-right: 20rpx;" @click="GotoList">
|
||
<image src="/static/jl.png" mode="scaleToFill" style="width: 35rpx;height:35rpx;" lazy-load="true"></image>
|
||
<text style="font-size: 26rpx;">定制记录</text>
|
||
</view>
|
||
<view class="buy-button" @click="handleBuy">
|
||
<view class="button-content">
|
||
<view class="price-info">
|
||
<text class="footer-price-symbol">¥</text>
|
||
<text class="footer-price-value">{{ selectedSku.price || goodsInfo.price }}</text>
|
||
</view>
|
||
<text class="buy-text">{{ $t('goods.buyNow') }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 规格选择弹窗 -->
|
||
<view class="popup-mask" v-if="showSkuPopup" @click="closeSkuPopup">
|
||
<view class="popup-container" @click.stop>
|
||
<view class="sku-popup">
|
||
<view class="popup-header">
|
||
<text class="popup-title">选择规格</text>
|
||
<view class="close-btn" @click="closeSkuPopup">
|
||
<text class="close-icon">✕</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="sku-info">
|
||
<image v-if="selectedSku.pictureUrl" :src="selectedSku.pictureUrl" class="sku-image"
|
||
mode="aspectFit" lazy-load="true"></image>
|
||
<view class="sku-detail">
|
||
<text class="sku-price">¥{{ selectedSku.price || goodsInfo.price }}</text>
|
||
<text class="sku-name">{{ selectedSku.optionName || '请选择规格' }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="sku-list">
|
||
<text class="sku-list-title">规格</text>
|
||
<view class="sku-options">
|
||
<view v-for="sku in goodsInfo.skuList" :key="sku.skuId" class="sku-option"
|
||
:class="{ 'active': selectedSku.skuId === sku.skuId }" @click="selectSku(sku)">
|
||
<text class="sku-option-text">{{ sku.optionName }}</text>
|
||
<text class="sku-option-color" v-if="sku.color">{{ sku.color }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="quantity-section">
|
||
<text class="quantity-label">数量</text>
|
||
<view class="quantity-control">
|
||
<view class="quantity-btn" :class="{ 'disabled': quantity <= 1 }" @click="decreaseQuantity">
|
||
<text>-</text>
|
||
</view>
|
||
<input class="quantity-input" type="number" v-model.number="quantity"
|
||
@blur="validateQuantity" />
|
||
<view class="quantity-btn" @click="increaseQuantity">
|
||
<text>+</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="popup-footer">
|
||
<view class="confirm-btn" @click="confirmSku">
|
||
<text>{{ $t('common.confirm') }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 收货信息填写弹窗 -->
|
||
<view class="popup-mask" v-if="showAddressPopup" @click="closeAddressPopup">
|
||
<view class="popup-container" @click.stop>
|
||
<view class="address-popup">
|
||
<view class="popup-header">
|
||
<text class="popup-title">填写收货信息</text>
|
||
<view class="close-btn" @click="closeAddressPopup">
|
||
<text class="close-icon">✕</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-section">
|
||
<view class="form-item">
|
||
<text class="form-label">收件人</text>
|
||
<input class="form-input" v-model="addressForm.receiverName" placeholder="请输入收件人姓名"
|
||
placeholder-class="input-placeholder" />
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="form-label">手机号</text>
|
||
<input class="form-input" v-model="addressForm.receiverPhone" type="number" maxlength="11"
|
||
placeholder="请输入手机号" placeholder-class="input-placeholder" />
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="form-label">收货地区</text>
|
||
<!-- 非支付宝小程序:使用多列 picker -->
|
||
<!-- #ifndef MP-ALIPAY -->
|
||
<picker mode="multiSelector" :range="regionColumns" range-key="name" :value="regionIndexes"
|
||
@change="onRegionChange" @columnchange="onRegionColumnChange">
|
||
<view class="form-input region-selector">
|
||
<text v-if="addressForm.province && addressForm.city && addressForm.district"
|
||
class="region-text">
|
||
{{ addressForm.province }} {{ addressForm.city }} {{ addressForm.district }}
|
||
</text>
|
||
<text v-else class="input-placeholder">请选择省市区</text>
|
||
<text class="arrow-icon">›</text>
|
||
</view>
|
||
</picker>
|
||
<!-- #endif -->
|
||
|
||
<!-- 支付宝小程序:使用自定义弹窗 + picker-view -->
|
||
<!-- #ifdef MP-ALIPAY -->
|
||
<view class="form-input region-selector" @click="showRegionPicker = true">
|
||
<text v-if="addressForm.province && addressForm.city && addressForm.district"
|
||
class="region-text">
|
||
{{ addressForm.province }} {{ addressForm.city }} {{ addressForm.district }}
|
||
</text>
|
||
<text v-else class="input-placeholder">请选择省市区</text>
|
||
<text class="arrow-icon">›</text>
|
||
</view>
|
||
<!-- #endif -->
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="form-label">详细地址</text>
|
||
<textarea class="form-textarea" v-model="addressForm.receiverAddress"
|
||
placeholder="请输入详细地址(街道、门牌号等)" placeholder-class="input-placeholder" :maxlength="200"
|
||
:show-confirm-bar="false" />
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="form-label">备注</text>
|
||
<textarea class="form-textarea" v-model="addressForm.remark" placeholder="选填,可以告诉我们您的特殊需求"
|
||
placeholder-class="input-placeholder" :maxlength="200" :show-confirm-bar="false" />
|
||
</view>
|
||
</view>
|
||
|
||
<view class="popup-footer">
|
||
<view class="confirm-btn" @click="confirmAddress">
|
||
<text>确认并支付</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 地址展示弹窗(已有地址时显示) -->
|
||
<view class="popup-mask" v-if="showAddressDisplay" @click="closeAddressDisplay">
|
||
<view class="popup-container" @click.stop>
|
||
<view class="address-popup">
|
||
<view class="popup-header">
|
||
<text class="popup-title">收货信息</text>
|
||
<view class="close-btn" @click="closeAddressDisplay">
|
||
<text class="close-icon">✕</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-section">
|
||
<view class="display-item">
|
||
<text class="display-label">收件人</text>
|
||
<text class="display-value">{{ savedAddress.receiverName }}</text>
|
||
</view>
|
||
|
||
<view class="display-item">
|
||
<text class="display-label">手机号</text>
|
||
<text class="display-value">{{ savedAddress.receiverPhone }}</text>
|
||
</view>
|
||
|
||
<view class="display-item">
|
||
<text class="display-label">收货地址</text>
|
||
<text class="display-value">{{ savedAddress.fullAddress }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="popup-footer">
|
||
<view class="button-group">
|
||
<view class="secondary-btn" @click="changeAddress">
|
||
<text>更换地址</text>
|
||
</view>
|
||
<view class="confirm-btn" @click="payNow">
|
||
<text>立即支付</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 支付宝小程序地区选择弹窗 -->
|
||
<!-- #ifdef MP-ALIPAY -->
|
||
<view class="popup-mask" v-if="showRegionPicker" @click="closeRegionPicker">
|
||
<view class="popup-container" @click.stop>
|
||
<view class="address-popup">
|
||
<view class="popup-header">
|
||
<text class="popup-title">选择省市区</text>
|
||
<view class="close-btn" @click="closeRegionPicker">
|
||
<text class="close-icon">✕</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-section">
|
||
<picker-view :value="regionIndexes" @change="onAliRegionChange"
|
||
indicator-style="height: 50px;">
|
||
<picker-view-column>
|
||
<view v-for="item in regionColumns[0]" :key="item.code">
|
||
{{ item.name }}
|
||
</view>
|
||
</picker-view-column>
|
||
<picker-view-column>
|
||
<view v-for="item in regionColumns[1]" :key="item.code">
|
||
{{ item.name }}
|
||
</view>
|
||
</picker-view-column>
|
||
<picker-view-column>
|
||
<view v-for="item in regionColumns[2]" :key="item.code">
|
||
{{ item.name }}
|
||
</view>
|
||
</picker-view-column>
|
||
</picker-view>
|
||
</view>
|
||
|
||
<view class="popup-footer">
|
||
<view class="confirm-btn" @click="confirmAliRegion">
|
||
<text>{{ $t('common.confirm') }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- #endif -->
|
||
|
||
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
ref,
|
||
onMounted,
|
||
computed
|
||
} from 'vue'
|
||
import {
|
||
onLoad
|
||
} from '@dcloudio/uni-app'
|
||
import {
|
||
useI18n
|
||
} from '@/utils/i18n.js'
|
||
import {
|
||
getProductDetail,
|
||
createProductOrder,
|
||
getUserAddress
|
||
} from '@/config/api/product.js'
|
||
import {
|
||
cancelProductOrder
|
||
} from '@/config/api/order.js'
|
||
import pcaData from '@/components/pca.json'
|
||
|
||
const {
|
||
t
|
||
} = useI18n()
|
||
|
||
// 商品信息
|
||
const goodsInfo = ref({
|
||
id: '',
|
||
name: '',
|
||
price: 99,
|
||
image: '/static/product-fan.png',
|
||
imageList: ['/static/product-fan.png'], // 图片列表用于轮播
|
||
description: '',
|
||
features: [],
|
||
skuList: [] // 商品规格列表
|
||
})
|
||
|
||
// 选中的规格
|
||
const selectedSku = ref({
|
||
skuId: '',
|
||
price: '',
|
||
pictureUrl: '',
|
||
optionName: '',
|
||
color: ''
|
||
})
|
||
|
||
// 购买数量
|
||
const quantity = ref(1)
|
||
|
||
// 收货信息表单
|
||
const addressForm = ref({
|
||
receiverName: '',
|
||
receiverPhone: '',
|
||
province: '',
|
||
provinceCode: '',
|
||
city: '',
|
||
cityCode: '',
|
||
district: '',
|
||
districtCode: '',
|
||
receiverAddress: '',
|
||
remark: ''
|
||
})
|
||
|
||
// 已保存的地址信息(用于判断是否已有地址)
|
||
const savedAddress = ref(null)
|
||
|
||
// 地区选择相关(uni原生picker)
|
||
const provinceList = ref([])
|
||
const cityList = ref([])
|
||
const districtList = ref([])
|
||
const regionColumns = ref([
|
||
[],
|
||
[],
|
||
[]
|
||
]) // 三列数据:省、市、区
|
||
const regionIndexes = ref([0, 0, 0]) // 当前选中的索引
|
||
|
||
// 弹窗显示状态
|
||
const showSkuPopup = ref(false)
|
||
const showAddressPopup = ref(false)
|
||
const showAddressDisplay = ref(false) // 地址展示弹窗
|
||
const showRegionPicker = ref(false) // 支付宝地区选择弹窗
|
||
|
||
// 计算是否已有地址
|
||
const hasAddress = computed(() => {
|
||
return savedAddress.value !== null
|
||
})
|
||
|
||
// 加载状态
|
||
const loading = ref(false)
|
||
|
||
// 页面加载
|
||
onLoad((options) => {
|
||
if (options.productId) {
|
||
goodsInfo.value.id = options.productId
|
||
// 根据 productId 获取商品详情
|
||
fetchGoodsDetail(options.productId)
|
||
} else if (options.goodsId) {
|
||
// 兼容旧的参数名
|
||
goodsInfo.value.id = options.goodsId
|
||
fetchGoodsDetail(options.goodsId)
|
||
}
|
||
})
|
||
|
||
onMounted(() => {
|
||
uni.setNavigationBarTitle({
|
||
title: t('goods.goodsTitle')
|
||
})
|
||
// 初始化地区数据
|
||
initRegionData()
|
||
// 获取用户收货地址
|
||
fetchUserAddress()
|
||
})
|
||
|
||
// 初始化地区数据
|
||
const initRegionData = () => {
|
||
// 获取省份列表
|
||
const provinces = pcaData['86']
|
||
provinceList.value = Object.keys(provinces).map(code => ({
|
||
code,
|
||
name: provinces[code]
|
||
}))
|
||
|
||
// 初始化第一列(省份)
|
||
regionColumns.value[0] = provinceList.value
|
||
|
||
// 初始化第二列(城市)- 默认加载第一个省份的城市
|
||
if (provinceList.value.length > 0) {
|
||
const firstProvinceCode = provinceList.value[0].code
|
||
const cities = pcaData[firstProvinceCode]
|
||
if (cities) {
|
||
cityList.value = Object.keys(cities).map(code => ({
|
||
code,
|
||
name: cities[code]
|
||
}))
|
||
regionColumns.value[1] = cityList.value
|
||
|
||
// 初始化第三列(区县)- 默认加载第一个城市的区县
|
||
if (cityList.value.length > 0) {
|
||
const firstCityCode = cityList.value[0].code
|
||
const districts = pcaData[firstCityCode]
|
||
if (districts) {
|
||
districtList.value = Object.keys(districts).map(code => ({
|
||
code,
|
||
name: districts[code]
|
||
}))
|
||
regionColumns.value[2] = districtList.value
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 获取城市列表
|
||
const getCityList = (provinceCode) => {
|
||
const cities = pcaData[provinceCode]
|
||
if (cities) {
|
||
return Object.keys(cities).map(code => ({
|
||
code,
|
||
name: cities[code]
|
||
}))
|
||
}
|
||
return []
|
||
}
|
||
|
||
// 获取区县列表
|
||
const getDistrictList = (cityCode) => {
|
||
const districts = pcaData[cityCode]
|
||
if (districts) {
|
||
return Object.keys(districts).map(code => ({
|
||
code,
|
||
name: districts[code]
|
||
}))
|
||
}
|
||
return []
|
||
}
|
||
|
||
// picker列变化事件
|
||
const onRegionColumnChange = (e) => {
|
||
const {
|
||
column,
|
||
value
|
||
} = e.detail
|
||
const newIndexes = [...regionIndexes.value]
|
||
newIndexes[column] = value
|
||
|
||
if (column === 0) {
|
||
// 省份变化,更新城市和区县列表
|
||
const provinceCode = regionColumns.value[0][value].code
|
||
const cities = getCityList(provinceCode)
|
||
regionColumns.value[1] = cities
|
||
newIndexes[1] = 0 // 重置城市索引
|
||
|
||
if (cities.length > 0) {
|
||
const cityCode = cities[0].code
|
||
const districts = getDistrictList(cityCode)
|
||
regionColumns.value[2] = districts
|
||
newIndexes[2] = 0 // 重置区县索引
|
||
} else {
|
||
regionColumns.value[2] = []
|
||
}
|
||
} else if (column === 1) {
|
||
// 城市变化,更新区县列表
|
||
const cityCode = regionColumns.value[1][value].code
|
||
const districts = getDistrictList(cityCode)
|
||
regionColumns.value[2] = districts
|
||
newIndexes[2] = 0 // 重置区县索引
|
||
}
|
||
|
||
regionIndexes.value = newIndexes
|
||
}
|
||
|
||
// picker确认选择
|
||
const onRegionChange = (e) => {
|
||
const indexes = e.detail.value
|
||
regionIndexes.value = indexes
|
||
|
||
// 获取选中的省市区
|
||
const province = regionColumns.value[0][indexes[0]]
|
||
const city = regionColumns.value[1][indexes[1]]
|
||
const district = regionColumns.value[2][indexes[2]]
|
||
|
||
// 更新表单数据
|
||
addressForm.value.province = province.name
|
||
addressForm.value.provinceCode = province.code
|
||
addressForm.value.city = city.name
|
||
addressForm.value.cityCode = city.code
|
||
addressForm.value.district = district.name
|
||
addressForm.value.districtCode = district.code
|
||
}
|
||
|
||
// 支付宝小程序 picker-view 列变化
|
||
const onAliRegionChange = (e) => {
|
||
const newVal = e.detail.value || []
|
||
const oldVal = regionIndexes.value || []
|
||
|
||
// 找出发生变化的列
|
||
let column = -1
|
||
for (let i = 0; i < newVal.length; i++) {
|
||
if (newVal[i] !== oldVal[i]) {
|
||
column = i
|
||
break
|
||
}
|
||
}
|
||
|
||
if (column !== -1) {
|
||
onRegionColumnChange({
|
||
detail: {
|
||
column,
|
||
value: newVal[column]
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// 关闭支付宝地区弹窗
|
||
const closeRegionPicker = () => {
|
||
showRegionPicker.value = false
|
||
}
|
||
|
||
// 支付宝地区选择“确定”
|
||
const confirmAliRegion = () => {
|
||
onRegionChange({
|
||
detail: {
|
||
value: regionIndexes.value
|
||
}
|
||
})
|
||
closeRegionPicker()
|
||
}
|
||
|
||
// 获取用户收货地址
|
||
const fetchUserAddress = async () => {
|
||
try {
|
||
const res = await getUserAddress()
|
||
console.log('获取收货地址结果:', res)
|
||
|
||
if (res && res.code === 200 && res.data) {
|
||
// 保存地址信息
|
||
savedAddress.value = {
|
||
receiverName: res.data.userName || '',
|
||
receiverPhone: res.data.userPhone || '',
|
||
province: res.data.province || '',
|
||
city: res.data.city || '',
|
||
district: res.data.district || '',
|
||
receiverAddress: res.data.userAddress || '',
|
||
fullAddress: `${res.data.province || ''}${res.data.city || ''}${res.data.district || ''}${res.data.userAddress || ''}`
|
||
}
|
||
console.log('收货地址已保存:', savedAddress.value)
|
||
}
|
||
} catch (error) {
|
||
console.error('获取收货地址异常:', error)
|
||
// 获取地址失败不影响用户继续操作,静默处理
|
||
}
|
||
}
|
||
|
||
// 获取商品详情
|
||
const fetchGoodsDetail = async (productId) => {
|
||
try {
|
||
loading.value = true
|
||
uni.showLoading({
|
||
title: t('common.loading'),
|
||
mask: true
|
||
})
|
||
|
||
const res = await getProductDetail(productId)
|
||
|
||
console.log('商品详情查询结果:', res)
|
||
|
||
if (res && res.code === 200 && res.data) {
|
||
const product = res.data
|
||
|
||
// 获取第一个 SKU 的信息(价格和图片)
|
||
const firstSku = product.skuList && product.skuList.length > 0 ? product.skuList[0] : null
|
||
|
||
// 收集所有 SKU 的图片用于轮播
|
||
let imageList = []
|
||
if (product.skuList && product.skuList.length > 0) {
|
||
product.skuList.forEach(sku => {
|
||
// 如果有 pictureUrlList,使用它
|
||
if (sku.pictureUrlList && sku.pictureUrlList.length > 0) {
|
||
imageList = imageList.concat(sku.pictureUrlList)
|
||
} else if (sku.pictureUrl) {
|
||
// 否则使用单张图片
|
||
imageList.push(sku.pictureUrl)
|
||
}
|
||
})
|
||
}
|
||
|
||
// 去重图片列表
|
||
imageList = [...new Set(imageList)]
|
||
|
||
// 如果没有图片,使用默认图片
|
||
if (imageList.length === 0) {
|
||
imageList = ['/static/product-fan.png']
|
||
}
|
||
|
||
// 更新商品信息(根据实际返回的字段名)
|
||
goodsInfo.value = {
|
||
id: product.id,
|
||
name: product.productName || '商品名称',
|
||
price: firstSku?.price || 99,
|
||
image: imageList[0], // 主图
|
||
imageList: imageList, // 图片列表用于轮播
|
||
description: product.productDescription || product.description || '暂无描述',
|
||
features: product.features || [],
|
||
skuList: product.skuList || [], // 保存规格列表
|
||
productNo: product.productNo, // 商品编号
|
||
remark: product.remark, // 备注
|
||
color: firstSku?.color, // 颜色
|
||
optionName: firstSku?.optionName // 规格名称
|
||
}
|
||
|
||
// 默认选中第一个规格
|
||
if (firstSku) {
|
||
selectedSku.value = {
|
||
skuId: firstSku.skuId,
|
||
price: firstSku.price,
|
||
pictureUrl: firstSku.pictureUrl,
|
||
optionName: firstSku.optionName,
|
||
color: firstSku.color
|
||
}
|
||
}
|
||
|
||
console.log('商品详情加载成功:', goodsInfo.value)
|
||
} else {
|
||
console.warn('获取商品详情失败:', res?.msg || '未知错误')
|
||
uni.showToast({
|
||
title: res?.msg || t('common.loadFailed'),
|
||
icon: 'none'
|
||
})
|
||
}
|
||
} catch (error) {
|
||
console.error('获取商品详情异常:', error)
|
||
uni.showToast({
|
||
title: t('common.loadFailed'),
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
loading.value = false
|
||
uni.hideLoading()
|
||
}
|
||
}
|
||
|
||
// 处理购买 - 打开规格选择弹窗
|
||
const handleBuy = () => {
|
||
if (goodsInfo.value.skuList.length === 0) {
|
||
uni.showToast({
|
||
title: '商品暂无可选规格',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
showSkuPopup.value = true
|
||
}
|
||
|
||
// 选择规格
|
||
const selectSku = (sku) => {
|
||
selectedSku.value = {
|
||
skuId: sku.skuId,
|
||
price: sku.price,
|
||
pictureUrl: sku.pictureUrl,
|
||
optionName: sku.optionName,
|
||
color: sku.color
|
||
}
|
||
}
|
||
|
||
// 减少数量
|
||
const decreaseQuantity = () => {
|
||
if (quantity.value > 1) {
|
||
quantity.value--
|
||
}
|
||
}
|
||
|
||
// 增加数量
|
||
const increaseQuantity = () => {
|
||
quantity.value++
|
||
}
|
||
|
||
// 验证数量
|
||
const validateQuantity = () => {
|
||
if (!quantity.value || quantity.value < 1) {
|
||
quantity.value = 1
|
||
}
|
||
quantity.value = Math.floor(quantity.value)
|
||
}
|
||
|
||
// 确认规格选择
|
||
const confirmSku = () => {
|
||
if (!selectedSku.value.skuId) {
|
||
uni.showToast({
|
||
title: '请选择规格',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
closeSkuPopup()
|
||
// 判断是否已有地址
|
||
if (hasAddress.value) {
|
||
// 显示地址展示弹窗
|
||
showAddressDisplay.value = true
|
||
} else {
|
||
// 打开收货信息填写弹窗
|
||
showAddressPopup.value = true
|
||
}
|
||
}
|
||
|
||
// 关闭规格弹窗
|
||
const closeSkuPopup = () => {
|
||
showSkuPopup.value = false
|
||
}
|
||
|
||
// 关闭收货信息弹窗
|
||
const closeAddressPopup = () => {
|
||
showAddressPopup.value = false
|
||
}
|
||
|
||
// 关闭地址展示弹窗
|
||
const closeAddressDisplay = () => {
|
||
showAddressDisplay.value = false
|
||
}
|
||
|
||
// 更换地址
|
||
const changeAddress = () => {
|
||
// 关闭展示弹窗
|
||
closeAddressDisplay()
|
||
// 打开填写弹窗
|
||
showAddressPopup.value = true
|
||
}
|
||
|
||
// 确认收货信息并支付
|
||
const confirmAddress = async () => {
|
||
// 验证表单
|
||
if (!addressForm.value.receiverName) {
|
||
uni.showToast({
|
||
title: '请输入收件人姓名',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
if (!addressForm.value.receiverPhone) {
|
||
uni.showToast({
|
||
title: '请输入手机号',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
// 验证手机号格式
|
||
const phoneReg = /^1[3-9]\d{9}$/
|
||
if (!phoneReg.test(addressForm.value.receiverPhone)) {
|
||
uni.showToast({
|
||
title: '请输入正确的手机号',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
if (!addressForm.value.province || !addressForm.value.city || !addressForm.value.district) {
|
||
uni.showToast({
|
||
title: '请选择收货地区',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
if (!addressForm.value.receiverAddress) {
|
||
uni.showToast({
|
||
title: '请输入详细地址',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
// 保存地址信息(将省市区和详细地址拼接)
|
||
const fullAddress =
|
||
`${addressForm.value.province}${addressForm.value.city}${addressForm.value.district}${addressForm.value.receiverAddress}`
|
||
savedAddress.value = {
|
||
receiverName: addressForm.value.receiverName,
|
||
receiverPhone: addressForm.value.receiverPhone,
|
||
province: addressForm.value.province,
|
||
city: addressForm.value.city,
|
||
district: addressForm.value.district,
|
||
receiverAddress: fullAddress, // 完整地址
|
||
fullAddress: fullAddress // 用于显示
|
||
}
|
||
|
||
// 关闭弹窗
|
||
closeAddressPopup()
|
||
|
||
// 调用支付接口
|
||
await createOrder()
|
||
}
|
||
|
||
// 立即支付(使用已保存的地址)
|
||
const payNow = async () => {
|
||
// 关闭展示弹窗
|
||
closeAddressDisplay()
|
||
// 调用支付接口
|
||
await createOrder()
|
||
}
|
||
|
||
const GotoList = ()=>{
|
||
uni.navigateTo({
|
||
url:'/subPackages/business/device-orderList'
|
||
})
|
||
}
|
||
|
||
// 创建订单并支付(接入商品多支付平台方案:微信 / 支付宝,小程序端)
|
||
const createOrder = async () => {
|
||
try {
|
||
uni.showLoading({
|
||
title: '正在创建订单...',
|
||
mask: true
|
||
})
|
||
|
||
// 构建订单数据(优先使用已保存的地址)
|
||
const addressData = savedAddress.value || addressForm.value
|
||
// 拼接完整地址:省市区 + 详细地址
|
||
const fullAddress = savedAddress.value ?
|
||
savedAddress.value.receiverAddress :
|
||
`${addressForm.value.province}${addressForm.value.city}${addressForm.value.district}${addressForm.value.receiverAddress}`
|
||
|
||
// 根据当前运行环境确定支付平台
|
||
let paymentPlatform = 'WECHAT' // 默认微信
|
||
// #ifdef MP-ALIPAY
|
||
paymentPlatform = 'ALIPAY'
|
||
// #endif
|
||
// #ifdef H5
|
||
// H5 预留 Antom 支付,这里暂时仍按微信处理,如需接入可改为 ANTOM 并补充 paymentType / osType
|
||
paymentPlatform = 'WECHAT'
|
||
// #endif
|
||
|
||
const orderData = {
|
||
skuId: selectedSku.value.skuId,
|
||
quantity: quantity.value,
|
||
receiverName: addressData.receiverName,
|
||
receiverPhone: addressData.receiverPhone,
|
||
receiverAddress: fullAddress, // 传递完整地址(省市区+详细地址)
|
||
remark: addressForm.value.remark || '',
|
||
paymentPlatform // WECHAT / ALIPAY /(预留)ANTOM
|
||
}
|
||
|
||
console.log('创建订单数据:', orderData)
|
||
|
||
const res = await createProductOrder(orderData)
|
||
|
||
console.log('创建订单结果:', res)
|
||
|
||
if (res && res.code === 200 && res.data) {
|
||
uni.hideLoading()
|
||
|
||
// 统一获取平台订单号(商品统一支付订单号)
|
||
const outOrderNo = res.data.OutOrderNo || res.data.outOrderNo
|
||
|
||
// ====================== 微信小程序支付 ======================
|
||
// #ifdef MP-WEIXIN
|
||
const payParams = res.data
|
||
uni.requestPayment({
|
||
timeStamp: payParams.timeStamp,
|
||
nonceStr: payParams.nonceStr,
|
||
package: payParams.package,
|
||
signType: payParams.signType,
|
||
paySign: payParams.paySign,
|
||
success: (payRes) => {
|
||
console.log('支付成功:', payRes)
|
||
uni.showToast({
|
||
title: '支付成功',
|
||
icon: 'success',
|
||
duration: 2000
|
||
})
|
||
|
||
// 重置表单
|
||
resetForm()
|
||
|
||
// 跳转到订单页面
|
||
setTimeout(() => {
|
||
uni.switchTab({
|
||
url: '/subPackages/business/device-orderList'
|
||
})
|
||
}, 2000)
|
||
},
|
||
fail: async (payErr) => {
|
||
console.error('支付失败:', payErr)
|
||
|
||
// 判断是用户取消还是支付失败
|
||
if (payErr.errMsg && payErr.errMsg.includes('cancel')) {
|
||
// 用户取消支付,这里预留调用取消订单接口
|
||
try {
|
||
// await cancelProductOrder(outOrderNo)
|
||
uni.showToast({
|
||
title: '支付已取消',
|
||
icon: 'none'
|
||
})
|
||
} catch (cancelError) {
|
||
console.error('取消订单失败:', cancelError)
|
||
uni.showToast({
|
||
title: '支付已取消',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
} else {
|
||
// 支付失败
|
||
uni.showToast({
|
||
title: '支付失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
})
|
||
// #endif
|
||
|
||
// ====================== 支付宝小程序支付 ======================
|
||
// #ifdef MP-ALIPAY
|
||
console.log(res.data,'支付宝支付参数');
|
||
|
||
const tradeNO = res.data.tradeNo
|
||
if (!tradeNO) {
|
||
uni.showToast({
|
||
title: '未获取到支付宝支付参数',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
my.tradePay({
|
||
tradeNO,
|
||
success: (payRes) => {
|
||
console.log('支付宝支付结果:', payRes)
|
||
if (payRes.resultCode === '9000') {
|
||
uni.showToast({
|
||
title: '支付成功',
|
||
icon: 'success',
|
||
duration: 2000
|
||
})
|
||
|
||
resetForm()
|
||
|
||
setTimeout(() => {
|
||
uni.switchTab({
|
||
url: '/subPackages/business/device-orderList'
|
||
})
|
||
}, 2000)
|
||
} else {
|
||
uni.showToast({
|
||
title: '支付失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
},
|
||
fail: () => {
|
||
uni.showToast({
|
||
title: '支付失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
})
|
||
// #endif
|
||
|
||
// ====================== H5 环境(预留 Antom 支付) ======================
|
||
// #ifdef H5
|
||
uni.showToast({
|
||
title: '当前环境暂不支持购买,请使用微信或支付宝小程序',
|
||
icon: 'none'
|
||
})
|
||
// #endif
|
||
} else {
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res?.msg || '创建订单失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
} catch (error) {
|
||
console.error('创建订单异常:', error)
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: '创建订单失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
|
||
// 重置表单
|
||
const resetForm = () => {
|
||
quantity.value = 1
|
||
addressForm.value = {
|
||
receiverName: '',
|
||
receiverPhone: '',
|
||
province: '',
|
||
provinceCode: '',
|
||
city: '',
|
||
cityCode: '',
|
||
district: '',
|
||
districtCode: '',
|
||
receiverAddress: '',
|
||
remark: ''
|
||
}
|
||
regionIndexes.value = [0, 0, 0]
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.goods-container {
|
||
min-height: 100vh;
|
||
background: radial-gradient(95.17% 36.74% at 95% 5%, #B1FCCB 0%, #F1F2F6 100%);
|
||
padding-bottom: 200rpx;
|
||
position: relative;
|
||
}
|
||
|
||
// 价格区域
|
||
.price-section {
|
||
padding: 40rpx 40rpx 20rpx;
|
||
display: flex;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
// align-items: baseline;
|
||
|
||
.price-main {
|
||
display: flex;
|
||
// align-items: baseline;
|
||
|
||
.price-value {
|
||
font-size: 88rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
line-height: 1;
|
||
font-family: 'DIN Alternate', 'Arial', sans-serif;
|
||
}
|
||
|
||
.price-unit {
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
font-weight: 600;
|
||
margin-left: 8rpx;
|
||
}
|
||
}
|
||
|
||
.price-per-unit {
|
||
font-size: 32rpx;
|
||
color: #666;
|
||
margin-left: 8rpx;
|
||
}
|
||
}
|
||
|
||
// 商品图片轮播
|
||
.product-swiper-wrapper {
|
||
width: 100%;
|
||
height: 510rpx;
|
||
// padding: 0 60rpx;
|
||
margin-bottom: 40rpx;
|
||
|
||
.product-swiper {
|
||
width: 100%;
|
||
height: 100%;
|
||
// border-radius: 24rpx;
|
||
overflow: hidden;
|
||
|
||
.swiper-item-wrapper {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: rgba(255, 255, 255, 0.5);
|
||
|
||
.product-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
// transform: rotate(-15deg);
|
||
// animation: float 3s ease-in-out infinite;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@keyframes float {
|
||
|
||
0%,
|
||
100% {
|
||
transform: rotate(-15deg) translateY(0);
|
||
}
|
||
|
||
50% {
|
||
transform: rotate(-15deg) translateY(-20rpx);
|
||
}
|
||
}
|
||
|
||
// 商品名称
|
||
.product-name {
|
||
padding: 0 60rpx;
|
||
// margin-bottom: 60rpx;
|
||
margin: 30rpx auto;
|
||
// width: 680rpx;
|
||
|
||
text {
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
line-height: 1.6;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
// 特性区域
|
||
.features-section {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
// grid-template-columns: repeat(2, 1fr);
|
||
// gap: 40rpx;
|
||
padding: 0 60rpx;
|
||
// margin-bottom: 60rpx;
|
||
|
||
.feature-item {
|
||
// background: rgba(255, 255, 255, 0.8);
|
||
border-radius: 24rpx;
|
||
padding: 20rpx 15rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
// box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||
backdrop-filter: blur(10rpx);
|
||
|
||
.feature-icon {
|
||
width: 100rpx;
|
||
height: 100rpx;
|
||
background: #fff;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
.icon-img {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
}
|
||
}
|
||
|
||
.feature-label {
|
||
font-size: 24rpx;
|
||
color: #333;
|
||
font-weight: 600;
|
||
// margin-bottom: 8rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.feature-desc {
|
||
font-size: 24rpx;
|
||
color: #333;
|
||
font-weight: 600;
|
||
text-align: center;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 商品详情描述
|
||
.description-section {
|
||
background: #fff;
|
||
border-radius: 32rpx;
|
||
padding: 60rpx;
|
||
margin: 40rpx;
|
||
|
||
.section-title {
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.description-content {
|
||
.description-text {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
line-height: 1.8;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 底部购买区域
|
||
.footer {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: #fff;
|
||
padding: 30rpx 60rpx;
|
||
padding-bottom: calc(30rpx + env(safe-area-inset-bottom));
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 100;
|
||
|
||
.buy-button {
|
||
width: 100%;
|
||
height: 96rpx;
|
||
border-radius: 48rpx;
|
||
background: linear-gradient(135deg, #07c160, #10d673);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 24rpx rgba(7, 193, 96, 0.4);
|
||
|
||
// &:active {
|
||
// transform: scale(0.98);
|
||
// opacity: 0.9;
|
||
// }
|
||
|
||
.button-content {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 40rpx;
|
||
text-align: center;
|
||
|
||
.buy-text {
|
||
color: #fff;
|
||
font-size: 34rpx;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.price-info {
|
||
display: flex;
|
||
align-items: baseline;
|
||
|
||
.footer-price-symbol {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #fff;
|
||
}
|
||
|
||
.footer-price-value {
|
||
font-size: 48rpx;
|
||
font-weight: bold;
|
||
color: #fff;
|
||
font-family: 'DIN Alternate', 'Arial', sans-serif;
|
||
margin-left: 4rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 弹窗遮罩层
|
||
.popup-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
z-index: 999;
|
||
display: flex;
|
||
align-items: flex-end;
|
||
|
||
.popup-container {
|
||
width: 100%;
|
||
animation: slideUp 0.3s ease-out;
|
||
}
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from {
|
||
transform: translateY(100%);
|
||
}
|
||
|
||
to {
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
// 规格选择弹窗
|
||
.sku-popup {
|
||
background: #fff;
|
||
border-radius: 32rpx 32rpx 0 0;
|
||
padding: 40rpx;
|
||
max-height: 80vh;
|
||
overflow-y: auto;
|
||
|
||
.popup-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 40rpx;
|
||
|
||
.popup-title {
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.close-btn {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #f5f5f5;
|
||
border-radius: 50%;
|
||
|
||
.close-icon {
|
||
font-size: 32rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
}
|
||
|
||
.sku-info {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 30rpx;
|
||
background: #f8f8f8;
|
||
border-radius: 16rpx;
|
||
margin-bottom: 40rpx;
|
||
|
||
.sku-image {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
border-radius: 12rpx;
|
||
margin-right: 24rpx;
|
||
background: #fff;
|
||
}
|
||
|
||
.sku-detail {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.sku-price {
|
||
font-size: 40rpx;
|
||
font-weight: bold;
|
||
color: #ff4d4f;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.sku-name {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
}
|
||
|
||
.sku-list {
|
||
margin-bottom: 40rpx;
|
||
|
||
.sku-list-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-bottom: 24rpx;
|
||
display: block;
|
||
}
|
||
|
||
.sku-options {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 20rpx;
|
||
|
||
.sku-option {
|
||
padding: 20rpx 32rpx;
|
||
background: #f5f5f5;
|
||
border-radius: 12rpx;
|
||
border: 2rpx solid transparent;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
&.active {
|
||
background: #e6f7ff;
|
||
border-color: #07c160;
|
||
|
||
.sku-option-text,
|
||
.sku-option-color {
|
||
color: #07c160;
|
||
}
|
||
}
|
||
|
||
.sku-option-text {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
margin-bottom: 4rpx;
|
||
}
|
||
|
||
.sku-option-color {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.quantity-section {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 40rpx;
|
||
|
||
.quantity-label {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.quantity-control {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.quantity-btn {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #f5f5f5;
|
||
border-radius: 12rpx;
|
||
font-size: 32rpx;
|
||
color: #333;
|
||
|
||
&.disabled {
|
||
opacity: 0.5;
|
||
}
|
||
}
|
||
|
||
.quantity-input {
|
||
width: 100rpx;
|
||
height: 60rpx;
|
||
text-align: center;
|
||
font-size: 28rpx;
|
||
margin: 0 20rpx;
|
||
background: #f5f5f5;
|
||
border-radius: 12rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.popup-footer {
|
||
.confirm-btn {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
background: linear-gradient(135deg, #07c160, #10d673);
|
||
border-radius: 44rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 24rpx rgba(7, 193, 96, 0.3);
|
||
|
||
text {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
}
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 收货信息弹窗
|
||
.address-popup {
|
||
background: #fff;
|
||
border-radius: 32rpx 32rpx 0 0;
|
||
max-height: 85vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.popup-header {
|
||
position: sticky;
|
||
top: 0;
|
||
background: #fff;
|
||
z-index: 10;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 40rpx 40rpx 30rpx;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
|
||
.popup-title {
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.close-btn {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #f5f5f5;
|
||
border-radius: 50%;
|
||
|
||
.close-icon {
|
||
font-size: 32rpx;
|
||
color: #666;
|
||
}
|
||
}
|
||
}
|
||
|
||
.form-section {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 40rpx 60rpx;
|
||
|
||
.form-item {
|
||
margin-bottom: 40rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.form-label {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 600;
|
||
margin-bottom: 16rpx;
|
||
display: block;
|
||
}
|
||
|
||
.form-input {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
padding: 0 24rpx;
|
||
background: #f5f5f5;
|
||
border-radius: 12rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.region-selector {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
cursor: pointer;
|
||
|
||
.region-text {
|
||
color: #333;
|
||
}
|
||
|
||
.arrow-icon {
|
||
font-size: 40rpx;
|
||
color: #999;
|
||
font-weight: 300;
|
||
}
|
||
}
|
||
|
||
.form-textarea {
|
||
width: 100%;
|
||
min-height: 160rpx;
|
||
padding: 20rpx 24rpx;
|
||
background: #f5f5f5;
|
||
border-radius: 12rpx;
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
line-height: 1.6;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.input-placeholder {
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
.display-item {
|
||
margin-bottom: 40rpx;
|
||
padding: 30rpx;
|
||
background: #f8f8f8;
|
||
border-radius: 16rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.display-label {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
margin-bottom: 12rpx;
|
||
display: block;
|
||
}
|
||
|
||
.display-value {
|
||
font-size: 30rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
line-height: 1.6;
|
||
}
|
||
}
|
||
}
|
||
|
||
.popup-footer {
|
||
position: sticky;
|
||
bottom: 0;
|
||
background: #fff;
|
||
z-index: 10;
|
||
padding: 30rpx 60rpx;
|
||
padding-bottom: calc(30rpx + env(safe-area-inset-bottom));
|
||
border-top: 1rpx solid #f0f0f0;
|
||
|
||
.button-group {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
|
||
.secondary-btn {
|
||
flex: 1;
|
||
height: 96rpx;
|
||
background: #fff;
|
||
border: 2rpx solid #07c160;
|
||
border-radius: 48rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
text {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #07c160;
|
||
}
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
opacity: 0.8;
|
||
}
|
||
}
|
||
|
||
.confirm-btn {
|
||
flex: 1;
|
||
height: 96rpx;
|
||
background: linear-gradient(135deg, #07c160, #10d673);
|
||
border-radius: 48rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 24rpx rgba(7, 193, 96, 0.3);
|
||
|
||
text {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
}
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
}
|
||
|
||
.confirm-btn {
|
||
width: 100%;
|
||
height: 96rpx;
|
||
background: linear-gradient(135deg, #07c160, #10d673);
|
||
border-radius: 48rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 24rpx rgba(7, 193, 96, 0.3);
|
||
|
||
text {
|
||
font-size: 34rpx;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
}
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style> |