瀏覽代碼

提交材料

liurongli 5 天之前
父節點
當前提交
3c5ad13cbc

+ 12 - 0
common/common.js

@@ -185,4 +185,16 @@ export const fileSizeConvert = (byte)=>{
     }else{
         return kbSize.toFixed(2) + 'KB'
     }
+}
+// 计算文件大小方法,如果文件可以算出 MB,就返回 MB,否则返回 KB
+export const getFileSize = (fileSize) => {
+    if (!fileSize) return '0 KB'
+
+    if (fileSize >= 1024 * 1024) {
+        fileSize = Math.round(fileSize / (1024 * 1024))
+        return `${fileSize} MB`
+    } else {
+        fileSize = Math.round(fileSize / 1024)
+        return `${fileSize} KB`
+    }
 }

+ 11 - 4
pages.json

@@ -261,16 +261,23 @@
 			}
 		},
 		{
-			"path": "pages/materialCollection/submitMaterial/index",
+			"path": "pages/materialCollection/submitMaterial/taskDetail",
 			"style": {
-				"navigationBarTitleText": "提交材料",
+				"navigationBarTitleText": "提交材料-任务详情",
 				"navigationStyle": "custom"
 			}
 		},
 		{
-			"path": "pages/materialCollection/reviewMaterial/index",
+			"path": "pages/materialCollection/submitMaterial/submitDetail",
 			"style": {
-				"navigationBarTitleText": "审核材料",
+				"navigationBarTitleText": "提交材料-提交材料",
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/materialCollection/reviewMaterial/auditDetail",
+			"style": {
+				"navigationBarTitleText": "审核材料-审核详情",
 				"navigationStyle": "custom"
 			}
 		}

+ 549 - 0
pages/materialCollection/reviewMaterial/auditDetail.vue

@@ -0,0 +1,549 @@
+<template>
+	<view class="page_body">
+		<!-- 头部 -->
+		<nav-bar height="96" title="任务详情" :onlyTitle="true" :showHeaderborder="true" @GoBack="GoBack"></nav-bar>
+		<!-- 吸顶 -->
+		<uv-sticky style="top:98rpx">
+			<view class="search_tabs" v-if="state.isSticky">
+				<scroll-view class="scroll_tabs" scroll-x="true" scroll-left="0" :show-scrollbar="false">
+					<view :class="['tab_item',{'selected':item.value==state.tabsSelected}]" v-for="item in state.tabList"
+						:key="item.value" @click="SelectTabsValue(item.value)">
+						{{item.label}}
+					</view>
+				</scroll-view>
+			</view>
+		</uv-sticky>
+		<view class="scroll_view_box">
+			<scroll-view
+				class="scroll_view_y" 
+				:scroll-top="state.scrollTop" 
+				scroll-y="true" 
+				:refresher-enabled="false"
+				:enable-back-to-top="true" 
+				:show-scrollbar="false" 
+				:scroll-with-animation="true"
+				refresher-default-style="none" 
+				refresher-background="#F8F9FD" 
+				:refresher-triggered="state.isRefreshing"
+				@scrolltolower="OnLoadMore" 
+				@refresherrefresh="OnRefresh" 
+				@scroll="OnScroll">
+				<view class="refresh_loading" v-if="state.isRefreshing"><uni-load-more status="loading" /></view>
+				<view class="page_content padding_btm">
+					<view class="task_title">{{state?.details?.taskName}}</view>
+					<view class="task_class_collect">
+						<text class="class_item">{{state?.details?.departmentName}}</text>
+						<text class="class_item">{{state?.details?.materialCategoryName}}</text>
+						<text class="class_item">{{state?.details?.createBy}}</text>
+					</view>
+					<view class="task_time">
+						<view class="time_day">
+							<text class="time_item">{{state?.details?.startTime}}</text>
+							<text class="time_item">至</text>
+							<text class="time_item">{{state?.details?.endTime}}</text>
+							<text class="time_item" v-if="remainingDays(state?.details?.endTime) > 0">剩余{{remainingDays(state?.details?.endTime)}}天</text>
+						</view>
+						<view :class="['task_status', statusClassMap[state?.details?.taskStatus]]">
+							{{taskStatusMap[state?.details.taskStatus]}}
+						</view>
+					</view>
+					<view :class="['task_desc',{'over':!state.isExpand}]">
+						采集要求:{{state?.details?.collectionRequire}}
+					</view>
+					<view class="task_desc_all">
+						采集要求:{{state?.details?.collectionRequire}}
+					</view>
+					<view class="expand_btn" v-if="state.showExpandBtn" @click="toggleExpand"><text
+							class="expand_text">{{!state.isExpand?'展开':'收起'}}</text><uni-icons class="expand_icon"
+							:type="!state.isExpand?'down':'up'"></uni-icons></view>
+				</view>
+				<!-- 间隔 -->
+				<view class="row_gap"></view>
+				<!-- 吸顶 -->
+				<!-- <uv-sticky style="top:98rpx"> -->
+				<view class="search_tabs">
+					<scroll-view class="scroll_tabs" scroll-x="true" scroll-left="0" :show-scrollbar="false">
+						<view :class="['tab_item',{'selected':item.value==state.tabsSelected}]" v-for="item in state.tabList"
+							:key="item.value" @click="SelectTabsValue(item.value)">
+							{{item.label}}
+						</view>
+					</scroll-view>
+				</view>	
+				<!-- </uv-sticky> -->
+				<view class="page_content page_content_data">
+						<view class="total">共 {{state.pageList?.length || 0}} 人</view>
+						<view class="list_item" v-for="item in state.pageList" :Key="item.id">
+							<view class="item_name_file">
+								<view class="name_file">
+									<text class="name">{{item.targetName}}</text>
+									<text class="file_num">文件数量:{{item?.taskTargetFileVos?.length || 0}}</text>
+								</view>
+								<view class="btn">{{state.tabsSelected>0?'查看':'审核'}}</view>
+							</view>
+							<view class="item_info">
+								<text class="info_text">{{item.showInfo}}</text>
+								<text class="info_text">{{item?.taskTargetFileVos?.[item.taskTargetFileVos.length -1]?.createTime}}</text>
+							</view>
+						</view>
+						<view class="no_data" v-if="!state.isLoading && state.pageList.length == 0">
+							<image class="no_data_img" src="@/static/image/bg/no_data.png"></image>
+							<text class="no_data_text">暂无数据</text>
+						</view>
+				</view>
+				<uni-load-more v-if="state.pageList.length > 0" :status="state.loadStatus" />
+			</scroll-view>	
+		</view>
+	</view>
+</template>
+
+<script setup>
+	import uniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue';
+	import uvSticky from '@/uni_modules/uv-sticky/components/uv-sticky/uv-sticky.vue'; //吸顶
+	import navBar from '@/components/navBar.vue';
+	import { remainingDays } from '@/common/common.js';
+	import {getReviewTaskList} from '@/reqApi/notification.js';
+	import {
+		onLoad
+	} from '@dcloudio/uni-app';
+	import {
+		reactive,
+		ref,
+		nextTick,
+		getCurrentInstance,
+		onMounted,
+		onUnmounted
+	} from 'vue';
+	const state = reactive({
+		taskId: '', //任务id
+		tabsSelected: 0, //默认选中tab值
+		tabList: [{
+			label: '待审核',
+			value: 0
+		}, {
+			label: '已审核',
+			value: 1
+		}, {
+			label: '已驳回',
+			value: 2
+		}], //tab列表
+		showExpandBtn: false, //是否显示展开按钮
+		isExpand: false, //是否展开
+		pageSize: 50,
+		pageNum: 1,
+		scrollTop: 0,
+		oldScrollTop: 0,
+		isRefreshing: false, //设置当前下拉刷新状态,true 表示下拉刷新已经被触发,false 表示下拉刷新未被触发
+		isLoading: true, //加载中
+		noMoreData: false, //没有更多数据了
+		loadStatus: 'more', //loading状态
+		details: {}, //详情信息
+		pageList: [], //列表数据
+		searchBoxTop:0,//搜索框距离顶部的高度
+		isSticky:false,//是否吸顶
+	});
+	const statusClassMap = {
+		1: 'status_published',
+		2: 'status_closed'
+	};
+	const taskStatusMap = {
+		1: '进行中',
+		2: '已结束'
+	};
+	onLoad((option) => {
+		state.taskId = option.taskId;
+	})
+	onMounted(() => {
+		state.details = uni.getStorageSync('collectReviewDetailInfo') || {};
+		OnLoadData(true,true); // 数据加载完成后触发 checkTextOverflow
+	});
+	onUnmounted(() => {
+		uni.removeStorageSync('collectReviewDetailInfo');//移除缓存
+	})
+	const checkTextOverflow = () => {
+		nextTick(() => {
+			const instance = getCurrentInstance();
+			// 安全保护
+			if (!instance || !instance.proxy) {
+				// 备用方案:全局查询(要求元素 class 唯一)
+				let taskDescAllHeight = 0,
+					taskDescHeight = 0;
+				const query = uni.createSelectorQuery();
+				query.select('.task_desc_all').boundingClientRect((rect) => {
+					taskDescAllHeight = rect?.height || 0;
+				});
+				query.select('.task_desc').boundingClientRect((rect) => {
+					taskDescHeight = rect?.height || 0;
+				});
+				query.select('.page_content_data').boundingClientRect((rect) => {
+					state.searchBoxTop = rect?.top || 0;
+				});
+				query.exec(() => {
+					// 所有回调执行完毕后触发
+					state.showExpandBtn = taskDescAllHeight > taskDescHeight + 1;
+				});
+				return;
+			}
+			let taskDescAllHeight = 0,
+				taskDescHeight = 0;
+			const query = uni.createSelectorQuery().in(instance.proxy);
+			query.select('.task_desc_all').boundingClientRect((rect) => {
+				taskDescAllHeight = rect?.height || 0;
+			});
+			query.select('.task_desc').boundingClientRect((rect) => {
+				taskDescHeight = rect?.height || 0;
+			});
+			query.select('.page_content_data').boundingClientRect((rect) => {
+				state.searchBoxTop = rect?.top || 0;
+			});
+			query.exec(() => {
+				// 所有回调执行完毕后触发
+				state.showExpandBtn = taskDescAllHeight > taskDescHeight + 1;
+			});
+		});
+	}
+	// 展开收起切换
+	const toggleExpand = () => {
+		state.isExpand = !state.isExpand;
+	}
+	//切换tab
+	const SelectTabsValue = (val) => {
+		state.tabsSelected = val;
+		OnLoadData(true);
+	}
+	//搜索
+	const HandleSearchInput = (val) => {
+		state.searchWord = val;
+		OnLoadData(true);
+	}
+	const ToSafeArr = (val) => {
+	  if (Array.isArray(val)) return val
+	  return val ? [val] : []
+	}
+	/*
+	 *详情
+	 * initPage:初始分页 从第一开始
+	 * initData:初始加载
+	 */
+	const OnLoadData = (initPage,initData = false) => {
+		// uni.showLoading({
+		// 	title: '加载中'
+		// });
+		if (initPage) state.pageNum = 1;//如果是下拉刷新、重新查询 默认加载第一页
+		state.isLoading = true;
+		state.loadStatus = 'loading';
+		getReviewTaskList({
+			taskId: state.taskId,
+			reviewStatus: state.tabsSelected,
+			pageNum:  state.pageNum,
+			pageSize:  state.pageSize
+		}).then(res=>{
+			if(res.code == 200){
+				const total = Number(res?.data?.reviewInfoVoPageResult?.total) || 0;//总数
+				const pages = Math.ceil(total / state.pageSize);//总页数
+				const recordsData = res?.data?.reviewInfoVoPageResult?.records || [];
+				state.pageList = initPage ? recordsData : [...state.pageList,...recordsData];
+				state.noMoreData = state.pageNum == pages;
+				state.pageNum++;
+				if(initData){
+					checkTextOverflow();
+				}
+			}
+		}).finally(()=>{
+			// uni.hideLoading();
+			state.isLoading = false;
+			state.isRefreshing = false;//下拉刷新未被触发
+			if(state.noMoreData){
+				state.loadStatus = 'no-more';
+			}else{
+				state.loadStatus = 'more';
+			}
+			//返回到页面顶部
+			if(initPage){
+				GoTop();
+			}
+		})
+	}
+	// 自定义下拉刷新被触发	 下拉刷新
+	const OnRefresh = () => {
+		state.isRefreshing = true; //下拉刷新已经被触发
+		OnLoadData(true);
+	}
+	//滚动到底部/右边 触发上拉刷新
+	const OnLoadMore = () => {
+		if (state.isLoading || state.noMoreData) return;
+		OnLoadData(false);
+	}
+	//	滚动时触发
+	const OnScroll = (e) => {
+		const scrollTop = e.detail.scrollTop;
+		if(scrollTop > state.searchBoxTop){
+			state.isSticky = true;
+		}else{
+			state.isSticky = false;
+		}
+		state.oldScrollTop = scrollTop;
+	}
+	//返回到顶部
+	const GoTop = () => {
+		// 解决view层不同步的问题
+		state.scrollTop = state.oldScrollTop;
+		nextTick(() => {
+			state.scrollTop = 0
+		});
+	}
+	//返回
+	const GoBack = () => {
+		uni.navigateBack({
+			delta: 1
+		});
+	}
+</script>
+
+<style scoped lang="scss">
+	.page_body{
+		height: 100%;
+		min-height: auto;
+		.scroll_view_box{
+			width: 100%;
+			display: flex;
+			flex-direction: column;
+			height: calc(100% - 96rpx);
+		}
+		.scroll_view_y {
+			height: 100%;
+		
+			.refresh_loading {
+				padding: 10rpx 0;
+			}
+		}
+	}
+	.page_content {
+		&.padding_btm {
+			padding-bottom: 24rpx;
+
+			.task_title {
+				font-weight: 500;
+				font-size: 32rpx;
+				color: #333333;
+				margin-top: 24rpx;
+			}
+
+			.task_class_collect {
+				display: inline-flex;
+				align-items: center;
+				flex-wrap: wrap;
+				width: 100%;
+				margin-top: 16rpx;
+				gap: 16rpx;
+				.class_item {
+					font-weight: 400;
+					font-size: 24rpx;
+					color: #999999;
+				}
+			}
+
+			.task_time {
+				display: flex;
+				align-items: flex-start;
+				width: 100%;
+				margin-top: 16rpx;
+
+				.time_day {
+					display: inline-flex;
+					align-items: center;
+					flex-wrap: wrap;
+					flex: 1;
+
+					.time_item {
+						font-weight: 400;
+						font-size: 24rpx;
+						line-height: 48rpx;
+						color: #999999;
+
+						&:nth-child(2) {
+							margin: 0 8rpx;
+						}
+
+						&:nth-child(4) {
+							color: #2E64FA;
+							margin-left: 16rpx;
+						}
+					}
+				}
+
+				.task_status {
+					display: inline-flex;
+					align-items: center;
+					height: 48rpx;
+					padding: 0 12rpx;
+					border-radius: 8rpx;
+					font-weight: 400;
+					font-size: 24rpx;
+
+					&.status_pending {
+						background: rgba(46, 100, 250, 0.1);
+						color: #2E64FA;
+					}
+					
+					&.status_published {
+						background: rgba(82, 196, 26, 0.1);
+						color: #2BC644;
+					}
+					
+					&.status_closed {
+						background: #6666661a;
+						color: #666666;
+					}
+				}
+			}
+
+			.task_desc {
+				margin-top: 24rpx;
+				display: flex;
+				width: 100%;
+				padding-top: 24rpx;
+				border-top: 2rpx solid #F3F3F3;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #999999;
+				line-height: 44rpx;
+				box-sizing: border-box;
+				position: relative;
+
+				&.over {
+					max-height: 168rpx;
+					overflow: hidden;
+					transition: all 0.25s;
+					display: -webkit-box;
+					-webkit-line-clamp: 3;
+					-webkit-box-orient: vertical;
+				}
+			}
+
+			.task_desc_all {
+				display: flex;
+				width: 100%;
+				padding-top: 24rpx;
+				border-top: 2rpx solid #F3F3F3;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #999999;
+				line-height: 44rpx;
+				box-sizing: border-box;
+				position: absolute;
+				left: -9999px;
+				top: 0;
+				visibility: hidden; // 不可见但占据空间(不影响布局,因为absolute)
+				pointer-events: none;
+			}
+
+			.expand_btn {
+				width: 100%;
+				height: 40rpx;
+				background-color: #FFFFFF;
+				display: flex;
+				justify-content: flex-end;
+				align-items: center;
+
+				.expand_text {
+					font-weight: 400;
+					font-size: 28rpx;
+					color: #2E64FA;
+					margin-right: 8rpx;
+				}
+
+				.expand_icon {
+					font-size: 32rpx !important;
+					color: #2E64FA !important;
+				}
+			}
+		}
+	}
+
+	.row_gap {
+		background-color: #F8F9FD;
+		width: 100%;
+		height: 24rpx;
+	}
+	:deep(.search_tabs){
+		display: flex;
+		width: 100%;
+		flex-direction: column;
+		background-color: #FFFFFF;
+		padding: 24rpx;
+		border-bottom: 2rpx solid #F3F3F3;
+	}
+	.scroll_tabs {
+		margin-top:0;
+		padding: 0;
+		background-color: #FFFFFF;
+	}
+
+	.total {
+		width: 100%;
+		display: flex;
+		font-weight: 400;
+		font-size: 28rpx;
+		color: #333333;
+		padding: 24rpx 0;
+		border-bottom: 2rpx solid #F3F3F3;
+	}
+
+	.list_item {
+		display: flex;
+		flex-direction: column;
+		width: 100%;
+		padding: 24rpx 0;
+		border-bottom: 2rpx solid #F3F3F3;
+		.item_name_file{
+			display: flex;
+			width: 100%;
+			align-items: center;
+			gap: 20rpx;
+			.name_file{
+				flex:1;
+				gap: 20rpx;
+				display: flex;
+				align-items: center;
+				.name{
+					font-weight: 500;
+					font-size: 32rpx;
+					color: #333333;
+				}
+				.file_num{
+					font-weight: 400;
+					font-size: 24rpx;
+					color: #2E64FA;
+				}
+			}
+			.btn{
+				width: 104rpx;
+				height: 64rpx;
+				border-radius: 8rpx;
+				border: 2rpx solid #2E64FA;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #2E64FA;
+				display: inline-flex;
+				align-items: center;
+				justify-content: center;
+			}
+		}
+		.item_info{
+			margin-top: 16rpx;
+			display: flex;
+			gap: 16rpx;
+			align-items: center;
+			flex-wrap: wrap;
+			.info_text{
+				font-weight: 400;
+				font-size: 24rpx;
+				color: #999999;
+				flex-shrink: 0;
+			}
+		}
+	}
+	.no_data{
+		margin-top: 24rpx;
+	}
+</style>

+ 5 - 10
pages/materialCollection/reviewMaterial/index.vue

@@ -52,7 +52,7 @@
 							</text>
 						</view>
 						<view class="notice_actions">
-							<view class="action_btn edit">
+							<view class="action_btn edit" @click="handleDetail(item)">
 								<text>开始审核</text>
 							</view>
 						</view>
@@ -304,16 +304,11 @@ const GoTop = () => {
 		state.scrollTop = 0
 	});
 }
-//编辑
-const handleEdit = (item) => {
+//审核详情
+const handleDetail = (item) => {
+	uni.setStorageSync('collectReviewDetailInfo', item);
 	uni.navigateTo({
-		url: `/pages/materialCollection/taskOverview/components/index?id=${item.id}`
-	});
-};
-//
-const handleDetail = (id) => {
-	uni.navigateTo({
-		url: `/pages/materialCollection/submitMaterial/index?id=${id}`
+		url: `/pages/materialCollection/reviewMaterial/auditDetail?taskId=${item.id}`
 	});
 };
 onMounted(async () => {

+ 18 - 13
pages/materialCollection/submitMaterial/index.vue

@@ -52,7 +52,7 @@
 							</text>
 						</view>
 						<view class="notice_actions">
-							<view class="action_btn edit" >
+							<view class="action_btn edit" @click="handleDetail(item)">
 								<text>查看</text>
 							</view>
 						</view>
@@ -69,7 +69,7 @@
 </template>
 
 <script setup>
-import { reactive,ref,nextTick, onMounted } from 'vue';
+import { reactive,ref,nextTick, onMounted,onUnmounted } from 'vue';
 import navBar from '@/components/navBar.vue';
 import {findTeacherTaskTargetList,getSchoolYearList,getCategoryList} from '@/reqApi/notification.js';
 import { truncateText,remainingDays } from '@/common/common.js';
@@ -306,23 +306,28 @@ const GoTop = () => {
 		state.scrollTop = 0
 	});
 }
-//编辑
-const handleEdit = (item) => {
-	uni.navigateTo({
-		url: `/pages/materialCollection/taskOverview/components/index?id=${item.id}`
-	});
-};
-//
-const handleDetail = (id) => {
-	uni.navigateTo({
-		url: `/pages/materialCollection/submitMaterial/index?id=${id}`
-	});
+//任务详情
+const handleDetail = (item) => {
+	state.details = uni.setStorageSync('submitMaterialDetailInfo',item);
+	if(item?.taskTargetVos?.length > 1){
+		uni.navigateTo({
+			url: `/pages/materialCollection/submitMaterial/taskDetail?taskId=${item.id}&auditStatus=${state.params.auditStatus}`
+		});
+	}else{
+		const targetId = item?.taskTargetVos?.[0].id;
+		uni.navigateTo({
+			url: `/pages/materialCollection/submitMaterial/submitDetail?taskId=${item.id}&targetId=${targetId}`
+		});
+	}
 };
 onMounted(async () => {
 	await fetchSchoolYearList();//学年学期列表
 	fetchCategoryList();//归属分类
 	OnLoadData(true);
 });
+onUnmounted(() => {
+	uni.removeStorageSync('submitMaterialDetailInfo');//移除缓存
+})
 </script>
 
 <style lang="scss" scoped>

+ 665 - 0
pages/materialCollection/submitMaterial/submitDetail.vue

@@ -0,0 +1,665 @@
+<template>
+	<view class="page_body">
+		<!-- 头部 -->
+		<nav-bar height="96" title="提交材料" :onlyTitle="true" :showHeaderborder="true" @GoBack="GoBack"></nav-bar>
+		<view class="page_content_height">
+			<view class="page_content padding_btm">
+				<view class="task_title">{{state?.details?.taskName}}</view>
+				<view class="task_class_collect">
+					<text class="class_item">{{state?.details?.departmentName}}</text>
+					<text class="class_item">{{state?.details?.materialCategoryName}}</text>
+					<text class="class_item">{{state?.details?.createdBy}}</text>
+				</view>
+				<view class="task_time">
+					<view class="time_day">
+						<text class="time_item">{{state?.details?.startTime}}</text>
+						<text class="time_item">至</text>
+						<text class="time_item">{{state?.details?.endTime}}</text>
+						<text class="time_item" v-if="state?.details?.taskStatus === 1 && remainingDays(state?.details?.endTime) > 0">剩余{{remainingDays(state?.details?.endTime)}}天</text>
+					</view>
+					<view :class="['task_status', statusClassMap[state?.details?.taskStatus]]">
+						{{taskStatusMap[state?.details?.taskStatus]}}
+					</view>
+				</view>
+				<view :class="['task_desc',{'over':!state.isExpand}]">
+					采集要求:{{state?.details?.collectionRequire}}
+				</view>
+				<view class="task_desc_all">
+					采集要求:{{state?.details?.collectionRequire}}
+				</view>
+				<view class="expand_btn" v-if="state.showExpandBtn" @click="toggleExpand"><text
+						class="expand_text">{{!state.isExpand?'展开':'收起'}}</text><uni-icons class="expand_icon"
+						:type="!state.isExpand?'down':'up'"></uni-icons></view>
+			</view>
+			<!-- 间隔 -->
+			<view class="row_gap"></view>
+			<view class="page_content">
+				<view class="target_status">
+					<view class="target_tag">
+						<template v-for="text in ['departmentName','departmentGroupName','subjectName','gradeName','classTypeName','className']">
+							<text class="info_text" v-if="state?.targetFileList?.[text]" :key="text">{{state?.targetFileList?.[text]}}</text>
+						</template>
+					</view>
+					<template v-if="state?.details?.auditMechanism === 1">
+						<view v-if="state?.targetFileList?.targetStatus=== 1" class="status_tag notSubmitList">
+							<text>待提交</text>
+						</view>
+						<view v-if="state?.targetFileList?.targetStatus=== 3" class="status_tag reviewReasonList">
+							<text>已驳回</text>
+						</view>
+						<view v-if="state?.targetFileList?.targetStatus=== 2" class="status_tag notReviewList">
+							<text>审核中</text>
+						</view>
+						<view v-if="state?.targetFileList?.targetStatus=== 4" class="status_tag reviewList">
+							<text>已提交</text>
+						</view>
+					</template>
+					<template v-if="state?.details?.auditMechanism === 0">
+						<view v-if="state?.targetFileList?.targetStatus=== 1" class="status_tag notSubmitList">
+							<text>待提交</text>
+						</view>
+						<view v-else class="status_tag reviewList">
+							<text>已提交</text>
+						</view>
+					</template>
+					
+				</view>
+				<!-- 上传按钮 提交状态 1待提交 2审核中 3已驳回 4已提交 -->
+				<view class="upload_box">
+					<template v-if="state?.targetFileList?.targetStatus === 1 && state?.details?.taskStatus !== 2">
+						<image class="upload_icon" src="@/static/image/icon/upload_icon.png"></image>
+						<view class="upload_text">
+							<view class="upload_title">点击上传文件</view>
+							<view class="upload_desc">支持Word、Excel、PPT、图片(最大100MB)</view>
+						</view>
+					</template>
+					<template v-if="(state?.targetFileList?.targetStatus === 2 || state?.targetFileList?.targetStatus === 4) && state?.details?.taskStatus !== 2">
+						<image class="upload_icon" src="@/static/image/icon/upload_locked.png"></image>
+						<view class="upload_text">
+							<view class="upload_title">点击重新编辑可再提交</view>
+							<view class="upload_desc">支持Word、Excel、PPT、图片(最大100MB)</view>
+						</view>
+					</template>
+					<template v-if="state?.targetFileList?.targetStatus === 3 && state?.details?.taskStatus !== 2">
+						<image class="upload_icon" src="@/static/image/icon/upload_locked.png"></image>
+						<view class="upload_text">
+							<view class="upload_title">请先删除全部文件,再提交调整后的合规材料</view>
+							<view class="upload_desc">支持Word、Excel、PPT、图片(最大100MB)</view>
+						</view>
+					</template>
+					<template v-if="state?.details?.taskStatus == 2">
+						<image class="upload_icon" src="@/static/image/icon/upload_locked.png"></image>
+						<view class="upload_text">
+							<view class="upload_title">已结束不可再提交</view>
+							<view class="upload_desc">支持Word、Excel、PPT、图片(最大100MB)</view>
+						</view>
+					</template>
+				</view>
+				<view class="list_item" v-for="item in (state?.targetFileList?.taskTargetFileVos || [])" :Key="item.id">
+					<image class="file_icon" src="@/static/image/icon/file.png"></image>
+					<view class="file_info">
+						<view class="file_name">{{item.fileName}}</view>
+						<view class="file_size">
+							<text>{{getFileSize(item.fileSize)}}</text>
+							<text>{{ item.createTime }}</text>
+						</view>
+					</view>
+					<view class="file_btn">
+						<text @click="HandleDeleteFileList(item)" :class="['del',{'disabled':state?.targetFileList?.targetStatus === 2 || state?.targetFileList?.targetStatus === 4}]">删除</text>
+						<text class="download">下载</text>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="page_foot" v-if="state?.details?.taskStatus!=2">
+			<!-- 提交状态 1待提交 2审核中 3已驳回 4已提交 -->
+			<template v-if="(state?.targetFileList?.targetStatus === 1 || state?.targetFileList?.targetStatus === 3)">
+				<view @click="DeleteAllFiles" :class="['del',{'disabled':!state?.targetFileList?.taskTargetFileVos?.length}]">删除全部</view>
+				<view class="submit" @click="UpdateTaskTarget">提交</view>
+			</template>
+			<template v-else>
+				<view class="del disabled">删除全部</view>
+				<view class="submit" @click="UpdateTaskTargetReset">重新编辑</view>
+			</template>
+		</view>
+		<!-- 删除确认框 -->
+		<popupDialog ref="popupDelDialogRef" :showFootBorder="true" :content="state.delDialog.content" @DialogConfirm="DialogDelConfirm" />
+	</view>
+</template>
+
+<script setup>
+	import uniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue';
+	import popupDialog from '@/components/popupDialog.vue';
+	import navBar from '@/components/navBar.vue';
+	import { remainingDays,getFileSize } from '@/common/common.js';
+	import {getTaskTargetFileList,DeleteFileList,DelTaskTargetFileList,updateTaskTarget} from '@/reqApi/notification.js';
+	import {onLoad} from '@dcloudio/uni-app';
+	import {
+		reactive,
+		ref,
+		nextTick,
+		getCurrentInstance,
+		onMounted
+	} from 'vue';
+	const state = reactive({
+		taskId: '', //任务id
+		targetId:'',//
+		showExpandBtn: false, //是否显示展开按钮
+		isExpand: false, //是否展开
+		details: {}, //详情信息
+		targetFileList:{},
+		delDialog:{
+			type:'',
+			content:'',
+			fileUrls:[],
+			targetFileIds:[]
+		},
+	});
+	const popupDelDialogRef = ref(null);
+	const statusClassMap = {
+		1: 'status_published',
+		2: 'status_closed'
+	};
+	const taskStatusMap = {
+		1: '进行中',
+		2: '已结束'
+	};
+	onLoad((option) => {
+		state.taskId = option.taskId;
+		state.targetId = option.targetId;
+	})
+	onMounted(() => {
+		state.details = uni.getStorageSync('submitMaterialDetailInfo') || {};
+		checkTextOverflow();
+		GetTaskTargetFileList();
+	});
+	const checkTextOverflow = () => {
+		nextTick(() => {
+			const instance = getCurrentInstance();
+			// 安全保护
+			if (!instance || !instance.proxy) {
+				// 备用方案:全局查询(要求元素 class 唯一)
+				let taskDescAllHeight = 0,
+					taskDescHeight = 0;
+				const query = uni.createSelectorQuery();
+				query.select('.task_desc_all').boundingClientRect((rect) => {
+					taskDescAllHeight = rect?.height || 0;
+				});
+				query.select('.task_desc').boundingClientRect((rect) => {
+					taskDescHeight = rect?.height || 0;
+				});
+				query.select('.page_content_data').boundingClientRect((rect) => {
+					state.searchBoxTop = rect?.top || 0;
+				});
+				query.exec(() => {
+					// 所有回调执行完毕后触发
+					state.showExpandBtn = taskDescAllHeight > taskDescHeight + 1;
+				});
+				return;
+			}
+			let taskDescAllHeight = 0,
+				taskDescHeight = 0;
+			const query = uni.createSelectorQuery().in(instance.proxy);
+			query.select('.task_desc_all').boundingClientRect((rect) => {
+				taskDescAllHeight = rect?.height || 0;
+			});
+			query.select('.task_desc').boundingClientRect((rect) => {
+				taskDescHeight = rect?.height || 0;
+			});
+			query.select('.page_content_data').boundingClientRect((rect) => {
+				state.searchBoxTop = rect?.top || 0;
+			});
+			query.exec(() => {
+				// 所有回调执行完毕后触发
+				state.showExpandBtn = taskDescAllHeight > taskDescHeight + 1;
+			});
+		});
+	}
+	// 展开收起切换
+	const toggleExpand = () => {
+		state.isExpand = !state.isExpand;
+	}
+	//获取当前教师提交的文件列表
+	const GetTaskTargetFileList = () => {
+		getTaskTargetFileList(state.targetId).then(res=>{
+			if(res.code == 200){
+				state.targetFileList = res.data || {};
+			}
+		})
+	}
+	//单个删除
+	const HandleDeleteFileList = (item) => {
+		if(state?.targetFileList?.targetStatus === 2 || state?.targetFileList?.targetStatus === 4){
+			return false
+		}
+		const { filePath, id } = item;
+		state.delDialog = {
+			type:'del',
+			content:'是否删除当前文件?',
+			fileUrls:[filePath],
+			targetFileIds:[id]
+		};
+		popupDelDialogRef.value.OpenDialog();
+	}
+	//删除全部
+	const DeleteAllFiles = () => {
+		let fileUrls = [],targetFileIds = [];
+		const taskTargetFileVos = state?.targetFileList?.taskTargetFileVos || [];
+		if(taskTargetFileVos.length > 0){
+			taskTargetFileVos.forEach(item => {
+				fileUrls.push(item.filePath);
+				targetFileIds.push(item.id)
+			})
+			state.delDialog = {
+				type:'del',
+				content:'是否删除全部当前文件?',
+				fileUrls:fileUrls,
+				targetFileIds:targetFileIds
+			};
+			popupDelDialogRef.value.OpenDialog();
+		}
+	}
+	//提交
+	const UpdateTaskTarget = () => {
+		const taskTargetFileVos = state?.targetFileList?.taskTargetFileVos || [];
+		if(taskTargetFileVos.length == 0){
+			uni.showToast({
+				title: '请上传提交文件!',
+				icon: 'none'
+			});
+			return false
+		}
+		state.delDialog = {
+			type:'submit',
+			content:'是否提交当前任务?',
+			fileUrls:[],
+			targetFileIds:[]
+		};
+		popupDelDialogRef.value.OpenDialog();
+	}
+	//重新提交
+	const UpdateTaskTargetReset = () => {
+		state.delDialog = {
+			type:'submit',
+			content:'是否重新编辑当前任务?',
+			fileUrls:[],
+			targetFileIds:[]
+		};
+		popupDelDialogRef.value.OpenDialog();
+	}
+	//确定删除 确定提交
+	const DialogDelConfirm = () => {
+		if(state.delDialog.type == 'del'){//单个删除 批量删除
+			DelFileList();
+		}else if(state.delDialog.type == 'submit') {//提交
+			submitTaskTarget();
+		}
+	}
+	const DelFileList = async() => {
+		const res = await DeleteFileList({ fileUrls: state.delDialog.fileUrls })
+		if (res.code !== 200) {
+			uni.showToast({
+				title: res.msg,
+				icon: 'none'
+			});
+			return false
+		}
+		const resData = await DelTaskTargetFileList({
+			targetId: state.targetId,
+			targetFileIds: state.delDialog.targetFileIds
+		})
+		if (resData.code === 200) {
+			uni.showToast({
+				title: '删除成功!',
+				icon: 'none'
+			});
+			GetTaskTargetFileList();
+		}
+	}
+	//提交
+	const submitTaskTarget = () => {
+		updateTaskTarget(state.targetId).then(res => {
+			if (res.code === 200) {
+				uni.showToast({
+					title: '提交成功!',
+					icon: 'none'
+				});
+				GetTaskTargetFileList();
+			}
+		})
+	}
+	//返回
+	const GoBack = () => {
+		uni.navigateBack({
+			delta: 1
+		});
+	}
+</script>
+
+<style scoped lang="scss">
+	.page_content_height{
+		display: flex;
+		flex-direction: column;
+		width: 100%;
+		height: calc(100% - 226rpx);
+	}
+	.page_content {
+		&.padding_btm {
+			padding-bottom: 24rpx;
+
+			.task_title {
+				font-weight: 500;
+				font-size: 32rpx;
+				color: #333333;
+				margin-top: 24rpx;
+			}
+
+			.task_class_collect {
+				display: inline-flex;
+				align-items: center;
+				flex-wrap: wrap;
+				width: 100%;
+				margin-top: 16rpx;
+				gap: 16rpx;
+				.class_item {
+					font-weight: 400;
+					font-size: 24rpx;
+					color: #999999;
+				}
+			}
+
+			.task_time {
+				display: flex;
+				align-items: flex-start;
+				width: 100%;
+				margin-top: 16rpx;
+
+				.time_day {
+					display: inline-flex;
+					align-items: center;
+					flex-wrap: wrap;
+					flex: 1;
+
+					.time_item {
+						font-weight: 400;
+						font-size: 24rpx;
+						line-height: 48rpx;
+						color: #999999;
+
+						&:nth-child(2) {
+							margin: 0 8rpx;
+						}
+
+						&:nth-child(4) {
+							color: #2E64FA;
+							margin-left: 16rpx;
+						}
+					}
+				}
+
+				.task_status {
+					display: inline-flex;
+					align-items: center;
+					height: 48rpx;
+					padding: 0 12rpx;
+					border-radius: 8rpx;
+					font-weight: 400;
+					font-size: 24rpx;
+
+					&.status_pending {
+						background: rgba(46, 100, 250, 0.1);
+						color: #2E64FA;
+					}
+					
+					&.status_published {
+						background: rgba(82, 196, 26, 0.1);
+						color: #2BC644;
+					}
+					
+					&.status_closed {
+						background: #6666661a;
+						color: #666666;
+					}
+				}
+			}
+
+			.task_desc {
+				margin-top: 24rpx;
+				display: flex;
+				width: 100%;
+				padding-top: 24rpx;
+				border-top: 2rpx solid #F3F3F3;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #999999;
+				line-height: 44rpx;
+				box-sizing: border-box;
+				position: relative;
+
+				&.over {
+					max-height: 168rpx;
+					overflow: hidden;
+					transition: all 0.25s;
+					display: -webkit-box;
+					-webkit-line-clamp: 3;
+					-webkit-box-orient: vertical;
+				}
+			}
+
+			.task_desc_all {
+				display: flex;
+				width: 100%;
+				padding-top: 24rpx;
+				border-top: 2rpx solid #F3F3F3;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #999999;
+				line-height: 44rpx;
+				box-sizing: border-box;
+				position: absolute;
+				left: -9999px;
+				top: 0;
+				visibility: hidden; // 不可见但占据空间(不影响布局,因为absolute)
+				pointer-events: none;
+			}
+
+			.expand_btn {
+				width: 100%;
+				height: 40rpx;
+				background-color: #FFFFFF;
+				display: flex;
+				justify-content: flex-end;
+				align-items: center;
+
+				.expand_text {
+					font-weight: 400;
+					font-size: 28rpx;
+					color: #2E64FA;
+					margin-right: 8rpx;
+				}
+
+				.expand_icon {
+					font-size: 32rpx !important;
+					color: #2E64FA !important;
+				}
+			}
+		}
+	}
+
+	.row_gap {
+		background-color: #F8F9FD;
+		width: 100%;
+		height: 24rpx;
+	}
+
+	.target_status {
+		width: 100%;
+		display: flex;
+		align-items: center;
+		padding: 24rpx 0;
+		border-bottom: 2rpx solid #F3F3F3;
+		.target_tag{
+			flex: 1;
+			font-weight: 500;
+			font-size: 32rpx;
+			color: #333333;
+			.info_text{
+				flex-shrink: 0;
+				margin-right: 12rpx;
+			}
+		}
+		.status_tag{
+			font-size: 24rpx;
+			text-align: center;
+			padding: 8rpx 12rpx;
+			font-weight: 400;
+			border-radius: 8rpx;
+			flex-shrink: 0;
+			&.notSubmitList {
+				background: rgba(46,100,250,0.1);
+				color: #2E64FA;
+			}
+			&.notReviewList {
+				background: rgba(251,159,52,0.1);
+				color: #FB9F34;
+			}
+			&.reviewReasonList {
+				background: rgba(245,108,108,0.1);
+				color: #F56C6C;
+			}
+			&.reviewList {
+				background: rgba(43,198,68,0.1);
+				color: #2BC644;
+			}
+		}
+	}
+	.upload_box{
+		margin-top: 24rpx;
+		width: 100%;
+		background-color: #FAFBFF;
+		border-radius: 20rpx;
+		border: 2rpx dashed #CCCED4;
+		display: flex;
+		box-sizing: border-box;
+		align-items: center;
+		padding: 22rpx 0 22rpx 40rpx;
+		.upload_icon{
+			width: 64rpx;
+			height: 64rpx;
+			margin-right: 24rpx;
+			flex-shrink: 0;
+		}
+		.upload_text{
+			flex:1;
+			display: flex;
+			flex-direction: column;
+			.upload_title{
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #666666;
+			}
+			.upload_desc{
+				margin-top: 8rpx;
+				font-weight: 400;
+				font-size: 20rpx;
+				color: #CCCED4;
+			}
+		}
+	}
+	.list_item {
+		display: flex;
+		align-items: center;
+		width: 100%;
+		padding: 20rpx 24rpx;
+		background: #FFFFFF;
+		border-radius: 20rpx;
+		border: 2rpx solid #EBEEF5;
+		box-sizing: border-box;
+		margin-top: 24rpx;
+		gap:20rpx;
+		.file_icon{
+			flex-shrink: 0;
+			width: 80rpx;
+			height: 80rpx;
+		}
+		.file_info{
+			flex:1;
+			display: flex;
+			flex-direction: column;
+			overflow: hidden;
+			.file_name{
+				width: 100%;
+				font-weight: 500;
+				font-size: 28rpx;
+				color: #333333;
+				overflow: hidden;
+				white-space: nowrap;
+				text-overflow: ellipsis;
+			}
+			.file_size{
+				display: flex;
+				width: 100%;
+				gap: 20rpx;
+				font-weight: 400;
+				font-size: 24rpx;
+				color: #999999;
+				margin-top: 8rpx;
+			}
+		}
+		.file_btn{
+			flex-shrink: 0;
+			display: flex;
+			gap:24rpx;
+			font-weight: 400;
+			font-size: 28rpx;
+			.del{
+				color: #F56C6C;
+				&.disabled{
+					color: #C0C4CC;
+				}
+			}
+			.download{
+				color: #2E64FA;
+			}
+		}
+	}
+	.page_foot{
+		position: fixed;
+		display: flex;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		padding: 20rpx 24rpx;
+		background-color: #FFFFFF;
+		gap: 32rpx;
+		.del{
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			width: 240rpx;
+			height: 90rpx;
+			box-sizing: border-box;
+			border-radius: 8rpx;
+			border: 2rpx solid #F56C6C;
+			font-weight: 400;
+			font-size: 28rpx;
+			color: #F56C6C;
+			&.disabled{
+				color: #C0C4CC;
+				border: 2rpx solid #C0C4CC;
+			}
+		}
+		.submit {
+			flex:1;
+			height: 90rpx;
+			background-color: #2E64FA;
+			border-radius: 8rpx;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			font-weight: 500;
+			font-size: 28rpx;
+			color: #FFFFFF;
+		}
+	}
+</style>

+ 395 - 0
pages/materialCollection/submitMaterial/taskDetail.vue

@@ -0,0 +1,395 @@
+<template>
+	<view class="page_body">
+		<!-- 头部 -->
+		<nav-bar height="96" title="任务详情" :onlyTitle="true" :showHeaderborder="true" @GoBack="GoBack"></nav-bar>
+		<view class="page_content padding_btm">
+			<view class="task_title">{{state?.details?.taskName}}</view>
+			<view class="task_class_collect">
+				<text class="class_item">{{state?.details?.departmentName}}</text>
+				<text class="class_item">{{state?.details?.materialCategoryName}}</text>
+				<text class="class_item">{{state?.details?.createdBy}}</text>
+			</view>
+			<view class="task_time">
+				<view class="time_day">
+					<text class="time_item">{{state?.details?.startTime}}</text>
+					<text class="time_item">至</text>
+					<text class="time_item">{{state?.details?.endTime}}</text>
+					<text class="time_item" v-if="state?.details?.taskStatus === 1 && remainingDays(state?.details?.endTime) > 0">剩余{{remainingDays(state?.details?.endTime)}}天</text>
+				</view>
+				<view :class="['task_status', statusClassMap[state?.details?.taskStatus]]">
+					{{taskStatusMap[state?.details?.taskStatus]}}
+				</view>
+			</view>
+			<view :class="['task_desc',{'over':!state.isExpand}]">
+				采集要求:{{state?.details?.collectionRequire}}
+			</view>
+			<view class="task_desc_all">
+				采集要求:{{state?.details?.collectionRequire}}
+			</view>
+			<view class="expand_btn" v-if="state.showExpandBtn" @click="toggleExpand"><text
+					class="expand_text">{{!state.isExpand?'展开':'收起'}}</text><uni-icons class="expand_icon"
+					:type="!state.isExpand?'down':'up'"></uni-icons></view>
+		</view>
+		<!-- 间隔 -->
+		<view class="row_gap"></view>
+		<view class="page_content">
+			<view class="total">
+				<view class="status_tag" :class="state.auditStatus">
+					<text>{{ tabStatusClassMap[state.auditStatus] }}:{{ state?.details?.taskTargetVos?.length || 0 }}</text>
+				</view>
+			</view>
+			<view class="list_item" v-for="item in (state?.details?.taskTargetVos || [])" :Key="item.id">
+				<view class="item_name_file">
+					<view class="name_file">
+						<text class="name">{{item.targetName}}</text>
+						<text class="file_num">文件数量:{{item?.fileNum || 0}}</text>
+					</view>
+					<view class="btn" @click="HandleDetail(item.id)">{{state.auditStatus == 'notSubmitList'?'立即提交':'查看'}}</view>
+				</view>
+				<view class="item_info">
+					<template v-for="text in ['departmentName','departmentGroupName','subjectName','gradeName','classTypeName','className','updateTime']">
+						<text class="info_text" v-if="item?.[text]" :key="text">{{item?.[text]}}</text>
+					</template>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script setup>
+	import uniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue';
+	import navBar from '@/components/navBar.vue';
+	import { remainingDays } from '@/common/common.js';
+	import {onLoad} from '@dcloudio/uni-app';
+	import {
+		reactive,
+		ref,
+		nextTick,
+		getCurrentInstance,
+		onMounted,
+		onUnmounted
+	} from 'vue';
+	const state = reactive({
+		taskId: '', //任务id
+		auditStatus:'',//审核状态
+		showExpandBtn: false, //是否显示展开按钮
+		isExpand: false, //是否展开
+		details: {}, //详情信息
+	});
+	const statusClassMap = {
+		1: 'status_published',
+		2: 'status_closed'
+	};
+	const taskStatusMap = {
+		1: '进行中',
+		2: '已结束'
+	};
+	const tabStatusClassMap = {
+		'notSubmitList':'待提交',// 待提交
+		'notReviewList':'审核中',// 审核中
+		'reviewReasonList':'已驳回',// 已驳回
+		'reviewList':'已提交'// 已提交
+	};
+	onLoad((option) => {
+		state.taskId = option.taskId;
+		state.auditStatus = option.auditStatus;
+	})
+	onMounted(() => {
+		state.details = uni.getStorageSync('submitMaterialDetailInfo') || {};
+		checkTextOverflow();
+	});
+	const checkTextOverflow = () => {
+		nextTick(() => {
+			const instance = getCurrentInstance();
+			// 安全保护
+			if (!instance || !instance.proxy) {
+				// 备用方案:全局查询(要求元素 class 唯一)
+				let taskDescAllHeight = 0,
+					taskDescHeight = 0;
+				const query = uni.createSelectorQuery();
+				query.select('.task_desc_all').boundingClientRect((rect) => {
+					taskDescAllHeight = rect?.height || 0;
+				});
+				query.select('.task_desc').boundingClientRect((rect) => {
+					taskDescHeight = rect?.height || 0;
+				});
+				query.select('.page_content_data').boundingClientRect((rect) => {
+					state.searchBoxTop = rect?.top || 0;
+				});
+				query.exec(() => {
+					// 所有回调执行完毕后触发
+					state.showExpandBtn = taskDescAllHeight > taskDescHeight + 1;
+				});
+				return;
+			}
+			let taskDescAllHeight = 0,
+				taskDescHeight = 0;
+			const query = uni.createSelectorQuery().in(instance.proxy);
+			query.select('.task_desc_all').boundingClientRect((rect) => {
+				taskDescAllHeight = rect?.height || 0;
+			});
+			query.select('.task_desc').boundingClientRect((rect) => {
+				taskDescHeight = rect?.height || 0;
+			});
+			query.select('.page_content_data').boundingClientRect((rect) => {
+				state.searchBoxTop = rect?.top || 0;
+			});
+			query.exec(() => {
+				// 所有回调执行完毕后触发
+				state.showExpandBtn = taskDescAllHeight > taskDescHeight + 1;
+			});
+		});
+	}
+	// 展开收起切换
+	const toggleExpand = () => {
+		state.isExpand = !state.isExpand;
+	}
+	//提交材料
+	const HandleDetail = (targetId) => {
+		uni.navigateTo({
+			url: `/pages/materialCollection/submitMaterial/submitDetail?taskId=${state.taskId}&targetId=${targetId}`
+		});
+	}
+	//返回
+	const GoBack = () => {
+		uni.navigateBack({
+			delta: 1
+		});
+	}
+</script>
+
+<style scoped lang="scss">
+	.page_content {
+		&.padding_btm {
+			padding-bottom: 24rpx;
+
+			.task_title {
+				font-weight: 500;
+				font-size: 32rpx;
+				color: #333333;
+				margin-top: 24rpx;
+			}
+
+			.task_class_collect {
+				display: inline-flex;
+				align-items: center;
+				flex-wrap: wrap;
+				width: 100%;
+				margin-top: 16rpx;
+				gap: 16rpx;
+				.class_item {
+					font-weight: 400;
+					font-size: 24rpx;
+					color: #999999;
+				}
+			}
+
+			.task_time {
+				display: flex;
+				align-items: flex-start;
+				width: 100%;
+				margin-top: 16rpx;
+
+				.time_day {
+					display: inline-flex;
+					align-items: center;
+					flex-wrap: wrap;
+					flex: 1;
+
+					.time_item {
+						font-weight: 400;
+						font-size: 24rpx;
+						line-height: 48rpx;
+						color: #999999;
+
+						&:nth-child(2) {
+							margin: 0 8rpx;
+						}
+
+						&:nth-child(4) {
+							color: #2E64FA;
+							margin-left: 16rpx;
+						}
+					}
+				}
+
+				.task_status {
+					display: inline-flex;
+					align-items: center;
+					height: 48rpx;
+					padding: 0 12rpx;
+					border-radius: 8rpx;
+					font-weight: 400;
+					font-size: 24rpx;
+
+					&.status_pending {
+						background: rgba(46, 100, 250, 0.1);
+						color: #2E64FA;
+					}
+					
+					&.status_published {
+						background: rgba(82, 196, 26, 0.1);
+						color: #2BC644;
+					}
+					
+					&.status_closed {
+						background: #6666661a;
+						color: #666666;
+					}
+				}
+			}
+
+			.task_desc {
+				margin-top: 24rpx;
+				display: flex;
+				width: 100%;
+				padding-top: 24rpx;
+				border-top: 2rpx solid #F3F3F3;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #999999;
+				line-height: 44rpx;
+				box-sizing: border-box;
+				position: relative;
+
+				&.over {
+					max-height: 168rpx;
+					overflow: hidden;
+					transition: all 0.25s;
+					display: -webkit-box;
+					-webkit-line-clamp: 3;
+					-webkit-box-orient: vertical;
+				}
+			}
+
+			.task_desc_all {
+				display: flex;
+				width: 100%;
+				padding-top: 24rpx;
+				border-top: 2rpx solid #F3F3F3;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #999999;
+				line-height: 44rpx;
+				box-sizing: border-box;
+				position: absolute;
+				left: -9999px;
+				top: 0;
+				visibility: hidden; // 不可见但占据空间(不影响布局,因为absolute)
+				pointer-events: none;
+			}
+
+			.expand_btn {
+				width: 100%;
+				height: 40rpx;
+				background-color: #FFFFFF;
+				display: flex;
+				justify-content: flex-end;
+				align-items: center;
+
+				.expand_text {
+					font-weight: 400;
+					font-size: 28rpx;
+					color: #2E64FA;
+					margin-right: 8rpx;
+				}
+
+				.expand_icon {
+					font-size: 32rpx !important;
+					color: #2E64FA !important;
+				}
+			}
+		}
+	}
+
+	.row_gap {
+		background-color: #F8F9FD;
+		width: 100%;
+		height: 24rpx;
+	}
+
+	.total {
+		width: 100%;
+		display: flex;
+		padding: 24rpx 0;
+		border-bottom: 2rpx solid #F3F3F3;
+		.status_tag{
+			font-size: 24rpx;
+			text-align: center;
+			padding: 8rpx 12rpx;
+			font-weight: 400;
+			border-radius: 8rpx;
+			flex-shrink: 0;
+			&.notSubmitList {
+				background: rgba(46,100,250,0.1);
+				color: #2E64FA;
+			}
+			&.notReviewList {
+				background: rgba(251,159,52,0.1);
+				color: #FB9F34;
+			}
+			&.reviewReasonList {
+				background: rgba(245,108,108,0.1);
+				color: #F56C6C;
+			}
+			&.reviewList {
+				background: rgba(43,198,68,0.1);
+				color: #2BC644;
+			}
+		}
+	}
+
+	.list_item {
+		display: flex;
+		flex-direction: column;
+		width: 100%;
+		padding: 24rpx 0;
+		border-bottom: 2rpx solid #F3F3F3;
+		.item_name_file{
+			display: flex;
+			width: 100%;
+			align-items: center;
+			gap: 20rpx;
+			.name_file{
+				flex:1;
+				gap: 20rpx;
+				display: flex;
+				align-items: center;
+				.name{
+					font-weight: 500;
+					font-size: 32rpx;
+					color: #333333;
+				}
+				.file_num{
+					font-weight: 400;
+					font-size: 24rpx;
+					color: #2E64FA;
+				}
+			}
+			.btn{
+				padding: 12rpx 24rpx;
+				border-radius: 8rpx;
+				border: 2rpx solid #2E64FA;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #2E64FA;
+				display: inline-flex;
+				align-items: center;
+				justify-content: center;
+			}
+		}
+		.item_info{
+			margin-top: 16rpx;
+			display: flex;
+			gap: 16rpx;
+			align-items: center;
+			flex-wrap: wrap;
+			.info_text{
+				font-weight: 400;
+				font-size: 24rpx;
+				color: #999999;
+				flex-shrink: 0;
+			}
+		}
+	}
+</style>

+ 178 - 12
pages/materialCollection/taskOverview/taskDetail.vue

@@ -12,6 +12,9 @@
 						<image src="@/static/image/overview/search.png" class="search_icon"></image>
 					</template>
 				</uni-easyinput>
+				<view class="scroll_select">
+					<uni-data-select v-for="(item,index) in state.selectData" :key="item.prop" v-model="state.selectProp[item.prop]" :clear="false" :placeholder="`请选择`" :localdata="item.list" @change="(val) => ChangeSelectData(val,index,item.list)"></uni-data-select>
+				</view>
 				<scroll-view class="scroll_tabs" scroll-x="true" scroll-left="0" :show-scrollbar="false">
 					<view :class="['tab_item',{'selected':item.value==state.tabsSelected}]" v-for="item in state.tabList"
 						:key="item.value" @click="SelectTabsValue(item.value)">
@@ -48,7 +51,7 @@
 							<text class="time_item">{{state?.details?.startTime}}</text>
 							<text class="time_item">至</text>
 							<text class="time_item">{{state?.details?.endTime}}</text>
-							<text class="time_item" v-if="state.taskStatus!== 2 && remainingDays(state?.details?.endTime) > 0">剩余{{remainingDays(state?.details?.endTime)}}</text>
+							<text class="time_item" v-if="state.taskStatus!== 2 && remainingDays(state?.details?.endTime) > 0">剩余{{remainingDays(state?.details?.endTime)}}</text>
 						</view>
 						<view :class="['task_status', statusClassMap[state.taskStatus]]">
 							{{taskStatusMap[state.taskStatus]}}
@@ -64,6 +67,7 @@
 							class="expand_text">{{!state.isExpand?'展开':'收起'}}</text><uni-icons class="expand_icon"
 							:type="!state.isExpand?'down':'up'"></uni-icons></view>
 				</view>
+				<!-- 间隔 -->
 				<view class="row_gap"></view>
 				<!-- 吸顶 -->
 				<!-- <uv-sticky style="top:98rpx"> -->
@@ -75,6 +79,9 @@
 							<image src="@/static/image/overview/search.png" class="search_icon"></image>
 						</template>
 					</uni-easyinput>
+					<view class="scroll_select">
+						<uni-data-select v-for="(item,index) in state.selectData" :key="item.prop" v-model="state.selectProp[item.prop]" :clear="false" :placeholder="`请选择`" :localdata="item.list" @change="(val) => ChangeSelectData(val,index,item.list)"></uni-data-select>
+					</view>
 					<scroll-view class="scroll_tabs" scroll-x="true" scroll-left="0" :show-scrollbar="false">
 						<view :class="['tab_item',{'selected':item.value==state.tabsSelected}]" v-for="item in state.tabList"
 							:key="item.value" @click="SelectTabsValue(item.value)">
@@ -88,12 +95,20 @@
 						<view class="list_item" v-for="item in state.tabListData" :Key="item.id">
 							<view class="item_name_file">
 								<view class="name_file">
-									<text class="name">刘惟安</text>
-									<text class="file_num">文件数量:2</text>
+									<text class="name">{{item.targetName}}</text>
+									<text class="file_num" v-if="state?.[`${state.tabsSelected}Header`]?.includes('fileNum')">文件数量:{{item?.fileNum || 0}}</text>
 								</view>
-								<view>查看</view>
+								<view class="btn">查看</view>
+							</view>
+							<view class="item_info">
+								<template v-for="prop in state?.[`${state.tabsSelected}Header`]">
+									<text v-if="['targetName','fileNum','reviewBy','reviewTime'].indexOf(prop) == -1" class="info_text">{{item[prop]}}</text>
+								</template>
+							</view>
+							<view class="item_info" v-if="state?.[`${state.tabsSelected}Header`]?.includes('reviewBy')">
+								<text class="info_text">审核人:{{item.reviewBy}}</text>
+								<text class="info_text">{{item.reviewTime}}</text>
 							</view>
-							<view class=""></view>
 						</view>
 						<view class="no_data" v-if="!state.isLoading && state.tabListData.length == 0">
 							<image class="no_data_img" src="@/static/image/bg/no_data.png"></image>
@@ -108,13 +123,15 @@
 
 <script setup>
 	import uniEasyinput from '@/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue';
+	import uniDataSelect from '@/uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue';
 	import uniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue';
 	import uvSticky from '@/uni_modules/uv-sticky/components/uv-sticky/uv-sticky.vue'; //吸顶
 	import navBar from '@/components/navBar.vue';
 	import { remainingDays } from '@/common/common.js';
 	import {
 		GetCollectionTask,
-		getTaskTargetList
+		getTaskTargetList,
+		getTaskSelectCommon
 	} from '@/reqApi/notification.js';
 	import {
 		onLoad
@@ -159,7 +176,7 @@
 		}], //无需审核
 		showExpandBtn: false, //是否显示展开按钮
 		isExpand: false, //是否展开
-		pageSize: 30,
+		pageSize: 50,
 		pageNum: 1,
 		scrollTop: 0,
 		oldScrollTop: 0,
@@ -168,6 +185,8 @@
 		noMoreData: false, //没有更多数据了
 		loadStatus: 'more', //loading状态
 		details: {}, //详情信息
+		selectData:[],//筛选项
+		selectProp:{},//筛选项字段及值
 		notReviewList: [],//待审核
 		notSubmitList: [],//待提交
 		reviewList: [],//已审核
@@ -176,6 +195,10 @@
 		notSubmitPages: 0,//待提交分页
 		reviewPagess: 0,//已审核分页
 		reviewReasonPagess: 0,//已驳回分页
+		notReviewHeader: [],//待审核表头
+		notSubmitHeader: [],//待提交表头
+		reviewHeader: [],//已审核表头
+		reviewReasonHeader: [],//已驳回表头
 		tabListData: [], //列表数据
 		searchBoxTop:0,//搜索框距离顶部的高度
 		isSticky:false,//是否吸顶
@@ -196,6 +219,7 @@
 	})
 	onMounted(() => {
 		QueryCollectionTask();//获取指定的采集任务详情
+		QueryTaskSelectCommon();//获取当前任务的筛选表头和数据表的表头
 		OnLoadData(true,true); // 数据加载完成后触发 checkTextOverflow
 	});
 	//获取指定的采集任务详情
@@ -209,6 +233,74 @@
 			}
 		})
 	}
+	//获取当前任务的筛选表头和数据表的表头
+	const QueryTaskSelectCommon = () => {
+		getTaskSelectCommon(state.taskId).then(res=>{
+			if(res.code == 200){
+				const list = res?.data?.commonSelect || [];
+				const selectList = list.map(item=>({
+					...item,
+					text:item.label,
+					value:item.value ?? ''
+				}))
+				if(list.length > 0){
+					const prop = list?.[0]?.prop || '';
+					const value = list?.[0]?.value ?? '';
+					//筛选项字段及字段对应的值
+					state.selectData.push({
+						prop:prop,
+						list:selectList
+					})
+					state.selectProp[prop] = value;
+				}
+				const notReviewTableHeader = res?.data?.notReviewTableHeader || [];
+				const notSubmitTableHeader = res?.data?.notSubmitTableHeader || [];
+				const tableHeader = res?.data?.tableHeader || [];
+				const tableHeaderProp = tableHeader.filter(item=>item.display).map(item=>item.prop);
+				state.notReviewHeader = notReviewTableHeader.filter(item=>item.display).map(item=>item.prop);//待审核表头
+				state.notSubmitHeader = notSubmitTableHeader.filter(item=>item.display).map(item=>item.prop);//待提交表头
+				state.reviewHeader = tableHeaderProp;//已审核表头
+				state.reviewReasonHeader = tableHeaderProp;//已驳回表头
+			}
+		})
+	}
+	//切换下拉框数据
+	const ChangeSelectData = (val,index,list) => {
+		const childrenData = list.find(item=>item.value === val)?.children || [];
+		if(childrenData.length > 0){
+			let childrenList = childrenData.map(item=>({
+				...item,
+				text:item.label,
+				value:item.value ?? ''
+			}))
+			const titleName = childrenData?.[0]?.titleName || '';
+			const value = childrenData?.[0]?.value ?? '';
+			console.log(childrenData?.[0]?.value,value,1232323,value!=='')
+			const prop = childrenData?.[0]?.prop;
+			if(value!==''){//添加全部
+				childrenList.unshift({
+					text:titleName,
+					value:''
+				})
+			}else{
+				const defaultVal = childrenList?.[0]?.value ?? '';
+				state.selectProp[prop] = defaultVal;
+			}
+			state.selectData[index + 1] = {
+				prop:prop,
+				list:childrenList
+			}
+			
+		}else{
+			state.selectData.splice(index + 1);//删除
+			Object.keys(state.selectProp).forEach((key,i)=>{
+				if(i >= index + 1){
+					delete state.selectProp[key]
+				}
+			})
+		}
+		OnLoadData(true);
+	}
 	const checkTextOverflow = () => {
 		nextTick(() => {
 			const instance = getCurrentInstance();
@@ -281,7 +373,8 @@
 			state.pageNum = 1; //如果是下拉刷新、重新查询 默认加载第一页
 			getTaskTargetList({
 				taskId: state.taskId,
-				queryStr: state.searchWord
+				queryStr: state.searchWord,
+				...state.selectProp
 			}).then(res => {
 				if (res.code == 200) {
 					const data = res?.data ?? {};
@@ -296,7 +389,7 @@
 					const total = state[`${state.tabsSelected}List`]?.length || 0;
 					state[`${state.tabsSelected}Pages`] = Math.ceil(total / state.pageSize);//总页数
 					//默认加载
-					state.tabListData = state[`${state.tabsSelected}List`].slice(0, state.pageSize);
+					state.tabListData = state?.[`${state.tabsSelected}List`]?.slice(0, state.pageSize) || [];
 					//没有更多了
 					state.noMoreData = state.pageNum == state[`${state.tabsSelected}Pages`];
 					//分页+1
@@ -317,7 +410,7 @@
 		}else{
 			const start = (state.pageNum - 1) * state.pageSize;
 			const end = start + state.pageSize;
-			const list = state[`${state.tabsSelected}List`].slice(start, end);
+			const list = state?.[`${state.tabsSelected}List`]?.slice(start, end) || [];
 			state.tabListData = [...state.tabListData, ...list];
 			state.noMoreData = state.pageNum == state[`${state.tabsSelected}Pages`];
 			state.pageNum++;
@@ -564,7 +657,34 @@
 			}
 		}
 	}
-
+	.scroll_select{
+		width: 100%;
+		display: flex;
+		padding: 0 24rpx 24rpx;
+		box-sizing: border-box;
+		gap: 32rpx;
+		:deep(.uni-stat__select){
+			flex: 1;
+			max-width: calc((100% - 32rpx * 2) / 3);
+			min-width: 212rpx;
+			width: auto;
+			.uni-select{
+				height: 72rpx;
+				border-radius: 8rpx;
+				border: 2rpx solid #DCDFE6;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #333333;
+				.uni-select__input-box{
+					height: 72rpx;
+				}
+				.uni-icons{
+					color: #909399;
+					font-size: 24rpx;
+				}
+			}
+		}
+	}
 	.scroll_tabs {
 		margin-top:0;
 		background-color: #FFFFFF;
@@ -586,7 +706,53 @@
 		width: 100%;
 		padding: 24rpx 0;
 		border-bottom: 2rpx solid #F3F3F3;
-		
+		.item_name_file{
+			display: flex;
+			width: 100%;
+			align-items: center;
+			gap: 20rpx;
+			.name_file{
+				flex:1;
+				gap: 20rpx;
+				display: flex;
+				align-items: center;
+				.name{
+					font-weight: 500;
+					font-size: 32rpx;
+					color: #333333;
+				}
+				.file_num{
+					font-weight: 400;
+					font-size: 24rpx;
+					color: #2E64FA;
+				}
+			}
+			.btn{
+				width: 104rpx;
+				height: 64rpx;
+				border-radius: 8rpx;
+				border: 2rpx solid #2E64FA;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #2E64FA;
+				display: inline-flex;
+				align-items: center;
+				justify-content: center;
+			}
+		}
+		.item_info{
+			margin-top: 16rpx;
+			display: flex;
+			gap: 16rpx;
+			align-items: center;
+			flex-wrap: wrap;
+			.info_text{
+				font-weight: 400;
+				font-size: 24rpx;
+				color: #999999;
+				flex-shrink: 0;
+			}
+		}
 	}
 	.no_data{
 		margin-top: 24rpx;

+ 24 - 0
reqApi/notification.js

@@ -51,4 +51,28 @@ export const GetCollectionTask = (data) => {
 // 获取当前采集任务具体采集详情列表
 export const getTaskTargetList = (data) => {
   return request.post('/api/v1/teaching_plan/collect/Task_statistics_info/get_task_target_list',data)
+}
+// 获取当前任务的筛选表头 和数据表的表头
+export const getTaskSelectCommon = (taskId) => {
+  return request.post(`/api/v1/teaching_plan/collect/Task_statistics_info/get_task_select_common?taskId=${taskId}`)
+}
+// 获取指定状态的采集任务列表
+export const getReviewTaskList = (data) => {
+  return request.post('/api/v1/teaching_plan/collect/task_target_review/get_review_task_list',data)
+}
+// 获取当前教师提交的文件列表
+export const getTaskTargetFileList = (targetId) => {
+  return request.post(`/api/v1/teaching_plan/collect/task_target_file/get_task_target_file_list?targetId=${targetId}`)
+}
+// 批量删除OSS文件
+export const DeleteFileList = (data) => {
+  return request.post('/api/v1/teach/oss/file/oss/delete_file_list',data)
+}
+//删除指定的文件
+export const DelTaskTargetFileList = (data) => {
+  return request.post('/api/v1/teaching_plan/collect/task_target_file/del_task_target_file_list', data)
+}
+// 修改教师任务提交状态为待提交
+export const updateTaskTarget = (taskTargetId) => {
+  return request.post(`/api/v1/teaching_plan/collect/task_target/update_task_target?taskTargetId=${taskTargetId}`)
 }

二進制
static/image/icon/upload_icon.png


二進制
static/image/icon/upload_locked.png