feat(商品管理)
This commit is contained in:
parent
0a2d2e4601
commit
30f3a4c2c4
|
@ -163,6 +163,15 @@
|
||||||
"meta": {
|
"meta": {
|
||||||
"auth": false
|
"auth": false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "sku",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "商品属性"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"auth": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
<template>
|
||||||
|
<div class="sku-item">
|
||||||
|
<uni-forms label-width="160" label-position="top">
|
||||||
|
<uni-forms-item label="商品封面图" name="picUrl">
|
||||||
|
<p-uploader
|
||||||
|
v-model:url="formData.picUrl"
|
||||||
|
fileMediatype="image"
|
||||||
|
limit="1"
|
||||||
|
mode="grid"
|
||||||
|
:imageStyles="{ width: '168rpx', height: '168rpx' }"
|
||||||
|
/>
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="商品条码" name="barCode">
|
||||||
|
<uni-easyinput type="text" trim="all" v-model="formData.barCode" placeholder="请输入商品条码" />
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="销售价" name="price">
|
||||||
|
<uni-easyinput type="number" trim="all" v-model="formData.price" placeholder="请输入商品销售价" />
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="市场价" name="marketPrice">
|
||||||
|
<uni-easyinput type="number" trim="all" v-model="formData.marketPrice" placeholder="请输入商品销售价" />
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="成本价" name="costPrice">
|
||||||
|
<uni-easyinput type="number" trim="all" v-model="formData.costPrice" placeholder="请输入商品销售价" />
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="库存" name="stock">
|
||||||
|
<uni-easyinput type="number" trim="all" v-model="formData.stock" placeholder="请输入商品库存" />
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="重量(kg)" name="weight">
|
||||||
|
<uni-easyinput type="number" trim="all" v-model="formData.weight" placeholder="请输入商品重量" />
|
||||||
|
</uni-forms-item>
|
||||||
|
<uni-forms-item label="体积(m³)" name="volume">
|
||||||
|
<uni-easyinput type="number" trim="all" v-model="formData.volume" placeholder="请输入商品体积" />
|
||||||
|
</uni-forms-item>
|
||||||
|
</uni-forms>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const formData = ref({})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.sku-item {
|
||||||
|
margin: 40rpx;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,132 @@
|
||||||
|
<template>
|
||||||
|
<view class="property-list">
|
||||||
|
<uni-popup type="bottom" ref="propertyListPopupRef" background-color="#fff">
|
||||||
|
<view class="popup-header">
|
||||||
|
<view class="button-cancel" @click="onClosePopup">取消</view>
|
||||||
|
<view class="button-link" @click="onConfirmPopup">确定</view>
|
||||||
|
</view>
|
||||||
|
<view class="popup-content">
|
||||||
|
<view
|
||||||
|
v-for="item in propertyList"
|
||||||
|
:key="item.id"
|
||||||
|
:class="['property-item', item.checked ? 'active' : '']"
|
||||||
|
@tap="chooseProperty(item)"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, defineEmits, defineProps, defineExpose } from 'vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* todo 底部高度配置
|
||||||
|
*/
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
default: () => [],
|
||||||
|
required: true,
|
||||||
|
type: Array,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const propertyList = ref([
|
||||||
|
{
|
||||||
|
label: '红色',
|
||||||
|
id: 1,
|
||||||
|
checked: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '蓝色',
|
||||||
|
id: 2,
|
||||||
|
checked: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '白色',
|
||||||
|
id: 3,
|
||||||
|
checked: false,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const propertyListPopupRef = ref()
|
||||||
|
|
||||||
|
const onClosePopup = () => {
|
||||||
|
propertyListPopupRef.value.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
function chooseProperty(item) {
|
||||||
|
const index = props.modelValue.findIndex((v) => v.id === item.id)
|
||||||
|
item.checked = !item.checked
|
||||||
|
if (index === -1) {
|
||||||
|
props.modelValue.push(item)
|
||||||
|
} else {
|
||||||
|
props.modelValue.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onConfirmPopup() {
|
||||||
|
emit('update:modelValue', propertyList.value.filter((item) => item.checked).map((item) => item.id) ?? [])
|
||||||
|
onClosePopup()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOpen() {
|
||||||
|
propertyList.value.map((item) => {
|
||||||
|
item.checked = false
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
propertyList.value = propertyList.value.map((item) =>
|
||||||
|
props.modelValue.some((id) => id === item.id) ? { ...item, checked: true } : item
|
||||||
|
)
|
||||||
|
propertyListPopupRef.value.open('bottom')
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
onOpen,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.property-list {
|
||||||
|
.popup-content {
|
||||||
|
height: 500rpx;
|
||||||
|
padding: 40rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20rpx;
|
||||||
|
justify-content: flex-start;
|
||||||
|
.property-item {
|
||||||
|
text-align: center;
|
||||||
|
line-height: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
padding: 0 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: var(--ui-BG-4);
|
||||||
|
}
|
||||||
|
.active {
|
||||||
|
color: #fff;
|
||||||
|
background-color: var(--ui-BG-Main);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-link {
|
||||||
|
color: #1892ea;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
.button-cancel {
|
||||||
|
color: #888;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
.popup-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 24rpx 38rpx 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,10 @@
|
||||||
|
export const SPEC_TYPE = [
|
||||||
|
{
|
||||||
|
label: '单规格',
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '多规格',
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
]
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { SPEC_TYPE } from './config'
|
||||||
|
|
||||||
|
const pickerRef = ref(null)
|
||||||
|
|
||||||
|
const propertyList = ref([1, 2])
|
||||||
|
|
||||||
|
const propertyListRef = ref(null)
|
||||||
|
|
||||||
|
const formData = ref({
|
||||||
|
specType: true,
|
||||||
|
specText: SPEC_TYPE[0].label,
|
||||||
|
})
|
||||||
|
|
||||||
|
function onRDPickerConfirm(e) {
|
||||||
|
formData.value.specText = SPEC_TYPE[e.value[0]].label
|
||||||
|
formData.value.specType = SPEC_TYPE[e.value[0]].value
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPropertyConfirm(e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { pickerRef, propertyListRef, formData, onRDPickerConfirm, onPropertyConfirm, propertyList }
|
|
@ -1,50 +1,117 @@
|
||||||
<template>
|
<template>
|
||||||
<pb-layout class="manage-goods" title="发布商品" leftIcon="leftIcon" navbar="normal" :bgStyle="bgStyle"
|
<pb-layout
|
||||||
opacityBgUi="bg-white" color="black">
|
class="manage-goods"
|
||||||
|
title="发布商品"
|
||||||
|
leftIcon="leftIcon"
|
||||||
|
navbar="normal"
|
||||||
|
:bgStyle="bgStyle"
|
||||||
|
opacityBgUi="bg-white"
|
||||||
|
color="black"
|
||||||
|
>
|
||||||
<view class="goods-form">
|
<view class="goods-form">
|
||||||
<uni-forms ref="formRef" v-model="formData" :rules="rules" label-position="top" label-width="160">
|
<uni-forms ref="formRef" v-model="formData" :rules="rules" label-position="top" label-width="160">
|
||||||
<uni-forms-item label="商品封面图" name="picUrl" required>
|
<uni-forms-item label="商品封面图" name="picUrl" required>
|
||||||
<p-uploader v-model:url="formData.picUrl" fileMediatype="image" limit="1" mode="grid"
|
<p-uploader
|
||||||
:imageStyles="{ width: '168rpx', height: '168rpx' }" />
|
v-model:url="formData.picUrl"
|
||||||
|
fileMediatype="image"
|
||||||
|
limit="1"
|
||||||
|
mode="grid"
|
||||||
|
:imageStyles="{ width: '168rpx', height: '168rpx' }"
|
||||||
|
/>
|
||||||
</uni-forms-item>
|
</uni-forms-item>
|
||||||
<uni-forms-item label="商品轮播图" name="sliderPicUrls" required>
|
<uni-forms-item label="商品轮播图" name="sliderPicUrls" required>
|
||||||
<p-uploader v-model:url="formData.sliderPicUrls" fileMediatype="image" limit="6" mode="grid"
|
<p-uploader
|
||||||
:imageStyles="{ width: '168rpx', height: '168rpx' }" />
|
v-model:url="formData.sliderPicUrls"
|
||||||
|
fileMediatype="image"
|
||||||
|
limit="6"
|
||||||
|
mode="grid"
|
||||||
|
:imageStyles="{ width: '168rpx', height: '168rpx' }"
|
||||||
|
/>
|
||||||
</uni-forms-item>
|
</uni-forms-item>
|
||||||
<uni-forms-item label="商品名称" name="name" required>
|
<uni-forms-item label="商品名称" name="name" required>
|
||||||
<uni-easyinput type="text" trim="all" v-model="formData.name" placeholder="请输入商品名称" />
|
<uni-easyinput type="text" trim="all" v-model="formData.name" placeholder="请输入商品名称" />
|
||||||
</uni-forms-item>
|
</uni-forms-item>
|
||||||
<uni-forms-item label="商品分类" name="categoryId" label-position="left" required>
|
<uni-forms-item
|
||||||
<uni-easyinput type="text" v-model="formData.categoryId" :styles="selfStyles" placeholderStyle="color:#8a8a8a"
|
label="商品分类"
|
||||||
:clearable="false" :inputBorder="false" placeholder="请选择商品分类" disabled>
|
@tap="openPicker('category', 'multiple')"
|
||||||
|
name="categoryId"
|
||||||
|
label-position="left"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<uni-easyinput
|
||||||
|
type="text"
|
||||||
|
v-model="formData.categoryText"
|
||||||
|
:styles="selfStyles"
|
||||||
|
placeholderStyle="color:#8a8a8a"
|
||||||
|
:clearable="false"
|
||||||
|
:inputBorder="false"
|
||||||
|
placeholder="请选择商品分类"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
<template v-slot:right>
|
<template v-slot:right>
|
||||||
<uni-icons type="right" />
|
<uni-icons type="right" />
|
||||||
</template>
|
</template>
|
||||||
</uni-easyinput>
|
</uni-easyinput>
|
||||||
</uni-forms-item>
|
</uni-forms-item>
|
||||||
<uni-forms-item label="商品品牌" name="brandId" label-position="left" required>
|
<uni-forms-item label="商品品牌" name="brandId" label-position="left" required>
|
||||||
<uni-easyinput type="text" v-model="formData.brandId" :styles="selfStyles" placeholderStyle="color:#8a8a8a"
|
<uni-easyinput
|
||||||
:clearable="false" :inputBorder="false" placeholder="请选择商品品牌" disabled>
|
type="text"
|
||||||
|
v-model="formData.brandId"
|
||||||
|
:styles="selfStyles"
|
||||||
|
placeholderStyle="color:#8a8a8a"
|
||||||
|
:clearable="false"
|
||||||
|
:inputBorder="false"
|
||||||
|
placeholder="请选择商品品牌"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
<template v-slot:right>
|
<template v-slot:right>
|
||||||
<uni-icons type="right" />
|
<uni-icons type="right" />
|
||||||
</template>
|
</template>
|
||||||
</uni-easyinput>
|
</uni-easyinput>
|
||||||
</uni-forms-item>
|
</uni-forms-item>
|
||||||
<uni-forms-item label="商品规格" name="skus" label-position="left" required>
|
<uni-forms-item label="商品规格" name="skus" label-position="left" required>
|
||||||
<uni-easyinput type="text" v-model="formData.skus" :styles="selfStyles" placeholderStyle="color:#8a8a8a"
|
<uni-easyinput
|
||||||
:clearable="false" :inputBorder="false" placeholder="请添加商品规格" disabled>
|
type="text"
|
||||||
<template v-slot:right>
|
v-model="formData.skus"
|
||||||
<uni-icons type="right" /> </template></uni-easyinput>
|
:styles="selfStyles"
|
||||||
|
placeholderStyle="color:#8a8a8a"
|
||||||
|
:clearable="false"
|
||||||
|
:inputBorder="false"
|
||||||
|
placeholder="请添加商品规格"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<template v-slot:right> <uni-icons type="right" /> </template
|
||||||
|
></uni-easyinput>
|
||||||
</uni-forms-item>
|
</uni-forms-item>
|
||||||
<uni-forms-item label="商品关键词" name="keyword" required>
|
<uni-forms-item label="商品关键词" name="keyword" required>
|
||||||
<uni-easyinput type="text" v-model="formData.keyword" placeholder="请输入商品关键词" />
|
<uni-easyinput type="text" v-model="formData.keyword" placeholder="请输入商品关键词" />
|
||||||
</uni-forms-item>
|
</uni-forms-item>
|
||||||
<uni-forms-item label="商品简介" name="introduction" required>
|
<uni-forms-item label="商品简介" name="introduction" required>
|
||||||
<uni-easyinput type="textarea" trim="all" autoHeight v-model="formData.introduction" placeholder="请输入商品简介" />
|
<uni-easyinput
|
||||||
|
type="textarea"
|
||||||
|
trim="all"
|
||||||
|
autoHeight
|
||||||
|
v-model="formData.introduction"
|
||||||
|
placeholder="请输入商品简介"
|
||||||
|
/>
|
||||||
</uni-forms-item>
|
</uni-forms-item>
|
||||||
<uni-forms-item label="物流设置" @click="openPicker" name="deliveryTypes" label-position="left" required>
|
<uni-forms-item
|
||||||
<uni-easyinput type="text" :clearable="false" :styles="selfStyles" placeholderStyle="color:#8a8a8a"
|
label="物流设置"
|
||||||
:inputBorder="false" v-model="formData.keyword" placeholder="请选择配送方式" disabled>
|
@tap="openPicker('delivery', 'single')"
|
||||||
|
name="deliveryTypes"
|
||||||
|
label-position="left"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<uni-easyinput
|
||||||
|
type="text"
|
||||||
|
:clearable="false"
|
||||||
|
:styles="selfStyles"
|
||||||
|
placeholderStyle="color:#8a8a8a"
|
||||||
|
:inputBorder="false"
|
||||||
|
v-model="formData.deliveryText"
|
||||||
|
placeholder="请选择配送方式"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
<template v-slot:right>
|
<template v-slot:right>
|
||||||
<uni-icons type="right" />
|
<uni-icons type="right" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -53,15 +120,12 @@
|
||||||
</uni-forms>
|
</uni-forms>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<p-picker
|
||||||
<uni-popup ref="popupRef" type="bottom" border-radius="10px 10px 0 0">
|
ref="pickerRef"
|
||||||
<picker-view v-if="visible" :indicator-style="indicatorStyle" :value="value" @change="bindChange"
|
:mode="pickerMode"
|
||||||
class="picker-view">
|
:options-cols="optionsCols"
|
||||||
<picker-view-column>
|
@confirm="onRDPickerConfirm"
|
||||||
<view class="item" v-for="(item, index) in DELIVERY_TYPES" :key="index">{{ item.label }}</view>
|
></p-picker>
|
||||||
</picker-view-column>
|
|
||||||
</picker-view>
|
|
||||||
</uni-popup>
|
|
||||||
</pb-layout>
|
</pb-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -69,6 +133,7 @@
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { onLoad } from '@dcloudio/uni-app'
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
import peach from '@/peach'
|
import peach from '@/peach'
|
||||||
|
import { handleTree } from '@/peach/utils'
|
||||||
import GoodsApi from '@/peach/api/trade/goods'
|
import GoodsApi from '@/peach/api/trade/goods'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
@ -78,21 +143,22 @@ const bgStyle = {
|
||||||
description: '',
|
description: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
const DELIVERY_TYPES = [{
|
const DELIVERY_TYPES = [
|
||||||
|
{
|
||||||
value: 3,
|
value: 3,
|
||||||
label: '到店核销'
|
label: '到店核销',
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
value: 4,
|
value: 4,
|
||||||
label: '商家配送'
|
label: '商家配送',
|
||||||
}]
|
},
|
||||||
|
]
|
||||||
const indicatorStyle = 'height: 50px'
|
|
||||||
|
|
||||||
const selfStyles = {
|
const selfStyles = {
|
||||||
backgroundColor: '#f9f9f9',
|
backgroundColor: '#f9f9f9',
|
||||||
}
|
}
|
||||||
|
|
||||||
const popupRef = ref()
|
const pickerRef = ref()
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
picUrl: 'http://101.43.181.163:9001/mall-backend/8f11e372520501531d06bfce15ea97bbecead41c5e4a36d15d7e40af85729ff3.png',
|
picUrl: 'http://101.43.181.163:9001/mall-backend/8f11e372520501531d06bfce15ea97bbecead41c5e4a36d15d7e40af85729ff3.png',
|
||||||
sliderPicUrls: [
|
sliderPicUrls: [
|
||||||
|
@ -101,10 +167,12 @@ const formData = ref({
|
||||||
'http://101.43.181.163:9001/mall-backend/8f11e372520501531d06bfce15ea97bbecead41c5e4a36d15d7e40af85729ff3.png',
|
'http://101.43.181.163:9001/mall-backend/8f11e372520501531d06bfce15ea97bbecead41c5e4a36d15d7e40af85729ff3.png',
|
||||||
],
|
],
|
||||||
name: '测试商品',
|
name: '测试商品',
|
||||||
categoryId: 91,
|
categoryId: [],
|
||||||
brandId: 4,
|
categoryText: '',
|
||||||
|
brandId: '',
|
||||||
keyword: '香酥鸭,但家',
|
keyword: '香酥鸭,但家',
|
||||||
deliveryTypes: [3],
|
deliveryTypes: [],
|
||||||
|
deliveryText: '',
|
||||||
introduction: '但家贵阳香酥鸭现榨香酥鸭无任何添加剂香酥鸭但家贵阳香酥鸭现榨香酥鸭无任何添加剂香酥鸭',
|
introduction: '但家贵阳香酥鸭现榨香酥鸭无任何添加剂香酥鸭但家贵阳香酥鸭现榨香酥鸭无任何添加剂香酥鸭',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -184,10 +252,41 @@ const rules = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const formRef = ref(null)
|
const formRef = ref(null)
|
||||||
|
const pickerMode = ref('single')
|
||||||
|
const popMark = ref('')
|
||||||
|
const categoryList = ref([])
|
||||||
|
const optionsCols = ref([])
|
||||||
|
|
||||||
function openPicker() {
|
function openPicker(mark, mode) {
|
||||||
console.log('123')
|
pickerMode.value = mode
|
||||||
popupRef.value.open()
|
popMark.value = mark
|
||||||
|
if (mark === 'delivery') {
|
||||||
|
optionsCols.value = DELIVERY_TYPES
|
||||||
|
pickerRef.value.onOpen([0])
|
||||||
|
} else if (mark === 'category') {
|
||||||
|
optionsCols.value = categoryList.value
|
||||||
|
pickerRef.value.onOpen([0, 0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRDPickerConfirm(e) {
|
||||||
|
if (popMark.value === 'delivery') {
|
||||||
|
formData.value.deliveryTypes = []
|
||||||
|
formData.value.deliveryText = DELIVERY_TYPES[e.value[0]].label
|
||||||
|
formData.value.deliveryTypes.push(DELIVERY_TYPES[e.value[0]].value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (popMark.value === 'category') {
|
||||||
|
formData.value.categoryId = e.value
|
||||||
|
formData.value.categoryText =
|
||||||
|
categoryList.value[e.value[0]].name + '/' + categoryList.value[e.value[0]].children[e.value[1]].name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProduct() {
|
||||||
|
GoodsApi.getProduct(formData.value.id).then((res) => {
|
||||||
|
formData.value = res
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSubmit() {
|
function onSubmit() {
|
||||||
|
@ -213,7 +312,20 @@ function onSubmit() {
|
||||||
// 获取商品详情
|
// 获取商品详情
|
||||||
function getGoodsInfo() {}
|
function getGoodsInfo() {}
|
||||||
|
|
||||||
|
// 获得商品分类
|
||||||
|
async function getCategoryList() {
|
||||||
|
let { data } = await GoodsApi.getGoodsCategory()
|
||||||
|
categoryList.value = handleTree(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得商品品牌
|
||||||
|
async function getBrandList() {
|
||||||
|
let { data } = await GoodsApi.getBrandList()
|
||||||
|
console.log(data)
|
||||||
|
}
|
||||||
|
|
||||||
onLoad((options) => {
|
onLoad((options) => {
|
||||||
|
getCategoryList()
|
||||||
if (options.id) {
|
if (options.id) {
|
||||||
getProduct()
|
getProduct()
|
||||||
}
|
}
|
||||||
|
@ -258,16 +370,5 @@ onLoad((options) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.picker-view {
|
|
||||||
width: 750rpx;
|
|
||||||
height: 600rpx;
|
|
||||||
margin-top: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item {
|
|
||||||
line-height: 100rpx;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
<template>
|
||||||
|
<pb-layout
|
||||||
|
class="goods-property"
|
||||||
|
title="商品属性"
|
||||||
|
leftIcon="leftIcon"
|
||||||
|
navbar="normal"
|
||||||
|
:bgStyle="bgStyle"
|
||||||
|
opacityBgUi="bg-white"
|
||||||
|
color="black"
|
||||||
|
>
|
||||||
|
<view class="property">
|
||||||
|
<uni-forms ref="formRef" v-model="formData" :rules="rules" label-position="top" label-width="160">
|
||||||
|
<uni-forms-item
|
||||||
|
label="商品规格"
|
||||||
|
@tap="pickerRef.onOpen([0])"
|
||||||
|
name="specType"
|
||||||
|
label-position="left"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<uni-easyinput
|
||||||
|
type="text"
|
||||||
|
:clearable="false"
|
||||||
|
:styles="selfStyles"
|
||||||
|
placeholderStyle="color:#8a8a8a"
|
||||||
|
:inputBorder="false"
|
||||||
|
v-model="formData.specText"
|
||||||
|
placeholder="请选择商品规格"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<template v-slot:right>
|
||||||
|
<uni-icons type="right" />
|
||||||
|
</template>
|
||||||
|
</uni-easyinput>
|
||||||
|
</uni-forms-item>
|
||||||
|
</uni-forms>
|
||||||
|
</view>
|
||||||
|
<template v-if="formData.specType">
|
||||||
|
<button class="ss-reset-button add-property" @tap="propertyListRef.onOpen()">+添加规格</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<SkuItem />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<p-picker ref="pickerRef" mode="single" :options-cols="SPEC_TYPE" @confirm="onRDPickerConfirm"></p-picker>
|
||||||
|
|
||||||
|
<PropertyList ref="propertyListRef" v-model="propertyList" @confirm="onPropertyConfirm" />
|
||||||
|
</pb-layout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import SkuItem from './components/item'
|
||||||
|
import PropertyList from './components/propertyList'
|
||||||
|
import { SPEC_TYPE } from './js/config'
|
||||||
|
import { pickerRef, propertyListRef, onRDPickerConfirm, formData, propertyList, onPropertyConfirm } from './js/sku'
|
||||||
|
|
||||||
|
const bgStyle = {
|
||||||
|
backgroundImage: '',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
description: '',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.goods-property {
|
||||||
|
.property {
|
||||||
|
margin: 40rpx;
|
||||||
|
|
||||||
|
:deep() {
|
||||||
|
.uni-easyinput__content-input {
|
||||||
|
font-size: 28rpx !important;
|
||||||
|
color: #333333 !important;
|
||||||
|
line-height: normal !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-easyinput__placeholder-class {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-direction-left {
|
||||||
|
.uni-forms-item__label {
|
||||||
|
padding-left: 10px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border-radius: 10px 0 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
uni-icons {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-easyinput__content {
|
||||||
|
border-radius: 0 10px 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-disabled {
|
||||||
|
color: #333333;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-property {
|
||||||
|
margin: 40rpx;
|
||||||
|
border: 1px dotted var(--ui-BG-Main);
|
||||||
|
color: var(--ui-BG-Main);
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -33,6 +33,15 @@ const GoodsApi = {
|
||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
// 商品分类
|
||||||
|
getGoodsCategory: (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/product/category/list',
|
||||||
|
method: 'GET',
|
||||||
|
params: data,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 商品品牌
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GoodsApi
|
export default GoodsApi
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
<template>
|
||||||
|
<view class="custom-picker">
|
||||||
|
<!-- 普通弹窗 -->
|
||||||
|
<uni-popup type="bottom" ref="pickerPopupRef" background-color="#fff">
|
||||||
|
<view class="popup-header">
|
||||||
|
<view class="button-cancel" @click="onClosePopup">取消</view>
|
||||||
|
<view class="button-link" @click="onConfirmPopup">确定</view>
|
||||||
|
</view>
|
||||||
|
<view class="popup-content">
|
||||||
|
<picker-view
|
||||||
|
:indicator-style="indicatorStyle"
|
||||||
|
:value="pickerViewValue"
|
||||||
|
@change="bindChange"
|
||||||
|
class="picker-view"
|
||||||
|
>
|
||||||
|
<template v-if="mode === 'single'">
|
||||||
|
<picker-view-column>
|
||||||
|
<view
|
||||||
|
class="item"
|
||||||
|
v-for="(item, index) in props.optionsCols"
|
||||||
|
:key="`${item.value}-${index}`"
|
||||||
|
>{{ item.label }}</view
|
||||||
|
>
|
||||||
|
</picker-view-column>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<picker-view-column>
|
||||||
|
<view
|
||||||
|
class="item"
|
||||||
|
v-for="(item, index) in props.optionsCols"
|
||||||
|
:key="`${item.value}-${index}`"
|
||||||
|
>{{ item.name }}</view
|
||||||
|
>
|
||||||
|
</picker-view-column>
|
||||||
|
<picker-view-column>
|
||||||
|
<view class="item" v-for="(item, index) in childrenList" :key="`${item.value}-${index}`">{{
|
||||||
|
item.name
|
||||||
|
}}</view>
|
||||||
|
</picker-view-column>
|
||||||
|
</template>
|
||||||
|
</picker-view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineEmits, defineProps, ref, onMounted, computed, defineExpose } from 'vue'
|
||||||
|
const pickerViewValue = ref([])
|
||||||
|
const indicatorStyle = `height: 50px`
|
||||||
|
const props = defineProps({
|
||||||
|
// 传入pickerview列表 value label字段
|
||||||
|
optionsCols: {
|
||||||
|
default: () => [],
|
||||||
|
required: true,
|
||||||
|
type: Array,
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'single',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const childrenList = computed(() => {
|
||||||
|
return props.optionsCols[pickerViewValue.value[0]]?.children
|
||||||
|
})
|
||||||
|
|
||||||
|
// 暴露两个方法confirm change
|
||||||
|
const emit = defineEmits(['confirm', 'change'])
|
||||||
|
// 弹窗ref
|
||||||
|
const pickerPopupRef = ref(null)
|
||||||
|
/**
|
||||||
|
* @author: joey wong
|
||||||
|
* @description: 打开弹窗
|
||||||
|
* @param {Array} defaultValue 默认选中index的值,如:[0,1]
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
const onOpen = (defaultValue) => {
|
||||||
|
pickerViewValue.value = defaultValue
|
||||||
|
pickerPopupRef.value.open('bottom')
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @author: joey wong
|
||||||
|
* @description: 滚动改变事件
|
||||||
|
* @param {*} e
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
const bindChange = (e) => {
|
||||||
|
pickerViewValue.value = e.detail.value
|
||||||
|
|
||||||
|
if (props.mode === 'multiple') {
|
||||||
|
if (pickerViewValue.value[0] !== e.detail.value[0]) {
|
||||||
|
pickerViewValue.value[1] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('change', {
|
||||||
|
value: pickerViewValue.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @author: joey wong
|
||||||
|
* @description: 关闭模态框事件
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
const onClosePopup = () => {
|
||||||
|
pickerPopupRef.value.close()
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @author: joey wong
|
||||||
|
* @description: 确认事件
|
||||||
|
* @return {*}
|
||||||
|
*/
|
||||||
|
const onConfirmPopup = () => {
|
||||||
|
emit('confirm', {
|
||||||
|
value: pickerViewValue.value,
|
||||||
|
})
|
||||||
|
onClosePopup()
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
onOpen,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.custom-picker {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
margin-right: 5.7rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
width: 19rpx;
|
||||||
|
height: 12rpx;
|
||||||
|
margin-bottom: 3rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content {
|
||||||
|
height: 500rpx;
|
||||||
|
.item {
|
||||||
|
text-align: center;
|
||||||
|
line-height: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.picker-view {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
}
|
||||||
|
.button-link {
|
||||||
|
color: #1892ea;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
.button-cancel {
|
||||||
|
color: #888;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
.popup-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 24rpx 38rpx 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,5 +1,61 @@
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造树型结构数据
|
||||||
|
* @param {*} data 数据源
|
||||||
|
* @param {*} id id字段 默认 'id'
|
||||||
|
* @param {*} parentId 父节点字段 默认 'parentId'
|
||||||
|
* @param {*} children 孩子节点字段 默认 'children'
|
||||||
|
*/
|
||||||
|
export const handleTree = (data, id, parentId, children) => {
|
||||||
|
if (!Array.isArray(data)) {
|
||||||
|
console.warn('data must be an array')
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const config = {
|
||||||
|
id: id || 'id',
|
||||||
|
parentId: parentId || 'parentId',
|
||||||
|
childrenList: children || 'children',
|
||||||
|
}
|
||||||
|
|
||||||
|
const childrenListMap = {}
|
||||||
|
const nodeIds = {}
|
||||||
|
const tree = []
|
||||||
|
|
||||||
|
for (const d of data) {
|
||||||
|
const parentId = d[config.parentId]
|
||||||
|
if (childrenListMap[parentId] == null) {
|
||||||
|
childrenListMap[parentId] = []
|
||||||
|
}
|
||||||
|
nodeIds[d[config.id]] = d
|
||||||
|
childrenListMap[parentId].push(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const d of data) {
|
||||||
|
const parentId = d[config.parentId]
|
||||||
|
if (nodeIds[parentId] == null) {
|
||||||
|
tree.push(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const t of tree) {
|
||||||
|
adaptToChildrenList(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
function adaptToChildrenList(o) {
|
||||||
|
if (childrenListMap[o[config.id]] !== null) {
|
||||||
|
o[config.childrenList] = childrenListMap[o[config.id]]
|
||||||
|
}
|
||||||
|
if (o[config.childrenList]) {
|
||||||
|
for (const c of o[config.childrenList]) {
|
||||||
|
adaptToChildrenList(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tree
|
||||||
|
}
|
||||||
|
|
||||||
export function resetPagination(pagination) {
|
export function resetPagination(pagination) {
|
||||||
pagination.list = []
|
pagination.list = []
|
||||||
pagination.total = 0
|
pagination.total = 0
|
||||||
|
|
Loading…
Reference in New Issue