elementui实现复杂表单的实践

简介

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

表单效果演示

基础信息

spu属性

sku详情

关键点

动态组件的处理

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

实现原理

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

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

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

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

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

动态添加spu属性的代码

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

<el-form-item label="选择属性"><el-select filterable v-model="state.auxiliaries.optional.attr.spu.dynamic"style="display: inline-block;width: 198px"><el-option label="请选择" :value="0"></el-option><el-option v-for="(spuAttr, spuK) in state.auxiliaries.optional.attr.list" :key="spuK" :label="spuAttr.name":value="spuAttr.attrId"></el-option></el-select><el-divider direction="vertical" border-style="none" /><el-button type="primary" @click="addSpuItem">添加规格属性</el-button></el-form-item><el-form-item v-for="(dynamicSpu, dspuK) in state.auxiliaries.optional.attr.spu.dynamicItems" :key="dspuK":label="dynamicSpu.name"><el-input v-model="dynamicSpu.selectedValue" v-if="dynamicSpu.type === 'CUSTOM'"style="max-width: 40%"></el-input><el-radio-group v-model="dynamicSpu.selectedValue" v-else-if="dynamicSpu.type === 'SINGLE'"><el-radio v-for="(dspuAttr, dspuaK) in dynamicSpu.value" :key="dspuaK" :label="dspuAttr">{{ dspuAttr}}</el-radio></el-radio-group><el-checkbox-group v-model="dynamicSpu.selectedValue" v-else-if="dynamicSpu.type === 'MULTIPLE'"><el-checkbox v-for="(dspumAttr, dspumK) in dynamicSpu.value" :key="dspumK" :label="dspumAttr":value="dspumAttr"></el-checkbox></el-checkbox-group><el-divider direction="vertical"></el-divider><el-button type="danger" @click="rmAttr('spu', dspuK)" size="small">删除</el-button></el-form-item>

向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 < state.value.auxiliaries.optional.attr.list.length; i++) {if (state.value.auxiliaries.optional.attr.list[i].attrId === state.value.auxiliaries.optional.attr.spu.dynamic) {// 利用JSON实现简单对象的深拷贝let dynamicItem = JSON.parse(JSON.stringify(state.value.auxiliaries.optional.attr.list[i]))dynamicItem.selectedValue = []state.value.auxiliaries.optional.attr.spu.dynamicItems.push(dynamicItem)}}
}

删除动态组件

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

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

    <!-- 商品轮播图 --><el-form-item label="商品轮播图" label-width="120"><el-upload v-model:file-list="state.auxiliaries.configs.upload.coversForm[dDetailItemK].covers"action="http://localhost:9900/upload/cover" multiple :headers="state.auxiliaries.configs.upload.headers":on-success="handleUploadCoverSuccess" :on-error="handleUploadCoverFailed" method="post"list-type="picture-card" :on-preview="handlePictureCardPreview" :on-remove="handleRemove"><el-icon><Plus /></el-icon></el-upload></el-form-item>

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

定义headers

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

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

动态使用富文本编辑器

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

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

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

<template><!-- 商详 --><el-form-item v-for="(dynamicSku, dskuK) in state.auxiliaries.optional.attr.sku.dynamicItems":key="dskuK" label="商品详情" label-width="120"><!-- 用tinymce富文本编辑器 --><Editor ref="skuDetailRef" :init="state.auxiliaries.configs.editor.configs" initial-value="Welcome to TinyMCE!"></Editor></el-form-item>
</template>
<script setup lang="ts">
import Editor from '@tinymce/tinymce-vue'
</script>

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

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

<script setup lang="ts">import {ref} from 'vue'
const skuDetailRef = ref()
const submitForm = () => {// 将sku的详情追加到formfor (let skuIdx = 0; skuIdx < skuDetailRef.value.length; skuIdx++) {skuDetailRef.value[skuIdx].getEditor().getContent()}
}
</script>

相关的完整代码

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

<template><el-row style="margin-top: 20px;"><el-col :span="22" :offset="1"><el-card><el-steps style="width: 100%" :active="state.auxiliaries.configs.step.active" finish-status="success"><el-step title="基础信息" /><el-step title="设置规格" /><el-step title="设置SKU" /></el-steps><el-divider border-style="none"></el-divider><el-form v-model="state.form"><el-tabs v-model="state.auxiliaries.configs.step.stepTabActive" class="demo-tabs"@tab-change="handleTabChange"><el-tab-pane label="基础信息" name="product-base"><el-form-item label="商品名称"><el-input v-model="state.form.name"></el-input></el-form-item><el-form-item label="选择品牌"><el-select v-model="state.form.brandId"><el-option :value=0 label="请选择"></el-option><el-option v-for="(brand, bk) in state.auxiliaries.optional.brandList" :key="bk":label="brand.label" :value="brand.value"></el-option></el-select></el-form-item><el-form-item label="所属分类"><el-cascader v-model="state.form.categoryId" clearable:props="state.auxiliaries.optional.categories.props":options="state.auxiliaries.optional.categories.list"></el-cascader></el-form-item><el-form-item><el-button type="primary" @click="nextStep">下一步</el-button></el-form-item></el-tab-pane><el-tab-pane label="设置规格" name="set-spu"><el-form-item label="选择属性"><el-select filterable v-model="state.auxiliaries.optional.attr.spu.dynamic"style="display: inline-block;width: 198px"><el-option label="请选择" :value="0"></el-option><el-option v-for="(spuAttr, spuK) in state.auxiliaries.optional.attr.list":key="spuK" :label="spuAttr.name" :value="spuAttr.attrId"></el-option></el-select><el-divider direction="vertical" border-style="none" /><el-button type="primary" @click="addSpuItem">添加规格属性</el-button></el-form-item><el-form-itemv-for="(dynamicSpu, dspuK) in state.auxiliaries.optional.attr.spu.dynamicItems":key="dspuK" :label="dynamicSpu.name"><el-input v-model="dynamicSpu.selectedValue" v-if="dynamicSpu.type === 'CUSTOM'"style="max-width: 40%"></el-input><el-radio-group v-model="dynamicSpu.selectedValue" v-else-if="dynamicSpu.type === 'SINGLE'"><el-radio v-for="(dspuAttr, dspuaK) in dynamicSpu.value" :key="dspuaK":label="dspuAttr">{{ dspuAttr }}</el-radio></el-radio-group><el-checkbox-group v-model="dynamicSpu.selectedValue" v-else-if="dynamicSpu.type === 'MULTIPLE'"><el-checkbox v-for="(dspumAttr, dspumK) in dynamicSpu.value" :key="dspumK":label="dspumAttr" :value="dspumAttr"></el-checkbox></el-checkbox-group><el-divider direction="vertical"></el-divider><el-button type="danger" @click="rmAttr('spu', dspuK)" size="small">删除</el-button></el-form-item><el-form-item><el-button type="primary" @click="nextStep">下一步</el-button></el-form-item></el-tab-pane><el-tab-pane label="设置SKU" name="set-sku"><el-form-item label="选择属性"><el-select filterable v-model="state.auxiliaries.optional.attr.sku.dynamic"style="display: inline-block;width: 198px"><el-option label="请选择" :value="0"></el-option><el-option v-for="(skuAttr, skuK) in state.auxiliaries.optional.attr.list":key="skuK" :label="skuAttr.name" :value="skuAttr.attrId"></el-option></el-select><el-divider direction="vertical" border-style="none" /><el-button type="primary" @click="addSkuItem">添加规格属性</el-button><el-divider border-style="none" direction="vertical"></el-divider><el-button type="success" @click="genSkuItems">生成SKU条目</el-button></el-form-item><el-form-itemv-for="(dynamicSku, dskuK) in state.auxiliaries.optional.attr.sku.dynamicItems":key="dskuK" :label="dynamicSku.name"><el-checkbox-group v-model="dynamicSku.selectedValue"><el-checkbox v-for="(dskumAttr, dskumK) in dynamicSku.value" :key="dskumK":label="dskumAttr" :value="dskumAttr"></el-checkbox></el-checkbox-group><el-divider direction="vertical"></el-divider><el-button type="danger" @click="rmAttr('sku', dskuK)" size="small">删除</el-button></el-form-item><el-form-item v-if="state.auxiliaries.optional.attr.sku.dynamicDetailItems.length > 0"><el-divider content-position="left">设置具体的SKU信息</el-divider><el-button-group style="margin-bottom: 15px"><el-button type="primary" @click="initSkuInfo('title')">初始化标题</el-button><el-button type="primary" @click="initSkuInfo('summary')">初始化简介</el-button><el-button type="primary" @click="initSkuInfo('stock')">初始化库存</el-button><el-button type="primary" @click="initSkuInfo('price')">初始化价格</el-button></el-button-group><!-- 用折叠面板显示各种SKU的数据 --><el-collapse style="width: 100%"v-model="state.auxiliaries.optional.attr.sku.dynamicDetailCollapaseActiveNames"><el-collapse-item :title="genCollapseTitle(dDetailItemV)":name="genCollapseTitle(dDetailItemV)"v-for="(dDetailItemV, dDetailItemK) in state.auxiliaries.optional.attr.sku.dynamicDetailItems":key="dDetailItemK"><!-- 销售标题 --><el-form-item label="销售标题" label-width="120"><el-input v-model="state.form.skuDetails[dDetailItemK].title"></el-input></el-form-item><el-divider border-style="none"></el-divider><el-form-item label="SKU简介" label-width="120"><el-input v-model="state.form.skuDetails[dDetailItemK].summary"></el-input></el-form-item><el-divider border-style="none"></el-divider><!-- 商品轮播图 --><el-form-item label="商品轮播图" label-width="120"><el-uploadv-model:file-list="state.auxiliaries.configs.upload.coversForm[dDetailItemK].covers"action="http://localhost:9900/upload/cover" multiple:headers="state.auxiliaries.configs.upload.headers":on-success="handleUploadCoverSuccess":on-error="handleUploadCoverFailed" method="post"list-type="picture-card" :on-preview="handlePictureCardPreview":on-remove="handleRemove"><el-icon><Plus /></el-icon></el-upload></el-form-item><el-divider border-style="none"></el-divider><!-- 价格 --><el-form-item label="价格" label-width="120"><el-input type="number"v-model="state.form.skuDetails[dDetailItemK].price"></el-input></el-form-item><el-divider border-style="none"></el-divider><!-- 库存 --><el-form-item label="库存" label-width="120"><el-input type="number"v-model="state.form.skuDetails[dDetailItemK].stock"></el-input></el-form-item><el-divider border-style="none"></el-divider><!-- 商详 --><el-form-item label="商品详情" label-width="120"><!-- 用tinymce富文本编辑器 --><Editor ref="skuDetailRef" :init="state.auxiliaries.configs.editor.configs"initial-value="Welcome to TinyMCE!"></Editor></el-form-item><el-divider border-style="none"></el-divider></el-collapse-item></el-collapse></el-form-item><el-form-item><el-button type="primary" @click="submitForm">提交</el-button><el-button>重置</el-button></el-form-item></el-tab-pane></el-tabs></el-form><el-dialog v-model="state.auxiliaries.optional.attr.sku.imgDialog.visiable"><img w-full :src="state.auxiliaries.optional.attr.sku.imgDialog.url" alt="Preview Image" /></el-dialog></el-card></el-col></el-row>
</template>
<script setup lang="ts">
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<any>}},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].namestate.value.auxiliaries.configs.step.active += 1
}const handleTabChange = (currentTabName: string) => {for (let i = 0; i < state.value.auxiliaries.configs.step.stepTab.length; i++) {if (state.value.auxiliaries.configs.step.stepTab[i].name === currentTabName) {state.value.auxiliaries.configs.step.active = state.value.auxiliaries.configs.step.stepTab[i].step}}
}
// 动态生成SPU条目
const addSpuItem = () => {if (state.value.auxiliaries.optional.attr.spu.dynamic === 0) {ElMessage.error("请选择一个属性")return}// 已选择的SPU属性for (let i = 0; i < state.value.auxiliaries.optional.attr.list.length; i++) {if (state.value.auxiliaries.optional.attr.list[i].attrId === state.value.auxiliaries.optional.attr.spu.dynamic) {let dynamicItem = JSON.parse(JSON.stringify(state.value.auxiliaries.optional.attr.list[i]))dynamicItem.selectedValue = []state.value.auxiliaries.optional.attr.spu.dynamicItems.push(dynamicItem)}}
}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条目
const addSkuItem = () => {if (state.value.auxiliaries.optional.attr.sku.dynamic === 0) {ElMessage.error("请选择一个属性")return}// 已选择的sku属性for (let i = 0; i < state.value.auxiliaries.optional.attr.list.length; i++) {if (state.value.auxiliaries.optional.attr.list[i].attrId === state.value.auxiliaries.optional.attr.sku.dynamic) {let dynamicItem = JSON.parse(JSON.stringify(state.value.auxiliaries.optional.attr.list[i]))// SKU的值全部多选,然后做排列组合dynamicItem.selectedValue = []state.value.auxiliaries.optional.attr.sku.dynamicItems.push(dynamicItem)}}
}// 根据选择的SKU属性值,按排列组合生成SKU条目
// 递归比较适合处理这种问题
const genSkuItems = () => {// 清空已有的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 < skus.length; i++) {// 遍历属性选择的值if (tempSkuItems.length <= 0) {for (let j = 0; j < skus[i].selectedValue.length; j++) {tempSkuItems.push([{attrId: skus[i].attrId,name: skus[i].name,selectedValue: [skus[i].selectedValue[j]]}])}} else {let iterTempSkuItems = []for (let j = 0; j < skus[i].selectedValue.length; j++) {// 判断临时组合是否存在for (let k = 0; k < tempSkuItems.length; k++) {let iterTempSkuItem = JSON.parse(JSON.stringify(tempSkuItems[k]))iterTempSkuItem.push({attrId: skus[i].attrId,name: skus[i].name,selectedValue: [skus[i].selectedValue[j]]})iterTempSkuItems.push(iterTempSkuItem)}}// 让最终的排列组合临时变量接管,遍历中的临时变量tempSkuItems = JSON.parse(JSON.stringify(iterTempSkuItems))}}// 为表单的SKU生成对应数量的SKU表单字段console.log("covers form ", state.value.auxiliaries.configs.upload.coversForm)for (let k = 0; k < tempSkuItems.length; k++) {state.value.form.skuDetails.push({id: null,sku: JSON.parse(JSON.stringify(tempSkuItems[k])),title: "",summary: "",price: 0,stock: 0,covers: [],detail: ""})state.value.auxiliaries.configs.upload.coversForm.push({covers: []})}state.value.auxiliaries.optional.attr.sku.dynamicDetailItems = JSON.parse(JSON.stringify(tempSkuItems))console.log("covers form ", state.value.auxiliaries.configs.upload.coversForm)console.log("sku detail form ", state.value.form.skuDetails)
}const initSkuInfo = (type: string) => {// 用确认框的方式进行交互let msgBoxTitle = ""switch (type) {case "title":msgBoxTitle = "初始化销售标题"breakcase "summary":msgBoxTitle = "初始化SKU简介"breakcase "stock":msgBoxTitle = "初始化库存"breakcase "price":msgBoxTitle = "初始化价格"breakdefault:msgBoxTitle = "-"}ElMessageBox.prompt(msgBoxTitle, '初始化sku数据', {confirmButtonText: '提交',cancelButtonText: '取消',}).then(({ value }) => {// 将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 < state.value.form.skuDetails.length; i++) {let skuTitle = ""for (let j = 0; j < state.value.form.skuDetails[i].sku.length; j++) {skuTitle += " " + state.value.form.skuDetails[i].sku[j].selectedValue.join("")}state.value.form.skuDetails[i].title = titlePrefix + skuTitle}
}const initSummary = (summary: string) => {for (let i = 0; i < state.value.form.skuDetails.length; i++) {state.value.form.skuDetails[i].summary = summary}
}const initStock = (stock: number) => {for (let i = 0; i < state.value.form.skuDetails.length; i++) {state.value.form.skuDetails[i].stock = stock}
}const initPrice = (price: number) => {for (let i = 0; i < state.value.form.skuDetails.length; i++) {state.value.form.skuDetails[i].price = price}
}const uploadImg = (uploadForm: FormData) => {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<any>): string => {let tempTitle = ""for (let n = 0; n < tempSkuItems.length; n++) {tempTitle += " " + tempSkuItems[n].selectedValue[0]}return tempTitle
}const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {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// 遍历skufor (let i = 0; i < state.value.auxiliaries.configs.upload.coversForm.length; i++) {// 遍历sku的轮播图let skuCovers = []for (let j = 0; j < state.value.auxiliaries.configs.upload.coversForm[i].covers.length; j++) {// 提取轮播图urllet coverRs = JSON.parse(JSON.stringify(state.value.auxiliaries.configs.upload.coversForm[i].covers[j].response))skuCovers.push(coverRs.data)}// 赋值到对应的sku的coversstate.value.form.skuDetails[i].covers = JSON.parse(JSON.stringify(skuCovers))}// 将sku的详情追加到formfor (let skuIdx = 0; skuIdx < skuDetailRef.value.length; skuIdx++) {state.value.form.skuDetails[skuIdx].detail = skuDetailRef.value[skuIdx].getEditor().getContent()}// 将sku选择的值追加到formstate.value.form.sku = JSON.parse(JSON.stringify(state.value.auxiliaries.optional.attr.sku.dynamicItems))// 将SPU选择的值追加到formstate.value.form.spu = JSON.parse(JSON.stringify(state.value.auxiliaries.optional.attr.spu.dynamicItems))// 特殊处理spu的值,值都认为是多选的for (let b = 0; b < state.value.form.spu.length; b++) {if (!(state.value.form.spu[b].selectedValue instanceof Object)) {state.value.form.spu[b].selectedValue = [state.value.form.spu[b].selectedValue]}}console.log("submit form: ", state.value.form)afterSubmit()
}const afterSubmit = () => {// 重置表单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 = []
}
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/374418.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

曝宝马汽车门店亏损严重价格战带来的伤害太大了

今年以来不仅餐饮行业难,就连一些车企都陷入困境当中,多家车企选择打价 格战。只不过日前的时候媒体爆料称,宝马汽车门店因为打价格战,最终亏损严 重,为了避免亏损再度出现,因此宝马7月将会开始降量保价。文章来源于&#xff1a;股城网www.gucheng.com 实际上,进入2024年…

如何在 C 语言中进行选择排序?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; &#x1f4d9;C 语言百万年薪修炼课程 通俗易懂&#xff0c;深入浅出&#xff0c;匠心打磨&#xff0c;死磕细节&#xff0c;6年迭代&#xff0c;看过的人都说好。 文章目…

B站大课堂-自动化精品视频(个人存档)

基础知识 工业通信协议 Modbus 施耐德研发&#xff0c;有基于以太网的 ModbusTCP 协议和使用 485/232 串口通信的 ModbusRTU/ASCII。 Modbus 协议面世较早、协议简洁高效、商用免费、功能灵活、实现简单&#xff0c;是目前应用最广泛的现场总线协议。 我的笔记里边有一些推荐…

本地开发微信小程序,使用巴比达内网穿透

在微信小程序开发的热潮中&#xff0c;开发者常面临的一个挑战是如何在复杂的网络环境下测试和调试内网环境中的服务。巴比达正为这一难题提供了一条解决方案&#xff0c;极大简化了微信小程序与内网服务器之间通信的流程&#xff0c;加速了开发迭代周期。 以往&#xff0c;开…

关于力反馈设备应用方向的探讨

力反馈是在虚拟现实 (VR)等模拟环境中通过机动运动或阻力模拟真实世界的物理触觉。大多数人都是通过视频游戏控制器&#xff08;如方向盘或踏板&#xff09;和其他设备&#xff08;如飞行模拟器操纵杆&#xff09;来了解力反馈效果。但我们都知道该技术的用途远不止于游戏。 触…

Golang语法规范和风格指南(一)——简单指南

1. 前引 一个语言的规范的学习是重要的&#xff0c;直接关系到你的代码是否易于维护和理解&#xff0c;同时学习好对应的语言规范可以在前期学习阶段有效规避该语言语法和未知编程风格的冲突。 这里是 Google 提供的规范&#xff0c;有助于大家在开始学习阶段对 Golang 进行一…

基于 BERT 的非结构化领域文本知识抽取

文章目录 题目摘要方法实验 题目 食品测试的大型语言模型 论文地址&#xff1a;https://arxiv.org/abs/2103.00728 摘要 随着知识图谱技术的发展和商业应用的普及&#xff0c;从各类非结构化领域文本中提取出知识图谱实体及关系数据的需求日益增加。这使得针对领域文本的自动化…

【UE5】调用ASR接口,录制系统输出。录制音频采样率不匹配

暂时测出window能用。阿里的ASR接口当前仅支持8000和16000。UE默认采样44100。

MES系统助力塑料制品行业数字化转型

注塑MES系统助力工厂生产力提升具体体现在&#xff1a;覆盖生产全流程&#xff1b;数据自动收集、科学规划排产&#xff1b;优化配送模型、平衡物流运转&#xff1b;严格把控品质、异常自动分析&#xff1b;实时监控设备&#xff0c;保证正常运转&#xff1b;产品快速追溯&…

教学神器大比拼:SmartEDA、Multisim、Proteus,谁是你的最佳选择?

随着科技的飞速发展&#xff0c;教学工具也在不断升级。在电子设计自动化&#xff08;EDA&#xff09;和电路仿真领域&#xff0c;SmartEDA、Multisim和Proteus三款软件备受关注。那么&#xff0c;对于广大教育工作者和学生们来说&#xff0c;这三者之间该如何选择呢&#xff1…

AI绘画之儿童绘本制作变现途径(附详细教程)

AI技术飞速发展&#xff0c;创作儿童绘本或是故事书已经不再是专业插画师和作家的专利。在AI技术的介入下&#xff0c;为那些有创意但缺乏绘画技巧的人们打开了一扇新的大门。通过AI工具&#xff0c;我们可以轻松地创作出既有趣又富有教育意义的儿童故事书&#xff0c;并通过多…

LLMs可以进行任务规划吗?如果不行,LLMs+GNN可以吗?

深度图学习与大模型LLM(小编): 大家好,今天向大家介绍一篇最新发布的研究论文&#xff08;20240530&#xff09;。这篇论文探讨了如何通过引入GNN来提高大模型在任务规划(task planning)中的性能。*论文分析了LLMs在任务规划上的局限性,并提出了一种简单而有效的解决方案。* 1.…

@RequiredArgsConstructor实现构造器注入

RequiredArgsConstructor实现构造器注入 1. Autowired 和 Resource 注解 Autowired Autowired 是 Spring 框架提供的注解&#xff0c;用于自动装配依赖。可以用于字段、构造函数和 setter 方法。 Autowired private ISysUserService userService;Resource Resource 是 Jav…

python接口自动化(二十一)--unittest简介(详解)

宏哥微信粉丝群&#xff1a;https://bbs.csdn.net/topics/618423372 有兴趣的可以扫码加入 简介 前边的随笔主要介绍的requests模块的有关知识个内容&#xff0c;接下来看一下python的单元测试框架unittest。熟悉 或者了解java 的小伙伴应该都清楚常见的单元测试框架 Junit 和…

广州旭之源模块电源PIN TO PIN替换金升阳

广州旭之源科技有限公司&#xff0c;创立ATAZ工业电源品牌&#xff0c;是一家集研发、生产、销售和服务于一体的标准工业电源解决方案提供商。 以电力电子和自动化控制为核心技术&#xff0c;产品涵盖了壳架式、模块式、导轨式等工业电源。所生产的产品广泛应用于工业控制、电力…

AIGC产品经理学习路径

基础篇&#xff08;课时 2 &#xff09; AIGC 行业视角 AIGC 的行业发展演进&#xff1a;传统模型/深度学习/大模型 AIGC 的产品设计演进&#xff1a;AI Embedded / AI Copilot / AI Agen AIGC 的行业产业全景图 AIGC 的产品应用全景图 AIGC 职业视角 AI 产品经理/ AIGC…

vue3中antd上传图片组件及回显

实现效果&#xff1a; 调用后端接口后&#xff0c;后端返回的数据&#xff1a; 1.在项目components/base下新建UploadNew.vue文件&#xff08;上传图片公共组件&#xff09; <template><div class"clearfix"><a-uploadv-model:file-list"fileL…

视频汇聚平台EasyCVR设备录像回看请求播放时间和实际时间对不上,是何原因?

安防监控EasyCVR视频汇聚平台可提供多协议&#xff08;RTSP/RTMP/国标GB28181/GAT1400/海康Ehome/大华/海康/宇视等SDK&#xff09;的设备接入、音视频采集、视频转码、处理、分发等服务&#xff0c;系统具备实时监控、云端录像、回看、告警、平台级联以及多视频流格式分发等视…

【自监督学习】DINO in ICCV 2021

一、引言 论文&#xff1a; DINO: Emerging Properties in Self-Supervised Vision Transformers 作者&#xff1a; Facebook AI Research 代码&#xff1a; DINO 特点&#xff1a; 对于一张图片&#xff0c;该方法首先进行全局和局部的裁剪与增强并分别送入教师和学生网络&am…

关于.NETCORE站点程序部署到nginx上无法访问静态文件和无法正确生成文件的问题解决过程。

我的netcore6项目&#xff0c;部署到IIS的时候&#xff0c;生成报告时&#xff0c;需要获取公司LOGO图片放到PDF报告文件中&#xff0c;这时候访问静态图片没有问题。 然后还有生成邀请二维码图片&#xff0c;这时候动态创建图片路径和图片也没有问题&#xff0c;可以在站点的…