实战Vue:Mint UI移动端购物商城

文章目录

        • UI组件库
        • 下载安装配置UI组件库
        • 使用mint-ui组件库中组件
          • 短消息提示框
          • iconfont
          • 确认框
          • 创建空组件实现提示框案例
          • mint-ui表单组件
            • 输入框
            • 开关组件
            • 单选列表
            • 表单组件案例
          • mint-ui组件库面板底部导航条
            • 面板底部导航条案例
            • 组件中数据传递(父组件传递数据子组件) 父组件在模板中调用子组件
            • 微信消息列表功能案例
          • 用户登录案例
            • 安装xampp 软件
            • 安装nodejs 软件
            • 创建服务器目录下载安装第三方模块
            • app.js功能一 : 用户登录验证(用户登录服务器端(node))
            • 无法访问服务器错误一
            • 脚手架概念
            • 案例的脚手架文件 Login.vue
            • 发送ajax请求 [脚手架8080-->服务器4000]
            • 安装使用axios
            • session对象
            • app.js 功能一 -- 功能三
            • 无法访问服务器错误二
            • 商品列表
            • app.js功能二 : 显示商品列表-(分页)
            • npm run serve报错
            • 将商品添加至购物车功能
            • app.js功能三 : 将商品添加至购物车
            • app.js 中打开浏览器报错
            • 脚手架 添加购物车组件Product.vue
            • 常见错误一
            • app.js功能四 : 查询(指定用户)购物车列表
            • app.js 功能一 -- 功能五
            • 脚手架 购物车组件Cart.vue
            • 查询购物车组件 Cart.vue
            • 常见错误二
            • app.js功能五:删除购物车中一条商品信息
            • 全选按钮 Cart.vue
            • 删除选中的商品 Cart.vue
            • 购物车中商品数量 vuex
            • vuex安装配置与使用
            • 查询全选删除购物车 Cart.vue
            • app.js 功能一 -- 功能六

UI组件库

组件库作用:提高开发效率
PC端组件库
饿了么 elementUI
https://element.eleme.cn/#/zh-CN/
移动端组件库
饿了么 mintui
https://mint-ui.github.io/docs/#/zh-cn2
有赞: vant[vue 移动端/小程序项目]
https://youzan.github.io/vant-weapp/#/
滴滴 cube-ui
https://didi.github.io/cube-ui/#/zh-CN/

下载安装配置UI组件库

①下载脚手架 : 自己写项目的时候自己创建新脚手架-(创建|打包)
②启动脚手架
(1)启动位置脚手架文件package.json右键终端 npm run serve 回车 (终端node cmd都可以,不能powershell)
(2)打开浏览器 http://127.0.0.1:8080/#/第一个 / 当前项目 , #/ 后面是组件路径
mint UI组件库下载与配置
③ 下载组件库
在脚手架根目录下执行安装指令
脚手架文件目录打开cmd → npm i mint-ui -S // i (install)安装组件 , -S 保存组件名称与版本至package.json文件中
④配置组件库 main.js (配置第三方组件库)
(1)将组件库中所有组件引入到当前项目中
import MintUI from "mint-ui"//mint-ui文件夹
(2)单独引入mint-ui组中样式文件
import "mint-ui/lib/style.css"
(3)将组件库注册Vue对象
Vue.use(MintUI);
(4)new Vue中自带store(存储对象)要删除不然会报错
实战Vue:Mint UI移动端购物商城_第1张图片

使用mint-ui组件库中组件
短消息提示框

功能:显示消息框其中有一段文字,默认3秒后隐藏
作用:提示用户消息(删除成功;添加成功;添加失败;…)
简洁语法: this.$ toast(“一段文字”);
标准语法:this.$toast({})

{}常用参数 " "
参数一:message:" " 消息框中显示文字
参数二:position: " " 消息框显示位置 top:bottom;middle
参数三:iconClass:" " 指定图标样式名
iconfont

遇到问题:在消息框中显示图标(有意义小图片)Mint-ui组件库提供图标外观不精美,不满足用户需求
解决问题:专业图标网站 https://www.iconfont.cn/ 阿里巴巴下 下载图标方式
方式一:直接下载图标对应图片 (建议:使用图片少量)
方式二:将多个图标保存字体图标文件 ttf (建议:使用图片大量)
①点击图标对应购物车按钮 (选择图标) 纯色
②点击购物车按钮(下载代码)download.zip (包含css,ttf文件) ttf 是字体图标文件
使用图标字体
③在脚手架src目录下创建文件夹font (保存图标字体)
将下载的download.zip内容解压缩,打开内部的文件(demo.css,iconfont.css…)复制到font文件夹中
④在main.js 引入图标字体样式文件 iconfont.css
全局引入图标字体,可以在任何组件中使用图标
⑤在指定组件中调用图标
iconClass:”字体class 指定图标class名” iconClass:"iconfont icon-meishi"
如何调整图标大小,修改字体大小图标跟随变大 iconfont.css 文件第12行 font-size:16px

确认框

功能:显示一个确认框只有用户操作后隐藏
#重要性高一些
作用:是否删除指定数据,用户名格式有误
(1)this.$messagebox("标题","内容");
(2)this.$messagebox.confirm("确认消息")
.then(res=>{}) 当用户选择确认按钮回调函数
.catch(err=>{}) 当用户选择取消按钮回调函数
(3)this.$messagebox.prompt("输入消息文件")
.then((value)=>{}) 当用户输入成功回调函数
.catch(err=>{}) 输入失败

创建空组件实现提示框案例

(1)创建空组件 src/components/2.21/exam/exam01.vue
(2)为空组件指定访问路径 router.js path:"/exam01"
(3)通过浏览器地址栏 http://127.0.0.1:8080/#/exam01
第一步 : 右键package.json终端打开 npm run serve 启动脚手架
第二步 : src文件夹下的components里创建组件文件

src/components/2.21/exam/exam01.vue

<template>
  <div>
    <h3>mintui练习组件exam01.vueh3>
    
    <button @click="openToast">显示消息框button>
    
    <button @click="openAlert">显示确认框button>
  div>
template>
<script>
export default {
  //2:添加一组函数
  methods:{
    //2.1:添加显示消息提示框函数
    openToast(){
      //console.log(123);
     // 2.2:显示短消息提示框:默认3s后结束
     //this指当前组件对象exam01.vue
     this.$toast({
          message:"添加成功",//显示的文字
          position:"top",//文字位置
          iconClass:"iconfont icon-diannao"//指定字体图标  电脑图标
     });
    },
    //添加显示确认消息函数
    openAlert(){
  // 确认消息      确定
    //  this.$messagebox("消息","用户名格式不正确")
  //确认按钮消息    确定  取消
    // this.$messagebox.confirm("是否删除指定数据")//跟上面的只能用一个
    // .then(res=>{
    //   console.log("确认")//确认后的回调函数
    // })
    // .catch(err=>{
    //   console.log("取消")
    //   })
  // 输入框
   this.$messagebox.prompt("请输入")//跟上面的只能用一个
    .then(value=>{
      console.log(value)//确认后的回调函数
    })
    .catch(err=>{
      console.log(err)
      })

    },

  }
}
script>

第三步:为新建组件文件指定访问路径, 在router文件夹下index.js文件中 path:"/exam01"
src/router/index.js

import Vue from 'vue'              //引入Vue对象
import VueRouter from 'vue-router' //引入路由
//引入自定义组件
//1.引入组件exam01.vue
import exam01 from "../components/2.21/exam/exam01.vue"

Vue.use(VueRouter)//将路由对象注册V
//配置组件-:访问路径
const routes = [
  {
    path: '/exam01',
   
    component: exam01
  }
  
]
//打开浏览器地址:http://127.0.0.1:8080/#/exam01

const router = new VueRouter({
  routes
})

export default router

第四步: main.js里引入新组件exam01.vue所需组件库
main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false
//功能一:引入和配置mint-ui组件库
  //1:引入mint-ui所有组件
import MintUI from "mint-ui"
  //2:单独引入mint-ui样式文件
import "mint-ui/lib/style.css"
  //3:将mint-ui对象注册Vue实例
Vue.use(MintUI);

//功能二:引入图标字体中样式文件,使所有自定义组件均可使用
import "./font/iconfont.css"

new Vue({
  router,
  //store,  //存储对象删除
  render: h => h(App)
}).$mount('#app')
mint-ui表单组件
输入框

组件:
属性: label:左侧标签 , type:text password … , attr:原生属性
①创建组件
src/components/2.21/exam/exam02.vue

<template>
    <div>
      <h3>表单组件h3>

       <mt-field label="用户名" 
       placeholder="请输入用户名"
       v-model="uname"
       :attr="{maxlength:10,autofocus:true}"
       >mt-field>
      

       <mt-field label="密码"
       placeholder="请输入密码"
       v-model="upwd">mt-field>

       <mt-button @click="login">登录mt-button>  
    div>
template>
<script>
export default {
    data(){      //组件共享数据保存data 11
       return {
           uname:"",  //双向绑定用户名
           upwd:""    //向双向绑定密码
       } 
    },
    methods:{
        login(){
           //功能:用户登录任务
           //1:创建正则表达式验证用户名和密码格式
           //  规则:字母数字3~12
           var reg = /^[a-z0-9]{3,12}$/i;
           //2:获取用户输入用户名
           var u = this.uname;
           //3:获取用户输入密码
           var p = this.upwd;
           console.log(u+"_"+p);
           //4:通过正则表达式验证用户名
           //  如果格式不正确 确认消息 "用户名格式不正确"
           if(!reg.test(u)){
              this.$messagebox("消息","用户名格式不正确");
              return; //返回 
           }
           //5:通过正则表达式验证密码  
           //  如果格式不正确 确认消息  "密码格式不正确"
           if(!reg.test(p)){
             this.$messagebox("消息","密码格式不正确");
             return;
           }
           //#6:输出正确 通过ajax请求服务器程序帮助完成登录任务       
        }
    }
}
script>


②为组件指定访问路径index.js           /exam02
src/router/index.js

import Vue from 'vue'              //引入Vue对象
import VueRouter from 'vue-router' //引入路由
//引入自定义组件
//1.引入组件exam02.vue
import exam01 from "../components/2.21/exam/exam01.vue"
import exam02 from "../components/2.21/exam/exam02.vue"

Vue.use(VueRouter)//将路由对象注册V
//配置组件-:访问路径
const routes = [
  {
    path: '/exam01',
   
    component: exam01
  },
  {
    path: '/exam02',
   
    component: exam02
  }
  
]
//打开浏览器地址:http://127.0.0.1:8080/#/exam02

const router = new VueRouter({
  routes
})

export default router

③在main.js中引入mint-ui库
main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false
//功能一:引入和配置mint-ui组件库
//1:引入mint-ui所有组件
import MintUI from "mint-ui"
//2:单独引入mint-ui样式文件
import "mint-ui/lib/style.css"
//3:将mint-ui对象注册Vue实例
Vue.use(MintUI);

new Vue({
  router,
  //store,  //存储对象删除
  render: h => h(App)
}).$mount('#app')
开关组件

标准语法: 标题内容
属性: val2: 变量 , 值为boolean (true打开状态 False 关闭状态) , handle2函数写在script >export default>methods:{}里
事件: @change="" 修改事件

单选列表

组件:
属性: title: 标题 , :options="" 列表选项 [{label:value},{}] , v-model="" 绑定变量选中值

表单组件案例

①启动脚手架 , main.js引入mint-ui
②创建组件
src/components/2.21/exam/exam03.vue

<template>
    <div>
        <h3>Exam03.vueh3>

        <mt-switch v-model="val2" @change="handle2">
          是否接收消息
        mt-switch>

        <mt-radio
          title="最喜欢的职业选手"
          :options="list"
          v-model="v3"
        >
        mt-radio>
        <mt-button @click="handle3">
            获取单选列表中选中值
        mt-button>
    div>
template>
<script>
export default {
    data(){
        return {
          val2:true, //开关组件状态 true 打开
          v3:"",   //单选列表-->选中值
          list:[     //单选列表
              {label:"上单",value:"theshy"},
              {label:"中单",value:"Faker"},
              {label:"ADC",value:"uzi"}
              ]
        }//return end
    },//data end
    methods:{
      handle2(){
          //功能:获取开关组件状态
          //this.$messagebox(this.val2);
          console.log(this.val2)
      },
      handle3(){
          //功能:获取用户选中列表中值
          this.$toast(this.v3)
      }  
    }
}
script>

③为组件指定路径           /exam03
src/router/index.js

import Vue from 'vue'          //引入Vue对象
import Router from 'vue-router'//引入路由
//引入自定义组件
//1:引入组件exam03.vue
import exam03 from "../components/2.21/exam/exam03.vue"
Vue.use(Router)            
export default new Router({ //export default用于导出模块,在别的模块需要调用这个模块的时候,可以通过import命令引入使用  
  //配置组件-:访问路径
  routes: [
    {path:'/exam03',component:exam03},
  ]
})
//打浏览器地址 http://127.0.0.1:8080/#/exam03
mint-ui组件库面板底部导航条

Ⅰ 面板语法 :
多个面板显示不同内容同一时刻只能显示一个面板内容
面板父元素
     面板一
面板一:
    
     面板二
面板二:
    
面板三
面板三:
    

注意 :
面板中id与原生标签id不同,只需保证在当前组件中id不重复即可
变量active 保存某个面板id,则显示某个面板

Ⅱ 底部导航条语法
① https://www.iconfont.cn/  下载指定图片 (81*81 png)
②创建tabbar 底部导航条
父元素

        按钮
                按钮中图片
                文字 按钮中文字
       

        按钮
                按钮中图片
                文字 按钮中文字
       

        按钮
                按钮中图片
                文字 按钮中文字
       

注意 :
当用户点击某个按钮时,将当前按钮id赋值active变量
img 元素添加属性 slot="icon" 将图片元素添加上方div元素

面板底部导航条案例

示例 : active:"tab1"显示生鲜 ; active:"tab2" 显示酒 ; active:"tab3" 显示化妆品组件

创建组件exam04.vue
src/components/2.21/exam/exam04.vue

<template>
    <div>
       <h3>Exam04.vueh3> 
       
       <mt-tab-container v-model="active">
         <mt-tab-container-item id="tab1">
             生鲜面板
         mt-tab-container-item>
         <mt-tab-container-item id="tab2">mt-tab-container-item>
         <mt-tab-container-item id="tab3">
             化妆品
         mt-tab-container-item>
       mt-tab-container>
       
       <mt-tabbar v-model="active">  
          <mt-tab-item id="tab1">    
              <img src="../../../assets/01.png" slot="icon" />
              生鲜
          mt-tab-item>
          <mt-tab-item id="tab2">
              <img src="../../../assets/02.png" slot="icon"/>mt-tab-item>
          <mt-tab-item  id="tab3">
              <img src="../../../assets/03.png" slot="icon"/>
              化妆品
          mt-tab-item>
       mt-tabbar>
    div>
template>
<script>
export default {
    data(){
        return {
            //显示指定面板  化妆品
            active:"tab1"
        }
    }
}
script>

分配访问路径      /exam04
src/router/index.js

import Vue from 'vue'          //引入Vue对象
import Router from 'vue-router'//引入路由
//引入自定义组件
//1:引入组件exam04.vue
import exam04 from "../components/2.21/exam/exam04.vue"
Vue.use(Router)            
export default new Router({ //export default用于导出模块,在别的模块需要调用这个模块的时候,可以通过import命令引入使用  
  //配置组件-:访问路径
  routes: [
    {path:'/exam04',component:exam04},
  ]
})
//打浏览器地址 http://127.0.0.1:8080/#/exam04

① 引入和配置mint-ui组件库
import "./font/iconfont.css"引入图标字体库
main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false
//功能一:引入和配置mint-ui组件库
  //1:引入mint-ui所有组件
import MintUI from "mint-ui"
  //2:单独引入mint-ui样式文件
import "mint-ui/lib/style.css"
  //3:将mint-ui对象注册Vue实例
Vue.use(MintUI);

//功能二:引入图标字体中样式文件,使所有自定义组件均可使用
import "./font/iconfont.css"

new Vue({
  router,
  //store,  //存储对象删除
  render: h => h(App)
}).$mount('#app')
组件中数据传递(父组件传递数据子组件) 父组件在模板中调用子组件

父组件:负责创建数据并且传递子组件 , 需要路径
子组件:负责显示数据 , 不要需指定路径

  • Sub05.vue 子组件负责显示数据

export default{
   props:{
      msg:{default:""}//声明接收父组件数据msg
         }
   }
  • Fa06.vue 父组件创建数据并且传递子组件
    (1)引入子组件 import Sub05 from "./Sub05.vue"
    (2)注册子组件
    components:{
    Sub05
    }

        (3) 在模板中调用子组件


父组件在模板中调用子组件案例
创建子组件 Sub05.vue , Sub05.vue 子组件负责显示数据,不用指定路径
src/components/2.21/exam/Sub05.vue

<template>
    <div>
       Sub05.vue
       
       <h1>{{msg}}h1> 
    div>
template>
<script>
export default {
    props:{   //声明接收父组件数据
      msg:{default:""}//消息数据
    }
}
script>

创建父组件 Fa06.vue , Fa06.vue 父组件创建数据并且传递子组件
src/components/2.21/exam/Fa06.vue

<template>
    <div>
       <h3>Fa06.vue 父组件h3>
       
       <sub05 :msg="msg2">sub05>   
    div>
template>
<script>
//1:引入指定子组件
import Sub05 from "./Sub05.vue"
export default {
data(){
   return {
       msg2:"今天2月10日"
   }
},    
//2:注册子组件
components:{
    Sub05
}    
}
script>

src/router/index.js

import Vue from 'vue'          //引入Vue对象
import Router from 'vue-router'//引入路由
//引入自定义组件
//1:引入组件exam04.vue
import Sub05 from "../components/2.21/exam/Sub05.vue"
import Fa06 from "../components/2.21/exam/Fa06.vue"
Vue.use(Router)            
export default new Router({ //export default用于导出模块,在别的模块需要调用这个模块的时候,可以通过import命令引入使用  
  //配置组件-:访问路径
  routes: [
    {path:'/Sub05',component:Sub05},
    {path:'/Fa06',component:Fa06}
  ]
})
//打浏览器地址 http://127.0.0.1:8080/#/Fa06
微信消息列表功能案例

实战Vue:Mint UI移动端购物商城_第2张图片
第一部分:顶部标题(子组件)
分析功能 :子组件 TitleBar.vue
左侧文字 leftTitle , 右侧两张图片 rightFristImg rightSecondImg
创建子组件 weixin/common/TitleBar.vue , 此组件显示数据
Home.vue 负责创建数据并且调用子组件

子组件负责:显示数据(输出;样式)
父组件负责:创建数据并且传递子组件
目录结构 :
common 文件夹:所有子组件
json 文件夹:模拟数据
Home.vue 父组件    /Home

src/components/2.21/weixin/common/TitleBar.vue

<template>
    <div class="page-head">
      
      <span>{{leftTitle}}span>  
      
      <div class="right-head">
         <div class="searchdiv">
             <img :src="rightFirstImg" style="width:25px;" />
         div>
         <div class="searchdiv" style="margin-left:20px;">
             <img :src="rightSecondImg"
              style="width:25px;margin-right:15px;"/>
         div>
      div>
    div>
template>
<script>
export default {
    props:{ //声明接收父组件数据   
      //左侧标题
      leftTitle:{default:""},
      //右侧图片
      rightFirstImg:{default:""},
      rightSecondImg:{default:""}
    }
}
script>
<style scoped>
/*1:外层父元素弹性布局*/
.page-head{
   display: flex;/*指定布局方式:弹性布局*/
   position: fixed;/*固定定位*/
   z-index: 999;/*显示元素上方*/
   width: 100%;/*填满父无素*/
   justify-content: space-between;/*子元素两端对齐*/
   align-items: center;/*子元素垂直居中*/ 
   background-color:#3e3a39;
   padding-left:7px;/*内边距*/
   padding-right:7px;
   height:48px;/*元素高度*/
   color:#fff;
   font-size:18px;
}
/*2:右侧元素弹性布局*/
.right-head{
    display: flex;
}
style>

src/components/2.21/weixin/Home.vue

<template>
    <div>
        <div>
            
           
           <titlebar
             leftTitle="微信(100)"
             :rightFirstImg="require('../../../assets/ic_search.png')"
             :rightSecondImg="require('../../../assets/ic_add.png')"
           >titlebar>
        div>
    div>
template>
<script>
//1:引入顶部标题子组件
import TitleBar from "./common/TitleBar.vue"
export default {
 //2:注册顶部标题子组件
 components:{
     //指定标签名称:子组件对象
    "titlebar":TitleBar
 }    
}
script>

清空项目页中空白区域
(1)App.vue(根组件) 清除 padding-top:40px;
(2)index.html     n.css 实战Vue:Mint UI移动端购物商城_第3张图片
app.vue

<template>
 <div class="app-container">
    
    <router-view>
       
    router-view>
 div>
template>
<style>
   .app-container{
     padding-top:0px;
     padding-bottom:0px;
     overflow-x:hidden;
   }
 .mui-bar-tab .mui-tab-item-tao.mui-active {
    color: #007aff;
 }
.mui-bar-tab .mui-tab-item-tao {
    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-tao .mui-icon {
    top: 3px;
    width: 24px;
    height: 24px;
    padding-top: 0;
    padding-bottom: 0;
}

.mui-bar-tab .mui-tab-item-tao .mui-icon~.mui-tab-label {
   font-size:11px;
   display:block;
   overflow:hidden;
   text-overflow:ellipsis;

}
style>

index.html

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    
    <link rel="stylesheet" href="n.css">

    <title>xz_admin_v2title>
  head>
  <body>
    <noscript>
     
    noscript>
    <div id="app">div>
    
  body>
html>

第二部分:消息列表(一条消息+多条消息+面板)
创建子组件 common/Message.vue
左侧图片 imgurl
左侧标题 title subtitle
右侧时间 sendtime
src/components/2.21/weixin/common/Message.vue

<template>
   
    <div class="rootstyle">
        
        <div class="leftimgtxt">
            <img :src="imgurl" class="imgstyle" />
            <div class="lefttitle">
               <span class="title">{{title}}span>
               <span class="subtitle">{{subtitle}}span>
            div>
        div>
        
        <span class="sendtime">{{sendtime}}span>
    div>
template>
<script>
//接收父组件传递数据  imgurl;title;subtitle;sendtime
export default {
    props:{ //声明接收父组件传递数据
      imgurl:{default:""}, //左侧图片    默认值空字符串
      title:{default:""},//左侧标题      默认值空字符串
      subtitle:{default:""},//左侧子标题 默认值空字符串
      sendtime:{default:""}//发送时间    默认值空字符串
    }
}
script>
<style scoped> 
/*当style标签具有该scoped属性时,其CSS将仅应用于当前组件的元素, 下面的style样式只在上面template这个组件里起作用*/
 /*1:一条消息元素元素*/
 .rootstyle{
     display: flex;/*弹性布局*/
     justify-content: space-between;/*子元素两端对齐*/
     align-items: center;/*子元素垂直居中*/
 }
 /*2:左侧图片与标题父元素*/
 .leftimgtxt{
     display: flex;/*子元素水平排列*/
 }
 /*3:左侧图片宽度和高度*/
 .imgstyle{
     width:50px;/*图片宽度和高度*/
     height:50px;
 }
 /*4:左侧标题与子标题父元素*/
 .lefttitle{
    display: flex;/*弹性布局*/
    flex-direction: column;/*子元素按钮列:排列*/
    justify-content: center;/*居中对齐*/
    margin-left:7px;/*标题与左侧图片7像素间距*/ 
 }
 /*5:左侧标题*/
 .title{
     color:#000;  /*标题颜色与大小*/
     font-size:17px;
 }
 /*6:左侧子标题*/
 .subtitle{
     color:gray;
     margin-top:4px;/*子标题上面标题间距*/
 }
 /*7:右侧发送时间*/
 .sendtime{
     color:gray;
 }
style>

多条消息组件common/MessageList.vue 消息列表 , 引入子组件Message.vue

加载json数据 import messagejson from "../json/messagelist.json"import 将json数据->直接->转换js对象
{data:[{}]}—>js对象
data(){
return { rows:messagejson.data}
}

{{rows}}

左侧rows 在data声明变量rows
赋值 rows:messagejson.data 消息列表数组赋值rows
在模板中创建循环显示数组中值
创建循环遍历数据并且传递Message.vue
临时将消息列表组件转全局组件
动态读取图片路径

messagejson.json  01.jpg

 :imgurl="require(`./../../../../assets/`+item.img)"

src/components/2.21/weixin/common/MessageList.vue

<template>
    <div>
        
        <message
         class="itemstyle"
         v-for="(item,i) of rows"
         :key="i"
         :imgurl="require(`./../../../../assets/`+item.img)"
         :title="item.title"
         :subtitle="item.subtitle"
         :sendtime="item.time"
        >message>
    div>
template>
<script>
//1:引入子组件
import Message from "./Message.vue"
//1.1:引入json文件
import messagelistjson from "../json/messagelist.json"
export default {
data(){
    return { 
        //赋值:将json数组赋值变量rows
        //messagejson.json{"data":[...]}
        rows:messagelistjson.data
   }
},
created(){
    console.log(this.rows);
},    
//2:注册子组件
components:{
    "message":Message //左侧标签名称:组件对象
}    
}
script>

<style scoped>
  /*一条消息底部添加一条 灰色下划线  */
  .itemstyle{
     padding:5px;
     border-bottom:1px solid #d9d9d9; 
  }
style>

负责加载模拟数据 json/messagelist.json
查看模拟数据 {“data”:[{},{}]}
src/components/2.21/weixin/json/messagelist.json

{
  "data":[
    {
      "id":1,
      "img":"01.png",
      "title":"微信支付",
      "subtitle":"获取鼓励通知",
      "time":"16:57"
    },
    {
      "id":2,
      "img":"02.png",
      "title":"腾讯新闻",
      "subtitle":"白天喝上十杯水才是真正的身体好",
      "time":"16:36"
    },
    {
      "id":2,
      "img":"a_7.png",
      "title":"腾讯新闻",
      "subtitle":"白天喝上十杯水才是真正的身体好",
      "time":"16:36"
    }         
  ]
}

将消息列表组件完成组装Home.vue , 在Home.vue 创建面板
实战Vue:Mint UI移动端购物商城_第4张图片
src/components/2.21/weixin/Home.vue

<template>
    <div>
        <div>
            
           
           <titlebar
             leftTitle="微信(100)"
             :rightFirstImg="require('../../../assets/ic_search.png')"
             :rightSecondImg="require('../../../assets/ic_add.png')"
           >titlebar>
           
           
           <mt-tab-container v-model="active">   
              <mt-tab-container-item id="message"> 
                   <messagelist>messagelist>
              mt-tab-container-item>
           mt-tab-container>
           
        div>
    div>
template>
<script>
//1:引入顶部标题子组件
import TitleBar from "./common/TitleBar.vue"
//1.1:引入消息列表子组件
import MessageList from "./common/MessageList.vue"
export default {
 data(){
     return {
         active:"message", //默认消息列表组件显示
     }
 },   
 //2:注册顶部标题子组件
 components:{
     //指定标签名称:子组件对象
    "titlebar":TitleBar,
    "messagelist":MessageList
 }    
}
script>

第三部分:底部导航条(tabbar)图片切换+文字颜色
mint-ui 导航条默认图片不能切换
解决方案:创建子组件 功能:显示按钮上方图片并且实现图片切换
common/TabbarIcon.vue

接收三个数据
1.当前按钮是否被选中 focused:true
2.选中图片 selectedImg绿色
3.默认图片 normallmage灰色
src/components/2.21/weixin/common/TabbarIcon.vue

<template>
    <div>
       
        
    <img :src="focused?selectedImage:normalImage" class="imgstyle"/>
    div>
template>
<script>
export default {
    props:{ //声明接收父组件数据
         //声明当前元素选中状态
         focused:false,
         //选中时显示图片
         selectedImage:{default:""},
         //默认时显示图片
         normalImage:{default:""}
    }
}
script>
<style scoped>
 .imgstyle{
     width:30px;
     height:30px;
 }
style>

src/components/2.21/weixin/common/TabBar.vue

<template>
    <div class="page-head">
      
      <span>{{leftTitle}}span>  
      
      <div class="right-head">
         <div class="searchdiv">
             <img :src="rightFirstImg" style="width:25px;" />
         div>
         <div class="searchdiv" style="margin-left:20px;">
             <img :src="rightSecondImg"
              style="width:25px;margin-right:15px;"/>
         div>
      div>
    div>
template>
<script>
export default {
    props:{ //声明接收父组件数据   
      //左侧标题
      leftTitle:{default:""},
      //右侧图片
      rightFirstImg:{default:""},
      rightSecondImg:{default:""}

    }
}
script>
<style scoped>
/*1:外层父元素弹性布局*/
.page-head{
   display: flex;/*指定布局方式:弹性布局*/
   position: fixed;/*固定定位*/
   z-index: 999;/*显示元素上方*/
   width: 100%;/*填满父无素*/
   justify-content: space-between;/*子元素两端对齐*/
   align-items: center;/*子元素垂直居中*/ 
   background-color:#3e3a39;/*16:49*/
   padding-left:7px;/*内边距*/
   padding-right:7px;
   height:48px;/*元素高度*/
   color:#fff;
   font-size:18px;
}
/*2:右侧元素弹性布局*/
.right-head{
    display: flex;
}
style>

mint-ui 导航条默认按钮中文字蓝色 实际要求绿色文字
解决办法:覆盖原组件中样式
查找原先组件中样式选择器
默认选中蓝色文字选择器
.mint-tabbar > .mint-tab-item.is-selected{
color:#45c018;
}
默认蓝色文字选择器
.mint-tabbar > .mint-tab-item{
color:#999999;
}

为按钮绑定点击事件 ,完成图片切换
当点选中按钮图片绿,其它图片灰色
为按钮元素绑定点击事件
遇到问题 为按钮绑定点击事件失效
问题原因:按钮组件不支持点击事件
解决思路:添加事件属性 native native是使用原生组件点击事件

src/components/2.21/weixin/Home.vue

<template>
    <div class="page-tabbar">
        <div class="page-wrap">
            
           
           <titlebar
             leftTitle="微信(100)"
             :rightFirstImg="require('../../../assets/ic_search.png')"
             :rightSecondImg="require('../../../assets/ic_add.png')"
           >titlebar>
           
           <div style="margin-top:48px;">div>
           
           <mt-tab-container v-model="active">   
              <mt-tab-container-item id="message"> 
                   <messagelist>messagelist>
              mt-tab-container-item>
           mt-tab-container>
           
           <mt-tabbar v-model="active" fixed>
               
               <mt-tab-item   id="message" @click.native="changeState(0)">
                 <tabbaricon
                   :focused="list[0].isSelect"
                   :selectedImage="require('../../../assets/ic_weixin_selected.png')"
                   :normalImage="require('../../../assets/ic_weixin_normal.png')"
                 >tabbaricon>  
                 微信
               mt-tab-item>
               
               <mt-tab-item  id="contact" @click.native="changeState(1)">
                  <tabbaricon
                    :focused="list[1].isSelect"
                    :selectedImage="require('../../../assets/ic_contacts_selected.png')"
                    :normalImage="require('../../../assets/ic_contacts_normal.png')"
                  >tabbaricon> 
                  通讯录
               mt-tab-item>
               
               <mt-tab-item id="find" @click.native="changeState(2)">
                   <tabbaricon
                     :focused="list[2].isSelect"
                     :selectedImage="require('../../../assets/ic_find_selected.png')"
                     :normalImage="require('../../../assets/ic_find_normal.png')"
                   >tabbaricon>
                   发现
               mt-tab-item>
               
               <mt-tab-item id="me"  @click.native="changeState(3)">
                   <tabbaricon
                   :focused="list[3].isSelect"
                   :selectedImage="require('../../../assets/ic_me_selected.png')"
                   :normalImage="require('../../../assets/ic_me_normal.png')">
                   tabbaricon>mt-tab-item>
           mt-tabbar>
        div>
    div>
template>
<script>
//1:引入顶部标题子组件
import TitleBar from "./common/TitleBar.vue"
//1.1:引入消息列表子组件
import MessageList from "./common/MessageList.vue"
//1.2:引入底图图片组件
import TabBarIcon from "./common/TabbarIcon.vue"
export default {
 data(){
     return {
         active:"message", //默认消息列表组件显示
         list:[
             {isSelect:true},  //第一个按钮状态 0
             {isSelect:false}, //第二个按钮状态 1
             {isSelect:false}, //第三个按钮状态 2
             {isSelect:false}, //第四个按钮状态 3
             
         ]
     }
 },  
 methods:{
     changeState(idx){
       //功能:完成点击按钮切换图片任务 当前按钮true 其它按钮false
       ///参数idx按钮下标 0 1 2 3
       //1:创建变量size 表示数组长度
       var size = this.list.length;
       //2:创建循环遍历数据list每个元素
       for(var i=0;i<size;i++){
       //3:判断如果参数下载idx与当前按钮下标相同
       if(i==idx){
           this.list[i].isSelect=true;
       }else{
           this.list[i].isSelect=false;
       }
       //4:当前元素选中状态true
       //5:其它元素默认状态false
       }

     }
 }, 
 //2:注册顶部标题子组件
 components:{
     //指定标签名称:子组件对象
    "titlebar":TitleBar,         //顶部标题子组件
    "messagelist":MessageList,   //中间消息列表子组件
    "tabbaricon":TabBarIcon,     //底部导航条图片子组件
 }    
}
script>
<style scoped>
/*1:最外层父元素*/
.page-tabbar{
    overflow: hidden; /*溢出隐藏*/
}
/*2:内层元素*/
.page-wrap{
    overflow: auto;  /*数据多出现滚动条*/
    padding-bottom: 60px;/*底部导栏空间*/
}
/*3:覆盖mint-ui组件原有样式-tabbar按钮中文字选中样式 42*/
.mint-tabbar > .mint-tab-item.is-selected{
    background-color: transparent;
    color:#45c018;
}
/*4:覆盖mint-ui组件原有样式-tabbar按钮中文字默认样式*/
.mint-tabbar > .mint-tab-item{
    color:#999999;
}
style>

为Home组件指定访问路径
src/router/index.js

import Vue from 'vue'          //引入Vue对象
import Router from 'vue-router'//引入路由
//引入自定义组件
//1:引入组件Home.vue
import Home from "../components/2.21/weixin/Home.vue"
import MessageList from "../components/2.21/weixin/common/MessageList.vue"

Vue.use(Router)            
export default new Router({  
  //配置组件-:访问路径
  routes: [
    {path:'/Home',component:Home},    
    {path:'/MessageList',component:MessageList}, 
  ]
})
//打浏览器地址 http://127.0.0.1:8080/#/Home
//                     "/"当前项目  "#"分隔符  "/"路径
用户登录案例

Vue ui 组件库学子商城完成功能:
-用户登录
-商品列表
-将商品添加至购物车
-购物车

学子商城用户登录功能
分析用户登录流程

实战Vue:Mint UI移动端购物商城_第5张图片
功能分析

  • 用户打开网页访问登录组件输入用户名和密码
  • 格式错误: 3~12位 提示:用户名格式错误
    密码格式不错误
  • 没有用户输入用户名和密码
    提示:用户名和密码不正确
  • 用户名和密码正确 跳转商列表组件

分析代码实现(核心内容)
创建一个用户登录数据库表并且保存合法用户名和密码
有一张用户表:xz_user

  • 用户登录表 xz_login 表名
  • id 记录编号 INT PRIMARY KEY AUTO_INCREMENT
  • uname 用户名 VARCHAR(50)
  • upwd 用户密码 VARCHAR(32)

密码数据特殊,此数据需要加密后才能保存数据库中 加密密码原因是:防止内部工作人员泄密
加密密码方式 mysql有一个函数md5() 加密函数
md5 单向加密算法
使用: md5(‘123’)
SELECT md5(“123”) => “2389dsuisd7832iuwe89w23e98” 32位
高级别安全建议
(1)多次循环加密 md5(md5(‘123’))
(2)加强密码规则[最有效]
8位以上:小写大写字母数字特殊符号 示例:1_bC_-!00A

参与完成此功能所有程序
-数据库表 xz_login
-node 服务器程序 app.js
-Login.vue 脚手架创建组件
服务器端目录结构
vue_app_00_null 脚手架下载保存所在文件命名为
vue_server_00 服务器程序 脚手架 服务器程序是分开放的 放一起就都不能用
vue_server_00/
                   public 静态资源(图片;视频;音频;…)
                   app.js 服务器程序
                   db.sql 所有sql语法
                   node_modules #第三方模块此目录和脚手架node_modules完全不同
实战Vue:Mint UI移动端购物商城_第6张图片

安装xampp 软件

xampp软件包(mysql;php;apache;…)
官网 : windows https://www.apachefriends.org/index.html       mac http://sourceforge.net/projects/xampp/files/

安装nodejs 软件

node.js 淘宝镜像https://npm.taobao.org/mirrors/node/       https://nodejs.org/dist/v9.10.0/

  • windows
    node-v9.10.0-x64.msi #64系统
    node-v9.10.0-x86.msi #32 系统
  • mac
    node-v9.10.0.pkg #mac系统
    注意版本 #8.10 最低要求
    >node -v
    v8.11.4
创建服务器目录下载安装第三方模块

express web服务器
express-session session对象
mysql mysql驱动程序
cors 跨域模块
express-session

  • mysql 配置连接池
    var pool = mysql.createPool({
    host:”127.0.0.1”,
    user:”root”,
    password:””,
    database:”xz”,
    port:3306
    })
  • cors 跨域
    脚手架8080 node服务器(安装跨域模块) 4000
    以上脚手架发送请求给node服务器出错(跨域)
    解决方式 : 跨域模块 跨域模块在服务器端运行:(允许跨域访问脚手架)列表
    cors({
    origin:[“http://127.0.0.1:8080”,”http://localhost:8080”] //允许跨域访问脚手架
    })
app.js功能一 : 用户登录验证(用户登录服务器端(node))
  • node描述功能:
    (1)获取脚手架提供用户名和密码
    (2)将用户名和密码拼接sql语句查询是否有匹配合法记录
    (3)将结果返回脚手架 登录成功 登录失败
  • 获取脚手架提供用户名和密码
    GET /login?uname=tom&upwd=123
    var u = req.query.uname; 接收脚手架用户名
    var p = req.query.upwd 接收脚手架用户密码
  • 将用户名和密码拼接sql语句查询是否有匹配合法记录
    #通用套路 查询;查找;搜索;获取… SELECT
    SELECT id FROM xz_login WHERE uname=? AND upwd=md5(?)
  • 将结果返回脚手架 登录成功 登录失败
    pool.query(sql,[uname,upwd],(err,result)=>{
    if(err) throw err; #程序出现严重错,停止执行抛出错误对象
    result 查询sql结果数组
    result.length==0 查询失败 用户名或密码错
    result.length!=0 查询成功 登录成功
    });
    res.send({code:1,msg:“登录成功”})
    res.send({code:-1,msg:“用户名或密码有误”})

db.sql

#功能一:完成用户登录表创建
CREATE DATABASE xz;
#1:进入xz库
USE xz;
#2:创建xz_login表
CREATE TABLE xz_login(
  id INT PRIMARY KEY AUTO_INCREMENT,
  uname VARCHAR(50),
  upwd  VARCHAR(32)
);
#3:添加二条测试数据[合法]
INSERT INTO xz_login VALUES(null,'tom',md5('123'));
INSERT INTO xz_login VALUES(null,'jerry',md5('123'));

准备工作① : 启动数据库,复制上面创建数据库粘贴进来
实战Vue:Mint UI移动端购物商城_第7张图片
准备工作② : 启动服务器 vscode中右键app.js文件打开终端输入node app.js回车
app.js

//功能:服务器程序
//1:引入四个模块
const express = require("express"); //web服务器模块
const mysql = require("mysql");//mysql模块
const session = require("express-session");//session模块
const cors = require("cors");//跨域
//2:创建连接池
var pool = mysql.createPool({
    host:"127.0.0.1",
    user:"root",
    password:"",
    database:"xz",
    port:3306,
    connectionLimit:15
})
//3:创建web服务器
var server = express();
//4:配置跨域模块
//4.1:允许程序列表 脚手架
//4.2:每次请求验证
server.use(cors({
    origin:["http://127.0.0.1:8080","http://localhost:8080"],
    credentials:true 
}))
//5:指定静态资源目录 public
server.use(express.static("public"));
//6:配置session对象 !!!
//7:为服务器绑定监听端口 4000
server.listen(4000);
console.log("服务器起动.......");

//功能一:用户登录验证
server.get("/login",(req,res)=>{
  //1:获取脚手架传递用户名和密码
  var u = req.query.uname;
  var p = req.query.upwd;
  //2:创建sql语法并且将用户名和密码加入
  var sql = "SELECT id FROM xz_login WHERE uname=? AND upwd=md5(?)";
  //3:执行sql语法并且获取返回结果
  pool.query(sql,[u,p],(err,result)=>{
     //3.1:如果出现严重错误抛出  
     if(err)throw err;
     if(result.length==0){
         res.send({code:-1,msg:"用户名或密码有误"});
     }else{
         //
         res.send({code:1,msg:"登录成功"})
     }
  });
})
//测试:你 
//启动服务器  node app.js
//打开浏览器在地址栏输入
//      http://127.0.0.1:4000/login?uname=tom&upwd=123 如果出现错误,检查数据库重新开启,开启后node也需要重新开启    错误二:服务器错误 node已开启重复开占端口需要关闭
//      http://127.0.0.1:4000/login?uname=tom&upwd=122

准备工作③ : 打开浏览器在地址栏输入 http://127.0.0.1:4000/login?uname=tom&upwd=123

无法访问服务器错误一

错误一: 检查数据库3306是否开启,开启后node也需要重新开启
错误二: 服务器错误 node已开启重复开占端口需要关闭,如果报错服务器错误,如下图显示4000端口占用说明重复开启服务器会导致地址栏获取不到数据报服务器错误,点击图示删除按钮重新node app.js开启
实战Vue:Mint UI移动端购物商城_第8张图片

脚手架概念

脚手架是一个工具叫做 vue-cli 只需要全局安装一次
vue-cli 生成一个脚手架项目 vue_app_00_null(上面案例脚手架文件名) 本机有node即可使用
vue_app_00_null 使用项目

案例的脚手架文件 Login.vue

创建组件vue_app_00_null/src/components/2.21/xz/Login.vue 指定路径 /login
添加二个输入框 , 一个登录按钮
vue_app_00_null/src/components/2.21/xz/Login.vue

<template>
    <div>
        <h3>Login.vueh3>
        
        <mt-field placeholder="请输入用户名" v-model="uname">mt-field>
        <mt-field placeholder="请输入密码" v-model="upwd">mt-field>
        
        <mt-button size="large" @click="login"
        >登录mt-button>
    div>
template>
<script>
export default {
    data(){
        return {
            uname:"tom",
            upwd:"123"
        }
    },
    methods:{
        login(){
           //功能:完成用户登录   
           //1:创建正则表达式用于验证用户名和密码
           //  字母和数字 3~12
           var reg = /^[a-z0-9]{3,12}$/i;
           //2:获取用户名和密码
           var u = this.uname;
           var p = this.upwd;
           console.log(u+"_"+p);
           //3:验证用户名如果格式不正确,提示错误信息
           if(!reg.test(u)){
             this.$messagebox("消息","用户名格式不正确");
             return;//停止程序运行
           }
           //4:验证用户密码如果格式不正确,提示错误信息
           if(!reg.test(p)){
             this.$messagebox("消息","密码格式不正确");
             return;//停止程序运行  
           }
           //console.log(3);
           //5:创建url变量,保存请求服务器地址
           var url = "login";
           //6:创建obj变量,保存请求时参数
           var obj = {uname:u,upwd:p};
           //7:发送ajax请求
           this.axios.get(url,{params:obj}).then(res=>{
              //8:接收服务器返回结果
              //9:如果-1  提示用户名和密码有误
              //10:如果1  跳转商品列表组件  /Product 
              if(res.data.code==-1){
                  this.$messagebox("消息","用户名或密码有误");
              }else{
                  this.$toast("登录成功");
                  this.$router.push("/Product");
              }
           })
         
        }
    }
}
script>

ue_app_00_null/src/router/index.js

import Vue from 'vue'          //引入Vue对象
import Router from 'vue-router'//引入路由
//引入自定义组件
//1:引入组件Login.vue
import Login from "../components/2.21/xz/Login.vue"
Vue.use(Router)               
export default new Router({
  //配置组件-:访问路径
  routes: [
    {path:'/Login',component:Login}
  ]
})
发送ajax请求 [脚手架8080–>服务器4000]

解决问题使用ajax模块
ajax解决方式 : 原生ajax , jquery , axios(vue ajax模块) , request

安装使用axios

①下载第三方ajax模块 脚手架文件夹下打开cmd 输入指令npm i axios安装
vue_app_00_null/ npm i axios
②在main.js 全局配置 [所有组件均可以使用axios] :

  1. 将模块引入当前项目中 import axios from "axios"
  2. 配置访问服务器基础地址 axios.defaults.baseURL = "http://127.0.0.1:4000/";
  3. 发送请求保存session信息 axios.defaults.withCredentials=true
  4. 将axios对象注册Vue Vue.prototype.axios = axios;
    示例:请求用户登录程序this.axios.get("login"); baseURL会把ttp://127.0.0.1:4000/放到前面 , this.axios.get(“http://127.0.0.1:4000/login”);

vue_app_00_null/src/main.js

import Vue from 'vue'       //vue实例对象
import App from './App.vue' //根组件
import router from './router'//路由模块

//选择代码 main.js 5~75 删除   删除!!!
//选择一行代码删除 
//功能一:引入和配置mint-ui组件库
//1:引入mint-ui所有组件
import MintUI from "mint-ui"
//2:单独引入mint-ui样式文件
import "mint-ui/lib/style.css"
//3:将mint-ui对象注册Vue实例 34
Vue.use(MintUI);

//功能二:引入图标字体中样式文件,使所有自定义组件均可使用
import "./font/iconfont.css"

//功能三:引入axios库
//1:引入axios库
import axios from "axios"
//2:配置访问服务器基础路径
axios.defaults.baseURL="http://127.0.0.1:4000/"
//3:配置保存session数据
axios.defaults.withCredentials=true
//4:注册
Vue.prototype.axios = axios;

//以下代码一定在main.js最后
new Vue({
  router,         //路由对象
  render: h => h(App),
}).$mount('#app')
session对象

实战Vue:Mint UI移动端购物商城_第9张图片

  • session对象 是保存在服务器端的对象,保存的数据 : 用户登录凭证 用户权限
  • session对象生命周期
    创建用户打开浏览器访问服务器时:session对象创建-----开始
    中间用户可以通过浏览器访问服务器多个程序:session-----使用中
    当用户关闭浏览器session对象销毁或者服务器程序停止工作 session对象销毁
  • session对象下载配置和使用
    session对象下载第三方模块 在服务器端程序下载模块express-session npm i express-session
    app.js 服务器端配置session对象 server.use(session({})) 有三个配置项 :
    secret : 安全字符串session对象加密数据使
    resave:true 每次请求更新服务器数据
    saveUninitialized 保存初始化数据
    如何在服务器端使用
    ①用户登录成功后将登录凭证保存session对象中
    req.session.uid = 1; //将用户登录id作为凭证保存session对象中
    ②当用户访问商品列表时获取session对象中uid
    var uid = req.session.uid; //获取uid
    if(!uid){ //判断
    res.send({code:-2,msg:“请求登录”});return;
    }
app.js 功能一 – 功能三

服务器 vue_server_00/app.js

//功能:服务器程序
//1:引入四个模块
const express = require("express"); //web服务器模块
const mysql = require("mysql");//mysql模块
const session = require("express-session");//session模块
const cors = require("cors");//跨域
//2:创建连接池
var pool = mysql.createPool({
    host:"127.0.0.1",
    user:"root",
    password:"",
    database:"xz",
    port:3306,
    connectionLimit:15
})
//3:创建web服务器
var server = express();
//4:配置跨域模块
//4.1:允许程序列表 脚手架
//4.2:每次请求验证
server.use(cors({
    origin:["http://127.0.0.1:8080","http://localhost:8080"],
    credentials:true 
}))
//5:指定静态资源目录 public
server.use(express.static("public"));
//6:配置session对象
server.use(session({
    secret:"128位安全字符串",//加密条件
    resave:true,//每次请求更新数据
    saveUninitialized:true,//保存初始化数据
}));
//7:为服务器绑定监听端口 4000
server.listen(4000);
console.log("服务器起动.......");

//功能一:用户登录验证
server.get("/login",(req,res)=>{
  //1:获取脚手架传递用户名和密码
  var u = req.query.uname;
  var p = req.query.upwd;
  //2:创建sql语法并且将用户名和密码加入
  var sql = "SELECT id FROM xz_login WHERE uname=? AND upwd=md5(?)";
  //3:执行sql语法并且获取返回结果
  pool.query(sql,[u,p],(err,result)=>{
     //3.1:如果出现严重错误抛出  
     if(err)throw err;
     //3.2:如果result.length长度为0,表示登录失败
     if(result.length==0){
         res.send({code:-1,msg:"用户名或密码有误"});
     }else{
         //登录成功
         //如果用户登录成功:创建session对象并且将登录凭证uid
         //保存对象中    result=[{id:1}]
         //将当前登录用户id保存session对象中作业:登录凭证
         req.session.uid = result[0].id;
         res.send({code:1,msg:"登录成功"})
     }
  });
})
//测试:你 
//启动服务器  node app.js
//打开浏览器在地址栏输入
//      http://127.0.0.1:4000/login?uname=tom&upwd=123
//      http://127.0.0.1:4000/login?uname=tom&upwd=122

//功能二:显示商品列表-(分页)
//1:GET /product 
server.get("/product",(req,res)=>{
//2:接收参数 页码 一页几行
var pno = req.query.pno;
var ps = req.query.pageSize;
//3:为参数设置默认值  1   20
if(!pno){pno=1}
if(!ps){ps=20}
//4:创建sql语句分页查询商品列表
var sql = "SELECT lid,lname,price,pic FROM xz_laptop LIMIT ?,?";
var offset = (pno-1)*ps;
ps = parseInt(ps);
//5:执行sql语句并且将结果发送脚手架
pool.query(sql,[offset,ps],(err,result)=>{
    if(err)throw err;
    res.send({code:1,msg:"查询成功",data:result})
})
})
//测试
//   1:启动node app.js
//   2:http://127.0.0.1:4000/product
//   3:http://127.0.0.1:4000/product?pno=2
//   4:http://127.0.0.1:4000/01.jpg
//   5:http://127.0.0.1:4000/02.jpg

//功能三:将商品添加至购物车
server.get("/addcart",(req,res)=>{
  //1:获取当前登录用户凭证
  var uid = req.session.uid;
  //2:如果当前用户没有登录凭证  输出请登录
  if(!uid){
      res.send({code:-2,msg:"请登录"});
      return;
  }
  //3:获取脚手架传递数据 lid,lname,price
  var lid = req.query.lid;
  var lname = req.query.lname;
  var price = req.query.price;
  //4:创建sql语句 查询当前用户是否购买过此商品
  var sql = "SELECT id FROM xz_cart WHERE uid=? AND lid=?";
  //5:执行sql语句
  pool.query(sql,[uid,lid],(err,result)=>{
  //6:在回调函数(钩子函数)
  if(err)throw err;
  //7:获取查询结果[判断是否购买过此商品]
  if(result.length==0){
  //8:如果没买过此商品 创建INSERT SQL
  var sql = `INSERT INTO xz_cart VALUES(null,${lid},'${lname}',${price},1,${uid})`;
  }else{
  //9:如果己购买过此商品 创建UPDATE SQL
  var sql = `UPDATE xz_cart SET count=count+1 WHERE uid=${uid} AND lid=${lid}`;
  }
  //10:执行sql
  //11:返回结果脚手架
  pool.query(sql,(err,result)=>{
      if(err)throw err;
      res.send({code:1,msg:"添加成功"})
  })
})//select end

//测试
//1:下载 db02.sql 复制代码 mysql执行
//2:启动node   node app.js
//3:浏览器地址
//  http://127.0.0.1:4000/addcart?lid=1&price=9&lname=aa
//  http://127.0.0.1:4000/login?uname=tom&upwd=123 //登录 浏览器不能关
//  http://127.0.0.1:4000/addcart?lid=1&price=9&lname=aa //添加
//  http://127.0.0.1:4000/addcart?lid=1&price=9&lname=aa //再试一次添加
//  SELECT * FROM xz_cart; //查询数据库 空xz_cart添加一条数据
})
无法访问服务器错误二

下面 商品列表 > 服务器程序 > 数据库 node 栏目将介绍 上述代码中功能二:显示商品列表-(分页) , 功能三:将商品添加至购物车 原理
vscode中右键app.js文件打开终端输入node app.js开启服务器 , 浏览器地址栏输入上面(app.js)代码测试中的地址, 如报错可查看vscode终端 ,注意数据库中列表表头名是否写错或者图片是否引入(商品列表 > 服务器程序 > 数据库 ⑤中)

商品列表

商城网站图片保存在服务器端 public
实战Vue:Mint UI移动端购物商城_第10张图片
服务器程序
数据库
①商品表 xz_laptop 添加一列 pic 保存商品指定图片
②更新xz_laptop eg: lid=1 01.jpg , lid>1 02.jpg
③截取两张图片 01.jpg ,02.jpg , 将图片保存服务器端public目录下 即vue_server_00/public

如果xz_laptop 表不存在需要导入此表
数据库中导入表格 ①#mysql -uroot -p > 拖动xz.sql 命令窗口按回车
②#mysql -uroot -p > use xz;>复制xz.sql内容 shell中右键(粘贴) ,如下

实战Vue:Mint UI移动端购物商城_第11张图片
vue_server_00/db01.sql

#db01.sql

#功能二:商品列表  
#1:向xz_laptop 商品列表中添加一列pic保存商品图片
USE xz;
ALTER TABLE xz_laptop ADD pic VARCHAR(255);
#2:更新pic列数据
UPDATE xz_laptop SET pic='01.jpg' WHERE lid = 1;
UPDATE xz_laptop SET pic='02.jpg' WHERE lid > 1;
#3:复制mysql控制台执行生效(你操作)

启动XAMPP,见端口号3306即为正常工作, 打开shell 输入mysql -uroot -p开启数据库,复制上面db01.sql指令,粘贴到MariaDB [(none)]> 后 , 本例两张图片即存进了数据库

未调图片进数据库报的错(vscode /node app.js) , 解决: shell中use xz;粘贴 db01.sql里的指令进来
实战Vue:Mint UI移动端购物商城_第12张图片

app.js功能二 : 显示商品列表-(分页)

node app.js功能二
请求方式 GET     请求地址 /product
参数    页码2 pno      一页几行 20 pageSize
sql     
SELECT lid,lname,price,pic FROM xz_laptop
LIMIT ?,?
? 起始行数 (算法公式:var offset =(pno-1)*pageSize;页码减1乘以页大小(页大小:一页有几行))
? 一页显示几行 pageSize
-将数据组装什么样式格式返回脚手架
res.send({code:1,msg:“查询成功”,data:result})

脚手架
创建组件 xz/Product.vue 分配访问路径 /Product
设计组件
实战Vue:Mint UI移动端购物商城_第13张图片
组件创建成功显示第一页数据
created(){} 当组件创建成功后立即调用此函数(创建 生命周期)
loadMore(){} 发送ajax请求获取商品列表

点击按钮显示下一页数据
方法一 : 点击加载更多按钮完成此操作-vue ui
加载更多
方法二 : 向上滑动完成下一页加载操作-小程序

准备工作④ : vscode中右键package.json打开终端 npm run serve
vue_app_00_null/src/components/2.21/xz/Product.vue

<template>
    
    <div class="product_app">
       
       <div class="goods-item" v-for="(item,i) of list" :key="i">
           
           <img :src="'http://127.0.0.1:4000/'+item.pic" />
           
           <h4>{{item.lname}}h4>
           
           <h5>{{item.price}}h5>
           
           <mt-button>加入购物车mt-button>
       div> 
       
       <mt-button size="large">查看购物车mt-button>
       
       <mt-button size="large" @click="loadMore">加载更多mt-button>
    div>
template>
<script>
export default {
     data(){
         return {
            list:[],//商品列表
            pno:0,  //页码1 2 3 .. 
         }
     },
     created(){//组件创建成功
         this.loadMore(); //加载数据
     },
     methods:{
        loadMore(){
         //console.log(123);    
         //功能:发送ajax请求获取服务器返回商品列表
         //     并且把数据保存data list:[]
         //node GET /product pno pageSize
         //1:创建变量url保存请求地址48
         var url = "product";
         //1.1:修改页码值加1
         this.pno++;
         //2:创建变量obj 请求服务器参数
         var obj = {pno:this.pno,pageSize:20}
         //3:发送ajax请求
         this.axios.get(url,{params:obj}).then(res=>{
          //4:获取服务器端返回商品列表数据
          //console.log(res.data);
          //5:在data添加属性list:[] 保存商品列表
          //this.list = res.data.data;
          //多页数据追加操作
          var rows = this.list.concat(res.data.data);
          this.list = rows;
          //6:将返回数据保存list结束
         })
        } 
     }
}
script>
<style scoped>
 /*1:最外层父元素弹性布局*/
 .product_app{
    display: flex;/*弹性布局*/
    flex-wrap:wrap;/*子元素换行*/
    justify-content: space-between;/*子元素两端对齐*/
    padding:4px;/*内边距*/ 
 }
 /*2:一个商品*/
 .goods-item{
    width:48%;/*占用屏幕一半*/
    border:1px solid #ccc;/*边框*/
    border-radius:5px;/*圆角*/
    margin:2px 0;/*外边距*/
    padding:2px;
    display:flex;   /*子元素设置弹性布局*/
    flex-direction: column;/*按列排放*/
    min-height:275px; 
 }
 /*3:一张图片   */
 .goods-item img{
     width:100%;
 }
style>
npm run serve报错

实战Vue:Mint UI移动端购物商城_第14张图片
解决方法 : 下载第三方ajax模块 脚手架文件夹下打开cmd 输入指令npm i axios安装 , vue_app_00_null/ npm i axios (参见 “发送ajax请求 [脚手架8080–>服务器4000]” 栏目)

vue_app_00_null/src/router/index.js

import Vue from 'vue'          //引入Vue对象
import Router from 'vue-router'//引入路由
//引入自定义组件
//1:引入组件Login.vue
import Login from "../components/2.21/xz/Login.vue"
import Product from "../components/2.21/xz/Product.vue"
Vue.use(Router)               
export default new Router({
  //配置组件-:访问路径
  routes: [
    {path:'/Login',component:Login},
    {path:'/Product',component:Product}
  ]
})
将商品添加至购物车功能

实战Vue:Mint UI移动端购物商城_第15张图片
功能分析 : 用户将商品添加至购物车 , 购物车数据可以保存在哪里?
-客户端浏览器cookie       京东
-服务器端数据库       xz_cart(此次方案)
-服务器端文件(读取慢 不可取)       cart.txt

node.js 程序(异步执行)

同步运行 异步运行
java;php;.net;c++ node.js
优点:有序 优点:高效 缺点:效率低 缺点:无序(难控制)

数据库表
xz_cart [id;lid;price;lname;count;uid];
id 数据编号
lid 产品编号
price 价格
lname 商品名称
count 购物此商品数量
uid 谁买的 uid

app.js功能三 : 将商品添加至购物车

中间人程序 app.js功能三
GET /addcart       lid,price,lname
xz_cart[id;lid,;lname;price;count,uid]
sql 语法:
#查询当前用户是否购买过此商品(没买过添加,买过更新)
SELECT id FROM xz_cart WHERE uid=? AND lid=?
#将用户喜欢商品添加至购物车表
INSERT INTO xz_cart VALUES(null,?,?,?,1,?);
#更新购物车中某件商品数量
UPDATE xz_cart SET count=count+1 WHERE id=? AND uid=?
遇到问题 : 以上操作会触发node异步工作方法(无序), 由于无序结果一定错!
解决问题 :
(1)回调函数then[钩子函数] --简单
(2)Promise 对象
vue_server_00/db02.sql

#功能三:创建购物车表
USE xz;
CREATE TABLE xz_cart(
  id INT PRIMARY KEY AUTO_INCREMENT,
  lid INT,
  lname VARCHAR(255),
  price DECIMAL(10,2),
  count INT,
  uid INT
);

全选复制db02.sql指令 , 打开XAMPP shell
实战Vue:Mint UI移动端购物商城_第16张图片
node app.js 重启服务器 , 要删除之前开过的 避免4000端口占用

app.js 中打开浏览器报错

在这里插入图片描述
数据库问题 : 原因:登录验证表(xz_login)不存在 , 购物车表(xz_cart)没有引入 , 商品列表(xz_laptop)不存在
XAMPP打开 Apache 然后点击MySQL的Admin按钮查看数据库是否存在 , 不存在则添加
实战Vue:Mint UI移动端购物商城_第17张图片
脚手架操作流程
为(加入购物车)按钮绑定点击事件
加入购物车
获取当前商品三个数据 lname;lid;price
加入购物车
发送ajax请求完成添加购物车操作
addcart(lname,lid,price){
var url = “addcart”;
var obj={lname,lid,price};
this.axios.get(url,{params:obj}).then(res=>{
})
}
服务器返回信息 {code:-2,msg:“未登录”} 跳转登录组件
this.$ router.push("/Login");
服务器返回信息 {code:1,msg:“添加成功”} 显示提示短消息
this.$toast(“添加成功”)

脚手架 添加购物车组件Product.vue

vue_app_00_null/src/components/2.21/xz/Product.vue

<template>
    
    <div class="product_app">
       
       <div class="goods-item" v-for="(item,i) of list" :key="i">
           
           <img :src="'http://127.0.0.1:4000/'+item.pic" />
           
           <h4>{{item.lname}}h4>
           
           <h5>{{item.price}}h5>
           
           <mt-button
            @click="addcart(item.lid,item.lname,item.price)"
            >加入购物车mt-button>
       div> 
       
       <mt-button size="large" @click="findCart">查看购物车mt-button>
       
       <mt-button size="large" @click="loadMore">加载更多mt-button>
    div>
template>
<script>
export default {
     data(){
         return {
            list:[],//商品列表
            pno:0,  //页码1 2 3 .. 
         }
     },
     created(){//组件创建成功
         this.loadMore(); //加载数据
     },
     methods:{
         findCart(){
             this.$router.push("/Cart");
         },
         addcart(lid,lname,price){
            console.log(1);
            console.log(lid+"_"+lname+"_"+price); 
           //功能:将商品信息添加至购物车  完成任务
           //1:创建变量url保存请求服务器地址
           var url="addcart";
           //2:创建变量obj请求时数据 lid,lname,price
           var obj = {lid,lname,price};
           //3:发送ajax请求
           this.axios.get(url,{params:obj}).then(res=>{//4:接收服务器返回数据
               if(res.data.code==-2){//5:判断code==-2 提示请登录  跳转 /Login组件
                  this.$toast("请登录");
                  this.$router.push("/Login");
               }else{
                  this.$toast("添加成功") //6:判断code==1 提示添加成功 
               }
           })  
         },
        loadMore(){   
         //console.log(123);    
         //功能:发送ajax请求获取服务器返回商品列表
         //     并且把数据保存data list:[]
         //node GET /product pno pageSize
         //1:创建变量url保存请求地址48
         var url = "product";
         //1.1:修改页码值加1
         this.pno++;
         //2:创建变量obj 请求服务器参数
         var obj = {pno:this.pno,pageSize:20}
         //3:发送ajax请求
         this.axios.get(url,{params:obj}).then(res=>{
          //4:获取服务器端返回商品列表数据
          //console.log(res.data);
          //5:在data添加属性list:[] 保存商品列表
          //this.list = res.data.data;
          //多页数据追加操作
          var rows = this.list.concat(res.data.data);
          this.list = rows;
          //6:将返回数据保存list结束
         })
        } 
     }
}
script>
<style scoped>
 /*1:最外层父元素弹性布局*/
 .product_app{
    display: flex;/*弹性布局*/
    flex-wrap:wrap;/*子元素换行*/
    justify-content: space-between;/*子元素两端对齐*/
    padding:4px;/*内边距*/ 
 }
 /*2:一个商品*/
 .goods-item{
    width:48%;/*占用屏幕一半*/
    border:1px solid #ccc;/*边框*/
    border-radius:5px;/*圆角*/
    margin:2px 0;/*外边距*/
    padding:2px;
    display:flex;   /*子元素设置弹性布局*/
    flex-direction: column;/*按列排放*/
    min-height:275px; 
 }
 /*3:一张图片   */
 .goods-item img{
     width:100%;
 }
style>

完成上面代码后, 检查数据库服务器是否开启正常,然后开启脚手架,打开浏览器查看
实战Vue:Mint UI移动端购物商城_第18张图片

常见错误一

登录不成功 Login.vue    tom/123
①正则表达式 ②脚手架没写完:登录成功跳转商品列表组件
Network error
如何解决:仔细查询app.js 服务器控制台错误信息
①数据库没有启动 错误码 Error: connect ECONNREFUSED 127.0.0.1:3306 mysql没启动
②表不存在
③sql语句写错了
④参数中数据不正确 undefined ;; lid/lname/price
错误码 ER_BAD_FIELD_ERROR: Unknown column 'undefined' in 'field list'

app.js功能四 : 查询(指定用户)购物车列表

实战Vue:Mint UI移动端购物商城_第19张图片
上图 : req.session.uid=1登录凭证 用户的uid    1脚手架组件发送给中间人node服务器    2查数据库    3返回    4数据显示到脚手架组件中
代码实现 :
数据库中的表 (购物车) xz_cart       xz_cart(id;lid;price;lname,count,uid)

中间人 app.js

  • 前置条件:用户必须登录成功后才能查询购物车信息
  • 判断登录凭证 req.session.uid
  • 创建sql 语句(服务器端代码)
    SELECT id,lid,lname,price,count FROM xz_cart WHERE uid = ?
  • 将查询结果返回脚手架
    res.send({code:1,msg:“查询成功”,data:result})

脚手架组件 (购物车) Cart.vue
实战Vue:Mint UI移动端购物商城_第20张图片

app.js 功能一 – 功能五

vue_server_00/app.js

//功能:服务器程序
//1:引入四个模块
const express = require("express"); //web服务器模块
const mysql = require("mysql");//mysql模块
const session = require("express-session");//session模块
const cors = require("cors");//跨域
//2:创建连接池
var pool = mysql.createPool({
    host:"127.0.0.1",
    user:"root",
    password:"",
    database:"xz",
    port:3306,
    connectionLimit:15
})
//3:创建web服务器
var server = express();
//4:配置跨域模块
//4.1:允许程序列表 脚手架
//4.2:每次请求验证
server.use(cors({
    origin:["http://127.0.0.1:8080","http://localhost:8080"],
    credentials:true 
}))
//5:指定静态资源目录 public
server.use(express.static("public"));
//6:配置session对象
server.use(session({
    secret:"128位安全字符串",//加密条件
    resave:true,//每次请求更新数据
    saveUninitialized:true,//保存初始化数据
}));
//7:为服务器绑定监听端口 4000
server.listen(4000);
console.log("服务器起动.......");

//功能一:用户登录验证
server.get("/login",(req,res)=>{
  //1:获取脚手架传递用户名和密码
  var u = req.query.uname;
  var p = req.query.upwd;
  //2:创建sql语法并且将用户名和密码加入
  var sql = "SELECT id FROM xz_login WHERE uname=? AND upwd=md5(?)";
  //3:执行sql语法并且获取返回结果
  pool.query(sql,[u,p],(err,result)=>{
     //3.1:如果出现严重错误抛出  
     if(err)throw err;
     //3.2:如果result.length长度为0,表示登录失败
     if(result.length==0){
         res.send({code:-1,msg:"用户名或密码有误"});
     }else{
         //登录成功
         //如果用户登录成功:创建session对象并且将登录凭证uid
         //保存对象中    result=[{id:1}]
         //将当前登录用户id保存session对象中作业:登录凭证
         req.session.uid = result[0].id;
         res.send({code:1,msg:"登录成功"})
     }
  });
})
//测试:你 
//启动服务器  node app.js
//打开浏览器在地址栏输入
//      http://127.0.0.1:4000/login?uname=tom&upwd=123
//      http://127.0.0.1:4000/login?uname=tom&upwd=122

//功能二:显示商品列表-(分页)
//1:GET /product 
server.get("/product",(req,res)=>{
//2:接收参数 页码 一页几行
var pno = req.query.pno;
var ps = req.query.pageSize;
//3:为参数设置默认值  1   20
if(!pno){pno=1}
if(!ps){ps=20}
//4:创建sql语句分页查询商品列表
var sql = "SELECT lid,lname,price,pic FROM xz_laptop LIMIT ?,?";
var offset = (pno-1)*ps;
ps = parseInt(ps);
//5:执行sql语句并且将结果发送脚手架
pool.query(sql,[offset,ps],(err,result)=>{
    if(err)throw err;
    res.send({code:1,msg:"查询成功",data:result})
})
})

//测试
//   1:启动node app.js
//   2:http://127.0.0.1:4000/product
//   3:http://127.0.0.1:4000/product?pno=2
//   4:http://127.0.0.1:4000/01.jpg
//   5:http://127.0.0.1:4000/02.jpg



//功能三:将商品添加至购物车
server.get("/addcart",(req,res)=>{
  //1:获取当前登录用户凭证
  var uid = req.session.uid;
  //2:如果当前用户没有登录凭证  输出请登录
  if(!uid){
      res.send({code:-2,msg:"请登录"});
      return;
  }
  //3:获取脚手架传递数据 lid,lname,price
  var lid = req.query.lid;
  var lname = req.query.lname;
  var price = req.query.price;
  //4:创建sql语句 查询当前用户是否购买过此商品
  var sql = "SELECT id FROM xz_cart WHERE uid=? AND lid=?";
  //5:执行sql语句
  pool.query(sql,[uid,lid],(err,result)=>{
  //6:在回调函数(钩子函数)
  if(err)throw err;
  //7:获取查询结果[判断是否购买过此商品]
  if(result.length==0){
  //8:如果没买过此商品 创建INSERT SQL
  var sql = `INSERT INTO xz_cart VALUES(null,${lid},'${lname}',${price},1,${uid})`;
  }else{
  //9:如果己购买过此商品 创建UPDATE SQL
  var sql = `UPDATE xz_cart SET count=count+1 WHERE uid=${uid} AND lid=${lid}`;
  }
  //10:执行sql
  //11:返回结果脚手架
  pool.query(sql,(err,result)=>{
      if(err)throw err;
      res.send({code:1,msg:"添加成功"})
  })
})//select end

//测试
 // 1:启动 xmapp 启动mysql   
 // USE xz;
 // SELECT * FROM xz_cart;
 // 下载昨天db02.sql 执行
//2:启动node   node app.js
//3:浏览器地址
 // http://127.0.0.1:4000/addcart?lid=1&price=9&lname=aa
 // http://127.0.0.1:4000/login?uname=tom&upwd=123
 // http://127.0.0.1:4000/addcart?lid=1&price=9&lname=aa
 // http://127.0.0.1:4000/addcart?lid=1&price=9&lname=aa
 // SELECT * FROM xz_cart;
})


//功能四:查询指定用户购物车列表
server.get("/findcart",(req,res)=>{
  //1:获取用户登录凭证uid
  var uid = req.session.uid;
  //2:没有uid表示此用户未登录 发送请登录信息
  if(!uid){
      res.send({code:-2,msg:"请登录",data:[]});
      return;
  }
  //3:创建sql语句
  var sql = "SELECT id,lid,lname,price,count FROM xz_cart WHERE uid=?";
  //4:发送sql语句并且将查询结果返回脚手架
  pool.query(sql,[uid],(err,result)=>{
      if(err)throw err;
      res.send({code:1,msg:"查询成功",data:result});
  })
});
//测试
//  重新启动node    node app.js
//  打开浏览器在地址栏 
//  http://127.0.0.1:4000/findcart                   #请登录
//  http://127.0.0.1:4000/login?uname=tom&upwd=123 
//  http://127.0.0.1:4000/findcart                  #查询结果
//  http://127.0.0.1:4000/addcart?lid=1&price=9&lname=aa
//  http://127.0.0.1:4000/addcart?lid=2&price=9&lname=aa
//  http://127.0.0.1:4000/findcart                  #查询结果



//功能五:删除购物车中一条商品信息
server.get("/del",(req,res)=>{
  //1:获取用户登录凭证
  var uid = req.session.uid;
  //2:如果没有登录凭证 请登录
  if(!uid){
     res.send({code:-2,msg:"请登录"});
     return;
  }
  //3:获取脚手架传递数据 id
  var id = req.query.id;
  //4:创建sql依据id删除数据
  var sql = "DELETE FROM xz_cart WHERE id = ? AND uid=?";
  //5:执行sql语句获取返回结果
  pool.query(sql,[id,uid],(err,result)=>{
       if(err)throw err;
       //判断删除是否成功 affectedRows 影响行数
       if(result.affectedRows>0){
           res.send({code:1,msg:"删除成功"});
       }else{
           res.send({code:-1,msg:"删除失败"});
       }
  });
  //6:将结果返回脚手架
})

//测试
// 1:   重新启动 node app.js   
//       select * from xz_cart;
// 2:   打开浏览器测  http://127.0.0.1:4000/del?id=1
// 3:   打开浏览器测  http://127.0.0.1:4000/login?uname=tom&upwd=123
// 4:   打开浏览器测  http://127.0.0.1:4000/del?id=1
// 5:   打开浏览器测  http://127.0.0.1:4000/del?id=1
脚手架 购物车组件Cart.vue

vue_app_00_null/src/components/2.21/xz/Cart.vue

<template>
    <div>
        
        
        <div class="selectall">
            全选<input type="checkbox" v-model="isAgree"/>
        div>
        
        <div class="cartItem" v-for="(item,i) of list" :key="i">
             <div class="leftImgTxt">
                 <input type="checkbox" :checked="isAgree"/>
                 <div class="title">{{item.lname}}div>
                 <div class="title">{{item.price}}div>
             div>
             <mt-button @click="del(item.id)">mt-button>
        div>
        
        <div>
            购物车中商品数量<span style="color:red">3span>
            <mt-button>删除选中商品mt-button>
            <mt-button>清空购物车mt-button>
        div>
    div>
template>
<script>
export default {
    data(){
        return {
            list:[],       //购物车购物信息
            isAgree:false, //全选按钮状态
        }
    },
    created(){
      //组件创建成功后获取购物车列表信息
      this.loadMore();  
    },
    methods:{
        del(id){
          //功能:删除购物车中指定商品
          console.log(id);
          //1:显示交互对话框
          this.$messagebox.confirm("是否删除指定商品?")
          .then(res=>{ //用户点击确认按钮
          //2:如果有户选中 确认
          //3:创建变量url 保存中间人删除程序地直 /del
          var url = "del";
          //4:创建变量obj 保存id
          var obj = {id}
          //5:发送ajax请求
          this.axios.get(url,{params:obj}).then(res=>{
            //6:获取返回结果 提示 重新加载数据[刷新]
            if(res.data.code==1){
                this.$toast("删除成功");
                this.loadMore();//重新加载数据 刷新
            }
          })
          })
          .catch(err=>{//用户点击取消按钮
          })
        },
        loadMore(){
            console.log(123);
            //发送请求获取购物车数据
            //1:创建变量url保存请求服务器程序地址
            var url = "findcart";
            //2:发送ajax请求并且获取服务器返回数据
            this.axios.get(url).then(res=>{
             //3:获取服务器返回数据 code==-2
             //  提示请登录  跳转Login组件
             if(res.data.code==-2){
                  this.$toast("请登录");
                  this.$router.push("/Login");
                  return;
             }else{
             //4:获取服务器返回数据  code==1
             //5:将服务器返回购物车列表保存list
             this.list = res.data.data;
             //console.log(this.list);
             //6:在模板中创建循环显示购物车列表数据即可 
             }
            })
        }
    }
}
script>
<style scoped>
 /*1:一个商品项目元素*/
 .cartItem{
     display:flex;/*弹性布局*/
     justify-content: space-between;/*子元素两端对齐*/
     align-items: center;/*子元素垂直居中*/
     border-bottom:1px solid #ccc;
     margin-top:25px;/*商品之间间距*/
 }
 /*2:左侧【商品名称与价格】*/
 .leftImgTxt{
     display: flex;
     align-items: center;
 }
 .title{
     margin-left:15px;
 }
style>

vue_app_00_null/src/router/index.js

import Vue from 'vue'          //引入Vue对象
import Router from 'vue-router'//引入路由
//引入自定义组件
//1:引入组件Login.vue
import Login from "../components/2.21/xz/Login.vue"
import Product from "../components/2.21/xz/Product.vue"
import Cart from "../components/2.21/xz/Cart.vue"
Vue.use(Router)               
export default new Router({
  //配置组件-:访问路径
  routes: [
    {path:'/Login',component:Login},
    {path:'/Product',component:Product},
    {path:'/Cart',component:Cart}
  ]
})

//打浏览器地址 http://127.0.0.1:8080/#/Cart
//                     "/"当前项目  "#"分隔符  "/"路径

保存引入组件路径文件,脚手架终端也会更新 终端代码DONE Compiled successfully in 178ms ,打开路径进入浏览器查看引入是否有误

查询购物车组件 Cart.vue

查询当前登录用户购物车列表
服务器: GET /findcart
脚手架组件Cart.vue
当组件创建成功后立即发送ajax请求 GET /findcart
服务器返回值
{code:-2,msg:“请求登录”}       this.$router.push("/Login")
{code:1,msg:“查询成功”,data:[…]}       res.data.data //第一个data是框架发请求所有用户都保存在data中 , 第二个是服务器保存数组的data,此处接收

常见错误二

登录成功后查询购物车,又跳转登录页面请求登录
问题根源 : session对象失效
解决问题 :
脚手架项目main.js中 axios.defaults.withCredentials=true 位置出错
服务器端 app.js 配置写错
server.use(session({
secret:“128位安全字符串”,//加密条件
resave:true,//每次请求更新数据
saveUninitialized:true,//保存初始化数据
}));

app.js功能五:删除购物车中一条商品信息

购物车第二个功能 : 删除一个指定商品
实战Vue:Mint UI移动端购物商城_第21张图片
注意事项
(1)删除商品为什么id
原因:id INT 主键 #删除速度极快
lname VARCHAR #速度慢
查询某一个商品;更新一个商品;删除一个商品 , id作为首选条件
(2)删除是一种有危险性操作 , 删除数据极难恢复

实现功能 :
数据库表    xz_cart(id;lid;lname;price;count;uid)
中间人
接收脚手架组件传递数据 id
sql        DELETE FROM xz_cart WHERE id = ? AND uid = ?
json       {code:1,msg:“删除成功”}
脚手架组件

del(id){
显示确认框:是否删除指定商品 确认 取消
url obj get
重新加载数据
}

全选按钮 Cart.vue

实战Vue:Mint UI移动端购物商城_第22张图片
全选                        Cart.vue

  • 当选中全选按钮       所有商品前复选框       选中
  • 当清空全选按钮       所有商品前复选框       清空
  • 如果手工选中所有商品       全选选中
  • 如果清除某一个商品选中状态       全选清空

常规解决方案 :
全选按钮绑定v-model="allcb"变量
商品放在list里,加个属性cb显示商品前面选中状态 list:[{cb:false,id:1,lname:"aa"},{cb:false,id:2,lname:"bb"}]
商品前面加一个事件@change=“itemChange” itemChange函数计算商品前面选中(cb:true)数allcb等于商品数

删除选中的商品 Cart.vue

分析组件
实战Vue:Mint UI移动端购物商城_第23张图片 [
{cb:false,id:1,lname:“mac”},
{cb:true,id:2,lname:“7888”},
{cb:true,id:3,lname:“7488”}
]
2,3”   删除编号为2编号为3商品
分析中间人app.js
参数:id=“2,3”
sql: "DELETE FROM xz_cart WHERE id =2 or id =3"
"DELETE FROM xz_cart WHERE id IN(2,3)"
写组件功能

购物车中商品数量 vuex

作用:vuex 脚手架全局变量保存多个组件共享数据
实战Vue:Mint UI移动端购物商城_第24张图片

vuex安装配置与使用

vuex特点
集合式存储多个组件共享数据(全局共享)
如果vuex中保存数据发生更新vuex通知所有使用此数据组件修改数据
vuex下载 : 脚手架文件夹下打开cmd 输入指令npm i vuex安装
vue_app_00_null >npm i vuex
vuex 配置 main.js

  1. 引入vuex 组件 import Vuex from "vuex"

  2. 注册 Vue.use(Vuex)

  3. 创建存储对象(!!) var store = new Vuex.Store({})

  4. 将存储对象添加到vue实例中
    new Vue({
    store
    })

语法 :
var store = new Vuex.Store({
store:{ //保存全局数据
fa:12 //亮哥头发根数
},
//如何需要获取全局数据或者修改此数据需要通过函数调用
mutations:{//保存修改全局数据函数
subFa(state){state.fa- -}//state.fa减减
},
getters:{ //获取全局数据函数
getFa(state){return state.fa}
}
}) //subFa getFa 是自定义函数名
vuex 使用 :

  • 在模板中获取数据
<template>
  <div>亮哥有几根头发<span>{{$store.getters.getFa}}</span></div>
</template>
  • 修改全局数据(js) this.$store.commit("subFa")
查询全选删除购物车 Cart.vue

vue_app_00_null/src/components/2.21/xz/Cart.vue

<template>
    <div>
        
        
        <div class="selectall">
            全选<input type="checkbox" v-model="allcb"  @change="selectAll"/>
        div>
        
        <div class="cartItem" v-for="(item,i) of list" :key="i">
             <div class="leftImgTxt">
                 <input type="checkbox" v-model="item.cb"  @change="changeItem"/>
                 <div class="title">{{item.lname}}div>
                 <div class="title">{{item.price}}div>
             div>
             <mt-button @click="del(item.id)">mt-button>
        div>
        
        <div>
            购物车中商品数量<span style="color:red">
               {{$store.getters.getCartCount}} 
            span>
            <mt-button @click="delm">删除选中商品mt-button>
            <mt-button>清空购物车mt-button>
        div>
    div>
template>
<script>
export default {
    data(){
        return {
            list:[],       //购物车购物信息
            allcb:false,   //全选按钮状态
        }
    },
    created(){
      //组件创建成功后获取购物车列表信息
      this.loadMore();  
    },
    methods:{
        sub(){
           //功能:修改全局共享数据fa
           this.$store.commit("subFa"); 
        },
        delm(){
            //功能:删除选中商品
            //1:判断商品数量如果数量为0 this.list
            //2:显示确认框   当前没有可删除商品
            //3:返回
            if(this.list.length==0){
               this.$messagebox("消息","当前没有可删除商品");
               return;
            }
            //4:创建变量str  目的拼接字符串 "2,3,4"
            var str = "";
            //5:创建循环遍历数组 this.list
            //6:判断当前商品是否是选中状态 cb==true
            //  获取id拼接字符串str+","
            for(var item of this.list){
                if(item.cb){str+=item.id+","}
            }
            //str  = "";
            //7:判断:如果用户没有选中商品str.length==0
            //8:显示确认框  请选择要删除的商品
            if(str.length==0){
              this.$messagebox("消息","请选择需要删除的商品");
              return; 
            }
            //8.1:截取字符串  "2,3,"=>"2,3"
            str = str.substring(0,str.length-1);
            //9:显示确认交互框 是否删除指数据
            //10:创建变量 url obj
            //11:发送ajax请求删除指定数据
            this.$messagebox.confirm("是否删除指定商品?")
            .then(res=>{//用户选中  确认按钮
                var url = "delm";
                var obj = {id:str};
                this.axios.get(url,{params:obj}).then(res=>{
                    //重新请求数据刷新页面
                    this.loadMore();
                    this.$toast("删除成功")
                })
            })
            .catch(err=>{})//用户选中 取消按钮  
        },
        selectAll(){
            //功能:全选按钮状态修改操作
            //1:获取全选按钮状态
            var cb = this.allcb;
            //2:创建循环遍历所有商品状态赋值
            for(var item of this.list){
                item.cb = cb;
            }
        },
        changeItem(){
            //功能:商品状态修改操作
            //功能一:累加商品状态为true
            var sum=0;
            for(var item of this.list){
                if(item.cb)sum++;
            }
            //功能二:判断如果商品总数量与等于true总数量相同
            if(this.list.length==sum){
                this.allcb = true;
            }else{
                this.allcb = false;
            }

        },
        del(id){
          //功能:删除购物车中指定商品
          console.log(id);
          //1:显示交互对话框
          this.$messagebox.confirm("是否删除指定商品?")
          .then(res=>{ //用户点击确认按钮
          //2:如果有户选中 确认
          //3:创建变量url 保存中间人删除程序地直 /del
          var url = "del";
          //4:创建变量obj 保存id
          var obj = {id}
          //5:发送ajax请求
          this.axios.get(url,{params:obj}).then(res=>{
            //6:获取返回结果 提示 重新加载数据[刷新]
            if(res.data.code==1){
                this.$toast("删除成功");
                this.loadMore();
            }
          })
          })
          .catch(err=>{//用户点击取消按钮
          })
        },
        loadMore(){
            console.log(123);
            //发送请求获取购物车数据
            //1:创建变量url保存请求服务器程序地址
            var url = "findcart";
            //2:发送ajax请求并且获取服务器返回数据
            this.axios.get(url).then(res=>{
             //3:获取服务器返回数据 code==-2
             //  提示请登录  跳转Login组件
             if(res.data.code==-2){
                  this.$toast("请登录");
                  this.$router.push("/Login");
                  return;
             }else{
             //4:获取服务器返回数据  code==1
             //5:将服务器返回购物车列表保存list
             //this.list = res.data.data;
             //注意:以下程序顺序
             //5.1:获取服务器中购物车列表,临时保存变量rows
             var rows = res.data.data;
             //5.2:创建循环遍历数组中每一个商品并且添加cb属性
             //    表示商品是否选中状态
             for(var item of rows){
                 item.cb = false;
             }
             //5.3:将新数组赋值list
             this.list = rows;
             //console.log(this.list);
             //6:在模板中创建循环显示购物车列表数据即可
             //7:修改购物车全局数据
             var sum = 0;
             for(var item of this.list){
                 sum+=item.count;
             }
            this.$store.commit("addmCart",sum);
             }
            })
        }
    }
}
script>
<style scoped>
 /*1:一个商品项目元素*/
 .cartItem{
     display:flex;/*弹性布局*/
     justify-content: space-between;/*子元素两端对齐*/
     align-items: center;/*子元素垂直居中*/
     border-bottom:1px solid #ccc;
     margin-top:25px;/*商品之间间距*/
 }
 /*2:左侧【商品名称与价格】*/
 .leftImgTxt{
     display: flex;
     align-items: center;
 }
 .title{
     margin-left:15px;
 }
style>
app.js 功能一 – 功能六

服务器 vue_server_00/app.js

//功能:服务器程序
//1:引入四个模块
const express = require("express"); //web服务器模块
const mysql = require("mysql");//mysql模块
const session = require("express-session");//session模块
const cors = require("cors");//跨域
//2:创建连接池
var pool = mysql.createPool({
    host:"127.0.0.1",
    user:"root",
    password:"",
    database:"xz",
    port:3306,
    connectionLimit:15
})
//3:创建web服务器
var server = express();
//4:配置跨域模块
//4.1:允许程序列表 脚手架
//4.2:每次请求验证
server.use(cors({
    origin:["http://127.0.0.1:8080","http://localhost:8080"],
    credentials:true 
}))
//5:指定静态资源目录 public
server.use(express.static("public"));
//6:配置session对象
server.use(session({
    secret:"128位安全字符串",//加密条件
    resave:true,//每次请求更新数据
    saveUninitialized:true,//保存初始化数据
}));
//7:为服务器绑定监听端口 4000
server.listen(4000);
console.log("服务器起动.......");

//功能一:用户登录验证
server.get("/login",(req,res)=>{
  //1:获取脚手架传递用户名和密码
  var u = req.query.uname;
  var p = req.query.upwd;
  //2:创建sql语法并且将用户名和密码加入
  var sql = "SELECT id FROM xz_login WHERE uname=? AND upwd=md5(?)";
  //3:执行sql语法并且获取返回结果
  pool.query(sql,[u,p],(err,result)=>{
     //3.1:如果出现严重错误抛出  
     if(err)throw err;
     //3.2:如果result.length长度为0,表示登录失败
     if(result.length==0){
         res.send({code:-1,msg:"用户名或密码有误"});
     }else{
         //登录成功
         //如果用户登录成功:创建session对象并且将登录凭证uid
         //保存对象中    result=[{id:1}]
         //将当前登录用户id保存session对象中作业:登录凭证
         req.session.uid = result[0].id;
         res.send({code:1,msg:"登录成功"})
     }
  });
})
//测试:你 
//启动服务器  node app.js
//打开浏览器在地址栏输入
//      http://127.0.0.1:4000/login?uname=tom&upwd=123
//      http://127.0.0.1:4000/login?uname=tom&upwd=122

//功能二:显示商品列表-(分页)
//1:GET /product 
server.get("/product",(req,res)=>{
//2:接收参数 页码 一页几行
var pno = req.query.pno;
var ps = req.query.pageSize;
//3:为参数设置默认值  1   20
if(!pno){pno=1}
if(!ps){ps=20}
//4:创建sql语句分页查询商品列表
var sql = "SELECT lid,lname,price,pic FROM xz_laptop LIMIT ?,?";
var offset = (pno-1)*ps;
ps = parseInt(ps);
//5:执行sql语句并且将结果发送脚手架
pool.query(sql,[offset,ps],(err,result)=>{
    if(err)throw err;
    res.send({code:1,msg:"查询成功",data:result})
})
})

//测试
//   1:启动node app.js
//   2:http://127.0.0.1:4000/product
//   3:http://127.0.0.1:4000/product?pno=2
//   4:http://127.0.0.1:4000/01.jpg
//   5:http://127.0.0.1:4000/02.jpg



//功能三:将商品添加至购物车
server.get("/addcart",(req,res)=>{
  //1:获取当前登录用户凭证
  var uid = req.session.uid;
  //2:如果当前用户没有登录凭证  输出请登录
  if(!uid){
      res.send({code:-2,msg:"请登录"});
      return;
  }
  //3:获取脚手架传递数据 lid,lname,price
  var lid = req.query.lid;
  var lname = req.query.lname;
  var price = req.query.price;
  //4:创建sql语句 查询当前用户是否购买过此商品
  var sql = "SELECT id FROM xz_cart WHERE uid=? AND lid=?";
  //5:执行sql语句
  pool.query(sql,[uid,lid],(err,result)=>{
  //6:在回调函数(钩子函数)
  if(err)throw err;
  //7:获取查询结果[判断是否购买过此商品]
  if(result.length==0){
  //8:如果没买过此商品 创建INSERT SQL
  var sql = `INSERT INTO xz_cart VALUES(null,${lid},'${lname}',${price},1,${uid})`;
  }else{
  //9:如果己购买过此商品 创建UPDATE SQL
  var sql = `UPDATE xz_cart SET count=count+1 WHERE uid=${uid} AND lid=${lid}`;
  }
  //10:执行sql
  //11:返回结果脚手架
  pool.query(sql,(err,result)=>{
      if(err)throw err;
      res.send({code:1,msg:"添加成功"})
  })
})//select end

//测试
 // 1:启动 xmapp 启动mysql   
 // USE xz;
 // SELECT * FROM xz_cart;
 // 下载昨天db02.sql 执行
//2:启动node   node app.js
//3:浏览器地址
 // http://127.0.0.1:4000/addcart?lid=1&price=9&lname=aa
 // http://127.0.0.1:4000/login?uname=tom&upwd=123
 // http://127.0.0.1:4000/addcart?lid=1&price=9&lname=aa
 // http://127.0.0.1:4000/addcart?lid=1&price=9&lname=aa
 // SELECT * FROM xz_cart;
})


//功能四:查询指定用户购物车列表
server.get("/findcart",(req,res)=>{
  //1:获取用户登录凭证uid
  var uid = req.session.uid;
  //2:没有uid表示此用户未登录 发送请登录信息
  if(!uid){
      res.send({code:-2,msg:"请登录",data:[]});
      return;
  }
  //3:创建sql语句
  var sql = "SELECT id,lid,lname,price,count FROM xz_cart WHERE uid=?";
  //4:发送sql语句并且将查询结果返回脚手架
  pool.query(sql,[uid],(err,result)=>{
      if(err)throw err;
      res.send({code:1,msg:"查询成功",data:result});
  })
});
//测试
//  重新启动node    node app.js
//  打开浏览器在地址栏 
//  http://127.0.0.1:4000/findcart                   #请登录
//  http://127.0.0.1:4000/login?uname=tom&upwd=123 
//  http://127.0.0.1:4000/findcart                  #查询结果
//  http://127.0.0.1:4000/addcart?lid=1&price=9&lname=aa
//  http://127.0.0.1:4000/addcart?lid=2&price=9&lname=aa
//  http://127.0.0.1:4000/findcart                  #查询结果



//功能五:删除购物车中一条商品信息
server.get("/del",(req,res)=>{
  //1:获取用户登录凭证
  var uid = req.session.uid;
  //2:如果没有登录凭证 请登录
  if(!uid){
     res.send({code:-2,msg:"请登录"});
     return;
  }
  //3:获取脚手架传递数据 id
  var id = req.query.id;
  //4:创建sql依据id删除数据
  var sql = "DELETE FROM xz_cart WHERE id = ? AND uid=?";
  //5:执行sql语句获取返回结果
  pool.query(sql,[id,uid],(err,result)=>{
       if(err)throw err;
       //判断删除是否成功 affectedRows 影响行数
       if(result.affectedRows>0){
           res.send({code:1,msg:"删除成功"});
       }else{
           res.send({code:-1,msg:"删除失败"});
       }
  });
  //6:将结果返回脚手架
})

//测试
// 1:   重新启动 node app.js   
//       select * from xz_cart;
// 2:   打开浏览器测  http://127.0.0.1:4000/del?id=1
// 3:   打开浏览器测  http://127.0.0.1:4000/login?uname=tom&upwd=123
// 4:   打开浏览器测  http://127.0.0.1:4000/del?id=1
// 5:   打开浏览器测  http://127.0.0.1:4000/del?id=1


//功能六:删除用户指定商品
//1:接收请求 /delm
server.get("/delm",(req,res)=>{
//2:获取用户登录凭证
var uid = req.session.uid;
//3:请登录
if(!uid){
  res.send({code:-2,msg:"请登录"});
  return;
}
//4:接收参数 id = "2,3"
var id = req.query.id;
//5:创建sql语句执行删除多条记录功能
var sql = `DELETE FROM xz_cart WHERE id IN (${id})`;
//6:判断是否删除成功 并且返回值
pool.query(sql,(err,result)=>{
    if(err)throw err;
    if(result.affectedRows>0){
        res.send({code:1,msg:"删除成功"})
    }else{
        res.send({code:-1,msg:"删除失败"})
    }
})
})

  //测试
  //  1:查询mysql xz_cart 表几个id
  //  2:重新启动node app.js
  //  3:打开浏览器
  //  http://127.0.0.1:4000/delm?id=2,3
  //  http://127.0.0.1:4000/login?uname=tom&upwd=123
  //  http://127.0.0.1:4000/delm?id=2,3

脚手架引入需要的组件库 vue_app_00_null/src/main.js

import Vue from 'vue'       //vue实例对象
import App from './App.vue' //根组件
import router from './router'//路由模块

//选择代码 main.js 5~75 删除   删除!!!
//选择一行代码删除 
//功能一:引入和配置mint-ui组件库
//1:引入mint-ui所有组件
import MintUI from "mint-ui"
//2:单独引入mint-ui样式文件
import "mint-ui/lib/style.css"
//3:将mint-ui对象注册Vue实例 34
Vue.use(MintUI);

//功能二:引入图标字体中样式文件,使所有自定义组件均可使用
import "./font/iconfont.css"

//功能三:引入axios库
//1:引入axios库
import axios from "axios"
//2:配置访问服务器基础路径
axios.defaults.baseURL="http://127.0.0.1:4000/"
//3:配置保存session数据
axios.defaults.withCredentials=true
//4:注册
Vue.prototype.axios = axios;

//功能四:引入全局对象vuex存储数据
//1:引入vuex
import Vuex from "vuex";
//2:注册vuex
Vue.use(Vuex);
//3:创建存储对象
var store = new Vuex.Store({
  state:{   //全局共享数据
     fa:12,      //亮哥头发数量
     cartCount:0,//购物车中商品数量
  },
  mutations:{//所有修改全局共享数据函数
    subFa(state){state.fa--},
    subCart(state){state.cartCount--},//减一个
    addmCart(state,n){state.cartCount=n},//加多个
    clearCart(state){state.cartCount=0},//清空购物车数量
  },
  getters:{  //所有获取全局共享数函数
    getFa(state){return state.fa},
    getCartCount(state){return state.cartCount}
  }
})

//以下代码一定在main.js最后
new Vue({
  router,         //路由对象
  render: h => h(App),
  store           //4:将存储对象添加vue实例中
}).$mount('#app')

启动数据库 服务器 脚手架

你可能感兴趣的:(Vue,Angular,React)