19 changed files with 2178 additions and 94 deletions
@ -1,32 +1,99 @@ |
|||||
<template> |
<template> |
||||
<div id="app"> |
<div id="app"> |
||||
<nav> |
<!-- 加载中状态 --> |
||||
<router-link to="/">Home</router-link> | |
<!-- <el-loading |
||||
<router-link to="/about">About</router-link> |
v-if="loading" |
||||
</nav> |
fullscreen |
||||
<router-view/> |
text="加载中..." |
||||
|
background="rgba(255, 255, 255, 0.7)" |
||||
|
></el-loading> --> |
||||
|
|
||||
|
<!-- 顶部导航 --> |
||||
|
<HeaderNav v-if="$route.name !== 'Login' && $route.name !== 'Register'" /> |
||||
|
|
||||
|
<!-- 主内容区 --> |
||||
|
<main class="main-container"> |
||||
|
<router-view /> |
||||
|
</main> |
||||
|
|
||||
|
<!-- 页脚 --> |
||||
|
<Footer v-if="$route.name !== 'Login' && $route.name !== 'Register'" /> |
||||
|
|
||||
|
<!-- 回到顶部按钮 --> |
||||
|
<BackToTop /> |
||||
</div> |
</div> |
||||
</template> |
</template> |
||||
|
|
||||
|
<script> |
||||
|
import HeaderNav from "./components/layout/HeaderNav"; |
||||
|
import Footer from './components/layout/Footer' |
||||
|
import BackToTop from "./components/common/BackToTop"; |
||||
|
import { mapGetters } from "vuex"; |
||||
|
|
||||
|
export default { |
||||
|
name: "App", |
||||
|
components: { |
||||
|
HeaderNav, |
||||
|
Footer, |
||||
|
BackToTop, |
||||
|
}, |
||||
|
computed: { |
||||
|
...mapGetters(["getLoadingStatus"]), |
||||
|
loading() { |
||||
|
return this.getLoadingStatus; |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
<style lang="scss"> |
<style lang="scss"> |
||||
|
// 全局样式 |
||||
#app { |
#app { |
||||
font-family: Avenir, Helvetica, Arial, sans-serif; |
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", |
||||
|
"Microsoft YaHei", "微软雅黑", Arial, sans-serif; |
||||
-webkit-font-smoothing: antialiased; |
-webkit-font-smoothing: antialiased; |
||||
-moz-osx-font-smoothing: grayscale; |
-moz-osx-font-smoothing: grayscale; |
||||
text-align: center; |
color: #333; |
||||
color: #2c3e50; |
min-height: 100vh; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
} |
} |
||||
|
|
||||
nav { |
.main-container { |
||||
padding: 30px; |
flex: 1; |
||||
|
width: 100%; |
||||
|
max-width: 1200px; |
||||
|
margin: 0 auto; |
||||
|
padding: 20px; |
||||
|
box-sizing: border-box; |
||||
|
|
||||
a { |
// 响应式调整 |
||||
font-weight: bold; |
@media (max-width: 1200px) { |
||||
color: #2c3e50; |
padding: 15px; |
||||
|
} |
||||
|
|
||||
&.router-link-exact-active { |
@media (max-width: 768px) { |
||||
color: #42b983; |
padding: 10px; |
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|
||||
|
// 全局样式重置 |
||||
|
* { |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
|
||||
|
a { |
||||
|
text-decoration: none; |
||||
|
color: inherit; |
||||
|
} |
||||
|
|
||||
|
ul { |
||||
|
list-style: none; |
||||
|
} |
||||
|
|
||||
|
img { |
||||
|
vertical-align: middle; |
||||
|
} |
||||
</style> |
</style> |
||||
|
@ -0,0 +1,49 @@ |
|||||
|
body, html { |
||||
|
padding: 0; |
||||
|
margin: 0; |
||||
|
} |
||||
|
|
||||
|
div { |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
|
||||
|
/*单行隐藏*/ |
||||
|
.text-overflow { |
||||
|
overflow-x: hidden; |
||||
|
overflow-y: inherit; |
||||
|
text-overflow: ellipsis; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
/*两行隐藏,其他行设置-webkit-line-clamp:n */ |
||||
|
.text-overflowRows { |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
-webkit-line-clamp: 2; |
||||
|
word-break: break-all; |
||||
|
display: -webkit-box; |
||||
|
-webkit-box-orient: vertical; |
||||
|
} |
||||
|
|
||||
|
.flex-between { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.flex-center { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.flex-around { |
||||
|
display: flex; |
||||
|
justify-content: space-around; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.flex-column { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
@ -0,0 +1,72 @@ |
|||||
|
<template> |
||||
|
<div |
||||
|
class="back-to-top" |
||||
|
v-if="showBackToTop" |
||||
|
@click="scrollToTop" |
||||
|
> |
||||
|
<el-icon name="el-icon-arrow-up"></el-icon> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'BackToTop', |
||||
|
data() { |
||||
|
return { |
||||
|
showBackToTop: false |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
window.addEventListener('scroll', this.handleScroll) |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
window.removeEventListener('scroll', this.handleScroll) |
||||
|
}, |
||||
|
methods: { |
||||
|
handleScroll() { |
||||
|
// 当滚动超过500px时显示回到顶部按钮 |
||||
|
this.showBackToTop = window.pageYOffset > 500 |
||||
|
}, |
||||
|
scrollToTop() { |
||||
|
// 平滑滚动到顶部 |
||||
|
const scrollToTop = window.setInterval(() => { |
||||
|
const position = window.pageYOffset |
||||
|
if (position > 0) { |
||||
|
window.scrollTo(0, position - Math.max(20, position / 10)) |
||||
|
} else { |
||||
|
window.clearInterval(scrollToTop) |
||||
|
} |
||||
|
}, 16) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.back-to-top { |
||||
|
position: fixed; |
||||
|
bottom: 30px; |
||||
|
right: 30px; |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
background-color: #409eff; |
||||
|
color: white; |
||||
|
border-radius: 50%; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
cursor: pointer; |
||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); |
||||
|
transition: all 0.3s ease; |
||||
|
z-index: 1000; |
||||
|
|
||||
|
&:hover { |
||||
|
background-color: #337ab7; |
||||
|
transform: translateY(-3px); |
||||
|
} |
||||
|
|
||||
|
.el-icon-arrow-up { |
||||
|
font-size: 20px; |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,149 @@ |
|||||
|
<template> |
||||
|
<div class="footer-container"> |
||||
|
<div class="footer-top"> |
||||
|
<div class="footer-column"> |
||||
|
<h3>关于我们</h3> |
||||
|
<ul> |
||||
|
<li>平台简介</li> |
||||
|
<li>政策文件</li> |
||||
|
<li>平台标识指南</li> |
||||
|
<li>营业执照</li> |
||||
|
<li>食品经营许可证</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
<div class="footer-column"> |
||||
|
<h3>新手上路</h3> |
||||
|
<ul> |
||||
|
<li>供应商入驻流程</li> |
||||
|
<li>供应商操作手册</li> |
||||
|
<li>采购人入驻流程</li> |
||||
|
<li>采购人操作手册</li> |
||||
|
<li>供应商入驻联系方式</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
<div class="footer-column"> |
||||
|
<h3>交易流程</h3> |
||||
|
<ul> |
||||
|
<li>直购交易流程</li> |
||||
|
<li>竞购交易流程</li> |
||||
|
<li>货款结算流程</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
<div class="footer-column"> |
||||
|
<h3>常见问题</h3> |
||||
|
<ul> |
||||
|
<li>预留份额填报</li> |
||||
|
<li>支付结算问题</li> |
||||
|
<li>账号管理问题</li> |
||||
|
<li>在线客服问题</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
<div class="contact-info"> |
||||
|
<p>联系方式:</p> |
||||
|
<p>客服电话:222-222-222</p> |
||||
|
<p>工作时间:工作日 9:00-18:00</p> |
||||
|
<p>客服邮箱:xxxxxxxxx@stn.com</p> |
||||
|
<p>商务合作:18999999999</p> |
||||
|
</div> |
||||
|
<div class="qrcode-group"> |
||||
|
<div class="qrcode-item"> |
||||
|
<!-- 这里用 Element UI 布局占位,实际替换成真实二维码图片 --> |
||||
|
<el-empty |
||||
|
description="二维码" |
||||
|
style="width: 100px; height: 100px" |
||||
|
></el-empty> |
||||
|
<p>时味苏州小程序</p> |
||||
|
</div> |
||||
|
<div class="qrcode-item"> |
||||
|
<el-empty |
||||
|
description="二维码" |
||||
|
style="width: 100px; height: 100px" |
||||
|
></el-empty> |
||||
|
<p>时味苏州服务号</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="footer-bottom"> |
||||
|
<p>版权所有 苏州市特色农产品发展有限公司 | 苏ICP备2023023300号-1</p> |
||||
|
<p>本网站由 江苏大运远见文化科技发展有限公司 运营维护</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "Footer", |
||||
|
data() { |
||||
|
return { |
||||
|
// 可根据实际补充数据,比如动态加载链接、二维码地址等 |
||||
|
}; |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.footer-container { |
||||
|
background-color: #fff; |
||||
|
color: #666; |
||||
|
font-size: 12px; |
||||
|
border-top: 1px solid #eaeaea; |
||||
|
} |
||||
|
.footer-top { |
||||
|
display: flex; |
||||
|
justify-content: space-around; |
||||
|
padding: 20px 0; |
||||
|
flex-wrap: wrap; |
||||
|
} |
||||
|
.footer-link { |
||||
|
display: flex; |
||||
|
gap: 20px; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.footer-link span { |
||||
|
cursor: pointer; |
||||
|
transition: color 0.3s ease; |
||||
|
} |
||||
|
.footer-link span:hover { |
||||
|
color: #1890ff; |
||||
|
} |
||||
|
.contact-info p { |
||||
|
margin: 5px 0; |
||||
|
} |
||||
|
.footer-column { |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
.footer-column h3 { |
||||
|
font-size: 14px; |
||||
|
font-weight: bold; |
||||
|
margin-bottom: 10px; |
||||
|
color: #333; |
||||
|
} |
||||
|
.footer-column ul { |
||||
|
list-style: none; |
||||
|
padding: 0; |
||||
|
margin: 0; |
||||
|
} |
||||
|
.footer-column ul li { |
||||
|
margin: 5px 0; |
||||
|
cursor: pointer; |
||||
|
transition: color 0.3s ease; |
||||
|
} |
||||
|
.footer-column ul li:hover { |
||||
|
color: #1890ff; |
||||
|
} |
||||
|
.qrcode-group { |
||||
|
display: flex; |
||||
|
gap: 40px; |
||||
|
} |
||||
|
.qrcode-item { |
||||
|
text-align: center; |
||||
|
} |
||||
|
.footer-bottom { |
||||
|
text-align: center; |
||||
|
padding: 10px 0; |
||||
|
border-top: 1px solid #eaeaea; |
||||
|
} |
||||
|
.footer-bottom p { |
||||
|
margin: 5px 0; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,372 @@ |
|||||
|
<template> |
||||
|
<header class="header-nav"> |
||||
|
<!-- 顶部通知栏 --> |
||||
|
<div class="top-notice"> |
||||
|
<div class="container"> |
||||
|
<p> |
||||
|
欢迎来到企业采购平台! |
||||
|
<a href="/register" class="highlight">立即注册</a> |
||||
|
</p> |
||||
|
<div class="top-links"> |
||||
|
<a href="/user" v-if="isLogin"> |
||||
|
<img v-lazy="userInfo.avatar" alt="用户头像" class="avatar" /> |
||||
|
{{ userInfo.username }} |
||||
|
</a> |
||||
|
<a href="/login" v-else>登录</a> |
||||
|
<span class="separator" v-if="isLogin">|</span> |
||||
|
<a href="/register" v-if="isLogin">注册</a> |
||||
|
<a href="/user">采购人中心</a> |
||||
|
<a href="/userCenter" v-if="isLogin">我的订单</a> |
||||
|
<a href="">商户后台</a> |
||||
|
<a href="javascript:;" @click="handleLogout" v-if="isLogin">退出</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 主导航栏 --> |
||||
|
<div class="main-nav"> |
||||
|
<div class="container"> |
||||
|
<div class="logo"> |
||||
|
<a href="/"> |
||||
|
<h1>精品商城</h1> |
||||
|
</a> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 搜索框 --> |
||||
|
<div class="search-box"> |
||||
|
<el-input |
||||
|
placeholder="请输入搜索内容" |
||||
|
v-model="searchText" |
||||
|
class="search-input" |
||||
|
@keyup.enter.native="handleSearch" |
||||
|
> |
||||
|
<el-button |
||||
|
slot="append" |
||||
|
icon="el-icon-search" |
||||
|
@click="handleSearch" |
||||
|
></el-button> |
||||
|
</el-input> |
||||
|
<div class="hot-tags"> |
||||
|
<span>热门搜索:</span> |
||||
|
<a |
||||
|
href="javascript:;" |
||||
|
@click=" |
||||
|
searchText = '手机'; |
||||
|
handleSearch(); |
||||
|
" |
||||
|
>手机</a |
||||
|
> |
||||
|
<a |
||||
|
href="javascript:;" |
||||
|
@click=" |
||||
|
searchText = '电脑'; |
||||
|
handleSearch(); |
||||
|
" |
||||
|
>电脑</a |
||||
|
> |
||||
|
<a |
||||
|
href="javascript:;" |
||||
|
@click=" |
||||
|
searchText = '服装'; |
||||
|
handleSearch(); |
||||
|
" |
||||
|
>服装</a |
||||
|
> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 购物车入口 --> |
||||
|
<div class="cart-entry"> |
||||
|
<a href="/cart" class="cart-link"> |
||||
|
<i class="el-icon-shopping-cart-full cart-icon"></i> |
||||
|
<span>购物车</span> |
||||
|
<span class="cart-count" v-if="cartTotalCount > 0">{{ |
||||
|
cartTotalCount |
||||
|
}}</span> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 分类导航 --> |
||||
|
<div class="category-nav" v-if="false"> |
||||
|
<div class="container"> |
||||
|
<ul class="nav-list"> |
||||
|
<li class="nav-item"> |
||||
|
<a |
||||
|
href="/" |
||||
|
class="nav-link" |
||||
|
:class="{ active: $route.path === '/' }" |
||||
|
>首页</a |
||||
|
> |
||||
|
</li> |
||||
|
<li |
||||
|
v-for="category in categories" |
||||
|
:key="category.id" |
||||
|
class="nav-item" |
||||
|
> |
||||
|
<a |
||||
|
href="/category/:id" |
||||
|
:to="`/category/${category.id}`" |
||||
|
class="nav-link" |
||||
|
:class="{ active: $route.params.id == category.id }" |
||||
|
> |
||||
|
{{ category.name }} |
||||
|
</a> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
</header> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { mapGetters, mapActions } from "vuex"; |
||||
|
|
||||
|
export default { |
||||
|
name: "HeaderNav", |
||||
|
data() { |
||||
|
return { |
||||
|
searchText: "", |
||||
|
}; |
||||
|
}, |
||||
|
computed: { |
||||
|
...mapGetters([ |
||||
|
"getCategories", |
||||
|
"getCartTotalCount", |
||||
|
"isUserLogin", |
||||
|
"getUserInfo", |
||||
|
]), |
||||
|
categories() { |
||||
|
return this.getCategories; |
||||
|
}, |
||||
|
cartTotalCount() { |
||||
|
return this.getCartTotalCount; |
||||
|
}, |
||||
|
isLogin() { |
||||
|
// return this.isUserLogin; |
||||
|
return true; |
||||
|
}, |
||||
|
userInfo() { |
||||
|
return this.getUserInfo || {}; |
||||
|
}, |
||||
|
}, |
||||
|
created() { |
||||
|
this.fetchCategories(); |
||||
|
}, |
||||
|
methods: { |
||||
|
...mapActions(["fetchCategories", "logout"]), |
||||
|
handleSearch() { |
||||
|
if (this.searchText.trim()) { |
||||
|
// 这里实际项目中应该跳转到搜索结果页 |
||||
|
this.$message.success(`搜索: ${this.searchText}`); |
||||
|
} else { |
||||
|
this.$message.warning("请输入搜索内容"); |
||||
|
} |
||||
|
}, |
||||
|
handleLogout() { |
||||
|
this.$confirm("确定要退出登录吗?", "提示", { |
||||
|
confirmButtonText: "确定", |
||||
|
cancelButtonText: "取消", |
||||
|
type: "warning", |
||||
|
}) |
||||
|
.then(() => { |
||||
|
this.logout(); |
||||
|
this.$message.success("退出登录成功"); |
||||
|
this.$router.push("/"); |
||||
|
}) |
||||
|
.catch(() => { |
||||
|
// 取消退出 |
||||
|
}); |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.header-nav { |
||||
|
width: 100%; |
||||
|
|
||||
|
.top-notice { |
||||
|
background-color: #f5f5f5; |
||||
|
padding: 8px 0; |
||||
|
|
||||
|
.container { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
max-width: 1200px; |
||||
|
margin: 0 auto; |
||||
|
padding: 0 20px; |
||||
|
|
||||
|
p { |
||||
|
font-size: 14px; |
||||
|
color: #666; |
||||
|
|
||||
|
.highlight { |
||||
|
color: #ff4400; |
||||
|
margin: 0 5px; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.top-links { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 15px; |
||||
|
font-size: 14px; |
||||
|
|
||||
|
.avatar { |
||||
|
width: 24px; |
||||
|
height: 24px; |
||||
|
border-radius: 50%; |
||||
|
margin-right: 5px; |
||||
|
vertical-align: middle; |
||||
|
} |
||||
|
|
||||
|
.separator { |
||||
|
color: #ccc; |
||||
|
} |
||||
|
|
||||
|
a { |
||||
|
color: #666; |
||||
|
transition: color 0.2s; |
||||
|
|
||||
|
&:hover { |
||||
|
color: #409eff; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.main-nav { |
||||
|
background-color: #fff; |
||||
|
padding: 15px 0; |
||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); |
||||
|
|
||||
|
.container { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
max-width: 1200px; |
||||
|
margin: 0 auto; |
||||
|
padding: 0 20px; |
||||
|
} |
||||
|
|
||||
|
.logo { |
||||
|
a { |
||||
|
display: block; |
||||
|
|
||||
|
h1 { |
||||
|
font-size: 28px; |
||||
|
color: #409eff; |
||||
|
margin: 0; |
||||
|
font-weight: 700; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.search-box { |
||||
|
flex: 0 0 500px; |
||||
|
|
||||
|
@media (max-width: 992px) { |
||||
|
flex: 0 0 350px; |
||||
|
} |
||||
|
|
||||
|
@media (max-width: 768px) { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.search-input { |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.hot-tags { |
||||
|
margin-top: 8px; |
||||
|
font-size: 12px; |
||||
|
color: #999; |
||||
|
|
||||
|
span { |
||||
|
margin-right: 5px; |
||||
|
} |
||||
|
|
||||
|
a { |
||||
|
margin: 0 5px; |
||||
|
color: #666; |
||||
|
|
||||
|
&:hover { |
||||
|
color: #409eff; |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.cart-entry { |
||||
|
.cart-link { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
color: #333; |
||||
|
font-size: 16px; |
||||
|
|
||||
|
.cart-icon { |
||||
|
font-size: 20px; |
||||
|
margin-right: 5px; |
||||
|
} |
||||
|
|
||||
|
.cart-count { |
||||
|
display: inline-block; |
||||
|
width: 18px; |
||||
|
height: 18px; |
||||
|
background-color: #ff4400; |
||||
|
color: white; |
||||
|
border-radius: 50%; |
||||
|
font-size: 12px; |
||||
|
text-align: center; |
||||
|
line-height: 18px; |
||||
|
margin-left: 5px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.category-nav { |
||||
|
background-color: #409eff; |
||||
|
|
||||
|
.container { |
||||
|
max-width: 1200px; |
||||
|
margin: 0 auto; |
||||
|
padding: 0 20px; |
||||
|
} |
||||
|
|
||||
|
.nav-list { |
||||
|
display: flex; |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
|
||||
|
@media (max-width: 992px) { |
||||
|
overflow-x: auto; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
.nav-item { |
||||
|
list-style: none; |
||||
|
|
||||
|
.nav-link { |
||||
|
display: inline-block; |
||||
|
padding: 12px 20px; |
||||
|
color: #fff; |
||||
|
font-size: 16px; |
||||
|
transition: background-color 0.2s; |
||||
|
|
||||
|
&:hover, |
||||
|
&.active { |
||||
|
background-color: #337ab7; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,227 @@ |
|||||
|
<template> |
||||
|
<div class="home-layout-container"> |
||||
|
<!-- 左侧导航栏 --> |
||||
|
<div class="left-nav"> |
||||
|
<ul class="nav-list"> |
||||
|
<li class="nav-item"><i class="el-icon-sell"></i>全部商品</li> |
||||
|
<li class="nav-item"><i class="el-icon-burger"></i>米面油</li> |
||||
|
<li class="nav-item"><i class="el-icon-fork-spoon"></i>调味干货</li> |
||||
|
<li class="nav-item"><i class="el-icon-potato-strips"></i>休闲食品</li> |
||||
|
<li class="nav-item"><i class="el-icon-dish"></i>禽畜肉蛋</li> |
||||
|
<li class="nav-item"><i class="el-icon-grape"></i>鲜果蔬菜</li> |
||||
|
<li class="nav-item"><i class="el-icon-present"></i>组合集市</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 轮播图 --> |
||||
|
<div class="main-content"> |
||||
|
<el-carousel style="height: 100%"> |
||||
|
<el-carousel-item v-for="item in 4" :key="item.id"> |
||||
|
<img |
||||
|
src=" |
||||
|
https://static.ticket.sz-trip.com/jundaosuzhou/images/scenicType/topImg.png |
||||
|
" |
||||
|
class="carousel-img" |
||||
|
/> |
||||
|
</el-carousel-item> |
||||
|
</el-carousel> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 右侧用户信息栏 --> |
||||
|
<div class="right-info"> |
||||
|
<div class="avatar-container"> |
||||
|
<img |
||||
|
src="https://picsum.photos/id/64/120/120" |
||||
|
alt="用户头像" |
||||
|
class="user-avatar" |
||||
|
/> |
||||
|
<div class="welcome-text">Hi-欢迎您</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="btn-group"> |
||||
|
<el-button type="danger" size="mini">登录</el-button> |
||||
|
<el-button type="warning" size="mini">注册</el-button> |
||||
|
<el-button type="primary" size="mini">客服</el-button> |
||||
|
</div> |
||||
|
|
||||
|
<div class="func-icons"> |
||||
|
<div class="icon-item"> |
||||
|
<i class="icon el-icon-user"></i> |
||||
|
<span>个人中心</span> |
||||
|
</div> |
||||
|
<div class="icon-item"> |
||||
|
<i class="icon el-icon-goods"></i> |
||||
|
<span>我的订单</span> |
||||
|
</div> |
||||
|
<div class="icon-item"> |
||||
|
<i class="icon el-icon-star-off"></i> |
||||
|
<span>我的收藏</span> |
||||
|
</div> |
||||
|
<div class="icon-item"> |
||||
|
<i class="icon el-icon-pie-chart"></i> |
||||
|
<span>议价单</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="announcement"> |
||||
|
<el-tag type="danger" size="mini">公告</el-tag> |
||||
|
<span>2099年12月平台重要新规速递</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: "HomeLayout", |
||||
|
data() { |
||||
|
return { |
||||
|
// 轮播图数据 |
||||
|
carouselItems: [ |
||||
|
{ |
||||
|
id: 1, |
||||
|
imageUrl: "https://picsum.photos/id/26/1200/500", |
||||
|
altText: "新鲜水果促销", |
||||
|
}, |
||||
|
{ |
||||
|
id: 2, |
||||
|
imageUrl: "https://picsum.photos/id/292/1200/500", |
||||
|
altText: "有机蔬菜专场", |
||||
|
}, |
||||
|
{ |
||||
|
id: 3, |
||||
|
imageUrl: "https://picsum.photos/id/431/1200/500", |
||||
|
altText: "粮油特惠活动", |
||||
|
}, |
||||
|
], |
||||
|
}; |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.home-layout-container { |
||||
|
display: flex; |
||||
|
width: 100%; |
||||
|
height: auto; |
||||
|
} |
||||
|
|
||||
|
/* 左侧导航栏样式 */ |
||||
|
.left-nav { |
||||
|
width: 200px; |
||||
|
background-color: #f8f9fa; |
||||
|
padding: 20px 0; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
.nav-list { |
||||
|
list-style: none; |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
} |
||||
|
.nav-item { |
||||
|
padding: 12px 20px; |
||||
|
cursor: pointer; |
||||
|
color: #333; |
||||
|
transition: all 0.3s ease; |
||||
|
|
||||
|
i { |
||||
|
color: #f63131; |
||||
|
margin-right: 5px; |
||||
|
} |
||||
|
} |
||||
|
.nav-item:hover { |
||||
|
background-color: #e9ecef; |
||||
|
padding-left: 25px; |
||||
|
} |
||||
|
|
||||
|
/* 中间主内容样式 */ |
||||
|
.main-content { |
||||
|
flex: 1; |
||||
|
height: auto; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
.carousel-img { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
object-fit: cover; |
||||
|
} |
||||
|
.main-content ::v-deep .el-carousel__container { |
||||
|
height: 100% !important; |
||||
|
} |
||||
|
|
||||
|
/* 右侧用户信息栏样式 */ |
||||
|
.right-info { |
||||
|
width: 280px; |
||||
|
background-color: #fff; |
||||
|
padding: 20px; |
||||
|
box-sizing: border-box; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
border-left: 1px solid #eee; |
||||
|
} |
||||
|
.avatar-container { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
.user-avatar { |
||||
|
width: 60px; |
||||
|
height: 60px; |
||||
|
border-radius: 50%; |
||||
|
margin-bottom: 10px; |
||||
|
border: 2px solid #f0f0f0; |
||||
|
} |
||||
|
.welcome-text { |
||||
|
font-size: 14px; |
||||
|
color: #333; |
||||
|
} |
||||
|
.btn-group { |
||||
|
display: flex; |
||||
|
gap: 10px; |
||||
|
margin-bottom: 30px; |
||||
|
width: 100%; |
||||
|
} |
||||
|
.func-icons { |
||||
|
display: flex; |
||||
|
justify-content: space-around; |
||||
|
width: 100%; |
||||
|
margin-bottom: 30px; |
||||
|
padding: 10px 0; |
||||
|
border-top: 1px dashed #eee; |
||||
|
border-bottom: 1px dashed #eee; |
||||
|
} |
||||
|
.icon-item { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
cursor: pointer; |
||||
|
color: #666; |
||||
|
transition: color 0.3s ease; |
||||
|
width: 50px; |
||||
|
} |
||||
|
.icon-item:hover { |
||||
|
color: #1890ff; |
||||
|
} |
||||
|
.icon { |
||||
|
font-size: 24px; |
||||
|
margin-bottom: 5px; |
||||
|
} |
||||
|
.icon-item span { |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
.announcement { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
font-size: 12px; |
||||
|
color: #666; |
||||
|
width: 100%; |
||||
|
padding-top: 10px; |
||||
|
|
||||
|
span { |
||||
|
margin-left: 10px; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
|
|
@ -0,0 +1,191 @@ |
|||||
|
<template> |
||||
|
<div class="product-list"> |
||||
|
<div class="product-grid"> |
||||
|
<div v-for="product in products" :key="product.id" class="product-card"> |
||||
|
<div class="product-img"> |
||||
|
<a :href="`/product/${product.id}`" :to="`/product/${product.id}`"> |
||||
|
<img |
||||
|
v-lazy="product.image" |
||||
|
:alt="product.name" |
||||
|
class="product-pic" |
||||
|
/> |
||||
|
</a> |
||||
|
</div> |
||||
|
<div class="product-info"> |
||||
|
<div class="flex-between"> |
||||
|
<div class="product-price"> |
||||
|
<span class="current-price">¥{{ product.price.toFixed(2) }}</span> |
||||
|
<span class="original-price" v-if="product.originalPrice" |
||||
|
>¥{{ product.originalPrice.toFixed(2) }}</span |
||||
|
> |
||||
|
</div> |
||||
|
<div class="product-sales" v-if="product.sales"> |
||||
|
<span>已售 {{ product.sales }} 件</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<h3 class="product-name"> |
||||
|
<a :href="`/product/${product.id}`" :to="`/product/${product.id}`"> |
||||
|
{{ product.name }} |
||||
|
</a> |
||||
|
</h3> |
||||
|
<div class="product-actions"> |
||||
|
<el-button |
||||
|
type="primary" |
||||
|
size="small" |
||||
|
style="background-color: #ff6e90; border: none" |
||||
|
@click="addToCart(product)" |
||||
|
> |
||||
|
<el-icon name="el-icon-shopping-cart"></el-icon> 加入购物车 |
||||
|
</el-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 无商品时显示 --> |
||||
|
<div class="no-products" v-if="products.length === 0"> |
||||
|
<el-empty description="暂无相关商品"></el-empty> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { mapActions } from "vuex"; |
||||
|
|
||||
|
export default { |
||||
|
name: "ProductList", |
||||
|
props: { |
||||
|
products: { |
||||
|
type: Array, |
||||
|
default: () => [], |
||||
|
}, |
||||
|
}, |
||||
|
methods: { |
||||
|
...mapActions(["addToCart"]), |
||||
|
addToCart(product) { |
||||
|
// 检查用户是否登录 |
||||
|
if (!this.$store.getters.isUserLogin) { |
||||
|
this.$confirm("您尚未登录,是否前往登录?", "提示", { |
||||
|
confirmButtonText: "登录", |
||||
|
cancelButtonText: "取消", |
||||
|
type: "info", |
||||
|
}) |
||||
|
.then(() => { |
||||
|
this.$router.push({ |
||||
|
path: "/login", |
||||
|
query: { redirect: this.$route.fullPath }, |
||||
|
}); |
||||
|
}) |
||||
|
.catch(() => { |
||||
|
// 取消登录 |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.addToCart({ |
||||
|
id: product.id, |
||||
|
name: product.name, |
||||
|
price: product.price, |
||||
|
image: product.image, |
||||
|
quantity: 1, |
||||
|
}); |
||||
|
this.$message.success("已加入购物车"); |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.product-list { |
||||
|
.product-grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); |
||||
|
gap: 20px; |
||||
|
} |
||||
|
|
||||
|
.product-card { |
||||
|
border: 1px solid #eaeaea; |
||||
|
border-radius: 8px; |
||||
|
overflow: hidden; |
||||
|
transition: all 0.3s ease; |
||||
|
padding: 10px; |
||||
|
|
||||
|
&:hover { |
||||
|
transform: translateY(-5px); |
||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.product-img { |
||||
|
height: 250px; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
.product-pic { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
object-fit: cover; |
||||
|
transition: transform 0.5s ease; |
||||
|
|
||||
|
&:hover { |
||||
|
transform: scale(1.05); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.product-info { |
||||
|
padding: 10px; |
||||
|
|
||||
|
.product-name { |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
min-height: 40px; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
display: -webkit-box; |
||||
|
-webkit-line-clamp: 2; |
||||
|
-webkit-box-orient: vertical; |
||||
|
margin-bottom: 10px; |
||||
|
|
||||
|
a { |
||||
|
color: #333; |
||||
|
|
||||
|
&:hover { |
||||
|
color: #409eff; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.product-price { |
||||
|
margin-bottom: 15px; |
||||
|
|
||||
|
.current-price { |
||||
|
color: #ff4400; |
||||
|
font-size: 16px; |
||||
|
font-weight: 700; |
||||
|
} |
||||
|
|
||||
|
.original-price { |
||||
|
color: #999; |
||||
|
font-size: 12px; |
||||
|
text-decoration: line-through; |
||||
|
margin-left: 8px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.product-actions { |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
.product-sales { |
||||
|
font-size: 12px; |
||||
|
color: #999; |
||||
|
margin-bottom: 15px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.no-products { |
||||
|
padding: 50px 0; |
||||
|
text-align: center; |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -1,12 +1,64 @@ |
|||||
import Vue from 'vue' |
import Vue from 'vue' |
||||
import App from './App.vue' |
import App from './App' |
||||
import router from './router' |
import router from './router' |
||||
import store from './store' |
import store from './store' |
||||
|
import ElementUI from 'element-ui' |
||||
|
import 'element-ui/lib/theme-chalk/index.css' |
||||
|
import axios from 'axios' |
||||
|
import VueLazyload from 'vue-lazyload' |
||||
|
import '@/assets/css/common.css' |
||||
|
|
||||
|
// 全局配置
|
||||
Vue.config.productionTip = false |
Vue.config.productionTip = false |
||||
|
Vue.use(ElementUI) |
||||
|
Vue.prototype.$http = axios |
||||
|
|
||||
|
// 配置图片懒加载
|
||||
|
Vue.use(VueLazyload, { |
||||
|
preLoad: 1.3, // 预加载高度比例
|
||||
|
error: require('./assets/logo.png'), |
||||
|
loading: require('./assets/logo.png'), |
||||
|
attempt: 3, // 增加尝试次数
|
||||
|
listenEvents: ['scroll', 'wheel', 'mousewheel', 'resize', 'animationend', 'transitionend', 'touchmove'], // 确保监听事件完整
|
||||
|
adapter: { |
||||
|
// 增加加载状态日志
|
||||
|
loaded({ bindType, el, naturalHeight, naturalWidth, $parent, src, loading, error, Init }) { |
||||
|
console.log('图片加载完成:', src) |
||||
|
}, |
||||
|
error({ bindType, el, error, $parent, src, loading }) { |
||||
|
console.log('图片加载失败:', src) |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 请求拦截器设置
|
||||
|
axios.interceptors.request.use( |
||||
|
config => { |
||||
|
// 可以在这里添加token等信息
|
||||
|
return config |
||||
|
}, |
||||
|
error => { |
||||
|
return Promise.reject(error) |
||||
|
} |
||||
|
) |
||||
|
|
||||
|
// 响应拦截器设置
|
||||
|
axios.interceptors.response.use( |
||||
|
response => { |
||||
|
return response.data |
||||
|
}, |
||||
|
error => { |
||||
|
// 统一错误处理
|
||||
|
ElementUI.Message.error('请求失败,请稍后重试') |
||||
|
return Promise.reject(error) |
||||
|
} |
||||
|
) |
||||
|
|
||||
|
/* eslint-disable no-new */ |
||||
new Vue({ |
new Vue({ |
||||
|
el: '#app', |
||||
router, |
router, |
||||
store, |
store, |
||||
render: h => h(App) |
components: { App }, |
||||
}).$mount('#app') |
template: '<App/>' |
||||
|
}) |
||||
|
@ -1,29 +1,153 @@ |
|||||
import Vue from 'vue' |
import Vue from 'vue' |
||||
import VueRouter from 'vue-router' |
import Router from 'vue-router' |
||||
import HomeView from '../views/HomeView.vue' |
|
||||
|
|
||||
Vue.use(VueRouter) |
Vue.use(Router) |
||||
|
|
||||
const routes = [ |
const router = new Router({ |
||||
{ |
|
||||
path: '/', |
|
||||
name: 'home', |
|
||||
component: HomeView |
|
||||
}, |
|
||||
{ |
|
||||
path: '/about', |
|
||||
name: 'about', |
|
||||
// route level code-splitting
|
|
||||
// this generates a separate chunk (about.[hash].js) for this route
|
|
||||
// which is lazy-loaded when the route is visited.
|
|
||||
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue') |
|
||||
} |
|
||||
] |
|
||||
|
|
||||
const router = new VueRouter({ |
|
||||
mode: 'history', |
mode: 'history', |
||||
base: process.env.BASE_URL, |
base: process.env.BASE_URL, |
||||
routes |
routes: [ |
||||
|
{ |
||||
|
path: '/', |
||||
|
name: 'Index', |
||||
|
meta: { |
||||
|
title: '首页 - 精品商城', |
||||
|
keepAlive: false |
||||
|
}, |
||||
|
component: () => import('@/views/Index.vue') |
||||
|
}, |
||||
|
{ |
||||
|
path: '/Home', |
||||
|
name: 'Home', |
||||
|
meta: { |
||||
|
title: '首页 - 精品商城', |
||||
|
keepAlive: false |
||||
|
}, |
||||
|
component: () => import('@/views/Home.vue') |
||||
|
}, |
||||
|
// {
|
||||
|
// path: '/category/:id?',
|
||||
|
// name: 'Category',
|
||||
|
// meta: {
|
||||
|
// title: '商品分类 - 精品商城',
|
||||
|
// keepAlive: false
|
||||
|
// },
|
||||
|
// component: () => import('@/views/Category.vue')
|
||||
|
// },
|
||||
|
// {
|
||||
|
// path: '/product/:id',
|
||||
|
// name: 'ProductDetail',
|
||||
|
// meta: {
|
||||
|
// title: '商品详情 - 精品商城',
|
||||
|
// keepAlive: false
|
||||
|
// },
|
||||
|
// component: () => import('@/views/ProductDetail.vue')
|
||||
|
// },
|
||||
|
// {
|
||||
|
// path: '/cart',
|
||||
|
// name: 'Cart',
|
||||
|
// meta: {
|
||||
|
// title: '购物车 - 精品商城',
|
||||
|
// requireAuth: true,
|
||||
|
// keepAlive: false
|
||||
|
// },
|
||||
|
// component: () => import('@/views/Cart.vue')
|
||||
|
// },
|
||||
|
// {
|
||||
|
// path: '/checkout',
|
||||
|
// name: 'Checkout',
|
||||
|
// meta: {
|
||||
|
// title: '结算 - 精品商城',
|
||||
|
// requireAuth: true,
|
||||
|
// keepAlive: false
|
||||
|
// },
|
||||
|
// component: () => import('@/views/Checkout.vue')
|
||||
|
// },
|
||||
|
{ |
||||
|
path: '/userCenter', |
||||
|
name: 'UserCenter', |
||||
|
meta: { |
||||
|
title: '个人中心 - 精品商城', |
||||
|
requireAuth: true, |
||||
|
keepAlive: false |
||||
|
}, |
||||
|
component: () => import('@/views/User/UserCenter.vue') |
||||
|
}, |
||||
|
{ |
||||
|
path: 'orderList', |
||||
|
name: 'OrderList', |
||||
|
meta: { |
||||
|
title: '我的订单 - 精品商城', |
||||
|
requireAuth: true, |
||||
|
keepAlive: false |
||||
|
}, |
||||
|
component: () => import('@/views/User/OrderList.vue') |
||||
|
}, |
||||
|
// {
|
||||
|
// path: '/user/orders/:id',
|
||||
|
// name: 'OrderDetail',
|
||||
|
// meta: {
|
||||
|
// title: '订单详情 - 精品商城',
|
||||
|
// requireAuth: true,
|
||||
|
// keepAlive: false
|
||||
|
// },
|
||||
|
// component: () => import('@/views/OrderDetail.vue')
|
||||
|
// },
|
||||
|
// {
|
||||
|
// path: '/login',
|
||||
|
// name: 'Login',
|
||||
|
// meta: {
|
||||
|
// title: '登录 - 精品商城',
|
||||
|
// keepAlive: false
|
||||
|
// },
|
||||
|
// component: () => import('@/views/Login.vue')
|
||||
|
// },
|
||||
|
// {
|
||||
|
// path: '/register',
|
||||
|
// name: 'Register',
|
||||
|
// meta: {
|
||||
|
// title: '注册 - 精品商城',
|
||||
|
// keepAlive: false
|
||||
|
// },
|
||||
|
// component: () => import('@/views/Register.vue')
|
||||
|
// },
|
||||
|
// {
|
||||
|
// path: '*',
|
||||
|
// name: 'NotFound',
|
||||
|
// meta: {
|
||||
|
// title: '页面不存在 - 精品商城',
|
||||
|
// keepAlive: false
|
||||
|
// },
|
||||
|
// component: () => import('@/views/NotFound.vue')
|
||||
|
// }
|
||||
|
], |
||||
|
scrollBehavior(to, from, savedPosition) { |
||||
|
// 页面滚动到顶部
|
||||
|
return { x: 0, y: 0 } |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 路由守卫
|
||||
|
router.beforeEach((to, from, next) => { |
||||
|
// 设置页面标题
|
||||
|
if (to.meta.title) { |
||||
|
document.title = to.meta.title |
||||
|
} |
||||
|
|
||||
|
// 验证登录状态
|
||||
|
if (to.meta.requireAuth) { |
||||
|
const token = localStorage.getItem('token') |
||||
|
if (token) { |
||||
|
next() |
||||
|
} else { |
||||
|
next({ |
||||
|
path: '/login', |
||||
|
query: { redirect: to.fullPath } |
||||
|
}) |
||||
|
} |
||||
|
} else { |
||||
|
next() |
||||
|
} |
||||
}) |
}) |
||||
|
|
||||
export default router |
export default router |
@ -1,17 +1,202 @@ |
|||||
import Vue from 'vue' |
import Vue from 'vue' |
||||
import Vuex from 'vuex' |
import Vuex from 'vuex' |
||||
|
import createPersistedState from 'vuex-persistedstate' |
||||
|
|
||||
Vue.use(Vuex) |
Vue.use(Vuex) |
||||
|
|
||||
export default new Vuex.Store({ |
export default new Vuex.Store({ |
||||
|
plugins: [ |
||||
|
createPersistedState({ |
||||
|
storage: window.localStorage, |
||||
|
reducer(val) { |
||||
|
return { |
||||
|
// 只持久化需要的状态
|
||||
|
cart: val.cart, |
||||
|
user: val.user |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
], |
||||
state: { |
state: { |
||||
}, |
// 购物车状态
|
||||
getters: { |
cart: { |
||||
|
items: [], // 购物车商品列表
|
||||
|
totalCount: 0, // 商品总数
|
||||
|
totalPrice: 0 // 商品总价
|
||||
|
}, |
||||
|
// 用户状态
|
||||
|
user: { |
||||
|
isLogin: false, |
||||
|
info: null, |
||||
|
token: '' |
||||
|
}, |
||||
|
// 分类数据
|
||||
|
categories: [], |
||||
|
// 全局加载状态
|
||||
|
loading: false |
||||
}, |
}, |
||||
mutations: { |
mutations: { |
||||
|
// 更新分类数据
|
||||
|
UPDATE_CATEGORIES(state, categories) { |
||||
|
state.categories = categories |
||||
|
}, |
||||
|
|
||||
|
// 更新加载状态
|
||||
|
UPDATE_LOADING(state, status) { |
||||
|
state.loading = status |
||||
|
}, |
||||
|
|
||||
|
// 用户登录
|
||||
|
USER_LOGIN(state, { userInfo, token }) { |
||||
|
state.user.isLogin = true |
||||
|
state.user.info = userInfo |
||||
|
state.user.token = token |
||||
|
localStorage.setItem('token', token) |
||||
|
}, |
||||
|
|
||||
|
// 用户登出
|
||||
|
USER_LOGOUT(state) { |
||||
|
state.user.isLogin = false |
||||
|
state.user.info = null |
||||
|
state.user.token = '' |
||||
|
localStorage.removeItem('token') |
||||
|
}, |
||||
|
|
||||
|
// 添加商品到购物车
|
||||
|
ADD_TO_CART(state, product) { |
||||
|
const existingItem = state.cart.items.find(item => item.id === product.id) |
||||
|
|
||||
|
if (existingItem) { |
||||
|
existingItem.quantity += product.quantity || 1 |
||||
|
} else { |
||||
|
state.cart.items.push({ |
||||
|
...product, |
||||
|
quantity: product.quantity || 1 |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
this.commit('UPDATE_CART_TOTAL') |
||||
|
}, |
||||
|
|
||||
|
// 从购物车移除商品
|
||||
|
REMOVE_FROM_CART(state, productId) { |
||||
|
state.cart.items = state.cart.items.filter(item => item.id !== productId) |
||||
|
this.commit('UPDATE_CART_TOTAL') |
||||
|
}, |
||||
|
|
||||
|
// 更新购物车商品数量
|
||||
|
UPDATE_CART_ITEM_QUANTITY(state, { productId, quantity }) { |
||||
|
const item = state.cart.items.find(item => item.id === productId) |
||||
|
if (item) { |
||||
|
item.quantity = quantity |
||||
|
this.commit('UPDATE_CART_TOTAL') |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 清空购物车
|
||||
|
CLEAR_CART(state) { |
||||
|
state.cart.items = [] |
||||
|
this.commit('UPDATE_CART_TOTAL') |
||||
|
}, |
||||
|
|
||||
|
// 更新购物车总计
|
||||
|
UPDATE_CART_TOTAL(state) { |
||||
|
state.cart.totalCount = state.cart.items.reduce((total, item) => { |
||||
|
return total + item.quantity |
||||
|
}, 0) |
||||
|
|
||||
|
state.cart.totalPrice = state.cart.items.reduce((total, item) => { |
||||
|
return total + (item.price * item.quantity) |
||||
|
}, 0) |
||||
|
} |
||||
}, |
}, |
||||
actions: { |
actions: { |
||||
|
// 获取分类数据
|
||||
|
fetchCategories({ commit }) { |
||||
|
commit('UPDATE_LOADING', true) |
||||
|
// 模拟API请求
|
||||
|
return new Promise(resolve => { |
||||
|
setTimeout(() => { |
||||
|
const categories = [ |
||||
|
{ id: 1, name: '电子产品', icon: 'el-icon-laptop' }, |
||||
|
{ id: 2, name: '服装鞋帽', icon: 'el-icon-shopping-bag-1' }, |
||||
|
{ id: 3, name: '家居用品', icon: 'el-icon-home' }, |
||||
|
{ id: 4, name: '美妆个护', icon: 'el-icon-present' }, |
||||
|
{ id: 5, name: '食品饮料', icon: 'el-icon-dish' }, |
||||
|
{ id: 6, name: '图书音像', icon: 'el-icon-document' } |
||||
|
] |
||||
|
commit('UPDATE_CATEGORIES', categories) |
||||
|
commit('UPDATE_LOADING', false) |
||||
|
resolve(categories) |
||||
|
}, 500) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 用户登录
|
||||
|
login({ commit }, { username, password }) { |
||||
|
commit('UPDATE_LOADING', true) |
||||
|
// 模拟登录API请求
|
||||
|
return new Promise((resolve, reject) => { |
||||
|
setTimeout(() => { |
||||
|
if (username === 'test' && password === '123456') { |
||||
|
const userInfo = { id: 1, username: 'test', avatar: 'https://picsum.photos/200' } |
||||
|
const token = 'fake-token-123456' |
||||
|
commit('USER_LOGIN', { userInfo, token }) |
||||
|
commit('UPDATE_LOADING', false) |
||||
|
resolve(userInfo) |
||||
|
} else { |
||||
|
commit('UPDATE_LOADING', false) |
||||
|
reject(new Error('用户名或密码错误')) |
||||
|
} |
||||
|
}, 1000) |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 用户登出
|
||||
|
logout({ commit }) { |
||||
|
commit('USER_LOGOUT') |
||||
|
}, |
||||
|
|
||||
|
// 添加商品到购物车
|
||||
|
addToCart({ commit }, product) { |
||||
|
commit('ADD_TO_CART', product) |
||||
|
}, |
||||
|
|
||||
|
// 从购物车移除商品
|
||||
|
removeFromCart({ commit }, productId) { |
||||
|
commit('REMOVE_FROM_CART', productId) |
||||
|
}, |
||||
|
|
||||
|
// 更新购物车商品数量
|
||||
|
updateCartItemQuantity({ commit }, payload) { |
||||
|
commit('UPDATE_CART_ITEM_QUANTITY', payload) |
||||
|
}, |
||||
|
|
||||
|
// 清空购物车
|
||||
|
clearCart({ commit }) { |
||||
|
commit('CLEAR_CART') |
||||
|
} |
||||
}, |
}, |
||||
modules: { |
getters: { |
||||
|
// 获取分类列表
|
||||
|
getCategories: state => state.categories, |
||||
|
|
||||
|
// 获取购物车信息
|
||||
|
getCart: state => state.cart, |
||||
|
|
||||
|
// 获取购物车商品总数
|
||||
|
getCartTotalCount: state => state.cart.totalCount, |
||||
|
|
||||
|
// 获取购物车商品总价
|
||||
|
getCartTotalPrice: state => state.cart.totalPrice, |
||||
|
|
||||
|
// 获取用户登录状态
|
||||
|
isUserLogin: state => state.user.isLogin, |
||||
|
|
||||
|
// 获取用户信息
|
||||
|
getUserInfo: state => state.user.info, |
||||
|
|
||||
|
// 获取加载状态
|
||||
|
getLoadingStatus: state => state.loading |
||||
} |
} |
||||
}) |
}) |
||||
|
@ -1,5 +0,0 @@ |
|||||
<template> |
|
||||
<div class="about"> |
|
||||
<h1>This is an about page</h1> |
|
||||
</div> |
|
||||
</template> |
|
@ -0,0 +1,384 @@ |
|||||
|
<template> |
||||
|
<div class="home-page"> |
||||
|
<!-- 轮播图 --> |
||||
|
<el-carousel |
||||
|
height="500px" |
||||
|
indicator-position="outside" |
||||
|
class="home-carousel" |
||||
|
> |
||||
|
<el-carousel-item v-for="item in 4" :key="item"> |
||||
|
<img |
||||
|
v-lazy="`https://picsum.photos/1200/500?random=${item}`" |
||||
|
alt="轮播图片" |
||||
|
class="carousel-img" |
||||
|
> |
||||
|
</el-carousel-item> |
||||
|
</el-carousel> |
||||
|
|
||||
|
<!-- 分类导航 --> |
||||
|
<div class="category-nav"> |
||||
|
<h2 class="section-title">商品分类</h2> |
||||
|
<div class="category-list"> |
||||
|
<div |
||||
|
v-for="category in categories" |
||||
|
:key="category.id" |
||||
|
class="category-item" |
||||
|
@click="$router.push(`/category/${category.id}`)" |
||||
|
> |
||||
|
<el-icon :name="category.icon" class="category-icon"></el-icon> |
||||
|
<span class="category-name">{{ category.name }}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 热门商品 --> |
||||
|
<div class="hot-products"> |
||||
|
<div class="section-header"> |
||||
|
<h2 class="section-title">热门商品</h2> |
||||
|
<a href="#" class="more-link">查看更多 <i class="el-icon-arrow-right"></i></a> |
||||
|
</div> |
||||
|
|
||||
|
<ProductList :products="hotProducts" /> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 新品上市 --> |
||||
|
<div class="new-products"> |
||||
|
<div class="section-header"> |
||||
|
<h2 class="section-title">新品上市</h2> |
||||
|
<a href="#" class="more-link">查看更多 <i class="el-icon-arrow-right"></i></a> |
||||
|
</div> |
||||
|
|
||||
|
<ProductList :products="newProducts" /> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 促销活动 --> |
||||
|
<div class="promotion-section"> |
||||
|
<h2 class="section-title">限时促销</h2> |
||||
|
<div class="promotion-container"> |
||||
|
<div class="promotion-item"> |
||||
|
<img |
||||
|
v-lazy="`https://picsum.photos/600/300?random=10`" |
||||
|
alt="促销活动图片" |
||||
|
class="promotion-img" |
||||
|
> |
||||
|
<div class="promotion-info"> |
||||
|
<h3>夏季大促</h3> |
||||
|
<p>全场商品低至5折</p> |
||||
|
<el-button type="primary" size="medium">立即抢购</el-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="promotion-item"> |
||||
|
<img |
||||
|
v-lazy="`https://picsum.photos/600/300?random=11`" |
||||
|
alt="促销活动图片" |
||||
|
class="promotion-img" |
||||
|
> |
||||
|
<div class="promotion-info"> |
||||
|
<h3>新品首发</h3> |
||||
|
<p>限量发售,先到先得</p> |
||||
|
<el-button type="primary" size="medium">立即抢购</el-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { mapGetters, mapActions } from 'vuex' |
||||
|
import ProductList from '../components/product/ProductList' |
||||
|
|
||||
|
export default { |
||||
|
name: 'Home', |
||||
|
components: { |
||||
|
ProductList |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
// 模拟热门商品数据 |
||||
|
hotProducts: [ |
||||
|
{ |
||||
|
id: 1, |
||||
|
name: '超薄笔记本电脑', |
||||
|
price: 5999, |
||||
|
originalPrice: 6999, |
||||
|
image: 'https://picsum.photos/300/300?random=1', |
||||
|
sales: 1254, |
||||
|
categoryId: 1 |
||||
|
}, |
||||
|
{ |
||||
|
id: 2, |
||||
|
name: '智能手表', |
||||
|
price: 1599, |
||||
|
originalPrice: 1799, |
||||
|
image: 'https://picsum.photos/300/300?random=2', |
||||
|
sales: 856, |
||||
|
categoryId: 1 |
||||
|
}, |
||||
|
{ |
||||
|
id: 3, |
||||
|
name: '纯棉T恤', |
||||
|
price: 99, |
||||
|
originalPrice: 199, |
||||
|
image: 'https://picsum.photos/300/300?random=3', |
||||
|
sales: 2356, |
||||
|
categoryId: 2 |
||||
|
}, |
||||
|
{ |
||||
|
id: 4, |
||||
|
name: '休闲牛仔裤', |
||||
|
price: 199, |
||||
|
originalPrice: 399, |
||||
|
image: 'https://picsum.photos/300/300?random=4', |
||||
|
sales: 1890, |
||||
|
categoryId: 2 |
||||
|
}, |
||||
|
{ |
||||
|
id: 5, |
||||
|
name: '舒适沙发', |
||||
|
price: 2999, |
||||
|
originalPrice: 3999, |
||||
|
image: 'https://picsum.photos/300/300?random=5', |
||||
|
sales: 324, |
||||
|
categoryId: 3 |
||||
|
}, |
||||
|
{ |
||||
|
id: 6, |
||||
|
name: '智能扫地机器人', |
||||
|
price: 1899, |
||||
|
originalPrice: 2299, |
||||
|
image: 'https://picsum.photos/300/300?random=6', |
||||
|
sales: 754, |
||||
|
categoryId: 3 |
||||
|
} |
||||
|
], |
||||
|
// 模拟新品数据 |
||||
|
newProducts: [ |
||||
|
{ |
||||
|
id: 7, |
||||
|
name: '高清投影仪', |
||||
|
price: 3299, |
||||
|
originalPrice: 3699, |
||||
|
image: 'https://picsum.photos/300/300?random=7', |
||||
|
sales: 156, |
||||
|
categoryId: 1 |
||||
|
}, |
||||
|
{ |
||||
|
id: 8, |
||||
|
name: '保湿面霜', |
||||
|
price: 299, |
||||
|
originalPrice: 359, |
||||
|
image: 'https://picsum.photos/300/300?random=8', |
||||
|
sales: 423, |
||||
|
categoryId: 4 |
||||
|
}, |
||||
|
{ |
||||
|
id: 9, |
||||
|
name: '有机水果礼盒', |
||||
|
price: 159, |
||||
|
originalPrice: 199, |
||||
|
image: 'https://picsum.photos/300/300?random=9', |
||||
|
sales: 287, |
||||
|
categoryId: 5 |
||||
|
}, |
||||
|
{ |
||||
|
id: 10, |
||||
|
name: '经典文学名著', |
||||
|
price: 129, |
||||
|
originalPrice: 199, |
||||
|
image: 'https://picsum.photos/300/300?random=10', |
||||
|
sales: 342, |
||||
|
categoryId: 6 |
||||
|
}, |
||||
|
{ |
||||
|
id: 11, |
||||
|
name: '无线蓝牙耳机', |
||||
|
price: 799, |
||||
|
originalPrice: 999, |
||||
|
image: 'https://picsum.photos/300/300?random=11', |
||||
|
sales: 567, |
||||
|
categoryId: 1 |
||||
|
}, |
||||
|
{ |
||||
|
id: 12, |
||||
|
name: '运动鞋', |
||||
|
price: 499, |
||||
|
originalPrice: 699, |
||||
|
image: 'https://picsum.photos/300/300?random=12', |
||||
|
sales: 432, |
||||
|
categoryId: 2 |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
...mapGetters([ |
||||
|
'getCategories' |
||||
|
]), |
||||
|
categories() { |
||||
|
return this.getCategories |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
this.fetchCategories() |
||||
|
}, |
||||
|
methods: { |
||||
|
...mapActions([ |
||||
|
'fetchCategories' |
||||
|
]) |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.home-page { |
||||
|
.home-carousel { |
||||
|
margin-bottom: 30px; |
||||
|
|
||||
|
.carousel-img { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
object-fit: cover; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.category-nav { |
||||
|
margin-bottom: 40px; |
||||
|
|
||||
|
.category-list { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
flex-wrap: wrap; |
||||
|
gap: 20px; |
||||
|
margin-top: 20px; |
||||
|
} |
||||
|
|
||||
|
.category-item { |
||||
|
flex: 1; |
||||
|
min-width: 120px; |
||||
|
height: 150px; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
border: 1px solid #eaeaea; |
||||
|
border-radius: 8px; |
||||
|
cursor: pointer; |
||||
|
transition: all 0.3s ease; |
||||
|
|
||||
|
&:hover { |
||||
|
transform: translateY(-5px); |
||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); |
||||
|
border-color: #409eff; |
||||
|
} |
||||
|
|
||||
|
.category-icon { |
||||
|
font-size: 36px; |
||||
|
color: #409eff; |
||||
|
margin-bottom: 15px; |
||||
|
} |
||||
|
|
||||
|
.category-name { |
||||
|
font-size: 16px; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.section-title { |
||||
|
font-size: 24px; |
||||
|
color: #333; |
||||
|
padding-bottom: 10px; |
||||
|
border-bottom: 2px solid #409eff; |
||||
|
display: inline-block; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
|
||||
|
.section-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 20px; |
||||
|
|
||||
|
.more-link { |
||||
|
color: #409eff; |
||||
|
font-size: 14px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
&:hover { |
||||
|
text-decoration: underline; |
||||
|
} |
||||
|
|
||||
|
i { |
||||
|
margin-left: 5px; |
||||
|
font-size: 16px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.hot-products, .new-products { |
||||
|
margin-bottom: 40px; |
||||
|
} |
||||
|
|
||||
|
.promotion-section { |
||||
|
margin: 40px 0; |
||||
|
|
||||
|
.promotion-container { |
||||
|
display: flex; |
||||
|
gap: 20px; |
||||
|
margin-top: 20px; |
||||
|
|
||||
|
@media (max-width: 768px) { |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.promotion-item { |
||||
|
flex: 1; |
||||
|
position: relative; |
||||
|
height: 300px; |
||||
|
border-radius: 8px; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
.promotion-img { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
object-fit: cover; |
||||
|
transition: transform 0.5s ease; |
||||
|
|
||||
|
&:hover { |
||||
|
transform: scale(1.05); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.promotion-info { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
background-color: rgba(0, 0, 0, 0.5); |
||||
|
color: #fff; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
padding: 20px; |
||||
|
text-align: center; |
||||
|
|
||||
|
h3 { |
||||
|
font-size: 24px; |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
p { |
||||
|
font-size: 16px; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
|
</script> |
@ -1,18 +0,0 @@ |
|||||
<template> |
|
||||
<div class="home"> |
|
||||
<img alt="Vue logo" src="../assets/logo.png"> |
|
||||
<HelloWorld msg="Welcome to Your Vue.js App"/> |
|
||||
</div> |
|
||||
</template> |
|
||||
|
|
||||
<script> |
|
||||
// @ is an alias to /src |
|
||||
import HelloWorld from '@/components/HelloWorld.vue' |
|
||||
|
|
||||
export default { |
|
||||
name: 'HomeView', |
|
||||
components: { |
|
||||
HelloWorld |
|
||||
} |
|
||||
} |
|
||||
</script> |
|
@ -0,0 +1,88 @@ |
|||||
|
<template> |
||||
|
<div class="bg"> |
||||
|
<HomeLayout /> |
||||
|
|
||||
|
<div class="product-box"> |
||||
|
<h2>今日推荐</h2> |
||||
|
<ProductList :products="newProducts" /> |
||||
|
</div> |
||||
|
|
||||
|
<div class="product-box"> |
||||
|
<h2>热销排行</h2> |
||||
|
<ProductList :products="newProducts" /> |
||||
|
</div> |
||||
|
|
||||
|
<div class="product-box"> |
||||
|
<h2>新品上市</h2> |
||||
|
<ProductList :products="newProducts" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import HomeLayout from "@/components/layout/HomeLayout.vue"; |
||||
|
import ProductList from "@/components/product/ProductList.vue"; |
||||
|
|
||||
|
export default { |
||||
|
components: { |
||||
|
HomeLayout, |
||||
|
ProductList, |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
newProducts: [ |
||||
|
{ |
||||
|
id: 7, |
||||
|
name: "高清投影仪高清投影仪高清投影仪高清投影仪高清投影仪高清投影仪高清投影仪高清投影仪", |
||||
|
price: 3299, |
||||
|
originalPrice: 3699, |
||||
|
image: |
||||
|
"https://static.ticket.sz-trip.com/jundaosuzhou/images/scenicType/topImg.png", |
||||
|
sales: 156, |
||||
|
categoryId: 1, |
||||
|
}, |
||||
|
{ |
||||
|
id: 8, |
||||
|
name: "保湿面霜", |
||||
|
price: 299, |
||||
|
originalPrice: 359, |
||||
|
image: |
||||
|
"https://static.ticket.sz-trip.com/jundaosuzhou/images/scenicType/topImg.png", |
||||
|
sales: 423, |
||||
|
categoryId: 4, |
||||
|
}, |
||||
|
{ |
||||
|
id: 9, |
||||
|
name: "有机水果礼盒", |
||||
|
price: 159, |
||||
|
originalPrice: 199, |
||||
|
image: |
||||
|
"https://static.ticket.sz-trip.com/jundaosuzhou/images/scenicType/topImg.png", |
||||
|
sales: 287, |
||||
|
categoryId: 5, |
||||
|
}, |
||||
|
{ |
||||
|
id: 10, |
||||
|
name: "经典文学名著", |
||||
|
price: 129, |
||||
|
originalPrice: 199, |
||||
|
image: |
||||
|
"https://static.ticket.sz-trip.com/jundaosuzhou/images/scenicType/topImg.png", |
||||
|
sales: 342, |
||||
|
categoryId: 6, |
||||
|
}, |
||||
|
], |
||||
|
}; |
||||
|
}, |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.product-box { |
||||
|
margin: 30px 0; |
||||
|
|
||||
|
h2 { |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -1,4 +1,5 @@ |
|||||
const { defineConfig } = require('@vue/cli-service') |
const { defineConfig } = require('@vue/cli-service') |
||||
module.exports = defineConfig({ |
module.exports = defineConfig({ |
||||
transpileDependencies: true |
transpileDependencies: true, |
||||
|
runtimeCompiler: true |
||||
}) |
}) |
||||
|
Loading…
Reference in new issue