Merge branch 'dev' into 'master'

uniapp【修复】: 修复页面下拉刷新时数据重复问题/修复app中保存图片不成功

See merge request jianweie/coreshoppro!77
This commit is contained in:
花城
2024-11-11 08:51:55 +00:00
11 changed files with 930 additions and 907 deletions

View File

@@ -1,64 +1,64 @@
<template>
<view :class="props.type == 'horizontal' ? 'horizontal-good-card-box' : 'vertical-good-card-box'">
<view class="img-box" :style="{ 'width': props.imgWidth, 'height': props.imgHeight }" @click="hanldeClickGoods">
<image class="good-img" :src="props.goodsData.image"></image>
<view class="recommend" v-if="props.goodsData.isRecommend">推荐</view>
<view class="hot" v-if="props.goodsData.isHot">热门</view>
</view>
<view class="good-msg"
:style="{ 'width': props.type == 'horizontal' ? `calc(100% - 10px - ${props.imgWidth})` : props.imgWidth}">
<view v-if="props.goodsData.name" :class="props.nameOverflow === 1 ? 'name' : 'name-two'"
@click="hanldeClickGoods">
{{ props.goodsData.name }}
</view>
<view v-if="props.goodsData.brief" :class="props.briefOverflow === 1 ? 'desc' : 'desc-two'"
@click="hanldeClickGoods">
{{ props.goodsData.brief }}
</view>
<view :class="props.type == 'horizontal' ? 'horizontal-good-card-box' : 'vertical-good-card-box'">
<view class="img-box" :style="{ 'width': props.imgWidth, 'height': props.imgHeight }" @click="hanldeClickGoods">
<image class="good-img" :src="props.goodsData.image"></image>
<view class="recommend" v-if="props.goodsData.isRecommend">推荐</view>
<view class="hot" v-if="props.goodsData.isHot">热门</view>
</view>
<view class="good-msg"
:style="{ 'width': props.type == 'horizontal' ? `calc(100% - 10px - ${props.imgWidth})` : props.imgWidth}">
<view v-if="props.goodsData.name" :class="props.nameOverflow === 1 ? 'name' : 'name-two'"
@click="hanldeClickGoods">
{{ props.goodsData.name }}
</view>
<view v-if="props.goodsData.brief" :class="props.briefOverflow === 1 ? 'desc' : 'desc-two'"
@click="hanldeClickGoods">
{{ props.goodsData.brief }}
</view>
<slot name="goodTag"></slot>
<slot name="goodTag"></slot>
<slot name="goodPrice">
<view class="price-msg">
<view class="price-box">
<view class="price">
<text class="symbol"></text>
<text class="num">{{ props.goodsData.price }}</text>
</view>
<view class="underlin-price">{{ props.goodsData.mktprice }}</view>
</view>
<view class="btn">
立即购买
</view>
</view>
</slot>
</view>
</view>
<slot name="other"></slot>
<slot name="goodPrice">
<view class="price-msg">
<view class="price-box">
<view class="price">
<text class="symbol"></text>
<text class="num">{{ props.goodsData.price }}</text>
</view>
<view class="underlin-price">{{ props.goodsData.mktprice }}</view>
</view>
<view class="btn">
立即购买
</view>
</view>
</slot>
</view>
</view>
<slot name="other"></slot>
</template>
<script setup lang="ts">
const props = withDefaults(defineProps<{
type : 'vertical' | 'horizontal',
imgWidth : string,
imgHeight : string,
nameOverflow : number,
briefOverflow : number,
goodsData : any,
}>(), {
type: 'horizontal', // vertical 垂直 horizontal 水平
imgWidth: '190rpx',
imgHeight: '190rpx',
nameOverflow: 1,
briefOverflow: 1,
goodsData: {},
});
const props = withDefaults(defineProps<{
type ?: 'vertical' | 'horizontal',
imgWidth ?: string,
imgHeight ?: string,
nameOverflow ?: number,
briefOverflow ?: number,
goodsData ?: any,
}>(), {
type: 'horizontal', // vertical 垂直 horizontal 水平
imgWidth: '190rpx',
imgHeight: '190rpx',
nameOverflow: 1,
briefOverflow: 1,
goodsData: {},
});
const emit = defineEmits(['hanldeClickGoods']);
const emit = defineEmits(['hanldeClickGoods']);
const hanldeClickGoods = () => {
emit('hanldeClickGoods', props.goodsData)
}
const hanldeClickGoods = () => {
emit('hanldeClickGoods', props.goodsData)
}
</script>
<style lang="scss" scoped>
@import './coreshop-goods-card.scss';
@import './coreshop-goods-card.scss';
</style>

View File

@@ -1,39 +1,39 @@
<template>
<view class="more-box" @click="hanldeClickViewMore">
<text class="more-text" :style="{ 'color': props.color }">{{ props.title }}</text>
<view class="more-icon">
<uv-icon name="arrow-right" :color="props.color" :size="15"></uv-icon>
</view>
</view>
<view class="more-box" @click="hanldeClickViewMore">
<text class="more-text" :style="{ 'color': props.color }">{{ props.title }}</text>
<view class="more-icon">
<uv-icon name="arrow-right" :color="props.color" :size="15"></uv-icon>
</view>
</view>
</template>
<script setup lang="ts">
const props = withDefaults(defineProps<{
title : string,
color : string,
}>(), {
title: '查看更多',
color: '#6E737D'
});
const props = withDefaults(defineProps<{
title ?: string,
color ?: string,
}>(), {
title: '查看更多',
color: '#6E737D'
});
const emit = defineEmits(['hanldeClickViewMore']);
const emit = defineEmits(['hanldeClickViewMore']);
const hanldeClickViewMore = () => {
emit('hanldeClickViewMore');
}
const hanldeClickViewMore = () => {
emit('hanldeClickViewMore');
}
</script>
<style lang="scss" scoped>
.more-box {
display: flex;
align-items: center;
.more-box {
display: flex;
align-items: center;
.more-text {
font-size: 24rpx;
}
.more-text {
font-size: 24rpx;
}
.more-icon {
width: 30rpx;
height: 30rpx;
margin-top: 2rpx;
}
}
.more-icon {
width: 30rpx;
height: 30rpx;
margin-top: 2rpx;
}
}
</style>

View File

@@ -3,3 +3,6 @@ export const onHomePageShow = "onHomePageShow";
/** 监听分类onHide需要的常量字段 */
export const onClassifyPageHide = "onClassifyPageHide";
/** 监听上拉刷新onPullDownRefresh要的常量字段 */
export const onClassifyPagePullDownRefresh = "onClassifyPagePullDownRefresh";

View File

@@ -240,6 +240,7 @@
onPullDownRefresh(async () => {
state.page = 1;
state.totalPages = 1;
state.goodsList = [[], []];
await handleuQueryProduct(queryParams);
uni.stopPullDownRefresh();
});

View File

@@ -1,410 +1,416 @@
<template>
<view class="layout-classify" :style="{ height: `${props.height}px` }">
<!-- 头部大分类 -->
<view class="big-classify">
<view class="scroll-view-box">
<scroll-view class="scroll-view" enable-flex :scroll-x="true" :scroll-left="state.topScrollLeft"
:scroll-with-animation="true">
<view :class="['item', { 'on': state.topTabId === item.id }]"
v-for="item, index in state.classifyData" :key="index" @click="hanldeChangeTopTab(item, index)">
<view class="img-box">
<image class="img" :src="item.imageUrl"></image>
</view>
<view>
<text class="tit">{{ item.name }}</text>
</view>
</view>
</scroll-view>
</view>
<view class="all-big-classify" @click="hanldeShowBigClassifyPop">
<text class="tit">全部</text>
<image class="img" :src="handleStaticResources('/static/images/icon/all-drop-down.png')"></image>
</view>
</view>
<view class="layout-classify" :style="{ height: `${props.height}px` }">
<!-- 头部大分类 -->
<view class="big-classify">
<view class="scroll-view-box">
<scroll-view class="scroll-view" enable-flex :scroll-x="true" :scroll-left="state.topScrollLeft"
:scroll-with-animation="true">
<view :class="['item', { 'on': state.topTabId === item.id }]"
v-for="item, index in state.classifyData" :key="index" @click="hanldeChangeTopTab(item, index)">
<view class="img-box">
<image class="img" :src="item.imageUrl"></image>
</view>
<view>
<text class="tit">{{ item.name }}</text>
</view>
</view>
</scroll-view>
</view>
<view class="all-big-classify" @click="hanldeShowBigClassifyPop">
<text class="tit">全部</text>
<image class="img" :src="handleStaticResources('/static/images/icon/all-drop-down.png')"></image>
</view>
</view>
<view class="classify-box" :style="{ height: `${props.height - state.bigClassifyH}px` }">
<view class="classify-left">
<scroll-view class="scroll-view" enable-flex :scroll-y="true">
<view :class="['item', { 'on': state.leftTabId === item.id }]"
v-for="item, index in state.leftTabList" :key="index" @click="hanldeChangeLeftTab(item)">
{{ item.name }}
</view>
<view class="no-more"></view>
</scroll-view>
</view>
<view class="classify-right">
<view class="right-tab-box">
<view>
<coreshop-tabs :list="state.rightTabList"
@hanldeClickTab="hanldeChangeRightTab"></coreshop-tabs>
</view>
</view>
<scroll-view class="scroll-view" enable-flex
:style="{ 'height': `${props.height - state.bigClassifyH - state.rightTabH}px` }" :scroll-y="true"
@scrolltolower="handleScrolltolower">
<view class="advert-box radius-15">
<coreshop-advert :code="advertPosition.goodsClassifyBanner"></coreshop-advert>
</view>
<view class="data-box" v-if="state.goodsList.length > 0">
<view class="item-box" v-for="item in state.goodsList" :key="item.id">
<coreshop-goods-card imgWidth="150rpx" imgHeight="150rpx"
:goodsData="hanldeCombinationGoodsData(item)" @hanldeClickGoods="hanldeClickGoods">
<template #goodPrice>
<view class="price-msg">
<view class="price-box">
<view class="price">
<text class="symbol"></text>
<text class="num">{{ item.price }}</text>
</view>
<view class="underlin-price">{{ item.mktprice }}</view>
</view>
<view class="btn" @click="handleSelectSku(item.id)">
<image class="img" :src="handleStaticResources('/static/images/cart.png')">
</image>
</view>
</view>
</template>
</coreshop-goods-card>
</view>
<view class="no-more">
<uv-divider :dashed="true"
:text="state.totalPages > state.page ? '下滑加载更多' : '没有更多了'"></uv-divider>
</view>
</view>
<view v-else>
<coreshop-empty></coreshop-empty>
</view>
</scroll-view>
</view>
</view>
<view class="classify-box" :style="{ height: `${props.height - state.bigClassifyH}px` }">
<view class="classify-left">
<scroll-view class="scroll-view" enable-flex :scroll-y="true">
<view :class="['item', { 'on': state.leftTabId === item.id }]"
v-for="item, index in state.leftTabList" :key="index" @click="hanldeChangeLeftTab(item)">
{{ item.name }}
</view>
<view class="no-more"></view>
</scroll-view>
</view>
<view class="classify-right">
<view class="right-tab-box">
<view>
<coreshop-tabs :list="state.rightTabList"
@hanldeClickTab="hanldeChangeRightTab"></coreshop-tabs>
</view>
</view>
<scroll-view class="scroll-view" enable-flex
:style="{ 'height': `${props.height - state.bigClassifyH - state.rightTabH}px` }" :scroll-y="true"
@scrolltolower="handleScrolltolower">
<view class="advert-box radius-15">
<coreshop-advert :code="advertPosition.goodsClassifyBanner"></coreshop-advert>
</view>
<view class="data-box" v-if="state.goodsList.length > 0">
<view class="item-box" v-for="item in state.goodsList" :key="item.id">
<coreshop-goods-card imgWidth="150rpx" imgHeight="150rpx"
:goodsData="hanldeCombinationGoodsData(item)" @hanldeClickGoods="hanldeClickGoods">
<template #goodPrice>
<view class="price-msg">
<view class="price-box">
<view class="price">
<text class="symbol"></text>
<text class="num">{{ item.price }}</text>
</view>
<view class="underlin-price">{{ item.mktprice }}</view>
</view>
<view class="btn" @click="handleSelectSku(item.id)">
<image class="img" :src="handleStaticResources('/static/images/cart.png')">
</image>
</view>
</view>
</template>
</coreshop-goods-card>
</view>
<view class="no-more">
<uv-divider :dashed="true"
:text="state.totalPages > state.page ? '下滑加载更多' : '没有更多了'"></uv-divider>
</view>
</view>
<view v-else>
<coreshop-empty></coreshop-empty>
</view>
</scroll-view>
</view>
</view>
<!-- 购物车盒子 -->
<view class="shopping-box">
<view class="shopping-Bag">
<view class="bag-box">
<image class="img" :src="handleStaticResources('/static/images/cart-bag.png')"></image>
</view>
<view class="price-box">
<text class="num">购物车数量:{{ state.cartCount }}</text>
<text class="price">{{ state.cartMoney }}</text>
</view>
</view>
<view class="btn-buy" @click="handleGoPay">
去结算
</view>
</view>
<!-- 购物车盒子 -->
<view class="shopping-box">
<view class="shopping-Bag">
<view class="bag-box">
<image class="img" :src="handleStaticResources('/static/images/cart-bag.png')"></image>
</view>
<view class="price-box">
<text class="num">购物车数量:{{ state.cartCount }}</text>
<text class="price">{{ state.cartMoney }}</text>
</view>
</view>
<view class="btn-buy" @click="handleGoPay">
去结算
</view>
</view>
<!-- 商品sku弹框 -->
<GoodsDetailSkuPopup :showSku="state.showSku" :goodsDetailData="state.goodsDetailData"
:safeAreaInsetBottom="false" :buyNowNowloading="buyNowLoading" :addCartloading="addCartLoading"
@handleChangePopup="handleChangePopup" @handleAddCart="handleAddCart" @handleBuyNow="handleBuyNow">
</GoodsDetailSkuPopup>
<!-- 商品sku弹框 -->
<GoodsDetailSkuPopup :showSku="state.showSku" :goodsDetailData="state.goodsDetailData"
:safeAreaInsetBottom="false" :buyNowNowloading="buyNowLoading" :addCartloading="addCartLoading"
@handleChangePopup="handleChangePopup" @handleAddCart="handleAddCart" @handleBuyNow="handleBuyNow">
</GoodsDetailSkuPopup>
<!-- 头部大分类的弹框 -->
<uv-popup ref="bigClassifyPop" mode="top" :safeAreaInsetBottom="false">
<view class="big-classify-pop" :style="{ 'padding-top': `${props.statusBarHeight}px` }">
<view class="title">全部分类</view>
<view class="item-box">
<view :class="['item', { 'on': state.topTabId === item.id }]"
v-for="item, index in state.classifyData" :key="index" @click="hanldeChangeTopTab(item, index)">
<view class="img-box">
<image class="img" :src="item.imageUrl"></image>
</view>
<view>
<text class="tit">{{ item.name }}</text>
</view>
</view>
</view>
<view class="put-away" @click="hanlderHidebigClassifyPop">点击收起<uv-icon name="arrow-up" size="15px"
color="6E737D"></uv-icon></view>
</view>
</uv-popup>
</view>
<!-- 头部大分类的弹框 -->
<uv-popup ref="bigClassifyPop" mode="top" :safeAreaInsetBottom="false">
<view class="big-classify-pop" :style="{ 'padding-top': `${props.statusBarHeight}px` }">
<view class="title">全部分类</view>
<view class="item-box">
<view :class="['item', { 'on': state.topTabId === item.id }]"
v-for="item, index in state.classifyData" :key="index" @click="hanldeChangeTopTab(item, index)">
<view class="img-box">
<image class="img" :src="item.imageUrl"></image>
</view>
<view>
<text class="tit">{{ item.name }}</text>
</view>
</view>
</view>
<view class="put-away" @click="hanlderHidebigClassifyPop">点击收起<uv-icon name="arrow-up" size="15px"
color="6E737D"></uv-icon></view>
</view>
</uv-popup>
</view>
</template>
<script setup lang="ts">
import { reactive, watch, ref, onMounted, getCurrentInstance, nextTick } from 'vue';
import { advertPosition, UserToken, onClassifyPageHide } from '@/core/consts';
import { useLoginStore } from '@/core/store';
import { queryGoodsPageList, queryCartNumAndMoney, queryGoodsDetailByToken, queryGoodsDetail, queryAddCart } from '@/core/api';
import type { CategoriesType, Response, GoodsListType, GoodsType } from '@/core/models';
import { getDomInfo, handleStaticResources, handleRouteNavigateTo, handleShowToast, handleRouteSwitchTab } from '@/core/utils';
import { AddCartEnum, PaymentTypeEnum, RouteSwitchTabEnum } from '@/core/enum';
import GoodsDetailSkuPopup from '@/pages/components/goods-detail/components/goods-detail-sku/goods-detail-sku.vue';
import { useLoadingFn } from '@/core/hooks';
import { reactive, watch, ref, onMounted, getCurrentInstance, nextTick } from 'vue';
import { advertPosition, UserToken, onClassifyPageHide, onClassifyPagePullDownRefresh } from '@/core/consts';
import { useLoginStore } from '@/core/store';
import { queryGoodsPageList, queryCartNumAndMoney, queryGoodsDetailByToken, queryGoodsDetail, queryAddCart } from '@/core/api';
import type { CategoriesType, Response, GoodsListType, GoodsType } from '@/core/models';
import { getDomInfo, handleStaticResources, handleRouteNavigateTo, handleShowToast, handleRouteSwitchTab } from '@/core/utils';
import { AddCartEnum, PaymentTypeEnum, RouteSwitchTabEnum } from '@/core/enum';
import GoodsDetailSkuPopup from '@/pages/components/goods-detail/components/goods-detail-sku/goods-detail-sku.vue';
import { useLoadingFn } from '@/core/hooks';
const instance = getCurrentInstance();
const instance = getCurrentInstance();
/** 登录store */
const _useLoginStore = useLoginStore();
/** 登录store */
const _useLoginStore = useLoginStore();
const props = withDefaults(defineProps<{
data : Array<CategoriesType>,
height : number,
statusBarHeight : number,
}>(), {
data: () => [],
height: 0,
statusBarHeight: 0,
});
const props = withDefaults(defineProps<{
data : Array<CategoriesType>,
height : number,
statusBarHeight : number,
}>(), {
data: () => [],
height: 0,
statusBarHeight: 0,
});
const bigClassifyPop = ref();
const bigClassifyPop = ref();
const state = reactive<{
classifyData : Array<CategoriesType>;
topTabId : number;
topScrollLeft : number;
leftTabId : number;
leftTabList : Array<CategoriesType>;
rightTabList : Array<CategoriesType>;
catId : number;
goodsList : Array<GoodsType>;
page : number;
totalPages : number;
bigClassifyH : number; // 大分类盒子的高度
rightTabH : number; // 右侧tab盒子的高度
cartCount : number;
cartMoney : number,
showSku : boolean;
goodsDetailData : any;
}>({
classifyData: [],
topTabId: 0,
topScrollLeft: 0,
leftTabId: 0,
leftTabList: [],
rightTabList: [],
catId: 0,
goodsList: [],
page: 1,
totalPages: 0,
bigClassifyH: 0,
rightTabH: 0,
cartCount: 0,
cartMoney: 0,
showSku: false,
goodsDetailData: {},
});
const state = reactive<{
classifyData : Array<CategoriesType>;
topTabId : number;
topScrollLeft : number;
leftTabId : number;
leftTabList : Array<CategoriesType>;
rightTabList : Array<CategoriesType>;
catId : number;
goodsList : Array<GoodsType>;
page : number;
totalPages : number;
bigClassifyH : number; // 大分类盒子的高度
rightTabH : number; // 右侧tab盒子的高度
cartCount : number;
cartMoney : number,
showSku : boolean;
goodsDetailData : any;
}>({
classifyData: [],
topTabId: 0,
topScrollLeft: 0,
leftTabId: 0,
leftTabList: [],
rightTabList: [],
catId: 0,
goodsList: [],
page: 1,
totalPages: 0,
bigClassifyH: 0,
rightTabH: 0,
cartCount: 0,
cartMoney: 0,
showSku: false,
goodsDetailData: {},
});
const buyNowLoading = ref(false);
const addCartLoading = ref(false);
const buyNowLoading = ref(false);
const addCartLoading = ref(false);
const handleBuyNow = useLoadingFn(onBuyNow, buyNowLoading);
const handleAddCart = useLoadingFn(onAddCart, addCartLoading)
const handleBuyNow = useLoadingFn(onBuyNow, buyNowLoading);
const handleAddCart = useLoadingFn(onAddCart, addCartLoading)
watch(() => props.data, (newVal : Array<CategoriesType>) => {
if (newVal) {
state.classifyData = newVal.map((item : CategoriesType) => {
item.child.forEach((cell : CategoriesType) => {
cell.child.unshift({
name: "全部",
id: cell.id,
})
})
return item;
});
watch(() => props.data, (newVal : Array<CategoriesType>) => {
if (newVal) {
state.classifyData = newVal.map((item : CategoriesType) => {
item.child.forEach((cell : CategoriesType) => {
cell.child.unshift({
name: "全部",
id: cell.id,
})
})
return item;
});
state.topTabId = state.classifyData[0]?.id;
state.leftTabId = state.classifyData[0]?.child[0]?.id;
state.leftTabList = state.classifyData[0]?.child;
state.rightTabList = state.classifyData[0]?.child[0]?.child;
state.topTabId = state.classifyData[0]?.id;
state.leftTabId = state.classifyData[0]?.child[0]?.id;
state.leftTabList = state.classifyData[0]?.child;
state.rightTabList = state.classifyData[0]?.child[0]?.child;
state.catId = state.classifyData[0].child[0]?.id;
getGoodsPageList();
}
})
state.catId = state.classifyData[0]?.child[0]?.id;
getGoodsPageList();
}
})
onMounted(() => {
if (uni.getStorageSync(UserToken)) {
getCartNumAndMoney();
}
onMounted(() => {
if (uni.getStorageSync(UserToken)) {
getCartNumAndMoney();
}
uni.$on(onClassifyPageHide, () => {
state.showSku = false;
})
uni.$on(onClassifyPagePullDownRefresh, () => {
state.page = 1;
state.totalPages = 1;
state.goodsList = [];
})
nextTick(async () => {
state.bigClassifyH = ((await getDomInfo('.big-classify', instance.proxy)) as { height : number }).height;
setTimeout(async () => {
state.rightTabH = ((await getDomInfo('.right-tab-box', instance.proxy)) as { height : number }).height;
}, 100)
})
})
uni.$on(onClassifyPageHide, () => {
state.showSku = false;
})
// 获取商品列表数据
const getGoodsPageList = async () => {
nextTick(async () => {
state.bigClassifyH = ((await getDomInfo('.big-classify', instance.proxy)) as { height : number }).height;
setTimeout(async () => {
state.rightTabH = ((await getDomInfo('.right-tab-box', instance.proxy)) as { height : number }).height;
}, 100)
})
})
const goodsPageList : Response<GoodsListType> = await queryGoodsPageList({
page: state.page,
limit: 10,
where: `{"catId":${state.catId}}`
});
if (goodsPageList.status) {
state.totalPages = goodsPageList.data?.totalPages;
state.goodsList = state.goodsList.concat(goodsPageList.data?.list);
}
}
// 获取商品列表数据
const getGoodsPageList = async () => {
if (!state.catId) { return; }
const goodsPageList : Response<GoodsListType> = await queryGoodsPageList({
page: state.page,
limit: 10,
where: `{"catId":${state.catId}}`
});
if (goodsPageList.status) {
state.totalPages = goodsPageList.data?.totalPages;
state.goodsList = state.goodsList.concat(goodsPageList.data?.list);
}
}
/** 获取购物车数量和价格 */
const getCartNumAndMoney = async () => {
const cartNumAndMoney : Response<{ count : number, money : number }> = await queryCartNumAndMoney();
state.cartCount = cartNumAndMoney?.data?.count || 0;
state.cartMoney = cartNumAndMoney?.data?.money || 0;
}
/** 获取购物车数量和价格 */
const getCartNumAndMoney = async () => {
const cartNumAndMoney : Response<{ count : number, money : number }> = await queryCartNumAndMoney();
state.cartCount = cartNumAndMoney?.data?.count || 0;
state.cartMoney = cartNumAndMoney?.data?.money || 0;
}
/** 选择sku */
const handleSelectSku = (id : number) => {
_useLoginStore.checkLogin(async () => {
let goodsDetail : any = null;
if (uni.getStorageSync(UserToken)) {
goodsDetail = await queryGoodsDetailByToken({
id: id,
data: true,
})
} else {
goodsDetail = await queryGoodsDetail({
id: id,
data: true,
})
}
state.goodsDetailData = goodsDetail.data;
state.showSku = true;
});
}
/** 选择sku */
const handleSelectSku = (id : number) => {
_useLoginStore.checkLogin(async () => {
let goodsDetail : any = null;
if (uni.getStorageSync(UserToken)) {
goodsDetail = await queryGoodsDetailByToken({
id: id,
data: true,
})
} else {
goodsDetail = await queryGoodsDetail({
id: id,
data: true,
})
}
state.goodsDetailData = goodsDetail.data;
state.showSku = true;
});
}
/** 底部按钮去结算 */
const handleGoPay = () => {
_useLoginStore.checkLogin(() => {
handleRouteSwitchTab(RouteSwitchTabEnum.cart);
});
}
/** 底部按钮去结算 */
const handleGoPay = () => {
_useLoginStore.checkLogin(() => {
handleRouteSwitchTab(RouteSwitchTabEnum.cart);
});
}
/** 添加购物车 */
async function onAddCart({ productId, nums } : any) {
const addCart : Response<number> = await queryAddCart({
productId,
nums,
type: AddCartEnum.add,
});
if (addCart.status) {
handleShowToast(addCart.msg, "success");
/** 添加成功后,重新获取购物车数量和价格 */
getCartNumAndMoney();
/** 关闭sku弹框 */
handleChangePopup(false);
} else {
handleShowToast(addCart.msg);
}
}
/** 添加购物车 */
async function onAddCart({ productId, nums } : any) {
const addCart : Response<number> = await queryAddCart({
productId,
nums,
type: AddCartEnum.add,
});
if (addCart.status) {
handleShowToast(addCart.msg, "success");
/** 添加成功后,重新获取购物车数量和价格 */
getCartNumAndMoney();
/** 关闭sku弹框 */
handleChangePopup(false);
} else {
handleShowToast(addCart.msg);
}
}
/** 立即购买 */
async function onBuyNow({ productId, nums } : any) {
const addCart : Response<number> = await queryAddCart({
productId,
nums,
type: AddCartEnum.buy,
cartType: PaymentTypeEnum.common,
});
if (addCart.status) {
handleRouteNavigateTo(`/pages/order/submit/submit?cartIds=${addCart.data}`)
/** 关闭sku弹框 */
handleChangePopup(false);
} else {
handleShowToast(addCart.msg);
}
}
/** 立即购买 */
async function onBuyNow({ productId, nums } : any) {
const addCart : Response<number> = await queryAddCart({
productId,
nums,
type: AddCartEnum.buy,
cartType: PaymentTypeEnum.common,
});
if (addCart.status) {
handleRouteNavigateTo(`/pages/order/submit/submit?cartIds=${addCart.data}`)
/** 关闭sku弹框 */
handleChangePopup(false);
} else {
handleShowToast(addCart.msg);
}
}
/** sku弹框显示隐藏 */
const handleChangePopup = (show : boolean) => {
state.showSku = show;
}
/** sku弹框显示隐藏 */
const handleChangePopup = (show : boolean) => {
state.showSku = show;
}
/** 切换顶部tab */
const hanldeChangeTopTab = (item : CategoriesType, index : number) => {
if (state.leftTabId != item.id) {
state.topTabId = item.id;
state.catId = item.id;
/** 切换顶部tab */
const hanldeChangeTopTab = (item : CategoriesType, index : number) => {
if (state.leftTabId != item.id) {
state.topTabId = item.id;
state.catId = item.id;
state.leftTabId = item?.child[0].id;
state.leftTabList = item?.child;
state.rightTabList = item?.child[0]?.child;
calculationTopMovingDistance(index);
handleResetGoodsList();
hanlderHidebigClassifyPop();
}
}
state.leftTabId = item?.child[0].id;
state.leftTabList = item?.child;
state.rightTabList = item?.child[0]?.child;
calculationTopMovingDistance(index);
handleResetGoodsList();
hanlderHidebigClassifyPop();
}
}
/** 显示顶部大分类弹框 */
const hanldeShowBigClassifyPop = () => {
bigClassifyPop.value.open();
}
/** 隐藏顶部大分类弹框 */
const hanlderHidebigClassifyPop = () => {
bigClassifyPop.value.close();
}
/** 显示顶部大分类弹框 */
const hanldeShowBigClassifyPop = () => {
bigClassifyPop.value.open();
}
/** 隐藏顶部大分类弹框 */
const hanlderHidebigClassifyPop = () => {
bigClassifyPop.value.close();
}
/** 计算顶部tab移动的距离 */
const calculationTopMovingDistance = (index : number) => {
let topScrollLeft = 0;
for (let i = 0; i < index - 1; i++) {
topScrollLeft += 60
};
state.topScrollLeft = topScrollLeft
}
/** 计算顶部tab移动的距离 */
const calculationTopMovingDistance = (index : number) => {
let topScrollLeft = 0;
for (let i = 0; i < index - 1; i++) {
topScrollLeft += 60
};
state.topScrollLeft = topScrollLeft
}
/** 切换左侧tab */
const hanldeChangeLeftTab = (item : CategoriesType) => {
if (state.leftTabId != item.id) {
state.leftTabId = item.id;
state.catId = item.id;
state.rightTabList = item?.child;
handleResetGoodsList();
}
}
/** 切换左侧tab */
const hanldeChangeLeftTab = (item : CategoriesType) => {
if (state.leftTabId != item.id) {
state.leftTabId = item.id;
state.catId = item.id;
state.rightTabList = item?.child;
handleResetGoodsList();
}
}
/** 切换右侧tab */
const hanldeChangeRightTab = (item : CategoriesType) => {
state.catId = item.id;
handleResetGoodsList();
}
/** 切换右侧tab */
const hanldeChangeRightTab = (item : CategoriesType) => {
state.catId = item.id;
handleResetGoodsList();
}
/** 组合商品数据传入组件 */
const hanldeCombinationGoodsData = (item : GoodsType) => {
return {
id: item.id,
image: item.image,
name: item.name,
brief: item.brief,
price: item.price,
mktprice: item.mktprice,
isRecommend: item.isRecommend,
isHot: item.isHot,
}
}
/** 组合商品数据传入组件 */
const hanldeCombinationGoodsData = (item : GoodsType) => {
return {
id: item.id,
image: item.image,
name: item.name,
brief: item.brief,
price: item.price,
mktprice: item.mktprice,
isRecommend: item.isRecommend,
isHot: item.isHot,
}
}
const hanldeClickGoods = (item : GoodsType) => {
uni.navigateTo({
url: `/pages/goods/detail?id=${item.id}`
})
}
const hanldeClickGoods = (item : GoodsType) => {
uni.navigateTo({
url: `/pages/goods/detail?id=${item.id}`
})
}
/** 加载下一页数据 */
const handleScrolltolower = () => {
if (state.totalPages > state.page) {
state.page++;
getGoodsPageList();
}
}
/** 加载下一页数据 */
const handleScrolltolower = () => {
if (state.totalPages > state.page) {
state.page++;
getGoodsPageList();
}
}
/** 重置商品列表数据 */
const handleResetGoodsList = () => {
state.page = 1;
state.goodsList = [];
getGoodsPageList();
}
/** 重置商品列表数据 */
const handleResetGoodsList = () => {
state.page = 1;
state.goodsList = [];
getGoodsPageList();
}
</script>
<style lang="scss" scoped>
@import './classify-five.scss';
@import './classify-five.scss';
</style>

View File

@@ -1,301 +1,307 @@
<template>
<view class="classify-box" :style="{ height: `${props.height}px` }">
<view class="classify-left">
<scroll-view class="scroll-view" :scroll-y="true">
<view :class="['item', { 'on': state.leftTabId === item.id }]" v-for="item, index in state.classifyData"
:key="index" @click="hanldeChangeLeftTab(item)">
{{ item.name }}
</view>
<view class="no-more"></view>
</scroll-view>
</view>
<view class="classify-right">
<view class="right-tab-box">
<coreshop-tabs :list="state.rightTabList" @hanldeClickTab="hanldeChangeRightTab"></coreshop-tabs>
</view>
<view class="right-data-list">
<scroll-view class="scroll-view" :scroll-y="true" @scrolltolower="handleScrolltolower">
<view class="advert-box radius-15">
<coreshop-advert :code="advertPosition.goodsClassifyBanner"></coreshop-advert>
</view>
<view class="data-box" v-if="state.goodsList.length > 0">
<view class="item-box" v-for="item in state.goodsList" :key="item.id">
<coreshop-goods-card imgWidth="150rpx" imgHeight="150rpx"
:goodsData="hanldeCombinationGoodsData(item)" @hanldeClickGoods="hanldeClickGoods">
<template #goodPrice>
<view class="price-msg">
<view class="price-box">
<view class="price">
<text class="symbol"></text>
<text class="num">{{ item.price }}</text>
</view>
<view class="underlin-price">{{ item.mktprice }}</view>
</view>
<view class="btn" @click="handleSelectSku(item.id)">
<image class="img" :src="handleStaticResources('/static/images/cart.png')">
</image>
</view>
</view>
</template>
</coreshop-goods-card>
</view>
<view class="no-more">
<uv-divider :dashed="true"
:text="state.totalPages > state.page ? '下滑加载更多' : '没有更多了'"></uv-divider>
</view>
</view>
<view v-else>
<coreshop-empty></coreshop-empty>
</view>
</scroll-view>
</view>
</view>
<view class="shopping-box">
<view class="shopping-Bag">
<view class="bag-box">
<image class="img" :src="handleStaticResources('/static/images/cart-bag.png')"></image>
</view>
<view class="price-box">
<text class="num">购物车数量:{{ state.cartCount }}</text>
<text class="price">{{ state.cartMoney }}</text>
</view>
</view>
<view class="btn-buy" @click="handleGoPay">
去结算
</view>
</view>
<view class="classify-box" :style="{ height: `${props.height}px` }">
<view class="classify-left">
<scroll-view class="scroll-view" :scroll-y="true">
<view :class="['item', { 'on': state.leftTabId === item.id }]" v-for="item, index in state.classifyData"
:key="index" @click="hanldeChangeLeftTab(item)">
{{ item.name }}
</view>
<view class="no-more"></view>
</scroll-view>
</view>
<view class="classify-right">
<view class="right-tab-box">
<coreshop-tabs :list="state.rightTabList" @hanldeClickTab="hanldeChangeRightTab"></coreshop-tabs>
</view>
<view class="right-data-list">
<scroll-view class="scroll-view" :scroll-y="true" @scrolltolower="handleScrolltolower">
<view class="advert-box radius-15">
<coreshop-advert :code="advertPosition.goodsClassifyBanner"></coreshop-advert>
</view>
<view class="data-box" v-if="state.goodsList.length > 0">
<view class="item-box" v-for="item in state.goodsList" :key="item.id">
<coreshop-goods-card imgWidth="150rpx" imgHeight="150rpx"
:goodsData="hanldeCombinationGoodsData(item)" @hanldeClickGoods="hanldeClickGoods">
<template #goodPrice>
<view class="price-msg">
<view class="price-box">
<view class="price">
<text class="symbol"></text>
<text class="num">{{ item.price }}</text>
</view>
<view class="underlin-price">{{ item.mktprice }}</view>
</view>
<view class="btn" @click="handleSelectSku(item.id)">
<image class="img" :src="handleStaticResources('/static/images/cart.png')">
</image>
</view>
</view>
</template>
</coreshop-goods-card>
</view>
<view class="no-more">
<uv-divider :dashed="true"
:text="state.totalPages > state.page ? '下滑加载更多' : '没有更多了'"></uv-divider>
</view>
</view>
<view v-else>
<coreshop-empty></coreshop-empty>
</view>
</scroll-view>
</view>
</view>
<view class="shopping-box">
<view class="shopping-Bag">
<view class="bag-box">
<image class="img" :src="handleStaticResources('/static/images/cart-bag.png')"></image>
</view>
<view class="price-box">
<text class="num">购物车数量:{{ state.cartCount }}</text>
<text class="price">{{ state.cartMoney }}</text>
</view>
</view>
<view class="btn-buy" @click="handleGoPay">
去结算
</view>
</view>
<!-- 商品sku弹框 -->
<GoodsDetailSkuPopup :showSku="state.showSku" :goodsDetailData="state.goodsDetailData"
:safeAreaInsetBottom="false" :buyNowNowloading="buyNowLoading" :addCartloading="addCartLoading"
@handleChangePopup="handleChangePopup" @handleAddCart="handleAddCart" @handleBuyNow="handleBuyNow">
</GoodsDetailSkuPopup>
<!-- 商品sku弹框 -->
<GoodsDetailSkuPopup :showSku="state.showSku" :goodsDetailData="state.goodsDetailData"
:safeAreaInsetBottom="false" :buyNowNowloading="buyNowLoading" :addCartloading="addCartLoading"
@handleChangePopup="handleChangePopup" @handleAddCart="handleAddCart" @handleBuyNow="handleBuyNow">
</GoodsDetailSkuPopup>
</view>
</view>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref, watch, nextTick } from 'vue';
import { UserToken, advertPosition, onClassifyPageHide } from '@/core/consts';
import { queryGoodsPageList, queryCartNumAndMoney, queryGoodsDetailByToken, queryGoodsDetail, queryAddCart } from '@/core/api';
import type { CategoriesType, Response, GoodsListType, GoodsType } from '@/core/models';
import { handleStaticResources, handleRouteNavigateTo, handleShowToast, handleRouteSwitchTab } from '@/core/utils';
import { useLoginStore } from '@/core/store'
import { AddCartEnum, PaymentTypeEnum, RouteSwitchTabEnum } from '@/core/enum';
import GoodsDetailSkuPopup from '@/pages/components/goods-detail/components/goods-detail-sku/goods-detail-sku.vue';
import { useLoadingFn } from '@/core/hooks';
import { onMounted, reactive, ref, watch } from 'vue';
import { UserToken, advertPosition, onClassifyPageHide, onClassifyPagePullDownRefresh } from '@/core/consts';
import { queryGoodsPageList, queryCartNumAndMoney, queryGoodsDetailByToken, queryGoodsDetail, queryAddCart } from '@/core/api';
import type { CategoriesType, Response, GoodsListType, GoodsType } from '@/core/models';
import { handleStaticResources, handleRouteNavigateTo, handleShowToast, handleRouteSwitchTab } from '@/core/utils';
import { useLoginStore } from '@/core/store'
import { AddCartEnum, PaymentTypeEnum, RouteSwitchTabEnum } from '@/core/enum';
import GoodsDetailSkuPopup from '@/pages/components/goods-detail/components/goods-detail-sku/goods-detail-sku.vue';
import { useLoadingFn } from '@/core/hooks';
/** 登录store */
const _useLoginStore = useLoginStore();
/** 登录store */
const _useLoginStore = useLoginStore();
const props = withDefaults(defineProps<{
data : Array<CategoriesType>,
height : number,
}>(), {
data: () => [],
height: 0,
});
const props = withDefaults(defineProps<{
data : Array<CategoriesType>,
height : number,
}>(), {
data: () => [],
height: 0,
});
const state = reactive<{
classifyData : Array<CategoriesType>;
leftTabId : number;
rightTabList : Array<CategoriesType>;
catId : number;
goodsList : Array<GoodsType>;
page : number;
totalPages : number;
cartCount : number;
cartMoney : number,
showSku : boolean;
goodsDetailData : any;
}>({
classifyData: [],
leftTabId: 0,
rightTabList: [],
catId: 0,
goodsList: [],
page: 1,
totalPages: 0,
cartCount: 0,
cartMoney: 0,
showSku: false,
goodsDetailData: {},
});
const state = reactive<{
classifyData : Array<CategoriesType>;
leftTabId : number;
rightTabList : Array<CategoriesType>;
catId : number;
goodsList : Array<GoodsType>;
page : number;
totalPages : number;
cartCount : number;
cartMoney : number,
showSku : boolean;
goodsDetailData : any;
}>({
classifyData: [],
leftTabId: 0,
rightTabList: [],
catId: 0,
goodsList: [],
page: 1,
totalPages: 0,
cartCount: 0,
cartMoney: 0,
showSku: false,
goodsDetailData: {},
});
const buyNowLoading = ref(false);
const addCartLoading = ref(false);
const buyNowLoading = ref(false);
const addCartLoading = ref(false);
const handleBuyNow = useLoadingFn(onBuyNow, buyNowLoading);
const handleAddCart = useLoadingFn(onAddCart, addCartLoading)
const handleBuyNow = useLoadingFn(onBuyNow, buyNowLoading);
const handleAddCart = useLoadingFn(onAddCart, addCartLoading)
watch(() => props.data, (newVal : Array<CategoriesType>) => {
if (newVal) {
state.classifyData = newVal.map((item : CategoriesType) => {
item.child.unshift({
name: "全部",
id: item.id,
})
return item;
});
watch(() => props.data, (newVal : Array<CategoriesType>) => {
if (newVal) {
state.classifyData = newVal.map((item : CategoriesType) => {
item.child.unshift({
name: "全部",
id: item.id,
})
return item;
});
state.leftTabId = state.classifyData[0]?.id;
state.catId = state.classifyData[0]?.id;
state.rightTabList = state.classifyData[0]?.child;
state.leftTabId = state.classifyData[0]?.id;
state.catId = state.classifyData[0]?.id;
state.rightTabList = state.classifyData[0]?.child;
getGoodsPageList();
}
})
getGoodsPageList();
}
})
onMounted(() => {
if (uni.getStorageSync(UserToken)) {
getCartNumAndMoney();
}
onMounted(() => {
if (uni.getStorageSync(UserToken)) {
getCartNumAndMoney();
}
uni.$on(onClassifyPageHide, () => {
state.showSku = false;
})
});
uni.$on(onClassifyPagePullDownRefresh, () => {
state.page = 1;
state.totalPages = 1;
state.goodsList = [];
})
/** 获取商品列表数据 */
const getGoodsPageList = async () => {
uni.$on(onClassifyPageHide, () => {
state.showSku = false;
})
});
const goodsPageList : Response<GoodsListType> = await queryGoodsPageList({
page: state.page,
limit: 10,
where: `{"catId":${state.catId}}`
});
if (goodsPageList.status) {
state.totalPages = goodsPageList.data?.totalPages;
state.goodsList = state.goodsList.concat(goodsPageList.data?.list);
}
}
/** 获取商品列表数据 */
const getGoodsPageList = async () => {
if (!state.catId) { return; }
const goodsPageList : Response<GoodsListType> = await queryGoodsPageList({
page: state.page,
limit: 10,
where: `{"catId":${state.catId}}`
});
if (goodsPageList.status) {
state.totalPages = goodsPageList.data?.totalPages;
state.goodsList = state.goodsList.concat(goodsPageList.data?.list);
}
}
/** 获取购物车数量和价格 */
const getCartNumAndMoney = async () => {
const cartNumAndMoney : Response<{ count : number, money : number }> = await queryCartNumAndMoney();
state.cartCount = cartNumAndMoney?.data?.count || 0;
state.cartMoney = cartNumAndMoney?.data?.money || 0;
}
/** 获取购物车数量和价格 */
const getCartNumAndMoney = async () => {
const cartNumAndMoney : Response<{ count : number, money : number }> = await queryCartNumAndMoney();
state.cartCount = cartNumAndMoney?.data?.count || 0;
state.cartMoney = cartNumAndMoney?.data?.money || 0;
}
/** 选择sku */
const handleSelectSku = (id : number) => {
_useLoginStore.checkLogin(async () => {
let goodsDetail : any = null;
if (uni.getStorageSync(UserToken)) {
goodsDetail = await queryGoodsDetailByToken({
id: id,
data: true,
})
} else {
goodsDetail = await queryGoodsDetail({
id: id,
data: true,
})
}
state.goodsDetailData = goodsDetail.data;
state.showSku = true;
})
}
/** 选择sku */
const handleSelectSku = (id : number) => {
_useLoginStore.checkLogin(async () => {
let goodsDetail : any = null;
if (uni.getStorageSync(UserToken)) {
goodsDetail = await queryGoodsDetailByToken({
id: id,
data: true,
})
} else {
goodsDetail = await queryGoodsDetail({
id: id,
data: true,
})
}
state.goodsDetailData = goodsDetail.data;
state.showSku = true;
})
}
/** 底部按钮去结算 */
const handleGoPay = () => {
_useLoginStore.checkLogin(() => {
handleRouteSwitchTab(RouteSwitchTabEnum.cart);
});
}
/** 底部按钮去结算 */
const handleGoPay = () => {
_useLoginStore.checkLogin(() => {
handleRouteSwitchTab(RouteSwitchTabEnum.cart);
});
}
/** 添加购物车 */
async function onAddCart({ productId, nums } : any) {
const addCart : Response<number> = await queryAddCart({
productId,
nums,
type: AddCartEnum.add,
});
if (addCart.status) {
handleShowToast(addCart.msg, 'success');
/** 添加成功后,重新获取购物车数量和价格 */
await getCartNumAndMoney();
/** 关闭sku弹框 */
handleChangePopup(false);
} else {
handleShowToast(addCart.msg);
}
}
/** 添加购物车 */
async function onAddCart({ productId, nums } : any) {
const addCart : Response<number> = await queryAddCart({
productId,
nums,
type: AddCartEnum.add,
});
if (addCart.status) {
handleShowToast(addCart.msg, 'success');
/** 添加成功后,重新获取购物车数量和价格 */
await getCartNumAndMoney();
/** 关闭sku弹框 */
handleChangePopup(false);
} else {
handleShowToast(addCart.msg);
}
}
/** 立即购买 */
async function onBuyNow({ productId, nums } : any) {
const addCart : Response<number> = await queryAddCart({
productId,
nums,
type: AddCartEnum.buy,
cartType: PaymentTypeEnum.common,
});
if (addCart.status) {
handleRouteNavigateTo(`/pages/order/submit/submit?cartIds=${addCart.data}`)
/** 关闭sku弹框 */
handleChangePopup(false);
} else {
handleShowToast(addCart.msg);
}
}
/** 立即购买 */
async function onBuyNow({ productId, nums } : any) {
const addCart : Response<number> = await queryAddCart({
productId,
nums,
type: AddCartEnum.buy,
cartType: PaymentTypeEnum.common,
});
if (addCart.status) {
handleRouteNavigateTo(`/pages/order/submit/submit?cartIds=${addCart.data}`)
/** 关闭sku弹框 */
handleChangePopup(false);
} else {
handleShowToast(addCart.msg);
}
}
/** sku弹框显示隐藏 */
const handleChangePopup = (show : boolean) => {
state.showSku = show;
}
/** sku弹框显示隐藏 */
const handleChangePopup = (show : boolean) => {
state.showSku = show;
}
/** 切换左侧tab */
const hanldeChangeLeftTab = (item : CategoriesType) => {
if (state.leftTabId != item.id) {
state.leftTabId = item.id;
state.catId = item.id;
state.rightTabList = item?.child;
handleResetGoodsList();
}
}
/** 切换左侧tab */
const hanldeChangeLeftTab = (item : CategoriesType) => {
if (state.leftTabId != item.id) {
state.leftTabId = item.id;
state.catId = item.id;
state.rightTabList = item?.child;
handleResetGoodsList();
}
}
/** 切换右侧tab */
const hanldeChangeRightTab = (item : CategoriesType) => {
state.catId = item.id;
handleResetGoodsList();
}
/** 切换右侧tab */
const hanldeChangeRightTab = (item : CategoriesType) => {
state.catId = item.id;
handleResetGoodsList();
}
/** 组合商品数据传入组件 */
const hanldeCombinationGoodsData = (item : any) => {
return {
id: item.id,
image: item.image,
name: item.name,
brief: item.brief,
price: item.price,
mktprice: item.mktprice,
isRecommend: item.isRecommend,
isHot: item.isHot,
}
}
/** 组合商品数据传入组件 */
const hanldeCombinationGoodsData = (item : any) => {
return {
id: item.id,
image: item.image,
name: item.name,
brief: item.brief,
price: item.price,
mktprice: item.mktprice,
isRecommend: item.isRecommend,
isHot: item.isHot,
}
}
/** 加载下一页数据 */
const handleScrolltolower = () => {
if (state.totalPages > state.page) {
state.page++;
getGoodsPageList();
}
}
/** 加载下一页数据 */
const handleScrolltolower = () => {
if (state.totalPages > state.page) {
state.page++;
getGoodsPageList();
}
}
/** 重置商品列表数据 */
const handleResetGoodsList = () => {
state.page = 1;
state.goodsList = [];
getGoodsPageList();
}
/** 重置商品列表数据 */
const handleResetGoodsList = () => {
state.page = 1;
state.goodsList = [];
getGoodsPageList();
}
const hanldeClickGoods = (item : GoodsType) => {
uni.navigateTo({
url: `/pages/goods/detail?id=${item.id}`
})
}
const hanldeClickGoods = (item : GoodsType) => {
uni.navigateTo({
url: `/pages/goods/detail?id=${item.id}`
})
}
</script>
<style lang="scss" scoped>
@import './classify-four.scss';
@import './classify-four.scss';
</style>

View File

@@ -1,72 +1,74 @@
<template>
<view class="classify-box" :style="{height: `${height}px`}">
<view class="classify-left">
<scroll-view class="scroll-view" enable-flex :scroll-y="true">
<view :class="['item', { 'on': state.leftTabId === item.id }]" v-for="item, index in props.data"
:key="index" @click="hanldeChangeLeftTab(item)">
{{ item.name }}
</view>
</scroll-view>
</view>
<view class="classify-right">
<scroll-view class="scroll-view" enable-flex :scroll-y="true">
<view class="advert-box radius-15">
<coreshop-advert :code="advertPosition.goodsClassifyBanner"></coreshop-advert>
</view>
<view class="data-box" v-if="state.list.length > 0">
<view class="item-box" v-for="item,index in state.list" :key="index"
@click="handleGoCategory(item.id)">
<image class="img" :src="item.imageUrl"></image>
<view class="name">{{ item.name }}</view>
</view>
</view>
<view v-else>
<coreshop-empty></coreshop-empty>
</view>
</scroll-view>
</view>
</view>
<view class="classify-box" :style="{height: `${height}px`}">
<view class="classify-left">
<scroll-view class="scroll-view" enable-flex :scroll-y="true">
<view :class="['item', { 'on': state.leftTabId === item.id }]" v-for="item, index in props.data"
:key="index" @click="hanldeChangeLeftTab(item)">
{{ item.name }}
</view>
</scroll-view>
</view>
<view class="classify-right">
<scroll-view class="scroll-view" enable-flex :scroll-y="true">
<view class="advert-box radius-15">
<coreshop-advert :code="advertPosition.goodsClassifyBanner"></coreshop-advert>
</view>
<view class="data-box" v-if="state.list && state.list.length > 0">
<view class="item-box" v-for="item,index in state.list" :key="index"
@click="handleGoCategory(item.id)">
<image class="img" :src="item.imageUrl"></image>
<view class="name">{{ item.name }}</view>
</view>
</view>
<view v-else>
<coreshop-empty></coreshop-empty>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup lang="ts">
import { reactive, watch } from 'vue';
import type { CategoriesType } from '@/core/models';
import { advertPosition } from '@/core/consts';
import { handleRouteNavigateTo } from '@/core/utils';
import { reactive, watch } from 'vue';
import type { CategoriesType } from '@/core/models';
import { advertPosition } from '@/core/consts';
import { handleRouteNavigateTo } from '@/core/utils';
const props = withDefaults(defineProps<{
data : Array<CategoriesType>,
height : number,
}>(), {
data: () => [],
height: 0,
});
const props = withDefaults(defineProps<{
data : Array<CategoriesType>,
height : number,
}>(), {
data: () => [],
height: 0,
});
const state = reactive<{
leftTabId : number;
list : Array<CategoriesType>
}>({
leftTabId: 0,
list: [],
});
const state = reactive<{
leftTabId : number;
list : Array<CategoriesType>
}>({
leftTabId: 0,
list: [],
});
watch(() => props.data, (newVal : Array<CategoriesType>) => {
state.leftTabId = newVal[0]?.id;
state.list = newVal[0]?.child;
})
watch(() => props.data, (newVal : Array<CategoriesType>) => {
if(newVal){
state.leftTabId = newVal[0]?.id;
state.list = newVal[0]?.child;
}
})
const hanldeChangeLeftTab = (item : CategoriesType) => {
if (state.leftTabId != item.id) {
state.leftTabId = item.id;
state.list = item?.child;
}
}
const hanldeChangeLeftTab = (item : CategoriesType) => {
if (state.leftTabId != item.id) {
state.leftTabId = item.id;
state.list = item?.child;
}
}
const handleGoCategory = (id : number) => {
handleRouteNavigateTo(`/pages/category/category?catId=${id}`)
}
const handleGoCategory = (id : number) => {
handleRouteNavigateTo(`/pages/category/category?catId=${id}`)
}
</script>
<style lang="scss" scoped>
@import './classify-three.scss';
@import './classify-three.scss';
</style>

View File

@@ -1,115 +1,118 @@
<template>
<coreshop-page :isBack="false" bgColor="rgba(0,0,0,0)" :isShowStatusBarHeight="false" showLoginModalDom
needLoadingPage :loadingPage="loading">
<view class="layout-classify-page">
<!-- #ifndef MP-ALIPAY -->
<uv-navbar bgColor="#EEF3F7" :height="menuButtonHeight+'px'">
<template #left>
<!-- #ifdef MP -->
<view class="search-box" style="max-width: 500rpx;">
<!-- #endif -->
<!-- #ifdef APP -->
<view class="search-box" style="width: 700rpx;">
<!-- #endif -->
<view class="page-name">分类</view>
<uv-input v-model="state.keyWord" @confirm="handleSearch" shape="circle"
placeholder="请输入关键词" prefixIcon="search"
:customStyle="{ 'background-color': '#fff' ,'padding':'3px 9px'}"
prefixIconStyle="font-size: 22px;color: #909399">
<template #suffix>
<view class="search-tit" @click.stop="handleSearch">搜索</view>
</template>
</uv-input>
</view>
</template>
</uv-navbar>
<!-- #endif -->
<view class="content-box" :style="{'height': `${state.height}px` }">
<classifyOne v-if="shopConfigStore.config.cateStyle == GoodsListEnum.one" :data="state.categoriesList">
</classifyOne>
<classifyTwo v-else-if="shopConfigStore.config.cateStyle == GoodsListEnum.two"
:data="state.categoriesList">
</classifyTwo>
<classifyThree v-else-if="shopConfigStore.config.cateStyle == GoodsListEnum.three"
:data="state.categoriesList" :height="state.height"></classifyThree>
<classifyFour v-else-if="shopConfigStore.config.cateStyle == GoodsListEnum.four"
:data="state.categoriesList" :height="state.height">
</classifyFour>
<classifyFive v-else-if="shopConfigStore.config.cateStyle == GoodsListEnum.five"
:data="state.categoriesList" :height="state.height" :statusBarHeight="statusBarHeight">
</classifyFive>
</view>
</view>
</coreshop-page>
<coreshop-page :isBack="false" bgColor="rgba(0,0,0,0)" :isShowStatusBarHeight="false" showLoginModalDom
needLoadingPage :loadingPage="loading">
<view class="layout-classify-page">
<!-- #ifndef MP-ALIPAY -->
<uv-navbar bgColor="#EEF3F7" :height="menuButtonHeight+'px'">
<template #left>
<!-- #ifdef MP -->
<view class="search-box" style="max-width: 500rpx;">
<!-- #endif -->
<!-- #ifdef APP -->
<view class="search-box" style="width: 700rpx;">
<!-- #endif -->
<view class="page-name">分类</view>
<uv-input v-model="state.keyWord" @confirm="handleSearch" shape="circle"
placeholder="请输入关键词" prefixIcon="search"
:customStyle="{ 'background-color': '#fff' ,'padding':'3px 9px'}"
prefixIconStyle="font-size: 22px;color: #909399">
<template #suffix>
<view class="search-tit" @click.stop="handleSearch">搜索</view>
</template>
</uv-input>
</view>
</template>
</uv-navbar>
<!-- #endif -->
<view class="content-box" :style="{'height': `${state.height}px` }">
<classifyOne v-if="shopConfigStore.config.cateStyle == GoodsListEnum.one" :data="state.categoriesList">
</classifyOne>
<classifyTwo v-else-if="shopConfigStore.config.cateStyle == GoodsListEnum.two"
:data="state.categoriesList">
</classifyTwo>
<classifyThree v-else-if="shopConfigStore.config.cateStyle == GoodsListEnum.three"
:data="state.categoriesList" :height="state.height"></classifyThree>
<classifyFour v-else-if="shopConfigStore.config.cateStyle == GoodsListEnum.four"
:data="state.categoriesList" :height="state.height">
</classifyFour>
<classifyFive v-else-if="shopConfigStore.config.cateStyle == GoodsListEnum.five"
:data="state.categoriesList" :height="state.height" :statusBarHeight="statusBarHeight">
</classifyFive>
</view>
</view>
</coreshop-page>
</template>
<script setup lang="ts">
import { onMounted, reactive, watchEffect, ref } from 'vue';
import { onHide, onPullDownRefresh } from '@dcloudio/uni-app';
import { useShopConfigStore } from '@/core/store';
import { queryAllCategories } from '@/core/api';
import { GoodsListEnum } from '@/core/enum';
import { handleRouteNavigateTo } from '@/core/utils';
import { onClassifyPageHide } from '@/core/consts';
import type { Response, ShopConfigStoreType, CategoriesType } from '@/core/models';
import classifyOne from './classify-page/classify-one/classify-one.vue';
import classifyTwo from './classify-page/classify-two/classify-two.vue';
import classifyThree from './classify-page/classify-three/classify-three.vue';
import classifyFour from './classify-page/classify-four/classify-four.vue';
import classifyFive from './classify-page/classify-five/classify-five.vue';
import { useLoadingFn, useSystemInfo } from '@/core/hooks';
import { onMounted, reactive, watchEffect, ref } from 'vue';
import { onHide, onPullDownRefresh } from '@dcloudio/uni-app';
import { useShopConfigStore } from '@/core/store';
import { queryAllCategories } from '@/core/api';
import { GoodsListEnum } from '@/core/enum';
import { handleRouteNavigateTo } from '@/core/utils';
import { onClassifyPageHide, onClassifyPagePullDownRefresh } from '@/core/consts';
import type { Response, ShopConfigStoreType, CategoriesType } from '@/core/models';
import classifyOne from './classify-page/classify-one/classify-one.vue';
import classifyTwo from './classify-page/classify-two/classify-two.vue';
import classifyThree from './classify-page/classify-three/classify-three.vue';
import classifyFour from './classify-page/classify-four/classify-four.vue';
import classifyFive from './classify-page/classify-five/classify-five.vue';
import { useLoadingFn, useSystemInfo } from '@/core/hooks';
// 获取项目配置
const shopConfigStore : ShopConfigStoreType = useShopConfigStore();
// 获取项目配置
const shopConfigStore : ShopConfigStoreType = useShopConfigStore();
// 获取自定义导航栏高度
const { statusBarHeight, systemInfo, menuButtonHeight } = useSystemInfo();
const loading = ref(true);
// 获取自定义导航栏高度
const { statusBarHeight, systemInfo, menuButtonHeight } = useSystemInfo();
const loading = ref(true);
const state = reactive<{
categoriesList : Array<CategoriesType>,
height : number,
keyWord : string;
}>({
categoriesList: [],
height: 0,
keyWord: "",
})
const state = reactive<{
categoriesList : Array<CategoriesType>,
height : number,
keyWord : string;
}>({
categoriesList: [],
height: 0,
keyWord: "",
})
const handleuAllCategories = useLoadingFn(getAllCategories, loading);
const handleuAllCategories = useLoadingFn(getAllCategories, loading);
onMounted(async () => {
handleuAllCategories();
})
onMounted(async () => {
handleuAllCategories();
})
watchEffect(() => {
state.height = systemInfo.value.windowHeight - statusBarHeight.value ;
})
watchEffect(() => {
state.height = systemInfo.value.windowHeight - statusBarHeight.value;
})
onHide(() => {
/** 触发自定义onhide事件让后代组件监听页面是关闭 */
uni.$emit(onClassifyPageHide);
});
onHide(() => {
/** 触发自定义onhide事件让后代组件监听页面是关闭 */
uni.$emit(onClassifyPageHide);
});
onPullDownRefresh(async () => {
await handleuAllCategories()
uni.stopPullDownRefresh();
});
onPullDownRefresh(async () => {
/** 触发onPullDownRefresh事件让后代组件监听页面是下拉刷新 */
uni.$emit(onClassifyPagePullDownRefresh);
state.categoriesList = [];
await handleuAllCategories()
uni.stopPullDownRefresh();
});
/** 获取购物车数量价格 */
async function getAllCategories() {
const allCategories : Response<Array<CategoriesType>> = await queryAllCategories();
state.categoriesList = allCategories.data || [];
}
/** 获取购物车数量价格 */
async function getAllCategories() {
const allCategories : Response<Array<CategoriesType>> = await queryAllCategories();
state.categoriesList = allCategories.data || [];
}
/** 搜索 */
const handleSearch = () => {
handleRouteNavigateTo(`/pages/category/category?key=${state.keyWord}`);
}
/** 搜索 */
const handleSearch = () => {
handleRouteNavigateTo(`/pages/category/category?key=${state.keyWord}`);
}
</script>
<style lang="scss" scoped>
@import './classify.scss';
@import './classify.scss';
</style>

View File

@@ -1,39 +1,39 @@
<template>
<swiper class="swiper m-b-25" v-if="props.data.list.length > 0" :vertical="false" :indicator-dots="props.dotsSelect"
:autoplay="props.autoplaySelect" :interval="props.data.duration" :circular="props.circularSelect"
:indicator-color="props.indicatorColor" :indicator-active-color="props.indicatorColorActive">
<swiper-item v-for="item, index in props.data.list" :key="index" @click="hanldeClickImage(item)">
<image class="swiper-img" :src="item.image"></image>
</swiper-item>
</swiper>
<swiper class="swiper m-b-25" v-if="props.data.list.length > 0" :vertical="false" :indicator-dots="props.dotsSelect"
:autoplay="props.autoplaySelect" :interval="props.data.duration" :circular="props.circularSelect"
:indicator-color="props.indicatorColor" :indicator-active-color="props.indicatorColorActive">
<swiper-item v-for="item, index in props.data.list" :key="index" @click="hanldeClickImage(item)">
<image class="swiper-img" :src="item.image"></image>
</swiper-item>
</swiper>
</template>
<script setup lang="ts">
import type { PageConfigItemsImgSlideType } from '@/core/models';
import { handleAdvertiseDetail } from '@/core/utils';
import type { PageConfigItemsImgSlideType } from '@/core/models';
import { handleAdvertiseDetail } from '@/core/utils';
const props = withDefaults(defineProps<{
data : any,
dotsSelect : boolean,
autoplaySelect : boolean,
intervalSelect : number,
circularSelect : boolean,
indicatorColor : string,
indicatorColorActive : string,
}>(), {
data: {},
dotsSelect: true,
autoplaySelect: true,
intervalSelect: 3000,
circularSelect: true,
indicatorColor: '#EEF2F6',
indicatorColorActive: '#E74435'
});
const props = withDefaults(defineProps<{
data ?: any,
dotsSelect ?: boolean,
autoplaySelect ?: boolean,
intervalSelect ?: number,
circularSelect ?: boolean,
indicatorColor ?: string,
indicatorColorActive ?: string,
}>(), {
data: {},
dotsSelect: true,
autoplaySelect: true,
intervalSelect: 3000,
circularSelect: true,
indicatorColor: '#EEF2F6',
indicatorColorActive: '#E74435'
});
const hanldeClickImage = (item : PageConfigItemsImgSlideType) => {
handleAdvertiseDetail(Number(item.linkType), item.linkValue);
}
const hanldeClickImage = (item : PageConfigItemsImgSlideType) => {
handleAdvertiseDetail(Number(item.linkType), item.linkValue);
}
</script>
<style lang="scss" scoped>
@import "./home-swiper.scss";
@import "./home-swiper.scss";
</style>

View File

@@ -53,9 +53,11 @@
</lpainterView>
</lpainterView>
</LPainter>
<!-- #ifdef MP -->
<view class="tip-box" v-if="state.imgSrc">
如果您下载失败或者以前拒绝过弹出的授权相册提醒请点击右上角三个点进入设置页面开启添加到相册功能
</view>
<!-- #endif -->
<view class="btn-box">
<view class="btn">
<view class="back" @click="handlenNavigateBack">返回上一页</view>
@@ -328,11 +330,11 @@
/** 保存到本地 */
const handlesavePoster = async () => {
// #ifdef APP-PLUS || APP-PLUS-NVUE
uni.downloadFile({
url: state.imgSrc,
uni.getImageInfo({
src: state.imgSrc,
success(res : any) {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
filePath: res.path,
success() {
handleShowToast('保存成功', 'success');
},

View File

@@ -13,7 +13,7 @@
</view>
</view>
<view class="title-box" v-if="state.formInfo?.description">
<view calss="title">{{ state.formInfo?.description }}1</view>
<view calss="title">{{ state.formInfo?.description }}</view>
</view>
<!-- 表单主体内容 -->
<view class="form-content">