feat:完成立即购买

merge-requests/8/head
张征 2 years ago
commit 09bae8e4df

@ -101,6 +101,27 @@ export {
@include adj(transform, translate3d(-50%, 0, 0)); @include adj(transform, translate3d(-50%, 0, 0));
} }
```
## 登录相关
```javascript
// 访问token
this.$store.state.token
// 设置token
this.$store.commit('setToken')
// 退出登录
this.$store.commit('setLoginOut')
// 获取登录的用户信息
this.$store.state.userInfo
// 登录拦截
// 示例:点击购买课程前需要判断当前用户是否登录
function onPurchaseCourse() {
if (!this.$isLoginValidate()) {
return;
}
// 此处省略其他业务代码...
}
``` ```

@ -147,7 +147,11 @@ export default {
Message.error("请勾选同意《用户协议》和《隐私协议》"); Message.error("请勾选同意《用户协议》和《隐私协议》");
return; return;
} }
const { result } = await ApiPostLogin({ ...this.form }); const { result } = await ApiPostLogin({
...this.form,
clientId: 1,
systemId: 1,
});
if (result) { if (result) {
this.dialogTableVisible = false; this.dialogTableVisible = false;
this.$store.commit("setToken", result.token); this.$store.commit("setToken", result.token);

@ -8,25 +8,69 @@
<template> <template>
<div class="layout"> <div class="layout">
<BsLogin :visible.sync="loginVisible" /> <BsLogin :visible.sync="loginVisible" />
<!-- <Header /> --> <Header
:is-categroy-open="categroyOption.open"
:show-categroy-tab="categroyOption.show"
:is-sticky="isSticky"
/>
<Nuxt /> <Nuxt />
<Footer /> <Footer />
</div> </div>
</template> </template>
<script> <script>
import BsLogin from "@/components/BsLogin.vue"; import BsLogin from "@/components/BsLogin.vue";
import { TOKEN_KEY } from "@/constants";
import Header from "./module/header/index.vue"; import Header from "./module/header/index.vue";
import Footer from "./module/footer/index.vue"; import Footer from "./module/footer/index.vue";
const CATEGROY_OPEN_PAGES = ["/"]; // tab
const CATEGROY_HIDE_PAGES = ["/account"]; // tab
export default { export default {
name: "Layout", name: "Layout",
components: { Header, Footer, BsLogin }, components: { Header, Footer, BsLogin },
data() { data() {
return { return {
loginVisible: true, isSticky: false,
ticking: false,
}; };
}, },
computed: {
loginVisible: {
get() {
return this.$store.state.loginVisible;
},
set(val) {
this.$store.commit("setLoginVisible", val);
},
},
categroyOption() {
//
const currentPath = this.$route.path;
return {
open: CATEGROY_OPEN_PAGES.includes(currentPath),
show: !CATEGROY_HIDE_PAGES.includes(currentPath),
};
},
},
mounted() {
//
window.addEventListener("scroll", this.scrollEventMethod);
},
destroyed() {
window.removeEventListener("scroll", this.scrollEventMethod);
},
methods: {
scrollEventMethod(e) {
const that = this;
//
if (!that.ticking) {
window.requestAnimationFrame(function () {
that.ticking = false;
that.isSticky = window.scrollY > 300;
});
that.ticking = true;
}
},
},
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

@ -6,33 +6,42 @@
<div class="header-wrap__logo">马士兵严选欢迎你</div> <div class="header-wrap__logo">马士兵严选欢迎你</div>
<div class="header-wrap__content flex flex-middle"> <div class="header-wrap__content flex flex-middle">
<div class="header-wrap-content__login"> <div class="header-wrap-content__login">
<el-dropdown v-if="isLogin" @visible-change="menuVisible = $event"> <!-- 已登录 -->
<el-dropdown
v-if="token"
@visible-change="menuVisible = $event"
@command="handleCommandClick"
>
<div <div
class="wrap-content-login__info flex flex-middle flex-center" class="wrap-content-login__info flex flex-middle flex-center"
:class="{ 'wrap-content-login__info--hover': menuVisible }" :class="{ 'wrap-content-login__info--hover': menuVisible }"
> >
<span>你好{{ userInfo.name }}</span> <span>你好{{ userInfo.nickname }}</span>
<img class="content-login-info__logo" :src="menuIcon" /> <img class="content-login-info__logo" :src="menuIcon" />
</div> </div>
<el-dropdown-menu slot="dropdown" class="dropdown-menu-self"> <el-dropdown-menu slot="dropdown" class="dropdown-menu-self">
<div class="menu-item__wrap flex flex-middle"> <div class="menu-item__wrap flex flex-middle">
<img class="menu-item-wrap__avatar" /> <img class="menu-item-wrap__avatar" :src="userInfo.avatar" />
<span>{{ userInfo.name }}</span> <span>{{ userInfo.nickname }}</span>
</div> </div>
<div class="menu-item__line"></div> <div class="menu-item__line"></div>
<el-dropdown-item <el-dropdown-item
class="flex flex-between flex-middle" class="flex flex-between flex-middle"
v-for="item in menuList" v-for="item in menuList"
:key="item.value" :key="item.value"
:command="item.value"
> >
<span> {{ item.label }}</span> <span> {{ item.label }}</span>
<img src="@/static/images/layout/icon-arrow.png" /> <img src="@/static/images/layout/icon-arrow.png" />
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
<!-- 未登录 -->
<div v-else class="wrap-content-login__text flex"> <div v-else class="wrap-content-login__text flex">
<span>请先</span> <span>请先</span>
<span class="content-login-text--light">登录/注册</span> <span class="content-login-text--light" @click="onLoginClick">
登录/注册
</span>
</div> </div>
</div> </div>
<template> <template>
@ -58,6 +67,7 @@
</div> </div>
</template> </template>
<script> <script>
import { mapState } from "vuex";
const MENU_VALUE = { const MENU_VALUE = {
PERSONAL: 1, PERSONAL: 1,
ADDRESS: 2, ADDRESS: 2,
@ -65,12 +75,9 @@ const MENU_VALUE = {
}; };
export default { export default {
name: "HeaderInfoBar",
data() { data() {
return { return {
isLogin: true,
userInfo: {
name: "仙女广",
},
menuVisible: false, menuVisible: false,
menuList: [ menuList: [
{ {
@ -89,6 +96,7 @@ export default {
}; };
}, },
computed: { computed: {
...mapState(["userInfo", "token"]),
menuIcon() { menuIcon() {
return this.menuVisible return this.menuVisible
? require("@/static/images/layout/icon-up-light.png") ? require("@/static/images/layout/icon-up-light.png")
@ -96,7 +104,21 @@ export default {
}, },
}, },
methods: { methods: {
onMenuClick() {}, onLoginClick() {
this.$isLoginValidate();
},
handleCommandClick(event) {
switch (event) {
case MENU_VALUE.PERSONAL:
this.$router.push('/account');
break;
case MENU_VALUE.ADDRESS:
this.$router.push('/account');
break;
case MENU_VALUE.LOGON_OUT:
this.$store.commit("setLoginOut");
}
},
}, },
}; };
</script> </script>

@ -1,5 +1,5 @@
<template> <template>
<div class="layout-header" :class="{ 'layout-sticky-bar-header': isSticky }"> <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">
@ -9,7 +9,7 @@
src="@/static/images/layout/logo-sticky.png" src="@/static/images/layout/logo-sticky.png"
/> />
<el-menu <el-menu
:default-active="tabIndex" :default-active="tabPath"
mode="horizontal" mode="horizontal"
@select="handleTabSelect" @select="handleTabSelect"
> >
@ -27,12 +27,21 @@
<img src="@/static/images/layout/icon-shop-sticky.png" /> <img src="@/static/images/layout/icon-shop-sticky.png" />
<span class="">3</span> <span class="">3</span>
</div> </div>
<div class="header-wrap-icons__login">登录</div> <div v-if="token" class="header-wrap-icons__login">
<img :src="userInfo.avatar" />
</div>
<div
v-else
class="header-wrap-icons__unlogin"
@click="onLoginClick"
>
登录
</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<template v-else> <template>
<HeaderInfoBar /> <HeaderInfoBar />
<div class="default-bar-header"> <div class="default-bar-header">
<div class="bar-header-box"> <div class="bar-header-box">
@ -61,29 +70,38 @@
</div> </div>
</div> </div>
<div class="bar-header-box__tab flex flex-middle"> <div class="bar-header-box__tab flex flex-middle">
<div class="header-box-tab__category"> <div
v-show="showCategroyTab"
class="header-box-tab__category"
@mouseenter="handleCategoryChange(true)"
@mouseleave="handleCategoryChange(false)"
>
<div class="tab-category__label flex flex-center flex-middle"> <div class="tab-category__label flex flex-center flex-middle">
<img src="@/static/images/layout/icon-category.png" /> <img src="@/static/images/layout/icon-category.png" />
<span>热门分类</span> <span>热门分类</span>
</div> </div>
<div class="tab-category__menu flex flex-left"> <div
v-show="isCategroyOpen || categroyVisible"
class="tab-category__menu flex flex-left"
@mouseenter="handleCategoryTwoChange(true)"
@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 categrayData"
:key="item.id" :key="item.id"
@mouseenter="handleMouEnter(item.id)" @mouseenter="handleCategoSelect(item.id)"
@mouseleave="categrayHoverVisible = false"
class="menu-left__item flex flex-middle" class="menu-left__item flex flex-middle"
> >
<img /> <img />
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
</div> </div>
</div> </div>
<!-- 右侧二级分类 -->
<div <div
v-show="categrayHoverVisible" v-show="categroyTwoVisible"
class="tab-category-menu__right flex flex-wrap" class="tab-category-menu__right"
@mouseenter="categrayHoverVisible = true"
@mouseleave="categrayHoverVisible = false"
> >
<div <div
v-for="item in currentCategrayList" v-for="item in currentCategrayList"
@ -101,9 +119,9 @@
class="header-box-tab__common flex flex-center flex-middle" class="header-box-tab__common flex flex-center flex-middle"
:class="{ :class="{
'header-box-tab__common--light': 'header-box-tab__common--light':
item.path === $nuxt.$route.path, item.value === $nuxt.$route.path,
}" }"
:to="item.path" :to="item.value"
> >
{{ item.label }} {{ item.label }}
</nuxt-link> </nuxt-link>
@ -114,43 +132,53 @@
</div> </div>
</template> </template>
<script> <script>
import { mapState } from "vuex";
import HeaderInfoBar from "./HeaderInfoBar.vue"; import HeaderInfoBar from "./HeaderInfoBar.vue";
import { import {
ApiGetCategoryOneList, ApiGetCategoryOneList,
ApiGetCategoryTwoAndGoods, ApiGetCategoryTwoAndGoods,
} from "@/plugins/api/goods"; } from "@/plugins/api/goods";
const TAB_TYPE = {
HOME: 1,
RECOMMEND: 2,
BOOK: 3,
TIME_LIMIT: 4,
};
export default { export default {
name: "DefaultHeader", name: "DefaultHeader",
components: { HeaderInfoBar }, components: { HeaderInfoBar },
props: { props: {
//
isSticky: { isSticky: {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
//
isCategroyOpen: {
type: Boolean,
default: false,
},
// tab
showCategroyTab: {
type: Boolean,
default: true,
},
}, },
data() { data() {
return { return {
searchContent: "", searchContent: "",
tabIndex: TAB_TYPE.HOME, tabPath: "/",
tabList: [ tabList: [
{ label: "首页", value: TAB_TYPE.HOME, path: "/" }, { label: "首页", value: "/" },
{ label: "爆款推荐", value: TAB_TYPE.RECOMMEND, path: "/hot" }, { label: "爆款推荐", value: "/hot" },
{ label: "开发书籍", value: TAB_TYPE.BOOK, path: "/book" }, { label: "开发书籍", value: "/book" },
{ label: "限时秒杀", value: TAB_TYPE.TIME_LIMIT, path: "/skill" }, { label: "限时秒杀", value: "/skill" },
], ],
categrayHoverVisible: false, categroyTwoVisible: false, //
currentCategrayId: 0, categroyVisible: false, //
currentCategrayId: 0, // id
categrayData: [], categrayData: [],
}; };
}, },
computed: { computed: {
...mapState(["userInfo", "token"]),
currentCategrayList() { currentCategrayList() {
const data = this.categrayData.find(({ id }) => { const data = this.categrayData.find(({ id }) => {
return this.currentCategrayId === id; return this.currentCategrayId === id;
@ -162,10 +190,24 @@ export default {
this.getCategroyData(); this.getCategroyData();
}, },
methods: { methods: {
handleMouEnter(id) { onLoginClick() {
this.categrayHoverVisible = true; this.$isLoginValidate();
},
//
handleCategoSelect(id) {
this.currentCategrayId = id; this.currentCategrayId = id;
}, },
//
handleCategoryChange(val) {
this.categroyVisible = val;
},
//
handleCategoryTwoChange(val) {
this.categroyTwoVisible = val;
},
handleTabSelect(path) {
this.$router.push({ path });
},
// //
async getCategroyData() { async getCategroyData() {
const { result } = await ApiGetCategoryOneList(); const { result } = await ApiGetCategoryOneList();
@ -178,7 +220,7 @@ export default {
if (resultGoods && resultGoods.length > 0) { if (resultGoods && resultGoods.length > 0) {
return { return {
...item, ...item,
list: resultGoods.data, list: resultGoods,
}; };
} }
}) })
@ -189,15 +231,15 @@ export default {
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.layout-sticky-bar-header {
height: 50px;
}
.sticky-bar-header { .sticky-bar-header {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 50px; height: 50px;
z-index: 10;
background: #ffffff;
box-shadow: 0px 4px 10px 1px rgba(0, 0, 0, 0.10000000149011612);
.sticky-bar-header__wrap { .sticky-bar-header__wrap {
width: 1200px; width: 1200px;
height: 100%; height: 100%;
@ -210,15 +252,12 @@ export default {
.bar-header-wrap__icons { .bar-header-wrap__icons {
cursor: pointer; cursor: pointer;
img { img {
width: 23px; width: 24px;
height: 23px; height: 24px;
} }
.header-wrap-icons__shop { .header-wrap-icons__shop {
position: relative; position: relative;
margin: 0 30px 0 14px; margin-left: 14px;
img {
width: 30px;
}
span { span {
position: absolute; position: absolute;
right: -6px; right: -6px;
@ -235,8 +274,17 @@ export default {
} }
} }
.header-wrap-icons__login { .header-wrap-icons__login {
margin-left: 47px;
img {
width: 34px;
height: 34px;
border-radius: 50%;
}
}
.header-wrap-icons__unlogin {
font-size: 16px; font-size: 16px;
color: #909399; color: #909399;
margin-left: 30px;
} }
} }
/deep/ .el-menu { /deep/ .el-menu {
@ -291,6 +339,7 @@ export default {
background: linear-gradient(270deg, #ffa25a 0%, #ff7f39 100%); background: linear-gradient(270deg, #ffa25a 0%, #ff7f39 100%);
border-radius: 0px 8px 8px 0px; border-radius: 0px 8px 8px 0px;
z-index: 2; z-index: 2;
cursor: pointer;
img { img {
width: 26px; width: 26px;
height: 26px; height: 26px;
@ -373,13 +422,11 @@ export default {
box-shadow: 7px 0px 10px 1px rgba(0, 0, 0, 0.10000000149011612); box-shadow: 7px 0px 10px 1px rgba(0, 0, 0, 0.10000000149011612);
border: 1px solid #eeeeee; border: 1px solid #eeeeee;
background: #ffffff; background: #ffffff;
.menu-right__item:last-child {
margin-bottom: 0;
}
.menu-right__item:hover { .menu-right__item:hover {
color: #ff875b; color: #ff875b;
} }
.menu-right__item { .menu-right__item {
display: inline-block;
font-size: 12px; font-size: 12px;
color: #999999; color: #999999;
margin-right: 20px; margin-right: 20px;

@ -42,7 +42,8 @@ export default {
plugins: [ plugins: [
'@/plugins/element-ui', '@/plugins/element-ui',
'@/plugins/axios', '@/plugins/axios',
'@plugins/axiosTk.js' '@plugins/axiosTk.js',
'@plugins/vue-inject.js'
], ],
// Auto import components: https://go.nuxtjs.dev/config-components // Auto import components: https://go.nuxtjs.dev/config-components

@ -0,0 +1,17 @@
import Vue from "vue";
import { TOKEN_KEY } from "@/constants";
const injectOptions = {
// 是否需要登录拦截
$isLoginValidate() {
if (this.$cookies.get(TOKEN_KEY)) {
return true;
}
this.$store.commit("setLoginVisible", true);
return false;
},
};
for (let key in injectOptions) {
Vue.prototype[key] = injectOptions[key];
}

@ -12,6 +12,7 @@ const ONE_DAY = 86400000; // 一天的毫秒数 24 * 60 * 60 * 1000;
const state = () => ({ const state = () => ({
token: "", token: "",
userInfo: {}, userInfo: {},
loginVisible: false, // 是否展示登录弹窗
}); });
const mutations = { const mutations = {
setUserInfo(state, info) { setUserInfo(state, info) {
@ -29,6 +30,9 @@ const mutations = {
state.userInfo = {}; state.userInfo = {};
this.$cookies.remove(TOKEN_KEY); this.$cookies.remove(TOKEN_KEY);
}, },
setLoginVisible(state, visible) {
state.loginVisible = visible;
},
}; };
const actions = { const actions = {
async nuxtServerInit({ state, commit, dispatch }) { async nuxtServerInit({ state, commit, dispatch }) {
@ -44,10 +48,6 @@ const actions = {
commit("setUserInfo", result); commit("setUserInfo", result);
} }
}, },
loginOut({ commit }) {
commit("setLoginOut");
// 此处请求接口
},
}; };
export { state, mutations, actions }; export { state, mutations, actions };

Loading…
Cancel
Save