Pārlūkot izejas kodu

chore:换电脑

周杰伦 4 gadi atpakaļ
vecāks
revīzija
9534aaa336
97 mainītis faili ar 3393 papildinājumiem un 189 dzēšanām
  1. 48 8
      project.config.json
  2. 21 0
      src/api/Home.ts
  3. 37 0
      src/api/My.ts
  4. 55 0
      src/api/QuestionCard.ts
  5. 10 0
      src/api/Rank.ts
  6. 11 0
      src/api/Report.ts
  7. 6 1
      src/app.config.ts
  8. BIN
      src/assets/Entrance/lockBg.png
  9. BIN
      src/assets/Entrance/lockbtn.png
  10. BIN
      src/assets/Entrance/star2.png
  11. BIN
      src/assets/Entrance/star3.png
  12. BIN
      src/assets/Evaluation/topic-bg.png
  13. BIN
      src/assets/GetBag/bag-logo.png
  14. BIN
      src/assets/GetBag/bg.png
  15. BIN
      src/assets/GetBag/get-btn.png
  16. BIN
      src/assets/GetBag/shop.png
  17. BIN
      src/assets/GetBag/xQrcode.png
  18. BIN
      src/assets/My/flag.png
  19. BIN
      src/assets/Report/bg.png
  20. BIN
      src/assets/Report/btn.png
  21. BIN
      src/assets/Report/cloud.png
  22. BIN
      src/assets/Report/collect.png
  23. BIN
      src/assets/Report/dog.png
  24. BIN
      src/assets/Report/poremote.png
  25. BIN
      src/assets/Report/ratebg.png
  26. BIN
      src/assets/Report/rect.png
  27. BIN
      src/assets/Report/star.png
  28. BIN
      src/assets/Report/stargrey.png
  29. BIN
      src/assets/Report/watch.png
  30. BIN
      src/assets/TopicPage/Topicbg.png
  31. BIN
      src/assets/TopicPage/again.png
  32. BIN
      src/assets/TopicPage/befated.png
  33. BIN
      src/assets/TopicPage/bg.png
  34. BIN
      src/assets/TopicPage/correct.png
  35. BIN
      src/assets/TopicPage/dog.png
  36. BIN
      src/assets/TopicPage/dot.png
  37. BIN
      src/assets/TopicPage/error.png
  38. BIN
      src/assets/TopicPage/face.png
  39. BIN
      src/assets/TopicPage/logo.png
  40. BIN
      src/assets/TopicPage/next.png
  41. BIN
      src/assets/TopicPage/ok.png
  42. BIN
      src/assets/TopicPage/pass.png
  43. BIN
      src/assets/TopicPage/promote.png
  44. BIN
      src/assets/TopicPage/quit.png
  45. BIN
      src/assets/TopicPage/second.png
  46. BIN
      src/assets/TopicPage/star0.png
  47. BIN
      src/assets/TopicPage/star1.png
  48. BIN
      src/assets/TopicPage/star2.png
  49. BIN
      src/assets/TopicPage/star3.png
  50. BIN
      src/assets/TopicPage/star4.png
  51. BIN
      src/assets/TopicPage/submit.png
  52. BIN
      src/assets/TopicPage/time-over.png
  53. BIN
      src/assets/TopicPage/trumpet.png
  54. BIN
      src/assets/TopicPage/watch.png
  55. BIN
      src/assets/index/index1-X.png
  56. BIN
      src/assets/index/index1.png
  57. BIN
      src/assets/index/ruler.png
  58. 2 1
      src/components/NavBar/index.scss
  59. 11 5
      src/components/NavBar/index.tsx
  60. 64 0
      src/hook/index.ts
  61. 14 0
      src/pages/Entrance/index.scss
  62. 111 13
      src/pages/Entrance/index.tsx
  63. 0 4
      src/pages/Evaluation/index.config.ts
  64. 0 35
      src/pages/Evaluation/index.scss
  65. 0 28
      src/pages/Evaluation/index.tsx
  66. 2 1
      src/pages/GetBag/index.config.ts
  67. 69 0
      src/pages/GetBag/index.scss
  68. 42 3
      src/pages/GetBag/index.tsx
  69. 4 0
      src/pages/GetCourse/index.config.ts
  70. 11 0
      src/pages/GetCourse/index.scss
  71. 24 0
      src/pages/GetCourse/index.tsx
  72. 69 8
      src/pages/My/index.scss
  73. 234 42
      src/pages/My/index.tsx
  74. 3 0
      src/pages/Rank/index.config.ts
  75. 314 0
      src/pages/Rank/index.scss
  76. 175 0
      src/pages/Rank/index.tsx
  77. 4 0
      src/pages/Report/index.config.ts
  78. 196 0
      src/pages/Report/index.scss
  79. 283 0
      src/pages/Report/index.tsx
  80. 4 0
      src/pages/TopicPage/Analysis.config.ts
  81. 152 0
      src/pages/TopicPage/Analysis.scss
  82. 302 0
      src/pages/TopicPage/Analysis.tsx
  83. 32 0
      src/pages/TopicPage/component/BgPanel.scss
  84. 30 0
      src/pages/TopicPage/component/BgPanel.tsx
  85. 141 0
      src/pages/TopicPage/component/Modal.scss
  86. 140 0
      src/pages/TopicPage/component/Modal.tsx
  87. 37 0
      src/pages/TopicPage/component/TimeCom.scss
  88. 23 0
      src/pages/TopicPage/component/TimeCom.tsx
  89. 4 0
      src/pages/TopicPage/index.config.ts
  90. 111 0
      src/pages/TopicPage/index.scss
  91. 411 0
      src/pages/TopicPage/index.tsx
  92. 4 0
      src/pages/WebView/index.config.ts
  93. 7 0
      src/pages/WebView/index.tsx
  94. 29 0
      src/pages/index/index.scss
  95. 66 10
      src/pages/index/index.tsx
  96. 5 1
      src/service/config.ts
  97. 75 29
      src/service/request.ts

+ 48 - 8
project.config.json

@@ -2,17 +2,17 @@
   "miniprogramRoot": "dist/",
   "projectname": "agent-auth-react",
   "description": "",
-  "appid": "wxe57e116e4fe55532",
+  "appid": "wx079600d756c84edc",
   "setting": {
-    "urlCheck": true,
-    "es6": false,
+    "urlCheck": false,
+    "es6": true,
     "enhance": false,
-    "postcss": false,
+    "postcss": true,
     "preloadBackgroundData": false,
-    "minified": false,
+    "minified": true,
     "newFeature": true,
     "coverView": true,
-    "nodeModules": false,
+    "nodeModules": true,
     "autoAudits": false,
     "showShadowRootInWxmlPanel": false,
     "scopeDataCheck": false,
@@ -65,20 +65,60 @@
     "miniprogram": {
       "list": [
         {
+          "id": -1,
           "name": "pages/Entrance/index",
           "pathName": "pages/Entrance/index",
           "query": "",
           "scene": null
         },
         {
+          "id": -1,
           "name": "pages/My/index",
           "pathName": "pages/My/index",
           "query": "",
           "scene": null
         },
         {
-          "name": "pages/Evaluation/index",
-          "pathName": "pages/Evaluation/index",
+          "name": "pages/Report/index",
+          "pathName": "pages/Report/index",
+          "query": "cardIdList=%5B%7B%22key%22%3A%221%22%2C%22id%22%3A%221364912927351578625%22%7D%5D",
+          "scene": null
+        },
+        {
+          "name": "pages/TopicPage/index",
+          "pathName": "pages/TopicPage/index",
+          "query": "pointState=2",
+          "scene": null
+        },
+        {
+          "id": -1,
+          "name": "pages/Rank/index",
+          "pathName": "pages/Rank/index",
+          "query": "",
+          "scene": null
+        },
+        {
+          "name": "pages/TopicPage/Analysis",
+          "pathName": "pages/TopicPage/Analysis",
+          "query": "ids=%5B%7B%22key%22%3A%221%22%2C%22id%22%3A%221364912927351578625%22%7D%5D",
+          "scene": null
+        },
+        {
+          "name": "pages/GetBag/index",
+          "pathName": "pages/GetBag/index",
+          "query": "ids=%5B%221362734503036841986%22%2C%221362734486859411457%22%5D",
+          "scene": null
+        },
+        {
+          "name": "pages/GetCourse/index",
+          "pathName": "pages/GetCourse/index",
+          "query": "ids=%5B%221362734503036841986%22%2C%221362734486859411457%22%5D",
+          "scene": null
+        },
+        {
+          "name": "pages/WebView/index",
+          "pathName": "pages/WebView/index",
+          "query": "cardIdList=%5B%7B%22key%22%3A1%2C%22id%22%3A%221363768085251371010%22%7D%5D",
           "scene": null
         }
       ]

+ 21 - 0
src/api/Home.ts

@@ -0,0 +1,21 @@
+import { request } from '../service/request'
+
+/**
+ * 是否第一次闯关
+ * @return {boolean} 
+ * true 是第一次闯关 
+ * false 不是第一次闯关 
+ */ 
+export const isFirstState = () => {
+    return request({
+        url: '/index/firstState'
+    })
+}
+
+
+// 获取关卡
+export const getPointCard = () => {
+    return request({
+        url: '/index/pointCard'
+    })
+}

+ 37 - 0
src/api/My.ts

@@ -0,0 +1,37 @@
+import { request } from '../service/request'
+
+/**
+ * @description 个人中心- 历史做题记录 
+ */ 
+export const getPointHisory = (cardId: any) => {
+    return request({
+        url: `/auth/history?cardId=${cardId}`
+    })
+}
+
+/**
+ * @description 个人中心- 排行榜
+ */ 
+export const getRank = (cardId: any) => {
+    return request({
+        url: `/auth/rank?cardId=${cardId}`,
+    })
+}
+
+
+/**
+ * @description 用户登录
+ */
+
+ type loginParmasType = {
+    avatarUrl?: string,
+    jsCode: string,
+    nickName?: string
+ }
+
+export const login = (params: loginParmasType) => {
+    return request({
+        url: `/auth/login`,
+        data: params
+    })
+}

+ 55 - 0
src/api/QuestionCard.ts

@@ -0,0 +1,55 @@
+
+import { request } from '../service/request'
+
+/**
+ * @param { string } pointState  模式id 1 ~ 8 张题卡
+ */ 
+export const getQuestionCard = (pointState: string) => {
+    return request({
+        url: `/questionCard/${pointState}`
+    })
+}
+
+
+/**
+ * 提交题卡
+ * 
+ * @param {params} 参数 
+ */ 
+
+type optionAnswersType = {
+    color: string,
+    optionId: string,
+    yourAnswer: string
+}
+
+type postQuestionCardParams = {
+    cardId: string,
+    takeTime: number,
+    optionAnswers: optionAnswersType[]
+}
+
+
+export const postQuestionCard = (params: postQuestionCardParams) => {
+    return request({
+        url: `/questionCard/answer`,
+        method: 'POST',
+        data: params
+    })
+}
+
+
+
+
+/**
+ * 
+ * @description  错题解析
+ * @param ids 题卡id的集合 
+ */
+
+
+export const questionAnalysis = (ids: string[]) => {
+    return request({
+        url: `/score/analysis?ids=${ids}`
+    })
+}

+ 10 - 0
src/api/Rank.ts

@@ -0,0 +1,10 @@
+import { request } from '../service/request'
+
+
+export const getRank = () => {
+    return request({
+        url: '/gamecontest/location'
+    })
+}
+
+

+ 11 - 0
src/api/Report.ts

@@ -0,0 +1,11 @@
+
+import { request } from '../service/request'
+
+/**
+ * @param { string[] } ids 本次游戏的题卡id集合
+ */ 
+export const getReport = (ids: string[]) => {
+    return request({
+        url: `/score/report?ids=${ids}`,
+    })
+}

+ 6 - 1
src/app.config.ts

@@ -3,7 +3,12 @@ export default {
     'pages/index/index',
     'pages/Entrance/index',
     'pages/My/index',
-    'pages/Evaluation/index',
+    'pages/Report/index',
+    'pages/TopicPage/index',
+    'pages/TopicPage/Analysis',
+    'pages/GetBag/index',
+    'pages/GetCourse/index',
+    'pages/WebView/index',
   ],
   window: {
     backgroundTextStyle: 'light',

BIN
src/assets/Entrance/lockBg.png


BIN
src/assets/Entrance/lockbtn.png


BIN
src/assets/Entrance/star2.png


BIN
src/assets/Entrance/star3.png


BIN
src/assets/Evaluation/topic-bg.png


BIN
src/assets/GetBag/bag-logo.png


BIN
src/assets/GetBag/bg.png


BIN
src/assets/GetBag/get-btn.png


BIN
src/assets/GetBag/shop.png


BIN
src/assets/GetBag/xQrcode.png


BIN
src/assets/My/flag.png


BIN
src/assets/Report/bg.png


BIN
src/assets/Report/btn.png


BIN
src/assets/Report/cloud.png


BIN
src/assets/Report/collect.png


BIN
src/assets/Report/dog.png


BIN
src/assets/Report/poremote.png


BIN
src/assets/Report/ratebg.png


BIN
src/assets/Report/rect.png


BIN
src/assets/Report/star.png


BIN
src/assets/Report/stargrey.png


BIN
src/assets/Report/watch.png


BIN
src/assets/TopicPage/Topicbg.png


BIN
src/assets/TopicPage/again.png


BIN
src/assets/TopicPage/befated.png


BIN
src/assets/TopicPage/bg.png


BIN
src/assets/TopicPage/correct.png


BIN
src/assets/TopicPage/dog.png


BIN
src/assets/TopicPage/dot.png


BIN
src/assets/TopicPage/error.png


BIN
src/assets/TopicPage/face.png


BIN
src/assets/TopicPage/logo.png


BIN
src/assets/TopicPage/next.png


BIN
src/assets/TopicPage/ok.png


BIN
src/assets/TopicPage/pass.png


BIN
src/assets/TopicPage/promote.png


BIN
src/assets/TopicPage/quit.png


BIN
src/assets/TopicPage/second.png


BIN
src/assets/TopicPage/star0.png


BIN
src/assets/TopicPage/star1.png


BIN
src/assets/TopicPage/star2.png


BIN
src/assets/TopicPage/star3.png


BIN
src/assets/TopicPage/star4.png


BIN
src/assets/TopicPage/submit.png


BIN
src/assets/TopicPage/time-over.png


BIN
src/assets/TopicPage/trumpet.png


BIN
src/assets/TopicPage/watch.png


BIN
src/assets/index/index1-X.png


BIN
src/assets/index/index1.png


BIN
src/assets/index/ruler.png


+ 2 - 1
src/components/NavBar/index.scss

@@ -1,4 +1,5 @@
 .custom-navbar {
+    position: absolute;
  .empty {
 
  }
@@ -11,7 +12,7 @@
      .icon {
          position: absolute;
          top: 50%;
-         left: 5%;
+         left: 48px;
          transform: translate(-50%, -50%);
      }
  }

+ 11 - 5
src/components/NavBar/index.tsx

@@ -22,6 +22,8 @@ const NavBar: React.FC<NavBar.NavBarProps> = (
     const getNavBarHeight = (): void => {
         Taro.getSystemInfo({
             success: function (res) {
+                console.log(res.statusBarHeight, 'res.statusBarHeight');
+                
                 setStatusBarHeight(res.statusBarHeight)
             }
         });
@@ -31,18 +33,22 @@ const NavBar: React.FC<NavBar.NavBarProps> = (
 
     useEffect( () => {
         getNavBarHeight()
-        text().then( res => {
-            console.log(res, 'res');
-            
-        })
+        
     }, [])
 
+    const goPage = () => {
+        Taro.navigateBack({
+             delta: 1
+        });
+    }
+
+
     return (
         <view className='custom-navbar' style={{ background }}>
             <view className='empty' style={{height: statusBarHeight + 2 + 'px'}} />
             <view className='NavBar' style={{height:  (boundingData.top - statusBarHeight) * 2 + boundingData.height + 'px' }}>
                <text> { title } </text>
-               <view className='icon'>
+               <view className='icon' onClick={goPage}>
                     {icon}
                </view>
             </view>

+ 64 - 0
src/hook/index.ts

@@ -0,0 +1,64 @@
+import { useEffect, useState, useRef } from 'react'
+
+/**
+ * @date 2021/2/23
+ * 
+ * @description 同步useState 
+ * 
+ * @param { T } params 参数 
+ * 
+ * @return { Array }
+ * 
+ * @example 
+ *  
+ *  const [name , setName] = useStateSync<string>('怡宝')
+ *  
+ *  useEffect( () => {
+ *      setName('农夫山泉', (res) => {
+ *         console.log(res)  // 农夫山泉
+ *      })
+ *  }, [])
+ * 
+ */
+
+
+export const useStateSync = <T>(params: T): Array<any> => {
+
+    const cbRef = useRef((_params: T) => {})
+
+    const [ state, setState ] = useState<T>(params)
+
+    useEffect( () => {
+        cbRef.current &&  cbRef.current(state)
+    }, [state] )
+
+ 
+    return [state,  (val: T, callback: (_params: T) => void): void => {
+        cbRef.current = callback;
+        setState(val);
+    }]
+}
+
+ 
+
+
+
+export const useInterval = (callback, delay) => {
+  const savedCallback = useRef();
+
+  // Remember the latest callback.
+  useEffect(() => {
+    savedCallback.current = callback;
+  });
+
+  // Set up the interval.
+  useEffect(() => {
+    function tick() {
+      savedCallback.current();
+    }
+    if (delay !== null) {
+      let id = setInterval(tick, delay);
+      return () => clearInterval(id);
+    }
+  }, [delay]);
+}

+ 14 - 0
src/pages/Entrance/index.scss

@@ -73,5 +73,19 @@
             margin: 0 auto;
             margin-top: 64px;
         }
+
+        .lock-bg {
+            width: 506px;
+            height: 612px;
+        }
+
+        .lock-btn {
+            width: 374px;
+            height: 70px;
+            position: absolute;
+            bottom: 78px;
+            left: 50%;
+            transform: translateX(-50%);
+        }
     }
 }

+ 111 - 13
src/pages/Entrance/index.tsx

@@ -1,11 +1,26 @@
-import React, {useState} from 'react'
+import React, { useState, useEffect} from 'react'
 
 import { View, Image } from '@tarojs/components'
 
 import Taro from '@tarojs/taro'
 
+import { isFirstState, getPointCard } from '../../api/Home'
+
 import './index.scss'
 
+type pointCardType =  {
+    id: string,
+    lock: boolean,
+    label: any,
+    title: string,
+    pointState: number,
+    [key: string]: any
+}
+
+type LockModal = {
+    closeLockModal: () => void
+}
+
 const entranceList = [
     {
         id: 0,
@@ -49,50 +64,133 @@ const entranceList = [
     }
 ]
 
+
+const LockModal: React.FC<LockModal> = ({closeLockModal}) => {
+    return (
+        <React.Fragment>
+        <View className='rule-pop'>
+                <View className='container'>
+                    <Image className='lock-bg' mode='aspectFill' src={require('../../assets/Entrance/lockBg.png')} ></Image>
+                    <Image className='lock-btn' onClick={() => closeLockModal()} src={require('../../assets/Entrance/lockbtn.png')} ></Image>
+                </View>
+            </View>
+       </React.Fragment>
+    )
+}
+
 const Enteance: React.FC = () => {
 
     const [visible, setVisible] = useState<boolean>(false)
 
-    // 去我的页面
+    const [ firstSate, setFirstState ] = useState<boolean>(false)
+
+    const [ pointCard, setPointCard ] = useState<pointCardType[]>([])
+
+    const [ curSite, setCurSite ] = useState<number>(0)
+
+    useEffect( () => {
+        IsFirstState()
+    }, [])
 
+    Taro.useDidShow ( () => {
+        GetPointCard()
+        Taro.removeStorageSync('cardIdList');
+    })
+  
+    // 是否第一次闯关
+    const IsFirstState = async () => {
+       const data = await isFirstState()
+       console.log(data);
+       setFirstState(data)
+    }
+
+    // 获取关卡
+
+    const GetPointCard = async () => {
+        const data = await getPointCard()
+      
+        const assginData = data.map( item => {
+            return {
+                ...entranceList[item.pointState - 1],
+                ...item,
+            }
+        })
+        
+        setPointCard(assginData)
+        setCurSite( assginData.findIndex(item => !item.lock) - 1)
+    }
+
+
+    // 去我的页面
     const goMypage = () => {
         Taro.navigateTo({
              url: '/pages/My/index'
         });
     }
 
+    // 开始闯关
+    const start = (record: pointCardType) => {
+        if (record.lock) {
+            Taro.navigateTo({
+                url: `/pages/TopicPage/index?pointState=${record.pointState}`
+           });
+        } else {
+            setVisible(true)
+        }
+    }
+
+
     return (
         <View className='Enteance'>
             <View className='game-box'>
                 <Image className='my' onClick={() => goMypage()} src={require('../../assets/Entrance/my.png')} />
-                <Image className='game-report' src={require('../../assets/Entrance/game-report.png')} />
             </View>
 
             {/* 游戏入口 */}
 
             {
-                entranceList.map( (item, index) => (
-                    <View key={item.id} className='enteance-item' style={{top: item.y, left: item.x, position: 'absolute'}}>
-                        <Image className='enteance-item-img' src={require(`../../assets/Entrance/${index + 1}.png`)}></Image>
-                        <Image className='star' src={require('../../assets/Entrance/star0.png')} />
+                pointCard?.map( (item, index) => (
+                    <View 
+                      key={item.id} 
+                      className='enteance-item' 
+                      style={{top: item.y, left: item.x, position: 'absolute'}}
+                      onClick={() => start(item)}
+                    >
+                        <Image
+                          className='enteance-item-img' 
+                          src={require(`../../assets/Entrance/${item.lock ? '' : 'g'}${item.pointState}.png`)}
+                        />
+
                         <Image 
-                          className='step' 
-                          style={{ left: item.id === 3 ? 64 : -55}} 
-                          src={require(`../../assets/Entrance/${item.id === 3 ? 'setp-4' : 'step'}.png`)}
+                          className='star' 
+                          src={typeof item.starNum === 'number' ? require(`../../assets/Entrance/star${item.starNum}.png`) : ''}
                         />
+
+                        {
+                             curSite === index ? <Image 
+                               className='step' 
+                               style={{ left: item.pointState - 1 === 3 ? 64 : -55}} 
+                               src={require(`../../assets/Entrance/${item.pointState - 1 === 3 ? 'setp-4' : 'step'}.png`)}
+                             /> : null
+                        }
+                        
                     </View>
-                 ))
+                ))
             }
 
             {/* 第一次进入的活动规则 */}
             {
-               visible && <View className='rule-pop'>
+               firstSate && <View className='rule-pop'>
                     <View className='container'>
                         <Image className='rule-img' src={require('../../assets/Entrance/first-tip.png')} ></Image>
-                        <Image className='go-btn' onClick={() => setVisible(false)} src={require('../../assets/Entrance/go.png')} ></Image>
+                        <Image className='go-btn' onClick={() => setFirstState(false)} src={require('../../assets/Entrance/go.png')} ></Image>
                     </View>
                 </View>
             }
+
+
+            {/* 未解锁弹窗 */}
+            { visible ? <LockModal closeLockModal={() => setVisible(false)} /> : null }
         </View>
     )
 }

+ 0 - 4
src/pages/Evaluation/index.config.ts

@@ -1,4 +0,0 @@
-export default {
-    navigationBarTitleText: '测评报告'
-}
-  

+ 0 - 35
src/pages/Evaluation/index.scss

@@ -1,35 +0,0 @@
-.Evaluation {
-    width: 100vw;
-    min-height: 100vh;
-    background: #F2F6F9;
-    overflow: hidden;
-}
-
-
-.rate {
-    width: 702px;
-    height: 484px;
-    background: #FFFFFF;
-    box-shadow: 0px 4px 20px 0px #E9E9EA;
-    border-radius: 20px;
-    position: relative;
-    margin: 0 auto;
-    margin-top: 54px;
-    .topic-bg-box {
-        position: relative;
-        .topic-bg {
-            width: 416px;
-            height: 100px;
-            position: absolute;
-            top: 0;
-            left: 50%;
-            transform: translate(-50%, -30%);
-        }
-        Text {
-            position: absolute;
-            top: 0;
-            left: 50%;
-            transform: translate(-50%, -30%);
-        }
-    }
-}

+ 0 - 28
src/pages/Evaluation/index.tsx

@@ -1,28 +0,0 @@
-import React from 'react'
-
-import { View, Image, Text} from '@tarojs/components'
-
-import './index.scss'
-
-const Rate: React.FC = () => {
-    return (
-        <View className='rate'>
-            <View className='topic-bg-box'>
-                <Image className='topic-bg' src={require('../../assets/Evaluation/topic-bg.png')} />
-                <Text>答对3题,共18题</Text>
-            </View>
-            
-        </View>
-    )
-}
-
-
-const Evaluation: React.FC = () => {
-    return (
-        <View className='Evaluation'>
-            {/* 正确率 */}
-            <Rate />
-        </View>
-    )
-}
-export default Evaluation

+ 2 - 1
src/pages/GetBag/index.config.ts

@@ -1,4 +1,5 @@
 export default {
-    navigationBarTitleText: '提升课'
+    // navigationBarTitleText: '提升课'
+    navigationStyle: 'custom'
 }
   

+ 69 - 0
src/pages/GetBag/index.scss

@@ -0,0 +1,69 @@
+.GetBag {
+    width: 100vw;
+    height: 1832px;
+    background: url('../../assets/GetBag/bg.png') no-repeat;
+    background-size: cover;
+}
+
+.bag-logo {
+    width: 619;
+    height: 297px;
+    position: absolute;
+    top: 96px;
+    left: 50%;
+    transform: translateX(-50%);
+}
+
+
+.card {
+    position: absolute;
+    top: 1040px;
+    left: 50%;
+    transform: translateX(-50%);
+}
+
+.card-item {
+    width: 666px;
+    height: 330px;
+    background: #F6FDFF;
+    border-radius: 40px;
+    border: 10px solid rgba(88, 177, 255, 1);
+    padding: 28px 12px 18px 30px;
+    box-sizing: border-box;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-top: 48px;
+    position: relative;
+    .shop {
+        width: 210px;
+        height: 284px;
+    }
+    .desc {
+        width: 328px;
+        height: 100%;
+        Text {
+            font-size: 36px;
+            font-family: PingFangSC-Semibold, PingFang SC;
+            font-weight: 600;
+            color: #0088FF;
+        }
+        .btn {
+            position: absolute;
+            right: 30px;
+            bottom: 0;
+            width: 376px;
+            height: 140px;
+            background: url('../../assets/GetBag/get-btn.png') no-repeat;
+            background-size: 100% 100%;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            font-size: 28px;
+            font-family: PingFangSC-Medium, PingFang SC;
+            font-weight: 500;
+            color: #FFFFFF;
+            line-height: 40px;
+        }
+    }
+}

+ 42 - 3
src/pages/GetBag/index.tsx

@@ -1,12 +1,51 @@
 import React from 'react'
 
-import { View } from '@tarojs/components'
+import { View, Image, Text } from '@tarojs/components'
 
+import Taro from '@tarojs/taro'
+
+import './index.scss'
 
 const GetBag: React.FC = () => {
+
+    // 跳转小程序
+    const goOtherMiniProgram = () => {
+        Taro.navigateToMiniProgram({
+            appId: 'wx01e79cb36b9436ee',
+            path: 'pages/goods/detail?id=123',
+            // extraData: {
+            //     foo: 'bar'
+            // },
+
+            success: function(res) {
+                // 打开成功
+            }
+        })
+    }
+
     return (
-        <View>
-            
+        <View className='GetBag'>
+            <Image  
+              className='bag-logo'
+              src={require('../../assets/GetBag/bag-logo.png')} 
+            />
+            <View className='card'>
+               {
+                   [1, 2].map( item => (
+                    <View className='card-item' key={item}>
+                        <Image className='shop' src={require('../../assets/GetBag/shop.png')} />
+                        <View className='desc'>
+                            <Text>
+                                逻辑狗新品思考技巧3-9岁数理逻辑能力提升
+                            </Text>
+                            <View className='btn' onClick={goOtherMiniProgram}>
+                                点我
+                            </View>
+                        </View>
+                    </View>
+                   ))
+               }
+            </View>
         </View>
     )
 }

+ 4 - 0
src/pages/GetCourse/index.config.ts

@@ -0,0 +1,4 @@
+export default {
+    navigationBarTitleText: '提升课'
+}
+  

+ 11 - 0
src/pages/GetCourse/index.scss

@@ -0,0 +1,11 @@
+.GetCourse {
+    width: 100vw;
+    height: 100vh;
+    overflow: hidden;
+}
+
+.qrcode {
+    width: 100vw;
+    height: 100vh;
+    display: block;
+}

+ 24 - 0
src/pages/GetCourse/index.tsx

@@ -0,0 +1,24 @@
+import React from 'react'
+
+import { View, Image } from '@tarojs/components'
+
+import './index.scss'
+
+
+const GetCourse: React.FC = () => {
+
+
+    
+    return (
+        <View className='GetCourse'>
+            <Image 
+              className='qrcode' 
+              src={require('../../assets/GetBag/xQrcode.png')}
+              mode='widthFix'
+              showMenuByLongpress
+            />
+        </View>
+    )
+}
+
+export default GetCourse

+ 69 - 8
src/pages/My/index.scss

@@ -1,6 +1,8 @@
+@import "~taro-ui/dist/style/components/icon.scss";
 .My {
     min-height: 100vh;
     background: #F4F5F7;
+    padding-bottom: 46px;
 }
 
 .header {
@@ -49,7 +51,6 @@
         width: 108px;
         margin-right: 52px;
         height: 108px;
-        background: #28D626;
         box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.1);
         border-radius: 20px;
         border: 6px solid #FFFFFF;
@@ -84,6 +85,13 @@
                 color: #28D626;
             }
         }
+        .flag {
+            width: 20px;
+            height: 22px;
+            position: absolute;
+            top: 0;
+            right: 16px;
+        }
     }
 
     
@@ -136,9 +144,19 @@
             margin: 0 auto;
         }
     }
+    .no-record {
+        height: 974px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        Image {
+            width: 328px;
+            height: 352px;
+        }
+    }
     .rank {
         width: 686px;
-        height: 1998px;
+        
         background: #FFFFFF;
         box-shadow: 0px 4px 14px 0px rgba(234, 234, 235, 0.88);
         border-radius: 0px 0px 22px 22px;
@@ -153,7 +171,7 @@
         width: 686px;
         height: 126px;
         padding-left: 28px;
-        padding-right: 70px;
+        padding-right: 20px;
         box-sizing: border-box;
         background-color: #fff;
         margin: 0 auto;
@@ -176,15 +194,22 @@
             margin-right: 34px;
         }
         .user-name {
-            width: 64px;
-            margin-right: 42px;
+            width: 84px;
+            margin-right: 17px;
+            font-size: 32px;
+            font-family: PingFangSC-Medium, PingFang SC;
+            font-weight: 500;
+            color: #4D5259;
         }
         .success-rate {
+            width: 202px;
             font-size: 20px;
             font-family: PingFangSC-Regular, PingFang SC;
             font-weight: 400;
             color: #ABB4C1;
-            margin-right: 16px;
+            display: flex;
+            justify-content: flex-start;
+            align-items: center;
             Text {
                 font-size: 32px;
                 color: #5B73F9;
@@ -192,11 +217,15 @@
             }
         }
         .date {
+            width: 134px;
             font-size: 20px;
             font-family: PingFangSC-Regular, PingFang SC;
             font-weight: 400;
             color: #ABB4C1;
-            margin-right: 16px;
+            // margin-right: 16px;
+            display: flex;
+            align-items: center;
+             justify-content: flex-start;
             Text {
                 color: #23D321;
                 font-size: 32px;
@@ -209,15 +238,47 @@
     width: 686px;
     background: #FFFFFF;
     box-shadow: 0px 4px 14px 0px rgba(234, 234, 235, 0.88);
-    border-radius: 22px;
     margin: 0 auto;
     .topic-item {
+        width: 100%;
+        height: 128px;
+        padding: 0 32px;
+        box-sizing: border-box;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        
         .date {
             font-size: 32px;
             font-family: PingFangSC-Medium, PingFang SC;
             font-weight: 500;
             color: #212020;
         }
+        .info {
+            margin-top: 8px;
+            font-size: 20px;
+            font-family: PingFangSC-Regular, PingFang SC;
+            font-weight: 400;
+            color: #ABB4C1;
+            width: 374px;
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+        }
+        .right {
+            width: 136px;
+            height: 48px;
+            background: linear-gradient(90deg, #6AEA01 0%, #05DD63 100%);
+            box-shadow: 0px 4px 18px 0px rgba(95, 243, 64, 0.6);
+            border-radius: 142px;
+            font-size: 24px;
+            font-family: PingFangSC-Semibold, PingFang SC;
+            font-weight: 600;
+            color: #FFFFFF;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+        }
     }
 }
 

+ 234 - 42
src/pages/My/index.tsx

@@ -1,36 +1,117 @@
-import React, {useState} from 'react'
+import React, {useState, useEffect} from 'react'
 
 import { View, Image, Text } from '@tarojs/components'
 
+import Taro from '@tarojs/taro'
+
+import { AtIcon } from 'taro-ui'
+
 import './index.scss'
 
-const stepList = new Array(8).fill(0)
+import { getPointCard } from '../../api/Home'
+
+import { getPointHisory, getRank} from '../../api/My'
+
+import Tip from '../../utils/tip'
+
+import NavBar from '../../components/NavBar/index'
+
+
+
+type RankListProps = {
+    rankList: any,
+    history: Record<string, any>[]
+}
+
+
+
+const transferTime = (_timeStamp: number) => {
+    return _timeStamp < 10 ? '0' + _timeStamp : _timeStamp
+}
+
+const RankList: React.FC<RankListProps> = ({rankList, history}) => {
 
-const RankList: React.FC = () => {
 
     // 当前选中的Menu下标
     const [MenuIndex, setMenuIndex] = useState(0)
 
+    useEffect( () => {
+
+    }, [MenuIndex])
+
 
     // 渲染排名等级
     const RenderThree = (rank: number) => {
+        console.log();
+        
         if (rank < 4) {
             return <Image  style={{width: 26, height: 29}} src={require(`../../assets/My/rank${rank}.png`)} />
         } else {
             return rank
         }
+    }
+    
+
+    // 查看报告
+    const lookReport = (item: any) => {
 
+        const encodeId = encodeURIComponent( JSON.stringify([{id: item.id}]))
+
+        console.log(encodeId, 'encodeId');
+        
+        Taro.navigateTo({
+             url: `/pages/Report/index?cardIdList=${encodeId}`
+        });
     }
 
     // 渲染做题记录
 
     const RenderTopicRecord = () => {
+
+        const _transferTime = (timeStamp) => {
+            if (timeStamp < 60) {
+                return `0:${transferTime(timeStamp)}`
+            } else if ( timeStamp >= 60 && timeStamp < 120 ) {
+
+                let cache = timeStamp - 60
+                return `1:${transferTime(cache)}`
+            } else if ( timeStamp >= 120 && timeStamp < 180) {
+                let cache = timeStamp - 120
+                return `2:${transferTime(cache)}`
+            }
+        }
+
         return (
             <View className='topic'>
-                <View className='topic-item'>
-                    <View className='date'>2020/4/18</View>
-
-                </View>
+                {
+                    history.length === 0 ? 
+                    <View className='rank no-record'>
+                        <Image  src='http://res.training.luojigou.vip/FjNk6pyGhegX8dgBvcYV7lTXs4AM' />
+                    </View>
+                    :
+                    <View className='rank'>
+                        {
+                            history.map( item => (
+                                <View className='topic-item' key={item.id} onClick={() => lookReport(item)}>
+                                    <View className='left'>
+                                        <View className='date'>{item.createTime.split(' ')[0]}</View>
+                                        <View className='info'>
+                                            <Text>做题卡一张</Text>
+                                            <Text>用时{_transferTime(item.takeTime)}</Text>
+                                            <Text>正确率{item.scoreRate}%</Text>
+                                        </View>
+                                    </View>
+                                    <View className='right'>
+                                        <View className='look-btn'>
+                                            查看报告
+                                        </View>
+                                    </View>
+                                
+                                </View>
+                            ))
+                        }
+                    </View>
+                }
             </View>
         )
     }
@@ -38,33 +119,47 @@ const RankList: React.FC = () => {
 
     // 渲染排行榜
     const RenderRank = () => {
+
+        const { myRank, gameRanks} = rankList
+
         return (
             <React.Fragment>
-                <View className='rank-item'>
-                        <View className='rank-count'>
-                            {RenderThree(2)}
-                        </View>
-                        <Image className='user-ava'  src={require('../../assets/My/ava.jpg')} />
-                        <View className='user-name over1'>22222222222222222</View>
-                        <View className='success-rate'>正确率:<Text>80%</Text></View>
-                        <View className='date'>用时:<Text>10s</Text></View>
-                </View>
-                <View className='rank'>
+                {
+                    myRank.id && 
+                            <View className='rank-item'>
+                            <View className='rank-count'>
+                                {RenderThree(myRank.myRank)}
+                            </View>
+                            <Image className='user-ava'  src={myRank.userImage || require('../../assets/My/ava.jpg')} />
+                            <View className='user-name over1'>{myRank.nickName || '逻辑狗'}</View>
+                            <View className='success-rate'>正确率: <Text>{myRank?.scoreRate?.split('.')[0] || 0}%</Text></View>
+                            <View className='date'>用时: <Text>{myRank.takeTime || 0}s</Text></View>
+                    </View>
+                }
+              
+                {
+                    gameRanks.length === 0 ? 
+                    <View className='rank no-record'>
+                        <Image  src='http://res.training.luojigou.vip/FjNk6pyGhegX8dgBvcYV7lTXs4AM' />
+                    </View>
+                    :
+                    <View className='rank'>
                     {
-                        stepList.map( (item, index) =>  (
-                            <View className='rank-item' key={index}>
+                        gameRanks.map( (item, index) =>  (
+                            <View className='rank-item' key={item.id}>
                                 <View className='rank-count'>
                                     {RenderThree(index + 1)}
                                 </View>
-                                <Image className='user-ava'  src={require('../../assets/My/ava.jpg')} />
-                                <View className='user-name over1'>22222222222222222</View>
-                                <View className='success-rate'>正确率:<Text>80%</Text></View>
-                                <View className='date'>用时:<Text>10s</Text></View>
+                                <Image className='user-ava'  src={item.userImage || require('../../assets/My/ava.jpg')} />
+                                <View className='user-name over1'>{item.nickName || '逻辑狗'}</View>
+                                <View className='success-rate'>正确率: <Text>{item.scoreRate.split('.')[0] || 0}%</Text></View>
+                                <View className='date'>用时: <Text>{item.takeTime || 0}s</Text></View>
                             </View>
+                            // {item.takeTime}
                         ))
-                    }
-                    
-                </View>
+                    }  
+                    </View>
+                }
             </React.Fragment>
         )
     }
@@ -91,42 +186,139 @@ const RankList: React.FC = () => {
 
 const My: React.FC = () => {
 
-    const RenderTopicResult = () => {
-        // return <Image className='star' src={require('../../assets/My/star0.png')} />
-        return (
-            <View className='clock'>
-                <Image style={{width: 8, height: 9}} src={require('../../assets/My/clock.png')} />
-                <Text>未解锁</Text>
-            </View>
-        )
+    // 星级记录
+    const [pointCard, setPointCard] = useState<Record<string, any>[]>([])
+
+    // 当前选择的题卡
+    const [ curId, setCurId ] = useState<string>('') 
+
+    // 游戏排名
+    const [ rankList, setRankList] = useState<Record<string, any>>({})
+
+    // 做题记录
+    const [ history, setHistory ] = useState<Record<string, any>[]>([])
+
+    // 个人信息
+
+    const [userInfo, setUserInfo ] = useState<Record<string, any>>([])
+
+    useEffect( () => {
+        GetPointCard()
+        setUserInfo(Taro.getStorageSync('userInfo'))
+    }, [])
+
+    useEffect( () => {
+        curId && GetRank()
+        curId && GetPointHisory()
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [curId])
+
+    // 获取星级
+
+    const GetPointCard = async () => {
+        Tip.loading()
+        const data = await getPointCard()
+        Tip.loaded()
+        console.log(data);
+        setPointCard(data)
+        // 找到第一个没开启的关卡, 然后默认去选择最后一个开启的关卡
+        const index  =  data.findIndex(item => !item.lock)
+
+        // 没找到说明全部解锁, 默认最后一关
+        if (index === -1) {
+            setCurId( data[data.length - 1].id )
+        } else {
+            setCurId(data[index - 1].id)
+        }
+        
+        
+    }
+
+    // 获取排行榜
+    const GetRank = async () => {
+        
+        const data = await getRank(curId)
+        
+        setRankList(data)
+        
+    }
+
+    // 获取做题记录
+    const GetPointHisory = async () => {
+        const data = await getPointHisory(curId)
+        setHistory(data)
+    }
+    
+    // 切换当前题卡
+    const changePointCard = (item: any) => {
+        setCurId(item.id)
+    }
+
+    // 去活动领取
+    const goPage = () => {
+        Taro.navigateTo({
+             url: '/pages/GetBag/index'
+        });
+    }
+
+
+    const RenderTopicResult = (item: any) => {
+        if (item.lock) {
+            if (typeof item.starNum  === 'number') {
+                return <Image className='star' src={require(`../../assets/My/star${item.starNum}.png`)} />
+            } else {
+                return null
+            }
+            
+        } else {
+            return (
+                <View className='clock'>
+                    <Image style={{width: 8, height: 9}} src={require('../../assets/My/clock.png')} />
+                    <Text>未解锁</Text>
+                </View>
+            )
+        }
     }
 
     return (
         <View className='My'>
+            <NavBar title='' icon={<AtIcon value='chevron-left' size='24' color='#fff'></AtIcon>} />
             <View className='header'></View>
             <View className='user'>
-                <Image className='user-ava' src={require('../../assets/My/ava.jpg')} />
-                <Text>小明</Text>
+                <Image className='user-ava' src={userInfo.avatarUrl} />
+                <Text>{userInfo.nickName}</Text>
             </View>
             {/* 闯关进度 */}
             <View className='customs-step'>
                 {
-                    stepList.map( (item, index) => (
-                        <View className='step' style={{marginRight: [3, 7].includes(index)?  0 : 27}} key={index} >
+                     pointCard?.map( (item, index) => (
+                        <View 
+                          className='step' 
+                          style={{
+                              marginRight: [3, 7].includes(index)?  0 : 27,
+                              background: item.lock ? '#28D626' : 'rgba(40, 214, 38, 0.4)'
+                            }} 
+                          key={index}
+                          onClick={() => changePointCard(item)}
+                        >
                             <Image style={{width: 21, height: 27}} src={require(`../../assets/My/${index + 1 }.png`)} />
                             {
-                                RenderTopicResult()
+                                RenderTopicResult(item)
+                            }
+                            {
+                                curId === item.id ?  <Image className='flag' src={require('../../assets/My/flag.png')} /> : null
                             }
+                           
                         </View>
                     ))
                 }
             </View>
             {/* 卡片 */}
-            <View className='card'>
-                <Image className='card-btn' src={require('../../assets/My/card-btn.png')} />
+            <View className='card' >
+                <Image className='card-btn' onClick={goPage} src={require('../../assets/My/card-btn.png')} />
             </View>
             {/* 排行榜 */}
-            <RankList />
+            {  rankList.hasOwnProperty('myRank')  ? <RankList rankList={rankList} history={history} /> : null }
         </View>
     )
 }

+ 3 - 0
src/pages/Rank/index.config.ts

@@ -0,0 +1,3 @@
+export default {
+    navigationStyle: 'custom'
+}

+ 314 - 0
src/pages/Rank/index.scss

@@ -0,0 +1,314 @@
+.Rank {
+    overflow: hidden;
+    width: 100vw;
+    min-height: 100vh;
+    background-color: rgba(52, 226, 50, 1);
+    font-family: PingFangSC-Medium, PingFang SC;
+    .rankbg {
+        width: 100vw;
+        height: 488px;
+        display: block;
+        margin-top: -55px;
+        position: absolute;
+        top: 0px;
+        left: 0px;
+        z-index: 1;
+    }
+}
+
+
+.header {
+    color: #ffffff;
+    margin: 0 auto;
+    margin-top: 96px;
+    position: relative;
+    z-index: 3;
+    .header-tip {
+        display: flex;
+        flex-direction: column;
+        justify-content: space-between;
+        align-items: center;
+        .big {
+            font-size: 36px;
+            margin-bottom: 10px;
+        }
+        .small {
+            font-size: 24px;
+        }
+    }
+ }
+
+ 
+
+ .top-three {
+    margin: 0 auto;
+    margin-top: 60px;
+    position: relative;
+    z-index: 4;
+    box-sizing: border-box;
+    display: flex;
+    align-items: center;
+    .top-bg {
+        width: 690rpx;
+        height: 356rpx;
+        margin: 0 auto;
+    }
+    
+
+    .unify-Count {
+        position: absolute;
+        top: 0;
+        left: 50%;
+        transform: translate(-50%, -50%);
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        .unify-Count-img {
+            position: absolute;
+            top: 0px;
+            left: 0px;
+        }
+        .num {
+            position: relative;
+            z-index: 1;
+            font-size: 24px;
+            font-weight: 500;
+            color: #FFFFFF;
+        }
+    }
+  
+    .unify-city-name {
+        font-size: 26px;
+        font-weight: 500;
+        color: #FB6D7E;
+    }
+
+    .second {
+        width: 226px;
+        height: 308px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        position: absolute;
+        top: 42px;
+        left: 36px;
+        .Ava {
+            width: 96px;
+            height: 96px;
+            border-radius: 50%;
+            position: absolute;
+            top: 60px;
+            left: 64px;
+            border: 4px solid rgb(181, 212, 249);
+            box-sizing: border-box;
+        }
+        .Count {
+            width: 128px;
+            height: 38px;
+            .Count-img {
+                width: 100%;
+                height: 100%;
+            }
+
+        }
+        .Top {
+            width: 61px;
+            height: 42px;
+            position: absolute;
+            top: 36px;
+            left: 82px;
+        }
+        .Bottom {
+            width: 168px;
+            height: 60px;
+            position: absolute;
+            top: 126px;
+            left: 50%;
+            transform: translateX(-50%);
+        }
+
+        .city-name {
+           
+            position: absolute;
+            bottom: 72px;
+            left: 50%;
+            transform: translate(-50%, -50%);
+        }
+    }
+
+    .first {
+        width: 244px;
+        height: 346px;
+        position: absolute;
+        left: 252px;
+        .first-bg {
+            position: absolute;
+            top: 67px;
+            left: 54%;
+            width: 185px;
+            height: 120px;
+            transform: translateX(-50%);
+        }
+        .Ava {
+            width: 108px;
+            height: 108px;
+            border-radius: 50%;
+            box-sizing: border-box;
+            position: absolute;
+            top: 72px;
+            left: 50%;
+            transform: translateX(-50%);
+        }
+        .Count {
+            width: 130px;
+            height: 40px;
+            .Count-img {
+                width: 100%;
+                height: 100%;
+            }
+            .num {
+
+            }
+        }
+        .Top {
+            width: 76.2px;
+            height: 52.8px;
+            position: absolute;
+            top: 30px;
+            left: 50%;
+            transform: translateX(-50%);
+        }
+        .Bottom {
+            width: 168px;
+            height: 60px;
+            position: absolute;
+            top: 146px;
+            left: 50%;
+            transform: translateX(-50%);
+        }
+        .city-name {
+            position: absolute;
+            left: 50%;
+            bottom: 96px;
+            transform: translateX(-50%);
+        }
+    }
+
+    .thirdly {
+        width: 226px;
+        height: 286px;
+        position: absolute;
+        right: 32px;
+        top: 64px;
+        .Ava {
+            width: 96px;
+            height: 96px;
+            border-radius: 50%;
+            border: 4px solid rgb(239, 210, 174);
+            position: absolute;
+            top: 58px;
+            left: 50%;
+            transform: translateX(-50%);
+        }
+        .Count {
+            width: 128px;
+            height: 38px;
+            .Count-img {
+                width: 100%;
+                height: 100%;
+            }
+        }
+        .Top {
+            width: 61px;
+            height: 42px;
+            position: absolute;
+            top: 22px;
+            left: 50%;
+            transform: translateX(-50%);
+        }
+        .Bottom {
+            width: 168px;
+            height: 60px;
+            position: absolute;
+            top: 124px;
+            left: 50%;
+            transform: translateX(-50%);
+        }
+        .city-name {
+            position: absolute;
+            bottom: 52px;
+            left: 50%;
+            transform: translateX(-50%);
+        }
+    }
+}
+
+.green-shade {
+    width: 97%;
+    height: 54px;
+    background:rgba(133,220,132,0.37);;
+    position: absolute;
+    left: 50%;
+    bottom: 0px;
+    transform: translateX(-50%);
+    z-index: 0;
+}
+
+
+.rank-content {
+    background-color: #fff;
+    border-radius: 18rpx;
+    width: 680rpx;
+    padding:  0 42rpx;
+    box-sizing: border-box;
+    margin: 0 auto;
+
+    .rank-content-item {
+        height: 144rpx;
+        display: flex;
+        align-items: center;
+        .ranking {
+            color: #ABB4C1;
+            font-size: 36rpx;
+            margin-right: 32rpx;
+            width: 60rpx;
+            text-align: center;
+            
+        }
+        .city-ava {
+            margin-right: 30rpx;
+            image {
+                width: 82rpx;
+                height: 82rpx;
+                border-radius: 50%;
+            }
+        }
+        .city-info {
+            .city-name {
+               color: #000;
+                font-size: 30rpx;
+                font-weight: bold;
+            }
+            .city-hot {
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                margin-left: -20rpx;
+                image {
+                    width: 40rpx;
+                    height: 46rpx;
+                    margin-right: 16rpx;
+                }
+                .hot-num {
+                    margin-top: 20rpx;
+                    font-size: 26rpx;
+                    color: #ABB4C1;
+                    text {
+                        color: #000;
+                        margin-left: 12rpx;
+                    }
+                }
+            }
+        }
+    }
+}

+ 175 - 0
src/pages/Rank/index.tsx

@@ -0,0 +1,175 @@
+import React, { useEffect, useState } from 'react'
+
+import { View, Image, Text } from "@tarojs/components";
+
+import { getRank } from "../../api/Rank";
+
+import './index.scss'
+
+
+
+const imgUrl =  [
+    {
+        count: 'http://res.training.luojigou.vip/Fmijl8dAfngLORY1_wQHupkK6O1H',
+        top: 'http://res.training.luojigou.vip/FosT3op3qAbtb_AHStAch2Ioi7pD',
+        bottom: 'http://res.training.luojigou.vip/FntpZcGDIsZsYYkgosM6s9sBQTDn'
+    },
+    {
+        count: 'http://res.training.luojigou.vip/FvmR-7IPqXwNtI4REBYNmlSecA8D',
+        top: 'http://res.training.luojigou.vip/Fldl2pRZblLQVdztOhlODPEhhlQF',
+        bottom: 'http://res.training.luojigou.vip/FnPemTe4MKzHzofwZx3cB77dLM3z'
+    },
+    {
+        count: 'http://res.training.luojigou.vip/FnXFslAE0LXmxtlTYWdeGzPU9Qxb',
+        top: 'http://res.training.luojigou.vip/FvKskKpeDP68TBe0cg15LY7Zom13',
+        bottom: 'http://res.training.luojigou.vip/FieXA8zqeIevUgQlhy5VNXBB6OG4'
+    }
+]
+
+type RankListType = {
+    hotNum: number
+    id: string
+    imageUrl: string
+    name: string
+    province: string,
+    title: string
+}
+
+
+const Rank: React.FC = () => {
+
+    const [ RankList, setRankList ] = useState<RankListType[]>([])
+    
+    const [ topThree, setTopThree ] = useState<RankListType[]>([])
+
+
+
+    useEffect( () => {
+        // GetRank()
+   
+        
+    },[])
+
+    // 获取地区排行榜
+    const GetRank = async () => {
+      const data = await getRank()
+      let cacheData = JSON.parse( JSON.stringify(data) )
+      
+      setRankList(cacheData.splice(3))
+
+      setTopThree(cacheData.splice(0, 3))
+    }
+
+    const RenderToeThree = (index: number) => {
+        
+        // 头像
+        const Ava = () =>  {
+            switch (index) {
+                case 0:
+                   return <View>
+                            <Image className='first-bg' src='http://res.training.luojigou.vip/Fth2HDhbpMpVZFanOLYDRjlmyAF_' />
+                            <Image className='Ava' src={topThree[index].imageUrl} />
+                          </View>
+             
+            
+                default:
+                    return  <Image className='Ava' src={topThree[index].imageUrl} />
+            }
+        }
+        
+        const Count = () => <View className='Count unify-Count'>
+                                <Image className='Count-img unify-Count-img' src={imgUrl[index].count} />
+                                <Text className='num'>12121</Text>
+                            </View> 
+
+        const Top = () => <Image className='Top' src={imgUrl[index].top} />
+
+        const Bottom =  () => <Image className='Bottom' src={imgUrl[index].bottom} />
+
+        return (
+            <View>
+                <Ava />
+                <Count />
+                <Top />
+                <Bottom />
+                <Text className='city-name unify-city-name'>河南</Text>
+            </View>
+        )
+    }
+
+    return (
+        <View className='Rank'>
+            <Image
+              className='rankbg' 
+              src={require('../../assets/Rank/rankbg.png')}
+            />
+            <View className='header'>
+                <View className='header-tip'>
+                    <View className='big'>
+                        地区热度排行榜
+                    </View>
+                    <View className='small'>
+                        地区参与人数
+                    </View>
+                </View>
+            </View>
+            {/* 背景图 */}
+            <View className='top-three'>
+                <Image className='top-bg' mode='aspectFit' src='http://res.training.luojigou.vip/FhueN6Svm_2LjXF4vqMOymO7nnAm' />
+                
+                <View className='second'>
+                    {
+                        topThree.length && RenderToeThree(1)
+                    }
+                </View>
+                <View className='first'>
+                    {
+                        topThree.length && RenderToeThree(0)
+                    }
+                </View>
+                <View className='thirdly'>
+                    {
+                        topThree.length && RenderToeThree(2)
+                    }
+                </View>
+
+                <View className='green-shade' />
+                
+            
+            </View>
+
+
+
+
+            <View className='rank-content'>
+                {
+                    RankList?.map((item, index) => (
+                        <View className='rank-content-item'  key={item.name}>
+                            <View className='ranking'>
+                                {index + 4}
+                            </View>
+                            <View className='city-ava'>
+                                <Image  mode='aspectFill' src={item.imageUrl} />
+                            </View>
+                            <View className='city-info'>
+                                <View className='city-name'>
+                                    {item.name}
+                                </View>
+                                <View className='city-hot'>
+                                    <Image mode='aspectFill' src='http://res.training.luojigou.vip/FrJMYoPUctdc_8BaoN9yWddoJyKq' />
+                                    <View className='hot-num'>
+                                        热度:  <Text> {item.hotNum}</Text> 
+                                    </View>
+                                </View>
+                            </View>
+                        </View>
+                    ))
+                }
+                
+            </View>
+           
+        </View>
+    )
+}
+
+export default Rank

+ 4 - 0
src/pages/Report/index.config.ts

@@ -0,0 +1,4 @@
+export default {
+    navigationBarTitleText: '游戏报告'
+}
+  

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 196 - 0
src/pages/Report/index.scss


+ 283 - 0
src/pages/Report/index.tsx

@@ -0,0 +1,283 @@
+import React, { useEffect, useState} from 'react'
+
+import { View, Image, Text, WebView } from '@tarojs/components'
+
+import Taro from '@tarojs/taro'
+
+import './index.scss'
+
+import Tip from '../../utils/tip'
+ 
+import { getReport } from '../../api/Report'
+
+type RateProps = {
+    allQuestionNum: number,
+    trueQuestionNum: number,
+    takeTime: number,
+    date: string
+}
+
+const Rate: React.FC<RateProps> = ({allQuestionNum, takeTime, date, trueQuestionNum}) => {
+
+    const rateFn = (): string => ( trueQuestionNum / allQuestionNum * 100 ).toFixed(2)
+
+    const formatDate = (): string => date.split(' ')[0].replace(/\-/g, '.')
+
+
+    const RenderItem = () => {
+        if (String(trueQuestionNum).length === 1) {
+            return (
+                <View className='percent'>
+                        <View className='correct'>
+                            {trueQuestionNum}
+                        </View>
+                        <View className='line'>
+                            /
+                        </View>
+                        <View className='total'>
+                            {allQuestionNum}
+                        </View>
+                </View>
+            )
+        } else {
+            return (
+                <View className='overflow-percent percent'>
+                    <View className='overflow-correct'>
+                        {trueQuestionNum}
+                    </View>
+                    <View className='overflow-line'>
+                        /
+                    </View>
+                    <View className='overflow-total'>
+                        {allQuestionNum}
+                    </View>
+                </View>
+            )
+        }
+        
+    }
+    
+
+    return (
+        <View className='Rate'>
+            <View className='ratebg-box'>
+                <Text> 答对{trueQuestionNum}题,共{allQuestionNum}题 </Text>
+            </View>
+            <Image className='cloud' mode='aspectFill'  src={require('../../assets/Report/cloud.png')} />
+            <RenderItem />
+            <Image mode='aspectFill' className='dog' src={require('../../assets/Report/dog.png')} />
+            <View className='data'>
+                <View>
+                    <Text className='label'>总时间:</Text>
+                    <Text className='value'>{takeTime}s</Text>
+                </View>
+                <View>
+                    <Text className='label'>正确率:</Text>
+                    <Text className='value'>{rateFn()}%</Text>
+                </View>
+                <View>
+                    <Text className='label'>时间:</Text>
+                    <Text className='value'>{formatDate()}</Text>
+                </View>
+            </View>
+        </View>
+    )
+}
+
+type CardProps = {
+    list: Record<string, any>[]
+}
+
+const Card: React.FC<CardProps> = ({list}) => {
+
+    const transfer = (label: string) => {
+        switch (label) {
+            case '1':
+              return '一'  
+            case '2':
+              return '二'  
+            case '3':
+              return '三'  
+            case '4':
+              return '四'  
+            case '5':
+              return '五'  
+            case '6':
+              return '六'  
+            case '7':
+              return '七'  
+            case '8':
+              return '八'  
+              
+        }
+    }
+
+    return (
+        <View className='Card'>
+            {
+                list.length && list.map ( item => (
+                    <View className='card-item' key={item.id}>
+                        <View className='label'>第{transfer(item.cardId)}关卡片</View>
+                        <View className='desc'>
+                            <View className='type'>逻辑思维</View>
+                            <View className='date'>
+                                <Image src={require('../../assets/Report/watch.png')} />
+                                <Text>{item.takeTime}s</Text>
+                            </View>
+                        </View>
+                        <View className='line'></View>
+                        <View className='star'>
+                        {
+                            new Array(item.trueQuestionNum).fill(0).map( (_item, index) => (
+                                <Image key={index} src={require('../../assets/Report/star.png')} />
+                            ))
+  
+                        }
+                        {
+                            new Array(item.allQuestionNum - item.trueQuestionNum).fill(0).map( (_item, index) => (
+                                <Image key={index} src={require('../../assets/Report/stargrey.png')} />
+                            ))
+                        }
+                        </View>
+                    </View>
+                ))
+            }
+            
+        </View>
+    )
+}
+
+const PromoteCard: React.FC = () => {
+
+    const goGetBagPage = () => {
+        Taro.navigateTo({
+             url: '/pages/GetBag/index'
+        });
+   }
+
+    return (
+        <View className='PromoteCard'>
+            <View className='content'>
+                <View className='label'>数理逻辑能力提升方案</View>
+                <View className='desc'>卡片闯关游戏是提升我们能力哦</View>
+            </View>
+            <Image mode='aspectFill' src={require('../../assets/Report/btn.png')} onClick={goGetBagPage} />
+        </View>
+    )
+}
+
+type CollectProps = {
+    goWebViewPage: () => void
+}
+
+const Collect: React.FC<CollectProps> = ({goWebViewPage}) => {
+    return (
+        <View className='Collect'>
+            <View className='label-box'>
+                <Image className='collect-img' src={require('../../assets/Report/collect.png')} />
+                <View className='label'>参与E20数理逻辑能力</View>
+            </View>
+            
+            <View className='desc-box'>
+                <View className='desc'>中国关工委健体中心素质能力培养计划</View>
+                <View className='btn' onClick={() => goWebViewPage()}>活动报名</View>
+            </View> 
+        </View>
+    )
+}
+
+
+type FloatCardProps = {
+    ids: string[]
+} 
+
+const FloatCard: React.FC<FloatCardProps> = ({ids}) => {
+
+    const goMyPage = () => Taro.navigateTo({ url: '/pages/My/index'})
+
+    const goAnalysisPage = () => {
+        const encodeIds = encodeURIComponent( JSON.stringify(ids) )
+
+        console.log(encodeIds, 'encodeIds');
+        
+        Taro.navigateTo({
+            url: `/pages/TopicPage/Analysis?ids=${encodeIds}`
+       })
+    }
+
+    return (
+        <View className='FloatCard'>
+            <View className='card left' onClick={goAnalysisPage}>题卡解析</View>
+            <View className='card right' onClick={goMyPage}>查看排行榜</View>
+        </View>
+    )
+}
+
+
+type reportDataType = {
+    allQuestionNum: number,
+    takeTime: number,
+    trueQuestionNum:  number,
+    date: string,
+    list: Record<string, any>[]
+}
+
+const Report: React.FC = () => {
+
+    const [ cardIdList, setCardIdList ] = useState<string[]>([])
+
+    const [ reportData, setReportData ] = useState<reportDataType>({} as reportDataType )
+
+    useEffect( () => {
+        
+        const _cardIdList: string[] = JSON.parse(decodeURIComponent(Taro.getCurrentInstance().router?.params.cardIdList as string)).map( item => item.id ) 
+
+        console.log(_cardIdList, '_cardIdList');
+        
+        setCardIdList( _cardIdList)
+
+    }, [])
+
+
+    useEffect( () => {
+        cardIdList.length && GetReport()
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [ cardIdList])
+
+    // 获取游戏报告
+    const GetReport = async () => {
+        Tip.loading()
+        const data =  await getReport(cardIdList)
+        Tip.loaded()
+        console.log(data);
+        setReportData(data)
+    }
+
+    // 打开webview
+    const goWebViewPage = () => {
+        Taro.navigateTo({
+             url: '/pages/WebView/index'
+        });
+    }
+
+    return (
+        <View className='Report' >
+            {
+                reportData.date && <View>
+                    <Rate
+                      allQuestionNum={reportData.allQuestionNum} 
+                      takeTime={reportData.takeTime} 
+                      date={reportData.date}
+                      trueQuestionNum={reportData.trueQuestionNum}
+                    />
+                    <Card list={reportData.list} />
+                    <PromoteCard />
+                    <Collect goWebViewPage={goWebViewPage} />
+                    <FloatCard ids={cardIdList} /> 
+              </View>
+            }
+        </View>
+    )
+}
+export default Report
+

+ 4 - 0
src/pages/TopicPage/Analysis.config.ts

@@ -0,0 +1,4 @@
+export default {
+    navigationStyle: 'custom'
+}
+  

+ 152 - 0
src/pages/TopicPage/Analysis.scss

@@ -0,0 +1,152 @@
+.Analysis {
+    width: 100vw;
+    height: 100vh;
+    background-color: rgba(35, 211, 33, 1);
+    font-family: PingFangSC-Regular, PingFang SC;
+    position: relative;
+    .bg {
+        width: 100vw;
+        height: 250px;
+    }
+}
+
+.Course {
+    width: 95.2vw;
+    height: 83vh;
+    background-color: #fff;
+    margin: 0 auto;
+    position: absolute;
+    top: 15.39vh;
+    left: 2.4vw;
+}
+
+.show-panel {
+    position: relative;
+    width: 470px;
+    height: 49.51vh;
+    .round {
+        width: 8vw;
+        height: 8vw;
+        border-radius: 50%;
+        position: absolute;
+        z-index: 2;
+    }
+
+    .game-item {
+        position: absolute;
+    }
+    .show-image {
+        position: absolute;
+    }
+}
+
+.opra-panel {
+    width: 23vw;
+    height: 49.51vh;
+    border: 2px solid #23D321;
+    border-top-right-radius: 20rpx;
+    border-bottom-right-radius: 20rpx;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    align-items: center;
+    position: absolute;
+    top: 20.5%;
+    right: 6vw;
+    .answer-item {
+        width: 70%;
+        height: 192px;
+        border-bottom: 2px solid #23D321;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        position: relative;
+        .answer-round {
+            width: 36px;
+            height: 36px;
+            position: absolute;
+            top: 5px;
+            right: 5px;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            .round {
+                width: 100%;
+                height: 100%;
+                border-radius: 50%;
+            }
+            .correct {
+                width: 36px;
+                height: 36px;
+                position: absolute;
+            }
+        }
+       
+        image {
+            width: 100%;
+            height: 62.5%;
+        }
+    }
+    .answer-item:last-child {
+        border: none;
+    }
+}
+
+.Topicbg {
+    width: 89.6vw;
+    height: 53.57vh;
+    position: absolute;
+    right: -2.4vw;
+    bottom: 5.9vh;
+}
+
+
+.dot {
+    width: 24px;
+    height: 20px;
+    position: absolute;
+    
+}
+
+.rotate-dot {
+    transform: rotate(90deg);
+}
+
+.CorrectAnswer {
+    position: absolute;
+    bottom: 3.85vh;
+    left: 50%;
+    transform: translateX(-50%);
+    .label {
+        font-family: PingFangSC-Medium, PingFang SC;
+        margin-bottom: 10px;
+        margin-left: 2.4%;
+    }
+    .label Text:first-child{
+        font-size: 24px;
+        font-weight: 500;
+        color: #6A719E;
+    }
+
+    .label Text:last-child {
+        font-size: 20px;
+        color: #6A719E;
+    }
+    
+    .answer-box {
+        width: 82.4vw;
+        height: 8.25vh;
+        border-radius: 12px;
+        border: 2px solid #23D321;
+        display: flex;
+        justify-content: space-around;
+        align-items: center;
+        .answer-item {
+            width: 67px;
+            height: 67px;
+            border-radius: 50%;
+            // background: linear-gradient(180deg, #7CB47E 0%, #477F49 100%);
+            // box-shadow: 0px 4px 8px 0px rgba(147, 241, 150, 0.33);
+        }
+    }
+}

+ 302 - 0
src/pages/TopicPage/Analysis.tsx

@@ -0,0 +1,302 @@
+/* eslint-disable @typescript-eslint/no-use-before-define */
+import React, { useEffect, useState } from 'react'
+
+import { View , Image, Text} from '@tarojs/components'
+
+import Taro from '@tarojs/taro'
+
+import './Analysis.scss'
+
+import TimeCom from './component/TimeCom'
+
+import BgPanel from './component/BgPanel'
+
+import { questionAnalysis } from '../../api/QuestionCard'
+
+import Tip from '../../utils/tip'
+
+const dotList = [
+    {
+        id: 0,
+        x:  '0vw',
+        y: '56.11vh'
+    },
+    {
+        id: 1,
+        x:  '0vw',
+        y: '44.03vh'
+    },
+    {
+        id: 2,
+        x:  '0vw',
+        y: '32.85vh'
+    },
+    {
+        id: 3,
+        x:  '0vw',
+        y: '21.86vh'
+    },
+    {
+        id: 4,
+        x:  '84.8vw',
+        y: '0vh'
+    },
+    {
+        id: 5,
+        x:  '60.27vw',
+        y: '0vh'
+    },
+    {
+        id: 6,
+        x:  '36.27vw',
+        y: '0vh'
+    },
+    {
+        id: 6,
+        x:  '10.36vw',
+        y: '0vh'
+    },
+]
+
+type CorrectAnswerProps = {
+    trueAnswer: string[]
+}
+
+const CorrectAnswer: React.FC<CorrectAnswerProps> = ({trueAnswer}) => {
+
+    
+    return (
+        <View className='CorrectAnswer'>
+            <View className='label'><Text>正确答案:</Text> <Text>从左往右-从上往下</Text> </View>
+            <View className='answer-box'>
+                {
+                    trueAnswer.length && trueAnswer.map( item => (
+                        <View 
+                          className='answer-item' 
+                          key={item} 
+                          style={{backgroundColor: item}}
+                        />
+                    )) 
+                }
+                
+            </View>
+        </View>
+    )
+}
+
+type CourseProps = {
+    content: any,
+    options: any,
+    index: number,
+    trueAnswer: string[],
+    label: string
+}
+
+const Course: React.FC<CourseProps> = ({content, options, index, trueAnswer, label}) => {
+
+
+
+    // 屏幕信息
+    const [ screen, setScreen ]  = useState<Record<string, any>>({})
+
+    useEffect( () =>{
+        Taro.getSystemInfo({
+            success: function (res) {
+                console.log(res);
+                setScreen(res)
+            }
+        })
+    }, [])
+
+    return (
+        // 2.3vh
+        <View className='Course' >
+            <BgPanel label={label} />
+
+            {/* 绿色背景 */}
+            {/* <Image className='Topicbg'  src={require('../../assets/TopicPage/Topicbg.png')} /> */}
+
+            {
+                dotList.map( item => (
+                    <Image 
+                      key={item.id} 
+                      className={['dot', item.id >= 4 ? 'rotate-dot' : ''].join(',')} 
+                      style={{right: item.x, bottom: item.y}} 
+                      src={require('../../assets/TopicPage/dot.png')}
+                    />
+                ))
+            }
+
+            {/* 左侧显示面板 */}
+            <View className='show-panel'>
+                {/* 展示图 */}
+                {
+                   content.length && content?.map( item => (
+                       <View 
+                         className='game-item' 
+                         key={item.id}
+                         style={{
+                             width: item.pw * (screen.screenWidth / 375) ,
+                             height: item.ph * (screen.screenHeight / 812) ,
+                             top: item.py * (screen.screenHeight / 812),
+                             left:  item.px *  (screen.screenWidth / 375), 
+                         }}
+                       >
+                           {/* 圆 */}
+                           {/*     width: item.iw ,
+                                height: item.ih, */}
+                           <View 
+                             className='round'
+                             style={{
+                                background: item.color,
+                                width: item.iw * (screen.screenWidth / 375) ,
+                                height: item.ih * (screen.screenHeight / 812),
+                                top: item.iy * (screen.screenHeight / 812),
+                                left: item.ix  * (screen.screenWidth / 375)
+                             }}
+                           />
+
+                           {/* 图片 */}
+                           <Image
+                             className={['show-image'].join(',')}
+                             src={item.imgUrl}
+                             mode='aspectFit'
+                             style={{
+                                 width:   item.w * (screen.screenWidth / 375)  ,
+                                 height:  item.h * (screen.screenHeight / 812) ,
+                                 top:  item.y * (screen.screenHeight / 812),
+                                 left:  item.x *  (screen.screenWidth / 375) ,
+                                 position: 'absolute'
+                             }}
+                           />
+                       </View>
+                   )) 
+                }
+                
+            </View>
+            {/* 右侧选择面板 */}
+            <View className='opra-panel' >
+                {
+                    options?.map( item => (
+                        <View className='answer-item' key={item.id} >
+                            <View className='answer-round'>
+                                <View  className='round' style={{background: item.color}} />
+                                {
+                                    item.ifTrue ?  
+                                        <Image className='correct' mode='aspectFit' src={require('../../assets/TopicPage/correct.png')} /> 
+                                        :
+                                        <Image className='correct' mode='aspectFit' src={require('../../assets/TopicPage/error.png')} />
+                                    
+                                }  
+                            </View>
+                            <Image  mode='aspectFill' src={item.imgUrl} />
+                        </View>
+                    ))
+                }
+            </View>
+
+
+            <CorrectAnswer trueAnswer={trueAnswer} />
+        </View>
+    )
+}
+
+const transferTime = (_timeStamp: number) => {
+    return _timeStamp < 10 ? '0' + _timeStamp : _timeStamp
+}
+
+type AnalysisItemProps = {
+    item: any,
+    index: number
+}
+
+const AnalysisItem: React.FC<AnalysisItemProps> = ({item, index}) => {
+
+    const [ time , setTime] = useState<string>('0:00')
+
+    useEffect( () => {
+        tranferTime()
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [])
+
+    const tranferTime = () => {
+        const { takeTime } = item
+        if ( takeTime < 60 ) {
+            setTime(`0:${transferTime(takeTime)}`)
+        } else if ( takeTime >= 60 && takeTime < 120 ) {
+
+            let cache = takeTime - 60
+
+            setTime(`1:${transferTime(cache)}`)
+        } else if ( takeTime >= 120 && takeTime < 180) {
+            let cache = takeTime - 120
+            setTime(`2:${transferTime(cache)}`)
+        } 
+    }
+
+    return (
+        <View className='Analysis'>
+            
+            {
+                index === 0 ? <Image className='bg' src={require('../../assets/TopicPage/bg.png')} /> : null
+            }
+            
+
+            <TimeCom time={time} />
+
+            <Course 
+              content={item.content} 
+              options={item.options} 
+              index={index} 
+              trueAnswer={item.trueAnswer} 
+              label={item.label}
+            />
+
+        </View>
+    )
+}
+
+
+const AnalysisPage: React.FC = () => {
+    const [ ids, setIds ] =  useState<string[]>([])
+
+    const [ Analysis, setAnalysis ] = useState<Record<string, any>[]>([])
+
+    
+
+    useEffect( () => {
+
+        setIds( JSON.parse( decodeURIComponent(Taro.getCurrentInstance().router?.params.ids as string) ) )
+
+    }, [])
+
+    useEffect( () => {
+
+        ids.length && QuestionAnalysis()
+    
+        // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [ids])
+
+    // 获取错题解析数据
+    const QuestionAnalysis = async () => {
+       Tip.loading()
+       const data = await questionAnalysis(ids)
+       Tip.loaded()
+       console.log(data);
+       setAnalysis(data)
+
+       
+    }
+
+    return (
+        <React.Fragment>
+            {
+                Analysis.length && Analysis.map( (item, index) => (
+                    <AnalysisItem key={index} item={item} index={index} />
+                ))
+            }
+        </React.Fragment>
+    )
+}
+
+export default AnalysisPage

+ 32 - 0
src/pages/TopicPage/component/BgPanel.scss

@@ -0,0 +1,32 @@
+.BgPanel {
+    width: 100%;
+    height: 21.7%;
+    .dog {
+        width: 114px;
+        height: 150px;
+        position: absolute;
+        top: 3%;
+        left: 3.3vw;
+    }
+    .trumpet {
+        width: 14.85vw;
+        height: 5.05vh;
+        position: absolute;
+        top: 1.8%;
+        right: 3.6%;
+    }
+    .tip {
+        width: 70.3vw;
+        // min-height: 6.16vh;
+        background: rgba(198, 199, 211, 0.09);
+        border-radius: 8px;
+        position: absolute;
+        right: 4.26%;
+        top: 7.23%;
+        padding: 6px;
+        box-sizing: border-box;
+        font-size: 24px;
+        font-weight: 400;
+        color: #6A719E;
+    }
+}

+ 30 - 0
src/pages/TopicPage/component/BgPanel.tsx

@@ -0,0 +1,30 @@
+import React, { memo } from 'react'
+
+import { View, Image, Text,  } from '@tarojs/components'
+
+import Taro from '@tarojs/taro'
+
+import './BgPanel.scss'
+
+
+type BgPanelProps = {
+    label: string
+}
+
+// 背景板
+const BgPanel: React.FC<BgPanelProps> = memo(({label}) => {
+
+    
+        return (
+            <View className='BgPanel'>
+                <Image className='dog' src={require('../../../assets/TopicPage/dog.png')} />
+                <Image className='trumpet' src={require('../../../assets/TopicPage/trumpet.png')} />
+                <View className='tip'>
+                    {label}
+                </View>
+            </View>
+        )
+    }
+) 
+
+export default BgPanel

+ 141 - 0
src/pages/TopicPage/component/Modal.scss

@@ -0,0 +1,141 @@
+.Modal {
+    width: 100vw;
+    height: 100vh;
+    background: rgba(0, 0, 0, 0.79);
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 10;
+    .container {
+        position: absolute;
+        bottom: 536px;
+        left: 50%;
+        transform: translateX(-50%);
+        width: 578px;
+        height: 716px;
+        .star {
+            position: absolute;
+            top: 0;
+            left: 0;
+            width: 578px;
+            height: 716px;
+        }
+        .left {
+            width: 206px;
+            height: 109px;
+            position: absolute;
+            left: 54px;
+            bottom: 70px;
+        }
+        .right{
+            width: 206px;
+            height: 109px;
+            position: absolute;
+            right: 54px;
+            bottom: 70px;
+        }
+        .pass-label {
+            width: 350px;
+            height: 78px;
+            position: absolute;
+            left: 50%;
+            bottom: 344px;
+            transform: translateX(-50%);
+        }
+        .time-over-label {
+            width: 512px;
+            height: 40px;
+            position: absolute;
+            left: 50%;
+            bottom: 344px;
+            transform: translateX(-50%);
+        }
+        .befated-label {
+            width: 384px;
+            height: 40px;
+            position: absolute;
+            left: 50%;
+            bottom: 344px;
+            transform: translateX(-50%);
+        }
+        .content {
+            font-size: 26px;
+            font-family: PingFangSC-Regular, PingFang SC;
+            font-weight: 400;
+            color: #000000;
+            position: absolute;
+            left: 50%;
+            bottom: 234px;
+            text-align: center;
+            transform: translateX(-50%);
+        }
+        .pass-content {
+            width: 344px;
+        }
+        .befated-content {
+            width: 396px;
+            bottom: 215px;
+        }
+    }
+}
+
+.LifeNum-zero {
+    width: 578px;
+    height: 590px;
+    background: #FFFFFF;
+    border-radius: 28px;
+    position: absolute;
+    bottom: 536px;
+    left: 50%;
+    transform: translateX(-50%);
+    .face {
+        width: 114px;
+        height: 114px;
+        position: absolute;
+        left: 50%;
+        top: 14px;
+        transform: translateX(-50%);
+    }
+    .LifeNum-zero-label {
+        width: 100%;
+        height: 98px;
+        background: #FFF3A0;
+        position: absolute;
+        top: 150px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        .LifeNum-zero-befated {
+            width: 322px;
+            height: 40px;
+            
+        }
+    }
+    .LifeNum-zero-content {
+        width: 330px;
+        font-size: 28px;
+        font-family: PingFangSC-Semibold, PingFang SC;
+        font-weight: 600;
+        color: #000000;
+        position: absolute;
+        top: 300px;
+        left: 50%;
+        transform: translateX(-50%);
+    }
+    .LifeNum-zero-left {
+        width: 206px;
+        height: 109px;
+        position: absolute;
+        left: 54px;
+        bottom: 80px;
+    }
+
+    .LifeNum-zero-right {
+        width: 206px;
+        height: 109px;
+        position: absolute;
+        right: 54px;
+        bottom: 80px;
+    }
+
+}

+ 140 - 0
src/pages/TopicPage/component/Modal.tsx

@@ -0,0 +1,140 @@
+import React from 'react'
+
+import { View, Image, Text } from '@tarojs/components'
+
+import Taro from '@tarojs/taro'
+
+import './Modal.scss'
+
+const Modal = ({trueQuestionNum, LifeNum, cardId, setLifeNum, timeStamp}) => {
+
+   console.log(LifeNum, 'LifeNum');
+   
+    
+    // 退出
+    const quit = () => {
+        const cardIdList = Taro.getStorageSync('cardIdList')
+
+
+        if (!cardIdList) {
+            Taro.navigateBack({
+                 delta: 1
+            });
+            return 
+        }
+
+        const encodeCardIdList = encodeURIComponent( JSON.stringify(cardIdList))
+        
+        console.log(encodeCardIdList, 'encodeCardIdListencodeCardIdList');
+        
+        Taro.removeStorageSync('cardIdList');
+        Taro.redirectTo({
+             url: `/pages/Report/index?cardIdList=${encodeCardIdList}`
+        })
+    }
+    // 点我提升
+    const goGetBagPage = ()  => {
+        Taro.redirectTo({
+             url: '/pages/GetBag/index'
+        });
+    }
+
+    // 下一关
+    const goNextCard = () => {
+        Taro.redirectTo({
+             url: `/pages/TopicPage/index?pointState=${Number(cardId) + 1}`
+        });
+    }
+
+    // 复活再来
+
+    const again = () => {
+        setLifeNum(LifeNum + 1)
+
+        if (LifeNum + 1 < 6) {
+            Taro.redirectTo({
+                 url: `/pages/TopicPage/index?pointState=${cardId}`
+            });
+        } 
+    }
+
+    const RenderCom = () => {
+
+        if (trueQuestionNum <= 2 && timeStamp < 180) {
+            return (
+                <React.Fragment>
+                    <Image className='right' onClick={again} src={require('../../../assets/TopicPage/again.png')} />
+                    <Image className='befated-label' mode='aspectFit' src={require('../../../assets/TopicPage/befated.png')} />
+                    <View className='content befated-content'>
+                        世上无难事,无难事只要肯坚持,
+                        你有一次复活的机会,再来!
+                    </View>
+                </React.Fragment>
+            )
+        } else if (trueQuestionNum >= 3 && timeStamp < 180) {
+            return (
+                <React.Fragment>
+                    <Image className='right' onClick={goNextCard} src={require('../../../assets/TopicPage/next.png')} />
+                    <Image className='pass-label'  mode='aspectFill'  src={require('../../../assets/TopicPage/pass.png')} />
+                    <View className='content pass-content'>
+                        本关用时: {timeStamp}s
+                    </View>
+                </React.Fragment>
+            )
+        }
+
+        if (trueQuestionNum <= 2 && timeStamp === 180 ) {
+            return (
+                <React.Fragment>
+                    <Image className='right' onClick={again} src={require('../../../assets/TopicPage/again.png')} />
+                    <Image className='time-over-label'  mode='aspectFill'  src={require('../../../assets/TopicPage/time-over.png')} />
+                    <View className='content pass-content'>
+                        每关答题时间为3分钟,
+                        你有一次复活的机会,再来!
+                    </View>
+                </React.Fragment>
+            )
+        } else if (trueQuestionNum >= 3 && timeStamp === 180 ) {
+            return (
+                <React.Fragment>
+                    <Image className='right' onClick={goNextCard} src={require('../../../assets/TopicPage/next.png')} />
+                    <Image className='time-over-label'  mode='aspectFill'  src={require('../../../assets/TopicPage/time-over.png')} />
+                    <View className='content pass-content'>
+                        本关用时: {timeStamp}s
+                    </View>
+                </React.Fragment>
+            )
+        }
+
+    }
+
+    const RenderLifeZeroCom = () => {
+        return (
+           <View className='LifeNum-zero'>
+               <Image className='face' src={require('../../../assets/TopicPage/face.png')} />
+               <View className='LifeNum-zero-label'>
+                   <Image className='LifeNum-zero-befated'  src={require('../../../assets/TopicPage/befated.png')} />
+               </View>
+               <View className='LifeNum-zero-content'>
+                    你的6次复活机会用完了哦
+               </View>
+               <Image className='LifeNum-zero-left' onClick={() => quit()} src={require('../../../assets/TopicPage/quit.png')} />
+               <Image className='LifeNum-zero-right' onClick={() => goGetBagPage()} src={require('../../../assets/TopicPage/promote.png')} />
+           </View>
+        )
+    }
+    return (
+        <View className='Modal'>
+            {
+                LifeNum < 6 ? <View className='container'>
+                <Image className='star' src={require(`../../../assets/TopicPage/star${trueQuestionNum}.png`)} />
+                <Image className='left' onClick={() => quit()} src={require('../../../assets/TopicPage/quit.png')} />
+                { RenderCom() }
+                </View> : RenderLifeZeroCom()   
+            }
+           
+        </View>
+    )
+}
+
+export default Modal

+ 37 - 0
src/pages/TopicPage/component/TimeCom.scss

@@ -0,0 +1,37 @@
+.TimeCom {
+    position: absolute;
+    top: 7.51vh;
+    left: 2.4vw;
+    width: 95.2vw;
+    height: 7.39vh;
+    background: #FFFFFF;
+    box-shadow: 0px 4px 8px 0px #33B519;
+    border-radius: 6px;
+    padding: 0 38px 0px 22px;
+    box-sizing: border-box;
+    display: flex;
+    align-items: center;
+    .watch {
+        width: 38px;
+        height: 38px;
+    }
+    .second {
+        width: 102px;
+        height: 52px;
+        background: url('../../../assets/TopicPage/second.png') no-repeat;
+        background-size: 100% 100%;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        font-size: 28px;
+        font-weight: 600;
+        color: #FFFFFF;
+        margin-left: 20px;
+    }
+    .logo {
+        width: 240px;
+        height: 70px;
+        position: absolute;
+        right: 22px;
+    }
+}

+ 23 - 0
src/pages/TopicPage/component/TimeCom.tsx

@@ -0,0 +1,23 @@
+import React from 'react'
+
+import { View, Image, Text,  } from '@tarojs/components'
+
+import './TimeCom.scss'
+
+type TimeComProps = {
+    time: string
+}
+
+const TimeCom: React.FC<TimeComProps> = ({time}) => {
+    return (
+        <View className='TimeCom'>
+            <Image  className='watch' src={require('../../../assets/TopicPage/watch.png')} />
+            <View className='second'>
+                <Text>{time}</Text>
+            </View>
+            <Image className='logo' src={require('../../../assets/TopicPage/logo.png')} />
+        </View>
+    )
+}
+
+export default TimeCom

+ 4 - 0
src/pages/TopicPage/index.config.ts

@@ -0,0 +1,4 @@
+export default {
+    navigationStyle: 'custom'
+}
+  

+ 111 - 0
src/pages/TopicPage/index.scss

@@ -0,0 +1,111 @@
+.TopicPage {
+    width: 100vw;
+    height: 100vh;
+    background-color: rgba(35, 211, 33, 1);
+    font-family: PingFangSC-Regular, PingFang SC;
+    .bg {
+        width: 100vw;
+        height: 250px;
+    }
+}
+
+.Course {
+    width: 95.2vw;
+    height: 83vh;
+    background-color: #fff;
+    margin: 0 auto;
+    position: absolute;
+    top: 15.37vh;
+    left: 2.4vw;
+}
+
+.Topicbg {
+    width: 89.6vw;
+    height: 53.57vh;
+    position: absolute;
+    right: -5vw;
+    top: 25vh;
+}
+
+.show-panel {
+    position: relative;
+    width: 470px;
+    height: 49.51vh;
+    .round {
+        border-radius: 50%;
+        position: absolute;
+        z-index: 2;
+    }
+
+    .game-item {
+        position: absolute;
+    }
+    .show-image {
+        position: absolute;
+    }
+}
+
+.opra-panel {
+    width: 23vw;
+    height: 49.51vh;
+    border: 2px solid #23D321;
+    border-top-right-radius: 20rpx;
+    border-bottom-right-radius: 20rpx;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    align-items: center;
+    position: absolute;
+    top: 20.5%;
+    right: 6vw;
+    background-color: #fff;
+    .answer-item {
+        width: 70%;
+        height: 192px;
+        border-bottom: 2px solid #23D321;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        position: relative;
+        .answer-round {
+            width: 36px;
+            height: 36px;
+            border-radius: 50%;
+            position: absolute;
+            top: 5px;
+            right: 5px;
+        }
+        image {
+            width: 100%;
+            height: 62.5%;
+        }
+    }
+    .answer-item:last-child {
+        border: none;
+    }
+}
+
+.ok {
+    width: 24.2vw;
+    height: 24.2vw;
+    position: fixed;
+    right: 11.87vw;
+    bottom: 3.82vh;
+
+}
+
+// 闪烁动画
+.ficker {
+    animation: fickerAni 2s infinite;
+}
+@keyframes fickerAni {
+    0% {
+        transform: scale(1);
+    }
+    50% {
+        transform: scale(1.4);
+    }
+    100% {
+        transform: scale(0.8);
+    }
+}

+ 411 - 0
src/pages/TopicPage/index.tsx

@@ -0,0 +1,411 @@
+import React, { useState, useEffect, memo , useCallback }  from 'react'
+
+import { View, Image } from '@tarojs/components'
+
+import Taro from '@tarojs/taro'
+
+import './index.scss'
+
+import { getQuestionCard, postQuestionCard } from '../../api/QuestionCard'
+
+import TimeCom from './component/TimeCom'
+
+import BgPanel  from './component/BgPanel'
+
+import Modal from './component/Modal'
+
+import Tip from '../../utils/tip'
+
+const transferTime = (_timeStamp: number): string => {
+
+    const formatTime = ( second ) => second < 10 ? '0' + second : second
+
+    let cache = 0
+    if (_timeStamp < 60) {
+       return `0:${formatTime(_timeStamp)}`
+   
+    } else if ( _timeStamp >= 60 && _timeStamp < 120 ) {
+
+         cache = _timeStamp - 60
+        return `1:${formatTime(cache)}`
+
+    } else if ( _timeStamp >= 120 && _timeStamp < 180) {
+        cache = _timeStamp - 120
+
+        return `2:${formatTime(cache)}`
+    }
+
+    return ''
+ 
+}
+
+type CourseType = {
+    fickerId: number,
+    content: Record<string, any>,
+    answerList: Record<string, any>[],
+    label: string,
+    clickAnswer: (item: any) => void,
+    PostQuestionCard: () => void
+
+}
+
+let count: number = 0
+
+interface IScreen {
+    WScreen: number,
+    HScreen: number
+}
+
+// 点击的过程卡片
+const Course: React.FC<CourseType> =  ({
+    fickerId,
+    content,
+    answerList,
+    label,
+    clickAnswer,
+    PostQuestionCard
+}) => {
+    
+    // 屏幕信息比率
+    const [ screen, setScreen ]  = useState<IScreen>({WScreen: 0, HScreen: 0})
+
+    console.log('cccc', count += 1);
+
+    useEffect( () => {
+        Taro.getSystemInfo({
+            success: function (res) {
+                const {screenWidth, screenHeight} = res
+                setScreen({WScreen: screenWidth / 375, HScreen: screenHeight / 812})
+            }
+        })
+    }, [])
+
+    // const RenderCom = memo(() =>   />) 
+
+
+    return (
+        <View className='Course'>
+            <BgPanel label={label} />
+            {/*  */}
+           
+
+            {/* 绿色背景 */}
+            <Image className='Topicbg' src={require('../../assets/TopicPage/Topicbg.png')} />
+
+            {/* 左侧显示面板 */}
+            <View className='show-panel'>
+                {/* 展示图 */}
+                {
+                   content.length && content?.map( item => (
+                       <View 
+                         className='game-item' 
+                         key={item.id}
+                         style={{
+                             width: item.pw * screen.WScreen ,
+                             height: item.ph * screen.HScreen ,
+                             top: item.py * screen.HScreen,
+                             left:  item.px *  screen.WScreen, 
+                         }}
+                       >
+                           {/* 圆 */}
+                           {
+                               item.bg === 'false' &&  <View 
+                                 className={['round',  item.id === fickerId? 'ficker' : ''].join(',')}
+                                 style={{
+                                 background: item.color,
+                                 width: item.iw  * screen.WScreen,
+                                 height: item.ih  * screen.HScreen,
+                                 top: item.iy * screen.HScreen,
+                                 left: item.ix *  screen.WScreen
+                              }}
+                               />
+                           }
+                           
+
+                           {/* 图片 */}
+                           <Image
+                             className={['show-image'].join(',')}
+                             src={item.imgUrl}
+                             mode='aspectFit'
+                             style={{
+                                 width:   item.w * screen.WScreen  ,
+                                 height:  item.h * screen.HScreen ,
+                                 top:  item.y * screen.HScreen,
+                                 left:  item.x *  screen.WScreen ,
+                                 position: 'absolute'
+                             }}
+                           />
+                       </View>
+                       
+                   )) 
+                }
+                
+            </View>
+            {/* 右侧选择面板 */}
+            <View className='opra-panel' >
+                {
+                    answerList?.map( item => (
+                        <View className='answer-item' key={item.id} onClick={() => clickAnswer(item)}>
+                            <View  className='answer-round' style={{background: item.yourAnswer}} />
+                            <Image  mode='aspectFit' src={item.imgUrl} />
+                        </View>
+                    ))
+                }
+            </View>
+            
+            {/* ok 提交按钮 */}
+            <Image mode='aspectFill' onClick={() => PostQuestionCard()} className='ok' src={require('../../assets/TopicPage/ok.png')} />
+
+          
+        </View>
+    )
+}
+
+const CoursePage = memo((props: CourseType) => <Course {...props} />)
+
+
+let timeStamp: number = 0
+
+const TopicPage: React.FC = () => {
+
+    // 题卡id
+    const [ cardId, setCardId]  = useState<string>('')
+
+    const [ visible, setVisible ] = useState<boolean>(false)
+
+    const [time, setTime] = useState<string>("0:00")
+
+    // 当前闪烁下标
+    const [ fickerId, setFickerId ] = useState<number>(0)
+
+    // 闪烁颜色
+    const [ fickerColor, setFickerColor ] = useState<string>('')
+
+    const [ content, setContent ] = useState<Record<string, any>>({})
+
+    const [ answerList, setAnswerList] = useState<Record<string, any>>([])
+
+    // 正确数
+    const [TrueQuestionNum, setTrueQuestionNum] = useState<number>(0)
+
+    // 复活次数
+    const [ LifeNum, setLifeNum ] = useState<number>(0)
+
+    // 题卡标题
+    const [ label, setLabel ] = useState<string>('')
+
+    // 时间
+    // const [ timeStamp, setTimeStamp ] = useState<number>(0) 
+
+    // 定时器
+    const [timeId, setTimeId] = useState<any>('')
+
+    useEffect( () => {
+        const _timeId = setInterval( () => {
+
+            timeStamp += 1
+
+            setTime(transferTime(timeStamp))
+
+            
+        }, 1000)
+        setTimeId(_timeId)
+
+        return () => {
+            
+            stopSetinterval(_timeId)
+        }
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [])
+
+    
+    // 获取屏幕信息及其路由参数 
+    useEffect( () => {
+
+        const routeParams =  Taro.getCurrentInstance().router?.params.pointState as string
+
+        setCardId(routeParams)
+
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [])
+
+    useEffect( () => {
+        cardId && GetQuestionCard()
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [cardId])
+
+    useEffect( () => {
+        setFickerColor(content.length && content.filter( item => item.id === fickerId )[0]?.color )
+    }, [content, fickerId])
+
+    // 获取题卡
+    const GetQuestionCard = async () => {
+        
+        Tip.loading()
+        const data = await getQuestionCard(cardId)
+        Tip.loaded()
+        setContent(data.content.map(item => {
+            return {...item, flag: false}
+        }))
+        
+        setAnswerList(data.options)
+
+        if (data.lifeNum >= 6) {
+            
+            openModal()
+        }
+
+        setLifeNum(data.lifeNum)
+        setLabel(data.label)
+
+        setFickerId(data.content.find( item => !item.flag && item.bg === 'false')?.id )
+    }
+
+    // 提交题卡
+    const PostQuestionCard = useCallback( async () => {
+
+        const $par = {
+            cardId: cardId,
+            takeTime: timeStamp,
+            optionAnswers: answerList.map(item => {
+                return {
+                    ...item,
+                    optionId: item.id
+                }
+            })
+        }
+
+        Tip.loading()
+        const data = await postQuestionCard($par)
+        Tip.loaded()
+        console.log(data);
+
+        setTrueQuestionNum(data.trueQuestionNum)
+
+        // stopInterval(timer)
+
+        openModal()
+
+        let cardIdList: Record<string, any>[] = []
+
+
+        console.log(Taro.getStorageSync('cardIdList'), 'Taro.getStorageSync');
+        
+        const getOldCardIdList = Taro.getStorageSync('cardIdList') instanceof Array ?  Taro.getStorageSync('cardIdList').filter( item => item.key !== cardId) : []
+
+        console.log(getOldCardIdList, 'getOldCardIdList');
+        
+        cardIdList.push({
+            key: cardId,
+            id: data.id
+        })
+
+        // 存储题卡Id
+        if (getOldCardIdList.length > 0) {
+            cardIdList = cardIdList.concat(getOldCardIdList)
+        } 
+
+
+        Taro.setStorageSync('cardIdList', cardIdList)
+
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [])
+ 
+
+    // 点击选择当前答案  选择当前的颜色
+    const clickAnswer = useCallback( (item: Record<string, any>) => {
+
+        const deepItem = JSON.parse(JSON.stringify(item))
+
+        const deepAnswerList = JSON.parse( JSON.stringify(answerList))
+
+        const deepContent = JSON.parse(JSON.stringify(content))
+
+        // 如果存在 yourAnswer 则说明是取消选择
+        if (deepItem.yourAnswer) {
+
+            deepItem.yourAnswer = ''
+
+            const index = deepAnswerList.findIndex(answer => answer.id === deepItem.id)
+
+            deepAnswerList.splice(index, 1, deepItem)
+
+            deepContent.forEach( _content => {
+                if (_content.id === deepItem.bindId) {
+                    _content.flag = false
+                }
+            })
+            
+            setFickerId(deepItem.bindId)
+            setAnswerList(deepAnswerList)
+            setContent(deepContent)
+
+            return
+        }
+
+        deepItem.yourAnswer = fickerColor
+
+        deepItem.bindId = fickerId
+
+        const index = deepAnswerList.findIndex(answer => answer.id === deepItem.id)
+
+        console.log(index, 'index');
+        
+
+        deepAnswerList.splice(index, 1, deepItem)
+        
+        setAnswerList(deepAnswerList)
+
+        deepContent.forEach( _content => {
+            if (_content.id === fickerId) {
+                _content.flag = true
+            }
+        })
+
+        setContent(deepContent)
+
+        setFickerId(deepContent.find( _item => !_item.flag && _item.bg === 'false')?.id )
+
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+    },[]) 
+
+    // 做完题目后弹窗
+    const openModal = useCallback(() => {
+        
+        setVisible(true)
+        stopSetinterval(timeId)
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [])
+
+    // 关闭定时器
+    const stopSetinterval = (_timeId) => {
+        console.log('???');
+        clearInterval(_timeId)
+    }
+
+    return (
+        <View className='TopicPage'>
+            <Image className='bg' src={require('../../assets/TopicPage/bg.png')} />
+            <TimeCom  time={time} />
+            <CoursePage 
+              fickerId={fickerId}
+              label={label}
+              content={content}
+              answerList={answerList as  Record<string, any>[]}
+              PostQuestionCard={PostQuestionCard}
+              clickAnswer={clickAnswer}
+            />
+
+              {/* 弹窗 */}
+            { visible ? <Modal 
+              trueQuestionNum={TrueQuestionNum}  
+              LifeNum={LifeNum} 
+              cardId={cardId} 
+              setLifeNum={setLifeNum}
+              timeStamp={timeStamp}
+            /> : null }
+        </View>
+    )
+}
+
+export default TopicPage

+ 4 - 0
src/pages/WebView/index.config.ts

@@ -0,0 +1,4 @@
+export default {
+    navigationStyle: 'custom'
+}
+  

+ 7 - 0
src/pages/WebView/index.tsx

@@ -0,0 +1,7 @@
+import React from 'react'
+
+import { WebView } from '@tarojs/components'
+
+const WebViewPage: React.FC = () => <WebView src='http://lps.eqxiul.com/ls/Hb1xk5r7?eqrcode=1&toPage=11620877' />
+
+export default WebViewPage

+ 29 - 0
src/pages/index/index.scss

@@ -52,4 +52,33 @@
     height: 236px;
     left: 34.13vw;
     bottom: 6.19vh;
+    background-color: transparent;
+    color: none;
+    border: none;
+    padding: 0px;
+    margin: none;
+    .btn {
+        width: 100%;
+        height: 100%;
+        animation: ani 3s infinite;
+    }
+}
+
+
+@keyframes ani {
+    0% {
+        transform: scale(1); 
+    }
+    50% {
+        transform: scale(.8);
+    }
+    100% {
+        transform: scale(1); 
+    }
+}
+
+
+.start-btn::after {
+    border: none;
+    padding: none;
 }

+ 66 - 10
src/pages/index/index.tsx

@@ -1,7 +1,7 @@
 /* eslint-disable import/no-commonjs */
-import React, { useEffect, useState } from 'react'
+import React, { useState, useEffect } from 'react'
 
-import { View, Text, Image} from '@tarojs/components'
+import { View,  Image, Button} from '@tarojs/components'
 
 import { AtCurtain } from 'taro-ui'
 
@@ -9,10 +9,18 @@ import Taro from '@tarojs/taro'
 
 import './index.scss'
 
+import { login } from '../../api/My'
+
+import Tip from '../../utils/tip'
+
 const Index: React.FC = () => { 
 
   const [ visible, setVisble ] = useState<boolean>(false)
 
+  // 是否下下移获取大礼包背景板 --> iphone x及以上机型下移
+
+  const [ moveState, setMoveState ] = useState<boolean>(false) 
+
   // 跳转页面
   const goPage = () => {
     Taro.navigateTo({
@@ -20,6 +28,51 @@ const Index: React.FC = () => {
     });
   }
 
+  // 去领取物品页面
+  const goGetBagPage = () => {
+    Taro.navigateTo({
+       url: '/pages/GetBag/index'
+    });
+ }
+
+ useEffect( () => {
+  Taro.getSystemInfo({
+    success: function (res) {
+      const { windowHeight } = res
+      if (windowHeight >= 812) {
+        setMoveState(true)
+      }
+
+    }
+  });
+ }, [])
+
+  //  获取用户信息
+  const getUserInfo = async (e) => {
+    const token = Taro.getStorageSync('userInfo').token
+    if (!token) {
+      Tip.loading('登陆中')
+      const { code } =  await Taro.login()
+
+      const { nickName, avatarUrl} = e.detail.userInfo
+
+      const $par =  {
+        nickName,
+        avatarUrl,
+        jsCode: code
+      }
+
+
+      const data = await login($par)
+
+      console.log(data);
+
+      Taro.setStorageSync('userInfo', data)
+    } 
+
+    goPage()
+    
+  }
 
   // 渲染弹窗
   const RenderRuler = () => {
@@ -37,22 +90,25 @@ const Index: React.FC = () => {
         <Image 
           mode='scaleToFill'
           className='bg'
-          src={require('../../assets/index/index1.png')}
+          src={require('../../assets/index/index1-X.png')}
         />
 
         <Image className='ruler' onClick={() => setVisble(true)} src={require('../../assets/index/index2.png')} />
 
-        <View className='activity'>
+        <View className='activity' style={{top: moveState ? '52.83vh': '45.88vh'}}>
           <Image className='gift-bag' src={require('../../assets/index/index3.png')} />
 
-          <Image className='get-btn' src={require('../../assets/index/index5.png')} />
+          <Image onClick={goGetBagPage} className='get-btn' src={require('../../assets/index/index5.png')} />
         </View>
 
-        <Image 
-          className='start-btn' 
-          src={require('../../assets/index/index4.png')}
-          onClick={goPage}
-        />
+        <Button className='start-btn' openType='getUserInfo' onGetUserInfo={getUserInfo} >
+          <Image 
+            className='btn'
+            src={require('../../assets/index/index4.png')}
+         
+          />
+        </Button>
+        
         
 
         {/* 活动规则 */}

+ 5 - 1
src/service/config.ts

@@ -14,4 +14,8 @@ export const errorCode: number = 0
 
 /**
  * 错误描述
- */
+ */
+
+//  export const BASEURL = 'https://open.luojigou.vip'
+//  export const BASEURL = 'http://192.168.1.21:38094'
+ export const BASEURL = 'https://open.luojigou.vip/ggw'

+ 75 - 29
src/service/request.ts

@@ -1,6 +1,8 @@
 import Taro from '@tarojs/taro'
 import Tip from '../utils/tip'
-import { errorKey, errorCode } from './config'
+import { errorKey, errorCode, BASEURL } from './config'
+
+import { login } from '../api/My'
 
 type paramsType = {
     baseUrl?: string,
@@ -14,67 +16,111 @@ type paramsType = {
 
 // 错误处理
 const errorHandle = (statusCode: number, code: number,  message: string, url: string) => {
-    console.log(code, 'code');
+
     
     if (code === errorCode) {
         // 请求很不错
+        // urlPool = []
     } else if (code === 403) {
 
     }
-    else if (statusCode !== 200) {
-        Tip.toast('啊哦, 您的网络出了一点小问题')
-        throw new Error(`${url} ---> 接口请求失败`)
-    }  else if (code !== errorCode) {
+    // else if (statusCode !== 200) {
+    //     Tip.toast('啊哦, 您的网络出了一点小问题')
+    //     throw new Error(`${url} ---> 接口请求失败`)
+    // }  
+    else if (code !== errorCode) {
+        console.log(message);
+        
         Tip.toast(message)
-        throw new Error(`${url} ---> 接口请求失败`)
+        // throw new Error(`${url} ---> 接口请求失败`)
     }
+    Tip.loaded()
+    
 }
 
+
+// 请求池
+let urlPool: paramsType[] = [] 
+
+
+// 是否挂起后续 请求重新登录的请求
+
+let deferReuqest: boolean = false 
+
+let relogin = () => {
+
+    if (deferReuqest) {
+        return null
+    }
+
+    deferReuqest = true
+
+    return new Promise( async resolve => {
+        Tip.loading()
+
+        const res = await Taro.login()
+
+        const { code } = res
+
+        const  data = await login({jsCode: code})
+
+        Taro.setStorageSync('userInfo', data)
+
+        resolve(true)
+
+
+    
+        Tip.loaded()
+    })  
+}
+
+
+
 // 请求
 export const request = async (params: paramsType): Promise<any> => {
     const {baseUrl, url, method, data, header, timeout, responseType} = params
+    
     return new Promise( async resolve => {
         const result = await Taro.request({
-            url: baseUrl + url,
+            url:  baseUrl ? baseUrl + url : BASEURL + url,
             method: method || 'GET',
             data,
             header: {
                 'Content-Type': 'application/json',
-                token: Taro.getStorageSync('token'),
+                token: Taro.getStorageSync('userInfo').token,
                 ...header
             },
             timeout,
             responseType
         });
         
+
+        console.log(result);
+        
         // eslint-disable-next-line no-shadow
         const { statusCode  } = result
-        const { message, data: Data } = result.data
+        const { msg, data: Data } = result.data
         console.log(Data);
         
-        errorHandle (statusCode, result.data[errorKey], message, baseUrl + url)
+    
+
 
-        if (result.data.status === 403) {
+        
+        if (result.data.code === 5001) {
             // token失效, 刷新token, 并重发请求
-            console.log('????');
-              Tip.loading()
-            //   Taro.login({
-            //       success: res => {
-            //           console.log(res);
-            //           const { code: Code } = res
-            //           reloadlogin(Code).then ( async _ => {
-            //               Taro.setStorageSync('token', _.id)
-                          
-            //           })
-            //       }
-            //   })
-
-              // resolve(await request(params))
-              Tip.loaded()
-  
-              return 
+            urlPool.push(params)
+            relogin()?.then( _ => {
+                if (_) {
+                    urlPool.forEach( async (item: paramsType) =>  resolve(await request(item)))
+                }
+            })
+
+            return
+
           }
 
+          errorHandle (statusCode, result.data[errorKey], msg, baseUrl + url)
+
           resolve(Data)
     }).catch( e => e)    
 }

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels