【UniApp开发小程序】小程序首页完善(滑到底部数据翻页、回到顶端、基于回溯算法的两列数据高宽比平衡)【后端基于若依管理系统开发】

文章目录

  • 说明
  • 细节一:首页滑动到底部,需要查询下一页的商品
    • 界面预览
    • 页面实现
  • 细节二:当页面滑动到下方,出现一个回到顶端的悬浮按钮
  • 细节三:商品分列
    • 说明
    • 优化前后效果对比
    • 使用回溯算法实现
      • Controller
      • Service
      • 回溯算法
    • 优化:减少图片的网络请求
      • 数据表增加字段
      • 将数据表中已有数据的宽高比计算出来,并更新到数据表中
      • 修改商品发布页面的代码
      • Service改进
    • 优化:考虑分页的分组高宽比总和平衡
      • 页面代码
      • Controller
      • Service
      • 回溯算法
  • 页面整体代码

说明

之前已经在【UniApp开发小程序】小程序首页(展示商品、商品搜索、商品分类搜索)【后端基于若依管理系统开发】这篇文章中介绍了首页的实现,但是当时的实现并不是完善的,因为项目开发是一个持续的过程,也因为我是个人的第一次尝试开发这种类型的项目,很多细节没有提前构思清楚,因此这篇文章作为一个补充,用来优化前面的一些问题

细节一:首页滑动到底部,需要查询下一页的商品

界面预览

当滑动底部的时候,底部出现”正在加载“字样,同时向后端发送请求获取下一页的商品数据

在这里插入图片描述
当商品被全部加载出之后,显示“没有更多了”字样

在这里插入图片描述

页面实现

下面的方法可以监听用户滑动页面到达底部,当滑动到底部的时候,调用方法查询更多商品

// 监听用户滑动到底部
onReachBottom() {this.getMoreProductVo();
},

注意,当还有商品没有被查询出来时,才会调用listProductVo方法去找服务端查询数据。如果没有了,则提示“没有更多了”

/*** 获取下一页的商品*/
getMoreProductVo() {if (this.productList[0].length + this.productList[1].length >= this.total) {// this.$refs.uToast.show({// 	type: 'warning',// 	message: "已经加载完所有商品数据",// 	duration: 1000// })} else {if (this.loadData != true) {// console.log("--------------------------获取下一页商品---------------------------")this.page.pageNum++;// 显示正在加载this.loadmoreStatus = "loading";this.listProductVo().then(() => {if (this.productList[0].length + this.productList[1].length >= this.total) {// 没有更多了this.loadmoreStatus = "nomore";} else {// 加载更多this.loadmoreStatus = "loadmore";}});}}
},

细节二:当页面滑动到下方,出现一个回到顶端的悬浮按钮

增加一个标签

<!-- 回到上方按钮 -->
<u-back-top :scroll-top="scrollTop"></u-back-top>

因为标签绑定了一个变量,需要声明出来

// 用来控制滚动到最上方
scrollTop: 0

除此之外,还需要实时记录滚动的位置

// 在滑动过程实时获取现在的滚动条位置,并保存当前的滚动条位置
onPageScroll(e) {this.scrollTop = e.scrollTop;
},

细节三:商品分列

说明

上篇文章中,使用了最简单的方式来实现分列,那就是直接遍历一遍商品数组,依次将商品分到第一列和第二列,但是这样会出现两列商品高度不平衡的情况,如下图
在这里插入图片描述
因此,我们需要更换一种分组策略,用来平衡两列商品内容的高度,这样视觉效果更好。问题可以理解为:假设有十个物品,每个物品的长度不太一样,要求将这些物品分到两组中,最后两组物品长度总和最接近,请问需要怎么来分这两个组?

优化前后效果对比

在这里插入图片描述

使用回溯算法实现

因为采用的是分页查询,而且每次查询出来的数据量并不大,因此可以直接使用回溯算法获取所有的分组情况,最后选择出高度差距最小的分组方案即可

Controller

/*** 查询商品Vo列表*/
@PreAuthorize("@ss.hasPermi('market:product:list')")
@PostMapping("/listProductVo")
@ApiOperation("获取商品列表")
public AjaxResult listProductVo(@RequestBody ProductVo productVo) {startPage();if (productVo.getProductCategoryId() != null) {// --if-- 当分类不为空的时候,只按照分类来搜索productVo.setKeyword(null);}if (productVo.getIsSearchStar() != null && productVo.getIsSearchStar() == true) {productVo.setStarPeopleId(getLoginUser().getUserId());}List<ProductVo> productVoList = productService.selectProductVoList(productVo);// 将productVoList分成两组,要求两组的高度之和相差最小List<ProductVo>[] groups = productService.splitToTwoGroups(productVoList);Map<String, Object> map = new HashMap<>();TableDataInfo pageMes = getDataTable(productVoList);map.put("pageMes", pageMes);map.put("groups", groups);return AjaxResult.success(map);
}

Service

@Override
public List<ProductVo>[] splitToTwoGroups(List<ProductVo> productVoList) {List<ProductVo>[] resultArr = new List[2];for (int i = 0; i < resultArr.length; i++) {resultArr[i] = new ArrayList<>();}/// 数据准备// 获取每个图片的高宽比Map<Long, Double> idAndRatioMap = new HashMap<>();// 存储所有商品的idList<Long> idList = new ArrayList<>();long start = System.currentTimeMillis();for (ProductVo productVo : productVoList) {idList.add(productVo.getId());if (productVo.getPicList() != null && productVo.getPicList().size() > 0) {try {BufferedImage sourceImg = ImageIO.read(new URL(productVo.getPicList().get(0)).openStream());idAndRatioMap.put(productVo.getId(), sourceImg.getHeight() * 1.0 / sourceImg.getWidth());} catch (IOException e) {throw new RuntimeException(e);}} else {idAndRatioMap.put(productVo.getId(), 0.0);}}System.out.println("分组时间:" + (System.currentTimeMillis() - start) + "ms");/// 深度优先遍历,找出所有方案,选择两组高度差距最小的分组方案GroupDivide groupDivide = new GroupDivide();groupDivide.dfsSearch(idList, 0, new ArrayList<>(), idAndRatioMap);/// 最后处理分组List<Long> group1 = groupDivide.bestGroup1;List<Long> group2 = new ArrayList<>();for (Long id : idList) {if (group1.indexOf(id) == -1) {group2.add(id);}}for (ProductVo productVo : productVoList) {if (group1.indexOf(productVo.getId()) != -1) {resultArr[0].add(productVo);} else {resultArr[1].add(productVo);}}return resultArr;
}

由于下面的方法获取每个图片的高宽比都需要进行网络请求,因此速度较慢,因此需要进行优化

BufferedImage sourceImg = ImageIO.read(new URL(productVo.getPicList().get(0)).openStream());
idAndRatioMap.put(productVo.getId(), sourceImg.getHeight() * 1.0 / sourceImg.getWidth());

在这里插入图片描述

回溯算法

为了加速算法的求解,其中使用了减枝策略,不去搜索没有必要搜索的方案

package com.shm.algorithm;import com.ruoyi.common.utils.clone.CloneUtil;import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** 首页商品数据分组** @Author dam* @create 2023/8/30 14:12*/
public class GroupDivide {/*** 最小间距*/private double minOffSet = Double.MAX_VALUE;/*** 存储最好的第一组*/public List<Long> bestGroup1=null;public void dfsSearch(List<Long> idList, int begin, List<Long> curGroup1, Map<Long, Double> idAndRatioMap) {if (begin == idList.size()) {// 递归完成return;}for (int i = begin; i < idList.size(); i++) {curGroup1.add(idList.get(i));// 计算组1的长度-组2的长度double offSet = calculateGroup1DifHeifGroup2Hei(idList, curGroup1, idAndRatioMap);if (offSet > minOffSet) {// 如果当前差距已经大于最小差距,执行剪枝,因为如果再往第一组增加图片的话,那差距只会更大,没必要再往下搜索了// 删除最后一个元素curGroup1.remove(curGroup1.size() - 1);continue;} else if (Math.abs(offSet) < minOffSet) {// 找到更小的间距,保存最优解minOffSet = Math.abs(offSet);bestGroup1 = CloneUtil.arrayListClone(curGroup1);}dfsSearch(idList, i + 1, curGroup1, idAndRatioMap);// 删除最后一个元素curGroup1.remove(curGroup1.size() - 1);}}/*** 计算第一组的图片的总高度 减去 第二组图片的总高度** @param idList* @param group1* @param idAndRatioMap* @return*/private double calculateGroup1DifHeifGroup2Hei(List<Long> idList, List<Long> group1, Map<Long, Double> idAndRatioMap) {double sum1 = 0, sum2 = 0;for (Long id : idList) {if (group1.indexOf(id) == -1) {sum2 += idAndRatioMap.get(id);}else {sum1 += idAndRatioMap.get(id);}}return sum1 - sum2;}}

优化:减少图片的网络请求

因为图片的高宽比是一个不变量,可以将其作为一个属性存储到数据表中,这样只需要查询出来即可,不再需要使用网络请求来获取,但是需要在存储图片到数据表之前获取高宽比,并将该属性进行存储

数据表增加字段

在这里插入图片描述

将数据表中已有数据的宽高比计算出来,并更新到数据表中

因为我的数据表中已经存在了一些图片数据,为了小程序地正确运行,需要对这批数据进行修复,即为每张图片补充高宽比。因为数据表的数据量不大,而且是一次性任务,直接每次修改单条数据即可。如果数据量很大,可以使用多线程和分批批量修改来优化修复速度

@Overridepublic void updatePictureSheetSetAspectRatio() {Picture picture = new Picture();picture.setType(0);// 取消使用分页clearPage();List<Picture> pictureList = pictureMapper.selectPictureList(picture);for (Picture picture1 : pictureList) {String address = picture1.getAddress();try {BufferedImage sourceImg = ImageIO.read(new URL(address));picture1.setAspectRatio(sourceImg.getHeight() * 1.0 / sourceImg.getWidth());pictureMapper.updatePicture(picture1);} catch (IOException e) {throw new RuntimeException(e);}}}

修改商品发布页面的代码

现在数据表需要保存图片的高宽比,虽然可以直接由服务端在保存图片之前计算高宽比,但是这样还是要发送很多网络请求,影响接口的并发性能,因此建议由客户端来计算高宽比,然后直接上传给服务端,服务端直接将数据保存即可

/**
* 上传闲置商品
*/
uploadSellProduct() {// console.log("上传闲置商品picList:" + JSON.stringify(this.picList));if (this.product.productCategoryId) {if (this.picList.length == 0) {this.$refs.uToast.show({type: 'error',message: "商品图片没有上传成功"})} else {this.setPicAspectRatio().then(() => {// console.log("即将上传的商品:" + JSON.stringify(this.product));uploadSellProduct(this.product).then(res => {this.$refs.uToast.show({type: 'success',message: "您的商品已经发布到平台"})setTimeout(() => {uni.reLaunch({url: "/pages/index/index"})}, 1000)}).catch(error => {console.log("error:" + JSON.stringify(error));this.$refs.uToast.show({type: 'error',message: "商品发布失败"})});});}} else {this.$refs.uToast.show({type: 'error',message: "请选择分类"})}
},
/*** 设置图片的宽高比*/
setPicAspectRatio() {return new Promise((resolve, reject) => {this.product.picList = [];let promises = [];for (let i = 0; i < this.picList.length; i++) {let picUrl = this.picList[i];promises.push(this.getAspectRatio(picUrl).then((res) => {let pic = {address: picUrl,aspectRatio: res}this.product.picList.push(pic);console.log("当前图片高宽比设置完成");}))}Promise.all(promises).then(() => {console.log("所有图片高宽比设置完成,this.product.picList:" + JSON.stringify(this.product.picList));resolve();})})
},
/*** 获取单个图片的高宽比* @param {Object} url*/
getAspectRatio(url) {return new Promise((resolve, reject) => {uni.getImageInfo({src: url,success: function(res) {let aspectRatio = res.height / res.width;resolve(aspectRatio);}});})
},

注意点:

  • 因为getAspectRatio方法获取图片的高宽比发送网络请求,因此使用Promise来确保高宽比获取成功才resolve
  • 在上传商品之前,需要先设置商品所对应的所有图片的高宽比。如果图片有多张,需要等待所有图片的高宽比都设置完成,本文使用Promise.all(promises)来等待所有图片的高宽比都设置完成,再resolve

Service改进

因为已经将图片的高宽比存储到数据表中,因此不需要再发送网路请求,直接获取属性值即可

@Override
public List<ProductVo>[] splitToTwoGroups(List<ProductVo> productVoList) {List<ProductVo>[] resultArr = new List[2];for (int i = 0; i < resultArr.length; i++) {resultArr[i] = new ArrayList<>();}/// 数据准备// 获取每个图片的高宽比Map<Long, Double> idAndRatioMap = new HashMap<>();// 存储所有商品的idList<Long> idList = new ArrayList<>();long start = System.currentTimeMillis();for (ProductVo productVo : productVoList) {idList.add(productVo.getId());if (productVo.getPicList() != null && productVo.getPicList().size() > 0) {
//                try {
//                    BufferedImage sourceImg = ImageIO.read(new URL(productVo.getPicList().get(0)).openStream());
//                    idAndRatioMap.put(productVo.getId(), sourceImg.getHeight() * 1.0 / sourceImg.getWidth());
//                } catch (IOException e) {
//                    throw new RuntimeException(e);
//                }idAndRatioMap.put(productVo.getId(), productVo.getPicList().get(0).getAspectRatio());} else {idAndRatioMap.put(productVo.getId(), 0.0);}}System.out.println("分组时间:" + (System.currentTimeMillis() - start) + "ms");/// 深度优先遍历,找出所有方案,选择两组高度差距最小的分组方案GroupDivide groupDivide = new GroupDivide();groupDivide.dfsSearch(idList, 0, new ArrayList<>(), idAndRatioMap);/// 最后处理分组List<Long> group1 = groupDivide.bestGroup1;List<Long> group2 = new ArrayList<>();for (Long id : idList) {if (group1.indexOf(id) == -1) {group2.add(id);}}for (ProductVo productVo : productVoList) {if (group1.indexOf(productVo.getId()) != -1) {resultArr[0].add(productVo);} else {resultArr[1].add(productVo);}}return resultArr;
}

【测试】
在不需要发送网络请求之后,可以看到获取图片高宽比的时间被大大减少

在这里插入图片描述

优化:考虑分页的分组高宽比总和平衡

虽然上面已经使用算法来平衡两列的高宽比总和了,但是还存在一个问题,即商品数据是分页查询的,比如第第一页查询的结果是第一列的高宽比总和大于第二列的高宽比总和。那么为了可以更好地平衡两列的高宽比总和,第二页数据的查询结果应该是第二列的高宽比总和大于第一列的高宽比总和。为了处理这个问题,在使用回溯算法的时候,需要接收当前已渲染页面的两列宽高比,这样才能方便更好地进行决策

页面代码

从下面的代码中,可以很直观地看到,每次分页查询都更新两列对应地高宽比总和,并在发送请求的时候带上这两个参数

/**
* 查询商品vo集合
*/
listProductVo() {
return new Promise((resolve, reject) => {// 设置当前两列的高宽比总和this.searchForm.sumAspectRatioOfColumn1 = this.sumAspectRatioOfColumn1;this.searchForm.sumAspectRatioOfColumn2 = this.sumAspectRatioOfColumn2;listProductVo(this.searchForm, this.page).then(res => {// console.log("listProductVo:" + JSON.stringify(res))let productVoList = res.data.pageMes.rows;this.total = res.data.pageMes.total;// 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]);// 	}// }let groups = res.data.groups;for (var i = 0; i < groups[0].length; i++) {if (groups[0][i].picList != null && groups[0][i].picList.length > 0) {this.sumAspectRatioOfColumn1 += groups[0][i].picList[0].aspectRatio;}}for (var i = 0; i < groups[1].length; i++) {if (groups[1][i].picList != null && groups[1][i].picList.length > 0) {this.sumAspectRatioOfColumn2 += groups[1][i].picList[0].aspectRatio;}}this.productList[0] = this.productList[0].concat(groups[0]);this.productList[1] = this.productList[1].concat(groups[1]);resolve();})})},

Controller

/*** 查询商品Vo列表*/
@PreAuthorize("@ss.hasPermi('market:product:list')")
@PostMapping("/listProductVo")
@ApiOperation("获取商品列表")
public AjaxResult listProductVo(@RequestBody ProductVo productVo) {startPage();if (productVo.getProductCategoryId() != null) {// --if-- 当分类不为空的时候,只按照分类来搜索productVo.setKeyword(null);}if (productVo.getIsSearchStar() != null && productVo.getIsSearchStar() == true) {productVo.setStarPeopleId(getLoginUser().getUserId());}List<ProductVo> productVoList = productService.selectProductVoList(productVo);// 将productVoList分成两组,要求两组的高度之和相差最小List<ProductVo>[] groups = productService.splitToTwoGroups(productVoList, productVo.getSumAspectRatioOfColumn1(), productVo.getSumAspectRatioOfColumn2());Map<String, Object> map = new HashMap<>();TableDataInfo pageMes = getDataTable(productVoList);map.put("pageMes", pageMes);map.put("groups", groups);return AjaxResult.success(map);
}

Service

@Override
public List<ProductVo>[] splitToTwoGroups(List<ProductVo> productVoList, Double sumAspectRatioOfColumn1, Double sumAspectRatioOfColumn2) {List<ProductVo>[] resultArr = new List[2];for (int i = 0; i < resultArr.length; i++) {resultArr[i] = new ArrayList<>();}/// 数据准备// 获取每个图片的高宽比Map<Long, Double> idAndRatioMap = new HashMap<>();// 存储所有商品的idList<Long> idList = new ArrayList<>();long start = System.currentTimeMillis();for (ProductVo productVo : productVoList) {idList.add(productVo.getId());if (productVo.getPicList() != null && productVo.getPicList().size() > 0) {
//                try {
//                    BufferedImage sourceImg = ImageIO.read(new URL(productVo.getPicList().get(0)).openStream());
//                    idAndRatioMap.put(productVo.getId(), sourceImg.getHeight() * 1.0 / sourceImg.getWidth());
//                } catch (IOException e) {
//                    throw new RuntimeException(e);
//                }idAndRatioMap.put(productVo.getId(), productVo.getPicList().get(0).getAspectRatio());} else {idAndRatioMap.put(productVo.getId(), 0.0);}}System.out.println("分组时间:" + (System.currentTimeMillis() - start) + "ms");/// 深度优先遍历,找出所有方案,选择两组高度差距最小的分组方案GroupDivide groupDivide = new GroupDivide();groupDivide.dfsSearch(idList, 0, new ArrayList<>(), idAndRatioMap,sumAspectRatioOfColumn1,sumAspectRatioOfColumn2);/// 最后处理分组List<Long> group1 = groupDivide.bestGroup1;List<Long> group2 = new ArrayList<>();for (Long id : idList) {if (group1.indexOf(id) == -1) {group2.add(id);}}for (ProductVo productVo : productVoList) {if (group1.indexOf(productVo.getId()) != -1) {resultArr[0].add(productVo);} else {resultArr[1].add(productVo);}}return resultArr;
}

回溯算法

package com.shm.algorithm;import com.ruoyi.common.utils.clone.CloneUtil;import java.util.List;
import java.util.Map;/*** 首页商品数据分组** @Author dam* @create 2023/8/30 14:12*/
public class GroupDivide {/*** 最小间距*/private double minOffSet = Double.MAX_VALUE;/*** 存储最好的第一组*/public List<Long> bestGroup1 = null;public void dfsSearch(List<Long> idList, int begin, List<Long> curGroup1, Map<Long, Double> idAndRatioMap, Double sumAspectRatioOfColumn1, Double sumAspectRatioOfColumn2) {if (begin == idList.size()) {// 递归完成return;}for (int i = begin; i < idList.size(); i++) {curGroup1.add(idList.get(i));// 计算组1的长度-组2的长度double offSet = calculateGroup1DifHeifGroup2Hei(idList, curGroup1, idAndRatioMap, sumAspectRatioOfColumn1, sumAspectRatioOfColumn2);if (offSet > minOffSet) {// 如果当前差距已经大于最小差距,执行剪枝,因为如果再往第一组增加图片的话,那差距只会更大,没必要再往下搜索了// 删除最后一个元素curGroup1.remove(curGroup1.size() - 1);continue;} else if (Math.abs(offSet) < minOffSet) {// 找到更小的间距,保存最优解minOffSet = Math.abs(offSet);bestGroup1 = CloneUtil.arrayListClone(curGroup1);}dfsSearch(idList, i + 1, curGroup1, idAndRatioMap, sumAspectRatioOfColumn1, sumAspectRatioOfColumn2);// 删除最后一个元素curGroup1.remove(curGroup1.size() - 1);}}/*** 计算第一组的图片的总高度 减去 第二组图片的总高度** @param idList* @param group1* @param idAndRatioMap* @param sumAspectRatioOfColumn1* @param sumAspectRatioOfColumn2* @return*/private double calculateGroup1DifHeifGroup2Hei(List<Long> idList, List<Long> group1, Map<Long, Double> idAndRatioMap, Double sumAspectRatioOfColumn1, Double sumAspectRatioOfColumn2) {// 设置初始值double sum1 = sumAspectRatioOfColumn1, sum2 = sumAspectRatioOfColumn2;for (Long id : idList) {if (group1.indexOf(id) == -1) {sum2 += idAndRatioMap.get(id);} else {sum1 += idAndRatioMap.get(id);}}return sum1 - sum2;}}

页面整体代码

【index页面】

<template><view class="content"><u-toast ref="uToast"></u-toast><!-- 回到上方按钮 --><u-back-top :scroll-top="scrollTop"></u-back-top><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].address" width="100%":height="productVo.picList[0].aspectRatio*100+'%'" radius="10" mode="widthFix" :lazy-load="true":fade="true" duration="450" @error="reloadPir(productVo.picList[0].address)"><!-- 加载失败展示 --><view slot="error" style="font-size: 24rpx;">加载失败</view><!-- 加载中提示 --><template v-slot:loading><u-loading-icon color="red"></u-loading-icon></template></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-loadmore v-if="productList[0].length>0&&loadData==false" :status="loadmoreStatus" /><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/product.js";import pictureApi from "@/utils/picture.js";import Vue from 'vue';import {debounce} from "@/utils/debounce.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,// 用来锁定,防止多次同时进行websocket连接lockReconnect: false,// 心跳一次间隔的时间,单位毫秒heartbeatTime: 5000,page: {pageNum: 1,pageSize: 10},// 总数据条数total: 0,// 数据加载状态loadmoreStatus: "loadmore",// 用来控制滚动到最上方scrollTop: 0,// 分别存储两列的高宽比总和sumAspectRatioOfColumn1: 0,sumAspectRatioOfColumn2: 0,}},onLoad() {},created() {this.loadData = true;this.listProductVo().then(() => {this.loadData = false;});this.initWebsocket();// this.getMoreProductVo = debounce(this.getMoreProductVo);},// 监听用户滑动到底部onReachBottom() {this.getMoreProductVo();},// 在滑动过程实时获取现在的滚动条位置,并保存当前的滚动条位置onPageScroll(e) {this.scrollTop = e.scrollTop;},methods: {/*** 查询商品vo集合*/listProductVo() {return new Promise((resolve, reject) => {// 设置当前两列的高宽比总和this.searchForm.sumAspectRatioOfColumn1 = this.sumAspectRatioOfColumn1;this.searchForm.sumAspectRatioOfColumn2 = this.sumAspectRatioOfColumn2;listProductVo(this.searchForm, this.page).then(res => {// console.log("listProductVo:" + JSON.stringify(res))let productVoList = res.data.pageMes.rows;this.total = res.data.pageMes.total;// 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]);// 	}// }let groups = res.data.groups;for (var i = 0; i < groups[0].length; i++) {if (groups[0][i].picList != null && groups[0][i].picList.length > 0) {this.sumAspectRatioOfColumn1 += groups[0][i].picList[0].aspectRatio;}}for (var i = 0; i < groups[1].length; i++) {if (groups[1][i].picList != null && groups[1][i].picList.length > 0) {this.sumAspectRatioOfColumn2 += groups[1][i].picList[0].aspectRatio;}}this.productList[0] = this.productList[0].concat(groups[0]);this.productList[1] = this.productList[1].concat(groups[1]);resolve();})})},/*** 获取下一页的商品*/getMoreProductVo() {if (this.productList[0].length + this.productList[1].length >= this.total) {// this.$refs.uToast.show({// 	type: 'warning',// 	message: "已经加载完所有商品数据",// 	duration: 1000// })} else {if (this.loadData != true) {// console.log("--------------------------获取下一页商品---------------------------")this.page.pageNum++;// 显示正在加载this.loadmoreStatus = "loading";this.listProductVo().then(() => {if (this.productList[0].length + this.productList[1].length >= this.total) {// 没有更多了this.loadmoreStatus = "nomore";} else {// 加载更多this.loadmoreStatus = "loadmore";}});}}},/*** 跳转到卖闲置页面*/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))})},/*** 重新加载图片*/reloadPir(pic) {console.log("图片加载失败,pic:" + pic)},/*** 创建websocket连接*/initWebsocket() {console.log("this.socket:" + JSON.stringify(this.$socket))// this.$socket == null,刚刚进入首页,还没有建立过websocket连接// this.$socket.readyState==0 表示正在连接当中// this.$socket.readyState==1 表示处于连接状态// this.$socket.readyState==2 表示连接正在关闭// this.$socket.readyState==3 表示连接已经关闭if (this.$socket == null || (this.$socket.readyState != 1 && this.$socket.readyState != 0)) {this.$socket = uni.connectSocket({url: "ws://10.23.17.146:8085/websocket/" + uni.getStorageSync("curUser").userName,success(res) {console.log('WebSocket连接成功', res);},})// console.log("this.socket:" + this.$socket)// 监听WebSocket连接打开事件this.$socket.onOpen((res) => {console.log("websocket连接成功")Vue.prototype.$socket = this.$socket;// 连接成功,开启心跳this.headbeat();});// 连接异常this.$socket.onError((res) => {console.log("websocket连接出现异常");// 重连this.reconnect();})// 连接断开this.$socket.onClose((res) => {console.log("websocket连接关闭");// 重连this.reconnect();})}},/*** 重新连接*/reconnect() {// console.log("重连");// 防止重复连接if (this.lockReconnect == true) {return;}// 锁定,防止重复连接this.lockReconnect = true;// 间隔一秒再重连,避免后台服务出错时,客户端连接太频繁setTimeout(() => {this.initWebsocket();}, 1000)// 连接完成,设置为falsethis.lockReconnect = false;},// 开启心跳headbeat() {// console.log("websocket心跳");var that = this;setTimeout(function() {if (that.$socket.readyState == 1) {// websocket已经连接成功that.$socket.send({data: JSON.stringify({status: "ping"})})// 调用启动下一轮的心跳that.headbeat();} else {// websocket还没有连接成功,重连that.reconnect();}}, that.heartbeatTime);},}}
</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: #F84442;font-weight: bold;.number {font-size: 22px;}}.originPrice {color: #A2A2A2;font-size: 15px;// 给文字添加中划线text-decoration: line-through;}}}
</style>

【上传销售商品页面】

<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.description" placeholder="请输入商品描述" height="150"></u--textarea><!-- 图片上传 --><view><imageUpload v-model="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/product.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: undefined,number: 1,unit: '个',isContribute: 0,originalPrice: 0.00,price: 0.00,// 成色fineness: 0,// 功能状态functionalStatus: 0,brandId: 0},value: 'dasdas',categoryNameList: ["选择分类"],finenessList: ["全新", "几乎全新", "轻微使用痕迹", "明显使用痕迹", "外观破损"],functionList: ["功能完好无维修", "维修过,可正常使用", "有小问题,不影响使用", "无法正常使用"],picList: [],}},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() {// console.log("上传闲置商品picList:" + JSON.stringify(this.picList));if (this.product.productCategoryId) {if (this.picList.length == 0) {this.$refs.uToast.show({type: 'error',message: "商品图片没有上传成功"})} else {this.setPicAspectRatio().then(() => {// console.log("即将上传的商品:" + JSON.stringify(this.product));uploadSellProduct(this.product).then(res => {this.$refs.uToast.show({type: 'success',message: "您的商品已经发布到平台"})setTimeout(() => {uni.reLaunch({url: "/pages/index/index"})}, 1000)}).catch(error => {console.log("error:" + JSON.stringify(error));this.$refs.uToast.show({type: 'error',message: "商品发布失败"})});});}} else {this.$refs.uToast.show({type: 'error',message: "请选择分类"})}},/*** 设置图片的宽高比*/setPicAspectRatio() {return new Promise((resolve, reject) => {this.product.picList = [];let promises = [];for (let i = 0; i < this.picList.length; i++) {let picUrl = this.picList[i];promises.push(this.getAspectRatio(picUrl).then((res) => {let pic = {address: picUrl,aspectRatio: res}this.product.picList.push(pic);console.log("当前图片高宽比设置完成");}))}Promise.all(promises).then(() => {console.log("所有图片高宽比设置完成,this.product.picList:" + JSON.stringify(this.product.picList));resolve();})})},/*** 获取单个图片的高宽比* @param {Object} url*/getAspectRatio(url) {return new Promise((resolve, reject) => {uni.getImageInfo({src: url,success: function(res) {let aspectRatio = res.height / res.width;resolve(aspectRatio);}});})},/*** 选择分类*/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>

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

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

相关文章

使用 Netty 实现群聊功能的步骤和注意事项

文章目录 前言声明功能说明实现步骤WebSocket 服务启动Channel 初始化HTTP 请求处理HTTP 页面内容WebSocket 请求处理 效果展示总结 前言 通过之前的文章介绍&#xff0c;我们可以深刻认识到Netty在网络编程领域的卓越表现和强大实力。这篇文章将介绍如何利用 Netty 框架开发一…

基于非洲秃鹫算法优化的BP神经网络(预测应用) - 附代码

基于非洲秃鹫算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于非洲秃鹫算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.非洲秃鹫优化BP神经网络2.1 BP神经网络参数设置2.2 非洲秃鹫算法应用 4.测试结果&#xff1a;5…

实现带头双向循环链表

&#x1f308;带头双向循环链表 描述&#xff1a;一个节点内包含两个指针&#xff0c;一个指向上一个节点&#xff0c;另一个指向下一个节点。哨兵位指向的下一个节点为头节点&#xff0c;哨兵位的上一个指向尾节点。 结构优势&#xff1a;高效率找尾节点&#xff1b;高效率插入…

度矩阵、邻接矩阵

度矩阵&#xff08;degree matrix&#xff09; 度矩阵是对角阵&#xff0c;对角上的元素为各个顶点的度&#xff0c;顶点vi的度表示和该顶点相关联的变得数量。 在无向图中&#xff0c;顶点vi的度d(vi)N(i)&#xff08;即与顶点相连的边的数目&#xff09;有向图中&#xff0…

【力扣】55、跳跃游戏

var canJump function(nums){let cover 0;for(let i0;i<nums.length;i){if(i<cover){cover Math.max(nums[i]i,cover);if(cover >nums.length-1){return true;}}}}

stm32之DS18B20

DS18B20与stm32之间也是通过单总线进行数据的传输的。单总线协议在DHT11中已经介绍过。虽说这两者外设都是单总线&#xff0c;但时序电路却很不一样&#xff0c;DS18B20是更为麻烦一点的。 DS18B20 举例&#xff08;原码补码反码转换_原码反码补码转换_王小小鸭的博客-CSDN博客…

打开软件报错mfc100u.dll缺失是什么意思?简单式修复mfc100u.dll问题

首先&#xff0c;我们需要了解什么是MFC100U.dll文件以及它的作用。MFC100U.dll是一个Microsoft Foundation Class (MFC)库文件&#xff0c;它是Visual C应用程序开发的一部分。MFC库提供了许多通用的功能&#xff0c;如窗口管理、消息处理等&#xff0c;可以帮助开发者更快速地…

C++中前置++和后置++的详细讲解

参考链接&#xff08;链接讲的很全&#xff09;&#xff1a;C前置和后置的区别 对于迭代器和其他模板对象使用前缀形式 (i) 的自增, 自减运算符.&#xff0c;理由是 前置自增 (i) 通常要比后置自增 (i) 效率更高。 class Age { public: Age& operator() //前置 {…

fastjson-1.2.24-rce(CVE-2017-18349)fastjson-1.2.47-rce(CNVD-2019-22238)

一.fastjson 1.2.24 反序列化导致任意命令执行漏洞(CVE-2017-18349) fastjson在解析json的过程中&#xff0c;支持使用autoType来实例化某一个具体的类&#xff0c;并调用该类的set/get方法来访问属性。通过查找代码中相关的方法&#xff0c;即可构造出一些恶意利用链 影响范围…

Ansible学习笔记2

Ansible是Python开发的自动化运维工具&#xff0c;集合了众多运维工具&#xff08;Puppet、cfengine、chef、func、fabric&#xff09;的优点&#xff0c;实现了批量系统配置&#xff0c;批量程序部署、批量运行命令等功能。 特点&#xff1a; 1&#xff09;部署简单&#xff…

基于金枪鱼群算法优化的BP神经网络(预测应用) - 附代码

基于金枪鱼群算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于金枪鱼群算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.金枪鱼群优化BP神经网络2.1 BP神经网络参数设置2.2 金枪鱼群算法应用 4.测试结果&#xff1a;5…

【Java基础增强】Stream流

1.Stream流 1.1体验Stream流【理解】 案例需求 按照下面的要求完成集合的创建和遍历 创建一个集合&#xff0c;存储多个字符串元素 把集合中所有以"张"开头的元素存储到一个新的集合 把"张"开头的集合中的长度为3的元素存储到一个新的集合 遍历上一步得…

Swift使用PythonKit调用Python

打开Xcode项目。然后选择“File→Add Packages”&#xff0c;然后输入软件包依赖链接&#xff1a; ​https://github.com/pvieito/PythonKit.git https://github.com/kewlbear/Python-iOS.git Python-iOS包允许在iOS应用程序中使用python模块。 用法&#xff1a; import Pyth…

【项目 计网7】4.20 多进程实现并发服务器 4.22 多线程实现并发服务器

文章目录 4.20 多进程实现并发服务器server_process.cclient.c4.22 多线程实现并发服务器客户端代码&#xff1a;服务端代码&#xff1a; 4.20 多进程实现并发服务器 要实现TCP通信服务器处理并发的任务&#xff0c;使用多线程或者多进程来解决。 思路&#xff1a; 1、一个父进…

【leetcode 力扣刷题】字符串翻转合集(全部反转///部分反转)

字符串翻转合集 344. 反转字符串541. 反转字符串Ⅱ151. 反转字符串中的单词剑指 Offer 58 - II. 左旋转字符串反转单词思路循环挪动子串和子串的拼接 344. 反转字符串 题目链接&#xff1a;344. 反转字符串 题目内容&#xff1a; 题目中重点强调了必须原地修改输入数组&#…

2023_Spark_实验三:基于IDEA开发Scala例子

一、创建一个空项目&#xff0c;作为整个项目的基本框架 二、创建SparkStudy模块&#xff0c;用于学习基本的Spark基础 三、创建项目结构 1、在SparkStudy模块下的pom.xml文件中加入对应的依赖&#xff0c;并等待依赖包下载完毕。 在pom.xml文件中加入对应的依赖 ​<!-- S…

理论转换实践之keepalived+nginx实现HA

背景&#xff1a; keepalivednginx实现ha是网站和应用服务器常用的方法&#xff0c;之前项目中单独用nginx实现过负载均衡和服务转发&#xff0c;keepalived一直停留在理论节点&#xff0c;加之最近工作编写的一个技术文档用到keepalived&#xff0c;于是便有了下文。 服务组件…

基于MyBatis注解的学生管理程序--mybatis注解开发的练手项目

基于MyBatis注解的学生管理程序 需求&#xff1a;完成基于MyBatis注解的学生管理程序&#xff0c;能够用MyBatis注解实现查询操作、实现修改操作、实现一对多查询 &#xff08;1&#xff09;MyBatis注解开发实现查询操作。根据表1和表2在数据库分别创建一个学生表tb_student和…

论文笔记: One Fits All:Power General Time Series Analysis by Pretrained LM

1 intro 时间序列领域预训练模型/foundation 模型的研究还不是很多 主要挑战是缺乏大量的数据来训练用于时间序列分析的基础模型——>论文利用预训练的语言模型进行通用的时间序列分析 为各种时间序列任务提供了一个统一的框架 论文还调查了为什么从语言领域预训练的Transf…

C语言控制语句——分支语句

条件语句用来根据不同的条件来执行不同的语句&#xff0c;C语言中常用的条件语句包括if语句和switch语句。 if 语句 语法格式&#xff1a; if (条件) {条件成立时&#xff0c;要做的事…… }案例需求&#xff1a; 定义一个整数变量记录年龄判断是否满 18 岁 &#xff08;>…