• uniapp——规格选择判断库存(类似电商商品加购)


    背景需求:当用户下单时需要选择设定的规格,同时根据库存判断是否可以选择,可以选择按钮高亮显示,不可以选择按钮禁用。

    实现逻辑:进去商品页面时即获取当前商品的所有库存信息和规格列表,点击按钮时组合当前点击的规格,与后台获取的规格组合进行比对,相同时再判断库存,没有库存禁用按钮,有库存则高亮显示。

    核心代码:

    <template>
        <view class="detail_page" v-cloak :class="isShowPopup? 'popup-show': ''">
            <navBar :title="topInfo.title" :url="topInfo.url" :icon_title="topInfo.icon_title" :type="topInfo.type">
            </navBar>
            <view class="pages" v-cloak>
                <view class="info scroll_item" id="scroll0">
                    <view class="banner_top">
                        <swiper :swiperConfig="swiperConfig"></swiper>
                    </view>
                    <view class="page_info page_item">
                        <view class="price">
                            <text class="large">{{listData.price}}</text>
                            元起
                        </view>
                        <view class="title">{{listData.name}}</view>
                        <view class="sub_title">强效清洗,全面爱家,重新焕发地毯新生</view>
                    </view>
                    <view class="form_box page_item">
                        <uni-forms ref="signUpform" :value="signUpformData" :rules="rules">
                            <uni-forms-item label="小区" name="housing_id" style="display: none;"></uni-forms-item>
                            <uni-forms-item label="地址" name="user_address_id" @tap="goAddress">
                                <text class="picker_lable">{{address}}</text>
                            </uni-forms-item>
                            <uni-forms-item label="规格" name="flag" @tap="chorceSpecs">
                                <text class="picker_lable">{{specs}}</text>
                            </uni-forms-item>
                            <uni-forms-item label="时间" name="start_time" @tap="openDatetimePicker">
                                <text class="picker_lable">{{signUpformData.start_time}}</text>
                            </uni-forms-item>
                        </uni-forms>
                    </view>
                
                
                </view>
                
                <view class="details scroll_item " id="scroll1" style="padding-bottom:80px;">
                    <view style="color: #a29696;margin-bottom: 20rpx; text-align: center;">——详情——</view>
                    <view class="page_detail page_item" v-html="detail_content" style="margin-bottom:70px;">
                        {{detail_content}}
                    </view>
                </view>    
                
                <view class="footer">
                    <view>
                        <image class="service" mode="aspectFit" @tap="callPhone" src="../../static/images/service.png">
                        </image>
                        <view class="byButton" form-type="submit" @tap="handleBuy">立即购买</view>
                    </view>
                </view>
            </view>
    
            <!-- 页面滚动到详情时再显示锚点 -->
            <view class="fixedtop" v-show="scrollDistance > 100">
                <view class="fixedtop_box">
                    <view v-for="(item,index) in fixedList" :key="index" :class="{'active':currentIndex == index}"
                        @tap="changeactive(index)">{{item.name}}
                    </view>
                </view>
            </view>
            <!-- 规格 -->
            <uni-popup ref="popup" type="bottom" :mask-click="false">
                <view class="specs_box spec-shade">
                    <view class="specs_box_box">
                        <view class="title">
                            <text></text>
                            <text class="title">请选择服务规格</text>
                            <!--   <text @click="closeProp"
                                   class="iconfont icon-close"></text> -->
                            <text></text>
                        </view>
                        <view class="spec-cont">
                            <view class="goods-choose-show flex-star">
                                <view class="goods-choose-show-right" v-if="currentItem">
                                    <p>¥{{currentItem.price || 0}}</p>
                                    <view>库存{{currentItem.all_count || 0}}件</view>
                                    <!-- <view>{{specShowString}}</view> -->
                                </view>
                            </view>
                            <view class="spec-linebox" style="max-height: 550rpx;overflow-y: scroll;">
                                <view class="spec-item" v-for="(skuItem,skuIndex) in skuInfo">
                                    <p>{{skuItem.title}}</p>
                                    <view class="item-cont flex-star">
                                        <span v-for="(item,index) in skuItem.option"
                                            :class="[item.isShow ? '' : 'noProduct',subIndex[skuIndex] == item.id ? 'act' : '']"
                                            @click="item.isShow ? specificationBtn(item.info,skuIndex,item.id) : disabled">{{item.info}}</span>
                                    </view>
                                </view>
                            </view>
                        </view>
                        <button class="confirm" @click="confirmSpec">确定</button>
                    </view>
                </view>
            </uni-popup>
    
            <!-- 时间 -->
            <SimpleDateTimePicker ref="myPicker" @submit="handleSubmit" :start-year="year" :end-year="year+20" />
        </view>
    
    </template>
    
    <script>
        import navBar from "@/components/navBar/navBar.vue"
        import swiper from "@/components/swiper/swiper.vue"
        import forms from "@/components/uni-forms/uni-forms.vue"
        import formItems from "@/components/uni-forms-item/uni-forms-item.vue"
        import SimpleDateTimePicker
        from "uni_modules/buuug7-simple-datetime-picker/components/buuug7-simple-datetime-picker/buuug7-simple-datetime-picker.vue";
        import popup from "@/components/uni-popup/uni-popup.vue"
        import {
            mapMutations
        } from 'vuex';
    
        export default {
            components: {
                navBar,
                swiper,
                formItems,
                forms,
                SimpleDateTimePicker,
                popup
            },
    
            data() {
                return {
                    isShowPopup: false,
                    payParams: 0,
                    goods_id: 0,
                    isInit: 0, // 默认只显示第一组规格
                    allCount_id: 0, // 库存为0的id
                    stockIndex: 0, //库存为0的id组
                    stock_flag: [],
                    getSpecs: [],
                    topInfo: {
                        url: "../../pages/index/index",
                        title: "预约详情",
                        icon_title: "返回",
                        type: 'tab'
                    },
                    token: "",
                    year: "",
                    listData: {},
                    swiperConfig: {
                        indicatorDots: true,
                        vertical: false,
                        autoplay: true,
                        interval: 2000,
                        duration: 600,
                        bannerList: [],
                        swiperH_height: "400rpx"
                    },
                    detail_content: "", //详情内容
                    specs: "请选择规格",
                    address: "请选择服务地址",
                    signUpformData: {
                        user_address_id: "",
                        housing_id: "",
                        flag: "",
                        start_time: "请选择服务时间"
                    },
                    currentIndex: 0,
                    fixedList: [{
                            name: "商品",
                            targetDom: ".info"
                        },
                        {
                            name: "详情",
                            targetDom: ".details"
                        }
                    ], // 固定顶部锚点链接
                    rules: {
                        address: {
                            rules: [{
                                required: true,
                                errorMessage: "请选择服务地址"
                            }]
                        },
                        type: {
                            rules: [{
                                required: true,
                                errorMessage: "请选择服务规格"
                            }]
                        },
                        serviceTime: {
                            rules: [{
                                required: true,
                                errorMessage: "请选择服务时间"
                            }]
                        }
                    },
                    elHeight: "",
                    scrollDistance: 0,
                    doms: "",
                    topList: [],
                    multiIndex: 0,
                    specsList: [],
                    isShow: false,
                    selectArr: [],
                    subIndex: [],
                    currentItem: {
                        flag: '',
                        price: 0,
                        all_count: 0,
                        title: ""
                    },
                    skuInfo: [],
                    specShowString: '选择规格',
                    goodsData: {
                        priceInfo: [],
                        SKUInfo: [],
                    },
                    category_type: 0,// 分类id(只针对垃圾回收)
                }
            },
    
            onLoad(option) {
                console.log('下单确定地址', option);
                /* 获取当前年份 */
                let date = new Date();
                this.year = date.getFullYear();
                /* 设置页面滚动高度 */
                let screenH = uni.getSystemInfoSync().screenHeight;
                this.elHeight = screenH;
    
    
                let loginRes = this.checkLogin();
                if (!loginRes) {
                    return false;
                } else {
                    /* 初始化页面 */
                    if (option.id) {
                        uni.setStorageSync("goods_id", option.id);
                    }
                    if(option.category_type){
                        uni.setStorageSync("category_type", option.category_type);
                    }
                    this.token = uni.getStorageSync("token");
                    this.goods_id = uni.getStorageSync("goods_id");
                    this.category_type = uni.getStorageSync("category_type");
                }
    
                // this.firstStock();
                this.getStock();
                this.getInfo();
    
                /* 选择地址 */
                if (option.address_id) {
                    this.signUpformData.user_address_id = option.address_id;
                    this.address = option.address;
    
                }
                /* 小区id */
                if (option.housing_id) {
                    this.signUpformData.housing_id = option.housing_id;
                }
    
    
            },
        
            methods: {
                getStock() {
                    let _param = {};
                    _param.operate = "order.get_price",
                        _param.goods_id = this.goods_id;
                    _param.token = this.token;
                    _param.flag = [0];
    
                    this.$myRequset({
                        url: "/api?operate=goods.get_price",
                        method: "POST",
                        data: _param,
                    }).then((res) => {
                        this.goodsData.priceInfo = res.data.data;
                        // console.log('获取价格', this.goodsData.priceInfo);
                    });
                },
                getInfo() {
                    this.$myRequset({
                        url: "/api?operate=goods.info",
                        method: "POST",
                        data: {
                            token: this.token,
                            goods_id: this.goods_id
                        },
                    }).then((res) => {
                        this.swiperConfig.bannerList = res.data.data.album_images;
                        this.listData.name = this.topInfo.title = res.data.data.name;
                        this.listData.price = res.data.data.price;
                        this.detail_content = res.data.data.content;
                        this.goodsData.SKUInfo = res.data.data.spec_config;
                        this.skuInfo = this.goodsData.SKUInfo;
                        if(this.skuInfo){
                            this.skuInfo.forEach(item => {
                                item.option.forEach(specItem => {
                                    specItem.isShow = true;
                                })
                            })
                        }
                        // console.log('规格列表', this.goodsData.SKUInfo);
                        // console.log('规格列表_skuInfo', this.skuInfo);
                    });
    
                },
                disabled() {
                    uni.showToast({
                        title: "不可点击"
                    })
                },
                /* 规格 */
                chorceSpecs() {
                    setInterval(function() {
                        uni.hideKeyboard(); //隐藏软键盘
                    }, 10);
                    if (!this.signUpformData.user_address_id) {
                        uni.showToast({
                            title: "请先选择地址",
                            icon: "none"
                        })
                    } else if (this.specsList) {
                        this.$refs.popup.open();
                        this.isShowPopup = true;
                    } else {
                        uni.showToast({
                            title: "暂无规格可选",
                            icon: "none"
                        })
                    }
                },
                closeProp() {
                    if (this.subIndex.length == 0) {
                        uni.showToast({
                            icon: "error",
                            title: "请选择服务规格"
                        });
                    } else {
                        this.$refs.popup.close();
                    }
                },
                /* 确定规格 */
                confirmSpec() {
                    console.log('当前选择', this.subIndex);
                    console.log('当前选择', this.selectArr);
                    // this.signUpformData.type = this.subIndex.toString(",");
    
                    this.signUpformData.flag = this.subIndex;
                    this.specs = this.specShowString;
                    if (this.currentItem.price == 0) {
                        uni.showToast({
                            icon: "none",
                            title: "请选择完整规格"
                        })
                    } else {
                        this.$refs.popup.close();
                    }
                },
    
                /**
                 * @param {String} specName 当前点击规格按钮的值(黑色,35)
                 * @param {Number} specIndex 选择的规格下标(例子中颜色是0,尺码是1)
                 * @param {Number} specItemIndex 选择规格值id(例子中35下标是0)
                 */
                specificationBtn(specName, specIndex, specItemIndex) {
                    if (this.selectArr[specIndex] != specName) { //判断所选规格数组中是否包含当前点击规格
                        this.selectArr[specIndex] = specName; //如果没有则把当前规格添加
                        this.subIndex[specIndex] = Number(specItemIndex); //添加选中样式
                    } else {
                        this.selectArr[specIndex] = '';
                        this.subIndex[specIndex] = -1; //去除样式
                    }
                    this.specShowString = this.spaceRemoveArr(this.selectArr).join(',') || '选择规格'; //所选规格页面中展示,数组为空则变为选择规格
                    this.inventoryLookup(); //当规格选完后,去匹配
                    this.clickPitch(); //库存判断,实现不可点击
                },
                spaceRemoveArr(arr) { //数组去除空字符串
                    let tempArr = []
                    arr.forEach(item => {
                        if (item) {
                            tempArr.push(item)
                        }
                    })
                    return tempArr;
                },
                /* 规格选完后,去匹配 */
                inventoryLookup() {
                    try {
                        this.goodsData.priceInfo.forEach((item, index) => {
                            if (item.title == this.specShowString) {
                                this.currentItem = item;
                                this.listData.price = this.currentItem.price;
                                throw new Error();
                            } else {
                                this.currentItem = {
                                    flag: '',
                                    price: 0,
                                    all_count: 0,
                                    title: ""
                                };
                            }
                        })
                    } catch (e) {}
                },
                /* 库存判断,实现不可点击 */
                clickPitch() {
                    let result = [];
                    // console.log('库存判断:',this.goodsData.SKUInfo);
                    for (let i in this.goodsData.SKUInfo) {
                        result[i] = this.selectArr[i] ? this.selectArr[i] : '';
                    }
                    //最难理解的大概就是这里了,这里跟着循环里打印结果,多走几遍就大致明白了  假象.jpg
                    for (let i in this.goodsData.SKUInfo) {
                        let last = result[i];
                        // console.log("最后点击======",last);
                        for (let k in this.goodsData.SKUInfo[i].option) {
                            result[i] = this.goodsData.SKUInfo[i].option[k].info;
                            // console.log('result--------', result, "last:", last);
                            this.skuInfo[i].option[k].isShow = this.isMay(result)
                        }
                        result[i] = last;
                    }
                    // console.log('库存结果111:',result);
                },
                isMay(result) {
                    for (let i in result) {
                        if (result[i] == '') {
                            return true;
                        }
                    }
                    for (let i in this.goodsData.priceInfo) {
                        if (this.goodsData.priceInfo[i].title == result.join(",") && this.goodsData.priceInfo[i].all_count >
                            0) {
                            return true;
                        }
                    }
                },
                goAddress() {
                    console.log('goAddress');
                    uni.redirectTo({
                        url: "../addressList/addressList"
                    });
                },
                /* 时间 */
                openDatetimePicker() {
                    if (!this.signUpformData.user_address_id) {
                        uni.showToast({
                            icon: "none",
                            title: "请选择服务地址"
                        })
                    } else if (!this.signUpformData.flag) {
                        uni.showToast({
                            icon: "none",
                            title: "请选择服务规格"
                        })
    
                    } else {
                        this.$refs.myPicker.show();
                    }
                },
                /* 选择时间 */
                handleSubmit(e) {
                    console.log('times', e);
                    this.signUpformData.start_time = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}`;
                },
            // 锚点链接滚动监听
                onPageScroll(res){
                    this.scrollDistance = res.scrollTop;
                    // console.log('滚动距离',res.scrollTop);//距离页面顶部距离
                    if(res.scrollTop >=600){
                        this.currentIndex = 1;
                    }else{
                        this.currentIndex = 0;
                    }
                },
                          /* 锚点点击*/          
                changeactive(index) {
                    console.log('sdsd',index);
                    if(index==1){
                        uni.pageScrollTo({
                            duration:300, // 毫秒
                            scrollTop:600 // 位置
                        });
                    }else{
                        uni.pageScrollTo({
                            duration:300, // 毫秒
                            scrollTop:0 // 位置
                        });
                    }
                },
    
                /* 表单提交 */
                handleBuy() {
                    this.$refs.signUpform.validate().then(res => {
                        console.log('表单数据信息:', res);
                        let _param = res;
                        _param.operate = "wechat.miniapp";
                        _param.token = this.token;
                        _param.goods_id = this.goods_id;
                        
                        // 垃圾回收
                        if(this.category_type == 21){
                            _param.category_type = this.category_type;
                        }
                        console.log('(_param.flag', typeof _param.flag);
                        let _res = this.$myRequset({
                            url: "/api/pay/index",
                            method: "POST",
                            data: _param,
                        }).then((res) => {
                            console.log('_res', res);
                            if (res.data.code == 1) {
                                let signType = res.data.data.signType
                                let nonceStr = res.data.data.nonceStr;
                                let _package = res.data.data.package;
                                let paySign = res.data.data.paySign;
                                let timeStamp = res.data.data.timeStamp;
    
                                uni.requestPayment({
                                    provider: 'wxpay',
                                    timeStamp: timeStamp,
                                    nonceStr: nonceStr,
                                    package: _package,
                                    signType: signType,
                                    paySign: paySign,
                                    success: function(res) {
                                        console.log('pay_success:' + JSON.stringify(res));
                                        uni.reLaunch({
                                            url: "../../pages/order/order"
                                        });
                                    },
                                    fail: function(err) {
                                        console.log('pay_fail:' + JSON.stringify(err));
                                        uni.reLaunch({
                                            url: "../../pages/order/order",
                                        });
                                    }
                                });
    
                            } else {
                                uni.showToast({
                                    icon: "none",
                                    title: "请选择规格"
                                })
                                return false;
                            }
    
                        });
    
    
                    }).catch(err => {
                        uni.showToast({
                            title: err[0].errorMessage,
                            icon: "none"
                        });
                    })
                },
                /* 拨打电话 */
                callPhone() {
                    uni.makePhoneCall({
                        phoneNumber: "15902341080"
                    });
    
                },
            }
        }
    </script>
    <style>
        @import url("buyDetail.css");
    </style>
    <style lang="scss">
        .spec-shade {
            .spec-cont {
                .goods-choose-show {
                    margin: -40rpx 0 10rpx 0;
    
                    img {
                         180rpx;
                        height: 180rpx;
                        box-shadow: 0 0 10px rgba(0, 0, 0, .2);
                        border-radius: 10rpx;
                        margin-right: 20rpx;
                        border: 5rpx solid #fff;
                    }
    
                    .goods-choose-show-right {
                        position: relative;
    
                        p {
                            color: #FE9E01;
                            font-size: 32rpx;
                            font-weight: bold;
                        }
    
                        view {
                            color: #666;
                            font-size: 26rpx;
                            margin-top: 5rpx;
                        }
                    }
    
                    i.icon-cuowu {
                        position: absolute;
                        top: 30rpx;
                        right: 30rpx;
                    }
                }
    
                .spec-item {
                    padding: 15upx 0;
                    border-bottom: 1px solid #eee;
    
                    p {
                        font-weight: bold;
                        line-height: 50rpx;
                    }
    
                    .item-cont {
                        padding: 15rpx 0;
    
                        span {
                            display: inline-block;
                            padding: 10rpx 18rpx;
                            border-radius: 8rpx;
                            color: #666;
                            font-size: 36rpx;
                            background: #F6F4F5;
                            margin: 0 30rpx 30rpx 0;
    
                            &:last-child {
                                margin: 0;
                            }
                        }
    
                        span.act {
                            background: #FDAB27;
                            color: white;
                        }
    
                        span.noProduct {
                            color: #ccc;
                        }
                    }
                }
    
    
            }
        }
    </style>

    规格判断库存难点:

          1、开始的逻辑是点击一次获取一次后台库存再判断按钮是否高亮或禁用,此方法不通,因为库存判断和按钮点击是同步进行的,不能异步判断。

          2、按钮的高亮和禁用一定是在点击按钮的同时就判断,不能分开,否则就会乱。

    商品详情页面锚点滚动判断:不要使用scroll-view组件  不要使用scroll-view组件  不要使用scroll-view组件 重要的事情说三遍,,这个组件有bug 用了之后页面滚动有问题,总是会卡顿并且总是有双层滚动条还找不到是哪个模块出的问题。。直接使用uniapp内置方法 onPageScroll() 监听滚动距离即可。

  • 相关阅读:
    HTML5 Input 类型
    Html5 web 储存
    解决json日期格式问题的3种方法(转载)
    Json格式串处理
    全局图片防盗链处理
    我的博客开张了
    iPhone手机屏幕分辨率
    通过CSS3伪类,美化Radio按钮样式
    测试用例 相关
    MongoDB基本命令
  • 原文地址:https://www.cnblogs.com/LindaBlog/p/15570833.html
Copyright © 2020-2023  润新知