630 lines
16 KiB
Vue
630 lines
16 KiB
Vue
|
|
<template>
|
|||
|
|
<view class="confirm-page">
|
|||
|
|
<!-- 确认签到阶段 -->
|
|||
|
|
<view v-if="currentStep === 'confirm'" class="confirm-step">
|
|||
|
|
<view class="confirm-header">
|
|||
|
|
<text class="confirm-title">确认签到</text>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<view class="meeting-info">
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="info-label">会议名称:</text>
|
|||
|
|
<text class="info-value">{{ meetingInfo?.qdmc || '未设置' }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="info-label">会议时间:</text>
|
|||
|
|
<text class="info-value">{{ formatTime(meetingInfo?.qdkstime) }} - {{ formatTime(meetingInfo?.qdjstime) }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="info-label">会议地点:</text>
|
|||
|
|
<text class="info-value">{{ meetingInfo?.qdwz || '未设置' }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="info-label">签到人员:</text>
|
|||
|
|
<text class="info-value">{{ userInfo?.jsxm || '未设置' }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<view v-if="sign_file" class="signature-preview">
|
|||
|
|
<text class="preview-label">签名预览:</text>
|
|||
|
|
<image :src="sign_file" class="signature-preview-img" />
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<button @click="submitSignIn" class="submit-btn">确认签到</button>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 签到成功阶段 -->
|
|||
|
|
<view v-else-if="currentStep === 'success'" class="success-step">
|
|||
|
|
<view class="success-content">
|
|||
|
|
<u-icon name="checkmark-circle" size="80" color="#67C23A" />
|
|||
|
|
<text class="success-title">签到成功</text>
|
|||
|
|
<text class="success-time">{{ formatTime(new Date()) }}</text>
|
|||
|
|
<text class="success-tip">您已成功完成签到</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 不在签到名单阶段 -->
|
|||
|
|
<view v-else-if="currentStep === 'notInList'" class="not-in-list-step">
|
|||
|
|
<view class="not-in-list-content">
|
|||
|
|
<u-icon name="close-circle" size="80" color="#F56C6C" />
|
|||
|
|
<text class="not-in-list-title">您不在签到名单中</text>
|
|||
|
|
<text class="not-in-list-subtitle">抱歉,您没有在此次会议的签到名单中</text>
|
|||
|
|
<text class="not-in-list-tip">请联系会议组织者确认您的参会资格</text>
|
|||
|
|
|
|||
|
|
<view class="meeting-info-card">
|
|||
|
|
<text class="card-title">会议信息</text>
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="info-label">会议名称:</text>
|
|||
|
|
<text class="info-value">{{ meetingInfo?.qdmc || '未设置' }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="info-label">会议时间:</text>
|
|||
|
|
<text class="info-value">{{ formatTime(meetingInfo?.qdkstime) }} - {{ formatTime(meetingInfo?.qdjstime) }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="info-label">会议地点:</text>
|
|||
|
|
<text class="info-value">{{ meetingInfo?.qdwz || '未设置' }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<button @click="goBack" class="back-btn">返回</button>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 签到时间已结束阶段 -->
|
|||
|
|
<view v-else-if="currentStep === 'timeExpired'" class="time-expired-step">
|
|||
|
|
<view class="time-expired-content">
|
|||
|
|
<u-icon name="clock" size="80" color="#E6A23C" />
|
|||
|
|
<text class="time-expired-title">签到时间已结束</text>
|
|||
|
|
<text class="time-expired-subtitle">抱歉,本次签到的时间已经结束</text>
|
|||
|
|
<text class="time-expired-tip">请关注下次签到通知</text>
|
|||
|
|
|
|||
|
|
<view class="meeting-info-card">
|
|||
|
|
<text class="card-title">会议信息</text>
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="info-label">会议名称:</text>
|
|||
|
|
<text class="info-value">{{ meetingInfo?.qdmc || '未设置' }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="info-label">会议时间:</text>
|
|||
|
|
<text class="info-value">{{ formatTime(meetingInfo?.qdkstime) }} - {{ formatTime(meetingInfo?.qdjstime) }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="info-label">会议地点:</text>
|
|||
|
|
<text class="info-value">{{ meetingInfo?.qdwz || '未设置' }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="info-label">当前时间:</text>
|
|||
|
|
<text class="info-value">{{ formatTime(new Date()) }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<button @click="goBack" class="back-btn">返回</button>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 二维码过期阶段 -->
|
|||
|
|
<view v-else-if="currentStep === 'qrExpired'" class="qr-expired-step">
|
|||
|
|
<view class="qr-expired-content">
|
|||
|
|
<u-icon name="close-circle" size="80" color="#F56C6C" />
|
|||
|
|
<text class="qr-expired-title">二维码已过期</text>
|
|||
|
|
<text class="qr-expired-subtitle">抱歉,您扫描的二维码已经过期</text>
|
|||
|
|
<text class="qr-expired-tip">请重新扫描最新的二维码</text>
|
|||
|
|
|
|||
|
|
<view class="qr-info-card">
|
|||
|
|
<text class="card-title">二维码信息</text>
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="info-label">二维码有效期:</text>
|
|||
|
|
<text class="info-value">60秒</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="info-label">当前时间:</text>
|
|||
|
|
<text class="info-value">{{ formatTime(new Date()) }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<button @click="goBack" class="back-btn">返回</button>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 签名组件 -->
|
|||
|
|
<BasicSign v-if="showSignature" ref="signCompRef" :title="signTitle"></BasicSign>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script lang="ts" setup>
|
|||
|
|
import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue';
|
|||
|
|
import { onLoad } from '@dcloudio/uni-app';
|
|||
|
|
import { qdFindByIdApi, qdzxSignInApi, qdzxFindByQdParamsApi, qdzxFindByQdAndJsApi } from '@/api/base/server';
|
|||
|
|
import { useUserStore } from '@/store/modules/user';
|
|||
|
|
|
|||
|
|
// 页面参数
|
|||
|
|
const qdId = ref('');
|
|||
|
|
const qdzxId = ref('');
|
|||
|
|
|
|||
|
|
// 当前步骤
|
|||
|
|
const currentStep = ref<'sign' | 'confirm' | 'success' | 'notInList' | 'timeExpired' | 'qrExpired'>('confirm');
|
|||
|
|
|
|||
|
|
// 用户信息
|
|||
|
|
const userInfo = ref<any>(null);
|
|||
|
|
|
|||
|
|
// 会议信息
|
|||
|
|
const meetingInfo = ref<any>(null);
|
|||
|
|
|
|||
|
|
// 签到执行记录
|
|||
|
|
const qdzxRecord = ref<any>(null);
|
|||
|
|
|
|||
|
|
// 签名组件相关
|
|||
|
|
const signCompRef = ref<any>(null);
|
|||
|
|
const signTitle = ref<string>("签到签名");
|
|||
|
|
const sign_file = ref<string>("");
|
|||
|
|
const showSignature = ref<boolean>(false);
|
|||
|
|
|
|||
|
|
// 获取用户store
|
|||
|
|
const userStore = useUserStore();
|
|||
|
|
|
|||
|
|
onLoad(async (options) => {
|
|||
|
|
if (options?.qdId) {
|
|||
|
|
qdId.value = options.qdId;
|
|||
|
|
}
|
|||
|
|
if (options?.qdzxId) {
|
|||
|
|
qdzxId.value = options.qdzxId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 第一步:获取缓存的教师信息
|
|||
|
|
const jsData = userStore.getJs;
|
|||
|
|
if (jsData && Object.keys(jsData).length > 0) {
|
|||
|
|
userInfo.value = jsData;
|
|||
|
|
console.log('获取到教师信息:', jsData);
|
|||
|
|
} else {
|
|||
|
|
console.error('未找到教师信息,请先登录');
|
|||
|
|
uni.showToast({ title: '请先登录', icon: 'none' });
|
|||
|
|
// 跳转到登录页面
|
|||
|
|
setTimeout(() => {
|
|||
|
|
uni.navigateTo({
|
|||
|
|
url: '/pages/system/login/login'
|
|||
|
|
});
|
|||
|
|
}, 1500);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证二维码时间戳是否过期
|
|||
|
|
if (options?.timestamp) {
|
|||
|
|
const qrTimestamp = parseInt(options.timestamp);
|
|||
|
|
const currentTimestamp = Date.now();
|
|||
|
|
const timeDiff = currentTimestamp - qrTimestamp;
|
|||
|
|
const maxValidTime = 60 * 1000; // 60秒有效期
|
|||
|
|
|
|||
|
|
console.log('二维码时间戳验证:', {
|
|||
|
|
qrTimestamp,
|
|||
|
|
currentTimestamp,
|
|||
|
|
timeDiff,
|
|||
|
|
maxValidTime
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (timeDiff > maxValidTime) {
|
|||
|
|
console.error('二维码已过期');
|
|||
|
|
currentStep.value = 'qrExpired';
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 第二步:通过qdId和教师ID查询该教师是否能签到
|
|||
|
|
await checkTeacherSignInPermission();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 检查教师签到权限
|
|||
|
|
const checkTeacherSignInPermission = async () => {
|
|||
|
|
try {
|
|||
|
|
const result = await qdzxFindByQdAndJsApi({
|
|||
|
|
qdId: qdId.value,
|
|||
|
|
jsId: userInfo.value.id
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (result && result.resultCode === 1 && result.result) {
|
|||
|
|
// 找到签到记录
|
|||
|
|
qdzxRecord.value = result.result;
|
|||
|
|
qdzxId.value = result.result.id;
|
|||
|
|
console.log('找到教师签到记录:', result.result);
|
|||
|
|
|
|||
|
|
// 第三步:获取签到详情,验证是否需要签名
|
|||
|
|
await loadMeetingInfo();
|
|||
|
|
} else {
|
|||
|
|
// 没有找到签到记录,显示"不在签到名单中"界面
|
|||
|
|
currentStep.value = 'notInList';
|
|||
|
|
await loadMeetingInfo(); // 仍然加载会议信息用于显示
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('查询教师签到权限失败:', error);
|
|||
|
|
uni.showToast({ title: '查询签到权限失败', icon: 'none' });
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 加载会议信息
|
|||
|
|
const loadMeetingInfo = async () => {
|
|||
|
|
try {
|
|||
|
|
const result = await qdFindByIdApi({ id: qdId.value });
|
|||
|
|
if (result && result.resultCode === 1) {
|
|||
|
|
meetingInfo.value = result.result;
|
|||
|
|
|
|||
|
|
// 1. 验证签到时间是否已结束
|
|||
|
|
const currentTime = new Date();
|
|||
|
|
const endTime = meetingInfo.value?.qdjstime ? new Date(meetingInfo.value.qdjstime) : null;
|
|||
|
|
|
|||
|
|
if (endTime && currentTime > endTime) {
|
|||
|
|
// 签到时间已结束,显示"签到时间已结束"界面
|
|||
|
|
currentStep.value = 'timeExpired';
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 只有在找到qdzxId时才检查是否需要签名
|
|||
|
|
if (qdzxId.value) {
|
|||
|
|
// 检查是否需要签名
|
|||
|
|
if (meetingInfo.value?.mdqz === '1') {
|
|||
|
|
// 需要签名,调用签名组件
|
|||
|
|
await handleSignature();
|
|||
|
|
} else {
|
|||
|
|
// 不需要签名,直接到确认界面
|
|||
|
|
currentStep.value = 'confirm';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('加载会议信息失败:', error);
|
|||
|
|
uni.showToast({ title: '加载会议信息失败', icon: 'none' });
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 处理签名
|
|||
|
|
const handleSignature = async () => {
|
|||
|
|
try {
|
|||
|
|
showSignature.value = true;
|
|||
|
|
sign_file.value = '';
|
|||
|
|
signTitle.value = "签到签名";
|
|||
|
|
|
|||
|
|
// 等待组件渲染完成
|
|||
|
|
await nextTick();
|
|||
|
|
const data = await signCompRef.value.getSyncSignature();
|
|||
|
|
sign_file.value = data.base64;
|
|||
|
|
showSignature.value = false;
|
|||
|
|
|
|||
|
|
// 签名完成后,跳转到确认界面
|
|||
|
|
currentStep.value = 'confirm';
|
|||
|
|
} catch (error) {
|
|||
|
|
showSignature.value = false;
|
|||
|
|
console.error('签名失败:', error);
|
|||
|
|
uni.showToast({ title: '签名失败', icon: 'none' });
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 提交签到
|
|||
|
|
const submitSignIn = async () => {
|
|||
|
|
try {
|
|||
|
|
const params: any = {
|
|||
|
|
qdzxId: qdzxId.value,
|
|||
|
|
signInTime: new Date().toISOString() // 转换为ISO字符串格式
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 如果有签名文件,添加到参数中
|
|||
|
|
if (sign_file.value) {
|
|||
|
|
params.signatureImage = sign_file.value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('提交签到参数:', JSON.stringify(params, null, 2));
|
|||
|
|
console.log('参数类型检查:', {
|
|||
|
|
qdzxId: typeof params.qdzxId,
|
|||
|
|
signInTime: typeof params.signInTime,
|
|||
|
|
signatureImage: typeof params.signatureImage
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const result = await qdzxSignInApi(params);
|
|||
|
|
console.log('签到结果:', result);
|
|||
|
|
|
|||
|
|
if (result && result.resultCode === 1) {
|
|||
|
|
currentStep.value = 'success';
|
|||
|
|
} else {
|
|||
|
|
const errorMsg = result?.msg || result?.message || '签到失败';
|
|||
|
|
uni.showToast({ title: errorMsg, icon: 'none' });
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('签到失败:', error);
|
|||
|
|
uni.showToast({ title: '网络异常,请重试', icon: 'none' });
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 格式化时间
|
|||
|
|
const formatTime = (time: string | Date) => {
|
|||
|
|
if (!time) return '未设置';
|
|||
|
|
const date = new Date(time);
|
|||
|
|
return date.toLocaleString('zh-CN', {
|
|||
|
|
year: 'numeric',
|
|||
|
|
month: '2-digit',
|
|||
|
|
day: '2-digit',
|
|||
|
|
hour: '2-digit',
|
|||
|
|
minute: '2-digit'
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 返回上一页
|
|||
|
|
const goBack = () => {
|
|||
|
|
uni.navigateBack();
|
|||
|
|
};
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.confirm-page {
|
|||
|
|
min-height: 100vh;
|
|||
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|||
|
|
padding: 20px;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确认签到阶段
|
|||
|
|
.confirm-step {
|
|||
|
|
background: white;
|
|||
|
|
border-radius: 16px;
|
|||
|
|
padding: 24px;
|
|||
|
|
margin-top: 40px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.confirm-header {
|
|||
|
|
text-align: center;
|
|||
|
|
margin-bottom: 24px;
|
|||
|
|
|
|||
|
|
.confirm-title {
|
|||
|
|
font-size: 24px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #333;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.meeting-info {
|
|||
|
|
margin-bottom: 24px;
|
|||
|
|
|
|||
|
|
.info-item {
|
|||
|
|
display: flex;
|
|||
|
|
margin-bottom: 12px;
|
|||
|
|
|
|||
|
|
.info-label {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #666;
|
|||
|
|
min-width: 80px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-value {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #333;
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.signature-preview {
|
|||
|
|
margin-bottom: 24px;
|
|||
|
|
|
|||
|
|
.preview-label {
|
|||
|
|
display: block;
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #666;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.signature-preview-img {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100px;
|
|||
|
|
border: 1px solid #ddd;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.submit-btn {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 44px;
|
|||
|
|
background: linear-gradient(135deg, #67C23A 0%, #85ce61 100%);
|
|||
|
|
color: white;
|
|||
|
|
border: none;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 签到成功阶段
|
|||
|
|
.success-step {
|
|||
|
|
background: white;
|
|||
|
|
border-radius: 16px;
|
|||
|
|
padding: 40px 24px;
|
|||
|
|
margin-top: 40px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.success-content {
|
|||
|
|
.success-title {
|
|||
|
|
display: block;
|
|||
|
|
font-size: 24px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #333;
|
|||
|
|
margin: 16px 0 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.success-time {
|
|||
|
|
display: block;
|
|||
|
|
font-size: 16px;
|
|||
|
|
color: #666;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.success-tip {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #999;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 不在签到名单阶段
|
|||
|
|
.not-in-list-step {
|
|||
|
|
background: white;
|
|||
|
|
border-radius: 16px;
|
|||
|
|
padding: 24px;
|
|||
|
|
margin-top: 40px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.not-in-list-content {
|
|||
|
|
.not-in-list-title {
|
|||
|
|
display: block;
|
|||
|
|
font-size: 24px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #333;
|
|||
|
|
margin: 16px 0 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.not-in-list-subtitle {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #666;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.not-in-list-tip {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #999;
|
|||
|
|
margin-bottom: 24px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.meeting-info-card {
|
|||
|
|
background: #f9f9f9;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 16px;
|
|||
|
|
margin-bottom: 24px;
|
|||
|
|
|
|||
|
|
.card-title {
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #333;
|
|||
|
|
margin-bottom: 12px;
|
|||
|
|
padding-bottom: 8px;
|
|||
|
|
border-bottom: 1px solid #eee;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-item {
|
|||
|
|
display: flex;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
|
|||
|
|
.info-label {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #666;
|
|||
|
|
min-width: 80px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-value {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #333;
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.back-btn {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 44px;
|
|||
|
|
background: #f5f5f5;
|
|||
|
|
color: #666;
|
|||
|
|
border: none;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
font-size: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 签到时间已结束阶段 */
|
|||
|
|
.time-expired-step {
|
|||
|
|
background: white;
|
|||
|
|
border-radius: 16px;
|
|||
|
|
padding: 40px 24px;
|
|||
|
|
margin-top: 40px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.time-expired-content {
|
|||
|
|
.time-expired-title {
|
|||
|
|
display: block;
|
|||
|
|
font-size: 24px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #333;
|
|||
|
|
margin: 16px 0 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.time-expired-subtitle {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #666;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.time-expired-tip {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #999;
|
|||
|
|
margin-bottom: 24px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 二维码过期阶段 */
|
|||
|
|
.qr-expired-step {
|
|||
|
|
background: white;
|
|||
|
|
border-radius: 16px;
|
|||
|
|
padding: 40px 24px;
|
|||
|
|
margin-top: 40px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.qr-expired-content {
|
|||
|
|
.qr-expired-title {
|
|||
|
|
display: block;
|
|||
|
|
font-size: 24px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #333;
|
|||
|
|
margin: 16px 0 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.qr-expired-subtitle {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #666;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.qr-expired-tip {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #999;
|
|||
|
|
margin-bottom: 24px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.qr-info-card {
|
|||
|
|
background: #f9f9f9;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 16px;
|
|||
|
|
margin-bottom: 24px;
|
|||
|
|
|
|||
|
|
.card-title {
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #333;
|
|||
|
|
margin-bottom: 12px;
|
|||
|
|
padding-bottom: 8px;
|
|||
|
|
border-bottom: 1px solid #eee;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-item {
|
|||
|
|
display: flex;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
|
|||
|
|
.info-label {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #666;
|
|||
|
|
min-width: 80px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-value {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #333;
|
|||
|
|
flex: 1;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|