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

(cherry picked from commit e555977757)
This commit is contained in:
puhui999 2023-05-30 18:14:40 +08:00 committed by shizhong
parent e4d6b4301b
commit cf7f4ff89e
9 changed files with 238 additions and 88 deletions

View File

@ -7,8 +7,7 @@ export interface Property {
valueName?: string // 属性值名称 valueName?: string // 属性值名称
} }
// TODO puhui999是不是直接叫 Sku 更简洁一点哈。type 待后面,总感觉有个类型? export interface Sku {
export interface SkuType {
id?: number // 商品 SKU 编号 id?: number // 商品 SKU 编号
spuId?: number // SPU 编号 spuId?: number // SPU 编号
properties?: Property[] // 属性数组 properties?: Property[] // 属性数组
@ -25,8 +24,7 @@ export interface SkuType {
salesCount?: number // 商品销量 salesCount?: number // 商品销量
} }
// TODO puhui999是不是直接叫 Spu 更简洁一点哈。type 待后面,总感觉有个类型? export interface Spu {
export interface SpuType {
id?: number id?: number
name?: string // 商品名称 name?: string // 商品名称
categoryId?: number | null // 商品分类 categoryId?: number | null // 商品分类
@ -39,9 +37,9 @@ export interface SpuType {
brandId?: number | null // 商品品牌编号 brandId?: number | null // 商品品牌编号
specType?: boolean // 商品规格 specType?: boolean // 商品规格
subCommissionType?: boolean // 分销类型 subCommissionType?: boolean // 分销类型
skus: SkuType[] // sku数组 skus: Sku[] // sku数组
description?: string // 商品详情 description?: string // 商品详情
sort?: string // 商品排序 sort?: number // 商品排序
giveIntegral?: number // 赠送积分 giveIntegral?: number // 赠送积分
virtualSalesCount?: number // 虚拟销量 virtualSalesCount?: number // 虚拟销量
recommendHot?: boolean // 是否热卖 recommendHot?: boolean // 是否热卖
@ -62,12 +60,12 @@ export const getTabsCount = () => {
} }
// 创建商品 Spu // 创建商品 Spu
export const createSpu = (data: SpuType) => { export const createSpu = (data: Spu) => {
return request.post({ url: '/product/spu/create', data }) return request.post({ url: '/product/spu/create', data })
} }
// 更新商品 Spu // 更新商品 Spu
export const updateSpu = (data: SpuType) => { export const updateSpu = (data: Spu) => {
return request.put({ url: '/product/spu/update', data }) return request.put({ url: '/product/spu/update', data })
} }

View File

@ -3,6 +3,7 @@ interface TreeHelperConfig {
children: string children: string
pid: string pid: string
} }
const DEFAULT_CONFIG: TreeHelperConfig = { const DEFAULT_CONFIG: TreeHelperConfig = {
id: 'id', id: 'id',
children: 'children', children: 'children',
@ -133,6 +134,7 @@ export const filter = <T = any>(
): T[] => { ): T[] => {
config = getConfig(config) config = getConfig(config)
const children = config.children as string const children = config.children as string
function listFilter(list: T[]) { function listFilter(list: T[]) {
return list return list
.map((node: any) => ({ ...node })) .map((node: any) => ({ ...node }))
@ -141,6 +143,7 @@ export const filter = <T = any>(
return func(node) || (node[children] && node[children].length) return func(node) || (node[children] && node[children].length)
}) })
} }
return listFilter(tree) return listFilter(tree)
} }
@ -264,6 +267,7 @@ export const handleTree = (data: any[], id?: string, parentId?: string, children
} }
} }
} }
return tree return tree
} }
/** /**
@ -300,3 +304,80 @@ export const handleTree2 = (data, id, parentId, children, rootId) => {
}) })
return treeData !== '' ? treeData : data return treeData !== '' ? treeData : data
} }
/**
*
* @param tree
* @param nodeId
* @param level ,
*/
export const checkSelectedNode = (tree: any[], nodeId, level = 2) => {
if (typeof tree === 'undefined' || !Array.isArray(tree) || tree.length === 0) {
console.warn('tree must be an array')
return false
}
// 校验是否是一级节点
if (tree.some((item) => item.id === nodeId)) {
return false
}
// 递归计数
let count = 1
// 深层次校验
function performAThoroughValidation(arr) {
count += 1
for (const item of arr) {
if (item.id === nodeId) {
return true
} else if (typeof item.children !== 'undefined' && item.children.length !== 0) {
performAThoroughValidation(item.children)
}
}
return false
}
for (const item of tree) {
count = 1
if (performAThoroughValidation(item.children)) {
// 找到后对比是否是期望的层级
if (count >= level) return true
}
}
return false
}
/**
*
* @param tree
* @param nodeId id
*/
export const treeToString = (tree: any[], nodeId) => {
if (typeof tree === 'undefined' || !Array.isArray(tree) || tree.length === 0) {
console.warn('tree must be an array')
return ''
}
// 校验是否是一级节点
const node = tree.find((item) => item.id === nodeId)
if (typeof node !== 'undefined') {
return node.name
}
let str = ''
function performAThoroughValidation(arr) {
for (const item of arr) {
if (item.id === nodeId) {
str += `/${item.name}`
return true
} else if (typeof item.children !== 'undefined' && item.children.length !== 0) {
performAThoroughValidation(item.children)
}
}
return false
}
for (const item of tree) {
str = `${item.name}`
if (performAThoroughValidation(item.children)) {
break
}
}
return str
}

View File

@ -51,15 +51,15 @@ const basicInfoRef = ref<ComponentRef<typeof BasicInfoForm>>() // 商品信息Re
const descriptionRef = ref<ComponentRef<typeof DescriptionForm>>() // Ref const descriptionRef = ref<ComponentRef<typeof DescriptionForm>>() // Ref
const otherSettingsRef = ref<ComponentRef<typeof OtherSettingsForm>>() // Ref const otherSettingsRef = ref<ComponentRef<typeof OtherSettingsForm>>() // Ref
// spu // spu
const formData = ref<ProductSpuApi.SpuType>({ const formData = ref<ProductSpuApi.Spu>({
name: '', // name: '', //
categoryId: null, // categoryId: null, //
keyword: '', // keyword: '', //
unit: null, // unit: null, //
picUrl: '', // picUrl: '', //
sliderPicUrls: [], // sliderPicUrls: [''], //
introduction: '', // introduction: '', //
deliveryTemplateId: 1, // deliveryTemplateId: null, //
brandId: null, // brandId: null, //
specType: false, // specType: false, //
subCommissionType: false, // subCommissionType: false, //
@ -94,7 +94,7 @@ const getDetail = async () => {
if (id) { if (id) {
formLoading.value = true formLoading.value = true
try { try {
const res = (await ProductSpuApi.getSpu(id)) as ProductSpuApi.SpuType const res = (await ProductSpuApi.getSpu(id)) as ProductSpuApi.Spu
res.skus.forEach((item) => { res.skus.forEach((item) => {
// //
item.price = formatToFraction(item.price) item.price = formatToFraction(item.price)
@ -120,8 +120,9 @@ const submitForm = async () => {
await unref(basicInfoRef)?.validate() await unref(basicInfoRef)?.validate()
await unref(descriptionRef)?.validate() await unref(descriptionRef)?.validate()
await unref(otherSettingsRef)?.validate() await unref(otherSettingsRef)?.validate()
const deepCopyFormData = cloneDeep(unref(formData.value)) // fix: server // , server
// TODO sku const deepCopyFormData = cloneDeep(unref(formData.value))
// sku
formData.value.skus.forEach((sku) => { formData.value.skus.forEach((sku) => {
// //
if (sku.barCode === '') { if (sku.barCode === '') {
@ -150,7 +151,7 @@ const submitForm = async () => {
}) })
deepCopyFormData.sliderPicUrls = newSliderPicUrls deepCopyFormData.sliderPicUrls = newSliderPicUrls
// //
const data = deepCopyFormData as ProductSpuApi.SpuType const data = deepCopyFormData as ProductSpuApi.Spu
const id = params.spuId as number const id = params.spuId as number
if (!id) { if (!id) {
await ProductSpuApi.createSpu(data) await ProductSpuApi.createSpu(data)

View File

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

View File

@ -7,7 +7,7 @@
</el-form> </el-form>
</template> </template>
<script lang="ts" name="DescriptionForm" setup> <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 { Editor } from '@/components/Editor'
import { PropType } from 'vue' import { PropType } from 'vue'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
@ -16,13 +16,13 @@ import { copyValueToTarget } from '@/utils'
const message = useMessage() // const message = useMessage() //
const props = defineProps({ const props = defineProps({
propFormData: { propFormData: {
type: Object as PropType<SpuType>, type: Object as PropType<Spu>,
default: () => {} default: () => {}
}, },
activeName: propTypes.string.def('') activeName: propTypes.string.def('')
}) })
const descriptionFormRef = ref() // Ref const descriptionFormRef = ref() // Ref
const formData = ref<SpuType>({ const formData = ref<Spu>({
description: '' // description: '' //
}) })
// //

View File

@ -52,7 +52,7 @@
</el-form> </el-form>
</template> </template>
<script lang="ts" name="OtherSettingsForm" setup> <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 { PropType } from 'vue'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { copyValueToTarget } from '@/utils' import { copyValueToTarget } from '@/utils'
@ -61,7 +61,7 @@ const message = useMessage() // 消息弹窗
const props = defineProps({ const props = defineProps({
propFormData: { propFormData: {
type: Object as PropType<SpuType>, type: Object as PropType<Spu>,
default: () => {} default: () => {}
}, },
activeName: propTypes.string.def('') activeName: propTypes.string.def('')
@ -69,7 +69,7 @@ const props = defineProps({
const otherSettingsFormRef = ref() // Ref const otherSettingsFormRef = ref() // Ref
// //
const formData = ref<SpuType>({ const formData = ref<Spu>({
sort: 1, // sort: 1, //
giveIntegral: 1, // giveIntegral: 1, //
virtualSalesCount: 1, // virtualSalesCount: 1, //

View File

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

View File

@ -1,6 +1,6 @@
<template> <template>
<el-table <el-table
:data="isBatch ? skuList : formData.skus" :data="isBatch ? skuList : formData!.skus"
border border
class="tabNumWidth" class="tabNumWidth"
max-height="500" max-height="500"
@ -11,7 +11,7 @@
<UploadImg v-model="row.picUrl" height="80px" width="100%" /> <UploadImg v-model="row.picUrl" height="80px" width="100%" />
</template> </template>
</el-table-column> </el-table-column>
<template v-if="formData.specType && !isBatch"> <template v-if="formData!.specType && !isBatch">
<!-- 根据商品属性动态添加 --> <!-- 根据商品属性动态添加 -->
<el-table-column <el-table-column
v-for="(item, index) in tableHeaders" v-for="(item, index) in tableHeaders"
@ -21,8 +21,10 @@
min-width="120" min-width="120"
> >
<template #default="{ row }"> <template #default="{ row }">
<!-- TODO puhui999展示成蓝色有点区分度哈 --> <!-- TODO puhui999展示成蓝色有点区分度哈 fix: 字体加粗颜色使用 #99a9bf 蓝色有点不好看哈哈-->
{{ row.properties[index]?.valueName }} <span style="font-weight: bold; color: #99a9bf">
{{ row.properties[index]?.valueName }}
</span>
</template> </template>
</el-table-column> </el-table-column>
</template> </template>
@ -73,7 +75,7 @@
<el-input-number v-model="row.volume" :min="0" :precision="2" :step="0.1" class="w-100%" /> <el-input-number v-model="row.volume" :min="0" :precision="2" :step="0.1" class="w-100%" />
</template> </template>
</el-table-column> </el-table-column>
<template v-if="formData.subCommissionType"> <template v-if="formData!.subCommissionType">
<el-table-column align="center" label="一级返佣(元)" min-width="168"> <el-table-column align="center" label="一级返佣(元)" min-width="168">
<template #default="{ row }"> <template #default="{ row }">
<el-input-number <el-input-number
@ -97,7 +99,7 @@
</template> </template>
</el-table-column> </el-table-column>
</template> </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 }"> <template #default="{ row }">
<el-button v-if="isBatch" link size="small" type="primary" @click="batchAdd"> <el-button v-if="isBatch" link size="small" type="primary" @click="batchAdd">
批量添加 批量添加
@ -108,15 +110,15 @@
</el-table> </el-table>
</template> </template>
<script lang="ts" name="SkuList" setup> <script lang="ts" name="SkuList" setup>
import { PropType } from 'vue' import { PropType, Ref } from 'vue'
import { copyValueToTarget } from '@/utils' import { copyValueToTarget } from '@/utils'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { UploadImg } from '@/components/UploadFile' 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({ const props = defineProps({
propFormData: { propFormData: {
type: Object as PropType<SpuType>, type: Object as PropType<Spu>,
default: () => {} default: () => {}
}, },
propertyList: { propertyList: {
@ -125,8 +127,8 @@ const props = defineProps({
}, },
isBatch: propTypes.bool.def(false) // isBatch: propTypes.bool.def(false) //
}) })
const formData = ref<SpuType>() // const formData: Ref<Spu | undefined> = ref<Spu>() //
const skuList = ref<SkuType[]>([ const skuList = ref<Sku[]>([
{ {
price: 0, // price: 0, //
marketPrice: 0, // marketPrice: 0, //
@ -140,24 +142,37 @@ const skuList = ref<SkuType[]>([
subCommissionSecondPrice: 0 // subCommissionSecondPrice: 0 //
} }
]) // ]) //
// TODO @puhui999 0.01
/** 批量添加 */ /** 批量添加 */
const batchAdd = () => { const batchAdd = () => {
formData.value.skus.forEach((item) => { formData.value!.skus.forEach((item) => {
copyValueToTarget(item, skuList.value[0]) copyValueToTarget(item, skuList.value[0])
}) })
} }
/** 删除 sku */ /** 删除 sku */
const deleteSku = (row) => { const deleteSku = (row) => {
const index = formData.value.skus.findIndex( const index = formData.value!.skus.findIndex(
// //
(sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties) (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 }[]>([]) // 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 * 将传进来的值赋值给 skuList
@ -185,14 +200,13 @@ const generateTableData = (propertyList: any[]) => {
valueName: v.name valueName: v.name
})) }))
) )
// TODO @puhui buildSkuListitem sku const buildSkuList = build(propertyValues)
const buildList = build(propertyValues)
// sku skus // sku skus
if (!validateData(propertyList)) { if (!validateData(propertyList)) {
// sku // sku
formData.value!.skus = [] formData.value!.skus = []
} }
for (const item of buildList) { for (const item of buildSkuList) {
const row = { const row = {
properties: Array.isArray(item) ? item : [item], // property properties: Array.isArray(item) ? item : [item], // property
price: 0, price: 0,
@ -213,7 +227,7 @@ const generateTableData = (propertyList: any[]) => {
if (index !== -1) { if (index !== -1) {
continue continue
} }
formData.value.skus.push(row) formData.value!.skus.push(row)
} }
} }
@ -222,7 +236,7 @@ const generateTableData = (propertyList: any[]) => {
*/ */
const validateData = (propertyList: any[]) => { const validateData = (propertyList: any[]) => {
const skuPropertyIds = [] const skuPropertyIds = []
formData.value.skus.forEach((sku) => formData.value!.skus.forEach((sku) =>
sku.properties sku.properties
?.map((property) => property.propertyId) ?.map((property) => property.propertyId)
.forEach((propertyId) => { .forEach((propertyId) => {
@ -263,7 +277,7 @@ watch(
() => props.propertyList, () => props.propertyList,
(propertyList) => { (propertyList) => {
// //
if (!formData.value.specType) { if (!formData.value!.specType) {
return return
} }
// 使 // 使
@ -313,5 +327,5 @@ watch(
} }
) )
// sku // sku
defineExpose({ generateTableData }) defineExpose({ generateTableData, validateSku })
</script> </script>

View File

@ -8,18 +8,16 @@
class="-mb-15px" class="-mb-15px"
label-width="68px" label-width="68px"
> >
<!-- TODO @puhui999品牌应该是数据下拉哈 --> <el-form-item label="商品名称" prop="name">
<el-form-item label="品牌名称" prop="name">
<el-input <el-input
v-model="queryParams.name" v-model="queryParams.name"
class="!w-240px" class="!w-240px"
clearable clearable
placeholder="请输入名称" placeholder="请输入品名称"
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
/> />
</el-form-item> </el-form-item>
<!-- TODO 分类只能选择二级分类目前还没做还是先以联调通顺为主 --> <!-- TODO 分类只能选择二级分类目前还没做还是先以联调通顺为主 fixL: 已完善 -->
<!-- TODO puhui999我们要不改成支持选择一级如果选择一级后端要递归查询下子分类然后去 in -->
<el-form-item label="商品分类" prop="categoryId"> <el-form-item label="商品分类" prop="categoryId">
<el-tree-select <el-tree-select
v-model="queryParams.categoryId" v-model="queryParams.categoryId"
@ -29,6 +27,7 @@
class="w-1/1" class="w-1/1"
node-key="id" node-key="id"
placeholder="请选择商品分类" placeholder="请选择商品分类"
@change="nodeClick"
/> />
</el-form-item> </el-form-item>
<el-form-item label="创建时间" prop="createTime"> <el-form-item label="创建时间" prop="createTime">
@ -80,31 +79,60 @@
/> />
</el-tabs> </el-tabs>
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list">
<!-- TODO puhui这几个属性哈一行三个 <!-- TODO puhui这几个属性哈一行三个 fix
商品分类服装鞋包/箱包 商品分类服装鞋包/箱包
商品市场价格100.00 商品市场价格100.00
成本价0.00 成本价0.00
收藏5 收藏5
虚拟销量999 --> 虚拟销量999 -->
<el-table-column type="expand" width="30"> <el-table-column type="expand" width="30">
<template #default="{ row }"> <template #default="{ row }">
<el-form class="demo-table-expand" inline label-position="left"> <el-form class="demo-table-expand" label-position="left">
<el-form-item label="市场价:"> <el-row>
<span>{{ formatToFraction(row.marketPrice) }}</span> <el-col :span="24">
</el-form-item> <el-row>
<el-form-item label="成本价:"> <el-col :span="8">
<span>{{ formatToFraction(row.costPrice) }}</span> <el-form-item label="商品分类:">
</el-form-item> <span>{{ categoryString(row.categoryId) }}</span>
<el-form-item label="虚拟销量:"> </el-form-item>
<span>{{ row.virtualSalesCount }}</span> </el-col>
</el-form-item> <el-col :span="8">
<el-form-item label="市场价:">
<span>{{ formatToFraction(row.marketPrice) }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="成本价:">
<span>{{ formatToFraction(row.costPrice) }}</span>
</el-form-item>
</el-col>
</el-row>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-row>
<el-col :span="8">
<el-form-item label="收藏:">
<!-- TODO 没有这个属性暂时写死 5 -->
<span>5</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="虚拟销量:">
<span>{{ row.virtualSalesCount }}</span>
</el-form-item>
</el-col>
</el-row>
</el-col>
</el-row>
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column key="id" align="center" label="商品编号" prop="id" /> <el-table-column key="id" align="center" label="商品编号" prop="id" />
<el-table-column label="商品图" min-width="80"> <el-table-column label="商品图" min-width="80">
<template #default="{ row }"> <template #default="{ row }">
<el-image :src="row.picUrl" @click="imagePreview(row.picUrl)" class="w-30px h-30px" /> <el-image :src="row.picUrl" class="w-30px h-30px" @click="imagePreview(row.picUrl)" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" /> <el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" />
@ -202,7 +230,7 @@ import { TabsPaneContext } from 'element-plus'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { createImageViewer } from '@/components/ImageViewer' import { createImageViewer } from '@/components/ImageViewer'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import { defaultProps, handleTree } from '@/utils/tree' import { checkSelectedNode, defaultProps, handleTree, treeToString } from '@/utils/tree'
import { ProductSpuStatusEnum } from '@/utils/constants' import { ProductSpuStatusEnum } from '@/utils/constants'
import { formatToFraction } from '@/utils' import { formatToFraction } from '@/utils'
import download from '@/utils/download' import download from '@/utils/download'
@ -391,7 +419,7 @@ const handleExport = async () => {
} }
} }
// TODO @puhui999fix: //
watch( watch(
() => currentRoute.value, () => currentRoute.value,
() => { () => {
@ -400,6 +428,22 @@ watch(
) )
const categoryList = ref() // const categoryList = ref() //
/**
* 获取分类的节点的完整结构
* @param categoryId 分类id
*/
const categoryString = (categoryId) => {
return treeToString(categoryList.value, categoryId)
}
/**
* 校验所选是否为二级节点
*/
const nodeClick = () => {
if (!checkSelectedNode(categoryList.value, queryParams.value.categoryId)) {
queryParams.value.categoryId = null
message.warning('必须选择二级节点!!')
}
}
/** 初始化 **/ /** 初始化 **/
onMounted(async () => { onMounted(async () => {
await getTabsCount() await getTabsCount()