后台页面中还存在商品信息的添加和修改等。接下来我们逐步进行分析和展开。包含页面布局和数据库逻辑等等。
1、整体效果
样式效果如下,依然采用了表单形式来完成和商家信息差不多,但在商品属性上多做了一些弹窗等界面,样式和功能点表多。
2、整体样式和布局
2.1 代码效果:
2.2 表单间隔
从图上看到 每一个标签有一个间隔。
.goodsView{
padding:30rpx;
3、 这里的布局是使用的uni-froms 以及uni-forms-item
3.1 uni-froms 的使用
<uni-forms ref="goodsForm" :model="goodsFormData" :rules="goodsRules" :label-width="100" label-align="right">
依然是ref 、model、rules
rules 这里就没有自定义规则(没有uniapp-商城-50-后台 商家信息 第4点的问题)
创作中心-CSDNhttps://mpbeta.csdn.net/mp_blog/creation/editor/147853834
3.2 商品图片的上传 uni-file-picker
上传图片依然用的是 uni-file-picker 没有对上传张数限制
<uni-file-picker v-model="goodsFormData.thumb" fileMediatype="image" mode="grid"></uni-file-picker>
3.3 商品名称的页面 uni-easyinput
使用的是 uni-easyinput 进行名称的添加。trim是否自动去掉空格。
uni-easyinput 增强输入框 | uni-app官网uni-app,uniCloud,serverless,uni-easyinput 增强输入框,介绍,基本用法,输入框带左右图标,插槽,输入框禁用,密码框,输入框聚焦,多行文本,多行文本自动高度,取消边框,API,Easyinput Props,Type Options,ConfirmType Ophttps://uniapp.dcloud.net.cn/component/uniui/uni-easyinput.html<uni-data-select collection="kt-mall-category" field="_id as value, name as text"
v-model="goodsFormData.category_id"></uni-data-select>
collection 数据库
field 选择数据的范围
3.4 商品分类 uni-data-select
这里的数据来源于前面商品分类添加的数据,需要进行后台数据联动。
使用的是选择组件 uni-data-select,然后根据前面商品属性添加的类别进行选择。
安装:
3.5 商品价格 uni-easyinput
使用的 uni-forms-item uni-easyinput, trim是否自动去掉空格。
3.6 商品属性 u-cell
使用 u-cell 展示。
这个是u-view 提供的组件,需要重新安装和使用
Cell 单元格 | uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架uView UI,是 uni-app 生态最优秀的 UI 框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水https://uviewui.com/components/cell.html<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>
3.7 商品描述 uni-easyinput
使用的 uni-forms-item 、uni-easyinput, trim是否自动去掉空格。
4、页面中使用 弹出层 uni-popup 用于3.6的商品属性
4.1 bug处理
主要是使用的 uni-popup,依然会有前面的bug 但是我们在前面的章节已经处理了。
创作中心-CSDNhttps://mpbeta.csdn.net/mp_blog/creation/editor/147851881
4. 2 整体页面感知
包含两部分弹窗,上面部分是从底部弹出,下面的是弹出聚焦在中部。
上面分三个盒子 上中下 三个,
复杂的就是中部盒子。
<view class="safe-area-bottom"></view> 兼容ios系统的下边的安全区域遮挡我们也页面显示。
中部:
整体样式结果:(这里没有联动分类数据所以没有分类数据可以选择)
5、整体代码
<template>
<view class="goodsView">
<!-- 添加商品 -->
<uni-forms ref="goodsForm" :model="goodsFormData" :rules="goodsRules" :label-width="100" label-align="right">
<uni-forms-item label="商品图片">
<uni-file-picker v-model="goodsFormData.thumb" fileMediatype="image" mode="grid"></uni-file-picker>
</uni-forms-item>
<uni-forms-item label="商品名称" required name="name">
<uni-easyinput v-model="goodsFormData.name" placeholder="请输入商品名称" trim="both"></uni-easyinput>
</uni-forms-item>
<uni-forms-item label="产品分类" required name="category_id">
<uni-data-select collection="kt-mall-category" 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">
<uni-easyinput type="number" v-model="goodsFormData.price" placeholder="请输入商品价格"
trim="both"></uni-easyinput>
</uni-forms-item>
<uni-forms-item label="商品原价">
<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="商品描述">
<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() {
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>