feat:学情报告数据显示
This commit is contained in:
parent
e0fc132885
commit
defc2ab5f1
@ -8,21 +8,23 @@ import { storeToRefs } from 'pinia'
|
|||||||
import { useBadgeStore } from '@/store/badge'
|
import { useBadgeStore } from '@/store/badge'
|
||||||
import { useChildStore } from '@/store/child'
|
import { useChildStore } from '@/store/child'
|
||||||
import hud from '@/utils/hud'
|
import hud from '@/utils/hud'
|
||||||
import { getStudentStudyDataApi } from '@/api'
|
import { useStudyDataStore } from '@/store/study-data'
|
||||||
|
import { secondsToHours } from '@/utils'
|
||||||
|
|
||||||
const childStore = useChildStore()
|
const childStore = useChildStore()
|
||||||
const { getChildren } = childStore
|
const { getChildren } = childStore
|
||||||
const { children } = storeToRefs(childStore)
|
const { children } = storeToRefs(childStore)
|
||||||
const { refreshBadge } = useBadgeStore()
|
const { refreshBadge } = useBadgeStore()
|
||||||
|
const { getStudentStudyData } = useStudyDataStore()
|
||||||
|
|
||||||
const showChild = ref<ChildrenType>() // 展示的孩子
|
const showChild = ref<ChildrenType>() // 展示的孩子
|
||||||
|
const showChildData = ref({} as any)
|
||||||
const OSS_URL = import.meta.env.VITE_OSS_HOST
|
const OSS_URL = import.meta.env.VITE_OSS_HOST
|
||||||
const arrow = `${OSS_URL}/iconfont/down_arrow.png`
|
const arrow = `${OSS_URL}/iconfont/down_arrow.png`
|
||||||
|
|
||||||
const defaultAvatar = `${OSS_URL}/urm/default_avatar.png`
|
const defaultAvatar = `${OSS_URL}/urm/default_avatar.png`
|
||||||
const showAllChildren = ref(false)
|
const showAllChildren = ref(false)
|
||||||
const checkedId = ref<string>()
|
const checkedId = ref()
|
||||||
function handleShowChild(i: ChildrenType) {
|
function handleShowChild(i: ChildrenType) {
|
||||||
checkedId.value = i.child?.id
|
checkedId.value = i.child?.id
|
||||||
showChild.value = i
|
showChild.value = i
|
||||||
@ -90,7 +92,13 @@ const subjectOptions = [
|
|||||||
const subject = ref(subjectOptions[0].value)
|
const subject = ref(subjectOptions[0].value)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
[() => dataType.value, () => timeType.value, () => timeDay.value, () => subject.value],
|
[
|
||||||
|
() => dataType.value,
|
||||||
|
() => timeType.value,
|
||||||
|
() => timeDay.value,
|
||||||
|
() => subject.value,
|
||||||
|
() => checkedId.value,
|
||||||
|
],
|
||||||
() => {
|
() => {
|
||||||
const params = {
|
const params = {
|
||||||
queryType: dataType.value,
|
queryType: dataType.value,
|
||||||
@ -104,7 +112,9 @@ watch(
|
|||||||
if (dataType.value === dataTypeSubject) {
|
if (dataType.value === dataTypeSubject) {
|
||||||
params.subjectId = subject.value
|
params.subjectId = subject.value
|
||||||
}
|
}
|
||||||
getStudentStudyDataApi(params)
|
getStudentStudyData(params).then(map => {
|
||||||
|
showChildData.value = map[checkedId.value] || {}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
)
|
)
|
||||||
@ -214,94 +224,94 @@ onMounted(() => {
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="study-data">
|
<view v-if="checkedId" class="study-data">
|
||||||
<mj-segment v-model="dataType" :options="dataTypeOptions" />
|
|
||||||
<view class="time-type-box">
|
<view class="time-type-box">
|
||||||
<mj-segment class="time-type" v-model="timeType" :options="timeTypeOptions" />
|
<mj-segment class="time-type" v-model="timeType" :options="timeTypeOptions" />
|
||||||
<view class="time-type-text" v-if="timeType === timeTypeDay" @click="timeDayRef.show()">{{
|
<view class="time-type-text" v-if="timeType === timeTypeDay" @click="timeDayRef.show()">{{
|
||||||
timeDay
|
timeDay
|
||||||
}}</view>
|
}}</view>
|
||||||
</view>
|
</view>
|
||||||
|
<mj-segment v-model="dataType" :options="dataTypeOptions" />
|
||||||
<mj-segment v-if="dataType === dataTypeSubject" v-model="subject" :options="subjectOptions" />
|
<mj-segment v-if="dataType === dataTypeSubject" v-model="subject" :options="subjectOptions" />
|
||||||
<tui-grid v-if="dataType === dataTypeSum" class="data-grid">
|
<tui-grid v-if="dataType === dataTypeSum" class="data-grid">
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ secondsToHours(showChildData.learnTime) }}</view>
|
||||||
<text class="label">有效学习时长</text>
|
<text class="label">有效学习时长</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.knowledgeCount || 0 }}</view>
|
||||||
<text class="label">知识点</text>
|
<text class="label">知识点</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.errorCount || 0 }}</view>
|
||||||
<text class="label">错题</text>
|
<text class="label">错题</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.correctCount || 0 }}</view>
|
||||||
<text class="label">订正</text>
|
<text class="label">订正</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
</tui-grid>
|
</tui-grid>
|
||||||
<tui-grid v-else-if="dataType === dataTypeWord" class="data-grid">
|
<tui-grid v-else-if="dataType === dataTypeWord" class="data-grid">
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.wordStrangerCount || 0 }}</view>
|
||||||
<text class="label">单词陌生</text>
|
<text class="label">单词陌生</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.wordKnowCount || 0 }}</view>
|
||||||
<text class="label">单词认识</text>
|
<text class="label">单词认识</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.wordKnowWellCount || 0 }}</view>
|
||||||
<text class="label">单词熟悉</text>
|
<text class="label">单词熟悉</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.wordGraspCount || 0 }}</view>
|
||||||
<text class="label">单词掌握</text>
|
<text class="label">单词掌握</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.wordCount || 0 }}</view>
|
||||||
<text class="label">单词总数</text>
|
<text class="label">单词总数</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.starCount || 0 }}</view>
|
||||||
<text class="label">单词闯关星星</text>
|
<text class="label">单词闯关星星</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.sentenceCount || 0 }}</view>
|
||||||
<text class="label">语感训练句子</text>
|
<text class="label">语感训练句子</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.learnTime || 0 }}</view>
|
||||||
<text class="label">有效学习时长</text>
|
<text class="label">有效学习时长</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
</tui-grid>
|
</tui-grid>
|
||||||
<tui-grid v-else-if="dataType === dataTypeSubject" class="data-grid">
|
<tui-grid v-else-if="dataType === dataTypeSubject" class="data-grid">
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.successTestCount || 0 }}</view>
|
||||||
<text class="label">答题正确</text>
|
<text class="label">答题正确</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.errorTestCount || 0 }}</view>
|
||||||
<text class="label">答题错误</text>
|
<text class="label">答题错误</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.allKnowledgeCount || 0 }}</view>
|
||||||
<text class="label">知识点总数</text>
|
<text class="label">知识点总数</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.allKnowledgeGraspCount || 0 }}</view>
|
||||||
<text class="label">知识点掌握</text>
|
<text class="label">知识点掌握</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.allReviewCount || 0 }}</view>
|
||||||
<text class="label">订正总数</text>
|
<text class="label">订正总数</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.successReviewCount || 0 }}</view>
|
||||||
<text class="label">订正正确</text>
|
<text class="label">订正正确</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
<tui-grid-item :cell="2">
|
<tui-grid-item :cell="2">
|
||||||
<view class="value">{{ 0 }}</view>
|
<view class="value">{{ showChildData.learnTime || 0 }}</view>
|
||||||
<text class="label">有效学习时长</text>
|
<text class="label">有效学习时长</text>
|
||||||
</tui-grid-item>
|
</tui-grid-item>
|
||||||
</tui-grid>
|
</tui-grid>
|
||||||
@ -366,251 +376,81 @@ onMounted(() => {
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 0 30rpx 30rpx;
|
padding: 0 30rpx 30rpx;
|
||||||
border-radius: 20rpx;
|
border-radius: 20rpx;
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
.children {
|
||||||
margin-bottom: 30rpx;
|
|
||||||
font-size: 34rpx;
|
|
||||||
line-height: 48rpx;
|
|
||||||
color: $font-color;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title2 {
|
|
||||||
margin: 30rpx 0 20rpx 0;
|
|
||||||
font-size: 28rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 40rpx;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trend_icon {
|
|
||||||
margin-left: 10rpx;
|
|
||||||
width: 26rpx;
|
|
||||||
height: 26rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.study_situation {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.item {
|
|
||||||
flex: 1;
|
|
||||||
padding: 42rpx 30rpx;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
height: 119rpx;
|
padding: 30rpx 0 30rpx 0;
|
||||||
border-radius: 20rpx;
|
border-bottom: 1rpx solid $border-color;
|
||||||
background-color: #f7faff;
|
|
||||||
font-size: 26rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
&:first-child {
|
.left {
|
||||||
margin-right: 30rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.date {
|
|
||||||
color: #3fd15f;
|
|
||||||
font-size: 28rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.study_situation2 {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.item {
|
|
||||||
width: calc(50% - 15rpx);
|
|
||||||
text-align: center;
|
|
||||||
font-weight: 500;
|
|
||||||
|
|
||||||
&_label {
|
|
||||||
display: block;
|
|
||||||
height: 76rpx;
|
|
||||||
line-height: 76rpx;
|
|
||||||
background-color: #ebf2ff;
|
|
||||||
border-bottom: 1rpx solid #d9e1f2;
|
|
||||||
border-radius: 20rpx 20rpx 0 0;
|
|
||||||
font-size: 26rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
&_date {
|
|
||||||
display: block;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 79rpx;
|
|
||||||
line-height: 79rpx;
|
|
||||||
background-color: #f7faff;
|
|
||||||
border-radius: 0 0 20rpx 20rpx;
|
|
||||||
|
|
||||||
font-size: 28rpx;
|
.children_avatar {
|
||||||
// rgba(255, 40, 40, 1)jiang rgba(255, 141, 95, 1)ping
|
margin-right: 20rpx;
|
||||||
|
width: 100rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name_vip {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 6rpx;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 210rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 32rpx;
|
||||||
|
line-height: 48rpx;
|
||||||
|
color: $font-color;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vip {
|
||||||
|
margin-left: 10rpx;
|
||||||
|
width: 85rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-size: 24rpx;
|
||||||
|
line-height: 34rpx;
|
||||||
|
color: $font-aux-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.keep {
|
.icon {
|
||||||
color: #ff8d5f;
|
width: 30rpx;
|
||||||
|
height: 30rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.down {
|
.checked_icon {
|
||||||
color: #ff2828;
|
margin: auto 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.up {
|
.rotate {
|
||||||
color: #21d17a;
|
transform: rotate(-180deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.date_filter_box {
|
.children_info {
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.children {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 30rpx 0 30rpx 0;
|
|
||||||
border-bottom: 1rpx solid $border-color;
|
|
||||||
|
|
||||||
.left {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
padding: 30rpx 0 0 0;
|
||||||
|
|
||||||
.children_avatar {
|
|
||||||
margin-right: 20rpx;
|
|
||||||
width: 100rpx;
|
|
||||||
height: 100rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.name_vip {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 6rpx;
|
|
||||||
|
|
||||||
.name {
|
|
||||||
display: inline-block;
|
|
||||||
max-width: 210rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 32rpx;
|
|
||||||
line-height: 48rpx;
|
|
||||||
color: $font-color;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vip {
|
|
||||||
margin-left: 10rpx;
|
|
||||||
width: 85rpx;
|
|
||||||
height: 32rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.time {
|
|
||||||
font-size: 24rpx;
|
|
||||||
line-height: 34rpx;
|
|
||||||
color: $font-aux-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
width: 30rpx;
|
|
||||||
height: 30rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checked_icon {
|
|
||||||
margin: auto 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rotate {
|
|
||||||
transform: rotate(-180deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.children_info {
|
|
||||||
display: flex;
|
|
||||||
padding: 30rpx 0 0 0;
|
|
||||||
|
|
||||||
.item {
|
|
||||||
width: 50%;
|
|
||||||
font-size: 28rpx;
|
|
||||||
line-height: 40rpx;
|
|
||||||
color: $font-aux-color;
|
|
||||||
|
|
||||||
.level {
|
|
||||||
color: $font-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter_box {
|
|
||||||
padding: 30rpx 30rpx 13rpx 30rpx;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.learning_time {
|
|
||||||
.date_filter_box {
|
|
||||||
margin: 50rpx 0 30rpx 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.video_learning {
|
|
||||||
.date_filter_box {
|
|
||||||
margin: 30rpx 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.exercise_statistics {
|
|
||||||
.study_situation2 {
|
|
||||||
margin: 30rpx 0 0 0;
|
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
width: 210rpx;
|
width: 50%;
|
||||||
}
|
font-size: 28rpx;
|
||||||
}
|
line-height: 40rpx;
|
||||||
}
|
color: $font-aux-color;
|
||||||
|
|
||||||
.language_statistics {
|
.level {
|
||||||
.study_situation2 {
|
color: $font-color;
|
||||||
margin: 30rpx 0 50rpx 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.knowledge_mapping {
|
|
||||||
.chart_label {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: rgba(102, 102, 102, 1);
|
|
||||||
|
|
||||||
.iden_color {
|
|
||||||
display: inline-block;
|
|
||||||
width: 30rpx;
|
|
||||||
height: 8rpx;
|
|
||||||
margin-right: 4rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.increase {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-right: 16rpx;
|
|
||||||
|
|
||||||
.iden_color {
|
|
||||||
background-color: rgba(69, 203, 70, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.decrease {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.iden_color {
|
|
||||||
background-color: rgba(248, 104, 96, 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="code_btn">
|
<view class="code_btn">
|
||||||
<template v-if="codeTime > 0">
|
<template v-if="codeTime > 0">
|
||||||
<text class="time_tip">{{ codeTimeContet }} 秒后重新发送</text>
|
<text class="time_tip">{{ codeTimeContent }} 秒后重新发送</text>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<text class="time_tip" @click="getCode">获取验证码</text>
|
<text class="time_tip" @click="getCode">获取验证码</text>
|
||||||
@ -67,7 +67,7 @@ const { getUserInfo, setToken } = useUserStore()
|
|||||||
const formatPhone = ref('')
|
const formatPhone = ref('')
|
||||||
const verifyCode = ref('')
|
const verifyCode = ref('')
|
||||||
const codeTime = ref(0)
|
const codeTime = ref(0)
|
||||||
const codeTimeContet = computed(() => {
|
const codeTimeContent = computed(() => {
|
||||||
return codeTime.value
|
return codeTime.value
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -119,7 +119,6 @@ async function handleLogin() {
|
|||||||
await smsLogin()
|
await smsLogin()
|
||||||
}
|
}
|
||||||
|
|
||||||
let wxUser: any
|
|
||||||
async function smsLogin() {
|
async function smsLogin() {
|
||||||
hud.load({
|
hud.load({
|
||||||
task: async () => {
|
task: async () => {
|
||||||
@ -128,7 +127,7 @@ async function smsLogin() {
|
|||||||
clientType: 'PARENT',
|
clientType: 'PARENT',
|
||||||
phone: formatPhone.value.replace(/\s+/g, ''),
|
phone: formatPhone.value.replace(/\s+/g, ''),
|
||||||
verifyCode: verifyCode.value,
|
verifyCode: verifyCode.value,
|
||||||
wxUser,
|
wxUser: db.get('wxUser') || undefined,
|
||||||
})
|
})
|
||||||
await loginSuccess(token)
|
await loginSuccess(token)
|
||||||
},
|
},
|
||||||
@ -150,16 +149,13 @@ async function wxLogin() {
|
|||||||
const data = await wxLoginApi({
|
const data = await wxLoginApi({
|
||||||
code,
|
code,
|
||||||
})
|
})
|
||||||
// 存储openid
|
// 存储wxUser
|
||||||
if (data.wxUser.openid) {
|
if (data.wxUser) {
|
||||||
db.set('openid', data.wxUser.openid)
|
db.set('wxUser', data.wxUser)
|
||||||
}
|
}
|
||||||
if (data.accessToken) {
|
if (data.accessToken) {
|
||||||
// 说明已经有绑定过账号
|
// 说明已经有绑定过账号
|
||||||
await loginSuccess(data.accessToken)
|
await loginSuccess(data.accessToken)
|
||||||
} else {
|
|
||||||
// 说明没有绑定过账号
|
|
||||||
wxUser = data.wxUser
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
option: '登录',
|
option: '登录',
|
||||||
|
@ -93,6 +93,7 @@ onMounted(() => {})
|
|||||||
<text class="identity">家长</text>
|
<text class="identity">家长</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="right-bottom">
|
<view class="right-bottom">
|
||||||
|
<text class="account">{{ userInfo.account }}</text>
|
||||||
<text
|
<text
|
||||||
>乐贝:<text class="currency">{{ userInfo.currency }}</text></text
|
>乐贝:<text class="currency">{{ userInfo.currency }}</text></text
|
||||||
>
|
>
|
||||||
@ -168,6 +169,11 @@ onMounted(() => {})
|
|||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
line-height: 40rpx;
|
line-height: 40rpx;
|
||||||
color: $font-aux-color;
|
color: $font-aux-color;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 30rpx;
|
||||||
|
.account {
|
||||||
|
}
|
||||||
|
|
||||||
.currency {
|
.currency {
|
||||||
color: #333;
|
color: #333;
|
||||||
|
12
src/store/study-data.ts
Normal file
12
src/store/study-data.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { getStudentStudyDataApi } from '@/api'
|
||||||
|
import obj from '@/utils/obj'
|
||||||
|
import { defineStore, storeToRefs } from 'pinia'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export const useStudyDataStore = defineStore('study-data', () => {
|
||||||
|
async function getStudentStudyData(params: any) {
|
||||||
|
const res = await getStudentStudyDataApi(params)
|
||||||
|
return obj.list2map(res, 'userId')
|
||||||
|
}
|
||||||
|
return { getStudentStudyData }
|
||||||
|
})
|
@ -25,7 +25,7 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
const getUserInfo = async () => {
|
const getUserInfo = async () => {
|
||||||
const mode = import.meta.env.MODE
|
const mode = import.meta.env.MODE
|
||||||
// 如果是正式环境 && 且缺少openid,直接跳回登录页面
|
// 如果是正式环境 && 且缺少openid,直接跳回登录页面
|
||||||
if (mode !== 'dev' && mode !== 'mp' && !db.get('openid')) {
|
if (mode !== 'dev' && mode !== 'mp' && !db.get('wxUser')) {
|
||||||
router.toLogin()
|
router.toLogin()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
230
src/utils/obj.ts
230
src/utils/obj.ts
@ -1,25 +1,229 @@
|
|||||||
// 对象相关处理
|
// 对象相关处理
|
||||||
function clearNullProps(obj: any) {
|
function clearNullProps(obj: any) {
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
if (obj[key] === null || obj[key] === undefined) {
|
if (obj[key] === null || obj[key] === undefined) {
|
||||||
delete obj[key]
|
delete obj[key]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算父节点和子节点的总数
|
// 计算父节点和子节点的总数
|
||||||
function getNodesSize(nodes: any[], childrenKey = 'children') {
|
function getNodesSize(nodes: any[], childrenKey = 'children') {
|
||||||
let size = 0
|
let size = 0
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
size += 1
|
size += 1
|
||||||
if (node[childrenKey]?.length > 0) {
|
if (node[childrenKey]?.length > 0) {
|
||||||
size += getNodesSize(node.children, childrenKey)
|
size += getNodesSize(node.children, childrenKey)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return size
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只赋值dst中存在的属性
|
||||||
|
function assignExits(dst: any, src: any) {
|
||||||
|
for (const key in dst) {
|
||||||
|
dst[key] = src[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只提取指定的属性
|
||||||
|
function filterKeys(obj: any, keys: string[]) {
|
||||||
|
const newObj: any = {}
|
||||||
|
for (const key of keys) {
|
||||||
|
newObj[key] = obj[key]
|
||||||
|
}
|
||||||
|
return newObj
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否没有值
|
||||||
|
function isNullOrUndefined(obj: any) {
|
||||||
|
return obj === null || obj === undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEqualIgnoreNumberString(obj1: any, obj2: any) {
|
||||||
|
return isEqual(obj1, obj2, { ignoreNumberString: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEqual(
|
||||||
|
obj1: any,
|
||||||
|
obj2: any,
|
||||||
|
options?: {
|
||||||
|
ignoreNumberString?: boolean
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
if (obj1 === obj2) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 忽略数字、字符串的比较
|
||||||
|
if (
|
||||||
|
options?.ignoreNumberString &&
|
||||||
|
(typeof obj1 === 'number' || typeof obj1 === 'string') &&
|
||||||
|
(typeof obj2 === 'number' || typeof obj2 === 'string')
|
||||||
|
) {
|
||||||
|
return String(obj1) === String(obj2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const keys1 = Object.keys(obj1)
|
||||||
|
const keys2 = Object.keys(obj2)
|
||||||
|
|
||||||
|
if (keys1.length !== keys2.length) return false
|
||||||
|
|
||||||
|
for (const key of keys1) {
|
||||||
|
if (!keys2.includes(key)) return false
|
||||||
|
if (!isEqual(obj1[key], obj2[key], options)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function isKeyValueObject(obj: any) {
|
||||||
|
return (
|
||||||
|
obj !== null &&
|
||||||
|
typeof obj === 'object' &&
|
||||||
|
!Array.isArray(obj) &&
|
||||||
|
!(obj instanceof Date) &&
|
||||||
|
!(obj instanceof RegExp) &&
|
||||||
|
!(obj instanceof Map) &&
|
||||||
|
!(obj instanceof Set)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function coverNullProps(dst: any, src: any) {
|
||||||
|
return cover(dst, src, {
|
||||||
|
onlyNullProps: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveElement<T>(arr: T[], fromIndex: number, toIndex: number): T[] {
|
||||||
|
// 处理边界情况
|
||||||
|
if (fromIndex < 0 || fromIndex >= arr.length || toIndex < 0 || toIndex >= arr.length) {
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除fromIndex位置的元素
|
||||||
|
const [element] = arr.splice(fromIndex, 1)
|
||||||
|
// 插入到toIndex位置
|
||||||
|
arr.splice(toIndex, 0, element)
|
||||||
|
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 覆盖属性
|
||||||
|
function cover(
|
||||||
|
dst: any,
|
||||||
|
src: any,
|
||||||
|
options?: {
|
||||||
|
onlyNullProps?: boolean
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
if (!isKeyValueObject(dst) || !isKeyValueObject(src)) {
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
const onlyNull = options?.onlyNullProps
|
||||||
|
// 合并所有的key
|
||||||
|
const keys = Array.from(new Set([...Object.keys(dst), ...Object.keys(src)]))
|
||||||
|
for (const key of keys) {
|
||||||
|
// 跳过
|
||||||
|
if (!(key in src)) continue
|
||||||
|
// 来源值
|
||||||
|
const srcVal = src[key]
|
||||||
|
// 目标值
|
||||||
|
const dstVal = dst[key]
|
||||||
|
if (onlyNull && dstVal !== undefined && dstVal !== null) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (Array.isArray(srcVal)) {
|
||||||
|
// 如果是数组
|
||||||
|
if (Array.isArray(dstVal)) {
|
||||||
|
dst[key] = [...dstVal, ...srcVal]
|
||||||
|
} else {
|
||||||
|
dst[key] = [...srcVal]
|
||||||
|
}
|
||||||
|
} else if (isKeyValueObject(srcVal) && isKeyValueObject(dstVal)) {
|
||||||
|
// 如果是对象类型
|
||||||
|
dst[key] = cover(dstVal, srcVal)
|
||||||
|
} else {
|
||||||
|
// 如果是非对象类型
|
||||||
|
dst[key] = srcVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAllMethods(obj: any): Record<string, any> {
|
||||||
|
const methods: Record<string, any> = {}
|
||||||
|
let current = obj
|
||||||
|
|
||||||
|
while (current && current !== Object.prototype) {
|
||||||
|
const proto = Object.getPrototypeOf(current)
|
||||||
|
Object.getOwnPropertyNames(proto)
|
||||||
|
.filter(name => name !== 'constructor' && typeof proto[name] === 'function')
|
||||||
|
.forEach(name => {
|
||||||
|
// 这个判断很重要,优先返回自己的属性,不希望被父类的属性覆盖
|
||||||
|
if (methods[name]) return
|
||||||
|
methods[name] = proto[name]
|
||||||
|
})
|
||||||
|
current = proto
|
||||||
|
}
|
||||||
|
|
||||||
|
return methods
|
||||||
|
}
|
||||||
|
|
||||||
|
function snowflakeId(workerId = 1): string {
|
||||||
|
// 自定义起始时间
|
||||||
|
const timestamp = BigInt(Date.now())
|
||||||
|
const workerBits = BigInt(workerId) << 12n
|
||||||
|
const sequenceBits = BigInt(Math.floor(Math.random() * 4096))
|
||||||
|
return String((timestamp << 22n) | workerBits | sequenceBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
function list2map(list: any[], key: string, value?: string): any {
|
||||||
|
const map: Record<string, any> = {}
|
||||||
|
if (!list?.length) return map
|
||||||
|
for (const item of list) {
|
||||||
|
map[item[key]] = value ? item[value] : item
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
function options2map(list: any[]): any {
|
||||||
|
return list2map(list, 'value', 'label')
|
||||||
|
}
|
||||||
|
|
||||||
|
function map2options(map: Record<number, string>) {
|
||||||
|
// 将map转换成options
|
||||||
|
const arr = []
|
||||||
|
for (const value in map) {
|
||||||
|
arr.push({
|
||||||
|
value,
|
||||||
|
label: map[value],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return arr
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
clearNullProps,
|
clearNullProps,
|
||||||
getNodesSize,
|
getNodesSize,
|
||||||
|
assignExits,
|
||||||
|
filterKeys,
|
||||||
|
isEqual,
|
||||||
|
isEqualIgnoreNumberString,
|
||||||
|
cover,
|
||||||
|
map2options,
|
||||||
|
list2map,
|
||||||
|
coverNullProps,
|
||||||
|
isKeyValueObject,
|
||||||
|
isNullOrUndefined,
|
||||||
|
getAllMethods,
|
||||||
|
snowflakeId,
|
||||||
|
options2map,
|
||||||
|
moveElement,
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import isToday from 'dayjs/plugin/isToday'
|
import isToday from 'dayjs/plugin/isToday'
|
||||||
import isYesterday from 'dayjs/plugin/isYesterday'
|
import isYesterday from 'dayjs/plugin/isYesterday'
|
||||||
|
import duration from 'dayjs/plugin/duration'
|
||||||
import db from './db'
|
import db from './db'
|
||||||
|
|
||||||
const OSS_URL = import.meta.env.VITE_OSS_HOST
|
const OSS_URL = import.meta.env.VITE_OSS_HOST
|
||||||
@ -8,6 +9,7 @@ const defaultAvatar = `${OSS_URL}/urm/default_avatar.png`
|
|||||||
|
|
||||||
dayjs.extend(isToday)
|
dayjs.extend(isToday)
|
||||||
dayjs.extend(isYesterday)
|
dayjs.extend(isYesterday)
|
||||||
|
dayjs.extend(duration)
|
||||||
// store统一设置
|
// store统一设置
|
||||||
export const setCache = (key: string, value: any) => {
|
export const setCache = (key: string, value: any) => {
|
||||||
if (!key) throw new Error('key is required')
|
if (!key) throw new Error('key is required')
|
||||||
@ -36,6 +38,18 @@ export const ArrToObj = (arr: any[], key = 'code', value = 'value'): { [key: str
|
|||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function secondsToHours(s: any) {
|
||||||
|
let t = Number(s)
|
||||||
|
if (isNaN(t) || t <= 0) {
|
||||||
|
return '0'
|
||||||
|
}
|
||||||
|
const day = dayjs.duration(t, 's')
|
||||||
|
return `${String(Math.floor(day.asHours())).padStart(2, '0')}:${String(day.minutes()).padStart(
|
||||||
|
2,
|
||||||
|
'0',
|
||||||
|
)}:${String(day.seconds()).padStart(2, '0')}`
|
||||||
|
}
|
||||||
|
|
||||||
// 延迟运行
|
// 延迟运行
|
||||||
export const $sleep = (time: number) => {
|
export const $sleep = (time: number) => {
|
||||||
return new Promise(res => {
|
return new Promise(res => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user