elementui实现复杂表单的实践

2024-07-11 1629阅读

简介

文章主要讲述在vue3项目中使用elementui框架实现复杂表单的方式。表单中涉及动态组件的生成、文件上传和富文本编辑器的使用,只会将在实现过程中较复杂的部分进行分享,然后提供一份完整的前端代码。

表单效果演示

基础信息

elementui实现复杂表单的实践

spu属性

elementui实现复杂表单的实践

sku详情

elementui实现复杂表单的实践

elementui实现复杂表单的实践

elementui实现复杂表单的实践

关键点

动态组件的处理

在spu属性步骤中,CPU型号、操作系统和生物解锁三个属性都是动态生成的。

实现原理

组件动态生成和删除的原理是通过操作v-for组件的数据源实现的。向数据源添加数据会动态生成组件,将数据源的数据减少就会删除对应的组件。

所以在点击添加属性时,只要向v-for的数据源中添加属性配置,就能实现动态生成组件的效果。

组件的类型、可选值都是提前定义的。

分析JD、淘宝等各类商城关于spu属性的展示,可以发现基本可以用自定义、单选和多选属性把所有spu属性覆盖。

因此就可以提前将商品可用的属性按类型和可选值完成定义,在页面选择对应属性添加到页面即可。

动态添加spu属性的代码

只需要重点关注 addSpuItem、rmAttr 方法和 v-for="(dynamicSpu, dspuK) in state.auxiliaries.optional.attr.spu.dynamicItems"

        
            
            
        
        
        添加规格属性
    
    
        
        
            {{ dspuAttr
                }}
        
        
            
        
        
        删除
    

向spu数据源添加元素

主要是向 state.auxiliaries.optional.attr.spu.dynamicItems 这个数组中追加元素

// 动态生成SPU条目
const addSpuItem = () => {
    if (state.value.auxiliaries.optional.attr.spu.dynamic === 0) {
        ElMessage.error("请选择一个属性")
        return
    }
    // 已选择的SPU属性
    for (let i = 0; i  

删除动态组件

将对应数据源的元素删除,就可以实现动态删除生成的组件

const rmAttr = (type: string, idx: number) => {
    if (type === "spu") {
        // 删除动态添加的SPU属性
        state.value.auxiliaries.optional.attr.spu.dynamicItems.splice(idx, 1)
        return
    }
    if (type === "sku") {
        state.value.auxiliaries.optional.attr.sku.dynamicItems.splice(idx, 1)
    }
}

文件上传组件的使用

在sku部分有上传组件的使用。

利用elementUI提供的组件实现,相对来说没有什么难度。由于是动态组件,每个上传文件的组件要设置一个单独的参数接收数据,避免出现数据覆盖的情况。

    
    
        
            
                
            
        
    

主要用到了组件的action、headers属性。action设置上传文件的接口、headers设置调用接口时必要的请求头

定义headers

一般项目都是前后端分离的,headers通常设置调用接口的token等身份认证信息。

const state = ref({
    auxiliaries: {
        configs: {
            upload: {
                headers: {
                    Authorization: "Bearer " + Session.get('token')
                },
                coversForm: [] as Array
            }
        }
    }
})

动态使用富文本编辑器

富文本编辑器使用的是tinymce,特点就是轻量、简洁。具体使用教程可以看这里。

动态的富文本编辑器实现原理与上传组件一样,区别就是在获取富文本编辑器中的内容的方式有所不同。

在vue3中富文本编辑器的组件需要设置ref属性,在获取编辑器的内容时需要通过ref获取到编辑器的实例,然后再获取内容。

    
    
        
        
        
    


import Editor from '@tinymce/tinymce-vue'

在vue3中当页面有多个Editor实例时,对应ref的值会是数组。

获取多富文本编辑器的内容

import {ref} from 'vue'
const skuDetailRef = ref()
const submitForm = () => {
    // 将sku的详情追加到form
    for (let skuIdx = 0; skuIdx  

相关的完整代码

运行的依赖有Vue3、element-plus和tinymce

    
        
            
                
                    
                    
                    
                
                
                
                    
                        
                            
                                
                            
                            
                                
                                    
                                    
                                
                            
                            
                                
                            
                            
                                下一步
                            
                        
                        
                            
                                
                                    
                                    
                                
                                
                                添加规格属性
                            
                            
                                
                                
                                    {{ dspuAttr }}
                                
                                
                                    
                                
                                
                                删除
                            
                            
                                下一步
                            
                        
                        
                            
                                
                                    
                                    
                                
                                
                                添加规格属性
                                
                                生成SKU条目
                            
                            
                                
                                    
                                
                                
                                删除
                            
                            
                                设置具体的SKU信息
                                
                                    初始化标题
                                    初始化简介
                                    初始化库存
                                    初始化价格
                                
                                
                                
                                    
                                        
                                        
                                            
                                        
                                        
                                        
                                            
                                        
                                        
                                        
                                        
                                            
 
     
 
                                            
                                        
                                        
                                        
                                        
                                            
                                        
                                        
                                        
                                        
                                            
                                        
                                        
                                        
                                        
                                            
                                            
                                            
                                        
                                        
                                    
                                
                            
                            
                                提交
                                重置
                            
                        
                    
                
                
                    
                
            
        
    


import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import type { UploadProps } from 'element-plus'
import Editor from '@tinymce/tinymce-vue'
import { Session } from '/@/utils/storage';
import { UploadAjaxError } from 'element-plus/es/components/upload/src/ajax'
import { useFiletApi } from '/@/api/file'
const skuDetailRef = ref()
const fileApi = useFiletApi()
const state = ref({
    form: {
        id: null,
        name: "",
        brandId: 0,
        categoryId: [],
        spu: [] as ProductAttrType[],
        sku: [] as ProductAttrType[],
        skuDetails: [] as ProductSkuFormType[],
    } as ProductFormType,
    rawForm: {
        id: null,
        name: "",
        brandId: 0,
        categoryId: [],
        spu: [] as ProductAttrType[],
        sku: [] as ProductAttrType[],
        skuDetails: [] as ProductSkuFormType[]
    } as ProductFormType,
    auxiliaries: {
        configs: {
            step: {
                active: 1, // 步骤条
                stepTab: [
                    { name: "product-base", step: 1 },
                    { name: "set-spu", step: 2 },
                    { name: "set-sku", step: 3 },
                ],
                stepTabActive: "product-base",
            },
            editor: {
                configs: {
                    width: '100%',
                    resize: 'both',
                    min_height: 300,
                    plugins: "image fullscreen",
                    // 实现上传逻辑
                    images_upload_handler: async (blobInfo: any, success: any) => {
                        console.log("upload image ", blobInfo, success)
                        const formData = new FormData();
                        formData.append('file', blobInfo.blob());
                        // 上传
                        const uploadRs = await uploadImg(formData)
                        // success("https://cdn3-banquan.ituchong.com/weili/image/l/1517224685217251339.jpeg")
                        success(uploadRs.data)
                    }
                }
            },
            upload: {
                headers: {
                    Authorization: "Bearer " + Session.get('token')
                },
                coversForm: [] as Array
            }
        },
        optional: {
            brandList: [
                { label: "华为", value: 1 }, { label: "苹果", value: 2 },
            ],
            categories: {
                list: [
                    {
                        label: "手机品类", value: 1, children: [
                            { label: "手机", value: 3 },
                            { label: "手机配件", value: 4 }
                        ]
                    },
                    {
                        label: "电脑", value: 2, children: [
                            { label: "电脑配件", value: 5 },
                            {
                                label: "外设产品", value: 6, children: [
                                    { label: "鼠标", value: 7 },
                                    { label: "键盘", value: 8 }
                                ]
                            }
                        ]
                    }
                ],
                props: {
                    expandTrigger: 'hover' as const
                }
            },
            attr: {
                list: [
                    { attrId: 1, name: "颜色", value: ["雅川青", "雅丹黑", "南糯紫", "白沙银", "red"], type: "SINGLE" },
                    { attrId: 2, name: "版本", value: ["12GB+512GB", "12GB+1TB", "16GB+1TB"], type: "SINGLE" },
                    { attrId: 3, name: "CPU型号", value: [], type: "CUSTOM" },
                    { attrId: 4, name: "操作系统", value: ["HarmonyOS", "IOS", "Android"], type: "SINGLE" },
                    { attrId: 5, name: "生物解锁", value: ["指纹", "面部", "视网膜"], type: "MULTIPLE" },
                ],
                spu: {
                    dynamic: 0, // 动态选择的SPU属性组
                    dynamicItems: [] as any[], // 动态选择并生成的SPU属性表单项
                },
                sku: {
                    dynamic: 0, // 动态选择的SPU属性组
                    dynamicItems: [] as any[], // 动态选择并生成的SPU属性表单项
                    dynamicDetailItems: [] as any[], // 根据选择的SKU属性排列组合生成的SKU具体条目
                    dynamicDetailCollapaseActiveNames: [], // 激活状态的折叠面板
                    imgDialog: {
                        visiable: false,
                        url: ""
                    }
                }
            }
        }
    }
})
const nextStep = () => {
    state.value.auxiliaries.configs.step.stepTabActive = state.value.auxiliaries.configs.step.stepTab[state.value.auxiliaries.configs.step.active].name
    state.value.auxiliaries.configs.step.active += 1
}
const handleTabChange = (currentTabName: string) => {
    for (let i = 0; i  {
    if (state.value.auxiliaries.optional.attr.spu.dynamic === 0) {
        ElMessage.error("请选择一个属性")
        return
    }
    // 已选择的SPU属性
    for (let i = 0; i  {
    if (type === "spu") {
        // 删除动态添加的SPU属性
        state.value.auxiliaries.optional.attr.spu.dynamicItems.splice(idx, 1)
        return
    }
    if (type === "sku") {
        state.value.auxiliaries.optional.attr.sku.dynamicItems.splice(idx, 1)
    }
}
// 动态生成SKU条目
const addSkuItem = () => {
    if (state.value.auxiliaries.optional.attr.sku.dynamic === 0) {
        ElMessage.error("请选择一个属性")
        return
    }
    // 已选择的sku属性
    for (let i = 0; i  {
    // 清空已有的SKU信息
    state.value.form.skuDetails = [] as ProductSkuFormType[]
    state.value.auxiliaries.configs.upload.coversForm = [] as any[]
    // 获取所有SKU属性和选择的值
    const skus = state.value.auxiliaries.optional.attr.sku.dynamicItems
    // 遍历属性数
    let tempSkuItems = [] // 排列组合临时状态
    for (let i = 0; i  {
            // 将sku的对应信息初始化
            if (type === 'title') {
                initTitle(value)
                return
            }
            if (type === 'summary') {
                initSummary(value)
                return
            }
            if (type === 'stock') {
                initStock(parseInt(value))
                return
            }
            if (type === 'price') {
                initPrice(parseFloat(value))
                return
            }
        })
}
// 初始化sku的销售标题
const initTitle = (titlePrefix: string) => {
    // 自定义的标题加设置的sku属性名
    for (let i = 0; i  {
    for (let i = 0; i  {
    for (let i = 0; i  {
    for (let i = 0; i  {
    console.log("调用上传文件的接口", uploadForm.get('rawFilename'))
    return fileApi.uploadCover(uploadForm)
}
const handleUploadCoverSuccess = (response: any) => {
    console.log("upload success ", response)
}
const handleUploadCoverFailed = (error: UploadAjaxError) => {
    let rs = JSON.parse(error.message)
    ElMessage.error(rs.msg)
}
const genCollapseTitle = (tempSkuItems: Array): string => {
    let tempTitle = ""
    for (let n = 0; n  {
    console.log(uploadFile, uploadFiles)
}
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
    state.value.auxiliaries.optional.attr.sku.imgDialog.url = uploadFile.url!
    state.value.auxiliaries.optional.attr.sku.imgDialog.visiable = true
}
const submitForm = () => {
    console.log("upload covers: ", state.value.auxiliaries.configs.upload.coversForm)
    // 从临时保存轮播图数据的变量中提取轮播图的url
    // 遍历sku
    for (let i = 0; i  {
    // 重置表单
    state.value.form = JSON.parse(JSON.stringify(state.value.rawForm))
    // 重置sku、spu的动态数据
    state.value.auxiliaries.optional.attr.sku.dynamicItems = []
    state.value.auxiliaries.optional.attr.sku.dynamicDetailItems = []
    state.value.auxiliaries.optional.attr.spu.dynamicItems = []
}
VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]