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

界面效果

【悬浮按钮】

在这里插入图片描述
【闲置商品描述信息填写界面】

在这里插入图片描述
【商品分类选择界面】

在这里插入图片描述
【分类选择完成】
在这里插入图片描述

界面实现

悬浮按钮实现

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

【悬浮按钮组件】

<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;
},

同项目其他文章

该项目的其他文章请查看【易售小程序项目】项目介绍、小程序页面展示与系列文章集合

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hello Dam

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值