review + mall:商品管理

This commit is contained in:
YunaiV 2023-05-24 20:27:45 +08:00
parent c296c9e53d
commit 0fba3fd503
8 changed files with 63 additions and 59 deletions

View File

@ -102,7 +102,8 @@ const include = [
'element-plus/es/components/timeline-item/style/css', 'element-plus/es/components/timeline-item/style/css',
'element-plus/es/components/collapse/style/css', 'element-plus/es/components/collapse/style/css',
'element-plus/es/components/collapse-item/style/css', 'element-plus/es/components/collapse-item/style/css',
'element-plus/es/components/button-group/style/css' 'element-plus/es/components/button-group/style/css',
'element-plus/es/components/text/style/css'
] ]
const exclude = ['@iconify/json'] const exclude = ['@iconify/json']

View File

@ -7,6 +7,7 @@ export interface Property {
valueName?: string // 属性值名称 valueName?: string // 属性值名称
} }
// TODO puhui999是不是直接叫 Sku 更简洁一点哈。type 待后面,总感觉有个类型?
export interface SkuType { export interface SkuType {
id?: number // 商品 SKU 编号 id?: number // 商品 SKU 编号
spuId?: number // SPU 编号 spuId?: number // SPU 编号
@ -24,6 +25,7 @@ export interface SkuType {
salesCount?: number // 商品销量 salesCount?: number // 商品销量
} }
// TODO puhui999是不是直接叫 Spu 更简洁一点哈。type 待后面,总感觉有个类型?
export interface SpuType { export interface SpuType {
id?: number id?: number
name?: string // 商品名称 name?: string // 商品名称

View File

@ -174,6 +174,7 @@ export const copyValueToTarget = (target, source) => {
Object.assign(target, newObj) Object.assign(target, newObj)
} }
// TODO @puhui999返回要带上 .00 哈.例如说 1.00
/** /**
* *
* @param num * @param num

View File

@ -146,7 +146,6 @@ const submitForm = async () => {
const newSliderPicUrls = [] const newSliderPicUrls = []
deepCopyFormData.sliderPicUrls.forEach((item) => { deepCopyFormData.sliderPicUrls.forEach((item) => {
// //
// TODO @puhui999 object fix
typeof item === 'object' ? newSliderPicUrls.push(item.url) : newSliderPicUrls.push(item) typeof item === 'object' ? newSliderPicUrls.push(item.url) : newSliderPicUrls.push(item)
}) })
deepCopyFormData.sliderPicUrls = newSliderPicUrls deepCopyFormData.sliderPicUrls = newSliderPicUrls

View File

@ -96,7 +96,6 @@
<!-- 多规格添加--> <!-- 多规格添加-->
<el-col :span="24"> <el-col :span="24">
<el-form-item v-if="formData.specType" label="商品属性"> <el-form-item v-if="formData.specType" label="商品属性">
<!-- TODO @puhui999参考 https://admin.java.crmeb.net/store/list/creatProduct fix-->
<el-button class="mr-15px mb-10px" @click="attributesAddFormRef.open">添加规格</el-button> <el-button class="mr-15px mb-10px" @click="attributesAddFormRef.open">添加规格</el-button>
<ProductAttributes :propertyList="propertyList" @success="generateSkus" /> <ProductAttributes :propertyList="propertyList" @success="generateSkus" />
</el-form-item> </el-form-item>
@ -137,9 +136,8 @@ const props = defineProps({
}, },
activeName: propTypes.string.def('') activeName: propTypes.string.def('')
}) })
const attributesAddFormRef = ref() // TODO @puhui999 fix const attributesAddFormRef = ref() //
const productSpuBasicInfoRef = ref() // Ref TODO @puhui999 fix const productSpuBasicInfoRef = ref() // Ref
// TODO @puhui999attributeList propertyList fix
const propertyList = ref([]) // const propertyList = ref([]) //
const skuListRef = ref() // Ref const skuListRef = ref() // Ref
/** 调用 SkuList generateTableData 方法*/ /** 调用 SkuList generateTableData 方法*/
@ -180,17 +178,17 @@ const rules = reactive({
watch( watch(
() => props.propFormData, () => props.propFormData,
(data) => { (data) => {
if (!data) return if (!data) {
// fix使 copyValueToTarget 使 formData.value = data return
}
copyValueToTarget(formData, data) copyValueToTarget(formData, data)
// fix: url
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) {
// TODO @puhui999 propertyName id + uniapp // skus propertyList
// fix: 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 }) => {
@ -209,7 +207,6 @@ watch(
} }
}, },
{ {
// fix: ,
immediate: true immediate: true
} }
) )

View File

@ -1,7 +1,6 @@
<template> <template>
<el-form ref="otherSettingsFormRef" :model="formData" :rules="rules" label-width="120px"> <el-form ref="otherSettingsFormRef" :model="formData" :rules="rules" label-width="120px">
<el-row> <el-row>
<!-- TODO @puhui999横着三个哈 fix-->
<el-col :span="24"> <el-col :span="24">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="8"> <el-col :span="8">
@ -35,7 +34,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<!-- TODO tag展示暂时不考虑排序 --> <!-- TODO tag展示暂时不考虑排序 -->
<el-form-item label="活动优先级"> <el-form-item label="活动优先级">
<el-tag>默认</el-tag> <el-tag>默认</el-tag>
<el-tag class="ml-2" type="success">秒杀</el-tag> <el-tag class="ml-2" type="success">秒杀</el-tag>
@ -86,7 +85,6 @@ const rules = reactive({
giveIntegral: [required], giveIntegral: [required],
virtualSalesCount: [required] virtualSalesCount: [required]
}) })
// TODO @puhui999 recommendOptions fix
const recommendOptions = [ const recommendOptions = [
{ name: '是否热卖', value: 'recommendHot' }, { name: '是否热卖', value: 'recommendHot' },
{ name: '是否优惠', value: 'recommendBenefit' }, { name: '是否优惠', value: 'recommendBenefit' },
@ -98,7 +96,6 @@ const checkboxGroup = ref<string[]>([]) // 选中的推荐选项
/** 选择商品后赋值 */ /** 选择商品后赋值 */
const onChangeGroup = () => { const onChangeGroup = () => {
// TODO @puhui999 recommendfix
recommendOptions.forEach(({ value }) => { recommendOptions.forEach(({ value }) => {
formData.value[value] = checkboxGroup.value.includes(value) formData.value[value] = checkboxGroup.value.includes(value)
}) })
@ -110,21 +107,21 @@ const onChangeGroup = () => {
watch( watch(
() => props.propFormData, () => props.propFormData,
(data) => { (data) => {
if (!data) return if (!data) {
// fix使 copyValueToTarget 使 formData.value = data return
}
copyValueToTarget(formData.value, data) copyValueToTarget(formData.value, data)
recommendOptions.forEach(({ value }) => { recommendOptions.forEach(({ value }) => {
// TODO fix:
if (formData.value[value] && !checkboxGroup.value.includes(value)) { if (formData.value[value] && !checkboxGroup.value.includes(value)) {
checkboxGroup.value.push(value) checkboxGroup.value.push(value)
} }
}) })
}, },
{ {
// fix: ,
immediate: true immediate: true
} }
) )
/** /**
* 表单校验 * 表单校验
*/ */

View File

@ -12,7 +12,7 @@
</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"
:key="index" :key="index"
@ -21,17 +21,16 @@
min-width="120" min-width="120"
> >
<template #default="{ row }"> <template #default="{ row }">
<!-- TODO puhui999展示成蓝色有点区分度哈 -->
{{ row.properties[index]?.valueName }} {{ row.properties[index]?.valueName }}
</template> </template>
</el-table-column> </el-table-column>
</template> </template>
<!-- TODO @puhui999 controls-position=" " 可以去掉哈不然太长了手动输入更方便 fix -->
<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 v-model="row.barCode" class="w-100%" /> <el-input v-model="row.barCode" class="w-100%" />
</template> </template>
</el-table-column> </el-table-column>
<!-- TODO @puhui999用户输入的时候是按照元分主要是我们自己用fix -->
<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 v-model="row.price" :min="0" :precision="2" :step="0.1" class="w-100%" /> <el-input-number v-model="row.price" :min="0" :precision="2" :step="0.1" class="w-100%" />
@ -141,6 +140,7 @@ const skuList = ref<SkuType[]>([
subCommissionSecondPrice: 0 // subCommissionSecondPrice: 0 //
} }
]) // ]) //
// TODO @puhui999 0.01
/** 批量添加 */ /** 批量添加 */
const batchAdd = () => { const batchAdd = () => {
@ -148,6 +148,7 @@ const batchAdd = () => {
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(
@ -159,7 +160,7 @@ const deleteSku = (row) => {
const tableHeaders = ref<{ prop: string; label: string }[]>([]) // const tableHeaders = ref<{ prop: string; label: string }[]>([]) //
/** /**
* 将传进来的值赋值给skuList * 将传进来的值赋值给 skuList
*/ */
watch( watch(
() => props.propFormData, () => props.propFormData,
@ -173,11 +174,10 @@ watch(
} }
) )
// TODO @ chatgpt fix:
/** 生成表数据 */ /** 生成表数据 */
const generateTableData = (propertyList: any[]) => { const generateTableData = (propertyList: any[]) => {
// fix: 使mapfor //
const propertiesItemList = propertyList.map((item) => const propertyValues = propertyList.map((item) =>
item.values.map((v) => ({ item.values.map((v) => ({
propertyId: item.id, propertyId: item.id,
propertyName: item.name, propertyName: item.name,
@ -185,15 +185,16 @@ const generateTableData = (propertyList: any[]) => {
valueName: v.name valueName: v.name
})) }))
) )
const buildList = build(propertiesItemList) // TODO @puhui buildSkuListitem sku
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 buildList) {
const row = { const row = {
properties: Array.isArray(item) ? item : [item], // property properties: Array.isArray(item) ? item : [item], // property
price: 0, price: 0,
marketPrice: 0, marketPrice: 0,
costPrice: 0, costPrice: 0,
@ -205,16 +206,17 @@ const generateTableData = (propertyList: any[]) => {
subCommissionFirstPrice: 0, subCommissionFirstPrice: 0,
subCommissionSecondPrice: 0 subCommissionSecondPrice: 0
} }
// sku
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)
) )
// sku
if (index !== -1) { if (index !== -1) {
continue continue
} }
formData.value.skus.push(row) formData.value.skus.push(row)
} }
} }
/** /**
* 生成 skus 前置校验 * 生成 skus 前置校验
*/ */
@ -232,6 +234,7 @@ const validateData = (propertyList: any[]) => {
const propertyIds = propertyList.map((item) => item.id) const propertyIds = propertyList.map((item) => item.id)
return skuPropertyIds.length === propertyIds.length return skuPropertyIds.length === propertyIds.length
} }
/** 构建所有排列组合 */ /** 构建所有排列组合 */
const build = (propertyValuesList: Property[][]) => { const build = (propertyValuesList: Property[][]) => {
if (propertyValuesList.length === 0) { if (propertyValuesList.length === 0) {
@ -255,13 +258,15 @@ const build = (propertyValuesList: Property[][]) => {
} }
} }
/** 监听属性列表生成相关参数和表头 */ /** 监听属性列表生成相关参数和表头 */
watch( watch(
() => props.propertyList, () => props.propertyList,
(propertyList) => { (propertyList) => {
// //
if (!formData.value.specType) return if (!formData.value.specType) {
// 使 return
}
// 使
if (props.isBatch) { if (props.isBatch) {
skuList.value = [ skuList.value = [
{ {
@ -278,8 +283,11 @@ watch(
} }
] ]
} }
// //
if (JSON.stringify(propertyList) === '[]') return if (JSON.stringify(propertyList) === '[]') {
return
}
// //
tableHeaders.value = [] tableHeaders.value = []
// //
@ -287,10 +295,16 @@ watch(
// nameindex // nameindex
tableHeaders.value.push({ prop: `name${index}`, label: item.name }) tableHeaders.value.push({ prop: `name${index}`, label: item.name })
}) })
// sku // sku
if (validateData(propertyList)) return if (validateData(propertyList)) {
return
}
// //
if (propertyList.some((item) => item.values.length === 0)) return if (propertyList.some((item) => item.values.length === 0)) {
return
}
// table sku
generateTableData(propertyList) generateTableData(propertyList)
}, },
{ {
@ -298,6 +312,6 @@ watch(
immediate: true immediate: true
} }
) )
// sku fix: spu skus sku // sku
defineExpose({ generateTableData }) defineExpose({ generateTableData })
</script> </script>

View File

@ -8,7 +8,7 @@
class="-mb-15px" class="-mb-15px"
label-width="68px" label-width="68px"
> >
<!-- TODO @puhui999https://admin.java.crmeb.net/store/index使 + fix--> <!-- 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"
@ -19,6 +19,7 @@
/> />
</el-form-item> </el-form-item>
<!-- TODO 分类只能选择二级分类目前还没做还是先以联调通顺为主 --> <!-- TODO 分类只能选择二级分类目前还没做还是先以联调通顺为主 -->
<!-- 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"
@ -70,7 +71,12 @@
/> />
</el-tabs> </el-tabs>
<el-table v-loading="loading" :data="list"> <el-table v-loading="loading" :data="list">
<!-- TODO 折叠数据按需增加暂定以下三个 --> <!-- TODO puhui这几个属性哈一行三个
商品分类服装鞋包/箱包
商品市场价格100.00
成本价0.00
收藏5
虚拟销量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" inline label-position="left">
@ -86,19 +92,13 @@
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<!-- TODO puhui999: ID 编号的展示 fix-->
<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 <el-image :src="row.picUrl" @click="imagePreview(row.picUrl)" class="w-30px h-30px" />
:src="row.picUrl"
style="width: 30px; height: 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" />
<!-- 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 }"> <template #default="{ row }">
{{ formatToFraction(row.price) }} {{ formatToFraction(row.price) }}
@ -116,7 +116,6 @@
/> />
<el-table-column align="center" label="状态" min-width="80"> <el-table-column align="center" label="状态" min-width="80">
<template #default="{ row }"> <template #default="{ row }">
<!-- fix: 修复打开回收站时商品误触改变商品状态的事件因为el-switch只用0和1没有-1所以当商品状态为-1时状态使用el-tag显示 -->
<template v-if="row.status >= 0"> <template v-if="row.status >= 0">
<el-switch <el-switch
v-model="row.status" v-model="row.status"
@ -238,7 +237,6 @@ const tabsData = ref([
/** 获得每个 Tab 的数量 */ /** 获得每个 Tab 的数量 */
const getTabsCount = async () => { const getTabsCount = async () => {
// TODO @puhui999 try catch fix
const res = await ProductSpuApi.getTabsCount() const res = await ProductSpuApi.getTabsCount()
for (let objName in res) { for (let objName in res) {
tabsData.value[Number(objName)].count = res[objName] tabsData.value[Number(objName)].count = res[objName]
@ -251,7 +249,6 @@ const queryParams = ref({
}) // }) //
const queryFormRef = ref() // Ref const queryFormRef = ref() // Ref
// TODO @puhui999 handleTabClickfix
const handleTabClick = (tab: TabsPaneContext) => { const handleTabClick = (tab: TabsPaneContext) => {
queryParams.value.tabType = tab.paneName queryParams.value.tabType = tab.paneName
getList() getList()
@ -269,7 +266,6 @@ const getList = async () => {
} }
} }
// TODO @puhui999 changeStatus addToTrash fix
/** /**
* 更改 SPU 状态 * 更改 SPU 状态
* *
@ -277,7 +273,6 @@ const getList = async () => {
* @param status 更改前的值 * @param status 更改前的值
*/ */
const changeStatus = async (row, status?: number) => { const changeStatus = async (row, status?: number) => {
// fix: changeStatus
const deepCopyValue = cloneDeep(unref(row)) const deepCopyValue = cloneDeep(unref(row))
if (typeof status !== 'undefined') deepCopyValue.status = status if (typeof status !== 'undefined') deepCopyValue.status = status
try { try {
@ -293,7 +288,6 @@ const changeStatus = async (row, status?: number) => {
text = `加入${ProductSpuStatusEnum.RECYCLE.name}` text = `加入${ProductSpuStatusEnum.RECYCLE.name}`
break break
} }
// fix:
await message.confirm( await message.confirm(
deepCopyValue.status === -1 deepCopyValue.status === -1
? `确认要将[${row.name}]${text}吗?` ? `确认要将[${row.name}]${text}吗?`
@ -331,12 +325,8 @@ const handleDelete = async (id: number) => {
} catch {} } catch {}
} }
/** /** 商品图预览 */
* 商品图预览
* @param imgUrl
*/
const imagePreview = (imgUrl: string) => { const imagePreview = (imgUrl: string) => {
// fix: https://kailong110120130.gitee.io/vue-element-plus-admin-doc/components/image-viewer.html
createImageViewer({ createImageViewer({
urlList: [imgUrl] urlList: [imgUrl]
}) })
@ -367,19 +357,22 @@ const openForm = (id?: number) => {
// //
push('/product/productSpuAdd') push('/product/productSpuAdd')
} }
/** /**
* 查看商品详情 * 查看商品详情
*/ */
const openDetail = () => { const openDetail = () => {
message.alert('查看详情未完善!!!') message.alert('查看详情未完善!!!')
} }
// TODO @puhui999fix:
//
watch( watch(
() => currentRoute.value, () => currentRoute.value,
() => { () => {
getList() getList()
} }
) )
const categoryList = ref() // const categoryList = ref() //
/** 初始化 **/ /** 初始化 **/
onMounted(async () => { onMounted(async () => {