基于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];
  },
目录
相关文章
|
2天前
|
JavaScript 前端开发 测试技术
探索现代JavaScript开发的最佳实践
本文探讨了现代JavaScript开发中的最佳实践,涵盖ES6+特性、现代框架使用、模块化与代码分割、测试驱动开发、代码质量与性能优化、异步编程、SPA与MPA架构选择、服务端渲染和静态站点生成等内容,旨在帮助开发者提升代码质量和开发效率。
|
6天前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端开发
【10月更文挑战第36天】本文将引导您探索Node.js的世界,通过实际案例揭示其背后的原理和实践方法。从基础的安装到高级的异步处理,我们将一起构建一个简单的后端服务,并讨论如何优化性能。无论您是新手还是有经验的开发者,这篇文章都将为您提供新的视角和深入的理解。
|
11天前
|
Web App开发 存储 JavaScript
深入浅出Node.js后端开发
【10月更文挑战第31天】本文将引导你进入Node.js的奇妙世界,探索其如何革新后端开发。通过浅显易懂的语言和实际代码示例,我们将一起学习Node.js的核心概念、搭建开发环境,以及实现一个简单但完整的Web应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇通往高效后端开发的大门。
|
7天前
|
运维 监控 JavaScript
鸿蒙next版开发:分析JS Crash(进程崩溃)
在HarmonyOS 5.0中,JS Crash指未处理的JavaScript异常导致应用意外退出。本文详细介绍如何分析JS Crash,包括异常捕获、日志分析和典型案例,帮助开发者定位问题、修复错误,提升应用稳定性。通过DevEco Studio收集日志,结合HiChecker工具,有效解决JS Crash问题。
24 4
|
12天前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端开发
【10月更文挑战第30天】本文将通过一个Node.js的简单示例,引导你进入Node.js的世界。我们将从基础概念讲起,然后一步步深入到代码实现,最后总结Node.js在后端开发中的优势和应用场景。无论你是前端开发者还是后端新手,这篇文章都将为你打开一扇了解Node.js的大门。
25 2
|
20天前
|
开发框架 JavaScript 前端开发
HarmonyOS UI开发:掌握ArkUI(包括Java UI和JS UI)进行界面开发
【10月更文挑战第22天】随着科技发展,操作系统呈现多元化趋势。华为推出的HarmonyOS以其全场景、多设备特性备受关注。本文介绍HarmonyOS的UI开发框架ArkUI,探讨Java UI和JS UI两种开发方式。Java UI适合复杂界面开发,性能较高;JS UI适合快速开发简单界面,跨平台性好。掌握ArkUI可高效打造符合用户需求的界面。
71 8
|
18天前
|
JavaScript 前端开发
javascript开发的简单的弹幕插件
这是一个原生javascript开发的简单的弹幕插件,具有美观、易用,占用的资源较低等特点,可以给弹幕设置内容、颜色、头像、链接地址等属性,鼠标悬停等,简单实用,欢迎下载!
35 5
|
20天前
|
JavaScript 前端开发 持续交付
构建现代Web应用:Vue.js与Node.js的完美结合
【10月更文挑战第22天】随着互联网技术的快速发展,Web应用已经成为了人们日常生活和工作的重要组成部分。前端技术和后端技术的不断创新,为Web应用的构建提供了更多可能。在本篇文章中,我们将探讨Vue.js和Node.js这两大热门技术如何完美结合,构建现代Web应用。
19 4
|
9天前
|
Web App开发 JavaScript 前端开发
探索后端开发:Node.js与Express的完美结合
【10月更文挑战第33天】本文将带领读者深入了解Node.js和Express的强强联手,通过实际案例揭示它们如何简化后端开发流程,提升应用性能。我们将一起探索这两个技术的核心概念、优势以及它们如何共同作用于现代Web开发中。准备好,让我们一起开启这场技术之旅!
23 0
|
16天前
|
JavaScript 前端开发 安全
深入浅出Node.js后端开发
【10月更文挑战第26天】在这篇文章中,我们将一起探索Node.js的奇妙世界。不同于传统的Java或Python,Node.js以其异步非阻塞I/O和事件驱动的特性,在后端开发领域独树一帜。无论你是初学者还是资深开发者,这篇文章都将为你提供新的视角和思考。从基础概念到实际应用,我们一步步深入Node.js的世界,让你了解其不仅仅是JavaScript运行环境那么简单。