基于node.js和Vue的前后端分离智能客服商城系统设计

今天给大家介绍一个前后端分离的商城项目,说到前后端分离,大家肯定以为后端是java一类的,但是今天的项目采用的是Node.js的express框架。

基于VUE的前后端分离商城,附源码

架构

  • 前端:vue全家桶
  • 后端: node:express框架
  • 数据库:MySQL

基本功能

普通用户

  • 注册、登录(图形验证码)
  • 定位 (腾讯地图定位功能)、自主选择所在城市
  • 商品
    • 分类
    • 简单展示商品
    • 查看商品详情
    • 商品评论
  • 分页功能
  • 购物车功能
    • 加入购物车
    • 购物车商品数量增减
    • 清空购物车
    • 商品结算
  • 关键词模糊搜索商品 (关键词需为商品名称)
  • 用户个人中心
  • 查看订单
    • 修改用户信息 (头像、昵称、简介...)
    • 修改手机号
    • 修改密码
    • 联系客服

管理员

  • 登录(固定账号密码:admin)
  • 查看所有用户
  • 查看数据库商品信息
  • 商品上架(添加商品)
  • 删除/修改商品
  • 查看订单
  • 分页功能
  • 客服管理

商品代码展示

<template>
  <div id="container" v-if="goodsDetail[0]">
    <div class="pro_detail">
      <div class="pro_img">
        <div class="tb_booth">
          <img :src="goodsDetail[0].image_url" class="pro_middle_img" />
        </div>
      </div>
      <div class="pro_meg">
        <div class="pro_meg_hd">
          <h1>
            {{ goodsDetail[0].goods_name }}
          </h1>
        </div>
        <div class="pro_meg_price">
          <dl>
            <dt>促销价</dt>
            <dd>
              <div class="promo_price">
                <span class="tm-price">{{
                  (goodsDetail[0].price / 100) | moneyFormat
                }}</span>
                <b>优惠促销</b>
              </div>
            </dd>
          </dl>
          <dl>
            <dt>市场价</dt>
            <dd class="nor_price">
              {{ (goodsDetail[0].normal_price / 100) | moneyFormat }}
            </dd>
          </dl>
          <dl>
            <dt>本店优惠</dt>
            <dd>包邮</dd>
          </dl>
          <dl>
            <dt class="sale_tip">{{ goodsDetail[0].sales_tip }}</dt>
          </dl>
          <dl>
            <dt>服务承诺</dt>
            <dd>
              <span>正品保证</span>
              <span>极速退货</span>
            </dd>
          </dl>
        </div>
        <div class="pro_meg_deliver">
          <dl>
            <dt>运费</dt>
            <dd>
              福建福州&nbsp;&nbsp;至&nbsp;&nbsp;福建福州&nbsp;&nbsp;&nbsp;快递:0.00
            </dd>
          </dl>
        </div>
        <div class="pro_meg_console">
          <dl class="tb-sku">
            <dt>数量</dt>
            <dd>
              <div class="item-amout">
                <el-input-number
                  v-model="shopNum"
                  :min="1"
                  :max="goodsDetail[0].counts"
                ></el-input-number>
              </div>
              <span>库存</span><em>{{ goodsDetail[0].counts }}</em
              ><span>件</span>
            </dd>
          </dl>
          <div class="shopping_car">
            <el-button
              type="danger"
              @click.prevent="dealWithCellBtnClick(goodsDetail[0])"
              >加入购物车</el-button
            >
          </div>
        </div>
      </div>
    </div>
    <div class="pro_comment">
      <h3>商品评价</h3>
      <div v-if="goodsDetail[0].comments_count">
        <div
          class="media"
          v-for="(comment, index) in goodsComment"
          :key="index"
        >
          <div class="media-body">
            <h6 class="media-heading" v-if="comment.user_nickname">
              用户:&nbsp;{{ comment.user_nickname }}
            </h6>
            <h6 class="media-heading" v-else>
              用户:&nbsp;{{ comment.user_name | nameFormat }}
            </h6>
            <span>评论:</span>&nbsp;{{ comment.comment_detail }}
            <el-rate
              v-model="comment.comment_rating"
              disabled
              show-score
              text-color="#ff9900"
            >
            </el-rate>
          </div>
        </div>
      </div>
      <div class="media" v-else>
        <div class="media-body">本商品暂无评论</div>
      </div>
    </div>
    <div class="pro_judge" v-if="userInfo.user_name">
      <h3>评价该商品</h3>
      <span>为该商品评分</span>
      <el-rate
        v-model="rating"
        :colors="colors"
        show-score
        text-color="#ff9900"
      >
      </el-rate>
      <el-input
        type="textarea"
        autosize
        placeholder="请输入内容"
        v-model="textarea"
      >
      </el-input>
      <el-button type="primary" @click="post()"
        >发布<i class="el-icon-edit el-icon--right"></i
      ></el-button>
    </div>
    <div class="pro_judge" v-else>
      <h3>评价该商品</h3>
      <span class="judge_erro_tip">登录后才可发表评论</span>
    </div>
  </div>
</template>

<script>
import { postComment, addGoodsToCart } from "./../../api/index";
import { MessageBox } from "element-ui";
import { mapActions } from "vuex";
import { mapState } from "vuex";

export default {
  data() {
    return {
      textarea: "",
      rating: 0,
      colors: ["#99A9BF", "#F7BA2A", "#FF9900"],
      currentGoodsId: 0,
      shopNum: 1,
    };
  },
  computed: {
    ...mapState(["goodsDetail", "userInfo", "goodsComment"]),
  },
  created() {
    this.currentGoodsId = Number(this.$route.params.id);
    this.$store.dispatch("reqGoodsDetail", {
      goodsNo: this.currentGoodsId,
    });
    this.$store.dispatch("reqGoodsComment", {
      goodsId: this.currentGoodsId,
    });
  },
  watch: {
    $route() {
      this.currentGoodsId = Number(this.$route.params.id);
      this.$store.dispatch("reqGoodsDetail", {
        goodsNo: this.currentGoodsId,
      });
      this.$store.dispatch("reqGoodsComment", {
        goodsId: this.currentGoodsId,
      });
      // 请求当前用户信息
      this.$store.dispatch("getUserInfo");
    },
  },
  methods: {
    async post() {
      if (!this.textarea) {
        MessageBox({
          type: "info",
          message: "评论不得为空",
          showClose: true,
        });
        return;
      }
      let result = await postComment(
        this.goodsDetail[0].goods_id,
        this.textarea,
        this.rating,
        this.userInfo.id
      );
      if (result.success_code === 200) {
        MessageBox({
          type: "success",
          message: "发布成功",
          showClose: true,
        });
        this.textarea = "";
        this.$store.dispatch("reqGoodsComment", {
          goodsId: this.currentGoodsId,
        });
      } else {
        MessageBox({
          type: "info",
          message: "发布失败",
          showClose: true,
        });
      }
    },
    // 监听商品点击
    async dealWithCellBtnClick(goods) {
      // 1. 发送请求
      // user_id, goods_id, goods_name, thumb_url, price, buy_count, counts
      if (this.userInfo.user_name) {
        let result = await addGoodsToCart(
          this.userInfo.id,
          goods.goods_id,
          goods.short_name,
          goods.thumb_url,
          goods.price,
          this.shopNum,
          goods.counts
        );
        if (result.success_code === 200) {
          MessageBox({
            type: "success",
            message: result.message,
            showClose: true,
          });
          let user_id = this.userInfo.id;
          // 请求商品数据
          this.$store.dispatch("reqCartsGoods", { user_id });
          this.shopNum = 1;
        }
      }
    },
  },
};
</script>

<style scoped>
#container > .pro_detail {
  width: 990px;
  position: relative;
  z-index: 100;
  margin: 20px auto;
}
#container > .pro_comment {
  width: 73%;
  position: relative;
  margin: 40px auto 0;
  padding: 20px;
  border: 1px solid #ccc;
  border-bottom: none;
}
#container > .pro_judge {
  width: 73%;
  position: relative;
  margin: 0 auto 60px;
  padding: 20px;
  border-top: none;
  border: 1px solid #ccc;
}
.pro_judge > span {
  font-size: 12px;
  color: #ccc;
}
.pro_judge > .el-rate {
  display: inline-block;
}
.pro_judge .el-textarea {
  width: 50%;
  display: block;
  margin: 20px 0;
}
.pro_comment > h3 {
  font-weight: bold;
  margin-bottom: 20px;
}
.pro_comment .media {
  border-top: 1px dashed #ccc;
  padding: 10px 0;
}
.pro_comment .media .media-heading {
  margin-bottom: 10px;
  font-weight: bolder;
}
.pro_comment .media .media-body {
  font-size: 14px;
}
.pro_comment .media .media-body span {
  font-weight: bolder;
}
.pro_comment .el-rate {
  display: inline-block;
  margin-left: 20px;
}
.pro_judge > h3 {
  font-weight: bold;
  margin-bottom: 20px;
}
.pro_judge .judge_erro_tip {
  font-size: 15px;
  font-weight: bolder;
  color: #000;
}
.pro_detail > .pro_img {
  float: left;
  position: relative;
  padding: 100px 0;
  width: 480px;
  border: 1px solid #ccc;
}
.pro_img > .tb_booth {
  position: relative;
  z-index: 1;
}
.tb_booth > .pro_middle_img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
}
.pro_detail > .pro_meg {
  margin: 0 0 0 520px;
  color: #666;
  padding: 0 0 3px;
}
.pro_meg > .pro_meg_hd {
  padding: 0 10px 12px;
  color: #000;
}
.pro_meg_hd > h1 {
  font-size: 16px;
  font-weight: 700;
  color: #000;
}
.pro_meg > .pro_meg_price {
  padding: 5px 20px;
  height: 200px;
  background-color: rgba(247, 244, 244, 0.6);

  display: flex;
  flex-direction: column;
  justify-content: space-around;
}
.pro_meg_price dl {
  display: flex;
  align-items: center;

  margin-bottom: 0 !important;
  cursor: pointer;
}
.pro_meg_price dl dt {
  width: 70px;
  color: #999;
  font-size: 12px;
}
.pro_meg_price dl dd {
  margin-bottom: 0 !important;
  font-family: Arial;
}
.pro_meg_price dl dd div {
  display: flex;
  align-items: center;
}
.pro_meg_price dl:last-child dd {
  color: #ff0036;
  font-weight: bold;
  font-size: 12px;
}
.promo_price {
  line-height: 24px;
  vertical-align: middle;
  color: #ff0036;
  font-size: 18px;
  font-family: Arial;
  -webkit-font-smoothing: antialiased;
}
.promo_price b {
  display: inline-block;
  font-weight: normal;
}
.promo_price b:last-child {
  font-size: 12px;
  background: #f47a86;
  color: white;
  padding: 0 6px;
}
.promo_price > .tm-price {
  font-size: 20px;
  display: inline-block;
  margin-right: 12px;
  font-weight: bold;
}
.nor_price {
  text-decoration: line-through;
}
.sale_tip {
  color: #ff0036 !important;
  font-weight: bolder;
  width: 80px !important;
}
.pro_meg_deliver {
  margin: 5px 20px -15px 0;
  padding: 5px;
}
.pro_meg_deliver dl {
  padding: 5px;
  font-size: 14px;
  color: black;
  cursor: pointer;
}
.pro_meg_deliver dl dt {
  color: #999;
  font-size: 14px;
  text-align: left;
  width: 69px;
  margin: 0 0 0 8px;
  float: left;
}
.pro_meg_deliver dl dd {
  font-size: 13px;
}
.pro_meg_console {
  margin: 5px 20px 5px 0;
  padding: 5px;
}
.tb-sku {
  padding: 5px;
  font-size: 14px;
  color: black;
  cursor: pointer;
}
.tb-sku dt {
  color: #999;
  font-size: 14px;
  text-align: left;
  width: 69px;
  margin: 0 0 0 8px;
  float: left;
}
.tb-sku dd {
  font-size: 13px;
}
.tb-sku dd div {
  display: inline-block;
  margin-right: 20px;
}
.item-amout {
  height: 25px;
}
.item-amout a {
  display: inline-block;
  height: 23px;
  width: 17px;
  border: 1px solid #e5e5e5;
  background: #f0f0f0;
  text-align: center;
  line-height: 23px;
  color: #444;
  cursor: pointer;
}
.item-amout a:hover {
  color: #f50;
  border-color: #f60;
}
.item-amout > .text_amount {
  width: 40px;
  height: 20px;
  text-align: center;
  display: inline-block;
}
.shopping_car {
  margin: 20px auto 0;

  display: flex;
  justify-content: center;
}
.shopping_car button {
  outline: none;
}
</style>

演示视频

基于Node和Vue的前后端分离商城系统智能客服

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
基于Vue.jsNode.js的点餐系统设计与实现如下: 1. 设计: a. 前端使用Vue.js框架进行开发,通过组件化的方式构建点餐系统的各个功能模块,如菜单展示、购物车、订单等。 b. 后端使用Node.js搭建服务器,并使用Express框架提供API接口,处理前端发送的请求,提供餐厅菜单、订单信息的获取和修改等功能。 c. 数据库采用MySQL来存储菜单、订单等相关信息,并通过Node.js连接进行数据的访问和修改。 2. 实现: a. 前端通过Vue Router实现不同页面之间的跳转,如首页、菜单页、购物车等。 b. 使用Vue组件库(如Element UI)来构建系统的UI界面,简化开发过程。 c. 通过Vue的请求库(如axios)向后端发送HTTP请求,获取菜单列表、订单信息等数据。 d. 用户可以在菜单页中浏览和选择菜品,并将选中的菜品加入购物车。 e. 在购物车页面,用户可以查看已选择的菜品列表,可以增加、删除或修改数量。 f. 当用户确认订单后,前端通过HTTP请求将订单信息发送给后端后端将订单信息保存到数据库中。 g. 后端通过数据库查询和修改接口,向前端提供菜单的展示、订单的查看和修改等功能。 基于Vue.jsNode.js的点餐系统设计与实现,通过前后端的分离架构,实现了用户友好的界面、高效的数据交互和维护的数据库。同时,还可以结合其他工具和技术,如Webpack进行打包和优化,实现更加完善的点餐系统

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值