springboot vue 开源 会员收银系统 (6) 收银台的搭建

news2024/11/25 5:44:17

前言

完整版演示

前面我们对会员系统 分类和商品的开发 完成了收银所需的基础信息 下面我们开始完成收银台的开发

简单画了一个收银的流程图大家参考下
收银流程图

从这张图我们可以分析一下几点

  • 可以选择会员或散客收银
  • 选择会员使用相应的会员价结算
  • 使用会员卡则在价格基础根据卡折扣结算

根据上述分析我搭建了一个简单的收银台
收银台

  • 左边显示会员和已选择的商品信息
  • 右边显示商品的价格信息
  • 商品选择后可以修改数字
  • 总金额自动计算
  • 后续完成挂单功能便于保证收银连续性

开发中运用的组件

  1. Tabs 标签页 和 Card 完成右侧的商品选择
  2. el-autocomplete 完成会员的检索
  3. InputNumber Table 完成购物车开发

收银台运用过多组件会导致页面很乱 我们后续需要进行组件的封装

<template>
  <div class="app-container"  style="border-box:box-sizing;">

<!--    left-->
    <el-row :gutter="30" style="">
      <el-col :span="10">

        <el-autocomplete
          style="width: 100%"
          popper-class="my-autocomplete"
          v-model="querySearchStr"
          :fetch-suggestions="querySearch"
          placeholder="会员检索"
          @select="handleSelect">
          <i
            class="el-icon-search el-input__icon"
            slot="suffix"
           >
          </i>
          <template slot-scope="{ item }">
            <div v-if="item.errorMsg">
              {{item.errorMsg}}
            </div>

            <template v-else>
              <div class="name">
                <i class="el-icon-user"></i>
                {{ item.memberName }}
              </div>
              <span class="addr">
                <i class="el-icon-phone"></i>
                {{ item.memberPhone }}
              </span>
            </template>
          </template>
        </el-autocomplete>

        <el-descriptions  border title="用户信息" v-if="member.memberId" style="margin-top: 20px;margin-bottom: 20px;">
          <el-descriptions-item label="用户名">{{member.memberName}}</el-descriptions-item>
          <el-descriptions-item label="手机号">{{member.memberPhone}}</el-descriptions-item>
          <el-descriptions-item label="备注">
             {{member.remark}}
          </el-descriptions-item>
        </el-descriptions>
        <el-descriptions  border title="用户信息" v-else style="margin-top: 20px;margin-bottom: 20px;">
          <el-descriptions-item label="用户名">散客</el-descriptions-item>
        </el-descriptions>
        <div class="grid-content bg-purple">
          <el-table

            border
            height="400"
            max-height="400"
            :data="shopCart"
            style="width: 100%;margin-bottom: 20px;">

        <el-table-column
          align="center"
          prop="categoryName"
          label="分类"
          width="100">
        </el-table-column>

        <el-table-column
          align="center"
          prop="productName"
          label="商品名"
          >
        </el-table-column>

        <el-table-column
          align="center"
          prop="productAmount"
          label="价格"
          width="100"
        >
          <template slot-scope="scope">
            <span v-if="app.isEmpty(member.memberId)" class="price">{{ scope.row.productAmount }}</span>
            <span v-else="member.memberId" class="price">{{ scope.row.productMemberAmount }}</span>
          </template>
        </el-table-column>

        <el-table-column
          align="center"
          prop="count"
          label="数量"
          width="200"
        >
          <template slot-scope="scope">
            <el-input-number size="mini" :min="0" :max="100" v-model="scope.row.count" @change="handleChange($event,scope)" ></el-input-number>
          </template>
        </el-table-column>

         <el-table-column
           align="center"
          prop="totalPrice"
          label="总金额"
          width="100"
         >
           <template slot-scope="scope">
             <span class="price">{{ scope.row.totalPrice }}</span>
           </template>
        </el-table-column>
      </el-table>
          <el-divider></el-divider>
          总金额: <span class="price">{{app.getFloatStr(getTotalPrice)}}</span>
          <el-divider></el-divider>

          <el-row>
            <el-button type="success">结算</el-button>
            <el-button type="warning">挂单</el-button>
            <el-button type="primary">取单</el-button>
          </el-row>
      </div>
      </el-col>
<!--right-->
      <el-col :span="14">
        <div class="grid-content bg-purple">
        <el-tabs  v-model="activeName" @tab-click="handleClick" type="border-card" style="height: calc(100vh - 180px);width: 100%;overflow: scroll;overflow:hidden;">
          <el-tab-pane
            v-for="(item, index) in categoryList"
            :key="item.categoryId"
            :label="item.categoryName"
            :name="item.categoryId"
          >
            <div>
                <el-row :gutter="20" style="" v-if="productList.length>0">
                <el-col @click.native="chooseProduct(item)" :span="6" v-for="item in productList" :key="item.productId" style="margin-top: 20px;cursor: pointer;">
                  <el-card :body-style="{ padding: '0px' }" style="height: 230px;">

                    <div style="height: 150px;display: flex;align-items: center;justify-content: center; 	box-sizing: border-box;padding-top: 10px;">
                      <img style="width:85%;height: 140px;border-radius: 5px;border: 1px solid #eee;" :src="baseUrl  + item.productImageUrl" class="image"/>
                    </div>
                    <div style="padding:10px 0 0 10px;font-size: 16px;">
                      {{item.productName}}
                    </div>
                    <div style="padding:5px 0 0 10px;">
                      <span style="font-size: 14px;"> 散客价 <span class="price">¥ {{item.productAmount}}</span></span><br/>
                      <span style="font-size: 14px;"> 会员价 <span class="price">¥ {{item.productMemberAmount}}</span></span>
                    </div>

                  </el-card>
                </el-col>
              </el-row>
              <div v-else>
                <el-empty description="该分类商品为空">
                </el-empty>
                </div>
            </div>
          </el-tab-pane>
        </el-tabs>
        </div>
      </el-col>
    </el-row>
  </div>
</template>

<script>
    import {accAdd, accMul,isEmpty} from "@/utils";
    import confirm from "@/utils/confirm";
    import {getCategoryList} from "@/api/business/category/category";
    import {getProductList} from "@/api/business/product/product";
    import {getMemberList} from "@/api/business/member/member";

    export default {
        name: "cashierDesk",
        data(){
          return{
            shopCart: [],
            categoryList: [],
            productList: [],
            activeName: "",
            member: {},
            querySearchStr: '',
          }
        },
      mounted() {
          this.getCategoryList()
      },
      computed:{
        getTotalPrice(){
          let totalPrice = 0;
          for(let product of this.shopCart){
            const noMember = isEmpty(this.member.memberId)
            let productTotalPrice
            if(noMember){
               productTotalPrice = accMul(product.productAmount,product.count)
            }else{
              productTotalPrice = accMul(product.productMemberAmount,product.count)
            }

            this.$set(product, 'totalPrice', productTotalPrice)
            totalPrice = accAdd(totalPrice,productTotalPrice);
          }
          return totalPrice;
        }
      },
      watch:{
        shopCart(newVal,oldVal){
          let showCardList = newVal
          console.log('这是监听属性新的')
        }
      },
      methods:{
        querySearch(queryString, cb) {
            if(queryString){
              getMemberList({querySearch:queryString}).then(res=>{
                // 调用 callback 返回建议列表的数据
                cb(res.data);
              })
            }else{
              cb([
                {errorMsg:'暂无数据'}
              ])
            }

          },
          getCategoryList(){
            getCategoryList().then(res=>{
              this.categoryList = res.data
              if(this.categoryList.length>0){
                this.activeName = this.categoryList[0].categoryId
                getProductList({categoryId:this.activeName }).then(res=>{
                  this.productList = res.data
                })
              }
            })
          },
         handleClick(e){
            //拿到分类id
           const categoryId = e.name
           getProductList({categoryId:categoryId}).then(res=>{
             this.productList = res.data
           })
         },
        handleChange(e,scope){
          console.log(e)
          console.log(scope.$index)
          if(e==0){
            confirm('要删除该条目吗?').then(res=>{
              if(res){
                this.shopCart.splice(scope.$index,1)
              }else{
                this.shopCart[scope.$index].count = 1
              }
            })
          }
        },
        chooseProduct(e){
            let product = e;
            let findProductIndex = this.shopCart.findIndex(p => p.productId ==  product.productId);
            console.log(findProductIndex)
            if(findProductIndex == -1){
              this.$set(product, 'count', 1)
              this.shopCart.push(product)
            }else{
              this.shopCart[findProductIndex].count += 1
            }
            console.log(this.shopCart)
        },
        handleSelect(e){
          console.log(e)
          this.member = e
        }
      }
    }
</script>

<style scoped>
  ::v-deep .el-tabs--border-card>.el-tabs__header .el-tabs__item{
    height: 50px;
    line-height: 50px;
    font-size: 14px;
  }

  .price{
    font-weight: bold;
    color: #ff5b57;
  }
</style>

代码地址
https://gitee.com/ddeatrr/memberShop
然后为了大家预览我把开发板也部署了 大家可以参考
http://store.gbadd.space/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1795946.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

sql server:数据库处于单用户模式,当前某个用户已与其连接

在 SQL Server 中&#xff0c;数据库可以设置为不同的用户模式&#xff0c;以便根据需要限制对数据库的访问。单用户模式&#xff08;Single-User Mode&#xff09;是其中一种模式&#xff0c;它限制了对数据库的访问&#xff0c;使得一次只能有一个用户连接到数据库。 单用户…

七月份大理站、ACM独立出版、高录用稳检索,2024年云计算与大数据国际学术会议(ICCBD 2024)

【ACM独立出版 | 高录用 | EI核心检索稳定】 2024年云计算与大数据国际学术会议&#xff08;ICCBD 2024) 2024 International Conference on Cloud Computing and Big Data (ICCBD 2024) 一、重要信息 大会官网&#xff1a;www.iccbd.net &#xff08;点击投稿/参会/了解会…

Vue2项目错误提示:Vue: <template v-for> key should be placed on the <template> tag.

1. 场景还原 升级了最新的Webstorm后打开Vue2项目提示以下波浪线错误&#xff1a; Vue: <template v-for> key should be placed on the <template> tag. 该错误不会影响正常运行和构建&#xff0c;但我们看到了会不舒服。 2. 错误原因 Vue2中key不能放在temp…

[数据集][图像分类]城市异常情况路边倒树火灾水灾交通事故分类数据集15223张8类别

数据集类型&#xff1a;图像分类用&#xff0c;不可用于目标检测无标注文件 数据集格式&#xff1a;仅仅包含jpg图片&#xff0c;每个类别文件夹下面存放着对应图片 图片数量(jpg文件个数)&#xff1a;15223 分类类别数&#xff1a;8 类别名称:[“badroad”,“fallentree”,“f…

四、 【源码】数据源的解析、创建和使用

源码地址&#xff1a;https://github.com/mybatis/mybatis-3/ 仓库地址&#xff1a;https://gitcode.net/qq_42665745/mybatis/-/tree/04-datasource-use 数据源的解析、创建和使用 流程&#xff1a; 1.Resources加载MyBatis配置文件生成Reader字符流 2.SqlSessionFactory…

[ZJCTF 2019]NiZhuanSiWei、[HUBUCTF 2022 新生赛]checkin、[SWPUCTF 2021 新生赛]pop

目录 [ZJCTF 2019]NiZhuanSiWei [HUBUCTF 2022 新生赛]checkin 1.PHP 关联数组 PHP 数组 | 菜鸟教程 2.PHP 弱比较绕过 PHP 类型比较 | 菜鸟教程 [SWPUCTF 2021 新生赛]pop [ZJCTF 2019]NiZhuanSiWei BUUCTF [ZJCTF 2019]NiZhuanSiWei特详解&#xff08;php伪…

推荐个 Edge/Chrome/Firefox 都支持的 IP 定位查询扩展

作为一个博客站长&#xff0c;对 IP 地址应该都不陌生&#xff0c;可以说是跟站长的工作是息息相关的&#xff0c;反正明月几乎每天都会面临 IP 查询、定位的需要&#xff0c;今天让明月给找到了一个叫”IP 定位查询“的浏览器扩展&#xff0c;在 Edge 和 Firefox 下体验后感觉…

新奇css模板

引言 (csscoco.com)https://csscoco.com/inspiration/#/./init 可视化集合 (hepengwei.cn)http://hepengwei.cn/#/html/visualDesign 30 秒代码 (30secondsofcode.org)https://www.30secondsofcode.org/ Animate.css |CSS动画的跨浏览器库。https://animate.style/

springboot+vue+mybatis房屋租贷系统+PPT+论文+讲解+售后

本论文系统地描绘了整个网上房屋租赁系统的设计与实现&#xff0c;主要实现的功能有以下几点&#xff1a;管理员&#xff1b;首页、个人中心、房屋类型管理、房屋租赁管理、会员管理、订单信息管理、合同信息管理、退房评价管理、管理员管理&#xff0c;系统管理&#xff0c;前…

基于SSM前后端分离版本的论坛系统-自动化测试

目录 前言 一、测试环境 二、环境部署 三、测试用例 四、执行测试 4.1、公共类设计 创建浏览器驱动对象 测试套件 释放驱动类 4.2、功能测试 注册页面 登录页面 版块 帖子 用户个人中心页 站内信 4.3、界面测试 注册页面 登录页面 版块 帖子 用户个人中心页…

【Python】教你彻底了解 Python中的文件处理

​​​​ 文章目录 一、文件的打开与关闭1. 打开文件2. 关闭文件3. 文件模式 二、文件的读写操作1. 读取文件内容2. 写入文件内容 三、使用上下文管理器四、异常处理五、二进制文件操作1. 读取二进制文件2. 写入二进制文件 六、实际应用示例1. 处理CSV文件2. 处理JSON文件 结论…

如何学习使用淘宝API?淘宝API运营场景

学习使用淘宝API涉及对其功能、分类、调用方法及实际应用的综合理解。下面按部分详细解释如何系统地学习和掌握淘宝API的使用&#xff1a; 淘宝API接口入门 了解淘宝开放平台&#xff1a;淘宝开放平台为开发者提供了一个可以与淘宝数据进行交互的平台&#xff0c;涵盖了丰富的A…

信息学奥赛初赛天天练-21-完善程序-动态规划、编辑距离与字符数组应用的极致探索

PDF文档公众号回复关键字:20240606 1 2023 CSP-J 完善程序2 完善程序&#xff08;单选题&#xff0c;每小题 3 分&#xff0c;共计 30 分&#xff09; 给定两个字符串&#xff0c;每次操作可以选择删除&#xff08;Delete&#xff09;、插入&#xff08;Insert&#xff09;…

NextJs 实现自定义点火操作

NextJs 实现自定义点火操作 前言实现自定义点火 前言 我希望在Nextjs 启动的时候&#xff0c;能够自定义实现一些项目的初始化逻辑&#xff0c;也可以说是一些点火操作&#xff0c;比如资源的加载&#xff0c;数据的初始化等操作。 实现自定义点火 我们可以在根目录下创建一…

MySQL 存储过程(二)

本篇继续介绍MySQL存储过程的相关内容。 目录 一、if语句 二、case 三、循环语句 while loop repeat 一、if语句 在存储过程中&#xff0c;可以使用if语句进行条件判断&#xff0c;其语法结构如下&#xff1a; if 判断语句 then 逻辑语句..... elseif 判断语句 then 逻…

Vulnhub-DC-2

靶机IP:192.168.20.135 网络有问题的可以看下搭建Vulnhub靶机网络问题(获取不到IP) kaliIP:192.168.20.128 扫描靶机端口及服务版本 发现开放了80和7744端口 并且是wordpress建站 dirsearch扫描目录 访问前端界面&#xff0c;发现存在重定向 在hosts文件中增加192.168.2…

英伟达再创历史,市值超越苹果,跃居全球第二大上市公司

进入2024年&#xff0c;英伟达股价依然突飞猛进。 今天凌晨&#xff0c;英伟达凭借其在AI领域强劲的创新能力和市场势头&#xff0c;达成了历史性的里程碑——市值首次突破3万亿美元&#xff0c;成功超越苹果&#xff0c;成为全球市值第二大上市公司。 排名仅次于微软。 英伟达…

[职场] 项目助理需具备的能力素养有哪些? #笔记#知识分享#经验分享

项目助理需具备的能力素养有哪些&#xff1f; 项目助理是一个关键的职位&#xff0c;他们在项目的执行过程中扮演着重要的角色。为了胜任这个职位&#xff0c;项目助理需要具备一系列的能力素养。以下是项目助理需具备的能力素养的详细介绍。 1. 组织能力&#xff1a;项目助理需…

OpenCV学习(4.4) 平滑图像

1.目的 在本教程中将学习&#xff1a; 用各种低通滤波器模糊图像。对图像应用自定义过滤器&#xff08;二维卷积&#xff09;。 在图像处理中&#xff0c;平滑图像是一种去噪和模糊技术&#xff0c;用于减少图像中的噪声和细节&#xff0c;使得图像看起来更加平滑。平滑处理…

泛型基础及深入

泛型深入 泛型定义&#xff1a; JDK5引入的特性&#xff0c;可以在编译阶段约束操作的数据类型&#xff0c;并进行检查 泛型格式&#xff1a; <数据类型> 注意&#xff1a;泛型只能支持引用数据类型 优势&#xff1a; 统一数据类型&#xff1b; 把运行时期的问题提前到…