index.vue 7.3 KB

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