Vue 实战自定义组件,可以删除和增加

RecommendFunction.vue

javascript 复制代码
<template>
    <div>
        <div v-for="(item, index) in dataList" :key="index" class="data-item">
            <div class="group-row">
                <el-form ref="item" :model="item" label-width="100px" :rules="rules" label-position="top"
                    style="width: 90%">
                    <el-form-item label="介绍" prop="title">
                        <el-input v-model="item.title" placeholder="请输入介绍" />
                    </el-form-item>
                    <el-form-item label="品类" prop="fruitCatagory" style="margin-top: 10px;">
                        <div style="display: flex; align-items: center;">
                            <el-select v-model="item.fruitCatagory" placeholder="请选择" clearable
                                style="width: 200px; margin-right: 10px;" @change="handleNoRepeat">
                                <el-option v-for="option in getAvailableFruitCatagoryOptions(index)" :key="option.value"
                                    :label="option.label" :value="option.value" />
                            </el-select>
                            <el-input v-model="item.fruitDescription" placeholder="请输入详情" />
                        </div>
                    </el-form-item>
                    <el-form-item label="价格" prop="price" style="margin-top: 10px;">
                        <el-input v-model="item.price" placeholder="请输入价格" type="number" min="0" max="100" />
                    </el-form-item>
                </el-form>
                <el-button icon="el-icon-delete" type="text" style="margin-left: 10px;" @click="deleteItem(index)" />
            </div>
        </div>
        <div v-if="haveAbleChoice" class="data-btn" @click="addItem">
            <i class="el-icon-plus" style="font-weight: bold; margin-right:5px;" />增加组件
        </div>
    </div>
</template>

<script>
export default {
    name: 'RecommendationConfig',
    props: {
        fruitOptions: {
            type: Array,
            required: true,
            default: () => []
        },
        dataList: {
            type: Array,
            required: true
        },
    },
    data() {
        return {
            itemStruct: {
                title: '',
                fruitCatagory: '',
                fruitDescription: '',
                price: 50
            },
            rules: {
                title: [{ required: true, message: '此项为必填', trigger: 'blur' }],
                fruitCatagory: [{ required: true, message: '此项为必填', trigger: 'blur' }],
                price: [{ required: true, message: '此项为必填', trigger: 'blur' }]
            }
        }
    },
    computed: {
        haveAbleChoice() {
            const selectedTypes = this.dataList.map(item => item.fruitCatagory);
            return this.fruitOptions.some(option => !selectedTypes.includes(option.value));
        }
    },
    methods: {
        /* eslint-disable */
        getAvailableFruitCatagoryOptions(index) {
            const selectedTypes = this.dataList
                .map((item, idx) => (idx !== index ? item.fruitCatagory : null))
                .filter(type => type !== null);
            return this.fruitOptions.filter(option => !selectedTypes.includes(option.value));
        },
        handleNoRepeat() {
            this.$forceUpdate();
        },
        addItem() {
            const selectedTypes = this.dataList.map(item => item.fruitCatagory);
            const firstAvailableType = this.fruitOptions.find(option => !selectedTypes.includes(option.value));
            if (firstAvailableType) {
                const itemData = JSON.parse(JSON.stringify(this.itemStruct));
                itemData.fruitCatagory = firstAvailableType.value;
                this.dataList.push(itemData);
            }
        },
        deleteItem(index) {
            this.dataList.splice(index, 1);
        }
    }
}
</script>

<style scoped>
.data-btn {
    width: 140px;
    height: 32px;
    display: flex;
    justify-content: center;
    align-items: center;
    border: 1px dashed #d9d9d9;
    background: #ffffff;
    padding: 4px 15px;
}

.data-btn:hover {
    cursor: pointer;
    color: #409EFF;
    border: 1px dashed #409EFF;
}

.data-btn:active {
    /* background: #f2f0f7; */
    background: #e3f5f2;
}

.data-item {
    margin: 0 0 24px;
    background-color: #e3f5f2;
    padding: 16px;
    border-radius: 8px;
}

.group-row {
    display: flex;
    flex-direction: row;
    align-items: end;
}
</style>

父组件引用:

javascript 复制代码
 <RecommendFunction :data-list="form.dataList" :fruit-options="form.fruitOptions" />

export default {
  components: { RecommendFunction },
  data() {
    return {
      form: {
        fruitOptions: [
          { label: "苹果", value: "0" },
          { label: "香蕉", value: "1" }
        ],
        dataList: []
      },
    }
   }
}
相关推荐
真的很上进3 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203983 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
supermapsupport5 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap
m0_748254885 小时前
vue+elementui实现下拉表格多选+搜索+分页+回显+全选2.0
前端·vue.js·elementui
噢,我明白了6 小时前
同源策略:为什么XMLHttpRequest不能跨域请求资源?
javascript·跨域
sanguine__6 小时前
APIs-day2
javascript·css·css3
苹果醋36 小时前
Golang的文件加密工具
运维·vue.js·spring boot·nginx·课程设计
关你西红柿子7 小时前
小程序app封装公用顶部筛选区uv-drop-down
前端·javascript·vue.js·小程序·uv
济南小草根7 小时前
把一个Vue项目的页面打包后再另一个项目中使用
前端·javascript·vue.js
小木_.7 小时前
【python 逆向分析某有道翻译】分析有道翻译公开的密文内容,webpack类型,全程扣代码,最后实现接口调用翻译,仅供学习参考
javascript·python·学习·webpack·分享·逆向分析