588 lines
15 KiB
Vue
588 lines
15 KiB
Vue
<template>
|
||
<pb-layout
|
||
class="manage-goods"
|
||
:title="goodsTitle"
|
||
leftIcon="leftIcon"
|
||
navbar="normal"
|
||
:bgStyle="bgStyle"
|
||
opacityBgUi="bg-white"
|
||
color="black"
|
||
>
|
||
<view class="goods-form">
|
||
<uni-forms ref="formRef" v-model="formData" :rules="rules" label-position="top" label-width="160">
|
||
<uni-forms-item label="商品封面图" name="picUrl" required>
|
||
<p-uploader
|
||
v-model:url="formData.picUrl"
|
||
:readonly="!canEdit"
|
||
fileMediatype="image"
|
||
limit="1"
|
||
mode="grid"
|
||
:imageStyles="{ width: '168rpx', height: '168rpx' }"
|
||
/>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="商品轮播图" name="sliderPicUrls" required>
|
||
<p-uploader
|
||
v-model:url="formData.sliderPicUrls"
|
||
:readonly="!canEdit"
|
||
fileMediatype="image"
|
||
limit="6"
|
||
mode="grid"
|
||
:imageStyles="{ width: '168rpx', height: '168rpx' }"
|
||
/>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="商品名称" name="name" required>
|
||
<uni-easyinput
|
||
type="text"
|
||
trim="all"
|
||
v-model="formData.name"
|
||
:disabled="!canEdit"
|
||
placeholder="请输入商品名称"
|
||
/>
|
||
</uni-forms-item>
|
||
<uni-forms-item
|
||
label="商品分类"
|
||
@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>
|
||
<uni-icons type="right" />
|
||
</template>
|
||
</uni-easyinput>
|
||
</uni-forms-item>
|
||
<uni-forms-item
|
||
label="商品品牌"
|
||
name="brandId"
|
||
label-position="left"
|
||
required
|
||
@tap="openPicker('brand', 'single')"
|
||
>
|
||
<uni-easyinput
|
||
type="text"
|
||
v-model="formData.brandText"
|
||
: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 label="商品规格" name="skus" required label-position="left">
|
||
<view class="btn-group">
|
||
<button class="ss-reset-button ss-set-property" @tap="clickSetProperty">规格设置</button>
|
||
</view>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="商品关键词" name="keyword" required>
|
||
<uni-easyinput type="text" v-model="formData.keyword" :disabled="!canEdit" placeholder="请输入商品关键词" />
|
||
</uni-forms-item>
|
||
<uni-forms-item label="商品简介" name="introduction" required>
|
||
<uni-easyinput
|
||
type="textarea"
|
||
:disabled="!canEdit"
|
||
trim="all"
|
||
autoHeight
|
||
v-model="formData.introduction"
|
||
placeholder="请输入商品简介"
|
||
/>
|
||
</uni-forms-item>
|
||
<uni-forms-item
|
||
label="物流设置"
|
||
@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>
|
||
<uni-icons type="right" />
|
||
</template>
|
||
</uni-easyinput>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="虚拟销量" name="virtualSalesCount" required>
|
||
<uni-easyinput
|
||
v-model="formData.virtualSalesCount"
|
||
type="number"
|
||
:disabled="!canEdit"
|
||
placeholder="请输入虚拟销量"
|
||
/>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="商品详情" required>
|
||
<piaoyiEditor
|
||
:values="richValues"
|
||
@changes="saveContens"
|
||
:maxlength="3000"
|
||
:readOnly="richReadOnly"
|
||
:photoUrl="photoUrl"
|
||
:api="richApi"
|
||
/>
|
||
</uni-forms-item>
|
||
</uni-forms>
|
||
<view @tap="onSubmit" v-if="canEdit">
|
||
<button class="ss-reset-button submit-button ui-Shadow-Main">提交</button>
|
||
</view>
|
||
</view>
|
||
|
||
<p-picker ref="pickerRef" :mode="pickerMode" :options-cols="optionsCols" @confirm="onRDPickerConfirm"></p-picker>
|
||
</pb-layout>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue'
|
||
import { onLoad, onPageScroll, onShow } from '@dcloudio/uni-app'
|
||
import _ from 'lodash'
|
||
import GoodsApi from '@/peach/api/trade/goods'
|
||
import piaoyiEditor from '@/uni_modules/piaoyi-editor/components/piaoyi-editor/piaoyi-editor.vue'
|
||
import peach from '@/peach'
|
||
import { baseUrl, apiPath } from '@/peach/config'
|
||
import { handleTree, floatToFixed2, convertToInteger } from '@/peach/utils'
|
||
import { validateSku } from './js/sku'
|
||
|
||
const bgStyle = {
|
||
backgroundImage: '',
|
||
backgroundColor: '#fff',
|
||
description: '',
|
||
}
|
||
|
||
const DELIVERY_TYPES = [
|
||
{
|
||
value: 3,
|
||
label: '到店核销',
|
||
},
|
||
{
|
||
value: 4,
|
||
label: '商家配送',
|
||
},
|
||
]
|
||
|
||
const selfStyles = {
|
||
backgroundColor: '#f9f9f9',
|
||
}
|
||
|
||
const pickerRef = ref()
|
||
const richValues = ref('')
|
||
const photoUrl = baseUrl + apiPath
|
||
const richApi = '/infra/file/upload'
|
||
const richReadOnly = ref(true)
|
||
const formData = ref({
|
||
picUrl: '',
|
||
sliderPicUrls: [],
|
||
name: '',
|
||
categoryId: '',
|
||
categoryText: '',
|
||
brandId: '',
|
||
keyword: '',
|
||
deliveryTypes: [],
|
||
deliveryText: '',
|
||
sort: 0,
|
||
giveIntegral: 0,
|
||
virtualSalesCount: 0,
|
||
subCommissionType: false,
|
||
introduction: '',
|
||
})
|
||
|
||
const rules = {
|
||
name: {
|
||
rules: [
|
||
{
|
||
required: true,
|
||
errorMessage: '请输入商品名称',
|
||
},
|
||
],
|
||
},
|
||
picUrl: {
|
||
rules: [
|
||
{
|
||
required: true,
|
||
errorMessage: '请上传商品封面图',
|
||
},
|
||
],
|
||
},
|
||
sliderPicUrls: {
|
||
rules: [
|
||
{
|
||
required: true,
|
||
errorMessage: '请上传商品轮播图',
|
||
},
|
||
],
|
||
},
|
||
categoryId: {
|
||
rules: [
|
||
{
|
||
required: true,
|
||
errorMessage: '请选择商品分类',
|
||
},
|
||
],
|
||
},
|
||
brandId: {
|
||
rules: [
|
||
{
|
||
required: true,
|
||
errorMessage: '请选择商品品牌',
|
||
},
|
||
],
|
||
},
|
||
skus: {
|
||
rules: [
|
||
{
|
||
required: true,
|
||
errorMessage: '请选择商品规格',
|
||
},
|
||
],
|
||
},
|
||
keyword: {
|
||
rules: [
|
||
{
|
||
required: true,
|
||
errorMessage: '请输入商品关键字',
|
||
},
|
||
],
|
||
},
|
||
introduction: {
|
||
rules: [
|
||
{
|
||
required: true,
|
||
errorMessage: '请输入商品简介',
|
||
},
|
||
],
|
||
},
|
||
deliveryTypes: {
|
||
rules: [
|
||
{
|
||
required: true,
|
||
errorMessage: '请选择商品物流',
|
||
},
|
||
],
|
||
},
|
||
}
|
||
|
||
const formRef = ref(null)
|
||
const pickerMode = ref('single')
|
||
const popMark = ref('')
|
||
const goodsTitle = ref('')
|
||
const categoryList = ref([])
|
||
const brandList = ref([])
|
||
const optionsCols = ref([])
|
||
const canEdit = computed(() => peach.$store('trade').canEdit)
|
||
|
||
function openPicker(mark, mode) {
|
||
if (!canEdit.value) return
|
||
pickerMode.value = mode
|
||
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])
|
||
} else if (mark === 'brand') {
|
||
optionsCols.value = brandList.value
|
||
pickerRef.value.onOpen([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 = categoryList.value[e.value[0]].children[e.value[1]].id
|
||
formData.value.categoryText =
|
||
categoryList.value[e.value[0]].name + '/' + categoryList.value[e.value[0]].children[e.value[1]].name
|
||
}
|
||
|
||
if (popMark.value === 'brand') {
|
||
formData.value.brandId = brandList.value[e.value[0]].id
|
||
formData.value.brandText = brandList.value[e.value[0]].name
|
||
}
|
||
}
|
||
|
||
function clickSetProperty() {
|
||
if (formData.value.skus) {
|
||
// 如果是多规格,处理格式问题,合并属性
|
||
let temp = formData.value.skus
|
||
.map((item) => {
|
||
return item.properties.map((sitem) => ({
|
||
id: sitem.propertyId,
|
||
children: [sitem.valueId],
|
||
}))
|
||
})
|
||
.flat(1)
|
||
|
||
// 去除重复数据
|
||
let result = temp.reduce((pre, cur) => {
|
||
let index = pre.findIndex((item) => item.id === cur.id)
|
||
if (index !== -1) {
|
||
pre[index].children.push(...new Set(cur.children))
|
||
} else {
|
||
pre.push(cur)
|
||
}
|
||
|
||
return pre
|
||
}, [])
|
||
|
||
peach.$store('trade').$patch({
|
||
selectedProperty: result,
|
||
})
|
||
}
|
||
|
||
peach.$router.go('/pages/product/sku')
|
||
}
|
||
|
||
function getProduct(id) {
|
||
GoodsApi.getProduct({ id }).then((res) => {
|
||
formData.value = res.data
|
||
|
||
// 内容为图片时,需要在末尾插入空行
|
||
richValues.value = res.data.description ? res.data.description + '<p><br/></p>' : ''
|
||
|
||
// 循环遍历 categoryList,从二级分类找出和 formData.value.categoryId 相等的
|
||
|
||
let tempCategory = categoryList.value.find((item) => {
|
||
return item.children?.find((child) => {
|
||
return child.id === formData.value.categoryId
|
||
})
|
||
})
|
||
|
||
formData.value.categoryText =
|
||
tempCategory.name +
|
||
'/' +
|
||
tempCategory.children.find((item) => {
|
||
return item.id === formData.value.categoryId
|
||
}).name
|
||
|
||
// 循环遍历 brandList, 从一级分类找出和 formData.value.brandId 相等的
|
||
|
||
let tempBrand = brandList.value.find((item) => {
|
||
return item.id === formData.value.brandId
|
||
})
|
||
|
||
formData.value.brandText = tempBrand.name
|
||
|
||
// 从 DELIVERY_TYPES 找出和 formData.value.deliveryTypes 相等的
|
||
formData.value.deliveryText = DELIVERY_TYPES.find((item) => {
|
||
return item.value === formData.value.deliveryTypes[0]
|
||
}).label
|
||
|
||
formData.value.skus.forEach((item) => {
|
||
item.price = floatToFixed2(item.price)
|
||
item.marketPrice = floatToFixed2(item.marketPrice)
|
||
item.costPrice = floatToFixed2(item.costPrice)
|
||
})
|
||
|
||
peach.$store('trade').$patch({
|
||
goodsInfo: formData.value,
|
||
skus: formData.value.skus,
|
||
specType: formData.value.specType,
|
||
})
|
||
})
|
||
}
|
||
|
||
function saveContens(e) {
|
||
formData.value.description = e.html
|
||
}
|
||
|
||
function onSubmit() {
|
||
formData.value.skus = peach.$store('trade').skus
|
||
|
||
formRef.value
|
||
.validate()
|
||
.then(async (res) => {
|
||
// 校验 skus 是否正确填写完成
|
||
validateSku()
|
||
|
||
let tempObj = _.cloneDeep(formData.value)
|
||
|
||
tempObj.skus.forEach((item) => {
|
||
item.name = formData.value.name
|
||
item.price = convertToInteger(item.price)
|
||
item.marketPrice = convertToInteger(item.marketPrice)
|
||
item.costPrice = convertToInteger(item.costPrice)
|
||
})
|
||
|
||
tempObj.specType = peach.$store('trade').specType
|
||
|
||
let msg = ''
|
||
if (formData.value.id) {
|
||
tempObj.id = formData.value.id
|
||
await GoodsApi.editProduct(tempObj)
|
||
msg = '修改成功'
|
||
} else {
|
||
await GoodsApi.addProduct(tempObj)
|
||
msg = '添加成功'
|
||
}
|
||
|
||
uni.showToast({
|
||
title: msg,
|
||
icon: 'none',
|
||
duration: 4000,
|
||
})
|
||
|
||
setTimeout(() => {
|
||
peach.$router.redirect('/pages/index/product')
|
||
}, 1000)
|
||
})
|
||
.catch((err) => {
|
||
console.log('err', err)
|
||
if (err) {
|
||
uni.showToast({
|
||
title: err[0]?.errorMessage,
|
||
icon: 'none',
|
||
duration: 4000,
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
// 获得商品分类
|
||
async function getCategoryList() {
|
||
let { data } = await GoodsApi.getGoodsCategory()
|
||
categoryList.value = handleTree(data)
|
||
}
|
||
|
||
// 获得商品品牌
|
||
async function getBrandList() {
|
||
let { data } = await GoodsApi.getBrand()
|
||
brandList.value = data
|
||
}
|
||
|
||
onLoad(async (options) => {
|
||
await getCategoryList()
|
||
await getBrandList()
|
||
|
||
goodsTitle.value = options.title
|
||
|
||
if (options.id) {
|
||
getProduct(options.id)
|
||
peach.$store('trade').detailTag = options.mark
|
||
}
|
||
})
|
||
|
||
onShow(() => {
|
||
let isSave = peach.$store('trade').isSave
|
||
|
||
if (isSave) {
|
||
formData.value.skus = peach.$store('trade').skus
|
||
formData.value.specType = peach.$store('trade').specType
|
||
peach.$store('trade').isSave = false
|
||
} else {
|
||
peach.$store('trade').$patch({
|
||
skus: formData.value.skus || [],
|
||
specType: formData.value.specType,
|
||
})
|
||
}
|
||
})
|
||
|
||
onPageScroll((e) => {
|
||
if (canEdit.value && e.scrollTop > 200) {
|
||
richReadOnly.value = false
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@mixin ss-set-property {
|
||
width: 80px;
|
||
height: 60rpx;
|
||
line-height: normal;
|
||
background: var(--ui-BG-Main);
|
||
border-radius: 28rpx;
|
||
font-size: 26rpx;
|
||
font-weight: 500;
|
||
color: #fff;
|
||
}
|
||
|
||
.manage-goods {
|
||
.goods-form {
|
||
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-direction-left {
|
||
.is-disabled {
|
||
color: #333333;
|
||
text-align: right;
|
||
}
|
||
|
||
.uni-forms-item__error {
|
||
left: -160rpx !important;
|
||
}
|
||
}
|
||
}
|
||
|
||
.btn-group {
|
||
height: 100%;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
align-items: center;
|
||
background-color: #f9f9f9;
|
||
border-radius: 0 10px 10px 0;
|
||
|
||
.ss-set-property {
|
||
@include ss-set-property;
|
||
}
|
||
}
|
||
|
||
.submit-button {
|
||
width: 100%;
|
||
height: 80rpx;
|
||
border-radius: 40rpx;
|
||
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
|
||
color: $white;
|
||
}
|
||
}
|
||
}
|
||
</style>
|