董国庆
2026-01-04 2605aa9c63f74600333474a1bae056c0b2e433e6
修改bug
1个文件已添加
10个文件已修改
1096 ■■■■■ 已修改文件
miniprogram/config/index.js 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram/pages/home/home.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram/pages/home/service.js 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram/pages/order/order.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram/pagesA/bannerDetail/bannerDetail.vue 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram/pagesA/goodsDetail/goodsDetail.vue 247 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram/pagesA/orderDetails/orderDetails.vue 394 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram/pagesA/paySuccess/paySuccess.vue 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram/pagesA/promotion/promotion.vue 234 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram/static/image/mine/success.png 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram/utils/utils.js 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram/config/index.js
@@ -1,6 +1,6 @@
export default {
    // BASE_URL: 'http://192.168.110.188:9100',
    BASE_URL: 'https://jiashikang.cn/applet',
    BASE_URL: 'http://192.168.110.188:9100',
    // BASE_URL: 'https://jiashikang.cn/applet',
    uploadUrl: "https://jiashikang.cn/applet/open/file/upload",
    imageUrl: 'https://jiashikang.cn/images/'
};
miniprogram/pages/home/home.vue
@@ -62,7 +62,7 @@
    import goodsItem from "@/components/goodsItem.vue";
    import invitePopup from "@/components/invitePopup.vue";
    import config from '../../config/index.js'
    import { getSuperiorInfo, confirmSuperior, getBannerList, getBigList, getGoodList, htmlConfig } from './service.js'
    import { record, getBannerList, getBigList, getGoodList, htmlConfig } from './service.js'
    const background = reactive({
        backgroundColor: "transparent",
@@ -93,6 +93,11 @@
                }
            }
        })
        if (uni.getStorageSync('token')) {
            record().then(res => {
                console.log(res)
            })
        }
    })
    onLoad((query) => {
miniprogram/pages/home/service.js
@@ -24,4 +24,8 @@
//协议配置
export const htmlConfig = (type) => {
    return request.get(`/html-config/${type}`, )
}
//协议配置
export const record = (type) => {
    return request.post(`/appUserVisit/record`, )
}
miniprogram/pages/order/order.vue
@@ -19,7 +19,7 @@
                        <view class="fs-23 lh-33 color3">订单编号:{{ item.code }}</view>
                        <view class="shrink0 fs-23 lh-46 px-23 br-8 border1 bgcolor3 color4"
                            :class="item.status == 3 && 'border2 bgcolor7 color1'">
                            {{ ['待支付', '已付定金', '已支付'][item.status - 1] }}
                            {{ ['待支付', '已付定金', '已支付','已收货'][item.status - 1] }}
                        </view>
                    </view>
                    <view class="px-27 pt-27 pb-29">
@@ -190,9 +190,9 @@
            clearInterval(countdownTimers[orderNo].timer);
            // 倒计时结束时刷新列表数据
            // formData.pageNum = 1;
            // isLoading.value = true;
            // getList();
            formData.pageNum = 1;
            isLoading.value = true;
            getList();
            return;
        }
miniprogram/pagesA/bannerDetail/bannerDetail.vue
@@ -8,7 +8,7 @@
            </view>
            <view class="flex a-center">
                <image src="/static/image/home/view.png" class="w-23 h-19 shrink0 mr-12" />
                <view>4983</view>
                <view>{{info.readCount||0}}</view>
            </view>
        </view>
        <rich-text :nodes="content" class="mt-37"></rich-text>
@@ -23,7 +23,7 @@
    const content = ref<string>("");
    const info = ref({
        bannerName: '', createTime: ''
        bannerName: '', createTime: '', readCount: 0
    })
    onLoad((params) => {
miniprogram/pagesA/goodsDetail/goodsDetail.vue
@@ -23,149 +23,158 @@
                <rich-text :nodes="content"></rich-text>
            </view>
        </view>
        <view v-if="(detailType == 1 && detailInfo.inventory > 0) || detailType == 2">
        <view>
            <view class="bottom_box"></view>
            <view class="bottom_btn" @tap.stop="toBuy()">
                <view class="btn">立即购买</view>
                <!-- <view v-if="detailType == 1" class="btn btn_disable">库存不足,请联系平台客服</view> -->
                <view v-if="(detailType == 1 && detailInfo.inventory > 0)" class="btn btn_disable">库存不足,请联系平台客服</view>
            </view>
        </view>
    </view>
</template>
<script setup lang="ts">
import { ref, reactive } from "vue";
import { onShareAppMessage, onLoad } from '@dcloudio/uni-app'
import banner from "@/components/banner.vue";
import { getBigInfo, getPhysicalInfo } from './service.js'
import config from '../../config/index.js'
    import { ref, reactive } from "vue";
    import { onShareAppMessage, onLoad } from '@dcloudio/uni-app'
    import banner from "@/components/banner.vue";
    import { getBigInfo, getPhysicalInfo } from './service.js'
    import config from '../../config/index.js'
const list = ref([]);
const detailType = ref(1)
const content = ref<string>("");
const detailInfo = ref({ name: '', introduction: '', totalPrice: '', inventory: 0, id: '' })
    const list = ref([]);
    const detailType = ref(1)
    const content = ref<string>("");
    const detailInfo = ref({ name: '', introduction: '', totalPrice: '', inventory: 0, id: '' })
onLoad((query) => {
    wx.showShareMenu({
        menus: ['shareAppMessage', 'shareTimeline']
    })
    detailType.value = query.type
    // type  1 大礼包 2 实物
    if (query.type == 1) {
        getBigInfo(query.id).then(res => {
            if (res.code == 200) {
                list.value = res.data.detailedImage.split(',').map(item => {
                    return {
                        imageUrl: config.imageUrl + item
                    }
                })
                if (res.data.detailedDescription) {
                    res.data.detailedDescription = res.data.detailedDescription.replace(/<img/gi,
                        '<img style="max-width:100%;height:auto;margin:0 auto;display:block"')
                }
                content.value = res.data.detailedDescription
                detailInfo.value = res.data
            }
    onLoad((query) => {
        wx.showShareMenu({
            menus: ['shareAppMessage', 'shareTimeline']
        })
    } else {
        getPhysicalInfo(query.id).then(res => {
            if (res.code == 200) {
                list.value = res.data.detailedImage.split(',').map(item => {
                    return {
                        imageUrl: config.imageUrl + item
        detailType.value = query.type
        // type  1 大礼包 2 实物
        if (query.type == 1) {
            getBigInfo(query.id).then(res => {
                if (res.code == 200) {
                    list.value = res.data.detailedImage.split(',').map(item => {
                        return {
                            imageUrl: config.imageUrl + item
                        }
                    })
                    if (res.data.detailedDescription) {
                        res.data.detailedDescription = res.data.detailedDescription.replace(/<img/gi,
                            '<img style="max-width:100%;height:auto;margin:0 auto;display:block"')
                    }
                })
                if (res.data.detailedDescription) {
                    res.data.detailedDescription = res.data.detailedDescription.replace(/<img/gi,
                        '<img style="max-width:100%;height:auto;margin:0 auto;display:block"')
                    content.value = res.data.detailedDescription
                    detailInfo.value = res.data
                }
                content.value = res.data.detailedDescription
                detailInfo.value = res.data
            })
        } else {
            getPhysicalInfo(query.id).then(res => {
                if (res.code == 200) {
                    list.value = res.data.detailedImage.split(',').map(item => {
                        return {
                            imageUrl: config.imageUrl + item
                        }
                    })
                    if (res.data.detailedDescription) {
                        res.data.detailedDescription = res.data.detailedDescription.replace(/<img/gi,
                            '<img style="max-width:100%;height:auto;margin:0 auto;display:block"')
                    }
                    content.value = res.data.detailedDescription
                    detailInfo.value = res.data
                }
            })
        }
    })
    onShareAppMessage((e) => {
        if (uni.getStorageSync('userInfo')) {
            let userInfo = uni.getStorageSync('userInfo')
            if (userInfo.userType && userInfo.userType > 1) {
                return {
                    title: '诚邀您一起加入嘉视达',
                    path: `/pages/home/home?code=${userInfo.code}&type=2`
                }
            } else {
                return {
                    title: '诚邀您一起加入嘉视达',
                    path: '/pages/home/home'
                }
            }
        } else {
            return {
                title: '诚邀您一起加入嘉视达',
                path: '/pages/home/home'
            }
        }
    })
    const back = () => {
        uni.navigateBack()
    }
    const toBuy = () => {
        if (detailType.value == 1 && detailInfo.value.inventory == 0) return
        uni.navigateTo({
            url: `/pagesA/placeAnOrder/placeAnOrder?id=${detailInfo.value.id}&type=${detailType.value}`
        })
    }
})
onShareAppMessage((e) => {
    if (uni.getStorageSync('userInfo')) {
        let userInfo = uni.getStorageSync('userInfo')
        return {
            title: '诚邀您一起加入嘉视达',
            path: `/pages/home/home?code=${userInfo.code}&type=2`
        }
    } else {
        return {
            title: '诚邀您一起加入嘉视达',
            path: '/pages/home/home'
        }
    }
})
const back = () => {
    uni.navigateBack()
}
const toBuy = () => {
    uni.navigateTo({
        url: `/pagesA/placeAnOrder/placeAnOrder?id=${detailInfo.value.id}&type=${detailType.value}`
    })
}
</script>
<style>
page {
    background-color: #f5f5f5;
}
    page {
        background-color: #f5f5f5;
    }
</style>
<style scoped lang="scss">
.bgcolor1 {
    background-color: #fff;
}
.color1 {
    color: rgba(0, 0, 0, 0.5);
}
.color2 {
    color: #ff4d4f;
}
.color3 {
    color: rgba(0, 0, 0, 0.45);
}
.br1 {
    border-radius: 38rpx 38rpx 0rpx 0rpx;
}
.bottom_btn {
    position: fixed;
    bottom: 0;
    width: 100%;
    background: #fff;
    box-shadow: 0rpx -8rpx 15rpx 0rpx rgba(0, 0, 0, 0.06);
    padding: 38rpx 39rpx;
    padding-bottom: calc(env(safe-area-inset-bottom) + 38rpx);
    .btn {
        background: linear-gradient(270deg, #6BEADF 0%, #1BA8EC 100%);
        box-shadow: 0rpx 8rpx 19rpx 0rpx rgba(27, 168, 236, 0.3);
        border-radius: 67rpx;
        line-height: 96rpx;
        color: #fff;
        text-align: center;
        font-weight: 600;
        font-size: 35rpx;
    .bgcolor1 {
        background-color: #fff;
    }
    .btn_disable {
        background: rgba(8, 29, 59, 0.18);
        color: #FFFFFF;
        box-shadow: unset;
    .color1 {
        color: rgba(0, 0, 0, 0.5);
    }
}
.bottom_box {
    height: calc(96rpx + 38rpx + 38rpx);
}
    .color2 {
        color: #ff4d4f;
    }
    .color3 {
        color: rgba(0, 0, 0, 0.45);
    }
    .br1 {
        border-radius: 38rpx 38rpx 0rpx 0rpx;
    }
    .bottom_btn {
        position: fixed;
        bottom: 0;
        width: 100%;
        background: #fff;
        box-shadow: 0rpx -8rpx 15rpx 0rpx rgba(0, 0, 0, 0.06);
        padding: 38rpx 39rpx;
        padding-bottom: calc(env(safe-area-inset-bottom) + 38rpx);
        .btn {
            background: linear-gradient(270deg, #6BEADF 0%, #1BA8EC 100%);
            box-shadow: 0rpx 8rpx 19rpx 0rpx rgba(27, 168, 236, 0.3);
            border-radius: 67rpx;
            line-height: 96rpx;
            color: #fff;
            text-align: center;
            font-weight: 600;
            font-size: 35rpx;
        }
        .btn_disable {
            background: rgba(8, 29, 59, 0.18);
            color: #FFFFFF;
            box-shadow: unset;
        }
    }
    .bottom_box {
        height: calc(96rpx + 38rpx + 38rpx);
    }
</style>
miniprogram/pagesA/orderDetails/orderDetails.vue
@@ -66,7 +66,7 @@
                    <view class="fs-27 font-bold">微信支付</view>
                </view>
            </view>
            <view v-if="status >2 && goodsType == 1">
            <view v-if="status >2 && goodsType == 1 && orderDetail.paymentType==1">
                <view class="flex a-center j-between mt-31">
                    <view class="fs-27 lh-38 color3">已付定金</view>
                    <view class="fs-27 font-bold">¥{{ orderDetail.deposit || 0.00 }}</view>
@@ -86,6 +86,20 @@
                <view class="flex a-center j-between mt-31">
                    <view class="fs-27 lh-38 color3">尾款支付时间</view>
                    <view class="fs-27 font-bold">{{ orderDetail.finalPaymentTime || "" }}</view>
                </view>
                <view class="flex a-center j-between mt-31">
                    <view class="fs-27 lh-38 color3">支付方式</view>
                    <view class="fs-27 font-bold">微信支付</view>
                </view>
            </view>
            <view v-else>
                <view class="flex a-center j-between mt-31">
                    <view class="fs-27 lh-38 color3">支付金额</view>
                    <view class="fs-27 font-bold">¥{{ orderDetail.paymentPrice || 0.00 }}</view>
                </view>
                <view class="flex a-center j-between mt-31">
                    <view class="fs-27 lh-38 color3">支付时间</view>
                    <view class="fs-27 font-bold">{{ orderDetail.paymentTime || "" }}</view>
                </view>
                <view class="flex a-center j-between mt-31">
                    <view class="fs-27 lh-38 color3">支付方式</view>
@@ -114,220 +128,220 @@
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
import { getBigGiftOrderDetail, getPhysicalOrderDetail, bigGiftConfirm, physicalConfirm, bigGiftPayNow, physicalPayNow } from './service'
    import { ref, reactive } from 'vue'
    import { onLoad, onUnload } from '@dcloudio/uni-app'
    import { getBigGiftOrderDetail, getPhysicalOrderDetail, bigGiftConfirm, physicalConfirm, bigGiftPayNow, physicalPayNow } from './service'
const orderId = ref('')
const goodsType = ref<number | null>(null)
const status = ref<number | null>(null)
const orderDetail = ref<any>({ goodsCover: '', goodsName: '', goodsIntro: '', orderPrice: '', deposit: '', depositTime: '', finalPayment: '', finalPaymentTime: '', paymentPrice: '', paymentTime: '', deliveryTime: '' })
    const orderId = ref('')
    const goodsType = ref<number | null>(null)
    const status = ref<number | null>(null)
    const orderDetail = ref<any>({ goodsCover: '', goodsName: '', goodsIntro: '', orderPrice: '', deposit: '', depositTime: '', finalPayment: '', finalPaymentTime: '', paymentPrice: '', paymentTime: '', deliveryTime: '' })
// 单个订单的倒计时数据
const countdown = reactive({
    hours: '00',
    minutes: '00',
    seconds: '00',
    isExpired: false,
    timer: null as any
})
    // 单个订单的倒计时数据
    const countdown = reactive({
        hours: '00',
        minutes: '00',
        seconds: '00',
        isExpired: false,
        timer: null as any
    })
// 更新倒计时
const updateCountdown = (endTime: number) => {
    const now = new Date().getTime()
    const distance = endTime - now
    // 更新倒计时
    const updateCountdown = (endTime : number) => {
        const now = new Date().getTime()
        const distance = endTime - now
    if (distance <= 0) {
        countdown.hours = '00'
        countdown.minutes = '00'
        countdown.seconds = '00'
        countdown.isExpired = true
        if (distance <= 0) {
            countdown.hours = '00'
            countdown.minutes = '00'
            countdown.seconds = '00'
            countdown.isExpired = true
            if (countdown.timer) {
                clearInterval(countdown.timer)
                countdown.timer = null
            }
            return
        }
        const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
        const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60))
        const seconds = Math.floor((distance % (1000 * 60)) / 1000)
        countdown.hours = hours.toString().padStart(2, '0')
        countdown.minutes = minutes.toString().padStart(2, '0')
        countdown.seconds = seconds.toString().padStart(2, '0')
    }
    // 开始倒计时(60分钟)
    const startCountdown = (createTimeStr : string) => {
        if (!createTimeStr) return
        const createTime = new Date(decodeURIComponent(createTimeStr)).getTime()
        if (isNaN(createTime)) return
        const endTime = createTime + 60 * 60 * 1000
        // 先清一次老的
        if (countdown.timer) {
            clearInterval(countdown.timer)
            countdown.timer = null
        }
        return
    }
        countdown.isExpired = false
    const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
    const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60))
    const seconds = Math.floor((distance % (1000 * 60)) / 1000)
    countdown.hours = hours.toString().padStart(2, '0')
    countdown.minutes = minutes.toString().padStart(2, '0')
    countdown.seconds = seconds.toString().padStart(2, '0')
}
// 开始倒计时(60分钟)
const startCountdown = (createTimeStr: string) => {
    if (!createTimeStr) return
    const createTime = new Date(decodeURIComponent(createTimeStr)).getTime()
    if (isNaN(createTime)) return
    const endTime = createTime + 60 * 60 * 1000
    // 先清一次老的
    if (countdown.timer) {
        clearInterval(countdown.timer)
        countdown.timer = null
    }
    countdown.isExpired = false
    // 立即更新一次
    updateCountdown(endTime)
    // 每秒更新
    countdown.timer = setInterval(() => {
        // 立即更新一次
        updateCountdown(endTime)
    }, 1000)
}
onLoad(async (query: any) => {
    orderId.value = query.id
    goodsType.value = Number(query.goodsType)
    await getOrderDetail(goodsType.value, orderId.value)
})
        // 每秒更新
        countdown.timer = setInterval(() => {
            updateCountdown(endTime)
        }, 1000)
    }
onUnload(() => {
    if (countdown.timer) {
        clearInterval(countdown.timer)
        countdown.timer = null
    }
})
const getOrderDetail = async (type: number, id: string) => {
    let res = null
    if (type == 1) {
        res = await getBigGiftOrderDetail(id)
    } else {
        res = await getPhysicalOrderDetail(id)
    }
    console.log('res res res', res)
    if (res.code == 200) {
        // 只有待支付状态才启动倒计时
        if (res.data.status === 1 && res.data.createTime) {
            startCountdown(res.data.createTime)
        }
        status.value = res.data.status
        orderDetail.value = res.data
    }
}
const toPayMoney = async () => {
    // uni.navigateTo({
    //     url: `/pagesA/placeAnOrder/placeAnOrder?type=${goodsType.value}&id=${orderDetail.value.goodsId}&orderId=${orderId.value}`
    // })
    let res = null
    if (goodsType.value == 1) {
        res = await bigGiftPayNow(orderId.value)
    } else {
        res = await physicalPayNow(orderId.value)
    }
    if (res.code == 200) {
        payMoney(res.data)
    }
}
const toPayBalance = () => {
    uni.navigateTo({
        url: `/pagesA/placeAnOrder/placeAnOrder?id=${orderDetail.value.goodsId}&isBalance=1&type=${goodsType.value}&orderId=${orderId.value}`
    })
}
const payMoney = (payInfo: any) => {
    uni.requestPayment({
        timeStamp: payInfo.paymentParams.timestamp,
        nonceStr: payInfo.paymentParams.noncestr,
        package: payInfo.paymentParams.package,
        signType: payInfo.paymentParams.signType,
        paySign: payInfo.paymentParams.sign,
        success: (resp) => {
            uni.redirectTo({
                url: '/pagesA/paySuccess/paySuccess'
            })
        },
        fail: (err) => {
            console.log('err', err)
        }
    });
}
const toConfirm = async () => {
    let res = null
    if (goodsType.value == 1) {
        res = await bigGiftConfirm(orderId.value)
    } else {
        res = await physicalConfirm(orderId.value)
    }
    if (res.code == 200) {
        uni.showToast({
            title: '确认收货成功',
            icon: 'success'
        })
    onLoad(async (query : any) => {
        orderId.value = query.id
        goodsType.value = Number(query.goodsType)
        await getOrderDetail(goodsType.value, orderId.value)
    })
    onUnload(() => {
        if (countdown.timer) {
            clearInterval(countdown.timer)
            countdown.timer = null
        }
    })
    const getOrderDetail = async (type : number, id : string) => {
        let res = null
        if (type == 1) {
            res = await getBigGiftOrderDetail(id)
        } else {
            res = await getPhysicalOrderDetail(id)
        }
        console.log('res res res', res)
        if (res.code == 200) {
            // 只有待支付状态才启动倒计时
            if (res.data.status === 1 && res.data.createTime) {
                startCountdown(res.data.createTime)
            }
            status.value = res.data.status
            orderDetail.value = res.data
        }
    }
}
const back = () => {
    uni.navigateBack();
};
    const toPayMoney = async () => {
        // uni.navigateTo({
        //     url: `/pagesA/placeAnOrder/placeAnOrder?type=${goodsType.value}&id=${orderDetail.value.goodsId}&orderId=${orderId.value}`
        // })
        let res = null
        if (goodsType.value == 1) {
            res = await bigGiftPayNow(orderId.value)
        } else {
            res = await physicalPayNow(orderId.value)
        }
        if (res.code == 200) {
            payMoney(res.data)
        }
    }
    const toPayBalance = () => {
        uni.navigateTo({
            url: `/pagesA/placeAnOrder/placeAnOrder?id=${orderDetail.value.goodsId}&isBalance=1&type=${goodsType.value}&orderId=${orderId.value}`
        })
    }
    const payMoney = (payInfo : any) => {
        uni.requestPayment({
            timeStamp: payInfo.paymentParams.timestamp,
            nonceStr: payInfo.paymentParams.noncestr,
            package: payInfo.paymentParams.package,
            signType: payInfo.paymentParams.signType,
            paySign: payInfo.paymentParams.sign,
            success: (resp) => {
                uni.redirectTo({
                    url: '/pagesA/paySuccess/paySuccess'
                })
            },
            fail: (err) => {
                console.log('err', err)
            }
        });
    }
    const toConfirm = async () => {
        let res = null
        if (goodsType.value == 1) {
            res = await bigGiftConfirm(orderId.value)
        } else {
            res = await physicalConfirm(orderId.value)
        }
        if (res.code == 200) {
            uni.showToast({
                title: '确认收货成功',
                icon: 'success'
            })
            await getOrderDetail(goodsType.value, orderId.value)
        }
    }
    const back = () => {
        uni.navigateBack();
    };
</script>
<style>
page {
    background: linear-gradient(180deg, rgba(58, 211, 247, 0.1) 0%, rgba(27, 168, 236, 0) 100%);
}
    page {
        background: linear-gradient(180deg, rgba(58, 211, 247, 0.1) 0%, rgba(27, 168, 236, 0) 100%);
    }
</style>
<style scoped lang="scss">
.color1 {
    color: rgba(0, 0, 0, 0.45);
}
    .color1 {
        color: rgba(0, 0, 0, 0.45);
    }
.color2 {
    color: #FF9023;
}
    .color2 {
        color: #FF9023;
    }
.color3 {
    color: rgba(0, 0, 0, 0.65);
}
    .color3 {
        color: rgba(0, 0, 0, 0.65);
    }
.color4 {
    color: #1BA8EC !important;
}
    .color4 {
        color: #1BA8EC !important;
    }
.color5 {
    color: rgba(0, 0, 0, 0.5);
}
    .color5 {
        color: rgba(0, 0, 0, 0.5);
    }
.color6 {
    color: #ff4d4f;
}
    .color6 {
        color: #ff4d4f;
    }
.bgcolor1 {
    background-color: #fff;
}
    .bgcolor1 {
        background-color: #fff;
    }
.timeBox {
    background: #FF9023;
    border-radius: 8rpx;
    color: #FFFFFF;
    line-height: 27rpx;
    font-size: 23rpx;
    width: 38rpx;
    text-align: center;
}
    .timeBox {
        background: #FF9023;
        border-radius: 8rpx;
        color: #FFFFFF;
        line-height: 27rpx;
        font-size: 23rpx;
        width: 38rpx;
        text-align: center;
    }
.bottomBtn {
    position: fixed;
    left: 50%;
    transform: translate(-50%);
    width: calc(100% - 39rpx * 2);
    bottom: calc(env(safe-area-inset-bottom) + 56rpx);
    font-weight: 600;
    font-size: 35rpx;
    color: #FFFFFF;
    background: linear-gradient(270deg, #6BEADF 0%, #1BA8EC 100%);
    box-shadow: 0rpx 8rpx 19rpx 0rpx rgba(27, 168, 236, 0.3);
    border-radius: 67rpx;
    line-height: 96rpx;
    text-align: center;
}
    .bottomBtn {
        position: fixed;
        left: 50%;
        transform: translate(-50%);
        width: calc(100% - 39rpx * 2);
        bottom: calc(env(safe-area-inset-bottom) + 56rpx);
        font-weight: 600;
        font-size: 35rpx;
        color: #FFFFFF;
        background: linear-gradient(270deg, #6BEADF 0%, #1BA8EC 100%);
        box-shadow: 0rpx 8rpx 19rpx 0rpx rgba(27, 168, 236, 0.3);
        border-radius: 67rpx;
        line-height: 96rpx;
        text-align: center;
    }
</style>
miniprogram/pagesA/paySuccess/paySuccess.vue
@@ -2,7 +2,7 @@
    <view class="txt-center">
        <u-navbar title="支付成功" back-icon-name="arrow-leftward" :background="background" :border-bottom="false"
            title-bold title-color="#000" />
        <image src="/static/image/mine/edit.png" class="w-231 h-231 mt-138" />
        <image src="/static/image/mine/success.png" class="w-231 h-231 mt-138" />
        <view class="mt-46 fs-31 lh-42 font-bold">支付成功</view>
        <view class="mt-31 lh-33 fs-23 color1">{{ content }}</view>
        <view @tap.stop="backHome" class="mt-65 lh-96 mx-183 br-67 white bgcolor1 fs-35">返回主页</view>
@@ -10,40 +10,40 @@
</template>
<script setup lang="ts">
import { ref,reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
    import { ref, reactive } from 'vue'
    import { onLoad } from '@dcloudio/uni-app'
const background = reactive({
    backgroundColor: "#fff",
});
const content = ref('')
    const background = reactive({
        backgroundColor: "#fff",
    });
    const content = ref('')
const backHome = () => {
    uni.switchTab({
        url: '/pages/home/home'
    })
}
onLoad((options) => {
    if (options.content) {
        content.value = options.content
    const backHome = () => {
        uni.switchTab({
            url: '/pages/home/home'
        })
    }
    onLoad((options) => {
        if (options.content) {
            content.value = options.content
        }
})
    })
</script>
<style>
page {
    background-color: #fff;
}
    page {
        background-color: #fff;
    }
</style>
<style scoped lang="scss">
.color1 {
    color: rgba(0, 0, 0, 0.65);
}
    .color1 {
        color: rgba(0, 0, 0, 0.65);
    }
.bgcolor1 {
    background: linear-gradient(270deg, #6BEADF 0%, #1BA8EC 100%);
    box-shadow: 0rpx 8rpx 19rpx 0rpx rgba(27, 168, 236, 0.3);
}
    .bgcolor1 {
        background: linear-gradient(270deg, #6BEADF 0%, #1BA8EC 100%);
        box-shadow: 0rpx 8rpx 19rpx 0rpx rgba(27, 168, 236, 0.3);
    }
</style>
miniprogram/pagesA/promotion/promotion.vue
@@ -39,6 +39,9 @@
            <view class="bgcolor2 border3 pt-30 pb-42">
                <image @touchend="handleTouch" show-menu-by-longpress :src="imgBase64"
                    style="width: 550rpx;height: 550rpx;" />
                <!-- 隐藏的 canvas 用于转换 base64(2d 模式) -->
                <canvas type="2d" canvas-id="tempCanvas" id="tempCanvas"
                    style="position: fixed; top: -9999px; width: 550px; height: 550px;"></canvas>
                <view class="mt-18 fs-23 lh-33 color3">双击保存图片,分享给你好友吧</view>
                <button open-type="share" class="mt-35 mx-115 lh-85 white fs-31 font-bold br-67 bgColor3 btnSty">
                    分享链接至微信好友</button>
@@ -48,22 +51,25 @@
</template>
<script setup lang="ts">
    import { ref, reactive } from "vue";
    import { ref, reactive, getCurrentInstance } from "vue";
    import { onShareAppMessage, onLoad } from '@dcloudio/uni-app'
    import { saveImage } from '@/utils/utils.js'
    import config from '../../config/index.js'
    const userInfo = ref<any>({});
    const imgBase64 = ref('')
    const background = reactive({
        backgroundColor: "transparent",
    });
    const touchCount = ref(0)
    const uploadedImageUrl = ref('') // 存储上传后的图片链接
    const instance = getCurrentInstance()
    onLoad(() => {
        wx.showShareMenu({
            menus: ['shareAppMessage', 'shareTimeline']
        })
        userInfo.value = uni.getStorageSync('userInfo')
        imgBase64.value = userInfo.value.promotionQrCode ? 'data:image/png; eyJlcnJjb2RlIjo0MTAzMCwiZXJybXNnIjoiaW52YWxpZCBwYWdlIHJpZDogNjk0ZGU3OTEtMGVjOTE1MGEtNDhjOWYyNzcifQ==' : ''
        imgBase64.value = userInfo.value.promotionQrCode ? 'data:image/jpeg;base64,' + userInfo.value.promotionQrCode : ''
    })
    onShareAppMessage((e) => {
@@ -78,17 +84,237 @@
        return num.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, "$&,");
    };
    // 将 base64 转换为临时文件(优先用 2d canvas 压缩生成 temp 文件,避免超限)
    const base64ToTempFile = (base64Data: string): Promise<string> => {
        return new Promise((resolve, reject) => {
            drawBase64ToCanvas(base64Data).then(resolve).catch(reject)
        })
    }
    // 使用 2d canvas 绘制 base64 图片并导出为临时文件(压缩防止超限)
    const drawBase64ToCanvas = (base64Data: string): Promise<string> => {
        return new Promise((resolve, reject) => {
            // 目标尺寸适当缩小,减少文件体积
            const targetSize = 500
            const quality = 0.7
            const query = uni.createSelectorQuery()
            // 作用域限定在当前组件,避免在自定义组件中拿不到节点
            // @ts-ignore
            if (instance?.proxy) {
                // @ts-ignore
                query.in(instance.proxy)
            }
            query
                .select('#tempCanvas')
                .fields({ node: true, size: true })
                .exec((res) => {
                    if (!res || !res[0] || !res[0].node) {
                        reject(new Error('canvas 节点获取失败'))
                        return
                    }
                    const canvas = res[0].node
                    const ctx = canvas.getContext('2d')
                    const dpr = uni.getSystemInfoSync().pixelRatio || 1
                    canvas.width = targetSize * dpr
                    canvas.height = targetSize * dpr
                    ctx.scale(dpr, dpr)
                    const img = canvas.createImage()
                    img.onload = () => {
                        ctx.clearRect(0, 0, targetSize, targetSize)
                        ctx.drawImage(img, 0, 0, targetSize, targetSize)
                        uni.canvasToTempFilePath({
                            canvas,
                            quality,
                            destWidth: targetSize,
                            destHeight: targetSize,
                            success: (res2) => resolve(res2.tempFilePath),
                            fail: (err) => {
                                console.error('canvas 导出失败:', err)
                                reject(err)
                            }
                        })
                    }
                    img.onerror = (err) => {
                        console.error('图片加载失败:', err)
                        reject(err)
                    }
                    img.src = base64Data
                })
        })
    }
    // 上传 base64 图片并获取链接
    const uploadBase64Image = async (base64Data: string, showLoading = false): Promise<string> => {
        return new Promise((resolve, reject) => {
            try {
                if (showLoading) {
                    uni.showLoading({
                        title: '正在处理图片...'
                    })
                }
                // 将 base64 转换为临时文件
                base64ToTempFile(base64Data).then((tempFilePath) => {
                    // 上传文件
                    uni.uploadFile({
                        url: config.uploadUrl,
                        name: 'file',
                        filePath: tempFilePath,
                        header: {
                            'Authorization': uni.getStorageSync('token') || '',
                        },
                        success: (res) => {
                            try {
                                const data = JSON.parse(res.data).data
                                const imageUrl = config.imageUrl + data
                                uploadedImageUrl.value = imageUrl
                                if (showLoading) {
                                    uni.hideLoading()
                                }
                                resolve(imageUrl)
                            } catch (err) {
                                console.error('解析上传结果失败:', err)
                                if (showLoading) {
                                    uni.hideLoading()
                                    uni.showToast({
                                        title: '处理失败,请重试',
                                        icon: 'error'
                                    })
                                }
                                reject(err)
                            }
                        },
                        fail: (err) => {
                            console.error('上传失败:', err)
                            if (showLoading) {
                                uni.hideLoading()
                                uni.showToast({
                                    title: '上传失败,请重试',
                                    icon: 'error'
                                })
                            }
                            reject(err)
                        },
                        complete: () => {
                            // 清理临时文件
                            uni.getFileSystemManager().unlink({
                                filePath: tempFilePath,
                                fail: (err) => {
                                    console.error('清理临时文件失败:', err)
                                }
                            })
                        }
                    })
                }).catch((err) => {
                    console.error('处理图片失败:', err)
                    if (showLoading) {
                        uni.hideLoading()
                        uni.showToast({
                            title: '处理失败,请重试',
                            icon: 'error'
                        })
                    }
                    reject(err)
                })
            } catch (err) {
                console.error('处理图片失败:', err)
                if (showLoading) {
                    uni.hideLoading()
                    uni.showToast({
                        title: '处理失败,请重试',
                        icon: 'error'
                    })
                }
                reject(err)
            }
        })
    }
    const handleTouch = () => {
        touchCount.value++;
        setTimeout(() => {
            if (touchCount.value >= 2) {
                if (imgBase64.value) {
                    saveImage(imgBase64.value)
                if (uploadedImageUrl.value) {
                    // 如果已有上传后的链接,直接使用
                    saveImage(uploadedImageUrl.value)
                } else if (imgBase64.value) {
                    // 如果没有链接,直接保存 base64(内部会转成临时文件,并压缩)
                    saveBase64Image(imgBase64.value)
                }
            }
            touchCount.value = 0;
        }, 250);
    }
    // 直接保存 base64 图片(使用 canvas 转换,避免写入临时文件)
    const saveBase64Image = (base64Data: string) => {
        uni.showLoading({
            title: '正在保存图片...'
        });
        uni.getSetting({
            success: (res) => {
                const saveToAlbum = () => {
                    // 直接使用 canvas 转换 base64 为临时文件
                    drawBase64ToCanvas(base64Data).then((tempFilePath) => {
                        uni.saveImageToPhotosAlbum({
                            filePath: tempFilePath,
                            success: () => {
                                uni.hideLoading();
                                uni.showToast({
                                    title: "保存成功!"
                                });
                            },
                            fail: (err) => {
                                console.error("保存失败:", err);
                                uni.hideLoading();
                                uni.showToast({
                                    title: "保存失败,请重试!",
                                    icon: 'error'
                                });
                            }
                        });
                    }).catch((err) => {
                        console.error("转换图片失败:", err);
                        uni.hideLoading();
                        uni.showToast({
                            title: "保存失败,请重试!",
                            icon: 'error'
                        });
                    });
                };
                if (!res.authSetting["scope.writePhotosAlbum"]) {
                    uni.authorize({
                        scope: "scope.writePhotosAlbum",
                        success: saveToAlbum,
                        fail: () => {
                            uni.hideLoading();
                            uni.showModal({
                                title: "权限未授权",
                                content: "请前往设置中开启相册权限",
                                success: (res) => {
                                    if (res.confirm) {
                                        uni.openSetting();
                                    }
                                }
                            });
                        }
                    });
                } else {
                    saveToAlbum();
                }
            },
            fail: () => {
                uni.hideLoading();
                uni.showToast({
                    title: "获取权限失败!",
                    icon: 'error'
                });
            }
        });
    }
</script>
<style>
miniprogram/static/image/mine/success.png
miniprogram/utils/utils.js
@@ -2,31 +2,62 @@
    uni.showLoading({
        title: '正在保存图片...'
    });
    // 检查是否是 base64 格式
    const isBase64 = url && (url.startsWith('data:image') || url.startsWith('base64://'));
    // 如果是 base64,需要先转换为临时文件
    const getImagePath = (imageUrl) => {
        return new Promise((resolve, reject) => {
            if (isBase64) {
                // base64 图片,使用 canvas 转换为临时文件
                convertBase64ToTempFile(imageUrl).then(resolve).catch(reject);
            } else {
                // 普通图片链接,直接获取图片信息
                uni.getImageInfo({
                    src: imageUrl,
                    success: (res) => {
                        resolve(res.path);
                    },
                    fail: reject
                });
            }
        });
    };
    uni.getSetting({
        success: async (res) => {
            if (!res.authSetting["scope.writePhotosAlbum"]) {
                uni.authorize({
                    scope: "scope.writePhotosAlbum",
                    success: async () => {
                        const res = await uni.getImageInfo({
                            src: url
                        });
                        uni.saveImageToPhotosAlbum({
                            filePath: res.path,
                            success: () => {
                                uni.hideLoading();
                                uni.showToast({
                                    title: "保存成功!"
                                });
                            },
                            fail: (err) => {
                                console.error("保存失败:", err);
                                uni.showToast({
                                    title: "保存失败,请重试!",
                                    icon: 'error'
                                });
                            }
                        });
                        try {
                            const filePath = await getImagePath(url);
                            uni.saveImageToPhotosAlbum({
                                filePath: filePath,
                                success: () => {
                                    uni.hideLoading();
                                    uni.showToast({
                                        title: "保存成功!"
                                    });
                                },
                                fail: (err) => {
                                    console.error("保存失败:", err);
                                    uni.hideLoading();
                                    uni.showToast({
                                        title: "保存失败,请重试!",
                                        icon: 'error'
                                    });
                                }
                            });
                        } catch (err) {
                            console.error("处理图片失败:", err);
                            uni.hideLoading();
                            uni.showToast({
                                title: "保存失败,请重试!",
                                icon: 'error'
                            });
                        }
                    },
                    fail: () => {
                        uni.hideLoading();
@@ -42,25 +73,33 @@
                    }
                });
            } else {
                const res = await uni.getImageInfo({
                    src: url
                });
                uni.saveImageToPhotosAlbum({
                    filePath: res.path,
                    success: () => {
                        uni.hideLoading();
                        uni.showToast({
                            title: "保存成功!"
                        });
                    },
                    fail: (err) => {
                        console.error("保存失败:", err);
                        uni.showToast({
                            title: "保存失败,请重试!",
                            icon: 'error'
                        });
                    }
                });
                try {
                    const filePath = await getImagePath(url);
                    uni.saveImageToPhotosAlbum({
                        filePath: filePath,
                        success: () => {
                            uni.hideLoading();
                            uni.showToast({
                                title: "保存成功!"
                            });
                        },
                        fail: (err) => {
                            console.error("保存失败:", err);
                            uni.hideLoading();
                            uni.showToast({
                                title: "保存失败,请重试!",
                                icon: 'error'
                            });
                        }
                    });
                } catch (err) {
                    console.error("处理图片失败:", err);
                    uni.hideLoading();
                    uni.showToast({
                        title: "保存失败,请重试!",
                        icon: 'error'
                    });
                }
            }
        },
        fail: () => {
@@ -71,4 +110,31 @@
            });
        }
    });
};
// 将 base64 转换为临时文件(直接写入文件,避免 getImageInfo 失败)
const convertBase64ToTempFile = (base64Data) => {
    return new Promise((resolve, reject) => {
        try {
            const fs = uni.getFileSystemManager();
            // 兼容不同前缀
            const match = /^data:image\/(\w+);base64,/.exec(base64Data);
            const ext = match ? match[1] : 'jpg';
            const pureBase64 = base64Data.replace(/^data:image\/\w+;base64,/, '').replace(/^base64:\/\//, '');
            const filePath = `${wx.env.USER_DATA_PATH}/temp_${Date.now()}.${ext}`;
            const arrayBuffer = (wx.base64ToArrayBuffer || uni.base64ToArrayBuffer)(pureBase64);
            fs.writeFile({
                filePath,
                data: arrayBuffer,
                encoding: 'binary',
                success: () => resolve(filePath),
                fail: (err) => {
                    console.error('writeFile base64 失败:', err);
                    reject(err);
                }
            });
        } catch (err) {
            reject(err);
        }
    });
};