表单选项互斥问题(vue)

本文介绍了一个使用Vue实现的乒乓球放入不同盒子的示例,展示了如何在多个多选框中实现选项互斥功能,即一个选项一旦被选择,就无法在其他多选框中再次选择。
最近有一个需求:
表单中有3个多选框,而他们的选项是一样的,但是如果其中一个选项被选择之后,在另外2个多选框里面就不能再选了。
这样的问题,让我想到了“将乒乓球放入不同盒子”的例子。

上代码

// index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <meta http-equiv="X-UA-Compatible" content="ie=edge" />
        <title>Document</title>

        <link rel="stylesheet" href="./styles.css" />
        <script src="https://cdn.bootcss.com/vue/2.6.5/vue.min.js"></script>
    </head>
    <body>
        <div id="app">
            <p>乒乓球放入不同的盒子(选项互斥)</p>
            <div class="ping-pong-list">
                <span
                    v-for="ball in ballList"
                    :key="ball.id"
                    :class="ball.className"
                    @click="onBallClick(ball.id)"
                    v-text="ball.id"
                ></span>
            </div>

            <div class="box-wrapper">
                <div
                    v-for="(category,index) in categories"
                    :key="index"
                    :class="['box', category, currCategory === category ? 'active' : null]"
                    @click="onBoxClick(category)"
                >
                    <span
                        v-for="ball in ballList"
                        :key="ball.id"
                        :class="ball.className"
                        v-show="ball.category === category"
                        v-text="ball.id"
                    ></span>
                </div>
            </div>
        </div>

        <script src="./main.js"></script>
    </body>
</html>
// main.js

const balls = Array(12)
    .fill(undefined)
    .map((v, i) => ({ id: i < 9 ? `0${i + 1}` : i + 1, category: null }))

const vm = new window.Vue({
    el: "#app",

    data: () => ({
        msg: "hahahhaha",
        balls,
        categories: ["red", "green", "blue"],
        currCategory: "red"
    }),

    computed: {
        ballList() {
            return this.balls.map(v => {
                let className = ["ping-pong-ball"]
                if (v.category) {
                    className.push(v.category)
                    if (v.category !== this.currCategory) {
                        className.push("disable")
                    }
                }
                return { ...v, className }
            })
        }
    },

    methods: {
        onBoxClick(category) {
            if (category !== this.currCategory) {
                this.currCategory = category
            }
        },

        onBallClick(id) {
            const ball = this.balls.find(v => v.id === id)
            if (ball) {
                ball.category = ball.category ? null : this.currCategory
            }
        }
    }
})
// styles.css

#app {
    user-select: none;
}

.ping-pong-list {
    display: flex;
    margin: 20px;
    justify-content: center;
    flex-wrap: wrap;
}

.ping-pong-ball {
    width: 50px;
    height: 50px;
    margin: 5px;
    border-radius: 50%;
    box-shadow: 0 0 10px 0 #aaa;
    text-align: center;
    line-height: 50px;
}

.ping-pong-list .ping-pong-ball {
    cursor: pointer;
}

.box-wrapper {
    display: flex;
    justify-content: space-around;
    margin-top: 30px;
}

.box {
    display: flex;
    align-content: flex-start;
    flex-wrap: wrap-reverse;
    width: 300px;
    height: 200px;
    border: 10px solid;
    border-top-width: 0;
    cursor: pointer;
    transition: all 0.25s;
}

.box.red {
    border-color: rgb(238, 97, 97);
}

.box.green {
    border-color: rgb(97, 238, 156);
}

.box.blue {
    border-color: rgb(97, 146, 238);
}

.box.active {
    box-shadow: 0 10px 20px 0 #aaa;
}

.ping-pong-ball.red,
.box.red .ping-pong-ball {
    background-color: rgb(238, 97, 97);
}

.ping-pong-ball.green,
.box.green .ping-pong-ball {
    background-color: rgb(97, 238, 156);
}

.ping-pong-ball.blue,
.box.blue .ping-pong-ball {
    background-color: rgb(97, 146, 238);
}

.ping-pong-ball.disable {
    opacity: 0.25;
    pointer-events: none;
}

每个ball对象都有一个category属性,用来表示它属于哪个盒子。然后在渲染的时候,根据category来计算使用的类名。

### Vue 表单输入与数据绑定方法 在 Vue.js 中,`v-model` 是用于表单控件和应用状态之间创建双向数据绑定的核心指令[^1]。当用户通过表单交互时,Vue 实例中的对应数据会自动同步更新;同样地,如果更改了 Vue 实例的数据,表单元素的状态也会相应改变。 #### 文本框的绑定 对于简单的文本输入框,可以通过 `v-model` 将其值绑定到 Vue 实例的一个属性上: ```html <div id="app"> <input type="text" v-model="message" placeholder="请输入内容"> <p>您输入的内容是: {{ message }}</p> </div> <script> new Vue({ el: '#app', data: { message: '' } }); </script> ``` 上述代码展示了如何利用 `v-model` 绑定一个文本框至 Vue 的实例变量 `message` 上。每当用户修改输入框内的文字时,`message` 值都会实时更新并反映在页面上的 `<p>` 标签中。 #### 复选框 (Checkbox) 和 单选按钮 (Radio) 复选框通常用来表示多个可选项的选择情况,而单选按钮则是从一组互斥选项中选取唯一的一项。两者的 `v-model` 使用方式有所不同: - **复选框**: 如果有多个复选框,则它们共享同一个数组类型的模型值。 ```html <div id="app"> <label><input type="checkbox" value="JavaScript" v-model="languages"> JavaScript</label> <label><input type="checkbox" value="Python" v-model="languages"> Python</label> <label><input type="checkbox" value="C++" v-model="languages"> C++</label> <br/> 您选择了: {{ languages }} </div> <script> new Vue({ el: '#app', data: { languages: [] } }); </script> ``` 这里定义了一个名为 `languages` 的数组来存储被勾选的语言名称列表[^2]。 - **单选按钮**: 对于单选按钮组来说,所有的成员都应具有相同的 `name` 属性以及不同的 `value` 值,并且这些按钮共用一个字符串形式的模型值。 ```html <div id="app"> <label><input type="radio" name="language" value="JavaScript" v-model="picked"> JavaScript</label> <label><input type="radio" name="language" value="Python" v-model="picked"> Python</label> <label><input type="radio" name="language" value="C++" v-model="picked"> C++</label> <br/> 您选择的是: {{ picked }} </div> <script> new Vue({ el: '#app', data: { picked: 'JavaScript' } }); </script> ``` 在这个例子中,无论哪个单选按钮被点击,`picked` 都会被设置成该按钮关联的值。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值