Vue.js完成一个浅易问卷平台(项目中碰到的题目总结)

项目地点

使命形貌

  • 参考设想图

  • 完成一个简易版的问卷治理体系,有以下功用:

问卷治理列表

  • 有一个头部能够显现logo,不须要完成登录等操纵

  • 问卷治理列表页面默许为首页

  • 有一个表格用于展示一切已建立的问卷

  • 列表中包含列有:问卷称号,问卷状况(未宣布,宣布中,已完毕),和操纵地区(编辑、删除、检察数据)

  • 问卷状况为未宣布时,能够做的操纵为编辑、删除、检察问卷

  • 问卷状况为宣布中和已完毕时,能够做的操纵为检察数据、检察问卷

  • 表格最左边有批量挑选(多选)的checkbox,多选后,能够举行批量删除功用,checkbox款式用默许即可,不须要根据设想图的款式

  • 当一个问卷都没有的时刻,表格不展示,页面显现大大的新建问卷按钮

问卷新建及编辑

  • 点击问卷治理列表中的新建按钮后,进入到问卷新建页面

  • 点击问卷列表中某个问卷行的编辑按钮后,进入到问卷的编辑页面

  • 新建页面和编辑页面基础雷同

  • 问卷有一个问题字段,点击后能够进入编辑状况

  • 能够针对问卷中的问题举行增编削操纵,每一个问卷起码一个问题,最多十个问题

  • 问题范例包含:单选题、多选题、单行文本题

  • 能够对一切问题举行位置转变(上移、下移),复用,删除的操纵

  • 最上面的问题没有上移操纵,最下面的问题没有下移操纵

  • 点击复用时,在被复用的问题紧接着的下方新增一个和被复用完整一样的问题(包含选项)

  • 关于单选题和多选题,能够对问题的选项举行增、删、改、排序操纵

  • 文本题能够设定是必填还黑白必填的问题

  • 有一个问卷调查填写停止时候,运用一个日历组件来举行时候的挑选,日期挑选不能早于当前日期

  • 保留问卷能够举行问卷的保留

  • 宣布问卷能够使得问卷状况变成宣布中的状况

  • 当点击宣布时,假如停止日期早于当前日期,则须要提醒修正停止日期

删除问卷

  • 在问卷治理列表中点击某个问卷的删除按钮后,弹出一个浮出层,让用户二次确认是不是删除该问卷,假如用户点击是,则删撤除该问卷

检察问卷

  • 在问卷治理列表中点击检察问卷的按钮后,在新窗口中翻开该问卷的页面,该页面是可供用户举行问卷填写的页面,在问卷未宣布状况和已完毕状况时,问卷提交是无效的。

  • 该页面在挪动端须要举行优越的兼容支撑

检察数据

  • 在问卷治理列表中点击检察数据按钮后,进入到一个数据报告页面,用图表情势显现各个单选题和多选题的挑选状况

  • 如设想稿中显现,每一个问题在右边用某种图表来显现答题状况,自行挑选适宜的图表,设想稿中仅为表示,图表款式不须要和设想稿一致。引荐单选题运用饼状图,多选题运用条形图

  • 文本题用一个百分比图展示有用回复占比即可

  • 返回按钮点击后返回列表页面

  • 在项目中尝试模块化的要领及东西

  • 在项目中尝试CSS预处理东西

  • 在项目中尝试项目构建、打包东西

问题总结

全选功用的完成

起首每一个列表项都运用了v-model举行双向数据绑定通报是不是被选中状况

<template v-for="item in qsList">
  <ul>
    <li><input type="checkbox" v-model="item.checked"></li>

然后给全选按钮也用v-model绑定是不是全选状况

<label><input type="checkbox" id="all-check" v-model="selectAll">全选</label>

下一步在computed中定义三个盘算属性

  1. selectAll: 是不是全选

  2. selectCount: 盘算有若干项被选中

  3. selectGroup: 存储当前选中项,以便对它们举行操纵

selectAll盘算属性:

selectAll: {
    get() { //this.qsList是一个数组,明白代码时能够看为[{checked: false}, {checked: false}]
      return this.selectCount === this.qsList.length && this.selectCount !== 0;
    },
    set(value) {
      this.qsList.forEach( item => {
        item.checked = value;
      } );
      return value;
    }
  }

经由过程get要领猎取当前选中数,从而完成当列表项全被选中时,全选按钮自动被选中

经由过程set要领完成当全选按钮选中时,一切列表项也被选中

selectCount盘算属性

selectCount() {
    let i = 0;
    this.qsList.forEach( item => {
      if (item.checked) i++;
    } );
    return i;
  },

盘算当前有若干项被选中,selectAll经由过程此变量来盘算当前是不是一切列表项都被选中

selectGroup盘算属性

selectGroup() {
    let group = [];
    this.qsList.forEach( item => {
      if (item.checked) group.push(item);
    } );
    return group;
  }

存储被选中项,举行一致操纵

检测表单必填项是不是填写

这个问题我运用了v-model来处理,问卷中总共有三种范例的表单项,radio,checkbox,textarea 由于关于radio的v-model来讲只能绑定一个基础范例的值, checkbox的v-model应当绑定一个数组,如许选中项就会一个一个push到数组中,而且是双向绑定的,textarea的v-model也应当是一个基础范例,我设置的是字符串

<p v-for="option in item.options" class="option">
        <label>
          <input 
          type="radio" 
          :name="`${item.num}-${item.title}`"
          v-model="requiredItem[item.num]"
          v-if="item.type === 'radio'"
          :value="option">
          <input 
          type="checkbox" 
          :name="`${item.num}-${item.title}`"
          v-model="requiredItem[item.num]"
          v-if="item.type === 'checkbox'"
          :value="option">{{option}}
        </label>
      </p>
      <textarea 
      v-if="item.type === 'textarea'" 
      v-model="requiredItem[item.num]"></textarea>
      
      
      //猎取必选项,用对象存储起来,相当于 {1: '', 2: [], 3: ''}
      getRequiredItem() {
        this.qsItem.question.forEach( item => {
        if (item.isNeed) {
          if (item.isNeed) {
            if (item.type === 'checkbox') {
              this.requiredItem[item.num] = [] //多选框双向绑定的值
            } else {
              this.requiredItem[item.num] = '' //单选框 文本框双向绑定的值
            }
          }
        }
      } )
    }
    
    //直接检测双向绑定的值的内容长度即可晓得必填项是不是有值
     validate() {
      for (let i in this.requiredItem) {
        if (this.requiredItem[i].length === 0) return false
      }
      return true
    }

这里另有一个问题,我现在在v-for中经由过程v-if来推断表单项范例,如许看起来有些冗余,为何不直接动态绑定type来衬着表单项呢,如许就不必v-if了

<p v-for="option in item.options" class="option">
  <input 
    :type="item.type" 
    :name="`${item.num}-${item.title}`"
    v-model="requiredItem[item.num]"
    :value="option">
</p>

如许看起来简约多了,然则如许写会报错,v-model不能绑定在type属性为动态值的表单项上,即type是bind的表单项不能用v-model,所以这里只能退一步运用v-if来挑选衬着哪一种范例的表单项

耽误实行函数

当用户点击删除某一项时,平常的做法时弹出一个弹出层讯问用户是不是删除,用户点击肯定再举行删除操纵。这时候只要给肯定按钮绑定一个点击事宜举行删除操纵即可,然则当要屡次点击肯定举行下一个步骤,或许页面多个操纵事宜都是弹出这个弹出层,这时候肯定按钮就要去推断绑定哪一个操纵事宜等等,很快就变得异常复杂起来
这里能够运用ES6的Generator函数,能够很轻易的处理这个问题

<div class="shadow" v-if="showDialog">
  <div class="del-dialog">
    <header>
      <span>提醒</span>
      <span class="close-btn" @click="showDialog = false">X</span>
    </header>
    <p>{{info}}</p>
    <div class="btn-box">
      <button class="yes" @click="iterator.next();">肯定</button>
      <button @click="showDialog = false">作废</button>
    </div>
  </div>
</div>

弹出层内容

data() {
  return {
    qsList: [],
    showDialog: false, //是不是显现弹出层
    iterator: {}, //当前迭代器
    info: '' //弹出层提醒内容
  }
}

data中的数据

*delItem(num) {
    yield this.showDialogMsg('确认要删除此问卷')

    yield (() => {
      let index = 0;
      for (let length = this.qsList.length; index < length; index++) {
        if (this.qsList[index].num === num) break;
      }
      this.qsList.splice(index, 1);
      this.showDialog = false;
    })();
  },
  *delItems() {
    yield this.showDialogMsg('确认要删除选中的问卷?');

    yield (() => {
      this.showDialog = false;
      if (this.selectAll) {
        this.qsList = [];
        return;
      }
      if (this.selectGroup == []) return;

      this.selectGroup.forEach( item => {
        if (this.qsList.indexOf(item) > -1) this.qsList.splice(this.qsList.indexOf(item), 1);
      } )
    })();     
  },
  *edit(item) {
    yield (() => {
      if (item.state === 'noissue') {
        this.showDialogMsg('确认要编辑?');
      } else {
        this.showDialogMsg('只要未宣布的问卷才编辑');
      }
    })();
    yield (() => {
      if (item.state !== 'noissue') {
        this.showDialog = false;
      } else {
        this.showDialog = false;
        this.$router.push({name: 'qsEdit', params: { num: item.num }})
      }
    })();
  },
  *watchData(item) {
    yield (() => {
      if (item.state === 'noissue') {
        this.showDialogMsg('未宣布的问卷无数据可检察');
      } else {
        this.$router.push({ name: 'qsData', params: { num: item.num }})
      }
    })();
    yield this.showDialog = false;
  }

能够看到 页面中多个操纵都绑定在一个弹出层上,完成最大水平的复用,而且不会争执,只要把当前要实行的操纵的迭代器赋给肯定按钮,肯定按钮实行next要领即可

v-for每次衬着元素就自动实行一个函数

偶然我们须要v-for的每次遍历中就实行一个函数,我们能够如许

<li v-for="item in data">{{doSomething()}}</li>

然则这类做法假如实行比较复杂的要领很轻易涌现一些毛病比方无穷轮回等毛病,而且也不引荐

根据须要能够斟酌在js中再次遍历这个数据然后在遍历中对每一项举行操纵

watch无意中形成的无穷轮回毛病

在编辑问卷功用中,问题号应当要根据问题的上移下移复用删除新建等操纵举行变化,我运用了watch来监测变化然后变动题号

watch: {
  '$route': 'fetchData',
  qsItem: {
    handler(newVal) {
      newVal.question.forEach( (item, index) => {
        item.num = `Q${index + 1}`
      } )
    },
    deep: true
  }
}

我在举行上移,下移,删除,新建问题等操纵时都没有问题,然则在复用操纵时发生了无穷轮回的问题

<div class="questions" v-for="(qs, index) in qsItem.question">
    <span @click="copy(index, qs)">复用</span>
</div>

复用按钮,和复用要领

copy(index, qs) {
  if (this.questionLength === 10) return alert('问卷已满!')
  this.qsItem.question.splice(index, 0, qs)
}

如许写看起来没什么问题,哪一个item下的复用按钮被点击,就将这个item添加到本身下一项。
然则qs添加到watch监测的变量中后,会触发watch的要领,变动问题号,即qs的问题号被变动,同时qs又是谁人被点击的item,它们之间存在援用,这就会形成qs问题号的变动会使点击的item的问题号随着一同变化,如许item一变化,watch又被触发,同时item的问题号由于随着一同变化,致使问题号不是它准确的问题号,watch触发后,item的问题号又会变化为原本的,由于存在援用qs的又会随着变,然后再次触发watch….一向轮回下去
处理要领是用Object.assign()举行一次深拷贝,如许qs和item之间就不存在援用了

copy(index, qs) {
  if (this.questionLength === 10) return alert('问卷已满!')
  qs = Object.assign({}, qs)
  this.qsItem.question.splice(index, 0, qs)
}

这类做法不引荐,由于这类状况下运用watch原本就是不应当的,异常轻易形成想不到的问题

引荐的做法是将watch中的要领封装成一个函数,每次操纵时就挪用这个函数,固然照样须要Object.assign()来消除复用元素之间的绑定

这里我为了演习照样运用了watch这个不引荐的要领

总结完成,交功课了

    原文作者:reus_js
    原文地址: https://segmentfault.com/a/1190000009000663
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞