uniapp + node.js 开发问卷调查小程序

前后端效果图
在这里插入图片描述
后端:nodejs 12.8 ; mongoDB 4.0
前端:uniapp
开发工具:HBuilderX 3.99

  • 前端首页代码 index.vue
<!-- 源码下载地址  https://pan.baidu.com/s/1AVB71AjEX06wpc4wbcV_tQ?pwd=l9zp --><template><view class="container"><view class="content"><view class="question" v-for="(item,index) in qusetionList" :key='index'><view class="question_header"><view class="header_title">{{item.subjectContent}}<text style="font-weight: 500;">({{item.type==0?'单选':'多选'}})</text></view></view><view class="question_option"><view :class="{option_item:true,active_option:items.id==items.active}"v-for="(items,indexs) in item.optionList" :key='indexs' @tap.stop="optionItem(items)"><view class="option_box"><image src="@/static/hook.png" mode=""></image></view><text>{{items.optionContent}}</text></view></view></view><view style="height: 180rpx;"><!-- 占位框,避免内容被提交按键遮挡 --></view></view><!-- 底部提交按键,@tap.stop阻止冒泡事件 --><view class="submit_box" @longpress="goAdmin"><button class="sub_btn" type="default" @tap.stop="subQuestion">提交</button></view></view>
</template><script>export default {data() {return {baseUrl:'',active: 0,qusetionList: [],}},onLoad() {// 获取全局变量 baseUrlthis.baseUrl = getApp().globalData.baseUrl;// 调用方法this.getData()},methods: {//获取用户信息getUserInfo(param) {},//获取题目、选项getData() {uni.request({url: this.baseUrl + 'query',method: "GET",data: {},success: (res) => {var arr =res.data.dataArrvar dataList = arr.sort(this.compare('sort')) //按对象内的sort字段进行排序数组// 每个问卷都加上状态字段activefor (let i in dataList) {var optionList = []for (let j in dataList[i].optionList) {dataList[i].optionList[j].active = ''optionList.push(dataList[i].optionList[j])}dataList[i].optionList = optionList}this.qusetionList = dataList},fail: () => {uni.showToast({title: "网络请求失败!",icon: 'none',duration: 2000})}})},//--- 数组内的对象按某个字段进行排序 ---//compare(property){return function(a,b){var value1 = a[property];var value2 = b[property];return value1 - value2;  //升序,  降序为value2 - value1}},// 选择及未选择样式切换optionItem(param) {// 根据每个字段的id作为唯一状态标识是否选中this.active = param.idfor (var i in this.qusetionList) {// 单项选择if (this.qusetionList[i].type == 0) {if (this.qusetionList[i].groudId == param.subjectId) {for (var j in this.qusetionList[i].optionList) {if (this.qusetionList[i].optionList[j].id == param.id && this.qusetionList[i].optionList[j].active =='') {this.qusetionList[i].optionList[j].active = param.id} else {this.qusetionList[i].optionList[j].active = ''}}}// 多项选择} else if (this.qusetionList[i].type == 1) {for (var j in this.qusetionList[i].optionList) {if (this.qusetionList[i].optionList[j].id == param.id) {if (this.qusetionList[i].optionList[j].active == '') {this.qusetionList[i].optionList[j].active = param.id} else if (this.qusetionList[i].optionList[j].active != '') {this.qusetionList[i].optionList[j].active = ''}}}}}},// 提交问卷subQuestion() {var subTime = Date.now()var userName = '名字' + subTime.toString ().slice(-3)var activeQuestion = [] //已选择的数据列表// 循环判断active是否为空,单选和多选因为传参格式需要区分判断for (var i in this.qusetionList) {// 单选判断循环if (this.qusetionList[i].type == 0) {for (var j in this.qusetionList[i].optionList) {if (this.qusetionList[i].optionList[j].active != '') {// 把已选择的数据追加到列表activeQuestion.push({subTime:subTime,userName: userName,// groudId: this.qusetionList[i].groudId,sort: this.qusetionList[i].sort,subjectContent: this.qusetionList[i].subjectContent,optionContent: this.qusetionList[i].optionList[j].optionContent})}}} else {// 多选判断循环,选项ID以逗号拼接成字符串var optionArr = []for (var j in this.qusetionList[i].optionList) {if (this.qusetionList[i].optionList[j].active != '') {// optionArr.push(this.qusetionList[i].optionList[j].id)optionArr.push(this.qusetionList[i].optionList[j].optionContent)}}// 把已选择的数据追加到列表if (optionArr != '') {activeQuestion.push({subTime:subTime,userName: userName,// groudId: this.qusetionList[i].groudId,sort: this.qusetionList[i].sort,subjectContent: this.qusetionList[i].subjectContent,//optionId: optionArr.join()optionContent:optionArr.join()})}}}//console.log(activeQuestion)if(activeQuestion.length < this.qusetionList.length){uni.showToast({title: "问题还没有回答完!",icon: 'none',duration: 2000});} else {//提交数据给后端uni.request({url: this.baseUrl + 'addAnswer',method: 'POST',header: {'content-type' : "application/x-www-form-urlencoded"},data: {formData: JSON.stringify(activeQuestion) //转换为JSON格式字符串},success: (res) => {// 服务器返回数据,后续业务逻辑处理console.log(res)// 调用方法,刷新数据this.getData()uni.showToast({title: "保存成功", icon : "success",duration:3000})},fail: (err) => {console.log(err)uni.showToast({ title: "服务器响应失败,请稍后再试!", icon : "none",})},complete: () => {}})}},// 跳转到页面goAdmin() {uni.navigateTo({url: '../admin/admin'})}}}
</script><style lang="less" scoped>.question {.question_header {// height: 90rpx;固定高度之后,长内容换行不能自动增加高度background-color: #f1f1f1;font-size: 34rpx;font-weight: 700;color: #333333;.header_title {width: 95%;margin-left: 37rpx;line-height: 90rpx;}}.question_option {width: 650rpx;margin-top: 7rpx;// background-color: #F0AD4E;display: flex;justify-content: space-between;flex-wrap: wrap;margin: 0 auto;margin-bottom: 40rpx;.option_item {width: 300rpx;margin-top: 34rpx;// background-color: #DD524D;font-size: 30rpx;color: #666666;display: flex;align-items: center;.option_box {width: 35rpx;height: 35rpx;border: 1rpx solid #999999;border-radius: 5px;margin-right: 10rpx;// background-color: #FF852A;display: flex;justify-content: center;align-items: center;image {width: 20rpx;height: 20rpx;}}}}}.active_option {.option_box {background: linear-gradient(-30deg, #ff7029 0%, #faa307 100%);border: 1rpx solid #faa307 !important;}text {color: #ff7029;}}.submit_box {width: 750rpx;height: 160rpx;background-color: #F1F1F1;position: fixed;bottom: 0;}.sub_btn {width: 80%;height: 88rpx;background: linear-gradient(-30deg, #dc4011 0%, #faa307 100%);border-radius: 44rpx;margin: 40rpx auto;font-size: 32rpx;font-weight: 700;color: #ffffff;text-align: center;line-height: 88rpx;}// 按钮原生会存在上下黑线,该属性去除button::after {border: none;}
</style>
  • 后台管理部分页面代码 charts.vue
<template><view><block v-for="(item,index) in dataList" :key="index"><view style="margin: 50rpx;">{{item.subjectContent}}</view><canvas :canvas-id="'id'+index" style="width: 350px; height: 300px;" ></canvas></block></view>
</template><script>// 引入外部 jsimport canvas from '@/static/canvas.js'export default {data() {return {baseUrl: '',dataList: []}},onReady() {// 获取全局变量 baseUrlthis.baseUrl = getApp().globalData.baseUrl;// 调用方法this.getData()},methods: {//从后端获取数据getData() {uni.showLoading({title: '数据加载中...'})uni.request({url: this.baseUrl + 'queryByGroup',method: "GET",data: {},success: (res) => {//console.log(res)let tempArr = res.datathis.dataList = tempArrlet arr = tempArr.sort(this.compare('sort')) //按对象内的sort字段进行排序数组// 延迟1秒等待canvas组件渲染完成,再调用方法绘画,否则绘画不成功setTimeout(function(){for (let x in arr) {// 调用外部方法并传入参数: canvas-id,数组,总数量canvas.canvasGraph('id'+x, arr[x].list, arr[x].list[0].total)}},1000)},fail: (err) => {uni.showToast({title: "网络请求失败!",icon: 'none',duration: 2000})},complete: () => {setTimeout(function(){uni.hideLoading()},1000)}})},//--- 数组内的对象按某个字段进行排序 ---//compare(property){return function(a,b){var value1 = a[property];var value2 = b[property];return value1 - value2;  //升序,  降序为value2 - value1}}}}
</script><style></style>
  • 后端使用 nodejs + mongoDB 搭建服务
  • 程序入口文件 app.js
const express = require('express');
const cors=require('cors');
const bodyParser = require('body-parser');
const app = express();//全局变量,数据库地址
global.G_url = "mongodb://127.0.0.1:27017";//处理跨域
app.use(cors()) 
//对post请求的请求体进行解析
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())//设置share文件夹下的所有文件能通过网址访问,用作静态文件web服务
app.use(express.static("./share"))//路由配置
const index=require('./routes/index.js')
const query=require('./routes/query.js')
const add=require('./routes/add.js')
const del=require('./routes/del.js')
const edit=require('./routes/edit.js')
const update=require('./routes/update.js')
const addAnswer=require('./routes/addAnswer.js')
const queryAnswer=require('./routes/queryAnswer.js')
const queryByGroup=require('./routes/queryByGroup.js')
const delAll=require('./routes/delAll.js')app.use('/index',index)
app.use('/query',query)
app.use('/add',add)
app.use('/del',del)
app.use('/edit',edit)
app.use('/update',update)
app.use('/addAnswer',addAnswer)
app.use('/queryAnswer',queryAnswer)
app.use('/queryByGroup',queryByGroup)
app.use('/delAll',delAll)//启动服务器
app.listen(3000,()=>{console.log('http://127.0.0.1:3000')
})
  • 对原始数据按题目名称进行分组,然后追加需要用到的字段,再把处理好的数据发给前端进行渲染。
// queryByGroup.jsconst express = require('express');
const router = express.Router();
const MongoClient = require("mongodb").MongoClient;const url = G_url; //G_url是全局变量,在app.js定义router.get('/', function(req, res, next) {// 调用方法dataOperate()/*操作数据库,异步方法*/async function dataOperate() {var allArr = []var arr = nullvar conn = nulltry {conn = await MongoClient.connect(url)// 定义使用的数据库和表const dbo = conn.db("mydb").collection("answer")// 查询所有arr = await dbo.find().toArray()// 调用 byGroup方法对原始数组按指定字段进行分组let groupBySubjectContent = byGroup(arr, 'subjectContent')// 循环执行for (var n in groupBySubjectContent) {let subjectContent = groupBySubjectContent[n].subjectContentlet nameList = groupBySubjectContent[n].list// 从原数组中过滤字段等于subjectContent ,取最后一个元素let lastArr = (arr.filter(item => item.subjectContent == subjectContent)).slice(-1)let sort = lastArr[0].sort// 计算数组中某个元素的累计数量let countedNameObj = nameList.reduce((prev, item) => {if (item in prev) {prev[item]++} else {prev[item] = 1}return prev}, {})// 一个对象分割为多个对象let list = []for (var key in countedNameObj) {var temp = {}temp.title = keytemp.money = countedNameObj[key]list.push(temp)}// 所有对象 money字段求和let listSum = list.reduce((prev, item) => {prev += item.moneyreturn prev}, 0)// 对象循环追加键值对for (var k in list) {list[k].total = listSumlist[k].value = (list[k].money / listSum).toFixed(4) //计算比例,保留4位小数list[k].color = randomColor(k) //指定颜色//list[k].color = '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).substr(-6) //随机颜色}// 对象追加到数组allArr.push({"sort": sort,"subjectContent": subjectContent,"list": list})}//给前端返回数据res.send(allArr)} catch (err) {console.log("错误:" + err.message)} finally {//关闭数据库连接if (conn != null) conn.close()}}/*** 数据按字段分组处理* @param arr [Array] 被处理的数组* @param group_key [String] 分组字段*/function byGroup(arr, group_key) {let map = {}let res = []for (let i = 0; i < arr.length; i++) {let ai = arr[i]if (!map[ai[group_key]]) {// map[ai[group_key]] = [ai] //原始代码//optionContent是要筛选出来的字段map[ai[group_key]] = ai.optionContent.split(',')} else {// map[ai[group_key]].push(ai) //原始代码// split()通过指定分隔符对字符串进行分割,生成新的数组; arr = [...arr, ...arr2]  数组合并map[ai[group_key]] = [...map[ai[group_key]], ...ai.optionContent.split(',')]}}Object.keys(map).forEach(item => {res.push({[group_key]: item,list: map[item]})})return res}/**随机指定颜色**/function randomColor(index) {let colorList = ["#63b2ee","#76da91","#f8cb7f","#7cd6cf","#f89588","#9192ab","#efa666","#7898e1","#eddd86","#9987ce","#76da91","#63b2ee"]// let index = Math.floor(Math.random() * colorList.length)return colorList[index]}});module.exports = router;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/240951.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

十二、Qt 操作PDF文件(2)

一、在《十、Qt 操作PDF文件-CSDN博客》中我们用Poppler类库打开了PDF文件&#xff0c;并显示到窗体上&#xff0c;但只能显示一页&#xff0c;功能还没完善&#xff0c;在本章节中&#xff0c;加入了&#xff1a; 通过选择框选择PDF文件并打开&#xff0c;默认打开第一页。通…

Spring Boot 配置文件和日志

目录 配置文件格式 properties配置文件说明 1.properties基本语法 2.读取配置文件 3.properties缺点 yml配置文件说明 1.yml基本语法 2.配置不同数据类型 3.字符串特殊情况 4.配置对象 properties和yml对比 日志 日志的使用 日志级别 日志持久化 Lombok Lombo…

C++大学教程(第九版)5.18进制表

目录 题目 代码 运行截图 题目 &#xff08;进制表&#xff09;编写一个程序要求打印一张表&#xff0c;内容是1~256范围内每个十进制数对应的二进制、八进制和十六进制形式。如果还不熟悉这些计数系统&#xff0c;可先阅读附录 D。提示:可以使用流操纵符dec、oct 和 hex来…

网络部署实战具体学习内容总结

网络部署实战具体学习内容总结 &#x1f4bb;网络部署实战课程通常旨在教授学生如何规划、配置、维护和优化计算机网络。这些课程涵盖了广泛的主题&#xff0c;以确保学生具备网络部署和管理所需的技能。 网络部署实战课程具体学习内容&#x1f447; 1️⃣网络架构设计及网络原…

ARM 1.12

norflash与nandflash的区别&#xff1a; 一、NAND flash和NOR flash的性能比较 1、NOR的读速度比NAND稍快一些。 2、NAND的写入速度比NOR快很多。 3、NAND的4ms擦除速度远比NOR的5s快。 4、大多数写入操作需要先进行擦除操作。 5、NAND的擦除单元更小&#xff0c;相应的擦除电…

yolo9000:Better, Faster, Stronger的目标检测网络

目录 一、回顾yolov1二、yolov2详细讲解2.1 Better部分创新点&#xff08;1&#xff09;Batch Normalization(批量归一化)&#xff08;2&#xff09;High Resolution Classifier---高分辨率分类器&#xff08;3&#xff09;Anchor Boxes---锚框&#xff08;4&#xff09;Dimens…

4D毫米波雷达——原理、对比、优势、行业现状

前言 4D 毫米波雷达是传统毫米波雷达的升级版&#xff0c;4D指的是速度、距离、水平角度、垂直高度四个维度。 相比传统 3D 毫米波雷达&#xff0c;4D 毫米波雷达增加了“高度”的探测&#xff0c;将第四个维度整合到传统毫米波雷达中。 4D毫米波雷达被视为未来车载雷达的一…

Elasticsearch:和 LIamaIndex 的集成

LlamaIndex 是一个数据框架&#xff0c;供 LLM 应用程序摄取、构建和访问私有或特定领域的数据。 LlamaIndex 是开源的&#xff0c;可用于构建各种应用程序。 在 GitHub 上查看该项目。 安装 在 Docker 上设置 Elasticsearch 使用以下 docker 命令启动单节点 Elasticsearch 实…

【Go面试向】rune和byte类型的认识与使用

【Go】rune和byte类型的认识与使用 大家好 我是寸铁&#x1f44a; 总结了一篇rune和byte类型的认识与使用的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; byte和rune类型定义 byte,占用1个字节&#xff0c;共8个比特位&#xff0c;所以它实际上和uint8没什么本质区别,它表示…

基于Docker的Nginx的安装与配置

基于Docker的Nginx的安装与配置 1 为Nginx创建一个容器1.1 学习docker run1.2 通过docker run为Nginx创建并启动一个容器 2 配置Nginx2.1 学习docker的bind mount技术2.2 在Nginx容器中找到想修改的文件所在的目录2.2.1 认识nginx.conf文件2.2.2 访问Nginx服务&#xff0c;默认…

【陈老板赠书活动 - 22期】- 人工智能(第三版)

陈老老老板&#x1f9d9;‍♂️ &#x1f46e;‍♂️本文专栏&#xff1a;赠书活动专栏&#xff08;为大家争取的福利&#xff0c;免费送书&#xff09; &#x1f934;本文简述&#xff1a;活就像海洋,只有意志坚强的人,才能到达彼岸。 &#x1f473;‍♂️上一篇文章&#xff…

学习JavaEE的日子 day13 封装 static private this 类加载机制

Day13 1. private – 私有化 理解&#xff1a;private是访问修饰符的一种&#xff0c;访问修饰符规定了访问权限. 作用&#xff1a; ​ 1.private修饰属性&#xff1a;该属性只能在类的内部使用 ​ 2.private修饰方法&#xff1a;该方法只能在类的内部使用 应用场景&#xff1…

【Flutter 问题系列第 80 篇】TextField 输入框组件限制可输入的最大长度后,输入的内容中包含表情符号时,获取输入的内容数还是会超出限制的问题

这是【Flutter 问题系列第 80 篇】&#xff0c;如果觉得有用的话&#xff0c;欢迎关注专栏。 博文当前所用 Flutter SDK&#xff1a;3.10.5、Dart SDK&#xff1a;3.0.5 一&#xff1a;问题描述 在输入用户名称、简介等内容时&#xff0c;一般我们都会限制输入框内最大可输入…

011:vue结合css动画animation实现下雪效果

文章目录 1. 实现效果2. 编写一个下雪效果组件 VabSnow.vue3. 页面使用4. 注意点 1. 实现效果 GIF录屏文件太卡有点卡&#xff0c;实际是很丝滑的 2. 编写一个下雪效果组件 VabSnow.vue 在 src 下新建 components 文件&#xff0c;创建VabSnow.vue组件文件 <template>…

系分备考计算机网络传输介质、通信方式和交换方式

文章目录 1、概述2、传输介质3、网络通信4、网络交换5、总结 1、概述 计算机网路是系统分析师考试的常考知识点&#xff0c;本篇主要记录了知识点&#xff1a;网络传输介质、网络通信和数据交换方式等。 2、传输介质 网络的传输最常见的就是网线&#xff0c;也就是双绞线&…

k8s---ingress对外服务(ingress-controller)

ingress 概念 k8s的对外服务&#xff0c;ingress service作用现在两个方面&#xff1a; 1、集群内部&#xff1a;不断跟踪的变化&#xff0c;更新endpoint中的pod对象&#xff0c;基于pod的ip地址不断变化的一种服务发现机制。 2、集群外部&#xff1a;类似于负载均衡器&a…

球幕影院气膜:未来娱乐的奇妙之旅

球幕影院气膜&#xff1a;未来娱乐的奇妙之旅 在科技日新月异的时代&#xff0c;娱乐体验的创新与演变从未停歇。气膜球幕影院&#xff0c;作为一项领航未来的前沿科技&#xff0c;正以其沉浸感和颠覆性的观影体验&#xff0c;吸引着人们驻足体验。 创新科技的巅峰之作 气膜球幕…

ubuntu开放ssh服务

&#x1f4d1;前言 本文主要是【ubuntu】——ubuntu开放ssh服务的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一…

港科夜闻|香港科大团队研发多功能,可重构和抗破坏单线感测器阵列

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科大团队研发多功能、可重构和抗破坏单线感测器阵列。研究人员开发出一种受人类听觉系统启发的感测器阵列设计技术。透过模仿人耳根据音位分布来区分声音的能力&#xff0c;这种新型感测器阵列方法可能优化感测器阵列…

【JaveWeb教程】(26) Mybatis基础操作(新增、修改、查询、删除) 详细代码示例讲解(最全面)

目录 1. Mybatis基础操作1.1 需求1.2 准备1.3 删除1.3.1 功能实现1.3.2 日志输入1.3.3 预编译SQL1.3.3.1 介绍1.3.3.2 SQL注入1.3.3.3 参数占位符 1.4 新增1.4.1 基本新增1.4.2 主键返回 1.5 更新1.6 查询1.6.1 根据ID查询1.6.2 数据封装1.6.3 条件查询1.6.4 参数名说明 1. Myb…