文章目录
- 界面效果
- 界面实现
- 悬浮按钮实现
- 商品分类选择界面
- 使元素均匀分布
- 闲置商品描述信息填写界面
- 价格校验
界面效果
【悬浮按钮】
【闲置商品描述信息填写界面】
【商品分类选择界面】
【分类选择完成】
界面实现
悬浮按钮实现
悬浮按钮漂浮于页面之上,等页面滑动时,悬浮按钮的位置相对于屏幕不会改变
【悬浮按钮组件】
<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;
},