【易售小程序项目】修改“我的”界面前端实现;查看、重新编辑、下架自己发布的商品【后端基于若依管理系统开发】

简介: 【易售小程序项目】修改“我的”界面前端实现;查看、重新编辑、下架自己发布的商品【后端基于若依管理系统开发】

“我的”界面修改

效果

界面实现

界面的实现使用了一张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>


目录
相关文章
|
2天前
|
存储 JavaScript Java
后端开发的艺术:从新手到专家的旅程
在数字化时代,后端开发是构建现代应用程序不可或缺的一部分。本文将探讨后端开发的核心概念、技术栈选择、最佳实践以及如何从初学者成长为专家。我们将通过一系列实用的建议和策略,帮助读者理解并掌握后端开发的精髓,从而在这个充满挑战和机遇的领域中取得成功。
|
1天前
|
缓存 负载均衡 安全
后端开发的艺术:构建高效、可扩展的API
在现代软件开发中,后端开发扮演着至关重要的角色。它不仅负责处理数据存储、业务逻辑和安全性,还需要提供高效、可扩展的API供前端和其他服务使用。本文将深入探讨后端开发的关键概念和技术,帮助读者了解如何构建高效、可扩展的API,并提供一些实用的建议和最佳实践。
|
2天前
|
关系型数据库 API 数据库
后端开发的艺术:从零到一构建高效服务器
在数字化时代,后端开发是支撑现代互联网应用的基石。本文旨在探讨后端开发的核心概念、关键技术以及如何构建一个高效的服务器。我们将从基础的编程语言选择开始,逐步深入到数据库设计、API开发和性能优化等关键领域。通过实际案例分析,我们将揭示后端开发的复杂性和挑战性,同时提供实用的解决方案和最佳实践。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的见解和启发。
|
1天前
|
NoSQL Java API
后端开发的艺术:从初学者到高手的旅程
在这个数字时代,后端开发是构建现代应用程序不可或缺的一部分。本文旨在为初学者提供一条清晰的路径,从理解后端开发的基本概念开始,逐步深入到掌握高级技能。我们将探讨后端开发的核心技术,如编程语言、框架、数据库和API设计,并讨论如何通过实际项目经验来提升技能。此外,我们还将介绍一些实用的学习资源和社区,帮助读者在后端开发的旅途中不断进步。
|
1天前
|
JavaScript Java 云计算
后端开发的演变与未来趋势
在数字化时代的浪潮中,后端开发扮演着至关重要的角色。本文将探讨后端技术的历史演变、当前主流技术和框架、以及面临的挑战和未来的发展趋势。通过深入浅出的方式,为读者揭示后端开发的奥秘,并启发对未来技术的思考。
|
1天前
|
关系型数据库 测试技术 API
探索后端开发:构建高效API的艺术
【10月更文挑战第25天】在数字化时代,后端开发不仅仅是编写代码那么简单。它是连接用户与数据的桥梁,是实现业务逻辑的基石。本文将深入探讨如何构建高效的API,从理解RESTful原则到选择合适的框架,再到处理数据库交互,每一步骤都是精心策划的舞蹈。我们将通过实际案例,揭示如何在保证性能和安全性的同时,提供流畅的用户体验。让我们一起走进后端开发的世界,发现那些隐藏在代码背后的智慧和创造力。
|
2天前
|
前端开发 JavaScript NoSQL
探索后端开发之旅:从基础到高级实战
【10月更文挑战第24天】在这个数字时代的浪潮中,后端开发如同一座巨大的宝藏岛,等待着勇敢的探险者去发掘。本文将作为你的藏宝图,引领你从浅滩走向深海,探索后端开发的广阔天地。无论你是初心者还是资深开发者,这篇文章都将为你提供价值连城的知识和技能。准备好了吗?让我们启航,一起构建强大、高效、安全的后端系统!
|
10天前
|
缓存 Java 数据库
后端开发的艺术与科学
在这篇文章中,我们将深入探讨后端开发的核心理念和技术。从基础的编程语言和框架到复杂的系统架构和性能优化,我们将一探究竟。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和实用的技巧。让我们一起走进后端开发的世界,探索它的艺术与科学。
|
10天前
|
Java 持续交付 微服务
后端开发中的微服务架构实践与挑战####
本文深入探讨了微服务架构在现代后端开发中的应用,通过具体案例分析,揭示了其如何助力企业应对业务复杂性、提升系统可维护性和可扩展性。文章首先概述了微服务的核心概念及其优势,随后详细阐述了实施微服务过程中的关键技术选型、服务拆分策略、容错机制以及持续集成/持续部署(CI/CD)的最佳实践。最后,通过一个真实世界的应用实例,展示了微服务架构在实际项目中的成功应用及其带来的显著成效。 ####
|
3天前
|
缓存 运维 监控
后端开发中的微服务架构实践与挑战#### 一、
【10月更文挑战第22天】 本文探讨了微服务架构在后端开发中的应用实践,深入剖析了其核心优势、常见挑战及应对策略。传统后端架构难以满足快速迭代与高可用性需求,而微服务通过服务拆分与独立部署,显著提升了系统的灵活性和可维护性。文章指出,实施微服务需关注服务划分的合理性、通信机制的选择及数据一致性等问题。以电商系统为例,详细阐述了微服务改造过程,包括用户、订单、商品等服务的拆分与交互。最终强调,微服务虽优势明显,但落地需谨慎规划,持续优化。 #### 二、

热门文章

最新文章