index.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. <template>
  2. <view class="swh-page">
  3. <view class="filter-card">
  4. <view class="filter-title">数据填报</view>
  5. <view class="filter-subtitle">填报并维护各期生产运营双周会数据</view>
  6. <view class="filter-field">
  7. <text>专业公司</text>
  8. <uni-data-select v-model="deptId" :localdata="companyOptions" placeholder="请选择专业公司" />
  9. </view>
  10. <view class="filter-field">
  11. <text>会议日期</text>
  12. <uni-datetime-picker v-model="meetingDate" type="daterange" return-type="string" />
  13. </view>
  14. <view class="filter-actions">
  15. <button class="plain-btn" @click="resetQuery">重置</button>
  16. <button class="primary-btn" @click="search">查询</button>
  17. </view>
  18. </view>
  19. <view class="list-head">
  20. <text>会议记录({{ total }})</text>
  21. <button class="create-btn" @click="openForm('create')">+ 新建</button>
  22. </view>
  23. <view v-if="loading" class="state"><uni-load-more status="loading" /></view>
  24. <view v-else-if="!list.length" class="state"><text>暂无会议数据</text></view>
  25. <view v-else class="card-list">
  26. <view v-for="item in list" :key="item.id" class="meeting-card">
  27. <view class="card-head">
  28. <view class="series">{{ item.meetingSeries || "-" }}</view>
  29. <view class="date">{{ formatDate(item.meetingDate) }}</view>
  30. </view>
  31. <view class="company">
  32. <uni-icons type="staff" size="16" color="#64748b" />
  33. <text>{{ item.companyName || "-" }}</text>
  34. </view>
  35. <view class="card-actions">
  36. <text @click="openForm('view', item.id)">查看</text>
  37. <text class="edit" @click="openForm('edit', item.id)">编辑</text>
  38. </view>
  39. </view>
  40. </view>
  41. <uni-load-more
  42. v-if="list.length"
  43. :status="loadStatus"
  44. :content-text="loadText"
  45. @clickLoadMore="loadMore"
  46. />
  47. </view>
  48. </template>
  49. <script setup>
  50. import { computed, ref } from "vue";
  51. import { onLoad, onReachBottom, onShow } from "@dcloudio/uni-app";
  52. import dayjs from "dayjs";
  53. import {
  54. getOperationMeetingCompanies,
  55. getOperationMeetingPage,
  56. } from "@/api/operationMeeting";
  57. const meetingDate = ref([]);
  58. const deptId = ref("");
  59. const companyOptions = ref([]);
  60. const list = ref([]);
  61. const total = ref(0);
  62. const pageNo = ref(1);
  63. const pageSize = 10;
  64. const loading = ref(false);
  65. const loadingMore = ref(false);
  66. const hasMore = computed(() => list.value.length < total.value);
  67. const loadStatus = computed(() =>
  68. loadingMore.value ? "loading" : hasMore.value ? "more" : "noMore",
  69. );
  70. const loadText = {
  71. contentdown: "上拉加载更多",
  72. contentrefresh: "加载中",
  73. contentnomore: "没有更多数据了",
  74. };
  75. const fetchList = async (append = false) => {
  76. append ? (loadingMore.value = true) : (loading.value = true);
  77. try {
  78. const res = await getOperationMeetingPage({
  79. pageNo: pageNo.value,
  80. pageSize,
  81. deptId: deptId.value || undefined,
  82. meetingDate: meetingDate.value?.length
  83. ? [
  84. `${meetingDate.value[0]} 00:00:00`,
  85. `${meetingDate.value[1]} 23:59:59`,
  86. ]
  87. : undefined,
  88. });
  89. const data = res?.data || {};
  90. const rows = data.list || [];
  91. list.value = append ? [...list.value, ...rows] : rows;
  92. total.value = data.total || 0;
  93. } finally {
  94. loading.value = false;
  95. loadingMore.value = false;
  96. }
  97. };
  98. const search = () => {
  99. pageNo.value = 1;
  100. fetchList();
  101. };
  102. const resetQuery = () => {
  103. meetingDate.value = [];
  104. deptId.value = "";
  105. search();
  106. };
  107. const loadMore = () => {
  108. if (!hasMore.value || loadingMore.value) return;
  109. pageNo.value += 1;
  110. fetchList(true);
  111. };
  112. const openForm = (type, id) => {
  113. const query = id ? `?type=${type}&id=${id}` : `?type=${type}`;
  114. uni.navigateTo({ url: `/pages/swh/form${query}` });
  115. };
  116. const formatDate = (value) => (value ? dayjs(value).format("YYYY-MM-DD") : "-");
  117. onLoad(async () => {
  118. const res = await getOperationMeetingCompanies();
  119. companyOptions.value = (Array.isArray(res?.data) ? res.data : [])
  120. .filter((item) => item.type === "1")
  121. .map((item) => ({ text: item.name, value: item.id }));
  122. });
  123. onShow(search);
  124. onReachBottom(loadMore);
  125. </script>
  126. <style lang="scss" scoped>
  127. .swh-page { min-height: 100vh; padding: 24rpx; box-sizing: border-box; background: #f4f7fb; }
  128. .filter-card, .meeting-card { background: #fff; border-radius: 20rpx; box-shadow: 0 8rpx 28rpx rgba(30, 64, 175, .05); }
  129. .filter-card { padding: 30rpx 26rpx; }
  130. .filter-title { color: #0f172a; font-size: 36rpx; font-weight: 700; }
  131. .filter-subtitle { margin: 8rpx 0 26rpx; color: #94a3b8; font-size: 24rpx; }
  132. .filter-field + .filter-field { margin-top: 20rpx; }
  133. .filter-field > text { display: block; margin-bottom: 10rpx; color: #64748b; font-size: 24rpx; }
  134. .filter-actions { display: flex; margin-top: 24rpx; }
  135. .filter-actions button { flex: 1; height: 72rpx; margin: 0; line-height: 72rpx; font-size: 28rpx; border-radius: 14rpx; }
  136. .filter-actions button + button { margin-left: 18rpx; }
  137. .plain-btn { color: #475569; background: #f1f5f9; }
  138. .primary-btn, .create-btn { color: #fff; background: #2563eb; }
  139. .list-head { display: flex; align-items: center; justify-content: space-between; margin: 30rpx 4rpx 20rpx; color: #172033; font-weight: 700; }
  140. .create-btn { width: 150rpx; height: 64rpx; line-height: 64rpx; margin: 0; font-size: 26rpx; border-radius: 12rpx; }
  141. .card-list { padding-bottom: 20rpx; }
  142. .meeting-card + .meeting-card { margin-top: 20rpx; }
  143. .meeting-card { padding: 26rpx; }
  144. .card-head { display: flex; justify-content: space-between; align-items: center; }
  145. .series { color: #1d4ed8; font-size: 32rpx; font-weight: 700; }
  146. .date { color: #64748b; font-size: 24rpx; }
  147. .company { display: flex; align-items: center; margin-top: 22rpx; color: #334155; font-size: 28rpx; }
  148. .company text { margin-left: 10rpx; }
  149. .card-actions { display: flex; justify-content: flex-end; margin-top: 24rpx; padding-top: 20rpx; border-top: 1rpx solid #eef2f7; color: #16a34a; font-size: 26rpx; }
  150. .card-actions .edit { margin-left: 42rpx; color: #2563eb; }
  151. .state { padding: 100rpx 0; color: #94a3b8; text-align: center; }
  152. button::after { border: 0; }
  153. </style>