equipment-calendar.vue 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <template>
  2. <view class="px3 pb-3">
  3. <!-- 月份切换:默认看当月,可往前翻历史月份 -->
  4. <view class="d-flex a-center j-between bg-color-white p2 mt-2" style="border-radius: 16rpx">
  5. <view class="month-btn" @tap="changeMonth(-1)">‹ 上一月</view>
  6. <view class="f-size-32 f-weight-bold text-color-3">{{ monthLabel }}</view>
  7. <view class="month-btn" @tap="changeMonth(1)">下一月 ›</view>
  8. </view>
  9. <!-- 当月汇总 -->
  10. <view class="p2 mt-2 bg-color-white" style="border-radius: 16rpx">
  11. <view class="d-flex a-center">
  12. <text class="f-size-28 text-color-3">本月已点检</text>
  13. <text class="f-size-36 f-weight-bold count-text mx2">{{ calendar.finishDays || 0 }}</text>
  14. <text class="f-size-28 text-color-3">天,共 {{ calendar.finishNum || 0 }} 项任务</text>
  15. </view>
  16. <view class="f-size-26 text-color-6 mt-1">最后一次点检:{{ lastFinishText }}</view>
  17. </view>
  18. <!-- 日历:已完成的日期标绿打勾 -->
  19. <view class="p2 mt-2 bg-color-white" style="border-radius: 16rpx">
  20. <view class="d-flex">
  21. <view class="day-cell week-head" v-for="(week, index) in weeks" :key="'week' + index">{{ week }}</view>
  22. </view>
  23. <view class="d-flex f-wrap">
  24. <view v-for="(cell, index) in cells" :key="index"
  25. class="day-cell"
  26. :class="{ 'day-checked': cell.checked, 'day-today': cell.today }"
  27. @tap="handleDay(cell)">
  28. <text v-if="!cell.blank" class="day-num">{{ cell.day }}</text>
  29. <text v-if="cell.checked" class="day-mark">√</text>
  30. </view>
  31. </view>
  32. <view class="d-flex a-center mt-2 f-size-24 text-color-6">
  33. <text class="legend legend-checked"></text>
  34. <text class="ml-1">已点检</text>
  35. <text class="legend legend-normal ml-3"></text>
  36. <text class="ml-1">未点检</text>
  37. <text class="legend legend-today ml-3"></text>
  38. <text class="ml-1">今天</text>
  39. </view>
  40. </view>
  41. <view class="f-size-24 text-color-9 mt-2">
  42. 数据来源为本设备的保养(点检)任务,绿色打勾表示当天有已完成记录;点击任意日期可查看当天的任务名称、执行人与完成时间。
  43. </view>
  44. <u-popup v-model="dayShow" mode="bottom" border-radius="20">
  45. <view class="p3">
  46. <view class="f-size-30 f-weight-bold text-center">{{ selectedDate }} 点检记录</view>
  47. <view v-if="selectedJobs.length === 0" class="f-size-28 text-color-9 mt-3">该日无已完成记录</view>
  48. <view v-for="(job, index) in selectedJobs" :key="index" class="job-item p2 mt-2">
  49. <view class="f-size-28 text-color-3">{{ job.name || job.standardName || '保养任务' }}</view>
  50. <view class="f-size-24 text-color-6 mt-1">
  51. 执行人:{{ job.actualUser || job.checkUserName || job.checkDeptName || '-' }}
  52. </view>
  53. <view class="f-size-24 text-color-6 mt-1">完成时间:{{ job.actualEndTime || '-' }}</view>
  54. </view>
  55. <u-button type="primary" @click="dayShow = false" style="margin-top: 20rpx">关闭</u-button>
  56. </view>
  57. </u-popup>
  58. </view>
  59. </template>
  60. <script>
  61. export default {
  62. data() {
  63. return {
  64. sbId: '',
  65. // 设备详情页带过来的“上次保养完成时间”,当月无记录时兜底展示
  66. deviceLastFinish: '',
  67. month: '',
  68. weeks: ['日', '一', '二', '三', '四', '五', '六'],
  69. cells: [],
  70. calendar: {},
  71. dayShow: false,
  72. selectedDate: '',
  73. selectedJobs: []
  74. };
  75. },
  76. computed: {
  77. monthLabel() {
  78. if (!this.month) {
  79. return '';
  80. }
  81. const parts = this.month.split('-');
  82. return parts[0] + '年' + Number(parts[1]) + '月';
  83. },
  84. lastFinishText() {
  85. // 优先显示当月最后一次完成时间,当月没做过则显示设备上的历史最近时间
  86. return this.calendar.lastFinishTime || this.deviceLastFinish || '暂无';
  87. }
  88. },
  89. onLoad(options) {
  90. this.sbId = options.sbId || options.detailId;
  91. this.deviceLastFinish = options.last ? decodeURIComponent(options.last) : '';
  92. this.month = this.currentMonth();
  93. this.loadData();
  94. },
  95. methods: {
  96. pad(value) {
  97. return value < 10 ? '0' + value : '' + value;
  98. },
  99. currentMonth() {
  100. const now = new Date();
  101. return now.getFullYear() + '-' + this.pad(now.getMonth() + 1);
  102. },
  103. changeMonth(offset) {
  104. const parts = this.month.split('-');
  105. const target = new Date(Number(parts[0]), Number(parts[1]) - 1 + offset, 1);
  106. const now = new Date();
  107. // 未来月份没有意义,直接拦掉,避免现场误判为“漏保养”
  108. if (
  109. target.getFullYear() > now.getFullYear() ||
  110. (target.getFullYear() === now.getFullYear() && target.getMonth() + 1 > now.getMonth() + 1)
  111. ) {
  112. uni.showToast({ title: '暂不支持查看未来月份', icon: 'none' });
  113. return;
  114. }
  115. this.month = target.getFullYear() + '-' + this.pad(target.getMonth() + 1);
  116. this.loadData();
  117. },
  118. loadData() {
  119. this.$Http
  120. .getSbCheckCalendarIgnores({
  121. sbId: this.sbId,
  122. month: this.month
  123. })
  124. .then((res) => {
  125. this.calendar = res.data || {};
  126. this.buildCells(res.data && res.data.dayList);
  127. });
  128. },
  129. // 按月历格子生成:前置空格对齐星期,末尾补空格凑满整行
  130. buildCells(dayList) {
  131. const parts = this.month.split('-');
  132. const year = Number(parts[0]);
  133. const month = Number(parts[1]);
  134. const finishedMap = {};
  135. (dayList || []).forEach((item) => {
  136. finishedMap[item.date] = item;
  137. });
  138. const todayStr = this.formatDate(new Date());
  139. const cells = [];
  140. const firstWeekday = new Date(year, month - 1, 1).getDay();
  141. for (let i = 0; i < firstWeekday; i++) {
  142. cells.push({ blank: true });
  143. }
  144. const daysOfMonth = new Date(year, month, 0).getDate();
  145. for (let day = 1; day <= daysOfMonth; day++) {
  146. const date = year + '-' + this.pad(month) + '-' + this.pad(day);
  147. const finished = finishedMap[date];
  148. cells.push({
  149. blank: false,
  150. date: date,
  151. day: day,
  152. checked: !!finished,
  153. today: date === todayStr,
  154. jobList: finished ? finished.jobList : []
  155. });
  156. }
  157. while (cells.length % 7 !== 0) {
  158. cells.push({ blank: true });
  159. }
  160. this.cells = cells;
  161. },
  162. formatDate(date) {
  163. return date.getFullYear() + '-' + this.pad(date.getMonth() + 1) + '-' + this.pad(date.getDate());
  164. },
  165. handleDay(cell) {
  166. if (cell.blank) {
  167. return;
  168. }
  169. this.selectedDate = cell.date;
  170. this.selectedJobs = cell.jobList || [];
  171. this.dayShow = true;
  172. }
  173. }
  174. };
  175. </script>
  176. <style lang="less">
  177. .month-btn {
  178. font-size: 26rpx;
  179. color: #0f6fb9;
  180. padding: 6rpx 16rpx;
  181. }
  182. .count-text {
  183. color: #19be6b;
  184. }
  185. .day-cell {
  186. width: 14.2857%;
  187. height: 92rpx;
  188. display: flex;
  189. flex-direction: column;
  190. align-items: center;
  191. justify-content: center;
  192. box-sizing: border-box;
  193. font-size: 26rpx;
  194. color: #333333;
  195. }
  196. .week-head {
  197. height: 50rpx;
  198. font-size: 24rpx;
  199. color: #999999;
  200. }
  201. .day-num {
  202. line-height: 34rpx;
  203. }
  204. .day-mark {
  205. font-size: 22rpx;
  206. line-height: 24rpx;
  207. color: #ffffff;
  208. }
  209. /* 已点检:绿底白字打勾 */
  210. .day-checked {
  211. background-color: #19be6b;
  212. border-radius: 10rpx;
  213. color: #ffffff;
  214. }
  215. .day-today {
  216. border: 2rpx solid #0f6fb9;
  217. border-radius: 10rpx;
  218. }
  219. .legend {
  220. display: inline-block;
  221. width: 24rpx;
  222. height: 24rpx;
  223. border-radius: 6rpx;
  224. }
  225. .legend-checked {
  226. background-color: #19be6b;
  227. }
  228. .legend-normal {
  229. background-color: #f2f3f5;
  230. }
  231. .legend-today {
  232. background-color: #ffffff;
  233. border: 2rpx solid #0f6fb9;
  234. }
  235. .job-item {
  236. background-color: #f7f8fa;
  237. border-radius: 12rpx;
  238. }
  239. </style>