商品管理: 完善表单校验,优化信息提示,完成新建、编辑、提交逻辑
(cherry picked from commit c38abc365c
)
This commit is contained in:
parent
6e604f9e07
commit
083a0c4760
22
src/api/mall/product/management/type/index.ts
Normal file
22
src/api/mall/product/management/type/index.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export interface SpuType {
|
||||
name?: string // 商品名称
|
||||
categoryId?: number // 商品分类
|
||||
keyword?: string // 关键字
|
||||
unit?: string // 单位
|
||||
picUrl?: string // 商品封面图
|
||||
sliderPicUrls?: string[] // 商品轮播图
|
||||
introduction?: string // 商品简介
|
||||
deliveryTemplateId?: number // 运费模版
|
||||
selectRule?: string // 选择规格 TODO 暂时定义
|
||||
specType?: boolean // 商品规格
|
||||
subCommissionType?: boolean // 分销类型
|
||||
description?: string // 商品详情
|
||||
sort?: string // 商品排序
|
||||
giveIntegral?: number // 赠送积分
|
||||
virtualSalesCount?: number // 虚拟销量
|
||||
recommendHot?: boolean // 是否热卖
|
||||
recommendBenefit?: boolean // 是否优惠
|
||||
recommendBest?: boolean // 是否精品
|
||||
recommendNew?: boolean // 是否新品
|
||||
recommendGood?: boolean // 是否优品
|
||||
}
|
17
src/utils/object.ts
Normal file
17
src/utils/object.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 将值复制到目标对象,且以目标对象属性为准,例:target: {a:1} source:{a:2,b:3} 结果为:{a:2}
|
||||
* @param target 目标对象
|
||||
* @param source 源对象
|
||||
*/
|
||||
export const copyValueToTarget = (target, source) => {
|
||||
const newObj = Object.assign({}, target, source)
|
||||
// 删除多余属性
|
||||
Object.keys(newObj).forEach((key) => {
|
||||
// 如果不是target中的属性则删除
|
||||
if (Object.keys(target).indexOf(key) === -1) {
|
||||
delete newObj[key]
|
||||
}
|
||||
})
|
||||
// 更新目标对象值
|
||||
Object.assign(target, newObj)
|
||||
}
|
@ -2,13 +2,25 @@
|
||||
<ContentWrap v-loading="formLoading">
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="商品信息" name="basicInfo">
|
||||
<BasicInfoForm ref="basicInfoRef" />
|
||||
<BasicInfoForm
|
||||
ref="BasicInfoRef"
|
||||
v-model:activeName="activeName"
|
||||
:propFormData="formData"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="商品详情" name="description">
|
||||
<DescriptionForm ref="DescriptionRef" />
|
||||
<DescriptionForm
|
||||
ref="DescriptionRef"
|
||||
v-model:activeName="activeName"
|
||||
:propFormData="formData"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="其他设置" name="otherSettings">
|
||||
<OtherSettingsForm ref="otherSettingsRef" />
|
||||
<OtherSettingsForm
|
||||
ref="OtherSettingsRef"
|
||||
v-model:activeName="activeName"
|
||||
:propFormData="formData"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-form>
|
||||
@ -22,6 +34,7 @@
|
||||
<script lang="ts" name="ProductManagementForm" setup>
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import { BasicInfoForm, DescriptionForm, OtherSettingsForm } from './components'
|
||||
import { SpuType } from '@/api/mall/product/management/type' // const { t } = useI18n() // 国际化
|
||||
|
||||
// const { t } = useI18n() // 国际化
|
||||
// const message = useMessage() // 消息弹窗
|
||||
@ -30,18 +43,77 @@ const { push, currentRoute } = useRouter() // 路由
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const activeName = ref('otherSettings') // Tag 激活的窗口
|
||||
const basicInfoRef = ref<ComponentRef<typeof BasicInfoForm>>()
|
||||
const DescriptionRef = ref<ComponentRef<typeof DescriptionForm>>()
|
||||
|
||||
const activeName = ref('basicInfo') // Tag 激活的窗口
|
||||
const BasicInfoRef = ref<ComponentRef<typeof BasicInfoForm>>() // 商品信息Ref
|
||||
const DescriptionRef = ref<ComponentRef<typeof DescriptionForm>>() // 商品详情Ref
|
||||
const OtherSettingsRef = ref<ComponentRef<typeof OtherSettingsForm>>() // 其他设置Ref
|
||||
const formData = ref<SpuType>({
|
||||
name: '', // 商品名称
|
||||
categoryId: 0, // 商品分类
|
||||
keyword: '', // 关键字
|
||||
unit: '', // 单位
|
||||
picUrl: '', // 商品封面图
|
||||
sliderPicUrls: [], // 商品轮播图
|
||||
introduction: '', // 商品简介
|
||||
deliveryTemplateId: 0, // 运费模版
|
||||
selectRule: '',
|
||||
specType: false, // 商品规格
|
||||
subCommissionType: false, // 分销类型
|
||||
description: '', // 商品详情
|
||||
sort: 1, // 商品排序
|
||||
giveIntegral: 1, // 赠送积分
|
||||
virtualSalesCount: 1, // 虚拟销量
|
||||
recommendHot: false, // 是否热卖
|
||||
recommendBenefit: false, // 是否优惠
|
||||
recommendBest: false, // 是否精品
|
||||
recommendNew: false, // 是否新品
|
||||
recommendGood: false // 是否优品
|
||||
})
|
||||
/** 获得详情 */
|
||||
const getDetail = async () => {}
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = async () => {}
|
||||
|
||||
const submitForm = async () => {
|
||||
// TODO 三个表单逐一校验,如果有一个表单校验不通过则切换到对应表单,如果有两个及以上的情况则切换到最前面的一个并弹出提示消息
|
||||
// 校验各表单
|
||||
try {
|
||||
await unref(BasicInfoRef)?.validate()
|
||||
await unref(DescriptionRef)?.validate()
|
||||
await unref(OtherSettingsRef)?.validate()
|
||||
// 校验都通过后提交表单
|
||||
console.log(formData.value)
|
||||
} catch {}
|
||||
}
|
||||
/**
|
||||
* 重置表单
|
||||
*/
|
||||
const resetForm = async () => {
|
||||
formData.value = {
|
||||
name: '', // 商品名称
|
||||
categoryId: 0, // 商品分类
|
||||
keyword: '', // 关键字
|
||||
unit: '', // 单位
|
||||
picUrl: '', // 商品封面图
|
||||
sliderPicUrls: [], // 商品轮播图
|
||||
introduction: '', // 商品简介
|
||||
deliveryTemplateId: 0, // 运费模版
|
||||
selectRule: '',
|
||||
specType: false, // 商品规格
|
||||
subCommissionType: false, // 分销类型
|
||||
description: '', // 商品详情
|
||||
sort: 1, // 商品排序
|
||||
giveIntegral: 1, // 赠送积分
|
||||
virtualSalesCount: 1, // 虚拟销量
|
||||
recommendHot: false, // 是否热卖
|
||||
recommendBenefit: false, // 是否优惠
|
||||
recommendBest: false, // 是否精品
|
||||
recommendNew: false, // 是否新品
|
||||
recommendGood: false // 是否优品
|
||||
}
|
||||
}
|
||||
/** 关闭按钮 */
|
||||
const close = () => {
|
||||
resetForm()
|
||||
delView(unref(currentRoute))
|
||||
push('/product/product-management')
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px">
|
||||
<el-form ref="ProductManagementBasicInfoRef" :model="formData" :rules="rules" label-width="120px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="商品名称" prop="name">
|
||||
@ -40,26 +40,12 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="商品封面图" prop="picUrl">
|
||||
<div class="demo-image__preview pt-5px pb-5px pl-11x pr-11px">
|
||||
<el-image
|
||||
:initial-index="0"
|
||||
:preview-src-list="srcList"
|
||||
:src="url"
|
||||
:zoom-rate="1.2"
|
||||
fit="cover"
|
||||
style="width: 100%; height: 90px"
|
||||
/>
|
||||
</div>
|
||||
<UploadImg v-model="formData.picUrl" height="80px" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="商品轮播图" prop="sliderPicUrls">
|
||||
<el-button>添加轮播图</el-button>
|
||||
<el-carousel :interval="3000" height="200px" style="width: 100%" type="card">
|
||||
<el-carousel-item v-for="item in 6" :key="item">
|
||||
<h3 justify="center" text="2xl">{{ item }}</h3>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
<UploadImgs v-model="formData.sliderPicUrls" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@ -72,6 +58,7 @@
|
||||
<el-col :span="12">
|
||||
<el-button class="ml-20px">运费模板</el-button>
|
||||
</el-col>
|
||||
<!-- TODO 商品规格和分销类型切换待定 -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="商品规格" props="specType">
|
||||
<el-radio-group v-model="formData.specType" @change="changeSpecType(formData.specType)">
|
||||
@ -113,23 +100,31 @@
|
||||
</el-form>
|
||||
</template>
|
||||
<script lang="ts" name="ProductManagementBasicInfoForm" setup>
|
||||
// TODO 商品封面测试数据
|
||||
import { PropType } from 'vue'
|
||||
import { defaultProps } from '@/utils/tree'
|
||||
import type { SpuType } from '@/api/mall/product/management/type'
|
||||
import { UploadImg, UploadImgs } from '@/components/UploadFile'
|
||||
import { copyValueToTarget } from '@/utils/object'
|
||||
|
||||
const url = 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg'
|
||||
const srcList = ['https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg']
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps({
|
||||
propFormData: {
|
||||
type: Object as PropType<SpuType>,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
|
||||
const formRef = ref()
|
||||
const formData = ref({
|
||||
const ProductManagementBasicInfoRef = ref() // 表单Ref
|
||||
const formData = ref<SpuType>({
|
||||
name: '', // 商品名称
|
||||
categoryId: '', // 商品分类
|
||||
categoryId: 155415, // 商品分类
|
||||
keyword: '', // 关键字
|
||||
unit: '', // 单位
|
||||
picUrl: '', // 商品封面图
|
||||
sliderPicUrls: [], // 商品轮播图
|
||||
introduction: '', // 商品简介
|
||||
deliveryTemplateId: '', // 运费模版
|
||||
selectRule: '',
|
||||
selectRule: '', // 选择规则 TODO 暂定
|
||||
specType: false, // 商品规格
|
||||
subCommissionType: false // 分销类型
|
||||
})
|
||||
@ -138,12 +133,47 @@ const rules = reactive({
|
||||
categoryId: [required],
|
||||
keyword: [required],
|
||||
unit: [required],
|
||||
introduction: [required],
|
||||
picUrl: [required],
|
||||
sliderPicUrls: [required],
|
||||
deliveryTemplateId: [required],
|
||||
specType: [required],
|
||||
subCommissionType: [required]
|
||||
sliderPicUrls: [required]
|
||||
// deliveryTemplateId: [required],
|
||||
// specType: [required],
|
||||
// subCommissionType: [required],
|
||||
})
|
||||
/**
|
||||
* 将传进来的值赋值给formData
|
||||
*/
|
||||
watch(
|
||||
() => props.propFormData,
|
||||
(data) => {
|
||||
if (!data) return
|
||||
copyValueToTarget(formData.value, data)
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
const emit = defineEmits(['update:activeName'])
|
||||
/**
|
||||
* 表单校验
|
||||
*/
|
||||
const validate = async () => {
|
||||
// 校验表单
|
||||
if (!ProductManagementBasicInfoRef) return
|
||||
return await unref(ProductManagementBasicInfoRef).validate((valid) => {
|
||||
if (!valid) {
|
||||
message.warning('商品信息未完善!!')
|
||||
emit('update:activeName', 'basicInfo')
|
||||
// 目的截断之后的校验
|
||||
throw new Error('商品信息未完善!!')
|
||||
} else {
|
||||
// 校验通过更新数据
|
||||
Object.assign(props.propFormData, formData.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
defineExpose({ validate })
|
||||
// 选择规格
|
||||
const changeSpecType = (specType) => {
|
||||
console.log(specType)
|
||||
@ -157,35 +187,3 @@ const confirm = () => {}
|
||||
// 添加规格
|
||||
const addRule = () => {}
|
||||
</script>
|
||||
<style scoped>
|
||||
/*TODO 商品轮播图测试样式*/
|
||||
.el-carousel__item h3 {
|
||||
color: #475669;
|
||||
opacity: 0.75;
|
||||
line-height: 200px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-carousel__item:nth-child(2n) {
|
||||
background-color: #99a9bf;
|
||||
}
|
||||
|
||||
.el-carousel__item:nth-child(2n + 1) {
|
||||
background-color: #d3dce6;
|
||||
}
|
||||
|
||||
/*TODO 商品封面测试样式*/
|
||||
.demo-image__error .image-slot {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.demo-image__error .image-slot .el-icon {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.demo-image__error .el-image {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,13 +1,79 @@
|
||||
<template>
|
||||
<!--富文本编辑器组件-->
|
||||
<el-row>
|
||||
<Editor v-model="content" :editor-config="editorConfig" />
|
||||
</el-row>
|
||||
<el-form ref="DescriptionFormRef" :model="formData" :rules="rules" label-width="120px">
|
||||
<!--富文本编辑器组件-->
|
||||
<el-form-item label="商品详情" prop="description">
|
||||
<Editor v-model:modelValue="formData.description" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script lang="ts" name="DescriptionForm" setup>
|
||||
import type { SpuType } from '@/api/mall/product/management/type'
|
||||
import { Editor } from '@/components/Editor'
|
||||
import { createEditorConfig } from '@/views/mp/draft/editor-config'
|
||||
// TODO 模拟参数
|
||||
const content = ref('')
|
||||
const editorConfig = createEditorConfig('', 1)
|
||||
import { PropType } from 'vue'
|
||||
import { copyValueToTarget } from '@/utils/object'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps({
|
||||
propFormData: {
|
||||
type: Object as PropType<SpuType>,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
const DescriptionFormRef = ref() // 表单Ref
|
||||
const formData = ref<SpuType>({
|
||||
description: '' // 商品详情
|
||||
})
|
||||
/**
|
||||
* 富文本编辑器如果输入过再清空会有残留,需再重置一次
|
||||
*/
|
||||
watch(
|
||||
() => formData.value.description,
|
||||
(newValue) => {
|
||||
if ('<p><br></p>' === newValue) {
|
||||
formData.value.description = ''
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
// 表单规则
|
||||
const rules = reactive({
|
||||
description: [required]
|
||||
})
|
||||
/**
|
||||
* 将传进来的值赋值给formData
|
||||
*/
|
||||
watch(
|
||||
() => props.propFormData,
|
||||
(data) => {
|
||||
if (!data) return
|
||||
copyValueToTarget(formData.value, data)
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
const emit = defineEmits(['update:activeName'])
|
||||
/**
|
||||
* 表单校验
|
||||
*/
|
||||
const validate = async () => {
|
||||
// 校验表单
|
||||
if (!DescriptionFormRef) return
|
||||
return unref(DescriptionFormRef).validate((valid) => {
|
||||
if (!valid) {
|
||||
message.warning('商品详情为完善!!')
|
||||
emit('update:activeName', 'description')
|
||||
// 目的截断之后的校验
|
||||
throw new Error('商品详情为完善!!')
|
||||
} else {
|
||||
// 校验通过更新数据
|
||||
Object.assign(props.propFormData, formData.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
defineExpose({ validate })
|
||||
</script>
|
||||
|
@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px">
|
||||
<el-form ref="OtherSettingsFormRef" :model="formData" :rules="rules" label-width="120px">
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="商品排序">
|
||||
<el-form-item label="商品排序" prop="sort">
|
||||
<el-input-number v-model="formData.sort" :min="0" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="赠送积分">
|
||||
<el-form-item label="赠送积分" prop="giveIntegral">
|
||||
<el-input-number v-model="formData.giveIntegral" :min="0" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="虚拟销量">
|
||||
<el-form-item label="虚拟销量" prop="virtualSalesCount">
|
||||
<el-input-number
|
||||
v-model="formData.virtualSalesCount"
|
||||
:min="0"
|
||||
@ -50,6 +50,18 @@
|
||||
</template>
|
||||
<script lang="ts" name="OtherSettingsForm" setup>
|
||||
// 商品推荐
|
||||
import type { SpuType } from '@/api/mall/product/management/type'
|
||||
import { PropType } from 'vue'
|
||||
import { copyValueToTarget } from '@/utils/object'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps({
|
||||
propFormData: {
|
||||
type: Object as PropType<SpuType>,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
// 商品推荐选项
|
||||
const recommend = [
|
||||
{ name: '是否热卖', value: 'recommendHot' },
|
||||
{ name: '是否优惠', value: 'recommendBenefit' },
|
||||
@ -57,7 +69,9 @@ const recommend = [
|
||||
{ name: '是否新品', value: 'recommendNew' },
|
||||
{ name: '是否优品', value: 'recommendGood' }
|
||||
]
|
||||
const checkboxGroup = ref<string[]>([])
|
||||
// 选中推荐选项
|
||||
const checkboxGroup = ref<string[]>(['recommendHot'])
|
||||
// 选择商品后赋值
|
||||
const onChangeGroup = () => {
|
||||
checkboxGroup.value.includes('recommendHot')
|
||||
? (formData.value.recommendHot = true)
|
||||
@ -75,20 +89,63 @@ const onChangeGroup = () => {
|
||||
? (formData.value.recommendGood = true)
|
||||
: (formData.value.recommendGood = false)
|
||||
}
|
||||
const formRef = ref()
|
||||
const formData = ref({
|
||||
sort: '',
|
||||
giveIntegral: 666,
|
||||
virtualSalesCount: 565656,
|
||||
recommendHot: false,
|
||||
recommendBenefit: false,
|
||||
recommendBest: false,
|
||||
recommendNew: false,
|
||||
recommendGood: false
|
||||
const OtherSettingsFormRef = ref() // 表单Ref
|
||||
// 表单数据
|
||||
const formData = ref<SpuType>({
|
||||
sort: 12, // 商品排序
|
||||
giveIntegral: 666, // 赠送积分
|
||||
virtualSalesCount: 565656, // 虚拟销量
|
||||
recommendHot: false, // 是否热卖
|
||||
recommendBenefit: false, // 是否优惠
|
||||
recommendBest: false, // 是否精品
|
||||
recommendNew: false, // 是否新品
|
||||
recommendGood: false // 是否优品
|
||||
})
|
||||
// 表单规则
|
||||
const rules = reactive({
|
||||
sort: [required],
|
||||
giveIntegral: [required],
|
||||
virtualSalesCount: [required]
|
||||
})
|
||||
/**
|
||||
* 将传进来的值赋值给formData
|
||||
*/
|
||||
watch(
|
||||
() => props.propFormData,
|
||||
(data) => {
|
||||
if (!data) return
|
||||
copyValueToTarget(formData.value, data)
|
||||
checkboxGroup.value = []
|
||||
formData.value.recommendHot ? checkboxGroup.value.push('recommendHot') : ''
|
||||
formData.value.recommendBenefit ? checkboxGroup.value.push('recommendBenefit') : ''
|
||||
formData.value.recommendBest ? checkboxGroup.value.push('recommendBest') : ''
|
||||
formData.value.recommendNew ? checkboxGroup.value.push('recommendNew') : ''
|
||||
formData.value.recommendGood ? checkboxGroup.value.push('recommendGood') : ''
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
const emit = defineEmits(['update:activeName'])
|
||||
/**
|
||||
* 表单校验
|
||||
*/
|
||||
const validate = async () => {
|
||||
// 校验表单
|
||||
if (!OtherSettingsFormRef) return
|
||||
return await unref(OtherSettingsFormRef).validate((valid) => {
|
||||
if (!valid) {
|
||||
message.warning('商品其他设置未完善!!')
|
||||
emit('update:activeName', 'otherSettings')
|
||||
// 目的截断之后的校验
|
||||
throw new Error('商品其他设置未完善!!')
|
||||
} else {
|
||||
// 校验通过更新数据
|
||||
Object.assign(props.propFormData, formData.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ validate })
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user