|
|
<template>
|
|
|
<view class="container">
|
|
|
<!-- 顶部导航栏 -->
|
|
|
<view class="nav-bar">
|
|
|
<view class="back-btn" @click="goBack">
|
|
|
<uv-icon name="arrow-left" color="#333" size="20"></uv-icon>
|
|
|
</view>
|
|
|
<view class="title">新增地址</view>
|
|
|
<view class="right-placeholder"></view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 表单内容 -->
|
|
|
<view class="form-content">
|
|
|
<!-- 联系人信息 -->
|
|
|
<view class="form-section">
|
|
|
<view class="form-item">
|
|
|
<text class="label">联系人</text>
|
|
|
<input class="input" v-model="formData.name" placeholder="请输入联系人姓名" />
|
|
|
</view>
|
|
|
<view class="form-item">
|
|
|
<text class="label">手机号码</text>
|
|
|
<input class="input" v-model="formData.phone" type="number" placeholder="请输入手机号码" maxlength="11" />
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 地址信息 -->
|
|
|
<view class="form-section">
|
|
|
<view class="form-item location-item" @click="chooseLocation">
|
|
|
<text class="label">地址位置</text>
|
|
|
<view class="location-value">
|
|
|
<text v-if="formData.address">{{ formData.address }}</text>
|
|
|
<text v-else class="placeholder">点击选择位置</text>
|
|
|
<uv-icon name="arrow-right" color="#CCCCCC" size="16"></uv-icon>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="form-item">
|
|
|
<text class="label">门牌号</text>
|
|
|
<input class="input" v-model="formData.addressDetail" placeholder="详细地址,如:1号楼1单元101室" />
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 地址标签 -->
|
|
|
<view class="form-section">
|
|
|
<view class="label-title">地址标签</view>
|
|
|
<view class="tag-list">
|
|
|
<view
|
|
|
class="tag-item"
|
|
|
:class="{ active: formData.tag === tag }"
|
|
|
v-for="(tag, index) in tags"
|
|
|
:key="index"
|
|
|
@click="selectTag(tag)"
|
|
|
>
|
|
|
{{ tag }}
|
|
|
</view>
|
|
|
<view
|
|
|
class="tag-item custom-tag"
|
|
|
:class="{ active: isCustomTag }"
|
|
|
@click="showCustomTagInput"
|
|
|
>
|
|
|
<text v-if="!isCustomTag">自定义</text>
|
|
|
<input
|
|
|
v-else
|
|
|
class="custom-tag-input"
|
|
|
v-model="formData.customTag"
|
|
|
placeholder="自定义标签"
|
|
|
focus
|
|
|
@blur="onCustomTagBlur"
|
|
|
@confirm="onCustomTagConfirm"
|
|
|
/>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 默认地址设置 -->
|
|
|
<view class="form-section">
|
|
|
<view class="form-item switch-item">
|
|
|
<text class="label">设为默认地址</text>
|
|
|
<switch
|
|
|
:checked="formData.isDefault"
|
|
|
color="#4080FF"
|
|
|
@change="onDefaultChange"
|
|
|
/>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 底部保存按钮 -->
|
|
|
<view class="footer">
|
|
|
<view class="save-btn" @click="saveAddress">保存</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import { ref, computed } from 'vue';
|
|
|
import { useUserStore } from "@/store";
|
|
|
|
|
|
// 表单数据
|
|
|
const formData = ref({
|
|
|
name: '',
|
|
|
phone: '',
|
|
|
address: '',
|
|
|
addressDetail: '',
|
|
|
latitude: '',
|
|
|
longitude: '',
|
|
|
tag: '家',
|
|
|
customTag: '',
|
|
|
isDefault: false
|
|
|
});
|
|
|
|
|
|
// 预设标签
|
|
|
const tags = ref(['家', '公司', '学校']);
|
|
|
|
|
|
// 是否使用自定义标签
|
|
|
const isCustomTag = computed(() => {
|
|
|
return !tags.value.includes(formData.value.tag);
|
|
|
});
|
|
|
|
|
|
// 选择标签
|
|
|
const selectTag = (tag) => {
|
|
|
formData.value.tag = tag;
|
|
|
};
|
|
|
|
|
|
// 显示自定义标签输入框
|
|
|
const showCustomTagInput = () => {
|
|
|
if (!isCustomTag.value) {
|
|
|
formData.value.customTag = '';
|
|
|
formData.value.tag = '';
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 自定义标签输入框失焦
|
|
|
const onCustomTagBlur = () => {
|
|
|
if (formData.value.customTag) {
|
|
|
formData.value.tag = formData.value.customTag;
|
|
|
} else {
|
|
|
formData.value.tag = '家'; // 默认回到第一个标签
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 自定义标签输入确认
|
|
|
const onCustomTagConfirm = () => {
|
|
|
if (formData.value.customTag) {
|
|
|
formData.value.tag = formData.value.customTag;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 默认地址切换
|
|
|
const onDefaultChange = (e) => {
|
|
|
formData.value.isDefault = e.detail.value;
|
|
|
};
|
|
|
|
|
|
// 选择位置
|
|
|
const chooseLocation = () => {
|
|
|
uni.chooseLocation({
|
|
|
success: (res) => {
|
|
|
console.log('选择位置成功:', res);
|
|
|
formData.value.address = res.name || res.address;
|
|
|
formData.value.latitude = res.latitude;
|
|
|
formData.value.longitude = res.longitude;
|
|
|
},
|
|
|
fail: (err) => {
|
|
|
console.error('选择位置失败:', err);
|
|
|
// 检查是否是因为权限问题
|
|
|
if (err.errMsg.includes('auth deny')) {
|
|
|
uni.showModal({
|
|
|
title: '提示',
|
|
|
content: '需要您授权使用位置信息',
|
|
|
confirmText: '去设置',
|
|
|
success: (res) => {
|
|
|
if (res.confirm) {
|
|
|
uni.openSetting({
|
|
|
success: (settingRes) => {
|
|
|
console.log('设置结果:', settingRes);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
} else {
|
|
|
uni.showToast({
|
|
|
title: '选择位置失败',
|
|
|
icon: 'none'
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
|
|
|
// 保存地址
|
|
|
const saveAddress = () => {
|
|
|
// 表单验证
|
|
|
if (!formData.value.name) {
|
|
|
return uni.showToast({
|
|
|
title: '请输入联系人姓名',
|
|
|
icon: 'none'
|
|
|
});
|
|
|
}
|
|
|
|
|
|
if (!formData.value.phone) {
|
|
|
return uni.showToast({
|
|
|
title: '请输入手机号码',
|
|
|
icon: 'none'
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 简单的手机号验证
|
|
|
if (!/^1\d{10}$/.test(formData.value.phone)) {
|
|
|
return uni.showToast({
|
|
|
title: '请输入正确的手机号码',
|
|
|
icon: 'none'
|
|
|
});
|
|
|
}
|
|
|
|
|
|
if (!formData.value.address) {
|
|
|
return uni.showToast({
|
|
|
title: '请选择地址位置',
|
|
|
icon: 'none'
|
|
|
});
|
|
|
}
|
|
|
|
|
|
if (!formData.value.addressDetail) {
|
|
|
return uni.showToast({
|
|
|
title: '请输入详细地址',
|
|
|
icon: 'none'
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 构建地址对象
|
|
|
const addressObj = {
|
|
|
id: Date.now().toString(), // 使用时间戳作为临时ID
|
|
|
name: formData.value.name,
|
|
|
phone: formData.value.phone,
|
|
|
address: formData.value.address,
|
|
|
addressDetail: formData.value.addressDetail,
|
|
|
latitude: formData.value.latitude,
|
|
|
longitude: formData.value.longitude,
|
|
|
tag: formData.value.tag,
|
|
|
isDefault: formData.value.isDefault
|
|
|
};
|
|
|
|
|
|
// 获取已保存的地址列表
|
|
|
let addressList = [];
|
|
|
try {
|
|
|
const savedAddresses = uni.getStorageSync('userAddressList');
|
|
|
if (savedAddresses) {
|
|
|
addressList = JSON.parse(savedAddresses);
|
|
|
}
|
|
|
} catch (e) {
|
|
|
console.error('获取地址列表失败', e);
|
|
|
}
|
|
|
|
|
|
// 如果设置为默认地址,需要将其他地址设为非默认
|
|
|
if (addressObj.isDefault) {
|
|
|
addressList.forEach(item => {
|
|
|
item.isDefault = false;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 添加新地址
|
|
|
addressList.push(addressObj);
|
|
|
|
|
|
// 保存地址列表
|
|
|
try {
|
|
|
uni.setStorageSync('userAddressList', JSON.stringify(addressList));
|
|
|
|
|
|
// 如果是默认地址,更新当前使用的地址
|
|
|
if (addressObj.isDefault) {
|
|
|
useUserStore().setAdressInfo({
|
|
|
latitude: addressObj.latitude,
|
|
|
longitude: addressObj.longitude,
|
|
|
adress: addressObj.address,
|
|
|
addressDetail: addressObj.addressDetail
|
|
|
});
|
|
|
}
|
|
|
|
|
|
uni.showToast({
|
|
|
title: '保存成功',
|
|
|
icon: 'success'
|
|
|
});
|
|
|
|
|
|
// 延迟返回上一页
|
|
|
setTimeout(() => {
|
|
|
uni.navigateBack({
|
|
|
delta: 1
|
|
|
});
|
|
|
}, 1500);
|
|
|
} catch (e) {
|
|
|
console.error('保存地址失败', e);
|
|
|
uni.showToast({
|
|
|
title: '保存失败,请重试',
|
|
|
icon: 'none'
|
|
|
});
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 返回上一页
|
|
|
const goBack = () => {
|
|
|
uni.navigateBack({
|
|
|
delta: 1
|
|
|
});
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.container {
|
|
|
font-size: 14px;
|
|
|
min-height: 100vh;
|
|
|
background-color: #f8f8f8;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
|
|
|
.nav-bar {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
height: 90rpx;
|
|
|
background-color: #FFFFFF;
|
|
|
padding: 0 30rpx;
|
|
|
position: relative;
|
|
|
|
|
|
.back-btn {
|
|
|
width: 60rpx;
|
|
|
height: 60rpx;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
.title {
|
|
|
position: absolute;
|
|
|
left: 50%;
|
|
|
top: 50%;
|
|
|
transform: translate(-50%, -50%);
|
|
|
font-size: 32rpx;
|
|
|
font-weight: bold;
|
|
|
}
|
|
|
|
|
|
.right-placeholder {
|
|
|
width: 60rpx;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.form-content {
|
|
|
flex: 1;
|
|
|
padding-bottom: 120rpx;
|
|
|
}
|
|
|
|
|
|
.form-section {
|
|
|
background-color: #FFFFFF;
|
|
|
margin-top: 20rpx;
|
|
|
padding: 0 30rpx;
|
|
|
|
|
|
.label-title {
|
|
|
padding: 30rpx 0 20rpx;
|
|
|
font-size: 28rpx;
|
|
|
color: #333333;
|
|
|
}
|
|
|
|
|
|
.form-item {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
min-height: 100rpx;
|
|
|
border-bottom: 1px solid #F5F5F5;
|
|
|
|
|
|
&:last-child {
|
|
|
border-bottom: none;
|
|
|
}
|
|
|
|
|
|
.label {
|
|
|
width: 160rpx;
|
|
|
font-size: 28rpx;
|
|
|
color: #333333;
|
|
|
}
|
|
|
|
|
|
.input {
|
|
|
flex: 1;
|
|
|
height: 100rpx;
|
|
|
font-size: 28rpx;
|
|
|
}
|
|
|
|
|
|
&.location-item {
|
|
|
.location-value {
|
|
|
flex: 1;
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
.placeholder {
|
|
|
color: #999999;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
&.switch-item {
|
|
|
justify-content: space-between;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.tag-list {
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
padding: 20rpx 0 30rpx;
|
|
|
|
|
|
.tag-item {
|
|
|
padding: 10rpx 30rpx;
|
|
|
background-color: #F5F5F5;
|
|
|
border-radius: 30rpx;
|
|
|
margin-right: 20rpx;
|
|
|
margin-bottom: 20rpx;
|
|
|
font-size: 26rpx;
|
|
|
color: #666666;
|
|
|
|
|
|
&.active {
|
|
|
background-color: #E6F2FF;
|
|
|
color: #4080FF;
|
|
|
border: 1px solid #4080FF;
|
|
|
}
|
|
|
|
|
|
&.custom-tag {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
min-width: 120rpx;
|
|
|
|
|
|
.custom-tag-input {
|
|
|
width: 120rpx;
|
|
|
text-align: center;
|
|
|
height: 40rpx;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.footer {
|
|
|
position: fixed;
|
|
|
bottom: 0;
|
|
|
left: 0;
|
|
|
right: 0;
|
|
|
background-color: #FFFFFF;
|
|
|
padding: 20rpx 30rpx;
|
|
|
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
|
|
|
|
.save-btn {
|
|
|
height: 80rpx;
|
|
|
background-color: #4080FF;
|
|
|
color: #FFFFFF;
|
|
|
border-radius: 40rpx;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
font-size: 30rpx;
|
|
|
}
|
|
|
}
|
|
|
</style> |