# vue_cms **Repository Path**: UniverseKing/vue_cms ## Basic Information - **Project Name**: vue_cms - **Description**: No description available - **Primary Language**: JavaScript - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 25 - **Forks**: 8 - **Created**: 2018-01-14 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # vue_cms ## webpack项目基本环境搭建 ```JavaScript . ├── .babelrc // babel配置文件 ├── .gitignore // git忽略文件 ├── LICENSE // 版权 ├── README.md // 文档 ├── dist // 输出目录 ├── package.json // npm配置文件 ├── src // 源码目录 │   ├── index.html // 首页模板 │   └── main.js // 程序入口文件 └── webpack.config.js // webpack配置文件 ``` ## 集成Vue 1.新建App.vue文件 ```JavaScript ``` 2. `main.js`中集成`Vue` ```JavaScript // 1. 导入vue import Vue from 'vue' // 2. 导入app.vue import app from './App.vue' // 3. 创建vue实例并渲染app组件 new Vue({ el: '#app', render: c => c(app) }) ``` ## 制作项目首页Header和Tabbar 1. 导入并注册`mint-ui`中的`header`组件 ```JavaScript // 導入mint-ui中的header組件 import { Header } from 'mint-ui' // 导入mint-ui的样式 import 'mint-ui/lib/style.css' // 將組件註冊為Vue的全局組件 Vue.omponent(Header.name, Header) ``` 2. 在`App.vue`中使用`mt-header`组件 ```HTML
.app-container{ padding-top: 40px; } ``` 4. `main.js`中导入`mui`样式 ```JavaScript // 导入mui样式 import './lib/mui/css/mui.min.css' ``` 5. 在`App.vue`中加入`MUI`的`tabbar`结构代码 ## 完成底部Tabbar购物车小图标替换 1. mui的css文件夹中引入icon-extra.css文件并在main.js中导入 ```JavaScript // 导入mui字体图标样式 import './lib/mui/css/icons-extra.css' ``` 2. mui的fonts文件夹中引入mui-icon-extra.tff文件 3. 购物车模块span标签上添加样式类 ```HTML 0 购物车 ``` ## 集成路由vue-router并实现tabbar点击高亮切换 1. 在`src`目录创建`router`文件夹,并创建`router.js`,导出`router`实例 ```JavaScript import VueRouter from 'vue-router' // 创建路由对象 var router = new VueRouter({ routes: [ ] }) // 导出路由对象 export default router ``` 2. 在`main.js`中集成`vue-router` ```JavaScript // 1.1 导入vue-router库 import VueRouter from 'vue-router' // 1.2 安装vue-router Vue.use(VueRouter) // 1.3 导入vue-router实例 import router from './router/router.js' // 1.4 将router实例挂载到vue new Vue({ el: '#app', render: c => c(app), router:router }) ``` 3. 将`App.vue`中`tabbar`的a标签修改为`` 4. 在router.js中vue-router实例内添加linkActiveClass属性,指定高亮类名 ```JavaScript var router = new VueRouter({ linkActiveClass: 'mui-active',// 修改路由默认高亮类,默认类名为link-active-class routes: [ ] }) ``` ## 实现Tabbar路由切换 1. 在src目录下创建components/tabbar文件夹,再创建tabbar对应的4个vue组件 2. 在router.js中导入四个组件并定义路由规则 ```JavaScript var router = new VueRouter({ linkActiveClass: 'mui-active', routes: [ { path: '/home', component: HomeContainer }, { path: '/member', component: MemberContainer }, { path: '/shopcar', component: ShopCarContainer }, { path: '/search', component: SearchContainer } ] }) ``` 3. 在App.vue中修改`router-link`标签上的to属性为路由规则中的path路径 ```HTML 首页 ``` 4. 在App.vue的Heade区域下面添加路由占位标签`` ```HTML ``` ## 完成首页轮播图样式布局 1. main.js中导入并注册mint-ui的`Swipe, SwipeItem`组件 ```JavaScript import { Swipe, SwipeItem } from 'mint-ui' Vue.component(Swipe.name, Swipe) Vue.component(SwipeItem.name, SwipeItem) ``` 2. `HomeContainer.vue`中书写轮播组件代码 ```HTML 1 2 3 ``` 3. 在组件的style中书写样式 ```SCSS .mint-swipe { height: 200px; .mint-swipe-item { &:nth-child(1) { background-color: skyblue; } &:nth-child(2) { background-color: pink; } &:nth-child(3) { background-color: cornflowerblue; } } } ``` ## 集成VueResource实现轮播图数据请求 1. 安装vue-resource ```JavaScript npm i vue-resource --save ``` 2. 在main.js中集成`vue-resource` ```JavaScript // 2. 集成VueResource // 2.1 导入vue-resource import VueResource from 'vue-resource' // 2.2 安装vue-resource Vue.use(VueResource) ``` 3. 在`HomeContainer.vue`中发起数据请求 ```JavaScript export default { data() { return { imageList: [] // 定义存放图片的数组 }; }, created() { // 发起获取轮播图的数据请求 this.getlunbo(); }, methods: { getlunbo() { // 使用vue-resource的get请求方式发送数据请求 this.$http.get("http://vue.studyit.io/api/getlunbo").then(result => { if (result.body.status === 0) { // 将请求回来的数据赋值给存放图片的数组 this.imageList = result.body.message; } else { Toast("数据加载失败..."); } }); } } }; ``` 4. 使用v-for指令循环渲染`mt-swipe-item` ```HTML ``` 5. 给img添加样式 ```CSS img{ width: 100%; height: 100%; } ``` ## 完成首页九宫格布局 1. 使用MUI九宫格布局结构 ```HTML ``` 2. 设置样式 ```SCSS .mui-grid-view.mui-grid-9 { background-color: #fff; border: none; img { width: 60px; height: 60px; } .mui-media-body { font-size: 13px; } } .mui-grid-view.mui-grid-9 .mui-table-view-cell { border: 0; } ``` ## 实现路由切换动画 1. 用`transition`标签包裹`router-view` ```HTML ``` 2. 使用vue提供的过渡类控制进入离开动画样式 ```CSS .app-container{ padding-top: 40px; overflow-x: hidden; /*隐藏超出屏幕部分*/ } .v-enter { opacity: 0; transform: translateX(100%); /*进入起点在屏幕100%部分,即屏幕最右*/ } .v-leave-to { opacity: 0; transform: translateX(-100%); /*离开的终点在屏幕-100%部分*/ position: absolute; } .v-enter-active, .v-leave-active { transition: all 0.5s ease; } ``` ## 实现新闻资讯路由跳转 1. 在components/news文件夹下面创建`NewsList.vue`组件 2. 在`router.js`导入`NewsList.vue`组件中并添加路由规则 ```JavaScript import NewsList from '../components/news/NewsList.vue' { path: '/home/newslist', component: NewsList } ``` 3. 在`HomeContainer.vue`组件中修改**新闻资讯**图标的a标签为`router-link`并设置to属性为路由规则中的path属性值 ```HTML
新闻资讯
``` ## 实现新闻资讯界面布局和样式 1. 使用MUI中的`media-list.html`图文列表 - 缩略图居左 ```HTML ``` 2. 设置样式 ```CSS .mui-table-view { li { h1 { font-size: 14px; } .mui-ellipsis { font-size: 12px; color: #0094ff; display: flex; justify-content: space-between; } } } ``` ## 实现新闻资讯列表数据动态化 1. 在main.js中配置vue-resource请求根域名 ```JavaScript // 2.3 全局配置请求的URL根域名 Vue.http.options.root = "http://vue.studyit.io" ``` 2. 在`NewsList.vue`组件中发送数据请求 ```JavaScript import { Toast } from "mint-ui"; export default { data() { return { newslist: [] // 新闻列表 }; }, created() { this.getNewsList(); }, methods: { getNewsList() { // 获取新闻列表 this.$http.get("api/getnewslist").then(result => { if (result.body.status === 0) { // 如果没有失败,应该把数据保存到 data 上 this.newslist = result.body.message; } else { Toast("获取新闻列表失败!"); } }); } } } ``` 3. 使用v-for循环渲染图文列表,并绑定数据 ```HTML
  • {{item.title}}

    发表时间:{{item.add_time}} 点击:{{item.click}}次

  • ``` ## 实现新闻列表时间格式化 1. 安装时间格式化组件库moment ```JavaScript npm i moment --save ``` 2. 在main.js中定义全局过滤器进行时间格式化 ```JavaScript // 导入时间格式化组件库moment import moment from 'moment' // 定义时间格式化全局过滤器 Vue.filter('dateFormat', function (dateStr, pattern = "YYYY-MM-DD HH:mm:ss") { return moment(dateStr).format(pattern) }) ``` 3. 在新闻列表页面使用过滤器 ```HTML 发表时间:{{item.add_time | dateFormat('YYYY-MM-DD')}} ``` ## 实现新闻列表点击跳转到新闻详情页面 1. 创建`NewsInfo.vue`组件 2. 在`router.js`中导入组件并添加路由规则 ```JavaScript import NewsInfo from '../components/news/NewsInfo.vue' { path: '/home/newsinfo/:id', component: NewsInfo } ``` 3. 修改新闻列表中的a标签为`router-link`,并设置to属性的值为跳转路径 ```HTML ``` 4. 在`NewsInfo.vue`组件data中定义属性id,用于接收路由传递的参数 ```JavaScript export default { data() { return { id: this.$route.params.id }; } } ``` ## 完成新闻详情页面布局及数据动态化 > [深度作用选择器](https://vue-loader.vuejs.org/zh-cn/features/scoped-css.html) 1. HTML结构布局 ```HTML

    {{ newsinfo.title }}

    发表时间:{{ newsinfo.add_time | dateFormat }} 点击:{{ newsinfo.click }}次


    ``` 2. CSS样式 ```SCSS .newsinfo-container { padding: 0 4px; .title { font-size: 16px; text-align: center; margin: 15px 0; color: red; } .subtitle { font-size: 13px; color: #226aff; display: flex; justify-content: space-between; } .content { img { width: 100%; } } } ``` 3. 请求数据 ```JavaScript export default { data() { return { id: this.$route.params.id, newsinfo: {} // 新闻对象 }; }, created() { this.getNewsInfo(); }, methods: { getNewsInfo() { // 获取新闻详情 this.$http.get("api/getnew/" + this.id).then(result => { if (result.body.status === 0) { this.newsinfo = result.body.message[0]; } else { Toast("获取新闻失败!"); } }); } } }; ``` ## 封装评论子组件并集成到新闻详情组件中 1. 在`components/subcomponents`文件夹中创建`component.vue`组件 2. 在`NewsInfo.vue`组件中导入并注册`component.vue`组件 ```JavaScript import comment from '../subcomponents/comment.vue' // 注册子组件 components:{ "comment-box":comment } ``` 3. 使用子组件 ```HTML ``` 4. 完成子组件布局和样式 ## 获取评论子组件真实数据 1. 使用父子组件传值将数据id传到子组件中 ```JavaScript // 1 NewsInfo.vue中使用comment组件时绑定id属性传值 // 2. comment.vue组件对象中定义props:["id"]接收值 props: ["id"] ``` 2. 使用vue-resource发起数据请求 ```JavaScript import { Toast } from "mint-ui"; export default { data() { return { pageIndex: 1, // 默认展示第一页数据 comments: [] // 所有的评论数据 }; }, created() { this.getComments(); }, methods: { getComments() { // 获取评论 this.$http .get("api/getcomments/" + this.id + "?pageindex=" + this.pageIndex) .then(result => { if (result.body.status === 0) { this.comments = result.body.message; } else { Toast("获取评论失败!"); } }); } }, props: ["id"] }; ``` 3. 使用vue指令在HTML结构中绑定数据 ## 完成评论组件点击加载更多功能 1. 注册点击事件 ```HTML 加载更多 ``` 2. 自增pageIndx,然后重新发送数据请求 ```JavaScript getMore() { // 加载更多 this.pageIndex++; this.getComments(); } ``` 3. 将新请求回来的数据和以前的数据进行拼接 ```JavaScript this.comments = this.comments.concat(result.body.message); ``` ## 完成发表评论功能 1. 双向数据绑定获取评论框内容并注册点击事件 ```HTML 发表评论 ``` 2. `main.js`中全局设置vue-resource post数据请求格式 ```JavaScript // 2.4 全局设置 post 时候表单数据格式组织形式 application/x-www-form-urlencoded Vue.http.options.emulateJSON = true; ``` 3. 发送数据请求 ```JavaScript postComment() { // 校验是否为空内容 if (this.msg.trim().length === 0) { return Toast("评论内容不能为空!"); } this.$http .post("api/postcomment/" + this.$route.params.id, { content: this.msg.trim() }) .then(function(result) { if (result.body.status === 0) { // 数据提交成功后,将发送的数据组装成一个消息对象,拼接到当前数据最前面 var cmt = { user_name: "匿名用户", add_time: Date.now(), content: this.msg.trim() }; this.comments.unshift(cmt); this.msg = ""; } }); } ``` ## 完成图片列表顶部导航布局 1. 修改`HomeContainer.vue`图片分享按钮的a标签为`router-link`并设置to属性为'/home/photolist' ```HTML
  • 图片分享
  • ``` 2. 新建`photos/PhotoList.vue`文件,在`router.js`中配置路由规则 ```JavaScript import PhotoList from '../components/photos/PhotoList.vue' { path: '/home/photolist', component: PhotoList } ``` 3. 在`PhotoList.vue`中引用MUI中的'tab-top-webview-main.html'中的顶部代码,并去掉`mui-fullscreen`样式类 ```HTML ``` ## 解决导入MUIjs文件后产生的问题 1. `PhotoList.vue`中导入MUIjs文件,实现顶部导航滚动效果 ```JavaScript import mui from "../../lib/mui/js/mui.min.js"; mounted() { // 需要在组件的 mounted 事件钩子中,注册 mui 的 scroll 滚动事件 mui(".mui-scroll-wrapper").scroll({ deceleration: 0.0005 //flick 减速系数,系数越大,滚动速度越慢,滚动距离越小,默认值0.0006 }); } ``` 2. 安装`babel-plugin-transform-remove-strict-mode`移除webpack打包js后默认加上的严格模式 > [babel-plugin-transform-remove-strict-mode](https://github.com/genify/babel-plugin-transform-remove-strict-mode) ```JavaScript // 1. 安装babel-plugin-transform-remove-strict-mode cnpm i babel-plugin-transform-remove-strict-mode --save-dev // 2. 在.babelrc文件的plugins节点中配置 "transform-remove-strict-mode" ``` 3. 加入样式消除chrome控制台警告 > 原因:(是chrome为了提高页面的滑动流畅度而新折腾出来的一个东西) > http://www.cnblogs.com/pearl07/p/6589114.html >https://developer.mozilla.org/zh-CN/docs/Web/CSS/touch-action ```CSS * { touch-action: pan-y; } ``` 4. 修改tabbar样式类名,解决tabbar不能点击问题 ```CSS /* 1. 将tabbar中的mui-tab-item改为mui-tab-item-llb */ /* 2. 在组件中加入以下样式*/ .mui-bar-tab .mui-tab-item-llb.mui-active { color: #007aff; } .mui-bar-tab .mui-tab-item-llb { display: table-cell; overflow: hidden; width: 1%; height: 50px; text-align: center; vertical-align: middle; white-space: nowrap; text-overflow: ellipsis; color: #929292; } .mui-bar-tab .mui-tab-item-llb .mui-icon { top: 3px; width: 24px; height: 24px; padding-top: 0; padding-bottom: 0; } .mui-bar-tab .mui-tab-item-llb .mui-icon~.mui-tab-label { font-size: 11px; display: block; overflow: hidden; text-overflow: ellipsis; } ``` ## 渲染图片分类真实数据 1. 发送数据请求 ```JavaScript data() { return { cates: [] // 所有分类的列表数组 }; }, created(){ this.getAllCategory(); }, methods: { getAllCategory() { // 获取所有的图片分类 this.$http.get("api/getimgcategory").then(result => { if (result.body.status === 0) { // 手动拼接出一个最完整的 分类列表 result.body.message.unshift({ title: "全部", id: 0 }); this.cates = result.body.message; } }); } } ``` 2. `v-for`渲染界面 ```HTML ``` ## 渲染图片列表并实现懒加载图片 > 如果使用mint-ui中的懒加载指令实现图片懒加载效果,需要全局注册mint-ui,即 ```JavaScript import MintUi from 'mint-ui' Vue.use(MintUi) import 'mint-ui/lib/style.css' ``` 1. 使用mint-ui中的v-lazy搭建页面结构 ```HTML ``` 2. 书写样式美化界面 ```SCSS .photo-list { list-style: none; margin: 0; padding: 10px; padding-bottom: 0; li { background-color: #ccc; text-align: center; margin-bottom: 10px; box-shadow: 0 0 9px #999; position: relative; img { width: 100%; vertical-align: middle; } /*图片懒加载样式 mint-ui提供*/ img[lazy="loading"] { width: 40px; height: 300px; margin: auto; } .info { color: white; text-align: left; position: absolute; bottom: 0; background-color: rgba(0, 0, 0, 0.4); max-height: 84px; .info-title { font-size: 14px; } .info-body { font-size: 13px; } } } ``` 3. 发送数据请求获取数据 ```JavaScript getPhotoListByCateId(cateId) { // 根据 分类Id,获取图片列表 this.$http.get("api/getimages/" + cateId).then(result => { if (result.body.status === 0) { this.list = result.body.message; } }); } ``` ## 实现图片详情的页面跳转和数据加载及评论子组件集成 1. 更改`PhotoList.vue`中的li标签为`router-link`,并设置to属性 ```HTML ``` 2. 创建`PhotoInfo.vue`组件,添加路由匹配规则 ```JavaScript import PhotoInfo from '../components/photos/PhotoInfo.vue' { path: '/home/photoinfo/:id', component: PhotoInfo } ``` 3. 搭建`PhotoInfo.vue`页面结构 ```HTML

    {{ photoinfo.title }}

    发表时间:{{ photoinfo.add_time | dateFormat }} 点击:{{ photoinfo.click }}次


    ``` 4. 书写样式 ```SCSS .photoinfo-container { padding: 3px; h3 { color: #26a2ff; font-size: 15px; text-align: center; margin: 15px 0; } .subtitle { display: flex; justify-content: space-between; font-size: 13px; } .content { font-size: 13px; line-height: 30px; } } ``` 5. 获取数据,渲染页面 ```JavaScript data() { return { id: this.$route.params.id, // 从路由中获取到的 图片Id photoinfo: {} // 图片详情 }; }, created() { this.getPhotoInfo(); }, methods: { getPhotoInfo() { // 获取图片的详情 this.$http.get("api/getimageInfo/" + this.id).then(result => { if (result.body.status === 0) { this.photoinfo = result.body.message[0]; } }); } }, // 注册评论子组件 components: { // 注册 评论子组件 "cmt-box": comment } ``` ## 图片详情缩略图预览功能 > [vue-preview](https://www.npmjs.com/package/vue-preview) 1. 安装vue-preview插件,在main.js中导入并注册 ```JavaScript cnpm i vue-preview --save import VuePreview from 'vue-preview' Vue.use(VuePreview) ``` 2. 引入结构代码并书写样式 ```HTML
    ``` ```CSS .thumbs { img { margin: 10px; box-shadow: 0 0 8px #999; } } ``` 3. 获取缩略图数据,渲染界面 ```JavaScript getThumbs() { // 获取缩略图 this.$http.get("api/getthumimages/" + this.id).then(result => { if (result.body.status === 0) { // 循环每个图片数据,补全图片的宽和高 result.body.message.forEach(item => { item.w = 600; item.h = 400; }); // 把完整的数据保存到 list 中 this.list = result.body.message; } }); } ``` ## 商品列表静态布局 1. 改造*商品购买*按钮为`router-link` ```HTML
  • 商品购买
  • ``` 2. 新建'goods/GoodsList.vue'组件,并添加路由规则 ```JavaScript import GoodsList from '../components/goods/GoodsList.vue' { path: '/home/goodslist', component: GoodsList } ``` 3. 书写静态结构 ```HTML

    小米(Mi)小米Note 16G双网通版

    ¥899 ¥999

    热卖中 剩60件

    ``` 4. 书写CSS ```SCSS ``` ## 商品列表真实数据获取及上拉加载更多下拉刷新 1. 使用mint-ui的`mt-loadmore`组件包裹列表 ```HTML

    {{ item.title }}

    ¥{{ item.sell_price }} ¥{{ item.market_price }}

    热卖中 剩{{ item.stock_quantity }}件

    ``` 2. 定义数据请求方法请求数据 ```JavaScript getGoodsList() { // 获取商品列表 this.$http .get("api/getgoods?pageindex=" + this.pageindex) .then(result => { if (result.body.status != 0) { Toast(result.body.message); return; } if (this.pageindex == 1) { // 重置下拉状态 this.$refs.loadmore.onTopLoaded(); // 直接赋值第一页的数据 this.goodslist = result.body.message; } else { // 重置上拉状态 if (result.body.message.length == 0) { this.allLoaded = true; } // 重置上拉状态 this.$refs.loadmore.onBottomLoaded(); this.goodslist = this.goodslist.concat(result.body.message); } }); } ``` 3. 定义上拉和下拉时触发的方法 ```JavaScript // 下拉时触发 请求最新数据 loadTop() { console.log("下拉"); this.pageindex = 1; this.getGoodsList(); }, //上拉 loadBottom() { console.log("上拉"); this.pageindex++; this.getGoodsList(); } ``` ## 编程式导航跳转商品详情页面 1. 给需要点击跳转的div注册事件 ```HTML
    ``` 2. 使用vue-router提供的编程式导航跳转 ```JavaScript goDetail(id) { // 1. 最简单的 // this.$router.push("/home/goodsinfo/" + id); // 2. 传递对象 // this.$router.push({ path: "/home/goodsinfo/" + id }); // 3. 传递命名的路由(需要在定义的路由规则对象中添加name属性) this.$router.push({ name: "goodsinfo", params: { id } }); } ``` 3. 使用MUI提供的卡片视图代码段布局 ```HTML
    御剑乘风来 除魔天地间
    页眉
    御剑乘风来 除魔天地间
    页眉
    御剑乘风来 除魔天地间
    ``` ```CSS .goodsinfo-container { background-color: #eee; overflow: hidden; } ``` ## 封装轮播图组件并在首页和商品详情页面使用 1. 新建一个`swiper.vue`文件,将`HomeContainer.vue`中的轮播图代码抽取出来 ```JavaScript ``` 2. 将首页的轮播图代码替换成封装好的swiper组件使用 ```JavaScript // 1. 导入swiper组件 import swiper from '../subcomponents/swiper.vue' // 2. 注册成子组件 components:{ swiper } // 3. 使用组件 ``` 3. 在商品详情页面集成swiper组件 ```HTML
    ``` 4. 定义存放数据的数组并发送请求 ```JavaScript data() { return { id: this.$route.params.id, // 将路由参数对象中的 id 挂载到 data , 方便后期调用 lunbotu: [] // 轮播图的数据 }; }, created() { this.getLunbotu(); }, methods: { getLunbotu() { this.$http.get("api/getthumimages/" + this.id).then(result => { if (result.body.status === 0) { // 先循环轮播图数组的每一项,为 item 添加 img 属性,因为 轮播图 组件中,只认识 item.img, 不认识 item.src result.body.message.forEach(item => { item.img = item.src; }); this.lunbotu = result.body.message; } }); } }, components: { swiper } ``` ## 解决轮播图宽度问题 1. 给轮播图组件定义一个属性控制图片的宽度是否应该设置100%,该属性由父组件传递进来 ```JavaScript props: ["lunbotuList","isfull"] ``` 2. 通过该属性控制是否给img添加样式类`full` ```HTML ``` ```CSS .full { width: 100%; } ``` 3. 首页和商品详情页面使用swiper组件时传递不同的值 ```HTML ``` ## 商品购买区域结构样式 1. 结构 ```HTML
    商品名称

    市场价:¥2399  销售价:¥1999

    购买数量:

    立即购买 加入购物车

    ``` 2. 定义购买数量numbox组件 ```JavaScript ``` 3. `GoodsInfo.vue`中集成组件 ```JavaScript // 1. 导入组件 import numbox from "../subcomponents/goodsinfo_numbox.vue"; // 2. 注册组件 components: { numbox } // 3. 使用组件

    购买数量:

    ``` ## 渲染商品详情页面数据 1. 商品参数区域结构 ```HTML
    商品参数

    商品货号:

    库存情况:

    上架时间:

    ``` 2. 样式 ```SCSS .goodsinfo-container { background-color: #eee; overflow: hidden; .now_price { color: red; font-size: 16px; font-weight: bold; } .mui-card-footer { display: block; button { margin: 15px 0; } } } ``` 3. 发送请求获取数据 ```JavaScript getGoodsInfo() { // 获取商品的信息 this.$http.get("api/goods/getinfo/" + this.id).then(result => { if (result.body.status === 0) { this.goodsinfo = result.body.message[0]; } }); } ``` ## 完成商品详情中的图文介绍和商品评论 1. 绑定跳转事件 ```HTML ``` 2. 编程式导航进行跳转 ```JavaScript goDesc(id) { // 点击使用编程式导航跳转到 图文介绍页面 this.$router.push({ name: "goodsdesc", params: { id } }); }, goComment(id) { // 点击跳转到 评论页面 this.$router.push({ name: "goodscomment", params: { id } }); } ``` 3. 创建组件并注册路由规则 ```JavaScript import GoodsDesc from '../components/goods/GoodsDesc.vue' import GoodsComment from '../components/goods/GoodsComment.vue' { path: '/goods/goodsdesc/:id', component: GoodsDesc ,name:'goodsdesc'}, { path: '/goods/goodscomment/:id', component: GoodsComment ,name:'goodscomment'} ``` 4. 发送请求并渲染组件 ## 实现加入购物车小球动画 1. 小球结构及样式 ```HTML
    ``` ```SCSS .ball { width: 15px; height: 15px; border-radius: 50%; background-color: red; position: absolute; z-index: 99; top: 390px; left: 146px; } ``` 2. 动画的钩子函数实现 ```JavaScript // 1. 使用transition标签包裹并绑定钩子函数
    // 2. 书写钩子函数动画逻辑 beforeEnter(el) { el.style.transform = "translate(0, 0)"; }, enter(el, done) { el.offsetWidth; el.style.transform = "translate(93px,230px)"; el.style.transition = "all 0.5s cubic-bezier(.4,-0.3,1,.68)"; done(); }, afterEnter(el) { this.ballFlag = !this.ballFlag; } ``` 3. 加入购物车按钮绑定事件 ```JavaScript addToShopCar() { // 添加到购物车 this.ballFlag = !this.ballFlag; } ``` ## 优化小球动画结束位置 1. 给App.vue中的徽章添加id ```HTML 0 ``` 2. 给小球添加ref属性 ```HTML
    ``` 3. 获取小球和徽章的位置,计算横纵坐标差 ```JavaScript // 获取小球的 在页面中的位置 const ballPosition = this.$refs.ball.getBoundingClientRect(); // 获取 徽标 在页面中的位置 const badgePosition = document .getElementById("badge") .getBoundingClientRect(); const xDist = badgePosition.left - ballPosition.left; const yDist = badgePosition.top - ballPosition.top; el.style.transform = `translate(${xDist}px, ${yDist}px)`; el.style.transition = "all 0.5s cubic-bezier(.4,-0.3,1,.68)"; ``` ## 子组件向父组件传递购买数量值 1. 子组件中改变数量时使用this.$emit发布事件 ```JavaScript methods: { countChanged() { // 每当 文本框的数据被修改的时候,立即把 最新的数据,通过事件调用,传递给父组件 // console.log(this.$refs.numbox.value); this.$emit("getcount", parseInt(this.$refs.numbox.value)); } } ``` 2. 父组件中使用子组件时绑定事件,事件名称和子组件发布事件名称相同 ```HTML

    购买数量:

    ``` 3. 父组件中定义事件处理函数接收子组件的传值 ```JavaScript getSelectedCount(count) { // 当子组件把 选中的数量传递给父组件的时候,把选中的值保存到 data 上 this.selectedCount = count; console.log("父组件拿到的数量值为: " + this.selectedCount); } ``` ## 设置购买数量组件最大值 1. 使用父组件向子组件传值的方式将最大值传递过去 ```JavaScript // 1. 父组件中使用子组件时绑定属性传值

    购买数量:

    // 2. 子组件中定义props接收值 props: ["max"] ``` 2. 子组件使用watch监视max的变化并使用MUI的jsAPI设置最大值 ```JavaScript watch: { // 属性监听 max: function(newVal, oldVal) { // 使用 JS API 设置 numbox 的最大值 mui(".mui-numbox") .numbox() .setOption("max", newVal); } } ``` ## 集成Vuex 1. 安装Vuex ```JavaScript cnpm i vuex --save ``` 2. `main.js`中实例化vuex ```JavaScript // 注册 vuex import Vuex from 'vuex' Vue.use(Vuex) var store = new Vuex.Store({ state: { // this.$store.state.*** }, mutations: { // this.$store.commit('方法的名称', '按需传递唯一的参数') }, getters: { // this.$store.getters.*** // 相当于 计算属性,也相当于 filters } }) ``` 3. 将store实例挂载到vue实例上 ```JavaScript new Vue({ el: '#app', render: c => c(app), router, // 将router实例挂载到vue store // 挂载 store 状态管理对象 }) ``` ## 使用vuex实现加入购物车保存数据 1. 在state中定义数组car用于保存数据 ```JavaScript state: { // this.$store.state.*** car: [] // 将 购物车中的商品的数据,用一个数组存储起来,在 car 数组中,存储一些商品的对象, 咱们可以暂时将这个商品对象,设计成这个样子 // { id:商品的id, count: 要购买的数量, price: 商品的单价,selected: false } }, ``` 2. 在mutations中定义方法实现加入购物车改变数据 ```JavaScript addToCar(state, goodsinfo) { // 点击加入购物车,把商品信息,保存到 store 中的 car 上 // 分析: // 1. 如果购物车中,之前就已经有这个对应的商品了,那么,只需要更新数量 // 2. 如果没有,则直接把 商品数据,push 到 car 中即可 // 假设 在购物车中,没有找到对应的商品 var flag = false state.car.some(item => { if (item.id == goodsinfo.id) { item.count += parseInt(goodsinfo.count) flag = true return true } }) // 如果最终,循环完毕,得到的 flag 还是 false,则把商品数据直接 push 到 购物车中 if (!flag) { state.car.push(goodsinfo) } } ``` 3. 在`GoodsInfo.vue`中点击加入购物车时,拼装数据触发store中改变数据的方法 ```JavaScript var goodsinfo = { id: this.id, count: this.selectedCount, price: this.goodsinfo.sell_price, selected: true }; // 调用 store 中的 mutations 来将商品加入购物车 this.$store.commit("addToCar", goodsinfo); ``` ## getters实现加入购物车徽章变化 1. store中定义getters计算购物车总数量 ```JavaScript getters:{ getAllCount(state) { var c = 0; state.car.forEach(item => { c += item.count }) return c } } ``` 2. 使用this.$store.getters.getAllCount显示数据 ```HTML {{this.$store.getters.getAllCount}} ``` ## 实现购物车数据的本地持久化存储 1. 进入网站先从本地localstorage中获取数据并设置给state中的car ```JavaScript var car = JSON.parse(localStorage.getItem('car') || '[]') state: { car: car } ``` 2. 改变数据时将数据存储到localstorage ```JavaScript // 当 更新 car 之后,把 car 数组,存储到 本地的 localStorage 中 localStorage.setItem('car', JSON.stringify(state.car)) ``` ## 购物车列表界面布局 1. HTML结构 ```HTML

    华为(HUAWEI)荣耀6Plus 16G双4G版

    ¥2199 删除

    总计(不含运费)

    已勾选商品 件, 总价

    去结算
    ``` 2.样式 ```SCSS .shopcar-container { background-color: #eee; overflow: hidden; .goods-list { .mui-card-content-inner { display: flex; align-items: center; } img { width: 60px; } h1 { font-size: 13px; } .info { display: flex; flex-direction: column; justify-content: space-between; .price { color: red; font-weight: bold; } } } .jiesuan { display: flex; justify-content: space-between; align-items: center; .red { color: red; font-weight: bold; font-size: 16px; } } } ``` ## 获取商品数据并渲染到界面 1. 获取商品数据 ```JavaScript created(){ this.getGoodsList() }, methods: { getGoodsList() { // 1. 获取到 store 中所有的商品的Id,然后拼接出一个 用逗号分隔的 字符串 var idArr = []; this.$store.state.car.forEach(item => idArr.push(item.id)); // 如果 购物车中没有商品,则直接返回,不需要请求数据接口,否则会报错 if (idArr.length <= 0) { return; } // 获取购物车商品列表 this.$http .get("api/goods/getshopcarlist/" + idArr.join(",")) .then(result => { if (result.body.status === 0) { this.goodslist = result.body.message; } }); } } ``` 2. 渲染界面 ## 购物车列表中初始化数量值 1. getters中定义方法创造出需要使用的数据结构 ```JavaScript // 将[{"id":"87","count":3,"price":2195,"selected":true},{"id":89,"count":4,"price":2199,"selected":true}] 变成 {87:3,89:4} getGoodsCount(state) { var o = {} state.car.forEach(item => { o[item.id] = item.count }) return o } ``` 2. 使用numbox时传递数量值 ```JavaScript ``` 3. 在numbox组件内部获取initcount使用 ```JavaScript props:["initcount"] ``` ## 实现购物车商品数量改变同步到store中 1. 在mutations中定义用于同步数量的方法 ```JavaScript updateGoodsInfo(state, goodsinfo) { // 修改购物车中商品的数量值 state.car.some(item => { if (item.id == goodsinfo.id) { item.count = parseInt(goodsinfo.count) return true } }) // 当修改完商品的数量,把最新的购物车数据,保存到 本地存储中 localStorage.setItem('car', JSON.stringify(state.car)) } ``` 2. 将商品id使用父子组件传值传递到numbox ```JavaScript props:['goodsid'] ``` 3. 购物车数量发生变化时触发`updateGoodsInfo` ```JavaScript countChanged() { // 每当数量值改变,则立即把最新的数量同步到 购物车的 store 中,覆盖之前的数量值 this.$store.commit("updateGoodsInfo", { id: this.goodsid, count: this.$refs.numbox.value }); } ``` ## 实现购物车商品数据的删除 1. 在mutations中定义删除store中数据的方法 ```JavaScript removeFormCar(state, id) { // 根据Id,从store 中的购物车中删除对应的那条商品数据 state.car.some((item, i) => { if (item.id == id) { state.car.splice(i, 1) return true; } }) // 将删除完毕后的,最新的购物车数据,同步到 本地存储中 localStorage.setItem('car', JSON.stringify(state.car)) } ``` 2. 给a标签注册删除事件 ```HTML 删除 ``` 3. 定义删除的函数 ```JavaScript remove(id, index) { // 点击删除,把商品从 store 中根据 传递的 Id 删除,同时,把 当前组件中的 goodslist 中,对应要删除的那个商品,使用 index 来删除 this.goodslist.splice(index, 1); this.$store.commit("removeFormCar", id); } ``` ## 实现勾选数量和总价的自动计算 1. 把store中的选中状态同步到页面 ```JavaScript // 1. getters中定义 计算选中的商品 的方法 getGoodsSelected(state) { var o = {} state.car.forEach(item => { o[item.id] = item.selected }) return o } // 2. mt-switch组件上绑定选中状态 ``` 2. 将页面选中状态同步到store中保存 ```JavaScript // 1. mutations中定义更新选中状态的方法 updateGoodsSelected(state, info) { state.car.some(item => { if (item.id == info.id) { item.selected = info.selected } }) // 把最新的 所有购物车商品的状态保存到 store 中去 localStorage.setItem('car', JSON.stringify(state.car)) } // 2. mt-switch绑定点击事件 // 3. 定义事件处理函数 selectedChanged(id, val) { // 每当点击开关,把最新的 快关状态,同步到 store 中 this.$store.commit("updateGoodsSelected", { id, selected: val }); } ``` 3. getters中定义计算勾选商品件数和总价格的方法 ```JavaScript getGoodsCountAndAmount(state) { var o = { count: 0, // 勾选的数量 amount: 0 // 勾选的总价 } state.car.forEach(item => { if (item.selected) { o.count += item.count o.amount += item.price * item.count } }) return o } ``` 4. 使用getters中的方法显示数据 ```HTML

    已勾选商品 {{ $store.getters.getGoodsCountAndAmount.count }} 件, 总价 ¥{{ $store.getters.getGoodsCountAndAmount.amount }}

    ``` ## 实现项目的返回按钮功能 1. 使用mint-ui的返回按钮组件 ```HTML 返回 ``` 2. 使用vue-router提供的`this.$router.go(-1)`方法实现返回 ```JavaScript export default { data() { return { flag: false }; }, created() { this.flag = this.$route.path === "/home" ? false : true; }, methods: { goBack() { // 点击后退 this.$router.go(-1); } }, watch: { "$route.path": function(newVal) { if (newVal === "/home") { this.flag = false; } else { this.flag = true; } } } }; ```