Files
apple 079076a70e miao33: 从 main 同步 single_uniapp22miao,dart-sass 兼容修复,DEPLOY.md 更新
- 从 main 获取 single_uniapp22miao 子项目
- dart-sass: /deep/ -> ::v-deep,calc 运算符加空格
- DEPLOY.md 采用 shccd159 版本(4 子项目架构说明)

Made-with: Cursor
2026-03-16 11:16:42 +08:00

438 lines
9.4 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="address-detail-page">
<view class="form-container">
<!-- 收货人 -->
<view class="form-item">
<view class="label">收货人<text class="required">*</text></view>
<input
v-model="formData.name"
placeholder="请输入收货人姓名"
class="input"
/>
</view>
<!-- 手机号 -->
<view class="form-item">
<view class="label">手机号<text class="required">*</text></view>
<input
v-model="formData.mobile"
type="number"
maxlength="11"
placeholder="请输入手机号"
class="input"
/>
</view>
<!-- 所在地区 -->
<view class="form-item" @click="showRegionPicker">
<view class="label">所在地区<text class="required">*</text></view>
<view class="picker-value">
<text v-if="regionText" class="text">{{ regionText }}</text>
<text v-else class="placeholder">请选择省//</text>
<text class="arrow"></text>
</view>
</view>
<!-- 详细地址 -->
<view class="form-item">
<view class="label">详细地址<text class="required">*</text></view>
<textarea
v-model="formData.detail"
placeholder="请输入详细地址(街道、楼牌号等)"
class="textarea"
maxlength="100"
/>
</view>
<!-- 设为默认 -->
<view class="form-item checkbox-item">
<checkbox-group @change="onDefaultChange">
<label>
<checkbox
:checked="formData.is_default == 1"
color="#FF4757"
/>
<text>设为默认地址</text>
</label>
</checkbox-group>
</view>
</view>
<!-- 保存按钮 -->
<view class="btn-container">
<button
class="save-btn"
:disabled="!canSave"
:loading="saving"
@click="handleSave"
>
保存
</button>
</view>
<!-- 地区选择器 -->
<picker-view
v-if="showPicker"
:value="pickerValue"
@change="onPickerChange"
class="picker-view"
>
<picker-view-column>
<view v-for="(item, index) in provinces" :key="index">{{ item.name }}</view>
</picker-view-column>
<picker-view-column>
<view v-for="(item, index) in cities" :key="index">{{ item.name }}</view>
</picker-view-column>
<picker-view-column>
<view v-for="(item, index) in districts" :key="index">{{ item.name }}</view>
</picker-view-column>
</picker-view>
<!-- 遮罩层 -->
<view class="mask" v-if="showPicker" @click="hidePicker">
<view class="picker-toolbar" @click.stop>
<text class="cancel" @click="hidePicker">取消</text>
<text class="title">选择地区</text>
<text class="confirm" @click="confirmRegion">确定</text>
</view>
</view>
</view>
</template>
<script>
import regionData from '@/static/data/region.js'; // 需要地区数据文件
export default {
data() {
return {
addressId: null,
formData: {
name: '',
mobile: '',
province: '',
city: '',
district: '',
detail: '',
is_default: 0
},
showPicker: false,
pickerValue: [0, 0, 0],
provinces: [],
cities: [],
districts: [],
saving: false
}
},
computed: {
regionText() {
if (this.formData.province && this.formData.city && this.formData.district) {
return `${this.formData.province} ${this.formData.city} ${this.formData.district}`;
}
return '';
},
canSave() {
return this.formData.name &&
this.formData.mobile.length === 11 &&
this.formData.province &&
this.formData.city &&
this.formData.district &&
this.formData.detail;
}
},
onLoad(options) {
this.addressId = options.id;
this.initRegionData();
if (this.addressId) {
this.loadAddressDetail();
}
},
methods: {
// 初始化地区数据
initRegionData() {
// 这里应该加载完整的地区数据
// 简化示例,实际应从 region.js 加载
this.provinces = [
{ name: '北京市', cities: [] },
{ name: '上海市', cities: [] },
// ... 更多省份
];
this.updateCities();
},
// 更新城市列表
updateCities() {
if (this.provinces[this.pickerValue[0]]) {
this.cities = this.provinces[this.pickerValue[0]].cities || [];
this.updateDistricts();
}
},
// 更新区县列表
updateDistricts() {
if (this.cities[this.pickerValue[1]]) {
this.districts = this.cities[this.pickerValue[1]].districts || [];
}
},
// 加载地址详情
async loadAddressDetail() {
try {
const res = await this.$http.get('/api/address/detail', {
id: this.addressId
});
if (res.code === 0) {
this.formData = res.data;
}
} catch (error) {
console.error('加载地址详情失败:', error);
}
},
// 显示地区选择器
showRegionPicker() {
this.showPicker = true;
},
// 隐藏选择器
hidePicker() {
this.showPicker = false;
},
// 选择器变化
onPickerChange(e) {
this.pickerValue = e.detail.value;
this.updateCities();
},
// 确认地区选择
confirmRegion() {
const province = this.provinces[this.pickerValue[0]];
const city = this.cities[this.pickerValue[1]];
const district = this.districts[this.pickerValue[2]];
this.formData.province = province.name;
this.formData.city = city.name;
this.formData.district = district.name;
this.hidePicker();
},
// 默认地址变化
onDefaultChange(e) {
this.formData.is_default = e.detail.value.length > 0 ? 1 : 0;
},
// 保存
async handleSave() {
if (!/^1[3-9]\d{9}$/.test(this.formData.mobile)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none'
});
return;
}
this.saving = true;
try {
const url = this.addressId ? '/api/address/update' : '/api/address/insert';
const data = {
...this.formData,
id: this.addressId
};
const res = await this.$http.post(url, data);
if (res.code === 0) {
uni.showToast({
title: '保存成功',
icon: 'success'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
} catch (error) {
uni.showToast({
title: error.msg || '保存失败',
icon: 'none'
});
} finally {
this.saving = false;
}
}
}
}
</script>
<style lang="scss" scoped>
.address-detail-page {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx;
}
.form-container {
background-color: #fff;
padding: 20rpx 30rpx;
}
.form-item {
padding: 30rpx 0;
border-bottom: 1px solid #f5f5f5;
&:last-child {
border-bottom: none;
}
.label {
font-size: 28rpx;
color: #333;
margin-bottom: 20rpx;
.required {
color: #FF4757;
margin-left: 4rpx;
}
}
.input {
height: 70rpx;
padding: 0 20rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
font-size: 28rpx;
}
.textarea {
min-height: 150rpx;
padding: 20rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
font-size: 28rpx;
}
.picker-value {
display: flex;
align-items: center;
justify-content: space-between;
height: 70rpx;
padding: 0 20rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
.text {
font-size: 28rpx;
color: #333;
}
.placeholder {
font-size: 28rpx;
color: #999;
}
.arrow {
font-size: 40rpx;
color: #ccc;
}
}
&.checkbox-item {
label {
display: flex;
align-items: center;
checkbox {
margin-right: 10rpx;
}
text {
font-size: 28rpx;
color: #333;
}
}
}
}
.btn-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20rpx 30rpx;
background-color: #fff;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
}
.save-btn {
width: 100%;
height: 90rpx;
line-height: 90rpx;
background: linear-gradient(90deg, #FF6B6B, #FF4757);
color: #fff;
border-radius: 45rpx;
font-size: 32rpx;
border: none;
&[disabled] {
opacity: 0.6;
}
}
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
}
.picker-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
padding: 0 30rpx;
background-color: #fff;
.cancel,
.confirm {
font-size: 28rpx;
}
.cancel {
color: #999;
}
.confirm {
color: #FF4757;
}
.title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
}
.picker-view {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 500rpx;
background-color: #fff;
z-index: 1000;
}
</style>