【易售小程序项目】小程序首页(展示商品、商品搜索、商品分类搜索)【后端基于若依管理系统开发】

简介: 【易售小程序项目】小程序首页(展示商品、商品搜索、商品分类搜索)【后端基于若依管理系统开发】

界面效果

【说明】

  • 界面中商品的图片来源于闲鱼,若侵权请联系删除
  • 关于商品分类页面的实现,请在我的Uniapp系列文章中寻找来查看
  • 关于页面中悬浮按钮的实现,请在我的Uniapp系列文章中寻找来查看

界面实现

工具js

该工具类的作用是,给定一个图片的url地址,计算出图片的高宽比,计算高宽比的作用是让图片可以按照正常比例显示

/**
 * 获取uuid
 */
export default {
  /**
   * 获取高宽比 乘以 100%
   */
  getAspectRatio(url) {
    uni.getImageInfo({
      src: url,
      success: function(res) {
        let aspectRatio = res.height * 100.0 / res.width;
        // console.log("aspectRatio:" + aspectRatio);
        return aspectRatio + "%";
      }
    });
  },
}

页面

首页

<template>
  <view class="content">
    <view style="display: flex;align-items: center;">
      <u-search placeholder="请输入商品名称" v-model="searchForm.keyword" @search="listProductVo" :showAction="false"
        :clearabled="true"></u-search>
      <text class="iconfont" style="font-size: 35px;" @click="selectCategory()">&#xe622;</text>
    </view>
    <u-row customStyle="margin-top: 10px" gutter="10" align="start" v-if="productList[0].length>0&&loadData==false">
      <u-col span="6" class="col" v-for="(data,index) in productList" :key="index">
        <view class="productVoItem" v-for="(productVo,index1) in data" :key="index1"
          @click="seeProductDetail(productVo)">
          <u--image v-if="productVo.picList!=null&&productVo.picList.length>0" :showLoading="true"
            :src="productVo.picList[0]" width="100%" :height="getAspectRatio(productVo.picList[0])"
            radius="10" mode="widthFix"></u--image>
          <!-- <u--image v-else :showLoading="true" :src="src" @click="click"></u--image> -->
          <view class="productMes">
            <text class="productName">【{{productVo.name}}】</text>
            <text>
              {{productVo.description==null?'':productVo.description}}
            </text>
          </view>
          <view style="display: flex;align-items: center;">
            <!-- 现价 -->
            <view class="price">¥<text class="number">{{productVo.price}}</text>/{{productVo.unit}}</view>
            <view style="width: 10px;"></view>
            <!-- 原价 -->
            <view class="originPrice">¥{{productVo.originalPrice}}/{{productVo.unit}}
            </view>
          </view>
          <view style="display: flex;align-items: center;">
            <u--image :src="productVo.avatar" width="20" height="20" shape="circle"></u--image>
            <view style="width: 10px;"></view>
            <view> {{productVo.nickname}}</view>
          </view>
        </view>
      </u-col>
    </u-row>
    <u-empty v-if="productList[0].length==0&&loadData==false" mode="data" texColor="#ffffff" iconSize="180"
      iconColor="#D7DEEB" text="所选择的分类没有对应的商品,请重新选择" textColor="#D7DEEB" textSize="18" marginTop="30">
    </u-empty>
    <view style="margin-top: 20px;" v-if="loadData==true">
      <u-skeleton :loading="true" :animate="true" rows="10"></u-skeleton>
    </view>
    <!-- 浮动按钮 -->
    <FloatButton @click="cellMyProduct()">
      <u--image :src="floatButtonPic" shape="circle" width="60px" height="60px"></u--image>
    </FloatButton>
  </view>
</template>
<script>
  import FloatButton from "@/components/FloatButton/FloatButton.vue";
  import {
    listProductVo
  } from "@/api/market/prodct.js";
  import pictureApi from "@/utils/picture.js";
  export default {
    components: {
      FloatButton
    },
    onShow: function() {
      let categoryNameList = uni.getStorageSync("categoryNameList");
      if (categoryNameList) {
        this.categoryNameList = categoryNameList;
        this.searchForm.productCategoryId = uni.getStorageSync("productCategoryId");
        this.searchForm.keyword = this.getCategoryLayerName(this.categoryNameList);
        uni.removeStorageSync("categoryNameList");
        uni.removeStorageSync("productCategoryId");
        this.listProductVo();
      }
    },
    data() {
      return {
        title: 'Hello',
        // 浮动按钮的图片
        floatButtonPic: require("@/static/cellLeaveUnused.png"),
        searchForm: {
          // 商品搜索关键词
          keyword: "",
          productCategoryId: undefined
        },
        productList: [
          [],
          []
        ],
        loadData: false,
      }
    },
    onLoad() {
    },
    created() {
      this.listProductVo();
    },
    methods: {
      /**
       * 查询商品vo集合
       */
      listProductVo() {
        this.loadData = true;
        listProductVo(this.searchForm).then(res => {
          this.loadData = false;
          // console.log("listProductVo:" + JSON.stringify(res))
          let productVoList = res.rows;
          this.productList = [
            [],
            []
          ];
          for (var i = 0; i < productVoList.length; i++) {
            if (i % 2 == 0) {
              // 第一列数据
              this.productList[0].push(productVoList[i]);
            } else {
              // 第二列数据
              this.productList[1].push(productVoList[i]);
            }
          }
        })
      },
      /**
       * 跳转到卖闲置页面
       */
      cellMyProduct() {
        console.log("我要卖闲置");
        uni.navigateTo({
          url: "/pages/sellMyProduct/sellMyProduct"
        })
      },
      /**
       * 获取高宽比 乘以 100%
       */
      getAspectRatio(url) {
        return pictureApi.getAspectRatio(url);
      },
      /**
       * 选择分类
       */
      selectCategory() {
        uni.navigateTo({
          url: "/pages/sellMyProduct/selectCategory"
        })
      },
      /**
       * 获取商品名称
       */
      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];
      },
      /**
       * 查看商品的详情
       */
      seeProductDetail(productVo) {
        // console.log("productVo:"+JSON.stringify(productVo))
        uni.navigateTo({
          url: "/pages/product/detail?productVo=" + encodeURIComponent(JSON.stringify(productVo))
        })
      }
    }
  }
</script>
<style lang="scss">
  .content {
    padding: 20rpx;
    .col {
      width: 50%;
    }
    .productVoItem {
      margin-bottom: 20px;
      .productMes {
        overflow: hidden;
        text-overflow: ellipsis;
        display: -webkit-box;
        /* 显示2行 */
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        .productName {
          font-weight: bold;
        }
      }
      .price {
        color: #ff0000;
        font-weight: bold;
        .number {
          font-size: 22px;
        }
      }
      .originPrice {
        color: #A2A2A2;
        font-size: 15px;
        // 给文字添加中划线
        text-decoration: line-through;
      }
    }
  }
</style>
让文字只显示两行
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
/* 显示2行 */
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
路由跳转传递对象

因为首页已经查询到商品的很多信息了,点击查看商品详情的时候,很多信息不需要再查询一遍了,可以直接将商品的已知信息通过路由传递到新的页面去,需要注意的时候,将对象作为参数传递之前,需要先将对象进行编码

uni.navigateTo({
  url: "/pages/product/detail?productVo=" + encodeURIComponent(JSON.stringify(productVo))
})
将商品分为两列显示

首先将查询到的商品分为两组

let productVoList = res.rows;
this.productList = [
  [],
  []
];
for (var i = 0; i < productVoList.length; i++) {
  if (i % 2 == 0) {
    // 第一列数据
    this.productList[0].push(productVoList[i]);
  } else {
    // 第二列数据
    this.productList[1].push(productVoList[i]);
  }
}

然后在布局中使用行列布局即可,即使用一行两列的方式来显示商品信息

使用中划线划掉原价

// 给文字添加中划线
text-decoration: line-through;

后端

商品

controller

 /**
  * 查询商品Vo列表
  */
 @PreAuthorize("@ss.hasPermi('market:product:list')")
 @PostMapping("/listProductVo")
 @ApiOperation("获取商品列表")
 public TableDataInfo listProductVo(@RequestBody ProductVo productVo) {
     startPage();
     if (productVo.getProductCategoryId() != null) {
         // --if-- 当分类不为空的时候,只按照分类来搜索
         productVo.setKeyword(null);
     }
     List<ProductVo> list = productService.selectProductVoList(productVo);
     return getDataTable(list);
 }

service

/**
 * 查询商品Vo列表
 *
 * @param productVo
 * @return
 */
@Override
public List<ProductVo> selectProductVoList(ProductVo productVo) {
//        List<ProductVo> productVoList = new ArrayList<>();
    List<ProductVo> productVoList = baseMapper.selectProductVoList(productVo);
    ///设置每个商品的图片
    // 获取所有商品的id
    List<Long> productIdList = productVoList.stream().map(item -> {
        return item.getId();
    }).collect(Collectors.toList());
    // 查询出所有商品的图片
    if (productIdList.size() > 0) {
        List<Picture> pictureList = pictureService.selectPictureListByItemIdListAndType(productIdList, PictureType.PRODUCT.getType());
        Map<Long, List<String>> itemIdAndPicList = new HashMap<>();
        for (Picture picture : pictureList) {
            if (!itemIdAndPicList.containsKey(picture.getItemId())) {
                List<String> picList = new ArrayList<>();
                picList.add(picture.getAddress());
                itemIdAndPicList.put(picture.getItemId(), picList);
            } else {
                itemIdAndPicList.get(picture.getItemId()).add(picture.getAddress());
            }
        }
        // 给每个商品设置图片
        for (ProductVo vo : productVoList) {
            vo.setPicList(itemIdAndPicList.get(vo.getId()));
        }
    }
    return productVoList;
}

mapper

void starNumDiffOne(@Param("productId") Long productId);

sql

  <select id="selectProductVoList" parameterType="ProductVo" resultMap="ProductVoResult">
      SELECT
      p.id,
      p.NAME,
      p.description,
      p.original_price,
      p.price,
      p.product_category_id,
      pc.NAME AS productCategoryName,
      p.user_id,
      u.user_name as username,
      u.nick_name as nickname,
      u.avatar as avatar,
      p.reviewer_id,
      u1.user_name as reviewerUserName,
      p.fineness,
      p.unit,
      p.STATUS,
      p.is_contribute,
      p.functional_status,
      p.brand_id,
      b.NAME AS brandName
      FROM
      product AS p
      LEFT JOIN product_category AS pc ON p.product_category_id = pc.id
      LEFT JOIN brand AS b ON p.brand_id = b.id
      LEFT JOIN sys_user AS u ON p.user_id = u.user_id
      LEFT JOIN sys_user AS u1 ON p.reviewer_id = u1.user_id
      <where>
          <if test="keyword != null  and keyword != ''">and p.name like concat('%', #{keyword}, '%')</if>
          <if test="keyword != null  and keyword != ''">or p.description like concat('%', #{keyword}, '%')</if>
          <if test="productCategoryId != null  and productCategoryId != ''">and p.product_category_id =
              #{productCategoryId}
          </if>
      </where>
  </select>
目录
相关文章
|
9月前
|
JSON 自然语言处理 前端开发
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
496 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
|
7月前
|
Java 关系型数据库 MySQL
在Linux平台上进行JDK、Tomcat、MySQL的安装并部署后端项目
现在,你可以通过访问http://Your_IP:Tomcat_Port/Your_Project访问你的项目了。如果一切顺利,你将看到那绚烂的胜利之光照耀在你的项目之上!
404 41
|
8月前
|
监控 前端开发 小程序
陪练,代练,护航,代打小程序源码/前端UNIAPP-VUE2.0开发 后端Thinkphp6管理/具备家政服务的综合型平台
这款APP通过技术创新,将代练、家政、娱乐社交等场景融合,打造“全能型生活服务生态圈”。以代练为切入点,提供模块化代码支持快速搭建平台,结合智能匹配与技能审核机制,拓展家政服务和商业管理功能。技术架构具备高安全性和扩展性,支持多业务复用,如押金冻结、录屏监控等功能跨领域应用。商业模式多元,包括交易抽成、增值服务及广告联名,同时设计跨领域积分体系提升用户粘性,实现生态共生与B端赋能。
805 12
|
8月前
|
人工智能 小程序 NoSQL
【一步步开发AI运动小程序】二十一、如何将AI运动项目配置持久化到后端?
本文介绍基于云智「Ai运动识别引擎」的运动配置持久化方案,旨在优化小程序或Uni APP中AI运动识别能力。通过将运动检测参数(如`Key`、`Name`、`TickMode`、`rules`或`samples`)持久化到后端,可避免因频繁调整运动参数而重新发布应用,提升用户体验。持久化数据结构支持规则和姿态样本存储,适用于关系数据库、文件或文档数据库(如MongoDB)。此外,云智还提供运动自动适配工具及「AI乐运动」产品,助力快速实现AI体育、全民健身等场景。
|
10月前
|
SQL JavaScript 安全
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
440 11
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
|
10月前
|
存储 小程序 前端开发
微信小程序与Java后端实现微信授权登录功能
微信小程序极大地简化了登录注册流程。对于用户而言,仅仅需要点击授权按钮,便能够完成登录操作,无需经历繁琐的注册步骤以及输入账号密码等一系列复杂操作,这种便捷的登录方式极大地提升了用户的使用体验
3045 12
|
5月前
|
人工智能 Java API
后端开发必看:零代码实现存量服务改造成MCP服务
本文介绍如何通过 **Nacos** 和 **Higress** 实现存量 Spring Boot 服务的零代码改造,使其支持 MCP 协议,供 AI Agent 调用。全程无需修改业务代码,仅通过配置完成服务注册、协议转换与工具映射,显著降低改造成本,提升服务的可集成性与智能化能力。
1478 1
|
5月前
|
前端开发 Java 数据库连接
后端开发中的错误处理实践:原则与实战
在后端开发中,错误处理是保障系统稳定性的关键。本文介绍了错误分类、响应设计、统一处理机制及日志追踪等实践方法,帮助开发者提升系统的可维护性与排障效率,做到防患于未然。
|
7月前
|
存储 消息中间件 前端开发
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
校园圈子系统校园论坛小程序采用uni-app前端框架,支持多端运行,结合PHP后端(如ThinkPHP/Laravel),实现用户认证、社交关系管理、动态发布与实时聊天功能。前端通过组件化开发和uni.request与后端交互,后端提供RESTful API处理业务逻辑并存储数据于MySQL。同时引入Redis缓存热点数据,RabbitMQ处理异步任务,优化系统性能。核心功能包括JWT身份验证、好友系统、WebSocket实时聊天及活动管理,确保高效稳定的用户体验。
446 4
PHP后端与uni-app前端协同的校园圈子系统:校园社交场景的跨端开发实践
|
存储 缓存 负载均衡
后端开发中的性能优化策略
本文将探讨几种常见的后端性能优化策略,包括代码层面的优化、数据库查询优化、缓存机制的应用以及负载均衡的实现。通过这些方法,开发者可以显著提升系统的响应速度和处理能力,从而提供更好的用户体验。
413 6