feat: 商品详情提交
This commit is contained in:
parent
c198ca35ea
commit
c0c64aee57
@ -22,7 +22,7 @@ const login = {
|
||||
*/
|
||||
getCommodityList: ['/product/list'], // 获取商品列表
|
||||
getCommodityDetail: ['/product/detail'], // 获取商品详情
|
||||
addOrUpdateCommodity: [''] // 修改商品详情
|
||||
addOrUpdateCommodity: ['/product/insertOrUpadate'] // 修改商品详情
|
||||
}
|
||||
|
||||
export default login
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
interface Props {
|
||||
/** 是否允许多选文件 */
|
||||
multiple?: boolean
|
||||
size?: 'small' | 'normal'
|
||||
size?: 'small' | 'default'
|
||||
/** 接受的文件类型 */
|
||||
accept?: 'image/*,video/*' | 'image/*' | 'video/*'
|
||||
/** 按钮文本 */
|
||||
@ -31,7 +31,7 @@ withDefaults(defineProps<Props>(), {
|
||||
multiple: false,
|
||||
accept: 'image/*,video/*',
|
||||
buttonText: '选择文件',
|
||||
size: 'normal'
|
||||
size: 'default'
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@ -13,7 +13,7 @@ export const useImportFile = (fileList: Ref<PreviewItem[]>) => {
|
||||
videoUrl: '视频',
|
||||
subImageUrl: '副图'
|
||||
}
|
||||
const curFileType = ref<'mainImageUrl' | 'videoUrl' | 'subImage'>('mainImageUrl')
|
||||
const curFileType = ref<'mainImageUrl' | 'videoUrl' | 'subImageUrl'>('mainImageUrl')
|
||||
const currentPathArray = ref<{ name: string; id: number }[]>([{ name: '根目录', id: 0 }])
|
||||
|
||||
// 处理文件选择
|
||||
@ -21,7 +21,7 @@ export const useImportFile = (fileList: Ref<PreviewItem[]>) => {
|
||||
handleChooseFiles(curFileType.value, files, fileList.value)
|
||||
}
|
||||
|
||||
const onClickChooseResourceBtn = (type: 'mainImageUrl' | 'videoUrl' | 'subImage') => {
|
||||
const onClickChooseResourceBtn = (type: 'mainImageUrl' | 'videoUrl' | 'subImageUrl') => {
|
||||
curFileType.value = type
|
||||
showFileExplorer.value = true
|
||||
}
|
||||
@ -34,14 +34,14 @@ export const useImportFile = (fileList: Ref<PreviewItem[]>) => {
|
||||
}
|
||||
|
||||
export const handleChooseFiles = (
|
||||
fileType: 'mainImageUrl' | 'videoUrl' | 'subImage',
|
||||
fileType: 'mainImageUrl' | 'videoUrl' | 'subImageUrl',
|
||||
files: any = [],
|
||||
fileList: PreviewItem[]
|
||||
) => {
|
||||
const fileNameMap = {
|
||||
mainImageUrl: '主图',
|
||||
videoUrl: '视频',
|
||||
subImage: '副图'
|
||||
subImageUrl: '副图'
|
||||
}
|
||||
if (['mainImageUrl', 'videoUrl'].includes(fileType)) {
|
||||
const index = fileList.findIndex((item: any) => item.fileType === fileType)
|
||||
|
||||
@ -4,8 +4,8 @@ export const initConfig = () => {
|
||||
configData.value = pageConfig({
|
||||
dialog: [
|
||||
{
|
||||
title: { label: '商品标题', class: '!w-full' },
|
||||
appCategoryIds: { label: 'app类目', slot: 'appCategoryId' },
|
||||
title: { label: '商品标题', class: '!w-full', rule: true },
|
||||
appCategoryIds: { label: 'app类目', slot: 'appCategoryId', rule: true },
|
||||
mainImageUrl: { label: '主图', slot: 'mainFile' },
|
||||
videoUrl: { label: '视频', slot: 'videoFile' },
|
||||
subImageUrl: { label: '副图', slot: 'subFile' },
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
<template #mainFile>
|
||||
<file-upload-btn
|
||||
accept="image/*"
|
||||
size="normal"
|
||||
size="default"
|
||||
@change="(val) => handleChooseFiles('mainImageUrl', val, fileList)"
|
||||
/>
|
||||
<el-button class="ml-2" @click="onClickChooseResourceBtn('mainImageUrl')"
|
||||
@ -31,7 +31,7 @@
|
||||
</template>
|
||||
<template #videoFile>
|
||||
<file-upload-btn
|
||||
size="normal"
|
||||
size="default"
|
||||
accept="video/*"
|
||||
@change="(val) => handleChooseFiles('videoUrl', val, fileList)"
|
||||
/>
|
||||
@ -41,12 +41,12 @@
|
||||
</template>
|
||||
<template #subFile>
|
||||
<file-upload-btn
|
||||
size="normal"
|
||||
size="default"
|
||||
multiple
|
||||
accept="image/*"
|
||||
@change="(val) => handleChooseFiles('subImage', val, fileList)"
|
||||
@change="(val) => handleChooseFiles('subImageUrl', val, fileList)"
|
||||
/>
|
||||
<el-button class="ml-2" @click="onClickChooseResourceBtn('subImage')"
|
||||
<el-button class="ml-2" @click="onClickChooseResourceBtn('subImageUrl')"
|
||||
>资源库导入</el-button
|
||||
>
|
||||
</template>
|
||||
@ -67,7 +67,9 @@
|
||||
:default-expanded-keys="defaultCheckedNodes.admin"
|
||||
@check="
|
||||
(checkedNode: any, checkedStatus: any) =>
|
||||
handleCheckChange(checkedNode, checkedStatus, 'admin')
|
||||
handleAfterChangeCategory(
|
||||
handleCheckChange(checkedNode, checkedStatus, 'admin', $dialog.data)
|
||||
)
|
||||
"
|
||||
:load="handleLoadNode"
|
||||
lazy
|
||||
@ -77,6 +79,20 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="测试商品">
|
||||
<el-switch v-model="$dialog.data.isTest" active-value="1" inactive-value="0" />
|
||||
<el-popover
|
||||
:visible="visiblePropertyLine"
|
||||
placement="bottom"
|
||||
title="请输入属性类型标题"
|
||||
:width="200"
|
||||
trigger="click"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button type="primary" class="ml-2 !text-white" @click="visiblePropertyLine = true"
|
||||
>新增属性类型</el-button
|
||||
>
|
||||
</template>
|
||||
<el-input v-model="inputPropertyLineValue" @keyup.enter="onAddPropertyLine"></el-input>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div ref="dragRef">
|
||||
@ -138,7 +154,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { initConfig } from './config'
|
||||
import { categoryDataMock } from './mock'
|
||||
import {
|
||||
generateSkuList,
|
||||
usePropertyValue,
|
||||
@ -151,7 +166,11 @@ import {
|
||||
defaultCheckedNodes,
|
||||
handleGetDialogData,
|
||||
fileList,
|
||||
TSkuList
|
||||
TSkuList,
|
||||
handleGetNewAdminCategoryData,
|
||||
handleGetNewSkuList,
|
||||
handleValidFormData,
|
||||
handleGetSubmitParams
|
||||
} from './use-method'
|
||||
import categoryConfig from './category-config.vue'
|
||||
import wanEditor from './editor.vue'
|
||||
@ -160,6 +179,7 @@ import resourceReview from '@/components/ResourceReview/index.vue'
|
||||
import { useImportFile, handleChooseFiles } from './choose-file-method'
|
||||
import FileExplorerDialog from '@/components/FileExplorerDialog/index.vue'
|
||||
import { Atrans$DialogRes } from '@/utils/page/config'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const route = useRoute()
|
||||
const $dialog = ref<Atrans$DialogRes>({} as Atrans$DialogRes)
|
||||
@ -170,12 +190,15 @@ const init = async () => {
|
||||
$dialog.value.config = data.dialog1
|
||||
$dialog.value.data = { appCategoryId: '', adminCategoryId: '', isTest: 0, title: '' }
|
||||
adminCategoryData.value = []
|
||||
skuList.value = []
|
||||
if (route.query.id) {
|
||||
const res = await api.commodity.getCommodityDetail.post!<any>({ productId: route.query.id })
|
||||
handleGetDialogData(res.data, $dialog, fileList)
|
||||
adminCategoryData.value = categoryDataMock
|
||||
adminCategoryData.value = handleGetNewAdminCategoryData(res.data.vvProductPropertyList)
|
||||
initPropertyData(adminCategoryData.value)
|
||||
skuList.value = handleGetNewSkuList(res.data.vvSkuList)
|
||||
} else {
|
||||
initPropertyData(adminCategoryData.value)
|
||||
skuList.value = generateSkuList(adminCategoryData.value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,25 +212,55 @@ const {
|
||||
const adminCategoryData = ref([])
|
||||
const {
|
||||
inputValue,
|
||||
visiblePropertyLine,
|
||||
inputPropertyLineValue,
|
||||
InputRefs,
|
||||
inputVisibles,
|
||||
initPropertyData,
|
||||
onAddPropertyValue,
|
||||
onClosePropertyValue,
|
||||
handleInputConfirm
|
||||
handleInputConfirm,
|
||||
onAddPropertyLine
|
||||
} = usePropertyValue()
|
||||
|
||||
const { dragRef, createDrag } = useDrag(adminCategoryData)
|
||||
createDrag()
|
||||
const skuList = ref<TSkuList[]>([])
|
||||
|
||||
// 当改变后台类目的时候,进行修改重置sku
|
||||
const handleAfterChangeCategory = (data: any) => {
|
||||
console.warn('----- my data is data.vvCategoryPropertyDTOList: ', data)
|
||||
adminCategoryData.value = data.categoryPropertyList || []
|
||||
initPropertyData(adminCategoryData.value)
|
||||
skuList.value = generateSkuList(adminCategoryData.value)
|
||||
}
|
||||
|
||||
const htmlContent = ref('自定义')
|
||||
|
||||
const onDeletePropertyValue = (index: number, id: number) =>
|
||||
onClosePropertyValue(index, id, skuList)
|
||||
|
||||
const onSubmit = () => {
|
||||
const msg = handleValidFormData(
|
||||
$dialog.value.data,
|
||||
fileList.value,
|
||||
adminCategoryData.value,
|
||||
htmlContent.value,
|
||||
skuList.value
|
||||
)
|
||||
// if (msg) return ElMessage.error(msg)
|
||||
console.warn('----- my data is htmlContent: ', htmlContent.value)
|
||||
const params = handleGetSubmitParams(
|
||||
$dialog.value.data,
|
||||
fileList.value,
|
||||
adminCategoryData.value,
|
||||
htmlContent.value,
|
||||
skuList.value
|
||||
)
|
||||
console.warn('----- my data is params111: ', params)
|
||||
api.commodity.addOrUpdateCommodity.post!(params).then((res) => {
|
||||
console.warn('----- my data is res: ', res)
|
||||
})
|
||||
}
|
||||
init()
|
||||
</script>
|
||||
|
||||
@ -27,8 +27,11 @@ export interface TSkuList {
|
||||
vvSkuPropertyValueList: any[]
|
||||
}
|
||||
|
||||
// 创建sku列表
|
||||
// 当改变后台类目的时候,重新创建sku列表
|
||||
export const generateSkuList = (adminCategoryData: CategoryDataMock): TSkuList[] => {
|
||||
if (adminCategoryData.length === 0) {
|
||||
return []
|
||||
}
|
||||
const result: TSkuList[] = []
|
||||
const optionGroups: Option[][] = adminCategoryData.map((category) => category.vvPropertyValueList)
|
||||
|
||||
@ -69,6 +72,8 @@ export const usePropertyValue = () => {
|
||||
|
||||
const InputRefs = ref()
|
||||
const inputValue = ref('')
|
||||
const visiblePropertyLine = ref(false)
|
||||
const inputPropertyLineValue = ref('')
|
||||
const onAddPropertyValue = (index: number) => {
|
||||
inputVisibles.value[index] = true
|
||||
nextTick(() => {
|
||||
@ -147,11 +152,26 @@ export const usePropertyValue = () => {
|
||||
console.warn('----- my data is result111: ', result)
|
||||
return result
|
||||
}
|
||||
const onAddPropertyLine = () => {
|
||||
visiblePropertyLine.value = false
|
||||
adminCategoryData.push({
|
||||
id: Math.random(),
|
||||
categoryId: 17,
|
||||
categoryName: '',
|
||||
visiblePropertyLine,
|
||||
categoryPropertyName: inputPropertyLineValue.value,
|
||||
vvPropertyValueList: []
|
||||
})
|
||||
inputPropertyLineValue.value = ''
|
||||
}
|
||||
return {
|
||||
inputVisibles,
|
||||
visiblePropertyLine,
|
||||
inputPropertyLineValue,
|
||||
InputRefs,
|
||||
inputValue,
|
||||
initPropertyData,
|
||||
onAddPropertyLine,
|
||||
onAddPropertyValue,
|
||||
onClosePropertyValue,
|
||||
handleInputConfirm
|
||||
@ -194,7 +214,7 @@ export const handleLoadNode = (node: any, resolve: any) => {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
hasChild: item.hasChild,
|
||||
disabled: item.hasChild,
|
||||
disabled: !!item.hasChild,
|
||||
categoryPropertyList: item.vvCategoryPropertyDTOList
|
||||
}))
|
||||
)
|
||||
@ -209,7 +229,7 @@ export const handleLoadNode2 = (node: any, resolve: any) => {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
hasChild: item.hasChild,
|
||||
disabled: item.hasChild,
|
||||
disabled: !!item.hasChild,
|
||||
categoryPropertyList: item.vvCategoryPropertyDTOList
|
||||
}))
|
||||
)
|
||||
@ -220,9 +240,15 @@ export const handleLoadNode2 = (node: any, resolve: any) => {
|
||||
export const fileList = ref<PreviewItem[]>([])
|
||||
// 选中后台类目和app类目,获取父子id
|
||||
export const defaultCheckedNodes = ref<{ admin: number[]; app: number[] }>({ admin: [], app: [] })
|
||||
export const handleCheckChange = (_: any, checkedStatus: any, type: 'admin' | 'app', data: any) => {
|
||||
export const handleCheckChange = (
|
||||
checkedData: any,
|
||||
checkedStatus: any,
|
||||
type: 'admin' | 'app',
|
||||
data: any
|
||||
) => {
|
||||
// checkedStatus: { checkedKeys, checkedNodes, halfCheckedKeys, halfCheckedNodes }
|
||||
data[type + 'CategoryIds'] = [...checkedStatus.halfCheckedKeys, ...checkedStatus.checkedKeys]
|
||||
return checkedData
|
||||
}
|
||||
|
||||
interface PreviewItem {
|
||||
@ -230,6 +256,7 @@ interface PreviewItem {
|
||||
name: string
|
||||
resourceUrl: string
|
||||
type: 'image' | 'video'
|
||||
fileType: 'mainImageUrl' | 'videoUrl' | 'subImageUrl'
|
||||
}
|
||||
|
||||
// 编辑的时候从接口响应数据提取$dialog数据
|
||||
@ -246,8 +273,20 @@ export const handleGetDialogData = (
|
||||
result.isTest = data.isTest
|
||||
result.title = data.title
|
||||
const files = [
|
||||
{ fileName: '主图', name: '主图', resourceUrl: data.mainImageUrl, type: 'image' },
|
||||
{ fileName: '视频', name: '视频', resourceUrl: data.videoUrl, type: 'video' }
|
||||
{
|
||||
fileName: '主图',
|
||||
name: '主图',
|
||||
fileType: 'mainImageUrl',
|
||||
resourceUrl: data.mainImageUrl,
|
||||
type: 'image'
|
||||
},
|
||||
{
|
||||
fileName: '视频',
|
||||
name: '视频',
|
||||
fileType: 'videoUrl',
|
||||
resourceUrl: data.videoUrl,
|
||||
type: 'video'
|
||||
}
|
||||
]
|
||||
files.push(
|
||||
...data.vvProductDetailList
|
||||
@ -256,6 +295,7 @@ export const handleGetDialogData = (
|
||||
fileName: '副图',
|
||||
name: '副图',
|
||||
resourceUrl: item.resourceUrl,
|
||||
fileType: 'subImageUrl',
|
||||
type: 'image'
|
||||
}))
|
||||
)
|
||||
@ -276,3 +316,133 @@ function extractAdminCategoryIds(obj: Record<string, any>): number[] {
|
||||
.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
|
||||
}))
|
||||
}))
|
||||
}
|
||||
|
||||
// 编辑的时候对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 = (
|
||||
$dialogData: any,
|
||||
fileList: PreviewItem[],
|
||||
adminCategoryData: CategoryDataMock,
|
||||
htmlContent: string,
|
||||
skuList: TSkuList[]
|
||||
) => {
|
||||
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: adminCategoryData.map((item) => ({
|
||||
...item,
|
||||
productPropertyName: item.categoryPropertyName,
|
||||
vvProductPropertyValueList: item.vvPropertyValueList.map((item) => ({
|
||||
...item,
|
||||
productPropertyValue: item.categoryPropertyValue
|
||||
}))
|
||||
})),
|
||||
vvSkuList: skuList.map((item) => ({
|
||||
...item,
|
||||
vvSkuPropertyValueList: item.vvSkuPropertyValueList.map((item) => ({
|
||||
...item,
|
||||
productPropertyValue: item.categoryPropertyValue
|
||||
}))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user