fix: 修改 review 提到的问题,完善分类选择层级校验、完整层级展示

This commit is contained in:
puhui999
2023-05-30 18:14:40 +08:00
parent 820d8ab76a
commit e555977757
9 changed files with 238 additions and 88 deletions

View File

@@ -7,7 +7,7 @@
</el-form-item>
</el-col>
<el-col :span="12">
<!-- TODO @puhui999只能选根节点 -->
<!-- TODO @puhui999只能选根节点 fix: 已完善-->
<el-form-item label="商品分类" prop="categoryId">
<el-tree-select
v-model="formData.categoryId"
@@ -17,6 +17,7 @@
class="w-1/1"
node-key="id"
placeholder="请选择商品分类"
@change="nodeClick"
/>
</el-form-item>
</el-col>
@@ -119,9 +120,9 @@
import { PropType } from 'vue'
import { copyValueToTarget } from '@/utils'
import { propTypes } from '@/utils/propTypes'
import { defaultProps, handleTree } from '@/utils/tree'
import { checkSelectedNode, defaultProps, handleTree } from '@/utils/tree'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import type { SpuType } from '@/api/mall/product/spu'
import type { Spu } from '@/api/mall/product/spu'
import { UploadImg, UploadImgs } from '@/components/UploadFile'
import { ProductAttributes, ProductAttributesAddForm, SkuList } from './index'
import * as ProductCategoryApi from '@/api/mall/product/category'
@@ -131,7 +132,7 @@ const message = useMessage() // 消息弹窗
const props = defineProps({
propFormData: {
type: Object as PropType<SpuType>,
type: Object as PropType<Spu>,
default: () => {}
},
activeName: propTypes.string.def('')
@@ -144,7 +145,7 @@ const skuListRef = ref() // 商品属性列表Ref
const generateSkus = (propertyList) => {
skuListRef.value.generateTableData(propertyList)
}
const formData = reactive<SpuType>({
const formData = reactive<Spu>({
name: '', // 商品名称
categoryId: null, // 商品分类
keyword: '', // 关键字
@@ -185,26 +186,24 @@ watch(
formData.sliderPicUrls = data['sliderPicUrls'].map((item) => ({
url: item
}))
// TODO @puhui999if return减少嵌套层级
// 只有是多规格才处理
if (formData.specType) {
// 直接拿返回的 skus 属性逆向生成出 propertyList
const properties = []
formData.skus.forEach((sku) => {
sku.properties.forEach(({ propertyId, propertyName, valueId, valueName }) => {
// 添加属性
if (!properties.some((item) => item.id === propertyId)) {
properties.push({ id: propertyId, name: propertyName, values: [] })
}
// 添加属性值
const index = properties.findIndex((item) => item.id === propertyId)
if (!properties[index].values.some((value) => value.id === valueId)) {
properties[index].values.push({ id: valueId, name: valueName })
}
})
if (!formData.specType) return
// 直接拿返回的 skus 属性逆向生成出 propertyList
const properties = []
formData.skus.forEach((sku) => {
sku.properties.forEach(({ propertyId, propertyName, valueId, valueName }) => {
// 添加属性
if (!properties.some((item) => item.id === propertyId)) {
properties.push({ id: propertyId, name: propertyName, values: [] })
}
// 添加属性值
const index = properties.findIndex((item) => item.id === propertyId)
if (!properties[index].values.some((value) => value.id === valueId)) {
properties[index].values.push({ id: valueId, name: valueName })
}
})
propertyList.value = properties
}
})
propertyList.value = properties
},
{
immediate: true
@@ -216,6 +215,11 @@ watch(
*/
const emit = defineEmits(['update:activeName'])
const validate = async () => {
// 校验 sku
if (!skuListRef.value.validateSku()) {
message.warning('商品相关价格不能低于0.01元!!')
throw new Error('商品相关价格不能低于0.01元!!')
}
// 校验表单
if (!productSpuBasicInfoRef) return
return await unref(productSpuBasicInfoRef).validate((valid) => {
@@ -263,6 +267,15 @@ const onChangeSpec = () => {
}
const categoryList = ref([]) // 分类树
/**
* 选择分类时触发校验
*/
const nodeClick = () => {
if (!checkSelectedNode(categoryList.value, formData.categoryId)) {
formData.categoryId = null
message.warning('必须选择二级节点!!')
}
}
const brandList = ref([]) // 精简商品品牌列表
onMounted(async () => {
// 获得分类树

View File

@@ -7,7 +7,7 @@
</el-form>
</template>
<script lang="ts" name="DescriptionForm" setup>
import type { SpuType } from '@/api/mall/product/spu'
import type { Spu } from '@/api/mall/product/spu'
import { Editor } from '@/components/Editor'
import { PropType } from 'vue'
import { propTypes } from '@/utils/propTypes'
@@ -16,13 +16,13 @@ import { copyValueToTarget } from '@/utils'
const message = useMessage() // 消息弹窗
const props = defineProps({
propFormData: {
type: Object as PropType<SpuType>,
type: Object as PropType<Spu>,
default: () => {}
},
activeName: propTypes.string.def('')
})
const descriptionFormRef = ref() // 表单Ref
const formData = ref<SpuType>({
const formData = ref<Spu>({
description: '' // 商品详情
})
// 表单规则

View File

@@ -52,7 +52,7 @@
</el-form>
</template>
<script lang="ts" name="OtherSettingsForm" setup>
import type { SpuType } from '@/api/mall/product/spu'
import type { Spu } from '@/api/mall/product/spu'
import { PropType } from 'vue'
import { propTypes } from '@/utils/propTypes'
import { copyValueToTarget } from '@/utils'
@@ -61,7 +61,7 @@ const message = useMessage() // 消息弹窗
const props = defineProps({
propFormData: {
type: Object as PropType<SpuType>,
type: Object as PropType<Spu>,
default: () => {}
},
activeName: propTypes.string.def('')
@@ -69,7 +69,7 @@ const props = defineProps({
const otherSettingsFormRef = ref() // 表单Ref
// 表单数据
const formData = ref<SpuType>({
const formData = ref<Spu>({
sort: 1, // 商品排序
giveIntegral: 1, // 赠送积分
virtualSalesCount: 1, // 虚拟销量

View File

@@ -90,8 +90,7 @@ const submitForm = async () => {
/** 重置表单 */
const resetForm = () => {
formData.value = {
name: '',
remark: ''
name: ''
}
formRef.value?.resetFields()
}

View File

@@ -1,6 +1,6 @@
<template>
<el-table
:data="isBatch ? skuList : formData.skus"
:data="isBatch ? skuList : formData!.skus"
border
class="tabNumWidth"
max-height="500"
@@ -11,7 +11,7 @@
<UploadImg v-model="row.picUrl" height="80px" width="100%" />
</template>
</el-table-column>
<template v-if="formData.specType && !isBatch">
<template v-if="formData!.specType && !isBatch">
<!-- 根据商品属性动态添加 -->
<el-table-column
v-for="(item, index) in tableHeaders"
@@ -21,8 +21,10 @@
min-width="120"
>
<template #default="{ row }">
<!-- TODO puhui999展示成蓝色有点区分度哈 -->
{{ row.properties[index]?.valueName }}
<!-- TODO puhui999展示成蓝色有点区分度哈 fix: 字体加粗颜色使用 #99a9bf 蓝色有点不好看哈哈-->
<span style="font-weight: bold; color: #99a9bf">
{{ row.properties[index]?.valueName }}
</span>
</template>
</el-table-column>
</template>
@@ -73,7 +75,7 @@
<el-input-number v-model="row.volume" :min="0" :precision="2" :step="0.1" class="w-100%" />
</template>
</el-table-column>
<template v-if="formData.subCommissionType">
<template v-if="formData!.subCommissionType">
<el-table-column align="center" label="一级返佣(元)" min-width="168">
<template #default="{ row }">
<el-input-number
@@ -97,7 +99,7 @@
</template>
</el-table-column>
</template>
<el-table-column v-if="formData.specType" align="center" fixed="right" label="操作" width="80">
<el-table-column v-if="formData?.specType" align="center" fixed="right" label="操作" width="80">
<template #default="{ row }">
<el-button v-if="isBatch" link size="small" type="primary" @click="batchAdd">
批量添加
@@ -108,15 +110,15 @@
</el-table>
</template>
<script lang="ts" name="SkuList" setup>
import { PropType } from 'vue'
import { PropType, Ref } from 'vue'
import { copyValueToTarget } from '@/utils'
import { propTypes } from '@/utils/propTypes'
import { UploadImg } from '@/components/UploadFile'
import type { Property, SkuType, SpuType } from '@/api/mall/product/spu'
import type { Property, Sku, Spu } from '@/api/mall/product/spu'
const props = defineProps({
propFormData: {
type: Object as PropType<SpuType>,
type: Object as PropType<Spu>,
default: () => {}
},
propertyList: {
@@ -125,8 +127,8 @@ const props = defineProps({
},
isBatch: propTypes.bool.def(false) // 是否作为批量操作组件
})
const formData = ref<SpuType>() // 表单数据
const skuList = ref<SkuType[]>([
const formData: Ref<Spu | undefined> = ref<Spu>() // 表单数据
const skuList = ref<Sku[]>([
{
price: 0, // 商品价格
marketPrice: 0, // 市场价
@@ -140,24 +142,37 @@ const skuList = ref<SkuType[]>([
subCommissionSecondPrice: 0 // 二级分销的佣金
}
]) // 批量添加时的临时数据
// TODO @puhui999保存时每个商品规格的表单要校验下。例如说销售金额最低是 0.01 这种。
/** 批量添加 */
const batchAdd = () => {
formData.value.skus.forEach((item) => {
formData.value!.skus.forEach((item) => {
copyValueToTarget(item, skuList.value[0])
})
}
/** 删除 sku */
const deleteSku = (row) => {
const index = formData.value.skus.findIndex(
const index = formData.value!.skus.findIndex(
// 直接把列表转成字符串比较
(sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties)
)
formData.value.skus.splice(index, 1)
formData.value!.skus.splice(index, 1)
}
const tableHeaders = ref<{ prop: string; label: string }[]>([]) // 多属性表头
/**
* 保存时,每个商品规格的表单要校验下。例如说,销售金额最低是 0.01 这种。
*/
const validateSku = (): boolean => {
const checks = ['price', 'marketPrice', 'costPrice']
let validate = true // 默认通过
for (const sku of formData.value!.skus) {
if (checks.some((check) => sku[check] < 0.01)) {
validate = false // 只要有一个不通过则直接不通过
break
}
}
return validate
}
/**
* 将传进来的值赋值给 skuList
@@ -185,14 +200,13 @@ const generateTableData = (propertyList: any[]) => {
valueName: v.name
}))
)
// TODO @puhui是不是 buildSkuList这样容易理解一点哈。item 改成 sku
const buildList = build(propertyValues)
const buildSkuList = build(propertyValues)
// 如果回显的 sku 属性和添加的属性不一致则重置 skus 列表
if (!validateData(propertyList)) {
// 如果不一致则重置表数据,默认添加新的属性重新生成 sku 列表
formData.value!.skus = []
}
for (const item of buildList) {
for (const item of buildSkuList) {
const row = {
properties: Array.isArray(item) ? item : [item], // 如果只有一个属性的话返回的是一个 property 对象
price: 0,
@@ -213,7 +227,7 @@ const generateTableData = (propertyList: any[]) => {
if (index !== -1) {
continue
}
formData.value.skus.push(row)
formData.value!.skus.push(row)
}
}
@@ -222,7 +236,7 @@ const generateTableData = (propertyList: any[]) => {
*/
const validateData = (propertyList: any[]) => {
const skuPropertyIds = []
formData.value.skus.forEach((sku) =>
formData.value!.skus.forEach((sku) =>
sku.properties
?.map((property) => property.propertyId)
.forEach((propertyId) => {
@@ -263,7 +277,7 @@ watch(
() => props.propertyList,
(propertyList) => {
// 如果不是多规格则结束
if (!formData.value.specType) {
if (!formData.value!.specType) {
return
}
// 如果当前组件作为批量添加数据使用,则重置表数据
@@ -313,5 +327,5 @@ watch(
}
)
// 暴露出生成 sku 方法,给添加属性成功时调用
defineExpose({ generateTableData })
defineExpose({ generateTableData, validateSku })
</script>