Files
frontend-app/pages/other_package/productInfo/productInfo.vue
T
2026-09-14 13:44:30 +08:00

2710 lines
83 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">
<up-image src="/static/common/left_b_st.png" width="30" height="30" bgColor="#f1f6ff00" v-if="!isShareUrl"
@click="jumpLeft"></up-image>
</view>
<view class="img_comm right_warp">
<up-image v-if="!isShareUrl" src="/static/common/share_st.png" width="30" height="30" bgColor="#f1f6ff00"
@click="jumpShare"></up-image>
</view>
</view>
<!-- 数据详情 -->
<view class="product_content_warp">
<!-- 标题,系列 -->
<view class="content_data">
<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>
<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">
<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>
</view>
<MyBtn v-if="isShareUrl" 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>
<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 === '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 : ''" :price="orderInfo.minPrice ? orderInfo.minPrice : ''"
:tu="swiperList.length && swiperList[0]"></SharePopup>
<EvaluateDialog ref="evaluateDialog" v-if="evaluateDialogShow" :show="evaluateDialogShow"
@close="evaluateDialogShow = false" :evalId="id" :mainGraph="orderInfo.mainGraph" :name="orderInfo.name">
</EvaluateDialog>
<!-- 优惠券弹窗 (图二高保真) -->
<up-popup v-model:show="couponPopupShow" mode="bottom" :round="16" :closeable="true" :safeAreaInsetBottom="true"
@close="couponPopupShow = false">
<view class="goods_coupon_popup_container">
<view class="popup_header">
<text class="popup_title">优惠券</text>
</view>
<!-- 优惠券列表 (可滑动) -->
<scroll-view scroll-y class="popup_coupon_scroll">
<view class="popup_coupon_list">
<view v-for="(coupon, index) in goodsCouponList" :key="coupon.templateId || coupon.id || index"
class="coupon-card" :class="{ 'card-disabled': getCardDisabled(coupon) }">
<!-- 左侧:金额与门槛 -->
<view class="card-left">
<view class="price-box">
<text class="currency">¥</text>
<text class="amount">{{ getDiscountAmount(coupon) }}</text>
</view>
<text class="condition">{{ getThresholdText(coupon) }}</text>
</view>
<!-- 带有上下半圆凹槽的虚线分割线 -->
<view class="divider-wrapper">
<view class="notch top-notch"></view>
<view class="divider-line"></view>
<view class="notch bottom-notch"></view>
</view>
<!-- 右侧:详细内容与操作按钮 -->
<view class="card-right">
<view class="info-content">
<view class="coupon-title">{{ coupon.title }}</view>
<view class="tag-row">
<text class="coupon-tag" :class="{ 'tag-disabled': getCardDisabled(coupon) }">
{{ getTagText(coupon) }}
</text>
<text class="coupon-scope">{{ getScopeText(coupon) }}</text>
</view>
<view class="coupon-validity">{{ getValidityText(coupon) }}</view>
</view>
<!-- 操作按钮 (图二高保真: 立即领取 / 已领取) -->
<view class="btn-box">
<view v-if="!getCardReceived(coupon) && getCouponCanReceive(coupon)" class="action-btn btn-claim"
@click="onReceiveGoodsCoupon(coupon, index)">
立即领取
</view>
<view v-else-if="getCardReceived(coupon)" class="action-btn btn-received">
已领取
</view>
<view v-else class="action-btn btn-disabled">
{{ coupon.receiveStatusText || '已达上限' }}
</view>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 底部确定按钮 -->
<view class="popup_bottom_btn_wrap">
<view class="popup_btn_ok" @click="couponPopupShow = false">
确定
</view>
</view>
</view>
</up-popup>
<!-- 贡献值说明弹窗 -->
<up-popup :show="contributionTipShow" mode="bottom" :round="16" :closeOnClickOverlay="true"
@close="contributionTipShow = false">
<view class="contribution_popup_container">
<!-- 头部 -->
<view class="contribution_popup_header">
<text class="contribution_header_title">贡献值说明</text>
<view class="contribution_header_close" @click="contributionTipShow = false">
<u-icon name="close" size="18" color="#71737c"></u-icon>
</view>
</view>
<!-- 内容主体 -->
<scroll-view scroll-y class="contribution_popup_body">
<view class="contribution_section">
<view class="contribution_section_title">我能获得多少贡献值?</view>
<view class="contribution_text">1.商品详情页中展示为预估获得贡献值。</view>
<view class="contribution_text">2.实际赠送的贡献值将根据订单实付金额及商品贡献系数综合计算,其中商品贡献系数由商品毛利等因素确定。</view>
<view class="contribution_text">3.使用优惠券、发生退款或售后时,贡献值可能相应调整,最终以订单完成后的实际到账为准。</view>
</view>
<view class="contribution_section">
<view class="contribution_section_title">生效条件</view>
<view class="contribution_text">1.收到商品后,在订单中点击【确认收货】时,立马到账。可在【我的-钱包】贡献值进行查看</view>
<view class="contribution_text">2.消费者权益次日到账,可在【我的-钱包】数字积分-消费者权益中进行查看</view>
</view>
<view class="contribution_section">
<view class="contribution_section_title">售后调整</view>
<view class="contribution_text">1.如发生退货/退款,所有的贡献值、消费者权益将会进行扣除。</view>
</view>
</scroll-view>
<!-- 底部按钮 -->
<view class="contribution_popup_footer">
<view class="contribution_btn_confirm" @click="contributionTipShow = false">
我知道了
</view>
</view>
</view>
</up-popup>
<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 } from "@/api/product.js";
import { getGoodsCouponList, receiveCoupon } from "@/api/coupon.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 {
CUSTOMER_SERVICE_PHONE_NUMBER,
CLOUD_SERVICE_PHONE_NUMBER,
SHARE_URL,
} 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;
},
},
components: {
Address,
SharePopup,
Evaluation,
EvaluateDialog,
MyBtn,
},
data() {
return {
id: 0,
shareShow: false,
addressShow: false, // 收件地址选择
couponPopupShow: false, // 优惠券弹框显示 (图二)
contributionTipShow: false, // 贡献值说明弹框显示
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: "",
};
},
onBackPress(options) {
console.log("我是侧滑", options)
// #ifdef APP-PLUS
if (options.from === 'backbutton' && this.isSplash) {
// 1. 阻止默认 pop
plus.webview.currentWebview().canBack = false // 关键句
uni.reLaunch({ url: '/pages/home/home' }) // 举例:回首页
return true // 返回 true 即“我处理过了”
}
// #endif
},
onLoad(options) {
// #ifdef MP-WEIXIN
// 默认禁止小程序右上角的分享菜单
// uni.hideShareMenu();
// #endif
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) {
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;
},
onUnload() {
timer && clearTimeout(timer);
timer = null;
},
onShareAppMessage(res) {
const queryStr = this.buildShareParams();
return {
title: this.orderInfo.name || "商品分享",
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();
return {
title: this.orderInfo.name || "商品分享",
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}`,
});
},
buildShareParams() {
const params = [];
if (this.id) params.push(`id=${this.id}`);
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) {
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 {
let last;
// qsc.setRect({ x: 0, y: 0,r:12, height: 960, width: 620});
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,
});
// last = await qsc.drawImg({
// val:'https://niu8.letaocm.top/17557729915067836.jpg',
// x: 20,
// y: 20,
// height: 270,
// width: 270,
// })
// #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() {
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';
}
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;
this.detailRich = this.orderInfo.detailInfo.replace(
/<img([^>]*?)>/gi,
'<img$1 style="max-width:100%;height:auto;display:block;" loading="lazy">'
);
this.fetchGoodsCouponList();
}
},
/**
* 来自商城的订单
*/
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;
}
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) {
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, // 商品数量
};
console.log("selectTempl", selectTempl);
if (this.isShowCart == 1 || this.isCoupon) {
console.log("活动类型params---秦星星", params, this.isCoupon, this.isShowCart == 1);
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 (!url) {
url = `/pages/order_package/order_submit/order_submit?sed=${JSON.stringify(
params
)}&address=${this.selectAddress?.id}`;
this.specificationShow = false;
}
console.log("活动类型params---秦星星--1", url);
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) 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",
});
}
},
getDiscountAmount(coupon) {
return coupon.discountAmount !== undefined ? coupon.discountAmount : (coupon.amount || 0);
},
getThresholdText(coupon) {
const threshold = coupon.thresholdAmount !== undefined ? Number(coupon.thresholdAmount) : 0;
if (threshold === 0) {
return "无门槛";
}
return `满${threshold}元可用`;
},
getTagText(coupon) {
if (coupon.scopeType === 1) return "通用券";
if (coupon.scopeType === 2) return "商品券";
if (coupon.scopeType === 3 || coupon.scopeType === 4) return "品类券";
if (coupon.scopeText && coupon.scopeText.includes("通用")) return "通用券";
return "通用券";
},
getScopeText(coupon) {
if (coupon.scopeText) return coupon.scopeText;
if (coupon.scopeType === 1) return "所有商品可用";
if (coupon.scopeType === 2) return "指定商品可用";
if (coupon.scopeType === 3) return "指定类目可用";
if (coupon.scopeType === 4) return "指定专区商品可用";
return "所有商品可用";
},
getValidityText(coupon) {
if (coupon.validDays && coupon.validDays > 0) {
return `领取之日起${coupon.validDays}天内有效`;
}
if (coupon.useEndTime) {
return `有效期至 ${this.formatTime(coupon.useEndTime)}`;
}
if (coupon.receiveEndTime) {
return `截止时间 ${this.formatTime(coupon.receiveEndTime)}`;
}
return "领取之日起30天内有效";
},
getCardReceived(coupon) {
return coupon.received || coupon.receiveStatus === 1;
},
getCouponCanReceive(coupon) {
return coupon.canReceive !== false && coupon.receiveStatus !== 2;
},
getCardDisabled(coupon) {
return coupon.receiveStatus === 2 || (coupon.canReceive === false && !coupon.received && coupon.receiveStatus !== 1);
},
formatTime(time) {
if (!time) return "";
if (typeof time === "string" && time.includes("-")) return time;
const date = new Date(Number(time));
if (isNaN(date.getTime())) return String(time);
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, "0");
const d = String(date.getDate()).padStart(2, "0");
const hh = String(date.getHours()).padStart(2, "0");
const mm = String(date.getMinutes()).padStart(2, "0");
const ss = String(date.getSeconds()).padStart(2, "0");
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
},
},
};
</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;
}
.left_warp {
left: 40rpx;
}
.right_warp {
right: 40rpx;
}
}
.product_content_warp {
// background: linear-gradient(0deg, #F9F9F9 0%, #FFFFFF 100%);
border-radius: 30rpx 30rpx 0rpx 24rpx;
padding: 20rpx;
.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;
}
}
}
.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;
}
}
.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;
}
}
/* 领券弹窗组件 (图二高保真) */
.goods_coupon_popup_container {
background-color: #ffffff;
border-radius: 32rpx 32rpx 0 0;
padding: 32rpx 24rpx 40rpx 24rpx;
box-sizing: border-box;
display: flex;
flex-direction: column;
.popup_header {
position: relative;
text-align: center;
margin-bottom: 24rpx;
.popup_title {
font-size: 32rpx;
font-weight: bold;
color: #333333;
}
}
.popup_coupon_scroll {
max-height: 700rpx;
min-height: 300rpx;
.popup_coupon_list {
padding: 8rpx 0;
}
}
.popup_bottom_btn_wrap {
margin-top: 24rpx;
padding: 0 16rpx;
.popup_btn_ok {
width: 100%;
height: 84rpx;
line-height: 84rpx;
text-align: center;
background: linear-gradient(135deg, #a855f7 0%, #7a35f6 100%);
color: #ffffff;
font-size: 30rpx;
font-weight: bold;
border-radius: 42rpx;
box-shadow: 0 8rpx 20rpx rgba(122, 53, 246, 0.25);
transition: transform 0.1s ease;
&:active {
transform: scale(0.98);
}
}
}
/* 弹窗内的优惠券卡片高保真样式 */
.coupon-card {
position: relative;
display: flex;
align-items: center;
background: rgba(253, 28, 36, 0.06);
border-radius: 16rpx;
margin-bottom: 20rpx;
box-sizing: border-box;
&.card-disabled {
background: #f5f5f5 !important;
.card-left {
.price-box {
color: #a0a0a0 !important;
}
.condition {
color: #999999 !important;
}
}
.coupon-title {
color: #333333 !important;
}
}
.card-left {
width: 170rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 24rpx 0;
.price-box {
display: flex;
align-items: baseline;
color: #ff2442;
.currency {
font-size: 28rpx;
font-weight: bold;
margin-right: 2rpx;
}
.amount {
font-size: 60rpx;
font-weight: 700;
line-height: 1;
}
}
.condition {
font-size: 24rpx;
color: #ff2442;
margin-top: 10rpx;
font-weight: 500;
}
}
.divider-wrapper {
position: relative;
width: 2rpx;
align-self: stretch;
.divider-line {
height: 100%;
border-left: 2rpx dashed #fca5a5;
}
.notch {
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 24rpx;
height: 24rpx;
background-color: #ffffff;
border-radius: 50%;
z-index: 10;
}
.top-notch {
top: -12rpx;
}
.bottom-notch {
bottom: -12rpx;
}
}
.card-disabled .divider-line {
border-left: 2rpx dashed #dddddd !important;
}
.card-right {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 20rpx 20rpx 24rpx;
.info-content {
flex: 1;
min-width: 0;
margin-right: 12rpx;
.coupon-title {
font-size: 30rpx;
font-weight: bold;
color: #1a1a1a;
margin-bottom: 10rpx;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tag-row {
display: flex;
align-items: center;
margin-bottom: 10rpx;
white-space: nowrap;
.coupon-tag {
display: inline-block;
font-size: 20rpx;
color: #ff2442;
background: #ffe4e6;
border: 1px solid #ff99a4;
border-radius: 6rpx;
padding: 2rpx 10rpx;
margin-right: 10rpx;
line-height: 1.2;
font-weight: 500;
flex-shrink: 0;
white-space: nowrap;
&.tag-disabled {
color: #888888 !important;
background: #eeeeee !important;
border: 1px solid #cccccc !important;
}
}
.coupon-scope {
font-size: 22rpx;
color: #666666;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.coupon-validity {
font-size: 22rpx;
color: #999999;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.btn-box {
flex-shrink: 0;
.action-btn {
width: 130rpx;
height: 54rpx;
line-height: 54rpx;
text-align: center;
font-size: 24rpx;
font-weight: bold;
border-radius: 8rpx;
box-sizing: border-box;
&.btn-claim {
background: #ff1e38;
color: #ffffff;
}
&.btn-received {
background: #ffe8eb;
color: #ff2442;
border: 1px solid #ff4d5e;
line-height: 52rpx;
font-weight: 500;
}
&.btn-disabled {
background: #eeeeee;
color: #bbbbbb;
}
}
}
}
}
}
/* 贡献值说明弹窗高保真样式 */
.contribution_popup_container {
background: #ffffff;
border-radius: 32rpx 32rpx 0 0;
display: flex;
flex-direction: column;
max-height: 80vh;
box-sizing: border-box;
.contribution_popup_header {
display: flex;
justify-content: center;
align-items: center;
position: relative;
padding: 32rpx 32rpx 16rpx;
.contribution_header_title {
font-weight: 500;
font-size: 32rpx;
color: #71737c;
}
.contribution_header_close {
position: absolute;
right: 32rpx;
top: 28rpx;
width: 48rpx;
height: 48rpx;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
}
.contribution_popup_body {
max-height: 60vh;
padding: 10rpx 36rpx 20rpx;
box-sizing: border-box;
.contribution_section {
margin-bottom: 28rpx;
&:last-child {
margin-bottom: 10rpx;
}
.contribution_section_title {
font-size: 34rpx;
font-weight: bold;
color: #111111;
margin-bottom: 20rpx;
line-height: 1.4;
}
.contribution_text {
font-size: 28rpx;
line-height: 1.75;
color: #666666;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
}
}
}
.contribution_popup_footer {
padding: 20rpx 36rpx;
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
background: #ffffff;
box-sizing: border-box;
.contribution_btn_confirm {
width: 100%;
height: 88rpx;
background: #7934f6;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #ffffff;
font-weight: 500;
cursor: pointer;
transition: opacity 0.15s ease;
&:active {
opacity: 0.85;
}
}
}
}
</style>