This commit is contained in:
2026-03-06 13:41:22 +08:00
commit f39c6a705f
394 changed files with 159599 additions and 0 deletions

View File

@ -0,0 +1,175 @@
<template>
<uni-swipe-action :key="data.id" class="address-item-container">
<uni-swipe-action-item
class="address-item-content"
:right-options="{}"
:auto-close="false"
@click="$emit('jumpToDetails', data)"
>
<view
class="flex-row-start address-item"
>
<view class="address-item-clickable" @click="$emit('toDetails', data)">
<view class="address-content">
<view class="flex-row-start">
<text
class="fs-20 PingFangSC-Regular default-tip"
v-if="data.is_default"
>默认</text
>
<text class="fs-30 app-fc-main PingFangSC-Medium address-user">
{{ data.name || "" }}
</text>
<text class="fs-30 app-fc-main PingFangSC-Medium">
{{ data.phone || "" }}
</text>
</view>
<view
class="app-fc-normal fs-26 PingFangSC-Regular app-text-ellipse address-name"
>
{{ data.address || "" }}
</view>
</view>
</view>
<view class="address-right-content">
<view class="switch-container" @tap.stop="handleSwitchClick">
<switch
:checked="data.is_default"
@change="onDefaultChange"
color="#FF19A0"
class="default-switch"
/>
</view>
<image
class="address-edit-icon"
src="@/static/images/address_edit.png"
@click.stop="$emit('toDetails', data)"
/>
</view>
</view>
<view slot="right" class="flex-center address-right">
<image
class="address-delete"
src="@/static/images/mine_delete.png"
@click.stop="$emit('delete', data)"
/>
</view>
</uni-swipe-action-item>
</uni-swipe-action>
</template>
<script>
export default {
props: {
data: {
type: Object,
default: () => {},
},
},
data() {
return {};
},
methods: {
handleSwitchClick(e) {
console.log('handleSwitchClick 触发');
// 手动触发开关切换
const newValue = !this.data.is_default;
this.emitSetDefault(newValue);
},
onDefaultChange(e) {
console.log('onDefaultChange 触发:', e, e.detail, this.data);
// 阻止事件冒泡到父元素,避免触发编辑
if (e && e.stopPropagation) {
e.stopPropagation();
}
// 获取新的开关状态
const newValue = e.detail?.value !== undefined ? e.detail.value : (e.detail?.checked !== undefined ? e.detail.checked : !this.data.is_default);
console.log('新的开关状态:', newValue);
this.emitSetDefault(newValue);
},
emitSetDefault(newValue) {
// 发送设置默认地址事件
const newData = {
...this.data,
is_default: newValue ? 1 : 0
};
console.log('发送 setDefault 事件:', newData);
this.$emit('setDefault', newData);
}
},
};
</script>
<style lang="scss" scoped>
.address-item-container {
.address-item {
width: calc(100vw - 32rpx * 2);
padding: 40rpx 30rpx;
background: #fff;
border-radius: 30rpx 30rpx 30rpx 30rpx;
box-sizing: border-box;
margin-bottom: 20rpx;
margin-left: 32rpx;
margin-right: 32rpx;
.address-right-content {
display: flex;
align-items: center;
gap: 20rpx;
flex-shrink: 0;
}
.switch-container {
display: flex;
align-items: center;
padding: 10rpx;
margin: -10rpx;
}
.default-switch {
transform: scale(0.8);
transform-origin: right center;
}
.address-edit-icon {
width: 48rpx;
height: 48rpx;
}
.address-item-clickable {
flex: 1;
display: flex;
flex-direction: column;
}
.address-content {
flex: 1;
overflow: hidden;
.default-tip {
padding: 6rpx;
color: #40ae36;
background: rgba(64, 174, 54, 0.2);
margin-right: 16rpx;
}
.address-name {
margin-top: 12rpx;
}
.address-user {
margin-right: 16rpx;
}
}
}
.address-right {
padding: 0 32rpx 0 0;
.address-delete {
width: 88rpx;
height: 88rpx;
}
}
}
</style>

View File

@ -0,0 +1,407 @@
<template>
<view class="address-edit">
<view class="edit-form">
<form-cell
title="姓名"
placeholderText="请输入姓名"
:value="addressInfo.name"
:showRightArrow="false"
@onChange="(value) => onChange(value, 'name')"
/>
<form-cell
title="手机号"
placeholderText="请输入手机号"
:value="addressInfo.phone"
:showRightArrow="false"
@onChange="(value) => onChange(value, 'phone')"
/>
<form-cell
title="省市区"
type="checkAddress"
:value="regionText"
:defaultRegion="[provinceIndex, cityIndex, areaIndex]"
@onChange="onRegionChange"
/>
<form-cell
title="详细地址"
placeholderText="请输入详细地址"
:value="addressInfo.address"
:showRightArrow="false"
:noBorder="true"
@onChange="(value) => onChange(value, 'address')"
/>
</view>
<view class="edit-form default">
<form-cell
title="设为默认地址"
type="custom"
class="default-wrapper"
:showRightArrow="false"
:noBorder="true"
>
<view class="flex-row-end default-cell" slot="right">
<switch
:checked="isDefault"
@change="onSwitchChange"
color="#FF19A0"
class="default-switch"
/>
</view>
</form-cell>
</view>
<view class="flex-center edit-bottom">
<view
class="flex-center PingFangSC-Semibold fs-30 confirm-btn"
@click="save"
>
保存
</view>
<view class="bottom-safe-area"></view>
</view>
</view>
</template>
<script>
import FormCell from "@/components/FormCell.vue";
import { createAddress, updateAddress, getAddressInfo } from "../../../api/address";
import { pcaData } from "@/api/areas.js";
import appConfig from "../../../constants/app.config";
const QQMapWX = require("./static/qqmap-wx-jssdk.min.js");
var qqmapsdk;
export default {
components: {
FormCell,
},
data() {
return {
addressInfo: {
address_id: 0,
name: "",
phone: "",
address: "",
province: "",
provinceId: "",
city: "",
cityId: "",
area: "",
areaId: "",
},
isDefault: false,
provinceIndex: 0,
cityIndex: 0,
areaIndex: 0,
regionList: pcaData,
isAdd: true,
id: 0,
lastSaveTime: 0, // 上次保存时间戳
};
},
computed: {
userInfo() {
return this.$store.state?.user?.userInfo || {};
},
regionText() {
const parts = [
this.addressInfo.province,
this.addressInfo.city,
this.addressInfo.area
].filter(item => item && item.trim());
return parts.join(' ');
}
},
watch: {
"addressInfo.provinceId": {
async handler(val) {
this.provinceIndex = this.regionList.findIndex(
(v) => v.area_id === val
);
},
deep: true,
},
"addressInfo.cityId": {
handler(val) {
const citytList = this.regionList?.[this.provinceIndex]?.children || [];
this.cityIndex = citytList.findIndex((v) => v.area_id === val);
},
},
"addressInfo.areaId": {
handler(val) {
const citytList = this.regionList?.[this.provinceIndex]?.children || [];
const areaList = citytList?.[this.cityIndex]?.children || [];
this.areaIndex = areaList.findIndex((v) => v.area_id === val);
},
},
},
onLoad(options) {
const { isAdd, id } = options;
this.isAdd = !!isAdd;
if (id) {
this.id = id;
this.addressInfo.address_id = id;
this.getInfo(id);
}
qqmapsdk = new QQMapWX({
key: appConfig.tencentMapKey,
});
},
methods: {
onChange(value, key) {
this.addressInfo[key] = value;
},
onSwitchChange(e) {
this.isDefault = e.detail.value;
},
onRegionChange(data) {
const [province, city, area] = data;
this.addressInfo.province = province.label;
this.addressInfo.provinceId = province.value;
this.addressInfo.city = city.label;
this.addressInfo.cityId = city.value;
this.addressInfo.area = area.label;
this.addressInfo.areaId = area.value;
},
getInfo(id) {
uni.showLoading({ title: "加载中..." });
getAddressInfo(id).then((res) => {
uni.hideLoading();
const info = res.info || res.data || res;
// 填充地址信息
this.addressInfo.address_id = info.address_id || info.id || id;
this.addressInfo.name = info.name || info.recipient_name || '';
this.addressInfo.phone = info.phone || '';
this.addressInfo.address = info.address || info.full_address || '';
this.addressInfo.province = info.province || info.sheng || '';
this.addressInfo.provinceId = info.province_id;
this.addressInfo.city = info.city || info.shi || '';
this.addressInfo.cityId = info.city_id;
this.addressInfo.area = info.district || info.area || info.qu || '';
this.addressInfo.areaId = info.district_id;
// 设置默认地址状态
this.isDefault = !!info.is_default || !!info.isDefault;
// 设置省市区索引(用于地区选择器)
if (this.addressInfo.provinceId) {
this.provinceIndex = this.regionList.findIndex(
(v) => v.area_id === this.addressInfo.provinceId
);
}
if (this.addressInfo.cityId && this.provinceIndex >= 0) {
const cityList = this.regionList[this.provinceIndex]?.children || [];
this.cityIndex = cityList.findIndex(
(v) => v.area_id === this.addressInfo.cityId
);
}
if (this.addressInfo.areaId && this.cityIndex >= 0) {
const cityList = this.regionList[this.provinceIndex]?.children || [];
const areaList = cityList[this.cityIndex]?.children || [];
this.areaIndex = areaList.findIndex(
(v) => v.area_id === this.addressInfo.areaId
);
}
}).catch((err) => {
uni.hideLoading();
console.error('获取地址详情失败:', err);
uni.showToast({
title: '获取地址信息失败',
icon: 'none'
});
});
},
// 获取经纬度
getLatLotData() {},
save() {
// 防抖处理1秒内只能调用1次接口
const now = Date.now();
if (now - this.lastSaveTime < 1000) {
uni.showToast({
title: '操作过于频繁,请稍后再试',
icon: 'none',
duration: 1500
});
return;
}
// 更新上次保存时间
this.lastSaveTime = now;
const {
province = "",
city = "",
area = "",
address = "",
} = this.addressInfo;
// const addressText = province + city + area + address
// uni.request({
// url: `https://apis.map.qq.com/ws/geocoder/v1/?address=${addressText}&key=${appConfig.tencentMapKey}`,
// method: 'GET',
// success: (res) => {
// console.log('地址转经纬度----->', res?.data)
// }
// })
qqmapsdk.geocoder({
address: province + city + area + address,
sig: appConfig.tencentSecret,
success: (res) => {
console.log("解析地址成功----->", res);
const { lat, lng } = res?.result?.location || {};
this.editAddress(lat, lng);
},
fail: (error) => {
console.error("解析地址失败----->", error);
this.editAddress();
},
});
return;
},
editAddress(l) {
const addressParam = {
user_id: this.userInfo.userID,
// address_id: this.addressInfo.address_id,
recipient_name: this.addressInfo.name.trim(),
phone: this.addressInfo.phone.trim(),
is_default: this.isDefault,
province: this.addressInfo.province,
province_id: this.addressInfo.provinceId,
city: this.addressInfo.city,
city_id: this.addressInfo.cityId,
district: this.addressInfo.area,
district_id: this.addressInfo.areaId,
full_address: this.addressInfo.address.trim(),
region_id: 1
};
console.log("addressParam", addressParam);
const error = Object.keys(addressParam).some((v) => {
if (!addressParam[v] && v !== "address_id" && v !== "is_default") {
if (v === "sheng_id") {
uni.showToast({ icon: "none", title: "请选择省市区" });
return true;
}
if (v === "phone") {
uni.showToast({ icon: "none", title: "请输入手机号" });
return true;
}
if (v === "name") {
uni.showToast({ icon: "none", title: "请输入姓名" });
return true;
}
if (v === "address") {
uni.showToast({ icon: "none", title: "请输入详细地址" });
return true;
}
}
if (v === "phone" && !/^1[0-9]{10}$/.test(addressParam[v])) {
uni.showToast({ icon: "none", title: "手机号有误" });
return true;
}
});
if (error) {
return;
}
// 如果是编辑模式,需要添加 address_id
if (!this.isAdd && this.addressInfo.address_id) {
addressParam.id = this.addressInfo.address_id;
}
uni.showLoading({ title: "处理中..." });
// 根据 isAdd 标志决定调用创建还是更新接口
const savePromise = this.isAdd
? createAddress(addressParam)
: updateAddress(addressParam);
savePromise.then((r) => {
uni.hideLoading();
uni.showToast({ icon: "success", title: "保存成功" });
// const eventChannel = this.getOpenerEventChannel();
// eventChannel.emit("refreshAddress");
uni.navigateBack();
}).catch((err) => {
uni.hideLoading();
console.error('保存地址失败:', err);
uni.showToast({
title: err?.msg || err?.message || '保存失败,请稍后重试',
icon: 'none'
});
});
},
},
};
</script>
<style lang="scss" scoped>
.address-edit {
min-height: 100vh;
width: 100%;
box-sizing: border-box;
background: #F7F8FA;
padding: 20rpx 20rpx 0;
display: flex;
flex-direction: column;
.edit-form {
background: #fff;
border-radius: 16rpx;
padding: 0 24rpx;
margin-bottom: 20rpx;
&.default {
margin-top: 20rpx;
}
}
.default-wrapper {
::v-deep {
.form-cell .form-label {
color: #3D3D3D;
}
}
.default-cell {
width: 100%;
.default-switch {
transform: scale(0.8);
transform-origin: right center;
}
}
}
.edit-bottom {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background-color: #fff;
border-radius: 32rpx 32rpx 0 0;
padding: 24rpx 24rpx 0;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
box-sizing: border-box;
.confirm-btn {
width: 100%;
margin: auto;
margin-bottom: 24rpx;
border-radius: 100rpx;
background: #FF19A0;
color: #fff;
padding: 32rpx 0rpx;
text-align: center;
font-size: 32rpx;
font-weight: 600;
}
.bottom-safe-area {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
}
}
</style>

View File

@ -0,0 +1,160 @@
<template>
<view class="address-container">
<list-page-temp
class="address-list-inner"
:getDataPromise="getAddressList"
:reloadFlag="reloadFlag"
:requestData="requestData"
>
<template v-slot:item="{ data }">
<address-item
:data="data"
@toDetails="toDetails(data)"
@delete="deleteAction"
@refresh="handleRefresh"
/>
</template>
<view slot="bottom" class="place-view"></view>
</list-page-temp>
<view class="flex-center address-list-bottom">
<view class="flex-row-center address-add" @click="jumpToAdd">
<text class="fs-60 app-fc-white PingFangSC-Semibold add-icon">+</text>
<text class="fs-30 app-fc-white PingFangSC-Semibold add-icon">
添加地址
</text>
</view>
</view>
<pop-up-modal
v-if="showModal"
content="确定要删除该地址信息吗?"
@confirm="deleteAddress(data)"
@cancel="showModal = false"
/>
</view>
</template>
<script>
import AddressItem from "./components/AddressItem.vue";
import PopUpModal from "../../../components/PopUpModal.vue";
import { delAddress, getAddressList } from "../../../api/address";
export default {
components: {
AddressItem,
PopUpModal,
},
data() {
return {
reloadFlag: 0,
requestData: {},
deleteData: {},
showModal: false,
isSelectAddress: false, // 从订单页跳转过来选择地址的标识
selectAddressId: "", // 选中的地址id
};
},
computed: {
userInfo() {
return this.$store.state?.user?.userInfo || {};
}
},
watch: {
userInfo: {
immediate: true,
handler(newVal) {
if (newVal && newVal.userID) {
this.requestData = {
user_id: newVal.userID
};
}
}
}
},
onShow() {
this.reloadFlag = Math.random();
},
onLoad(options) {
const { typeSelect, addressId } = options;
this.isSelectAddress = typeSelect;
this.selectAddressId = addressId;
},
methods: {
getAddressList,
toDetails(data) {
if (this.isSelectAddress) {
this.selectAddressId = data?.id || "";
uni.navigateBack();
uni.$emit('selectAddress', data)
return;
}
uni.navigateTo({
url: `/pages/client/address/edit?id=${data?.id || ""}`,
});
},
jumpToAdd() {
uni.navigateTo({
url: `/pages/client/address/edit?isAdd=1`,
});
},
deleteAction(data) {
this.deleteData = data;
this.showModal = true;
},
deleteAddress() {
this.showModal = false;
console.log("this.deleteData", this.deleteData);
delAddress(this.deleteData.id).then(() => {
this.reloadFlag = Math.random();
});
},
handleRefresh() {
// 刷新地址列表
this.reloadFlag = Math.random();
},
},
};
</script>
<style lang="scss" scoped>
.address-container {
height: 100%;
width: 100%;
box-sizing: border-box;
background: #fbf8fc;
padding-bottom: 0;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
padding-top: 20rpx;
.address-list-inner {
height: 100%;
}
.place-view {
height: 90rpx;
}
.address-list-bottom {
position: fixed;
bottom: 0;
bottom: constant(safe-area-inset-bottom);
bottom: env(safe-area-inset-bottom);
left: 0;
width: 100%;
.address-add {
width: 630rpx;
height: 90rpx;
background: $app_color_main;
border-radius: 45rpx 45rpx 45rpx 45rpx;
.add-icon {
margin-right: 20rpx;
line-height: 60rpx;
}
}
}
}
</style>

File diff suppressed because one or more lines are too long