diff --git a/uniapp/pages/assessment/questions/index.vue b/uniapp/pages/assessment/questions/index.vue
index 730ee94..960f676 100644
--- a/uniapp/pages/assessment/questions/index.vue
+++ b/uniapp/pages/assessment/questions/index.vue
@@ -3,10 +3,11 @@
* 测评答题页面
*
* 功能:
- * - 展示所有测评题目和选项
+ * - 单题翻页模式展示测评题目
* - 每题10个选项,单选
+ * - 底部导航:题目导航、上一题、下一题
+ * - 侧边题目导航面板,按题号跳转
* - 提交时检测未答题目
- * - 未答题弹窗提示
*/
import { ref, computed } from 'vue'
@@ -26,17 +27,20 @@ const questions = ref([])
// 答案数据 { questionId: selectedIndex }
const answers = ref({})
+// 当前题目索引
+const currentIndex = ref(0)
+
// 页面状态
const pageLoading = ref(true)
const submitting = ref(false)
+// 题目导航弹窗
+const showNavPopup = ref(false)
+
// 未答题弹窗
const showUnansweredPopup = ref(false)
const unansweredQuestions = ref([])
-// scroll-view 滚动目标
-const scrollTarget = ref('')
-
// 评分标准选项(从后台加载)
const scoreOptions = ref([
{ score: 1, label: '极弱', desc: '完全不符合' },
@@ -51,30 +55,46 @@ const scoreOptions = ref([
{ score: 10, label: '极强', desc: '完全符合' }
])
-/** 已答题数量 */
-const answeredCount = computed(() => Object.keys(answers.value).length)
+/** 当前题目 */
+const currentQuestion = computed(() => questions.value[currentIndex.value] || null)
+
+/** 当前题目ID */
+const currentQuestionId = computed(() => {
+ if (!currentQuestion.value) return 0
+ return currentQuestion.value.id || currentIndex.value + 1
+})
/** 总题目数量 */
const totalCount = computed(() => questions.value.length)
+/** 是否第一题 */
+const isFirst = computed(() => currentIndex.value === 0)
+
+/** 是否最后一题 */
+const isLast = computed(() => currentIndex.value === totalCount.value - 1)
+
+/** 判断某题是否已答 */
+function isAnswered(index) {
+ const q = questions.value[index]
+ const qId = q?.id || index + 1
+ return answers.value[qId] !== undefined
+}
+
/** 加载题目列表 */
async function loadQuestions() {
pageLoading.value = true
try {
- // 并行加载题目和评分标准
const [questionsRes, scoreRes] = await Promise.all([
getQuestionList(typeId.value),
getScoreOptions(typeId.value)
])
- // 处理题目数据
if (questionsRes && questionsRes.code === 0 && questionsRes.data) {
questions.value = questionsRes.data.list || questionsRes.data || []
} else {
questions.value = generateMockQuestions()
}
- // 处理评分标准数据
if (scoreRes && scoreRes.code === 0 && scoreRes.data && scoreRes.data.length > 0) {
scoreOptions.value = scoreRes.data.map(item => ({
score: item.score,
@@ -82,7 +102,6 @@ async function loadQuestions() {
desc: item.description
}))
}
- // 如果加载失败,保留默认值
} catch (error) {
console.error('加载数据失败:', error)
questions.value = generateMockQuestions()
@@ -118,14 +137,43 @@ function isSelected(questionId, scoreIndex) {
return answers.value[questionId] === scoreIndex
}
+/** 上一题 */
+function goPrev() {
+ if (!isFirst.value) {
+ currentIndex.value--
+ }
+}
+
+/** 下一题 */
+function goNext() {
+ if (!isLast.value) {
+ currentIndex.value++
+ }
+}
+
+/** 跳转到指定题目(题目导航用) */
+function goToQuestion(index) {
+ currentIndex.value = index
+ showNavPopup.value = false
+}
+
+/** 打开题目导航 */
+function openNavPopup() {
+ showNavPopup.value = true
+}
+
+/** 关闭题目导航 */
+function closeNavPopup() {
+ showNavPopup.value = false
+}
+
/** 提交答案 */
async function handleSubmit() {
- // 检查未答题目
const unanswered = []
questions.value.forEach((q, index) => {
const qId = q.id || index + 1
if (answers.value[qId] === undefined) {
- unanswered.push(q.questionNo || index + 1)
+ unanswered.push({ no: q.questionNo || index + 1, index })
}
})
@@ -167,14 +215,10 @@ function closeUnansweredPopup() {
showUnansweredPopup.value = false
}
-/** 滚动到未答题目 */
-function scrollToQuestion(questionNo) {
+/** 跳转到未答题目 */
+function goToUnanswered(index) {
closeUnansweredPopup()
- // 先清空再赋值,确保相同 id 也能触发滚动
- scrollTarget.value = ''
- setTimeout(() => {
- scrollTarget.value = `question-${questionNo}`
- }, 50)
+ currentIndex.value = index
}
/** 页面加载 */
@@ -197,45 +241,89 @@ onLoad((options) => {
加载题目中...
-
-
-
-
-
-
-
- {{ question.questionNo || index + 1 }}
- {{ question.content }}
-
+
+
+
+
+
+ 当前题目
+ /总题数:
+ {{ currentIndex + 1 }}/{{ totalCount }}
+
-
-
-
-
-
-
- 【{{ option.label }}】{{ option.desc }}
-
+
+
+ {{ currentQuestion.questionNo || currentIndex + 1 }}
+ {{ currentQuestion.content }}
+
+
+
+
+
+
+
+ 【{{ option.label }}】{{ option.desc }}
-
+
-
-
-
- {{ submitting ? '提交中...' : '提交' }}
+
+
+
+ ☰
+ 题目导航
+
+
+
+
+ 上一题
+
+
+ 下一题
+
+
+ {{ submitting ? '提交中...' : '提交' }}
+
+
+
+
+
+
@@ -250,12 +338,12 @@ onLoad((options) => {
- 第 {{ qNo }} 题
+ 第 {{ item.no }} 题
›
@@ -307,35 +395,51 @@ onLoad((options) => {
to { transform: rotate(360deg); }
}
-// ========== 滚动区域 ==========
-.scroll-area {
+// ========== 答题区域 ==========
+.question-area {
flex: 1;
- overflow: hidden;
+ overflow-y: auto;
+ padding: $spacing-sm $spacing-lg;
+ display: flex;
+ align-items: center;
}
-.scroll-inner {
- padding: $spacing-lg;
-}
-
-.questions-card {
+.question-card {
background-color: $bg-white;
- border-radius: $border-radius-xl;
+ border-radius: 32rpx;
padding: $spacing-xl $spacing-lg;
+ width: 100%;
+ min-height: 65vh;
}
-// ========== 单个题目 ==========
-.question-block {
- margin-bottom: $spacing-xl;
+// ========== 进度信息 ==========
+.progress-bar {
+ display: flex;
+ align-items: center;
+ margin-bottom: $spacing-lg;
- &:last-child {
- margin-bottom: 0;
+ .progress-current {
+ font-size: $font-size-sm;
+ color: #FF6B60;
+ }
+
+ .progress-sep {
+ font-size: $font-size-sm;
+ color: $text-placeholder;
+ }
+
+ .progress-nums {
+ font-size: $font-size-sm;
+ color: $text-placeholder;
+ margin-left: 4rpx;
}
}
+// ========== 题目标题 ==========
.question-title {
display: flex;
align-items: flex-start;
- margin-bottom: $spacing-lg;
+ margin-bottom: $spacing-xl;
}
.question-no {
@@ -406,32 +510,91 @@ onLoad((options) => {
line-height: 1.5;
}
-// ========== 底部固定提交按钮 ==========
-.submit-fixed {
+// ========== 底部操作栏 ==========
+.bottom-bar {
flex-shrink: 0;
- padding: $spacing-lg $spacing-lg;
- padding-bottom: calc(#{$spacing-lg} + env(safe-area-inset-bottom));
- background-color: rgba(255, 234, 231, 1);
+ display: flex;
+ align-items: center;
+ padding: $spacing-md $spacing-lg;
+ padding-bottom: calc(#{$spacing-md} + env(safe-area-inset-bottom));
+ background-color: $bg-white;
+ border-top: 1rpx solid $border-light;
}
-.submit-btn {
- height: 96rpx;
- background-color: #FF6B60;
- border-radius: 48rpx;
+.nav-btn-left {
+ display: flex;
+ align-items: center;
+ padding: $spacing-sm 0;
+
+ .nav-icon-text {
+ font-size: 32rpx;
+ color: #FF6B60;
+ margin-right: 8rpx;
+ line-height: 1;
+ }
+
+ .nav-label {
+ font-size: $font-size-sm;
+ color: #FF6B60;
+ }
+
+ &:active {
+ opacity: 0.7;
+ }
+}
+
+.bar-divider {
+ width: 1rpx;
+ height: 48rpx;
+ background-color: $border-color;
+ margin: 0 $spacing-lg;
+}
+
+.bar-actions {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ gap: $spacing-md;
+}
+
+.action-btn {
+ flex: 1;
+ height: 76rpx;
+ border-radius: 38rpx;
display: flex;
align-items: center;
justify-content: center;
text {
- font-size: $font-size-xl;
- font-weight: $font-weight-bold;
- color: $text-white;
+ font-size: $font-size-md;
+ font-weight: $font-weight-medium;
}
&:active {
- opacity: 0.9;
+ opacity: 0.8;
transform: scale(0.98);
}
+}
+
+.action-prev {
+ background-color: #FF6B60;
+
+ text {
+ color: $text-white;
+ }
+
+ &.action-disabled {
+ opacity: 0.4;
+ pointer-events: none;
+ }
+}
+
+.action-next {
+ background-color: #FF6B60;
+
+ text {
+ color: $text-white;
+ }
&.btn-loading {
opacity: 0.7;
@@ -439,6 +602,103 @@ onLoad((options) => {
}
}
+// ========== 题目导航侧边弹窗 ==========
+.nav-popup-mask {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 1000;
+ display: flex;
+}
+
+.nav-popup-panel {
+ width: 580rpx;
+ height: 100%;
+ background-color: $bg-white;
+ display: flex;
+ flex-direction: column;
+}
+
+.nav-popup-header {
+ display: flex;
+ align-items: center;
+ padding: $spacing-xl $spacing-lg;
+ padding-top: calc(#{$spacing-xl} + env(safe-area-inset-top) + 88rpx);
+
+ .nav-popup-indicator {
+ width: 8rpx;
+ height: 36rpx;
+ background-color: $text-color;
+ border-radius: 4rpx;
+ margin-right: $spacing-sm;
+ }
+
+ .nav-popup-title {
+ font-size: $font-size-lg;
+ font-weight: $font-weight-bold;
+ color: $text-color;
+ }
+}
+
+.nav-popup-divider {
+ height: 1rpx;
+ background: repeating-linear-gradient(
+ to right,
+ $border-color 0,
+ $border-color 8rpx,
+ transparent 8rpx,
+ transparent 16rpx
+ );
+ margin: 0 $spacing-lg;
+}
+
+.nav-popup-grid-scroll {
+ flex: 1;
+ overflow: hidden;
+}
+
+.nav-popup-grid {
+ display: flex;
+ flex-wrap: wrap;
+ padding: $spacing-lg;
+ gap: $spacing-md;
+}
+
+.nav-grid-item {
+ width: 100rpx;
+ height: 100rpx;
+ border-radius: $border-radius-md;
+ background-color: #F0F0F0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ text {
+ font-size: $font-size-lg;
+ color: $text-secondary;
+ font-weight: $font-weight-medium;
+ }
+
+ &.nav-grid-answered {
+ background-color: #FF6B60;
+
+ text {
+ color: $text-white;
+ }
+ }
+
+ &.nav-grid-current {
+ border: 3rpx solid #FF6B60;
+ }
+
+ &:active {
+ opacity: 0.8;
+ }
+}
+
// ========== 未答题弹窗 ==========
.popup-mask {
position: fixed;
@@ -446,7 +706,7 @@ onLoad((options) => {
left: 0;
right: 0;
bottom: 0;
- background-color: $bg-mask;
+ background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;