<template> <view class="container"> <view style="position: fixed;left: 0;right: 0;z-index: 1"> <uni-nav-bar @clickLeft="back" fixed="true" status-bar="true" left-icon="back" color="white" :backgroundColor="bgColor" :border="false"></uni-nav-bar> </view> <scroll-view class="product-scroll" @scroll="handleScoll" :style="{bottom:176*(productInfo.platformProductPriceVos.length==1?1:2)+100 + 40 +'rpx',paddingBottom:safeBottom +'px'}" scroll-y="true"> <view> <view class="swiper-content-box"> <swiper class="swiper" circular :indicator-dots="isIndicator"> <swiper-item v-for="(item, index) in swiperList" :key="index"> <image class="img" :src="item" mode="aspectFill"></image> </swiper-item> </swiper> </view> <view class="name-price-box"> <view class="box name">{{productInfo.productVo.name ||''}}</view> <view class="taxRate" style="padding-top: 8px;color: #697077;"> {{productInfo.productVo.brandNumber}}/{{productInfo.productVo.manufacturer}}/{{productInfo.productVo.varieties}} </view> <view class="box"> <text class="price" v-if="priceInfo.price">¥{{priceInfo.price||$t('index.price-expire')}}<text class="unit">{{$t('index.unit.price')}}/{{productInfo.productVo.unit}}</text></text> <text class="price" v-else>{{$t('index.price-expire')}}</text> <text class="taxRate">{{$t('index.tax-rate')}}:{{productInfo.productVo.taxRate}}%</text> </view> <view class="price-bg"> <view class="price-bg-item"> <view class=""> <text>{{$t('index.price')}}</text> </view> <view class=""> <text>{{$t('index.bactch-num')}}</text> </view> </view> <view class="price-bg-item" v-for="(item,index) in priceInfo.productTieredPricings" :key="index"> <view> <text>¥{{item.salePrice}}</text> </view> <view> <text v-if="item.maxBatchNum == undefined">></text> <text>{{item.minBatchNum}}</text> <text v-if="item.maxBatchNum != undefined">-</text> <text>{{item.maxBatchNum}}</text> </view> </view> </view> </view> <!-- 店铺信息 --> <view class="shop" v-if="shopInfo!=null" @tap="enterShop"> <view class="cover"> <image :src="shopInfo.customerVo.logoUrl" mode="aspectFit"></image> </view> <view class="info"> <view class="name-row"> <view class="name" style="color: #222;"> {{shopInfo.customerVo.customerName}} </view> <view> <uni-icons type="right" size="15" color="697077"></uni-icons> </view> </view> <view class="desc"> <view style="display: flex;flex-grow: 1;"> <view v-for="(item,index) in tags" :key="index"> <view class="tag"> {{item}} </view> </view> </view> <view style="display: flex;flex-direction: column;justify-content: center;align-items: center;" @tap.stop="collectShop"> <uni-icons :type="isCollect?'star-filled':'star'" :color="isCollect?'#B60001':'#202228'" size="24"></uni-icons> <view> <text :style="{color:isCollect?'#B60001':'#202228'}">{{isCollect?$t('index.cancel'):$t('index.collect')}}</text> </view> </view> </view> </view> </view> <!-- 其他信息 --> <view class="list-box custom-uni-list"> <uni-list> <uni-list-item :title="$t('index.spec')" :rightText="productInfo.productVo.varieties" /> <uni-list-item :title="$t('index.card-num')" :rightText="productInfo.productVo.brandNumber" /> <uni-list-item :title="$t('index.factory')" :rightText="productInfo.productVo.manufacturer" /> <uni-list-item :title="$t('index.interaction-mode')" :rightText="priceInfo.deliveryWayName" /> <uni-list-item :title="$t('index.delivery-area')" v-if="isLogin && warehouseKey !== -1 && priceInfo.deliverScope" :rightText="priceInfo.deliverScope" /> </uni-list> </view> <!-- 图片详情 --> <view class="img-content-box"> <view class="img-title">{{$t('index.product-detail')}}</view> <view class="img-box" v-for="(item, index) in productInfo.desImageUrl" :key="index"> <image class="img" :src="item" @click="onPreviewImage(index)"></image> </view> </view> </view> </scroll-view> <view class="product-bottom" :style="{paddingBottom: safeBottom+'px'}"> <view class="spec-content"> <scroll-view scroll-y="true" class="spec-scroll" :style="{height: 176*(productInfo.platformProductPriceVos.length==1?1:2)+20+'rpx'}"> <view v-for="(item,index) in productInfo.platformProductPriceVos" :key="index"> <view class="spec-item-bg" @tap="selectSpec(item)"> <view class="spec-item-warehouse"> <text>{{$t('index.warehouse-address')}}:{{item.warehouseName}}</text> </view> <view class="spec-item"> <view class="spec-item-image"> <image :src="item.imageUrl" /> </view> <view class="line-two"> <text>{{productInfo.productVo.name}}</text> </view> <view v-if="item.productTieredPricings.length>0"> <text>¥{{item.productTieredPricings[0].salePrice}}</text> </view> <view class="line-two"> <text>{{$t('index.inventory')}}{{inventory(item.availableCount)}}{{productInfo.productVo.unit}}</text> </view> <view> <step-price :item="item" :max="item.availableCount" :value="item.buyNum" @reduce="onNumberChange" @plus="onNumberChange" @input="onNumberChange"></step-price> </view> </view> </view> </view> </scroll-view> </view> <view class="fixed-content-box"> <view class="car-box" @click="onRouterPage"> <image class="icon" :src="web('icon_product_cart.png')"></image> <text class="number" v-if="carNumber">{{carNumber}}</text> </view> <view class="btn green" @click="addCart">{{$t('index.cart-add')}}</view> <view class="btn yellow" @click="buyNow">{{$t('index.buy-now')}}</view> </view> </view> <uni-popup ref="popup" type="bottom" :safeArea="false"> <view class="screen-box"> <view class="content-box"> <view class="choose-price"> <view class="price" :class="{'active': warehouseKey === index, 'disabled': item.disable}" v-for="(item, index) in productInfo.platformProductPriceVos" :key="index" @click="onChoseType(item,index)">{{item.deliveryWayName}} {{ item.warehouseName }} <text v-if="isCreditFacilities && item.businessModel === 'supplier'" style="margin-left: 3px">{{item.supplierName}}</text></view> </view> <view class="step-box"> <step-price ref="stepRef" :value="stepRefPriceValue" :disabled="isInputDisabled" :max="priceInfo.availableCount" @reduce="number=> onNumberChange(number)" @plus="number=> onNumberChange(number)" @input="number=> onNumberChange(number)" /> </view> </view> <view class="confirm-box"> <view class="btn reset" @click="addCart">{{$t('index.cart-add')}}</view> <view class="btn confirm" @click="buyNow">{{$t('index.buy-now')}}</view> </view> </view> </uni-popup> </view> </template> <script> import { getSearchById, getCartNumApi, addCartPost, getIsCreditUser, getShopInfo, collectShop } from '@/config/api.js' import stepPrice from '../../components/step-price/step-price.vue'; import { web } from '@/utils/util.js' export default { name: 'product', data() { return { isLogin: false, isCreditFacilities: false, // 是否授信用户 warehouseKey: -1, // 默认发货仓库 productInfo: { productVo: { name: '' }, platformProductPriceVos: [] }, // 详情 swiperList: [], // 轮播图集合 carNumber: 0, // 购物车数量 priceInfo: {}, // 价格对象 stepRefPriceValue: 1, platformType: '', shopInfo: null, isCollect: false, tags: [], bgColor: 'transparent', safeBottom: 0 } }, components: { stepPrice }, computed: { isIndicator() { return this.swiperList.length > 1 }, productModel() { if (this.productInfo.platformProductPriceVos && this.warehouseKey !== -1) { let item = this.productInfo.platformProductPriceVos[this.warehouseKey] if (this.isCreditFacilities && item.businessModel === 'supplier') { return item.deliveryWayName + ' ' + item.warehouseName + ' ' + item.supplierName } else { return item.deliveryWayName + ' ' + item.warehouseName } } else { return '' } }, isInputDisabled() { return this.warehouseKey === -1 } }, watch: { warehouseKey(newV) { this.priceInfo = this.productInfo.platformProductPriceVos[newV] this.stepRefPriceValue = 1 } }, methods: { web(name) { return web(name) }, handleScoll(event) { if (event.detail.scrollTop < 275) { this.bgColor = 'transparent' } else { this.bgColor = '#b60001' } }, back() { uni.navigateBack() }, async getProductDetail(id, platformType) { let { content } = await getSearchById({ id, platformType }) for (let i = 0; i < content.platformProductPriceVos?.length; i++) { let item = content.platformProductPriceVos[i]; item.buyNum = i == 0 ? 1 : 0 item['disable'] = item.availableCount <= 0 || item.isInvalid == 'T' } this.productInfo = content this.swiperList = content.imageUrl let validKey = content.platformProductPriceVos.findIndex(item => item.isDefaultPrice && item .isInvalid == 'F') if (validKey === -1) { this.priceInfo = this.productInfo.platformProductPriceVos[0] } else { this.warehouseKey = validKey } console.log(content) let params = { id: content.shopId, platformType: content.platformType, code: content.platformType == 'supplier' ? content.supplierCode : content.productVo.sellerCode } this.getShopInfo(params) }, async getShopInfo(params) { let { content } = await getShopInfo(params) this.shopInfo = content this.isCollect = this.shopInfo.followed let str = content.customerVo.supplierLabel || "" if (str != "") { let labels = JSON.parse(str) this.tags = [...labels] if (this.tags.length > 3) { this.tags.splice(3, this.tags.length - 3) } } }, onOpenPopup() { this.$refs.popup.open() }, inventory(count) { if (count < 10000) { return count } else { return (count / 1000).toFixed(1) + 'w' } }, onPreviewImage(index) { uni.previewImage({ current: index, urls: this.productInfo.desImageUrl }); }, onCartNum() { getCartNumApi().then(res => { this.carNumber = res.content }) }, onChoseType(item, index) { if (item.disable) { uni.showToast({ title: this.$t('index.msg-price-lose-efficacy'), icon: 'none' }); } else { this.warehouseKey = index } }, onRouterPage() { uni.switchTab({ url: '/pages/cart/index' }); }, addCart() { this.mixinCheckLogin().then(() => { let msg = null let list = []; this.productInfo.platformProductPriceVos.forEach(item => { if (item.buyNum != 0) { if (!item.price) { msg = this.$t('index.msg-price-expire') } list.push({ businessId: this.productInfo.id, count: item.buyNum, sellerCode: this.platformType == 'operator' ? item .platformCode : (item.platformType == 'seller' ? item .sellerCode : item.supplierCode), sellerName: this.platformType == 'operator' ? item .platformName : (item.platformType == 'seller' ? item .sellerName : item.supplierName), source: this.platformType == 'operator' ? "platformSelf" : ( item.platformType == 'seller' ? 'onSale' : 'supplierSale'), specsId: item.id }) } }) if (list.length == 0) { msg = this.$t('index.enter-product-num') } if (msg) { uni.showToast({ title: msg, icon: 'none' }); return } addCartPost({ cartList: list }).then((res) => { this.$refs.popup.close() setTimeout(() => { uni.showToast({ title: this.$t('index.operate-success') }); }, 30) }) }).catch(err => { uni.navigateTo({ url: `/pages/login/index?url=${encodeURIComponent(JSON.stringify('/pages/product/index?id=' + this.productInfo.id+'&paltformType='+this.productInfo.platformType))}` }); }) }, buyNow() { this.mixinCheckLogin().then(() => { let msg = null let list = []; let skuNums = [] this.productInfo.platformProductPriceVos.forEach(item => { if (item.buyNum != 0) { if (!item.price) { msg = this.$t('index.msg-price-expire') } list.push({ count: item.buyNum, specsId: item.id }) } skuNums.push(item.buyNum) }) if (list.length == 0) { msg = this.$t('index.enter-product-num') } if (msg) { uni.showToast({ title: msg, icon: 'none' }); return } uni.navigateTo({ url: `/pages/write/index?id=${this.productInfo.id}&platformType=${this.platformType}&platformId=${this.priceInfo.id}&list=` + JSON.stringify(list) }); }).catch(err => { uni.navigateTo({ url: `/pages/login/index?url=${encodeURIComponent(JSON.stringify('/pages/product/index?id=' + this.productInfo.id +'&platformType='+this.productInfo.platformType))}` }); }) }, onIsCreditUser() { getIsCreditUser().then(res => { this.isCreditFacilities = res.content; }) }, onNumberChange(count, item) { item.buyNum = Math.floor(Number(count)) this.productInfo.platformProductPriceVos.forEach(product => { if (item.id == product.id) { product.buyNum = count } }) }, selectSpec(item) { this.priceInfo = item }, enterShop() { let code = this.productInfo.platformType == 'supplier' ? this.productInfo.supplierCode : this.productInfo .productVo .sellerCode uni.navigateTo({ url: `/pages/shop/shop?id=${this.shopInfo.id}&platformType=${this.productInfo.platformType}&code=${code}` }) }, collectShop() { let _this = this collectShop(this.shopInfo.id, this.isCollect).then(res => { _this.isCollect = !_this.isCollect }) } }, onLoad(options) { this.getProductDetail(options.id, options.platformType) this.onCartNum() this.platformType = options.platformType const value = uni.getStorageSync('token'); this.isLogin = !!value if (this.isLogin) { this.onIsCreditUser() } const { safeAreaInsets } = uni.getSystemInfoSync() this.safeBottom = safeAreaInsets.bottom } } </script> <style lang="scss" scoped> .container { height: 100vh; background-color: #F5F5F6; } .product-scroll { overflow: auto; top: 0; right: 0; left: 0; position: fixed; } .swiper-content-box { width: 100%; height: 750rpx; .swiper, .img { width: 100%; height: 100%; } } .name-price-box { background-color: #fff; padding: 50rpx 30rpx 30rpx 30rpx; .box { width: 100%; display: flex; align-items: center; .name { font-size: 36rpx; color: #222; } .price { margin-top: 30rpx; color: #DE0000; font-size: 44rpx; font-weight: bold; .unit { font-size: 28rpx; font-weight: medium; } } .taxRate { margin-top: 36rpx; margin-left: 30rpx; font-size: 12px; color: #91642A; } } } .price-bg { display: flex; flex-direction: row; align-items: center; justify-content: space-between; color: #99A0AF; margin: 30rpx 0; background: #f6f7f8; padding: 20rpx 20rpx; border-radius: 8rpx; line-height: 50rpx; } .price-bg-item { display: flex; flex-direction: column; } .shop { margin-top: 20rpx; padding: 20rpx 20rpx; display: flex; flex-direction: row; align-items: center; height: 200rpx; background: white; .cover { width: 160rpx; height: 160rpx; overflow: hidden; flex-shrink: 0; background: #f6f7f8; image { height: 100%; width: 100%; } } .info { display: flex; flex-grow: 1; flex-direction: column; padding-left: 20rpx; overflow: hidden; .name-row { display: flex; height: 60rpx; justify-content: space-between; .name { font-weight: 500; color: #B60001; } } .desc { display: flex; overflow: hidden; height: 100rpx; flex-wrap: wrap; -webkit-line-clamp: 2; overflow: hidden; .tag { display: flex; white-space: nowrap; padding: 4rpx 16rpx; background: #f6f7f8; margin-right: 10rpx; margin-top: 38rpx; color: white; background: linear-gradient(90deg, #5DCFD0 0%, #3477DB 100%); } } } .btn { width: 140rpx; margin-top: 10rpx; display: flex; flex-direction: column; justify-content: space-between; margin-left: 20rpx; flex-shrink: 0; line-height: 70rpx; .collect { background: #f60001; display: flex; align-items: center; justify-content: center; border-radius: 10rpx; color: white; } .enter { margin-top: 20rpx; background: #f6f7f8; display: flex; align-items: center; justify-content: center; border-radius: 10rpx; border: 1rpx solid #f60001; } } } .list-box { margin-top: 20rpx; } .img-content-box { margin-top: 20rpx; background-color: #fff; padding: 0 15px; overflow: hidden; .img-title { line-height: 100rpx; color: #B60001; font-size: 28rpx; } .img-box { .img { width: 100%; } } } .product-bottom { position: fixed; width: 100%; left: 0; bottom: 0; background: white; } .fixed-content-box { width: 100%; height: 100rpx; left: 0; bottom: 0; display: flex; .car-box { flex: auto; height: 100rpx; display: flex; flex-direction: column; justify-content: center; align-items: center; background-color: #fff; position: relative; .icon { width: 60rpx; height: 60rpx; } .text { font-size: 12px; color: #999; } .number { position: absolute; left: 55%; top: 5px; width: 16px; height: 16px; border-radius: 8px; background-color: #B60001; padding: 0 10rpx; color: #fff; text-align: center; line-height: 16px; font-size: 12px; } } .btn { flex: none; width: 240rpx; height: 100rpx; text-align: center; line-height: 100rpx; color: #fff; font-size: 28rpx; &.green { background-color: #086DCA; } &.yellow { background-color: #B60001; } } } .screen-box { background-color: #fff; padding: 30rpx 60rpx; border-top-left-radius: 15px; border-top-right-radius: 15px; position: relative; .content-box { max-height: 70vh; overflow-y: auto; padding-bottom: 50rpx; .choose-price { display: flex; flex-wrap: wrap; justify-content: center; .price { box-sizing: border-box; width: 500rpx; padding: 10rpx 0; border: 1rpx solid #dcdfe6; border-radius: 3px; color: #2c3e50; font-size: 12px; text-align: center; &.active { border-color: #409eff; background-color: #409eff; color: #fff; } &.disabled { border-color: #f8f8f8; background-color: #f8f8f8; color: #999; } &+.price { margin-top: 20rpx; } } } .step-box { margin-top: 30rpx; text-align: center; /deep/ .stepper-box { width: 500rpx; .uni-input { width: 500rpx; } } } } .confirm-box { display: flex; justify-content: space-between; font-size: 28rpx; color: #FFFFFF; .btn { width: 40%; height: 80rpx; line-height: 80rpx; text-align: center; &.reset { background-color: #1ABC9C; border-radius: 40rpx; } &.confirm { background-color: #ffa64d; border-radius: 40rpx; } } } } .spec-content { background: white; border-top: 1px solid #f2f2f2; } .spec-scroll { height: 176rpx; } .spec-item-bg { padding: 0 30rpx; } .spec-item-warehouse { height: 88rpx; line-height: 88rpx; color: #222; font-size: 28rpx; } .line-two { margin-left: 10rpx; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; text-overflow: ellipsis; max-width: 140rpx; } .spec-item { height: 88rpx; font-size: 26rpx; color: #666; display: flex; align-items: center; justify-content: space-between; } .spec-item-image { height: 84rpx; width: 84rpx; flex-shrink: 0; } .spec-item-image>image { width: 100%; height: 100%; } </style>