Quellcode durchsuchen

feat: 练习报告

zhubo vor 2 Jahren
Ursprung
Commit
678107992f
5 geänderte Dateien mit 574 neuen und 0 gelöschten Zeilen
  1. 14 0
      src/api/practice.ts
  2. 14 0
      src/pages.json
  3. 13 0
      src/pages/Practice/course.vue
  4. 521 0
      src/pages/Practice/report.vue
  5. 12 0
      src/typing.d.ts

+ 14 - 0
src/api/practice.ts

@@ -0,0 +1,14 @@
+import { request } from '@/service/index'
+
+/**
+ * @param {string} scoreId - 成绩id
+ * @returns 练习报告详情
+ */
+export const getPracticeDetailByScoreId = (scoreId: string) => {
+    return request({
+        url: `/gamecontest/v2/score/practice/detail?scoreId=${scoreId}`,
+        method: 'GET',
+    })
+}
+
+

+ 14 - 0
src/pages.json

@@ -18,6 +18,20 @@
 				"navigationStyle": "custom",
 				"disableScroll": true
 			}
+		},
+		{
+			"path": "pages/Practice/report",
+			"style": {
+				"navigationBarTitleText": "练习报告",
+				"navigationBarBackgroundColor": "#fff"
+			}
+		},
+		{
+			"path": "pages/Practice/course",
+			"style": {
+				"navigationBarTitleText": "练习报告",
+				"navigationBarBackgroundColor": "#fff"
+			}
 		}
 	],
 	"globalStyle": {

+ 13 - 0
src/pages/Practice/course.vue

@@ -0,0 +1,13 @@
+<template>
+    <view class="course">
+        <web-view :src="url"></web-view>
+    </view>
+</template>
+
+<script lang="ts" setup>
+const url = 'https://zaojiao.net/luojigou_web_shop_html/#/acPage?id=1628281115852095490'
+</script>
+
+<style scoped>
+
+</style>

+ 521 - 0
src/pages/Practice/report.vue

@@ -0,0 +1,521 @@
+<template>
+    <view class="pr">
+        <image class="pr-gift" :src="STATIC_IMAGES.gift" mode="aspectFit" @click="jumpGift"></image>
+        <view class="pr-score">
+            <view class="pr-score-top" :style="{backgroundImage: `url(${STATIC_IMAGES.topBGI})`}">
+                答对{{ state.practice?.rightCount }}题,共{{ state.practice?.totalCount }}题
+            </view>
+            <image class="pr-score-cloud" :src="STATIC_IMAGES.cloud" mode="aspectFit"></image>
+            <image class="pr-score-dog" :src="STATIC_IMAGES.dog" mode="aspectFit"></image>
+            <view class="pr-score-content" :style="{backgroundImage: `url(${STATIC_IMAGES.contentBGI})`}">
+                <view class="pr-score-content-num">{{ state.practice?.rightCount }}/</view>
+                <view class="pr-score-content-total">{{ state.practice?.totalCount }}</view>
+            </view>
+            <view class="pr-score-bottom">
+                <view class="pr-score-bottom-item" v-for="item in INFO_LIST" :key="item.id">
+                    <text>{{item.name}}</text>
+                    <text class="high">{{handleInfo(item.key)}}</text>
+                </view>
+            </view>
+        </view>
+        <view class="pr-card" v-for="(item, index) in state.practice.userAnswer">
+            <view class="pr-card-top">
+                <view class="pr-card-top-title">{{ item.title }}</view>
+                <view class="pr-card-top-desc">卡片{{ index + 1 }}</view>
+            </view>
+            <view class="pr-card-info">
+                <view class="pr-card-info-label">{{item.ableVar }}</view>
+                <view class="pr-card-info-time">
+                    <image :src="STATIC_IMAGES.timer" mode="aspectFit"></image>
+                    <text>{{item.takeTime }}s</text>
+                </view>
+            </view>
+            <view class="pr-card-star">
+                <image
+                    v-for="star in item.rightOptionCount"
+                    :key="star"
+                    :src="STATIC_IMAGES.highStar"
+                    mode="aspectFit"
+                ></image>
+                <image
+                    v-for="star in item.totalOptionCount - item.rightOptionCount"
+                    :key="star"
+                    :src="STATIC_IMAGES.star"
+                    mode="aspectFit"
+                ></image>
+            </view>
+        </view>
+        <view class="pr-recommend">
+            <view class="pr-recommend-title">提升推荐</view>
+            <image :src="STATIC_IMAGES.recommend" mode="aspectFit" @click="openMiniProgram"></image>
+        </view>
+        <view class="pr-more">
+            <view class="pr-more-title">更多宝宝学习计划</view>
+            <view class="pr-more-content">
+                <view class="pr-more-content-left">
+                    <view class="pr-more-content-left-title">更多宝贝启蒙福利</view>
+                    <view class="welfare">
+                        <view v-for="item in WELFARE_LIST" :key="item.id" class="welfare-item">
+                            <image :src="STATIC_IMAGES.mark" mode="aspectFit"></image>
+                            <view>{{item.name}}</view>
+                        </view>
+                    </view>
+                    <image class="pr-more-content-left-btn" :src="STATIC_IMAGES.btn" mode="aspectFit"></image>
+                </view>
+                <image
+                    class="pr-more-content-code"
+                    :src="STATIC_IMAGES.code"
+                    mode="aspectFill"
+                    show-menu-by-longpress
+                ></image>
+            </view>
+        </view>
+        <view class="pr-share">
+            <image :src="STATIC_IMAGES.share"></image>
+        </view>
+    </view>
+</template>
+
+<script lang="ts" setup>
+import {computed, reactive} from "vue";
+import {onLoad} from "@dcloudio/uni-app";
+import {getPracticeDetailByScoreId} from '@/api/practice'
+interface QueryParams {
+    id: string,
+}
+
+interface State {
+    scoreId: string,
+    practice: Partial<API.Practice> | null
+}
+
+const STATIC_IMAGES = {
+    gift: "https://img.luojigou.vip/FmwR1PujFE7gzFVB9zZWgI0B9Y_K",
+    topBGI: "https://img.luojigou.vip/FhkO2X4z8dq2pCWROrSTtvp74RrA",
+    cloud: "https://img.luojigou.vip/Fralw2fXHmSbe6dMmAAXvpHjwQpe",
+    dog: "https://img.luojigou.vip/FqS0G4tJfwStzaB0q9kLHGmBTmM2",
+    contentBGI: "https://img.luojigou.vip/FpshEMbGAig57gJLl_XowZ9-_d2O",
+    timer: "https://img.luojigou.vip/Fujv5BArFDJ2AtQ8an5LzNxindbL",
+    highStar: "https://img.luojigou.vip/Fu3EQtCfehtiO5VwZ1vu7QCrP1Kw",
+    star: "https://img.luojigou.vip/FoY0UunWCZTmvoJNPyJgon9E3Vji",
+    recommend: "https://img.luojigou.vip/FlcTV-wuDWfUh4p9vU1UjCaSR8RA",
+    code: "https://img.luojigou.vip/FkUhmOKcaNSP4HGER7-xBbnGspLw",
+    mark: "https://img.luojigou.vip/Fty-uqpNksTTJnJkhDNPNPsFXsRa",
+    btn: "https://img.luojigou.vip/FiKBJMkl2ZGwJCz-u77bWDi2GVKX",
+    share: "https://img.luojigou.vip/FrmMXUNh4aLXV1XlPFHtRNFKUqs4"
+}
+
+const INFO_LIST = [
+    {id: 0, name: '总时间:', key: 'totalTime'},
+    {id: 1, name: '正确率:', key: 'accuracy'},
+    {id: 2, name: '时间:', key: 'createTime'},
+]
+
+const WELFARE_LIST = [
+    {id: 0, name: '免费课程'},
+    {id: 1, name: '干货资料'},
+    {id: 2, name: '大额折扣'},
+    {id: 3, name: '专属答疑'},
+]
+
+onLoad(query => {
+    const options = query as QueryParams
+    state.scoreId = options.id
+    getDetail()
+})
+
+
+const state = reactive<State>({
+    scoreId: '',
+    practice: {
+        accuracy: 0,
+        answerItems: [],
+        createTime: '',
+        id: '',
+        rightCount: 0,
+        scoreId: '',
+        totalCount: 0,
+        totalTime: 0,
+        userAnswer: [],
+    }
+})
+
+const handleInfo = computed(() => {
+    return (key: string) => {
+        if(key === 'totalTime' && state.practice?.totalTime) {
+            return state.practice?.totalTime + 's'
+        } else if(key === 'accuracy' && state.practice?.accuracy) {
+            return state.practice.accuracy * 100 + '%'
+        } else if(key === 'createTime' && state.practice?.createTime) {
+            return state.practice?.createTime.split(' ')[0]
+        } else {
+            return ''
+        }
+    }
+})
+
+const getDetail = async () => {
+    const {result} = await getPracticeDetailByScoreId(state.scoreId)
+    state.practice = result
+}
+
+const openMiniProgram = () => {
+    uni.navigateToMiniProgram({
+        appId: 'wx01e79cb36b9436ee',
+        success(res) {
+            // 打开成功
+            console.log(res)
+        }
+    })
+}
+
+const jumpGift = () => {
+    uni.navigateTo({
+        url: '/pages/Practice/course'
+    });
+}
+</script>
+
+<style lang="less" scoped>
+.pr {
+    padding-bottom: 240rpx;
+    width: 100vw;
+    min-height: 100vh;
+    overflow: hidden;
+    background-color: #F6F6F6;
+
+    image {
+        display: block;
+    }
+
+    >view {
+        margin-left: auto;
+        margin-right: auto;
+    }
+
+    &-gift {
+        margin: 30rpx auto 0;
+        width: 702rpx;
+        height: 162rpx;
+    }
+
+    &-score {
+        position: relative;
+        margin-top: 64rpx;
+        padding-top: 54rpx;
+        width: 702rpx;
+        height: 484rpx;
+        background: #FFFFFF;
+        box-shadow: 0 4rpx 20rpx 0 #E9E9EA;
+        border-radius: 20rpx;
+
+        &-top {
+            position: absolute;
+            top: -32rpx;
+            left: 50%;
+            transform: translateX(-50%);
+            padding-top: 54rpx;
+            width: 416rpx;
+            height: 100rpx;
+            background-repeat: no-repeat;
+            background-size: 100% 100%;
+            font-size: 24rpx;
+            font-family: PingFangSC-Medium, PingFang SC;
+            font-weight: 600;
+            color: #FF862B;
+            line-height: 34rpx;
+            box-sizing: border-box;
+            text-align: center;
+        }
+
+        &-cloud {
+            position: absolute;
+            top: 86rpx;
+            left: 18rpx;
+            width: 192rpx;
+            height: 82rpx;
+        }
+
+        &-dog {
+            position: absolute;
+            right: 56rpx;
+            bottom: 88rpx;
+            width: 82rpx;
+            height: 116rpx;
+        }
+
+        &-content {
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            position: absolute;
+            top: 98rpx;
+            left: 50%;
+            transform: translateX(-50%);
+            width: 306rpx;
+            height: 306rpx;
+            background-repeat: no-repeat;
+            background-size: 100% 100%;
+
+            &-num {
+                font-size: 172rpx;
+                font-family: PingFangSC-Semibold, PingFang SC;
+                font-weight: 600;
+                color: #FF8024;
+            }
+
+            &-total {
+                margin-top: 100rpx;
+                margin-left: -20rpx;
+                font-size: 60rpx;
+                color: #DBDCDB;
+            }
+        }
+
+        &-bottom {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            position: absolute;
+            bottom: 26rpx;
+            left: 50%;
+            transform: translateX(-50%);
+            width: 628rpx;
+            height: 40rpx;
+
+            &-item {
+                display: flex;
+
+                text {
+                    font-size: 24rpx;
+                    font-family: PingFangSC-Regular, PingFang SC;
+                    font-weight: 400;
+                    color: #C0C1C3;
+                    line-height: 34rpx;
+                }
+
+                .high {
+                    font-size: 28rpx;
+                    color: #FF8024;
+                }
+            }
+        }
+    }
+
+    &-card {
+        padding: 42rpx 44rpx 78rpx;
+        margin-top: 26rpx;
+        width: 702rpx;
+        height: 370rpx;
+        background: #FFFFFF;
+        box-shadow: 0rpx 4rpx 20rpx 0rpx #E9E9EA;
+        border-radius: 20rpx;
+        overflow: hidden;
+        box-sizing: border-box;
+
+        &-top {
+            display: flex;
+            align-items: center;
+            margin-bottom: 12rpx;
+
+            &-title {
+                margin-right: 20rpx;
+                height: 50rpx;
+                font-size: 36rpx;
+                font-family: PingFangSC-Medium, PingFang SC;
+                font-weight: 500;
+                color: #333330;
+                line-height: 50rpx;
+            }
+
+            &-desc {
+                height: 34rpx;
+                font-size: 24rpx;
+                font-family: PingFangSC-Medium, PingFang SC;
+                font-weight: 500;
+                color: #A7A7B2;
+                line-height: 34rpx;
+            }
+        }
+
+        &-info {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding-bottom: 18rpx;
+            border-bottom: 1rpx solid #EDEFED;
+
+            &-label {
+                display: flex;
+                justify-content: center;
+                align-items: center;
+                padding: 0 10rpx;
+                height: 32rpx;
+                border-radius: 52rpx;
+                border: 1rpx solid #FF8024;
+                font-size: 20rpx;
+                font-family: PingFangSC-Regular, PingFang SC;
+                font-weight: 400;
+                color: #FF8024;
+            }
+
+            &-time {
+                display: flex;
+                align-items: center;
+
+                image {
+                    margin-right: 14rpx;
+                    width: 34rpx;
+                    height: 34rpx;
+                }
+                text {
+                    margin-right: 16rpx;
+                    height: 44rpx;
+                    font-size: 32rpx;
+                    font-family: PingFangSC-Medium, PingFang SC;
+                    font-weight: 500;
+                    color: #FF8024;
+                    line-height: 44rpx;
+                }
+            }
+        }
+
+        &-star {
+            display: flex;
+            justify-content: space-evenly;
+            align-items: center;
+            margin-top: 34rpx;
+
+            image {
+                width: 98rpx;
+                height: 98rpx;
+            }
+        }
+    }
+
+    &-recommend {
+        margin-top: 80rpx;
+        width: 722rpx;
+        overflow: hidden;
+
+        &-title {
+            margin-left: 10rpx;
+            margin-bottom: 20rpx;
+            height: 50rpx;
+            font-size: 36rpx;
+            font-family: PingFangSC-Semibold, PingFang SC;
+            font-weight: 600;
+            color: #333330;
+            line-height: 50rpx;
+        }
+
+        image {
+            width: 722rpx;
+            height: 388rpx;
+        }
+    }
+
+    &-more {
+        margin-top: 62rpx;
+        width: 702rpx;
+
+        &-title {
+            margin-bottom: 34rpx;
+            height: 50rpx;
+            font-size: 36rpx;
+            font-family: PingFangSC-Semibold, PingFang SC;
+            font-weight: 600;
+            color: #333330;
+            line-height: 50rpx;
+        }
+
+        &-content {
+            display: flex;
+            justify-content: space-between;
+            width: 702rpx;
+            height: 292rpx;
+            background: #FFFFFF;
+            box-shadow: 0rpx 4rpx 20rpx 0rpx #E9E9EA;
+            border-radius: 20rpx;
+
+            &-left {
+                margin-top: 28rpx;
+                margin-left: 38rpx;
+                width: 360rpx;
+
+                &-title {
+                    margin-bottom: 16rpx;
+                    height: 60rpx;
+                    font-size: 32rpx;
+                    font-family: PingFangSC-Medium, PingFang SC;
+                    font-weight: 600;
+                    color: #333330;
+                    line-height: 60rpx;
+                }
+
+                .welfare {
+                    display: flex;
+                    flex-wrap: wrap;
+
+                    &-item {
+                        display: flex;
+                        align-items: center;
+
+                        image {
+                            margin-right: 8rpx;
+                            width: 20rpx;
+                            height: 20rpx;
+                        }
+
+                        view {
+                            height: 28rpx;
+                            font-size: 26rpx;
+                            font-family: PingFangSC-Regular, PingFang SC;
+                            font-weight: 400;
+                            color: #666666;
+                            line-height: 28rpx;
+                        }
+                    }
+
+                    .welfare-item:nth-child(2n+1) {
+                        margin-right: 38rpx;
+                    }
+
+                    .welfare-item:nth-child(-n+2) {
+                        margin-bottom: 14rpx;
+                    }
+                }
+
+                &-btn {
+                    margin-top: 30rpx;
+                    width: 270rpx;
+                    height: 58rpx;
+                }
+            }
+
+            &-code {
+                margin-top: 10rpx;
+                width: 284rpx;
+                height: 276rpx;
+            }
+        }
+    }
+
+    &-share {
+        position: fixed;
+        bottom: 0;
+        left: 0;
+        width: 100vw;
+        height: 208rpx;
+        background: #FFFFFF;
+        box-shadow: 0rpx -8rpx 68rpx 0rpx rgba(0,0,0,0.1);
+        overflow: hidden;
+
+        image {
+            margin: 36rpx auto 0;
+            width: 694rpx;
+            height: 96rpx;
+        }
+    }
+}
+</style>

+ 12 - 0
src/typing.d.ts

@@ -28,6 +28,7 @@ declare namespace API {
 
   type Response<T> = {
     data: T,
+    result?: any,
     message: string,
     status: status
   }
@@ -66,6 +67,17 @@ declare namespace API {
     ansType: 0 | 1 // 答题模式 0 顺序 | 1 随机
     cardIds: string[]
   }
+  interface Practice {
+    accuracy: number
+    answerItems: []
+    createTime: string
+    id: string
+    rightCount: number
+    scoreId: string
+    totalCount: number
+    totalTime: number
+    userAnswer: []
+  }
   
   interface UseSchedulerFastCall {
     timeId: number | null,