2925 lines
87 KiB
Vue
2925 lines
87 KiB
Vue
<template>
|
||
<view data-component-name="diy-category-item" class="item-wrap" :class="type">
|
||
<block v-if="type == 'category' && category.child_list && category.child_list.length">
|
||
<view class="category-adv" v-if="category.image_adv" @tap.stop="diyRedirectTo(category.link_url)">
|
||
<image :src="$util.img(category.image_adv)" mode="widthFix" show-menu-by-longpress />
|
||
</view>
|
||
|
||
<block v-if="value.template == 99">
|
||
<scroll-view scroll-x="true" class="second-category-btns" :scroll-with-animation="true">
|
||
<view class="second-category-btn"
|
||
v-for="(one, oneIndex) in category.child_list"
|
||
:key="oneIndex"
|
||
:class="{ selected: selectedSecondCategory == one.category_id }"
|
||
@tap="selectSecondCategory(one)">
|
||
{{ one.category_name }}
|
||
</view>
|
||
</scroll-view>
|
||
<view class="category-goods-list" v-if="selectedSecondCategory">
|
||
<view class="goods-list show" :class="{ 'double-column': !isList, 'single-column': isList }" :data-template="value.template" v-if="secondCategoryGoodsList.length">
|
||
<view class="goods-item" v-for="(item, index) in secondCategoryGoodsList" :key="index">
|
||
<view class="goods-img" @tap.stop="toDetail(item)">
|
||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" show-menu-by-longpress />
|
||
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
|
||
</view>
|
||
<view class="info-wrap">
|
||
<view class="name-wrap" @tap.stop="toDetail(item)">
|
||
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
||
</view>
|
||
<view class="price-wrap">
|
||
<view class="discount-price">
|
||
<block v-if="item.isinformation == 0">
|
||
<text class="unit price-style small">{{ $lang('common.currencySymbol') }}</text>
|
||
<text class="price price-style large">{{ parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
|
||
<text class="unit price-style small">.{{ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||
</block>
|
||
</view>
|
||
<view class="delete-price font-size-activity-tag color-tip price-font"
|
||
v-if="item.isinformation == 0 && showMarketPrice(item)">
|
||
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
|
||
<text>{{ showMarketPrice(item) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="footer-wrap">
|
||
<view class="consult-now-btn-wrap" v-if="item.isinformation == 1">
|
||
<view class="consult-now-btn" @tap.stop="sendToAIChat(item)">
|
||
{{ $lang('components-diy.diy-category-item.consultNow') }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="category-goods-empty" v-if="selectedSecondCategory && !secondCategoryGoodsList.length && !loading">
|
||
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" show-menu-by-longpress></image>
|
||
<view class="tips">{{ $lang('components-diy.diy-category.noCategory') }}</view>
|
||
</view>
|
||
</block>
|
||
|
||
<!-- 模板1: 后台配置三级分类 + 有子分类时, 按二级分类分别显示各自的商品(图 f325d5a779 样式)
|
||
只渲染 filteredGroupedChildren(过滤掉无商品的二级分类), 没商品时整块(标题+网格)都不显示 -->
|
||
<block v-else-if="isGroupedDisplay">
|
||
<block v-for="(one, oneIndex) in filteredGroupedChildren" :key="one.category_id">
|
||
<view :class="['category-title', 'category-title-with-lines']">{{ one.category_name }}</view>
|
||
<view class="template1-goods-wrap">
|
||
<view class="goods-list show" :class="{ 'double-column': !isList, 'single-column': isList }" v-if="template1ChildGoodsList[one.category_id] && template1ChildGoodsList[one.category_id].length">
|
||
<view class="goods-item" v-for="(item, index) in template1ChildGoodsList[one.category_id]" :key="index">
|
||
<view class="goods-img" @tap.stop="toDetail(item)">
|
||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="template1ImgError(one.category_id, index)" show-menu-by-longpress />
|
||
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
|
||
</view>
|
||
<view class="info-wrap">
|
||
<view class="name-wrap" @tap.stop="toDetail(item)">
|
||
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
||
</view>
|
||
<view class="price-wrap">
|
||
<view class="discount-price">
|
||
<block v-if="item.isinformation == 0">
|
||
<text class="unit price-style small">{{ $lang('common.currencySymbol') }}</text>
|
||
<text class="price price-style large">{{ parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
|
||
<text class="unit price-style small">.{{ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||
</block>
|
||
</view>
|
||
<view class="delete-price font-size-activity-tag color-tip price-font"
|
||
v-if="item.isinformation == 0 && showMarketPrice(item)">
|
||
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
|
||
<text>{{ showMarketPrice(item) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="footer-wrap footer-wrap-template1">
|
||
<view class="detail-btn" @tap.stop="toDetail(item)">{{ $lang('components-diy.diy-category-item.consultNow') }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- filteredGroupedChildren 已保证加载完成且 length > 0, 此处无需再渲染空状态 -->
|
||
</view>
|
||
</block>
|
||
</block>
|
||
|
||
<!-- 模板1: 后台配置二级分类 + 有子分类时, 在一级分类标题下以双列网格显示所有商品(图 c2df0f3d29 样式)
|
||
模板5 不走这里: 它有专属分支 value.template == 5(后台"展示分类"决定渲染商品网格或二级分类网格) -->
|
||
<view v-else-if="value.template == 1">
|
||
<view :class="['category-title', 'category-title-with-lines']">{{ category.category_name }}</view>
|
||
<view class="template1-goods-wrap">
|
||
<view class="goods-list show" :class="{ 'double-column': !isList, 'single-column': isList }" v-if="template1GoodsList.length">
|
||
<view class="goods-item" v-for="(item, index) in template1GoodsList" :key="index">
|
||
<view class="goods-img" @tap.stop="toDetail(item)">
|
||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="template1ImgError(index)" show-menu-by-longpress />
|
||
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
|
||
</view>
|
||
<view class="info-wrap">
|
||
<view class="name-wrap" @tap.stop="toDetail(item)">
|
||
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
||
</view>
|
||
<view class="price-wrap">
|
||
<view class="discount-price">
|
||
<block v-if="item.isinformation == 0">
|
||
<text class="unit price-style small">{{ $lang('common.currencySymbol') }}</text>
|
||
<text class="price price-style large">{{ parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
|
||
<text class="unit price-style small">.{{ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||
</block>
|
||
</view>
|
||
<view class="delete-price font-size-activity-tag color-tip price-font"
|
||
v-if="item.isinformation == 0 && showMarketPrice(item)">
|
||
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
|
||
<text>{{ showMarketPrice(item) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="footer-wrap footer-wrap-template1">
|
||
<view class="detail-btn" @tap.stop="toDetail(item)">{{ $lang('components-diy.diy-category-item.consultNow') }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="category-goods-empty" v-else-if="!template1Loading">
|
||
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" show-menu-by-longpress></image>
|
||
<view class="tips">暂无商品</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 模板5 专属: 后台"展示分类"决定右侧渲染内容(对应图2/图3)
|
||
二级分类(value.level == 2) → 一级分类标题 + 商品网格(图2)
|
||
三级分类(value.level == 3) → 一级分类标题 + 二级分类网格(图3) -->
|
||
<block v-else-if="value.template == 5">
|
||
<block v-if="value.level == 2">
|
||
<view :class="['category-title', '']">{{ category.category_name }}</view>
|
||
<view class="template1-goods-wrap">
|
||
<view class="goods-list show" :class="{ 'double-column': !isList, 'single-column': isList }" v-if="template1GoodsList.length">
|
||
<view class="goods-item" v-for="(item, index) in template1GoodsList" :key="index">
|
||
<view class="goods-img" @tap.stop="toDetail(item)">
|
||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="template1ImgError(index)" show-menu-by-longpress />
|
||
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
|
||
</view>
|
||
<view class="info-wrap">
|
||
<view class="name-wrap" @tap.stop="toDetail(item)">
|
||
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
||
</view>
|
||
<view class="price-wrap">
|
||
<view class="discount-price">
|
||
<block v-if="item.isinformation == 0">
|
||
<text class="unit price-style small">{{ $lang('common.currencySymbol') }}</text>
|
||
<text class="price price-style large">{{ parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
|
||
<text class="unit price-style small">.{{ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||
</block>
|
||
</view>
|
||
<view class="delete-price font-size-activity-tag color-tip price-font"
|
||
v-if="item.isinformation == 0 && showMarketPrice(item)">
|
||
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
|
||
<text>{{ showMarketPrice(item) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="footer-wrap footer-wrap-template1">
|
||
<view class="detail-btn" @tap.stop="toDetail(item)">{{ $lang('components-diy.diy-category-item.consultNow') }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="category-goods-empty" v-else-if="!template1Loading">
|
||
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" show-menu-by-longpress></image>
|
||
<view class="tips">暂无商品</view>
|
||
</view>
|
||
</view>
|
||
</block>
|
||
<block v-else>
|
||
<!-- 三级分类(及更深层级): 一级分类标题 + 二级分类网格(图3) -->
|
||
<view :class="['category-title', '']">{{ category.category_name }}</view>
|
||
<view class="category-list">
|
||
<view class="category-item" v-for="(one, oneIndex) in category.child_list" :key="oneIndex"
|
||
@tap.stop="$util.redirectTo('/pages_goods/list', { category_id: one.category_id })">
|
||
<view class="img-box">
|
||
<image :src="categoryImg(one.image)" mode="aspectFit" @error="categoryImgError(2, oneIndex, -1)" show-menu-by-longpress />
|
||
</view>
|
||
<view class="name">{{ one.category_name }}</view>
|
||
</view>
|
||
</view>
|
||
</block>
|
||
</block>
|
||
</block>
|
||
|
||
<!-- 模板1: 无子分类时(外层块不进入), 在一级分类标题下以双列网格显示所有商品(图 c2df0f3d29 样式) -->
|
||
<view v-else-if="(value.template == 1 || value.template == 5) && type == 'category'">
|
||
<view :class="['category-title', 'category-title-with-lines']">{{ category.category_name }}</view>
|
||
<view class="template1-goods-wrap">
|
||
<view class="goods-list show" :class="{ 'double-column': !isList, 'single-column': isList }" v-if="template1GoodsList.length">
|
||
<view class="goods-item" v-for="(item, index) in template1GoodsList" :key="index">
|
||
<view class="goods-img" @tap.stop="toDetail(item)">
|
||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="template1ImgError(index)" show-menu-by-longpress />
|
||
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
|
||
</view>
|
||
<view class="info-wrap">
|
||
<view class="name-wrap" @tap.stop="toDetail(item)">
|
||
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
||
</view>
|
||
<view class="price-wrap">
|
||
<view class="discount-price">
|
||
<block v-if="item.isinformation == 0">
|
||
<text class="unit price-style small">{{ $lang('common.currencySymbol') }}</text>
|
||
<text class="price price-style large">{{ parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
|
||
<text class="unit price-style small">.{{ parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||
</block>
|
||
</view>
|
||
<view class="delete-price font-size-activity-tag color-tip price-font"
|
||
v-if="item.isinformation == 0 && showMarketPrice(item)">
|
||
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
|
||
<text>{{ showMarketPrice(item) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="footer-wrap footer-wrap-template1">
|
||
<view class="detail-btn" @tap.stop="toDetail(item)">{{ $lang('components-diy.diy-category-item.consultNow') }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="category-goods-empty" v-else-if="!template1Loading">
|
||
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" show-menu-by-longpress></image>
|
||
<view class="tips">暂无商品</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<block v-if="type == 'goods'">
|
||
<view class="categoty-goods-wrap" v-if="loadType == 'part'"
|
||
:style="'padding-top:' + (value.search ? 0 : '20rpx')">
|
||
<!-- 分类筛选 -->
|
||
<block v-if="(value.template == 4 ? (currentOneCategory && currentOneCategory.child_list) : category.child_list) && value.goodsLevel == 2">
|
||
<view class="screen-category-wrap">
|
||
<scroll-view scroll-x="true" class="screen-category"
|
||
:class="{ 'screen-category-4': value.template == 4 }" :scroll-with-animation="true"
|
||
:scroll-into-view="scrollIntoView">
|
||
<view class="item" id="category-2--1" :class="{ selected: categoryId == -1 }"
|
||
@tap.stop="selectCategory(-1)">{{ $lang('components-diy.diy-category-item.all') }}</view>
|
||
<view class="item" :id="'category-2-' + oneIndex"
|
||
:class="{ selected: categoryId == oneIndex }" @tap.stop="selectCategory(oneIndex)"
|
||
v-for="(one, oneIndex) in (value.template == 4 ? currentOneCategory.child_list : category.child_list)" :key="oneIndex">
|
||
{{ one.category_name }}
|
||
</view>
|
||
</scroll-view>
|
||
<view class="iconfont icon-unfold" @tap.stop="$refs.screenCategoryPopup.open()"></view>
|
||
</view>
|
||
<uni-popup type="top" ref="screenCategoryPopup">
|
||
<view class="screen-category-popup" @tap.stop="$refs.screenCategoryPopup.close()">
|
||
<scroll-view scroll-y="true" class="screen-category"
|
||
:class="{ 'screen-category-4': value.template == 4 }">
|
||
<view class="title">{{ $lang('components-diy.diy-category-item.all') }}</view>
|
||
<view class="item" :class="{ selected: categoryId == oneIndex }"
|
||
@tap.stop="selectCategory(oneIndex)" v-for="(one, oneIndex) in (value.template == 4 ? currentOneCategory.child_list : category.child_list)"
|
||
:key="oneIndex">
|
||
{{ one.category_name }}
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</uni-popup>
|
||
</block>
|
||
<!---->
|
||
<scroll-view scroll-y="true" class="scroll-goods-view" lower-threshold="300" :scroll-top="scrollTop"
|
||
@scrolltolower="scrolltolower" @touchstart="touchstart" @touchend="touchend" @scroll="listenScroll">
|
||
|
||
<!--一级分类展示商品显示-->
|
||
<view class="category-title" v-if="value.template != 3 && value.template != 4" :style="{ fontSize: (themeStyle && themeStyle.diy_category_item_goods_title_size) || '28rpx', paddingBottom: 0 }">
|
||
{{ category.category_name }}
|
||
</view>
|
||
|
||
<!--一级-->
|
||
<view class="goods-list"
|
||
:class="{ 'double-column': !isList, 'single-column': isList, 'show': true }"
|
||
:data-template="value.template">
|
||
<block v-if="goodsList.length">
|
||
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
|
||
<view class="goods-img" @tap.stop="toDetail(item)">
|
||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)" show-menu-by-longpress />
|
||
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}
|
||
</view>
|
||
</view>
|
||
<!-- 模板3: 卡片式布局 - 上方名称, 下方价格+按钮 -->
|
||
<view class="info-wrap" v-if="value.template == 3">
|
||
<view class="name-wrap" @tap.stop="toDetail(item)">
|
||
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
||
</view>
|
||
<view class="info-bottom">
|
||
<view class="price-wrap">
|
||
<view class="discount-price">
|
||
<block v-if="item.isinformation == 0">
|
||
<text class="unit price-style small">{{ $lang('common.currencySymbol') }}</text>
|
||
<text class="price price-style large">{{
|
||
parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
|
||
<text class="unit price-style small">.{{
|
||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||
</block>
|
||
</view>
|
||
<view class="delete-price font-size-activity-tag color-tip price-font"
|
||
v-if="showMarketPrice(item)">
|
||
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
|
||
<text>{{ showMarketPrice(item) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="buy-now-btn color-base-bg" @tap.stop="toDetail(item)">
|
||
{{ $lang('components-diy.diy-category-item.buyNow') }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 其他模板: 保留原结构 -->
|
||
<view class="info-wrap" v-else>
|
||
<view class="name-wrap" @tap.stop="toDetail(item)">
|
||
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
||
</view>
|
||
<view class="price-wrap">
|
||
<view class="discount-price">
|
||
<block v-if="item.isinformation == 0">
|
||
<text class="unit price-style small">{{ $lang('common.currencySymbol') }}</text>
|
||
<text class="price price-style large">{{
|
||
parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
|
||
<text class="unit price-style small">.{{
|
||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||
</block>
|
||
</view>
|
||
<view class="member-price-tag"
|
||
v-if="item.member_price && item.member_price == showPrice(item) && value.template != 4">
|
||
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix" show-menu-by-longpress />
|
||
</view>
|
||
<view class="member-price-tag" v-else-if="item.promotion_type == 1 && value.template != 4">
|
||
<image :src="$util.img('public/uniapp/index/discount.png')"
|
||
mode="widthFix" show-menu-by-longpress />
|
||
</view>
|
||
<!-- 模板2/4: 删除线价格(放在现价下方, 样式见 price-wrap 的 delete-price 规则) -->
|
||
<view class="delete-price font-size-activity-tag color-tip price-font"
|
||
v-if="showMarketPrice(item) && (value.template == 2 || value.template == 4)">
|
||
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
|
||
<text>{{ showMarketPrice(item) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="footer-wrap">
|
||
<view class="pro-info">
|
||
<view class="delete-price font-size-activity-tag color-tip price-font"
|
||
v-if="showMarketPrice(item) && value.template != 4 && value.template != 2">
|
||
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
|
||
<text>{{ showMarketPrice(item) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="consult-now-btn-wrap" v-if="item.isinformation >= 1">
|
||
<view class="consult-now-btn" @tap.stop="sendToAIChat(item)">
|
||
{{ $lang('components-diy.diy-category-item.consultNow') }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 风格2/模板4: 右侧+按钮/择规格按钮 -->
|
||
<view class="right-wrap" v-if="(value.template == 2 || value.template == 4) && showAddCart && item.isinformation == 0">
|
||
<block v-if="item.is_virtual">
|
||
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">{{ $lang('components-diy.diy-category-item.buyNow') }}</view>
|
||
</block>
|
||
<block v-else>
|
||
<view v-if="item.goods_spec_format" class="color-base-bg select-sku"
|
||
@tap.stop="selectSku(item)">
|
||
<text>{{ $lang('components-diy.diy-category-item.selectSpecs') }}</text>
|
||
<text class="num-tag" v-if="item.num">{{ item.num }}</text>
|
||
</view>
|
||
<block v-else>
|
||
<block
|
||
v-if="cartList['goods_' + item.goods_id] && cartList['goods_' + item.goods_id]['sku_' + item.sku_id]">
|
||
<view class="num-action reduce" @tap.stop="reduce(item)">
|
||
<text class="iconfont icon-jian"></text>
|
||
</view>
|
||
<view class="num">{{ cartList['goods_' + item.goods_id]['sku_' +
|
||
item.sku_id].num }}</view>
|
||
<view class="num-action" :id="'cart-num-' + index"
|
||
@tap.stop="increase($event, item)">
|
||
<text class="iconfont icon-jia"></text>
|
||
<view class="click-event"></view>
|
||
</view>
|
||
</block>
|
||
<view class="num-action" v-else :id="'cart-num-' + index"
|
||
@tap.stop="increase($event, item, 0)">
|
||
<text class="iconfont icon-jia"></text>
|
||
<view class="click-event"></view>
|
||
</view>
|
||
</block>
|
||
</block>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</block>
|
||
<view class="category-empty" v-else>
|
||
<image :src="$util.img('public/uniapp/category/empty.png')" mode="widthFix" show-menu-by-longpress />
|
||
</view>
|
||
<!-- <view class="end-tips" ref="endTips" v-if="last && (categoryId == -1 || !category.child_list || (category.child_list && categoryId == category.child_list.length - 1))">已经到底了~</view>
|
||
<view class="end-tips" ref="endTips" v-else @tap.stop="switchCategory('next')">
|
||
<text class="iconfont icon-xiangshangzhanhang"></text>
|
||
上滑查看下一分类
|
||
</view> -->
|
||
</view>
|
||
|
||
|
||
<loading-cover ref="loadingCover" :init-show="loading"></loading-cover>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<block v-if="loadType == 'all' && goodsList.length">
|
||
<view class="goods-list" :class="{ 'double-column': !isList, 'single-column': isList }"
|
||
:data-template="value.template">
|
||
<view class="goods-item" v-for="(item, index) in goodsList" :key="index">
|
||
<view class="goods-img" @tap.stop="toDetail(item)">
|
||
<image :src="goodsImg(item.goods_image)" mode="widthFix" @error="imgError(index)"
|
||
:lazy-load="true" show-menu-by-longpress />
|
||
<view class="color-base-bg goods-tag" v-if="item.label_name">{{ item.label_name }}</view>
|
||
</view>
|
||
<!-- 模板3: 卡片式布局 - 上方名称, 下方价格+按钮 -->
|
||
<view class="info-wrap" v-if="value.template == 3">
|
||
<view class="name-wrap" @tap.stop="toDetail(item)">
|
||
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
||
</view>
|
||
<view class="info-bottom">
|
||
<view class="price-wrap">
|
||
<view class="discount-price">
|
||
<block v-if="item.isinformation == 0">
|
||
<text class="unit price-style small">{{ $lang('common.currencySymbol') }}</text>
|
||
<text class="price price-style large">{{
|
||
parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
|
||
<text class="unit price-style small">.{{
|
||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||
</block>
|
||
</view>
|
||
<view class="delete-price font-size-activity-tag color-tip price-font"
|
||
v-if="showMarketPrice(item)">
|
||
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
|
||
<text>{{ showMarketPrice(item) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="buy-now-btn color-base-bg" @tap.stop="toDetail(item)">
|
||
{{ $lang('components-diy.diy-category-item.buyNow') }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 其他模板: 保留原结构 -->
|
||
<view class="info-wrap" v-else>
|
||
<view class="name-wrap" @tap.stop="toDetail(item)">
|
||
<view class="goods-name">{{ isEnEnv ? item.en_goods_name : item.goods_name }}</view>
|
||
</view>
|
||
<view class="price-wrap">
|
||
<view class="discount-price">
|
||
<block v-if="item.isinformation == 0">
|
||
<text class="unit price-style small">{{ $lang('common.currencySymbol') }}</text>
|
||
<text class="price price-style large">{{
|
||
parseFloat(showPrice(item)).toFixed(2).split('.')[0] }}</text>
|
||
<text class="unit price-style small">.{{
|
||
parseFloat(showPrice(item)).toFixed(2).split('.')[1] }}</text>
|
||
</block>
|
||
</view>
|
||
<view class="member-price-tag"
|
||
v-if="item.member_price && item.member_price == showPrice(item)">
|
||
<image :src="$util.img('public/uniapp/index/VIP.png')" mode="widthFix" show-menu-by-longpress />
|
||
</view>
|
||
<view class="member-price-tag" v-else-if="item.promotion_type == 1">
|
||
<image :src="$util.img('public/uniapp/index/discount.png')" mode="widthFix" show-menu-by-longpress />
|
||
</view>
|
||
</view>
|
||
<view class="footer-wrap">
|
||
<view class="pro-info">
|
||
<view class="delete-price font-size-activity-tag color-tip price-font"
|
||
v-if="showMarketPrice(item)">
|
||
<text class="unit">{{ $lang('common.currencySymbol') }}</text>
|
||
<text>{{ showMarketPrice(item) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="consult-now-btn-wrap" v-if="item.isinformation >= 1">
|
||
<view class="consult-now-btn" @tap.stop="sendToAIChat(item)">
|
||
{{ $lang('components-diy.diy-category-item.consultNow') }}
|
||
</view>
|
||
</view>
|
||
|
||
<view class="right-wrap" v-if="(value.template == 2 || value.template == 4) && showAddCart && item.isinformation == 0">
|
||
<block v-if="item.is_virtual">
|
||
<view class="color-base-bg select-sku" @tap.stop="toDetail(item)">{{ $lang('components-diy.diy-category-item.buyNow') }}</view>
|
||
</block>
|
||
<block v-else>
|
||
<view v-if="item.goods_spec_format" class="color-base-bg select-sku"
|
||
@tap.stop="selectSku(item)">
|
||
<text>{{ $lang('components-diy.diy-category-item.selectSpecs') }}</text>
|
||
<text class="num-tag" v-if="item.num">{{ item.num }}</text>
|
||
</view>
|
||
<block v-else>
|
||
<block
|
||
v-if="cartList['goods_' + item.goods_id] && cartList['goods_' + item.goods_id]['sku_' + item.sku_id]">
|
||
<view class="num-action reduce" @tap.stop="reduce(item)">
|
||
<text class="iconfont icon-jian"></text>
|
||
</view>
|
||
<view class="num">{{ cartList['goods_' + item.goods_id]['sku_' +
|
||
item.sku_id].num }}</view>
|
||
<view class="num-action" :id="'cart-num-' + index"
|
||
@tap.stop="increase($event, item)">
|
||
<text class="iconfont icon-jia"></text>
|
||
<view class="click-event"></view>
|
||
</view>
|
||
</block>
|
||
<view class="num-action" v-else :id="'cart-num-' + index"
|
||
@tap.stop="increase($event, item, 0)">
|
||
<text class="iconfont icon-jia"></text>
|
||
<view class="click-event"></view>
|
||
</view>
|
||
</block>
|
||
</block>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</block>
|
||
</block>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import DiyMinx from './minx.js'
|
||
|
||
// 商品分类项
|
||
export default {
|
||
name: 'diy-category-item',
|
||
props: {
|
||
category: {
|
||
type: Object
|
||
},
|
||
value: {
|
||
type: Object,
|
||
default: () => {
|
||
return {};
|
||
}
|
||
},
|
||
index: {
|
||
type: Number,
|
||
default: 0
|
||
},
|
||
select: {
|
||
type: Number,
|
||
default: 0
|
||
},
|
||
oneCategorySelect: {
|
||
type: Number,
|
||
default: 0
|
||
},
|
||
last: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
isList: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
lastSecondCategoryName: {
|
||
type: String,
|
||
default: ''
|
||
},
|
||
currentOneCategory: {
|
||
type: Object,
|
||
default: null
|
||
},
|
||
},
|
||
mixins: [DiyMinx],
|
||
data() {
|
||
return {
|
||
level: 3,
|
||
categoryId: -1,
|
||
loading: false,
|
||
goodsList: [],
|
||
pageIndex: 0,
|
||
pageSize: 10,
|
||
// 已加载完成的分类ID, 用于避免重复拉取同一分类的商品
|
||
_loadedGoodsKey: null,
|
||
totalPage: 1,
|
||
touchstartPosition: 0,
|
||
scrollType: 'top',
|
||
contentWrapHeight: 0,
|
||
scrollIntoView: 'category-2--1',
|
||
scrollTop: 0,
|
||
loadType: '',
|
||
timeout: {},
|
||
cartEditRepeat: false,
|
||
selectedSecondCategory: null,
|
||
secondCategoryGoodsList: [],
|
||
// 模板1专用:选中的分类ID和商品列表
|
||
selectedCategoryId: null,
|
||
template1GoodsList: [],
|
||
template1Loading: false,
|
||
// 模板1专用(categoryLevel == 3): 按二级分类ID分别存储各自商品列表
|
||
// key: child.category_id, value: 商品数组
|
||
template1ChildGoodsList: {},
|
||
template1ChildLoading: {}
|
||
};
|
||
},
|
||
computed: {
|
||
// 后台开关:加购按钮 = cartBtn; 未下发 cartBtn 时兼容旧字段 quickBuy
|
||
// 后台开启就显示, 关闭就不显示
|
||
showAddCart() {
|
||
const t = this.getDiyToggle(['showAddCart', 'show_add_cart', 'cartBtn', 'cart_btn']);
|
||
if (t !== undefined) return t;
|
||
const legacy = this.getDiyToggle(['quickBuy', 'quick_buy']);
|
||
return legacy !== undefined ? legacy : false;
|
||
},
|
||
/**
|
||
* 当前分类层级 (参考 template 2 的 goodsLevel 模式)
|
||
* 优先级: 数据层级 > 后台配置 > 兜底
|
||
* 1. 先检测数据实际支持几级 (二级分类下是否还有 child_list)
|
||
* 2. 后台配置的 level 不能超过数据支持的层级
|
||
* (数据只有 2 级时,就算后台配 3 也强制用 2,避免出现空标题列表)
|
||
* 3. 都没配置就按数据层级来
|
||
*/
|
||
categoryLevel() {
|
||
// 先检测数据层级
|
||
let dataLevel = 2; // 兜底
|
||
if (this.category && this.category.child_list && this.category.child_list.length > 0) {
|
||
const hasThirdLevel = this.category.child_list.some(item =>
|
||
item.child_list && item.child_list.length > 0
|
||
);
|
||
dataLevel = hasThirdLevel ? 3 : 2;
|
||
}
|
||
// 后台配置: 不能超过数据支持的层级
|
||
if (this.value.level && this.value.level <= dataLevel) {
|
||
return this.value.level;
|
||
}
|
||
// 否则按数据层级
|
||
return dataLevel;
|
||
},
|
||
/**
|
||
* 模板1: 是否按子分类分组显示
|
||
* 后台"展示分类"配置:
|
||
* 二级分类 (value.level == 2) → false → 单网格显示所有商品
|
||
* 三级分类 (value.level == 3) → true → 按二级分类分别显示各自商品
|
||
*/
|
||
isGroupedDisplay() {
|
||
// 仅模板1 走"按二级分类分组显示商品"; 模板5 的层级由 value.level 直接决定(见下方 value.template == 5 分支)
|
||
return this.value.template == 1 && this.value.level == 3;
|
||
},
|
||
/**
|
||
* 模板1三级分类: 只渲染"有商品"的二级分类
|
||
* 无商品的二级分类整块(标题 + 网格)都不渲染; 加载中的二级分类也暂不渲染(避免闪烁)
|
||
* 判定时机: 该二级分类加载完成(template1ChildLoading[id] === false)且商品列表非空
|
||
*/
|
||
filteredGroupedChildren() {
|
||
if (!this.isGroupedDisplay) return [];
|
||
if (!this.category || !this.category.child_list || !this.category.child_list.length) return [];
|
||
return this.category.child_list.filter(one => {
|
||
// 还在加载中: 暂不渲染(避免闪烁)
|
||
if (this.template1ChildLoading[one.category_id]) return false;
|
||
// 加载完成: 只有该二级分类下有商品时才显示, 无商品则整块隐藏
|
||
const list = this.template1ChildGoodsList[one.category_id];
|
||
return !!(list && list.length);
|
||
});
|
||
},
|
||
/**
|
||
* 渲染类型: 模板1/99 用分类树卡片(category), 其它模板用商品卡片(goods)
|
||
* 用计算属性而非 created() 里的一次性赋值:
|
||
* 页面下拉刷新/装修数据更新时 value.template 可能变化, 组件被 Vue 复用不会重建,
|
||
* 若 type 不跟随更新, 模板1 会误走 goods 分支, 导致"资讯类商品=否"的商品不显示立即咨询按钮
|
||
*/
|
||
type() {
|
||
return this.value.template == 1 || this.value.template == 5 || this.value.template == 99 ? 'category' : 'goods';
|
||
},
|
||
},
|
||
created() {
|
||
if (this.index == this.select && this.pageIndex == 0) this.getGoodsList();
|
||
this.loadType = this.value.goodsLevel == 1 && this.value.loadType == 'all' ? 'all' : 'part';
|
||
if (this.value.template == 99 && this.index == this.select && this.category.child_list && this.category.child_list.length > 0) {
|
||
let targetCategory = null;
|
||
if (this.lastSecondCategoryName) {
|
||
targetCategory = this.category.child_list.find(item => item.category_name === this.lastSecondCategoryName);
|
||
}
|
||
if (!targetCategory) {
|
||
targetCategory = this.category.child_list[0];
|
||
}
|
||
this.selectedSecondCategory = targetCategory.category_id;
|
||
this.secondCategoryGoodsList = [];
|
||
// 用当前一级分类下同名二级分类的 id 查商品(而非全局按钮的 id)
|
||
this.refreshGoodsByCurrentCategory();
|
||
}
|
||
// 模板1:根据后台"展示分类"配置 + 数据层级决定加载方式
|
||
// 三级分类模式 (isGroupedDisplay) + 有二级分类时,按二级分类分别加载各自商品
|
||
// 其他情况(二级分类模式 / 无二级分类),直接加载当前一级分类下的所有商品
|
||
// 模板5 渲染规则:
|
||
// 二级分类(value.level == 2) → 商品网格(图2), 需要加载该一级分类下所有商品(合并其二级分类)
|
||
// 三级分类(value.level == 3) → 二级分类网格(图3), 静态网格无需加载商品
|
||
// 若该一级分类下没有子分类(兜底), 仍需要加载商品
|
||
const template5NeedGoods = this.value.template == 5 &&
|
||
(this.value.level == 2 ||
|
||
!(this.category && this.category.child_list && this.category.child_list.length));
|
||
if ((this.value.template == 1 || template5NeedGoods) && this.category && this.category.category_id) {
|
||
if (this.isGroupedDisplay && this.category.child_list && this.category.child_list.length) {
|
||
this.category.child_list.forEach(child => {
|
||
this.getTemplate1ChildGoods(child.category_id);
|
||
});
|
||
} else if (this.category.child_list && this.category.child_list.length) {
|
||
// 二级分类模式: 合并所有二级分类的商品, 避免只查一级分类ID时漏掉子分类商品
|
||
this.loadTemplate1Level2Goods();
|
||
} else {
|
||
this.getTemplate1Goods(this.category.category_id);
|
||
}
|
||
}
|
||
},
|
||
mounted() {
|
||
setTimeout(() => {
|
||
const query = uni.createSelectorQuery().in(this);
|
||
query.select('.scroll-goods-view')
|
||
.boundingClientRect(data => {
|
||
if (data) this.contentWrapHeight = data.height;
|
||
}).exec();
|
||
}, 1000);
|
||
if (this.value.template == 99 && this.category.child_list && this.category.child_list.length > 0) {
|
||
let targetCategory = null;
|
||
if (this.lastSecondCategoryName) {
|
||
targetCategory = this.category.child_list.find(item => item.category_name === this.lastSecondCategoryName);
|
||
}
|
||
if (!targetCategory) {
|
||
targetCategory = this.category.child_list[0];
|
||
}
|
||
this.selectedSecondCategory = targetCategory.category_id;
|
||
this.secondCategoryGoodsList = [];
|
||
this.getSecondCategoryGoods(targetCategory.category_id);
|
||
}
|
||
},
|
||
watch: {
|
||
oneCategorySelect: function () {
|
||
this.scrollTop = -1;
|
||
this.goodsList = [];
|
||
this.selectCategory(-1);
|
||
},
|
||
select: function () {
|
||
if (this.index == this.select) {
|
||
this.scrollTop = 0;
|
||
if (this.pageIndex == 0) {
|
||
this.getGoodsList();
|
||
} else if (this.$refs.loadingCover && this.loadType == 'part') {
|
||
this.$refs.loadingCover.show();
|
||
setTimeout(() => {
|
||
this.$refs.loadingCover.hide();
|
||
}, 300);
|
||
}
|
||
if (!this.contentWrapHeight) {
|
||
const query = uni.createSelectorQuery().in(this);
|
||
query.select('.scroll-goods-view')
|
||
.boundingClientRect(data => {
|
||
if (data) this.contentWrapHeight = data.height;
|
||
}).exec();
|
||
}
|
||
} else if (this.loadType == 'part') {
|
||
this.scrollTop = -1;
|
||
}
|
||
if (this.value.template == 99 && this.category.child_list && this.category.child_list.length > 0) {
|
||
let targetCategory = null;
|
||
if (this.lastSecondCategoryName) {
|
||
targetCategory = this.category.child_list.find(item => item.category_name === this.lastSecondCategoryName);
|
||
}
|
||
if (!targetCategory) {
|
||
targetCategory = this.category.child_list[0];
|
||
}
|
||
this.selectedSecondCategory = targetCategory.category_id;
|
||
this.secondCategoryGoodsList = [];
|
||
this.refreshGoodsByCurrentCategory();
|
||
}
|
||
// 模板1:切换一级分类时重新加载该分类下的商品
|
||
// 三级分类模式 + 有二级分类时,按二级分类分别重新加载,否则直接加载当前分类
|
||
// 注意: 模板5 是"全部分类纵向平铺", 商品由 created 首次加载 + 父级链式懒加载提供,
|
||
// 滚动时 select 会随 listenScroll 变化, 若此处也重载会导致滚动中反复清空/重复请求, 故排除 5
|
||
if (this.value.template == 1 && this.index == this.select && this.category && this.category.category_id) {
|
||
this.template1GoodsList = [];
|
||
this.template1ChildGoodsList = {};
|
||
this.template1ChildLoading = {};
|
||
if (this.isGroupedDisplay && this.category.child_list && this.category.child_list.length) {
|
||
this.category.child_list.forEach(child => {
|
||
this.getTemplate1ChildGoods(child.category_id);
|
||
});
|
||
} else if (this.category.child_list && this.category.child_list.length) {
|
||
// 二级分类模式: 切换一级分类时重新合并所有二级分类商品
|
||
this.loadTemplate1Level2Goods();
|
||
} else {
|
||
this.getTemplate1Goods(this.category.category_id);
|
||
}
|
||
}
|
||
},
|
||
// 切换一级分类时, 根据当前一级分类下同名二级分类重新加载商品
|
||
currentOneCategory: {
|
||
handler() {
|
||
if (this.value.template == 99) {
|
||
this.refreshGoodsByCurrentCategory();
|
||
}
|
||
},
|
||
deep: true
|
||
},
|
||
'globalStoreInfo.store_id': {
|
||
handler(nval, oval) {
|
||
if (nval != oval) {
|
||
if (this.index == this.select || this.loadType == 'all') {
|
||
this.pageIndex = 0;
|
||
this.categoryId = -1;
|
||
this.totalPage = 1;
|
||
this.getGoodsList();
|
||
}
|
||
}
|
||
},
|
||
deep: true
|
||
}
|
||
},
|
||
methods: {
|
||
/**
|
||
* 拉取某分类下的全部商品(按后台实际数量)
|
||
* 后端 pageByCategory 存在分页上限, 这里自动翻页直到最后一页再合并返回,
|
||
* 保证分类模板(1/2/3/4/99)显示该分类下的全部商品, 而不是只显示第一页
|
||
* @param {Number} categoryId 分类ID
|
||
* @param {String} order 排序字段
|
||
* @param {String} sort 排序方向
|
||
* @returns {Promise<Array>}
|
||
*/
|
||
getCategoryGoodsAll(categoryId, order = '', sort = '') {
|
||
return new Promise(resolve => {
|
||
const MAX_PAGE = 200; // 安全上限, 防止后台 page 参数失效时死循环
|
||
const all = [];
|
||
let prevLen = 0;
|
||
const fetchPage = page => {
|
||
if (page > MAX_PAGE) {
|
||
resolve(all);
|
||
return;
|
||
}
|
||
this.$api.sendRequest({
|
||
url: '/api/goodssku/pageByCategory',
|
||
data: {
|
||
category_id: categoryId,
|
||
page: page,
|
||
// 每次按较大 page_size 取, 减少请求次数; 实际每页条数由后台决定
|
||
page_size: 100,
|
||
order: order,
|
||
sort: sort
|
||
},
|
||
success: res => {
|
||
if (res.code == 0 && res.data && res.data.list && res.data.list.length) {
|
||
const list = res.data.list;
|
||
all.push(...list);
|
||
const pageCount = res.data.page_count;
|
||
// 后台返回了可靠的 page_count 时, 以它为准停止翻页
|
||
if (typeof pageCount === 'number' && pageCount >= 1 && page >= pageCount) {
|
||
resolve(all);
|
||
return;
|
||
}
|
||
// 自适应翻页: 不信任 page_count 时也按"上一页是否满页"判断
|
||
// 当前页条数 >= 上一页条数 → 可能还有下一页, 继续拉取
|
||
// 当前页比上一页少 → 已是末页(不满), 停止
|
||
if (page === 1 || list.length >= prevLen) {
|
||
prevLen = list.length;
|
||
fetchPage(page + 1);
|
||
} else {
|
||
resolve(all);
|
||
}
|
||
} else {
|
||
// 当前页无数据, 全部商品已取完
|
||
resolve(all);
|
||
}
|
||
},
|
||
fail: () => resolve(all)
|
||
});
|
||
};
|
||
fetchPage(1);
|
||
});
|
||
},
|
||
/**
|
||
* 拉取某分类及其所有二级子分类下的全部商品(自动翻页 + 合并去重)
|
||
* 解决后端 pageByCategory 只返回"直接"挂在当前分类的商品, 漏掉子分类商品的问题,
|
||
* 保证一级分类下的所有二级分类商品都能全部显示
|
||
* @param {Object} category 分类对象(含 child_list)
|
||
* @param {Number} categoryId 当前分类ID
|
||
* @param {String} order 排序字段
|
||
* @param {String} sort 排序方向
|
||
* @returns {Promise<Array>}
|
||
*/
|
||
getCategoryGoodsWithChildrenAll(category, categoryId, order = '', sort = '') {
|
||
const ids = [categoryId];
|
||
if (category && category.child_list && category.child_list.length) {
|
||
category.child_list.forEach(child => {
|
||
if (child.category_id) ids.push(child.category_id);
|
||
});
|
||
}
|
||
const promises = ids.map(id => this.getCategoryGoodsAll(id, order, sort));
|
||
return Promise.all(promises).then(lists => {
|
||
const all = [];
|
||
const seen = new Set();
|
||
lists.forEach(list => {
|
||
list.forEach(item => {
|
||
const key = item.sku_id || item.goods_id;
|
||
if (key && !seen.has(key)) {
|
||
seen.add(key);
|
||
all.push(item);
|
||
}
|
||
});
|
||
});
|
||
return all;
|
||
});
|
||
},
|
||
/**
|
||
* 查询商品列表(模板 2/3/4)
|
||
* 一次性拉取该分类下的全部商品, 不再依赖滚动分页
|
||
*/
|
||
getGoodsList() {
|
||
if (this.type != 'goods' || this.loading) return;
|
||
// 已加载过该分类时, 避免 scrolltolower 重复拉取
|
||
const categoryId = this.categoryId != -1 ? (this.value.template == 4 && this.currentOneCategory && this.currentOneCategory.child_list ? this.currentOneCategory.child_list[this.categoryId].category_id : this.category.child_list[this.categoryId].category_id)
|
||
: (this.value.template == 4 && this.currentOneCategory ? this.currentOneCategory.category_id : this.category.category_id);
|
||
if (this._loadedGoodsKey === categoryId) return;
|
||
this.loading = true;
|
||
this.goodsList = [];
|
||
|
||
let order = '';
|
||
let sort = '';
|
||
if (this.value.sortWay) {
|
||
if (this.value.sortWay == 'default') {
|
||
order = '';
|
||
sort = '';
|
||
} else if (this.value.sortWay == 'sales') {
|
||
order = 'sale_num';
|
||
sort = 'desc';
|
||
} else if (this.value.sortWay == 'price') {
|
||
order = 'discount_price';
|
||
sort = 'desc';
|
||
} else if (this.value.sortWay == 'news') {
|
||
order = 'create_time';
|
||
sort = 'desc';
|
||
}
|
||
}
|
||
|
||
if (this.$refs.loadingCover && this.loadType == 'part') {
|
||
setTimeout(() => {
|
||
this.$refs.loadingCover.show();
|
||
});
|
||
}
|
||
|
||
// 模板2/3/4: 未选中具体二级分类时, 后端 pageByCategory 只返回直接挂在一级分类的商品,
|
||
// 需合并其下所有二级子分类商品, 才能显示全; 选中具体二级分类时, 只查该分类
|
||
let goodsPromise;
|
||
if (this.value.template == 4 && this.categoryId == -1 && this.currentOneCategory && this.currentOneCategory.child_list && this.currentOneCategory.child_list.length) {
|
||
// 模板4: 当前一级分类在 currentOneCategory, 合并其下所有二级分类商品
|
||
goodsPromise = this.getCategoryGoodsWithChildrenAll(this.currentOneCategory, categoryId, order, sort);
|
||
} else if ((this.value.template == 2 || this.value.template == 3) && this.categoryId == -1 && this.category && this.category.child_list && this.category.child_list.length) {
|
||
goodsPromise = this.getCategoryGoodsWithChildrenAll(this.category, categoryId, order, sort);
|
||
} else {
|
||
goodsPromise = this.getCategoryGoodsAll(categoryId, order, sort);
|
||
}
|
||
goodsPromise.then(list => {
|
||
this.goodsList = list;
|
||
this._loadedGoodsKey = categoryId;
|
||
this.loadGoodsCartNum();
|
||
this.loading = false;
|
||
if (this.$refs.loadingCover && this.loadType == 'part') this.$refs.loadingCover.hide();
|
||
setTimeout(() => {
|
||
if (this.loadType == 'all') this.$emit('loadfinish', this.index);
|
||
const query = uni.createSelectorQuery().in(this);
|
||
query
|
||
.select('.goods-list')
|
||
.boundingClientRect(data => {
|
||
if (data && data.height < this.contentWrapHeight) this
|
||
.scrollType = 'none';
|
||
})
|
||
.exec();
|
||
}, 500);
|
||
});
|
||
},
|
||
/**
|
||
* 商品图片处理
|
||
* @param {Object} imgStr
|
||
*/
|
||
goodsImg(imgStr) {
|
||
let imgs = imgStr.split(',');
|
||
return imgs[0] ?
|
||
this.$util.img(imgs[0], {
|
||
size: this.value == 3 ? 'big' : 'mid'
|
||
}) :
|
||
this.$util.getDefaultImage().goods;
|
||
},
|
||
/**
|
||
* 分类图片处理
|
||
* @param {Object} imgStr
|
||
*/
|
||
categoryImg(imgStr) {
|
||
if (!imgStr) return this.$util.getDefaultImage().goods;
|
||
return this.$util.img(imgStr);
|
||
},
|
||
categoryImgError(level, oneIndex, twoIndex) {
|
||
// 分类图片加载失败处理
|
||
if (level == 2 && this.category.child_list && this.category.child_list[oneIndex]) {
|
||
this.category.child_list[oneIndex].image = '';
|
||
} else if (level == 3 && this.category.child_list && this.category.child_list[oneIndex]
|
||
&& this.category.child_list[oneIndex].child_list && this.category.child_list[oneIndex].child_list[twoIndex]) {
|
||
this.category.child_list[oneIndex].child_list[twoIndex].image = '';
|
||
}
|
||
},
|
||
imgError(index) {
|
||
this.goodsList[index].goods_image = this.$util.getDefaultImage().goods;
|
||
},
|
||
showPrice(data) {
|
||
let price = data.discount_price;
|
||
if (data.member_price && parseFloat(data.member_price) < parseFloat(price)) price = data.member_price;
|
||
return price;
|
||
},
|
||
showMarketPrice(item) {
|
||
if (item.market_price_show) {
|
||
let price = this.showPrice(item);
|
||
if (item.market_price > 0) {
|
||
return item.market_price;
|
||
} else if (parseFloat(item.price) > parseFloat(price)) {
|
||
return item.price;
|
||
}
|
||
}
|
||
return '';
|
||
},
|
||
/**
|
||
* 跳转商品详情
|
||
* @param {Object} item
|
||
*/
|
||
toDetail(item) {
|
||
this.$util.redirectTo('/pages_goods/detail', {
|
||
goods_id: item.goods_id
|
||
});
|
||
},
|
||
/**
|
||
* 购物车数量增加
|
||
*/
|
||
increase(event, data) {
|
||
if (!this.storeToken) {
|
||
this.$emit('tologin');
|
||
return;
|
||
}
|
||
|
||
if (this.cartEditRepeat) return;
|
||
|
||
let cart =
|
||
this.cartList['goods_' + data.goods_id] && this.cartList['goods_' + data.goods_id]['sku_' + data
|
||
.sku_id
|
||
] ?
|
||
this.cartList['goods_' + data.goods_id]['sku_' + data.sku_id] :
|
||
null;
|
||
let cartNum = cart ? cart.num : 0;
|
||
let api = cart && cart.cart_id ? '/api/cart/edit' : '/api/cart/add';
|
||
let minBuy = data.min_buy > 0 ? data.min_buy - 1 : 0;
|
||
let num = cartNum >= data.min_buy ? cartNum : minBuy;
|
||
let _num = num + 1;
|
||
let cart_id = cart ? cart.cart_id : 0;
|
||
|
||
if (_num > parseInt(data.stock)) {
|
||
this.$util.showToast({
|
||
title: this.$lang('components-diy.diy-category-item.insufficientStock')
|
||
});
|
||
return;
|
||
}
|
||
if (data.is_limit && data.max_buy && _num > parseInt(data.max_buy)) {
|
||
this.$util.showToast({
|
||
title: this.$lang('components-diy.diy-category-item.purchaseLimit', {
|
||
maxBuy: data.max_buy,
|
||
unit: data.unit || this.$lang('components-diy.diy-category-item.pieceUnit')
|
||
})
|
||
});
|
||
return;
|
||
}
|
||
|
||
const query = uni.createSelectorQuery().in(this);
|
||
query
|
||
.select('#' + event.currentTarget.id + ' .click-event')
|
||
.boundingClientRect(data => {
|
||
if (data) {
|
||
this.$emit('addCart', data.left, data.top);
|
||
}
|
||
})
|
||
.exec();
|
||
|
||
if (this.timeout[data.sku_id]) clearTimeout(this.timeout[data.sku_id]);
|
||
|
||
if (cart) {
|
||
this.cartList['goods_' + data.goods_id]['sku_' + data.sku_id].num = _num;
|
||
} else {
|
||
|
||
// 如果商品第一次添加,则初始化数据
|
||
if (!this.cartList['goods_' + data.goods_id]) {
|
||
this.cartList['goods_' + data.goods_id] = {};
|
||
}
|
||
|
||
let discount_price = data.discount_price;
|
||
if (data.member_price > 0 && Number(data.member_price) <= Number(data.discount_price)) {
|
||
discount_price = data.member_price;
|
||
}
|
||
|
||
this.cartList['goods_' + data.goods_id]['sku_' + data.sku_id] = {
|
||
cart_id,
|
||
goods_id: data.goods_id,
|
||
sku_id: data.sku_id,
|
||
num: _num,
|
||
discount_price
|
||
};
|
||
}
|
||
|
||
this.$forceUpdate();
|
||
|
||
// 立即同步购物车数量/金额到 store: 底部结算栏按 cartNumber 显隐,
|
||
// 若只等 800ms 防抖 + 接口回调才计算, 会出现"点了加购但结算栏没弹出"
|
||
this.$store.dispatch('cartCalculate');
|
||
|
||
this.timeout[data.sku_id] = setTimeout(() => {
|
||
this.cartEditRepeat = true;
|
||
this.$api.sendRequest({
|
||
url: api,
|
||
data: {
|
||
cart_id,
|
||
sku_id: data.sku_id,
|
||
num: _num
|
||
},
|
||
success: res => {
|
||
this.cartEditRepeat = false;
|
||
if (res.code == 0) {
|
||
if (cart_id == 0) {
|
||
this.cartList['goods_' + data.goods_id]['sku_' + data.sku_id].cart_id = res.data;
|
||
}
|
||
this.$store.dispatch('cartCalculate');
|
||
} else {
|
||
this.$util.showToast({
|
||
title: res.message
|
||
});
|
||
}
|
||
}
|
||
});
|
||
}, 800);
|
||
},
|
||
/**
|
||
* 购物车数量减少
|
||
*/
|
||
reduce(data) {
|
||
if (this.cartEditRepeat) return;
|
||
|
||
let cart = this.cartList['goods_' + data.goods_id] && this.cartList['goods_' + data.goods_id]['sku_' + data.sku_id] ? this.cartList['goods_' + data.goods_id]['sku_' + data.sku_id] : null;
|
||
let num = cart.num > data.min_buy ? cart.num : 1;
|
||
let api = num > 1 ? '/api/cart/edit' : '/api/cart/delete';
|
||
let _num = num - 1;
|
||
|
||
if (this.timeout[data.sku_id]) clearTimeout(this.timeout[data.sku_id]);
|
||
|
||
if (_num) {
|
||
this.cartList['goods_' + data.goods_id]['sku_' + data.sku_id].num = _num;
|
||
} else {
|
||
delete this.cartList['goods_' + data.goods_id]['sku_' + data.sku_id];
|
||
if (Object.keys(this.cartList['goods_' + data.goods_id]).length == 2) {
|
||
delete this.cartList['goods_' + data.goods_id];
|
||
}
|
||
}
|
||
this.$forceUpdate();
|
||
|
||
// 同上: 减购后立即重算, 减到 0 时结算栏同步消失
|
||
this.$store.dispatch('cartCalculate');
|
||
|
||
// 防止点击太快
|
||
if (cart && cart.cart_id == 0) {
|
||
return;
|
||
}
|
||
|
||
this.timeout[data.sku_id] = setTimeout(() => {
|
||
this.cartEditRepeat = true;
|
||
this.$api.sendRequest({
|
||
url: api,
|
||
data: {
|
||
cart_id: cart.cart_id,
|
||
sku_id: data.sku_id,
|
||
num: _num
|
||
},
|
||
success: res => {
|
||
this.cartEditRepeat = false;
|
||
if (res.code == 0) {
|
||
this.$store.dispatch('cartCalculate');
|
||
} else {
|
||
this.$util.showToast({
|
||
title: res.message
|
||
});
|
||
}
|
||
}
|
||
});
|
||
}, 800);
|
||
},
|
||
/**
|
||
* 选择规格
|
||
* @param {Object} data
|
||
*/
|
||
selectSku(data) {
|
||
if (!this.storeToken) {
|
||
this.$emit('tologin');
|
||
return;
|
||
}
|
||
this.$emit('selectsku', data);
|
||
},
|
||
selectCategory(index) {
|
||
this.categoryId = index;
|
||
this.pageIndex = 0;
|
||
this.totalPage = 1;
|
||
this.getGoodsList();
|
||
setTimeout(() => {
|
||
this.scrollIntoView = 'category-2-' + index;
|
||
});
|
||
},
|
||
/**
|
||
* 滚动到底部加载
|
||
*/
|
||
scrolltolower() {
|
||
this.getGoodsList();
|
||
},
|
||
/**
|
||
* 监听滚动
|
||
* @param {Object} e
|
||
*/
|
||
listenScroll(e) {
|
||
if (e.detail.scrollTop <= 10) {
|
||
this.scrollType = 'top';
|
||
} else if (parseInt(e.detail.scrollTop + this.contentWrapHeight + 30) >= e.detail.scrollHeight) {
|
||
this.scrollType = 'bottom';
|
||
} else {
|
||
this.scrollType = '';
|
||
}
|
||
},
|
||
/**
|
||
* 触摸开始
|
||
* @param {Object} e
|
||
*/
|
||
touchstart(e) {
|
||
this.touchstartPosition = e.changedTouches[0].clientY;
|
||
},
|
||
/**
|
||
* 触摸结束
|
||
* @param {Object} e
|
||
*/
|
||
touchend(e) {
|
||
let end = e.changedTouches[0].clientY;
|
||
if ((this.scrollType == 'top' || this.scrollType == 'none') && end - this.touchstartPosition > 100) {
|
||
this.switchCategory('prev');
|
||
} else if ((this.scrollType == 'bottom' || this.scrollType == 'none') && this.touchstartPosition - end >
|
||
100) {
|
||
this.switchCategory('next');
|
||
}
|
||
},
|
||
/**
|
||
* 切换分类
|
||
* @param {Object} action
|
||
*/
|
||
switchCategory(action) {
|
||
if (action == 'prev') {
|
||
if (this.categoryId == -1) {
|
||
let index = this.index - 1;
|
||
if (index == -1) return;
|
||
this.$emit('switch', index);
|
||
} else {
|
||
let index = this.categoryId - 1;
|
||
this.selectCategory(index);
|
||
}
|
||
} else {
|
||
if (this.categoryId == -1 || (this.category.child_list && this.categoryId == this.category.child_list.length - 1)) {
|
||
let index = this.index + 1;
|
||
this.$emit('switch', index);
|
||
} else {
|
||
let index = this.categoryId + 1;
|
||
this.selectCategory(index);
|
||
}
|
||
}
|
||
},
|
||
/**
|
||
* 跳转到商品详情
|
||
* @param {Object} item 商品信息
|
||
*/
|
||
sendToAIChat(item) {
|
||
this.$util.redirectTo('/pages_goods/detail', {
|
||
goods_id: item.goods_id
|
||
});
|
||
},
|
||
diyRedirectTo(link) {
|
||
if (!link) return;
|
||
this.$util.diyRedirectTo(JSON.parse(link));
|
||
},
|
||
/**
|
||
* 根据当前一级分类(currentOneCategory)下与选中按钮同名的二级分类, 加载对应商品
|
||
* 按钮是全局去重的(选中状态用全局 category_id), 但商品只显示当前一级分类下的
|
||
* 当前一级分类下若没有同名二级分类, 则商品清空(按钮保持选中)
|
||
*/
|
||
refreshGoodsByCurrentCategory() {
|
||
if (this.selectedSecondCategory == null) {
|
||
this.secondCategoryGoodsList = [];
|
||
return;
|
||
}
|
||
// 取当前选中按钮的名称
|
||
const cur = (this.category.child_list || []).find(c => c.category_id === this.selectedSecondCategory);
|
||
if (!cur) {
|
||
this.secondCategoryGoodsList = [];
|
||
return;
|
||
}
|
||
const one = this.currentOneCategory;
|
||
if (!one || !one.child_list || one.child_list.length === 0) {
|
||
this.secondCategoryGoodsList = [];
|
||
return;
|
||
}
|
||
const match = one.child_list.find(c => c.category_name === cur.category_name);
|
||
if (match) {
|
||
this.getSecondCategoryGoods(match.category_id);
|
||
} else {
|
||
// 当前一级分类下没有该二级分类, 商品清空(按钮仍保持选中)
|
||
this.secondCategoryGoodsList = [];
|
||
}
|
||
},
|
||
selectSecondCategory(category) {
|
||
if (this.selectedSecondCategory === category.category_id) {
|
||
this.selectedSecondCategory = null;
|
||
this.secondCategoryGoodsList = [];
|
||
} else {
|
||
this.selectedSecondCategory = category.category_id;
|
||
this.secondCategoryGoodsList = [];
|
||
this.$emit('saveSecondCategory', category.category_name);
|
||
// 用当前一级分类下同名 category_id 查商品
|
||
this.refreshGoodsByCurrentCategory();
|
||
}
|
||
},
|
||
getSecondCategoryGoods(categoryId) {
|
||
this.loading = true;
|
||
let order = '';
|
||
let sort = '';
|
||
if (this.value.sortWay) {
|
||
if (this.value.sortWay == 'default') {
|
||
order = '';
|
||
sort = '';
|
||
} else if (this.value.sortWay == 'sales') {
|
||
order = 'sales';
|
||
sort = 'desc';
|
||
} else if (this.value.sortWay == 'price') {
|
||
order = 'min_price';
|
||
sort = this.value.sortType == 'desc' ? 'desc' : 'asc';
|
||
}
|
||
}
|
||
this.getCategoryGoodsAll(categoryId, order, sort).then(list => {
|
||
this.loading = false;
|
||
this.secondCategoryGoodsList = list;
|
||
}).catch(() => {
|
||
this.loading = false;
|
||
this.secondCategoryGoodsList = [];
|
||
});
|
||
},
|
||
/**
|
||
* 模板1: 选择分类后加载该分类下的商品
|
||
* @param {Object} category 选中的分类对象
|
||
*/
|
||
selectCategoryForGoods(category) {
|
||
if (this.selectedCategoryId === category.category_id) {
|
||
return; // 已选中,不重复加载
|
||
}
|
||
this.selectedCategoryId = category.category_id;
|
||
this.template1GoodsList = [];
|
||
this.getTemplate1Goods(category.category_id);
|
||
},
|
||
/**
|
||
* 模板1: 根据分类ID获取商品列表
|
||
*/
|
||
getTemplate1Goods(categoryId) {
|
||
this.template1Loading = true;
|
||
this.getCategoryGoodsAll(categoryId).then(list => {
|
||
this.template1GoodsList = list;
|
||
this.template1Loading = false;
|
||
}).catch(() => {
|
||
this.template1GoodsList = [];
|
||
this.template1Loading = false;
|
||
});
|
||
},
|
||
/**
|
||
* 模板1二级分类模式: 加载当前一级分类下所有二级分类的商品并合并
|
||
* 避免只按一级分类ID查询时, 后端不返回子分类商品, 导致商品显示不全
|
||
*/
|
||
loadTemplate1Level2Goods() {
|
||
this.template1Loading = true;
|
||
const promises = this.category.child_list.map(child => this.getCategoryGoodsAll(child.category_id));
|
||
Promise.all(promises).then(lists => {
|
||
const all = [];
|
||
const seen = new Set();
|
||
lists.forEach(list => {
|
||
list.forEach(item => {
|
||
const key = item.sku_id || item.goods_id;
|
||
if (key && !seen.has(key)) {
|
||
seen.add(key);
|
||
all.push(item);
|
||
}
|
||
});
|
||
});
|
||
this.template1GoodsList = all;
|
||
this.template1Loading = false;
|
||
}).catch(() => {
|
||
this.template1GoodsList = [];
|
||
this.template1Loading = false;
|
||
});
|
||
},
|
||
/**
|
||
* 模板1: 根据二级分类ID分别加载各自的商品列表
|
||
* 用于 categoryLevel == 3 场景, 每个二级分类展示自己的商品
|
||
* @param {Number} categoryId 二级分类ID
|
||
*/
|
||
getTemplate1ChildGoods(categoryId) {
|
||
this.$set(this.template1ChildLoading, categoryId, true);
|
||
this.getCategoryGoodsAll(categoryId).then(list => {
|
||
this.$set(this.template1ChildLoading, categoryId, false);
|
||
this.$set(this.template1ChildGoodsList, categoryId, list);
|
||
}).catch(() => {
|
||
this.$set(this.template1ChildLoading, categoryId, false);
|
||
this.$set(this.template1ChildGoodsList, categoryId, []);
|
||
});
|
||
},
|
||
/**
|
||
* 模板1: 商品图片加载失败处理
|
||
* @param {Number} categoryId 二级分类ID(可选,用于按子分类定位)
|
||
* @param {Number} index 商品在列表中的索引
|
||
*/
|
||
template1ImgError(categoryId, index) {
|
||
// 兼容旧调用: 仅传 index 的情况(categoryLevel == 2)
|
||
if (categoryId === undefined || categoryId === null) {
|
||
if (this.template1GoodsList[index]) {
|
||
this.template1GoodsList[index].goods_image = this.$util.getDefaultImage().goods;
|
||
}
|
||
return;
|
||
}
|
||
const list = this.template1ChildGoodsList[categoryId];
|
||
if (list && list[index]) {
|
||
list[index].goods_image = this.$util.getDefaultImage().goods;
|
||
}
|
||
},
|
||
//加载商品在购物车的总数量
|
||
loadGoodsCartNum(isRefresh = false) {
|
||
this.goodsList.forEach((item) => {
|
||
item.num = this.cartList['goods_' + item.goods_id] ? this.cartList['goods_' + item.goods_id].num : 0;
|
||
})
|
||
if (isRefresh) this.$forceUpdate();
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.small {
|
||
font-size: 28rpx !important;
|
||
}
|
||
|
||
.item-wrap {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
padding: 0 0rpx;
|
||
|
||
&.goods {
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
.category-title {
|
||
padding: 20rpx 0;
|
||
font-size: var(--diy-category-item-title-size, 26rpx);
|
||
// font-weight: bold;
|
||
color: #222;
|
||
position: relative;
|
||
text-align: left;
|
||
padding-left: 30rpx;
|
||
// &::after,
|
||
// &::before {
|
||
// content: ' ';
|
||
// width: 46rpx;
|
||
// border-top: 2rpx solid #dfdfdf;
|
||
// position: absolute;
|
||
// top: 50%;
|
||
// }
|
||
|
||
&::after {
|
||
margin-left: 40rpx;
|
||
}
|
||
|
||
&::before {
|
||
margin-left: -80rpx;
|
||
}
|
||
}
|
||
|
||
// 模板1 专用: 标题居中 + 两侧横线
|
||
.category-title-with-lines {
|
||
display: flex !important;
|
||
align-items: center;
|
||
justify-content: center;
|
||
text-align: center;
|
||
padding: 30rpx 0 20rpx !important;
|
||
padding-left: 0 !important;
|
||
color: #333;
|
||
font-size: 30rpx;
|
||
font-weight: 500;
|
||
letter-spacing: 2rpx;
|
||
// 删除标题行整块的白色块, 背景设为透明, 让上层网格的 #f5f5f5 透出,
|
||
// 标题行与下方 .template1-goods-wrap 灰色融为一体, 只保留居中的文字与两侧短横线
|
||
background: transparent !important;
|
||
|
||
&::before,
|
||
&::after {
|
||
content: '';
|
||
flex: 0 0 60rpx;
|
||
height: 0;
|
||
border-top: 1rpx solid #d9d9d9;
|
||
margin: 0 24rpx;
|
||
}
|
||
}
|
||
|
||
.category-adv {
|
||
width: 100%;
|
||
overflow: hidden;
|
||
line-height: 1;
|
||
padding: 20rpx 0;
|
||
|
||
image {
|
||
width: 100%;
|
||
border-radius: 8rpx;
|
||
}
|
||
}
|
||
|
||
// 分类网格(模板5 / legacy 商品分类): 白底卡片 + 3 列 图片 + 名称
|
||
// 与 Branch_5cfee417 的商品分类保持一致
|
||
.category-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
background: #fff;
|
||
margin: 0 20rpx;
|
||
border-radius: 10rpx;
|
||
padding: 10rpx 0;
|
||
|
||
.category-item {
|
||
width: 33.33%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 16rpx;
|
||
box-sizing: border-box;
|
||
|
||
.img-box {
|
||
width: 80%;
|
||
height: 0;
|
||
padding-top: 80%;
|
||
margin: 0 auto;
|
||
overflow: hidden;
|
||
margin-bottom: 20rpx;
|
||
position: relative;
|
||
background: transparent;
|
||
|
||
image {
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
left: 0;
|
||
top: 0;
|
||
transform: none;
|
||
}
|
||
}
|
||
|
||
.name {
|
||
width: 100%;
|
||
text-overflow: ellipsis;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-align: center;
|
||
line-height: 1;
|
||
|
||
font-size: var(--diy-category-item-name-size, 24rpx);
|
||
}
|
||
|
||
&.selected {
|
||
.img-box {
|
||
border: 2rpx solid var(--base-color, #FF6B35);
|
||
border-radius: 8rpx;
|
||
}
|
||
.name {
|
||
color: var(--base-color, #FF6B35);
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 模板1商品列表容器
|
||
.template1-goods-wrap {
|
||
padding: 20rpx;
|
||
background: #f5f5f5;
|
||
|
||
.goods-list {
|
||
margin: 0;
|
||
padding-top: 0;
|
||
display: flex;
|
||
|
||
.goods-item {
|
||
width: calc(50% - 10rpx);
|
||
background: #fff;
|
||
border-radius: 12rpx;
|
||
overflow: hidden;
|
||
margin-bottom: 20rpx;
|
||
|
||
// 划线价放在现价正下方(左边与现价对齐, 间距 6rpx)
|
||
.price-wrap {
|
||
.delete-price {
|
||
margin-left: 0;
|
||
margin-top: 6rpx;
|
||
width: 100%;
|
||
display: inline-flex;
|
||
align-items: baseline;
|
||
text-decoration: line-through;
|
||
}
|
||
}
|
||
|
||
.footer-wrap-template1 {
|
||
padding: 16rpx 4rpx 0rpx;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
|
||
.detail-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 0 32rpx;
|
||
height: 55rpx;
|
||
background-color: #ff4545;
|
||
color: #fff;
|
||
font-size: 25rpx;
|
||
border-radius: 26rpx;
|
||
box-sizing: border-box;
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.goods-list {
|
||
/*双列*/
|
||
display: none;
|
||
margin: 0 24rpx;
|
||
padding-top: $margin-updown;
|
||
position: relative;
|
||
flex-wrap: wrap;
|
||
justify-content: space-between;
|
||
|
||
&.show {
|
||
display: flex;
|
||
}
|
||
|
||
.goods-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
border-radius: $border-radius;
|
||
overflow: hidden;
|
||
background-color: #fff;
|
||
margin-bottom: 20rpx;
|
||
|
||
.name-wrap {
|
||
display: var(--diy-category-item-name-wrap-display, block);
|
||
flex: var(--diy-category-item-name-wrap-flex, auto);
|
||
overflow: var(--diy-category-item-name-wrap-overflow, visible);
|
||
}
|
||
|
||
.goods-img {
|
||
position: relative;
|
||
overflow: hidden;
|
||
padding-top: 100%;
|
||
border-top-left-radius: $border-radius;
|
||
border-top-right-radius: $border-radius;
|
||
|
||
image {
|
||
width: 100%;
|
||
position: absolute !important;
|
||
top: 50%;
|
||
left: 0;
|
||
transform: translateY(-50%);
|
||
}
|
||
}
|
||
|
||
.goods-tag {
|
||
color: #fff;
|
||
line-height: 1;
|
||
padding: 8rpx 16rpx;
|
||
position: absolute;
|
||
border-bottom-right-radius: $border-radius;
|
||
top: 0;
|
||
left: 0;
|
||
font-size: var(--diy-category-item-goods-tag-size, 22rpx);
|
||
}
|
||
|
||
.goods-tag-img {
|
||
position: absolute;
|
||
border-top-left-radius: $border-radius;
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 5;
|
||
overflow: hidden;
|
||
|
||
image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
.info-wrap {
|
||
padding: var(--diy-category-item-info-wrap-padding, 0 20rpx);
|
||
display: flex;
|
||
flex-direction: column;
|
||
flex: 1;
|
||
}
|
||
|
||
.goods-name {
|
||
font-size: var(--diy-category-item-goods-name-size, 28rpx);
|
||
line-height: var(--diy-category-item-goods-name-line-height, 1.3);
|
||
margin-top: var(--diy-category-item-goods-name-margin-top, 20rpx);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.lineheight-clear {
|
||
margin-top: 16rpx;
|
||
}
|
||
|
||
.price-wrap {
|
||
// 原 SCSS 完全没有 .price-wrap 样式定义(元素是 block 布局,初始值)
|
||
// 保留 5 个属性的主题化能力,但所有 fallback 都设为 initial value,
|
||
// 不改变原渲染。远端可覆写为 flex / baseline / wrap / space-between / 8rpx
|
||
display: var(--diy-category-item-price-wrap-display, block);
|
||
align-items: var(--diy-category-item-price-wrap-align-items, normal);
|
||
flex-wrap: var(--diy-category-item-price-wrap-flex-wrap, nowrap);
|
||
justify-content: var(--diy-category-item-price-wrap-justify-content, normal);
|
||
margin-top: var(--diy-category-item-price-wrap-margin-top, 0);
|
||
}
|
||
|
||
.discount-price {
|
||
display: inline-block;
|
||
font-weight: var(--diy-category-item-discount-price-weight, bold);
|
||
line-height: var(--diy-category-item-discount-price-line-height, 1);
|
||
|
||
.unit {
|
||
margin-right: 6rpx;
|
||
color: var(--price-color);
|
||
}
|
||
|
||
.price {
|
||
color: var(--price-color);
|
||
}
|
||
}
|
||
|
||
.pro-info {
|
||
display: flex;
|
||
margin-top: auto;
|
||
align-items: center;
|
||
|
||
.block-wrap {
|
||
flex: 1;
|
||
line-height: 1;
|
||
margin-right: 20rpx;
|
||
|
||
.sale {
|
||
font-size: $font-size-tag !important;
|
||
}
|
||
}
|
||
}
|
||
|
||
.delete-price {
|
||
|
||
// display: inline-block;
|
||
// margin-left: 6rpx;
|
||
// float: right;
|
||
.unit {
|
||
margin-right: 6rpx;
|
||
}
|
||
|
||
text {
|
||
line-height: var(--diy-category-item-delete-price-line-height, 1);
|
||
font-size: var(--diy-category-item-delete-price-size, $font-size-tag) !important;
|
||
}
|
||
}
|
||
|
||
.member-price-tag {
|
||
display: inline-block;
|
||
width: var(--diy-category-item-member-price-tag-width, 60rpx);
|
||
line-height: var(--diy-category-item-member-price-tag-line-height, 1);
|
||
margin-left: var(--diy-category-item-member-price-tag-margin-left, 6rpx);
|
||
|
||
image {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
.footer-wrap {
|
||
display: var(--diy-category-item-footer-wrap-display, flex);
|
||
justify-content: var(--diy-category-item-footer-wrap-justify-content, space-between);
|
||
align-items: var(--diy-category-item-footer-wrap-align-items, normal);
|
||
margin-top: var(--diy-category-item-footer-wrap-margin-top, 0);
|
||
}
|
||
|
||
.consult-now-btn-wrap {
|
||
display: var(--diy-category-item-consult-now-wrap-display, flex);
|
||
justify-content: var(--diy-category-item-consult-now-wrap-justify-content, flex-end);
|
||
margin-top: var(--diy-category-item-consult-now-wrap-margin-top, auto);
|
||
padding-top: var(--diy-category-item-consult-now-wrap-padding-top, 10rpx);
|
||
}
|
||
|
||
.sell-out {
|
||
position: absolute;
|
||
z-index: 1;
|
||
width: 100%;
|
||
height: 100%;
|
||
top: 0;
|
||
left: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
|
||
text {
|
||
color: #fff;
|
||
font-size: 250rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 双列布局 */
|
||
&.double-column {
|
||
.goods-item {
|
||
width: calc(50% - 10rpx);
|
||
}
|
||
}
|
||
|
||
/* 单列布局(不包含模板2/4,模板2/4使用模板4卡片样式) */
|
||
&.single-column:not([data-template='2']):not([data-template='4']) {
|
||
flex-direction: column;
|
||
|
||
.goods-item {
|
||
width: 100%;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
|
||
.goods-img {
|
||
width: 180rpx;
|
||
height: 180rpx;
|
||
padding-top: 0;
|
||
margin-right: 14rpx;
|
||
border-radius: $border-radius;
|
||
|
||
image {
|
||
border-radius: $border-radius;
|
||
}
|
||
}
|
||
|
||
.goods-name {
|
||
white-space: normal;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**/
|
||
/*.goods-item {
|
||
padding: 0 0 26rpx 0;
|
||
background: #fff;
|
||
display: flex;
|
||
position: relative;
|
||
|
||
.goods-img {
|
||
width: 180rpx;
|
||
height: 180rpx;
|
||
overflow: hidden;
|
||
border-radius: $border-radius;
|
||
margin-right: 14rpx;
|
||
|
||
image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
.goods-tag {
|
||
color: #fff;
|
||
line-height: 1;
|
||
padding: 8rpx 12rpx;
|
||
position: absolute;
|
||
border-top-left-radius: $border-radius;
|
||
border-bottom-right-radius: $border-radius;
|
||
top: 0;
|
||
left: 0;
|
||
font-size: $font-size-goods-tag;
|
||
}
|
||
|
||
.goods-tag-img {
|
||
position: absolute;
|
||
border-top-left-radius: $border-radius;
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
top: 26rpx;
|
||
left: 26rpx;
|
||
z-index: 5;
|
||
overflow: hidden;
|
||
|
||
image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
.info-wrap {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
width: 0;
|
||
}
|
||
|
||
.name-wrap {
|
||
flex: 1;
|
||
}
|
||
|
||
.goods-name {
|
||
font-size: var(--diy-category-item-goods-name-size, $font-size-base);
|
||
line-height: var(--diy-category-item-goods-name-line-height, 1.3);
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
|
||
.introduction {
|
||
line-height: 1;
|
||
margin-top: 10rpx;
|
||
}
|
||
|
||
.discount-price {
|
||
display: inline-block;
|
||
font-weight: var(--diy-category-item-discount-price-weight, bold);
|
||
line-height: var(--diy-category-item-discount-price-line-height, 1);
|
||
margin-top: var(--diy-category-item-discount-price-margin-top, 16rpx);
|
||
color: var(--price-color);
|
||
|
||
.unit {
|
||
margin-right: 6rpx;
|
||
}
|
||
}
|
||
|
||
.pro-info {
|
||
display: flex;
|
||
margin-top: var(--diy-category-item-pro-info-margin-top, 16rpx);
|
||
|
||
.delete-price {
|
||
text-decoration: var(--diy-category-item-delete-price-text-decoration, line-through);
|
||
flex: 1;
|
||
|
||
.unit {
|
||
margin-right: 0rpx;
|
||
}
|
||
}
|
||
|
||
&>view {
|
||
line-height: 1;
|
||
|
||
&:nth-child(2) {
|
||
text-align: right;
|
||
}
|
||
}
|
||
}
|
||
|
||
.member-price-tag {
|
||
display: inline-block;
|
||
width: var(--diy-category-item-member-price-tag-width, 60rpx);
|
||
line-height: var(--diy-category-item-member-price-tag-line-height, 1);
|
||
margin-left: var(--diy-category-item-member-price-tag-margin-left, 6rpx);
|
||
height: var(--diy-category-item-member-price-tag-height, 28rpx);
|
||
|
||
image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
.footer-wrap {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
|
||
.right-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
|
||
.num {
|
||
width: auto;
|
||
padding: 0 20rpx;
|
||
line-height: 1;
|
||
}
|
||
|
||
.num-action {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
background: $base-color;
|
||
border-radius: 50%;
|
||
position: relative;
|
||
|
||
.click-event {
|
||
position: absolute;
|
||
width: 2rpx;
|
||
height: 2rpx;
|
||
left: 0;
|
||
top: 0;
|
||
transform: translate(-50%, -50%);
|
||
z-index: 5;
|
||
}
|
||
|
||
&.reduce {
|
||
width: 38rpx;
|
||
height: 38rpx;
|
||
background-color: transparent;
|
||
border: 2rpx solid $base-color;
|
||
box-sizing: border-box;
|
||
|
||
.icon-jian {
|
||
color: $base-color;
|
||
}
|
||
}
|
||
}
|
||
|
||
.icon-jian,
|
||
.icon-jia {
|
||
color: var(--btn-text-color);
|
||
font-weight: bold;
|
||
font-size: 26rpx;
|
||
line-height: 1;
|
||
}
|
||
|
||
.select-sku {
|
||
font-weight: bold;
|
||
color: var(--btn-text-color);
|
||
font-size: 24rpx;
|
||
padding: 16rpx 24rpx;
|
||
position: relative;
|
||
// height: 40rpx;
|
||
line-height: 1;
|
||
text-align: center;
|
||
border-radius: 50rpx;
|
||
|
||
.num-tag {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
transform: translateY(-50%);
|
||
display: inline-block;
|
||
box-sizing: border-box;
|
||
color: #fff;
|
||
line-height: 1.2;
|
||
text-align: center;
|
||
font-size: 24rpx;
|
||
padding: 0 6rpx;
|
||
min-width: 32rpx;
|
||
border-radius: 16rpx;
|
||
background-color: $base-color;
|
||
border: 2rpx solid #fff;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}*/
|
||
|
||
// ===================== 模板3 商品分类样式 (卡片式布局: 图片顶部 + 名称上行 + 价格和按钮下行) =====================
|
||
&[data-template='3'] {
|
||
// 模板3 的商品列表容器: 灰色背景让卡片有层次
|
||
background: #f4f6fa;
|
||
|
||
// 重置 double-column 的影响, 强制单列布局
|
||
.goods-item {
|
||
display: block;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
width: 100% !important;
|
||
max-width: 100%;
|
||
background: #fff;
|
||
border-radius: 12rpx;
|
||
overflow: hidden;
|
||
margin: 0 0 20rpx;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
// 强制取消 single-column 的 row 布局(防止同时应用)
|
||
&.single-column .goods-item,
|
||
&.double-column .goods-item {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
}
|
||
|
||
// 图片: 占满卡片宽度, 自适应高度(类似 banner)
|
||
.goods-img {
|
||
width: 100%;
|
||
height: 0;
|
||
padding-top: 50%;
|
||
position: relative;
|
||
margin-right: 0;
|
||
line-height: 1;
|
||
border-radius: 0;
|
||
overflow: hidden;
|
||
|
||
image {
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
top: 0;
|
||
left: 0;
|
||
transform: none;
|
||
border-radius: 0;
|
||
}
|
||
}
|
||
|
||
// 信息区域: 上下结构 (名称一行 + 价格+按钮一行)
|
||
.info-wrap {
|
||
display: flex;
|
||
flex-direction: column;
|
||
width: 100%;
|
||
margin-top: 0;
|
||
padding: 24rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
// 商品名称: 单行省略
|
||
.name-wrap {
|
||
width: 100%;
|
||
|
||
.goods-name {
|
||
font-size: 28rpx;
|
||
line-height: 1.4;
|
||
margin-top: 0;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
color: #333;
|
||
}
|
||
}
|
||
|
||
// 底部行: 价格在左, 按钮在右
|
||
.info-bottom {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 12rpx;
|
||
}
|
||
|
||
// 价格区域: 现价在上, 划线价在下方
|
||
.price-wrap {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
flex: 1;
|
||
min-width: 0;
|
||
|
||
.discount-price {
|
||
display: inline-flex;
|
||
align-items: baseline;
|
||
font-weight: bold;
|
||
line-height: 1;
|
||
font-size: 30rpx;
|
||
color: var(--price-color, #FF6B35);
|
||
|
||
.unit {
|
||
font-size: 22rpx;
|
||
margin-right: 2rpx;
|
||
color: var(--price-color, #FF6B35);
|
||
}
|
||
|
||
.price {
|
||
font-size: 30rpx;
|
||
color: var(--price-color, #FF6B35);
|
||
}
|
||
}
|
||
|
||
// 删除线价格: 在现价下方
|
||
.delete-price {
|
||
margin-left: 0;
|
||
margin-top: 6rpx;
|
||
display: inline-flex;
|
||
align-items: baseline;
|
||
text-decoration: line-through;
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
|
||
.unit {
|
||
margin-right: 2rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 立即购买按钮: 在底部行右侧
|
||
.buy-now-btn {
|
||
flex-shrink: 0;
|
||
margin-left: 20rpx;
|
||
width: 160rpx;
|
||
height: 56rpx;
|
||
line-height: 56rpx;
|
||
text-align: center;
|
||
font-size: 26rpx;
|
||
font-weight: bold;
|
||
color: #fff;
|
||
border-radius: 28rpx;
|
||
}
|
||
}
|
||
|
||
// ===================== 模板2/4 商品分类样式(支持右上角视图切换) =====================
|
||
// 单列视图: 左图右文横向卡片 + 右侧+按钮
|
||
// 双列视图: 上下结构宫格卡片, 每行两个
|
||
&[data-template='2'],
|
||
&[data-template='4'] {
|
||
// 整体灰色背景让卡片有层次
|
||
background: #f4f6fa;
|
||
padding: 20rpx;
|
||
margin: 0;
|
||
|
||
.goods-item {
|
||
display: flex;
|
||
align-items: stretch;
|
||
max-width: 100%;
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
overflow: hidden;
|
||
margin: 0 0 20rpx;
|
||
box-sizing: border-box;
|
||
position: relative;
|
||
}
|
||
|
||
// 单列视图: 整行横向卡片
|
||
&.single-column .goods-item {
|
||
flex-direction: row;
|
||
width: 100% !important;
|
||
padding: 20rpx;
|
||
}
|
||
|
||
// 双列视图: 每行两个纵向卡片
|
||
&.double-column .goods-item {
|
||
flex-direction: column;
|
||
width: calc(50% - 10rpx) !important;
|
||
padding: 0;
|
||
|
||
.goods-img {
|
||
width: 100%;
|
||
height: 0;
|
||
padding-top: 100%;
|
||
margin-right: 0;
|
||
border-radius: 12rpx 12rpx 0 0;
|
||
|
||
image {
|
||
border-radius: 12rpx 12rpx 0 0;
|
||
}
|
||
}
|
||
|
||
.info-wrap {
|
||
padding: 16rpx 16rpx 20rpx;
|
||
}
|
||
|
||
// 竖卡片下价格不再给右侧按钮留位
|
||
.price-wrap {
|
||
margin-top: auto;
|
||
padding-right: 0;
|
||
}
|
||
|
||
// 加购/选规格按钮从绝对定位改为跟在价格下方右侧, 避免遮挡价格
|
||
.footer-wrap {
|
||
position: static;
|
||
right: auto;
|
||
bottom: auto;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
align-items: center;
|
||
margin-top: 12rpx;
|
||
padding: 0 16rpx;
|
||
}
|
||
}
|
||
|
||
// 商品图片: 左侧正方形
|
||
.goods-img {
|
||
width: 200rpx;
|
||
height: 200rpx;
|
||
padding-top: 0;
|
||
margin-right: 20rpx;
|
||
border-radius: 12rpx;
|
||
flex-shrink: 0;
|
||
position: relative;
|
||
overflow: hidden;
|
||
background: #fff;
|
||
|
||
image {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: absolute !important;
|
||
top: 0;
|
||
left: 0;
|
||
transform: none;
|
||
border-radius: 12rpx;
|
||
}
|
||
|
||
// 标签(秒杀/爆款): 位于图片左上角
|
||
.goods-tag {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
padding: 6rpx 16rpx;
|
||
font-size: 22rpx;
|
||
color: #fff;
|
||
background: $base-color;
|
||
border-bottom-right-radius: 12rpx;
|
||
border-top-left-radius: 12rpx;
|
||
z-index: 2;
|
||
line-height: 1;
|
||
}
|
||
}
|
||
|
||
// 信息区域: 右侧占满
|
||
.info-wrap {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding: 0;
|
||
min-width: 0;
|
||
position: relative;
|
||
}
|
||
|
||
// 商品名称: 2行省略
|
||
.name-wrap {
|
||
width: 100%;
|
||
|
||
.goods-name {
|
||
font-size: 26rpx;
|
||
line-height: 1.4;
|
||
margin-top: 0;
|
||
white-space: normal;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
color: #333;
|
||
}
|
||
}
|
||
|
||
// 价格区域: 当前价(红) + 删除价(灰色删除线)
|
||
.price-wrap {
|
||
margin-top: auto;
|
||
display: flex;
|
||
align-items: baseline;
|
||
flex-wrap: wrap;
|
||
padding-right: 90rpx; // 留出右侧+按钮的位置
|
||
|
||
.discount-price {
|
||
display: inline-flex;
|
||
align-items: baseline;
|
||
color: var(--price-color, #FF6B35);
|
||
font-weight: bold;
|
||
line-height: 1;
|
||
|
||
.unit {
|
||
font-size: 24rpx;
|
||
margin-right: 2rpx;
|
||
color: var(--price-color, #FF6B35);
|
||
}
|
||
|
||
.price {
|
||
font-size: 36rpx;
|
||
color: var(--price-color, #FF6B35);
|
||
}
|
||
}
|
||
|
||
// 删除线价格: 紧跟当前价,灰色
|
||
.delete-price {
|
||
margin-left: 16rpx;
|
||
display: inline-flex;
|
||
align-items: baseline;
|
||
text-decoration: line-through;
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
line-height: 1;
|
||
|
||
.unit {
|
||
margin-right: 2rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 底部行: 仅在模板4下保留用于挂载 right-wrap
|
||
.footer-wrap {
|
||
position: absolute;
|
||
right: 20rpx;
|
||
bottom: 20rpx;
|
||
margin-top: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
// 右侧+按钮 / 择规格按钮
|
||
.right-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
margin-top: 0;
|
||
padding: 0;
|
||
|
||
.num {
|
||
width: auto;
|
||
padding: 0 16rpx;
|
||
line-height: 1;
|
||
color: #333;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.num-action {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 50rpx;
|
||
height: 50rpx;
|
||
background: $base-color;
|
||
border-radius: 8rpx;
|
||
position: relative;
|
||
box-shadow: 0 2rpx 6rpx rgba(255, 107, 53, 0.3);
|
||
|
||
.click-event {
|
||
position: absolute;
|
||
width: 2rpx;
|
||
height: 2rpx;
|
||
left: 0;
|
||
top: 0;
|
||
transform: translate(-50%, -50%);
|
||
z-index: 5;
|
||
}
|
||
|
||
&.reduce {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
background-color: #fff;
|
||
border: 2rpx solid $base-color;
|
||
box-sizing: border-box;
|
||
|
||
.icon-jian {
|
||
color: $base-color;
|
||
}
|
||
}
|
||
}
|
||
|
||
.icon-jian,
|
||
.icon-jia {
|
||
color: var(--btn-text-color, #fff);
|
||
font-weight: bold;
|
||
font-size: 30rpx;
|
||
line-height: 1;
|
||
}
|
||
|
||
// 择规格按钮
|
||
.select-sku {
|
||
font-weight: bold;
|
||
color: var(--btn-text-color, #fff);
|
||
font-size: 24rpx;
|
||
padding: 12rpx 24rpx;
|
||
position: relative;
|
||
line-height: 1;
|
||
text-align: center;
|
||
border-radius: 30rpx;
|
||
background: $base-color;
|
||
box-shadow: 0 2rpx 6rpx rgba(255, 107, 53, 0.3);
|
||
|
||
.num-tag {
|
||
position: absolute;
|
||
top: -8rpx;
|
||
right: -8rpx;
|
||
display: inline-block;
|
||
box-sizing: border-box;
|
||
color: #fff;
|
||
line-height: 1.2;
|
||
text-align: center;
|
||
font-size: 22rpx;
|
||
padding: 0 6rpx;
|
||
min-width: 28rpx;
|
||
border-radius: 16rpx;
|
||
background-color: var(--price-color, #FF6B35);
|
||
border: 2rpx solid #fff;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 模板2/4: 划线价放在现价正下方(price-wrap 为 flex wrap, 划线价占满一行即换到现价下一行, 左边与现价对齐)
|
||
&[data-template='2'],
|
||
&[data-template='4'] {
|
||
.price-wrap .delete-price {
|
||
margin-left: 0;
|
||
margin-top: 6rpx;
|
||
width: 100%;
|
||
}
|
||
}
|
||
}
|
||
|
||
.categoty-goods-wrap {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
|
||
.scroll-goods-view {
|
||
flex: 1;
|
||
height: 0;
|
||
transform: translateX(0px);
|
||
}
|
||
}
|
||
|
||
.screen-category-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
position: relative;
|
||
|
||
.icon-unfold {
|
||
position: absolute;
|
||
right: 15rpx;
|
||
top: 0;
|
||
z-index: 2;
|
||
width: 70rpx;
|
||
height: 70rpx;
|
||
line-height: 62rpx;
|
||
text-align: center;
|
||
font-size: 36rpx;
|
||
color: #999;
|
||
padding: 0;
|
||
}
|
||
}
|
||
|
||
.screen-category {
|
||
flex: 1;
|
||
width: 0;
|
||
padding: 0 70rpx 20rpx 0;
|
||
white-space: nowrap;
|
||
height: 60rpx;
|
||
|
||
.item {
|
||
display: inline-block;
|
||
padding: 4rpx 24rpx;
|
||
background: #f5f5f5;
|
||
color: #666;
|
||
margin-right: 20rpx;
|
||
border-radius: 40rpx;
|
||
|
||
&.selected {
|
||
background-color: $base-color;
|
||
color: var(--btn-text-color);
|
||
}
|
||
}
|
||
}
|
||
|
||
::v-deep .uni-popup__wrapper-box {
|
||
border-radius: 0;
|
||
}
|
||
|
||
.screen-category-popup {
|
||
display: flex;
|
||
|
||
.screen-category {
|
||
white-space: break-spaces;
|
||
padding: 20rpx;
|
||
height: auto;
|
||
}
|
||
|
||
.title {
|
||
line-height: 1;
|
||
margin-bottom: 20rpx;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.item {
|
||
margin-bottom: 20rpx;
|
||
}
|
||
}
|
||
|
||
.end-tips {
|
||
text-align: center;
|
||
color: #999;
|
||
font-size: 24rpx;
|
||
padding: 30rpx 0 30rpx 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
::v-deep .loading-layer {
|
||
background: #fff !important;
|
||
}
|
||
|
||
.category-empty {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-direction: column;
|
||
padding-top: 100rpx;
|
||
|
||
image {
|
||
width: 280rpx;
|
||
height: 252rpx;
|
||
}
|
||
|
||
.tips {
|
||
font-size: 26rpx;
|
||
font-weight: 500;
|
||
color: #999;
|
||
margin-top: 50rpx;
|
||
}
|
||
}
|
||
|
||
.screen-category-4 .item {
|
||
background-color: #f2f2f2 !important;
|
||
padding: 16rpx 40rpx;
|
||
line-height: 1;
|
||
font-size: $font-size-tag;
|
||
|
||
&.selected {
|
||
background-color: $base-color !important;
|
||
color: #fff;
|
||
}
|
||
}
|
||
|
||
.consult-now-btn-wrap {
|
||
display: var(--diy-category-item-consult-now-wrap-display, flex);
|
||
justify-content: var(--diy-category-item-consult-now-wrap-justify-content, flex-end);
|
||
margin-top: var(--diy-category-item-consult-now-wrap-margin-top, auto);
|
||
padding-top: var(--diy-category-item-consult-now-wrap-padding-top, 10rpx);
|
||
}
|
||
|
||
.consult-now-btn {
|
||
// 兼容: 微信小程序 / 鸿蒙元服务 — 所有 var() 属性无问题
|
||
// 10 个属性都用 --diy-category-item-consult-now-* 独立 token,
|
||
// 便于后台单独调整任何一项(配色 / 尺寸 / 字重 / 对齐 / 显示模式)
|
||
background-color: var(--diy-category-item-consult-now-bg, #FF4646);
|
||
color: var(--diy-category-item-consult-now-text, #FFFFFF);
|
||
padding: var(--diy-category-item-consult-now-padding, 12rpx 30rpx);
|
||
border-radius: var(--diy-category-item-consult-now-radius, 40rpx);
|
||
font-size: var(--diy-category-item-consult-now-size, 26rpx);
|
||
font-weight: var(--diy-category-item-consult-now-weight, 500);
|
||
line-height: var(--diy-category-item-consult-now-line-height,1.2);
|
||
text-align: var(--diy-category-item-consult-now-text-align, center);
|
||
white-space: var(--diy-category-item-consult-now-white-space,nowrap);
|
||
display: var(--diy-category-item-consult-now-display, inline-block);
|
||
}
|
||
|
||
.font-size-activity-tag {
|
||
font-size: var(--diy-category-item-delete-price-size, $font-size-activity-tag) !important;
|
||
}
|
||
.color-tip {
|
||
color: var(--diy-category-item-color-tip-color, $color-tip) !important;
|
||
}
|
||
.price-font {
|
||
font-weight: var(--diy-category-item-price-font-weight, 500) !important;
|
||
}
|
||
|
||
// ===================== 模板99 商品分类样式 =====================
|
||
.second-category-btns {
|
||
white-space: nowrap;
|
||
padding: 20rpx 20rpx 20rpx 24rpx;
|
||
height: 100rpx;
|
||
background: #fff;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
box-sizing: border-box;
|
||
|
||
.second-category-btn {
|
||
display: inline-block;
|
||
padding: 10rpx 24rpx;
|
||
background: #f5f5f5;
|
||
color: #666;
|
||
border-radius: 30rpx;
|
||
font-size: 20rpx;
|
||
margin: 0 14rpx 0rpx 0;
|
||
max-width: 100%;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
vertical-align: middle;
|
||
|
||
&.selected {
|
||
background: #FF4646;
|
||
color: #FFFFFF;
|
||
}
|
||
}
|
||
}
|
||
|
||
.category-goods-list {
|
||
padding: 20rpx;
|
||
background: #f5f5f5;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
|
||
.goods-list {
|
||
margin: 0;
|
||
padding-top: 0;
|
||
display: flex;
|
||
|
||
.goods-item {
|
||
width: calc(50% - 10rpx);
|
||
background: #fff;
|
||
border-radius: 12rpx;
|
||
overflow: hidden;
|
||
margin-bottom: 20rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
box-sizing: border-box;
|
||
|
||
.goods-img {
|
||
width: 100%;
|
||
height: 0;
|
||
padding-top: 100%;
|
||
position: relative;
|
||
overflow: hidden;
|
||
background: #fff;
|
||
border-top-left-radius: 12rpx;
|
||
border-top-right-radius: 12rpx;
|
||
|
||
image {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: absolute !important;
|
||
top: 0;
|
||
left: 0;
|
||
transform: none;
|
||
}
|
||
|
||
.goods-tag {
|
||
position: absolute;
|
||
top: 10rpx;
|
||
left: 10rpx;
|
||
padding: 4rpx 12rpx;
|
||
color: #fff;
|
||
font-size: 22rpx;
|
||
border-radius: 6rpx;
|
||
background: #FF6B35;
|
||
z-index: 2;
|
||
}
|
||
}
|
||
|
||
.info-wrap {
|
||
flex: 1;
|
||
padding: 16rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.name-wrap {
|
||
.goods-name {
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
-webkit-line-clamp: 2;
|
||
display: -webkit-box;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
line-height: 1.4;
|
||
margin-top: 0;
|
||
white-space: normal;
|
||
text-overflow: clip;
|
||
}
|
||
}
|
||
|
||
.price-wrap {
|
||
margin-top: 10rpx;
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
// 划线价占满一行, 换行到现价正下方
|
||
flex-wrap: wrap;
|
||
|
||
.discount-price {
|
||
color: #FF6B35;
|
||
font-size: 30rpx;
|
||
font-weight: bold;
|
||
|
||
.unit {
|
||
font-size: 22rpx;
|
||
}
|
||
}
|
||
|
||
.delete-price {
|
||
margin-left: 0;
|
||
margin-top: 6rpx;
|
||
width: 100%;
|
||
display: inline-flex;
|
||
align-items: baseline;
|
||
text-decoration: line-through;
|
||
}
|
||
}
|
||
|
||
.footer-wrap {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
margin-top: 10rpx;
|
||
}
|
||
}
|
||
|
||
&.single-column {
|
||
flex-direction: column;
|
||
padding: 0 20rpx 0 8rpx;
|
||
gap: 20rpx;
|
||
|
||
.goods-item {
|
||
width: calc(100% + 20rpx);
|
||
flex-direction: row;
|
||
align-items: flex-start;
|
||
background: #fff;
|
||
border-radius: 12rpx;
|
||
margin-bottom: 0;
|
||
padding: 20rpx;
|
||
box-sizing: border-box;
|
||
height: 220rpx;
|
||
|
||
.goods-img {
|
||
width: 180rpx;
|
||
height: 180rpx;
|
||
padding-top: 0;
|
||
margin-right: 14rpx;
|
||
border-radius: 12rpx;
|
||
flex-shrink: 0;
|
||
|
||
image {
|
||
border-radius: 12rpx;
|
||
}
|
||
}
|
||
|
||
.info-wrap {
|
||
flex: 1;
|
||
padding: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
}
|
||
|
||
.name-wrap {
|
||
.goods-name {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
-webkit-line-clamp: 2;
|
||
display: -webkit-box;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
line-height: 1.4;
|
||
margin-top: 0;
|
||
white-space: normal;
|
||
text-overflow: clip;
|
||
}
|
||
}
|
||
|
||
.price-wrap {
|
||
margin-top: 8rpx;
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: flex-start;
|
||
|
||
.discount-price {
|
||
color: #FF6B35;
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
|
||
.unit {
|
||
font-size: 24rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.footer-wrap {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
margin-top: auto;
|
||
}
|
||
}
|
||
}
|
||
|
||
&.double-column {
|
||
.goods-item {
|
||
width: calc(50% - 10rpx);
|
||
flex-direction: column;
|
||
background: #fff;
|
||
border-radius: 12rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.category-goods-empty {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 80rpx 0;
|
||
background: #f5f5f5;
|
||
width: 100%;
|
||
|
||
image {
|
||
width: 280rpx;
|
||
height: 252rpx;
|
||
}
|
||
|
||
.tips {
|
||
font-size: 26rpx;
|
||
color: #999;
|
||
margin-top: 20rpx;
|
||
}
|
||
}
|
||
</style> |