调查问卷WebApp

1. 效果演示

2. 主要知识点

  • 使用slot分发内容
  • 动态组件
  • 组件通信
  • 实例的生命周期
  • 表单

3. 遇到的问题

  • bus 通信 第一次 $on 监听不到

    // 解决bus 第一次通信 $on 监听不到
    this.$nextTick(function () {
          if (_this.totalSize - 1 == _this.order) {
             bus.$emit('submiteDisabled', disabledStatus)
          } else {
             bus.$emit('nextStepDisabled', disabledStatus)
          }
     })
    
  • bus 通信 $on 多次触发

    //  bus传值之后要进行销毁, 尤其是跳转页面进行使用的时候
    beforeDestroy() {
         if (this.totalSize - 1 == this.order) {
                bus.$off('submiteDisabled')
          } else {
                bus.$off('nextStepDisabled')
          }
    },
    

4. 代码整理

  • HTML

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>答题卡</title>
        <link rel="stylesheet" href="./css/style.css">
    </head>
    <body>
        <div id="app">
            <main-component v-for="(question, index) in questions" :key="index" :title="question.title" :order="index"
                v-if="index === currentOrder" :content="question.chooses">
                <template slot="title" slot-scope="props">
                    <span>{{props.order}}. {{props.title}}</span>
                </template>
                <template slot="content" slot-scope="props">
                    <component :is="props.type" 
                    :datas="question.chooses.values" 
                    :selected = "question.chooses.selected"
                    :order = "index"
                    :total-size = "questions.length"
                    v-if="props.type != ''"></component>
                    <textarea-component :value="question.chooses.value" 
                        :order="index" 
                        :total-size = "questions.length" v-else></textarea-component>
                </template>
                <template slot="functionalDomain">
                    <button-component :now-order = "currentOrder" 
                        :total-elements = "questions.length"
                        :next-step-disabled = "true"
                        ></button-component>
                </template>
            </main-component>
        </div>
        <script src="./js/vue.js"></script>
        <script src="./js/index.js"></script>
    </body>
    </html>
    
  • JS

    var bus = new Vue();
    Vue.component('main-component', {
        props: {
            order: Number,
            title: String,
            content: {
                type: Object
            }
        },
        template: '<div class="main">\
                    <div class="title">\
                        <slot name="title" :title="mytitle" :order="myorder + 1"></slot>\
                    </div>\
                    <div class="content">\
                        <slot name="content" :type="typeSelect"></slot>\
                    </div>\
                    <div class="footer">\
                        <slot name="functionalDomain" ></slot>\
                    </div>\
                </div>',
        data: function () {
            return {
                mytitle: this.title,
                myorder: this.order
            }
        },
        computed: {
            typeSelect: function () {
                let type_co = this.content.type;
                if ('radio' == type_co || 'checkbox' == type_co) {
                    return type_co + '-component';
                }else{
                    return '';
                }
            }
        },
    });
    Vue.component('radio-component', {
        props:{
            selected: String,
            datas:Array,
            order: Number,
            totalSize: Number
    
        },
        template: '<div>\
                        <label v-for="(item, index) in mydatas" :key="index"><input type="radio" :value="item.value" v-model="results" />{{item.name}}</label>\
                    </div>',
        data:function (){
            return {
                results: this.selected,
                mydatas: this.datas
            }
        },
        methods: {
            updatNextStepStatus: function (val) {
                
                let disabledStatus = true;
                let _this = this;
                if (val != '') {
                    disabledStatus = false;
                }
                // 解决bus 第一次通信 $on 监听不到
                this.$nextTick(function () {
                    if (_this.totalSize - 1 == _this.order) {
                        bus.$emit('submiteDisabled', disabledStatus)
                    } else {
                        bus.$emit('nextStepDisabled', disabledStatus)
                    }
    
                })
                
            }
        },
        watch: {
            results: function (val) {
                this.updatNextStepStatus(val);
                bus.$emit('valueChange', this.order, val)
            },
            selected: function (val) {
                this.results = val
            }
        },
        mounted() {
            this.updatNextStepStatus(this.results);
        },
        beforeDestroy() {
            if (this.totalSize - 1 == this.order) {
                bus.$off('submiteDisabled')
            } else {
                bus.$off('nextStepDisabled')
            }
        },
    });
    Vue.component('checkbox-component', {
        props: {
            selected: Array,
            datas: Array,
            order: Number,
            totalSize: Number
        },
        template: '<div>\
                        <label v-for="(item, index) in mydatas" :key="index"><input type="checkbox" :value="item.value" v-model="results" />{{item.name}}</label>\
                    </div>',
        data: function () {
            return {
                results: this.selected,
                mydatas: this.datas
            }
        },
        methods: {
            updatNextStepStatus: function (newValue, oldValue) {
                let disabledStatus = true;
                let _this = this;
                if (oldValue.length < 2 && newValue.length < 2) {
                    return;
                } else if (oldValue.length >= 2 && newValue.length < 2) {
                    disabledStatus = true;
                } else if (newValue.length >= 3){
                    alert("多选,最多选择3个选项")
                }else{
                    disabledStatus = false;
                }
                // 解决bus 第一次通信 $on 监听不到
                this.$nextTick(function () {
                    if (_this.totalSize - 1 == _this.order) {
                        bus.$emit('submiteDisabled', disabledStatus)
                    } else {
                        bus.$emit('nextStepDisabled', disabledStatus)
                    }
    
                })
            }
        },
        watch: {
            results: function (newValue, oldValue) {
                this.updatNextStepStatus(newValue, oldValue);
                bus.$emit('valueChange', this.order, newValue)
            },
            selected: function (val) {
                this.results = val
            }
        },
        mounted() {
            this.updatNextStepStatus(this.results,[]);
        },
        beforeDestroy() {
            if (this.totalSize - 1 == this.order) {
                bus.$off('submiteDisabled')
            } else {
                bus.$off('nextStepDisabled')
            }
        },
    });
    Vue.component('textarea-component', {
        props: {
            value: String,
            order: Number,
            totalSize: Number
        },
        template: '<textarea v-model="results" placeholder="不少于100字"></textarea>',
        data: function () {
            return {
                results: this.value,
            }
        },
        methods: {
            updatNextStepStatus: function (newValue, oldValue) {
    
                let disabledStatus = true;
                let _this = this;
                if (newValue.length > 0 && newValue.length <= 5) {
                    disabledStatus = false;
                }else{
                    disabledStatus = true;
                }
                // 解决bus 第一次通信 $on 监听不到
                this.$nextTick(function () {
                    if (_this.totalSize - 1 == _this.order) {
                        bus.$emit('submiteDisabled', disabledStatus)
                    }else{
                        bus.$emit('nextStepDisabled', disabledStatus)
                    }
                    
                })
    
            }
        },
        watch: {
            results: function (newValue, oldValue) {
                this.updatNextStepStatus(newValue, '');
                bus.$emit('valueChange', this.order, newValue)
            },
            value: function (val) {
                this.results = val
            }
        },
        mounted() {
            this.updatNextStepStatus(this.results, '');
        },
        beforeDestroy() {
            if (this.totalSize - 1 == this.order) {
                bus.$off('submiteDisabled')
            }else{
                bus.$off('nextStepDisabled')
            }
            
        },
    });
    Vue.component('button-component', {
        props: {
            basicClasses: {
                type: Object,
                default: function () {
                    return {
                        'button-white': true
                    }
                }
            },
            nextStepClasses: {
                type: Object,
                default: function () {
                    return {
                        'button-primary': true
                    }
                }
            },
            nextStepDisabled:{
                type: Boolean,
                default: false
            },
            nowOrder: Number,
            totalElements: Number
        },
        template: '<div>\
                        <button class="btn" :disabled="nextStepStatus" :class="primary" \
                            v-if="order  != total - 1" @click="handleNextStep">下一步</button>\
                        <button class="btn" :disabled="submiteStatus" :class="primary" \
                            v-if="order  == total - 1" @click="handleSubmit">提交</button>\
                        <button class="btn" :disabled="false" :class="basic"\
                            v-if= "order  != 0" @click="handleBackStep">上一步</button>\
                        <button class="btn" :disabled="false" @click="handleReset" :class="basic">重置</button>\
                    </div>',
        data() {
            return {
                basic: this.basicClasses,
                primary: this.nextStepClasses,
                nextStepStatus: this.nextStepDisabled,
                order: this.nowOrder,
                total: this.totalElements,
                submiteStatus: true
            }
        },
        methods: {
            handleNextStep: function () {
                bus.$emit('nextStep')
            },
            handleBackStep: function () {
                bus.$emit('backStep')
            },
            handleSubmit: function () {
                bus.$emit('submit')
            },
            handleReset: function () {
                let _this = this;
                bus.$emit('reset', _this.order)
            }
        },
        mounted() {
            let _this = this;
            bus.$on('nextStepDisabled', function (status) {
                _this.nextStepStatus = status
                
            });
            bus.$on('submiteDisabled', function (status) {
                _this.submiteStatus = status
    
            })
        },
    
    });
    var app = new Vue({
        el: "#app",
        data: {
            currentOrder: 0,
            questions: [
                {
                    title: '请问您的性别是?',
                    chooses: {
                        type: 'radio',
                        values: [{ 'name': '男', 'value': '1' }, { 'name': '女', 'value': '2' }, { 'name': '保密', 'value': '3' }],
                        selected: ''
                    }
                },
                {
                    title: '请选择您的兴趣爱好?',
                    chooses: {
                        type: 'checkbox',
                        values: [{ 'name': '看书', 'value': 'read book' },
                        { 'name': '游泳', 'value': 'swim' },
                        { 'name': '跑步', 'value': 'run' },
                        { 'name': '看电影', 'value': 'see movie' }],
                        selected: []
                    }
                },
                {
                    title: '请介绍一下你自己',
                    chooses: {
                        type: 'text',
                        value: ''
                    }
                },
                
            ]
        },
        methods: {
            handleNext: function () {
                let current = this.currentOrder;
                if (++current >= this.questions.length) {
                    return;
                }else{
                    this.currentOrder += 1;
                }
                
            },
            handleBack: function () {
                let currentOrder = this.currentOrder;
                if (-- currentOrder < 0) {
                    return;
                }else{
                    this.currentOrder -= 1;
                }
            },
            handleReset: function (order, val) {
                let currentQuestion = this.questions[order].chooses;
                if (currentQuestion.type == 'radio') {
                    currentQuestion.selected = val === undefined ? '': val;
                } else if (currentQuestion.type == 'checkbox') {
                    currentQuestion.selected = val === undefined ? [] : val;
                } else {
                    currentQuestion.value = val === undefined ? '' : val
                }
            }
        },
        mounted: function () {
            let _this = this;
            bus.$on('nextStep', function (msg) {
                let current = _this.currentOrder;
                if (++current >= _this.questions.length) {
                    return;
                } else {
                    _this.currentOrder += 1;
                }
            });
            bus.$on('backStep', function (msg) {
                let currentOrder = _this.currentOrder;
                if (--currentOrder < 0) {
                    return;
                } else {
                    _this.currentOrder -= 1;
                }
            });
            bus.$on('submit', function (msg) {
                console.log('提交')
            });
            bus.$on('reset', function (order) {
                _this.handleReset(order)
            });
            bus.$on('valueChange', function (order, val) {
                _this.handleReset(order, val)
            });
        }
    })
    
  • CSS

    [v-cloak]{
        display: none;
    }
    .btn {
        border: none;
        outline:none;
        color: white;
        padding: 7px 25px;
        text-align: center;
        text-decoration: none;
        display: inline-block;
        font-size: 16px;
        margin: 4px 2px;
        cursor: pointer;
    }
    .button-white {
        background-color: rgb(217, 219, 223);
        color: #000000;
    }
    .button-white:active {
        background-color: rgb(134, 149, 179);
        color: #000000;
    }
    .button-primary {
        background-color: rgb(39, 126, 228);
        border: none;
        color: white;
        padding: 7px 25px;
        text-align: center;
        text-decoration: none;
        display: inline-block;
        font-size: 16px;
        margin: 4px 2px;
        cursor: pointer;
    }
    .button-primary:active {
        background-color: rgb(185, 171, 31);
    }
    .button-primary:disabled {
        background-color: rgb(115, 122, 130);
    }
    

5. 升级

  • 组件重复内容很多,可合并
posted @ 2019-12-11 10:11  πSen  阅读(380)  评论(0编辑  收藏  举报