Skip to content

Commit c38abc3

Browse files
author
puhui999
committed
商品管理: 完善表单校验,优化信息提示,完成新建、编辑、提交逻辑
1 parent a0014be commit c38abc3

File tree

7 files changed

+367
-126
lines changed

7 files changed

+367
-126
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export interface SpuType {
2+
name?: string // 商品名称
3+
categoryId?: number // 商品分类
4+
keyword?: string // 关键字
5+
unit?: string // 单位
6+
picUrl?: string // 商品封面图
7+
sliderPicUrls?: string[] // 商品轮播图
8+
introduction?: string // 商品简介
9+
deliveryTemplateId?: number // 运费模版
10+
selectRule?: string // 选择规格 TODO 暂时定义
11+
specType?: boolean // 商品规格
12+
subCommissionType?: boolean // 分销类型
13+
description?: string // 商品详情
14+
sort?: string // 商品排序
15+
giveIntegral?: number // 赠送积分
16+
virtualSalesCount?: number // 虚拟销量
17+
recommendHot?: boolean // 是否热卖
18+
recommendBenefit?: boolean // 是否优惠
19+
recommendBest?: boolean // 是否精品
20+
recommendNew?: boolean // 是否新品
21+
recommendGood?: boolean // 是否优品
22+
}

src/utils/object.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* 将值复制到目标对象,且以目标对象属性为准,例:target: {a:1} source:{a:2,b:3} 结果为:{a:2}
3+
* @param target 目标对象
4+
* @param source 源对象
5+
*/
6+
export const copyValueToTarget = (target, source) => {
7+
const newObj = Object.assign({}, target, source)
8+
// 删除多余属性
9+
Object.keys(newObj).forEach((key) => {
10+
// 如果不是target中的属性则删除
11+
if (Object.keys(target).indexOf(key) === -1) {
12+
delete newObj[key]
13+
}
14+
})
15+
// 更新目标对象值
16+
Object.assign(target, newObj)
17+
}

src/views/mall/product/management/addForm.vue

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,25 @@
22
<ContentWrap v-loading="formLoading">
33
<el-tabs v-model="activeName">
44
<el-tab-pane label="商品信息" name="basicInfo">
5-
<BasicInfoForm ref="basicInfoRef" />
5+
<BasicInfoForm
6+
ref="BasicInfoRef"
7+
v-model:activeName="activeName"
8+
:propFormData="formData"
9+
/>
610
</el-tab-pane>
711
<el-tab-pane label="商品详情" name="description">
8-
<DescriptionForm ref="DescriptionRef" />
12+
<DescriptionForm
13+
ref="DescriptionRef"
14+
v-model:activeName="activeName"
15+
:propFormData="formData"
16+
/>
917
</el-tab-pane>
1018
<el-tab-pane label="其他设置" name="otherSettings">
11-
<OtherSettingsForm ref="otherSettingsRef" />
19+
<OtherSettingsForm
20+
ref="OtherSettingsRef"
21+
v-model:activeName="activeName"
22+
:propFormData="formData"
23+
/>
1224
</el-tab-pane>
1325
</el-tabs>
1426
<el-form>
@@ -22,6 +34,7 @@
2234
<script lang="ts" name="ProductManagementForm" setup>
2335
import { useTagsViewStore } from '@/store/modules/tagsView'
2436
import { BasicInfoForm, DescriptionForm, OtherSettingsForm } from './components'
37+
import { SpuType } from '@/api/mall/product/management/type' // const { t } = useI18n() // 国际化
2538
2639
// const { t } = useI18n() // 国际化
2740
// const message = useMessage() // 消息弹窗
@@ -30,18 +43,77 @@ const { push, currentRoute } = useRouter() // 路由
3043
const { delView } = useTagsViewStore() // 视图操作
3144
3245
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
33-
const activeName = ref('otherSettings') // Tag 激活的窗口
34-
const basicInfoRef = ref<ComponentRef<typeof BasicInfoForm>>()
35-
const DescriptionRef = ref<ComponentRef<typeof DescriptionForm>>()
36-
46+
const activeName = ref('basicInfo') // Tag 激活的窗口
47+
const BasicInfoRef = ref<ComponentRef<typeof BasicInfoForm>>() // 商品信息Ref
48+
const DescriptionRef = ref<ComponentRef<typeof DescriptionForm>>() // 商品详情Ref
49+
const OtherSettingsRef = ref<ComponentRef<typeof OtherSettingsForm>>() // 其他设置Ref
50+
const formData = ref<SpuType>({
51+
name: '', // 商品名称
52+
categoryId: 0, // 商品分类
53+
keyword: '', // 关键字
54+
unit: '', // 单位
55+
picUrl: '', // 商品封面图
56+
sliderPicUrls: [], // 商品轮播图
57+
introduction: '', // 商品简介
58+
deliveryTemplateId: 0, // 运费模版
59+
selectRule: '',
60+
specType: false, // 商品规格
61+
subCommissionType: false, // 分销类型
62+
description: '', // 商品详情
63+
sort: 1, // 商品排序
64+
giveIntegral: 1, // 赠送积分
65+
virtualSalesCount: 1, // 虚拟销量
66+
recommendHot: false, // 是否热卖
67+
recommendBenefit: false, // 是否优惠
68+
recommendBest: false, // 是否精品
69+
recommendNew: false, // 是否新品
70+
recommendGood: false // 是否优品
71+
})
3772
/** 获得详情 */
3873
const getDetail = async () => {}
3974
4075
/** 提交按钮 */
41-
const submitForm = async () => {}
42-
76+
const submitForm = async () => {
77+
// TODO 三个表单逐一校验,如果有一个表单校验不通过则切换到对应表单,如果有两个及以上的情况则切换到最前面的一个并弹出提示消息
78+
// 校验各表单
79+
try {
80+
await unref(BasicInfoRef)?.validate()
81+
await unref(DescriptionRef)?.validate()
82+
await unref(OtherSettingsRef)?.validate()
83+
// 校验都通过后提交表单
84+
console.log(formData.value)
85+
} catch {}
86+
}
87+
/**
88+
* 重置表单
89+
*/
90+
const resetForm = async () => {
91+
formData.value = {
92+
name: '', // 商品名称
93+
categoryId: 0, // 商品分类
94+
keyword: '', // 关键字
95+
unit: '', // 单位
96+
picUrl: '', // 商品封面图
97+
sliderPicUrls: [], // 商品轮播图
98+
introduction: '', // 商品简介
99+
deliveryTemplateId: 0, // 运费模版
100+
selectRule: '',
101+
specType: false, // 商品规格
102+
subCommissionType: false, // 分销类型
103+
description: '', // 商品详情
104+
sort: 1, // 商品排序
105+
giveIntegral: 1, // 赠送积分
106+
virtualSalesCount: 1, // 虚拟销量
107+
recommendHot: false, // 是否热卖
108+
recommendBenefit: false, // 是否优惠
109+
recommendBest: false, // 是否精品
110+
recommendNew: false, // 是否新品
111+
recommendGood: false // 是否优品
112+
}
113+
}
43114
/** 关闭按钮 */
44115
const close = () => {
116+
resetForm()
45117
delView(unref(currentRoute))
46118
push('/product/product-management')
47119
}

src/views/mall/product/management/components/BasicInfoForm.vue

Lines changed: 58 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px">
2+
<el-form ref="ProductManagementBasicInfoRef" :model="formData" :rules="rules" label-width="120px">
33
<el-row>
44
<el-col :span="12">
55
<el-form-item label="商品名称" prop="name">
@@ -40,26 +40,12 @@
4040
</el-col>
4141
<el-col :span="12">
4242
<el-form-item label="商品封面图" prop="picUrl">
43-
<div class="demo-image__preview pt-5px pb-5px pl-11x pr-11px">
44-
<el-image
45-
:initial-index="0"
46-
:preview-src-list="srcList"
47-
:src="url"
48-
:zoom-rate="1.2"
49-
fit="cover"
50-
style="width: 100%; height: 90px"
51-
/>
52-
</div>
43+
<UploadImg v-model="formData.picUrl" height="80px" />
5344
</el-form-item>
5445
</el-col>
5546
<el-col :span="24">
5647
<el-form-item label="商品轮播图" prop="sliderPicUrls">
57-
<el-button>添加轮播图</el-button>
58-
<el-carousel :interval="3000" height="200px" style="width: 100%" type="card">
59-
<el-carousel-item v-for="item in 6" :key="item">
60-
<h3 justify="center" text="2xl">{{ item }}</h3>
61-
</el-carousel-item>
62-
</el-carousel>
48+
<UploadImgs v-model="formData.sliderPicUrls" />
6349
</el-form-item>
6450
</el-col>
6551
<el-col :span="12">
@@ -72,6 +58,7 @@
7258
<el-col :span="12">
7359
<el-button class="ml-20px">运费模板</el-button>
7460
</el-col>
61+
<!-- TODO 商品规格和分销类型切换待定 -->
7562
<el-col :span="12">
7663
<el-form-item label="商品规格" props="specType">
7764
<el-radio-group v-model="formData.specType" @change="changeSpecType(formData.specType)">
@@ -113,23 +100,31 @@
113100
</el-form>
114101
</template>
115102
<script lang="ts" name="ProductManagementBasicInfoForm" setup>
116-
// TODO 商品封面测试数据
103+
import { PropType } from 'vue'
117104
import { defaultProps } from '@/utils/tree'
105+
import type { SpuType } from '@/api/mall/product/management/type'
106+
import { UploadImg, UploadImgs } from '@/components/UploadFile'
107+
import { copyValueToTarget } from '@/utils/object'
118108
119-
const url = 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg'
120-
const srcList = ['https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg']
109+
const message = useMessage() // 消息弹窗
110+
const props = defineProps({
111+
propFormData: {
112+
type: Object as PropType<SpuType>,
113+
default: () => {}
114+
}
115+
})
121116
122-
const formRef = ref()
123-
const formData = ref({
117+
const ProductManagementBasicInfoRef = ref() // 表单Ref
118+
const formData = ref<SpuType>({
124119
name: '', // 商品名称
125-
categoryId: '', // 商品分类
120+
categoryId: 155415, // 商品分类
126121
keyword: '', // 关键字
127122
unit: '', // 单位
128123
picUrl: '', // 商品封面图
129124
sliderPicUrls: [], // 商品轮播图
130125
introduction: '', // 商品简介
131126
deliveryTemplateId: '', // 运费模版
132-
selectRule: '',
127+
selectRule: '', // 选择规则 TODO 暂定
133128
specType: false, // 商品规格
134129
subCommissionType: false // 分销类型
135130
})
@@ -138,12 +133,47 @@ const rules = reactive({
138133
categoryId: [required],
139134
keyword: [required],
140135
unit: [required],
136+
introduction: [required],
141137
picUrl: [required],
142-
sliderPicUrls: [required],
143-
deliveryTemplateId: [required],
144-
specType: [required],
145-
subCommissionType: [required]
138+
sliderPicUrls: [required]
139+
// deliveryTemplateId: [required],
140+
// specType: [required],
141+
// subCommissionType: [required],
146142
})
143+
/**
144+
* 将传进来的值赋值给formData
145+
*/
146+
watch(
147+
() => props.propFormData,
148+
(data) => {
149+
if (!data) return
150+
copyValueToTarget(formData.value, data)
151+
},
152+
{
153+
deep: true,
154+
immediate: true
155+
}
156+
)
157+
const emit = defineEmits(['update:activeName'])
158+
/**
159+
* 表单校验
160+
*/
161+
const validate = async () => {
162+
// 校验表单
163+
if (!ProductManagementBasicInfoRef) return
164+
return await unref(ProductManagementBasicInfoRef).validate((valid) => {
165+
if (!valid) {
166+
message.warning('商品信息未完善!!')
167+
emit('update:activeName', 'basicInfo')
168+
// 目的截断之后的校验
169+
throw new Error('商品信息未完善!!')
170+
} else {
171+
// 校验通过更新数据
172+
Object.assign(props.propFormData, formData.value)
173+
}
174+
})
175+
}
176+
defineExpose({ validate })
147177
// 选择规格
148178
const changeSpecType = (specType) => {
149179
console.log(specType)
@@ -157,35 +187,3 @@ const confirm = () => {}
157187
// 添加规格
158188
const addRule = () => {}
159189
</script>
160-
<style scoped>
161-
/*TODO 商品轮播图测试样式*/
162-
.el-carousel__item h3 {
163-
color: #475669;
164-
opacity: 0.75;
165-
line-height: 200px;
166-
margin: 0;
167-
text-align: center;
168-
}
169-
170-
.el-carousel__item:nth-child(2n) {
171-
background-color: #99a9bf;
172-
}
173-
174-
.el-carousel__item:nth-child(2n + 1) {
175-
background-color: #d3dce6;
176-
}
177-
178-
/*TODO 商品封面测试样式*/
179-
.demo-image__error .image-slot {
180-
font-size: 30px;
181-
}
182-
183-
.demo-image__error .image-slot .el-icon {
184-
font-size: 30px;
185-
}
186-
187-
.demo-image__error .el-image {
188-
width: 100%;
189-
height: 200px;
190-
}
191-
</style>

0 commit comments

Comments
 (0)