前后端效果圖
后端:nodejs 12.8 ; mongoDB 4.0
前端:uniapp
開發(fā)工具:HBuilderX 3.99文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-799413.html
- 前端首頁(yè)代碼 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;">
<!-- 占位框,避免內(nèi)容被提交按鍵遮擋 -->
</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() {
// 獲取全局變量 baseUrl
this.baseUrl = getApp().globalData.baseUrl;
// 調(diào)用方法
this.getData()
},
methods: {
//獲取用戶信息
getUserInfo(param) {},
//獲取題目、選項(xiàng)
getData() {
uni.request({
url: this.baseUrl + 'query',
method: "GET",
data: {},
success: (res) => {
var arr =res.data.dataArr
var dataList = arr.sort(this.compare('sort')) //按對(duì)象內(nèi)的sort字段進(jìn)行排序數(shù)組
// 每個(gè)問卷都加上狀態(tài)字段active
for (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: "網(wǎng)絡(luò)請(qǐng)求失敗!",
icon: 'none',
duration: 2000
})
}
})
},
//--- 數(shù)組內(nèi)的對(duì)象按某個(gè)字段進(jìn)行排序 ---//
compare(property){
return function(a,b){
var value1 = a[property];
var value2 = b[property];
return value1 - value2; //升序, 降序?yàn)関alue2 - value1
}
},
// 選擇及未選擇樣式切換
optionItem(param) {
// 根據(jù)每個(gè)字段的id作為唯一狀態(tài)標(biāo)識(shí)是否選中
this.active = param.id
for (var i in this.qusetionList) {
// 單項(xiàng)選擇
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 = ''
}
}
}
// 多項(xiàng)選擇
} 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 = [] //已選擇的數(shù)據(jù)列表
// 循環(huán)判斷active是否為空,單選和多選因?yàn)閭鲄⒏袷叫枰獏^(qū)分判斷
for (var i in this.qusetionList) {
// 單選判斷循環(huán)
if (this.qusetionList[i].type == 0) {
for (var j in this.qusetionList[i].optionList) {
if (this.qusetionList[i].optionList[j].active != '') {
// 把已選擇的數(shù)據(jù)追加到列表
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 {
// 多選判斷循環(huán),選項(xiàng)ID以逗號(hào)拼接成字符串
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)
}
}
// 把已選擇的數(shù)據(jù)追加到列表
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 {
//提交數(shù)據(jù)給后端
uni.request({
url: this.baseUrl + 'addAnswer',
method: 'POST',
header: {'content-type' : "application/x-www-form-urlencoded"},
data: {
formData: JSON.stringify(activeQuestion) //轉(zhuǎn)換為JSON格式字符串
},
success: (res) => {
// 服務(wù)器返回?cái)?shù)據(jù),后續(xù)業(yè)務(wù)邏輯處理
console.log(res)
// 調(diào)用方法,刷新數(shù)據(jù)
this.getData()
uni.showToast({
title: "保存成功",
icon : "success",
duration:3000
})
},
fail: (err) => {
console.log(err)
uni.showToast({
title: "服務(wù)器響應(yīng)失敗,請(qǐng)稍后再試!",
icon : "none",
})
},
complete: () => {
}
})
}
},
// 跳轉(zhuǎn)到頁(yè)面
goAdmin() {
uni.navigateTo({
url: '../admin/admin'
})
}
}
}
</script>
<style lang="less" scoped>
.question {
.question_header {
// height: 90rpx;固定高度之后,長(zhǎng)內(nèi)容換行不能自動(dòng)增加高度
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;
}
// 按鈕原生會(huì)存在上下黑線,該屬性去除
button::after {
border: none;
}
</style>
- 后臺(tái)管理部分頁(yè)面代碼 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>
// 引入外部 js
import canvas from '@/static/canvas.js'
export default {
data() {
return {
baseUrl: '',
dataList: []
}
},
onReady() {
// 獲取全局變量 baseUrl
this.baseUrl = getApp().globalData.baseUrl;
// 調(diào)用方法
this.getData()
},
methods: {
//從后端獲取數(shù)據(jù)
getData() {
uni.showLoading({
title: '數(shù)據(jù)加載中...'
})
uni.request({
url: this.baseUrl + 'queryByGroup',
method: "GET",
data: {},
success: (res) => {
//console.log(res)
let tempArr = res.data
this.dataList = tempArr
let arr = tempArr.sort(this.compare('sort')) //按對(duì)象內(nèi)的sort字段進(jìn)行排序數(shù)組
// 延遲1秒等待canvas組件渲染完成,再調(diào)用方法繪畫,否則繪畫不成功
setTimeout(function(){
for (let x in arr) {
// 調(diào)用外部方法并傳入?yún)?shù): canvas-id,數(shù)組,總數(shù)量
canvas.canvasGraph('id'+x, arr[x].list, arr[x].list[0].total)
}
},1000)
},
fail: (err) => {
uni.showToast({
title: "網(wǎng)絡(luò)請(qǐng)求失敗!",
icon: 'none',
duration: 2000
})
},
complete: () => {
setTimeout(function(){
uni.hideLoading()
},1000)
}
})
},
//--- 數(shù)組內(nèi)的對(duì)象按某個(gè)字段進(jìn)行排序 ---//
compare(property){
return function(a,b){
var value1 = a[property];
var value2 = b[property];
return value1 - value2; //升序, 降序?yàn)関alue2 - value1
}
}
}
}
</script>
<style>
</style>
- 后端使用 nodejs + mongoDB 搭建服務(wù)
- 程序入口文件 app.js
const express = require('express');
const cors=require('cors');
const bodyParser = require('body-parser');
const app = express();
//全局變量,數(shù)據(jù)庫(kù)地址
global.G_url = "mongodb://127.0.0.1:27017";
//處理跨域
app.use(cors())
//對(duì)post請(qǐng)求的請(qǐng)求體進(jìn)行解析
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
//設(shè)置share文件夾下的所有文件能通過(guò)網(wǎng)址訪問,用作靜態(tài)文件web服務(wù)
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)
//啟動(dòng)服務(wù)器
app.listen(3000,()=>{
console.log('http://127.0.0.1:3000')
})
- 對(duì)原始數(shù)據(jù)按題目名稱進(jìn)行分組,然后追加需要用到的字段,再把處理好的數(shù)據(jù)發(fā)給前端進(jìn)行渲染。
// queryByGroup.js
const 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) {
// 調(diào)用方法
dataOperate()
/*操作數(shù)據(jù)庫(kù),異步方法*/
async function dataOperate() {
var allArr = []
var arr = null
var conn = null
try {
conn = await MongoClient.connect(url)
// 定義使用的數(shù)據(jù)庫(kù)和表
const dbo = conn.db("mydb").collection("answer")
// 查詢所有
arr = await dbo.find().toArray()
// 調(diào)用 byGroup方法對(duì)原始數(shù)組按指定字段進(jìn)行分組
let groupBySubjectContent = byGroup(arr, 'subjectContent')
// 循環(huán)執(zhí)行
for (var n in groupBySubjectContent) {
let subjectContent = groupBySubjectContent[n].subjectContent
let nameList = groupBySubjectContent[n].list
// 從原數(shù)組中過(guò)濾字段等于subjectContent ,取最后一個(gè)元素
let lastArr = (arr.filter(item => item.subjectContent == subjectContent)).slice(-1)
let sort = lastArr[0].sort
// 計(jì)算數(shù)組中某個(gè)元素的累計(jì)數(shù)量
let countedNameObj = nameList.reduce((prev, item) => {
if (item in prev) {
prev[item]++
} else {
prev[item] = 1
}
return prev
}, {})
// 一個(gè)對(duì)象分割為多個(gè)對(duì)象
let list = []
for (var key in countedNameObj) {
var temp = {}
temp.title = key
temp.money = countedNameObj[key]
list.push(temp)
}
// 所有對(duì)象 money字段求和
let listSum = list.reduce((prev, item) => {
prev += item.money
return prev
}, 0)
// 對(duì)象循環(huán)追加鍵值對(duì)
for (var k in list) {
list[k].total = listSum
list[k].value = (list[k].money / listSum).toFixed(4) //計(jì)算比例,保留4位小數(shù)
list[k].color = randomColor(k) //指定顏色
//list[k].color = '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).substr(-6) //隨機(jī)顏色
}
// 對(duì)象追加到數(shù)組
allArr.push({
"sort": sort,
"subjectContent": subjectContent,
"list": list
})
}
//給前端返回?cái)?shù)據(jù)
res.send(allArr)
} catch (err) {
console.log("錯(cuò)誤:" + err.message)
} finally {
//關(guān)閉數(shù)據(jù)庫(kù)連接
if (conn != null) conn.close()
}
}
/**
* 數(shù)據(jù)按字段分組處理
* @param arr [Array] 被處理的數(shù)組
* @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是要篩選出來(lái)的字段
map[ai[group_key]] = ai.optionContent.split(',')
} else {
// map[ai[group_key]].push(ai) //原始代碼
// split()通過(guò)指定分隔符對(duì)字符串進(jìn)行分割,生成新的數(shù)組; arr = [...arr, ...arr2] 數(shù)組合并
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
}
/**隨機(jī)指定顏色**/
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;
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-799413.html
到了這里,關(guān)于uniapp + node.js 開發(fā)問卷調(diào)查小程序的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!