公文流转
This commit is contained in:
parent
e97d71f230
commit
d866410866
@ -41,7 +41,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, onMounted} from "vue";
|
||||
import {ref, onMounted, onActivated} from "vue";
|
||||
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
|
||||
import { xxtsListApi } from "@/api/base/server";
|
||||
import { getTimeAgo } from "@/utils/dateUtils";
|
||||
@ -80,7 +80,7 @@ const fetchDbLxMap = async () => {
|
||||
const [register, {reload, setParam}] = useLayout({
|
||||
api: xxtsListApi,
|
||||
componentProps: {
|
||||
auto: false
|
||||
auto: true // 改为true,让组件自动调用接口
|
||||
},
|
||||
});
|
||||
|
||||
@ -107,6 +107,12 @@ onMounted(() => {
|
||||
fetchListData(currentTab.value);
|
||||
});
|
||||
|
||||
// 页面激活时重新加载数据(解决第二次进入不调用接口的问题)
|
||||
onActivated(() => {
|
||||
console.log('页面激活,重新加载数据');
|
||||
fetchListData(currentTab.value);
|
||||
});
|
||||
|
||||
const goToDetail = (data: any) => {
|
||||
if (data && data.id) {
|
||||
setDb(data);
|
||||
|
||||
@ -29,33 +29,106 @@
|
||||
<!-- 文件信息 -->
|
||||
<view class="file-section">
|
||||
<view class="section-title">附件</view>
|
||||
<view class="file-list">
|
||||
<view class="file-list" v-if="hasAttachments">
|
||||
<!-- 处理单个附件(从gwInfo直接获取) -->
|
||||
<view
|
||||
v-if="gwInfo.fileUrl"
|
||||
class="file-item"
|
||||
@click="previewSingleFile"
|
||||
>
|
||||
<view class="file-icon">
|
||||
<text v-if="isImage(gwInfo.fileFormat || '')">🖼️</text>
|
||||
<text v-else-if="isVideo(gwInfo.fileFormat || '')">🎥</text>
|
||||
<text v-else-if="canPreview(gwInfo.fileFormat || '')">📄</text>
|
||||
<text v-else>📎</text>
|
||||
</view>
|
||||
<view class="file-info">
|
||||
<text class="file-name">{{ gwInfo.fileName || '未知文件' }}</text>
|
||||
<text class="file-type">{{ (gwInfo.fileFormat || 'unknown').toUpperCase() }}</text>
|
||||
</view>
|
||||
<view class="file-actions">
|
||||
<u-button
|
||||
v-if="canPreview(gwInfo.fileFormat || '') && !isVideo(gwInfo.fileFormat || '')"
|
||||
text="预览"
|
||||
size="mini"
|
||||
type="primary"
|
||||
@click.stop="previewSingleFile"
|
||||
/>
|
||||
<u-button
|
||||
v-if="isVideo(gwInfo.fileFormat || '')"
|
||||
text="播放"
|
||||
size="mini"
|
||||
type="success"
|
||||
@click.stop="previewSingleFile"
|
||||
/>
|
||||
<u-button
|
||||
v-if="isImage(gwInfo.fileFormat || '')"
|
||||
text="查看"
|
||||
size="mini"
|
||||
type="warning"
|
||||
@click.stop="previewSingleFile"
|
||||
/>
|
||||
<u-button
|
||||
text="下载"
|
||||
size="mini"
|
||||
type="info"
|
||||
@click.stop="downloadSingleFile"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 处理多个附件(从files数组获取) -->
|
||||
<view
|
||||
v-for="(file, index) in gwInfo.files"
|
||||
:key="index"
|
||||
class="file-item"
|
||||
@click="previewFile(file)"
|
||||
>
|
||||
<view class="file-icon">📎</view>
|
||||
<view class="file-icon">
|
||||
<text v-if="isImage(getFileSuffix(file))">🖼️</text>
|
||||
<text v-else-if="isVideo(getFileSuffix(file))">🎥</text>
|
||||
<text v-else-if="canPreview(getFileSuffix(file))">📄</text>
|
||||
<text v-else>📎</text>
|
||||
</view>
|
||||
<view class="file-info">
|
||||
<text class="file-name">{{ file.name }}</text>
|
||||
<text class="file-name">{{ getFileName(file) }}</text>
|
||||
<text class="file-size">{{ formatFileSize(file.size) }}</text>
|
||||
<text class="file-type">{{ getFileSuffix(file).toUpperCase() }}</text>
|
||||
</view>
|
||||
<view class="file-actions">
|
||||
<u-button
|
||||
v-if="canPreview(getFileSuffix(file)) && !isVideo(getFileSuffix(file))"
|
||||
text="预览"
|
||||
size="mini"
|
||||
type="primary"
|
||||
@click.stop="previewFile(file)"
|
||||
/>
|
||||
<u-button
|
||||
v-if="isVideo(getFileSuffix(file))"
|
||||
text="播放"
|
||||
size="mini"
|
||||
type="success"
|
||||
@click.stop="previewFile(file)"
|
||||
/>
|
||||
<u-button
|
||||
v-if="isImage(getFileSuffix(file))"
|
||||
text="查看"
|
||||
size="mini"
|
||||
type="warning"
|
||||
@click.stop="previewFile(file)"
|
||||
/>
|
||||
<u-button
|
||||
text="下载"
|
||||
size="mini"
|
||||
type="info"
|
||||
@click.stop="downloadFile(file)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="no-files">
|
||||
<text>暂无附件</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 当前处理人 -->
|
||||
@ -95,7 +168,7 @@
|
||||
<view class="section-title">抄送人</view>
|
||||
<view class="cc-list">
|
||||
<view
|
||||
v-for="ccUser in ccUsers"
|
||||
v-for="ccUser in displayedCcUsers"
|
||||
:key="ccUser.id"
|
||||
class="cc-item"
|
||||
>
|
||||
@ -108,6 +181,17 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 更多按钮 -->
|
||||
<view
|
||||
v-if="ccUsers.length > 2"
|
||||
class="more-button"
|
||||
@click="toggleCcExpanded"
|
||||
>
|
||||
<text class="more-text">
|
||||
{{ ccExpanded ? '收起' : `更多(${ccUsers.length - 2})` }}
|
||||
</text>
|
||||
<text class="more-icon" :class="{ expanded: ccExpanded }">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作记录 -->
|
||||
@ -115,7 +199,7 @@
|
||||
<view class="section-title">操作记录</view>
|
||||
<view class="log-list">
|
||||
<view
|
||||
v-for="log in operationLogs"
|
||||
v-for="log in displayedOperationLogs"
|
||||
:key="log.id"
|
||||
class="log-item"
|
||||
>
|
||||
@ -136,12 +220,25 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 更多按钮 -->
|
||||
<view
|
||||
v-if="operationLogs.length > 2"
|
||||
class="more-button"
|
||||
@click="toggleLogExpanded"
|
||||
>
|
||||
<text class="more-text">
|
||||
{{ logExpanded ? '收起' : `更多(${operationLogs.length - 2})` }}
|
||||
</text>
|
||||
<text class="more-icon" :class="{ expanded: logExpanded }">▼</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
<!-- 底部固定按钮 -->
|
||||
<view class="bottom-actions">
|
||||
<view class="bottom-actions" v-if="canCurrentUserOperate">
|
||||
<!-- 驳回按钮暂时隐藏 -->
|
||||
<!-- <u-button
|
||||
text="驳回"
|
||||
@ -194,13 +291,23 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { ref, onMounted, computed } from "vue";
|
||||
import BasicLayout from "@/components/BasicLayout/Layout.vue";
|
||||
import { navigateTo } from "@/utils/uniapp";
|
||||
|
||||
import { getGwFlowByIdApi, gwApproveApi } from "@/api/routine/gw";
|
||||
import dayjs from "dayjs";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { imagUrl } from "@/utils";
|
||||
import {
|
||||
isVideo,
|
||||
isImage,
|
||||
canPreview,
|
||||
previewFile as previewFileUtil,
|
||||
previewVideo as previewVideoUtil,
|
||||
previewImage as previewImageUtil,
|
||||
downloadFile as downloadFileUtil
|
||||
} from "@/utils/filePreview";
|
||||
|
||||
// 类型定义
|
||||
interface GwInfo {
|
||||
@ -211,7 +318,7 @@ interface GwInfo {
|
||||
status: string;
|
||||
createdBy: string;
|
||||
createdTime: Date;
|
||||
files: FileInfo[];
|
||||
files?: FileInfo[]; // 改为可选,因为实际数据中可能没有这个字段
|
||||
approvers?: Approver[];
|
||||
ccUsers?: CCUser[];
|
||||
operationLogs?: OperationLog[];
|
||||
@ -219,12 +326,19 @@ interface GwInfo {
|
||||
urgencyLevel: string; // 新增
|
||||
tjrId: string; // 新增
|
||||
spRule?: string; // 新增审批规则字段
|
||||
// 附件相关字段(根据实际数据结构)
|
||||
fileUrl?: string; // 文件URL
|
||||
fileName?: string; // 文件名
|
||||
fileFormat?: string; // 文件格式
|
||||
}
|
||||
|
||||
interface FileInfo {
|
||||
name: string;
|
||||
size: number;
|
||||
url: string;
|
||||
resourName?: string; // 资源名称
|
||||
resourUrl?: string; // 资源URL
|
||||
resSuf?: string; // 文件后缀
|
||||
}
|
||||
|
||||
interface Approver {
|
||||
@ -277,6 +391,62 @@ const ccUsers = ref<CCUser[]>([]);
|
||||
const operationLogs = ref<OperationLog[]>([]);
|
||||
const currentLog = ref<OperationLog>({} as OperationLog);
|
||||
|
||||
// 抄送人展开状态
|
||||
const ccExpanded = ref(false);
|
||||
|
||||
// 操作记录展开状态
|
||||
const logExpanded = ref(false);
|
||||
|
||||
// 计算属性:是否有附件
|
||||
const hasAttachments = computed(() => {
|
||||
return gwInfo.value.fileUrl || (gwInfo.value.files && gwInfo.value.files.length > 0);
|
||||
});
|
||||
|
||||
// 计算属性:显示的抄送人列表
|
||||
const displayedCcUsers = computed(() => {
|
||||
if (ccExpanded.value || ccUsers.value.length <= 2) {
|
||||
return ccUsers.value;
|
||||
}
|
||||
return ccUsers.value.slice(0, 2);
|
||||
});
|
||||
|
||||
// 计算属性:显示的操作记录列表
|
||||
const displayedOperationLogs = computed(() => {
|
||||
if (logExpanded.value || operationLogs.value.length <= 2) {
|
||||
return operationLogs.value;
|
||||
}
|
||||
return operationLogs.value.slice(0, 2);
|
||||
});
|
||||
|
||||
// 计算属性:当前用户是否可以操作(转办/同意)
|
||||
const canCurrentUserOperate = computed(() => {
|
||||
const userStore = useUserStore();
|
||||
const getUser = userStore.getUser;
|
||||
const getJs = userStore.getJs;
|
||||
const currentUserId = getJs?.id;
|
||||
|
||||
if (!currentUserId || !approvers.value || approvers.value.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 查找当前用户对应的审批人记录
|
||||
const currentUserApprover = approvers.value.find(approver => {
|
||||
return approver.userId === currentUserId || approver.id === currentUserId;
|
||||
});
|
||||
|
||||
if (!currentUserApprover) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果审批状态是approved或rejected,则不能操作
|
||||
const approveStatus = currentUserApprover.approveStatus;
|
||||
if (approveStatus === 'approved' || approveStatus === 'rejected') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
// 获取公文信息
|
||||
const getGwInfo = async () => {
|
||||
@ -358,6 +528,16 @@ const showLogDetail = (log: OperationLog) => {
|
||||
showLogDetailModal.value = true;
|
||||
};
|
||||
|
||||
// 切换抄送人展开状态
|
||||
const toggleCcExpanded = () => {
|
||||
ccExpanded.value = !ccExpanded.value;
|
||||
};
|
||||
|
||||
// 切换操作记录展开状态
|
||||
const toggleLogExpanded = () => {
|
||||
logExpanded.value = !logExpanded.value;
|
||||
};
|
||||
|
||||
// 驳回处理
|
||||
const handleReject = () => {
|
||||
uni.showModal({
|
||||
@ -494,10 +674,20 @@ const approveGw = async () => {
|
||||
uni.showToast({
|
||||
title: "同意成功",
|
||||
icon: "success",
|
||||
duration: 1500
|
||||
});
|
||||
|
||||
// 刷新页面数据
|
||||
await getGwInfo();
|
||||
// 延迟返回上一级并刷新
|
||||
setTimeout(() => {
|
||||
// 返回上一级页面
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
success: () => {
|
||||
// 通过事件总线通知上一级页面刷新
|
||||
uni.$emit('refreshGwList');
|
||||
}
|
||||
});
|
||||
}, 1500);
|
||||
} else {
|
||||
throw new Error(response.message || '同意失败');
|
||||
}
|
||||
@ -514,6 +704,23 @@ const approveGw = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 获取当前用户的审批状态
|
||||
const getCurrentUserApproverStatus = () => {
|
||||
const userStore = useUserStore();
|
||||
const getJs = userStore.getJs;
|
||||
const currentUserId = getJs?.id;
|
||||
|
||||
if (!currentUserId || !approvers.value || approvers.value.length === 0) {
|
||||
return '未知';
|
||||
}
|
||||
|
||||
const currentUserApprover = approvers.value.find(approver => {
|
||||
return approver.userId === currentUserId || approver.id === currentUserId;
|
||||
});
|
||||
|
||||
return currentUserApprover?.approveStatus || '未知';
|
||||
};
|
||||
|
||||
// 获取当前用户在审批人列表中的ID
|
||||
const getCurrentUserApproverId = (currentUserId: string) => {
|
||||
console.log('=== getCurrentUserApproverId 函数调试 ===');
|
||||
@ -560,16 +767,221 @@ const getCurrentUserApproverId = (currentUserId: string) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
// 预览单个附件(从gwInfo直接获取)
|
||||
const previewSingleFile = () => {
|
||||
console.log("=== 预览单个附件 ===");
|
||||
console.log("附件信息:", {
|
||||
fileUrl: gwInfo.value.fileUrl,
|
||||
fileName: gwInfo.value.fileName,
|
||||
fileFormat: gwInfo.value.fileFormat
|
||||
});
|
||||
|
||||
if (!gwInfo.value.fileUrl) {
|
||||
console.error("没有找到附件URL");
|
||||
return;
|
||||
}
|
||||
|
||||
const fileUrl = imagUrl(gwInfo.value.fileUrl);
|
||||
const fileName = gwInfo.value.fileName || '未知文件';
|
||||
const fileFormat = gwInfo.value.fileFormat || '';
|
||||
|
||||
console.log("处理后的文件URL:", fileUrl);
|
||||
console.log("文件名:", fileName);
|
||||
console.log("文件格式:", fileFormat);
|
||||
|
||||
// 根据文件类型选择预览方式
|
||||
if (isVideo(fileFormat)) {
|
||||
handlePreviewVideoSingle(fileUrl, fileName);
|
||||
} else if (isImage(fileFormat)) {
|
||||
handlePreviewImageSingle(fileUrl);
|
||||
} else if (canPreview(fileFormat)) {
|
||||
handlePreviewDocumentSingle(fileUrl, fileName, fileFormat);
|
||||
} else {
|
||||
// 不支持预览的文件类型,直接下载
|
||||
downloadSingleFile();
|
||||
}
|
||||
};
|
||||
|
||||
// 下载单个附件
|
||||
const downloadSingleFile = () => {
|
||||
if (!gwInfo.value.fileUrl) {
|
||||
console.error("没有找到附件URL");
|
||||
return;
|
||||
}
|
||||
|
||||
const fileUrl = imagUrl(gwInfo.value.fileUrl);
|
||||
const fileName = gwInfo.value.fileName || '未知文件';
|
||||
const fileFormat = gwInfo.value.fileFormat || '';
|
||||
const fullFileName = fileFormat ? `${fileName}.${fileFormat}` : fileName;
|
||||
|
||||
console.log("下载单个附件:", { fileUrl, fullFileName });
|
||||
|
||||
downloadFileUtil(fileUrl, fullFileName)
|
||||
.then(() => {
|
||||
console.log('文件下载成功');
|
||||
uni.showToast({
|
||||
title: '下载成功',
|
||||
icon: 'success'
|
||||
});
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('文件下载失败:', error);
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 文件预览
|
||||
const previewFile = (file: FileInfo) => {
|
||||
// 实现文件预览逻辑
|
||||
console.log("预览文件:", file);
|
||||
console.log("=== 处理文件预览 ===");
|
||||
console.log("文件信息:", file);
|
||||
|
||||
// 确定文件URL和名称
|
||||
const fileUrl = file.resourUrl ? imagUrl(file.resourUrl) : file.url;
|
||||
const fileName = file.resourName ? `${file.resourName}.${file.resSuf}` : file.name;
|
||||
const fileSuf = file.resSuf || file.name.split('.').pop() || '';
|
||||
|
||||
console.log("处理后的文件URL:", fileUrl);
|
||||
console.log("文件名:", fileName);
|
||||
console.log("文件后缀:", fileSuf);
|
||||
|
||||
// 根据文件类型选择预览方式
|
||||
if (isVideo(fileSuf)) {
|
||||
handlePreviewVideo(file);
|
||||
} else if (isImage(fileSuf)) {
|
||||
handlePreviewImage(file);
|
||||
} else if (canPreview(fileSuf)) {
|
||||
handlePreviewDocument(file);
|
||||
} else {
|
||||
// 不支持预览的文件类型,直接下载
|
||||
downloadFileAction(file);
|
||||
}
|
||||
};
|
||||
|
||||
// 文档预览
|
||||
const handlePreviewDocument = (file: FileInfo) => {
|
||||
const fileUrl = file.resourUrl ? imagUrl(file.resourUrl) : file.url;
|
||||
const fileName = file.resourName ? `${file.resourName}.${file.resSuf}` : file.name;
|
||||
const fileSuf = file.resSuf || file.name.split('.').pop() || '';
|
||||
|
||||
previewFileUtil(fileUrl, fileName, fileSuf)
|
||||
.then(() => {
|
||||
console.log('文档预览成功');
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('文档预览失败:', error);
|
||||
// 预览失败时尝试下载
|
||||
downloadFileAction(file);
|
||||
});
|
||||
};
|
||||
|
||||
// 单个附件文档预览
|
||||
const handlePreviewDocumentSingle = (fileUrl: string, fileName: string, fileFormat: string) => {
|
||||
const fullFileName = fileFormat ? `${fileName}.${fileFormat}` : fileName;
|
||||
|
||||
previewFileUtil(fileUrl, fullFileName, fileFormat)
|
||||
.then(() => {
|
||||
console.log('单个附件文档预览成功');
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('单个附件文档预览失败:', error);
|
||||
// 预览失败时尝试下载
|
||||
downloadSingleFile();
|
||||
});
|
||||
};
|
||||
|
||||
// 视频预览
|
||||
const handlePreviewVideo = (file: FileInfo) => {
|
||||
console.log('=== 处理视频预览 ===');
|
||||
console.log('视频文件:', file);
|
||||
|
||||
const videoUrl = file.resourUrl ? imagUrl(file.resourUrl) : file.url;
|
||||
const videoName = file.resourName || file.name;
|
||||
|
||||
console.log('处理后的视频URL:', videoUrl);
|
||||
|
||||
previewVideoUtil(videoUrl, videoName)
|
||||
.then(() => {
|
||||
console.log('视频预览成功');
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('视频预览失败:', error);
|
||||
});
|
||||
};
|
||||
|
||||
// 单个附件视频预览
|
||||
const handlePreviewVideoSingle = (videoUrl: string, videoName: string) => {
|
||||
console.log('=== 处理单个附件视频预览 ===');
|
||||
console.log('视频URL:', videoUrl);
|
||||
console.log('视频名称:', videoName);
|
||||
|
||||
previewVideoUtil(videoUrl, videoName)
|
||||
.then(() => {
|
||||
console.log('单个附件视频预览成功');
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('单个附件视频预览失败:', error);
|
||||
});
|
||||
};
|
||||
|
||||
// 图片预览
|
||||
const handlePreviewImage = (file: FileInfo) => {
|
||||
const imageUrl = file.resourUrl ? imagUrl(file.resourUrl) : file.url;
|
||||
|
||||
previewImageUtil(imageUrl)
|
||||
.then(() => {
|
||||
console.log('图片预览成功');
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('图片预览失败:', error);
|
||||
});
|
||||
};
|
||||
|
||||
// 单个附件图片预览
|
||||
const handlePreviewImageSingle = (imageUrl: string) => {
|
||||
console.log('=== 处理单个附件图片预览 ===');
|
||||
console.log('图片URL:', imageUrl);
|
||||
|
||||
previewImageUtil(imageUrl)
|
||||
.then(() => {
|
||||
console.log('单个附件图片预览成功');
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('单个附件图片预览失败:', error);
|
||||
});
|
||||
};
|
||||
|
||||
// 文件下载
|
||||
const downloadFile = (file: FileInfo) => {
|
||||
// 实现文件下载逻辑
|
||||
downloadFileAction(file);
|
||||
};
|
||||
|
||||
// 文件下载实现
|
||||
const downloadFileAction = (file: FileInfo) => {
|
||||
const fileUrl = file.resourUrl ? imagUrl(file.resourUrl) : file.url;
|
||||
const fileName = file.resourName ? `${file.resourName}.${file.resSuf}` : file.name;
|
||||
|
||||
console.log("下载文件:", file);
|
||||
console.log("下载URL:", fileUrl);
|
||||
console.log("文件名:", fileName);
|
||||
|
||||
downloadFileUtil(fileUrl, fileName)
|
||||
.then(() => {
|
||||
console.log('文件下载成功');
|
||||
uni.showToast({
|
||||
title: '下载成功',
|
||||
icon: 'success'
|
||||
});
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('文件下载失败:', error);
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 格式化时间
|
||||
@ -579,11 +991,42 @@ const formatTime = (time: any) => {
|
||||
|
||||
// 格式化文件大小
|
||||
const formatFileSize = (size: any) => {
|
||||
if (!size) return "0B";
|
||||
if (size < 1024) return size + "B";
|
||||
if (size < 1024 * 1024) return (size / 1024).toFixed(2) + "KB";
|
||||
return (size / (1024 * 1024)).toFixed(2) + "MB";
|
||||
};
|
||||
|
||||
// 获取文件名
|
||||
const getFileName = (file: FileInfo) => {
|
||||
if (file.resourName) {
|
||||
return file.resourName;
|
||||
}
|
||||
if (file.name) {
|
||||
// 如果name包含扩展名,去掉扩展名
|
||||
const lastDotIndex = file.name.lastIndexOf('.');
|
||||
if (lastDotIndex > 0) {
|
||||
return file.name.substring(0, lastDotIndex);
|
||||
}
|
||||
return file.name;
|
||||
}
|
||||
return '未知文件';
|
||||
};
|
||||
|
||||
// 获取文件后缀
|
||||
const getFileSuffix = (file: FileInfo) => {
|
||||
if (file.resSuf) {
|
||||
return file.resSuf;
|
||||
}
|
||||
if (file.name) {
|
||||
const lastDotIndex = file.name.lastIndexOf('.');
|
||||
if (lastDotIndex > 0) {
|
||||
return file.name.substring(lastDotIndex + 1);
|
||||
}
|
||||
}
|
||||
return 'unknown';
|
||||
};
|
||||
|
||||
// 获取状态样式类
|
||||
const getStatusClass = (status: any) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
@ -830,6 +1273,10 @@ onMounted(() => {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.log-section {
|
||||
margin-bottom: 40px; // 操作记录区域增加底部间距
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
@ -885,14 +1332,23 @@ onMounted(() => {
|
||||
.file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
padding: 12px;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
background: #fafafa;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: translateY(1px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
margin-right: 10px;
|
||||
font-size: 20px;
|
||||
margin-right: 12px;
|
||||
font-size: 24px;
|
||||
width: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.file-info {
|
||||
@ -901,21 +1357,49 @@ onMounted(() => {
|
||||
.file-name {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
margin-bottom: 2px;
|
||||
margin-bottom: 4px;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.file-size {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.file-type {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
background: #f0f0f0;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.file-actions {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
gap: 4px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
|
||||
.u-button {
|
||||
min-width: 20px;
|
||||
height: 26px;
|
||||
font-size: 12px;
|
||||
border-radius: 13px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-files {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.approver-item,
|
||||
.cc-item {
|
||||
display: flex;
|
||||
@ -995,6 +1479,41 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.more-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px 16px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px; // 增加底部间距,避免被底部按钮挡住
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
background: #e9ecef;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.more-text {
|
||||
font-size: 14px;
|
||||
color: #007aff;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.more-icon {
|
||||
font-size: 12px;
|
||||
color: #007aff;
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
&.expanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.log-item {
|
||||
padding: 10px;
|
||||
border: 1px solid #eee;
|
||||
|
||||
@ -5,8 +5,15 @@
|
||||
</view>
|
||||
<template #bottom>
|
||||
<view class="flex-row items-center pb-10 pt-5">
|
||||
<u-button text="取消" class="mx-15" @click="handleCancel" />
|
||||
<u-button text="确认转办" class="mx-15" type="primary" @click="handleTransfer" />
|
||||
<u-button text="取消" class="mx-15" :disabled="isTransferring" @click="handleCancel" />
|
||||
<u-button
|
||||
:text="isTransferring ? '转办中...' : '确认转办'"
|
||||
class="mx-15"
|
||||
type="primary"
|
||||
:disabled="isTransferring"
|
||||
:loading="isTransferring"
|
||||
@click="handleTransfer"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
</BasicLayout>
|
||||
@ -32,6 +39,8 @@ const xxtsInfo = ref<any>(null);
|
||||
const gwInfo = ref<any>(null);
|
||||
const approvers = ref<any[]>([]);
|
||||
const ccUsers = ref<any[]>([]);
|
||||
// 转办状态控制
|
||||
const isTransferring = ref(false);
|
||||
|
||||
// 表单配置
|
||||
const [register, { getValue, setValue }] = useForm({
|
||||
@ -69,6 +78,26 @@ const [register, { getValue, setValue }] = useForm({
|
||||
disabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "isCc",
|
||||
label: "是否抄送",
|
||||
component: "BasicPicker",
|
||||
defaultValue: "1",
|
||||
required: true,
|
||||
componentProps: {
|
||||
placeholder: "请选择是否抄送",
|
||||
range: [
|
||||
{ label: "是", value: "1" },
|
||||
{ label: "否", value: "0" }
|
||||
],
|
||||
rangeKey: "label",
|
||||
savaKey: "value",
|
||||
onChange: async (value: any) => {
|
||||
// 手动更新表单值,确保数据回显
|
||||
setValue({ isCc: value });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "transferTo",
|
||||
label: "转办人",
|
||||
@ -107,9 +136,9 @@ const [register, { getValue, setValue }] = useForm({
|
||||
field: "transferReason",
|
||||
label: "转办描述",
|
||||
component: "BasicInput",
|
||||
required: true,
|
||||
required: false,
|
||||
componentProps: {
|
||||
placeholder: "请输入转办原因和描述",
|
||||
placeholder: "请输入转办原因和描述(可选)",
|
||||
type: "textarea",
|
||||
rows: 5,
|
||||
},
|
||||
@ -226,6 +255,11 @@ const handleCancel = () => {
|
||||
|
||||
// 确认转办
|
||||
const handleTransfer = async () => {
|
||||
// 防止重复点击
|
||||
if (isTransferring.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const value = await getValue();
|
||||
|
||||
@ -234,6 +268,9 @@ const handleTransfer = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 设置转办状态,防止重复点击
|
||||
isTransferring.value = true;
|
||||
|
||||
// 显示加载提示
|
||||
uni.showLoading({
|
||||
title: '正在转办...',
|
||||
@ -260,7 +297,8 @@ const handleTransfer = async () => {
|
||||
|
||||
// 转办相关数据
|
||||
transferTo: value.transferTo,
|
||||
ccTo: value.ccTo || [], // 添加抄送人数据
|
||||
isCc: value.isCc, // 是否抄送标识
|
||||
ccTo: value.isCc === "1" ? (value.ccTo || []) : [], // 根据是否抄送决定是否传递抄送人数据
|
||||
spRule: gwInfo.value?.spRule, // 从gwInfo中获取spRule字段
|
||||
transferReason: value.transferReason,
|
||||
transferTime: new Date().toISOString(),
|
||||
@ -268,6 +306,7 @@ const handleTransfer = async () => {
|
||||
};
|
||||
|
||||
console.log('转办数据:', transferData);
|
||||
console.log('是否抄送:', value.isCc);
|
||||
console.log('转办人数据类型:', typeof value.transferTo);
|
||||
console.log('转办人数据内容:', JSON.stringify(value.transferTo));
|
||||
console.log('抄送人数据类型:', typeof value.ccTo);
|
||||
@ -305,11 +344,23 @@ const handleTransfer = async () => {
|
||||
title: "转办失败",
|
||||
icon: "error",
|
||||
});
|
||||
} finally {
|
||||
// 无论成功还是失败,都要重置转办状态
|
||||
isTransferring.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 表单验证
|
||||
const validateForm = (value: any) => {
|
||||
// 验证是否抄送
|
||||
if (!value.isCc) {
|
||||
uni.showToast({
|
||||
title: "请选择是否抄送",
|
||||
icon: "error",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证转办人
|
||||
if (!value.transferTo || value.transferTo.length === 0) {
|
||||
uni.showToast({
|
||||
@ -319,14 +370,6 @@ const validateForm = (value: any) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证转办描述
|
||||
if (!value.transferReason || value.transferReason.trim() === "") {
|
||||
uni.showToast({
|
||||
title: "请输入转办描述",
|
||||
icon: "error",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证必要的数据是否存在
|
||||
if (!xxtsInfo.value?.id) {
|
||||
@ -353,8 +396,6 @@ const validateForm = (value: any) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 抄送人是可选的,不需要验证
|
||||
|
||||
return true;
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-item">
|
||||
<BasicSearch
|
||||
placeholder="搜索公文标题或编号"
|
||||
placeholder="搜索公文标题、编号或类型"
|
||||
@search="handleSearch"
|
||||
class="search-input"
|
||||
/>
|
||||
@ -46,24 +46,29 @@
|
||||
<view class="info-item">
|
||||
<text class="info-label">类型:</text>
|
||||
<text class="info-value">{{ data.docType }}</text>
|
||||
<text class="info-label" style="margin-left: 20px;">提交时间:</text>
|
||||
<text class="info-value">{{ formatDate(data.tjrtime) }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">紧急程度:</text>
|
||||
<text class="info-value urgency-tag" :class="getUrgencyClass(data.urgencyLevel)">
|
||||
{{ getUrgencyText(data.urgencyLevel) }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">审批进度:</text>
|
||||
<text class="info-value">{{ getApproverProgress(data) }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">提交人:</text>
|
||||
<text class="info-value">{{ data.tjrxm || '未知' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">提交时间:</text>
|
||||
<text class="info-value">{{ formatTime(data.tjrtime || data.createdTime) }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 附件列表 -->
|
||||
<view class="attachments-section" v-if="hasAttachments(data)">
|
||||
<view class="attachments-list">
|
||||
<!-- 统一处理所有附件 -->
|
||||
<view
|
||||
v-for="(file, index) in parseFileList(data)"
|
||||
:key="index"
|
||||
class="attachment-item"
|
||||
@click="previewAttachmentFile(file)"
|
||||
>
|
||||
<view class="attachment-icon">
|
||||
<text v-if="isImage(getFileSuffix(file))">🖼️</text>
|
||||
<text v-else-if="isVideo(getFileSuffix(file))">🎥</text>
|
||||
<text v-else-if="canPreview(getFileSuffix(file))">📄</text>
|
||||
<text v-else>📎</text>
|
||||
</view>
|
||||
<text class="attachment-name">{{ getFileName(file) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@ -71,31 +76,19 @@
|
||||
<view class="card-footer">
|
||||
<view class="footer-actions">
|
||||
<u-button
|
||||
text="详情"
|
||||
:text="getButtonText(data)"
|
||||
size="mini"
|
||||
type="primary"
|
||||
:class="getButtonClass(data)"
|
||||
@click="goToDetail(data)"
|
||||
/>
|
||||
<u-button
|
||||
v-if="data.gwStatus === 'B'"
|
||||
text="编辑"
|
||||
size="mini"
|
||||
@click="editGw(data)"
|
||||
/>
|
||||
<u-button
|
||||
v-if="data.gwStatus === 'B'"
|
||||
text="删除"
|
||||
size="mini"
|
||||
type="error"
|
||||
@click="deleteGw(data)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 新建公文按钮放在bottom插槽中 -->
|
||||
<template #bottom>
|
||||
<!-- 新建公文按钮已隐藏 -->
|
||||
<!-- <template #bottom>
|
||||
<view class="flex-row items-center pb-10 pt-5">
|
||||
<u-button
|
||||
text="新建公文"
|
||||
@ -104,35 +97,73 @@
|
||||
@click="createNewGw"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
</template> -->
|
||||
</BasicListLayout>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted } from "vue";
|
||||
import { ref, computed, watch, onMounted, onUnmounted } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import { navigateTo } from "@/utils/uniapp";
|
||||
import BasicSearch from "@/components/BasicSearch/Search.vue";
|
||||
import BasicLayout from "@/components/BasicLayout/Layout.vue";
|
||||
import BasicListLayout from "@/components/BasicListLayout/ListLayout.vue";
|
||||
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
|
||||
import { gwFindPageApi, gwLogicDeleteApi } from "@/api/routine/gw";
|
||||
import { GwStatus, UrgencyLevel, ApproverStatus } from "@/types/gw";
|
||||
import type { GwInfo, GwListItem } from "@/types/gw";
|
||||
import { gwFindPageApi } from "@/api/routine/gw";
|
||||
import dayjs from "dayjs";
|
||||
import { imagUrl } from "@/utils";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import {
|
||||
isVideo,
|
||||
isImage,
|
||||
canPreview,
|
||||
previewFile as previewFileUtil,
|
||||
previewVideo as previewVideoUtil,
|
||||
previewImage as previewImageUtil,
|
||||
downloadFile as downloadFileUtil
|
||||
} from "@/utils/filePreview";
|
||||
|
||||
// 附件类型定义
|
||||
interface FileInfo {
|
||||
name: string;
|
||||
size: number;
|
||||
url: string;
|
||||
resourName?: string; // 资源名称
|
||||
resourUrl?: string; // 资源URL
|
||||
resSuf?: string; // 文件后缀
|
||||
resourSuf?: string; // 资源文件后缀
|
||||
}
|
||||
|
||||
// 公文列表项类型定义
|
||||
interface GwListItem {
|
||||
id: string;
|
||||
title: string;
|
||||
docType: string;
|
||||
gwStatus: string;
|
||||
fileUrl?: string;
|
||||
fileName?: string;
|
||||
fileFormat?: string;
|
||||
files?: FileInfo[];
|
||||
spZbqd?: string; // 审批人ID(逗号分隔,从审批人表查询)
|
||||
tjrtime?: string; // 提交时间
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// 筛选标签
|
||||
const filterTabs = [
|
||||
{ key: "all", label: "全部" },
|
||||
{ key: "A", label: "已提交" },
|
||||
{ key: "B", label: "草稿" },
|
||||
{ key: "C", label: "审批中" },
|
||||
{ key: "D", label: "已完结" },
|
||||
];
|
||||
|
||||
const activeTab = ref("all");
|
||||
const searchKeyword = ref("");
|
||||
|
||||
// 获取用户store
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 使用 BasicListLayout
|
||||
const [register, { reload, setParam }] = useLayout({
|
||||
api: gwFindPageApi,
|
||||
@ -141,6 +172,7 @@ const [register, { reload, setParam }] = useLayout({
|
||||
},
|
||||
param: {
|
||||
title: "",
|
||||
docType: "",
|
||||
gwStatus: "",
|
||||
},
|
||||
});
|
||||
@ -148,21 +180,28 @@ const [register, { reload, setParam }] = useLayout({
|
||||
// 数据列表
|
||||
const dataList = ref<GwListItem[]>([]);
|
||||
|
||||
// 获取当前教师ID
|
||||
const getCurrentTeacherId = () => {
|
||||
const jsData = userStore.getJs;
|
||||
return jsData?.id || null;
|
||||
};
|
||||
|
||||
// 筛选后的公文列表
|
||||
const filteredGwList = computed(() => {
|
||||
let list = dataList.value;
|
||||
|
||||
// 按状态筛选
|
||||
if (activeTab.value !== "all") {
|
||||
list = list.filter(item => item.gwStatus === activeTab.value);
|
||||
list = list.filter((item: GwListItem) => item.gwStatus === activeTab.value);
|
||||
}
|
||||
|
||||
// 按关键词搜索
|
||||
if (searchKeyword.value) {
|
||||
const keyword = searchKeyword.value.toLowerCase();
|
||||
list = list.filter(item =>
|
||||
list = list.filter((item: GwListItem) =>
|
||||
item.title.toLowerCase().includes(keyword) ||
|
||||
(item.gwNo && item.gwNo.toLowerCase().includes(keyword))
|
||||
(item.gwNo && item.gwNo.toLowerCase().includes(keyword)) ||
|
||||
(item.docType && item.docType.toLowerCase().includes(keyword))
|
||||
);
|
||||
}
|
||||
|
||||
@ -181,61 +220,20 @@ const switchTab = (tabKey: string) => {
|
||||
// 搜索处理
|
||||
const handleSearch = (keyword: string) => {
|
||||
searchKeyword.value = keyword;
|
||||
// 更新查询参数并重新加载
|
||||
setParam({ title: keyword });
|
||||
// 更新查询参数并重新加载,支持按标题和类型搜索
|
||||
setParam({
|
||||
title: keyword,
|
||||
docType: keyword // 同时按类型搜索
|
||||
});
|
||||
reload();
|
||||
};
|
||||
|
||||
// 跳转到详情页面
|
||||
const goToDetail = (item: GwListItem) => {
|
||||
navigateTo(`/pages/view/routine/gwlz/gwDetail?id=${item.id}`);
|
||||
navigateTo(`/pages/view/routine/gwlz/gwFlow?id=${item.id}`);
|
||||
};
|
||||
|
||||
// 编辑公文
|
||||
const editGw = (item: GwListItem) => {
|
||||
console.log('编辑公文,ID:', item.id, '标题:', item.title);
|
||||
const url = `/pages/view/routine/gwlz/gwAdd?id=${item.id}&mode=edit`;
|
||||
|
||||
// 先存储编辑参数到本地存储,确保页面跳转后能获取到
|
||||
uni.setStorageSync('gwEditMode', 'edit');
|
||||
uni.setStorageSync('gwEditId', item.id);
|
||||
|
||||
console.log('存储编辑参数到本地存储:', { mode: 'edit', id: item.id });
|
||||
console.log('跳转到编辑页面:', url);
|
||||
|
||||
navigateTo(url);
|
||||
};
|
||||
|
||||
// 删除公文
|
||||
const deleteGw = (item: GwListItem) => {
|
||||
uni.showModal({
|
||||
title: "确认删除",
|
||||
content: `确定要删除公文"${item.title}"吗?`,
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
// 调用软删除API - 需要传递 {ids: item.id} 格式的参数
|
||||
await gwLogicDeleteApi({ ids: item.id });
|
||||
|
||||
// 删除成功后刷新列表
|
||||
reload();
|
||||
|
||||
uni.showToast({
|
||||
title: "删除成功",
|
||||
icon: "success",
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("删除公文失败:", error);
|
||||
uni.showToast({
|
||||
title: "删除失败",
|
||||
icon: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
// 编辑和删除相关函数已删除
|
||||
|
||||
// 新建公文
|
||||
const createNewGw = () => {
|
||||
@ -253,8 +251,10 @@ const createNewGw = () => {
|
||||
// 获取状态样式类
|
||||
const getStatusClass = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'A': "status-submitted", // 已提交状态
|
||||
'B': "status-draft", // 草稿状态
|
||||
'A': "status-draft", // A暂存
|
||||
'B': "status-submitted", // B提交
|
||||
'C': "status-pending", // C审批中
|
||||
'D': "status-completed", // D已完结
|
||||
};
|
||||
return statusMap[status] || "status-default";
|
||||
};
|
||||
@ -262,87 +262,245 @@ const getStatusClass = (status: string) => {
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'A': "已提交", // 已提交状态
|
||||
'B': "草稿", // 草稿状态
|
||||
'A': "暂存", // A暂存
|
||||
'B': "提交", // B提交
|
||||
'C': "审批中", // C审批中
|
||||
'D': "已完结", // D已完结
|
||||
};
|
||||
return statusMap[status] || "未知";
|
||||
};
|
||||
|
||||
// 获取紧急程度样式类
|
||||
const getUrgencyClass = (urgency: string) => {
|
||||
const urgencyMap: Record<string, string> = {
|
||||
'low': "urgency-low",
|
||||
'normal': "urgency-normal",
|
||||
'high': "urgency-high",
|
||||
'urgent': "urgency-urgent",
|
||||
};
|
||||
return urgencyMap[urgency] || "urgency-normal";
|
||||
};
|
||||
|
||||
// 获取紧急程度文本
|
||||
const getUrgencyText = (urgency: string) => {
|
||||
const urgencyMap: Record<string, string> = {
|
||||
'low': "普通",
|
||||
'normal': "一般",
|
||||
'high': "紧急",
|
||||
'urgent': "特急",
|
||||
};
|
||||
return urgencyMap[urgency] || "一般";
|
||||
};
|
||||
|
||||
// 获取审批进度
|
||||
const getApproverProgress = (item: GwListItem) => {
|
||||
let spCount = 0;
|
||||
let ccCount = 0;
|
||||
// 获取按钮文本
|
||||
const getButtonText = (item: GwListItem) => {
|
||||
const currentTeacherId = getCurrentTeacherId();
|
||||
const { gwStatus, spZbqd } = item;
|
||||
|
||||
// 统计审批人数量 - 处理逗号分隔的字符串或数组
|
||||
if (item.spId) {
|
||||
if (Array.isArray(item.spId)) {
|
||||
spCount = item.spId.length;
|
||||
} else {
|
||||
// 如果是逗号分隔的字符串,按逗号分割并统计
|
||||
const spIdStr = String(item.spId);
|
||||
if (spIdStr.trim()) {
|
||||
spCount = spIdStr.split(',').filter((id: string) => id.trim()).length;
|
||||
}
|
||||
// 如果当前教师ID在spZbqd中,且状态为B(提交),则显示"审批"
|
||||
if (currentTeacherId && spZbqd && gwStatus === 'B') {
|
||||
const approverIds = spZbqd.split(',').map(id => id.trim());
|
||||
if (approverIds.includes(currentTeacherId)) {
|
||||
return '审批';
|
||||
}
|
||||
}
|
||||
|
||||
// 统计抄送人数量 - 处理逗号分隔的字符串或数组
|
||||
if (item.ccId) {
|
||||
if (Array.isArray(item.ccId)) {
|
||||
ccCount = item.ccId.length;
|
||||
} else {
|
||||
// 如果是逗号分隔的字符串,按逗号分割并统计
|
||||
const ccIdStr = String(item.ccId);
|
||||
if (ccIdStr.trim()) {
|
||||
ccCount = ccIdStr.split(',').filter((id: string) => id.trim()).length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (spCount === 0 && ccCount === 0) {
|
||||
return "无审批人和抄送人";
|
||||
}
|
||||
|
||||
let result = "";
|
||||
if (spCount > 0) {
|
||||
result += `${spCount}个审批人`;
|
||||
}
|
||||
if (ccCount > 0) {
|
||||
if (result) result += ",";
|
||||
result += `${ccCount}个抄送人`;
|
||||
}
|
||||
|
||||
return result;
|
||||
// 其他情况显示"详情"
|
||||
return '详情';
|
||||
};
|
||||
|
||||
// 获取按钮样式类
|
||||
const getButtonClass = (item: GwListItem) => {
|
||||
const buttonText = getButtonText(item);
|
||||
return buttonText === '审批' ? 'action-button-approve' : 'action-button-detail';
|
||||
};
|
||||
|
||||
// 紧急程度相关函数已删除
|
||||
|
||||
// 审批进度相关函数已删除
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (time: string | Date | undefined) => {
|
||||
if (!time) return '暂无';
|
||||
return dayjs(time).format("MM-DD HH:mm");
|
||||
};
|
||||
|
||||
// 格式化日期(年月日)
|
||||
const formatDate = (time: string | Date | undefined) => {
|
||||
if (!time) return '暂无';
|
||||
return dayjs(time).format("YYYY-MM-DD");
|
||||
};
|
||||
|
||||
// 检查是否有附件
|
||||
const hasAttachments = (data: any) => {
|
||||
return data.fileUrl || (data.files && data.files.length > 0);
|
||||
};
|
||||
|
||||
// 解析逗号分隔的文件信息
|
||||
const parseFileList = (data: any) => {
|
||||
const fileList: FileInfo[] = [];
|
||||
|
||||
// 处理单个文件(从data直接获取)
|
||||
if (data.fileUrl && data.fileName) {
|
||||
const urls = data.fileUrl.split(',').map((url: string) => url.trim());
|
||||
const names = data.fileName.split(',').map((name: string) => name.trim());
|
||||
|
||||
urls.forEach((url: string, index: number) => {
|
||||
if (url) {
|
||||
const fileName = names[index] || `文件${index + 1}`;
|
||||
const fileSuffix = url.split('.').pop() || '';
|
||||
|
||||
fileList.push({
|
||||
name: fileName,
|
||||
url: url,
|
||||
resourName: fileName,
|
||||
resourUrl: url,
|
||||
resourSuf: fileSuffix,
|
||||
size: 0 // 无法从URL获取文件大小
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理files数组
|
||||
if (data.files && data.files.length > 0) {
|
||||
fileList.push(...data.files);
|
||||
}
|
||||
|
||||
return fileList;
|
||||
};
|
||||
|
||||
// 预览单个附件(从data直接获取)
|
||||
const previewAttachment = (data: any) => {
|
||||
if (!data.fileUrl) {
|
||||
console.error("没有找到附件URL");
|
||||
return;
|
||||
}
|
||||
|
||||
const fileUrl = imagUrl(data.fileUrl);
|
||||
const fileName = data.fileName || '未知文件';
|
||||
const fileFormat = data.fileFormat || '';
|
||||
|
||||
// 根据文件类型选择预览方式
|
||||
if (isVideo(fileFormat)) {
|
||||
previewVideoUtil(fileUrl, fileName)
|
||||
.then(() => console.log('视频预览成功'))
|
||||
.catch((error: any) => console.error('视频预览失败:', error));
|
||||
} else if (isImage(fileFormat)) {
|
||||
previewImageUtil(fileUrl)
|
||||
.then(() => console.log('图片预览成功'))
|
||||
.catch((error: any) => console.error('图片预览失败:', error));
|
||||
} else if (canPreview(fileFormat)) {
|
||||
const fullFileName = fileFormat ? `${fileName}.${fileFormat}` : fileName;
|
||||
previewFileUtil(fileUrl, fullFileName, fileFormat)
|
||||
.then(() => console.log('文档预览成功'))
|
||||
.catch((error: any) => console.error('文档预览失败:', error));
|
||||
} else {
|
||||
// 不支持预览的文件类型,直接下载
|
||||
downloadAttachment(data);
|
||||
}
|
||||
};
|
||||
|
||||
// 预览附件文件(从files数组获取)
|
||||
const previewAttachmentFile = (file: FileInfo) => {
|
||||
const fileUrl = file.resourUrl ? imagUrl(file.resourUrl) : (file.url ? imagUrl(file.url) : '');
|
||||
const fileName = file.resourName || file.name || '未知文件';
|
||||
const fileSuf = getFileSuffix(file);
|
||||
|
||||
if (!fileUrl) {
|
||||
console.error("没有找到文件URL");
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据文件类型选择预览方式
|
||||
if (isVideo(fileSuf)) {
|
||||
previewVideoUtil(fileUrl, fileName)
|
||||
.then(() => console.log('视频预览成功'))
|
||||
.catch((error: any) => console.error('视频预览失败:', error));
|
||||
} else if (isImage(fileSuf)) {
|
||||
previewImageUtil(fileUrl)
|
||||
.then(() => console.log('图片预览成功'))
|
||||
.catch((error: any) => console.error('图片预览失败:', error));
|
||||
} else if (canPreview(fileSuf)) {
|
||||
previewFileUtil(fileUrl, fileName, fileSuf)
|
||||
.then(() => console.log('文档预览成功'))
|
||||
.catch((error: any) => console.error('文档预览失败:', error));
|
||||
} else {
|
||||
// 不支持预览的文件类型,直接下载
|
||||
downloadAttachmentFile(file);
|
||||
}
|
||||
};
|
||||
|
||||
// 下载单个附件
|
||||
const downloadAttachment = (data: any) => {
|
||||
if (!data.fileUrl) {
|
||||
console.error("没有找到附件URL");
|
||||
return;
|
||||
}
|
||||
|
||||
const fileUrl = imagUrl(data.fileUrl);
|
||||
const fileName = data.fileName || '未知文件';
|
||||
const fileFormat = data.fileFormat || '';
|
||||
const fullFileName = fileFormat ? `${fileName}.${fileFormat}` : fileName;
|
||||
|
||||
downloadFileUtil(fileUrl, fullFileName)
|
||||
.then(() => {
|
||||
console.log('文件下载成功');
|
||||
uni.showToast({
|
||||
title: '下载成功',
|
||||
icon: 'success'
|
||||
});
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('文件下载失败:', error);
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 下载附件文件
|
||||
const downloadAttachmentFile = (file: FileInfo) => {
|
||||
const fileUrl = file.resourUrl ? imagUrl(file.resourUrl) : (file.url ? imagUrl(file.url) : '');
|
||||
const fileName = file.resourName || file.name || '未知文件';
|
||||
|
||||
if (!fileUrl) {
|
||||
console.error("没有找到文件URL");
|
||||
return;
|
||||
}
|
||||
|
||||
downloadFileUtil(fileUrl, fileName)
|
||||
.then(() => {
|
||||
console.log('文件下载成功');
|
||||
uni.showToast({
|
||||
title: '下载成功',
|
||||
icon: 'success'
|
||||
});
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.error('文件下载失败:', error);
|
||||
uni.showToast({
|
||||
title: '下载失败',
|
||||
icon: 'error'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 获取文件名
|
||||
const getFileName = (file: FileInfo) => {
|
||||
if (file.resourName) {
|
||||
return file.resourName;
|
||||
}
|
||||
if (file.name) {
|
||||
// 如果name包含扩展名,去掉扩展名
|
||||
const lastDotIndex = file.name.lastIndexOf('.');
|
||||
if (lastDotIndex > 0) {
|
||||
return file.name.substring(0, lastDotIndex);
|
||||
}
|
||||
return file.name;
|
||||
}
|
||||
return '未知文件';
|
||||
};
|
||||
|
||||
// 获取文件后缀
|
||||
const getFileSuffix = (file: FileInfo) => {
|
||||
if (file.resSuf) {
|
||||
return file.resSuf;
|
||||
}
|
||||
if (file.url) {
|
||||
const lastDotIndex = file.url.lastIndexOf('.');
|
||||
if (lastDotIndex > 0) {
|
||||
return file.url.substring(lastDotIndex + 1);
|
||||
}
|
||||
}
|
||||
if (file.name) {
|
||||
const lastDotIndex = file.name.lastIndexOf('.');
|
||||
if (lastDotIndex > 0) {
|
||||
return file.name.substring(lastDotIndex + 1);
|
||||
}
|
||||
}
|
||||
return 'unknown';
|
||||
};
|
||||
|
||||
// 监听数据变化
|
||||
watch(dataList, (val) => {
|
||||
// 数据变化监听
|
||||
@ -356,6 +514,17 @@ onShow(() => {
|
||||
// 页面加载时也加载一次数据
|
||||
onMounted(() => {
|
||||
reload();
|
||||
|
||||
// 监听来自gwFlow页面的刷新事件
|
||||
uni.$on('refreshGwList', () => {
|
||||
console.log('收到刷新事件,重新加载数据');
|
||||
reload();
|
||||
});
|
||||
});
|
||||
|
||||
// 页面销毁时移除事件监听
|
||||
onUnmounted(() => {
|
||||
uni.$off('refreshGwList');
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -459,6 +628,7 @@ onMounted(() => {
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-word;
|
||||
}
|
||||
@ -477,6 +647,14 @@ onMounted(() => {
|
||||
color: white;
|
||||
}
|
||||
&.status-submitted {
|
||||
background: linear-gradient(135deg, #2196f3 0%, #1976d2 100%);
|
||||
color: white;
|
||||
}
|
||||
&.status-pending {
|
||||
background: linear-gradient(135deg, #ff9800 0%, #f57c00 100%);
|
||||
color: white;
|
||||
}
|
||||
&.status-completed {
|
||||
background: linear-gradient(135deg, #66bb6a 0%, #4caf50 100%);
|
||||
color: white;
|
||||
}
|
||||
@ -506,10 +684,9 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.info-label {
|
||||
width: 80px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin-right: 8px;
|
||||
margin-right: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@ -519,28 +696,53 @@ onMounted(() => {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.urgency-tag {
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
// 紧急程度相关样式已删除
|
||||
|
||||
&.urgency-low {
|
||||
background: linear-gradient(135deg, #66bb6a 0%, #4caf50 100%);
|
||||
color: white;
|
||||
// 附件相关样式
|
||||
.attachments-section {
|
||||
margin-top: 12px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
// 附件标题样式已删除
|
||||
|
||||
.attachments-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.attachment-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px 10px;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
max-width: 200px;
|
||||
|
||||
&:active {
|
||||
transform: translateY(1px);
|
||||
background: #e9ecef;
|
||||
border-color: #007aff;
|
||||
}
|
||||
&.urgency-normal {
|
||||
background: linear-gradient(135deg, #90a4ae 0%, #78909c 100%);
|
||||
color: white;
|
||||
|
||||
.attachment-icon {
|
||||
margin-right: 6px;
|
||||
font-size: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
&.urgency-high {
|
||||
background: linear-gradient(135deg, #ffa726 0%, #ff9800 100%);
|
||||
color: white;
|
||||
}
|
||||
&.urgency-urgent {
|
||||
background: linear-gradient(135deg, #ef5350 0%, #e53935 100%);
|
||||
color: white;
|
||||
|
||||
.attachment-name {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -557,6 +759,32 @@ onMounted(() => {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
// 按钮样式,与状态标签大小相同
|
||||
.action-button-approve,
|
||||
.action-button-detail {
|
||||
padding: 4px 8px !important;
|
||||
border-radius: 12px !important;
|
||||
font-size: 12px !important;
|
||||
font-weight: 500 !important;
|
||||
white-space: nowrap !important;
|
||||
flex-shrink: 0 !important;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important;
|
||||
height: auto !important;
|
||||
min-height: 24px !important;
|
||||
}
|
||||
|
||||
.action-button-approve {
|
||||
background: linear-gradient(135deg, #ff9800 0%, #f57c00 100%) !important;
|
||||
color: white !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.action-button-detail {
|
||||
background: linear-gradient(135deg, #2196f3 0%, #1976d2 100%) !important;
|
||||
color: white !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
// 响应式优化
|
||||
@media (max-width: 375px) {
|
||||
.query-component {
|
||||
|
||||
@ -35,6 +35,10 @@
|
||||
<view class="info-label">开课地点:</view>
|
||||
<view class="info-data">{{ xkkc.kcdd }}</view>
|
||||
</view>
|
||||
<view class="course-info-item">
|
||||
<view class="info-label">开课年级:</view>
|
||||
<view class="info-data">{{ xkkc.njname || '暂无' }}</view>
|
||||
</view>
|
||||
<view class="course-info-item">
|
||||
<view class="info-label">上课人数:</view>
|
||||
<view class="info-data"
|
||||
|
||||
@ -34,6 +34,14 @@
|
||||
<view class="time-label">上课地点:</view>
|
||||
<view class="time-value">{{ xkkc.kcdd }}</view>
|
||||
</view>
|
||||
<view class="time-item">
|
||||
<view class="time-label">开课年级:</view>
|
||||
<view class="time-value">{{ xkkc.njname || '暂无' }}</view>
|
||||
</view>
|
||||
<view class="time-item">
|
||||
<view class="time-label">上课人数:</view>
|
||||
<view class="time-value">{{ xkkc.hasNum || 0 }} | {{ xkkc.maxNum || 0 }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 巡查时间状态 -->
|
||||
|
||||
@ -71,6 +71,42 @@
|
||||
</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">
|
||||
@ -184,7 +220,7 @@ const qdzxId = ref('');
|
||||
const qrExpireTime = ref(60); // 默认60秒
|
||||
|
||||
// 当前步骤
|
||||
const currentStep = ref<'sign' | 'confirm' | 'success' | 'notInList' | 'timeExpired' | 'qrExpired' | 'alreadySigned'>('confirm');
|
||||
const currentStep = ref<'sign' | 'confirm' | 'success' | 'notInList' | 'timeNotStarted' | 'timeExpired' | 'qrExpired' | 'alreadySigned'>('confirm');
|
||||
|
||||
// 用户信息
|
||||
const userInfo = ref<any>(null);
|
||||
@ -286,10 +322,19 @@ const loadMeetingInfo = async () => {
|
||||
if (result && result.resultCode === 1) {
|
||||
meetingInfo.value = result.result;
|
||||
|
||||
// 1. 验证签到时间是否已结束
|
||||
// 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';
|
||||
@ -418,18 +463,22 @@ const goBack = () => {
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
margin-bottom: 12px;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -542,18 +591,22 @@ const goBack = () => {
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
margin-bottom: 8px;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -568,6 +621,37 @@ const goBack = () => {
|
||||
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;
|
||||
@ -647,18 +731,22 @@ const goBack = () => {
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
margin-bottom: 8px;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,11 +17,15 @@
|
||||
<text class="info-value">{{ qdInfo.qdwz || '未设置' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">开始时间:</text>
|
||||
<text class="info-label">签到时间:</text>
|
||||
<text class="info-value">{{ formatTime(qdInfo.dkkstime) }}</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-label">会议结束:</text>
|
||||
<text class="info-value">{{ formatTime(qdInfo.qdjstime) }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
@ -49,6 +53,10 @@
|
||||
<text class="stat-number signed">{{ signedCount }}</text>
|
||||
<text class="stat-label">已签到</text>
|
||||
</view>
|
||||
<view class="stat-item" @click="showTeacherList('late')">
|
||||
<text class="stat-number late">{{ lateCount }}</text>
|
||||
<text class="stat-label">迟到</text>
|
||||
</view>
|
||||
<view class="stat-item" @click="showTeacherList('unsigned')">
|
||||
<text class="stat-number unsigned">{{ unsignedCount }}</text>
|
||||
<text class="stat-label">未签到</text>
|
||||
@ -141,6 +149,7 @@ interface QdInfo {
|
||||
jsId: string;
|
||||
jsxm: string;
|
||||
qdFbtime: string;
|
||||
dkkstime: string;
|
||||
qdkstime: string;
|
||||
qdjstime: string;
|
||||
qdry: string;
|
||||
@ -166,11 +175,28 @@ const currentFilter = ref('all');
|
||||
const totalCount = computed(() => teacherList.value.length);
|
||||
const signedCount = computed(() => teacherList.value.filter(t => t.qdStatus === '1').length);
|
||||
const unsignedCount = computed(() => teacherList.value.filter(t => t.qdStatus === '0').length);
|
||||
const lateCount = computed(() => {
|
||||
if (!qdInfo.value.qdkstime) return 0;
|
||||
const meetingStartTime = new Date(qdInfo.value.qdkstime);
|
||||
return teacherList.value.filter(t => {
|
||||
if (t.qdStatus !== '1' || !t.qdwctime) return false;
|
||||
const signInTime = new Date(t.qdwctime);
|
||||
return signInTime > meetingStartTime;
|
||||
}).length;
|
||||
});
|
||||
|
||||
const filteredTeacherList = computed(() => {
|
||||
switch (currentFilter.value) {
|
||||
case 'signed':
|
||||
return teacherList.value.filter(t => t.qdStatus === '1');
|
||||
case 'late':
|
||||
if (!qdInfo.value.qdkstime) return [];
|
||||
const meetingStartTime = new Date(qdInfo.value.qdkstime);
|
||||
return teacherList.value.filter(t => {
|
||||
if (t.qdStatus !== '1' || !t.qdwctime) return false;
|
||||
const signInTime = new Date(t.qdwctime);
|
||||
return signInTime > meetingStartTime;
|
||||
});
|
||||
case 'unsigned':
|
||||
return teacherList.value.filter(t => t.qdStatus === '0');
|
||||
default:
|
||||
@ -225,6 +251,9 @@ const showTeacherList = (filter: string) => {
|
||||
case 'signed':
|
||||
popupTitle.value = `已签到人员 (${signedCount.value}人)`;
|
||||
break;
|
||||
case 'late':
|
||||
popupTitle.value = `迟到人员 (${lateCount.value}人)`;
|
||||
break;
|
||||
case 'unsigned':
|
||||
popupTitle.value = `未签到人员 (${unsignedCount.value}人)`;
|
||||
break;
|
||||
@ -421,6 +450,10 @@ const handleBack = () => {
|
||||
&.unsigned {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
&.late {
|
||||
color: #ff9800;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
|
||||
@ -61,11 +61,15 @@
|
||||
<text class="info-value">{{ data.qdwz || '未设置' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">开始时间:</text>
|
||||
<text class="info-label">签到时间:</text>
|
||||
<text class="info-value">{{ formatTime(data.dkkstime) }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">会议开始:</text>
|
||||
<text class="info-value">{{ formatTime(data.qdkstime) }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">结束时间:</text>
|
||||
<text class="info-label">会议结束:</text>
|
||||
<text class="info-value">{{ formatTime(data.qdjstime) }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
@ -129,6 +133,7 @@ interface QdItem {
|
||||
jsId: string; // 发布人ID
|
||||
jsxm: string; // 发布人姓名
|
||||
qdFbtime: string; // 发布时间
|
||||
dkkstime: string; // 签到打卡开始时间
|
||||
qdkstime: string; // 签到开始时间
|
||||
qdjstime: string; // 签到结束时间
|
||||
qdry: string; // 签到人员
|
||||
@ -396,6 +401,7 @@ onMounted(() => {
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@ -5,14 +5,18 @@
|
||||
<!-- 签到名称 -->
|
||||
<view class="info-card">
|
||||
<view class="form-item">
|
||||
<view class="form-label">
|
||||
<text class="required-asterisk">*</text>
|
||||
<text class="label-text">签到名称</text>
|
||||
</view>
|
||||
<uni-easyinput
|
||||
type="textarea"
|
||||
autoHeight
|
||||
v-model="formData.qdmc"
|
||||
placeholder="请输入签到名称 (必填)"
|
||||
:inputBorder="false"
|
||||
placeholder-style="font-weight:bold; font-size: 18px; color: #999;"
|
||||
class="title-input"
|
||||
placeholder="请输入签到名称"
|
||||
:inputBorder="true"
|
||||
placeholder-style="font-size: 16px; color: #999;"
|
||||
class="input-field"
|
||||
></uni-easyinput>
|
||||
</view>
|
||||
</view>
|
||||
@ -20,13 +24,16 @@
|
||||
<!-- 签到地点 -->
|
||||
<view class="info-card">
|
||||
<view class="form-item">
|
||||
<view class="form-label">
|
||||
<text class="required-asterisk">*</text>
|
||||
<text class="label-text">签到地点</text>
|
||||
</view>
|
||||
<uni-easyinput
|
||||
type="textarea"
|
||||
autoHeight
|
||||
type="text"
|
||||
v-model="formData.qdwz"
|
||||
placeholder="请输入签到地点 (必填)"
|
||||
:inputBorder="false"
|
||||
class="content-input"
|
||||
placeholder="请输入签到地点"
|
||||
:inputBorder="true"
|
||||
class="input-field single-line"
|
||||
></uni-easyinput>
|
||||
</view>
|
||||
</view>
|
||||
@ -34,7 +41,10 @@
|
||||
<!-- 签到人员 -->
|
||||
<view class="info-card">
|
||||
<view class="card-header picker-header" @click="showTeacherTree">
|
||||
<text class="section-title">签到人员</text>
|
||||
<view class="section-title-container">
|
||||
<text class="required-asterisk">*</text>
|
||||
<text class="section-title">签到人员</text>
|
||||
</view>
|
||||
<view class="target-class">
|
||||
<text :class="{ placeholder: !formData.targetTeachers.length }">
|
||||
{{ formData.targetTeachers.length ? `已选择${formData.targetTeachers.length}人` : "请选择教师" }}
|
||||
@ -75,15 +85,36 @@
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<!-- 签到打卡开始时间 -->
|
||||
<view class="info-card list-item-card">
|
||||
<uni-datetime-picker type="datetime" v-model="formData.dkkstime">
|
||||
<view class="list-item-row">
|
||||
<view class="list-label-container">
|
||||
<text class="required-asterisk">*</text>
|
||||
<text class="list-label">签到开始</text>
|
||||
</view>
|
||||
<view class="list-value">
|
||||
<text :class="{ placeholder: !formData.dkkstime }">
|
||||
{{ formData.dkkstime || '请选择签到打卡开始时间' }}
|
||||
</text>
|
||||
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</uni-datetime-picker>
|
||||
</view>
|
||||
|
||||
<!-- 签到开始时间 -->
|
||||
<view class="info-card list-item-card">
|
||||
<uni-datetime-picker type="datetime" v-model="formData.qdkstime">
|
||||
<view class="list-item-row">
|
||||
<text class="list-label">开始时间</text>
|
||||
<view class="list-label-container">
|
||||
<text class="required-asterisk">*</text>
|
||||
<text class="list-label">会议开始</text>
|
||||
</view>
|
||||
<view class="list-value">
|
||||
<text :class="{ placeholder: !formData.qdkstime }">
|
||||
{{ formData.qdkstime || '请选择开始时间' }}
|
||||
{{ formData.qdkstime || '请选择会议开始时间' }}
|
||||
</text>
|
||||
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
@ -95,10 +126,13 @@
|
||||
<view class="info-card list-item-card">
|
||||
<uni-datetime-picker type="datetime" v-model="formData.qdjstime">
|
||||
<view class="list-item-row">
|
||||
<text class="list-label">结束时间</text>
|
||||
<view class="list-label-container">
|
||||
<text class="required-asterisk">*</text>
|
||||
<text class="list-label">会议结束</text>
|
||||
</view>
|
||||
<view class="list-value">
|
||||
<text :class="{ placeholder: !formData.qdjstime }">
|
||||
{{ formData.qdjstime || '请选择结束时间' }}
|
||||
{{ formData.qdjstime || '请选择会议结束时间' }}
|
||||
</text>
|
||||
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
@ -106,6 +140,7 @@
|
||||
</uni-datetime-picker>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 发布人 -->
|
||||
<view class="info-card list-item-card">
|
||||
<view class="list-item-row">
|
||||
@ -171,6 +206,7 @@ const formData = reactive({
|
||||
mdqz: '0',
|
||||
qdkstime: '',
|
||||
qdjstime: '',
|
||||
dkkstime: '',
|
||||
jsxm: '',
|
||||
qdFbtime: '',
|
||||
targetTeachers: [] as TeacherInfo[]
|
||||
@ -273,12 +309,17 @@ const handlePublish = async () => {
|
||||
}
|
||||
|
||||
if (!formData.qdkstime) {
|
||||
uni.showToast({ title: '请选择开始时间', icon: 'none' });
|
||||
uni.showToast({ title: '请选择会议开始时间', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.qdjstime) {
|
||||
uni.showToast({ title: '请选择结束时间', icon: 'none' });
|
||||
uni.showToast({ title: '请选择会议结束时间', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.dkkstime) {
|
||||
uni.showToast({ title: '请选择签到打卡开始时间', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -312,6 +353,7 @@ const handlePublish = async () => {
|
||||
mdqz: formData.mdqz,
|
||||
qdkstime: formatDateTime(formData.qdkstime),
|
||||
qdjstime: formatDateTime(formData.qdjstime),
|
||||
dkkstime: formatDateTime(formData.dkkstime),
|
||||
jsId: js.id,
|
||||
jsxm: formData.jsxm,
|
||||
qdFbtime: formData.qdFbtime,
|
||||
@ -372,38 +414,72 @@ const handlePublish = async () => {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.title-input {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
|
||||
:deep(.uni-easyinput__content) {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:deep(.uni-easyinput__content-input) {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
:deep(.uni-easyinput__placeholder-class) {
|
||||
color: #999;
|
||||
}
|
||||
.form-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.content-input {
|
||||
.required-asterisk {
|
||||
color: #ff4757;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.label-text {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.section-title-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.list-label-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
|
||||
:deep(.uni-easyinput__content) {
|
||||
background: transparent;
|
||||
background: #fff;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
:deep(.uni-easyinput__content-input) {
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
:deep(.uni-easyinput__placeholder-class) {
|
||||
color: #999;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
:deep(.uni-easyinput__content):focus-within {
|
||||
border-color: #007aff;
|
||||
box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.1);
|
||||
}
|
||||
|
||||
&.single-line {
|
||||
:deep(.uni-easyinput__content) {
|
||||
min-height: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
:deep(.uni-easyinput__content-input) {
|
||||
line-height: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -26,6 +26,10 @@
|
||||
</view>
|
||||
|
||||
<view class="qr-info">
|
||||
<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">{{ formatTime(meetingInfo?.qdkstime) }} - {{ formatTime(meetingInfo?.qdjstime) }}</text>
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
</view>
|
||||
|
||||
<!-- 具体选择 -->
|
||||
<view class="filter-row" v-if="selectType && datas.length > 0 && selectType !== 1">
|
||||
<view class="filter-row" v-if="selectType && selectType !== 1">
|
||||
<view class="filter-label">{{ getSecondSelectLabel() }}</view>
|
||||
<view class="multi-select-container">
|
||||
<view class="selected-items-display" @click="showMultiSelectModal">
|
||||
@ -256,7 +256,8 @@ const onSelectTypeChange = async (e: any) => {
|
||||
// 按科目:需要加载科目数据
|
||||
await loadKmData();
|
||||
} else if (selectType.value === 4) {
|
||||
// 按职务:需要加载职务数据
|
||||
// 按职务:默认选择党政职务,直接加载职务数据
|
||||
zwType.value = 1; // 默认选择党政职务
|
||||
await loadZwData();
|
||||
}
|
||||
};
|
||||
@ -329,20 +330,20 @@ const loadAllTeachersFromStorage = async () => {
|
||||
console.log('parsedData是否为对象:', typeof parsedData === 'object');
|
||||
console.log('parsedData的所有属性:', Object.keys(parsedData || {}));
|
||||
console.log('parsedData的data属性:', parsedData?.data);
|
||||
console.log('parsedData的allJs属性:', parsedData?.allJs);
|
||||
console.log('parsedData的allJsBasicInfoVo属性:', parsedData?.allJsBasicInfoVo);
|
||||
|
||||
// 检查数据结构,可能allJs直接在parsedData下
|
||||
// 检查数据结构,可能allJsBasicInfoVo直接在parsedData下
|
||||
let allJsData;
|
||||
if (parsedData.data && parsedData.data.allJs) {
|
||||
allJsData = parsedData.data.allJs;
|
||||
console.log('从parsedData.data.allJs获取数据');
|
||||
} else if (parsedData.allJs) {
|
||||
allJsData = parsedData.allJs;
|
||||
console.log('从parsedData.allJs获取数据');
|
||||
if (parsedData.data && parsedData.data.allJsBasicInfoVo) {
|
||||
allJsData = parsedData.data.allJsBasicInfoVo;
|
||||
console.log('从parsedData.data.allJsBasicInfoVo获取数据');
|
||||
} else if (parsedData.allJsBasicInfoVo) {
|
||||
allJsData = parsedData.allJsBasicInfoVo;
|
||||
console.log('从parsedData.allJsBasicInfoVo获取数据');
|
||||
} else {
|
||||
console.warn('localStorage中没有找到allJs数据');
|
||||
console.warn('localStorage中没有找到allJsBasicInfoVo数据');
|
||||
console.warn('可用的属性:', Object.keys(parsedData));
|
||||
uni.showToast({ title: '未找到教师数据(allJs)', icon: 'none' });
|
||||
uni.showToast({ title: '未找到教师数据(allJsBasicInfoVo)', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -440,18 +441,18 @@ const loadTeachersByNjIdFromStorage = async () => {
|
||||
parsedData = storageData;
|
||||
}
|
||||
|
||||
// 检查数据结构,可能allJs直接在parsedData下
|
||||
// 检查数据结构,可能allJsBasicInfoVo直接在parsedData下
|
||||
let allJsData;
|
||||
if (parsedData.data && parsedData.data.allJs) {
|
||||
allJsData = parsedData.data.allJs;
|
||||
console.log('从parsedData.data.allJs获取数据');
|
||||
} else if (parsedData.allJs) {
|
||||
allJsData = parsedData.allJs;
|
||||
console.log('从parsedData.allJs获取数据');
|
||||
if (parsedData.data && parsedData.data.allJsBasicInfoVo) {
|
||||
allJsData = parsedData.data.allJsBasicInfoVo;
|
||||
console.log('从parsedData.data.allJsBasicInfoVo获取数据');
|
||||
} else if (parsedData.allJsBasicInfoVo) {
|
||||
allJsData = parsedData.allJsBasicInfoVo;
|
||||
console.log('从parsedData.allJsBasicInfoVo获取数据');
|
||||
} else {
|
||||
console.warn('localStorage中没有找到allJs数据');
|
||||
console.warn('localStorage中没有找到allJsBasicInfoVo数据');
|
||||
console.warn('可用的属性:', Object.keys(parsedData));
|
||||
uni.showToast({ title: '未找到教师数据(allJs)', icon: 'none' });
|
||||
uni.showToast({ title: '未找到教师数据(allJsBasicInfoVo)', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
@ -509,6 +510,8 @@ const loadTeachersByZwIdFromStorage = async () => {
|
||||
console.log('=== 开始从localStorage按职务加载教师数据 ===');
|
||||
console.log('选中的职务ID:', selectTwoType.value);
|
||||
console.log('职务类型:', zwType.value === 1 ? '党政职务' : '其他职务');
|
||||
console.log('selectTwoType.value类型:', typeof selectTwoType.value);
|
||||
console.log('selectTwoType.value是否为数组:', Array.isArray(selectTwoType.value));
|
||||
|
||||
// 从localStorage获取所有教师数据
|
||||
const storageData = uni.getStorageSync('app-common');
|
||||
@ -535,15 +538,15 @@ const loadTeachersByZwIdFromStorage = async () => {
|
||||
parsedData = storageData;
|
||||
}
|
||||
|
||||
// 检查数据结构,从parsedData.data.allJs获取教师数据
|
||||
if (!parsedData.data || !parsedData.data.allJs) {
|
||||
// 检查数据结构,从parsedData.data.allJsBasicInfoVo获取教师数据
|
||||
if (!parsedData.data || !parsedData.data.allJsBasicInfoVo) {
|
||||
console.warn('localStorage中没有找到教师数据');
|
||||
console.warn('可用的属性:', Object.keys(parsedData || {}));
|
||||
uni.showToast({ title: '未找到教师数据', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
const allJsData = parsedData.data.allJs;
|
||||
const allJsData = parsedData.data.allJsBasicInfoVo;
|
||||
console.log('allJs数据结构:', allJsData);
|
||||
|
||||
// 检查allJs是否有result字段
|
||||
@ -556,26 +559,42 @@ const loadTeachersByZwIdFromStorage = async () => {
|
||||
const teacherArray = allJsData.result;
|
||||
console.log('教师数组数据:', teacherArray);
|
||||
console.log('教师数组长度:', teacherArray.length);
|
||||
|
||||
// 打印前几个教师的数据结构,特别关注职务字段
|
||||
if (teacherArray.length > 0) {
|
||||
console.log('第一个教师完整数据:', teacherArray[0]);
|
||||
console.log('第一个教师的dzzw字段:', teacherArray[0].dzzw);
|
||||
console.log('第一个教师的qtzw字段:', teacherArray[0].qtzw);
|
||||
if (teacherArray.length > 1) {
|
||||
console.log('第二个教师完整数据:', teacherArray[1]);
|
||||
console.log('第二个教师的dzzw字段:', teacherArray[1].dzzw);
|
||||
console.log('第二个教师的qtzw字段:', teacherArray[1].qtzw);
|
||||
}
|
||||
}
|
||||
|
||||
// 根据职务类型和选中的职务ID过滤教师
|
||||
const selectedZwId = selectTwoType.value[0]; // 假设只选择一个职务
|
||||
const selectedZwIds = selectTwoType.value; // 支持多个职务选择
|
||||
console.log('选中的职务ID列表:', selectedZwIds);
|
||||
|
||||
const filteredTeachers = teacherArray.filter((teacher: any) => {
|
||||
let hasZwId = false;
|
||||
|
||||
if (zwType.value === 1) {
|
||||
// 党政职务:检查dzzw字段
|
||||
if (teacher.dzzw) {
|
||||
const dzzwArray = teacher.dzzw.split(',');
|
||||
hasZwId = dzzwArray.includes(selectedZwId);
|
||||
const dzzwArray = teacher.dzzw.split(',').map((id: string) => id.trim());
|
||||
// 检查教师是否有任何一个选中的党政职务
|
||||
hasZwId = selectedZwIds.some(selectedId => dzzwArray.includes(selectedId));
|
||||
}
|
||||
console.log(`教师${teacher.jsxm || teacher.name}的党政职务:`, teacher.dzzw, '是否匹配职务ID:', selectedZwId, '结果:', hasZwId);
|
||||
console.log(`教师${teacher.jsxm || teacher.name}的党政职务:`, teacher.dzzw, '是否匹配职务ID列表:', selectedZwIds, '结果:', hasZwId);
|
||||
} else if (zwType.value === 2) {
|
||||
// 其他职务:检查qtzw字段
|
||||
if (teacher.qtzw) {
|
||||
const qtzwArray = teacher.qtzw.split(',');
|
||||
hasZwId = qtzwArray.includes(selectedZwId);
|
||||
const qtzwArray = teacher.qtzw.split(',').map((id: string) => id.trim());
|
||||
// 检查教师是否有任何一个选中的其他职务
|
||||
hasZwId = selectedZwIds.some(selectedId => qtzwArray.includes(selectedId));
|
||||
}
|
||||
console.log(`教师${teacher.jsxm || teacher.name}的其他职务:`, teacher.qtzw, '是否匹配职务ID:', selectedZwId, '结果:', hasZwId);
|
||||
console.log(`教师${teacher.jsxm || teacher.name}的其他职务:`, teacher.qtzw, '是否匹配职务ID列表:', selectedZwIds, '结果:', hasZwId);
|
||||
}
|
||||
|
||||
return hasZwId;
|
||||
@ -725,9 +744,9 @@ const loadZwData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有缓存数据,则调用getZwListByLx方法获取并缓存
|
||||
if (!zwData) {
|
||||
console.log('localStorage中没有职务数据,开始调用getZwListByLx获取并缓存');
|
||||
// 如果没有缓存数据或缺少某种职务类型,则调用getZwListByLx方法获取并缓存
|
||||
if (!zwData || !zwData['党政职务'] || !zwData['其他职务']) {
|
||||
console.log('localStorage中没有完整职务数据,开始调用getZwListByLx获取并缓存');
|
||||
|
||||
try {
|
||||
// 导入并调用getZwListByLx方法
|
||||
@ -773,7 +792,10 @@ const loadZwData = async () => {
|
||||
|
||||
if (filteredData.length === 0) {
|
||||
console.warn('没有找到对应类型的职务数据');
|
||||
uni.showToast({ title: '未找到对应类型的职务数据', icon: 'none' });
|
||||
// 不显示错误提示,因为可能还没有选择具体职务
|
||||
// uni.showToast({ title: '未找到对应类型的职务数据', icon: 'none' });
|
||||
// 设置空数组,让界面显示选择框
|
||||
datas.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
@ -971,15 +993,19 @@ const ensureTeacherDataCached = async () => {
|
||||
}
|
||||
|
||||
// 检查是否已有教师数据
|
||||
if (parsedData?.data?.allJs?.result && parsedData.data.allJs.result.length > 0) {
|
||||
if (parsedData?.data?.allJsBasicInfoVo?.result && parsedData.data.allJsBasicInfoVo.result.length > 0) {
|
||||
hasTeacherData = true;
|
||||
console.log('localStorage中已有教师数据,数量:', parsedData.data.allJs.result.length);
|
||||
console.log('localStorage中已有教师数据,数量:', parsedData.data.allJsBasicInfoVo.result.length);
|
||||
}
|
||||
|
||||
// 检查是否已有职务数据
|
||||
if (parsedData?.data?.zw) {
|
||||
// 检查是否已有职务数据(需要同时包含党政职务和其他职务)
|
||||
if (parsedData?.data?.zw &&
|
||||
parsedData.data.zw['党政职务'] &&
|
||||
parsedData.data.zw['其他职务']) {
|
||||
hasZwData = true;
|
||||
console.log('localStorage中已有职务数据');
|
||||
console.log('localStorage中已有完整的职务数据');
|
||||
} else if (parsedData?.data?.zw) {
|
||||
console.log('localStorage中只有部分职务数据,需要重新加载');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
</view>
|
||||
<view class="student-info">
|
||||
<text class="student-name">{{ xs.xsXm || xs.xsxm }}</text>
|
||||
<text class="student-class">{{ xs.bjmc }}</text>
|
||||
<text class="student-class">{{ xs.njmcName || xs.njmc }}{{ xs.bjmc ? ' ' + xs.bjmc : '' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -258,6 +258,7 @@ const loadXsList = async () => {
|
||||
tx: dmXs.tx || dmXs.xstx || dmXs.avatar,
|
||||
bjmc: dmXs.bjmc,
|
||||
njmc: dmXs.njmc,
|
||||
njmcName: dmXs.njmcName || dmXs.njmc,
|
||||
jzxm: dmXs.jzxm,
|
||||
jzdh: dmXs.jzdh,
|
||||
xsxm: dmXs.xsxm || dmXs.xm,
|
||||
|
||||
@ -32,6 +32,10 @@
|
||||
<view class="info-label">开课地点:</view>
|
||||
<view class="info-data">{{ xkkc.kcdd }}</view>
|
||||
</view>
|
||||
<view class="course-info-item">
|
||||
<view class="info-label">开课年级:</view>
|
||||
<view class="info-data">{{ xkkc.njname || '暂无' }}</view>
|
||||
</view>
|
||||
<view class="course-info-item">
|
||||
<view class="info-label">上课人数:</view>
|
||||
<view class="info-data">{{ xkkc.hasNum || 0 }} | {{ xkkc.maxNum || 0 }}</view>
|
||||
@ -240,7 +244,6 @@ const goDm = (xkkc: any) => {
|
||||
} else {
|
||||
msg = "上课时间未到,无法点名";
|
||||
}
|
||||
// TODO: 测试阶段,默认都可以点名,不计算时间
|
||||
dmFlag = true;
|
||||
if (dmFlag) {
|
||||
setData(xkkc);
|
||||
|
||||
@ -35,6 +35,10 @@
|
||||
<view class="info-label">开课地点:</view>
|
||||
<view class="info-data">{{ xkkc.kcdd }}</view>
|
||||
</view>
|
||||
<view class="course-info-item">
|
||||
<view class="info-label">开课年级:</view>
|
||||
<view class="info-data">{{ xkkc.njname || '暂无' }}</view>
|
||||
</view>
|
||||
<view class="course-info-item">
|
||||
<view class="info-label">上课人数:</view>
|
||||
<view class="info-data">{{ xkkc.hasNum || 0 }} | {{ xkkc.maxNum || 0 }}</view>
|
||||
|
||||
@ -19,6 +19,10 @@
|
||||
<view class="info-label">上课周期:</view>
|
||||
<view class="info-data">{{ xkkc.skzqmc }}</view>
|
||||
</view>
|
||||
<view class="course-info-item">
|
||||
<view class="info-label">开课年级:</view>
|
||||
<view class="info-data">{{ xkkc.njname || '暂无' }}</view>
|
||||
</view>
|
||||
<view class="course-info-item">
|
||||
<view class="info-label">上课时间:</view>
|
||||
<view class="info-data">{{ formatClassTime(xkkc.skkstime, xkkc.skjstime) }}</view>
|
||||
@ -204,8 +208,13 @@ const schema = reactive<FormsSchema[]>([
|
||||
{
|
||||
field: "jhsj",
|
||||
label: "计划时间",
|
||||
component: "BasicDateTimes",
|
||||
componentProps: {},
|
||||
component: "BasicInput",
|
||||
componentProps: {
|
||||
type: "date",
|
||||
placeholder: "请选择计划日期",
|
||||
// 在微信浏览器中强制显示日期选择器
|
||||
style: "position: relative; z-index: 1000;",
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "jhdd",
|
||||
@ -222,6 +231,8 @@ const schema = reactive<FormsSchema[]>([
|
||||
},
|
||||
componentProps: {
|
||||
type: "textarea",
|
||||
maxlength: -1, // 不限字数
|
||||
showCount: false, // 不显示字数统计
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user