This commit is contained in:
2026-09-24 16:25:22 +08:00
commit 7428184f01
1198 changed files with 314515 additions and 0 deletions
+288
View File
@@ -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),
* 改为定时器逐帧推进 accT0 → 1),再由 accStyle() 下发 opacity + translateY。
* 240ms30ms × 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>
+81
View File
@@ -0,0 +1,81 @@
<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">
<hr-card>
<view class="hr-row">
<view class="hr-avt hr-avt-lg hr-avt-grn ct-avt">
<text>▤</text>
</view>
<view class="hr-flex1">
<text class="ct-title">有效证书 {{ certs.length }} 张</text>
<text class="ct-sub">其中 1 张将于 2026-10-15 到期</text>
</view>
</view>
</hr-card>
<hr-sec title="证书列表"></hr-sec>
<hr-card variant="list">
<hr-lrow v-for="(c, i) in certs" :key="i" :icon="c.statusTheme == 'grn' ? '▤' : '!'"
:theme="c.statusTheme == 'grn' ? 'grn' : 'amb'" :title="c.name"
:sub="c.gotAt + ' 取得 · ' + (c.statusTheme == 'grn' ? '有效至 ' + c.expireAt : c.expireAt + ' 到期')"
:value="c.status + ' '" :value-theme="c.statusTheme" :value-bold="true"
:is-last="i == certs.length - 1" @click="onCertClick(c)"></hr-lrow>
</hr-card>
<hr-tip style="margin-top:14px" theme="warn" icon="⚑"
text="BLS 证书将在 22 天后到期,请完成年度复训后自动续期。"></hr-tip>
<view style="height:14px"></view>
</view>
</hr-page>
</template>
<script lang="uts" setup>
import { ref } from 'vue'
import { certList } from '@/mock/index.uts'
import { HrCert } from '@/mock/types.uts'
/** s13 我的证书 */
const certs = ref<HrCert[]>(certList)
function onCertClick(c : HrCert) : void {
uni.showModal({
title: c.name,
content: `取得时间:${c.gotAt}\n有效期至:${c.expireAt}\n状态:${c.status}`,
cancelText: '关闭',
confirmText: '查看证书',
success: (res) => {
if (res.confirm) {
uni.showToast({ title: '已生成证书图片', icon: 'none' })
}
}
})
}
function onSort() : void {
uni.showToast({ title: '按取得时间排序', icon: 'none' })
}
</script>
<style>
.ct-avt {
margin-right: 24rpx;
}
.ct-title {
font-size: 31.4rpx;
font-weight: bold;
color: #1F2937;
}
.ct-sub {
font-size: 25.8rpx;
color: #6B7280;
margin-top: 8rpx;
}
</style>
+185
View File
@@ -0,0 +1,185 @@
<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">
<view class="hr-seg">
<view v-for="(s, i) in segs" :key="i" :class="seg == i ? 'hr-seg-i hr-seg-i-on' : 'hr-seg-i'"
@click="seg = i">
<text :class="seg == i ? 'ex-seg-t ex-seg-t-on' : 'ex-seg-t'">{{ s }}</text>
</view>
</view>
<!-- ============ 待考(P0 紧急) ============ -->
<view v-if="seg != 2 && plans.length > 0" class="ex-plan">
<view class="hr-row-top">
<view class="ex-plan-ic">
<text class="ex-plan-ic-t">!</text>
</view>
<view class="hr-flex1">
<view class="hr-row-between">
<text class="ex-plan-t">{{ plans[0].name }}</text>
<hr-pc :level="plans[0].priority"></hr-pc>
</view>
<text class="ex-plan-cd">{{ plans[0].countdown }}</text>
<text class="ex-plan-meta">{{ plans[0].time }} · {{ plans[0].place }}</text>
</view>
</view>
<view class="ex-plan-btn" @click="onPrepare">
<text class="ex-plan-btn-t">进入备考</text>
</view>
</view>
<!-- ============ 已完成 ============ -->
<hr-card v-if="seg != 1" variant="list" style="margin-top:10px">
<hr-lrow v-for="(r, i) in shownRecords" :key="i" :icon="r.passed ? '✓' : '✗'"
:theme="r.passed ? 'grn' : 'red'" :title="r.name"
:sub="r.date + ' · 得分 ' + r.score + ' · ' + (r.passed ? '已通过' : '未通过')"
:value="r.score.toString() + ' '" :value-theme="r.passed ? 'grn' : 'red'" :value-bold="true"
:is-last="i == shownRecords.length - 1" @click="onRecordClick(r)"></hr-lrow>
</hr-card>
<hr-empty v-if="seg == 1 && plans.length == 0" icon="✓" title="暂无待考安排"
sub="有新的考试安排时会在消息中心提醒你"></hr-empty>
<hr-tip style="margin-top:14px" theme="info" icon=""
text="理论测验通过后自动计入课题完成度;未通过的测验可在 7 天后重新参加。"></hr-tip>
<view style="height:14px"></view>
</view>
</hr-page>
</template>
<script lang="uts" setup>
import { ref, computed } from 'vue'
import { examPlans, examRecords } from '@/mock/index.uts'
import { HrExamRecord, HrExamPlan } from '@/mock/types.uts'
import { navTo } from '@/utils/nav.uts'
/** s12 我的考试 */
const segs : string[] = ['全部', '待考', '已完成']
const seg = ref(0)
const plans = ref<HrExamPlan[]>(examPlans)
const records = ref<HrExamRecord[]>(examRecords)
const passOnly = ref(false)
const shownRecords = computed(() : HrExamRecord[] => {
if (passOnly.value) {
const list : HrExamRecord[] = []
for (let i = 0; i < records.value.length; i++) {
if (records.value[i].passed) { list.push(records.value[i]) }
}
return list
}
return records.value
})
function onPrepare() : void {
navTo('/pages/course/practice')
}
function onRecordClick(r : HrExamRecord) : void {
if (r.passed) {
uni.showModal({
title: r.name,
content: `得分 ${r.score} 分 · ${r.date}\n可查看错题解析与答卷详情。`,
cancelText: '关闭',
confirmText: '查看错题',
success: (res) => {
if (res.confirm) { navTo('/pages/study/wrong-book') }
}
})
return
}
uni.showModal({
title: r.name,
content: `得分 ${r.score} 分 · 未通过\n补考开放时间:${r.date} 起 7 天后`,
cancelText: '关闭',
confirmText: '进入复习',
success: (res) => {
if (res.confirm) { navTo('/pages/course/practice') }
}
})
}
function onSort() : void {
uni.showToast({ title: '按时间排序', icon: 'none' })
}
</script>
<style>
.ex-seg-t {
font-size: 29.2rpx;
color: #6B7280;
}
.ex-seg-t-on {
color: #2563EB;
font-weight: bold;
}
.ex-plan {
margin-top: 20rpx;
background-color: #FFF5F5;
border: 2rpx solid #FECACA;
border-radius: 26rpx;
padding: 24rpx 26rpx 24rpx 26rpx;
}
.ex-plan-ic {
width: 68rpx;
height: 68rpx;
border-radius: 20rpx;
background-color: #DC2626;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.ex-plan-ic-t {
font-size: 35.8rpx;
font-weight: bold;
color: #FFFFFF;
}
.ex-plan-t {
font-size: 30.2rpx;
font-weight: bold;
color: #1F2937;
}
.ex-plan-cd {
font-size: 25.8rpx;
color: #DC2626;
font-weight: bold;
margin-top: 12rpx;
}
.ex-plan-meta {
font-size: 24.6rpx;
color: #6B7280;
margin-top: 10rpx;
}
.ex-plan-btn {
margin-top: 24rpx;
height: 88rpx;
border-radius: 24rpx;
background-color: #DC2626;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.ex-plan-btn-t {
font-size: 31.4rpx;
font-weight: bold;
color: #FFFFFF;
}
</style>
+270
View File
@@ -0,0 +1,270 @@
<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">
<!-- 课题筛选(组件库 xRadioButton 分段切换) -->
<view style="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 style="margin-top:10px">
<view v-for="(q, i) in list" :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>
<view class="qa-row">
<text class="qa-k">你的答案</text>
<text class="qa-wrong">{{ wrongKeys[i] }}</text>
<text class="qa-k qa-k2">正确答案</text>
<text class="qa-right">{{ rightAnswer(q) }}</text>
</view>
<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">{{ dates[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-tip theme="info" icon="" text="已复习的错题不会自动移除;连续答对 2 次后可手动移出错题本。"></hr-tip>
<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 { RADIO_BUTTON } from '@/uni_modules/tmx-ui/interface.uts'
/** s16 错题本 */
/**
* 课题筛选(组件库 xRadioButton 分段切换,替代原 hr-chip 横向滚动)
* ⚠ RADIO_BUTTON 的 id 是 string:统一用下标字符串,选中值即筛选序号。
*/
const chipTabs : RADIO_BUTTON[] = [
{ id: '0', title: '全部 12' } 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 wrongTimes : string[] = ['答错 2 次', '答错 1 次', '答错 1 次']
const dates : string[] = ['09-15', '09-14', '09-12']
const wrongKeys : string[] = ['A. 3~4 cm', 'C. 60~80 次', 'D. 每 6 秒 1 次']
const opened = ref<boolean[]>([false, false, false])
const all = ref<HrQuestion[]>(wrongQuestions)
const list = computed(() : HrQuestion[] => {
return all.value
})
/** 选项文案里若已含「A. 」前缀则去掉,避免出现「A. A. xxx」 */
function stripKey(text : string) : string {
const i = text.indexOf('. ')
if (i == 1 || i == 2) { return text.substring(i + 2) }
return text
}
// ⚠ UTS 不提升函数声明:stripKey 必须写在 rightAnswer 之前
function rightAnswer(q : HrQuestion) : string {
if (q.answer < q.options.length) {
const o = q.options[q.answer]
return `${o.key}. ${stripKey(o.text)}`
}
return '—'
}
/*
* 手风琴展开入场动画
* ⚠ App 端 uvue 不支持 @keyframes / animation(编译期直接报 ERROR),
* 改为定时器逐帧推进 accT0 → 1),再由 accStyle() 下发 opacity + translateY。
* 240ms30ms × 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 < list.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 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-row {
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
margin-top: 14rpx;
}
.qa-k {
font-size: 26.8rpx;
color: #6B7280;
}
.qa-k2 {
margin-left: 12rpx;
}
.qa-wrong {
font-size: 26.8rpx;
color: #DC2626;
font-weight: bold;
margin-left: 6rpx;
}
.qa-right {
font-size: 26.8rpx;
color: #059669;
font-weight: bold;
margin-left: 6rpx;
}
.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>