feat: 商品详情联调

This commit is contained in:
zc 2025-10-27 23:01:46 +08:00
parent dc006fddf6
commit 6b3895b6a1
4 changed files with 284 additions and 229 deletions

View File

@ -0,0 +1,95 @@
import { Atrans$DialogRes } from '@/utils/page/config'
interface PreviewItem {
fileName: string
name: string
resourceUrl: string
type: 'image' | 'video'
fileType: 'mainImageUrl' | 'videoUrl' | 'subImageUrl'
}
// 编辑的时候从接口响应数据提取$dialog数据
export const handleGetDialogData = (
data: any,
$dialog: Ref<Atrans$DialogRes>,
fileList: Ref<PreviewItem[]>,
defaultCheckedNodes: Ref<{ admin: number[]; app: number[] }>
) => {
const result = { ...$dialog.value.data }
result.appCategoryId = data.appCategoryId2
defaultCheckedNodes.value.admin = extractAdminCategoryIds(data)
result.adminCategoryId = defaultCheckedNodes.value.admin.slice(-1)[0]
defaultCheckedNodes.value.app = [data.appCategoryId1, data.appCategoryId2]
result.isTest = data.isTest
result.title = data.title
const files = [
{
fileName: '主图',
name: '主图',
fileType: 'mainImageUrl',
resourceUrl: data.mainImageUrl,
type: 'image'
},
{
fileName: '视频',
name: '视频',
fileType: 'videoUrl',
resourceUrl: data.videoUrl,
type: 'video'
}
]
files.push(
...data.vvProductDetailList
.filter((item: any) => item.type === 1)
.map((item: any) => ({
fileName: '副图',
name: '副图',
resourceUrl: item.detail,
fileType: 'subImageUrl',
type: 'image'
}))
)
fileList.value = files as PreviewItem[]
$dialog.value.data = result
}
// 将adminCategoryId1, adminCategoryId2, ... 提取出来并排序
function extractAdminCategoryIds(obj: Record<string, any>): number[] {
return Object.entries(obj)
.filter(([key]) => key.startsWith('adminCategoryId'))
.sort(([keyA], [keyB]) => {
// 按数字部分排序 (adminCategoryId1 < adminCategoryId2)
const numA = parseInt(keyA.replace('adminCategoryId', ''))
const numB = parseInt(keyB.replace('adminCategoryId', ''))
return numA - numB
})
.map(([, value]) => value)
.filter((val) => ![undefined, null].includes(val)) as number[]
}
// 编辑的时候对后台类目的数据进行重组
export const handleGetNewAdminCategoryData = (data: any) => {
return data.map((item: any) => ({
...item,
categoryPropertyName: item.productPropertyName,
vvPropertyValueList: item.vvProductPropertyValueList.map((child: any) => ({
...child,
categoryPropertyName: item.productPropertyName,
categoryPropertyValue: child.productPropertyValue
})),
vvProductPropertyValueList: undefined
}))
}
// 编辑的时候对sku进行重组
export const handleGetNewSkuList = (data: any) => {
return data.map((item: any) => ({
...item,
name: item.vvSkuPropertyValueList.reduce((str: string, cur: any) => {
cur.categoryPropertyName = cur.productPropertyName
cur.categoryPropertyValue = cur.productPropertyValue
str += cur.productPropertyValue
return str
}, '')
}))
}

View File

@ -164,14 +164,15 @@ import {
handleCheckChange,
defaultAdminCategoryTreeProps,
defaultCheckedNodes,
handleGetDialogData,
fileList,
TSkuList,
handleGetNewAdminCategoryData,
handleGetNewSkuList,
handleValidFormData,
handleGetSubmitParams
TSkuList
} from './use-method'
import {
handleGetDialogData,
handleGetNewAdminCategoryData,
handleGetNewSkuList
} from './edit-method'
import { handleValidFormData, handleGetSubmitParams } from './submit-method'
import categoryConfig from './category-config.vue'
import wanEditor from './editor.vue'
import fileUploadBtn from '@/components/FileUploadBtn/index.vue'
@ -193,11 +194,12 @@ const init = async () => {
skuList.value = []
if (route.query.id) {
const res = await api.commodity.getCommodityDetail.post!<any>({ productId: route.query.id })
handleGetDialogData(res.data, $dialog, fileList)
handleGetDialogData(res.data, $dialog, fileList, defaultCheckedNodes)
adminCategoryData.value = handleGetNewAdminCategoryData(res.data.vvProductPropertyList)
initPropertyData(adminCategoryData.value)
skuList.value = handleGetNewSkuList(res.data.vvSkuList)
} else {
fileList.value = []
initPropertyData(adminCategoryData.value)
}
}
@ -240,6 +242,7 @@ const htmlContent = ref('自定义')
const onDeletePropertyValue = (index: number, id: number) =>
onClosePropertyValue(index, id, skuList)
const router = useRouter()
const onSubmit = () => {
const msg = handleValidFormData(
$dialog.value.data,
@ -256,10 +259,13 @@ const onSubmit = () => {
fileList.value,
adminCategoryData.value,
htmlContent.value,
skuList.value
skuList.value,
defaultCheckedNodes
)
console.warn('----- my data is params111: ', params)
if (route.query.type === 'edit') Object.assign(params, { id: route.query.id })
api.commodity.addOrUpdateCommodity.post!(params).then((res) => {
router.replace({ path: '/goods/commodity/index' })
console.warn('----- my data is res: ', res)
})
}

View File

@ -0,0 +1,165 @@
interface PreviewItem {
fileName: string
name: string
resourceUrl: string
type: 'image' | 'video'
fileType: 'mainImageUrl' | 'videoUrl' | 'subImageUrl'
}
interface Option {
id: number
categoryPropertyName: string
categoryPropertyValue: string
isAdd?: boolean
}
interface Category {
typeName: string
typeId: number
vvPropertyValueList: Option[]
}
// 定义目标数据的结构类型
export interface TSkuList {
serialNo: string
name: string
salePrice: string
imageUrl: string
stock: number
originPrice: number
promotionPrice: string
vvSkuPropertyValueList: any[]
}
type CategoryDataMock = Category[]
export const handleValidFormData = (
$dialogData: any,
fileList: PreviewItem[],
adminCategoryData: CategoryDataMock,
htmlContent: string,
skuList: TSkuList[]
) => {
console.warn(
'----- my data is fileList, adminCategoryData, skuList: ',
fileList,
adminCategoryData,
skuList
)
if (!$dialogData.title) {
return '请输入商品标题'
}
if (!$dialogData.appCategoryId) {
return '请选择app类目'
}
if (!$dialogData.adminCategoryId) {
return '请选择后台类目'
}
if (fileList.length === 0) {
return '请上传主图'
}
if (fileList.some((item) => item.fileType === 'mainImageUrl' && !item.resourceUrl)) {
return '请上传主图'
}
if (fileList.some((item) => item.fileType === 'videoUrl' && !item.resourceUrl)) {
return '请上传视频'
}
if (fileList.some((item) => item.fileType === 'subImageUrl' && !item.resourceUrl)) {
return '请上传副图'
}
if (adminCategoryData.length === 0) {
return '请添加商品属性'
}
let msg = ''
skuList.some((item) => {
const nameMap = {
stock: '库存',
originPrice: '成本价',
salePrice: '原价',
promotionPrice: '售价'
}
if (!item.stock) {
msg = `${item.name}${nameMap.stock}不能为空`
}
if (item.originPrice) {
msg = `${item.name}${nameMap.originPrice}不能为空`
}
if (!item.salePrice) {
msg = `${item.name}${nameMap.salePrice}不能为空`
}
if (!item.promotionPrice) {
msg = `${item.name}${nameMap.promotionPrice}不能为空`
}
return msg
})
if (msg) return msg
if (!htmlContent) {
return '请填写商品详情'
}
return ''
}
export const handleGetSubmitParams = (
isAdd: boolean,
$dialogData: any,
fileList: PreviewItem[],
adminCategoryData: CategoryDataMock,
htmlContent: string,
skuList: TSkuList[],
defaultCheckedNodes: Ref<{ admin: number[]; app: number[] }>
) => {
let vvProductPropertyList = []
if (isAdd) {
vvProductPropertyList = adminCategoryData.map((item) => ({
// ...item,
productPropertyName: item.categoryPropertyName,
vvProductPropertyValueList: item.vvPropertyValueList.map((item) => ({
// ...item,
productPropertyValue: item.categoryPropertyValue
}))
}))
} else {
vvProductPropertyList = adminCategoryData.map((item) => ({
...item,
productPropertyName: item.categoryPropertyName,
vvProductPropertyValueList: item.vvPropertyValueList.map((item) => {
if (item.isAdd) delete item.id
return {
...item,
productPropertyValue: item.categoryPropertyValue
}
})
}))
}
return {
...$dialogData,
mainImageUrl: fileList.find((item) => item.fileType === 'mainImageUrl')?.resourceUrl,
videoUrl: fileList.find((item) => item.fileType === 'videoUrl')?.resourceUrl,
appCategoryId1: $dialogData.appCategoryIds?.[0] || defaultCheckedNodes.value.app[0],
appCategoryId2: $dialogData.appCategoryIds?.[1] || defaultCheckedNodes.value.app[1],
...($dialogData.adminCategoryIds || defaultCheckedNodes.value.admin).reduce((acc, cur, i) => {
acc[`adminCategoryId${i + 1}`] = cur
return acc
}, {}),
vvProductDetailList: fileList
.filter((item) => item.fileType === 'subImageUrl')
.map((item) => ({
detail: item.resourceUrl,
type: 1
})),
vvProductPropertyList,
vvSkuList: skuList.map((item) => {
const result = {}
const canDeleteId = !item.id
return {
...item,
vvSkuPropertyValueList: item.vvSkuPropertyValueList.map((child) => ({
...child,
id: canDeleteId ? undefined : child.id,
productPropertyValue: child.categoryPropertyValue, // testzc 编辑提交有问题,没有效果
productPropertyName: child.categoryPropertyName // testzc 编辑提交有问题,没有效果
}))
}
})
}
}

View File

@ -1,8 +1,8 @@
import { Atrans$DialogRes } from '@/utils/page/config'
import { useDraggable } from 'vue-draggable-plus'
// 定义源数据的结构类型
interface Option {
id: number
categoryPropertyName: string
categoryPropertyValue: string
isAdd?: boolean
}
@ -84,13 +84,13 @@ export const usePropertyValue = () => {
debugger
const i = adminCategoryData[index].vvPropertyValueList.findIndex((item: any) => item.id === id)
const temp = adminCategoryData[index].vvPropertyValueList[i]
const filterName = temp.productPropertyName + ':' + temp.productPropertyValue
const filterName = temp.categoryPropertyName + ':' + temp.categoryPropertyValue
adminCategoryData[index].vvPropertyValueList.splice(i, 1)
const deleteIndexs: number[] = []
skuList.value.forEach((item: TSkuList, skuIndex: number) => {
if (
item.vvSkuPropertyValueList.find(
(it) => it.productPropertyName + ':' + it.productPropertyValue === filterName
(it) => it.categoryPropertyName + ':' + it.categoryPropertyValue === filterName
)
) {
deleteIndexs.push(skuIndex)
@ -222,7 +222,13 @@ export const handleLoadNode = (node: any, resolve: any) => {
value: item.id,
hasChild: item.hasChild,
disabled: !!item.hasChild,
categoryPropertyList: item.vvCategoryPropertyDTOList
categoryPropertyList: (item.vvCategoryPropertyDTOList || []).map((child: any) => ({
...child,
vvPropertyValueList: child.vvPropertyValueList.map((it: any) => ({
...it,
categoryPropertyName: child.categoryPropertyName
}))
}))
}))
)
})
@ -257,220 +263,3 @@ export const handleCheckChange = (
data[type + 'CategoryIds'] = [...checkedStatus.halfCheckedKeys, ...checkedStatus.checkedKeys]
return checkedData
}
interface PreviewItem {
fileName: string
name: string
resourceUrl: string
type: 'image' | 'video'
fileType: 'mainImageUrl' | 'videoUrl' | 'subImageUrl'
}
// 编辑的时候从接口响应数据提取$dialog数据
export const handleGetDialogData = (
data: any,
$dialog: Ref<Atrans$DialogRes>,
fileList: Ref<PreviewItem[]>
) => {
const result = { ...$dialog.value.data }
result.appCategoryId = data.appCategoryId2
result.adminCategoryId = data.adminCategoryId
defaultCheckedNodes.value.admin = extractAdminCategoryIds(data)
defaultCheckedNodes.value.app = [data.appCategoryId1, data.appCategoryId2]
result.isTest = data.isTest
result.title = data.title
const files = [
{
fileName: '主图',
name: '主图',
fileType: 'mainImageUrl',
resourceUrl: data.mainImageUrl,
type: 'image'
},
{
fileName: '视频',
name: '视频',
fileType: 'videoUrl',
resourceUrl: data.videoUrl,
type: 'video'
}
]
files.push(
...data.vvProductDetailList
.filter((item: any) => item.type === 1)
.map((item: any) => ({
fileName: '副图',
name: '副图',
resourceUrl: item.resourceUrl,
fileType: 'subImageUrl',
type: 'image'
}))
)
fileList.value = files as PreviewItem[]
$dialog.value.data = result
}
// 将adminCategoryId1, adminCategoryId2, ... 提取出来并排序
function extractAdminCategoryIds(obj: Record<string, any>): number[] {
return Object.entries(obj)
.filter(([key]) => key.startsWith('adminCategoryId'))
.sort(([keyA], [keyB]) => {
// 按数字部分排序 (adminCategoryId1 < adminCategoryId2)
const numA = parseInt(keyA.replace('adminCategoryId', ''))
const numB = parseInt(keyB.replace('adminCategoryId', ''))
return numA - numB
})
.map(([, value]) => value)
.filter((val) => ![undefined, null].includes(val)) as number[]
}
// 编辑的时候对后台类目的数据进行重组
export const handleGetNewAdminCategoryData = (data: any) => {
return data.map((item: any) => ({
...item,
categoryPropertyName: item.productPropertyName,
vvPropertyValueList: item.vvProductPropertyValueList.map((item: any) => ({
...item,
categoryPropertyValue: item.productPropertyValue
})),
vvProductPropertyValueList: undefined
}))
}
// 编辑的时候对sku进行重组
export const handleGetNewSkuList = (data: any) => {
return data.map((item: any) => ({
...item,
name: item.vvSkuPropertyValueList.reduce((str: string, cur: any) => {
str += cur.productPropertyValue
return str
}, '')
}))
}
export const handleValidFormData = (
$dialogData: any,
fileList: PreviewItem[],
adminCategoryData: CategoryDataMock,
htmlContent: string,
skuList: TSkuList[]
) => {
console.warn(
'----- my data is fileList, adminCategoryData, skuList: ',
fileList,
adminCategoryData,
skuList
)
if (!$dialogData.title) {
return '请输入商品标题'
}
if (!$dialogData.appCategoryId) {
return '请选择app类目'
}
if (!$dialogData.adminCategoryId) {
return '请选择后台类目'
}
if (fileList.length === 0) {
return '请上传主图'
}
if (fileList.some((item) => item.fileType === 'mainImageUrl' && !item.resourceUrl)) {
return '请上传主图'
}
if (fileList.some((item) => item.fileType === 'videoUrl' && !item.resourceUrl)) {
return '请上传视频'
}
if (fileList.some((item) => item.fileType === 'subImageUrl' && !item.resourceUrl)) {
return '请上传副图'
}
if (adminCategoryData.length === 0) {
return '请添加商品属性'
}
let msg = ''
skuList.some((item) => {
const nameMap = {
stock: '库存',
originPrice: '成本价',
salePrice: '原价',
promotionPrice: '售价'
}
if (!item.stock) {
msg = `${item.name}${nameMap.stock}不能为空`
}
if (item.originPrice) {
msg = `${item.name}${nameMap.originPrice}不能为空`
}
if (!item.salePrice) {
msg = `${item.name}${nameMap.salePrice}不能为空`
}
if (!item.promotionPrice) {
msg = `${item.name}${nameMap.promotionPrice}不能为空`
}
return msg
})
if (msg) return msg
if (!htmlContent) {
return '请填写商品详情'
}
return ''
}
export const handleGetSubmitParams = (
isAdd: boolean,
$dialogData: any,
fileList: PreviewItem[],
adminCategoryData: CategoryDataMock,
htmlContent: string,
skuList: TSkuList[]
) => {
let vvProductPropertyList = []
if (isAdd) {
vvProductPropertyList = adminCategoryData.map((item) => ({
// ...item,
productPropertyName: item.categoryPropertyName,
vvProductPropertyValueList: item.vvPropertyValueList.map((item) => ({
// ...item,
productPropertyValue: item.categoryPropertyValue
}))
}))
} else {
vvProductPropertyList = adminCategoryData.map((item) => ({
...item,
productPropertyName: item.categoryPropertyName,
vvProductPropertyValueList: item.vvPropertyValueList.map((item) => ({
...item,
productPropertyValue: item.categoryPropertyValue
}))
}))
}
return {
...$dialogData,
mainImageUrl: fileList.find((item) => item.fileType === 'mainImageUrl')?.resourceUrl,
videoUrl: fileList.find((item) => item.fileType === 'videoUrl')?.resourceUrl,
appCategoryId1: $dialogData.appCategoryIds?.[0] || defaultCheckedNodes.value.app[0],
appCategoryId2: $dialogData.appCategoryIds?.[1] || defaultCheckedNodes.value.app[1],
...($dialogData.adminCategoryIds || defaultCheckedNodes.value.admin).reduce((acc, cur, i) => {
acc[`adminCategoryId${i + 1}`] = cur
return acc
}, {}),
vvProductDetailList: fileList
.filter((item) => item.fileType === 'subImageUrl')
.map((item) => ({
detail: item.resourceUrl,
type: 1
})),
vvProductPropertyList,
vvSkuList: skuList.map((item) => {
const result = {}
const canDeleteId = !item.id
return {
...item,
vvSkuPropertyValueList: item.vvSkuPropertyValueList.map((child) => ({
...child,
id: canDeleteId ? undefined : child.id,
productPropertyValue: item.categoryPropertyValue, // testzc 编辑提交有问题,没有效果
productPropertyName: item.categoryPropertyName // testzc 编辑提交有问题,没有效果
}))
}
})
}
}