软件工程2017第4次作业(第2组)

小组成员:(组长:周慧微;组员:曹洪茹、杨亮、陆祈桢、王圣奇)

小组Git链接:https://github.com/zhouhuiwei/weChat

 

项目要求:(10个基本功能+1个附加功能)

做成微信小程序,强化社交功能(参考跳一跳)。

1、 利用微信账号进行用户管理

2、 小程序分无尽模式和考试模式(娱乐和练习),无尽模式可以选择题目困难等级(简单/中等/困难)

3、 无尽模式得分模式设计:答对得一分,连答分数与连续答对次数相同,答错停止答题,显示得分

4、 无尽模式参与微信好友排名

5、 考试模式用户自由选择运算符(类型和个数)以及出题数目

6、 考试模式可以设置时间限制

7、 每套考试题结束后出现统计页面,显示正确率

8、 可以打印一套试卷,导出(复制黏贴)到微信小程序之外

9、 可以从程序内一键分享到微信聊天框;分享内容为无尽模式的得分或考试模式的正确率

10、 无尽模式一周清空排名,重新记录,同时保留历史最高成绩

*附加:程序界面支持多种语言

【注】小程序自身限制,无法直接分享到朋友圈等,故功能9已经稍作修改。

 

项目进度安排:

2月10号之前交第一版,实现无尽模式和考试模式 

——更新:在GitHub链接里的压缩包部分

2月20号之前交第二版,修改第一版意见,并增加分享和导出试卷,排名功能。

——更新:用户调查:没有发现bug,能够正常实现功能。

2月25日前修改第二版意见,提交最终稿

——更新:将无尽模式得分方式进行修改:

简单模式对一题累计1分,中等模式对一题累计得2分,困难模式对一题累计3分。

 

人员分工 :

功能点分配如下:

1、3、4:杨亮、曹洪茹  

5、7、8:周慧微  

2、6:王圣奇  

9、10:陆祈桢  

【注】随时交流,多多调整

更新:工作量总结:

周慧微22%
杨亮20%
曹洪茹20%
王圣奇19%
陆祁桢19%

前后台负责人如下:

前台:周慧微

后台(用java编写):杨亮、曹洪茹

 更新:

wxml是用html写,wxss是用css写,

           js、服务端是用JavaScript写。

 

界面原型设计:

使用墨刀和微信小程序提供基础按钮设计

代码规范:

Java编码规范——Alibaba

使用阿里代码规范检查工具,《阿里巴巴Java开发规约》插件

编程都符合相应的软件开发规范。从软件工程课程中习得,这样方便后续维护修改,尤其是团队编程过程中极为重要,可以提高效率。

 

小程序页面设计+核心代码(包括部分功能的测试):

 1、 功能点1_利用微信登录部分:

通过群内分享进入小程序的用户,将另外获取群ID并排名

    // 登录
    wx.login({
      success: res => {
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
        //获取openid
        wx.request({
          url: that.globalData.host + "GetOpenId",
          data: res,
          method: 'POST',
          header: { 'content-type': "application/x-www-form-urlencoded" },
          success: function (e) {
            console.log(e.data);
            that.globalData.secret = e.data;
            // 获取用户信息,并上传服务器
            wx.getUserInfo({
              success: res => {
                // 可以将 res 发送给后台解码出 unionId
                that.globalData.userInfo = res.userInfo
                //将用户信息openID,nickName,avatarUrl,上传服务器
                let cs = {};
                console.log("以下是globalData的数据:");
                console.log(that.globalData);
                cs.nickName = that.globalData.userInfo.nickName;
                cs.openID = that.globalData.secret.openid;
                cs.avatarUrl = that.globalData.userInfo.avatarUrl;
                wx.request({
                  url: that.globalData.host + "InsertUserInfoszys",
                  data: cs,
                  method: 'POST',
                  header: { 'content-type': "application/x-www-form-urlencoded" },
                  success: function (e) {
                    console.log(e);
                  }
                });
                // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
                // 所以此处加入 callback 以防止这种情况
                if (this.userInfoReadyCallback) {
                  this.userInfoReadyCallback(res)
                }
                //获取群id,前提是通过群排名进入小程序的用户,否则下面的步骤都不会执行
                wx.getShareInfo({
                  shareTicket: options.shareTicket,
                  success: function (e) {
                    console.log(e);
                    let cs = e;
                    cs['session_key'] = that.globalData.secret.session_key;
                    wx.request({
                      url: that.globalData.host + "JieMi",
                      data: cs,
                      method: 'POST',
                      header: { 'content-type': "application/x-www-form-urlencoded" },
                      success: function (e) {
                        console.log(e);
                        that.globalData.openGId = e.data.openGId;//群id

                      }
                    });
                  }
                });//end of wx.getShareInfo
              }
            });//end of wx.getUserInfo
          }
        });
      }
    });//end of wx.login
登陆_获取用户数据

 

 2、功能点2_小程序两种模式

【考试模式】  【无尽模式】  三档难度

 计算式生成(没有重复题目),并且计算正确答案

const formatNumber = n => {
  n = n.toString()
  return n[1] ? n : '0' + n
}
function getNum(weishu) {//this.data.weishu
  //获取一个操作数
  return Math.floor(Math.random() * Math.pow(10, weishu));
}
function getYunsuanfu(yunsuanfu) {
  //获取运算符
  var length = yunsuanfu.length;//this.data.yunsuanfu
  var mark = Math.floor(Math.random() * length);
  return yunsuanfu[mark];
}
function getBiaodashi(yunsuanshu, yunsuanfu, weishu, timu, correct_result, xiaoshuwei) {//参与运算的数字个数this.data.yunsuanshu,参与运算的运算符数组this.data.yunsuanfu,参与运算的数字位数this.data.weishu,当前的题库this.data.timu,正确答案数组this.data.correct_result,除法答案保留的小数位this.data.xiaoshuwei
  //获取表达式
  var biaodashi = "";
  for (var i = 0; i < yunsuanshu; i++) {
    biaodashi = biaodashi + getNum(weishu) + getYunsuanfu(yunsuanfu);
  }
  biaodashi = biaodashi.slice(0, -1);//把多余的运算符取消掉
  biaodashi = biaodashi + "=";//给表达式增加一个"="
  console.log(biaodashi);
  var result = judge_biaodashi(biaodashi.slice(0, -1),timu);
  if (result === false) {
    return getBiaodashi(yunsuanshu, yunsuanfu, weishu, timu, correct_result, xiaoshuwei);
  } else {
    
    result = Math.round(result * Math.pow(10, xiaoshuwei)) / Math.pow(10, xiaoshuwei);
    correct_result.push(result);
    //this.setData({ correct_result: tmp });
    //this.setData({ biaodashi: biaodashi });
    timu.push(biaodashi);
    //this.setData({ timu: tm });
    return {correct_result: correct_result,biaodashi: biaodashi, timu: timu};
  }
}
function judge_biaodashi(biaodashi,timu) {//表达式本身,当前的题库
  //验证表达式的结果是否正常,不能为负数,不能是Infinity,不能是NaN,不能超过设置的运算位数
  //不能有重复题目
  var result = calCommonExp(biaodashi);
  console.log(result);
  if (!isFinite(result)) {
    return false;
  }
  if (isNaN(result)) {
    return false;
  }
  if (result < 0) {
    return false;
  }
  if (timu.indexOf(biaodashi + "=") !== -1) {//不能有重复题目
    return false;
  }
  return result;
}
//以下代码是框架的代码,借用一下rpn.js
function isOperator(value) {
  var operatorString = '+-*/()×÷';
  return operatorString.indexOf(value) > -1;
}
function getPrioraty(value) {
  if (value == '-' || value == '+') {
    return 1;
  } else if (value == '*' || value == '/' || value == '×' || value == '÷') {
    return 2;
  } else {
    return 0;
  }
}
function prioraty(v1, v2) {
  return getPrioraty(v1) <= getPrioraty(v2);
}
function outputRpn(exp) {
  var inputStack = [];
  var outputStack = [];
  var outputQueue = [];
  var firstIsOperator = false;
  exp.replace(/\s/g, '');
  if (isOperator(exp[0])) {
    exp = exp.substring(1);
    firstIsOperator = true;
  }
  for (var i = 0, max = exp.length; i < max; i++) {
    if (!isOperator(exp[i]) && !isOperator(exp[i - 1]) && (i != 0)) {
      inputStack[inputStack.length - 1] = inputStack[inputStack.length - 1] + exp[i] + '';
    } else {
      inputStack.push(exp[i]);
    }
  }
  if (firstIsOperator) {
    inputStack[0] = -inputStack[0]
  }
  while (inputStack.length > 0) {
    var cur = inputStack.shift();
    if (isOperator(cur)) {
      if (cur == '(') {
        outputStack.push(cur);
      } else if (cur == ')') {
        var po = outputStack.pop();
        while (po != '(' && outputStack.length > 0) {
          outputQueue.push(po);
          po = outputStack.pop();
        }
      } else {
        while (prioraty(cur, outputStack[outputStack.length - 1]) && outputStack.length > 0) {
          outputQueue.push(outputStack.pop());
        }
        outputStack.push(cur)
      }
    } else {
      outputQueue.push(Number(cur));
    }
  }
  if (outputStack.length > 0) {
    while (outputStack.length > 0) {
      outputQueue.push(outputStack.pop());
    }
  }
  return outputQueue;
}
function calRpnExp(rpnArr) {
  var stack = [];
  for (var i = 0, max = rpnArr.length; i < max; i++) {
    if (!isOperator(rpnArr[i])) {
      stack.push(rpnArr[i]);
    } else {
      var num1 = stack.pop();
      var num2 = stack.pop();
      if (rpnArr[i] == '-') {
        var num = num2 - num1;
      } else if (rpnArr[i] == '+') {
        var num = num2 + num1;
      } else if (rpnArr[i] == '*' || rpnArr[i] == '×') {
        var num = num2 * num1;
      } else if (rpnArr[i] == '/' || rpnArr[i] == '÷') {
        var num = num2 / num1;
      }
      stack.push(num);
    }
  }
  return stack[0];
}
function calCommonExp(exp) {
  var rpnArr = outputRpn(exp);
  return calRpnExp(rpnArr)
}
function simplyBiaodashi(timu){
  var sz = timu;
  var tm = sz[0].slice(0, -1);
  for (var i = 1; i < sz.length; i++) {
    tm = tm + "," + sz[i].slice(0, -1);
  }
  return tm;
计算正确答案

 界面设置

<!--index.wxml-->
<view class="page" wx:if="{{isAuthority=='是'}}" xmlns:wx="http://www.w3.org/1999/xhtml">
  <view class="kj">
    <view class='title'>欢迎使用考试模式</view>
    <view class='shuoming'>*使用前,请先进行配置</view>
    <view class='peizhi'>

    <picker range="{{array_yunsuanshu}}" value="{{index_yunsuanshu}}" mode='selector' bindchange='yunsuanshuchanged'>
      <view class='peizhi-item'>
        <view class='peizhi-item-left'>
          <view>运算数</view>
          <view>参与运算的数字个数</view>
        </view>
        <view class='peizhi-item-middle'>
          <!-- <picker range="{{array_yunsuanshu}}" value="{{index_yunsuanshu}}" mode='selector'> -->
            <view class='picker'>{{array_yunsuanshu[index_yunsuanshu]}}个</view>
          <!-- </picker> -->
        </view>
        <view class='peizhi-item-right'><image style='width: 50rpx;height: 50rpx' src='/images/more.png'></image></view>
      </view>
    </picker>
      <view class='line'></view>

    <picker range="{{array_timushu}}" value="{{index_timushu}}" mode='selector' bindchange='timushuchanged'>
      <view class='peizhi-item'>
        <view class='peizhi-item-left'>
          <view>题目数量</view>
          <view>考试的题目数量</view>
        </view>
        <view class='peizhi-item-middle'>
          <view class='picker'>{{array_timushu[index_timushu]}}个</view>
        </view>
        <view class='peizhi-item-right'><image style='width: 50rpx;height: 50rpx' src='/images/more.png'></image></view>
      </view>
    </picker>
      <view class='line'></view>

      <picker range="{{array_weishu}}" value="{{index_weishu}}" mode='selector' bindchange='weishuchanged'>
      <view class='peizhi-item'>
        <view class='peizhi-item-left'>
          <view>位数</view>
          <view>参与运算的数字位数</view>
        </view>
        <view class='peizhi-item-middle'>
            <view class='picker'>{{array_weishu[index_weishu]}}位</view>
        </view>
        <view class='peizhi-item-right'><image style='width: 50rpx;height: 50rpx' src='/images/more.png'></image></view>
      </view>
      </picker>
      <view class='line'></view>
     
      <view class='peizhi-item' bindtap='yunsuanfu_change'>
        <view class='peizhi-item-left'>
          <view>运算符</view>
          <view>参与运算的运算符</view>
        </view>
        <view class='peizhi-item-middle'>
          {{array_yunsuanfu_result}}
        </view>
        <view class='peizhi-item-right'><image style='width: 50rpx;height: 50rpx' src='/images/more.png'></image></view>
      </view>
      <view class='line'></view>
    <picker range="{{array_time}}" value="{{index_time}}" mode='selector' bindchange='timechanged'>
      <view class='peizhi-item'>
        <view class='peizhi-item-left'>
          <view>考试时间</view>
          <view>设置考试所用时间</view>
        </view>
        <view class='peizhi-item-middle'>
          <view class='picker'>{{array_time[index_time]}}分钟</view>
        </view>
        <view class='peizhi-item-right'><image style='width: 50rpx;height: 50rpx' src='/images/more.png'></image></view>
      </view>
    </picker>
      <view class='line'></view>

      <view class='peizhi-item'>
        <view class='peizhi-item-left'>
          <view>结果只保留整数</view>
          <view>计算结果为小数,四舍五入得到整数</view>
        </view>
        <view class='peizhi-item-right peizhi-item-right2'><!-- 这里增加了一个类名,因为与上面的布局不同,上面的布局,主轴上有三个元素,宽度是80%,10%,10%,而现在这个布局,主轴上只有两个元素,宽度是80%,20% -->
          <switch checked='{{switchState}}' bindchange='switchChange'></switch>
        </view>
        
      </view>
      
      <view class='line'></view>
      <picker range="{{array_xiaoshuwei}}" value="{{index_xiaoshuwei}}" mode='selector' bindchange='xiaoshuweichanged'>
      <view class='peizhi-item' wx:if="{{!switchState}}">
        <view class='peizhi-item-left'>
          <view>保留小数位数</view>
          <view>运算结果经四舍五入后,保留的小数位数</view>
        </view>
        <view class='peizhi-item-middle'>
          <view class='picker'>{{array_xiaoshuwei[index_xiaoshuwei]}}个</view>
        </view>
        <view class='peizhi-item-right'><image style='width: 50rpx;height: 50rpx' src='/images/more.png'></image></view>
      </view>
      </picker>
      <view class='line' wx:if="{{!switchState}}"></view>
    </view>
    <view><button bindtap='btStart'>开始运算</button></view>
     <view><button bindtap='btExport' style='margin-top: 20rpx;'>导出题目</button></view> 
    <view class='yunsuanfu' wx:if="{{show_yunsuanfu}}">
      <view>
        <view class='cancel' bindtap='cancelTap'>取消</view>
        <view class='confirm' bindtap='confirmTap'>确定</view>
      </view>
      <checkbox-group bindchange="yunsuanfu_changed">
        <view wx:for="{{array_yunsuanfu}}">
          <checkbox value='{{item.name}}' checked='{{item.checked}}'>{{item.name}}</checkbox>
        </view>
      </checkbox-group>
      
      
    </view>
  </view>
</view>
<view wx:else></view>
考试模式界面设置
<!--pages/endless/endless.wxml-->
<view class='title'>欢迎进入无尽模式,请选择难度</view>
<view>
  <button data-grade='easy' bindtap='select'>简单</button>
  <button data-grade='normal' bindtap='select'>一般</button>
  <button data-grade='hard' bindtap='select'>困难</button>
</view>
无尽模式界面设置

 

/* pages/endless/endless.wxss */
button {
  margin-top: 20rpx;
}
.title {
  margin-top: 30rpx;
  font-size: 36rpx;
  text-align: center;
  color: gray;
}

 

3、功能点3_无尽模式得分设计

【简单】两个一位运算数,只有加减法,每对一题累计1分

【一般】三个一位运算数,加减乘,每对一题累计2分

【困难】三个两位运算数,加减乘,每对一题累计3分

三种模式都是:错一题即停止答题,并跳出结果页面。

可以直接分享,邀请群友一起参与,或在“我的”里分享到指定群里查看群排名。

1)做题页面,显示做题时间和已得分数:

                                       

2)结果页面:(包含分享和结果复制)

简单模式:每对一题得1分 

一般模式:每对一题计2分   

困难模式:每对一题计3分 

// pages/endless/endless.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
  
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
  
  },
  select: function(e) {
    var grade=e.target.dataset.grade;
    if(grade==='easy'){
      //简单模式
      wx.navigateTo({
        url: '/pages/main_endless/main_endless?yunsuanshu=' + 2 + "&weishu=" + 1 + "&yunsuanfu=" + '+,-' + "&timushu=" + 10000 + "&xiaoshuwei=" + 0+"&mode=endless"+"&timushu=-1"+"&time=-1"+"&grade="+grade
      });
    } else if (grade === 'normal'){
      //一般模式
      wx.navigateTo({
        url: '/pages/main_endless/main_endless?yunsuanshu=' + 3 + "&weishu=" + 1 + "&yunsuanfu=" + '+,-,×' + "&timushu=" + 10000 + "&xiaoshuwei=" + 0 + "&mode=endless" + "&timushu=-1" + "&time=-1" + "&grade=" + grade
      });
    } else if (grade === 'hard') {
      //困难模式
      wx.navigateTo({
        url: '/pages/main_endless/main_endless?yunsuanshu=' + 3 + "&weishu=" + 2 + "&yunsuanfu=" + '+,-,×' + "&timushu=" + 10000 + "&xiaoshuwei=" + 0 + "&mode=endless" + "&timushu=-1" + "&time=-1" + "&grade=" + grade
      });
    } else {
      console.log("未知难度");
    }
  }
})
无尽模式_三档难度

 

// pages/result/result.js
var util = require('../../utils/util.js');
Page({

  /**
   * 页面的初始数据
   */
  data: {
    result:[],//将以上结果合并后的结果
    haoshi:"",//耗费的时间
    num:0,//题目数量
    correct_num:0,//回答正确的数量
    comment:"",//对于这个正确率,给一个评语鼓励之类的话,或者是名人名言,对于儿童,一个漫画是更好的选择
    headImage:"",//用户头像的url
    mode:'',//用户选择的模式
    score: 0,//历史最高得分
    time: 0,//在exam模式中,规定的考试时间
    timushu:0, //在exam模式中规定的题目数量
    xishu: 0,//根据难度等级,得到得分系数
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    //根据难度等级,计算得分系数
    if(options.grade==='easy'){
      this.setData({xishu:1,mode:'简单'});
    } else if (options.grade === 'normal'){
      this.setData({ xishu: 2 ,mode:'一般'});
    }else{
      this.setData({ xishu: 3 ,mode:'困难'});
    }
    var that=this;
    var app = getApp();
    this.setData({ headImage: app.globalData.userInfo.avatarUrl });
    //获取用户操作的信息
    console.log(options);
    var begin_time=0;
    var end_time=Number(options.haoshi)*1000;
    this.setData({haoshi:util.calcHaoshi(begin_time,end_time)});//将耗时进行格式化,用于显示
    var timu=options.timu.split(",");
    var answer=options.answer.split(",");
    var correct_result=options.correct_result.split(",");
    var result=[];
    var obj={};
    for(var i=0;i<timu.length;i++){
      obj={timu:timu[i],answer:answer[i],correct_result:correct_result[i]};
      result.push(obj);
    }
    this.setData({result:result});
    this.setData({correct_num: this.calcCorrectNum(result)});
    this.setData({num:result.length});
    this.setData({time: options.time,timushu: options.timushu});
    var score = wx.getStorageSync('score') || 0;
    if(score<this.data.correct_num){
      wx.setStorageSync('score', this.data.correct_num);
      this.setData({ score: this.data.correct_num});
    }else {
      this.setData({ score: score });
    }
    //将本次得分上传服务器
    let cs={};
    cs['openID']=getApp().globalData.secret.openid;
    cs['score']=this.data.correct_num;
    wx.request({
      url: getApp().globalData.host + "InsertScoreszys",
      data: cs,
      method: 'POST',
      header: { 'content-type': "application/x-www-form-urlencoded" },
      success: function (e) {
        console.log(e.data);
      }
    });//end of 本次得分上传服务器
  },
  
  calcCorrectNum: function(result){
    var correct_num=0;
    for(var i=0;i<result.length;i++){
      if(result[i].answer===result[i].correct_result){
        correct_num++;
      }
    }
    return correct_num;
  },
  copytap: function(){
    //导出结果
    var res=this.data.result;
    //obj={timu:timu[i],answer:answer[i],correct_result:correct_result[i]};
    var result="题目\t答案\t正确结果\t结论";
    var t="";
    for(var i=0;i<res.length;i++){
      if(res[i].answer===res[i].correct_result){
        t="正确";
      }else{
        t="错误";
      }
      result=result+"\n"+res[i].timu+"="+"\t"+res[i].answer+"\t"+res[i].correct_result+"\t"+t;
    }
    wx.setClipboardData({
      data: result,
      success: function(res){
        wx.showModal({
          title: '复制成功',
          content: '已复制,请打开记事本或Excel完成粘贴',
          showCancel: false,
          confirmText: '知道了',
          confirmColor: '#349023'
        });
      },
      fail: function(res){
        console.log(res);
        wx.showModal({
          title: '复制失败',
          content: '没有完成复制',
          showCancel: false,
          confirmText: '知道了',
          confirmColor: '#349023'
        });
      }
    });
  },
  sharetap: function(){

  },
  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {
    var score = wx.getStorageSync('score') || 0;
    
      return{
        title: '我在无尽模式中的最高得分是'+score+'分,敢来挑战吗?',
        path : '/pages/index/index'
      }
  }
})
得分上传、分享

 

两种分享结果的方法: 

                       

群内分享页面:

// pages/contact/contact.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    imgUrl: ''
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    var url=getApp().globalData.host+'GetHeadImage?username=ad1';
    this.setData({imgUrl: url});
  },
  scan: function(e) {
    console.log(e);
    wx.scanCode({
      success: function(e) {
        console.log(e);
      }
    })
  },
  copy: function(e) {
    wx.setClipboardData({
      data: 'pangxiebiancheng',
      success: function(e) {
        wx.showToast({
          title: '复制成功,请用微信搜索联系人',
        });
      },
      fail: function(e) {
        wx.showToast({
          title: '复制失败',
        });
      }
    })
  },
 
  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {
  
  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {
  
  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {
  
  }
})
搜索分享的用户

 

4、功能点4_ 无尽模式参与微信好友排名

 

群排名的原理是,通过分享一个固定的页面到群里,当群成员点击分享链接进入到小程序后,小程序会获得这个群的id,将群id与用户id保存在服务器的数据库中,服务器会返回该群的成员的排名。

// pages/paiming/paiming.js
var util = require('../../utils/util.js');
Page({

  /**
   * 页面的初始数据
   */
  data: {
    userlist:[],//返回排名信息的列表
    isShow: true,//是否显示提示信息和按钮
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onShow: function (options) {
    var that=this;
    console.log("paiming.wxml");
    setTimeout(function(){
      //将openGId与openid的关系对上传到服务器
      let cs = {};
      cs['openID'] = getApp().globalData.secret.openid;
      cs['openGId'] = getApp().globalData.openGId;
      wx.request({
        url: getApp().globalData.host + "InsertRelationshipBetweenOpenIdAndOpenGIdszys",
        data: cs,
        method: 'POST',
        header: { 'content-type': "application/x-www-form-urlencoded" },
        success: function (e) {
          console.log(e.data);
          //获取群排名
          let cs = { 'openGId': getApp().globalData.openGId };
          wx.request({
            url: getApp().globalData.host + "GetQunPaiMing",
            data: cs,
            method: 'POST',
            header: { 'content-type': "application/x-www-form-urlencoded" },
            success: function (e) {
              console.log(e.data);
              that.setData({ userlist:e.data,isShow:!that.data.isShow});
            }
          });//end of 获取群排名
        }
      });//end of openGId与openid的关系对上传到服务器
    },6000);
    
  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {
  
  },
  openMini: function(){
    wx.switchTab({
      url: '/pages/index/index',
    });
  }
})
群排名

 

 

 

点击上图下拉菜单,新建编译模式,上述填写的内容,配置好模式。选择该模式后会出现下面的界面,测试成功:

// pages/allpaiming/allpaiming.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    users:[],//用户信息
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    var that=this;
    //获取全部用户的信息
    wx.request({
      url: getApp().globalData.host + "GetAllPaiMing",
      method: 'POST',
      header: { 'content-type': "application/x-www-form-urlencoded" },
      success: function (e) {
        that.setData({users:e.data});
      }
    });//end of 获取全部用户信息
  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {
  
  }
})
全部用户排名+分享

 

页面式样实现:

/* pages/allpaiming/allpaiming.wxss */
image {
  width: 80rpx;
  height: 80rpx;
  vertical-align: middle;
}
.lb {
  display: flex;
  justify-content: space-around;
  align-items: center;
  margin-top: 20rpx;
}
所有排名_页面设计

 

系统功能测试代码:

{
    "description": "项目配置文件。",
    "setting": {
        "urlCheck": false,
        "es6": true,
        "postcss": true,
        "minified": true,
        "newFeature": true
    },
    "compileType": "miniprogram",
    "libVersion": "1.6.4",
    "appid": "wx281e819778f97d50",
    "projectname": "%E5%9B%9B%E5%88%99%E8%BF%90%E7%AE%97%E5%87%BA%E9%A2%98%E5%99%A8%E5%A8%B1%E4%B9%90%E6%A8%A1%E5%BC%8F",
    "condition": {
        "search": {
            "current": -1,
            "list": []
        },
        "conversation": {
            "current": -1,
            "list": []
        },
        "game": {
            "current": -1,
            "list": []
        },
        "miniprogram": {
            "current": -1,
            "list": [
                {
                    "id": 0,
                    "name": "群排名",
                    "pathName": "pages/paiming/paiming",
                    "query": "",
                    "scene": "1044",
                    "shareInfo": {
                        "groupName": "测试模拟群0",
                        "shareName": "8IdvumOXlRxM7zcX3Iuyhg4OXuhflgaazFh6-SbJ9rk@cr4dev",
                        "shareKey": "MUdcFDmsac0d8E4dUVOX6O9ps6yh9OYMazT7WQfTa6Ny9H1V92eO0W6HhcG1hv290XEM8BTe5xf4nW99LKPDcA~~"
                    }
                }
            ]
        }
    }
}
分享_模拟测试

 

 

5、功能点5_考试模式用户自由选择运算符(类型和个数)以及出题数目

     功能点6_考试模式可以设置时间限制

 考试模式界面:

 用户自由设置题目数量、运算符形式和考试时间;如果超时,将停止答题。

 考试模式_出题过程:

记录用户输入的答案,并且判断是否改变按钮文字;

判断是否题目回答完了,要跳转出最后结果页面。

//index.js
//获取应用实例
const app = getApp()
var util=require('../../utils/util.js');
Page({
  data: {
    motto: 'Hello World',
    userInfo: {},
    hasUserInfo: false,
    canIUse: wx.canIUse('button.open-type.getUserInfo'),
    array_yunsuanshu: [2,3,4,5],//参与运算的数字个数
    index_yunsuanshu: 0,
    array_weishu: [1,2,3,4],//参与运算的数字位数
    index_weishu: 0,  
    array_timushu: [10,15,20,25,30],//出题数量
    index_timushu: 2,
    array_xiaoshuwei: [1,2,3,4],//结果保留的小数位
    index_xiaoshuwei: 0,
    xiaoshuwei_result: 0,
    array_yunsuanfu: [{ name: '+', checked: false }, { name: '-', checked: false }, { name: '×', checked: false }, { name: '÷', checked: false }],
    array_yunsuanfu_result: ['+','-'],//默认选中的运算符是+和-
    array_yunsuanfu_tmp: [],//运算符的临时数组,用来记录checkbox-group变化时的返回值
    show_yunsuanfu: false,//运算符配置的页面默认是false,不显2
    switchState: true,
    timu:[],//题目数组
    correct_result:[],//答案数组
    array_time: [5,10,15,20,25],//待选择的考试时间,单位是分钟
    index_time: 0,//默认的array_time下标
    time_result: 300,//答题时间,用户选择时是分钟,传递参数时最终转换为秒
    isAuthority: getApp().globalData.authority,
  },
  switchChange: function(e){//结果取整
    this.setData({switchState: e.detail.value});
    if(e.detail.value==true){
      this.setData({xiaoshuwei_result:0});
    }else{
      this.setData({xiaoshuwei_result:this.data.array_xiaoshuwei[this.data.index_xiaoshuwei]});
    }
  },
  timechanged: function(e) {
    //设置考试时间
    this.setData({index_time: e.detail.value});
    var time=this.data.array_time[this.data.index_time]*60;
    this.setData({time_result:time});
  },
  //事件处理函数
  btStart: function(e) {
    wx.navigateTo({
      url: '/pages/main_exam/main_exam?yunsuanshu=' + this.data.array_yunsuanshu[this.data.index_yunsuanshu] + "&weishu=" + this.data.array_weishu[this.data.index_weishu] + "&yunsuanfu=" + this.data.array_yunsuanfu_result + "&timushu=" + this.data.array_timushu[this.data.index_timushu] + "&xiaoshuwei=" + this.data.xiaoshuwei_result + "&mode=exam" + "&time=" + this.data.time_result
    });
  }, 
  btExport: function(e){
    var num = this.data.array_timushu[this.data.index_timushu];
    for(var i=0;i<num;i++){
      var result = util.getBiaodashi(this.data.array_yunsuanshu[this.data.index_yunsuanshu], this.data.array_yunsuanfu_result, this.data.array_weishu[this.data.index_weishu], this.data.timu, this.data.correct_result, this.data.xiaoshuwei_result);//return {correct_result: correct_result,biaodashi: biaodashi, timu: timu}
    //yunsuanshu, yunsuanfu, weishu, timu, correct_result, xiaoshuwei
      this.setData({ correct_result: result.correct_result, timu: result.timu });
    }
    var timu=this.data.timu;
    var correct_result=this.data.correct_result;
    var text="题目\t正确答案";
    for(var i=0;i<timu.length;i++){
      text=text+"\n"+timu[i]+"\t"+correct_result[i];
    }
    wx.setClipboardData({
      data: text,
      success: function(){
        wx.showModal({
          title: '成功',
          content: '请打开Excel或记事本,完成粘贴',
        });
      }
    })
  },
  yunsuanfu_change: function(e){//点击了运算符配置选项后,配置页面出现了
    this.setData({show_yunsuanfu: true});
  },
  timushuchanged: function(e){
    this.setData({index_timushu:e.detail.value});
  },
  yunsuanfu_changed: function(e){//
    this.setData({array_yunsuanfu_tmp:e.detail.value});
  },
  onLoad: function () {
    console.log("获取的数据:"+this.data.isAuthority);
    this.setData({isAuthority: getApp().globalData.authority});
    wx.setNavigationBarTitle({
      title: '四则运算练习器',
    });
    
    if (app.globalData.userInfo) {
      this.setData({
        userInfo: app.globalData.userInfo,
        hasUserInfo: true
      })
    } else if (this.data.canIUse){
      // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
      // 所以此处加入 callback 以防止这种情况
      app.userInfoReadyCallback = res => {
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        })
      }
    } else {
      // 在没有 open-type=getUserInfo 版本的兼容处理
      wx.getUserInfo({
        success: res => {
          app.globalData.userInfo = res.userInfo
          this.setData({
            userInfo: res.userInfo,
            hasUserInfo: true
          })
        }
      })
    }
  },
  getUserInfo: function(e) {
    console.log(e)
    app.globalData.userInfo = e.detail.userInfo
    this.setData({
      userInfo: e.detail.userInfo,
      hasUserInfo: true
    })
  },
  confirmTap: function(e){
    //选中的运算符不能为空
    if (this.data.array_yunsuanfu_tmp.length == 0) {
      wx.showToast({
        title: '必须至少选择一个运算符',
        duration: 2000
      });
      return;
    }
    this.setData({array_yunsuanfu_result:this.data.array_yunsuanfu_tmp});
    this.setData({show_yunsuanfu:false});
  },
  cancelTap: function(e){
    this.setData({ array_yunsuanfu_result:['+','-']});
    this.setData({show_yunsuanfu:false});
  },
  yunsuanshuchanged: function(e) {
    this.setData({index_yunsuanshu:e.detail.value});
  },
  weishuchanged: function(e) {
    this.setData({index_weishu:e.detail.value});
  },
  xiaoshuweichanged: function(e) {
    this.setData({index_xiaoshuwei:e.detail.value});
    this.setData({ xiaoshuwei_result: this.data.array_xiaoshuwei[this.data.index_xiaoshuwei] });
  },
  onShareAppMessage: function () {
    return {
      title: '四则运算自动出题器',
      path: '/pages/index/index'
    }
  }
})
考试出题设置、导出
var util = require('../../utils/util.js');
var id = 0;
var num = 0;//剩余题目数量

Page({
  data: {
    yunsuanshu: 0,//参与运算的数字个数
    weishu: 0,//参与运算的数字位数
    yunsuanfu: [],//参与运算的运算符,这是个数组
    originalTimushu: 0,//原始的题目数
    timushu: 1,//题目数量
    xioashuwei: 0,//结果的保留的位数
    bt_text: "下一题",
    biaodashi: "",
    timu: [],//题干
    answer: [],//用户输入结果
    correct_result: [],//正确结果
    input: "",//用户输入的答案
    focus: true,//输入框获得焦点
    begin_time: 0,//记录开始时间
    time: 0,//初始的时间,即选定的考试时间
    daojishi: 0,//倒计时,即在页面上展示的时间
    isAuthority: getApp().globalData.authority
  },


  answer_input: function (e) {
    //用户输入时,记录输入内容
    var input = e.detail.value;
    this.setData({ input: input });
  },
  onUnload: function(e){
    clearInterval(id);
  },
  onLoad: function (option) {
    this.setData({
      yunsuanshu: option.yunsuanshu,
      weishu: option.weishu,
      yunsuanfu: option.yunsuanfu.split(","),
      timushu: option.timushu,
      originalTimushu: option.timushu,
      xiaoshuwei: option.xiaoshuwei,
      begin_time: new Date().getTime(),
      time: Number(option.time)//设置的考试时长
    });
    num = option.timushu;//剩余题目数量
    var that = this;
    var t = Number(option.time);//设置的考试时长
    console.log(option);
    id = setInterval(function () {
      t--;
      
      that.setData({ daojishi: t });
      if (t <= 0) {
        //应该停止运算了,时间到了
        clearInterval(id);
        wx.showModal({
          title: '时间到了',
          content: '时间到,点击确定查看结果',
          showCancel: false,
          success: function (res) {
            if (res.confirm) {
              var tm = util.simplyBiaodashi(that.data.timu);//将this.data.timu中的等号取消掉,否则传参数的时候,会有bug
              wx.redirectTo({
                url: '/pages/result_exam/result_exam?timu=' + tm + "&answer=" + that.data.answer + "&correct_result=" + that.data.correct_result + "&time=" + that.data.time + "&timushu=" + that.data.originalTimushu + "&haoshi=" + that.data.time * 1000//haoshi是实际用时,因为时间到,因此实际用时与考试的规定时间是一致的。
              });
            }
          }
        });
      }
    }, 1000);

    //初始化题目
    var result = util.getBiaodashi(this.data.yunsuanshu, this.data.yunsuanfu, this.data.weishu, this.data.timu, this.data.correct_result, this.data.xiaoshuwei);//return {correct_result: correct_result,biaodashi: biaodashi, timu: timu}
    this.setData({ correct_result: result.correct_result, biaodashi: result.biaodashi, timu: result.timu });
    num--;//第一道题出完,因此num--
  },
  nextTap: function () {
    //下一题
    /*
    剩余题目数-1;
    记录用户输入的答案;
    判断是否应该更改按钮的提示文字了
    判断是否该跳转到结果报告了
     */
    var ans = this.data.answer;
    ans.push(this.data.input);
    this.setData({ answer: ans });
    if (num == 0) {
      clearInterval(id);
      //页面跳转查看结果
      var haoshi = new Date().getTime() - this.data.begin_time;
      var tm = util.simplyBiaodashi(this.data.timu);//将this.data.timu中的等号取消掉,否则传参数的时候,会有bug
      var url = '/pages/result_exam/result_exam?timu=' + tm + "&answer=" + this.data.answer + "&correct_result=" + this.data.correct_result + "&time=" + this.data.time + "&timushu=" + this.data.originalTimushu + "&haoshi=" + haoshi;
      console.log(url);
      wx.redirectTo({
        url: url
      });
    } else {
      this.setData({ input: '', focus: true });//清空,还原
      this.setData({ timushu: num });
      var result = util.getBiaodashi(this.data.yunsuanshu, this.data.yunsuanfu, this.data.weishu, this.data.timu, this.data.correct_result, this.data.xiaoshuwei);//return {correct_result: correct_result,biaodashi: biaodashi, timu: timu}
      this.setData({ correct_result: result.correct_result, biaodashi: result.biaodashi, timu: result.timu });
      num--;//出完一道题,num--
    }
    if (num == 0) {
      //最后一题
      this.setData({ bt_text: "完成,查看结果" });
    }
  }
}
)
题目数量设置
<view class="page" wx:if="{{isAuthority=='是'}}" xmlns:wx="http://www.w3.org/1999/xhtml">
  <view>倒计时:{{daojishi}}秒,剩余题目:{{timushu}}道</view>
  <view class='question'>{{biaodashi}}</view>
  <input class='answer' type='text' focus='{{focus}}' type='digit' confirm-type='确定' bindinput='answer_input' value='{{input}}'></input>
  <button id='bt' bindtap="nextTap">{{bt_text}}</button>
</view>
<view wx:else>请联系微信号:pangxiebiancheng获取授权</view>
答题界面设置

 

6、功能点7_ 考试模式每套考试题结束后出现统计页面,显示正确率

// pages/result/result.js
var util=require('../../utils/util.js');
Page({

  /**
   * 页面的初始数据
   */
  data: {
    result:[],//将以上结果合并后的结果
    haoshi:"",//耗费的时间
    num:0,//题目数量
    correct_num:0,//回答正确的数量
    comment:"",//对于这个正确率,给一个评语鼓励之类的话,或者是名人名言,对于儿童,一个漫画是更好的选择
    headImage:"",//用户头像的url
    mode:'',//用户选择的模式
    score: 0,//历史最高得分
    time: 0,//在exam模式中,规定的考试时间
    timushu:0 //在exam模式中规定的题目数量
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    var that=this;
    var app=getApp();
    console.log(app.globalData);
    this.setData({headImage: app.globalData.userInfo.avatarUrl});
    
    //获取用户操作的信息
    console.log(options);
    var end_time=options.haoshi;
    this.setData({haoshi:util.calcHaoshi(0,end_time)});//将耗时进行格式化,用于显示
    var timu=options.timu.split(",");
    var answer=options.answer.split(",");
    var correct_result=options.correct_result.split(",");
    var result=[];
    var obj={};
    for(var i=0;i<timu.length;i++){
      obj={timu:timu[i],answer:answer[i],correct_result:correct_result[i]};
      result.push(obj);
    }
    this.setData({result:result});
    this.setData({correct_num: this.calcCorrectNum(result)});
    this.setData({num:result.length});
    this.setData({time: util.calcHaoshi(0,Number(options.time)*1000),timushu: options.timushu});
    var score = wx.getStorageSync('score') || 0;
    if(score<this.data.correct_num){
      wx.setStorageSync('score', this.data.correct_num);
      this.setData({ score: this.data.correct_num});
    }else {
      this.setData({ score: score });
    }

  },
  calcCorrectNum: function(result){
    var correct_num=0;
    for(var i=0;i<result.length;i++){
      if(result[i].answer===result[i].correct_result){
        correct_num++;
      }
    }
    return correct_num;
  },
  copytap: function(){
    //导出结果
    var res=this.data.result;
    //obj={timu:timu[i],answer:answer[i],correct_result:correct_result[i]};
    var result="题目\t答案\t正确结果\t结论";
    var t="";
    for(var i=0;i<res.length;i++){
      if(res[i].answer===res[i].correct_result){
        t="正确";
      }else{
        t="错误";
      }
      result=result+"\n"+res[i].timu+"="+"\t"+res[i].answer+"\t"+res[i].correct_result+"\t"+t;
    }
    wx.setClipboardData({
      data: result,
      success: function(res){
        wx.showModal({
          title: '复制成功',
          content: '已复制,请打开记事本或Excel完成粘贴',
          showCancel: false,
          confirmText: '知道了',
          confirmColor: '#349023'
        });
      },
      fail: function(res){
        console.log(res);
        wx.showModal({
          title: '复制失败',
          content: '没有完成复制',
          showCancel: false,
          confirmText: '知道了',
          confirmColor: '#349023'
        });
      }
    });
  },
  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {
    var mode=this.data.mode;
    var score = wx.getStorageSync('score') || 0;
      return {
        title :'我的本次考试中的'+this.data.num+'题中做对了'+this.data.correct_num+'题',
        path: '/pages/index/index'
      }
  }
})
View Code

7、 功能点8_可以打印一套试卷,导出(复制黏贴)到微信小程序之外

设置好后点击“导出题目”,则跳出窗口如图:  

                                          剪贴板黏贴效果: 

8、功能点9_ 可以从程序内一键分享到微信聊天框

分享内容为      考试模式的正确率                         或                       无尽模式的得分

     

<!--pages/paiming/paiming.wxml-->
<text wx:if="{{isShow}}">正在获取排名,请稍候……</text>
<view class='lb'>
  <text>排名</text>
  <view>用户</view>
  <text>得分</text>
</view>
 <block wx:for="{{userlist}}">
  <view class='lb'>
    <text>{{index+1}}</text>
    <view>
      <image src='{{item.avatarUrl}}' mode='aspectFit'></image>
      <text>{{item.nickName}}</text>
    </view>
    <text>{{item.score}}</text>
  </view>
</block>
<button bindtap='openMini' wx:if="{{!isShow}}">打开小程序</button>
分享打开页面

 

9、功能点10_无尽模式一周清空排名,重新记录,同时保留历史最高成绩

1)保留历史最高记录:将最高成绩保存在小程序里。

2)一周清空排名:利用数据库,在服务器上实现该功能。

第一次执行的时候,设定周一零点的时候,执行该命令,将用户的成绩全部清零:

CREATE EVENT update_score
ON SCHEDULE EVERY 7 DAY
DO update szys_userinfo set score=0;

 

在MySQL数据库命令行中,执行上述语句,则每七天清空一次,重新记录。

 

 

解决项目的心路历程与收获:

  这个项目对我们来说难度很大,最开始由于时间紧张和对知识的不了解,错误地安排了项目完成计划。在老师的理解和帮助下我们继续开展学习和开发,期间得到了朋友的帮助和指导,基本按照第二次安排时间完成了项目预计进度。在软件工程团队项目中,有很多不可控因素,但是调动大家积极性并且合理分工还是非常重要的。小程序的代码很是庞杂,技术上解决的问题不再赘述,这次的大作业是一次非常宝贵的编程经验,学习了解了更多的语言,发现了更多的学习渠道。

转载于:https://www.cnblogs.com/vv06160/p/8401877.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值