index.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. <template>
  2. <view class="page_notice" :style="{paddingTop: statusBarHeight + 310 + 'rpx', paddingBottom: safeAreaBottom + 160 + 'rpx' }">
  3. <notice-header
  4. title="任务总览"
  5. mode="taskOverview"
  6. :showSearch="true"
  7. :showFilter="true"
  8. :showTabs="true"
  9. :showCreateBtn="true"
  10. :tabs="tabs"
  11. :activeTab="activeTab"
  12. @tabChange="handleTabChange"
  13. @academicYearChange="handleAcademicYearChange"
  14. @categoryChange="handleCategoryChange"
  15. @materialTypeChange="handleMaterialTypeChange"
  16. @searchChange="handleSearchChange"
  17. @create="handleCreate">
  18. </notice-header>
  19. <view class="notice_content">
  20. <view class="notice_list">
  21. <scroll-view scroll-y v-for="item in taskList" :key="item.id" class="notice_item">
  22. <view class="item_content">
  23. <view class="notice_title_row">
  24. <image class="notice_icon" :src="item.icon" mode="aspectFit"></image>
  25. <text class="notice_title">{{ item.title }}</text>
  26. </view>
  27. <view class="notice_meta">
  28. <view class="status_tag" :class="item.statusClass">
  29. <text>{{ item.status }}</text>
  30. </view>
  31. <text class="meta_text textLength">{{ item.academicYear }}</text>
  32. <text class="meta_text textLength">{{ item.category }}</text>
  33. <text class="meta_text">{{ item.time }}</text>
  34. </view>
  35. </view>
  36. <view class="item_bottom">
  37. <view class="notice_stats">
  38. <text class="stats_text">提交人: {{ item.submitter }}</text>
  39. <text class="stats_text">截止: {{ item.deadline }}</text>
  40. </view>
  41. <view class="notice_actions">
  42. <view class="action_btn detail" @click="handleDetail(item.id)">
  43. <text>详情</text>
  44. </view>
  45. </view>
  46. </view>
  47. </scroll-view>
  48. <no-data v-if="taskList.length === 0" centered text="暂无任务" />
  49. </view>
  50. </view>
  51. <common-tabbar></common-tabbar>
  52. </view>
  53. </template>
  54. <script setup>
  55. import { ref, onMounted } from 'vue';
  56. import NoticeHeader from '@/components/notice-header.vue';
  57. import CommonTabbar from '@/components/commonTabbar.vue';
  58. import NoData from '@/components/no-data.vue';
  59. import { useSafeArea } from '@/common/safeArea';
  60. const { statusBarHeight, safeAreaBottom, initSafeArea } = useSafeArea();
  61. const tabs = ['全部', '待开始', '进行中', '已结束'];
  62. const activeTab = ref(-1);
  63. const academicYearIndex = ref(0);
  64. const categoryIndex = ref(0);
  65. const materialTypeIndex = ref(0);
  66. const word = ref('');
  67. const taskList = ref([]);
  68. const statusMap = {
  69. 0: '待开始',
  70. 1: '进行中',
  71. 2: '已结束'
  72. };
  73. const statusClassMap = {
  74. 0: 'status-pending',
  75. 1: 'status-progress',
  76. 2: 'status-ended'
  77. };
  78. const mockTaskList = [
  79. {
  80. id: 1,
  81. title: '2025年秋季学期教案材料收集',
  82. status: '待开始',
  83. statusClass: 'status-pending',
  84. academicYear: '2025-2026学年',
  85. category: '日常教学',
  86. time: '2025-07-28',
  87. submitter: '张老师',
  88. deadline: '2025-08-15',
  89. icon: '/static/image/workspace/icon2.png'
  90. },
  91. {
  92. id: 2,
  93. title: '期中考试试卷提交任务',
  94. status: '进行中',
  95. statusClass: 'status-progress',
  96. academicYear: '2025-2026学年',
  97. category: '考试测评',
  98. time: '2025-07-25',
  99. submitter: '李老师',
  100. deadline: '2025-08-30',
  101. icon: '/static/image/workspace/icon1.png'
  102. },
  103. {
  104. id: 3,
  105. title: '课件资源库更新任务',
  106. status: '进行中',
  107. statusClass: 'status-progress',
  108. academicYear: '2025-2026学年',
  109. category: '日常教学',
  110. time: '2025-07-20',
  111. submitter: '王老师',
  112. deadline: '2025-09-01',
  113. icon: '/static/image/workspace/icon3.png'
  114. },
  115. {
  116. id: 4,
  117. title: '教研活动材料归档',
  118. status: '已结束',
  119. statusClass: 'status-ended',
  120. academicYear: '2024-2025学年',
  121. category: '教研活动',
  122. time: '2025-06-15',
  123. submitter: '赵老师',
  124. deadline: '2025-07-01',
  125. icon: '/static/image/workspace/icon4.png'
  126. }
  127. ];
  128. const fetchTaskList = () => {
  129. let filteredList = [...mockTaskList];
  130. if (activeTab.value >= 0) {
  131. filteredList = filteredList.filter(item => {
  132. const statusIndex = Object.keys(statusMap).findIndex(key => statusMap[key] === item.status);
  133. return statusIndex === activeTab.value;
  134. });
  135. }
  136. if (word.value) {
  137. filteredList = filteredList.filter(item =>
  138. item.title.includes(word.value)
  139. );
  140. }
  141. taskList.value = filteredList;
  142. };
  143. onMounted(() => {
  144. initSafeArea();
  145. fetchTaskList();
  146. });
  147. const handleTabChange = (status) => {
  148. activeTab.value = status;
  149. fetchTaskList();
  150. };
  151. const handleAcademicYearChange = (index) => {
  152. academicYearIndex.value = index;
  153. fetchTaskList();
  154. };
  155. const handleCategoryChange = (index) => {
  156. categoryIndex.value = index;
  157. fetchTaskList();
  158. };
  159. const handleMaterialTypeChange = (index) => {
  160. materialTypeIndex.value = index;
  161. fetchTaskList();
  162. };
  163. const handleSearchChange = (keyword) => {
  164. word.value = keyword;
  165. fetchTaskList();
  166. };
  167. const handleCreate = () => {
  168. uni.navigateTo({
  169. url: '/pages/materialCollection/submitMaterial/index'
  170. });
  171. };
  172. const handleDetail = (id) => {
  173. uni.navigateTo({
  174. url: `/pages/materialCollection/submitMaterial/index?id=${id}`
  175. });
  176. };
  177. </script>
  178. <style lang="scss">
  179. .page_notice {
  180. min-height: 100vh;
  181. display: flex;
  182. flex-direction: column;
  183. background-color: #FFFFFF;
  184. box-sizing: border-box;
  185. }
  186. .notice_content {
  187. flex: 1;
  188. display: flex;
  189. flex-direction: column;
  190. overflow: hidden;
  191. padding-left: 0rpx;
  192. padding-right: 0rpx;
  193. }
  194. .notice_list {
  195. flex: 1;
  196. overflow-y: auto;
  197. overflow-x: hidden;
  198. padding: 0 24rpx;
  199. display: flex;
  200. flex-direction: column;
  201. }
  202. .notice_item {
  203. background-color: #F9FAFF;
  204. border-radius: 20rpx;
  205. margin-bottom: 24rpx;
  206. border: 2rpx solid #E4E7ED;
  207. .item_content {
  208. padding: 24rpx;
  209. .notice_title_row {
  210. display: flex;
  211. align-items: center;
  212. margin-bottom: 16rpx;
  213. .notice_icon {
  214. width: 32rpx;
  215. height: 32rpx;
  216. margin-right: 16rpx;
  217. }
  218. .notice_title {
  219. flex: 1;
  220. font-weight: 500;
  221. font-size: 28rpx;
  222. color: #333333;
  223. overflow: hidden;
  224. text-overflow: ellipsis;
  225. white-space: nowrap;
  226. }
  227. }
  228. .notice_meta {
  229. display: flex;
  230. align-items: center;
  231. flex-wrap: wrap;
  232. gap: 16rpx;
  233. .status_tag {
  234. font-size: 24rpx;
  235. height: 48rpx;
  236. width: 96rpx;
  237. text-align: center;
  238. line-height: 48rpx;
  239. border-radius: 4rpx;
  240. font-weight: 400;
  241. border-radius: 8rpx;
  242. &.status-pending {
  243. background: rgba(46, 100, 250, 0.1);
  244. color: #2E64FA;
  245. }
  246. &.status-progress {
  247. background: rgba(230, 162, 60, 0.1);
  248. color: #E6A23C;
  249. }
  250. &.status-ended {
  251. background: rgba(144, 147, 153, 0.1);
  252. color: #909399;
  253. }
  254. }
  255. .meta_text {
  256. font-size: 24rpx;
  257. color: #999999;
  258. line-height: 48rpx;
  259. }
  260. .textLength {
  261. max-width: 160rpx;
  262. overflow: hidden;
  263. text-overflow: ellipsis;
  264. white-space: nowrap;
  265. }
  266. }
  267. }
  268. .item_bottom {
  269. border-top: 2rpx solid #E4E7ED;
  270. display: flex;
  271. flex-direction: row;
  272. justify-content: space-between;
  273. align-items: center;
  274. padding: 24rpx 16rpx;
  275. .notice_stats {
  276. display: flex;
  277. .stats_text {
  278. font-size: 24rpx;
  279. color: #999999;
  280. &:first-child {
  281. margin-right: 32rpx;
  282. }
  283. }
  284. }
  285. .notice_actions {
  286. display: flex;
  287. justify-content: flex-end;
  288. gap: 16rpx;
  289. .action_btn {
  290. font-size: 24rpx;
  291. padding: 12rpx 24rpx;
  292. border-radius: 8rpx;
  293. border: 2rpx solid transparent;
  294. &.detail {
  295. background-color: #2E64FA;
  296. color: #FFFFFF;
  297. }
  298. }
  299. }
  300. }
  301. &:last-child {
  302. margin-bottom: 0;
  303. }
  304. }
  305. </style>