ideson há 6 anos atrás
commit
2b952c5956

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+node_modules
+miniprogram_npm
+typings
+.idea
+.vscode
+package-lock.json

+ 9 - 0
app.js

@@ -0,0 +1,9 @@
+//app.js
+App({
+  onLaunch: function () {
+    
+  },
+  globalData: {
+    userInfo: null
+  }
+})

+ 12 - 0
app.json

@@ -0,0 +1,12 @@
+{
+  "pages": [
+    "pages/index/index",
+    "pages/index/components/history/HistoryList"
+  ],
+  "window": {
+    "backgroundTextStyle": "light",
+    "navigationBarBackgroundColor": "#fff",
+    "navigationBarTitleText": "语音答题",
+    "navigationBarTextStyle": "black"
+  }
+}

+ 7 - 0
app.wxss

@@ -0,0 +1,7 @@
+/**app.wxss**/
+.container {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  padding: 20px;
+} 

BIN
assets/icon-del.png


BIN
assets/icon-history.png


BIN
assets/icon-pause.png


BIN
assets/icon-play.png


BIN
assets/icon-scan.png


BIN
assets/icon-stop.png


BIN
assets/icon-upload.png


+ 101 - 0
iconfont.wxss

@@ -0,0 +1,101 @@
+@font-face {
+  font-family: 'iconfont';  /* project id 1136474 */
+  src: url('//at.alicdn.com/t/font_1136474_83vi6bqod3h.eot');
+  src: url('//at.alicdn.com/t/font_1136474_83vi6bqod3h.eot?#iefix') format('embedded-opentype'),
+  url('//at.alicdn.com/t/font_1136474_83vi6bqod3h.woff2') format('woff2'),
+  url('//at.alicdn.com/t/font_1136474_83vi6bqod3h.woff') format('woff'),
+  url('//at.alicdn.com/t/font_1136474_83vi6bqod3h.ttf') format('truetype'),
+  url('//at.alicdn.com/t/font_1136474_83vi6bqod3h.svg#iconfont') format('svg');
+}.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+}
+
+.icon-bofang1:before {
+  content: "\e630";
+}
+
+.icon-zanting1:before {
+  content: "\e635";
+}
+
+.icon-saomiao:before {
+  content: "\e619";
+}
+
+.icon-gengduo1:before {
+  content: "\e64d";
+}
+
+.icon-yonghu:before {
+  content: "\e606";
+}
+
+.icon-bianji:before {
+  content: "\e608";
+}
+
+.icon-yinliangguan:before {
+  content: "\e609";
+}
+
+.icon-yinliangkai:before {
+  content: "\e60a";
+}
+
+.icon-kuaitui:before {
+  content: "\e60b";
+}
+
+.icon-kuaijin:before {
+  content: "\e60c";
+}
+
+.icon-tingzhi:before {
+  content: "\e60d";
+}
+
+.icon-zanting:before {
+  content: "\e60e";
+}
+
+.icon-bofang:before {
+  content: "\e60f";
+}
+
+.icon-gengduo:before {
+  content: "\e610";
+}
+
+.icon-guanbi:before {
+  content: "\e611";
+}
+
+.icon-tianjia:before {
+  content: "\e612";
+}
+
+.icon-xiazai:before {
+  content: "\e613";
+}
+
+.icon-shangchuan:before {
+  content: "\e614";
+}
+
+.icon-yulan:before {
+  content: "\e615";
+}
+
+.icon-gengxin:before {
+  content: "\e616";
+}
+
+.icon-lishi:before {
+  content: "\e617";
+}
+
+.icon-shanchu:before {
+  content: "\e618";
+}

+ 16 - 0
package.json

@@ -0,0 +1,16 @@
+{
+  "name": "skynet",
+  "version": "1.0.0",
+  "description": "",
+  "main": "app.js",
+  "scripts": {
+    "tsc": "./node_modules/typescript/bin/tsc",
+    "compile": "tsc"
+  },
+  "devDependencies": {
+    "typescript": "^3.3.3"
+  },
+  "dependencies": {
+    "miniprogram-api-typings": "^2.4.2"
+  }
+}

+ 56 - 0
pages/index/components/history/HistoryList.js

@@ -0,0 +1,56 @@
+// pages/index/components/history/HistoryList.js
+Component({
+
+  properties: {
+    histories: {
+      type: Array,
+      observer: function (list) {
+        console.log('histories')
+        console.log(list)
+      }
+    }
+  },
+
+  data: {
+    currentPidx: -1,
+    currentIdx: -1,
+    src: ""
+  },
+
+  methods: {
+    selectAudio: function(e) {
+      const dataset = e.currentTarget.dataset
+      const src = this.data.histories[dataset.pid]['answers'][dataset.id]['studentAnswer'] + "?t=" + new Date().getTime()
+      this.setData({
+        currentPidx: dataset.pid,
+        currentIdx: dataset.id,
+        src: src
+      })
+      console.log(this.data)
+    }
+  },
+
+  attached() {
+    const that = this
+    this.setData({
+      src: ""
+    })
+    console.log("history attached")
+
+    const player = this.selectComponent('.player')
+    if (player) {
+      player.audioCallback(function (src) {
+        that.setData({
+          currentPidx: 0,
+          currentIdx: 0,
+          src: ''
+        })
+        console.log('player bind failed', src)
+      })
+    }
+  },
+
+  detached() {
+    console.log("detached")
+  }
+})

+ 6 - 0
pages/index/components/history/HistoryList.json

@@ -0,0 +1,6 @@
+{
+  "component": true,
+  "usingComponents": {
+    "AudioPlayer": "../player/AudioPlayer"
+  }
+}

+ 18 - 0
pages/index/components/history/HistoryList.wxml

@@ -0,0 +1,18 @@
+<!--pages/index/components/history/HistoryList.wxml-->
+<view style='padding-top:2px;padding-bottom:8px;padding-left:16px;padding-right:16px;' wx:if="{{histories.length > 0}}">
+      <AudioPlayer class="player" src="{{src}}" auto="{{true}}"></AudioPlayer>
+    </view>
+<scroll-view scroll-y style="max-height:calc(100vh - 240px);">
+<view class='list-container'>
+  <view class='group' wx:for="{{histories}}" wx:key="{{idx}}" wx:for-index="pidx" wx:if="{{item}}">
+    <view class='title' wx:if="{{item.title}}">{{item.title}}</view>
+    <view class='item-container'>
+      <view class='item' wx:for="{{item.answers}}" wx:key="{{order}}" wx:for-index="idx" data-pid="{{pidx}}" data-id="{{idx}}" bindtap='selectAudio'>
+        <view class="content {{currentPidx === pidx && currentIdx == idx ? 'item-selected' : ''}}">{{item.subNumber}}</view>
+      </view>
+    </view>
+  </view>
+
+  <view class='empty' wx:if="{{!histories || histories.length == 0}}">暂无上传记录</view>
+</view>
+</scroll-view>

+ 63 - 0
pages/index/components/history/HistoryList.wxss

@@ -0,0 +1,63 @@
+/* pages/index/components/history/HistoryList.wxss */
+/*列表*/
+.list-container {
+  padding-left: 16px;
+  padding-right: 16px;
+  padding-bottom: 48px;
+  background: #f8f8f8;
+}
+
+.list-container .group .title{
+  font-size: 16px;
+  color:black;
+  padding-top:10px;
+  padding-bottom: 4px;
+}
+
+.list-container .group .item-container {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  align-content: flex-start;
+}
+
+.list-container .group .item {
+  width: 14.285714285714286%;
+  height: calc((100vw - 32px) / 7);
+  flex-direction: row;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.list-container .group .item .content{
+  width: 35px;
+  height: 35px;
+  font-size: 18px;
+  box-sizing: border-box;
+  border-radius: 50%;
+  border: 1px solid #ccc;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: #2d8cf0;
+  background: 0 0;
+  text-decoration: none;
+  outline: 0;
+  transition: color .2s ease;
+}
+
+.item-selected {
+    box-shadow: 0 0 15px gold;
+    background-color: #a8bcf7!important;
+    transform: scale(1.2);
+}
+
+.empty {
+  display: flex;
+  min-height: 120px;
+  align-items: center;
+  justify-content: center;
+  font-size: 18px;
+  color: #ccc;
+}

+ 252 - 0
pages/index/components/player/AudioPlayer.js

@@ -0,0 +1,252 @@
+// pages/index/components/player/AudioPlayer.js
+var AudioContext = wx.createInnerAudioContext()
+Component({
+  /**
+   * 组件的属性列表
+   */
+  properties: {
+    src: {
+      type: String,
+      observer: function (src) {
+        console.log(this.data.auto)
+        console.log("audio src changed",src)
+
+        this.stop()
+        if (this.data.auto) {
+          this.resetAudioContext(src)
+        }
+      }
+    },
+    updated: {
+      type: String,
+      observer: function(v) {
+        console.log(v)
+      }
+    },
+    auto: {
+      type: Boolean
+    }
+  },
+  /**
+   * 组件的初始数据
+   */
+  data: {
+    isPlay: false,
+    progress: 0,
+    formatCurrent: '00:00',
+    formatDuration: '00:00'
+  },
+  audioCallback: null,
+
+  canPlay: false,
+
+  fetchDurationHandler: 0,
+  /**
+   * 组件的方法列表
+   */
+  methods: {
+    stop: function() {
+      AudioContext.stop()
+
+      this.setData({
+        isPlay: false,
+        progress: 0,
+        formatCurrent: '00:00'
+      })
+    },
+    playAction: function (e) {
+      if (this.fetchDurationHandler) {
+        clearInterval(this.fetchDurationHandler)
+      }
+      if (!this.data.src) {
+        return
+      }
+      if (AudioContext.src != this.data.src) {
+        this.resetAudioContext(this.data.src)
+      }
+      
+      if (this.data.isPlay) {
+        this.stop()
+      } else {
+        AudioContext.play()
+      }
+    },
+
+    formatTimer: function (currentTime) {
+      const minutes = parseInt(currentTime / 60)
+      const seconds = parseInt(currentTime % 60)
+      var time = ''
+      if (minutes > 9) {
+        time += minutes
+      } else {
+        time += '0' + minutes
+      }
+
+      time += ':'
+      if (seconds > 9) {
+        time += seconds
+      } else {
+        time += '0' + seconds
+      }
+      return time
+    },
+
+    resetAudioContext(src) {
+      AudioContext.stop()
+      AudioContext.destroy()
+
+      AudioContext = wx.createInnerAudioContext()
+
+      wx.setInnerAudioOption({
+        obeyMuteSwitch: false
+      })
+
+      AudioContext.offCanplay()
+      AudioContext.offPlay()
+      AudioContext.offEnded()
+      AudioContext.offStop()
+      AudioContext.offTimeUpdate()
+      AudioContext.offError()
+
+      this.canPlay = false
+      this.setData({
+        isPlay: false,
+        progress: 0,
+        formatCurrent: '00:00',
+        formatDuration: '00:00'
+      })
+      const that = this
+      AudioContext.onCanplay(() => {
+        console.log("onCanPlay", AudioContext, AudioContext.duration)
+        that.canPlay = true
+
+        setTimeout(function() {
+          const formatDuration = that.formatTimer(AudioContext.duration)
+          that.setData({
+            formatCurrent: '00:00',
+            formatDuration: formatDuration
+          })
+        }, 500)
+      })
+      AudioContext.onPlay(() => {
+        console.log("onPlay")
+        that.setData({
+          isPlay: true,
+        })
+      })
+
+      AudioContext.onEnded(() => {
+        console.log("onEnded")
+        const formatCurrent = that.formatTimer(AudioContext.duration)
+        that.setData({
+          isPlay: false,
+          formatCurrent: formatCurrent,
+          progress: 100
+        })
+      })
+
+      AudioContext.onStop(() => {
+        console.log("onStop")
+        that.setData({
+          isPlay: false,
+          formatCurrent: '00:00',
+          progress: 0
+        })
+      })
+      AudioContext.onTimeUpdate(() => {
+        console.log("onTimeUpdate")
+        const formatCurrent = that.formatTimer(AudioContext.currentTime)
+        const formatDuration = that.formatTimer(AudioContext.duration)
+        that.setData({
+          progress: parseInt(100 * AudioContext.currentTime / AudioContext.duration),
+          formatCurrent: formatCurrent,
+          formatDuration: formatDuration
+        })
+      })
+      AudioContext.onError(error => {
+        if (this.fetchDurationHandler) {
+          clearInterval(this.fetchDurationHandler)
+        }
+        console.log("onError", error)
+        var errMessage = ''
+        switch (error.errCode) {
+          case 10001:
+            errMessage = '系统错误'
+            break;
+          case 10002:
+            errMessage = '网络错误'
+            break;
+          case 10003:
+            errMessage = '文件错误'
+            break;
+          case 10004:
+            errMessage = '格式错误'
+            break;
+          default:
+            errMessage = '未知错误'
+            break;
+        }
+        console.log(errMessage)
+        wx.showToast({
+          title: errMessage,
+          icon: 'none'
+        })
+        that.setData({
+          isPlay: false,
+          formatCurrent: '00:00',
+          formatDuration: '00:00',
+          progress: 0
+        })
+        if (that.audioCallback) {
+          that.audioCallback(AudioContext.src)
+        }
+      })
+
+      if (this.data.src) {
+        AudioContext.src = this.data.src
+      }
+      AudioContext.autoplay = this.data.auto
+    },
+
+    audioCallback: function(callback) {
+      this.audioCallback = callback
+    }
+  },
+
+  attached() {
+    this.resetAudioContext()
+    const that = this
+
+    wx.onAudioInterruptionBegin(function () {
+      console.log('player onAudioInterruptionBegin')
+      if (that.data.isPlay) {
+        that.stop()
+      }
+    })
+
+    wx.onAppHide(function() {
+      console.log('player onAppHide')
+      console.log(that.data.isPlay)
+      if (that.data.isPlay) {
+        that.stop()
+      }
+    })
+    wx.onAppShow(function() {
+      console.log("player onAppShow")
+      if (that.data.src && !that.canPlay) {
+        that.resetAudioContext(that.data.src)
+      }
+    })
+  },
+  detached() {
+    AudioContext.stop()
+    AudioContext.destroy()
+
+    wx.offAudioInterruptionBegin(() => {
+      console.log('player offAudioInterruptionBegin')
+    })
+    wx.offAppHide(() => {
+      console.log('player offAppHide')
+    })
+  }
+})

+ 4 - 0
pages/index/components/player/AudioPlayer.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}

+ 6 - 0
pages/index/components/player/AudioPlayer.wxml

@@ -0,0 +1,6 @@
+<!--pages/index/components/player/AudioPlayer.wxml-->
+<view class="player-container {{updated == 'true' ? 'updated' : ''}}">
+  <image mode="aspectFit" src="/assets/icon-{{!isPlay ? 'play' : 'stop'}}.png" bindtap='playAction' style='height: 40px;width: 40px;'></image>
+  <progress percent="{{progress}}" stroke-width="6" color="rgba(56, 121, 217, .8)" border-radius="6" />
+  <view class='timer'>{{formatCurrent}}/{{formatDuration}}</view>
+</view>

+ 49 - 0
pages/index/components/player/AudioPlayer.wxss

@@ -0,0 +1,49 @@
+/* pages/index/components/player/AudioPlayer.wxss */
+@import "/iconfont.wxss";
+
+.player-container {
+  display: flex;
+  flex-direction: row;
+  padding: 10px 0px 10px 16px;
+  border-radius: 6px;
+  border: 1rpx solid gainsboro;
+  background: gainsboro;
+  align-items: center;
+}
+
+.player-container.updated {
+  border: 1rpx solid rgba(93, 167, 228, 0.6)!important;
+  background: rgba(93, 167, 228, 0.6)!important;
+}
+
+.updated .timer {
+  color:aliceblue;
+}
+
+.play-btn {
+  width: 60rpx;
+  height: 60rpx;
+  color:rgba(56, 121, 217, .8);
+  font-size: 60rpx;
+  line-height: 60rpx;
+}
+
+.act-btn {
+  color:rgba(128, 128, 128, .8);
+  font-size: 40rpx;
+}
+
+progress {
+  width: calc(100% - 16px - 16px - 80px);
+  margin-left: 16px;
+  margin-right: 16px;
+}
+
+.timer {
+  color: #aaa;
+  font-size: 14px;
+  text-align: right;
+  vertical-align: middle;
+  padding-right: 10px;
+  width: 80px;
+}

+ 541 - 0
pages/index/index.js

@@ -0,0 +1,541 @@
+//index.js
+//获取应用实例
+const WX_REG = "http://wxapp2.qmth.com.cn/"
+const ZH_CN = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十',
+  '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十',
+  '三十一', '三十二', '三十三', '三十四', '三十五', '三十六', '三十七', '三十八', '三十九', '四十',
+  '四十一', '四十二', '四十三', '四十四', '四十五', '四十六', '四十七', '四十八', '四十九', '五十']
+const II = ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩',
+  '⑪', '⑫', '⑬', '⑭', '⑮', '⑯', '⑰', '⑱', '⑲', '⑳']
+import {
+  Api
+} from "../../utils/api";
+const API = new Api()
+const app = getApp()
+const RECORD_CONFIG = {
+  duration: 120000,
+  sampleRate: 44100,
+  numberOfChannels: 1,
+  encodeBitRate: 128000,
+  format: 'mp3',
+}
+
+Page({
+  data: {
+    audio: '',
+    updated: false,
+    isRecord: false,
+    recordTime: 0.0,
+    duration: "00:00",
+    windowWidth: 0,
+    maskOpacity: 0,
+
+    paper: {
+      courseId: 0,
+      courseName: "",
+      examRecordDataId: 0,
+      examStudentId: 0,
+      questionMainNumber: 0,
+      questionOrder: 0,
+      subNumber: 0
+    },
+    currentQuestionTitle: '请扫描题目',
+
+    audioList: [],
+    histories: [],
+  },
+
+  secret: '',
+
+  recordHandler: 0,
+
+  recordManager: null,
+
+  paperStruct: [],
+  
+  resetData: function() {
+    this.secret = ''
+    this.setData({
+      audio: '',
+      updated: false,
+      isRecord: false,
+      recordTime: 0.0,
+      duration: "00:00",
+      windowWidth: 0,
+      maskOpacity: 0,
+
+      paper: {
+        courseId: 0,
+        courseName: "",
+        examRecordDataId: 0,
+        examStudentId: 0,
+        questionMainNumber: 0,
+        questionOrder: 0,
+        subNumber: 0
+      },
+      currentQuestionTitle: '请扫描题目',
+
+      audioList: [],
+      histories: [],
+    })
+  },
+
+  scanQuestion: function(e) {
+    // 正在录音 禁止操作
+    if (this.data.isRecord) {
+      return
+    }
+
+    const that = this
+    wx.scanCode({
+      onlyFromCamera: true,
+      scanType: ['qrCode'],
+      success: function(res) {
+        console.log(res)
+        if (res) {
+          that.secret = res.result.replace(WX_REG, '')
+          that.checkQrcode(that.secret, true)
+        } else {
+          wx.showToast({
+            title: '扫码失败',
+            icon: 'none'
+          })
+        }
+      }
+    })
+  },
+
+  lockRecord: false,
+
+  audioAction: function(e) {
+    if (this.lockRecord) {
+      return false
+    }
+
+    if (!this.data.paper || !this.data.paper.questionOrder) {
+      wx.showToast({
+        title: '请重新扫描题目二维码',
+        icon: 'none'
+      })
+      return false
+    }
+
+    this.lockRecord = true
+    this.startRecord()
+  },
+
+  startRecord: function() {
+    const that = this
+    if (!this.data.isRecord) {
+      if (this.data.audio) {
+        wx.showModal({
+          title: '提示',
+          content: '已存在未上传的录音,是否开始重新录音',
+          confirmText: '开始录音',
+          success(res) {
+            if (res.confirm) {
+              that.recordManager.start(RECORD_CONFIG)
+            } else {
+              that.lockRecord = false
+            }
+          }
+        })
+      } else {
+        wx.showLoading({
+          title: '',
+          mask: true
+        })
+        that.recordManager.start(RECORD_CONFIG)
+      }
+    } else {
+      that.recordManager.stop()
+    }
+  },
+
+  /**
+   * 显示题目列表
+   */
+  showListPanel() {
+    const player = this.selectComponent("#player")
+    if (player != null) {
+      console.log(player)
+      player.stop()
+    }
+
+    if (!this.secret) {
+      wx.showToast({
+        title: '请先扫描题目',
+        icon: 'none'
+      })
+      return
+    }
+
+    // 正在录音 禁止操作
+    if (this.data.isRecord) {
+      return
+    }
+
+    wx.showLoading({
+      title: '正在加载',
+      mask: true
+    })
+    const that = this
+
+    /*if (this.paperStruct && this.paperStruct.length > 0 && this.data.paperthis.paperStruct[0].examRecordDataId == this.data.paper.examRecordDataId) {
+      API.getUploadedAudioAnswerList(this.data.paper.examRecordDataId).then(list => {
+        that.__inline_getAnswerListSuccess(list)
+      }).catch(e => {
+        console.log(e)
+        wx.hideLoading()
+        wx.showToast({
+          title: '加载失败',
+          icon: 'none'
+        })
+        this.hideActionPanel()
+      })
+    } else {
+      
+    }*/
+
+    API.getPaperStruct(this.data.paper.examRecordDataId).then(list => {
+      var struct = []
+      if (list.defaultPaper.questionGroupList && Array.isArray(list.defaultPaper.questionGroupList) && list.defaultPaper.questionGroupList.length > 0) {
+        list.defaultPaper.questionGroupList.forEach(item => {
+          struct.push(item.groupName)
+        })
+      }
+      this.paperStruct = struct
+      return API.getUploadedAudioAnswerList(this.data.paper.examRecordDataId)
+    }).then(list => {
+      that.__inline_getAnswerListSuccess(list)
+    }).catch(e => {
+      console.log(e)
+      wx.hideLoading()
+      wx.showToast({
+        title: '加载失败',
+        icon: 'none'
+      })
+      this.hideActionPanel()
+    })
+  },
+
+  __inline_getAnswerListSuccess: function(list) {
+    //list = TEST_LIST
+
+    var histories = []
+    if (list && Array.isArray(list) && list.length > 0) {
+      list.forEach(item => {
+        if (item.mainNumber <= this.paperStruct.length && item.mainNumber > 0) {
+          let index = histories.findIndex((v) => {
+            return v && v['idx'] === item.mainNumber
+          })
+          if (index > -1) {
+            histories[index]['answers'].push(item)
+          } else {
+            histories[item.mainNumber] = { 
+              idx: item.mainNumber,
+              title: this.paperStruct[item.mainNumber - 1], 
+              answers: new Array(item)
+            }
+          }
+        } else {
+          throw new Error('试题结构异常')
+        }
+      })
+    }
+
+    this.setData({
+      histories: histories,
+      maskOpacity: 0.6
+    })
+    wx.hideLoading()
+  },
+
+  /**
+   * 关闭题目列表
+   */
+  hideActionPanel() {
+    this.setData({
+      maskOpacity: 0
+    })
+  },
+
+  uploadAudio(e) {
+    // 正在录音 禁止操作
+    if (this.data.isRecord) {
+      return
+    }
+
+    const filepath = this.data.audio
+    const that = this
+    wx.showLoading({
+      title: '正在上传录音',
+      mask: true
+    })
+
+    const duration = filepath
+    wx.getFileInfo({
+      filePath: filepath,
+      success: function(res) {
+        console.log(res)
+        // 获取上传签名
+        API.getSign(that.data.paper.examRecordDataId, that.data.paper.examStudentId,
+          that.data.paper.questionOrder, res.digest)
+          .then(res => {
+            // 上传文件
+            console.log(res)
+            return API.uploadFile(filepath, res.policy, res.signature, res.uploadUrl)
+          }).then(res => {
+            console.log(res)
+            //文件上传成功后通知服务端
+            return API.notifyServer(that.data.paper.examRecordDataId, that.data.paper.examStudentId, that.data.paper.questionOrder, res.url)
+          }).then(res => {
+            // 轮询答案处理结果
+            const _startTime = new Date().getTime()
+            var times = 10
+            const handler = setInterval(function() {
+              API.getNotifyResult(that.data.paper.examRecordDataId, that.data.paper.examStudentId, that.data.paper.questionOrder, res)
+                .then(res => {
+                  console.log(res)
+                  if (res) {
+                    clearInterval(handler)
+                    wx.hideLoading()
+                    wx.showToast({
+                      title: '上传成功,继续扫描下一题',
+                      icon: 'none'
+                    })
+                    var paper = that.data.paper
+                    paper['questionMainNumber'] = 0
+                    paper['questionOrder'] = 0
+                    paper['subNumber'] = 0
+                    that.setData({
+                      audio: '',
+                      updated: false,
+                      paper: paper,
+                      currentQuestionTitle: '请扫描题目'
+                    })
+                  } else {
+                    throw '答案未同步'
+                  }
+                }).catch(exceptions => {
+                  times--
+                  if (new Date().getTime() - _startTime >= 1000 * 10 || times < 0) {
+                    clearInterval(handler)
+                    wx.hideLoading()
+                    wx.showToast({
+                      title: '上传失败,请重新上传',
+                      icon: 'none'
+                    })
+                  }
+                })
+            }, 1000)
+          }).catch(exceptions => {
+            console.log('catch', exceptions)
+            var error = '上传失败,请重新上传'
+            try {
+              if (exceptions.statusCode == 500) {
+                error = exceptions.data.desc
+              }
+            } catch (e) {
+
+            }
+            wx.hideLoading()
+            wx.showToast({
+              title: error,
+              icon: 'none'
+            })
+          })
+      }
+    })
+  },
+
+  removeAudio(e) {
+    const that = this
+    wx.showModal({
+      title: '提示',
+      content: '删除后无法恢复,是否直接删除',
+      confirmText: '直接删除',
+      confirmColor: '#ff0000',
+      success(res) {
+        if (res.confirm) {
+          that.setData({
+            audio: ''
+          })
+        }
+      }
+    })
+  },
+
+  onRecordStart() {
+    this.lockRecord = false
+    console.log("record start")
+    const that = this
+
+    this.setData({
+      audio: '',
+      updated: false,
+      isRecord: true
+    })
+
+    this.recordHandler = setInterval(function() {
+      var recordTime = that.data.recordTime + 0.1
+      var second = parseInt(recordTime)
+      var msc = parseInt((recordTime - second) * 10) * 10
+      var duration = (second > 9 ? second : ('0' + second)) + ":" + (msc > 0 ? msc : "00")
+      that.setData({
+        isRecord: true,
+        recordTime: recordTime,
+        duration: duration
+      })
+    }, 100)
+  },
+  onRecordStop(audioPath) {
+    this.lockRecord = false
+    console.log("record stop", audioPath)
+    clearInterval(this.recordHandler)
+    this.recordHandler = 0
+
+    this.setData({
+      audio: audioPath,
+      recordTime: 0,
+      duration: "00:00",
+      isRecord: false,
+      updated: false
+    })
+  },
+
+  onShow: function() {
+    /*if (this.secret) {
+      this.checkQrcode(this.secret, false)
+    }*/
+  },
+
+  showConfirmDialog: function(msg, confirm, callback) {
+    const confirmText = confirm ? confirm : '确定'
+    wx.showModal({
+      title: '提示',
+      content: msg,
+      showCancel: false,
+      confirmText: confirmText,
+      success(res) {
+        if (res.confirm) {
+          console.log('用户点击确定')
+          if (callback) {
+            callback.apply()
+          }
+        }
+      }
+    })
+  },
+
+  onLoad: function(options) {
+    console.log(options)
+    const encodeReg = encodeURIComponent(WX_REG)
+    if (options && options.q && options.q.indexOf(encodeReg) == 0) {
+      this.secret = options.q.replace(encodeReg, '')
+    }
+
+    const that = this
+
+    this.recordManager = wx.getRecorderManager()
+    this.recordManager.onError(function(res) {
+      console.log(res)
+      that.lockRecord = false
+      wx.showToast({
+        title: '录音出错',
+        icon: 'none'
+      })
+    })
+
+    this.recordManager.onStart(function () {
+      wx.hideLoading()
+      that.onRecordStart()
+    })
+    this.recordManager.onStop(function (res) {
+      wx.hideLoading()
+      that.onRecordStop(res.tempFilePath)
+    })
+    this.recordManager.onInterruptionBegin(function () {
+      wx.hideLoading()
+      console.log("onInterruptionBegin")
+      that.lockRecord = false
+      that.recordManager.stop()
+    })
+    this.recordManager.onInterruptionEnd(function () {
+      wx.hideLoading()
+      that.lockRecord = false
+      console.log("onInterruptionEnd")
+    })
+
+    API.setTokenValidCallback(this.tokenValidCallback)
+
+    wx.onAppHide(() => {
+      console.log("index onAppHide")
+      if (that.data.isRecord) {
+        wx.hideLoading()
+        that.recordManager.stop()
+
+        that.lockRecord = false
+      }
+    })
+  },
+
+  checkQrcode: function(code, fromScan) {
+    if (fromScan) {
+      wx.showLoading({
+        title: '正在加载',
+        mask: true
+      })
+    }
+
+    const that = this
+    API.checkQrcode(code)
+      .then(function(res) {
+        const title = `第${ZH_CN.length >= res.questionMainNumber ? ZH_CN[res.questionMainNumber - 1] : res.questionMainNumber}大题 第${II.length >= res.subNumber ? II[res.subNumber-1] : res.subNumber}小题 `
+        that.setData({
+          audio: '',
+          isRecord: false,
+          updated: false,
+          paper: res,
+          currentQuestionTitle: title,
+        })
+        if (fromScan) {
+          wx.hideLoading()
+          wx.showToast({
+            title: '扫码成功',
+            icon: 'none'
+          })
+        }
+      })
+      .catch(function(error) {
+        wx.hideLoading()
+        var errorMsg = error
+        if (errorMsg.indexOf('request:fail') == 0) {
+          errorMsg = "网络请求失败,请稍后重试"
+        }
+        that.showConfirmDialog(`${errorMsg}`, '确定', function() {
+          console.log('callback')
+          wx.reLaunch({
+            url: '/pages/index/index',
+          })
+        })
+      })
+  },
+
+  tokenValidCallback: function() {
+    this.showConfirmDialog('二维码已过期,请重新扫描题目', '立即扫描', function() {
+      console.log('callback')
+      this.scanQuestion()
+    })
+  },
+
+  onUnload: function() {
+    const that = this
+    wx.offAppHide(() => {
+      console.log("index offAppHide")
+      if (that.data.isRecord) {
+        that.recordManager.stop()
+      }
+    })
+  }
+})

+ 7 - 0
pages/index/index.json

@@ -0,0 +1,7 @@
+{
+  "usingComponents": {
+    "AudioPlayer":"components/player/AudioPlayer",
+    "HistoryList": "components/history/HistoryList"
+  },
+  "disableScroll": true
+}

+ 54 - 0
pages/index/index.wxml

@@ -0,0 +1,54 @@
+<!--index.wxml-->
+<view class="container">
+  <view class="action-container">
+    <view class='action-btn' bindtap='scanQuestion'>
+      <image mode="aspectFit" src='/assets/icon-scan.png' style='width:50px;height:50px;'></image>
+      <view class='label'>扫描题目</view>
+    </view>
+    <view class='action-btn' bindtap='showListPanel'>
+      <image mode="aspectFit" src='/assets/icon-history.png' style='width:50px;height:50px;'></image>
+      <view class='label'>上传记录</view>
+    </view>
+  </view>
+
+  <view class='recording-header'>
+    <view class='q-name'>{{paper && paper.courseName ? paper.courseName : ''}}</view>
+    <view class='recording-title'>{{currentQuestionTitle}}</view>
+    <view class='recording-tips'>(最长120秒)</view>
+    <view class='recording-duration'>
+      <text>{{duration}}</text>
+    </view>
+  </view>
+
+  <view style='padding-top:15px;padding-bottom:15px;width:100%;' wx:if="{{paper && audio}}">
+    <view class='question-group-header'>{{currentQuestionTitle}}</view>
+    <view style='padding-top:2px;padding-bottom:8px;'>
+      <AudioPlayer id="player" class="player" src="{{audio}}" updated="{{updated}}" wx:if="{{audio}}"></AudioPlayer>
+    </view>
+  </view>
+</view>
+
+<view class='bottom-action-container' wx:if="{{paper && paper.courseId > 0}}">
+  <view class='act-btn' wx:if="{{audio}}" bindtap='removeAudio'>
+    <view class='btn cancel-btn'>
+      <image mode="aspectFit" src='/assets/icon-del.png' style='width:30px;height:30px;'></image>
+    </view>
+  </view>
+
+  <view class='record-btn' bindtap='audioAction'>
+    <view class='recording' wx:if="{{isRecord}}"></view>
+  </view>
+  <view class='act-btn' wx:if="{{audio}}" bindtap='uploadAudio'>
+    <view class='btn upload-btn {{updated ? "updated":""}}'>
+      <image mode="aspectFit" src='/assets/icon-upload.png' style='width:30px;height:30px;'></image>
+    </view>
+  </view>
+</view>
+<view class="mask" style="opacity: {{maskOpacity}}" ontap="hideActionPanel" wx:if="{{maskOpacity > 0}}"></view>
+<view class='panel-container' wx:if="{{maskOpacity > 0}}">
+  <view class="head">
+    <view class='title'>上传记录</view>
+    <view class='panel-close' bindtap='hideActionPanel'>关闭</view>
+  </view>
+  <HistoryList histories="{{histories}}"></HistoryList>
+</view>

+ 237 - 0
pages/index/index.wxss

@@ -0,0 +1,237 @@
+/**index.wxss**/
+@import "/iconfont.wxss";
+
+.recording-header {
+  display: flex;
+  width: 100%;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  margin-top: 10px;
+}
+
+.recording-duration {
+  font-size: 38px;
+  color: black;
+}
+
+.recording-tips {
+  font-size: 14px;
+  padding: 2px;
+  color:#666;
+}
+
+.q-name {
+  color:#666;
+  font-size: 18px!important;
+  padding-bottom: 4px;
+}
+
+.recording-title {
+  color:#666;
+  font-size: 16px!important;
+  border-bottom: 1px solid #666;
+  padding-bottom: 2px;
+}
+
+.n {
+  border: 1px #666 solid;
+  width: 15px;
+  height: 15px;
+  display: inline-flex;
+  justify-content: center;
+  font-size: 11px;
+  border-radius: 50%;
+  margin-bottom: 4px;
+}
+
+.player {
+  width: 100%;
+}
+
+.action-container {
+  width: 100%;
+  height: 90px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: space-around;
+}
+
+.action-container .action-btn {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+}
+.scan-btn {
+  width: 100rpx;
+  height: 100rpx;
+  color: green;
+  font-size: 100rpx;
+}
+.action-btn .label {
+  font-size: 16px;
+  color: #999;
+  margin-top: 6px;
+}
+
+.record-btn {
+  width: 130rpx;
+  height: 130rpx;
+  border-radius: 50%;
+  background-color: red;
+  box-shadow: 0 0 10px #f00;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.record-btn .recording {
+  width: 40rpx;
+  height: 40rpx;
+  background-color: white;
+}
+
+.panel-container {
+  background-color: transparent;
+  width: 100%;
+  position:absolute;
+  bottom: 0px;
+  color: rgba(56, 121, 217, .8);
+  background: #f8f8f8;
+}
+
+.panel-container .head {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  height: 50px;
+  background: #fafafa;
+}
+
+.panel-container .head .title {
+  font-size:17px;
+  color:rgb(53, 54, 56);
+  padding-left: 16px;
+}
+
+.panel-container .head .panel-close {
+  font-size:14px;
+  color:rgba(139,142,156,1);
+  padding-left: 16px;
+  padding-right: 16px;  
+  height: 50px;
+  line-height: 50px;
+}
+
+.panel-list {
+  background: white;
+  display: flex;
+  flex-direction: column;
+  align-items: stretch;
+  justify-content: space-between;
+  border-radius: 8px;
+}
+
+.mask {
+  position: fixed;
+  height: 100vh;
+  width: 100vw;
+  transition: all 0.3s;
+  background:rgb(7,24,54);
+  top:0;
+}
+
+.panel-list .item {
+  padding: 8px;
+  text-align: center;
+  font-size: 20px;
+  border-bottom: 1rpx solid gainsboro;
+}
+
+.panel-list .item:last-child {
+  border: none;
+}
+
+.panel-container .close {
+  color: rgba(56, 121, 217, .8);
+  border-radius: 8px;
+  background: white;
+  display: flex;
+  flex-direction: column;
+  align-items: stretch;
+  justify-content: space-between;
+  padding: 8px;
+  text-align: center;
+  margin-top: 8px;
+  font-size: 20px;
+}
+
+.question-group-header {
+  font-size: 14px;
+  padding: 8px;
+  color:#666;
+}
+
+.bottom-action-container {
+  display: flex;
+  width: 100%;
+  flex-direction: row;
+  justify-content: space-around;
+  align-items: center;
+  height: 120px;
+  position: fixed;
+  bottom: 0;
+  background: #f8f8f8;
+  padding-bottom: 20px;
+}
+
+.act-btn {
+  width: 100rpx;
+  height: 100rpx;
+  padding: 20rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.act-btn .btn {
+  width: 100%;
+  height:100%;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.act-btn .btn.cancel-btn {
+  box-shadow: 0 0 10px #d8d8d8;
+  border: 1rpx solid #d8d8d8;
+  border-style: none;
+  background: #d8d8d8;
+}
+
+.act-btn .btn.upload-btn {
+  box-shadow: 0 0 10px #d8d8d8;
+  border: 1rpx solid #d8d8d8;
+  border-style: none;
+  background: #d8d8d8;
+}
+
+.cancel {
+  color: rgb(255, 255, 255)!important;
+  font-size: 50rpx!important;
+}
+
+.upload {
+  color: rgb(255, 255, 255)!important;
+  font-size: 50rpx!important;
+}
+
+.act-btn .updated {
+  box-shadow: 0 0 10rpx green!important;
+  border: none!important;
+  background: green!important;
+}

+ 45 - 0
project.config.json

@@ -0,0 +1,45 @@
+{
+	"description": "项目配置文件",
+	"packOptions": {
+		"ignore": []
+	},
+	"setting": {
+		"urlCheck": true,
+		"es6": true,
+		"postcss": true,
+		"minified": true,
+		"newFeature": true,
+		"nodeModules": false,
+		"autoAudits": false
+	},
+	"compileType": "miniprogram",
+	"libVersion": "2.7.1",
+	"appid": "wx4263754944d4d184",
+	"projectname": "tools",
+	"debugOptions": {
+		"hidedInDevtools": []
+	},
+	"isGameTourist": false,
+	"condition": {
+		"search": {
+			"current": -1,
+			"list": []
+		},
+		"conversation": {
+			"current": -1,
+			"list": []
+		},
+		"plugin": {
+			"current": -1,
+			"list": []
+		},
+		"game": {
+			"currentL": -1,
+			"list": []
+		},
+		"miniprogram": {
+			"current": -1,
+			"list": []
+		}
+	}
+}

+ 33 - 0
tsconfig.json

@@ -0,0 +1,33 @@
+{
+  "compilerOptions": {
+    "strictNullChecks": true,
+    "noImplicitAny": true,
+    "module": "CommonJS",
+    "target": "ES5",
+    "allowJs": false,
+    "experimentalDecorators": true,
+    "noImplicitThis": true,
+    "noImplicitReturns": true,
+    "alwaysStrict": true,
+    "inlineSourceMap": true,
+    "inlineSources": true,
+    "noFallthroughCasesInSwitch": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "strict": true,
+    "removeComments": true,
+    "pretty": true,
+    "strictPropertyInitialization": true,
+    "typeRoots": [
+      "typings"
+    ]
+  },
+  "include": [
+    "./**/*.ts"
+  ],
+  "exclude": [
+    "node_modules",
+    "miniprogram_dist",
+    "**/*.spec.ts"
+  ]
+}

Diff do ficheiro suprimidas por serem muito extensas
+ 257 - 0
utils/api.js


+ 373 - 0
utils/api.ts

@@ -0,0 +1,373 @@
+///<reference path="../typings/wx/lib.wx.api.d.ts"/>
+
+declare namespace tools {
+    export interface Entity {
+
+    }
+
+    export interface Result<T extends Entity> {
+        code?: number,
+        desc?: string,
+        data?: T | T[]
+    }
+
+    export interface UpyunResult {
+        code: number,
+        message?: string,
+        time: number,
+        url: string
+    }
+
+    export interface CheckQrResult extends Entity {
+        /**
+         * 课程ID
+         */
+        courseId: number,
+        /**
+         * 课程名称
+         */
+        courseName: string,
+        /**
+         * 考试记录DataID
+         */
+        examRecordDataId: number,
+        /**
+         * 考生ID
+         */
+        examStudentId: number,
+        /**
+         * 考试试题大题号
+         */
+        questionMainNumber: number,
+        /**
+         * 考试试题题序
+         */
+        questionOrder: number,
+        /**
+         * 登录信息key
+         */
+        key: string,
+        /**
+         * 登录信息token
+         */
+        token: string,
+
+        subNumber: number,
+    }
+
+    export interface SignResult extends Entity {
+        filePath: string,
+        policy: string,
+        signature: string,
+        uploadUrl: string
+    }
+
+    export interface NotifyResult extends Entity {
+        filePath: string,
+        result: boolean
+    }
+
+    export interface AudioAnswer extends Entity {
+        examRecordDataId: number,
+        mainNumber: number,
+        order: number,
+        studentAnswer: string,
+        subNumber: string
+    }
+}
+
+interface ApiConfigOption {
+    ver: number,
+    versionName: string,
+    baseUrl: string
+}
+
+interface RequestOption {
+    url: string,
+    parameter: any
+}
+
+type TokenValidCallback = () => void;
+
+interface API {
+    configOption: ApiConfigOption
+    version(): string
+    setTokenValidCallback(callback: TokenValidCallback): void
+
+    checkQrcode(secret: string): Promise<tools.CheckQrResult>
+
+    getPaperStruct(examRecordDataId: number) : Promise<any>
+
+    getSign(examRecordDataId: number, examStudentId: number, order: number, fileMd5: string) : Promise<tools.SignResult>
+
+    uploadFile(filePath: string, policy: string, signature: string, uploadUrl: string) : Promise<tools.UpyunResult>
+
+    notifyServer(examRecordDataId: number, examStudentId: number, order: number, filePath: string) : Promise<string>
+
+    getNotifyResult(examRecordDataId: number, examStudentId: number, order: number, filePath: string) : Promise<boolean>
+
+    getUploadedAudioAnswerList(examRecordDataId: number) : Promise<tools.AudioAnswer[]>
+
+    getRequest(option: RequestOption, auth: boolean): Promise<any>
+
+    postRequest(option: RequestOption, auth: boolean): Promise<any>
+}
+
+export class Api implements API {
+    configOption: ApiConfigOption = { ver: 1, versionName: 'v1.0.0', baseUrl: 'https://ecs.qmth.com.cn:8878' }
+    private key: string = ''
+    private token: string = ''
+    private tokenValidCallback: TokenValidCallback = () => {}
+
+    version(): string {
+        return this.configOption.versionName
+    }
+
+    /**
+     * 授权成功后配置
+     * @param {number} uid
+     * @param {string} token
+     * @param {number} partitionId
+     */
+    configAuthorize(key: string, token: string) {
+        this.key = key
+        this.token = token
+    }
+
+    setTokenValidCallback(callback: TokenValidCallback): void {
+        this.tokenValidCallback = callback
+    }
+
+    checkQrcode(secret: string): Promise<tools.CheckQrResult> {
+        const that = this
+        return new Promise<tools.CheckQrResult>((resolve, reject) => {
+            return this.postRequest({
+                url: '/api/ecs_oe_student/examControl/checkQrCode',
+                parameter: {
+                    qrCode: secret
+                }
+            }, false).then(res => {
+                if (res.key && res.token) {
+                    that.configAuthorize(res.key, res.token)
+
+                    // model transformer
+                    var result = {
+                        courseId: res.courseId,
+                        courseName: res.courseName,
+                        examRecordDataId: res.examRecordDataId,
+                        examStudentId: res.examStudentId,
+                        questionMainNumber: res.questionMainNumber,
+                        questionOrder: res.questionOrder,
+                        subNumber: res.subNumber
+                    }
+                    resolve((result as tools.CheckQrResult))
+                } else {
+                    reject(new Error('二维码已失效'))
+                }
+            }, error => {
+                reject(error)
+            })
+        })
+    }
+
+    getPaperStruct(examRecordDataId: number) : Promise<any> {
+        return this.getRequest({
+            url: '/api/ecs_oe_student/examRecordPaperStruct/getExamRecordPaperStruct',
+            parameter: {
+                examRecordDataId: examRecordDataId
+            }
+        })
+    }
+
+    getSign(examRecordDataId: number, examStudentId: number, order: number, fileMd5: string) : Promise<tools.SignResult> {
+        return new Promise<tools.SignResult>((resolve, reject) => {
+            return this.postRequest({
+                url: '/api/ecs_oe_student/examControl/upyunSignature',
+                parameter: {
+                    examRecordDataId: examRecordDataId,
+                    examStudentId: examStudentId,
+                    order: order,
+                    fileMd5: fileMd5,
+                    fileSuffix: 'mp3'
+                }
+            }).then(res => {
+                var result = {
+                    filePath: res.filePath,
+                    policy: res.policy,
+                    signature: res.signature,
+                    uploadUrl: res.uploadUrl,
+                }
+                resolve((result as tools.SignResult))
+            }, error => {
+                reject(error)
+            })
+        })
+    }
+
+    uploadFile(filePath: string, policy: string, signature: string, uploadUrl: string) : Promise<tools.UpyunResult> {
+        return new Promise(function (resolve, reject) {
+            wx.uploadFile({
+                url: uploadUrl,
+                filePath: filePath,
+                name: 'file',
+                formData: {
+                  authorization: signature,
+                  policy: policy
+                },
+                success: res => {
+                    const result = JSON.parse(res.data)
+                    if (res.statusCode !== 200) {
+                        reject(result.message)
+                    } else {
+                        resolve(result)
+                    }
+                },
+                fail: error => {
+                    if (error.errMsg == 'uploadFile:fail url not in domain list') {
+                        reject('请点击右上角"•••"按钮开启调试模式')
+                    } else {
+                        reject(error.errMsg)
+                    }
+                }
+              })
+        })
+    }
+
+    notifyServer(examRecordDataId: number, examStudentId: number, 
+        order: number, filePath: string) : Promise<string> {
+        return new Promise((resovle, reject) => {
+            return this.postRequest({
+                url: '/api/ecs_oe_student/examControl/audioUploadSave',
+                parameter: {
+                    examRecordDataId: examRecordDataId,
+                    examStudentId: examStudentId,
+                    order: order,
+                    filePath: filePath
+                }
+            }).then( _ => {
+                resovle(filePath)
+            }).catch( exceptions => {
+                reject(exceptions)
+            })
+        })
+    }
+
+    getNotifyResult(examRecordDataId: number, examStudentId: number, order: number, filePath: string) : Promise<any> {
+        return this.postRequest({
+            url: '/api/ecs_oe_student/examControl/getAudioUploadSaveStatus',
+            parameter: {
+                examRecordDataId: examRecordDataId,
+                examStudentId: examStudentId,
+                order: order,
+                filePath: filePath
+            }
+        })
+    }
+
+    getUploadedAudioAnswerList(examRecordDataId: number) : Promise<tools.AudioAnswer[]> {
+        return new Promise((resolve, reject) => {
+            return this.postRequest({
+                url: '/api/ecs_oe_student/examControl/getUploadedAudioAnswerList',
+                parameter: {
+                    examRecordDataId: examRecordDataId
+                }
+            }).then( res => {
+                resolve(res as tools.AudioAnswer[])
+            }, error => {
+                reject(error)
+            }).catch(e => {
+                reject(e)
+            })
+        })
+    }
+
+    getRequest(option: RequestOption, auth: boolean = true): Promise<any> {
+        const that = this
+        const header = auth ? {
+            'Content-Type': 'application/x-www-form-urlencoded',
+            key: that.key,
+            token: that.token
+        } : {
+            'Content-Type': 'application/x-www-form-urlencoded',
+        }
+        return new Promise(function (resolve, reject) {
+            wx.request({
+                url: that.configOption.baseUrl + option.url,
+                method: 'GET',
+                header: header,
+                data: option.parameter,
+                success: res => {
+                    console.log("request success", res);
+                    if (that.isTokenInValidCode(res.statusCode)) {
+                        reject('token失效')
+                        // token失效
+                        if (that.tokenValidCallback) {
+                            that.tokenValidCallback()
+                        }
+                    } else if (res.statusCode !== 200) {
+                        reject((res.data as tools.Result<any>).desc)
+                    } else {
+                        resolve(res.data)
+                    }
+                },
+                fail: function (res) {
+                    console.log("request fail", res);
+                    if (res.errMsg == 'request:fail url not in domain list') {
+                        reject('请点击右上角"•••"按钮开启调试模式')
+                    } else if (res.errMsg == 'request:fail ') {
+                        reject("网络请求失败");
+                    } else {
+                        reject(res.errMsg);
+                    }
+                }
+            })
+        })
+    }
+
+    postRequest(option: RequestOption, auth: boolean = true): Promise<any> {
+        const that = this
+        const header = auth ? {
+            'Content-Type': 'application/x-www-form-urlencoded',
+            key: that.key,
+            token: that.token
+        } : {
+            'Content-Type': 'application/x-www-form-urlencoded',
+        }
+        return new Promise(function (resolve, reject) {
+            wx.request({
+                url: that.configOption.baseUrl + option.url,
+                method: 'POST',
+                header: header,
+                data: option.parameter,
+                success: res => {
+                    console.log("request success", res);
+                    if (that.isTokenInValidCode(res.statusCode)) {
+                        reject('token失效')
+                        // token失效
+                        if (that.tokenValidCallback) {
+                            that.tokenValidCallback()
+                        }
+                    } else if (res.statusCode !== 200) {
+                        reject((res.data as tools.Result<any>).desc)
+                    } else {
+                        resolve(res.data)
+                    }
+                },
+                fail: function (res) {
+                    console.log("request fail", res);
+                    if (res.errMsg == 'request:fail url not in domain list') {
+                        reject('请点击右上角"•••"按钮开启调试模式')
+                    } else if (res.errMsg == 'request:fail ') {
+                        reject("网络请求失败");
+                    } else {
+                        reject(res.errMsg);
+                    }
+                }
+            })
+        })
+    }
+
+    private isTokenInValidCode(code: number) : boolean {
+        return code === 401 || code === 403
+    }
+} 

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff