2025-07-23 22:32:01 +08:00

438 lines
9.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<BasicLayout>
<scroll-view scroll-y class="detail-scroll-view">
<view class="detail-container">
<!-- 签到基本信息 -->
<view class="info-card">
<view class="card-header">
<text class="qd-title">{{ qdInfo.qdmc }}</text>
<text class="qd-status" :class="getStatusClass(qdInfo.qdStatus)">
{{ getStatusText(qdInfo.qdStatus) }}
</text>
</view>
<view class="info-list">
<view class="info-item">
<text class="info-label">签到地点</text>
<text class="info-value">{{ qdInfo.qdwz || '未设置' }}</text>
</view>
<view class="info-item">
<text class="info-label">开始时间</text>
<text class="info-value">{{ formatTime(qdInfo.qdkstime) }}</text>
</view>
<view class="info-item">
<text class="info-label">结束时间</text>
<text class="info-value">{{ formatTime(qdInfo.qdjstime) }}</text>
</view>
<view class="info-item">
<text class="info-label">发布者</text>
<text class="info-value">{{ qdInfo.jsxm || '未知' }}</text>
</view>
<view class="info-item">
<text class="info-label">发布时间</text>
<text class="info-value">{{ formatTime(qdInfo.qdFbtime) }}</text>
</view>
</view>
</view>
<!-- 签到人员统计 -->
<view class="stats-card">
<view class="stats-header">
<text class="stats-title">签到统计</text>
</view>
<view class="stats-content">
<view class="stat-item">
<text class="stat-number">{{ totalCount }}</text>
<text class="stat-label">总人数</text>
</view>
<view class="stat-item">
<text class="stat-number signed">{{ signedCount }}</text>
<text class="stat-label">已签到</text>
</view>
<view class="stat-item">
<text class="stat-number unsigned">{{ unsignedCount }}</text>
<text class="stat-label">未签到</text>
</view>
</view>
</view>
<!-- 签到人员列表 -->
<view class="teacher-list-card">
<view class="list-header">
<text class="list-title">签到人员</text>
</view>
<view class="teacher-list">
<view
v-for="teacher in teacherList"
:key="teacher.id"
class="teacher-item"
>
<view class="teacher-info">
<text class="teacher-name">{{ teacher.jsxm }}</text>
<text class="teacher-position">{{ teacher.dzzw || '' }} {{ teacher.qtzw || '' }}</text>
</view>
<view class="teacher-status">
<text class="status-text" :class="getStatusClass(teacher.qdStatus)">
{{ getStatusText(teacher.qdStatus) }}
</text>
<text v-if="teacher.qdwctime" class="sign-time">
{{ formatTime(teacher.qdwctime) }}
</text>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 底部操作 -->
<template #bottom>
<view class="bottom-actions">
<button class="action-btn back-btn" @click="handleBack">
返回列表
</button>
</view>
</template>
</BasicLayout>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { qdFindByIdApi, qdzxFindByQdParamsApi } from "@/api/base/server";
interface QdInfo {
id: string;
qdmc: string;
qdwz: string;
qdStatus: string;
jsId: string;
jsxm: string;
qdFbtime: string;
qdkstime: string;
qdjstime: string;
qdry: string;
}
interface TeacherInfo {
id: string;
jsId: string;
jsxm: string;
dzzw: string;
qtzw: string;
qdStatus: string;
qdwctime: string;
}
const qdId = ref<string>('');
const qdInfo = ref<QdInfo>({} as QdInfo);
const teacherList = ref<TeacherInfo[]>([]);
const totalCount = computed(() => teacherList.value.length);
const signedCount = computed(() => teacherList.value.filter(t => t.qdStatus === '1').length);
const unsignedCount = computed(() => totalCount.value - signedCount.value);
onLoad((options) => {
if (options && options.id) {
qdId.value = options.id;
loadQdDetail();
loadTeacherList();
} else {
uni.showToast({ title: "缺少签到ID参数", icon: "error" });
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
});
const loadQdDetail = async () => {
try {
const result = await qdFindByIdApi({ id: qdId.value });
if (result.resultCode === 1 && result.result) {
qdInfo.value = result.result;
}
} catch (error) {
console.error('加载签到详情失败:', error);
}
};
const loadTeacherList = async () => {
try {
const result = await qdzxFindByQdParamsApi({ qdId: qdId.value });
if (result.resultCode === 1 && result.result) {
teacherList.value = result.result;
}
} catch (error) {
console.error('加载教师列表失败:', error);
}
};
const getStatusClass = (status: string) => {
switch (status) {
case 'A':
return 'status-pending';
case 'B':
return 'status-draft';
case 'C':
return 'status-pushed';
case '1':
return 'status-signed';
case '0':
default:
return 'status-unsigned';
}
};
const getStatusText = (status: string) => {
switch (status) {
case 'A':
return '待推送';
case 'B':
return '暂存';
case 'C':
return '已推送';
case '1':
return '已签到';
case '0':
default:
return '未签到';
}
};
const formatTime = (time: string) => {
if (!time) return '未设置';
return new Date(time).toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
};
const handleBack = () => {
uni.navigateBack();
};
</script>
<style lang="scss" scoped>
.detail-scroll-view {
height: calc(100vh - 120px);
}
.detail-container {
padding: 15px;
}
.info-card {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.qd-title {
font-size: 20px;
font-weight: 600;
color: #333;
flex: 1;
}
.qd-status {
padding: 6px 12px;
border-radius: 16px;
font-size: 12px;
font-weight: 500;
}
.status-pending {
background: #fff3cd;
color: #856404;
}
.status-draft {
background: #d1ecf1;
color: #0c5460;
}
.status-pushed {
background: #d4edda;
color: #155724;
}
.status-signed {
background: #d4edda;
color: #155724;
}
.status-unsigned {
background: #f8d7da;
color: #721c24;
}
.info-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.info-item {
display: flex;
align-items: center;
}
.info-label {
font-size: 14px;
color: #666;
min-width: 80px;
font-weight: 500;
}
.info-value {
font-size: 14px;
color: #333;
flex: 1;
}
.stats-card {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.stats-header {
margin-bottom: 20px;
}
.stats-title {
font-size: 18px;
font-weight: 600;
color: #333;
}
.stats-content {
display: flex;
justify-content: space-around;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.stat-number {
font-size: 24px;
font-weight: 700;
color: #333;
&.signed {
color: #28a745;
}
&.unsigned {
color: #dc3545;
}
}
.stat-label {
font-size: 14px;
color: #666;
}
.teacher-list-card {
background: white;
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.list-header {
margin-bottom: 15px;
}
.list-title {
font-size: 18px;
font-weight: 600;
color: #333;
}
.teacher-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.teacher-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
}
.teacher-info {
flex: 1;
}
.teacher-name {
display: block;
font-size: 16px;
font-weight: 500;
color: #333;
margin-bottom: 4px;
}
.teacher-position {
font-size: 14px;
color: #666;
}
.teacher-status {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 4px;
}
.status-text {
font-size: 14px;
font-weight: 500;
padding: 2px 8px;
border-radius: 8px;
}
.sign-time {
font-size: 12px;
color: #999;
}
.bottom-actions {
padding: 15px;
background: white;
border-top: 1px solid #f0f0f0;
}
.back-btn {
width: 100%;
padding: 12px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
}
</style>