从零开始,搭建一个简单的购物平台(十三)前端商城部分:
https://blog.csdn.net/time_____/article/details/108514710
项目源码(持续更新):https://gitee.com/DieHunter/myCode/tree/master/shopping
本篇文章介绍实现部分组件和首页部分,首页组件如下,首页中的数据通过分批异步加载,也就是每个组件的数据单独请求,减少数据请求堵塞,下面介绍一下实现过程
pageTitle(页面标题)
首先要考虑到标题的返回功能,在methods里写个路由返回函数
methods: { goBack() { this.$router.go(-1); } }
接着通过组件属性传参数(标题内容,是否有返回按钮) ,通过props将组件属性放在当前data中
props: ["title", "isBack"],
标签中根据isBack决定是否有返回按钮,并显示title值
<template> <div id="top"> <span v-if="isBack" class="back iconfont icon-fanhui" @click="goBack"></span> <span class="title">{{title}}</span> </div> </template>
banner(主页轮播)
轮播图组件中的图片是单独请求的,所以用了model管理数据,bussiness进行请求,后面的组件有数据请求交互都是用这种写法
model.js内容,保存banner列表,vue实例,页面配置信息
export default class BannerModel {//banner数据存取 constructor() { this._bannerList = [] this._pageConfig = {} } static getInstance() { //单例写法 if (!BannerModel._instance) { Object.defineProperty(BannerModel, "_instance", { value: new BannerModel() }) } return BannerModel._instance; } set vueComponent(val) { this._vueComponent = val } get vueComponent() { return this._vueComponent } set pageConfig(val) { this._pageConfig = val this._pageConfig.picType = 1 } get pageConfig() { return this._pageConfig } set bannerList(val) { this._bannerList = val this._vueComponent.list = this.bannerList } get bannerList() { return this._bannerList } }
bussiness.js 做请求和逻辑处理
import Vue from 'vue' import config from "../../config/config" import BannerModel from "./model"; import Clone from "../../utils/clone" const { DefaultPageConfig, ServerApi } = config export default class BannerBussiness extends Vue {//业务处理 constructor(_vueComponent) { super() BannerModel.getInstance().vueComponent = _vueComponent//取到显示层vue实例 this.initPageConfig() this.getBanner() } initPageConfig() {//拷贝分页默认配置,并且不更改原常量 BannerModel.getInstance().pageConfig = Clone.shallowClone(DefaultPageConfig) } getBanner() {//请求处理,this.$crypto.setCrypto加密 this.$axios .get(ServerApi.shop.shopList, { params: { crypto: this.$crypto.setCrypto(BannerModel.getInstance().pageConfig) }, }).then(res => { switch (res.result) { case 1: BannerModel.getInstance().bannerList = res.data.list break; default: break; } }) } }
banner.vue页面展示
<template> <div class="swiper"> <mt-swipe :auto="3000"> <mt-swipe-item v-for="(item,index) in list" :key="index"> <img class="imgs" :src="imgPath+item.shopPic" @click="clickHandler(item)" /> </mt-swipe-item> </mt-swipe> </div> </template> <script> import { Swipe, SwipeItem } from "mint-ui"; import Config from "../../config/config"; import BannerBussiness from "./bussiness"; export default { name: "banner", data() { return { list: [],//图片列表 imgPath: Config.RequestPath//图片根路径 }; }, created() { this.init(); }, methods: { init() { new BannerBussiness(this);//初始化banner请求 }, clickHandler(_shop) {//banner点击跳转 this.$router.push({ name: "ShopTheme", query: { _type: _shop.shopType, _shopName: _shop.shopName } }); } } }; </script> <style lang="less" scoped> @import "../../style/init.less"; .imgs { .h(500); width: 100%; } .swiper { width: 100%; .h(500); } </style>
tableBar(导航栏)
在iconfont下载相对应的图标字体资源,我这里直接下载在style目录下,用类名的方式显示,在tabbar文件夹新建model.js用于取数据(其实这个可以放到config)
export default class TableBarModel { static MenuList = [{ name: "主页", path: "/Home", icon: "icon-shouye li iconfont" }, { name: "分类", path: "/Kind", icon: "icon-fenlei li iconfont" }, { name: "购物车", path: "/ShopCar", icon: "icon-daohang-gouwuche li iconfont" }, { name: "我的", path: "/Info", icon: "icon-wode li iconfont" } ] }
tabbar.vue ,通过列表
<template> <ul id="tab"> <router-link v-for="(item, index) in menuList" :key="index" :to="item.path" tag="li" :class="item.icon" active-class="change" replace > <br /> {{item.name}} </router-link> </ul> </template> <script> import tableBarModel from "./model"; export default { name: "tabBar", data() { return { menuList: tableBarModel.MenuList }; } }; </script> <style lang='less' scoped> @import "../../style/init.less"; #tab { display: flex; box-shadow: -1px 0 8px #999; z-index: 100; justify-content: space-around; position: fixed; bottom: 0; left: 0; right: 0; .h(130); .bcolor(); .li { .h(130); box-sizing: border-box; padding-top: unit(10 / @pxtorem, rem); width: 25%; text-align: center; .fontColorOff(); } .li::before { .f_s(58); } .li { .f_s(26); } .change { .fontColorOn(); } } </style>
title(标题)
title用h2简单做了个样式修改
<template> <div> <h2>{{title}}</h2> </div> </template> <script> export default { props: ["title"] }; </script> <style lang="less" scoped> @import "../../style/init.less"; h2 { .h2Font(); } </style>
shopItem(商品列表)
新建model.js存放可读写商品列表,vue组件实例和默认分页配置
export default class ItemModel {//存放可读写商品列表,vue组件实例和默认分页配置
constructor() { this._shopList = []//商品列表 this._pageConfig = {}//默认分页配置 } static getInstance() { //单例写法 if (!ItemModel._instance) { Object.defineProperty(ItemModel, "_instance", { value: new ItemModel() }) } return ItemModel._instance; } set vueComponent(val) { this._vueComponent = val } get vueComponent() { return this._vueComponent } set pageConfig(val) { this._pageConfig = val this._pageConfig.picType = 0//默认商品类型:单个商品 } get pageConfig() { return this._pageConfig } set shopList(val) { this._shopList = val this._vueComponent.list = this._shopList//获取到商品列表后重新渲染 } get shopList() { return this._shopList } }
新建做业务处理的bussiness.js
import Vue from 'vue'; import config from "../../config/config"; import ItemModel from "./model"; import Clone from "../../utils/clone"; const { DefaultPageConfig, ServerApi } = config export default class ItemBussiness extends Vue { constructor(_vueComponent) { super() ItemModel.getInstance().vueComponent = _vueComponent//Vue组件实例 this.initPageConfig(_vueComponent.shopType) this.getShopItem() } initPageConfig(_shopType) {//获取默认分页配置 ItemModel.getInstance().pageConfig = Clone.shallowClone(DefaultPageConfig) ItemModel.getInstance().pageConfig.shopType = _shopType } getShopItem() {//获取商品列表 this.$axios .get(ServerApi.shop.shopList, { params: { crypto: this.$crypto.setCrypto(ItemModel.getInstance().pageConfig) }, }).then(res => { switch (res.result) { case 1: ItemModel.getInstance().shopList = res.data.list//渲染页面 break; default: break; } }) } }
在shopItem.vue中进行列表渲染,并添加点击事件,跳转至商品详情页
<template> <ul class="more"> <li v-for="(item,index) in list" :key="index" @click="clickHandler(item)"> <img :src="imgPath+item.shopPic" alt :class="'imgs'+index" /> <span>{{item.shopName}} {{item.shopScale}}克</span> <div>¥{{item.shopPrice}}</div> </li> </ul> </template> <script> import ShopBussiness from "./bussiness"; import Config from "../../config/config"; export default { name: "shopItem", props: ["shopType"], data() { return { list: [], imgPath: Config.RequestPath }; }, mounted() { new ShopBussiness(this); }, methods: { clickHandler(data) { this.$router.push({ name: "ShopInfo", query: { ...data } }); } } }; </script> <style lang="less" scoped> @import "../../style/init.less"; .more { li { .shopItem(); } } </style>
最后的商品主题组件和商品列表相似,添加点击事件跳转至主题详情页
home页面
在page文件夹下新建home文件夹以及home.vue文件,将以上组件在home中引入并构成页面,效果如下
home.vue
<template> <div> <Top title="零食商贩"></Top> <div class="content"> <Banner></Banner> <H2 title="精选主题"></H2> <Theme></Theme> <H2 title="最近新品"></H2> <ShopItem :shopType="shopType"></ShopItem> </div> <TabBar></TabBar> </div> </template> <script> import TabBar from "../../components/tabBar/tabBar"; import Top from "../../components/top/top"; import Banner from "../../components/banner/banner"; import Theme from "../../components/theme/theme"; import ShopItem from "../../components/shopItem/shopItem"; import H2 from "../../components/h2/h2"; export default { name: "Home", data() { return { shopType: "" }; }, components: { Top, H2, Banner, Theme, ShopItem, TabBar } }; </script> <style lang="less" scoped> @import "../../style/init.less"; </style>