【易售小程序项目】悬浮按钮+出售闲置商品+商品分类选择【后端基于若依管理系统开发】

简介: 【易售小程序项目】悬浮按钮+出售闲置商品+商品分类选择【后端基于若依管理系统开发】

界面效果

【悬浮按钮】

【闲置商品描述信息填写界面】

【商品分类选择界面】

【分类选择完成】

界面实现

悬浮按钮实现

悬浮按钮漂浮于页面之上,等页面滑动时,悬浮按钮的位置相对于屏幕不会改变

【悬浮按钮组件】

<template>
  <div class="floating-button" @click="onClick">
    <slot>
      <!-- 这里可以放置默认的按钮样式等 -->
    </slot>
  </div>
</template>
<script>
  export default {
    name: 'FloatButton',
    props: {
    },
    data() {
      return {
      };
    },
    mounted() {
    },
    methods: {
      onClick() {
        this.$emit('click');
      }
    },
    computed: {
    }
  };
</script>
<style>
  .floating-button {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 58px;
    height: 58px;
    border-radius: 50%;
    background-color: #007aff;
    color: #fff;
    position: fixed;
    right: 20rpx;
    bottom: 20rpx;
  }
  /* 按钮点击之后会产生偏移 */
  .floating-button:active {
    transform: translate(0, 2px);
  }
</style>

【在其他界面中使用】

因为组件中使用了插槽,可以在该组件中插入其他组件

<FloatButton @click="cellMyProduct()">
  <u--image :src="floatButtonPic" shape="circle" width="60px" height="60px"></u--image>
</FloatButton>

示例中,我给浮动按钮的插槽中添加了图片组件,图片使用项目静态资源中的图片

data() {
    return {
      title: 'Hello',
      floatButtonPic: require("@/static/cellLeaveUnused.png"),
    }
  },

商品分类选择界面

分类数据的格式如下

{
  "msg": "productCategoryItemVoList",
  "code": 200,
  "data": [
    {
      "id": 5,
      "name": "数码产品",
      "children": [
        {
          "id": 10,
          "name": "电脑",
          "children": [
            {
              "id": 12,
              "name": "台式机",
              "children": [
              ],
              "icon": "a",
              "sort": 1,
              "description": "a"
            },
            {
              "id": 13,
              "name": "笔记本",
              "children": [
              ],
              "icon": "a",
              "sort": 1,
              "description": "a"
            }
          ],
          "icon": "a",
          "sort": 1,
          "description": "a"
        },
        {
          "id": 11,
          "name": "手机",
          "children": [
            {
              "id": 14,
              "name": "老人机",
              "children": [
              ],
              "icon": "a",
              "sort": 1,
              "description": "a"
            },
            {
              "id": 15,
              "name": "智能手机",
              "children": [
              ],
              "icon": "a",
              "sort": 1,
              "description": "a"
            }
          ],
          "icon": "a",
          "sort": 1,
          "description": "a"
        }
      ],
      "icon": "a",
      "sort": 1,
      "description": "a"
    },
    {
      "id": 6,
      "name": "服装",
      "children": [
      ],
      "icon": "a",
      "sort": 1,
      "description": "a"
    },
    {
      "id": 7,
      "name": "教育用品",
      "children": [
      ],
      "icon": "a",
      "sort": 1,
      "description": "a"
    },
    {
      "id": 8,
      "name": "食品",
      "children": [
      ],
      "icon": "a",
      "sort": 1,
      "description": "a"
    }
  ]
}
<template>
  <view class="container">
    <u-toast ref="uToast"></u-toast>
    <view class="titleView">
      <view class="controlButton" @click="back">
        <u-icon name="arrow-left" color="#ffffff"></u-icon>
        上一级
      </view>
      <text>{{getCategoryLayerName()}}</text>
      <view class="controlButton" @click="commit">
        完成
        <u-icon name="checkmark" color="#ffffff"></u-icon>
      </view>
    </view>
    <view style="height: 20px;"></view>
    <u-empty v-if="curLayerCategoryData.length==0" mode="search" texColor="#ffffff" iconSize="180" iconColor="#2b92ff" text="分类选择完成,请点击右上角的完成" textColor="#2b92ff" textSize="18" marginTop="30">
    </u-empty>
    <u-list @scrolltolower="scrolltolower" v-else>
      <u-list-item v-for="(category, index) in curLayerCategoryData" :key="index">
        <u-cell :title="category.name" @click="selectCurCategory(category)">
          <u-avatar slot="icon" shape="square" size="35" :src="category.icon"
            customStyle="margin: -3px 5px -3px 0"></u-avatar>
        </u-cell>
      </u-list-item>
    </u-list>
  </view>
</template>
<script>
  import {
    getProductCategoryTree
  } from "@/api/market/category.js";
  export default {
    data() {
      return {
        categoryNameList: ["分类未选择"],
        categoryTreeData: [],
        // 当前层级分类数据
        curLayerCategoryData: [],
        // 已经选择的层级分类数据
        haveSelectLayerCategoryData: [],
        // 层级
        layer: 0,
        // 商品所属分类
        productCategoryId: 0,
      }
    },
    created() {
      this.getProductCategoryTree();
    },
    methods: {
      getCategoryLayerName() {
        let str = '';
        for (let i = 0; i < this.categoryNameList.length - 1; i++) {
          str += this.categoryNameList[i] + '/';
        }
        return str + this.categoryNameList[this.categoryNameList.length - 1];
      },
      /**
       * 查询商品分类的树形结构数据
       */
      getProductCategoryTree() {
        getProductCategoryTree().then(res => {
          // console.log("getProductCategoryTree:" + JSON.stringify(res));
          this.categoryTreeData = res.data;
          this.curLayerCategoryData = this.categoryTreeData;
        })
      },
      /**
       * 选择分类
       * @param {Object} category 当前选择的分类
       */
      selectCurCategory(category) {
        if (this.layer == 0) {
          this.categoryNameList = [];
        }
        this.categoryNameList.push(category.name);
        this.productCategoryId = category.id;
        this.layer++;
        // 将当前层的数据设置进haveSelectLayerCategoryData
        this.haveSelectLayerCategoryData.push(this.curLayerCategoryData);
        this.curLayerCategoryData = category.children;
        if (this.curLayerCategoryData.length == 0) {
          this.$refs.uToast.show({
            type: 'success',
            message: "分类选择完成,请提交数据"
          })
        }
      },
      /**
       * 返回上一级
       */
      back() {
        if (this.layer == 0) {
          this.$refs.uToast.show({
            type: 'warning',
            message: "已经是第一层级,无法返回上一级"
          })
        } else {
          this.layer--;
          this.curLayerCategoryData = this.haveSelectLayerCategoryData[this.haveSelectLayerCategoryData.length -
            1];
          // 删掉最后一条数据
          this.haveSelectLayerCategoryData.splice(this.haveSelectLayerCategoryData.length - 1, 1);
        }
      },
      /**
       * 提交分类数据
       */
      commit() {
        if (this.curLayerCategoryData.length != 0) {
          this.$refs.uToast.show({
            type: 'error',
            message: "分类还没有选择完成,请继续选择"
          })
        } else {
          uni.setStorageSync("productCategoryId", this.productCategoryId);
          uni.setStorageSync("categoryNameList", this.categoryNameList);
          uni.navigateBack();
        }
      }
    }
  }
</script>
<style lang="scss">
  .container {
    background: #F6F6F6;
    min-height: 100vh;
    padding: 20rpx;
    .titleView {
      display: flex;
      justify-content: space-between;
      align-items: center;
      background: #2b92ff;
      color: #ffffff;
      border-radius: 4px;
      .controlButton {
        // width: 100px;
        display: flex;
        // border: #2b92ff 1px solid;
        padding: 10px;
      }
    }
  }
</style>

使元素均匀分布

使用下面的代码,可以让元素在组件中的子组件在组件中横向均匀分布,效果如下图

<view class="titleView">
  <view class="controlButton" @click="back">
    <u-icon name="arrow-left" color="#ffffff"></u-icon>
    上一级
  </view>
  <text>{{getCategoryLayerName()}}</text>
  <view class="controlButton" @click="commit">
    完成
    <u-icon name="checkmark" color="#ffffff"></u-icon>
  </view>
</view>
display: flex;
justify-content: space-between;

闲置商品描述信息填写界面

<template>
  <view class="container">
    <u-toast ref="uToast"></u-toast>
    <view class="content">
      <view class="item">
        <view class="labelName">商品名称</view>
        <u--input placeholder="请输入商品名称" border="surround" v-model="product.name"></u--input>
      </view>
      <u-divider text="商品描述和外观"></u-divider>
      <!-- 商品描述 -->
      <u--textarea v-model="product.descripption" placeholder="请输入商品描述" height="150"></u--textarea>
      <!-- 图片上传 -->
      <view>
        <imageUpload v-model="product.picList" maxCount="9"></imageUpload>
      </view>
      <u-divider text="分类选择/自定义标签"></u-divider>
      <!-- 分类选择/自定义标签 -->
      <view class="item">
        <view class="labelName">分类</view>
        <view class="selectTextClass" @click="selectCategory">{{getCategoryLayerName()}}</view>
      </view>
      <!-- 商品的属性 新度 功能完整性 -->
      <view class="item">
        <view class="labelName">成色</view>
        <view class="columnClass">
          <view :class="product.fineness==index?'selectTextClass':'textClass'"
            v-for="(finessName,index) in finenessList" :key="index" @click="changeFineness(index)">
            {{finessName}}
          </view>
        </view>
      </view>
      <view class="item">
        <view class="labelName">功能状态</view>
        <view class="columnClass">
          <view :class="product.functionalStatus==index?'selectTextClass':'textClass'"
            v-for="(functionName,index) in functionList" :key="index"
            @click="changeFunctionalStatus(index)">{{functionName}}
          </view>
        </view>
      </view>
      <u-row customStyle="margin-bottom: 10px">
        <u-col span="5">
          <view class="item">
            <view class="labelName">数量</view>
            <u--input placeholder="请输入商品数量" border="surround" v-model="product.number"></u--input>
          </view>
        </u-col>
        <u-col span="7">
          <view class="item">
            <view class="labelName">计量单位</view>
            <u--input placeholder="请输入计量单位" border="surround" v-model="product.unit"></u--input>
          </view>
        </u-col>
      </u-row>
      <!-- 价格 原价 现价 -->
      <u-divider text="价格"></u-divider>
      <u-row customStyle="margin-bottom: 10px">
        <u-col span="6">
          <view class="item">
            <view class="labelName">原价</view>
            <u-input placeholder="请输入原价" border="surround" v-model="product.originalPrice" color="#ff0000"
              @blur="originalPriceChange">
              <u--text text="¥" slot="prefix" margin="0 3px 0 0" type="error"></u--text>
            </u-input>
          </view>
        </u-col>
        <u-col span="6">
          <view class="item">
            <view class="labelName">出售价格</view>
            <u-input placeholder="请输入出售价格" border="surround" v-model="product.price" color="#ff0000"
              @blur="priceChange">
              <u--text text="¥" slot="prefix" margin="0 3px 0 0" type="error"></u--text>
            </u-input>
          </view>
        </u-col>
      </u-row>
      <u-button text="出售" size="large" type="primary" @click="uploadSellProduct"></u-button>
    </view>
  </view>
</template>
<script>
  import imageUpload from "@/components/ImageUpload/ImageUpload.vue";
  import {
    uploadSellProduct
  } from "@/api/market/prodct.js"
  export default {
    components: {
      imageUpload
    },
    onShow: function() {
      let categoryNameList = uni.getStorageSync("categoryNameList");
      if (categoryNameList) {
        this.categoryNameList = categoryNameList;
        this.product.productCategoryId = uni.getStorageSync("productCategoryId");
        uni.removeStorageSync("categoryNameList");
        uni.removeStorageSync("productCategoryId");
      }
    },
    data() {
      return {
        product: {
          name: '',
          descripption: '',
          picList: [],
          productCategoryId: 0,
          number: 1,
          unit: '',
          isContribute: 0,
          originalPrice: 0.00,
          price: 0.00,
          // 成色
          fineness: 0,
          // 功能状态
          functionalStatus: 0,
          brandId: 0
        },
        value: 'dasdas',
        categoryNameList: ["选择分类"],
        finenessList: ["全新", "几乎全新", "轻微使用痕迹", "明显使用痕迹", "外观破损"],
        functionList: ["功能完好无维修", "维修过,可正常使用", "有小问题,不影响使用", "无法正常使用"]
      }
    },
    methods: {
      getCategoryLayerName() {
        let str = '';
        for (let i = 0; i < this.categoryNameList.length - 1; i++) {
          str += this.categoryNameList[i] + '/';
        }
        return str + this.categoryNameList[this.categoryNameList.length - 1];
      },
      /**
       * 价格校验
       * @param {Object} price 价格
       */
      priceVerify(price) {
        if (isNaN(price)) {
          this.$refs.uToast.show({
            type: 'error',
            message: "输入的价格不是数字,请重新输入"
          })
          return false;
        }
        if (price < 0) {
          this.$refs.uToast.show({
            type: 'error',
            message: "输入的价格不能为负数,请重新输入"
          })
          return false;
        }
        if (price.toString().indexOf('.') !== -1 && price.toString().split('.')[1].length > 2) {
          this.$refs.uToast.show({
            type: 'error',
            message: "输入的价格小数点后最多只有两位数字,请重新输入"
          })
          return false;
        }
        return true;
      },
      originalPriceChange() {
        let haha = this.priceVerify(this.product.originalPrice);
        if (haha === false) {
          console.log("haha:" + haha);
          this.product.originalPrice = 0.00;
          console.log("this.product" + JSON.stringify(this.product));
        }
      },
      priceChange() {
        if (this.priceVerify(this.product.price) === false) {
          this.product.price = 0.00;
        }
      },
      /**
       * 修改成色
       * @param {Object} index
       */
      changeFineness(index) {
        this.product.fineness = index;
      },
      /**
       * 修改功能状态
       * @param {Object} index
       */
      changeFunctionalStatus(index) {
        this.product.functionalStatus = index;
      },
      /**
       * 上传闲置商品
       */
      uploadSellProduct() {
        uploadSellProduct(this.product).then(res => {
          this.$refs.uToast.show({
            type: 'success',
            message: "您的商品已经发布到平台"
          })
          setTimeout(() => {
            uni.reLaunch({
              url: "/pages/index/index"
            })
          }, 1500)
        })
      },
      /**
       * 选择分类
       */
      selectCategory() {
        uni.navigateTo({
          url: "/pages/sellMyProduct/selectCategory"
        })
      }
    }
  }
</script>
<style lang="scss">
  .container {
    background: #F6F6F6;
    min-height: 100vh;
    padding: 20rpx;
    .content {
      background: #ffffff;
      padding: 20rpx;
      .item {
        display: flex;
        align-items: center;
        height: 50px;
        margin-bottom: 5px;
        .labelName {
          width: 70px;
          margin-right: 10px;
        }
        .textClass {
          display: inline;
          background: #F7F7F7;
          padding: 10px;
          margin-right: 15px;
          border-radius: 5px;
        }
        .selectTextClass {
          display: inline;
          background: #2B92FF;
          padding: 10px;
          margin-right: 15px;
          border-radius: 5px;
          color: #ffffff;
          font-weight: bold;
        }
        .columnClass {
          // height: 50px;
          display: flex;
          align-items: center;
          width: calc(100% - 70px);
          overflow-x: auto;
          // // 让内容只有一行
          white-space: nowrap;
        }
        .columnClass::-webkit-scrollbar {
          background-color: transparent;
          /* 设置滚动条背景颜色 */
          // width: 0px;
          height: 0px;
        }
      }
    }
  }
</style>

价格校验

价格是商品比较关键的属性,一定要确保其数据没有问题,所以在用户提交之前一定要对商品的价格进行校验,防止用户乱输或者输错数据,这里对价格有如下规定:

  • 输入的价格必须是数字,不可以是字符串
  • 输入的价格必须是正数,不可以是负数
  • 输入的价格的小数点有限制,不可以输入太多小数点

那么校验应该在什么时候触发呢?本示例在用户输入结束之后,手指离开输入组件时触发,即当元素失去焦点时触发,使用的是@blur事件

/**
 * 价格校验
* @param {Object} price 价格
 */
priceVerify(price) {
  if (isNaN(price)) {
    this.$refs.uToast.show({
      type: 'error',
      message: "输入的价格不是数字,请重新输入"
    })
    return false;
  }
  if (price < 0) {
    this.$refs.uToast.show({
      type: 'error',
      message: "输入的价格不能为负数,请重新输入"
    })
    return false;
  }
  if (price.toString().indexOf('.') !== -1 && price.toString().split('.')[1].length > 2) {
    this.$refs.uToast.show({
      type: 'error',
      message: "输入的价格小数点后最多只有两位数字,请重新输入"
    })
    return false;
  }
  return true;
},


目录
相关文章
|
6天前
|
存储 SQL API
探索后端开发:构建高效API与数据库交互
【10月更文挑战第36天】在数字化时代,后端开发是连接用户界面和数据存储的桥梁。本文深入探讨如何设计高效的API以及如何实现API与数据库之间的无缝交互,确保数据的一致性和高性能。我们将从基础概念出发,逐步深入到实战技巧,为读者提供一个清晰的后端开发路线图。
|
5天前
|
JSON 前端开发 API
后端开发中的API设计与文档编写指南####
本文探讨了后端开发中API设计的重要性,并详细阐述了如何编写高效、可维护的API接口。通过实际案例分析,文章强调了清晰的API设计对于前后端分离项目的关键作用,以及良好的文档习惯如何促进团队协作和提升开发效率。 ####
|
7天前
|
存储 SQL 数据库
深入浅出后端开发之数据库优化实战
【10月更文挑战第35天】在软件开发的世界里,数据库性能直接关系到应用的响应速度和用户体验。本文将带你了解如何通过合理的索引设计、查询优化以及恰当的数据存储策略来提升数据库性能。我们将一起探索这些技巧背后的原理,并通过实际案例感受优化带来的显著效果。
25 4
|
6天前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端开发
【10月更文挑战第36天】本文将引导您探索Node.js的世界,通过实际案例揭示其背后的原理和实践方法。从基础的安装到高级的异步处理,我们将一起构建一个简单的后端服务,并讨论如何优化性能。无论您是新手还是有经验的开发者,这篇文章都将为您提供新的视角和深入的理解。
|
7天前
|
监控 API 持续交付
后端开发中的微服务架构实践与挑战####
本文深入探讨了微服务架构在后端开发中的应用,分析了其优势、面临的挑战以及最佳实践策略。不同于传统的单体应用,微服务通过细粒度的服务划分促进了系统的可维护性、可扩展性和敏捷性。文章首先概述了微服务的核心概念及其与传统架构的区别,随后详细阐述了构建微服务时需考虑的关键技术要素,如服务发现、API网关、容器化部署及持续集成/持续部署(CI/CD)流程。此外,还讨论了微服务实施过程中常见的问题,如服务间通信复杂度增加、数据一致性保障等,并提供了相应的解决方案和优化建议。总之,本文旨在为开发者提供一份关于如何在现代后端系统中有效采用和优化微服务架构的实用指南。 ####
|
9天前
|
消息中间件 设计模式 运维
后端开发中的微服务架构实践与挑战####
本文深入探讨了微服务架构在现代后端开发中的应用,通过实际案例分析,揭示了其在提升系统灵活性、可扩展性及促进技术创新方面的显著优势。同时,文章也未回避微服务实施过程中面临的挑战,如服务间通信复杂性、数据一致性保障及部署运维难度增加等问题,并基于实践经验提出了一系列应对策略,为开发者在构建高效、稳定的微服务平台时提供有价值的参考。 ####
|
9天前
|
存储 关系型数据库 Java
探索后端开发:从基础到进阶
【10月更文挑战第33天】在这篇文章中,我们将深入探讨后端开发的各个方面,包括基本概念、关键技术和最佳实践。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供有价值的信息和启示。我们将通过代码示例来展示一些常见任务的实现方法,并分享一些实用的技巧和策略,帮助你提高后端开发的效率和质量。无论你是想学习新的编程语言还是想了解最新的后端技术趋势,这篇文章都会为你提供有益的指导和灵感。让我们一起开启后端开发的探索之旅吧!
|
5天前
|
缓存 前端开发 API
探索后端开发中的API设计原则
【10月更文挑战第37天】本文旨在引导读者理解API设计的核心理念,通过简明的语言和直观的示例,揭示如何构建高效、稳定且易于维护的后端接口。我们将深入浅出地探讨RESTful API的设计规范,并通过一个简易的代码样例,展示如何在实战中应用这些原则。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的参考和启示。
|
9天前
|
Web App开发 JavaScript 前端开发
探索后端开发:Node.js与Express的完美结合
【10月更文挑战第33天】本文将带领读者深入了解Node.js和Express的强强联手,通过实际案例揭示它们如何简化后端开发流程,提升应用性能。我们将一起探索这两个技术的核心概念、优势以及它们如何共同作用于现代Web开发中。准备好,让我们一起开启这场技术之旅!
24 0
|
9天前
|
存储 Java 关系型数据库
探索后端开发:从基础到进阶的旅程
【10月更文挑战第33天】在这篇文章中,我们将一起踏上一段激动人心的旅程,深入探索后端开发的奥秘。我们将从最基本的概念开始,逐步深入,直到能够理解并应用高级后端技术。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的见解和知识。让我们一起开始这段旅程吧!

热门文章

最新文章