uniapp-商城-55-后台 新增商品(分类、验证和弹窗属性)

发布于:2025-05-13 ⋅ 阅读:(14) ⋅ 点赞:(0)

1、概述

        在前面 ,我们将商品页面的布局给完成了,这里来对表单的标签输入进行校验,看看这里的校验还是不是也需要兼容微信小程序,还有没有前面遇到的自定义正则进行校验的情况。

        另外这里还需要完成商品属性的添加,就是前面在布局中提到的弹窗。弹窗这里涉及的内容还很多,后面我们还会进一步来分析和研究。

2、表单校验

包含图片、名称、价格、类别、属性、描述等等,这里我们看到应该必须要有图片、名称、价格、类别这个几个元素

    <uni-forms ref="goodsForm" :model="goodsFormData" :rules="goodsRules" :label-width="100" label-align="right">

2.1 这里就没有删除rules ,因为我们这里没有自定义的校验规则。

2.2  必须填写的属性,页面的froms-item 中要加入 required这个属性值,js规则部分,要通过name定义其他规则,也加入required,没有就不管了,如这里的图。

2.3 要进行规则的校验,必须填写name 便于进行规则的指定

没有name,没有办法指定,见上图

2.4 data的规则数据定义。绑定到标签的 name

如这里的name 和 price  就是名称和价格的规则。写道我们表单的rules里面(goodsRules)

特别要注意书写的格式。

3、分类 使用的下拉框  云端数据获取 就这个uni-data-select 可以获取云端数据,所以用这个组件较为方便。

  需要安装,并使用云端数据获取

			<uni-forms-item label="产品分类" required name="category_id">
				<!-- 云端数据  下拉框获取  field 就是获取的内容  一定要写成 value text 这是官方定义的  value选中的值,text显示的文本,也就是value后台用,text前台用 -->
				<!-- 利用这个组件就实现了后端数据库的读取 -->
				<uni-data-select collection="green-mall-categories" field="_id as value, name as text"
					v-model="goodsFormData.category_id"></uni-data-select>
			</uni-forms-item>

4、商品属性 采用的cell的单元格(uview中组件,可以被点击)

4.1 cell

该组件就是cell单元格。一般用于一组列表的情况,比如个人中心页,设置页等,默认有一个边框,且整个可以被点击 。

title是组件显示的名称; 

isLink  就是显示一个右侧的箭头;

border:不用显示默认的边框; 注意使用加v-band(也就是加 :)

<u-cell :title="skuTitle" isLink :border="false" @click="clickSelect"></u-cell>

然后我们给这个点击加一个事件。需要-可以读5小节代码。

            //点击选择属性
            clickSelect() {
                this.$refs.attrWrapPop.open();
                if (this.skuArr.length) return;
                this.getSkuData();

            },

4.2 在这个组件下面,可以将填写的属性展示出来。

具体可以阅读,5小节的代码。

                <view class="skuList">
                    <view class="item" v-for="item in goodsFormData.sku_select" @click="clickSelect">
                        <view class="left">{{item.skuName}}:</view>
                        <view class="right">{{skuChildName(item.children)}}</view>
                    </view>
                </view>

4.3 弹出窗 使用的uni-popup

前面的章节,也对这个组件进行过研究。

/https://uniapp.dcloud.net.cn/component/uniui/uni-popup.html

在 47 48章节 。

4.3.1 第一点击cell 从底部弹出窗

注意:type要加

		<uni-popup ref="attrWrapPop" type="bottom">
			<!-- 底部弹出 type 
			 ref 是一个名字属性,便于被调用 给 clickSelect 
			 在 clickSelect 函数中调用了该接口
			 -->
			<view class="attrWrapper">
				<view class="head">
					<view class="title">商品属性</view>
					<view class="addAttr" @click="clickAddAttr()">+ 添加属性</view>
				</view>

				<view class="body">
					<view class="item" v-for="(item,index) in skuArr">
						<view class="top">
							<checkbox :checked="item.checked" @click="changeCheckbox(index)"></checkbox>
							<view class="font">{{item.skuName}}</view>
						</view>
						<view class="btnGroup" v-if="item.checked">
							<view class="btn" :class="child.checked?'active':''" v-for="(child,cIdx) in item.children"
								@click="clickChlidBtn(index,cIdx)">{{child.name}}</view>
							<view class="btn" @click="clickAddAttr(index)">
								<u-icon name="plus"></u-icon>
							</view>
						</view>
					</view>
				</view>

				<view class="foot">
					<button type="primary" @click="clickConfirmSelect">确认选择</button>
				</view>
			</view>

			<view class="safe-area-bottom"></view>
		</uni-popup>

4.3.2 就是展示可以选手属性元素 布局如下

这一部分就是显示可以选的属性值

1、属性 可以有多个,如颜色、规格、口味等,就是一个 循环 实现

        <view class="item" v-for="(item,index) in skuArr">

2、每一个属性 可以罗列多个可选项 进行选择,并可以手动添加 可选项

        2.1 分成两部分 一个top(显示属性 ) 前面有一个可选框checkbox,后面是属性名字

        <checkbox :checked="item.checked" @click="changeCheckbox(index)"></checkbox>

        <view class="font">{{item.skuName}}</view>

        2.2 一个就是可以选的属性组排列,最后跟一个图标 +号

 <view class="top">
          <checkbox :checked="item.checked" @click="changeCheckbox(index)"></checkbox>
          <view class="font">{{item.skuName}}</view>
  </view>


<view class="btnGroup" v-if="item.checked">
           <view class="btn" :class="child.checked?'active':''" v-for="(child,cIdx) in item.children"
                                @click="clickChlidBtn(index,cIdx)">{{child.name}}</view>
           <view class="btn" @click="clickAddAttr(index)">
                   <u-icon name="plus"></u-icon>
           </view>
 </view>

3、点击添加,又有点击事件--->弹出窗

                 <view class="btn" @click="clickAddAttr(index)">
                        <u-icon name="plus"></u-icon>
                 </view>

4、具体的css样式见5的代码

5、代码

<template>
	<view class="goodsView">
		<!-- 添加商品 -->
		<uni-forms ref="goodsForm" :model="goodsFormData" :rules="goodsRules" :label-width="100" label-align="right">
			<uni-forms-item label="商品图片" required="true">
				<uni-file-picker v-model="goodsFormData.thumb" fileMediatype="image" mode="grid" ></uni-file-picker>
			</uni-forms-item>

			<uni-forms-item label="商品名称" required name="name" >
				<!-- trim 去空格 -->
				<uni-easyinput v-model="goodsFormData.name" placeholder="请输入商品名称" trim="both"></uni-easyinput>
			</uni-forms-item>

			<uni-forms-item label="产品分类" required name="category_id">
				<!-- 云端数据  下拉框获取  field 就是获取的内容  一定要写成 value text 这是官方定义的  value选中的值,text显示的文本,也就是value后台用,text前台用 -->
				<!-- 利用这个组件就实现了后端数据库的读取 -->
				<uni-data-select collection="green-mall-categories" field="_id as value, name as text"
					v-model="goodsFormData.category_id"></uni-data-select>
			</uni-forms-item>

			<uni-forms-item label="商品价格" required name="price">
				<!-- trim 去空格 -->
				<uni-easyinput type="number" v-model="goodsFormData.price" placeholder="请输入商品价格"
					trim="both"></uni-easyinput>
			</uni-forms-item>

			<uni-forms-item label="商品原价">
				<!-- trim 去空格 -->
				<uni-easyinput type="number" v-model="goodsFormData.before_price" placeholder="请输入原价"
					trim="both"></uni-easyinput>
			</uni-forms-item>

			<uni-forms-item label="商品属性" >
				<u-cell :title="skuTitle" isLink :border="false" @click="clickSelect"></u-cell>
				<view class="skuList">
					<view class="item" v-for="item in goodsFormData.sku_select" @click="clickSelect">
						<view class="left">{{item.skuName}}:</view>
						<view class="right">{{skuChildName(item.children)}}</view>
					</view>
				</view>
			</uni-forms-item>

			<uni-forms-item label="商品描述">
				<!-- type 是类型  textarea 就是大框 -->
				<uni-easyinput type="textarea" placeholder="请输入详细的描述信息"
					v-model="goodsFormData.description"></uni-easyinput>
			</uni-forms-item>

			<view class="btnView">
				<button type="primary" @click="onSubmit">确认提交</button>
			</view>

		</uni-forms>


		<uni-popup ref="attrWrapPop" type="bottom">
			<view class="attrWrapper">
				<view class="head">
					<view class="title">商品属性</view>
					<view class="addAttr" @click="clickAddAttr()">+ 添加属性</view>
				</view>

				<view class="body">
					<view class="item" v-for="(item,index) in skuArr">
						<view class="top">
							<checkbox :checked="item.checked" @click="changeCheckbox(index)"></checkbox>
							<view class="font">{{item.skuName}}</view>
						</view>
						<view class="btnGroup" v-if="item.checked">
							<view class="btn" :class="child.checked?'active':''" v-for="(child,cIdx) in item.children"
								@click="clickChlidBtn(index,cIdx)">{{child.name}}</view>
							<view class="btn" @click="clickAddAttr(index)">
								<u-icon name="plus"></u-icon>
							</view>
						</view>
					</view>
				</view>

				<view class="foot">
					<button type="primary" @click="clickConfirmSelect">确认选择</button>
				</view>
			</view>

			<view class="safe-area-bottom"></view>
		</uni-popup>


		<uni-popup ref="addAttrPop">
			<uni-popup-dialog mode="input" title="新增" placeholder="请输入新增的内容"
				@confirm="dialogConfirm"></uni-popup-dialog>
		</uni-popup>
	</view>
</template>

<script>
	const skuCloudObj = uniCloud.importObject("kt-mall-sku", {
		"customUI": true
	});

	const goodsCloudObj = uniCloud.importObject("kt-mall-goods", {
		"customUI": true
	})

	export default {
		data() {
			return {
				goodsFormData: {
					thumb: [],
					name: "",
					category_id: null,
					price: null,
					before_price: null,
					description: "",
					sku_select: []
				},
				addAttrType: "parent", //parent代表父,child代表子

				goodsRules: {
					name: {
						rules: [{
							required: true,
							errorMessage: "请输入产品名称"
						}]
					},
					price: {
						rules: [{
							required: true,
							errorMessage: "请输入产品价格"
						}]
					},
					category_id: {
						rules: [{
							required: true,
							errorMessage: "请输入产品分类"
						}]
					}
				},

				skuArr: []
			};
		},

		onLoad() {

		},

		computed: {
			skuTitle() {
				if (this.goodsFormData.sku_select.length) {
					let arr = this.goodsFormData.sku_select.map(item => {
						return item.skuName
					})
					return arr.join("/")
				} else {
					return "点击添加属性"
				}
			}
		},


		methods: {
			//属性返回子元素的名称
			skuChildName(arr) {
				let nsArr = arr.map(item => {
					return item.name
				})
				return nsArr.join("/")
			},


			//点击确认选择
			clickConfirmSelect() {
				let arr = this.skuArr.filter(item => {
					let state = item.children.some(child => child.checked)
					return item.checked && state
				}).map(item => {
					let children = item.children.filter(child => {
						return child.checked
					})
					return {
						...item,
						children
					}
				})
				this.goodsFormData.sku_select = arr
				this.$refs.attrWrapPop.close();

			},
			//获取sku列表
			async getSkuData() {
				let res = await skuCloudObj.get();
				this.skuArr = res.data
				console.log(res);
			},


			//点击添加属性
			clickAddAttr(index = null) {
				if (index == null) {
					this.addAttrType = "parent"
					this.attrIndex = null
				} else {
					this.addAttrType = "child"
					this.attrIndex = index
				}
				this.$refs.addAttrPop.open();
			},
			//添加属性弹窗的确认按钮
			async dialogConfirm(e) {
				if (!e) return;
				if (this.addAttrType == "parent") {
					let obj = {
						skuName: e,
						checked: true,
						children: []
					}
					let res = await skuCloudObj.add(obj)
					obj._id = res.id;
					this.skuArr.push(obj)

				} else if (this.addAttrType == "child") {
					let obj = {
						name: e,
						checked: true
					}
					let id = this.skuArr[this.attrIndex]._id;
					let res = await skuCloudObj.updateChild(id, obj)
					this.skuArr[this.attrIndex].children.push(obj)
				}

			},

			//点击属性的复选框
			changeCheckbox(index) {
				this.skuArr[index].checked = !this.skuArr[index].checked
			},

			//点击属性值的子元素
			clickChlidBtn(index, cIdx) {
				this.skuArr[index].children[cIdx].checked = !this.skuArr[index].children[cIdx].checked
			},


			//点击选择属性
			clickSelect() {
				this.$refs.attrWrapPop.open();
				if (this.skuArr.length) return;
				this.getSkuData();

			},

			//点击提交表单
			onSubmit() {
				this.$refs.goodsForm.validate().then(res => {
					this.toDataBase();

				}).catch(err => {
					console.log(err);
				})
			},
			//上传到云数据库
			async toDataBase() {
				//这里缺少一个更新的按钮,需要在list中去实现
				this.goodsFormData.thumb = this.goodsFormData.thumb.map(item => {
					return {
						url: item.url,
						name: item.name,
						extname: item.extname
					}
				})

				let res = await goodsCloudObj.add(this.goodsFormData)
				uni.showToast({
					title: "新增商品成功"
				})
				setTimeout(() => {
					uni.navigateBack()
				}, 1500)

			}


		}
	}
</script>

<style lang="scss" scoped>
	.goodsView {
		padding: 30rpx;

		.skuList {
			.item {
				padding: 30rpx;
				background: $page-bg-color;
				margin: 15rpx 0;
				@include flex-box-set(start);
			}
		}
	}


	.attrWrapper {
		padding: 30rpx;
		background: #fff;
		border-radius: 20rpx 20rpx 0 0;

		.head {
			@include flex-box();
			font-size: 34rpx;
			margin-bottom: 30rpx;

			.title {
				font-weight: bold;
			}

			.addAttr {
				color: $brand-theme-color-aux;
			}
		}

		.body {
			.item {
				border-top: 1px solid $border-color-light;

				&:last-child {
					border-bottom: 1px solid $border-color-light;
				}

				.top {
					padding: 30rpx 0;
					@include flex-box-set(start);

					.font {
						padding-left: 10rpx;
						font-weight: bold;
					}
				}

				.btnGroup {
					padding: 10rpx 0 30rpx;
					@include flex-box-set(start);
					flex-wrap: wrap;

					.btn {
						padding: 0rpx 25rpx;
						height: 60rpx;
						border: 1rpx solid $border-color-light;
						margin-right: 20rpx;
						border-radius: 10rpx;
						color: $text-font-color-2;
						margin-bottom: 20rpx;
						@include flex-box-set();

						&.active {
							border-color: $brand-theme-color;
							color: $brand-theme-color;
							background: rgba(236, 87, 79, 0.1);
						}
					}
				}
			}
		}

		.foot {
			padding: 50rpx 200rpx;
		}
	}
</style>


网站公告

今日签到

点亮在社区的每一天
去签到