继续完善购物逻辑闭环,consumer模块完成度75%
This commit is contained in:
@@ -1,12 +1,6 @@
|
||||
<!-- 结算页面 -->
|
||||
<template>
|
||||
<view class="checkout-page">
|
||||
<!-- 顶部栏 -->
|
||||
<view class="checkout-header">
|
||||
<text class="back-btn" @click="goBack">‹</text>
|
||||
<text class="header-title">订单结算</text>
|
||||
</view>
|
||||
|
||||
<scroll-view class="checkout-content" scroll-y>
|
||||
<!-- 收货地址 -->
|
||||
<view class="address-section" @click="selectAddress">
|
||||
@@ -107,12 +101,120 @@
|
||||
</view>
|
||||
<button class="submit-btn" @click="submitOrder">提交订单</button>
|
||||
</view>
|
||||
|
||||
<!-- 地址选择弹窗 -->
|
||||
<view v-if="showAddressPopup" class="address-popup-mask" @click="showAddressPopup = false">
|
||||
<view class="address-popup" @click.stop>
|
||||
<view class="popup-header">
|
||||
<text class="popup-title">选择收货地址</text>
|
||||
<text class="popup-close" @click="showAddressPopup = false">×</text>
|
||||
</view>
|
||||
|
||||
<scroll-view class="address-list-container" scroll-y>
|
||||
<!-- 地址列表 -->
|
||||
<view v-if="addressList.length > 0">
|
||||
<view v-for="address in addressList" :key="address.id"
|
||||
class="popup-address-item" @click="handleSelectAddress(address)">
|
||||
<view class="popup-address-header">
|
||||
<text class="popup-address-name">{{ address.recipient_name }}</text>
|
||||
<text class="popup-address-phone">{{ address.phone }}</text>
|
||||
<view v-if="address.is_default" class="popup-default-tag">
|
||||
<text class="popup-tag-text">默认</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="popup-address-detail">{{ getFullAddress(address) }}</text>
|
||||
<view v-if="selectedAddress && selectedAddress.id === address.id" class="popup-selected-indicator">
|
||||
<text>✓</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-else class="popup-empty-address">
|
||||
<text class="popup-empty-icon">📍</text>
|
||||
<text class="popup-empty-text">暂无收货地址</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 新建地址按钮 -->
|
||||
<view class="popup-add-address-btn" @click="handleAddNewAddress">
|
||||
<text class="popup-btn-icon">+</text>
|
||||
<text class="popup-btn-text">新建收货地址</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 新建地址表单弹窗 -->
|
||||
<view v-if="showNewAddressForm" class="address-form-mask" @click="cancelNewAddress">
|
||||
<view class="address-form-popup" @click.stop>
|
||||
<view class="form-header">
|
||||
<text class="form-title">新建收货地址</text>
|
||||
<text class="form-close" @click="cancelNewAddress">×</text>
|
||||
</view>
|
||||
|
||||
<scroll-view class="form-content" scroll-y>
|
||||
<view class="form-item">
|
||||
<text class="form-label">收货人</text>
|
||||
<input class="form-input" v-model="newAddress.recipient_name"
|
||||
placeholder="请输入收货人姓名" />
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">手机号</text>
|
||||
<input class="form-input" v-model="newAddress.phone"
|
||||
placeholder="请输入手机号码" type="number" />
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">智能填写地址</text>
|
||||
<textarea class="form-textarea smart-address-input"
|
||||
v-model="smartAddressInput"
|
||||
placeholder="粘贴如:北京市朝阳区三里屯SOHO A座 张三 13800138000"
|
||||
@input="parseSmartAddress"
|
||||
maxlength="200" />
|
||||
<text class="smart-tip">自动识别:地址+姓名+电话(支持粘贴文本)</text>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">所在地区</text>
|
||||
<view class="region-inputs">
|
||||
<input class="form-input region-input" v-model="newAddress.province"
|
||||
placeholder="省" readonly />
|
||||
<input class="form-input region-input" v-model="newAddress.city"
|
||||
placeholder="市" readonly />
|
||||
<input class="form-input region-input" v-model="newAddress.district"
|
||||
placeholder="区/县" readonly />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="form-item">
|
||||
<text class="form-label">详细地址</text>
|
||||
<textarea class="form-textarea" v-model="newAddress.detail"
|
||||
placeholder="街道、小区、楼栋、门牌号等"
|
||||
maxlength="100" />
|
||||
</view>
|
||||
|
||||
<view class="form-item checkbox-item">
|
||||
<view class="checkbox-wrapper" @click="newAddress.is_default = !newAddress.is_default">
|
||||
<view :class="['checkbox', { checked: newAddress.is_default }]">
|
||||
<text v-if="newAddress.is_default" class="checkbox-check">✓</text>
|
||||
</view>
|
||||
<text class="checkbox-label">设为默认地址</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="form-buttons">
|
||||
<button class="form-cancel-btn" @click="cancelNewAddress">取消</button>
|
||||
<button class="form-submit-btn" @click="saveNewAddress">保存</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import supa from '@/components/supadb/aksupainstance.uts'
|
||||
|
||||
type CheckoutItemType = {
|
||||
id: string
|
||||
@@ -150,6 +252,20 @@ const deliveryOptions = ref<Array<DeliveryOptionType>>([
|
||||
const selectedDelivery = ref<string>('standard')
|
||||
const selectedCoupon = ref<UserCouponType | null>(null)
|
||||
const remark = ref<string>('')
|
||||
const showAddressPopup = ref<boolean>(false)
|
||||
const addressList = ref<Array<any>>([])
|
||||
const newAddress = ref<any>({
|
||||
recipient_name: '',
|
||||
phone: '',
|
||||
province: '',
|
||||
city: '',
|
||||
district: '',
|
||||
detail: '',
|
||||
is_default: false
|
||||
})
|
||||
const showNewAddressForm = ref<boolean>(false)
|
||||
const showSaveConfirm = ref<boolean>(false)
|
||||
const smartAddressInput = ref<string>('')
|
||||
|
||||
// 计算属性
|
||||
const totalAmount = computed(() => {
|
||||
@@ -180,6 +296,24 @@ const actualAmount = computed(() => {
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
loadCheckoutData()
|
||||
|
||||
// 监听地址更新事件
|
||||
uni.$on('addressUpdated', (updatedAddressList: any) => {
|
||||
addressList.value = updatedAddressList
|
||||
|
||||
// 如果当前没有选中地址,尝试选择默认地址
|
||||
if (!selectedAddress.value && addressList.value.length > 0) {
|
||||
const defaultAddress = addressList.value.find(addr => addr.is_default)
|
||||
if (defaultAddress) {
|
||||
selectedAddress.value = defaultAddress
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// 组件卸载时移除事件监听
|
||||
onUnmounted(() => {
|
||||
uni.$off('addressUpdated')
|
||||
})
|
||||
|
||||
// 加载结算数据
|
||||
@@ -196,26 +330,68 @@ const loadCheckoutData = () => {
|
||||
|
||||
// 加载默认地址
|
||||
const loadDefaultAddress = async () => {
|
||||
const userId = getCurrentUserId()
|
||||
if (!userId) return
|
||||
|
||||
try {
|
||||
const { data, error } = await supa
|
||||
.from('user_addresses')
|
||||
.select('*')
|
||||
.eq('user_id', userId)
|
||||
.eq('is_default', true)
|
||||
.single()
|
||||
|
||||
if (error !== null) {
|
||||
console.error('加载默认地址失败:', error)
|
||||
return
|
||||
// 从本地存储加载地址数据
|
||||
const storedAddresses = uni.getStorageSync('addresses')
|
||||
if (storedAddresses) {
|
||||
try {
|
||||
const addresses = JSON.parse(storedAddresses as string) as any[]
|
||||
if (addresses && addresses.length > 0) {
|
||||
// 查找默认地址
|
||||
const defaultAddress = addresses.find((addr: any) => addr.isDefault === true)
|
||||
if (defaultAddress) {
|
||||
// 转换地址格式以匹配selectedAddress的结构
|
||||
selectedAddress.value = {
|
||||
id: defaultAddress.id,
|
||||
recipient_name: defaultAddress.name,
|
||||
phone: defaultAddress.phone,
|
||||
province: defaultAddress.province,
|
||||
city: defaultAddress.city,
|
||||
district: defaultAddress.district,
|
||||
detail: defaultAddress.detail,
|
||||
is_default: defaultAddress.isDefault
|
||||
}
|
||||
} else {
|
||||
// 如果没有默认地址,使用第一个地址
|
||||
const firstAddress = addresses[0]
|
||||
selectedAddress.value = {
|
||||
id: firstAddress.id,
|
||||
recipient_name: firstAddress.name,
|
||||
phone: firstAddress.phone,
|
||||
province: firstAddress.province,
|
||||
city: firstAddress.city,
|
||||
district: firstAddress.district,
|
||||
detail: firstAddress.detail,
|
||||
is_default: firstAddress.isDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('解析地址数据失败:', err)
|
||||
}
|
||||
|
||||
selectedAddress.value = data
|
||||
} catch (err) {
|
||||
console.error('加载默认地址异常:', err)
|
||||
}
|
||||
} else {
|
||||
// 如果没有地址数据,尝试使用Mock数据初始化(为了演示效果)
|
||||
const mockAddress = {
|
||||
id: 'addr_mock_default',
|
||||
name: '测试用户',
|
||||
phone: '13800138000',
|
||||
province: '北京市',
|
||||
city: '北京市',
|
||||
district: '朝阳区',
|
||||
detail: '三里屯SOHO A座',
|
||||
isDefault: true
|
||||
}
|
||||
uni.setStorageSync('addresses', JSON.stringify([mockAddress]))
|
||||
selectedAddress.value = {
|
||||
id: mockAddress.id,
|
||||
recipient_name: mockAddress.name,
|
||||
phone: mockAddress.phone,
|
||||
province: mockAddress.province,
|
||||
city: mockAddress.city,
|
||||
district: mockAddress.district,
|
||||
detail: mockAddress.detail,
|
||||
is_default: mockAddress.isDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前用户ID
|
||||
@@ -229,6 +405,276 @@ const getFullAddress = (address: any): string => {
|
||||
return `${address.province}${address.city}${address.district}${address.detail}`
|
||||
}
|
||||
|
||||
// 加载地址列表
|
||||
const loadAddressList = async () => {
|
||||
// 从本地存储加载地址数据
|
||||
const storedAddresses = uni.getStorageSync('addresses')
|
||||
if (storedAddresses) {
|
||||
try {
|
||||
const addresses = JSON.parse(storedAddresses as string) as any[]
|
||||
if (addresses && addresses.length > 0) {
|
||||
// 转换地址格式以匹配addressList的结构
|
||||
addressList.value = addresses.map((addr: any) => ({
|
||||
id: addr.id,
|
||||
recipient_name: addr.name,
|
||||
phone: addr.phone,
|
||||
province: addr.province,
|
||||
city: addr.city,
|
||||
district: addr.district,
|
||||
detail: addr.detail,
|
||||
is_default: addr.isDefault
|
||||
}))
|
||||
} else {
|
||||
addressList.value = []
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('解析地址数据失败:', err)
|
||||
addressList.value = []
|
||||
}
|
||||
} else {
|
||||
addressList.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 选择地址
|
||||
const handleSelectAddress = (address: any) => {
|
||||
selectedAddress.value = address
|
||||
showAddressPopup.value = false
|
||||
}
|
||||
|
||||
// 新建地址
|
||||
const handleAddNewAddress = () => {
|
||||
showNewAddressForm.value = true
|
||||
}
|
||||
|
||||
// 保存新地址
|
||||
const saveNewAddress = async () => {
|
||||
// 验证表单
|
||||
if (!newAddress.value.recipient_name || !newAddress.value.phone || !newAddress.value.detail) {
|
||||
uni.showToast({
|
||||
title: '请填写完整信息',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const userId = getCurrentUserId()
|
||||
if (!userId) return
|
||||
|
||||
// 触发保存确认弹窗
|
||||
showSaveConfirm.value = true
|
||||
}
|
||||
|
||||
// 处理保存确认
|
||||
const handleSaveConfirm = async (save: boolean) => {
|
||||
showSaveConfirm.value = false
|
||||
|
||||
const newAddressData = {
|
||||
id: `addr_${Date.now()}`,
|
||||
name: newAddress.value.recipient_name,
|
||||
phone: newAddress.value.phone,
|
||||
province: newAddress.value.province,
|
||||
city: newAddress.value.city,
|
||||
district: newAddress.value.district,
|
||||
detail: newAddress.value.detail,
|
||||
isDefault: newAddress.value.is_default,
|
||||
label: ''
|
||||
}
|
||||
|
||||
if (save) {
|
||||
// 从本地存储加载现有地址
|
||||
const storedAddresses = uni.getStorageSync('addresses')
|
||||
let addresses: any[] = []
|
||||
if (storedAddresses) {
|
||||
try {
|
||||
addresses = JSON.parse(storedAddresses as string) as any[]
|
||||
} catch (e) {
|
||||
console.error('解析地址数据失败:', e)
|
||||
addresses = []
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是默认地址,取消其他默认地址
|
||||
if (newAddressData.isDefault) {
|
||||
addresses.forEach(addr => {
|
||||
addr.isDefault = false
|
||||
})
|
||||
}
|
||||
|
||||
// 如果是第一个地址且未设置默认,则自动设为默认
|
||||
if (addresses.length === 0 && !newAddressData.isDefault) {
|
||||
newAddressData.isDefault = true
|
||||
}
|
||||
|
||||
// 添加到地址列表
|
||||
addresses.unshift(newAddressData)
|
||||
|
||||
// 保存到本地存储
|
||||
uni.setStorageSync('addresses', JSON.stringify(addresses))
|
||||
|
||||
// 发布地址更新事件,让address-list页面也能获取到
|
||||
uni.$emit('addressUpdated', addresses.map((addr) => ({
|
||||
id: addr.id,
|
||||
recipient_name: addr.name,
|
||||
phone: addr.phone,
|
||||
province: addr.province,
|
||||
city: addr.city,
|
||||
district: addr.district,
|
||||
detail: addr.detail,
|
||||
is_default: addr.isDefault
|
||||
})))
|
||||
}
|
||||
|
||||
// 转换为checkout页面格式并添加到当前地址列表
|
||||
const checkoutFormatAddress = {
|
||||
id: newAddressData.id,
|
||||
recipient_name: newAddressData.name,
|
||||
phone: newAddressData.phone,
|
||||
province: newAddressData.province,
|
||||
city: newAddressData.city,
|
||||
district: newAddressData.district,
|
||||
detail: newAddressData.detail,
|
||||
is_default: newAddressData.isDefault
|
||||
}
|
||||
|
||||
// 如果是默认地址,取消其他默认地址
|
||||
if (checkoutFormatAddress.is_default) {
|
||||
addressList.value.forEach(addr => {
|
||||
addr.is_default = false
|
||||
})
|
||||
}
|
||||
|
||||
// 添加到当前地址列表
|
||||
addressList.value.unshift(checkoutFormatAddress)
|
||||
|
||||
// 如果选择了保存,address-list 已通过事件收到更新;未保存则不影响地址簿
|
||||
|
||||
// 如果保存的是默认地址,或者当前没有选中地址,则选中这个新地址
|
||||
if (checkoutFormatAddress.is_default || !selectedAddress.value) {
|
||||
selectedAddress.value = checkoutFormatAddress
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
newAddress.value = {
|
||||
recipient_name: '',
|
||||
phone: '',
|
||||
province: '',
|
||||
city: '',
|
||||
district: '',
|
||||
detail: '',
|
||||
is_default: false
|
||||
}
|
||||
smartAddressInput.value = ''
|
||||
|
||||
showNewAddressForm.value = false
|
||||
|
||||
uni.showToast({
|
||||
title: '地址保存成功',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
|
||||
// 解析智能地址
|
||||
const parseSmartAddress = () => {
|
||||
const input = smartAddressInput.value.trim()
|
||||
if (!input) return
|
||||
|
||||
// 重置表单
|
||||
newAddress.value.recipient_name = ''
|
||||
newAddress.value.phone = ''
|
||||
newAddress.value.province = ''
|
||||
newAddress.value.city = ''
|
||||
newAddress.value.district = ''
|
||||
newAddress.value.detail = ''
|
||||
|
||||
// 尝试匹配手机号码(11位数字)
|
||||
const phoneRegex = /(1[3-9]\d{9})/g
|
||||
const phoneMatches = input.match(phoneRegex)
|
||||
if (phoneMatches && phoneMatches.length > 0) {
|
||||
newAddress.value.phone = phoneMatches[0]
|
||||
}
|
||||
|
||||
// 尝试匹配收件人姓名(中文姓名,2-4个汉字)
|
||||
const nameRegex = /([\u4e00-\u9fa5]{2,4})/g
|
||||
const nameMatches = input.match(nameRegex)
|
||||
if (nameMatches && nameMatches.length > 0) {
|
||||
// 取第一个匹配的中文姓名作为收件人
|
||||
newAddress.value.recipient_name = nameMatches[0]
|
||||
}
|
||||
|
||||
// 提取地址部分(移除姓名和手机号)
|
||||
let addressText = input
|
||||
if (newAddress.value.recipient_name) {
|
||||
addressText = addressText.replace(newAddress.value.recipient_name, '')
|
||||
}
|
||||
if (newAddress.value.phone) {
|
||||
addressText = addressText.replace(newAddress.value.phone, '')
|
||||
}
|
||||
|
||||
// 清理地址文本(移除多余的空格和标点)
|
||||
addressText = addressText.replace(/[,,;;\s]+/g, ' ').trim()
|
||||
|
||||
// 地址解析逻辑
|
||||
const patterns = [
|
||||
// 匹配格式:省市区详细地址
|
||||
/^(.*?省)?(.*?市)?(.*?[区县])?(.*)$/,
|
||||
// 匹配格式:省市详细地址
|
||||
/^(.*?省)?(.*?市)?(.*)$/
|
||||
]
|
||||
|
||||
for (const pattern of patterns) {
|
||||
const match = addressText.match(pattern)
|
||||
if (match) {
|
||||
const [, province, city, district, detail] = match
|
||||
|
||||
if (province) newAddress.value.province = province.replace('省', '').trim()
|
||||
if (city) newAddress.value.city = city.replace('市', '').trim()
|
||||
if (district) newAddress.value.district = district.trim()
|
||||
if (detail) newAddress.value.detail = detail.trim()
|
||||
|
||||
// 如果详细地址为空,但还有剩余内容,则作为详细地址
|
||||
if (!newAddress.value.detail && district && detail) {
|
||||
newAddress.value.detail = detail.trim()
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有匹配到模式,尝试简单分割
|
||||
if (!newAddress.value.province && !newAddress.value.city && !newAddress.value.district) {
|
||||
// 尝试按常见分隔符分割
|
||||
const parts = addressText.split(/[省市县区]/)
|
||||
if (parts.length >= 2) {
|
||||
newAddress.value.province = parts[0] || ''
|
||||
newAddress.value.city = parts[1] || ''
|
||||
newAddress.value.detail = parts.slice(2).join('').trim() || addressText
|
||||
} else {
|
||||
newAddress.value.detail = addressText
|
||||
}
|
||||
}
|
||||
|
||||
// 如果地址部分为空,但还有剩余文本,则作为详细地址
|
||||
if (!newAddress.value.detail && addressText.trim()) {
|
||||
newAddress.value.detail = addressText.trim()
|
||||
}
|
||||
}
|
||||
|
||||
// 取消新建地址
|
||||
const cancelNewAddress = () => {
|
||||
showNewAddressForm.value = false
|
||||
newAddress.value = {
|
||||
recipient_name: '',
|
||||
phone: '',
|
||||
province: '',
|
||||
city: '',
|
||||
district: '',
|
||||
detail: '',
|
||||
is_default: false
|
||||
}
|
||||
smartAddressInput.value = ''
|
||||
}
|
||||
|
||||
// 获取规格文本
|
||||
const getSpecText = (specs: any): string => {
|
||||
if (!specs) return ''
|
||||
@@ -242,14 +688,8 @@ const getSpecText = (specs: any): string => {
|
||||
|
||||
// 选择地址
|
||||
const selectAddress = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/mall/consumer/address',
|
||||
events: {
|
||||
addressSelected: (address: any) => {
|
||||
selectedAddress.value = address
|
||||
}
|
||||
}
|
||||
})
|
||||
showAddressPopup.value = true
|
||||
loadAddressList()
|
||||
}
|
||||
|
||||
// 选择配送方式
|
||||
@@ -282,6 +722,12 @@ const submitOrder = async () => {
|
||||
})
|
||||
return
|
||||
}
|
||||
// 模拟提交成功跳转
|
||||
// 实际项目中应等待API返回
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/consumer/payment?orderId=ORDER_MOCK_${Date.now()}&amount=${actualAmount.value}`
|
||||
})
|
||||
return;
|
||||
|
||||
const userId = getCurrentUserId()
|
||||
if (!userId) {
|
||||
@@ -313,7 +759,7 @@ const submitOrder = async () => {
|
||||
|
||||
try {
|
||||
// 创建订单
|
||||
const { data: order, error: orderError } = await supa
|
||||
/* const { data: order, error: orderError } = await supa
|
||||
.from('orders')
|
||||
.insert(orderData)
|
||||
.select()
|
||||
@@ -364,6 +810,15 @@ const submitOrder = async () => {
|
||||
// 跳转到支付页面
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/consumer/payment?orderId=${order.id}&amount=${actualAmount.value}`
|
||||
}) */
|
||||
|
||||
// MOCK ORDER SUBMISSION
|
||||
// 模拟创建成功
|
||||
const mockOrderId = `order_mock_${Date.now()}`
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/consumer/payment?orderId=${mockOrderId}&amount=${actualAmount.value}`
|
||||
})
|
||||
|
||||
} catch (err) {
|
||||
@@ -393,7 +848,7 @@ const clearShoppingCart = async () => {
|
||||
const productIds = checkoutItems.value.map(item => item.product_id)
|
||||
|
||||
try {
|
||||
const { error } = await supa
|
||||
/* const { error } = await supa
|
||||
.from('shopping_cart')
|
||||
.delete()
|
||||
.eq('user_id', userId)
|
||||
@@ -401,7 +856,7 @@ const clearShoppingCart = async () => {
|
||||
|
||||
if (error !== null) {
|
||||
console.error('清空购物车失败:', error)
|
||||
}
|
||||
} */
|
||||
} catch (err) {
|
||||
console.error('清空购物车异常:', err)
|
||||
}
|
||||
@@ -421,21 +876,16 @@ const goBack = () => {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 删除未使用的顶部栏样式 */
|
||||
.checkout-header {
|
||||
background-color: #ffffff;
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
font-size: 24px;
|
||||
color: #333333;
|
||||
padding: 5px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
@@ -736,4 +1186,323 @@ const goBack = () => {
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
/* 地址选择弹窗样式 */
|
||||
.address-popup-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
z-index: 9998;
|
||||
}
|
||||
|
||||
.address-popup {
|
||||
background-color: #ffffff;
|
||||
width: 100%;
|
||||
max-height: 70vh;
|
||||
border-radius: 20px 20px 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.popup-header {
|
||||
padding: 20px 15px;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.popup-close {
|
||||
font-size: 24px;
|
||||
color: #999999;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.address-list-container {
|
||||
flex: 1;
|
||||
padding: 15px;
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
.popup-address-item {
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #e5e5e5;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.popup-address-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.popup-address-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.popup-address-name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.popup-address-phone {
|
||||
font-size: 14px;
|
||||
color: #666666;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.popup-default-tag {
|
||||
background-color: #ff4757;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.popup-tag-text {
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.popup-address-detail {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
line-height: 1.4;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.popup-selected-indicator {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
color: #007aff;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.popup-empty-address {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.popup-empty-icon {
|
||||
font-size: 60px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.popup-empty-text {
|
||||
font-size: 16px;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.popup-add-address-btn {
|
||||
background-color: #007aff;
|
||||
margin: 15px;
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.popup-btn-icon {
|
||||
color: #ffffff;
|
||||
font-size: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.popup-btn-text {
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 新建地址表单弹窗样式 */
|
||||
.address-form-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.address-form-popup {
|
||||
background-color: #ffffff;
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
max-height: 80vh;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-header {
|
||||
padding: 20px 15px;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.form-close {
|
||||
font-size: 24px;
|
||||
color: #999999;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.form-content {
|
||||
flex: 1;
|
||||
padding: 15px;
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid #e5e5e5;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-input[readonly] {
|
||||
background-color: #f9f9f9;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.region-inputs {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.region-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
min-height: 80px;
|
||||
padding: 12px;
|
||||
border: 1px solid #e5e5e5;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.smart-address-input {
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
.smart-tip {
|
||||
font-size: 12px;
|
||||
color: #999999;
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.checkbox-item {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.checkbox-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 1px solid #e5e5e5;
|
||||
border-radius: 4px;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.checkbox.checked {
|
||||
background-color: #007aff;
|
||||
border-color: #007aff;
|
||||
}
|
||||
|
||||
.checkbox-check {
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.form-buttons {
|
||||
display: flex;
|
||||
padding: 15px;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.form-cancel-btn {
|
||||
flex: 1;
|
||||
background-color: #f5f5f5;
|
||||
color: #333333;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.form-submit-btn {
|
||||
flex: 1;
|
||||
background-color: #007aff;
|
||||
color: #ffffff;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user