Files
frontend-app/pages/other_package/productInfo/productInfo.vue
T
2026-09-23 10:29:41 +08:00

2871 lines
92 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="product_info_warp">
<!-- <image v-if="tmpImg" :src="tmpImg" style="width:620rpx;height:960rpx"></image> -->
<!-- 头部产品描述图 -->
<view class="product_info_header_warp" :style="{ height: swiperHeight + 'rpx' }">
<up-swiper v-if="swiperList && swiperList.length > 0" :key="swiperHeight + '_' + swiperList.length"
:list="swiperList" class="product_info_header_swiper" @click="onSwiper"
@change="(e) => (currentNum = e.current)" :height="swiperHeight + 'rpx'">
<template #indicator>
<view class="indicator-num">
<text class="indicator-num__text">{{ currentNum + 1 }}/{{ swiperList.length }}</text>
<qiaobao-assistant page-key="pages/other_package/productInfo/productInfo" title="商品详情" />
</view>
</template>
</up-swiper>
<view class="img_comm left_warp" @click="jumpLeft">
<up-image src="/static/common/left_b_st.png" width="30" height="30" bgColor="#f1f6ff00"
v-if="!isShareUrl || isGroupProduct" @click="jumpLeft"></up-image>
</view>
<view class="img_comm right_warp">
<up-image v-if="!isShareUrl || isGroupProduct" src="/static/common/share_st.png" width="30" height="30"
bgColor="#f1f6ff00" @click="jumpShare"></up-image>
</view>
<!-- 拼团专属商品卡片 (悬浮在轮播图上) -->
<view class="group_product_card" v-if="isGroupProduct">
<!-- 顶部拼团价 + 倒计时横条 -->
<view class="group_price_bar">
<view class="group_price_left">
<text class="group_price_label">拼团价</text>
<text class="group_price_symbol">¥</text>
<text class="group_price_int">{{ orderInfo.groupPrice }}</text>
<text class="group_price_origin" v-if="groupOriginPrice">¥{{ groupOriginPrice }}</text>
</view>
<view class="group_price_right">
<text class="group_remaining_text">只剩 {{ groupCountdownText }}</text>
</view>
</view>
<!-- 卡片内容区域:标题 + 贡献值 + 销量 -->
<view class="group_card_body">
<view class="content_data_title">
{{ orderInfo.name }}
</view>
<view class="group_series_row">
<view class="group_series_left">
<view class="group_contribution_pill" v-if="orderInfo.contribution">
<image class="contribution_icon" src="https://static.tbmall.xin/static/mine-purse/vcoin_1.png"
mode="aspectFit"></image>
<text class="contribution_text">下单送{{ orderInfo.contribution }}贡献值</text>
</view>
<image src="/pages/rwa_package/static/tips.png" class="group_tips_icon" mode="aspectFit"
@click="onContributionTip"></image>
</view>
<view class="group_sales_count">全网销量{{ limit999(orderInfo.sales) }}</view>
</view>
</view>
</view>
</view>
<!-- 数据详情 -->
<view class="product_content_warp" :class="{ 'group_content_warp': isGroupProduct }">
<!-- 普通商品标题,系列 (非拼团时展示) -->
<view class="content_data" v-if="!isGroupProduct">
<view style="
display: flex;
align-items: center;
justify-content: space-between;
">
<!-- 优惠券 -->
<view class="content_data_price coupon_price_box" v-if="orderInfo.couponPrice">
<text class="coupon_label">券后</text>
<text class="coupon_symbol">¥</text>
<text class="coupon_int">{{ String(orderInfo.couponPrice).split('.')[0] }}</text>
<text class="coupon_dec" v-if="String(orderInfo.couponPrice).split('.')[1]">.{{
String(orderInfo.couponPrice).split('.')[1] }}</text>
<text class="coupon_split">|</text>
<text class="coupon_origin">优惠前¥<template v-if="orderInfo.minPrice && orderInfo.maxPrice">{{
orderInfo.minPrice }} - {{ orderInfo.maxPrice }}</template><template v-else>{{ orderInfo.minPrice
}}</template></text>
</view>
<!-- 数字积分 -->
<view class="content_data_price buy_deduction" v-else-if="orderInfo.buyDeductionValue">
<view class="buy_deduction_value">
<text class="text-38 text-bold mr-10">{{ orderInfo.buyDeductionValue }}数字积分</text>
<text class="text-24">+{{ orderInfo.minPrice }}元</text>
</view>
<view class="buy_deduction_label">
兑后数字积分不退
</view>
</view>
<!-- 正常 -->
<view class="content_data_price" v-else>
<text>¥</text>
<text style="font-size: 60rpx" v-if="orderInfo.minPrice && orderInfo.maxPrice">{{ orderInfo.minPrice }} -
{{
orderInfo.maxPrice }}</text>
<text style="font-size: 60rpx" v-else>{{
orderInfo.minPrice
}}</text>
<text class="price_o">¥{{ orderInfo.adPrice }}</text>
</view>
<view class="text-24" style="color: #ff5a5a; height: 38rpx; display: flex; align-items: center; gap: 5rpx"
v-if="orderInfo?.goodsTagList">
<image v-for="val in orderInfo.goodsTagList" :key="val" :src="val.icon" mode="heightFix"
style="height: 38rpx">
</image>
</view>
</view>
<view class="content_data_title">
{{ orderInfo.name }}
</view>
<view class="content_data_series">
<!-- 暂时隐藏 -->
<view class="content_data_series_tag" v-if="orderInfo.contribution" @click="onContributionTip">
<image class="uni-image" src="https://static.tbmall.xin/static/mine-purse/vcoin_1.png"></image>
下单预计获得{{ orderInfo.contribution }}贡献值
<image src="/pages/rwa_package/static/tips.png" class="uni-image" style="margin-left: 5rpx;"></image>
</view>
<!-- 销量还需要改 -->
<view class="sales_count">全网销量{{ limit999(orderInfo.sales) }}件</view>
</view>
</view>
<!-- 具体规格 -->
<view class="content_concrete">
<view class="concrete_item concrete_item_but" @click="checkSpecification(true, 'n')">
<view class="concrete_item_">
<view class="concrete_item_title">规格</view>
<view class="concrete_item_value">
{{ selectSpecificationTempl.combTypes || "暂未选择规格" }}
</view>
</view>
<up-image src="/static/common/right.png" width="20" height="20" bgColor="#f1f6ff00"></up-image>
</view>
<!-- 领券栏 (图一高保真) -->
<view class="concrete_item concrete_item_but" v-if="goodsCouponList && goodsCouponList.length > 0"
@click="couponPopupShow = true">
<view class="concrete_item_">
<view class="concrete_item_title">领券</view>
<view class="concrete_item_value coupon_tags_preview">
<text v-for="(cItem, cIdx) in goodsCouponList.slice(0, 3)" :key="cItem.templateId || cIdx"
class="preview_coupon_tag">
{{ cItem.title || '优惠券' }}
</text>
</view>
</view>
<up-image src="/static/common/right.png" width="20" height="20" bgColor="#f1f6ff00"></up-image>
</view>
<!-- <view class="concrete_item">
<view class="concrete_item_">
<view class="concrete_item_title">收货地址</view>
<view class="concrete_item_value" @click="jumpAddAddress">
<view class="address_warp" v-if="!adressInfo">
<up-image src="/static/common/address2.png" width="10" height="10" bgColor="#f1f6ff00"></up-image>
<view style="margin-left: 10rpx">请填写地址</view>
</view>
<view class="address_warp" v-else>{{ adressInfo }}</view>
</view>
</view>
</view> -->
<view class="concrete_item" v-if="adressInfo">
<view class="concrete_item_">
<view class="concrete_item_title">运费</view>
<view class="concrete_item_value">
<view class="address_warp">
<!-- <view style="margin-left: 10rpx">包邮</view> -->
<view style="margin-left: 10rpx">{{
postage ? `¥${postage}元` : "包邮"
}}</view>
</view>
</view>
</view>
</view>
<view class="concrete_item">
<view class="concrete_item_">
<view class="concrete_item_title">购物保障</view>
<view class="concrete_item_value concrete_item_bz">
<view class="concrete_item_bz concrete_item_bz_item">
<up-image src="/pages/other_package/static/product/product_b.png" width="10" height="12"
bgColor="#f1f6ff00"></up-image>
<view class="concrete_item_bz_item_left">品质严保</view>
</view>
<view class="concrete_item_bz">
<up-image src="/pages/other_package/static/product/product_js.png" width="10" height="10"
bgColor="#f1f6ff00"></up-image>
<view class="concrete_item_bz_item_left">极速退款</view>
</view>
</view>
</view>
<!-- <up-image src="/static/common/right.png" width="20" height="20" bgColor="#f1f6ff00"></up-image> -->
</view>
</view>
<!-- 拼团玩法与可直接参与的拼团 -->
<product-group-rules :isGroupProduct="isGroupProduct" :displayGroupTeams="displayGroupTeams"
@openJoinablePopup="openJoinablePopup" @joinTeam="onJoinTeam" />
<Evaluation @goMore="onGoMore" :evalId="id"></Evaluation>
<view class="tui-view">
<text class="text-32 text-zu">退货提示</text>
<text class="text-28 text-fu1">拆封后影响品质”或“影响二次销售的”不予退货。</text>
<view class="tui-list">
<view class="tui-box" :key="index" v-for="(item, index) in tuiList">
<image :src="`https://static.tbmall.xin/static/new/tui_${index}.png`"></image>
<text class="text-24 text-gray">{{ item }}</text>
</view>
<!-- tuiList -->
</view>
</view>
<!-- 商品详情 -->
<view class="content_details">
<view class="content_details_title">商品详情</view>
<view v-html="detailRich" class="content_details_content"></view>
</view>
</view>
<!-- 底部编辑按钮 -->
<view class="product_but_warp">
<view class="product_but_left">
<view class="left_item">
<up-image src="/pages/other_package/static/product/home_1.png" width="20" height="20" bgColor="#f1f6ff00"
@click="jumpOther('home')"></up-image>
<view>首页</view>
</view>
<view class="left_item">
<up-image src="/pages/other_package/static/product/kefu.png" width="20" height="20" bgColor="#f1f6ff00"
@click="onZiXun"></up-image>
<view>咨询</view>
</view>
<view class="left_item">
<up-image src="/pages/other_package/static/product/cart.png" width="24" height="20" bgColor="#f1f6ff00"
@click="jumpOther('cart')"></up-image>
<view>购物车</view>
</view>
</view>
<view class="product_but_right" v-if="!isShareUrl || isGroupProduct">
<!-- 拼团专属操作按钮 (高保真还原) -->
<template v-if="isGroupProduct">
<view class="but_comm join_warp" @click="checkSpecification(true, 'buy')">
单独购买
</view>
<view class="but_comm buy_warp" @click="checkSpecification(true, 'groupBuy')">
发起拼团
</view>
</template>
<!-- 普通/秒杀商品按钮 -->
<template v-else>
<view v-if="!isShowCart && !isCoupon" class="but_comm join_warp" @click="checkSpecification(true, 'addCart')">
加入购物车
</view>
<!-- 限时秒杀现在要改为展示购物车icon 并且立即购买按钮变长一点 -->
<view class="but_comm buy_warp" :class="{ 'border-16': isShowCart || isCoupon }"
:style="isShowCart ? 'width: 360rpx' : ''" @click="checkSpecification(true, 'buy')">
立即购买
</view>
</template>
</view>
<MyBtn v-if="isShareUrl && !isGroupProduct" mh="88rpx" text="打开APP" wd="200rpx" @ok="onOpenApp"></MyBtn>
</view>
<!-- 选择规格的弹框 -->
<up-popup :show="specificationShow" :round="10" :closeOnClickOverlay="false" :closeable="true"
@close="specificationShow = false" mode="bottom">
<view class="specification_warp">
<!-- 选择地址 -->
<view class="specification_adress_warp">
<view class="specification_adress_msg">10+刚刚抢购</view>
<!-- <view class="specification_adress_arr">
<up-image src="/static/common/address_st.png" width="30" height="30" bgColor="#f1f6ff00"></up-image>
<view class="address_title address_title_sel" v-if="orderInfo && orderInfo.addressObj">
<text style="font-size: 30rpx;margin-right: 20rpx;">{{orderInfo.addressObj.name}}</text>
<text>{{orderInfo.addressObj.address}}</text>
</view>
<view class="address_title" v-else>暂无收货地址,请添加收货地址!</view>
<view><up-image src="/static/common/right.png" width="16" height="16" bgColor="#f1f6ff00"></up-image></view>
</view> -->
</view>
<!-- 商品 -->
<view class="specification_product_warp">
<up-image :src="skuBySpecInfo.skuImage" width="100" height="100" bgColor="#f1f6ff00"></up-image>
<view class="product_content">
<view class="price_warp">
<!-- 优惠券价格 -->
<view class="price_content_warp coupon_price_box"
v-if="(selectSpecificationTempl && selectSpecificationTempl.couponPrice) || orderInfo.couponPrice">
<text class="coupon_label">券后</text>
<text class="coupon_symbol">¥</text>
<text class="coupon_int">{{ String((selectSpecificationTempl && selectSpecificationTempl.couponPrice) ||
orderInfo.couponPrice).split('.')[0] }}</text>
<text class="coupon_dec"
v-if="String((selectSpecificationTempl && selectSpecificationTempl.couponPrice) || orderInfo.couponPrice).split('.')[1]">.{{
String((selectSpecificationTempl && selectSpecificationTempl.couponPrice) ||
orderInfo.couponPrice).split('.')[1] }}</text>
<text class="coupon_origin">优惠前¥<template
v-if="selectSpecificationTempl && selectSpecificationTempl.price">{{ selectSpecificationTempl.price
}}</template><template v-else-if="skuBySpecInfo.originalPrice">{{ skuBySpecInfo.originalPrice
}}<template v-if="skuBySpecInfo.price">- {{ skuBySpecInfo.price }}</template></template><template
v-else>{{ orderInfo.minPrice }}</template></text>
</view>
<!-- 数字积分 -->
<view class="price_content_warp" v-else-if="isCoupon">
<text class="text-38 text-bold mr-10">{{ orderInfo.buyDeductionValue }}数字积分</text>
<text class="text-24" v-if="
selectSpecificationTempl &&
Object.keys(selectSpecificationTempl).length !== 0">+{{ selectSpecificationTempl.price }}元</text>
<text v-else>+{{ orderInfo.minPrice }}元</text>
</view>
<!-- 拼团价格 -->
<view class="price_content_warp"
v-else-if="isGroupProduct && (specificationShowType === 'groupBuy' || specificationShowType === 'joinGroup')">
<view style="font-size: 28rpx; color: #7A35F6; font-weight: bold; margin-right: 4rpx;">拼团价¥</view>
<text style="font-size: 50rpx; color: #7A35F6; font-weight: bold;">
{{ (selectSpecificationTempl && selectSpecificationTempl.groupPrice) ?
selectSpecificationTempl.groupPrice
: orderInfo.groupPrice }}
</text>
<view class="price_content_coupon_warp"
style="background: #F3EBFE; color: #7A35F6; margin-left: 10rpx;">
{{ orderInfo.groupSize || 2 }}人团
</view>
</view>
<!-- 正常价格 -->
<view class="price_content_warp" v-else>
<view>¥</view>
<text style="font-size: 50rpx" v-if="
selectSpecificationTempl &&
Object.keys(selectSpecificationTempl).length !== 0
">
{{ selectSpecificationTempl.price }}
</text>
<view style="font-size: 50rpx" v-else>
{{ skuBySpecInfo.originalPrice }}
<text v-if="skuBySpecInfo.price">- {{ skuBySpecInfo.price }}</text>
</view>
<view class="price_content_coupon_warp" v-if="orderInfo.couponPrice && orderInfo.couponPrice !== 0">
优惠券{{
orderInfo.couponPrice }}</view>
</view>
<view class="oOrice_content_warp"
v-if="!isCoupon && !((selectSpecificationTempl && selectSpecificationTempl.couponPrice) || orderInfo.couponPrice)">
¥{{ skuBySpecInfo.originalPrice }}</view>
<view class="coupon_content_warp" v-if="orderInfo.couponPrice && orderInfo.couponPrice !== 0">
优惠券已优惠¥{{
orderInfo.couponPrice }}</view>
<view class="coupon_content_stock">
<view>库存</view>
<view>{{ skuBySpecInfo.stock || 0 }}件</view>
</view>
</view>
<view class="inventory_warp">
<!-- <view>库存</view>
<view>{{ skuBySpecInfo.stock || 0 }}件</view> -->
<view>已选规格: {{ skuBySpecInfo.skuName }}</view>
</view>
</view>
</view>
<!-- 规格 -->
<view class="specification_list_warp">
<view v-for="(item, index) in specificationList" :key="item.id" class="list_warp">
<view class="list_title_warp">{{ item.type }} <text></text></view>
<view class="specification_item_warp">
<view :class="oItem.disabled || oItem.inventory === 0
? 'item_Warp item_Warp_no_warp'
: item.selectVal === oItem.n
? 'item_Warp item_Warp_yes_warp'
: 'item_Warp'
" v-for="(oItem, oIndex) in item.items" :key="oItem.id || oItem.n"
@click="clickSpecification(index, item, oItem)">
{{ oItem.n }}
</view>
</view>
</view>
</view>
<!-- 购买数量 -->
<view class="specification_but">
<view class="specification_buy">
<view>数量</view>
<view class="shopping_info_quantity">
<view class="quantity_but" @click="quantityFun('minus')">-</view>
<view class="quantity_value">{{ orderInfo.quantity }}</view>
<view class="quantity_but" @click="quantityFun('add')">+</view>
</view>
</view>
<view v-if="specificationShowType === 'groupBuy'" class="specification_buy_but group_popup_buy_btn"
@click="addCart(true)">确定发起拼团</view>
<view v-else-if="specificationShowType === 'joinGroup'" class="specification_buy_but group_popup_buy_btn"
@click="addCart(true)">确定参与拼团</view>
<view v-else-if="specificationShowType === 'buy'" class="specification_buy_but" @click="addCart(true)">去支付
</view>
<view v-else-if="specificationShowType === 'addCart'" class="specification_buy_but" @click="addCart(false)">
加入购物车
</view>
<view v-else class="product_but_right popup_buts">
<view class="but_comm join_warp" @click="addCart(false)">加入购物车</view>
<view class="but_comm buy_warp" @click="addCart(true)">立即购买</view>
</view>
</view>
</view>
</up-popup>
<!-- 我的客服 -->
<up-modal :show="serviceShow" :showConfirmButton="false">
<view class="slot-content">
<view class="close_warp" @click="serviceShow = false">
<up-image src="/static/common/close.png" width="20" height="20" bgColor="#f1f6ff00"></up-image>
</view>
<view class="content_">
<view class="content_1">{{
source === "supply_chain" ? "云仓客服" : "平台客服"
}}</view>
<view class="content_3">{{
source === "supply_chain"
? CLOUD_SERVICE_PHONE_NUMBER
: CUSTOMER_SERVICE_PHONE_NUMBER
}}</view>
</view>
<view class="but_">
<view class="but_copy" @click="
copyMobile(
source === 'supply_chain'
? CLOUD_SERVICE_PHONE_NUMBER
: CUSTOMER_SERVICE_PHONE_NUMBER
)
">复制号码</view>
<view class="but_close" @click="serviceShow = false">取消</view>
</view>
</view>
</up-modal>
<!-- 暂未开放 -->
<up-overlay :show="openShow">
<view class="up_content open_Show" @tap.stop>
<view class="uni-banner">
<view class="banner_box">
<view class="title_">温馨提示</view>
<view class="msg_">该商家暂未开通供应链权限,请联系供应链客服开通授权。</view>
<view class="phone_">{{ CLOUD_SERVICE_PHONE_NUMBER }}</view>
<view class="but_">
<view class="but_b" @click="copyMobile(CLOUD_SERVICE_PHONE_NUMBER)">立即联系</view>
</view>
</view>
</view>
</view>
</up-overlay>
<!-- 选择地址 -->
<up-popup v-model:show="addressShow" :round="10" :closeOnClickOverlay="false" :closeable="true"
:safeAreaInsetBottom="false" @close="checkAddressShow(false, null, 0, 0)">
<view class="address_popup_warp">
<view class="address_popup_title">收件地址</view>
<view class="address_popup_content">
<view class="address_popup_content_add" @click="addAdress">
+ 新增地址</view>
<Address @valueFunc="getAddressValueFunc" />
</view>
<view class="popup_but" @click="addressOk"> 确认 </view>
</view>
</up-popup>
<SharePopup ref="share" v-if="shareOpen" :show="shareShow" @close="onClose" @wxShare="onWxShare" @share="onShare"
:id="id" :text="orderInfo.name ? orderInfo.name : ''" :tu="swiperList.length && swiperList[0]"
:price="isGroupProduct ? String(groupActivityInfo?.groupPrice || orderInfo.price) : String(orderInfo.price || '')"
:groupPrice="groupActivityInfo?.groupPrice ? String(groupActivityInfo.groupPrice) : ''"
:requiredNum="groupActivityInfo?.requiredNum || 2"
:sharePath="isGroupProduct ? `pages/other_package/productInfo/productInfo?id=${id}&activedType=groupBuy&activityId=1` : ''">
</SharePopup>
<EvaluateDialog ref="evaluateDialog" v-if="evaluateDialogShow" :show="evaluateDialogShow"
@close="evaluateDialogShow = false" :evalId="id" :mainGraph="orderInfo.mainGraph" :name="orderInfo.name">
</EvaluateDialog>
<!-- 优惠券弹窗 -->
<product-coupon-popup :show="couponPopupShow" :list="goodsCouponList" @update:show="couponPopupShow = $event"
@receive="({ coupon, index }) => onReceiveGoodsCoupon(coupon, index)" />
<!-- 贡献值说明弹窗 -->
<product-contribution-dialog :show="contributionTipShow" @update:show="contributionTipShow = $event" />
<!-- 可直接参与的拼团弹窗 -->
<product-joinable-popup :show="joinablePopupShow" :list="displayGroupTeams"
@update:show="joinablePopupShow = $event" @join="onJoinTeamFromPopup" />
<view class="hideCanvasView">
<canvas id="myCanvas" canvas-id="myCanvas" :style="{
height: bgObj.height + 'px',
width: bgObj.width + 'px',
}"></canvas>
</view>
</view>
</template>
<script>
import { getGoodsDetail, addGoods, getGoodsSpecs, getGoodsSpecsInfo, getGroupBuyJoinableList, getGroupBuyCurrent } from "@/api/product.js";
import { getGoodsCouponList, receiveCoupon } from "@/api/coupon.js";
import { getStorageFun, TOKEN_NAME } from "@/utils/auth.js";
import { getValue, PRODUCT_DELIVERY_TIME, ACTIVITY_TYPE } from "@/utils/enumUtils.js";
import { copyData } from "@/utils/index.js";
import Evaluation from "@/pages/other_package/productInfo/evaluation.vue";
import EvaluateDialog from "@/pages/other_package/productInfo/evaluate-dialog.vue";
import SharePopup from "@/pages/other_package/components/share-popup.vue";
import MyBtn from "@/components/common/my-btn.vue";
import { getPostageQuery } from "@/api/cart.js";
import { assembleAddress } from "@/utils/index.js";
import QSCanvas from "qs-canvas";
import func from "@/utils/func.js";
import Address from "@/components/address.vue";
import ProductCouponPopup from "./components/product-coupon-popup.vue";
import ProductJoinablePopup from "./components/product-joinable-popup.vue";
import ProductContributionDialog from "./components/product-contribution-dialog.vue";
import ProductGroupRules from "./components/product-group-rules.vue";
import {
CUSTOMER_SERVICE_PHONE_NUMBER,
CLOUD_SERVICE_PHONE_NUMBER,
SHARE_URL,
MINI_PROGRAM_TYPE,
} from "@/utils/config.js";
let qsc = null;
let timer = null;
export default {
computed: {
PRODUCT_DELIVERY_TIME() {
return PRODUCT_DELIVERY_TIME;
},
CUSTOMER_SERVICE_PHONE_NUMBER() {
return CUSTOMER_SERVICE_PHONE_NUMBER;
},
CLOUD_SERVICE_PHONE_NUMBER() {
return CLOUD_SERVICE_PHONE_NUMBER;
},
isGroupProduct() {
return Boolean(
this.orderInfo &&
this.orderInfo.groupPrice !== undefined &&
this.orderInfo.groupPrice !== null &&
this.orderInfo.groupPrice !== ""
);
},
groupOriginPrice() {
if (!this.orderInfo) return "";
return (
this.orderInfo.originalPrice ||
this.orderInfo.salePrice ||
this.orderInfo.adPrice ||
this.orderInfo.minPrice ||
""
);
},
displayGroupTeams() {
// 优先使用接口返回的 joinableTeamList 数据
if (Array.isArray(this.joinableTeamList) && this.joinableTeamList.length > 0) {
return this.joinableTeamList.map((item, idx) => ({
id: item.teamId || idx,
teamId: item.teamId,
teamNo: item.teamNo,
nickname: item.leaderName || "拼团团长",
avatar1: item.leaderAvatar || "https://static.tbmall.xin/static/new-home/home_3.png",
avatar2: "https://static.tbmall.xin/static/mine-purse/vcoin_1.png",
countdownText: this.formatTeamCountdown(item),
remainingNum: item.remainingNum,
currentNum: item.currentNum,
requiredNum: item.requiredNum,
virtualLeader: item.virtualLeader,
...item
}));
}
// fallback: orderInfo 自带的 groupTeamList
if (
this.orderInfo &&
Array.isArray(this.orderInfo.groupTeamList) &&
this.orderInfo.groupTeamList.length > 0
) {
return this.orderInfo.groupTeamList.map((item, idx) => ({
id: item.id || item.teamId || idx,
nickname: item.leaderName || item.nickname || item.userName || "拼团团长",
avatar1: item.leaderAvatar || item.avatar1 || item.userAvatar || "https://static.tbmall.xin/static/new-home/home_3.png",
avatar2: item.avatar2 || "https://static.tbmall.xin/static/mine-purse/vcoin_1.png",
countdownText: this.formatTeamCountdown(item),
...item
}));
}
return [];
},
},
components: {
Address,
SharePopup,
Evaluation,
EvaluateDialog,
MyBtn,
ProductCouponPopup,
ProductJoinablePopup,
ProductContributionDialog,
ProductGroupRules,
},
data() {
return {
id: 0,
shareShow: false,
addressShow: false, // 收件地址选择
couponPopupShow: false, // 优惠券弹框显示 (图二)
contributionTipShow: false, // 贡献值说明弹框显示
joinablePopupShow: false, // 可直接参加的拼团弹框
joinableTeamList: [], // 可直接参加的拼团列表
goodsCouponList: [], // 商品可用优惠券列表
evaluateDialogShow: false, // 评价弹框显示
shareOpen: false,
tuiList: ["破损", "污渍", "划痕", "标签", "其他"],
swiperList: [],
currentNum: 0,
swiperHeight: 800,
specificationShow: false,
specificationShowType: "", // 弹框类型
orderInfo: {},
specificationList: [], //规格
selectSpecification: [], // 选择的规格
specificationTempl: [], // 规格模版数据
selectSpecificationTempl: {}, // 选择规格模版数据
selectedSpecValueIds: [],
skuBySpecKey: {},
serviceShow: false, // 联系客服弹框
openShow: false,
source: null, // 来源:供应链,自营
isShowCart: 0,
selectAddress: {}, // 选择的地址
postage: null, // 邮费
adressInfo: "",
urlPath: "",
posterImage: "",
canvasId: "default_PosterCanvasId",
poster: "",
bgObj: {
height: 480,
width: 310,
},
draw: null,
tmpImg: "",
isShareUrl: false,
isSplash: false,
isCoupon: false, // 是否是优惠券商品
curAmount: 0, // 当前人剩余优惠券金额
activedType: "", // 活动类型
activityId: "", // 秒杀活动ID
skuBySpecInfo: {},
detailRich: "",
groupTimer: null,
groupCountdownSeconds: 0,
groupCountdownText: "00:00:00",
groupActivityInfo: null,
currentJoinTeam: null,
fromBuyAlone: false,
};
},
onBackPress(options) {
console.log("productInfo onBackPress, fromBuyAlone:", this.fromBuyAlone, options);
if (this.fromBuyAlone) {
this.fromBuyAlone = false;
uni.reLaunch({
url: "/pages/active/group-buying/group-buying?fromBuyAlone=1",
fail: () => {
uni.redirectTo({
url: "/pages/active/group-buying/group-buying?fromBuyAlone=1",
});
},
});
return true;
}
// #ifdef APP-PLUS
if (options.from === 'backbutton' && this.isSplash) {
plus.webview.currentWebview().canBack = false;
uni.reLaunch({ url: '/pages/home/home' });
return true;
}
// #endif
},
onLoad(options) {
// #ifdef MP-WEIXIN
// 微信扫描普通链接二维码打开小程序时,微信会将二维码完整 URL 进行 urlencode 放入 options.q 中
if (options && options.q) {
try {
const qrUrl = decodeURIComponent(options.q);
console.log("扫码打开小程序 options.q:", qrUrl);
const queryIndex = qrUrl.indexOf("?");
if (queryIndex !== -1) {
const queryString = qrUrl.slice(queryIndex + 1);
const pairs = queryString.split("&");
pairs.forEach((pair) => {
if (!pair) return;
const [k, v] = pair.split("=");
if (k) {
options[decodeURIComponent(k)] = v ? decodeURIComponent(v) : "";
}
});
}
} catch (e) {
console.error("解析二维码参数 options.q 失败:", e);
}
}
// #endif
if (options.fromBuyAlone || options.backToGroupBuying) {
this.fromBuyAlone = true;
}
const id = options.id;
this.activedType = options.activedType;
this.activityId = options.activityId;
this.isCoupon = options.isPar;
if (this.isCoupon && options.curAmount) {
this.curAmount = options.curAmount;
}
const source = options.source;
this.isSplash = options.isSplash;
console.log("isSplash", this.isSplash == "true");
if (id && id !== 0) {
this.id = id ? parseInt(id, 10) : 0;
if (source && source === "supply_chain") {
this.source = source;
this.getSupplyChainById();
} else {
this.getById();
}
}
// #ifdef H5
let url = window.location.href;
if (url.indexOf(SHARE_URL) != -1) {
if (this.activedType !== 'groupBuy' && options.activedType !== 'groupBuy') {
this.isShareUrl = true;
}
}
console.log("url", url, SHARE_URL);
// #endif
qsc = new QSCanvas(
{
canvasId: "myCanvas",
setCanvasWH: (bg) => {
this.bgObj = bg;
},
},
this
);
},
onHide() {
timer && clearTimeout(timer);
timer = null;
if (this.groupTimer) {
clearInterval(this.groupTimer);
this.groupTimer = null;
}
},
onUnload() {
timer && clearTimeout(timer);
timer = null;
if (this.groupTimer) {
clearInterval(this.groupTimer);
this.groupTimer = null;
}
},
beforeDestroy() {
if (this.groupTimer) {
clearInterval(this.groupTimer);
this.groupTimer = null;
}
},
onShow() {
if (this.id && getStorageFun(TOKEN_NAME)) {
this.fetchGoodsCouponList();
}
},
onShareAppMessage(res) {
const queryStr = this.buildShareParams();
let title = this.orderInfo.name || "商品分享";
if (this.isGroupProduct) {
title = `【拼团抢购】快来和我一起拼团!${this.orderInfo.name || ""}`;
}
return {
title: title,
path: `/pages/other_package/productInfo/productInfo?${queryStr}`,
imageUrl: (this.swiperList && this.swiperList.length > 0) ? this.swiperList[0] : (this.orderInfo.mainGraph || ""),
};
},
onShareTimeline(res) {
const queryStr = this.buildShareParams();
let title = this.orderInfo.name || "商品分享";
if (this.isGroupProduct) {
title = `【拼团抢购】快来和我一起拼团!${this.orderInfo.name || ""}`;
}
return {
title: title,
query: queryStr,
imageUrl: (this.swiperList && this.swiperList.length > 0) ? this.swiperList[0] : (this.orderInfo.mainGraph || ""),
};
},
methods: {
assembleAddress,
onContributionTip() {
this.contributionTipShow = true;
},
limit999(n) {
const num = Number(n);
if (isNaN(num) || num < 10000) {
return n != null && n !== '' ? String(n) : '0';
}
return `${Math.floor(num / 10000)}万+`;
},
async calcSwiperHeight() {
if (!this.swiperList || this.swiperList.length === 0) return;
try {
const imgUrl = this.swiperList[0];
const res = await uni.getImageInfo({
src: imgUrl
});
if (res && res.width && res.height) {
const ratio = res.height / res.width;
this.swiperHeight = Math.round(750 * ratio);
}
} catch (e) {
console.error('calcSwiperHeight error:', e);
this.swiperHeight = 750;
}
},
onOpenApp() {
//https://download.tbmall.xin/1002/apppacket/20251113/20251113183449483.apk
let iphone = this.iosOrAnd();
window.location = "trustbridgeapp://";
timer = setTimeout(() => {
if (timer != null) {
uni.showModal({
title: "未成功唤起APP",
content: "是否前往下载?",
success: (res) => {
if (res.confirm) {
window.open("https://app.tbmall.xin");
}
},
});
}
}, 3000);
},
iosOrAnd() {
let userAgent = navigator.userAgent || navigator.vendor || window.opera;
if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
return "ios";
}
if (/Android/.test(userAgent)) {
return "and";
}
return "Other";
},
onZiXun() {
if (this.isShareUrl) {
return;
}
uni.setStorageSync("kefu-pending-context", {
type: "product",
data: {
goodsId: this.id,
goodsName: this.orderInfo.name,
goodsMainGraph: this.orderInfo.mainGraph,
currentPrice: this.orderInfo.adPrice,
},
});
uni.navigateTo({
url: `/pages/rwa_package/kefu/kefu?scene=product&goodsId=${this.id}`,
});
},
getCurrentPagePathWithArgs() {
try {
const pages = getCurrentPages();
if (pages && pages.length) {
const cur = pages[pages.length - 1];
if (cur && cur.route) {
let path = "/" + cur.route;
const opts = cur.options || {};
const qs = Object.keys(opts)
.map((k) => `${k}=${encodeURIComponent(opts[k])}`)
.join("&");
if (qs) path += "?" + qs;
return path;
}
}
} catch (e) { }
let path = `/pages/other_package/productInfo/productInfo?id=${this.id}`;
if (this.isGroupProduct || this.activedType === "groupBuy") {
path += `&activedType=groupBuy&activityId=${this.activityId || 1}`;
} else {
if (this.activedType) path += `&activedType=${this.activedType}`;
if (this.activityId) path += `&activityId=${this.activityId}`;
}
if (this.isCoupon) path += `&isPar=${this.isCoupon}`;
return path;
},
redirectToLogin() {
const redirectUrl = encodeURIComponent(this.getCurrentPagePathWithArgs());
uni.navigateTo({
url: `/pages/login_package/login/login?redirect=${redirectUrl}`,
});
},
buildShareParams() {
const params = [];
if (this.id) params.push(`id=${this.id}`);
if (this.isGroupProduct || this.activedType === "groupBuy") {
params.push("activedType=groupBuy");
params.push(`activityId=${this.activityId || 1}`);
} else {
if (this.activedType) params.push(`activedType=${this.activedType}`);
if (this.activityId) params.push(`activityId=${this.activityId}`);
}
if (this.isCoupon) params.push(`isPar=${this.isCoupon}`);
return params.join("&");
},
// 微信小程序环境分享
onWxShare(index) {
console.log("微信小程序分享", index);
if (index == 0) {
// 微信好友:通过 button open-type="share" 触发,此处只需关闭分享弹窗
this.onClose();
} else if (index == 1) {
// 分享到微信朋友圈:开启右上角朋友圈分享菜单并引导用户点击右上角 [...]
// #ifdef MP-WEIXIN
uni.showShareMenu({
withShareTicket: true,
menus: ["shareTimeline"],
success: () => {
uni.showModal({
title: "提示",
content: "已为您开启朋友圈分享功能,请点击右上角 [...] 选择【分享到朋友圈】",
showCancel: false,
});
},
fail: () => {
uni.showModal({
title: "提示",
content: "请点击右上角 [...] 分享到朋友圈",
showCancel: false,
});
},
});
// #endif
this.onClose();
}
},
onShare(index) {
if (this.isGroupProduct) {
const targetPath = `/pages/other_package/productInfo/productInfo?id=${this.id}&activedType=groupBuy&activityId=1`;
const targetUrl = `${SHARE_URL}/#${targetPath}`;
const price = this.groupActivityInfo?.groupPrice || this.orderInfo?.price || "";
const title = `【拼团抢购】快来和我一起拼团!${this.orderInfo?.name || ""}`;
const summary = `【信任桥】邀请好友拼团!${this.orderInfo?.name || ""} 拼团价 ¥${price}`;
const imageUrl = (this.swiperList && this.swiperList.length > 0) ? this.swiperList[0] : (this.orderInfo.mainGraph || "");
if (index == 0) {
// #ifdef APP-PLUS
uni.share({
provider: "weixin",
scene: "WXSceneSession",
type: 5,
imageUrl: imageUrl,
title: title,
miniProgram: {
id: "gh_c31503d62920",
path: targetPath,
type: MINI_PROGRAM_TYPE,
webUrl: targetUrl,
},
success: () => {
this.onClose();
},
fail: (err) => {
console.error("App小程序分享失败,降级为普通网页分享:", err);
uni.share({
provider: "weixin",
scene: "WXSceneSession",
type: 0,
href: targetUrl,
title: title,
summary: summary,
imageUrl: imageUrl,
complete: () => {
this.onClose();
},
});
},
});
// #endif
// #ifdef H5
uni.setClipboardData({
data: targetUrl,
success: () => {
uni.showToast({
title: "拼团链接已复制,请发送给微信好友",
icon: "none",
});
},
});
this.onClose();
// #endif
return;
} else if (index == 1) {
// #ifdef APP-PLUS
uni.share({
provider: "weixin",
scene: "WXSceneTimeline",
type: 0,
href: targetUrl,
title: title,
summary: summary,
imageUrl: imageUrl,
});
// #endif
// #ifdef H5
uni.setClipboardData({
data: targetUrl,
success: () => {
uni.showToast({
title: "拼团链接已复制,去分享到朋友圈吧",
icon: "none",
});
},
});
// #endif
this.onClose();
return;
}
}
let tmp = `【信任桥】消费共享数字化权益 ${SHARE_URL}/#/pages/other_package/productInfo/productInfo?id=${this.id} ${this.orderInfo.name}`;
if (index == 0) {
// WXSceneTimeline
uni.share({
provider: "weixin",
scene: "WXSceneSession",
type: 1,
summary: tmp,
});
this.onClose();
} else if (index == 1) {
uni.share({
provider: "weixin",
scene: "WXSceneTimeline",
type: 1,
summary: tmp,
});
} else if (index == 2) {
this.onClose();
} else if (index == 3) {
setTimeout(() => {
this.onDraw();
}, 1000);
this.onClose();
} else if (index == 4) {
return func.myToast("暂未开放");
uni.share({
provider: "qq",
type: 1,
summary: tmp,
});
}
},
async onDraw() {
let _this = this;
try {
qsc.setPaintbrush({
strokeStyle: "#EBEBEB",
font: {
fontSize: 16,
},
});
last = await qsc.drawImg({
val: "https://static.tbmall.xin/static/new/di.png",
x: 0,
y: 0,
height: 480,
width: 310,
});
last = await qsc.drawImg({
val: "https://static.tbmall.xin/static/new/logo.png",
x: 228,
y: 393,
height: 62,
width: 62,
});
// #ifndef H5
last = await qsc.drawImg({
val: _this.swiperList[0],
x: 20,
y: 20,
height: 270,
width: 270,
});
// #endif
qsc.setPaintbrush({
strokeStyle: "#EBEBEB",
});
// { x1: obj.x, y1: y, x2: obj.x + tw, y2: y, style, gap, width, color }
last = await qsc.drawLine({
style: "solide",
x1: 210,
y1: 313,
x2: 210,
y2: 342,
width: 1,
gap: 1,
});
last = await qsc.drawLine({
style: "solide",
x1: 20,
y1: 368,
x2: 290,
y2: 368,
width: 1,
gap: 1,
});
// last = await qsc.drawImg({
// val:'https://hashmall-prod.oss-cn-shenzhen.aliyuncs.com/1002/image/20250818/20250818134614672.jpg',
// x: 20,
// y: 20,
// height: 270,
// width: 270,
// })
last = await qsc.drawText({
val: this.orderInfo.name,
x: 20,
y: 310,
maxWidth: 180,
line: 2,
});
last = await qsc.drawText({
val: "1、保存图片到相册",
x: 93,
y: 397,
paintbrushProps: {
fillStyle: "#7f7f7f",
font: { fontSize: 12, fontWeight: 400 },
},
});
last = await qsc.drawText({
val: "2、扫一扫下载信任桥",
x: 93,
y: 438,
paintbrushProps: {
fillStyle: "#7f7f7f",
font: { fontSize: 12, fontWeight: 400 },
},
});
last = await qsc.drawText({
val: "¥" + this.orderInfo.minPrice,
x: 230,
y: 317,
paintbrushProps: {
fillStyle: "#7A35F6",
font: { fontSize: 20, fontWeight: 700 },
},
});
last = await qsc.drawQrCode({
val: `${SHARE_URL}/#/pages/other_package/productInfo/productInfo?id=${this.id}`,
x: 20,
y: 391,
size: 65,
});
// await qsc.updateCanvasWH({ width: '300px', height: '960px'})
await qsc.draw();
setTimeout(() => {
uni.canvasToTempFilePath({
canvasId: "myCanvas",
success: function (res) {
const tempFilePath = res.tempFilePath;
_this.tmpImg = tempFilePath;
// #ifdef H5
uni.downloadFile({
url: tempFilePath,
success: function () {
let link = document.createElement("a");
link.href = tempFilePath;
link.download = "分享图片";
link.click();
},
fail: function (err) {
uni.showToast({
title: "保存失败",
icon: "none",
duration: 2000,
});
},
});
// #endif
// #ifndef H5
uni.saveImageToPhotosAlbum({
filePath: tempFilePath,
success: function () {
uni.showToast({
title: "保存成功",
icon: "success",
duration: 2000,
});
},
fail: function (err) {
console.error("保存失败:", err);
uni.showModal({
title: "错误",
content: "保存图片到相册失败",
showCancel: false,
});
},
});
// #endif
},
fail: function (error) {
console.error("获取图片失败:", error);
},
});
}, 200);
// let img = await qsc.toImage({x:0,y:0,width:620,height:960,
// destWidth:310 * 4,destHeight:480 * 4,fileType:'png'});
// uni.saveImageToPhotosAlbum({filePath:img});
// console.log('img',img)
} catch (e) {
//TODO handle the exception
console.log("绘制异常", e);
uni.showModal({
content: JSON.stringify(e),
});
}
},
onClose() {
this.shareShow = false;
setTimeout(() => {
this.shareOpen = false;
}, 1000);
},
onSwiper(index) {
uni.previewImage({
current: index,
loop: true,
urls: this.swiperList,
});
},
jumpAddAddress() {
if (this.isShareUrl) {
return;
}
this.addressShow = true;
},
// 地址选中
getAddressValueFunc(item) {
const addressObj = item;
addressObj.addressStr = assembleAddress(addressObj).names;
this.selectAddress = addressObj;
},
// 地址类型
checkAddressShow(status, type, addIndex, index) {
this.addressShow = status;
this.addressType = type;
this.addressIndex = addIndex;
this.shopsIndex = index;
},
// 增加收货地址
addAdress() {
uni.navigateTo({
url:
"/pages/mine_package/mine_address_add/mine_address_add?detail=" +
true,
});
},
// 地址选中OK
async addressOk() {
console.log("this.selectAddress", this.selectAddress);
const params = {
addrId: this.selectAddress.id,
};
const resp = await getPostageQuery(params);
if (resp && resp.bizcode === 100) {
this.adressInfo =
this.selectAddress.countryName +
this.selectAddress.provinceName +
this.selectAddress.cityName +
this.selectAddress.address;
this.addressShow = false;
this.postage = resp.data;
}
},
copyMobile(mobile) {
copyData(mobile);
this.serviceShow = false;
this.openShow = false;
},
jumpLeft() {
console.log("productInfo jumpLeft called, fromBuyAlone:", this.fromBuyAlone);
// 如果是从拼团详情“单独购买”弹窗跳转过来的,返回时直达拼团列表
if (this.fromBuyAlone) {
this.fromBuyAlone = false;
uni.reLaunch({
url: "/pages/active/group-buying/group-buying?fromBuyAlone=1",
fail: (err) => {
console.error("reLaunch failed, fallback to redirectTo:", err);
uni.redirectTo({
url: "/pages/active/group-buying/group-buying?fromBuyAlone=1",
});
},
});
return;
}
const pages = getCurrentPages();
// 如果是从分享链接直接打开(页面栈数量 <= 1)或闪屏/引导打开,点击返回直接回到首页
if (this.isSplash == "true" || pages.length <= 1) {
uni.switchTab({
url: "/pages/home/home",
});
return;
}
uni.navigateBack({
delta: 1, // 返回上一级页面
fail: () => {
uni.switchTab({
url: "/pages/home/home",
});
},
});
},
jumpOther(type) {
if (this.isShareUrl) {
return;
}
if (type === "home") {
uni.switchTab({
// 首页
url: "/pages/home/home",
});
} else if (type === "share") {
// 分享
uni.navigateTo({
url: "/pages/other_package/invite_friends/invite_friends",
});
} else if (type === "cart") {
// 购物车
uni.switchTab({
url: "/pages/shopping_cart/shopping_cart",
});
}
},
/**
* 查询详细内容
*/
async getById() {
const params = {
id: this.id,
};
if (this.isCoupon) {
params.type = -1;
}
if (this.activedType === 'seckill') {
params.activityId = this.activityId;
params.activityType = 'seckill';
}
if (this.activedType === 'groupBuy') {
params.activityId = this.activityId;
params.type = 5;
}
const resp = await getGoodsDetail(params);
if (resp && resp.bizcode === 100) {
const data = resp.data;
// 限时秒杀 和 积分兑换 不需要购物车
//限时秒杀现在要改为展示购物车icon
this.isShowCart = this.activedType === 'seckill' || data.type == 3 ? 1 : 0;
// 商品图
if (data.goodsGraph) {
this.swiperList = data.goodsGraph.split(",").map(item => item.trim()).filter(Boolean);
this.calcSwiperHeight();
}
console.log('----', data.goodsGraph, this.swiperList)
data.addressObj = {}; // 收件人地址
data.quantity = 1; // 数量
data.inventory = 0; // 库存
if (this.activedType === 'seckill') {
data.minPrice = data.activity?.activityPrice;
}
console.log("查询详细内容---", data)
this.orderInfo = data;
if (data.groupPrice !== undefined && data.groupPrice !== null && data.groupPrice !== "") {
this.fetchGroupBuyCurrent(data);
this.fetchJoinableList();
}
this.detailRich = this.orderInfo.detailInfo.replace(
/<img([^>]*?)>/gi,
'<img$1 style="max-width:100%;height:auto;display:block;" loading="lazy">'
);
if (getStorageFun(TOKEN_NAME)) {
this.fetchGoodsCouponList();
}
}
},
onJoinTeam(team) {
this.currentJoinTeam = team;
this.checkSpecification(true, "joinGroup");
},
// 打开"可直接参与的拼团"弹窗,同时拉取列表
openJoinablePopup() {
this.joinablePopupShow = true;
this.fetchJoinableList();
},
// 拉取可参与拼团列表
async fetchJoinableList() {
try {
const params = { goodsId: this.id };
if (this.activityId) params.activityId = this.activityId;
const res = await getGroupBuyJoinableList(params);
if (res && res.bizcode === 100 && Array.isArray(res.data)) {
this.joinableTeamList = res.data;
} else {
this.joinableTeamList = [];
}
} catch (e) {
console.error("fetchJoinableList error:", e);
this.joinableTeamList = [];
}
},
// 格式化拼团倒计时文本,如 "12:17后结束"
formatTeamCountdown(item) {
let seconds = 0;
if (item.remainingSeconds !== undefined && item.remainingSeconds !== null) {
seconds = Number(item.remainingSeconds);
} else if (item.expireTime) {
seconds = Math.floor((Number(item.expireTime) - Date.now()) / 1000);
}
if (seconds <= 0) return "已结束";
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
if (h > 0) {
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}后结束`;
}
return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}后结束`;
},
// 从弹窗中点击"去拼团"
onJoinTeamFromPopup(team) {
this.joinablePopupShow = false;
this.currentJoinTeam = team;
this.$nextTick(() => {
this.checkSpecification(true, "joinGroup");
});
},
// 从 /groupBuy/current/0910 接口获取当前拼团活动(startTime, endTime, remainingSeconds等)
async fetchGroupBuyCurrent(fallbackData) {
try {
const params = {};
if (this.id) params.goodsId = this.id;
if (this.activityId) params.activityId = this.activityId;
const res = await getGroupBuyCurrent(params);
if (res && res.bizcode === 100 && res.data) {
const actData = res.data;
this.groupActivityInfo = actData;
if (actData.activityId && !this.activityId) {
this.activityId = actData.activityId;
}
if (actData.groupSize && this.orderInfo) {
this.$set(this.orderInfo, "groupSize", actData.groupSize);
}
this.initGroupCountdown(actData);
return;
}
} catch (e) {
console.error("fetchGroupBuyCurrent error:", e);
}
if (fallbackData) {
this.initGroupCountdown(fallbackData);
}
},
initGroupCountdown(data) {
if (this.groupTimer) {
clearInterval(this.groupTimer);
this.groupTimer = null;
}
if (!data) {
this.groupCountdownSeconds = 0;
this.groupCountdownText = "00:00:00";
return;
}
let remaining = 0;
const now = Date.now();
// 支持 13位毫秒时间戳 endTime / expireTime / activityEndTime
const endTime = data.endTime
? Number(data.endTime)
: data.expireTime
? Number(data.expireTime)
: data.activityEndTime
? Number(data.activityEndTime)
: data.activity?.endTime
? Number(data.activity.endTime)
: 0;
if (endTime > 0) {
// 使用 13位毫秒时间戳计算精确剩余秒数
remaining = Math.floor((endTime - now) / 1000);
} else if (data.remainingSeconds !== undefined && data.remainingSeconds !== null) {
remaining = Number(data.remainingSeconds);
} else if (data.activity?.remainingSeconds !== undefined && data.activity?.remainingSeconds !== null) {
remaining = Number(data.activity.remainingSeconds);
} else {
remaining = 0;
}
this.groupCountdownSeconds = remaining > 0 ? remaining : 0;
this.updateGroupCountdownText();
if (this.groupCountdownSeconds > 0) {
this.groupTimer = setInterval(() => {
this.groupCountdownSeconds--;
if (this.groupCountdownSeconds <= 0) {
this.groupCountdownSeconds = 0;
this.groupCountdownText = "00:00:00";
clearInterval(this.groupTimer);
this.groupTimer = null;
} else {
this.updateGroupCountdownText();
}
}, 1000);
} else {
this.groupCountdownText = "00:00:00";
}
},
updateGroupCountdownText() {
const s = this.groupCountdownSeconds;
const hours = Math.floor(s / 3600);
const minutes = Math.floor((s % 3600) / 60);
const seconds = s % 60;
const pad = (num) => String(num).padStart(2, "0");
this.groupCountdownText = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
},
/**
* 来自商城的订单
*/
getSupplyChainById() {
const params = {
goods_id: this.id,
app_id: "2024111300164927",
sign_type: "MD5",
sign: "d04be0cedd49608e758458325faecf90",
};
uni.request({
url: "https://cyysc.gscm.top/outside/1/atm/goods/detail",
method: "get",
data: params,
header: {},
success: (res) => {
const data = res.data;
if (data && data.result === 1) {
const objData = data.data;
const content = objData.content
.replace(/&amp;/g, "&")
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, "'")
.replace(/&#39;/g, '"')
.replace(/\n/g, "<br>");
const orderInfo = {
detailInfo: content,
minPrice: objData.min_price,
// maxPrice:objData.max_price,
adPrice: objData.price,
sales: objData.virtual_sales,
};
const domain = "https://cyysc.gscm.top";
let currentThumb = objData.thumb;
if (
objData.thumb.indexOf("https://") === -1 &&
objData.thumb.indexOf("http://") === -1
) {
currentThumb = domain + "/static/upload/" + objData.thumb;
}
this.swiperList = [currentThumb]; // 封面图
this.calcSwiperHeight();
this.detailRich = content.replace(
/<img([^>]*?)>/gi,
'<img$1 style="max-width:100%;height:auto;display:block;" loading="lazy">'
);
this.orderInfo = orderInfo;
}
},
fail: (error) => {
console.log("error", error);
},
});
},
async checkSpecification(val, type) {
if (this.isShareUrl) {
return;
}
if (val && type !== "n" && !getStorageFun(TOKEN_NAME)) {
this.redirectToLogin();
return;
}
const source = this.source;
if (type == "n" && (this.isShowCart == 1 || this.isCoupon)) {
type = "buy";
}
console.log("afsdfsd", source, type);
if (source && source === "supply_chain") {
this.openShow = true;
return;
}
// 需要查询一下最新的规格
if (val) {
const hasStructuredSpecs = await this.querySpecification();
if (!hasStructuredSpecs) {
uni.showToast({ title: "SKU规格加载失败,请稍后重试", icon: "none" });
return;
}
} else {
this.specificationList = []; //规格
this.selectSpecification = []; // 选择的规格
this.specificationTempl = []; // 规格模版数据
}
this.specificationShow = val;
this.specificationShowType = type;
},
// 获取规格模版数据
async querySpecification() {
let infoResp = null;
const params = {
id: this.id,
};
if (this.isCoupon) {
params.type = -1;
}
if (this.activedType === 'seckill') {
params.activityId = this.activityId;
params.activityType = 'seckill';
}
try {
infoResp = await getGoodsSpecsInfo(params);
} catch (e) {
return false;
}
if (!infoResp || infoResp.bizcode !== 100 || !infoResp.data) {
return false;
}
const groups = infoResp.data.specGroups || [];
const skus = infoResp.data.skus || [];
if (!groups.length || !skus.length) {
return false;
}
const skuBySpecKey = {};
skus.forEach((sku) => {
const valueIds = this.getSkuValueIds(sku, groups);
if (valueIds.length !== groups.length) {
return;
}
const normalizedSku = Object.assign({}, sku, {
id: sku.id,
valueIds,
minBuyNum: sku.minBuyNum || 1,
combTypes: this.formatSkuSelection(groups, valueIds),
});
skuBySpecKey[valueIds.join("_")] = normalizedSku;
});
if (!Object.keys(skuBySpecKey).length) {
return false;
}
this.skuBySpecKey = skuBySpecKey;
this.specificationTempl = Object.keys(skuBySpecKey).map((key) => skuBySpecKey[key]);
this.orderInfo.inventory = this.specificationTempl.reduce(
(total, sku) => total + Number(sku.stock || 0),
0
);
this.selectedSpecValueIds = groups.map(() => null);
this.selectSpecification = groups.map(() => null);
this.selectSpecificationTempl = {};
this.specificationList = groups.map((group) => ({
id: group.id,
type: group.name,
selectVal: null,
items: (group.values || []).map((value) => ({
id: value.id,
groupId: group.id,
n: value.name,
image: value.image,
disabled: false,
})),
}));
this.refreshSpecOptionState();
// 默认选中每组规格的第一个可用选项(若均无可用则选中第一个选项)
this.specificationList.forEach((group, groupIndex) => {
const defaultItem = group.items.find((item) => !item.disabled) || group.items[0];
if (defaultItem) {
this.clickSpecification(groupIndex, group, defaultItem);
}
});
return true;
},
getSkuValueIds(sku, groups) {
if (sku.specValueIds) {
try {
const ids = JSON.parse(sku.specValueIds);
if (Array.isArray(ids)) {
return ids.map((id) => Number(id));
}
} catch (e) { }
}
let path = {};
try {
path = JSON.parse(sku.specValuePath || "{}") || {};
} catch (e) { }
const legacyValues = String(path.legacyName || "")
.split(/\s*(?:\/|♧)\s*/)
.filter(Boolean);
return groups.map((group, index) => {
const selectedName = path[group.name] || legacyValues[index];
const value = (group.values || []).find((item) => item.name === selectedName);
return value ? Number(value.id) : null;
}).filter((id) => id !== null);
},
formatSkuSelection(groups, valueIds) {
return groups.map((group, index) => {
const value = (group.values || []).find((item) => Number(item.id) === Number(valueIds[index]));
return value ? `${group.name}:${value.name}` : "";
}).filter(Boolean).join(";");
},
refreshSpecOptionState() {
this.specificationList.forEach((group, groupIndex) => {
group.items.forEach((value) => {
const candidate = this.selectedSpecValueIds.slice();
candidate[groupIndex] = Number(value.id);
value.disabled = !Object.keys(this.skuBySpecKey).some((key) => {
const sku = this.skuBySpecKey[key];
const skuIds = sku.valueIds || [];
return candidate.every((selectedId, index) =>
selectedId == null || Number(skuIds[index]) === Number(selectedId)
) && Number(sku.stock || 0) > 0;
});
});
});
},
//改变规格的样式
clickSpecification(groupIndex, item, oItem) {
console.log(
"改变规格的样式",
groupIndex,
item,
oItem,
this.specificationTempl,
this.specificationList
);
if (oItem.disabled) {
return;
}
this.selectedSpecValueIds.splice(groupIndex, 1, Number(oItem.id));
this.selectSpecification.splice(groupIndex, 1, oItem.n);
this.specificationList[groupIndex].selectVal = oItem.n;
this.refreshSpecOptionState();
const skuBySpecBash = this.getSkuBySpecValueIds(this.selectedSpecValueIds) || {};
console.log("----111", skuBySpecBash)
this.skuBySpecInfo = { ...this.orderInfo, skuImage: skuBySpecBash.skuImage, stock: skuBySpecBash.stock, supplyPrice: skuBySpecBash.supplyPrice, price: skuBySpecBash.price, skuName: skuBySpecBash.skuName, originalPrice: skuBySpecBash.originalPrice }
console.log("----222", this.orderInfo)
if (this.selectedSpecValueIds.every((id) => id != null)) {
const currentSelectSpecification = this.skuBySpecKey[this.selectedSpecValueIds.join("_")] || {};
this.selectSpecificationTempl = currentSelectSpecification;
this.orderInfo.quantity = currentSelectSpecification.minBuyNum || 1;
this.orderInfo.inventory = currentSelectSpecification.stock || 0;
} else {
this.selectSpecificationTempl = {};
}
},
// 根据规格 ID 数组(如 [颜色id, 尺码id] -> [146913, 146916])获取单条 SKU 对象
getSkuBySpecValueIds(specValueIds) {
if (!specValueIds || !specValueIds.length) return null;
// 方式一:直接通过预先拼接的 key ("146913_146916") 获取 (O(1))
const key = specValueIds.join("_");
if (this.skuBySpecKey && this.skuBySpecKey[key]) {
return this.skuBySpecKey[key];
}
// 方式二:在 specificationTempl (skus) 数组中按 specValueIds 检索匹配
return (this.specificationTempl || []).find((sku) => {
const ids = sku.valueIds || JSON.parse(sku.specValueIds || "[]");
return specValueIds.every((id) => ids.map(Number).includes(Number(id)));
}) || null;
},
// 选择规格
selectSpecificationFun(index, item) {
console.log("selectSpecificationTempl");
const currentSelectSpecification = this.selectSpecification;
currentSelectSpecification[index] = item.n;
this.selectSpecification = currentSelectSpecification;
const oldLength = this.specificationList.length;
const newLength = this.selectSpecification.length;
// 开始做校验
if (oldLength === newLength) {
const newLengthStr = this.selectSpecification.join("♧");
const specificationTempl = this.specificationTempl;
let selectTempl = {};
specificationTempl.map((item) => {
if (item.combNames === newLengthStr) {
selectTempl = item;
return;
}
});
this.selectSpecificationTempl = selectTempl;
this.orderInfo.inventory = selectTempl.stock; // 库存
}
},
// 改变数量
quantityFun(type) {
let currentQuantity = this.orderInfo.quantity; // 当前数量
const minBuyNum = this.selectSpecificationTempl.minBuyNum || 1; // 当前起订量,默认为1
console.log(" 当前起订量,默认为1", currentQuantity, minBuyNum);
// 确保起订量至少为1
const step = Math.max(minBuyNum, 1);
if (type === "minus") {
// 计算减少后的数量,确保不低于最小起订量且是其倍数
const newQuantity = currentQuantity - step;
if (newQuantity < minBuyNum) {
this.$refs.uToastRef.show({
type: "error",
message: `不能低于最低购买数量${minBuyNum}`,
});
} else {
currentQuantity = newQuantity;
}
} else if (type === "add") {
currentQuantity = currentQuantity + step;
}
this.orderInfo.quantity = currentQuantity;
},
// 加入购入车
async addCart(isBy = false) {
if (!getStorageFun(TOKEN_NAME)) {
this.redirectToLogin();
return;
}
const selectTempl = this.selectSpecificationTempl;
if (!selectTempl || Object.keys(selectTempl).length === 0) {
uni.showToast({
title: "请先选择规格",
icon: "none", // 可选 success/loading/none
duration: 2000, // 持续时长,单位ms
});
return;
}
if (
!(
selectTempl.minBuyNum > 0 &&
this.orderInfo.quantity >= selectTempl.minBuyNum &&
this.orderInfo.quantity % selectTempl.minBuyNum === 0
)
) {
uni.showToast({
title: `购买数量需要大于等于起订量${selectTempl.minBuyNum}`,
icon: "none", // 可选 success/loading/none
duration: 2000, // 持续时长,单位ms
});
return;
}
const params = {
id: this.id,
specsId: selectTempl.id, // 规格ID
goodsNum: this.orderInfo.quantity, // 商品数量
};
const isGroupAction = Boolean(
this.isGroupProduct &&
(this.specificationShowType === "groupBuy" ||
this.specificationShowType === "joinGroup" ||
this.activedType === "group")
);
if (this.isShowCart == 1 || this.isCoupon || isGroupAction) {
delete selectTempl.skuImage;
delete selectTempl.specValueIds;
delete selectTempl.activityGoodsId;
delete selectTempl.skuName;
delete selectTempl.legacyName;
delete selectTempl.combTypes;
delete selectTempl.specValuePath;
Object.keys(selectTempl).forEach((key) => {
if (selectTempl[key] === null) {
delete selectTempl[key];
}
});
console.log('selectTempl', selectTempl)
params["specsId"] = selectTempl;
let url = "";
if (this.activedType == "seckill") {
params["activedType"] = this.activedType; // 活动类型
params["activityId"] = this.activityId;// 秒杀活动ID
url = `/pages/order_package/order_submit/order_submit?sed=${JSON.stringify(
params
)}`;
}
if (this.isCoupon) {
url = `/pages/order_package/order_submit/order_submit?sed=${JSON.stringify(
params
)}&address=${this.selectAddress?.id}&isCoupon=${this.isCoupon}`
}
if (isGroupAction) {
params["activedType"] = this.specificationShowType || "group";
params["activityId"] = this.orderInfo.activityId || this.activityId || "";
params["activityGoodsId"] = this.orderInfo.activityGoodsId || "";
params["groupPrice"] = this.orderInfo.groupPrice;
if (this.currentJoinTeam && this.currentJoinTeam.id) {
params["teamId"] = this.currentJoinTeam.id;
}
url = `/pages/order_package/order_submit/order_submit?sed=${JSON.stringify(params)}&address=${this.selectAddress?.id}`;
}
if (!url) {
url = `/pages/order_package/order_submit/order_submit?sed=${JSON.stringify(
params
)}&address=${this.selectAddress?.id}`;
}
this.specificationShow = false;
uni.navigateTo({
url: url
});
return;
}
const resp = await addGoods(params);
if (resp && resp.bizcode === 100) {
if (isBy) {
uni.navigateTo({
url: `/pages/order_package/order_submit/order_submit?ids=${resp.data}&address=${this.selectAddress?.id}`,
});
} else {
this.specificationShow = false;
uni.showToast({
title: "成功加入购物车",
icon: "success", // 可选 success/loading/none
duration: 2000, // 持续时长,单位ms
});
}
}
},
// 跳转到分享页面
jumpShare() {
console.log("jumpShare");
this.shareOpen = true;
this.shareShow = true;
},
// 查看全部评价
onGoMore() {
this.evaluateDialogShow = true;
},
// 获取商品优惠券列表
async fetchGoodsCouponList() {
if (!this.id || !getStorageFun(TOKEN_NAME)) return;
try {
const res = await getGoodsCouponList({ goodsId: this.id, payway2: this.orderInfo.buyDeductionValue ? 'vcion' : null });
if (res && res.bizcode === 100) {
this.goodsCouponList = res.data || [];
}
} catch (e) {
console.error("获取商品优惠券失败:", e);
}
},
// 弹窗中点击“立即领取”
async onReceiveGoodsCoupon(coupon, index) {
const templateId = coupon.templateId || coupon.id;
if (!templateId) return;
try {
const res = await receiveCoupon({ templateId });
if (res && res.bizcode === 100) {
uni.showToast({
title: "领取成功",
icon: "success",
});
this.$set(this.goodsCouponList[index], "received", true);
this.$set(this.goodsCouponList[index], "receiveStatus", 1);
} else {
uni.showToast({
title: res?.msg || "领取失败",
icon: "none",
});
}
} catch (e) {
console.error("领取优惠券失败:", e);
uni.showToast({
title: "领取失败,请重试",
icon: "none",
});
}
},
},
};
</script>
<style lang="scss" scoped>
.border-16 {
border-radius: 16rpx !important;
}
::v-deep(.u-swiper__wrapper) {
.u-swiper__wrapper__item__wrapper {
height: 100%;
}
}
.product_info_warp {
min-height: 100vh;
background-color: $app-bj;
padding-bottom: calc(160rpx + env(safe-area-inset-bottom) + 20rpx);
box-sizing: border-box;
}
.product_info_header_warp {
position: relative;
.product_info_header_swiper {
.u-swiper {
height: 100% !important;
}
::v-deep(.u-swiper__wrapper) {
height: 100% !important;
.u-swiper__wrapper {
height: 100% !important;
.u-swiper__wrapper__item__wrapper__image {
height: 100% !important;
}
}
}
}
.img_comm {
position: absolute;
top: 170rpx;
z-index: 100;
cursor: pointer;
}
.left_warp {
left: 40rpx;
}
.right_warp {
right: 40rpx;
}
// 拼团专属商品卡片 - 绝对定位悬浮在轮播图底部
.group_product_card {
position: absolute;
bottom: -190rpx;
left: 20rpx;
right: 20rpx;
z-index: 10;
background: #ffffff;
border-radius: 24rpx;
overflow: hidden;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
.group_price_bar {
display: flex;
align-items: center;
justify-content: space-between;
background: linear-gradient(90deg, #7433f6 0%, #b34ef7 100%);
padding: 0 28rpx;
height: 104rpx;
box-sizing: border-box;
.group_price_left {
display: flex;
align-items: baseline;
.group_price_label {
color: #ffffff;
font-size: 28rpx;
font-weight: 500;
margin-right: 6rpx;
}
.group_price_symbol {
color: #ffffff;
font-size: 30rpx;
font-weight: bold;
margin-right: 4rpx;
}
.group_price_int {
color: #ffffff;
font-size: 60rpx;
font-weight: 900;
line-height: 1;
}
.group_price_origin {
color: rgba(255, 255, 255, 0.75);
font-size: 26rpx;
text-decoration: line-through;
margin-left: 20rpx;
font-weight: normal;
}
}
.group_price_right {
.group_remaining_text {
color: #ffffff;
font-size: 26rpx;
font-weight: 500;
letter-spacing: 1rpx;
}
}
}
.group_card_body {
padding: 24rpx 24rpx 28rpx 24rpx;
.content_data_title {
font-weight: bold;
font-size: 34rpx;
color: #1a1a1a;
line-height: 48rpx;
margin-top: 0;
margin-bottom: 24rpx;
}
.group_series_row {
display: flex;
justify-content: space-between;
align-items: center;
.group_series_left {
display: flex;
align-items: center;
.group_contribution_pill {
display: inline-flex;
align-items: center;
background: #f4ebfe;
border: 1rpx solid #e1cefc;
border-radius: 8rpx;
padding: 6rpx 14rpx;
.contribution_icon {
width: 30rpx;
height: 30rpx;
margin-right: 8rpx;
}
.contribution_text {
font-size: 24rpx;
font-weight: 500;
color: #7934f6;
line-height: 1;
}
}
.group_tips_icon {
width: 32rpx;
height: 32rpx;
margin-left: 14rpx;
}
}
.group_sales_count {
font-size: 24rpx;
color: #999999;
font-weight: normal;
}
}
}
}
}
.product_content_warp {
// background: linear-gradient(0deg, #F9F9F9 0%, #FFFFFF 100%);
border-radius: 30rpx 30rpx 0rpx 24rpx;
padding: 20rpx;
overflow: hidden;
&.group_content_warp {
padding-top: 210rpx;
.content_concrete {
margin-top: 0;
}
}
.content_data {
padding: 20rpx;
}
.content_data_price {
font-weight: bold;
font-size: 36rpx;
color: $app-bt-bj;
&.coupon_price_box {
display: flex;
align-items: baseline;
.coupon_label {
font-size: 28rpx;
font-weight: normal;
margin-right: 6rpx;
}
.coupon_symbol {
font-size: 28rpx;
font-weight: 500;
margin-right: 4rpx;
}
.coupon_int {
font-size: 56rpx;
font-weight: bold;
line-height: 1;
}
.coupon_dec {
font-size: 32rpx;
font-weight: bold;
line-height: 1;
}
.coupon_split {
font-size: 28rpx;
margin: 0 16rpx;
font-weight: normal;
opacity: 0.7;
}
.coupon_origin {
font-size: 28rpx;
font-weight: normal;
}
}
.price_o {
font-weight: 500;
font-size: 26rpx;
color: #999999;
text-decoration-line: line-through;
margin-left: 10rpx;
}
&.buy_deduction {
width: 100%;
display: flex;
justify-content: space-between;
.buy_deduction_value {
font-weight: 500;
font-size: 26rpx;
color: #7A35F6;
margin-left: 10rpx;
}
.buy_deduction_label {
font-size: 24rpx;
color: #E88900;
display: flex;
align-items: center;
}
}
}
.content_data_title {
font-weight: bold;
font-size: 36rpx;
color: #111111;
margin-top: 10rpx;
}
.content_data_series {
display: flex;
justify-content: space-between;
align-items: center;
font-weight: 500;
font-size: 24rpx;
color: #999999;
margin-top: 10rpx;
.content_data_series_tag {
font-weight: 500;
font-size: 24rpx;
height: 45rpx;
color: #7934F6;
padding: 0 12rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(121, 52, 246, 0.1);
.uni-image {
width: 28rpx;
height: 28rpx;
margin-right: 5rpx;
}
}
.sales_count {
margin-left: auto;
}
}
.tui-view {
width: 100%;
height: 286rpx;
min-height: 286rpx;
background-color: #fff;
border-radius: 24rpx;
display: flex;
flex-direction: column;
padding: 32rpx;
box-sizing: border-box;
margin-top: 24rpx;
.tui-list {
width: 100%;
height: 104rpx;
min-height: 104rpx;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-top: 24rpx;
.tui-box {
width: 64rpx;
min-height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
image {
width: 64rpx;
height: 64rpx;
}
}
}
}
.content_concrete {
padding: 20rpx;
background: #ffffff;
border-radius: 30rpx;
margin-top: 20rpx;
.concrete_item {
display: flex;
justify-content: space-between;
font-size: 28rpx;
padding: 20rpx 0;
}
.concrete_item_ {
display: flex;
width: 100%;
}
.concrete_item_but {
border-bottom: 1rpx solid #eeeeeeff;
}
.concrete_item_title {
color: #808080;
width: 140rpx;
}
.concrete_item_value {
font-weight: 500;
color: #1a1a1a;
width: calc(100% - 140rpx);
.address_warp {
display: flex;
margin-bottom: 10rpx;
}
.address_72_warp {
font-size: 24rpx;
color: #808080;
}
}
.concrete_item_bz {
display: flex;
}
.concrete_item_bz_item {
margin-right: 20rpx;
}
.concrete_item_bz_item_left {
margin-left: 10rpx;
}
}
}
.content_details {
background: #ffffff;
border-radius: 24rpx;
padding: 10rpx 10rpx 30rpx;
margin-top: 30rpx;
// margin-bottom: 200rpx;
.content_details_title {
padding: 20rpx 30rpx;
}
.content_details_content {
rich-text {
width: 700rpx;
}
}
img,
img[alt] {
max-width: 100%;
}
}
.product_but_warp {
padding: 20rpx 20rpx calc(20rpx + env(safe-area-inset-bottom));
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: calc(160rpx + env(safe-area-inset-bottom));
background-color: #ffffff;
display: flex;
justify-content: space-between;
box-sizing: border-box;
z-index: 99;
.product_but_left {
display: flex;
justify-content: space-between;
// width: 225rpx;
margin-top: 6rpx;
.left_item {
// width: 50%;
width: 78rpx;
text-align: center;
font-weight: 500;
font-size: 20rpx;
color: #666666;
}
}
}
.product_but_right {
display: flex;
justify-content: flex-end;
flex: 1;
.but_comm {
text-align: center;
padding: 20rpx 30rpx;
color: #ffffff;
height: 46rpx;
font-size: 32rpx;
line-height: 46rpx;
}
.join_warp {
background: #e4d6fd;
border-radius: 16rpx 0rpx 0rpx 16rpx;
color: $app-zt-color;
}
.buy_warp {
background: $app-bt-bj;
border-radius: 0rpx 16rpx 16rpx 0rpx;
}
}
.group_popup_buy_btn {
background: linear-gradient(90deg, #8a3bf8 0%, #742af5 100%) !important;
color: #ffffff !important;
}
.specification_warp {
background-color: $app-bj;
.specification_adress_warp {
padding: 30rpx;
background: #ffffff;
border-radius: 24rpx;
}
.specification_adress_msg {
text-align: center;
font-weight: 500;
font-size: 24rpx;
color: #808080;
margin: 0 10rpx 0 20rpx;
}
.specification_adress_arr {
display: flex;
align-items: center;
margin-top: 20rpx;
}
.address_title {
font-weight: 500;
font-size: 26rpx;
color: #bbbbbb;
margin-bottom: 10rpx;
margin-right: 20rpx;
width: 592rpx;
white-space: nowrap;
/* 防止文本换行 */
overflow: hidden;
/* 隐藏溢出的文本 */
text-overflow: ellipsis;
/* 使用省略号表示溢出的内容 */
}
.address_title_sel {
color: #333333;
}
.specification_product_warp {
display: flex;
background: #ffffff;
border-radius: 24rpx;
padding: 30rpx;
margin-top: 20rpx;
.product_content {
margin-left: 10rpx;
// display: grid;
}
.price_content_warp {
// font-weight: bold;
font-size: 36rpx;
color: $app-bt-bj;
display: flex;
align-items: baseline;
&.coupon_price_box {
display: flex;
align-items: baseline;
.coupon_label {
font-size: 26rpx;
font-weight: normal;
margin-right: 6rpx;
}
.coupon_symbol {
font-size: 26rpx;
font-weight: 500;
margin-right: 4rpx;
}
.coupon_int {
font-size: 50rpx;
font-weight: bold;
line-height: 1;
}
.coupon_dec {
font-size: 30rpx;
font-weight: bold;
line-height: 1;
}
.coupon_origin {
font-size: 26rpx;
font-weight: normal;
margin-left: 20rpx;
}
}
}
.price_content_coupon_warp {
margin-left: 20rpx;
padding: 4rpx 10rpx;
text-align: center;
color: $app-bt-bj;
font-size: 22rpx;
font-weight: 500;
border: 1px solid $app-bt-bj;
border-radius: 6rpx;
}
.oOrice_content_warp {
font-size: 26rpx;
color: #808080;
line-height: 11rpx;
text-decoration-line: line-through;
margin-top: 10rpx;
}
.coupon_content_warp {
font-weight: 500;
font-size: 22rpx;
color: $app-bt-bj;
margin-top: 10rpx;
}
.coupon_content_stock {
display: flex;
font-size: 22rpx;
color: #808080;
margin-top: 10rpx;
}
.inventory_warp {
display: flex;
font-size: 24rpx;
color: #808080;
margin-top: 16rpx;
}
}
.specification_list_warp {
background: #ffffff;
border-radius: 24rpx;
padding: 30rpx;
margin-top: 20rpx;
.list_warp {
margin-bottom: 30rpx;
}
.list_title_warp {
font-weight: 500;
font-size: 30rpx;
color: #1a1a1a;
line-height: 48rpx;
}
.specification_item_warp {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx 24rpx;
margin-top: 16rpx;
}
.item_Warp {
width: 100%;
min-width: 0;
box-sizing: border-box;
padding: 0 12rpx;
background: $app-bj;
border-radius: 10rpx;
color: #333333;
text-align: center;
font-size: 26rpx;
font-weight: 500;
height: 56rpx;
line-height: 54rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border: 1px solid transparent;
}
.item_Warp_no_warp {
background: #f5f5f5;
color: #999999;
text-decoration-line: line-through;
border-color: transparent;
}
.item_Warp_yes_warp {
border-color: $app-bt-bj;
color: $app-bt-bj;
background: rgba(121, 52, 246, 0.1);
}
}
.specification_but {
background: #ffffff;
border-radius: 24rpx;
padding: 30rpx;
margin-top: 30rpx;
.specification_buy {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.specification_buy_but {
width: 100%;
padding: 20rpx 0;
text-align: center;
background-color: $app-bt-bj;
color: #ffffff;
margin-top: 20rpx;
font-weight: 500;
font-size: 28rpx;
color: #ffffff;
border-radius: 16rpx;
}
}
}
.popup_buts {
background-color: #ffffff;
.but_comm {
width: 40%;
}
}
.open_Show {
/* 弹出层形式的广告 */
.uni-banner {
display: flex;
align-items: center;
flex-direction: column;
position: fixed;
left: 50%;
top: 50%;
z-index: 1000;
transform: translate(-50%, -50%);
width: 80%;
background-image: url("https://static.tbmall.xin/static/productInfo_opt_1.png");
background-size: 100% 100%;
background-position: center;
/* 将图片居中显示 */
height: 746rpx;
}
.banner_box {
margin-top: 140rpx;
padding: 40rpx;
width: 88%;
}
.title_ {
font-size: 62rpx;
color: rgba(0, 0, 0, 0.9);
margin-top: 30rpx;
}
.msg_ {
font-size: 32rpx;
color: rgba(0, 0, 0, 0.9);
line-height: 42rpx;
text-align: center;
margin-top: 50rpx;
}
.phone_ {
background: rgba(216, 216, 216, 0.16);
border-radius: 15rpx;
text-align: center;
width: 100%;
padding: 20rpx 0;
margin-top: 50rpx;
}
.but_ {
display: flex;
justify-content: center;
margin-top: 50rpx;
.but_b {
background: #03ad27;
border-radius: 41rpx;
font-size: 32rpx;
color: #ffffff;
width: 70%;
text-align: center;
padding: 20rpx 0;
}
}
}
.hideCanvasView {
width: 100%;
height: 100%;
position: fixed;
top: 10000px;
left: 10000px;
z-index: 1000000000000;
display: flex;
align-items: center;
justify-content: center;
}
/* 领券预览标签样式 (图一高保真) */
.coupon_tags_preview {
display: flex;
align-items: center;
gap: 12rpx;
overflow: hidden;
max-width: 520rpx;
.preview_coupon_tag {
display: inline-block;
font-size: 22rpx;
color: #ff2442;
background: #ffe4e6;
border: 1px solid #ff99a4;
border-radius: 6rpx;
padding: 2rpx 12rpx;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 170rpx;
flex-shrink: 0;
}
}
</style>