From c198ca35ea4a5b4a555f100881bdcbda650ac331 Mon Sep 17 00:00:00 2001 From: zc <2064281269@qq.com> Date: Sat, 25 Oct 2025 19:56:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=95=86=E5=93=81=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E7=BC=96=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail-dialog/choose-file-method.ts | 25 +-- .../goods/commodity/detail-dialog/config.ts | 2 +- .../goods/commodity/detail-dialog/index.vue | 52 ++++--- .../goods/commodity/detail-dialog/mock.ts | 146 ++++++++++++++++++ .../commodity/detail-dialog/use-method.ts | 101 +++++++++--- 5 files changed, 278 insertions(+), 48 deletions(-) diff --git a/src/views/goods/commodity/detail-dialog/choose-file-method.ts b/src/views/goods/commodity/detail-dialog/choose-file-method.ts index e44d7a5..503cc4d 100644 --- a/src/views/goods/commodity/detail-dialog/choose-file-method.ts +++ b/src/views/goods/commodity/detail-dialog/choose-file-method.ts @@ -1,5 +1,12 @@ +interface PreviewItem { + fileName: string + name: string + resourceUrl: string + type: 'image' | 'video' +} + // 选择资源库图片 -export const useImportFile = () => { +export const useImportFile = (fileList: Ref) => { const showFileExplorer = ref(false) const fileType = { mainImageUrl: '主图', @@ -10,8 +17,8 @@ export const useImportFile = () => { const currentPathArray = ref<{ name: string; id: number }[]>([{ name: '根目录', id: 0 }]) // 处理文件选择 - const handleChooseResourceFileCallback = (files: any[] = []) => { - handleChooseFiles(curFileType.value, files) + const handleChooseResourceFileCallback = (files: PreviewItem[] = []) => { + handleChooseFiles(curFileType.value, files, fileList.value) } const onClickChooseResourceBtn = (type: 'mainImageUrl' | 'videoUrl' | 'subImage') => { @@ -26,10 +33,10 @@ export const useImportFile = () => { } } -export const fileList = ref([]) export const handleChooseFiles = ( fileType: 'mainImageUrl' | 'videoUrl' | 'subImage', - files: any = [] + files: any = [], + fileList: PreviewItem[] ) => { const fileNameMap = { mainImageUrl: '主图', @@ -37,12 +44,10 @@ export const handleChooseFiles = ( subImage: '副图' } if (['mainImageUrl', 'videoUrl'].includes(fileType)) { - const index = fileList.value.findIndex((item: any) => item.fileType === fileType) + const index = fileList.findIndex((item: any) => item.fileType === fileType) if (index !== -1) { - fileList.value.splice(index, 1) + fileList.splice(index, 1) } } - fileList.value.push( - ...files.map((item: any) => ({ ...item, fileType, name: fileNameMap[fileType] })) - ) + fileList.push(...files.map((item: any) => ({ ...item, fileType, name: fileNameMap[fileType] }))) } diff --git a/src/views/goods/commodity/detail-dialog/config.ts b/src/views/goods/commodity/detail-dialog/config.ts index 4486c39..a1160da 100644 --- a/src/views/goods/commodity/detail-dialog/config.ts +++ b/src/views/goods/commodity/detail-dialog/config.ts @@ -5,7 +5,7 @@ export const initConfig = () => { dialog: [ { title: { label: '商品标题', class: '!w-full' }, - appCategoryId: { label: 'app类目', slot: 'appCategoryId' }, + appCategoryIds: { label: 'app类目', slot: 'appCategoryId' }, mainImageUrl: { label: '主图', slot: 'mainFile' }, videoUrl: { label: '视频', slot: 'videoFile' }, subImageUrl: { label: '副图', slot: 'subFile' }, diff --git a/src/views/goods/commodity/detail-dialog/index.vue b/src/views/goods/commodity/detail-dialog/index.vue index b809155..ca56d06 100644 --- a/src/views/goods/commodity/detail-dialog/index.vue +++ b/src/views/goods/commodity/detail-dialog/index.vue @@ -9,9 +9,10 @@ v-model="$dialog.data.appCategoryId" :props="defaultAdminCategoryTreeProps" :render-after-expand="false" + :default-expanded-keys="defaultCheckedNodes.app" @check=" (checkedNode: any, checkedStatus: any) => - handleCheckChange(checkedNode, checkedStatus, 'app') + handleCheckChange(checkedNode, checkedStatus, 'app', $dialog.data) " :load="handleLoadNode2" lazy @@ -22,7 +23,7 @@ 资源库导入 资源库导入 资源库导入 -
+
{{ item.categoryPropertyName }}:
@@ -146,44 +148,58 @@ import { handleLoadNode2, handleCheckChange, defaultAdminCategoryTreeProps, - checkedNodes + defaultCheckedNodes, + handleGetDialogData, + fileList, + TSkuList } from './use-method' import categoryConfig from './category-config.vue' import wanEditor from './editor.vue' import fileUploadBtn from '@/components/FileUploadBtn/index.vue' import resourceReview from '@/components/ResourceReview/index.vue' -import { useImportFile, fileList, handleChooseFiles } from './choose-file-method' +import { useImportFile, handleChooseFiles } from './choose-file-method' import FileExplorerDialog from '@/components/FileExplorerDialog/index.vue' +import { Atrans$DialogRes } from '@/utils/page/config' const route = useRoute() +const $dialog = ref({} as Atrans$DialogRes) const init = async () => { - checkedNodes.value = { admin: [], app: [] } - await api.commodity.getCommodityDetail.post!({ productId: route.query.id }) + defaultCheckedNodes.value = { admin: [], app: [] } + const data = initConfig().value! + $dialog.value = data.$dialog + $dialog.value.config = data.dialog1 + $dialog.value.data = { appCategoryId: '', adminCategoryId: '', isTest: 0, title: '' } + adminCategoryData.value = [] + if (route.query.id) { + const res = await api.commodity.getCommodityDetail.post!({ productId: route.query.id }) + handleGetDialogData(res.data, $dialog, fileList) + adminCategoryData.value = categoryDataMock + initPropertyData(adminCategoryData.value) + skuList.value = generateSkuList(adminCategoryData.value) + } } -const { $dialog, dialog1 } = toRefs(initConfig().value!) -$dialog.value.config = dialog1.value - const { showFileExplorer, currentPathArray, handleChooseResourceFileCallback, onClickChooseResourceBtn -} = useImportFile() +} = useImportFile(fileList) -const categoryData = ref(categoryDataMock) +const adminCategoryData = ref([]) const { inputValue, InputRefs, inputVisibles, + initPropertyData, onAddPropertyValue, onClosePropertyValue, handleInputConfirm -} = usePropertyValue(categoryData.value) +} = usePropertyValue() -const { dragRef, createDrag } = useDrag(categoryData) +const { dragRef, createDrag } = useDrag(adminCategoryData) createDrag() -const skuList = generateSkuList(categoryData.value) +const skuList = ref([]) const htmlContent = ref('自定义') diff --git a/src/views/goods/commodity/detail-dialog/mock.ts b/src/views/goods/commodity/detail-dialog/mock.ts index c0eaa9b..55f186c 100644 --- a/src/views/goods/commodity/detail-dialog/mock.ts +++ b/src/views/goods/commodity/detail-dialog/mock.ts @@ -207,6 +207,152 @@ export const categoryDataMock = [ } ] +/* export const categoryDataMock = [ + { + id: 7, + isDelete: 0, + createTime: 1755940247000, + modifyTime: 1755940247000, + productId: 5, + productPropertyName: '珍珠直径', + defalutSort: 1, + vvProductPropertyValueList: [ + { + id: 21, + isDelete: 0, + createTime: 1755940416000, + modifyTime: 1755940416000, + productId: 5, + productPropertyName: '珍珠直径', + productPropertyValue: '7-8mm', + productPropertyId: 7, + defalutSort: 1 + }, + { + id: 20, + isDelete: 0, + createTime: 1755940416000, + modifyTime: 1755940416000, + productId: 5, + productPropertyName: '珍珠直径', + productPropertyValue: '8-9mm', + productPropertyId: 7, + defalutSort: 2 + }, + { + id: 19, + isDelete: 0, + createTime: 1755940416000, + modifyTime: 1755940416000, + productId: 5, + productPropertyName: '珍珠直径', + productPropertyValue: '9-10mm', + productPropertyId: 7, + defalutSort: 3 + }, + { + id: 18, + isDelete: 0, + createTime: 1755940416000, + modifyTime: 1755940416000, + productId: 5, + productPropertyName: '珍珠直径', + productPropertyValue: '10-11mm', + productPropertyId: 7, + defalutSort: 4 + } + ] + }, + { + id: 6, + isDelete: 0, + createTime: 1755940247000, + modifyTime: 1755940247000, + productId: 5, + productPropertyName: '项链长度', + defalutSort: 2, + vvProductPropertyValueList: [ + { + id: 17, + isDelete: 0, + createTime: 1755940428000, + modifyTime: 1755940428000, + productId: 5, + productPropertyName: '项链长度', + productPropertyValue: '43cm', + productPropertyId: 6, + defalutSort: 1 + }, + { + id: 16, + isDelete: 0, + createTime: 1755940519000, + modifyTime: 1755940519000, + productId: 5, + productPropertyName: '项链长度', + productPropertyValue: '45cm', + productPropertyId: 6, + defalutSort: 2 + }, + { + id: 15, + isDelete: 0, + createTime: 1755940519000, + modifyTime: 1755940519000, + productId: 5, + productPropertyName: '项链长度', + productPropertyValue: '47cm', + productPropertyId: 6, + defalutSort: 3 + }, + { + id: 14, + isDelete: 0, + createTime: 1755940519000, + modifyTime: 1755940519000, + productId: 5, + productPropertyName: '项链长度', + productPropertyValue: '50cm', + productPropertyId: 6, + defalutSort: 4 + } + ] + }, + { + id: 5, + isDelete: 0, + createTime: 1755940247000, + modifyTime: 1755940247000, + productId: 5, + productPropertyName: '颜色分类', + defalutSort: 3, + vvProductPropertyValueList: [ + { + id: 13, + isDelete: 0, + createTime: 1755940570000, + modifyTime: 1755940570000, + productId: 5, + productPropertyName: '颜色分类', + productPropertyValue: '【极光白透粉】大几乎无瑕大配证书', + productPropertyId: 5, + defalutSort: 1 + }, + { + id: 12, + isDelete: 0, + createTime: 1755940570000, + modifyTime: 1755940570000, + productId: 5, + productPropertyName: '颜色分类', + productPropertyValue: '【极光冷白】大几乎无瑕大配证书', + productPropertyId: 5, + defalutSort: 2 + } + ] + } +] */ + // export const a = [ { diff --git a/src/views/goods/commodity/detail-dialog/use-method.ts b/src/views/goods/commodity/detail-dialog/use-method.ts index ad3617f..7162606 100644 --- a/src/views/goods/commodity/detail-dialog/use-method.ts +++ b/src/views/goods/commodity/detail-dialog/use-method.ts @@ -1,3 +1,4 @@ +import { Atrans$DialogRes } from '@/utils/page/config' import { useDraggable } from 'vue-draggable-plus' // 定义源数据的结构类型 interface Option { @@ -15,7 +16,7 @@ interface Category { type CategoryDataMock = Category[] // 定义目标数据的结构类型 -interface TSkuList { +export interface TSkuList { serialNo: string name: string salePrice: string @@ -27,9 +28,9 @@ interface TSkuList { } // 创建sku列表 -export const generateSkuList = (categoryData: CategoryDataMock): Ref => { +export const generateSkuList = (adminCategoryData: CategoryDataMock): TSkuList[] => { const result: TSkuList[] = [] - const optionGroups: Option[][] = categoryData.map((category) => category.vvPropertyValueList) + const optionGroups: Option[][] = adminCategoryData.map((category) => category.vvPropertyValueList) // 使用递归生成所有可能的组合 const combine = (index: number, current: Option[]) => { @@ -54,24 +55,29 @@ export const generateSkuList = (categoryData: CategoryDataMock): Ref } combine(0, []) - return ref(result) + return result } // 增加属性、删除属性 -export const usePropertyValue = (categoryData: CategoryDataMock) => { - const inputVisibles = ref(Array.from({ length: categoryData.length }, (_) => false)) +export const usePropertyValue = () => { + let adminCategoryData: CategoryDataMock = [] + const inputVisibles = ref([]) + const initPropertyData = (initData: CategoryDataMock) => { + inputVisibles.value = Array.from({ length: initData.length }, (_) => false) + adminCategoryData = initData + } const InputRefs = ref() const inputValue = ref('') const onAddPropertyValue = (index: number) => { inputVisibles.value[index] = true nextTick(() => { - InputRefs.value[index].input!.focus() + InputRefs.value[0].input!.focus() }) } const onClosePropertyValue = (index: number, id: number, skuList: Ref) => { - const i = categoryData[index].vvPropertyValueList.findIndex((item: any) => item.id === id) - categoryData[index].vvPropertyValueList.splice(i, 1) + const i = adminCategoryData[index].vvPropertyValueList.findIndex((item: any) => item.id === id) + adminCategoryData[index].vvPropertyValueList.splice(i, 1) const deleteIndexs: number[] = [] skuList.value.forEach((item: TSkuList, skuIndex: number) => { if (item.vvSkuPropertyValueList.find((it) => it.id === id)) { @@ -86,7 +92,7 @@ export const usePropertyValue = (categoryData: CategoryDataMock) => { const handleInputConfirm = (lineIndex: number, skuList: TSkuList[] = []) => { if (inputValue.value) { const id = Math.random() - categoryData[lineIndex].vvPropertyValueList.push({ + adminCategoryData[lineIndex].vvPropertyValueList.push({ id, categoryPropertyValue: inputValue.value, isAdd: true @@ -95,7 +101,7 @@ export const usePropertyValue = (categoryData: CategoryDataMock) => { ...generateAddSkuList( lineIndex, { id, categoryPropertyValue: inputValue.value }, - categoryData + adminCategoryData ) ) } @@ -107,16 +113,16 @@ export const usePropertyValue = (categoryData: CategoryDataMock) => { const generateAddSkuList = ( lineIndex: number, addData: Option, - categoryData: CategoryDataMock + adminCategoryData: CategoryDataMock ): TSkuList[] => { const result: TSkuList[] = [] - const optionGroups: Option[][] = categoryData + const optionGroups: Option[][] = adminCategoryData .filter((_, index) => index !== lineIndex) .map((category) => category.vvPropertyValueList) // 使用递归生成所有可能的组合 const combine = (index: number, current: Option[]) => { - if (index === categoryData.length) { + if (index === optionGroups.length) { const serialNo = current.map((opt) => opt.id.toString()).join('-') const name = current.map((opt) => opt.categoryPropertyValue).join('-') result.push({ @@ -136,13 +142,16 @@ export const usePropertyValue = (categoryData: CategoryDataMock) => { } } - combine(1, [addData]) + combine(0, [addData]) + //bug + console.warn('----- my data is result111: ', result) return result } return { inputVisibles, InputRefs, inputValue, + initPropertyData, onAddPropertyValue, onClosePropertyValue, handleInputConfirm @@ -207,9 +216,63 @@ export const handleLoadNode2 = (node: any, resolve: any) => { }) } -export const checkedNodes = ref<{ admin: number[]; app: number[] }>({ admin: [], app: [] }) -export const handleCheckChange = (_: any, checkedStatus: any, type: 'admin' | 'app') => { +// 图片预览列表 +export const fileList = ref([]) +// 选中后台类目和app类目,获取父子id +export const defaultCheckedNodes = ref<{ admin: number[]; app: number[] }>({ admin: [], app: [] }) +export const handleCheckChange = (_: any, checkedStatus: any, type: 'admin' | 'app', data: any) => { // checkedStatus: { checkedKeys, checkedNodes, halfCheckedKeys, halfCheckedNodes } - checkedNodes.value[type] = [...checkedStatus.halfCheckedKeys, ...checkedStatus.checkedKeys] - console.warn('----- my data is checkedNodes: ', checkedNodes) + data[type + 'CategoryIds'] = [...checkedStatus.halfCheckedKeys, ...checkedStatus.checkedKeys] +} + +interface PreviewItem { + fileName: string + name: string + resourceUrl: string + type: 'image' | 'video' +} + +// 编辑的时候从接口响应数据提取$dialog数据 +export const handleGetDialogData = ( + data: any, + $dialog: Ref, + fileList: Ref +) => { + 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: '主图', resourceUrl: data.mainImageUrl, type: 'image' }, + { fileName: '视频', name: '视频', resourceUrl: data.videoUrl, type: 'video' } + ] + files.push( + ...data.vvProductDetailList + .filter((item: any) => item.type === 1) + .map((item: any) => ({ + fileName: '副图', + name: '副图', + resourceUrl: item.resourceUrl, + type: 'image' + })) + ) + fileList.value = files as PreviewItem[] + $dialog.value.data = result +} + +// 将adminCategoryId1, adminCategoryId2, ... 提取出来并排序 +function extractAdminCategoryIds(obj: Record): 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[] }