Components文件夹
Carousel
<template>
<div class="swiper-container" id="floor1Swiper">
<div class="swiper-wrapper">
<div class="swiper-slide" v-for="(carousel, index) in list" :key="carousel.id" >
<img :src="carousel.imgUrl" />
</div>
</div>
<!-- 如果需要分页器 -->
<div class="swiper-pagination"></div>
<!-- 如果需要导航按钮 -->
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
</template>
<script>
import Swiper from "swiper";
export default {
name:"Carousel",
props: ["list"],
watch: {
list: {
immediate: true,
handler() {
this.$nextTick(() => {
var mySwiper = new Swiper(".swiper-container", {
loop: true,
pagination: {
el: ".swiper-pagination",
clickable: true,
},
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
scrollbar: {
el: ".swiper-scrollbar",
},
});
});
},
},
},
}
</script>
<style>
</style>
Footer
<template>
<!-- 底部 -->
<div class="footer">
<div class="footer-container">
<div class="footerList">
<div class="footerItem">
<h4>购物指南</h4>
<ul class="footerItemCon">
<li>购物流程</li>
<li>会员介绍</li>
<li>生活旅行/团购</li>
<li>常见问题</li>
<li>购物指南</li>
</ul>
</div>
<div class="footerItem">
<h4>配送方式</h4>
<ul class="footerItemCon">
<li>上门自提</li>
<li>211限时达</li>
<li>配送服务查询</li>
<li>配送费收取标准</li>
<li>海外配送</li>
</ul>
</div>
<div class="footerItem">
<h4>支付方式</h4>
<ul class="footerItemCon">
<li>货到付款</li>
<li>在线支付</li>
<li>分期付款</li>
<li>邮局汇款</li>
<li>公司转账</li>
</ul>
</div>
<div class="footerItem">
<h4>售后服务</h4>
<ul class="footerItemCon">
<li>售后政策</li>
<li>价格保护</li>
<li>退款说明</li>
<li>返修/退换货</li>
<li>取消订单</li>
</ul>
</div>
<div class="footerItem">
<h4>特色服务</h4>
<ul class="footerItemCon">
<li>夺宝岛</li>
<li>DIY装机</li>
<li>延保服务</li>
<li>尚品汇E卡</li>
<li>尚品汇通信</li>
</ul>
</div>
<div class="footerItem">
<h4>帮助中心</h4>
<img src="./images/wx_cz.jpg" alt="">
</div>
</div>
<div class="copyright">
<ul class="helpLink">
<li>
关于我们
<span class="space"></span>
</li>
<li>
联系我们
<span class="space"></span>
</li>
<li>
关于我们
<span class="space"></span>
</li>
<li>
商家入驻
<span class="space"></span>
</li>
<li>
营销中心
<span class="space"></span>
</li>
<li>
友情链接
<span class="space"></span>
</li>
<li>
关于我们
<span class="space"></span>
</li>
<li>
营销中心
<span class="space"></span>
</li>
<li>
友情链接
<span class="space"></span>
</li>
<li>关于我们</li>
</ul>
<p>地址:北京市昌平区宏福科技园综合楼6层</p>
<p>京ICP备19006430号</p>
</div>
</div>
</div>
</template>
<script>
export default {
name: "",
};
</script>
<style lang="less" scoped>
.footer {
background-color: #eaeaea;
.footer-container {
width: 1200px;
margin: 0 auto;
padding: 0 15px;
.footerList {
padding: 20px;
border-bottom: 1px solid #e4e1e1;
border-top: 1px solid #e4e1e1;
overflow: hidden;
padding-left: 40px;
.footerItem {
width: 16.6666667%;
float: left;
h4 {
font-size: 14px;
}
.footerItemCon {
li {
line-height: 18px;
}
}
&:last-child img {
width: 121px;
}
}
}
.copyright {
padding: 20px;
.helpLink {
text-align: center;
li {
display: inline;
.space {
border-left: 1px solid #666;
width: 1px;
height: 13px;
background: #666;
margin: 8px 10px;
}
}
}
p {
margin: 10px 0;
text-align: center;
}
}
}
}
</style>
Header
<template>
<header class="header">
<!-- 头部的第一行 -->
<div class="top">
<div class="container">
<div class="loginList">
<p>尚品汇欢迎您!</p>
<p v-if="!userName">
<span>请</span>
<!-- <a href="###">登录</a> -->
<!-- 编程式导航,务必有to属性 -->
<router-link to="/login">登录</router-link>
<router-link to="/register" class="register">免费注册</router-link>
</p>
<p v-else>
<a href="">{{ userName }}</a>
<a class="register" @click="logout">退出登录</a>
</p>
</div>
<div class="typeList">
<!-- <a href="###">我的订单</a> -->
<!-- <a href="###">我的购物车</a> -->
<router-link to="/center">我的订单</router-link>
<router-link to="/shopcart">我的购物车</router-link>
<a href="###">我的尚品汇</a>
<a href="###">尚品汇会员</a>
<a href="###">企业采购</a>
<a href="###">关注尚品汇</a>
<a href="###">合作招商</a>
<a href="###">商家后台</a>
</div>
</div>
</div>
<!--头部第二行 搜索区域-->
<div class="bottom">
<h1 class="logoArea">
<router-link class="logo" to="/home">
<img src="./images/logo.png" alt="" />
</router-link>
</h1>
<div class="searchArea">
<form action="###" class="searchForm">
<input
type="text"
id="autocomplete"
class="input-error input-xxlarge"
v-model="keyword"
/>
<button
class="sui-btn btn-xlarge btn-danger"
type="button"
@click="goSearch"
>
搜索
</button>
</form>
</div>
</div>
</header>
</template>
<script>
export default {
name: "",
data() {
return {
keyword: "",
};
},
mounted() {
this.$bus.$on("isclear", () => {
this.keyword = "";
});
},
methods: {
goSearch() {
let location = {
name: "search",
params: { keyword: this.keyword },
query: { k: this.keyword.toUpperCase() },
};
if (this.$route.query) {
location.query = this.$route.query;
}
this.$router.push(location);
},
async logout() {
try {
await this.$store.dispatch("userLogout");
this.$router.push("/home");
} catch (error) {
alert(error.message);
}
},
},
computed: {
userName() {
return this.$store.state.user.userInfo.loginName;
},
},
};
</script>
<style scoped lang="less">
.header {
& > .top {
background-color: #eaeaea;
height: 30px;
line-height: 30px;
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
.loginList {
float: left;
p {
float: left;
margin-right: 10px;
.register {
border-left: 1px solid #b3aeae;
padding: 0 5px;
margin-left: 5px;
}
}
}
.typeList {
float: right;
a {
padding: 0 10px;
& + a {
border-left: 1px solid #b3aeae;
}
}
}
}
}
& > .bottom {
width: 1200px;
margin: 0 auto;
overflow: hidden;
.logoArea {
float: left;
.logo {
img {
width: 175px;
margin: 25px 45px;
}
}
}
.searchArea {
float: right;
margin-top: 35px;
.searchForm {
overflow: hidden;
input {
box-sizing: border-box;
width: 490px;
height: 32px;
padding: 0px 4px;
border: 2px solid #ea4a36;
float: left;
&:focus {
outline: none;
}
}
button {
height: 32px;
width: 68px;
background-color: #ea4a36;
border: none;
color: #fff;
float: left;
cursor: pointer;
&:focus {
outline: none;
}
}
}
}
}
}
</style>
Pagination
<template>
<div class="pagination">
<button :disabled="pageNo==1" @click="$emit('getPageNo',pageNo-1)">上一页</button>
<button v-if="startNumAndendNum.start>1" @click="$emit('getPageNo',1)" :class="{active:pageNo==1}">1</button>
<button v-if="startNumAndendNum.start>2">···</button>
<button v-for="(page,index) in startNumAndendNum.end" :key="index" v-if="page>=startNumAndendNum.start" @click="$emit('getPageNo',page)" :class="{active:pageNo==page}">{{page}}</button>
<button v-if="startNumAndendNum.end<totalPage-1" >···</button>
<button v-if="startNumAndendNum.end<totalPage" @click="$emit('getPageNo',totalPage)" :class="{active:pageNo==totalPage}">{{ totalPage }}</button>
<button :disabled="pageNo==totalPage" @click="$emit('getPageNo',pageNo+1)">下一页</button>
<button style="margin-left: 30px">共 {{ total }}条</button>
</div>
</template>
<script>
export default {
name: "Pagination",
props: ["pageNo", "pageSize", "total", "continues"],
computed: {
totalPage() {
return Math.ceil(this.total / this.pageSize);
},
startNumAndendNum() {
const {totalPage , continues} = this;
let start = 0,
end = 0;
if (totalPage < continues) {
start = 1;
end = totalPage;
}
else {
start = this.pageNo - parseInt(continues / 2);
end = this.pageNo + parseInt(continues / 2);
if (start <= 0) {
start = 1;
end = continues;
}
if (end > totalPage) {
end = totalPage;
start = totalPage - continues + 1;
}
}
return { start, end };
},
},
};
</script>
<style lang="less" scoped>
.pagination {
button {
margin: 0 5px;
background-color: #f4f4f5;
color: #606266;
outline: none;
border-radius: 2px;
padding: 0 4px;
vertical-align: top;
display: inline-block;
font-size: 13px;
min-width: 35.5px;
height: 28px;
line-height: 28px;
cursor: pointer;
box-sizing: border-box;
text-align: center;
border: 0;
&[disabled] {
color: #c0c4cc;
cursor: not-allowed;
}
&.active {
cursor: not-allowed;
background-color: #409eff;
color: #fff;
}
}
}
</style>
TypeNav
<template>
<!-- 商品分类导航 -->
<div class="type-nav">
<div class="container">
<div @mouseleave="leaveIndex" @mouseenter="changeShow">
<h2 class="all">全部商品分类</h2>
<transition name="sort">
<div class="sort" v-show="show">
<div class="all-sort-list2" @click="goSearch">
<div
class="item"
v-for="(c1, index) in categoryList"
:key="c1.categoryId"
>
<h3
@mouseenter="changeIndex(index)"
:class="{ cur: currentIndex === index }"
>
<a
:data-categoryName="c1.categoryName"
:data-category1id="c1.categoryId"
>{{ c1.categoryName }}--{{ index }}</a
>
</h3>
<!-- 二三级分类 -->
<div
class="item-list"
clearfix
:style="{
display: currentIndex === index ? 'block' : 'none',
}"
>
<div
class="subitem"
v-for="(c2, index) in c1.categoryChild"
:key="c2.categoryId"
>
<dl class="fore">
<dt>
<a
:data-categoryName="c2.categoryName"
:data-category2id="c2.categoryId"
>{{ c2.categoryName }}</a
>
</dt>
<dd>
<em
v-for="(c3, index) in c2.categoryChild"
:key="c3.categoryId"
>
<a
:data-categoryName="c3.categoryName"
:data-category3id="c3.categoryId"
>{{ c3.categoryName }}</a
>
</em>
</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
</transition>
</div>
<nav class="nav">
<a href="###">服装城</a>
<a href="###">美妆馆</a>
<a href="###">尚品汇超市</a>
<a href="###">全球购</a>
<a href="###">闪购</a>
<a href="###">团购</a>
<a href="###">有趣</a>
<a href="###">秒杀</a>
</nav>
</div>
</div>
</template>
<script>
import { mapState } from "vuex";
import throttle from "lodash/throttle";
export default {
name: "TypeNav",
data() {
return {
currentIndex: "-1",
show: true,
};
},
mounted() {
if (this.$route.path != "/home") {
this.show = false;
}
},
methods: {
changeIndex: throttle(function (index) {
this.currentIndex = index;
}, 50),
leaveIndex() {
this.currentIndex = -1;
if (this.$route.path != "/home") {
this.show = false;
}
},
goSearch(event) {
let { categoryname, category1id, category2id, category3id } =
event.target.dataset;
if (categoryname) {
let location = { name: "search" };
let query = { categoryName: categoryname };
if (category1id) {
query.category1id = category1id;
} else if (category2id) {
query.category2id = category2id;
} else {
query.category3id = category3id;
}
if (this.$route.params) {
location.params = this.$route.params;
}
location.query = query;
this.$router.push(location);
}
},
changeShow() {
if (this.$route.path != "/home") {
this.show = true;
}
},
},
computed: {
...mapState({
categoryList: (state) => {
return state.home.categoryList;
},
}),
},
};
</script>
<style scoped lang="less">
.type-nav {
border-bottom: 2px solid #e1251b;
.container {
width: 1200px;
margin: 0 auto;
display: flex;
position: relative;
.all {
width: 210px;
height: 45px;
background-color: #e1251b;
line-height: 45px;
text-align: center;
color: #fff;
font-size: 14px;
font-weight: bold;
}
.nav {
a {
height: 45px;
margin: 0 22px;
line-height: 45px;
font-size: 16px;
color: #333;
}
}
.sort {
position: absolute;
left: 0;
top: 45px;
width: 210px;
height: 461px;
position: absolute;
background: #fafafa;
z-index: 999;
.all-sort-list2 {
.item {
h3 {
line-height: 30px;
font-size: 14px;
font-weight: 400;
overflow: hidden;
padding: 0 20px;
margin: 0;
a {
color: #333;
}
}
.item-list {
position: absolute;
width: 734px;
min-height: 460px;
background: #f7f7f7;
left: 210px;
border: 1px solid #ddd;
top: 0;
z-index: 9999 !important;
.subitem {
float: left;
width: 650px;
padding: 0 4px 0 8px;
dl {
border-top: 1px solid #eee;
padding: 6px 0;
overflow: hidden;
zoom: 1;
&.fore {
border-top: 0;
}
dt {
float: left;
width: 54px;
line-height: 22px;
text-align: right;
padding: 3px 6px 0 0;
font-weight: 700;
}
dd {
float: left;
width: 415px;
padding: 3px 0 0;
overflow: hidden;
em {
float: left;
height: 14px;
line-height: 14px;
padding: 0 8px;
margin-top: 5px;
border-left: 1px solid #ccc;
}
}
}
}
}
&:hover {
.item-list {
}
}
}
.cur {
background-color: skyblue;
}
}
}
.sort-enter {
height: 0px;
}
.sort-enter-to {
height: 461px;
}
.sort-enter-active {
transition: all 0.5s linear;
}
}
}
</style>
Plugin文件夹
myPlugins.js
let myPlugins={};
myPlugins.install=function(Vue,options){
Vue.directive(options.name,(element,params)=>{
element.innnerHTML=params.value.toUpperCase();
})
}
export default myPlugins
validate.js
import Vue from "vue"
import VeeValidate from "vee-validate"
import zh_CN from 'vee-validate/dist/locale/zh_CN'
Vue.use(VeeValidate);
VeeValidate.Validator.localize('zh_CN', {
messages: {
...zh_CN.messages,
is: (field) => `${field}必须与密码相同`
},
attributes: {
phone: '手机号',
code: '验证码',
password: '密码',
password1: '确认密码',
isCheck: '协议',
ageree:"协议",
}
})
VeeValidate.Validator.extend('tongyi', {
validate: value => {
return value
},
getMessage: field => field + '必须同意'
})
utils文件夹
token.js
export const setToken=(token)=>{
localStorage.setItem("TOKEN",token)
}
export const getToken=()=>{
return localStorage.getItem("TOKEN")
}
export const removeToken=()=>{
localStorage.removeItem("TOKEN")
}
uuid_token.js
import { v4 as uuidV4 } from "uuid"
export const getUUID = () => {
let uuid_token = localStorage.getItem("UUIDTOKEN");
if (!uuid_token) {
uuid_token = uuidV4();
localStorage.setItem("UUIDTOKEN", uuid_token);
}
return uuid_token;
}
router文件夹
index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import routes from "./routes"
import store from "@/store"
let originPush = VueRouter.prototype.push;
let originReplace = VueRouter.prototype.replace;
VueRouter.prototype.push = function (location, resolve, reject) {
if (resolve && reject) {
originPush.call(this, location, resolve, reject)
}
else {
originPush.call(this, location, () => { }, () => { })
}
}
VueRouter.prototype.replace = function (location, resolve, reject) {
if (resolve && reject) {
originReplace.call(this, location, resolve, reject)
}
else {
originReplace.call(this, location, () => { }, () => { })
}
}
let router = new VueRouter({
routes,
scrollBehavior(to, from, savedPosition) {
return { y: 0 }
},
})
router.beforeEach(async (to, from, next) => {
next()
let token = store.state.user.token;
let name = store.state.user.userInfo.name;
if (token) {
if (to.path == "/login") {
next("/home")
} else {
if (name) {
next();
} else {
try {
await store.dispatch("getUserInfo");
next()
} catch (error) {
await store.dispatch("userLogout")
next("/login");
}
}
}
} else {
let toPath = to.path;
if (toPath.indexOf("/center") != -1 || toPath.indexOf("/trade") != -1 || toPath.indexOf("/pay") != -1|| toPath.indexOf("/shopcart") != -1) {
next('/login?redirect='+toPath)
}
else {
next();
}
}
})
export default router;
routes.js
import Search from "@/pages/Search"
import Login from "@/pages/Login"
import Register from "@/pages/Register"
import AddCartSuccess from "@/pages/AddCartSuccess"
import ShopCart from "@/pages/ShopCart"
import Trade from "@/pages/Trade"
import Pay from "@/pages/Pay"
import PaySuccess from "@/pages/PaySuccess"
import Center from "@/pages/Center"
import myOrder from "@/pages/Center/myOrder"
import groupOrder from "@/pages/Center/groupOrder"
const Foo = () =>{
console.log("我是Detail组件")
return import("@/pages/Detail")
}
export default [
{
path: "/home",
component: () => import('@/pages/Home'),
meta: { show: true }
},
{
path: "/search/:keyword?",
component: Search ,
meta: { show: true },
name: "search",
},
{
path: "/login",
component: Login,
meta: { show: false }
},
{
path: "/register",
component: Register,
meta: { show: false }
},
{
path: '*',
redirect: "/home"
},
{
path: "/detail/:id",
component: Foo,
name: "detail",
meta: { show: false }
},
{
path: "/addcartsuccess",
name: "addcartsuccess",
component: AddCartSuccess,
meta: { show: true }
},
{
path: "/shopcart",
component: ShopCart,
name: "shopcart",
meta: { show: true }
},
{
path: "/trade",
component: Trade,
name: "trade",
beforeEnter: (to, from, next) => {
if (from.path == "/shopcart") {
next();
} else {
next(false);
}
}
},
{
path: "/pay",
component: Pay,
name: "pay",
beforeEnter: (to, from, next)=>{
if (from.path == "/trade") {
next()
}
else {
next(false);
}
}
},
{
path: "/paysuccess",
component: PaySuccess,
name: "paysuccess",
},
{
path: "/center",
component: Center,
name: "center",
children: [
{
path: "myorder",
component: myOrder,
},
{
path: "grouporder",
component: groupOrder,
},
{
path: "/center",
redirect: "/center/myorder"
}
],
},
]