lepaoRecords.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <template>
  2. <div class="container">
  3. <Breadcrumb :items="['校园乐跑', '乐跑记录']" />
  4. <a-card title="乐跑记录">
  5. <a-row>
  6. <a-col :flex="'1000px'">
  7. <a-form :model="queryData" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }"
  8. label-align="left">
  9. <a-row :gutter="16">
  10. <a-col :span="12">
  11. <a-form-item field="name" label="账号名称">
  12. <a-input v-model="queryData.name" placeholder="请输入账号名称" allow-clear />
  13. </a-form-item>
  14. </a-col>
  15. <a-col :span="12">
  16. <a-form-item field="lepao_account" label="学号">
  17. <a-input-number v-model="queryData.lepao_account" placeholder="请输入学号" :step="1" :precision="0"
  18. allow-clear />
  19. </a-form-item>
  20. </a-col>
  21. <a-col :span="12">
  22. <a-form-item field="email" label="通知邮箱">
  23. <a-input v-model="queryData.email" placeholder="请输入通知邮箱" allow-clear />
  24. </a-form-item>
  25. </a-col>
  26. </a-row>
  27. </a-form>
  28. </a-col>
  29. <a-divider style="height: 84px" direction="vertical" />
  30. <a-col :flex="1">
  31. <a-space direction="vertical" :size="18">
  32. <a-button type="primary" @click="search">
  33. <template #icon>
  34. <icon-search />
  35. </template>
  36. 搜索
  37. </a-button>
  38. <a-button @click="reset">
  39. <template #icon>
  40. <icon-refresh />
  41. </template>
  42. 重置
  43. </a-button>
  44. </a-space>
  45. </a-col>
  46. </a-row>
  47. <a-table :data="data" :bordered="false" hoverable class="table" :loading="loading" :pagination="{
  48. showPageSize: true,
  49. showJumper: true,
  50. showTotal: true,
  51. pageSize: pagination.pagesize,
  52. current: pagination.current,
  53. total: pagination.total
  54. }" @page-change="handlePageChange" @page-size-change="handlePageSizeChange">
  55. <template #name-filter="{ filterValue, setFilterValue, handleFilterConfirm, handleFilterReset }">
  56. <div class="custom-filter">
  57. <a-space direction="vertical">
  58. <a-input :model-value="filterValue[0]" @input="(value) => setFilterValue([value])" />
  59. <div class="custom-filter-footer">
  60. <a-button @click="handleFilterReset">重置</a-button>
  61. <a-button @click="handleFilterConfirm">确定</a-button>
  62. </div>
  63. </a-space>
  64. </div>
  65. </template>
  66. <template #columns>
  67. <a-table-column title="学号" :width="120" data-index="lepao_account" ellipsis tooltip :filterable="{
  68. filter: (value, record) => (record.lepao_account ?? '').includes(value),
  69. slotName: 'name-filter',
  70. icon: () => h(IconSearch)
  71. }"></a-table-column>
  72. <a-table-column title="账号名称" :filterable="{
  73. filter: (value, record) => (record.name ?? '').includes(value),
  74. slotName: 'name-filter',
  75. icon: () => h(IconSearch)
  76. }" :width="140">
  77. <template #cell="{ record }">
  78. <a-avatar :size="30">
  79. <img :alt="record.name ?? ''"
  80. :src="record.user_avatar ?? 'https://lepao-cloud.xxoo365.top/view.php/25aa126dc406974ff3579a99a2c6501a.png'" />
  81. </a-avatar>
  82. {{ record.name }}
  83. </template>
  84. </a-table-column>
  85. <a-table-column title="状态" ellipsis tooltip :width="230">
  86. <template #cell="{ record }">
  87. <div class="state">
  88. <div class="circle one" v-if="record.result.record_failed_reason === '自动确认有效'"></div>
  89. <div class="circle else" v-else></div>
  90. {{ record.result.record_failed_reason }}
  91. </div>
  92. </template>
  93. </a-table-column>
  94. <a-table-column title="跑区" :filterable="{
  95. filter: (value, record) => (record.result.pass_tit ?? '').includes(value),
  96. slotName: 'name-filter',
  97. icon: () => h(IconSearch)
  98. }" :width="220">
  99. <template #cell="{ record }">
  100. {{ record.result.pass_tit }}
  101. </template>
  102. </a-table-column>
  103. <a-table-column title="乐跑距离" :width="120" ellipsis tooltip>
  104. <template #cell="{ record }">
  105. {{ record.result.distance }} Km
  106. </template>
  107. </a-table-column>
  108. <a-table-column title="跑步时长" :width="120" ellipsis tooltip>
  109. <template #cell="{ record }">
  110. {{ formatSecondsToMinSec(record.result.time) }}
  111. </template>
  112. </a-table-column>
  113. <a-table-column title="平均配速" :width="120" ellipsis tooltip>
  114. <template #cell="{ record }">
  115. {{ calculatePace(record.result.time, record.result.distance) }}
  116. </template>
  117. </a-table-column>
  118. <a-table-column title="乐跑时间" :width="170" ellipsis tooltip>
  119. <template #cell="{ record }">
  120. {{ stramptoTime(record.time) }}
  121. </template>
  122. </a-table-column>
  123. <a-table-column title="" :width="100" fixed="right">
  124. <template #cell="{ record }">
  125. <a-button @click="$router.push(`/admin/lepaoRecords/${record.id}`)">查看详情</a-button>
  126. </template>
  127. </a-table-column>
  128. </template>
  129. </a-table>
  130. </a-card>
  131. </div>
  132. </template>
  133. <script setup>
  134. import { ref, reactive, h } from 'vue'
  135. import { adminLepaoRecords } from '@/api/lepao'
  136. import { Notification } from '@arco-design/web-vue'
  137. import { IconSearch } from '@arco-design/web-vue/es/icon'
  138. const data = ref([])
  139. const loading = ref(false)
  140. const queryData = reactive({
  141. name: '',
  142. lepao_account: '',
  143. email: ''
  144. })
  145. const pagination = reactive({
  146. total: 0,
  147. current: 1,
  148. pagesize: 20
  149. })
  150. const search = () => {
  151. pagination.current = 1
  152. getRecords()
  153. }
  154. const reset = () => {
  155. pagination.current = 1
  156. queryData.name = ''
  157. queryData.lepao_account = ''
  158. queryData.email = ''
  159. queryData.area = ''
  160. getRecords()
  161. }
  162. const getRecords = async () => {
  163. try {
  164. loading.value = true
  165. const reqData = {
  166. ...queryData,
  167. pagesize: pagination.pagesize,
  168. current: pagination.current
  169. }
  170. const res = await adminLepaoRecords(reqData)
  171. if (!res || res.code !== 0)
  172. return Notification.error({
  173. title: '获取乐跑记录失败!',
  174. content: res?.msg ?? '请稍后再试'
  175. })
  176. data.value = res.data
  177. pagination.total = res.pagination.total
  178. } catch (error) {
  179. Notification.error({
  180. title: '获取乐跑记录失败!',
  181. content: error.message || '请稍后再试'
  182. })
  183. } finally {
  184. loading.value = false
  185. }
  186. }
  187. // 分页 - 页码变化
  188. const handlePageChange = (page) => {
  189. pagination.current = page
  190. getRecords()
  191. }
  192. // 分页 - 每页条数变化
  193. const handlePageSizeChange = (size) => {
  194. pagination.pagesize = size
  195. pagination.current = 1 // 页大小变化后回到第一页
  196. getRecords()
  197. }
  198. const stramptoTime = (time) => {
  199. return new Date(time).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })
  200. }
  201. function calculatePace(seconds, kilometers) {
  202. const paceInSeconds = seconds / kilometers;
  203. const minutes = Math.floor(paceInSeconds / 60);
  204. const remainingSeconds = Math.round(paceInSeconds % 60);
  205. return `${minutes}'${remainingSeconds.toString().padStart(2, '0')}''`;
  206. }
  207. function formatSecondsToMinSec(totalSeconds) {
  208. const minutes = Math.floor(totalSeconds / 60);
  209. const seconds = totalSeconds % 60;
  210. return `${minutes}分${seconds.toString().padStart(2, '0')}秒`;
  211. }
  212. getRecords()
  213. </script>
  214. <style scoped lang="less">
  215. .container {
  216. padding: 0 20px 20px 20px;
  217. }
  218. .table {
  219. margin-top: 15px;
  220. .state {
  221. display: flex;
  222. align-items: center;
  223. .circle {
  224. border-radius: 50%;
  225. height: 8px;
  226. min-height: 8px;
  227. width: 8px;
  228. min-width: 8px;
  229. margin-right: 5px;
  230. }
  231. .zero {
  232. background-color: rgb(var(--orange-6));
  233. }
  234. .one {
  235. background-color: rgb(var(--green-6));
  236. }
  237. .else {
  238. background-color: rgb(var(--red-6));
  239. }
  240. }
  241. }
  242. .custom-filter {
  243. padding: 20px;
  244. background: var(--color-bg-5);
  245. border: 1px solid var(--color-neutral-3);
  246. border-radius: var(--border-radius-medium);
  247. box-shadow: 0 2px 5px rgb(0 0 0 / 10%);
  248. }
  249. .custom-filter-footer {
  250. display: flex;
  251. justify-content: space-between;
  252. }
  253. </style>