Merge branch 'feature/task1.0.0_xg' into 'feature/task1.0.0'

feat: 调整热门分类逻辑以及交互

See merge request yanxuan-frontend/shop-pc!17
merge-requests/18/head
肖广 3 years ago
commit 0b9bc32561

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

@ -92,3 +92,19 @@ $baseFontSize: 100 !default;
width: 1200px; width: 1200px;
margin: 0 auto; margin: 0 auto;
} }
/*单行溢出*/
@mixin ellipsis {
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap
}
/*多行溢出*/
@mixin ellipses($line) {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: $line;
-webkit-box-orient: vertical;
}

@ -19,4 +19,10 @@ const SEX_TYPE = {
UNKNOW: 3, // 未知 UNKNOW: 3, // 未知
}; };
export { TOKEN_KEY, ORDER_STATUS, SEX_TYPE }; // 热门分类类级
const CATEGROY_LEVEL = {
ONE: 1,
TWO: 2,
};
export { TOKEN_KEY, ORDER_STATUS, SEX_TYPE, CATEGROY_LEVEL };

@ -1,5 +1,6 @@
<template> <template>
<div class="layout-header"> <div class="layout-header">
<!-- 滚动吸顶头部 -->
<template v-if="isSticky"> <template v-if="isSticky">
<div class="sticky-bar-header"> <div class="sticky-bar-header">
<div class="sticky-bar-header__wrap flex flex-middle flex-between"> <div class="sticky-bar-header__wrap flex flex-middle flex-between">
@ -75,6 +76,7 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 热门分类 -->
<div class="bar-header-box__tab flex flex-middle"> <div class="bar-header-box__tab flex flex-middle">
<div <div
v-show="showCategroyTab" v-show="showCategroyTab"
@ -88,17 +90,21 @@
</div> </div>
<div <div
v-show="isCategroyOpen || categroyVisible" v-show="isCategroyOpen || categroyVisible"
class="tab-category__menu flex flex-left" class="tab-category__menu flex"
@mouseenter="handleCategoryTwoChange(true)" @mouseenter="handleCategoryTwoChange(true)"
@mouseleave="handleCategoryTwoChange(false)" @mouseleave="handleCategoryTwoChange(false)"
> >
<!-- 左侧一级分类 --> <!-- 左侧一级分类 -->
<div class="tab-category-menu__left"> <div class="tab-category-menu__left">
<div <div
v-for="item in categrayData" v-for="item in categroyData"
:key="item.id" :key="item.id"
@mouseenter="handleCategorySelect(item.id)" @mouseenter="handleCategoryHover(item.id)"
@click="onCategoryClick(item.id, CATEGROY_LEVEL.ONE)"
class="menu-left__item flex flex-middle" class="menu-left__item flex flex-middle"
:class="{
'menu-left__item--light': item.id === currentCategroyId,
}"
> >
<img /> <img />
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
@ -107,15 +113,25 @@
<!-- 右侧二级分类 --> <!-- 右侧二级分类 -->
<div <div
v-show="categroyTwoVisible" v-show="categroyTwoVisible"
class="tab-category-menu__right" class="tab-category-menu__right flex-1"
> >
<div <div
v-for="item in currentCategrayList" v-for="item in categroyData"
:key="item.id" :key="item.id"
class="menu-right__item" @mouseenter="handleCategoryHover(item.id)"
@click="onCateogryTwoClick(item)" class="category-menu-right__wrap"
:class="{
'category-menu-right__wrap--light':
item.id === currentCategroyId,
}"
> >
{{ item.name }} <span
v-for="itemList in item.list"
:key="itemList.id"
class="menu-right-wrap__item"
@click="onCategoryClick(itemList.id, CATEGROY_LEVEL.TWO)"
>{{ itemList.name }}</span
>
</div> </div>
</div> </div>
</div> </div>
@ -141,11 +157,12 @@
</template> </template>
<script> <script>
import { mapState } from "vuex"; import { mapState } from "vuex";
import HeaderInfoBar from "./HeaderInfoBar.vue";
import { import {
ApiGetCategoryOneList, ApiGetCategoryOneList,
ApiGetCategoryTwoAndGoods, ApiGetCategoryTwoAndGoods,
} from "@/plugins/api/goods"; } from "@/plugins/api/goods";
import { CATEGROY_LEVEL } from "@/constants";
import HeaderInfoBar from "./HeaderInfoBar.vue";
export default { export default {
name: "DefaultHeader", name: "DefaultHeader",
@ -177,28 +194,29 @@ export default {
}, },
data() { data() {
return { return {
CATEGROY_LEVEL,
searchContent: "", searchContent: "",
tabPath: "/", tabPath: "/",
tabList: [ tabList: [
{ label: "首页", value: "/" }, { label: "首页", value: "/" },
{ label: "爆款推荐", value: "/goods/list?id=1" }, {
{ label: "开发书籍", value: "/goods/list?id=2" }, label: "爆款推荐",
value: `/goods/list?id=recommend`,
},
{
label: "开发书籍",
value: `/goods/list?id=6&levelType=${CATEGROY_LEVEL.ONE}`,
},
{ label: "限时秒杀", value: "/sckill" }, { label: "限时秒杀", value: "/sckill" },
], ],
categroyTwoVisible: false, // categroyTwoVisible: false, //
categroyVisible: false, // categroyVisible: false, //
currentCategrayId: 0, // id currentCategroyId: 0, // id
categrayData: [], categroyData: [],
}; };
}, },
computed: { computed: {
...mapState(["userInfo", "token"]), ...mapState(["userInfo", "token"]),
currentCategrayList() {
const data = this.categrayData.find(({ id }) => {
return this.currentCategrayId === id;
});
return (data && data.list) || [];
},
}, },
mounted() { mounted() {
this.getCategroyData(); this.getCategroyData();
@ -207,24 +225,35 @@ export default {
onLoginClick() { onLoginClick() {
this.$isLoginValidate(); this.$isLoginValidate();
}, },
// //
handleCategorySelect(id) { handleCategoryHover(id) {
this.currentCategrayId = id; this.currentCategroyId = id;
}, },
// //
onCateogryTwoClick() { onCategoryClick(id, levelType) {
this.$router.push("/goods/list"); this.categroyVisible = false;
this.categroyTwoVisible = false;
this.$router.push({
path: "/goods/list",
query: {
id,
levelType,
},
});
}, },
// //
handleCategoryChange(val) { handleCategoryChange(val) {
this.categroyVisible = val; this.categroyVisible = val;
if (!val) {
this.currentCategroyId = 0;
}
}, },
// //
handleCategoryTwoChange(val) { handleCategoryTwoChange(val) {
this.categroyTwoVisible = val; this.categroyTwoVisible = val;
}, },
handleTabSelect(path) { handleTabSelect(value) {
this.$router.push({ path }); this.$router.push({ path: value });
}, },
onTabSelect({ value }) { onTabSelect({ value }) {
this.tabPath = value; this.tabPath = value;
@ -240,7 +269,7 @@ export default {
async getCategroyData() { async getCategroyData() {
const { result } = await ApiGetCategoryOneList(); const { result } = await ApiGetCategoryOneList();
if (result && result.length > 0) { if (result && result.length > 0) {
this.categrayData = await Promise.all( this.categroyData = await Promise.all(
result.map(async (item) => { result.map(async (item) => {
const { result: resultGoods } = await ApiGetCategoryTwoAndGoods({ const { result: resultGoods } = await ApiGetCategoryTwoAndGoods({
categoryId: item.id, categoryId: item.id,
@ -436,13 +465,14 @@ export default {
width: 190px; width: 190px;
padding: 15px 0; padding: 15px 0;
background: #ffffff; background: #ffffff;
.menu-left__item:hover {
color: #ff875b;
}
.menu-left__item { .menu-left__item {
height: 50px; height: 50px;
cursor: pointer; cursor: pointer;
padding: 0 24px 0 41px; padding: 0 24px 0 41px;
&:hover,
&--light {
color: #ff875b;
}
img { img {
width: 20px; width: 20px;
height: 20px; height: 20px;
@ -451,20 +481,29 @@ export default {
} }
} }
.tab-category-menu__right { .tab-category-menu__right {
width: 510px; padding: 15px 26px;
padding: 30px 26px;
box-shadow: 7px 0px 10px 1px rgba(0, 0, 0, 0.1); box-shadow: 7px 0px 10px 1px rgba(0, 0, 0, 0.1);
border: 1px solid #eeeeee; border: 1px solid #eeeeee;
background: #ffffff; background: #ffffff;
.menu-right__item:hover { .category-menu-right__wrap {
color: #ff875b; height: 50px;
} line-height: 50px;
.menu-right__item { padding: 0 16px;
display: inline-block;
font-size: 12px; font-size: 12px;
color: #999999; color: #999999;
margin-right: 20px; white-space: nowrap;
cursor: pointer; &:hover,
&--light {
background: #f8f8f8;
}
.menu-right-wrap__item {
color: #999999;
margin-right: 20px;
cursor: pointer;
&:hover {
color: #ff875b;
}
}
} }
} }
} }

2800
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -20,6 +20,7 @@
"core-js": "^3.19.3", "core-js": "^3.19.3",
"element-ui": "^2.15.8", "element-ui": "^2.15.8",
"js-util-all": "^1.0.6", "js-util-all": "^1.0.6",
"lodash": "^4.17.21",
"nuxt": "^2.15.8", "nuxt": "^2.15.8",
"vue": "^2.6.14", "vue": "^2.6.14",
"vue-server-renderer": "^2.6.14", "vue-server-renderer": "^2.6.14",

@ -48,7 +48,7 @@
<UiConfirm <UiConfirm
title="确认收到货了吗?" title="确认收到货了吗?"
:visible.sync="ensureOrderVisible" :visible.sync="ensureOrderVisible"
@ensure="handleOrderEnsure" @confirm="handleOrderEnsure"
/> />
</div> </div>
</template> </template>

@ -6,16 +6,35 @@
* @Description: file content * @Description: file content
--> -->
<template> <template>
<div> <div class="home">
<Banner /> <Banner />
<!-- <Seckil /> --> <div class="home-wrap">
<Seckil v-if="true || seckillData.activityTimeVO" :data="seckillData" />
</div>
</div> </div>
</template> </template>
<script> <script>
import { ApiGetHomeSeckill } from "@/plugins/api/seckill";
import Banner from "./module/Banner.vue"; import Banner from "./module/Banner.vue";
import Seckil from "./module/Seckill.vue"; import Seckil from "./module/Seckill.vue";
export default { export default {
components: { Banner, Seckil }, components: { Banner, Seckil },
async asyncData() {
//
const { result } = await ApiGetHomeSeckill();
return {
seckillData: result,
};
},
}; };
</script> </script>
<style lang="scss" scoped>
.home {
background: #f8f8f8;
.home-wrap {
@include layout-box;
padding: 30px 0 30px 0;
}
}
</style>

@ -7,18 +7,218 @@
--> -->
<template> <template>
<div>我是秒杀模块</div> <div class="home-sckill flex">
<div class="home-sckill-bar" :style="{ backgroundImage: `url(${bkgUrl})` }">
<strong class="home-sckill-title">限时秒杀</strong>
<div class="home-sckill-wrap">
<div class="home-sckill-wrap__tip">
<strong>10:00</strong>
<span>点场 距结束</span>
</div>
<div class="home-sckill-wrap__countdown flex flex-middle flex-center">
<div class="sckill-wrap-countdown__time">{{ countdown.hour }}</div>
<span class="sckill-wrap-countdown--mark">:</span>
<div class="sckill-wrap-countdown__time">{{ countdown.minute }}</div>
<span class="sckill-wrap-countdown--mark">:</span>
<div class="sckill-wrap-countdown__time">{{ countdown.second }}</div>
</div>
</div>
</div>
<div class="home-sckill-carousel">
<el-carousel
height="260px"
arrow="always"
:autoplay="false"
:loop="false"
>
<el-carousel-item v-for="(item, index) in goodsList" :key="index">
<div class="carousel-goods flex">
<div
v-for="(itemChild, indexChild) in item"
:key="itemChild.productId"
class="carousel-goods-box flex flex-middle"
>
<div
v-if="indexChild !== 0"
class="carousel-goods-box--line"
></div>
<div class="carousel-goods-box__item">
<img
class="goods-box-item__cover"
:src="itemChild.productMainPicture"
/>
<div class="goods-box-item__wrap">
<p class="box-item-wrap__title">
{{ itemChild.productName }}
</p>
<strong class="box-item-wrap__price"
>{{ itemChild.activityPrice }}</strong
>
</div>
</div>
</div>
</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
</template> </template>
<script> <script>
export default { import _ from "lodash";
name : 'Seckill',
data(){
return {
} const CAROUSEL_COUNT = 5; //
}
} export default {
name: "HomeSeckill",
props: {
data: {
type: Object,
default: () => ({}),
},
},
data() {
return {
bkgUrl: require("~/assets/img/sckill/bkg-small.png"),
goodsList: [],
};
},
watch: {
data: {
deep: true,
immediate: true,
handler(val) {
const { activityProductListVO: products } = val;
if (products && products.length > 0) {
this.getFormatData(products);
}
},
},
},
computed: {
countdown() {
return {
hour: "01",
minute: "32",
second: "09",
};
},
},
methods: {
//
getFormatData(list) {
const listCopy = _.cloneDeep(list);
const part = Math.ceil(listCopy.length / CAROUSEL_COUNT);
if (part === 1) {
this.goodsList = [listCopy.splice(0, CAROUSEL_COUNT)];
return;
}
for (let i = 0; i < part; i++) {
const goodsListItem = listCopy.splice(0, CAROUSEL_COUNT);
this.goodsList.push(goodsListItem);
}
},
},
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.home-sckill {
height: 260px;
.home-sckill-bar {
width: 190px;
height: 100%;
padding: 45px 0 28px 0;
text-align: center;
color: #ffffff;
background-size: 100% 100%;
.home-sckill-title {
display: block;
font-size: 28px;
margin-bottom: 90px;
}
.home-sckill-wrap {
margin-bottom: 10px;
.home-sckill-wrap__tip {
font-size: 0;
strong {
font-size: 18px;
}
span {
font-size: 14px;
}
}
.home-sckill-wrap__countdown {
margin-top: 10px;
.sckill-wrap-countdown__time {
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
color: #ffffff;
font-size: 18px;
font-weight: bold;
background: #2f3430;
}
.sckill-wrap-countdown--mark {
display: block;
font-size: 20px;
margin: 0 8px;
}
}
}
}
.home-sckill-carousel {
/deep/.el-carousel {
width: 1010px;
.el-carousel__container {
padding: 0 10px;
background: #ffffff;
.el-carousel__arrow {
i {
font-size: 14px;
font-weight: bold;
}
&--left {
left: -11px;
}
&--right {
right: -11px;
}
}
}
.carousel-goods {
height: 100%;
.carousel-goods-box {
.carousel-goods-box--line {
height: 160px;
width: 1px;
background: #eeeeee;
}
.carousel-goods-box__item {
width: 198px;
padding: 20px 24px;
.goods-box-item__cover {
width: 150px;
height: 150px;
object-fit: productMainPicture;
margin-bottom: 14px;
}
.goods-box-item__wrap {
font-size: 14px;
color: #333333;
text-align: center;
.box-item-wrap__title {
@include ellipsis;
margin-bottom: 8px;
}
.box-item-wrap__price {
color: #ff512b;
text-align: center;
}
}
}
}
}
}
}
}
</style> </style>
Loading…
Cancel
Save