- ChatComposer panel contract: callers now pass uni-icons type names
(image/sound/list/help/checkmark) instead of emoji
- voice playback buttons: sound icon + pulse animation, no more 🔊/◼ chars
- practice recent list: rare symbols ⌕⌁◉ (tofu risk) -> wallet/gear/chatbubble
- sop resource icon ▶/文 -> 视/文 character badges
- back arrows: 22 char-based ← -> uni-icons back across 21 pages
- line-height centering -> flex in 8 spots (back buttons, media-remove,
turn-voice capsule, option-marker, text-button, play-question)
52 lines
3.2 KiB
Vue
52 lines
3.2 KiB
Vue
<template>
|
||
<view class="page supervisor-exams-page">
|
||
<view class="subpage-header">
|
||
<button class="subpage-back-button" aria-label="返回主管首页" @click="goBack"><uni-icons type="back" size="20" color="#25282d" /></button>
|
||
<view class="subpage-heading"><text class="page-title">岗前测试</text><text class="page-subtitle">按岗位组卷、选择真实员工并查看完成情况。</text></view>
|
||
</view>
|
||
<button class="primary-button create-button" @click="createExam">新建考试</button>
|
||
<text v-if="message" class="status-message">{{ message }}</text>
|
||
<view v-if="loading" class="panel">考试列表加载中…</view>
|
||
<view v-else-if="exams.length" class="exam-list">
|
||
<view v-for="item in exams" :key="item.id" class="panel exam-card">
|
||
<view class="exam-head"><text class="panel-title">{{ item.title }}</text><text class="status-chip">{{ item.status }}</text></view>
|
||
<text class="muted">{{ item.positionCode || '通用岗位' }} · {{ item.questionCount }} 题 · {{ item.targetCount }} 人 · 及格 {{ item.passingScore }} 分</text>
|
||
<text v-if="item.dueDate" class="muted">截止 {{ item.dueDate }}</text>
|
||
<view class="exam-actions">
|
||
<button class="secondary-button" :disabled="item.status !== 'DRAFT'" @click="editExam(item.id)">编辑</button>
|
||
<button class="ghost-button" @click="viewResults(item.id)">查看结果</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view v-else class="panel empty">暂无考试,先创建岗位题目并派发。</view>
|
||
<SupervisorTabBar active="team" />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref } from 'vue';
|
||
import { onShow } from '@dcloudio/uni-app';
|
||
import type { ExamSummary } from '@/types/api';
|
||
import { listSupervisorExams } from '@/services/exam';
|
||
import SupervisorTabBar from '@/components/SupervisorTabBar.vue';
|
||
const exams = ref<ExamSummary[]>([]); const loading = ref(false); const message = ref('');
|
||
const goBack = () => uni.redirectTo({ url: '/pages/supervisor/index/index' });
|
||
const createExam = () => uni.navigateTo({ url: '/pages/supervisor/exams/edit' });
|
||
const editExam = (id: number) => uni.navigateTo({ url: `/pages/supervisor/exams/edit?id=${id}` });
|
||
const viewResults = (id: number) => uni.navigateTo({ url: `/pages/supervisor/exams/results?id=${id}` });
|
||
const load = async () => { loading.value = true; try { exams.value = await listSupervisorExams(); } catch (error) { message.value = error instanceof Error ? error.message : '考试列表读取失败'; } finally { loading.value = false; } };
|
||
onShow(() => { void load(); });
|
||
</script>
|
||
|
||
<style scoped>
|
||
.supervisor-exams-page { max-width: var(--mobile-shell-max-width); margin: 0 auto; }
|
||
.create-button { width: 100%; margin-top: 18px; }
|
||
.status-message { display: block; margin-top: 12px; color: #a9000d; }
|
||
.exam-list { display: grid; gap: 4px; }
|
||
.exam-card { display: grid; gap: 9px; }
|
||
.exam-head, .exam-actions { display: flex; gap: 10px; align-items: center; justify-content: space-between; }
|
||
.status-chip { padding: 4px 8px; border-radius: 999px; background: #eef3f9; color: var(--knowledge); font-size: 12px; }
|
||
.exam-actions button { flex: 1; min-width: 0; }
|
||
.empty { color: var(--muted); }
|
||
</style>
|