前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用Sqlite3+Express.js+React实现在线答题(下)

使用Sqlite3+Express.js+React实现在线答题(下)

作者头像
fanzhh
发布2019-08-20 11:26:51
3K0
发布2019-08-20 11:26:51
举报

使用Sqlite3+Express.js+React实现在线答题(上)中,我们将题目数据从word文件转为txt格式并导入到sqlite3中,使用Express.js建立了json数据API接口。本篇文章我们将使用ReactJS建立前端。

建立React项目

首先安装create-react-app,如果你已安装,请略过。

代码语言:javascript
复制
$ npm install -g create-react-app

然后新建项目,我们项目的名字为frontend

代码语言:javascript
复制
$ create-react-app frontend

安装过程需要几分钟:

2018-01-31 13-49-01屏幕截图.png

从服务器获取json数据我们需要用jquery,安装

代码语言:javascript
复制
npm install query

界面设计

我们预想的操作界面是这样的(原谅我粗狂的画风^^):

sketch-1517379201030.png

每道题在一个<div>中,上面是题目描述部分,下面是选择框。

修改App.js

修改frontend/src/App.js文件。

导入

代码语言:javascript
复制
import React, { Component } from 'react';
import $ from 'jquery';
import './App.css';

题目描述部件

代码语言:javascript
复制
class DescriptionBar extends Component {
  render() {
    return <p>{this.props.description}</p>
  }
}

答案选择部件

代码语言:javascript
复制
class SelectionsBar extends Component {
  constructor(props) {
    super(props);
    this.handleChange=this.handleChange.bind(this); // 为事件绑定this
  }
  handleChange(event) {
    this.props.onChange(event) // 答案选择触发事件传递给props中的onChange
  }
  render() {
    var selection_type = this.props.remark === '1' ? 'checkbox' : 'radio'; // 多选题使用checkbox,单选题使用radio,注意判断题也是单选
    var selection_name = this.props.reamrk === '1' ? 'choose_mul' : 'choose_one'
    return (
      <form>
        <fieldset>
          <input name={selection_name} type={selection_type} id={this.props.id+'_A'} value='A' onChange={this.handleChange} /><label htmlFor={this.props.id+'_A'}>{this.props.answerA}</label><br /> // 每道题至少两个选择项A和B
          <input name={selection_name}  type={selection_type} id={this.props.id+'_B'} value='B'  onChange={this.handleChange} /><label htmlFor={this.props.id+'_B'}>{this.props.answerB}</label><br /> // 控件ID设为题目的ID+该控件的符号(A?B?C?...)
          {this.props.answerC === '' ? '' : (<span><input name={selection_name}  type={selection_type} id={this.props.id+'_C'} value='C'  onChange={this.handleChange} /><label htmlFor={this.props.id+'_C'}>{this.props.answerC}</label><br /></span>)} // C以下根据内容不为空则显示
          {this.props.answerD === '' ? '' : (<span><input name={selection_name}  type={selection_type} id={this.props.id+'_D'} value='D'  onChange={this.handleChange} /><label htmlFor={this.props.id+'_D'}>{this.props.answerD}</label><br /></span>)}
          {this.props.answerE === '' ? '' : (<span><input name={selection_name}  type={selection_type} id={this.props.id+'_E'} value='E'  onChange={this.handleChange} /><label htmlFor={this.props.id+'_E'}>{this.props.answerE}</label><br /></span>)}
        </fieldset>
      </form>
    )
  }
}

提交部件

代码语言:javascript
复制
class SubmitBar extends Component {
  constructor(props) {
    super(props);
    this.onClick=this.onClick.bind(this);
  }
  onClick(event) {
    this.props.onClick(event) // 提交事件传递给父部件
  }
  render() {
    return(
      <form>
        <button type="submit" onClick={this.onClick} >{this.props.answered?'再做一遍错题':'检查'}</button> // 根据父控件状态判断现在是检查之前还是之后,相应改变按钮文字
      </form>
    )
  }
}

问题部件

问题部件是题目描述和答案选择的父部件。

代码语言:javascript
复制
class QuestionBar extends Component {
  render() {
    return (
      <div>
        <DescriptionBar description={this.props.question.description} /> // 题目描述部件
        <SelectionsBar // 选择部件
            id={this.props.question.id} // 传递属性值
            answer={this.props.question.answer}
            answerA={this.props.question.A}
            answerB={this.props.question.B}
            answerC={this.props.question.C}
            answerD={this.props.question.D}
            answerE={this.props.question.E}
            remark={this.props.question.remark}
            onChange={this.props.onChange}
          />
          {this.props.answered ? (this.props.question.answer===this.props.answer.answer? ('') : (<p  style={{"color":"red"}}>正确答案:{this.props.question.answer}</p>) ) : ('')} // 如果当前已经检查,且回答与正确答案不符,则以红色显示正确的答案。
      </div>
      )
  }
}

整体App部件

代码语言:javascript
复制
class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      questions: [], // 初始题目集
      current_questions: [], // 当前题目集,加载页面后与初始题目集相同,检查后则只保留错题
      answered: false, // 当前答题状态
      answers: [], // 答案集
    }
    this.handleChange = this.handleChange.bind(this);
    this.handleCheckClick = this.handleCheckClick.bind(this);
  }
  componentDidMount() { // 部件加载后获取数据
    var that = this;
    const url = 'http://localhost:3000/data/';
    $(function(){$.ajax({ // 这里很关键不要写错,$(function(){}困扰了我N天,:-<
      headers: {
        'Content-Type': 'application/json',
      },
      url: url,
      type: "GET",
      dataType: "json",
      data: {},
      success: function(result) {
        that.setState({questions:result,current_questions:result,});
        var answers = [];
        result.forEach((r)=>{
          answers.push({'id':r.id,'answer':''})
        });
        that.setState({answers:answers,});
      },
      error: function(xhr, status, err) {
        console.log(err.Message);
      },
    })})
  }
  handleChange(event) { // 选择控件的相应事件
    const id = parseInt(event.target.id.split('_')[0]); // 由控件ID获得题目ID和所作选择
    const selection = event.target.id.split('_')[1];
    const type = event.target.type;
    var answers = this.state.answers;
    if (type==='radio') { // 单选题直接给答案赋值
      answers.find(answer=>answer.id===id).answer = selection;
    } else {
      if (event.target.checked) { // 多选题,如果勾选
        if (!answers.find(answer=>answer.id===id).answer.includes(selection)){
          var tmp = answers.find(answer=>answer.id===id).answer + selection;
          tmp = tmp.split('').sort().join(''); // 赋值前排序,考虑到用户奇怪的操作方式,想想:ABC===ACB吗?
          answers.find(answer=>answer.id===id).answer = tmp;
        }
      } else { // 如果去掉勾选,答案中也要相应删除
        if (answers.find(answer=>answer.id===id).answer.includes(selection)){
          answers.find(answer=>answer.id===id).answer = answers.find(answer=>answer.id===id).answer.replace(selection,'')
        }
      }
    }
    this.setState({answers:answers,})
  }
  handleCheckClick(event) { // 检查按钮的相应事件
    event.preventDefault();
    if (event.target.innerHTML==='检查') {
      this.setState({answered:true,});
    } else { // 若是再做一遍错题,则需要根据正确与否更新错题库
      var current_questions = [];
      var answers = [];
      this.state.current_questions.forEach((question)=> {
        if (this.state.answers.find(answer=>answer.id===question.id).answer!==question.answer) {
          current_questions.push(question);
          answers.push({'id':question.id,'answer':''})
        }
      });
      this.setState({current_questions:current_questions,answers:answers,answered:false,})
    }
  }
  render() {
    var questions = [];
    this.state.current_questions.forEach((question)=>{
      questions.push(<div className="box effect2"><QuestionBar key={question.id} question={question} answer={this.state.answers.find(answer=>answer.id===question.id)} answered={this.state.answered} onChange={this.handleChange} /></div>)
    })
    return <div><div>{questions}</div><div className="box effect2"><SubmitBar answered={this.state.answered} onClick={this.handleCheckClick} /></div></div>
  }
}

export default App;

OK。

Github

这个项目我放在github上了,地址在这儿

演示地址

点击这儿可以查看heroku上的演示(题库数据量较大,加载大概需要十几秒钟)。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.01.31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 建立React项目
  • 界面设计
  • 修改App.js
    • 导入
      • 题目描述部件
        • 答案选择部件
        • 提交部件
        • 问题部件
        • 整体App部件
        • Github
        • 演示地址
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档