vue案例 - v-model实现自定义样式の多选与单选
接,上文:https://www.cnblogs.com/padding1015/p/9265985.html
这两天在玩mpvue,但是下午如果对着文档大眼瞪小眼的话,肯定会睡着的。
想起昨晚的flag,我就想直接用demo上手吧,一举两得
谁想到我好不容易快做完了,v-model在小程序中不起作用!
来不及研究为什么,我先直接在原来项目上赶紧建了一个test页面,先赶紧实现我的这种设想:
使用v-model和原生表单也可以实现这么好看且达到需求的效果。
重要的是不用自己跟在用户屁股后面屁颠屁颠的监听人家到底何时用了点击事件,又把点击事件用在何处了!
效果图如下,和之前的没什么两样呢!
具体实现我想,vue官网有关于表单输入绑定的讲解和demo,事实上,我只要做到利用他的demo把我的数据和样式调整一下就万事大吉了!
没有什么比简单解决一个功能更让人开心的了!
说干就干,我直接在原来项目代码的基础上动手:
之前的选项处理就一个li孤军奋战,数据渲染、样式切换、包括点击事件都绑定在上边,
ul.qus-list
li(v-for="(item,index) in state.ExamInfo.QuestionAnswerCode" @click="choosed(index)" v-bind:class="{'li-focus' : chooseNum==index}" ref="liId") {{item.Code}}、{{item.Description}}
简直忙到没朋友啊有没有!光他和ul的长度差距就说明了一切!
现在我们把他要做的事分解一下:
现在他只负责v-for循环数据渲染
ul.qus-list
li(v-for="(item,index) in state.ExamInfo.QuestionAnswerCode" v-bind:class="{'li-focus' : chooseNum==index}")
内部分配给他两个小弟
input:radio/checkbox和label,这俩人一个负责点击后与数据的绑定,一个负责样式。这么一说大神就明了了,好你可以走了,把沙发腾出来。
这俩人中,Input负责数据绑定,其实也就是利用v-model。具体原理直接看https://cn.vuejs.org/v2/guide/forms.html
input( type="radio" :value="item.Code" :id="'choice1'+index" v-model="picked")
然后时label负责样式。样式也包括用户看到的选项文本的展示:
label(:for="'choice1'+index" class="choice-item") {{item.Code}}、{{item.Description}}
至于他具体怎么负责样式?这个也利用了css的选择器
主要是:checked选择器和+相邻兄弟选择器
/*普通样式*/
.choice-item{
display: block;
margin: .2rem auto 0;
padding: .3rem .3rem .34rem;
color: $qusTxt;
font-size: .34rem;
text-align: center;
@include boxStyle(1rem,.12rem,rgba(49,32,114,0.16));
}
/*input被选中时,label的样式*/
input:checked + .choice-item{
background: $purpleClr;
color: #FFF;
}
于是就有了这样的样式:
这里可以看出,二者是相互成就的关系:
首先通过html那里,label的for属性和input的id属性关联,使得点击label的时候,input也就被选择上了。
然后是css样式这里,label除了自己正常的样式,还受input被选中状态的影响,当input被选中后(input:checked),作为input在li爸爸内部的唯一兄弟元素(+选择符),label的样式就被重新更新了选中态。
因为选中展示的效果被label做了,那么input也就可以归隐山林,幽香田园生活了。所以直接设置样式不可见即可。
这也就是我上一篇说的,不会巧妙的利用每一个代码的特性。
而这一篇的实现方式正是还算巧妙的利用了该用的知识点。
也就不再需要li身上绑定的哪个choose事件来监听用户点击了。代码自己给我们做了!
甚至最后连用户选了什么都不用管,直接将v-model绑定的变量传给后端即可。
强大的v-model!
最后因为本需求有多选和单选,作为单页应用,又因不需要渲染很多道题目,每次只渲染一道。
所以我们可以最后根据选项判断确定是需要多选还是单选,动态的切换这两套就行了。
这么一看是不是特别简单明了!却被我之前实现的那么麻烦。。。。。我也是佩服自己光脚登山的傻劲。
整篇源码:
<template lang='pug'>
//- 答题 组件
#QuestionTest
//- 弹层
layer(:layerItem="layerItem" @confirmsubmit= "confirmSubmit($event)" @changelayershow= "changeLayerShow($event)" @hidelayer="hideLayer($event)" v-show="showLayer")
h3.zhanshi 您的选择是:{{picked}}
//- 题目表单
form.question
div
h3.qus-title(:data-id="state.ExamInfo.QuestionID") {{state.ExamInfo.ExamQuestionNo}}、{{state.ExamInfo.Description}}
ul.qus-list
li(v-for="(item,index) in state.ExamInfo.QuestionAnswerCode" v-bind:class="{'li-focus' : chooseNum==index}")
input( type="radio" :value="item.Code" :id="'choice1'+index" v-model="picked")
label(:for="'choice1'+index" class="choice-item") {{item.Code}}、{{item.Description}}
h3.zhanshi 您的多选选择是:{{pickedBox}}
form.question
div
h3.qus-title(:data-id="state.ExamInfo.QuestionID") 15、这是多选题目?-多选
ul.qus-list
li(v-for="(item,index) in state.ExamInfo.QuestionAnswerCode" v-bind:class="{'li-focus' : chooseNum==index}")
input( type="checkbox" :value="item.Code" :id="'choice2'+index" v-model="pickedBox")
label(:for="'choice2'+index" class="choice-item") {{item.Code}}、多选{{item.Description.substring(2)}}
</template>
<script>
import $axios from '../fetch/api'
export default {
name: 'questiontest',
data () {
return {
picked: '',
pickedBox: [],
state: {
dataUrl: this.$store.state.ownSet.dataUrl,
progress: this.$store.state.init.ActiveProgressEnum,
ExamInfo: this.$store.state.init.ExamInfo,
PersonID: this.$store.state.init.PersonID,
TeamID: this.$store.state.init.TeamID,
},
unclickable: true, // 判断是否已选择答案,不选择不能下一题,并置灰按钮
showLayer: false, //是否显示弹层
layerItem: {
isQuestion: false,
isSubmit: false, //是否是最后一道题时触发“下一题"按钮,点击了提交
isSuccess: false,
isLoading: false
},
chooseNum: null,
isFocus: false,
isLast: false,
isClicked: false//是否已经点击下一题,防止二次提交
}
},
created(){
// 点击开始答题,新页面应该定位到顶头题干位置
document.body.scrollTop = 0;
if(this.state.progress > 100107 && this.state.progress !== 100112){
alert('您已答题完毕!');
}
if(this.state.ExamInfo.QuestionID == 15){//答到14题退出的情况
//判断切换下一题和提交按钮
this.isLast = true;
}
},
methods: {
choosed(index){
this.chooseNumStr = '';//初始化
// 单选or多选
if(this.state.ExamInfo.IsMulti){
// 多选
if(this.$refs.liId[index].className.length <= 0){
// 添加类
this.$refs.liId[index].className = 'li-focus';
}else{
// 选中再取消
this.$refs.liId[index].className = '';
}
// 获取选中结果
for (let i = 0; i < this.$refs.liId.length; i++) {
if(this.$refs.liId[i].className.length > 0){
this.chooseNumStr += this.$refs.liId[i].innerText.substring(0,1);
}
}
// 置灰提交按钮与否
if(this.chooseNumStr.length > 0){
this.unclickable = false;
}else{
// 没有选东西,就置灰按钮
this.unclickable = true;
// 注意,再添加按钮的不可点击状态
}
}else{
// 单选
this.unclickable = false;
this.chooseNum = index;
//索引0-3对应答案A-B
// 注意,这里看看最多的选项是多少个,进行下配置,当前只是配置到了F
switch(index){
case 0: this.chooseNumStr = 'A';
break;
case 1: this.chooseNumStr = 'B';
break;
case 2: this.chooseNumStr = 'C';
break;
case 3: this.chooseNumStr = 'D';
break;
case 4: this.chooseNumStr = 'E';
break;
case 5: this.chooseNumStr = 'F';
break;
}
}
},
nextItem(){//下一题
if(this.$store.state.ownSet.test){
// let submitFun = false;
var newExamInfo = {
QuestionID: 15,
Description: "这里是一个测试标题?-多选",
QuestionAnswerCode: [{
Code: "A",
Description: "多选一"
},{
Code: "B",
Description: "多选二"
},{
Code: "C",
Description: "多选三"
},{
Code: "D",
Description: "多选四"
}],
IsMulti: true,
ExamQuestionNo: 15,
PersonID: 1
}
if(!this.isClicked){
// 按钮可以点击-如果提交过一次,不能二次提交,如果提交失败,可以二次提交
if(this.unclickable){
alert('您还没有选择答案哦!');
}else{
this.isClicked = true; // 还没提交过,可以提交
this.ajaxFun(newExamInfo,false)
}
}
}else{
if(this.state.progress > 100107 && this.state.progress != 100112){
alert('您已答题完毕!不能重复答题。');
}else{
if(!this.isClicked){
// 按钮可以点击-如果提交过一次,不能二次提交,如果提交失败,可以二次提交
if(this.unclickable){
alert('您还没有选择答案哦!');
}else{
this.isClicked = true; // 还没提交过,可以提交
let postData = `Type=2&PersonID=${this.state.PersonID}&QuestionID=${this.state.ExamInfo.QuestionID}&Result=${this.chooseNumStr}`;//2为下一题
if(this.state.TeamID > 0){
postData+= `&TeamID=${this.state.TeamID}`;
}
this.ajaxFun(postData,false)
.then((response)=>{
// console.log(this.state.ExamInfo.ExamQuestionNo)
})
.catch((err)=>{
this.isClicked = false;
console.log(err);
});
}
}
}
}
},
submitItem(){//提交按钮
if(!this.isClicked){
if(this.unclickable){
alert('您还没有选择答案哦!');
}else if(!this.$store.state.ownSet.test){
if(this.state.progress > 100107){
alert('您已答题完毕!不能重复答题。');
}else{
this.showLayer = true;
this.layerItem.isSubmit = true;
}
}
if(this.$store.state.ownSet.test){
this.showLayer = true;
this.layerItem.isSubmit = true;
}
}
},
confirmSubmit(data){// 提交弹层 之 确定
if(this.$store.state.ownSet.test){
this.ajaxFun('',true)
}else{
if(!this.isClicked){
this.isClicked = true;
// 发送ajax
let postData = `Type=3&PersonID=${this.state.PersonID}&QuestionID=${this.state.ExamInfo.QuestionID}&Result=${this.chooseNumStr}`;//3为提交
if(this.state.TeamID > 0){
postData+= `&TeamID=${this.state.TeamID}`;
}
this.ajaxFun(postData,true)
.then((response)=>{
// 关闭提交弹层
})
.catch((err)=>{
this.isClicked = false;
console.log(err);
});
}
}
},
changeLayerShow(data){// 提交弹层 之 取消 + 状态重置
this.showLayer = false;
this.layerItem.isSubmit = false;
},
hideLayer(data){
this.showLayer = false;
},
ajaxFun(postData,submitFun){
let _this = this;
if(this.$store.state.ownSet.test){
//测试效果
return new Promise(function(resolve,reject){
if(submitFun){
// 关闭提交弹层
_this.layerItem.isSubmit = false;
}
// 判断返回结果-弹层
_this.layerItem.isQuestion = true;
_this.showLayer = true;
setTimeout(()=>{
if(submitFun){
// 提交
// 判断返回结果
_this.layerItem.isSuccess = false;
// 改值
_this.$store.dispatch('setProgress',100110);
_this.$router.replace('redpacket');
}else{
// 判断返回结果
_this.layerItem.isSuccess = true;
// 下一题
if(_this.state.ExamInfo.QuestionID == 14){ //ExamQuestionNo
//判断切换下一题和提交按钮
_this.isLast = true;
}
// 下一题重新赋值
_this.state.ExamInfo = postData;
_this.$store.dispatch('setExaminfo',postData)
// 点击下一题,新页面应该定位到顶头题干位置
document.body.scrollTop = 0;
// 样式清空
for (let i = 0; i < _this.$refs.liId.length; i++) {
_this.$refs.liId[i].className = '';
}
}
_this.showLayer = false;
_this.layerItem.isQuestion = false;
_this.chooseNumStr = '';
_this.chooseNum = null;
_this.unclickable = true;
_this.isClicked = false;
}, 2000);
});
}else{
return new Promise(function(resolve,reject){
if(submitFun){
// 关闭提交弹层
_this.layerItem.isSubmit = false;
}
_this.layerItem.isQuestion = false;
_this.showLayer = true;
_this.layerItem.isLoading = true;
$axios.get(_this.state.dataUrl+'ExamAnswer?'+postData)
.then((response)=>{
console.log(response);
if(response && response.data && response.data.result === 1){
_this.layerItem.isLoading = false;
_this.layerItem.isQuestion = true;
// 判断返回结果
if(response.data.RetValue.proResult){
_this.layerItem.isSuccess = true;
}else{
_this.layerItem.isSuccess = false;
}
resolve(response);
setTimeout(()=>{
if(submitFun){
// 提交
// resolve(response);
_this.$store.dispatch('setUser',response.data.RetValue);
_this.$router.replace('redpacket');
}else{
// 下一题
if(_this.state.ExamInfo.QuestionID == 14){ //ExamQuestionNo
//判断切换下一题和提交按钮
_this.isLast = true;
}
// 下一题重新赋值
_this.state.ExamInfo = response.data.RetValue;
// 点击下一题,新页面应该定位到顶头题干位置
document.body.scrollTop = 0;
// 样式清空
for (let i = 0; i < _this.$refs.liId.length; i++) {
_this.$refs.liId[i].className = '';
}
}
_this.showLayer = false;
_this.layerItem.isQuestion = false;
_this.chooseNumStr = '';
_this.chooseNum = null;
_this.unclickable = true;
_this.isClicked = false;
}, 2000);
}else{
_this.showLayer = false;
_this.layerItem.isQuestion = false;
_this.isClicked = false;
reject('数据提交失败,请刷新重试!')
}
})
.catch((err)=>{
_this.showLayer = false;
_this.layerItem.isQuestion = false;
_this.isClicked = false;
reject(err)
});
});
}
}
}
}
</script>
<style scoped lang='scss'>
@import '../assets/css/var.scss';
body{
position: relative;
}
.zhanshi{
padding: .1rem .35rem;
color: #fff;
font-size: .28rem;
}
.question{
position: relative;
padding: .77rem .3rem .4rem;
margin: .21rem .3rem 1rem;
@include boxStyle();
.qus-title{
margin-bottom: .77rem;
font-size: .38rem;
color: $textClr;
}
}
.qus-box{
display: inline-block;
width: .3rem;
height: .3rem;
margin-right: .2rem;
}
.qus-list li{
input{
display: none;
}
input:checked + .choice-item{
background: $purpleClr;
color: #FFF;
}
.choice-item{
display: block;
margin: .2rem auto 0;
padding: .3rem .3rem .34rem;
color: $qusTxt;
font-size: .34rem;
text-align: center;
@include boxStyle(1rem,.12rem,rgba(49,32,114,0.16));
}
&.li-focus .choice-item{
background: $purpleClr;
color: #FFF;
}
}
</style>
个人学习理解和总结,很多不足还请指正~
声明:
请尊重博客园原创精神,转载或使用图片请注明:
博主:xing.org1^
出处:http://www.cnblogs.com/padding1015/
vue案例 - v-model实现自定义样式の多选与单选的更多相关文章
- sencha touch Model validations 自定义验证 二选一输入验证、重复验证、时间验证、比较验证、条件验证(2015-1-14)
项目初始化时执行以下代码 //重写模型,方便进行自定义验证 Ext.define("Ext.zh.data.Model", { override: "Ext.data.M ...
- js自定义修改复选框单选框样式,清除复选框单选框默认样式
之前做项目的时候,也遇到过需要按照设计稿把<input type="checkbox">和<input type="radio">的默认 ...
- vue案例 - 使用vue实现自定义多选与单选的答题功能
4月底立得flag,五月底插上小旗,结果拖到六月底七月初才来执行.说什么工作忙都是借口,就是睡的比猪早,起的比猪晚. 本来实现多选单选这个功能,vue组件中在表单方面提供了一个v-model指令,非常 ...
- vue 微信二维码扫码登录,附加 自定义样式
大概流程: 先安装 微信 的登录, 然后 局部引入,局部注册,方法调用,存 token,跳转路由 npm 安装 npm install vue-wxlogin --save-dev 微信安装 微信 ...
- Dialog详解(包括进度条、PopupWindow、自定义view、自定义样式的对话框)
Dialog详解(包括进度条.PopupWindow.自定义view.自定义样式的对话框) Android中提供了多种对话框,在实际应用中我们可能会需要修改这些已有的对话框.本实例就是从实际出发, ...
- Yii2 ActiveForm表单自定义样式
实例: <?php $form = ActiveForm::begin([ 'fieldConfig' => [ 'template' => '<div class=" ...
- windows phone (12) 小试自定义样式
原文:windows phone (12) 小试自定义样式 样式在BS开发中经常用到,在wp中系统也提供了解决办法,就是对设置的样式的一种资源共享,首先是共享资源的位置,它是在App类中,之前我们已经 ...
- 【百度地图API】如何制作自定义样式的公交导航结果面板?
原文:[百度地图API]如何制作自定义样式的公交导航结果面板? 摘要: 百度地图API有默认的公交导航结果面板,但样式比较单一:而百度地图上的结果面板就比较美观.如何利用百度地图API来制作一个比较美 ...
- Vue 框架-05-动态绑定 css 样式
Vue 框架-05-动态绑定 css 样式 今天的小实例是关于 Vue 框架动态绑定 css 样式,这也是非常常用的一个部分 首先说一下 动态绑定,相对的大家都知道静态绑定,静态绑定的话,直接加 cl ...
随机推荐
- (转)关于yuv 格式:planar和semi-planar格式
关于yuv 格式 YUV 格式通常有两大类:打包(packed)格式和平面(planar)格式.前者将 YUV 分量存放在同一个数组中,通常是几个相邻的像素组成一个宏像素(macro-pixel);而 ...
- perl 安装Image::Magick 模块
ImageMagick 是一个处理图片的库,有C, perl, python, java 等多种语言对应的库 在安装perl 对应的Image::Magick 模块之前,首先需要安装 ImgeMagi ...
- nodejs npm 使用淘宝 NPM 镜像
使用淘宝 NPM 镜像 大家都知道国内直接使用 npm 的官方镜像是非常慢的,这里推荐使用淘宝 NPM 镜像. 淘宝 NPM 镜像是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读), ...
- gridview根据条件来改变行的颜色以及改变单元格的颜色。
gridview根据条件来改变行的颜色以及改变单元格的颜色. 通过在RowDataBound事件中写代码来实现,见代码. protected void GridView1_RowDataBound(o ...
- Xcode不太常见又实用的小技巧
备份, 原文: http://rocry.com/2012/12/17/xcode-tips/ 让代码中的TODO和FIXME变成Warning 选中某个Target > Build Phase ...
- ios模拟器已安装但xcode无法显示
最近把系统抹盘重装了, 然后用Time Machine恢复到原始状态, 一切安好, 但是使用xcode的时候发现一个模拟器都没有了: 各种折腾, 重装SDK啊, 重装xcode啊,最后发现, 如果你的 ...
- create table repo_folder_operate_log_bak as select * from repo_folder_operate_log;
create table repo_folder_operate_log_bak as select * from repo_folder_operate_log;
- docker 相关文章
https://baijiahao.baidu.com/s?id=1581420975184566963&wfr=spider&for=pc 创建centos基础镜像 https ...
- 分表需要解决的问题 & 基于MyBatis 的轻量分表落地方案
分表:垂直拆分.水平拆分 垂直拆分:根据业务将一个表拆分为多个表. 如:将经常和不常访问的字段拆分至不同的表中.由于与业务关系密切,目前的分库分表产品均使用水平拆分方式. 水平拆分:根据分片算法将一个 ...
- logstash结合rsyslog,收集系统日志
rsyslog是日志收集工具.如今非常多Linux都自带rsyslog,用其替换掉syslog.怎样安装rsyslog就不讲了.大概讲下原理.然后讲logstash的相关配置. rsyslog本身有一 ...