|
|
- <template>
- <view class="container" :style="{paddingTop: (statusBarHeight + 88) + 'rpx'}">
- <!-- 顶部导航 -->
- <view class="nav-bar" :style="{height: (statusBarHeight + 88) + 'rpx', paddingTop: statusBarHeight + 'px'}">
- <view class="back" @tap="goBack">
- <uni-icons type="left" size="20"></uni-icons>
- </view>
- <text class="title">选择寄件地址</text>
- </view>
-
- <!-- 地址列表 -->
- <view class="address-list">
- <view
- class="address-item"
- @click="selectAddress(item)"
- v-for="(item, index) in addressList"
- :key="index"
- >
- <view class="address-header">
- <text class="name">{{item.name}}</text>
- <text class="phone">{{item.phone}}</text>
- <text v-if="item.defaultFlag === 'Y'" class="default-tag">默认</text>
- </view>
- <view class="address-text">{{item.address}}</view>
- <view class="dashed-line"></view>
- <view class="address-actions">
- <view class="action" @tap="setDefault(item.id)">
- <view class="circle" :class="{ active: item.defaultFlag === 'Y' }">
- <text v-if="item.defaultFlag === 'Y'" class="check">✔</text>
- </view>
- <text :class="{ 'active-text': item.defaultFlag === 'Y' }">默认地址</text>
- </view>
- <view class="action" @tap="editAddress(item)">
- <uni-icons type="compose" size="22" color="#bbb" />
- <text>编辑</text>
- </view>
- <view class="action" @tap="deleteAddress(item.id)">
- <uni-icons type="trash" size="22" color="#bbb" />
- <text>删除</text>
- </view>
- </view>
- </view>
- </view>
-
- <!-- 新建地址按钮 -->
- <view class="bottom-bar">
- <button class="main-btn" @tap="goToAddAddress">新建地址</button>
- </view>
-
- <!-- 遮罩 -->
- <view v-if="showAddressModal" class="modal-mask" @tap="closeAddressModal"></view>
- <!-- 新建地址弹窗 -->
- <view v-if="showAddressModal" class="address-modal">
- <view class="modal-header">
- <text class="close-btn" @tap="closeAddressModal">关闭</text>
- <text class="modal-title">新建地址</text>
- </view>
- <view class="modal-form">
- <view class="form-item">
- <text class="label">联系人</text>
- <input class="input" v-model="form.name" placeholder="请输入" />
- </view>
- <view class="form-item">
- <text class="label">手机号</text>
- <input class="input" v-model="form.phone" placeholder="请输入" />
- </view>
- <view class="form-item" @tap="showRegionPicker = true">
- <text class="label">所在地区</text>
- <view class="input region-input">
- <text :class="{placeholder: !form.address}">
- {{ form.address || '选择省市区街道' }}
- </text>
- <text class="arrow">></text>
- </view>
- </view>
- <view class="form-item">
- <text class="label">详细地址</text>
- <input class="input" v-model="form.addressDetails" placeholder="小区楼栋、门牌号、村等" />
- </view>
- </view>
- <button class="main-btn" @tap="saveAddress">保存</button>
- </view>
-
- <!-- 地区选择器弹窗 -->
- <view v-if="showRegionPicker" class="region-modal">
- <view class="modal-header">
- <text class="close-btn" @tap="showRegionPicker = false">关闭</text>
- <text class="modal-title">所在地区</text>
- </view>
- <view class="region-picker">
- <picker-view style="height:300px;" :value="regionIndex" @change="onRegionChange">
- <picker-view-column >
- <view v-for="(item,index) in provinces" :key="index" class="item">{{item.name}}</view>
- </picker-view-column>
- <picker-view-column>
- <view v-for="(item,index) in cities" :key="index" class="item">{{item.name}}</view>
- </picker-view-column>
- <picker-view-column>
- <view v-for="(item,index) in districts" :key="index" class="item">{{item.name}}</view>
- </picker-view-column>
- </picker-view>
- </view>
- <button class="main-btn" @tap="confirmRegion">确认</button>
- </view>
- </view>
- </template>
-
- <script>
- import regionData from '@/pages/subcomponent/region-data.js'
- import pullRefreshMixin from '@/pages/mixins/pullRefreshMixin.js'
-
- export default {
- mixins: [pullRefreshMixin],
- data() {
- return {
- statusBarHeight: 0,
- addressList: [
- {
- name: '郑文锦',
- phone: '18108341643',
- address: '海南省海口市秀英区秀英街道5单元183室',
- defaultFlag: 'Y'
- },
- {
- name: '周俊',
- phone: '13293992217',
- address: '贵州省遵义市道真仡佬族苗族自治县洛龙镇5幢172室',
- defaultFlag: 'N'
- },
- {
- name: '何炜',
- phone: '18108341643',
- address: '新疆维吾尔自治区乌鲁木齐市沙依巴克区仓房沟片区街道4单元50室',
- defaultFlag: 'N'
- },
- {
- name: '赵晓艳',
- phone: '15022123314',
- address: '海南省海口市秀英区秀英街道5单元183室',
- defaultFlag: 'N'
- },
- {
- name: '冯云',
- phone: '15731435825',
- address: '甘肃省兰州市城关区滑源路街道13幢199室',
- defaultFlag: 'N'
- },
- {
- name: '钱皓皓',
- phone: '18734717201',
- address: '西藏自治区拉萨市堆龙德庆县东嘎镇7单元94室',
- defaultFlag: 'N'
- }
- ],
- batchMode: false,
- selectedIndexes: [],
- showAddressModal: false,
- showRegionPicker: false,
- form: {
- name: '',
- phone: '',
- region: [],
- address: '',
- addressDetails: ''
- },
- provinces: regionData,
- cities: [],
- districts: [],
- regionIndex: [0, 0, 0],
- mode: ''
- }
- },
- watch: {
- regionIndex: {
- handler(val) {
- let pIdx = val[0] < this.provinces.length ? val[0] : 0
- let cIdx = val[1] < (this.provinces[pIdx]?.children?.length || 0) ? val[1] : 0
- this.cities = this.provinces[pIdx]?.children || []
- this.districts = this.cities[cIdx]?.children || []
- },
- immediate: true
- }
- },
- onPullDownRefresh() {
- this.getAddressList(() => {
- uni.stopPullDownRefresh();
- });
- },
- onLoad(options) {
- this.statusBarHeight = uni.getSystemInfoSync().statusBarHeight
- this.cities = this.provinces[0]?.children || []
- this.districts = this.cities[0]?.children || []
- this.regionIndex = [0, 0, 0]
- this.getAddressList();
- this.mode = options.mode || ''
- },
- methods: {
- async onRefresh() {
- // 模拟刷新数据
- await new Promise(resolve => setTimeout(resolve, 1000))
- this.stopPullRefresh()
- },
- getAddressList(callback) {
- this.$api('getAddressList', {}, res => {
- if (res && res.result && res.result.records) {
- this.addressList = res.result.records;
- }
- if (typeof callback === 'function') callback();
- });
- },
- goBack() {
- uni.navigateBack()
- },
- startBatchDelete() {
- this.batchMode = true
- this.selectedIndexes = []
- },
- cancelBatchDelete() {
- this.batchMode = false
- this.selectedIndexes = []
- },
- selectItem(index) {
- if (!this.batchMode) return
-
- const idx = this.selectedIndexes.indexOf(index)
- if (idx > -1) {
- this.selectedIndexes.splice(idx, 1)
- } else {
- this.selectedIndexes.push(index)
- }
- },
- confirmDelete() {
- if (this.selectedIndexes.length === 0) return
-
- uni.showModal({
- title: '提示',
- content: '确定要删除选中的地址吗?',
- success: (res) => {
- if (res.confirm) {
- // 从后往前删除,避免索引变化的问题
- this.selectedIndexes.sort((a, b) => b - a).forEach(index => {
- this.addressList.splice(index, 1)
- })
- this.batchMode = false
- this.selectedIndexes = []
- }
- }
- })
- },
- editAddress(item) {
- uni.navigateTo({
- url: '/pages/component/add'
- })
- },
- goToAddAddress() {
- this.showAddressModal = true
- this.form = {
- name: '',
- phone: '',
- region: [],
- address: '',
- addressDetails: ''
- }
- this.regionIndex = [0, 0, 0]
- this.cities = this.provinces[0]?.children || []
- this.districts = this.cities[0]?.children || []
- },
- selectAddress(address) {
- if (this.mode === 'select') {
- uni.$emit('addressSelected', {
- name: address.name,
- phone: address.phone,
- address: address.address
- })
- uni.navigateBack()
- }
- },
- closeAddressModal() {
- this.showAddressModal = false
- },
- saveAddress() {
- if (!this.form.name) return uni.showToast({ title: '请输入联系人', icon: 'none' })
- if (!this.form.phone) return uni.showToast({ title: '请输入手机号', icon: 'none' })
- if (!this.form.address) return uni.showToast({ title: '请选择地区', icon: 'none' })
- if (!this.form.addressDetails) return uni.showToast({ title: '请输入详细地址', icon: 'none' })
- this.$api('saveOrUpdateAddress',{
- name:this.form.name,
- phone:this.form.phone,
- address:this.form.address,
- addressDetails:this.form.addressDetails
- },(res)=>{
- if(res.code == 200){
- uni.showToast({ title: '保存成功', icon: 'success' })
- this.closeAddressModal()
- this.$forceUpdate();
- }
- })
- },
- onRegionChange(e) {
- let [pIdx, cIdx, dIdx] = e.detail.value
- if (pIdx !== this.regionIndex[0]) {
- cIdx = 0
- dIdx = 0
- } else if (cIdx !== this.regionIndex[1]) {
- dIdx = 0
- }
- this.regionIndex = [pIdx, cIdx, dIdx]
- },
- confirmRegion() {
- const province = this.provinces[this.regionIndex[0]]
- const city = this.cities[this.regionIndex[1]]
- const district = this.districts[this.regionIndex[2]]
-
- this.form.region = [
- province?.code || '',
- city?.code || '',
- district?.code || ''
- ]
-
- this.form.address = [
- province?.name || '',
- city?.name || '',
- district?.name || ''
- ].filter(Boolean).join(' ')
-
- this.showRegionPicker = false
- },
- setDefault(id) {
- this.$api('updateDefaultAddress',{id:id},(res)=>{
- console.log(res)
- if(res.code == 200){
- this.$api('getAddressList', {}, res => {
- console.log(res,'res');
- this.addressList = res.result.records
- })
- }
- })
- },
- deleteAddress(id) {
- uni.showModal({
- title: '提示',
- content: '确定要删除该地址吗?',
- success: (res) => {
- if (res.confirm) {
- this.$api('deleteAddress', {id:id}, res => {
- console.log(res);
- })
- }
- }
- })
- },
- initRegionPicker() {
- if (this.form.region.length > 0) {
- const [provinceCode, cityCode, districtCode] = this.form.region
- const provinceIndex = this.provinces.findIndex(p => p.code === provinceCode)
- if (provinceIndex > -1) {
- this.cities = this.provinces[provinceIndex].children || []
- if (this.cities.length === 0) {
- this.cities = [{ code: '', name: '请选择' }]
- }
- const cityIndex = this.cities.findIndex(c => c.code === cityCode)
- if (cityIndex > -1) {
- this.districts = this.cities[cityIndex].children || []
- if (this.districts.length === 0) {
- this.districts = [{ code: '', name: '请选择' }]
- }
- const districtIndex = this.districts.findIndex(d => d.code === districtCode)
- this.regionIndex = [
- provinceIndex,
- cityIndex,
- districtIndex > -1 ? districtIndex : 0
- ]
- return
- }
- }
- }
- this.cities = this.provinces[0]?.children || []
- if (this.cities.length === 0) {
- this.cities = [{ code: '', name: '请选择' }]
- }
- this.districts = this.cities[0]?.children || []
- if (this.districts.length === 0) {
- this.districts = [{ code: '', name: '请选择' }]
- }
- this.regionIndex = [0, 0, 0]
- }
- },
- }
- </script>
-
- <style lang="scss" scoped>
- .container {
- min-height: 100vh;
- background: #f8f8f8;
- padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
- }
-
- .nav-bar {
- display: flex;
- align-items: center;
- background: #fff;
- padding: 0 30rpx;
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 999;
- .back {
- padding: 20rpx;
- margin-left: -20rpx;
- }
-
- .title {
- flex: 1;
- text-align: center;
- font-size: 34rpx;
- font-weight: 500;
- color: #222;
- }
-
- .right-btns {
- display: flex;
- align-items: center;
- gap: 30rpx;
-
- .more, .target {
- font-size: 40rpx;
- color: #333;
- }
- }
- }
-
- .address-list {
- padding: 32rpx;
- margin-top: calc(88rpx + var(--status-bar-height));
- min-height: calc(100vh - 88rpx - var(--status-bar-height) - 140rpx - env(safe-area-inset-bottom));
- box-sizing: border-box;
-
- .address-item {
- background: #fff;
- border-radius: 24rpx;
- margin-bottom: 24rpx;
- padding: 32rpx 28rpx 0 28rpx;
- box-shadow: 0 4rpx 24rpx rgba(0,0,0,0.04);
-
- .address-header {
- display: flex;
- align-items: center;
- .name { font-size: 32rpx; color: #222; font-weight: 500; margin-right: 20rpx; }
- .phone { font-size: 32rpx; color: #222; }
- .default-tag {
- margin-left: 18rpx;
- font-size: 24rpx;
- color: #FF9500;
- border: 1rpx solid #FF9500;
- border-radius: 8rpx;
- padding: 2rpx 12rpx;
- background: #FFF7E6;
- vertical-align: middle;
- }
- }
- .address-text {
- font-size: 28rpx;
- color: #888;
- margin: 16rpx 0 0 0;
- line-height: 1.5;
- }
- .dashed-line {
- border-bottom: 1rpx dashed #eee;
- margin: 24rpx 0 0 0;
- }
- .address-actions {
- display: flex;
- align-items: center;
- justify-content: space-around;
- padding: 0 0 18rpx 0;
- .action {
- display: flex;
- align-items: center;
- margin-right: 48rpx;
- .circle {
- width: 36rpx; height: 36rpx; border-radius: 50%;
- border: 2rpx solid #FF9500; margin-right: 8rpx;
- display: flex; align-items: center; justify-content: center;
- &.active {
- background: #FF9500; border: none;
- .check { color: #fff; font-size: 24rpx; }
- }
- }
- text { font-size: 26rpx; color: #bbb; }
- .active-text { color: #FF9500; }
- }
- }
- }
- }
-
- .bottom-bar {
- position: fixed;
- left: 0; right: 0; bottom: 0;
- background: #fff;
- padding: 24rpx 32rpx env(safe-area-inset-bottom);
- z-index: 10;
- }
-
- .main-btn {
- width: 100%;
- height: 90rpx;
- background: linear-gradient(90deg, #FFB74D 0%, #FF9500 100%);
- color: #fff;
- font-size: 32rpx;
- border-radius: 45rpx;
- font-weight: bold;
- margin: 0 auto;
- display: flex; align-items: center; justify-content: center;
- box-shadow: 0 4rpx 16rpx rgba(255, 149, 0, 0.08);
- }
-
- .modal-mask {
- position: fixed; left: 0; right: 0; top: 0; bottom: 0;
- background: rgba(0,0,0,0.5); z-index: 1000;
- }
- .address-modal, .region-modal {
- position: fixed; left: 0; right: 0; bottom: 0; z-index: 1001;
- background: #fff; border-radius: 32rpx 32rpx 0 0;
- box-shadow: 0 -4rpx 32rpx rgba(0,0,0,0.08);
- padding-bottom: env(safe-area-inset-bottom);
- animation: slideUp 0.2s;
- }
- @keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
- .modal-header {
- display: flex; align-items: center; justify-content: space-between;
- padding: 32rpx 32rpx 0 32rpx;
- .close-btn { color: #bbb; font-size: 30rpx; }
- .modal-title { flex: 1; text-align: center; font-size: 34rpx; font-weight: bold; color: #222; }
- }
- .modal-form {
- padding: 0 32rpx;
- .form-item {
- border-bottom: 1rpx solid #f2f2f2;
- padding: 28rpx 0 10rpx 0;
- .label { color: #222; font-size: 28rpx; margin-bottom: 8rpx; }
- .input, .region-input {
- width: 100%; font-size: 28rpx; color: #222; background: none; border: none; outline: none;
- &.placeholder { color: #ccc; }
- }
- .region-input { display: flex; align-items: center; justify-content: space-between; }
- .arrow { color: #ccc; font-size: 28rpx; margin-left: 10rpx; }
- }
- }
- .region-picker {
- padding: 32rpx 0 0 0;
- .picker-view {
- width: 100%; height: 300rpx; display: flex; justify-content: center; align-items: center;
- .active { color: #222; font-weight: bold; }
- view { color: #ccc; font-size: 28rpx; text-align: center; }
- }
-
- .item {
- // line-height: 100upx;
- text-align: center;
- display: flex;
- align-items: center;
- justify-content: center;
- // height: 100upx;
- font-size: 30rpx;
- color: #222;
- }
- }
-
- </style>
|