taskTodo.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. <template>
  2. <!-- 打卡任务 -->
  3. <view class="task-container">
  4. <view class="task-header">
  5. <view class="task-title" v-if="tasks.length && tasks.length > 0">任务({{ tasks.length }}个)
  6. </view>
  7. <view class="task-title" v-else>任务</view>
  8. <view class="task-add-btn" @click="goPunchInEditPage">
  9. <uni-icons type="plusempty" size="30" color="#406CE7"></uni-icons>
  10. </view>
  11. </view>
  12. <view class="task-item" v-for="task in tasks" :key="task.id">
  13. <view class="main-box" @click="goPunchInDetailPage(task.id)">
  14. <view class="item-header">
  15. <span class="item-title">{{ task.taskName }}</span>
  16. <span class="item-reward">x{{ task.points }}</span>
  17. <view class="item-tag" v-if="task.fullAttendanceFlag">全勤奖励</view>
  18. <view class="item-tag" v-if="task.holidayFlag">节假日双倍</view>
  19. </view>
  20. <view class="item-desc"
  21. v-if="task.punchInMethod == PUNCH_IN_METHOD.COUNT || task.punchInMethod == PUNCH_IN_METHOD.TIMING">
  22. <span v-if="task.punchInMethod == PUNCH_IN_METHOD.COUNT">规则:打卡次数</span>
  23. <span v-if="task.punchInMethod == PUNCH_IN_METHOD.TIMING">规则:计时时间</span>
  24. <dict-item :dictCode="dict.PUNCH_IN_RULE" :itemCode="task.rule"></dict-item>
  25. <span v-if="task.compareRule == COMPARE_RULE.GTE">大于等于</span>
  26. <span v-if="task.compareRule == COMPARE_RULE.LTE">小于等于等于</span>
  27. <span v-if="task.punchInMethod == PUNCH_IN_METHOD.COUNT">{{ holidayFlag ? task.holidayCountTrack :
  28. task.countTrack }}次</span>
  29. <span v-if="task.punchInMethod == PUNCH_IN_METHOD.TIMING">{{ holidayFlag ? task.holidayTimeTrack :
  30. task.timeTrack.slice(0, 5) }}</span>
  31. </view>
  32. <view class="item-desc">
  33. 描述:{{ task.description }}
  34. </view>
  35. </view>
  36. <view
  37. :class="task.punchInResult == PUNCH_IN_RESULT.DONE ? 'func-box func-box-finish' : 'func-box func-box-unfinish'"
  38. v-if="task.punchInMethod == PUNCH_IN_METHOD.SINGLE" @click="doPunchIn(task.id)">
  39. <span v-if="task.punchInResult == PUNCH_IN_RESULT.UNDONE">打卡</span>
  40. <span v-else>已完成</span>
  41. </view>
  42. <view
  43. :class="task.punchInResult == PUNCH_IN_RESULT.DONE ? 'func-box func-box-finish' : 'func-box func-box-unfinish'"
  44. v-if="task.punchInMethod == PUNCH_IN_METHOD.COUNT" @click="doPunchIn(task.id)">
  45. <span>{{ task.currentCountTrack }}</span>
  46. <span>计数</span>
  47. </view>
  48. <view
  49. :class="task.punchInResult == PUNCH_IN_RESULT.DONE ? 'func-box func-box-finish' : 'func-box func-box-unfinish'"
  50. v-if="task.punchInMethod == PUNCH_IN_METHOD.TIMING" @click="doPunchInForTimeTrack(task.id, task.timeTrack)">
  51. <span>
  52. {{ task.currentTimeTrack.slice(0, 5) }}
  53. </span>
  54. <span>计时</span>
  55. </view>
  56. </view>
  57. <uni-load-more status="no-more" v-if="!tasks || tasks.length == 0" />
  58. </view>
  59. <!-- 计时弹出框 -->
  60. <uni-popup ref="timeTrackInputDialog" type="dialog" :is-mask-click="false">
  61. <uni-popup-dialog mode="input" :before-close="true" title="计时记录" confirmText="保存" @confirm="timeTrackFormConfirm"
  62. @close="timeTrackFormClose">
  63. <view class="pick-box" @click="timeTrackClick">
  64. <picker mode="time" :value="timeTrackFormData.timeTrack" @change="timeTrackChange">
  65. <view class="pick-box-time">{{ timeTrackFormData.timeTrack }}</view>
  66. </picker>
  67. </view>
  68. <view style="display: none;">
  69. <uni-forms ref="timeTrackForm" :modelValue="timeTrackFormData" :rules="timeTrackFormRules">
  70. <uni-forms-item name="timeTrack">
  71. <uni-easyinput v-model="timeTrackFormData.timeTrack" />
  72. </uni-forms-item>
  73. </uni-forms>
  74. </view>
  75. </uni-popup-dialog>
  76. </uni-popup>
  77. </template>
  78. <script setup>
  79. import { ref } from 'vue';
  80. import { onPullDownRefresh, onShow } from "@dcloudio/uni-app";
  81. import { punchInApi } from '@/apis/apis.js';
  82. import router from '@/common/router.js';
  83. import dict from '@/common/dict.js';
  84. import { PUNCH_IN_METHOD, PUNCH_IN_RESULT, COMPARE_RULE } from '@/common/enums.js';
  85. // 组件
  86. /**
  87. * 计时弹出框
  88. */
  89. const timeTrackInputDialog = ref(null);
  90. /**
  91. * 计时表单
  92. */
  93. const timeTrackForm = ref(null);
  94. // 属性
  95. /**
  96. * 打卡任务
  97. */
  98. const tasks = ref([]);
  99. /**
  100. * 计时表单数据
  101. */
  102. const timeTrackFormData = ref({
  103. timeTrack: '00:00'
  104. });
  105. /**
  106. * 计时表单规则
  107. */
  108. const timeTrackFormRules = ref({
  109. timeTrack: {
  110. rules: [{
  111. required: true,
  112. errorMessage: "请输入时间"
  113. }]
  114. }
  115. });
  116. // 方法
  117. /**
  118. * 计时时间选择监听
  119. */
  120. const timeTrackChange = (e) => {
  121. timeTrackFormData.value.timeTrack = e.detail.value;
  122. }
  123. /**
  124. * 打卡(计时)
  125. */
  126. const doPunchInForTimeTrack = (id, timeTrack) => {
  127. // 重置上一轮的表单数据
  128. timeTrackFormData.value = {
  129. id,
  130. timeTrack: timeTrack ? timeTrack.slice(0, 5) : '00:00'
  131. };
  132. timeTrackInputDialog.value.open();
  133. }
  134. /**
  135. * 计时表单提交
  136. */
  137. const timeTrackFormConfirm = async () => {
  138. timeTrackForm.value.validate(['id']).then(data => {
  139. return punchInApi.doPunchIn(data);
  140. }).then(e => {
  141. timeTrackInputDialog.value.close();
  142. loadData();
  143. });
  144. }
  145. /**
  146. * 打卡
  147. */
  148. const doPunchIn = async (id) => {
  149. await punchInApi.doPunchIn({
  150. id
  151. });
  152. loadData();
  153. }
  154. /**
  155. * 跳转打卡编辑页面
  156. */
  157. const goPunchInEditPage = () => {
  158. uni.navigateTo({
  159. url: router.PUNCHIN_EDIT_URL
  160. });
  161. };
  162. /**
  163. * 跳转打卡详情页面
  164. */
  165. const goPunchInDetailPage = (id) => {
  166. uni.navigateTo({
  167. url: router.PUNCHIN_DETAIL_URL + "?id=" + id
  168. });
  169. };
  170. /**
  171. * 获取待办任务
  172. */
  173. const loadData = async () => {
  174. let res = await punchInApi.queryToDoList();
  175. tasks.value = res;
  176. };
  177. onShow(() => {
  178. loadData();
  179. });
  180. onPullDownRefresh(() => {
  181. loadData();
  182. uni.stopPullDownRefresh();
  183. });
  184. </script>
  185. <style lang="scss" scoped>
  186. .task-container {
  187. padding: 0rpx 24rpx;
  188. .task-header {
  189. position: relative;
  190. height: 80rpx;
  191. display: flex;
  192. justify-content: center;
  193. /* 水平居中 */
  194. align-items: center;
  195. /* 垂直居中 */
  196. .task-title {
  197. position: absolute;
  198. left: 0rpx;
  199. font-size: 30rpx;
  200. font-weight: 400;
  201. line-height: 43.44rpx;
  202. color: rgba(0, 0, 0, 1);
  203. }
  204. .task-add-btn {
  205. display: inline-flex;
  206. justify-content: center;
  207. align-items: center;
  208. position: absolute;
  209. right: 0rpx;
  210. width: 60rpx;
  211. height: 60rpx;
  212. border-radius: 10rpx;
  213. border: 3px solid rgba(64, 108, 231, 1);
  214. }
  215. }
  216. .task-item {
  217. margin-top: 16rpx;
  218. width: 100%;
  219. // height: 239rpx;
  220. border-radius: 24rpx;
  221. background: #FFFFFF;
  222. border: 0.5px solid #E4E4E4;
  223. box-shadow: 0px 1px 6px #D8D8D8;
  224. display: flex;
  225. .main-box {
  226. flex-grow: 1;
  227. padding: 16rpx 16rpx 16rpx 24rpx;
  228. .item-header {
  229. // position: relative;
  230. display: flex;
  231. align-items: center;
  232. .item-title {
  233. font-size: 30rpx;
  234. font-weight: 400;
  235. letter-spacing: 0rpx;
  236. line-height: 43.44rpx;
  237. color: #000000;
  238. }
  239. .item-reward {
  240. margin-left: 8rpx;
  241. font-size: 24rpx;
  242. font-weight: 400;
  243. letter-spacing: 0rpx;
  244. line-height: 34.75rpx;
  245. color: #000000;
  246. }
  247. .item-tag:first-child {
  248. margin-left: 24rpx;
  249. }
  250. .item-tag {
  251. margin-left: 16rpx;
  252. width: 94rpx;
  253. height: 38rpx;
  254. opacity: 1;
  255. border-radius: 24rpx;
  256. background: #FFFFFF;
  257. border: 1px solid #406CE7;
  258. display: inline-flex;
  259. justify-content: center;
  260. align-items: center;
  261. font-size: 18rpx;
  262. font-weight: 400;
  263. letter-spacing: 0rpx;
  264. // line-height: 26.06rpx;
  265. color: #406CE7;
  266. }
  267. .item-btn {
  268. display: inline-flex;
  269. position: absolute;
  270. right: 0rpx;
  271. width: 123rpx;
  272. height: 42rpx;
  273. border-radius: 30rpx;
  274. border: 1rpx solid #2A82E4;
  275. justify-content: center;
  276. align-items: center;
  277. font-size: 20rpx;
  278. font-weight: 400;
  279. line-height: 28.96px;
  280. color: rgba(64, 108, 231, 1);
  281. }
  282. }
  283. .item-desc {
  284. margin-top: 16rpx;
  285. font-size: 24rpx;
  286. font-weight: 400;
  287. letter-spacing: 0rpx;
  288. line-height: 34.75rpx;
  289. color: #000000;
  290. }
  291. }
  292. .func-box {
  293. flex-shrink: 0;
  294. width: 160rpx;
  295. border-radius: 0rpx 24rpx 24rpx 0rpx;
  296. font-size: 36rpx;
  297. font-weight: 400;
  298. letter-spacing: 0rpx;
  299. line-height: 52.13rpx;
  300. color: #FFFFFF;
  301. display: flex;
  302. justify-content: center;
  303. align-items: center;
  304. flex-direction: column;
  305. }
  306. .func-box-finish {
  307. background: #F2607A;
  308. }
  309. .func-box-unfinish {
  310. background: #406CE7;
  311. }
  312. }
  313. }
  314. .pick-box {
  315. width: 100%;
  316. height: 100rpx;
  317. border-radius: 8px;
  318. background: #ffffff;
  319. // 阴影
  320. border: 0.5px solid #e4e4e4;
  321. picker {
  322. width: 100%;
  323. height: 100%;
  324. .pick-box-time {
  325. width: 100%;
  326. height: 100rpx;
  327. display: flex;
  328. align-items: center;
  329. justify-content: center;
  330. color: #000000;
  331. border-radius: 24rpx;
  332. text-align: center;
  333. }
  334. }
  335. }
  336. </style>