🏠️ 项目仓库:易售校园二手平台
📙 项目介绍:【易售校园二手平台】开源说明(包含项目介绍、界面展示与系列文章集合)

“我的”界面修改

效果

在这里插入图片描述

界面实现

界面的实现使用了一张png图片,图片直接使用PS制作一张即可,资源下载可以查看易售小程序我的界面上方背景

在这里插入图片描述

将图片放到项目的静态资源文件夹下面

在这里插入图片描述

使用下方的代码来设置页面的背景图片

.background {
	background-image: url("@/static/uniappMineBackground.png");
	background-repeat: no-repeat;
	background-size: 100%;
	width: 100%;
	padding-top: 20px;
	margin-bottom: 25rpx;
}

要想实现元素悬浮在背景图片上面的感觉,只需要修改一下元素的透明度即可,如下面的代码

.top {
	background: rgba(255, 255, 255, 0.5);
	border-radius: 15px;
	box-shadow: 5rpx 10rpx 20rpx rgba(0, 0, 0, 0.2);
	margin: 0px 20rpx;
	height: 350rpx;
}

界面整体代码

<template>
	<view class="container">
		<view class="background">
			<view class="top">
				<!-- 头像、昵称展示 -->
				<view class="userDisplay">
					<view class="avatar" @click="this.avatarChangeShow=true">
						<u--image :src="userInfo.avatar" width="120rpx" height="120rpx" shape="circle"
							:lazy-load="true">
							<view slot="error" style="font-size: 24rpx;">加载失败</view>
						</u--image>
					</view>
					<view class="nameView">
						<view>
							<view style="font-weight: bold;font-size: 36rpx;color: #000000;">{{userInfo.nickName}}
							</view>
							<view style="font-size: 30rpx;display: flex;margin-top: 10rpx;color: #6f6d71;">
								<!-- <text class="iconfont" style="margin-right: 7rpx;color: #ffffff;">&#xe654;</text> -->
								<text class="selfIntroductionText">简介:{{userInfo.selfIntroduction}}</text>
							</view>
						</view>
					</view>
				</view>
				<view class="buttonView">
					<view class="buttonItem" @click="goToMyPublish">
						<text class="iconfont" style="margin-right: 15rpx;font-size: 50rpx;">&#xe613;</text>
						我发布的
					</view>
					<!-- 一条竖线 -->
					<view style="border-right: #2b92ff solid 1px;height: 40rpx;">
					</view>
					<view class="buttonItem">
						<text class="iconfont" style="margin-right: 15rpx;font-size: 50rpx;">&#xe638;</text>
						我的足迹
					</view>
				</view>
			</view>

		</view>

		<view class="userMessage">
	<!-- 		<view style="font-size: 18px;font-weight: bold;	padding: 20rpx;">
				<text>我的信息</text>
			</view> -->
			<u-cell-group>
				<u-cell name="userName" :value="userInfo.userName" :isLink="true"
					@click="editMessage('userName',userInfo.userName,'用户名')">
					<view slot="title" class="u-slot-title">
						<text class="iconfont" style="margin-right: 10rpx;">&#xe605;</text>
						<text class="u-cell-text">用户名</text>
					</view>
				</u-cell>
				<u-cell name="nickName" :value="userInfo.nickName" :isLink="true"
					@click="editMessage('nickName',userInfo.nickName,'昵称')">
					<view slot="title" class="u-slot-title">
						<text class="iconfont" style="margin-right: 10rpx;">&#xe605;</text>
						<text class="u-cell-text">昵称</text>
					</view>
				</u-cell>
				<u-cell name="avatar" :isLink="true" @click="editMessage('avatar',userInfo.avatar,'头像')">
					<view slot="title" class="u-slot-title">
						<text class="iconfont" style="margin-right: 10rpx;">&#xe60d;</text>
						<text class="u-cell-text">头像</text>
					</view>
				</u-cell>
				<u-cell name="schoolName" :value="userInfo.schoolName" :isLink="true"
					@click="editMessage('schoolId',userInfo.schoolName,'大学',userInfo.schoolId)">
					<view slot="title" class="u-slot-title">
						<text class="iconfont" style="margin-right: 10rpx;">&#xe916;</text>
						<text class="u-cell-text">大学</text>
					</view>
				</u-cell>
				<u-cell name="campusName" :value="userInfo.campusName" :isLink="true"
					@click="editMessage('campusId',userInfo.campusName,'校区',userInfo.campusId,userInfo.schoolId)">
					<view slot="title" class="u-slot-title">
						<text class="iconfont" style="margin-right: 10rpx;">&#xe628;</text>
						<text class="u-cell-text">校区</text>
					</view>
				</u-cell>
				<u-cell name="sex" :value="sexName" :isLink="true" @click="editMessage('sex',userInfo.sex,'性别')">
					<view slot="title" class="u-slot-title">
						<text class="iconfont" style="margin-right: 10rpx;">&#xe614;</text>
						<text class="u-cell-text">性别</text>
					</view>
				</u-cell>
				<u-cell name="email" :value="userInfo.email" :isLink="true"
					@click="editMessage('email',userInfo.email,'邮箱')">
					<view slot="title" class="u-slot-title">
						<text class="iconfont" style="margin-right: 10rpx;">&#xe672;</text>
						<text class="u-cell-text">邮箱</text>
					</view>
				</u-cell>
				<u-cell name="contactInformation" :isLink="true"
					@click="editMessage('contactInformation',userInfo.contactInformation,'联系方式')">
					<view slot="title" class="u-slot-title">
						<text class="iconfont" style="margin-right: 10rpx;">&#xe637;</text>
						<text class="u-cell-text">联系方式</text>
					</view>
				</u-cell>
				<u-cell name="selfIntroduction" :isLink="true"
					@click="editMessage('selfIntroduction',userInfo.selfIntroduction,'自我介绍')">
					<view slot="title" class="u-slot-title">
						<text class="iconfont" style="margin-right: 10rpx;">&#xe654;</text>
						<text class="u-cell-text">自我介绍</text>
					</view>
				</u-cell>
				<u-cell name="password" :isLink="true" @click="editMessage('password','-1','修改密码')">
					<view slot="title" class="u-slot-title">
						<text class="iconfont" style="margin-right: 10rpx;">&#xe603;</text>
						<text class="u-cell-text">修改密码</text>
					</view>
				</u-cell>
				<u-cell :isLink="true" @click="logout()">
					<view slot="title" class="u-slot-title">
						<text class="iconfont" style="margin-right: 10rpx;">&#xe659;</text>
						<text class="u-cell-text">退出账号</text>
					</view>
				</u-cell>
			</u-cell-group>
		</view>
	</view>
</template>

<script>
	import {
		logout
	} from "@/api/login";
	import {
		getUserProfileVo
	} from "@/api/user";

	export default {

		data() {
			return {
				userInfo: {
					avatar: '',
					nickName: "你好呀",
					userName: "admin",
					schoolName: "XX大学",
					campusName: "XX学院",
					sex: 0,
					selfIntroduction: "自我介绍,打撒活动啊速宏达搜好滴傻大搜到阿斯顿撒旦好骚",
					contactInformation: "联系方式",
					email: "32136712361@qq.com"
				},
				sexName: '其他',
			}
		},
		created() {
			// this.getUserProfile();
		},
		onShow: function() {
			console.log("查询个人信息")
			this.getUserProfile();
		},

		methods: {
			editMessage(editKey, currentValue, editName, valueId = undefined, schoolId = undefined) {
				uni.navigateTo({
					url: "/pages/my/profileEdit?editKey=" + editKey + "&currentValue=" + currentValue +
						"&editName=" + editName + "&valueId=" + valueId + "&schoolId=" + schoolId
				})
			},
			/**
			 * 登出账号
			 */
			logout() {
				// console.log("退出账号:")
				logout().then(res => {
					// console.log("退出账号:" + JSON.stringify(res));
					// 清除所有缓存
					uni.clearStorageSync();
					// 跳转到登录页
					uni.redirectTo({
						url: "/pages/login/login"
					})
				})
			},
			/**
			 * 获取用户信息
			 */
			getUserProfile() {
				getUserProfileVo().then(res => {
					// console.log("getUserProfile:" + JSON.stringify(res));
					this.userInfo = res.data;
					this.sexName = this.getSexName(this.userInfo.sex);
					console.log("头像:" + this.userInfo.avatar);
					// console.log("this.userInfo.sex:" + this.userInfo.sex + ",this.sexName:" + this.sexName);
					// console.log("this.userInfo:" + JSON.stringify(this.userInfo));
				})
			},
			getSexName(type) {
				if (type == 0) {
					return "男";
				} else if (type == 1) {
					return "女";
				} else if (type == 2) {
					return "其他";
				}
			},
			/**
			 * 查看我的发布
			 */
			goToMyPublish(){
				uni.navigateTo({
					url:"/pages/myPublish/myPublish"
				})
			}
		}
	}
</script>

<style lang="scss">
	.container {
		background: #F4F5F7;
		min-height: 100vh;
		font-family: sans-serif;

		.background {
			background-image: url("@/static/uniappMineBackground.png");
			background-repeat: no-repeat;
			background-size: 100%;
			width: 100%;
			// height: 150px;
			padding-top: 20px;
			margin-bottom: 25rpx;

			.top {
				background: rgba(255, 255, 255, 0.5);
				border-radius: 15px;
				box-shadow: 5rpx 10rpx 20rpx rgba(0, 0, 0, 0.2);
				margin: 0px 20rpx;
				height: 350rpx;

				.userDisplay {
					display: flex;
					padding: 25rpx;

					.avatar {
						display: flex;
						justify-content: center;
						align-items: center;
						margin-right: 20rpx;
						background: #ffffff;
						border-radius: 50%;
						padding: 3px;
					}

					.nameView {
						display: flex;
						justify-content: center;
						align-items: center;

						.selfIntroductionText {
							overflow: hidden;
							text-overflow: ellipsis;
							display: -webkit-box;
							/* 显示1行 */
							-webkit-line-clamp: 1;
							-webkit-box-orient: vertical;
						}
					}
				}

				.buttonView {
					display: flex;
					height: 180rpx;
					align-items: center;

					.buttonItem {
						flex: 1;
						display: flex;
						align-items: center;
						justify-content: center;
						font-size: 32rpx;
					}
				}
			}

		}

		.userMessage {
			margin: 0rpx 20rpx;
			background: #F4F5F7;
			border-radius: 20rpx;
		}
	}
</style>

查看已发布商品

界面效果

在这里插入图片描述

商品数据表

在这里插入图片描述

因为使用了status字段来表示了商品的状态,直接设置不同的status就可以分别查询出在售草稿或者已下架的商品

后端

上架、下架商品

实现产品状态的切换非常简单,直接设置状态并发送请求即可。要注意的是,后台要判断执行修改的用户是不是当前登录的用户,防止用户直接绕过前端发请求随意修改其他用户的商品

Controller
/**
 * 修改商品
 */
@PreAuthorize("@ss.hasPermi('market:product:edit')")
@Log(title = "商品", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody Product product) {
    // 设置当前登录用户的用户id
    product.setUserId(getLoginUser().getUserId());
    if (product.getStatus() == 0) {
        // 如果想要将商品改成发布状态,需要对数据库的商品进行校验
        Product productInDatabase = productService.getById(product.getId());
        ProductValidate.validate(productInDatabase);
    }
    return toAjax(productService.updateProduct(product));
}
Mapper

通过添加and user_id = #{userId},确保修改人为商品主人才能修改商品信息

<update id="updateProduct" parameterType="Product">
    update product
    <trim prefix="SET" suffixOverrides=",">
        <if test="createTime != null">create_time = #{createTime},</if>
        <if test="updateTime != null">update_time = #{updateTime},</if>
        <if test="isDeleted != null">is_deleted = #{isDeleted},</if>
        <if test="name != null and name != ''">name = #{name},</if>
        <if test="description != null">description = #{description},</if>
        <if test="originalPrice != null">original_price = #{originalPrice},</if>
        <if test="price != null">price = #{price},</if>
        <if test="productCategoryId != null">product_category_id = #{productCategoryId},</if>
        <if test="userId != null">user_id = #{userId},</if>
        <if test="reviewerId != null">reviewer_id = #{reviewerId},</if>
        <if test="fineness != null">fineness = #{fineness},</if>
        <if test="number != null and number != ''">number = #{number},</if>
        <if test="unit != null and unit != ''">unit = #{unit},</if>
        <if test="status != null">status = #{status},</if>
        <if test="isContribute != null">is_contribute = #{isContribute},</if>
        <if test="functionalStatus != null">functional_status = #{functionalStatus},</if>
        <if test="brandId != null">brand_id = #{brandId},</if>
    </trim>
    where id = #{id} and user_id = #{userId}
</update>

界面整体代码

<template>
	<view class="outside">
		<u-toast ref="uToast"></u-toast>
		<u-tabs :list="tabNameList" @click="tabClick"></u-tabs>
		<view class="container">
			<u-empty mode="data" icon="http://cdn.uviewui.com/uview/empty/data.png" v-if="productVoList.length==0"
				:text="emptyText">
			</u-empty>

			<view v-for="(productVo,index) in productVoList" class="item">
				<view class="productItem">
					<u--image v-if="productVo.picList!=null&&productVo.picList.length>0" :showLoading="true"
						:src="productVo.picList[0].address" width="200rpx" height="200rpx" radius="10" mode="aspectFill"
						:fade="true" duration="450" @click="seeProductDetail(productVo)">
						<view slot="error" style="font-size: 24rpx;">加载失败</view>
					</u--image>
					<view style="margin: 10rpx;"></view>
					<view class="productMessage" style="width: 100%;">
						<view @click="seeProductDetail(productVo)">
							<view class="productTitle">{{productVo.name}}</view>
							<view class="price">¥<text class="number">{{productVo.price}}</text></view>
						</view>
						<view style="display: flex; justify-content: space-between;align-items: center;width: 100%;">
							<view style="font-size: 28rpx;color: #B6B6B6;">
								{{formatDateToString(productVo.createTime)}}
							</view>
							<view style="display: flex;align-items: center;">
								<view class="buttonView" @click="editProduct(productVo)"
									style="border: #00BFFF solid 1px;color: #00BFFF;">编 辑</view>
								<view style="width: 15rpx;"></view>
								<view class="buttonView" @click="offShelf(productVo.id)"
									style="border: #FF5A5F  solid 1px;color: #FF5A5F;" v-if="productStatus==0">下 架
								</view>
								<view class="buttonView" @click="onShelf(productVo.id)"
									style="border: #76D7C4  solid 1px;color: #76D7C4;" v-if="productStatus==3">上 架
								</view>
								<view class="buttonView" @click="onShelf(productVo.id)" v-if="productStatus==2"
									style="border: #76D7C4  solid 1px;color: #76D7C4;">重新上架</view>
							</view>
						</view>
					</view>
				</view>


			</view>
		</view>

	</view>
</template>

<script>
	import {
		listProductVo,
		updateProduct
	} from "@/api/market/product.js";

	export default {
		data() {
			return {
				tabNameList: [{
					name: '在售',
				}, {
					name: '草稿',
				}, {
					name: '已下架'
				}],
				page: {
					pageNum: 1,
					pageSize: 10
				},
				productVoList: [],
				emptyText: '您还没有商品处于出售状态哟',
				// 商品状态 0:在售 1:售出 2:下架 3:草稿
				productStatus: 0,
			}
		},
		created() {
			this.listProductVo();
		},
		methods: {
			tabClick(item) {
				// console.log('item', item);
				if (item.name == "在售") {
					this.emptyText = "您还没有商品处于出售状态哟";
					this.productStatus = 0;
				} else if (item.name == "草稿") {
					this.emptyText = "您的草稿箱是空的";
					this.productStatus = 3;
				} else if (item.name == "已下架") {
					this.emptyText = "您还没有下架过商品哟";
					this.productStatus = 2;
				}
				this.listProductVo();
			},
			/**
			 * 获取商品
			 */
			listProductVo() {
				listProductVo({
					userId: uni.getStorageSync("curUser").userId,
					status: this.productStatus
				}, this.page).then(res => {
					console.log("listProductVo:" + JSON.stringify(res))
					this.productVoList = res.data.pageMes.rows;
				})
			},
			/**
			 * 查看商品的详情
			 */
			seeProductDetail(productVo) {
				uni.navigateTo({
					url: "/pages/product/detail?productVo=" + encodeURIComponent(JSON.stringify(productVo))
				})
			},
			/**
			 * 格式化日期
			 * @param {Object} date
			 */
			formatDateToString(dateStr) {
				let date = new Date(dateStr);
				// 今天的日期
				let curDate = new Date();
				if (date.getFullYear() == curDate.getFullYear() && date.getMonth() == curDate.getMonth() && date
					.getDate() == curDate.getDate()) {
					// 如果和今天的年月日都一样,那就只显示时间
					return this.toDoubleNum(date.getHours()) + ":" + this.toDoubleNum(date.getMinutes());
				} else {
					// 如果年份一样,就只显示月日
					return (curDate.getFullYear() == date.getFullYear() ? "" : (date.getFullYear() + "-")) + this
						.toDoubleNum((
							date
							.getMonth() + 1)) +
						"-" +
						this.toDoubleNum(date.getDate());
				}
			},
			/**
			 * 如果传入的数字是两位数,直接返回;
			 * 否则前面拼接一个0
			
			 * @param {Object} num
			 */
			toDoubleNum(num) {
				if (num >= 10) {
					return num;
				} else {
					return "0" + num;
				}
			},
			/**
			 * 编辑商品
			 */
			editProduct(productVo) {
				uni.navigateTo({
					url: "/pages/sellMyProduct/sellMyProduct?productVo=" + encodeURIComponent(JSON.stringify(
						productVo))
				})
			},
			/**
			 * 下架商品

			 * @param {Object} productId
			 */
			offShelf(productId) {
				let product = {
					id: productId,
					status: 2
				};
				updateProduct(product).then(res => {
					this.$refs.uToast.show({
						type: 'success',
						message: "下架成功",
						duration: 500
					});
					this.listProductVo();
				})
			},
			/**
			 * 上架商品

			 * @param {Object} productId
			 */
			onShelf(productId) {
				let product = {
					id: productId,
					status: 0
				};
				updateProduct(product).then(res => {
					this.$refs.uToast.show({
						type: 'success',
						message: "上架成功",
						duration: 500
					});
					this.listProductVo();
				})
			},
			/**
			 * 跳转回来所执行的方法
			 */
			back() {
				// 重新获取一遍数据
				this.listProductVo();
			}
		}
	}
</script>

<style lang="scss">
	.outside {
		// background-color: #0093E9;
		// background-image: linear-gradient(160deg, #0093E9 0%, #80D0C7 100%);

		font-family: sans-serif;
	}

	.container {
		padding: 25rpx;

		.item {
			background: rgba(255, 255, 255, 1.0);
			border-radius: 5px;
			box-shadow: 10rpx 10rpx 20rpx rgba(234, 235, 236, 1.0),
				inset 1rpx 1rpx 1rpx rgba(234, 235, 236, 0.7);
			padding: 30rpx;
			margin-bottom: 30rpx;

			.productItem {
				display: flex;

				.productMessage {
					display: flex;
					flex-direction: column;
					justify-content: space-between;

					.productTitle {
						font-weight: 500;
						margin-bottom: 10rpx;
						// color: #ffffff;
					}

					.price {
						color: #F84442;
						// font-weight: bold;

						.number {
							font-size: 20px;
						}
					}
				}

			}

			.buttonView {
				padding: 10rpx 20rpx;
				color: #B6B6B6;
				border-radius: 10px;
				// border: #B6B6B6 solid 1px;
				font-size: 24rpx;
				font-weight: bold;
			}
		}

	}
</style>

back方法

这里写了一个back方法,但是这个页面却没有调用过该方法,写这个方法是何用意呢?其实,这个方法是给其他页面调用的。当用户修改商品时,会跳转到商品编辑发布的页面(即sellMyProduct页面),当商品信息修改结束之后,执行uni.navigateBack();返回到上一个页面,这时可以先执行相应页面的back方法再进行跳转。这样开发的优点是:跳转到sellMyProduct页面的前置页面不只有一个,如果指定跳转页面的话,需要写一些判断逻辑来判断要跳转到哪个页面,同时还需要传参来告诉所跳转到的页面要执行什么样的逻辑。而如果使用了back方法,可以将方法的实现交给页面本身,如果页面不需要执行逻辑,则直接将方法留空,调用者不需要管back如何实现,只需要调用即可,这样更加方便代码的维护和拓展。调用者的具体代码如下:

// 获取上一页
let pages = getCurrentPages();
let prevPage = pages[pages.length - 2];
// 调用上一页的返回方法
prevPage.$vm.back();
uni.navigateBack();

编辑商品、商品发布、保存草稿

后端

后端没有什么特殊的地方,只需要区分一下商品发布和草稿保存的校验过程即可

商品校验方法

package com.shm.dataValidate;

import com.ruoyi.common.core.domain.entity.Product;
import com.ruoyi.common.enums.ErrorCode;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;

import java.math.BigDecimal;

/**
 * @Author dam
 * @create 2023/9/2 17:14
 */
public class ProductValidate {
    public static void validate(Product product) {
        if (StringUtils.isEmpty(product.getName())) {
            throw new ServiceException("请填写商品名称", ErrorCode.OPERATION_ERROR.getCode());
        } else if (StringUtils.isEmpty(product.getDescription())) {
            throw new ServiceException("请填写商品描述", ErrorCode.OPERATION_ERROR.getCode());
        } else if (product.getOriginalPrice() == null) {
            throw new ServiceException("请填写商品原价", ErrorCode.OPERATION_ERROR.getCode());
        } else if (product.getPrice() == null) {
            throw new ServiceException("请填写商品现价", ErrorCode.OPERATION_ERROR.getCode());
        } else if (product.getProductCategoryId() == null) {
            throw new ServiceException("请选择商品分类", ErrorCode.OPERATION_ERROR.getCode());
        } else if (product.getFineness() == null) {
            throw new ServiceException("请选择商品成色", ErrorCode.OPERATION_ERROR.getCode());
        } else if (product.getNumber() == null) {
            throw new ServiceException("请填写商品数量", ErrorCode.OPERATION_ERROR.getCode());
        } else if (StringUtils.isEmpty(product.getUnit())) {
            throw new ServiceException("请填写商品单位", ErrorCode.OPERATION_ERROR.getCode());
        } else if (product.getFunctionalStatus() == null) {
            throw new ServiceException("请选择商品功能状态", ErrorCode.OPERATION_ERROR.getCode());
        }

        if (product.getPrice().compareTo(product.getOriginalPrice()) == 1) {
            throw new ServiceException("商品的销售价格比原价高,请重新填写", ErrorCode.OPERATION_ERROR.getCode());
        }
        if (!validateNumber(product.getPrice())) {
            throw new ServiceException("商品出售价格输入有误,需要是正数且只有两位小数", ErrorCode.OPERATION_ERROR.getCode());
        }
        if (!validateNumber(product.getOriginalPrice())) {
            throw new ServiceException("商品原价输入有误,需要是正数且只有两位小数", ErrorCode.OPERATION_ERROR.getCode());
        }
    }

    /**
     * 校验价格是否符合要求
     * 正数
     * 小数点后面最多只有两位小数
     * @param number
     * @return
     */
    private static boolean validateNumber(BigDecimal number) {
        // 判断是否为正数
        if (number.compareTo(BigDecimal.ZERO) <= 0) {
            return false;
        }

        // 判断小数位数是否超过两位
        BigDecimal fractionalPart = number.remainder(BigDecimal.ONE);
        if (fractionalPart.scale() > 2) {
            return false;
        }

        return true;
    }
}

Controller

/**
 * 上传商品
 */
@PreAuthorize("@ss.hasPermi('market:product:add')")
@Log(title = "商品", businessType = BusinessType.INSERT)
@PostMapping("/uploadSellProduct")
// 因为操作了多个数据表,添加事务注解,要么都成功,要么都失败
@Transactional
public AjaxResult uploadSellProduct(@RequestBody ProductVo productVo) {
    /// 存储商品
    Product product = new Product();
    BeanUtils.copyProperties(productVo, product);

    if (product.getStatus() == 0) {
        //--if-- 如果是要发布商品,要先进行数据校验,确保商品的数据都是合法的
        ProductValidate.validate(product);
        if (product.getId() == null) {
            // 设置商品主人
            product.setUserId(getLoginUser().getUserId());
            productService.insertProduct(product);
        } else {
            // 更新商品信息
            productService.updateProduct(product);
        }

        if (productVo.getPicList() == null || productVo.getPicList().size() == 0) {
            throw new ServiceException("商品没有上传对应的图片", ErrorCode.OPERATION_ERROR.getCode());
        }
    } else if (product.getStatus() == 3) {
        if (StringUtils.isEmpty(product.getName())) {
            throw new ServiceException("保存草稿时,商品名称不能为空", ErrorCode.OPERATION_ERROR.getCode());
        }

        //--if-- 如果只是保存草稿,不需要进行数据校验
        if (product.getId() == null) {
            // 设置商品主人
            product.setUserId(getLoginUser().getUserId());
            productService.insertProduct(product);
        } else {
            // 更新商品信息
            productService.updateProduct(product);
        }
    }

    /// 存储图片
    if (product.getId() != null) {
        // 先将商品绑定的图片删除
        pictureService.deletePicturesByItem(product.getId(), 0);
    }
    for (Picture picture : productVo.getPicList()) {
        picture.setItemId(product.getId());
        picture.setType(0);
    }
    if (productVo.getPicList().size() > 0) {
        pictureService.insertPictures(productVo.getPicList());
    }

    return AjaxResult.success();
}

页面整体代码

该页面已经在【UniApp开发小程序】悬浮按钮+出售闲置商品+商品分类选择【后端基于若依管理系统开发】文章中进行了介绍,这里主要做一些微小的调整

<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">
					{{product.productCategoryName?product.productCategoryName:"请选择分类"}}
				</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>

			<view style="display: flex;margin: 0rpx 60rpx;">
				<view style="background:#A9A9A9 ;" class="buttonView" @click="saveDraft">存 草 稿</view>
				<view style="width: 40rpx;"></view>
				<view style="background:#3C9CFF ;" class="buttonView" @click="uploadSellProduct">发 布</view>
			</view>

		</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");
				this.product.productCategoryName = categoryNameList[categoryNameList.length - 1];
				uni.removeStorageSync("categoryNameList");
				uni.removeStorageSync("productCategoryId");
			}
		},
		data() {
			return {
				product: {
					id: undefined,
					name: '',
					descripption: '',
					picList: [],
					productCategoryId: undefined,
					productCategoryName: undefined,
					number: 1,
					unit: '个',
					isContribute: 0,
					originalPrice: 0.00,
					price: 0.00,
					// 成色
					fineness: 0,
					// 功能状态
					functionalStatus: 0,
					brandId: 0
				},
				value: 'dasdas',
				categoryNameList: ["选择分类"],
				finenessList: ["全新", "几乎全新", "轻微使用痕迹", "明显使用痕迹", "外观破损"],
				functionList: ["功能完好无维修", "维修过,可正常使用", "有小问题,不影响使用", "无法正常使用"],
				picList: [],
			}
		},
		onLoad(e) {
			if (e.productVo) {
				// 路由中携带了产品信息,说明是要修改产品信息
				this.product = JSON.parse(decodeURIComponent(e.productVo));
				for (var i = 0; i < this.product.picList.length; i++) {
					this.picList.push(this.product.picList[i].address);
				}
				console.log("this.product:" + JSON.stringify(this.product));
				console.log("this.picList:" + JSON.stringify(this.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));
							this.product.status = 0;
							uploadSellProduct(this.product).then(res => {
								if (!this.product.id) {
									this.$refs.uToast.show({
										type: 'success',
										message: "您的商品已经发布到平台"
									})
								} else {
									this.$refs.uToast.show({
										type: 'success',
										message: "您的商品修改并发布成功"
									})
								}
								setTimeout(() => {
									// 获取上一页
									let pages = getCurrentPages();
									let prevPage = pages[pages.length - 2];
									// 调用上一页的返回方法
									prevPage.$vm.back();
									uni.navigateBack();
								}, 500)
							}).catch(error => {
								console.log("error:" + JSON.stringify(error));
								this.$refs.uToast.show({
									type: 'error',
									message: "商品发布失败"
								})
							});
						});

					}
				} else {
					this.$refs.uToast.show({
						type: 'error',
						message: "请选择分类"
					})
				}
			},
			/**
			 * 保存商品草稿
			 */
			saveDraft() {
				this.setPicAspectRatio().then(() => {
					// console.log("即将上传的商品:" + JSON.stringify(this.product));
					this.product.status = 3;
					uploadSellProduct(this.product).then(res => {
						this.$refs.uToast.show({
							type: 'success',
							message: "您的草稿保存成功"
						})
						setTimeout(() => {
							// 获取上一页
							let pages = getCurrentPages();
							let prevPage = pages[pages.length - 2];
							// 调用上一页的返回方法
							prevPage.$vm.back();
							uni.navigateBack();
						}, 500)
					}).catch(error => {
						// console.log("error:" + JSON.stringify(error));
						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;
		font-family: sans-serif;

		.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: 15rpx;
					margin-right: 15px;
					border-radius: 5px;
				}

				.selectTextClass {
					display: inline;
					background: #2B92FF;
					padding: 15rpx;
					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;
				}

			}

			.buttonView {
				padding: 20rpx;
				flex: 1;
				text-align: center;
				border-radius: 10rpx;
				color: #ffffff;
				font-weight: bold;
			}

		}
	}
</style>

同项目其他文章

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

Logo

快速构建 Web 应用程序

更多推荐