feat(address): 添加地址管理功能,支持增删改查及空状态展示
This commit is contained in:
parent
3489a065a6
commit
76e0db644d
@ -1,48 +1,296 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref, reactive, onMounted } from 'vue';
|
||||||
|
import tab from '@/plugins/tab';
|
||||||
|
import citySelect from '@/components/u-city-select/u-city-select.vue';
|
||||||
|
|
||||||
|
// 页面状态变量
|
||||||
const show = ref(false);
|
const show = ref(false);
|
||||||
|
const defaultAddress = ref(false);
|
||||||
|
const selectedTag = ref('家');
|
||||||
|
const isEdit = ref(false); // 是否为编辑模式
|
||||||
|
const editId = ref(''); // 编辑地址的ID
|
||||||
|
|
||||||
const setDefault = () => { };
|
// 表单数据和验证状态
|
||||||
|
const form = reactive({
|
||||||
|
name: '',
|
||||||
|
phone: '',
|
||||||
|
region: '',
|
||||||
|
address: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const formErrors = reactive({
|
||||||
|
name: false,
|
||||||
|
phone: false,
|
||||||
|
region: false,
|
||||||
|
address: false
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 获取传递过来的地址数据(如果是编辑模式)
|
||||||
|
const pages = getCurrentPages();
|
||||||
|
const currentPage = pages[pages.length - 1];
|
||||||
|
const options = currentPage.$page?.options;
|
||||||
|
|
||||||
|
if (options && options.id) {
|
||||||
|
isEdit.value = true;
|
||||||
|
editId.value = options.id;
|
||||||
|
loadAddressData(options.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 加载要编辑的地址数据
|
||||||
|
const loadAddressData = (id: string) => {
|
||||||
|
try {
|
||||||
|
const addressList = uni.getStorageSync('addressList') || [];
|
||||||
|
const address = addressList.find((item: any) => item.id === id);
|
||||||
|
|
||||||
|
if (address) {
|
||||||
|
form.name = address.name;
|
||||||
|
form.phone = address.phoneOriginal || address.phone; // 使用原始手机号,而不是隐藏处理后的号码
|
||||||
|
form.region = address.region;
|
||||||
|
form.address = address.address;
|
||||||
|
selectedTag.value = address.tag || '家';
|
||||||
|
defaultAddress.value = address.isDefault;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载地址数据失败', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 设置默认地址
|
||||||
|
const setDefault = (e: any) => {
|
||||||
|
defaultAddress.value = e.detail.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 显示地区选择器
|
||||||
const showRegionPicker = () => {
|
const showRegionPicker = () => {
|
||||||
show.value = true;
|
show.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 确认选择地区
|
||||||
|
const cityChange = (e) => {
|
||||||
|
form.region = e.province.label + e.city.label + e.area.label;
|
||||||
|
formErrors.region = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择标签
|
||||||
|
const selectTag = (tag: string) => {
|
||||||
|
selectedTag.value = tag;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 表单验证
|
||||||
|
const validateForm = () => {
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
// 验证姓名
|
||||||
|
if (!form.name.trim()) {
|
||||||
|
formErrors.name = true;
|
||||||
|
isValid = false;
|
||||||
|
} else {
|
||||||
|
formErrors.name = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证手机号
|
||||||
|
const phoneReg = /^1[3-9]\d{9}$/;
|
||||||
|
if (!phoneReg.test(form.phone)) {
|
||||||
|
formErrors.phone = true;
|
||||||
|
isValid = false;
|
||||||
|
} else {
|
||||||
|
formErrors.phone = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证地区
|
||||||
|
if (!form.region) {
|
||||||
|
formErrors.region = true;
|
||||||
|
isValid = false;
|
||||||
|
} else {
|
||||||
|
formErrors.region = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证详细地址
|
||||||
|
if (!form.address.trim()) {
|
||||||
|
formErrors.address = true;
|
||||||
|
isValid = false;
|
||||||
|
} else {
|
||||||
|
formErrors.address = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 保存地址
|
||||||
|
const saveAddress = () => {
|
||||||
|
if (!validateForm()) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请填写完整信息',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取现有地址列表
|
||||||
|
let addressList = uni.getStorageSync('addressList') || [];
|
||||||
|
|
||||||
|
// 创建新地址对象
|
||||||
|
const addressData = {
|
||||||
|
id: isEdit.value ? editId.value : Date.now().toString(),
|
||||||
|
name: form.name,
|
||||||
|
phone: form.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'), // 隐藏中间4位
|
||||||
|
phoneOriginal: form.phone, // 保存原始手机号码用于编辑
|
||||||
|
region: form.region,
|
||||||
|
address: form.address,
|
||||||
|
tag: selectedTag.value,
|
||||||
|
isDefault: defaultAddress.value
|
||||||
|
};
|
||||||
|
|
||||||
|
if (defaultAddress.value) {
|
||||||
|
// 如果设为默认,将其他地址设为非默认
|
||||||
|
addressList = addressList.map((item: any) => {
|
||||||
|
return { ...item, isDefault: false };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEdit.value) {
|
||||||
|
// 更新现有地址
|
||||||
|
const index = addressList.findIndex((item: any) => item.id === editId.value);
|
||||||
|
if (index !== -1) {
|
||||||
|
addressList[index] = addressData;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 添加新地址
|
||||||
|
addressList.push(addressData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存到本地存储
|
||||||
|
uni.setStorageSync('addressList', addressList);
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: isEdit.value ? '修改成功' : '添加成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 延迟返回,让用户看到提示
|
||||||
|
setTimeout(() => {
|
||||||
|
tab.navigateBack();
|
||||||
|
}, 1000);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('保存地址失败', e);
|
||||||
|
uni.showToast({
|
||||||
|
title: '保存失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除地址
|
||||||
|
const deleteAddress = () => {
|
||||||
|
if (!isEdit.value) return;
|
||||||
|
|
||||||
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '确定要删除此地址吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
try {
|
||||||
|
let addressList = uni.getStorageSync('addressList') || [];
|
||||||
|
addressList = addressList.filter((item: any) => item.id !== editId.value);
|
||||||
|
uni.setStorageSync('addressList', addressList);
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '删除成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
tab.navigateBack();
|
||||||
|
}, 1000);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('删除地址失败', e);
|
||||||
|
uni.showToast({
|
||||||
|
title: '删除失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<view class="wrap">
|
<view class="wrap">
|
||||||
|
<view class="container">
|
||||||
<view class="top">
|
<view class="top">
|
||||||
<view class="item">
|
<view class="item">
|
||||||
<view class="left">收货人</view>
|
<view class="left">
|
||||||
<input type="text" placeholder-class="line" placeholder="请填写收货人姓名" />
|
<text class="required">*</text>收货人
|
||||||
</view>
|
</view>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="form.name"
|
||||||
|
placeholder-class="line"
|
||||||
|
placeholder="请填写收货人姓名"
|
||||||
|
:class="{ 'error-input': formErrors.name }"
|
||||||
|
/>
|
||||||
|
<u-icon name="account" size="36rpx" color="#999"></u-icon>
|
||||||
|
</view>
|
||||||
|
<view class="error-msg" v-if="formErrors.name">请输入收货人姓名</view>
|
||||||
|
|
||||||
<view class="item">
|
<view class="item">
|
||||||
<view class="left">手机号码</view>
|
<view class="left">
|
||||||
<input type="text" placeholder-class="line" placeholder="请填写收货人手机号" />
|
<text class="required">*</text>手机号码
|
||||||
</view>
|
</view>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
v-model="form.phone"
|
||||||
|
placeholder-class="line"
|
||||||
|
placeholder="请填写收货人手机号"
|
||||||
|
maxlength="11"
|
||||||
|
:class="{ 'error-input': formErrors.phone }"
|
||||||
|
/>
|
||||||
|
<u-icon name="phone" size="36rpx" color="#999"></u-icon>
|
||||||
|
</view>
|
||||||
|
<view class="error-msg" v-if="formErrors.phone">请输入正确的手机号码</view>
|
||||||
|
|
||||||
<view class="item" @tap="showRegionPicker">
|
<view class="item" @tap="showRegionPicker">
|
||||||
<view class="left">所在地区</view>
|
<view class="left">
|
||||||
<input disabled type="text" placeholder-class="line" placeholder="省市区县、乡镇等" />
|
<text class="required">*</text>所在地区
|
||||||
</view>
|
</view>
|
||||||
|
<input
|
||||||
|
disabled
|
||||||
|
v-model="form.region"
|
||||||
|
type="text"
|
||||||
|
placeholder-class="line"
|
||||||
|
placeholder="省市区县、乡镇等"
|
||||||
|
:class="{ 'error-input': formErrors.region }"
|
||||||
|
/>
|
||||||
|
<u-icon name="arrow-right" size="36rpx" color="#999"></u-icon>
|
||||||
|
</view>
|
||||||
|
<view class="error-msg" v-if="formErrors.region">请选择所在地区</view>
|
||||||
|
|
||||||
<view class="item address">
|
<view class="item address">
|
||||||
<view class="left">详细地址</view>
|
<view class="left">
|
||||||
<textarea type="text" placeholder-class="line" placeholder="街道、楼牌等" />
|
<text class="required">*</text>详细地址
|
||||||
</view>
|
</view>
|
||||||
<!-- <view class="site-clipboard">
|
<textarea
|
||||||
<textarea placeholder-class="line" value="" placeholder="粘贴文本,可自动识别姓名和地址等" />
|
v-model="form.address"
|
||||||
<view class="clipboard">
|
type="text"
|
||||||
地址粘贴板
|
placeholder-class="line"
|
||||||
<u-icon name="arrow-down" class="icon" :size="20"></u-icon>
|
placeholder="街道、楼牌等"
|
||||||
|
:class="{ 'error-textarea': formErrors.address }"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view> -->
|
<view class="error-msg" v-if="formErrors.address">请输入详细地址</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="bottom">
|
<view class="bottom">
|
||||||
<view class="tag">
|
<view class="tag">
|
||||||
<view class="left">标签</view>
|
<view class="left">标签</view>
|
||||||
<view class="right">
|
<view class="right">
|
||||||
<text class="tags">家</text>
|
<text class="tags" :class="{'active': selectedTag === '家'}" @tap="selectTag('家')">家</text>
|
||||||
<text class="tags">公司</text>
|
<text class="tags" :class="{'active': selectedTag === '公司'}" @tap="selectTag('公司')">公司</text>
|
||||||
<text class="tags">学校</text>
|
<text class="tags" :class="{'active': selectedTag === '学校'}" @tap="selectTag('学校')">学校</text>
|
||||||
<view class="tags plus"><u-icon size="22" name="plus"></u-icon></view>
|
<view class="tags plus"><u-icon size="22" name="plus" color="#999"></u-icon></view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="default">
|
<view class="default">
|
||||||
@ -51,11 +299,22 @@ const showRegionPicker = () => {
|
|||||||
<view class="tips">提醒:每次下单会默认推荐该地址</view>
|
<view class="tips">提醒:每次下单会默认推荐该地址</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="right">
|
<view class="right">
|
||||||
<switch color="red" @change="setDefault" />
|
<switch color="#fa3534" :checked="defaultAddress" @change="setDefault" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<u-picker mode="region" ref="uPicker" v-model="show" />
|
|
||||||
|
<view class="button-group">
|
||||||
|
<view class="save-btn" @tap="saveAddress">
|
||||||
|
{{ isEdit ? '保存修改' : '保存地址' }}
|
||||||
|
</view>
|
||||||
|
<view v-if="isEdit" class="delete-btn" @tap="deleteAddress">
|
||||||
|
删除地址
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<city-select v-model="show" @city-change="cityChange"></city-select>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -66,65 +325,84 @@ const showRegionPicker = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.wrap {
|
.wrap {
|
||||||
background-color: #f2f2f2;
|
background-color: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 20rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
border-radius: 16rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
.top {
|
.top {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-top: solid 2rpx $u-border-color;
|
padding: 30rpx;
|
||||||
padding: 22rpx;
|
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
line-height: 100rpx;
|
line-height: 100rpx;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-bottom: solid 2rpx $u-border-color;
|
border-bottom: solid 1rpx #eeeeee;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
width: 180rpx;
|
width: 180rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
.required {
|
||||||
|
color: #fa3534;
|
||||||
|
margin-right: 4rpx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
flex: 1;
|
||||||
|
height: 100rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
|
||||||
|
&.error-input {
|
||||||
|
border-bottom: 1px solid #fa3534;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u-icon {
|
||||||
|
margin-left: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-msg {
|
||||||
|
color: #fa3534;
|
||||||
|
font-size: 24rpx;
|
||||||
|
padding-left: 180rpx;
|
||||||
|
margin-top: -10rpx;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.address {
|
.address {
|
||||||
padding: 20rpx 0;
|
padding: 20rpx 0;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
padding-top: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
// width: 100%;
|
flex: 1;
|
||||||
height: 150rpx;
|
height: 180rpx;
|
||||||
background-color: #f7f7f7;
|
background-color: #f9f9f9;
|
||||||
line-height: 60rpx;
|
line-height: 60rpx;
|
||||||
margin: 40rpx auto;
|
margin: 20rpx 0;
|
||||||
padding: 20rpx;
|
padding: 20rpx;
|
||||||
}
|
border-radius: 12rpx;
|
||||||
}
|
font-size: 30rpx;
|
||||||
|
|
||||||
.site-clipboard {
|
&.error-textarea {
|
||||||
padding-right: 40rpx;
|
border: 1px solid #fa3534;
|
||||||
|
|
||||||
textarea {
|
|
||||||
// width: 100%;
|
|
||||||
height: 150rpx;
|
|
||||||
background-color: #f7f7f7;
|
|
||||||
line-height: 60rpx;
|
|
||||||
margin: 40rpx auto;
|
|
||||||
padding: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clipboard {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: $u-tips-color;
|
|
||||||
height: 80rpx;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
margin-top: 6rpx;
|
|
||||||
margin-left: 10rpx;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,55 +410,106 @@ const showRegionPicker = () => {
|
|||||||
|
|
||||||
.bottom {
|
.bottom {
|
||||||
margin-top: 20rpx;
|
margin-top: 20rpx;
|
||||||
padding: 40rpx;
|
padding: 30rpx;
|
||||||
padding-right: 0;
|
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
|
||||||
.tag {
|
.tag {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
width: 160rpx;
|
width: 160rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right {
|
.right {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
.tags {
|
.tags {
|
||||||
width: 140rpx;
|
width: 150rpx;
|
||||||
padding: 16rpx 8rpx;
|
padding: 20rpx 10rpx;
|
||||||
border: solid 2rpx $u-border-color;
|
border: solid 2rpx #eeeeee;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 50rpx;
|
border-radius: 100rpx;
|
||||||
margin: 0 10rpx 20rpx;
|
margin: 0 20rpx 20rpx 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: $u-content-color;
|
color: #333;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #ffebec;
|
||||||
|
color: #fa3534;
|
||||||
|
border-color: #fa3534;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.plus {
|
.plus {
|
||||||
//padding: 10rpx 0;
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.default {
|
.default {
|
||||||
margin-top: 50rpx;
|
margin-top: 30rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
border-bottom: solid 2rpx $u-border-color;
|
align-items: center;
|
||||||
line-height: 64rpx;
|
padding-bottom: 20rpx;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
.set {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
font-size: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
.tips {
|
.tips {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.right {}
|
.button-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 60rpx;
|
||||||
|
|
||||||
|
.save-btn {
|
||||||
|
background: linear-gradient(90deg, #ff4034, #fa3534);
|
||||||
|
color: #fff;
|
||||||
|
height: 90rpx;
|
||||||
|
line-height: 90rpx;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 32rpx;
|
||||||
|
border-radius: 45rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
box-shadow: 0 10rpx 20rpx rgba(250, 53, 52, 0.2);
|
||||||
|
letter-spacing: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn {
|
||||||
|
margin-top: 30rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #fa3534;
|
||||||
|
border: 1px solid #fa3534;
|
||||||
|
height: 90rpx;
|
||||||
|
line-height: 90rpx;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 32rpx;
|
||||||
|
border-radius: 45rpx;
|
||||||
|
letter-spacing: 2rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,64 +1,147 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { onShow } from '@dcloudio/uni-app';
|
||||||
import tab from '@/plugins/tab';
|
import tab from '@/plugins/tab';
|
||||||
|
|
||||||
const siteList = ref([]);
|
const siteList = ref([]);
|
||||||
|
const emptyStatus = ref(false);
|
||||||
|
|
||||||
onMounted(() => {
|
// 使用onShow钩子确保每次页面显示时都刷新地址列表
|
||||||
|
onShow(() => {
|
||||||
getData();
|
getData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 从本地存储获取地址数据
|
||||||
function getData() {
|
function getData() {
|
||||||
siteList.value = [
|
try {
|
||||||
{
|
const addressList = uni.getStorageSync('addressList') || [];
|
||||||
id: 1,
|
siteList.value = addressList;
|
||||||
name: '游X',
|
emptyStatus.value = addressList.length === 0;
|
||||||
phone: '183****5523',
|
} catch (e) {
|
||||||
tag: [
|
console.error('获取地址列表失败', e);
|
||||||
{ tagText: '默认' },
|
uni.showToast({
|
||||||
{ tagText: '家' }
|
title: '获取地址列表失败',
|
||||||
],
|
icon: 'none'
|
||||||
site: '广东省深圳市宝安区 自由路66号'
|
});
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: '李XX',
|
|
||||||
phone: '183****5555',
|
|
||||||
tag: [
|
|
||||||
{ tagText: '公司' }
|
|
||||||
],
|
|
||||||
site: '广东省深圳市宝安区 翻身路xx号'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: '王YY',
|
|
||||||
phone: '153****5555',
|
|
||||||
tag: [],
|
|
||||||
site: '广东省深圳市宝安区 平安路13号'
|
|
||||||
}
|
}
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 跳转到添加地址页面
|
||||||
function toAddSite() {
|
function toAddSite() {
|
||||||
tab.navigateTo('/pages_template/pages/address/addSite');
|
tab.navigateTo('/pages_template/pages/address/addSite');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 跳转到编辑地址页面
|
||||||
|
function toEditSite(id) {
|
||||||
|
tab.navigateTo(`/pages_template/pages/address/addSite?id=${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置为默认地址
|
||||||
|
function setAsDefault(id) {
|
||||||
|
try {
|
||||||
|
let addressList = uni.getStorageSync('addressList') || [];
|
||||||
|
addressList = addressList.map(item => {
|
||||||
|
return { ...item, isDefault: item.id === id };
|
||||||
|
});
|
||||||
|
|
||||||
|
uni.setStorageSync('addressList', addressList);
|
||||||
|
getData(); // 刷新列表
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '设置成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('设置默认地址失败', e);
|
||||||
|
uni.showToast({
|
||||||
|
title: '设置失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除地址
|
||||||
|
function deleteAddress(id) {
|
||||||
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '确定要删除此地址吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
try {
|
||||||
|
let addressList = uni.getStorageSync('addressList') || [];
|
||||||
|
addressList = addressList.filter(item => item.id !== id);
|
||||||
|
uni.setStorageSync('addressList', addressList);
|
||||||
|
getData(); // 刷新列表
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: '删除成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('删除地址失败', e);
|
||||||
|
uni.showToast({
|
||||||
|
title: '删除失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择并返回地址(用于从订单页面选择地址的场景)
|
||||||
|
function selectAddress(address) {
|
||||||
|
const pages = getCurrentPages();
|
||||||
|
const prevPage = pages[pages.length - 2];
|
||||||
|
|
||||||
|
// 检查页面是否从订单页面跳转而来
|
||||||
|
if (prevPage && prevPage.$page?.options?.from === 'order') {
|
||||||
|
// 设置选中的地址并返回
|
||||||
|
uni.$emit('address-selected', address);
|
||||||
|
tab.navigateBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<view>
|
<view class="address-container">
|
||||||
<view class="item" v-for="(res, index) in siteList" :key="res.id">
|
<!-- 空状态 -->
|
||||||
<view class="top">
|
<view class="empty-state" v-if="emptyStatus">
|
||||||
<view class="name">{{ res.name }}</view>
|
<image src="/static/images/empty-address.png" mode="aspectFit" class="empty-image"></image>
|
||||||
<view class="phone">{{ res.phone }}</view>
|
<view class="empty-text">您还没有添加收货地址</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 地址列表 -->
|
||||||
|
<view v-else>
|
||||||
|
<view class="item" v-for="(address, index) in siteList" :key="address.id">
|
||||||
|
<view class="top" @tap="selectAddress(address)">
|
||||||
|
<view class="name">{{ address.name }}</view>
|
||||||
|
<view class="phone">{{ address.phone }}</view>
|
||||||
<view class="tag">
|
<view class="tag">
|
||||||
<text v-for="(item, index) in res.tag" :key="index" :class="{ red: item.tagText == '默认' }">{{
|
<text v-if="address.isDefault" class="red">默认</text>
|
||||||
item.tagText }}</text>
|
<text v-if="address.tag">{{ address.tag }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="bottom">
|
<view class="bottom" @tap="selectAddress(address)">
|
||||||
广东省深圳市宝安区 自由路66号
|
{{ address.region }} {{ address.address }}
|
||||||
<u-icon name="edit-pen" :size="40" color="#999999"></u-icon>
|
</view>
|
||||||
|
<view class="actions">
|
||||||
|
<view class="action-btn" @tap="setAsDefault(address.id)" v-if="!address.isDefault">
|
||||||
|
<u-icon name="checkmark-circle" color="#999" size="40rpx"></u-icon>
|
||||||
|
<text>设为默认</text>
|
||||||
|
</view>
|
||||||
|
<view class="action-btn" @tap="toEditSite(address.id)">
|
||||||
|
<u-icon name="edit-pen" color="#999" size="40rpx"></u-icon>
|
||||||
|
<text>编辑</text>
|
||||||
|
</view>
|
||||||
|
<view class="action-btn" @tap="deleteAddress(address.id)">
|
||||||
|
<u-icon name="trash" color="#999" size="40rpx"></u-icon>
|
||||||
|
<text>删除</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 新建地址按钮 -->
|
||||||
<view class="addSite" @tap="toAddSite">
|
<view class="addSite" @tap="toAddSite">
|
||||||
<view class="add">
|
<view class="add">
|
||||||
<u-icon name="plus" color="#ffffff" class="icon" :size="30"></u-icon>新建收货地址
|
<u-icon name="plus" color="#ffffff" class="icon" :size="30"></u-icon>新建收货地址
|
||||||
@ -66,68 +149,120 @@ function toAddSite() {
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.address-container {
|
||||||
|
padding-bottom: 180rpx;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 200rpx;
|
||||||
|
|
||||||
|
.empty-image {
|
||||||
|
width: 200rpx;
|
||||||
|
height: 200rpx;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
color: #999;
|
||||||
|
font-size: 30rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
padding: 40rpx 20rpx;
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
margin: 20rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
background-color: #ffffff;
|
||||||
|
|
||||||
.top {
|
.top {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 32rpx;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 34rpx;
|
|
||||||
|
|
||||||
.phone {
|
.phone {
|
||||||
margin-left: 60rpx;
|
margin-left: 40rpx;
|
||||||
|
color: #666666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag {
|
.tag {
|
||||||
display: flex;
|
margin-left: auto;
|
||||||
font-weight: normal;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
text {
|
text {
|
||||||
display: block;
|
background-color: #e0f7fa;
|
||||||
width: 60rpx;
|
color: #00bcd4;
|
||||||
height: 34rpx;
|
border-radius: 20rpx;
|
||||||
line-height: 34rpx;
|
padding: 8rpx 16rpx;
|
||||||
color: #ffffff;
|
font-size: 24rpx;
|
||||||
font-size: 20rpx;
|
margin-left: 10rpx;
|
||||||
border-radius: 6rpx;
|
|
||||||
text-align: center;
|
|
||||||
margin-left: 30rpx;
|
|
||||||
background-color: rgb(49, 145, 253);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.red {
|
.red {
|
||||||
background-color: red
|
background-color: #ffebee;
|
||||||
|
color: #d32f2f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom {
|
.bottom {
|
||||||
display: flex;
|
|
||||||
margin-top: 20rpx;
|
margin-top: 20rpx;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
justify-content: space-between;
|
color: #666666;
|
||||||
color: #999999;
|
line-height: 1.6;
|
||||||
|
padding-bottom: 20rpx;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 30rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
text {
|
||||||
|
margin-left: 6rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.addSite {
|
.addSite {
|
||||||
display: flex;
|
position: fixed;
|
||||||
justify-content: space-around;
|
bottom: 40rpx;
|
||||||
width: 600rpx;
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 80%;
|
||||||
|
height: 100rpx;
|
||||||
line-height: 100rpx;
|
line-height: 100rpx;
|
||||||
position: absolute;
|
background: linear-gradient(90deg, #ff4034, #fa3534);
|
||||||
bottom: 30rpx;
|
border-radius: 50rpx;
|
||||||
left: 80rpx;
|
text-align: center;
|
||||||
background-color: red;
|
color: #ffffff;
|
||||||
border-radius: 60rpx;
|
font-size: 32rpx;
|
||||||
font-size: 30rpx;
|
box-shadow: 0 8rpx 16rpx rgba(250, 53, 52, 0.2);
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
.add {
|
.add {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: #ffffff;
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
margin-right: 10rpx;
|
margin-right: 10rpx;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user