fix:解决商品上一版遗留的各种小bug关键部分已添加fix注释。完成的TODO也已添加fix标记

(cherry picked from commit 0c42b76a52)
This commit is contained in:
puhui999 2023-05-19 17:04:13 +08:00 committed by shizhong
parent f26c72ced9
commit 4410bdd93c
7 changed files with 205 additions and 75 deletions

View File

@ -54,3 +54,8 @@ export const getBrand = (id: number) => {
export const getBrandParam = (params: PageParam) => {
return request.get({ url: '/product/brand/page', params })
}
// 获得商品品牌精简信息列表
export const getSimpleBrandList = () => {
return request.get({ url: '/product/brand/list-all-simple' })
}

View File

@ -34,6 +34,7 @@ export interface SpuType {
sliderPicUrls?: string[] // 商品轮播图
introduction?: string // 商品简介
deliveryTemplateId?: number | null // 运费模版
brandId?: number | null // 商品品牌编号
specType?: boolean // 商品规格
subCommissionType?: boolean // 分销类型
skus: SkuType[] // sku数组

View File

@ -173,3 +173,24 @@ export const copyValueToTarget = (target, source) => {
// 更新目标对象值
Object.assign(target, newObj)
}
/**
*
* @param num
*/
export const formatToFraction = (num: number | string | undefined): number => {
if (typeof num === 'undefined') return 0
const parsedNumber = typeof num === 'string' ? parseFloat(num) : num
return parseFloat((parsedNumber / 100).toFixed(2))
}
/**
*
* @param num
*/
export const convertToInteger = (num: number | string | undefined): number => {
if (typeof num === 'undefined') return 0
const parsedNumber = typeof num === 'string' ? parseFloat(num) : num
// TODO 分转元后还有小数则四舍五入
return Math.round(parsedNumber * 100)
}

View File

@ -38,6 +38,7 @@ import { BasicInfoForm, DescriptionForm, OtherSettingsForm } from './components'
// api
import * as ProductSpuApi from '@/api/mall/product/spu'
import * as PropertyApi from '@/api/mall/product/property'
import { convertToInteger, formatToFraction } from '@/utils'
const { t } = useI18n() //
const message = useMessage() //
@ -60,6 +61,7 @@ const formData = ref<ProductSpuApi.SpuType>({
sliderPicUrls: [], //
introduction: '', //
deliveryTemplateId: 1, //
brandId: null, //
specType: false, //
subCommissionType: false, //
skus: [
@ -94,14 +96,34 @@ const getDetail = async () => {
formLoading.value = true
try {
const res = (await ProductSpuApi.getSpu(id)) as ProductSpuApi.SpuType
res.skus.forEach((item) => {
//
item.price = formatToFraction(item.price)
item.marketPrice = formatToFraction(item.marketPrice)
item.costPrice = formatToFraction(item.costPrice)
item.subCommissionFirstPrice = formatToFraction(item.subCommissionFirstPrice)
item.subCommissionSecondPrice = formatToFraction(item.subCommissionSecondPrice)
})
formData.value = res
// id
// TODO @puhui999 propertyName id + uniapp
const propertyIds = res.skus[0]?.properties.map((item) => item.propertyId)
const PropertyS = await PropertyApi.getPropertyListAndValue({ propertyIds })
await nextTick()
//
basicInfoRef.value.addAttribute(PropertyS)
//
if (res.specType) {
// TODO @puhui999 propertyName id + uniapp
// fix: sku sku
const propertyIds = []
res.skus.forEach((sku) =>
sku.properties
?.map((property) => property.propertyId)
.forEach((propertyId) => {
if (propertyIds.indexOf(propertyId) === -1) {
propertyIds.push(propertyId)
}
})
)
const properties = await PropertyApi.getPropertyListAndValue({ propertyIds })
await nextTick()
//
basicInfoRef.value.addAttribute(properties)
}
} finally {
formLoading.value = false
}
@ -119,10 +141,26 @@ const submitForm = async () => {
await unref(descriptionRef)?.validate()
await unref(otherSettingsRef)?.validate()
const deepCopyFormData = cloneDeep(unref(formData.value)) // fix: server
//
// TODO sku SkuList TODO
formData.value.skus.forEach((sku) => {
//
if (sku.barCode === '') {
const index = deepCopyFormData.skus.findIndex(
(item) => JSON.stringify(item.properties) === JSON.stringify(sku.properties)
)
// sku
deepCopyFormData.skus.splice(index, 1)
}
})
deepCopyFormData.skus.forEach((item) => {
// sku name
item.name = deepCopyFormData.name
// sku
item.price = convertToInteger(item.price)
item.marketPrice = convertToInteger(item.marketPrice)
item.costPrice = convertToInteger(item.costPrice)
item.subCommissionFirstPrice = convertToInteger(item.subCommissionFirstPrice)
item.subCommissionSecondPrice = convertToInteger(item.subCommissionSecondPrice)
})
//
const newSliderPicUrls = []
@ -148,34 +186,6 @@ const submitForm = async () => {
}
}
/**
* 重置表单
* fix:先注释保留如果后期没有使用到则移除
*/
// 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 = () => {
delView(unref(currentRoute))

View File

@ -59,13 +59,23 @@
</el-col>
<el-col :span="12">
<el-form-item label="运费模板" prop="deliveryTemplateId">
<el-select v-model="formData.deliveryTemplateId" class="w-1/1" placeholder="请选择">
<el-select v-model="formData.deliveryTemplateId" placeholder="请选择">
<el-option v-for="item in []" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-button class="ml-20px">运费模板</el-button>
</el-form-item>
</el-col>
<el-col :span="12">
<el-button class="ml-20px">运费模板</el-button>
<el-form-item label="品牌" prop="brandId">
<el-select v-model="formData.brandId" placeholder="请选择">
<el-option
v-for="item in brandList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="商品规格" props="specType">
@ -108,14 +118,15 @@
</template>
<script lang="ts" name="ProductSpuBasicInfoForm" setup>
import { PropType } from 'vue'
import { copyValueToTarget } from '@/utils'
import { propTypes } from '@/utils/propTypes'
import { defaultProps, handleTree } from '@/utils/tree'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import type { SpuType } 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'
import { propTypes } from '@/utils/propTypes'
import { copyValueToTarget } from '@/utils'
import { getSimpleBrandList } from '@/api/mall/product/brand'
const message = useMessage() //
@ -135,15 +146,20 @@ const propertyList = ref([]) // 商品属性列表
const addAttribute = (property: any) => {
Array.isArray(property) ? (propertyList.value = property) : propertyList.value.push(property)
}
/** 调用 SkuList generateTableData 方法*/
// const generateSkus(propertyList){
// skuList.value.generateTableData()
// }
const formData = reactive<SpuType>({
name: '', //
categoryId: undefined, //
categoryId: null, //
keyword: '', //
unit: '', //
picUrl: '', //
sliderPicUrls: [], //
introduction: '', //
deliveryTemplateId: 1, //
brandId: null, //
specType: false, //
subCommissionType: false, //
skus: []
@ -157,6 +173,7 @@ const rules = reactive({
picUrl: [required],
sliderPicUrls: [required],
// deliveryTemplateId: [required],
brandId: [required],
specType: [required],
subCommissionType: [required]
})
@ -232,10 +249,13 @@ const onChangeSpec = () => {
]
}
const categoryList = ref() //
const categoryList = ref([]) //
const brandList = ref([]) //
onMounted(async () => {
//
const data = await ProductCategoryApi.getCategoryList({})
categoryList.value = handleTree(data, 'id', 'parentId')
//
brandList.value = await getSimpleBrandList()
})
</script>

View File

@ -34,17 +34,29 @@
<!-- TODO @puhui999用户输入的时候是按照元分主要是我们自己用fix -->
<el-table-column align="center" label="销售价(元)" min-width="168">
<template #default="{ row }">
<el-input-number v-model="row.price" :min="0" class="w-100%" />
<el-input-number v-model="row.price" :min="0" :precision="2" :step="0.1" class="w-100%" />
</template>
</el-table-column>
<el-table-column align="center" label="市场价(元)" min-width="168">
<template #default="{ row }">
<el-input-number v-model="row.marketPrice" :min="0" class="w-100%" />
<el-input-number
v-model="row.marketPrice"
:min="0"
:precision="2"
:step="0.1"
class="w-100%"
/>
</template>
</el-table-column>
<el-table-column align="center" label="成本价(元)" min-width="168">
<template #default="{ row }">
<el-input-number v-model="row.costPrice" :min="0" class="w-100%" />
<el-input-number
v-model="row.costPrice"
:min="0"
:precision="2"
:step="0.1"
class="w-100%"
/>
</template>
</el-table-column>
<el-table-column align="center" label="库存" min-width="168">
@ -54,42 +66,54 @@
</el-table-column>
<el-table-column align="center" label="重量(kg)" min-width="168">
<template #default="{ row }">
<el-input-number v-model="row.weight" :min="0" class="w-100%" />
<el-input-number v-model="row.weight" :min="0" :precision="2" :step="0.1" class="w-100%" />
</template>
</el-table-column>
<el-table-column align="center" label="体积(m^3)" min-width="168">
<template #default="{ row }">
<el-input-number v-model="row.volume" :min="0" class="w-100%" />
<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">
<el-table-column align="center" label="一级返佣(元)" min-width="168">
<template #default="{ row }">
<el-input-number v-model="row.subCommissionFirstPrice" :min="0" class="w-100%" />
<el-input-number
v-model="row.subCommissionFirstPrice"
:min="0"
:precision="2"
:step="0.1"
class="w-100%"
/>
</template>
</el-table-column>
<el-table-column align="center" label="二级返佣(元)" min-width="168">
<template #default="{ row }">
<el-input-number v-model="row.subCommissionSecondPrice" :min="0" class="w-100%" />
<el-input-number
v-model="row.subCommissionSecondPrice"
:min="0"
:precision="2"
:step="0.1"
class="w-100%"
/>
</template>
</el-table-column>
</template>
<el-table-column v-if="formData.specType" align="center" fixed="right" label="操作" width="80">
<template #default>
<template #default="{ row }">
<el-button v-if="isBatch" link size="small" type="primary" @click="batchAdd">
批量添加
</el-button>
<el-button v-else link size="small" type="primary">删除</el-button>
<el-button v-else link size="small" type="primary" @click="deleteSku(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script lang="ts" name="SkuList" setup>
import { UploadImg } from '@/components/UploadFile'
import { PropType } from 'vue'
import type { Property, SkuType, SpuType } from '@/api/mall/product/spu'
import { propTypes } from '@/utils/propTypes'
import { copyValueToTarget } from '@/utils'
import { propTypes } from '@/utils/propTypes'
import { UploadImg } from '@/components/UploadFile'
import type { Property, SkuType, SpuType } from '@/api/mall/product/spu'
const props = defineProps({
propFormData: {
@ -124,7 +148,14 @@ const batchAdd = () => {
copyValueToTarget(item, skuList.value[0])
})
}
/** 删除 sku */
const deleteSku = (row) => {
const index = formData.value.skus.findIndex(
//
(sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties)
)
formData.value.skus.splice(index, 1)
}
const tableHeaders = ref<{ prop: string; label: string }[]>([]) //
/**
@ -142,11 +173,11 @@ watch(
}
)
// TODO @ chatgpt fix
// TODO @ chatgpt fix:
/** 生成表数据 */
const generateTableData = (data: any[]) => {
const generateTableData = (propertyList: any[]) => {
// fix: 使mapfor
const propertiesItemList = data.map((item) =>
const propertiesItemList = propertyList.map((item) =>
item.values.map((v) => ({
propertyId: item.id,
propertyName: item.name,
@ -155,19 +186,14 @@ const generateTableData = (data: any[]) => {
}))
)
const buildList = build(propertiesItemList)
// sku,
// fix:
if (
buildList.length === formData.value.skus.length ||
data.some((item) => item.values.length === 0)
) {
return
// sku skus
if (!validateData(propertyList)) {
// sku
formData.value!.skus = []
}
//
formData.value!.skus = []
buildList.forEach((item) => {
for (const item of buildList) {
const row = {
properties: Array.isArray(item) ? item : [item],
properties: Array.isArray(item) ? item : [item], // property
price: 0,
marketPrice: 0,
costPrice: 0,
@ -179,8 +205,36 @@ const generateTableData = (data: any[]) => {
subCommissionFirstPrice: 0,
subCommissionSecondPrice: 0
}
const index = formData.value!.skus.findIndex(
(sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties)
)
// sku
if (index !== -1) {
continue
}
/**
* TODO 有一个问题回显数据时已删除的 sku 会被重新添加暂时没想到好办法保存时先手动重新删除一下因为是一条空数据很好辨别 不手动删也没是提交表单时会检测删除空sku来兜底
*
*/
formData.value.skus.push(row)
})
}
}
/**
* 生成 skus 前置校验
*/
const validateData = (propertyList: any[]) => {
const skuPropertyIds = []
formData.value.skus.forEach((sku) =>
sku.properties
?.map((property) => property.propertyId)
.forEach((propertyId) => {
if (skuPropertyIds.indexOf(propertyId) === -1) {
skuPropertyIds.push(propertyId)
}
})
)
const propertyIds = propertyList.map((item) => item.id)
return skuPropertyIds.length === propertyIds.length
}
/** 构建所有排列组合 */
const build = (propertyValuesList: Property[][]) => {
@ -237,6 +291,10 @@ watch(
// nameindex
tableHeaders.value.push({ prop: `name${index}`, label: item.name })
})
// sku
if (validateData(propertyList)) return
//
if (propertyList.some((item) => item.values.length === 0)) return
generateTableData(propertyList)
},
{

View File

@ -75,10 +75,10 @@
<template #default="{ row }">
<el-form class="demo-table-expand" inline label-position="left">
<el-form-item label="市场价:">
<span>{{ row.marketPrice }}</span>
<span>{{ formatToFraction(row.marketPrice) }}</span>
</el-form-item>
<el-form-item label="成本价:">
<span>{{ row.costPrice }}</span>
<span>{{ formatToFraction(row.costPrice) }}</span>
</el-form-item>
<el-form-item label="虚拟销量:">
<span>{{ row.virtualSalesCount }}</span>
@ -99,7 +99,11 @@
</el-table-column>
<el-table-column :show-overflow-tooltip="true" label="商品名称" min-width="300" prop="name" />
<!-- TODO 价格 / 100.0 -->
<el-table-column align="center" label="商品售价" min-width="90" prop="price" />
<el-table-column align="center" label="商品售价" min-width="90" prop="price">
<template #default="{ row }">
{{ formatToFraction(row.price) }}
</template>
</el-table-column>
<el-table-column align="center" label="销量" min-width="90" prop="salesCount" />
<el-table-column align="center" label="库存" min-width="90" prop="stock" />
<el-table-column align="center" label="排序" min-width="70" prop="sort" />
@ -129,9 +133,12 @@
</template>
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" min-width="150">
<el-table-column align="center" fixed="right" label="操作" min-width="200">
<template #default="{ row }">
<!-- TODO @puhui999详情可以后面点做哈 -->
<el-button v-hasPermi="['product:spu:update']" link type="primary" @click="openDetail">
详情
</el-button>
<template v-if="queryParams.tabType === 4">
<el-button
v-hasPermi="['product:spu:delete']"
@ -151,7 +158,9 @@
</el-button>
</template>
<template v-else>
<!-- 只有不是上架和回收站的商品可以编辑 -->
<el-button
v-if="queryParams.tabType !== 0"
v-hasPermi="['product:spu:update']"
link
type="primary"
@ -189,6 +198,7 @@ import { defaultProps, handleTree } from '@/utils/tree'
import { ProductSpuStatusEnum } from '@/utils/constants'
import * as ProductSpuApi from '@/api/mall/product/spu'
import * as ProductCategoryApi from '@/api/mall/product/category'
import { formatToFraction } from '@/utils'
const message = useMessage() //
const { t } = useI18n() //
@ -357,7 +367,12 @@ const openForm = (id?: number) => {
//
push('/product/productSpuAdd')
}
/**
* 查看商品详情
*/
const openDetail = () => {
message.alert('查看详情未完善!!!')
}
// TODO @puhui999fix:
watch(
() => currentRoute.value,