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

news2024/11/23 1:22:21

文章目录

  • 说明
  • 细节一:首页滑动到底部,需要查询下一页的商品
    • 界面预览
    • 页面实现
  • 细节二:当页面滑动到下方,出现一个回到顶端的悬浮按钮
  • 细节三:商品分列
    • 说明
    • 优化前后效果对比
    • 使用回溯算法实现
      • 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<>();
    // 存储所有商品的id
    List<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;
    }

}

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

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

数据表增加字段

在这里插入图片描述

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

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

@Override
 public 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<>();
    // 存储所有商品的id
    List<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<>();
    // 存储所有商品的id
    List<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)
				// 连接完成,设置为false
				this.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.coloradmin.cn/o/950587.html

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

相关文章

Mac网页开发工具 RapidWeaver 9

RapidWeaver是一款Mac上的网页设计和开发工具&#xff0c;它使用户能够快速创建专业级别的网站&#xff0c;而无需编写代码。 以下是RapidWeaver软件的一些主要特点和功能&#xff1a; 模板和主题&#xff1a;RapidWeaver提供了大量的预先设计好的模板和主题&#xff0c;使用…

【jvm】运行时数据区

目录 一、运行时数据区一、作用二、说明三、线程共用与私有区域 一、运行时数据区 一、作用 1.内存是非常重要的系统资源&#xff0c;是硬盘和CPU 的中间仓库及桥梁&#xff0c;承载着操作系统和应用程序的实时运行。JVM内存布局规定了Java在运行过程中内存申请、分配、管理的策…

扫盲:OLTP和OLAP的区别

定义 OLTP是Online Transaction Processing的缩写&#xff0c;其中文含义为&#xff1a;联机事务处理&#xff1b; OLAP是Online Analysis Processing的缩写&#xff0c;其中文含义为&#xff1a;联机分析处理。 上世纪60年代&#xff0c;关系数据库之父E.F.Codd提出了关系模…

URL重定向漏洞

URL重定向漏洞 1. URL重定向1.1. 漏洞位置 2. URL重定向基础演示2.1. 查找漏洞2.1.1. 测试漏洞2.1.2. 加载完情况2.1.3. 验证漏洞2.1.4. 成功验证 2.2. 代码修改2.2.1. 用户端代码修改2.2.2. 攻击端代码修改 2.3. 利用思路2.3.1. 用户端2.3.1.1. 验证跳转 2.3.2. 攻击端2.3.2.1…

Stable Diffusion 提示词入门指南

前言 本文主要讲解 Stable Diffusion &#xff08;下文简称 SD&#xff09;提示词的用法&#xff0c;帮助大家生成更高质量的图片 本章节主要讲解文生图&#xff0c;其他类型读者可以自行探索。同时本文主要是以 Stable Diffusion Discard 的形式生成图片 如果各位对于图片隐…

leetcode idea debug 刷题debug神器

目录 1 安装leetcode editor插件2 创建刷题目录和包3 配置配置插件模板4 参考 配置好后debug效果&#xff0c;可以一步步debug设置断点查看各个变量的变化&#xff0c;也可以跳转到对应的方法查看源码&#xff01;&#xff01;&#xff01; 1 安装leetcode editor插件 这个直接…

小赢科技,寻找金融科技核心价

如果说金融是经济的晴雨表&#xff0c;是通过改善供给质量以提高经济质量的切入口&#xff0c;那么金融科技公司&#xff0c;就是这一切行动的推手。上半年&#xff0c;社会经济活跃程度提高背后&#xff0c;金融科技公司既是奉献者&#xff0c;也是受益者。 8月29日&#xff0…

智慧景区方案:AI与视频融合技术如何助力景区监管智能化升级?

随着经济的发展&#xff0c;人们对生活的需求也不再局限于温饱层面&#xff0c;越来越多的人们开始追求文化、艺术的高层次需求&#xff0c;旅游也逐渐成为人们日常放松的一种方式。由于我国人口多、易扎堆等特点&#xff0c;景区的运营监管方式也亟需改革。TSINGSEE青犀智能分…

118.杨辉三角

一、题目 118. 杨辉三角 - 力扣&#xff08;LeetCode&#xff09; 二、代码 class Solution { public:vector<vector<int>> generate(int numRows) {vector<vector<int>>data(numRows);for(int i0;i<numRows;i){data[i].resize(i1);//扩容data[i]…

java解析html

目录 场景描述一.引入依赖二.调用接口响应回来的html三.测试代码 场景描述 我调用外部接口&#xff0c;但是返回来的数据是html的格式&#xff0c;所以我就需要进行处理来获得我想要的数据。我使用的是jsoup。 一.引入依赖 <dependency><groupId>org.jsoup</gr…

C++:初始化表总结

1.为成员变量赋值2.指定基类的构造函数3.指定组合对象所在类的构造函数

Vue.js安装步骤和注意事项

安装完node.js后开始安装和部署Vue在检查webpack的下载版本时出现错误出现错误的原因是之前下载时未指定对应的版本号导致版本不兼容先卸载掉之前下载的版本 cnpm uninstall webpack-cli -g cnpm install webpack-cli4.9.2 -g 最后检查版本是否对应

【不会用这个工具,你的Linux服务器就是个摆设!】

01 Tcpdump Tcpdump 是一个强大的网络监控工具&#xff0c;它允许用户有效地过滤网络上的数据包和流量。 这可以获得有关 TCP/IP 和网络上传输的数据包的详细信息。 当你遇到网络协议问题一筹莫展的时候&#xff0c;这时候往往可以通过tcpdump来看网络的通讯过程中发生了什么…

Android Glide preload RecyclerView切入后台不可见再切换可见只加载当前视野可见区域item图片,Kotlin

Android Glide preload RecyclerView切入后台不可见再切换可见只加载当前视野可见区域item图片&#xff0c;Kotlin <uses-permission android:name"android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name"android.permission.RE…

C++学习记录——삼십 智能指针

文章目录 1、为什么需要智能指针&#xff1f;2、内存泄漏3、智能指针的使用及原理1、RAII思想2、拷贝问题1、unique_ptr2、shared_ptr1、多线程2、循环引用3、定制删除器 1、为什么需要智能指针&#xff1f; 看一个场景 int div() {int a, b;cin >> a >> b;if (b…

初探---Qt

目录 一、介绍Qt 二、软件安装 三、QT工具介绍 四、Assistant帮助文档的使用 五、设计师界面的介绍 ​编辑 六、QT工程项目各文件初始程序的介绍 1> 配置文件&#xff1a;.pro文件 2> 头文件 3> 源文件 4> 主程序 5> 各文件之间调用方式 七、第一个…

一篇文章搞定《WebView的优化及封装》

一篇文章搞定《WebView的优化及封装》 前言WebView的过程分析确定优化方案一、预加载&#xff0c;复用缓冲池&#xff08;初始化优化&#xff09;优化的解析说明具体的实现 二、预置模版&#xff08;请求、渲染优化&#xff09;优化的解析说明具体的实现1、离线包2、预获取数据…

【云原生进阶之PaaS中间件】第一章Redis-2.3.3集群模式

1 集群模式 Redis集群是一个提供在多个Redis节点之间共享数据的程序集。它并不像Redis主从复制模式那样只提供一个master节点提供写服务,而是会提供多个master节点提供写服务,每个master节点中存储的数据都不一样,这些数据通过数据分片的方式被自动分割到不同的master节点上…

手写实现call() apply() bind()函数,附有详细注释,包含this指向、arguments讲解

手写实现call() apply() bind()函数是很经典的问题&#xff0c;但是能掰扯清楚的文章确实不算多&#xff0c;于是笔者才决定写下本文&#xff0c;希望能给读者带来一些启发&#xff0c;如有错误欢迎指正。 目录 补充知识 函数中的this指向 类数组对象arguments call() 原理…