前端(jquery)+ 后端(nodeJS)+数据库(mysql)实现云点餐

效果

  1. 主页
    请添加图片描述

  2. 购物车
    请添加图片描述

  3. 用户界面
    请添加图片描述

  4. 商品详细介绍
    请添加图片描述

  5. 实现方法

  • 项目前端采用了jquery,并还引入了轮播图插件swiper用来制作轮播效果
  • 用户进入主界面时,前端采用ajax技术向后端请求轮播图和商品信息
  • 用户点击搜索时,后端根据用户搜索返回相应信息
  • 在商品信息页面,客户可以加入购物车,也可以点击进去查看商品,这里通过绑定点击事件直接将商品id传入,以便后续使用
  • 再加入购物车时,需要判定一下购物车是非存在该商品
  • 在购物车页面,用户可以增加,减少,删除,这个通过拿到text里面的值,点击加时就加一并将值重新赋给text文本框
  • 用户可以点击登录或注册完成登录,这里通过ajax向后台发送请求时要携带输入框中内容
  • 判定当用户登陆后,拿到后台给的信息并渲染欢迎页面
  • 后端在主页面需要解决跨域问题,封装res.send信息,并且需要收集错误信息并判断出错内容
  • 在路由代码模块,需要引入模式验证,对前端传来的用户名,密码,id进行模式验证,强度验证
  • 在路由处理中,注册用户名时需要先对用户名进行判定是否已经存在该用户名,判定没有该用户才将用户名和密码存入数据库中
  • 搜索处理中,拿到前端传入的信息进行比较,正确后向数据库进行查询

前端

  1. 主界面
  • 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">
    <title>Document</title>
    <link rel="stylesheet" href="../lib/font/iconfont.css">
    <link rel="stylesheet" href="../lib/swiper8/swiper-bundle.min.css">
    <link rel="stylesheet" href="../css/common.css">
    <link rel="stylesheet" href="../css/index.css">
</head>
<body>
    <div id="head">
        <div>云点餐项目</div>
        <div class="search">
            <input type="text" name="search">
            <span class="iconfont icon-sousuo"></span>
        </div>
    </div>
    <div id="main">
        <div class="swiper">
            <div class="swiper-wrapper">
                <!-- <div class="swiper-slide">Slide 1</div>
                <div class="swiper-slide">Slide 2</div>
                <div class="swiper-slide">Slide 3</div> -->
            </div>
            <!-- 如果需要分页器 -->
            <div class="swiper-pagination"></div>
            
            <!-- 如果需要导航按钮 -->
            <div class="swiper-button-prev"></div>
            <div class="swiper-button-next"></div>
        </div>
        <div class="specificFunctions"></div>
        <div class="render">
            
        </div>
    </div>
    <div id="foot">
        <span class="iconfont icon-dingdan"></span>
        <span class="iconfont icon-gouwuche"></span>
        <span class="iconfont icon-yonghu"></span>
    </div>
    <script src="../lib/swiper8/swiper-bundle.min.js"></script>
    <script src="../js/jquery.js"></script>
    <script src="../js/index.js"></script>
   
</body>
</html>
  • JS
//获取轮播图信息
function getBanner(){
    $.ajax({
        url:'http://127.0.0.1:4000/api/banner',
        method:'get',
        success:function(data){
            if(data.status==0){
                let banner=data.data
                //拿到数据遍历出来
            let bannerStr=banner.map(item=>{
                return `
                <div class="swiper-slide"><img src="${item.url}"></div>
                `
            }).join('')
            $('.swiper-wrapper').html(bannerStr)
            //渲染完成后初始化轮播图
            var mySwiper = new Swiper ('.swiper', {
                direction: 'horizontal', // 垂直切换选项
                loop: true, // 循环模式选项
                autoplay:true,
                // 如果需要分页器
                pagination: {
                el: '.swiper-pagination',
                },
                
                // 如果需要前进后退按钮
                navigation: {
                nextEl: '.swiper-button-next',
                prevEl: '.swiper-button-prev',
                },
                
                // 如果需要滚动条
                scrollbar: {
                el: '.swiper-scrollbar',
                },
            }) 
            }
        }
    })
}
//渲染商品列表
function render(){
    $.ajax({
        url:'http://127.0.0.1:4000/api/list',
        method:'get',
        success:function(data){
            if(data.status==0){
                let list=data.data
                //拿到数据后遍历出来
                let productStr=list.map(item=>{
                    return `
                        <div class="listSingle">
                            <img src="${item.url}" οnclick=shoplist(${item.id})>
                            <div>
                                <p>${item.product}</p>
                                <p>¥${item.id}</p>
                            </div>
                            <span class="iconfont icon-gouwuche" data-index="${item.id}")></span>
                        </div>
                    `

                }).join('')
               $('.render').html(productStr)
               //加入购物车
               addCart()
            }
        }
    })

}
//搜索框搜索功能
function search(){
    $('.icon-sousuo').on('click',function(){
        $.ajax({
            url:'http://127.0.0.1:4000/api/search',
            method:'get',
            data:{
                info:$('input[name="search"]').val()
            },
            success:function(data){
                if(data.status==1){
                    alert(data.message)
                }else{
                    let list=data.data
                    let productStr=list.map(item=>{
                        return `
                            <div class="listSingle")>
                                <img src="${item.url}">
                                <div>
                                    <p>${item.product}</p>
                                    <p>¥${item.id}</p>
                                </div>
                                <span class="iconfont icon-gouwuche"></span>
                            </div>
                        `
                    }).join('')
                   $('.render').html(productStr) 
                }
            }
        })
    })
}
//底部跳转功能
function jump(){
    $('#foot').on('click','.icon-dingdan',function(){
        location.href='index.html'
    }).on('click','.icon-gouwuche',function(){
        location.href='gouwuche.html'
    }).on('click','.icon-yonghu',function(){
        location.href="user.html"
    })
}
//加入购物车
function addCart(){
    $('.listSingle').on('click','.icon-gouwuche',function(){
   		//获取购物车
        let id=$(this).attr('data-index');
       $.ajax({
        url:'http://127.0.0.1:4000/api//list/id',
        method:'get',
        data:{
            id:id
        },
        success:function(data){
            console.log(data);
            if(data.status==0){
            	//拿到本地存储的购物车信息
                let product=localStorage.getItem('shopList')
                let productArr=JSON.parse(product)||[]
                //判定是否已经有该商品
                let isOk=productArr.some(item=>item[0].id==id)
                if(!isOk){
                    productArr.push(data.data)
                    localStorage.setItem('shopList',JSON.stringify(productArr))
                }
               
            }
        }
       })
        
    })
}
//进入商品信息界面
function shoplist(id){
    location.href='shoplist.html?id='+id
}

getBanner()
render()
search()
jump()
  • CSS
#main {
  width: 100%;
  flex: 1;
  overflow: auto;
  background-color: aquamarine;
}
#main .swiper {
  width: 100%;
  height: 350px;
}
#main .specificFunctions {
  width: 100%;
  height: 288px;
  background: url("../images/bc1.jpg") no-repeat 0px -228px;
  background-size: 700px auto;
}
#main .render .listSingle {
  transition: 0.25s transform ease-in-out;
  margin-top: 30px;
  display: flex;
}
#main .render .listSingle img {
  width: 438px;
  height: 350px;
}
#main .render .listSingle div {
  margin-left: 20px;
}
#main .render .listSingle div p:nth-child(1) {
  font-size: 40px;
  font-weight: 700;
}
#main .render .listSingle div p:nth-child(2) {
  margin-top: 110px;
  font-size: 20px;
  color: grey;
}
#main .render .listSingle span {
  margin-left: 18px;
  margin-top: 86px;
  font-size: 77px;
  color: #f06c60;
}

/*# sourceMappingURL=index.css.map */
  1. 购物车代码
  • 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">
    <title>Document</title>
    <link rel="stylesheet" href="../lib/font/iconfont.css">
    <link rel="stylesheet" href="../css/common.css">
    <link rel="stylesheet" href="../css/gouwuche.css">
</head>
<body>
    <div id="head">
        <div>云点餐项目</div>
        <div class="search">
            <input type="text" name="search">
            <span class="iconfont icon-sousuo"></span>
        </div>
    </div>
    <div id="main">
        <table></table>
        <div class="total"></div>
    </div>
    <div id="foot">
        <span class="iconfont icon-dingdan"></span>
        <span class="iconfont icon-gouwuche"></span>
        <span class="iconfont icon-yonghu"></span>
    </div>
    <script src="../js/jquery.js"></script>
    <script src="../js/gouwuche.js"></script>
</body>
</html>
  • JS
//底部跳转功能
function jump(){
    $('#foot').on('click','.icon-dingdan',function(){
        location.href='index.html'
    }).on('click','.icon-gouwuche',function(){
        location.href='gouwuche.html'
    }).on('click','.icon-yonghu',function(){
        location.href="user.html"
    })
}
//渲染商品信息
function getlist(){
    let product=localStorage.getItem('shopList')
    let productArr=JSON.parse(product)||[]
    let tableHead=`
    <tr>
        <td>商品名</td>
        <td>图片</td>
        <td>价格</td>
        <td>数量</td>
        <td>总价</td>
        <td>删除</td>
        </tr>
    `
    let tableStr=productArr.map(item=>{
        return `
            <tr>
                <td>${item[0].product}</td>
                <td><img src="${item[0].url}"></td>
                <td>${item[0].id}</td>
                <td class="fun"><input type="button" name="sub" value="-"><input type="text" name="text" value="1"><input type="button" name="add" value="+"></td>
                <td class="singleTotal">${item[0].id}</td>
                <td class="del" data-index="${item[0].id}">删除</td>
            <tr>
        `
    }).join('')
    $('table').html(tableHead+tableStr)
}
//删除功能
$('table').on('click','.del',function(){
	//拿到id根据id删除
    let id=$(this).attr('data-index')
    let data=JSON.parse(localStorage.getItem('shopList'))
    let productIndex=data.findIndex(item=>item.id==id)
    data.splice(productIndex,1)
    localStorage.setItem('shopList',JSON.stringify(data))
    getlist()
})
//增加和减少
$('table').on('click','input[name="sub"]',function(){
        
    if($(this).next().val()>1){
    $(this).next().val($(this).next().val()-1)
    let priceAll=$(this).next().val()*$(this).parent().prev().html()
    $(this).parent().next().html(priceAll.toFixed(2))
    }
    total()
 }).on('click','input[name="add"]',function(){
     $(this).prev().val(parseInt($(this).prev().val())+1)
     let priceAll=$(this).prev().val()*$(this).parent().prev().html()
     $(this).parent().next().html(priceAll.toFixed(2))
     total()
 })
 //计算总价
 function total(){
    let sum=0
    $('.singleTotal').each((index,item)=>{
        sum=sum+Number($(item).html())
    })
    $('.total').html('¥'+sum)
 }
 total()

getlist()
jump()
  • CSS
#main {
  width: 100%;
  flex: 1;
  overflow: auto;
  background-color: aquamarine;
}
#main table {
  width: 100%;
  height: 100%;
}
#main table tr td img {
  width: 80px;
  height: 80px;
}
#main table tr .fun {
  width: 100px;
}
#main table tr .fun input {
  vertical-align: top;
  width: 20px;
  height: 20px;
}
#main .total {
  margin-left: 30px;
  font-size: 30px;
  color: red;
}

/*# sourceMappingURL=gouwuche.css.map */
  1. 用户界面代码
  • 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">
    <title>Document</title>
    <link rel="stylesheet" href="../lib/font/iconfont.css">
    <link rel="stylesheet" href="../css/common.css">
    <style>
        #head{
            height: 300px;
        }
        #main{
            height: 100%;
        }
        #main .login,.sigin,.siginout{
            
            width: 700px;
            height: 50px;
            background-color: #f46c60;
            text-align: center;
            line-height: 50px;
            cursor: pointer;
            
        }
        .input{
            width: 700px;
            height: 50px;
            text-align: center;
        }
        .input input{
            width: 400px;
            height: 50px;
            outline: none;
        }
    </style>
</head>
<body>
    <div id="head">
        <div>云点餐项目</div>
        <div class="search">
            <input type="text" name="search">
            <span class="iconfont icon-sousuo"></span>
        </div>
    </div>
   <div id="main">
    <div class="sigin">登录</div>
    <div class="login">注册</div>
    <div class="input">
        <!-- <form class="loginInput">
            <input type="text" name="name"><br>
            <input type="password" name="password"><br>
            <input type="submit" name="submit" value="登录提交">
        </form> -->
    </div>
   </div>
    <div id="foot">
        <span class="iconfont icon-dingdan"></span>
        <span class="iconfont icon-gouwuche"></span>
        <span class="iconfont icon-yonghu"></span>
    </div>
    <script src="../js/jquery.js"></script>
    <script src="../js/user.js"></script>
    <script>
        
        
    </script>
</body>
</html>
  • JS
//底部跳转功能
function jump(){
    $('#foot').on('click','.icon-dingdan',function(){
        location.href='index.html'
    }).on('click','.icon-gouwuche',function(){
        location.href='gouwuche.html'
    }).on('click','.icon-yonghu',function(){
        location.href="user.html"
    })
}
//渲染注册输入框
$('.login').on('click',function(){
   let str=`
        <form class="loginInput">
            <input type="text" name="name"><br>
            <input type="password" name="password"><br>
            <input type="submit" name="submit" value="注册提交">
        </form>
        `
    $('.input').html(str)
    login()
})
//渲染登录输入框
$('.sigin').on('click',function(){
    let str=`
        <form class="siginInput">
            <input type="text" name="name"><br>
            <input type="password" name="password"><br>
            <input type="submit" name="submit" value="登录提交">
        </form>
        `
    $('.input').html(str)
    sigin()
})
//注册功能
function login(){
    $('.loginInput').on('submit',function(e){
        e.preventDefault()
        $.ajax({
            url:'http://127.0.0.1:4000/api/login',
            method:'get',
            data:{
                username:$('.loginInput input[name="name"]').val(),
                password:$('.loginInput input[name="password"]').val(),
            },
            success:function(data){
                if(data.status==0){
                    alert('注册成功')
                }
            }
        })
    })
}
//登录功能
function sigin(){
    $('.siginInput').on('submit',function(e){
        e.preventDefault()
        console.log($('input[name="name"]').val());
        $.ajax({
            url:'http://127.0.0.1:4000/api/sigin',
            method:'get',
            data:{
                username:$('input[name="name"]').val(),
                password:$('input[name="password"]').val(),
            },
            success:function(data){
                console.log(data);
                if(data.status==0){
                     //登录成功渲染欢迎界面
                    alert('登录成功')
                    let str=`
                        <div>欢迎${data.data[0].username}</div>
                        <div class="siginout">退出</div>
                    `

                    $('#main').html(str)
                    //退出功能
                    siginout()
                }
            }
        })
    })
}
//欢迎界面和退出
function siginout(){
    $('.siginout').on('click',function(){
        let str=`
        <div class="sigin">登录</div>
        <div class="login">注册</div>
        <div class="input"></div>
        `
        $('#main').html(str)
    })
}
jump()
  1. 商品详细代码
  • 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">
    <title>Document</title>
    <link rel="stylesheet" href="../lib/font/iconfont.css">
   <link rel="stylesheet" href="../css/shoplist.css">
</head>
<body>
    <div class="back"><span class="iconfont icon-shangyiye"></span></div>
    <div class="render"></div>
    <script src="../js/jquery.js"></script>
    <script src="../js/shoplist.js"></script>
</body>
</html>
  • JS
function getShop(){
	//分割字符串拿到url中的id
    let url=location.href
    let idArr=url.substring((url.indexOf('?')+1))
    let id=idArr.split('=')[1]
    console.log(id);
    $.ajax({
        url:'http://127.0.0.1:4000/api//list/id',
        method:'get',
        data:{
            id:id
        },
        success:function(data){
            if(data.status==0){
                let list=data.data
                    let productStr=list.map(item=>{
                        return `
                            <div class="listSingle")>
                                <img src="${item.url}">
                                <div>
                                    <p>${item.product}</p>
                                    <p>¥${item.id}</p>
                                </div>
                            </div>
                            <div class="buyNow">立即购买</div>
                        `
                    }).join('')
                    $('.render').html(productStr)
            }
        }
    })
}
//回到主界面
$('.back').on('click',function(){
    location.href='index.html'
})
getShop()
  • CSS
.back span {
  margin-left: 10px;
  font-size: 40px;
}

.listSingle img {
  width: 700px;
}
.listSingle div p:nth-child(1) {
  font-size: 30px;
  color: black;
}
.listSingle div p:nth-child(2) {
  font-size: 20px;
  color: grey;
}

.buyNow {
  width: 100px;
  height: 50px;
  border-radius: 25px;
  background-color: #f4606c;
  text-align: center;
  line-height: 50px;
  cursor: pointer;
}

/*# sourceMappingURL=shoplist.css.map */

后端

  1. 主界面代码app.js
//导入express模块
const express=require('express')
const app=express()
const joi=require('joi')
//解决跨域问题
const cors=require('cors')
app.use(cors())
//解析表单数据
app.use(express.urlencoded({extended:false}))
//封装res.send()响应错误信息
app.use(function(req,res,next){
    //status值默认失败1
    res.cc=function(err,status=1){
        res.send({
            status,
            message:err instanceof Error ?err.message:err,
        })
    }
    next()
})
//导入用户路由模块
const apiRouter=require('./router/api')
app.use('/api',apiRouter)

//定义错误级别中间件
app.use((err,req,res,next)=>{
    //验证失败错误
    if(err instanceof joi.ValidationError) return res.cc(err)
    //身份认证出错
    if(err.name==='UnauthorizedError') return res.cc('身份认证出错')
    res.cc(err)
})


//启动监听端口
app.listen(4000,function(){
    console.log('express listen running at 4000');
})
  1. 路由代码router/api.js
const express=require('express')
const router=express.Router()
//引入路由处理模块
const api_handler=require('../router_handler/api_handler')
//验证模块 模式验证
const expressJoi=require('@escook/express-joi')
const {login_schema,sigin_schema,listId_schema}=require('../schema/user')
//注册接口
router.get('/login',expressJoi(login_schema),api_handler.login)
//登录接口
router.get('/sigin',expressJoi(sigin_schema),api_handler.sigin)
//商品列表接口
router.get('/list',api_handler.list)
//查询商品接口
router.get('/list/id',expressJoi(listId_schema),api_handler.listId)
//搜索框搜索
router.get('/search',api_handler.search)
//轮播图
router.get('/banner',api_handler.banner) 
module.exports=router
  1. 路由处理函数router_handler
const db=require('../db/index')
//注册处理函数
exports.login=function(req,res){
        let userinfo=req.query
        
        let sql='SELECT * FROM ev_users WHERE username=?'
        db.query(sql,userinfo.username,function(err,results){
            if(err) return res.cc(err)
            if(results.length>0) return res.cc('用户名已存在')
            //注册新用户
            let sql='INSERT INTO ev_users SET ?'
            db.query(sql,{username:userinfo.username,password:userinfo.password},function(err,results){
                if(err) return res.cc(err)
                if(results.affectedRows!==1) return res.cc('出错了')
                res.cc('注册成功',0)
            })
        })
}
//登录处理函数api_handler.sigin
exports.sigin=function(req,res){
    let userinfo=req.query
    let sql='SELECT * FROM ev_users WHERE username=? AND password=?';
    db.query(sql,[userinfo.username,userinfo.password],(err,results)=>{
        if(err) return res.cc(err)
        if(results.length!==1) return res.cc('出错') 
        res.send({
            status:0,
            message:'OK',
            data:results
        })
    })
  
}
//商品列表
exports.list=function(req,res){
    let sql='SELECT * FROM shoplist '
    db.query(sql,(err,results)=>{
        if(err) return res.cc(err)
        if(results.affectedRows==0) return res.cc('数据为空')
        res.send({
            status:0,
            message:'ok',
            data:results
        })
    })
}
//查询商品
exports.listId=(req,res)=>{
    let idinfo=req.query
    let sql='SELECT * FROM shoplist WHERE id=?'
    db.query(sql,idinfo.id,(err,results)=>{
        if(err) return res.cc(err)
        if(results.length!==1) return res.cc('查询出错')
        res.send({
            status:0,
            message:'ok',
            data:results
        })
    })
}
//轮播图
exports.banner=(req,res)=>{
    let sql='SELECT * FROM shoplist LIMIT 0,3'
    db.query(sql,(err,results)=>{
        if(err) return res.cc(err)
        
        res.send({
            status:0,
            message:'ok',
            data:results
        })
    })
}

//搜索框搜索
exports.search=(req,res)=>{
    let search=req.query
    let sql='SELECT * FROM shoplist WHERE id=?'
    if(search.info=='商品1'){
        db.query(sql,1,(err,results)=>{
            if(err) return res.cc(err)
            if(results.length!==1) return res.cc('查询出错')
            res.send({
                status:0,
                message:'ok',
                data:results
            })
        })
    }else  if(search.info=='商品2'){
        db.query(sql,2,(err,results)=>{
            if(err) return res.cc(err)
            if(results.length!==1) return res.cc('查询出错')
            res.send({
                status:0,
                message:'ok',
                data:results
            })
        })
    }else  if(search.info=='商品3'){
        db.query(sql,3,(err,results)=>{
            if(err) return res.cc(err)
            if(results.length!==1) return res.cc('查询出错')
            res.send({
                status:0,
                message:'ok',
                data:results
            })
        })
    }else  if(search.info=='商品4'){
        db.query(sql,4,(err,results)=>{
            if(err) return res.cc(err)
            if(results.length!==1) return res.cc('查询出错')
            res.send({
                status:0,
                message:'ok',
                data:results
            })
        })
    }else  if(search.info=='商品5'){
        db.query(sql,5,(err,results)=>{
            if(err) return res.cc(err)
            if(results.length!==1) return res.cc('查询出错')
            res.send({
                status:0,
                message:'ok',
                data:results
            })
        })
    }else{
        res.cc('未找到商品',1)
    }
}
  1. 连接数据库代码db/index.js
const mysql=require('mysql')
const db=mysql.createPool({
    host:'127.0.0.1',
    user:'root',
    password:'123456',
    database:'test2',
})
module.exports=db
  1. 验证模块schema/user.js
const joi =require('joi')
const username=joi.string().alphanum().min(4).required()
const password=joi.string().alphanum().min(4).required()
const id=joi.number().required()
exports.login_schema={
    query:{
        username,
        password
    }
}
exports.sigin_schema={
    query:{
        username,
        password
    }
}
exports.listId_schema={
    query:{
        id
    }
}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>