Vue2.0 完成调查问卷WebApp

最近一直在看vue.js实战,里面有一个项目实践,需求与效果如下:


效果

需求

我现在把此项目分成index.html,question.js,index.js以及style.css四个文件(其中说到的按钮制作成组件,可以自己实现,我这里就没做)。

注:此处是手机端,请调整到手机调试下跑该项目。

index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <link rel="stylesheet" href="style.css">
</head>
<body>
<div id="app" v-cloak>
    <question v-model="page"></question>
</div>
<script src="question.js"></script>
<script src="index.js"></script>
</body>
</html>

index.html 注意:v-model是双向绑定的数据,其实这里用v-bind也可以,它与v-model的区别是v-bind是单向数据流,而v-model是双向数据流。也就是子组件可以通过$emit来传递数据回父组件,而v-bind只能是从父组件传递数据到子组件。

question.js

Vue.component('question',{
    props:['value'],
    template: '<div><div style="height:100%" v-if="this.page == 1"><span>1.请问您的性别是:</span>'
        + '<div class="options" v-for="(item,index) in sex_list">'
        + '<input :value="item.name" type="radio" @change="radio_change($event,index)" name="sexy" :checked="item.checked">{{item.name}}'
        + '</div>'
        + '<div class="step_bottom">'
        + '<button :disabled="disabledOne" :class="[{disabledColor: disabledOne},buttonone,greycolor]" @click="nextQuestionTwo">下一步</button>'
        + '<button @click="restartQuestionOne" :class="[buttonone]">重置</button>'
        + '</div></div>'
        + '<div v-else-if="this.page == 2"><span>2.请选择您的兴趣爱好:</span>'
        + '<div class="options" v-for="(item,index) in hobbies">'
        + '<input type="checkbox" name="hobbies" value="item.name" @change="checkboxChange($event,index)" :checked="item.checked">{{item.name}}<br>'
        + '</div>'
        + '<div class="step_bottom second_quesiton">'
        + '<button :disabled="disabledTwo" :class="[greycolor,{disabledColor: disabledTwo}]" @click="nextQuestionThree">下一步</button>'
        + '<button @click="lastStepOne">上一步</button>'
        + '<button @click="restartQuestionTwo">重置</button>'
        + '</div></div>'
        + '<div v-else><span>3.请介绍一下自己:</span>'
        + '<div><textarea name="introduction" autofocus rows="10" cols="40" @blur="checkLength" :value="text"></textarea></div>'
        + '<div class="step_bottom second_quesiton">'
        + '<button :disabled="disabledThree" :class="[greycolor,{disabledColor: disabledThree}]" @click="submit">提交</button>'
        + '<button class="" @click="lastStepTwo">上一步</button>'
        + '<button class="" @click="restartQuestionThree">重置</button>'
        + '</div></div></div>',
    data:function () {
        return{
            sex_list:[
                {name:'男'},
                {name:'女'},
                {name:'保密'}
            ],
            hobbies: [
                {name:'看书'},
                {name:'游泳'},
                {name:'跑步'},
                {name:'看电影'},
                {name:'听音乐'}
            ],
            disabledOne: true,
            disabledTwo: true,
            disabledThree: true,
            buttonone:'buttonOne',
            greycolor:'greyColor',
            text:'',
            page: this.value,
            user_data:{
            }
        }
    },
    methods:{
        radio_change: function (el,index) {
            var radio_value = el.target.value;
            if( typeof radio_value != 'undefined' && radio_value != '' ){
                this.disabledOne = false;
                this.sex_list[index].checked = true;
            }else{
                this.disabledOne = true;
            }
        },
        checkboxChange: function (el,index) {
            var boxvalue = el.target.checked;
            var count = 0;
            if( boxvalue == true ){
                this.hobbies[index].checked = true;
            }else{
                this.hobbies[index].checked = false;
            }

            this.hobbies.forEach(function (item) {
                if( item.checked == true ){
                    count ++;
                }
            });

            if( count >=2 ){
                this.disabledTwo = false;
            }else {
                this.disabledTwo = true;
            }
        },
        checkLength:function (el) {
            var value = el.target.value;
            var length = value.length;
            if( length >= 10 ){
                this.disabledThree = false;
            }else{
                this.disabledThree = true;
            }
            this.text = value;
        },
        restartQuestionOne:function () {
            this.sex_list.forEach(function (item) {
                item.checked = false;
            });
            this.disabledOne = true;
        },
        restartQuestionTwo:function () {
            this.hobbies.forEach(function (item) {
                item.checked = false;
            });
            this.disabledTwo = true;
        },
        restartQuestionThree:function () {
            this.text = '';
            this.disabledThree = true;
        },
        nextQuestionTwo:function () {
            this.page ++;
            var obj = {};
            this.sex_list.forEach(function (item) {
                if( item.checked ){
                    obj.sex = item.name;
                }
            });
            this.user_data = obj;
        },
        nextQuestionThree:function () {
            var count = 0;
            var obj = this.user_data;
            obj.hobbies = [];
            this.hobbies.forEach(function (item) {
                if( item.checked == true ){
                    obj.hobbies.push(item.name);
                    count ++;
                }
            });
            this.user_data = obj;
            if(count > 3){
                alert('不得超过三项,请重新选择');
            }else{
                this.page ++;
            }
        },
        lastStepOne:function () {
            this.user_data = {};
            this.hobbies.forEach(function (item) {
                item.checked = false;
            });
            this.page --;
        },
        lastStepTwo:function () {
            this.text = '';
            if( typeof this.user_data.introduction != 'undefined' ) delete this.user_data.introduction;
            this.page --;
        },
        submit:function () {
            var obj = this.user_data;
            obj.introduction = this.text;
            this.user_data = obj;
            console.log(this.user_data);
            // ajax 发送数据到后台
        },
    }
});

question.js注意
1、这里我把三个页面整合到一起,能过v-if,v-else-if,v-else 来判断显示模块。
2、需要注意的是单项选择的时候,标签中只要存在checked属性就会被选中,所以这里我用:checked来判断是否有该checked属性。
3、进入下一步之前先要判断当前的选择是否符合需求。单选选中之后再可以进入下一步,重置选项可以清除当前页的选择; 复选框最少要有两个选择,最多则只有三个选择,选择两个的时候显示下一步并判断当前的选项有几个,如果超出三个给出提示,并不能进入下一步。
4、textarea中有属性autofocus是进入当前页,自动聚焦。必须有rows,cols属性才能有blur事件。
5、点击所有的上一步,都要清空当前页的数据。

index.js

var app = new Vue({
    el:'#app',
    data:{
        page:1
    },
});
style.css
[v-cloak]{
    display: none;
}
body,input,button,textarea,div{
    margin:0;
    padding:0;
}
#app{
    margin:20px 15px;
}
.options{
    margin-top:13px;
}
.options input[type=radio]:not(:first-child){
    margin-left:13px;
}
.options input[type=radio]{
    margin-right:5px;
}
.options input[type=checkbox]:not(:first-child){
    margin-top:15px;
}
.options input[type=checkbox]{
    margin-right:5px;
}
.not_display{
    display: none;
}
textarea{
    /*padding:50px 85px;*/
    margin-left:11px;
    margin-top:9px;
}
.able_color{
    color:#3399ff;
}
.disable_color{
    color:#808080;
}
.restart-color{
    color:#fff;
}
.big-size{
    width:200px;
}
.small-size{
    width:150px;
}
.step_bottom{
    height:35px;
    width:100%;
    position: absolute;
    bottom: 30px;
}
button{
    background-color: transparent;
    outline: none;
    border:1px solid #cccccc;
    border-radius: 5px;
}
.buttonOne{
    padding:7px 30px;
    margin-right:10px;
    width:45%;
}
input[type=submit]{
    width:29%;
    height:32px;
    margin-right:7px;
    border:1px solid #cccccc;
    border-radius: 5px;
}
/*.buttonOne:hover{*/
/*background-color:#cccccc;*/
/*border:1px solid #cccccc;*/
/*}*/
.greyColor{
    background-color:#808080;
    border:1px solid #808080;
    color:#fff;
}
.disabledColor{
    background-color:#cccccc;
    border:1px solid #cccccc;
}
.second_quesiton button{
    padding:7px 30px;
    width:30%;
    margin-right:5px;
}

表单提交相关的文章可以参考:
https://blog.csdn.net/caixiaodaohaha/article/details/78860452

最终效果图截图:








最终数据:


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 这篇笔记主要包含 Vue 2 不同于 Vue 1 或者特有的内容,还有我对于 Vue 1.0 印象不深的内容。关于...
    云之外阅读 5,044评论 0 29
  • Vue 实例 属性和方法 每个 Vue 实例都会代理其 data 对象里所有的属性:var data = { a:...
    云之外阅读 2,198评论 0 6
  • 轻纺城30走势前三段盘整背驰,目前跌回6.85,走势在向下演化,关注5分级别三段结构完成情况。30关键点位6.85...
    OLALA66666阅读 209评论 0 0
  • 一 1939年,当德国的爪牙伸向欧洲领土时,海滨小镇圣布里亚克的人们正在举行一年一度的音乐会。青草地上支起白色的棚...
    虹霓阅读 1,139评论 19 9
  • 今天发现一个问题——我以为我了解。 以前,我认为,我了解我父母,知道ta们的生日,知道ta们的爱好,知道ta们的遗...
    5070黑土阅读 224评论 0 2