绿满眶商城微信小程序-uniapp
3fd8a13fa5cd843f6a116653711526d580f98330..cd4fa2cfed76c15143511a40783abf3a75eb3146
2025-09-10 peng
扫码修改地址
cd4fa2 对比 | 目录
2025-09-10 peng
扫码修改订单地址
2ea4de 对比 | 目录
2个文件已修改
1个文件已添加
583 ■■■■■ 已修改文件
api/order.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages.json 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/order/editOrderAddress/editOrderAddress.vue 565 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/order.js
@@ -81,6 +81,17 @@
    needToken: true,
  });
}
/**
 * 获取订单详情
 * @param orderSn 订单编号
 */
export function getOrderDetailEdit(orderSn) {
  return http.request({
    url: `/order/order/editAddress/${orderSn}`,
    method: Method.GET,
    needToken: true,
  });
}
/**
 * 取消订单
pages.json
@@ -1777,6 +1777,13 @@
              "u-notice-bar": "view"
            }
          }
        },
        {
            "path" : "editOrderAddress/editOrderAddress",
            "style" :
            {
                "navigationBarTitleText" : "修改订单"
            }
        }
      ]
    },
pages/order/editOrderAddress/editOrderAddress.vue
New file
@@ -0,0 +1,565 @@
<template>
    <view class="edit-address-page">
        <!-- 订单信息 -->
        <view class="order-info">
            <view class="order-title">订单信息</view>
            <view class="order-sn">订单号:{{ orderDetail.order.sn || '' }}</view>
            <!-- 商品列表 -->
            <view class="goods-list">
                <view class="goods-item" v-for="item in orderDetail.orderItems" :key="item.id">
                    <image class="goods-image" :src="item.image" mode="aspectFill"></image>
                    <view class="goods-info">
                        <view class="goods-name">{{ item.goodsName }}</view>
                        <view class="goods-price-qty">
                            <text class="price">¥{{ item.flowPrice }}</text>
                            <text class="qty">x{{ item.num }}</text>
                        </view>
                    </view>
                </view>
            </view>
        </view>
        <!-- 地址填写表单 -->
        <view class="address-form">
            <view class="form-title">配送地址</view>
            <view class="form-item">
                <text class="label">收货人</text>
                <input class="input" v-model="addressForm.consigneeName" placeholder="请输入收货人姓名" />
            </view>
            <view class="form-item">
                <text class="label">联系电话</text>
                <input class="input" v-model="addressForm.consigneeMobile" placeholder="请输入联系电话" type="number" />
            </view>
            <view class="form-item">
                <text class="label">所在地区</text>
                <view class="picker-text" @click="showPicker">
                    {{ addressForm.___path || '请选择所在地区' }}
                </view>
            </view>
            <view class="form-item">
                <text class="label">详细地址</text>
                <textarea class="textarea" v-model="addressForm.consigneeDetail"
                    placeholder="请输入详细地址(如街道、门牌号等)"></textarea>
            </view>
        </view>
        <!-- 提交按钮(已适配安全区域) -->
        <view class="submit-btn" @tap="submitAddress">保存地址</view>
        <!-- 地址选择组件 -->
        <m-city :provinceData="list" headTitle="区域选择" ref="cityPicker" @funcValue="getpickerParentValue" pickerSize="4">
        </m-city>
    </view>
</template>
<script>
    import {
        getOrderDetailEdit
    } from '@/api/order.js'
    import {
        http
    } from '@/utils/request.js'
    import '@/components/uview-components/uview-ui';
    import city from "../m-city/m-city.vue";
    export default {
        components: {
            "m-city": city
        },
        data() {
            return {
                orderSn: '', // 订单编号
                orderDetail: {
                    orderItems: []
                }, // 订单详情
                addressForm: {
                    consigneeName: '',
                    consigneeMobile: '',
                    consigneeAddressPath: '',
                    consigneeAddressIdPath: '',
                    consigneeDetail: '',
                    ___path: '' // 显示用的地区文本
                },
                regionData: [
                    [],
                    [],
                    []
                ], // 省市区数据
                regionIndex: [0, 0, 0], // 选中的索引
                regionText: '', // 显示的地区文本
                regionCodes: [], // 地区编码
                list: [{
                    id: "",
                    localName: "请选择",
                    children: [],
                }, ]
            }
        },
        onShow(){
            console.log('触发onShow-------------------------->',this.orderSn)
            this.loadOrderDetail()
        },
        onLoad(options) {
            console.log('页面参数:', options)
            if (options.q) {
                // 双重解码:微信对URL进行了两次编码
                const decodedUrl = decodeURIComponent(decodeURIComponent(options.q));
                console.log('原始URL:', decodedUrl);
                // 解析URL中的查询参数
                const params = this.parseUrlParams(decodedUrl);
                this.orderSn =  params.orderSn;
                this.loadOrderDetail()
            }
        },
        methods: {
            // 安全的toast方法,适配小程序环境
            safeShowToast(options) {
                // 确保loading和之前的toast已关闭
                uni.hideLoading();
                uni.hideToast();
                // 使用nextTick确保在下一个事件循环中执行
                this.$nextTick(() => {
                    uni.showToast({
                        ...options,
                        duration: options.duration || 2000
                    });
                });
            },
            // 解析URL参数
            parseUrlParams(url) {
                const params = {};
                // 处理可能存在的hash(如果有的话)
                const cleanUrl = url.split('#')[0];
                const queryStr = cleanUrl.split('?')[1] || '';
                queryStr.split('&').forEach(pair => {
                    const [key, value] = pair.split('=');
                    if (key) {
                        // 如果值存在,则解码,否则设为空字符串
                        params[key] = value ? decodeURIComponent(value) : '';
                    }
                });
                return params;
            },
            // 加载订单详情
            async loadOrderDetail() {
                if (!this.orderSn) {
                    console.log('订单号为空,无法加载订单详情');
                    this.safeShowToast({
                        title: '订单号不能为空',
                        icon: 'none'
                    });
                    return;
                }
                let loadingShown = false;
                try {
                    uni.showLoading({
                        title: '加载中...'
                    });
                    loadingShown = true;
                    console.log('开始请求订单详情,订单号:', this.orderSn);
                    const res = await getOrderDetailEdit(this.orderSn);
                    console.log('----------获取订单返回结果------------->',res);
                    // 检查响应状态
                    if (res && res.data) {
                        if (res.data.success) {
                            this.orderDetail = res.data.result;
                            console.log('----------------------->订单数据', JSON.stringify(this.orderDetail));
                        } else {
                            console.log('API返回失败:', res.data.message);
                            // 先关闭loading再显示toast
                            uni.hideLoading();
                            loadingShown = false;
                            this.safeShowToast({
                                title: res.data.message || '获取订单信息失败',
                                icon: 'none'
                            });
                        }
                    } else {
                        console.log('服务器响应异常:', res);
                        uni.hideLoading();
                        loadingShown = false;
                        this.safeShowToast({
                            title: '服务器响应异常',
                            icon: 'none'
                        });
                    }
                } catch (error) {
                    console.error('获取订单详情失败:', error);
                    // 网络异常或其他错误
                    if (loadingShown) {
                        uni.hideLoading();
                        loadingShown = false;
                    }
                    this.safeShowToast({
                        title: '网络异常,请稍后重试',
                        icon: 'none'
                    });
                } finally {
                    // 确保loading被关闭
                    if (loadingShown) {
                        uni.hideLoading();
                    }
                }
            },
            // 地区选择改变
            async onColumnChange(e) {
                const {
                    column,
                    value
                } = e.detail
                this.regionIndex[column] = value
                if (column === 0) {
                    // 选择省份时,加载城市数据
                    const provinceId = this.regionData[0][value].id
                    try {
                        const cityRes = await http.request({
                            url: '/common/common/region/tree',
                            method: 'GET',
                            params: {
                                parentId: provinceId
                            }
                        })
                        if (cityRes.data.success) {
                            this.regionData[1] = cityRes.data.result.map(item => ({
                                name: item.name,
                                id: item.id
                            }))
                            this.regionData[2] = []
                            this.regionIndex[1] = 0
                            this.regionIndex[2] = 0
                        }
                    } catch (error) {
                        console.error('加载城市数据失败:', error)
                    }
                } else if (column === 1) {
                    // 选择城市时,加载区县数据
                    const cityId = this.regionData[1][value].id
                    try {
                        const areaRes = await http.request({
                            url: '/common/common/region/tree',
                            method: 'GET',
                            params: {
                                parentId: cityId
                            }
                        })
                        if (areaRes.data.success) {
                            this.regionData[2] = areaRes.data.result.map(item => ({
                                name: item.name,
                                id: item.id
                            }))
                            this.regionIndex[2] = 0
                        }
                    } catch (error) {
                        console.error('加载区县数据失败:', error)
                    }
                }
            },
            // 地区选择确认
            onRegionChange(e) {
                const {
                    value
                } = e.detail
                this.regionIndex = value
                const province = this.regionData[0][value[0]]
                const city = this.regionData[1][value[1]]
                const area = this.regionData[2][value[2]]
                if (province && city && area) {
                    this.regionText = `${province.name} ${city.name} ${area.name}`
                    this.addressForm.consigneeAddressIdPath = `${province.id},${city.id},${area.id}`
                }
            },
            // 显示地址选择器
            showPicker() {
                this.$nextTick(() => {
                    if (this.$refs.cityPicker) {
                        this.$refs.cityPicker.show();
                    }
                });
            },
            // 地址选择回调
            getpickerParentValue(e) {
                // 将需要绑定的地址设置为空,并赋值
                this.addressForm.consigneeAddressIdPath = '';
                let name = "";
                let idPath = [];
                let namePath = []
                e.forEach((item, index) => {
                    if (item.id) {
                        // 遍历数据
                        idPath.push(item.id);
                        namePath.push(item.localName);
                        name += item.localName;
                        this.addressForm.___path = name;
                    }
                });
                // 设置地址路径
                this.addressForm.consigneeAddressIdPath = idPath.join(',');
                this.addressForm.consigneeAddressPath = namePath.join(',');
            },
            // 提交地址
            async submitAddress() {
                // 表单验证
                if (!this.addressForm.consigneeName) {
                    uni.showToast({
                        title: '请输入收货人姓名',
                        icon: 'none'
                    })
                    return
                }
                if (!this.addressForm.consigneeMobile) {
                    uni.showToast({
                        title: '请输入联系电话',
                        icon: 'none'
                    })
                    return
                }
                if (!/^1[3-9]\d{9}$/.test(this.addressForm.consigneeMobile)) {
                    uni.showToast({
                        title: '请输入正确的手机号',
                        icon: 'none'
                    })
                    return
                }
                if (!this.addressForm.consigneeAddressIdPath || !this.addressForm.___path) {
                    uni.showToast({
                        title: '请选择所在地区',
                        icon: 'none'
                    })
                    return
                }
                if (!this.addressForm.consigneeDetail) {
                    uni.showToast({
                        title: '请输入详细地址',
                        icon: 'none'
                    })
                    return
                }
                try {
                    uni.showLoading({
                        title: '保存中...'
                    })
                    console.log('-------------------->', JSON.stringify(this.addressForm))
                    // 提交地址到订单(注释部分为实际接口调用逻辑)
                    const res = await http.request({
                        url: `/order/order/update/editAddress/${this.orderSn}/consignee`,
                        method: 'POST',
                        needToken: true,
                        data: this.addressForm
                    })
                    console.log("地址保存")
                    if (res.data.success) {
                        // uni.showToast({
                        //     title: '地址保存成功',
                        //     icon: 'success'
                        // })
                        uni.redirectTo({
                            url:'/pages/tabbar/index/home',
                            fail(e) {
                                console.log("跳转失败原因",e)
                            }
                        })
                    } else {
                        uni.showToast({
                            title: res.data.message || '保存失败',
                            icon: 'none'
                        })
                    }
                } catch (error) {
                    console.error('提交地址失败:', error)
                    uni.showToast({
                        title: '保存失败,请重试',
                        icon: 'none'
                    })
                } finally {
                    uni.hideLoading()
                }
            }
        }
    }
</script>
<style scoped>
    /* 根容器:底部内边距适配按钮+安全区域 */
    .edit-address-page {
        background-color: #f5f5f5;
        min-height: 100vh;
        /* 按钮高度(100rpx) + 安全区域高度 + 额外间距(20rpx),避免内容被按钮遮挡 */
        padding-bottom: calc(100rpx + 20rpx + constant(safe-area-inset-bottom));
        padding-bottom: calc(100rpx + 20rpx + env(safe-area-inset-bottom));
    }
    /* 订单信息区域 */
    .order-info {
        background: white;
        margin: 20rpx;
        padding: 30rpx;
        border-radius: 16rpx;
    }
    .order-title {
        font-size: 32rpx;
        font-weight: bold;
        margin-bottom: 20rpx;
        color: #333;
    }
    .order-sn {
        font-size: 28rpx;
        color: #666;
        margin-bottom: 30rpx;
    }
    /* 商品列表 */
    .goods-item {
        display: flex;
        align-items: center;
        padding: 20rpx 0;
        border-bottom: 1rpx solid #f0f0f0;
    }
    .goods-item:last-child {
        border-bottom: none;
    }
    .goods-image {
        width: 120rpx;
        height: 120rpx;
        border-radius: 12rpx;
        margin-right: 20rpx;
    }
    .goods-info {
        flex: 1;
    }
    .goods-name {
        font-size: 28rpx;
        color: #333;
        margin-bottom: 10rpx;
        line-height: 1.4;
    }
    .goods-price-qty {
        display: flex;
        justify-content: space-between;
        align-items: center;
    }
    .price {
        font-size: 28rpx;
        color: #ff6b35;
        font-weight: bold;
    }
    .qty {
        font-size: 24rpx;
        color: #666;
    }
    /* 地址表单区域 */
    .address-form {
        background: white;
        margin: 20rpx;
        padding: 30rpx;
        border-radius: 16rpx;
    }
    .form-title {
        font-size: 32rpx;
        font-weight: bold;
        margin-bottom: 30rpx;
        color: #333;
    }
    .form-item {
        display: flex;
        align-items: flex-start;
        margin-bottom: 30rpx;
        min-height: 80rpx;
    }
    .label {
        width: 160rpx;
        font-size: 28rpx;
        color: #333;
        line-height: 80rpx;
        flex-shrink: 0;
    }
    .input {
        flex: 1;
        height: 80rpx;
        line-height: 80rpx;
        font-size: 28rpx;
        color: #333;
        padding: 0 20rpx;
        border: 1rpx solid #e0e0e0;
        border-radius: 8rpx;
    }
    .picker-text {
        flex: 1;
        height: 80rpx;
        line-height: 80rpx;
        font-size: 28rpx;
        color: #333;
        padding: 0 20rpx;
        border: 1rpx solid #e0e0e0;
        border-radius: 8rpx;
        background: white;
    }
    .textarea {
        flex: 1;
        min-height: 120rpx;
        font-size: 28rpx;
        color: #333;
        padding: 20rpx;
        border: 1rpx solid #e0e0e0;
        border-radius: 8rpx;
        line-height: 1.4;
    }
    /* 底部提交按钮(核心:适配安全区域) */
    .submit-btn {
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        height: 100rpx;
        line-height: 100rpx;
        /* 保证文字垂直居中 */
        background: #ff6b35;
        color: white;
        text-align: center;
        font-size: 32rpx;
        font-weight: bold;
        /* 兼容新旧iOS和安卓的安全区域 */
        padding-bottom: constant(safe-area-inset-bottom);
        /* iOS 11.0-11.1 */
        padding-bottom: env(safe-area-inset-bottom);
        /* iOS 11.2+ 及安卓 */
    }
</style>