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.pswp.cn/web/43743.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/43743.shtml
英文地址,請注明出處:http://en.pswp.cn/web/43743.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

字符串s構造前綴樹,并判斷p是否屬于s的子串

文章目錄 1、描述2、notes3、code 1、描述 根據幾個單詞&#xff0c;構造一個前綴樹&#xff0c;再給定一個單詞p,判斷p是否屬于s的前綴 輸入&#xff1a;vec {“hello”, “world”, “hey”, “hi”} p “hell” 輸入&#xff1a;yes 2、notes 就直接構造 3、code #in…

編程語言如何和計算機交互:深入解析交互機制

編程語言如何和計算機交互&#xff1a;深入解析交互機制 在數字化世界的深處&#xff0c;編程語言與計算機之間的交互是構建數字邏輯、實現功能需求的基石。這一過程既充滿神秘&#xff0c;又充滿力量。那么&#xff0c;編程語言究竟是如何與計算機進行交互的呢&#xff1f;本…

好的管理是什么樣子的?放權與監督

背景 身份&#xff1a;一線管理干部&#xff08;組長、基層部門負責人&#xff09;目標&#xff1a;部門承接的任務能夠按期高質量完成&#xff1b;在80%以上的時間里&#xff0c;部門所有成員知道自己要做什么&#xff0c;如何做好 措施 帶團隊已經有幾年時間了&#xff0c…

行為模式8.狀態模式------燈泡狀態切換

行為型模式 模板方法模式&#xff08;Template Method Pattern&#xff09;命令模式&#xff08;Command Pattern&#xff09;迭代器模式&#xff08;Iterator Pattern&#xff09;觀察者模式&#xff08;Observer Pattern&#xff09;中介者模式&#xff08;Mediator Pattern…

融合CDN是什么?為什么需要融合CDN?其應用方法與原理是什么?

你了解融合CDN是什么嗎&#xff1f;為什么需要融合CDN&#xff1f;你可能有聽過融合CDN&#xff0c;但你知道它的應用方法與原理嗎&#xff1f;本文將帶你一次了解什么是融合CDN&#xff0c;詳細介紹融合CDN的應用方法與運用原理&#xff0c;立刻替您解開心中疑惑&#xff01; …

【Qt】xml Dom復制

1. 功能 將A.xml文件中的copyNode節點全部復制到B.xml中的testRoot節點。 2. 代碼 #include <QDomDocument> #include <QFile> #include <QIODevice> #include <QtXml>void copyNodeXml() {// 源文件DOMQDomDocument ADoc;// 加載源文件QFile fileA(…

[微信小程序知識點]自定義組件-拓展-外部樣式類

使用組件時&#xff0c;組件使用者可以給組件傳入css類名&#xff0c;通過傳入的類名修改組件的樣式 。 如果需要使用外部樣式類修改組件的樣式&#xff0c;在Component中需要用extemalClassess定義若干個外部樣式類。 具體用法如下: (1)在Components文件里創建custom06組件 (…

EtherCAT ESI文件CRC32計算規則和方法

EtherCAT ESI文件CRC32計算規則和方法 EtherCAT ESI文件的CRC32計算遵循特定的規則&#xff0c;以確保設備描述的完整性。以下是詳細的規則和計算步驟&#xff0c;以及C#實現示例&#xff1a; 計算規則 使用標準的CRC32多項式&#xff1a;0x04C11DB7初始值&#xff1a;0xFFF…

Python實現文件訪問和加密GUI應用程序

Python實現文件訪問和加密 簡單的文本文件加密和解密的GUI應用程序&#xff0c;實現了一個簡單的凱撒密碼加密和解密算法 運行效果 1.實現UI界面 [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-yG28ajb1-1720676735133)(https://i-blog.csdnimg.…

免費SSL證書申請指南

申請免費SSL證書的步驟相對直接&#xff0c;以下是基于當前可用信息的簡明指南&#xff0c;特別是針對一些熱門的免費SSL證書提供商&#xff0c;下面以JoySSL證書商為例&#xff1a; 1、注冊賬號 打開JoySSL官網&#xff0c;注冊并填寫邀請碼230920&#xff0c;獲取免費證書與…

RK系列UST-OTG切換為HOST模式或DEVICE模式的兩種方法(DTS修改和軟件命令修改)

1、修改DTS dr_mode: tells Dual-Role USB controllers that we want to work on a particular mode. Valid arguments are “host”, “peripheral” and “otg”. In case this attribute isn’t passed via DT, USB DRD controllers should default to OTG. usb20_otg: usb…

淺談三車平臺車型對比功能實用獎-競品分析

目錄&#xff1a; 一、項目背景 二、競品概述 三、競品目標功能對比 3.1、車型對比入口位置 3.2、車型對比首頁 3.3、添加/刪除車型功能 3.4、選擇車型后功能對比 3.5、配置對比的功能 四、總結 一、項目背景 在汽車購買過程中&#xff0c;消費者經常面臨著選擇困難&…

六、數據可視化—Echars(爬蟲及數據可視化)

六、數據可視化—Echars&#xff08;爬蟲及數據可視化&#xff09; Echarts應用 Echarts Echarts官網&#xff0c;很多圖表等都是我們可以 https://echarts.apache.org/zh/index.html 是百度自己做的圖表&#xff0c;后來用的人越來越多&#xff0c;捐給了orange組織&#xf…

【好生意】暢捷通好生意各版本之間的區別

【暢捷通好生意各版本區別】 隨著產品線的增加&#xff0c;不同版本之間存在差異。 以下是針對自己使用、研究過程中的記錄。 完善ing 功能普及版標準版采購運費分攤沒有單獨的采購費用分攤單&#xff0c;但是支持隨單分攤。支持

企業如何挑選策劃公司,這些標準你了解嗎?

誠然&#xff0c;在這個競爭激烈的市場環境下&#xff0c;企業有時候就像是站在十字路口的旅人&#xff0c;面前擺著的是一條條花錢卻未必能看見收益的道路。 這時候&#xff0c;找一家對的策劃公司就很重要&#xff0c;這里分享一點個人多年經驗&#xff0c;希望對你有所幫助…

【精簡教程】VSCode 連接 Remix

初始化 Node.js 項目 yarn init v1.22.19安裝 Remix yarn add remix-project/remixd -g?? 此時如果直接敲 remix&#xff0c;顯示找不到這個命令。 使用 Node.js 來直接執行 remixd.js 文件 node node_modules\remix-project\remixd\src\bin\remixd.js&#x1f604; 連接上了…

安全極客團隊榮獲首屆“矩陣杯”網絡安全大賽人工智能挑戰賽“三等獎”

近日&#xff0c;東半球規格高、規模大且獎金豐厚的網絡安全頂級賽事——首屆“矩陣杯”網絡安全大賽在青島國際會議中心圓滿落幕。本次大賽設置了五大賽事&#xff0c;包括通用產品漏挖賽、國產軟硬件安全檢測賽、原創漏洞挖掘賽、人工智能&#xff08;大模型&#xff09;挑戰…

【Linux】Windows平臺使用gdb調試FFmpeg源碼

FFmpeg是一個跨平臺的多媒體庫&#xff0c;有時需要在別的平臺上進行開發和調試&#xff0c;記錄一下在linux環境下使用gdb來調試FFmpeg源碼的基本方式 1.可執行文件 在windows平臺使用linux環境來調試FFmpeg源碼&#xff0c;需要編譯生成一個后綴有_g的exe文件&#xff0c;參…

HTTP中常見的狀態碼有哪些?

常用的包括以下幾個&#xff1a; 200&#xff1a;表示客戶端請求成功 201&#xff1a;請求成功,服務器創建了新資源。 204&#xff1a;無內容&#xff0c;服務器成功處理請求&#xff0c;但未返回任何內容。 206: 表示“部分內容”,當客戶端請求一個資源的一部分時&#xff0c;…

YOLOv10部署教程,使用tensorRT部署,有轉化和推理代碼

YOLOv10部署教程,使用tensorRT部署,有轉化和推理代碼 一、使用平臺1. 轉化onnx模型轉化trt模型模型推理全部的代碼論文題目:YOLOv10: Real-Time End-to-End Object Detection 研究單位:清華大學 論文鏈接:http://arxiv.org/abs/2405.14458 代碼鏈接:https://github.com/T…