statistics.vue 8.3 KB

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