Code Monkey home page Code Monkey logo

vue-elm-seller's Introduction

vue-elm-seller

线上预览

http://123.57.204.48:5000/

qcode

安装依赖

npm install

开启一个具有热重载功能的开发服务器

npm run serve

构建项目

npm run build

主要功能

  • 商品菜单联动导航展示
  • 添加移除商品到购物车
  • 购物车商品列表
  • 清空购物车
  • 确认支付提示
  • 查看商品详情
  • 商品评价
  • 店铺综合评论
  • 商家信息展示
  • 商家收藏
  • 商家详情弹窗
  • 商品滚动列表菜单标题吸顶
  • 添加商品到购物车时的动画效果
  • 项目整体优化

技术栈

vue better-scroll lib-flexible

axios express node javascript

less css es6/7

webpack vue-cli

项目目录

├── public
│ ├── data  // 服务端接口数据
│ │ ├── goods.json
│ │ ├── ratings.json
│ │ └── seller.json
│ ├── favicon.ico
│ └── index.html
├── server  // 服务端
│ ├── app.js  // 启动服务器入口
│ ├── package-lock.json
│ └── package.json
├── src   // 源代码
│ ├── assets  // 静态资源
│ │ ├── font.css
│ │ ├── logo.png
│ │ └── reset.css
│ ├── components   // 项目组件
│ │ ├── alert  // 提示弹窗
│ │ │ ├── main.js
│ │ │ └── main.vue
│ │ ├── confirm  // 带有取消和确认按钮的弹窗
│ │ │ ├── index.vue
│ │ │ └── main.js
│ │ ├── food-picker  // 食品选择器
│ │ │ └── index.vue
│ │ ├── header  // 页面头部
│ │ │ └── index.vue
│ │ ├── header-detail  // 页面头部详情弹窗
│ │ │ ├── close.svg
│ │ │ └── index.vue
│ │ ├── icon  // 通用图标组件
│ │ │ ├── index.vue
│ │ ├── rating  // 商品评分组件
│ │ │ └── index.vue
│ │ ├── scroll  // 通用滚动组件
│ │ │ └── index.vue
│ │ ├── shopping-cart  // 购物车
│ │ │ ├── index.vue
│ │ │ └── shopping_cart.svg
│ │ ├── star-score  //  评分星级组件
│ │ │ └── index.vue
│ │ ├── svg-icon   //  svg图标
│ │ │ └── index.vue
│ │ └── tab-bar  //  可切换的导航栏组件
│ │ └── index.vue
│ ├── helper  // 帮助工具
│ │ └── directive.js
│ ├── views  // 页面
│ │ ├── food-detail  // 商品详情
│ │ │ └── index.vue
│ │ ├── goods  // 商品页
│ │ │ └── index.vue
│ │ ├── ratings  // 评价页
│ │ │ └── index.vue
│ │ └── shop  // 商家店铺
│ │ └── index.vue
│ ├── App.vue  // 应用根组件
│ ├── main.js  // 应用入口
│ └── request.js  // http 请求示例
├── README.md
├── babel.config.js
├── dir.txt
├── package-lock.json
├── package.json
├── postcss.config.js
└── vue.config.js   // 项目配置

项目截图

项目总结

支持

本项目是受 https://github.com/ustbhuangyi/vue-sell此应用界面功能的启发并由个人独立完成(源代码不同)。项目技术实现上都是自己精心思考研究后开发出来的,与此同时也练习了一次vue项目的开发流程,个人完成项目开源后也有很大的收获。希望对看到此项目的你在学习上能有所帮助,喜欢本项目请右上角star一下:grin:,谢谢!我会持续开源更多好玩有趣的项目出来。

vue-elm-seller's People

Contributors

konglingwen94 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

vue-elm-seller's Issues

项目总结

概述

本篇文章从应用功能技术实现一些方面剖析此项目的开发过程以及当中采到的坑,特此写下一篇总结文章。

项目功能截图

项目技术栈

  1. 前端

    • vue开发项目核心框架
    • axios HTTP请求模块
    • lib-flexible 移动端屏幕适配方案
    • better-scroll 仿IOS效果的移动端滚动库
    • normalize.css 第三方css样式初始化模块
    • es 6/7 下一代javascript语法
  2. 后端

    • express 搭建服务端应用核心框架
  3. 开发

    • vue-cli 项目初始化脚手架
    • vue-devtools 项目开发环境调试工具
    • vscode chrome git macbookpro
  4. 部署

应用功能

  • 商品页

    • 商品分类导航和商品列表的联动效果
    • 点击商品分类菜单展示对应商品列表信息
    • 添加/删除商品到购物车
    • 点击商品进入到详情页面
    • 商品添加到购物车动画效果
    • 页面滚动到对应商品类别时的标题吸顶效果
  • 评论页

    • 综合评论信息渲染
    • 切换评论筛选项按钮展示对应的信息
    • 选择展示是否有内容的评论
  • 商家页

    • 商家店铺信息展示
    • 收藏店铺
    • 商家实景图片具有bounce效果的滑动显示
  • 应用头部

    • 点击展示详情
    • 公告信息动态滚动显示
  • 购物车

    • 根据商品个数显示不同的状态
    • 购物车商品列表
    • 支付弹窗
    • 清空购物车
    • 增加/删除商品
  • 应用局部优化

bounce效果是指在应用中页面位置滚动到一个端点继续滑动时出现反弹的效果,常见场景是IOS系统应用滑动效果

功能难点

商品导航和内容的左右联动效果

效果演示

完整的组件代码点https://github.com/konglingwen94/vue-elm-sell/blob/master/src/views/goods/index.vue

思路

由于商品导航和内容是两个独立的滚动容器,当滚动到一个目标内容块时怎么才能激活它所关联的导航项呢?我们知道导航项列表和内容列表在排列顺序上是一致的,如果能计算出内容滚动位置处在对应区间块的索引,也就得到了导航列表应该激活的目标索引,然后就可以用Vue数据驱动视图的**去实现这一切。

容器的左右联动效果是指容器滚动到目标内容时激活其关联的导航菜单项并滚动到可视区域。

逻辑实现

找到要激活的目标导航项索引的第一步需要把商品内容的各个类别块在容器内的纵坐标位置存储起来(给之后找到激活的目标索引提供比较对象),由于列表内容时动态渲染的,所以这里需要等所有数据已经渲染完成后才能操作,下面直接看代码演示吧!

template部分

<template>
    /* 这里只显示部分代码*/
     <ul class="foods-list">
          <li ref="foodsGroup" class="foods-group" v-for="(item,index) in data" :key="index">
            <dl class="foods-group-wrapper">
              <dt :class="{fixed:currentIndex===index}" class="foods-group-name">{{item.name}}</dt>
              <dd
                class="foods-group-item"
                v-for="(food ,key) in item.foods"
                :key="key"
              >
                 {{food.name}}
              </dd>
            </dl>
          </li>
        </ul>
</template>

script部分

 
export default {
    data(){
      return {
          currentIndex: 0,//导航项激活的索引
          currentFood: {},
          data:[],
          sectionHeight: [0],//第一个高度块坐标`y`值为`0`
          // 渲染完成后的值为 `[0,1281,1459,1612,2000,2270,2565,2952,3574,4436]`
      }  
    },
    created() {
        request
          .get("/goods")
          .then(response => {
            this.data = response;
          })
          .then(() => {
            setTimeout(() => {
              const sections = this.$refs.foodsGroup;
    
              sections.reduce((prevTotal, current) => {
                const sectionHeight = prevTotal + current.clientHeight;
                this.sectionHeight.push(sectionHeight);
    
                return sectionHeight;
              }, 0);
            });
        });
  }
}

有了各个商品块的y坐标,下一步就需要注册容器元素的滚动事件了,在回调函数里通过找到实时滚动位置disanceY处在sectionHeight数组中两个相邻元素之间的位置从而就得到了待激活导航索引currentIndex的值,具体代码实现如下

<template>
    <div>
        <!--导航菜单-->
         <scroll class="menu">
            <ul class="menu-list">
              <li
                @tap="selectMenu(index)"
                class="menu-item"
                :class="{selected:currentIndex===index}"
                v-for="(item,index) in data"
                :key="index"
              >
               <span>{{ item.name}}</span>
              </li>
            </ul>
          </scroll>
          
        <!--商品内容-->
        <scroll ref="foodsScroll" @scroll="onFoodScroll" class="foods">
        <!--这里省略商品内容模板的代码-->
        </scroll>
    
    </div>
</template>
export default {

    // 这里省略其他代码
    
    methods:{
        onFoodScroll({ x, y }) {
          const distanceY = Math.abs(Math.round(y));
          for (let index = 0; index < this.sectionHeight.length; index++) {
            if (
              distanceY >= this.sectionHeight[index] &&
              distanceY < this.sectionHeight[index + 1]
            ) {
              this.currentIndex = index;
            }
          }
        }
    }
}

完整的组件代码点https://github.com/konglingwen94/vue-elm-sell/blob/master/src/views/goods/index.vue。由于左右两侧的布局容器都是基于better-scroll实现的页面滚动,所以这里需要侦听better-scroll提供的scroll事件而不是浏览器原生的滚动事件。查看better-scrollscroll事件API这里

添加/删除 商品到购物车

效果截图

完整代码https://github.com/konglingwen94/vue-elm-sell/blob/master/src/components/food-picker/index.vue

思路

添加商品到购物车是一个多场景的功能,由于这里的购物车功能是一个多页面联动的效果,购物车商品数量的实时更改也需要同步到商品内容页和商品详情页。从功能映射到javascript语言数据结构层面的话,不难想到对象引用传递的特点可以作为实现此功能的底层架构思路,那就让我们去实现它吧。

实现

为了统计商品的数量。首先需要给每一个商品信息对象添加一个默认值为0count属性,添加后的对象长这样

{
  "count": 0, // 此变量用来存储添加到购物车的数量
  "name": "皮蛋瘦肉粥",
  "price": 10,
  "oldPrice": "",
  "description": "咸粥",
  "sellCount": 229,
  "rating": 100,
  "info": "一碗皮蛋瘦肉粥,总是我到粥店时的不二之选。香浓软滑,饱腹暖心,皮蛋的Q弹与瘦肉的滑嫩伴着粥香溢于满口,让人喝这样的一碗粥也觉得心满意足",
  "ratings": [
    {
      "username": "3******b",
      "rateTime": 1469261964000,
      "rateType": 1,
      "text": "",
      "avatar": "http://static.galileo.xiaojukeji.com/static/tms/default_header.png"
    }
  ],
  "icon": "http://fuss10.elemecdn.com/c/cd/c12745ed8a5171e13b427dbc39401jpeg.jpeg?imageView2/1/w/114/h/114",
  "image": "http://fuss10.elemecdn.com/c/cd/c12745ed8a5171e13b427dbc39401jpeg.jpeg?imageView2/1/w/750/h/750"
}

由于每一个商品项都有一个添加到购物车的数量选择器功能,这样我们直接给商品数量选择器组件设计一个名为foodInfo的对象类型props,这样在增加/减少商品数量的时候直接操作foodInfocount属性来实现同步数据的效果。

/components/food-picker.vue 组件代码

<template>
  <div class="food-picker" @click.stop>
    <div class="reduce-wrapper" @click="reduce">
      <i class="iconfont reduce"></i>
    </div>
    <div class="counter">{{foodInfo.count}}</div>
    <div class="add-wrapper" @click="add">
      <i class="iconfont add"></i>
    </div>
  </div>
</template>
<script>
export default {
  name: "food-picker",
  props: {
    foodInfo: {
      type: Object,
      default: () => ({})
    }
  },
  methods: {
    reduce() {
      if (parseInt(this.foodInfo.count) > 0) {
        this.foodInfo.count--;
      }
    },
    add() {
      this.foodInfo.count++;
    }
  }
};
</script>
<style lang="less" scoped>
.food-picker {
  min-width: 180px;
  max-width: 200px;

  display: flex;
  align-items: center;
  width: 100%;
  justify-content: space-between;
  .iconfont {
    color: #00a0dc;
    font-size: 38px;
  }
  .counter {
    // margin: 0 20px;
  }
}
</style>

下一个目标

基于目前已经实现的功能,整个应用的数据都是以json文件的格式存储在服务器,服务端并没有可以用来增删改查API接口可供使用。下一步我计划做出管理后台和服务端API用来管理前端页面的数据,使所有模块的数据都是可配置的,这样前端所渲染出来的数据也都是动态的,能够整合三端到一个项目也满足了当下Web全栈开发的场景需要。

总结

通过真实的开发这样一个复杂交互的应用,自己对Vue在实际业务场景中的使用和理解有深入了一步。深入理解了Vue数据驱动视图改变的**,熟练的掌握了组件化开发项目的流程,同时也感受到所带来的便利,为自己接下来预备做的中大型项目建筑好了桥梁。

支持

如果本项目对您学习有帮助,请您动手点个starhttps://github.com/konglingwen94/vue-elm-sell。也希望您继续关注我的动态https://github.com/konglingwen94,有了您的支持我会有动力开源更多有趣的项目。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.