签到调整
This commit is contained in:
parent
daf902029b
commit
1a154398b9
@ -694,6 +694,14 @@
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/view/routine/qd/confirmself",
|
||||
"style": {
|
||||
"navigationBarTitleText": "签到确认",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "pages/view/routine/JiaoXueZiYuan/add-resource",
|
||||
"style": {
|
||||
|
||||
@ -54,34 +54,27 @@ async function forceRefreshPermission(changeTime?: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
function goByJs(js: any) {
|
||||
// 检查是否有签到相关参数
|
||||
const globalData = getGlobal;
|
||||
console.log('goByJs - globalData:', globalData);
|
||||
console.log('goByJs - js:', js);
|
||||
|
||||
if (globalData && globalData.qdId) {
|
||||
function goByqd(data: any) {
|
||||
if (data && data.qdId) {
|
||||
// 有签到参数,重定向到签到确认页面
|
||||
let confirmUrl = `/pages/view/routine/qd/confirm?qdId=${globalData.qdId}`;
|
||||
if (globalData.rqgqtime) {
|
||||
confirmUrl += `&rqgqtime=${globalData.rqgqtime}`;
|
||||
let confirmUrl = `/pages/view/routine/qd/confirm?qdId=${data.qdId}`;
|
||||
if (data.rqgqtime) {
|
||||
confirmUrl += `&rqgqtime=${data.rqgqtime}`;
|
||||
}
|
||||
if (globalData.timestampqd) {
|
||||
confirmUrl += `×tampqd=${globalData.timestampqd}`;
|
||||
if (data.timestampqd) {
|
||||
confirmUrl += `×tampqd=${data.timestampqd}`;
|
||||
}
|
||||
|
||||
|
||||
console.log('重定向到签到确认页面:', confirmUrl);
|
||||
uni.reLaunch({
|
||||
url: confirmUrl
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (getGlobal && getGlobal.type === '1') {
|
||||
uni.reLaunch({
|
||||
url: "/pages/view/routine/xk/qd?from=login"
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function goByJs(js: any) {
|
||||
if (js.confirmStatus == "A") {
|
||||
// 跳转到自助服务首页
|
||||
uni.reLaunch({
|
||||
@ -102,20 +95,15 @@ function goByJs(js: any) {
|
||||
|
||||
onLoad(async (data: any) => {
|
||||
console.log('launchPage onLoad - 接收到的参数:', data);
|
||||
|
||||
if (data && data.openId) {
|
||||
setGlobal(data);
|
||||
console.log('launchPage onLoad - 设置global数据后:', getGlobal);
|
||||
|
||||
try {
|
||||
const res = await checkOpenId({
|
||||
openId: data.openId,
|
||||
appCode: "JS",
|
||||
});
|
||||
|
||||
console.log('launchPage onLoad - checkOpenId结果:', res);
|
||||
|
||||
if (res.resultCode == 1 && res.result) {
|
||||
if (data && data.qdId) {
|
||||
goByqd(data);
|
||||
}else if (res.resultCode == 1 && res.result) {
|
||||
// 执行登录操作
|
||||
afterLoginAction(res.result);
|
||||
|
||||
|
||||
717
src/pages/view/routine/qd/confirmself.vue
Normal file
717
src/pages/view/routine/qd/confirmself.vue
Normal file
@ -0,0 +1,717 @@
|
||||
<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 === 'timeNotStarted'" class="time-not-started-step">
|
||||
<view class="time-not-started-content">
|
||||
<u-icon name="clock" size="80" color="#409EFF" />
|
||||
<text class="time-not-started-title">签到时间未开始</text>
|
||||
<text class="time-not-started-subtitle">签到打卡时间还未开始,请耐心等待</text>
|
||||
<text class="time-not-started-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">{{ formatTime(meetingInfo?.dkkstime) }}</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 === '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 === 'alreadySigned'" class="already-signed-step">
|
||||
<view class="already-signed-content">
|
||||
<u-icon name="checkmark-circle" size="80" color="#67C23A" />
|
||||
<text class="already-signed-title">您已签到成功</text>
|
||||
<text class="already-signed-subtitle">您已经完成本次签到,请勿重复签到</text>
|
||||
<text class="already-signed-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(qdzxRecord?.qdwctime) }}</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 { checkToken } = useUserStore();
|
||||
|
||||
// 页面参数
|
||||
const options = ref<any>({});
|
||||
|
||||
// 页面加载时接收参数
|
||||
onLoad((params) => {
|
||||
console.log('confirmself.vue onLoad - 接收到的参数:', params);
|
||||
|
||||
// 只从URL参数中获取qdId
|
||||
if (params && params.qdId) {
|
||||
console.log('confirmself.vue onLoad - 检测到qdId参数:', params.qdId);
|
||||
options.value = params;
|
||||
// 延迟执行,确保页面完全加载
|
||||
setTimeout(() => {
|
||||
console.log('confirmself.vue onLoad - 开始执行initializePage');
|
||||
initializePage();
|
||||
}, 100);
|
||||
} else {
|
||||
console.log('confirmself.vue onLoad - 没有qdId参数,显示错误');
|
||||
uni.showToast({ title: '缺少签到ID参数', icon: 'none' });
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
}
|
||||
});
|
||||
|
||||
// 签到相关变量
|
||||
const qdId = ref('');
|
||||
const qdzxId = ref('');
|
||||
|
||||
// 当前步骤
|
||||
const currentStep = ref<'sign' | 'confirm' | 'success' | 'notInList' | 'timeNotStarted' | 'timeExpired' | 'alreadySigned'>('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();
|
||||
|
||||
// 页面初始化函数
|
||||
const initializePage = async () => {
|
||||
console.log('confirmself.vue initializePage - 开始初始化');
|
||||
console.log('confirmself.vue initializePage - options.value:', options.value);
|
||||
|
||||
// 获取签到ID
|
||||
qdId.value = options.value.qdId;
|
||||
console.log('confirmself.vue initializePage - 设置qdId:', qdId.value);
|
||||
|
||||
|
||||
// 第一步:获取缓存的教师信息
|
||||
const jsData = userStore.getJs;
|
||||
console.log('confirmself.vue initializePage - 获取教师信息:', jsData);
|
||||
|
||||
if (jsData && Object.keys(jsData).length > 0) {
|
||||
userInfo.value = jsData;
|
||||
console.log('confirmself.vue initializePage - 教师信息有效,继续执行');
|
||||
} else {
|
||||
console.log('confirmself.vue initializePage - 教师信息无效,跳转登录页面');
|
||||
uni.showToast({ title: '请先登录', icon: 'none' });
|
||||
// 跳转到登录页面
|
||||
setTimeout(() => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/system/login/login'
|
||||
});
|
||||
}, 1500);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 第二步:通过qdId和教师ID查询该教师是否能签到
|
||||
console.log('confirmself.vue initializePage - 准备调用checkTeacherSignInPermission');
|
||||
await checkTeacherSignInPermission();
|
||||
};
|
||||
|
||||
// 页面初始化
|
||||
onMounted(async () => {
|
||||
// 如果页面已经在onLoad中初始化完成,这里不需要额外操作
|
||||
// 但可以添加一些DOM相关的初始化逻辑
|
||||
});
|
||||
|
||||
// 检查教师签到权限
|
||||
const checkTeacherSignInPermission = async () => {
|
||||
console.log('confirmself.vue checkTeacherSignInPermission - 开始检查签到权限');
|
||||
console.log('confirmself.vue checkTeacherSignInPermission - qdId:', qdId.value);
|
||||
console.log('confirmself.vue checkTeacherSignInPermission - userInfo.value.id:', userInfo.value?.id);
|
||||
|
||||
try {
|
||||
console.log('confirmself.vue checkTeacherSignInPermission - 准备调用qdzxFindByQdAndJsApi');
|
||||
const result = await qdzxFindByQdAndJsApi({
|
||||
qdId: qdId.value,
|
||||
jsId: userInfo.value.id
|
||||
});
|
||||
|
||||
console.log('confirmself.vue checkTeacherSignInPermission - API调用结果:', result);
|
||||
|
||||
if (result && result.resultCode === 1 && result.result) {
|
||||
// 找到签到记录
|
||||
console.log('confirmself.vue checkTeacherSignInPermission - 找到签到记录');
|
||||
qdzxRecord.value = result.result;
|
||||
qdzxId.value = result.result.id;
|
||||
|
||||
// 第三步:获取签到详情,验证是否需要签名
|
||||
console.log('confirmself.vue checkTeacherSignInPermission - 准备调用loadMeetingInfo');
|
||||
await loadMeetingInfo();
|
||||
} else if (result && result.resultCode === 0) {
|
||||
// 后端明确返回未找到记录
|
||||
console.log('confirmself.vue checkTeacherSignInPermission - 未找到签到记录');
|
||||
currentStep.value = 'notInList';
|
||||
await loadMeetingInfo(); // 仍然加载会议信息用于显示
|
||||
} else {
|
||||
// 其他错误情况
|
||||
console.log('confirmself.vue checkTeacherSignInPermission - 查询失败:', result);
|
||||
uni.showToast({ title: '查询签到权限失败', icon: 'none' });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('confirmself.vue checkTeacherSignInPermission - 网络异常:', 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 startTime = meetingInfo.value?.dkkstime ? new Date(meetingInfo.value.dkkstime) : null;
|
||||
const endTime = meetingInfo.value?.qdjstime ? new Date(meetingInfo.value.qdjstime) : null;
|
||||
|
||||
// 检查签到时间是否未开始
|
||||
if (startTime && currentTime < startTime) {
|
||||
// 签到时间未开始,显示"签到时间未开始"界面
|
||||
currentStep.value = 'timeNotStarted';
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查签到时间是否已结束
|
||||
if (endTime && currentTime > endTime) {
|
||||
// 签到时间已结束,显示"签到时间已结束"界面
|
||||
currentStep.value = 'timeExpired';
|
||||
return;
|
||||
}
|
||||
|
||||
// 只有在找到qdzxId时才检查是否需要签名
|
||||
if (qdzxId.value) {
|
||||
// 检查是否已经签到
|
||||
if (qdzxRecord.value?.qdStatus === '1') {
|
||||
// 已经签到,跳转到重复签到界面
|
||||
currentStep.value = 'alreadySigned';
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否需要签名
|
||||
if (meetingInfo.value?.mdqz === '1') {
|
||||
// 需要签名,调用签名组件
|
||||
await handleSignature();
|
||||
} else {
|
||||
// 不需要签名,直接到确认界面
|
||||
currentStep.value = 'confirm';
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (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;
|
||||
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;
|
||||
}
|
||||
|
||||
const result = await qdzxSignInApi(params);
|
||||
|
||||
if (result && result.resultCode === 1) {
|
||||
currentStep.value = 'success';
|
||||
} else {
|
||||
const errorMsg = result?.message || '签到失败';
|
||||
uni.showToast({ title: errorMsg, icon: 'none' });
|
||||
}
|
||||
} catch (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: 16px;
|
||||
align-items: flex-start;
|
||||
|
||||
.info-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
min-width: 80px;
|
||||
text-align: right !important;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
text-align: left !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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: 12px;
|
||||
align-items: flex-start;
|
||||
|
||||
.info-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
min-width: 80px;
|
||||
text-align: right !important;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
text-align: left !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
background: #f5f5f5;
|
||||
color: #666;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* 签到时间未开始阶段 */
|
||||
.time-not-started-step {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
padding: 40px 24px;
|
||||
margin-top: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.time-not-started-content {
|
||||
.time-not-started-title {
|
||||
display: block;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 16px 0 8px;
|
||||
}
|
||||
|
||||
.time-not-started-subtitle {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.time-not-started-tip {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 签到时间已结束阶段 */
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 重复签到阶段 */
|
||||
.already-signed-step {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
padding: 40px 24px;
|
||||
margin-top: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.already-signed-content {
|
||||
.already-signed-title {
|
||||
display: block;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 16px 0 8px;
|
||||
}
|
||||
|
||||
.already-signed-subtitle {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.already-signed-tip {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -84,22 +84,34 @@
|
||||
</view>
|
||||
<view class="card-footer">
|
||||
<view class="footer-actions">
|
||||
<image
|
||||
src="/static/base/qr-code.png"
|
||||
class="footer-action-icon qr-icon"
|
||||
@click="goToQRCode(data.id)"
|
||||
/>
|
||||
<image
|
||||
src="/static/base/details.png"
|
||||
class="footer-action-icon details-icon"
|
||||
@click="goToFeedback(data.id)"
|
||||
/>
|
||||
<image
|
||||
v-if="data.qdStatus === 'A'"
|
||||
src="/static/base/push.png"
|
||||
class="footer-action-icon push-icon"
|
||||
@click="goToPush(data.id)"
|
||||
/>
|
||||
<view class="action-item" @click="goToQRCode(data.id)">
|
||||
<image
|
||||
src="/static/base/qr-code.png"
|
||||
class="footer-action-icon qr-icon"
|
||||
/>
|
||||
<view v-if="showTooltip" class="tooltip">二维码</view>
|
||||
</view>
|
||||
<view class="action-item" @click="goToSignIn(data.id)">
|
||||
<image
|
||||
src="/static/base/view/qdself.png"
|
||||
class="footer-action-icon qdself-icon"
|
||||
/>
|
||||
<view v-if="showTooltip" class="tooltip">签到</view>
|
||||
</view>
|
||||
<view class="action-item" @click="goToFeedback(data.id)">
|
||||
<image
|
||||
src="/static/base/details.png"
|
||||
class="footer-action-icon details-icon"
|
||||
/>
|
||||
<view v-if="showTooltip" class="tooltip">详情</view>
|
||||
</view>
|
||||
<view v-if="data.qdStatus === 'A'" class="action-item" @click="goToPush(data.id)">
|
||||
<image
|
||||
src="/static/base/push.png"
|
||||
class="footer-action-icon push-icon"
|
||||
/>
|
||||
<view v-if="showTooltip" class="tooltip">推送</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -124,6 +136,7 @@
|
||||
import { qdFindPageApi } from "@/api/base/server";
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
|
||||
interface QdItem {
|
||||
id: string;
|
||||
@ -149,6 +162,9 @@ const searchForm = reactive({
|
||||
const dataList = ref<QdItem[]>([]);
|
||||
const isLoading = ref(false);
|
||||
|
||||
// 气泡提示显示状态
|
||||
const showTooltip = ref(true);
|
||||
|
||||
// 开始时间选择
|
||||
const onStartTimeChange = (e: any) => {
|
||||
searchForm.startTime = e.detail.value;
|
||||
@ -258,6 +274,43 @@ const goToQRCode = (id: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
// 跳转到签到页面
|
||||
const goToSignIn = (id: string) => {
|
||||
// 获取用户store
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 获取缓存的教师信息
|
||||
const jsData = userStore.getJs;
|
||||
|
||||
// 找到对应的签到项目
|
||||
const qdItem = dataList.value.find(item => item.id === id);
|
||||
|
||||
if (!qdItem) {
|
||||
uni.showToast({ title: '签到信息不存在', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证发布者权限
|
||||
if (jsData && jsData.id && qdItem.jsId) {
|
||||
if (jsData.id !== qdItem.jsId) {
|
||||
uni.showToast({
|
||||
title: '只有发布者才能使用点击签到功能',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
uni.showToast({ title: '用户信息获取失败,请重新登录', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
// 权限验证通过,跳转到签到页面
|
||||
uni.navigateTo({
|
||||
url: `/pages/view/routine/qd/confirmself?qdId=${id}`
|
||||
});
|
||||
};
|
||||
|
||||
// 页面显示时刷新数据
|
||||
onShow(() => {
|
||||
getQdList();
|
||||
@ -266,6 +319,11 @@ onShow(() => {
|
||||
// 页面加载时也调用一次
|
||||
onMounted(() => {
|
||||
getQdList();
|
||||
|
||||
// 6秒后隐藏气泡提示
|
||||
setTimeout(() => {
|
||||
showTooltip.value = false;
|
||||
}, 6000);
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -474,6 +532,14 @@ onMounted(() => {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
.action-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.footer-action-icon {
|
||||
padding: 5px;
|
||||
}
|
||||
@ -484,6 +550,22 @@ onMounted(() => {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.qdself-icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.details-icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
@ -498,6 +580,43 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
// 气泡提示样式
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
top: -35px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
z-index: 1000;
|
||||
animation: tooltipFadeIn 0.3s ease-out;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border: 4px solid transparent;
|
||||
border-top-color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes tooltipFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-50%) translateY(-5px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
// 新增按钮 - 固定在底部
|
||||
.add-button-fixed {
|
||||
position: fixed;
|
||||
|
||||
@ -42,8 +42,23 @@
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<!-- 自定义教师选择 -->
|
||||
<view class="filter-row" v-if="selectType === 0">
|
||||
<view class="filter-label">{{ getSecondSelectLabel() }}</view>
|
||||
<view class="custom-teacher-picker">
|
||||
<BasicJsPicker
|
||||
:defaultValue="customSelectedTeachers"
|
||||
:multiple="true"
|
||||
@change="onCustomTeacherChange"
|
||||
placeholder="请选择教师"
|
||||
title="选择教师"
|
||||
ref="customTeacherPickerRef"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 具体选择 -->
|
||||
<view class="filter-row" v-if="selectType && selectType !== 1">
|
||||
<view class="filter-row" v-if="selectType && selectType !== 1 && selectType !== 0">
|
||||
<view class="filter-label">{{ getSecondSelectLabel() }}</view>
|
||||
<view class="multi-select-container">
|
||||
<view class="selected-items-display" @click="showMultiSelectModal">
|
||||
@ -60,6 +75,11 @@
|
||||
<text class="tip-text">💡 提示:选择"所有老师"将直接加载所有教师数据</text>
|
||||
</view>
|
||||
|
||||
<!-- 自定义教师提示 -->
|
||||
<view class="filter-tip" v-if="selectType === 0">
|
||||
<text class="tip-text">💡 提示:选择"自定义教师"可以手动选择特定的教师</text>
|
||||
</view>
|
||||
|
||||
<!-- 多选弹窗 -->
|
||||
<view v-if="showModal" class="modal-overlay" @click="hideMultiSelectModal">
|
||||
<view class="modal-content" @click.stop>
|
||||
@ -159,6 +179,7 @@ import {
|
||||
kmFindAllApi,
|
||||
zwFindAllApi
|
||||
} from "@/api/base/server";
|
||||
import BasicJsPicker from '@/components/BasicJsPicker/Picker.vue';
|
||||
|
||||
interface TeacherInfo {
|
||||
id: string;
|
||||
@ -177,8 +198,13 @@ const selectedTeachers = ref<TeacherInfo[]>([]);
|
||||
const isLoading = ref(false);
|
||||
const showModal = ref(false); // 控制多选弹窗的显示
|
||||
|
||||
// 自定义教师选择相关
|
||||
const customTeacherPickerRef = ref<any>(null);
|
||||
const customSelectedTeachers = ref<string[]>([]);
|
||||
|
||||
// 选择类别选项
|
||||
const selectTypeOptions = [
|
||||
{ value: 0, label: '自定义教师' },
|
||||
{ value: 1, label: '所有老师' },
|
||||
{ value: 2, label: '按科目' },
|
||||
{ value: 3, label: '按班主任' },
|
||||
@ -207,6 +233,8 @@ const getZwTypeText = () => {
|
||||
// 获取第二个选择框的标签
|
||||
const getSecondSelectLabel = () => {
|
||||
switch (selectType.value) {
|
||||
case 0:
|
||||
return '选择教师';
|
||||
case 1:
|
||||
return '年级/班级';
|
||||
case 2:
|
||||
@ -241,6 +269,12 @@ const onSelectTypeChange = async (e: any) => {
|
||||
selectType.value = selectTypeOptions[e.detail.value].value;
|
||||
selectTwoType.value = [];
|
||||
selectedTeachers.value = [];
|
||||
customSelectedTeachers.value = [];
|
||||
|
||||
// 如果选择"自定义教师",清空选择
|
||||
if (selectType.value === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果选择"所有老师",直接加载所有教师数据
|
||||
if (selectType.value === 1) {
|
||||
@ -293,6 +327,25 @@ const confirmMultiSelect = () => {
|
||||
loadTeachers();
|
||||
};
|
||||
|
||||
// 自定义教师选择变化处理
|
||||
const onCustomTeacherChange = (teachers: any[]) => {
|
||||
console.log('自定义教师选择变化:', teachers);
|
||||
|
||||
// 更新自定义选择的教师ID列表
|
||||
customSelectedTeachers.value = teachers.map(teacher => teacher.value || teacher.id);
|
||||
|
||||
// 转换为统一的教师信息格式
|
||||
selectedTeachers.value = teachers.map(teacher => ({
|
||||
id: teacher.id || teacher.value,
|
||||
jsxm: teacher.jsxm || teacher.label,
|
||||
jsId: teacher.id || teacher.value,
|
||||
dzzw: teacher.dzzw || '',
|
||||
qtzw: teacher.qtzw || ''
|
||||
}));
|
||||
|
||||
console.log('处理后的自定义教师数据:', selectedTeachers.value);
|
||||
};
|
||||
|
||||
// 从localStorage加载所有教师数据
|
||||
const loadAllTeachersFromStorage = async () => {
|
||||
try {
|
||||
@ -1114,6 +1167,10 @@ const ensureTeacherDataCached = async () => {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.custom-teacher-picker {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.selected-items-display {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
BIN
src/static/base/view/qdself.png
Normal file
BIN
src/static/base/view/qdself.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
Loading…
x
Reference in New Issue
Block a user