statistics.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. <template>
  2. <view class="statistics">
  3. <!-- 背景图片区域 -->
  4. <img class="img" src="../../static/images/center-bg.png" />
  5. <!-- 时间选择区域 -->
  6. <div class="picker">
  7. <uni-datetime-picker type="date" :clear-icon="false" v-model="time" @change="changeTime"></uni-datetime-picker>
  8. </div>
  9. <!-- 状态选择区域 -->
  10. <div class="type">
  11. <div class="type_box" :class="{ active: currentType === 1 }" @click="changeType(1)">未监听({{ info?.abnormal?.count }})</div>
  12. <div class="type_box" :class="{ active: currentType === 2 }" @click="changeType(2)">监听({{ info?.normal?.count }})</div>
  13. <div class="type_box" :class="{ active: currentType === 3 }" @click="changeType(3)">请假({{ info?.ack?.count }})</div>
  14. </div>
  15. <!-- 列表数据 -->
  16. <div class="list" v-if="listData.length">
  17. <div class="list_header">
  18. <div class="header_box">姓名</div>
  19. <div class="header_box">学号</div>
  20. <div v-if="currentType != 1" class="header_box">操作</div>
  21. </div>
  22. <div class="list_content">
  23. <div class="list_box" v-for="item in listData" :key="item.userId">
  24. <div class="box" @click="clickName(item)">{{ item.name }}</div>
  25. <div class="box">{{ item.cardNo }}</div>
  26. <div v-if="currentType != 1" class="box color" @click="clickDetail(item)">详情</div>
  27. </div>
  28. </div>
  29. </div>
  30. <!-- 没有数据时展示的页面 -->
  31. <NoData v-else />
  32. <!-- 详情弹窗区域 -->
  33. <uni-popup ref="popup" type="center" :is-mask-click="false">
  34. <view class="popup_box">
  35. <!-- 弹窗标题区域 -->
  36. <view class="pop_header">
  37. 请假详情
  38. <view class="header_icon" @click="popup.close()">×</view>
  39. </view>
  40. <!-- 弹窗详细信息区域 -->
  41. <view class="pop_info">
  42. <view class="info_key">请假人姓名:</view>
  43. <view class="info_value">{{ askInfo.name }}</view>
  44. </view>
  45. <view class="pop_info">
  46. <view class="info_key">学号:</view>
  47. <view class="info_value">{{ askInfo.cardNo }}</view>
  48. </view>
  49. <view class="pop_info">
  50. <view class="info_key">请假时间:</view>
  51. <view class="info_value">{{ dayjs(askInfo.startTime).format('YYYY-MM-DD hh:mm:ss') }} - {{ dayjs(askInfo.endTime).format('YYYY-MM-DD hh:mm:ss') }}</view>
  52. </view>
  53. <view class="pop_info">
  54. <view class="info_key">请假说明:</view>
  55. <view class="info_value">{{ askInfo.reason }}</view>
  56. </view>
  57. <view class="pop_info">
  58. <view class="info_key">创建时间:</view>
  59. <view class="info_value">{{ dayjs(askInfo.initiateTime).format('YYYY-MM-DD hh:mm:ss') }}</view>
  60. </view>
  61. </view>
  62. </uni-popup>
  63. </view>
  64. </template>
  65. <script setup>
  66. import { ref } from 'vue'
  67. import { onLoad } from '@dcloudio/uni-app'
  68. import { myRequest } from '@/utils/api.js'
  69. import dayjs from 'dayjs'
  70. import { decryptDes } from '@/utils/des.js'
  71. import NoData from '@/components/noData.vue'
  72. // 时间选择框绑定数据
  73. const time = ref('')
  74. // 当前状态
  75. const currentType = ref(1)
  76. // 展示的列表数据
  77. const listData = ref([])
  78. // 全部数据
  79. const info = ref({})
  80. // 弹窗元素标记
  81. const popup = ref(null)
  82. // 弹窗请假信息
  83. const askInfo = ref({})
  84. onLoad(() => {
  85. // 获取当前时间
  86. time.value = dayjs(Date.now()).format('YYYY-MM-DD') + ' 00:00:00'
  87. getData()
  88. })
  89. // 获取列表数据
  90. const getData = async () => {
  91. const res = await myRequest({
  92. url: '/wanzai/api/smartUser/statisticsCampus',
  93. data: {
  94. classId: uni.getStorageSync('userInfo').schoolClass,
  95. dateTime: time.value
  96. }
  97. })
  98. // console.log(res)
  99. if (res.code == 200) {
  100. const result = JSON.parse(decryptDes(res.data))
  101. // console.log(result)
  102. info.value = result
  103. if (currentType.value === 1) {
  104. listData.value = result.abnormal.date
  105. } else if (currentType.value === 2) {
  106. listData.value = result.normal.date
  107. } else if (currentType.value === 3) {
  108. listData.value = result.ack.date
  109. }
  110. }
  111. }
  112. // 切换时间时的回调
  113. const changeTime = (v) => {
  114. time.value = v + ' 00:00:00'
  115. getData()
  116. }
  117. // 切换状态时的回调
  118. const changeType = (v) => {
  119. if (currentType.value != v) {
  120. currentType.value = v
  121. getData()
  122. }
  123. }
  124. // 点击姓名时触发的回调
  125. const clickName = async (item) => {
  126. // 获取学生相关的信息
  127. const res = await myRequest({
  128. url: '/wanzai/api/smartUser/addressBook',
  129. data: {
  130. userId: item.userId
  131. }
  132. })
  133. // console.log(res)
  134. if (res.code == 200) {
  135. const result = JSON.parse(decryptDes(res.data))
  136. // console.log(result)
  137. uni.navigateTo({
  138. url: `/pages/student/student?msg=${JSON.stringify(result)}`
  139. })
  140. }
  141. }
  142. // 点击详情时触发的回调
  143. const clickDetail = (item) => {
  144. // console.log(item)
  145. if (currentType.value === 2) {
  146. // 跳转 轨迹
  147. uni.navigateTo({
  148. url: `/pages/track/track?id=${item.userId}&time=${time.value}`
  149. })
  150. } else if (currentType.value === 3) {
  151. // 弹窗
  152. askInfo.value = {}
  153. // 获取请假信息
  154. getAskInfo(item.userId)
  155. popup.value.open()
  156. }
  157. }
  158. const getAskInfo = async (userId) => {
  159. const res = await myRequest({
  160. url: '/wanzai/api/smartAttendance/ackDetail',
  161. data: {
  162. userId,
  163. dateTime: time.value
  164. }
  165. })
  166. // console.log(res)
  167. if (res.code == 200) {
  168. const result = JSON.parse(decryptDes(res.data))
  169. // console.log(result)
  170. askInfo.value = result[0]
  171. }
  172. }
  173. </script>
  174. <style lang="scss" scoped>
  175. .statistics {
  176. position: relative;
  177. box-sizing: border-box;
  178. padding: 130rpx 20rpx 0;
  179. height: 100vh;
  180. overflow: hidden;
  181. background-color: #f1f6fe;
  182. .img {
  183. position: absolute;
  184. top: -50rpx;
  185. right: -50rpx;
  186. width: 589rpx;
  187. height: 320rpx;
  188. }
  189. .picker {
  190. position: absolute;
  191. top: 30rpx;
  192. right: 20rpx;
  193. width: 273rpx;
  194. height: 80rpx;
  195. }
  196. .type {
  197. position: relative;
  198. display: flex;
  199. justify-content: space-evenly;
  200. align-items: center;
  201. width: 710rpx;
  202. height: 122rpx;
  203. background-color: #fff;
  204. .type_box {
  205. display: flex;
  206. justify-content: center;
  207. align-items: center;
  208. width: 210rpx;
  209. height: 90rpx;
  210. font-size: 28rpx;
  211. border-radius: 10rpx;
  212. background-color: #f2f6fc;
  213. }
  214. .active {
  215. background-color: #3464ff;
  216. color: #fff;
  217. }
  218. }
  219. .list {
  220. padding: 30rpx 20rpx;
  221. margin-top: 20rpx;
  222. max-height: calc(100vh - 402rpx);
  223. font-size: 28rpx;
  224. background-color: #fff;
  225. .list_header {
  226. display: flex;
  227. justify-content: space-evenly;
  228. align-items: center;
  229. height: 73rpx;
  230. background-color: #f2f6fc;
  231. .header_box {
  232. width: 33%;
  233. text-align: center;
  234. }
  235. }
  236. .list_content {
  237. max-height: calc(100vh - 475rpx);
  238. overflow-y: auto;
  239. .list_box {
  240. display: flex;
  241. justify-content: space-evenly;
  242. align-items: center;
  243. height: 85rpx;
  244. border-bottom: 2rpx solid #e5e5e5;
  245. .box {
  246. width: 33%;
  247. text-align: center;
  248. }
  249. .color {
  250. color: #0061ff;
  251. }
  252. }
  253. }
  254. }
  255. .popup_box {
  256. padding-bottom: 50rpx;
  257. width: 710rpx;
  258. border-radius: 22rpx;
  259. background-color: #fff;
  260. .pop_header {
  261. display: flex;
  262. justify-content: center;
  263. align-items: center;
  264. position: relative;
  265. height: 94rpx;
  266. font-size: 28rpx;
  267. border-bottom: 1rpx solid #e6e6e6;
  268. .header_icon {
  269. position: absolute;
  270. top: 18rpx;
  271. right: 25rpx;
  272. font-size: 40rpx;
  273. }
  274. }
  275. .pop_info {
  276. display: flex;
  277. box-sizing: border-box;
  278. margin-top: 22rpx;
  279. padding: 0 35rpx;
  280. width: 100%;
  281. line-height: 42rpx;
  282. font-size: 28rpx;
  283. .info_key {
  284. color: #999999;
  285. }
  286. .info_value {
  287. flex: 1;
  288. }
  289. }
  290. }
  291. }
  292. </style>