当前位置: 首页 > Web前端 > CSS

Vue.js实现一个简单的问卷调查平台(项目中遇到的问题总结)

时间:2023-03-30 18:02:02 CSS

项目地址源码地址预览地址(无响应,请在电脑上打开)使用我自制的日历组件(第一次做的时候做的)学过vue是的,比较粗糙)日历-录入任务说明参考设计图实现一个简易版的问卷管理系统,有以下功能:问卷管理列表有一个可以显示logo的head,还有无需实现登录等操作。问卷管理列表页默认首页有一个表格用于显示所有已创建的问卷,包括:问卷名称、问卷状态(未发布、发布、已结束)、问卷时的操作区域(编辑、删除、查看数据)status为unpublished,可以进行的操作有编辑、删除、查看问卷。提供批量删除功能,复选框样式默认即可,无需按照设计图样式。没有问卷时,表单不会显示,页面会显示一个大大的新建问卷按钮。要创建和编辑问卷,请单击问卷管理列表中的新建。点击按钮后,进入新建问卷页面。点击问卷列表中某行问卷的编辑按钮后,进入该问卷的编辑页面。新建页面和编辑页面基本一样。添加、删除和修改问题。每份问卷至少有一个问题,最多有十个问题,被删除的操作不会向上移动最上面的问题,也不会向下移动底部问题。当您点击重用时,与重用问题(包括选项)完全相同的新问题将立即添加到重用问题下方。)对于单选题和多选题,可以对题型的选项进行增删改、排序等操作。无论是否需要,都可以设置文本问题。有问卷填写截止时间,使用日历组件来选择时间,日期选择不能早于当前日期。保存问卷并保存问卷。发布问卷可以使问卷状态变为发布状态。点击发布时,如果截止日期早于当前日期,需要提示修改截止日期删除问卷在问卷管理列表中点击某个问卷的删除按钮后,会弹出一个弹层,让用户再次确认是否删除问卷。如果用户点击是,问卷将被删除。在问卷管理列表中查看问卷点击按钮查看问卷后,会在新窗口打开问卷页面。该页面是用户填写问卷的页面。当问卷未发布或未完成时,问卷提交无效。该页面需要很好的兼容移动端,支持查看数据。点击问卷管理列表中的查看数据按钮后,会进入数据报表页面,将每道单选题和多选题的选择以图表的形式呈现出来,如设计稿所示。Presentation,每道题都用右边的某种图表来呈现答题情况,自己选择合适的图表,设计稿只是为了说明,图表风格不需要和设计稿保持一致。单项选择题推荐使用饼图,多项选择题使用条形图。使用百分比图表显示文本问题的有效答案的比例。点击返回按钮返回列表页面。在项目中尝试模块化方法和工具。CSS预处理工具在项目中尝试项目构建打包工具问题全选功能实现总结首先,每个列表项使用v-model进行双向数据绑定,传递是否被选中

  • 然后也用v-model绑定全选按钮是否全选-selectedstate接下来在computedselectAll中定义三个计算属性:是否全选selectCount:计算选中了多少项selectGroup:存储当前选中的项,以便对selectAll进行操作Computedproperty:selectAll:{get(){//this.qsList是一个数组,可以看做是[{checked:false},{checked:false}理解代码时]returnthis.selectCount===this.qsList.length&&this.selectCount!==0;},set(value){this.qsList.forEach(item=>{item.checked=value;});返回值;}}通过get方法获取当前选中的数字,这样当列表项全部选中时,自动选中全选按钮。通过set方法,当全选按钮被选中时,所有的列表项也被选中。selectCount计算属性selectCount(){leti=0;this.qsList.forEach(item=>{if(item.check编辑)我++;});返回我;},计算当前选中了多少项,selectAll通过这个变量计算当前是否所有列表项都被选中selectGroup计算属性selectGroup(){letgroup=[];this.qsList.forEach(item=>{if(item.checked)group.push(item);});返回组;}存储选中的item,统一操作检测是否填写了表单中的必填项。我使用了v-model来解决,问卷中的表单项一共有三种,radio,checkbox,textarea,因为radio的v-model只能绑定一种基本类型的value,而checkbox的v-model应该绑定数组。项目会被一个一个的推入数组,是双向绑定的。textarea的v-model也应该是基本类型。我设置字符串

    //获取需要的item并存储到对象中,相当于{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(letiinthis.requiredItem){if(this.requiredItem[i].length===0)returnfalse}returntrue}这里还有一个问题,我现在在v-for中使用v-if来判断表单项的类型,这看起来有点多余,为什么不直接动态绑定它设置类型来渲染表单项,这样就不需要v-if

    这样看起来简洁多了,但是会报错,v-modelcannotbeboundtoformitemtypeattributeisadynamicvalue,即type为动态值的表单项bind不能使用v-model,所以这里只能退一步,使用v-if来选择渲染哪种类型的表单项延迟执行功能当用户点击删除一个item时,会弹出一个弹层到询问用户是否删除,用户点击确定然后删除这时候只需要给确定按钮绑定一个点击事件就可以删除操作,但是当你想多次点击确定进入下一步,或者页面有多个操作事件弹出这个弹窗层,那么OK按钮需要判断绑定哪个操作事件等等,很快就会变得很复杂这里可以使用ES6的Generator功能,可以轻松解决这个问题
    提示X

    {{info}}

    确定Cancel
弹出层内容data(){return{qsList:[],showDialog:false,//是否显示弹出层iterator:{},//currentIteratorinfo:''//弹出层提示内容}}dataindata*delItem(num){yieldthis.showDialogMsg('确认删除此问卷')yield(()=>{letindex=0;for(letlength=this.qsList.length;index{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}})}})();yieldthis.showDialog=false;可以看到页面上的多个操作都绑定了一个弹出层来实现最大程度的复用而不冲突,只要将当前要执行的操作的迭代器赋值给确定的按钮,确认按钮就可以执行next方法,而v-for会在每次渲染元素时自动执行一个函数。有时我们需要在v-for的每次遍历中执行一个函数。我们可以这样做{{doSomething()}}但是如果这个方法实现的比较复杂,容易出现一些死循环等错误,不推荐。可以考虑在js中再次遍历数据,然后在遍历中,对每一项进行操作,watch无意造成死循环错误。在编辑问卷的功能中,题号会随着问题的上移、下移、复用、删除、创建等操作而变化,我用watch来监控变化。然后改题号watch:{'$route':'fetchData',qsItem:{handler(newVal){newVal.question.forEach((item,index)=>{item.num=`Q${index+1}`})},deep:true}}我上移、下移、删除、新建题等都没有问题,但是复用操作时出现死循环问题重用
重用按钮,重用方法copy(index,qs){if(this.questionLength===10)returnalert('问卷已满!')this.qsItem.question.splice(index,0,qs)}这样写好像没什么问题。当item下的复用按钮被点击时,将这个item添加到你的nextitem但是将qs添加到watch监控的变量中后,会触发watch方法改变topicnumber,即topicnumberofqs改变了,而qs是被点击的item,它们之间存在引用,会导致qs话题号的改变,使得被点击的item的title号一起改变,所以当item改变时,watch是再次触发。同时,由于物品的题号一起变化,所以题号不是其正确的题号。触发watch后,item的titlenumber会变成Theoriginal,因为有qs的引用,所以会相应变化,然后再触发watch....解决办法是用Object.assign()进行深拷贝,使得qs和item之间没有引用copy(index,qs){if(this.questionLength===10)returnalert('问卷已满!')qs=Object.assign({},qs)this.qsItem.question.splice(index,0,qs)}不推荐这种方式,因为这种情况下不适合使用watch,而且很容易出现意想不到的问题。推荐的方法是把watch中的方法封装成一个函数,每次操作的时候调用这个函数。当然,仍然需要Object.assign()来释放重用元素之间的绑定。这里我还是用watch的方式,不推荐练习。总结完成,交作业。