Explorar o código

材料采集、校内通知问题修改

吴朋磊 hai 2 semanas
pai
achega
598f3fa7a2

+ 44 - 0
common/safeArea.js

@@ -0,0 +1,44 @@
+import { ref } from 'vue';
+
+export const statusBarHeight = ref(20);
+export const safeAreaBottom = ref(0);
+export const safeAreaTop = ref(20);
+
+export const useSafeArea = () => {
+	const initSafeArea = () => {
+		try {
+			const systemInfo = uni.getSystemInfoSync();
+			statusBarHeight.value = systemInfo.statusBarHeight || 20;
+			
+			const safeAreaInsets = systemInfo.safeAreaInsets || {};
+			safeAreaTop.value = safeAreaInsets.top || statusBarHeight.value;
+			safeAreaBottom.value = safeAreaInsets.bottom || 0;
+		} catch (e) {
+			statusBarHeight.value = 20;
+			safeAreaBottom.value = 0;
+			safeAreaTop.value = 20;
+		}
+	};
+
+	return {
+		statusBarHeight,
+		safeAreaBottom,
+		safeAreaTop,
+		initSafeArea
+	};
+};
+
+export const initSafeArea = () => {
+	try {
+		const systemInfo = uni.getSystemInfoSync();
+		statusBarHeight.value = systemInfo.statusBarHeight || 20;
+		
+		const safeAreaInsets = systemInfo.safeAreaInsets || {};
+		safeAreaTop.value = safeAreaInsets.top || statusBarHeight.value;
+		safeAreaBottom.value = safeAreaInsets.bottom || 0;
+	} catch (e) {
+		statusBarHeight.value = 20;
+		safeAreaBottom.value = 0;
+		safeAreaTop.value = 20;
+	}
+};

+ 10 - 3
components/common-header.vue

@@ -1,5 +1,5 @@
 <template>
-	<view class="common_header">
+	<view class="common_header" :style="{ paddingTop: statusBarHeight + 62 + 'rpx' }">
 		<view class="header_left" @click="handleBack">
 			<uni-icons type="back" size="24" color="#333333"></uni-icons>
 		</view>
@@ -11,6 +11,9 @@
 </template>
 
 <script setup>
+import { onMounted } from 'vue';
+import { useSafeArea } from '@/common/safeArea';
+import uniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue';
 const props = defineProps({
 	title: {
 		type: String,
@@ -19,6 +22,11 @@ const props = defineProps({
 });
 
 const emit = defineEmits(['back']);
+const { statusBarHeight, initSafeArea } = useSafeArea();
+
+onMounted(() => {
+	initSafeArea();
+});
 
 const handleBack = () => {
 	emit('back');
@@ -35,10 +43,9 @@ const handleBack = () => {
 	display: flex;
 	align-items: center;
 	justify-content: space-between;
-	padding-top: env(safe-area-inset-top);
-	height: calc(96rpx + env(safe-area-inset-top));
 	padding-left: 24rpx;
 	padding-right: 24rpx;
+	padding-bottom: 24rpx;
 	background-color: #FFFFFF;
 	border-bottom: 2rpx solid #F3F3F3;
 

+ 42 - 14
components/commonTabbar.vue

@@ -1,7 +1,17 @@
 <template>
-	<view v-if="currentType === 'notice'" class="common_tabbar">
-		<view v-for="(item, idx) in noticeTabs" :key="idx" class="tabbar_item" :class="{ active: currentPath === item.path }" @click="switchTab(item.path)">
-			<image :src="currentPath === item.path ? item.selectedIconPath : item.iconPath" class="tabbar_icon" mode="aspectFit"></image>
+	<view class="common_tabbar">
+		<view 
+			v-for="(item, idx) in currentTabs" 
+			:key="idx" 
+			class="tabbar_item" 
+			:class="{ active: currentPath === item.path }" 
+			@click="switchTab(item.path)"
+		>
+			<image 
+				:src="currentPath === item.path ? item.selectedIconPath : item.iconPath" 
+				class="tabbar_icon" 
+				mode="aspectFit"
+			></image>
 			<text class="tabbar_text">{{ item.text }}</text>
 		</view>
 	</view>
@@ -12,6 +22,27 @@ import { ref, computed, onMounted } from 'vue';
 
 const currentPath = ref('');
 
+const workspaceTabs = [
+	{
+		path: '/pages/workspace/index',
+		iconPath: '/static/image/tabbar/workspace.png',
+		selectedIconPath: '/static/image/tabbar/workspace_cur.png',
+		text: '工作台'
+	},
+	{
+		path: '/pages/todo/index',
+		iconPath: '/static/image/tabbar/todo.png',
+		selectedIconPath: '/static/image/tabbar/todo_cur.png',
+		text: '待办'
+	},
+	{
+		path: '/pages/message/index',
+		iconPath: '/static/image/tabbar/message.png',
+		selectedIconPath: '/static/image/tabbar/message_cur.png',
+		text: '消息'
+	}
+];
+
 const noticeTabs = [
 	{
 		path: '/pages/notice/overview/index',
@@ -33,11 +64,11 @@ const noticeTabs = [
 	}
 ];
 
-const currentType = computed(() => {
+const currentTabs = computed(() => {
 	if (currentPath.value.startsWith('/pages/notice/')) {
-		return 'notice';
+		return noticeTabs;
 	}
-	return 'home';
+	return workspaceTabs;
 });
 
 const updateCurrentPath = () => {
@@ -50,16 +81,14 @@ const updateCurrentPath = () => {
 
 const switchTab = (path) => {
 	if (currentPath.value === path) return;
-	uni.redirectTo({ url: path });
+	uni.reLaunch({ url: path });
 };
 
 onMounted(() => {
 	updateCurrentPath();
-	if (currentType.value === 'notice') {
-		uni.hideTabBar({
-			fail: () => {}
-		});
-	}
+	uni.hideTabBar({
+		fail: () => {}
+	});
 });
 </script>
 
@@ -75,7 +104,6 @@ onMounted(() => {
 	padding: 16rpx 0;
 	padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
 	border-top: 1rpx solid #F0F0F0;
-	height: 84px;
 	box-sizing: border-box;
 	z-index: 999;
 
@@ -103,4 +131,4 @@ onMounted(() => {
 		}
 	}
 }
-</style>
+</style>

+ 17 - 8
components/no-data.vue

@@ -1,17 +1,21 @@
 <template>
-	<view class="no_data">
+	<view class="no_data" :class="{ 'no_data_centered': centered }">
 		<image class="no_data_img" src="@/static/image/bg/no_data.png"></image>
 		<text class="no_data_text">{{text}}</text>
 	</view>
 </template>
 
 <script setup>
-	const props = defineProps({
-		text:{
-			type: String,
-			default: '暂无数据'
-		}
-	})
+const props = defineProps({
+	text:{
+		type: String,
+		default: '暂无数据'
+	},
+	centered:{
+		type: Boolean,
+		default: false
+	}
+})
 </script>
 
 <style scoped lang="scss">
@@ -32,4 +36,9 @@
 			line-height: 36rpx;
 		}
 	}
-</style>
+	.no_data_centered{
+		flex: 1;
+		height: auto;
+		min-height: 0;
+	}
+</style>

+ 156 - 17
components/notice-header.vue

@@ -1,5 +1,5 @@
 <template>
-	<view class="notice_header">
+	<view class="notice_header" :style="{ paddingTop: statusBarHeight + 'rpx' }">
 		<!-- 顶部栏 -->
 		<view class="header_top">
 			<view class="header_left">
@@ -12,16 +12,14 @@
 			</view>
 			<text v-else-if="showSave" class="save_btn" @click="handleSave">保存</text>
 		</view>
-		<!-- 过滤栏 -->
-		<view v-if="showFilter" class="filter_bar">
-			<!-- 通知类型筛选 -->
+		<!-- 通知模式过滤栏 -->
+		<view v-if="showFilter && mode === 'notice'" class="filter_bar">
 			<picker mode="selector" :range="typeOptions" :value="typeIndex" @change="handleTypeChange">
 				<view class="filter_item">
 					<text class="filter_text">{{ typeOptions[typeIndex] }}</text>
 					<uni-icons type="down" size="16" color="#999999"></uni-icons>
 				</view>
 			</picker>
-			<!-- 时间筛选 -->
 			<picker mode="selector" :range="timeOptions" :value="timeIndex" @change="handleTimeChange">
 				<view class="filter_item">
 					<text class="filter_text">{{ timeOptions[timeIndex] }}</text>
@@ -29,10 +27,40 @@
 				</view>
 			</picker>
 		</view>
-		<!-- 标签栏 -->
-		<view v-if="showTabs" class="tabs_bar">
+		<!-- 通知模式标签栏 -->
+		<view v-if="showTabs && mode === 'notice'" class="tabs_bar">
 			<text v-for="(tab, idx) in tabs" :key="idx" class="tab_item" :class="{ active: activeTabIndex === idx }" @click="handleTabChange(idx)">{{ tab }}</text>
 		</view>
+		
+		<!-- 任务总览模式过滤栏 -->
+		<view v-if="showFilter && mode === 'taskOverview'" class="filter_bar task_overview_filter">
+			<picker mode="selector" class="picker_wide" :range="academicYearOptions" :value="academicYearIndex" @change="handleAcademicYearChange">
+				<view class="filter_item">
+					<text class="filter_text">{{ academicYearOptions[academicYearIndex] }}</text>
+					<uni-icons type="down" size="16" color="#999999"></uni-icons>
+				</view>
+			</picker>
+			<picker mode="selector" class="picker_narrow" :range="categoryOptions" :value="categoryIndex" @change="handleCategoryChange">
+				<view class="filter_item">
+					<text class="filter_text">{{ categoryOptions[categoryIndex] }}</text>
+					<uni-icons type="down" size="16" color="#999999"></uni-icons>
+				</view>
+			</picker>
+			<picker mode="selector" class="picker_narrow" :range="materialTypeOptions" :value="materialTypeIndex" @change="handleMaterialTypeChange">
+				<view class="filter_item">
+					<text class="filter_text">{{ materialTypeOptions[materialTypeIndex] }}</text>
+					<uni-icons type="down" size="16" color="#999999"></uni-icons>
+				</view>
+			</picker>
+		</view>
+		<!-- 任务总览模式标签栏 -->
+		<view v-if="(showTabs || showCreateBtn) && mode === 'taskOverview'" class="tabs_bar task_overview_tabs">
+			<text v-for="(tab, idx) in tabs" :key="idx" class="tab_item" :class="{ active: activeTabIndex === idx }" @click="handleTabChange(idx)">{{ tab }}</text>
+			<view v-if="showCreateBtn" class="create_btn" @click="handleCreate">
+				<image src="/static/image/workspace/add.png" class="add_icon" mode="aspectFit"></image>
+				<text class="create_text">新建</text>
+			</view>
+		</view>
 	</view>
 </template>
 
@@ -40,6 +68,9 @@
 import { ref, onMounted, computed } from 'vue';
 import uniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue';
 import overviewApi from '@/reqApi/overview.js';
+import { useSafeArea } from '@/common/safeArea';
+
+const { statusBarHeight, initSafeArea } = useSafeArea();
 
 const typeIndex = ref(0);
 const timeIndex = ref(0);
@@ -47,11 +78,20 @@ const typeData = ref([{ id: '0', name: '全部类型' }]);
 const typeOptions = ref(['全部类型']);
 const searchWord = ref('');
 
+const academicYearIndex = ref(0);
+const categoryIndex = ref(0);
+const materialTypeIndex = ref(0);
+
 const props = defineProps({
 	title: {
 		type: String,
 		default: ''
 	},
+	mode: {
+		type: String,
+		default: 'notice',
+		validator: (value) => ['notice', 'taskOverview'].includes(value)
+	},
 	showSearch: {
 		type: Boolean,
 		default: false
@@ -68,10 +108,26 @@ const props = defineProps({
 		type: Array,
 		default: () => ['全部时间', '最近3天', '最近7天', '最近1月', '最近3月']
 	},
+	academicYearOptions: {
+		type: Array,
+		default: () => ['2025-2026学年', '2024-2025学年', '2023-2024学年']
+	},
+	categoryOptions: {
+		type: Array,
+		default: () => ['全部分类', '日常教学', '考试测评', '教研活动']
+	},
+	materialTypeOptions: {
+		type: Array,
+		default: () => ['全部类目', '教案', '课件', '试卷', '作业']
+	},
 	showTabs: {
 		type: Boolean,
 		default: false
 	},
+	showCreateBtn: {
+		type: Boolean,
+		default: false
+	},
 	tabs: {
 		type: Array,
 		default: () => ['全部', '待发布', '已发布', '已关闭']
@@ -95,7 +151,10 @@ const activeTabIndex = computed(() => {
 	return statusMap.indexOf(props.activeTab);
 });
 
-const emit = defineEmits(['save', 'tabChange', 'typeChange', 'timeChange', 'searchChange']);
+const emit = defineEmits([
+	'save', 'tabChange', 'typeChange', 'timeChange', 'searchChange',
+	'academicYearChange', 'categoryChange', 'materialTypeChange', 'create'
+]);
 
 const fetchNoticeTypeList = async () => {
 	try {
@@ -114,7 +173,10 @@ const fetchNoticeTypeList = async () => {
 };
 
 onMounted(() => {
-	fetchNoticeTypeList();
+	initSafeArea();
+	if (props.mode === 'notice') {
+		fetchNoticeTypeList();
+	}
 });
 
 const handleTabChange = (idx) => {
@@ -137,8 +199,23 @@ const handleTimeChange = (e) => {
 	emit('timeChange', timeIndex.value);
 };
 
+const handleAcademicYearChange = (e) => {
+	academicYearIndex.value = e.detail.value;
+	emit('academicYearChange', academicYearIndex.value);
+};
+
+const handleCategoryChange = (e) => {
+	categoryIndex.value = e.detail.value;
+	emit('categoryChange', categoryIndex.value);
+};
+
+const handleMaterialTypeChange = (e) => {
+	materialTypeIndex.value = e.detail.value;
+	emit('materialTypeChange', materialTypeIndex.value);
+};
+
 const goBack = () => {
-	uni.switchTab({
+	uni.redirectTo({
 		url: '/pages/workspace/index'
 	});
 };
@@ -150,6 +227,10 @@ const handleSave = () => {
 const handleSearchInput = () => {
 	emit('searchChange', searchWord.value);
 };
+
+const handleCreate = () => {
+	emit('create');
+};
 </script>
 
 <style lang="scss" scoped>
@@ -162,8 +243,6 @@ const handleSearchInput = () => {
 	flex-direction: column;
 	background-color: #FFFFFF;
 	z-index: 999;
-	padding-top: env(safe-area-inset-top);
-	// border-bottom: 2rpx solid #EBEEF5;
 
 	.header_top {
 		display: flex;
@@ -232,7 +311,6 @@ const handleSearchInput = () => {
 			background-color: #FFFFFF;
 			border-radius: 8rpx;
 			min-width: 240rpx;
-			// height: 72rpx;
 			border: 2rpx solid #DCDFE6;
 
 			.filter_text {
@@ -243,10 +321,36 @@ const handleSearchInput = () => {
 		}
 	}
 
+	.task_overview_filter {
+		gap: 16rpx;
+
+		.picker_wide {
+			flex: 1.2;
+		}
+
+		.picker_narrow {
+			flex: 1;
+		}
+
+		.filter_item {
+			width: 100%;
+			min-width: 0;
+			padding: 16rpx 12rpx;
+			box-sizing: border-box;
+			.filter_text {
+				font-size: 24rpx;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+			}
+		}
+	}
+
 	.tabs_bar {
 		display: flex;
 		height: 56rpx;
-		padding:4rpx 24rpx 24rpx 24rpx;
+		padding: 4rpx 24rpx 12rpx 24rpx;
+
 		.tab_item {
 			font-size: 32rpx;
 			color: #999999;
@@ -264,13 +368,48 @@ const handleSearchInput = () => {
 					bottom: 0rpx;
 					left: 50%;
 					transform: translateX(-50%);
-					width: 48rpx;
+					width: 100%;
 					height: 4rpx;
 					background-color: #2E64FA;
-					// border-radius: 2rpx;
 				}
 			}
 		}
 	}
+
+	.task_overview_tabs {
+		justify-content: flex-start;
+		align-items: center;
+		height: 72rpx;
+		.tab_item {
+			margin-right: 48rpx;
+			&.active {
+
+				&::after {
+					bottom: -8rpx;
+				}
+			}
+		}
+
+		.create_btn {
+			margin-left: auto;
+			background-color: #2E64FA;
+			border-radius: 8rpx;
+			width: 148rpx;
+			height: 72rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			gap: 8rpx;
+			.add_icon {
+				width: 32rpx;
+				height: 32rpx;
+			}
+			.create_text {
+				color: #FFFFFF;
+				font-size: 28rpx;
+				font-weight: 500;
+			}
+		}
+	}
 }
-</style>
+</style>

+ 10 - 1
components/tree-node.vue

@@ -8,7 +8,10 @@
 						<image v-else-if="item.checked && (!item.children || item.children.length === 0)" src="/static/image/publish/correct.png" class="checkbox_check_image" mode="aspectFit"></image>
 					</view>
 				</view>
-				<text class="row_text" :class="{ 'leaf-node': !item.children || item.children.length === 0 }">{{ item.name }}</text>
+				<text class="row_text" :class="{ 'leaf-node': !item.children || item.children.length === 0 }">
+					{{ item.name }}
+					<text v-if="item.userAccount" class="user-account">({{ item.userAccount }})</text>
+				</text>
 				<view v-if="item.children && item.children.length > 0" class="expand_icon">
 					<image src="/static/image/icon/xiala.png" class="expand-arrow" :class="{ expanded: expandedIds.includes(item.id) }" mode="aspectFit"></image>
 				</view>
@@ -126,6 +129,12 @@ export default {
 			font-size: 28rpx;
 			color: #333333;
 
+			.user-account {
+				font-size: 24rpx;
+				color: #999999;
+				margin-left: 8rpx;
+			}
+
 			&.leaf-node {
 				color: #666666;
 			}

+ 22 - 31
pages.json

@@ -182,6 +182,27 @@
 				"navigationBarTitleText": "教师发展-新建/修改荣誉类型",
 				"navigationStyle": "custom"
 			}
+		},
+		{
+			"path": "pages/materialCollection/taskOverview/index",
+			"style": {
+				"navigationBarTitleText": "任务总览",
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/materialCollection/submitMaterial/index",
+			"style": {
+				"navigationBarTitleText": "提交材料",
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/materialCollection/reviewMaterial/index",
+			"style": {
+				"navigationBarTitleText": "审核材料",
+				"navigationStyle": "custom"
+			}
 		}
 	],
 	"easycom": {
@@ -196,35 +217,5 @@
 		"navigationBarBackgroundColor": "#F8F8F8",
 		"backgroundColor": "#F8F8F8"
 	},
-	"uniIdRouter": {},
-	"tabBar": {
-		"color": "#7A7E83",
-		"selectedColor": "#2E64FA",
-		"borderStyle": "black",
-		"fontSize": "12px",
-		"backgroundColor": "#FFFFFF",
-		"height": "136rpx",
-		"list": [{
-				"pagePath": "pages/workspace/index",
-				"iconPath": "static/image/tabbar/workspace.png",
-				"selectedIconPath": "static/image/tabbar/workspace_cur.png",
-				"text": "工作台"
-			}, {
-				"pagePath": "pages/todo/index",
-				"iconPath": "static/image/tabbar/todo.png",
-				"selectedIconPath": "static/image/tabbar/todo_cur.png",
-				"text": "待办"
-			}, {
-				"pagePath": "pages/message/index",
-				"iconPath": "static/image/tabbar/message.png",
-				"selectedIconPath": "static/image/tabbar/message_cur.png",
-				"text": "消息"
-			}, {
-				"pagePath": "pages/my/index",
-				"iconPath": "static/image/tabbar/my.png",
-				"selectedIconPath": "static/image/tabbar/my_cur.png",
-				"text": "我的"
-			}
-		]
-	}
+	"uniIdRouter": {}
 }

+ 189 - 0
pages/materialCollection/reviewMaterial/index.vue

@@ -0,0 +1,189 @@
+<template>
+	<view class="page_review" :style="{paddingTop: statusBarHeight + 120 + 'rpx', paddingBottom: safeAreaBottom + 100 + 'rpx' }">
+		<notice-header title="审核材料" mode="review">
+		</notice-header>
+
+		<view class="review_content">
+			<view class="review_list">
+				<scroll-view v-if="reviewList.length > 0" scroll-y>
+					<view v-for="item in reviewList" :key="item.id" class="review_item">
+						<view class="item_header">
+							<text class="submitter_name">{{ item.submitter }}</text>
+							<view class="review_status" :class="item.statusClass">
+								<text>{{ item.status }}</text>
+							</view>
+						</view>
+						<view class="item_info">
+							<text class="info_text">提交时间: {{ item.submitTime }}</text>
+							<text class="info_text">材料: {{ item.materialCount }}个</text>
+						</view>
+						<view class="item_actions">
+							<view class="action_btn reject" @click="handleReject(item)">
+								<text>驳回</text>
+							</view>
+							<view class="action_btn approve" @click="handleApprove(item)">
+								<text>通过</text>
+							</view>
+						</view>
+					</view>
+				</scroll-view>
+
+				<no-data v-else centered text="暂无待审核材料" />
+			</view>
+		</view>
+	</view>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue';
+import NoticeHeader from '@/components/notice-header.vue';
+import NoData from '@/components/no-data.vue';
+import { useSafeArea } from '@/common/safeArea';
+
+const { statusBarHeight, safeAreaBottom, initSafeArea } = useSafeArea();
+
+const reviewList = ref([
+	{
+		id: 1,
+		submitter: '张老师',
+		status: '待审核',
+		statusClass: 'status-pending',
+		submitTime: '2025-07-28 10:30',
+		materialCount: 5
+	},
+	{
+		id: 2,
+		submitter: '李老师',
+		status: '待审核',
+		statusClass: 'status-pending',
+		submitTime: '2025-07-28 11:20',
+		materialCount: 3
+	},
+	{
+		id: 3,
+		submitter: '王老师',
+		status: '待审核',
+		statusClass: 'status-pending',
+		submitTime: '2025-07-27 16:45',
+		materialCount: 8
+	}
+]);
+
+onMounted(() => {
+	initSafeArea();
+});
+
+const handleApprove = (item) => {
+	uni.showModal({
+		title: '确认通过',
+		content: `确定要通过 ${item.submitter} 提交的材料吗?`,
+		success: (res) => {
+			if (res.confirm) {
+				uni.showToast({ title: '审核通过', icon: 'success' });
+				reviewList.value = reviewList.value.filter(i => i.id !== item.id);
+			}
+		}
+	});
+};
+
+const handleReject = (item) => {
+	uni.showModal({
+		title: '确认驳回',
+		content: `确定要驳回 ${item.submitter} 提交的材料吗?`,
+		success: (res) => {
+			if (res.confirm) {
+				uni.showToast({ title: '已驳回', icon: 'none' });
+				reviewList.value = reviewList.value.filter(i => i.id !== item.id);
+			}
+		}
+	});
+};
+</script>
+
+<style lang="scss" scoped>
+.page_review {
+	min-height: 100vh;
+	background-color: #F5F7FA;
+}
+
+.review_content {
+	flex: 1;
+	padding: 24rpx;
+}
+
+.review_list {
+	background-color: #FFFFFF;
+	border-radius: 16rpx;
+	overflow: hidden;
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+}
+
+.review_item {
+	padding: 24rpx;
+	border-bottom: 1rpx solid #EBEEF5;
+
+	&:last-child {
+		border-bottom: none;
+	}
+
+	.item_header {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		margin-bottom: 16rpx;
+
+		.submitter_name {
+			font-size: 30rpx;
+			font-weight: 500;
+			color: #333333;
+		}
+
+		.review_status {
+			font-size: 24rpx;
+			padding: 8rpx 16rpx;
+			border-radius: 8rpx;
+
+			&.status-pending {
+				background: rgba(230, 162, 60, 0.1);
+				color: #E6A23C;
+			}
+		}
+	}
+
+	.item_info {
+		display: flex;
+		gap: 32rpx;
+		margin-bottom: 20rpx;
+
+		.info_text {
+			font-size: 26rpx;
+			color: #999999;
+		}
+	}
+
+	.item_actions {
+		display: flex;
+		justify-content: flex-end;
+		gap: 24rpx;
+
+		.action_btn {
+			font-size: 28rpx;
+			padding: 16rpx 32rpx;
+			border-radius: 12rpx;
+
+			&.reject {
+				background-color: #FFFFFF;
+				border: 2rpx solid #E4E7ED;
+				color: #666666;
+			}
+
+			&.approve {
+				background-color: #2E64FA;
+				color: #FFFFFF;
+			}
+		}
+	}
+}
+</style>

+ 260 - 0
pages/materialCollection/submitMaterial/index.vue

@@ -0,0 +1,260 @@
+<template>
+	<view class="page_submit" :style="{paddingTop: statusBarHeight + 200 + 'rpx', paddingBottom: safeAreaBottom + 100 + 'rpx' }">
+		<notice-header 
+			:title="isEdit ? '编辑任务' : '新建任务'" 
+			mode="submit"
+			:showSave="true"
+			@save="handleSave">
+		</notice-header>
+
+		<view class="form_container">
+			<scroll-view scroll-y class="form_scroll">
+				<view class="form_section">
+					<view class="section_title">基本信息</view>
+					
+					<view class="form_item">
+						<text class="form_label">任务标题</text>
+						<input class="form_input" v-model="formData.title" placeholder="请输入任务标题" />
+					</view>
+
+					<view class="form_item">
+						<text class="form_label">学年</text>
+						<picker mode="selector" :range="academicYearOptions" :value="academicYearIndex" @change="handleAcademicYearChange">
+							<view class="picker_item">
+								<text>{{ academicYearOptions[academicYearIndex] }}</text>
+								<uni-icons type="down" size="16" color="#999999"></uni-icons>
+							</view>
+						</picker>
+					</view>
+
+					<view class="form_item">
+						<text class="form_label">归属分类</text>
+						<picker mode="selector" :range="categoryOptions" :value="categoryIndex" @change="handleCategoryChange">
+							<view class="picker_item">
+								<text>{{ categoryOptions[categoryIndex] }}</text>
+								<uni-icons type="down" size="16" color="#999999"></uni-icons>
+							</view>
+						</picker>
+					</view>
+
+					<view class="form_item">
+						<text class="form_label">材料类目</text>
+						<picker mode="selector" :range="materialTypeOptions" :value="materialTypeIndex" @change="handleMaterialTypeChange">
+							<view class="picker_item">
+								<text>{{ materialTypeOptions[materialTypeIndex] }}</text>
+								<uni-icons type="down" size="16" color="#999999"></uni-icons>
+							</view>
+						</picker>
+					</view>
+				</view>
+
+				<view class="form_section">
+					<view class="section_title">时间设置</view>
+					
+					<view class="form_item">
+						<text class="form_label">开始日期</text>
+						<picker mode="date" :value="formData.startDate" @change="handleStartDateChange">
+							<view class="picker_item">
+								<text>{{ formData.startDate || '请选择开始日期' }}</text>
+								<uni-icons type="down" size="16" color="#999999"></uni-icons>
+							</view>
+						</picker>
+					</view>
+
+					<view class="form_item">
+						<text class="form_label">截止日期</text>
+						<picker mode="date" :value="formData.deadline" @change="handleDeadlineChange">
+							<view class="picker_item">
+								<text>{{ formData.deadline || '请选择截止日期' }}</text>
+								<uni-icons type="down" size="16" color="#999999"></uni-icons>
+							</view>
+						</picker>
+					</view>
+				</view>
+
+				<view class="form_section">
+					<view class="section_title">任务描述</view>
+					<textarea 
+						class="form_textarea" 
+						v-model="formData.description" 
+						placeholder="请输入任务描述" 
+						placeholder-class="textarea_placeholder"
+						maxlength="500"
+					></textarea>
+				</view>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue';
+import { onLoad } from '@dcloudio/uni-app';
+import NoticeHeader from '@/components/notice-header.vue';
+import uniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue';
+import { useSafeArea } from '@/common/safeArea';
+
+const { statusBarHeight, safeAreaBottom, initSafeArea } = useSafeArea();
+
+const taskId = ref(null);
+const isEdit = computed(() => !!taskId.value);
+
+const academicYearOptions = ['2025-2026学年', '2024-2025学年', '2023-2024学年'];
+const categoryOptions = ['全部分类', '日常教学', '考试测评', '教研活动'];
+const materialTypeOptions = ['全部类目', '教案', '课件', '试卷', '作业'];
+
+const academicYearIndex = ref(0);
+const categoryIndex = ref(0);
+const materialTypeIndex = ref(0);
+
+const formData = ref({
+	title: '',
+	startDate: '',
+	deadline: '',
+	description: ''
+});
+
+onLoad((options) => {
+	if (options && options.id) {
+		taskId.value = options.id;
+		loadTaskDetail(options.id);
+	}
+});
+
+onMounted(() => {
+	initSafeArea();
+});
+
+const loadTaskDetail = (id) => {
+	// 模拟加载任务详情
+	formData.value = {
+		title: '2025年秋季学期教案材料收集',
+		startDate: '2025-07-28',
+		deadline: '2025-08-15',
+		description: '请各位教师按时提交本学期的教案材料'
+	};
+};
+
+const handleAcademicYearChange = (e) => {
+	academicYearIndex.value = e.detail.value;
+};
+
+const handleCategoryChange = (e) => {
+	categoryIndex.value = e.detail.value;
+};
+
+const handleMaterialTypeChange = (e) => {
+	materialTypeIndex.value = e.detail.value;
+};
+
+const handleStartDateChange = (e) => {
+	formData.value.startDate = e.detail.value;
+};
+
+const handleDeadlineChange = (e) => {
+	formData.value.deadline = e.detail.value;
+};
+
+const handleSave = () => {
+	if (!formData.value.title) {
+		uni.showToast({ title: '请输入任务标题', icon: 'none' });
+		return;
+	}
+	
+	uni.showModal({
+		title: '提示',
+		content: isEdit.value ? '确定要保存修改吗?' : '确定要创建任务吗?',
+		success: (res) => {
+			if (res.confirm) {
+				uni.showToast({ 
+					title: isEdit.value ? '保存成功' : '创建成功', 
+					icon: 'success' 
+				});
+				setTimeout(() => {
+					uni.navigateBack();
+				}, 1500);
+			}
+		}
+	});
+};
+</script>
+
+<style lang="scss" scoped>
+.page_submit {
+	min-height: 100vh;
+	background-color: #F5F7FA;
+}
+
+.form_container {
+	flex: 1;
+	padding: 24rpx;
+}
+
+.form_scroll {
+	height: 100%;
+}
+
+.form_section {
+	background-color: #FFFFFF;
+	border-radius: 16rpx;
+	padding: 24rpx;
+	margin-bottom: 24rpx;
+
+	.section_title {
+		font-size: 30rpx;
+		font-weight: 600;
+		color: #333333;
+		margin-bottom: 24rpx;
+	}
+}
+
+.form_item {
+	display: flex;
+	align-items: center;
+	padding: 24rpx 0;
+	border-bottom: 1rpx solid #EBEEF5;
+
+	&:last-child {
+		border-bottom: none;
+	}
+
+	.form_label {
+		width: 180rpx;
+		font-size: 28rpx;
+		color: #666666;
+	}
+
+	.form_input {
+		flex: 1;
+		font-size: 28rpx;
+		color: #333333;
+		text-align: right;
+	}
+
+	.picker_item {
+		flex: 1;
+		display: flex;
+		align-items: center;
+		justify-content: flex-end;
+		font-size: 28rpx;
+		color: #333333;
+		gap: 8rpx;
+	}
+}
+
+.form_textarea {
+	width: 100%;
+	min-height: 200rpx;
+	font-size: 28rpx;
+	color: #333333;
+	padding: 16rpx;
+	background-color: #F9FAFF;
+	border-radius: 12rpx;
+	box-sizing: border-box;
+}
+
+.textarea_placeholder {
+	color: #999999;
+	font-size: 28rpx;
+}
+</style>

+ 349 - 0
pages/materialCollection/taskOverview/index.vue

@@ -0,0 +1,349 @@
+<template>
+	<view class="page_notice" :style="{paddingTop: statusBarHeight + 310 + 'rpx', paddingBottom: safeAreaBottom + 160 + 'rpx' }">
+		<notice-header 
+			title="任务总览" 
+			mode="taskOverview"
+			:showSearch="true" 
+			:showFilter="true" 
+			:showTabs="true" 
+			:showCreateBtn="true"
+			:tabs="tabs"
+			:activeTab="activeTab" 
+			@tabChange="handleTabChange" 
+			@academicYearChange="handleAcademicYearChange"
+			@categoryChange="handleCategoryChange"
+			@materialTypeChange="handleMaterialTypeChange"
+			@searchChange="handleSearchChange"
+			@create="handleCreate">
+		</notice-header>
+
+		<view class="notice_content">
+			<view class="notice_list">
+				<scroll-view v-if="taskList.length > 0" scroll-y>
+					<view v-for="item in taskList" :key="item.id" class="notice_item">
+						<view class="item_content">
+							<view class="notice_title_row">
+								<image class="notice_icon" :src="item.icon" mode="aspectFit"></image>
+								<text class="notice_title">{{ item.title }}</text>
+							</view>
+							<view class="notice_meta">
+								<view class="status_tag" :class="item.statusClass">
+									<text>{{ item.status }}</text>
+								</view>
+								<text class="meta_text textLength">{{ item.academicYear }}</text>
+								<text class="meta_text textLength">{{ item.category }}</text>
+								<text class="meta_text">{{ item.time }}</text>
+							</view>
+						</view>
+						<view class="item_bottom">
+							<view class="notice_stats">
+								<text class="stats_text">提交人: {{ item.submitter }}</text>
+								<text class="stats_text">截止: {{ item.deadline }}</text>
+							</view>
+							<view class="notice_actions">
+								<view class="action_btn detail" @click="handleDetail(item.id)">
+									<text>详情</text>
+								</view>
+							</view>
+						</view>
+					</view>
+				</scroll-view>
+
+				<no-data v-else centered text="暂无任务" />
+			</view>
+		</view>
+
+		<common-tabbar></common-tabbar>
+	</view>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue';
+import NoticeHeader from '@/components/notice-header.vue';
+import CommonTabbar from '@/components/commonTabbar.vue';
+import NoData from '@/components/no-data.vue';
+import { useSafeArea } from '@/common/safeArea';
+
+const { statusBarHeight, safeAreaBottom, initSafeArea } = useSafeArea();
+
+const tabs = ['全部', '待开始', '进行中', '已结束'];
+const activeTab = ref(-1);
+const academicYearIndex = ref(0);
+const categoryIndex = ref(0);
+const materialTypeIndex = ref(0);
+const word = ref('');
+const taskList = ref([]);
+
+const statusMap = {
+	0: '待开始',
+	1: '进行中',
+	2: '已结束'
+};
+
+const statusClassMap = {
+	0: 'status-pending',
+	1: 'status-progress',
+	2: 'status-ended'
+};
+
+const mockTaskList = [
+	{
+		id: 1,
+		title: '2025年秋季学期教案材料收集',
+		status: '待开始',
+		statusClass: 'status-pending',
+		academicYear: '2025-2026学年',
+		category: '日常教学',
+		time: '2025-07-28',
+		submitter: '张老师',
+		deadline: '2025-08-15',
+		icon: '/static/image/workspace/icon2.png'
+	},
+	{
+		id: 2,
+		title: '期中考试试卷提交任务',
+		status: '进行中',
+		statusClass: 'status-progress',
+		academicYear: '2025-2026学年',
+		category: '考试测评',
+		time: '2025-07-25',
+		submitter: '李老师',
+		deadline: '2025-08-30',
+		icon: '/static/image/workspace/icon1.png'
+	},
+	{
+		id: 3,
+		title: '课件资源库更新任务',
+		status: '进行中',
+		statusClass: 'status-progress',
+		academicYear: '2025-2026学年',
+		category: '日常教学',
+		time: '2025-07-20',
+		submitter: '王老师',
+		deadline: '2025-09-01',
+		icon: '/static/image/workspace/icon3.png'
+	},
+	{
+		id: 4,
+		title: '教研活动材料归档',
+		status: '已结束',
+		statusClass: 'status-ended',
+		academicYear: '2024-2025学年',
+		category: '教研活动',
+		time: '2025-06-15',
+		submitter: '赵老师',
+		deadline: '2025-07-01',
+		icon: '/static/image/workspace/icon4.png'
+	}
+];
+
+const fetchTaskList = () => {
+	let filteredList = [...mockTaskList];
+	
+	if (activeTab.value >= 0) {
+		filteredList = filteredList.filter(item => {
+			const statusIndex = Object.keys(statusMap).findIndex(key => statusMap[key] === item.status);
+			return statusIndex === activeTab.value;
+		});
+	}
+	
+	if (word.value) {
+		filteredList = filteredList.filter(item => 
+			item.title.includes(word.value)
+		);
+	}
+	
+	taskList.value = filteredList;
+};
+
+onMounted(() => {
+	initSafeArea();
+	fetchTaskList();
+});
+
+const handleTabChange = (status) => {
+	activeTab.value = status;
+	fetchTaskList();
+};
+
+const handleAcademicYearChange = (index) => {
+	academicYearIndex.value = index;
+	fetchTaskList();
+};
+
+const handleCategoryChange = (index) => {
+	categoryIndex.value = index;
+	fetchTaskList();
+};
+
+const handleMaterialTypeChange = (index) => {
+	materialTypeIndex.value = index;
+	fetchTaskList();
+};
+
+const handleSearchChange = (keyword) => {
+	word.value = keyword;
+	fetchTaskList();
+};
+
+const handleCreate = () => {
+	uni.navigateTo({
+		url: '/pages/materialCollection/submitMaterial/index'
+	});
+};
+
+const handleDetail = (id) => {
+	uni.navigateTo({
+		url: `/pages/materialCollection/submitMaterial/index?id=${id}`
+	});
+};
+</script>
+
+<style lang="scss">
+.page_notice {
+	min-height: 100vh;
+	display: flex;
+	flex-direction: column;
+	background-color: #FFFFFF;
+	box-sizing: border-box;
+}
+
+.notice_content {
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+	overflow: hidden;
+	padding-left: 0rpx;
+	padding-right: 0rpx;
+}
+
+.notice_list {
+	flex: 1;
+	overflow-y: auto;
+	overflow-x: hidden;
+	padding: 0 24rpx;
+	display: flex;
+	flex-direction: column;
+}
+
+.notice_item {
+	background-color: #F9FAFF;
+	border-radius: 20rpx;
+	margin-bottom: 24rpx;
+	border: 2rpx solid #E4E7ED;
+
+	.item_content {
+		padding: 24rpx;
+
+		.notice_title_row {
+			display: flex;
+			align-items: center;
+			margin-bottom: 16rpx;
+
+			.notice_icon {
+				width: 32rpx;
+				height: 32rpx;
+				margin-right: 16rpx;
+			}
+
+			.notice_title {
+				flex: 1;
+				font-weight: 500;
+				font-size: 28rpx;
+				color: #333333;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+			}
+		}
+
+		.notice_meta {
+			display: flex;
+			align-items: center;
+			flex-wrap: wrap;
+			gap: 16rpx;
+
+			.status_tag {
+				font-size: 24rpx;
+				height: 48rpx;
+				width: 96rpx;
+				text-align: center;
+				line-height: 48rpx;
+				border-radius: 8rpx;
+				font-weight: 400;
+
+				&.status-pending {
+					background: rgba(46, 100, 250, 0.1);
+					color: #2E64FA;
+				}
+
+				&.status-progress {
+					background: rgba(230, 162, 60, 0.1);
+					color: #E6A23C;
+				}
+
+				&.status-ended {
+					background: rgba(144, 147, 153, 0.1);
+					color: #909399;
+				}
+			}
+
+			.meta_text {
+				font-size: 24rpx;
+				color: #999999;
+				line-height: 48rpx;
+			}
+			.textLength {
+				max-width: 160rpx;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+			}
+		}
+	}
+
+	.item_bottom {
+		border-top: 2rpx solid #E4E7ED;
+		display: flex;
+		flex-direction: row;
+		justify-content: space-between;
+		align-items: center;
+		padding: 24rpx 16rpx;
+
+		.notice_stats {
+			display: flex;
+
+			.stats_text {
+				font-size: 24rpx;
+				color: #999999;
+
+				&:first-child {
+					margin-right: 32rpx;
+				}
+			}
+		}
+
+		.notice_actions {
+			display: flex;
+			justify-content: flex-end;
+			gap: 16rpx;
+
+			.action_btn {
+				font-size: 24rpx;
+				padding: 12rpx 24rpx;
+				border-radius: 8rpx;
+				border: 2rpx solid transparent;
+
+				&.detail {
+					background-color: #2E64FA;
+					color: #FFFFFF;
+				}
+			}
+		}
+	}
+
+	&:last-child {
+		margin-bottom: 0;
+	}
+}
+</style>

+ 4 - 25
pages/message/index.vue

@@ -26,10 +26,7 @@
 					</view>
 				</view>
 			</view>
-			<view v-if="messageList.length === 0" class="schedule_empty">
-				<image src="@/static/image/bg/no_data.png" class="empty_image" mode="aspectFit"></image>
-				<text class="empty_text">暂无系统消息</text>
-			</view>
+			<no-data v-if="messageList.length === 0" centered text="暂无系统消息" />
 		</view>
 		<common-tabbar></common-tabbar>
 	</view>
@@ -40,6 +37,7 @@
 	import { onShow } from '@dcloudio/uni-app';
 	import uniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue';
 	import PageHeader from '@/components/page-header.vue';
+	import NoData from '@/components/no-data.vue';
 	import workspace from '@/reqApi/workspace.js';
 
 	const messageList = ref([]);
@@ -160,6 +158,8 @@
 	flex: 1;
 	overflow-y: auto;
 	padding:0 24rpx;
+	display: flex;
+	flex-direction: column;
 	.message_item {
 		display: flex;
 		align-items: center;
@@ -260,25 +260,4 @@
 		}
 	}
 }
-
-.schedule_empty {
-	display: flex;
-	flex-direction: column;
-	align-items: center;
-	font-size: 28rpx;
-	color: #999999;
-	position: relative;
-
-	.empty_image {
-		width: 400rpx;
-		height: 400rpx;
-	}
-
-	.empty_text {
-		position: absolute;
-		bottom: 15%;
-		left: 50%;
-		transform: translate(-50%, -50%);
-	}
-}
 </style>

+ 3 - 1
pages/notice/mine/index.vue

@@ -25,7 +25,7 @@
 					</view>
 				</scroll-view>
 
-				<no-data v-if="noticeList.length === 0" text="暂无通知" />
+				<no-data v-if="noticeList.length === 0" centered text="暂无通知" />
 			</view>
 		</view>
 
@@ -174,6 +174,8 @@ const handleDetail = (id) => {
 	overflow-y: auto;
 	overflow-x: hidden;
 	padding: 0 24rpx;
+	display: flex;
+	flex-direction: column;
 }
 
 .notice_item {

+ 3 - 1
pages/notice/overview/index.vue

@@ -43,7 +43,7 @@
 
 				</scroll-view>
 
-				<no-data v-if="noticeList.length === 0" text="暂无通知" />
+				<no-data v-if="noticeList.length === 0" centered text="暂无通知" />
 			</view>
 		</view>
 
@@ -222,6 +222,8 @@ const handleDetail = (id) => {
 	overflow-y: auto;
 	overflow-x: hidden;
 	padding: 0 24rpx;
+	display: flex;
+	flex-direction: column;
 }
 
 .notice_item {

+ 9 - 2
pages/notice/overview/noticeDetail.vue

@@ -1,6 +1,6 @@
 <template>
 	<view class="notice_detail">
-		<view class="detail_header">
+		<view class="detail_header" :style="{ paddingTop: statusBarHeight + 'px' }">
 			<uni-icons type="back" size="24" color="#333333" @click="handleBack"></uni-icons>
 			<text class="header_title">通知详情</text>
 			<text class="header_placeholder"></text>
@@ -68,7 +68,7 @@
 						</view>
 					</view>
 
-					<no-data v-if="filteredReaders.length === 0" text="暂无数据" />
+					<no-data v-if="filteredReaders.length === 0" centered text="暂无数据" />
 				</scroll-view>
 			</view>
 		</view>
@@ -80,6 +80,9 @@ import { ref, computed, onMounted } from 'vue';
 import uniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue';
 import overviewApi from '@/reqApi/overview.js';
 import NoData from '@/components/no-data.vue';
+import { useSafeArea } from '@/common/safeArea';
+
+const { statusBarHeight, initSafeArea } = useSafeArea();
 
 const tabs = ['全部', '已读', '未读'];
 const activeTab = ref(0);
@@ -168,6 +171,7 @@ const handleBack = () => {
 };
 
 onMounted(() => {
+	initSafeArea();
 	const pages = getCurrentPages();
 	const currentPage = pages[pages.length - 1];
 	const itemStr = currentPage.options?.item;
@@ -208,6 +212,7 @@ onMounted(() => {
 		padding: 24rpx;
 		background-color: #FFFFFF;
 		border-bottom: 2rpx solid #F3F3F3;
+		flex-shrink: 0;
 
 		.header_title {
 			font-size: 32rpx;
@@ -380,6 +385,8 @@ onMounted(() => {
 		.reader_list {
 			flex: 1;
 			overflow-y: auto;
+			display: flex;
+			flex-direction: column;
 			::-webkit-scrollbar {
 				display: none;
 			}

+ 2 - 25
pages/todo/index.vue

@@ -37,10 +37,7 @@
 					</view>
 				</view>
 			</view>
-			<view v-if="todoList.length === 0" class="schedule_empty">
-				<image src="@/static/image/bg/no_data.png" class="empty_image" mode="aspectFit"></image>
-				<text class="empty_text">暂无待办事项</text>
-			</view>
+			<no-data v-if="todoList.length === 0" centered text="暂无待办事项" />
 		</view>
 		<common-tabbar></common-tabbar>
 	</view>
@@ -51,6 +48,7 @@
 	import { onShow } from '@dcloudio/uni-app';
 	import uniIcons from '@/uni_modules/uni-icons/components/uni-icons/uni-icons.vue';
 	import PageHeader from '@/components/page-header.vue';
+	import NoData from '@/components/no-data.vue';
 	import workspace from '@/reqApi/workspace.js';
 
 	const todoList = ref([]);
@@ -222,25 +220,4 @@
 		}
 	}
 }
-
-.schedule_empty {
-	display: flex;
-	flex-direction: column;
-	align-items: center;
-	font-size: 28rpx;
-	color: #999999;
-	position: relative;
-
-	.empty_image {
-		width: 400rpx;
-		height: 400rpx;
-	}
-
-	.empty_text {
-		position: absolute;
-		bottom: 15%;
-		left: 50%;
-		transform: translate(-50%, -50%);
-	}
-}
 </style>

+ 10 - 3
pages/workspace/index.vue

@@ -104,7 +104,7 @@
 				<!-- 日程列表 -->
 				<view class="schedule_list">
 					<view class="schedule_title">当日日程</view>
-					<no-data v-if="scheduleList.length === 0" text="暂无日程安排" />
+					<no-data v-if="scheduleList.length === 0" centered text="暂无日程安排" />
 					<view 
 						v-else 
 						v-for="item in scheduleList" 
@@ -174,7 +174,7 @@ const schoolName = ref('');
 const systemItems = ref([
 	{ name: '大数据精准教学诊断平台', key:'', image: '/static/image/workspace/iconLeft.png', isLarge: true, visible: true },
 	{ name: '选择题判分', key:'', image: '/static/image/workspace/icon1.png', isLarge: false, visible: true },
-	{ name: '材料采集', key:'', image: '/static/image/workspace/icon2.png', isLarge: false, visible: true },
+	{ name: '材料采集', key:'materialCollection', image: '/static/image/workspace/icon2.png', isLarge: false, visible: true },
 	{ name: '教师研修', key:'teacherStudy', image: '/static/image/workspace/icon3.png', isLarge: false, visible: true },
 	{ name: '学生学习', key:'studentStudy', image: '/static/image/workspace/icon4.png', isLarge: false, visible: true },
 	{ name: '智能选课', key:'', image: '/static/image/workspace/icon5.png', isLarge: false, visible: true },
@@ -350,7 +350,11 @@ const handleSystemClick = (item) => {
 				url: '/pages/notice/mine/index?hideTabbar=true'
 			});
 		}
-	}else if(item.key == 'teacherStudy' || item.key == 'studentStudy' || item.key == 'teacherHonor'){//教师研修 学生学习 教师发展
+	} else if(item.key === 'materialCollection'){
+		uni.navigateTo({
+			url: '/pages/materialCollection/taskOverview/index'
+		});
+	} else if(item.key == 'teacherStudy' || item.key == 'studentStudy' || item.key == 'teacherHonor'){//教师研修 学生学习 教师发展
 		uni.navigateTo({
 			url: `/pages/${item.key}/index`
 		});
@@ -804,6 +808,9 @@ $shadow-sm: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
 		background: #F9FAFF;
 		border-radius: 20rpx 20rpx 20rpx 20rpx;
 		padding: $spacing-lg;
+		display: flex;
+		flex-direction: column;
+		flex: 1;
 		.schedule_title {
 			font-size: 28rpx;
 			font-weight: 500;

+ 0 - 16
uni_modules/uni-icons/components/uni-icons/uni-icons.vue

@@ -1,12 +1,5 @@
 <template>
-	<!-- #ifdef APP-NVUE -->
 	<text :style="styleObj" class="uni-icons" @click="_onClick">{{unicode}}</text>
-	<!-- #endif -->
-	<!-- #ifndef APP-NVUE -->
-	<text :style="styleObj" class="uni-icons" :class="['uniui-'+type,customPrefix,customPrefix?type:'']" @click="_onClick">
-		<slot></slot>
-	</text>
-	<!-- #endif -->
 </template>
 
 <script>
@@ -17,15 +10,6 @@
 		return (typeof val === 'number' || reg.test(val)) ? val + 'px' : val;
 	}
 
-	// #ifdef APP-NVUE
-	var domModule = weex.requireModule('dom');
-	import iconUrl from './uniicons.ttf'
-	domModule.addRule('fontFace', {
-		'fontFamily': "uniicons",
-		'src': "url('" + iconUrl + "')"
-	});
-	// #endif
-
 	/**
 	 * Icons 图标
 	 * @description 用于展示 icons 图标

+ 4 - 4
unpackage/dist/cache/.vite/deps/_metadata.json

@@ -1,13 +1,13 @@
 {
-  "hash": "af6e7a40",
-  "configHash": "89b37c30",
+  "hash": "66d9a829",
+  "configHash": "6d67b660",
   "lockfileHash": "65c58edb",
-  "browserHash": "c99899b1",
+  "browserHash": "f25b4267",
   "optimized": {
     "jsencrypt": {
       "src": "../../../../../node_modules/jsencrypt/lib/index.js",
       "file": "jsencrypt.js",
-      "fileHash": "d7735089",
+      "fileHash": "7a7f50e4",
       "needsInterop": false
     }
   },

+ 3 - 1
vite.config.js

@@ -3,7 +3,9 @@ import {
 } from 'vite'
 import uni from '@dcloudio/vite-plugin-uni'
 import path from 'path'
-const define = {}
+const define = {
+	__VUE_PROD_DEVTOOLS__: 'false'
+}
 if (!["mp-weixin", "h5", "web"].includes(process.env.UNI_PLATFORM)) {
 	define['global'] = null
 	define['wx'] = 'uni'