index.vue 8.3 KB

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