表单生成器(大学组) 介绍 小蓝现在遇到一个需求,要求是设计在线动态表单生成器,通过请求到的 JSON 数据即快速生成网页表单,这可把他难倒了。请你按照题目中给出的 Vue 组件与 JSON 数据格式,帮助小蓝完成多选框、下拉框的代码编写。
准备 开始答题前,需要先打开本题的项目代码文件夹,目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ├── components │ ├── checkbox.js │ ├── rate.js │ └── select.js ├── css │ ├── icon │ │ ├── star.svg │ │ └── star_full.svg │ ├── index.css │ ├── rate.css │ └── reset.css ├── data.json ├── effect-1.gif ├── effect-2.gif ├── index.html └── lib ├── axios.min.js └── vue.min.js
其中:
index.html 是主页面。
components 是项目组件文件夹。
lib 是存放项目依赖的文件夹。
css 是存放项目样式的文件夹。
data.json 是请求需要用到 JSON 数据。
effect-1.gif 和 effect-2.gif 是项目完成后的效果图。
请通过 VS Code 中的 live server 插件启动本项目,让项目运行起来,效果如下:
注意:一定要通过 live server 插件启动项目,避免项目无法访问,影响做题。
项目中的评分组件(rate.js)已完成。
目标 找到 index.html 以及 components 下面的多选框组件(checkbox.js)和下拉框组件(select.js)中的 TODO 部分,达成以下目标:
请仔细阅读接口设计,完成 index.html 中的 TODO 部分,根据请求回来的 JSON 数据中 type 的不同,动态生成对应的表单组件。完成多选框组件(checkbox.js)和下拉框组件(select.js)初始化时需要定义的 data 以及方法,将数据正确显示到对应的组中,确保页面无报错且组件正常显示。
接口数据设计如下:
type 表示组件类型
title 为表单项的名称
field 为数据所对应的原始字段名
value 为表单项对应的值,有多种类型
options 有选项的表单选择内容
具体各个组件的数据结构说明如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 { type : 'checkbox' title : 表单项名称, 例:选择美食 field : 对应接口字段, 例:likes value : 多选选中的值, 例:["spn" ,"oaj" ] options : 选项值,例: [ { "name" : "黄金糕" , "label" : "hjg" }, { "name" : "双皮奶" , "label" : "spn" }, { "name" : "蚵仔煎" , "label" : "oaj" }, { "name" : "龙须面" , "label" : "lxm" } ] }
1 2 3 4 5 6 7 8 9 10 11 { type : 'select' title : 表单项名称, 例:参加活动 field : 对应接口字段, 例:activity value : 选中的值, 例:online options : 选项值,例: [ { "label" : "online" , "name" : "线上活动" }, { "label" : "ground" , "name" : "地推活动" }, { "label" : "topic" , "name" : "线下主题活动" } ] }
1 2 3 4 5 6 { type : 'rate' title : 表单项名称, 例:评分 field : 对应接口字段, 例:score value : 分值 1 ~5 , 例:5 }
目标 1 完成后效果如下:
继续完善多选框组件(checkbox.js)和下拉框组件(select.js), 使组件数据改变时在 index.html(即父组件中)点击获取数据按钮能够正常获取表单数据。
目标 2 完成后效果 :
在 index.html(即父组件) 中点击渲染数据,表单中的数据(即对应的子组件数据)需要正确显示。
目标 3 完成后效果(考生可以根据提供的 JSON 数据自行测试) :
。
规定
请严格按照考试步骤操作,切勿修改考试默认提供项目中的文件名称、文件夹路径、class 名、id 名、图片名等,以免造成无法判题通过。
满足需求后,保持 Web 服务处于可以正常访问状态,点击「提交检测」系统会自动检测。
判分标准
完成目标 1,得 10 分。
目标 2 中每完成一个组件,得 5 分,共 10 分。
完成目标 3,得 5 分。
总通过次数: 1 | 总提交次数: 1 | 通过率: 100%
难度: 中等 标签: 蓝桥杯, 2023, 国赛, Web 前端, Vue.js
题解 index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 <!DOCTYPE html> <html lang="zn-CH"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <script src="./lib/vue.min.js"></script> <script src="./lib/axios.min.js"></script> <link rel="stylesheet" href="./css/index.css" /> <title>表单生成器</title> </head> <body> <div id="app"> <form> <div class="left-box"> <!-- 组件容器 --> <div v-for="(item, i) in formData" :key="`${i}item`" class="item"> <!-- 此处 label 为表单的标题 --> <div class="label">{{item.title}}</div> <!-- TODO: 根据 formData 中的 type 不同完成组件渲染 --> <my-select v-if="item.type === 'select'" :options="item.options" v-model="item.value"></my-select> <my-checkbox v-if="item.type === 'checkbox'" v-for="radio in item.options" :key="radio" :label="radio.label" v-model="item.value">{{ radio.name }}</my-checkbox> <my-rate v-if="item.type === 'rate'" v-model="item.value"></my-rate> </div> </div> <!-- 表单设置 --> <div class="right-box"> <div> <button class="btn" @click="fetchData">初始化</button> <button class="btn" id="result" @click="confirm">获得数据</button> <button class="btn" id="render" @click="setData">渲染数据</button ><br /><br /> </div> <textarea v-model="testData" cols="45" rows="12"></textarea> <div class="error" v-if="hasError">JSON 格式输入有误,请仔细检查</div> </div> </form> </div> <script src="./components/checkbox.js"></script> <script src="./components/select.js"></script> <script src="./components/rate.js"></script> <script> var app = new Vue({ el: "#app", data: { formData: [], testData: "", hasError: false, }, created() { this.fetchData(); }, methods: { async fetchData() { // 请求 JSON 数据 const { data } = await axios.get("./data.json"); this.formData = data.result; this.confirm(); }, // 设置数据 setData(e) { e && e.preventDefault(); try { const newData = JSON.parse(this.testData); this.formData = this.formData.map((x) => { x.value = newData[x.field]; return x; }); this.hasError = false; } catch (err) { if (err) { this.hasError = true; } } }, // 获得数据 confirm(e) { e && e.preventDefault(); const params = {}; this.formData.forEach(({ field, value }) => { params[field] = value; }); this.testData = JSON.stringify(params, null, 2); }, }, }); </script> </body> </html>
checkbox.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 const checkboxTemplate = ` <div class="radio-box"> <input type="checkbox" :checked="isCheck" @change="change" :id="label"> <label :for="label" class="radio-stype check-box"></label><span> <slot /> </span> </div> ` ;Vue .component ("my-checkbox" , { template : checkboxTemplate, props : ["label" , "value" ], data ( ) { return { isCheck : false , checkList : [] } }, mounted ( ) { this .isCheck = this .value .includes (this .label ); }, watch : { value (newValue ) { this .isCheck = newValue.includes (this .label ); } }, computed : { }, methods : { change ( ) { console .log (this .label , this .value ); if (this .value .includes (this .label )) { this .value = this .value .filter (item => item !== this .label ) } else { this .value .push (this .label ) } console .log (this .label , this .value ); this .$emit("input" , this .value ); } } });
select.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 const selectTemplate = ` <div class="select"> <div class="input" @click="openValue"> <input id="selectVal" v-model="content" type="text" placeholder="请点击选择"> <span class="tri" :class="{tri_up: show}"></span> </div> <div class="list" v-show="show"> <ul> <li @click="getvalue(item)" v-for="(item,index) in listData" :key="item.index">{{ item.name }}</li> </ul> </div> </div> ` ;Vue .component ("my-select" , { template : selectTemplate, props : ["options" , "value" ], data ( ) { return { content : '' , show : false , listData : [], label : '' } }, watch : { value ( ) { this .options .forEach (item => { if (item.label === this .value ) { this .content = item.name } }) }, options ( ) { this .listData = this .options this .listData .forEach ((item, index ) => { item.index = index }) } }, mounted ( ) { this .options .forEach (item => { if (item.label === this .value ) { this .content = item.name } }) this .listData = this .options this .listData .forEach ((item, index ) => { item.index = index }) }, methods : { openValue ( ) { this .show = !this .show }, getvalue (item ) { this .content = item.name this .show = !this .show this .$emit('input' , item.label ) } } });
rate.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 const rateTemplate = ` <div class="rate"> <div @click="select(i)" v-for="i in 5" :key="i+'s'" class="item"> <object class="icon" :data="getPath(i)" type="image/svg+xml"></object> </div> </div> ` ;Vue .component ("my-rate" , { template : rateTemplate, props : ["value" ], data ( ) { return { curCount : 0 , }; }, watch : { value ( ) { this .curCount = this .value ; }, }, mounted ( ) { this .curCount = this .value ; }, methods : { select (i ) { this .curCount === i ? (this .curCount = 0 ) : (this .curCount = i); this .$emit("input" , this .curCount ); }, getPath (i ) { return `css/icon/star${i <= this .curCount ? "_full" : "" } .svg` ; }, }, });