1799 lines
46 KiB
Vue
1799 lines
46 KiB
Vue
<template>
|
||
<view data-component-name="diy-category" :class="['category-page-wrap', 'category-template-' + value.template]"
|
||
:style="{ height: 'calc(100vh - var(--window-top, 0px) - ' + tabBarHeight + ')' }">
|
||
<!-- 高度说明: 100vh - H5顶部导航栏(--window-top, 小程序为0) - 底部导航高度(tabBarHeight),
|
||
保证底部购物车结算栏始终落在可视区内, 点击加购后无需向下翻即可看到 -->
|
||
<!-- 模板4: 顶部搜索框(图片样式) -->
|
||
<view class="search-box search-box-template-4" v-if="value.template == 4 && value.search"
|
||
@tap.stop="onClickSearch()">
|
||
<view class="search-content">
|
||
<input type="text" class="uni-input" maxlength="50" :placeholder="$lang('components-diy.diy-search.searchBtn')"
|
||
confirm-type="search" @tap.stop="onClickSearch()" disabled="true" />
|
||
<text class="iconfont icon-sousuo3"></text>
|
||
</view>
|
||
</view>
|
||
<!-- #ifdef MP-WEIXIN -->
|
||
<!-- <block v-if="value.template == 4">
|
||
<view class="search-box" v-if="value.search" @tap.stop="$util.redirectTo('/pages_tool/goods/search')" :style="navbarInnerStyle">
|
||
<view class="search-content">
|
||
<input type="text" class="uni-input font-size-tag" maxlength="50" :placeholder="$lang('components-diy.diy-search.searchBtn')" confirm-type="search" @tap.stop="onClickSearch()" disabled="true" />
|
||
<text class="iconfont icon-sousuo3"></text>
|
||
</view>
|
||
</view>
|
||
<view :style="navbarInnerStyle" v-if="!value.search">商品分类</view>
|
||
</block> -->
|
||
<block v-if="value.template != 4">
|
||
<!-- <view :style="navbarInnerStyle">商品分类</view> -->
|
||
<view class="search-box" v-if="value.search" @tap.stop="onClickSearch()"
|
||
:style="wxSearchHeight">
|
||
<view class="search-content">
|
||
<input type="text" class="uni-input" maxlength="50" :placeholder="$lang('components-diy.diy-search.searchBtn')"
|
||
confirm-type="search" @tap.stop="onClickSearch()"
|
||
disabled="true" />
|
||
<text class="iconfont icon-sousuo3"></text>
|
||
</view>
|
||
<view class="iconfont" :class="{ 'icon-apps': !isList, 'icon-list': isList }"
|
||
@tap.stop.prevent="changeListStyle()"></view>
|
||
</view>
|
||
</block>
|
||
<!-- #endif -->
|
||
<!-- #ifdef H5 || MP-HARMONY -->
|
||
|
||
<view class="search-box" v-if="value.search && value.template != 4" @tap.stop="onClickSearch()">
|
||
<view class="search-content">
|
||
<input type="text" class="uni-input" maxlength="50" :placeholder="$lang('components-diy.diy-search.searchBtn')" confirm-type="search"
|
||
@tap.stop="onClickSearch()" disabled="true" />
|
||
<text class="iconfont icon-sousuo3"></text>
|
||
</view>
|
||
<view class="iconfont" :class="{ 'icon-apps': !isList, 'icon-list': isList }"
|
||
@tap.stop.prevent="changeListStyle()"></view>
|
||
</view>
|
||
|
||
<!-- #endif -->
|
||
<view class="template-four wx" v-if="value.template == 4">
|
||
<scroll-view scroll-x="true" class="template-four-wrap" :scroll-with-animation="true"
|
||
:scroll-into-view="'category-one-' + oneCategorySelect" enable-flex="true">
|
||
<view class="category-item" :id="'category-one-' + index" v-for="(item, index) in templateFourData"
|
||
:key="index" :class="{ select: oneCategorySelect == index }" @tap.stop="templateFourOneFn(index)">
|
||
<view class="image-warp" :class="[{ 'color-base-border': oneCategorySelect == index }]">
|
||
<image :src="$util.img(item.image)" mode="aspectFill" show-menu-by-longpress/>
|
||
</view>
|
||
<view :class="['text', { 'color-base-bg': oneCategorySelect == index }]">{{ item.category_name }}
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
<view class="category-item-all" @tap.stop="$refs.templateFourPopup.open()">
|
||
<view class="category-item-all-wrap">
|
||
<text class="text">{{ $lang('components-diy.diy-category.expand') }}</text>
|
||
<image class="img" :src="$util.img('/public/uniapp/category/unfold.png')" mode="aspectFill" show-menu-by-longpress></image>
|
||
</view>
|
||
</view>
|
||
<uni-popup type="top" ref="templateFourPopup" :top="uniPopupTop">
|
||
<view class="template-four-popup">
|
||
<scroll-view scroll-y="true" class="template-four-scroll" enable-flex="true">
|
||
<view class="item" :class="{ selected: oneCategorySelect == index }"
|
||
@tap.stop="templateFourOneFn(index)" v-for="(item, index) in templateFourData" :key="index">
|
||
<view class="image-warp" :class="[{ 'color-base-border': oneCategorySelect == index }]">
|
||
<image :src="$util.img(item.image)" mode="aspectFill" show-menu-by-longpress></image>
|
||
</view>
|
||
<view :class="['text', { 'color-base-bg': oneCategorySelect == index }]">{{
|
||
item.category_name }}</view>
|
||
</view>
|
||
</scroll-view>
|
||
<view class="pack-up" @tap.stop="$refs.templateFourPopup.close()">
|
||
<text>{{ $lang('components-diy.diy-category.clickToCollapse') }}</text>
|
||
<text class="iconfont icon-iconangledown-copy"></text>
|
||
</view>
|
||
</view>
|
||
</uni-popup>
|
||
</view>
|
||
|
||
<!-- 全屏骨架占位 (只显示: 左分类树 + 右商品网格 + 底部空隙, 不画 navbar/TabBar,
|
||
避免与小程序/H5 顶部 navbar / diy-bottom-nav 错位)。
|
||
通过 v-if 拿到 categoryTreeLoading 时按真实布局骨架填充。
|
||
search-box / 商品 nav-bar 由外层 category.vue 渲染, 不在本组件里动它 -->
|
||
<view class="category-page-skeleton" v-if="categoryTreeLoading">
|
||
<view class="skeleton-content">
|
||
<view class="skeleton-tree">
|
||
<view class="skeleton-tree-item" v-for="n in 8" :key="n"></view>
|
||
</view>
|
||
<view class="skeleton-grid">
|
||
<view class="skeleton-card" v-for="n in 6" :key="n"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="content-box" v-if="categoryTree">
|
||
<block v-if="categoryTree.length">
|
||
<scroll-view scroll-y="true" class="tree-wrap">
|
||
<view class="category-item-wrap">
|
||
<!-- 模板4: 左侧显示一级分类(顶部轮播中的分类) -->
|
||
<view v-if="value.template == 4">
|
||
<view class="category-item" v-for="(item, index) in templateFourData" :key="index" :class="[
|
||
{ select: oneCategorySelect == index },
|
||
{ 'border-bottom': oneCategorySelect + 1 === index },
|
||
{ 'border-top': oneCategorySelect - 1 === index }
|
||
]" @tap.stop="templateFourOneFn(index)">
|
||
<view class="">{{ item.category_name }}</view>
|
||
</view>
|
||
</view>
|
||
<!-- 其他模板: 左侧显示二级分类 -->
|
||
<view v-else>
|
||
<view class="category-item" v-for="(item, index) in categoryTree" :key="index" :class="[
|
||
{ select: select == index },
|
||
{ 'border-bottom': value.template == 4 && select + 1 === index },
|
||
{ 'border-top': value.template == 4 && select - 1 === index },
|
||
{ 'first-category-item': index == 0 && value.template == 99 }
|
||
]" @tap.stop="switchOneCategory(index)">
|
||
<view class="">{{ item.category_name }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<view class="right-flex-wrap">
|
||
<!-- 模板99 + 二级分类: 全局二级分类按钮(去重, 一直显示) + 商品列表 -->
|
||
<scroll-view scroll-y="true" class="content-wrap" v-if="value.template == 99 && template99Category"
|
||
:refresher-enabled="true" refresher-default-style="none"
|
||
:refresher-triggered="triggered" @refresherrefresh="onRefresh" @refresherrestore="onRestore">
|
||
<diy-category-item :category="template99Category" :value="value" ref="categoryItem" :index="0" :select="0"
|
||
:oneCategorySelect="oneCategorySelect" :isList="isList"
|
||
:currentOneCategory="categoryTree && categoryTree[select] ? categoryTree[select] : null"
|
||
:lastSecondCategoryName="lastSecondCategoryName"
|
||
@tologin="toLogin" @selectsku="selectSku($event, 0)" @addCart="addCartPoint"
|
||
@saveSecondCategory="saveSecondCategoryName"></diy-category-item>
|
||
</scroll-view>
|
||
|
||
<!-- 模板1: 仿京东/淘宝商品分类 - 按 template 2 的 part-load 逻辑, 切换一级分类时只渲染选中分类的子分类网格 -->
|
||
<scroll-view scroll-y="true" class="content-wrap" v-else-if="value.template == 1"
|
||
:refresher-enabled="true" refresher-default-style="none"
|
||
:refresher-triggered="triggered" @refresherrefresh="onRefresh" @refresherrestore="onRestore">
|
||
<view class="child-category-wrap" v-for="(item, index) in categoryTree" :key="index"
|
||
:id="'category-' + index" :style="{ display: select == index ? 'block' : 'none' }">
|
||
<diy-category-item :category="item" :value="value" ref="categoryItem" :index="index"
|
||
:select="select" :oneCategorySelect="oneCategorySelect" :isList="isList"
|
||
@tologin="toLogin" @selectsku="selectSku($event, index)" @addCart="addCartPoint"
|
||
@saveSecondCategory="saveSecondCategoryName"></diy-category-item>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<scroll-view scroll-y="true" class="content-wrap" v-else-if="value.template == 99 || loadType == 'all'"
|
||
ref="contentWrap" :scroll-into-view="categoryId" :scroll-with-animation="true"
|
||
@scroll="listenScroll" @touchstart="touchStart" :refresher-enabled="true"
|
||
refresher-default-style="none" :refresher-triggered="triggered" @refresherrefresh="onRefresh"
|
||
@refresherrestore="onRestore">
|
||
<view class="child-category" v-for="(item, index) in categoryTree" :key="index"
|
||
:id="'category-' + index" :style="{ display: value.template == 99 && select != index ? 'none' : 'block' }">
|
||
<diy-category-item :category="item" :value="value" ref="categoryItem" :index="index"
|
||
:select="select" :oneCategorySelect="oneCategorySelect" :isList="isList"
|
||
:lastSecondCategoryName="value.template == 99 ? lastSecondCategoryName : ''"
|
||
:currentOneCategory="categoryTree && categoryTree[select] ? categoryTree[select] : null"
|
||
@tologin="toLogin" @selectsku="selectSku($event, index)" @addCart="addCartPoint"
|
||
@loadfinish="getHeightArea" @saveSecondCategory="saveSecondCategoryName"></diy-category-item>
|
||
</view>
|
||
<view class="end-tips" ref="endTips" :style="{ opacity: endTips }">{{ $lang('components-diy.diy-category.reachedBottom') }}</view>
|
||
</scroll-view>
|
||
|
||
<view class="content-wrap"
|
||
v-if="(value.template == 2 || value.template == 3 || value.template == 4) && loadType == 'part'">
|
||
<view class="child-category-wrap" v-for="(item, index) in categoryTree" :key="index"
|
||
:id="'category-' + index" :style="{ display: select == index ? 'block' : 'none' }">
|
||
<diy-category-item :category="item" :value="value" ref="categoryItem" :index="index"
|
||
:last="index == categoryTree.length - 1 ? true : false" :select="select"
|
||
:oneCategorySelect="oneCategorySelect" :isList="isList" @tologin="toLogin"
|
||
:currentOneCategory="value.template == 4 && templateFourData[oneCategorySelect] ? templateFourData[oneCategorySelect] : null"
|
||
@selectsku="selectSku($event, index)" @addCart="addCartPoint"
|
||
@switch="switchOneCategory"></diy-category-item>
|
||
</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></image>
|
||
<view class="tips">{{ $lang('components-diy.diy-category.noCategory') }}</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 风格2/模板4: 底部购物车栏
|
||
1) 不用 position:fixed: 小程序/H5/鸿蒙下 fixed 受页面避让与父级 transform 影响,
|
||
会"跟在内容后面"只有滚到底才看得见。
|
||
.category-page-wrap 是固定高度(calc(100vh - tabBarHeight))的纵向 flex 容器,
|
||
content-box 为 flex:1 height:0, 把结算栏作为普通子项排在末尾,
|
||
无论右侧商品列表怎么滚动, 结算栏始终固定显示在底部
|
||
2) cartNumber > 0: 只在购物车里有商品时才出现(点加购即出现, 减到 0 即消失),
|
||
商品加购后会立即 dispatch cartCalculate 同步 cartNumber, 不依赖接口回调 -->
|
||
<view class="cart-box cart-box-template-4"
|
||
v-if="(value.template == 2 || value.template == 4) && showCartSettlement && storeToken && cartNumber > 0 && categoryTree && categoryTree.length">
|
||
<view class="left-wrap">
|
||
<view class="cart-icon" ref="cartIcon" :animation="cartAnimation"
|
||
@tap.stop="$util.redirectTo('/pages_goods/cart')">
|
||
<text class="iconfont icon-ziyuan1"></text>
|
||
<view class="num" v-if="cartNumber">{{ cartNumber < 99 ? cartNumber : '99+' }}</view>
|
||
</view>
|
||
<view class="price">
|
||
<text class="title">总计:</text>
|
||
<text class="unit font-size-tag price-font">¥</text>
|
||
<text class="money font-size-toolbar price-font">{{ cartTotalMoney[0] }}</text>
|
||
<text class="unit font-size-tag price-font">.{{ cartTotalMoney[1] ? cartTotalMoney[1] : '00' }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="right-wrap"><button type="primary" class="settlement-btn"
|
||
@tap.stop="settlement">去结算</button>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="cart-point" v-for="(item, index) in carIconList" :key="index"
|
||
:style="{ left: item.left + 'px', top: item.top + 'px' }"></view>
|
||
|
||
<ns-goods-sku-category ref="skuSelect" @refresh="refreshData" @addCart="addCartPoint"></ns-goods-sku-category>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
// 获取系统状态栏的高度
|
||
let systemInfo = uni.getSystemInfoSync();
|
||
let menuButtonInfo = {};
|
||
// 如果是小程序,获取右上角胶囊的尺寸信息,避免导航栏右侧内容与胶囊重叠(支付宝小程序非本API,尚未兼容)
|
||
// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
|
||
menuButtonInfo = uni.getMenuButtonBoundingClientRect();
|
||
// #endif
|
||
|
||
import DiyMinx from './minx.js'
|
||
var contentWrapHeight, query, cartPosition;
|
||
|
||
// 商品分类
|
||
export default {
|
||
name: 'diy-category',
|
||
props: {
|
||
value: {
|
||
type: Object,
|
||
default: () => {
|
||
return {};
|
||
}
|
||
}
|
||
},
|
||
mixins: [DiyMinx],
|
||
data() {
|
||
return {
|
||
oneCategorySelect: 0,
|
||
select: 0,
|
||
categoryId: 'category-0',
|
||
categoryTree: null,
|
||
scrollLock: false,
|
||
triggered: true,
|
||
heightArea: [],
|
||
isSub: false,
|
||
carIconList: {},
|
||
endTips: 0,
|
||
cartAnimation: {},
|
||
loadType: '',
|
||
templateFourData: [],
|
||
isList: false,
|
||
lang: uni.getStorageSync("lang"),
|
||
lastSecondCategoryName: '',
|
||
template99Category: null,
|
||
// 修复体验: 进入页面初期显示 "暂未分类" 然后又变成有数据的体验很差
|
||
// 用 categoryTreeLoading 区分"还在请求"和"真的没分类", 请求未完成时
|
||
// 显示骨架占位而非空态文案
|
||
categoryTreeLoading: true,
|
||
|
||
};
|
||
},
|
||
created() {
|
||
this.getCategoryTree();
|
||
this.loadType = this.value.goodsLevel == 1 && this.value.loadType == 'all' ? 'all' : 'part';
|
||
// 模板2/4: 默认单列列表布局(左图右文卡片), 点右上角图标切换为双列宫格
|
||
if (this.value.template == 2 || this.value.template == 4) {
|
||
this.isList = true;
|
||
}
|
||
},
|
||
mounted() {
|
||
query = uni.createSelectorQuery().in(this);
|
||
query
|
||
.select('.content-wrap')
|
||
.boundingClientRect(data => {
|
||
if (data) contentWrapHeight = data.height;
|
||
})
|
||
.exec();
|
||
setTimeout(() => {
|
||
query
|
||
.select('.end-tips')
|
||
.boundingClientRect(data => {
|
||
if (data && data.top > contentWrapHeight) this.endTips = 1;
|
||
})
|
||
.exec();
|
||
query
|
||
.select('.cart-icon')
|
||
.boundingClientRect(data => {
|
||
if (data) cartPosition = data;
|
||
})
|
||
.exec();
|
||
if (this.value.template == 1) this.getHeightArea(-1);
|
||
}, 500);
|
||
},
|
||
watch: {
|
||
// 组件刷新监听
|
||
componentRefresh: function (nval) { },
|
||
categoryTreeLoading: function (nval, oval) {
|
||
if (nval === false && oval === true) {
|
||
this.$nextTick(() => {
|
||
if (!this.heightArea.length) this.getHeightArea(-1);
|
||
});
|
||
}
|
||
}
|
||
},
|
||
computed: {
|
||
cartTotalMoney() {
|
||
let money = parseFloat(this.cartMoney).toFixed(2);
|
||
return money.split('.');
|
||
},
|
||
// 后台新增开关:快捷购买(显示购物车及结算按钮) / 加购按钮
|
||
// 后台开启哪个就显示哪个, 关闭哪个就不显示哪个; 未下发新字段时兼容旧字段 quickBuy
|
||
showCartSettlement() {
|
||
const t = this.getDiyToggle(['showCartSettlement', 'show_cart_settlement']);
|
||
if (t !== undefined) return t;
|
||
const legacy = this.getDiyToggle(['quickBuy', 'quick_buy']);
|
||
return legacy !== undefined ? legacy : false;
|
||
},
|
||
showAddCart() {
|
||
const t = this.getDiyToggle(['showAddCart', 'show_add_cart']);
|
||
if (t !== undefined) return t;
|
||
const legacy = this.getDiyToggle(['quickBuy', 'quick_buy']);
|
||
return legacy !== undefined ? legacy : false;
|
||
},
|
||
// 转换字符数值为真正的数值
|
||
navbarHeight() {
|
||
// #ifdef APP-PLUS || H5
|
||
return 44;
|
||
// #endif
|
||
// #ifdef MP
|
||
// 小程序特别处理,让导航栏高度 = 胶囊高度 + 两倍胶囊顶部与状态栏底部的距离之差(相当于同时获得了导航栏底部与胶囊底部的距离)
|
||
// 此方法有缺陷,暂不用(会导致少了几个px),采用直接固定值的方式
|
||
// return menuButtonInfo.height + (menuButtonInfo.top - this.navbarHeight) * 2;//导航高度
|
||
// let height = systemInfo.platform == 'ios' ? 44 : 48;
|
||
let height = menuButtonInfo.top;
|
||
// height += systemInfo.navbarHeight;
|
||
return height;
|
||
// #endif
|
||
},
|
||
// 导航栏内部盒子的样式
|
||
navbarInnerStyle() {
|
||
let style = '';
|
||
// 导航栏宽度,如果在小程序下,导航栏宽度为胶囊的左边到屏幕左边的距离
|
||
// style += 'height:' + this.navbarHeight + 'px;';
|
||
// // 如果是各家小程序,导航栏内部的宽度需要减少右边胶囊的宽度
|
||
// #ifdef MP
|
||
if (this.value.template == 4 && this.value.search) {
|
||
style += 'height:' + menuButtonInfo.height + 'px;';
|
||
let rightButtonWidth = menuButtonInfo.width ? menuButtonInfo.width * 2 + 'rpx' : '70rpx';
|
||
style += 'padding-right:calc(' + rightButtonWidth + ' + 30rpx);';
|
||
style += 'padding-top:' + this.navbarHeight + 'px;';
|
||
}
|
||
// #endif
|
||
if (this.value.template != 4 || (this.value.template == 4 && !this.value.search)) {
|
||
style += 'height:' + menuButtonInfo.height * 2 + 'rpx;';
|
||
style += 'padding-top:' + this.navbarHeight + 'px;';
|
||
style += 'text-align: center;';
|
||
style += 'line-height:' + menuButtonInfo.height * 2 + 'rpx;';
|
||
style += 'font-size: 14px;';
|
||
style += 'padding-bottom: 10rpx;';
|
||
}
|
||
return style;
|
||
},
|
||
wxSearchHeight() {
|
||
let style = '';
|
||
// #ifdef MP
|
||
style += 'height: 64rpx;';
|
||
// #endif
|
||
return style;
|
||
},
|
||
uniPopupTop() {
|
||
let top = 0;
|
||
// #ifdef MP
|
||
top = this.navbarHeight + menuButtonInfo.height + 'px';
|
||
// #endif
|
||
|
||
// #ifdef H5
|
||
top = '100rpx';
|
||
// #endif
|
||
|
||
return top;
|
||
}
|
||
},
|
||
methods: {
|
||
/**
|
||
* 页面显示
|
||
*/
|
||
pageShow() {
|
||
this.$store.dispatch('getCartNumber');
|
||
if (!this.heightArea.length) this.getHeightArea(-1);
|
||
},
|
||
|
||
/**
|
||
* 获取高度区间
|
||
*
|
||
* 鸿蒙 JSVM 在 mounted() 之前触发 onShow,导致 query 还是 undefined
|
||
* 加防御性检查,避免上线后第一次进入崩溃
|
||
*/
|
||
getHeightArea(index) {
|
||
// 节流: 500ms 内的重复请求复用上次结果, 避免每次 onShow 都跑 selectAll 触发频闪
|
||
const now = Date.now();
|
||
if (this._lastHeightAreaTime && now - this._lastHeightAreaTime < 500 && this.heightArea && this.heightArea.length) {
|
||
if (index != -1 && index < this.categoryTree.length - 1 && Array.isArray(this.$refs.categoryItem) && this.$refs.categoryItem[index + 1]) this.$refs.categoryItem[index + 1].getGoodsList();
|
||
return;
|
||
}
|
||
this._lastHeightAreaTime = now;
|
||
let heightArea = [];
|
||
if (!query || typeof query.selectAll !== 'function') {
|
||
query = uni.createSelectorQuery && uni.createSelectorQuery().in(this);
|
||
}
|
||
if (!query || typeof query.selectAll !== 'function') {
|
||
// 极端兜底:鸿蒙下若 createSelectorQuery 不存在,跳过计算
|
||
this.heightArea = heightArea;
|
||
return;
|
||
}
|
||
query
|
||
.selectAll('.content-wrap .child-category')
|
||
.boundingClientRect(data => {
|
||
if (data && data.length) {
|
||
data.forEach((item, index) => {
|
||
if (index == 0) heightArea.push([0, item.height]);
|
||
else heightArea.push([heightArea[index - 1][1], heightArea[index - 1][1] +
|
||
item.height
|
||
]);
|
||
});
|
||
}
|
||
})
|
||
.exec();
|
||
this.heightArea = heightArea;
|
||
if (index != -1 && index < this.categoryTree.length - 1 && Array.isArray(this.$refs.categoryItem) && this.$refs.categoryItem[index + 1]) this.$refs.categoryItem[index + 1].getGoodsList();
|
||
},
|
||
/**
|
||
* 获取全部分类
|
||
*/
|
||
getCategoryTree() {
|
||
this.categoryTreeLoading = true;
|
||
this.$api.sendRequest({
|
||
url: '/api/goodscategory/tree',
|
||
data: {
|
||
level: 3
|
||
},
|
||
success: res => {
|
||
if (res.code == 0) {
|
||
|
||
this.categoryTree = res.data;
|
||
if (this.value.template == 4) {
|
||
this.templateFourData = JSON.parse(JSON.stringify(this.categoryTree));
|
||
this.categoryTree = this.templateFourData[0].child_list;
|
||
}
|
||
//切换英文标题
|
||
if (this.lang == 'en-us') {
|
||
this.categoryTree.map(item => {
|
||
item?.child_list?.map(items => {
|
||
items.category_name = items.en_category_name;
|
||
return items;
|
||
})
|
||
item.category_name = item.en_category_name;
|
||
return item;
|
||
});
|
||
}
|
||
// 模板99 + 二级分类: 全局查询所有有商品的二级分类(去重), 按钮一直显示
|
||
if (this.value.template == 99) {
|
||
this.loadGlobalSecondCategoryBtns();
|
||
}
|
||
}
|
||
},
|
||
fail: () => {
|
||
// 失败也保证 loading 关闭, 让骨架替换为"暂无分类"空态
|
||
this.categoryTree = this.categoryTree || [];
|
||
},
|
||
complete: () => {
|
||
// 无论成功失败都关闭 loading 骨架
|
||
this.categoryTreeLoading = false;
|
||
}
|
||
});
|
||
},
|
||
/**
|
||
* 全局查询所有有商品的二级分类, 按名称去重, 汇总成虚拟分类供 template99 渲染按钮
|
||
* 只要任何商品属于该二级分类, 按钮就显示; 切换一级分类时按钮不变(一直显示)
|
||
* 新增的二级分类只要有商品也会自动包含
|
||
*/
|
||
loadGlobalSecondCategoryBtns() {
|
||
if (!this.categoryTree || !this.categoryTree.length) {
|
||
this.template99Category = { category_name: '', child_list: [] };
|
||
return;
|
||
}
|
||
// 收集所有二级分类(保留原始顺序, 不去重, 查询所有同名后再去重)
|
||
const allSecond = [];
|
||
this.categoryTree.forEach(one => {
|
||
if (one.child_list && one.child_list.length) {
|
||
one.child_list.forEach(two => {
|
||
allSecond.push(Object.assign({ _origIndex: allSecond.length }, two));
|
||
});
|
||
}
|
||
});
|
||
if (!allSecond.length) {
|
||
this.template99Category = { category_name: '', child_list: [] };
|
||
return;
|
||
}
|
||
let pending = allSecond.length;
|
||
const results = [];
|
||
const finishOne = () => {
|
||
pending--;
|
||
if (pending <= 0) {
|
||
results.sort((a, b) => a._origIndex - b._origIndex);
|
||
// 按名称去重, 同名只保留第一个有商品的(只要任一同名有商品就显示)
|
||
const deduped = [];
|
||
const seenNames = {};
|
||
results.forEach(item => {
|
||
if (!seenNames[item.category_name]) {
|
||
seenNames[item.category_name] = true;
|
||
deduped.push(item);
|
||
}
|
||
});
|
||
this.template99Category = {
|
||
category_name: '',
|
||
child_list: deduped
|
||
};
|
||
}
|
||
};
|
||
allSecond.forEach(two => {
|
||
this.$api.sendRequest({
|
||
url: '/api/goodssku/pageByCategory',
|
||
data: {
|
||
category_id: two.category_id,
|
||
page: 1,
|
||
page_size: 1
|
||
},
|
||
success: res => {
|
||
if (res && res.code == 0 && res.data && res.data.list && res.data.list.length > 0) {
|
||
results.push(two);
|
||
}
|
||
},
|
||
complete: () => {
|
||
finishOne();
|
||
}
|
||
});
|
||
});
|
||
},
|
||
/**
|
||
* 切换一级分类
|
||
* @param {Object} index
|
||
*/
|
||
switchOneCategory(index) {
|
||
if (index >= this.categoryTree.length) return;
|
||
this.select = index;
|
||
this.categoryId = 'category-' + index;
|
||
this.scrollLock = true;
|
||
},
|
||
saveSecondCategoryName(name) {
|
||
this.lastSecondCategoryName = name;
|
||
},
|
||
touchStart() {
|
||
this.scrollLock = false;
|
||
},
|
||
/**
|
||
* 监听滚动
|
||
* @param {Object} event
|
||
*/
|
||
listenScroll(event) {
|
||
if (this.scrollLock) return;
|
||
let scrollTop = event.detail.scrollTop;
|
||
if (this.heightArea.length) {
|
||
for (let i = 0; i < this.heightArea.length; i++) {
|
||
if (scrollTop >= this.heightArea[i][0] && scrollTop <= this.heightArea[i][1]) {
|
||
this.select = i;
|
||
break;
|
||
}
|
||
}
|
||
if (this.value.template != 1 && this.value.loadType == 'all' && this.heightArea[this.select][1] -
|
||
scrollTop - contentWrapHeight < 300) {
|
||
if (Array.isArray(this.$refs.categoryItem) && this.$refs.categoryItem[this.select]) this.$refs.categoryItem[this.select].getGoodsList();
|
||
}
|
||
}
|
||
},
|
||
onRefresh() {
|
||
this.triggered = false;
|
||
},
|
||
onRestore() {
|
||
this.triggered = 'restore'; // 需要重置
|
||
},
|
||
toLogin() {
|
||
this.$emit('tologin');
|
||
},
|
||
/**
|
||
* sku选择
|
||
* @param {Object} data
|
||
* @param {Object} index
|
||
*/
|
||
selectSku(data, index) {
|
||
this.$api.sendRequest({
|
||
url: '/api/goodssku/getInfoForCategory',
|
||
data: {
|
||
sku_id: data.sku_id
|
||
},
|
||
success: res => {
|
||
if (res.code >= 0) {
|
||
let item = res.data;
|
||
item.unit = item.unit || this.$lang('components-diy.diy-category.pieceUnit');
|
||
|
||
if (item.sku_images) item.sku_images = item.sku_images.split(',');
|
||
else item.sku_images = [];
|
||
|
||
// 多规格时合并主图
|
||
if (item.goods_spec_format && item.goods_image) {
|
||
item.goods_image = item.goods_image.split(',');
|
||
item.sku_images = item.goods_image.concat(item.sku_images);
|
||
}
|
||
|
||
// 当前商品SKU规格
|
||
if (item.sku_spec_format) item.sku_spec_format = JSON.parse(item.sku_spec_format);
|
||
|
||
// 商品SKU格式
|
||
if (item.goods_spec_format) item.goods_spec_format = JSON.parse(item.goods_spec_format);
|
||
|
||
// 限时折扣
|
||
if (item.promotion_type == 1) {
|
||
item.discountTimeMachine = this.$util.countDown(item.end_time - res.timestamp);
|
||
}
|
||
|
||
if (item.promotion_type == 1 && item.discountTimeMachine) {
|
||
if (item.member_price > 0 && Number(item.member_price) <= Number(item.discount_price)) {
|
||
item.show_price = item.member_price;
|
||
} else {
|
||
item.show_price = item.discount_price;
|
||
}
|
||
} else if (item.member_price > 0) {
|
||
item.show_price = item.member_price;
|
||
} else {
|
||
item.show_price = item.price;
|
||
}
|
||
|
||
this.$refs.skuSelect.show(item);
|
||
}
|
||
}
|
||
});
|
||
},
|
||
settlement() {
|
||
if (!this.cartIds.length || this.isSub) return;
|
||
this.isSub = true;
|
||
|
||
uni.removeStorageSync('delivery');
|
||
uni.setStorage({
|
||
key: 'orderCreateData',
|
||
data: {
|
||
cart_ids: this.cartIds.toString()
|
||
},
|
||
success: () => {
|
||
this.$util.redirectTo('/pages_order/payment');
|
||
this.isSub = false;
|
||
}
|
||
});
|
||
},
|
||
/**
|
||
* 添加点
|
||
* @param {Object} left
|
||
* @param {Object} top
|
||
*/
|
||
addCartPoint(left, top) {
|
||
if (this.value.template != 2 && this.value.template != 4) return;
|
||
if (!this.showCartSettlement) return;
|
||
|
||
let key = new Date().getTime();
|
||
this.$set(this.carIconList, key, {
|
||
left: left,
|
||
top: top,
|
||
index: 0,
|
||
bezierPos: this.$util.bezier([{
|
||
x: left,
|
||
y: top
|
||
}, {
|
||
x: left - 200,
|
||
y: left - 120
|
||
},
|
||
{
|
||
x: cartPosition.left + 10,
|
||
y: cartPosition.top
|
||
}
|
||
], 6).bezier_points,
|
||
timer: null
|
||
});
|
||
this.startAnimation(key);
|
||
},
|
||
/**
|
||
* 执行动画
|
||
* @param {Object} key
|
||
*/
|
||
startAnimation(key) {
|
||
let bezierPos = this.carIconList[key].bezierPos,
|
||
index = this.carIconList[key].index;
|
||
|
||
this.carIconList[key].timer = setInterval(() => {
|
||
if (index < 6) {
|
||
this.carIconList[key].left = bezierPos[index].x;
|
||
this.carIconList[key].top = bezierPos[index].y;
|
||
index++;
|
||
} else {
|
||
clearInterval(this.carIconList[key].timer);
|
||
delete this.carIconList[key];
|
||
this.$forceUpdate();
|
||
|
||
// 购物车图标
|
||
setTimeout(() => {
|
||
this.$store.commit('setCartChange');
|
||
}, 100);
|
||
let animation = uni.createAnimation({
|
||
duration: 200,
|
||
timingFunction: 'ease'
|
||
});
|
||
animation.scale(1.2).step();
|
||
this.cartAnimation = animation.export();
|
||
setTimeout(() => {
|
||
animation.scale(1).step();
|
||
this.cartAnimation = animation.export();
|
||
}, 300);
|
||
}
|
||
}, 50);
|
||
},
|
||
// 风格四头部切换
|
||
templateFourOneFn(index) {
|
||
this.categoryTree = this.templateFourData[index].child_list || [];
|
||
this.oneCategorySelect = index;
|
||
this.select = 0;
|
||
},
|
||
// 操作多规格商品弹框后,刷新商品数据
|
||
refreshData() {
|
||
if (Array.isArray(this.$refs.categoryItem) && this.$refs.categoryItem[this.select]) this.$refs.categoryItem[this.select].loadGoodsCartNum(true);
|
||
},
|
||
changeListStyle() {
|
||
this.isList = !this.isList;
|
||
},
|
||
onClickSearch() {
|
||
this.$util.redirectTo('/pages_tool/goods/search');
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.category-page-wrap {
|
||
width: 100vw;
|
||
// height: calc(100vh - var(--tab-bar-height, 0));
|
||
display: flex;
|
||
flex-direction: column;
|
||
background-color: #fff;
|
||
}
|
||
|
||
.content-box {
|
||
flex: 1;
|
||
height: 0;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
|
||
.tree-wrap {
|
||
width: 170rpx;
|
||
height: 100%;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.right-flex-wrap {
|
||
flex: 1;
|
||
width: 0;
|
||
height: 100%;
|
||
background: #f4f6fa;
|
||
display: flex;
|
||
flex-direction: column;
|
||
transform: translateX(0px);
|
||
|
||
.content-wrap {
|
||
display: flex;
|
||
flex: 1;
|
||
height: 0;
|
||
width: 100%;
|
||
}
|
||
|
||
.child-category-wrap {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
}
|
||
|
||
.tree-wrap .category-item-wrap {
|
||
height: auto;
|
||
background-color: #fff;
|
||
}
|
||
|
||
.tree-wrap .category-item {
|
||
line-height: 1.5;
|
||
padding: 26rpx 28rpx;
|
||
box-sizing: border-box;
|
||
position: relative;
|
||
background-color: var(--diy-category-tree-normal-bg-color, #f5f5f5);
|
||
|
||
view {
|
||
color: var(--diy-category-tree-normal-text-color, #222222);
|
||
width: 100%;
|
||
white-space: break-word;
|
||
overflow: hidden;
|
||
text-align: center;
|
||
font-size: var(--diy-category-tree-normal-size, 20rpx);
|
||
}
|
||
|
||
&.border-top {
|
||
border-bottom-right-radius: 12rpx;
|
||
}
|
||
|
||
&.border-bottom {
|
||
border-top-right-radius: 12rpx;
|
||
}
|
||
|
||
&.select {
|
||
background: var(--diy-category-tree-select-bg-color, #fff);
|
||
|
||
view {
|
||
color: var(--diy-category-tree-select-text-color, #333);
|
||
font-weight: bold;
|
||
font-size: var(--diy-category-tree-select-size, 28rpx);
|
||
}
|
||
|
||
&::before {
|
||
content: ' ';
|
||
width: 8rpx;
|
||
height: 34rpx;
|
||
background: var(--base-color);
|
||
display: block;
|
||
position: absolute;
|
||
left: 0;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
}
|
||
}
|
||
}
|
||
|
||
.search-box {
|
||
position: relative;
|
||
padding: 20rpx 30rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
background: #fff;
|
||
|
||
.search-content {
|
||
position: relative;
|
||
/* #ifndef MP-WEIXIN */
|
||
height: 64rpx;
|
||
/* #endif */
|
||
/* #ifdef MP-WEIXIN */
|
||
height: 100%;
|
||
/* #endif */
|
||
border-radius: 40rpx;
|
||
flex: 1;
|
||
background-color: #f5f5f5;
|
||
|
||
input {
|
||
box-sizing: border-box;
|
||
display: block;
|
||
height: 100%;
|
||
width: 100%;
|
||
padding: 0 20rpx 0 40rpx;
|
||
background: #f5f5f5;
|
||
color: #333;
|
||
border-radius: 40rpx;
|
||
}
|
||
|
||
.iconfont {
|
||
position: absolute;
|
||
top: 50%;
|
||
right: 10rpx;
|
||
transform: translateY(-50%);
|
||
font-size: $font-size-toolbar;
|
||
z-index: 10;
|
||
color: #89899a;
|
||
width: 80rpx;
|
||
text-align: center;
|
||
}
|
||
}
|
||
|
||
// 切换列表图标
|
||
.iconfont {
|
||
font-size: 36rpx;
|
||
margin: 0 0rpx 0 20rpx;
|
||
}
|
||
}
|
||
|
||
.cart-box {
|
||
height: 100rpx;
|
||
width: 100%;
|
||
background: #fff;
|
||
border-top: 1px solid #f5f5f5;
|
||
box-sizing: border-box;
|
||
padding: 0 30rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
|
||
.left-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.cart-icon {
|
||
width: 70rpx;
|
||
height: 70rpx;
|
||
position: relative;
|
||
|
||
.iconfont {
|
||
color: var(--btn-text-color);
|
||
width: inherit;
|
||
height: inherit;
|
||
background-color: $base-color;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.num {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
transform: translate(60%, 0);
|
||
display: inline-block;
|
||
box-sizing: border-box;
|
||
color: #fff;
|
||
line-height: 1.2;
|
||
text-align: center;
|
||
font-size: 24rpx;
|
||
padding: 0 6rpx;
|
||
min-width: 30rpx;
|
||
border-radius: 16rpx;
|
||
background-color: var(--price-color);
|
||
border: 2rpx solid #fff;
|
||
}
|
||
}
|
||
|
||
.price {
|
||
margin-left: 30rpx;
|
||
|
||
.title {
|
||
color: #333;
|
||
}
|
||
|
||
.money {
|
||
font-weight: bold;
|
||
color: var(--price-color);
|
||
}
|
||
|
||
.unit {
|
||
font-weight: bold;
|
||
color: var(--price-color);
|
||
}
|
||
}
|
||
|
||
.settlement-btn {
|
||
margin: 0 0 0 20rpx;
|
||
width: 200rpx;
|
||
font-weight: bold;
|
||
border-radius: 50rpx;
|
||
height: 70rpx;
|
||
line-height: 70rpx;
|
||
}
|
||
}
|
||
|
||
.cart-point {
|
||
width: 26rpx;
|
||
height: 26rpx;
|
||
position: fixed;
|
||
z-index: 1000;
|
||
background: #f00;
|
||
border-radius: 50%;
|
||
transition: all 0.05s;
|
||
}
|
||
|
||
.category-empty {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-direction: column;
|
||
|
||
image {
|
||
width: 380rpx;
|
||
}
|
||
|
||
.tips {
|
||
font-size: 26rpx;
|
||
font-weight: 500;
|
||
color: #999;
|
||
margin-top: 50rpx;
|
||
}
|
||
}
|
||
|
||
.end-tips {
|
||
text-align: center;
|
||
color: #999;
|
||
font-size: 24rpx;
|
||
padding: 20rpx 0;
|
||
opacity: 0;
|
||
}
|
||
|
||
// 全屏骨架占位 — 不画 navbar/TabBar (由外层 category.vue 渲染), 避免错位
|
||
// 兼容鸿蒙元服务 (JSVM 对 gap 支持不一): 用 margin 兜底替代 gap
|
||
.category-page-skeleton {
|
||
flex: 1;
|
||
height: 0; // 与 .content-box 一致, 让 flex 子项撑开父容器剩余高度
|
||
display: flex;
|
||
background-color: #f4f6fa;
|
||
|
||
.skeleton-content {
|
||
flex: 1;
|
||
display: flex;
|
||
min-height: 0;
|
||
|
||
.skeleton-tree {
|
||
width: 170rpx;
|
||
background-color: #f5f5f5;
|
||
padding: 20rpx 0;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
flex-direction: column;
|
||
// #ifndef MP-HARMONY
|
||
gap: 18rpx;
|
||
// #endif
|
||
flex-shrink: 0;
|
||
|
||
.skeleton-tree-item {
|
||
height: 40rpx;
|
||
// #ifndef MP-HARMONY
|
||
margin: 0 28rpx;
|
||
// #endif
|
||
|
||
// #ifdef MP-HARMONY
|
||
margin: 9rpx 28rpx; // 18rpx 间距 上下各 9rpx, 替代 gap:18rpx
|
||
// #endif
|
||
|
||
border-radius: 12rpx;
|
||
background: linear-gradient(90deg, #ececec 25%, #f5f5f5 37%, #ececec 63%);
|
||
background-size: 400% 100%;
|
||
animation: category-skeleton-shimmer 1.4s ease infinite;
|
||
}
|
||
}
|
||
|
||
.skeleton-grid {
|
||
flex: 1;
|
||
padding: 24rpx;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-content: flex-start;
|
||
// #ifndef MP-HARMONY
|
||
gap: 16rpx;
|
||
// #endif
|
||
background-color: #f4f6fa;
|
||
min-width: 0;
|
||
|
||
.skeleton-card {
|
||
width: calc((100% - 32rpx) / 3);
|
||
height: 220rpx;
|
||
// #ifdef MP-HARMONY
|
||
margin: 8rpx;
|
||
// #endif
|
||
border-radius: 16rpx;
|
||
background: linear-gradient(90deg, #ececec 25%, #f5f5f5 37%, #ececec 63%);
|
||
background-size: 400% 100%;
|
||
animation: category-skeleton-shimmer 1.4s ease infinite;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
@keyframes category-skeleton-shimmer {
|
||
0% {
|
||
background-position: 100% 50%;
|
||
}
|
||
100% {
|
||
background-position: 0 50%;
|
||
}
|
||
}
|
||
|
||
// 风格四
|
||
.category-template-4 {
|
||
// 模板4 顶部搜索框(图片样式)
|
||
.search-box-template-4 {
|
||
padding: 20rpx 30rpx 16rpx;
|
||
background: #fff;
|
||
|
||
.search-content {
|
||
height: 64rpx;
|
||
background-color: #f5f5f5;
|
||
border-radius: 40rpx;
|
||
|
||
input {
|
||
height: 64rpx;
|
||
line-height: 64rpx;
|
||
padding: 0 30rpx 0 30rpx;
|
||
background: #f5f5f5;
|
||
border-radius: 40rpx;
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.iconfont {
|
||
font-size: 32rpx;
|
||
color: #89899a;
|
||
width: 70rpx;
|
||
text-align: center;
|
||
right: 10rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 模板4 底部购物车栏(图片样式)
|
||
.cart-box-template-4 {
|
||
height: 110rpx;
|
||
width: 100%;
|
||
flex-shrink: 0;
|
||
background: #fff;
|
||
border-top: 1rpx solid #f0f0f0;
|
||
position: relative;
|
||
z-index: 10;
|
||
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.04);
|
||
|
||
.left-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.cart-icon {
|
||
width: 90rpx;
|
||
height: 90rpx;
|
||
position: relative;
|
||
margin-top: -10rpx;
|
||
|
||
.iconfont {
|
||
color: var(--btn-text-color, #fff);
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
background-color: $base-color;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 40rpx;
|
||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.35);
|
||
}
|
||
|
||
.num {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
transform: translate(20%, 0);
|
||
display: inline-block;
|
||
box-sizing: border-box;
|
||
color: #fff;
|
||
line-height: 1.2;
|
||
text-align: center;
|
||
font-size: 24rpx;
|
||
padding: 0 8rpx;
|
||
min-width: 32rpx;
|
||
border-radius: 18rpx;
|
||
background-color: var(--price-color, #FF6B35);
|
||
border: 2rpx solid #fff;
|
||
}
|
||
}
|
||
|
||
.price {
|
||
margin-left: 16rpx;
|
||
display: flex;
|
||
align-items: baseline;
|
||
line-height: 1;
|
||
|
||
.title {
|
||
color: #333;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.unit,
|
||
.money {
|
||
color: var(--price-color, #FF6B35);
|
||
font-weight: bold;
|
||
}
|
||
|
||
.unit {
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.money {
|
||
font-size: 36rpx;
|
||
}
|
||
}
|
||
|
||
.right-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.settlement-btn {
|
||
margin: 0;
|
||
width: 220rpx;
|
||
font-weight: bold;
|
||
border-radius: 50rpx;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
font-size: 30rpx;
|
||
background: $base-color;
|
||
color: var(--btn-text-color, #fff);
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
}
|
||
}
|
||
|
||
.search-box {
|
||
.search-content input {
|
||
background-color: #f1f1f1;
|
||
}
|
||
}
|
||
|
||
::v-deep .template-four {
|
||
position: relative;
|
||
z-index: 1;
|
||
|
||
.template-four-wrap {
|
||
position: relative;
|
||
z-index: 1;
|
||
padding-left: 20rpx;
|
||
padding-right: 80rpx;
|
||
padding-bottom: 10rpx;
|
||
display: flex;
|
||
height: 172rpx;
|
||
align-items: baseline;
|
||
box-sizing: border-box;
|
||
box-shadow: 0 4rpx 4rpx rgba(123, 123, 123, 0.1);
|
||
}
|
||
|
||
.template-four-popup {
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
|
||
.title {
|
||
line-height: 1;
|
||
margin-bottom: 20rpx;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.template-four-scroll {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: baseline;
|
||
align-content: baseline;
|
||
padding: 20rpx;
|
||
white-space: nowrap;
|
||
height: 380rpx;
|
||
box-sizing: border-box;
|
||
|
||
.uni-scroll-view-content {
|
||
flex-wrap: wrap;
|
||
align-items: baseline;
|
||
align-content: baseline;
|
||
}
|
||
|
||
.item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 4rpx 0;
|
||
color: #666;
|
||
margin-right: 16rpx;
|
||
border-radius: 40rpx;
|
||
margin-bottom: 10rpx;
|
||
width: calc((100% - 64rpx) / 5);
|
||
|
||
&:nth-child(5n + 5) {
|
||
margin-right: 0;
|
||
}
|
||
|
||
.image-warp {
|
||
margin-bottom: 6rpx;
|
||
padding: 4rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 42rpx;
|
||
border: 4rpx solid transparent;
|
||
}
|
||
|
||
image {
|
||
width: 84rpx;
|
||
height: 84rpx;
|
||
border-radius: 32rpx;
|
||
}
|
||
|
||
.text {
|
||
padding: 2rpx 16rpx;
|
||
border-radius: 40rpx;
|
||
font-size: $font-size-tag;
|
||
}
|
||
|
||
&.selected {
|
||
.text {
|
||
background-color: $base-color;
|
||
color: var(--btn-text-color);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.pack-up {
|
||
font-size: $font-size-tag;
|
||
color: #888888;
|
||
height: 74rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-top: 2rpx solid #f2f2f2;
|
||
|
||
.iconfont {
|
||
font-size: 40rpx;
|
||
margin-left: -4rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.category-item-all {
|
||
position: absolute;
|
||
bottom: 0;
|
||
z-index: 1;
|
||
right: 0;
|
||
top: 0;
|
||
width: 72rpx;
|
||
line-height: 1;
|
||
background-color: #fff;
|
||
|
||
.category-item-all-wrap {
|
||
position: absolute;
|
||
bottom: 0;
|
||
right: 0;
|
||
top: 0;
|
||
left: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 2;
|
||
}
|
||
|
||
.text {
|
||
writing-mode: tb-rl;
|
||
margin-bottom: 6rpx;
|
||
letter-spacing: 4rpx;
|
||
font-size: $font-size-tag;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.img {
|
||
width: 20rpx;
|
||
height: 20rpx;
|
||
}
|
||
|
||
&::after {
|
||
content: '';
|
||
// box-shadow: -4rpx 10rpx 20rpx rgba(0, 0, 0, 0.1);
|
||
position: absolute;
|
||
left: 0;
|
||
width: 10rpx;
|
||
// height: 100rpx;
|
||
top: 20%;
|
||
bottom: 20%;
|
||
// transform: translateY(-50%);
|
||
// background-color: #F0F5FF;
|
||
}
|
||
}
|
||
|
||
.uni-scroll-view-content {
|
||
display: flex;
|
||
}
|
||
|
||
.category-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 130rpx;
|
||
flex-shrink: 0;
|
||
margin-right: 20rpx;
|
||
padding: 4rpx 0;
|
||
|
||
&:last-of-type {
|
||
margin-right: 0;
|
||
}
|
||
|
||
.image-warp {
|
||
margin-bottom: 6rpx;
|
||
padding: 4rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 42rpx;
|
||
border: 4rpx solid transparent;
|
||
}
|
||
|
||
image {
|
||
width: 84rpx;
|
||
height: 84rpx;
|
||
border-radius: 32rpx;
|
||
}
|
||
|
||
.text {
|
||
font-size: $font-size-tag;
|
||
}
|
||
}
|
||
|
||
.select {
|
||
.text {
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 40rpx;
|
||
color: #fff;
|
||
font-size: $font-size-tag;
|
||
line-height: 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 风格2 复用模板4 的底部购物车栏样式
|
||
.category-template-2 {
|
||
// 模板4 底部购物车栏(图片样式)
|
||
.cart-box-template-4 {
|
||
height: 110rpx;
|
||
width: 100%;
|
||
flex-shrink: 0;
|
||
background: #fff;
|
||
border-top: 1rpx solid #f0f0f0;
|
||
position: relative;
|
||
z-index: 10;
|
||
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.04);
|
||
|
||
.left-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.cart-icon {
|
||
width: 90rpx;
|
||
height: 90rpx;
|
||
position: relative;
|
||
margin-top: -10rpx;
|
||
|
||
.iconfont {
|
||
color: var(--btn-text-color, #fff);
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
background-color: $base-color;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 40rpx;
|
||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.35);
|
||
}
|
||
|
||
.num {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
transform: translate(20%, 0);
|
||
display: inline-block;
|
||
box-sizing: border-box;
|
||
color: #fff;
|
||
line-height: 1.2;
|
||
text-align: center;
|
||
font-size: 24rpx;
|
||
padding: 0 8rpx;
|
||
min-width: 32rpx;
|
||
border-radius: 18rpx;
|
||
background-color: var(--price-color, #FF6B35);
|
||
border: 2rpx solid #fff;
|
||
}
|
||
}
|
||
|
||
.price {
|
||
margin-left: 16rpx;
|
||
display: flex;
|
||
align-items: baseline;
|
||
line-height: 1;
|
||
|
||
.title {
|
||
color: #333;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.unit,
|
||
.money {
|
||
color: var(--price-color, #FF6B35);
|
||
font-weight: bold;
|
||
}
|
||
|
||
.unit {
|
||
font-size: 24rpx;
|
||
}
|
||
|
||
.money {
|
||
font-size: 36rpx;
|
||
}
|
||
}
|
||
|
||
.right-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.settlement-btn {
|
||
margin: 0;
|
||
width: 220rpx;
|
||
font-weight: bold;
|
||
border-radius: 50rpx;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
font-size: 30rpx;
|
||
background: $base-color;
|
||
color: var(--btn-text-color, #fff);
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.content-wrap .categoty-goods-wrap .goods-list {
|
||
margin-top: 30rpx;
|
||
}
|
||
|
||
.tree-wrap .category-item.select::before {
|
||
border-top-right-radius: 8rpx;
|
||
border-bottom-right-radius: 8rpx;
|
||
}
|
||
}
|
||
|
||
// ===================== 模板1 仿京东/淘宝商品分类样式 =====================
|
||
.category-template-1 {
|
||
.search-box {
|
||
padding: 20rpx 24rpx;
|
||
background: #fff;
|
||
|
||
.search-content {
|
||
background-color: #f5f5f5;
|
||
border-radius: 40rpx;
|
||
|
||
input {
|
||
background: #f5f5f5;
|
||
border-radius: 40rpx;
|
||
padding: 0 30rpx;
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.iconfont {
|
||
right: 16rpx;
|
||
color: #89899a;
|
||
font-size: 32rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.content-box {
|
||
background: #fff;
|
||
|
||
.tree-wrap {
|
||
width: 170rpx;
|
||
background-color: #fff;
|
||
}
|
||
|
||
.right-flex-wrap {
|
||
background: #fff;
|
||
|
||
.content-wrap {
|
||
background: #fff;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 穿透到子组件 diy-category-item: 调整 banner 横向内边距 + 分类网格容器为透明
|
||
::v-deep .category-adv {
|
||
padding: 0 20rpx;
|
||
margin-top: 0;
|
||
}
|
||
|
||
::v-deep .category-list {
|
||
margin: 0 20rpx;
|
||
border-radius: 0;
|
||
background: transparent;
|
||
padding: 0;
|
||
}
|
||
|
||
::v-deep .category-title-with-lines {
|
||
padding: 30rpx 0 20rpx;
|
||
}
|
||
|
||
// 穿透修复: 图片不需要灰色背景框,与目标图保持一致
|
||
::v-deep .img-box {
|
||
background: transparent !important;
|
||
border-radius: 0 !important;
|
||
width: 70% !important;
|
||
padding-bottom: 70% !important;
|
||
margin-bottom: 16rpx !important;
|
||
}
|
||
|
||
::v-deep .img-box image {
|
||
width: 100% !important;
|
||
height: 100% !important;
|
||
left: 0 !important;
|
||
top: 0 !important;
|
||
transform: none !important;
|
||
}
|
||
|
||
// 左侧分类栏样式
|
||
.tree-wrap .category-item {
|
||
padding: 28rpx 16rpx;
|
||
background-color: var(--diy-category-tree-normal-bg-color, #f5f5f5);
|
||
border-radius: 0;
|
||
|
||
view {
|
||
color: var(--diy-category-tree-normal-text-color, #222222);
|
||
font-size: 26rpx;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
&.border-top,
|
||
&.border-bottom {
|
||
border-radius: 0;
|
||
}
|
||
|
||
&.select {
|
||
background: var(--diy-category-tree-select-bg-color, #fff);
|
||
|
||
view {
|
||
color: var(--diy-category-tree-select-text-color, #333);
|
||
font-weight: bold;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
&::before {
|
||
content: ' ';
|
||
width: 6rpx;
|
||
height: 32rpx;
|
||
background: var(--base-color, #FF6B35);
|
||
display: block;
|
||
position: absolute;
|
||
left: 0;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
border-radius: 0 6rpx 6rpx 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ===================== 模板99 商品分类样式 =====================
|
||
.category-template-99 {
|
||
.search-box {
|
||
padding: 20rpx 24rpx;
|
||
background: #fff;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
|
||
.search-content {
|
||
height: 64rpx;
|
||
background-color: #f5f5f5;
|
||
border-radius: 40rpx;
|
||
|
||
input {
|
||
height: 64rpx;
|
||
line-height: 64rpx;
|
||
padding: 0 80rpx 0 30rpx;
|
||
background: #f5f5f5;
|
||
border-radius: 40rpx;
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
}
|
||
|
||
.iconfont {
|
||
font-size: 32rpx;
|
||
color: #89899a;
|
||
width: 70rpx;
|
||
text-align: center;
|
||
}
|
||
}
|
||
|
||
> .iconfont {
|
||
font-size: 40rpx;
|
||
color: #666;
|
||
margin-left: 20rpx;
|
||
}
|
||
}
|
||
|
||
.content-box {
|
||
.tree-wrap {
|
||
width: 200rpx;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.right-flex-wrap {
|
||
flex: 1;
|
||
width: 0;
|
||
background: #f5f5f5;
|
||
}
|
||
}
|
||
|
||
.tree-wrap .category-item {
|
||
padding: 30rpx 16rpx;
|
||
background-color: var(--diy-category-tree-normal-bg-color, #f5f5f5);
|
||
white-space: normal;
|
||
word-break: break-all;
|
||
|
||
view {
|
||
color: var(--diy-category-tree-normal-text-color, #222222);
|
||
font-size: 26rpx;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
&.select {
|
||
background: var(--diy-category-tree-select-bg-color, #fff);
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
|
||
view {
|
||
color: var(--diy-category-tree-select-text-color, #333);
|
||
font-weight: bold;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
&::before {
|
||
content: ' ';
|
||
width: 6rpx;
|
||
height: 36rpx;
|
||
background: #FF4646;
|
||
display: block;
|
||
position: absolute;
|
||
left: 0;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
border-radius: 0 6rpx 6rpx 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 模板99: 第一个分类项固定高度,与右侧二级分类按钮区域对齐
|
||
.tree-wrap .category-item.first-category-item {
|
||
height: 100rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
|
||
view {
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
}
|
||
|
||
.content-wrap {
|
||
background: #f5f5f5;
|
||
padding: 0;
|
||
// #ifdef H5
|
||
::-webkit-scrollbar {
|
||
display: none;
|
||
width: 0;
|
||
height: 0;
|
||
background: transparent;
|
||
}
|
||
// #endif
|
||
}
|
||
|
||
.child-category {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
}
|
||
</style> |