1
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
<template>
|
||||
<hr-page page-bg="#F3F4F6" status-bg="#FFFFFF" nav-title="学习中心" :xloading="true" @right-click="onSort">
|
||||
<template v-slot:right>
|
||||
<x-icon name="arrow-up-down-line" color="#6B7280" font-size="17"></x-icon>
|
||||
</template>
|
||||
|
||||
<view class="hr-body">
|
||||
|
||||
<!-- ============ 四大类目(xTabs) ============ -->
|
||||
<x-tabs :list="catTabs" :model-value="activeId" item-width="25%" height="84rpx" font-size="28rpx"
|
||||
active-font-size="28rpx" title-color="#6B7280" active-title-color="#2563EB" line-color="#2563EB"
|
||||
title-padding="0" :is-item-center="true" @change="onCatChange"></x-tabs>
|
||||
|
||||
<!-- ============ 课题筛选(组件库 xRadioButton 分段切换) ============ -->
|
||||
<view style="margin-top:10px;margin-bottom:8rpx">
|
||||
<x-radio-button v-model="chipId" :list="chipTabs" height="64rpx" space="4rpx" round="22rpx"
|
||||
text-style="font-weight:bold;" bg-color="#E5E7EB" active-color="#2563EB"
|
||||
active-font-color="#FFFFFF" font-color="#6B7280" font-size="22rpx"></x-radio-button>
|
||||
</view>
|
||||
|
||||
<!-- ============ 题目列表 ============ -->
|
||||
<view v-if="cat == 0" style="margin-top:10px">
|
||||
<view v-for="(q, i) in wrongList" :key="q.id" class="qa-card">
|
||||
<view class="qa-top">
|
||||
<text class="qa-stem">{{ q.stem }}</text>
|
||||
<hr-chip :text="wrongTimes(i)" theme="red"></hr-chip>
|
||||
</view>
|
||||
<text class="qa-ans">正确答案 <text class="qa-ans-hl">{{ rightAnswer(q) }}</text></text>
|
||||
<view class="qa-meta">
|
||||
<text class="qa-meta-t">来自 <text class="qa-meta-hl">{{ q.from }}</text></text>
|
||||
<text class="qa-meta-t qa-meta-date">{{ wrongDates[i] }}</text>
|
||||
<view class="hr-flex1"></view>
|
||||
<view class="qa-fold" @click="toggle(i)">
|
||||
<x-icon :name="opened[i] ? 'arrow-up-s-line' : 'arrow-down-s-line'" color="#2563EB"
|
||||
font-size="26rpx"></x-icon>
|
||||
<text class="qa-fold-t">{{ opened[i] ? '收起解析' : '查看解析' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 手风琴展开:展开进度由 accStyle() 逐帧下发 -->
|
||||
<view v-if="opened[i]" :style="accStyle(i)">
|
||||
<view class="qa-analysis">
|
||||
<text class="qa-analysis-t">{{ q.analysis }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 其它类目占位 -->
|
||||
<hr-empty v-else :icon="emptyIcon" :title="emptyTitle" :sub="emptySub" action="去课程库学习"
|
||||
@action="goLibrary"></hr-empty>
|
||||
|
||||
<view style="height:14px"></view>
|
||||
</view>
|
||||
</hr-page>
|
||||
</template>
|
||||
|
||||
<script lang="uts" setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { wrongQuestions } from '@/mock/index.uts'
|
||||
import { HrQuestion } from '@/mock/types.uts'
|
||||
import { navTo } from '@/utils/nav.uts'
|
||||
import { TABS_ITEM_INFO, TABS_ITEM, RADIO_BUTTON } from '@/uni_modules/tmx-ui/interface.uts'
|
||||
|
||||
/**
|
||||
* s38 学习中心
|
||||
* 汇总「错题 / 收藏 / 历史 / 笔记」四类目,替代原先散落各处的入口。
|
||||
* 四类目改用组件库 xTabs(原 lc-tabs 自绘分段控件已移除)。
|
||||
*/
|
||||
const catTabs : TABS_ITEM_INFO[] = [
|
||||
{ title: '错题 12', id: '0' } as TABS_ITEM_INFO,
|
||||
{ title: '收藏 8', id: '1' } as TABS_ITEM_INFO,
|
||||
{ title: '历史', id: '2' } as TABS_ITEM_INFO,
|
||||
{ title: '笔记 5', id: '3' } as TABS_ITEM_INFO
|
||||
]
|
||||
|
||||
/**
|
||||
* 课题筛选(组件库 xRadioButton 分段切换,替代原 hr-chip 横向滚动)
|
||||
* ⚠ RADIO_BUTTON 的 id 是 string:统一用下标字符串,选中值即筛选序号。
|
||||
*/
|
||||
const chipTabs : RADIO_BUTTON[] = [
|
||||
{ id: '0', title: '全部' } as RADIO_BUTTON,
|
||||
{ id: '1', title: '心肺复苏 5' } as RADIO_BUTTON,
|
||||
{ id: '2', title: '气道管理 4' } as RADIO_BUTTON,
|
||||
{ id: '3', title: 'AED 3' } as RADIO_BUTTON
|
||||
]
|
||||
const chipId = ref('0')
|
||||
|
||||
const wrongDates : string[] = ['09-15', '09-14', '09-12']
|
||||
|
||||
const activeId = ref('0')
|
||||
const opened = ref<boolean[]>([false, false, false])
|
||||
const wrongList = ref<HrQuestion[]>(wrongQuestions)
|
||||
|
||||
const cat = computed(() : number => {
|
||||
const i = parseInt(activeId.value)
|
||||
return isNaN(i) ? 0 : i
|
||||
})
|
||||
|
||||
// ⚠ x-tabs 的 change 下发的是 TABS_ITEM(不是 TABS_ITEM_INFO),形参类型必须一致
|
||||
function onCatChange(item : TABS_ITEM, index : number) : void {
|
||||
activeId.value = item.id == '' ? index.toString() : item.id
|
||||
}
|
||||
|
||||
const emptyIcon = computed(() : string => {
|
||||
if (cat.value == 1) { return '★' }
|
||||
if (cat.value == 2) { return '◔' }
|
||||
return '✎'
|
||||
})
|
||||
|
||||
const emptyTitle = computed(() : string => {
|
||||
if (cat.value == 1) { return '暂无收藏题目' }
|
||||
if (cat.value == 2) { return '暂无学习历史' }
|
||||
return '暂无学习笔记'
|
||||
})
|
||||
|
||||
const emptySub = computed(() : string => {
|
||||
return '在课题中收藏题目、记录笔记后会自动汇总到这里'
|
||||
})
|
||||
|
||||
function wrongTimes(i : number) : string {
|
||||
if (i == 0) { return '错 2 次' }
|
||||
return '错 1 次'
|
||||
}
|
||||
|
||||
function rightAnswer(q : HrQuestion) : string {
|
||||
if (q.answer < q.options.length) {
|
||||
const o = q.options[q.answer]
|
||||
return `${o.key}. ${o.text}`
|
||||
}
|
||||
return '—'
|
||||
}
|
||||
|
||||
/*
|
||||
* 手风琴展开入场动画
|
||||
* ⚠ App 端 uvue 不支持 @keyframes / animation(编译期直接报 ERROR),
|
||||
* 改为定时器逐帧推进 accT(0 → 1),再由 accStyle() 下发 opacity + translateY。
|
||||
* 240ms(30ms × 8 帧),与设计稿 qa-acc-in 一致。
|
||||
*/
|
||||
const accIdx = ref(-1)
|
||||
const accT = ref(1)
|
||||
let accTimer : number = 0
|
||||
|
||||
function stopAcc() : void {
|
||||
if (accTimer != 0) {
|
||||
clearInterval(accTimer)
|
||||
accTimer = 0
|
||||
}
|
||||
}
|
||||
|
||||
function runAcc(i : number) : void {
|
||||
stopAcc()
|
||||
accIdx.value = i
|
||||
accT.value = 0
|
||||
accTimer = setInterval(() => {
|
||||
const t = accT.value + 0.16
|
||||
if (t >= 1) {
|
||||
accT.value = 1
|
||||
stopAcc()
|
||||
return
|
||||
}
|
||||
accT.value = t
|
||||
}, 30)
|
||||
}
|
||||
|
||||
/** 仅正在展开的那一项需要过渡样式,其余返回空串(即最终态) */
|
||||
function accStyle(i : number) : string {
|
||||
if (i != accIdx.value || accT.value >= 1) { return '' }
|
||||
const t = accT.value
|
||||
const y = Math.round(-16 * (1 - t))
|
||||
return 'opacity:' + t.toString() + ';transform:translateY(' + y.toString() + 'rpx);'
|
||||
}
|
||||
|
||||
/** 手风琴:展开一项时其余自动收起 */
|
||||
function toggle(i : number) : void {
|
||||
const wasOpen = opened.value[i]
|
||||
const arr : boolean[] = []
|
||||
for (let k = 0; k < wrongList.value.length; k++) { arr.push(false) }
|
||||
arr[i] = !wasOpen
|
||||
opened.value = arr
|
||||
if (wasOpen) {
|
||||
stopAcc()
|
||||
accIdx.value = -1
|
||||
accT.value = 1
|
||||
} else {
|
||||
runAcc(i)
|
||||
}
|
||||
}
|
||||
|
||||
onUnload(() => {
|
||||
stopAcc()
|
||||
})
|
||||
|
||||
function goLibrary() : void {
|
||||
navTo('/pages/course/library')
|
||||
}
|
||||
|
||||
function onSort() : void {
|
||||
uni.showToast({ title: '按时间 / 次数排序', icon: 'none' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* ---------- 题目卡 ---------- */
|
||||
.qa-card {
|
||||
background-color: #FFFFFF;
|
||||
border: 2rpx solid #E5E7EB;
|
||||
border-left: 8rpx solid #2563EB;
|
||||
border-radius: 20rpx;
|
||||
padding: 22rpx 24rpx 22rpx 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.qa-top {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.qa-stem {
|
||||
flex: 1;
|
||||
font-size: 29.2rpx;
|
||||
font-weight: bold;
|
||||
color: #1F2937;
|
||||
margin-right: 18rpx;
|
||||
}
|
||||
|
||||
.qa-ans {
|
||||
font-size: 26.8rpx;
|
||||
color: #6B7280;
|
||||
margin-top: 14rpx;
|
||||
}
|
||||
|
||||
.qa-ans-hl {
|
||||
font-size: 26.8rpx;
|
||||
color: #059669;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.qa-meta {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.qa-meta-t {
|
||||
font-size: 24.6rpx;
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
.qa-meta-hl {
|
||||
font-size: 24.6rpx;
|
||||
color: #1F2937;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.qa-meta-date {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
|
||||
.qa-fold {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.qa-fold-t {
|
||||
font-size: 25.8rpx;
|
||||
color: #2563EB;
|
||||
font-weight: bold;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
|
||||
/* ---------- 手风琴展开 ----------
|
||||
⚠ App 端 uvue 不支持 @keyframes/animation,
|
||||
入场过渡已改为 script 内定时器推进 accT + accStyle() 下发样式。 */
|
||||
.qa-analysis {
|
||||
margin-top: 20rpx;
|
||||
padding: 18rpx 20rpx 18rpx 20rpx;
|
||||
border-radius: 18rpx;
|
||||
background-color: #F9FAFB;
|
||||
}
|
||||
|
||||
.qa-analysis-t {
|
||||
font-size: 25.8rpx;
|
||||
color: #6B7280;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user