Browse Source

答题活动开发

limengbo 3 years ago
parent
commit
cf6007f2fc
85 changed files with 13665 additions and 0 deletions
  1. 16 0
      .gitignore
  2. 60 0
      README.md
  3. 74 0
      build.js
  4. 155 0
      src/lib/css/base.css
  5. 3769 0
      src/lib/js/FocusEngine.pad.js
  6. 8 0
      src/lib/js/FocusEngine.pad.min.js
  7. 1467 0
      src/lib/js/TVUtil.js
  8. 1 0
      src/lib/js/blitz.js
  9. 377 0
      src/lib/js/event.js
  10. 1449 0
      src/lib/js/moye.js
  11. 1633 0
      src/lib/js/moye.mobile.js
  12. 10 0
      src/lib/js/vconsole.min.js
  13. 25 0
      src/manifest.json
  14. 24 0
      src/package.json
  15. 11 0
      src/res/tpl/match/ad.tpl
  16. 74 0
      src/res/tpl/match/answer.tpl
  17. 7 0
      src/res/tpl/match/details.juicer
  18. 83 0
      src/res/tpl/match/errorbook.tpl
  19. 16 0
      src/res/tpl/match/match.tpl
  20. 14 0
      src/res/tpl/match/matchDialog.tpl
  21. 108 0
      src/res/tpl/match/ranking.juicer
  22. 1 0
      src/res/values/api.json
  23. 3 0
      src/res/values/value.json
  24. 3 0
      src/res/values/value_en.json
  25. 20 0
      src/service/course.js
  26. 63 0
      src/service/match.js
  27. 1 0
      src/stage/check.html
  28. 280 0
      src/stage/index/Untitled-1.json
  29. BIN
      src/stage/index/assets/favicon.ico
  30. BIN
      src/stage/index/assets/match/1.png
  31. BIN
      src/stage/index/assets/match/2.png
  32. BIN
      src/stage/index/assets/match/3.png
  33. BIN
      src/stage/index/assets/match/baogao_bg.png
  34. BIN
      src/stage/index/assets/match/conduct.png
  35. BIN
      src/stage/index/assets/match/daojishi.png
  36. BIN
      src/stage/index/assets/match/end_bg.png
  37. BIN
      src/stage/index/assets/match/error_icon.png
  38. BIN
      src/stage/index/assets/match/game_course_new.png
  39. BIN
      src/stage/index/assets/match/hand.png
  40. BIN
      src/stage/index/assets/match/huojian.png
  41. BIN
      src/stage/index/assets/match/lately.png
  42. BIN
      src/stage/index/assets/match/no_data.png
  43. BIN
      src/stage/index/assets/match/now.png
  44. BIN
      src/stage/index/assets/match/one.png
  45. BIN
      src/stage/index/assets/match/quit_bg.png
  46. BIN
      src/stage/index/assets/match/ranking_bg.jpg
  47. BIN
      src/stage/index/assets/match/ranking_icon.png
  48. BIN
      src/stage/index/assets/match/reader.png
  49. BIN
      src/stage/index/assets/match/reader_new.png
  50. BIN
      src/stage/index/assets/match/tab_left.png
  51. BIN
      src/stage/index/assets/match/tab_right.png
  52. BIN
      src/stage/index/assets/match/three.png
  53. BIN
      src/stage/index/assets/match/tomorrow.png
  54. BIN
      src/stage/index/assets/match/total_ranking_bg.png
  55. BIN
      src/stage/index/assets/match/two.png
  56. BIN
      src/stage/index/assets/mobile/correct.png
  57. BIN
      src/stage/index/assets/mobile/question_bg.png
  58. BIN
      src/stage/index/assets/mobile/school_bg.png
  59. BIN
      src/stage/index/assets/mobile/select_bg.png
  60. BIN
      src/stage/index/assets/mobile/test_bg.png
  61. BIN
      src/stage/index/assets/mobile/white_bg.png
  62. BIN
      src/stage/index/assets/mobile/wrong.png
  63. BIN
      src/stage/index/assets/pre/board.png
  64. BIN
      src/stage/index/assets/pre/no_ques.png
  65. BIN
      src/stage/index/assets/pre/wrong.png
  66. 72 0
      src/stage/index/index.html
  67. 18 0
      src/stage/index/index.js
  68. 78 0
      src/stage/index/index.less
  69. 157 0
      src/stage/index/scene/match/adScene.js
  70. 385 0
      src/stage/index/scene/match/answerScene.js
  71. 53 0
      src/stage/index/scene/match/detailsScene.js
  72. 591 0
      src/stage/index/scene/match/errorbookScene.js
  73. 138 0
      src/stage/index/scene/match/matchDialogScene.js
  74. 268 0
      src/stage/index/scene/match/matchScene.js
  75. 89 0
      src/stage/index/scene/match/rankingScene.js
  76. 53 0
      src/stage/index/style/match/ad.less
  77. 494 0
      src/stage/index/style/match/answer.less
  78. 21 0
      src/stage/index/style/match/details.less
  79. 618 0
      src/stage/index/style/match/errorbook.less
  80. 190 0
      src/stage/index/style/match/match.less
  81. 55 0
      src/stage/index/style/match/matchDialog.less
  82. 172 0
      src/stage/index/style/match/ranking.less
  83. 182 0
      src/util/efunRequest.js
  84. 98 0
      src/util/login.js
  85. 181 0
      src/util/utils.js

+ 16 - 0
.gitignore

@@ -0,0 +1,16 @@
+.DS_Store
+node_modules
+*/node_modules
+npm-debug.log
+*.swp
+.sw*
+.idea/*
+.editorconfig
+.jshintrc
+.eslintrc
+.travis.yml
+.sass-cache/
+mods/.sass-cache/
+.cache
+build/
+buildLocal/

+ 60 - 0
README.md

@@ -0,0 +1,60 @@
+一、搭建开发环境
+安装nodejs(4.2.6)
+下载地址:https://nodejs.org/download/release/v4.2.6/ 下载对应操作系统的安装包并安装。
+
+node -v
+检测版本是否正常
+
+检查npm(3.10.5)
+npm -v
+判断npm版本是否为3.10.5(一般3.x.x版本都可以),如满足要求则忽略下面命令。
+
+(sudo) npm install -g npm --registry=http://registry.npm.taobao.org
+安装moye脚手架
+追加--registry参数可显著提高安装速度
+
+(sudo) npm install -g cyclone-shell --registry=http://registry.npm.taobao.org
+
+
+二、创建工程
+创建工程文件夹
+mkdir ott
+
+cd ott
+创建工程目录
+moye init
+项目默认名称和文件夹名保持一致,点击回车,提示输入应用包名:
+
+工程目录
+
+目录结构说明
+└── ott
+      ├── package.json            // 存放项目基本信息和node的依赖关系配置
+      ├── README.md               // 项目说明文档
+      ├── doc                     // 存放生成项目API文档
+      ├── src                     // 项目源文件目录
+          ├── component           // 存放UI组件文件
+          ├── lib                 // 存放基础js和css文件和第三方库
+          ├── res                 // 资源文件夹
+              ├── tpl             // 模版文件
+              ├── img             // 图片资源
+              └── values          // 国际化适配文件
+          └── stage             
+             └──index             // 一个页面
+                 ├── scene        // 里面放js文件
+                 ├── assets       // 存放本scene的独立资源(即不被index.js或者index.less所引用的资源)
+                 ├── index.js
+                 ├── index.less
+                 └── index.html
+          ├── service             // cyclone service 代码
+          ├── util                // 存放应用级别工具组件
+          └── manifest.json       // cyclone app 配置文件
+      ├── buildLocal              // 执行 moye build-cyclone 后生成的 cyclone 代码, 用于打包cyclone wpk包
+      └── build                   // 编译后的代码,用于在本地浏览器上调试页面
+
+
+
+三、本地预览
+moye b
+moye s -w
+根据提示在浏览器输入:http://127.0.0.1:8080/efunbox-xyyf-web/1.0.0/stage/index/index.html

+ 74 - 0
build.js

@@ -0,0 +1,74 @@
+var spawn = require('child_process').spawn;
+const path = require('path');
+const fs = require('fs');
+
+const writeApiJson = function () {
+  /* API Url set by build parameter */
+  let apiConfig = {
+    "course_api_url": "http://m-xyyf-api.ai160.com/",
+    "lesson_api_url": "http://m-xyyf-api.ai160.com/lesson",
+    "tts_api_url": "https://yfxxt-api.ai160.com/baidu/accessToken",
+    "xiaomi_qr": "https://h5.tv.mi.com/store/thirdparty/pricetag/shortkey/",
+    "en": "pro"
+  };
+  let options = process.argv;
+  for (let i = 0; i < options.length; i++) {
+    if (options[i].indexOf('-test') == 0) {
+      apiConfig = { // 开发环境
+        "course_api_url": "http://m-xyyf-api.ai160.com/",
+        "lesson_api_url": "http://m-xyyf-api.ai160.com/lesson",
+        "tts_api_url": "https://yfxxt-api.ai160.com/baidu/accessToken",
+        "xiaomi_qr": "https://h5.tv.mi.com/store/thirdparty/pricetag/shortkey/",
+        "en": "test"
+      };
+    }
+  }
+  fs.writeFileSync(path.join(__dirname, 'src/res/values/api.json'), JSON.stringify(apiConfig));
+  console.log('Write API Config Success!');
+}
+
+const writeTimeStamp = function () {
+  /* JavaScript and CSS Import TimeStamp set by UnixTimeStamp */
+  let ts = Math.floor(new Date().getTime() / 1000);
+  let fileStr = fs.readFileSync(path.join(__dirname, 'build/stage/index/index.html')) + '';
+  fileStr = fileStr.replace(/\[timestamp\]/g, ts); //时间戳标记替换为时间戳
+  fileStr = fileStr.replace(/\/index.js/g, '/index.min.js'); //index.js替换为index.min.js
+  fileStr = fileStr.replace(/\/index.css/g, '/index.min.css'); //index.js替换为index.min.js
+  fs.writeFileSync(path.join(__dirname, 'build/stage/index/index.html'), fileStr);
+  console.log('Write JavaScript and CSS Import TimeStamp = ' + ts + ' Success!');
+}
+
+//因为引用在代码里面的json文件,在webpack打包时会直接打在生成后的index.js里面,所以需要在执行moye b 之前,就修改src目录内的api.json
+writeApiJson();
+
+//启动
+const moyeb = spawn('moye', ['b'], {
+  shell: true
+});
+
+// 捕获标准输出并将其打印到控制台 
+moyeb.stdout.on('data', function (data) {
+
+  let stdoutStr = data.toString();
+  if (stdoutStr.indexOf("Finished \'default\' after") >= 0) {
+    writeTimeStamp();
+    setTimeout(function () {
+      console.log('TV Package Build Success!');
+      moyeb.kill();
+    }, 1000);
+  }
+});
+
+// 捕获标准错误输出并将其打印到控制台 
+moyeb.stderr.on('data', function (data) {
+  console.log('' + data);
+});
+
+// 注册子进程关闭事件 
+moyeb.on('exit', function (code, signal) {
+  if (code && code != 0) {
+    console.log('Build Error!! Code:' + code);
+    return;
+  }
+  console.log('Build Done!!');
+});

+ 155 - 0
src/lib/css/base.css

@@ -0,0 +1,155 @@
+/** 清除内外边距 **/
+body,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+hr,
+p,
+blockquote,
+dl,
+dt,
+dd,
+ul,
+li,
+pre,
+form,
+fieldset,
+legend,
+button,
+input,
+textarea,
+th,
+td {
+  margin: 0;
+  padding: 0;
+}
+/** 设置默认字体 **/
+body,
+button,
+input,
+select,
+textarea {
+  font: 12px/1.5 tahoma, arial, 'Heiti SC', '微软雅黑', sans-serif;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  font-size: 100%;
+}
+address,
+cite,
+dfn,
+em,
+var {
+  font-style: normal;
+}
+/* 将斜体扶正 */
+code,
+kbd,
+pre,
+samp {
+  font-family: courier new, courier, monospace;
+}
+/* 统一等宽字体 */
+small {
+  font-size: 12px;
+}
+/* 小于 12px 的中文很难阅读,让 small 正常化 */
+/** 重置列表元素 **/
+ul,
+ol {
+  list-style: none;
+}
+/** 重置文本格式元素 **/
+a {
+  text-decoration: none;
+  color: #000;
+}
+a:hover {
+  text-decoration: underline;
+}
+sup {
+  vertical-align: text-top;
+}
+/* 重置,减少对行高的影响 */
+sub {
+  vertical-align: text-bottom;
+}
+/** 重置表单元素 **/
+legend {
+  color: #000;
+}
+/* for ie6 */
+fieldset,
+img {
+  border: 0;
+}
+/* img 搭车:让链接里的 img 无边框 */
+img {
+  vertical-align: bottom;
+}
+button,
+input,
+select,
+textarea {
+  font-size: 100%;
+}
+/* 使得表单元素在 ie 下能继承字体大小 */
+/* 注:optgroup 无法扶正 */
+/** 重置表格元素 **/
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+/* 重置 HTML5 元素 */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section,
+summary,
+time,
+mark,
+audio,
+video {
+  display: block;
+  margin: 0;
+  padding: 0;
+}
+mark {
+  background: #ff0;
+}
+.clearfix:after {
+  content: ".";
+  display: block;
+  height: 0;
+  clear: both;
+  visibility: hidden;
+}
+.clearfix {
+  display: inline-block;
+}
+/* Hides from IE-mac */
+* html .clearfix {
+  height: 1%;
+}
+.clearfix {
+  display: block;
+}
+/* End hide from IE-mac */
+body {
+  font: 12px/1.5 tahoma, arial, 'Heiti SC', 'Microsoft Yahei', sans-serif;
+  position: relative;
+}

File diff suppressed because it is too large
+ 3769 - 0
src/lib/js/FocusEngine.pad.js


File diff suppressed because it is too large
+ 8 - 0
src/lib/js/FocusEngine.pad.min.js


File diff suppressed because it is too large
+ 1467 - 0
src/lib/js/TVUtil.js


File diff suppressed because it is too large
+ 1 - 0
src/lib/js/blitz.js


+ 377 - 0
src/lib/js/event.js

@@ -0,0 +1,377 @@
+// 安卓TV端专用:上下左右移动、选中跳转
+var keyTypeMap = {
+    'key_up': 'keyup',
+    'key_down': 'keydown'
+}
+var keyCodeMap = {
+    'KEY_SELECT': 13,
+    'KEY_BACK': 27,
+    'KEY_UP': 38,
+    'KEY_RIGHT': 39,
+    'KEY_DOWN': 40,
+    'KEY_LEFT': 37,
+}
+
+function getUrlValue(name) {
+    return new URLSearchParams(location.search).get(name)
+}
+
+function boxEventHandler(type, keyCode) {
+    console.log('返回键')
+    if (type === 'key_up' && keyCode === 'KEY_SELECT') {
+        console.log('返回键123')
+        if (typeof efunboxJS !== 'undefined') {
+            efunboxJS.clickMusic('1', '1')
+        }
+    }
+    var leaf = FocusEngine.getFocusedLeaf();
+    var eid = document.getElementById(leaf.id);
+    var evt = document.createEvent('Event');
+    evt.initEvent(keyTypeMap[type], true, true);
+    evt.keyCode = keyCodeMap[keyCode];
+    eid.dispatchEvent(evt);
+}
+
+function login() {
+    if (typeof efunboxJS != "undefined") {
+        efunboxJS.getUserPhone();
+    }
+}
+
+function buy(json) {
+    const buyData = JSON.stringify(json)
+    if (typeof efunboxJS != "undefined") {
+        efunboxJS.toPay(buyData)
+        // efunboxJS.getUserPhone();
+    }
+}
+
+function payBack(data) {
+    this.moye.curScene.buySuccess(data)
+}
+
+function speak(text, flag) {
+    try {
+        if (flag) {
+            if (typeof efunboxJS != "undefined") {
+                efunboxJS.speak(text, true)
+            }
+        } else {
+            if (typeof efunboxJS != "undefined") {
+                efunboxJS.speak(text, false)
+            }
+        }
+    } catch (err) {
+        console.log('没有安卓方法产生的', err)
+    }
+}
+
+function handleIntent(name, slots) {
+    if (name === 'ai.dueros.common.default_intent') {
+        speak('我没听清,可以点击触屏进行操作');
+        return false;
+    }
+    this.moye.curScene.aiHandle(name, slots)
+}
+
+function loginBack(phone) {
+    localStorage.setItem('phone', phone)
+    this.moye.curScene.loginSuccess(phone);
+    // this.moye.curScene.loginSuccess(phone)
+}
+
+function clickMp3() {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            efunboxJS.clickMusic('10', '10')
+        }
+    } catch (err) {
+        console.log('没有安卓方法产生的', err)
+    }
+}
+
+// 答对音效
+function rightMp3() {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            efunboxJS.startCorrect('10', '10')
+        }
+    } catch (err) {
+        console.log('没有安卓方法产生的', err)
+    }
+}
+// 答错音效
+function wrongMp3() {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            efunboxJS.startWrong('10', '10')
+        }
+    } catch (err) {
+        console.log('没有安卓方法产生的', err)
+    }
+}
+
+// 调用android方法,销毁webview后重新load首页,解决webview内存溢出问题
+var firstTimestamp = Date.parse(new Date());
+
+function restartWebView() {
+    if (typeof efunboxJS != "undefined") {
+        // 每次判断是否累加到指定时间,决策是否执行重载
+        if (Date.parse(new Date()) - firstTimestamp > 30 * 60 * 1000) {
+            firstTimestamp = Date.parse(new Date());
+            
+            if (typeof efunboxJS != "undefined") {
+                efunboxJS.reloadMain();
+            }
+        }
+    }
+}
+// 安卓日历回调
+function getAndroidDate(year, month, day) {
+    console.log(year, month, day)
+    this.moye.curScene.getDate(year, month, day)
+}
+// 配音秀获取测评结果
+function engineCallBack(json) {
+    this.moye.curScene.getResult(json)
+}
+// 配音秀上传视频完成
+function uploadCallBack(obj) {
+    console.log('uploadCallBack')
+    this.moye.curScene.getUpLoadUrl(obj)
+
+}
+// 获取手机号
+function getPhone() {
+    console.log(1111)
+    try {
+        if (typeof efunboxJS != "undefined") {
+            efunboxJS.showPhoneNumberLogin()
+        }
+    } catch (err) {
+        console.log('没有安卓方法产生的', err)
+    }
+}
+// 安卓回调获取手机号
+function phoneLogin(phoneNumber) {
+    this.moye.curScene.getPhoneLogin(phoneNumber)
+}
+// 安卓回调验证码登陆成功
+function phoneSuccess(data) {
+    this.moye.curScene.phoneSuccess(data)
+}
+// 安卓关闭授权弹窗
+function closeLogin(code) {
+    if (getUrlValue('version') * 1 > 10010) {
+        // 如果不是在首页直接关闭
+        console.log('关闭了')
+        if (document.querySelector('#ChineseMath').style.visibility === 'hidden') {
+            console.log('不是在首页关闭')
+            this.moye.hideScene({}, "ChineseMath")
+        }
+    } else {
+        this.moye.curScene.closeLogin(code);
+    }
+}
+// 华为一键登录
+function huaweiLoginSuccess(data) {
+    this.moye.curScene.huaweiLogin(data)
+}
+// 安卓调用切换手机号
+function changePhoneLogin(phoneNumber) {
+    this.moye.curScene.changePhone()
+}
+// 安卓存储第几次进来
+function androidSetStorage(key, data) {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            efunboxJS.setPreData(key, data)
+        }
+    } catch (err) {
+        console.log('没有安卓方法产生的', err)
+    }
+}
+
+function androidGetStorage(key) {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            return efunboxJS.getPreData(key)
+        }
+    } catch (err) {
+        console.log('没有安卓方法产生的', err)
+    }
+}
+
+function changeColor(color) {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            efunboxJS.setBackGround(color)
+        }
+    } catch (err) {
+        console.log('错误', err)
+    }
+}
+
+function showPhoneNumberLogin() {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            efunboxJS.showPhoneNumberLogin()
+        }
+    } catch (err) {
+        console.log('没有安卓方法产生的', err)
+    }
+}
+// 新的登录
+function newLogin() {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            efunboxJS.showPhoneNumberLoginLandscape()
+        }
+    } catch(error) {
+        console.log('没有安卓方法', err)
+    }
+}
+// 支付回调
+function commonPayResult(num) {
+    console.log(num)
+    if (num == 0) {
+        // 支付成功
+        console.log('paySuccess')
+        this.moye.curScene.paySuccess();
+    } else if (num == -2) {
+        console.log('userCancel')
+        // 支付取消
+        this.moye.curScene.payCancel();
+    } else {
+        console.log('payFail')
+        // 支付失败
+        this.moye.curScene.payFail();
+    }
+
+}
+// 设置屏幕
+function setScreen(num) {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            efunboxJS.setScreenOriention(num)
+        }
+    } catch (err) {
+        console.log('没有安卓方法产生的', err)
+    }
+}
+// 重新加载页面
+function setScreen(num) {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            efunboxJS.setScreenOriention(num)
+        }
+    } catch (err) {
+        console.log('没有安卓方法产生的', err)
+    }
+}
+// 重新加载页面
+function relWebView() {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            efunboxJS.relWebView()
+        }
+    } catch (err) {
+        console.log('没有安卓方法产生的', err)
+    }
+}
+// 安卓提示窗
+function toast(str) {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            efunboxJS.showToast(str)
+        }
+    } catch (err) {
+        console.log('没有安卓方法产生的', err)
+    }
+}
+// 删除用户
+function removeUser(str) {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            efunboxJS.removeSPUserPhone()
+        }
+    } catch (err) {
+        console.log('没有安卓方法产生的', err)
+    }
+}
+
+// 手机没有安装微信
+function noWeChat() {
+    if (this.moye.curScene.noWeChat) {
+        this.moye.curScene.noWeChat();
+    }
+}
+// 支付
+function huaweiOrder(data) {
+    try {
+        data = JSON.stringify(data)
+        efunboxPay.HuaWeiPay(data)
+    } catch (err) {
+        console.log('没有华为支付', err)
+    }
+}
+// 华为支付成功
+function huaweiOrderSuccess(data) {
+    console.log('------------hhhhh', this.moye.curScene)
+    this.moye.curScene.huaweiNotify(data);
+}
+// 查询华为有没有掉单
+function huaweiIsFail() {
+    try {
+        efunboxPay.checkDropOrder()
+    } catch (err) {
+        console.log('没有华为支付', err)
+    }
+}
+// 掉单查询
+function checkDropOrder(data) {
+    this.moye.curScene.huaweiCheckDropOrder(data);
+}
+// 华为查询失败订单
+function huaweiOrderList(data) {
+    try {
+        efunboxPay.huaweiConsumeOwned(data, "")
+    } catch (err) {
+        console.log('没有华为支付', err)
+    }
+}
+// 华为发货是否成功
+function huaweiConsumeOwned(data, orderId) {
+    this.moye.curScene.huaweiConsumeOwned(data);
+}
+// 天猫登录
+function getTBUser() {
+    try {
+        if (typeof efunboxJS != "undefined") {
+            return efunboxJS.getTBUserData()
+        }
+     } catch (err) {
+         console.log('没有天猫登录方法', err)
+     }
+}
+// 天猫语音
+function TMailASR(data) {
+    this.moye.curScene.TMailASRWeb(data);
+}
+// 天猫支付
+function TBbuy(data) {
+    try {
+        efunboxPay.TMailPay(data)
+    } catch (err) {
+        console.log('没有天猫支付方法', err)
+    }
+}
+// 天猫支付成功
+function AliPayCallBack(flag, data) {
+    this.moye.curScene.TMBuySuccess(flag, data);
+}
+
+function xiaoAiPayBack(json) {
+    let res = JSON.parse(json);
+    this.moye.curScene.xiaoMiPayBack(res);
+}

File diff suppressed because it is too large
+ 1449 - 0
src/lib/js/moye.js


File diff suppressed because it is too large
+ 1633 - 0
src/lib/js/moye.mobile.js


File diff suppressed because it is too large
+ 10 - 0
src/lib/js/vconsole.min.js


+ 25 - 0
src/manifest.json

@@ -0,0 +1,25 @@
+{
+  "package": "test.tv.yunos.com",
+  "versioncode": 2100100000,
+  "versionname": "1.0.0",
+  "use-sdk": "yunos_webos_v1.0",
+  "label": "@title",
+  "icon": "res/img/icon.png",
+  "developer": "ali",
+  "permissions": [],
+  "use-permissions": [
+    {"name": "INTERNET.permission.yunos.com"}
+  ],
+  "stages": {
+    "index": {
+      "description": "This is main stage.",
+      "entry": "stage/index/index.html",
+      "type": "web",
+      "category": [
+        "yunos.category.main"
+      ],
+      "require-permissions": [],
+      "background-color": "#ffffffff"
+    }
+  }
+}

+ 24 - 0
src/package.json

@@ -0,0 +1,24 @@
+{
+  "name": "efunbox-competition",
+  "version": "1.0.0",
+  "description": "",
+  "author": {
+    "name": "ali",
+    "email": ""
+  },
+  "repository": {
+    "type": "git",
+    "url": ""
+  },
+  "main": "index.js",
+  "babel": {
+  },
+  "license": "ISC",
+  "keywords": [
+    "EfuboxBaiduWeb"
+  ],
+  "dependencies": {
+  },
+  "devDependencies": {
+  }
+}

+ 11 - 0
src/res/tpl/match/ad.tpl

@@ -0,0 +1,11 @@
+<div id="ad" class="ad-con" fe-role="Switch">
+  <div class="ad_icon"></div>
+  <div class="log" style="display: none;"></div>
+  <div class="ad_img_box">
+      <div class="ad_img_btn" fe-role="Widget" data-type="jump">
+          <span>跳过</span>
+          <span class="ad_count_time"></span>
+      </div>
+      <img id="ad_img" class="img_banner" />
+  </div>
+</div>

+ 74 - 0
src/res/tpl/match/answer.tpl

@@ -0,0 +1,74 @@
+<div id="answer" class="answer_change center" fe-role="Switch" fe-cfg="disable_cache_pre:yes,disable_child_cache:yes,default_focus:no">
+    <div class="review_main">
+      <div class="anser_title">中华历史故事</div>
+      <div class="anser_time_submit">
+          <div class="answer_time">
+              <img  src="assets/match/daojishi.png"/>
+              <span></span>
+          </div>
+          <div class="answer_submit" fe-role="Widget" data-event="submitAnswer">交卷</div>
+      </div>
+      <div class="review_question">
+            <!-- 序号中间的横杠 -->
+            <!-- <div class="number_menu_bar"></div> -->
+            <!-- 序号 -->
+            <div class="number_menu" id="number_menu_wrapper">
+                <!--
+                <div class="scroll-list" id="number_menu">
+
+                </div>
+                -->
+            </div>
+            <!-- 题目切换 -->
+            <div class="prev_next">
+                <div class="prev" id="prev_first" fe-role="Widget" data-event="prev">上一题</div>
+                <div class="next" id="next_final" fe-role="Widget" data-event="next">下一题</div>
+            </div>
+            <!-- 环节主要内容区域 -->
+            <div id="lesson_main" class="lesson_main">
+                <!-- 文本区 -->
+                <text class="text_area" id="text_area">
+
+                </text>
+                <!-- 答案区 -->
+                <div class="answer_area" id='answer_area'>
+                    <div class="answer_item answer_a" id="answer_a" fe-role="Widget" data-event="answer" data-answer="a" data-index="0">
+                        <!-- <img class="answer_icon" src="http://ai-img.ai160.com/images/xyyf-res/lesson/icon_a.png" alt=""> -->
+                        <span class='answer_icon' id="answer_a_icon">A、</span>
+                        <img class="right_wrong" id="right_wrong_a" src="http://asxx-img.ai160.com/images/asxx/assets/pre/wrong.png" alt="">
+                        <div class="answer_text" id="answer_a_text"></div>
+                    </div>
+                    <div class="answer_item answer_b" id="answer_b" fe-role="Widget" data-event="answer" data-answer="b" data-index="1">
+                        <!-- <img class="answer_icon" src="http://ai-img.ai160.com/images/xyyf-res/lesson/icon_b.png" alt=""> -->
+                        <span class='answer_icon' id="answer_b_icon">B、</span>
+                        <img class="right_wrong" id="right_wrong_b" src="http://asxx-img.ai160.com/images/asxx/assets/pre/wrong.png" alt="">
+                        <div class="answer_text" id="answer_b_text"></div>
+                    </div>
+                    <div class="answer_item answer_c" id="answer_c" fe-role="Widget" data-event="answer" data-answer="c" data-index="2">
+                        <!-- <img class="answer_icon" src="http://ai-img.ai160.com/images/xyyf-res/lesson/icon_c.png" alt=""> -->
+                        <span class='answer_icon' id="answer_c_icon">C、</span>
+                        <img class="right_wrong" id="right_wrong_c" src="http://asxx-img.ai160.com/images/asxx/assets/pre/wrong.png" alt="">
+                        <div class="answer_text" id="answer_c_text"></div>
+                    </div>
+                </div>
+                <!-- 答案解析 -->
+                <div class="analysis" id="analysis">
+                    <div class="answer_line">
+                        <div class="user_answer">我的选择:<span id="user_answer"></span></div>
+                        <div class="correct_answer">正确答案:<span id="correct_answer"></span></div>
+                    </div>
+                    <div class="analysis_main" id="analysis_main">
+                        <span class="orange">解析:</span>
+                        <text id="analysis_con"></text>
+                    </div>
+                    <img id="analysis_img" style="width: 16.9rem;height: 5.52rem;"/>
+                    <!-- 底部按钮 -->
+                    <!-- <div class="analysis_btn"> -->
+                    <div class="analysis_btn" id="analysis_btn" fe-role="Widget" data-event="study">再深入学习一次</div>
+                    <!-- <div class="baidu_search"></div> -->
+                    <!-- </div> -->
+                </div>
+            </div>
+      </div>
+    </div>
+</div>

+ 7 - 0
src/res/tpl/match/details.juicer

@@ -0,0 +1,7 @@
+<div class="details" id="details" fe-role="Switch">
+  <div class="img_container">
+        {@each data as it, ind}
+          <img src="${it}">
+        {@/each}
+  </div>
+</div>

+ 83 - 0
src/res/tpl/match/errorbook.tpl

@@ -0,0 +1,83 @@
+<div id="errorbook" class="errorbook center" fe-role="Switch" fe-cfg="disable_cache_pre:yes,disable_child_cache:yes,default_focus:no">
+    <div class="errorbook-tab">
+        <div class="tab-item">
+            知识竞赛
+        </div>
+    </div>
+    <!-- 题目切换 -->
+    <div class="prev_next">
+        <div class="prev" id="prev_first" fe-role="Widget" data-event="prev">
+            上一题
+            <!--
+            <text>上一题</text>
+            <span></span>
+            -->
+        </div>
+        <div class="next" id="next_final" fe-role="Widget" data-event="next">
+            下一题
+            <!--
+            <text>下一题</text>
+            <span></span>
+            -->
+        </div>
+    </div>
+    <div class="review_main">
+        <!-- 课程切换 -->
+        <!--
+        <div class="category">
+            <div class="category_btn focus" id='chineses' fe-role="Widget" data-type="1" data-event='category'>语文
+            </div>
+            <div class="category_btn" id='math' fe-role="Widget" data-type="2" data-event='category'>数学</div>
+        </div>
+        -->
+        <!-- 环节主要内容区域 -->
+        <div id="lesson_main" class="lesson_main">
+            <!-- 文本区 -->
+            <text class="text_area" id="text_area">
+
+            </text>
+            <!-- 答案区 -->
+            <div class="answer_area" id='answer_area'>
+                <div class="answer_item answer_a" id="answer_a" fe-role="Widget" data-event="answer" data-answer="a" data-index="0">
+                    <!-- <img class="answer_icon" src="http://ai-img.ai160.com/images/xyyf-res/lesson/icon_a.png" alt=""> -->
+                    <span class='answer_icon' id="answer_a_icon">A</span>
+                    <img class="right_wrong" id="right_wrong_a" src="http://asxx-img.ai160.com/images/asxx/assets/pre/wrong.png" alt="">
+                    <div  class="answer_text" id="answer_a_text"></div>
+                </div>
+                <div class="answer_item answer_b" id="answer_b" fe-role="Widget" data-event="answer" data-answer="b" data-index="1">
+                    <!-- <img class="answer_icon" src="http://ai-img.ai160.com/images/xyyf-res/lesson/icon_b.png" alt=""> -->
+                    <span class='answer_icon' id="answer_b_icon">B</span>
+                    <img class="right_wrong" id="right_wrong_b" src="http://asxx-img.ai160.com/images/asxx/assets/pre/wrong.png" alt="">
+                    <div  class="answer_text" id="answer_b_text"></div>
+                </div>
+                <div class="answer_item answer_c" id="answer_c" fe-role="Widget" data-event="answer" data-answer="c" data-index="2">
+                    <!-- <img class="answer_icon" src="http://ai-img.ai160.com/images/xyyf-res/lesson/icon_c.png" alt=""> -->
+                    <span class='answer_icon' id="answer_c_icon">C</span>
+                    <img class="right_wrong" id="right_wrong_c" src="http://asxx-img.ai160.com/images/asxx/assets/pre/wrong.png" alt="">
+                    <div class="answer_text" id="answer_c_text"></div>
+                </div>
+            </div>
+            <!-- 知道了-->
+            <div class="know" id="answer_know" fe-role="Widget" data-event="know"></div>
+            <!-- 答案解析 -->
+            <div class="analysis" id="analysis">
+                <div class="answer_line">
+                    <div class="user_answer">我的选择:<span id="user_answer"></span></div>
+                    <div class="correct_answer">正确答案:<span id="correct_answer"></span></div>
+                </div>
+                <div class="analysis_main" id="analysis_main">
+                    <span class="orange">解析:</span>
+                    <text id="analysis_con"></text>
+                </div>
+                <img id="analysis_img" style="width: 16.5rem;height: 5.52rem;"/>
+            </div>
+
+            <img class="no_ques" id="no_ques" src="http://asxx-img.ai160.com/images/asxx/assets/pre/no_ques.png" />
+            <div class="scroll_analysis" id="scroll_analysis" fe-role='Widget' data-event="analysis">
+            </div>
+        </div>
+        <div class="tip_modal" id="tip_modal">
+        </div>
+    </div>
+
+</div>

+ 16 - 0
src/res/tpl/match/match.tpl

@@ -0,0 +1,16 @@
+<div id="match" class="match center" fe-role="Switch">
+  <div class="table">
+    <div class="whole_match match_title" data-event="whole" fe-role="Widget"></div>
+    <div class="my_match match_title" data-event="my" fe-role="Widget"></div>
+  </div>
+  <div class="match_two_icon">
+    <img class="error_icon"src="assets/match/error_icon.png" data-event="errorbook" fe-role="Widget"/>
+    <img class="ranking_icon" src="assets/match/ranking_icon.png"  data-event="ranking" fe-role="Widget"/>
+  </div>
+  <div class="rocket_icon" data-event="goTo" fe-role="Widget">
+      <img src="assets/match/huojian.png" />
+  </div>
+  <div class="match_container">
+
+  </div>
+</div>

+ 14 - 0
src/res/tpl/match/matchDialog.tpl

@@ -0,0 +1,14 @@
+<div id="matchDialog" class="match-dialog" fe-role="Switch">
+    <div class="dialog-container" style="background: url(${bg}) 0 0 / 100% 100%">
+        <div class="report-container" style="display: ${hide}">
+            <div>用时:1分钟</div>
+            <div>答对:${reportCon.rightAmount}/${reportCon.answerAmount}题</div>
+            <div>得分:${ totalScore }分</div>
+        </div>
+        <div class="one-btn ${type}" data-event="${type}"  fe-role="Widget" style="display: none"></div>
+        <div class="two-btn" style="display: none">
+            <div class="quit" data-event="quit"  fe-role="Widget"></div>
+            <div class="cancel" data-event="cancel"  fe-role="Widget"></div>
+        </div>
+    </div>
+</div>

+ 108 - 0
src/res/tpl/match/ranking.juicer

@@ -0,0 +1,108 @@
+<div class="ranking center" id="ranking" fe-role="Switch">
+    <div class="ranking_title">${type === 'single' ? data.race.title : '排行榜'}</div>
+    {@if data.memberRaceResult}
+    <div class="singl_my_ranking">
+      <div class="ranking_left_item">
+        <span class="ranking_number">${data.myRanking === 0 ?  '-' : data.myRanking}</span>
+      </div>
+      <div class="ranking_right_item">
+        <div class="ranking_name">我的成绩</div>
+        <div class="ranking_correct">答对${data.memberRaceResult.rightAmount}题</div>
+        <div class="ranking_fraction">${data.memberRaceResult.totalScore}分</div>
+      </div>
+    </div>
+    {@/if}
+    <div class="ranking_continer">
+      <!--通过type判断总排行榜还是单个排行榜-->
+      {@if type === 'single'}
+        {@if data.rankingListVOS.length > 0}
+          {@each data.rankingListVOS as it,index}
+          <div class="my_ranking_details">
+            <div class="ranking_item" style="background: #FAFAFA;">
+              <div class="ranking_head_title" style="text-align: center;width: 1rem;">
+                排名
+              </div>
+              <div class="ranking_head_title" style="width: 3rem;">昵称</div>
+              <div class="ranking_head_title" style="text-align: center;width: 1rem;">
+                成绩
+              </div>
+            </div>
+            {@each it as childIt, ind}
+            <div class="ranking_item" style="${ ((index * 10 + ind * 1 + 1) % 2 === 0) && 'background: #FAFAFA;'}">
+              <div class="ranking_left_item">
+                {@if rankImgs[index * 10 + ind * 1]}
+                <img src="assets/match/${rankImgs[index * 10 + ind * 1]}.png" class="ranking_number">
+                {@else}
+                <span class="ranking_number" style="${(index * 10 + ind * 1 + 1 > 10) && 'color: #616161'}">${index * 10 + ind * 1 + 1}</span>
+                {@/if}
+              </div>
+              <div class="ranking_name">${childIt.member.nickName}</div>
+              <div class="ranking_right_item">
+                ${childIt.memberRaceResult.totalScore}分
+              </div>
+            </div>
+            {@/each}
+          </div>
+          {@/each}
+        {@else}
+          <div class="no_data">
+            <img src="assets/match/no_data.png">
+          </div>
+        {@/if}
+
+      {@else}
+      {@if data.length > 0}
+        {@each data as it,index}
+        <div class="ranking_details">
+          <div class="ranking_details_title">
+            <h3>${it.race.title}</h3>
+          </div>
+          {@if it.rankingListVOS.length > 0}
+            <div class="ranking_item" style="background: #FAFAFA;">
+              <div class="ranking_head_title" style="text-align: center;width: 1rem;">
+                排名
+              </div>
+              <div class="ranking_head_title" style="width: 3rem;">昵称</div>
+              <div class="ranking_head_title" style="text-align: center;width: 1rem;">
+                成绩
+              </div>
+            </div>
+            {@each it.rankingListVOS as childIt, ind}
+              <div class="ranking_item" style="${ ((ind * 1 + 1) % 2 === 0) && 'background: #FAFAFA;'}">
+                <div class="ranking_left_item">
+                  {@if rankImgs[ind]}
+                  <img src="assets/match/${rankImgs[ind]}.png" class="ranking_number">
+                  {@else}
+                  <span class="ranking_number">${ind * 1+1}</span>
+                  {@/if}
+                </div>
+                <div class="ranking_name">${childIt.member.nickName}</div>
+                <div class="ranking_right_item">
+                  ${childIt.memberRaceResult.totalScore}分
+                </div>
+              </div>
+            {@/each}
+          {@else}
+              <img class="item_no_data" src="assets/match/no_data.png">
+          {@/if}
+          {@if it.memberRaceResult}
+          <div class="ranking_item my_ranking">
+            <div class="ranking_left_item">
+              <span class="ranking_number" style="color: #FF0000">${it.myRanking === 0 ?  '-' : it.myRanking}</span>
+            </div>
+            <div class="ranking_name" style="color: #FF0000">我的成绩</div>
+            <div class="ranking_right_item" style="color: #FF0000">
+              ${it.memberRaceResult.totalScore}分
+            </div>
+          </div>
+          {@/if}
+        </div>
+        {@/each}
+      {@else}
+        <div class="no_data">
+          <img src="assets/match/no_data.png">
+        </div>
+      {@/if}
+    {@/if}
+    </div>
+</div>

+ 1 - 0
src/res/values/api.json

@@ -0,0 +1 @@
+{"course_api_url":"http://m-xyyf-api.ai160.com/","lesson_api_url":"http://m-xyyf-api.ai160.com/lesson","tts_api_url":"https://yfxxt-api.ai160.com/baidu/accessToken","xiaomi_qr":"https://h5.tv.mi.com/store/thirdparty/pricetag/shortkey/","en":"test"}

+ 3 - 0
src/res/values/value.json

@@ -0,0 +1,3 @@
+{
+  "title": "Moye Demo"
+}

+ 3 - 0
src/res/values/value_en.json

@@ -0,0 +1,3 @@
+{
+  "title":"Moye Demo"
+}

+ 20 - 0
src/service/course.js

@@ -0,0 +1,20 @@
+import efunRequest from '../util/efunRequest';
+import APIConfig from '../res/values/api.json';
+
+function genLessonAPIUrl(path) {
+    return APIConfig.course_api_url + path;
+}
+export default class courseApi {
+    // 手机号注册
+    static mobileLogin(data) {
+        return efunRequest.getHttpRequest().url(genLessonAPIUrl('member/mobileAuth')).data(data).post();
+    }
+    // 第三方登录
+    static partnerLogin(data) {
+        return efunRequest.getHttpRequest().url(genLessonAPIUrl(`member/partnerLogin`)).data(data).post();
+    }
+    // 广告页图片
+    static getAdImg(code) {
+        return efunRequest.getHttpRequest().url(genLessonAPIUrl(`/channel/${code}`)).get();
+    }
+}

+ 63 - 0
src/service/match.js

@@ -0,0 +1,63 @@
+/**答题活动接口 */
+import efunRequest from '../util/efunRequest';
+import APIConfig from '../res/values/api.json';
+function genLessonAPIUrl(path) {
+  return APIConfig.course_api_url + path;
+}
+export default class matchApi {
+  /**获取全部赛事 */
+  static getRaceAll(grade) {
+    return efunRequest.getHttpRequest().params({
+      grade
+    }).url(genLessonAPIUrl('kt/race/all')).get();
+  }
+  /**我的赛事 */
+  static getMyRace(grade) {
+    return efunRequest.getHttpRequest().params({
+      grade
+    }).url(genLessonAPIUrl('kt/race/my')).get();
+  }
+  /**获取赛事信息 */
+  static getRaceInfo(raceId) {
+    return efunRequest.getHttpRequest().url(genLessonAPIUrl('kt/race/info')).params({
+      raceId
+    }).get();
+  }
+  /**提交单词答题 */
+  static postQuestion(data) {
+    return efunRequest.getHttpRequest().url(genLessonAPIUrl('kt/question')).data(data).post();
+  }
+  /**提交赛事 */
+  static postQuestionSubmit(data) {
+    return efunRequest.getHttpRequest().url(genLessonAPIUrl('kt/question/submit')).data(data).post();
+  }
+  /**预约赛事 */
+  static postSubscribe(data) {
+    return efunRequest.getHttpRequest().url(genLessonAPIUrl('kt/subscribe')).data(data).post();
+  }
+  /**总排名 */
+  static getRankingList(grade) {
+    return efunRequest.getHttpRequest().url(genLessonAPIUrl('kt/race/rankingList')).params({
+      grade,
+      pageSize: 99999
+    }).get();
+  }
+  /**赛事排名 */
+  static getRaceRanking(raceId, grade) {
+    return efunRequest.getHttpRequest().url(genLessonAPIUrl('kt/race/raceRanking')).params({
+      raceId,
+      grade,
+      pageSize: 99999
+    }).get();
+  }
+  /**新错题本 */
+  static getNewMistake(data) {
+    return efunRequest.getHttpRequest().params(data).url(genLessonAPIUrl('kt/question/mistakeBook')).get();
+  }
+  /**是否参赛 */
+  static getRaceJoin(raceId) {
+    return efunRequest.getHttpRequest().url(genLessonAPIUrl('kt/race/join')).params({
+      raceId
+    }).get();
+  }
+}

+ 1 - 0
src/stage/check.html

@@ -0,0 +1 @@
+ok

+ 280 - 0
src/stage/index/Untitled-1.json

@@ -0,0 +1,280 @@
+{
+    "request_id": "852ef5acb5e911eaae2bd31b6f895d02",
+    "applicationId": "a415",
+    "dtLastResponse": "2020-06-24 15:09:10:244",
+    "connect": {
+        "param": {
+            "app": {
+                "timestamp": "1592982515",
+                "userId": "123",
+                "sig": "fc6498657c088ea512f080ca8f2562f632d832a5",
+                "connect_id": "853e351cb5e911ea994097df0cb2b6b3",
+                "deviceId": "6c511e4b7a8e416a67a6581fdf8b11ce",
+                "applicationId": "a415"
+            },
+            "sdk": {
+                "os": "android",
+                "product": "fake",
+                "os_version": "0.0",
+                "source": 1,
+                "protocol": 1,
+                "type": 1,
+                "arch": "armv8l",
+                "version": 33556224
+            }
+        },
+        "cmd": "connect"
+    },
+    "params": {
+        "app": {
+            "timestamp": "1592982515",
+            "userId": "123",
+            "sig": "fc6498657c088ea512f080ca8f2562f632d832a5",
+            "connect_id": "853e351cb5e911ea994097df0cb2b6b3",
+            "deviceId": "6c511e4b7a8e416a67a6581fdf8b11ce",
+            "applicationId": "a415"
+        },
+        "audio": {
+            "saveAudio": 0,
+            "sampleBytes": 2,
+            "audioType": "opus",
+            "sampleRate": 16000,
+            "channel": 1
+        },
+        "request": {
+            "request_id": "852ef5acb5e911eaae2bd31b6f895d02",
+            "attachAudioUrl": 1,
+            "precision": 0.5,
+            "rateScale": 1,
+            "refText": "荷尽已无擎雨盖,\n        菊残犹有傲霜枝。\n        一年好景君须记,\n        正是橙黄橘绿时",
+            "coreType": "cn.pred.score",
+            "rank": 100
+        }
+    },
+    "recordId": "11eab5e989158b2eb4eea415d651c4da",
+    "refText": "荷尽已无擎雨盖,\n        菊残犹有傲霜枝。\n        一年好景君须记,\n        正是橙黄橘绿时",
+    "audioUrl": "http:\/\/files.cloud.ssapi.cn:8080\/a415\/11eab5e989158b2eb4eea415d651c4da",
+    "cloud_platform": {
+        "origin_audio_length": 74515
+    },
+    "result": {
+        "version": "0.0.80.2020.5.25.13:27:25",
+        "pron": 70.5,
+        "wavetime": 27700,
+        "accuracy": 70.5,
+        "pretime": 11,
+        "systime": 25566,
+        "res": "chn.pred.online.1.0",
+        "details": [{
+                    "text": "荷尽已无擎雨盖",
+                    "score": 47,
+                    "fluency": {
+                        "pause": 0,
+                        "overall": 72,
+                        "speed": 1
+                    },
+                    "snt_details": [{
+                        "char": "he",
+                        "tonescore": 90,
+                        "dur": 60,
+                        "end": 9790,
+                        "chn_char": "荷",
+                        "score": 0,
+                        "tone": 2,
+                        "start": 9730
+                    }, {
+                        "char": "jin",
+                        "tonescore": 96,
+                        "dur": 330,
+                        "end": 10120,
+                        "chn_char": "尽",
+                        "score": 81.5,
+                        "tone": 4,
+                        "start": 9790
+                    }, {
+                        "char": "yi",
+                        "tonescore": 26,
+                        "dur": 160,
+                        "end": 10280,
+                        "chn_char": "已",
+                        "score": 51,
+                        "tone": 3,
+                        "start": 10120
+                    }, {
+                        "char": "wu",
+                        "tonescore": 92,
+                        "dur": 180,
+                        "end": 10460,
+                        "chn_char": "无",
+                        "score": 0,
+                        "tone": 2,
+                        "start": 10280
+                    }, {
+                        "dp_type": 2,
+                        "tonescore": 99,
+                        "dur": 470,
+                        "chn_char": "擎",
+                        "start": 10630,
+                        "char": "qing",
+                        "score": 89.5,
+                        "tone": 2,
+                        "end": 11100
+                    }, {
+                        "char": "yu",
+                        "tonescore": 98,
+                        "dur": 170,
+                        "end": 11270,
+                        "chn_char": "雨",
+                        "score": 57.5,
+                        "tone": 3,
+                        "start": 11100
+                    }, {
+                        "char": "gai",
+                        "tonescore": 98,
+                        "dur": 400,
+                        "end": 11670,
+                        "chn_char": "盖",
+                        "score": 67,
+                        "tone": 4,
+                        "start": 11270
+                    }]
+                }, {
+                    "text": "菊残犹有傲霜枝",
+                    "score": 48,
+                    "fluency": {
+                        "pause": 0,
+                        "overall": 88,
+                        "speed": 0
+                    },
+                    "snt_details": [{
+                        "char": "ju",
+                        "tonescore": 89,
+                        "dur": 410,
+                        "end": 13760,
+                        "chn_char": "菊",
+                        "score": 79,
+                        "tone": 2,
+                        "start": 13350
+                    }, {
+                        "char": "can",
+                        "tonescore": 93,
+                        "dur": 400,
+                        "end": 14160,
+                        "chn_char": "残",
+                        "score": 39,
+                        "tone": 2,
+                        "start": 13760
+                    }, {
+                        "char": "you",
+                        "tonescore": 97,
+                        "dur": 260,
+                        "end": 14420,
+                        "chn_char": "犹",
+                        "score": 0,
+                        "tone": 2,
+                        "start": 14160
+                    }, {
+                        "char": "you",
+                        "tonescore": 99,
+                        "dur": 270,
+                        "end": 14690,
+                        "chn_char": "有",
+                        "score": 19.5,
+                        "tone": 3,
+                        "start": 14420
+                    }, {
+                        "char": "ao",
+                        "tonescore": 99,
+                        "dur": 360,
+                        "end": 15050,
+                        "chn_char": "傲",
+                        "score": 77,
+                        "tone": 4,
+                        "start": 14690
+                    }, {
+                        "char": "shuang",
+                        "tonescore": 100,
+                        "dur": 360,
+                        "end": 15410,
+                        "chn_char": "霜",
+                        "score": 58,
+                        "tone": 1,
+                        "start": 15050
+                    }, {
+                        "char": "zhi",
+                        "tonescore": 99,
+                        "dur": 550,
+                        "end": 15960,
+                        "chn_char": "枝",
+                        "score": 95,
+                        "tone": 1,
+                        "start": 15410
+                    }]
+                }, {
+                    "text": "一年好景君须记",
+                    "score": 92,
+                    "fluency": {
+                        "pause": 0,
+                        "overall": 82,
+                        "speed": 0
+                    },
+                    "snt_details": [{
+                                "char": "yi",
+                                "tonescore": 55,
+                                "dur": 210,
+                                "end": 18440,
+                                "chn_char": "一",
+                                "score": 79,
+                                "tone": 1,
+                                "start": 18230
+                            }, {
+                                "char": "nian",
+                                "tonescore": 100,
+                                "dur": 370,
+                                "end": 18810,
+                                "chn_char": "年",
+                                "score": 95,
+                                "tone": 2,
+                                "start": 18440
+                            }, {
+                                "char": "hao",
+                                "tonescore": 52,
+                                "dur": 290,
+                                "end": 19100,
+                                "chn_char": "好",
+                                "score": 78.5,
+                                "tone": 3,
+                                "start": 18810
+                            }, {
+                                "char": "jing",
+                                "tonescore": 100,
+                                "dur": 330,
+                                "end": 19430,
+                                "chn_char": "景",
+                                "score": 94.5,
+                                "tone": 3,
+                                "start": 19100
+                            }, {
+                                "char": "jun",
+                                "tonescore": 100,
+                                "dur": 350,
+                                "end": 19780,
+                                "chn_char": "君",
+                                "score": 93,
+                                "tone": 1,
+                                "start": 19430
+                            }, {
+                                "char": "xu",
+                                "tonescore": 97,
+                                "dur": 350,
+                                "end": 20130,
+                                "chn_char": "须",
+                                "score": 96,
+                                "tone": 1,
+                                "start": 19780
+                            }, {
+                                "char": "ji",
+                                "tonescore": 98,
+                                "dur": 460,
+                                "end": 20590,
+                                "chn_cha

BIN
src/stage/index/assets/favicon.ico


BIN
src/stage/index/assets/match/1.png


BIN
src/stage/index/assets/match/2.png


BIN
src/stage/index/assets/match/3.png


BIN
src/stage/index/assets/match/baogao_bg.png


BIN
src/stage/index/assets/match/conduct.png


BIN
src/stage/index/assets/match/daojishi.png


BIN
src/stage/index/assets/match/end_bg.png


BIN
src/stage/index/assets/match/error_icon.png


BIN
src/stage/index/assets/match/game_course_new.png


BIN
src/stage/index/assets/match/hand.png


BIN
src/stage/index/assets/match/huojian.png


BIN
src/stage/index/assets/match/lately.png


BIN
src/stage/index/assets/match/no_data.png


BIN
src/stage/index/assets/match/now.png


BIN
src/stage/index/assets/match/one.png


BIN
src/stage/index/assets/match/quit_bg.png


BIN
src/stage/index/assets/match/ranking_bg.jpg


BIN
src/stage/index/assets/match/ranking_icon.png


BIN
src/stage/index/assets/match/reader.png


BIN
src/stage/index/assets/match/reader_new.png


BIN
src/stage/index/assets/match/tab_left.png


BIN
src/stage/index/assets/match/tab_right.png


BIN
src/stage/index/assets/match/three.png


BIN
src/stage/index/assets/match/tomorrow.png


BIN
src/stage/index/assets/match/total_ranking_bg.png


BIN
src/stage/index/assets/match/two.png


BIN
src/stage/index/assets/mobile/correct.png


BIN
src/stage/index/assets/mobile/question_bg.png


BIN
src/stage/index/assets/mobile/school_bg.png


BIN
src/stage/index/assets/mobile/select_bg.png


BIN
src/stage/index/assets/mobile/test_bg.png


BIN
src/stage/index/assets/mobile/white_bg.png


BIN
src/stage/index/assets/mobile/wrong.png


BIN
src/stage/index/assets/pre/board.png


BIN
src/stage/index/assets/pre/no_ques.png


BIN
src/stage/index/assets/pre/wrong.png


+ 72 - 0
src/stage/index/index.html

@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<html id="rootHtml">
+
+<head>
+  <meta charset="UTF-8">
+  <meta blitz-debugger="localhost">
+  <meta name="viewport" content="width=1920,user-scalable=no,viewport-fit=cover">
+  <title>学有义方</title>
+  <link rel="shortcut icon" href="assets/favicon.ico">
+  <link rel="stylesheet" href="../../lib/css/base.css" />
+  <link rel="stylesheet" href="./index.css" />
+</head>
+<script>
+  (function (doc, win) {
+    var docEl = doc.documentElement,
+      resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
+      recalc = function () {
+        var screenDpi = window.devicePixelRatio;
+        var screenXDpi = window.screen.deviceXDPI;
+        var screenWidth = (window.screen.width) * screenDpi;
+        var screenHeight = window.screen.height * screenDpi;
+        var clientWidth = docEl.clientWidth;
+        var clientHeight = docEl.clientHeight;
+        if (!clientWidth) {
+          return;
+        }
+        var screenTemp = (screenWidth / screenHeight).toFixed(2);
+        var bodyStyle = window.getComputedStyle(document.body, null)
+        var scale = (bodyStyle['font-size']).replace('px', '') / 12;
+        scale = 1;
+        console.log(scale)
+        if (screenTemp <= 1.78) {
+          console.log('上下')
+          var screenTop = (clientHeight - 1080) / 200;
+          docEl.style.fontSize = 100 * (clientWidth / 1920) + 'px';
+        } else {
+          console.log('左右')
+          var fontSizeRadio = 100 * ((1920 / 1080) / (screenWidth / screenHeight)) / scale;
+          console.log(fontSizeRadio)
+          docEl.style.fontSize = fontSizeRadio + 'px';
+          var screenLeft = (docEl.offsetWidth / scale - document.body.offsetWidth / scale) / (2 * fontSizeRadio);
+        }
+      };
+    if (!doc.addEventListener) {
+      return;
+    }
+    doc.addEventListener('DOMContentLoaded', recalc, false) //绑定浏览器缩放与加载时间
+
+  })(document, window);
+
+</script>
+<div class="back" data-type="back" data-event="back" fe-role="Widget">
+    <img src="http://asxx-img.ai160.com/images/asxx/assets/img/back.png" alt="">
+</div>
+<div id="root">
+</div>
+<body onDragStart = "return false"onSelectStart = "return event.srcElement.tagName == 'INPUT'">
+<script src="../../lib/js/FocusEngine.pad.js"></script>
+<script src="../../lib/js/blitz.js"></script>
+<script src="../../lib/js/moye.mobile.js"></script>
+<script src="../../lib/js/event.js"></script>
+<script src="./index.js?t=[timestamp]"></script>
+<script src="../../lib/js/TVUtil.js"></script>
+<!--log-->
+
+<!-- <script src="../../lib/js/vconsole.min.js"></script>
+<script>
+    var vConsole = new VConsole();
+</script> -->
+
+</body>
+</html>

+ 18 - 0
src/stage/index/index.js

@@ -0,0 +1,18 @@
+import Utils from '../../util/utils'
+let option = {
+  // 此处请填上你的wpk包名
+  packagename: 'test.tv.yunos.com',
+  // 默认数据存放,以下为默认语言配置
+  default:{
+    lang:{
+      type:'zh',
+      data:require('../../res/values/value.json')
+    }
+  }
+};
+setTimeout(function(){
+  changeColor('#043923')
+}, 1000)
+let indexScene = require('./scene/match/adScene.js');
+moye.init(option);
+moye.showScene(indexScene);

+ 78 - 0
src/stage/index/index.less

@@ -0,0 +1,78 @@
+@charset 'utf-8';
+
+* {
+  font-family: 'Microsoft Yahei' !important;
+  -webkit-user-select: none;
+  -webkit-tap-highlight-color:transparent;
+  cursor: pointer;
+}
+
+html {
+  width: 100%;
+  height: 100%;
+}
+
+body {
+  width: 100%;
+  height: 100%;
+  box-sizing: border-box;
+  box-sizing: border-box;
+  overflow: hidden;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+img[src=""],img:not([src]){
+  visibility: hidden;
+  opacity:0;
+  border:none;
+}
+#root {
+  width: 19.2rem;
+  height: 10.8rem;
+  position: relative;
+  box-sizing: border-box;
+  box-sizing: border-box;
+  overflow: hidden;
+}
+
+.main {
+  width: 100%;
+  height: 100%;
+}
+
+.back {
+  width: 0.69rem;
+  height: 0.86rem;
+  position: absolute;
+  top: 0.72rem;
+  left: 1.2rem;
+  z-index: 999;
+
+  img {
+    width: 100%;
+    height: 100%;
+  }
+}
+
+.video-container {
+  display: none;
+  overflow: hidden;
+}
+
+
+@import '../../lib/css/base.css';
+
+//答题竞赛
+@import './style/match/ad.less';
+@import './style/match/match.less';
+@import './style/match/ranking.less';
+@import './style/match/answer.less';
+@import './style/match/errorbook.less';
+@import './style/match/matchDialog.less';
+@import './style/match/details.less';
+.center {
+  width: 19.2rem;
+  height: 10.8rem;
+  position: absolute;
+}

+ 157 - 0
src/stage/index/scene/match/adScene.js

@@ -0,0 +1,157 @@
+import {
+  writeLocalStorage,
+  setBack,
+  GetQueryString,
+  readLocalstorage,
+  $,
+  setBg
+} from '../../../../util/utils'
+import {
+  mobileLogin,
+  partnerLogin,
+  getAdImg
+} from '../../../../service/course'
+import {getPhoneLogin, huaweiLogin, phoneSuccess} from '../../../../util/login'
+class adScene extends scene {
+
+  constructor(scope) {
+    super(scope);
+    this.timer = 5;
+  }
+
+  onCreate(data) {
+    setBack(true)
+    this.setContentView(require('../../../../res/tpl/match/ad.tpl'), {}, 'ad', {
+      // containerId: 'root'
+    }, () => {
+      // 兼容网页端可预览
+      // if (sessionStorage.getItem("firstOpen") === 'true') {
+      if (sessionStorage.getItem("firstOpen") === 'true') {
+        this.enterMain();
+      } else {
+        this.countTime()
+        this.adImg(readLocalstorage("vip"))
+      }
+    });
+  }
+
+  onOK(e) {
+    const flag = e.target.con.attributes['data-type'].nodeValue;
+    if (flag === 'jump') {
+      this.enterMain();
+    }
+
+  }
+  countTime() {
+    $('.ad_count_time').innerHTML = '0' + this.timer;
+    this.countTimeTimer = setTimeout(() => {
+      this.timer--;
+      if (this.timer < 0) return;
+      this.countTime();
+    }, 1000)
+  }
+
+  enterMain() {
+    console.log('enterMain()');
+    clearTimeout(this.countTimeTimer)
+    clearTimeout(this.AdDelay)
+    sessionStorage.setItem("firstOpen", "true");
+    if (GetQueryString('userPhone')) {
+      const mobile = GetQueryString('userPhone')
+      this.renderLogin(mobile)
+      return false;
+    }
+    if ((~~GetQueryString('version') > 10010) && !readLocalstorage('mobile')) {
+      writeLocalStorage('uid', '123456');
+      writeLocalStorage('nickName', '');
+      // this.showScene(require('./ChineseMathScene.js'), {})
+      return false;
+    }
+    if (readLocalstorage('mobile') || readLocalstorage('uid')) {
+      // this.showScene(require('./ChineseMathScene.js'), {})
+    }else {
+      showPhoneNumberLogin()
+    }
+  }
+
+  onBack(e) {
+    try {
+      if (typeof efunboxJS != "undefined") {
+        efunboxJS.closeApp();
+      }
+    } catch (err) {
+      console.log('没有安卓方法产生的', err)
+    }
+    return true;
+  }
+  closeLogin(code) {
+    if (GetQueryString('appCode') === '2001') {
+      if (code * 1 === 700000) {
+        if (typeof efunboxJS != "undefined") {
+          efunboxJS.closeApp();
+        }
+        return false;
+      }
+      $('.log').style = 'display: block;font-size: 1rem;color: red;margin: 2rem 3rem';
+      $('.log').innerHTML = code
+    } else {
+      if (typeof efunboxJS != "undefined") {
+        efunboxJS.closeApp();
+      }
+    }
+  }
+  adImg(state) {
+      console.log('state:' + state);
+      state = state || '0'
+      const appCode = GetQueryString('appCode')
+      console.log('appCode:' + appCode);
+      getAdImg(appCode).success(res => {
+        if (res.data.vipStartImg && res.data.startImg) {
+            // 调用接口查看广告图片
+            $('#ad_img').src = state * 1 ? res.data.vipStartImg : res.data.startImg;
+            const color = state * 1 ? res.data.vipStartColor : res.data.startColor
+            setBg(color);
+            this.AdDelay = setTimeout(() => {
+              this.enterMain();
+            }, 5000)
+        }else {
+          this.enterMain();
+        }
+      })
+      if (appCode == 2010 || appCode == 2009 || appCode == 2012 || appCode == 3003 || appCode == 2013 || appCode == 2014) {
+        $('.ad_img_btn').style = 'right: .3rem; transform: rotate(0);'
+      } else {
+        $('.ad_img_btn').style = 'left: .3rem';
+      }
+      $('.ad_img_box').style.display = 'block';
+  }
+  renderLogin(mobileNo) {
+    const deviceCode = GetQueryString('uuid')
+    const channel = GetQueryString('appCode')
+    // const self = this;
+    mobileLogin({
+      mobileNo,
+      deviceCode,
+      channel
+    }).success(res => {
+      console.log(res)
+      writeLocalStorage('uid', res.data.uid);
+      writeLocalStorage('channelCode', res.data.channelCode);
+      writeLocalStorage('nickName', res.data.nickName);
+      writeLocalStorage('mobile', res.data.mobile);
+      writeLocalStorage('userCreateTime', res.data.gmtCreated);
+      console.log(123123, res.data.grade)
+      if (res.data.grade) {
+        writeLocalStorage('class', res.data.grade)
+      } else {
+        writeLocalStorage('class', '')
+      }
+      this.showScene(require('./matchScene'), {
+        type: 'whole'
+      });
+    }).fail(error => {
+      console.log(error)
+    })
+  }
+}
+module.exports = adScene;

+ 385 - 0
src/stage/index/scene/match/answerScene.js

@@ -0,0 +1,385 @@
+import { getRaceInfo, postQuestion, postQuestionSubmit } from '../../../../service/match';
+
+import Utils from '../../../../util/utils'
+class answerScene extends scene {
+    constructor(scope) {
+        super(scope);
+        this.quesIndex = 0;
+        this.quesLength = 0;
+        this.questionList = [];
+        this.requestId = undefined;
+        this.wareId = undefined;
+        this.target = undefined;
+        this.hasPostAnswer = false;
+        this.startTime = new Date(); // 页面开始时间
+
+    }
+
+    onCreate(data) {
+        Utils.setBack(false, '.42')
+        Utils.setBg('#3156AB')
+        // 保存单次请求Id
+        this.requestId = data.requestId;
+        this.wareId = data.wareId;
+        this.raceId = data.raceId;
+        this.setContentView(require('../../../../res/tpl/match/answer.tpl'), {}, 'answer', {
+            containerId: 'root'
+        }, () => {
+            // 渲染答题
+            this.getRaceInfoData(this.raceId)
+        });
+    }
+
+    getRaceInfoData(id) {
+        getRaceInfo(id).success(res => {
+            this.questionList = res.data.questions;
+            this.quesLength = this.questionList.length
+            this.raceId = res.data.race.id;
+            this.renderNumber(this.quesLength);
+            this.renderQues(this.quesIndex)
+            Utils.$('.anser_title').innerHTML = res.data.race.title;
+            const time = res.data.race.duration * 60
+            this.countdown(time)
+        })
+    }
+    onResume() {
+        Utils.setBack(false, '.42')
+        Utils.setBg('#3156AB')
+    }
+    onOK(e) {
+        const flag = e.target.con.attributes['data-event'].nodeValue;
+        switch (flag) {
+            case 'cancle':
+                clickMp3();
+                this.hideScene()
+                break;
+            case 'answer':
+                if (this.answerOnceOnly) break;
+                this.answerOnceOnly = true;
+                this.chooseAnswer(e.target.con.dataset.answer, e.target.con.dataset.index);
+                break;
+            case 'prev':
+                // 上一题
+                clickMp3();
+                const prevInd = this.quesIndex - 1
+                if ( prevInd < 0) return
+                console.log(this.quesIndex - 9)
+                if (this.quesIndex - 9 < this.quesLength && this.quesIndex >= 9) {
+                    document.querySelector('.number_menu').scrollTo((this.quesIndex - 9) * 120 ,0)
+                }
+                this.quesIndex--;
+                this.renderQues(this.quesIndex)
+
+                break;
+            case 'next':
+                // 下一题
+                clickMp3();
+
+                const index = this.quesIndex + 1
+                if ( index === this.quesLength) {
+                    // 提交回答
+                    clearTimeout(this.countDownTime);
+                    this.submitRecord().then(res => {
+                        console.log(res);
+                        this.showScene(require('./matchDialogScene.js'), {
+                            bg: 'assets/match/baogao_bg.png',
+                            type: 'report',
+                            reportCon: res,
+                            raceId: this.raceId
+                        })
+                    })
+                    return
+                }
+                if (index >= 9) {
+                    document.querySelector('.number_menu').scrollTo((index - 9) * 120,0)
+                }
+                this.quesIndex++;
+                this.renderQues(this.quesIndex)
+                break;
+            case 'number':
+                const quesIndex = e.target.con.attributes['data-index'].nodeValue
+                this.quesIndex = parseInt(quesIndex);
+                console.log(111111111, this.quesIndex)
+                this.renderQues(this.quesIndex)
+                break;
+            case 'back':
+                clickMp3();
+                this.showScene(require('./matchDialogScene.js'), {
+                    bg: 'assets/match/quit_bg.png',
+                    type: 'quit',
+                    raceId: this.raceId,
+                    reportCon: {}
+                })
+                break;
+            // 交卷
+            case 'submitAnswer':
+                clearTimeout(this.countDownTime);
+                // 提交回答
+                this.submitRecord().then(res => {
+                    console.log(res);
+                    this.showScene(require('./matchDialogScene.js'), {
+                        bg: 'assets/match/baogao_bg.png',
+                        type: 'report',
+                        reportCon: res,
+                        raceId: this.raceId
+                    })
+                })
+                break;
+        }
+    }
+
+    onBack(e) {
+        Utils.setBack(true)
+        clearTimeout(this.prevTime)
+        clearTimeout(this.nextTime)
+        clearTimeout(this.countDownTime);
+    }
+    renderNumber(length) {
+        for (let i = 0; i < length; i++) {
+            let number = document.createElement('div');
+            number.className = 'number'
+            number.setAttribute('fe-role', 'Widget')
+            number.setAttribute('data-event', 'number')
+            number.setAttribute('data-index', i)
+            let numberText = document.createElement('span');
+            numberText.innerHTML = i + 1;
+            number.appendChild(numberText)
+            document.getElementById('number_menu_wrapper').appendChild(number);
+        }
+        this.moye.root.reRender()
+    }
+    wrapDiv(str) {
+        let list = str.split('<br/>');
+        if (list.length > 1) {
+            return `
+                <div>${list[0]}</div>
+                <div>${list[1]}</div>
+            `
+        } else {
+            return str
+        }
+    }
+    renderQues(index) {
+
+        clearTimeout(this.changingAnswer)
+        this.analysisShow = false
+        document.getElementById('analysis').style.display = 'none';
+        document.getElementById('analysis_btn').style.display = 'none';
+
+        document.getElementById('text_area').style.display = 'block'
+        document.getElementById('answer_area').style.display = 'flex'
+        this.hideAnswerIcon();
+        this.renderCurrentNumber();
+        let obj = this.questionList[index];
+        // 最后一题
+        if (index + 1 === this.quesLength) {
+            document.getElementById('next_final').innerHTML = '提交'
+        } else {
+            document.getElementById('next_final').innerHTML = '下一题'
+        }
+        this.target = obj.target;
+        document.getElementById('text_area').innerHTML = `
+        <span class="ques_score">(${obj.score}分)</span>
+        <img src=${obj.image} class="ques_big_img" />
+        `
+         // 删除已选答案
+        this.removeClass();
+        Array.from(document.querySelectorAll('.answer_item')).forEach((item, index) => {
+            item.style = 'width: 3.7rem; height: 1rem;'
+            item.querySelector('.answer_icon').style = 'display: none;'
+            item.querySelector('.answer_text').style = 'display: none;'
+            item.querySelector('.right_wrong').style = 'top: -.2rem;width: 3.62rem;height: 1.42rem;left: -.5rem'
+        })
+        Utils.$('.text_area').style = 'height: 7.1rem; transform: scale(1);top: 0;'
+        Utils.$('.answer_area').style = 'display: flex;width: 70%;margin-top: -1.8rem; position: absolute;left: 3.4rem;'
+        this.analysisA = obj.analysisA;
+        this.analysisB = obj.analysisB;
+        this.analysisC = obj.analysisC;
+        // 没有答过的题要刷新状态
+        if (obj.answer === 'A') {
+            document.getElementById('right_wrong_a').src = 'assets/match/hand.png';
+        } else if (obj.answer === 'B') {
+            document.getElementById('right_wrong_b').src = 'assets/match/hand.png';
+        } else {
+            document.getElementById('right_wrong_c').src = 'assets/match/hand.png';
+        }
+
+
+        if (obj.isAnswered) {
+            this.answerOnceOnly = true;
+            Array.from(document.getElementsByClassName('right_wrong')).forEach(item => {
+                item.style.display = 'none'
+            })
+
+            if (obj.userOption === 'A') {
+                document.getElementById('answer_a_icon').className += ' choose';
+                document.getElementsByClassName('right_wrong')[0].style.display = 'block'
+            } else if (obj.userOption === 'B') {
+                document.getElementById('answer_b_icon').className += ' choose';
+                document.getElementsByClassName('right_wrong')[1].style.display = 'block'
+            } else {
+                document.getElementById('answer_c_icon').className += ' choose';
+                document.getElementsByClassName('right_wrong')[2].style.display = 'block'
+            }
+        } else {
+            // 答案弹出后只允许点击一次
+            this.answerOnceOnly = false;
+        }
+
+        document.getElementById('lesson_main').style.display = 'block'
+
+        // 让答案失去焦点 为了闪烁
+        this.moye.root.getWidgetById('answer_a').blur();
+        this.moye.root.getWidgetById('answer_b').blur();
+        this.moye.root.getWidgetById('answer_c').blur();
+
+    }
+    // 给当前的题目序号标上边框
+    renderCurrentNumber() {
+        let list = Array.from(document.getElementsByClassName('number'));
+        let reg = /current/g;
+        let prevQuesIndex = 0;
+        list.forEach((item, index) => {
+            if (reg.test(item.className)) {
+                prevQuesIndex = index
+            }
+        })
+        list.forEach((item, index) => {
+            if (index === this.quesIndex) {
+                item.className = 'number current';
+                this.moye.root.getWidgetById(item.id).focus()
+            } else {
+                item.className = item.className.replace('current', '')
+            }
+
+        })
+    }
+    // 修改样式
+    removeClass() {
+        document.getElementsByClassName('choose')[0] ? document.getElementsByClassName('choose')[0].classList.remove('choose') : '';
+    }
+
+    onTouch(e) {
+        console.log("touch")
+    }
+    // 记题板
+    // 记录这道题是否答过,答对答错
+    // type:是否作答
+    // answerType: 答对答错
+    // userOption: 用户作答选项
+    recordHandler(type, answerType, userOption, questionId) {
+        let currentQues = this.questionList[this.quesIndex]
+        currentQues.isAnswered = type;
+        currentQues.isCorrect = answerType;
+        currentQues.userOption = userOption;
+        let postData = {
+            questionId,
+            "questionAnswer": userOption,
+        }
+        postQuestion(postData).success(res => {
+            console.log('发送答案', res)
+            this.hasPostAnswer = true;
+        })
+    }
+    chooseAnswer(option, index) {
+        option = option ? option.toUpperCase() : undefined;
+        // 语音时也要闪烁
+        switch (option) {
+            case 'A':
+                this.moye.root.getWidgetById('answer_a').focus();
+                document.getElementById('answer_a_icon').className += ' choose';
+                break;
+            case 'B':
+                this.moye.root.getWidgetById('answer_b').focus();
+                document.getElementById('answer_b_icon').className += ' choose';
+
+                break;
+            case 'C':
+                this.moye.root.getWidgetById('answer_c').focus();
+                document.getElementById('answer_c_icon').className += ' choose';
+                break;
+            default:
+                break;
+
+        }
+        if (option) {
+            this.changingAnswer = setTimeout(() => {
+                let answerList = Array.from(document.getElementsByClassName('right_wrong'));
+                answerList.forEach((item) => {
+                    item.style.display = 'none'
+                });
+                document.getElementsByClassName('right_wrong')[index].style.display = 'block'
+            }, 600)
+        }
+        let correctAnswer = this.questionList[this.quesIndex].answer;
+        const questionId = this.questionList[this.quesIndex].id;
+        if (option === correctAnswer) {
+            // 答对逻辑
+            // rightMp3();
+            // 记录答对答错
+            this.recordHandler(true, true, option, questionId)
+        } else {
+            // 答错逻辑
+            // wrongMp3();
+            this.recordHandler(true, false, option, questionId)
+        }
+    }
+
+    // 提交本次记录
+    submitRecord() {
+        return new Promise((resolve, reject) => {
+            postQuestionSubmit({
+                raceId: this.raceId
+            }).success(res => {
+                resolve(res.data)
+                // this.hideScene();
+            })
+        })
+
+    }
+    // 隐藏答案图片
+    hideAnswerIcon() {
+        let list = Array.from(document.getElementsByClassName('right_wrong'));
+        list.forEach(item => {
+            item.src = 'assets/match/hand.png'
+            item.style.display = 'none'
+
+        })
+    }
+
+    aiHandle(name, slots) {
+
+    }
+  /**倒计时 */
+  countdown(time) {
+    if (time > 0) {
+        this.countDownTime = setTimeout(() => {
+            time--;
+            // console.log('分', Math.floor(time / 60), '秒', time % 60)
+            const h = Math.floor(time / 3600 % 24) < 10 ? '0' + Math.floor(time / 3600 % 24) : Math.floor(time / 3600 % 24)
+            const m = Math.floor(time / 60 % 60) < 10 ? '0' + Math.floor(time / 60 % 60) : Math.floor(time / 60 % 60);
+            const s = (time % 60) < 10 ?  '0' +  (time % 60) :  time % 60;
+            if (h * 1 === 0) {
+                Utils.$('.answer_time span').innerHTML = m + ':' + s
+            } else {
+                Utils.$('.answer_time span').innerHTML = h + ':' + m + ':' + s
+            }
+            this.countdown(time)
+        }, 1000)
+    } else {
+        console.log('竞赛结束')
+        // 提交回答
+        this.submitRecord().then(res => {
+            console.log(res);
+            this.showScene(require('./matchDialogScene.js'), {
+                bg: 'assets/match/end_bg.png',
+                type: 'end',
+                reportCon: res,
+                raceId: this.raceId
+            })
+        })
+    }
+  }
+}
+
+module.exports = answerScene;

+ 53 - 0
src/stage/index/scene/match/detailsScene.js

@@ -0,0 +1,53 @@
+// import {getRankingList, getRaceRanking } from '../../../../service/match';
+import { getRaceInfo } from '../../../../service/match';
+import {
+  $,
+  setBg,
+  setBack
+} from '../../../../util/utils'
+class detailsScene extends scene {
+    constructor(scope) {
+        super(scope);
+
+    }
+
+    onCreate(data) {
+        const raceId = data.raceId;
+        this.getInfoData(raceId).then(res => {
+            this.setContentView(require('../../../../res/tpl/match/details.juicer'), {
+                data: res
+            }, 'details', {
+              containerId: 'root'
+            }, () => {
+              setBack(false, '.32');
+              setBg('#000')
+            });
+        })
+
+    }
+    onOK(e) {
+        console.log(e)
+        const flag = e.target.con.attributes['data-event'].nodeValue;
+        switch (flag) {
+            case 'back':
+                this.hideScene({}, 'match')
+                break;
+
+        }
+    }
+    getInfoData(raceId) {
+        return new Promise((resolve, reject) => {
+          getRaceInfo(raceId).success(res => {
+            const data = res.data.race.description && res.data.race.description.split(',');
+            resolve(data)
+          })
+        })
+
+    }
+
+    onBack(e) {
+
+    }
+}
+
+module.exports = detailsScene;

+ 591 - 0
src/stage/index/scene/match/errorbookScene.js

@@ -0,0 +1,591 @@
+import { getNewMistake, postQuestion } from '../../../../service/match'
+
+import Utils from '../../../../util/utils'
+import { isLogin } from '../../../../util/login'
+
+class errorScene extends scene {
+    constructor(scope) {
+        super(scope);
+        this.quesIndex = 0;
+        this.quesLength = 0;
+        this.questionList = [];
+        this.requestId = undefined;
+        this.courseId = undefined;
+        this.lessonId = undefined;
+        this.wareId = undefined;
+        this.target = undefined;
+        this.category = 'CHINESE';
+        this.pageNum = 1;
+        this.pageSize = 5;
+        this.hasNext = false; // 是否还有下一页
+        this.startTime = new Date(); // 页面开始时间
+
+    }
+
+    onCreate(data) {
+        Utils.setBg('#8E53D9')
+        Utils.setBack(false, '.52')
+        this.setContentView(require('../../../../res/tpl/match/errorbook.tpl'), {}, 'errorbook', {
+            containerId: 'root'
+        }, () => {
+            // 获取数据
+            if (data && data.type) {
+                this.type = data.type;
+                this.changeCategory(this.type)
+            }
+        });
+    }
+
+    changeCategory(type) {
+        document.getElementById('scroll_analysis').style.display = 'none';
+        this.type = type;
+        this.getMistakeList(true)
+    }
+
+    getMistakeList(ifChange) {
+        getNewMistake({
+            grade: Utils.readLocalstorage('class'),
+            pageNum: this.pageNum,
+            pageSize: this.pageSize
+            }).success(res => {
+            this.getData(res, ifChange)
+        })
+    }
+    getData(res, ifChange) {
+        if (this.hasNext && !ifChange) {
+            console.log('加载下一页')
+            res.data.list.forEach(item => {
+                this.questionList.push(item)
+            })
+            this.quesLength = this.questionList.length;
+            this.hasNext = res.data.hasNext;
+        } else {
+            this.quesLength = res.data.list.length;
+            if (res.data.list.length === 0) {
+                // 没有错题,显示图片
+                this.showImg(true)
+                this.questionList = [];
+            } else {
+                this.showImg(false)
+                this.questionList = res.data.list;
+                this.quesIndex = 0;
+                this.renderQues(this.quesIndex);
+                if (res.data.hasNext) {
+                    this.hasNext = true
+                }
+            }
+        }
+    }
+    showImg(isShow) {
+        // return
+        if (isShow) {
+            document.getElementById('no_ques').style.display = 'block';
+            document.getElementById('text_area').style.display = 'none';
+            document.getElementById('answer_area').style.display = 'none';
+            document.getElementById('analysis').style.display = 'none';
+        } else {
+            document.getElementById('no_ques').style.display = 'none';
+            document.getElementById('text_area').style.display = 'block';
+            document.getElementById('answer_area').style.display = 'flex';
+        }
+
+    }
+    onResume() {
+        Utils.setBack(false, '.52')
+        Utils.setBg('#8E53D9')
+    }
+    onOK(e) {
+        const flag = e.target.con.attributes['data-event'].nodeValue;
+        switch (flag) {
+            case 'cancle':
+                clickMp3();
+                this.hideScene()
+                break;
+            case 'answer':
+                // if (this.answerOnceOnly) break;
+                // this.answerOnceOnly = true;
+                const questionListIndex = this.questionList[this.quesIndex];
+                if (questionListIndex.isAnswered || (questionListIndex.question && questionListIndex.question.isAnswered)) break;
+                this.chooseAnswer(e.target.con.dataset.answer, e.target.con.dataset.index);
+                break;
+            case 'prev':
+                // 上一题
+                clickMp3();
+                this.touchBtn('prev')
+                if (this.quesIndex - 1 < 0) {
+                    this.showTip('已经到头啦!')
+                    return;
+                }
+                this.quesIndex--;
+                this.renderQues(this.quesIndex)
+
+                break;
+            case 'next':
+                // 下一题
+                clickMp3();
+                this.touchBtn('next')
+                if (this.quesIndex + 2 === this.quesLength) {
+                    if (this.hasNext) {
+                        // 还有下一页
+                        this.pageNum++;
+                        this.getMistakeList()
+                        // return
+                    }
+                } else if (this.quesIndex + 1 === this.quesLength) {
+                    console.log('没有更多了!!!')
+                    // 提交回答
+                    this.submitRecord()
+                    this.pageNum = 1;
+                    this.showTip('已经到最后,没有更多啦!')
+                    return
+                }
+                this.quesIndex++;
+                this.renderQues(this.quesIndex)
+                break;
+            case 'analysis':
+                clickMp3();
+                if (this.analysisShow) {
+                    this.renderQues(this.quesIndex)
+                } else {
+                    this.showAnalysis();
+                }
+                break;
+            case 'back':
+                clickMp3();
+                this.hideScene();
+                break;
+        }
+    }
+
+    onBack(e) {
+        Utils.setBack(true)
+        clearTimeout(this.prevTime)
+        clearTimeout(this.nextTime)
+        clearTimeout(this.tipTime)
+        let hideTime = new Date() - this.startTime;
+    }
+
+    wrapDiv(str) {
+        let list = str.split('<br/>');
+        if (list.length > 1) {
+            return `
+                <div>${list[0]}</div>
+                <div>${list[1]}</div>
+            `
+        } else {
+            return str
+        }
+    }
+
+    renderQues(index) {
+        console.log('加载题目')
+        clearTimeout(this.changingAnswer)
+
+        this.analysisShow = false
+        let obj = this.questionList[index];
+        // 兼容知识竞赛错题本返回数据不同
+        obj = obj.answer ? obj : obj.question;
+        if (obj === undefined) return;
+        this.target = obj.target;
+        this.questionId = obj.id;
+        document.getElementById('analysis').style.display = 'none';
+        document.getElementById('text_area').style.display = 'block'
+        this.hideAnswerIcon();
+
+        if (obj.type === 'PICTURE') {
+            document.getElementById('text_area').innerHTML = `
+              <img src=${obj.image} class="ques_img" />
+            `
+        } else if(obj.type === 'BIG_PICTURE' || this.type === '2') {
+          document.getElementById('text_area').innerHTML = `
+            <img src=${obj.image} class="ques_big_img" />
+          `
+        } else {
+            document.getElementById('text_area').innerHTML = obj.content;
+        }
+        // 删除已选答案
+        this.removeClass();
+        if(obj.type === 'BIG_PICTURE' || this.type === '2') {
+          Array.from(document.querySelectorAll('.answer_item')).forEach((item, index) => {
+            item.style = 'width: 3.7rem; height: 1rem;'
+            item.querySelector('.answer_icon').style = 'display: none;'
+            item.querySelector('.answer_text').style = 'display: none;'
+            item.querySelector('.right_wrong').style = 'top: .1rem;left: 1.2rem;'
+            if (index == 1) {
+              item.querySelector('.right_wrong').style.left = '1.1rem'
+            }
+            if (index == 2) {
+              item.querySelector('.right_wrong').style.left = '1.6rem'
+            }
+          })
+          Utils.$('.text_area').style.height = '8rem'
+          Utils.$('.answer_area').style = 'display: flex;width: 70%;margin: -1.8rem auto;'
+
+        } else {
+          this.answerA = obj.options.A;
+          this.answerB = obj.options.B;
+          this.answerC = obj.options.C;
+          Array.from(document.querySelectorAll('.answer_item')).forEach(item => {
+            item.style = '';
+            item.querySelector('.answer_icon').style = '';
+            item.querySelector('.right_wrong').style = '';
+            item.querySelector('.answer_text').style = ''
+          })
+          Utils.$('.text_area').style.height = '5.8rem'
+          Utils.$('.answer_area').style = 'display: flex;';
+          document.getElementById('answer_a_text').innerHTML = this.wrapDiv(this.answerA);
+          document.getElementById('answer_b_text').innerHTML = this.wrapDiv(this.answerB);
+          document.getElementById('answer_c_text').innerHTML = this.wrapDiv(this.answerC);
+        }
+        this.analysisA = obj.analysisA;
+        this.analysisB = obj.analysisB;
+        this.analysisC = obj.analysisC;
+        // 没有答过的题要刷新状态
+        if (obj.answer === 'A') {
+            document.getElementById('right_wrong_a').src = 'assets/mobile/correct.png';
+        } else if (obj.answer === 'B') {
+            document.getElementById('right_wrong_b').src = 'assets/mobile/correct.png';
+        } else {
+            document.getElementById('right_wrong_c').src = 'assets/mobile/correct.png';
+        }
+
+        let scrollImg = document.getElementById('scroll_analysis')
+
+        if (obj.isAnswered) {
+            // this.answerOnceOnly = true;
+            Array.from(document.getElementsByClassName('right_wrong')).forEach(item => {
+                item.style.display = 'none'
+            })
+            if (obj.userOption === 'A') {
+                document.getElementById('answer_a_icon').className += ' choose';
+                document.getElementsByClassName('right_wrong')[0].style.display = 'block'
+            } else if (obj.userOption === 'B') {
+                document.getElementById('answer_b_icon').className += ' choose';
+                document.getElementsByClassName('right_wrong')[1].style.display = 'block'
+            } else {
+                document.getElementById('answer_c_icon').className += ' choose';
+                document.getElementsByClassName('right_wrong')[2].style.display = 'block'
+            }
+
+            scrollImg.style.display = 'block'
+
+            // 答过以后不允许再闪烁
+            this.moye.root.getWidgetById('answer_a').disable();
+            this.moye.root.getWidgetById('answer_b').disable();
+            this.moye.root.getWidgetById('answer_c').disable();
+        } else {
+            // 答案弹出后只允许点击一次
+            // this.answerOnceOnly = false;
+            this.moye.root.getWidgetById('answer_a').enable();
+            this.moye.root.getWidgetById('answer_b').enable();
+            this.moye.root.getWidgetById('answer_c').enable();
+            scrollImg.style.display = 'none'
+        }
+        scrollImg.style.left = null;
+        scrollImg.style.right = '.5rem';
+        scrollImg.style.background = "url('http://asxx-img.ai160.com/images/asxx/assets/pre/scroll_ana.gif') 0 0 / 100% 100% no-repeat";
+
+        document.getElementById('lesson_main').style.display = 'block'
+        // 让答案失去焦点 为了闪烁
+        this.moye.root.getWidgetById('answer_a').blur();
+        this.moye.root.getWidgetById('answer_b').blur();
+        this.moye.root.getWidgetById('answer_c').blur();
+
+    }
+
+    // 修改样式
+    removeClass() {
+        document.getElementsByClassName('choose')[0] ? document.getElementsByClassName('choose')[0].classList.remove('choose') : '';
+    }
+
+    onTouch(e) {}
+    onTouchStart(e) {
+        this.touchXStart = e.changedTouches[0].clientX
+        this.touchYStart = e.changedTouches[0].clientY
+    }
+    onTouchEnd(e) {
+        // 当前滑动手势,在黑板区域时,才会切换页面
+        // 遍历e的路径,如果在黑板区域时就跳出遍历
+        // 如果遍历不到就结束
+        let myContinue = false;
+        try {
+            e.path.forEach(item => {
+                if (item.className === 'lesson_main') {
+                    throw new Error('go')
+                } else {
+                    return
+                }
+            })
+        } catch (ee) {
+            if (ee.message === 'go') {
+                myContinue = true;
+            }
+        }
+        if (!myContinue) return;
+        // if (e.target.className !== 'text_area' && e.target.className !== 'ques_img' && e.target.className !== 'lesson_main' && e.target.className !== 'analysis_main') return;
+
+        this.touchXEnd = e.changedTouches[0].clientX
+        this.touchYEnd = e.changedTouches[0].clientY
+        let xDistance = this.touchXEnd - this.touchXStart;
+        let yDistance = this.touchYEnd - this.touchYStart;
+        let XorY = Math.abs(xDistance) - Math.abs(yDistance);
+        if (XorY > 0) {
+            // 横轴移动
+            let leftOrRight = this.touchXEnd - this.touchXStart;
+            if (Math.abs(leftOrRight) < 200) return;
+            if (leftOrRight < 0) {
+                // 向右滑屏 切换解析
+                this.showAnalysis();
+            } else {
+                // 向左滚屏 切换题目
+                this.renderQues(this.quesIndex);
+
+            }
+        } else {
+            // 竖轴移动
+            let upOrDown = this.touchXEnd - this.touchXStart;
+
+            if (Math.abs(upOrDown) < 200) return;
+            if (upOrDown < 0) {
+                // 向上滑屏
+                console.log('向上滚屏')
+
+            } else {
+                // 向下滚屏
+                console.log('向下')
+
+            }
+        }
+    }
+    onTouchMove(e) {
+        console.log("onTouchMove", e.changedTouches[0].clientX)
+    }
+
+    // 记题板
+    // 记录这道题是否答过,答对答错
+    // type:是否作答
+    // answerType: 答对答错
+    // userOption: 用户作答选项
+    recordHandler(type, answerType, userOption) {
+        let currentQues = this.questionList[this.quesIndex]
+        if(this.type === '2') {
+            currentQues.question.isAnswered = type;
+            currentQues.question.isCorrect = answerType;
+            currentQues.question.userOption = userOption;
+            let postData = {
+                questionId: this.questionId,
+                "questionAnswer": userOption,
+            }
+            postQuestion(postData).success(res => {
+                console.log('发送答案', res)
+                this.hasPostAnswer = true;
+            })
+        } else {
+            currentQues.isAnswered = type;
+            currentQues.isCorrect = answerType;
+            currentQues.userOption = userOption;
+            let postData = {
+                "target": this.target,
+                "answer": userOption,
+                "lessonId": currentQues.lessonId,
+            }
+            // LessonApi.postMistakeAnswer(postData).success(res => {
+            //     console.log('发送答案', res)
+            // })
+        }
+    }
+    chooseAnswer(option, index) {
+        option = option ? option.toUpperCase() : undefined;
+        // 语音时也要闪烁
+        switch (option) {
+            case 'A':
+                this.moye.root.getWidgetById('answer_a').focus();
+                document.getElementById('answer_a_icon').className += ' choose';
+                break;
+            case 'B':
+                this.moye.root.getWidgetById('answer_b').focus();
+                document.getElementById('answer_b_icon').className += ' choose';
+
+                break;
+            case 'C':
+                this.moye.root.getWidgetById('answer_c').focus();
+                document.getElementById('answer_c_icon').className += ' choose';
+                break;
+            default:
+                break;
+
+        }
+        if (option) {
+            this.changingAnswer = setTimeout(() => {
+                let answerList = Array.from(document.getElementsByClassName('right_wrong'));
+                answerList.forEach((item) => {
+                    item.style.display = 'none'
+                });
+                document.getElementsByClassName('right_wrong')[index].style.display = 'block'
+            }, 600)
+        }
+        let correctAnswer = this.questionList[this.quesIndex].answer || this.questionList[this.quesIndex].question.answer;
+        if (option === correctAnswer) {
+            // 答对逻辑
+            // 记录答对答错
+            rightMp3()
+            this.recordHandler(true, true, option)
+        } else {
+            // 答错逻辑
+            wrongMp3()
+            this.recordHandler(true, false, option)
+        }
+        document.getElementById('scroll_analysis').style.display = 'block'
+    }
+
+
+    // 提交本次记录
+    submitRecord() {
+        // LessonApi.reviewSubmit({
+        //     wareId: this.wareId,
+        //     requestId: this.requestId
+        // }).success(res => {
+        //     console.log('提交', res)
+        // })
+    }
+    // 隐藏答案图片
+    hideAnswerIcon() {
+        let list = Array.from(document.getElementsByClassName('right_wrong'));
+        list.forEach(item => {
+            item.src = 'assets/mobile/wrong.png'
+            item.style.display = 'none'
+
+        })
+    }
+
+    // 切换解析
+    showAnalysis() {
+
+        let obj = this.questionList[this.quesIndex]
+        // 兼容知识竞赛错题本返回数据不同
+        obj = obj.answer ? obj : obj.question;
+        if (!obj.isAnswered) return
+        if (this.analysisShow) return;
+        this.analysisShow = true
+
+        let scrollImg = document.getElementById('scroll_analysis')
+        scrollImg.style.left = '.2rem';
+        scrollImg.style.background = "url('http://asxx-img.ai160.com/images/asxx/assets/pre/scroll_que.gif') 0 0 / 100% 100% no-repeat";
+
+        document.getElementById('text_area').style.display = 'none'
+        document.getElementById('answer_area').style.display = 'none'
+        document.getElementById('analysis').style.display = 'block';
+        document.getElementById('user_answer').innerHTML = obj.userOption.toUpperCase();
+        document.getElementById('correct_answer').innerHTML = obj.answer.toUpperCase();
+
+        let objA = obj.commentA && JSON.parse(obj.commentA)
+        let ifPictrue = false
+        let imgBox = document.getElementById('analysis_img')
+        let textBox = document.getElementById('analysis_con')
+        if ((objA && objA.bgImg) || obj.analysis) {
+          imgBox.style = `
+          width: 13.3rem;
+          height: 7.48rem;
+          display: block;
+          margin: 0px auto;`
+          Utils.$('.answer_line').style = 'display: none;';
+          Utils.$('.analysis_main').style = 'display: none;';
+
+          imgBox.style.display = 'block';
+          imgBox.style.margin = '0 auto';
+          console.log(obj.userOption)
+          if (this.type === '2') {
+            imgBox.src = obj.analysis;
+          } else {
+            switch (obj.userOption) {
+                case 'A':
+                  imgBox.src = objA.bgImg;
+                    break;
+                case 'B':
+                  imgBox.src = JSON.parse(obj.commentB).bgImg;
+                    break;
+                case 'C':
+                  imgBox.src = JSON.parse(obj.commentC).bgImg;
+                    break;
+                default:
+                    break;
+            }
+          }
+        }else {
+          imgBox.style = `
+          width: 16.9rem;
+          height: 5.52rem;
+          display: block;
+          margin: 0px auto;`
+          Utils.$('.answer_line').style = '';
+          Utils.$('.analysis_main').style = '';
+          document.getElementById('user_answer').innerHTML = obj.userOption.toUpperCase();
+          document.getElementById('correct_answer').innerHTML = obj.answer.toUpperCase();
+          if (objA.img) {
+              imgBox.style.display = 'block'
+              document.getElementById('analysis_main').style.display = 'none';
+              ifPictrue = true;
+          } else {
+              imgBox.style.display = 'none'
+              document.getElementById('analysis_main').style.display = 'block';
+          }
+          // 播放解析语音
+          console.log(obj.userOption)
+          console.log(JSON.parse(obj.commentA))
+          console.log(JSON.parse(obj.commentB))
+          console.log(JSON.parse(obj.commentC))
+          switch (obj.userOption) {
+              case 'A':
+                  ifPictrue ? imgBox.src = objA.img : textBox.innerHTML = objA.text
+                  break;
+              case 'B':
+                  ifPictrue ? imgBox.src = JSON.parse(obj.commentB).img : textBox.innerHTML = JSON.parse(obj.commentB).text
+                  break;
+              case 'C':
+                  ifPictrue ? imgBox.src = JSON.parse(obj.commentC).img : textBox.innerHTML = JSON.parse(obj.commentC).text
+
+                  break;
+              default:
+                  break;
+          }
+        }
+        // 是否显示按钮
+        // if (obj.feature) {
+        //     document.getElementById('analysis_btn').style.display = 'block';
+        // }
+
+    }
+
+    aiHandle(name, slots) {
+
+    }
+
+    touchBtn(type) {
+        if (type === 'next') {
+            document.getElementById('next_final').className = 'next tap'
+            clearTimeout(this.nextTime)
+
+            this.nextTime = setTimeout(() => {
+                document.getElementById('next_final').className = 'next'
+            }, 210)
+        } else {
+            document.getElementById('prev_first').className = 'prev tap'
+            clearTimeout(this.prevTime)
+            this.prevTime = setTimeout(() => {
+                document.getElementById('prev_first').className = 'prev'
+            }, 210)
+        }
+    }
+    showTip(text) {
+        document.getElementById('tip_modal').innerText = text
+        document.getElementById('tip_modal').style.display = 'block'
+        clearTimeout(this.tipTime)
+        this.tipTime = setTimeout(() => {
+            document.getElementById('tip_modal').style.display = 'none'
+
+        }, 1000)
+
+    }
+}
+
+module.exports = errorScene;

+ 138 - 0
src/stage/index/scene/match/matchDialogScene.js

@@ -0,0 +1,138 @@
+/**
+ * 只针对答题使用其他使用需要改装
+ * bg弹窗背景图
+ * type:按钮类型和事件 end结束 quit退出 report报告
+ * raceId 比赛id
+ * reportCon报告数据
+ */
+import Utils from '../../../../util/utils'
+import { postQuestionSubmit } from '../../../../service/match';
+
+class matchDialogScene extends scene {
+    constructor(scope) {
+        super(scope);
+    }
+
+    onCreate(data) {
+        const hide = data.type === 'report' ? 'block' : 'none';
+        this.setContentView(require('../../../../res/tpl/match/matchDialog.tpl'), {
+            bg: data.bg,
+            type: data.type,
+            hide,
+            reportCon: data.reportCon,
+            totalScore: data.reportCon.totalScore || 0
+        }, 'matchDialog', {
+            'isParentShow': true
+        }, () => {
+            this.type = data.type;
+            if (this.type === 'report' || this.type === 'end') {
+                Utils.$('.one-btn').style = 'display: block';
+            } else {
+                Utils.$('.two-btn').style = 'display: flex';
+            }
+            if (this.type === 'report') {
+                Utils.$('.match-dialog').style.zIndex = '100'
+            }
+            this.raceId = data.raceId;
+        });
+    }
+
+
+    onResume() {
+
+    }
+
+    onPause() {
+
+    }
+
+    onDestroy() {
+
+    }
+
+    onActive() {
+
+    }
+
+    onInactive() {
+
+    }
+
+    // 所有事件类函数默认触发时会传递过来一个Event,其中包含着事件响应节点以及其他相关信息
+    // 查看信息:console.log(e)
+    // 获取Event节点Id的方法(前提是触发事件的节点存在Id)
+    // 在使用Atv时Id对应:e.target.id
+    // 非使用Atv是Id对应:e.target.id
+    onOK(e) {
+    const flag = e.target.con.attributes['data-event'].nodeValue;
+    // 代替switch
+    const actions = new Map([
+        ['back', () => {
+            this.hideScene({}, 'match')
+        }],
+        ['end', () => {
+            this.peport();
+            Utils.$('.one-btn').setAttribute('data-event', 'report');
+            Utils.$('.one-btn').style = 'bottom: .5rem';
+            this.moye.root.reRender();
+        }],
+        ['report', () => {
+            console.log('去排行榜')
+            this.hideScene();
+            this.showScene(require('./rankingScene'), {
+                raceId: this.raceId
+            });
+        }],
+        ['quit', () => {
+            // this.hideScene();
+            if (this.raceId) {
+                this.submitRecord().then(res => {
+                    console.log(res)
+                    console.log(res.rightAmount)
+                    const {rightAmount, answerAmount, totalScore} = res;
+                    Utils.$('.two-btn').setAttribute('data-event', 'report');
+                    Utils.$('.two-btn').setAttribute('fe-role', 'Widget');
+                    Utils.$('.two-btn').style = 'bottom: .5rem;width: 4.25rem;height: .74rem;';
+                    Utils.$('.two-btn').innerHTML = '';
+                    this.peport()
+                    Utils.$('.report-container').innerHTML = `
+                    <div>用时:1分钟</div>
+                    <div>答对:${rightAmount}/${answerAmount}题</div>
+                    <div>得分:${totalScore || 0}分</div>
+                    `
+                    this.moye.root.reRender();
+                })
+            }
+        }],
+        ['cancel', () => {
+            this.hideScene();
+        }]
+      ])
+      const action = actions.get(flag)
+      if (action) {
+        clickMp3();
+        action()
+      }
+    }
+
+    onBack() {
+    }
+    peport() {
+        Utils.$('.match-dialog').style.zIndex = '100';
+        Utils.$('.dialog-container').style.background = 'url(assets/match/baogao_bg.png) 0 0 / 100% 100%'
+        Utils.$('.report-container').style = 'display: block';
+    }
+    submitRecord() {
+        return new Promise((resolve, reject) => {
+            postQuestionSubmit({
+                raceId: this.raceId
+            }).success(res => {
+                resolve(res.data)
+                // this.hideScene();
+            })
+        })
+
+    }
+}
+
+module.exports = matchDialogScene;

+ 268 - 0
src/stage/index/scene/match/matchScene.js

@@ -0,0 +1,268 @@
+import { getRaceAll, postSubscribe, getMyRace, getRaceJoin } from '../../../../service/match';
+import {
+  $,
+  setBg,
+  setBack,
+  writeLocalStorage,
+  readLocalstorage
+} from '../../../../util/utils'
+import { isLogin } from '../../../../util/login'
+class matchScene extends scene {
+    constructor(scope) {
+        super(scope);
+        this.nowTime = '';
+    }
+
+    onCreate(data) {
+        this.setContentView(require('../../../../res/tpl/match/match.tpl'), data, 'match', {
+            containerId: 'root'
+        }, () => {
+            setBg('#403C94');
+            setBack(false, '.52');
+            const type = data.type;
+            this.grade = readLocalstorage('class');
+            this.checkoutTab(type)
+        });
+    }
+    onOK(e) {
+        const flag = e.target.con.attributes['data-event'].nodeValue;
+        switch (flag) {
+            case 'back':
+                this.hideScene();
+                break;
+            case 'ranking':
+                this.showScene(require('./rankingScene'), {
+                    raceId: e.target.con.dataset.id || ''
+                });
+                break;
+            case 'subscribe':
+                if (e.target.con.innerHTML !== '已预约') {
+                    postSubscribe({
+                        raceId: e.target.con.dataset.id
+                    }).success(res => {
+                        e.target.con.innerHTML = '已预约';
+                        const numberbox = e.target.con.parentNode.parentNode.querySelector('.course_match_sign_up span');
+                        numberbox.innerHTML = numberbox.innerHTML * 1 + 1;
+                    })
+                }
+                break;
+            case 'joinRanking':
+                 // console.log(e.target.con.dataset)
+                if (e.target.con.dataset.join === 'true') {
+                    this.submitRecord(e.target.con.dataset.id).then(res => {
+                        this.showScene(require('./matchDialogScene.js'), {
+                            bg: 'assets/match/baogao_bg.png',
+                            type: 'report',
+                            reportCon: res,
+                            raceId: e.target.con.dataset.id
+                        })
+                    })
+                    return
+                }
+                postSubscribe({
+                    raceId: e.target.con.dataset.id
+                }).success(res => {
+                    this.showScene(require('./answerScene.js'), {
+                        raceId: e.target.con.dataset.id
+                    })
+                })
+
+                break;
+            case 'whole':
+                this.checkoutTab('whole')
+                break;
+            case 'my':
+                this.checkoutTab('my')
+                break;
+            case 'goTo':
+                this.scrollTo(this.goToLength);
+                break;
+            case 'errorbook':
+                this.showScene(require('./errorbookScene.js'), {
+                    type: '2'
+                });
+                break;
+            case 'details':
+                this.showScene(require('./detailsScene.js'), {
+                    raceId: e.target.con.dataset.id
+                });
+                break;
+            // default:
+            //     break;
+        }
+    }
+    onResume(data) {
+        console.log(data)
+        setBack(false, '.52');
+        setBg('#403C94');
+        const type = readLocalstorage('type')
+        this.checkoutTab(type)
+    }
+    scrollTo(index) {
+        document.querySelector('.match_container').scrollTo(index * 960,0)
+    }
+    getData() {
+        return new Promise((resolve, reject) => {
+            getRaceAll(this.grade).success(res => {
+                const data = res.data;
+                this.nowTime = data.now;
+                data.todayRace = this.deformationArr(data.todayRace)
+                data.futureRace = this.deformationArr(data.futureRace)
+                data.historyRace = this.deformationArr(data.historyRace)
+                data.tomorrowRace = this.deformationArr(data.tomorrowRace)
+                resolve(data)
+            }).fail(error => {
+                reject();
+            })
+        })
+    }
+    getMyData() {
+        return new Promise((resolve, reject) => {
+            getMyRace(this.grade).success(res => {
+                const data = res.data;
+                this.nowTime = data.now;
+                data.todayRace = this.deformationArr(data.todayRace)
+                data.futureRace = this.deformationArr(data.futureRace)
+                data.historyRace = this.deformationArr(data.historyRace)
+                data.tomorrowRace = this.deformationArr(data.tomorrowRace)
+                resolve(data)
+            }).fail(error => {
+                reject();
+            })
+        })
+    }
+    /**
+     * 格式化数组
+     * data格式化的数组Array
+     * num 几个数组元素为一组number
+    */
+    deformationArr(data, num) {
+        const newArr = [];
+        num = num || 2;
+        data.forEach(item => {
+            const race = item.race
+            const startDate = new Date(race.startTime).toLocaleDateString().slice(5).replace('/', '月');
+            const startTime = new Date(race.startTime).toTimeString().slice(0, 5);
+            const endTime = new Date(race.endTime).toTimeString().slice(0, 5);
+            if(race.startTime < new Date() < race.endTime) {
+
+            }
+            race.join = (new Date(race.startTime) * 1 < this.nowTime) &&  (this.nowTime < new Date(race.endTime) * 1);
+            race.time = startDate + '日 ' + startTime + '-' +endTime;
+        })
+        while(data.length > 0) {
+            newArr.push(data.splice(0, num));
+        }
+        return newArr;
+    }
+    /**
+     * 封装切换的渲染方法
+     */
+    selectRender({
+        historyRace,
+        todayRace,
+        tomorrowRace,
+        futureRace
+    }) {
+        $('.match_container').innerHTML = '';
+        // 历史
+        this.createDom(historyRace, '');
+        // 今天
+        this.createDom(todayRace, 'now');
+        // 明天
+        this.createDom(tomorrowRace, 'tomorrow');
+        // 最近
+        this.createDom(futureRace, 'lately');
+        this.moye.root.reRender();
+        this.goToLength = historyRace.length;
+        this.scrollTo(historyRace.length);
+        isLogin()
+    }
+    createDom(data, type) {
+        if(data.length > 0) {
+            data.forEach((item, index) => {
+                const matchContainerChildren = document.createElement('div');
+                matchContainerChildren.className = 'match_container_children';
+                const matchContainerDetailBox = document.createElement('div');
+                matchContainerDetailBox.className = 'match_container_detail_box';
+                let str = '';
+                item.forEach(childIt => {
+                    /** 按钮展示 */
+                    let btn = '';
+                    let number = ''
+                    if (type) {
+                        if (childIt.race.join) {
+                            btn = `<div class="btn ranking_btn" data-event="joinRanking" data-id="${childIt.race.id}" data-join="${childIt.join}" fe-role="Widget">立即参赛</div>`
+                        } else if(childIt.subscribe) {
+                            btn = `<div class="btn ranking_btn" style="background: #52C755;">已预约</div>`
+                        } else {
+                            btn = `<div class="btn ranking_btn" style="background: #52C755;" data-id="${childIt.race.id}" data-event="subscribe" fe-role="Widget">一键预约</div>`
+                        }
+                        number = `<span>${childIt.race.subscribeAmount || 0}</span>`
+                    } else {
+                        btn = `<div class="btn ranking_btn" data-id="${childIt.race.id}" data-event="ranking" fe-role="Widget">排行榜</div>`
+                        number = childIt.race.subscribeAmount || 0
+                    }
+                    str+=`
+                    <div class="match_container_detail ${type ? 'now' : 'history'}">
+                        <img class="img_icon" src="${childIt.race.icon}"/>
+                        <div class="course_match">
+                            <span class="course_match_title">${childIt.race.title}</span>
+                            <span class="course_match_date">${childIt.race.time}</span>
+                            <span class="course_match_sign_up">${number}&nbsp报名</span>
+                            <div class="course_btn">
+                                <div class="btn detail_btn" data-id="${childIt.race.id}" data-event="details" fe-role="Widget">查看详情</div>
+                                ${btn}
+                            </div>
+                        </div>
+                    </div>
+                    `
+                })
+                matchContainerDetailBox.innerHTML = str;
+                if (type && index === 0) {
+                    const matchContainerIcon = document.createElement('img');
+                    matchContainerIcon.src = `assets/match/${type}.png`;
+                    matchContainerIcon.className = 'match_container_icon';
+                    matchContainerChildren.appendChild(matchContainerIcon);
+                }
+                matchContainerChildren.appendChild(matchContainerDetailBox)
+                $('.match_container').appendChild(matchContainerChildren);
+                /* 占位行 */
+                if(index === data.length -1 ){
+                    let placeDiv = document.createElement('div');
+                    placeDiv.className = 'place-right'
+                    $('.match_container').appendChild(placeDiv);
+                }
+            })
+        }
+    }
+    // 提交本次记录
+    submitRecord(raceId) {
+        return new Promise((resolve, reject) => {
+            getRaceJoin(raceId).success(res => {
+                resolve(res.data)
+            })
+        })
+
+    }
+    // 切换
+    checkoutTab(type) {
+        if (type === 'whole') {
+            $('.table').classList.add('whole_match_focus')
+            $('.table').classList.remove('my_match_focus')
+            writeLocalStorage('type', 'whole')
+            this.getData().then((data) => {
+                this.selectRender(data)
+            })
+        } else {
+            $('.table').classList.remove('whole_match_focus')
+            $('.table').classList.add('my_match_focus')
+            writeLocalStorage('type', 'my')
+            this.getMyData().then((data) => {
+                this.selectRender(data)
+            });
+        }
+    }
+}
+
+module.exports = matchScene;

+ 89 - 0
src/stage/index/scene/match/rankingScene.js

@@ -0,0 +1,89 @@
+import {getRankingList, getRaceRanking } from '../../../../service/match';
+import {
+  $,
+  setBg,
+  setBack,
+  readLocalstorage
+} from '../../../../util/utils'
+class rankingScene extends scene {
+    constructor(scope) {
+        super(scope);
+
+    }
+
+    onCreate(data) {
+        this.raceId = data.raceId;
+        const type = this.raceId ? 'single' : 'total'
+        this.getRanking().then(res => {
+            console.log(res)
+            this.setContentView(require('../../../../res/tpl/match/ranking.juicer'), {
+                data: res,
+                type,
+                rankImgs: ['one', 'two', 'three']
+            }, 'ranking', {
+                containerId: 'root'
+            }, () => {
+              type === 'single' ? setBg('#403C94') : setBg('assets/match/ranking_bg.jpg');
+              setBack(false, '.32');
+            });
+        })
+
+    }
+    onOK(e) {
+        console.log(e)
+        const flag = e.target.con.attributes['data-event'].nodeValue;
+        switch (flag) {
+            case 'back':
+                this.hideScene({}, 'match')
+                break;
+
+        }
+    }
+    getRanking() {
+        return new Promise((resolve, reject) => {
+            const grade = this.grade = readLocalstorage('class');
+            if(this.raceId) {
+                getRaceRanking(this.raceId, grade).success(res => {
+                    const data = res.data;
+                    data.rankingListVOS = this.deformationArr(data.rankingListVOS)
+                    this.nameToMobile(data.rankingListVOS)
+                    resolve(data)
+                })
+            }else {
+                getRankingList(grade).success(res => {
+                    this.nameToMobile(res.data.list)
+                    resolve(res.data.list);
+                })
+            }
+        })
+
+    }
+
+    onBack(e) {
+
+    }
+    deformationArr(data, num, ) {
+        const newArr = [];
+        num = num || 10;
+        while(data.length > 0) {
+            newArr.push(data.splice(0, num));
+        }
+        return newArr;
+    }
+    // 转换手机号
+    nameToMobile(data) {
+        data.forEach(item => {
+            const childData = item.rankingListVOS || item;
+            childData.forEach(childItem => {
+                if (childItem.member) {
+                    childItem.member.nickName = childItem.member.nickName || childItem.member.mobile.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
+                } else {
+                    childItem.member = {}
+                    childItem.member.nickName = '199****8888'
+                }
+            })
+        })
+    }
+}
+
+module.exports = rankingScene;

+ 53 - 0
src/stage/index/style/match/ad.less

@@ -0,0 +1,53 @@
+.ad-con {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100% !important;
+  height: 100% !important;
+  overflow: hidden;
+
+  // background: #000;
+  .ad_icon {
+    position: absolute;
+    left: 50%;
+    top: 1.35rem;
+    width: 13.01rem;
+    height: 8.28rem;
+    transform: translateX(-50%);
+    background-size: 100% 100%;
+  }
+
+  .ad_img_box {
+    width: 100%;
+    height: 100%;
+    position: absolute;
+    top: 0;
+    left: 0;
+    display: none;
+    .img_banner {
+      width: 19.2rem;
+      height: 100%;
+      position: absolute;
+      bottom: 0;
+      right: 0;
+    }
+
+    .ad_img_btn {
+      width: 1.73rem;
+      height: 0.9rem;
+      background: rgba(0, 0, 0, .6);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      border-radius: .3rem;
+      transform: rotate(270deg);
+      position: absolute;
+      top: 0.7rem;
+      z-index: 1;
+      span {
+        color: #fff;
+        font-size: .4rem;
+      }
+    }
+  }
+}

+ 494 - 0
src/stage/index/style/match/answer.less

@@ -0,0 +1,494 @@
+@keyframes getBig {
+    0% {
+        transform: scale(1);
+    }
+
+    100% {
+        transform: scale(1.2, 1.2);
+    }
+
+}
+
+@keyframes getShine {
+    0% {
+        color: #fff;
+    }
+
+    25% {
+        color: #fff;
+    }
+
+    50% {
+        color: #fff;
+    }
+
+    75% {
+        color: #fff;
+    }
+
+    100% {
+        color: #000;
+    }
+}
+
+.answer_change {
+    width: 100%;
+    height: 100%;
+    position: absolute;
+
+    .back {
+        width: 0.69rem;
+        height: 0.86rem;
+        position: absolute;
+        top: 1.05rem;
+        left: 0.93rem;
+
+        img {
+            width: 100%;
+            height: 100%;
+        }
+    }
+
+    .review_main {
+        width: 18.29rem;
+        height: 9.01rem;
+        // background: #fff;
+        position: relative;
+        top: 1.26rem;
+        left: .43rem;
+        background: url('assets/mobile/white_bg.png') no-repeat;
+        background-size: 100% 100%;
+    }
+
+    .number_menu {
+        width: 11.22rem;
+        height: 1.06rem;
+        border-radius: .5rem;
+        // border: .03rem solid #fff;
+        display: flex;
+        align-items: center;
+        overflow: scroll;
+        position: absolute;
+        top: 0.46rem;
+        left: 1.3rem;
+        background: #f2f7f6;
+        border-radius: .53rem;
+        padding: 0 .2rem;
+        box-sizing: border-box;
+        &::-webkit-scrollbar {
+            display: none;
+        }
+        .number {
+            flex-shrink: 0;
+            width: 0.76rem;
+            height: 0.76rem;
+            border-radius: 50%;
+            text-align: center;
+            color: #5f5f5f;
+            line-height: .68rem;
+            box-sizing: border-box;
+            font-size: .31rem;
+            background: #fff;
+            // box-shadow: 0px 5px 0px 0px rgba(80, 80, 80, 1);
+            border: .04rem solid #7a7a7a;
+            position: relative;
+            margin-right: .3rem;
+            img {
+                width: 0.36rem;
+                height: 0.36rem;
+                position: absolute;
+                right: -.12rem;
+                bottom: .4rem;
+                border-radius: 50%;
+            }
+
+            &.right {
+                background: rgba(142, 216, 0, 1) !important;
+                box-shadow: 0px 5px 0px 0px rgba(61, 124, 0, 1);
+            }
+
+            &.wrong {
+                background: rgba(250, 94, 36, 1) !important;
+                box-shadow: 0px 5px 0px 0px rgba(179, 49, 0, 1);
+            }
+
+            &.current {
+                // border: .06rem solid rgba(255, 255, 255, 1);
+                // background: #fff;
+                // color: #3A3A3A;
+                // transform: scale(1.2, 1.2);
+                // animation: getBig .9s ease;
+                // border: .02rem solid #fff;
+                background: rgba(255, 157, 48, 1);
+                border: .04rem solid rgba(244, 126, 31, 1);
+
+            }
+
+        }
+
+    }
+
+    .prev_next {
+        position: absolute;
+        left: 13rem;
+        top: 0.36rem;
+
+
+        .prev {
+            width: 2.11rem;
+            height: .94rem;
+            border-radius: .9rem;
+            background: #45c9a5;
+            box-shadow: 0 .05rem 0 0 #07a183;
+            position: absolute;
+            left: 0;
+            top: 0.1rem;
+            font-size: .44rem;
+            font-weight: 500;
+            color: #fff;
+            line-height: 1rem;
+            text-align: center;
+
+            &.shine {
+                animation: getShine .3s linear;
+            }
+        }
+
+        .next {
+            width: 2.11rem;
+            height: .94rem;
+            border-radius: .8rem;
+            background: #45c9a5;
+            box-shadow: 0 .05rem 0 0 #07a183;
+            position: absolute;
+            left: 2.55rem;
+            top: 0.1rem;
+            font-size: .44rem;
+            color: #fff;
+            font-weight: 500;
+            line-height: 1rem;
+            text-align: center;
+
+            &.shine {
+                animation: getShine .3s linear;
+            }
+        }
+    }
+
+    .lesson_main {
+        width: 16.68rem;
+        height: 8rem;
+        position: relative;
+        top: 1.65rem;
+        margin: 0 auto;
+        animation: fadeOut .5s linear;
+
+        .text_area {
+            width: 100%;
+            height: 5.8rem;
+            font-size: .6rem;
+            color: #000;
+            background: rgba(0, 0, 0, 0);
+            line-height: .84rem;
+            white-space: normal;
+            display: block;
+            position: relative;
+            top: -.35rem;
+            left: .55rem;
+            overflow: hidden;
+            transform: scale(.86);
+            .ques_img {
+              width: 16.84rem;
+              height: 5rem;
+            }
+            .ques_big_img {
+              width: 13.3rem;
+              height: 7.48rem;
+              margin: 0 auto;
+              display: block;
+              transform: translateY(-.5rem) scale(1);
+            }
+            .ques_score {
+                position: fixed;
+                font-size: .5rem;
+                left: 。7rem;
+                top: .2rem;
+                z-index: 1;
+                border-bottom: 0;
+            }
+            // 下划线
+            span {
+                border-bottom: .02rem solid #000;
+            }
+
+            // 加粗 实线下划线
+            b {
+                border-bottom: .02rem solid #000;
+
+            }
+
+            // 虚线下划线
+            i {
+                font-style: normal;
+                border-bottom: .02rem dashed #000;
+            }
+
+            // 控制空格
+            em {
+                width: 1.3rem;
+                display: inline-block;
+            }
+
+            // 斜体
+            p {
+                display: inline;
+                font-style: italic;
+            }
+        }
+
+        .answer_area {
+            width: 100%;
+            height: 1.5rem;
+            box-sizing: border-box;
+            display: flex;
+            align-items: center;
+            justify-content: space-around;
+            margin-top: -.5rem;
+            .answer_item {
+                width: auto;
+                position: relative;
+                display: flex;
+                align-items: center;
+
+                &.fe-focus {
+                    // &.fade_animation{
+                    animation: answerFade .7s ease;
+                    // }
+                }
+
+                .answer_icon {
+                    width: 1rem;
+                    height: 1rem;
+                    display: inline-block;
+                    // box-shadow: 0 0.02rem 0.08rem 0 rgba(208, 224, 231, 0.32);
+                    color: #FB9B38;
+                    font-size: .5rem;
+                    text-align: center;
+                    font-weight: 500;
+                    line-height: 1rem;
+                    border-radius: 50%;
+
+                    &.choose {
+                        // border: 0.04rem solid #f47e1f;
+                        // color: #fff;
+                        // background: #ff9d30;
+                    }
+                }
+
+                .right_wrong {
+                    width: 1.13rem;
+                    height: .81remrem;
+                    position: absolute;
+                    top: 0.58rem;
+                    left: .75rem;
+                    display: none;
+                }
+
+                .answer_text {
+                    display: flex;
+                    align-items: center;
+                    color: #000;
+                    font-size: .5rem;
+                    height: 1.85rem;
+                    line-height: .83rem;
+                    text-overflow: wrap;
+                    flex-wrap: wrap;
+                    max-width: 3.7rem;
+
+
+                    span {
+                        border-bottom: .02rem solid #000;
+                        height: .85rem;
+                    }
+
+                    b {
+                        border-bottom: .02rem solid #000;
+                        font-style: normal;
+                        font-weight: normal;
+                        height: .85rem;
+
+                    }
+
+                    i {
+                        font-style: normal;
+                        border-bottom: .02rem dashed #000;
+                        height: .85rem;
+
+                    }
+
+                    // 斜体
+                    p {
+                        display: inline;
+                        font-style: italic;
+                    }
+
+                    // 控制空格
+                    em {
+                        width: 1.3rem;
+                        display: inline-block;
+                    }
+                }
+            }
+        }
+
+        .analysis {
+            padding-left: .63rem;
+            padding-top: .41rem;
+            display: none;
+
+            .answer_line {
+                width: 100%;
+                display: flex;
+                align-items: center;
+                font-size: .6rem;
+                margin-bottom: .29rem;
+
+                .user_answer {
+                    color: #000;
+                    margin-right: .56rem;
+                }
+
+                .user_answer {
+                    color: #0d7a00;
+                }
+            }
+
+            .analysis_main {
+                font-size: .5rem;
+                color: #000;
+
+                .orange {
+                    color: #ff8800;
+                }
+                b {
+                    border-bottom: .02rem solid #000;
+                    font-style: normal;
+                    font-weight: normal;
+                    height: .85rem;
+
+                }
+
+                i {
+                    font-style: normal;
+                    border-bottom: .02rem dashed #000;
+                    height: .85rem;
+
+                }
+
+                // 控制空格
+                em {
+                    width: 1.3rem;
+                    display: inline-block;
+                }
+
+                // 斜体
+                p {
+                    display: inline;
+                    font-style: italic;
+                }
+
+
+
+                b {
+                    border-bottom: .02rem solid #000;
+                    font-style: normal;
+                    font-weight: normal;
+                    height: .85rem;
+
+                }
+
+                i {
+                    font-style: normal;
+                    border-bottom: .02rem dashed #000;
+                    height: .85rem;
+
+                }
+
+                // 斜体
+                p {
+                    display: inline;
+                    font-style: italic;
+                }
+
+                // 控制空格
+                em {
+                    width: 1.3rem;
+                    display: inline-block;
+                }
+
+                width: 16.72rem;
+
+            }
+
+            .analysis_btn {
+                display: none;
+                width: 4.5rem;
+                height: .99rem;
+                background: #4cb0ff;
+                box-shadow: 0 .08rem 0 0 #0D70BF;
+                border-radius: .55rem;
+                font-size: .52rem;
+                font-weight: 500;
+                color: #fff;
+                line-height: .99rem;
+                text-align: center;
+                margin: 0 auto;
+                position: absolute;
+                top: 7.25rem;
+                left: 0;
+                right: 0;
+            }
+        }
+
+    }
+    .anser_title {
+        position: absolute;
+        top: -.97rem;
+        left: 1.2rem;
+        font-size: .5rem;
+        font-weight: 500;
+        color: #FFFFFF;
+    }
+    .anser_time_submit {
+        position: absolute;
+        left: 50%;
+        top: -1rem;
+        display: flex;
+        transform: translateX(-50%);
+        .answer_time {
+            display: flex;
+            align-items: center;
+            img {
+                width: .62rem;
+                height: .73rem;
+                margin-right: .3rem;
+            }
+            span {
+                font-size: .53rem;
+                font-weight: 500;
+                color: #FFFFFF;
+            }
+        }
+        .answer_submit {
+            width: 1.84rem;
+            height: .74rem;
+            background: #FFA400;
+            border-radius: .38rem;
+            text-align: center;
+            line-height: .74rem;
+            color: #FFFFFF;
+            margin-left: .36rem;
+            font-size: .44rem;
+        }
+    }
+}

+ 21 - 0
src/stage/index/style/match/details.less

@@ -0,0 +1,21 @@
+.details {
+  position: absolute;
+  left: 0;
+  top: 0;
+  z-index: 1;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  .img_container {
+    overflow: scroll;
+    display: flex;
+    &::-webkit-scrollbar {
+        display: none;
+    }
+    img {
+      width: 100%;
+      height: 100%;
+      flex-shrink: 0;
+    }
+  }
+}

+ 618 - 0
src/stage/index/style/match/errorbook.less

@@ -0,0 +1,618 @@
+@keyframes fadeOutNew {
+    0% {
+        opacity: 1;
+    }
+
+    100% {
+        opacity: 0;
+    }
+}
+
+@keyframes shake {
+    0% {
+        width: 1.97rem;
+        height: 1.16rem;
+    }
+
+    10% {
+        width: 2.3rem;
+        height: 1.26rem;
+    }
+
+    15% {
+        width: 2.42rem;
+        height: 1.35rem;
+    }
+
+    20% {
+        width: 2.5rem;
+        height: 1.4rem;
+    }
+
+    25% {
+        width: 1.97rem;
+        height: 1.16rem;
+    }
+
+    50% {
+        width: 1.97rem;
+        height: 1.16rem;
+    }
+
+    75% {
+        width: 1.97rem;
+        height: 1.16rem;
+    }
+
+    100% {
+        width: 1.97rem;
+        height: 1.16rem;
+    }
+}
+
+@keyframes toLeft {
+    0% {
+        left: 0;
+    }
+
+    15% {
+        left: -.1rem;
+    }
+
+    25% {
+        left: -.15rem;
+    }
+
+    30% {
+        left: 0;
+    }
+
+    70% {
+        left: 0;
+    }
+
+    75% {
+        left: -.1rem;
+    }
+
+    85% {
+        left: -.15rem;
+    }
+
+    90% {
+        left: 0;
+    }
+
+    100% {
+        left: 0;
+    }
+}
+
+@keyframes toRight {
+    0% {
+        right: 0;
+    }
+
+    15% {
+        right: -.1rem;
+    }
+
+    25% {
+        right: -.15rem;
+    }
+
+    30% {
+        right: 0;
+    }
+
+    70% {
+        right: 0;
+    }
+
+    75% {
+        right: -.1rem;
+    }
+
+    85% {
+        right: -.15rem;
+    }
+
+    90% {
+        right: 0;
+    }
+
+    100% {
+        right: 0;
+    }
+}
+
+@keyframes myRotate {
+    0% {
+        transform: rotate(0deg);
+        transform-origin: bottom;
+
+    }
+
+    30% {
+        transform: rotate(45deg);
+        transform-origin: bottom;
+    }
+
+    60% {
+        transform: rotate(45deg);
+        transform-origin: bottom;
+    }
+
+    100% {
+        transform: rotate(0deg);
+        transform-origin: bottom;
+
+    }
+}
+
+.scroll_analysis {
+    width: 1.90rem;
+    height: 1.48rem;
+    position: absolute;
+    right: -0.5rem;
+    bottom: .5rem;
+    display: none;
+    background: url(http://asxx-img.ai160.com/images/asxx/assets/pre/scroll_ana.gif) no-repeat;
+    background-size: 100% 100%;
+}
+
+.errorbook {
+    width: 100%;
+    height: 100%;
+    position: absolute;
+    overflow: hidden;
+    background: #8E53D9;
+    background-size: 100% 100%;
+
+    .back {
+        width: 0.69rem;
+        height: 0.86rem;
+        position: absolute;
+        top: 0.38rem;
+        left: 0.63rem;
+
+        img {
+            width: 100%;
+            height: 100%;
+        }
+    }
+    .prev_next {
+        position: absolute;
+        right: 1rem;
+        top: .32rem;
+        display: flex;
+
+        .prev,
+        .next {
+            width: 2.11rem;
+            height: .94rem;
+            border-radius: .52rem;
+            background: #FEB32A;
+            font-size: .44rem;
+            font-weight: 500;
+            color: #fff;
+            line-height: .94rem;
+            text-align: center;
+
+            // span {
+            //     width: 3.46rem;
+            //     height: 0.1rem;
+            //     background: #e2d5c2;
+            //     opacity: .3;
+            //     display: block;
+            //     left: 0;
+            //     position: absolute;
+            //     bottom: 0;
+            // }
+
+            // &.tap {
+            //     background: #ff9d30;
+            //     box-shadow: 0 -.07rem 0 0 #ffbb6f;
+
+            //     span {
+            //         background: #e28929;
+            //         opacity: .3;
+            //     }
+            // }
+        }
+        .next {
+            margin-left: .74rem;
+        }
+        // .next {
+        //     width: 3.46rem;
+        //     height: .93rem;
+        //     border-radius: .5rem .5rem 0 0;
+        //     background: #fff4e4;
+        //     box-shadow: 0 -.07rem 0 0 rgba(255, 255, 255, 1);
+        //     position: absolute;
+        //     top: -.05rem;
+        //     left: 3.53rem;
+        //     font-size: .46rem;
+        //     color: #241600;
+        //     font-weight: 500;
+        //     line-height: 1rem;
+        //     text-align: center;
+
+        //     span {
+        //         width: 3.46rem;
+        //         height: 0.1rem;
+        //         background: #e2d5c2;
+        //         opacity: .3;
+        //         display: block;
+        //         left: 0;
+        //         position: absolute;
+        //         bottom: 0;
+        //     }
+
+        //     &.tap {
+        //         background: #ff9d30;
+        //         box-shadow: 0 -.07rem 0 0 #ffbb6f;
+
+        //         span {
+        //             background: #e28929;
+        //             opacity: .3;
+        //         }
+        //     }
+        // }
+    }
+    .errorbook-tab {
+        display: flex;
+        margin-top: .48rem;
+        .tab-item {
+          width: 3.08rem;
+          height: 1.07rem;
+          font-size: .5rem;
+          color: #fff;
+          box-sizing: border-box;
+          text-align: center;
+          line-height: 1.3rem;
+          &:nth-child(1) {
+            background: url('assets/mobile/school_bg.png') no-repeat;
+            background-size: 100% 100%;
+            margin-left: 1.64rem;
+          }
+          &:nth-child(2) {
+            background: url('assets/mobile/test_bg.png') no-repeat;
+            background-size: 100% 100%;
+          }
+          &:nth-child(3) {
+            background: url('assets/mobile/question_bg.png') no-repeat;
+            background-size: 100% 100%;
+          }
+        }
+        .tab-select {
+          background: url('assets/mobile/select_bg.png') no-repeat !important;
+          background-size: 100% 100% !important;
+          color: #333;
+        }
+      }
+    .category {
+        width: 6.5rem;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        position: absolute;
+        top: .27rem;
+        left: 1.75rem;
+
+        .category_btn {
+            text-align: center;
+            width: 3.07rem;
+            height: 0.95rem;
+            background: #fff4e4;
+            box-shadow: 0 .07rem 0 0 #919191;
+            font-size: .46rem;
+            font-weight: 500;
+            line-height: .95rem;
+            color: #353535;
+            border-radius: 1rem;
+
+            &.focus {
+                background: #ff9d30;
+                box-shadow: 0 .07rem 0 0 #f47e1f;
+                color: #fff;
+            }
+        }
+
+
+    }
+    .review_main {
+        width: 18.29rem;
+        height: 9.01rem;
+        background: url('assets/mobile/white_bg.png') no-repeat;
+        background-size: 100% 100%;
+        position: relative;
+        top: 0;
+        left: 0;
+        right: 0;
+        margin: 0 auto;
+    }
+
+    .no_ques {
+        display: none;
+        width: 3.95rem;
+        height: 4.64rem;
+        position: absolute;
+        top: 2rem;
+        left: 0;
+        right: 0;
+        margin: 0 auto;
+    }
+
+    .before_re_box {
+        width: 18.68rem;
+        height: 10.12rem;
+        background: url('http://asxx-img.ai160.com/images/asxx/assets/pre/broad.png') no-repeat;
+        position: relative;
+        top: 0.1rem;
+        margin: 0 auto;
+        animation: fadeOut .5s linear;
+
+
+    }
+
+    .lesson_main {
+        width: 17.45rem;
+        height: 8.08rem;
+        position: relative;
+        top: .65rem;
+        margin: 0 auto;
+        animation: fadeOut .5s linear;
+        // background: #fff;
+        border-radius: .2rem;
+
+        .text_area {
+            width: 15.5rem;
+            height: 5.8rem;
+            font-size: .6rem;
+            color: #000;
+            background: rgba(0, 0, 0, 0);
+            line-height: .84rem;
+            white-space: normal;
+            display: block;
+            position: relative;
+            top: .5rem;
+            left: 1.4rem;
+            overflow: hidden;
+            .ques_img {
+              width: 16.84rem;
+              height: 5rem;
+            }
+            .ques_big_img {
+              width: 13.3rem;
+              height: 7.48rem;
+              margin: 0 auto;
+              display: block;
+              transform: translateY(-.6rem) scale(1.1);
+            }
+            // 下划线
+            span {
+                border-bottom: .02rem solid #000;
+            }
+
+            // 加粗 实线下划线
+            b {
+                border-bottom: .02rem solid #000;
+
+            }
+
+            // 虚线下划线
+            i {
+                font-style: normal;
+                border-bottom: .02rem dashed #000;
+            }
+
+            // 控制空格
+            em {
+                width: 1.3rem;
+                display: inline-block;
+            }
+
+            // 斜体
+            p {
+                display: inline;
+                font-style: italic;
+            }
+        }
+
+        .answer_area {
+            display: none;
+            width: 100%;
+            height: 1.5rem;
+            // padding-left: 1.2rem;
+            box-sizing: border-box;
+            // display: flex;
+            align-items: center;
+            justify-content: space-around;
+
+            .answer_item {
+                width: auto;
+                position: relative;
+                display: flex;
+                align-items: center;
+
+                &.fe-focus {
+                    // &.fade_animation{
+                    animation: answerFade .7s ease;
+                    // }
+                }
+
+                .answer_icon {
+                    width: 1rem;
+                    height: 1rem;
+                    display: inline-block;
+                    margin-right: .48rem;
+                    // box-shadow: 0 0.02rem 0.08rem 0 rgba(208, 224, 231, 0.32);
+                    border: 0.04rem solid #7a7a7a;
+                    color: #7a7a7a;
+                    font-size: .56rem;
+                    text-align: center;
+                    font-weight: 500;
+                    line-height: 1rem;
+                    border-radius: 50%;
+
+                    &.choose {
+                        border: 0.04rem solid #f47e1f;
+                        color: #fff;
+                        background: #ff9d30;
+                    }
+                }
+
+                .right_wrong {
+                    width: 1.13rem;
+                    height: .81rem;
+                    position: absolute;
+                    top: 0.98rem;
+                    left: .75rem;
+                    display: none;
+                }
+
+                .answer_text {
+                    display: flex;
+                    align-items: center;
+                    color: #000;
+                    font-size: .58rem;
+                    height: 1.85rem;
+                    line-height: .83rem;
+                    text-overflow: wrap;
+                    flex-wrap: wrap;
+                    max-width: 3.7rem;
+
+
+                    // 下划线
+                    span {
+                        border-bottom: .02rem solid #000;
+                    }
+
+                    // 加粗 实线下划线
+                    b {
+                        border-bottom: .02rem solid #000;
+
+                    }
+
+                    // 虚线下划线
+                    i {
+                        font-style: normal;
+                        border-bottom: .02rem dashed #000;
+                    }
+
+                    // 控制空格
+                    em {
+                        width: 1.3rem;
+                        display: inline-block;
+                    }
+
+                    // 控制空格
+                    em {
+                        width: 1.3rem;
+                        display: inline-block;
+                    }
+
+                    // 斜体
+                    p {
+                        display: inline;
+                        font-style: italic;
+                    }
+                }
+            }
+        }
+
+        .know {
+            width: 5.58rem;
+            height: 1.22rem;
+            background: url('http://ai-img.ai160.com/images/xyyf-res/lesson/know.png') no-repeat;
+            margin: 0 auto;
+            display: none;
+            position: relative;
+            z-index: 2;
+        }
+
+        .analysis {
+            padding-left: .63rem;
+            padding-top: .41rem;
+            display: none;
+
+            .answer_line {
+                width: 100%;
+                display: flex;
+                align-items: center;
+                font-size: .6rem;
+                margin-bottom: .29rem;
+
+                .user_answer {
+                    color: #000;
+                    margin-right: .56rem;
+                }
+
+                .user_answer {
+                    color: #0d7a00;
+                }
+            }
+
+            .analysis_main {
+                font-size: .5rem;
+                color: #000;
+
+                .orange {
+                    color: #ff8800;
+                }
+
+                // 下划线
+                span {
+                    border-bottom: .02rem solid #000;
+                }
+
+                // 加粗 实线下划线
+                b {
+                    border-bottom: .02rem solid #000;
+
+                }
+
+                // 虚线下划线
+                i {
+                    font-style: normal;
+                    border-bottom: .02rem dashed #000;
+                }
+
+                // 控制空格
+                em {
+                    width: 1.3rem;
+                    display: inline-block;
+                }
+
+                // 斜体
+                p {
+                    display: inline;
+                    font-style: italic;
+                }
+
+                width: 16.72rem;
+
+            }
+        }
+    }
+
+
+    .tip_modal {
+        display: none;
+        width: 6.25rem;
+        height: 2.91rem;
+        border-radius: .3rem;
+        position: absolute;
+        left: 0;
+        right: 0;
+        margin: 0 auto;
+        top: 3.8rem;
+        background: rgba(0, 0, 0, .74);
+        font-size: .35rem;
+        color: #fff;
+        line-height: 2.91rem;
+        text-align: center;
+        animation: fadeOutNew .3s linear 1s;
+    }
+}

+ 190 - 0
src/stage/index/style/match/match.less

@@ -0,0 +1,190 @@
+.match {
+  .table {
+    position: fixed;
+    left: 2.2rem;
+    top: .42rem;
+    display: flex;
+    width: 5.16rem;
+    height: 1.02rem;
+
+    .match_title {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      width: 100%;
+      height: 100%;
+    }
+  }
+
+  .whole_match_focus {
+    background: url('assets/match/tab_left.png');
+    background-size: 5.16rem 1.02rem;
+    color: #fff;
+  }
+
+  .my_match_focus {
+    background: url('assets/match/tab_right.png');
+    background-size: 5.16rem 1.02rem;
+    color: #fff;
+  }
+
+  .match_two_icon {
+    position: fixed;
+    top: .42rem;
+    right: .3rem;
+
+    img {
+      width: 1.28rem;
+      height: 1.18rem;
+    }
+
+    .error_icon {
+      margin-right: 1rem;
+    }
+
+    .ranking_icon {
+      width: .95rem;
+    }
+  }
+
+  .match_container {
+    display: flex;
+    position: absolute;
+    left: 0;
+    top: 1.82rem;
+    width: 100%;
+    overflow-x: scroll;
+    padding-left: .3rem;
+
+    .place-right {
+      flex-shrink: 0;
+      display: flex;
+      width: 0.3rem;
+      height: 1rem;
+    }
+
+    .match_container_children {
+      flex-shrink: 0;
+      display: flex;
+      margin-right: .69rem;
+
+      .match_container_icon {
+        width: 2.17rem;
+        height: 6.08rem;
+        display: block;
+        margin-left: 1rem;
+      }
+
+      .match_container_detail_box {
+        display: flex;
+        flex-direction: column;
+      }
+
+      .match_container_detail {
+        display: flex;
+        align-items: center;
+        width: 9.63rem;
+        height: 3.9rem;
+        background: rgba(153, 153, 153, 0.6);
+        border-radius: .36rem;
+        padding: 0 .21rem;
+        margin-bottom: .6rem;
+
+        .img_icon {
+          width: 4.24rem;
+          height: 3.15rem;
+          margin-left: .28rem;
+          display: block;
+          border-radius: .4rem;
+        }
+
+        .course_match {
+          display: flex;
+          flex-direction: column;
+          margin-left: .36rem;
+
+          .course_match_title {
+            font-size: .45rem;
+            font-weight: 500;
+          }
+
+          .course_match_date {
+            font-size: .4rem;
+            font-weight: 400;
+          }
+
+          .course_match_sign_up {
+            font-size: .34rem;
+            margin-top: .3rem;
+          }
+
+          .course_btn {
+            display: flex;
+            color: #666;
+            font-size: .34rem;
+            font-weight: 600;
+            margin-top: .29rem;
+
+            .btn {
+              width: 2.02rem;
+              height: .81rem;
+              background: #fff;
+              border-radius: .4rem;
+              text-align: center;
+              line-height: .81rem;
+            }
+
+            .detail_btn {
+              margin-right: .36rem;
+            }
+          }
+        }
+      }
+
+      .history {
+        color: #fff;
+      }
+
+      .now {
+        background: #fff;
+
+        .course_match_title {
+          color: #3D3D3D;
+        }
+
+        .course_match_date {
+          color: #666;
+        }
+
+        .course_match_sign_up {
+          span {
+            color: #77CE6A;
+          }
+        }
+
+        .course_match .course_btn .detail_btn {
+          background: #fff;
+          border: .02rem solid #979797;
+        }
+
+        .course_match .course_btn .ranking_btn {
+          background: #FFA227;
+          color: #fff;
+        }
+      }
+    }
+
+  }
+
+  .rocket_icon {
+    position: fixed;
+    left: 16.49rem;
+    bottom: 0;
+    z-index: 9;
+
+    img {
+      width: 2.01rem;
+      height: 1.17rem;
+    }
+  }
+}

+ 55 - 0
src/stage/index/style/match/matchDialog.less

@@ -0,0 +1,55 @@
+.match-dialog {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: rgba(0, 0, 0, .5);
+  z-index: 1000;
+  .dialog-container {
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    width: 6.62rem;
+    height: 6.07rem;
+    .end {
+      position: absolute;
+      bottom: .9rem;
+      left: 50%;
+      width: 4.25rem;
+      height: .74rem;
+      transform: translateX(-50%);
+    }
+    .two-btn {
+      position: absolute;
+      bottom: .9rem;
+      left: 50%;
+      transform: translateX(-50%);
+      display: flex;
+    }
+    .two-btn div{
+      width: 2.73rem;
+      height: .74rem;
+    }
+    .quit {
+      margin-right: .46rem;
+    }
+    .report {
+      position: absolute;
+      bottom: .38rem;
+      left: 50%;
+      width: 4.21rem;
+      height: .82rem;
+      transform: translateX(-50%);
+    }
+    .report-container {
+      position: absolute;
+      top: 1.6rem;
+      left: 50%;
+      transform: translateX(-50%);
+      font-size: .44rem;
+      line-height: .86rem;
+    }
+  }
+}

+ 172 - 0
src/stage/index/style/match/ranking.less

@@ -0,0 +1,172 @@
+.ranking {
+  .ranking_title {
+    position: absolute;
+    top: .32rem;
+    left: 2.2rem;
+    font-size: .5rem;
+    font-weight: 500;
+    color: #fff;
+  }
+  .ranking_continer {
+    display: flex;
+    position: absolute;
+    top: 1.48rem;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    overflow-x: scroll;
+    .my_ranking_details,
+    .ranking_details {
+      position: relative;
+      flex-shrink: 0;
+      width: 6.2rem;
+      height: 9.01rem;
+      background: url('assets/match/total_ranking_bg.png');
+      background-size: 100% 100%;
+      margin-left: 1rem;
+      padding-top: 1.07rem;
+      box-sizing: border-box;
+      overflow: hidden;
+      .ranking_details_title {
+        position: absolute;
+        top: .1rem;
+        left: 50%;
+        transform: translateX(-50%);
+        text-align: center;
+        white-space: nowrap;
+        h3 {
+          font-size: .38rem;
+          font-weight: 500;
+          color: #fff;
+        }
+        span {
+          font-size: .28rem;
+          color: #666;
+        }
+      }
+      .ranking_item {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        width: 6.2rem;
+        height: .66rem;
+        padding: 0 .38rem;
+        box-sizing: border-box;
+        .ranking_left_item,
+        .ranking_right_item {
+          display: flex;
+          align-items: center;
+          height: .52rem;
+          width: 1rem;
+          justify-content: center;
+        }
+        .ranking_head_title {
+          font-weight: bold;
+          color: #333333;
+          font-size: .29rem;
+        }
+        .ranking_number {
+          width: .5rem;
+          height: .5rem;
+          text-align: center;
+          line-height: .5rem;
+          font-size: .34rem;
+          color: #FF8109;
+        }
+        .ranking_name {
+          font-size: .28rem;
+          color: #333333;
+          width: 3rem;
+        }
+        .ranking_right_item {
+          font-size: .28rem;
+          color: #333333;
+        }
+      }
+      .my_ranking {
+        background: #FFDF5A;
+        border-radius: 0 0 .37rem .37rem;
+        position: absolute;
+        bottom: 0;
+      }
+      .item_no_data {
+        position: absolute;
+        left: 50%;
+        top: 50%;
+        transform: translate(-50%, -50%);
+        width: 2.97rem;
+        height: 3.39rem;
+      }
+    }
+    .my_ranking_details {
+      width: 6.94rem;
+      height: 9.14rem;
+      background: #FFFFFF;
+      border-radius: .37rem;
+      padding-top: 0;
+      .ranking_item {
+        padding-top: .16rem;
+        width: 6.94rem;
+        height: .82rem;
+      }
+    }
+    .no_data {
+      position: absolute;
+      left: 50%;
+      top: .7rem;
+      transform: translateX(-50%);
+      width: 12.09rem;
+      height: 7.07rem;
+      background: #fff;
+      border-radius: .3rem;
+      img {
+        position: absolute;
+        left: 50%;
+        top: 50%;
+        transform: translate(-50%, -50%);
+        width: 2.97rem;
+        height: 3.39rem;
+      }
+    }
+  }
+  .singl_my_ranking {
+    position: absolute;
+    top: .23rem;
+    right: .42rem;
+    display: flex;
+    align-items: center;
+    justify-content: space-around;
+    width: 7.23rem;
+    height: .88rem;
+    background: #FFDF5A;
+    border-radius: .38rem;
+    padding: 0 .48rem;
+    box-sizing: border-box;
+    .ranking_left_item,
+    .ranking_right_item {
+      display: flex;
+      align-items: center;
+      height: .52rem;
+    }
+    .ranking_number {
+      width: .37rem;
+      height: .37rem;
+      text-align: center;
+      line-height: .37rem;
+      font-size: .4rem;
+      color: #FF8109;
+    }
+    .ranking_name {
+      font-size: .36rem;
+      color: #616161;
+      margin-right: .3rem;
+    }
+    .ranking_right_item {
+      font-size: .36rem;
+      color: #616161;
+    }
+    .ranking_correct {
+      margin-right: .49rem;
+    }
+  }
+}

+ 182 - 0
src/util/efunRequest.js

@@ -0,0 +1,182 @@
+import utils from "./utils";
+
+//http 网络请求封装类(和业务相关的代码请不要写进这个类中)
+// import UserDataStorage from './UserDataStorage';
+
+class efunRequest {
+
+    constructor() {
+        this._url = '';
+        this._params = {};
+        this._data = {};
+        this._header = {};
+        this.xmlHTTPRequest = null;
+        this._onSuccess = null;
+        this._onFailed = null;
+        this._deserialize = null;
+    }
+
+    request(method) {
+
+        const paramStr = () => {
+            if (!this._params) {
+                return ''
+            }
+            var pstr = '';
+            for (let key in this._params) {
+                pstr += `${key}=${this._params[key]}&`;
+            }
+            return pstr.slice(0, -1);
+        }
+
+        this.xmlHTTPRequest = new XMLHttpRequest();
+        this.xmlHTTPRequest.open(method, this._url + '?' + paramStr(), true);
+        if (!this._header['Content-Type']) {
+            this.xmlHTTPRequest.setRequestHeader('Content-Type', 'application/' + (method == 'POST' || method == 'PUT' ? 'json' : 'text'));
+        }
+        for (let key in this._header) {
+            this.xmlHTTPRequest.setRequestHeader(key, this._header[key]);
+        }
+
+        this.xmlHTTPRequest.onreadystatechange = () => {
+            this.whenResponse();
+        }
+
+        var sendBody = null;
+        if (this._data && (method == 'POST' || method == 'PUT')) {
+            if (this._header['Content-Type'] && this._header['Content-Type'].indexOf('application/x-www-form-urlencoded;') != -1 ) {
+                var arr = new Array();
+                var i = 0;
+                for (var attr in this._data) {
+                    arr[i] = encodeURIComponent(attr) + '=' + encodeURIComponent(this._data[attr]);
+                    i++;
+                }
+                sendBody = '&' + arr.join('&');
+            } else {
+                sendBody = JSON.stringify(this._data);
+            }
+        }
+        this.xmlHTTPRequest.send(sendBody);
+        return this;
+    }
+
+    get() {
+        this.request('GET');
+        return this;
+    }
+
+    post() {
+        this.request('POST');
+        return this;
+    }
+
+    put() {
+        this.request('PUT');
+        return this;
+    }
+
+    delete() {
+        this.request('DELETE');
+        return this;
+    }
+
+    whenResponse() {
+        if (this.xmlHTTPRequest.readyState != 4) {
+            return;
+        }
+
+        if (this._deserialize) {
+            this._deserialize(this.xmlHTTPRequest.responseText, this);
+            return;
+        }
+
+        let cb = this.xmlHTTPRequest.status == 200 ? this._onSuccess : this._onFailed;
+        if (!cb) {
+            return;
+        }
+        cb(this.xmlHTTPRequest.status, this.xmlHTTPRequest.responseText);
+    }
+
+    success(callback) {
+        this._onSuccess = callback;
+        return this;
+    }
+
+    fail(callback) {
+        this._onFailed = callback;
+        return this;
+    }
+
+    header(obj, rewrite) {
+        if (rewrite) {
+            this._header = obj;
+        } else {
+            for (let key in obj) {
+                this._header[key] = obj[key];
+            }
+        }
+        return this;
+    }
+
+    params(obj) {
+        this._params = obj;
+        return this;
+    }
+
+    data(obj) {
+        this._data = obj;
+        return this;
+    }
+
+    url(str) {
+        this._url = str;
+        return this;
+    }
+
+    responseDeserialize(func) {
+        this._deserialize = func;
+        return this;
+    }
+
+    static getHttpRequest() {
+        let request = new efunRequest();
+        if (utils.readLocalstorage('uid')) {
+            request.header({
+                'uid': utils.readLocalstorage('uid')
+            });
+        }
+        request.responseDeserialize(function (res, req) {
+            function callFailed(data) {
+                if (req._onFailed) {
+                    req._onFailed(data);
+                }
+            }
+
+            function callSuccess(data) {
+                if (req._onSuccess) {
+                    req._onSuccess(data);
+                }
+            }
+
+            let rObj = null;
+            try {
+                rObj = JSON.parse(res);
+            } catch (e) {
+                callFailed(e);
+                return;
+            }
+            if (req.xmlHTTPRequest.status != 200) {
+                callFailed(rObj);
+                return;
+            }
+            if (!rObj.success) {
+                callFailed(rObj);
+                return;
+            }
+            callSuccess(rObj);
+        });
+        return request;
+    }
+}
+
+export default efunRequest;

+ 98 - 0
src/util/login.js

@@ -0,0 +1,98 @@
+import {
+  writeLocalStorage,
+  readLocalstorage,
+  GetQueryString,
+  $
+} from './utils'
+import {
+  mobileLogin,
+  partnerLogin
+} from '../service/course'
+// 兼容老版本
+function loginPage() {
+  // this.showScene(require('../stage/index/scene/course/ChineseMathScene'));
+}
+export function isLogin() {
+  if (readLocalstorage('uid') === '123456') {
+    if (GetQueryString('appCode') === '2013') { // 小爱音箱
+      if (typeof efunboxJS != "undefined") {
+        efunboxJS.showPhoneNumberLogin();
+      } else {
+        console.log('小艾登录没有安卓方法')
+      }
+    } else {
+      newLogin()
+    }
+  }
+}
+// 安卓授权登陆
+export function getPhoneLogin(phone) {
+  if (phone) this.renderLogin(phone);
+}
+// 华为登录
+export function huaweiLogin(data) {
+  const channel = GetQueryString('appCode')
+  const deviceCode = GetQueryString('uuid')
+  partnerLogin({
+    channel,
+    deviceCode,
+    nikeName: data.displayName,
+    openId: data.openId
+  }).success(res => {
+    writeLocalStorage('uid', res.data.uid);
+    writeLocalStorage('channelCode', res.data.channelCode);
+    writeLocalStorage('nickName', res.data.nickName);
+    writeLocalStorage('mobile', res.data.mobile);
+    writeLocalStorage('userCreateTime', res.data.gmtCreated);
+    console.log(123123, res.data.grade)
+    if (res.data.grade) {
+      writeLocalStorage('class', res.data.grade)
+    } else {
+      writeLocalStorage('class', '')
+    }
+    loginPage.call(this, '')
+  })
+}
+// 安卓手机号登陆成功
+export function phoneSuccess(res) {
+  const data = res.data;
+  console.log('安卓登陆成功', data)
+  writeLocalStorage('uid', data.uid);
+  writeLocalStorage('channelCode', data.channelCode);
+  writeLocalStorage('nickName', data.nickName);
+  writeLocalStorage('mobile', data.mobile);
+  writeLocalStorage('userCreateTime', data.gmtCreated);
+  if (res.data.grade) {
+    writeLocalStorage('class', res.data.grade)
+  } else {
+    writeLocalStorage('class', '')
+  }
+  loginPage.call(this, '')
+}
+// 手机号登录调用接口
+export function renderLogin(mobileNo) {
+  const deviceCode = GetQueryString('uuid')
+  const channel = GetQueryString('appCode')
+  // const self = this;
+  mobileLogin({
+    mobileNo,
+    deviceCode,
+    channel
+  }).success(res => {
+    console.log(res)
+    writeLocalStorage('uid', res.data.uid);
+    writeLocalStorage('channelCode', res.data.channelCode);
+    writeLocalStorage('nickName', res.data.nickName);
+    writeLocalStorage('mobile', res.data.mobile);
+    writeLocalStorage('userCreateTime', res.data.gmtCreated);
+    console.log(123123, res.data.grade)
+    if (res.data.grade) {
+      writeLocalStorage('class', res.data.grade)
+    } else {
+      writeLocalStorage('class', '')
+    }
+    loginPage.call(this, '')
+  }).fail(error => {
+    console.log(error)
+  })
+}

+ 181 - 0
src/util/utils.js

@@ -0,0 +1,181 @@
+function addAero(num) {
+    return num * 1 < 10 ? '0' + num : num;
+}
+export default class utils {
+    //获取地址栏参数拿到uuid
+    static GetQueryString(name) {
+        var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
+        var r = window.location.search.substr(1).match(reg);
+        if (r != null) return unescape(r[2]);
+        return null;
+    }
+
+    static setHTMLBackground(setImage, bgColor) {
+        let rootHtml = document.getElementById('rootHtml');
+        if (bgColor) {
+            rootHtml.style.background = setImage;
+        } else {
+            rootHtml.style.background = `url('${setImage}') 0% 0% / 100% 100%  no-repeat`;
+            rootHtml.style.backgroundSize = 'cover';
+        }
+        // if (!delId) return;
+        // document.getElementById(delId).style.background = 'rgba(0,0,0,0)';
+        // document.getElementById(delId).style.backgroundColor = 'transparent';
+    }
+    static writeLocalStorage(name, str) {
+        if (window.localStorage) {
+            localStorage.setItem(name, str)
+        } else {
+            alert("浏览器支持localStorage");
+        }
+    }
+    static readLocalstorage(name) {
+        if (window.localStorage) {
+            return localStorage.getItem(name)
+        } else {
+            alert("浏览器支持localStorage");
+        }
+    }
+    // static locationName(name) {
+    //     return new URLSearchParams(location.search).get(name)
+    // }
+    //时间戳转为时间
+    static formatDate(time, flag) {
+        const t = new Date(time * 1);
+        const tf = function (i) {
+            return (i < 10 ? '0' : '') + i
+        };
+        const year = t.getFullYear();
+        const month = tf(t.getMonth() + 1);
+        const day = tf(t.getDate());
+        const hour = tf(t.getHours());
+        const minute = tf(t.getMinutes());
+        //console.log( month + '月' + day + '日' + hour + ':' + minute);
+        if (flag) {
+            return year + '年' + month + '月' + day + '日 ' + hour + ':' + minute;
+        } else {
+            return year + '年' + month + '月' + day + '日';
+        }
+    }
+    // 获得当前时间
+    static getDate() {
+        return new Date()
+    }
+    // 生成星星数组
+    static createStar(allStar, starNum) {
+        /**
+         * allStar星星展示首先确定有多少颗星
+         * starNum 多少颗星星是亮的
+         * starArr 星星数组,1是亮的,0是不亮的
+         */
+        let starArr = [];
+        for (let i = 0; i < allStar; i++) {
+            starArr.push(1)
+        }
+        for (let i = 0; i < allStar; i++) {
+            starArr.push(0)
+        }
+        const length = starArr.length;
+        const newStarArr = starArr.slice(allStar - starNum, length - starNum)
+        return newStarArr
+    }
+    // 生成星星dom元素把星星数组传进来
+    static renderStar(starArr, fileName) {
+        let starStr = ''
+        const file = fileName || 'img'
+        starArr.forEach(item => {
+            const starimg = item === 1 ? `assets/${file}/star1_1.png` : `assets/${file}/star1_2.png`;
+            starStr += `
+          <div class="star-item">
+              <img src="${ starimg }" alt="">
+          </div>
+          `
+        })
+        return starStr
+    }
+    // 生成日历年
+    static createdYear(start, end) {
+        const yearNum = end - start;
+        let str = ''
+        for (var i = 0; i <= yearNum; i++) {
+            str += `
+                <li class="option-item" data-type="year"  fe-role="Widget">${start + i}</li>
+            `
+        }
+        return str
+    }
+    // 生成月
+    static createdMonth() {
+        let str = ''
+        for (var i = 1; i <= 12; i++) {
+            str += `
+                <li class="option-item" data-type="month"  fe-role="Widget">${i < 10 ? '0' + i : i}</li>
+            `
+        }
+        return str
+    }
+    // 生成日
+    static createdDay(dayNum) {
+        let str = ''
+        for (var i = 1; i <= dayNum; i++) {
+            str += `
+                <li class="option-item" data-type="day"  fe-role="Widget">${i < 10 ? '0' + i : i}</li>
+            `
+        }
+        return str
+    }
+    // 元素选中
+    static $(name) {
+        return window.document.querySelector.call(window.document, name)
+    }
+    // 设置背景图
+    static setBg(imgOrColor) {
+        if (/png|jpg|gif/.test(imgOrColor)) {
+            document.body.style.background = `url(${imgOrColor}) 0% 0% / 100% 100% no-repeat`
+        } else {
+            document.body.style.background = `${imgOrColor}`
+        }
+    }
+    // 全局处理返回按钮
+    static setBack(hide, top) {
+        top = top || '.72'
+        document.querySelector('.back').style.display =  hide ? 'none' : 'block';
+        document.querySelector('.back').style.top = top + 'rem'
+    }
+    // 秒转分和秒
+    static s_to_ms(time) {
+        const _time = Math.floor(time)
+        const s = (_time % 60) < 10 ? '0' + _time % 60 : _time % 60;
+        const m = Math.floor(_time / 60) < 10 ? '0' + Math.floor(_time / 60) : Math.floor(_time / 60)
+        return m + ':' + s
+    }
+    // 解密视频
+    static DecryptEcb(keyword, secretKey) {
+        try {
+            const key = CryptoJS.enc.Utf8.parse(secretKey)
+            // 转换base64
+            const encryptedData = CryptoJS.enc.Base64.parse(keyword)
+            const encryptedHexStr = CryptoJS.enc.Hex.parse(encryptedData.toString())
+            const encryptedBase64Str = CryptoJS.enc.Base64.stringify(encryptedHexStr)
+            const decryptedData = CryptoJS.AES.decrypt(encryptedBase64Str, key, {
+                mode: CryptoJS.mode.ECB,
+                padding: CryptoJS.pad.Pkcs7
+            })
+
+            const decryptedStr = decryptedData.toString(CryptoJS.enc.Utf8)
+            console.log("解密后:", decryptedStr) // 解密后:onlystar
+            return decryptedStr.toString()
+        } catch (error) {
+            console.log(error)
+            return ''
+        }
+    }
+    static countSecondTime(endtime) {
+        var nowtime = new Date(); //获取当前时间
+        var lefttime = endtime - nowtime.getTime(), //距离结束时间的毫秒数
+            lefth = addAero(Math.floor(lefttime / (1000 * 60 * 60) % 24)), //计算小时数
+            leftm = addAero(Math.floor(lefttime / (1000 * 60) % 60)), //计算分钟数
+            lefts = addAero(Math.floor(lefttime / 1000 % 60)); //计算秒数
+        return [lefth,leftm,lefts] //返回倒计时的字符串
+    }
+}