基于Node+Vue.js高质量商城系统的开发

简介: 下面看一下开发的基于Node+Vue.js高质量商城系统的开发系统。

下面看一下开发的这个系统。


一,系统界面


微信图片_20221009223328.png


微信图片_20221009223337.png


微信图片_20221009223341.png


微信图片_20221009223345.png


微信图片_20221009223349.png


微信图片_20221009223354.png


微信图片_20221009223400.png


微信图片_20221009223405.png


微信图片_20221009223409.png


微信图片_20221009223413.png


微信图片_20221009223418.png


微信图片_20221009223422.png


微信图片_20221009223426.png


二,系统核心代码展示


// 小孟v:jishulearn
<template>
  <view class="cart">
  <!--<view class="header" :style="{ position: headerPosition, top: headerTop }">-->
    <!--<view class="title">购物车</view>-->
  <!--</view>-->
  <!--&lt;!&ndash; 占位 &ndash;&gt;-->
  <!--<view class="place"></view>-->
  <!-- 购物车为空 -->
  <view v-if="cartList.length === 0" class="empty">
    <text class="iconfont icongouwuche" :class="'text-'+themeColor.name"></text>
    <view v-if="hasLogin" class="empty-tips">
    空空如也
    <navigator
      class="navigator"
      :class="'text-'+themeColor.name"
      v-if="hasLogin"
      url="../category/category"
      open-type="switchTab"
      >随便逛逛></navigator
    >
    </view>
    <view v-else class="empty-tips">
    空空如也
    <view class="navigator" :class="'text-'+themeColor.name" @tap="navTo('/pages/public/logintype')"
      >登录/注册 ></view
    >
    </view>
  </view>
  <!-- 购物车列表 -->
  <view class="goods-list" v-else>
    <view class="rf-cart-row" v-for="(row, index) in cartList" :key="index">
    <!-- 删除按钮 -->
    <view class="menu" @tap.stop="deleteCartItem(row.id, 'one')"  :class="'bg-'+themeColor.name">
      <text class="iconfont icon iconiconfontshanchu1"></text>
    </view>
    <!-- 商品 -->
    <view
      class="carrier"
      :class="[
      theIndex === index ? 'open' : oldIndex === index ? 'close' : ''
      ]"
      @touchstart="touchStart(index, $event)"
      @touchmove="touchMove(index, $event)"
      @touchend="touchEnd(index, $event)"
    >
      <!-- checkbox -->
      <view class="checkbox-box" @tap="selected(index, row)">
      <view
        class="checkbox"
        :class="[
        parseInt(row.status, 10) === 0 ? `checkbox-disabled ${'text-'+themeColor.name}` : 'text-'+themeColor.name
        ]"
      >
        <view :class="[row.selected ? `on ${'bg-'+themeColor.name}` : '']"></view>
      </view>
      </view>
      <!-- 商品信息 -->
      <view class="goods-info">
      <view class="img">
        <image :src="row.productPic"></image>
      </view>
      <view class="info">
        <view
        class="title in2line"
        @tap="navTo(`/pages/product/product?id=${row.product.id}`)"
        >
        {{ row.productName }}
        </view>
        <view class="state-wrapper">
        <view class="spec" @tap.stop="toggleSpec(row)">{{ row.productAttr|attrFilter}}</view>
        <view class="state" v-if="parseInt(row.status, 10) === 0">
          已失效
        </view>
        </view>
        <view class="price-number">
        <view class="price">{{ moneySymbol }}{{
          row.price
        }}</view>
        <view class="remark" >{{ row.remark }}</view>
        <view class="number" >
          <view class="sub" @tap.stop="sub(row, index)">
          <text class="iconfont icon icon-jianhao"></text>
          </view>
          <view class="input" @tap.stop="discard">
          <input
            type="number"
            :class="'text-'+themeColor.name"
            v-model="row.quantity"
            @input.stop="numberChange(row, $event, index)"
          />
          </view>
          <view class="add" @tap.stop="add(row, index)">
          <text class="iconfont icon iconjia1"></text>
          </view>
        </view>
        </view>
      </view>
      </view>
    </view>
    </view>
  </view>
  <!-- 脚部菜单 -->
  <view
    class="footer"
    :style="{ bottom: footerbottom }"
    v-if="cartList.length !== 0"
  >
    <view class="checkbox-box" @tap="allSelect">
    <view class="checkbox" :class="'text-'+themeColor.name">
      <view :class="[isAllselected ? `on ${'bg-'+themeColor.name}` : '']"></view>
    </view>
    <view class="text">全选</view>
    </view>
    <view
    class="delBtn"
    :class="'text-'+themeColor.name"
    @tap="deleteCartItem"
    v-if="selectedList.length > 0"
    >删除</view
    >
    <view class="settlement">
    <view class="sum"
      >合计:
      <view class="money">{{ moneySymbol }}{{ sumPrice }}</view>
    </view>
    <view class="btn" :class="'bg-'+themeColor.name" @tap="createOrder"
      >结算({{ selectedList.length }})</view
    >
    </view>
  </view>
  <!-- 规格-模态层弹窗 -->
  <view
    class="popup spec show"
    v-if="specClass === 'show'"
    @touchmove.stop.prevent="stopPrevent"
    @tap="hideSpec"
  >
    <!-- 遮罩层 -->
    <view class="mask" @tap="hideSpec"></view>
    <view class="layer" @tap.stop="stopPrevent">
    <rf-attr-content
      :isSelectedNum="false"
      :product="productDetail"
      @toggle="toggleSpec"
      ></rf-attr-content>
    </view>
  </view>
  <!--页面加载动画-->
  <rfLoading isFullScreen :active="loading"></rfLoading>
  </view>
</template>
<script>
import {
  cartItemClear,
  cartItemDel,
  cartItemList,
  cartItemUpdateNum,
  cartItemUpdateSku,
  productDetail
} from '@/api/product';
import rfAttrContent from '@/components/rf-attr-content';
import { mapMutations } from 'vuex';
export default {
  components: { rfAttrContent },
  filters:{
  attrFilter(val){
    if(!val) return ;
    val = JSON.parse(val);
    let res = [];
    val.map(v=>{
    res.push(v.value);
    })
    return res.join(',');
  }
  },
  data() {
  return {
    sumPrice: '0.00',
    headerPosition: 'fixed',
    headerTop: null,
    statusTop: null,
    showHeader: true,
    selectedList: [],
    isAllselected: false,
    // 控制滑动效果
    theIndex: null,
    oldIndex: null,
    isStop: false,
    cartList: [],
    hasLogin: null,
    footerbottom: 0,
    specClass: 'none',
    productDetail: {
        base_attribute_format: [],
        sku: []
    },
    specSelected: [],
    specChildList: [],
    specList: [],
    currentSkuId: '',
    moneySymbol: this.moneySymbol,
    loading: true,
    singleSkuText: this.singleSkuText
  };
  },
  onPageScroll(e) {
  // 兼容iOS端下拉时顶部漂移
  this.headerPosition = e.scrollTop >= 0 ? 'fixed' : 'absolute';
  this.headerTop = e.scrollTop >= 0 ? null : 0;
  this.statusTop = e.scrollTop >= 0 ? null : -this.statusHeight + 'px';
  },
  // 下拉刷新,需要自己在page.json文件中配置开启页面下拉刷新 "enablePullDownRefresh": true
  onPullDownRefresh() {
  this.selectedList.length = 0;
  this.isAllselected = false;
  this.sumPrice = 0;
  this.getCartItemList('refresh');
  },
  onShow() {
  // 兼容H5下结算条位置
  // #ifdef H5
  this.footerbottom =
    document.getElementsByTagName('uni-tabbar')[0].offsetHeight + 'px';
  // #endif
  // #ifdef APP-PLUS
  this.showHeader = false;
  // eslint-disable-next-line
  this.statusHeight = plus.navigator.getStatusbarHeight();
  // #endif
  this.initData();
  },
  methods: {
  ...mapMutations(['setCartNum']),
  // 规格弹窗开关
  toggleSpec(row) {
    if (parseInt(row.status, 10) === 0) return;
    if (this.specClass === 'show') {
    if (!this.hasLogin) {
      this.specClass = 'none';
      this.$mHelper.toast('请先登录!');
      return;
    }
    this.handleCartItemUpdateSku(this.currentSkuId, row.skuId);
    this.specClass = 'hide';
    setTimeout(() => {
      this.specClass = 'none';
    }, 250);
    } else if (this.specClass === 'none') {
    if (row) {
      this.getProductDetail(row);
    }
    }
  },
  // 获取产品详情
  async getProductDetail(row) {
    this.currentSkuId = row.sku_id;
    await this.$http
    .get(`${productDetail}`, {
      id: row.product_id
    })
    .then(async r => {
      this.productDetail = r.data;
      this.productDetail.sku_name = row.sku_name;
      this.specClass = 'show';
    });
  },
  hideSpec() {
    this.specClass = 'hide';
    setTimeout(() => {
    this.specClass = 'none';
    }, 250);
  },
  stopPrevent() {},
  // 删除选中购物车商品
  async deleteCartItem(id, type) {
    let ids = [];
    for (let i = 0; i < this.cartList.length; i++) {
    if (this.cartList[i].selected) {
      ids.push(parseInt(this.cartList[i].id, 10));
    }
    }
    await this.$http2.post("cart/delete",{ids},{
    header:{
      'content-type':'application/x-www-form-urlencoded'
    }
    });
    this.selectedList.length = 0;
    this.isAllselected = false;
    this.sumPrice = 0;
    this.getCartItemList();
    this.oldIndex = null;
    this.theIndex = null;
    // await this.$http
    //  .post(`${cartItemDel}`, {
    //    sku_ids: JSON.stringify(skuIds)
    //  })
    //  .then(() => {
    //    this.selectedList.length = 0;
    //    this.isAllselected = false;
    //    this.sumPrice = 0;
    //    this.getCartItemList();
    //    this.oldIndex = null;
    //    this.theIndex = null;
    //  });
  },
  // 修改购物车商品sku
  async handleCartItemUpdateSku(skuId, newSkuId) {
    await this.$http
    .post(`${cartItemUpdateSku}`, {
      sku_id: skuId,
      new_sku_id: newSkuId
    })
    .then(() => {
      this.selectedList.length = 0;
      this.isAllselected = false;
      this.sumPrice = 0;
      this.getCartItemList();
    });
  },
  // 数据初始化
  initData() {
    this.hasLogin = this.$mStore.getters.hasLogin;
    if (this.hasLogin) {
    this.theIndex = null;
    this.oldIndex = null;
    this.selectedList.length = 0;
    this.isAllselected = false;
    this.sumPrice = 0;
    this.getCartItemList();
    } else {
    this.cartList = [];
    this.selectedList.length = 0;
    this.loading = false;
    }
  },
  // 通用跳转
  navTo(route) {
    if (!this.$mStore.getters.hasLogin) {
    uni.setStorageSync('backToPage', JSON.stringify({ route: '/pages/cart/cart' }));
    }
    this.$mRouter.push({ route });
  },
  // 获取购物车列表
  async getCartItemList(type) {
    let r = await this.$http2.get('cart/list').catch(() => {
    this.cartList = [];
    this.loading = false;
    if (type === 'refresh') {
      uni.stopPullDownRefresh();
    }
    });
    this.loading = false;
    this.cartList = r.data;
    this.$set(this,'cartList',[...r.data]);
  // 清空购物车
  clearCart(params) {
    const content = params ? '清空失效商品?' : '清空购物车?';
    uni.showModal({
    content,
    success: async e => {
      if (e.confirm) {
      await this.$http.post(`${cartItemClear}`, params).then(() => {
        this.selectedList.length = 0;
        this.isAllselected = false;
        this.sumPrice = 0;
        this.getCartItemList();
      });
      }
    }
    });
  },
  // 控制左滑删除效果
  touchStart(index, event) {
    // 多点触控不触发
    if (event.touches.length > 1) {
    this.isStop = true;
    return;
    }
    this.oldIndex = this.theIndex;
    this.theIndex = null;
    // 初始坐标
    this.initXY = [event.touches[0].pageX, event.touches[0].pageY];
  },
目录
相关文章
|
4天前
|
移动开发 前端开发 JavaScript
前端高效开发JavaScript库!
前端高效开发JavaScript库!
|
3天前
|
监控 安全 NoSQL
采用java+springboot+vue.js+uniapp开发的一整套云MES系统源码 MES制造管理系统源码
MES系统是一套具备实时管理能力,建立一个全面的、集成的、稳定的制造物流质量控制体系;对生产线、工艺、人员、品质、效率等多方位的监控、分析、改进,满足精细化、透明化、自动化、实时化、数据化、一体化管理,实现企业柔性化制造管理。
24 3
|
3天前
|
缓存 前端开发 JavaScript
React和Next.js开发常见的HTTP请求方法
React和Next.js开发常见的HTTP请求方法
7 0
|
3天前
Vue3+Vite+Pinia+Naive后台管理系统搭建之五:Pinia 状态管理
Vue3+Vite+Pinia+Naive后台管理系统搭建之五:Pinia 状态管理
8 1
|
3天前
Vue3+Vite+Pinia+Naive后台管理系统搭建之三:vue-router 的安装和使用
Vue3+Vite+Pinia+Naive后台管理系统搭建之三:vue-router 的安装和使用
8 0
|
3天前
Vue3+Vite+Pinia+Naive后台管理系统搭建之二:scss 的安装和使用
Vue3+Vite+Pinia+Naive后台管理系统搭建之二:scss 的安装和使用
8 0
|
4天前
Vue3+Vite+Pinia+Naive后台管理系统搭建之一:基础项目构建
Vue3+Vite+Pinia+Naive后台管理系统搭建之一:基础项目构建
8 1
|
4天前
|
JavaScript
js中如何使用工厂方式和构造函数创建对象,web开发项目实例
js中如何使用工厂方式和构造函数创建对象,web开发项目实例
|
4天前
|
设计模式 存储 前端开发
JS的几种设计模式,Web前端基础三剑客学习知识分享,前端零基础开发
JS的几种设计模式,Web前端基础三剑客学习知识分享,前端零基础开发
|
5天前
|
JSON JavaScript API
使用 Node.js 开发一个简单的 web 服务器响应 HTTP post 请求
使用 Node.js 开发一个简单的 web 服务器响应 HTTP post 请求
18 1