Browse Source

✨ feat: 增加网站管理员查看乐跑记录的功能

Pchen. 10 months ago
parent
commit
d9a3fa455b

+ 19 - 1
src/api/lepao.js

@@ -5,7 +5,9 @@ const api = {
   Records: '/Lepao/Records',
   Records: '/Lepao/Records',
   ChangeAutoRun: '/Lepao/ChangeAutoRun',
   ChangeAutoRun: '/Lepao/ChangeAutoRun',
   SingleRun: '/Lepao/SingleRun',
   SingleRun: '/Lepao/SingleRun',
-  GetRecordDetail: '/Lepao/GetRecordDetail'
+  GetRecordDetail: '/Lepao/GetRecordDetail',
+  AdminRecords: '/Admin/Lepao/Records',
+  AdminGetRecordDetail: '/Admin/Lepao/GetRecordDetail',
 }
 }
 
 
 export function addAccount (parameter) {
 export function addAccount (parameter) {
@@ -40,6 +42,14 @@ export function lepaoRecords (parameter) {
   })
   })
 }
 }
 
 
+export function adminLepaoRecords (parameter) {
+  return request({
+    url: api.AdminRecords,
+    method: 'get',
+    params: parameter
+  })
+}
+
 export function changeAutoRun (parameter) {
 export function changeAutoRun (parameter) {
   return request({
   return request({
     url: api.ChangeAutoRun,
     url: api.ChangeAutoRun,
@@ -63,3 +73,11 @@ export function GetRecordDetail (parameter) {
     params: parameter
     params: parameter
   })
   })
 }
 }
+
+export function adminGetRecordDetail (parameter) {
+  return request({
+    url: api.AdminGetRecordDetail,
+    method: 'get',
+    params: parameter
+  })
+}

+ 2 - 3
src/pages/admin/goods/goodsList.vue

@@ -9,9 +9,8 @@
                         label-align="left">
                         label-align="left">
                         <a-row :gutter="16">
                         <a-row :gutter="16">
                             <a-col :span="8">
                             <a-col :span="8">
-                                <a-form-item field="id" label="商品名称">
-                                    <a-input-number v-model="queryData.id" placeholder="请输入商品名称关键词" :step="1"
-                                        :precision="0" />
+                                <a-form-item field="keyword" label="商品名称">
+                                    <a-input v-model="queryData.keyword" placeholder="请输入商品名称关键词" />
                                 </a-form-item>
                                 </a-form-item>
                             </a-col>
                             </a-col>
                         </a-row>
                         </a-row>

+ 278 - 0
src/pages/admin/lepaoRecords/lepaoRecords.vue

@@ -0,0 +1,278 @@
+<template>
+
+  <div class="container">
+    <Breadcrumb :items="['校园乐跑', '乐跑记录']" />
+    <a-card title="乐跑记录">
+      <a-row>
+        <a-col :flex="'1000px'">
+          <a-form :model="queryData" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }"
+            label-align="left">
+            <a-row :gutter="16">
+              <a-col :span="12">
+                <a-form-item field="name" label="账号名称">
+                  <a-input v-model="queryData.name" placeholder="请输入账号名称" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="12">
+                <a-form-item field="lepao_account" label="学号">
+                  <a-input-number v-model="queryData.lepao_account" placeholder="请输入学号" :step="1" :precision="0" />
+                </a-form-item>
+              </a-col>
+              <a-col :span="12">
+                <a-form-item field="email" label="通知邮箱">
+                  <a-input v-model="queryData.email" placeholder="请输入通知邮箱" />
+                </a-form-item>
+              </a-col>
+            </a-row>
+          </a-form>
+        </a-col>
+        <a-divider style="height: 84px" direction="vertical" />
+        <a-col :flex="1" >
+          <a-space direction="vertical" :size="18">
+            <a-button type="primary" @click="search">
+              <template #icon>
+                <icon-search />
+              </template>
+              搜索
+            </a-button>
+            <a-button @click="reset">
+              <template #icon>
+                <icon-refresh />
+              </template>
+              重置
+            </a-button>
+          </a-space>
+        </a-col>
+      </a-row>
+
+      <a-table :data="data" stripe hoverable column-resizable class="table" :loading="loading" :pagination="{
+        showPageSize: true,
+        showJumper: true,
+        showTotal: true,
+        pageSize: pagination.pagesize,
+        current: pagination.current,
+        total: pagination.total
+      }" @page-change="handlePageChange" @page-size-change="handlePageSizeChange">
+
+        <template #name-filter="{ filterValue, setFilterValue, handleFilterConfirm, handleFilterReset }">
+          <div class="custom-filter">
+            <a-space direction="vertical">
+              <a-input :model-value="filterValue[0]" @input="(value) => setFilterValue([value])" />
+              <div class="custom-filter-footer">
+                <a-button @click="handleFilterReset">重置</a-button>
+                <a-button @click="handleFilterConfirm">确定</a-button>
+              </div>
+            </a-space>
+          </div>
+        </template>
+
+        <template #columns>
+          <a-table-column title="学号" :width="120" data-index="lepao_account" ellipsis tooltip :filterable="{
+            filter: (value, record) => (record.lepao_account ?? '').includes(value),
+            slotName: 'name-filter',
+            icon: () => h(IconSearch)
+          }"></a-table-column>
+          <a-table-column title="账号名称" :filterable="{
+            filter: (value, record) => (record.name ?? '').includes(value),
+            slotName: 'name-filter',
+            icon: () => h(IconSearch)
+          }">
+            <template #cell="{ record }">
+              {{ record.name }}
+            </template>
+          </a-table-column>
+          <a-table-column title="状态" ellipsis tooltip>
+            <template #cell="{ record }">
+              <div v-if="record.result.record_failed_reason === ''" class="state">
+                <div class="circle one"></div>正常
+              </div>
+              <div v-else class="state">
+                <div class="circle else"></div>{{ record.result.record_failed_reason }}
+              </div>
+            </template>
+          </a-table-column>
+          <a-table-column title="跑区" :filterable="{
+            filter: (value, record) => (record.result.pass_tit ?? '').includes(value),
+            slotName: 'name-filter',
+            icon: () => h(IconSearch)
+          }">
+            <template #cell="{ record }">
+              {{ record.result.pass_tit }}
+            </template>
+          </a-table-column>
+          <a-table-column title="乐跑距离" :width="120" ellipsis tooltip>
+            <template #cell="{ record }">
+              {{ record.result.distance }} Km
+            </template>
+          </a-table-column>
+          <a-table-column title="跑步时长" :width="120" ellipsis tooltip>
+            <template #cell="{ record }">
+              {{ formatSecondsToMinSec(record.result.time) }}
+            </template>
+          </a-table-column>
+          <a-table-column title="平均配速" :width="120" ellipsis tooltip>
+            <template #cell="{ record }">
+              {{ calculatePace(record.result.time, record.result.distance) }}
+            </template>
+          </a-table-column>
+          <a-table-column title="乐跑时间" :width="170" ellipsis tooltip>
+            <template #cell="{ record }">
+              {{ stramptoTime(record.time) }}
+            </template>
+          </a-table-column>
+          <a-table-column title="操作" :width="170" ellipsis tooltip>
+            <template #cell="{ record }">
+              <a-button @click="$router.push(`/admin/lepaoRecords/${record.id}`)">查看详情</a-button>
+            </template>
+          </a-table-column>
+        </template>
+      </a-table>
+    </a-card>
+  </div>
+
+</template>
+
+<script setup>
+import { ref, reactive, h } from 'vue'
+import { adminLepaoRecords } from '@/api/lepao'
+import { Notification } from '@arco-design/web-vue'
+import { IconSearch } from '@arco-design/web-vue/es/icon'
+
+const data = ref([])
+const loading = ref(false)
+
+const queryData = reactive({
+  name: '',
+  lepao_account: '',
+  email: ''
+})
+
+const pagination = reactive({
+  total: 0,
+  current: 1,
+  pagesize: 20
+})
+
+const search = () => {
+  pagination.current = 1
+  getRecords()
+}
+
+const reset = () => {
+  pagination.current = 1
+  queryData.name = ''
+  queryData.lepao_account = ''
+  queryData.email = ''
+  queryData.area = ''
+  getRecords()
+}
+
+const getRecords = async () => {
+  try {
+    loading.value = true
+    const reqData = {
+      ...queryData,
+      pagesize: pagination.pagesize,
+      current: pagination.current
+    }
+    const res = await adminLepaoRecords(reqData)
+    if (!res || res.code !== 0)
+      return Notification.error({
+        title: '获取乐跑记录失败!',
+        content: res?.msg ?? '请稍后再试'
+      })
+    data.value = res.data
+    pagination.total = res.pagination.total
+  } catch (error) {
+    Notification.error({
+      title: '获取乐跑记录失败!',
+      content: error.message || '请稍后再试'
+    })
+  } finally {
+    loading.value = false
+  }
+}
+
+// 分页 - 页码变化
+const handlePageChange = (page) => {
+  pagination.current = page
+  getPathList()
+}
+
+// 分页 - 每页条数变化
+const handlePageSizeChange = (size) => {
+  pagination.pagesize = size
+  pagination.current = 1 // 页大小变化后回到第一页
+  getPathList()
+}
+
+const stramptoTime = (time) => {
+  return new Date(time).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })
+}
+
+function calculatePace(seconds, kilometers) {
+  const paceInSeconds = seconds / kilometers;
+  const minutes = Math.floor(paceInSeconds / 60);
+  const remainingSeconds = Math.round(paceInSeconds % 60);
+
+  return `${minutes}'${remainingSeconds.toString().padStart(2, '0')}''`;
+}
+
+function formatSecondsToMinSec(totalSeconds) {
+  const minutes = Math.floor(totalSeconds / 60);
+  const seconds = totalSeconds % 60;
+
+  return `${minutes}分${seconds.toString().padStart(2, '0')}秒`;
+}
+
+getRecords()
+</script>
+
+<style scoped lang="less">
+.container {
+  padding: 0 20px 20px 20px;
+}
+
+.table {
+  margin-top: 15px;
+
+  .state {
+    display: flex;
+    align-items: center;
+
+    .circle {
+      border-radius: 50%;
+      height: 8px;
+      min-height: 8px;
+      width: 8px;
+      min-width: 8px;
+      margin-right: 5px;
+    }
+
+    .zero {
+      background-color: rgb(var(--orange-6));
+    }
+
+    .one {
+      background-color: rgb(var(--green-6));
+    }
+
+    .else {
+      background-color: rgb(var(--red-6));
+    }
+  }
+}
+
+.custom-filter {
+  padding: 20px;
+  background: var(--color-bg-5);
+  border: 1px solid var(--color-neutral-3);
+  border-radius: var(--border-radius-medium);
+  box-shadow: 0 2px 5px rgb(0 0 0 / 10%);
+}
+
+.custom-filter-footer {
+  display: flex;
+  justify-content: space-between;
+}
+</style>

+ 89 - 0
src/pages/admin/lepaoRecords/recordDetail.vue

@@ -0,0 +1,89 @@
+<template>
+    <div class="container">
+        <Breadcrumb :items="['乐跑记录', '记录详情']" />
+        <a-card title="记录详情">
+            <a-descriptions :data="info" :column="2" />
+            <MapContainer v-if="showMap" :point_list="data.result.point_list" :pathData="data.data" threeD style="margin-top: 10px;"/>
+        </a-card>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { adminGetRecordDetail } from '@/api/lepao'
+import { Notification } from '@arco-design/web-vue'
+import { useRoute } from 'vue-router'
+import MapContainer from '@/components/Map/MapContainer.vue'
+
+const route = useRoute()
+
+const loading = ref(false)
+const showMap = ref(false)
+const data = ref({})
+const info = ref([])
+
+const adminGetRecordDetail = async (id) => {
+    try {
+        loading.value = true
+        showMap.value = false
+        const res = await adminGetRecordDetail({ id })
+        if (!res || res.code !== 0)
+            return Notification.error({
+                title: '获取路径数据失败!',
+                content: res?.msg ?? '请稍后再试'
+            })
+
+        data.value = res.data
+        showMap.value = true
+
+        info.value = [
+            { label: '账号名称', value: res.data.name },
+            { label: '乐跑账号', value: res.data.lepao_account },
+            { label: '跑区名称', value: res.data.result.pass_tit },
+            { label: '记录时间', value: stramptoTime(res.data.time) },
+            { label: '开始时间', value: stramptoTime(res.data.result.start_time * 1000) },
+            { label: '打卡点数量', value: res.data.result.point_list.length },
+            { label: '跑步距离', value: res.data.result.distance + ' Km' },
+            { label: '跑步时长', value: formatSecondsToMinSec(res.data.result.time ) },
+            { label: '平均配速', value: calculatePace(res.data.result.time, res.data.result.distance) }
+        ]
+    } catch (error) {
+        Notification.error({
+            title: '获取路径数据失败!',
+            content: error.message || '请稍后再试'
+        })
+    } finally {
+        loading.value = false
+    }
+}
+
+onMounted(() => {
+    adminGetRecordDetail(route.params.id)
+})
+
+const stramptoTime = (time) => {
+  return new Date(time).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })
+}
+
+function calculatePace(seconds, kilometers) {
+  const paceInSeconds = seconds / kilometers;
+  const minutes = Math.floor(paceInSeconds / 60);
+  const remainingSeconds = Math.round(paceInSeconds % 60);
+
+  return `${minutes}'${remainingSeconds.toString().padStart(2, '0')}''`;
+}
+
+function formatSecondsToMinSec(totalSeconds) {
+    const minutes = Math.floor(totalSeconds / 60);
+    const seconds = totalSeconds % 60;
+
+    return `${minutes}分${seconds.toString().padStart(2, '0')}秒`;
+}
+
+</script>
+
+<style scoped lang="less">
+.container {
+    padding: 0 20px 20px 20px;
+}
+</style>

+ 19 - 0
src/router/index.js

@@ -152,6 +152,25 @@ const routes = [
             permission: ['admin', 'service', 'product']
             permission: ['admin', 'service', 'product']
         },
         },
         children: [
         children: [
+            {
+                path: 'lepaoRecords',
+                name: 'admin.lepaoRecords',
+                component: () => import('../pages/admin/lepaoRecords/lepaoRecords.vue'),
+                meta: {
+                    title: '乐跑记录',
+                    permission: ['admin', 'service']
+                }
+            },
+            {
+                path: 'lepaoRecords/:id',
+                name: 'admin.lepaoRecords.detail',
+                component: () => import('../pages/admin/lepaoRecords/recordDetail.vue'),
+                meta: {
+                    title: '乐跑记录详情',
+                    hideInMenu: true,
+                    permission: ['admin', 'service']
+                }
+            },
             {
             {
                 path: 'service/orderList',
                 path: 'service/orderList',
                 name: 'admin.service.orderList',
                 name: 'admin.service.orderList',