Web前端105天-day50~day60-项目实战

阶段项目实战

目录

前言

一、首页

二、视频菜谱页

三、笔记页

四、商城页

五、直播页

六、搜索页

七、登录页

八、注册页

九、个人信息页

总结


前言

美食广场项目开始

项目源码链接: 美食广场  

提取码: 7f32


一、首页

 

  •  JS代码(css与html代码过多,暂不展示)
$(function(){
  var url = 'https://serverms.xin88.top/index'
  $.get(url,data => {
    console.log(data);
    $('.topvideo>div>ul').html(
      data.hot_video.map(value => {
        const {mp4,pic,vname} = value
        return `
        <li><video src="assets/video/${mp4}"  preload="none" poster="assets/img/${pic}"></video>
        <i></i>
        <span>${vname}</span>
        </li>
        `
      })
    )
    $('.main>.hot-search>ul').html(
      data.today_hot.map(value => {
        const {name,emphasize} = value
        return `
        <li class="${emphasize?'em':''}"><a href="?p=search&wd=${name}">${name}</a></li>
        `
      })
    )
    $('.main>.today-meal>.menu>ul').html(
      data.today_meal.map((value,index) => {
        const {cate_name,contents} = value
        // 用append累加到轮播组件中
        $('.swiper-wrapper').append(
          contents.map(value => {
            const {pic,desc,title} = value
            return `
            <div class="swiper-slide">
            <div class="meal-item">
              <img src="assets/img/food/${pic}" alt="">
              <strong>${title}</strong>
              <span>${desc}</span>
            </div>
          </div>
            `
          })
        )
        return `
        <li class="${index == 0 ? 'active':''}">${cate_name}</li>
        `
      })
    )

    // index-items
    $('.index-items').html(
      data.index_items.map(value => {
        const {title,items} = value
        // 将items装换成HTML
        const els = items.map(value => {
          const {author,desc,pic,title} = value
          return `
          <li>
            <div>
              <img src="assets/img/food/${pic}" alt="">
              <span>${author}</span>
            </div>
            <strong class="line-1">${title}</strong>
            <div>
              <i></i>
              <span class="line-1">${desc}</span>
            </div>
          </li>
          `
        })
        // join():把数组中的元素 拼接为字符串,参数为 拼时的间隔内容
        return`<li>
        <h2>${title}</h2>
        <ul>${els.join('')}</ul>
        </li>`
      })
    )
  })
  $('.topvideo>div>ul').on('click','li',function(){
    // 判断当前激活项,是否已经具有 active样式
    if($(this).hasClass('active')){
      $(this).removeClass('active')
      $(this).siblings().removeClass('noactive')
      $(this).children('video').trigger('pause')
    }else{
      // 当前li 下的video 标签
      $(this).siblings().children().trigger('pause')

      $(this).children('video').trigger('play')

      // 未激活 宽度变小
      $(this).addClass('active').siblings().removeClass('active')
      $(this).removeClass('noactive').siblings().addClass('noactive')
    }


  })

  $('.main>.today-meal>.menu>ul').on('click','li',function(){
    $(this).addClass('active').siblings().removeClass('active')
    mySwiper.slideTo($(this).index() * 3,1000,false)
  })

  // 初始化swiper
  var mySwiper = new Swiper('.swiper',{
    slidesPerView : 3,
    slidesPerGroup : 3,//每组三个
    spaceBetween:10,
    autoplay: {
      delay: 3000,
      stopOnLastSlide: false,
      disableOnInteraction: true,
    },
    on:{
      slideChange:function(){
        // 当前激活的滚动项 序号
        $('.main>.today-meal>.menu>ul>li').eq(this.activeIndex / 3).click()
      }
    },
  })
  setInterval(() => {
    $('#banner').toggleClass('active')
  }, 4000);
})

二、视频菜谱页

  •  JS代码
// 两个目的
// 1.确保代码在DOM元素即加载完毕后在执行
// 2.防止变量全局污染
jQuery || require('jquery')
$(function(){
  // JQuery || require('jquery')
  // 封装:因为分页操作 需要多次发送请求,然后生成新的UI;这段代码需要重复使用
  function getData(pno){
    var url = 'https://serverms.xin88.top/video?page=' + pno
    // 个人剧请求到的数据,生成列表的内容
    $.get(url,data => {
      // 修改滚动条的位置到顶部
      $(window).scrollTop(0)

      $('.list').html(
      data.data.map(value => {
        const {pic,views,duration,title} = value
        return `<li>
        <div>
          <img src="assets/img/video/${pic}" alt="">
          <div>
            <span>${views}次播放</span>
            <span>${duration}</span>
          </div>
        </div>
          <span>${title}</span>
        </li>
        `
      })
      )
      // 清空所有子元素
      // 根据请求到的数据,生成分页内容
      $('.pager>ul').empty()
      const {page,pageCount} = data
      // 场景:当前页18,则左侧起始页16 关系:-2
      let start = Math.max(page -2,1)
      let end = start + 4
      if(end >= pageCount){
        start = Math.max(pageCount - 4,1)
        end = pageCount
      }
      for(let i = start;i <= end;i++){
      $('.pager>ul').append(`<li class="${i == data.page ? 'active' : ''}">${i}</li>`)
      }
      // console.log(data)

      // 隐藏上一页
      const $prevBtn = $('.pager>button').first()
      data.page == 1 ? $prevBtn.hide() : $prevBtn.show()
      // 隐藏下一页
      const $nextBtn = $('.pager>button').last()
      data.page == data.pageCount ? $nextBtn.hide() : $nextBtn.show()
      })
    }
  getData(1)//初始,请求第一页
  // 为请求后产生的 动态新增子元素,添加事件--用委托
  $('.pager>ul').on('click','li',function(){
    const pno = $(this).text()//读取标签内容,即页数
    getData(pno)
  })
  // 下一页
  $('.pager>button').last().on('click',function(){
    const pno = $('.pager>ul>li.active').next().text()
    getData(pno)

  })
  // 上一页
  $('.pager>button').first().on('click',function(){
    const pno = $('.pager>ul>li.active').prev().text()
    getData(pno)
  })
})

三、笔记页

  •  JS代码
$(function(){

  let li_arr = [] //存放已经完成布局的li

  // 获取某个元素的 底部 距离 顶部的偏移量
  function offset_bottom(el){
    // 元素的高 + 元素样式top偏移量 
    // top是css的值,读取的是'..px' 需要转换成数字 才能相加
    return $(el).height() + parseInt($(el).css('top'))
  }


  let = nowPage = 1
  let lock = false
  function getData(pno){
    var url = 'https://serverms.xin88.top/note?page=' + pno
    lock = true

    // 瀑布流思想:用定位方式,根据每个元素的实际宽和高,计算其摆放位置
    // 而:图片是延时加载的,如果没有指定宽和高,那么只能等图片加载完毕
    // 必须由服务器接口 提供图片的精准宽高 才能顺利完成 瀑布流
    // 通过JS实现布局相关的
    const space = 10//元素
    const li_w = (1000 - 2 * space) / 3

    // 等比例公式 img_h / img_w = height / width
    //  li_h = height / width * img_w

    $.get(url,data => {
      // console.log(data);
      lock =false
      nowPage = data.page
      if(nowPage == data.pageCount){
        $('.loadmore').text(`:-) 没有更多啦`).addClass('nomore')
      }
      $('.list').append(
        data.data.map(value => {
          const {cover,title,head_icon,name,favorite,height,width} = value

          const img_h = li_w * height / width

          return `
          <li style="width:${li_w}px;">
          <img style="height:${img_h}px;" src="assets/img/note/${cover}" alt="">
          <p class="line-2">${title}</p>
          <div>
            <div>
              <img src="assets/img/note/${head_icon}" alt="">
              <span>${name}</span>
            </div>
            <span>${favorite}</span>
          </div>
        </li>
          `
        })
      )

      // 每次布局清空 li_arr
      li_arr = []
      // 遍历添加的每个li元素,挨个进行布局
      // each:是JQ提供的,遍历查询到的元素
      $('.list>li').each((index,li) => {
        // 参数1:序号  参数2:元素
        // console.log(index,li);
        // 前四个 并排摆放在最开头
        if(index < 3){
          $(li).css({top:0,left: index * (li_w + space)})
          // 把完成布局的li,添加到数组
          li_arr.push(li)
        }else{
          // 假设 li_arr中,序号0的最小
          var min_li = li_arr[0]
          li_arr.forEach(li => {
            // 如果 遍历到的li的 底部偏移量 比 之前设定的最小的 还要小
            if(offset_bottom(li) < offset_bottom(min_li)){
              // 最小值修改
              min_li = li
            }
          })
          // 新元素要摆放在 最小元素的 正下方
          $(li).css({
            left:$(min_li).css('left'),
            top:offset_bottom(min_li) + space
          })

          // 调整存放已布局的元素数组,把最小的从里面删除,把新加入的添加进入
          li_arr.push(li)
          // splice(序号,个数):从数组中 指定序号位置开始 删除 指定元素个数
          const i = li_arr.indexOf(min_li)//获取最小元素的序号
          li_arr.splice(i,1)
        }
      })

      // 所有的li都是绝对定位 脱离文档流 导致其父元素的高度坍塌
      var max_li = li_arr[0]
      li_arr.forEach(li => {
        if(offset_bottom(li) > offset_bottom(max_li)) max_li = li
      })
      $('.list').css('height',offset_bottom(max_li))
    })
  }
  getData(1)
  $(window).on('scroll',function(){
    var win_h = $(window).height()
    var st = $(window).scrollTop()
    var top = $('.loadmore').offset().top
    if(st > top - win_h && !lock && !$('.loadmore').hasClass('nomore') ){
      getData(nowPage + 1 )
    }
  })
})

四、商城页

  •  JS代码
$(function () {
  let nowPage = 1
  let lock = false //锁: 初始状态-解锁

  function getData(pno) {
    var url = 'https://serverms.xin88.top/mall?page=' + pno

    lock = true // 请求前: 锁定
    $.get(url, data => {
      lock = false // 请求完: 解锁

      console.log(data)
      //更新当前页数
      nowPage = data.page

      if (nowPage == data.pageCount) {
        $('.loadmore').text("没有更多了").addClass('nomore')
      }

      // 列表内容
      $('.list').append(
        data.data.map(value => {
          const { name, pic, price, sale_count } = value

          return `<li>
          <img src="assets/img/mall/${pic}" alt="">
          <div>
            <p class="line-2">${name}</p>
            <div>
              <strong>¥${price}</strong>
              <span>月售${sale_count}</span>
            </div>
          </div>
        </li>`
        })
      )
    })
  }

  getData(1) //初始化

  $(window).on('scroll', function () {
    // 滚动量
    var st = $(window).scrollTop()
    // 窗口高
    var win_h = $(window).height()
    // 加载更多 的偏移量
    var top = $('.loadmore').offset().top

    // 超过滚动极限值 并且 未锁定状态 并且 不是没有更多时
    var nomore = $('.loadmore').hasClass('nomore')

    if (st >= top - win_h && !lock && !nomore) {
      console.log('加载..');

      getData(nowPage + 1)
    }
  })
})

五、直播页

  •  JS代码
$(function () {
  let nowPage = 1 //存放当前页
  // 应该记录 当前请求的加载状态, 如果请求中.. 就不应该请求下一次
  // 防止请求被多次重复调用, 请求前锁门
  let lock = false // 锁: 起始状态 未锁定

  function getData(pno) {
    var url = `https://douyu.xin88.top/api/room/list?page=${pno}&type=ms`

    lock = true // 请求前: 锁门
    $.get(url, data => {
      lock = false // 请求完: 解锁

      console.log(data)
      //请求完毕后, 更新当前页
      nowPage = data.data.nowPage
      // 如果最后一页: 则显示 没有更多数据了
      if (nowPage == data.data.pageCount) {
        // nomore: 并非为了加样式, 而是 作为一个标识, 代表没有更多数据
        // 在请求前: 判断 如果存在 nomore 样式, 就不要触发请求
        $('.loadmore').text('没有更多了').addClass('nomore')
      }

      // html: 覆盖原有内容
      // 新增: append
      $('.list').append(
        data.data.list.map(value => {
          const { hn, nickname, roomSrc, roomName } = value

          return `<li>
        <div>
          <img src="${roomSrc}" alt="">
          <span class="hn">${hn}</span>
          <span class="nickname">${nickname}</span>
        </div>
        <span class="line-1">${roomName}</span>
      </li>`
        })
      )
    })
  }

  getData(1)
  // 由于1页数据数量较少, 不足以充满整个页面
  // 初始化时, 请求两次
  getData(2)

  // 触 加载更多 监听
  $(window).on('scroll', function () {
    // 窗口高
    var win_h = $(window).height()
    // 滚动距离
    var st = $(window).scrollTop()
    // 加载更多 元素距离顶部的偏移量
    var top = $('.loadmore').offset().top

    // 滚动距离 大于 极限值(出现加载更多)

    // 额外条件: 非锁定状态
    // 额外条件: 不应该存在nomore样式
    if (st > top - win_h && !lock && !$('.loadmore').hasClass('nomore')) {
      console.log('请求更多数据...')
      // 当前页+1
      getData(nowPage + 1)
    }
  })
})

六、搜索页

  •  JS代码
$(function () {
  // 给 排序方式 加点击事件
  $(".area-sort>li").on('click', function () {
    $(this).addClass('active').siblings().removeClass('active')

    getData(1)
  })

  function getData(pno) {
    // 自动回到顶部
    $(window).scrollTop(0)

    // 请求类型: 与当前激活项的序号一致
    var type = $('.area-sort>li.active').index()

    // 如果路径中, wd 没有值, 则用 参数2 作为默认值
    var wd = $.s('wd', '')

    var url = `https://serverms.xin88.top/mall/search?type=${type}&kw=${wd}&page=${pno}`

    console.log(url)

    $.get(url, data => {
      console.log(data)

      $('.list').html(
        data.data.map(value => {
          const { name, pic, price, sale_count } = value

          return `<li>
          <img src="assets/img/mall/${pic}" alt="">
          <div>
            <h3>${name}</h3>
            <strong>¥${price}</strong>
            <span>销量: ${sale_count}</span>
          </div>
        </li>`
        })
      )

      // 修改 上一页和 下一页的 不可用状态
      var { page, pageCount } = data

      var $prev_btn = $('.pager>button').first()
      // 页数1, 则上一页不可用;  否则可用
      page == 1 ? $prev_btn.addClass('disabled') : $prev_btn.removeClass('disabled')

      // 下一页
      var $next_btn = $('.pager>button').last()
      // 动态获取方法名, 然后通过方括号语法 来找到此方法 执行
      var m = (page == pageCount ? 'addClass' : 'removeClass')
      $next_btn[m]('disabled')


      // 生成分页
      let start = page - 2
      if (start < 1) start = 1

      let end = start + 4
      if (end > pageCount) end = pageCount

      start = end - 4
      if (start < 1) start = 1

      $('.pager>ul').empty() //清空旧的

      for (let i = start; i <= end; i++) {
        $('.pager>ul').append(`<li class="${i == page ? 'active' : ''}">${i}</li>`)
      }

    })
  }

  getData(1)

  $('.pager>ul').on('click', 'li', function () {
    var pno = $(this).text()

    getData(pno)
  })

  // 下一页:
  $('.pager>button').last().on('click', function () {
    var pno = $('.pager>ul>li.active').next().text()

    getData(pno)
  })

  $('.pager>button').first().on('click', function () {
    var pno = $('.pager>ul>li.active').prev().text()

    getData(pno)
  })
})

七、登录页

  •  JS代码
// login.js
$(function () {
  var url = 'https://serverms.xin88.top/users/login'

  $('.area-login button').on('click', function () {
    var phone = $('.area-login input').eq(0).val()
    var pwd = $('.area-login input').eq(1).val()

    $.post(url, { phone, pwd }, data => {
      console.log(data);
      if (data.code == 200) {
        alert("登录成功! 即将跳转到首页")
        location.replace('?p=home')
        // 登录信息存储到浏览器
        // 根据是否勾选 下次自动登录, 来决定存储的方式  长期还是短期
        var checked = $('.area-login :checkbox').prop('checked')
        if (checked) { // 真: 长期
          localStorage.setItem('user', JSON.stringify(data.data))
        } else { // 假: 短期
          sessionStorage.setItem('user', JSON.stringify(data.data))
        }
      } else {
        alert(data.msg)
      }
    })
  })
})

八、注册页

  •  JS代码
$(function () {
  // 手机号: 失去焦点后检测
  $('.area-reg>.item:eq(0)>input')
    .on('blur', function () {
      var phone = $(this).val() //获取输入框的值

      // 如果没有值, 则不做任何事情
      if (phone == '') return

      // 手机号正则
      if (!/^1[3-9]\d{9}$/.test(phone)) {
        $(this).addClass('err').next().show()
      } else {
        // POST : 发送手机号给服务器 查验是否已注册
        var url = 'https://serverms.xin88.top/users/checkPhone'

        // POST与GET不同, 参数需要单独传递
        // 参数: 往往会故意制造巧合 - 参数名 和 值的变量名一样
        $.post(url, { phone }, data => {
          console.log(data);
          // 200: 手机号不存在, 说明可以注册
          if (data.code == 200) {
            // 输入框下方 所有兄弟元素中, 带有 .ok 的
            $(this).nextAll('.ok').show()
          }
          // 202: 手机号已注册
          if (data.code == 202) {
            $(this).addClass('err').next().next().show()
          }
        })
      }
    })
    .on('focus', function () {
      // nextAll: 下方所有兄弟元素
      $(this).removeClass('err').nextAll().hide()
    })

  // 密码验证

  $('.area-reg>.item:eq(1)>input')
    .on('focus', function () {
      $(this).removeClass('err').nextAll().hide()
    })
    .on('blur', function () {
      var pwd = $(this).val()

      if (pwd == '') return

      if (pwd.length < 6 || pwd.length > 12) {
        $(this).addClass('err').next().show()
      } else {
        $(this).next().next().show()
      }
    })
    // 预防: 如果有变更, 则再次验证
    .on('change', function () {
      $('.area-reg>.item:eq(2)>input').focus().blur()
    })

  // 再次验证
  $('.area-reg>.item:eq(2)>input')
    .on('focus', function () {
      $(this).removeClass('err').nextAll().hide()
    })
    .on('blur', function () {
      var re_pwd = $(this).val()
      var pwd = $('.area-reg>.item:eq(1)>input').val()

      if (re_pwd == '') return

      if (re_pwd == pwd) {
        $(this).next().next().show()
      } else {
        $(this).next().show()
      }

    })

  // 注册
  $('.area-reg>button').on('click', function () {
    // :checkbox  选中 type=checkbox 的标签
    // prop: 获取标签的属性
    var checked = $('.area-reg :checkbox').prop('checked')
    console.log('checked:', checked);

    //如果非勾选, 用抖动动画提示
    if (checked) {
      // 把有错误的的输入框, 即代表 class='err' 的, 抖动提示
      $('.area-reg input.err')
        .addClass('animate__shakeX animate__animated')
      // 如果有输入错误的输入框, 则只抖动提示, 不做后续处理
      if ($('.area-reg input.err').length > 0) return

      // 如果 3个 span.ok 的元素都是可见的, 就说明都是正确的
      // :visible  限定:可见的
      const $oks = $('.area-reg span.ok:visible')
      console.log($oks)
      if ($oks.length == 3) {
        // 注册
        var url = 'https://serverms.xin88.top/users/register'
        var phone = $('.area-reg input').eq(0).val()
        var pwd = $('.area-reg input').eq(1).val()

        $.post(url, { phone, pwd }, data => {
          console.log(data);
          if (data.code == 200) { //成功
            alert(`恭喜您注册成功, 成为第${data.id}位注册用户, 即将跳转到登录页面`)

            location.replace('?p=login')
          } else { //不成功
            alert(data.msg)
          }
        })

      } else {
        // 报错
        alert("请确保所有信息填写正确, 再进行注册")
      }

    } else {
      $(this).prev().addClass('animate__heartBeat')
    }
  })

  // 动画完毕时, 要移除样式, 方便下次动画的执行
  $('.area-reg>.agree').on('animationend', function () {
    $(this).removeClass('animate__heartBeat')
  })

  $('.area-reg input').on('animationend', function () {
    $(this).removeClass('animate__shakeX')
  })
})


九、个人信息页

  •  JS代码
$(function () {
  $('.sidemenu li').on('click', function () {
    $(this).addClass('active').siblings().removeClass('active')

    var i = $(this).index() //点击项的序号

    // 找到对应序号的内容
    $('.content>li').eq(i).show().siblings().hide()
  })

  // 初始化时, 显示个人信息
  $('.content>li').eq(0).show()

  //退出
  $('.content>li').last().children('button').on('click', function () {
    sessionStorage.removeItem('user')
    localStorage.removeItem('user')

    location.replace('?p=home')
  })


  // 展示用户信息
  var user = sessionStorage.getItem('user') || localStorage.getItem('user')
  user = JSON.parse(user)

  if (user.avatar) {
    $('.content>li:nth-child(2)>img').prop('src', user.avatar)
  }

  $('.phone').text(user.phone)
  // 时间戳: 转年月日
  // 第三方库: moment.js 专门处理时间
  // http://momentjs.cn/
  $('.created').text(moment(user.created).format('YYYY-MM-DD HH:mm:ss'))

  // 所有头像的接口: 
  var url_head = 'https://serverms.xin88.top/users/head_photos'

  $.get(url_head, data => {
    console.log(data)
    const baseURL = data.baseURL

    $('.head-photos').html(
      data.hero.map(value => {
        const { alias, selectAudio } = value
        const head_photo = baseURL.replace('${alias}', alias)

        return `<li>
          <img data-au="${selectAudio}" src="${head_photo}" alt="">
        </li>`
      })
    )
  })

  // 全局唯一的播放器
  var audio = new Audio()

  // 为动态新增数据绑定事件 -- 委托
  $('.head-photos').on('click', 'img', function () {
    var au = $(this).data('au')

    audio.src = au
    audio.play()

    // 替换图片
    // 设置图片地址 为 当前点击的图片地址
    $('.content>li:nth-child(2)>img').prop('src', $(this).prop('src'))
  })

  // 点击确定, 发请求更新头像给用户
  $('.content>li:nth-child(2)>button').on('click', function () {
    var url = 'https://serverms.xin88.top/users/head_photo'

    var id = user.id
    var alias = $('.content>li:nth-child(2)>img').prop('src')
    console.log({ id, alias });

    $.post(url, { id, alias }, data => {
      console.log(data)
      if (data.code == 200) {
        alert("头像更新成功")
        $('.user>img').prop('src', alias)
        // 同时更新本地存储的用户信息中的头像
        user.avatar = alias
        // 判断是临时存储 还是 长期存储, 更新对应数据
        if (sessionStorage.getItem('user')) {
          sessionStorage.setItem('user', JSON.stringify(user))
        }
        if (localStorage.getItem('user')) {
          localStorage.setItem('user', JSON.stringify(user))
        }
      } else {
        alert(data.msg)
      }
    })
  })
})


总结

10天项目实战

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值