商城(技术栈vue+vuecli+vant+axios)(1)

/}/

/.iconCartWrapper {/

/position: absolute;/

/top: 0;/

/right: 0;/

/width: 1.875rem;/

/}/

6.看完美女应该饿了

就来到了猜你喜欢

格式与今日热点 差不多,也不必过分讲解

//YouLike.vue

猜你喜欢

<YouLikeItem

v-for=“(item,index) in you_like_product_list”

:item=“item”

:key=“index”

/>

//YouLikeItem.vue

{{item.name}}

{{item.price}}

¥{{item.origin_price}}

6.当拉到很底部的时候,就需要返回定不了,这时候就需要返回顶部按钮

在这里插入图片描述

//Mark。vue

没错,这时候直接写这两行代码,就克斯实现返回顶部

let docB = document.documentElement || document.body;

// while (docB.scrollTop>0){

// setInterval(function () {

animate(docB,{scrollTop:‘0’},400,‘ease-out’);

// docB.scrollTo(0,0);

但是,它是直接闪现到顶部的,这显示的十分粗鲁,所以必须填加懂话,如代码所示

/**

  • 运动效果

  • @param {HTMLElement} element 运动对象,必选

  • @param {JSON} target 属性:目标值,必选

  • @param {number} duration 运动时间,可选

  • @param {string} mode 运动模式,可选

  • @param {function} callback 可选,回调函数,链式动画

*/

export const animate = (element, target, duration = 400, mode = ‘ease-out’, callback) => {

clearInterval(element.timer);

//判断不同参数的情况

if (duration instanceof Function) {

callback = duration;

duration = 400;

}else if(duration instanceof String){

mode = duration;

duration = 400;

}

//判断不同参数的情况

if (mode instanceof Function) {

callback = mode;

mode = ‘ease-out’;

}

//获取dom样式

const attrStyle = attr => {

if (attr === “opacity”) {

return Math.round(getStyle(element, attr, ‘float’) * 100);

} else {

return getStyle(element, attr);

}

};

//根字体大小,需要从此将 rem 改成 px 进行运算

const rootSize = parseFloat(document.documentElement.style.fontSize);

const unit = {};

const initState = {};

//获取目标属性单位和初始样式值

Object.keys(target).forEach(attr => {

if (/[\d.]+/gi.test(target[attr])) {

unit[attr] = target[attr].match(/[\d.]+/gi)[0] || ‘px’;

}else{

unit[attr] = ‘px’;

}

initState[attr] = attrStyle(attr);

});

//去掉传入的后缀单位

Object.keys(target).forEach(attr => {

if (unit[attr] === ‘rem’) {

target[attr] = Math.ceil(parseInt(target[attr])*rootSize);

}else{

target[attr] = parseInt(target[attr]);

}

});

let flag = true; //假设所有运动到达终点

const remberSpeed = {};//记录上一个速度值,在ease-in模式下需要用到

element.timer = setInterval(() => {

Object.keys(target).forEach(attr => {

let iSpeed = 0; //步长

let status = false; //是否仍需运动

let iCurrent = attrStyle(attr) || 0; //当前元素属性址

let speedBase = 0; //目标点需要减去的基础值,三种运动状态的值都不同

let intervalTime; //将目标值分为多少步执行,数值越大,步长越小,运动时间越长

switch(mode){

case ‘ease-out’:

speedBase = iCurrent;

intervalTime = duration*5/400;

break;

case ‘linear’:

speedBase = initState[attr];

intervalTime = duration*20/400;

break;

case ‘ease-in’:

let oldspeed = remberSpeed[attr] || 0;

iSpeed = oldspeed + (target[attr] - initState[attr])/duration;

remberSpeed[attr] = iSpeed;

break;

default:

speedBase = iCurrent;

intervalTime = duration*5/400;

}

if (mode !== ‘ease-in’) {

iSpeed = (target[attr] - speedBase) / intervalTime;

iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);

}

//判断是否达步长之内的误差距离,如果到达说明到达目标点

switch(mode){

case ‘ease-out’:

status = iCurrent !== target[attr];

break;

case ‘linear’:

status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed);

break;

case ‘ease-in’:

status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed);

break;

default:

status = iCurrent !== target[attr];

}

if (status) {

flag = false;

//opacity 和 scrollTop 需要特殊处理

if (attr === “opacity”) {

element.style.filter = “alpha(opacity:” + (iCurrent + iSpeed) + “)”;

element.style.opacity = (iCurrent + iSpeed) / 100;

} else if (attr === ‘scrollTop’) {

element.scrollTop = iCurrent + iSpeed;

}else{

element.style[attr] = iCurrent + iSpeed + ‘px’;

}

} else {

flag = true;

}

if (flag) {

clearInterval(element.timer);

if (callback) {

callback();

}

}

})

}, 20);

};

/**

  • 获取style样式

*/

export const getStyle = (element, attr, NumberMode = ‘int’) => {

let target;

// scrollTop 获取方式不同,没有它不属于style,而且只有document.body才能用

if (attr === ‘scrollTop’) {

target = element.scrollTop;

}else if(element.currentStyle){

target = element.currentStyle[attr];

}else{

target = document.defaultView.getComputedStyle(element,null)[attr];

}

//在获取 opactiy 时需要获取小数 parseFloat

return NumberMode === ‘float’ ? parseFloat(target) : parseInt(target);

};

然后直接用animate这个方法直接调用就可以了哦!

2.分类页面

=========================================================================

1.头部组件燥起来

在这里插入图片描述

//Header.vue

输入商品名称

2.好了,到了内容区域了,先写左边导航部分

在这里插入图片描述

请看静态界面的搭建

    • 推荐

    • 安心蔬菜

    • 豆制品

    • 新鲜水果

    • 肉禽蛋

    • 海鲜水产

    • 乳品烘焙

    • 营养早餐

    • 叮咚心选

    • 米面粮油

    • 调味品

    • 方便速食

    • 冰淇淋

    • 酒水饮料

    • 休闲零食

    • 快手菜

    • 南北干货

    • 宝宝餐

    • 厨房用品

      接下来,就要完成左边导航的业务部分了,首先呢,我们先得获取数据

      async initData(){

      let leftRes = await getCategories();

      if (leftRes.success===true){

      this.leftRes=leftRes.data.cate;

      }

      // let rightRes = await getCategoriesDetail();

      // console.log(rightRes);

      // if(rightRes.success===true){

      // this.rightRes=rightRes;

      // }

      }

      接下来,就要实现better-scroll部分了

      this.$nextTick(()=>{

      this.leftScroll = new BScroll(“.leftWrapper”,{probeType:3});

      console.log(this.leftScroll);

      });

      这时发现,并不能滑动,发现原因是

      wrapper 与 content 高度问题

      只有content的高度大于wrapper高度时候,才可以滚动。

      如何看?

      this.$nextTick(() => {

      if (!this.scroll) {

      this.scroll = new BScroll(this.$refs.wrapper, {})

      console.log(this.scroll)

      }

      })

      F12就可以看到打印结果:

      在这里插入图片描述

      以上就是可以滚动的情况,wrapperHeight(616) < scrollHeight(750),hasVerticalScroll为true;

      于是,把wrapper部分高度改成98%,就好了

      ok,已经可以滑动了。。。。

      接下来就是要实现点击哪里,高亮就在哪里

      //改变左侧当前索引

      changeCurrentIndex(index){

      console.log(index);

      console.log(this.currentIndex);

      // this.currentIndex=index;

      }

      这一部分简单,唯一要注意的就是,想要实现这里的点击事件必须要在配置better-scroll的时候,加上click:true

      ,同时还要实现,高亮的能贴到顶部去,所以可以使用

      //改变左侧当前索引

      changeCurrentIndex(index){

      this.currentIndex=index;

      //相应的元素滚动

      let menulist=this.$refs.menulist;

      let currentLi = menulist[index];

      this.leftScroll.scrollToElement(currentLi,300);

      }

      4.购物车

      ========================================================================

      1.网购物车里添加数据

      mutations: {

      //1.往购物车添加数据

      addGoods(state,{goodsId,goodsName,smallImage,goodsPrice}){

      let shopCart = state.shopCart;

      //1.1判断商品是否存在

      if(shopCart[goodsId]){//存在

      shopCart[goodsId][“num”]++;

      }else {//不存在

      shopCart[goodsId] = {

      “num”:1,

      “id”:goodsId,

      ‘goodsName’:goodsName,

      ‘smallImage’:smallImage,

      “goodsPrice”:goodsPrice

      }

      }

      //1.2产生新对象

      state.shopCart = […shopCart];

      }

      },

      2.数据本地化

      /**

      • 本地化存储

      */

      export const setStore = (name,content)=>{

      if(!name) return;

      if(typeof content !==‘string’){

      content =JSON.stringify(content);

      }

      window.localStorage.setItem(name,content);

      };

      /**

      • 获取本地化数据

      */

      export const getStor = (name)=>{

      if(!name) return;

      return window.localStorage.getItem(name);

      };

      /**

      *本地化删除

      */

      export const removeStore =(name)=>{

      if (!name) return;

      return window.localStorage.removeItem(name);

      }

      当本地有购物车数据时,应当把本地的购物车数据同步到vuex的state中

      //2.页面初始化,获取购物车的数据(本地)

      initShopCart(state){

      let initCart = getStore(‘shopCart’);

      if (initCart) {

      state.shopCart = JSON.parse(initCart);

      }

      }

      到这里,应该要让底部的Tabbar显示相对应的导航了在这里插入图片描述

      goodsNum(){

      let num = 0;

      Object.values(this.shopCart).forEach((item,index)=>{

      num += item.num;

      });

      return num;

      }

      3.1删除购物车数据

      //3.把商品移除购物车

      reduceCart(state,{goodsId}){

      let shopCart = state.shopCart;

      let goods = shopCart[goodsId];

      if(goods){//找到该商品

      if(goods[‘num’]>0){

      goods[‘num’]–;

      //3.1判断商品是不是只剩下一个

      if(goods[“num”]===1){

      delete shopCart[goodsId];

      }

      }else {

      goods = null;

      }

      }

      //3.2同时同步数据

      state.shopCart = shopCart;

      setStore(‘shopCart’,shopCart);

      }

      删除商品的界面操作

      removeOutCart(goodsId,goodsNum){

      if (goodsNum>1){

      this.$store.commit(“reduceCart”,{goodsId})

      }else if(goodsNum===1){//挽留

      Dialog.confirm({

      title: ‘培歌提醒你’,

      message: ‘你确定要删除该商品嘛’

      }).then(() => {

      // on confirm

      this.$store.commit(“reduceCart”,{goodsId})

      }).catch(() => {

      // on cancel

      });

      }

      },

      单个商品的选中和取消

      //4.单个商品的选中或取消

      selectSingerGoods(state,{goodsId}){

      let shopCart = state.shopCart;

      let goods = shopCart[goodsId];

      if(goods){

      if(goods.checked){

      goods.checked = !goods.checked;

      }else {

      Vue.set(goods,‘checked’,true);

      }

      //4.1同步数据

      state.shopCart = {…shopCart};

      setStore(“shopCart”,state.shopCart);

      }

      }

      所有商品选中和取消

      //5.所有商品的选中和取消

      selectAllGoods(isSelected){

      let shopCart = state.shopCart;

      Object.values(shopCart).forEach((goods,index)=>{

      if (goods.checked){

      goods.checked=!isSelected;

      }else{

      Vue.set(goods,“checked”,true)

      }

      });

      //5.1同步数据

      state.shopCart = {…shopCart};

      setStore(“shopCart”,state.shopCart);

      }

      界面实现所有商品的选中和取消

      computed:{

      …mapState([“shopCart”]),

      isSelectAll(){

      let tag = true;

      Object.values(this.shopCart).forEach((item,index)=>{

      if(!item.checked){

      tag = false;

      }

      });

      return tag;

      }

      },

      methods:{

      selectAll(isSelectAll){

      console.log(“我来全选了”);

      console.log(isSelectAll);

      this.$store.commit(“selectAllGoods”);

      }

      },

      商品总价

      //商品总价结算

      totalPrice(){

      let allPrice = 0;

      Object.values(this.shopCart).forEach((item,index) =>{

      if (item.checked){

      allPrice += item.num*item.goodsPrice;

      }

      });

      return allPrice;

      }

      购物车结算总数量

      //商品总结算数

      allSelectedGoodsCount(){

      let counts=0;

      Object.values(this.shopCart).forEach((item,index)=>{

      if (item.checked){

      counts+=1;

      }

      })

      return counts;

      }

      5.订单界面

      =========================================================================

      顶部导航实现

      在这里插入图片描述

      <van-nav-bar

      title=“标题”

      left-text=“返回”

      right-text=“按钮”

      left-arrow

      @click-left=“onClickLeft”

      @click-right=“onClickRight”

      />

      export default {

      name: “Order”,

      methods: {

      onClickLeft() {

      this.$router.back();

      },

      onClickRight() {

      }

      }

      }

      选择地址按钮实现

      在这里插入图片描述

      <van-contact-card

      style=“margin-top: 0.1rem”

      type=“add”

      add-text=“选择收获地址”

      @click=“chooseAddress()”

      />

      我的地址选择界面

      在这里插入图片描述

      与订单界面类似

      地址列表引入

      <van-address-list

      v-model=“chosenAddressId”

      :list=“list”

      @add=“onAdd”

      @edit=“onEdit”

      />

      data() {

      return {

      chosenAddressId: ‘1’,

      list: [

      {

      id: ‘1’,

      name: ‘张三’,

      tel: ‘13000000000’,

      address: ‘浙江省杭州市西湖区文三路 138 号东方通信大厦 7 楼 501 室’

      },

      {

      id: ‘2’,

      name: ‘李四’,

      tel: ‘1310000000’,

      address: ‘浙江省杭州市拱墅区莫干山路 50 号’

      }

      ],

      }

      },

      添加地址页面

      在这里插入图片描述

      <van-nav-bar

      title=“我的地址”

      left-text=“返回”

      right-text=“登陆”

      left-arrow

      @click-left=“onClickLeft”

      @click-right=“onClickRight”

      />

      <van-address-edit

      :area-list=“areaList”

      show-postal

      show-delete

      show-set-default

      show-search-result

      :search-result=“searchResult”

      @save=“onSave”

      @delete=“onDelete”

      @change-detail=“onChangeDetail”

      />

      编辑地址页面与添加地址页面相似

      在这里插入图片描述

      5.个人中心的基本界面

      ==============================================================================

      在这里插入图片描述

      <van-nav-bar

      class=“header”

      title=“个人中心”

      left-arrow

      />

      培歌行

      微信号:Venus211_

      <van-grid-item

      v-for=“(item,index) in orderData”

      :key=“index”

      :icon=“item.icon”

      :text=“item.title”

      />

    • 8
      点赞
    • 8
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    可以参考以下步骤: 1. 首先安装 axios 和 qs(如果需要) 2. 在 main.js 中引入 axios 并进行二次封装: ``` import axios from 'axios' import qs from 'qs' axios.defaults.baseURL = 'http://api.xxx.com' // 设置接口基地址 axios.defaults.timeout = 10000 // 设置超时时间 axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8' // 设置默认请求头 axios.interceptors.request.use(config => { // 在请求发送之前做一些处理 config.headers.token = localStorage.getItem('token') || '' return config }, error => { // 出错处理 return Promise.reject(error) }) axios.interceptors.response.use(response => { // 在响应成功处理之前做一些处理 return response }, error => { // 响应错误处理 return Promise.reject(error) }) function get (url, params) { return new Promise((resolve, reject) => { axios.get(url, { params }).then(res => { resolve(res.data) }).catch(err => { reject(err.data) }) }) } function post (url, data) { return new Promise((resolve, reject) => { axios.post(url, qs.stringify(data)).then(res => { resolve(res.data) }).catch(err => { reject(err.data) }) }) } export default { get, post } ``` 3. 在组件中使用二次封装后的 axios: ``` import request from '@/utils/request' methods: { getData () { request.get('/api/getData', { id: '123456' }).then(res => { console.log(res) }).catch(err => { console.log(err) }) } } ``` 以上是对于vue3 + vant + ts 配置axios的二次封装的一种实现,仅供参考。
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值